From a8e80c1ad179e12a0c7a0528ecd746ab863b4192 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Tue, 25 Nov 2025 07:28:58 +0100 Subject: [PATCH 001/701] GPU Vulkan Display: Device layers are deprecated --- GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.cxx | 2 -- 1 file changed, 2 deletions(-) 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); From f9d9c0f57458dcccc79e3847b38dbec4dda33422 Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Wed, 26 Nov 2025 14:54:30 +0100 Subject: [PATCH 002/701] DPL Analysis: rework of aod-spawner and aod-index-builder (#14854) --- Framework/AnalysisSupport/CMakeLists.txt | 6 + .../AnalysisSupport/src/AODReaderHelpers.cxx | 206 +++++++++ .../src}/AODReaderHelpers.h | 3 +- .../AnalysisSupport/src/OnDemandPlugin.cxx | 32 ++ Framework/Core/CMakeLists.txt | 4 +- Framework/Core/include/Framework/ASoA.h | 89 ++-- .../Core/include/Framework/AnalysisHelpers.h | 413 +++++++++++------- .../Core/include/Framework/AnalysisManagers.h | 33 +- Framework/Core/include/Framework/ArrowTypes.h | 50 +++ .../Core/include/Framework/Expressions.h | 2 + .../include/Framework/IndexBuilderHelpers.h | 158 +++---- .../Core/include/Framework/TableBuilder.h | 89 +--- Framework/Core/src/AODReaderHelpers.cxx | 280 ------------ Framework/Core/src/AnalysisHelpers.cxx | 177 +++++++- Framework/Core/src/AnalysisSupportHelpers.cxx | 1 - Framework/Core/src/ArrowSupport.cxx | 5 +- Framework/Core/src/ExpressionJSONHelpers.cxx | 57 ++- Framework/Core/src/Expressions.cxx | 16 + Framework/Core/src/IndexBuilderHelpers.cxx | 363 ++++++++++----- Framework/Core/src/IndexJSONHelpers.cxx | 230 ++++++++++ Framework/Core/src/IndexJSONHelpers.h | 25 ++ Framework/Core/src/TableBuilder.cxx | 89 +--- Framework/Core/src/WorkflowHelpers.cxx | 5 +- Framework/Core/test/test_Expressions.cxx | 29 ++ Framework/Core/test/test_IndexBuilder.cxx | 56 ++- 25 files changed, 1507 insertions(+), 911 deletions(-) create mode 100644 Framework/AnalysisSupport/src/AODReaderHelpers.cxx rename Framework/{Core/include/Framework => AnalysisSupport/src}/AODReaderHelpers.h (92%) create mode 100644 Framework/AnalysisSupport/src/OnDemandPlugin.cxx delete mode 100644 Framework/Core/src/AODReaderHelpers.cxx create mode 100644 Framework/Core/src/IndexJSONHelpers.cxx create mode 100644 Framework/Core/src/IndexJSONHelpers.h diff --git a/Framework/AnalysisSupport/CMakeLists.txt b/Framework/AnalysisSupport/CMakeLists.txt index 92fd55b86a33d..6024134a5495d 100644 --- a/Framework/AnalysisSupport/CMakeLists.txt +++ b/Framework/AnalysisSupport/CMakeLists.txt @@ -16,6 +16,12 @@ 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 diff --git a/Framework/AnalysisSupport/src/AODReaderHelpers.cxx b/Framework/AnalysisSupport/src/AODReaderHelpers.cxx new file mode 100644 index 0000000000000..40aa5a9537c7f --- /dev/null +++ b/Framework/AnalysisSupport/src/AODReaderHelpers.cxx @@ -0,0 +1,206 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#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/ConfigContext.h" +#include "Framework/AnalysisContext.h" + +namespace o2::framework::readers +{ +namespace +{ +struct Buildable { + bool exclusive = false; + std::string binding; + std::vector labels; + header::DataOrigin origin; + header::DataDescription description; + header::DataHeader::SubSpecificationType version; + std::vector records; + std::shared_ptr outputSchema; + + 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); + } + outputSchema = std::make_shared([](std::vector const& recs) { + std::vector> fields; + for (auto& r : recs) { + fields.push_back(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, + records, + outputSchema, + origin, + description, + version, + nullptr}; + } +}; + +} // namespace + +AlgorithmSpec AODReaderHelpers::indexBuilderCallback(ConfigContext const& ctx) +{ + auto& ac = ctx.services().get(); + return AlgorithmSpec::InitCallback{[requested = ac.requestedIDXs](InitContext& /*ic*/) { + std::vector buildables; + for (auto& i : requested) { + buildables.emplace_back(i); + } + std::vector builders; + for (auto& b : buildables) { + builders.push_back(b.createBuilder()); + } + return [builders](ProcessingContext& pc) mutable { + auto outputs = pc.outputs(); + for (auto& builder : builders) { + outputs.adopt(Output{builder.origin, builder.description, builder.version}, builder.materialize(pc)); + } + }; + }}; +} + +namespace +{ +struct Spawnable { + std::string binding; + std::vector labels; + std::vector projectors; + std::vector> expressions; + std::shared_ptr outputSchema; + std::shared_ptr inputSchema; + + header::DataOrigin origin; + header::DataDescription description; + header::DataHeader::SubSpecificationType version; + + 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& i : spec.metadata) { + if (i.name.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::vector> fields; + for (auto& s : schemas) { + std::copy(s->fields().begin(), s->fields().end(), 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, + expressions, + makeProjector(), + outputSchema, + inputSchema, + origin, + description, + version}; + } +}; + +} // namespace + +AlgorithmSpec AODReaderHelpers::aodSpawnerCallback(ConfigContext const& ctx) +{ + auto& ac = ctx.services().get(); + return AlgorithmSpec::InitCallback{[requested = ac.spawnerInputs](InitContext& /*ic*/) { + std::vector spawnables; + for (auto& i : requested) { + spawnables.emplace_back(i); + } + std::vector spawners; + for (auto& s : spawnables) { + spawners.push_back(s.createMaker()); + } + + return [spawners](ProcessingContext& pc) mutable { + auto outputs = pc.outputs(); + for (auto& spawner : spawners) { + 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 92% rename from Framework/Core/include/Framework/AODReaderHelpers.h rename to Framework/AnalysisSupport/src/AODReaderHelpers.h index 800d26c2aeae0..197907ca3ccb1 100644 --- a/Framework/Core/include/Framework/AODReaderHelpers.h +++ b/Framework/AnalysisSupport/src/AODReaderHelpers.h @@ -18,11 +18,10 @@ namespace o2::framework::readers { - struct AODReaderHelpers { static AlgorithmSpec rootFileReaderCallback(); static AlgorithmSpec aodSpawnerCallback(ConfigContext const& ctx); - static AlgorithmSpec indexBuilderCallback(std::vector& requested); + static AlgorithmSpec indexBuilderCallback(ConfigContext const& ctx); }; } // namespace o2::framework::readers 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/CMakeLists.txt b/Framework/Core/CMakeLists.txt index cefb903c29895..ce8fbb0dc55f7 100644 --- a/Framework/Core/CMakeLists.txt +++ b/Framework/Core/CMakeLists.txt @@ -10,8 +10,7 @@ # 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 @@ -143,6 +142,7 @@ o2_add_library(Framework src/Variant.cxx src/VariantJSONHelpers.cxx src/ExpressionJSONHelpers.cxx + src/IndexJSONHelpers.cxx src/VariantPropertyTreeHelpers.cxx src/WorkflowCustomizationHelpers.cxx src/WorkflowHelpers.cxx diff --git a/Framework/Core/include/Framework/ASoA.h b/Framework/Core/include/Framework/ASoA.h index 10c1fc4ac3ceb..a30363605af36 100644 --- a/Framework/Core/include/Framework/ASoA.h +++ b/Framework/Core/include/Framework/ASoA.h @@ -34,7 +34,6 @@ #include #include #include // IWYU pragma: export -#include namespace o2::framework { @@ -53,6 +52,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 +217,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 +257,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{})); } }; @@ -406,12 +419,6 @@ struct Binding { } }; -template -auto createFieldsFromColumns(framework::pack) -{ - return std::vector>{C::asArrowField()...}; -} - using SelectionVector = std::vector; template @@ -686,7 +693,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 @@ -1303,6 +1310,11 @@ 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() { @@ -3251,28 +3263,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/AnalysisHelpers.h b/Framework/Core/include/Framework/AnalysisHelpers.h index fa82151c6e756..3666fe1299489 100644 --- a/Framework/Core/include/Framework/AnalysisHelpers.h +++ b/Framework/Core/include/Framework/AnalysisHelpers.h @@ -26,10 +26,147 @@ #include "Framework/Traits.h" #include +namespace o2::soa +{ +struct IndexRecord { + std::string label; + 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 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> 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 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 @@ -44,6 +181,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 @@ -56,6 +203,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() { @@ -69,15 +226,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(), C::columnLabel(), IndexKind::IdxSelf, pos); + } else { + idx.emplace_back(o2::aod::label(), 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; } @@ -115,11 +323,8 @@ constexpr auto getExpressionMetadata() -> std::vector(o2::soa::createFieldsFromColumns(expression_pack_t{})); - auto json = framework::serializeProjectors(projectors); - return {framework::ConfigParamSpec{"projectors", framework::VariantType::String, json, {"\"\""}}, - framework::ConfigParamSpec{"schema", framework::VariantType::String, framework::serializeSchema(schema), {"\"\""}}}; + return {framework::ConfigParamSpec{"projectors", framework::VariantType::String, json, {"\"\""}}}; } template @@ -129,6 +334,21 @@ constexpr auto getExpressionMetadata() -> std::vector +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 @@ -141,6 +361,11 @@ constexpr auto tableRef2InputSpec() 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(), @@ -319,29 +544,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(); } @@ -367,15 +592,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(); @@ -389,6 +608,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 @@ -397,13 +617,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>; }; @@ -418,15 +642,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(); @@ -445,7 +663,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; @@ -462,7 +684,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; @@ -477,129 +699,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 @@ -613,12 +712,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(); @@ -639,10 +743,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); } }; @@ -651,7 +754,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..fbb499940b9b9 100644 --- a/Framework/Core/include/Framework/AnalysisManagers.h +++ b/Framework/Core/include/Framework/AnalysisManagers.h @@ -34,18 +34,6 @@ 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) { @@ -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) { - 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)); @@ -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(); 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/Expressions.h b/Framework/Core/include/Framework/Expressions.h index e08bf8db52bb4..0be19954f1faa 100644 --- a/Framework/Core/include/Framework/Expressions.h +++ b/Framework/Core/include/Framework/Expressions.h @@ -712,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> outputFields); + template std::shared_ptr createProjectors(framework::pack, std::vector> const& fields, gandiva::SchemaPtr schema) { 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/TableBuilder.h b/Framework/Core/include/Framework/TableBuilder.h index 7707afe45b380..845820dfe4bff 100644 --- a/Framework/Core/include/Framework/TableBuilder.h +++ b/Framework/Core/include/Framework/TableBuilder.h @@ -15,7 +15,6 @@ #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. @@ -26,6 +25,7 @@ #include #include #include +#include #include #include @@ -764,92 +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); - -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_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/src/AODReaderHelpers.cxx b/Framework/Core/src/AODReaderHelpers.cxx deleted file mode 100644 index 09ec16a93b087..0000000000000 --- a/Framework/Core/src/AODReaderHelpers.cxx +++ /dev/null @@ -1,280 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not 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/ExpressionHelpers.h" -#include "Framework/DataProcessingHelpers.h" -#include "Framework/AlgorithmSpec.h" -#include "Framework/ControlService.h" -#include "Framework/CallbackService.h" -#include "Framework/EndOfStreamContext.h" -#include "Framework/DataSpecUtils.h" -#include "ExpressionJSONHelpers.h" -#include "Framework/ConfigContext.h" -#include "Framework/AnalysisContext.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 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); -} - -struct Maker { - std::string binding; - std::vector labels; - std::vector> expressions; - std::shared_ptr projector = nullptr; - std::shared_ptr schema; - - header::DataOrigin origin; - header::DataDescription description; - header::DataHeader::SubSpecificationType version; - - std::shared_ptr make(ProcessingContext& pc) - { - std::vector> originals; - for (auto const& label : labels) { - originals.push_back(pc.inputs().get(label)->asArrowTable()); - } - auto fullTable = soa::ArrowHelpers::joinTables(std::move(originals), std::span{labels.begin(), labels.size()}); - if (fullTable->num_rows() == 0) { - return arrow::Table::MakeEmpty(schema).ValueOrDie(); - } - if (projector == nullptr) { - auto s = gandiva::Projector::Make( - fullTable->schema(), - expressions, - &projector); - if (!s.ok()) { - throw o2::framework::runtime_error_f("Failed to create projector: %s", s.ToString().c_str()); - } - } - - return spawnerHelper(fullTable, schema, binding.c_str(), schema->num_fields(), projector); - } -}; - -struct Spawnable { - std::string binding; - std::vector labels; - std::vector projectors; - std::vector> expressions; - std::shared_ptr outputSchema; - std::shared_ptr inputSchema; - - header::DataOrigin origin; - header::DataDescription description; - header::DataHeader::SubSpecificationType version; - - 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); - - for (auto& i : spec.metadata) { - if (i.name.starts_with("input:")) { - labels.emplace_back(i.name.substr(6)); - } - } - - std::vector> fields; - for (auto& p : projectors) { - expressions::walk(p.node.get(), - [&fields](expressions::Node* n) mutable { - if (n->self.index() == 1) { - auto& b = std::get(n->self); - if (std::find_if(fields.begin(), fields.end(), [&b](std::shared_ptr const& field) { return field->name() == b.name; }) == fields.end()) { - fields.emplace_back(std::make_shared(b.name, expressions::concreteArrowType(b.type))); - } - } - }); - } - inputSchema = std::make_shared(fields); - - int i = 0; - for (auto& p : projectors) { - expressions.push_back( - expressions::makeExpression( - expressions::createExpressionTree( - expressions::createOperations(p), - inputSchema), - outputSchema->field(i))); - ++i; - } - } - - std::shared_ptr makeProjector() - { - return expressions::createProjectorHelper(projectors.size(), projectors.data(), inputSchema, outputSchema->fields()); - } - - Maker createMaker() - { - o2::framework::addLabelToSchema(outputSchema, binding.c_str()); - return { - binding, - labels, - expressions, - nullptr, - outputSchema, - origin, - description, - version}; - } -}; - -} // namespace - -AlgorithmSpec AODReaderHelpers::aodSpawnerCallback(/*std::vector& requested*/ ConfigContext const& ctx) -{ - auto& ac = ctx.services().get(); - return AlgorithmSpec::InitCallback{[requested = ac.spawnerInputs](InitContext& /*ic*/) { - std::vector spawnables; - for (auto& i : requested) { - spawnables.emplace_back(i); - } - std::vector makers; - for (auto& s : spawnables) { - makers.push_back(s.createMaker()); - } - - return [makers](ProcessingContext& pc) mutable { - auto outputs = pc.outputs(); - for (auto& maker : makers) { - outputs.adopt(Output{maker.origin, maker.description, maker.version}, maker.make(pc)); - } - }; - }}; -} - -} // namespace o2::framework::readers diff --git a/Framework/Core/src/AnalysisHelpers.cxx b/Framework/Core/src/AnalysisHelpers.cxx index 4f78cc42f3f98..b8e0348d5df9c 100644 --- a/Framework/Core/src/AnalysisHelpers.cxx +++ b/Framework/Core/src/AnalysisHelpers.cxx @@ -8,11 +8,147 @@ // In applying this license CERN does not 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 (builders.empty()) { + builders = makeBuilders(std::move(tables), records); + } else { + resetBuilders(builders, std::move(tables)); + } + + std::vector finds; + finds.resize(builders.size()); + 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); + } + for (auto i = 0U; i < builders.size(); ++i) { + finds[i] = builders[i].find(idx); + } + if (exclusive) { + if (std::none_of(finds.begin(), finds.end(), [](bool const x) { return x == false; })) { + builders[0].fill(counter); + for (auto i = 1U; i < builders.size(); ++i) { + builders[i].fill(idx); + } + } + } else { + builders[0].fill(counter); + for (auto i = 1U; i < builders.size(); ++i) { + builders[i].fill(idx); + } + } + } + + std::vector> arrays; + arrays.reserve(builders.size()); + for (auto& builder : builders) { + arrays.push_back(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); + for (auto i = 0U; i < nColumns; ++i) { + arrays.push_back(std::make_shared(chunks[i])); + } + + 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) { @@ -35,10 +171,49 @@ std::string serializeProjectors(std::vector& return osm.str(); } -std::string serializeSchema(std::shared_ptr& schema) +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& labels) +{ + std::vector> tables; + for (auto const& label : labels) { + tables.emplace_back(pc.inputs().get(label.c_str())->asArrowTable()); + } + return tables; +} + +std::shared_ptr Spawner::materialize(ProcessingContext& pc) const +{ + auto tables = extractSources(pc, labels); + 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, labels); + 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..b5c898faa515a 100644 --- a/Framework/Core/src/AnalysisSupportHelpers.cxx +++ b/Framework/Core/src/AnalysisSupportHelpers.cxx @@ -219,7 +219,6 @@ void AnalysisSupportHelpers::addMissingOutputsToAnalysisCCDBFetcher( // 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()) { diff --git a/Framework/Core/src/ArrowSupport.cxx b/Framework/Core/src/ArrowSupport.cxx index 4150fda9f63f1..cf2d364027932 100644 --- a/Framework/Core/src/ArrowSupport.cxx +++ b/Framework/Core/src/ArrowSupport.cxx @@ -10,7 +10,6 @@ // or submit itself to any jurisdiction. #include "ArrowSupport.h" -#include "Framework/AODReaderHelpers.h" #include "Framework/ArrowContext.h" #include "Framework/ArrowTableSlicingCache.h" #include "Framework/DataProcessor.h" @@ -619,7 +618,7 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() 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); + builder->algorithm = PluginManager::loadAlgorithmFromPlugin("O2FrameworkOnDemandTablesSupport", "IndexTableBuilder", ctx); // readers::AODReaderHelpers::indexBuilderCallback(ctx); AnalysisSupportHelpers::addMissingOutputsToBuilder(ac.requestedIDXs, ac.requestedAODs, ac.requestedDYNs, *builder); } @@ -654,7 +653,7 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() spawner->inputs.clear(); // replace AlgorithmSpec // FIXME: it should be made more generic, so it does not need replacement... - spawner->algorithm = readers::AODReaderHelpers::aodSpawnerCallback(ctx); + spawner->algorithm = PluginManager::loadAlgorithmFromPlugin("O2FrameworkOnDemandTablesSupport", "ExtendedTableSpawner", ctx); AnalysisSupportHelpers::addMissingOutputsToSpawner({}, ac.spawnerInputs, ac.requestedAODs, *spawner); } diff --git a/Framework/Core/src/ExpressionJSONHelpers.cxx b/Framework/Core/src/ExpressionJSONHelpers.cxx index 8d4907a721f7e..a6e19875381cd 100644 --- a/Framework/Core/src/ExpressionJSONHelpers.cxx +++ b/Framework/Core/src/ExpressionJSONHelpers.cxx @@ -637,6 +637,18 @@ void o2::framework::ExpressionJSONHelpers::write(std::ostream& o, std::vector 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; @@ -658,6 +670,8 @@ struct SchemaReader : public rapidjson::BaseReaderHandler, Sch std::string name; atype::type type; + atype::type element; + int list_size = 1; SchemaReader() { @@ -667,7 +681,7 @@ struct SchemaReader : public rapidjson::BaseReaderHandler, Sch bool StartArray() { - debug << "Starting array" << std::endl; + debug << "StartArray()" << std::endl; if (states.top() == State::IN_START && currentKey.compare("fields") == 0) { states.push(State::IN_LIST); return true; @@ -678,7 +692,7 @@ struct SchemaReader : public rapidjson::BaseReaderHandler, Sch bool EndArray(SizeType) { - debug << "Ending array" << std::endl; + debug << "EndArray()" << std::endl; if (states.top() == State::IN_LIST) { // finalize schema schema = std::make_shared(fields); @@ -706,6 +720,12 @@ struct SchemaReader : public rapidjson::BaseReaderHandler, Sch 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); @@ -721,6 +741,9 @@ struct SchemaReader : public rapidjson::BaseReaderHandler, Sch if (states.top() == State::IN_LIST) { states.push(State::IN_FIELD); + list_size = 1; + element = atype::NA; + type = atype::NA; return true; } @@ -734,7 +757,7 @@ struct SchemaReader : public rapidjson::BaseReaderHandler, Sch if (states.top() == State::IN_FIELD) { states.pop(); // add a field - fields.emplace_back(std::make_shared(name, expressions::concreteArrowType(type))); + fields.emplace_back(std::make_shared(name, arrowDataTypeFromId(type, list_size, element))); return true; } @@ -754,6 +777,14 @@ struct SchemaReader : public rapidjson::BaseReaderHandler, Sch 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); @@ -777,6 +808,10 @@ struct SchemaReader : public rapidjson::BaseReaderHandler, Sch 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); } }; @@ -791,7 +826,7 @@ std::shared_ptr o2::framework::ArrowJSONHelpers::read(std::istrea bool ok = reader.Parse(isw, sreader); if (!ok) { - throw framework::runtime_error_f("Cannot parse serialized Expression, error: %s at offset: %d", rapidjson::GetParseError_En(reader.GetParseErrorCode()), reader.GetErrorOffset()); + throw framework::runtime_error_f("Cannot parse serialized Schema, error: %s at offset: %d", rapidjson::GetParseError_En(reader.GetParseErrorCode()), reader.GetErrorOffset()); } return sreader.schema; } @@ -804,6 +839,20 @@ void writeSchema(rapidjson::Writer& w, arrow::Schema* 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(); diff --git a/Framework/Core/src/Expressions.cxx b/Framework/Core/src/Expressions.cxx index 05a3462d6e4da..43143f781ddf4 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> outputFields) +{ + std::vector> expressions; + int i = 0; + for (auto& 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/IndexBuilderHelpers.cxx b/Framework/Core/src/IndexBuilderHelpers.cxx index 52d6080690fe1..d7231f72cbee8 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) +{ + static_cast(this)->reset(source); + if (!preSlice().ok()) { + throw framework::runtime_error("Cannot pre-slice the source for slice-index building"); + } + mListBuilder->Reset(); + mValues = nullptr; + mCounts = nullptr; + mValuePos = 0; +} + +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..19ae94a4bcd4c --- /dev/null +++ b/Framework/Core/src/IndexJSONHelpers.cxx @@ -0,0 +1,230 @@ +// 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; + 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("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, 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; + } + } + + 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("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/TableBuilder.cxx b/Framework/Core/src/TableBuilder.cxx index c80fef9f0533c..955fe686e12a8 100644 --- a/Framework/Core/src/TableBuilder.cxx +++ b/Framework/Core/src/TableBuilder.cxx @@ -81,94 +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); -} - -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); - for (auto i = 0U; i < nColumns; ++i) { - arrays.push_back(std::make_shared(chunks[i])); - } - - return arrow::Table::Make(newSchema, arrays); + addLabelToSchema(mSchema, label); } } // namespace o2::framework diff --git a/Framework/Core/src/WorkflowHelpers.cxx b/Framework/Core/src/WorkflowHelpers.cxx index b3af5636127f9..61443f5f71616 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" @@ -416,7 +415,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext "internal-dpl-aod-index-builder", {}, {}, - readers::AODReaderHelpers::indexBuilderCallback(ac.requestedIDXs), + PluginManager::loadAlgorithmFromPlugin("O2FrameworkOnDemandTablesSupport", "IndexTableBuilder", ctx), // readers::AODReaderHelpers::indexBuilderCallback(ctx), {}}; AnalysisSupportHelpers::addMissingOutputsToBuilder(ac.requestedIDXs, ac.requestedAODs, ac.requestedDYNs, indexBuilder); @@ -436,7 +435,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext "internal-dpl-aod-spawner", {}, {}, - readers::AODReaderHelpers::aodSpawnerCallback(ctx), + PluginManager::loadAlgorithmFromPlugin("O2FrameworkOnDemandTablesSupport", "ExtendedTableSpawner", ctx), // readers::AODReaderHelpers::aodSpawnerCallback(ctx), {}}; AnalysisSupportHelpers::addMissingOutputsToSpawner({}, ac.spawnerInputs, ac.requestedAODs, aodSpawner); diff --git a/Framework/Core/test/test_Expressions.cxx b/Framework/Core/test/test_Expressions.cxx index 41be7d53d2276..b4a65fb0c7b48 100644 --- a/Framework/Core/test/test_Expressions.cxx +++ b/Framework/Core/test/test_Expressions.cxx @@ -454,4 +454,33 @@ TEST_CASE("TestExpressionSerialization") 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_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); +} From 821e1a923e33d4c825dd5b01f1a2404f8d798b18 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 26 Nov 2025 14:45:19 +0100 Subject: [PATCH 003/701] GPU: Fix time measurements of reco steps, synchronization was missing --- GPU/GPUTracking/Global/GPUChain.h | 1 + 1 file changed, 1 insertion(+) diff --git a/GPU/GPUTracking/Global/GPUChain.h b/GPU/GPUTracking/Global/GPUChain.h index e3a20ad81a2cb..9ce3da1092e83 100644 --- a/GPU/GPUTracking/Global/GPUChain.h +++ b/GPU/GPUTracking/Global/GPUChain.h @@ -297,6 +297,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; } From f355002fa08e6a194814686138c6cc157fded843 Mon Sep 17 00:00:00 2001 From: Christian Sonnabend Date: Wed, 26 Nov 2025 18:30:49 +0100 Subject: [PATCH 004/701] FST checks to avoid node crashes for SLURM, MI100 and FMQ segments --- prodtests/full-system-test/start_tmux.sh | 34 ++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/prodtests/full-system-test/start_tmux.sh b/prodtests/full-system-test/start_tmux.sh index 22b603f48a0d0..22b658856803a 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 From 7b0862460d7cf26a67d492c2c081172fbc9ddcd1 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 26 Nov 2025 20:32:43 +0100 Subject: [PATCH 005/701] DPL: add protection against fork bomb (#14858) --- Framework/Core/src/runDataProcessing.cxx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Framework/Core/src/runDataProcessing.cxx b/Framework/Core/src/runDataProcessing.cxx index f5992f6dbf359..c36b1deadeefb 100644 --- a/Framework/Core/src/runDataProcessing.cxx +++ b/Framework/Core/src/runDataProcessing.cxx @@ -748,7 +748,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); From 297aa69d7e5ec458fee6d3de4dc5c4ee1e953c85 Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Wed, 26 Nov 2025 14:06:40 +0100 Subject: [PATCH 006/701] Fix of custom streamer code Fixing the custom streamer code for `CalArray` and resolving https://its.cern.ch/jira/browse/O2-6509 --- Detectors/TPC/base/src/TPCFlagsMemberCustomStreamer.cxx | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Detectors/TPC/base/src/TPCFlagsMemberCustomStreamer.cxx b/Detectors/TPC/base/src/TPCFlagsMemberCustomStreamer.cxx index 7e3499dec14d9..dca7ba35004c9 100644 --- a/Detectors/TPC/base/src/TPCFlagsMemberCustomStreamer.cxx +++ b/Detectors/TPC/base/src/TPCFlagsMemberCustomStreamer.cxx @@ -39,6 +39,7 @@ void MemberVectorPadFlagsStreamer(TBuffer& R__b, void* objp, int n) } std::vector* obj = static_cast*>(objp); if (R__b.IsReading()) { + obj->clear(); std::vector R__stl; R__stl.clear(); int R__n; @@ -50,7 +51,8 @@ void MemberVectorPadFlagsStreamer(TBuffer& R__b, void* objp, int n) R__stl.push_back(readtemp); } auto data = reinterpret_cast(R__stl.data()); - for (int i = 0; i < R__n; ++i) { + 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 { @@ -63,6 +65,8 @@ void MemberVectorPadFlagsStreamer(TBuffer& R__b, void* objp, int n) } // 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 = @@ -79,3 +83,4 @@ static __attribute__((used)) int _R__dummyStreamer_3 = return 0; })(); } // namespace ROOT +#endif \ No newline at end of file From 5b0d25dc159911a810c88d895f0c443ff24e3ff1 Mon Sep 17 00:00:00 2001 From: shahor02 Date: Thu, 27 Nov 2025 01:58:02 +0100 Subject: [PATCH 007/701] Possibility to combine 2 material LUTs (#14861) --- .../include/DetectorsBase/MatLayerCylSet.h | 2 +- Detectors/Base/src/MatLayerCylSet.cxx | 30 +++++++++++++++---- 2 files changed, 25 insertions(+), 7 deletions(-) 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/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 {} Date: Thu, 27 Nov 2025 09:52:04 +0100 Subject: [PATCH 008/701] ITS: GPU: reverse destruction order to fix crash Signed-off-by: Felix Schlepper --- GPU/GPUTracking/Global/GPUChainITS.cxx | 10 +++------- GPU/GPUTracking/Global/GPUChainITS.h | 6 +++--- 2 files changed, 6 insertions(+), 10 deletions(-) diff --git a/GPU/GPUTracking/Global/GPUChainITS.cxx b/GPU/GPUTracking/Global/GPUChainITS.cxx index 9be553de27f95..c72023bdf42ce 100644 --- a/GPU/GPUTracking/Global/GPUChainITS.cxx +++ b/GPU/GPUTracking/Global/GPUChainITS.cxx @@ -30,11 +30,11 @@ class GPUFrameworkExternalAllocator final : public o2::its::ExternalAllocator return mFWReco->AllocateDirectMemory(size, mType); } void deallocate(char* ptr, size_t size) final {} // this is a simple no-op - void pushTagOnStack(uint64_t tag) + void pushTagOnStack(uint64_t tag) final { mFWReco->PushNonPersistentMemory(tag); } - void popTagOffStack(uint64_t tag) + void popTagOffStack(uint64_t tag) final { mFWReco->PopNonPersistentMemory(GPUDataTypes::RecoStep::ITSTracking, tag); } @@ -45,11 +45,7 @@ class GPUFrameworkExternalAllocator final : public o2::its::ExternalAllocator }; } // namespace o2::its -GPUChainITS::~GPUChainITS() -{ - mITSTrackerTraits.reset(); - mITSVertexerTraits.reset(); -} +GPUChainITS::~GPUChainITS() = default; GPUChainITS::GPUChainITS(GPUReconstruction* rec) : GPUChain(rec) {} diff --git a/GPU/GPUTracking/Global/GPUChainITS.h b/GPU/GPUTracking/Global/GPUChainITS.h index a607f66322bab..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,10 +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 mFrameworkAllocator; }; } // namespace o2::gpu From f9f379846815b1076692725796322e9eed4910c1 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 27 Nov 2025 18:40:40 +0100 Subject: [PATCH 009/701] GPU: Hide FairLogger more effectively when compiling kernels --- GPU/Common/GPUCommonLogger.h | 2 +- GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAKernels.cu | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) 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/cuda/GPUReconstructionCUDAKernels.cu b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAKernels.cu index e8779415f0ea4..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" From 1d620f24fbe547e3f76ab3540ebc6ab0c43d711c Mon Sep 17 00:00:00 2001 From: Christian Sonnabend Date: Fri, 28 Nov 2025 10:53:22 +0100 Subject: [PATCH 010/701] [NN Clusterizer] CCDB fetching within reco workflow (#14841) * Improve GPU filling kernel speed * Adjusting parameter bounds and additional GPU kernel optimizations * Adding back if statement for early exit * const'ing + fixing CPU kernel * Remiving print statements * Fixing CI build issue * Working version of NN CCDB fetching and loading to file * Cleanup * Please consider the following formatting changes * Using char* buffer for model loading * Please consider the following formatting changes * Bug-fix * Working version of CCDB fetching and loading into ROOT class of std::vector * Please consider the following formatting changes * Disable dumpToFile by default * Moving macro, adding o2-test --------- Co-authored-by: ALICE Action Bot --- Common/ML/include/ML/OrtInterface.h | 1 + Common/ML/src/OrtInterface.cxx | 18 ++ .../TPC/base/test/testTPCCDBInterface.cxx | 1 - Detectors/TPC/calibration/CMakeLists.txt | 2 - .../TPCCalibration/NeuralNetworkClusterizer.h | 38 --- .../src/NeuralNetworkClusterizer.cxx | 48 ---- GPU/GPUTracking/CMakeLists.txt | 1 + GPU/GPUTracking/DataTypes/GPUDataTypes.h | 4 + .../DataTypes/ORTRootSerializer.cxx | 25 ++ GPU/GPUTracking/DataTypes/ORTRootSerializer.h | 43 ++++ GPU/GPUTracking/Definitions/GPUSettingsList.h | 12 +- .../GPUTrackingLinkDef_O2_DataTypes.h | 1 + .../Global/GPUChainTrackingClusterizer.cxx | 21 +- .../GPUTPCNNClusterizerHost.cxx | 2 +- .../include/GPUWorkflow/GPUWorkflowSpec.h | 7 +- GPU/Workflow/src/GPUWorkflowSpec.cxx | 129 ++++++---- GPU/Workflow/src/GPUWorkflowTPC.cxx | 28 +++ macro/CMakeLists.txt | 4 + macro/convert_onnx_to_root_serialized.C | 220 ++++++++++++++++++ 19 files changed, 459 insertions(+), 146 deletions(-) delete mode 100644 Detectors/TPC/calibration/include/TPCCalibration/NeuralNetworkClusterizer.h delete mode 100644 Detectors/TPC/calibration/src/NeuralNetworkClusterizer.cxx create mode 100644 GPU/GPUTracking/DataTypes/ORTRootSerializer.cxx create mode 100644 GPU/GPUTracking/DataTypes/ORTRootSerializer.h create mode 100644 macro/convert_onnx_to_root_serialized.C 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/Detectors/TPC/base/test/testTPCCDBInterface.cxx b/Detectors/TPC/base/test/testTPCCDBInterface.cxx index 3074c5e90a00c..5a5384a4134ed 100644 --- a/Detectors/TPC/base/test/testTPCCDBInterface.cxx +++ b/Detectors/TPC/base/test/testTPCCDBInterface.cxx @@ -22,7 +22,6 @@ // o2 includes #include "TPCBase/CDBInterface.h" -#include "TPCBase/CDBInterface.h" #include "TPCBase/CalArray.h" #include "TPCBase/CalDet.h" #include "TPCBase/Mapper.h" diff --git a/Detectors/TPC/calibration/CMakeLists.txt b/Detectors/TPC/calibration/CMakeLists.txt index 8bcb3254edb32..e5cc25230d2fc 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 @@ -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 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/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/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index 2a0b9b9edfa09..6dd718f07a9f1 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -209,6 +209,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) diff --git a/GPU/GPUTracking/DataTypes/GPUDataTypes.h b/GPU/GPUTracking/DataTypes/GPUDataTypes.h index 967d6a73914dd..8bf8084e048fd 100644 --- a/GPU/GPUTracking/DataTypes/GPUDataTypes.h +++ b/GPU/GPUTracking/DataTypes/GPUDataTypes.h @@ -85,6 +85,7 @@ class Cluster; namespace tpc { class CalibdEdxContainer; +class ORTRootSerializer; } // namespace tpc } // namespace o2 @@ -182,6 +183,9 @@ 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; + + // 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; diff --git a/GPU/GPUTracking/DataTypes/ORTRootSerializer.cxx b/GPU/GPUTracking/DataTypes/ORTRootSerializer.cxx new file mode 100644 index 0000000000000..82a8be1fdfec8 --- /dev/null +++ b/GPU/GPUTracking/DataTypes/ORTRootSerializer.cxx @@ -0,0 +1,25 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ORTRootSerializer.cxx +/// \author Christian Sonnabend + +#include "ORTRootSerializer.h" +#include + +using namespace o2::tpc; + +/// Initialize the serialization from a char* buffer containing the model +void ORTRootSerializer::setOnnxModel(const char* onnxModel, uint32_t size) +{ + 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/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index 052da8ae54c60..dc1742453ef39 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -277,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() 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/GPUChainTrackingClusterizer.cxx b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx index bfb0457744ce5..5426f0eafdad6 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx @@ -47,6 +47,7 @@ #ifdef GPUCA_HAS_ONNX #include "GPUTPCNNClusterizerKernels.h" #include "GPUTPCNNClusterizerHost.h" +#include "ORTRootSerializer.h" #endif #ifdef GPUCA_O2_LIB @@ -639,7 +640,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); @@ -677,7 +678,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); @@ -688,7 +693,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); @@ -699,7 +708,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; diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCNNClusterizerHost.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCNNClusterizerHost.cxx index 582a0c6d7435a..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") { diff --git a/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h b/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h index 160efd4048af0..d610269abca81 100644 --- a/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h +++ b/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h @@ -135,6 +135,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); @@ -230,7 +235,7 @@ class GPURecoWorkflowSpec : public o2::framework::Task uint32_t mNextThreadIndex = 0; bool mUpdateGainMapCCDB = true; std::unique_ptr mTFSettings; - std::unique_ptr mNNClusterizerSettings; + std::map nnCCDBSettings; Config mSpecConfig; std::shared_ptr mGGR; diff --git a/GPU/Workflow/src/GPUWorkflowSpec.cxx b/GPU/Workflow/src/GPUWorkflowSpec.cxx index d3d3eb14869e0..d7ea772c31653 100644 --- a/GPU/Workflow/src/GPUWorkflowSpec.cxx +++ b/GPU/Workflow/src/GPUWorkflowSpec.cxx @@ -54,6 +54,7 @@ #include "GPUO2Interface.h" #include "GPUO2InterfaceUtils.h" #include "CalibdEdxContainer.h" +#include "ORTRootSerializer.h" #include "GPUNewCalibValues.h" #include "TPCPadGainCalib.h" #include "TPCZSLinkMapping.h" @@ -78,7 +79,6 @@ #include "DetectorsRaw/RDHUtils.h" #include "ITStracking/TrackingInterface.h" #include "GPUWorkflowInternal.h" -#include "TPCCalibration/NeuralNetworkClusterizer.h" // #include "Framework/ThreadPool.h" #include @@ -133,50 +133,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 +141,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(); @@ -1124,6 +1081,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); @@ -1262,6 +1240,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..13a3c4b6162b8 100644 --- a/GPU/Workflow/src/GPUWorkflowTPC.cxx +++ b/GPU/Workflow/src/GPUWorkflowTPC.cxx @@ -49,6 +49,7 @@ #include "GPUO2Interface.h" #include "GPUO2InterfaceUtils.h" #include "CalibdEdxContainer.h" +#include "ORTRootSerializer.h" #include "GPUNewCalibValues.h" #include "TPCPadGainCalib.h" #include "TPCZSLinkMapping.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/macro/CMakeLists.txt b/macro/CMakeLists.txt index 843ad4a3be0ab..b5c51e50d3ffb 100644 --- a/macro/CMakeLists.txt +++ b/macro/CMakeLists.txt @@ -58,6 +58,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 +150,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 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); + } +} From abe259d43847d4d949db0314cece39c2be6e7932 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 27 Nov 2025 16:46:24 +0100 Subject: [PATCH 011/701] DPL: cleanup Variant copying for ArrayString Previous implementation was actually working by accident. --- Framework/Core/include/Framework/Variant.h | 25 +++++- .../Framework/VariantPropertyTreeHelpers.h | 14 ++-- Framework/Core/src/Variant.cxx | 83 +++++++++---------- .../Core/src/VariantPropertyTreeHelpers.cxx | 12 +-- 4 files changed, 75 insertions(+), 59 deletions(-) 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/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); From 93e99c90329fa96b810919304d1dc41bc98b0d3c Mon Sep 17 00:00:00 2001 From: David Rohr Date: Fri, 28 Nov 2025 14:16:50 +0100 Subject: [PATCH 012/701] GPU: Fix handling of non critical errors in double-pipeline mode --- GPU/GPUTracking/Base/GPUReconstruction.cxx | 21 ++++++++----------- .../Global/GPUChainTrackingCompression.cxx | 5 +++-- 2 files changed, 12 insertions(+), 14 deletions(-) diff --git a/GPU/GPUTracking/Base/GPUReconstruction.cxx b/GPU/GPUTracking/Base/GPUReconstruction.cxx index be2aff617ff34..cae7c5025609b 100644 --- a/GPU/GPUTracking/Base/GPUReconstruction.cxx +++ b/GPU/GPUTracking/Base/GPUReconstruction.cxx @@ -63,7 +63,7 @@ struct GPUReconstructionPipelineQueue { } // namespace struct GPUReconstructionPipelineContext { - std::queue queue; + std::queue pipelineQueue; std::mutex mutex; std::condition_variable cond; bool terminate = false; @@ -1089,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; @@ -1132,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 diff --git a/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx b/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx index e06699c0918b8..ca1352b3bda1b 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); From 1058417c46a2e306ed3c16ecf9994d60494d1513 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Fri, 28 Nov 2025 09:31:05 +0100 Subject: [PATCH 013/701] jobutils: Remove exception list from previous commands --- GPU/GPUTracking/Base/cuda/GPUReconstructionCUDArtc.cu | 2 +- Utilities/Tools/jobutils.sh | 5 +++-- 2 files changed, 4 insertions(+), 3 deletions(-) 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/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}) From 3f8a3843ae92a4287561ce08c191f493b0e6f664 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Fri, 28 Nov 2025 11:31:34 +0100 Subject: [PATCH 014/701] GPU Standalone: Add GPUCA_BUILD_DEBUG_HOSTONLY debug option --- GPU/GPUTracking/Base/hip/CMakeLists.txt | 6 ++++++ GPU/GPUTracking/Standalone/cmake/config.cmake | 1 + dependencies/FindO2GPU.cmake | 8 ++++---- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/GPU/GPUTracking/Base/hip/CMakeLists.txt b/GPU/GPUTracking/Base/hip/CMakeLists.txt index 17bbf46795761..501509d8dfcf6 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 diff --git a/GPU/GPUTracking/Standalone/cmake/config.cmake b/GPU/GPUTracking/Standalone/cmake/config.cmake index 88fe418d40e5b..ca723063b6d3b 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) diff --git a/dependencies/FindO2GPU.cmake b/dependencies/FindO2GPU.cmake index 5815657ff6386..0be3448ed6fce 100644 --- a/dependencies/FindO2GPU.cmake +++ b/dependencies/FindO2GPU.cmake @@ -10,7 +10,7 @@ # or submit itself to any jurisdiction. # NOTE!!!! - Whenever this file is changed, move it over to alidist/resources -# FindO2GPU.cmake Version 6 +# FindO2GPU.cmake Version 7 if(NOT DEFINED ENABLE_CUDA) set(ENABLE_CUDA "AUTO") @@ -166,14 +166,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( |$)") @@ -314,7 +314,7 @@ 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 From 580d11efdf866a2ede187655f612408870d81e0c Mon Sep 17 00:00:00 2001 From: shahoian Date: Thu, 27 Nov 2025 02:01:31 +0100 Subject: [PATCH 015/701] Methods for Kalman filter linearized wrt reference track --- DataFormats/Reconstruction/CMakeLists.txt | 1 + .../TrackParametrization.h | 1 + .../TrackParametrizationWithError.h | 11 +- .../src/TrackParametrization.cxx | 32 + .../src/TrackParametrizationWithError.cxx | 565 +++++++++++++++++- .../Base/include/DetectorsBase/Propagator.h | 28 + Detectors/Base/src/Propagator.cxx | 138 ++++- 7 files changed, 750 insertions(+), 26 deletions(-) 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 8cb22efd39e38..1d6c4d9f0e4ea 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h @@ -229,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/src/TrackParametrization.cxx b/DataFormats/Reconstruction/src/TrackParametrization.cxx index 7086e4d93cec8..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) diff --git a/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx b/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx index 01849bd0c9e8f..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) @@ -476,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()); @@ -493,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; @@ -604,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() @@ -1121,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/Detectors/Base/include/DetectorsBase/Propagator.h b/Detectors/Base/include/DetectorsBase/Propagator.h index d9b1522f4295b..6fa750577255d 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,6 +103,26 @@ 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; diff --git a/Detectors/Base/src/Propagator.cxx b/Detectors/Base/src/Propagator.cxx index 0763eb48ff474..02e7a05080ac5 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, From e82e4599fb904e9103661cf43c0eefb3fca41411 Mon Sep 17 00:00:00 2001 From: shahoian Date: Fri, 28 Nov 2025 17:34:48 +0100 Subject: [PATCH 016/701] Addapt ITS trackign to KF with external lin.ref At the moment reseeding of lin.ref is not done, it is taken from the existing track seed --- .../include/ITStracking/Configuration.h | 1 + .../include/ITStracking/TrackerTraits.h | 2 +- .../ITSMFT/ITS/tracking/src/TrackerTraits.cxx | 82 +++++++++++++++---- 3 files changed, 66 insertions(+), 19 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h index d7c4e27add739..3ed9e16373e22 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h @@ -66,6 +66,7 @@ struct TrackingParameters { o2::base::PropagatorImpl::MatCorrType CorrType = o2::base::PropagatorImpl::MatCorrType::USEMatCorrNONE; float MaxChi2ClusterAttachment = 60.f; float MaxChi2NDF = 30.f; + int reseedIfShorter = 7; // reseed for the final track with this and shorter length std::vector MinPt = {0.f, 0.f, 0.f, 0.f}; unsigned char StartLayerMask = 0x7F; bool FindShortTracks = false; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h index ee64cacb8fa2a..4d98b96abcd9d 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h @@ -93,7 +93,7 @@ class TrackerTraits 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); + 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/src/TrackerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx index 3a58ad1c000b7..76615bb0c5d06 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx @@ -765,21 +765,57 @@ void TrackerTraits::findRoads(const int iteration) 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); + o2::track::TrackPar linRef{seed}; + // do we want to reseed the track to get a stable reference? + /*{ + int ncl = temporaryTrack.getNClusters(); + if (ncl <= mTrkParams[0].reseedIfShorter) { + int lrMin = 999, lrMax = 0, lrMid = 0; // find midpoint + if (ncl == mTrkParams[0].NLayers) { + lrMin = 0; + lrMax = mTrkParams[0].NLayers - 1; + lrMid = (lrMin + lrMax) / 2; + } else { + for (int iL{0}; iL < nLayers; ++iL) { + if (seed.getCluster(iL) != constants::UnusedIndex) { + if (iLlrMax) { + lrMax = iL; + } + } + } + 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]); + // find the midpoint as closest to the midR + for (int iL{lrMid+1}; iL < lrMax-1; ++iL) { + auto dst = o2::gpu::GPUCommonMath::Abs(midR - mTrkParams[0].LayerRadii[iL]); + if (dst < dstMidR) { + lrMid = iL; + dstMidR = dst; + } + } + } + } + // RS TODO build seed: at the moment skip this: not sure how it will affect the GPU part) + }*/ + temporaryTrack.resetCovariance(); + temporaryTrack.setCov(temporaryTrack.getQ2Pt() * temporaryTrack.getQ2Pt() * temporaryTrack.getCov()[14], 14); + 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(); temporaryTrack.resetCovariance(); + temporaryTrack.setCov(temporaryTrack.getQ2Pt() * temporaryTrack.getQ2Pt() * temporaryTrack.getCov()[14], 14); 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; } @@ -1045,7 +1081,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 +1090,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; From 9e9f5bf948ae619357f83fd399d710d3e271cf74 Mon Sep 17 00:00:00 2001 From: shahoian Date: Fri, 28 Nov 2025 22:53:00 +0100 Subject: [PATCH 017/701] Unbinned residuals with ITS refit and PV point added With scdcalib.refitITS=true (default) the ITS track outer param will be refitted from scratch using stable Kalman filter staring from the inner param (and using it as as linearization point) and imposing PID of the global track (if any). Note that the reconstruction is done with extra syst.errors on the ITS clusters (20 microns): they can be added also for this refit with usual ITSCATrackerParam.sysErrY2... settings (though this is not necessary). The track residuals wrt the PV are added to the unbinned residuals with identified of padrow 190. Note that PV residuals interpretation differs from the rest: they are provided at the PCA of the track to PV, with PV rotated to the frame of the track. The X of the vertex in this frame is stored in the channel slot mapped from [-0.5:0.5] to short; the alpha of the track frame is stored as the angle in [-pi : pi] mapped to short: auto dy = yv - trkAtPCA.getY(), auto dz = zv - trkAtPCA.getZ(); short compXV = static_cast(xv * 0x7fff / param::MaxVtxX); // MaxVtxX = 0.5 cm mClRes.emplace_back(dy, dz, trkAtPCA.getAlpha() / TMath::Pi(), trkWorkITS.getY(), trkWorkITS.getZ(), 190, -1, compXV); --- .../calibration/SpacePoints/CMakeLists.txt | 1 + .../SpacePoints/SpacePointsCalibConfParam.h | 1 + .../SpacePoints/SpacePointsCalibParam.h | 1 + .../include/SpacePoints/TrackInterpolation.h | 4 + .../SpacePoints/src/TrackInterpolation.cxx | 107 +++++++++++++++++- 5 files changed, 111 insertions(+), 3 deletions(-) diff --git a/Detectors/TPC/calibration/SpacePoints/CMakeLists.txt b/Detectors/TPC/calibration/SpacePoints/CMakeLists.txt index 566558b7e982f..510cff4f7760c 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 diff --git a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/SpacePointsCalibConfParam.h b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/SpacePointsCalibConfParam.h index 819ca7b0ae07f..6b18df54bc903 100644 --- a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/SpacePointsCalibConfParam.h +++ b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/SpacePointsCalibConfParam.h @@ -41,6 +41,7 @@ struct SpacePointsCalibConfParam : public o2::conf::ConfigurableParamHelper 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 +340,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 diff --git a/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx b/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx index f9861bb26ff93..6da293bb33022 100644 --- a/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx +++ b/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx @@ -221,6 +221,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 +284,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); } } } @@ -360,13 +364,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; @@ -450,9 +454,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) { @@ -734,6 +741,27 @@ void TrackInterpolation::interpolateTrack(int iSeed) 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); + trackData.nExtDetResid++; + } + } break; } } @@ -826,6 +854,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]; @@ -987,7 +1018,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; @@ -1000,6 +1031,27 @@ void TrackInterpolation::extrapolateTrack(int iSeed) 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); + trackData.nExtDetResid++; + } + } break; } } @@ -1403,6 +1455,8 @@ void TrackInterpolation::reset() mGIDtables.clear(); mTrackTimes.clear(); mSeeds.clear(); + mITSRefitSeedID.clear(); + mTrackPVID.clear(); } //______________________________________________ @@ -1416,3 +1470,50 @@ 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.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; + } + } + seed = track; + // memorize that this ITS track was already refitted + mITSRefitSeedID[gid.getIndex()] = seedID; + return true; +} From 4f75f1076cf0a8f330313458f821b9e763babb1d Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Fri, 28 Nov 2025 08:39:08 +0100 Subject: [PATCH 018/701] Generalize path/file finding for external collision context --- Steer/src/CollisionContextTool.cxx | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/Steer/src/CollisionContextTool.cxx b/Steer/src/CollisionContextTool.cxx index 5287e1ef32799..a6c2b0e62e0ca 100644 --- a/Steer/src/CollisionContextTool.cxx +++ b/Steer/src/CollisionContextTool.cxx @@ -260,8 +260,15 @@ bool copy_collision_context(const std::string& external_path, int this_tf_id, in { namespace fs = std::filesystem; try { - // Construct source file path - fs::path filename = fs::path(external_path) / ("collission_context_" + std::to_string(this_tf_id) + ".root"); + 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; From 15087d6d60f9e802669c5fb343535e3968b2a317 Mon Sep 17 00:00:00 2001 From: shahoian Date: Sun, 30 Nov 2025 01:07:58 +0100 Subject: [PATCH 019/701] Possibility to select shorter ITS tracks for special output of trackMCStudy --- .../study/include/GlobalTrackingStudy/TrackMCStudyConfig.h | 1 + Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyConfig.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyConfig.h index e67abe6de3315..ed78ba2a710ec 100644 --- a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyConfig.h +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudyConfig.h @@ -34,6 +34,7 @@ struct TrackMCStudyConfig : o2::conf::ConfigurableParamHelper Date: Sun, 30 Nov 2025 01:08:50 +0100 Subject: [PATCH 020/701] Optionally reseed track before refit, shift reference to cluster --- .../include/ITStracking/Configuration.h | 3 +- .../include/ITStracking/TrackerTraits.h | 1 + .../include/ITStracking/TrackingConfigParam.h | 3 +- .../ITSMFT/ITS/tracking/src/Configuration.cxx | 3 +- .../ITSMFT/ITS/tracking/src/TrackerTraits.cxx | 101 ++++++++++-------- 5 files changed, 63 insertions(+), 48 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h index 3ed9e16373e22..9a6452270d144 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h @@ -66,9 +66,10 @@ struct TrackingParameters { o2::base::PropagatorImpl::MatCorrType CorrType = o2::base::PropagatorImpl::MatCorrType::USEMatCorrNONE; float MaxChi2ClusterAttachment = 60.f; float MaxChi2NDF = 30.f; - int reseedIfShorter = 7; // reseed for the final track with this and shorter length + 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; + 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/TrackerTraits.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h index 4d98b96abcd9d..f582b5ef3aec5 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h @@ -93,6 +93,7 @@ class TrackerTraits private: track::TrackParCov buildTrackSeed(const Cluster& cluster1, const Cluster& cluster2, const TrackingFrameInfo& tf3); + 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; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h index 6c4ecc5ab424d..2a3506f17fa2f 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h @@ -96,7 +96,8 @@ struct TrackerParamConfig : public o2::conf::ConfigurableParamHelper TrackingMode::getTrackingParameters(TrackingMode int lslot = tc.MaxTrackLength - ilg; p.MinPt[lslot] *= bFactor; } - + p.reseedIfShorter = tc.reseedIfShorter; + p.shiftRefToCluster = tc.shiftRefToCluster; p.createArtefactLabels = tc.createArtefactLabels; p.PrintMemory = tc.printMemory; diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx index 76615bb0c5d06..5c5eb47216051 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx @@ -763,55 +763,15 @@ 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.setChi2(0); - for (int iL{0}; iL < nLayers; ++iL) { - temporaryTrack.setExternalClusterIndex(iL, seed.getCluster(iL), seed.getCluster(iL) != constants::UnusedIndex); - } - o2::track::TrackPar linRef{seed}; - // do we want to reseed the track to get a stable reference? - /*{ - int ncl = temporaryTrack.getNClusters(); - if (ncl <= mTrkParams[0].reseedIfShorter) { - int lrMin = 999, lrMax = 0, lrMid = 0; // find midpoint - if (ncl == mTrkParams[0].NLayers) { - lrMin = 0; - lrMax = mTrkParams[0].NLayers - 1; - lrMid = (lrMin + lrMax) / 2; - } else { - for (int iL{0}; iL < nLayers; ++iL) { - if (seed.getCluster(iL) != constants::UnusedIndex) { - if (iLlrMax) { - lrMax = iL; - } - } - } - 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]); - // find the midpoint as closest to the midR - for (int iL{lrMid+1}; iL < lrMax-1; ++iL) { - auto dst = o2::gpu::GPUCommonMath::Abs(midR - mTrkParams[0].LayerRadii[iL]); - if (dst < dstMidR) { - lrMid = iL; - dstMidR = dst; - } - } - } - } - // RS TODO build seed: at the moment skip this: not sure how it will affect the GPU part) - }*/ - temporaryTrack.resetCovariance(); - temporaryTrack.setCov(temporaryTrack.getQ2Pt() * temporaryTrack.getQ2Pt() * temporaryTrack.getCov()[14], 14); + TrackITSExt temporaryTrack = seedTrackForRefit(trackSeeds[iSeed]); + o2::track::TrackPar linRef{temporaryTrack}; + o2::track::TrackParCov savTr = temporaryTrack; // REMOVE 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()[14], 14); temporaryTrack.setChi2(0); @@ -819,7 +779,6 @@ void TrackerTraits::findRoads(const int iteration) if (!fitSuccess || temporaryTrack.getPt() < mTrkParams[iteration].MinPt[mTrkParams[iteration].NLayers - temporaryTrack.getNClusters()]) { return 0; } - if constexpr (decltype(Tag)::value == PassMode::OnePass::value) { tracks.push_back(temporaryTrack); } else if constexpr (decltype(Tag)::value == PassMode::TwoPassCount::value) { @@ -1123,6 +1082,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); @@ -1240,6 +1203,54 @@ 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); + for (int iL = 0; iL < nLayers; ++iL) { + temporaryTrack.setExternalClusterIndex(iL, seed.getCluster(iL), seed.getCluster(iL) != constants::UnusedIndex); + } + int ncl = temporaryTrack.getNClusters(); + if (ncl < mTrkParams[0].reseedIfShorter) { // reseed with circle passing via edges and the midpoint + int lrMin = 999, lrMax = 0, lrMid = 0; + if (ncl == mTrkParams[0].NLayers) { + lrMin = 0; + lrMax = mTrkParams[0].NLayers - 1; + lrMid = (lrMin + lrMax) / 2; + } else { + for (int iL = 0; iL < nLayers; ++iL) { + if (seed.getCluster(iL) != constants::UnusedIndex) { + if (iL < lrMin) { + lrMin = iL; + } + if (iL > lrMax) { + lrMax = iL; + } + } + } + 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); + temporaryTrack.setQ2Pt(-temporaryTrack.getQ2Pt()); // we are calling buildTrackSeed with the clusters order opposite to what it expects + temporaryTrack.setSnp(-temporaryTrack.getSnp()); // we are calling buildTrackSeed with the clusters order opposite to what it expects + } + temporaryTrack.resetCovariance(); + temporaryTrack.setCov(temporaryTrack.getQ2Pt() * temporaryTrack.getQ2Pt() * temporaryTrack.getCov()[14], 14); + 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 From d99e306eaa32d4878708aaea8b03329b499f7cce Mon Sep 17 00:00:00 2001 From: shahoian Date: Sun, 30 Nov 2025 01:09:37 +0100 Subject: [PATCH 021/701] Optionally shift ITS track reference to cluster after update --- .../include/SpacePoints/SpacePointsCalibConfParam.h | 1 + .../TPC/calibration/SpacePoints/src/TrackInterpolation.cxx | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/SpacePointsCalibConfParam.h b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/SpacePointsCalibConfParam.h index 6b18df54bc903..8b884209dd697 100644 --- a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/SpacePointsCalibConfParam.h +++ b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/SpacePointsCalibConfParam.h @@ -42,6 +42,7 @@ struct SpacePointsCalibConfParam : public o2::conf::ConfigurableParamHelpershiftRefToCluster) { + refLin.setY(posTF[0]); + refLin.setZ(posTF[1]); + } } seed = track; // memorize that this ITS track was already refitted From 22e2e612661a00ff6f3fd6e6ce91a6f2e53b4b03 Mon Sep 17 00:00:00 2001 From: Felix Weiglhofer Date: Thu, 20 Nov 2025 10:56:51 +0100 Subject: [PATCH 022/701] GPU: Implement TPC timebin cut. --- .../Global/GPUChainTrackingClusterizer.cxx | 14 +- .../TPCClusterFinder/GPUTPCCFDecodeZS.cxx | 307 +++++++++--------- .../TPCClusterFinder/GPUTPCCFDecodeZS.h | 27 +- GPU/GPUTracking/kernels.cmake | 6 +- 4 files changed, 185 insertions(+), 169 deletions(-) diff --git a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx index 5426f0eafdad6..7b0c54cda5cb1 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx @@ -588,7 +588,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; // TODO: Implement time bin cut fultering + int32_t tpcTimeBinCut = (mUpdateNewCalibObjects && mNewCalibValues->newTPCTimeBinCut) ? mNewCalibValues->tpcTimeBinCut : param().tpcCutTimeBin; + mRec->PushNonPersistentMemory(qStr2Tag("TPCCLUST")); const auto& threadContext = GetThreadContext(); const bool doGPU = GetRecoStepsGPU() & RecoStep::TPCClusterFinding; @@ -911,13 +912,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); @@ -1273,6 +1274,11 @@ 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 diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx index 54af72f08a432..7ba32bd43275b 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx @@ -36,12 +36,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 +173,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 +211,69 @@ 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 { #ifdef GPUCA_GPUCODE - DecodeTBMultiThread( - clusterer, - iThread, - smem, - adcData, - nAdc, - channelMask, - timeBin, - decHdr->cruID, - tbHdr->fecInPartition, - pageDigitOffset); + DecodeTBMultiThread( + smem, + ctx, + adcData, + nAdc, + channelMask, + timeBin, + decHdr->cruID, + tbHdr->fecInPartition); #else // CPU - DecodeTBSingleThread( - clusterer, - adcData, - nAdc, - channelMask, - timeBin, - decHdr->cruID, - tbHdr->fecInPartition, - pageDigitOffset); + DecodeTBSingleThread( + ctx, + adcData, + nAdc, + channelMask, + timeBin, + decHdr->cruID, + tbHdr->fecInPartition); #endif - pageDigitOffset += nAdc; + } + + ctx.pageDigitOffset += nAdc; } // for (uint32_t t = 0; t < decHdr->nTimebinHeaders; t++) #ifdef GPUCA_CHECK_TPCZS_CORRUPTION @@ -286,20 +286,20 @@ GPUd() size_t GPUTPCCFDecodeZSLink::DecodePage(GPUSharedMemory& smem, processorT #endif*/ } #endif - return pageDigitOffset; + + return ctx.pageDigitOffset; } GPUd() void GPUTPCCFDecodeZSLink::DecodeTBSingleThread( - processorType& clusterer, + 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) { - const CfFragment& fragment = clusterer.mPmemory->fragment; + const CfFragment& fragment = ctx.clusterer.mPmemory->fragment; if constexpr (TPCZSHDRV2::TIGHTLY_PACKED_V3) { @@ -317,9 +317,9 @@ GPUd() void GPUTPCCFDecodeZSLink::DecodeTBSingleThread( } // Unpack data for cluster finder - o2::tpc::PadPos padAndRow = GetPadAndRowFromFEC(clusterer, cru, rawFECChannel, fecInPartition); + o2::tpc::PadPos padAndRow = GetPadAndRowFromFEC(ctx.clusterer, cru, rawFECChannel, fecInPartition); - WriteCharge(clusterer, byte, padAndRow, fragment.toLocal(timeBin), pageDigitOffset + nSamplesWritten); + WriteCharge(ctx.clusterer, byte, padAndRow, fragment.toLocal(timeBin), ctx.pageDigitOffset + nSamplesWritten); byte = byte >> DECODE_BITS; bits -= DECODE_BITS; @@ -337,31 +337,29 @@ GPUd() void GPUTPCCFDecodeZSLink::DecodeTBSingleThread( uint32_t adc = (adcData64[j / TPCZSHDRV2::SAMPLESPER64BIT] >> ((j % TPCZSHDRV2::SAMPLESPER64BIT) * DECODE_BITS)) & DECODE_MASK; - o2::tpc::PadPos padAndRow = GetPadAndRowFromFEC(clusterer, cru, rawFECChannel, fecInPartition); + o2::tpc::PadPos padAndRow = GetPadAndRowFromFEC(ctx.clusterer, cru, rawFECChannel, fecInPartition); float charge = ADCToFloat(adc, DECODE_MASK, DECODE_BITS_FACTOR); - WriteCharge(clusterer, charge, padAndRow, fragment.toLocal(timeBin), pageDigitOffset + j); + WriteCharge(ctx.clusterer, charge, padAndRow, fragment.toLocal(timeBin), ctx.pageDigitOffset + j); rawFECChannel++; } } } GPUd() void GPUTPCCFDecodeZSLink::DecodeTBMultiThread( - processorType& clusterer, - int32_t iThread, 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; @@ -429,10 +427,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 +460,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 +505,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,12 +594,12 @@ 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; @@ -598,11 +607,11 @@ GPUd() uint32_t GPUTPCCFDecodeZSDenseLink::DecodePage(GPUSharedMemory& smem, pro 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); + 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; @@ -612,7 +621,7 @@ GPUd() uint32_t GPUTPCCFDecodeZSDenseLink::DecodePage(GPUSharedMemory& smem, pro const bool extendsToNextPage = decHeader->flags & TPCZSHDRV2::ZSFlags::payloadExtendsToNextPage; - ConsumeBytes(page, decHeader->firstZSDataOffset - sizeof(o2::header::RAWDataHeader)); + ConsumeBytes(ctx.page, decHeader->firstZSDataOffset - sizeof(o2::header::RAWDataHeader)); int err = GPUErrors::ERROR_NONE; @@ -626,7 +635,7 @@ GPUd() uint32_t GPUTPCCFDecodeZSDenseLink::DecodePage(GPUSharedMemory& smem, pro for (uint16_t i = 0; i < decHeader->nTimebinHeaders && !err; i++) { - ptrdiff_t sizeLeftInPage = payloadEnd - page; + ptrdiff_t sizeLeftInPage = payloadEnd - ctx.page; if (sizeLeftInPage <= 0) { err = GPUErrors::ERROR_TPCZS_PAGE_OVERFLOW; break; @@ -642,13 +651,13 @@ GPUd() uint32_t GPUTPCCFDecodeZSDenseLink::DecodePage(GPUSharedMemory& smem, pro } if ((uint16_t)(raw::RDHUtils::getPageCounter(rawDataHeader) + 1) == raw::RDHUtils::getPageCounter(nextPage)) { - nSamplesWrittenTB = DecodeTB(clusterer, smem, iThread, page, pageDigitOffset, rawDataHeader, firstHBF, decHeader->cruID, nSamplesLeftInPage, payloadEnd, nextPage); + nSamplesWrittenTB = DecodeTB(smem, ctx, rawDataHeader, decHeader->cruID, nSamplesLeftInPage, payloadEnd, nextPage); } else { err = GPUErrors::ERROR_TPCZS_INCOMPLETE_HBF; break; } } else { - nSamplesWrittenTB = DecodeTB(clusterer, smem, iThread, page, pageDigitOffset, rawDataHeader, firstHBF, decHeader->cruID, nSamplesLeftInPage, payloadEnd, nextPage); + nSamplesWrittenTB = DecodeTB(smem, ctx, rawDataHeader, decHeader->cruID, nSamplesLeftInPage, payloadEnd, nextPage); } // Abort decoding the page if an error was detected. @@ -658,33 +667,33 @@ GPUd() uint32_t GPUTPCCFDecodeZSDenseLink::DecodePage(GPUSharedMemory& smem, pro } nSamplesWritten += nSamplesWrittenTB; - pageDigitOffset += nSamplesWrittenTB; + ctx.pageDigitOffset += nSamplesWrittenTB; } // for (uint16_t i = 0; i < decHeader->nTimebinHeaders; i++) if (nSamplesWritten != nSamplesInPage) { if (nSamplesWritten < nSamplesInPage) { - pageDigitOffset += FillWithInvalid(clusterer, iThread, nThreads, pageDigitOffset, nSamplesInPage - nSamplesWritten); + 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 } - if (iThread == 0 && err) { + if (ctx.iThread == 0 && err) { [[maybe_unused]] bool dumpPage = false; if (err == GPUErrors::ERROR_TPCZS_VERSION_MISMATCH) { - clusterer.raiseError(err, decHeader->version, ZSVersionDenseLinkBased); + ctx.clusterer.raiseError(err, decHeader->version, ZSVersionDenseLinkBased); } else if (err == GPUErrors::ERROR_TPCZS_INVALID_MAGIC_WORD) { - clusterer.raiseError(err, decHeader->magicWord); + ctx.clusterer.raiseError(err, decHeader->magicWord); } else if (err == GPUErrors::ERROR_TPCZS_INCOMPLETE_HBF) { - clusterer.raiseError(err, clusterer.mISector * 1000 + decHeader->cruID, raw::RDHUtils::getPageCounter(rawDataHeader), raw::RDHUtils::getPageCounter(nextPage)); + 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) { - clusterer.raiseError(err, extendsToNextPage); + ctx.clusterer.raiseError(err, extendsToNextPage); dumpPage = true; } else if (err == GPUErrors::ERROR_TPCZS_INVALID_NADC) { - clusterer.raiseError(err, nSamplesInPage, nSamplesWritten, extendsToNextPage); + ctx.clusterer.raiseError(err, nSamplesInPage, nSamplesWritten, extendsToNextPage); dumpPage = true; } else { - clusterer.raiseError(GPUErrors::ERROR_TPCZS_UNKNOWN, err); + ctx.clusterer.raiseError(GPUErrors::ERROR_TPCZS_UNKNOWN, err); } #ifdef GPUCA_CHECK_TPCZS_CORRUPTION @@ -700,18 +709,14 @@ GPUd() uint32_t GPUTPCCFDecodeZSDenseLink::DecodePage(GPUSharedMemory& smem, pro #endif } - return pageDigitOffset; + return ctx.pageDigitOffset; } template GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTB( - processorType& clusterer, [[maybe_unused]] GPUSharedMemory& smem, - int32_t iThread, - const uint8_t*& page, - uint32_t pageDigitOffset, + DecodeCtx& ctx, const header::RAWDataHeader* rawDataHeader, - int32_t firstHBF, int32_t cru, uint16_t nSamplesLeftInPage, const uint8_t* payloadEnd, @@ -719,11 +724,11 @@ GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTB( { if constexpr (DecodeInParallel) { - return DecodeTBMultiThread(clusterer, smem, iThread, page, pageDigitOffset, rawDataHeader, firstHBF, cru, nSamplesLeftInPage, payloadEnd, nextPage); + return DecodeTBMultiThread(smem, ctx, rawDataHeader, cru, nSamplesLeftInPage, payloadEnd, nextPage); } else { int16_t nSamplesWritten = 0; - if (iThread == 0) { - nSamplesWritten = DecodeTBSingleThread(clusterer, page, pageDigitOffset, rawDataHeader, firstHBF, cru, nSamplesLeftInPage, payloadEnd, nextPage); + if (ctx.iThread == 0) { + nSamplesWritten = DecodeTBSingleThread(ctx, rawDataHeader, cru, nSamplesLeftInPage, payloadEnd, nextPage); } return warp_broadcast(nSamplesWritten, 0); } @@ -731,13 +736,9 @@ GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTB( template GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( - processorType& clusterer, 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, uint16_t nSamplesLeftInPage, const uint8_t* payloadEnd, @@ -766,45 +767,45 @@ GPUd() int16_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; 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); - 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); + 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); @@ -816,10 +817,10 @@ GPUd() int16_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; } @@ -833,31 +834,31 @@ GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( // 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(page, (nSamplesInTB * DECODE_BITS + 7) / 8); - MAYBE_PAGE_OVERFLOW(page); + 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++; @@ -866,10 +867,10 @@ GPUd() int16_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) @@ -884,15 +885,12 @@ GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( template GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTBSingleThread( - processorType& clusterer, - const uint8_t*& page, - uint32_t pageDigitOffset, + DecodeCtx& ctx, const header::RAWDataHeader* rawDataHeader, - int32_t firstHBF, int32_t cru, uint16_t nSamplesLeftInPage, - [[maybe_unused]] const uint8_t* payloadEnd, - [[maybe_unused]] const uint8_t* nextPage) + const uint8_t* payloadEnd, + const uint8_t* nextPage) { #define MAYBE_PAGE_OVERFLOW(pagePtr) \ if constexpr (PayloadExtendsToNextPage) { \ @@ -909,28 +907,28 @@ GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTBSingleThread( using zerosupp_link_based::ChannelPerTBHeader; - const CfFragment& fragment = clusterer.mPmemory->fragment; + const CfFragment& fragment = ctx.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); + 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; // 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); linkIds[iLink] = timebinLinkHeaderStart & 0b00011111; @@ -938,15 +936,15 @@ GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTBSingleThread( uint16_t bitmaskL2 = 0x0FFF; 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); } 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); + nSamplesInTB += CAMath::Popcount(*Peek(ctx.page)); + channelMasks[10 * iLink + i] = ConsumeByte(ctx.page); + MAYBE_PAGE_OVERFLOW(ctx.page); } } @@ -956,11 +954,14 @@ GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTBSingleThread( return -GPUErrors::ERROR_TPCZS_INVALID_NADC; } - const uint8_t* adcData = ConsumeBytes(page, (nSamplesInTB * DECODE_BITS + 7) / 8); - MAYBE_PAGE_OVERFLOW(page); + const uint8_t* adcData = ConsumeBytes(ctx.page, (nSamplesInTB * DECODE_BITS + 7) / 8); + MAYBE_PAGE_OVERFLOW(ctx.page); + + bool discardTimeBin = not fragment.contains(timeBin); + discardTimeBin |= (ctx.tpcTimeBinCut > 0 && timeBin > ctx.tpcTimeBinCut); - if (not fragment.contains(timeBin)) { - return FillWithInvalid(clusterer, 0, 1, pageDigitOffset, nSamplesInTB); + if (discardTimeBin) { + return FillWithInvalid(ctx.clusterer, 0, 1, ctx.pageDigitOffset, nSamplesInTB); } // Unpack ADC @@ -982,10 +983,10 @@ GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTBSingleThread( int32_t rawFECChannelLink = rawFECChannel % ChannelPerTBHeader; // Unpack data for cluster finder - o2::tpc::PadPos padAndRow = GetPadAndRowFromFEC(clusterer, cru, rawFECChannelLink, linkIds[iLink]); + o2::tpc::PadPos padAndRow = GetPadAndRowFromFEC(ctx.clusterer, cru, rawFECChannelLink, linkIds[iLink]); float charge = ADCToFloat(byte, DECODE_MASK, DECODE_BITS_FACTOR); - WriteCharge(clusterer, charge, padAndRow, fragment.toLocal(timeBin), pageDigitOffset + nSamplesWritten); + WriteCharge(ctx.clusterer, charge, padAndRow, fragment.toLocal(timeBin), ctx.pageDigitOffset + nSamplesWritten); byte >>= DECODE_BITS; bits -= DECODE_BITS; diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h index 4697462a8c504..750df643f2d10 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h @@ -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) @@ -77,8 +77,17 @@ class GPUTPCCFDecodeZSLinkBase : public GPUKernelTemplate 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,13 @@ 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 DecodeTBSingleThread(DecodeCtx& ctx, const uint8_t* adcData, uint32_t nAdc, const uint32_t* channelMask, int32_t timeBin, int32_t cru, int32_t fecInPartition); + GPUd() static void DecodeTBMultiThread(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,7 +172,7 @@ 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); @@ -171,13 +180,13 @@ class GPUTPCCFDecodeZSDenseLink : public GPUTPCCFDecodeZSLinkBase // 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 int16_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, uint16_t nSamplesLeftInPage, 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); template - GPUd() static int16_t DecodeTBSingleThread(processorType& clusterer, const uint8_t*& page, uint32_t pageDigitOffset, const header::RAWDataHeader* rawDataHeader, int32_t firstHBF, int32_t cru, uint16_t nSamplesLeftInPage, const uint8_t* payloadEnd, const uint8_t* nextPage); + GPUd() static int16_t DecodeTBSingleThread(DecodeCtx& ctx, const header::RAWDataHeader* rawDataHeader, int32_t cru, uint16_t nSamplesLeftInPage, const uint8_t* payloadEnd, const uint8_t* nextPage); template - GPUd() static int16_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, uint16_t nSamplesLeftInPage, const uint8_t* payloadEnd, const uint8_t* nextPage); + GPUd() static int16_t DecodeTBMultiThread(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/kernels.cmake b/GPU/GPUTracking/kernels.cmake index e7271a9affbba..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 ERRORS" 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) From 15b5e54028b335b0ef3aee75d18ed3a625ac9202 Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Fri, 28 Nov 2025 22:46:57 +0100 Subject: [PATCH 023/701] Case-insensitive index access * fix the Binding node always using #_Table_ instead of _Label_ for custom index colums * use case-insensitive comparison for looking up index columns in soa::Table and arrow::Table, as the custom-declared index columns may have inconsistent capitalization in their names --- Framework/Core/include/Framework/ASoA.h | 12 ++++++++-- Framework/Core/src/ArrowTableSlicingCache.cxx | 24 ++++++++++++++++--- 2 files changed, 31 insertions(+), 5 deletions(-) diff --git a/Framework/Core/include/Framework/ASoA.h b/Framework/Core/include/Framework/ASoA.h index a30363605af36..43079a4634e97 100644 --- a/Framework/Core/include/Framework/ASoA.h +++ b/Framework/Core/include/Framework/ASoA.h @@ -1376,7 +1376,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 @@ -2866,7 +2874,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, "") diff --git a/Framework/Core/src/ArrowTableSlicingCache.cxx b/Framework/Core/src/ArrowTableSlicingCache.cxx index 373c98516bb09..72ced958c510c 100644 --- a/Framework/Core/src/ArrowTableSlicingCache.cxx +++ b/Framework/Core/src/ArrowTableSlicingCache.cxx @@ -19,6 +19,24 @@ namespace o2::framework { +namespace { +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)); +} +} + void updatePairList(Cache& list, std::string const& binding, std::string const& key, bool enabled = true) { auto locate = std::find_if(list.begin(), list.end(), [&binding, &key](auto const& entry) { return (entry.binding == binding) && (entry.key == key); }); @@ -99,7 +117,7 @@ arrow::Status ArrowTableSlicingCache::updateCacheEntry(int pos, std::shared_ptr< 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) { @@ -155,7 +173,7 @@ arrow::Status ArrowTableSlicingCache::updateCacheEntryUnsorted(int pos, const st 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()); @@ -252,7 +270,7 @@ 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 column = o2::framework::GetColumnByNameCI(input, key); auto array0 = static_cast>(column->chunk(0)->data()); int32_t prev = 0; int32_t cur = array0.Value(0); From 687291f970115c895f5be331860a7ca4997b54bf Mon Sep 17 00:00:00 2001 From: ALICE Action Bot Date: Fri, 28 Nov 2025 21:52:32 +0000 Subject: [PATCH 024/701] Please consider the following formatting changes --- Framework/Core/src/ArrowTableSlicingCache.cxx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Framework/Core/src/ArrowTableSlicingCache.cxx b/Framework/Core/src/ArrowTableSlicingCache.cxx index 72ced958c510c..75b4bbfac701d 100644 --- a/Framework/Core/src/ArrowTableSlicingCache.cxx +++ b/Framework/Core/src/ArrowTableSlicingCache.cxx @@ -19,23 +19,23 @@ namespace o2::framework { -namespace { +namespace +{ 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){ + 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){ + [](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, std::string const& binding, std::string const& key, bool enabled = true) { From 0ec9c27391866861153935d71a2e53090cc5c277 Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Mon, 1 Dec 2025 13:50:31 +0100 Subject: [PATCH 025/701] Exclude TPC CalDet test in dataflow builds --- Detectors/TPC/base/CMakeLists.txt | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Detectors/TPC/base/CMakeLists.txt b/Detectors/TPC/base/CMakeLists.txt index 8c796d7e4ff13..a82214d8c070f 100644 --- a/Detectors/TPC/base/CMakeLists.txt +++ b/Detectors/TPC/base/CMakeLists.txt @@ -80,12 +80,16 @@ o2_add_test(Base SOURCES test/testTPCBase.cxx LABELS tpc) -o2_add_test(CalDet +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::TPCBase SOURCES test/testTPCCalDet.cxx ENVIRONMENT O2_ROOT=${CMAKE_BINARY_DIR}/stage LABELS tpc) +endif() o2_add_test(Mapper COMPONENT_NAME tpc From 652c89bcfd4525c0e704e75b0b18c72a2259ca9d Mon Sep 17 00:00:00 2001 From: shahoian Date: Sun, 30 Nov 2025 21:36:33 +0100 Subject: [PATCH 026/701] Use linearization ref. in AB refit --- Detectors/GlobalTracking/src/MatchTPCITS.cxx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Detectors/GlobalTracking/src/MatchTPCITS.cxx b/Detectors/GlobalTracking/src/MatchTPCITS.cxx index 9c1a32a262e51..5f99ad2202073 100644 --- a/Detectors/GlobalTracking/src/MatchTPCITS.cxx +++ b/Detectors/GlobalTracking/src/MatchTPCITS.cxx @@ -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; From a8770865dead616d23268adc421c84eb9c0dbc4e Mon Sep 17 00:00:00 2001 From: David Rohr Date: Mon, 1 Dec 2025 14:55:37 +0100 Subject: [PATCH 027/701] GPU TPC: Don't cut on goodLeg flag, now marked on track level --- GPU/GPUTracking/Merger/GPUTPCGMO2Output.cxx | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) 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; From a807b70ee17ff00c0cfdf17175f2ad934f249788 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Tue, 2 Dec 2025 10:58:39 +0100 Subject: [PATCH 028/701] DPL: remove direct dependency on fair::mq::Device from the FairMQDeviceProxy --- .../include/Framework/FairMQDeviceProxy.h | 4 ++- Framework/Core/src/CommonMessageBackends.cxx | 13 ++++++++- .../Core/src/ExternalFairMQDeviceProxy.cxx | 13 ++++++++- Framework/Core/src/FairMQDeviceProxy.cxx | 28 ++++++++----------- 4 files changed, 38 insertions(+), 20 deletions(-) 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/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/ExternalFairMQDeviceProxy.cxx b/Framework/Core/src/ExternalFairMQDeviceProxy.cxx index 99176de0d9db6..b4bfc991db9ae 100644 --- a/Framework/Core/src/ExternalFairMQDeviceProxy.cxx +++ b/Framework/Core/src/ExternalFairMQDeviceProxy.cxx @@ -1090,7 +1090,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]() { 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 From 3c161fc076203b1f7d357b216e3e545304091e67 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Fri, 28 Nov 2025 23:14:19 +0100 Subject: [PATCH 029/701] FST: Switch to using jobutils2.sh --- prodtests/full_system_test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prodtests/full_system_test.sh b/prodtests/full_system_test.sh index 6100e3ed87a2c..a799afbbbfd3d 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 From 69c43d59ae93ac77dd7bb1484e2ec6bb0e935cc0 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Tue, 2 Dec 2025 20:57:29 +0100 Subject: [PATCH 030/701] GPU QA: Fix some histogram names / titles / axes --- GPU/GPUTracking/qa/GPUQA.cxx | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) diff --git a/GPU/GPUTracking/qa/GPUQA.cxx b/GPU/GPUTracking/qa/GPUQA.cxx index ce4a4a81db02c..9047f0bfce6f2 100644 --- a/GPU/GPUTracking/qa/GPUQA.cxx +++ b/GPU/GPUTracking/qa/GPUQA.cxx @@ -176,6 +176,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; @@ -525,7 +526,7 @@ int32_t GPUQA::InitQACreateHistograms() } createHist(mPadRow[0], "padrow0", "padrow0", GPUCA_ROW_COUNT, 0, GPUCA_ROW_COUNT - 1, GPUCA_ROW_COUNT, 0, GPUCA_ROW_COUNT - 1); - createHist(mPadRow[1], "padrow0", "padrow0", 100.f, -0.2f, 0.2f, GPUCA_ROW_COUNT, 0, GPUCA_ROW_COUNT - 1); + createHist(mPadRow[1], "padrow1", "padrow1", 100.f, -0.2f, 0.2f, GPUCA_ROW_COUNT, 0, GPUCA_ROW_COUNT - 1); } if (mQATasks & taskTrackStatistics) { @@ -2863,14 +2864,16 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) } mPPadRow[i]->cd(); e->SetOption("colz"); - e->GetXaxis()->SetTitle("First MC Pad Row"); + e->SetTitle("First Track Pad Row"); + e->GetXaxis()->SetTitle(i ? "Phi (sector)" : "First MC Pad Row"); e->GetYaxis()->SetTitle("First Pad Row"); e->Draw(); mCPadRow[i]->cd(); - snprintf(name, 2048, "plots/padrow%d.pdf", i); + static const constexpr char* PADROW_NAMES[2] = {"MC", "Phi"}; + snprintf(name, 2048, "plots/padRow%s.pdf", PADROW_NAMES[i]); mCPadRow[i]->Print(name); if (mConfig.writeRootFiles) { - snprintf(name, 2048, "plots/padrow%d.root", i); + snprintf(name, 2048, "plots/padRow%s.root", PADROW_NAMES[i]); mCPadRow[i]->Print(name); } } @@ -3052,13 +3055,14 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) mClRej[i]->Write(); } mPClRej[i]->cd(); + mClRej[i]->SetTitle(REJECTED_NAMES[i]); mClRej[i]->SetOption("colz"); mClRej[i]->Draw(); mCClRej[i]->cd(); - snprintf(name, 2048, "plots/clustersRej%d.pdf", i); + snprintf(name, 2048, "plots/clustersRej%d%s.pdf", i, REJECTED_NAMES[i]); mCClRej[i]->Print(name); if (mConfig.writeRootFiles) { - snprintf(name, 2048, "plots/clustersRej%d.root", i); + snprintf(name, 2048, "plots/clustersRej%d%s.root", i, REJECTED_NAMES[i]); mCClRej[i]->Print(name); } } @@ -3092,11 +3096,14 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) delete proj2; e->SetMinimum(-0.02); e->SetMaximum(0.22); + e->SetTitle("Rejected Clusters"); + e->GetXaxis()->SetTitle("Pad Row"); + e->GetYaxis()->SetTitle("Rejected Clusters (fraction)"); e->Draw(k == 0 ? "" : "same"); } - mPClRejP->Print("plots/clustersRejP.pdf"); // TODO: Add option to write pngs + mPClRejP->Print("plots/clustersRejProjected.pdf"); // TODO: Add option to write pngs if (mConfig.writeRootFiles) { - mPClRejP->Print("plots/clustersRejP.root"); + mPClRejP->Print("plots/clustersRejProjected.root"); } } } From c2b6369b11dcdaa85fb8574ed20a6262c688f070 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Tue, 2 Dec 2025 21:24:24 +0100 Subject: [PATCH 031/701] GPU QA: Add padrowphi1 histogram --- GPU/GPUTracking/qa/GPUQA.cxx | 14 +++++++++----- GPU/GPUTracking/qa/GPUQA.h | 6 +++--- 2 files changed, 12 insertions(+), 8 deletions(-) diff --git a/GPU/GPUTracking/qa/GPUQA.cxx b/GPU/GPUTracking/qa/GPUQA.cxx index 9047f0bfce6f2..53a4c7de99296 100644 --- a/GPU/GPUTracking/qa/GPUQA.cxx +++ b/GPU/GPUTracking/qa/GPUQA.cxx @@ -527,6 +527,7 @@ int32_t GPUQA::InitQACreateHistograms() createHist(mPadRow[0], "padrow0", "padrow0", GPUCA_ROW_COUNT, 0, GPUCA_ROW_COUNT - 1, GPUCA_ROW_COUNT, 0, GPUCA_ROW_COUNT - 1); createHist(mPadRow[1], "padrow1", "padrow1", 100.f, -0.2f, 0.2f, GPUCA_ROW_COUNT, 0, GPUCA_ROW_COUNT - 1); + createHist(mPadRow[2], "padrow2", "padrow2", 100.f, -0.2f, 0.2f, GPUCA_ROW_COUNT, 0, GPUCA_ROW_COUNT - 1); } if (mQATasks & taskTrackStatistics) { @@ -1114,8 +1115,11 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx const auto& trk = mTracking->mIOPtrs.mergedTracks[i]; if (trk.OK() && lowestPadRow[i] != 255 && trk.NClustersFitted() > 70 && CAMath::Abs(trk.GetParam().GetQPt()) < 0.5) { int32_t lowestRow = CAMath::Min(mTracking->mIOPtrs.mergedTrackHits[trk.FirstClusterRef()].row, mTracking->mIOPtrs.mergedTrackHits[trk.FirstClusterRef() + trk.NClusters() - 1].row); - mPadRow[0]->Fill((float)lowestPadRow[i], (float)lowestRow, 1.f); + 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] == 0 && lowestRow != 0) { + mPadRow[2]->Fill(CAMath::ATan2(trk.GetParam().GetY(), trk.GetParam().GetX()), lowestRow, 1.f); + } } } } @@ -2278,7 +2282,7 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) mPClRejP = createGarbageCollected("p0", "", 0.0, 0.0, 1.0, 1.0); mPClRejP->Draw(); - for (int32_t i = 0; i < 2; i++) { + for (int32_t i = 0; i < 3; i++) { snprintf(name, 2048, "cpadrow%d", i); mCPadRow[i] = createGarbageCollected(name, "First Track Pad Row", 0, 0, 700, 700. * 2. / 3.); mCPadRow[i]->cd(); @@ -2857,19 +2861,19 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) } } - for (int32_t i = 0; i < 2; i++) { + for (int32_t i = 0; i < 3; i++) { auto* e = mPadRow[i]; if (tout && !mConfig.inputHistogramsOnly) { e->Write(); } mPPadRow[i]->cd(); e->SetOption("colz"); - e->SetTitle("First Track Pad Row"); + e->SetTitle(i == 2 ? "First Track Pad Row (row_{MC} = 0, row_{trk} #ne 0)" : "First Track Pad Row"); e->GetXaxis()->SetTitle(i ? "Phi (sector)" : "First MC Pad Row"); e->GetYaxis()->SetTitle("First Pad Row"); e->Draw(); mCPadRow[i]->cd(); - static const constexpr char* PADROW_NAMES[2] = {"MC", "Phi"}; + static const constexpr char* PADROW_NAMES[3] = {"MC", "Phi", "Phi1"}; snprintf(name, 2048, "plots/padRow%s.pdf", PADROW_NAMES[i]); mCPadRow[i]->Print(name); if (mConfig.writeRootFiles) { diff --git a/GPU/GPUTracking/qa/GPUQA.h b/GPU/GPUTracking/qa/GPUQA.h index f6225c2d38276..54d1ceed9d365 100644 --- a/GPU/GPUTracking/qa/GPUQA.h +++ b/GPU/GPUTracking/qa/GPUQA.h @@ -323,9 +323,9 @@ class GPUQA TPad* mPClRej[3]; TPad* mPClRejP; - TH2F* mPadRow[2]; - TCanvas* mCPadRow[2]; - TPad* mPPadRow[2]; + TH2F* mPadRow[3]; + TCanvas* mCPadRow[3]; + TPad* mPPadRow[3]; std::vector mHistClusterCount; From 1f0b9466f009e7afe348e8e4436e86e1935b020a Mon Sep 17 00:00:00 2001 From: David Rohr Date: Tue, 2 Dec 2025 21:44:30 +0100 Subject: [PATCH 032/701] GPU QA: add plotsDir option --- GPU/GPUTracking/Definitions/GPUSettingsList.h | 1 + GPU/GPUTracking/qa/GPUQA.cxx | 46 +++++++++---------- 2 files changed, 24 insertions(+), 23 deletions(-) diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index dc1742453ef39..163a01eb1a2c7 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -530,6 +530,7 @@ AddOption(minNClFindable, uint32_t, 70, "", 0, "Minimum number of (weighted) MC 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") AddOption(perfFigure, int32_t, 0, "", 0, "Show as performance figure, positive value for MC, negative value for data") +AddOption(plotsDir, std::string, "plots", "", 0, "Directory to write plots to") AddShortcut("compare", 0, "--QAinput", "Compare QA histograms", "--qa", "--QAinputHistogramsOnly") AddHelp("help", 'h') EndConfig() diff --git a/GPU/GPUTracking/qa/GPUQA.cxx b/GPU/GPUTracking/qa/GPUQA.cxx index 53a4c7de99296..ccda942254a6f 100644 --- a/GPU/GPUTracking/qa/GPUQA.cxx +++ b/GPU/GPUTracking/qa/GPUQA.cxx @@ -830,7 +830,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 @@ -2394,9 +2394,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])); + mCEff[ii]->Print(Form("%s/eff_vs_%s.pdf", mConfig.plotsDir.c_str(), 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.root", mConfig.plotsDir.c_str(), VSPARAMETER_NAMES[ii])); } } } @@ -2632,9 +2632,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])); + can->Print(Form(p ? "%s/pull_vs_%s.pdf" : "%s/res_vs_%s.pdf", mConfig.plotsDir.c_str(), 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.root" : "%s/res_vs_%s.root", mConfig.plotsDir.c_str(), VSPARAMETER_NAMES[ii])); } } } @@ -2703,9 +2703,9 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) continue; } - can->Print(p ? "plots/pull_integral.pdf" : "plots/res_integral.pdf"); + can->Print(Form(p ? "%s/pull_integral.pdf" : "%s/res_integral.pdf", mConfig.plotsDir.c_str())); if (mConfig.writeRootFiles) { - can->Print(p ? "plots/pull_integral.root" : "plots/res_integral.root"); + can->Print(Form(p ? "%s/pull_integral.root" : "%s/res_integral.root", mConfig.plotsDir.c_str())); } } } @@ -2855,9 +2855,9 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) } 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"); + 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.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.root" : i == 1 ? "%s/clusters_relative.root" : "%s/clusters.root", mConfig.plotsDir.c_str())); } } @@ -2874,10 +2874,10 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) e->Draw(); mCPadRow[i]->cd(); static const constexpr char* PADROW_NAMES[3] = {"MC", "Phi", "Phi1"}; - snprintf(name, 2048, "plots/padRow%s.pdf", PADROW_NAMES[i]); + snprintf(name, 2048, "%s/padRow%s.pdf", mConfig.plotsDir.c_str(), PADROW_NAMES[i]); mCPadRow[i]->Print(name); if (mConfig.writeRootFiles) { - snprintf(name, 2048, "plots/padRow%s.root", PADROW_NAMES[i]); + snprintf(name, 2048, "%s/padRow%s.root", mConfig.plotsDir.c_str(), PADROW_NAMES[i]); mCPadRow[i]->Print(name); } } @@ -2941,9 +2941,9 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) mLTracks->Draw(); doPerfFigure(0.63, 0.7, 0.030); mCTracks->cd(); - mCTracks->Print("plots/tracks.pdf"); + mCTracks->Print(Form("%s/tracks.pdf", mConfig.plotsDir.c_str())); if (mConfig.writeRootFiles) { - mCTracks->Print("plots/tracks.root"); + mCTracks->Print(Form("%s/tracks.root", mConfig.plotsDir.c_str())); } for (int32_t i = 0; i < 2; i++) { @@ -2987,10 +2987,10 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) mLT0[i]->Draw(); doPerfFigure(0.63, 0.7, 0.030); mCT0[i]->cd(); - snprintf(name, 2048, "plots/t0%s.pdf", i ? "_res" : ""); + snprintf(name, 2048, "%s/t0%s.pdf", mConfig.plotsDir.c_str(), i ? "_res" : ""); mCT0[i]->Print(name); if (mConfig.writeRootFiles) { - snprintf(name, 2048, "plots/t0%s.root", i ? "_res" : ""); + snprintf(name, 2048, "%s/t0%s.root", mConfig.plotsDir.c_str(), i ? "_res" : ""); mCT0[i]->Print(name); } @@ -3034,10 +3034,10 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) mLNCl[i]->Draw(); doPerfFigure(0.6, 0.7, 0.030); mCNCl[i]->cd(); - snprintf(name, 2048, "plots/nClusters%s.pdf", i ? "_corrected" : ""); + snprintf(name, 2048, "%s/nClusters%s.pdf", mConfig.plotsDir.c_str(), i ? "_corrected" : ""); mCNCl[i]->Print(name); if (mConfig.writeRootFiles) { - snprintf(name, 2048, "plots/nClusters%s.root", i ? "_corrected" : ""); + snprintf(name, 2048, "%s/nClusters%s.root", mConfig.plotsDir.c_str(), i ? "_corrected" : ""); mCNCl[i]->Print(name); } } @@ -3046,9 +3046,9 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) mClXY->SetOption("colz"); mClXY->Draw(); mCClXY->cd(); - mCClXY->Print("plots/clustersXY.pdf"); + mCClXY->Print(Form("%s/clustersXY.pdf", mConfig.plotsDir.c_str())); if (mConfig.writeRootFiles) { - mCClXY->Print("plots/clustersXY.root"); + mCClXY->Print(Form("%s/clustersXY.root", mConfig.plotsDir.c_str())); } if (mQATasks & taskClusterCounts) { @@ -3063,10 +3063,10 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) mClRej[i]->SetOption("colz"); mClRej[i]->Draw(); mCClRej[i]->cd(); - snprintf(name, 2048, "plots/clustersRej%d%s.pdf", i, REJECTED_NAMES[i]); + snprintf(name, 2048, "%s/clustersRej%d%s.pdf", mConfig.plotsDir.c_str(), i, REJECTED_NAMES[i]); mCClRej[i]->Print(name); if (mConfig.writeRootFiles) { - snprintf(name, 2048, "plots/clustersRej%d%s.root", i, REJECTED_NAMES[i]); + snprintf(name, 2048, "%s/clustersRej%d%s.root", mConfig.plotsDir.c_str(), i, REJECTED_NAMES[i]); mCClRej[i]->Print(name); } } @@ -3105,9 +3105,9 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) e->GetYaxis()->SetTitle("Rejected Clusters (fraction)"); e->Draw(k == 0 ? "" : "same"); } - mPClRejP->Print("plots/clustersRejProjected.pdf"); // TODO: Add option to write pngs + mPClRejP->Print(Form("%s/clustersRejProjected.pdf", mConfig.plotsDir.c_str())); // TODO: Add option to write pngs if (mConfig.writeRootFiles) { - mPClRejP->Print("plots/clustersRejProjected.root"); + mPClRejP->Print(Form("%s/clustersRejProjected.root", mConfig.plotsDir.c_str())); } } } From 863af5c096baf78c65c978eb16063565810c4323 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Tue, 2 Dec 2025 21:56:57 +0100 Subject: [PATCH 033/701] GPU QA: Add option to write other files but ROOT --- GPU/GPUTracking/Definitions/GPUSettingsList.h | 2 +- GPU/GPUTracking/qa/GPUQA.cxx | 52 +++++++++---------- 2 files changed, 27 insertions(+), 27 deletions(-) diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index 163a01eb1a2c7..5a075bf7f9a02 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -516,7 +516,7 @@ 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(writeRootFiles, bool, false, "", 0, "Create ROOT canvas files") +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") 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") diff --git a/GPU/GPUTracking/qa/GPUQA.cxx b/GPU/GPUTracking/qa/GPUQA.cxx index ccda942254a6f..6adcd975ec45f 100644 --- a/GPU/GPUTracking/qa/GPUQA.cxx +++ b/GPU/GPUTracking/qa/GPUQA.cxx @@ -2084,11 +2084,11 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) } std::vector colorNums(COLORCOUNT); - if (!(qcout || mConfig.writeRootFiles)) { + 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 || mConfig.writeRootFiles) ? defaultColorNums[i] : mColors[i]->GetNumber(); + colorNums[i] = (qcout || mConfig.writeFileExt == "root" || mConfig.writeFileExt == "C") ? defaultColorNums[i] : mColors[i]->GetNumber(); } bool mcAvail = mcPresent(); @@ -2395,8 +2395,8 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) } doPerfFigure(0.2, 0.295, 0.025); mCEff[ii]->Print(Form("%s/eff_vs_%s.pdf", mConfig.plotsDir.c_str(), VSPARAMETER_NAMES[ii])); - if (mConfig.writeRootFiles) { - mCEff[ii]->Print(Form("%s/eff_vs_%s.root", 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())); } } } @@ -2633,8 +2633,8 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) } doPerfFigure(0.2, 0.295, 0.025); can->Print(Form(p ? "%s/pull_vs_%s.pdf" : "%s/res_vs_%s.pdf", mConfig.plotsDir.c_str(), VSPARAMETER_NAMES[ii])); - if (mConfig.writeRootFiles) { - can->Print(Form(p ? "%s/pull_vs_%s.root" : "%s/res_vs_%s.root", 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())); } } } @@ -2704,8 +2704,8 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) } can->Print(Form(p ? "%s/pull_integral.pdf" : "%s/res_integral.pdf", mConfig.plotsDir.c_str())); - if (mConfig.writeRootFiles) { - can->Print(Form(p ? "%s/pull_integral.root" : "%s/res_integral.root", 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())); } } } @@ -2856,8 +2856,8 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) doPerfFigure(i == 0 ? 0.37 : (i == 1 ? 0.34 : 0.6), 0.295, 0.030); mCClust[i]->cd(); 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.writeRootFiles) { - mCClust[i]->Print(Form(i == 2 ? "%s/clusters_integral.root" : i == 1 ? "%s/clusters_relative.root" : "%s/clusters.root", 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())); } } @@ -2876,8 +2876,8 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) static const constexpr char* PADROW_NAMES[3] = {"MC", "Phi", "Phi1"}; snprintf(name, 2048, "%s/padRow%s.pdf", mConfig.plotsDir.c_str(), PADROW_NAMES[i]); mCPadRow[i]->Print(name); - if (mConfig.writeRootFiles) { - snprintf(name, 2048, "%s/padRow%s.root", mConfig.plotsDir.c_str(), PADROW_NAMES[i]); + if (mConfig.writeFileExt != "") { + snprintf(name, 2048, "%s/padRow%s.%s", mConfig.plotsDir.c_str(), PADROW_NAMES[i], mConfig.writeFileExt.c_str()); mCPadRow[i]->Print(name); } } @@ -2942,8 +2942,8 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) doPerfFigure(0.63, 0.7, 0.030); mCTracks->cd(); mCTracks->Print(Form("%s/tracks.pdf", mConfig.plotsDir.c_str())); - if (mConfig.writeRootFiles) { - mCTracks->Print(Form("%s/tracks.root", mConfig.plotsDir.c_str())); + if (mConfig.writeFileExt != "") { + mCTracks->Print(Form("%s/tracks.%s", mConfig.plotsDir.c_str(), mConfig.writeFileExt.c_str())); } for (int32_t i = 0; i < 2; i++) { @@ -2989,8 +2989,8 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) mCT0[i]->cd(); snprintf(name, 2048, "%s/t0%s.pdf", mConfig.plotsDir.c_str(), i ? "_res" : ""); mCT0[i]->Print(name); - if (mConfig.writeRootFiles) { - snprintf(name, 2048, "%s/t0%s.root", mConfig.plotsDir.c_str(), i ? "_res" : ""); + if (mConfig.writeFileExt != "") { + snprintf(name, 2048, "%s/t0%s.%s", mConfig.plotsDir.c_str(), i ? "_res" : "", mConfig.writeFileExt.c_str()); mCT0[i]->Print(name); } @@ -3036,8 +3036,8 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) mCNCl[i]->cd(); snprintf(name, 2048, "%s/nClusters%s.pdf", mConfig.plotsDir.c_str(), i ? "_corrected" : ""); mCNCl[i]->Print(name); - if (mConfig.writeRootFiles) { - snprintf(name, 2048, "%s/nClusters%s.root", mConfig.plotsDir.c_str(), i ? "_corrected" : ""); + if (mConfig.writeFileExt != "") { + snprintf(name, 2048, "%s/nClusters%s.%s", mConfig.plotsDir.c_str(), i ? "_corrected" : "", mConfig.writeFileExt.c_str()); mCNCl[i]->Print(name); } } @@ -3047,8 +3047,8 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) mClXY->Draw(); mCClXY->cd(); mCClXY->Print(Form("%s/clustersXY.pdf", mConfig.plotsDir.c_str())); - if (mConfig.writeRootFiles) { - mCClXY->Print(Form("%s/clustersXY.root", mConfig.plotsDir.c_str())); + if (mConfig.writeFileExt != "") { + mCClXY->Print(Form("%s/clustersXY.%s", mConfig.plotsDir.c_str(), mConfig.writeFileExt.c_str())); } if (mQATasks & taskClusterCounts) { @@ -3065,8 +3065,8 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) mCClRej[i]->cd(); snprintf(name, 2048, "%s/clustersRej%d%s.pdf", mConfig.plotsDir.c_str(), i, REJECTED_NAMES[i]); mCClRej[i]->Print(name); - if (mConfig.writeRootFiles) { - snprintf(name, 2048, "%s/clustersRej%d%s.root", mConfig.plotsDir.c_str(), i, REJECTED_NAMES[i]); + if (mConfig.writeFileExt != "") { + snprintf(name, 2048, "%s/clustersRej%d%s.%s", mConfig.plotsDir.c_str(), i, REJECTED_NAMES[i], mConfig.writeFileExt.c_str()); mCClRej[i]->Print(name); } } @@ -3105,9 +3105,9 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) e->GetYaxis()->SetTitle("Rejected Clusters (fraction)"); e->Draw(k == 0 ? "" : "same"); } - mPClRejP->Print(Form("%s/clustersRejProjected.pdf", mConfig.plotsDir.c_str())); // TODO: Add option to write pngs - if (mConfig.writeRootFiles) { - mPClRejP->Print(Form("%s/clustersRejProjected.root", mConfig.plotsDir.c_str())); + 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())); } } } @@ -3131,7 +3131,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); } From f75706c4f8a2ba6a6c792a101946f7c6092b624c Mon Sep 17 00:00:00 2001 From: David Rohr Date: Tue, 2 Dec 2025 22:18:23 +0100 Subject: [PATCH 034/701] GPU QA: Get rid of several sprintf --- GPU/GPUTracking/qa/GPUQA.cxx | 110 +++++++++++++---------------------- 1 file changed, 39 insertions(+), 71 deletions(-) diff --git a/GPU/GPUTracking/qa/GPUQA.cxx b/GPU/GPUTracking/qa/GPUQA.cxx index 6adcd975ec45f..689dc20cb1606 100644 --- a/GPU/GPUTracking/qa/GPUQA.cxx +++ b/GPU/GPUTracking/qa/GPUQA.cxx @@ -2118,10 +2118,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); @@ -2144,15 +2142,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); @@ -2185,16 +2180,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); @@ -2227,8 +2218,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(); @@ -2240,7 +2231,7 @@ 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 = createGarbageCollected("ctrackspt", "ctrackspt", 0, 0, 700, 700. * 2. / 3.); mCTracks->cd(); mPTracks = createGarbageCollected("p0", "", 0.0, 0.0, 1.0, 1.0); mPTracks->Draw(); @@ -2249,7 +2240,7 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) 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(); @@ -2257,7 +2248,7 @@ 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(); @@ -2265,26 +2256,26 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) 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(); for (int32_t i = 0; i < 3; i++) { snprintf(name, 2048, "cnclrej%d", i); - mCClRej[i] = createGarbageCollected(name, i == 0 ? "Number of clusters" : (i == 1 ? "Rejected Clusters" : "Fraction of Rejected Clusters"), 0, 0, 700, 700. * 2. / 3.); + 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", "Fraction of Rejected Clusters", 0, 0, 700, 700. * 2. / 3.); + 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(); for (int32_t i = 0; i < 3; i++) { snprintf(name, 2048, "cpadrow%d", i); - mCPadRow[i] = createGarbageCollected(name, "First Track Pad Row", 0, 0, 700, 700. * 2. / 3.); + 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(); @@ -2370,8 +2361,7 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) 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) { @@ -2503,10 +2493,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(); @@ -2557,8 +2545,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) { @@ -2598,12 +2585,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"); } } } @@ -2823,8 +2805,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) { @@ -2868,17 +2849,15 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) } mPPadRow[i]->cd(); e->SetOption("colz"); - e->SetTitle(i == 2 ? "First Track Pad Row (row_{MC} = 0, row_{trk} #ne 0)" : "First Track Pad Row"); - e->GetXaxis()->SetTitle(i ? "Phi (sector)" : "First MC Pad Row"); + e->SetTitle(i == 2 ? "First Track Pad Row (row_{MC} = 0, row_{trk} > 0)" : "First Track Pad Row"); + e->GetXaxis()->SetTitle(i ? "#Phi (sector)" : "First MC Pad Row"); e->GetYaxis()->SetTitle("First Pad Row"); e->Draw(); mCPadRow[i]->cd(); static const constexpr char* PADROW_NAMES[3] = {"MC", "Phi", "Phi1"}; - snprintf(name, 2048, "%s/padRow%s.pdf", mConfig.plotsDir.c_str(), PADROW_NAMES[i]); - mCPadRow[i]->Print(name); + mCPadRow[i]->Print(Form("%s/padRow%s.pdf", mConfig.plotsDir.c_str(), PADROW_NAMES[i])); if (mConfig.writeFileExt != "") { - snprintf(name, 2048, "%s/padRow%s.%s", mConfig.plotsDir.c_str(), PADROW_NAMES[i], mConfig.writeFileExt.c_str()); - mCPadRow[i]->Print(name); + mCPadRow[i]->Print(Form("%s/padRow%s.%s", mConfig.plotsDir.c_str(), PADROW_NAMES[i], mConfig.writeFileExt.c_str())); } } } @@ -2935,8 +2914,7 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) e->SetLineColor(colorNums[k % COLORCOUNT]); e->Draw(k == 0 ? "" : "same"); GetName(fname, k, mConfig.inputHistogramsOnly); - snprintf(name, 2048, mConfig.inputHistogramsOnly ? "%s" : "%sTrack #it{p}_{T}", fname); - mLTracks->AddEntry(e, name, "l"); + mLTracks->AddEntry(e, Form(mConfig.inputHistogramsOnly ? "%s" : "%sTrack #it{p}_{T}", fname), "l"); } mLTracks->Draw(); doPerfFigure(0.63, 0.7, 0.030); @@ -2981,17 +2959,14 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) e->SetLineColor(colorNums[k % COLORCOUNT]); e->Draw(k == 0 ? "" : "same"); GetName(fname, k, mConfig.inputHistogramsOnly); - snprintf(name, 2048, mConfig.inputHistogramsOnly ? "%s (%s)" : "%sTrack t_{0} %s", fname, i ? "" : "resolution"); - mLT0[i]->AddEntry(e, name, "l"); + 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, "%s/t0%s.pdf", mConfig.plotsDir.c_str(), i ? "_res" : ""); - mCT0[i]->Print(name); + mCT0[i]->Print(Form("%s/t0%s.pdf", mConfig.plotsDir.c_str(), i ? "_res" : "")); if (mConfig.writeFileExt != "") { - snprintf(name, 2048, "%s/t0%s.%s", mConfig.plotsDir.c_str(), i ? "_res" : "", mConfig.writeFileExt.c_str()); - mCT0[i]->Print(name); + mCT0[i]->Print(Form("%s/t0%s.%s", mConfig.plotsDir.c_str(), i ? "_res" : "", mConfig.writeFileExt.c_str())); } tmpMax = 0.; @@ -3028,17 +3003,14 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) e->SetLineColor(colorNums[k % COLORCOUNT]); e->Draw(k == 0 ? "" : "same"); GetName(fname, k, mConfig.inputHistogramsOnly); - snprintf(name, 2048, mConfig.inputHistogramsOnly ? "%s" : (i ? "%sN_{Clusters}" : "%sN_{Rows with Clusters}"), fname); - mLNCl[i]->AddEntry(e, name, "l"); + 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, "%s/nClusters%s.pdf", mConfig.plotsDir.c_str(), i ? "_corrected" : ""); - mCNCl[i]->Print(name); + mCNCl[i]->Print(Form("%s/nClusters%s.pdf", mConfig.plotsDir.c_str(), i ? "_corrected" : "")); if (mConfig.writeFileExt != "") { - snprintf(name, 2048, "%s/nClusters%s.%s", mConfig.plotsDir.c_str(), i ? "_corrected" : "", mConfig.writeFileExt.c_str()); - mCNCl[i]->Print(name); + mCNCl[i]->Print(Form("%s/nClusters%s.%s", mConfig.plotsDir.c_str(), i ? "_corrected" : "", mConfig.writeFileExt.c_str())); } } @@ -3063,11 +3035,9 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) mClRej[i]->SetOption("colz"); mClRej[i]->Draw(); mCClRej[i]->cd(); - snprintf(name, 2048, "%s/clustersRej%d%s.pdf", mConfig.plotsDir.c_str(), i, REJECTED_NAMES[i]); - mCClRej[i]->Print(name); + mCClRej[i]->Print(Form("%s/clustersRej%d%s.pdf", mConfig.plotsDir.c_str(), i, REJECTED_NAMES[i])); if (mConfig.writeFileExt != "") { - snprintf(name, 2048, "%s/clustersRej%d%s.%s", mConfig.plotsDir.c_str(), i, REJECTED_NAMES[i], mConfig.writeFileExt.c_str()); - mCClRej[i]->Print(name); + mCClRej[i]->Print(Form("%s/clustersRej%d%s.%s", mConfig.plotsDir.c_str(), i, REJECTED_NAMES[i], mConfig.writeFileExt.c_str())); } } @@ -3077,15 +3047,13 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) if (GetHist(tmp, tin, k, nNewInput) == nullptr) { continue; } - snprintf(name, 2048, "clrejptmp1%d", k); // TODO: Clean up names, and how names are written to char arrays - TH1D* proj1 = tmp->ProjectionY(name); + 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; } - snprintf(name, 2048, "clrejptmp2%d", k); // TODO: Clean up names, and how names are written to char arrays - TH1D* proj2 = tmp->ProjectionY(name); + TH1D* proj2 = tmp->ProjectionY(Form("clrejptmp2%d", k)); proj2->SetDirectory(nullptr); auto* e = mClRejP; @@ -3131,7 +3099,7 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) if (!qcout) { clearGarbagageCollector(); } - GPUInfo("GPU TPC QA histograms have been written to pdf%s%s files", (mConfig.writeFileExt == "" ? "" : " and ", mConfig.writeFileExt.c_str()); + GPUInfo("GPU TPC QA histograms have been written to pdf%s%s files", mConfig.writeFileExt == "" ? "" : " and ", mConfig.writeFileExt.c_str()); gErrorIgnoreLevel = oldRootIgnoreLevel; return (0); } From c93179f2f80d6ab30e03cdd00d1fe68b89ed9dbf Mon Sep 17 00:00:00 2001 From: Felix Weiglhofer Date: Mon, 1 Dec 2025 14:14:33 +0100 Subject: [PATCH 035/701] GPU: Unify GPU and CPU versions of TPC ZS decoders. --- .../TPCClusterFinder/GPUTPCCFDecodeZS.cxx | 221 +----------------- .../TPCClusterFinder/GPUTPCCFDecodeZS.h | 11 +- 2 files changed, 7 insertions(+), 225 deletions(-) diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx index 7ba32bd43275b..67a3083d4a1ce 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx @@ -251,8 +251,7 @@ GPUd() size_t GPUTPCCFDecodeZSLink::DecodePage(GPUSharedMemory& smem, DecodeCtx& if (discardTimeBin) { FillWithInvalid(ctx.clusterer, ctx.iThread, ctx.nThreads, ctx.pageDigitOffset, nAdc); } else { -#ifdef GPUCA_GPUCODE - DecodeTBMultiThread( + DecodeTB( smem, ctx, adcData, @@ -261,16 +260,6 @@ GPUd() size_t GPUTPCCFDecodeZSLink::DecodePage(GPUSharedMemory& smem, DecodeCtx& timeBin, decHdr->cruID, tbHdr->fecInPartition); -#else // CPU - DecodeTBSingleThread( - ctx, - adcData, - nAdc, - channelMask, - timeBin, - decHdr->cruID, - tbHdr->fecInPartition); -#endif } ctx.pageDigitOffset += nAdc; @@ -290,62 +279,7 @@ GPUd() size_t GPUTPCCFDecodeZSLink::DecodePage(GPUSharedMemory& smem, DecodeCtx& return ctx.pageDigitOffset; } -GPUd() void GPUTPCCFDecodeZSLink::DecodeTBSingleThread( - DecodeCtx& ctx, - const uint8_t* adcData, - uint32_t nAdc, - const uint32_t* channelMask, - int32_t timeBin, - int32_t cru, - int32_t fecInPartition) -{ - const CfFragment& fragment = ctx.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(ctx.clusterer, cru, rawFECChannel, fecInPartition); - - WriteCharge(ctx.clusterer, byte, padAndRow, fragment.toLocal(timeBin), ctx.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(ctx.clusterer, cru, rawFECChannel, fecInPartition); - float charge = ADCToFloat(adc, DECODE_MASK, DECODE_BITS_FACTOR); - WriteCharge(ctx.clusterer, charge, padAndRow, fragment.toLocal(timeBin), ctx.pageDigitOffset + j); - rawFECChannel++; - } - } -} - -GPUd() void GPUTPCCFDecodeZSLink::DecodeTBMultiThread( +GPUd() void GPUTPCCFDecodeZSLink::DecodeTB( GPUSharedMemory& smem, DecodeCtx& ctx, const uint8_t* adcData, @@ -601,12 +535,6 @@ GPUd() void GPUTPCCFDecodeZSDenseLink::Thread<0>(int32_t nBlocks, int32_t nThrea 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 = ctx.page; const auto* rawDataHeader = Peek(ctx.page); @@ -651,13 +579,13 @@ GPUd() uint32_t GPUTPCCFDecodeZSDenseLink::DecodePage(GPUSharedMemory& smem, Dec } if ((uint16_t)(raw::RDHUtils::getPageCounter(rawDataHeader) + 1) == raw::RDHUtils::getPageCounter(nextPage)) { - nSamplesWrittenTB = DecodeTB(smem, ctx, rawDataHeader, decHeader->cruID, nSamplesLeftInPage, payloadEnd, nextPage); + nSamplesWrittenTB = DecodeTB(smem, ctx, rawDataHeader, decHeader->cruID, nSamplesLeftInPage, payloadEnd, nextPage); } else { err = GPUErrors::ERROR_TPCZS_INCOMPLETE_HBF; break; } } else { - nSamplesWrittenTB = DecodeTB(smem, ctx, rawDataHeader, decHeader->cruID, nSamplesLeftInPage, payloadEnd, nextPage); + nSamplesWrittenTB = DecodeTB(smem, ctx, rawDataHeader, decHeader->cruID, nSamplesLeftInPage, payloadEnd, nextPage); } // Abort decoding the page if an error was detected. @@ -712,30 +640,8 @@ GPUd() uint32_t GPUTPCCFDecodeZSDenseLink::DecodePage(GPUSharedMemory& smem, Dec return ctx.pageDigitOffset; } -template -GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTB( - [[maybe_unused]] GPUSharedMemory& smem, - DecodeCtx& ctx, - const header::RAWDataHeader* rawDataHeader, - int32_t cru, - uint16_t nSamplesLeftInPage, - const uint8_t* payloadEnd, - const uint8_t* nextPage) -{ - - if constexpr (DecodeInParallel) { - return DecodeTBMultiThread(smem, ctx, rawDataHeader, cru, nSamplesLeftInPage, payloadEnd, nextPage); - } else { - int16_t nSamplesWritten = 0; - if (ctx.iThread == 0) { - nSamplesWritten = DecodeTBSingleThread(ctx, rawDataHeader, cru, nSamplesLeftInPage, payloadEnd, nextPage); - } - return warp_broadcast(nSamplesWritten, 0); - } -} - template -GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( +GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTB( GPUSharedMemory& smem, DecodeCtx& ctx, const header::RAWDataHeader* rawDataHeader, @@ -883,123 +789,6 @@ GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTBMultiThread( #undef MAYBE_PAGE_OVERFLOW } -template -GPUd() int16_t GPUTPCCFDecodeZSDenseLink::DecodeTBSingleThread( - DecodeCtx& ctx, - const header::RAWDataHeader* rawDataHeader, - int32_t cru, - uint16_t nSamplesLeftInPage, - const uint8_t* payloadEnd, - 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 { \ - if (pagePtr > payloadEnd) { \ - return -GPUErrors::ERROR_TPCZS_PAGE_OVERFLOW; \ - } \ - } - - using zerosupp_link_based::ChannelPerTBHeader; - - const CfFragment& fragment = ctx.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(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) - ctx.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(ctx.page); - MAYBE_PAGE_OVERFLOW(ctx.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(ctx.page)); - MAYBE_PAGE_OVERFLOW(ctx.page); - } - - for (int32_t i = 0; i < 10; i++) { - if (bitmaskL2 & 1 << i) { - nSamplesInTB += CAMath::Popcount(*Peek(ctx.page)); - channelMasks[10 * iLink + i] = ConsumeByte(ctx.page); - MAYBE_PAGE_OVERFLOW(ctx.page); - } - } - - } // for (uint8_t iLink = 0; iLink < nLinksInTimebin; iLink++) - - if (nSamplesInTB > nSamplesLeftInPage) { - return -GPUErrors::ERROR_TPCZS_INVALID_NADC; - } - - const uint8_t* adcData = ConsumeBytes(ctx.page, (nSamplesInTB * DECODE_BITS + 7) / 8); - MAYBE_PAGE_OVERFLOW(ctx.page); - - bool discardTimeBin = not fragment.contains(timeBin); - discardTimeBin |= (ctx.tpcTimeBinCut > 0 && timeBin > ctx.tpcTimeBinCut); - - if (discardTimeBin) { - return FillWithInvalid(ctx.clusterer, 0, 1, ctx.pageDigitOffset, 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(ctx.clusterer, cru, rawFECChannelLink, linkIds[iLink]); - - float charge = ADCToFloat(byte, DECODE_MASK, DECODE_BITS_FACTOR); - WriteCharge(ctx.clusterer, charge, padAndRow, fragment.toLocal(timeBin), ctx.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) - - 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 750df643f2d10..b8d8765e0b2f2 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h @@ -148,8 +148,7 @@ class GPUTPCCFDecodeZSLink : public GPUTPCCFDecodeZSLinkBase 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(DecodeCtx& ctx, const uint8_t* adcData, uint32_t nAdc, const uint32_t* channelMask, int32_t timeBin, int32_t cru, int32_t fecInPartition); - GPUd() static void DecodeTBMultiThread(GPUSharedMemory& smem, DecodeCtx& ctx, const uint8_t* adcData, uint32_t nAdc, const uint32_t* channelMask, int32_t timeBin, int32_t cru, int32_t fecInPartition); + 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 @@ -179,14 +178,8 @@ class GPUTPCCFDecodeZSDenseLink : public GPUTPCCFDecodeZSLinkBase // 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 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); - template - GPUd() static int16_t DecodeTBSingleThread(DecodeCtx& ctx, const header::RAWDataHeader* rawDataHeader, int32_t cru, uint16_t nSamplesLeftInPage, const uint8_t* payloadEnd, const uint8_t* nextPage); - - template - GPUd() static int16_t DecodeTBMultiThread(GPUSharedMemory& smem, DecodeCtx& ctx, const header::RAWDataHeader* rawDataHeader, int32_t cru, uint16_t nSamplesLeftInPage, 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 From 3563a504b0b34b3e6243a897639b123dbba20ad0 Mon Sep 17 00:00:00 2001 From: Felix Weiglhofer Date: Mon, 1 Dec 2025 14:15:48 +0100 Subject: [PATCH 036/701] GPU: Remove unused code in ZS decoder. --- .../TPCClusterFinder/GPUTPCCFDecodeZS.cxx | 33 +------------------ .../TPCClusterFinder/GPUTPCCFDecodeZS.h | 2 +- 2 files changed, 2 insertions(+), 33 deletions(-) diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.cxx index 67a3083d4a1ce..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; @@ -302,26 +303,6 @@ GPUd() void GPUTPCCFDecodeZSLink::DecodeTB( 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; } @@ -331,28 +312,16 @@ GPUd() void GPUTPCCFDecodeZSLink::DecodeTB( 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; diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h index b8d8765e0b2f2..c633a5ebc2774 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 { From 1108c25afcf1d842c4c139c5a64a692fa7a79054 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Mon, 1 Dec 2025 14:22:39 +0100 Subject: [PATCH 037/701] ITS: GPU: adapt seed refit Signed-off-by: Felix Schlepper --- .../GPU/ITStrackingGPU/TrackingKernels.h | 10 +- .../tracking/GPU/cuda/TrackerTraitsGPU.cxx | 16 +- .../ITS/tracking/GPU/cuda/TrackingKernels.cu | 196 +++++++++++++----- .../include/ITStracking/Configuration.h | 4 +- .../include/ITStracking/TrackerTraits.h | 2 +- .../ITSMFT/ITS/tracking/src/Configuration.cxx | 4 +- .../ITSMFT/ITS/tracking/src/TrackerTraits.cxx | 40 ++-- 7 files changed, 180 insertions(+), 92 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h index 69d6799686654..a4e4328b3aa22 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h @@ -209,13 +209,17 @@ void processNeighboursHandler(const int startLayer, template void trackSeedHandler(CellSeed* trackSeeds, const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, o2::its::TrackITSExt* tracks, - std::vector& minPtsHost, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, 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 shiftRefToCluster, const o2::base::Propagator* propagator, const o2::base::PropagatorF::MatCorrType matCorrType, const int nBlocks, diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx index 05810f0074811..f94147747a475 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx @@ -325,17 +325,21 @@ void TrackerTraitsGPU::findRoads(const int iteration) 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, + trackSeedHandler(mTimeFrameGPU->getDeviceTrackSeeds(), // CellSeed* + mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), // TrackingFrameInfo** + mTimeFrameGPU->getDeviceArrayUnsortedClusters(), // Cluster** + mTimeFrameGPU->getDeviceTrackITSExt(), // o2::its::TrackITSExt* + this->mTrkParams[iteration].LayerRadii, // const std::vector& + this->mTrkParams[iteration].MinPt, // const std::vector& 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 + this->mTrkParams[0].ReseedIfShorter, + this->mTrkParams[0].ShiftRefToCluster, + mTimeFrameGPU->getDevicePropagator(), // const o2::base::Propagator* propagator + this->mTrkParams[0].CorrType, // o2::base::PropagatorImpl::MatCorrType conf.nBlocksTracksSeeds[iteration], conf.nThreadsTracksSeeds[iteration]); diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu index 71f1281401e9d..27d7fa97f7b5c 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -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,7 +126,8 @@ 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 x1 = cluster1.xCoordinate * ca + cluster1.yCoordinate * sa; @@ -115,12 +141,13 @@ GPUdii() o2::track::TrackParCov buildTrackSeed(const Cluster& cluster1, 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 sign = (reverse) ? -1.f : 1.f; + const float tgp = zeroField ? sign * o2::gpu::CAMath::ATan2(y3 - y1, x3 - x1) : 1.f; + const float crv = sign * (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 q2pt = zeroField ? sign / 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, @@ -132,6 +159,52 @@ GPUdii() o2::track::TrackParCov buildTrackSeed(const Cluster& cluster1, 0.f, 0.f, 0.f, 0.f, sg2q2pt}); } +template +GPUdii() TrackITSExt seedTrackForRefit(const CellSeed& seed, + const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, + const float* layerRadii, + const float bz, + const int reseedIfShorter) +{ + TrackITSExt temporaryTrack(seed); + int lrMin = nLayers, lrMax = 0, lrMid = 0; + for (int iL{0}; iL < nLayers; ++iL) { + const int idx = seed.getCluster(iL); + temporaryTrack.setExternalClusterIndex(iL, idx, idx != constants::UnusedIndex); + if (idx != constants::UnusedIndex) { + // TODO only works if does not have holes + lrMin = o2::gpu::CAMath::Min(lrMin, iL); + lrMax = o2::gpu::CAMath::Max(lrMax, iL); + } + } + const int ncl = temporaryTrack.getNClusters(); + if (ncl < reseedIfShorter && ncl > 0) { // need to check if there are any clusters since we keep invalidate seeeds around + if (ncl == nLayers) { + lrMin = 0; + lrMax = nLayers - 1; + lrMid = (lrMin + lrMax) / 2; + } else { + lrMid = lrMin + 1; + float midR = 0.5f * (layerRadii[lrMax] + layerRadii[lrMin]), dstMidR = o2::gpu::CAMath::Abs(midR - layerRadii[lrMid]); + for (int iL = lrMid + 1; iL < lrMax; ++iL) { // find the midpoint as closest to the midR + auto dst = o2::gpu::GPUCommonMath::Abs(midR - layerRadii[iL]); + if (dst < dstMidR) { + lrMid = iL; + dstMidR = dst; + } + } + } + const auto& cluster0_tf = foundTrackingFrameInfo[lrMin][seed.getCluster(lrMin)]; + const auto& cluster1_gl = unsortedClusters[lrMid][seed.getCluster(lrMid)]; + const auto& cluster2_gl = unsortedClusters[lrMax][seed.getCluster(lrMax)]; + temporaryTrack.getParamIn() = buildTrackSeed(cluster2_gl, cluster1_gl, cluster0_tf, bz, true); + } + temporaryTrack.resetCovariance(); + temporaryTrack.setCov(temporaryTrack.getQ2Pt() * temporaryTrack.getQ2Pt() * temporaryTrack.getCov()[o2::track::CovLabels::kSigQ2Pt2], o2::track::CovLabels::kSigQ2Pt2); + return temporaryTrack; +} + struct sort_tracklets { GPUhd() bool operator()(const Tracklet& a, const Tracklet& b) { @@ -206,27 +279,23 @@ template GPUg() void __launch_bounds__(256, 1) fitTrackSeedsKernel( CellSeed* trackSeeds, const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, o2::its::TrackITSExt* tracks, + 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 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); - } + 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 +307,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,7 +329,9 @@ 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; } @@ -1088,34 +1162,42 @@ void processNeighboursHandler(const int startLayer, template void trackSeedHandler(CellSeed* trackSeeds, const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, o2::its::TrackITSExt* tracks, - std::vector& minPtsHost, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, 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 shiftRefToCluster, const o2::base::Propagator* propagator, const o2::base::PropagatorF::MatCorrType matCorrType, const int nBlocks, const int nThreads) { thrust::device_vector minPts(minPtsHost); + thrust::device_vector layerRadii(layerRadiiHost); 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 + trackSeeds, // CellSeed* + foundTrackingFrameInfo, // TrackingFrameInfo** + unsortedClusters, // Cluster** + tracks, // TrackITSExt* + 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 + shiftRefToCluster, // bool + propagator, // const o2::base::Propagator* + matCorrType); // o2::base::PropagatorF::MatCorrType thrust::device_ptr tr_ptr(tracks); thrust::sort(tr_ptr, tr_ptr + nSeeds, gpu::compare_track_chi2()); - GPUChkErrS(cudaStreamSynchronize(gpu::Stream::DefaultStream)); } /// Explicit instantiation of ITS2 handlers @@ -1281,13 +1363,17 @@ template void processNeighboursHandler<7>(const int startLayer, template void trackSeedHandler(CellSeed<7>* trackSeeds, const TrackingFrameInfo** foundTrackingFrameInfo, + const Cluster** unsortedClusters, o2::its::TrackITSExt* tracks, - std::vector& minPtsHost, + const std::vector& layerRadiiHost, + const std::vector& minPtsHost, 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 shiftRefToCluster, const o2::base::Propagator* propagator, const o2::base::PropagatorF::MatCorrType matCorrType, const int nBlocks, diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h index 9a6452270d144..000c8fe822498 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h @@ -66,10 +66,10 @@ 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 + 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; - bool shiftRefToCluster = true; // TrackFit: after update shift the linearization reference to cluster + 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/TrackerTraits.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h index f582b5ef3aec5..ddc32ed18cbfe 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h @@ -92,7 +92,7 @@ class TrackerTraits virtual int getTFNumberOfCells() const { return mTimeFrame->getNumberOfCells(); } private: - track::TrackParCov buildTrackSeed(const Cluster& cluster1, const Cluster& cluster2, const TrackingFrameInfo& tf3); + 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); diff --git a/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx b/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx index c6ed343033996..87787eeee03a9 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx @@ -186,8 +186,8 @@ std::vector TrackingMode::getTrackingParameters(TrackingMode int lslot = tc.MaxTrackLength - ilg; p.MinPt[lslot] *= bFactor; } - p.reseedIfShorter = tc.reseedIfShorter; - p.shiftRefToCluster = tc.shiftRefToCluster; + p.ReseedIfShorter = tc.reseedIfShorter; + p.ShiftRefToCluster = tc.shiftRefToCluster; p.createArtefactLabels = tc.createArtefactLabels; p.PrintMemory = tc.printMemory; diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx index 5c5eb47216051..e6d3441f0bc06 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx @@ -765,7 +765,6 @@ void TrackerTraits::findRoads(const int iteration) auto forSeed = [&](auto Tag, int iSeed, int offset = 0) { TrackITSExt temporaryTrack = seedTrackForRefit(trackSeeds[iSeed]); o2::track::TrackPar linRef{temporaryTrack}; - o2::track::TrackParCov savTr = temporaryTrack; // REMOVE 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; @@ -773,7 +772,7 @@ void TrackerTraits::findRoads(const int iteration) temporaryTrack.getParamOut() = temporaryTrack.getParamIn(); linRef = temporaryTrack.getParamOut(); // use refitted track as lin.reference temporaryTrack.resetCovariance(); - temporaryTrack.setCov(temporaryTrack.getQ2Pt() * temporaryTrack.getQ2Pt() * temporaryTrack.getCov()[14], 14); + temporaryTrack.setCov(temporaryTrack.getQ2Pt() * temporaryTrack.getQ2Pt() * temporaryTrack.getCov()[o2::track::CovLabels::kSigQ2Pt2], o2::track::CovLabels::kSigQ2Pt2); temporaryTrack.setChi2(0); fitSuccess = fitTrack(temporaryTrack, mTrkParams[0].NLayers - 1, -1, -1, mTrkParams[0].MaxChi2ClusterAttachment, mTrkParams[0].MaxChi2NDF, 50.f, 0, &linRef); if (!fitSuccess || temporaryTrack.getPt() < mTrkParams[iteration].MinPt[mTrkParams[iteration].NLayers - temporaryTrack.getNClusters()]) { @@ -1082,7 +1081,7 @@ 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 + if (linRef && mTrkParams[0].ShiftRefToCluster) { // displace the reference to the last updated cluster linRef->setY(trackingHit.positionTrackingFrame[0]); linRef->setZ(trackingHit.positionTrackingFrame[1]); } @@ -1208,27 +1207,22 @@ template TrackITSExt TrackerTraits::seedTrackForRefit(const CellSeedN& seed) { TrackITSExt temporaryTrack(seed); + int lrMin = nLayers, lrMax = 0, lrMid = 0; for (int iL = 0; iL < nLayers; ++iL) { - temporaryTrack.setExternalClusterIndex(iL, seed.getCluster(iL), seed.getCluster(iL) != constants::UnusedIndex); + 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 - int lrMin = 999, lrMax = 0, lrMid = 0; + 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 { - for (int iL = 0; iL < nLayers; ++iL) { - if (seed.getCluster(iL) != constants::UnusedIndex) { - if (iL < lrMin) { - lrMin = iL; - } - if (iL > lrMax) { - lrMax = iL; - } - } - } 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 @@ -1242,19 +1236,17 @@ TrackITSExt TrackerTraits::seedTrackForRefit(const CellSeedN& seed) 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); - temporaryTrack.setQ2Pt(-temporaryTrack.getQ2Pt()); // we are calling buildTrackSeed with the clusters order opposite to what it expects - temporaryTrack.setSnp(-temporaryTrack.getSnp()); // we are calling buildTrackSeed with the clusters order opposite to what it expects + temporaryTrack.getParamIn() = buildTrackSeed(cluster2_gl, cluster1_gl, cluster0_tf, true); } temporaryTrack.resetCovariance(); - temporaryTrack.setCov(temporaryTrack.getQ2Pt() * temporaryTrack.getQ2Pt() * temporaryTrack.getCov()[14], 14); + 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}; o2::gpu::CAMath::SinCos(tf3.alphaTrackingFrame, sa, ca); @@ -1267,12 +1259,14 @@ track::TrackParCov TrackerTraits::buildTrackSeed(const Cluster& cluster const float x3 = tf3.xTrackingFrame; const float y3 = tf3.positionTrackingFrame[0]; const float z3 = tf3.positionTrackingFrame[1]; + const float sign = (reverse) ? -1.f : 1.f; 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}; if (mIsZeroField) { - tgp = o2::gpu::CAMath::ATan2(y3 - y1, x3 - x1); + tgp = sign * o2::gpu::CAMath::ATan2(y3 - y1, x3 - x1); snp = tgp / o2::gpu::CAMath::Sqrt(1.f + tgp * tgp); + q2pt *= sign; } else { - crv = math_utils::computeCurvature(x3, y3, x2, y2, x1, y1); + crv = sign * 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); q2pt2 = crv * crv; From e12ca36e0a505f502a45a64eae6bde32e1ee4d90 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Mon, 1 Dec 2025 20:16:57 +0100 Subject: [PATCH 038/701] ITS: simplify buildTrackSeed Signed-off-by: Felix Schlepper --- .../ITS/tracking/GPU/cuda/TrackingKernels.cu | 46 ++++++++++--------- .../ITSMFT/ITS/tracking/src/TrackerTraits.cxx | 35 +++++++------- 2 files changed, 43 insertions(+), 38 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu index 27d7fa97f7b5c..d9136cb96d00e 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -129,34 +129,36 @@ GPUdii() o2::track::TrackParCov buildTrackSeed(const Cluster& cluster1, 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 sign = (reverse) ? -1.f : 1.f; - const float tgp = zeroField ? sign * o2::gpu::CAMath::ATan2(y3 - y1, x3 - x1) : 1.f; - const float crv = sign * (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 ? sign / 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 diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx index e6d3441f0bc06..6b237ad0a63e8 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx @@ -1248,33 +1248,36 @@ TrackITSExt TrackerTraits::seedTrackForRefit(const CellSeedN& seed) template 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]; - const float sign = (reverse) ? -1.f : 1.f; - 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 = sign * o2::gpu::CAMath::ATan2(y3 - y1, x3 - x1); - snp = tgp / o2::gpu::CAMath::Sqrt(1.f + tgp * tgp); - q2pt *= sign; + 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 = sign * 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 From 5b0ae4534dd54d57897ab57a90ec28523e82b3b8 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 3 Dec 2025 11:23:06 +0100 Subject: [PATCH 039/701] DPL: sanity check silently skipping DataProcessingDevice When either DomainInfoHeader or SourceInfoHeader is present, we skip their processing. However we should complain if actual data is attached to the same message, because it will also be skipped. --- Framework/Core/src/DataProcessingDevice.cxx | 29 ++++++++++----------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index 3b430378dc0b0..3e9a0a3d996b9 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -558,25 +558,18 @@ static auto toBeForwardedHeader = [](void* header) -> bool { 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; + bool retval = !o2::header::get(header) && + !o2::header::get(header) && + o2::header::get(header); + // DataHeader is there. Complain if we have unexpected headers present / missing + if (!retval) { + LOGP(error, "Dropping data because of malformed header structure"); } - return true; + return retval; }; static auto toBeforwardedMessageSet = [](std::vector& cachedForwardingChoices, @@ -1858,11 +1851,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); @@ -1870,9 +1867,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?"); From a5c604de4c1f17839edd7801155024af739f7967 Mon Sep 17 00:00:00 2001 From: Felix Weiglhofer Date: Tue, 2 Dec 2025 14:24:23 +0100 Subject: [PATCH 040/701] GPU: Add throughput to pad filter kernel timer. --- GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx index 7b0c54cda5cb1..fdce8ef5a127d 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx @@ -964,6 +964,7 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) int32_t nBlocks = TPC_PADS_IN_SECTOR / GPUTPCCFCheckPadBaseline::PadsPerCacheline; 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}}); From 754936f607b76114cdfd282e34684bc3ee62f454 Mon Sep 17 00:00:00 2001 From: shahoian Date: Thu, 4 Dec 2025 15:37:45 +0100 Subject: [PATCH 041/701] Use stable lin.ref. point for alignment track initial fit --- Detectors/Align/include/Align/AlignConfig.h | 1 + .../Align/include/Align/AlignmentTrack.h | 42 +++++++-------- Detectors/Align/src/AlignmentTrack.cxx | 51 ++++++++++++------- .../Base/include/DetectorsBase/Propagator.h | 3 ++ Detectors/Base/src/Propagator.cxx | 17 +++++++ 5 files changed, 75 insertions(+), 39 deletions(-) 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/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/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/Base/include/DetectorsBase/Propagator.h b/Detectors/Base/include/DetectorsBase/Propagator.h index 6fa750577255d..75b9446aebade 100644 --- a/Detectors/Base/include/DetectorsBase/Propagator.h +++ b/Detectors/Base/include/DetectorsBase/Propagator.h @@ -127,6 +127,9 @@ class PropagatorImpl 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/Base/src/Propagator.cxx b/Detectors/Base/src/Propagator.cxx index 02e7a05080ac5..a5983cab8e257 100644 --- a/Detectors/Base/src/Propagator.cxx +++ b/Detectors/Base/src/Propagator.cxx @@ -648,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 From 99a77145c00dd215ddbc537b046173b1cbc13724 Mon Sep 17 00:00:00 2001 From: wiechula Date: Thu, 4 Dec 2025 21:25:19 +0100 Subject: [PATCH 042/701] Add possibility for head only object In case the file name is set to 'headersOnly' an empty object with only headers is created. This is e.g. necessary for pure redirect entries. --- CCDB/src/UploadTool.cxx | 54 +++++++++++++++++++++++------------------ 1 file changed, 31 insertions(+), 23 deletions(-) diff --git a/CCDB/src/UploadTool.cxx b/CCDB/src/UploadTool.cxx index 44b8d8e20bc7d..83b395bedb046 100644 --- a/CCDB/src/UploadTool.cxx +++ b/CCDB/src/UploadTool.cxx @@ -147,33 +147,41 @@ 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") { + 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; From 348e9b1a7fd56c499caafe255485af6aff9e0c04 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Fri, 5 Dec 2025 11:11:45 +0100 Subject: [PATCH 043/701] DPL: refactor input forwarding routing Separate routing of the forwarding to a separate helper. Add test for said helper. --- Framework/Core/CMakeLists.txt | 1 + .../include/Framework/DataProcessingHelpers.h | 11 +- Framework/Core/src/DataProcessingDevice.cxx | 125 +--- Framework/Core/src/DataProcessingHelpers.cxx | 134 ++++ Framework/Core/test/test_ForwardInputs.cxx | 691 ++++++++++++++++++ 5 files changed, 838 insertions(+), 124 deletions(-) create mode 100644 Framework/Core/test/test_ForwardInputs.cxx diff --git a/Framework/Core/CMakeLists.txt b/Framework/Core/CMakeLists.txt index ce8fbb0dc55f7..fe8a91eaa0449 100644 --- a/Framework/Core/CMakeLists.txt +++ b/Framework/Core/CMakeLists.txt @@ -224,6 +224,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 diff --git a/Framework/Core/include/Framework/DataProcessingHelpers.h b/Framework/Core/include/Framework/DataProcessingHelpers.h index d8d8b7caf9d0a..be02aae5d2f69 100644 --- a/Framework/Core/include/Framework/DataProcessingHelpers.h +++ b/Framework/Core/include/Framework/DataProcessingHelpers.h @@ -12,6 +12,10 @@ #define O2_FRAMEWORK_DATAPROCESSINGHELPERS_H_ #include +#include "Framework/TimesliceSlot.h" +#include "Framework/TimesliceIndex.h" +#include +#include namespace o2::framework { @@ -23,6 +27,9 @@ struct OutputChannelSpec; struct OutputChannelState; struct ProcessingPolicies; struct DeviceSpec; +struct FairMQDeviceProxy; +struct MessageSet; +struct ChannelIndex; enum struct StreamingState; enum struct TransitionHandlingState; @@ -45,7 +52,9 @@ struct DataProcessingHelpers { 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 routeForwardedMessages(FairMQDeviceProxy& proxy, TimesliceSlot slot, std::vector& currentSetOfInputs, + TimesliceIndex::OldestOutputInfo oldestTimeslice, bool copy, bool consume); }; - } // namespace o2::framework #endif // O2_FRAMEWORK_DATAPROCESSINGHELPERS_H_ diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index 3e9a0a3d996b9..406e93aaae98e 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -550,69 +550,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 dh = o2::header::get(header); - if (!dh) { - return false; - } - bool retval = !o2::header::get(header) && - !o2::header::get(header) && - o2::header::get(header); - // DataHeader is there. Complain if we have unexpected headers present / missing - if (!retval) { - LOGP(error, "Dropping data because of malformed header structure"); - } - return retval; -}; - -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; @@ -653,67 +590,9 @@ 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" : ""); - - 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; - } + auto forwardedParts = DataProcessingHelpers::routeForwardedMessages(proxy, slot, currentSetOfInputs, oldestTimeslice, copy, consume); - // 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_ID_GENERATE(sid, forwarding); 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) { diff --git a/Framework/Core/src/DataProcessingHelpers.cxx b/Framework/Core/src/DataProcessingHelpers.cxx index e144f426372b1..9c53bbf8b2c10 100644 --- a/Framework/Core/src/DataProcessingHelpers.cxx +++ b/Framework/Core/src/DataProcessingHelpers.cxx @@ -16,6 +16,7 @@ #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" @@ -31,6 +32,8 @@ #include "Framework/ControlService.h" #include "Framework/DataProcessingContext.h" #include "Framework/DeviceStateEnums.h" +#include "Headers/DataHeader.h" +#include "Framework/DataProcessingHeader.h" #include #include @@ -41,6 +44,7 @@ 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 { @@ -217,4 +221,134 @@ TransitionHandlingState DataProcessingHelpers::updateStateTransition(ServiceRegi } } +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 dh = o2::header::get(header); + if (!dh) { + return false; + } + bool retval = !o2::header::get(header) && + !o2::header::get(header) && + o2::header::get(header); + // DataHeader is there. Complain if we have unexpected headers present / missing + if (!retval) { + LOGP(error, "Dropping data because of malformed header structure"); + } + return retval; +}; + +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; +}; + +std::vector DataProcessingHelpers::routeForwardedMessages(FairMQDeviceProxy& proxy, TimesliceSlot slot, std::vector& currentSetOfInputs, + TimesliceIndex::OldestOutputInfo oldestTimeslice, bool copy, bool consume) +{ + // 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" : ""); + + 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))); + } + } + } + } + return forwardedParts; +}; + } // namespace o2::framework diff --git a/Framework/Core/test/test_ForwardInputs.cxx b/Framework/Core/test/test_ForwardInputs.cxx new file mode 100644 index 0000000000000..1406110e0e9ee --- /dev/null +++ b/Framework/Core/test/test_ForwardInputs.cxx @@ -0,0 +1,691 @@ +// 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/ServiceRegistry.h" +#include "Framework/ServiceRegistryRef.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; + + TimesliceIndex::OldestOutputInfo oldestTimeslice{.timeslice = {1}}; + std::vector currentSetOfInputs; + TimesliceSlot slot{0}; + + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, slot, currentSetOfInputs, oldestTimeslice, 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); + + TimesliceIndex::OldestOutputInfo oldestTimeslice{.timeslice = {0}}; + 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)); + + TimesliceSlot slot{0}; + + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, slot, currentSetOfInputs, oldestTimeslice, copyByDefault, consume); + REQUIRE(result.size() == 1); // One route + REQUIRE(result[0].Size() == 2); // Two messages for that route +} + +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); + + TimesliceIndex::OldestOutputInfo oldestTimeslice{.timeslice = {0}}; + 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)); + + TimesliceSlot slot{0}; + + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, slot, currentSetOfInputs, oldestTimeslice, copyByDefault, consume); + REQUIRE(result.size() == 1); // One route + REQUIRE(result[0].Size() == 0); // FIXME: this is an actual error. It should be 2 + // 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); + + TimesliceIndex::OldestOutputInfo oldestTimeslice{.timeslice = {0}}; + 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)); + + TimesliceSlot slot{0}; + + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, slot, currentSetOfInputs, oldestTimeslice, 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); + + TimesliceIndex::OldestOutputInfo oldestTimeslice{.timeslice = {0}}; + 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)); + + TimesliceSlot slot{0}; + + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, slot, currentSetOfInputs, oldestTimeslice, 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); + + TimesliceIndex::OldestOutputInfo oldestTimeslice{.timeslice = {0}}; + 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)); + + TimesliceSlot slot{0}; + + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, slot, currentSetOfInputs, oldestTimeslice, 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); + + TimesliceIndex::OldestOutputInfo oldestTimeslice{.timeslice = {0}}; + 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); + + TimesliceSlot slot{0}; + + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, slot, currentSetOfInputs, oldestTimeslice, 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); + + TimesliceIndex::OldestOutputInfo oldestTimeslice{.timeslice = {0}}; + 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)); + + TimesliceSlot slot{0}; + + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, slot, currentSetOfInputs, oldestTimeslice, 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 = 0; + 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); + + TimesliceIndex::OldestOutputInfo oldestTimeslice{.timeslice = {0}}; + 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)); + + TimesliceSlot slot{0}; + + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, slot, currentSetOfInputs, oldestTimeslice, copyByDefault, consume); + REQUIRE(result.size() == 2); // Two routes + CHECK(result[0].Size() == 2); // No messages on this route + CHECK(result[1].Size() == 5); // FIXME: Multipart matching has side effects also for the elements + // CHECK(result[1].Size() == 3); // FIXME: the correct forwarding is that only the multipart goes to the same route +} + +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); + + TimesliceIndex::OldestOutputInfo oldestTimeslice{.timeslice = {0}}; + 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)); + + TimesliceSlot slot{0}; + + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, slot, currentSetOfInputs, oldestTimeslice, 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); + + TimesliceIndex::OldestOutputInfo oldestTimeslice{.timeslice = {0}}; + 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)); + + TimesliceSlot slot{0}; + + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, slot, currentSetOfInputs, oldestTimeslice, copyByDefault, consume); + REQUIRE(result.size() == 1); // One route + REQUIRE(result[0].Size() == 0); // Oldest possible timeframe should not be forwarded +} From b6c63a0b2c0072f00673ca29b38479713f4bbdf1 Mon Sep 17 00:00:00 2001 From: Rita Sadek Date: Sat, 6 Dec 2025 05:41:47 -0800 Subject: [PATCH 044/701] [FT3] Modular structure for OT disks - first version of modules structure with dynamic disks paving for ALICE 3 sensors and initial material estimations (#14816) Co-authored-by: Rita Sadek --- .../ALICE3/FT3/simulation/CMakeLists.txt | 8 +- .../include/FT3Simulation/FT3Layer.h | 19 + .../include/FT3Simulation/FT3Module.h | 45 ++ .../ALICE3/FT3/simulation/src/Detector.cxx | 22 +- .../ALICE3/FT3/simulation/src/FT3Layer.cxx | 182 ++++- .../ALICE3/FT3/simulation/src/FT3Module.cxx | 699 ++++++++++++++++++ 6 files changed, 967 insertions(+), 8 deletions(-) create mode 100644 Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/FT3Module.h create mode 100644 Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx 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/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..aab8ae070d936 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; @@ -729,9 +731,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..87f5f27da6a38 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx @@ -0,0 +1,699 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not 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 + +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() +{ + + if (siliconMat) { + return; + } + + TGeoManager* gGeoManager = 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); +} + +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) +{ + + TGeoManager* gGeoManager = 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 = 0.5; + + 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 = gGeoManager->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 = gGeoManager->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 = gGeoManager->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 = gGeoManager->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 = gGeoManager->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 = gGeoManager->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 = gGeoManager->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 = gGeoManager->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 = gGeoManager->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 = gGeoManager->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 = gGeoManager->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 = gGeoManager->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 = gGeoManager->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 = gGeoManager->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 = gGeoManager->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 = gGeoManager->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 = gGeoManager->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 = gGeoManager->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 = gGeoManager->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 = gGeoManager->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++; + } + } +} + +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) +{ + create_layout(mZ, layerNumber, direction, Rin, Rout, overlap, face, layout_type, motherVolume); +} From 7b3397d53e879a22bad604ca5551607c33744d6c Mon Sep 17 00:00:00 2001 From: shahoian Date: Sat, 6 Dec 2025 14:46:53 +0100 Subject: [PATCH 045/701] Fix for headers-only CCDB uploads --- CCDB/src/CcdbApi.cxx | 14 ++++++++++++-- CCDB/src/UploadTool.cxx | 3 +++ 2 files changed, 15 insertions(+), 2 deletions(-) diff --git a/CCDB/src/CcdbApi.cxx b/CCDB/src/CcdbApi.cxx index 8b3c9e0c619c3..90776d6972e2c 100644 --- a/CCDB/src/CcdbApi.cxx +++ b/CCDB/src/CcdbApi.cxx @@ -375,6 +375,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); @@ -418,8 +422,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:"; diff --git a/CCDB/src/UploadTool.cxx b/CCDB/src/UploadTool.cxx index 83b395bedb046..9aba417b4f4a9 100644 --- a/CCDB/src/UploadTool.cxx +++ b/CCDB/src/UploadTool.cxx @@ -148,6 +148,9 @@ int main(int argc, char* argv[]) } 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, "", "", meta, starttimestamp, endtimestamp); From 1485ea0e4eae2f8e81f0dec243a4cf2bbd8383a8 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Fri, 5 Dec 2025 17:44:25 +0100 Subject: [PATCH 046/701] DPL: test for ConsumeExisting when forwarding --- Framework/Core/test/test_ForwardInputs.cxx | 54 ++++++++++++++++++++++ 1 file changed, 54 insertions(+) diff --git a/Framework/Core/test/test_ForwardInputs.cxx b/Framework/Core/test/test_ForwardInputs.cxx index 1406110e0e9ee..b1f42fb0398ca 100644 --- a/Framework/Core/test/test_ForwardInputs.cxx +++ b/Framework/Core/test/test_ForwardInputs.cxx @@ -107,6 +107,60 @@ TEST_CASE("ForwardInputsSingleMessageSingleRoute") 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); + + TimesliceIndex::OldestOutputInfo oldestTimeslice{.timeslice = {0}}; + 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)); + + TimesliceSlot slot{0}; + + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, slot, currentSetOfInputs, oldestTimeslice, 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; From 1cd002bf0ff0f4c21fd68136694abeade613a10e Mon Sep 17 00:00:00 2001 From: shahoian Date: Sun, 7 Dec 2025 01:43:40 +0100 Subject: [PATCH 047/701] add missing reset ot ITS track before the refit --- .../TPC/calibration/SpacePoints/src/TrackInterpolation.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx b/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx index dddb0a5c435bc..7db5b7455f1a7 100644 --- a/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx +++ b/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx @@ -1488,6 +1488,8 @@ bool TrackInterpolation::refITSTrack(o2::dataformats::GlobalTrackID gid, int see 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(); From 53130312eeee1ef0c1fff93f9f472ed0a21f2f3a Mon Sep 17 00:00:00 2001 From: Sean Murray Date: Mon, 3 Feb 2025 14:05:32 +0100 Subject: [PATCH 048/701] TRD add digit phases to CTF encoding, collapse phases on reading. --- .../TRD/include/DataFormatsTRD/Digit.h | 6 +- .../TRD/include/DataFormatsTRD/PHData.h | 60 +++++++++++++++++++ .../Detectors/TRD/src/DataFormatsTRDLinkDef.h | 2 + DataFormats/Detectors/TRD/src/Digit.cxx | 9 ++- .../include/TRDCalibration/PulseHeight.h | 2 + Detectors/TRD/calibration/src/PulseHeight.cxx | 6 ++ Detectors/TRD/qc/src/RawDataManager.cxx | 2 + .../include/TRDReconstruction/CTFCoder.h | 4 +- .../include/TRDReconstruction/CTFHelper.h | 4 +- .../TRD/reconstruction/src/CruRawReader.cxx | 4 ++ .../include/TRDWorkflow/TRDPulseHeightSpec.h | 2 + 11 files changed, 94 insertions(+), 7 deletions(-) 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/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/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/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/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/reconstruction/include/TRDReconstruction/CTFCoder.h b/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFCoder.h index 107a05a397c00..27e089fcf3555 100644 --- a/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFCoder.h +++ b/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFCoder.h @@ -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/TRDPulseHeightSpec.h b/Detectors/TRD/workflow/include/TRDWorkflow/TRDPulseHeightSpec.h index 536353c7a9e90..3cfbb16644e54 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; From 3cbed2e40e0d3b43efe5e4e016559375f6b1dde9 Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Fri, 5 Dec 2025 17:41:23 +0100 Subject: [PATCH 049/701] Add GRPLHCIF to AggregatedRunInfo --- .../include/DataFormatsParameters/AggregatedRunInfo.h | 4 +++- DataFormats/Parameters/src/AggregatedRunInfo.cxx | 8 +++++--- 2 files changed, 8 insertions(+), 4 deletions(-) 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 From 6db969d4c5c835def1263a38c9251e8faf7cb029 Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Fri, 5 Dec 2025 17:44:30 +0100 Subject: [PATCH 050/701] Introducing non-uniform mu InteractionSampler Provides a novel InteractionSampler for collision structure which is able to sample (orbit,bc) values according to an externally given hNBcVTX distribution (obtained from FIT, EventSelection) A unit test which shows and tests the new feature. Fixes https://its.cern.ch/jira/browse/O2-6450 --- DataFormats/simulation/CMakeLists.txt | 5 + .../SimulationDataFormat/InteractionSampler.h | 25 +++++ .../simulation/src/InteractionSampler.cxx | 97 ++++++++++++++++++- .../simulation/src/SimulationDataLinkDef.h | 1 + .../test/testInteractionSampler.cxx | 76 +++++++++++++++ 5 files changed, 203 insertions(+), 1 deletion(-) create mode 100644 DataFormats/simulation/test/testInteractionSampler.cxx 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/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/src/InteractionSampler.cxx b/DataFormats/simulation/src/InteractionSampler.cxx index 5e14e22e5f8db..61b2c4f61bc08 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,98 @@ 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) +{ + 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 From ff367092f58c49e08c707f5fc5dec6ae3524ba17 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Mon, 8 Dec 2025 10:19:38 +0100 Subject: [PATCH 051/701] DPL: drop support for FairMQ versions without shallow copy --- .../include/Framework/CompletionPolicyHelpers.h | 3 +-- Framework/Core/src/CompletionPolicy.cxx | 4 ---- Framework/Core/src/CompletionPolicyHelpers.cxx | 4 ---- Framework/Core/src/DataProcessingDevice.cxx | 14 -------------- Framework/Core/src/DataRelayer.cxx | 10 ---------- 5 files changed, 1 insertion(+), 34 deletions(-) diff --git a/Framework/Core/include/Framework/CompletionPolicyHelpers.h b/Framework/Core/include/Framework/CompletionPolicyHelpers.h index aa336d040d30d..7f77e4a96f76f 100644 --- a/Framework/Core/include/Framework/CompletionPolicyHelpers.h +++ b/Framework/Core/include/Framework/CompletionPolicyHelpers.h @@ -44,10 +44,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; }) { 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..67c726b7f4368 100644 --- a/Framework/Core/src/CompletionPolicyHelpers.cxx +++ b/Framework/Core/src/CompletionPolicyHelpers.cxx @@ -19,9 +19,7 @@ #include "Framework/TimingInfo.h" #include "DecongestionService.h" #include "Framework/Signpost.h" -#if __has_include() #include -#endif #include #include @@ -252,7 +250,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 +262,6 @@ CompletionPolicy CompletionPolicyHelpers::consumeWhenAnyZeroCount(const char* na }; return CompletionPolicy{name, matcher, callback, false}; } -#endif CompletionPolicy CompletionPolicyHelpers::consumeWhenAny(const char* name, CompletionPolicy::Matcher matcher) { diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index 406e93aaae98e..40f1061e60332 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -59,9 +59,7 @@ #include #include #include -#if __has_include() #include -#endif #include #include #include @@ -1046,14 +1044,6 @@ void DataProcessingDevice::fillContext(DataProcessorContext& context, DeviceCont 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"}) && deviceContext.processingPolicies.earlyForward == EarlyForwardPolicy::NORAW) { context.canForwardEarly = false; overriddenEarlyForward = true; @@ -2058,14 +2048,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()}; }; diff --git a/Framework/Core/src/DataRelayer.cxx b/Framework/Core/src/DataRelayer.cxx index 06e920112649e..df95aeda92a2b 100644 --- a/Framework/Core/src/DataRelayer.cxx +++ b/Framework/Core/src/DataRelayer.cxx @@ -47,9 +47,7 @@ #include #include #include -#if __has_include() #include -#endif #include #include #include @@ -215,14 +213,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 @@ -781,14 +775,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); From 1add74e3bf64b1ad7dde54675e7d5af2718695d9 Mon Sep 17 00:00:00 2001 From: shahoian Date: Mon, 8 Dec 2025 22:54:07 +0100 Subject: [PATCH 052/701] workflow to study ITS residuals --- .../study/CMakeLists.txt | 9 + .../include/GlobalTrackingStudy/CheckResid.h | 27 + .../GlobalTrackingStudy/CheckResidConfig.h | 38 ++ .../GlobalTrackingStudy/CheckResidTypes.h | 42 ++ .../study/src/CheckResid.cxx | 556 ++++++++++++++++++ .../study/src/CheckResidConfig.cxx | 14 + .../study/src/GlobalTrackingStudyLinkDef.h | 6 + .../study/src/check-resid-workflow.cxx | 78 +++ 8 files changed, 770 insertions(+) create mode 100644 Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResid.h create mode 100644 Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidConfig.h create mode 100644 Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidTypes.h create mode 100644 Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx create mode 100644 Detectors/GlobalTrackingWorkflow/study/src/CheckResidConfig.cxx create mode 100644 Detectors/GlobalTrackingWorkflow/study/src/check-resid-workflow.cxx diff --git a/Detectors/GlobalTrackingWorkflow/study/CMakeLists.txt b/Detectors/GlobalTrackingWorkflow/study/CMakeLists.txt index 776d3946283c3..df42af503db46 100644 --- a/Detectors/GlobalTrackingWorkflow/study/CMakeLists.txt +++ b/Detectors/GlobalTrackingWorkflow/study/CMakeLists.txt @@ -25,6 +25,8 @@ o2_add_library(GlobalTrackingStudy src/TrackMCStudyConfig.cxx src/TrackMCStudyTypes.cxx src/TPCClusSelector.cxx + src/CheckResid.cxx + src/CheckResidConfig.cxx PUBLIC_LINK_LIBRARIES O2::GlobalTracking O2::GlobalTrackingWorkflowReaders O2::GlobalTrackingWorkflowHelpers @@ -38,6 +40,8 @@ o2_target_root_dictionary(GlobalTrackingStudy include/GlobalTrackingStudy/TrackInfoExt.h include/GlobalTrackingStudy/TrackMCStudyConfig.h include/GlobalTrackingStudy/TrackMCStudyTypes.h + include/GlobalTrackingStudy/CheckResidTypes.h + include/GlobalTrackingStudy/CheckResidConfig.h LINKDEF src/GlobalTrackingStudyLinkDef.h ) @@ -76,6 +80,11 @@ o2_add_executable(dump-workfow SOURCES src/track-dump-workflow.cxx PUBLIC_LINK_LIBRARIES O2::GlobalTrackingStudy) +o2_add_executable(resid-workfow + COMPONENT_NAME check + SOURCES src/check-resid-workflow.cxx + PUBLIC_LINK_LIBRARIES O2::GlobalTrackingStudy) + if (OpenMP_CXX_FOUND) target_compile_definitions(${targetName} PRIVATE WITH_OPENMP) target_link_libraries(${targetName} PRIVATE OpenMP::OpenMP_CXX) diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResid.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResid.h new file mode 100644 index 0000000000000..a78fa5e8d41da --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResid.h @@ -0,0 +1,27 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_CHECK_RESID_H +#define O2_CHECK_RESID_H + +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "Framework/Task.h" +#include "Framework/DataProcessorSpec.h" +// #include "TPCCalibration/CorrectionMapsLoader.h" + +namespace o2::checkresid +{ +/// create a processor spec +o2::framework::DataProcessorSpec getCheckResidSpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool useMC /*, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts*/); + +} // namespace o2::checkresid + +#endif // O2_CHECK_RESID_H diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidConfig.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidConfig.h new file mode 100644 index 0000000000000..53dffeed7ad69 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidConfig.h @@ -0,0 +1,38 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#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 refitPV = true; + bool useStableRef = true; + bool doIBOB = true; + bool doResid = true; + + 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/src/CheckResid.cxx b/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx new file mode 100644 index 0000000000000..34643928db344 --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx @@ -0,0 +1,556 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not 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 + +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; + 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 + 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.useMeanVertexConstraint=false"); + mVertexer.init(); + } + 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) { + 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(); + 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)) { + 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(warn, "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(); + 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)) { + return false; + } + // readjust vertexZ + const auto& pool = mVertexer.getTracksPool(); + float zUpd = 0; + for (const auto& t : pool) { + zUpd += t.z; + } + if (pool.size()) { + pv.setZ(zUpd / pool.size()); + mVertexer.prepareVertexRefit(tracks, pv); + } + pv = mVertexer.refitVertex(useTrack, pv); + return pv.getChi2() > 0.f; +} + +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; + return; + } + if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { + LOG(info) << "cluster dictionary updated"; + mITSDict = (const o2::itsmft::TopologyDictionary*)obj; + return; + } +} + +DataProcessorSpec getCheckResidSpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC /*, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts*/) +{ + std::vector outputs; + auto dataRequest = std::make_shared(); + dataRequest->requestTracks(srcTracks, useMC); + dataRequest->requestClusters(srcClusters, useMC); + dataRequest->requestPrimaryVertices(useMC); + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + true, // GRPLHCIF + true, // GRPMagField + true, // askMatLUT + o2::base::GRPGeomRequest::Aligned, // geometry + dataRequest->inputs, + true); + dataRequest->inputs.emplace_back("meanvtx", "GLO", "MEANVERTEX", 0, Lifetime::Condition, ccdbParamSpec("GLO/Calib/MeanVertex", {}, 1)); + Options opts{ + {"nthreads", VariantType::Int, 1, {"number of threads"}}, + }; + // o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); + // o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); + + return DataProcessorSpec{ + "check-resid", + dataRequest->inputs, + outputs, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, useMC /*, sclOpts*/)}, + opts}; +} + +} // namespace o2::checkresid diff --git a/Detectors/GlobalTrackingWorkflow/study/src/CheckResidConfig.cxx b/Detectors/GlobalTrackingWorkflow/study/src/CheckResidConfig.cxx new file mode 100644 index 0000000000000..a754d1196017f --- /dev/null +++ b/Detectors/GlobalTrackingWorkflow/study/src/CheckResidConfig.cxx @@ -0,0 +1,14 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "GlobalTrackingStudy/CheckResidConfig.h" + +O2ParamImpl(o2::checkresid::CheckResidConfig); diff --git a/Detectors/GlobalTrackingWorkflow/study/src/GlobalTrackingStudyLinkDef.h b/Detectors/GlobalTrackingWorkflow/study/src/GlobalTrackingStudyLinkDef.h index 6075429b0b16e..416820fc9aebb 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/GlobalTrackingStudyLinkDef.h +++ b/Detectors/GlobalTrackingWorkflow/study/src/GlobalTrackingStudyLinkDef.h @@ -43,4 +43,10 @@ #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/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); +} From 93ff0dcf60e8bd709de653fc354dc5cd05cf760c Mon Sep 17 00:00:00 2001 From: Andrea Sofia Triolo Date: Tue, 9 Dec 2025 22:41:25 +0100 Subject: [PATCH 053/701] ALICE3-TRK: adjusted VD segmentation taking into account gaps between adjacent layers (#14903) --- Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h index 172d993be7283..a5a60422f77eb 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h @@ -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 From 82a55b7f67dce4512a3a39bb361046a468d377a8 Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Thu, 4 Dec 2025 11:35:58 +0100 Subject: [PATCH 054/701] DPL: refactor ccdb-fetcher service devic injection --- Framework/Core/src/WorkflowHelpers.cxx | 154 ++++++++++--------------- 1 file changed, 61 insertions(+), 93 deletions(-) diff --git a/Framework/Core/src/WorkflowHelpers.cxx b/Framework/Core/src/WorkflowHelpers.cxx index 61443f5f71616..034524ff0af8e 100644 --- a/Framework/Core/src/WorkflowHelpers.cxx +++ b/Framework/Core/src/WorkflowHelpers.cxx @@ -488,116 +488,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; - } + // ConcreteDataMatcher dstf{"FLP", "DISTSUBTIMEFRAME", 0xccdb}; + 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::any_of(workflow.begin(), workflow.end(), + [&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::any_of(workflow.begin(), workflow.end(), + [&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 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 (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 (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); From df0e932eed2d5821b3e8d21aa2ba815ff06b272b Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Mon, 8 Dec 2025 10:14:57 +0100 Subject: [PATCH 055/701] remove commented code --- Framework/Core/src/WorkflowHelpers.cxx | 1 - 1 file changed, 1 deletion(-) diff --git a/Framework/Core/src/WorkflowHelpers.cxx b/Framework/Core/src/WorkflowHelpers.cxx index 034524ff0af8e..8d273b2f33273 100644 --- a/Framework/Core/src/WorkflowHelpers.cxx +++ b/Framework/Core/src/WorkflowHelpers.cxx @@ -488,7 +488,6 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext extraSpecs.push_back(timePipeline(aodReader, ctx.options().get("readers"))); } - // ConcreteDataMatcher dstf{"FLP", "DISTSUBTIMEFRAME", 0xccdb}; InputSpec matcher{"dstf", "FLP", "DISTSUBTIMEFRAME", 0xccdb}; auto& dstf = std::get(matcher.matcher); // Check if any of the provided outputs is a DISTSTF From 7547f2843760f943f5eb09c0a1e570fce26e04d7 Mon Sep 17 00:00:00 2001 From: ALICE Action Bot Date: Mon, 8 Dec 2025 09:21:45 +0000 Subject: [PATCH 056/701] Please consider the following formatting changes --- Framework/Core/src/WorkflowHelpers.cxx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Framework/Core/src/WorkflowHelpers.cxx b/Framework/Core/src/WorkflowHelpers.cxx index 8d273b2f33273..17f6c9eb7ddb6 100644 --- a/Framework/Core/src/WorkflowHelpers.cxx +++ b/Framework/Core/src/WorkflowHelpers.cxx @@ -494,7 +494,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext // Check if any of the requested inputs is for a 0xccdb message bool providesDISTSTF = std::any_of(workflow.begin(), workflow.end(), [&matcher](auto const& dp) { - return std::any_of(dp.outputs.begin(), dp.outputs.end(), [&matcher](auto const& output){ + return std::any_of(dp.outputs.begin(), dp.outputs.end(), [&matcher](auto const& output) { return DataSpecUtils::match(matcher, output); }); }); @@ -503,8 +503,8 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext // we add to the first data processor which has no inputs (apart from // enumerations / timers) the responsibility to provide the DISTSUBTIMEFRAME bool requiresDISTSUBTIMEFRAME = std::any_of(workflow.begin(), workflow.end(), - [&dstf](auto const& dp) { - return std::any_of(dp.inputs.begin(), dp.inputs.end(), [&dstf](auto const& input){ + [&dstf](auto const& dp) { + return std::any_of(dp.inputs.begin(), dp.inputs.end(), [&dstf](auto const& input) { return DataSpecUtils::match(input, dstf); }); }); @@ -522,7 +522,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext } auto lifetime = dp.inputs[0].lifetime; if (lifetime == Lifetime::Enumeration && (enumCandidate == -1 || workflow[enumCandidate].name > dp.name)) { - enumCandidate = wi; + enumCandidate = wi; } if (lifetime == Lifetime::Timer && (timerCandidate == -1 || workflow[timerCandidate].name > dp.name)) { timerCandidate = wi; From 7ffc8976438775c755c4cd2af90591b73c1341f7 Mon Sep 17 00:00:00 2001 From: Martin Eide <43970264+mrtineide@users.noreply.github.com> Date: Wed, 26 Nov 2025 16:07:17 +0100 Subject: [PATCH 057/701] Use new User Agent in CCDB --- CCDB/src/CcdbApi.cxx | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/CCDB/src/CcdbApi.cxx b/CCDB/src/CcdbApi.cxx index 90776d6972e2c..f083d97b533df 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() From cb613568bf8594e0748e48f4fe4bf564ae959359 Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Wed, 10 Dec 2025 09:30:10 +0100 Subject: [PATCH 058/701] Several bug fixes in PHOS calibrator * avoid integer overflow * initialize variables * fix other evident logic bugs * check before talking to bitset Hopefully fixes/avoids a Exception while running: bitset::test: __position (which is 18446744073709548060) >= _Nb (which is 14337) observed in the ARM CI. --- .../include/PHOSCalibWorkflow/TurnOnHistos.h | 6 +++--- .../PHOS/calib/src/PHOSRunbyrunCalibrator.cxx | 6 +++--- .../PHOS/calib/src/PHOSTurnonCalibrator.cxx | 16 +++++++++------- 3 files changed, 15 insertions(+), 13 deletions(-) diff --git a/Detectors/PHOS/calib/include/PHOSCalibWorkflow/TurnOnHistos.h b/Detectors/PHOS/calib/include/PHOSCalibWorkflow/TurnOnHistos.h index 4457da2e100ad..046b8b3c39622 100644 --- a/Detectors/PHOS/calib/include/PHOSCalibWorkflow/TurnOnHistos.h +++ b/Detectors/PHOS/calib/include/PHOSCalibWorkflow/TurnOnHistos.h @@ -21,7 +21,7 @@ #include #include -#include "TObject.h" +#include "TObject.h" // # RTYpe ? namespace o2 { @@ -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); } //============================================== From d7257f81749184b56564cbf373518e9cac37ec2e Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Wed, 10 Dec 2025 20:37:02 +0100 Subject: [PATCH 059/701] fixup --- Detectors/PHOS/calib/include/PHOSCalibWorkflow/TurnOnHistos.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Detectors/PHOS/calib/include/PHOSCalibWorkflow/TurnOnHistos.h b/Detectors/PHOS/calib/include/PHOSCalibWorkflow/TurnOnHistos.h index 046b8b3c39622..0814fe0da4547 100644 --- a/Detectors/PHOS/calib/include/PHOSCalibWorkflow/TurnOnHistos.h +++ b/Detectors/PHOS/calib/include/PHOSCalibWorkflow/TurnOnHistos.h @@ -21,7 +21,7 @@ #include #include -#include "TObject.h" // # RTYpe ? +#include "TObject.h" namespace o2 { From fb08487608f0276c1b71dd848deeb86dec9230bb Mon Sep 17 00:00:00 2001 From: shahoian Date: Thu, 11 Dec 2025 13:53:33 +0100 Subject: [PATCH 060/701] Demote warning to debug level --- Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx b/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx index 34643928db344..691d731503b88 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx @@ -404,7 +404,7 @@ bool CheckResidSpec::processITSTrack(const o2::its::TrackITS& iTrack, const o2:: innerDone = true; } } else { - LOGP(warn, "No cluster on lr {}", i); + LOGP(debug, "No cluster on lr {}", i); } } } From 1c3bfa0d941dcdf6983173280d33cb1c2ad9cef4 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 3 Dec 2025 21:06:04 +0100 Subject: [PATCH 061/701] GPU QA: Add pad row vs occuapncy histogram --- GPU/GPUTracking/qa/GPUQA.cxx | 68 +++++++++++++++++++++++------------- GPU/GPUTracking/qa/GPUQA.h | 6 ++-- 2 files changed, 47 insertions(+), 27 deletions(-) diff --git a/GPU/GPUTracking/qa/GPUQA.cxx b/GPU/GPUTracking/qa/GPUQA.cxx index 689dc20cb1606..28b603f77e2ff 100644 --- a/GPU/GPUTracking/qa/GPUQA.cxx +++ b/GPU/GPUTracking/qa/GPUQA.cxx @@ -152,6 +152,7 @@ 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; @@ -525,9 +526,10 @@ int32_t GPUQA::InitQACreateHistograms() createHist(mClusters[i], name, name, AXIS_BINS[4], binsPt.get()); } - createHist(mPadRow[0], "padrow0", "padrow0", GPUCA_ROW_COUNT, 0, GPUCA_ROW_COUNT - 1, GPUCA_ROW_COUNT, 0, GPUCA_ROW_COUNT - 1); - createHist(mPadRow[1], "padrow1", "padrow1", 100.f, -0.2f, 0.2f, GPUCA_ROW_COUNT, 0, GPUCA_ROW_COUNT - 1); - createHist(mPadRow[2], "padrow2", "padrow2", 100.f, -0.2f, 0.2f, GPUCA_ROW_COUNT, 0, GPUCA_ROW_COUNT - 1); + 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) { @@ -968,7 +970,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); @@ -1069,7 +1071,7 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx } } } - if ((mQATasks & taskClusterAttach)) { + if ((mQATasks & taskClusterAttach) && !tracksExternal) { std::vector lowestPadRow(mTracking->mIOPtrs.nMergedTracks); // fill cluster adjacent status if (mTracking->mIOPtrs.mergedTrackHitAttachment) { @@ -1096,12 +1098,12 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx } } } - if (mTracking->mIOPtrs.nMergedTracks && mTracking->mIOPtrs.clustersNative) { + 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 < mTracking->mIOPtrs.clustersNative->nClusters[iSector][iRow]; iCl++) { - int32_t i = mTracking->mIOPtrs.clustersNative->clusterOffset[iSector][iRow] + iCl; + 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) { @@ -1113,12 +1115,21 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx } 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() > 70 && CAMath::Abs(trk.GetParam().GetQPt()) < 0.5) { - int32_t lowestRow = CAMath::Min(mTracking->mIOPtrs.mergedTrackHits[trk.FirstClusterRef()].row, mTracking->mIOPtrs.mergedTrackHits[trk.FirstClusterRef() + trk.NClusters() - 1].row); + 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] == 0 && lowestRow != 0) { - mPadRow[2]->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); + } } } } @@ -1485,7 +1496,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]; @@ -1715,7 +1726,7 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx 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) { @@ -1815,8 +1826,8 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx if (mQATasks & taskClusterCounts) { 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 < mTracking->mIOPtrs.clustersNative->nClusters[iSector][iRow]; iCl++) { - uint32_t i = mTracking->mIOPtrs.clustersNative->clusterOffset[iSector][iRow] + iCl; + 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); @@ -1873,8 +1884,8 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx if (r.unattached) { mClusterCounts.nUnattached++; } - if (mTracking && mTracking->mIOPtrs.clustersNative) { - const auto& cl = mTracking->mIOPtrs.clustersNative->clustersLinear[i]; + 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); @@ -1895,7 +1906,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"); } @@ -2273,7 +2284,7 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) mPClRejP = createGarbageCollected("p0", "", 0.0, 0.0, 1.0, 1.0); mPClRejP->Draw(); - for (int32_t i = 0; i < 3; i++) { + 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(); @@ -2842,19 +2853,28 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) } } - for (int32_t i = 0; i < 3; i++) { + for (int32_t i = 0; i < 4; i++) { auto* e = mPadRow[i]; if (tout && !mConfig.inputHistogramsOnly) { e->Write(); } mPPadRow[i]->cd(); e->SetOption("colz"); - e->SetTitle(i == 2 ? "First Track Pad Row (row_{MC} = 0, row_{trk} > 0)" : "First Track Pad Row"); - e->GetXaxis()->SetTitle(i ? "#Phi (sector)" : "First MC Pad Row"); + 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(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[3] = {"MC", "Phi", "Phi1"}; + 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())); diff --git a/GPU/GPUTracking/qa/GPUQA.h b/GPU/GPUTracking/qa/GPUQA.h index 54d1ceed9d365..7303ed62a9562 100644 --- a/GPU/GPUTracking/qa/GPUQA.h +++ b/GPU/GPUTracking/qa/GPUQA.h @@ -323,9 +323,9 @@ class GPUQA TPad* mPClRej[3]; TPad* mPClRejP; - TH2F* mPadRow[3]; - TCanvas* mCPadRow[3]; - TPad* mPPadRow[3]; + TH2F* mPadRow[4]; + TCanvas* mCPadRow[4]; + TPad* mPPadRow[4]; std::vector mHistClusterCount; From 215ac60fe27a46657b82fa2434923c28797c8d3d Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 4 Dec 2025 12:55:58 +0100 Subject: [PATCH 062/701] GPU QA Standalone: By default write histograms to output root file in plots folder --- GPU/GPUTracking/Standalone/Benchmark/standalone.cxx | 3 +++ 1 file changed, 3 insertions(+) diff --git a/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx b/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx index ca26f26d32612..857803d913372 100644 --- a/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx +++ b/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx @@ -197,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; From 4b0f130b0f0b5387247a075075add83a4cb09a32 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 3 Dec 2025 21:13:34 +0100 Subject: [PATCH 063/701] GPU: Remove non-working MI100 serialization workaround and obsolete StuckProtection --- GPU/GPUTracking/Base/GPUReconstructionCPU.h | 2 -- .../Base/opencl/GPUReconstructionOCL.cxx | 21 ------------------- .../Base/opencl/GPUReconstructionOCL.h | 1 - GPU/GPUTracking/Definitions/GPUSettingsList.h | 2 -- GPU/GPUTracking/Global/GPUChain.h | 2 -- .../Global/GPUChainTrackingSectorTracker.cxx | 3 --- prodtests/full-system-test/dpl-workflow.sh | 2 -- 7 files changed, 33 deletions(-) diff --git a/GPU/GPUTracking/Base/GPUReconstructionCPU.h b/GPU/GPUTracking/Base/GPUReconstructionCPU.h index a78a482db4e7a..d621d45fcd92b 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionCPU.h +++ b/GPU/GPUTracking/Base/GPUReconstructionCPU.h @@ -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/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/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index 5a075bf7f9a02..d70fac115eab7 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -301,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))") @@ -383,7 +382,6 @@ 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) diff --git a/GPU/GPUTracking/Global/GPUChain.h b/GPU/GPUTracking/Global/GPUChain.h index 9ce3da1092e83..6831fbd15080a 100644 --- a/GPU/GPUTracking/Global/GPUChain.h +++ b/GPU/GPUTracking/Global/GPUChain.h @@ -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) { diff --git a/GPU/GPUTracking/Global/GPUChainTrackingSectorTracker.cxx b/GPU/GPUTracking/Global/GPUChainTrackingSectorTracker.cxx index 122eb709b4356..e2d68f10819fb 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingSectorTracker.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingSectorTracker.cxx @@ -149,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); diff --git a/prodtests/full-system-test/dpl-workflow.sh b/prodtests/full-system-test/dpl-workflow.sh index ce5607d361cbe..754349c87eecc 100755 --- a/prodtests/full-system-test/dpl-workflow.sh +++ b/prodtests/full-system-test/dpl-workflow.sh @@ -284,8 +284,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 From 534219337bb9ccaebaca12b902bdac2a3fb513a2 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Mon, 8 Dec 2025 09:32:03 +0100 Subject: [PATCH 064/701] GPU QA: Dump also text output to output folder --- GPU/GPUTracking/qa/GPUQA.cxx | 11 ++++++++++- GPU/GPUTracking/qa/GPUQA.h | 2 ++ 2 files changed, 12 insertions(+), 1 deletion(-) diff --git a/GPU/GPUTracking/qa/GPUQA.cxx b/GPU/GPUTracking/qa/GPUQA.cxx index 28b603f77e2ff..3c176031dec08 100644 --- a/GPU/GPUTracking/qa/GPUQA.cxx +++ b/GPU/GPUTracking/qa/GPUQA.cxx @@ -3141,7 +3141,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); @@ -3153,6 +3155,9 @@ 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++) { // TODO: Check that these counts are still printed correctly! @@ -3191,6 +3196,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 7303ed62a9562..b42fa804c6212 100644 --- a/GPU/GPUTracking/qa/GPUQA.h +++ b/GPU/GPUTracking/qa/GPUQA.h @@ -62,6 +62,7 @@ class GPUQA #else #include "GPUTPCDef.h" +#include #include #include #include @@ -365,6 +366,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]); } From 7eb731a2da99bc377efdb2b5cd0c33a54fa49137 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 10 Dec 2025 19:16:24 +0100 Subject: [PATCH 065/701] GPU QA: Fix some task number inconsistencies --- GPU/GPUTracking/Global/GPUChainTracking.cxx | 4 +- GPU/GPUTracking/qa/GPUQA.cxx | 223 ++++++++++---------- GPU/GPUTracking/qa/GPUQA.h | 16 +- GPU/Workflow/src/GPUWorkflowSpec.cxx | 2 +- 4 files changed, 131 insertions(+), 114 deletions(-) diff --git a/GPU/GPUTracking/Global/GPUChainTracking.cxx b/GPU/GPUTracking/Global/GPUChainTracking.cxx index 14d0e04eb4dd3..0e7d4bc4f436e 100644 --- a/GPU/GPUTracking/Global/GPUChainTracking.cxx +++ b/GPU/GPUTracking/Global/GPUChainTracking.cxx @@ -475,7 +475,7 @@ int32_t GPUChainTracking::ForceInitQA() qa.reset(new GPUQA(this)); } if (!GetQA()->IsInitialized()) { - return GetQA()->InitQA(); + return GetQA()->InitQA(GetProcessingSettings().runQA <= 0 ? -GetProcessingSettings().runQA : GPUQA::tasksAutomatic); } return 0; } @@ -690,7 +690,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 : GPUQA::tasksAutomatic)) { return 1; } } diff --git a/GPU/GPUTracking/qa/GPUQA.cxx b/GPU/GPUTracking/qa/GPUQA.cxx index 3c176031dec08..852ac5c1feefb 100644 --- a/GPU/GPUTracking/qa/GPUQA.cxx +++ b/GPU/GPUTracking/qa/GPUQA.cxx @@ -544,7 +544,8 @@ int32_t GPUQA::InitQACreateHistograms() 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); @@ -577,8 +578,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"); @@ -593,7 +594,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()) { @@ -806,8 +807,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; @@ -815,7 +816,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; @@ -1823,7 +1824,7 @@ 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) { + 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++) { @@ -1831,64 +1832,68 @@ void GPUQA::RunQA(bool matchOnly, const std::vector* tracksEx 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 (GetMCTrackObj(mMCParam, label).pt <= 0.04) { - weight40 += 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(" "); + 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");*/ } - printf("\n");*/ + mClusterCounts.nAbove400++; } - mClusterCounts.nAbove400++; - } - if (totalWeight > 0 && weight40 >= 0.9 * totalWeight) { - mClusterCounts.nBelow40++; - if (r.protect || r.physics) { - mClusterCounts.nFakeProtect40++; + if (totalWeight > 0 && weight40 >= 0.9 * totalWeight) { + mClusterCounts.nBelow40++; + if (r.protect || r.physics) { + mClusterCounts.nFakeProtect40++; + } } } - } - if (r.physics) { - mClusterCounts.nPhysics++; - } - if (r.protect) { - mClusterCounts.nProt++; - } - if (r.unattached) { - mClusterCounts.nUnattached++; + if (r.physics) { + mClusterCounts.nPhysics++; + } + if (r.protect) { + mClusterCounts.nProt++; + } + if (r.unattached) { + mClusterCounts.nUnattached++; + } } - 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 (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); + } } } } @@ -2271,7 +2276,9 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) 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.); @@ -2283,7 +2290,9 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) 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.); @@ -3034,7 +3043,7 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) } } - mPClXY->cd(); + mPClXY->cd(); // TODO: This should become a separate task category mClXY->SetOption("colz"); mClXY->Draw(); mCClXY->cd(); @@ -3042,61 +3051,61 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) if (mConfig.writeFileExt != "") { mCClXY->Print(Form("%s/clustersXY.%s", mConfig.plotsDir.c_str(), mConfig.writeFileExt.c_str())); } + } - if (mQATasks & taskClusterCounts) { - mClRej[2]->Divide(mClRej[1], mClRej[0]); + 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(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())); - } + for (int32_t i = 0; i < 3; i++) { + if (tout && !mConfig.inputHistogramsOnly) { + mClRej[i]->Write(); + } + mPClRej[i]->cd(); + mClRej[i]->SetTitle(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); + 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("Rejected Clusters"); - e->GetXaxis()->SetTitle("Pad Row"); - e->GetYaxis()->SetTitle("Rejected Clusters (fraction)"); - e->Draw(k == 0 ? "" : "same"); + auto* e = mClRejP; + if (GetHist(e, tin, k, nNewInput) == nullptr) { + continue; } - 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())); + 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("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())); } } diff --git a/GPU/GPUTracking/qa/GPUQA.h b/GPU/GPUTracking/qa/GPUQA.h index b42fa804c6212..3dd49e2ec1373 100644 --- a/GPU/GPUTracking/qa/GPUQA.h +++ b/GPU/GPUTracking/qa/GPUQA.h @@ -56,6 +56,10 @@ class GPUQA static bool QAAvailable() { return false; } static bool IsInitialized() { return false; } void UpdateChain(GPUChainTracking* chain) {} + + enum QA_TASKS { + tasksAutomatic = 0 + }; }; } // namespace o2::gpu @@ -146,16 +150,20 @@ class GPUQA static constexpr int32_t MC_LABEL_INVALID = -1e9; - enum QA_TASKS { + enum QA_TASKS { // TODO: make this in32_t typed taskTrackingEff = 1, taskTrackingRes = 2, taskTrackingResPull = 4, + tasksAllMC = 8 - 1, taskClusterAttach = 8, taskTrackStatistics = 16, taskClusterCounts = 32, - taskDefault = 63, - taskDefaultPostprocess = 31, - tasksNoQC = 56 + taskClusterRejection = 64, + tasksAll = 128 - 1, + tasksDefault = tasksAll, + tasksDefaultPostprocess = tasksDefault & ~taskClusterCounts, + tasksAllNoQC = tasksAll & ~tasksAllMC, + tasksAutomatic = -1 }; private: diff --git a/GPU/Workflow/src/GPUWorkflowSpec.cxx b/GPU/Workflow/src/GPUWorkflowSpec.cxx index d7ea772c31653..fb1d489a8479d 100644 --- a/GPU/Workflow/src/GPUWorkflowSpec.cxx +++ b/GPU/Workflow/src/GPUWorkflowSpec.cxx @@ -180,7 +180,7 @@ 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 ? 15 : 0) | (mConfig->configQA.clusterRejectionHistograms ? 32 : 0); // TODO: Clean up using numeric flags! mConfig->configProcessing.runQA = -mQATaskMask; } } From 4d0047ce8f4c45fe0ed7abe664b230a163d39d78 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 29 Oct 2025 14:45:23 +0100 Subject: [PATCH 066/701] GPU TPC: Fix deterministic mode in combination of propagation of MC labels --- GPU/GPUTracking/Global/GPUChainTracking.h | 1 + .../Global/GPUChainTrackingClusterizer.cxx | 92 ++++++++++++++----- 2 files changed, 72 insertions(+), 21 deletions(-) diff --git a/GPU/GPUTracking/Global/GPUChainTracking.h b/GPU/GPUTracking/Global/GPUChainTracking.h index 8de49cc954e35..4b07aadfad357 100644 --- a/GPU/GPUTracking/Global/GPUChainTracking.h +++ b/GPU/GPUTracking/Global/GPUChainTracking.h @@ -306,6 +306,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 fdce8ef5a127d..c4566ffb968a7 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx @@ -57,6 +57,8 @@ #include "utils/VcShim.h" #include "utils/strtag.h" #include +#include +#include using namespace o2::gpu; using namespace o2::tpc; @@ -762,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); @@ -1281,21 +1282,20 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) } 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); @@ -1350,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")); @@ -1374,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); + } +} From 07e63da6ecc1733b0d6b5ce28afc7c852f1b0a89 Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Fri, 12 Dec 2025 10:19:12 +0100 Subject: [PATCH 067/701] DPL: fix reversed index when filling DataProcessingStates --- Framework/Core/src/DataRelayer.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Framework/Core/src/DataRelayer.cxx b/Framework/Core/src/DataRelayer.cxx index df95aeda92a2b..01e7a2b29fd35 100644 --- a/Framework/Core/src/DataRelayer.cxx +++ b/Framework/Core/src/DataRelayer.cxx @@ -1055,7 +1055,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, From 16f1a93b6982837ef3f188a4b9067888d843e874 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sat, 13 Dec 2025 09:17:14 +0100 Subject: [PATCH 068/701] Cleanup jobutils2 to work on ARM (#14901) --- Utilities/Tools/jobutils2.sh | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) 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}" } From c628d2979878efaeda5a4cabbea11b653c3e369d Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Tue, 9 Dec 2025 16:39:01 +0100 Subject: [PATCH 069/701] Integrate non-uniform InteractionSampler into CollisionContextTool Possibility to inject non-uniform MU(BC) distributions into the collision context creation. Distributions can come from ROOT file or CCDB and follow a format from EventSelectionQA (histogram hBcTVX). Example: ``` --nontrivial-mu-distribution ccdb://http://ccdb-test.cern.ch:8080/GLO/CALIB/EVSELQA/HBCTVX' ``` --- .../simulation/src/InteractionSampler.cxx | 3 + Steer/src/CollisionContextTool.cxx | 79 ++++++++++++++++++- 2 files changed, 79 insertions(+), 3 deletions(-) diff --git a/DataFormats/simulation/src/InteractionSampler.cxx b/DataFormats/simulation/src/InteractionSampler.cxx index 61b2c4f61bc08..f3ece5c51f90b 100644 --- a/DataFormats/simulation/src/InteractionSampler.cxx +++ b/DataFormats/simulation/src/InteractionSampler.cxx @@ -202,6 +202,9 @@ bool NonUniformMuInteractionSampler::setBCIntensityScales(const TH1F& 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) { diff --git a/Steer/src/CollisionContextTool.cxx b/Steer/src/CollisionContextTool.cxx index a6c2b0e62e0ca..b884909aedd9d 100644 --- a/Steer/src/CollisionContextTool.cxx +++ b/Steer/src/CollisionContextTool.cxx @@ -29,6 +29,9 @@ #include "DataFormatsParameters/GRPLHCIFData.h" #include "SimConfig/SimConfig.h" #include +#include +#include +#include // // Created by Sandro Wenzel on 13.07.21. @@ -64,6 +67,8 @@ struct Options { // 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 { @@ -72,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; @@ -216,8 +243,8 @@ bool parseOptions(int argc, char* argv[], Options& optvalues) "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]")( - "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."); + "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."); @@ -397,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")) { @@ -418,11 +485,17 @@ 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 { sampler->setFirstIR(o2::InteractionRecord(options.firstBC, orbitstart)); - sampler->init(); record = sampler->generateCollisionTime(); } while (options.noEmptyTF && usetimeframelength && record.orbit >= orbitstart + orbits_total); int count = 0; From deff3d8eb8595991c19dd4e5b166980c08c1ba98 Mon Sep 17 00:00:00 2001 From: Matthias Kleiner <48915672+matthias-kleiner@users.noreply.github.com> Date: Sat, 13 Dec 2025 19:26:36 +0100 Subject: [PATCH 070/701] TPC: Use CTP as fallback if no IDCs are available (#14919) * TPC: Use CTP as fallback if no IDCs are available * Dont update map as only the lumi is updated --- .../TPCCalibration/CorrectionMapsLoader.h | 1 + .../calibration/src/CorrectionMapsLoader.cxx | 37 +++++++++++++++++-- 2 files changed, 34 insertions(+), 4 deletions(-) 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/src/CorrectionMapsLoader.cxx b/Detectors/TPC/calibration/src/CorrectionMapsLoader.cxx index 0e4a5e2a73df4..e9d7474699ce2 100644 --- a/Detectors/TPC/calibration/src/CorrectionMapsLoader.cxx +++ b/Detectors/TPC/calibration/src/CorrectionMapsLoader.cxx @@ -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() From 519b2f7b7d84edd893cc28a55569497fd6d6fdf2 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 30 Oct 2025 16:57:23 +0100 Subject: [PATCH 071/701] GPU: Unify applying settings for sync reco between standalone and GPUWorkflow --- GPU/GPUTracking/Global/GPUChainTracking.cxx | 14 ++++++++++ GPU/GPUTracking/Global/GPUChainTracking.h | 3 ++ GPU/GPUTracking/Interface/GPUO2Interface.cxx | 5 ++++ GPU/GPUTracking/Interface/GPUO2Interface.h | 3 ++ .../Standalone/Benchmark/standalone.cxx | 28 ++++++------------- GPU/Workflow/src/GPUWorkflowSpec.cxx | 11 ++------ 6 files changed, 36 insertions(+), 28 deletions(-) diff --git a/GPU/GPUTracking/Global/GPUChainTracking.cxx b/GPU/GPUTracking/Global/GPUChainTracking.cxx index 0e7d4bc4f436e..8a0d45a33ca93 100644 --- a/GPU/GPUTracking/Global/GPUChainTracking.cxx +++ b/GPU/GPUTracking/Global/GPUChainTracking.cxx @@ -1007,3 +1007,17 @@ 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; + rec.tpc.rebuildTrackMaxNonIntCov = 0.f; + } + 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 4b07aadfad357..7d70e0b667946 100644 --- a/GPU/GPUTracking/Global/GPUChainTracking.h +++ b/GPU/GPUTracking/Global/GPUChainTracking.h @@ -66,6 +66,8 @@ struct GPUNewCalibValues; struct GPUTriggerOutputs; struct CfFragment; class GPUTPCClusterFinder; +struct GPUSettingsProcessing; +struct GPUSettingsRec; class GPUChainTracking : public GPUChain { @@ -86,6 +88,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; diff --git a/GPU/GPUTracking/Interface/GPUO2Interface.cxx b/GPU/GPUTracking/Interface/GPUO2Interface.cxx index d04db5e9bf271..95a57a4b17c4b 100644 --- a/GPU/GPUTracking/Interface/GPUO2Interface.cxx +++ b/GPU/GPUTracking/Interface/GPUO2Interface.cxx @@ -268,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 00c72cc5e3359..3b4dde2cb0f96 100644 --- a/GPU/GPUTracking/Interface/GPUO2Interface.h +++ b/GPU/GPUTracking/Interface/GPUO2Interface.h @@ -56,6 +56,8 @@ struct GPUInterfaceInputUpdate; struct GPUTrackingOutputs; struct GPUConstantMem; struct GPUNewCalibValues; +struct GPUSettingsProcessing; +struct GPUSettingsRec; struct GPUO2Interface_processingContext; struct GPUO2Interface_Internals; @@ -80,6 +82,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/Standalone/Benchmark/standalone.cxx b/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx index 857803d913372..1b3603a226af0 100644 --- a/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx +++ b/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx @@ -377,9 +377,6 @@ int32_t SetupReconstruction() } 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); - } if (configStandalone.runCompression != -1) { steps.steps.setBits(GPUDataTypes::RecoStep::TPCCompression, configStandalone.runCompression > 0); } @@ -434,23 +431,15 @@ int32_t SetupReconstruction() } } - bool runAsyncQA = procSet.runQA && !configStandalone.testSyncAsyncQcInSync; - 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.testSyncAsyncQcInSync) { - procSet.runQA = 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; } } - if (configStandalone.proc.rtc.optSpecialCode == -1) { - configStandalone.proc.rtc.optSpecialCode = configStandalone.testSyncAsync || configStandalone.testSync; - } rec->SetSettings(&grp, &recSet, &procSet, &steps); if (configStandalone.proc.doublePipeline) { @@ -470,13 +459,12 @@ int32_t SetupReconstruction() 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); } diff --git a/GPU/Workflow/src/GPUWorkflowSpec.cxx b/GPU/Workflow/src/GPUWorkflowSpec.cxx index fb1d489a8479d..ca929bb025f80 100644 --- a/GPU/Workflow/src/GPUWorkflowSpec.cxx +++ b/GPU/Workflow/src/GPUWorkflowSpec.cxx @@ -185,20 +185,15 @@ void GPURecoWorkflowSpec::init(InitContext& ic) } } 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; + GPUO2Interface::ApplySyncSettings(mConfig->configProcessing, mConfig->configReconstruction, mConfig->configWorkflow.steps, mConfParam->synchronousProcessing, runTracking ? mConfParam->rundEdx : -2); // Configure the "GPU workflow" i.e. which steps we run on the GPU (or CPU) - if (mSpecConfig.outputTracks || mSpecConfig.outputCompClustersRoot || mSpecConfig.outputCompClustersFlat) { + if (runTracking) { 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 (mSpecConfig.outputCompClustersRoot || mSpecConfig.outputCompClustersFlat) { mConfig->configWorkflow.steps.setBits(GPUDataTypes::RecoStep::TPCCompression, true); From d6d20b4ec4049593fbe5d49b9bc667976ceaf98c Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 11 Dec 2025 20:56:56 +0100 Subject: [PATCH 072/701] GPU DataTypes / QA: Clean up some data types / enums --- .../DataFormatsTRD/RecoInputContainer.h | 2 +- .../include/ITStracking/TrackingInterface.h | 2 +- .../include/ITSWorkflow/RecoWorkflow.h | 4 +- .../include/ITSWorkflow/TrackerSpec.h | 12 ++- .../ITSMFT/ITS/workflow/src/RecoWorkflow.cxx | 2 +- .../ITSMFT/ITS/workflow/src/TrackerSpec.cxx | 4 +- .../ITS/workflow/src/its-reco-workflow.cxx | 2 +- .../reconstruction/test/testGPUCATracking.cxx | 10 +-- Detectors/TPC/workflow/src/ZSSpec.cxx | 2 +- .../workflow/src/TRDGlobalTrackingSpec.cxx | 4 +- .../include/TRKWorkflow/RecoWorkflow.h | 4 +- .../include/TRKWorkflow/TrackerSpec.h | 8 +- .../ALICE3/TRK/workflow/src/RecoWorkflow.cxx | 4 +- .../ALICE3/TRK/workflow/src/TrackerSpec.cxx | 4 +- .../TRK/workflow/src/trk-reco-workflow.cxx | 2 +- .../include/ITS3Workflow/RecoWorkflow.h | 6 +- .../include/ITS3Workflow/TrackerSpec.h | 12 ++- .../ITS3/workflow/src/RecoWorkflow.cxx | 2 +- .../ITS3/workflow/src/TrackerSpec.cxx | 4 +- .../ITS3/workflow/src/its3-reco-workflow.cxx | 2 +- GPU/GPUTracking/Base/GPUConstantMem.h | 2 +- GPU/GPUTracking/Base/GPUGeneralKernels.h | 9 ++- GPU/GPUTracking/Base/GPUParam.cxx | 4 +- GPU/GPUTracking/Base/GPUReconstruction.cxx | 10 +-- GPU/GPUTracking/Base/GPUReconstruction.h | 14 ++-- GPU/GPUTracking/Base/GPUReconstructionCPU.cxx | 14 ++-- .../Base/GPUReconstructionCPUKernels.h | 4 +- .../Base/GPUReconstructionConvert.cxx | 2 +- .../Base/GPUReconstructionIncludes.h | 2 +- .../Base/GPUReconstructionLibrary.cxx | 10 +-- .../Base/GPUReconstructionProcessing.h | 12 +-- .../Base/GPUReconstructionTimeframe.h | 2 +- GPU/GPUTracking/CMakeLists.txt | 13 ++- .../DataCompression/GPUTPCCompression.cxx | 6 +- .../GPUTPCCompressionKernels.h | 2 +- .../GPUTPCDecompressionKernels.h | 2 +- GPU/GPUTracking/DataTypes/GPUConfigDump.cxx | 2 +- ...PUDataTypes.cxx => GPUDataTypesConfig.cxx} | 10 +-- .../DataTypes/GPUDataTypesConfig.h | 80 +++++++++++++++++++ .../{GPUDataTypes.h => GPUDataTypesIO.h} | 66 +++------------ GPU/GPUTracking/DataTypes/GPUDataTypesQA.h | 42 ++++++++++ .../DataTypes/GPUO2ConfigurableParam.cxx | 4 +- GPU/GPUTracking/DataTypes/GPUSettings.h | 4 +- GPU/GPUTracking/Global/GPUChain.cxx | 12 +-- GPU/GPUTracking/Global/GPUChain.h | 12 +-- GPU/GPUTracking/Global/GPUChainITS.cxx | 2 +- GPU/GPUTracking/Global/GPUChainTracking.cxx | 73 +++++++++-------- GPU/GPUTracking/Global/GPUChainTracking.h | 5 +- .../Global/GPUChainTrackingClusterizer.cxx | 10 +-- .../GPUChainTrackingDebugAndProfiling.cxx | 2 +- .../Global/GPUChainTrackingTransformation.cxx | 4 +- GPU/GPUTracking/Global/GPUErrors.cxx | 2 +- .../Global/GPUTrackingInputProvider.cxx | 6 +- GPU/GPUTracking/Interface/GPUO2Interface.cxx | 8 +- GPU/GPUTracking/Interface/GPUO2Interface.h | 5 +- .../Interface/GPUO2InterfaceConfiguration.cxx | 2 +- .../Interface/GPUO2InterfaceConfiguration.h | 2 +- .../Interface/GPUO2InterfaceDisplay.h | 2 +- GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx | 8 +- GPU/GPUTracking/Merger/GPUTPCGMMergerGPU.h | 2 +- .../Merger/GPUTPCGlobalDebugSortKernels.h | 2 +- .../Refit/GPUTrackingRefitKernel.h | 2 +- .../SectorTracker/GPUTPCCreateOccupancyMap.h | 2 +- .../SectorTracker/GPUTPCCreateTrackingData.h | 2 +- .../GPUTPCExtrapolationTracking.cxx | 26 +++--- .../GPUTPCExtrapolationTracking.h | 4 +- .../SectorTracker/GPUTPCNeighboursCleaner.h | 2 +- .../SectorTracker/GPUTPCNeighboursFinder.h | 2 +- .../GPUTPCSectorDebugSortKernels.h | 2 +- .../SectorTracker/GPUTPCStartHitsFinder.h | 2 +- .../SectorTracker/GPUTPCStartHitsSorter.h | 2 +- .../SectorTracker/GPUTPCTracker.cxx | 10 +-- .../SectorTracker/GPUTPCTrackletConstructor.h | 2 +- .../SectorTracker/GPUTPCTrackletSelector.h | 2 +- .../Standalone/Benchmark/standalone.cxx | 76 +++++++++--------- .../TPCClusterFinder/GPUTPCCFChainContext.h | 2 +- .../GPUTPCCFChargeMapFiller.h | 4 +- .../GPUTPCCFCheckPadBaseline.h | 4 +- .../TPCClusterFinder/GPUTPCCFClusterizer.h | 4 +- .../TPCClusterFinder/GPUTPCCFDecodeZS.h | 8 +- .../TPCClusterFinder/GPUTPCCFDeconvolution.h | 4 +- .../TPCClusterFinder/GPUTPCCFGather.h | 4 +- .../GPUTPCCFMCLabelFlattener.h | 4 +- .../GPUTPCCFNoiseSuppression.h | 4 +- .../TPCClusterFinder/GPUTPCCFPeakFinder.h | 4 +- .../GPUTPCCFStreamCompaction.h | 4 +- .../TPCClusterFinder/GPUTPCClusterFinder.cxx | 16 ++-- .../TPCClusterFinder/GPUTPCClusterFinder.h | 2 +- .../GPUTPCNNClusterizerKernels.h | 4 +- .../TRDTracking/GPUTRDTrackerKernels.h | 2 +- .../TRDTracking/macros/run_trd_tracker.C | 4 +- GPU/GPUTracking/dEdx/GPUdEdx.h | 1 + GPU/GPUTracking/display/GPUDisplay.h | 1 + .../display/render/GPUDisplayDraw.cxx | 3 +- GPU/GPUTracking/qa/GPUQA.h | 22 +---- .../GPUWorkflowHelper/GPUWorkflowHelper.h | 2 +- GPU/Workflow/src/GPUWorkflowInternal.h | 2 +- GPU/Workflow/src/GPUWorkflowPipeline.cxx | 2 +- GPU/Workflow/src/GPUWorkflowSpec.cxx | 45 ++++++----- doc/data/2021-01-o2_prs.json | 4 +- 100 files changed, 462 insertions(+), 397 deletions(-) rename GPU/GPUTracking/DataTypes/{GPUDataTypes.cxx => GPUDataTypesConfig.cxx} (73%) create mode 100644 GPU/GPUTracking/DataTypes/GPUDataTypesConfig.h rename GPU/GPUTracking/DataTypes/{GPUDataTypes.h => GPUDataTypesIO.h} (74%) create mode 100644 GPU/GPUTracking/DataTypes/GPUDataTypesQA.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/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h index d31b1f11a4983..a882ca9b779c4 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h @@ -24,7 +24,7 @@ #include "DataFormatsITSMFT/TopologyDictionary.h" #include "DataFormatsCalibration/MeanVertexObject.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" #include "GPUO2ExternalUser.h" #include "GPUChainITS.h" diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/RecoWorkflow.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/RecoWorkflow.h index 011ee6b88ff6f..90b38acb34a95 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 { @@ -28,7 +28,7 @@ namespace reco_workflow framework::WorkflowSpec getWorkflow(bool useMC, bool useCMtracker, 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/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/RecoWorkflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx index f375eaf67c04f..368ca6909240f 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx @@ -38,7 +38,7 @@ 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)) { 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..4e1721f4194b0 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/its-reco-workflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/its-reco-workflow.cxx @@ -69,7 +69,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) 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"); 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/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/TRD/workflow/src/TRDGlobalTrackingSpec.cxx b/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx index 598ce3c35c98c..9588888df5fc6 100644 --- a/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx +++ b/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx @@ -46,7 +46,7 @@ #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 +103,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); diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/RecoWorkflow.h b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/RecoWorkflow.h index 0c2489aa4b9c4..98d4154f11dd8 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/RecoWorkflow.h +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/RecoWorkflow.h @@ -13,7 +13,7 @@ #define O2_TRK_RECOWORKFLOW_H #include "Framework/WorkflowSpec.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesConfig.h" namespace o2::trk { @@ -25,7 +25,7 @@ o2::framework::WorkflowSpec getWorkflow(bool useMC, 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/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h index 3c82a4fd7b89d..dac1826e21cf6 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h @@ -20,7 +20,7 @@ #include "Framework/Task.h" #include "ITStracking/TrackingInterface.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesConfig.h" #include "DetectorsBase/GRPGeomHelper.h" @@ -33,7 +33,7 @@ class TrackerDPL : public framework::Task public: TrackerDPL(std::shared_ptr gr, bool isMC, - gpu::GPUDataTypes::DeviceType dType = gpu::GPUDataTypes::DeviceType::CPU); + gpu::gpudatatypes::DeviceType dType = gpu::gpudatatypes::DeviceType::CPU); ~TrackerDPL() override = default; void init(framework::InitContext& ic) final; void run(framework::ProcessingContext& pc) final; @@ -50,7 +50,7 @@ class TrackerDPL : public framework::Task TStopwatch mTimer; }; -framework::DataProcessorSpec getTrackerSpec(bool useMC, gpu::GPUDataTypes::DeviceType dType = gpu::GPUDataTypes::DeviceType::CPU); +framework::DataProcessorSpec getTrackerSpec(bool useMC, 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..09d447a576e48 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx @@ -21,11 +21,11 @@ framework::WorkflowSpec getWorkflow(bool useMC, 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)); 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/TrackerSpec.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx index 4057bab3b948f..868a8acc0fc6e 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx @@ -25,7 +25,7 @@ using Vertex = o2::dataformats::Vertex>; TrackerDPL::TrackerDPL(std::shared_ptr gr, bool isMC, - o2::gpu::GPUDataTypes::DeviceType dType) + o2::gpu::gpudatatypes::DeviceType dType) { // mITSTrackingInterface.setTrackingMode(trMode); } @@ -67,7 +67,7 @@ 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, o2::gpu::gpudatatypes::DeviceType dType) { std::vector inputs; 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..8f44b01da1c9c 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/trk-reco-workflow.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/trk-reco-workflow.cxx @@ -67,7 +67,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) // Update the (declared) parameters if changed from the command line auto useMC = !configcontext.options().get("disable-mc"); 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"); 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..004c3f6097167 100644 --- a/Detectors/Upgrades/ITS3/workflow/src/RecoWorkflow.cxx +++ b/Detectors/Upgrades/ITS3/workflow/src/RecoWorkflow.cxx @@ -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; diff --git a/Detectors/Upgrades/ITS3/workflow/src/TrackerSpec.cxx b/Detectors/Upgrades/ITS3/workflow/src/TrackerSpec.cxx index 216056153d095..0326c12f804e0 100644 --- a/Detectors/Upgrades/ITS3/workflow/src/TrackerSpec.cxx +++ b/Detectors/Upgrades/ITS3/workflow/src/TrackerSpec.cxx @@ -44,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} { @@ -88,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/GPU/GPUTracking/Base/GPUConstantMem.h b/GPU/GPUTracking/Base/GPUConstantMem.h index c496151c3dfd0..efb83a7e874c8 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" 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/GPUParam.cxx b/GPU/GPUTracking/Base/GPUParam.cxx index 7095766e8512e..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" @@ -127,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/GPUReconstruction.cxx b/GPU/GPUTracking/Base/GPUReconstruction.cxx index cae7c5025609b..ef336526080b9 100644 --- a/GPU/GPUTracking/Base/GPUReconstruction.cxx +++ b/GPU/GPUTracking/Base/GPUReconstruction.cxx @@ -296,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) { @@ -902,7 +902,7 @@ void GPUReconstruction::PopNonPersistentMemory(RecoStep step, uint64_t tag, cons 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 (!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()); + 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(); @@ -1074,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() { @@ -1222,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 fa636fa416538..b5dd29f940143 100644 --- a/GPU/GPUTracking/Base/GPUReconstruction.h +++ b/GPU/GPUTracking/Base/GPUReconstruction.h @@ -27,7 +27,7 @@ #include #include -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" #include "GPUMemoryResource.h" #include "GPUOutputControl.h" #include "GPUParam.h" @@ -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 diff --git a/GPU/GPUTracking/Base/GPUReconstructionCPU.cxx b/GPU/GPUTracking/Base/GPUReconstructionCPU.cxx index 3da96654b895d..409c28b8bf328 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionCPU.cxx +++ b/GPU/GPUTracking/Base/GPUReconstructionCPU.cxx @@ -262,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++) { @@ -296,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) { @@ -319,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) { 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/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/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index 6dd718f07a9f1..6a60eb9edd6d0 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -64,11 +64,11 @@ set(SRCS 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/CalibdEdxTrackTopologyPol.h DataTypes/CalibdEdxTrackTopologySpline.h) # Manual dependencies for ROOT dictionary generation set(SRCS_NO_CINT DataTypes/GPUMemorySizeScalers.cxx @@ -107,6 +107,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,9 +126,7 @@ 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 @@ -139,13 +140,10 @@ set(HDRS_INSTALL 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 @@ -160,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 diff --git a/GPU/GPUTracking/DataCompression/GPUTPCCompression.cxx b/GPU/GPUTracking/DataCompression/GPUTPCCompression.cxx index 85cd9598e0bf1..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,7 +106,7 @@ 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"); 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/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/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 8bf8084e048fd..fd98cba1dadaa 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" @@ -96,8 +95,6 @@ class TPCFastTransform; struct TPCPadGainCalib; struct TPCZSLinkMapping; -#include "utils/bitfield.h" - class GPUTPCTrack; class GPUTPCHitId; class GPUTPCGMMergedTrack; @@ -111,55 +108,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 { @@ -191,7 +143,7 @@ typedef GPUCalibObjectsTemplate GPUCalibObjects; // NOTE: These 2 mu 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]; @@ -209,7 +161,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; @@ -219,7 +171,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/GPUO2ConfigurableParam.cxx b/GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.cxx index f5b3ea8b285f5..60fdbe8042c2d 100644 --- a/GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.cxx +++ b/GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.cxx @@ -14,7 +14,7 @@ #include "GPUO2ConfigurableParam.h" #include "Interface/GPUO2InterfaceConfiguration.h" -#include "GPUDataTypes.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/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/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 6831fbd15080a..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; } diff --git a/GPU/GPUTracking/Global/GPUChainITS.cxx b/GPU/GPUTracking/Global/GPUChainITS.cxx index c72023bdf42ce..598f7a61cac1a 100644 --- a/GPU/GPUTracking/Global/GPUChainITS.cxx +++ b/GPU/GPUTracking/Global/GPUChainITS.cxx @@ -36,7 +36,7 @@ class GPUFrameworkExternalAllocator final : public o2::its::ExternalAllocator } void popTagOffStack(uint64_t tag) final { - mFWReco->PopNonPersistentMemory(GPUDataTypes::RecoStep::ITSTracking, tag); + mFWReco->PopNonPersistentMemory(gpudatatypes::RecoStep::ITSTracking, tag); } void setReconstructionFramework(o2::gpu::GPUReconstruction* fwr) { mFWReco = fwr; } diff --git a/GPU/GPUTracking/Global/GPUChainTracking.cxx b/GPU/GPUTracking/Global/GPUChainTracking.cxx index 8a0d45a33ca93..f370b756e2cdb 100644 --- a/GPU/GPUTracking/Global/GPUChainTracking.cxx +++ b/GPU/GPUTracking/Global/GPUChainTracking.cxx @@ -168,78 +168,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 +248,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 +265,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,7 +273,7 @@ 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; } @@ -282,13 +282,13 @@ bool GPUChainTracking::ValidateSettings() GPUError("Cannot use double pipeline with tpcFreeAllocatedMemoryAfterProcessing"); return false; } - if (!GetRecoStepsOutputs().isOnlySet(GPUDataTypes::InOutType::TPCMergedTracks, GPUDataTypes::InOutType::TPCCompressedClusters, GPUDataTypes::InOutType::TPCClusters)) { + 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; @@ -297,16 +297,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; } @@ -332,9 +332,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)"); } @@ -475,7 +475,7 @@ int32_t GPUChainTracking::ForceInitQA() qa.reset(new GPUQA(this)); } if (!GetQA()->IsInitialized()) { - return GetQA()->InitQA(GetProcessingSettings().runQA <= 0 ? -GetProcessingSettings().runQA : GPUQA::tasksAutomatic); + return GetQA()->InitQA(GetProcessingSettings().runQA <= 0 ? -GetProcessingSettings().runQA : gpudatatypes::gpuqa::tasksAutomatic); } return 0; } @@ -640,7 +640,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()) { @@ -690,7 +690,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 <= 0 ? -GetProcessingSettings().runQA : GPUQA::tasksAutomatic)) { + if (GetQA()->InitQA(GetProcessingSettings().runQA <= 0 ? -GetProcessingSettings().runQA : gpudatatypes::gpuqa::tasksAutomatic)) { return 1; } } @@ -1008,16 +1008,15 @@ void GPUChainTracking::SetO2Propagator(const o2::base::Propagator* prop) } } -void GPUChainTracking::ApplySyncSettings(GPUSettingsProcessing& proc, GPUSettingsRec& rec, GPUDataTypes::RecoStepField& steps, bool syncMode, int32_t dEdxMode) +void GPUChainTracking::ApplySyncSettings(GPUSettingsProcessing& proc, GPUSettingsRec& rec, gpudatatypes::RecoStepField& steps, bool syncMode, int32_t dEdxMode) { if (syncMode) { rec.useMatLUT = false; - rec.tpc.rebuildTrackMaxNonIntCov = 0.f; } if (proc.rtc.optSpecialCode == -1) { proc.rtc.optSpecialCode = syncMode; } if (dEdxMode != -2) { - steps.setBits(GPUDataTypes::RecoStep::TPCdEdx, dEdxMode == -1 ? !syncMode : dEdxMode > 0); + 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 7d70e0b667946..2dd1ece856ecf 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 @@ -88,7 +89,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); + static void ApplySyncSettings(GPUSettingsProcessing& proc, GPUSettingsRec& rec, gpudatatypes::RecoStepField& steps, bool syncMode, int32_t dEdxMode = -2); // Structures for input and output data GPUTrackingInOutPointers& mIOPtrs; diff --git a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx index c4566ffb968a7..7629086272ed6 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx @@ -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,7 +169,7 @@ 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++) { @@ -475,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; @@ -765,7 +765,7 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) std::unique_ptr tmpNativeClusterBuffer; 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 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); @@ -1277,7 +1277,7 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) } // Number of clusters is logged by tracking. This ensures clusters are still printed if it's not running - if (!(GetRecoSteps() & GPUDataTypes::RecoStep::TPCSectorTracking)) { + if (!(GetRecoSteps() & gpudatatypes::RecoStep::TPCSectorTracking)) { GPUInfo("Event has %zu TPC Clusters", nClsTotal); } diff --git a/GPU/GPUTracking/Global/GPUChainTrackingDebugAndProfiling.cxx b/GPU/GPUTracking/Global/GPUChainTrackingDebugAndProfiling.cxx index fab7179876c04..fd3c03a8b29ec 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingDebugAndProfiling.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingDebugAndProfiling.cxx @@ -240,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/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/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 9bb8b230e9e0b..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); @@ -101,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/Interface/GPUO2Interface.cxx b/GPU/GPUTracking/Interface/GPUO2Interface.cxx index 95a57a4b17c4b..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(); } @@ -269,7 +269,7 @@ 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) +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 3b4dde2cb0f96..ca56018908b41 100644 --- a/GPU/GPUTracking/Interface/GPUO2Interface.h +++ b/GPU/GPUTracking/Interface/GPUO2Interface.h @@ -17,7 +17,8 @@ #include "GPUO2ExternalUser.h" #include "GPUCommonDef.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" +#include "GPUDataTypesConfig.h" #include #include @@ -82,7 +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); + 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 f378fc2c4fb7b..0f8a3784f0a88 100644 --- a/GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.h +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.h @@ -17,7 +17,7 @@ #include "GPUO2ExternalUser.h" #include "GPUSettings.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" #include "GPUHostDataTypes.h" #include "GPUOutputControl.h" #include "DataFormatsTPC/Constants.h" diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceDisplay.h b/GPU/GPUTracking/Interface/GPUO2InterfaceDisplay.h index a7e9d309b6d3d..c0946bab0076d 100644 --- a/GPU/GPUTracking/Interface/GPUO2InterfaceDisplay.h +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceDisplay.h @@ -18,7 +18,7 @@ #include "GPUO2ExternalUser.h" #include #include -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" namespace o2::gpu { diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx b/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx index 8793f3bb399c8..260781c17406b 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx @@ -420,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); } @@ -443,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; @@ -515,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(); @@ -533,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; 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/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/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 c5e6a21460a36..03931f73a4a12 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; } @@ -86,7 +86,7 @@ void* GPUTPCTracker::SetPointersCommon(void* mem) bool GPUTPCTracker::MemoryReuseAllowed() { - return !mRec->GetProcessingSettings().keepDisplayMemory && ((mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCSectorTracking) || mRec->GetProcessingSettings().inKernelParallel == 1 || mRec->GetProcessingSettings().nHostThreads == 1); + return !mRec->GetProcessingSettings().keepDisplayMemory && ((mRec->GetRecoStepsGPU() & gpudatatypes::RecoStep::TPCSectorTracking) || mRec->GetProcessingSettings().inKernelParallel == 1 || mRec->GetProcessingSettings().nHostThreads == 1); } void GPUTPCTracker::RegisterMemoryAllocation() @@ -158,7 +158,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/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/standalone.cxx b/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx index 1b3603a226af0..b9825bc6da481 100644 --- a/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx +++ b/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx @@ -371,45 +371,45 @@ 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); + 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); } if (configStandalone.recoSteps >= 0) { @@ -420,12 +420,12 @@ 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 } @@ -447,14 +447,14 @@ int32_t SetupReconstruction() } 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 = runAsyncQA; procSet.eventDisplay = eventDisplay.get(); @@ -724,7 +724,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)); 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.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.h index 2403aa6d29ecd..25c93a4649662 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.h @@ -43,9 +43,9 @@ 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; } template 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.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h index c633a5ebc2774..b8ff90f511057 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFDecodeZS.h @@ -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,9 +72,9 @@ 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 { 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/GPUTPCNNClusterizerKernels.h b/GPU/GPUTracking/TPCClusterFinder/GPUTPCNNClusterizerKernels.h index 9353722568b1f..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 { 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..acfcf92370b00 100644 --- a/GPU/GPUTracking/TRDTracking/macros/run_trd_tracker.C +++ b/GPU/GPUTracking/TRDTracking/macros/run_trd_tracker.C @@ -17,7 +17,7 @@ #include "GPUReconstruction.h" #include "GPUChainTracking.h" #include "GPUSettings.h" -#include "GPUDataTypes.h" +#include "GPUDataTypesIO.h" #include "GPUTRDDef.h" #include "GPUTRDTrack.h" #include "GPUTRDTracker.h" @@ -70,7 +70,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); diff --git a/GPU/GPUTracking/dEdx/GPUdEdx.h b/GPU/GPUTracking/dEdx/GPUdEdx.h index 758c2a7eabfca..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 { diff --git a/GPU/GPUTracking/display/GPUDisplay.h b/GPU/GPUTracking/display/GPUDisplay.h index 837995ef38bb4..b0c1c1d11f2cf 100644 --- a/GPU/GPUTracking/display/GPUDisplay.h +++ b/GPU/GPUTracking/display/GPUDisplay.h @@ -37,6 +37,7 @@ class GPUTPCTracker; struct GPUParam; class GPUQA; class GPUTRDGeometry; +class GPUTPCGMPropagator; class GPUDisplay : public GPUDisplayInterface { 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/qa/GPUQA.h b/GPU/GPUTracking/qa/GPUQA.h index 3dd49e2ec1373..346c56a898806 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; @@ -56,16 +57,13 @@ class GPUQA static bool QAAvailable() { return false; } static bool IsInitialized() { return false; } void UpdateChain(GPUChainTracking* chain) {} - - enum QA_TASKS { - tasksAutomatic = 0 - }; }; } // namespace o2::gpu #else #include "GPUTPCDef.h" +#include "GPUDataTypesIO.h" #include #include #include @@ -150,21 +148,7 @@ class GPUQA static constexpr int32_t MC_LABEL_INVALID = -1e9; - enum QA_TASKS { // TODO: make this in32_t typed - taskTrackingEff = 1, - taskTrackingRes = 2, - taskTrackingResPull = 4, - tasksAllMC = 8 - 1, - taskClusterAttach = 8, - taskTrackStatistics = 16, - taskClusterCounts = 32, - taskClusterRejection = 64, - tasksAll = 128 - 1, - tasksDefault = tasksAll, - tasksDefaultPostprocess = tasksDefault & ~taskClusterCounts, - tasksAllNoQC = tasksAll & ~tasksAllMC, - tasksAutomatic = -1 - }; + using enum gpudatatypes::gpuqa::gpuQATaskIds; private: struct additionalMCParameters { 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/src/GPUWorkflowInternal.h b/GPU/Workflow/src/GPUWorkflowInternal.h index 73d3676f3d84a..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 diff --git a/GPU/Workflow/src/GPUWorkflowPipeline.cxx b/GPU/Workflow/src/GPUWorkflowPipeline.cxx index ba395cd98d64d..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" diff --git a/GPU/Workflow/src/GPUWorkflowSpec.cxx b/GPU/Workflow/src/GPUWorkflowSpec.cxx index ca929bb025f80..6011cc3dc3e9f 100644 --- a/GPU/Workflow/src/GPUWorkflowSpec.cxx +++ b/GPU/Workflow/src/GPUWorkflowSpec.cxx @@ -79,6 +79,7 @@ #include "DetectorsRaw/RDHUtils.h" #include "ITStracking/TrackingInterface.h" #include "GPUWorkflowInternal.h" +#include "GPUDataTypesQA.h" // #include "Framework/ThreadPool.h" #include @@ -180,7 +181,7 @@ 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); // TODO: Clean up using numeric flags! + mQATaskMask = (mSpecConfig.processMC ? gpudatatypes::gpuqa::tasksAllMC : gpudatatypes::gpuqa::tasksNone) | (mConfig->configQA.clusterRejectionHistograms ? gpudatatypes::gpuqa::taskClusterCounts : gpudatatypes::gpuqa::tasksNone); mConfig->configProcessing.runQA = -mQATaskMask; } } @@ -190,39 +191,39 @@ void GPURecoWorkflowSpec::init(InitContext& ic) // Configure the "GPU workflow" i.e. which steps we run on the GPU (or CPU) if (runTracking) { - mConfig->configWorkflow.steps.set(GPUDataTypes::RecoStep::TPCConversion, - GPUDataTypes::RecoStep::TPCSectorTracking, - GPUDataTypes::RecoStep::TPCMerging); - mConfig->configWorkflow.outputs.set(GPUDataTypes::InOutType::TPCMergedTracks); + mConfig->configWorkflow.steps.set(gpudatatypes::RecoStep::TPCConversion, + gpudatatypes::RecoStep::TPCSectorTracking, + gpudatatypes::RecoStep::TPCMerging); + mConfig->configWorkflow.outputs.set(gpudatatypes::InOutType::TPCMergedTracks); } 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; @@ -935,7 +936,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)"; } diff --git a/doc/data/2021-01-o2_prs.json b/doc/data/2021-01-o2_prs.json index 06cd97dd6d7b7..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" } }, { @@ -2445,7 +2445,7 @@ }, { "node": { - "path": "GPU/GPUTracking/DataTypes/GPUDataTypes.h" + "path": "GPU/GPUTracking/DataTypes/GPUDataTypesIO.h" } }, { From 670518f435ac5c22c532fbd5d4d696873d8d2db7 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sun, 14 Dec 2025 09:43:01 +0100 Subject: [PATCH 073/701] Upgrades ECal: Fix codechecker violation --- Detectors/Upgrades/ALICE3/ECal/DataFormatsECal/src/Cluster.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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); } From 303fbca420a30dbab975740f576c8673480943a4 Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Sun, 14 Dec 2025 22:11:27 +0100 Subject: [PATCH 074/701] add previously list init function call --- Steer/src/CollisionContextTool.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/Steer/src/CollisionContextTool.cxx b/Steer/src/CollisionContextTool.cxx index b884909aedd9d..6bee407c01264 100644 --- a/Steer/src/CollisionContextTool.cxx +++ b/Steer/src/CollisionContextTool.cxx @@ -496,6 +496,7 @@ int main(int argc, char* argv[]) // this loop makes sure that the first collision is within the range of orbits asked (if noEmptyTF is enabled) do { sampler->setFirstIR(o2::InteractionRecord(options.firstBC, orbitstart)); + sampler->init(); record = sampler->generateCollisionTime(); } while (options.noEmptyTF && usetimeframelength && record.orbit >= orbitstart + orbits_total); int count = 0; From 92f73dd45a43ac26c4fe1f577b75e992b32a84ad Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Mon, 15 Dec 2025 11:29:58 +0100 Subject: [PATCH 075/701] DPL GUI: Fix activity display (#14916) --- .../src/FrameworkGUIDataRelayerUsage.cxx | 60 ++++++++++--------- 1 file changed, 31 insertions(+), 29 deletions(-) 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)))); } } From 4090041b401c7aa6c919ca923126fff950cbccd1 Mon Sep 17 00:00:00 2001 From: iravasen Date: Mon, 15 Dec 2025 19:41:39 +0100 Subject: [PATCH 076/701] ITS calib: change option name to avoid ambiguity (#14920) --- Detectors/ITSMFT/ITS/workflow/src/ThresholdCalibratorSpec.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Detectors/ITSMFT/ITS/workflow/src/ThresholdCalibratorSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/ThresholdCalibratorSpec.cxx index 0a08841059d63..ce0b840f4a037 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/ThresholdCalibratorSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/ThresholdCalibratorSpec.cxx @@ -74,7 +74,7 @@ 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"); + isLocal = ic.options().get("local-processing"); std::string fittype = ic.options().get("fittype"); if (fittype == "derivative") { @@ -2069,7 +2069,7 @@ DataProcessorSpec getITSThresholdCalibratorSpec(const ITSCalibInpConf& inpConf) {"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"}}, - {"local", 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)"}}}}; + {"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 From 2eba0da0195570e9987ee2a65ff5193ebdffbacd Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Tue, 11 Nov 2025 10:05:19 +0100 Subject: [PATCH 077/701] Common: add host symbols to RangeRef Signed-off-by: Felix Schlepper --- .../include/CommonDataFormat/RangeReference.h | 38 +++++++++---------- 1 file changed, 19 insertions(+), 19 deletions(-) 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(); } From 33a68caa22edc089ec63ccb92a15890d5b3871b2 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Fri, 7 Nov 2025 10:37:22 +0100 Subject: [PATCH 078/701] Common: new linkdef for TimeStamp Signed-off-by: Felix Schlepper --- DataFormats/common/src/CommonDataFormatLinkDef.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) 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> + ; From 73948550572a8c1a1280290b15043b9aef17a50e Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Tue, 25 Nov 2025 14:52:37 +0100 Subject: [PATCH 079/701] Reco: Add cov setters for ind. elements --- .../ReconstructionDataFormats/Vertex.h | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) 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; From 0244a9838fd7dfff28a8fbff1fa0597af6a6234f Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Tue, 16 Dec 2025 11:37:27 +0100 Subject: [PATCH 080/701] ITSMFT&ITS3&TRK: load response functions from ccdb (#14902) * ITSMFT&ITS3: load response functions from ccdb Signed-off-by: Felix Schlepper * TRK: load response function from ccdb Signed-off-by: Felix Schlepper --------- Signed-off-by: Felix Schlepper --- .../data/AlpideResponseData/CMakeLists.txt | 25 -------- .../include/ITSMFTSimulation/Digitizer.h | 10 ++-- .../common/simulation/src/Digitizer.cxx | 30 ++-------- .../include/TRKSimulation/DigiParams.h | 11 ++-- .../include/TRKSimulation/Digitizer.h | 12 ++-- .../ALICE3/TRK/simulation/src/DigiParams.cxx | 13 +++- .../ALICE3/TRK/simulation/src/Digitizer.cxx | 10 +--- .../TRK/simulation/src/TRKSimulationLinkDef.h | 1 + Detectors/Upgrades/ITS3/CMakeLists.txt | 1 - Detectors/Upgrades/ITS3/data/CMakeLists.txt | 34 ----------- .../include/ITS3Simulation/DigiParams.h | 9 +-- .../include/ITS3Simulation/Digitizer.h | 20 +++---- .../ITS3/simulation/src/DigiParams.cxx | 10 ++-- .../ITS3/simulation/src/Digitizer.cxx | 59 +++++-------------- .../src/ITS3DigitizerSpec.cxx | 15 +++++ .../src/ITSMFTDigitizerSpec.cxx | 14 +++++ .../src/TRKDigitizerSpec.cxx | 6 ++ 17 files changed, 106 insertions(+), 174 deletions(-) delete mode 100644 Detectors/Upgrades/ITS3/data/CMakeLists.txt diff --git a/Detectors/ITSMFT/common/data/AlpideResponseData/CMakeLists.txt b/Detectors/ITSMFT/common/data/AlpideResponseData/CMakeLists.txt index d1f3e756394b1..f985857afa88c 100644 --- a/Detectors/ITSMFT/common/data/AlpideResponseData/CMakeLists.txt +++ b/Detectors/ITSMFT/common/data/AlpideResponseData/CMakeLists.txt @@ -9,33 +9,8 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. - o2_add_executable(alpide-response-generator SOURCES AlpideResponse.cxx PUBLIC_LINK_LIBRARIES O2::ITSMFTSimulation ROOT::Core TARGETVARNAME targetName) - -set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${CMAKE_SOURCE_DIR}/Detectors/ITSMFT/common/data/AlpideResponseData/AlpideResponse.cxx) - -if(ITSRESPONSE) - message(STATUS "ITSRESPONSE option provided, setting ITSRESPONSE_DIR from it: " ${ITSRESPONSE}) - set(ITSRESPONSE_DIR ${ITSRESPONSE} CACHE PATH "ITSResponse directory") -else() - message(STATUS "ITSRESPONSE option not provided, setting ITSRESPONSE_DIR from environment ITSRESPONSE_ROOT: " $ENV{ITSRESPONSE_ROOT}) - set(ITSRESPONSE_DIR $ENV{ITSRESPONSE_ROOT} CACHE PATH "ITSResponse directory") -endif() - -add_custom_command(TARGET O2exe-alpide-response-generator POST_BUILD - COMMAND ${CMAKE_BINARY_DIR}/stage/bin/o2-alpide-response-generator -i ${ITSRESPONSE_DIR}/response/AlpideResponseData/ -o ${CMAKE_CURRENT_BINARY_DIR}/ - BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/AlpideResponseData.root - COMMENT "Generating AlpideResponseData.root" -) - -# # Add a target that depends on the custom command output -add_custom_target( - GenerateAlpideResponse ALL - DEPENDS ${CMAKE_CURRENT_BINARY_DIR}/AlpideResponseData.root -) - -install(FILES "${CMAKE_CURRENT_BINARY_DIR}/AlpideResponseData.root" DESTINATION "${CMAKE_INSTALL_PREFIX}/share/Detectors/ITSMFT/data/AlpideResponseData/") diff --git a/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/Digitizer.h b/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/Digitizer.h index e3995068c52cf..670dd32bf9f46 100644 --- a/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/Digitizer.h +++ b/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/Digitizer.h @@ -62,6 +62,7 @@ class Digitizer : public TObject 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); @@ -124,11 +125,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/Digitizer.cxx b/Detectors/ITSMFT/common/simulation/src/Digitizer.cxx index e5dd35e6a084d..53e0a2fcb096f 100644 --- a/Detectors/ITSMFT/common/simulation/src/Digitizer.cxx +++ b/Detectors/ITSMFT/common/simulation/src/Digitizer.cxx @@ -48,24 +48,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,29 +55,29 @@ 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; + 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"; diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DigiParams.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DigiParams.h index 970b20c48816e..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; } @@ -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 8e7173af8b820..221d7b342bf59 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Digitizer.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Digitizer.h @@ -51,7 +51,7 @@ class Digitizer 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); @@ -66,7 +66,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 @@ -142,12 +141,9 @@ class Digitizer 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 - - // 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 + 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 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/src/DigiParams.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/DigiParams.cxx index df6f46ac0ecb0..ca4685d53de2a 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,11 @@ void DigiParams::print() const printf("Charge time-response:\n"); mSignalShape.print(); } + +void DigiParams::setAlpSimResponse(const o2::itsmft::AlpideSimResponse* resp) +{ + 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 20509782f21ee..3ee952801f0c3 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx @@ -48,15 +48,9 @@ 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"); + mChipSimResp = mParams.getAlpSimResponse(); mChipSimRespVD = mChipSimResp; /// for the moment considering the same response mChipSimRespMLOT = mChipSimResp; /// for the moment considering the same response @@ -92,7 +86,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; diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h index 9af868a2de44c..fec9cb6631a6f 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h @@ -24,6 +24,7 @@ #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/ITS3/CMakeLists.txt b/Detectors/Upgrades/ITS3/CMakeLists.txt index 5e40e59ad0068..bdaf1b4bf4292 100644 --- a/Detectors/Upgrades/ITS3/CMakeLists.txt +++ b/Detectors/Upgrades/ITS3/CMakeLists.txt @@ -12,7 +12,6 @@ #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) 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/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/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 7dd7110801f4a..4560a656c1762 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); 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..b40e377d58ca2 100644 --- a/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx @@ -222,6 +222,14 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer } return; } + if (matcher == ConcreteDataMatcher(mOrigin, "ALPIDERESPVbb0", 0)) { + LOG(info) << mID.getName() << " loaded AlpideResponseData for Vbb=0V"; + mDigitizer.setAlpideResponse((o2::itsmft::AlpideSimResponse*)obj, 0); + } + if (matcher == ConcreteDataMatcher(mOrigin, "ALPIDERESPVbbM3", 0)) { + LOG(info) << mID.getName() << " loaded AlpideResponseData for Vbb=-3V"; + mDigitizer.setAlpideResponse((o2::itsmft::AlpideSimResponse*)obj, 1); + } } protected: @@ -236,6 +244,8 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer // 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 + "_alpiderespvbb0"); + pc.inputs().get(detstr + "_alpiderespvbbm3"); auto& dopt = o2::itsmft::DPLDigitizerParam::Instance(); auto& aopt = o2::itsmft::DPLAlpideParam::Instance(); @@ -365,6 +375,8 @@ DataProcessorSpec getITSDigitizerSpec(int channel, bool mctruth) 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")); + 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{(detStr + "Digitizer").c_str(), inputs, makeOutChannels(detOrig, mctruth), @@ -384,6 +396,8 @@ DataProcessorSpec getMFTDigitizerSpec(int channel, bool mctruth) 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")); + 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")); parHelper << "Params as " << o2::itsmft::DPLDigitizerParam::getParamName().data() << ".=value;... with" << o2::itsmft::DPLDigitizerParam::Instance() << " or " << o2::itsmft::DPLAlpideParam::getParamName().data() << ".=value;... with" diff --git a/Steer/DigitizerWorkflow/src/TRKDigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/TRKDigitizerSpec.cxx index 37355cb5752c4..a3d4d1f245fc5 100644 --- a/Steer/DigitizerWorkflow/src/TRKDigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/TRKDigitizerSpec.cxx @@ -244,6 +244,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,6 +265,10 @@ class TRKDPLDigitizerTask : BaseDPLDigitizer // mDigitizer.setDeadChannelsMap((o2::itsmft::NoiseMap*)obj); // return; // } + if (matcher == ConcreteDataMatcher(mOrigin, "APTSRESP", 0)) { + LOG(info) << mID.getName() << " loaded APTSResponseData"; + mDigitizer.getParams().setAlpSimResponse((const o2::itsmft::AlpideSimResponse*)obj); + } } private: @@ -297,6 +302,7 @@ 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), From 1bcfeed348dd3bbc16345b622f2d03bc1626f0c0 Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Tue, 16 Dec 2025 12:40:18 +0100 Subject: [PATCH 081/701] Update CODEOWNERS --- CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From 528a5a59130c425ad933531cc65a54dc371e28ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Tue, 16 Dec 2025 16:50:47 +0100 Subject: [PATCH 082/701] Refine parameter getter methods with error handling (#14794) Updated getter functions to specify return types and added error logging for missing parameters. --- .../TOF/include/DataFormatsTOF/ParameterContainers.h | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/DataFormats/Detectors/TOF/include/DataFormatsTOF/ParameterContainers.h b/DataFormats/Detectors/TOF/include/DataFormatsTOF/ParameterContainers.h index e64bf8aa3e276..224906e43b8c6 100644 --- a/DataFormats/Detectors/TOF/include/DataFormatsTOF/ParameterContainers.h +++ b/DataFormats/Detectors/TOF/include/DataFormatsTOF/ParameterContainers.h @@ -210,7 +210,13 @@ class ParameterCollection : public TNamed } /// @brief getter for the parameters stored in the container matching to a pass - const auto& getPars(const std::string& pass) const { return mParameters.at(pass); } + const std::unordered_map& getPars(const std::string& pass) const + { + if (!hasKey(pass)) { + LOG(fatal) << "Parameters for pass " << pass << " not found!"; + } + return mParameters.at(pass); + } /// @brief printing function for the content of the pass /// @param pass pass to print @@ -221,7 +227,7 @@ class ParameterCollection : public TNamed /// @brief Getter of the full map of parameters stored in the container /// @return returns the full map of parameters - const auto& getFullMap() { return mParameters; } + const std::unordered_map>& getFullMap() const { return mParameters; } /// Loader from file /// \param FileName name of the input file From 70b10405ca2ffe3103bc01b26fde13b1a13ea6c9 Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Tue, 16 Dec 2025 22:02:47 +0100 Subject: [PATCH 083/701] DPL: modernize workflow construction code using ranges (#14907) --- .../Framework/AnalysisSupportHelpers.h | 5 - .../Core/include/Framework/DataSpecViews.h | 43 +++++- Framework/Core/src/AnalysisSupportHelpers.cxx | 127 +++++------------- Framework/Core/src/ArrowSupport.cxx | 60 +++------ Framework/Core/src/WorkflowHelpers.cxx | 64 +++------ 5 files changed, 114 insertions(+), 185 deletions(-) diff --git a/Framework/Core/include/Framework/AnalysisSupportHelpers.h b/Framework/Core/include/Framework/AnalysisSupportHelpers.h index cc4d45a46c8bc..c0eeb3bd9697d 100644 --- a/Framework/Core/include/Framework/AnalysisSupportHelpers.h +++ b/Framework/Core/include/Framework/AnalysisSupportHelpers.h @@ -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/DataSpecViews.h b/Framework/Core/include/Framework/DataSpecViews.h index 0782cefd0f632..162a12419594e 100644 --- a/Framework/Core/include/Framework/DataSpecViews.h +++ b/Framework/Core/include/Framework/DataSpecViews.h @@ -31,6 +31,32 @@ 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 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 +80,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 +88,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/src/AnalysisSupportHelpers.cxx b/Framework/Core/src/AnalysisSupportHelpers.cxx index b5c898faa515a..e59f36c72bdab 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" @@ -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& 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,52 +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 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 } // ============================================================================= diff --git a/Framework/Core/src/ArrowSupport.cxx b/Framework/Core/src/ArrowSupport.cxx index cf2d364027932..26594252e888b 100644 --- a/Framework/Core/src/ArrowSupport.cxx +++ b/Framework/Core/src/ArrowSupport.cxx @@ -595,23 +595,16 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() ac.providedTIMs.clear(); ac.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)); - } - } + for (auto& d : workflow | views::exclude_by_name(builder->name)) { + d.inputs | + views::partial_match_filter(header::DataOrigin{"IDX"}) | + sinks::update_input_list{ac.requestedIDXs}; } // recreate inputs and outputs builder->inputs.clear(); @@ -624,37 +617,27 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() 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); - } - } + for (auto& d : workflow | views::exclude_by_name(spawner->name)) { + d.inputs | + views::partial_match_filter(header::DataOrigin{"DYN"}) | + sinks::update_input_list{ac.requestedDYNs}; + d.outputs | + views::partial_match_filter(header::DataOrigin{"DYN"}) | + sinks::append_to{ac.providedDYNs}; } 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); - } - } + ac.requestedDYNs | + views::filter_not_matching(ac.providedDYNs) | + sinks::append_to{ac.spawnerInputs}; // recreate inputs and outputs spawner->outputs.clear(); spawner->inputs.clear(); + AnalysisSupportHelpers::addMissingOutputsToSpawner({}, ac.spawnerInputs, ac.requestedAODs, *spawner); // replace AlgorithmSpec // FIXME: it should be made more generic, so it does not need replacement... spawner->algorithm = PluginManager::loadAlgorithmFromPlugin("O2FrameworkOnDemandTablesSupport", "ExtendedTableSpawner", ctx); - AnalysisSupportHelpers::addMissingOutputsToSpawner({}, ac.spawnerInputs, ac.requestedAODs, *spawner); } if (analysisCCDB != workflow.end()) { @@ -675,7 +658,7 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() // FIXME: it should be made more generic, so it does not need replacement... // 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(ac.analysisCCDBInputs, ac.requestedAODs, ac.requestedDYNs, *analysisCCDB); } if (writer != workflow.end()) { @@ -686,12 +669,9 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() // 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{ac.requestedAODs}; } // remove unmatched outputs @@ -705,8 +685,6 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() } } - - // replace writer as some outputs may have become dangling and some are now consumed auto [outputsInputs, isDangling] = WorkflowHelpers::analyzeOutputs(workflow); diff --git a/Framework/Core/src/WorkflowHelpers.cxx b/Framework/Core/src/WorkflowHelpers.cxx index 17f6c9eb7ddb6..02141678fec7c 100644 --- a/Framework/Core/src/WorkflowHelpers.cxx +++ b/Framework/Core/src/WorkflowHelpers.cxx @@ -19,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" @@ -157,18 +156,6 @@ 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); - }; - }}; - DataProcessorSpec ccdbBackend{ .name = "internal-dpl-ccdb-backend", .outputs = {}, @@ -281,20 +268,9 @@ 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::any_of(processor.inputs.begin(), processor.inputs.end(), [](auto const& input) { return input.lifetime == Lifetime::Timeframe; }); + bool hasTimeframeOutputs = std::any_of(processor.outputs.begin(), processor.outputs.end(), [](auto const& output) { return output.lifetime == Lifetime::Timeframe; }); + // A timeframeSink consumes timeframes without creating new // timeframe data. bool timeframeSink = hasTimeframeInputs && !hasTimeframeOutputs; @@ -304,14 +280,13 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext 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::find_if(processor.outputs.begin(), processor.outputs.end(), [&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)}}); @@ -339,18 +314,12 @@ 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::none_of(processor.options.begin(), processor.options.end(), [](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); @@ -422,14 +391,10 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext ac.requestedTIMs | views::filter_not_matching(ac.providedTIMs) | sinks::append_to{ac.analysisCCDBInputs}; DeploymentMode deploymentMode = DefaultsHelpers::deploymentMode(); if (deploymentMode != DeploymentMode::OnlineDDS && deploymentMode != DeploymentMode::OnlineECS) { - AnalysisSupportHelpers::addMissingOutputsToAnalysisCCDBFetcher({}, ac.analysisCCDBInputs, ac.requestedAODs, ac.requestedTIMs, analysisCCDBBackend); + AnalysisSupportHelpers::addMissingOutputsToBuilder(ac.analysisCCDBInputs, ac.requestedAODs, ac.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); - } - } + ac.requestedDYNs | views::filter_not_matching(ac.providedDYNs) | sinks::append_to{ac.spawnerInputs}; DataProcessorSpec aodSpawner{ "internal-dpl-aod-spawner", @@ -440,6 +405,9 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext AnalysisSupportHelpers::addMissingOutputsToSpawner({}, ac.spawnerInputs, ac.requestedAODs, aodSpawner); AnalysisSupportHelpers::addMissingOutputsToReader(ac.providedAODs, ac.requestedAODs, aodReader); + + std::sort(requestedCCDBs.begin(), requestedCCDBs.end(), inputSpecLessThan); + std::sort(providedCCDBs.begin(), providedCCDBs.end(), outputSpecLessThan); AnalysisSupportHelpers::addMissingOutputsToReader(providedCCDBs, requestedCCDBs, ccdbBackend); std::vector extraSpecs; From a8feeedb8e05bc06b6fd4186199a2cfc7fe89204 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 17 Dec 2025 09:13:23 +0100 Subject: [PATCH 084/701] Avoid early initialization of Cling Requires v6-36-04-alice7 in order to compile / work. --- .../TPC/base/src/TPCFlagsMemberCustomStreamer.cxx | 12 +++--------- 1 file changed, 3 insertions(+), 9 deletions(-) diff --git a/Detectors/TPC/base/src/TPCFlagsMemberCustomStreamer.cxx b/Detectors/TPC/base/src/TPCFlagsMemberCustomStreamer.cxx index dca7ba35004c9..27ebfeb3c64bb 100644 --- a/Detectors/TPC/base/src/TPCFlagsMemberCustomStreamer.cxx +++ b/Detectors/TPC/base/src/TPCFlagsMemberCustomStreamer.cxx @@ -71,16 +71,10 @@ namespace ROOT { static __attribute__((used)) int _R__dummyStreamer_3 = ([]() { - auto cl = TClass::GetClass>(); - if (cl) { - if (!getenv("TPC_PADFLAGS_STREAMER_OFF")) { - cl->AdoptMemberStreamer("mData", new TMemberStreamer(MemberVectorPadFlagsStreamer)); - } - } else { - // we should never come here ... and if we do we should assert/fail - assert(false); + if (!getenv("TPC_PADFLAGS_STREAMER_OFF")) { + ROOT::GenerateInitInstance((o2::tpc::CalArray *)nullptr)->AdoptMemberStreamer("mData", new TMemberStreamer(MemberVectorPadFlagsStreamer)); } return 0; })(); } // namespace ROOT -#endif \ No newline at end of file +#endif From 4804b1c165d38b5163376d94f19bc35d88681897 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Thu, 20 Nov 2025 15:16:43 +0100 Subject: [PATCH 085/701] Common: EnumFlags add set --- Common/Utils/include/CommonUtils/EnumFlags.h | 148 +++++--- Common/Utils/test/testEnumFlags.cxx | 345 +++++++++++++++++++ 2 files changed, 449 insertions(+), 44 deletions(-) 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"); + } + } +} From 73c5a52720543e6405c8e6dd9e1ffa70c645d508 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Mon, 22 Dec 2025 21:06:16 +0100 Subject: [PATCH 086/701] DCA Fitter GPU: Disable failing test, which was not active before and seems broken --- Common/DCAFitter/GPU/cuda/CMakeLists.txt | 22 +++++++++++----------- Common/DCAFitter/GPU/hip/CMakeLists.txt | 24 ++++++++++++------------ GPU/Common/CMakeLists.txt | 20 ++++++++++---------- 3 files changed, 33 insertions(+), 33 deletions(-) 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/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) From bfcff02b88be6053c154e385acefd235dfed69d4 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 24 Dec 2025 23:16:32 +0100 Subject: [PATCH 087/701] Fix some codechecker violations (#14936) --- .../CPV/reconstruction/include/CPVReconstruction/CTFCoder.h | 2 +- .../CTP/reconstruction/include/CTPReconstruction/CTFCoder.h | 2 +- .../reconstruction/include/EMCALReconstruction/CTFCoder.h | 2 +- .../FDD/reconstruction/include/FDDReconstruction/CTFCoder.h | 2 +- .../FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h | 2 +- .../FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h | 2 +- Detectors/GlobalTrackingWorkflow/study/src/DumpTracks.cxx | 2 +- Detectors/GlobalTrackingWorkflow/study/src/SVStudy.cxx | 2 +- Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx | 2 +- Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx | 2 +- Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx | 4 ++-- .../reconstruction/include/HMPIDReconstruction/CTFCoder.h | 2 +- .../ITS/QC/TestDataReaderWorkflow/src/TestDataReader.cxx | 1 - .../reconstruction/include/ITSMFTReconstruction/CTFCoder.h | 2 +- Detectors/MUON/MCH/CTF/include/MCHCTF/CTFCoder.h | 2 +- Detectors/MUON/MID/CTF/include/MIDCTF/CTFCoder.h | 2 +- .../PHOS/reconstruction/include/PHOSReconstruction/CTFCoder.h | 2 +- .../TOF/reconstruction/include/TOFReconstruction/CTFCoder.h | 2 +- .../TPC/reconstruction/include/TPCReconstruction/CTFCoder.h | 2 +- .../TRD/reconstruction/include/TRDReconstruction/CTFCoder.h | 2 +- .../ZDC/reconstruction/include/ZDCReconstruction/CTFCoder.h | 2 +- Framework/Utils/include/DPLUtils/RootTreeWriter.h | 2 +- 22 files changed, 22 insertions(+), 23 deletions(-) diff --git a/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h b/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h index e9bd0f7249ef1..ab5082b5c748c 100644 --- a/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h +++ b/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h @@ -32,7 +32,7 @@ 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) {} diff --git a/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h b/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h index 9189df5d12685..87657f6a6f8c6 100644 --- a/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h +++ b/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h @@ -34,7 +34,7 @@ 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) {} diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h index 1617a9f1a7d54..23deb75ffb049 100644 --- a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h @@ -32,7 +32,7 @@ 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) {} diff --git a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h index dc11174908c75..94a0c6f64659d 100644 --- a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h +++ b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h @@ -33,7 +33,7 @@ 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) {} diff --git a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h index 4d749dbc90b42..65646c161dde5 100644 --- a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h +++ b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h @@ -34,7 +34,7 @@ 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) {} diff --git a/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h index cbec444ef11be..4398e19c0a5ed 100644 --- a/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h +++ b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h @@ -30,7 +30,7 @@ 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) {} 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/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 c4a0a30116557..8f6604b029605 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx @@ -83,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) diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx index 531ee03290201..b8a8f97737b4d 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx @@ -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/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h index da2461c2759ba..39242355a3de9 100644 --- a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h +++ b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h @@ -32,7 +32,7 @@ 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) {} 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/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h index 2d4aabc94fc82..94c14424f6ce3 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h @@ -39,7 +39,7 @@ namespace o2 namespace itsmft { -class CTFCoder : public o2::ctf::CTFCoderBase +class CTFCoder final : public o2::ctf::CTFCoderBase { public: using PMatrix = std::array, ClusterPattern::MaxColSpan + 2>; diff --git a/Detectors/MUON/MCH/CTF/include/MCHCTF/CTFCoder.h b/Detectors/MUON/MCH/CTF/include/MCHCTF/CTFCoder.h index fc090c5c7e16d..2d65cbbaea614 100644 --- a/Detectors/MUON/MCH/CTF/include/MCHCTF/CTFCoder.h +++ b/Detectors/MUON/MCH/CTF/include/MCHCTF/CTFCoder.h @@ -34,7 +34,7 @@ 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) {} diff --git a/Detectors/MUON/MID/CTF/include/MIDCTF/CTFCoder.h b/Detectors/MUON/MID/CTF/include/MIDCTF/CTFCoder.h index 3071b65db47b1..5afc42550ae3e 100644 --- a/Detectors/MUON/MID/CTF/include/MIDCTF/CTFCoder.h +++ b/Detectors/MUON/MID/CTF/include/MIDCTF/CTFCoder.h @@ -34,7 +34,7 @@ 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) {} diff --git a/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFCoder.h b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFCoder.h index 96ee5093bacca..8a7172f634a33 100644 --- a/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFCoder.h +++ b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFCoder.h @@ -32,7 +32,7 @@ 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) {} diff --git a/Detectors/TOF/reconstruction/include/TOFReconstruction/CTFCoder.h b/Detectors/TOF/reconstruction/include/TOFReconstruction/CTFCoder.h index e559dcce7a1da..e7a203cfcb25e 100644 --- a/Detectors/TOF/reconstruction/include/TOFReconstruction/CTFCoder.h +++ b/Detectors/TOF/reconstruction/include/TOFReconstruction/CTFCoder.h @@ -31,7 +31,7 @@ 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) {} diff --git a/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h b/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h index ab49d0d49d79b..12d66ef6a6e7c 100644 --- a/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h +++ b/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h @@ -119,7 +119,7 @@ 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) {} diff --git a/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFCoder.h b/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFCoder.h index 27e089fcf3555..9eeaf19db5025 100644 --- a/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFCoder.h +++ b/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFCoder.h @@ -33,7 +33,7 @@ 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) {} diff --git a/Detectors/ZDC/reconstruction/include/ZDCReconstruction/CTFCoder.h b/Detectors/ZDC/reconstruction/include/ZDCReconstruction/CTFCoder.h index c2f2163600f29..f8823e4fc66a5 100644 --- a/Detectors/ZDC/reconstruction/include/ZDCReconstruction/CTFCoder.h +++ b/Detectors/ZDC/reconstruction/include/ZDCReconstruction/CTFCoder.h @@ -32,7 +32,7 @@ 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) {} 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 From 89cebea4fdb75bb6bf85a9f5d33cb021a3093cb4 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 25 Dec 2025 18:44:28 +0100 Subject: [PATCH 088/701] GPU: Support virtual and real architectures in RTC --- GPU/GPUTracking/Base/cuda/CMakeLists.txt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/GPU/GPUTracking/Base/cuda/CMakeLists.txt b/GPU/GPUTracking/Base/cuda/CMakeLists.txt index 7f595b28a582a..e7a579bec794d 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") From 0bc34efd29e2b0cc4dade68f538da91046d4ae48 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 25 Dec 2025 16:02:08 +0100 Subject: [PATCH 089/701] GPU: Change default CUDA architectures to 80-real 86-real 89-real 120-real 75-virtual --- dependencies/FindO2GPU.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dependencies/FindO2GPU.cmake b/dependencies/FindO2GPU.cmake index 0be3448ed6fce..6ca311905e01c 100644 --- a/dependencies/FindO2GPU.cmake +++ b/dependencies/FindO2GPU.cmake @@ -10,7 +10,7 @@ # or submit itself to any jurisdiction. # NOTE!!!! - Whenever this file is changed, move it over to alidist/resources -# FindO2GPU.cmake Version 7 +# FindO2GPU.cmake Version 8 if(NOT DEFINED ENABLE_CUDA) set(ENABLE_CUDA "AUTO") @@ -32,7 +32,7 @@ if(CMAKE_BUILD_TYPE_UPPER STREQUAL "DEBUG") endif() if(CUDA_COMPUTETARGET AND CUDA_COMPUTETARGET STREQUAL "default") - set(CUDA_COMPUTETARGET 86 89) + set(CUDA_COMPUTETARGET 80-real 86-real 89-real 120-real 75-virtual) endif() if(HIP_AMDGPUTARGET AND HIP_AMDGPUTARGET STREQUAL "default") From cf919bf9a720c0b4135de03feaec600c84111c95 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Mon, 29 Dec 2025 22:27:33 +0100 Subject: [PATCH 090/701] GPU Workflow: Fix setting of dEdx processing step --- GPU/GPUTracking/Global/GPUChainTracking.cxx | 2 +- GPU/Workflow/src/GPUWorkflowSpec.cxx | 3 ++- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/GPU/GPUTracking/Global/GPUChainTracking.cxx b/GPU/GPUTracking/Global/GPUChainTracking.cxx index f370b756e2cdb..5c951053e155b 100644 --- a/GPU/GPUTracking/Global/GPUChainTracking.cxx +++ b/GPU/GPUTracking/Global/GPUChainTracking.cxx @@ -1017,6 +1017,6 @@ void GPUChainTracking::ApplySyncSettings(GPUSettingsProcessing& proc, GPUSetting proc.rtc.optSpecialCode = syncMode; } if (dEdxMode != -2) { - steps.setBits(gpudatatypes::RecoStep::TPCdEdx, dEdxMode == -1 ? !syncMode : dEdxMode > 0); + steps.setBits(gpudatatypes::RecoStep::TPCdEdx, dEdxMode == -1 ? !syncMode : (dEdxMode > 0)); } } diff --git a/GPU/Workflow/src/GPUWorkflowSpec.cxx b/GPU/Workflow/src/GPUWorkflowSpec.cxx index 6011cc3dc3e9f..2d5a955a5e911 100644 --- a/GPU/Workflow/src/GPUWorkflowSpec.cxx +++ b/GPU/Workflow/src/GPUWorkflowSpec.cxx @@ -187,7 +187,6 @@ void GPURecoWorkflowSpec::init(InitContext& ic) } mConfig->configInterface.outputToExternalBuffers = true; const bool runTracking = mSpecConfig.outputTracks || mSpecConfig.outputCompClustersRoot || mSpecConfig.outputCompClustersFlat; - GPUO2Interface::ApplySyncSettings(mConfig->configProcessing, mConfig->configReconstruction, mConfig->configWorkflow.steps, mConfParam->synchronousProcessing, runTracking ? mConfParam->rundEdx : -2); // Configure the "GPU workflow" i.e. which steps we run on the GPU (or CPU) if (runTracking) { @@ -196,6 +195,8 @@ void GPURecoWorkflowSpec::init(InitContext& ic) 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); From 02fde5c462bc123a8d882f9ec1e0796c2a40e540 Mon Sep 17 00:00:00 2001 From: Diego Stocco Date: Mon, 24 Nov 2025 11:34:09 +0100 Subject: [PATCH 091/701] Increase time range search to include QCDB objects created after EOR --- Detectors/MUON/MID/Calibration/macros/build_rejectlist.C | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) 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(); From b8c867dbb27d08e9ecf3134aeb72886fbb3c878c Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Thu, 25 Sep 2025 15:57:50 +0200 Subject: [PATCH 092/701] First implementation of loopers inclusion in base Generator class --- .../SimConfig/include/SimConfig/SimConfig.h | 3 + Common/SimConfig/src/SimConfig.cxx | 2 + Generators/CMakeLists.txt | 13 + Generators/include/Generators/Generator.h | 13 + .../include/Generators/TPCLoopersParam.h | 48 ++ Generators/include/TPCLoopers.h | 127 +++++ .../share/egconfig/ScalerComptonParams.json | 28 ++ .../share/egconfig/ScalerPairParams.json | 34 ++ Generators/share/egconfig/gaussian_params.csv | 4 + Generators/share/egconfig/poisson_params.csv | 3 + Generators/src/Generator.cxx | 442 ++++++++++++------ Generators/src/GeneratorsLinkDef.h | 4 + Generators/src/TPCLoopers.cxx | 417 +++++++++++++++++ Generators/src/TPCLoopersParam.cxx | 15 + 14 files changed, 1007 insertions(+), 146 deletions(-) create mode 100644 Generators/include/Generators/TPCLoopersParam.h create mode 100644 Generators/include/TPCLoopers.h create mode 100644 Generators/share/egconfig/ScalerComptonParams.json create mode 100644 Generators/share/egconfig/ScalerPairParams.json create mode 100644 Generators/share/egconfig/gaussian_params.csv create mode 100644 Generators/share/egconfig/poisson_params.csv create mode 100644 Generators/src/TPCLoopers.cxx create mode 100644 Generators/src/TPCLoopersParam.cxx diff --git a/Common/SimConfig/include/SimConfig/SimConfig.h b/Common/SimConfig/include/SimConfig/SimConfig.h index be88d9fbd8c33..8642a0e5bc225 100644 --- a/Common/SimConfig/include/SimConfig/SimConfig.h +++ b/Common/SimConfig/include/SimConfig/SimConfig.h @@ -52,6 +52,7 @@ struct SimConfigData { std::vector mActiveModules; // list of active modules std::vector mReadoutDetectors; // list of readout detectors std::string mMCEngine; // chosen VMC engine + bool mNoLoopers = false; // Disable automatic TPC loopers std::string mGenerator; // chosen VMC generator std::string mTrigger; // chosen VMC generator trigger unsigned int mNEvents; // number of events to be simulated @@ -138,6 +139,8 @@ class SimConfig // get selected active detectors std::vector const& getActiveModules() const { return mConfigData.mActiveModules; } std::vector const& getReadoutDetectors() const { return mConfigData.mReadoutDetectors; } + // get loopers veto + bool getLoopersVeto() const { return mConfigData.mNoLoopers; } // static helper functions to determine list of active / readout modules // can also be used from outside diff --git a/Common/SimConfig/src/SimConfig.cxx b/Common/SimConfig/src/SimConfig.cxx index 15879687872d5..5ddc3199e3d4a 100644 --- a/Common/SimConfig/src/SimConfig.cxx +++ b/Common/SimConfig/src/SimConfig.cxx @@ -74,6 +74,7 @@ void SimConfig::initOptions(boost::program_options::options_description& options "run", bpo::value()->default_value(-1), "ALICE run number")( "asservice", bpo::value()->default_value(false), "run in service/server mode")( "noGeant", bpo::bool_switch(), "prohibits any Geant transport/physics (by using tight cuts)")( + "noLoopers", bpo::bool_switch(), "disable automatic TPC loopers")( "forwardKine", bpo::bool_switch(), "forward kinematics on a FairMQ channel")( "noDiscOutput", bpo::bool_switch(), "switch off writing sim results to disc (useful in combination with forwardKine)"); options.add_options()("fromCollContext", bpo::value()->default_value(""), "Use a pregenerated collision context to infer number of events to simulate, how to embedd them, the vertex position etc. Takes precedence of other options such as \"--nEvents\". The format is COLLISIONCONTEXTFILE.root[:SIGNALNAME] where SIGNALNAME is the event part in the context which is relevant."); @@ -297,6 +298,7 @@ bool SimConfig::resetFromParsedMap(boost::program_options::variables_map const& using o2::detectors::DetID; mConfigData.mMCEngine = vm["mcEngine"].as(); mConfigData.mNoGeant = vm["noGeant"].as(); + mConfigData.mNoLoopers = vm["noLoopers"].as(); // Reset modules and detectors as they are anyway re-parsed mConfigData.mReadoutDetectors.clear(); diff --git a/Generators/CMakeLists.txt b/Generators/CMakeLists.txt index 02caa63df0d43..56fe8b8fc2284 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,10 @@ if(HepMC3_FOUND) target_compile_definitions(${targetName} PUBLIC GENERATORS_WITH_HEPMC3) endif() +if(onnxruntime_FOUND) + target_compile_definitions(${targetName} PUBLIC GENERATORS_WITH_ONNXRUNTIME) +endif() + set(headers include/Generators/Generator.h include/Generators/Trigger.h @@ -88,6 +95,12 @@ set(headers include/Generators/FlowMapper.h ) +if(onnxruntime_FOUND) + list(APPEND headers + include/Generators/TPCLoopers.h + include/Generators/TPCLoopersParam.h) +endif() + if(pythia_FOUND) list(APPEND headers include/Generators/GeneratorPythia8.h diff --git a/Generators/include/Generators/Generator.h b/Generators/include/Generators/Generator.h index bd35a00793e2d..374d53f324399 100644 --- a/Generators/include/Generators/Generator.h +++ b/Generators/include/Generators/Generator.h @@ -17,6 +17,10 @@ #include "FairGenerator.h" #include "TParticle.h" #include "Generators/Trigger.h" +#ifdef GENERATORS_WITH_ONNXRUNTIME +#include "Generators/TPCLoopers.h" +#include "Generators/TPCLoopersParam.h" +#endif #include #include #include @@ -73,6 +77,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 loopers(); // adds loopers to the event in case TPC is used virtual void updateHeader(o2::dataformats::MCEventHeader* eventHeader) {}; Bool_t triggerEvent(); @@ -154,6 +159,8 @@ class Generator : public FairGenerator private: void updateSubGeneratorInformation(o2::dataformats::MCEventHeader* header) const; + // loopers flag + Bool_t mAddLoopers = kFALSE; // 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 +169,12 @@ class Generator : public FairGenerator // global static information about (upper limit of) number of events to be generated static unsigned int gTotalNEvents; +#ifdef GENERATORS_WITH_ONNXRUNTIME + // Loopers generator instance + std::unique_ptr mLoopersGen = nullptr; +#endif + void initLoopersGen(); + ClassDefOverride(Generator, 2); }; /** class Generator **/ diff --git a/Generators/include/Generators/TPCLoopersParam.h b/Generators/include/Generators/TPCLoopersParam.h new file mode 100644 index 0000000000000..ceeea201538b2 --- /dev/null +++ b/Generators/include/Generators/TPCLoopersParam.h @@ -0,0 +1,48 @@ +// 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 { + 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/egconfig/poisson_params.csv"; // file with Poissonian parameters + std::string gauss = "${O2_ROOT}/share/Generators/egconfig/gaussian_params.csv"; // file with Gaussian parameters + std::string scaler_pair = "${O2_ROOT}/share/Generators/egconfig/ScalerPairParams.json"; // file with scaler parameters for e+e- pair production + std::string scaler_compton = "${O2_ROOT}/share/Generators/egconfig/ScalerComptonParams.json"; // file with scaler parameters for Compton scattering + bool flat_gas = true; // if true, the gas density is considered flat in the TPC volume + int nFlatGasLoopers = 500; // number of loopers to be generated per event in case of flat gas + float fraction_pairs = 0.08; // fraction of loopers + std::array multiplier = {1., 1.}; // multiplier for pairs and compton loopers for Poissonian and Gaussian sampling + std::array fixedNLoopers = {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 + O2ParamDef(GenTPCLoopersParam, "GenTPCLoopers"); +}; + +} // end namespace eventgen +} // end namespace o2 + +#endif // ALICEO2_EVENTGEN_TPCLOOPERSPARAM_H_ diff --git a/Generators/include/TPCLoopers.h b/Generators/include/TPCLoopers.h new file mode 100644 index 0000000000000..70146a82baf60 --- /dev/null +++ b/Generators/include/TPCLoopers.h @@ -0,0 +1,127 @@ +#ifndef ALICEO2_EVENTGEN_TPCLOOPERS_H_ +#define ALICEO2_EVENTGEN_TPCLOOPERS_H_ + +#ifdef GENERATORS_WITH_ONNXRUNTIME +#include +#endif +#include +#include +#include +#include +#include "CCDB/CCDBTimeStampUtils.h" +#include "CCDB/CcdbApi.h" +#include "DetectorsRaw/HBFUtils.h" +#include "TRandom3.h" +#include "TDatabasePDG.h" +#include +#include +#include "SimulationDataFormat/MCGenProperties.h" +#include "TParticle.h" +#include + +#ifdef GENERATORS_WITH_ONNXRUNTIME +// Static Ort::Env instance for multiple onnx model loading +extern Ort::Env global_env; +#endif + +#ifdef GENERATORS_WITH_ONNXRUNTIME +// This class is responsible for loading the scaler parameters from a JSON file +// and applying the inverse transformation to the generated data. +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_ONNXRUNTIME + +namespace o2 +{ +namespace eventgen +{ + +#ifdef GENERATORS_WITH_ONNXRUNTIME +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(std::array &mult); + + void setFlatGas(Bool_t &flat, const Int_t &number = -1); + + void setFractionPairs(float &fractionPairs); + + 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; + // Masses of the electrons and positrons + TDatabasePDG *mPDG = TDatabasePDG::Instance(); + double mMass_e = mPDG->GetParticle(11)->Mass(); + double mMass_p = mPDG->GetParticle(-11)->Mass(); + 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 + 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 +}; +#endif // GENERATORS_WITH_ONNXRUNTIME + +} // namespace eventgen +} // namespace o2 + +#endif // ALICEO2_EVENTGEN_TPCLOOPERS_H_ \ No newline at end of file diff --git a/Generators/share/egconfig/ScalerComptonParams.json b/Generators/share/egconfig/ScalerComptonParams.json new file mode 100644 index 0000000000000..d8e654847f46e --- /dev/null +++ b/Generators/share/egconfig/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/egconfig/ScalerPairParams.json b/Generators/share/egconfig/ScalerPairParams.json new file mode 100644 index 0000000000000..61434bfa2462e --- /dev/null +++ b/Generators/share/egconfig/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/egconfig/gaussian_params.csv b/Generators/share/egconfig/gaussian_params.csv new file mode 100644 index 0000000000000..8e07c22dd30bf --- /dev/null +++ b/Generators/share/egconfig/gaussian_params.csv @@ -0,0 +1,4 @@ +9.611554230339172022e+01 +1.963570744941765867e+01 +4.300000000000000000e+01 +1.690000000000000000e+02 diff --git a/Generators/share/egconfig/poisson_params.csv b/Generators/share/egconfig/poisson_params.csv new file mode 100644 index 0000000000000..ef26bd973d34c --- /dev/null +++ b/Generators/share/egconfig/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..153ef5cd5e35e 100644 --- a/Generators/src/Generator.cxx +++ b/Generators/src/Generator.cxx @@ -17,11 +17,14 @@ #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" namespace o2 { @@ -39,6 +42,18 @@ Generator::Generator() : FairGenerator("ALICEo2", "ALICEo2 Generator"), /** default constructor **/ mThisInstanceID = Generator::InstanceCounter; Generator::InstanceCounter++; + auto simConfig = o2::conf::SimConfig::Instance(); + auto noLoops = simConfig.getLoopersVeto(); + if (!noLoops) { + bool transport = (simConfig.getMCEngine() != "O2TrivialMCEngine"); + if (transport) { + bool tpcActive = (std::find(simConfig.getReadoutDetectors().begin(), simConfig.getReadoutDetectors().end(), "TPC") != simConfig.getReadoutDetectors().end()); + if (tpcActive) { + mAddLoopers = kTRUE; + initLoopersGen(); + } + } + } } /*****************************************************************/ @@ -49,6 +64,102 @@ Generator::Generator(const Char_t* name, const Char_t* title) : FairGenerator(na /** constructor **/ mThisInstanceID = Generator::InstanceCounter; Generator::InstanceCounter++; + auto simConfig = o2::conf::SimConfig::Instance(); + auto noLoops = simConfig.getLoopersVeto(); + if (!noLoops) { + bool transport = (simConfig.getMCEngine() != "O2TrivialMCEngine"); + if (transport) { + bool tpcActive = (std::find(simConfig.getReadoutDetectors().begin(), simConfig.getReadoutDetectors().end(), "TPC") != simConfig.getReadoutDetectors().end()); + if (tpcActive) { + mAddLoopers = kTRUE; + initLoopersGen(); + } + } + } +} + +/*****************************************************************/ + +void Generator::initLoopersGen() +{ +#ifdef GENERATORS_WITH_ONNXRUNTIME + // 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()); + 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()); + auto flat_gas = loopersParam.flat_gas; + const auto& nFlatGasLoopers = loopersParam.nFlatGasLoopers; + auto fraction_pairs = loopersParam.fraction_pairs; + auto multiplier = loopersParam.multiplier; + auto fixedNLoopers = loopersParam.fixedNLoopers; + const std::array models = {model_pairs, model_compton}; + const std::array local_names = {"WGANpair.onnx", "WGANcompton.onnx"}; + const std::array isAlien = {models[0].starts_with("alien://"), models[1].starts_with("alien://")}; + const std::array isCCDB = {models[0].starts_with("ccdb://"), models[1].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; })) { + o2::ccdb::CcdbApi ccdb_api; + ccdb_api.init("http://alice-ccdb.cern.ch"); + 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; + try { + // Create the TPC loopers generator with the provided parameters + mLoopersGen = std::make_unique(model_pairs, model_compton, poisson, gauss, scaler_pair, scaler_compton); + + // Configure the generator with flat gas loopers if enabled (default) + if (flat_gas) { + mLoopersGen->setFlatGas(flat_gas, nFlatGasLoopers); + mLoopersGen->setFractionPairs(fraction_pairs); + } else { + // Otherwise, Poisson+Gauss sampling or fixed number of loopers 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 + mLoopersGen->SetNLoopers(fixedNLoopers[0], fixedNLoopers[1]); + mLoopersGen->SetMultiplier(multiplier); + } + LOG(info) << "TPC Loopers generator initialized successfully"; + } catch (const std::exception& e) { + LOG(error) << "Failed to initialize TPC Loopers generator: " << e.what(); + mLoopersGen.reset(); + } +#else + LOG(warn) << "ONNX Runtime support not available, cannot initialize TPC loopers generator"; +#endif } /*****************************************************************/ @@ -65,191 +176,230 @@ Bool_t /*****************************************************************/ Bool_t - Generator::ReadEvent(FairPrimaryGenerator* primGen) + Generator::loopers() { - /** read event **/ - - /** endless generate-and-trigger loop **/ - while (true) { - mReadEventCounter++; - - /** clear particle vector **/ - mParticles.clear(); - - /** reset the sub-generator ID **/ - mSubGeneratorId = -1; - - /** generate event **/ - if (!generateEvent()) { - LOG(error) << "ReadEvent failed in generateEvent"; - return kFALSE; - } - - /** import particles **/ - if (!importParticles()) { - LOG(error) << "ReadEvent failed in importParticles"; - return kFALSE; - } - - if (mSubGeneratorsIdToDesc.empty() && mSubGeneratorId > -1) { - LOG(fatal) << "ReadEvent failed because no SubGenerator description given"; - } - - if (!mSubGeneratorsIdToDesc.empty() && mSubGeneratorId < 0) { - LOG(fatal) << "ReadEvent failed because SubGenerator description given but sub-generator not set"; - } - - /** trigger event **/ - if (triggerEvent()) { - mTriggerOkHook(mParticles, mReadEventCounter); - break; - } else { - mTriggerFalseHook(mParticles, mReadEventCounter); - } +#ifdef GENERATORS_WITH_ONNXRUNTIME + if (!mLoopersGen) { + LOG(error) << "Loopers generator not initialized"; + return kFALSE; } - /** add tracks **/ - if (!addTracks(primGen)) { - LOG(error) << "ReadEvent failed in addTracks"; + // Generate loopers using the initialized TPC loopers generator + if (!mLoopersGen->generateEvent()) { + LOG(error) << "Failed to generate loopers event"; return kFALSE; } - - /** update header **/ - auto header = primGen->GetEvent(); - auto o2header = dynamic_cast(header); - if (!header) { - LOG(fatal) << "MC event header is not a 'o2::dataformats::MCEventHeader' object"; + const auto& looperParticles = mLoopersGen->importParticles(); + if (looperParticles.empty()) { + LOG(error) << "Failed to import loopers particles"; return kFALSE; } - updateHeader(o2header); - updateSubGeneratorInformation(o2header); + // Append the generated looper particles to the main particle list + mParticles.insert(mParticles.end(), looperParticles.begin(), looperParticles.end()); - /** success **/ + LOG(debug) << "Added " << looperParticles.size() << " looper particles"; + return kTRUE; +#else + LOG(warn) << "ONNX Runtime support not available, skipping TPC loopers generation"; return kTRUE; +#endif } + /*****************************************************************/ + + Bool_t + Generator::ReadEvent(FairPrimaryGenerator * primGen) + { + /** read event **/ + + /** endless generate-and-trigger loop **/ + while (true) { + mReadEventCounter++; + + /** clear particle vector **/ + mParticles.clear(); + + /** reset the sub-generator ID **/ + mSubGeneratorId = -1; + + /** generate event **/ + if (!generateEvent()) { + LOG(error) << "ReadEvent failed in generateEvent"; + return kFALSE; + } + + /** import particles **/ + if (!importParticles()) { + LOG(error) << "ReadEvent failed in importParticles"; + return kFALSE; + } + + /** Add loopers **/ + if(mAddLoopers){ + if (!loopers()) { + LOG(error) << "ReadEvent failed in loopers"; + return kFALSE; + } + } + + if (mSubGeneratorsIdToDesc.empty() && mSubGeneratorId > -1) { + LOG(fatal) << "ReadEvent failed because no SubGenerator description given"; + } + + if (!mSubGeneratorsIdToDesc.empty() && mSubGeneratorId < 0) { + LOG(fatal) << "ReadEvent failed because SubGenerator description given but sub-generator not set"; + } + + /** trigger event **/ + if (triggerEvent()) { + mTriggerOkHook(mParticles, mReadEventCounter); + break; + } else { + mTriggerFalseHook(mParticles, mReadEventCounter); + } + } -/*****************************************************************/ + /** add tracks **/ + if (!addTracks(primGen)) { + LOG(error) << "ReadEvent failed in addTracks"; + return kFALSE; + } -Bool_t - Generator::addTracks(FairPrimaryGenerator* primGen) -{ - /** add tracks **/ + /** update header **/ + auto header = primGen->GetEvent(); + auto o2header = dynamic_cast(header); + if (!header) { + LOG(fatal) << "MC event header is not a 'o2::dataformats::MCEventHeader' object"; + return kFALSE; + } + updateHeader(o2header); + updateSubGeneratorInformation(o2header); - auto o2primGen = dynamic_cast(primGen); - if (!o2primGen) { - LOG(fatal) << "PrimaryGenerator is not a o2::eventgen::PrimaryGenerator"; - return kFALSE; + /** success **/ + return kTRUE; } - /** loop over particles **/ - for (const auto& particle : mParticles) { - o2primGen->AddTrack(particle.GetPdgCode(), - particle.Px() * mMomentumUnit, - particle.Py() * mMomentumUnit, - particle.Pz() * mMomentumUnit, - particle.Vx() * mPositionUnit, - particle.Vy() * mPositionUnit, - particle.Vz() * mPositionUnit, - particle.GetMother(0), - particle.GetMother(1), - particle.GetDaughter(0), - particle.GetDaughter(1), - particle.TestBit(ParticleStatus::kToBeDone), - particle.Energy() * mEnergyUnit, - particle.T() * mTimeUnit, - particle.GetWeight(), - (TMCProcess)particle.GetUniqueID(), - particle.GetStatusCode()); // generator status information passed as status code field - } + /*****************************************************************/ - /** success **/ - return kTRUE; -} + Bool_t + Generator::addTracks(FairPrimaryGenerator * primGen) + { + /** add tracks **/ -/*****************************************************************/ + auto o2primGen = dynamic_cast(primGen); + if (!o2primGen) { + LOG(fatal) << "PrimaryGenerator is not a o2::eventgen::PrimaryGenerator"; + return kFALSE; + } -Bool_t - Generator::boostEvent() -{ - /** boost event **/ + /** loop over particles **/ + for (const auto& particle : mParticles) { + o2primGen->AddTrack(particle.GetPdgCode(), + particle.Px() * mMomentumUnit, + particle.Py() * mMomentumUnit, + particle.Pz() * mMomentumUnit, + particle.Vx() * mPositionUnit, + particle.Vy() * mPositionUnit, + particle.Vz() * mPositionUnit, + particle.GetMother(0), + particle.GetMother(1), + particle.GetDaughter(0), + particle.GetDaughter(1), + particle.TestBit(ParticleStatus::kToBeDone), + particle.Energy() * mEnergyUnit, + particle.T() * mTimeUnit, + particle.GetWeight(), + (TMCProcess)particle.GetUniqueID(), + particle.GetStatusCode()); // generator status information passed as status code field + } - /** success **/ - return kTRUE; -} + /** success **/ + return kTRUE; + } -/*****************************************************************/ + /*****************************************************************/ -Bool_t - Generator::triggerEvent() -{ - /** trigger event **/ + Bool_t + Generator::boostEvent() + { + /** boost event **/ - /** check trigger presence **/ - if (mTriggers.size() == 0 && mDeepTriggers.size() == 0) { + /** success **/ return kTRUE; } - /** check trigger mode **/ - Bool_t triggered; - if (mTriggerMode == kTriggerOFF) { - return kTRUE; - } else if (mTriggerMode == kTriggerOR) { - triggered = kFALSE; - } else if (mTriggerMode == kTriggerAND) { - triggered = kTRUE; - } else { - return kTRUE; - } + /*****************************************************************/ - /** loop over triggers **/ - for (const auto& trigger : mTriggers) { - auto retval = trigger(mParticles); - if (mTriggerMode == kTriggerOR) { - triggered |= retval; + Bool_t + Generator::triggerEvent() + { + /** trigger event **/ + + /** check trigger presence **/ + if (mTriggers.size() == 0 && mDeepTriggers.size() == 0) { + return kTRUE; } - if (mTriggerMode == kTriggerAND) { - triggered &= retval; + + /** check trigger mode **/ + Bool_t triggered; + if (mTriggerMode == kTriggerOFF) { + return kTRUE; + } else if (mTriggerMode == kTriggerOR) { + triggered = kFALSE; + } else if (mTriggerMode == kTriggerAND) { + triggered = kTRUE; + } else { + return kTRUE; } - } - /** loop over deep triggers **/ - for (const auto& trigger : mDeepTriggers) { - auto retval = trigger(mInterface, mInterfaceName); - if (mTriggerMode == kTriggerOR) { - triggered |= retval; + /** loop over triggers **/ + for (const auto& trigger : mTriggers) { + auto retval = trigger(mParticles); + if (mTriggerMode == kTriggerOR) { + triggered |= retval; + } + if (mTriggerMode == kTriggerAND) { + triggered &= retval; + } } - if (mTriggerMode == kTriggerAND) { - triggered &= retval; + + /** loop over deep triggers **/ + for (const auto& trigger : mDeepTriggers) { + auto retval = trigger(mInterface, mInterfaceName); + if (mTriggerMode == kTriggerOR) { + triggered |= retval; + } + if (mTriggerMode == kTriggerAND) { + triggered &= retval; + } } - } - /** return **/ - return triggered; -} + /** return **/ + return triggered; + } -/*****************************************************************/ + /*****************************************************************/ -void Generator::addSubGenerator(int subGeneratorId, std::string const& subGeneratorDescription) -{ - if (subGeneratorId < 0) { - LOG(fatal) << "Sub-generator IDs must be >= 0, instead, passed value is " << subGeneratorId; + void Generator::addSubGenerator(int subGeneratorId, std::string const& subGeneratorDescription) + { + if (subGeneratorId < 0) { + LOG(fatal) << "Sub-generator IDs must be >= 0, instead, passed value is " << subGeneratorId; + } + mSubGeneratorsIdToDesc.insert({subGeneratorId, subGeneratorDescription}); } - mSubGeneratorsIdToDesc.insert({subGeneratorId, subGeneratorDescription}); -} -/*****************************************************************/ + /*****************************************************************/ -void Generator::updateSubGeneratorInformation(o2::dataformats::MCEventHeader* header) const -{ - if (mSubGeneratorId < 0) { - return; + void Generator::updateSubGeneratorInformation(o2::dataformats::MCEventHeader * header) const + { + if (mSubGeneratorId < 0) { + return; + } + header->putInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, mSubGeneratorId); + header->putInfo>(o2::mcgenid::GeneratorProperty::SUBGENERATORDESCRIPTIONMAP, mSubGeneratorsIdToDesc); } - header->putInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, mSubGeneratorId); - header->putInfo>(o2::mcgenid::GeneratorProperty::SUBGENERATORDESCRIPTIONMAP, mSubGeneratorsIdToDesc); -} -/*****************************************************************/ -/*****************************************************************/ + /*****************************************************************/ + /*****************************************************************/ } /* namespace eventgen */ } /* namespace o2 */ diff --git a/Generators/src/GeneratorsLinkDef.h b/Generators/src/GeneratorsLinkDef.h index 2b8d42f86bf9b..97896d8225042 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_ONNXRUNTIME +#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/TPCLoopers.cxx b/Generators/src/TPCLoopers.cxx new file mode 100644 index 0000000000000..4eacb7674599c --- /dev/null +++ b/Generators/src/TPCLoopers.cxx @@ -0,0 +1,417 @@ +#include "Generators/TPCLoopers.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 normal_min; + std::vector normal_max; + std::vector outlier_center; + std::vector 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 != "") + { + 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 != "") + { + 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 + nLoopers = 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())); + } + if (mGaussSet) { + mNLoopersCompton = static_cast(std::round(mMultiplier[1] * GaussianElectrons())); + } + // 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; + // 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 + mMass_e * mMass_e); + p_etot = TMath::Sqrt(px_p * px_p + py_p * py_p + pz_p * pz_p + mMass_p * mMass_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 + mMass_e * mMass_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(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, const Int_t& number) +{ + mFlatGas = flat; + if (mFlatGas) { + 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; + 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 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; +} + +} // 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); From 4b6530fbc6a93963de72035d123b6666b2991e32 Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Fri, 26 Sep 2025 15:57:46 +0200 Subject: [PATCH 093/701] Various improvements Fixed arrays in TPCLoopersParams and implemented comments --- .../SimConfig/include/SimConfig/SimConfig.h | 3 - Common/SimConfig/src/SimConfig.cxx | 2 - Generators/CMakeLists.txt | 4 +- Generators/include/Generators/Generator.h | 11 +- .../include/Generators/TPCLoopersParam.h | 5 +- Generators/include/TPCLoopers.h | 12 +- Generators/src/Generator.cxx | 392 +++++++++--------- Generators/src/GeneratorsLinkDef.h | 2 +- Generators/src/TPCLoopers.cxx | 3 - 9 files changed, 214 insertions(+), 220 deletions(-) diff --git a/Common/SimConfig/include/SimConfig/SimConfig.h b/Common/SimConfig/include/SimConfig/SimConfig.h index 8642a0e5bc225..be88d9fbd8c33 100644 --- a/Common/SimConfig/include/SimConfig/SimConfig.h +++ b/Common/SimConfig/include/SimConfig/SimConfig.h @@ -52,7 +52,6 @@ struct SimConfigData { std::vector mActiveModules; // list of active modules std::vector mReadoutDetectors; // list of readout detectors std::string mMCEngine; // chosen VMC engine - bool mNoLoopers = false; // Disable automatic TPC loopers std::string mGenerator; // chosen VMC generator std::string mTrigger; // chosen VMC generator trigger unsigned int mNEvents; // number of events to be simulated @@ -139,8 +138,6 @@ class SimConfig // get selected active detectors std::vector const& getActiveModules() const { return mConfigData.mActiveModules; } std::vector const& getReadoutDetectors() const { return mConfigData.mReadoutDetectors; } - // get loopers veto - bool getLoopersVeto() const { return mConfigData.mNoLoopers; } // static helper functions to determine list of active / readout modules // can also be used from outside diff --git a/Common/SimConfig/src/SimConfig.cxx b/Common/SimConfig/src/SimConfig.cxx index 5ddc3199e3d4a..15879687872d5 100644 --- a/Common/SimConfig/src/SimConfig.cxx +++ b/Common/SimConfig/src/SimConfig.cxx @@ -74,7 +74,6 @@ void SimConfig::initOptions(boost::program_options::options_description& options "run", bpo::value()->default_value(-1), "ALICE run number")( "asservice", bpo::value()->default_value(false), "run in service/server mode")( "noGeant", bpo::bool_switch(), "prohibits any Geant transport/physics (by using tight cuts)")( - "noLoopers", bpo::bool_switch(), "disable automatic TPC loopers")( "forwardKine", bpo::bool_switch(), "forward kinematics on a FairMQ channel")( "noDiscOutput", bpo::bool_switch(), "switch off writing sim results to disc (useful in combination with forwardKine)"); options.add_options()("fromCollContext", bpo::value()->default_value(""), "Use a pregenerated collision context to infer number of events to simulate, how to embedd them, the vertex position etc. Takes precedence of other options such as \"--nEvents\". The format is COLLISIONCONTEXTFILE.root[:SIGNALNAME] where SIGNALNAME is the event part in the context which is relevant."); @@ -298,7 +297,6 @@ bool SimConfig::resetFromParsedMap(boost::program_options::variables_map const& using o2::detectors::DetID; mConfigData.mMCEngine = vm["mcEngine"].as(); mConfigData.mNoGeant = vm["noGeant"].as(); - mConfigData.mNoLoopers = vm["noLoopers"].as(); // Reset modules and detectors as they are anyway re-parsed mConfigData.mReadoutDetectors.clear(); diff --git a/Generators/CMakeLists.txt b/Generators/CMakeLists.txt index 56fe8b8fc2284..f1921b8d8d72a 100644 --- a/Generators/CMakeLists.txt +++ b/Generators/CMakeLists.txt @@ -67,7 +67,7 @@ if(HepMC3_FOUND) endif() if(onnxruntime_FOUND) - target_compile_definitions(${targetName} PUBLIC GENERATORS_WITH_ONNXRUNTIME) + target_compile_definitions(${targetName} PUBLIC GENERATORS_WITH_TPCLOOPERS) endif() set(headers @@ -96,7 +96,7 @@ set(headers ) if(onnxruntime_FOUND) - list(APPEND headers + list(APPEND headers include/Generators/TPCLoopers.h include/Generators/TPCLoopersParam.h) endif() diff --git a/Generators/include/Generators/Generator.h b/Generators/include/Generators/Generator.h index 374d53f324399..4b68112517893 100644 --- a/Generators/include/Generators/Generator.h +++ b/Generators/include/Generators/Generator.h @@ -17,7 +17,8 @@ #include "FairGenerator.h" #include "TParticle.h" #include "Generators/Trigger.h" -#ifdef GENERATORS_WITH_ONNXRUNTIME +#include "CCDB/BasicCCDBManager.h" +#ifdef GENERATORS_WITH_TPCLOOPERS #include "Generators/TPCLoopers.h" #include "Generators/TPCLoopersParam.h" #endif @@ -77,7 +78,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 loopers(); // adds loopers to the event in case TPC is used + 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(); @@ -160,7 +161,7 @@ class Generator : public FairGenerator void updateSubGeneratorInformation(o2::dataformats::MCEventHeader* header) const; // loopers flag - Bool_t mAddLoopers = kFALSE; + 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) @@ -169,11 +170,11 @@ class Generator : public FairGenerator // global static information about (upper limit of) number of events to be generated static unsigned int gTotalNEvents; -#ifdef GENERATORS_WITH_ONNXRUNTIME +#ifdef GENERATORS_WITH_TPCLOOPERS // Loopers generator instance std::unique_ptr mLoopersGen = nullptr; -#endif void initLoopersGen(); +#endif ClassDefOverride(Generator, 2); diff --git a/Generators/include/Generators/TPCLoopersParam.h b/Generators/include/Generators/TPCLoopersParam.h index ceeea201538b2..9430f4e05ac6e 100644 --- a/Generators/include/Generators/TPCLoopersParam.h +++ b/Generators/include/Generators/TPCLoopersParam.h @@ -28,6 +28,7 @@ namespace eventgen ** allow the user to modify them **/ struct GenTPCLoopersParam : public o2::conf::ConfigurableParamHelper { + bool loopersVeto = false; // if true, no loopers are generated 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/egconfig/poisson_params.csv"; // file with Poissonian parameters @@ -37,8 +38,8 @@ struct GenTPCLoopersParam : public o2::conf::ConfigurableParamHelper multiplier = {1., 1.}; // multiplier for pairs and compton loopers for Poissonian and Gaussian sampling - std::array fixedNLoopers = {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 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 O2ParamDef(GenTPCLoopersParam, "GenTPCLoopers"); }; diff --git a/Generators/include/TPCLoopers.h b/Generators/include/TPCLoopers.h index 70146a82baf60..1c1f3585eb3ab 100644 --- a/Generators/include/TPCLoopers.h +++ b/Generators/include/TPCLoopers.h @@ -1,7 +1,7 @@ #ifndef ALICEO2_EVENTGEN_TPCLOOPERS_H_ #define ALICEO2_EVENTGEN_TPCLOOPERS_H_ -#ifdef GENERATORS_WITH_ONNXRUNTIME +#ifdef GENERATORS_WITH_TPCLOOPERS #include #endif #include @@ -19,12 +19,10 @@ #include "TParticle.h" #include -#ifdef GENERATORS_WITH_ONNXRUNTIME +#ifdef GENERATORS_WITH_TPCLOOPERS // Static Ort::Env instance for multiple onnx model loading extern Ort::Env global_env; -#endif -#ifdef GENERATORS_WITH_ONNXRUNTIME // This class is responsible for loading the scaler parameters from a JSON file // and applying the inverse transformation to the generated data. struct Scaler @@ -55,14 +53,14 @@ class ONNXGenerator Ort::Session session; TRandom3 rand_gen; }; -#endif // GENERATORS_WITH_ONNXRUNTIME +#endif // GENERATORS_WITH_TPCLOOPERS namespace o2 { namespace eventgen { -#ifdef GENERATORS_WITH_ONNXRUNTIME +#ifdef GENERATORS_WITH_TPCLOOPERS class GenTPCLoopers { public: @@ -119,7 +117,7 @@ class GenTPCLoopers double mTimeEnd = 0.0; // Time limit for the last event float mLoopsFractionPairs = 0.08; // Fraction of loopers from Pairs }; -#endif // GENERATORS_WITH_ONNXRUNTIME +#endif // GENERATORS_WITH_TPCLOOPERS } // namespace eventgen } // namespace o2 diff --git a/Generators/src/Generator.cxx b/Generators/src/Generator.cxx index 153ef5cd5e35e..6fc9f378148d3 100644 --- a/Generators/src/Generator.cxx +++ b/Generators/src/Generator.cxx @@ -42,18 +42,20 @@ Generator::Generator() : FairGenerator("ALICEo2", "ALICEo2 Generator"), /** default constructor **/ mThisInstanceID = Generator::InstanceCounter; Generator::InstanceCounter++; - auto simConfig = o2::conf::SimConfig::Instance(); - auto noLoops = simConfig.getLoopersVeto(); - if (!noLoops) { +#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) { - mAddLoopers = kTRUE; + mAddTPCLoopers = kTRUE; initLoopersGen(); } } } +#endif } /*****************************************************************/ @@ -64,25 +66,26 @@ Generator::Generator(const Char_t* name, const Char_t* title) : FairGenerator(na /** constructor **/ mThisInstanceID = Generator::InstanceCounter; Generator::InstanceCounter++; - auto simConfig = o2::conf::SimConfig::Instance(); - auto noLoops = simConfig.getLoopersVeto(); - if (!noLoops) { +#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) { - mAddLoopers = kTRUE; + mAddTPCLoopers = kTRUE; initLoopersGen(); } } } +#endif } /*****************************************************************/ - +#ifdef GENERATORS_WITH_TPCLOOPERS void Generator::initLoopersGen() { -#ifdef GENERATORS_WITH_ONNXRUNTIME // Expand all environment paths const auto& loopersParam = o2::eventgen::GenTPCLoopersParam::Instance(); std::string model_pairs = gSystem->ExpandPathName(loopersParam.model_pairs.c_str()); @@ -94,8 +97,9 @@ void Generator::initLoopersGen() auto flat_gas = loopersParam.flat_gas; const auto& nFlatGasLoopers = loopersParam.nFlatGasLoopers; auto fraction_pairs = loopersParam.fraction_pairs; - auto multiplier = loopersParam.multiplier; - auto fixedNLoopers = loopersParam.fixedNLoopers; + 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}; const std::array local_names = {"WGANpair.onnx", "WGANcompton.onnx"}; const std::array isAlien = {models[0].starts_with("alien://"), models[1].starts_with("alien://")}; @@ -116,8 +120,10 @@ void Generator::initLoopersGen() } } if (std::any_of(isCCDB.begin(), isCCDB.end(), [](bool v) { return v; })) { - o2::ccdb::CcdbApi ccdb_api; - ccdb_api.init("http://alice-ccdb.cern.ch"); + 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://" @@ -149,7 +155,7 @@ void Generator::initLoopersGen() // Otherwise, Poisson+Gauss sampling or fixed number of loopers 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 - mLoopersGen->SetNLoopers(fixedNLoopers[0], fixedNLoopers[1]); + mLoopersGen->SetNLoopers(nLoopersPairs, nLoopersCompton); mLoopersGen->SetMultiplier(multiplier); } LOG(info) << "TPC Loopers generator initialized successfully"; @@ -157,10 +163,8 @@ void Generator::initLoopersGen() LOG(error) << "Failed to initialize TPC Loopers generator: " << e.what(); mLoopersGen.reset(); } -#else - LOG(warn) << "ONNX Runtime support not available, cannot initialize TPC loopers generator"; -#endif } +#endif /*****************************************************************/ @@ -176,230 +180,228 @@ Bool_t /*****************************************************************/ Bool_t - Generator::loopers() + Generator::finalizeEvent() { -#ifdef GENERATORS_WITH_ONNXRUNTIME - if (!mLoopersGen) { - LOG(error) << "Loopers generator not initialized"; - return kFALSE; - } +#ifdef GENERATORS_WITH_TPCLOOPERS + if(mAddTPCLoopers) { + if (!mLoopersGen) { + LOG(error) << "Loopers generator not initialized"; + return kFALSE; + } - // Generate loopers using the initialized TPC loopers generator - if (!mLoopersGen->generateEvent()) { - LOG(error) << "Failed to generate loopers event"; - return kFALSE; - } - const auto& looperParticles = mLoopersGen->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()); + // Generate loopers using the initialized TPC loopers generator + if (!mLoopersGen->generateEvent()) { + LOG(error) << "Failed to generate loopers event"; + return kFALSE; + } + const auto& looperParticles = mLoopersGen->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"; - return kTRUE; -#else - LOG(warn) << "ONNX Runtime support not available, skipping TPC loopers generation"; - return kTRUE; + LOG(debug) << "Added " << looperParticles.size() << " looper particles"; + } #endif + return kTRUE; } - /*****************************************************************/ - Bool_t - Generator::ReadEvent(FairPrimaryGenerator * primGen) - { - /** read event **/ - - /** endless generate-and-trigger loop **/ - while (true) { - mReadEventCounter++; +/*****************************************************************/ - /** clear particle vector **/ - mParticles.clear(); +Bool_t + Generator::ReadEvent(FairPrimaryGenerator* primGen) +{ + /** read event **/ - /** reset the sub-generator ID **/ - mSubGeneratorId = -1; + /** endless generate-and-trigger loop **/ + while (true) { + mReadEventCounter++; - /** generate event **/ - if (!generateEvent()) { - LOG(error) << "ReadEvent failed in generateEvent"; - return kFALSE; - } + /** clear particle vector **/ + mParticles.clear(); - /** import particles **/ - if (!importParticles()) { - LOG(error) << "ReadEvent failed in importParticles"; - return kFALSE; - } + /** reset the sub-generator ID **/ + mSubGeneratorId = -1; - /** Add loopers **/ - if(mAddLoopers){ - if (!loopers()) { - LOG(error) << "ReadEvent failed in loopers"; - return kFALSE; - } - } + /** generate event **/ + if (!generateEvent()) { + LOG(error) << "ReadEvent failed in generateEvent"; + return kFALSE; + } - if (mSubGeneratorsIdToDesc.empty() && mSubGeneratorId > -1) { - LOG(fatal) << "ReadEvent failed because no SubGenerator description given"; - } + /** import particles **/ + if (!importParticles()) { + LOG(error) << "ReadEvent failed in importParticles"; + return kFALSE; + } - if (!mSubGeneratorsIdToDesc.empty() && mSubGeneratorId < 0) { - LOG(fatal) << "ReadEvent failed because SubGenerator description given but sub-generator not set"; - } + /** Event finalization**/ + if(!finalizeEvent()) { + LOG(error) << "ReadEvent failed in finalizeEvent"; + return kFALSE; + } - /** trigger event **/ - if (triggerEvent()) { - mTriggerOkHook(mParticles, mReadEventCounter); - break; - } else { - mTriggerFalseHook(mParticles, mReadEventCounter); - } + if (mSubGeneratorsIdToDesc.empty() && mSubGeneratorId > -1) { + LOG(fatal) << "ReadEvent failed because no SubGenerator description given"; } - /** add tracks **/ - if (!addTracks(primGen)) { - LOG(error) << "ReadEvent failed in addTracks"; - return kFALSE; + if (!mSubGeneratorsIdToDesc.empty() && mSubGeneratorId < 0) { + LOG(fatal) << "ReadEvent failed because SubGenerator description given but sub-generator not set"; } - /** update header **/ - auto header = primGen->GetEvent(); - auto o2header = dynamic_cast(header); - if (!header) { - LOG(fatal) << "MC event header is not a 'o2::dataformats::MCEventHeader' object"; - return kFALSE; + /** trigger event **/ + if (triggerEvent()) { + mTriggerOkHook(mParticles, mReadEventCounter); + break; + } else { + mTriggerFalseHook(mParticles, mReadEventCounter); } - updateHeader(o2header); - updateSubGeneratorInformation(o2header); + } - /** success **/ - return kTRUE; + /** add tracks **/ + if (!addTracks(primGen)) { + LOG(error) << "ReadEvent failed in addTracks"; + return kFALSE; + } + + /** update header **/ + auto header = primGen->GetEvent(); + auto o2header = dynamic_cast(header); + if (!header) { + LOG(fatal) << "MC event header is not a 'o2::dataformats::MCEventHeader' object"; + return kFALSE; } + updateHeader(o2header); + updateSubGeneratorInformation(o2header); - /*****************************************************************/ + /** success **/ + return kTRUE; +} - Bool_t - Generator::addTracks(FairPrimaryGenerator * primGen) - { - /** add tracks **/ +/*****************************************************************/ - auto o2primGen = dynamic_cast(primGen); - if (!o2primGen) { - LOG(fatal) << "PrimaryGenerator is not a o2::eventgen::PrimaryGenerator"; - return kFALSE; - } +Bool_t + Generator::addTracks(FairPrimaryGenerator* primGen) +{ + /** add tracks **/ - /** loop over particles **/ - for (const auto& particle : mParticles) { - o2primGen->AddTrack(particle.GetPdgCode(), - particle.Px() * mMomentumUnit, - particle.Py() * mMomentumUnit, - particle.Pz() * mMomentumUnit, - particle.Vx() * mPositionUnit, - particle.Vy() * mPositionUnit, - particle.Vz() * mPositionUnit, - particle.GetMother(0), - particle.GetMother(1), - particle.GetDaughter(0), - particle.GetDaughter(1), - particle.TestBit(ParticleStatus::kToBeDone), - particle.Energy() * mEnergyUnit, - particle.T() * mTimeUnit, - particle.GetWeight(), - (TMCProcess)particle.GetUniqueID(), - particle.GetStatusCode()); // generator status information passed as status code field - } + auto o2primGen = dynamic_cast(primGen); + if (!o2primGen) { + LOG(fatal) << "PrimaryGenerator is not a o2::eventgen::PrimaryGenerator"; + return kFALSE; + } - /** success **/ - return kTRUE; + /** loop over particles **/ + for (const auto& particle : mParticles) { + o2primGen->AddTrack(particle.GetPdgCode(), + particle.Px() * mMomentumUnit, + particle.Py() * mMomentumUnit, + particle.Pz() * mMomentumUnit, + particle.Vx() * mPositionUnit, + particle.Vy() * mPositionUnit, + particle.Vz() * mPositionUnit, + particle.GetMother(0), + particle.GetMother(1), + particle.GetDaughter(0), + particle.GetDaughter(1), + particle.TestBit(ParticleStatus::kToBeDone), + particle.Energy() * mEnergyUnit, + particle.T() * mTimeUnit, + particle.GetWeight(), + (TMCProcess)particle.GetUniqueID(), + particle.GetStatusCode()); // generator status information passed as status code field } - /*****************************************************************/ + /** success **/ + return kTRUE; +} + +/*****************************************************************/ - Bool_t - Generator::boostEvent() - { - /** boost event **/ +Bool_t + Generator::boostEvent() +{ + /** boost event **/ + + /** success **/ + return kTRUE; +} + +/*****************************************************************/ + +Bool_t + Generator::triggerEvent() +{ + /** trigger event **/ - /** success **/ + /** check trigger presence **/ + if (mTriggers.size() == 0 && mDeepTriggers.size() == 0) { return kTRUE; } - /*****************************************************************/ - - Bool_t - Generator::triggerEvent() - { - /** trigger event **/ + /** check trigger mode **/ + Bool_t triggered; + if (mTriggerMode == kTriggerOFF) { + return kTRUE; + } else if (mTriggerMode == kTriggerOR) { + triggered = kFALSE; + } else if (mTriggerMode == kTriggerAND) { + triggered = kTRUE; + } else { + return kTRUE; + } - /** check trigger presence **/ - if (mTriggers.size() == 0 && mDeepTriggers.size() == 0) { - return kTRUE; + /** loop over triggers **/ + for (const auto& trigger : mTriggers) { + auto retval = trigger(mParticles); + if (mTriggerMode == kTriggerOR) { + triggered |= retval; } - - /** check trigger mode **/ - Bool_t triggered; - if (mTriggerMode == kTriggerOFF) { - return kTRUE; - } else if (mTriggerMode == kTriggerOR) { - triggered = kFALSE; - } else if (mTriggerMode == kTriggerAND) { - triggered = kTRUE; - } else { - return kTRUE; + if (mTriggerMode == kTriggerAND) { + triggered &= retval; } + } - /** loop over triggers **/ - for (const auto& trigger : mTriggers) { - auto retval = trigger(mParticles); - if (mTriggerMode == kTriggerOR) { - triggered |= retval; - } - if (mTriggerMode == kTriggerAND) { - triggered &= retval; - } + /** loop over deep triggers **/ + for (const auto& trigger : mDeepTriggers) { + auto retval = trigger(mInterface, mInterfaceName); + if (mTriggerMode == kTriggerOR) { + triggered |= retval; } - - /** loop over deep triggers **/ - for (const auto& trigger : mDeepTriggers) { - auto retval = trigger(mInterface, mInterfaceName); - if (mTriggerMode == kTriggerOR) { - triggered |= retval; - } - if (mTriggerMode == kTriggerAND) { - triggered &= retval; - } + if (mTriggerMode == kTriggerAND) { + triggered &= retval; } - - /** return **/ - return triggered; } - /*****************************************************************/ + /** return **/ + return triggered; +} - void Generator::addSubGenerator(int subGeneratorId, std::string const& subGeneratorDescription) - { - if (subGeneratorId < 0) { - LOG(fatal) << "Sub-generator IDs must be >= 0, instead, passed value is " << subGeneratorId; - } - mSubGeneratorsIdToDesc.insert({subGeneratorId, subGeneratorDescription}); +/*****************************************************************/ + +void Generator::addSubGenerator(int subGeneratorId, std::string const& subGeneratorDescription) +{ + if (subGeneratorId < 0) { + LOG(fatal) << "Sub-generator IDs must be >= 0, instead, passed value is " << subGeneratorId; } + mSubGeneratorsIdToDesc.insert({subGeneratorId, subGeneratorDescription}); +} - /*****************************************************************/ +/*****************************************************************/ - void Generator::updateSubGeneratorInformation(o2::dataformats::MCEventHeader * header) const - { - if (mSubGeneratorId < 0) { - return; - } - header->putInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, mSubGeneratorId); - header->putInfo>(o2::mcgenid::GeneratorProperty::SUBGENERATORDESCRIPTIONMAP, mSubGeneratorsIdToDesc); +void Generator::updateSubGeneratorInformation(o2::dataformats::MCEventHeader* header) const +{ + if (mSubGeneratorId < 0) { + return; } + header->putInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, mSubGeneratorId); + header->putInfo>(o2::mcgenid::GeneratorProperty::SUBGENERATORDESCRIPTIONMAP, mSubGeneratorsIdToDesc); +} - /*****************************************************************/ - /*****************************************************************/ +/*****************************************************************/ +/*****************************************************************/ } /* namespace eventgen */ } /* namespace o2 */ diff --git a/Generators/src/GeneratorsLinkDef.h b/Generators/src/GeneratorsLinkDef.h index 97896d8225042..24b3f2e452498 100644 --- a/Generators/src/GeneratorsLinkDef.h +++ b/Generators/src/GeneratorsLinkDef.h @@ -35,7 +35,7 @@ #pragma link C++ class o2::eventgen::GeneratorFromEventPool + ; #pragma link C++ class o2::eventgen::GeneratorEventPoolParam + ; #pragma link C++ class o2::eventgen::EventPoolGenConfig + ; -#ifdef GENERATORS_WITH_ONNXRUNTIME +#ifdef GENERATORS_WITH_TPCLOOPERS #pragma link C++ class o2::eventgen::GenTPCLoopers + ; #pragma link C++ class o2::eventgen::GenTPCLoopersParam + ; #endif diff --git a/Generators/src/TPCLoopers.cxx b/Generators/src/TPCLoopers.cxx index 4eacb7674599c..109461ab71dfa 100644 --- a/Generators/src/TPCLoopers.cxx +++ b/Generators/src/TPCLoopers.cxx @@ -382,9 +382,6 @@ void GenTPCLoopers::setFlatGas(Bool_t& flat, const Int_t& number) 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(); From 8f8606a66c8499de6df999795d595f3dbab9e5b3 Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Sun, 12 Oct 2025 17:24:31 +0200 Subject: [PATCH 094/701] Vetoing loopers for FlatGas and \!collisioncontext --- Generators/include/Generators/Generator.h | 2 +- Generators/src/Generator.cxx | 21 ++++++++++++++++----- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/Generators/include/Generators/Generator.h b/Generators/include/Generators/Generator.h index 4b68112517893..67277e20736ce 100644 --- a/Generators/include/Generators/Generator.h +++ b/Generators/include/Generators/Generator.h @@ -173,7 +173,7 @@ class Generator : public FairGenerator #ifdef GENERATORS_WITH_TPCLOOPERS // Loopers generator instance std::unique_ptr mLoopersGen = nullptr; - void initLoopersGen(); + bool initLoopersGen(); #endif ClassDefOverride(Generator, 2); diff --git a/Generators/src/Generator.cxx b/Generators/src/Generator.cxx index 6fc9f378148d3..50b11c0c7bb53 100644 --- a/Generators/src/Generator.cxx +++ b/Generators/src/Generator.cxx @@ -50,8 +50,9 @@ Generator::Generator() : FairGenerator("ALICEo2", "ALICEo2 Generator"), if (transport) { bool tpcActive = (std::find(simConfig.getReadoutDetectors().begin(), simConfig.getReadoutDetectors().end(), "TPC") != simConfig.getReadoutDetectors().end()); if (tpcActive) { - mAddTPCLoopers = kTRUE; - initLoopersGen(); + if(initLoopersGen()){ + mAddTPCLoopers = kTRUE; + } } } } @@ -74,8 +75,9 @@ Generator::Generator(const Char_t* name, const Char_t* title) : FairGenerator(na if (transport) { bool tpcActive = (std::find(simConfig.getReadoutDetectors().begin(), simConfig.getReadoutDetectors().end(), "TPC") != simConfig.getReadoutDetectors().end()); if (tpcActive) { - mAddTPCLoopers = kTRUE; - initLoopersGen(); + if (initLoopersGen()) { + mAddTPCLoopers = kTRUE; + } } } } @@ -84,7 +86,7 @@ Generator::Generator(const Char_t* name, const Char_t* title) : FairGenerator(na /*****************************************************************/ #ifdef GENERATORS_WITH_TPCLOOPERS -void Generator::initLoopersGen() +bool Generator::initLoopersGen() { // Expand all environment paths const auto& loopersParam = o2::eventgen::GenTPCLoopersParam::Instance(); @@ -95,6 +97,14 @@ void Generator::initLoopersGen() const auto& poisson = gSystem->ExpandPathName(loopersParam.poisson.c_str()); const auto& gauss = gSystem->ExpandPathName(loopersParam.gauss.c_str()); auto flat_gas = loopersParam.flat_gas; + if (flat_gas) { + 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; + } + } const auto& nFlatGasLoopers = loopersParam.nFlatGasLoopers; auto fraction_pairs = loopersParam.fraction_pairs; std::array multiplier = {loopersParam.multiplier[0], loopersParam.multiplier[1]}; @@ -163,6 +173,7 @@ void Generator::initLoopersGen() LOG(error) << "Failed to initialize TPC Loopers generator: " << e.what(); mLoopersGen.reset(); } + return kTRUE; } #endif From 32c0f318ec9737e7c1718c373d3c9660ddff477c Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Fri, 24 Oct 2025 16:42:27 +0200 Subject: [PATCH 095/701] Implemented rate and collision system dependence (default) --- .../include/Generators/TPCLoopersParam.h | 4 ++ Generators/include/TPCLoopers.h | 8 ++- Generators/src/Generator.cxx | 28 +++++--- Generators/src/TPCLoopers.cxx | 64 ++++++++++++++++--- 4 files changed, 87 insertions(+), 17 deletions(-) diff --git a/Generators/include/Generators/TPCLoopersParam.h b/Generators/include/Generators/TPCLoopersParam.h index 9430f4e05ac6e..8571013cdec48 100644 --- a/Generators/include/Generators/TPCLoopersParam.h +++ b/Generators/include/Generators/TPCLoopersParam.h @@ -35,11 +35,15 @@ struct GenTPCLoopersParam : public o2::conf::ConfigurableParamHelper #include "SimulationDataFormat/MCGenProperties.h" #include "TParticle.h" +#include "TF1.h" #include #ifdef GENERATORS_WITH_TPCLOOPERS @@ -82,10 +83,14 @@ class GenTPCLoopers void SetMultiplier(std::array &mult); - void setFlatGas(Bool_t &flat, const Int_t &number = -1); + void setFlatGas(Bool_t& flat, const Int_t& number, const Int_t& nloopers_orbit); void setFractionPairs(float &fractionPairs); + void SetRate(const std::string &rateFile, const bool &isPbPb, const int &intRate); + + void SetAdjust(const float &adjust); + private: std::unique_ptr mONNX_pair = nullptr; std::unique_ptr mONNX_compton = nullptr; @@ -111,6 +116,7 @@ class GenTPCLoopers 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 diff --git a/Generators/src/Generator.cxx b/Generators/src/Generator.cxx index 50b11c0c7bb53..fea1a38f1a146 100644 --- a/Generators/src/Generator.cxx +++ b/Generators/src/Generator.cxx @@ -92,6 +92,7 @@ bool Generator::initLoopersGen() 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()); @@ -110,10 +111,10 @@ bool Generator::initLoopersGen() 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}; - const std::array local_names = {"WGANpair.onnx", "WGANcompton.onnx"}; - const std::array isAlien = {models[0].starts_with("alien://"), models[1].starts_with("alien://")}; - const std::array isCCDB = {models[0].starts_with("ccdb://"), models[1].starts_with("ccdb://")}; + 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://"); @@ -153,14 +154,25 @@ bool Generator::initLoopersGen() } 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 mLoopersGen = std::make_unique(model_pairs, model_compton, poisson, gauss, scaler_pair, scaler_compton); - - // Configure the generator with flat gas loopers if enabled (default) + auto& colsys = loopersParam.colsys; + auto &intrate = loopersParam.intrate; + // Configure the generator with flat gas loopers defined per orbit with clusters/track info if (flat_gas) { - mLoopersGen->setFlatGas(flat_gas, nFlatGasLoopers); - mLoopersGen->setFractionPairs(fraction_pairs); + if (colsys != "PbPb" && colsys != "pp") { + LOG(fatal) << "Error: collision system must be either 'PbPb' or 'pp'"; + exit(1); + } else { + if (intrate <= 0) { + LOG(fatal) << "Error: interaction rate must be positive!"; + exit(1); + } + mLoopersGen->SetRate(nclxrate, (colsys == "PbPb") ? true : false, intrate); + mLoopersGen->SetAdjust(loopersParam.adjust_flatgas); + } } else { // Otherwise, Poisson+Gauss sampling or fixed number of loopers will be used // Multiplier is applied only with distribution sampling diff --git a/Generators/src/TPCLoopers.cxx b/Generators/src/TPCLoopers.cxx index 109461ab71dfa..b771b53ed33d2 100644 --- a/Generators/src/TPCLoopers.cxx +++ b/Generators/src/TPCLoopers.cxx @@ -197,7 +197,8 @@ Bool_t GenTPCLoopers::generateEvent() 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 - nLoopers = mFlatGasNumber * (mTimeLimit / mIntTimeRecMean); + // 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); @@ -366,22 +367,34 @@ void GenTPCLoopers::SetMultiplier(std::array& mult) } } -void GenTPCLoopers::setFlatGas(Bool_t& flat, const Int_t& number) +void GenTPCLoopers::setFlatGas(Bool_t& flat, const Int_t& number = -1, const Int_t& nloopers_orbit = -1) { mFlatGas = flat; if (mFlatGas) { - if (number < 0) { - LOG(warn) << "Warning: Number of loopers per event must be non-negative! Switching option off."; - mFlatGas = false; - mFlatGasNumber = -1; + if (nloopers_orbit > 0) { + mFlatGasOrbit = true; + mFlatGasNumber = nloopers_orbit; + LOG(info) << "Flat gas loopers will be generated using orbit reference."; } else { - mFlatGasNumber = number; + 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(); @@ -397,7 +410,7 @@ void GenTPCLoopers::setFlatGas(Bool_t& flat, const Int_t& number) } else { mFlatGasNumber = -1; } - LOG(info) << "Flat gas loopers: " << (mFlatGas ? "ON" : "OFF") << ", Reference loopers number per event: " << mFlatGasNumber; + LOG(info) << "Flat gas loopers: " << (mFlatGas ? "ON" : "OFF") << ", Reference loopers number per " << (mFlatGasOrbit ? "orbit " : "event ") << mFlatGasNumber; } void GenTPCLoopers::setFractionPairs(float& fractionPairs) @@ -410,5 +423,40 @@ void GenTPCLoopers::setFractionPairs(float& fractionPairs) LOG(info) << "Pairs fraction set to: " << mLoopsFractionPairs; } +void GenTPCLoopers::SetRate(const std::string &rateFile, const bool &isPbPb = true, const int &intRate = 50000) +{ + // 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); + } + auto ref = static_cast(std::floor(fit->Eval(intRate / 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 " << intRate << " Hz interaction rate."; + auto flat = true; + setFlatGas(flat, -1, ref); + } +} + +void GenTPCLoopers::SetAdjust(const float& adjust = 0.f) +{ + 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 From a6f60e12edf933dbeb724b9ba7c51a4c5e49cffc Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Thu, 20 Nov 2025 22:40:33 +0100 Subject: [PATCH 096/701] Set automatic interaction rate from collision context --- .../include/Generators/TPCLoopersParam.h | 4 ++-- Generators/include/TPCLoopers.h | 3 +++ Generators/src/Generator.cxx | 10 +++++----- Generators/src/TPCLoopers.cxx | 19 +++++++++++++++++-- 4 files changed, 27 insertions(+), 9 deletions(-) diff --git a/Generators/include/Generators/TPCLoopersParam.h b/Generators/include/Generators/TPCLoopersParam.h index 8571013cdec48..74c3cf4cff0ad 100644 --- a/Generators/include/Generators/TPCLoopersParam.h +++ b/Generators/include/Generators/TPCLoopersParam.h @@ -37,9 +37,9 @@ struct GenTPCLoopersParam : public o2::conf::ConfigurableParamHelper mONNX_pair = nullptr; std::unique_ptr mONNX_compton = nullptr; @@ -122,6 +124,7 @@ class GenTPCLoopers 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 diff --git a/Generators/src/Generator.cxx b/Generators/src/Generator.cxx index fea1a38f1a146..18e28e4cc2668 100644 --- a/Generators/src/Generator.cxx +++ b/Generators/src/Generator.cxx @@ -107,7 +107,7 @@ bool Generator::initLoopersGen() } } const auto& nFlatGasLoopers = loopersParam.nFlatGasLoopers; - auto fraction_pairs = loopersParam.fraction_pairs; + const auto& fraction_pairs = loopersParam.fraction_pairs; std::array multiplier = {loopersParam.multiplier[0], loopersParam.multiplier[1]}; unsigned int nLoopersPairs = loopersParam.fixedNLoopers[0]; unsigned int nLoopersCompton = loopersParam.fixedNLoopers[1]; @@ -166,10 +166,6 @@ bool Generator::initLoopersGen() LOG(fatal) << "Error: collision system must be either 'PbPb' or 'pp'"; exit(1); } else { - if (intrate <= 0) { - LOG(fatal) << "Error: interaction rate must be positive!"; - exit(1); - } mLoopersGen->SetRate(nclxrate, (colsys == "PbPb") ? true : false, intrate); mLoopersGen->SetAdjust(loopersParam.adjust_flatgas); } @@ -217,6 +213,10 @@ Bool_t LOG(error) << "Failed to generate loopers event"; return kFALSE; } + if (mLoopersGen->getNLoopers() == 0) { + LOG(warning) << "No loopers generated for this event"; + return kTRUE; + } const auto& looperParticles = mLoopersGen->importParticles(); if (looperParticles.empty()) { LOG(error) << "Failed to import loopers particles"; diff --git a/Generators/src/TPCLoopers.cxx b/Generators/src/TPCLoopers.cxx index b771b53ed33d2..0fb76fcd8c3a9 100644 --- a/Generators/src/TPCLoopers.cxx +++ b/Generators/src/TPCLoopers.cxx @@ -437,13 +437,28 @@ void GenTPCLoopers::SetRate(const std::string &rateFile, const bool &isPbPb = tr LOG(fatal) << "Error: Could not find fit function '" << fitName << "' in rate file!"; exit(1); } - auto ref = static_cast(std::floor(fit->Eval(intRate / 1000.))); // fit expects rate in kHz + 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 " << intRate << " Hz interaction rate."; + 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); } From b48d4ec35cdb9252783a46916898d4bfbac928f3 Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Mon, 24 Nov 2025 10:11:42 +0100 Subject: [PATCH 097/701] Fixed bug + cleaned code --- Generators/include/Generators/TPCLoopersParam.h | 4 ++-- Generators/src/Generator.cxx | 4 +--- Generators/src/TPCLoopers.cxx | 4 ---- 3 files changed, 3 insertions(+), 9 deletions(-) diff --git a/Generators/include/Generators/TPCLoopersParam.h b/Generators/include/Generators/TPCLoopersParam.h index 74c3cf4cff0ad..24d905c59c967 100644 --- a/Generators/include/Generators/TPCLoopersParam.h +++ b/Generators/include/Generators/TPCLoopersParam.h @@ -39,8 +39,8 @@ struct GenTPCLoopersParam : public o2::conf::ConfigurableParamHelper multiplier = {loopersParam.multiplier[0], loopersParam.multiplier[1]}; unsigned int nLoopersPairs = loopersParam.fixedNLoopers[0]; unsigned int nLoopersCompton = loopersParam.fixedNLoopers[1]; @@ -170,7 +168,7 @@ bool Generator::initLoopersGen() mLoopersGen->SetAdjust(loopersParam.adjust_flatgas); } } else { - // Otherwise, Poisson+Gauss sampling or fixed number of loopers will be used + // 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 mLoopersGen->SetNLoopers(nLoopersPairs, nLoopersCompton); diff --git a/Generators/src/TPCLoopers.cxx b/Generators/src/TPCLoopers.cxx index 0fb76fcd8c3a9..07af5b25f99f9 100644 --- a/Generators/src/TPCLoopers.cxx +++ b/Generators/src/TPCLoopers.cxx @@ -27,10 +27,6 @@ void Scaler::load(const std::string &filename) normal_max = jsonArrayToVector(doc["normal"]["max"]); outlier_center = jsonArrayToVector(doc["outlier"]["center"]); outlier_scale = jsonArrayToVector(doc["outlier"]["scale"]); - std::vector normal_min; - std::vector normal_max; - std::vector outlier_center; - std::vector outlier_scale; } std::vector Scaler::inverse_transform(const std::vector &input) From e7a790b31d71d01e91a2a40123327febda65905f Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Mon, 24 Nov 2025 20:22:39 +0100 Subject: [PATCH 098/701] Improved logging + colsys check --- Generators/src/Generator.cxx | 31 ++++++++++++++++++++----------- Generators/src/TPCLoopers.cxx | 6 ++++-- 2 files changed, 24 insertions(+), 13 deletions(-) diff --git a/Generators/src/Generator.cxx b/Generators/src/Generator.cxx index 9e083913c3bc7..9c16c0dfb7e92 100644 --- a/Generators/src/Generator.cxx +++ b/Generators/src/Generator.cxx @@ -50,11 +50,15 @@ Generator::Generator() : FairGenerator("ALICEo2", "ALICEo2 Generator"), if (transport) { bool tpcActive = (std::find(simConfig.getReadoutDetectors().begin(), simConfig.getReadoutDetectors().end(), "TPC") != simConfig.getReadoutDetectors().end()); if (tpcActive) { - if(initLoopersGen()){ + if (initLoopersGen()) { 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 } @@ -78,8 +82,12 @@ Generator::Generator(const Char_t* name, const Char_t* title) : FairGenerator(na if (initLoopersGen()) { 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 } @@ -97,8 +105,14 @@ bool Generator::initLoopersGen() 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()); - auto flat_gas = loopersParam.flat_gas; + 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!"; @@ -156,17 +170,12 @@ bool Generator::initLoopersGen() try { // Create the TPC loopers generator with the provided parameters mLoopersGen = std::make_unique(model_pairs, model_compton, poisson, gauss, scaler_pair, scaler_compton); - auto& colsys = loopersParam.colsys; - auto &intrate = loopersParam.intrate; + 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) { - if (colsys != "PbPb" && colsys != "pp") { - LOG(fatal) << "Error: collision system must be either 'PbPb' or 'pp'"; - exit(1); - } else { - mLoopersGen->SetRate(nclxrate, (colsys == "PbPb") ? true : false, intrate); - mLoopersGen->SetAdjust(loopersParam.adjust_flatgas); - } + mLoopersGen->SetRate(nclxrate, (colsys == "PbPb") ? true : false, intrate); + mLoopersGen->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 diff --git a/Generators/src/TPCLoopers.cxx b/Generators/src/TPCLoopers.cxx index 07af5b25f99f9..ac1123b8d0bbd 100644 --- a/Generators/src/TPCLoopers.cxx +++ b/Generators/src/TPCLoopers.cxx @@ -141,7 +141,7 @@ GenTPCLoopers::GenTPCLoopers(std::string model_pairs, std::string model_compton, scaler_file[0].close(); scaler_file[1].close(); // Checking if the poisson file exists and it's not empty - if (poisson != "") + if (poisson != "" && poisson != "None" && poisson != "none") { std::ifstream poisson_file(poisson); if (!poisson_file.is_open() || poisson_file.peek() == std::ifstream::traits_type::eof()) @@ -157,7 +157,7 @@ GenTPCLoopers::GenTPCLoopers(std::string model_pairs, std::string model_compton, } } // Checking if the gauss file exists and it's not empty - if (gauss != "") + if (gauss != "" && gauss != "None" && gauss != "none") { std::ifstream gauss_file(gauss); if (!gauss_file.is_open() || gauss_file.peek() == std::ifstream::traits_type::eof()) @@ -205,9 +205,11 @@ Bool_t GenTPCLoopers::generateEvent() // 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) { From 569255b67223793d520de38f90463bc2f8ca6917 Mon Sep 17 00:00:00 2001 From: ALICE Action Bot Date: Tue, 25 Nov 2025 07:47:04 +0000 Subject: [PATCH 099/701] Please consider the following formatting changes --- Generators/include/Generators/Generator.h | 2 +- .../include/Generators/TPCLoopersParam.h | 24 +- Generators/include/TPCLoopers.h | 155 ++++++----- .../share/egconfig/ScalerComptonParams.json | 52 ++-- .../share/egconfig/ScalerPairParams.json | 64 ++--- Generators/src/Generator.cxx | 6 +- Generators/src/TPCLoopers.cxx | 256 ++++++++---------- 7 files changed, 271 insertions(+), 288 deletions(-) diff --git a/Generators/include/Generators/Generator.h b/Generators/include/Generators/Generator.h index 67277e20736ce..5a4921e036ca3 100644 --- a/Generators/include/Generators/Generator.h +++ b/Generators/include/Generators/Generator.h @@ -161,7 +161,7 @@ class Generator : public FairGenerator 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 + 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) diff --git a/Generators/include/Generators/TPCLoopersParam.h b/Generators/include/Generators/TPCLoopersParam.h index 24d905c59c967..49c8e5f5927b6 100644 --- a/Generators/include/Generators/TPCLoopersParam.h +++ b/Generators/include/Generators/TPCLoopersParam.h @@ -28,22 +28,22 @@ namespace eventgen ** allow the user to modify them **/ struct GenTPCLoopersParam : public o2::conf::ConfigurableParamHelper { - bool loopersVeto = false; // if true, no loopers are generated - 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/egconfig/poisson_params.csv"; // file with Poissonian parameters - std::string gauss = "${O2_ROOT}/share/Generators/egconfig/gaussian_params.csv"; // file with Gaussian parameters - std::string scaler_pair = "${O2_ROOT}/share/Generators/egconfig/ScalerPairParams.json"; // file with scaler parameters for e+e- pair production + bool loopersVeto = false; // if true, no loopers are generated + 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/egconfig/poisson_params.csv"; // file with Poissonian parameters + std::string gauss = "${O2_ROOT}/share/Generators/egconfig/gaussian_params.csv"; // file with Gaussian parameters + std::string scaler_pair = "${O2_ROOT}/share/Generators/egconfig/ScalerPairParams.json"; // file with scaler parameters for e+e- pair production std::string scaler_compton = "${O2_ROOT}/share/Generators/egconfig/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)] + 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"); }; diff --git a/Generators/include/TPCLoopers.h b/Generators/include/TPCLoopers.h index 8a4dc0030aa21..9addcf844e09d 100644 --- a/Generators/include/TPCLoopers.h +++ b/Generators/include/TPCLoopers.h @@ -26,33 +26,32 @@ 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. -struct Scaler -{ - std::vector normal_min; - std::vector normal_max; - std::vector outlier_center; - std::vector outlier_scale; +struct Scaler { + std::vector normal_min; + std::vector normal_max; + std::vector outlier_center; + std::vector outlier_scale; - void load(const std::string &filename); + void load(const std::string& filename); - std::vector inverse_transform(const std::vector &input); + std::vector inverse_transform(const std::vector& input); -private: - std::vector jsonArrayToVector(const rapidjson::Value &jsonArray); + 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); + public: + ONNXGenerator(Ort::Env& shared_env, const std::string& model_path); - std::vector generate_sample(); + std::vector generate_sample(); -private: - Ort::Env &env; - Ort::Session session; - TRandom3 rand_gen; + private: + Ort::Env& env; + Ort::Session session; + TRandom3 rand_gen; }; #endif // GENERATORS_WITH_TPCLOOPERS @@ -64,67 +63,67 @@ namespace eventgen #ifdef GENERATORS_WITH_TPCLOOPERS 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(std::array &mult); - - void setFlatGas(Bool_t& flat, const Int_t& number, const Int_t& nloopers_orbit); - - void setFractionPairs(float &fractionPairs); - - void SetRate(const std::string &rateFile, const bool &isPbPb, const int &intRate); - - void SetAdjust(const float &adjust); - - 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; - // Masses of the electrons and positrons - TDatabasePDG *mPDG = TDatabasePDG::Instance(); - double mMass_e = mPDG->GetParticle(11)->Mass(); - double mMass_p = mPDG->GetParticle(-11)->Mass(); - 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 + 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(std::array& mult); + + void setFlatGas(Bool_t& flat, const Int_t& number, const Int_t& nloopers_orbit); + + void setFractionPairs(float& fractionPairs); + + void SetRate(const std::string& rateFile, const bool& isPbPb, const int& intRate); + + void SetAdjust(const float& adjust); + + 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; + // Masses of the electrons and positrons + TDatabasePDG* mPDG = TDatabasePDG::Instance(); + double mMass_e = mPDG->GetParticle(11)->Mass(); + double mMass_p = mPDG->GetParticle(-11)->Mass(); + 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 diff --git a/Generators/share/egconfig/ScalerComptonParams.json b/Generators/share/egconfig/ScalerComptonParams.json index d8e654847f46e..157647fee2db7 100644 --- a/Generators/share/egconfig/ScalerComptonParams.json +++ b/Generators/share/egconfig/ScalerComptonParams.json @@ -1,28 +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 - ] - } + "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/egconfig/ScalerPairParams.json b/Generators/share/egconfig/ScalerPairParams.json index 61434bfa2462e..57cdac421d3f6 100644 --- a/Generators/share/egconfig/ScalerPairParams.json +++ b/Generators/share/egconfig/ScalerPairParams.json @@ -1,34 +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 - ] - } + "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/src/Generator.cxx b/Generators/src/Generator.cxx index 9c16c0dfb7e92..ce49254799587 100644 --- a/Generators/src/Generator.cxx +++ b/Generators/src/Generator.cxx @@ -170,7 +170,7 @@ bool Generator::initLoopersGen() try { // Create the TPC loopers generator with the provided parameters mLoopersGen = std::make_unique(model_pairs, model_compton, poisson, gauss, scaler_pair, scaler_compton); - const auto &intrate = loopersParam.intrate; + 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) { @@ -209,7 +209,7 @@ Bool_t Generator::finalizeEvent() { #ifdef GENERATORS_WITH_TPCLOOPERS - if(mAddTPCLoopers) { + if (mAddTPCLoopers) { if (!mLoopersGen) { LOG(error) << "Loopers generator not initialized"; return kFALSE; @@ -268,7 +268,7 @@ Bool_t } /** Event finalization**/ - if(!finalizeEvent()) { + if (!finalizeEvent()) { LOG(error) << "ReadEvent failed in finalizeEvent"; return kFALSE; } diff --git a/Generators/src/TPCLoopers.cxx b/Generators/src/TPCLoopers.cxx index ac1123b8d0bbd..258b6cce07b5b 100644 --- a/Generators/src/TPCLoopers.cxx +++ b/Generators/src/TPCLoopers.cxx @@ -6,7 +6,7 @@ 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) +void Scaler::load(const std::string& filename) { std::ifstream file(filename); if (!file.is_open()) { @@ -27,76 +27,73 @@ void Scaler::load(const std::string &filename) 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 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)]); - } + 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; + return output; } -std::vector Scaler::jsonArrayToVector(const rapidjson::Value &jsonArray) +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; + 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{}) + : 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); + // 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]); - } + 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; + return output; } namespace o2 @@ -105,79 +102,67 @@ 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) + 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 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; - } + } + // 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); + } + 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() @@ -352,17 +337,16 @@ void GenTPCLoopers::SetNLoopers(unsigned int& nsig_pair, unsigned int& nsig_comp void GenTPCLoopers::SetMultiplier(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]; - } + // 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, const Int_t& number = -1, const Int_t& nloopers_orbit = -1) @@ -421,7 +405,7 @@ void GenTPCLoopers::setFractionPairs(float& fractionPairs) LOG(info) << "Pairs fraction set to: " << mLoopsFractionPairs; } -void GenTPCLoopers::SetRate(const std::string &rateFile, const bool &isPbPb = true, const int &intRate = 50000) +void GenTPCLoopers::SetRate(const std::string& rateFile, const bool& isPbPb = true, const int& intRate = 50000) { // Checking if the rate file exists and is not empty TFile rate_file(rateFile.c_str(), "READ"); From 6837bccb95a137dd24d42d44fd4db5579ad3dbf9 Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Tue, 25 Nov 2025 08:54:19 +0100 Subject: [PATCH 100/701] Add copyright headers --- Generators/include/TPCLoopers.h | 13 +++++++++++++ Generators/src/TPCLoopers.cxx | 13 +++++++++++++ 2 files changed, 26 insertions(+) diff --git a/Generators/include/TPCLoopers.h b/Generators/include/TPCLoopers.h index 9addcf844e09d..57d178667b497 100644 --- a/Generators/include/TPCLoopers.h +++ b/Generators/include/TPCLoopers.h @@ -1,3 +1,16 @@ +// 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_ diff --git a/Generators/src/TPCLoopers.cxx b/Generators/src/TPCLoopers.cxx index 258b6cce07b5b..8dff795de40a3 100644 --- a/Generators/src/TPCLoopers.cxx +++ b/Generators/src/TPCLoopers.cxx @@ -1,3 +1,16 @@ +// 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" // Static Ort::Env instance for multiple onnx model loading From 1989aed2c6064aaec4d11dfaba2a08d101fed386 Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Tue, 25 Nov 2025 12:55:13 +0100 Subject: [PATCH 101/701] Corrected include folder --- Generators/CMakeLists.txt | 19 ++--- Generators/include/Generators/Generator.h | 5 +- .../include/{ => Generators}/TPCLoopers.h | 44 ++++++----- .../include/Generators/TPCLoopersParam.h | 35 ++++---- Generators/share/TPCLoopers/README.md | 79 +++++++++++++++++++ .../ScalerComptonParams.json | 0 .../ScalerPairParams.json | 0 .../gaussian_params.csv | 0 .../poisson_params.csv | 0 Generators/src/Generator.cxx | 28 ++++--- Generators/src/TPCLoopers.cxx | 40 +++++++--- 11 files changed, 172 insertions(+), 78 deletions(-) rename Generators/include/{ => Generators}/TPCLoopers.h (71%) create mode 100644 Generators/share/TPCLoopers/README.md rename Generators/share/{egconfig => TPCLoopers}/ScalerComptonParams.json (100%) rename Generators/share/{egconfig => TPCLoopers}/ScalerPairParams.json (100%) rename Generators/share/{egconfig => TPCLoopers}/gaussian_params.csv (100%) rename Generators/share/{egconfig => TPCLoopers}/poisson_params.csv (100%) diff --git a/Generators/CMakeLists.txt b/Generators/CMakeLists.txt index f1921b8d8d72a..287536ff118f7 100644 --- a/Generators/CMakeLists.txt +++ b/Generators/CMakeLists.txt @@ -41,8 +41,8 @@ o2_add_library(Generators src/GeneratorTParticleParam.cxx src/GeneratorService.cxx src/FlowMapper.cxx - $<$:src/TPCLoopers.cxx> - $<$:src/TPCLoopersParam.cxx> + src/TPCLoopers.cxx + src/TPCLoopersParam.cxx $<$:src/GeneratorPythia8.cxx> $<$:src/DecayerPythia8.cxx> $<$:src/GeneratorPythia8Param.cxx> @@ -55,7 +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> + onnxruntime::onnxruntime TARGETVARNAME targetName) if(pythia_FOUND) @@ -66,9 +66,7 @@ if(HepMC3_FOUND) target_compile_definitions(${targetName} PUBLIC GENERATORS_WITH_HEPMC3) endif() -if(onnxruntime_FOUND) - target_compile_definitions(${targetName} PUBLIC GENERATORS_WITH_TPCLOOPERS) -endif() +target_compile_definitions(${targetName} PUBLIC GENERATORS_WITH_TPCLOOPERS) set(headers include/Generators/Generator.h @@ -95,11 +93,9 @@ set(headers include/Generators/FlowMapper.h ) -if(onnxruntime_FOUND) - list(APPEND headers - include/Generators/TPCLoopers.h - include/Generators/TPCLoopersParam.h) -endif() +list(APPEND headers + include/Generators/TPCLoopers.h + include/Generators/TPCLoopersParam.h) if(pythia_FOUND) list(APPEND headers @@ -171,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 5a4921e036ca3..3484601aa42bb 100644 --- a/Generators/include/Generators/Generator.h +++ b/Generators/include/Generators/Generator.h @@ -17,7 +17,6 @@ #include "FairGenerator.h" #include "TParticle.h" #include "Generators/Trigger.h" -#include "CCDB/BasicCCDBManager.h" #ifdef GENERATORS_WITH_TPCLOOPERS #include "Generators/TPCLoopers.h" #include "Generators/TPCLoopersParam.h" @@ -172,8 +171,8 @@ class Generator : public FairGenerator #ifdef GENERATORS_WITH_TPCLOOPERS // Loopers generator instance - std::unique_ptr mLoopersGen = nullptr; - bool initLoopersGen(); + std::unique_ptr mTPCLoopersGen = nullptr; + bool initTPCLoopersGen(); #endif ClassDefOverride(Generator, 2); diff --git a/Generators/include/TPCLoopers.h b/Generators/include/Generators/TPCLoopers.h similarity index 71% rename from Generators/include/TPCLoopers.h rename to Generators/include/Generators/TPCLoopers.h index 57d178667b497..6a1d3ef262e22 100644 --- a/Generators/include/TPCLoopers.h +++ b/Generators/include/Generators/TPCLoopers.h @@ -17,21 +17,11 @@ #ifdef GENERATORS_WITH_TPCLOOPERS #include #endif -#include #include -#include #include -#include "CCDB/CCDBTimeStampUtils.h" -#include "CCDB/CcdbApi.h" -#include "DetectorsRaw/HBFUtils.h" #include "TRandom3.h" -#include "TDatabasePDG.h" #include -#include -#include "SimulationDataFormat/MCGenProperties.h" #include "TParticle.h" -#include "TF1.h" -#include #ifdef GENERATORS_WITH_TPCLOOPERS // Static Ort::Env instance for multiple onnx model loading @@ -39,6 +29,8 @@ 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; @@ -74,6 +66,20 @@ 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: @@ -83,7 +89,7 @@ class GenTPCLoopers Bool_t generateEvent(); - Bool_t generateEvent(double& time_limit); + Bool_t generateEvent(double time_limit); std::vector importParticles(); @@ -91,17 +97,17 @@ class GenTPCLoopers unsigned int GaussianElectrons(); - void SetNLoopers(unsigned int& nsig_pair, unsigned int& nsig_compton); + void SetNLoopers(unsigned int nsig_pair, unsigned int nsig_compton); - void SetMultiplier(std::array& mult); + void SetMultiplier(const std::array& mult); - void setFlatGas(Bool_t& flat, const Int_t& number, const Int_t& nloopers_orbit); + void setFlatGas(Bool_t flat, Int_t number = -1, Int_t nloopers_orbit = -1); - void setFractionPairs(float& fractionPairs); + void setFractionPairs(float fractionPairs); - void SetRate(const std::string& rateFile, const bool& isPbPb, const int& intRate); + void SetRate(const std::string& rateFile, bool isPbPb, int intRate = 50000); - void SetAdjust(const float& adjust); + void SetAdjust(float adjust = 0.f); unsigned int getNLoopers() const { return (mNLoopersPairs + mNLoopersCompton); } @@ -121,10 +127,6 @@ class GenTPCLoopers bool mGaussSet = false; // Random number generator TRandom3 mRandGen; - // Masses of the electrons and positrons - TDatabasePDG* mPDG = TDatabasePDG::Instance(); - double mMass_e = mPDG->GetParticle(11)->Mass(); - double mMass_p = mPDG->GetParticle(-11)->Mass(); 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 diff --git a/Generators/include/Generators/TPCLoopersParam.h b/Generators/include/Generators/TPCLoopersParam.h index 49c8e5f5927b6..87e4510d6e617 100644 --- a/Generators/include/Generators/TPCLoopersParam.h +++ b/Generators/include/Generators/TPCLoopersParam.h @@ -24,26 +24,27 @@ namespace eventgen /** ** a parameter class/struct to keep the settings of - ** the tpc loopers event-generator and + ** 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 - 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/egconfig/poisson_params.csv"; // file with Poissonian parameters - std::string gauss = "${O2_ROOT}/share/Generators/egconfig/gaussian_params.csv"; // file with Gaussian parameters - std::string scaler_pair = "${O2_ROOT}/share/Generators/egconfig/ScalerPairParams.json"; // file with scaler parameters for e+e- pair production - std::string scaler_compton = "${O2_ROOT}/share/Generators/egconfig/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)] + 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"); }; 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/egconfig/ScalerComptonParams.json b/Generators/share/TPCLoopers/ScalerComptonParams.json similarity index 100% rename from Generators/share/egconfig/ScalerComptonParams.json rename to Generators/share/TPCLoopers/ScalerComptonParams.json diff --git a/Generators/share/egconfig/ScalerPairParams.json b/Generators/share/TPCLoopers/ScalerPairParams.json similarity index 100% rename from Generators/share/egconfig/ScalerPairParams.json rename to Generators/share/TPCLoopers/ScalerPairParams.json diff --git a/Generators/share/egconfig/gaussian_params.csv b/Generators/share/TPCLoopers/gaussian_params.csv similarity index 100% rename from Generators/share/egconfig/gaussian_params.csv rename to Generators/share/TPCLoopers/gaussian_params.csv diff --git a/Generators/share/egconfig/poisson_params.csv b/Generators/share/TPCLoopers/poisson_params.csv similarity index 100% rename from Generators/share/egconfig/poisson_params.csv rename to Generators/share/TPCLoopers/poisson_params.csv diff --git a/Generators/src/Generator.cxx b/Generators/src/Generator.cxx index ce49254799587..465a8ffb7ee22 100644 --- a/Generators/src/Generator.cxx +++ b/Generators/src/Generator.cxx @@ -25,6 +25,8 @@ #include "TParticle.h" #include "TSystem.h" #include "TGrid.h" +#include "CCDB/BasicCCDBManager.h" +#include namespace o2 { @@ -50,7 +52,7 @@ Generator::Generator() : FairGenerator("ALICEo2", "ALICEo2 Generator"), if (transport) { bool tpcActive = (std::find(simConfig.getReadoutDetectors().begin(), simConfig.getReadoutDetectors().end(), "TPC") != simConfig.getReadoutDetectors().end()); if (tpcActive) { - if (initLoopersGen()) { + if (initTPCLoopersGen()) { mAddTPCLoopers = kTRUE; } } else { @@ -79,7 +81,7 @@ Generator::Generator(const Char_t* name, const Char_t* title) : FairGenerator(na if (transport) { bool tpcActive = (std::find(simConfig.getReadoutDetectors().begin(), simConfig.getReadoutDetectors().end(), "TPC") != simConfig.getReadoutDetectors().end()); if (tpcActive) { - if (initLoopersGen()) { + if (initTPCLoopersGen()) { mAddTPCLoopers = kTRUE; } } else { @@ -94,7 +96,7 @@ Generator::Generator(const Char_t* name, const Char_t* title) : FairGenerator(na /*****************************************************************/ #ifdef GENERATORS_WITH_TPCLOOPERS -bool Generator::initLoopersGen() +bool Generator::initTPCLoopersGen() { // Expand all environment paths const auto& loopersParam = o2::eventgen::GenTPCLoopersParam::Instance(); @@ -169,24 +171,24 @@ bool Generator::initLoopersGen() nclxrate = isAlien[2] || isCCDB[2] ? local_names[2] : nclxrate; try { // Create the TPC loopers generator with the provided parameters - mLoopersGen = std::make_unique(model_pairs, model_compton, poisson, gauss, scaler_pair, scaler_compton); + mTPCLoopersGen = std::make_unique(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) { - mLoopersGen->SetRate(nclxrate, (colsys == "PbPb") ? true : false, intrate); - mLoopersGen->SetAdjust(loopersParam.adjust_flatgas); + 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 - mLoopersGen->SetNLoopers(nLoopersPairs, nLoopersCompton); - mLoopersGen->SetMultiplier(multiplier); + 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(); - mLoopersGen.reset(); + mTPCLoopersGen.reset(); } return kTRUE; } @@ -210,21 +212,21 @@ Bool_t { #ifdef GENERATORS_WITH_TPCLOOPERS if (mAddTPCLoopers) { - if (!mLoopersGen) { + if (!mTPCLoopersGen) { LOG(error) << "Loopers generator not initialized"; return kFALSE; } // Generate loopers using the initialized TPC loopers generator - if (!mLoopersGen->generateEvent()) { + if (!mTPCLoopersGen->generateEvent()) { LOG(error) << "Failed to generate loopers event"; return kFALSE; } - if (mLoopersGen->getNLoopers() == 0) { + if (mTPCLoopersGen->getNLoopers() == 0) { LOG(warning) << "No loopers generated for this event"; return kTRUE; } - const auto& looperParticles = mLoopersGen->importParticles(); + const auto& looperParticles = mTPCLoopersGen->importParticles(); if (looperParticles.empty()) { LOG(error) << "Failed to import loopers particles"; return kFALSE; diff --git a/Generators/src/TPCLoopers.cxx b/Generators/src/TPCLoopers.cxx index 8dff795de40a3..6e5af7c0c84d8 100644 --- a/Generators/src/TPCLoopers.cxx +++ b/Generators/src/TPCLoopers.cxx @@ -12,6 +12,16 @@ /// \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"); @@ -46,10 +56,11 @@ 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) + if (i < input.size() - 2) { output.push_back(input[i] * (normal_max[i] - normal_min[i]) + normal_min[i]); - else + } else { output.push_back(input[i] * outlier_scale[i - (input.size() - 2)] + outlier_center[i - (input.size() - 2)]); + } } return output; @@ -80,8 +91,9 @@ std::vector ONNXGenerator::generate_sample() // Generate a latent vector (z) std::vector z(100); - for (auto& v : z) + for (auto& v : z) { v = rand_gen.Gaus(0.0, 1.0); + } // Prepare input tensor std::vector input_shape = {1, 100}; @@ -227,7 +239,7 @@ Bool_t GenTPCLoopers::generateEvent() return true; } -Bool_t GenTPCLoopers::generateEvent(double& time_limit) +Bool_t GenTPCLoopers::generateEvent(double time_limit) { LOG(info) << "Time constraint for loopers: " << time_limit << " ns"; // Generate pairs @@ -253,6 +265,8 @@ Bool_t GenTPCLoopers::generateEvent(double& time_limit) 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; @@ -268,8 +282,8 @@ std::vector GenTPCLoopers::importParticles() 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 + mMass_e * mMass_e); - p_etot = TMath::Sqrt(px_p * px_p + py_p * py_p + pz_p * pz_p + mMass_p * mMass_p); + 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); @@ -295,7 +309,7 @@ std::vector GenTPCLoopers::importParticles() vy = compton[4]; vz = compton[5]; time = compton[6]; - etot = TMath::Sqrt(px * px + py * py + pz * pz + mMass_e * mMass_e); + 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); @@ -329,7 +343,7 @@ unsigned int GenTPCLoopers::GaussianElectrons() return gaussValue; } -void GenTPCLoopers::SetNLoopers(unsigned int& nsig_pair, unsigned int& nsig_compton) +void GenTPCLoopers::SetNLoopers(unsigned int nsig_pair, unsigned int nsig_compton) { if (mFlatGas) { mNLoopersPairs = nsig_pair; @@ -348,7 +362,7 @@ void GenTPCLoopers::SetNLoopers(unsigned int& nsig_pair, unsigned int& nsig_comp } } -void GenTPCLoopers::SetMultiplier(std::array& mult) +void GenTPCLoopers::SetMultiplier(const std::array& mult) { // Multipliers will work only if the poissonian and gaussian parameters are set // otherwise they will be ignored @@ -362,7 +376,7 @@ void GenTPCLoopers::SetMultiplier(std::array& mult) } } -void GenTPCLoopers::setFlatGas(Bool_t& flat, const Int_t& number = -1, const Int_t& nloopers_orbit = -1) +void GenTPCLoopers::setFlatGas(Bool_t flat, Int_t number, Int_t nloopers_orbit) { mFlatGas = flat; if (mFlatGas) { @@ -408,7 +422,7 @@ void GenTPCLoopers::setFlatGas(Bool_t& flat, const Int_t& number = -1, const Int LOG(info) << "Flat gas loopers: " << (mFlatGas ? "ON" : "OFF") << ", Reference loopers number per " << (mFlatGasOrbit ? "orbit " : "event ") << mFlatGasNumber; } -void GenTPCLoopers::setFractionPairs(float& fractionPairs) +void GenTPCLoopers::setFractionPairs(float fractionPairs) { if (fractionPairs < 0 || fractionPairs > 1) { LOG(fatal) << "Error: Loops fraction for pairs must be in the range [0, 1]."; @@ -418,7 +432,7 @@ void GenTPCLoopers::setFractionPairs(float& fractionPairs) LOG(info) << "Pairs fraction set to: " << mLoopsFractionPairs; } -void GenTPCLoopers::SetRate(const std::string& rateFile, const bool& isPbPb = true, const int& intRate = 50000) +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"); @@ -459,7 +473,7 @@ void GenTPCLoopers::SetRate(const std::string& rateFile, const bool& isPbPb = tr } } -void GenTPCLoopers::SetAdjust(const float& adjust = 0.f) +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 << "%"; From 2a9acbed7eb2a7047b910122e6b8c8373e5a7d9b Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Mon, 5 Jan 2026 10:21:41 +0100 Subject: [PATCH 102/701] Revert "Corrected include folder" This reverts commit 1989aed2c6064aaec4d11dfaba2a08d101fed386. --- Generators/CMakeLists.txt | 19 +++-- Generators/include/Generators/Generator.h | 5 +- .../include/Generators/TPCLoopersParam.h | 35 ++++---- .../include/{Generators => }/TPCLoopers.h | 44 +++++------ Generators/share/TPCLoopers/README.md | 79 ------------------- .../ScalerComptonParams.json | 0 .../ScalerPairParams.json | 0 .../gaussian_params.csv | 0 .../poisson_params.csv | 0 Generators/src/Generator.cxx | 28 +++---- Generators/src/TPCLoopers.cxx | 40 +++------- 11 files changed, 78 insertions(+), 172 deletions(-) rename Generators/include/{Generators => }/TPCLoopers.h (71%) delete mode 100644 Generators/share/TPCLoopers/README.md rename Generators/share/{TPCLoopers => egconfig}/ScalerComptonParams.json (100%) rename Generators/share/{TPCLoopers => egconfig}/ScalerPairParams.json (100%) rename Generators/share/{TPCLoopers => egconfig}/gaussian_params.csv (100%) rename Generators/share/{TPCLoopers => egconfig}/poisson_params.csv (100%) diff --git a/Generators/CMakeLists.txt b/Generators/CMakeLists.txt index 287536ff118f7..f1921b8d8d72a 100644 --- a/Generators/CMakeLists.txt +++ b/Generators/CMakeLists.txt @@ -41,8 +41,8 @@ o2_add_library(Generators src/GeneratorTParticleParam.cxx src/GeneratorService.cxx src/FlowMapper.cxx - src/TPCLoopers.cxx - src/TPCLoopersParam.cxx + $<$:src/TPCLoopers.cxx> + $<$:src/TPCLoopersParam.cxx> $<$:src/GeneratorPythia8.cxx> $<$:src/DecayerPythia8.cxx> $<$:src/GeneratorPythia8Param.cxx> @@ -55,7 +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 + $<$:onnxruntime::onnxruntime> TARGETVARNAME targetName) if(pythia_FOUND) @@ -66,7 +66,9 @@ if(HepMC3_FOUND) target_compile_definitions(${targetName} PUBLIC GENERATORS_WITH_HEPMC3) endif() -target_compile_definitions(${targetName} PUBLIC GENERATORS_WITH_TPCLOOPERS) +if(onnxruntime_FOUND) + target_compile_definitions(${targetName} PUBLIC GENERATORS_WITH_TPCLOOPERS) +endif() set(headers include/Generators/Generator.h @@ -93,9 +95,11 @@ set(headers include/Generators/FlowMapper.h ) -list(APPEND headers - include/Generators/TPCLoopers.h - include/Generators/TPCLoopersParam.h) +if(onnxruntime_FOUND) + list(APPEND headers + include/Generators/TPCLoopers.h + include/Generators/TPCLoopersParam.h) +endif() if(pythia_FOUND) list(APPEND headers @@ -167,5 +171,4 @@ 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 3484601aa42bb..5a4921e036ca3 100644 --- a/Generators/include/Generators/Generator.h +++ b/Generators/include/Generators/Generator.h @@ -17,6 +17,7 @@ #include "FairGenerator.h" #include "TParticle.h" #include "Generators/Trigger.h" +#include "CCDB/BasicCCDBManager.h" #ifdef GENERATORS_WITH_TPCLOOPERS #include "Generators/TPCLoopers.h" #include "Generators/TPCLoopersParam.h" @@ -171,8 +172,8 @@ class Generator : public FairGenerator #ifdef GENERATORS_WITH_TPCLOOPERS // Loopers generator instance - std::unique_ptr mTPCLoopersGen = nullptr; - bool initTPCLoopersGen(); + std::unique_ptr mLoopersGen = nullptr; + bool initLoopersGen(); #endif ClassDefOverride(Generator, 2); diff --git a/Generators/include/Generators/TPCLoopersParam.h b/Generators/include/Generators/TPCLoopersParam.h index 87e4510d6e617..49c8e5f5927b6 100644 --- a/Generators/include/Generators/TPCLoopersParam.h +++ b/Generators/include/Generators/TPCLoopersParam.h @@ -24,27 +24,26 @@ namespace eventgen /** ** a parameter class/struct to keep the settings of - ** the TPC loopers event-generator and + ** 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)] + bool loopersVeto = false; // if true, no loopers are generated + 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/egconfig/poisson_params.csv"; // file with Poissonian parameters + std::string gauss = "${O2_ROOT}/share/Generators/egconfig/gaussian_params.csv"; // file with Gaussian parameters + std::string scaler_pair = "${O2_ROOT}/share/Generators/egconfig/ScalerPairParams.json"; // file with scaler parameters for e+e- pair production + std::string scaler_compton = "${O2_ROOT}/share/Generators/egconfig/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"); }; diff --git a/Generators/include/Generators/TPCLoopers.h b/Generators/include/TPCLoopers.h similarity index 71% rename from Generators/include/Generators/TPCLoopers.h rename to Generators/include/TPCLoopers.h index 6a1d3ef262e22..57d178667b497 100644 --- a/Generators/include/Generators/TPCLoopers.h +++ b/Generators/include/TPCLoopers.h @@ -17,11 +17,21 @@ #ifdef GENERATORS_WITH_TPCLOOPERS #include #endif +#include #include +#include #include +#include "CCDB/CCDBTimeStampUtils.h" +#include "CCDB/CcdbApi.h" +#include "DetectorsRaw/HBFUtils.h" #include "TRandom3.h" +#include "TDatabasePDG.h" #include +#include +#include "SimulationDataFormat/MCGenProperties.h" #include "TParticle.h" +#include "TF1.h" +#include #ifdef GENERATORS_WITH_TPCLOOPERS // Static Ort::Env instance for multiple onnx model loading @@ -29,8 +39,6 @@ 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; @@ -66,20 +74,6 @@ 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: @@ -89,7 +83,7 @@ class GenTPCLoopers Bool_t generateEvent(); - Bool_t generateEvent(double time_limit); + Bool_t generateEvent(double& time_limit); std::vector importParticles(); @@ -97,17 +91,17 @@ class GenTPCLoopers unsigned int GaussianElectrons(); - void SetNLoopers(unsigned int nsig_pair, unsigned int nsig_compton); + void SetNLoopers(unsigned int& nsig_pair, unsigned int& nsig_compton); - void SetMultiplier(const std::array& mult); + void SetMultiplier(std::array& mult); - void setFlatGas(Bool_t flat, Int_t number = -1, Int_t nloopers_orbit = -1); + void setFlatGas(Bool_t& flat, const Int_t& number, const Int_t& nloopers_orbit); - void setFractionPairs(float fractionPairs); + void setFractionPairs(float& fractionPairs); - void SetRate(const std::string& rateFile, bool isPbPb, int intRate = 50000); + void SetRate(const std::string& rateFile, const bool& isPbPb, const int& intRate); - void SetAdjust(float adjust = 0.f); + void SetAdjust(const float& adjust); unsigned int getNLoopers() const { return (mNLoopersPairs + mNLoopersCompton); } @@ -127,6 +121,10 @@ class GenTPCLoopers bool mGaussSet = false; // Random number generator TRandom3 mRandGen; + // Masses of the electrons and positrons + TDatabasePDG* mPDG = TDatabasePDG::Instance(); + double mMass_e = mPDG->GetParticle(11)->Mass(); + double mMass_p = mPDG->GetParticle(-11)->Mass(); 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 diff --git a/Generators/share/TPCLoopers/README.md b/Generators/share/TPCLoopers/README.md deleted file mode 100644 index 0e0ac858b8809..0000000000000 --- a/Generators/share/TPCLoopers/README.md +++ /dev/null @@ -1,79 +0,0 @@ -# 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/egconfig/ScalerComptonParams.json similarity index 100% rename from Generators/share/TPCLoopers/ScalerComptonParams.json rename to Generators/share/egconfig/ScalerComptonParams.json diff --git a/Generators/share/TPCLoopers/ScalerPairParams.json b/Generators/share/egconfig/ScalerPairParams.json similarity index 100% rename from Generators/share/TPCLoopers/ScalerPairParams.json rename to Generators/share/egconfig/ScalerPairParams.json diff --git a/Generators/share/TPCLoopers/gaussian_params.csv b/Generators/share/egconfig/gaussian_params.csv similarity index 100% rename from Generators/share/TPCLoopers/gaussian_params.csv rename to Generators/share/egconfig/gaussian_params.csv diff --git a/Generators/share/TPCLoopers/poisson_params.csv b/Generators/share/egconfig/poisson_params.csv similarity index 100% rename from Generators/share/TPCLoopers/poisson_params.csv rename to Generators/share/egconfig/poisson_params.csv diff --git a/Generators/src/Generator.cxx b/Generators/src/Generator.cxx index 465a8ffb7ee22..ce49254799587 100644 --- a/Generators/src/Generator.cxx +++ b/Generators/src/Generator.cxx @@ -25,8 +25,6 @@ #include "TParticle.h" #include "TSystem.h" #include "TGrid.h" -#include "CCDB/BasicCCDBManager.h" -#include namespace o2 { @@ -52,7 +50,7 @@ Generator::Generator() : FairGenerator("ALICEo2", "ALICEo2 Generator"), if (transport) { bool tpcActive = (std::find(simConfig.getReadoutDetectors().begin(), simConfig.getReadoutDetectors().end(), "TPC") != simConfig.getReadoutDetectors().end()); if (tpcActive) { - if (initTPCLoopersGen()) { + if (initLoopersGen()) { mAddTPCLoopers = kTRUE; } } else { @@ -81,7 +79,7 @@ Generator::Generator(const Char_t* name, const Char_t* title) : FairGenerator(na if (transport) { bool tpcActive = (std::find(simConfig.getReadoutDetectors().begin(), simConfig.getReadoutDetectors().end(), "TPC") != simConfig.getReadoutDetectors().end()); if (tpcActive) { - if (initTPCLoopersGen()) { + if (initLoopersGen()) { mAddTPCLoopers = kTRUE; } } else { @@ -96,7 +94,7 @@ Generator::Generator(const Char_t* name, const Char_t* title) : FairGenerator(na /*****************************************************************/ #ifdef GENERATORS_WITH_TPCLOOPERS -bool Generator::initTPCLoopersGen() +bool Generator::initLoopersGen() { // Expand all environment paths const auto& loopersParam = o2::eventgen::GenTPCLoopersParam::Instance(); @@ -171,24 +169,24 @@ bool Generator::initTPCLoopersGen() nclxrate = isAlien[2] || isCCDB[2] ? local_names[2] : nclxrate; try { // Create the TPC loopers generator with the provided parameters - mTPCLoopersGen = std::make_unique(model_pairs, model_compton, poisson, gauss, scaler_pair, scaler_compton); + mLoopersGen = std::make_unique(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); + mLoopersGen->SetRate(nclxrate, (colsys == "PbPb") ? true : false, intrate); + mLoopersGen->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); + mLoopersGen->SetNLoopers(nLoopersPairs, nLoopersCompton); + mLoopersGen->SetMultiplier(multiplier); } LOG(info) << "TPC Loopers generator initialized successfully"; } catch (const std::exception& e) { LOG(error) << "Failed to initialize TPC Loopers generator: " << e.what(); - mTPCLoopersGen.reset(); + mLoopersGen.reset(); } return kTRUE; } @@ -212,21 +210,21 @@ Bool_t { #ifdef GENERATORS_WITH_TPCLOOPERS if (mAddTPCLoopers) { - if (!mTPCLoopersGen) { + if (!mLoopersGen) { LOG(error) << "Loopers generator not initialized"; return kFALSE; } // Generate loopers using the initialized TPC loopers generator - if (!mTPCLoopersGen->generateEvent()) { + if (!mLoopersGen->generateEvent()) { LOG(error) << "Failed to generate loopers event"; return kFALSE; } - if (mTPCLoopersGen->getNLoopers() == 0) { + if (mLoopersGen->getNLoopers() == 0) { LOG(warning) << "No loopers generated for this event"; return kTRUE; } - const auto& looperParticles = mTPCLoopersGen->importParticles(); + const auto& looperParticles = mLoopersGen->importParticles(); if (looperParticles.empty()) { LOG(error) << "Failed to import loopers particles"; return kFALSE; diff --git a/Generators/src/TPCLoopers.cxx b/Generators/src/TPCLoopers.cxx index 6e5af7c0c84d8..8dff795de40a3 100644 --- a/Generators/src/TPCLoopers.cxx +++ b/Generators/src/TPCLoopers.cxx @@ -12,16 +12,6 @@ /// \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"); @@ -56,11 +46,10 @@ 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) { + if (i < input.size() - 2) output.push_back(input[i] * (normal_max[i] - normal_min[i]) + normal_min[i]); - } else { + else output.push_back(input[i] * outlier_scale[i - (input.size() - 2)] + outlier_center[i - (input.size() - 2)]); - } } return output; @@ -91,9 +80,8 @@ std::vector ONNXGenerator::generate_sample() // Generate a latent vector (z) std::vector z(100); - for (auto& v : z) { + for (auto& v : z) v = rand_gen.Gaus(0.0, 1.0); - } // Prepare input tensor std::vector input_shape = {1, 100}; @@ -239,7 +227,7 @@ Bool_t GenTPCLoopers::generateEvent() return true; } -Bool_t GenTPCLoopers::generateEvent(double time_limit) +Bool_t GenTPCLoopers::generateEvent(double& time_limit) { LOG(info) << "Time constraint for loopers: " << time_limit << " ns"; // Generate pairs @@ -265,8 +253,6 @@ Bool_t GenTPCLoopers::generateEvent(double time_limit) 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; @@ -282,8 +268,8 @@ std::vector GenTPCLoopers::importParticles() 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); + e_etot = TMath::Sqrt(px_e * px_e + py_e * py_e + pz_e * pz_e + mMass_e * mMass_e); + p_etot = TMath::Sqrt(px_p * px_p + py_p * py_p + pz_p * pz_p + mMass_p * mMass_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); @@ -309,7 +295,7 @@ std::vector GenTPCLoopers::importParticles() vy = compton[4]; vz = compton[5]; time = compton[6]; - etot = TMath::Sqrt(px * px + py * py + pz * pz + mass_e * mass_e); + etot = TMath::Sqrt(px * px + py * py + pz * pz + mMass_e * mMass_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); @@ -343,7 +329,7 @@ unsigned int GenTPCLoopers::GaussianElectrons() return gaussValue; } -void GenTPCLoopers::SetNLoopers(unsigned int nsig_pair, unsigned int nsig_compton) +void GenTPCLoopers::SetNLoopers(unsigned int& nsig_pair, unsigned int& nsig_compton) { if (mFlatGas) { mNLoopersPairs = nsig_pair; @@ -362,7 +348,7 @@ void GenTPCLoopers::SetNLoopers(unsigned int nsig_pair, unsigned int nsig_compto } } -void GenTPCLoopers::SetMultiplier(const std::array& mult) +void GenTPCLoopers::SetMultiplier(std::array& mult) { // Multipliers will work only if the poissonian and gaussian parameters are set // otherwise they will be ignored @@ -376,7 +362,7 @@ void GenTPCLoopers::SetMultiplier(const std::array& mult) } } -void GenTPCLoopers::setFlatGas(Bool_t flat, Int_t number, Int_t nloopers_orbit) +void GenTPCLoopers::setFlatGas(Bool_t& flat, const Int_t& number = -1, const Int_t& nloopers_orbit = -1) { mFlatGas = flat; if (mFlatGas) { @@ -422,7 +408,7 @@ void GenTPCLoopers::setFlatGas(Bool_t flat, Int_t number, Int_t nloopers_orbit) LOG(info) << "Flat gas loopers: " << (mFlatGas ? "ON" : "OFF") << ", Reference loopers number per " << (mFlatGasOrbit ? "orbit " : "event ") << mFlatGasNumber; } -void GenTPCLoopers::setFractionPairs(float fractionPairs) +void GenTPCLoopers::setFractionPairs(float& fractionPairs) { if (fractionPairs < 0 || fractionPairs > 1) { LOG(fatal) << "Error: Loops fraction for pairs must be in the range [0, 1]."; @@ -432,7 +418,7 @@ void GenTPCLoopers::setFractionPairs(float fractionPairs) LOG(info) << "Pairs fraction set to: " << mLoopsFractionPairs; } -void GenTPCLoopers::SetRate(const std::string& rateFile, bool isPbPb = true, int intRate) +void GenTPCLoopers::SetRate(const std::string& rateFile, const bool& isPbPb = true, const int& intRate = 50000) { // Checking if the rate file exists and is not empty TFile rate_file(rateFile.c_str(), "READ"); @@ -473,7 +459,7 @@ void GenTPCLoopers::SetRate(const std::string& rateFile, bool isPbPb = true, int } } -void GenTPCLoopers::SetAdjust(float adjust) +void GenTPCLoopers::SetAdjust(const float& adjust = 0.f) { if (mFlatGas && mFlatGasOrbit && adjust >= -1.f && adjust != 0.f) { LOG(info) << "Adjusting flat gas number per orbit by " << adjust * 100.f << "%"; From 63794a47f05379a1a293c1ace1e64ae392d59175 Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Mon, 5 Jan 2026 10:21:41 +0100 Subject: [PATCH 103/701] Revert "Add copyright headers" This reverts commit 6837bccb95a137dd24d42d44fd4db5579ad3dbf9. --- Generators/include/TPCLoopers.h | 13 ------------- Generators/src/TPCLoopers.cxx | 13 ------------- 2 files changed, 26 deletions(-) diff --git a/Generators/include/TPCLoopers.h b/Generators/include/TPCLoopers.h index 57d178667b497..9addcf844e09d 100644 --- a/Generators/include/TPCLoopers.h +++ b/Generators/include/TPCLoopers.h @@ -1,16 +1,3 @@ -// 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_ diff --git a/Generators/src/TPCLoopers.cxx b/Generators/src/TPCLoopers.cxx index 8dff795de40a3..258b6cce07b5b 100644 --- a/Generators/src/TPCLoopers.cxx +++ b/Generators/src/TPCLoopers.cxx @@ -1,16 +1,3 @@ -// 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" // Static Ort::Env instance for multiple onnx model loading From 7744200e31549fbbcf8078013cbf8eb497803ef3 Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Mon, 5 Jan 2026 10:21:41 +0100 Subject: [PATCH 104/701] Revert "Please consider the following formatting changes" This reverts commit 569255b67223793d520de38f90463bc2f8ca6917. --- Generators/include/Generators/Generator.h | 2 +- .../include/Generators/TPCLoopersParam.h | 24 +- Generators/include/TPCLoopers.h | 155 +++++------ .../share/egconfig/ScalerComptonParams.json | 52 ++-- .../share/egconfig/ScalerPairParams.json | 64 ++--- Generators/src/Generator.cxx | 6 +- Generators/src/TPCLoopers.cxx | 256 ++++++++++-------- 7 files changed, 288 insertions(+), 271 deletions(-) diff --git a/Generators/include/Generators/Generator.h b/Generators/include/Generators/Generator.h index 5a4921e036ca3..67277e20736ce 100644 --- a/Generators/include/Generators/Generator.h +++ b/Generators/include/Generators/Generator.h @@ -161,7 +161,7 @@ class Generator : public FairGenerator 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 + 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) diff --git a/Generators/include/Generators/TPCLoopersParam.h b/Generators/include/Generators/TPCLoopersParam.h index 49c8e5f5927b6..24d905c59c967 100644 --- a/Generators/include/Generators/TPCLoopersParam.h +++ b/Generators/include/Generators/TPCLoopersParam.h @@ -28,22 +28,22 @@ namespace eventgen ** allow the user to modify them **/ struct GenTPCLoopersParam : public o2::conf::ConfigurableParamHelper { - bool loopersVeto = false; // if true, no loopers are generated - 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/egconfig/poisson_params.csv"; // file with Poissonian parameters - std::string gauss = "${O2_ROOT}/share/Generators/egconfig/gaussian_params.csv"; // file with Gaussian parameters - std::string scaler_pair = "${O2_ROOT}/share/Generators/egconfig/ScalerPairParams.json"; // file with scaler parameters for e+e- pair production + bool loopersVeto = false; // if true, no loopers are generated + 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/egconfig/poisson_params.csv"; // file with Poissonian parameters + std::string gauss = "${O2_ROOT}/share/Generators/egconfig/gaussian_params.csv"; // file with Gaussian parameters + std::string scaler_pair = "${O2_ROOT}/share/Generators/egconfig/ScalerPairParams.json"; // file with scaler parameters for e+e- pair production std::string scaler_compton = "${O2_ROOT}/share/Generators/egconfig/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)] + 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"); }; diff --git a/Generators/include/TPCLoopers.h b/Generators/include/TPCLoopers.h index 9addcf844e09d..8a4dc0030aa21 100644 --- a/Generators/include/TPCLoopers.h +++ b/Generators/include/TPCLoopers.h @@ -26,32 +26,33 @@ 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. -struct Scaler { - std::vector normal_min; - std::vector normal_max; - std::vector outlier_center; - std::vector outlier_scale; +struct Scaler +{ + std::vector normal_min; + std::vector normal_max; + std::vector outlier_center; + std::vector outlier_scale; - void load(const std::string& filename); + void load(const std::string &filename); - std::vector inverse_transform(const std::vector& input); + std::vector inverse_transform(const std::vector &input); - private: - std::vector jsonArrayToVector(const rapidjson::Value& jsonArray); +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); +public: + ONNXGenerator(Ort::Env &shared_env, const std::string &model_path); - std::vector generate_sample(); + std::vector generate_sample(); - private: - Ort::Env& env; - Ort::Session session; - TRandom3 rand_gen; +private: + Ort::Env &env; + Ort::Session session; + TRandom3 rand_gen; }; #endif // GENERATORS_WITH_TPCLOOPERS @@ -63,67 +64,67 @@ namespace eventgen #ifdef GENERATORS_WITH_TPCLOOPERS 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(std::array& mult); - - void setFlatGas(Bool_t& flat, const Int_t& number, const Int_t& nloopers_orbit); - - void setFractionPairs(float& fractionPairs); - - void SetRate(const std::string& rateFile, const bool& isPbPb, const int& intRate); - - void SetAdjust(const float& adjust); - - 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; - // Masses of the electrons and positrons - TDatabasePDG* mPDG = TDatabasePDG::Instance(); - double mMass_e = mPDG->GetParticle(11)->Mass(); - double mMass_p = mPDG->GetParticle(-11)->Mass(); - 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 + 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(std::array &mult); + + void setFlatGas(Bool_t& flat, const Int_t& number, const Int_t& nloopers_orbit); + + void setFractionPairs(float &fractionPairs); + + void SetRate(const std::string &rateFile, const bool &isPbPb, const int &intRate); + + void SetAdjust(const float &adjust); + + 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; + // Masses of the electrons and positrons + TDatabasePDG *mPDG = TDatabasePDG::Instance(); + double mMass_e = mPDG->GetParticle(11)->Mass(); + double mMass_p = mPDG->GetParticle(-11)->Mass(); + 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 diff --git a/Generators/share/egconfig/ScalerComptonParams.json b/Generators/share/egconfig/ScalerComptonParams.json index 157647fee2db7..d8e654847f46e 100644 --- a/Generators/share/egconfig/ScalerComptonParams.json +++ b/Generators/share/egconfig/ScalerComptonParams.json @@ -1,28 +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 - ] - } + "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/egconfig/ScalerPairParams.json b/Generators/share/egconfig/ScalerPairParams.json index 57cdac421d3f6..61434bfa2462e 100644 --- a/Generators/share/egconfig/ScalerPairParams.json +++ b/Generators/share/egconfig/ScalerPairParams.json @@ -1,34 +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 - ] - } + "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/src/Generator.cxx b/Generators/src/Generator.cxx index ce49254799587..9c16c0dfb7e92 100644 --- a/Generators/src/Generator.cxx +++ b/Generators/src/Generator.cxx @@ -170,7 +170,7 @@ bool Generator::initLoopersGen() try { // Create the TPC loopers generator with the provided parameters mLoopersGen = std::make_unique(model_pairs, model_compton, poisson, gauss, scaler_pair, scaler_compton); - const auto& intrate = loopersParam.intrate; + 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) { @@ -209,7 +209,7 @@ Bool_t Generator::finalizeEvent() { #ifdef GENERATORS_WITH_TPCLOOPERS - if (mAddTPCLoopers) { + if(mAddTPCLoopers) { if (!mLoopersGen) { LOG(error) << "Loopers generator not initialized"; return kFALSE; @@ -268,7 +268,7 @@ Bool_t } /** Event finalization**/ - if (!finalizeEvent()) { + if(!finalizeEvent()) { LOG(error) << "ReadEvent failed in finalizeEvent"; return kFALSE; } diff --git a/Generators/src/TPCLoopers.cxx b/Generators/src/TPCLoopers.cxx index 258b6cce07b5b..ac1123b8d0bbd 100644 --- a/Generators/src/TPCLoopers.cxx +++ b/Generators/src/TPCLoopers.cxx @@ -6,7 +6,7 @@ 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) +void Scaler::load(const std::string &filename) { std::ifstream file(filename); if (!file.is_open()) { @@ -27,73 +27,76 @@ void Scaler::load(const std::string& filename) 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 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)]); - } + 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; + return output; } -std::vector Scaler::jsonArrayToVector(const rapidjson::Value& jsonArray) +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; + 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{}) +: 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); + // 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]); - } + 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; + return output; } namespace o2 @@ -102,67 +105,79 @@ 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) + 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 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); } - } - // 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; + 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); } - } - 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); + 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() @@ -337,16 +352,17 @@ void GenTPCLoopers::SetNLoopers(unsigned int& nsig_pair, unsigned int& nsig_comp void GenTPCLoopers::SetMultiplier(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]; - } + // 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, const Int_t& number = -1, const Int_t& nloopers_orbit = -1) @@ -405,7 +421,7 @@ void GenTPCLoopers::setFractionPairs(float& fractionPairs) LOG(info) << "Pairs fraction set to: " << mLoopsFractionPairs; } -void GenTPCLoopers::SetRate(const std::string& rateFile, const bool& isPbPb = true, const int& intRate = 50000) +void GenTPCLoopers::SetRate(const std::string &rateFile, const bool &isPbPb = true, const int &intRate = 50000) { // Checking if the rate file exists and is not empty TFile rate_file(rateFile.c_str(), "READ"); From 755d43950918deebb1559ef7cb3480d796b2a428 Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Mon, 5 Jan 2026 10:21:41 +0100 Subject: [PATCH 105/701] Revert "Improved logging + colsys check" This reverts commit e7a790b31d71d01e91a2a40123327febda65905f. --- Generators/src/Generator.cxx | 31 +++++++++++-------------------- Generators/src/TPCLoopers.cxx | 6 ++---- 2 files changed, 13 insertions(+), 24 deletions(-) diff --git a/Generators/src/Generator.cxx b/Generators/src/Generator.cxx index 9c16c0dfb7e92..9e083913c3bc7 100644 --- a/Generators/src/Generator.cxx +++ b/Generators/src/Generator.cxx @@ -50,15 +50,11 @@ Generator::Generator() : FairGenerator("ALICEo2", "ALICEo2 Generator"), if (transport) { bool tpcActive = (std::find(simConfig.getReadoutDetectors().begin(), simConfig.getReadoutDetectors().end(), "TPC") != simConfig.getReadoutDetectors().end()); if (tpcActive) { - if (initLoopersGen()) { + if(initLoopersGen()){ 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 } @@ -82,12 +78,8 @@ Generator::Generator(const Char_t* name, const Char_t* title) : FairGenerator(na if (initLoopersGen()) { 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 } @@ -105,14 +97,8 @@ bool Generator::initLoopersGen() 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; + auto flat_gas = loopersParam.flat_gas; 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!"; @@ -170,12 +156,17 @@ bool Generator::initLoopersGen() try { // Create the TPC loopers generator with the provided parameters mLoopersGen = std::make_unique(model_pairs, model_compton, poisson, gauss, scaler_pair, scaler_compton); - const auto &intrate = loopersParam.intrate; + auto& colsys = loopersParam.colsys; + 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) { - mLoopersGen->SetRate(nclxrate, (colsys == "PbPb") ? true : false, intrate); - mLoopersGen->SetAdjust(loopersParam.adjust_flatgas); + if (colsys != "PbPb" && colsys != "pp") { + LOG(fatal) << "Error: collision system must be either 'PbPb' or 'pp'"; + exit(1); + } else { + mLoopersGen->SetRate(nclxrate, (colsys == "PbPb") ? true : false, intrate); + mLoopersGen->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 diff --git a/Generators/src/TPCLoopers.cxx b/Generators/src/TPCLoopers.cxx index ac1123b8d0bbd..07af5b25f99f9 100644 --- a/Generators/src/TPCLoopers.cxx +++ b/Generators/src/TPCLoopers.cxx @@ -141,7 +141,7 @@ GenTPCLoopers::GenTPCLoopers(std::string model_pairs, std::string model_compton, 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") + if (poisson != "") { std::ifstream poisson_file(poisson); if (!poisson_file.is_open() || poisson_file.peek() == std::ifstream::traits_type::eof()) @@ -157,7 +157,7 @@ GenTPCLoopers::GenTPCLoopers(std::string model_pairs, std::string model_compton, } } // Checking if the gauss file exists and it's not empty - if (gauss != "" && gauss != "None" && gauss != "none") + if (gauss != "") { std::ifstream gauss_file(gauss); if (!gauss_file.is_open() || gauss_file.peek() == std::ifstream::traits_type::eof()) @@ -205,11 +205,9 @@ Bool_t GenTPCLoopers::generateEvent() // 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) { From 208f9eac7a888ee407fa18f3e475901a53d6c571 Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Mon, 5 Jan 2026 10:21:41 +0100 Subject: [PATCH 106/701] Revert "Fixed bug + cleaned code" This reverts commit b48d4ec35cdb9252783a46916898d4bfbac928f3. --- Generators/include/Generators/TPCLoopersParam.h | 4 ++-- Generators/src/Generator.cxx | 4 +++- Generators/src/TPCLoopers.cxx | 4 ++++ 3 files changed, 9 insertions(+), 3 deletions(-) diff --git a/Generators/include/Generators/TPCLoopersParam.h b/Generators/include/Generators/TPCLoopersParam.h index 24d905c59c967..74c3cf4cff0ad 100644 --- a/Generators/include/Generators/TPCLoopersParam.h +++ b/Generators/include/Generators/TPCLoopersParam.h @@ -39,8 +39,8 @@ struct GenTPCLoopersParam : public o2::conf::ConfigurableParamHelper multiplier = {loopersParam.multiplier[0], loopersParam.multiplier[1]}; unsigned int nLoopersPairs = loopersParam.fixedNLoopers[0]; unsigned int nLoopersCompton = loopersParam.fixedNLoopers[1]; @@ -168,7 +170,7 @@ bool Generator::initLoopersGen() mLoopersGen->SetAdjust(loopersParam.adjust_flatgas); } } else { - // Otherwise, Poisson+Gauss sampling or fixed number of loopers per event will be used + // Otherwise, Poisson+Gauss sampling or fixed number of loopers 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 mLoopersGen->SetNLoopers(nLoopersPairs, nLoopersCompton); diff --git a/Generators/src/TPCLoopers.cxx b/Generators/src/TPCLoopers.cxx index 07af5b25f99f9..0fb76fcd8c3a9 100644 --- a/Generators/src/TPCLoopers.cxx +++ b/Generators/src/TPCLoopers.cxx @@ -27,6 +27,10 @@ void Scaler::load(const std::string &filename) normal_max = jsonArrayToVector(doc["normal"]["max"]); outlier_center = jsonArrayToVector(doc["outlier"]["center"]); outlier_scale = jsonArrayToVector(doc["outlier"]["scale"]); + std::vector normal_min; + std::vector normal_max; + std::vector outlier_center; + std::vector outlier_scale; } std::vector Scaler::inverse_transform(const std::vector &input) From 6baae3eaf5599d35a2e24ca3a909d22baf808e89 Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Mon, 5 Jan 2026 10:21:41 +0100 Subject: [PATCH 107/701] Revert "Set automatic interaction rate from collision context" This reverts commit a6f60e12edf933dbeb724b9ba7c51a4c5e49cffc. --- .../include/Generators/TPCLoopersParam.h | 4 ++-- Generators/include/TPCLoopers.h | 3 --- Generators/src/Generator.cxx | 10 +++++----- Generators/src/TPCLoopers.cxx | 19 ++----------------- 4 files changed, 9 insertions(+), 27 deletions(-) diff --git a/Generators/include/Generators/TPCLoopersParam.h b/Generators/include/Generators/TPCLoopersParam.h index 74c3cf4cff0ad..8571013cdec48 100644 --- a/Generators/include/Generators/TPCLoopersParam.h +++ b/Generators/include/Generators/TPCLoopersParam.h @@ -37,9 +37,9 @@ struct GenTPCLoopersParam : public o2::conf::ConfigurableParamHelper mONNX_pair = nullptr; std::unique_ptr mONNX_compton = nullptr; @@ -124,7 +122,6 @@ class GenTPCLoopers 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 diff --git a/Generators/src/Generator.cxx b/Generators/src/Generator.cxx index 18e28e4cc2668..fea1a38f1a146 100644 --- a/Generators/src/Generator.cxx +++ b/Generators/src/Generator.cxx @@ -107,7 +107,7 @@ bool Generator::initLoopersGen() } } const auto& nFlatGasLoopers = loopersParam.nFlatGasLoopers; - const auto& fraction_pairs = loopersParam.fraction_pairs; + auto fraction_pairs = loopersParam.fraction_pairs; std::array multiplier = {loopersParam.multiplier[0], loopersParam.multiplier[1]}; unsigned int nLoopersPairs = loopersParam.fixedNLoopers[0]; unsigned int nLoopersCompton = loopersParam.fixedNLoopers[1]; @@ -166,6 +166,10 @@ bool Generator::initLoopersGen() LOG(fatal) << "Error: collision system must be either 'PbPb' or 'pp'"; exit(1); } else { + if (intrate <= 0) { + LOG(fatal) << "Error: interaction rate must be positive!"; + exit(1); + } mLoopersGen->SetRate(nclxrate, (colsys == "PbPb") ? true : false, intrate); mLoopersGen->SetAdjust(loopersParam.adjust_flatgas); } @@ -213,10 +217,6 @@ Bool_t LOG(error) << "Failed to generate loopers event"; return kFALSE; } - if (mLoopersGen->getNLoopers() == 0) { - LOG(warning) << "No loopers generated for this event"; - return kTRUE; - } const auto& looperParticles = mLoopersGen->importParticles(); if (looperParticles.empty()) { LOG(error) << "Failed to import loopers particles"; diff --git a/Generators/src/TPCLoopers.cxx b/Generators/src/TPCLoopers.cxx index 0fb76fcd8c3a9..b771b53ed33d2 100644 --- a/Generators/src/TPCLoopers.cxx +++ b/Generators/src/TPCLoopers.cxx @@ -437,28 +437,13 @@ void GenTPCLoopers::SetRate(const std::string &rateFile, const bool &isPbPb = tr 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 + auto ref = static_cast(std::floor(fit->Eval(intRate / 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."; + LOG(info) << "Set flat gas number to " << ref << " loopers per orbit using " << fitName << " from " << intRate << " Hz interaction rate."; auto flat = true; setFlatGas(flat, -1, ref); } From c035dd9e71953c8829c7afbdd5a7596061b006c0 Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Mon, 5 Jan 2026 10:21:41 +0100 Subject: [PATCH 108/701] Revert "Implemented rate and collision system dependence (default)" This reverts commit 32c0f318ec9737e7c1718c373d3c9660ddff477c. --- .../include/Generators/TPCLoopersParam.h | 4 -- Generators/include/TPCLoopers.h | 8 +-- Generators/src/Generator.cxx | 28 +++----- Generators/src/TPCLoopers.cxx | 64 +++---------------- 4 files changed, 17 insertions(+), 87 deletions(-) diff --git a/Generators/include/Generators/TPCLoopersParam.h b/Generators/include/Generators/TPCLoopersParam.h index 8571013cdec48..9430f4e05ac6e 100644 --- a/Generators/include/Generators/TPCLoopersParam.h +++ b/Generators/include/Generators/TPCLoopersParam.h @@ -35,15 +35,11 @@ struct GenTPCLoopersParam : public o2::conf::ConfigurableParamHelper #include "SimulationDataFormat/MCGenProperties.h" #include "TParticle.h" -#include "TF1.h" #include #ifdef GENERATORS_WITH_TPCLOOPERS @@ -83,14 +82,10 @@ class GenTPCLoopers void SetMultiplier(std::array &mult); - void setFlatGas(Bool_t& flat, const Int_t& number, const Int_t& nloopers_orbit); + void setFlatGas(Bool_t &flat, const Int_t &number = -1); void setFractionPairs(float &fractionPairs); - void SetRate(const std::string &rateFile, const bool &isPbPb, const int &intRate); - - void SetAdjust(const float &adjust); - private: std::unique_ptr mONNX_pair = nullptr; std::unique_ptr mONNX_compton = nullptr; @@ -116,7 +111,6 @@ class GenTPCLoopers 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 diff --git a/Generators/src/Generator.cxx b/Generators/src/Generator.cxx index fea1a38f1a146..50b11c0c7bb53 100644 --- a/Generators/src/Generator.cxx +++ b/Generators/src/Generator.cxx @@ -92,7 +92,6 @@ bool Generator::initLoopersGen() 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()); @@ -111,10 +110,10 @@ bool Generator::initLoopersGen() 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://")}; + const std::array models = {model_pairs, model_compton}; + const std::array local_names = {"WGANpair.onnx", "WGANcompton.onnx"}; + const std::array isAlien = {models[0].starts_with("alien://"), models[1].starts_with("alien://")}; + const std::array isCCDB = {models[0].starts_with("ccdb://"), models[1].starts_with("ccdb://")}; if (std::any_of(isAlien.begin(), isAlien.end(), [](bool v) { return v; })) { if (!gGrid) { TGrid::Connect("alien://"); @@ -154,25 +153,14 @@ bool Generator::initLoopersGen() } 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 mLoopersGen = std::make_unique(model_pairs, model_compton, poisson, gauss, scaler_pair, scaler_compton); - auto& colsys = loopersParam.colsys; - auto &intrate = loopersParam.intrate; - // Configure the generator with flat gas loopers defined per orbit with clusters/track info + + // Configure the generator with flat gas loopers if enabled (default) if (flat_gas) { - if (colsys != "PbPb" && colsys != "pp") { - LOG(fatal) << "Error: collision system must be either 'PbPb' or 'pp'"; - exit(1); - } else { - if (intrate <= 0) { - LOG(fatal) << "Error: interaction rate must be positive!"; - exit(1); - } - mLoopersGen->SetRate(nclxrate, (colsys == "PbPb") ? true : false, intrate); - mLoopersGen->SetAdjust(loopersParam.adjust_flatgas); - } + mLoopersGen->setFlatGas(flat_gas, nFlatGasLoopers); + mLoopersGen->setFractionPairs(fraction_pairs); } else { // Otherwise, Poisson+Gauss sampling or fixed number of loopers will be used // Multiplier is applied only with distribution sampling diff --git a/Generators/src/TPCLoopers.cxx b/Generators/src/TPCLoopers.cxx index b771b53ed33d2..109461ab71dfa 100644 --- a/Generators/src/TPCLoopers.cxx +++ b/Generators/src/TPCLoopers.cxx @@ -197,8 +197,7 @@ Bool_t GenTPCLoopers::generateEvent() 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)); + nLoopers = mFlatGasNumber * (mTimeLimit / mIntTimeRecMean); nLoopersPairs = static_cast(std::round(nLoopers * mLoopsFractionPairs)); nLoopersCompton = nLoopers - nLoopersPairs; SetNLoopers(nLoopersPairs, nLoopersCompton); @@ -367,34 +366,22 @@ void GenTPCLoopers::SetMultiplier(std::array& mult) } } -void GenTPCLoopers::setFlatGas(Bool_t& flat, const Int_t& number = -1, const Int_t& nloopers_orbit = -1) +void GenTPCLoopers::setFlatGas(Bool_t& flat, const Int_t& number) { mFlatGas = flat; if (mFlatGas) { - if (nloopers_orbit > 0) { - mFlatGasOrbit = true; - mFlatGasNumber = nloopers_orbit; - LOG(info) << "Flat gas loopers will be generated using orbit reference."; + if (number < 0) { + LOG(warn) << "Warning: Number of loopers per event must be non-negative! Switching option off."; + mFlatGas = false; + mFlatGasNumber = -1; } 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) { + mFlatGasNumber = number; 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(); @@ -410,7 +397,7 @@ void GenTPCLoopers::setFlatGas(Bool_t& flat, const Int_t& number = -1, const Int } else { mFlatGasNumber = -1; } - LOG(info) << "Flat gas loopers: " << (mFlatGas ? "ON" : "OFF") << ", Reference loopers number per " << (mFlatGasOrbit ? "orbit " : "event ") << mFlatGasNumber; + LOG(info) << "Flat gas loopers: " << (mFlatGas ? "ON" : "OFF") << ", Reference loopers number per event: " << mFlatGasNumber; } void GenTPCLoopers::setFractionPairs(float& fractionPairs) @@ -423,40 +410,5 @@ void GenTPCLoopers::setFractionPairs(float& fractionPairs) LOG(info) << "Pairs fraction set to: " << mLoopsFractionPairs; } -void GenTPCLoopers::SetRate(const std::string &rateFile, const bool &isPbPb = true, const int &intRate = 50000) -{ - // 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); - } - auto ref = static_cast(std::floor(fit->Eval(intRate / 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 " << intRate << " Hz interaction rate."; - auto flat = true; - setFlatGas(flat, -1, ref); - } -} - -void GenTPCLoopers::SetAdjust(const float& adjust = 0.f) -{ - 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 From fdce0869312a4e2c1a77a82b7c60c68e77bf203e Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Mon, 5 Jan 2026 10:21:41 +0100 Subject: [PATCH 109/701] Revert "Vetoing loopers for FlatGas and \!collisioncontext" This reverts commit 8f8606a66c8499de6df999795d595f3dbab9e5b3. --- Generators/include/Generators/Generator.h | 2 +- Generators/src/Generator.cxx | 21 +++++---------------- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/Generators/include/Generators/Generator.h b/Generators/include/Generators/Generator.h index 67277e20736ce..4b68112517893 100644 --- a/Generators/include/Generators/Generator.h +++ b/Generators/include/Generators/Generator.h @@ -173,7 +173,7 @@ class Generator : public FairGenerator #ifdef GENERATORS_WITH_TPCLOOPERS // Loopers generator instance std::unique_ptr mLoopersGen = nullptr; - bool initLoopersGen(); + void initLoopersGen(); #endif ClassDefOverride(Generator, 2); diff --git a/Generators/src/Generator.cxx b/Generators/src/Generator.cxx index 50b11c0c7bb53..6fc9f378148d3 100644 --- a/Generators/src/Generator.cxx +++ b/Generators/src/Generator.cxx @@ -50,9 +50,8 @@ Generator::Generator() : FairGenerator("ALICEo2", "ALICEo2 Generator"), if (transport) { bool tpcActive = (std::find(simConfig.getReadoutDetectors().begin(), simConfig.getReadoutDetectors().end(), "TPC") != simConfig.getReadoutDetectors().end()); if (tpcActive) { - if(initLoopersGen()){ - mAddTPCLoopers = kTRUE; - } + mAddTPCLoopers = kTRUE; + initLoopersGen(); } } } @@ -75,9 +74,8 @@ Generator::Generator(const Char_t* name, const Char_t* title) : FairGenerator(na if (transport) { bool tpcActive = (std::find(simConfig.getReadoutDetectors().begin(), simConfig.getReadoutDetectors().end(), "TPC") != simConfig.getReadoutDetectors().end()); if (tpcActive) { - if (initLoopersGen()) { - mAddTPCLoopers = kTRUE; - } + mAddTPCLoopers = kTRUE; + initLoopersGen(); } } } @@ -86,7 +84,7 @@ Generator::Generator(const Char_t* name, const Char_t* title) : FairGenerator(na /*****************************************************************/ #ifdef GENERATORS_WITH_TPCLOOPERS -bool Generator::initLoopersGen() +void Generator::initLoopersGen() { // Expand all environment paths const auto& loopersParam = o2::eventgen::GenTPCLoopersParam::Instance(); @@ -97,14 +95,6 @@ bool Generator::initLoopersGen() const auto& poisson = gSystem->ExpandPathName(loopersParam.poisson.c_str()); const auto& gauss = gSystem->ExpandPathName(loopersParam.gauss.c_str()); auto flat_gas = loopersParam.flat_gas; - if (flat_gas) { - 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; - } - } const auto& nFlatGasLoopers = loopersParam.nFlatGasLoopers; auto fraction_pairs = loopersParam.fraction_pairs; std::array multiplier = {loopersParam.multiplier[0], loopersParam.multiplier[1]}; @@ -173,7 +163,6 @@ bool Generator::initLoopersGen() LOG(error) << "Failed to initialize TPC Loopers generator: " << e.what(); mLoopersGen.reset(); } - return kTRUE; } #endif From d1a8157599ca1ce2ba45dc133210908b0f85583b Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Mon, 5 Jan 2026 10:21:41 +0100 Subject: [PATCH 110/701] Revert "Various improvements" This reverts commit 4b6530fbc6a93963de72035d123b6666b2991e32. --- .../SimConfig/include/SimConfig/SimConfig.h | 3 + Common/SimConfig/src/SimConfig.cxx | 2 + Generators/CMakeLists.txt | 4 +- Generators/include/Generators/Generator.h | 11 +- .../include/Generators/TPCLoopersParam.h | 5 +- Generators/include/TPCLoopers.h | 12 +- Generators/src/Generator.cxx | 392 +++++++++--------- Generators/src/GeneratorsLinkDef.h | 2 +- Generators/src/TPCLoopers.cxx | 3 + 9 files changed, 220 insertions(+), 214 deletions(-) diff --git a/Common/SimConfig/include/SimConfig/SimConfig.h b/Common/SimConfig/include/SimConfig/SimConfig.h index be88d9fbd8c33..8642a0e5bc225 100644 --- a/Common/SimConfig/include/SimConfig/SimConfig.h +++ b/Common/SimConfig/include/SimConfig/SimConfig.h @@ -52,6 +52,7 @@ struct SimConfigData { std::vector mActiveModules; // list of active modules std::vector mReadoutDetectors; // list of readout detectors std::string mMCEngine; // chosen VMC engine + bool mNoLoopers = false; // Disable automatic TPC loopers std::string mGenerator; // chosen VMC generator std::string mTrigger; // chosen VMC generator trigger unsigned int mNEvents; // number of events to be simulated @@ -138,6 +139,8 @@ class SimConfig // get selected active detectors std::vector const& getActiveModules() const { return mConfigData.mActiveModules; } std::vector const& getReadoutDetectors() const { return mConfigData.mReadoutDetectors; } + // get loopers veto + bool getLoopersVeto() const { return mConfigData.mNoLoopers; } // static helper functions to determine list of active / readout modules // can also be used from outside diff --git a/Common/SimConfig/src/SimConfig.cxx b/Common/SimConfig/src/SimConfig.cxx index 15879687872d5..5ddc3199e3d4a 100644 --- a/Common/SimConfig/src/SimConfig.cxx +++ b/Common/SimConfig/src/SimConfig.cxx @@ -74,6 +74,7 @@ void SimConfig::initOptions(boost::program_options::options_description& options "run", bpo::value()->default_value(-1), "ALICE run number")( "asservice", bpo::value()->default_value(false), "run in service/server mode")( "noGeant", bpo::bool_switch(), "prohibits any Geant transport/physics (by using tight cuts)")( + "noLoopers", bpo::bool_switch(), "disable automatic TPC loopers")( "forwardKine", bpo::bool_switch(), "forward kinematics on a FairMQ channel")( "noDiscOutput", bpo::bool_switch(), "switch off writing sim results to disc (useful in combination with forwardKine)"); options.add_options()("fromCollContext", bpo::value()->default_value(""), "Use a pregenerated collision context to infer number of events to simulate, how to embedd them, the vertex position etc. Takes precedence of other options such as \"--nEvents\". The format is COLLISIONCONTEXTFILE.root[:SIGNALNAME] where SIGNALNAME is the event part in the context which is relevant."); @@ -297,6 +298,7 @@ bool SimConfig::resetFromParsedMap(boost::program_options::variables_map const& using o2::detectors::DetID; mConfigData.mMCEngine = vm["mcEngine"].as(); mConfigData.mNoGeant = vm["noGeant"].as(); + mConfigData.mNoLoopers = vm["noLoopers"].as(); // Reset modules and detectors as they are anyway re-parsed mConfigData.mReadoutDetectors.clear(); diff --git a/Generators/CMakeLists.txt b/Generators/CMakeLists.txt index f1921b8d8d72a..56fe8b8fc2284 100644 --- a/Generators/CMakeLists.txt +++ b/Generators/CMakeLists.txt @@ -67,7 +67,7 @@ if(HepMC3_FOUND) endif() if(onnxruntime_FOUND) - target_compile_definitions(${targetName} PUBLIC GENERATORS_WITH_TPCLOOPERS) + target_compile_definitions(${targetName} PUBLIC GENERATORS_WITH_ONNXRUNTIME) endif() set(headers @@ -96,7 +96,7 @@ set(headers ) if(onnxruntime_FOUND) - list(APPEND headers + list(APPEND headers include/Generators/TPCLoopers.h include/Generators/TPCLoopersParam.h) endif() diff --git a/Generators/include/Generators/Generator.h b/Generators/include/Generators/Generator.h index 4b68112517893..374d53f324399 100644 --- a/Generators/include/Generators/Generator.h +++ b/Generators/include/Generators/Generator.h @@ -17,8 +17,7 @@ #include "FairGenerator.h" #include "TParticle.h" #include "Generators/Trigger.h" -#include "CCDB/BasicCCDBManager.h" -#ifdef GENERATORS_WITH_TPCLOOPERS +#ifdef GENERATORS_WITH_ONNXRUNTIME #include "Generators/TPCLoopers.h" #include "Generators/TPCLoopersParam.h" #endif @@ -78,7 +77,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 + Bool_t loopers(); // adds loopers to the event in case TPC is used virtual void updateHeader(o2::dataformats::MCEventHeader* eventHeader) {}; Bool_t triggerEvent(); @@ -161,7 +160,7 @@ class Generator : public FairGenerator 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 + Bool_t mAddLoopers = kFALSE; // 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) @@ -170,11 +169,11 @@ class Generator : public FairGenerator // global static information about (upper limit of) number of events to be generated static unsigned int gTotalNEvents; -#ifdef GENERATORS_WITH_TPCLOOPERS +#ifdef GENERATORS_WITH_ONNXRUNTIME // Loopers generator instance std::unique_ptr mLoopersGen = nullptr; - void initLoopersGen(); #endif + void initLoopersGen(); ClassDefOverride(Generator, 2); diff --git a/Generators/include/Generators/TPCLoopersParam.h b/Generators/include/Generators/TPCLoopersParam.h index 9430f4e05ac6e..ceeea201538b2 100644 --- a/Generators/include/Generators/TPCLoopersParam.h +++ b/Generators/include/Generators/TPCLoopersParam.h @@ -28,7 +28,6 @@ namespace eventgen ** allow the user to modify them **/ struct GenTPCLoopersParam : public o2::conf::ConfigurableParamHelper { - bool loopersVeto = false; // if true, no loopers are generated 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/egconfig/poisson_params.csv"; // file with Poissonian parameters @@ -38,8 +37,8 @@ struct GenTPCLoopersParam : public o2::conf::ConfigurableParamHelper multiplier = {1., 1.}; // multiplier for pairs and compton loopers for Poissonian and Gaussian sampling + std::array fixedNLoopers = {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 O2ParamDef(GenTPCLoopersParam, "GenTPCLoopers"); }; diff --git a/Generators/include/TPCLoopers.h b/Generators/include/TPCLoopers.h index 1c1f3585eb3ab..70146a82baf60 100644 --- a/Generators/include/TPCLoopers.h +++ b/Generators/include/TPCLoopers.h @@ -1,7 +1,7 @@ #ifndef ALICEO2_EVENTGEN_TPCLOOPERS_H_ #define ALICEO2_EVENTGEN_TPCLOOPERS_H_ -#ifdef GENERATORS_WITH_TPCLOOPERS +#ifdef GENERATORS_WITH_ONNXRUNTIME #include #endif #include @@ -19,10 +19,12 @@ #include "TParticle.h" #include -#ifdef GENERATORS_WITH_TPCLOOPERS +#ifdef GENERATORS_WITH_ONNXRUNTIME // Static Ort::Env instance for multiple onnx model loading extern Ort::Env global_env; +#endif +#ifdef GENERATORS_WITH_ONNXRUNTIME // This class is responsible for loading the scaler parameters from a JSON file // and applying the inverse transformation to the generated data. struct Scaler @@ -53,14 +55,14 @@ class ONNXGenerator Ort::Session session; TRandom3 rand_gen; }; -#endif // GENERATORS_WITH_TPCLOOPERS +#endif // GENERATORS_WITH_ONNXRUNTIME namespace o2 { namespace eventgen { -#ifdef GENERATORS_WITH_TPCLOOPERS +#ifdef GENERATORS_WITH_ONNXRUNTIME class GenTPCLoopers { public: @@ -117,7 +119,7 @@ class GenTPCLoopers double mTimeEnd = 0.0; // Time limit for the last event float mLoopsFractionPairs = 0.08; // Fraction of loopers from Pairs }; -#endif // GENERATORS_WITH_TPCLOOPERS +#endif // GENERATORS_WITH_ONNXRUNTIME } // namespace eventgen } // namespace o2 diff --git a/Generators/src/Generator.cxx b/Generators/src/Generator.cxx index 6fc9f378148d3..153ef5cd5e35e 100644 --- a/Generators/src/Generator.cxx +++ b/Generators/src/Generator.cxx @@ -42,20 +42,18 @@ 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) { + auto simConfig = o2::conf::SimConfig::Instance(); + auto noLoops = simConfig.getLoopersVeto(); + if (!noLoops) { bool transport = (simConfig.getMCEngine() != "O2TrivialMCEngine"); if (transport) { bool tpcActive = (std::find(simConfig.getReadoutDetectors().begin(), simConfig.getReadoutDetectors().end(), "TPC") != simConfig.getReadoutDetectors().end()); if (tpcActive) { - mAddTPCLoopers = kTRUE; + mAddLoopers = kTRUE; initLoopersGen(); } } } -#endif } /*****************************************************************/ @@ -66,26 +64,25 @@ 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) { + auto simConfig = o2::conf::SimConfig::Instance(); + auto noLoops = simConfig.getLoopersVeto(); + if (!noLoops) { bool transport = (simConfig.getMCEngine() != "O2TrivialMCEngine"); if (transport) { bool tpcActive = (std::find(simConfig.getReadoutDetectors().begin(), simConfig.getReadoutDetectors().end(), "TPC") != simConfig.getReadoutDetectors().end()); if (tpcActive) { - mAddTPCLoopers = kTRUE; + mAddLoopers = kTRUE; initLoopersGen(); } } } -#endif } /*****************************************************************/ -#ifdef GENERATORS_WITH_TPCLOOPERS + void Generator::initLoopersGen() { +#ifdef GENERATORS_WITH_ONNXRUNTIME // Expand all environment paths const auto& loopersParam = o2::eventgen::GenTPCLoopersParam::Instance(); std::string model_pairs = gSystem->ExpandPathName(loopersParam.model_pairs.c_str()); @@ -97,9 +94,8 @@ void Generator::initLoopersGen() auto flat_gas = loopersParam.flat_gas; const auto& nFlatGasLoopers = loopersParam.nFlatGasLoopers; auto fraction_pairs = loopersParam.fraction_pairs; - std::array multiplier = {loopersParam.multiplier[0], loopersParam.multiplier[1]}; - unsigned int nLoopersPairs = loopersParam.fixedNLoopers[0]; - unsigned int nLoopersCompton = loopersParam.fixedNLoopers[1]; + auto multiplier = loopersParam.multiplier; + auto fixedNLoopers = loopersParam.fixedNLoopers; const std::array models = {model_pairs, model_compton}; const std::array local_names = {"WGANpair.onnx", "WGANcompton.onnx"}; const std::array isAlien = {models[0].starts_with("alien://"), models[1].starts_with("alien://")}; @@ -120,10 +116,8 @@ void Generator::initLoopersGen() } } 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(); + o2::ccdb::CcdbApi ccdb_api; + ccdb_api.init("http://alice-ccdb.cern.ch"); for (size_t i = 0; i < models.size(); ++i) { if (isCCDB[i]) { auto model_path = models[i].substr(7); // Remove "ccdb://" @@ -155,7 +149,7 @@ void Generator::initLoopersGen() // Otherwise, Poisson+Gauss sampling or fixed number of loopers 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 - mLoopersGen->SetNLoopers(nLoopersPairs, nLoopersCompton); + mLoopersGen->SetNLoopers(fixedNLoopers[0], fixedNLoopers[1]); mLoopersGen->SetMultiplier(multiplier); } LOG(info) << "TPC Loopers generator initialized successfully"; @@ -163,8 +157,10 @@ void Generator::initLoopersGen() LOG(error) << "Failed to initialize TPC Loopers generator: " << e.what(); mLoopersGen.reset(); } -} +#else + LOG(warn) << "ONNX Runtime support not available, cannot initialize TPC loopers generator"; #endif +} /*****************************************************************/ @@ -180,228 +176,230 @@ Bool_t /*****************************************************************/ Bool_t - Generator::finalizeEvent() + Generator::loopers() { -#ifdef GENERATORS_WITH_TPCLOOPERS - if(mAddTPCLoopers) { - if (!mLoopersGen) { - LOG(error) << "Loopers generator not initialized"; - return kFALSE; - } - - // Generate loopers using the initialized TPC loopers generator - if (!mLoopersGen->generateEvent()) { - LOG(error) << "Failed to generate loopers event"; - return kFALSE; - } - const auto& looperParticles = mLoopersGen->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()); +#ifdef GENERATORS_WITH_ONNXRUNTIME + if (!mLoopersGen) { + LOG(error) << "Loopers generator not initialized"; + return kFALSE; + } - LOG(debug) << "Added " << looperParticles.size() << " looper particles"; + // Generate loopers using the initialized TPC loopers generator + if (!mLoopersGen->generateEvent()) { + LOG(error) << "Failed to generate loopers event"; + return kFALSE; } -#endif + const auto& looperParticles = mLoopersGen->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"; + return kTRUE; +#else + LOG(warn) << "ONNX Runtime support not available, skipping TPC loopers generation"; return kTRUE; +#endif } + /*****************************************************************/ -/*****************************************************************/ + Bool_t + Generator::ReadEvent(FairPrimaryGenerator * primGen) + { + /** read event **/ -Bool_t - Generator::ReadEvent(FairPrimaryGenerator* primGen) -{ - /** read event **/ + /** endless generate-and-trigger loop **/ + while (true) { + mReadEventCounter++; - /** endless generate-and-trigger loop **/ - while (true) { - mReadEventCounter++; + /** clear particle vector **/ + mParticles.clear(); - /** clear particle vector **/ - mParticles.clear(); + /** reset the sub-generator ID **/ + mSubGeneratorId = -1; - /** reset the sub-generator ID **/ - mSubGeneratorId = -1; + /** generate event **/ + if (!generateEvent()) { + LOG(error) << "ReadEvent failed in generateEvent"; + return kFALSE; + } - /** generate event **/ - if (!generateEvent()) { - LOG(error) << "ReadEvent failed in generateEvent"; - return kFALSE; - } + /** import particles **/ + if (!importParticles()) { + LOG(error) << "ReadEvent failed in importParticles"; + return kFALSE; + } - /** import particles **/ - if (!importParticles()) { - LOG(error) << "ReadEvent failed in importParticles"; - return kFALSE; - } + /** Add loopers **/ + if(mAddLoopers){ + if (!loopers()) { + LOG(error) << "ReadEvent failed in loopers"; + 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"; + } - if (mSubGeneratorsIdToDesc.empty() && mSubGeneratorId > -1) { - LOG(fatal) << "ReadEvent failed because no SubGenerator description given"; - } + if (!mSubGeneratorsIdToDesc.empty() && mSubGeneratorId < 0) { + LOG(fatal) << "ReadEvent failed because SubGenerator description given but sub-generator not set"; + } - if (!mSubGeneratorsIdToDesc.empty() && mSubGeneratorId < 0) { - LOG(fatal) << "ReadEvent failed because SubGenerator description given but sub-generator not set"; + /** trigger event **/ + if (triggerEvent()) { + mTriggerOkHook(mParticles, mReadEventCounter); + break; + } else { + mTriggerFalseHook(mParticles, mReadEventCounter); + } } - /** trigger event **/ - if (triggerEvent()) { - mTriggerOkHook(mParticles, mReadEventCounter); - break; - } else { - mTriggerFalseHook(mParticles, mReadEventCounter); + /** add tracks **/ + if (!addTracks(primGen)) { + LOG(error) << "ReadEvent failed in addTracks"; + return kFALSE; } - } - /** add tracks **/ - if (!addTracks(primGen)) { - LOG(error) << "ReadEvent failed in addTracks"; - return kFALSE; - } + /** update header **/ + auto header = primGen->GetEvent(); + auto o2header = dynamic_cast(header); + if (!header) { + LOG(fatal) << "MC event header is not a 'o2::dataformats::MCEventHeader' object"; + return kFALSE; + } + updateHeader(o2header); + updateSubGeneratorInformation(o2header); - /** update header **/ - auto header = primGen->GetEvent(); - auto o2header = dynamic_cast(header); - if (!header) { - LOG(fatal) << "MC event header is not a 'o2::dataformats::MCEventHeader' object"; - return kFALSE; + /** success **/ + return kTRUE; } - updateHeader(o2header); - updateSubGeneratorInformation(o2header); - /** success **/ - return kTRUE; -} + /*****************************************************************/ -/*****************************************************************/ + Bool_t + Generator::addTracks(FairPrimaryGenerator * primGen) + { + /** add tracks **/ -Bool_t - Generator::addTracks(FairPrimaryGenerator* primGen) -{ - /** add tracks **/ + auto o2primGen = dynamic_cast(primGen); + if (!o2primGen) { + LOG(fatal) << "PrimaryGenerator is not a o2::eventgen::PrimaryGenerator"; + return kFALSE; + } - auto o2primGen = dynamic_cast(primGen); - if (!o2primGen) { - LOG(fatal) << "PrimaryGenerator is not a o2::eventgen::PrimaryGenerator"; - return kFALSE; - } + /** loop over particles **/ + for (const auto& particle : mParticles) { + o2primGen->AddTrack(particle.GetPdgCode(), + particle.Px() * mMomentumUnit, + particle.Py() * mMomentumUnit, + particle.Pz() * mMomentumUnit, + particle.Vx() * mPositionUnit, + particle.Vy() * mPositionUnit, + particle.Vz() * mPositionUnit, + particle.GetMother(0), + particle.GetMother(1), + particle.GetDaughter(0), + particle.GetDaughter(1), + particle.TestBit(ParticleStatus::kToBeDone), + particle.Energy() * mEnergyUnit, + particle.T() * mTimeUnit, + particle.GetWeight(), + (TMCProcess)particle.GetUniqueID(), + particle.GetStatusCode()); // generator status information passed as status code field + } - /** loop over particles **/ - for (const auto& particle : mParticles) { - o2primGen->AddTrack(particle.GetPdgCode(), - particle.Px() * mMomentumUnit, - particle.Py() * mMomentumUnit, - particle.Pz() * mMomentumUnit, - particle.Vx() * mPositionUnit, - particle.Vy() * mPositionUnit, - particle.Vz() * mPositionUnit, - particle.GetMother(0), - particle.GetMother(1), - particle.GetDaughter(0), - particle.GetDaughter(1), - particle.TestBit(ParticleStatus::kToBeDone), - particle.Energy() * mEnergyUnit, - particle.T() * mTimeUnit, - particle.GetWeight(), - (TMCProcess)particle.GetUniqueID(), - particle.GetStatusCode()); // generator status information passed as status code field + /** success **/ + return kTRUE; } - /** success **/ - return kTRUE; -} - -/*****************************************************************/ + /*****************************************************************/ -Bool_t - Generator::boostEvent() -{ - /** boost event **/ - - /** success **/ - return kTRUE; -} - -/*****************************************************************/ - -Bool_t - Generator::triggerEvent() -{ - /** trigger event **/ + Bool_t + Generator::boostEvent() + { + /** boost event **/ - /** check trigger presence **/ - if (mTriggers.size() == 0 && mDeepTriggers.size() == 0) { + /** success **/ return kTRUE; } - /** check trigger mode **/ - Bool_t triggered; - if (mTriggerMode == kTriggerOFF) { - return kTRUE; - } else if (mTriggerMode == kTriggerOR) { - triggered = kFALSE; - } else if (mTriggerMode == kTriggerAND) { - triggered = kTRUE; - } else { - return kTRUE; - } + /*****************************************************************/ - /** loop over triggers **/ - for (const auto& trigger : mTriggers) { - auto retval = trigger(mParticles); - if (mTriggerMode == kTriggerOR) { - triggered |= retval; + Bool_t + Generator::triggerEvent() + { + /** trigger event **/ + + /** check trigger presence **/ + if (mTriggers.size() == 0 && mDeepTriggers.size() == 0) { + return kTRUE; } - if (mTriggerMode == kTriggerAND) { - triggered &= retval; + + /** check trigger mode **/ + Bool_t triggered; + if (mTriggerMode == kTriggerOFF) { + return kTRUE; + } else if (mTriggerMode == kTriggerOR) { + triggered = kFALSE; + } else if (mTriggerMode == kTriggerAND) { + triggered = kTRUE; + } else { + return kTRUE; } - } - /** loop over deep triggers **/ - for (const auto& trigger : mDeepTriggers) { - auto retval = trigger(mInterface, mInterfaceName); - if (mTriggerMode == kTriggerOR) { - triggered |= retval; + /** loop over triggers **/ + for (const auto& trigger : mTriggers) { + auto retval = trigger(mParticles); + if (mTriggerMode == kTriggerOR) { + triggered |= retval; + } + if (mTriggerMode == kTriggerAND) { + triggered &= retval; + } } - if (mTriggerMode == kTriggerAND) { - triggered &= retval; + + /** loop over deep triggers **/ + for (const auto& trigger : mDeepTriggers) { + auto retval = trigger(mInterface, mInterfaceName); + if (mTriggerMode == kTriggerOR) { + triggered |= retval; + } + if (mTriggerMode == kTriggerAND) { + triggered &= retval; + } } - } - /** return **/ - return triggered; -} + /** return **/ + return triggered; + } -/*****************************************************************/ + /*****************************************************************/ -void Generator::addSubGenerator(int subGeneratorId, std::string const& subGeneratorDescription) -{ - if (subGeneratorId < 0) { - LOG(fatal) << "Sub-generator IDs must be >= 0, instead, passed value is " << subGeneratorId; + void Generator::addSubGenerator(int subGeneratorId, std::string const& subGeneratorDescription) + { + if (subGeneratorId < 0) { + LOG(fatal) << "Sub-generator IDs must be >= 0, instead, passed value is " << subGeneratorId; + } + mSubGeneratorsIdToDesc.insert({subGeneratorId, subGeneratorDescription}); } - mSubGeneratorsIdToDesc.insert({subGeneratorId, subGeneratorDescription}); -} -/*****************************************************************/ + /*****************************************************************/ -void Generator::updateSubGeneratorInformation(o2::dataformats::MCEventHeader* header) const -{ - if (mSubGeneratorId < 0) { - return; + void Generator::updateSubGeneratorInformation(o2::dataformats::MCEventHeader * header) const + { + if (mSubGeneratorId < 0) { + return; + } + header->putInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, mSubGeneratorId); + header->putInfo>(o2::mcgenid::GeneratorProperty::SUBGENERATORDESCRIPTIONMAP, mSubGeneratorsIdToDesc); } - header->putInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, mSubGeneratorId); - header->putInfo>(o2::mcgenid::GeneratorProperty::SUBGENERATORDESCRIPTIONMAP, mSubGeneratorsIdToDesc); -} -/*****************************************************************/ -/*****************************************************************/ + /*****************************************************************/ + /*****************************************************************/ } /* namespace eventgen */ } /* namespace o2 */ diff --git a/Generators/src/GeneratorsLinkDef.h b/Generators/src/GeneratorsLinkDef.h index 24b3f2e452498..97896d8225042 100644 --- a/Generators/src/GeneratorsLinkDef.h +++ b/Generators/src/GeneratorsLinkDef.h @@ -35,7 +35,7 @@ #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 +#ifdef GENERATORS_WITH_ONNXRUNTIME #pragma link C++ class o2::eventgen::GenTPCLoopers + ; #pragma link C++ class o2::eventgen::GenTPCLoopersParam + ; #endif diff --git a/Generators/src/TPCLoopers.cxx b/Generators/src/TPCLoopers.cxx index 109461ab71dfa..4eacb7674599c 100644 --- a/Generators/src/TPCLoopers.cxx +++ b/Generators/src/TPCLoopers.cxx @@ -382,6 +382,9 @@ void GenTPCLoopers::setFlatGas(Bool_t& flat, const Int_t& number) 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(); From 707f03c2236c902b0042dd523ab0fde29445be06 Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Mon, 5 Jan 2026 10:21:41 +0100 Subject: [PATCH 111/701] Revert "First implementation of loopers inclusion in base Generator class" This reverts commit b8c867dbb27d08e9ecf3134aeb72886fbb3c878c. --- .../SimConfig/include/SimConfig/SimConfig.h | 3 - Common/SimConfig/src/SimConfig.cxx | 2 - Generators/CMakeLists.txt | 13 - Generators/include/Generators/Generator.h | 13 - .../include/Generators/TPCLoopersParam.h | 48 -- Generators/include/TPCLoopers.h | 127 ----- .../share/egconfig/ScalerComptonParams.json | 28 -- .../share/egconfig/ScalerPairParams.json | 34 -- Generators/share/egconfig/gaussian_params.csv | 4 - Generators/share/egconfig/poisson_params.csv | 3 - Generators/src/Generator.cxx | 442 ++++++------------ Generators/src/GeneratorsLinkDef.h | 4 - Generators/src/TPCLoopers.cxx | 417 ----------------- Generators/src/TPCLoopersParam.cxx | 15 - 14 files changed, 146 insertions(+), 1007 deletions(-) delete mode 100644 Generators/include/Generators/TPCLoopersParam.h delete mode 100644 Generators/include/TPCLoopers.h delete mode 100644 Generators/share/egconfig/ScalerComptonParams.json delete mode 100644 Generators/share/egconfig/ScalerPairParams.json delete mode 100644 Generators/share/egconfig/gaussian_params.csv delete mode 100644 Generators/share/egconfig/poisson_params.csv delete mode 100644 Generators/src/TPCLoopers.cxx delete mode 100644 Generators/src/TPCLoopersParam.cxx diff --git a/Common/SimConfig/include/SimConfig/SimConfig.h b/Common/SimConfig/include/SimConfig/SimConfig.h index 8642a0e5bc225..be88d9fbd8c33 100644 --- a/Common/SimConfig/include/SimConfig/SimConfig.h +++ b/Common/SimConfig/include/SimConfig/SimConfig.h @@ -52,7 +52,6 @@ struct SimConfigData { std::vector mActiveModules; // list of active modules std::vector mReadoutDetectors; // list of readout detectors std::string mMCEngine; // chosen VMC engine - bool mNoLoopers = false; // Disable automatic TPC loopers std::string mGenerator; // chosen VMC generator std::string mTrigger; // chosen VMC generator trigger unsigned int mNEvents; // number of events to be simulated @@ -139,8 +138,6 @@ class SimConfig // get selected active detectors std::vector const& getActiveModules() const { return mConfigData.mActiveModules; } std::vector const& getReadoutDetectors() const { return mConfigData.mReadoutDetectors; } - // get loopers veto - bool getLoopersVeto() const { return mConfigData.mNoLoopers; } // static helper functions to determine list of active / readout modules // can also be used from outside diff --git a/Common/SimConfig/src/SimConfig.cxx b/Common/SimConfig/src/SimConfig.cxx index 5ddc3199e3d4a..15879687872d5 100644 --- a/Common/SimConfig/src/SimConfig.cxx +++ b/Common/SimConfig/src/SimConfig.cxx @@ -74,7 +74,6 @@ void SimConfig::initOptions(boost::program_options::options_description& options "run", bpo::value()->default_value(-1), "ALICE run number")( "asservice", bpo::value()->default_value(false), "run in service/server mode")( "noGeant", bpo::bool_switch(), "prohibits any Geant transport/physics (by using tight cuts)")( - "noLoopers", bpo::bool_switch(), "disable automatic TPC loopers")( "forwardKine", bpo::bool_switch(), "forward kinematics on a FairMQ channel")( "noDiscOutput", bpo::bool_switch(), "switch off writing sim results to disc (useful in combination with forwardKine)"); options.add_options()("fromCollContext", bpo::value()->default_value(""), "Use a pregenerated collision context to infer number of events to simulate, how to embedd them, the vertex position etc. Takes precedence of other options such as \"--nEvents\". The format is COLLISIONCONTEXTFILE.root[:SIGNALNAME] where SIGNALNAME is the event part in the context which is relevant."); @@ -298,7 +297,6 @@ bool SimConfig::resetFromParsedMap(boost::program_options::variables_map const& using o2::detectors::DetID; mConfigData.mMCEngine = vm["mcEngine"].as(); mConfigData.mNoGeant = vm["noGeant"].as(); - mConfigData.mNoLoopers = vm["noLoopers"].as(); // Reset modules and detectors as they are anyway re-parsed mConfigData.mReadoutDetectors.clear(); diff --git a/Generators/CMakeLists.txt b/Generators/CMakeLists.txt index 56fe8b8fc2284..02caa63df0d43 100644 --- a/Generators/CMakeLists.txt +++ b/Generators/CMakeLists.txt @@ -41,8 +41,6 @@ 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> @@ -55,7 +53,6 @@ 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) @@ -66,10 +63,6 @@ if(HepMC3_FOUND) target_compile_definitions(${targetName} PUBLIC GENERATORS_WITH_HEPMC3) endif() -if(onnxruntime_FOUND) - target_compile_definitions(${targetName} PUBLIC GENERATORS_WITH_ONNXRUNTIME) -endif() - set(headers include/Generators/Generator.h include/Generators/Trigger.h @@ -95,12 +88,6 @@ set(headers include/Generators/FlowMapper.h ) -if(onnxruntime_FOUND) - list(APPEND headers - include/Generators/TPCLoopers.h - include/Generators/TPCLoopersParam.h) -endif() - if(pythia_FOUND) list(APPEND headers include/Generators/GeneratorPythia8.h diff --git a/Generators/include/Generators/Generator.h b/Generators/include/Generators/Generator.h index 374d53f324399..bd35a00793e2d 100644 --- a/Generators/include/Generators/Generator.h +++ b/Generators/include/Generators/Generator.h @@ -17,10 +17,6 @@ #include "FairGenerator.h" #include "TParticle.h" #include "Generators/Trigger.h" -#ifdef GENERATORS_WITH_ONNXRUNTIME -#include "Generators/TPCLoopers.h" -#include "Generators/TPCLoopersParam.h" -#endif #include #include #include @@ -77,7 +73,6 @@ 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 loopers(); // adds loopers to the event in case TPC is used virtual void updateHeader(o2::dataformats::MCEventHeader* eventHeader) {}; Bool_t triggerEvent(); @@ -159,8 +154,6 @@ class Generator : public FairGenerator private: void updateSubGeneratorInformation(o2::dataformats::MCEventHeader* header) const; - // loopers flag - Bool_t mAddLoopers = kFALSE; // 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) @@ -169,12 +162,6 @@ class Generator : public FairGenerator // global static information about (upper limit of) number of events to be generated static unsigned int gTotalNEvents; -#ifdef GENERATORS_WITH_ONNXRUNTIME - // Loopers generator instance - std::unique_ptr mLoopersGen = nullptr; -#endif - void initLoopersGen(); - ClassDefOverride(Generator, 2); }; /** class Generator **/ diff --git a/Generators/include/Generators/TPCLoopersParam.h b/Generators/include/Generators/TPCLoopersParam.h deleted file mode 100644 index ceeea201538b2..0000000000000 --- a/Generators/include/Generators/TPCLoopersParam.h +++ /dev/null @@ -1,48 +0,0 @@ -// 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 { - 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/egconfig/poisson_params.csv"; // file with Poissonian parameters - std::string gauss = "${O2_ROOT}/share/Generators/egconfig/gaussian_params.csv"; // file with Gaussian parameters - std::string scaler_pair = "${O2_ROOT}/share/Generators/egconfig/ScalerPairParams.json"; // file with scaler parameters for e+e- pair production - std::string scaler_compton = "${O2_ROOT}/share/Generators/egconfig/ScalerComptonParams.json"; // file with scaler parameters for Compton scattering - bool flat_gas = true; // if true, the gas density is considered flat in the TPC volume - int nFlatGasLoopers = 500; // number of loopers to be generated per event in case of flat gas - float fraction_pairs = 0.08; // fraction of loopers - std::array multiplier = {1., 1.}; // multiplier for pairs and compton loopers for Poissonian and Gaussian sampling - std::array fixedNLoopers = {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 - O2ParamDef(GenTPCLoopersParam, "GenTPCLoopers"); -}; - -} // end namespace eventgen -} // end namespace o2 - -#endif // ALICEO2_EVENTGEN_TPCLOOPERSPARAM_H_ diff --git a/Generators/include/TPCLoopers.h b/Generators/include/TPCLoopers.h deleted file mode 100644 index 70146a82baf60..0000000000000 --- a/Generators/include/TPCLoopers.h +++ /dev/null @@ -1,127 +0,0 @@ -#ifndef ALICEO2_EVENTGEN_TPCLOOPERS_H_ -#define ALICEO2_EVENTGEN_TPCLOOPERS_H_ - -#ifdef GENERATORS_WITH_ONNXRUNTIME -#include -#endif -#include -#include -#include -#include -#include "CCDB/CCDBTimeStampUtils.h" -#include "CCDB/CcdbApi.h" -#include "DetectorsRaw/HBFUtils.h" -#include "TRandom3.h" -#include "TDatabasePDG.h" -#include -#include -#include "SimulationDataFormat/MCGenProperties.h" -#include "TParticle.h" -#include - -#ifdef GENERATORS_WITH_ONNXRUNTIME -// Static Ort::Env instance for multiple onnx model loading -extern Ort::Env global_env; -#endif - -#ifdef GENERATORS_WITH_ONNXRUNTIME -// This class is responsible for loading the scaler parameters from a JSON file -// and applying the inverse transformation to the generated data. -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_ONNXRUNTIME - -namespace o2 -{ -namespace eventgen -{ - -#ifdef GENERATORS_WITH_ONNXRUNTIME -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(std::array &mult); - - void setFlatGas(Bool_t &flat, const Int_t &number = -1); - - void setFractionPairs(float &fractionPairs); - - 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; - // Masses of the electrons and positrons - TDatabasePDG *mPDG = TDatabasePDG::Instance(); - double mMass_e = mPDG->GetParticle(11)->Mass(); - double mMass_p = mPDG->GetParticle(-11)->Mass(); - 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 - 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 -}; -#endif // GENERATORS_WITH_ONNXRUNTIME - -} // namespace eventgen -} // namespace o2 - -#endif // ALICEO2_EVENTGEN_TPCLOOPERS_H_ \ No newline at end of file diff --git a/Generators/share/egconfig/ScalerComptonParams.json b/Generators/share/egconfig/ScalerComptonParams.json deleted file mode 100644 index d8e654847f46e..0000000000000 --- a/Generators/share/egconfig/ScalerComptonParams.json +++ /dev/null @@ -1,28 +0,0 @@ -{ - "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/egconfig/ScalerPairParams.json b/Generators/share/egconfig/ScalerPairParams.json deleted file mode 100644 index 61434bfa2462e..0000000000000 --- a/Generators/share/egconfig/ScalerPairParams.json +++ /dev/null @@ -1,34 +0,0 @@ -{ - "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/egconfig/gaussian_params.csv b/Generators/share/egconfig/gaussian_params.csv deleted file mode 100644 index 8e07c22dd30bf..0000000000000 --- a/Generators/share/egconfig/gaussian_params.csv +++ /dev/null @@ -1,4 +0,0 @@ -9.611554230339172022e+01 -1.963570744941765867e+01 -4.300000000000000000e+01 -1.690000000000000000e+02 diff --git a/Generators/share/egconfig/poisson_params.csv b/Generators/share/egconfig/poisson_params.csv deleted file mode 100644 index ef26bd973d34c..0000000000000 --- a/Generators/share/egconfig/poisson_params.csv +++ /dev/null @@ -1,3 +0,0 @@ -3.165383056343737511e+00 -1.000000000000000000e+00 -1.200000000000000000e+01 diff --git a/Generators/src/Generator.cxx b/Generators/src/Generator.cxx index 153ef5cd5e35e..9204ede98215e 100644 --- a/Generators/src/Generator.cxx +++ b/Generators/src/Generator.cxx @@ -17,14 +17,11 @@ #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" namespace o2 { @@ -42,18 +39,6 @@ Generator::Generator() : FairGenerator("ALICEo2", "ALICEo2 Generator"), /** default constructor **/ mThisInstanceID = Generator::InstanceCounter; Generator::InstanceCounter++; - auto simConfig = o2::conf::SimConfig::Instance(); - auto noLoops = simConfig.getLoopersVeto(); - if (!noLoops) { - bool transport = (simConfig.getMCEngine() != "O2TrivialMCEngine"); - if (transport) { - bool tpcActive = (std::find(simConfig.getReadoutDetectors().begin(), simConfig.getReadoutDetectors().end(), "TPC") != simConfig.getReadoutDetectors().end()); - if (tpcActive) { - mAddLoopers = kTRUE; - initLoopersGen(); - } - } - } } /*****************************************************************/ @@ -64,102 +49,6 @@ Generator::Generator(const Char_t* name, const Char_t* title) : FairGenerator(na /** constructor **/ mThisInstanceID = Generator::InstanceCounter; Generator::InstanceCounter++; - auto simConfig = o2::conf::SimConfig::Instance(); - auto noLoops = simConfig.getLoopersVeto(); - if (!noLoops) { - bool transport = (simConfig.getMCEngine() != "O2TrivialMCEngine"); - if (transport) { - bool tpcActive = (std::find(simConfig.getReadoutDetectors().begin(), simConfig.getReadoutDetectors().end(), "TPC") != simConfig.getReadoutDetectors().end()); - if (tpcActive) { - mAddLoopers = kTRUE; - initLoopersGen(); - } - } - } -} - -/*****************************************************************/ - -void Generator::initLoopersGen() -{ -#ifdef GENERATORS_WITH_ONNXRUNTIME - // 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()); - 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()); - auto flat_gas = loopersParam.flat_gas; - const auto& nFlatGasLoopers = loopersParam.nFlatGasLoopers; - auto fraction_pairs = loopersParam.fraction_pairs; - auto multiplier = loopersParam.multiplier; - auto fixedNLoopers = loopersParam.fixedNLoopers; - const std::array models = {model_pairs, model_compton}; - const std::array local_names = {"WGANpair.onnx", "WGANcompton.onnx"}; - const std::array isAlien = {models[0].starts_with("alien://"), models[1].starts_with("alien://")}; - const std::array isCCDB = {models[0].starts_with("ccdb://"), models[1].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; })) { - o2::ccdb::CcdbApi ccdb_api; - ccdb_api.init("http://alice-ccdb.cern.ch"); - 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; - try { - // Create the TPC loopers generator with the provided parameters - mLoopersGen = std::make_unique(model_pairs, model_compton, poisson, gauss, scaler_pair, scaler_compton); - - // Configure the generator with flat gas loopers if enabled (default) - if (flat_gas) { - mLoopersGen->setFlatGas(flat_gas, nFlatGasLoopers); - mLoopersGen->setFractionPairs(fraction_pairs); - } else { - // Otherwise, Poisson+Gauss sampling or fixed number of loopers 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 - mLoopersGen->SetNLoopers(fixedNLoopers[0], fixedNLoopers[1]); - mLoopersGen->SetMultiplier(multiplier); - } - LOG(info) << "TPC Loopers generator initialized successfully"; - } catch (const std::exception& e) { - LOG(error) << "Failed to initialize TPC Loopers generator: " << e.what(); - mLoopersGen.reset(); - } -#else - LOG(warn) << "ONNX Runtime support not available, cannot initialize TPC loopers generator"; -#endif } /*****************************************************************/ @@ -176,230 +65,191 @@ Bool_t /*****************************************************************/ Bool_t - Generator::loopers() + Generator::ReadEvent(FairPrimaryGenerator* primGen) { -#ifdef GENERATORS_WITH_ONNXRUNTIME - if (!mLoopersGen) { - LOG(error) << "Loopers generator not initialized"; - return kFALSE; + /** read event **/ + + /** endless generate-and-trigger loop **/ + while (true) { + mReadEventCounter++; + + /** clear particle vector **/ + mParticles.clear(); + + /** reset the sub-generator ID **/ + mSubGeneratorId = -1; + + /** generate event **/ + if (!generateEvent()) { + LOG(error) << "ReadEvent failed in generateEvent"; + return kFALSE; + } + + /** import particles **/ + if (!importParticles()) { + LOG(error) << "ReadEvent failed in importParticles"; + return kFALSE; + } + + if (mSubGeneratorsIdToDesc.empty() && mSubGeneratorId > -1) { + LOG(fatal) << "ReadEvent failed because no SubGenerator description given"; + } + + if (!mSubGeneratorsIdToDesc.empty() && mSubGeneratorId < 0) { + LOG(fatal) << "ReadEvent failed because SubGenerator description given but sub-generator not set"; + } + + /** trigger event **/ + if (triggerEvent()) { + mTriggerOkHook(mParticles, mReadEventCounter); + break; + } else { + mTriggerFalseHook(mParticles, mReadEventCounter); + } } - // Generate loopers using the initialized TPC loopers generator - if (!mLoopersGen->generateEvent()) { - LOG(error) << "Failed to generate loopers event"; + /** add tracks **/ + if (!addTracks(primGen)) { + LOG(error) << "ReadEvent failed in addTracks"; return kFALSE; } - const auto& looperParticles = mLoopersGen->importParticles(); - if (looperParticles.empty()) { - LOG(error) << "Failed to import loopers particles"; + + /** update header **/ + auto header = primGen->GetEvent(); + auto o2header = dynamic_cast(header); + if (!header) { + LOG(fatal) << "MC event header is not a 'o2::dataformats::MCEventHeader' object"; return kFALSE; } - // Append the generated looper particles to the main particle list - mParticles.insert(mParticles.end(), looperParticles.begin(), looperParticles.end()); + updateHeader(o2header); + updateSubGeneratorInformation(o2header); - LOG(debug) << "Added " << looperParticles.size() << " looper particles"; - return kTRUE; -#else - LOG(warn) << "ONNX Runtime support not available, skipping TPC loopers generation"; + /** success **/ return kTRUE; -#endif } - /*****************************************************************/ - - Bool_t - Generator::ReadEvent(FairPrimaryGenerator * primGen) - { - /** read event **/ - - /** endless generate-and-trigger loop **/ - while (true) { - mReadEventCounter++; - - /** clear particle vector **/ - mParticles.clear(); - - /** reset the sub-generator ID **/ - mSubGeneratorId = -1; - - /** generate event **/ - if (!generateEvent()) { - LOG(error) << "ReadEvent failed in generateEvent"; - return kFALSE; - } - - /** import particles **/ - if (!importParticles()) { - LOG(error) << "ReadEvent failed in importParticles"; - return kFALSE; - } - - /** Add loopers **/ - if(mAddLoopers){ - if (!loopers()) { - LOG(error) << "ReadEvent failed in loopers"; - return kFALSE; - } - } - - if (mSubGeneratorsIdToDesc.empty() && mSubGeneratorId > -1) { - LOG(fatal) << "ReadEvent failed because no SubGenerator description given"; - } - - if (!mSubGeneratorsIdToDesc.empty() && mSubGeneratorId < 0) { - LOG(fatal) << "ReadEvent failed because SubGenerator description given but sub-generator not set"; - } - - /** trigger event **/ - if (triggerEvent()) { - mTriggerOkHook(mParticles, mReadEventCounter); - break; - } else { - mTriggerFalseHook(mParticles, mReadEventCounter); - } - } - /** add tracks **/ - if (!addTracks(primGen)) { - LOG(error) << "ReadEvent failed in addTracks"; - return kFALSE; - } +/*****************************************************************/ - /** update header **/ - auto header = primGen->GetEvent(); - auto o2header = dynamic_cast(header); - if (!header) { - LOG(fatal) << "MC event header is not a 'o2::dataformats::MCEventHeader' object"; - return kFALSE; - } - updateHeader(o2header); - updateSubGeneratorInformation(o2header); +Bool_t + Generator::addTracks(FairPrimaryGenerator* primGen) +{ + /** add tracks **/ - /** success **/ - return kTRUE; + auto o2primGen = dynamic_cast(primGen); + if (!o2primGen) { + LOG(fatal) << "PrimaryGenerator is not a o2::eventgen::PrimaryGenerator"; + return kFALSE; } - /*****************************************************************/ + /** loop over particles **/ + for (const auto& particle : mParticles) { + o2primGen->AddTrack(particle.GetPdgCode(), + particle.Px() * mMomentumUnit, + particle.Py() * mMomentumUnit, + particle.Pz() * mMomentumUnit, + particle.Vx() * mPositionUnit, + particle.Vy() * mPositionUnit, + particle.Vz() * mPositionUnit, + particle.GetMother(0), + particle.GetMother(1), + particle.GetDaughter(0), + particle.GetDaughter(1), + particle.TestBit(ParticleStatus::kToBeDone), + particle.Energy() * mEnergyUnit, + particle.T() * mTimeUnit, + particle.GetWeight(), + (TMCProcess)particle.GetUniqueID(), + particle.GetStatusCode()); // generator status information passed as status code field + } - Bool_t - Generator::addTracks(FairPrimaryGenerator * primGen) - { - /** add tracks **/ + /** success **/ + return kTRUE; +} - auto o2primGen = dynamic_cast(primGen); - if (!o2primGen) { - LOG(fatal) << "PrimaryGenerator is not a o2::eventgen::PrimaryGenerator"; - return kFALSE; - } +/*****************************************************************/ - /** loop over particles **/ - for (const auto& particle : mParticles) { - o2primGen->AddTrack(particle.GetPdgCode(), - particle.Px() * mMomentumUnit, - particle.Py() * mMomentumUnit, - particle.Pz() * mMomentumUnit, - particle.Vx() * mPositionUnit, - particle.Vy() * mPositionUnit, - particle.Vz() * mPositionUnit, - particle.GetMother(0), - particle.GetMother(1), - particle.GetDaughter(0), - particle.GetDaughter(1), - particle.TestBit(ParticleStatus::kToBeDone), - particle.Energy() * mEnergyUnit, - particle.T() * mTimeUnit, - particle.GetWeight(), - (TMCProcess)particle.GetUniqueID(), - particle.GetStatusCode()); // generator status information passed as status code field - } +Bool_t + Generator::boostEvent() +{ + /** boost event **/ - /** success **/ - return kTRUE; - } + /** success **/ + return kTRUE; +} - /*****************************************************************/ +/*****************************************************************/ - Bool_t - Generator::boostEvent() - { - /** boost event **/ +Bool_t + Generator::triggerEvent() +{ + /** trigger event **/ - /** success **/ + /** check trigger presence **/ + if (mTriggers.size() == 0 && mDeepTriggers.size() == 0) { return kTRUE; } - /*****************************************************************/ - - Bool_t - Generator::triggerEvent() - { - /** trigger event **/ + /** check trigger mode **/ + Bool_t triggered; + if (mTriggerMode == kTriggerOFF) { + return kTRUE; + } else if (mTriggerMode == kTriggerOR) { + triggered = kFALSE; + } else if (mTriggerMode == kTriggerAND) { + triggered = kTRUE; + } else { + return kTRUE; + } - /** check trigger presence **/ - if (mTriggers.size() == 0 && mDeepTriggers.size() == 0) { - return kTRUE; + /** loop over triggers **/ + for (const auto& trigger : mTriggers) { + auto retval = trigger(mParticles); + if (mTriggerMode == kTriggerOR) { + triggered |= retval; } - - /** check trigger mode **/ - Bool_t triggered; - if (mTriggerMode == kTriggerOFF) { - return kTRUE; - } else if (mTriggerMode == kTriggerOR) { - triggered = kFALSE; - } else if (mTriggerMode == kTriggerAND) { - triggered = kTRUE; - } else { - return kTRUE; + if (mTriggerMode == kTriggerAND) { + triggered &= retval; } + } - /** loop over triggers **/ - for (const auto& trigger : mTriggers) { - auto retval = trigger(mParticles); - if (mTriggerMode == kTriggerOR) { - triggered |= retval; - } - if (mTriggerMode == kTriggerAND) { - triggered &= retval; - } + /** loop over deep triggers **/ + for (const auto& trigger : mDeepTriggers) { + auto retval = trigger(mInterface, mInterfaceName); + if (mTriggerMode == kTriggerOR) { + triggered |= retval; } - - /** loop over deep triggers **/ - for (const auto& trigger : mDeepTriggers) { - auto retval = trigger(mInterface, mInterfaceName); - if (mTriggerMode == kTriggerOR) { - triggered |= retval; - } - if (mTriggerMode == kTriggerAND) { - triggered &= retval; - } + if (mTriggerMode == kTriggerAND) { + triggered &= retval; } - - /** return **/ - return triggered; } - /*****************************************************************/ + /** return **/ + return triggered; +} + +/*****************************************************************/ - void Generator::addSubGenerator(int subGeneratorId, std::string const& subGeneratorDescription) - { - if (subGeneratorId < 0) { - LOG(fatal) << "Sub-generator IDs must be >= 0, instead, passed value is " << subGeneratorId; - } - mSubGeneratorsIdToDesc.insert({subGeneratorId, subGeneratorDescription}); +void Generator::addSubGenerator(int subGeneratorId, std::string const& subGeneratorDescription) +{ + if (subGeneratorId < 0) { + LOG(fatal) << "Sub-generator IDs must be >= 0, instead, passed value is " << subGeneratorId; } + mSubGeneratorsIdToDesc.insert({subGeneratorId, subGeneratorDescription}); +} - /*****************************************************************/ +/*****************************************************************/ - void Generator::updateSubGeneratorInformation(o2::dataformats::MCEventHeader * header) const - { - if (mSubGeneratorId < 0) { - return; - } - header->putInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, mSubGeneratorId); - header->putInfo>(o2::mcgenid::GeneratorProperty::SUBGENERATORDESCRIPTIONMAP, mSubGeneratorsIdToDesc); +void Generator::updateSubGeneratorInformation(o2::dataformats::MCEventHeader* header) const +{ + if (mSubGeneratorId < 0) { + return; } + header->putInfo(o2::mcgenid::GeneratorProperty::SUBGENERATORID, mSubGeneratorId); + header->putInfo>(o2::mcgenid::GeneratorProperty::SUBGENERATORDESCRIPTIONMAP, mSubGeneratorsIdToDesc); +} - /*****************************************************************/ - /*****************************************************************/ +/*****************************************************************/ +/*****************************************************************/ } /* namespace eventgen */ } /* namespace o2 */ diff --git a/Generators/src/GeneratorsLinkDef.h b/Generators/src/GeneratorsLinkDef.h index 97896d8225042..2b8d42f86bf9b 100644 --- a/Generators/src/GeneratorsLinkDef.h +++ b/Generators/src/GeneratorsLinkDef.h @@ -35,10 +35,6 @@ #pragma link C++ class o2::eventgen::GeneratorFromEventPool + ; #pragma link C++ class o2::eventgen::GeneratorEventPoolParam + ; #pragma link C++ class o2::eventgen::EventPoolGenConfig + ; -#ifdef GENERATORS_WITH_ONNXRUNTIME -#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/TPCLoopers.cxx b/Generators/src/TPCLoopers.cxx deleted file mode 100644 index 4eacb7674599c..0000000000000 --- a/Generators/src/TPCLoopers.cxx +++ /dev/null @@ -1,417 +0,0 @@ -#include "Generators/TPCLoopers.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 normal_min; - std::vector normal_max; - std::vector outlier_center; - std::vector 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 != "") - { - 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 != "") - { - 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 - nLoopers = 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())); - } - if (mGaussSet) { - mNLoopersCompton = static_cast(std::round(mMultiplier[1] * GaussianElectrons())); - } - // 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; - // 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 + mMass_e * mMass_e); - p_etot = TMath::Sqrt(px_p * px_p + py_p * py_p + pz_p * pz_p + mMass_p * mMass_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 + mMass_e * mMass_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(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, const Int_t& number) -{ - mFlatGas = flat; - if (mFlatGas) { - 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; - 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 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; -} - -} // namespace eventgen -} // namespace o2 \ No newline at end of file diff --git a/Generators/src/TPCLoopersParam.cxx b/Generators/src/TPCLoopersParam.cxx deleted file mode 100644 index 0202a8ced0535..0000000000000 --- a/Generators/src/TPCLoopersParam.cxx +++ /dev/null @@ -1,15 +0,0 @@ -// 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); From 51f8cefb58b705ba534d65bee42528c0a5d4221a Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Wed, 17 Dec 2025 14:17:57 +0100 Subject: [PATCH 112/701] Moved configFile check + expand env vars --- Generators/src/GeneratorFactory.cxx | 5 ----- Generators/src/GeneratorHybrid.cxx | 12 +++++++++--- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Generators/src/GeneratorFactory.cxx b/Generators/src/GeneratorFactory.cxx index 4102bd8ffd9b2..d04e785402915 100644 --- a/Generators/src/GeneratorFactory.cxx +++ b/Generators/src/GeneratorFactory.cxx @@ -279,11 +279,6 @@ 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 = o2::eventgen::GeneratorHybrid::Instance(config); primGen->AddGenerator(&hybrid); #endif diff --git a/Generators/src/GeneratorHybrid.cxx b/Generators/src/GeneratorHybrid.cxx index 370671a977a5c..2a13f9876e717 100644 --- a/Generators/src/GeneratorHybrid.cxx +++ b/Generators/src/GeneratorHybrid.cxx @@ -615,17 +615,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; } From 7211829480227b643715c57b2ac5e80a6bd17846 Mon Sep 17 00:00:00 2001 From: Ernst Hellbar Date: Tue, 2 Dec 2025 10:14:58 +0100 Subject: [PATCH 113/701] dpl-workflow.sh: add relaxed GPU_rec_tpc async cuts also as default for sync --- prodtests/full-system-test/dpl-workflow.sh | 2 ++ 1 file changed, 2 insertions(+) diff --git a/prodtests/full-system-test/dpl-workflow.sh b/prodtests/full-system-test/dpl-workflow.sh index 754349c87eecc..db491da5ebec5 100755 --- a/prodtests/full-system-test/dpl-workflow.sh +++ b/prodtests/full-system-test/dpl-workflow.sh @@ -145,6 +145,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 From 35b0becfa59f1d9be85f65a2ca829f99e93ef5f0 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Mon, 15 Dec 2025 11:02:17 +0100 Subject: [PATCH 114/701] Fix code checker issue --- Detectors/ITSMFT/ITS/postprocessing/studies/src/PIDStudy.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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; From ea23c378e45c2e2c84a707cd03abd044e52572ea Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Mon, 15 Dec 2025 11:02:17 +0100 Subject: [PATCH 115/701] MathUtils: move BetheBlochAleph to a common header --- .../include/MathUtils/BetheBlochAleph.h | 35 +++++++++++++++++++ .../include/DataFormatsTPC/BetheBlochAleph.h | 18 +++------- 2 files changed, 39 insertions(+), 14 deletions(-) create mode 100644 Common/MathUtils/include/MathUtils/BetheBlochAleph.h diff --git a/Common/MathUtils/include/MathUtils/BetheBlochAleph.h b/Common/MathUtils/include/MathUtils/BetheBlochAleph.h new file mode 100644 index 0000000000000..bd72faffb0503 --- /dev/null +++ b/Common/MathUtils/include/MathUtils/BetheBlochAleph.h @@ -0,0 +1,35 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef AliceO2_COMMON_BETHEBLOCH_H_ +#define AliceO2_COMMON_BETHEBLOCH_H_ + +#include "GPUCommonDef.h" +#include "GPUCommonMath.h" + +namespace o2::common +{ + +template +GPUdi() T BetheBlochAleph(T bg, T kp1, T kp2, T kp3, T kp4, T kp5) +{ + T beta = bg / o2::gpu::GPUCommonMath::Sqrt(static_cast(1.) + bg * bg); + + T aa = o2::gpu::GPUCommonMath::Pow(beta, kp4); + T bb = o2::gpu::GPUCommonMath::Pow(static_cast(1.) / bg, kp5); + bb = o2::gpu::GPUCommonMath::Log(kp3 + bb); + + return (kp2 - aa - bb) * kp1 / aa; +} + +} // namespace o2::common + +#endif diff --git a/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 From d6f6148d1a89eb2bfd955541ffa0717178cb122e Mon Sep 17 00:00:00 2001 From: "dependabot[bot]" <49699333+dependabot[bot]@users.noreply.github.com> Date: Mon, 15 Dec 2025 15:25:59 +0000 Subject: [PATCH 116/701] Bump actions/cache from 4 to 5 Bumps [actions/cache](https://github.com/actions/cache) from 4 to 5. - [Release notes](https://github.com/actions/cache/releases) - [Changelog](https://github.com/actions/cache/blob/main/RELEASES.md) - [Commits](https://github.com/actions/cache/compare/v4...v5) --- updated-dependencies: - dependency-name: actions/cache dependency-version: '5' dependency-type: direct:production update-type: version-update:semver-major ... Signed-off-by: dependabot[bot] --- .github/workflows/reports.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 From aab079f2ba980913451b4902749e8f424fcebdaa Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Tue, 6 Jan 2026 10:59:20 +0100 Subject: [PATCH 117/701] DataModel: improve DataHeader formatter Add splitPayloadIndex / splitPayloadParts to the default printout --- DataFormats/Headers/include/Headers/DataHeaderHelpers.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) 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("{}/{}/{}", From a5f88b79468c20cf515488c3f69f98fb2839830f Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Mon, 1 Dec 2025 12:49:46 +0100 Subject: [PATCH 118/701] AnalysisContext -> DanglingEdgesContext --- .../AnalysisSupport/src/AODReaderHelpers.cxx | 6 +- .../AnalysisSupport/src/AODWriterHelpers.cxx | 6 +- .../CCDBSupport/src/AnalysisCCDBHelpers.cxx | 4 +- .../Framework/AnalysisSupportHelpers.h | 2 +- ...alysisContext.h => DanglingEdgesContext.h} | 8 +- Framework/Core/src/AnalysisSupportHelpers.cxx | 6 +- Framework/Core/src/ArrowSupport.cxx | 64 +++++++------- Framework/Core/src/WorkflowHelpers.cxx | 85 +++++++++---------- 8 files changed, 90 insertions(+), 91 deletions(-) rename Framework/Core/include/Framework/{AnalysisContext.h => DanglingEdgesContext.h} (92%) diff --git a/Framework/AnalysisSupport/src/AODReaderHelpers.cxx b/Framework/AnalysisSupport/src/AODReaderHelpers.cxx index 40aa5a9537c7f..045ef072a3040 100644 --- a/Framework/AnalysisSupport/src/AODReaderHelpers.cxx +++ b/Framework/AnalysisSupport/src/AODReaderHelpers.cxx @@ -19,7 +19,7 @@ #include "Framework/AlgorithmSpec.h" #include "Framework/DataSpecUtils.h" #include "Framework/ConfigContext.h" -#include "Framework/AnalysisContext.h" +#include "Framework/DanglingEdgesContext.h" namespace o2::framework::readers { @@ -81,7 +81,7 @@ struct Buildable { AlgorithmSpec AODReaderHelpers::indexBuilderCallback(ConfigContext const& ctx) { - auto& ac = ctx.services().get(); + auto& ac = ctx.services().get(); return AlgorithmSpec::InitCallback{[requested = ac.requestedIDXs](InitContext& /*ic*/) { std::vector buildables; for (auto& i : requested) { @@ -183,7 +183,7 @@ struct Spawnable { AlgorithmSpec AODReaderHelpers::aodSpawnerCallback(ConfigContext const& ctx) { - auto& ac = ctx.services().get(); + auto& ac = ctx.services().get(); return AlgorithmSpec::InitCallback{[requested = ac.spawnerInputs](InitContext& /*ic*/) { std::vector spawnables; for (auto& i : requested) { diff --git a/Framework/AnalysisSupport/src/AODWriterHelpers.cxx b/Framework/AnalysisSupport/src/AODWriterHelpers.cxx index bcf27d0be5ba3..5a43683afd364 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,7 +62,7 @@ const static std::unordered_map ROOTfileNa AlgorithmSpec AODWriterHelpers::getOutputTTreeWriter(ConfigContext const& ctx) { - auto& ac = ctx.services().get(); + auto& ac = ctx.services().get(); auto dod = AnalysisSupportHelpers::getDataOutputDirector(ctx); int compressionLevel = 505; if (ctx.options().hasOption("aod-writer-compression")) { @@ -245,7 +245,7 @@ AlgorithmSpec AODWriterHelpers::getOutputTTreeWriter(ConfigContext const& ctx) AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& ctx) { using namespace monitoring; - auto& ac = ctx.services().get(); + auto& ac = ctx.services().get(); auto tskmap = ac.outTskMap; auto objmap = ac.outObjHistMap; diff --git a/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx index aba1f3ed4e13d..fcc856669cd92 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 @@ -69,7 +69,7 @@ void fillValidRoutes(CCDBFetcherHelper& helper, std::vector(); + auto& ac = ctx.services().get(); std::vector> schemas; auto schemaMetadata = std::make_shared(); diff --git a/Framework/Core/include/Framework/AnalysisSupportHelpers.h b/Framework/Core/include/Framework/AnalysisSupportHelpers.h index c0eeb3bd9697d..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 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/src/AnalysisSupportHelpers.cxx b/Framework/Core/src/AnalysisSupportHelpers.cxx index e59f36c72bdab..15b56f9afbff5 100644 --- a/Framework/Core/src/AnalysisSupportHelpers.cxx +++ b/Framework/Core/src/AnalysisSupportHelpers.cxx @@ -25,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(); @@ -200,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/ArrowSupport.cxx b/Framework/Core/src/ArrowSupport.cxx index 26594252e888b..ee4275281ab31 100644 --- a/Framework/Core/src/ArrowSupport.cxx +++ b/Framework/Core/src/ArrowSupport.cxx @@ -588,23 +588,23 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() 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& 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(); + 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{ac.requestedIDXs}; + sinks::update_input_list{dec.requestedIDXs}; } // recreate inputs and outputs builder->inputs.clear(); @@ -612,7 +612,7 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() // replace AlgorithmSpec // FIXME: it should be made more generic, so it does not need replacement... builder->algorithm = PluginManager::loadAlgorithmFromPlugin("O2FrameworkOnDemandTablesSupport", "IndexTableBuilder", ctx); // readers::AODReaderHelpers::indexBuilderCallback(ctx); - AnalysisSupportHelpers::addMissingOutputsToBuilder(ac.requestedIDXs, ac.requestedAODs, ac.requestedDYNs, *builder); + AnalysisSupportHelpers::addMissingOutputsToBuilder(dec.requestedIDXs, dec.requestedAODs, dec.requestedDYNs, *builder); } if (spawner != workflow.end()) { @@ -620,21 +620,21 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() for (auto& d : workflow | views::exclude_by_name(spawner->name)) { d.inputs | views::partial_match_filter(header::DataOrigin{"DYN"}) | - sinks::update_input_list{ac.requestedDYNs}; + sinks::update_input_list{dec.requestedDYNs}; d.outputs | views::partial_match_filter(header::DataOrigin{"DYN"}) | - sinks::append_to{ac.providedDYNs}; + sinks::append_to{dec.providedDYNs}; } - std::sort(ac.requestedDYNs.begin(), ac.requestedDYNs.end(), inputSpecLessThan); - std::sort(ac.providedDYNs.begin(), ac.providedDYNs.end(), outputSpecLessThan); - ac.spawnerInputs.clear(); - ac.requestedDYNs | - views::filter_not_matching(ac.providedDYNs) | - sinks::append_to{ac.spawnerInputs}; + std::sort(dec.requestedDYNs.begin(), dec.requestedDYNs.end(), inputSpecLessThan); + std::sort(dec.providedDYNs.begin(), dec.providedDYNs.end(), 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(); - AnalysisSupportHelpers::addMissingOutputsToSpawner({}, ac.spawnerInputs, ac.requestedAODs, *spawner); + AnalysisSupportHelpers::addMissingOutputsToSpawner({}, dec.spawnerInputs, dec.requestedAODs, *spawner); // replace AlgorithmSpec // FIXME: it should be made more generic, so it does not need replacement... spawner->algorithm = PluginManager::loadAlgorithmFromPlugin("O2FrameworkOnDemandTablesSupport", "ExtendedTableSpawner", ctx); @@ -642,14 +642,14 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() 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::sort(dec.requestedTIMs.begin(), dec.requestedTIMs.end(), inputSpecLessThan); + std::sort(dec.providedTIMs.begin(), dec.providedTIMs.end(), 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(); @@ -658,7 +658,7 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() // FIXME: it should be made more generic, so it does not need replacement... // FIXME how can I make the lookup depend on DYN tables as well?? analysisCCDB->algorithm = PluginManager::loadAlgorithmFromPlugin("O2FrameworkCCDBSupport", "AnalysisCCDBFetcherPlugin", ctx); - AnalysisSupportHelpers::addMissingOutputsToBuilder(ac.analysisCCDBInputs, ac.requestedAODs, ac.requestedDYNs, *analysisCCDB); + AnalysisSupportHelpers::addMissingOutputsToBuilder(dec.analysisCCDBInputs, dec.requestedAODs, dec.requestedDYNs, *analysisCCDB); } if (writer != workflow.end()) { @@ -671,12 +671,12 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() for (auto& d : workflow) { d.inputs | views::partial_match_filter(AODOrigins) | - sinks::update_input_list{ac.requestedAODs}; + 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()) { @@ -694,22 +694,22 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() // 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(); + dec.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]); + dec.outputsInputsAOD.emplace_back(outputsInputs[ii]); } } } // file sink for any AOD output - if (!ac.outputsInputsAOD.empty()) { + if (!dec.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"); + dec.outputsInputsAOD.emplace_back("tfn", "TFN", "TFNumber"); + dec.outputsInputsAOD.emplace_back("tff", "TFF", "TFFilename"); workflow.push_back(AnalysisSupportHelpers::getGlobalAODSink(ctx)); } // Move the dummy sink at the end, if needed diff --git a/Framework/Core/src/WorkflowHelpers.cxx b/Framework/Core/src/WorkflowHelpers.cxx index 02141678fec7c..fdcdb6093a111 100644 --- a/Framework/Core/src/WorkflowHelpers.cxx +++ b/Framework/Core/src/WorkflowHelpers.cxx @@ -247,8 +247,8 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext 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; @@ -257,7 +257,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext auto& processor = workflow[wi]; auto name = processor.name; auto hash = runtime_hash(name.c_str()); - ac.outTskMap.push_back({hash, name}); + dec.outTskMap.push_back({hash, name}); std::string prefix = "internal-dpl-"; if (processor.inputs.empty() && processor.name.compare(0, prefix.size(), prefix) != 0) { @@ -336,16 +336,16 @@ 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}); } } @@ -353,16 +353,16 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext 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::find_if(dec.outObjHistMap.begin(), dec.outObjHistMap.end(), [&](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); } @@ -375,10 +375,10 @@ 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::sort(dec.requestedDYNs.begin(), dec.requestedDYNs.end(), inputSpecLessThan); + std::sort(dec.requestedTIMs.begin(), dec.requestedTIMs.end(), inputSpecLessThan); + std::sort(dec.providedDYNs.begin(), dec.providedDYNs.end(), outputSpecLessThan); + std::sort(dec.providedTIMs.begin(), dec.providedTIMs.end(), outputSpecLessThan); DataProcessorSpec indexBuilder{ "internal-dpl-aod-index-builder", @@ -386,15 +386,15 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext {}, PluginManager::loadAlgorithmFromPlugin("O2FrameworkOnDemandTablesSupport", "IndexTableBuilder", ctx), // readers::AODReaderHelpers::indexBuilderCallback(ctx), {}}; - 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::addMissingOutputsToBuilder(ac.analysisCCDBInputs, ac.requestedAODs, ac.requestedTIMs, analysisCCDBBackend); + AnalysisSupportHelpers::addMissingOutputsToBuilder(dec.analysisCCDBInputs, dec.requestedAODs, dec.requestedTIMs, analysisCCDBBackend); } - ac.requestedDYNs | views::filter_not_matching(ac.providedDYNs) | sinks::append_to{ac.spawnerInputs}; + dec.requestedDYNs | views::filter_not_matching(dec.providedDYNs) | sinks::append_to{dec.spawnerInputs}; DataProcessorSpec aodSpawner{ "internal-dpl-aod-spawner", @@ -402,9 +402,8 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext {}, PluginManager::loadAlgorithmFromPlugin("O2FrameworkOnDemandTablesSupport", "ExtendedTableSpawner", ctx), // readers::AODReaderHelpers::aodSpawnerCallback(ctx), {}}; - AnalysisSupportHelpers::addMissingOutputsToSpawner({}, ac.spawnerInputs, ac.requestedAODs, aodSpawner); - - AnalysisSupportHelpers::addMissingOutputsToReader(ac.providedAODs, ac.requestedAODs, aodReader); + AnalysisSupportHelpers::addMissingOutputsToSpawner({}, dec.spawnerInputs, dec.requestedAODs, aodSpawner); + AnalysisSupportHelpers::addMissingOutputsToReader(dec.providedAODs, dec.requestedAODs, aodReader); std::sort(requestedCCDBs.begin(), requestedCCDBs.end(), inputSpecLessThan); std::sort(providedCCDBs.begin(), providedCCDBs.end(), outputSpecLessThan); @@ -547,7 +546,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); } @@ -557,8 +556,8 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext /// Analyze all ouputs auto [outputsInputsTmp, isDanglingTmp] = analyzeOutputs(workflow); - ac.isDangling = isDanglingTmp; - ac.outputsInputs = outputsInputsTmp; + dec.isDangling = isDanglingTmp; + dec.outputsInputs = outputsInputsTmp; // create DataOutputDescriptor std::shared_ptr dod = AnalysisSupportHelpers::getDataOutputDirector(ctx); @@ -566,28 +565,28 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext // 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]); + 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 (ac.outputsInputsAOD.size() > 0) { + if (dec.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"}); + dec.outputsInputsAOD.emplace_back(InputSpec{"tfn", "TFN", "TFNumber"}); + dec.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 { + auto it = std::find_if(dec.outputsInputs.begin(), dec.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; + size_t ii = std::distance(dec.outputsInputs.begin(), it); + dec.isDangling[ii] = false; } workflow.insert(workflow.end(), extraSpecs.begin(), extraSpecs.end()); @@ -595,20 +594,20 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext // 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; From 027cad2deaa056cd2bca7c465bd1f74309005ed1 Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Tue, 2 Dec 2025 09:48:09 +0100 Subject: [PATCH 119/701] Propagate dangling edges context to init context and delay algo loading --- .../AnalysisSupport/src/AODReaderHelpers.cxx | 16 ++++---- .../AnalysisSupport/src/AODReaderHelpers.h | 4 +- Framework/Core/src/ArrowSupport.cxx | 20 ++++++---- Framework/Core/src/WorkflowHelpers.cxx | 38 +++++-------------- Framework/Core/src/runDataProcessing.cxx | 5 +++ 5 files changed, 37 insertions(+), 46 deletions(-) diff --git a/Framework/AnalysisSupport/src/AODReaderHelpers.cxx b/Framework/AnalysisSupport/src/AODReaderHelpers.cxx index 045ef072a3040..7f08dd0b36a64 100644 --- a/Framework/AnalysisSupport/src/AODReaderHelpers.cxx +++ b/Framework/AnalysisSupport/src/AODReaderHelpers.cxx @@ -79,12 +79,12 @@ struct Buildable { } // namespace -AlgorithmSpec AODReaderHelpers::indexBuilderCallback(ConfigContext const& ctx) +AlgorithmSpec AODReaderHelpers::indexBuilderCallback(ConfigContext const& /*ctx*/) { - auto& ac = ctx.services().get(); - return AlgorithmSpec::InitCallback{[requested = ac.requestedIDXs](InitContext& /*ic*/) { + return AlgorithmSpec::InitCallback{[](InitContext& ic) { + auto const& requested = ic.services().get().requestedIDXs; std::vector buildables; - for (auto& i : requested) { + for (auto const& i : requested) { buildables.emplace_back(i); } std::vector builders; @@ -181,12 +181,12 @@ struct Spawnable { } // namespace -AlgorithmSpec AODReaderHelpers::aodSpawnerCallback(ConfigContext const& ctx) +AlgorithmSpec AODReaderHelpers::aodSpawnerCallback(ConfigContext const& /*ctx*/) { - auto& ac = ctx.services().get(); - return AlgorithmSpec::InitCallback{[requested = ac.spawnerInputs](InitContext& /*ic*/) { + return AlgorithmSpec::InitCallback{[](InitContext& ic) { + auto const& requested = ic.services().get().spawnerInputs; std::vector spawnables; - for (auto& i : requested) { + for (auto const& i : requested) { spawnables.emplace_back(i); } std::vector spawners; diff --git a/Framework/AnalysisSupport/src/AODReaderHelpers.h b/Framework/AnalysisSupport/src/AODReaderHelpers.h index 197907ca3ccb1..848ef6b696713 100644 --- a/Framework/AnalysisSupport/src/AODReaderHelpers.h +++ b/Framework/AnalysisSupport/src/AODReaderHelpers.h @@ -20,8 +20,8 @@ namespace o2::framework::readers struct AODReaderHelpers { static AlgorithmSpec rootFileReaderCallback(); - static AlgorithmSpec aodSpawnerCallback(ConfigContext const& ctx); - static AlgorithmSpec indexBuilderCallback(ConfigContext const& ctx); + static AlgorithmSpec aodSpawnerCallback(ConfigContext const& /*ctx*/); + static AlgorithmSpec indexBuilderCallback(ConfigContext const& /*ctx*/); }; } // namespace o2::framework::readers diff --git a/Framework/Core/src/ArrowSupport.cxx b/Framework/Core/src/ArrowSupport.cxx index ee4275281ab31..c0280b144e146 100644 --- a/Framework/Core/src/ArrowSupport.cxx +++ b/Framework/Core/src/ArrowSupport.cxx @@ -13,6 +13,7 @@ #include "Framework/ArrowContext.h" #include "Framework/ArrowTableSlicingCache.h" #include "Framework/DataProcessor.h" +#include "Framework/CommonDataProcessors.h" #include "Framework/DataProcessingStats.h" #include "Framework/ServiceRegistry.h" #include "Framework/ConfigContext.h" @@ -609,9 +610,9 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() // 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 = PluginManager::loadAlgorithmFromPlugin("O2FrameworkOnDemandTablesSupport", "IndexTableBuilder", ctx); // readers::AODReaderHelpers::indexBuilderCallback(ctx); + + // load real AlgorithmSpec before deployment + builder->algorithm = PluginManager::loadAlgorithmFromPlugin("O2FrameworkOnDemandTablesSupport", "IndexTableBuilder", ctx); AnalysisSupportHelpers::addMissingOutputsToBuilder(dec.requestedIDXs, dec.requestedAODs, dec.requestedDYNs, *builder); } @@ -634,10 +635,10 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() // recreate inputs and outputs spawner->outputs.clear(); spawner->inputs.clear(); - AnalysisSupportHelpers::addMissingOutputsToSpawner({}, dec.spawnerInputs, dec.requestedAODs, *spawner); - // replace AlgorithmSpec - // FIXME: it should be made more generic, so it does not need replacement... + + // load real AlgorithmSpec before deployment spawner->algorithm = PluginManager::loadAlgorithmFromPlugin("O2FrameworkOnDemandTablesSupport", "ExtendedTableSpawner", ctx); + AnalysisSupportHelpers::addMissingOutputsToSpawner({}, dec.spawnerInputs, dec.requestedAODs, *spawner); } if (analysisCCDB != workflow.end()) { @@ -654,8 +655,7 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() // 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::addMissingOutputsToBuilder(dec.analysisCCDBInputs, dec.requestedAODs, dec.requestedDYNs, *analysisCCDB); @@ -682,6 +682,10 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() if (reader->outputs.empty()) { // nothing to read workflow.erase(reader); + } else { + // load reader algorithm before deployment + auto&& algo = PluginManager::loadAlgorithmFromPlugin("O2FrameworkAnalysisSupport", "ROOTFileReader", ctx); + reader->algorithm = CommonDataProcessors::wrapWithTimesliceConsumption(algo); } } diff --git a/Framework/Core/src/WorkflowHelpers.cxx b/Framework/Core/src/WorkflowHelpers.cxx index fdcdb6093a111..fd9099e1aa24e 100644 --- a/Framework/Core/src/WorkflowHelpers.cxx +++ b/Framework/Core/src/WorkflowHelpers.cxx @@ -156,6 +156,7 @@ int defaultConditionQueryRateMultiplier() void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext& ctx) { + int rateLimitingIPCID = std::stoi(ctx.options().get("timeframes-rate-limit-ipcid")); DataProcessorSpec ccdbBackend{ .name = "internal-dpl-ccdb-backend", .outputs = {}, @@ -230,23 +231,6 @@ 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 DanglingEdgesContext)); auto& dec = ctx.services().get(); @@ -274,7 +258,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext // 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()); @@ -384,7 +368,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext "internal-dpl-aod-index-builder", {}, {}, - PluginManager::loadAlgorithmFromPlugin("O2FrameworkOnDemandTablesSupport", "IndexTableBuilder", ctx), // readers::AODReaderHelpers::indexBuilderCallback(ctx), + AlgorithmSpec::dummyAlgorithm(), // real algorithm will be set in adjustTopology {}}; AnalysisSupportHelpers::addMissingOutputsToBuilder(dec.requestedIDXs, dec.requestedAODs, dec.requestedDYNs, indexBuilder); @@ -400,7 +384,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext "internal-dpl-aod-spawner", {}, {}, - PluginManager::loadAlgorithmFromPlugin("O2FrameworkOnDemandTablesSupport", "ExtendedTableSpawner", ctx), // readers::AODReaderHelpers::aodSpawnerCallback(ctx), + AlgorithmSpec::dummyAlgorithm(), // real algorithm will be set in adjustTopology {}}; AnalysisSupportHelpers::addMissingOutputsToSpawner({}, dec.spawnerInputs, dec.requestedAODs, aodSpawner); AnalysisSupportHelpers::addMissingOutputsToReader(dec.providedAODs, dec.requestedAODs, aodReader); @@ -431,13 +415,11 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext 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 - auto&& algo = PluginManager::loadAlgorithmFromPlugin("O2FrameworkAnalysisSupport", "ROOTFileReader", ctx); - aodReader.algorithm = CommonDataProcessors::wrapWithTimesliceConsumption(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 - auto algo = AlgorithmSpec{ + // AODs are being injected on-the-fly, add error-handler reader + aodReader.algorithm = AlgorithmSpec{ adaptStateful( [outputs = aodReader.outputs](DeviceSpec const&) { LOGP(warn, "Workflow with injected AODs has unsatisfied inputs:"); @@ -448,7 +430,6 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext // to ensure the output type for adaptStateful return adaptStateless([](DataAllocator&) {}); })}; - aodReader.algorithm = CommonDataProcessors::wrapWithTimesliceConsumption(algo); } auto concrete = DataSpecUtils::asConcreteDataMatcher(aodReader.inputs[0]); timer.outputs.emplace_back(concrete.origin, concrete.description, concrete.subSpec, Lifetime::Enumeration); @@ -533,9 +514,6 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext // 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); } @@ -637,6 +615,10 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext 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)); } } diff --git a/Framework/Core/src/runDataProcessing.cxx b/Framework/Core/src/runDataProcessing.cxx index c36b1deadeefb..14bdb2d8c72d9 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 @@ -1016,6 +1017,7 @@ void doDefaultWorkflowTerminationHook() } int doChild(int argc, char** argv, ServiceRegistry& serviceRegistry, + DanglingEdgesContext& danglingEdgesContext, RunningWorkflowInfo const& runningWorkflow, RunningDeviceRef ref, DriverConfig const& driverConfig, @@ -1078,6 +1080,7 @@ int doChild(int argc, char** argv, ServiceRegistry& serviceRegistry, &spec, "aEvaluator, &serviceRegistry, + &danglingEdgesContext, &deviceState, &deviceProxy, &processingPolicies, @@ -1101,6 +1104,7 @@ int doChild(int argc, char** argv, ServiceRegistry& serviceRegistry, 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); @@ -1953,6 +1957,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, From 91a991f6baa4c002180612a7586edfa302ff940b Mon Sep 17 00:00:00 2001 From: Piotr Konopka Date: Tue, 16 Dec 2025 16:42:24 +0100 Subject: [PATCH 120/701] DPL: allow to disable oldest possible timeframe propagation with a label This allows to disable all DomainInfoHeader propagation with a corresponding DataProcessorLabel. It addresses the issue reported in QC-1320, where remote QC workflows were getting flooded with a DIH for each QC task instance in the setup. --- Framework/Core/CMakeLists.txt | 1 + .../Core/include/Framework/CommonLabels.h | 26 +++++++++++++++++++ Framework/Core/src/CommonLabels.cxx | 19 ++++++++++++++ Framework/Core/src/CommonServices.cxx | 7 +++++ Framework/Core/src/DataProcessingHelpers.cxx | 7 +++++ Framework/Core/src/DecongestionService.h | 2 ++ 6 files changed, 62 insertions(+) create mode 100644 Framework/Core/include/Framework/CommonLabels.h create mode 100644 Framework/Core/src/CommonLabels.cxx diff --git a/Framework/Core/CMakeLists.txt b/Framework/Core/CMakeLists.txt index fe8a91eaa0449..1daba5dbc9798 100644 --- a/Framework/Core/CMakeLists.txt +++ b/Framework/Core/CMakeLists.txt @@ -108,6 +108,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 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/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/CommonServices.cxx b/Framework/Core/src/CommonServices.cxx index 3aa46269bdd7e..f786d99fd2c0d 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" @@ -604,6 +605,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}; diff --git a/Framework/Core/src/DataProcessingHelpers.cxx b/Framework/Core/src/DataProcessingHelpers.cxx index 9c53bbf8b2c10..aea682a8d00c3 100644 --- a/Framework/Core/src/DataProcessingHelpers.cxx +++ b/Framework/Core/src/DataProcessingHelpers.cxx @@ -34,6 +34,7 @@ #include "Framework/DeviceStateEnums.h" #include "Headers/DataHeader.h" #include "Framework/DataProcessingHeader.h" +#include "DecongestionService.h" #include #include @@ -83,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; } @@ -93,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; } 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; From 9bbf6ecca2da073db51497ab83d380e4cff355e5 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 7 Jan 2026 10:53:36 +0100 Subject: [PATCH 121/701] DPL: more refactoring of the forwarding code Use a single helper function to improve readability. --- .../include/Framework/DataProcessingHelpers.h | 5 +- Framework/Core/src/DataProcessingDevice.cxx | 6 +- Framework/Core/src/DataProcessingHelpers.cxx | 158 +++++++----------- Framework/Core/test/test_ForwardInputs.cxx | 62 ++----- 4 files changed, 84 insertions(+), 147 deletions(-) diff --git a/Framework/Core/include/Framework/DataProcessingHelpers.h b/Framework/Core/include/Framework/DataProcessingHelpers.h index be02aae5d2f69..34bb87613d920 100644 --- a/Framework/Core/include/Framework/DataProcessingHelpers.h +++ b/Framework/Core/include/Framework/DataProcessingHelpers.h @@ -53,8 +53,9 @@ struct DataProcessingHelpers { /// 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 routeForwardedMessages(FairMQDeviceProxy& proxy, TimesliceSlot slot, std::vector& currentSetOfInputs, - TimesliceIndex::OldestOutputInfo oldestTimeslice, bool copy, bool consume); + static std::vector routeForwardedMessages(FairMQDeviceProxy& proxy, + std::vector& currentSetOfInputs, + const bool copyByDefault, bool consume); }; } // namespace o2::framework #endif // O2_FRAMEWORK_DATAPROCESSINGHELPERS_H_ diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index 40f1061e60332..63c333561f24e 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -588,10 +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(); - auto forwardedParts = DataProcessingHelpers::routeForwardedMessages(proxy, slot, currentSetOfInputs, oldestTimeslice, copy, consume); O2_SIGNPOST_ID_GENERATE(sid, forwarding); - O2_SIGNPOST_EVENT_EMIT(forwarding, sid, "forwardInputs", "Forwarding %zu messages", forwardedParts.size()); + 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::routeForwardedMessages(proxy, currentSetOfInputs, copy, consume); + for (int fi = 0; fi < proxy.getNumForwardChannels(); fi++) { if (forwardedParts[fi].Size() == 0) { continue; diff --git a/Framework/Core/src/DataProcessingHelpers.cxx b/Framework/Core/src/DataProcessingHelpers.cxx index aea682a8d00c3..76730e9edab4e 100644 --- a/Framework/Core/src/DataProcessingHelpers.cxx +++ b/Framework/Core/src/DataProcessingHelpers.cxx @@ -228,129 +228,99 @@ TransitionHandlingState DataProcessingHelpers::updateStateTransition(ServiceRegi } } -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 dh = o2::header::get(header); - if (!dh) { - return false; - } - bool retval = !o2::header::get(header) && - !o2::header::get(header) && - o2::header::get(header); - // DataHeader is there. Complain if we have unexpected headers present / missing - if (!retval) { - LOGP(error, "Dropping data because of malformed header structure"); - } - return retval; -}; - -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; -}; - -std::vector DataProcessingHelpers::routeForwardedMessages(FairMQDeviceProxy& proxy, TimesliceSlot slot, std::vector& currentSetOfInputs, - TimesliceIndex::OldestOutputInfo oldestTimeslice, bool copy, bool consume) +auto DataProcessingHelpers::routeForwardedMessages(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; forwardedParts.resize(proxy.getNumForwards()); - std::vector cachedForwardingChoices{}; + std::vector forwardingChoices{}; 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" : ""); 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(); + forwardingChoices.clear(); - for (size_t pi = 0; pi < currentSetOfInputs[ii].size(); ++pi) { - auto& messageSet = currentSetOfInputs[ii]; + for (size_t pi = 0; pi < messageSet.size(); ++pi) { auto& header = messageSet.header(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) { + continue; + } + auto dih = o2::header::get(header->GetData()); + if (dih) { + continue; + } + auto sih = o2::header::get(header->GetData()); + if (sih) { + 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" : ""); + continue; + } + auto& payload = messageSet.payload(pi); - auto total = messageSet.getNumberOfPayloads(pi); - if (!toBeforwardedMessageSet(cachedForwardingChoices, proxy, header, payload, total, consume)) { + 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); 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; + // 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 + // - 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. + if (dh->splitPayloadIndex == 0 || dh->splitPayloadParts <= 1 || messageSet.getNumberOfPayloads(pi) > 0) { + proxy.getMatchingForwardChannelIndexes(forwardingChoices, *dh, dph->startTime); } - auto* dh = o2::header::get(header->GetData()); - auto* dph = o2::header::get(header->GetData()); - if (copy) { - for (auto& cachedForwardingChoice : cachedForwardingChoices) { + if (forwardingChoices.empty()) { + // Nothing to forward go to the next messageset + 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) { 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); + fmt::format("{}/{}/{}@timeslice:{} tfCounter:{}", dh->dataOrigin, dh->dataDescription, dh->subSpecification, dph->startTime, dh->tfCounter).c_str(), choice.value); newHeader->Copy(*header); - forwardedParts[cachedForwardingChoice.value].AddPart(std::move(newHeader)); + forwardedParts[choice.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)); + forwardedParts[choice.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))); + fmt::format("{}/{}/{}@timeslice:{} tfCounter:{}", dh->dataOrigin, dh->dataDescription, dh->subSpecification, dph->startTime, dh->tfCounter).c_str(), forwardingChoices.back().value); + forwardedParts[forwardingChoices.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))); + forwardedParts[forwardingChoices.back().value].AddPart(std::move(messageSet.payload(pi, payloadIndex))); } } } diff --git a/Framework/Core/test/test_ForwardInputs.cxx b/Framework/Core/test/test_ForwardInputs.cxx index b1f42fb0398ca..5add90ec8f18e 100644 --- a/Framework/Core/test/test_ForwardInputs.cxx +++ b/Framework/Core/test/test_ForwardInputs.cxx @@ -15,8 +15,6 @@ #include "Framework/DataProcessingHelpers.h" #include "Framework/SourceInfoHeader.h" #include "Framework/DomainInfoHeader.h" -#include "Framework/ServiceRegistry.h" -#include "Framework/ServiceRegistryRef.h" #include "Framework/Signpost.h" #include "Framework/MessageSet.h" #include "Framework/FairMQDeviceProxy.h" @@ -45,11 +43,9 @@ TEST_CASE("ForwardInputsEmpty") bool copyByDefault = true; FairMQDeviceProxy proxy; - TimesliceIndex::OldestOutputInfo oldestTimeslice{.timeslice = {1}}; std::vector currentSetOfInputs; - TimesliceSlot slot{0}; - auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, slot, currentSetOfInputs, oldestTimeslice, copyByDefault, consume); + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, currentSetOfInputs, copyByDefault, consume); REQUIRE(result.empty()); } @@ -88,7 +84,6 @@ TEST_CASE("ForwardInputsSingleMessageSingleRoute") proxy.bind({}, {}, routes, findChannelByName, nullptr); - TimesliceIndex::OldestOutputInfo oldestTimeslice{.timeslice = {0}}; std::vector currentSetOfInputs; MessageSet messageSet; @@ -100,9 +95,7 @@ TEST_CASE("ForwardInputsSingleMessageSingleRoute") REQUIRE(messageSet.size() == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); - TimesliceSlot slot{0}; - - auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, slot, currentSetOfInputs, oldestTimeslice, copyByDefault, consume); + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, currentSetOfInputs, copyByDefault, consume); REQUIRE(result.size() == 1); // One route REQUIRE(result[0].Size() == 2); // Two messages for that route } @@ -141,7 +134,6 @@ TEST_CASE("ForwardInputsSingleMessageSingleRouteNoConsume") proxy.bind({}, {}, routes, findChannelByName, nullptr); - TimesliceIndex::OldestOutputInfo oldestTimeslice{.timeslice = {0}}; std::vector currentSetOfInputs; MessageSet messageSet; @@ -154,9 +146,7 @@ TEST_CASE("ForwardInputsSingleMessageSingleRouteNoConsume") REQUIRE(messageSet.size() == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); - TimesliceSlot slot{0}; - - auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, slot, currentSetOfInputs, oldestTimeslice, copyByDefault, true); + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(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. } @@ -199,7 +189,6 @@ TEST_CASE("ForwardInputsSingleMessageSingleRouteAtEOS") proxy.bind({}, {}, routes, findChannelByName, nullptr); - TimesliceIndex::OldestOutputInfo oldestTimeslice{.timeslice = {0}}; std::vector currentSetOfInputs; MessageSet messageSet; @@ -212,11 +201,10 @@ TEST_CASE("ForwardInputsSingleMessageSingleRouteAtEOS") REQUIRE(messageSet.size() == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); - TimesliceSlot slot{0}; - auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, slot, currentSetOfInputs, oldestTimeslice, copyByDefault, consume); + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(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 + 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); @@ -260,7 +248,6 @@ TEST_CASE("ForwardInputsSingleMessageSingleRouteWithOldestPossible") proxy.bind({}, {}, routes, findChannelByName, nullptr); - TimesliceIndex::OldestOutputInfo oldestTimeslice{.timeslice = {0}}; std::vector currentSetOfInputs; MessageSet messageSet; @@ -273,9 +260,7 @@ TEST_CASE("ForwardInputsSingleMessageSingleRouteWithOldestPossible") REQUIRE(messageSet.size() == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); - TimesliceSlot slot{0}; - - auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, slot, currentSetOfInputs, oldestTimeslice, copyByDefault, consume); + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(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 @@ -329,7 +314,6 @@ TEST_CASE("ForwardInputsSingleMessageMultipleRoutes") proxy.bind({}, {}, routes, findChannelByName, nullptr); - TimesliceIndex::OldestOutputInfo oldestTimeslice{.timeslice = {0}}; std::vector currentSetOfInputs; MessageSet messageSet; @@ -341,9 +325,7 @@ TEST_CASE("ForwardInputsSingleMessageMultipleRoutes") REQUIRE(messageSet.size() == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); - TimesliceSlot slot{0}; - - auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, slot, currentSetOfInputs, oldestTimeslice, copyByDefault, consume); + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(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 @@ -395,7 +377,6 @@ TEST_CASE("ForwardInputsSingleMessageMultipleRoutesExternals") proxy.bind({}, {}, routes, findChannelByName, nullptr); - TimesliceIndex::OldestOutputInfo oldestTimeslice{.timeslice = {0}}; std::vector currentSetOfInputs; MessageSet messageSet; @@ -407,9 +388,7 @@ TEST_CASE("ForwardInputsSingleMessageMultipleRoutesExternals") REQUIRE(messageSet.size() == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); - TimesliceSlot slot{0}; - - auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, slot, currentSetOfInputs, oldestTimeslice, copyByDefault, consume); + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(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); // @@ -468,7 +447,6 @@ TEST_CASE("ForwardInputsMultiMessageMultipleRoutes") proxy.bind({}, {}, routes, findChannelByName, nullptr); - TimesliceIndex::OldestOutputInfo oldestTimeslice{.timeslice = {0}}; std::vector currentSetOfInputs; auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); @@ -488,9 +466,7 @@ TEST_CASE("ForwardInputsMultiMessageMultipleRoutes") currentSetOfInputs.emplace_back(std::move(messageSet2)); REQUIRE(currentSetOfInputs.size() == 2); - TimesliceSlot slot{0}; - - auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, slot, currentSetOfInputs, oldestTimeslice, copyByDefault, consume); + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, currentSetOfInputs, copyByDefault, consume); REQUIRE(result.size() == 2); // Two routes REQUIRE(result[0].Size() == 2); // REQUIRE(result[1].Size() == 2); // @@ -542,7 +518,6 @@ TEST_CASE("ForwardInputsSingleMessageMultipleRoutesOnlyOneMatches") proxy.bind({}, {}, routes, findChannelByName, nullptr); - TimesliceIndex::OldestOutputInfo oldestTimeslice{.timeslice = {0}}; std::vector currentSetOfInputs; MessageSet messageSet; @@ -554,9 +529,7 @@ TEST_CASE("ForwardInputsSingleMessageMultipleRoutesOnlyOneMatches") REQUIRE(messageSet.size() == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); - TimesliceSlot slot{0}; - - auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, slot, currentSetOfInputs, oldestTimeslice, copyByDefault, consume); + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(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 @@ -615,7 +588,6 @@ TEST_CASE("ForwardInputsSplitPayload") proxy.bind({}, {}, routes, findChannelByName, nullptr); - TimesliceIndex::OldestOutputInfo oldestTimeslice{.timeslice = {0}}; std::vector currentSetOfInputs; MessageSet messageSet; @@ -639,9 +611,7 @@ TEST_CASE("ForwardInputsSplitPayload") REQUIRE(messageSet.size() == 2); currentSetOfInputs.emplace_back(std::move(messageSet)); - TimesliceSlot slot{0}; - - auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, slot, currentSetOfInputs, oldestTimeslice, copyByDefault, consume); + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, currentSetOfInputs, copyByDefault, consume); REQUIRE(result.size() == 2); // Two routes CHECK(result[0].Size() == 2); // No messages on this route CHECK(result[1].Size() == 5); // FIXME: Multipart matching has side effects also for the elements @@ -677,7 +647,6 @@ TEST_CASE("ForwardInputEOSSingleRoute") proxy.bind({}, {}, routes, findChannelByName, nullptr); - TimesliceIndex::OldestOutputInfo oldestTimeslice{.timeslice = {0}}; std::vector currentSetOfInputs; MessageSet messageSet; @@ -689,9 +658,7 @@ TEST_CASE("ForwardInputEOSSingleRoute") REQUIRE(messageSet.size() == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); - TimesliceSlot slot{0}; - - auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, slot, currentSetOfInputs, oldestTimeslice, copyByDefault, consume); + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, currentSetOfInputs, copyByDefault, consume); REQUIRE(result.size() == 1); // One route REQUIRE(result[0].Size() == 0); // Oldest possible timeframe should not be forwarded } @@ -725,7 +692,6 @@ TEST_CASE("ForwardInputOldestPossibleSingleRoute") proxy.bind({}, {}, routes, findChannelByName, nullptr); - TimesliceIndex::OldestOutputInfo oldestTimeslice{.timeslice = {0}}; std::vector currentSetOfInputs; MessageSet messageSet; @@ -737,9 +703,7 @@ TEST_CASE("ForwardInputOldestPossibleSingleRoute") REQUIRE(messageSet.size() == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); - TimesliceSlot slot{0}; - - auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, slot, currentSetOfInputs, oldestTimeslice, copyByDefault, consume); + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, currentSetOfInputs, copyByDefault, consume); REQUIRE(result.size() == 1); // One route REQUIRE(result[0].Size() == 0); // Oldest possible timeframe should not be forwarded } From 0e958cabd29f9dd6cba3fb1c20c318067807714b Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 7 Jan 2026 10:53:36 +0100 Subject: [PATCH 122/701] DPL: fix routing issues in forwarding If one (header, payload, ...) tuple in a MessageSet was to be copied, all the subsequent ones would have been copied. If one (header, payload, ...) tuple got redirected to more than one destination, all the subsequent ones would have been redirected there. --- Framework/Core/src/DataProcessingHelpers.cxx | 2 +- Framework/Core/test/test_ForwardInputs.cxx | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/Framework/Core/src/DataProcessingHelpers.cxx b/Framework/Core/src/DataProcessingHelpers.cxx index 76730e9edab4e..90dcee52d73da 100644 --- a/Framework/Core/src/DataProcessingHelpers.cxx +++ b/Framework/Core/src/DataProcessingHelpers.cxx @@ -240,7 +240,6 @@ auto DataProcessingHelpers::routeForwardedMessages(FairMQDeviceProxy& proxy, for (size_t ii = 0, ie = currentSetOfInputs.size(); ii < ie; ++ii) { auto& messageSet = currentSetOfInputs[ii]; - forwardingChoices.clear(); for (size_t pi = 0; pi < messageSet.size(); ++pi) { auto& header = messageSet.header(pi); @@ -291,6 +290,7 @@ auto DataProcessingHelpers::routeForwardedMessages(FairMQDeviceProxy& proxy, // - 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. if (dh->splitPayloadIndex == 0 || dh->splitPayloadParts <= 1 || messageSet.getNumberOfPayloads(pi) > 0) { + forwardingChoices.clear(); proxy.getMatchingForwardChannelIndexes(forwardingChoices, *dh, dph->startTime); } diff --git a/Framework/Core/test/test_ForwardInputs.cxx b/Framework/Core/test/test_ForwardInputs.cxx index 5add90ec8f18e..7ddbc831edad2 100644 --- a/Framework/Core/test/test_ForwardInputs.cxx +++ b/Framework/Core/test/test_ForwardInputs.cxx @@ -614,8 +614,7 @@ TEST_CASE("ForwardInputsSplitPayload") auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, currentSetOfInputs, copyByDefault, consume); REQUIRE(result.size() == 2); // Two routes CHECK(result[0].Size() == 2); // No messages on this route - CHECK(result[1].Size() == 5); // FIXME: Multipart matching has side effects also for the elements - // CHECK(result[1].Size() == 3); // FIXME: the correct forwarding is that only the multipart goes to the same route + CHECK(result[1].Size() == 3); } TEST_CASE("ForwardInputEOSSingleRoute") From c426fe5cb1ad85f34394d53e6c15a19283cae76e Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Mon, 10 Nov 2025 10:30:15 +0100 Subject: [PATCH 123/701] ITS: remove CookedTracker Signed-off-by: Felix Schlepper --- .../ITSMFT/ITS/reconstruction/CMakeLists.txt | 7 +- .../ITSReconstruction/CookedConfigParam.h | 42 - .../include/ITSReconstruction/CookedTracker.h | 267 ------ .../reconstruction/src/CookedConfigParam.cxx | 22 - .../ITS/reconstruction/src/CookedTracker.cxx | 865 ------------------ .../reconstruction/src/CookedTrackerLinkDef.h | 26 - .../src/ITSReconstructionLinkDef.h | 4 - Detectors/ITSMFT/ITS/workflow/CMakeLists.txt | 1 - .../include/ITSWorkflow/CookedTrackerSpec.h | 75 -- .../include/ITSWorkflow/RecoWorkflow.h | 2 +- .../ITS/workflow/src/CookedTrackerSpec.cxx | 327 ------- .../ITSMFT/ITS/workflow/src/RecoWorkflow.cxx | 60 +- .../ITS/workflow/src/its-reco-workflow.cxx | 5 +- macro/CMakeLists.txt | 34 - macro/run_trac_its.C | 222 ----- 15 files changed, 30 insertions(+), 1929 deletions(-) delete mode 100644 Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/CookedConfigParam.h delete mode 100644 Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/CookedTracker.h delete mode 100644 Detectors/ITSMFT/ITS/reconstruction/src/CookedConfigParam.cxx delete mode 100644 Detectors/ITSMFT/ITS/reconstruction/src/CookedTracker.cxx delete mode 100644 Detectors/ITSMFT/ITS/reconstruction/src/CookedTrackerLinkDef.h delete mode 100644 Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/CookedTrackerSpec.h delete mode 100644 Detectors/ITSMFT/ITS/workflow/src/CookedTrackerSpec.cxx delete mode 100644 macro/run_trac_its.C diff --git a/Detectors/ITSMFT/ITS/reconstruction/CMakeLists.txt b/Detectors/ITSMFT/ITS/reconstruction/CMakeLists.txt index 3e1544c65b9de..a5004418599e4 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/reconstruction/CMakeLists.txt @@ -11,8 +11,6 @@ o2_add_library(ITSReconstruction SOURCES src/ClustererTask.cxx - src/CookedTracker.cxx - src/CookedConfigParam.cxx src/RecoGeomHelper.cxx src/FastMultEstConfig.cxx src/FastMultEst.cxx @@ -24,9 +22,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 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/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/CookedConfigParam.cxx b/Detectors/ITSMFT/ITS/reconstruction/src/CookedConfigParam.cxx deleted file mode 100644 index 81087744b04a9..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/src/CookedConfigParam.cxx +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "ITSReconstruction/CookedConfigParam.h" - -namespace o2 -{ -namespace its -{ -static auto& sITSCookedTrackerParam = o2::its::CookedConfigParam::Instance(); - -O2ParamImpl(o2::its::CookedConfigParam); -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/CookedTracker.cxx b/Detectors/ITSMFT/ITS/reconstruction/src/CookedTracker.cxx deleted file mode 100644 index 5c804f6705dfd..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/src/CookedTracker.cxx +++ /dev/null @@ -1,865 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file CookedTracker.cxx -/// \brief Implementation of the "Cooked Matrix" ITS tracker -/// \author iouri.belikov@cern.ch - -//------------------------------------------------------------------------- -// A stand-alone ITS tracker -// The pattern recongintion based on the "cooked covariance" approach -//------------------------------------------------------------------------- -#include -#include -#include - -#include -#include - -#include - -#include "CommonConstants/MathConstants.h" -#include "DetectorsBase/Propagator.h" -#include "Field/MagneticField.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "DataFormatsITSMFT/TopologyDictionary.h" -#include "ITSReconstruction/CookedTracker.h" -#include "MathUtils/Utils.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" - -using namespace o2::its; -using namespace o2::itsmft; -using namespace o2::constants::math; -using o2::field::MagneticField; -using Label = o2::MCCompLabel; - -/*** Tracking parameters ***/ -// seed "windows" in z and phi: makeSeeds -Float_t CookedTracker::gzWin = 0.33; -Float_t CookedTracker::gminPt = 0.05; -Float_t CookedTracker::mMostProbablePt = o2::track::kMostProbablePt; -// Maximal accepted impact parameters for the seeds -Float_t CookedTracker::gmaxDCAxy = 3.; -Float_t CookedTracker::gmaxDCAz = 3.; -// Layers for the seeding -Int_t CookedTracker::gSeedingLayer1 = 6; -Int_t CookedTracker::gSeedingLayer2 = 4; -Int_t CookedTracker::gSeedingLayer3 = 5; -// Space point resolution -Float_t CookedTracker::gSigma2 = 0.0005 * 0.0005; -// Max accepted chi2 -Float_t CookedTracker::gmaxChi2PerCluster = 20.; -Float_t CookedTracker::gmaxChi2PerTrack = 30.; -// Tracking "road" from layer to layer -Float_t CookedTracker::gRoadY = 0.2; -Float_t CookedTracker::gRoadZ = 0.3; -// Minimal number of attached clusters -Int_t CookedTracker::gminNumberOfClusters = 4; - -const float kPI = 3.14159f; -const float k2PI = 2 * kPI; - -//************************************************ -// TODO: -//************************************************ -// Seeding: -// Precalculate cylidnrical (r,phi) for the clusters; -// use exact r's for the clusters - -CookedTracker::Layer CookedTracker::sLayers[CookedTracker::kNLayers]; - -CookedTracker::CookedTracker(Int_t n) : mNumOfThreads(n), mBz(0.) -{ - //-------------------------------------------------------------------- - // This default constructor needs to be provided - //-------------------------------------------------------------------- - const Double_t klRadius[7] = {2.34, 3.15, 3.93, 19.61, 24.55, 34.39, 39.34}; // tdr6 - - for (Int_t i = 0; i < kNLayers; i++) { - sLayers[i].setR(klRadius[i]); - } -} - -//__________________________________________________________________________ -Label CookedTracker::cookLabel(TrackITSExt& t, Float_t wrong) const -{ - //-------------------------------------------------------------------- - // This function "cooks" a track label. - // A label<0 indicates that some of the clusters are wrongly assigned. - //-------------------------------------------------------------------- - Int_t noc = t.getNumberOfClusters(); - std::map labelOccurence; - - for (int i = noc; i--;) { - const Cluster* c = getCluster(t.getClusterIndex(i)); - Int_t idx = c - &mClusterCache[0] + mFirstInFrame; // Index of this cluster in event - auto labels = mClsLabels->getLabels(idx); - - for (auto lab : labels) { // check all labels of the cluster - if (lab.isEmpty()) { - break; // all following labels will be empty also - } - // was this label already accounted for ? - labelOccurence[lab]++; - } - } - Label lab; - Int_t maxL = 0; // find most encountered label - for (auto [label, count] : labelOccurence) { - if (count <= maxL) { - continue; - } - maxL = count; - lab = label; - } - - if ((1. - Float_t(maxL) / noc) > wrong) { - // change the track ID to negative - lab.setFakeFlag(); - } - // t.SetFakeRatio((1.- Float_t(maxL)/noc)); - return lab; -} - -Double_t CookedTracker::getBz() const -{ - return mBz; -} - -static Double_t f1(Double_t x1, Double_t y1, Double_t x2, Double_t y2, Double_t x3, Double_t y3) -{ - //----------------------------------------------------------------- - // Initial approximation of the track curvature - //----------------------------------------------------------------- - Double_t d = (x2 - x1) * (y3 - y2) - (x3 - x2) * (y2 - y1); - Double_t a = - 0.5 * ((y3 - y2) * (y2 * y2 - y1 * y1 + x2 * x2 - x1 * x1) - (y2 - y1) * (y3 * y3 - y2 * y2 + x3 * x3 - x2 * x2)); - Double_t b = - 0.5 * ((x2 - x1) * (y3 * y3 - y2 * y2 + x3 * x3 - x2 * x2) - (x3 - x2) * (y2 * y2 - y1 * y1 + x2 * x2 - x1 * x1)); - - Double_t xr = TMath::Abs(d / (d * x1 - a)), yr = TMath::Abs(d / (d * y1 - b)); - - Double_t crv = xr * yr / sqrt(xr * xr + yr * yr); - if (d > 0) { - crv = -crv; - } - - return crv; -} - -static Double_t f2(Double_t x1, Double_t y1, Double_t x2, Double_t y2, Double_t x3, Double_t y3) -{ - //----------------------------------------------------------------- - // Initial approximation of the x-coordinate of the center of curvature - //----------------------------------------------------------------- - - Double_t k1 = (y2 - y1) / (x2 - x1), k2 = (y3 - y2) / (x3 - x2); - Double_t x0 = 0.5 * (k1 * k2 * (y1 - y3) + k2 * (x1 + x2) - k1 * (x2 + x3)) / (k2 - k1); - - return x0; -} - -static Double_t f3(Double_t x1, Double_t y1, Double_t x2, Double_t y2, Double_t z1, Double_t z2) -{ - //----------------------------------------------------------------- - // Initial approximation of the tangent of the track dip angle - //----------------------------------------------------------------- - return (z1 - z2) / sqrt((x1 - x2) * (x1 - x2) + (y1 - y2) * (y1 - y2)); -} - -o2::its::TrackITSExt CookedTracker::cookSeed(const Point3Df& r1, Point3Df& r2, const Point3Df& tr3, float rad2, float rad3, float_t alpha, float_t bz) -// const Float_t r1[4], const Float_t r2[4], const Float_t tr3[4], Double_t alpha, Double_t bz) -{ - //-------------------------------------------------------------------- - // This is the main cooking function. - // Creates seed parameters out of provided clusters. - //-------------------------------------------------------------------- - - Double_t ca = TMath::Cos(alpha), sa = TMath::Sin(alpha); - Double_t x1 = r1.X() * ca + r1.Y() * sa, y1 = -r1.X() * sa + r1.Y() * ca, z1 = r1.Z(); - Double_t x2 = r2.X() * ca + r2.Y() * sa, y2 = -r2.X() * sa + r2.Y() * ca, z2 = r2.Z(); - Double_t x3 = tr3.X(), y3 = tr3.Y(), z3 = tr3.Z(); - - std::array par; - par[0] = y3; - par[1] = z3; - Double_t crv = f1(x1, y1, x2, y2, x3, y3); // curvature - Double_t x0 = f2(x1, y1, x2, y2, x3, y3); // x-coordinate of the center - Double_t tgl12 = f3(x1, y1, x2, y2, z1, z2); - Double_t tgl23 = f3(x2, y2, x3, y3, z2, z3); - - Double_t sf = crv * (x3 - x0); // FIXME: sf must never be >= kAlmost1 - par[2] = sf; - - par[3] = 0.5 * (tgl12 + tgl23); - par[4] = (TMath::Abs(bz) < Almost0) ? 1 / CookedTracker::getMostProbablePt() : crv / (bz * B2C); - - std::array cov; - /* - for (Int_t i=0; i<15; i++) cov[i]=0.; - cov[0] =gSigma2*10; - cov[2] =gSigma2*10; - cov[5] =0.007*0.007*10; //FIXME all these lines - cov[9] =0.007*0.007*10; - cov[14]=0.1*0.1*10; - */ - const Double_t dlt = 0.0005; - Double_t fy = 1. / (rad2 - rad3); - Double_t tz = fy; - const auto big = sqrt(o2::constants::math::VeryBig); - auto cy = big; - if (TMath::Abs(bz) >= Almost0) { - auto tmp = dlt * bz * B2C; - cy = (f1(x1, y1, x2, y2 + dlt, x3, y3) - crv) / tmp; - cy *= 20; // FIXME: MS contribution to the cov[14] - } - Double_t s2 = gSigma2; - - cov[0] = s2; - cov[1] = 0.; - cov[2] = s2; - cov[3] = s2 * fy; - cov[4] = 0.; - cov[5] = s2 * fy * fy; - cov[6] = 0.; - cov[7] = s2 * tz; - cov[8] = 0.; - cov[9] = s2 * tz * tz; - cov[10] = s2 * cy; - cov[11] = 0.; - cov[12] = s2 * fy * cy; - cov[13] = 0.; - cov[14] = s2 * cy * cy; - - return o2::its::TrackITSExt(x3, alpha, par, cov); -} - -void CookedTracker::makeSeeds(std::vector& seeds, Int_t first, Int_t last) -{ - //-------------------------------------------------------------------- - // This is the main pattern recongition function. - // Creates seeds out of two clusters and another point. - //-------------------------------------------------------------------- - const float zv = getZ(); - - Layer& layer1 = sLayers[gSeedingLayer1]; - Layer& layer2 = sLayers[gSeedingLayer2]; - Layer& layer3 = sLayers[gSeedingLayer3]; - - auto bz = getBz(); - const Double_t maxC = (TMath::Abs(bz) < Almost0) ? 0.03 : TMath::Abs(bz * B2C / gminPt); - const Double_t kpWinC = TMath::ASin(0.5 * maxC * layer1.getR()) - TMath::ASin(0.5 * maxC * layer2.getR()); - const Double_t kpWinD = 2 * (TMath::ASin(gmaxDCAxy / layer2.getR()) - TMath::ASin(gmaxDCAxy / layer1.getR())); - const Double_t kpWin = std::max(kpWinC, kpWinD); - - for (Int_t n1 = first; n1 < last; n1++) { - const Cluster* c1 = layer1.getCluster(n1); - // - //auto lab = (mClsLabels->getLabels(c1 - &mClusterCache[0] + mFirstInFrame))[0]; - // - auto xyz1 = c1->getXYZGloRot(*mGeom); - auto z1 = xyz1.Z(); - auto r1 = xyz1.rho(); - - auto phi1 = layer1.getClusterPhi(n1); - auto tgl = std::abs((z1 - zv) / r1); - - auto zr2 = zv + layer2.getR() / r1 * (z1 - zv); - auto phir2 = phi1; - auto dz2 = gzWin * (1 + 2 * tgl); - - std::vector selected2; - float dy2 = kpWin * layer2.getR(); - layer2.selectClusters(selected2, phir2, dy2, zr2, dz2); - for (auto n2 : selected2) { - const Cluster* c2 = layer2.getCluster(n2); - // - //if ((mClsLabels->getLabels(c2 - &mClusterCache[0] + mFirstInFrame))[0] != lab) continue; - // - auto xyz2 = c2->getXYZGloRot(*mGeom); - auto z2 = xyz2.Z(); - auto r2 = xyz2.rho(); - - auto dx = xyz2.X() - xyz1.X(), dy = xyz2.Y() - xyz1.Y(); - auto d = (dx * xyz1.Y() - dy * xyz1.X()) / TMath::Sqrt(dx * dx + dy * dy); - auto phir3 = phi1 + TMath::ASin(d / r1) - TMath::ASin(d / layer3.getR()); - - auto zr3 = z1 + (layer3.getR() - r1) / (r2 - r1) * (z2 - z1); - auto dz3 = 0.5f * dz2; - - std::vector selected3; - float dy3 = 0.1 * kpWin * layer3.getR(); //Fixme - layer3.selectClusters(selected3, phir3, dy3, zr3, dz3); - for (auto n3 : selected3) { - const Cluster* c3 = layer3.getCluster(n3); - // - //if ((mClsLabels->getLabels(c3 - &mClusterCache[0] + mFirstInFrame))[0] != lab) continue; - // - auto xyz3 = c3->getXYZGloRot(*mGeom); - auto z3 = xyz3.Z(); - auto r3 = xyz3.rho(); - - zr3 = z1 + (r3 - r1) / (r2 - r1) * (z2 - z1); - if (std::abs(z3 - zr3) > 0.2 * dz3) { - continue; - } - - const Point3Df& txyz2 = c2->getXYZ(); // tracking coordinates - - TrackITSExt seed = cookSeed(xyz1, xyz3, txyz2, layer2.getR(), layer3.getR(), layer2.getAlphaRef(n2), getBz()); - - float ip[2]; - seed.getImpactParams(getX(), getY(), getZ(), getBz(), ip); - if (TMath::Abs(ip[0]) > gmaxDCAxy) { - continue; - } - if (TMath::Abs(ip[1]) > gmaxDCAz) { - continue; - } - { - Double_t xx0 = 0.008; // Rough layer thickness - Double_t radl = 9.36; // Radiation length of Si [cm] - Double_t rho = 2.33; // Density of Si [g/cm^3] - if (!seed.correctForMaterial(xx0, xx0 * radl * rho, kTRUE)) { - continue; - } - } - seed.setClusterIndex(gSeedingLayer1, n1); - seed.setClusterIndex(gSeedingLayer3, n3); - seed.setClusterIndex(gSeedingLayer2, n2); - seeds.push_back(seed); - } - } - } - /* - for (Int_t n1 = 0; n1 < nClusters1; n1++) { - Cluster* c1 = layer1.getCluster(n1); - ((Cluster*)c1)->goToFrameTrk(); - } - for (Int_t n2 = 0; n2 < nClusters2; n2++) { - Cluster* c2 = layer2.getCluster(n2); - ((Cluster*)c2)->goToFrameTrk(); - } - for (Int_t n3 = 0; n3 < nClusters3; n3++) { - Cluster* c3 = layer3.getCluster(n3); - ((Cluster*)c3)->goToFrameTrk(); - } - */ -} - -void CookedTracker::trackSeeds(std::vector& seeds) -{ - //-------------------------------------------------------------------- - // Loop over a subset of track seeds - //-------------------------------------------------------------------- - std::vector used[gSeedingLayer2]; - std::vector selec[gSeedingLayer2]; - for (Int_t l = gSeedingLayer2 - 1; l >= 0; l--) { - Int_t n = sLayers[l].getNumberOfClusters(); - used[l].resize(n, false); - selec[l].reserve(n / 100); - } - - for (auto& track : seeds) { - auto x = track.getX(); - auto y = track.getY(); - Float_t phi = track.getAlpha() + TMath::ATan2(y, x); - o2::math_utils::bringTo02Pi(phi); - float ip[2]; - track.getImpactParams(getX(), getY(), getZ(), getBz(), ip); - - auto z = track.getZ(); - auto crv = track.getCurvature(getBz()); - auto tgl = track.getTgl(); - Float_t r1 = sLayers[gSeedingLayer2].getR(); - - for (Int_t l = gSeedingLayer2 - 1; l >= 0; l--) { - Float_t r2 = sLayers[l].getR(); - selec[l].clear(); - if (TMath::Abs(ip[0]) > r2) { - break; - } - if (TMath::Abs(crv) < gRoadY / (0.5 * r1 * 0.5 * r1)) { - phi += TMath::ASin(ip[0] / r2) - TMath::ASin(ip[0] / r1); - z += tgl * (TMath::Sqrt(r2 * r2 - ip[0] * ip[0]) - TMath::Sqrt(r1 * r1 - ip[0] * ip[0])); - } else { // Fixme - phi += 0.5 * crv * (r2 - r1); - z += tgl / (0.5 * crv) * (TMath::ASin(0.5 * crv * r2) - TMath::ASin(0.5 * crv * r1)); - } - sLayers[l].selectClusters(selec[l], phi, gRoadY, z, gRoadZ * (1 + 2 * std::abs(tgl))); - r1 = r2; - } - - TrackITSExt best(track); - - Int_t volID = -1; - for (auto& ci3 : selec[3]) { - TrackITSExt t3(track); - if (used[3][ci3]) { - continue; - } - if (!attachCluster(volID, 3, ci3, t3, track)) { - continue; - } - if (t3.isBetter(best, gmaxChi2PerTrack)) { - best = t3; - } - - for (auto& ci2 : selec[2]) { - TrackITSExt t2(t3); - if (used[2][ci2]) { - continue; - } - if (!attachCluster(volID, 2, ci2, t2, t3)) { - continue; - } - if (t2.isBetter(best, gmaxChi2PerTrack)) { - best = t2; - } - - for (auto& ci1 : selec[1]) { - TrackITSExt t1(t2); - if (used[1][ci1]) { - continue; - } - if (!attachCluster(volID, 1, ci1, t1, t2)) { - continue; - } - if (t1.isBetter(best, gmaxChi2PerTrack)) { - best = t1; - } - - for (auto& ci0 : selec[0]) { - TrackITSExt t0(t1); - if (used[0][ci0]) { - continue; - } - if (!attachCluster(volID, 0, ci0, t0, t1)) { - continue; - } - if (t0.isBetter(best, gmaxChi2PerTrack)) { - best = t0; - } - volID = -1; - } - } - } - } - - if (best.getNumberOfClusters() >= gminNumberOfClusters) { - Int_t noc = best.getNumberOfClusters(); - for (Int_t ic = 3; ic < noc; ic++) { - Int_t index = best.getClusterIndex(ic); - Int_t l = (index & 0xf0000000) >> 28, c = (index & 0x0fffffff); - used[l][c] = true; - } - } - track = best; - } -} - -std::vector CookedTracker::trackInThread(Int_t first, Int_t last) -{ - //-------------------------------------------------------------------- - // This function is passed to a tracking thread - //-------------------------------------------------------------------- - std::vector seeds; - seeds.reserve(last - first + 1); - - for (const auto& vtx : *mVertices) { - mX = vtx.getX(); - mY = vtx.getY(); - mZ = vtx.getZ(); - makeSeeds(seeds, first, last); - } - - std::sort(seeds.begin(), seeds.end()); - - trackSeeds(seeds); - - makeBackPropParam(seeds); - - return seeds; -} - -void CookedTracker::process(gsl::span const& clusters, - gsl::span::iterator& pattIt, - const o2::itsmft::TopologyDictionary* dict, - TrackInserter& inserter, - o2::itsmft::ROFRecord& rof) -{ - //-------------------------------------------------------------------- - // This is the main tracking function - //-------------------------------------------------------------------- - if (mVertices == nullptr || mVertices->empty()) { - LOG(info) << "Not a single primary vertex provided. Skipping...\n"; - return; - } - LOG(info) << "\n CookedTracker::process(), number of threads: " << mNumOfThreads; - - auto start = std::chrono::system_clock::now(); - - mFirstInFrame = rof.getFirstEntry(); - - mClusterCache.reserve(rof.getNEntries()); - auto clusters_in_frame = rof.getROFData(clusters); - for (const auto& comp : clusters_in_frame) { - - auto pattID = comp.getPatternID(); - o2::math_utils::Point3D locXYZ; - float sigmaY2 = gSigma2, sigmaZ2 = gSigma2; - if (pattID != itsmft::CompCluster::InvalidPatternID) { - sigmaY2 = gSigma2; //dict.getErr2X(pattID); - sigmaZ2 = gSigma2; //dict.getErr2Z(pattID); - if (!dict->isGroup(pattID)) { - locXYZ = dict->getClusterCoordinates(comp); - } else { - o2::itsmft::ClusterPattern patt(pattIt); - locXYZ = dict->getClusterCoordinates(comp, patt); - } - } else { - o2::itsmft::ClusterPattern patt(pattIt); - locXYZ = dict->getClusterCoordinates(comp, patt, false); - } - auto sensorID = comp.getSensorID(); - // Inverse transformation to the local --> tracking - auto trkXYZ = mGeom->getMatrixT2L(sensorID) ^ locXYZ; - - Cluster c; - c.setSensorID(sensorID); - c.setPos(trkXYZ); - c.setErrors(sigmaY2, sigmaZ2, 0.f); - mClusterCache.push_back(c); - } - - auto nClFrame = loadClusters(); - - auto end = std::chrono::system_clock::now(); - std::chrono::duration diff = end - start; - LOG(info) << "Loading clusters: " << nClFrame << " in a single frame : " << diff.count() << " s"; - - start = end; - - auto [first, number] = processLoadedClusters(inserter); - rof.setFirstEntry(first); - rof.setNEntries(number); - - unloadClusters(); - end = std::chrono::system_clock::now(); - diff = end - start; - LOG(info) << "Processing time/clusters for single frame : " << diff.count() << " / " << nClFrame << " s"; - - start = end; -} - -std::tuple CookedTracker::processLoadedClusters(TrackInserter& inserter) -{ - //-------------------------------------------------------------------- - // This is the main tracking function for single frame, it is assumed that only clusters - // which may contribute to this frame is loaded - //-------------------------------------------------------------------- - Int_t numOfClusters = sLayers[gSeedingLayer1].getNumberOfClusters(); - if (!numOfClusters) { - return {0, 0}; - } - - std::vector>> futures(mNumOfThreads); - std::vector> seedArray(mNumOfThreads); - - for (Int_t t = 0, first = 0; t < mNumOfThreads; t++) { - Int_t rem = t < (numOfClusters % mNumOfThreads) ? 1 : 0; - Int_t last = first + (numOfClusters / mNumOfThreads) + rem; - futures[t] = std::async(std::launch::async, &CookedTracker::trackInThread, this, first, last); - first = last; - } - Int_t nSeeds = 0, ngood = 0; - int nAllTracks = 0, nTracks = 0; - for (Int_t t = 0; t < mNumOfThreads; t++) { - seedArray[t] = futures[t].get(); - nSeeds += seedArray[t].size(); - for (auto& track : seedArray[t]) { - if (track.getNumberOfClusters() < gminNumberOfClusters) { - continue; - } - - o2::dataformats::VertexBase vtx; - track.propagateToDCA(vtx, getBz()); - - nAllTracks = inserter(track); - nTracks++; - if (mTrkLabels) { - Label label = cookLabel(track, 0.); // For comparison only - if (label.getTrackID() >= 0) { - ngood++; - } - // the inserter returns the size of the track vector, the index of the last - // inserted track is thus n - 1 - mTrkLabels->emplace_back(label); - } - } - } - - if (nSeeds) { - LOG(info) << "Found tracks: " << nTracks; - LOG(info) << "CookedTracker::processLoadedClusters(), good_tracks:/seeds: " << ngood << '/' << nSeeds << "-> " - << Float_t(ngood) / nSeeds << '\n'; - } - // returning index of the first track and the number of add tracks - // inserted in this call - return {nAllTracks - nTracks, nTracks}; -} - -//____________________________________________________________ -void CookedTracker::makeBackPropParam(std::vector& seeds) const -{ - // refit in backward direction - for (auto& track : seeds) { - if (track.getNumberOfClusters() < gminNumberOfClusters) { - continue; - } - makeBackPropParam(track); - } -} - -//____________________________________________________________ -bool CookedTracker::makeBackPropParam(TrackITSExt& track) const -{ - // refit in backward direction - auto backProp = track.getParamOut(); - backProp = track; - backProp.resetCovariance(); - auto propagator = o2::base::Propagator::Instance(); - - Int_t noc = track.getNumberOfClusters(); - for (int ic = noc; ic--;) { // cluster indices are stored in inward direction - Int_t index = track.getClusterIndex(ic); - const Cluster* c = getCluster(index); - float alpha = mGeom->getSensorRefAlpha(c->getSensorID()); - if (!backProp.rotate(alpha)) { - return false; - } - if (!propagator->PropagateToXBxByBz(backProp, c->getX())) { - return false; - } - if (!backProp.update(static_cast&>(*c))) { - return false; - } - } - track.getParamOut() = backProp; - return true; -} - -int CookedTracker::loadClusters() -{ - //-------------------------------------------------------------------- - // This function reads the ITSU clusters from the tree, - // sort them, distribute over the internal tracker arrays, etc - //-------------------------------------------------------------------- - - if (mClusterCache.empty()) { - return 0; - } - - for (const auto& c : mClusterCache) { - Int_t layer = mGeom->getLayer(c.getSensorID()); - sLayers[layer].insertCluster(&c); - } - - std::vector> fut; - for (Int_t l = 0; l < kNLayers; l += mNumOfThreads) { - for (Int_t t = 0; t < mNumOfThreads; t++) { - if (l + t >= kNLayers) { - break; - } - auto f = std::async(std::launch::async, &CookedTracker::Layer::init, sLayers + (l + t)); - fut.push_back(std::move(f)); - } - for (size_t t = 0; t < fut.size(); t++) { - fut[t].wait(); - } - } - - return mClusterCache.size(); -} - -void CookedTracker::unloadClusters() -{ - //-------------------------------------------------------------------- - // This function unloads ITSU clusters from the RAM - //-------------------------------------------------------------------- - mClusterCache.clear(); - for (Int_t i = 0; i < kNLayers; i++) { - sLayers[i].unloadClusters(); - } -} - -const Cluster* CookedTracker::getCluster(Int_t index) const -{ - //-------------------------------------------------------------------- - // Return pointer to a given cluster - //-------------------------------------------------------------------- - Int_t l = (index & 0xf0000000) >> 28; - Int_t c = (index & 0x0fffffff) >> 00; - return sLayers[l].getCluster(c); -} - -CookedTracker::Layer::Layer() : mR(0) -{ - //-------------------------------------------------------------------- - // This default constructor needs to be provided - //-------------------------------------------------------------------- -} - -void CookedTracker::Layer::init() -{ - //-------------------------------------------------------------------- - // Sort clusters and cache their reference plane info in a thread - //-------------------------------------------------------------------- - std::sort(std::begin(mClusters), std::end(mClusters), - [](const Cluster* c1, const Cluster* c2) { return (c1->getZ() < c2->getZ()); }); - - Double_t r = 0.; - Int_t m = mClusters.size(); - for (Int_t i = 0; i < m; i++) { - const Cluster* c = mClusters[i]; - // Float_t xRef, aRef; - // mGeom->getSensorXAlphaRefPlane(c->getSensorID(),xRef, aRef); - mAlphaRef.push_back(mGeom->getSensorRefAlpha(c->getSensorID())); - auto xyz = c->getXYZGloRot(*mGeom); - r += xyz.rho(); - Float_t phi = xyz.Phi(); - o2::math_utils::bringTo02Pi(phi); - mPhi.push_back(phi); - Int_t s = phi * (int)kNSectors / k2PI; - mSectors[s < kNSectors ? s : kNSectors - 1].emplace_back(i, c->getZ()); - } - - if (m) { - mR = r / m; - } -} - -void CookedTracker::Layer::unloadClusters() -{ - //-------------------------------------------------------------------- - // Unload clusters from this layer - //-------------------------------------------------------------------- - mClusters.clear(); - mAlphaRef.clear(); - mPhi.clear(); - for (Int_t s = 0; s < kNSectors; s++) { - mSectors[s].clear(); - } -} - -Bool_t CookedTracker::Layer::insertCluster(const Cluster* c) -{ - //-------------------------------------------------------------------- - // This function inserts a cluster to this layer - //-------------------------------------------------------------------- - mClusters.push_back(c); - return kTRUE; -} - -Int_t CookedTracker::Layer::findClusterIndex(Float_t z) const -{ - //-------------------------------------------------------------------- - // This function returns the index of the first cluster with its fZ >= "z". - //-------------------------------------------------------------------- - auto found = std::upper_bound(std::begin(mClusters), std::end(mClusters), z, - [](Float_t zc, const Cluster* c) { return (zc < c->getZ()); }); - return found - std::begin(mClusters); -} - -void CookedTracker::Layer::selectClusters(std::vector& selec, Float_t phi, Float_t dy, Float_t z, Float_t dz) -{ - //-------------------------------------------------------------------- - // This function selects clusters within the "road" - //-------------------------------------------------------------------- - Float_t zMin = z - dz; - Float_t zMax = z + dz; - - o2::math_utils::bringTo02Pi(phi); - - Float_t dphi = dy / mR; - - int smin = (phi - dphi) / k2PI * (int)kNSectors; - int ds = (phi + dphi) / k2PI * (int)kNSectors - smin + 1; - - smin = (smin + kNSectors) % kNSectors; - - for (int is = 0; is < ds; is++) { - Int_t s = (smin + is) % kNSectors; - - auto cmp = [](Float_t zc, std::pair p) { return (zc < p.second); }; - auto imin = std::upper_bound(std::begin(mSectors[s]), std::end(mSectors[s]), zMin, cmp); - auto imax = std::upper_bound(imin, std::end(mSectors[s]), zMax, cmp); - for (; imin != imax; imin++) { - auto [i, zz] = *imin; - auto cdphi = std::abs(mPhi[i] - phi); - if (cdphi > dphi) { - if (cdphi > kPI) { - cdphi = k2PI - cdphi; - } - if (cdphi > dphi) { - continue; // check in Phi - } - } - selec.push_back(i); - } - } -} - -Bool_t CookedTracker::attachCluster(Int_t& volID, Int_t nl, Int_t ci, TrackITSExt& t, const TrackITSExt& o) const -{ - //-------------------------------------------------------------------- - // Try to attach a clusters with index ci to running track hypothesis - //-------------------------------------------------------------------- - Layer& layer = sLayers[nl]; - const Cluster* c = layer.getCluster(ci); - - Int_t vid = c->getSensorID(); - - if (vid != volID) { - volID = vid; - t = o; - Double_t alpha = layer.getAlphaRef(ci); - if (!t.propagate(alpha, c->getX(), getBz())) { - return kFALSE; - } - } - - Double_t chi2 = t.getPredictedChi2(*c); - if (chi2 > gmaxChi2PerCluster) { - return kFALSE; - } - - if (!t.update(*c, chi2)) { - return kFALSE; - } - t.setClusterIndex(nl, ci); - - Double_t xx0 = (nl > 2) ? 0.008 : 0.003; // Rough layer thickness - Double_t x0 = 9.36; // Radiation length of Si [cm] - Double_t rho = 2.33; // Density of Si [g/cm^3] - t.correctForMaterial(xx0, xx0 * x0 * rho, kTRUE); - return kTRUE; -} - -void CookedTracker::setGeometry(o2::its::GeometryTGeo* geom) -{ - /// attach geometry interface - mGeom = geom; - for (Int_t i = 0; i < kNLayers; i++) { - sLayers[i].setGeometry(geom); - } -} diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/CookedTrackerLinkDef.h b/Detectors/ITSMFT/ITS/reconstruction/src/CookedTrackerLinkDef.h deleted file mode 100644 index 1e8596fbb224c..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/src/CookedTrackerLinkDef.h +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifdef __CLING__ - -#pragma link off all globals; -#pragma link off all classes; -#pragma link off all functions; - -#pragma link C++ class o2::its::ClustererTask + ; -#pragma link C++ class o2::its::CookedTracker + ; -#pragma link C++ class o2::its::CookedConfigParam + ; -#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::CookedConfigParam> + ; -#pragma link C++ class o2::its::RecoGeomHelper + ; -#pragma link C++ class o2::its::FastMultEst + ; -#pragma link C++ class o2::its::FastMultEstConfig + ; - -#endif diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/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/workflow/CMakeLists.txt b/Detectors/ITSMFT/ITS/workflow/CMakeLists.txt index 3609560eccf72..f0d50e59493d4 100644 --- a/Detectors/ITSMFT/ITS/workflow/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/workflow/CMakeLists.txt @@ -16,7 +16,6 @@ o2_add_library(ITSWorkflow 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/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 90b38acb34a95..1d5d829a6f79a 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/RecoWorkflow.h +++ b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/RecoWorkflow.h @@ -26,7 +26,7 @@ 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); } 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 368ca6909240f..60e28556716f2 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx @@ -15,7 +15,6 @@ #include "ITSWorkflow/ClustererSpec.h" #include "ITSWorkflow/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, @@ -51,40 +49,36 @@ framework::WorkflowSpec getWorkflow(bool useMC, specs.emplace_back(o2::its::getClusterWriterSpec(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/its-reco-workflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/its-reco-workflow.cxx index 4e1721f4194b0..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,7 +64,6 @@ 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"); @@ -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/macro/CMakeLists.txt b/macro/CMakeLists.txt index b5c51e50d3ffb..1c39a96db1b60 100644 --- a/macro/CMakeLists.txt +++ b/macro/CMakeLists.txt @@ -48,7 +48,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 @@ -336,20 +335,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 @@ -463,18 +448,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 @@ -497,13 +470,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/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 From f088a3566139cb874dcf01f0fea37627f2ec2e16 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Mon, 10 Nov 2025 10:27:16 +0100 Subject: [PATCH 124/701] ITS: remove old ClustererTask Signed-off-by: Felix Schlepper --- .../ITSMFT/ITS/reconstruction/CMakeLists.txt | 6 +- .../include/ITSReconstruction/ClustererTask.h | 85 --------- .../ITS/reconstruction/src/ClustererTask.cxx | 163 ------------------ macro/CMakeLists.txt | 9 - macro/run_clus_itsSA.C | 65 ------- 5 files changed, 2 insertions(+), 326 deletions(-) delete mode 100644 Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/ClustererTask.h delete mode 100644 Detectors/ITSMFT/ITS/reconstruction/src/ClustererTask.cxx delete mode 100644 macro/run_clus_itsSA.C diff --git a/Detectors/ITSMFT/ITS/reconstruction/CMakeLists.txt b/Detectors/ITSMFT/ITS/reconstruction/CMakeLists.txt index a5004418599e4..d2126be1da2c6 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/reconstruction/CMakeLists.txt @@ -10,8 +10,7 @@ # or submit itself to any jurisdiction. o2_add_library(ITSReconstruction - SOURCES src/ClustererTask.cxx - src/RecoGeomHelper.cxx + SOURCES src/RecoGeomHelper.cxx src/FastMultEstConfig.cxx src/FastMultEst.cxx PUBLIC_LINK_LIBRARIES O2::ITSBase @@ -21,7 +20,6 @@ o2_add_library(ITSReconstruction o2_target_root_dictionary( ITSReconstruction - HEADERS include/ITSReconstruction/ClustererTask.h - include/ITSReconstruction/RecoGeomHelper.h + HEADERS include/ITSReconstruction/RecoGeomHelper.h include/ITSReconstruction/FastMultEst.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/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/macro/CMakeLists.txt b/macro/CMakeLists.txt index 1c39a96db1b60..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 @@ -243,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 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(); -} From 4c3ba3d4518705622921f677129045ff25c380f4 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 8 Jan 2026 11:08:17 +0100 Subject: [PATCH 125/701] DPL: fix warnings --- Framework/Core/include/Framework/ServiceSpec.h | 2 +- Framework/Core/include/Framework/StringHelpers.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Framework/Core/include/Framework/ServiceSpec.h b/Framework/Core/include/Framework/ServiceSpec.h index 5684889e85376..8ac0872edd1bf 100644 --- a/Framework/Core/include/Framework/ServiceSpec.h +++ b/Framework/Core/include/Framework/ServiceSpec.h @@ -31,7 +31,7 @@ struct DeviceSpec; struct ServiceRegistry; struct 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; } From 96e2f45f8c95c91f81b063e9ce35ad056b257fe9 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 8 Jan 2026 11:08:17 +0100 Subject: [PATCH 126/701] DPL: avoid MessageSet abstractions when forwarding This is most likely faster, and it will allow us to move the early forwarding at an earlier stage where the data is not yet in a MessageSet. --- .../include/Framework/DataProcessingHelpers.h | 9 +- Framework/Core/src/DataProcessingDevice.cxx | 2 +- Framework/Core/src/DataProcessingHelpers.cxx | 188 ++++++++++-------- Framework/Core/test/test_ForwardInputs.cxx | 27 ++- 4 files changed, 127 insertions(+), 99 deletions(-) diff --git a/Framework/Core/include/Framework/DataProcessingHelpers.h b/Framework/Core/include/Framework/DataProcessingHelpers.h index 34bb87613d920..a9bd95b69f4c7 100644 --- a/Framework/Core/include/Framework/DataProcessingHelpers.h +++ b/Framework/Core/include/Framework/DataProcessingHelpers.h @@ -16,6 +16,7 @@ #include "Framework/TimesliceIndex.h" #include #include +#include namespace o2::framework { @@ -53,9 +54,11 @@ struct DataProcessingHelpers { /// 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 routeForwardedMessages(FairMQDeviceProxy& proxy, - std::vector& currentSetOfInputs, - const bool copyByDefault, bool consume); + 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); }; } // namespace o2::framework #endif // O2_FRAMEWORK_DATAPROCESSINGHELPERS_H_ diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index 63c333561f24e..3925359b056b2 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -592,7 +592,7 @@ static auto forwardInputs = [](ServiceRegistryRef registry, TimesliceSlot slot, 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::routeForwardedMessages(proxy, currentSetOfInputs, copy, consume); + auto forwardedParts = DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copy, consume); for (int fi = 0; fi < proxy.getNumForwardChannels(); fi++) { if (forwardedParts[fi].Size() == 0) { diff --git a/Framework/Core/src/DataProcessingHelpers.cxx b/Framework/Core/src/DataProcessingHelpers.cxx index 90dcee52d73da..2f7a1f65f3bd3 100644 --- a/Framework/Core/src/DataProcessingHelpers.cxx +++ b/Framework/Core/src/DataProcessingHelpers.cxx @@ -228,102 +228,128 @@ TransitionHandlingState DataProcessingHelpers::updateStateTransition(ServiceRegi } } -auto DataProcessingHelpers::routeForwardedMessages(FairMQDeviceProxy& proxy, - std::vector& currentSetOfInputs, - const bool copyByDefault, bool consume) -> std::vector +void DataProcessingHelpers::routeForwardedMessages(FairMQDeviceProxy& proxy, std::span& messages, std::vector& forwardedParts, + const bool copyByDefault, bool consume) { - // we collect all messages per forward in a map and send them together - std::vector forwardedParts; - forwardedParts.resize(proxy.getNumForwards()); - std::vector forwardingChoices{}; O2_SIGNPOST_ID_GENERATE(sid, forwarding); + std::vector forwardingChoices{}; + size_t pi = 0; + while (pi < messages.size()) { + auto& header = messages[pi]; - for (size_t ii = 0, ie = currentSetOfInputs.size(); ii < ie; ++ii) { - auto& messageSet = currentSetOfInputs[ii]; + // 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; + } - for (size_t pi = 0; pi < messageSet.size(); ++pi) { - auto& header = messageSet.header(pi); + auto dph = o2::header::get(header->GetData()); + auto dh = o2::header::get(header->GetData()); - // 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) { - continue; - } - auto dih = o2::header::get(header->GetData()); - if (dih) { - continue; - } - auto sih = o2::header::get(header->GetData()); - if (sih) { - continue; - } + 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; + } - auto dph = o2::header::get(header->GetData()); - auto dh = o2::header::get(header->GetData()); + // 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 (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" : ""); - continue; - } + 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; + } - auto& payload = messageSet.payload(pi); + // 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. + // - 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); - continue; - } + forwardingChoices.clear(); + proxy.getMatchingForwardChannelIndexes(forwardingChoices, *dh, dph->startTime); - // 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 - // - 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. - if (dh->splitPayloadIndex == 0 || dh->splitPayloadParts <= 1 || messageSet.getNumberOfPayloads(pi) > 0) { - forwardingChoices.clear(); - proxy.getMatchingForwardChannelIndexes(forwardingChoices, *dh, dph->startTime); - } + if (forwardingChoices.empty()) { + // Nothing to forward go to the next messageset + pi += numberOfMessages; + continue; + } - if (forwardingChoices.empty()) { - // Nothing to forward go to the next messageset - 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); - // 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) { - 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(), choice.value); - newHeader->Copy(*header); - forwardedParts[choice.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[choice.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(), forwardingChoices.back().value); - forwardedParts[forwardingChoices.back().value].AddPart(std::move(messageSet.header(pi))); - for (size_t payloadIndex = 0; payloadIndex < messageSet.getNumberOfPayloads(pi); ++payloadIndex) { - forwardedParts[forwardingChoices.back().value].AddPart(std::move(messageSet.payload(pi, payloadIndex))); + 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; + } +} + +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; + forwardedParts.resize(proxy.getNumForwards()); + std::vector forwardingChoices{}; + + 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; }; diff --git a/Framework/Core/test/test_ForwardInputs.cxx b/Framework/Core/test/test_ForwardInputs.cxx index 7ddbc831edad2..fe9f70d1daadb 100644 --- a/Framework/Core/test/test_ForwardInputs.cxx +++ b/Framework/Core/test/test_ForwardInputs.cxx @@ -45,7 +45,7 @@ TEST_CASE("ForwardInputsEmpty") std::vector currentSetOfInputs; - auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, currentSetOfInputs, copyByDefault, consume); + auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); REQUIRE(result.empty()); } @@ -95,7 +95,7 @@ TEST_CASE("ForwardInputsSingleMessageSingleRoute") REQUIRE(messageSet.size() == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); - auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, currentSetOfInputs, copyByDefault, consume); + 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 } @@ -146,7 +146,7 @@ TEST_CASE("ForwardInputsSingleMessageSingleRouteNoConsume") REQUIRE(messageSet.size() == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); - auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, currentSetOfInputs, copyByDefault, true); + 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. } @@ -201,8 +201,7 @@ TEST_CASE("ForwardInputsSingleMessageSingleRouteAtEOS") REQUIRE(messageSet.size() == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); - - auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, currentSetOfInputs, copyByDefault, consume); + 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: @@ -260,7 +259,7 @@ TEST_CASE("ForwardInputsSingleMessageSingleRouteWithOldestPossible") REQUIRE(messageSet.size() == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); - auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, currentSetOfInputs, copyByDefault, consume); + 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 @@ -325,7 +324,7 @@ TEST_CASE("ForwardInputsSingleMessageMultipleRoutes") REQUIRE(messageSet.size() == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); - auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, currentSetOfInputs, copyByDefault, consume); + 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 @@ -388,7 +387,7 @@ TEST_CASE("ForwardInputsSingleMessageMultipleRoutesExternals") REQUIRE(messageSet.size() == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); - auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, currentSetOfInputs, copyByDefault, consume); + 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); // @@ -466,7 +465,7 @@ TEST_CASE("ForwardInputsMultiMessageMultipleRoutes") currentSetOfInputs.emplace_back(std::move(messageSet2)); REQUIRE(currentSetOfInputs.size() == 2); - auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, currentSetOfInputs, copyByDefault, consume); + 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); // @@ -529,7 +528,7 @@ TEST_CASE("ForwardInputsSingleMessageMultipleRoutesOnlyOneMatches") REQUIRE(messageSet.size() == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); - auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, currentSetOfInputs, copyByDefault, consume); + 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 @@ -541,7 +540,7 @@ TEST_CASE("ForwardInputsSplitPayload") dh.dataOrigin = "TST"; dh.dataDescription = "A"; dh.subSpecification = 0; - dh.splitPayloadIndex = 0; + dh.splitPayloadIndex = 2; dh.splitPayloadParts = 2; o2::header::DataHeader dh2; @@ -611,7 +610,7 @@ TEST_CASE("ForwardInputsSplitPayload") REQUIRE(messageSet.size() == 2); currentSetOfInputs.emplace_back(std::move(messageSet)); - auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, currentSetOfInputs, copyByDefault, consume); + 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); @@ -657,7 +656,7 @@ TEST_CASE("ForwardInputEOSSingleRoute") REQUIRE(messageSet.size() == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); - auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, currentSetOfInputs, copyByDefault, consume); + 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 } @@ -702,7 +701,7 @@ TEST_CASE("ForwardInputOldestPossibleSingleRoute") REQUIRE(messageSet.size() == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); - auto result = o2::framework::DataProcessingHelpers::routeForwardedMessages(proxy, currentSetOfInputs, copyByDefault, consume); + 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 } From bf75199a5345651cad229244d1f0a3f38dbbdb92 Mon Sep 17 00:00:00 2001 From: Florian Jonas Date: Fri, 9 Jan 2026 16:18:37 +0100 Subject: [PATCH 127/701] [EMCAL] implementation of number of local maxima variable * [EMCAL] implementation of number of local maxima variable * Please consider the following formatting changes * further optimizations of EMCAL evalNExMax * Please consider the following formatting changes --------- Co-authored-by: ALICE Action Bot --- .../base/include/EMCALBase/ClusterFactory.h | 4 ++ .../EMCAL/base/include/EMCALBase/Geometry.h | 8 +++ Detectors/EMCAL/base/src/ClusterFactory.cxx | 60 +++++++++++++++++++ Detectors/EMCAL/base/src/Geometry.cxx | 24 ++++++++ 4 files changed, 96 insertions(+) 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..970f7979ef86d 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,63 @@ 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) { From 696cf650cc142446b722e1b10212bfcc4d14f03d Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Fri, 9 Jan 2026 11:39:02 +0100 Subject: [PATCH 128/701] DPL: fix a few warnings --- Framework/Core/include/Framework/GuiCallbackContext.h | 2 +- Framework/Core/include/Framework/InitContext.h | 2 +- Framework/Core/include/Framework/ServiceRegistry.h | 2 +- Framework/Core/include/Framework/ServiceSpec.h | 4 ++-- Framework/Core/src/TMessageSerializer.cxx | 2 +- 5 files changed, 6 insertions(+), 6 deletions(-) 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/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/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/ServiceSpec.h b/Framework/Core/include/Framework/ServiceSpec.h index 8ac0872edd1bf..aa762b5d039e0 100644 --- a/Framework/Core/include/Framework/ServiceSpec.h +++ b/Framework/Core/include/Framework/ServiceSpec.h @@ -26,10 +26,10 @@ struct ProgOptions; namespace o2::framework { -struct InitContext; +class InitContext; struct DeviceSpec; struct ServiceRegistry; -struct ServiceRegistryRef; +class ServiceRegistryRef; struct DeviceState; class ProcessingContext; class EndOfStreamContext; 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 From 67c2a31833ceafdfb6a497b94ea1ae6be3f17651 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Tue, 6 Jan 2026 10:59:37 +0100 Subject: [PATCH 129/701] TPC: move PadFlags and related classes to TPCBaseRecSim o2::tpc::PadFlags and in particular vectors of thereof are affected by an old ROOT bug in serializing `std::vector`. Because of this we have a custom streamer which needs to be initialised very early in order to function correctly. This is fine, however, due to the fact we invoke `TClass::GetClass()` too early, ROOT is forced a bunch of extra stuff, unneededly, resulting in much increased memory footprint in analysis, which happens to use DataFormatsTPC. This makes sure the custom streamer is not initialised statically by DataFormatsTPC and prevents ROOT from loading the kitchen sink when the TClass::GetClass is invoked too early. --- .../TPC/include/DataFormatsTPC/Defs.h | 17 ------- .../Detectors/TPC/src/DataFormatsTPCLinkDef.h | 2 - Detectors/TPC/CMakeLists.txt | 1 + Detectors/TPC/base/CMakeLists.txt | 21 +-------- Detectors/TPC/base/src/TPCBaseLinkDef.h | 8 ---- .../TPC/base/test/testTPCCDBInterface.cxx | 2 +- Detectors/TPC/baserecsim/CMakeLists.txt | 35 +++++++++++++++ .../include/TPCBaseRecSim}/CDBInterface.h | 4 +- .../include/TPCBaseRecSim}/CDBTypes.h | 0 .../TPCBaseRecSim}/DeadChannelMapCreator.h | 4 +- .../include/TPCBaseRecSim/PadFlags.h | 44 +++++++++++++++++++ .../include/TPCBaseRecSim}/Painter.h | 0 .../{base => baserecsim}/src/CDBInterface.cxx | 2 +- .../src/DeadChannelMapCreator.cxx | 4 +- .../TPC/{base => baserecsim}/src/Painter.cxx | 3 +- .../TPC/baserecsim/src/TPCBaseRecSimLinkDef.h | 27 ++++++++++++ .../src/TPCFlagsMemberCustomStreamer.cxx | 0 .../test/testTPCCalDet.cxx | 2 +- Detectors/TPC/calibration/CMakeLists.txt | 10 ++--- .../include/TPCCalibration/IDCCCDBHelper.h | 1 + .../include/TPCCalibration/IDCFactorization.h | 1 + .../macro/comparePedestalsAndNoise.C | 4 +- .../calibration/macro/drawNoiseAndPedestal.C | 4 +- Detectors/TPC/calibration/macro/drawPulser.C | 2 +- .../TPC/calibration/macro/prepareCMFiles.C | 2 +- .../TPC/calibration/macro/prepareITFiles.C | 2 +- .../calibration/macro/preparePedestalFiles.C | 2 +- .../TPC/calibration/src/CalculatedEdx.cxx | 2 +- .../src/CalibPadGainTracksBase.cxx | 2 +- .../calibration/src/CorrectionMapsLoader.cxx | 2 +- .../TPC/calibration/src/IDCAverageGroup.cxx | 3 +- .../TPC/calibration/src/IDCCCDBHelper.cxx | 2 +- .../TPC/calibration/src/IDCDrawHelper.cxx | 2 +- .../src/PressureTemperatureHelper.cxx | 2 +- .../TPC/calibration/src/SACDrawHelper.cxx | 2 +- .../TPC/calibration/src/VDriftHelper.cxx | 2 +- Detectors/TPC/dcs/src/DCSConfigSpec.cxx | 2 +- Detectors/TPC/dcs/src/DCSSpec.cxx | 2 +- .../TPC/monitor/src/SimpleEventDisplayGUI.cxx | 2 +- Detectors/TPC/qc/macro/runClusters.C | 2 +- Detectors/TPC/qc/macro/runPID.C | 2 +- Detectors/TPC/qc/src/Clusters.cxx | 2 +- Detectors/TPC/qc/src/IDCsVsSACs.cxx | 2 +- Detectors/TPC/simulation/macro/toyCluster.C | 2 +- .../TPC/simulation/src/DigitContainer.cxx | 2 +- Detectors/TPC/simulation/src/Digitizer.cxx | 2 +- .../TPC/simulation/src/ElectronTransport.cxx | 2 +- .../TPC/simulation/src/GEMAmplification.cxx | 2 +- Detectors/TPC/simulation/src/IDCSim.cxx | 2 +- .../TPC/simulation/src/SAMPAProcessing.cxx | 2 +- .../simulation/test/testTPCDigitContainer.cxx | 2 +- .../test/testTPCElectronTransport.cxx | 2 +- .../test/testTPCGEMAmplification.cxx | 2 +- .../test/testTPCSAMPAProcessing.cxx | 2 +- Detectors/TPC/spacecharge/CMakeLists.txt | 2 +- .../macro/createSCHistosFromHits.C | 2 +- Detectors/TPC/spacecharge/src/SpaceCharge.cxx | 2 +- .../TPCWorkflow/CalibratorPadGainTracksSpec.h | 2 +- .../TPCWorkflow/TPCCalibPadGainTracksSpec.h | 2 +- .../include/TPCWorkflow/TPCCalibPadRawSpec.h | 2 +- .../include/TPCWorkflow/TPCFLPIDCSpec.h | 2 +- .../include/TPCWorkflow/TPCFactorizeIDCSpec.h | 2 +- .../include/TPCWorkflow/TPCFactorizeSACSpec.h | 2 +- .../src/CalDetMergerPublisherSpec.cxx | 2 +- Detectors/TPC/workflow/src/CalibdEdxSpec.cxx | 2 +- .../TPC/workflow/src/CalibratordEdxSpec.cxx | 2 +- .../TPC/workflow/src/SACProcessorSpec.cxx | 2 +- .../src/TPCMergeIntegrateClusterSpec.cxx | 2 +- Detectors/TPC/workflow/src/TPCScalerSpec.cxx | 2 +- GPU/Workflow/src/GPUWorkflowSpec.cxx | 4 +- GPU/Workflow/src/GPUWorkflowTPC.cxx | 4 +- .../src/SimpleDigitizerWorkflow.cxx | 2 +- .../src/TPCDigitizerSpec.cxx | 2 +- 73 files changed, 183 insertions(+), 118 deletions(-) create mode 100644 Detectors/TPC/baserecsim/CMakeLists.txt rename Detectors/TPC/{base/include/TPCBase => baserecsim/include/TPCBaseRecSim}/CDBInterface.h (99%) rename Detectors/TPC/{base/include/TPCBase => baserecsim/include/TPCBaseRecSim}/CDBTypes.h (100%) rename Detectors/TPC/{base/include/TPCBase => baserecsim/include/TPCBaseRecSim}/DeadChannelMapCreator.h (98%) create mode 100644 Detectors/TPC/baserecsim/include/TPCBaseRecSim/PadFlags.h rename Detectors/TPC/{base/include/TPCBase => baserecsim/include/TPCBaseRecSim}/Painter.h (100%) rename Detectors/TPC/{base => baserecsim}/src/CDBInterface.cxx (99%) rename Detectors/TPC/{base => baserecsim}/src/DeadChannelMapCreator.cxx (98%) rename Detectors/TPC/{base => baserecsim}/src/Painter.cxx (99%) create mode 100644 Detectors/TPC/baserecsim/src/TPCBaseRecSimLinkDef.h rename Detectors/TPC/{base => baserecsim}/src/TPCFlagsMemberCustomStreamer.cxx (100%) rename Detectors/TPC/{base => baserecsim}/test/testTPCCalDet.cxx (99%) 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/DataFormatsTPCLinkDef.h b/DataFormats/Detectors/TPC/src/DataFormatsTPCLinkDef.h index f463e9011c935..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/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 a82214d8c070f..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,16 +35,13 @@ 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) o2_target_root_dictionary(TPCBase - EXTRA_PATCH src/TPCFlagsMemberCustomStreamer.cxx 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 @@ -58,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 @@ -71,26 +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) -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::TPCBase - SOURCES test/testTPCCalDet.cxx - ENVIRONMENT O2_ROOT=${CMAKE_BINARY_DIR}/stage - LABELS tpc) -endif() - o2_add_test(Mapper COMPONENT_NAME tpc PUBLIC_LINK_LIBRARIES O2::TPCBase diff --git a/Detectors/TPC/base/src/TPCBaseLinkDef.h b/Detectors/TPC/base/src/TPCBaseLinkDef.h index 4fdde1ca55518..2b7a7ff19542d 100644 --- a/Detectors/TPC/base/src/TPCBaseLinkDef.h +++ b/Detectors/TPC/base/src/TPCBaseLinkDef.h @@ -21,20 +21,15 @@ #pragma link C++ class o2::tpc::CalArray < unsigned> + ; #pragma link C++ class o2::tpc::CalArray < short> + ; #pragma link C++ class o2::tpc::CalArray < bool> + ; -#pragma link C++ class o2::tpc::CalArray < o2::tpc::PadFlags> + ; #pragma link C++ class o2::tpc::CalDet < float> + ; #pragma link C++ class o2::tpc::CalDet < double> + ; #pragma link C++ class o2::tpc::CalDet < int> + ; #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; @@ -50,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> + ; @@ -90,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 5a5384a4134ed..a0f4142b3f807 100644 --- a/Detectors/TPC/base/test/testTPCCDBInterface.cxx +++ b/Detectors/TPC/base/test/testTPCCDBInterface.cxx @@ -21,7 +21,7 @@ #include "TFile.h" // o2 includes -#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/base/src/TPCFlagsMemberCustomStreamer.cxx b/Detectors/TPC/baserecsim/src/TPCFlagsMemberCustomStreamer.cxx similarity index 100% rename from Detectors/TPC/base/src/TPCFlagsMemberCustomStreamer.cxx rename to Detectors/TPC/baserecsim/src/TPCFlagsMemberCustomStreamer.cxx diff --git a/Detectors/TPC/base/test/testTPCCalDet.cxx b/Detectors/TPC/baserecsim/test/testTPCCalDet.cxx similarity index 99% rename from Detectors/TPC/base/test/testTPCCalDet.cxx rename to Detectors/TPC/baserecsim/test/testTPCCalDet.cxx index fda38c2d03e91..bf4cfddb780f0 100644 --- a/Detectors/TPC/base/test/testTPCCalDet.cxx +++ b/Detectors/TPC/baserecsim/test/testTPCCalDet.cxx @@ -24,7 +24,7 @@ #include "TPCBase/CalDet.h" #include "TFile.h" #include "Framework/TypeTraits.h" -#include "TPCBase/DeadChannelMapCreator.h" +#include "TPCBaseRecSim/DeadChannelMapCreator.h" namespace o2::tpc { diff --git a/Detectors/TPC/calibration/CMakeLists.txt b/Detectors/TPC/calibration/CMakeLists.txt index e5cc25230d2fc..27f7f0200bb92 100644 --- a/Detectors/TPC/calibration/CMakeLists.txt +++ b/Detectors/TPC/calibration/CMakeLists.txt @@ -58,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 @@ -118,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/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/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/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/CorrectionMapsLoader.cxx b/Detectors/TPC/calibration/src/CorrectionMapsLoader.cxx index e9d7474699ce2..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" 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/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/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/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/SpaceCharge.cxx b/Detectors/TPC/spacecharge/src/SpaceCharge.cxx index 07101bac15c23..9b6a572b46406 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" 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/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 e3943f92235ab..7c2e2db8188e8 100644 --- a/Detectors/TPC/workflow/src/CalibdEdxSpec.cxx +++ b/Detectors/TPC/workflow/src/CalibdEdxSpec.cxx @@ -29,7 +29,7 @@ #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 82e6d5075d7f0..87e339f0643f4 100644 --- a/Detectors/TPC/workflow/src/CalibratordEdxSpec.cxx +++ b/Detectors/TPC/workflow/src/CalibratordEdxSpec.cxx @@ -33,7 +33,7 @@ #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/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/GPU/Workflow/src/GPUWorkflowSpec.cxx b/GPU/Workflow/src/GPUWorkflowSpec.cxx index 2d5a955a5e911..7b1db436dbf7e 100644 --- a/GPU/Workflow/src/GPUWorkflowSpec.cxx +++ b/GPU/Workflow/src/GPUWorkflowSpec.cxx @@ -61,11 +61,11 @@ #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" diff --git a/GPU/Workflow/src/GPUWorkflowTPC.cxx b/GPU/Workflow/src/GPUWorkflowTPC.cxx index 13a3c4b6162b8..2b2f81246fc04 100644 --- a/GPU/Workflow/src/GPUWorkflowTPC.cxx +++ b/GPU/Workflow/src/GPUWorkflowTPC.cxx @@ -56,12 +56,12 @@ #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" 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" From c21d7d7a7ec080bbf31523f910935b266405a3e2 Mon Sep 17 00:00:00 2001 From: Francesco Noferini Date: Mon, 12 Jan 2026 17:47:47 +0100 Subject: [PATCH 130/701] add TOF channel in TPC timeseries (#14945) --- Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) 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 From ddcdd1f32cf09b7d153a90183eca358a1549eb0f Mon Sep 17 00:00:00 2001 From: shahoian Date: Tue, 16 Dec 2025 11:34:09 +0100 Subject: [PATCH 131/701] reduce verbosity of ITS/MFT digitizer --- Detectors/ITSMFT/common/simulation/src/Digitizer.cxx | 10 +++++----- .../Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx | 12 ++++++------ Detectors/Upgrades/ITS3/simulation/src/Digitizer.cxx | 8 ++++---- 3 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Detectors/ITSMFT/common/simulation/src/Digitizer.cxx b/Detectors/ITSMFT/common/simulation/src/Digitizer.cxx index 53e0a2fcb096f..4a8af0cbe9737 100644 --- a/Detectors/ITSMFT/common/simulation/src/Digitizer.cxx +++ b/Detectors/ITSMFT/common/simulation/src/Digitizer.cxx @@ -108,10 +108,10 @@ void Digitizer::process(const std::vector* hits, int evID, int srcID) { // digitize single event, the time must have been set beforehand - LOG(info) << "Digitizing " << mGeometry->getName() << " hits of entry " << evID << " from source " - << srcID << " at time " << mEventTime << " ROFrame= " << mNewROFrame << ")" - << " cont.mode: " << isContinuous() - << " Min/Max ROFrames " << mROFrameMin << "/" << mROFrameMax; + LOG(debug) << "Digitizing " << mGeometry->getName() << " 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) { @@ -164,7 +164,7 @@ void Digitizer::setEventTime(const o2::InteractionTimeRecord& irt) mNewROFrame = nbc / mParams.getROFrameLengthInBC(); 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; diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx index 3ee952801f0c3..7c988faebf2df 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx @@ -103,11 +103,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(); @@ -159,7 +159,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; diff --git a/Detectors/Upgrades/ITS3/simulation/src/Digitizer.cxx b/Detectors/Upgrades/ITS3/simulation/src/Digitizer.cxx index 4560a656c1762..4b0374d925401 100644 --- a/Detectors/Upgrades/ITS3/simulation/src/Digitizer.cxx +++ b/Detectors/Upgrades/ITS3/simulation/src/Digitizer.cxx @@ -76,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) { From 1dc98c8718409275e68056f2d3fd21664804fffa Mon Sep 17 00:00:00 2001 From: Matthias Kleiner Date: Thu, 9 Jan 2025 15:27:48 +0100 Subject: [PATCH 132/701] TPC space-charge: Improve GEM frame charging-up distortions Adding: - downsampling of space-charge objects - simulation of n-sectors only - some helper functions - weighted filling of charging-up of GEM frames for smoother potential - set global distortions from function --- .../include/TPCSpaceCharge/DataContainer3D.h | 3 + .../TPCSpaceCharge/PoissonSolverHelpers.h | 3 +- .../include/TPCSpaceCharge/SpaceCharge.h | 58 +++-- .../TPC/spacecharge/src/DataContainer3D.cxx | 31 ++- .../TPC/spacecharge/src/PoissonSolver.cxx | 26 +- Detectors/TPC/spacecharge/src/SpaceCharge.cxx | 231 +++++++++++++----- 6 files changed, 259 insertions(+), 93 deletions(-) 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/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 9b6a572b46406..b80d2a7606ee7 100644 --- a/Detectors/TPC/spacecharge/src/SpaceCharge.cxx +++ b/Detectors/TPC/spacecharge/src/SpaceCharge.cxx @@ -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; From b20c426d6a7678fa1dbee6491344076d9de868b6 Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Tue, 13 Jan 2026 13:28:50 +0100 Subject: [PATCH 133/701] use better criterion to add arrow support service --- Framework/Core/src/runDataProcessing.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Framework/Core/src/runDataProcessing.cxx b/Framework/Core/src/runDataProcessing.cxx index 14bdb2d8c72d9..166f26878c363 100644 --- a/Framework/Core/src/runDataProcessing.cxx +++ b/Framework/Core/src/runDataProcessing.cxx @@ -3010,8 +3010,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) { From 494f76c08b567f7660edccd151a76426fcac4f98 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 14 Jan 2026 16:45:15 +0100 Subject: [PATCH 134/701] Fix code checker report --- Detectors/EMCAL/calib/include/EMCALCalib/CellRecalibrator.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) 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 From 36176ca5c73048c3d34fb3261edd4298dd436178 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 14 Jan 2026 13:50:50 +0100 Subject: [PATCH 135/701] DPL: add test for routing messages --- Framework/Core/test/test_ForwardInputs.cxx | 74 ++++++++++++++++++++++ 1 file changed, 74 insertions(+) diff --git a/Framework/Core/test/test_ForwardInputs.cxx b/Framework/Core/test/test_ForwardInputs.cxx index fe9f70d1daadb..7081d600080b1 100644 --- a/Framework/Core/test/test_ForwardInputs.cxx +++ b/Framework/Core/test/test_ForwardInputs.cxx @@ -616,6 +616,80 @@ TEST_CASE("ForwardInputsSplitPayload") 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{}; From 7b11923549de0c2ebcd4656d8f9f98eda2e7a412 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 14 Jan 2026 13:56:36 +0100 Subject: [PATCH 136/701] DPL: add callback when inserting in the slot --- Framework/Core/include/Framework/DataRelayer.h | 4 ++++ Framework/Core/src/DataProcessingDevice.cxx | 1 + Framework/Core/src/DataRelayer.cxx | 10 ++++++++-- 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/Framework/Core/include/Framework/DataRelayer.h b/Framework/Core/include/Framework/DataRelayer.h index 012b909096317..1e010fc12f3d4 100644 --- a/Framework/Core/include/Framework/DataRelayer.h +++ b/Framework/Core/include/Framework/DataRelayer.h @@ -114,6 +114,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 +138,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/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index 3925359b056b2..343b567d8b852 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -1859,6 +1859,7 @@ void DataProcessingDevice::handleData(ServiceRegistryRef ref, InputChannelInfo& input, nMessages, nPayloadsPerHeader, + nullptr, onDrop); switch (relayed.type) { case DataRelayer::RelayChoice::Type::Backpressured: diff --git a/Framework/Core/src/DataRelayer.cxx b/Framework/Core/src/DataRelayer.cxx index 01e7a2b29fd35..ea2c4c0b73316 100644 --- a/Framework/Core/src/DataRelayer.cxx +++ b/Framework/Core/src/DataRelayer.cxx @@ -436,7 +436,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); @@ -482,6 +483,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 { @@ -512,7 +514,11 @@ 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); + if (onInsertion) { + onInsertion(services, span); + } + target.add([&span](size_t i) -> fair::mq::MessagePtr& { return span[i]; }, nPayloads + 1); mi += nPayloads; saved += nPayloads; } From 1afdd6c49095ffa5a6df708c439e282b9d640abd Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 14 Jan 2026 13:56:37 +0100 Subject: [PATCH 137/701] DPL: fix how many forwarded parts are needed In principle this is not fatal because the number of routes is always larger / equal than the number of channels by construction. Better safe than sorry. --- Framework/Core/src/DataProcessingHelpers.cxx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Framework/Core/src/DataProcessingHelpers.cxx b/Framework/Core/src/DataProcessingHelpers.cxx index 2f7a1f65f3bd3..87e7c9bf8962f 100644 --- a/Framework/Core/src/DataProcessingHelpers.cxx +++ b/Framework/Core/src/DataProcessingHelpers.cxx @@ -343,9 +343,7 @@ auto DataProcessingHelpers::routeForwardedMessageSet(FairMQDeviceProxy& proxy, const bool copyByDefault, bool consume) -> std::vector { // we collect all messages per forward in a map and send them together - std::vector forwardedParts; - forwardedParts.resize(proxy.getNumForwards()); - std::vector forwardingChoices{}; + std::vector forwardedParts(proxy.getNumForwardChannels()); for (size_t ii = 0, ie = currentSetOfInputs.size(); ii < ie; ++ii) { auto span = std::span(currentSetOfInputs[ii].messages); From 056504e47cb31862baef654a4d847aede982f93a Mon Sep 17 00:00:00 2001 From: shahoian Date: Wed, 14 Jan 2026 22:15:06 +0100 Subject: [PATCH 138/701] Fix ITS/MFT clusterization for some complex shapes, O2-6424. --- .../include/ITSMFTReconstruction/Clusterer.h | 14 ++- .../common/reconstruction/src/Clusterer.cxx | 117 +++++++++++------- 2 files changed, 80 insertions(+), 51 deletions(-) diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h index 960ce2ca33d5b..c66468905d0aa 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 } diff --git a/Detectors/ITSMFT/common/reconstruction/src/Clusterer.cxx b/Detectors/ITSMFT/common/reconstruction/src/Clusterer.cxx index 15dcc67a8967b..42e535e810a62 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/Clusterer.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/Clusterer.cxx @@ -133,15 +133,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); + } } } } @@ -214,14 +216,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 +247,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 +268,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 +355,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 +386,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 - } } //__________________________________________________ From eb3d49c31d23bd8d0c523ae7916c9ad5ba0d081c Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Wed, 14 Jan 2026 09:27:55 +0100 Subject: [PATCH 139/701] DPL Analysis: do not override error-handler reader for MC injected workflows --- Framework/Core/src/ArrowSupport.cxx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Framework/Core/src/ArrowSupport.cxx b/Framework/Core/src/ArrowSupport.cxx index c0280b144e146..c403d983325dc 100644 --- a/Framework/Core/src/ArrowSupport.cxx +++ b/Framework/Core/src/ArrowSupport.cxx @@ -684,8 +684,10 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() workflow.erase(reader); } else { // load reader algorithm before deployment - auto&& algo = PluginManager::loadAlgorithmFromPlugin("O2FrameworkAnalysisSupport", "ROOTFileReader", ctx); - reader->algorithm = CommonDataProcessors::wrapWithTimesliceConsumption(algo); + 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 } } From 5cce90740c429898ff787e978e3f24573161ed9e Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Wed, 14 Jan 2026 13:21:42 +0100 Subject: [PATCH 140/701] Fix embedding test --- prodtests/full_system_test.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prodtests/full_system_test.sh b/prodtests/full_system_test.sh index a799afbbbfd3d..8ee8b5992b846 100755 --- a/prodtests/full_system_test.sh +++ b/prodtests/full_system_test.sh @@ -71,7 +71,7 @@ else fi FST_MC_ENGINE=${FST_MC_ENGINE:-TGeant4} FST_EMBEDDING_CONFIG=${FST_EMBEDDING_CONFIG:-GeneratorPythia8.config=$O2_ROOT/prodtests/full-system-test/pythia8.cfg} -DO_EMBEDDING=${DO_EMBEDDING:-0} +DO_EMBEDDING=${DO_EMBEDDING:-1} if [[ $DO_EMBEDDING == 0 ]]; then SIM_SOURCES="o2sim" else @@ -139,7 +139,7 @@ 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 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 [[ $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 From 8c0bd3ce631100e5ee0bdc61e5d430ef1110cc29 Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Thu, 15 Jan 2026 13:27:17 +0100 Subject: [PATCH 141/701] Switch off default embedding --- prodtests/full_system_test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prodtests/full_system_test.sh b/prodtests/full_system_test.sh index 8ee8b5992b846..bf235a500cd8b 100755 --- a/prodtests/full_system_test.sh +++ b/prodtests/full_system_test.sh @@ -71,7 +71,7 @@ else fi FST_MC_ENGINE=${FST_MC_ENGINE:-TGeant4} FST_EMBEDDING_CONFIG=${FST_EMBEDDING_CONFIG:-GeneratorPythia8.config=$O2_ROOT/prodtests/full-system-test/pythia8.cfg} -DO_EMBEDDING=${DO_EMBEDDING:-1} +DO_EMBEDDING=${DO_EMBEDDING:-0} if [[ $DO_EMBEDDING == 0 ]]; then SIM_SOURCES="o2sim" else From 34d96168b97ba4c073585eafdcd3c9c93b271f8d Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Thu, 15 Jan 2026 14:43:39 +0100 Subject: [PATCH 142/701] DPL Analysis: fix for slice index builder resetting its caches in a wrong order --- Framework/Core/src/IndexBuilderHelpers.cxx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Framework/Core/src/IndexBuilderHelpers.cxx b/Framework/Core/src/IndexBuilderHelpers.cxx index d7231f72cbee8..0943dea42169c 100644 --- a/Framework/Core/src/IndexBuilderHelpers.cxx +++ b/Framework/Core/src/IndexBuilderHelpers.cxx @@ -161,14 +161,14 @@ SliceBuilder::SliceBuilder(std::shared_ptr source, arrow::M 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"); } - mListBuilder->Reset(); - mValues = nullptr; - mCounts = nullptr; - mValuePos = 0; } bool SliceBuilder::find(int idx) From 7245d49faf8a6e7cfd29d81a44f6cda5b52dfa2b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiktor=20Piero=C5=BCak?= <94726725+wpierozak@users.noreply.github.com> Date: Thu, 15 Jan 2026 17:11:35 +0100 Subject: [PATCH 143/701] AFIT-1 FV0 digitzer dead channel map (#14908) * FIT: added DeadChannelMap to FV0 Digitzier * FIT: changed default value of disable-dead-channel-map option for FV0 digitizer to false * FIT: included CCDBParamSpec.h in FV0DigitizerSpec.cxx * FIT: Fixed typo in FV0DigitizerSpec.cxx * FIT: change inputs to FV0 digitizer --- .../include/FV0Simulation/Digitizer.h | 4 +++ .../FIT/FV0/simulation/src/Digitizer.cxx | 5 ++++ .../src/FV0DigitizerSpec.cxx | 30 ++++++++++++++++--- 3 files changed, 35 insertions(+), 4 deletions(-) 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/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 From 7aa1bbc3f97df804c61ce31f0aeed65d81e520b7 Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Thu, 15 Jan 2026 19:45:39 +0100 Subject: [PATCH 144/701] DPL Analysis: Rework table input record extraction (#14944) --- .../src/AODJAlienReaderHelpers.cxx | 23 ------- .../AnalysisSupport/src/AODReaderHelpers.cxx | 25 +++++--- .../AnalysisSupport/src/AODWriterHelpers.cxx | 7 +-- .../CCDBSupport/src/AnalysisCCDBHelpers.cxx | 5 +- Framework/Core/include/Framework/ASoA.h | 63 ++++++++++++++++--- .../Core/include/Framework/AnalysisHelpers.h | 7 ++- .../Core/include/Framework/AnalysisManagers.h | 8 +-- .../Core/include/Framework/AnalysisTask.h | 8 +-- .../Framework/ArrowTableSlicingCache.h | 13 +++- .../include/Framework/ConcreteDataMatcher.h | 6 +- .../Core/include/Framework/DataSpecUtils.h | 6 ++ .../Core/include/Framework/DataSpecViews.h | 7 +++ .../Core/include/Framework/GroupSlicer.h | 2 +- .../Core/include/Framework/InputRecord.h | 22 +++++++ Framework/Core/src/AnalysisHelpers.cxx | 10 +-- Framework/Core/src/ArrowSupport.cxx | 4 +- Framework/Core/src/ArrowTableSlicingCache.cxx | 21 ++++--- Framework/Core/src/DataSpecUtils.cxx | 34 +++++++++- Framework/Core/src/IndexJSONHelpers.cxx | 12 +++- Framework/Core/src/InputRecord.cxx | 5 ++ Framework/Core/test/benchmark_EventMixing.cxx | 6 +- Framework/Core/test/test_ASoA.cxx | 5 +- Framework/Core/test/test_GroupSlicer.cxx | 33 ++++++---- Framework/TestWorkflows/CMakeLists.txt | 4 ++ .../TestWorkflows/src/o2TestHistograms.cxx | 6 +- .../TestWorkflows/src/o2TestMultisource.cxx | 47 ++++++++++++++ 26 files changed, 286 insertions(+), 103 deletions(-) create mode 100644 Framework/TestWorkflows/src/o2TestMultisource.cxx diff --git a/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx b/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx index b532c51b8d307..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 diff --git a/Framework/AnalysisSupport/src/AODReaderHelpers.cxx b/Framework/AnalysisSupport/src/AODReaderHelpers.cxx index 7f08dd0b36a64..485f3fa69edad 100644 --- a/Framework/AnalysisSupport/src/AODReaderHelpers.cxx +++ b/Framework/AnalysisSupport/src/AODReaderHelpers.cxx @@ -18,6 +18,7 @@ #include "Framework/DataProcessingHelpers.h" #include "Framework/AlgorithmSpec.h" #include "Framework/DataSpecUtils.h" +#include "Framework/DataSpecViews.h" #include "Framework/ConfigContext.h" #include "Framework/DanglingEdgesContext.h" @@ -29,6 +30,7 @@ struct Buildable { bool exclusive = false; std::string binding; std::vector labels; + std::vector matchers; header::DataOrigin origin; header::DataDescription description; header::DataHeader::SubSpecificationType version; @@ -52,6 +54,7 @@ struct Buildable { 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; @@ -68,6 +71,7 @@ struct Buildable { return { exclusive, labels, + matchers, records, outputSchema, origin, @@ -105,6 +109,7 @@ namespace struct Spawnable { std::string binding; std::vector labels; + std::vector matchers; std::vector projectors; std::vector> expressions; std::shared_ptr outputSchema; @@ -132,14 +137,17 @@ struct Spawnable { o2::framework::addLabelToSchema(outputSchema, binding.c_str()); std::vector> schemas; - for (auto& i : spec.metadata) { - if (i.name.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)); - } + 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)); + } + for (auto const& i : spec.metadata | views::filter_string_params_starts_with("input:") | std::ranges::views::transform([](auto const& param) { + return DataSpecUtils::fromMetadataString(param.defaultValue.template get()); + })) { + matchers.emplace_back(std::get(i.matcher)); } std::vector> fields; @@ -169,6 +177,7 @@ struct Spawnable { return { binding, labels, + matchers, expressions, makeProjector(), outputSchema, diff --git a/Framework/AnalysisSupport/src/AODWriterHelpers.cxx b/Framework/AnalysisSupport/src/AODWriterHelpers.cxx index 5a43683afd364..d868b7498fb76 100644 --- a/Framework/AnalysisSupport/src/AODWriterHelpers.cxx +++ b/Framework/AnalysisSupport/src/AODWriterHelpers.cxx @@ -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; diff --git a/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx index fcc856669cd92..9ec911518f754 100644 --- a/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx +++ b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx @@ -83,6 +83,7 @@ AlgorithmSpec AnalysisCCDBHelpers::fetchFromCCDB(ConfigContext const& ctx) if (m.name.starts_with("input:")) { auto name = m.name.substr(6); schemaMetadata->Append("sourceTable", name); + schemaMetadata->Append("sourceMatcher", DataSpecUtils::describe(std::get(DataSpecUtils::fromMetadataString(m.defaultValue.get()).matcher))); continue; } // Ignore the non ccdb: entries @@ -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", diff --git a/Framework/Core/include/Framework/ASoA.h b/Framework/Core/include/Framework/ASoA.h index 43079a4634e97..ec02c7e47132b 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 @@ -375,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; }; @@ -1393,6 +1400,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) { @@ -1427,6 +1440,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&&) { @@ -1477,7 +1515,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; @@ -1485,7 +1526,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})}, {}} { } @@ -1520,7 +1561,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 @@ -1667,10 +1712,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) { @@ -1679,19 +1724,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) { diff --git a/Framework/Core/include/Framework/AnalysisHelpers.h b/Framework/Core/include/Framework/AnalysisHelpers.h index 3666fe1299489..a01d14b6632a9 100644 --- a/Framework/Core/include/Framework/AnalysisHelpers.h +++ b/Framework/Core/include/Framework/AnalysisHelpers.h @@ -30,6 +30,7 @@ namespace o2::soa { struct IndexRecord { std::string label; + framework::ConcreteDataMatcher matcher; std::string columnLabel; IndexKind kind; int pos; @@ -142,6 +143,7 @@ std::vector> extractSources(ProcessingContext& pc, struct Spawner { std::string binding; std::vector labels; + std::vector matchers; std::vector> expressions; std::shared_ptr projector = nullptr; std::shared_ptr schema = nullptr; @@ -157,6 +159,7 @@ struct Spawner { struct Builder { bool exclusive; std::vector labels; + std::vector matchers; std::vector records; std::shared_ptr outputSchema; header::DataOrigin origin; @@ -258,9 +261,9 @@ inline constexpr auto getIndexMapping() ([&idx]() mutable { constexpr auto pos = o2::aod::MetadataTrait>::metadata::template getIndexPosToKey(); if constexpr (pos == -1) { - idx.emplace_back(o2::aod::label(), C::columnLabel(), IndexKind::IdxSelf, pos); + idx.emplace_back(o2::aod::label(), o2::aod::matcher(), C::columnLabel(), IndexKind::IdxSelf, pos); } else { - idx.emplace_back(o2::aod::label(), C::columnLabel(), getIndexKind(), pos); + idx.emplace_back(o2::aod::label(), o2::aod::matcher(), C::columnLabel(), getIndexKind(), pos); } }.template operator()>(), ...); diff --git a/Framework/Core/include/Framework/AnalysisManagers.h b/Framework/Core/include/Framework/AnalysisManagers.h index fbb499940b9b9..5112e3659f4aa 100644 --- a/Framework/Core/include/Framework/AnalysisManagers.h +++ b/Framework/Core/include/Framework/AnalysisManagers.h @@ -38,7 +38,7 @@ 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 @@ -151,7 +151,7 @@ template 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 = T::base_specs(); for (auto base_spec : base_specs) { @@ -586,7 +586,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) { @@ -604,7 +604,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/AnalysisTask.h b/Framework/Core/include/Framework/AnalysisTask.h index 53f6bc0f862d6..c50b5358990de 100644 --- a/Framework/Core/include/Framework/AnalysisTask.h +++ b/Framework/Core/include/Framework/AnalysisTask.h @@ -75,11 +75,11 @@ struct AnalysisDataProcessorBuilder { auto key = std::string{"fIndex"} + o2::framework::cutString(soa::getLabelFromType>()); ([&bk, &bku, &key, enabled]() mutable { 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); } } }(), @@ -214,7 +214,7 @@ struct AnalysisDataProcessorBuilder { 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(); } 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/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/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 162a12419594e..b38866d8aa6fd 100644 --- a/Framework/Core/include/Framework/DataSpecViews.h +++ b/Framework/Core/include/Framework/DataSpecViews.h @@ -43,6 +43,13 @@ static auto filter_string_params_with(std::string match) }); } +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) { 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/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/src/AnalysisHelpers.cxx b/Framework/Core/src/AnalysisHelpers.cxx index b8e0348d5df9c..f2ecb2d68ce28 100644 --- a/Framework/Core/src/AnalysisHelpers.cxx +++ b/Framework/Core/src/AnalysisHelpers.cxx @@ -185,18 +185,18 @@ std::string serializeIndexRecords(std::vector& irs) return osm.str(); } -std::vector> extractSources(ProcessingContext& pc, std::vector const& labels) +std::vector> extractSources(ProcessingContext& pc, std::vector const& matchers) { std::vector> tables; - for (auto const& label : labels) { - tables.emplace_back(pc.inputs().get(label.c_str())->asArrowTable()); + for (auto const& matcher : matchers) { + tables.emplace_back(pc.inputs().get(matcher)->asArrowTable()); } return tables; } std::shared_ptr Spawner::materialize(ProcessingContext& pc) const { - auto tables = extractSources(pc, labels); + 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(); @@ -212,7 +212,7 @@ std::shared_ptr Builder::materialize(ProcessingContext& pc) builders->reserve(records.size()); } std::shared_ptr result; - auto tables = extractSources(pc, labels); + auto tables = extractSources(pc, matchers); result = o2::soa::IndexBuilder::materialize(*builders.get(), std::move(tables), records, outputSchema, exclusive); return result; } diff --git a/Framework/Core/src/ArrowSupport.cxx b/Framework/Core/src/ArrowSupport.cxx index c403d983325dc..95e763343671a 100644 --- a/Framework/Core/src/ArrowSupport.cxx +++ b/Framework/Core/src/ArrowSupport.cxx @@ -753,7 +753,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()); } @@ -762,7 +762,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 75b4bbfac701d..634c51f71f5a6 100644 --- a/Framework/Core/src/ArrowTableSlicingCache.cxx +++ b/Framework/Core/src/ArrowTableSlicingCache.cxx @@ -37,12 +37,12 @@ std::shared_ptr GetColumnByNameCI(std::shared_ptrenabled && enabled) { + list.emplace_back(entry); + } else if (!locate->enabled && entry.enabled) { locate->enabled = true; } } @@ -110,7 +110,7 @@ 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()); } @@ -169,7 +169,7 @@ 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()); } @@ -210,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::find(bindingsKeys.begin(), bindingsKeys.end(), bindingKey); if (locate != bindingsKeys.end()) { return std::distance(bindingsKeys.begin(), locate); } @@ -219,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::find(bindingsKeysUnsorted.begin(), bindingsKeysUnsorted.end(), bindingKey); if (locate_unsorted != bindingsKeysUnsorted.end()) { return std::distance(bindingsKeysUnsorted.begin(), locate_unsorted); } @@ -269,7 +269,10 @@ SliceInfoUnsortedPtr ArrowTableSlicingCache::getCacheUnsortedForPos(int pos) con void ArrowTableSlicingCache::validateOrder(Entry const& bindingKey, const std::shared_ptr& input) { - auto const& [target, key, enabled] = bindingKey; + 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; 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/IndexJSONHelpers.cxx b/Framework/Core/src/IndexJSONHelpers.cxx index 19ae94a4bcd4c..a5c6c70579599 100644 --- a/Framework/Core/src/IndexJSONHelpers.cxx +++ b/Framework/Core/src/IndexJSONHelpers.cxx @@ -41,6 +41,7 @@ struct IndexRecordsReader : public rapidjson::BaseReaderHandler& w, std::vector const& schema, ConcreteDataMatcher concrete) { size_t inputIndex = 0; 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_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/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/o2TestHistograms.cxx b/Framework/TestWorkflows/src/o2TestHistograms.cxx index 640a165fb91ff..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; @@ -72,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) { @@ -110,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)}; +} From b5dfe5011ab8a7399ee9c41dba66175feee68033 Mon Sep 17 00:00:00 2001 From: ehellbar Date: Thu, 15 Jan 2026 19:50:34 +0100 Subject: [PATCH 145/701] start_tmux.sh: create ED directory to avoid filesystem errors during o2-eve-export-workflow (#14961) --- prodtests/full-system-test/start_tmux.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/prodtests/full-system-test/start_tmux.sh b/prodtests/full-system-test/start_tmux.sh index 22b658856803a..fb69cc1e6baec 100755 --- a/prodtests/full-system-test/start_tmux.sh +++ b/prodtests/full-system-test/start_tmux.sh @@ -91,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 From 0a8627db853ed9bf9537a2792e2e2c5afa4e3c94 Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Fri, 16 Jan 2026 15:00:07 +0100 Subject: [PATCH 146/701] Switch option from external to hybrid (#14951) --- .../include/Generators/GeneratorHybridParam.h | 7 +-- Generators/src/GeneratorFactory.cxx | 6 +++ .../GeneratorHyperloopHybrid.ini | 3 ++ .../GeneratorHyperloopHybridCocktail.ini | 3 ++ run/SimExamples/ExternalToHybrid/README.md | 45 ++++++++++++++++ .../ExternalToHybrid/cocktail.json | 49 +++++++++++++++++ run/SimExamples/ExternalToHybrid/rundpl.sh | 54 +++++++++++++++++++ .../ExternalToHybrid/sequential.json | 35 ++++++++++++ 8 files changed, 199 insertions(+), 3 deletions(-) create mode 100644 run/SimExamples/ExternalToHybrid/GeneratorHyperloopHybrid.ini create mode 100644 run/SimExamples/ExternalToHybrid/GeneratorHyperloopHybridCocktail.ini create mode 100644 run/SimExamples/ExternalToHybrid/README.md create mode 100644 run/SimExamples/ExternalToHybrid/cocktail.json create mode 100755 run/SimExamples/ExternalToHybrid/rundpl.sh create mode 100644 run/SimExamples/ExternalToHybrid/sequential.json 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/src/GeneratorFactory.cxx b/Generators/src/GeneratorFactory.cxx index d04e785402915..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 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 From c5c328283cc56fb8fe2d3d17912bf5e738cd02fe Mon Sep 17 00:00:00 2001 From: Andrea Sofia Triolo Date: Fri, 16 Jan 2026 16:23:38 +0100 Subject: [PATCH 147/701] ALICE3-TRK: partial fix to issue #14959 (#14965) * ALICE3-TRK: fix matrix path for cylindrical ML and OT geometries * ALICE3-TRK: setting turboStaves and staggered layouts as default for ML and OT, respectively --- .../TRK/base/include/TRKBase/GeometryTGeo.h | 4 ++++ .../TRK/base/include/TRKBase/TRKBaseParam.h | 7 +++++-- .../ALICE3/TRK/base/src/GeometryTGeo.cxx | 16 ++++++++++++++++ 3 files changed, 25 insertions(+), 2 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h index ee6f5f33fc9fe..deec53950cd5f 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h @@ -15,6 +15,7 @@ #include #include #include "DetectorsCommonDataFormats/DetID.h" +#include "TRKBase/TRKBaseParam.h" namespace o2 { @@ -221,6 +222,9 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache 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; }; diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h index 3f3f656c4b417..7f2f7f32b79d9 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h @@ -38,8 +38,11 @@ struct TRKBaseParam : public o2::conf::ConfigurableParamHelper { eOverallGeom overallGeom = kDefaultRadii; // Overall geometry option, to be used in Detector::buildTRKMiddleOuterLayers - eLayout layoutML = kCylinder; // Type of segmentation for the middle layers - eLayout layoutOL = kCylinder; // Type of segmentation for the outer layers + eLayout layoutML = kTurboStaves; // Type of segmentation for the middle layers + eLayout layoutOL = kStaggered; // Type of segmentation for the outer layers + + eLayout getLayoutML() const { return layoutML; } + eLayout getLayoutOL() const { return layoutOL; } O2ParamDef(TRKBaseParam, "TRKBase"); }; diff --git a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx index bfa23fe57c01a..b32c89164f18a 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx @@ -76,6 +76,11 @@ 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(); @@ -398,6 +403,17 @@ TString GeometryTGeo::getMatrixPath(int index) const // 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 From 8ead4583abb166786d6e76c6ca9f8fd90e595e76 Mon Sep 17 00:00:00 2001 From: tubagundem Date: Mon, 8 Dec 2025 15:58:08 +0100 Subject: [PATCH 148/701] TPC_MC_anchoring_simple: Added per region relative gas gain to simulate the change in the voltage settings of GEMs --- Detectors/TPC/base/include/TPCBase/ParameterGEM.h | 1 + .../TPC/simulation/include/TPCSimulation/GEMAmplification.h | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) 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/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; } } From 7cd4b7f29f7daf912f5773149f9d96d7d707a21f Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sat, 17 Jan 2026 00:00:15 +0100 Subject: [PATCH 149/701] DPL: keep code checker happy (#14966) The code checker complains about the unique_ptr going out of scope. However this is a false positive because such unique_ptr has a custom deletion policy to mimick the behavior of an observer_ptr. In order to keep the code checker happy, we use release, so that the bare pointer is returned without any complain. Given the custom deleter, the semantic is actually the same. --- Framework/Core/include/Framework/AnalysisManagers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Framework/Core/include/Framework/AnalysisManagers.h b/Framework/Core/include/Framework/AnalysisManagers.h index 5112e3659f4aa..fd41a079c6570 100644 --- a/Framework/Core/include/Framework/AnalysisManagers.h +++ b/Framework/Core/include/Framework/AnalysisManagers.h @@ -170,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; } From db7170bd9cd8ba0fd5622a617af7d4f78b584cb3 Mon Sep 17 00:00:00 2001 From: Andrea Sofia Triolo Date: Sat, 17 Jan 2026 01:14:20 +0100 Subject: [PATCH 150/701] ALICE3-TRK: fix the length of the layers for the kCylinder layout for ML and OT (#14967) --- .../ALICE3/TRK/simulation/src/TRKLayer.cxx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx index a24a8eea0be27..c4683f28918d0 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx @@ -50,7 +50,7 @@ TGeoVolume* TRKLayer::createSensor(std::string type) TGeoShape* sensor; if (type == "cylinder") { - sensor = new TGeoTube(mInnerRadius, mInnerRadius + mSensorThickness, mChipLength / 2); // TO BE CHECKED !!! + sensor = new TGeoTube(mInnerRadius, mInnerRadius + mSensorThickness, (constants::moduleMLOT::length * mNumberOfModules) / 2); // TO BE CHECKED !!! } else if (type == "flat") { sensor = new TGeoBBox((mChipWidth - mDeadzoneWidth) / 2, mSensorThickness / 2, mChipLength / 2); // TO BE CHECKED !!! } else { @@ -71,7 +71,7 @@ TGeoVolume* TRKLayer::createDeadzone(std::string type) TGeoShape* deadzone; if (type == "cylinder") { - deadzone = new TGeoTube(mInnerRadius, mInnerRadius + mSensorThickness, mChipLength / 2); // TO BE CHECKED !!! + 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 { @@ -92,7 +92,7 @@ TGeoVolume* TRKLayer::createMetalStack(std::string type) TGeoShape* metalStack; if (type == "cylinder") { - metalStack = new TGeoTube(mInnerRadius + mSensorThickness, mInnerRadius + mChipThickness, mChipLength / 2); // TO BE CHECKED !!! + 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 { @@ -118,7 +118,7 @@ TGeoVolume* TRKLayer::createChip(std::string type) TGeoVolume* metalVol; if (type == "cylinder") { - chip = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, mChipLength / 2); + chip = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, (constants::moduleMLOT::length * mNumberOfModules) / 2); chipVol = new TGeoVolume(chipName.c_str(), chip, medSi); sensVol = createSensor("cylinder"); @@ -175,7 +175,7 @@ TGeoVolume* TRKLayer::createModule(std::string type) TGeoVolume* moduleVol; if (type == "cylinder") { - module = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, mChipLength / 2); + module = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, (constants::moduleMLOT::length * mNumberOfModules) / 2); moduleVol = new TGeoVolume(moduleName.c_str(), module, medAir); TGeoVolume* chipVol = createChip("cylinder"); @@ -269,7 +269,7 @@ TGeoVolume* TRKLayer::createStave(std::string type) TGeoVolume* staveVol; if (type == "cylinder") { - stave = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, mChipLength / 2); + stave = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, (constants::moduleMLOT::length * mNumberOfModules) / 2); staveVol = new TGeoVolume(staveName.c_str(), stave, medAir); TGeoVolume* moduleVol = createModule("cylinder"); @@ -341,7 +341,7 @@ void TRKLayer::createLayer(TGeoVolume* motherVolume) TGeoVolume* layerVol; if (mLayout == eLayout::kCylinder) { - layer = new TGeoTube(mInnerRadius - 0.333 * layerThickness, mInnerRadius + 0.667 * layerThickness, mChipLength / 2); + layer = new TGeoTube(mInnerRadius - 0.333 * layerThickness, mInnerRadius + 0.667 * layerThickness, (constants::moduleMLOT::length * mNumberOfModules) / 2); layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); TGeoVolume* staveVol = createStave("cylinder"); From c5f00f245e4f185f7fb761b13bdd07d7ec659df2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Mon, 19 Jan 2026 08:37:02 +0100 Subject: [PATCH 151/701] A3: Fix geometry building of FT3 (#14968) Added logging for material initialization and layout creation. --- .../ALICE3/FT3/simulation/src/FT3Module.cxx | 53 +++++++++++-------- 1 file changed, 30 insertions(+), 23 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx index 87f5f27da6a38..efcad74bc2cb9 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx @@ -18,6 +18,7 @@ #include #include #include +#include #include #include #include @@ -41,12 +42,12 @@ TGeoMedium* FT3Module::AluminumMed = nullptr; void FT3Module::initialize_materials() { - + LOG(debug) << "FT3Module: initialize_materials"; if (siliconMat) { return; } - TGeoManager* gGeoManager = gGeoManager; + TGeoManager* geoManager = gGeoManager; auto* itsH = new TGeoElement("FT3_H", "Hydrogen", 1, 1.00794); auto* itsC = new TGeoElement("FT3_C", "Carbon", 6, 12.0107); @@ -73,6 +74,7 @@ void FT3Module::initialize_materials() 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) @@ -83,7 +85,8 @@ double calculate_y_circle(double x, double radius) 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) { - TGeoManager* gGeoManager = gGeoManager; + LOG(debug) << "FT3Module: create_layout - Layer " << layerNumber << ", Direction " << direction << ", Face " << face; + TGeoManager* geoManager = gGeoManager; FT3Module::initialize_materials(); @@ -479,13 +482,13 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double 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 = gGeoManager->MakeBox(sensor_name.c_str(), siliconMed, active_width / 2, active_height / 2, silicon_thickness / 2); + 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 = gGeoManager->MakeBox(inactive_name.c_str(), siliconMed, (sensor_width - active_width) / 2, sensor_height / 2, silicon_thickness / 2); + 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)); @@ -493,19 +496,19 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double } else { std::string sensor_name = "FT3sensor_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); - sensor = gGeoManager->MakeBox(sensor_name.c_str(), siliconMed, active_width / 2, sensor_height / 2, silicon_thickness / 2); + 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 = gGeoManager->MakeBox(inactive_name_left.c_str(), siliconMed, inactive_width / 2, sensor_height / 2, silicon_thickness / 2); + 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 = gGeoManager->MakeBox(inactive_name_right.c_str(), siliconMed, inactive_width / 2, sensor_height / 2, silicon_thickness / 2); + 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)); @@ -513,21 +516,21 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double // 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 = gGeoManager->MakeBox(glue_up_name.c_str(), epoxyMed, sensor_width / 2, sensor_height / 2, epoxy_thickness / 2); + 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 = gGeoManager->MakeBox(alu_name.c_str(), AluminumMed, sensor_width / 2, sensor_height / 2, copper_thickness / 2); + 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 = gGeoManager->MakeBox(copper_name.c_str(), copperMed, sensor_width / 2, sensor_height / 2, copper_thickness / 2); + 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)); @@ -535,14 +538,14 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double // kapton std::string fpc_name = "FT3fpc_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); - sensor = gGeoManager->MakeBox(fpc_name.c_str(), kaptonMed, sensor_width / 2, sensor_height / 2, kapton_thickness / 2); + 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 = gGeoManager->MakeBox(glue_down_name.c_str(), epoxyMed, sensor_width / 2, sensor_height / 2, epoxy_thickness / 2); + 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)); @@ -612,14 +615,14 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double // 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 = gGeoManager->MakeBox(glue_down_name.c_str(), epoxyMed, sensor_width / 2, sensor_height / 2, epoxy_thickness / 2); + 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 = gGeoManager->MakeBox(fpc_name.c_str(), kaptonMed, sensor_width / 2, sensor_height / 2, kapton_thickness / 2); + 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)); @@ -627,14 +630,14 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double 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 = gGeoManager->MakeBox(alu_name.c_str(), AluminumMed, sensor_width / 2, sensor_height / 2, copper_thickness / 2); + 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 = gGeoManager->MakeBox(copper_name.c_str(), copperMed, sensor_width / 2, sensor_height / 2, copper_thickness / 2); + 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)); @@ -642,7 +645,7 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double // 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 = gGeoManager->MakeBox(glue_up_name.c_str(), epoxyMed, sensor_width / 2, sensor_height / 2, epoxy_thickness / 2); + 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)); @@ -650,13 +653,13 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double 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 = gGeoManager->MakeBox(sensor_name.c_str(), siliconMed, active_width / 2, active_height / 2, silicon_thickness / 2); + 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 = gGeoManager->MakeBox(inactive_name.c_str(), siliconMed, (sensor_width - active_width) / 2, sensor_height / 2, silicon_thickness / 2); + 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)); @@ -664,21 +667,21 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double } 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 = gGeoManager->MakeBox(sensor_name.c_str(), siliconMed, active_width / 2, sensor_height / 2, silicon_thickness / 2); + 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 = gGeoManager->MakeBox(inactive_name_left.c_str(), siliconMed, inactive_width / 2, sensor_height / 2, silicon_thickness / 2); + 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 = gGeoManager->MakeBox(inactive_name_right.c_str(), siliconMed, inactive_width / 2, sensor_height / 2, silicon_thickness / 2); + 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)); @@ -691,9 +694,13 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double 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"; } From 705c73cda6dded60f2fe39c724842959b76134b6 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Fri, 16 Jan 2026 10:08:08 +0100 Subject: [PATCH 152/701] GPU CUDA: Do not link against nvrtc library, which is not used --- GPU/GPUTracking/Base/cuda/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/GPUTracking/Base/cuda/CMakeLists.txt b/GPU/GPUTracking/Base/cuda/CMakeLists.txt index e7a579bec794d..05ed091eb83ea 100644 --- a/GPU/GPUTracking/Base/cuda/CMakeLists.txt +++ b/GPU/GPUTracking/Base/cuda/CMakeLists.txt @@ -148,7 +148,7 @@ if (onnxruntime_FOUND) endif() # Setting target architecture and adding GPU libraries -target_link_libraries(${targetName} PRIVATE cuda cudart nvrtc) +target_link_libraries(${targetName} PRIVATE cuda cudart) set_target_cuda_arch(${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) From 36b13b4dde9cce37ed414ea310d8a6a635d4ce31 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Fri, 16 Jan 2026 10:36:46 +0100 Subject: [PATCH 153/701] GPU: Fix direct memory allocation debug message --- GPU/GPUTracking/Base/GPUReconstruction.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/GPUTracking/Base/GPUReconstruction.cxx b/GPU/GPUTracking/Base/GPUReconstruction.cxx index ef336526080b9..fbbe815f63c33 100644 --- a/GPU/GPUTracking/Base/GPUReconstruction.cxx +++ b/GPU/GPUTracking/Base/GPUReconstruction.cxx @@ -764,7 +764,7 @@ 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; } From 9ca7f3a7219d04b10219c6a653b9bf91cd2bf94c Mon Sep 17 00:00:00 2001 From: David Rohr Date: Fri, 16 Jan 2026 10:49:17 +0100 Subject: [PATCH 154/701] GPU: Fix crash with --noEvents option, and improve some debug messages --- .../Standalone/Benchmark/standalone.cxx | 36 ++++++++++--------- 1 file changed, 20 insertions(+), 16 deletions(-) diff --git a/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx b/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx index b9825bc6da481..5fa9da23d7423 100644 --- a/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx +++ b/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx @@ -173,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) { @@ -297,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()); } @@ -781,13 +782,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) { @@ -824,11 +829,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"); @@ -856,7 +857,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)) { @@ -889,12 +890,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; @@ -907,6 +910,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; } From fbe64c88c1e007d21024fbfd7f799a6fe4b1918a Mon Sep 17 00:00:00 2001 From: shahoian Date: Thu, 15 Jan 2026 22:35:14 +0100 Subject: [PATCH 155/701] Optionally refit ITS outward seeding with inward refit result By default set to false as no effect of repeating this fit was seen so far. --- .../GPU/ITStrackingGPU/TrackingKernels.h | 1 + .../tracking/GPU/cuda/TrackerTraitsGPU.cxx | 1 + .../ITS/tracking/GPU/cuda/TrackingKernels.cu | 32 +++++++++++++++++++ .../include/ITStracking/Configuration.h | 1 + .../include/ITStracking/TrackingConfigParam.h | 1 + .../ITSMFT/ITS/tracking/src/Configuration.cxx | 1 + .../ITSMFT/ITS/tracking/src/TrackerTraits.cxx | 16 ++++++++++ 7 files changed, 53 insertions(+) diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h index a4e4328b3aa22..6e0427f5413ba 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h @@ -219,6 +219,7 @@ void trackSeedHandler(CellSeed* trackSeeds, 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, diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx index f94147747a475..c4a5cfb4e26b3 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx @@ -336,6 +336,7 @@ void TrackerTraitsGPU::findRoads(const int iteration) startLevel, // const int startLevel, this->mTrkParams[0].MaxChi2ClusterAttachment, // float maxChi2ClusterAttachment this->mTrkParams[0].MaxChi2NDF, // float maxChi2NDF + this->mTrkParams[0].RepeatRefitOut, this->mTrkParams[0].ReseedIfShorter, this->mTrkParams[0].ShiftRefToCluster, mTimeFrameGPU->getDevicePropagator(), // const o2::base::Propagator* propagator diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu index d9136cb96d00e..85689488f5f6e 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -291,6 +291,7 @@ GPUg() void __launch_bounds__(256, 1) fitTrackSeedsKernel( 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) @@ -337,6 +338,34 @@ GPUg() void __launch_bounds__(256, 1) fitTrackSeedsKernel( if (!fitSuccess || temporaryTrack.getPt() < minPts[nLayers - temporaryTrack.getNClusters()]) { continue; } + if (repeatRefitOut) { // repeat outward refit seeding and linearizing with the stable inward fit result + o2::track::TrackParCov saveInw{temporaryTrack}; + linRef = saveInw; // use refitted track as lin.reference + float saveChi2 = temporaryTrack.getChi2(); + temporaryTrack.resetCovariance(); + temporaryTrack.setCov(temporaryTrack.getQ2Pt() * temporaryTrack.getQ2Pt() * temporaryTrack.getCov()[o2::track::CovLabels::kSigQ2Pt2], o2::track::CovLabels::kSigQ2Pt2); + temporaryTrack.setChi2(0); + fitSuccess = fitTrack(temporaryTrack, // TrackITSExt& track, + 0, // int lastLayer, + nLayers, // int firstLayer, + 1, // int firstCluster, + maxChi2ClusterAttachment, // float maxChi2ClusterAttachment, + maxChi2NDF, // float maxChi2NDF, + o2::constants::math::VeryBig, // float maxQoverPt, + 0, // nCl, + bz, // float bz, + foundTrackingFrameInfo, // TrackingFrameInfo** trackingFrameInfo, + propagator, // const o2::base::Propagator* propagator, + matCorrType, // o2::base::PropagatorF::MatCorrType matCorrType + &linRef, + shifRefToCluster); + if (!fitSuccess) { + continue; + } + temporaryTrack.getParamOut() = temporaryTrack.getParamIn(); + temporaryTrack.getParamIn() = saveInw; + temporaryTrack.setChi2(saveChi2); + } tracks[iCurrentTrackSeedIndex] = temporaryTrack; } } @@ -1174,6 +1203,7 @@ void trackSeedHandler(CellSeed* trackSeeds, 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, @@ -1195,6 +1225,7 @@ void trackSeedHandler(CellSeed* trackSeeds, maxChi2ClusterAttachment, // float maxChi2NDF, // float reseedIfShorter, // int + repeatRefitOut, // bool shiftRefToCluster, // bool propagator, // const o2::base::Propagator* matCorrType); // o2::base::PropagatorF::MatCorrType @@ -1375,6 +1406,7 @@ template void trackSeedHandler(CellSeed<7>* trackSeeds, 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, diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h index 000c8fe822498..1019a3e3d45a9 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h @@ -69,6 +69,7 @@ struct TrackingParameters { 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; + bool RepeatRefitOut = true; // 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; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h index 2a3506f17fa2f..0529bd53f2073 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h @@ -98,6 +98,7 @@ struct TrackerParamConfig : public o2::conf::ConfigurableParamHelper TrackingMode::getTrackingParameters(TrackingMode p.MinPt[lslot] *= bFactor; } p.ReseedIfShorter = tc.reseedIfShorter; + p.RepeatRefitOut = tc.repeatRefitOut; p.ShiftRefToCluster = tc.shiftRefToCluster; p.createArtefactLabels = tc.createArtefactLabels; diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx index 6b237ad0a63e8..fe67eadaf6f72 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx @@ -778,6 +778,22 @@ void TrackerTraits::findRoads(const int iteration) if (!fitSuccess || temporaryTrack.getPt() < mTrkParams[iteration].MinPt[mTrkParams[iteration].NLayers - temporaryTrack.getNClusters()]) { return 0; } + if (mTrkParams[0].RepeatRefitOut) { // repeat outward refit seeding and linearizing with the stable inward fit result + o2::track::TrackParCov saveInw{temporaryTrack}; + linRef = saveInw; // use refitted track as lin.reference + float saveChi2 = temporaryTrack.getChi2(); + temporaryTrack.resetCovariance(); + temporaryTrack.setCov(temporaryTrack.getQ2Pt() * temporaryTrack.getQ2Pt() * temporaryTrack.getCov()[o2::track::CovLabels::kSigQ2Pt2], o2::track::CovLabels::kSigQ2Pt2); + temporaryTrack.setChi2(0); + fitSuccess = fitTrack(temporaryTrack, 0, mTrkParams[0].NLayers, 1, mTrkParams[0].MaxChi2ClusterAttachment, mTrkParams[0].MaxChi2NDF, o2::constants::math::VeryBig, 0, &linRef); + if (!fitSuccess) { + return 0; + } + temporaryTrack.getParamOut() = temporaryTrack.getParamIn(); + temporaryTrack.getParamIn() = saveInw; + temporaryTrack.setChi2(saveChi2); + } + if constexpr (decltype(Tag)::value == PassMode::OnePass::value) { tracks.push_back(temporaryTrack); } else if constexpr (decltype(Tag)::value == PassMode::TwoPassCount::value) { From dd6691384326f1f46bd7e186d1550c116152ea97 Mon Sep 17 00:00:00 2001 From: Ernst Hellbar Date: Mon, 19 Jan 2026 10:52:57 +0100 Subject: [PATCH 156/701] dpl-workflow.sh: increase pvertexer.timeMarginVertexTime to 5 for sync raw pp --- prodtests/full-system-test/dpl-workflow.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prodtests/full-system-test/dpl-workflow.sh b/prodtests/full-system-test/dpl-workflow.sh index db491da5ebec5..f55605d1da485 100755 --- a/prodtests/full-system-test/dpl-workflow.sh +++ b/prodtests/full-system-test/dpl-workflow.sh @@ -133,7 +133,7 @@ if [[ $SYNCMODE == 1 ]]; then 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" From c990996954857d60b683fadc3bc037f055276c5d Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Mon, 19 Jan 2026 20:51:36 +0100 Subject: [PATCH 157/701] DPL Analysis: Use dangling edges context in more places (#14953) --- .../AnalysisSupport/src/AODWriterHelpers.cxx | 21 ++++--- .../CCDBSupport/src/AnalysisCCDBHelpers.cxx | 59 ++++++++++--------- .../CCDBSupport/src/AnalysisCCDBHelpers.h | 2 +- .../Core/include/Framework/AnalysisTask.h | 4 +- Framework/Core/src/AnalysisSupportHelpers.cxx | 4 +- 5 files changed, 45 insertions(+), 45 deletions(-) diff --git a/Framework/AnalysisSupport/src/AODWriterHelpers.cxx b/Framework/AnalysisSupport/src/AODWriterHelpers.cxx index d868b7498fb76..b76ffca13977e 100644 --- a/Framework/AnalysisSupport/src/AODWriterHelpers.cxx +++ b/Framework/AnalysisSupport/src/AODWriterHelpers.cxx @@ -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 @@ -241,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>>(); @@ -278,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) { @@ -474,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/CCDBSupport/src/AnalysisCCDBHelpers.cxx b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx index 9ec911518f754..413adfddecf04 100644 --- a/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx +++ b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx @@ -67,38 +67,39 @@ 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); - schemaMetadata->Append("sourceMatcher", DataSpecUtils::describe(std::get(DataSpecUtils::fromMetadataString(m.defaultValue.get()).matcher))); - 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); + schemaMetadata->Append("sourceMatcher", DataSpecUtils::describe(std::get(DataSpecUtils::fromMetadataString(m.defaultValue.get()).matcher))); + 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; @@ -129,11 +130,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/include/Framework/AnalysisTask.h b/Framework/Core/include/Framework/AnalysisTask.h index c50b5358990de..4f8a9e719e4b9 100644 --- a/Framework/Core/include/Framework/AnalysisTask.h +++ b/Framework/Core/include/Framework/AnalysisTask.h @@ -521,7 +521,7 @@ DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args) std::vector expressionInfos; /// 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([&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()); @@ -620,7 +620,7 @@ DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args) } // reset pre-slice for the next dataframe auto slices = pc.services().get(); - homogeneous_apply_refs([&pc, &slices](auto& element) { + homogeneous_apply_refs([&slices](auto& element) { return analysis_task_parsers::updateSliceInfo(element, slices); }, *(task.get())); diff --git a/Framework/Core/src/AnalysisSupportHelpers.cxx b/Framework/Core/src/AnalysisSupportHelpers.cxx index 15b56f9afbff5..7edf9a2d8d27f 100644 --- a/Framework/Core/src/AnalysisSupportHelpers.cxx +++ b/Framework/Core/src/AnalysisSupportHelpers.cxx @@ -98,7 +98,7 @@ std::shared_ptr AnalysisSupportHelpers::getDataOutputDirecto if (!keepString.empty()) { dod->reset(); std::string d("dangling"); - if (d.find(keepString) == 0) { + if (d.starts_with(keepString) == 0) { // use the dangling outputs std::vector danglingOutputs; for (auto ii = 0u; ii < OutputsInputs.size(); ii++) { @@ -144,7 +144,7 @@ void AnalysisSupportHelpers::addMissingOutputsToSpawner(std::vector sinks::append_to{publisher.outputs}; // append them to the publisher outputs std::vector additionalInputs; - for (auto& input : requestedSpecials | views::filter_not_matching(providedSpecials)) { + for (auto const& input : requestedSpecials | views::filter_not_matching(providedSpecials)) { input.metadata | views::filter_string_params_with("input:") | views::params_to_input_specs() | From a6471db3246688e5ad4302754449cc9b6f756a81 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Mon, 19 Jan 2026 13:59:07 +0100 Subject: [PATCH 158/701] DPL: more preparation for earlier forwarding Separate code in a different commit to minimize the critical changes. --- Framework/Core/src/DataProcessingDevice.cxx | 60 +++++++++++++++++++++ Framework/Core/src/DataRelayer.cxx | 9 ++-- 2 files changed, 66 insertions(+), 3 deletions(-) diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index 343b567d8b852..91f5fd0c2d547 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -617,6 +617,20 @@ 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. + auto forwardedParts = DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, true, consume); + + O2_SIGNPOST_END(forwarding, sid, "forwardInputs", "Forwarding done"); +}; + extern volatile int region_read_global_dummy_variable; volatile int region_read_global_dummy_variable; @@ -1680,6 +1694,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.canForwardEarly || 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 @@ -1854,6 +1913,7 @@ 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, diff --git a/Framework/Core/src/DataRelayer.cxx b/Framework/Core/src/DataRelayer.cxx index ea2c4c0b73316..d9c340cd9c225 100644 --- a/Framework/Core/src/DataRelayer.cxx +++ b/Framework/Core/src/DataRelayer.cxx @@ -499,6 +499,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. @@ -515,9 +521,6 @@ DataRelayer::RelayChoice continue; } auto span = std::span(messages + mi, messages + mi + nPayloads + 1); - if (onInsertion) { - onInsertion(services, span); - } target.add([&span](size_t i) -> fair::mq::MessagePtr& { return span[i]; }, nPayloads + 1); mi += nPayloads; saved += nPayloads; From ed8276cbddc8b3928c59fd6ee2b68dd60bb6afe1 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Mon, 19 Jan 2026 13:59:07 +0100 Subject: [PATCH 159/701] DPL: earlier forwarding This anticipates the forwarding to the earliest possible moment, i.e. when we are about to insert the messages in a slot. This is the earliest moment we can guarantee messages will be seen only once. --- .../include/Framework/DataProcessingContext.h | 8 ++- Framework/Core/src/DataProcessingDevice.cxx | 53 ++++++++++++++----- Framework/Core/src/DataRelayer.cxx | 2 + 3 files changed, 49 insertions(+), 14 deletions(-) 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/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index 91f5fd0c2d547..f65477c573772 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -1050,10 +1050,25 @@ void DataProcessingDevice::fillContext(DataProcessorContext& context, DeviceCont }; } - auto decideEarlyForward = [&context, &deviceContext, &spec, this]() -> bool { + auto decideEarlyForward = [&context, &deviceContext, &spec, this]() -> ForwardPolicy { + ForwardPolicy defaultEarlyForwardPolicy = getenv("DPL_OLD_EARLY_FORWARD") ? ForwardPolicy::AtCompletionPolicySatisified : ForwardPolicy::AtInjection; + /// We must make sure there is no optional /// if we want to optimize the forwarding - bool canForwardEarly = (spec.forwards.empty() == false) && deviceContext.processingPolicies.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) { @@ -1061,25 +1076,25 @@ void DataProcessingDevice::fillContext(DataProcessorContext& context, DeviceCont onlyConditions = false; } if (DataSpecUtils::partialMatch(forwarded.matcher, o2::header::DataDescription{"RAWDATA"}) && deviceContext.processingPolicies.earlyForward == EarlyForwardPolicy::NORAW) { - context.canForwardEarly = false; + 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() @@ -1700,7 +1715,7 @@ auto forwardOnInsertion(ServiceRegistryRef& ref, std::span auto& spec = ref.get(); auto& context = ref.get(); - if (!context.canForwardEarly || spec.forwards.empty()) { + if (context.forwardPolicy == ForwardPolicy::AfterProcessing || spec.forwards.empty()) { O2_SIGNPOST_EVENT_EMIT(device, sid, "device", "Early forwardinding not enabled / needed."); return; } @@ -1858,7 +1873,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; @@ -1919,7 +1934,7 @@ void DataProcessingDevice::handleData(ServiceRegistryRef ref, InputChannelInfo& input, nMessages, nPayloadsPerHeader, - nullptr, + context.forwardPolicy == ForwardPolicy::AtInjection ? forwardOnInsertion : nullptr, onDrop); switch (relayed.type) { case DataRelayer::RelayChoice::Type::Backpressured: @@ -2333,11 +2348,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(); @@ -2456,7 +2483,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); diff --git a/Framework/Core/src/DataRelayer.cxx b/Framework/Core/src/DataRelayer.cxx index d9c340cd9c225..05b64b6ed1dad 100644 --- a/Framework/Core/src/DataRelayer.cxx +++ b/Framework/Core/src/DataRelayer.cxx @@ -521,6 +521,8 @@ DataRelayer::RelayChoice continue; } 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; From 9634a2ee3fd8481f9f222c27eee03d1c459a1b13 Mon Sep 17 00:00:00 2001 From: Felix Weiglhofer Date: Tue, 20 Jan 2026 15:05:24 +0100 Subject: [PATCH 160/701] GPU: Don't override --recoSteps flags in standalone. --- .../Standalone/Benchmark/standalone.cxx | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx b/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx index 5fa9da23d7423..a2e74c45fcb86 100644 --- a/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx +++ b/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx @@ -413,6 +413,18 @@ int32_t SetupReconstruction() 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; } @@ -432,16 +444,6 @@ int32_t SetupReconstruction() } } - // 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; - } - } - rec->SetSettings(&grp, &recSet, &procSet, &steps); if (configStandalone.proc.doublePipeline) { recPipeline->SetSettings(&grp, &recSet, &procSet, &steps); From 96a6a753d2e56ec9db66ad200d693589a188f30a Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 21 Jan 2026 11:07:47 +0100 Subject: [PATCH 161/701] GPU QA: Improvements for some plots --- GPU/GPUTracking/Definitions/GPUSettingsList.h | 5 +- GPU/GPUTracking/qa/GPUQA.cxx | 62 ++++++++++--------- GPU/GPUTracking/qa/GPUQA.h | 8 +-- 3 files changed, 39 insertions(+), 36 deletions(-) diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index d70fac115eab7..9bfe6feb14d8d 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -527,9 +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") -AddOption(perfFigure, int32_t, 0, "", 0, "Show as performance figure, positive value for MC, negative value for data") +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") -AddShortcut("compare", 0, "--QAinput", "Compare QA histograms", "--qa", "--QAinputHistogramsOnly") +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() diff --git a/GPU/GPUTracking/qa/GPUQA.cxx b/GPU/GPUTracking/qa/GPUQA.cxx index 852ac5c1feefb..b58209efff744 100644 --- a/GPU/GPUTracking/qa/GPUQA.cxx +++ b/GPU/GPUTracking/qa/GPUQA.cxx @@ -413,20 +413,21 @@ void GPUQA::DrawHisto(TH1* histo, char* filename, char* options) void GPUQA::doPerfFigure(float x, float y, float size) { - const char* str_perf_figure_1 = "ALICE Performance"; - const char* str_perf_figure_2_mc = "MC, Pb#minusPb, #sqrt{s_{NN}} = 5.36 TeV"; - const char* str_perf_figure_2_data = "Pb#minusPb, #sqrt{s_{NN}} = 5.36 TeV"; - - if (mConfig.perfFigure == 0) { + if (mConfig.perfFigure == "") { return; } + 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, mConfig.perfFigure > 0 ? str_perf_figure_2_mc : str_perf_figure_2_data); + t->DrawLatex(x, y - 0.01 - size, str_perf_figure_2); } void GPUQA::SetMCTrackRange(int32_t min, int32_t max) @@ -539,7 +540,7 @@ 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); @@ -1738,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; @@ -2247,12 +2248,12 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) // Create Canvas for track statistic histos if (mQATasks & taskTrackStatistics) { - mCTracks = createGarbageCollected("ctrackspt", "ctrackspt", 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.5, 0.93 - (0.93 - 0.86) / 2. * (float)ConfigNumInputs, 0.98, 0.949); - SetLegend(mLTracks, true); + 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); @@ -2800,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); @@ -2878,7 +2879,7 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) } title += ")"; - e->SetTitle(title.c_str()); + 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(); @@ -2910,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; } @@ -2919,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; } @@ -2933,9 +2934,10 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) e->SetMinimum(tmpMax * -0.02); e->SetStats(kFALSE); e->SetLineWidth(1); - e->SetTitle("Number of Tracks vs #it{p}_{T}"); + 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); } @@ -2943,14 +2945,14 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) e->SetLineColor(colorNums[k % COLORCOUNT]); e->Draw(k == 0 ? "" : "same"); GetName(fname, k, mConfig.inputHistogramsOnly); - mLTracks->AddEntry(e, Form(mConfig.inputHistogramsOnly ? "%s" : "%sTrack #it{p}_{T}", fname), "l"); + mLTrackPt->AddEntry(e, Form(mConfig.inputHistogramsOnly ? "%s" : "%sTrack #it{p}_{T}", fname), "l"); } - mLTracks->Draw(); + mLTrackPt->Draw(); doPerfFigure(0.63, 0.7, 0.030); - mCTracks->cd(); - mCTracks->Print(Form("%s/tracks.pdf", mConfig.plotsDir.c_str())); + mCTrackPt->cd(); + mCTrackPt->Print(Form("%s/tracks.pdf", mConfig.plotsDir.c_str())); if (mConfig.writeFileExt != "") { - mCTracks->Print(Form("%s/tracks.%s", mConfig.plotsDir.c_str(), mConfig.writeFileExt.c_str())); + mCTrackPt->Print(Form("%s/tracks.%s", mConfig.plotsDir.c_str(), mConfig.writeFileExt.c_str())); } for (int32_t i = 0; i < 2; i++) { @@ -2978,7 +2980,7 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) e->SetMinimum(tmpMax * -0.02); e->SetStats(kFALSE); e->SetLineWidth(1); - e->SetTitle(i ? "Track t_{0} resolution" : "Track t_{0} distribution"); + e->SetTitle(mConfig.plotsNoTitle ? "" : (i ? "Track t_{0} resolution" : "Track t_{0} distribution")); e->GetYaxis()->SetTitle("a.u."); e->GetXaxis()->SetTitle(i ? "t_{0} - t_{0, mc}" : "t_{0}"); if (qcout) { @@ -3022,7 +3024,7 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) e->SetMinimum(tmpMax * -0.02); e->SetStats(kFALSE); e->SetLineWidth(1); - e->SetTitle(i ? "Number of Rows with attached Cluster" : "Number of Clusters"); + e->SetTitle(mConfig.plotsNoTitle ? "" : (i ? "Number of Rows with attached Cluster" : "Number of Clusters")); e->GetYaxis()->SetTitle("a.u."); e->GetXaxis()->SetTitle(i ? "N_{Rows with Clusters}" : "N_{Clusters}"); if (qcout) { @@ -3061,7 +3063,7 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) mClRej[i]->Write(); } mPClRej[i]->cd(); - mClRej[i]->SetTitle(REJECTED_NAMES[i]); + mClRej[i]->SetTitle(mConfig.plotsNoTitle ? "" : REJECTED_NAMES[i]); mClRej[i]->SetOption("colz"); mClRej[i]->Draw(); mCClRej[i]->cd(); @@ -3098,7 +3100,7 @@ int32_t GPUQA::DrawQAHistograms(TObjArray* qcout) delete proj2; e->SetMinimum(-0.02); e->SetMaximum(0.22); - e->SetTitle("Rejected Clusters"); + e->SetTitle(mConfig.plotsNoTitle ? "" : "Rejected Clusters"); e->GetXaxis()->SetTitle("Pad Row"); e->GetYaxis()->SetTitle("Rejected Clusters (fraction)"); e->Draw(k == 0 ? "" : "same"); diff --git a/GPU/GPUTracking/qa/GPUQA.h b/GPU/GPUTracking/qa/GPUQA.h index 346c56a898806..bd3c9be3a9aa5 100644 --- a/GPU/GPUTracking/qa/GPUQA.h +++ b/GPU/GPUTracking/qa/GPUQA.h @@ -290,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]; From a4989384162de86181f18515a2431fab19182ed8 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Tue, 20 Jan 2026 10:32:03 +0100 Subject: [PATCH 162/701] DPL: avoid needless copy of messages when cleaning up early forwarding --- .../include/Framework/DataProcessingHelpers.h | 2 + Framework/Core/src/DataProcessingDevice.cxx | 7 ++- Framework/Core/src/DataProcessingHelpers.cxx | 54 +++++++++++++++++++ 3 files changed, 61 insertions(+), 2 deletions(-) diff --git a/Framework/Core/include/Framework/DataProcessingHelpers.h b/Framework/Core/include/Framework/DataProcessingHelpers.h index a9bd95b69f4c7..87aeeb8922da3 100644 --- a/Framework/Core/include/Framework/DataProcessingHelpers.h +++ b/Framework/Core/include/Framework/DataProcessingHelpers.h @@ -59,6 +59,8 @@ struct DataProcessingHelpers { /// 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/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index f65477c573772..38c57c66c8a01 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -626,9 +626,12 @@ static auto cleanEarlyForward = [](ServiceRegistryRef registry, TimesliceSlot sl 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. - auto forwardedParts = DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, true, consume); + 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", "Forwarding done"); + O2_SIGNPOST_END(forwarding, sid, "forwardInputs", "Cleaning done"); }; extern volatile int region_read_global_dummy_variable; diff --git a/Framework/Core/src/DataProcessingHelpers.cxx b/Framework/Core/src/DataProcessingHelpers.cxx index 87e7c9bf8962f..334a0fc6045f6 100644 --- a/Framework/Core/src/DataProcessingHelpers.cxx +++ b/Framework/Core/src/DataProcessingHelpers.cxx @@ -338,6 +338,60 @@ void DataProcessingHelpers::routeForwardedMessages(FairMQDeviceProxy& proxy, std } } +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 From 8562189e36996069bd0de7a79c57454ebb3fb8fe Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 21 Jan 2026 16:55:11 +0100 Subject: [PATCH 163/701] DPL: make new early forward optional The new behavior breaks the TPC custom policy. --- Framework/Core/src/DataProcessingDevice.cxx | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index 38c57c66c8a01..fb54af9402079 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -1054,7 +1054,11 @@ void DataProcessingDevice::fillContext(DataProcessorContext& context, DeviceCont } auto decideEarlyForward = [&context, &deviceContext, &spec, this]() -> ForwardPolicy { - ForwardPolicy defaultEarlyForwardPolicy = getenv("DPL_OLD_EARLY_FORWARD") ? ForwardPolicy::AtCompletionPolicySatisified : ForwardPolicy::AtInjection; + //ForwardPolicy defaultEarlyForwardPolicy = getenv("DPL_OLD_EARLY_FORWARD") ? ForwardPolicy::AtCompletionPolicySatisified : ForwardPolicy::AtInjection; + // 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; /// We must make sure there is no optional /// if we want to optimize the forwarding From be1d553177b4c8edc9ad47863369f5390f1fefaa Mon Sep 17 00:00:00 2001 From: shahoian Date: Wed, 21 Jan 2026 18:06:42 +0100 Subject: [PATCH 164/701] Store missing GlobalTrackID in the CheckResid output --- Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx b/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx index 691d731503b88..d665af5747c60 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx @@ -252,6 +252,7 @@ void CheckResidSpec::process() auto& accum = slots[0]; #endif auto& resTrack = accum.emplace_back(); + resTrack.gid = vid; if (!processITSTrack(itsTrack, pve, resTrack)) { accum.pop_back(); continue; From 587eb9487ae816790b3c4f7fe7db3fd22e9a8d85 Mon Sep 17 00:00:00 2001 From: Ernst Hellbar Date: Wed, 21 Jan 2026 09:48:37 +0100 Subject: [PATCH 165/701] TRD: add missing OutputSpec in trd-pulseheight device --- Detectors/TRD/workflow/include/TRDWorkflow/TRDPulseHeightSpec.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Detectors/TRD/workflow/include/TRDWorkflow/TRDPulseHeightSpec.h b/Detectors/TRD/workflow/include/TRDWorkflow/TRDPulseHeightSpec.h index 3cfbb16644e54..be00e478608ec 100644 --- a/Detectors/TRD/workflow/include/TRDWorkflow/TRDPulseHeightSpec.h +++ b/Detectors/TRD/workflow/include/TRDWorkflow/TRDPulseHeightSpec.h @@ -69,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; @@ -103,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)) { From 397e0194d0ab4d02f61c75ca40eb024703208478 Mon Sep 17 00:00:00 2001 From: Barthelemy Date: Wed, 21 Jan 2026 16:55:50 +0100 Subject: [PATCH 166/701] [O2-6625] Fix the missing filename in the CCDb --- CCDB/src/CcdbApi.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CCDB/src/CcdbApi.cxx b/CCDB/src/CcdbApi.cxx index f083d97b533df..42bc13904bf61 100644 --- a/CCDB/src/CcdbApi.cxx +++ b/CCDB/src/CcdbApi.cxx @@ -416,7 +416,7 @@ 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"); - if (filename.empty()) { + if (!filename.empty()) { curl_mime_filedata(field, filename.c_str()); } if (buffer != nullptr && size > 0) { From 078eb5d8b2ed24527f7488e5b8fbae866820a46d Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 22 Jan 2026 15:58:36 +0100 Subject: [PATCH 167/701] Revert "DPL Analysis: Use dangling edges context in more places (#14953)" This reverts commit c990996954857d60b683fadc3bc037f055276c5d. --- .../AnalysisSupport/src/AODWriterHelpers.cxx | 21 +++---- .../CCDBSupport/src/AnalysisCCDBHelpers.cxx | 59 +++++++++---------- .../CCDBSupport/src/AnalysisCCDBHelpers.h | 2 +- .../Core/include/Framework/AnalysisTask.h | 4 +- Framework/Core/src/AnalysisSupportHelpers.cxx | 4 +- 5 files changed, 45 insertions(+), 45 deletions(-) diff --git a/Framework/AnalysisSupport/src/AODWriterHelpers.cxx b/Framework/AnalysisSupport/src/AODWriterHelpers.cxx index b76ffca13977e..d868b7498fb76 100644 --- a/Framework/AnalysisSupport/src/AODWriterHelpers.cxx +++ b/Framework/AnalysisSupport/src/AODWriterHelpers.cxx @@ -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, compressionLevel](InitContext& ic) -> std::function { - auto outputInputs = ic.services().get().outputsInputsAOD; + return AlgorithmSpec{[dod, outputInputs = ac.outputsInputsAOD, compressionLevel](InitContext& ic) -> std::function { LOGP(debug, "======== getGlobalAODSink::Init =========="); // find out if any table needs to be saved @@ -241,13 +241,14 @@ AlgorithmSpec AODWriterHelpers::getOutputTTreeWriter(ConfigContext const& ctx) }; } -AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& /*ctx*/) +AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& ctx) { - return AlgorithmSpec{[](InitContext& ic) -> std::function { - using namespace monitoring; - auto& dec = ic.services().get(); - auto tskmap = dec.outTskMap; - auto objmap = dec.outObjHistMap; + using namespace monitoring; + auto& ac = ctx.services().get(); + auto tskmap = ac.outTskMap; + auto objmap = ac.outObjHistMap; + + return AlgorithmSpec{[objmap, tskmap](InitContext& ic) -> std::function { auto& callbacks = ic.services().get(); auto inputObjects = std::make_shared>>(); @@ -277,7 +278,7 @@ AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& /*ct callbacks.set(endofdatacb); return [inputObjects, objmap, tskmap](ProcessingContext& pc) mutable -> void { - auto mergePart = [&inputObjects, &objmap, &tskmap](DataRef const& ref) { + auto mergePart = [&inputObjects, &objmap, &tskmap, &pc](DataRef const& ref) { O2_SIGNPOST_ID_GENERATE(hid, histogram_registry); O2_SIGNPOST_START(histogram_registry, hid, "mergePart", "Merging histogram"); if (!ref.header) { @@ -473,7 +474,7 @@ AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& /*ct }; O2_SIGNPOST_ID_GENERATE(rid, histogram_registry); O2_SIGNPOST_START(histogram_registry, rid, "processParts", "Start merging %zu parts received together.", pc.inputs().getNofParts(0)); - for (auto pi = 0U; pi < pc.inputs().getNofParts(0); ++pi) { + for (int pi = 0; 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/CCDBSupport/src/AnalysisCCDBHelpers.cxx b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx index 413adfddecf04..9ec911518f754 100644 --- a/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx +++ b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx @@ -67,39 +67,38 @@ void fillValidRoutes(CCDBFetcherHelper& helper, std::vector(); - std::vector> schemas; - auto schemaMetadata = std::make_shared(); + auto& ac = ctx.services().get(); + std::vector> schemas; + auto schemaMetadata = std::make_shared(); - for (auto& input : dec.analysisCCDBInputs) { - std::vector> fields; - schemaMetadata->Append("outputRoute", DataSpecUtils::describe(input)); - schemaMetadata->Append("outputBinding", input.binding); + for (auto& input : ac.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); - schemaMetadata->Append("sourceMatcher", DataSpecUtils::describe(std::get(DataSpecUtils::fromMetadataString(m.defaultValue.get()).matcher))); - 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)); + 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); + schemaMetadata->Append("sourceMatcher", DataSpecUtils::describe(std::get(DataSpecUtils::fromMetadataString(m.defaultValue.get()).matcher))); + continue; + } + // Ignore the non ccdb: entries + if (!m.name.starts_with("ccdb:")) { + continue; } - schemas.emplace_back(std::make_shared(fields, schemaMetadata)); + // 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)); + } + 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; @@ -130,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 const& _ : schema->fields()) { + for (auto& _ : schema->fields()) { builders.emplace_back(std::make_shared()); } - for (auto ci = 0; ci < timestampColumn->num_chunks(); ++ci) { + for (size_t 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 3be2138bd2b5c..f8175034da0ba 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&); + static AlgorithmSpec fetchFromCCDB(ConfigContext const& ctx); }; } // namespace o2::framework diff --git a/Framework/Core/include/Framework/AnalysisTask.h b/Framework/Core/include/Framework/AnalysisTask.h index 4f8a9e719e4b9..c50b5358990de 100644 --- a/Framework/Core/include/Framework/AnalysisTask.h +++ b/Framework/Core/include/Framework/AnalysisTask.h @@ -521,7 +521,7 @@ DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args) std::vector expressionInfos; /// make sure options and configurables are set before expression infos are created - homogeneous_apply_refs([&options](auto& element) { return analysis_task_parsers::appendOption(options, element); }, *task.get()); + homogeneous_apply_refs([&options, &hash](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()); @@ -620,7 +620,7 @@ DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args) } // reset pre-slice for the next dataframe auto slices = pc.services().get(); - homogeneous_apply_refs([&slices](auto& element) { + homogeneous_apply_refs([&pc, &slices](auto& element) { return analysis_task_parsers::updateSliceInfo(element, slices); }, *(task.get())); diff --git a/Framework/Core/src/AnalysisSupportHelpers.cxx b/Framework/Core/src/AnalysisSupportHelpers.cxx index 7edf9a2d8d27f..15b56f9afbff5 100644 --- a/Framework/Core/src/AnalysisSupportHelpers.cxx +++ b/Framework/Core/src/AnalysisSupportHelpers.cxx @@ -98,7 +98,7 @@ std::shared_ptr AnalysisSupportHelpers::getDataOutputDirecto if (!keepString.empty()) { dod->reset(); std::string d("dangling"); - if (d.starts_with(keepString) == 0) { + if (d.find(keepString) == 0) { // use the dangling outputs std::vector danglingOutputs; for (auto ii = 0u; ii < OutputsInputs.size(); ii++) { @@ -144,7 +144,7 @@ void AnalysisSupportHelpers::addMissingOutputsToSpawner(std::vector sinks::append_to{publisher.outputs}; // append them to the publisher outputs std::vector additionalInputs; - for (auto const& input : requestedSpecials | views::filter_not_matching(providedSpecials)) { + for (auto& input : requestedSpecials | views::filter_not_matching(providedSpecials)) { input.metadata | views::filter_string_params_with("input:") | views::params_to_input_specs() | From 538f355832f7b5e353704b09c123dd603521e7df Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 22 Jan 2026 10:09:02 +0100 Subject: [PATCH 168/701] DPL: do not do the new early forwarding for some of the data --- Framework/Core/src/DataProcessingDevice.cxx | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index fb54af9402079..3eaab36fb7908 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -1054,11 +1054,22 @@ void DataProcessingDevice::fillContext(DataProcessorContext& context, DeviceCont } auto decideEarlyForward = [&context, &deviceContext, &spec, this]() -> ForwardPolicy { - //ForwardPolicy defaultEarlyForwardPolicy = getenv("DPL_OLD_EARLY_FORWARD") ? ForwardPolicy::AtCompletionPolicySatisified : ForwardPolicy::AtInjection; - // 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; + 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; + } + } /// We must make sure there is no optional /// if we want to optimize the forwarding From 5376bb861a3a935a2a8211917d6d2e52e51c535e Mon Sep 17 00:00:00 2001 From: shahoian Date: Fri, 23 Jan 2026 17:01:44 +0100 Subject: [PATCH 169/701] PVertexer::refitVertexFull for refitting with different geom. In in case the residuals monitoring is running with geometry different from the one used for initial reconstruction pass a --configKeyValues option for the vertex refit as: ;pvertexer.useMeanVertexConstraint=false;pvertexer.iniScale2=100;pvertexer.acceptableScale2=10.; It will be used by the PVertexer::refitVertexFull. --- .../GlobalTrackingStudy/CheckResidConfig.h | 5 ++- .../study/src/CheckResid.cxx | 40 ++++++++++++------- .../include/DetectorsVertexing/PVertexer.h | 4 +- Detectors/Vertexing/src/PVertexer.cxx | 31 ++++++++++++++ 4 files changed, 62 insertions(+), 18 deletions(-) diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidConfig.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidConfig.h index 53dffeed7ad69..2a07eaf87930f 100644 --- a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidConfig.h +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/CheckResidConfig.h @@ -26,11 +26,14 @@ struct CheckResidConfig : o2::conf::ConfigurableParamHelper { bool pvcontribOnly = true; bool addPVAsCluster = true; - bool refitPV = 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 diff --git a/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx b/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx index d665af5747c60..e6584a7055446 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx @@ -38,14 +38,17 @@ #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; @@ -83,6 +86,7 @@ class CheckResidSpec : public Task 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 @@ -131,6 +135,7 @@ void CheckResidSpec::updateTimeDependentParams(ProcessingContext& 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(); @@ -142,9 +147,13 @@ void CheckResidSpec::updateTimeDependentParams(ProcessingContext& pc) } 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.useMeanVertexConstraint=false"); + o2::conf::ConfigurableParam::updateFromString("pvertexer.useTimeInChi2=false;"); mVertexer.init(); } + if (mMeanVertexUpdated) { + mMeanVertexUpdated = false; + mVertexer.initMeanVertexConstraint(); + } bool updateMaps = false; /* if (mTPCCorrMapsLoader.isUpdated()) { @@ -200,6 +209,7 @@ void CheckResidSpec::process() } 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; @@ -315,6 +325,7 @@ bool CheckResidSpec::processITSTrack(const o2::its::TrackITS& iTrack, const o2:: resTrack.points.clear(); if (!prop->propagateToDCA(pv, trFitOut, bz)) { + LOGP(debug, "Failed to propagateToDCA, {}", trFitOut.asString()); return false; } float cosAlp, sinAlp; @@ -418,7 +429,7 @@ bool CheckResidSpec::refitPV(o2::dataformats::PrimaryVertex& pv, int vid) std::vector tracks; std::vector useTrack; std::vector gidsITS; - int ntr = pv.getNContributors(); + int ntr = pv.getNContributors(), ntrIni = ntr; tracks.reserve(ntr); useTrack.reserve(ntr); gidsITS.reserve(ntr); @@ -447,20 +458,18 @@ bool CheckResidSpec::refitPV(o2::dataformats::PrimaryVertex& pv, int vid) ntr++; } if (ntr < params.minPVContributors || !mVertexer.prepareVertexRefit(tracks, pv)) { + LOGP(warn, "Abandon vertex refit: NcontribNew = {} vs NcontribOld = {}", ntr, ntrIni); return false; } - // readjust vertexZ - const auto& pool = mVertexer.getTracksPool(); - float zUpd = 0; - for (const auto& t : pool) { - zUpd += t.z; - } - if (pool.size()) { - pv.setZ(zUpd / pool.size()); - mVertexer.prepareVertexRefit(tracks, pv); + 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; } - pv = mVertexer.refitVertex(useTrack, pv); - return pv.getChi2() > 0.f; + return true; } bool CheckResidSpec::refitITStrack(o2::track::TrackParCov& track, GTrackID gid) @@ -515,6 +524,7 @@ void CheckResidSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) 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)) { 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 { From 5b572ed12a5b3ca085a18d9ece546a84a98c3c30 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Fri, 11 Jul 2025 14:01:38 +0200 Subject: [PATCH 170/701] ITSMFT: staggered digitization Signed-off-by: Felix Schlepper --- .../Detectors/ITSMFT/common/src/ROFRecord.cxx | 14 +- .../ITS/base/include/ITSBase/GeometryTGeo.h | 8 +- .../MFT/base/include/MFTBase/GeometryTGeo.h | 6 +- .../base/include/ITSMFTBase/DPLAlpideParam.h | 41 +- .../base/include/ITSMFTBase/GeometryTGeo.h | 5 +- .../include/ITSMFTSimulation/DigiParams.h | 45 +- .../include/ITSMFTSimulation/Digitizer.h | 24 +- .../common/simulation/src/DigiParams.cxx | 44 +- .../common/simulation/src/Digitizer.cxx | 98 ++--- .../include/ITSMFTWorkflow/DigitReaderSpec.h | 54 +-- .../common/workflow/src/DigitReaderSpec.cxx | 223 +++++----- .../common/workflow/src/DigitWriterSpec.cxx | 84 +++- .../src/ITSMFTDigitizerSpec.cxx | 413 ++++++++++-------- 13 files changed, 614 insertions(+), 445 deletions(-) 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/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h b/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h index 934c927ac3059..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 @@ -336,7 +336,7 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo 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 @@ -407,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/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/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 +#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 670dd32bf9f46..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,7 +58,7 @@ 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; } @@ -67,17 +69,17 @@ class Digitizer : public TObject 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; } @@ -92,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) { @@ -115,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 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 4a8af0cbe9737..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; @@ -73,14 +75,14 @@ void Digitizer::init() } else { LOG(fatal) << "Invalid ITS Inner Barrel back-bias value"; } - if (doptITS.OBVbb == 0.0) { // for ITS Outter Barrel + 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]; 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(); @@ -98,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(debug) << "Digitizing " << mGeometry->getName() << " hits of entry " << evID << " from source " + 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; @@ -161,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(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; } @@ -183,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; @@ -204,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); @@ -236,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? } @@ -251,7 +257,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::itsmft::Hit& hit, uint32_t& maxFr, int evID, int srcID, int lay) { // convert single hit to digits auto chipID = hit.GetDetectorID(); @@ -284,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(); @@ -332,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; } @@ -362,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 @@ -379,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); @@ -389,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; } @@ -426,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/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/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/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx index b40e377d58ca2..6809c8dee3f19 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,174 @@ 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 + + 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]) { - // digits are directly put into DPL owned resource - auto& digitsAccum = pc.outputs().make>(Output{mOrigin, "DIGITS", 0}); + // 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); - 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 + 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 + std::vector expDigitRofVec(nROFsTF); + for (int iROF{0}; iROF < nROFsTF; ++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 ROF:{} and received ROF:{}", expROF.asString(), rof.asString()); + } + } + int prevFirst{0}; + for (auto& rof : expDigitRofVec) { + if (rof.getFirstEntry() < 0) { + rof.setFirstEntry(prevFirst); } + prevFirst = rof.getFirstEntry(); } + 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; + 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(); } - 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 - } - } - 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 +243,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,30 +263,25 @@ 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(mOrigin, "ALPIDERESPVbb0", 0)) { - LOG(info) << mID.getName() << " loaded AlpideResponseData for Vbb=0V"; + 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(mOrigin, "ALPIDERESPVbbM3", 0)) { - LOG(info) << mID.getName() << " loaded AlpideResponseData for Vbb=-3V"; + if (matcher == ConcreteDataMatcher(Origin, "ALPIDERESPVbbM3", 0)) { + LOG(info) << ID.getName() << " loaded AlpideResponseData for Vbb=-3V"; mDigitizer.setAlpideResponse((o2::itsmft::AlpideSimResponse*)obj, 1); } } @@ -235,20 +289,19 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer 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); @@ -272,15 +325,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(); @@ -294,81 +361,61 @@ 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")); @@ -377,19 +424,18 @@ DataProcessorSpec getITSDigitizerSpec(int channel, bool mctruth) inputs.emplace_back("ITS_alppar", "ITS", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/AlpideParam")); 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{(detStr + "Digitizer").c_str(), - inputs, makeOutChannels(detOrig, mctruth), - AlgorithmSpec{adaptFromTask(mctruth)}, - Options{ + 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")); @@ -398,15 +444,12 @@ DataProcessorSpec getMFTDigitizerSpec(int channel, bool mctruth) inputs.emplace_back("MFT_alppar", "MFT", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("MFT/Config/AlpideParam")); 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")); - 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"}}}}; + 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 From b18b96ab60f829b2cb853b6a4553a47167948165 Mon Sep 17 00:00:00 2001 From: Roman Lietava Date: Sun, 25 Jan 2026 10:38:46 +0100 Subject: [PATCH 171/701] Ctpdev: task for populating BK with ctp config/scalers (#14993) * dev: code for repopulating BK with old configs/scalers * clang * fixes * fixes * clang * fix --- Detectors/CTP/workflowScalers/CMakeLists.txt | 8 + .../CTPWorkflowScalers/ctpCCDBManager.h | 5 +- .../CTP/workflowScalers/src/ctp-bk-write.cxx | 170 ++++++++++++++++++ .../workflowScalers/src/ctpCCDBManager.cxx | 27 +++ 4 files changed, 208 insertions(+), 2 deletions(-) create mode 100644 Detectors/CTP/workflowScalers/src/ctp-bk-write.cxx 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 From b33261326dc4203ba6e537df64e04b4bdb3716d5 Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Fri, 23 Jan 2026 14:55:22 +0100 Subject: [PATCH 172/701] Implement AO2D file checks for full_system_test Performed during ASYNC stage, where the AO2D is created --- prodtests/full_system_test.sh | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) diff --git a/prodtests/full_system_test.sh b/prodtests/full_system_test.sh index bf235a500cd8b..6408588d46e68 100755 --- a/prodtests/full_system_test.sh +++ b/prodtests/full_system_test.sh @@ -295,6 +295,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} From 1fba29618f1f469fb918c725234f1d9f45dfc183 Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Mon, 26 Jan 2026 10:23:20 +0100 Subject: [PATCH 173/701] DPL Analysis: modernize and cleanup some code (#14975) --- .../AnalysisSupport/src/AODReaderHelpers.cxx | 55 ++++------- Framework/Core/include/Framework/ASoA.h | 1 + .../Core/include/Framework/Expressions.h | 2 +- Framework/Core/src/ASoA.cxx | 96 +++++++++---------- Framework/Core/src/AnalysisHelpers.cxx | 37 +++---- Framework/Core/src/ArrowSupport.cxx | 26 ++--- Framework/Core/src/ArrowTableSlicingCache.cxx | 6 +- Framework/Core/src/Expressions.cxx | 4 +- Framework/Core/src/WorkflowHelpers.cxx | 74 +++++++------- 9 files changed, 129 insertions(+), 172 deletions(-) diff --git a/Framework/AnalysisSupport/src/AODReaderHelpers.cxx b/Framework/AnalysisSupport/src/AODReaderHelpers.cxx index 485f3fa69edad..4c1c065000186 100644 --- a/Framework/AnalysisSupport/src/AODReaderHelpers.cxx +++ b/Framework/AnalysisSupport/src/AODReaderHelpers.cxx @@ -37,7 +37,7 @@ struct Buildable { std::vector records; std::shared_ptr outputSchema; - Buildable(InputSpec const& spec) + explicit Buildable(InputSpec const& spec) : binding{spec.binding} { auto&& [origin_, description_, version_] = DataSpecUtils::asConcreteDataMatcher(spec); @@ -58,9 +58,8 @@ struct Buildable { } outputSchema = std::make_shared([](std::vector const& recs) { std::vector> fields; - for (auto& r : recs) { - fields.push_back(r.field()); - } + 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}})); @@ -87,19 +86,12 @@ AlgorithmSpec AODReaderHelpers::indexBuilderCallback(ConfigContext const& /*ctx* { return AlgorithmSpec::InitCallback{[](InitContext& ic) { auto const& requested = ic.services().get().requestedIDXs; - std::vector buildables; - for (auto const& i : requested) { - buildables.emplace_back(i); - } std::vector builders; - for (auto& b : buildables) { - builders.push_back(b.createBuilder()); - } + 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(); - for (auto& builder : builders) { - outputs.adopt(Output{builder.origin, builder.description, builder.version}, builder.materialize(pc)); - } + std::ranges::for_each(builders, [&pc, &outputs](auto& builder) { outputs.adopt(Output{builder.origin, builder.description, builder.version}, builder.materialize(pc)); }); }; }}; } @@ -119,7 +111,7 @@ struct Spawnable { header::DataDescription description; header::DataHeader::SubSpecificationType version; - Spawnable(InputSpec const& spec) + explicit Spawnable(InputSpec const& spec) : binding{spec.binding} { auto&& [origin_, description_, version_] = DataSpecUtils::asConcreteDataMatcher(spec); @@ -144,16 +136,19 @@ struct Spawnable { iws.str(json); schemas.emplace_back(ArrowJSONHelpers::read(iws)); } - for (auto const& i : spec.metadata | views::filter_string_params_starts_with("input:") | std::ranges::views::transform([](auto const& param) { - return DataSpecUtils::fromMetadataString(param.defaultValue.template get()); - })) { - matchers.emplace_back(std::get(i.matcher)); - } + 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; - for (auto& s : schemas) { - std::copy(s->fields().begin(), s->fields().end(), std::back_inserter(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()); @@ -194,20 +189,12 @@ AlgorithmSpec AODReaderHelpers::aodSpawnerCallback(ConfigContext const& /*ctx*/) { return AlgorithmSpec::InitCallback{[](InitContext& ic) { auto const& requested = ic.services().get().spawnerInputs; - std::vector spawnables; - for (auto const& i : requested) { - spawnables.emplace_back(i); - } std::vector spawners; - for (auto& s : spawnables) { - spawners.push_back(s.createMaker()); - } - + 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(); - for (auto& spawner : spawners) { - outputs.adopt(Output{spawner.origin, spawner.description, spawner.version}, spawner.materialize(pc)); - } + std::ranges::for_each(spawners, [&pc, &outputs](auto& spawner) { outputs.adopt(Output{spawner.origin, spawner.description, spawner.version}, spawner.materialize(pc)); }); }; }}; } diff --git a/Framework/Core/include/Framework/ASoA.h b/Framework/Core/include/Framework/ASoA.h index ec02c7e47132b..4fd35e0dc5065 100644 --- a/Framework/Core/include/Framework/ASoA.h +++ b/Framework/Core/include/Framework/ASoA.h @@ -1283,6 +1283,7 @@ 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); diff --git a/Framework/Core/include/Framework/Expressions.h b/Framework/Core/include/Framework/Expressions.h index 0be19954f1faa..c5f50311a7d19 100644 --- a/Framework/Core/include/Framework/Expressions.h +++ b/Framework/Core/include/Framework/Expressions.h @@ -712,7 +712,7 @@ 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> outputFields); +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/src/ASoA.cxx b/Framework/Core/src/ASoA.cxx index 6a846c3d45b6c..1c73b257f81e4 100644 --- a/Framework/Core/src/ASoA.cxx +++ b/Framework/Core/src/ASoA.cxx @@ -62,71 +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) { - 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()); - } - } std::vector> fields; std::vector> columns; - - for (auto& t : tables) { - auto tf = t->fields(); - std::copy(tf.begin(), tf.end(), std::back_inserter(fields)); - } - - 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)); + 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); } -std::shared_ptr ArrowHelpers::joinTables(std::vector>&& tables, std::span labels) +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].c_str(), labels[i + 1].c_str(), 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) @@ -135,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. @@ -165,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 f2ecb2d68ce28..b7eac692d3859 100644 --- a/Framework/Core/src/AnalysisHelpers.cxx +++ b/Framework/Core/src/AnalysisHelpers.cxx @@ -46,14 +46,12 @@ void IndexBuilder::resetBuilders(std::vector& bui 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 (builders.empty()) { + if (O2_BUILTIN_UNLIKELY(builders.empty())) { builders = makeBuilders(std::move(tables), records); } else { resetBuilders(builders, std::move(tables)); } - std::vector finds; - finds.resize(builders.size()); for (int64_t counter = 0; counter < size; ++counter) { int64_t idx = -1; if (std::get(builders[0].builder).keyIndex == nullptr) { @@ -61,29 +59,19 @@ std::shared_ptr IndexBuilder::materialize(std::vector(builders[0].builder).keyIndex->valueAt(counter); } - for (auto i = 0U; i < builders.size(); ++i) { - finds[i] = builders[i].find(idx); - } - if (exclusive) { - if (std::none_of(finds.begin(), finds.end(), [](bool const x) { return x == false; })) { - builders[0].fill(counter); - for (auto i = 1U; i < builders.size(); ++i) { - builders[i].fill(idx); - } - } - } else { + + bool found = true; + std::ranges::for_each(builders, [&idx, &found](auto& builder) { found &= builder.find(idx); }); + + if (!exclusive || found) { builders[0].fill(counter); - for (auto i = 1U; i < builders.size(); ++i) { - builders[i].fill(idx); - } + std::ranges::for_each(builders.begin() + 1, builders.end(), [&idx](auto& builder) { builder.fill(idx); }); } } std::vector> arrays; arrays.reserve(builders.size()); - for (auto& builder : builders) { - arrays.push_back(builder.result()); - } + std::ranges::transform(builders, std::back_inserter(arrays), [](auto& builder) { return builder.result(); }); return arrow::Table::Make(schema, arrays); } @@ -142,9 +130,7 @@ std::shared_ptr spawnerHelper(std::shared_ptr const& } arrays.reserve(nColumns); - for (auto i = 0U; i < nColumns; ++i) { - arrays.push_back(std::make_shared(chunks[i])); - } + std::ranges::transform(chunks, std::back_inserter(arrays), [](auto&& chunk) { return std::make_shared(chunk); }); return arrow::Table::Make(newSchema, arrays); } @@ -188,9 +174,8 @@ std::string serializeIndexRecords(std::vector& irs) std::vector> extractSources(ProcessingContext& pc, std::vector const& matchers) { std::vector> tables; - for (auto const& matcher : matchers) { - tables.emplace_back(pc.inputs().get(matcher)->asArrowTable()); - } + tables.reserve(matchers.size()); + std::ranges::transform(matchers, std::back_inserter(tables), [&pc](auto const& matcher) { return pc.inputs().get(matcher)->asArrowTable(); }); return tables; } diff --git a/Framework/Core/src/ArrowSupport.cxx b/Framework/Core/src/ArrowSupport.cxx index 95e763343671a..60277dfc38a74 100644 --- a/Framework/Core/src/ArrowSupport.cxx +++ b/Framework/Core/src/ArrowSupport.cxx @@ -531,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.", @@ -584,11 +578,11 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() } }, .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 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 reader = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { return spec.name.starts_with("internal-dpl-aod-reader"); }); + 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(); @@ -626,8 +620,8 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() views::partial_match_filter(header::DataOrigin{"DYN"}) | sinks::append_to{dec.providedDYNs}; } - std::sort(dec.requestedDYNs.begin(), dec.requestedDYNs.end(), inputSpecLessThan); - std::sort(dec.providedDYNs.begin(), dec.providedDYNs.end(), outputSpecLessThan); + std::ranges::sort(dec.requestedDYNs, inputSpecLessThan); + std::ranges::sort(dec.providedDYNs, outputSpecLessThan); dec.spawnerInputs.clear(); dec.requestedDYNs | views::filter_not_matching(dec.providedDYNs) | @@ -646,8 +640,8 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() 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(dec.requestedTIMs.begin(), dec.requestedTIMs.end(), inputSpecLessThan); - std::sort(dec.providedTIMs.begin(), dec.providedTIMs.end(), outputSpecLessThan); + std::ranges::sort(dec.requestedTIMs, inputSpecLessThan); + std::ranges::sort(dec.providedTIMs, outputSpecLessThan); // Use ranges::to> in C++23... dec.analysisCCDBInputs.clear(); dec.requestedTIMs | views::filter_not_matching(dec.providedTIMs) | sinks::append_to{dec.analysisCCDBInputs}; diff --git a/Framework/Core/src/ArrowTableSlicingCache.cxx b/Framework/Core/src/ArrowTableSlicingCache.cxx index 634c51f71f5a6..5162c698a1d66 100644 --- a/Framework/Core/src/ArrowTableSlicingCache.cxx +++ b/Framework/Core/src/ArrowTableSlicingCache.cxx @@ -210,7 +210,7 @@ std::pair ArrowTableSlicingCache::getCachePos(const Entry& bindingKey int ArrowTableSlicingCache::getCachePosSortedFor(Entry const& bindingKey) const { - auto locate = std::find(bindingsKeys.begin(), bindingsKeys.end(), bindingKey); + auto locate = std::ranges::find(bindingsKeys, bindingKey); if (locate != bindingsKeys.end()) { return std::distance(bindingsKeys.begin(), locate); } @@ -219,7 +219,7 @@ int ArrowTableSlicingCache::getCachePosSortedFor(Entry const& bindingKey) const int ArrowTableSlicingCache::getCachePosUnsortedFor(Entry const& bindingKey) const { - auto locate_unsorted = std::find(bindingsKeysUnsorted.begin(), bindingsKeysUnsorted.end(), bindingKey); + auto locate_unsorted = std::ranges::find(bindingsKeysUnsorted, bindingKey); if (locate_unsorted != bindingsKeysUnsorted.end()) { return std::distance(bindingsKeysUnsorted.begin(), locate_unsorted); } @@ -275,7 +275,7 @@ void ArrowTableSlicingCache::validateOrder(Entry const& bindingKey, const std::s } 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/Expressions.cxx b/Framework/Core/src/Expressions.cxx index 43143f781ddf4..02a862d30032b 100644 --- a/Framework/Core/src/Expressions.cxx +++ b/Framework/Core/src/Expressions.cxx @@ -1348,11 +1348,11 @@ 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> outputFields) +std::vector> materializeProjectors(std::vector const& projectors, std::shared_ptr const& inputSchema, std::vector> const& outputFields) { std::vector> expressions; int i = 0; - for (auto& p : projectors) { + for (auto const& p : projectors) { expressions.push_back( expressions::makeExpression( expressions::createExpressionTree( diff --git a/Framework/Core/src/WorkflowHelpers.cxx b/Framework/Core/src/WorkflowHelpers.cxx index fd9099e1aa24e..ff1ff1f4cf13d 100644 --- a/Framework/Core/src/WorkflowHelpers.cxx +++ b/Framework/Core/src/WorkflowHelpers.cxx @@ -100,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 { @@ -112,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}); @@ -240,7 +240,7 @@ 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()); + uint32_t hash = runtime_hash(name.c_str()); dec.outTskMap.push_back({hash, name}); std::string prefix = "internal-dpl-"; @@ -252,8 +252,8 @@ 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 = std::any_of(processor.inputs.begin(), processor.inputs.end(), [](auto const& input) { return input.lifetime == Lifetime::Timeframe; }); - bool hasTimeframeOutputs = std::any_of(processor.outputs.begin(), processor.outputs.end(), [](auto const& output) { return output.lifetime == Lifetime::Timeframe; }); + 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. @@ -261,10 +261,9 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext 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)}; - auto summaryOutput = std::find_if(processor.outputs.begin(), processor.outputs.end(), [&summaryMatcher](auto const& output) { return DataSpecUtils::match(output, summaryMatcher); }); + 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()); @@ -283,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"}}); } @@ -299,7 +298,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext } break; case Lifetime::Condition: { requestedCCDBs.emplace_back(input); - if ((hasConditionOption == false) && std::none_of(processor.options.begin(), processor.options.end(), [](auto const& option) { return (option.name.compare("condition-backend") == 0); })) { + 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; @@ -307,7 +306,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext } 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"}}); } @@ -333,7 +332,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext } } - 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)) { @@ -344,7 +343,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext dec.providedTIMs.emplace_back(output); } else if (DataSpecUtils::partialMatch(output, header::DataOrigin{"ATSK"})) { dec.providedOutputObjHist.emplace_back(output); - auto it = std::find_if(dec.outObjHistMap.begin(), dec.outObjHistMap.end(), [&](auto&& x) { return x.id == hash; }); + 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 { @@ -359,10 +358,10 @@ 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(dec.requestedDYNs.begin(), dec.requestedDYNs.end(), inputSpecLessThan); - std::sort(dec.requestedTIMs.begin(), dec.requestedTIMs.end(), inputSpecLessThan); - std::sort(dec.providedDYNs.begin(), dec.providedDYNs.end(), outputSpecLessThan); - std::sort(dec.providedTIMs.begin(), dec.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", @@ -389,8 +388,8 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext AnalysisSupportHelpers::addMissingOutputsToSpawner({}, dec.spawnerInputs, dec.requestedAODs, aodSpawner); AnalysisSupportHelpers::addMissingOutputsToReader(dec.providedAODs, dec.requestedAODs, aodReader); - std::sort(requestedCCDBs.begin(), requestedCCDBs.end(), inputSpecLessThan); - std::sort(providedCCDBs.begin(), providedCCDBs.end(), outputSpecLessThan); + std::ranges::sort(requestedCCDBs, inputSpecLessThan); + std::ranges::sort(providedCCDBs, outputSpecLessThan); AnalysisSupportHelpers::addMissingOutputsToReader(providedCCDBs, requestedCCDBs, ccdbBackend); std::vector extraSpecs; @@ -412,7 +411,7 @@ 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 aodReader.outputs.emplace_back(OutputSpec{"TFN", "TFNumber"}); @@ -440,22 +439,22 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext 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::any_of(workflow.begin(), workflow.end(), - [&matcher](auto const& dp) { - return std::any_of(dp.outputs.begin(), dp.outputs.end(), [&matcher](auto const& output) { - return DataSpecUtils::match(matcher, output); - }); - }); + 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::any_of(workflow.begin(), workflow.end(), - [&dstf](auto const& dp) { - return std::any_of(dp.inputs.begin(), dp.inputs.end(), [&dstf](auto const& input) { - return DataSpecUtils::match(input, dstf); - }); - }); + 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. @@ -560,7 +559,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext auto fileSink = AnalysisSupportHelpers::getGlobalAODSink(ctx); extraSpecs.push_back(fileSink); - auto it = std::find_if(dec.outputsInputs.begin(), dec.outputsInputs.end(), [](InputSpec& spec) -> bool { + auto it = std::ranges::find_if(dec.outputsInputs, [](InputSpec& spec) -> bool { return DataSpecUtils::partialMatch(spec, o2::header::DataOrigin("TFN")); }); size_t ii = std::distance(dec.outputsInputs.begin(), it); @@ -694,15 +693,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}); } @@ -1005,7 +997,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); } From 7cc3f1c550e70f8c6b919c7168f505ee13f75dc5 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Mon, 26 Jan 2026 11:22:10 +0100 Subject: [PATCH 174/701] DPL: move snapshot code to use concepts Should be faster to compile and have better debug information. It will also work seamlessly for gsl::span (assuming you have ms_gsl 4.2.1) and std::span. --- .../Core/include/Framework/DataAllocator.h | 243 ++++++++++-------- .../include/Framework/SerializationMethods.h | 13 +- 2 files changed, 143 insertions(+), 113 deletions(-) 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/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) {} From c6b7d8bf9c997a631449da7a98ad7e37d67bc151 Mon Sep 17 00:00:00 2001 From: atriolo Date: Mon, 19 Jan 2026 16:33:45 +0100 Subject: [PATCH 175/701] ALICE3-TRK: added possibility to use a local response file during digitization --- .../ALICE3/TRK/base/include/TRKBase/Specs.h | 8 ++++++ .../include/TRKSimulation/Digitizer.h | 5 ++++ .../ALICE3/TRK/simulation/src/DigiParams.cxx | 3 +++ .../ALICE3/TRK/simulation/src/Digitizer.cxx | 25 +++++++++++++----- .../src/TRKDigitizerSpec.cxx | 26 +++++++++++++++++-- 5 files changed, 59 insertions(+), 8 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h index a5a60422f77eb..95f9f9b00d7f3 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h @@ -131,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/simulation/include/TRKSimulation/Digitizer.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Digitizer.h index 221d7b342bf59..362de63fb8cb6 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Digitizer.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Digitizer.h @@ -45,6 +45,7 @@ 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; } @@ -136,6 +137,8 @@ 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) @@ -145,6 +148,8 @@ class Digitizer const o2::trk::ChipSimResponse* mChipSimRespVD = nullptr; // simulated response for VD chips const o2::trk::ChipSimResponse* mChipSimRespMLOT = nullptr; // simulated response for ML/OT chips + std::string mRespName; /// APTS or ALICE3, depending on the response to be used + bool mSimRespOrientation{false}; // wether the orientation in the response function is flipped float mSimRespVDShift{0.f}; // adjusting the Y-shift in the APTS response function to match sensor local coord. float mSimRespVDScaleX{1.f}; // scale x-local coordinate to response function x-coordinate diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/DigiParams.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/DigiParams.cxx index ca4685d53de2a..e2a78702204e5 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/DigiParams.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/DigiParams.cxx @@ -74,6 +74,9 @@ void DigiParams::print() const 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"); } diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx index 7c988faebf2df..0fd8c7820ce28 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx @@ -49,7 +49,7 @@ void Digitizer::init() } } - // setting the correct response function (for the moment, for both VD and MLOT the APTS response function is udes) + // 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 @@ -65,11 +65,24 @@ void Digitizer::init() 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 - 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; + 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; diff --git a/Steer/DigitizerWorkflow/src/TRKDigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/TRKDigitizerSpec.cxx index a3d4d1f245fc5..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}; @@ -267,7 +278,15 @@ class TRKDPLDigitizerTask : BaseDPLDigitizer // } if (matcher == ConcreteDataMatcher(mOrigin, "APTSRESP", 0)) { LOG(info) << mID.getName() << " loaded APTSResponseData"; - mDigitizer.getParams().setAlpSimResponse((const o2::itsmft::AlpideSimResponse*)obj); + 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"); + } } } @@ -275,6 +294,7 @@ class TRKDPLDigitizerTask : BaseDPLDigitizer 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{}; @@ -307,7 +327,9 @@ DataProcessorSpec getTRKDigitizerSpec(int channel, bool mctruth) 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 From a09a567d02caea8d4d10219dd82243f72144c4a5 Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Tue, 27 Jan 2026 14:50:19 +0100 Subject: [PATCH 176/701] DPL Analysis: Use dangling edges context in more places (#14988) --- .../AnalysisSupport/src/AODWriterHelpers.cxx | 21 +++-- .../CCDBSupport/src/AnalysisCCDBHelpers.cxx | 58 +++++++------- .../CCDBSupport/src/AnalysisCCDBHelpers.h | 2 +- .../Core/include/Framework/AnalysisTask.h | 4 +- Framework/Core/src/AnalysisSupportHelpers.cxx | 4 +- Framework/Core/src/ArrowSupport.cxx | 27 +------ Framework/Core/src/WorkflowHelpers.cxx | 79 +++++++++---------- Framework/Core/src/WorkflowHelpers.h | 3 + 8 files changed, 87 insertions(+), 111 deletions(-) diff --git a/Framework/AnalysisSupport/src/AODWriterHelpers.cxx b/Framework/AnalysisSupport/src/AODWriterHelpers.cxx index d868b7498fb76..b76ffca13977e 100644 --- a/Framework/AnalysisSupport/src/AODWriterHelpers.cxx +++ b/Framework/AnalysisSupport/src/AODWriterHelpers.cxx @@ -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 @@ -241,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>>(); @@ -278,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) { @@ -474,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/CCDBSupport/src/AnalysisCCDBHelpers.cxx b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx index 9ec911518f754..ea13d412cd0b8 100644 --- a/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx +++ b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx @@ -67,38 +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); - schemaMetadata->Append("sourceMatcher", DataSpecUtils::describe(std::get(DataSpecUtils::fromMetadataString(m.defaultValue.get()).matcher))); - 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; @@ -129,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/include/Framework/AnalysisTask.h b/Framework/Core/include/Framework/AnalysisTask.h index c50b5358990de..4f8a9e719e4b9 100644 --- a/Framework/Core/include/Framework/AnalysisTask.h +++ b/Framework/Core/include/Framework/AnalysisTask.h @@ -521,7 +521,7 @@ DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args) std::vector expressionInfos; /// 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([&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()); @@ -620,7 +620,7 @@ DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args) } // reset pre-slice for the next dataframe auto slices = pc.services().get(); - homogeneous_apply_refs([&pc, &slices](auto& element) { + homogeneous_apply_refs([&slices](auto& element) { return analysis_task_parsers::updateSliceInfo(element, slices); }, *(task.get())); diff --git a/Framework/Core/src/AnalysisSupportHelpers.cxx b/Framework/Core/src/AnalysisSupportHelpers.cxx index 15b56f9afbff5..7453751315626 100644 --- a/Framework/Core/src/AnalysisSupportHelpers.cxx +++ b/Framework/Core/src/AnalysisSupportHelpers.cxx @@ -98,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++) { @@ -144,7 +144,7 @@ void AnalysisSupportHelpers::addMissingOutputsToSpawner(std::vector sinks::append_to{publisher.outputs}; // append them to the publisher outputs std::vector additionalInputs; - for (auto& input : requestedSpecials | views::filter_not_matching(providedSpecials)) { + for (auto const& input : requestedSpecials | views::filter_not_matching(providedSpecials)) { input.metadata | views::filter_string_params_with("input:") | views::params_to_input_specs() | diff --git a/Framework/Core/src/ArrowSupport.cxx b/Framework/Core/src/ArrowSupport.cxx index 60277dfc38a74..31cddc9803d69 100644 --- a/Framework/Core/src/ArrowSupport.cxx +++ b/Framework/Core/src/ArrowSupport.cxx @@ -685,33 +685,8 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() } } - // 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! - dec.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]) { - dec.outputsInputsAOD.emplace_back(outputsInputs[ii]); - } - } - } + WorkflowHelpers::injectAODWriter(workflow, ctx); - // file sink for any AOD output - if (!dec.outputsInputsAOD.empty()) { - // add TFNumber and TFFilename as input to the writer - dec.outputsInputsAOD.emplace_back("tfn", "TFN", "TFNumber"); - dec.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") { diff --git a/Framework/Core/src/WorkflowHelpers.cxx b/Framework/Core/src/WorkflowHelpers.cxx index ff1ff1f4cf13d..714706952d26c 100644 --- a/Framework/Core/src/WorkflowHelpers.cxx +++ b/Framework/Core/src/WorkflowHelpers.cxx @@ -420,10 +420,10 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext // 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 @@ -531,43 +531,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext workflow.insert(workflow.end(), extraSpecs.begin(), extraSpecs.end()); extraSpecs.clear(); - /// Analyze all ouputs - auto [outputsInputsTmp, isDanglingTmp] = analyzeOutputs(workflow); - dec.isDangling = isDanglingTmp; - dec.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 < 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 - dec.outputsInputsAOD.emplace_back(InputSpec{"tfn", "TFN", "TFNumber"}); - dec.outputsInputsAOD.emplace_back(InputSpec{"tff", "TFF", "TFFilename"}); - auto fileSink = AnalysisSupportHelpers::getGlobalAODSink(ctx); - extraSpecs.push_back(fileSink); - - auto it = std::ranges::find_if(dec.outputsInputs, [](InputSpec& spec) -> bool { - return DataSpecUtils::partialMatch(spec, o2::header::DataOrigin("TFN")); - }); - size_t ii = std::distance(dec.outputsInputs.begin(), it); - dec.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; @@ -704,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, 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); From efad2290e1efb36f28e1c84c7e062525a919eb76 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Tue, 27 Jan 2026 13:39:03 +0100 Subject: [PATCH 177/701] ITSMFT: fix number of rofs per TF Signed-off-by: Felix Schlepper --- Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx index 6809c8dee3f19..eafb72c675a58 100644 --- a/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx @@ -184,8 +184,10 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer // 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 - std::vector expDigitRofVec(nROFsTF); - for (int iROF{0}; iROF < nROFsTF; ++iROF) { + // 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; @@ -204,7 +206,7 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer 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()); + LOGP(fatal, "detected mismatch between expected {} and received {}", expROF.asString(), rof.asString()); } } int prevFirst{0}; @@ -214,6 +216,9 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer } 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]); From 0cf7ec22173e7817599b997d189328ba65ecabc4 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Tue, 27 Jan 2026 21:10:07 +0100 Subject: [PATCH 178/701] GPU CMake: Improve architecture auto-detection --- dependencies/FindO2GPU.cmake | 24 ++++++++++++++++++------ 1 file changed, 18 insertions(+), 6 deletions(-) diff --git a/dependencies/FindO2GPU.cmake b/dependencies/FindO2GPU.cmake index 6ca311905e01c..21e2d7cad239a 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 8 +# FindO2GPU.cmake Version 9 + +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,11 +37,11 @@ if(CMAKE_BUILD_TYPE_UPPER STREQUAL "DEBUG") endif() if(CUDA_COMPUTETARGET AND CUDA_COMPUTETARGET STREQUAL "default") - set(CUDA_COMPUTETARGET 80-real 86-real 89-real 120-real 75-virtual) + 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) @@ -112,7 +117,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) @@ -156,6 +161,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 "") + 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}}") @@ -184,7 +196,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() @@ -305,7 +317,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 ") @@ -321,6 +332,7 @@ if(ENABLE_HIP) if(HIP_AMDGPUTARGET) set(CMAKE_HIP_ARCHITECTURES "${HIP_AMDGPUTARGET}") endif() + message(STATUS "HIP Found (${hip_HIPCC_EXECUTABLE} version ${hip_VERSION}, Architectures ${CMAKE_HIP_ARCHITECTURES})") else() set(HIP_ENABLED OFF) endif() From cb66b5edfc8322bc792b255368e52a897066c76a Mon Sep 17 00:00:00 2001 From: shahor02 Date: Wed, 28 Jan 2026 09:53:51 +0100 Subject: [PATCH 179/701] Add extra info with charge and timing and occupancy to unbinned residuals (#14969) * Add extra info with charge and timing to unbinned residuals * Store TOF time wrt t0 in DetInfoRes, diff to expectation in trackData.deltaTOF * Add per-stack TPC mult info to TrackData --- .../tpcinterpolationworkflow/CMakeLists.txt | 2 + .../TPCResidualAggregatorSpec.h | 6 +- .../TPCUnbinnedResidualReaderSpec.h | 1 + .../src/TPCInterpolationSpec.cxx | 15 +- .../src/TPCResidualWriterSpec.cxx | 1 + .../src/TPCUnbinnedResidualReaderSpec.cxx | 7 + .../calibration/SpacePoints/CMakeLists.txt | 3 +- .../include/SpacePoints/ResidualAggregator.h | 3 +- .../include/SpacePoints/TrackInterpolation.h | 95 ++++++++++- .../SpacePoints/src/ResidualAggregator.cxx | 10 +- .../SpacePoints/src/SpacePointCalibLinkDef.h | 2 + .../SpacePoints/src/TrackInterpolation.cxx | 154 ++++++++++++++++-- 12 files changed, 269 insertions(+), 30 deletions(-) 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/TPC/calibration/SpacePoints/CMakeLists.txt b/Detectors/TPC/calibration/SpacePoints/CMakeLists.txt index 510cff4f7760c..47bb9c09a9951 100644 --- a/Detectors/TPC/calibration/SpacePoints/CMakeLists.txt +++ b/Detectors/TPC/calibration/SpacePoints/CMakeLists.txt @@ -29,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/TrackInterpolation.h b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackInterpolation.h index 992925179ffce..e7d0fb197ea42 100644 --- a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackInterpolation.h +++ b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackInterpolation.h @@ -47,6 +47,11 @@ class TTree; namespace o2 { +namespace gpu +{ +class GPUParam; +} + namespace tpc { class VDriftCorrFact; @@ -101,15 +106,72 @@ struct UnbinnedResid { ClassDefNV(UnbinnedResid, 2); }; +struct DetInfoResid { // detector info associated with residual + uint32_t word = 0; // container interpreted in a different way depending on the detector type + // + // TPC view: qTot and qMax of the cluster + uint16_t qTotTPC() const { return static_cast(word & 0xFFFFu); } + uint16_t qMaxTPC() const { return static_cast((word >> 16) & 0xFFFFu); } + void setTPC(uint16_t qTot, uint16_t qMax) { word = (static_cast(qMax) << 16) | static_cast(qTot); } + // + // TRD view: q0, q1, q2 + calibrated slope (truncated to in +-3.5 range) + static constexpr uint32_t TRDQ0NB = 7, TRDQ1NB = 7, TRDQ2NB = 6, TRDSlpNB = 12; + static constexpr uint32_t TRDQ0Msk = (1 << TRDQ0NB) - 1, TRDQ1Msk = (1 << TRDQ1NB) - 1, TRDQ2Msk = ((1 << TRDQ2NB) - 1), TRDSlpMsk = (1 << TRDSlpNB) - 1; + static constexpr float TRDMaxSlope = 3.5, TRDSlope2Int = ((1 << TRDSlpNB) - 1) / (2 * TRDMaxSlope), TRDInt2Slope = 1.f / TRDSlope2Int; + uint16_t q0TRD() const { return static_cast(word & TRDQ0Msk); } + uint16_t q1TRD() const { return static_cast((word >> TRDQ0NB) & TRDQ1Msk); } + uint16_t q2TRD() const { return static_cast((word >> (TRDQ0NB + TRDQ1NB)) & TRDQ2Msk); } + float slopeTRD() const { return ((word >> (TRDQ0NB + TRDQ1NB + TRDQ2NB)) & TRDSlpMsk) * TRDInt2Slope - TRDMaxSlope; } + void setTRD(uint8_t q0, uint8_t q1, uint8_t q2, float slope) + { + float rslope = (slope + TRDMaxSlope) * TRDSlope2Int; + if (rslope < 0.f) { + rslope = 0; + } else if (rslope > TRDSlpMsk) { + rslope = TRDSlpMsk; + } + uint32_t slpI = std::round(rslope); + word = (static_cast(slpI << (TRDQ0NB + TRDQ1NB + TRDQ2NB)) | + static_cast((q2 & TRDQ2Msk) << (TRDQ0NB + TRDQ1NB)) | + static_cast((q1 & TRDQ1Msk) << TRDQ0NB) | + static_cast(q0 & TRDQ0Msk)); + } + // + // TOF view (time difference in \mus wrt seeding ITS-TPC track) + float timeTOF() const { return std::bit_cast(word); } + void setTOF(float t) { word = std::bit_cast(t); } + // + // No info for ITS is stored + // + // PV view (time difference in \mus wrt contributing ITS-TPC track) + float timePV() const { return std::bit_cast(word); } + void setPV(float t) { word = std::bit_cast(t); } + + ClassDefNV(DetInfoResid, 1); +}; + /// Structure for the information required to associate each residual with a given track type (ITS-TPC-TRD-TOF, etc) struct TrackDataCompact { TrackDataCompact() = default; - TrackDataCompact(uint32_t idx, uint8_t nRes, uint8_t source, 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 @@ -268,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 @@ -296,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; } @@ -308,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 @@ -348,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 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 7db5b7455f1a7..6c37be9ddc1b1 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 @@ -135,7 +140,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)); @@ -316,6 +321,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"; @@ -354,6 +363,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) { @@ -435,6 +445,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(); @@ -468,7 +480,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; @@ -481,6 +495,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 @@ -627,10 +647,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; } @@ -655,6 +684,7 @@ 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; @@ -662,6 +692,30 @@ void TrackInterpolation::interpolateTrack(int iSeed) } 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? @@ -670,7 +724,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; } @@ -684,6 +738,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++; } } @@ -710,8 +765,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; @@ -738,6 +801,7 @@ 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++; } } @@ -759,6 +823,11 @@ void TrackInterpolation::interpolateTrack(int iSeed) 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++; } } @@ -767,7 +836,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); @@ -785,7 +854,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); @@ -827,6 +897,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; } @@ -836,6 +912,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()); @@ -866,6 +944,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; @@ -897,10 +977,14 @@ 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; } @@ -937,6 +1021,7 @@ 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; @@ -944,6 +1029,30 @@ void TrackInterpolation::extrapolateTrack(int iSeed) } 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? @@ -955,7 +1064,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; } @@ -970,6 +1079,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++; } } @@ -979,8 +1089,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()}; @@ -1002,6 +1116,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; @@ -1028,6 +1149,7 @@ 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++; } } @@ -1049,6 +1171,11 @@ void TrackInterpolation::extrapolateTrack(int iSeed) 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++; } } @@ -1057,7 +1184,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; @@ -1445,6 +1572,7 @@ void TrackInterpolation::reset() mTrackDataCompact.clear(); mTrackDataExtended.clear(); mClRes.clear(); + mDetInfoRes.clear(); mTrackDataUnfiltered.clear(); mClResUnfiltered.clear(); mGIDsSuccess.clear(); From 0df45c42929034ad639a89a0618896090012270a Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Thu, 25 Sep 2025 15:57:50 +0200 Subject: [PATCH 180/701] Implementation of TPC loopers in O2 --- Generators/CMakeLists.txt | 10 + Generators/include/Generators/Generator.h | 13 + Generators/include/Generators/TPCLoopers.h | 148 ++++++ .../include/Generators/TPCLoopersParam.h | 54 ++ Generators/share/TPCLoopers/README.md | 79 +++ .../share/TPCLoopers/ScalerComptonParams.json | 28 + .../share/TPCLoopers/ScalerPairParams.json | 34 ++ .../share/TPCLoopers/gaussian_params.csv | 4 + .../share/TPCLoopers/poisson_params.csv | 3 + Generators/src/Generator.cxx | 184 +++++++ Generators/src/GeneratorsLinkDef.h | 4 + Generators/src/TPCLoopers.cxx | 486 ++++++++++++++++++ Generators/src/TPCLoopersParam.cxx | 15 + prodtests/full_system_test.sh | 37 +- 14 files changed, 1095 insertions(+), 4 deletions(-) create mode 100644 Generators/include/Generators/TPCLoopers.h create mode 100644 Generators/include/Generators/TPCLoopersParam.h create mode 100644 Generators/share/TPCLoopers/README.md create mode 100644 Generators/share/TPCLoopers/ScalerComptonParams.json create mode 100644 Generators/share/TPCLoopers/ScalerPairParams.json create mode 100644 Generators/share/TPCLoopers/gaussian_params.csv create mode 100644 Generators/share/TPCLoopers/poisson_params.csv create mode 100644 Generators/src/TPCLoopers.cxx create mode 100644 Generators/src/TPCLoopersParam.cxx 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..3484601aa42bb 100644 --- a/Generators/include/Generators/Generator.h +++ b/Generators/include/Generators/Generator.h @@ -17,6 +17,10 @@ #include "FairGenerator.h" #include "TParticle.h" #include "Generators/Trigger.h" +#ifdef GENERATORS_WITH_TPCLOOPERS +#include "Generators/TPCLoopers.h" +#include "Generators/TPCLoopersParam.h" +#endif #include #include #include @@ -73,6 +77,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 +159,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 +169,12 @@ class Generator : public FairGenerator // global static information about (upper limit of) number of events to be generated static unsigned int gTotalNEvents; +#ifdef GENERATORS_WITH_TPCLOOPERS + // Loopers generator instance + std::unique_ptr mTPCLoopersGen = nullptr; + bool initTPCLoopersGen(); +#endif + ClassDefOverride(Generator, 2); }; /** class Generator **/ diff --git a/Generators/include/Generators/TPCLoopers.h b/Generators/include/Generators/TPCLoopers.h new file mode 100644 index 0000000000000..6a1d3ef262e22 --- /dev/null +++ b/Generators/include/Generators/TPCLoopers.h @@ -0,0 +1,148 @@ +// 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 +#endif +#include +#include +#include "TRandom3.h" +#include +#include "TParticle.h" + +#ifdef GENERATORS_WITH_TPCLOOPERS +// 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..465a8ffb7ee22 100644 --- a/Generators/src/Generator.cxx +++ b/Generators/src/Generator.cxx @@ -17,11 +17,16 @@ #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 namespace o2 { @@ -39,6 +44,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,7 +73,126 @@ 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 +} + +/*****************************************************************/ +#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 = std::make_unique(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(); + mTPCLoopersGen.reset(); + } + return kTRUE; } +#endif /*****************************************************************/ @@ -64,6 +207,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 +269,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/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/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/prodtests/full_system_test.sh b/prodtests/full_system_test.sh index 6408588d46e68..e2ecca590140f 100755 --- a/prodtests/full_system_test.sh +++ b/prodtests/full_system_test.sh @@ -54,8 +54,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 +137,40 @@ 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 1 \ + --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 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_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 From 52b0e23ac5a60f3953749a91a29285b67d7d1558 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 28 Jan 2026 15:32:46 +0100 Subject: [PATCH 181/701] DPL: disable early forwarding for output proxies --- Framework/Core/src/DataProcessingDevice.cxx | 9 +++++++++ Framework/Core/src/ExternalFairMQDeviceProxy.cxx | 2 ++ 2 files changed, 11 insertions(+) diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index 3eaab36fb7908..fd03b7725d847 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -1070,6 +1070,15 @@ void DataProcessingDevice::fillContext(DataProcessorContext& context, DeviceCont 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 diff --git a/Framework/Core/src/ExternalFairMQDeviceProxy.cxx b/Framework/Core/src/ExternalFairMQDeviceProxy.cxx index b4bfc991db9ae..3b0275879a158 100644 --- a/Framework/Core/src/ExternalFairMQDeviceProxy.cxx +++ b/Framework/Core/src/ExternalFairMQDeviceProxy.cxx @@ -1045,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; } @@ -1180,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; } From 3669ad3516f3d4a7ced7685735c35d60a15277f7 Mon Sep 17 00:00:00 2001 From: Felix Weiglhofer Date: Thu, 22 Jan 2026 16:36:03 +0100 Subject: [PATCH 182/701] GPU: Parallelize TPC pad filter over pad rows instead of cachelines. --- GPU/GPUTracking/DataTypes/GPUTPCGeometry.h | 2 + .../Definitions/GPUDefParametersDefaults.h | 8 +- .../Global/GPUChainTrackingClusterizer.cxx | 2 +- .../GPUTPCCFCheckPadBaseline.cxx | 101 +++++++++++++----- .../GPUTPCCFCheckPadBaseline.h | 40 ++++++- 5 files changed, 118 insertions(+), 35 deletions(-) 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/Definitions/GPUDefParametersDefaults.h b/GPU/GPUTracking/Definitions/GPUDefParametersDefaults.h index 01ae33dc3b4d8..1be881ee6323e 100644 --- a/GPU/GPUTracking/Definitions/GPUDefParametersDefaults.h +++ b/GPU/GPUTracking/Definitions/GPUDefParametersDefaults.h @@ -68,7 +68,7 @@ #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, 10 + #define GPUCA_LB_GPUTPCCFCheckPadBaseline 576, 2 #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillIndexMap 512 #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillFromDigits 512 #define GPUCA_LB_GPUTPCCFChargeMapFiller_findFragmentStart 512 @@ -133,7 +133,7 @@ #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, 2 + #define GPUCA_LB_GPUTPCCFCheckPadBaseline 576, 2 #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillIndexMap 512 #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillFromDigits 512 #define GPUCA_LB_GPUTPCCFChargeMapFiller_findFragmentStart 512 @@ -197,7 +197,7 @@ #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_GPUTPCCFCheckPadBaseline 576,2 #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillIndexMap 448 #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillFromDigits 448 #define GPUCA_LB_GPUTPCCFChargeMapFiller_findFragmentStart 448 @@ -447,7 +447,7 @@ #define GPUCA_LB_GPUTPCStartHitsSorter 256 #endif #ifndef GPUCA_LB_GPUTPCCFCheckPadBaseline - #define GPUCA_LB_GPUTPCCFCheckPadBaseline 64 + #define GPUCA_LB_GPUTPCCFCheckPadBaseline 576 #endif #ifndef GPUCA_LB_GPUTPCCFChargeMapFiller_fillIndexMap #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillIndexMap 512 diff --git a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx index 7629086272ed6..bf6577cfd929e 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx @@ -962,7 +962,7 @@ 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); diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.cxx index 3248e517ff465..33ed089890bc4 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.cxx @@ -9,13 +9,12 @@ // 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 @@ -28,51 +27,88 @@ 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 +} -#else // CPU CODE +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); + + if (not basePos.valid()) { + return; + } constexpr size_t ElemsInTileRow = (size_t)TilingLayout>::WidthInTiles * TimebinsPerCacheline * PadsPerCacheline; @@ -122,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; @@ -130,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 25c93a4649662..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; @@ -48,11 +61,28 @@ class GPUTPCCFCheckPadBaseline : public GPUKernelTemplate 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); }; From f86363afb697abe64b9bf593a6cd92dd45b49aae Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Mon, 26 Jan 2026 13:16:00 +0100 Subject: [PATCH 183/701] ITS: instaniate TRK classes Signed-off-by: Felix Schlepper --- Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx | 4 ++++ Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx | 13 +++++++++++-- Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx | 4 ++++ 3 files changed, 19 insertions(+), 2 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx index 0d8b461181741..70f4e3d1d3fc7 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx @@ -667,5 +667,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 fe67eadaf6f72..15459576a1031 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx @@ -1327,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 From 2d37a89a8539690def207cc7fd55dbf8b7514b03 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Mon, 26 Jan 2026 13:53:46 +0100 Subject: [PATCH 184/701] ITS: enlarge StartLayerMask for TRK Signed-off-by: Felix Schlepper --- .../ITSMFT/ITS/tracking/include/ITStracking/Configuration.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h index 1019a3e3d45a9..1c4d604a629ed 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h @@ -68,7 +68,7 @@ struct TrackingParameters { 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 = true; // repeat outward refit using inward refit as a seed bool ShiftRefToCluster = true; // TrackFit: after update shift the linearization reference to cluster bool FindShortTracks = false; From 8eebfb5e616c670e42b7a41742b64c123a9e7b54 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Mon, 26 Jan 2026 17:30:26 +0100 Subject: [PATCH 185/701] ITS: GPU: reduce TrackITS allocation Signed-off-by: Felix Schlepper --- .../GPU/ITStrackingGPU/TimeFrameGPU.h | 8 +- .../GPU/ITStrackingGPU/TrackingKernels.h | 61 +++++-- .../ITS/tracking/GPU/cuda/TimeFrameGPU.cu | 23 +-- .../tracking/GPU/cuda/TrackerTraitsGPU.cxx | 65 ++++--- .../ITS/tracking/GPU/cuda/TrackingKernels.cu | 169 +++++++++++++----- 5 files changed, 234 insertions(+), 92 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h index 8095d690bbcc8..d6d87eb8c1143 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h @@ -80,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(); @@ -140,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]; } @@ -219,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 6e0427f5413ba..53992ccf3eb85 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h @@ -207,23 +207,48 @@ void processNeighboursHandler(const int startLayer, const int nThreads); template -void trackSeedHandler(CellSeed* trackSeeds, - const TrackingFrameInfo** foundTrackingFrameInfo, - const Cluster** unsortedClusters, - o2::its::TrackITSExt* tracks, - 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, - 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/cuda/TimeFrameGPU.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu index 6532165d9628a..c7d6e31ec771a 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu @@ -439,8 +439,10 @@ 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->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); - GPUChkErrS(cudaHostRegister(seeds.data(), seeds.size() * sizeof(CellSeedN), cudaHostRegisterPortable)); 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 @@ -458,14 +460,15 @@ void TimeFrameGPU::createNeighboursDevice(const unsigned int layer) } 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->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); - 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 @@ -588,13 +591,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 diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx index c4a5cfb4e26b3..60774e3313d7f 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx @@ -322,29 +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* - mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), // TrackingFrameInfo** - mTimeFrameGPU->getDeviceArrayUnsortedClusters(), // Cluster** - mTimeFrameGPU->getDeviceTrackITSExt(), // o2::its::TrackITSExt* - this->mTrkParams[iteration].LayerRadii, // const std::vector& - this->mTrkParams[iteration].MinPt, // const std::vector& - 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 - this->mTrkParams[0].RepeatRefitOut, - this->mTrkParams[0].ReseedIfShorter, - this->mTrkParams[0].ShiftRefToCluster, - 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].RepeatRefitOut, + this->mTrkParams[0].ReseedIfShorter, + 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].RepeatRefitOut, + this->mTrkParams[0].ReseedIfShorter, + this->mTrkParams[0].ShiftRefToCluster, + mTimeFrameGPU->getDevicePropagator(), + this->mTrkParams[0].CorrType, + mTimeFrameGPU->getFrameworkAllocator(), + conf.nBlocksTracksSeeds[iteration], + conf.nThreadsTracksSeeds[iteration]); + mTimeFrameGPU->downloadTrackITSExtDevice(); auto& tracks = mTimeFrameGPU->getTrackITSExt(); diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu index 85689488f5f6e..e5427c07cb52b 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -277,12 +277,13 @@ 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, @@ -297,6 +298,13 @@ GPUg() void __launch_bounds__(256, 1) fitTrackSeedsKernel( const o2::base::PropagatorF::MatCorrType matCorrType) { for (int iCurrentTrackSeedIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentTrackSeedIndex < nSeeds; iCurrentTrackSeedIndex += blockDim.x * gridDim.x) { + + if constexpr (!initRun) { + if (seedLUT[iCurrentTrackSeedIndex] == seedLUT[iCurrentTrackSeedIndex + 1]) { + continue; + } + } + TrackITSExt temporaryTrack = seedTrackForRefit(trackSeeds[iCurrentTrackSeedIndex], foundTrackingFrameInfo, unsortedClusters, layerRadii, bz, reseedIfShorter); o2::track::TrackPar linRef{temporaryTrack}; bool fitSuccess = fitTrack(temporaryTrack, // TrackITSExt& track, @@ -366,7 +374,12 @@ GPUg() void __launch_bounds__(256, 1) fitTrackSeedsKernel( temporaryTrack.getParamIn() = saveInw; temporaryTrack.setChi2(saveChi2); } - tracks[iCurrentTrackSeedIndex] = temporaryTrack; + + if constexpr (initRun) { + seedLUT[iCurrentTrackSeedIndex] = 1; + } else { + tracks[seedLUT[iCurrentTrackSeedIndex]] = temporaryTrack; + } } } @@ -1191,32 +1204,84 @@ void processNeighboursHandler(const int startLayer, } template -void trackSeedHandler(CellSeed* trackSeeds, - const TrackingFrameInfo** foundTrackingFrameInfo, - const Cluster** unsortedClusters, - o2::its::TrackITSExt* tracks, - 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, - 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); + 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<<>>( + 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 @@ -1229,8 +1294,9 @@ void trackSeedHandler(CellSeed* trackSeeds, 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()); + thrust::sort(sync_policy, tr_ptr, tr_ptr + nTracks, gpu::compare_track_chi2()); } /// Explicit instantiation of ITS2 handlers @@ -1394,23 +1460,46 @@ template void processNeighboursHandler<7>(const int startLayer, const int nBlocks, const int nThreads); -template void trackSeedHandler(CellSeed<7>* trackSeeds, - const TrackingFrameInfo** foundTrackingFrameInfo, - const Cluster** unsortedClusters, - o2::its::TrackITSExt* tracks, - 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, - 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 From c3f85c1d7cbbd4c9e16819f1f0d23b47d7004796 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Mon, 26 Jan 2026 17:31:50 +0100 Subject: [PATCH 186/701] ITS: fix correctForMaterial arg for actual layer Signed-off-by: Felix Schlepper --- Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu | 2 +- Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu index e5427c07cb52b..7c257bc1d0ba1 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -505,7 +505,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; } diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx index 15459576a1031..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; } From b62d3d6084dc219ffe1e99c8d42042d043c73773 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Wed, 28 Jan 2026 11:10:20 +0100 Subject: [PATCH 187/701] ITS: GPU: create compile time stack tags Signed-off-by: Felix Schlepper --- .../ITS/tracking/GPU/cuda/TimeFrameGPU.cu | 25 +++++++++++++++---- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu index c7d6e31ec771a..da0cd51478945 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu @@ -11,7 +11,6 @@ /// #include -#include #include #include @@ -633,21 +632,37 @@ 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 - const auto name = fmt::format("ITSITER{}", iteration); - (this->mExternalAllocator)->pushTagOnStack(qStr2Tag(name.c_str())); + (this->mExternalAllocator)->pushTagOnStack(detail::kIterTags[iteration]); } template void TimeFrameGPU::popMemoryStack(const int iteration) { // pop all memory on the stack from this iteration - const auto name = fmt::format("ITSITER{}", iteration); - (this->mExternalAllocator)->popTagOffStack(qStr2Tag(name.c_str())); + (this->mExternalAllocator)->popTagOffStack(detail::kIterTags[iteration]); } template From 5f4f95a4797a1f5082d3c388fbc05aff8438ba95 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Tue, 27 Jan 2026 19:25:24 +0100 Subject: [PATCH 188/701] GPU: add constexpr version of qStr2Tag Signed-off-by: Felix Schlepper --- GPU/GPUTracking/utils/strtag.h | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) 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 From e4a4f1a0029b3370a50410ee061b6acfa1a83ec1 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Tue, 27 Jan 2026 20:13:38 +0100 Subject: [PATCH 189/701] ITS: GPU: add skipping of parts where nothing was found Signed-off-by: Felix Schlepper --- .../ITS/tracking/GPU/cuda/TrackingKernels.cu | 25 +++++++++++++++---- .../ITS/tracking/GPU/hip/CMakeLists.txt | 2 ++ 2 files changed, 22 insertions(+), 5 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu index 7c257bc1d0ba1..a12237358c8bd 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -399,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]}; @@ -464,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]}; @@ -526,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; + } } } @@ -692,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) { 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 From aa3ef3751f282ee477e0636d6bd5697c43103381 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Tue, 27 Jan 2026 20:14:15 +0100 Subject: [PATCH 190/701] ITS: GPU: more memory clearing in processNeighbours Signed-off-by: Felix Schlepper --- .../ITS/tracking/GPU/cuda/TrackingKernels.cu | 84 +++++++++++++------ 1 file changed, 58 insertions(+), 26 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu index a12237358c8bd..50888c676df77 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -39,6 +39,7 @@ // O2 track model #include "ReconstructionDataFormats/Track.h" #include "DetectorsBase/Propagator.h" +#include "utils/strtag.h" using namespace o2::track; namespace o2::its @@ -1106,11 +1107,19 @@ void processNeighboursHandler(const int startLayer, const int nBlocks, const int nThreads) { + constexpr uint64_t Tag = qStr2Tag("ITS_PNH1"); + + // allocators used auto allocInt = gpu::TypedAllocator(alloc); auto allocCellSeed = gpu::TypedAllocator>(alloc); - thrust::device_vector> foundSeedsTable(nCells[startLayer] + 1, 0, allocInt); - auto nosync_policy = THRUST_NAMESPACE::par_nosync(gpu::TypedAllocator(alloc)).on(gpu::Stream::DefaultStream); + // use sync_policy, this part cannot be run async but tell thrust to use the allocator + auto sync_policy = THRUST_NAMESPACE::par(gpu::TypedAllocator(alloc)); + + // put initial computation on Tag1 + alloc->pushTagOnStack(Tag); + // start processing of cells + thrust::device_vector> foundSeedsTable(nCells[startLayer] + 1, 0, allocInt); gpu::processNeighboursKernel<<>>( startLayer, startLevel, @@ -1129,10 +1138,10 @@ void processNeighboursHandler(const int startLayer, maxChi2ClusterAttachment, propagator, matCorrType); - thrust::exclusive_scan(nosync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), foundSeedsTable.begin()); - - thrust::device_vector> updatedCellId(foundSeedsTable.back(), 0, allocInt); - thrust::device_vector, gpu::TypedAllocator>> updatedCellSeed(foundSeedsTable.back(), allocCellSeed); + thrust::exclusive_scan(sync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), foundSeedsTable.begin()); + auto foundSeeds{foundSeedsTable.back()}; + thrust::device_vector> updatedCellId(foundSeeds, 0, allocInt); + thrust::device_vector, gpu::TypedAllocator>> updatedCellSeed(foundSeeds, allocCellSeed); gpu::processNeighboursKernel<<>>( startLayer, startLevel, @@ -1151,20 +1160,41 @@ void processNeighboursHandler(const int startLayer, maxChi2ClusterAttachment, propagator, matCorrType); - GPUChkErrS(cudaStreamSynchronize(gpu::Stream::DefaultStream)); + // now do inward steps until stop is reached int level = startLevel; - thrust::device_vector> lastCellId(allocInt); - thrust::device_vector, gpu::TypedAllocator>> lastCellSeed(allocCellSeed); + + // Host buffers to break dependency + // FIXME: these should be on our memory resource! + std::vector hostCellId; + std::vector> hostCellSeed; + + // inward loop for (int iLayer{startLayer - 1}; iLayer > 0 && level > 2; --iLayer) { - lastCellSeed.swap(updatedCellSeed); - lastCellId.swap(updatedCellId); + // copy current results to host + hostCellId.resize(updatedCellId.size()); + hostCellSeed.resize(updatedCellSeed.size()); + thrust::copy(updatedCellId.begin(), updatedCellId.end(), hostCellId.begin()); + thrust::copy(updatedCellSeed.begin(), updatedCellSeed.end(), hostCellSeed.begin()); + + auto lastCellSeedSize{hostCellSeed.size()}; + // but before we clear the memory, and immediately start a new block + alloc->popTagOffStack(Tag); + alloc->pushTagOnStack(Tag); + + // based on the previous step's result create new LUT and zero it + thrust::device_vector>(allocInt).swap(foundSeedsTable); + foundSeedsTable.resize(lastCellSeedSize + 1); + thrust::fill(sync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), 0); + + // recreate lastCell vectors from host + thrust::device_vector> lastCellId(hostCellId.begin(), hostCellId.end(), allocInt); + thrust::device_vector, gpu::TypedAllocator>> lastCellSeed(hostCellSeed.begin(), hostCellSeed.end(), allocCellSeed); + // also create new vectors on new block thrust::device_vector, gpu::TypedAllocator>>(allocCellSeed).swap(updatedCellSeed); thrust::device_vector>(allocInt).swap(updatedCellId); - auto lastCellSeedSize{lastCellSeed.size()}; - foundSeedsTable.resize(lastCellSeedSize + 1); - thrust::fill(nosync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), 0); + // start step gpu::processNeighboursKernel<<>>( iLayer, --level, @@ -1183,14 +1213,13 @@ void processNeighboursHandler(const int startLayer, maxChi2ClusterAttachment, propagator, matCorrType); - thrust::exclusive_scan(nosync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), foundSeedsTable.begin()); - - auto foundSeeds{foundSeedsTable.back()}; + // how many new seeds where found + thrust::exclusive_scan(sync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), foundSeedsTable.begin()); + foundSeeds = foundSeedsTable.back(); + // do a resize, we don't need to set the memory now since we know that all of these are written to + // Note though this does not clear the memory... updatedCellId.resize(foundSeeds); - thrust::fill(nosync_policy, updatedCellId.begin(), updatedCellId.end(), 0); updatedCellSeed.resize(foundSeeds); - thrust::fill(nosync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), CellSeed()); - gpu::processNeighboursKernel<<>>( iLayer, level, @@ -1210,12 +1239,15 @@ void processNeighboursHandler(const int startLayer, propagator, matCorrType); } - GPUChkErrS(cudaStreamSynchronize(gpu::Stream::DefaultStream)); - thrust::device_vector, gpu::TypedAllocator>> outSeeds(updatedCellSeed.size(), allocCellSeed); - auto end = thrust::copy_if(nosync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), outSeeds.begin(), gpu::seed_selector(1.e3, maxChi2NDF * ((startLevel + 2) * 2 - 5))); - auto s{end - outSeeds.begin()}; - seedsHost.reserve(seedsHost.size() + s); - thrust::copy(outSeeds.begin(), outSeeds.begin() + s, std::back_inserter(seedsHost)); + + // final copy of result + const auto selector = gpu::seed_selector(1.e3, maxChi2NDF * ((startLevel + 2) * 2 - 5)); + const auto count = thrust::count_if(sync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), selector); + thrust::device_vector, gpu::TypedAllocator>> outSeeds(count, allocCellSeed); + thrust::copy_if(sync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), outSeeds.begin(), selector); + seedsHost.reserve(seedsHost.size() + count); + thrust::copy(outSeeds.begin(), outSeeds.end(), std::back_inserter(seedsHost)); + alloc->popTagOffStack(Tag); } template From 0bb564d94f58153218e6cb421a5c666239c44428 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Wed, 28 Jan 2026 12:35:23 +0100 Subject: [PATCH 191/701] ITS: GPU: fix wrong argument parsing for outward refit Signed-off-by: Felix Schlepper --- Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx | 4 ++-- .../ITSMFT/ITS/tracking/include/ITStracking/Configuration.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx index 60774e3313d7f..42d2227de60f8 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx @@ -337,8 +337,8 @@ void TrackerTraitsGPU::findRoads(const int iteration) startLevel, this->mTrkParams[0].MaxChi2ClusterAttachment, this->mTrkParams[0].MaxChi2NDF, - this->mTrkParams[0].RepeatRefitOut, this->mTrkParams[0].ReseedIfShorter, + this->mTrkParams[0].RepeatRefitOut, this->mTrkParams[0].ShiftRefToCluster, mTimeFrameGPU->getDevicePropagator(), this->mTrkParams[0].CorrType, @@ -359,8 +359,8 @@ void TrackerTraitsGPU::findRoads(const int iteration) startLevel, this->mTrkParams[0].MaxChi2ClusterAttachment, this->mTrkParams[0].MaxChi2NDF, - this->mTrkParams[0].RepeatRefitOut, this->mTrkParams[0].ReseedIfShorter, + this->mTrkParams[0].RepeatRefitOut, this->mTrkParams[0].ShiftRefToCluster, mTimeFrameGPU->getDevicePropagator(), this->mTrkParams[0].CorrType, diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h index 1c4d604a629ed..10e1681c73e8d 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h @@ -69,7 +69,7 @@ struct TrackingParameters { int ReseedIfShorter = 6; // reseed for the final fit track with the length shorter than this std::vector MinPt = {0.f, 0.f, 0.f, 0.f}; uint16_t StartLayerMask = 0x7F; - bool RepeatRefitOut = true; // repeat outward refit using inward refit as a seed + 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; From 63e5c6136f9eb261968541ae80cc589d2c1b0a7f Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Thu, 29 Jan 2026 13:44:05 +0100 Subject: [PATCH 192/701] DPL Analysis: cleanup AnalysisTask.h and ASoA.h (#14996) * remove unnecessary include; unnecessary mutability; too specific requires; unused function * use non-capturing lambdas; restrict template * we only expect void process functions * improve getIterators * improve homogeneous_apply_ref --------- Co-authored-by: ALICE Action Bot --- Framework/Core/include/Framework/ASoA.h | 62 +++---- .../Core/include/Framework/AnalysisManagers.h | 6 - .../Core/include/Framework/AnalysisTask.h | 163 +++++++++--------- .../include/Framework/StructToTuple.h | 46 ++--- 4 files changed, 128 insertions(+), 149 deletions(-) diff --git a/Framework/Core/include/Framework/ASoA.h b/Framework/Core/include/Framework/ASoA.h index 4fd35e0dc5065..7586d6a6d3c63 100644 --- a/Framework/Core/include/Framework/ASoA.h +++ b/Framework/Core/include/Framework/ASoA.h @@ -405,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 @@ -1293,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(); @@ -2724,7 +2727,7 @@ consteval auto getIndexTargets() return !(*mColumnIterator).empty(); \ } \ \ - template \ + template \ auto _Getter_##_as() const \ { \ if (O2_BUILTIN_UNLIKELY(mBinding.ptr == nullptr)) { \ @@ -2734,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)) { \ @@ -2747,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 \ @@ -3090,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; \ } \ diff --git a/Framework/Core/include/Framework/AnalysisManagers.h b/Framework/Core/include/Framework/AnalysisManagers.h index fd41a079c6570..121ce7f4b4a77 100644 --- a/Framework/Core/include/Framework/AnalysisManagers.h +++ b/Framework/Core/include/Framework/AnalysisManagers.h @@ -534,12 +534,6 @@ void bindExternalIndicesPartition(P& partition, T*... tables) } /// Cache handling -template -bool preInitializeCache(InitContext&, T&) -{ - return false; -} - template bool initializeCache(ProcessingContext&, T&) { diff --git a/Framework/Core/include/Framework/AnalysisTask.h b/Framework/Core/include/Framework/AnalysisTask.h index 4f8a9e719e4b9..365c6b1d41692 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" @@ -63,17 +62,20 @@ 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>()) { Entry e{soa::getLabelFromTypeForKey>(key), soa::getMatcherFromTypeForKey>(key), key, enabled}; if constexpr (o2::soa::is_smallgroups>) { @@ -82,9 +84,9 @@ struct AnalysisDataProcessorBuilder { framework::updatePairList(bk, e); } } - }(), + }(bk, bku, enabled, key), ...); - }(framework::pack{}); + }(framework::pack{}, bk, bku, enabled); } template @@ -168,8 +170,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,37 +179,37 @@ 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&) { } @@ -282,51 +284,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 +348,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 +360,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 +377,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 +395,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 +524,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](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 +544,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 +588,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([&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/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_ From a979c459f80ce84473fcc430700f82e5f352f433 Mon Sep 17 00:00:00 2001 From: Ernst Hellbar Date: Fri, 16 Jan 2026 14:05:07 +0100 Subject: [PATCH 193/701] dpl-workflow.sh: enable ALPIDE_ERR_DUMPS by default in online physics runs --- prodtests/full-system-test/dpl-workflow.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/prodtests/full-system-test/dpl-workflow.sh b/prodtests/full-system-test/dpl-workflow.sh index f55605d1da485..f559fcdf91cf5 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= From fd54f4a12076c934e1359a0da6dffed4a0649578 Mon Sep 17 00:00:00 2001 From: ehellbar Date: Fri, 30 Jan 2026 11:03:46 +0100 Subject: [PATCH 194/701] DPL: fix device signpost segfaults for o2-dpl-raw-proxy (#15003) --- Framework/Core/src/DataProcessingDevice.cxx | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index fd03b7725d847..8a306c7b96001 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -1509,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; @@ -1523,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(); @@ -1546,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; } } @@ -1564,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)); @@ -1598,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); } } From 515ba3a699331dbd7a6c772ce8405ef49a492a99 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Fri, 30 Jan 2026 12:05:55 +0100 Subject: [PATCH 195/701] Revert "DPL Analysis: cleanup AnalysisTask.h and ASoA.h (#14996)" (#15005) This reverts commit 63e5c6136f9eb261968541ae80cc589d2c1b0a7f. --- Framework/Core/include/Framework/ASoA.h | 62 ++++--- .../Core/include/Framework/AnalysisManagers.h | 6 + .../Core/include/Framework/AnalysisTask.h | 163 +++++++++--------- .../include/Framework/StructToTuple.h | 46 +++-- 4 files changed, 149 insertions(+), 128 deletions(-) diff --git a/Framework/Core/include/Framework/ASoA.h b/Framework/Core/include/Framework/ASoA.h index 7586d6a6d3c63..4fd35e0dc5065 100644 --- a/Framework/Core/include/Framework/ASoA.h +++ b/Framework/Core/include/Framework/ASoA.h @@ -405,15 +405,15 @@ class Table; /// Type-checking index column binding struct Binding { void const* ptr = nullptr; - uint32_t hash = 0; - // std::span refs; + size_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 @@ -1293,9 +1293,6 @@ 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(); @@ -2727,7 +2724,7 @@ consteval auto getIndexTargets() return !(*mColumnIterator).empty(); \ } \ \ - template \ + template \ auto _Getter_##_as() const \ { \ if (O2_BUILTIN_UNLIKELY(mBinding.ptr == nullptr)) { \ @@ -2737,15 +2734,10 @@ consteval auto getIndexTargets() if (O2_BUILTIN_UNLIKELY(t == nullptr)) { \ o2::soa::dereferenceWithWrongType(#_Getter_, #_Table_); \ } \ - auto result = std::vector(); \ - result.reserve((*mColumnIterator).size()); \ - for (auto& i : *mColumnIterator) { \ - result.emplace_back(t->rawIteratorAt(i)); \ - } \ - return result; \ + return getIterators(); \ } \ \ - template \ + template \ auto filtered_##_Getter_##_as() const \ { \ if (O2_BUILTIN_UNLIKELY(mBinding.ptr == nullptr)) { \ @@ -2755,17 +2747,37 @@ consteval auto getIndexTargets() if (O2_BUILTIN_UNLIKELY(t == nullptr)) { \ o2::soa::dereferenceWithWrongType(#_Getter_, #_Table_); \ } \ - 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 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)); \ + } \ + } \ + return result; \ + } else { \ + static_assert(o2::framework::always_static_assert_v, "T is not a Filtered type"); \ + } \ + return {}; \ + } \ + \ auto _Getter_() const \ { \ return _Getter_##_as(); \ @@ -3078,9 +3090,15 @@ 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(t->rawIteratorAt(i)); \ + result.push_back(mBinding.get()->rawIteratorAt(i)); \ } \ return result; \ } \ diff --git a/Framework/Core/include/Framework/AnalysisManagers.h b/Framework/Core/include/Framework/AnalysisManagers.h index 121ce7f4b4a77..fd41a079c6570 100644 --- a/Framework/Core/include/Framework/AnalysisManagers.h +++ b/Framework/Core/include/Framework/AnalysisManagers.h @@ -534,6 +534,12 @@ void bindExternalIndicesPartition(P& partition, T*... tables) } /// Cache handling +template +bool preInitializeCache(InitContext&, T&) +{ + return false; +} + template bool initializeCache(ProcessingContext&, T&) { diff --git a/Framework/Core/include/Framework/AnalysisTask.h b/Framework/Core/include/Framework/AnalysisTask.h index 365c6b1d41692..4f8a9e719e4b9 100644 --- a/Framework/Core/include/Framework/AnalysisTask.h +++ b/Framework/Core/include/Framework/AnalysisTask.h @@ -22,6 +22,7 @@ #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" @@ -62,20 +63,17 @@ 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) { - [](framework::pack, Cache& bk, Cache& bku, bool enabled) { + [&bk, &bku, enabled](framework::pack) mutable { auto key = std::string{"fIndex"} + o2::framework::cutString(soa::getLabelFromType>()); - ([](Cache& bk, Cache& bku, bool enabled, std::string const& key) { + ([&bk, &bku, &key, enabled]() mutable { if constexpr (soa::relatedByIndex, std::decay_t>()) { Entry e{soa::getLabelFromTypeForKey>(key), soa::getMatcherFromTypeForKey>(key), key, enabled}; if constexpr (o2::soa::is_smallgroups>) { @@ -84,9 +82,9 @@ struct AnalysisDataProcessorBuilder { framework::updatePairList(bk, e); } } - }(bk, bku, enabled, key), + }(), ...); - }(framework::pack{}, bk, bku, enabled); + }(framework::pack{}); } template @@ -170,8 +168,8 @@ struct AnalysisDataProcessorBuilder { return true; } /// 1. enumeration (must be the only argument) - template - static void inputsFromArgs(void (C::*)(A), const char* /*name*/, bool /*value*/, std::vector& inputs, std::vector&) //, Cache&, Cache&) + template + static void inputsFromArgs(R (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. @@ -179,37 +177,37 @@ struct AnalysisDataProcessorBuilder { } /// 2. 1st argument is an iterator - template - static void inputsFromArgs(void (C::*)(A, Args...), const char* name, bool value, std::vector& inputs, std::vector& eInfos) //, Cache& bk, Cache& bku) + template + static void inputsFromArgs(R (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(void (C::*)(Args...), const char* name, bool value, std::vector& inputs, std::vector& eInfos) //, Cache&, Cache&) + template + static void inputsFromArgs(R (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(void (C::*)(A), bool, Cache&, Cache&) + template + static void cacheFromArgs(R (C::*)(A), bool, Cache&, Cache&) { } /// 2. iterator (the only grouping case) - template - static void cacheFromArgs(void (C::*)(A, Args...), bool value, Cache& bk, Cache& bku) + template + static void cacheFromArgs(R (C::*)(A, Args...), bool value, Cache& bk, Cache& bku) { addGroupingCandidates(bk, bku, value); } /// 3. generic case (no grouping) - template - static void cacheFromArgs(void (C::*)(A, Args...), bool, Cache&, Cache&) + template + static void cacheFromArgs(R (C::*)(A, Args...), bool, Cache&, Cache&) { } @@ -284,53 +282,51 @@ struct AnalysisDataProcessorBuilder { } } - template - static auto bindGroupingTable(InputRecord& record, void (C::*)(Grouping, Args...), std::vector& infos) + template + static auto bindGroupingTable(InputRecord& record, R (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, void (C::*)(Grouping, Args...), std::vector& infos) + template + static auto bindAssociatedTables(InputRecord& record, R (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, void (Task::*processingFunction)(Grouping, Associated...), std::vector& infos, ArrowTableSlicingCache& slices) + template + static void invokeProcess(Task& task, InputRecord& inputs, R (C::*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_sized([&groupingTable](auto& element) { + homogeneous_apply_refs([&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_sized([&groupingTable](auto& element) { + homogeneous_apply_refs([&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); @@ -348,7 +344,7 @@ struct AnalysisDataProcessorBuilder { // pre-bind self indices std::apply( [&task](auto&... t) mutable { - (homogeneous_apply_refs_sized( + (homogeneous_apply_refs( [&t](auto& p) { analysis_task_parsers::bindInternalIndicesPartition(p, &t); return true; @@ -360,12 +356,12 @@ struct AnalysisDataProcessorBuilder { auto binder = [&task, &groupingTable, &associatedTables](auto& x) mutable { x.bindExternalIndices(&groupingTable, &std::get>(associatedTables)...); - homogeneous_apply_refs_sized([&x](auto& t) mutable { + homogeneous_apply_refs([&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)...); @@ -377,11 +373,11 @@ struct AnalysisDataProcessorBuilder { associatedTables); // GroupedCombinations bound separately, as they should be set once for all associated tables - homogeneous_apply_refs_sized([&groupingTable, &associatedTables](auto& t) { + homogeneous_apply_refs([&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); @@ -395,28 +391,28 @@ struct AnalysisDataProcessorBuilder { associatedSlices); // bind partitions and grouping table - homogeneous_apply_refs_sized([&groupingTable](auto& x) { + homogeneous_apply_refs([&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_sized([&groupingTable](auto& x) { + homogeneous_apply_refs([&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)...); @@ -524,18 +520,16 @@ 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_sized([&options](auto& element) { return analysis_task_parsers::appendOption(options, element); }, *task.get()); + homogeneous_apply_refs([&options](auto& element) { return analysis_task_parsers::appendOption(options, element); }, *task.get()); /// extract conditions and append them as inputs - homogeneous_apply_refs_sized([&inputs](auto& element) { return analysis_task_parsers::appendCondition(inputs, element); }, *task.get()); + homogeneous_apply_refs([&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_sized( + homogeneous_apply_refs( [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); @@ -544,39 +538,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_sized([&inputs](auto& element) { + homogeneous_apply_refs([&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_sized([&outputs, &hash](auto& element) { return analysis_task_parsers::appendOutput(outputs, element, hash); }, *task.get()); + homogeneous_apply_refs([&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_sized([&requiredServices](auto& element) { return analysis_task_parsers::addService(requiredServices, element); }, *task.get()); + homogeneous_apply_refs([&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_sized([&bindingsKeys, &bindingsKeysUnsorted](auto& element) { return analysis_task_parsers::registerCache(element, bindingsKeys, bindingsKeysUnsorted); }, *task.get()); + homogeneous_apply_refs([&bindingsKeys, &bindingsKeysUnsorted](auto& element) { return analysis_task_parsers::registerCache(element, bindingsKeys, bindingsKeysUnsorted); }, *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()); + 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()); auto& callbacks = ic.services().get(); auto eoscb = [task](EndOfStreamContext& eosContext) { - homogeneous_apply_refs_sized([&eosContext](auto& element) { + homogeneous_apply_refs([&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); }; @@ -588,75 +582,84 @@ DataProcessorSpec adaptAnalysisTask(ConfigContext const& ctx, Args&&... args) } /// update configurables in filters and partitions - homogeneous_apply_refs_sized( + homogeneous_apply_refs( [&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_sized([&expressionInfos](auto& element) { + homogeneous_apply_refs([&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_sized( - [&bindingsKeys, &bindingsKeysUnsorted](auto& x) { + homogeneous_apply_refs( + [&bindingsKeys, &bindingsKeysUnsorted](auto& x) mutable { 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_sized([&pc](auto& element) { return analysis_task_parsers::newDataframeCondition(pc.inputs(), element); }, *task.get()); + homogeneous_apply_refs([&pc](auto& element) { return analysis_task_parsers::newDataframeCondition(pc.inputs(), element); }, *task.get()); // reset partitions once per dataframe - homogeneous_apply_refs_sized([](auto& element) { return analysis_task_parsers::newDataframePartition(element); }, *task.get()); + homogeneous_apply_refs([](auto& element) { return analysis_task_parsers::newDataframePartition(element); }, *task.get()); // reset selections for the next dataframe - std::ranges::for_each(expressionInfos, [](auto& info) { info.resetSelection = true; }); + for (auto& info : expressionInfos) { + info.resetSelection = true; + } // reset pre-slice for the next dataframe auto slices = pc.services().get(); - homogeneous_apply_refs_sized([&slices](auto& element) { + homogeneous_apply_refs([&slices](auto& element) { return analysis_task_parsers::updateSliceInfo(element, slices); }, - *(task.get())); + *(task.get())); // initialize local caches - homogeneous_apply_refs_sized([&pc](auto& element) { return analysis_task_parsers::initializeCache(pc, element); }, *(task.get())); + homogeneous_apply_refs([&pc](auto& element) { return analysis_task_parsers::initializeCache(pc, element); }, *(task.get())); // prepare outputs - homogeneous_apply_refs_sized([&pc](auto& element) { return analysis_task_parsers::prepareOutput(pc, element); }, *task.get()); + homogeneous_apply_refs([&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 { &T::process; }) { + if constexpr (requires { AnalysisDataProcessorBuilder::invokeProcess(*(task.get()), pc.inputs(), &T::process, expressionInfos, slices); }) { AnalysisDataProcessorBuilder::invokeProcess(*(task.get()), pc.inputs(), &T::process, expressionInfos, slices); } // execute optional process() - homogeneous_apply_refs_sized( - [&pc, &expressionInfos, &task, &slices](auto& x) { - if constexpr (is_process_configurable) { + homogeneous_apply_refs( + [&pc, &expressionInfos, &task, &slices](auto& x) mutable { + if constexpr (base_of_template>) { 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_sized([&pc](auto& element) { return analysis_task_parsers::prepareDelayedOutput(pc, element); }, *task.get()); + homogeneous_apply_refs([&pc](auto& element) { return analysis_task_parsers::prepareDelayedOutput(pc, element); }, *task.get()); // finalize outputs - homogeneous_apply_refs_sized([&pc](auto& element) { return analysis_task_parsers::finalizeOutput(pc, element); }, *task.get()); + homogeneous_apply_refs([&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/Foundation/include/Framework/StructToTuple.h b/Framework/Foundation/include/Framework/StructToTuple.h index 1c7aa62260bd3..5748329f6a50d 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, typename L> +template () / 10> requires(D == 9) -constexpr auto homogeneous_apply_refs(L l, T&& object) +auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -194,9 +194,9 @@ constexpr auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10, typename L> +template () / 10> requires(D == 8) -constexpr auto homogeneous_apply_refs(L l, T&& object) +auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -214,9 +214,9 @@ constexpr auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10, typename L> +template () / 10> requires(D == 7) -constexpr auto homogeneous_apply_refs(L l, T&& object) +auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -234,9 +234,9 @@ constexpr auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10, typename L> +template () / 10> requires(D == 6) -constexpr auto homogeneous_apply_refs(L l, T&& object) +auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -254,9 +254,9 @@ constexpr auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10, typename L> +template () / 10> requires(D == 5) -constexpr auto homogeneous_apply_refs(L l, T&& object) +auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -274,9 +274,9 @@ constexpr auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10, typename L> +template () / 10> requires(D == 4) -constexpr auto homogeneous_apply_refs(L l, T&& object) +auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -294,9 +294,9 @@ constexpr auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10, typename L> +template () / 10> requires(D == 3) -constexpr auto homogeneous_apply_refs(L l, T&& object) +auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -314,9 +314,9 @@ constexpr auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10, typename L> +template () / 10> requires(D == 2) -constexpr auto homogeneous_apply_refs(L l, T&& object) +auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -334,9 +334,9 @@ constexpr auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10, typename L> +template () / 10> requires(D == 1) -constexpr auto homogeneous_apply_refs(L l, T&& object) +auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -354,9 +354,9 @@ constexpr auto homogeneous_apply_refs(L l, T&& object) // clang-format on } -template () / 10, typename L> +template () / 10> requires(D == 0) -constexpr auto homogeneous_apply_refs(L l, T&& object) +auto homogeneous_apply_refs(L l, T&& object) { constexpr int numElements = nested_brace_constructible_size(); // clang-format off @@ -373,12 +373,6 @@ constexpr 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_ From dd70bca748eca6084e14c34d048c7ad4b79c120b Mon Sep 17 00:00:00 2001 From: Pavel Larionov Date: Fri, 30 Jan 2026 17:02:29 +0100 Subject: [PATCH 196/701] ALICE 3: add fully cylindrical IRIS, correct Si thickness, add v3 building (#14979) * VD layer sensitive silicon 20 um, 80 um non-sens * Add an option for pure cylindrical IRIS * add v3 building option for FT3 * build pure cyl IRIS v3 by default --- .../include/FT3Simulation/Detector.h | 1 + .../ALICE3/FT3/simulation/src/Detector.cxx | 61 +++- .../ALICE3/TRK/base/include/TRKBase/Specs.h | 4 +- .../include/TRKSimulation/VDGeometryBuilder.h | 8 +- .../include/TRKSimulation/VDLayer.h | 13 +- .../ALICE3/TRK/simulation/src/Detector.cxx | 2 +- .../TRK/simulation/src/VDGeometryBuilder.cxx | 307 ++++++++++++++---- .../ALICE3/TRK/simulation/src/VDLayer.cxx | 280 ++++++++++++++-- 8 files changed, 585 insertions(+), 91 deletions(-) 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/src/Detector.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx index aab8ae070d936..9303979ada930 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx @@ -346,6 +346,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() { @@ -411,7 +470,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) diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h index 95f9f9b00d7f3..c3c7de9dbe910 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h @@ -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 { diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDGeometryBuilder.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDGeometryBuilder.h index 0a2cb68f2233a..c337ddb102147 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDGeometryBuilder.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDGeometryBuilder.h @@ -24,9 +24,11 @@ namespace o2::trk // Each function builds one local petal assembly (walls + layers + disks) // and then places/rotates the petal once into the mother volume. -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 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); diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDLayer.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDLayer.h index 9e9ca2971bc3b..acf9b19342e4b 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDLayer.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDLayer.h @@ -39,9 +39,10 @@ class VDLayer 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 mModuleWidth{4.54f}; // cm + 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) }; @@ -54,6 +55,8 @@ class VDCylindricalLayer : public VDLayer 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: @@ -73,6 +76,8 @@ class VDRectangularLayer : public VDLayer 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: @@ -91,6 +96,8 @@ class VDDiskLayer : public VDLayer 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; } diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx index 0924be5fb6764..e0fc6ef1ed35b 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx @@ -266,7 +266,7 @@ void Detector::createGeometry() // Alternatives: createIRIS5Geometry(vTRK); createIRIS4aGeometry(vTRK); o2::trk::clearVDSensorRegistry(); - o2::trk::createIRIS4Geometry(vTRK); + o2::trk::createIRISGeometryFullCyl(vTRK); // Fill sensor names from registry right after geometry creation const auto& regs = o2::trk::vdSensorRegistry(); diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx index 5df875713262c..6ce04bb8443ef 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx @@ -67,6 +67,12 @@ inline bool isSolidToCut(const TGeoVolume* v) 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; } @@ -166,7 +172,34 @@ inline void buildPetalSolidsComposite(TGeoVolume* petalAsm) // Build the global cutout by rotating the petal-local composite n times with (p+0.5) phase inline void buildIrisCutoutFromPetalSolid(int nPetals) { - // Create n rotation transforms + 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); @@ -175,47 +208,23 @@ inline void buildIrisCutoutFromPetalSolid(int nPetals) 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()); - new TGeoCompositeShape("IRIS_CUTOUTsh", cutFormula.Data()); - // --- Sanity check: required matrices & shapes exist - auto* mats = gGeoManager ? gGeoManager->GetListOfMatrices() : nullptr; - auto* shps = gGeoManager ? gGeoManager->GetListOfShapes() : nullptr; + LOGP(info, "IRIS_CUTOUTsh formula: {}", cutFormula.Data()); + auto* cut = new TGeoCompositeShape("IRIS_CUTOUTsh", cutFormula.Data()); + (void)cut; - if (!mats || !shps) { - LOGP(error, "IRIS cutout sanity: gGeoManager not initialized properly (mats/shapes missing)."); + // 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 { - bool ok = true; - - // Check the petal rotations were registered and referenced - for (int p = 0; p < nPetals; ++p) { - const TString name = Form("IRIS_PETAL_ROT_%d", p); - if (!mats->FindObject(name)) { - LOGP(error, "IRIS cutout sanity: missing matrix {}", name.Data()); - ok = false; - } - } - - // Check that the local petal composite exists - if (!shps->FindObject("IRIS_PETAL_SOLIDSsh")) { - LOGP(error, "IRIS cutout sanity: shape 'IRIS_PETAL_SOLIDSsh' not found."); - ok = false; - } - - // Check that the global cutout shape was created - if (!shps->FindObject("IRIS_CUTOUTsh")) { - LOGP(error, "IRIS cutout sanity: shape 'IRIS_CUTOUTsh' not found."); - ok = false; - } - - if (ok) { - LOGP(info, "IRIS cutout sanity: OK ({} petals).", nPetals); - } + LOGP(info, "IRIS cutout sanity: OK ({} petals).", nPetals); } } @@ -299,7 +308,11 @@ inline TGeoCombiTrans rotZ(double phiDeg) // ============ 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) +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"); @@ -314,11 +327,21 @@ static void addPetalWalls(TGeoVolume* petalAsm, int nPetals, double outerRadius_ return; } - const double halfPhi = 0.5f * (360.f / static_cast(nPetals)); - const double halfZ = 0.5f * kPetalZ_cm; + const double halfZ = 0.5 * kPetalZ_cm; - // ---- Inner cylindrical wall (always at r=4.8 mm) ---- - { + // 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), @@ -330,8 +353,16 @@ static void addPetalWalls(TGeoVolume* petalAsm, int nPetals, double outerRadius_ petalAsm->AddNode(v, 1); } - // ---- Outer arc wall ---- - { + // ---- 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), @@ -343,6 +374,11 @@ static void addPetalWalls(TGeoVolume* petalAsm, int nPetals, double outerRadius_ 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), @@ -369,7 +405,7 @@ static void addPetalWalls(TGeoVolume* petalAsm, int nPetals, double outerRadius_ // 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) +static void addBarrelLayers(TGeoVolume* petalAsm, int nPetals, int petalID, bool rectangularL0, bool fullCylinders) { if (!petalAsm) { LOGP(error, "addBarrelLayers: petalAsm is null"); @@ -382,15 +418,15 @@ static void addBarrelLayers(TGeoVolume* petalAsm, int nPetals, int petalID, bool constexpr double arcL0_cm = 0.6247f; // 6.247 mm // φ spans - const double phiL0_deg = phiSpanFromGap(nPetals, gapL0_cm, rL0_cm); // L0 gap-defined - const double phiL1_deg = phiSpanFromGap(nPetals, gapL1L2_cm, rL1_cm); // L1 gap-defined - const double phiL2_deg = phiSpanFromGap(nPetals, gapL1L2_cm, rL2_cm); // L2 gap-defined + 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 (rectangularL0) { + if (!fullCylinders && rectangularL0) { VDRectangularLayer L0(0, nameL0, kX2X0, kL0RectWidth_cm, kLenZ_cm, kLenZ_cm); @@ -438,7 +474,7 @@ static void addBarrelLayers(TGeoVolume* petalAsm, int nPetals, int petalID, bool } // Build cold plate (cylindrical) in local coordinates, and add it to the petal assembly. -static void addColdPlate(TGeoVolume* petalAsm, int nPetals, int petalId) +static void addColdPlate(TGeoVolume* petalAsm, int nPetals, int petalId, bool fullCylinders = false) { if (!petalAsm) { LOGP(error, "addColdPlate: petalAsm is null"); @@ -455,8 +491,9 @@ static void addColdPlate(TGeoVolume* petalAsm, int nPetals, int petalId) constexpr double gapL1L2_cm = 0.12f; // 1.2 mm // φ spans - const double phiSpanColdplate_deg = phiSpanFromGap(nPetals, gapL1L2_cm, rL2_cm); // L2 gap-defined - const double halfPhiDeg = 0.5f * phiSpanColdplate_deg; + 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; @@ -625,7 +662,7 @@ static void addIRISServiceModulesSegmented(TGeoVolume* petalAsm, int nPetals) // 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) +static void addDisks(TGeoVolume* petalAsm, int nPetals, int petalID, bool fullCylinders) { if (!petalAsm) { @@ -633,7 +670,7 @@ static void addDisks(TGeoVolume* petalAsm, int nPetals, int petalID) return; } - const double phiDisk_deg = phiSpanFromGap(nPetals, 2 * kWallThick_cm, diskRin_cm); + 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 = @@ -651,21 +688,124 @@ static void addDisks(TGeoVolume* petalAsm, int nPetals, int petalID) } } +// 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) +static TGeoVolume* buildPetalAssembly(int nPetals, + int petalID, + bool rectangularL0, + bool fullCylinders, + bool withSideWalls) { auto* petalAsm = new TGeoVolumeAssembly(Form("PETAL_%d", petalID)); - addPetalWalls(petalAsm, nPetals, kOuterWallRadius_cm); - // Pass petalID to layers/disks for naming - addBarrelLayers(petalAsm, nPetals, petalID, rectangularL0); - addColdPlate(petalAsm, nPetals, petalID); - addDisks(petalAsm, nPetals, 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); + + 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) @@ -679,7 +819,9 @@ void createIRIS4Geometry(TGeoVolume* motherVolume) constexpr int nPetals = 4; for (int p = 0; p < nPetals; ++p) { - auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ false); + 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 @@ -704,7 +846,9 @@ void createIRIS5Geometry(TGeoVolume* motherVolume) constexpr int nPetals = 4; for (int p = 0; p < nPetals; ++p) { - auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ true); + 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 @@ -729,7 +873,9 @@ void createIRIS4aGeometry(TGeoVolume* motherVolume) constexpr int nPetals = 3; for (int p = 0; p < nPetals; ++p) { - auto* petal = buildPetalAssembly(nPetals, p, /*rectangularL0*/ false); + 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 @@ -743,9 +889,48 @@ void createIRIS4aGeometry(TGeoVolume* motherVolume) 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); + 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); diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx index 20f36f1f6f4e7..411dd485684b9 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx @@ -11,6 +11,7 @@ #include "TRKSimulation/VDLayer.h" #include "TRKBase/GeometryTGeo.h" +#include "TRKBase/Specs.h" #include "Framework/Logger.h" @@ -32,6 +33,8 @@ VDLayer::VDLayer(int layerNumber, const std::string& layerName, double layerX2X0 { 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 @@ -83,7 +86,7 @@ TGeoVolume* VDCylindricalLayer::createSensor() const } std::string sensName = Form("%s_%s%d", this->mLayerName.c_str(), GeometryTGeo::getTRKSensorPattern(), this->mLayerNumber); const double rIn = mRadius; - const double rOut = mRadius + mChipThickness; + 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); @@ -106,8 +109,8 @@ TGeoVolume* VDRectangularLayer::createSensor() const } 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 * mChipThickness; // thickness in Y - const double hz = 0.5 * mLengthSensZ; // <-- use sensor Z length, not full layer + 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); @@ -134,8 +137,8 @@ TGeoVolume* VDDiskLayer::createSensor() const return nullptr; } std::string sensName = Form("%s_%s%d", this->mLayerName.c_str(), GeometryTGeo::getTRKSensorPattern(), this->mLayerNumber); - const double halfThickness = 0.5 * mChipThickness; // disk thickness is along Z - const double halfPhi = 0.5 * mPhiSpanDeg; // degrees + 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); @@ -147,6 +150,243 @@ TGeoVolume* VDDiskLayer::createSensor() const 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 */ @@ -184,14 +424,14 @@ void VDCylindricalLayer::createLayer(TGeoVolume* motherVolume, TGeoMatrix* combi layerVol->SetLineColor(kYellow); layerVol->SetTransparency(30); - // Sensor volume (must use mLengthSensZ internally) - TGeoVolume* sensorVol = VDCylindricalLayer::createSensor(); - if (!sensorVol) { - LOGP(error, "VDCylindricalLayer::createSensor() returned null"); + // Chip volume (must use mLengthSensZ internally) + TGeoVolume* chipVol = VDCylindricalLayer::createChip(); + if (!chipVol) { + LOGP(error, "VDCylindricalLayer::createChip() returned null"); return; } - LOGP(debug, "Inserting {} in {} ", sensorVol->GetName(), layerVol->GetName()); - layerVol->AddNode(sensorVol, 1, nullptr); + 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) @@ -238,14 +478,14 @@ void VDRectangularLayer::createLayer(TGeoVolume* motherVolume, TGeoMatrix* combi layerVol->SetTransparency(30); // Sensor volume (uses mLengthSensZ internally) - TGeoVolume* sensorVol = VDRectangularLayer::createSensor(); - if (!sensorVol) { - LOGP(error, "VDRectangularLayer::createSensor() returned null"); + TGeoVolume* chipVol = VDRectangularLayer::createChip(); + if (!chipVol) { + LOGP(error, "VDRectangularLayer::chipVol() returned null"); return; } - LOGP(debug, "Inserting {} in {} ", sensorVol->GetName(), layerVol->GetName()); - layerVol->AddNode(sensorVol, 1, nullptr); + 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) @@ -292,14 +532,14 @@ void VDDiskLayer::createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans) layerVol->SetTransparency(30); // Sensor (same size & shape as the layer for disks) - TGeoVolume* sensorVol = VDDiskLayer::createSensor(); - if (!sensorVol) { - LOGP(error, "VDDiskLayer::createSensor() returned null"); + TGeoVolume* chipVol = VDDiskLayer::createChip(); + if (!chipVol) { + LOGP(error, "VDDiskLayer::createChip() returned null"); return; } // Insert single sensor (no Z-segmentation for disks) - layerVol->AddNode(sensorVol, 1, nullptr); + layerVol->AddNode(chipVol, 1, nullptr); TGeoTranslation tz(0.0, 0.0, mZPos); motherVolume->AddNode(layerVol, 1, combiTrans ? combiTrans : &tz); From 02a0aebb5718b3faba48c0aad82916e45e93621d Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sat, 31 Jan 2026 18:59:49 +0100 Subject: [PATCH 197/701] DPL: improve type_to_task_name function (#15006) - Out of line and avoid usage of stringstream. - Remove non-sense abbreviations --- Framework/Core/CMakeLists.txt | 2 + .../Core/include/Framework/AnalysisTask.h | 3 + Framework/Core/src/AnalysisTask.cxx | 77 +++++++++++++++++++ Framework/Core/test/test_TypeToTaskName.cxx | 65 ++++++++++++++++ .../include/Framework/TypeIdHelpers.h | 17 ---- 5 files changed, 147 insertions(+), 17 deletions(-) create mode 100644 Framework/Core/src/AnalysisTask.cxx create mode 100644 Framework/Core/test/test_TypeToTaskName.cxx diff --git a/Framework/Core/CMakeLists.txt b/Framework/Core/CMakeLists.txt index 1daba5dbc9798..7357167a3fcd8 100644 --- a/Framework/Core/CMakeLists.txt +++ b/Framework/Core/CMakeLists.txt @@ -16,6 +16,7 @@ o2_add_library(Framework src/ArrowTableSlicingCache.cxx src/AnalysisDataModel.cxx src/AnalysisSupportHelpers.cxx + src/AnalysisTask.cxx src/ASoA.cxx src/ASoAHelpers.cxx src/AsyncQueue.cxx @@ -254,6 +255,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 diff --git a/Framework/Core/include/Framework/AnalysisTask.h b/Framework/Core/include/Framework/AnalysisTask.h index 4f8a9e719e4b9..eb98d55cc24b2 100644 --- a/Framework/Core/include/Framework/AnalysisTask.h +++ b/Framework/Core/include/Framework/AnalysisTask.h @@ -37,6 +37,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 diff --git a/Framework/Core/src/AnalysisTask.cxx b/Framework/Core/src/AnalysisTask.cxx new file mode 100644 index 0000000000000..e88e6fbc6f041 --- /dev/null +++ b/Framework/Core/src/AnalysisTask.cxx @@ -0,0 +1,77 @@ +// 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[] = { + {"-h-m-p-i-d", "-hmpid"}, + {"-e-m-c-a-l", "-emcal"}, + {"-e-m-c", "-emc"}, + {"-i-t-s", "-its"}, + {"-t-p-c", "-tpc"}, + {"-q-c-d", "-qcd"}, + {"-t-o-f", "-tof"}, + {"-t-r-d", "-trd"}, + {"-f-v0", "-fv0"}, + {"-q-a", "-qa"}, + {"-b-c", "-bc"}, + {"-q-c", "-qc"}}; + + 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/test/test_TypeToTaskName.cxx b/Framework/Core/test/test_TypeToTaskName.cxx new file mode 100644 index 0000000000000..b7b440b13ecfd --- /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/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_ From dee4e246302c2a78b02fe2d4b7a29d0f48d2b05f Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Mon, 2 Feb 2026 13:16:21 +0100 Subject: [PATCH 198/701] Revert abbreviations until we get green light from the affected people (#15009) --- Framework/Core/src/AnalysisTask.cxx | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/Framework/Core/src/AnalysisTask.cxx b/Framework/Core/src/AnalysisTask.cxx index e88e6fbc6f041..993c597a95f49 100644 --- a/Framework/Core/src/AnalysisTask.cxx +++ b/Framework/Core/src/AnalysisTask.cxx @@ -34,18 +34,9 @@ std::string type_to_task_name(std::string_view const& camelCase) std::string_view pattern; std::string_view replacement; } abbreviations[] = { - {"-h-m-p-i-d", "-hmpid"}, {"-e-m-c-a-l", "-emcal"}, - {"-e-m-c", "-emc"}, - {"-i-t-s", "-its"}, - {"-t-p-c", "-tpc"}, - {"-q-c-d", "-qcd"}, - {"-t-o-f", "-tof"}, - {"-t-r-d", "-trd"}, - {"-f-v0", "-fv0"}, - {"-q-a", "-qa"}, - {"-b-c", "-bc"}, - {"-q-c", "-qc"}}; + {"-e-m-c", "-emc"} + }; std::string consolidated; consolidated.reserve(result.size()); From 597fc9ee8673ea2bb916c6caf5bbbec8f631a224 Mon Sep 17 00:00:00 2001 From: shahoian Date: Wed, 28 Jan 2026 20:42:39 +0100 Subject: [PATCH 199/701] Leave single implementation of TRD RecoParam, init from GPUSettingsRecTRD --- .../include/Align/AlignableDetectorTRD.h | 4 +- Detectors/Align/src/AlignableDetectorTRD.cxx | 11 +- .../include/SpacePoints/TrackInterpolation.h | 4 +- .../SpacePoints/src/TrackInterpolation.cxx | 4 +- Detectors/TRD/base/CMakeLists.txt | 2 - .../TRD/base/include/TRDBase/RecoParam.h | 64 ----------- Detectors/TRD/base/src/RecoParam.cxx | 64 ----------- Detectors/TRD/base/src/TRDBaseLinkDef.h | 1 - Detectors/TRD/calibration/CMakeLists.txt | 1 + .../include/TRDCalibration/TrackBasedCalib.h | 4 +- .../TRD/calibration/src/TrackBasedCalib.cxx | 5 +- Detectors/TRD/qc/CMakeLists.txt | 1 + Detectors/TRD/qc/include/TRDQC/Tracking.h | 4 +- Detectors/TRD/qc/src/Tracking.cxx | 5 +- .../TRDWorkflow/TRDGlobalTrackingSpec.h | 4 +- .../workflow/src/TRDGlobalTrackingSpec.cxx | 6 +- GPU/GPUTracking/Base/GPUConstantMem.h | 1 - GPU/GPUTracking/CMakeLists.txt | 3 +- GPU/GPUTracking/DataTypes/GPUDataTypesIO.h | 3 +- GPU/GPUTracking/DataTypes/GPUTRDRecoParam.cxx | 100 ++++++++++++++++++ GPU/GPUTracking/DataTypes/GPUTRDRecoParam.h | 84 +++++++++++++++ GPU/GPUTracking/GPUTrackingLinkDef_O2.h | 1 + GPU/GPUTracking/Global/GPUChainTracking.cxx | 13 +++ GPU/GPUTracking/Global/GPUChainTracking.h | 5 + .../Global/GPUChainTrackingGetters.inc | 2 + GPU/GPUTracking/Global/GPUChainTrackingIO.cxx | 10 ++ GPU/GPUTracking/TRDTracking/GPUTRDTracker.cxx | 48 ++------- GPU/GPUTracking/TRDTracking/GPUTRDTracker.h | 15 +-- .../TRDTracking/macros/run_trd_tracker.C | 4 +- .../include/GPUWorkflow/GPUWorkflowSpec.h | 3 + GPU/Workflow/src/GPUWorkflowSpec.cxx | 27 +++-- 31 files changed, 286 insertions(+), 217 deletions(-) delete mode 100644 Detectors/TRD/base/include/TRDBase/RecoParam.h delete mode 100644 Detectors/TRD/base/src/RecoParam.cxx create mode 100644 GPU/GPUTracking/DataTypes/GPUTRDRecoParam.cxx create mode 100644 GPU/GPUTracking/DataTypes/GPUTRDRecoParam.h 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/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/TPC/calibration/SpacePoints/include/SpacePoints/TrackInterpolation.h b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackInterpolation.h index e7d0fb197ea42..58627250d815e 100644 --- a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackInterpolation.h +++ b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackInterpolation.h @@ -39,7 +39,7 @@ #include "TPCReconstruction/TPCFastTransformHelperO2.h" #include "DetectorsBase/Propagator.h" #include "DataFormatsGlobalTracking/RecoContainer.h" -#include "TRDBase/RecoParam.h" +#include "GPUTRDRecoParam.h" #include "TRDBase/Geometry.h" class TTree; @@ -441,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/TrackInterpolation.cxx b/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx index 6c37be9ddc1b1..cd5e3960160a6 100644 --- a/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx +++ b/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx @@ -126,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(); 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/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/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/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/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/workflow/include/TRDWorkflow/TRDGlobalTrackingSpec.h b/Detectors/TRD/workflow/include/TRDWorkflow/TRDGlobalTrackingSpec.h index 9f3b9b27d37b5..93f07dd58445e 100644 --- a/Detectors/TRD/workflow/include/TRDWorkflow/TRDGlobalTrackingSpec.h +++ b/Detectors/TRD/workflow/include/TRDWorkflow/TRDGlobalTrackingSpec.h @@ -20,6 +20,7 @@ #include "TRDBase/GeometryFlat.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/src/TRDGlobalTrackingSpec.cxx b/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx index 9588888df5fc6..f2d4aad829fe5 100644 --- a/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx +++ b/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx @@ -43,6 +43,7 @@ // GPU header #include "GPUReconstruction.h" #include "GPUChainTracking.h" +#include "GPUChainTrackingGetters.inc" #include "GPUO2InterfaceConfiguration.h" #include "GPUO2InterfaceUtils.h" #include "GPUSettings.h" @@ -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/GPU/GPUTracking/Base/GPUConstantMem.h b/GPU/GPUTracking/Base/GPUConstantMem.h index efb83a7e874c8..94ccfa7fa6db9 100644 --- a/GPU/GPUTracking/Base/GPUConstantMem.h +++ b/GPU/GPUTracking/Base/GPUConstantMem.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/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index 6a60eb9edd6d0..9e9344108ccfb 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -59,6 +59,7 @@ set(SRCS Merger/GPUTPCGMPhysicalTrackModel.cxx Merger/GPUTPCGMPolynomialFieldManager.cxx DataTypes/GPUTRDTrack.cxx + DataTypes/GPUTRDRecoParam.cxx TRDTracking/GPUTRDTracker.cxx TRDTracking/GPUTRDTrackletWord.cxx TRDTracking/GPUTRDTrackerKernels.cxx @@ -68,7 +69,7 @@ set(SRCS_DATATYPES DataTypes/GPUDataTypesConfig.cxx DataTypes/GPUConfigDump.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/GPUDataTypesIO.h DataTypes/GPUDataTypesConfig.h DataTypes/GPUDataTypesQA.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 diff --git a/GPU/GPUTracking/DataTypes/GPUDataTypesIO.h b/GPU/GPUTracking/DataTypes/GPUDataTypesIO.h index fd98cba1dadaa..76fa569a16824 100644 --- a/GPU/GPUTracking/DataTypes/GPUDataTypesIO.h +++ b/GPU/GPUTracking/DataTypes/GPUDataTypesIO.h @@ -95,6 +95,7 @@ class TPCFastTransform; struct TPCPadGainCalib; struct TPCZSLinkMapping; +class GPUTRDRecoParam; class GPUTPCTrack; class GPUTPCHitId; class GPUTPCGMMergedTrack; @@ -135,7 +136,7 @@ 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}; }; 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/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/Global/GPUChainTracking.cxx b/GPU/GPUTracking/Global/GPUChainTracking.cxx index 5c951053e155b..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" @@ -435,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)); } @@ -536,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(). @@ -602,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; diff --git a/GPU/GPUTracking/Global/GPUChainTracking.h b/GPU/GPUTracking/Global/GPUChainTracking.h index 2dd1ece856ecf..fd75136f51d76 100644 --- a/GPU/GPUTracking/Global/GPUChainTracking.h +++ b/GPU/GPUTracking/Global/GPUChainTracking.h @@ -59,6 +59,7 @@ class GPUDisplayInterface; class GPUQA; class GPUTPCClusterStatistics; class GPUTRDGeometry; +class GPUTRDRecoParam; class TPCFastTransform; class GPUTrackingInputProvider; struct GPUChainTrackingFinalContext; @@ -178,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); @@ -267,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; 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/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/macros/run_trd_tracker.C b/GPU/GPUTracking/TRDTracking/macros/run_trd_tracker.C index acfcf92370b00..e4b37500e1a60 100644 --- a/GPU/GPUTracking/TRDTracking/macros/run_trd_tracker.C +++ b/GPU/GPUTracking/TRDTracking/macros/run_trd_tracker.C @@ -24,6 +24,7 @@ #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 @@ -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/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h b/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h index d610269abca81..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; @@ -212,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; @@ -245,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/GPUWorkflowSpec.cxx b/GPU/Workflow/src/GPUWorkflowSpec.cxx index 7b1db436dbf7e..a8f95841a4dc9 100644 --- a/GPU/Workflow/src/GPUWorkflowSpec.cxx +++ b/GPU/Workflow/src/GPUWorkflowSpec.cxx @@ -73,6 +73,7 @@ #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" @@ -273,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; @@ -1059,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; From 8e1f22798d5e2ccfd03ace4ea07c45a18845e3e2 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Tue, 3 Feb 2026 10:48:58 +0100 Subject: [PATCH 200/701] Disable tests for reverted exceptions (#15011) --- Framework/Core/test/test_TypeToTaskName.cxx | 60 ++++++++++----------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/Framework/Core/test/test_TypeToTaskName.cxx b/Framework/Core/test/test_TypeToTaskName.cxx index b7b440b13ecfd..af51bc04613f5 100644 --- a/Framework/Core/test/test_TypeToTaskName.cxx +++ b/Framework/Core/test/test_TypeToTaskName.cxx @@ -18,48 +18,48 @@ 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")); +// 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("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"); +// 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_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("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"); +// 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"); -} +//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"); +//} From ee2b995e2450fb5b6a5314b5eca18969e5d260c6 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Tue, 3 Feb 2026 14:01:01 +0100 Subject: [PATCH 201/701] Brown paperbag issue with reverted feature. (#15012) --- Framework/Core/test/test_TypeToTaskName.cxx | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Framework/Core/test/test_TypeToTaskName.cxx b/Framework/Core/test/test_TypeToTaskName.cxx index af51bc04613f5..cd5a359db0446 100644 --- a/Framework/Core/test/test_TypeToTaskName.cxx +++ b/Framework/Core/test/test_TypeToTaskName.cxx @@ -25,10 +25,10 @@ TEST_CASE("TypeIdHelpers_BasicConversion") TEST_CASE("TypeIdHelpers_AbbreviationConsolidation") { - // Test ALICE detector abbreviations +// 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("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"); @@ -42,13 +42,13 @@ TEST_CASE("TypeIdHelpers_AbbreviationConsolidation") // REQUIRE(type_to_task_name(std::string_view("QCDAnalysis")) == "qcd-analysis"); //} -TEST_CASE("TypeIdHelpers_ComplexNames") -{ - // Test complex combinations +//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("TPCEMCQCTask")) == "tpc-emc-qc-task"); // REQUIRE(type_to_task_name(std::string_view("MyITSTask")) == "my-its-task"); -} +//} //TEST_CASE("TypeIdHelpers_EdgeCases") //{ From ff39f95db1067234eac4ab9dd3681ecba15949bd Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Tue, 3 Feb 2026 15:33:50 +0100 Subject: [PATCH 202/701] Fix and improve TPC Loopers implementation --- Generators/include/Generators/Generator.h | 12 +++++------ Generators/include/Generators/TPCLoopers.h | 2 -- Generators/src/Generator.cxx | 23 ++++++++++++++++++++-- 3 files changed, 26 insertions(+), 11 deletions(-) diff --git a/Generators/include/Generators/Generator.h b/Generators/include/Generators/Generator.h index 3484601aa42bb..f413aeccfa3ab 100644 --- a/Generators/include/Generators/Generator.h +++ b/Generators/include/Generators/Generator.h @@ -17,10 +17,6 @@ #include "FairGenerator.h" #include "TParticle.h" #include "Generators/Trigger.h" -#ifdef GENERATORS_WITH_TPCLOOPERS -#include "Generators/TPCLoopers.h" -#include "Generators/TPCLoopersParam.h" -#endif #include #include #include @@ -38,6 +34,8 @@ namespace o2 namespace eventgen { +class GenTPCLoopers; // Forward declaration + /*****************************************************************/ /*****************************************************************/ @@ -60,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; @@ -169,9 +167,9 @@ class Generator : public FairGenerator // global static information about (upper limit of) number of events to be generated static unsigned int gTotalNEvents; -#ifdef GENERATORS_WITH_TPCLOOPERS // Loopers generator instance - std::unique_ptr mTPCLoopersGen = nullptr; + o2::eventgen::GenTPCLoopers* mTPCLoopersGen = nullptr; +#ifdef GENERATORS_WITH_TPCLOOPERS bool initTPCLoopersGen(); #endif diff --git a/Generators/include/Generators/TPCLoopers.h b/Generators/include/Generators/TPCLoopers.h index 6a1d3ef262e22..a144a947fc11b 100644 --- a/Generators/include/Generators/TPCLoopers.h +++ b/Generators/include/Generators/TPCLoopers.h @@ -16,14 +16,12 @@ #ifdef GENERATORS_WITH_TPCLOOPERS #include -#endif #include #include #include "TRandom3.h" #include #include "TParticle.h" -#ifdef GENERATORS_WITH_TPCLOOPERS // Static Ort::Env instance for multiple onnx model loading extern Ort::Env global_env; diff --git a/Generators/src/Generator.cxx b/Generators/src/Generator.cxx index 465a8ffb7ee22..ecea311c94de7 100644 --- a/Generators/src/Generator.cxx +++ b/Generators/src/Generator.cxx @@ -27,6 +27,10 @@ #include "TGrid.h" #include "CCDB/BasicCCDBManager.h" #include +#ifdef GENERATORS_WITH_TPCLOOPERS +#include "Generators/TPCLoopers.h" +#include "Generators/TPCLoopersParam.h" +#endif namespace o2 { @@ -94,6 +98,19 @@ Generator::Generator(const Char_t* name, const Char_t* title) : FairGenerator(na #endif } +/*****************************************************************/ + +Generator::~Generator() +{ + /** destructor **/ +#ifdef GENERATORS_WITH_TPCLOOPERS + if (mTPCLoopersGen) { + delete mTPCLoopersGen; + mTPCLoopersGen = nullptr; + } +#endif +} + /*****************************************************************/ #ifdef GENERATORS_WITH_TPCLOOPERS bool Generator::initTPCLoopersGen() @@ -171,7 +188,7 @@ bool Generator::initTPCLoopersGen() nclxrate = isAlien[2] || isCCDB[2] ? local_names[2] : nclxrate; try { // Create the TPC loopers generator with the provided parameters - mTPCLoopersGen = std::make_unique(model_pairs, model_compton, poisson, gauss, scaler_pair, scaler_compton); + 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 @@ -188,7 +205,9 @@ bool Generator::initTPCLoopersGen() LOG(info) << "TPC Loopers generator initialized successfully"; } catch (const std::exception& e) { LOG(error) << "Failed to initialize TPC Loopers generator: " << e.what(); - mTPCLoopersGen.reset(); + delete mTPCLoopersGen; + mTPCLoopersGen = nullptr; + return kFALSE; } return kTRUE; } From 71634e3b6983e1e54596784828b6fcb145261c9f Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 5 Feb 2026 09:20:34 +0100 Subject: [PATCH 203/701] DPL: Improve message when we do not have enough resources to process. (#15016) --- Framework/Core/src/DataProcessingDevice.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index 8a306c7b96001..ccfb58db7559a 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -1399,12 +1399,12 @@ void DataProcessingDevice::Run() 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.", + "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.", + "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()); } From bfa44ca0e7a6b8d42cca3ac93f0b7a423869c5a0 Mon Sep 17 00:00:00 2001 From: Gabriele Cimador Date: Fri, 19 Dec 2025 10:59:27 +0100 Subject: [PATCH 204/701] GPU Framework: remove GPUDefParametersDefaults.h and automatically generate GPU parameters using json file and CMake --- .../ITS/tracking/GPU/cuda/CMakeLists.txt | 2 +- GPU/GPUTracking/Base/cuda/CMakeLists.txt | 4 +- GPU/GPUTracking/Base/hip/CMakeLists.txt | 4 +- GPU/GPUTracking/CMakeLists.txt | 21 +- .../Definitions/.clang-format-ignore | 1 + .../Definitions/GPUDefParametersDefaults.h | 589 ------------------ .../Definitions/GPUParameters.json | 582 +++++++++++++++++ GPU/GPUTracking/Definitions/GPUSettingsList.h | 2 +- .../cmake/generateGPUParamHeader.cmake | 37 ++ .../cmake/gpu_param_header_generator.cmake | 105 ++++ GPU/documentation/build-O2.md | 2 +- dependencies/FindO2GPU.cmake | 43 +- log.txt | 0 13 files changed, 777 insertions(+), 615 deletions(-) create mode 100644 GPU/GPUTracking/Definitions/.clang-format-ignore delete mode 100644 GPU/GPUTracking/Definitions/GPUDefParametersDefaults.h create mode 100644 GPU/GPUTracking/Definitions/GPUParameters.json create mode 100644 GPU/GPUTracking/cmake/generateGPUParamHeader.cmake create mode 100644 GPU/GPUTracking/cmake/gpu_param_header_generator.cmake create mode 100644 log.txt 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/GPU/GPUTracking/Base/cuda/CMakeLists.txt b/GPU/GPUTracking/Base/cuda/CMakeLists.txt index 05ed091eb83ea..226bacbf88157 100644 --- a/GPU/GPUTracking/Base/cuda/CMakeLists.txt +++ b/GPU/GPUTracking/Base/cuda/CMakeLists.txt @@ -74,7 +74,7 @@ add_custom_command( COMMAND cat ${GPUDIR}/Base/GPUStdSystemHeaders.h >> ${GPU_RTC_BIN}.src COMMAND ${CMAKE_CUDA_COMPILER} ${GPU_RTC_DEFINES} ${GPU_RTC_INCLUDES} -std=c++${CMAKE_CUDA_STANDARD} -D__CUDA_ARCH__=${RTC_CUDA_ARCH} -Wno-deprecated-gpu-targets -D__CUDACC__ -x c++ -M -MD -MT ${GPU_RTC_BIN}.src -MF ${GPU_RTC_BIN}.src.d ${GPU_RTC_SRC} COMMAND ${CMAKE_CUDA_COMPILER} ${GPU_RTC_DEFINES} ${GPU_RTC_INCLUDES} -std=c++${CMAKE_CUDA_STANDARD} -D__CUDA_ARCH__=${RTC_CUDA_ARCH} -Wno-deprecated-gpu-targets -D__CUDACC__ -x c++ -E -Xcompiler "-nostdinc -P" ${GPU_RTC_SRC} >> ${GPU_RTC_BIN}.src - DEPENDS ${GPU_RTC_SRC} ${GPUDIR}/Base/GPUStdSystemHeaders.h ${GPUDIR}/Base/cuda/GPUReconstructionCUDAIncludesSystem.h ${GPUDIR}/Base/GPUStdSystemHeaders.h + DEPENDS ${GPU_RTC_SRC} ${GPUDIR}/Base/GPUStdSystemHeaders.h ${GPUDIR}/Base/cuda/GPUReconstructionCUDAIncludesSystem.h ${GPUDIR}/Base/GPUStdSystemHeaders.h GPU_PARAM_HEADER_AUTO_ALL DEPFILE ${GPU_RTC_BIN}.src.d COMMAND_EXPAND_LISTS COMMENT "Preparing CUDA RTC source file ${GPU_RTC_BIN}.src" @@ -149,7 +149,7 @@ endif() # Setting target architecture and adding GPU libraries target_link_libraries(${targetName} PRIVATE cuda cudart) -set_target_cuda_arch(${targetName}) +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/hip/CMakeLists.txt b/GPU/GPUTracking/Base/hip/CMakeLists.txt index 501509d8dfcf6..d148e376abca9 100644 --- a/GPU/GPUTracking/Base/hip/CMakeLists.txt +++ b/GPU/GPUTracking/Base/hip/CMakeLists.txt @@ -125,7 +125,7 @@ add_custom_command( COMMAND cat ${GPUDIR}/Base/hip/GPUReconstructionHIPIncludesSystem.h | grep -v GPUStdSystemHeaders.h >> ${GPU_RTC_BIN}.src COMMAND cat ${GPUDIR}/Base/GPUStdSystemHeaders.h >> ${GPU_RTC_BIN}.src COMMAND ${CMAKE_HIP_COMPILER} ${GPU_RTC_DEFINES} ${GPU_RTC_INCLUDES} -std=c++${CMAKE_HIP_STANDARD} -D__HIPCC__ -D__HIP_DEVICE_COMPILE__ -x c++ -nostdinc -E -P ${GPU_RTC_SRC} -MD -MT ${GPU_RTC_BIN}.src -MF ${GPU_RTC_BIN}.src.d >> ${GPU_RTC_BIN}.src - DEPENDS ${GPU_RTC_SRC} ${GPUDIR}/Base/GPUStdSystemHeaders.h ${GPUDIR}/Base/hip/GPUReconstructionHIPIncludesSystem.h ${GPUDIR}/Base/GPUStdSystemHeaders.h ${MODULE}_HIPIFIED + DEPENDS ${GPU_RTC_SRC} ${GPUDIR}/Base/GPUStdSystemHeaders.h ${GPUDIR}/Base/hip/GPUReconstructionHIPIncludesSystem.h ${GPUDIR}/Base/GPUStdSystemHeaders.h ${MODULE}_HIPIFIED GPU_PARAM_HEADER_AUTO_ALL DEPFILE ${GPU_RTC_BIN}.src.d COMMAND_EXPAND_LISTS COMMENT "Preparing HIP RTC source file ${GPU_RTC_BIN}.src" @@ -219,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/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index 9e9344108ccfb..a2d91b6ed4c5e 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -107,6 +107,12 @@ set(SRCS_NO_H SectorTracker/GPUTPCTrackerDump.cxx Global/GPUChainTrackingDebugAndProfiling.cxx Global/GPUChainTrackingIO.cxx) +set(ON_THE_FLY_DIR ${CMAKE_CURRENT_BINARY_DIR}/include_gpu_onthefly) +file(MAKE_DIRECTORY ${ON_THE_FLY_DIR}) +include(cmake/generateGPUParamHeader.cmake) +set(GPU_DEFAULT_PARAMS_HEADER ${ON_THE_FLY_DIR}/GPUDefParametersDefaults.h) +generate_gpu_param_header("AUTO" ${GPU_DEFAULT_PARAMS_HEADER}) # generate header with default GPU parameters, arch selected by CMake variables + set(HDRS_INSTALL ${HDRS_CINT_O2} ${HDRS_CINT_DATATYPES} @@ -135,9 +141,9 @@ set(HDRS_INSTALL DataTypes/GPUO2ExternalUser.h Debug/GPUROOTDump.h Definitions/GPUDefConstantsAndSettings.h + ${GPU_DEFAULT_PARAMS_HEADER} Definitions/GPUDefParametersWrapper.h Definitions/GPUDefParametersConstants.h - Definitions/GPUDefParametersDefaults.h Definitions/GPUDef.h Definitions/GPUDefMacros.h Definitions/GPULogging.h @@ -239,8 +245,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}) @@ -286,6 +290,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 +307,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 @@ -409,15 +414,17 @@ set(GPU_CONST_PARAM_ARCHITECTUES AMPERE TURING VEGA MI100) 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) + set(GPU_ARCH_PARAMS_HEADER ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch/GPUDefParametersDefaults_${GPU_ARCH}.h) + generate_gpu_param_header(${GPU_ARCH} ${GPU_ARCH_PARAMS_HEADER}) 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'" + "echo -e '#define GPUCA_GPUTYPE_${GPU_ARCH}\\n#define PARAMETER_FILE \"${GPU_ARCH_PARAMS_HEADER}\"\\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 + DEPENDS ${GPU_ARCH_PARAMS_HEADER} ${ON_THE_FLY_DIR}/GPUDefParametersLoadPrepare.h ${ON_THE_FLY_DIR}/GPUDefParametersLoad.inc COMMENT "Generating GPU parameter set for architecture ${GPU_ARCH}") diff --git a/GPU/GPUTracking/Definitions/.clang-format-ignore b/GPU/GPUTracking/Definitions/.clang-format-ignore new file mode 100644 index 0000000000000..5ffee2498bd7e --- /dev/null +++ b/GPU/GPUTracking/Definitions/.clang-format-ignore @@ -0,0 +1 @@ +GPUParameters.json diff --git a/GPU/GPUTracking/Definitions/GPUDefParametersDefaults.h b/GPU/GPUTracking/Definitions/GPUDefParametersDefaults.h deleted file mode 100644 index 1be881ee6323e..0000000000000 --- a/GPU/GPUTracking/Definitions/GPUDefParametersDefaults.h +++ /dev/null @@ -1,589 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not 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_MI100) - #define GPUCA_WARP_SIZE 64 - #define GPUCA_PAR_AMD_EUS_PER_CU 4 - #define GPUCA_THREAD_COUNT_DEFAULT 256 - #define GPUCA_LB_GPUTPCCreateTrackingData 256, 7 - #define GPUCA_LB_GPUTPCStartHitsSorter 1024, 5 - #define GPUCA_LB_GPUTPCStartHitsFinder 1024, 2 - #define GPUCA_LB_GPUTPCTrackletConstructor 768, 8 - #define GPUCA_LB_GPUTPCTrackletSelector 384, 5 - #define GPUCA_LB_GPUTPCNeighboursFinder 192, 8 - #define GPUCA_LB_GPUTPCNeighboursCleaner 128, 5 - #define GPUCA_LB_GPUTPCExtrapolationTracking 256, 7 - #define GPUCA_LB_GPUTPCCFDecodeZS 64, 4 - #define GPUCA_LB_GPUTPCCFDecodeZSLink GPUCA_WARP_SIZE - #define GPUCA_LB_GPUTPCCFDecodeZSDenseLink GPUCA_WARP_SIZE, 4 - #define GPUCA_LB_GPUTPCCFGather 1024, 5 - #define GPUCA_LB_GPUTPCGMMergerTrackFit 192, 2 - #define GPUCA_LB_GPUTPCGMMergerFollowLoopers 256, 5 - #define GPUCA_LB_GPUTPCGMMergerSectorRefit 64, 4 - #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 768, 1 - #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, 1 - #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 576, 2 - #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillIndexMap 512 - #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillFromDigits 512 - #define GPUCA_LB_GPUTPCCFChargeMapFiller_findFragmentStart 512 - #define GPUCA_LB_GPUTPCCFPeakFinder 512, 9 - #define GPUCA_LB_GPUTPCCFNoiseSuppression 512 - #define GPUCA_LB_GPUTPCCFDeconvolution 512, 5 - #define GPUCA_LB_GPUTPCCFClusterizer 448, 3 - #define GPUCA_LB_COMPRESSION_GATHER 1024 - #define GPUCA_PAR_NEIGHBOURS_FINDER_MAX_NNEIGHUP 10 - #define PAR_NEIGHBOURS_FINDER_UNROLL_GLOBAL 4 - #define GPUCA_PAR_NEIGHBOURS_FINDER_UNROLL_SHARED 0 - #define GPUCA_PAR_TRACKLET_SELECTOR_HITS_REG_SIZE 9 - #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_PAR_AMD_EUS_PER_CU 4 - #define GPUCA_THREAD_COUNT_DEFAULT 256 - #define GPUCA_LB_GPUTPCCreateTrackingData 192, 2 - #define GPUCA_LB_GPUTPCStartHitsSorter 512, 7 - #define GPUCA_LB_GPUTPCStartHitsFinder 1024, 7 - #define GPUCA_LB_GPUTPCTrackletConstructor 512, 10 - #define GPUCA_LB_GPUTPCTrackletSelector 192, 10 - #define GPUCA_LB_GPUTPCNeighboursFinder 960, 8 - #define GPUCA_LB_GPUTPCNeighboursCleaner 384, 9 - #define GPUCA_LB_GPUTPCExtrapolationTracking 256, 2 - #define GPUCA_LB_GPUTPCCFDecodeZS 64, 1 - #define GPUCA_LB_GPUTPCCFDecodeZSLink GPUCA_WARP_SIZE - #define GPUCA_LB_GPUTPCCFDecodeZSDenseLink GPUCA_WARP_SIZE, 14 - #define GPUCA_LB_GPUTPCCFGather 1024, 1 - #define GPUCA_LB_GPUTPCGMMergerTrackFit 64, 7 - #define GPUCA_LB_GPUTPCGMMergerFollowLoopers 256, 4 - #define GPUCA_LB_GPUTPCGMMergerSectorRefit 256, 2 - #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 1024, 1 - #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 576, 2 - #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillIndexMap 512 - #define GPUCA_LB_GPUTPCCFChargeMapFiller_fillFromDigits 512 - #define GPUCA_LB_GPUTPCCFChargeMapFiller_findFragmentStart 512 - #define GPUCA_LB_GPUTPCCFPeakFinder 512, 4 - #define GPUCA_LB_GPUTPCCFNoiseSuppression 512 - #define GPUCA_LB_GPUTPCCFDeconvolution 512, 5 - #define GPUCA_LB_GPUTPCCFClusterizer 512, 2 - #define GPUCA_LB_COMPRESSION_GATHER 1024 - #define GPUCA_PAR_NEIGHBOURS_FINDER_MAX_NNEIGHUP 4 - #define GPUCA_PAR_NEIGHBOURS_FINDER_UNROLL_GLOBAL 2 - #define GPUCA_PAR_NEIGHBOURS_FINDER_UNROLL_SHARED 0 - #define GPUCA_PAR_TRACKLET_SELECTOR_HITS_REG_SIZE 27 - #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 576,2 - #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_PAR_AMD_EUS_PER_CU - #define GPUCA_PAR_AMD_EUS_PER_CU 0 - #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 576 - #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 1024 - #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/GPUParameters.json b/GPU/GPUTracking/Definitions/GPUParameters.json new file mode 100644 index 0000000000000..e8f1c24520813 --- /dev/null +++ b/GPU/GPUTracking/Definitions/GPUParameters.json @@ -0,0 +1,582 @@ +{ + "CORE": { + "WARP_SIZE": { + "default": 32, + "MI100": 64, + "VEGA": 64, + "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], + "AMPERE": [256, 2], + "TURING": [256, 2] + }, + "GPUTPCTrackletSelector": { + "default": 256, + "MI100": [384, 5], + "VEGA": [192, 10], + "AMPERE": [192, 3], + "TURING": [192, 3] + }, + "GPUTPCNeighboursFinder": { + "default": 256, + "MI100": [192, 8], + "VEGA": [960, 8], + "AMPERE": [640, 1], + "TURING": [640, 1] + }, + "GPUTPCNeighboursCleaner": { + "default": 256, + "MI100": [128, 5], + "VEGA": [384, 9], + "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], + "AMPERE": 512, + "TURING": 512 + }, + "GPUTPCStartHitsSorter": { + "default": 256, + "MI100": [1024, 5], + "VEGA": [512, 7], + "AMPERE": [512, 1], + "TURING": [512, 1] + }, + "GPUTPCCFCheckPadBaseline": { + "default": 64, + "MI100": [64, 10], + "VEGA": [64, 2], + "AMPERE": [64, 8] + }, + "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/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index 9bfe6feb14d8d..c61056466929e 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -624,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") diff --git a/GPU/GPUTracking/cmake/generateGPUParamHeader.cmake b/GPU/GPUTracking/cmake/generateGPUParamHeader.cmake new file mode 100644 index 0000000000000..712bf4641b825 --- /dev/null +++ b/GPU/GPUTracking/cmake/generateGPUParamHeader.cmake @@ -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. + +# file generateGPUParamHeader.cmake +# author Gabriele Cimador + +function(generate_gpu_param_header GPU_ARCH OUT_HEADER) + set(GPU_PARAM_JSON ${CMAKE_SOURCE_DIR}/GPU/GPUTracking/Definitions/GPUParameters.json) + set(TARGET_ARCH "UNKNOWN") + if(GPU_ARCH STREQUAL "AUTO") + detect_gpu_arch("AUTO") + else() + set(TARGET_ARCH ${GPU_ARCH}) + endif() + add_custom_command( + OUTPUT ${OUT_HEADER} + COMMAND ${CMAKE_COMMAND} + -DOUT_HEADER=${OUT_HEADER} + -DGPU_PARAM_JSON=${GPU_PARAM_JSON} + -DTARGET_ARCH_SHORT=${TARGET_ARCH} + -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/gpu_param_header_generator.cmake + DEPENDS + ${CMAKE_CURRENT_SOURCE_DIR}/cmake/gpu_param_header_generator.cmake + ${GPU_PARAM_JSON} + COMMENT "Generating GPU parameter header for ${TARGET_ARCH}" + VERBATIM + ) + add_custom_target(GPU_PARAM_HEADER_${GPU_ARCH}_ALL ALL DEPENDS ${OUT_HEADER}) +endfunction() \ No newline at end of file diff --git a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake new file mode 100644 index 0000000000000..3949322b5abfa --- /dev/null +++ b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake @@ -0,0 +1,105 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not 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 + +file(READ "${GPU_PARAM_JSON}" JSON_CONTENT) +set(TMP_HEADER "${OUT_HEADER}.tmp") +file(WRITE "${TMP_HEADER}" "#ifndef GPUDEFPARAMETERSDEFAULTS_H\n#define GPUDEFPARAMETERSDEFAULTS_H\n\n") +file(APPEND "${TMP_HEADER}" "// This file is auto-generated from gpu_params.json. Do not edit directly.\n") +string(REPLACE "," ";" ARCH_LIST "${TARGET_ARCH_SHORT}") +file(APPEND "${TMP_HEADER}" "// Architectures: ${TARGET_ARCH_SHORT}\n\n") +file(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") + +# Types +set(TYPES CORE LB PAR) +foreach(ARCH IN LISTS ARCH_LIST) + file(APPEND "${TMP_HEADER}" "#if defined(GPUCA_GPUTYPE_${ARCH})\n\n") + foreach(TYPE IN LISTS TYPES) + # Get all keys of this TYPE as a semicolon-separated list + 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}") + 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 "${ARCH}") + 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}") + file(APPEND "${TMP_HEADER}" "${MACRO_DEFINITION}\n") + endif() + endforeach() + endforeach() + endforeach() + file(APPEND "${TMP_HEADER}" "\n#endif // GPUCA_GPUTYPE_${ARCH}\n\n") +endforeach() + +file(APPEND "${TMP_HEADER}" "\n// Default parameters if not defined for the target architecture\n\n") +#Default parameters +foreach(TYPE IN LISTS TYPES) + # Get all keys of this TYPE as a semicolon-separated list + 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 param_values GET "${JSON_CONTENT}" "${TYPE}" "${param_name}" "default") + 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}") + file(APPEND "${TMP_HEADER}" "#ifndef ${MACRO_NAME}\n ${MACRO_DEFINITION}\n#endif\n\n") + endforeach() +endforeach() +file(APPEND "${TMP_HEADER}" "#endif // defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_GENRTC) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS)\n\n") + +#Defaults for non-LB parameters also for CPU fallback +file(APPEND "${TMP_HEADER}" "#ifndef GPUCA_GPUCODE_GENRTC //Defaults for non-LB parameters also for CPU fallback\n\n") # Get all keys of this TYPE as a semicolon-separated list +string(JSON n_params LENGTH "${JSON_CONTENT}" "PAR") +math(EXPR last "${n_params} - 1") +foreach(i RANGE 0 ${last}) + string(JSON param_name MEMBER "${JSON_CONTENT}" "PAR" "${i}") + string(JSON param_values GET "${JSON_CONTENT}" "PAR" "${param_name}" "default_cpu") + set(MACRO_NAME "GPUCA_PAR_${param_name}") + 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}") + file(APPEND "${TMP_HEADER}" "#ifndef ${MACRO_NAME}\n ${MACRO_DEFINITION}\n#endif\n\n") +endforeach() +file(APPEND "${TMP_HEADER}" "\n#endif // GPUCA_GPUCODE_GENRTC\n") + +file(APPEND "${TMP_HEADER}" "\n#endif // GPUDEFPARAMETERSDEFAULTS_H\n") +file(RENAME "${TMP_HEADER}" "${OUT_HEADER}") +message(STATUS "Generated ${OUT_HEADER}") 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/dependencies/FindO2GPU.cmake b/dependencies/FindO2GPU.cmake index 21e2d7cad239a..3cfcaef82fcca 100644 --- a/dependencies/FindO2GPU.cmake +++ b/dependencies/FindO2GPU.cmake @@ -10,7 +10,7 @@ # or submit itself to any jurisdiction. # NOTE!!!! - Whenever this file is changed, move it over to alidist/resources -# FindO2GPU.cmake Version 9 +# FindO2GPU.cmake Version 10 set(CUDA_COMPUTETARGET_DEFAULT_FULL 80-real 86-real 89-real 120-real 75-virtual) set(HIP_AMDGPUTARGET_DEFAULT_FULL gfx906;gfx908) @@ -44,34 +44,53 @@ if(HIP_AMDGPUTARGET AND HIP_AMDGPUTARGET STREQUAL "default") 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")) +function(detect_gpu_arch backend) # Detect GPU architecture, optionally filterring by backend + set(TARGET_ARCH "") + set(CUDA_TARGET "") + set(HIP_TARGET "") + + if(CUDA_COMPUTETARGET AND CUDA_COMPUTETARGET MATCHES "86|89") + set(CUDA_TARGET AMPERE) 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") + set(CUDA_TARGET TURING) message(STATUS "Using optimized CUDA settings for Turing GPU") - target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_TURING) else() + set(CUDA_TARGET AMPERE) message(STATUS "Defaulting optimized CUDA settings for Ampere GPU") - target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_AMPERE) endif() -endfunction() -function(set_target_hip_arch target) if(HIP_AMDGPUTARGET AND HIP_AMDGPUTARGET MATCHES "gfx906") + set(HIP_TARGET VEGA) 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") + set(HIP_TARGET MI100) message(STATUS "Using optimized HIP settings for MI100 GPU") - target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_MI100) elseif(HIP_AMDGPUTARGET AND HIP_AMDGPUTARGET MATCHES "gfx90a") + set(HIP_TARGET MI100) message(STATUS "Using optimized HIP settings for MI210 GPU") - target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_MI100) else() - target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_VEGA) + set(HIP_TARGET VEGA) + message(STATUS "Defaulting optimized HIP settings for VEGA GPU") + endif() + + if(backend STREQUAL "CUDA") # CUDA filter + set(TARGET_ARCH "${CUDA_TARGET}" PARENT_SCOPE) + return() + elseif(backend STREQUAL "HIP") # HIP filter + set(TARGET_ARCH "${HIP_TARGET}" PARENT_SCOPE) + return() + else() # Return both + set(TARGET_ARCH "${CUDA_TARGET},${HIP_TARGET}" PARENT_SCOPE) endif() endfunction() +function(set_target_gpu_arch backend target) + detect_gpu_arch("${backend}") + message(STATUS "Compiling for ${TARGET_ARCH}") + 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}") diff --git a/log.txt b/log.txt new file mode 100644 index 0000000000000..e69de29bb2d1d From f683cac8423902087d678de4181d4a2089376296 Mon Sep 17 00:00:00 2001 From: Gabriele Cimador Date: Wed, 28 Jan 2026 17:53:20 +0100 Subject: [PATCH 205/701] GPU Framework: refactor generation of default GPU parameters --- GPU/GPUTracking/CMakeLists.txt | 2 +- .../Definitions/Parameters/.clang-format | 1 + .../{ => Parameters}/.clang-format-ignore | 0 .../{ => Parameters}/GPUParameters.json | 0 .../cmake/generateGPUParamHeader.cmake | 37 ------ .../cmake/gpu_param_header_generator.cmake | 117 ++++++++---------- dependencies/FindO2GPU.cmake | 8 +- 7 files changed, 58 insertions(+), 107 deletions(-) create mode 100644 GPU/GPUTracking/Definitions/Parameters/.clang-format rename GPU/GPUTracking/Definitions/{ => Parameters}/.clang-format-ignore (100%) rename GPU/GPUTracking/Definitions/{ => Parameters}/GPUParameters.json (100%) delete mode 100644 GPU/GPUTracking/cmake/generateGPUParamHeader.cmake diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index a2d91b6ed4c5e..816d578fb31a3 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -109,7 +109,7 @@ set(SRCS_NO_H SectorTracker/GPUTPCTrackerDump.cxx set(ON_THE_FLY_DIR ${CMAKE_CURRENT_BINARY_DIR}/include_gpu_onthefly) file(MAKE_DIRECTORY ${ON_THE_FLY_DIR}) -include(cmake/generateGPUParamHeader.cmake) +include(cmake/gpu_param_header_generator.cmake) set(GPU_DEFAULT_PARAMS_HEADER ${ON_THE_FLY_DIR}/GPUDefParametersDefaults.h) generate_gpu_param_header("AUTO" ${GPU_DEFAULT_PARAMS_HEADER}) # generate header with default GPU parameters, arch selected by CMake variables diff --git a/GPU/GPUTracking/Definitions/Parameters/.clang-format b/GPU/GPUTracking/Definitions/Parameters/.clang-format new file mode 100644 index 0000000000000..e3845288a2aec --- /dev/null +++ b/GPU/GPUTracking/Definitions/Parameters/.clang-format @@ -0,0 +1 @@ +DisableFormat: true diff --git a/GPU/GPUTracking/Definitions/.clang-format-ignore b/GPU/GPUTracking/Definitions/Parameters/.clang-format-ignore similarity index 100% rename from GPU/GPUTracking/Definitions/.clang-format-ignore rename to GPU/GPUTracking/Definitions/Parameters/.clang-format-ignore diff --git a/GPU/GPUTracking/Definitions/GPUParameters.json b/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json similarity index 100% rename from GPU/GPUTracking/Definitions/GPUParameters.json rename to GPU/GPUTracking/Definitions/Parameters/GPUParameters.json diff --git a/GPU/GPUTracking/cmake/generateGPUParamHeader.cmake b/GPU/GPUTracking/cmake/generateGPUParamHeader.cmake deleted file mode 100644 index 712bf4641b825..0000000000000 --- a/GPU/GPUTracking/cmake/generateGPUParamHeader.cmake +++ /dev/null @@ -1,37 +0,0 @@ -# Copyright 2019-2020 CERN and copyright holders of ALICE O2. -# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -# All rights not expressly granted are reserved. -# -# This software is distributed under the terms of the GNU General Public -# License v3 (GPL Version 3), copied verbatim in the file "COPYING". -# -# In applying this license CERN does not waive the privileges and immunities -# granted to it by virtue of its status as an Intergovernmental Organization -# or submit itself to any jurisdiction. - -# file generateGPUParamHeader.cmake -# author Gabriele Cimador - -function(generate_gpu_param_header GPU_ARCH OUT_HEADER) - set(GPU_PARAM_JSON ${CMAKE_SOURCE_DIR}/GPU/GPUTracking/Definitions/GPUParameters.json) - set(TARGET_ARCH "UNKNOWN") - if(GPU_ARCH STREQUAL "AUTO") - detect_gpu_arch("AUTO") - else() - set(TARGET_ARCH ${GPU_ARCH}) - endif() - add_custom_command( - OUTPUT ${OUT_HEADER} - COMMAND ${CMAKE_COMMAND} - -DOUT_HEADER=${OUT_HEADER} - -DGPU_PARAM_JSON=${GPU_PARAM_JSON} - -DTARGET_ARCH_SHORT=${TARGET_ARCH} - -P ${CMAKE_CURRENT_SOURCE_DIR}/cmake/gpu_param_header_generator.cmake - DEPENDS - ${CMAKE_CURRENT_SOURCE_DIR}/cmake/gpu_param_header_generator.cmake - ${GPU_PARAM_JSON} - COMMENT "Generating GPU parameter header for ${TARGET_ARCH}" - VERBATIM - ) - add_custom_target(GPU_PARAM_HEADER_${GPU_ARCH}_ALL ALL DEPENDS ${OUT_HEADER}) -endfunction() \ No newline at end of file diff --git a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake index 3949322b5abfa..38b92421616f2 100644 --- a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake +++ b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake @@ -12,31 +12,18 @@ # file gpu_param_header_generator.cmake # author Gabriele Cimador -file(READ "${GPU_PARAM_JSON}" JSON_CONTENT) -set(TMP_HEADER "${OUT_HEADER}.tmp") -file(WRITE "${TMP_HEADER}" "#ifndef GPUDEFPARAMETERSDEFAULTS_H\n#define GPUDEFPARAMETERSDEFAULTS_H\n\n") -file(APPEND "${TMP_HEADER}" "// This file is auto-generated from gpu_params.json. Do not edit directly.\n") -string(REPLACE "," ";" ARCH_LIST "${TARGET_ARCH_SHORT}") -file(APPEND "${TMP_HEADER}" "// Architectures: ${TARGET_ARCH_SHORT}\n\n") -file(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") - -# Types -set(TYPES CORE LB PAR) -foreach(ARCH IN LISTS ARCH_LIST) - file(APPEND "${TMP_HEADER}" "#if defined(GPUCA_GPUTYPE_${ARCH})\n\n") - foreach(TYPE IN LISTS TYPES) - # Get all keys of this TYPE as a semicolon-separated list - string(JSON n_params LENGTH "${JSON_CONTENT}" "${TYPE}") +function(generate_macros json_content header types arch_key use_ifndef_guard) + 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 param_name MEMBER "${json_content}" "${TYPE}" "${i}") string(JSON n_archs LENGTH "${JSON_CONTENT}" "${TYPE}" "${param_name}") 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 "${ARCH}") - string(JSON param_values GET "${JSON_CONTENT}" "${TYPE}" "${param_name}" "${ARCH}") + if(arch STREQUAL "${arch_key}") + string(JSON param_values GET "${JSON_CONTENT}" "${TYPE}" "${param_name}" "${arch_key}") if(TYPE STREQUAL "LB") set(MACRO_NAME "GPUCA_LB_${param_name}") elseif(TYPE STREQUAL "PAR") @@ -49,57 +36,57 @@ foreach(ARCH IN LISTS ARCH_LIST) string(REGEX REPLACE " *\\]$" "" vals "${vals}") string(REGEX REPLACE "\"" "" vals "${vals}") set(MACRO_DEFINITION "#define ${MACRO_NAME} ${vals}") - file(APPEND "${TMP_HEADER}" "${MACRO_DEFINITION}\n") + if(use_ifndef_guard) + # fallback defaults are wrapped in #ifndef + file(APPEND "${header}" "#ifndef ${MACRO_NAME}\n ${MACRO_DEFINITION}\n#endif\n\n") + else() + file(APPEND "${header}" "${MACRO_DEFINITION}\n") + endif() endif() endforeach() endforeach() endforeach() - file(APPEND "${TMP_HEADER}" "\n#endif // GPUCA_GPUTYPE_${ARCH}\n\n") -endforeach() +endfunction() + +function(generate_gpu_param_header GPU_ARCH OUT_HEADER) + set(GPU_PARAM_JSON ${CMAKE_SOURCE_DIR}/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json) + set(TARGET_ARCH "UNKNOWN") + if(GPU_ARCH STREQUAL "AUTO") + detect_gpu_arch("ALL") + else() + set(TARGET_ARCH ${GPU_ARCH}) + endif() + file(READ "${GPU_PARAM_JSON}" JSON_CONTENT) + set(TMP_HEADER "${OUT_HEADER}.tmp") + message(STATUS "OUT_HEADER = '${OUT_HEADER}'") + message(STATUS "TMP_HEADER = '${TMP_HEADER}'") + file(WRITE "${TMP_HEADER}" "#ifndef GPUDEFPARAMETERSDEFAULTS_H\n#define GPUDEFPARAMETERSDEFAULTS_H\n\n") + file(APPEND "${TMP_HEADER}" "// This file is auto-generated from gpu_params.json. Do not edit directly.\n") + string(REPLACE "," ";" ARCH_LIST "${TARGET_ARCH}") + file(APPEND "${TMP_HEADER}" "// Architectures: ${TARGET_ARCH}\n\n") + file(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") -file(APPEND "${TMP_HEADER}" "\n// Default parameters if not defined for the target architecture\n\n") -#Default parameters -foreach(TYPE IN LISTS TYPES) - # Get all keys of this TYPE as a semicolon-separated list - 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 param_values GET "${JSON_CONTENT}" "${TYPE}" "${param_name}" "default") - 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}") - file(APPEND "${TMP_HEADER}" "#ifndef ${MACRO_NAME}\n ${MACRO_DEFINITION}\n#endif\n\n") + # Types + set(TYPES CORE LB PAR) + # Per architecture definitions + foreach(ARCH IN LISTS ARCH_LIST) + file(APPEND "${TMP_HEADER}" "#if defined(GPUCA_GPUTYPE_${ARCH})\n\n") + generate_macros("${JSON_CONTENT}" "${TMP_HEADER}" "${TYPES}" "${ARCH}" "") + file(APPEND "${TMP_HEADER}" "\n#endif // GPUCA_GPUTYPE_${ARCH}\n\n") endforeach() -endforeach() -file(APPEND "${TMP_HEADER}" "#endif // defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_GENRTC) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS)\n\n") -#Defaults for non-LB parameters also for CPU fallback -file(APPEND "${TMP_HEADER}" "#ifndef GPUCA_GPUCODE_GENRTC //Defaults for non-LB parameters also for CPU fallback\n\n") # Get all keys of this TYPE as a semicolon-separated list -string(JSON n_params LENGTH "${JSON_CONTENT}" "PAR") -math(EXPR last "${n_params} - 1") -foreach(i RANGE 0 ${last}) - string(JSON param_name MEMBER "${JSON_CONTENT}" "PAR" "${i}") - string(JSON param_values GET "${JSON_CONTENT}" "PAR" "${param_name}" "default_cpu") - set(MACRO_NAME "GPUCA_PAR_${param_name}") - 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}") - file(APPEND "${TMP_HEADER}" "#ifndef ${MACRO_NAME}\n ${MACRO_DEFINITION}\n#endif\n\n") -endforeach() -file(APPEND "${TMP_HEADER}" "\n#endif // GPUCA_GPUCODE_GENRTC\n") + # Default parameters + file(APPEND "${TMP_HEADER}" "\n// Default parameters if not defined for the target architecture\n\n") + generate_macros("${JSON_CONTENT}" "${TMP_HEADER}" "${TYPES}" "default" "use_ifndef_guard") + file(APPEND "${TMP_HEADER}" "#endif // defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_GENRTC) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS)\n\n") + + # CPU fallback + file(APPEND "${TMP_HEADER}" "#ifndef GPUCA_GPUCODE_GENRTC //Defaults for non-LB parameters also for CPU fallback\n\n") # Get all keys of this TYPE as a semicolon-separated list + generate_macros("${JSON_CONTENT}" "${TMP_HEADER}" "PAR" "default_cpu" "use_ifndef_guard") + file(APPEND "${TMP_HEADER}" "\n#endif // GPUCA_GPUCODE_GENRTC\n") -file(APPEND "${TMP_HEADER}" "\n#endif // GPUDEFPARAMETERSDEFAULTS_H\n") -file(RENAME "${TMP_HEADER}" "${OUT_HEADER}") -message(STATUS "Generated ${OUT_HEADER}") + file(APPEND "${TMP_HEADER}" "\n#endif // GPUDEFPARAMETERSDEFAULTS_H\n") + file(RENAME "${TMP_HEADER}" "${OUT_HEADER}") + message(STATUS "Generated ${OUT_HEADER}") + add_custom_target(GPU_PARAM_HEADER_${GPU_ARCH}_ALL ALL DEPENDS ${OUT_HEADER} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/gpu_param_header_generator.cmake ${GPU_PARAM_JSON}) +endfunction() \ No newline at end of file diff --git a/dependencies/FindO2GPU.cmake b/dependencies/FindO2GPU.cmake index 3cfcaef82fcca..4ed29ec61f3e1 100644 --- a/dependencies/FindO2GPU.cmake +++ b/dependencies/FindO2GPU.cmake @@ -45,9 +45,6 @@ if(HIP_AMDGPUTARGET AND HIP_AMDGPUTARGET STREQUAL "default") endif() function(detect_gpu_arch backend) # Detect GPU architecture, optionally filterring by backend - set(TARGET_ARCH "") - set(CUDA_TARGET "") - set(HIP_TARGET "") if(CUDA_COMPUTETARGET AND CUDA_COMPUTETARGET MATCHES "86|89") set(CUDA_TARGET AMPERE) @@ -80,8 +77,11 @@ function(detect_gpu_arch backend) # Detect GPU architecture, optionally filterri elseif(backend STREQUAL "HIP") # HIP filter set(TARGET_ARCH "${HIP_TARGET}" PARENT_SCOPE) return() - else() # Return both + elseif(backend STREQUAL "ALL") # Return both set(TARGET_ARCH "${CUDA_TARGET},${HIP_TARGET}" PARENT_SCOPE) + return() + else() + message(FATAL_ERROR "Unknown backend provided: ${backend}") endif() endfunction() From fe431a1ae55bf5a05e617210f7fab1f4f35aa378 Mon Sep 17 00:00:00 2001 From: Gabriele Cimador Date: Thu, 29 Jan 2026 13:16:58 +0100 Subject: [PATCH 206/701] GPU Framework: Add OpenCL support to GPU param header generation + update CFCheckPadBaseline parameters --- .../Definitions/Parameters/GPUParameters.json | 8 ++++---- .../cmake/gpu_param_header_generator.cmake | 20 ++++++++++++------- dependencies/FindO2GPU.cmake | 19 ++++++++++++------ 3 files changed, 30 insertions(+), 17 deletions(-) diff --git a/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json b/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json index e8f1c24520813..674efc9ea0912 100644 --- a/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json +++ b/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json @@ -338,10 +338,10 @@ "TURING": [512, 1] }, "GPUTPCCFCheckPadBaseline": { - "default": 64, - "MI100": [64, 10], - "VEGA": [64, 2], - "AMPERE": [64, 8] + "default": 576, + "MI100": [576, 2], + "VEGA": [576, 2], + "AMPERE": [576, 2] }, "GPUTPCCFChargeMapFiller_fillIndexMap": { "default": 512, diff --git a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake index 38b92421616f2..3770e30f2583c 100644 --- a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake +++ b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake @@ -49,7 +49,7 @@ function(generate_macros json_content header types arch_key use_ifndef_guard) endfunction() function(generate_gpu_param_header GPU_ARCH OUT_HEADER) - set(GPU_PARAM_JSON ${CMAKE_SOURCE_DIR}/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json) + set(GPU_PARAM_JSON ${CMAKE_CURRENT_SOURCE_DIR}/Definitions/Parameters/GPUParameters.json) set(TARGET_ARCH "UNKNOWN") if(GPU_ARCH STREQUAL "AUTO") detect_gpu_arch("ALL") @@ -58,8 +58,6 @@ function(generate_gpu_param_header GPU_ARCH OUT_HEADER) endif() file(READ "${GPU_PARAM_JSON}" JSON_CONTENT) set(TMP_HEADER "${OUT_HEADER}.tmp") - message(STATUS "OUT_HEADER = '${OUT_HEADER}'") - message(STATUS "TMP_HEADER = '${TMP_HEADER}'") file(WRITE "${TMP_HEADER}" "#ifndef GPUDEFPARAMETERSDEFAULTS_H\n#define GPUDEFPARAMETERSDEFAULTS_H\n\n") file(APPEND "${TMP_HEADER}" "// This file is auto-generated from gpu_params.json. Do not edit directly.\n") string(REPLACE "," ";" ARCH_LIST "${TARGET_ARCH}") @@ -69,11 +67,19 @@ function(generate_gpu_param_header GPU_ARCH OUT_HEADER) # Types set(TYPES CORE LB PAR) # Per architecture definitions + set(_first TRUE) foreach(ARCH IN LISTS ARCH_LIST) - file(APPEND "${TMP_HEADER}" "#if defined(GPUCA_GPUTYPE_${ARCH})\n\n") - generate_macros("${JSON_CONTENT}" "${TMP_HEADER}" "${TYPES}" "${ARCH}" "") - file(APPEND "${TMP_HEADER}" "\n#endif // GPUCA_GPUTYPE_${ARCH}\n\n") + if(_first) + file(APPEND "${TMP_HEADER}" "#if defined(GPUCA_GPUTYPE_${ARCH})\n\n") + set(_first FALSE) + else() + file(APPEND "${TMP_HEADER}" "#elif defined(GPUCA_GPUTYPE_${ARCH})\n\n") + endif() + generate_macros("${JSON_CONTENT}" "${TMP_HEADER}" "${TYPES}" "${ARCH}" "") endforeach() + if(NOT _first) + file(APPEND "${TMP_HEADER}" "#else\n#error GPU TYPE NOT SET\n#endif\n") + endif() # Default parameters file(APPEND "${TMP_HEADER}" "\n// Default parameters if not defined for the target architecture\n\n") @@ -81,7 +87,7 @@ function(generate_gpu_param_header GPU_ARCH OUT_HEADER) file(APPEND "${TMP_HEADER}" "#endif // defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_GENRTC) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS)\n\n") # CPU fallback - file(APPEND "${TMP_HEADER}" "#ifndef GPUCA_GPUCODE_GENRTC //Defaults for non-LB parameters also for CPU fallback\n\n") # Get all keys of this TYPE as a semicolon-separated list + file(APPEND "${TMP_HEADER}" "#ifndef GPUCA_GPUCODE_GENRTC //Defaults for non-LB parameters also for CPU fallback\n\n") generate_macros("${JSON_CONTENT}" "${TMP_HEADER}" "PAR" "default_cpu" "use_ifndef_guard") file(APPEND "${TMP_HEADER}" "\n#endif // GPUCA_GPUCODE_GENRTC\n") diff --git a/dependencies/FindO2GPU.cmake b/dependencies/FindO2GPU.cmake index 4ed29ec61f3e1..e02bf932ab784 100644 --- a/dependencies/FindO2GPU.cmake +++ b/dependencies/FindO2GPU.cmake @@ -73,13 +73,21 @@ function(detect_gpu_arch backend) # Detect GPU architecture, optionally filterri if(backend STREQUAL "CUDA") # CUDA filter set(TARGET_ARCH "${CUDA_TARGET}" PARENT_SCOPE) - return() elseif(backend STREQUAL "HIP") # HIP filter set(TARGET_ARCH "${HIP_TARGET}" PARENT_SCOPE) - return() - elseif(backend STREQUAL "ALL") # Return both - set(TARGET_ARCH "${CUDA_TARGET},${HIP_TARGET}" PARENT_SCOPE) - return() + elseif(backend STREQUAL "ALL") # Return enabled backends + set(_archs "") + if(CUDA_ENABLED) + list(APPEND _archs "${CUDA_TARGET}") + endif() + if(HIP_ENABLED) + list(APPEND _archs "${HIP_TARGET}") + endif() + if(OPENCL_ENABLED) + list(APPEND _archs "OPENCL") + endif() + list(JOIN _archs "," TARGET_ARCH) + set(TARGET_ARCH "${TARGET_ARCH}" PARENT_SCOPE) else() message(FATAL_ERROR "Unknown backend provided: ${backend}") endif() @@ -87,7 +95,6 @@ endfunction() function(set_target_gpu_arch backend target) detect_gpu_arch("${backend}") - message(STATUS "Compiling for ${TARGET_ARCH}") target_compile_definitions(${target} PUBLIC GPUCA_GPUTYPE_${TARGET_ARCH}) endfunction() From 5a15ab5c1b12b1010a439bcd16751e375059795f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Thu, 5 Feb 2026 11:47:27 +0100 Subject: [PATCH 207/701] GPU: Delete unused files (#15015) * GPU: Remove unused files * Add missing suffix * Revert "Add missing suffix" This reverts commit 81879979abeac8839164d57d33d65a392c4b3aea. * Remove cxx suffix --- ...tGPUsortHIP.hip.cxx => testGPUsortHIP.hip} | 0 .../SectorTracker/GPUTPCDefinitions.h | 25 -- .../utils/makefile_opencl_compiler.cxx | 258 ------------------ .../utils/opencl_compiler_structs.h | 28 -- GPU/GPUTracking/utils/opencl_obtain_program.h | 91 ------ 5 files changed, 402 deletions(-) rename GPU/GPUTracking/Base/hip/test/{testGPUsortHIP.hip.cxx => testGPUsortHIP.hip} (100%) delete mode 100644 GPU/GPUTracking/SectorTracker/GPUTPCDefinitions.h delete mode 100644 GPU/GPUTracking/utils/makefile_opencl_compiler.cxx delete mode 100644 GPU/GPUTracking/utils/opencl_compiler_structs.h delete mode 100644 GPU/GPUTracking/utils/opencl_obtain_program.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/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/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_compiler_structs.h b/GPU/GPUTracking/utils/opencl_compiler_structs.h deleted file mode 100644 index 68e0a4f184480..0000000000000 --- a/GPU/GPUTracking/utils/opencl_compiler_structs.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. - -/// \file opencl_compiler_structs.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; -}; - -struct _makefiles_opencl_device_info { - char device_name[64]; - char device_vendor[64]; - cl_uint nbits; - size_t binary_size; -}; 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 From d64bc8603cc75d8de8e6523d75ac49f83f265364 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 5 Feb 2026 12:07:23 +0100 Subject: [PATCH 208/701] Add the .clang-format-ignore (#15019) --- .clang-format-ignore | 1 + 1 file changed, 1 insertion(+) create mode 100644 .clang-format-ignore 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 From 414ba09ac65b6bfe4202cab6327246817dee2646 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 5 Feb 2026 10:24:37 +0100 Subject: [PATCH 209/701] Add back tuned parameters for old architectures TAHITI TESLA FERMI PASCAL KEPLER --- .../Definitions/Parameters/.clang-format | 1 - .../Parameters/.clang-format-ignore | 1 - .../Definitions/Parameters/GPUParameters.json | 46 +++++++++++++++++-- 3 files changed, 41 insertions(+), 7 deletions(-) delete mode 100644 GPU/GPUTracking/Definitions/Parameters/.clang-format delete mode 100644 GPU/GPUTracking/Definitions/Parameters/.clang-format-ignore diff --git a/GPU/GPUTracking/Definitions/Parameters/.clang-format b/GPU/GPUTracking/Definitions/Parameters/.clang-format deleted file mode 100644 index e3845288a2aec..0000000000000 --- a/GPU/GPUTracking/Definitions/Parameters/.clang-format +++ /dev/null @@ -1 +0,0 @@ -DisableFormat: true diff --git a/GPU/GPUTracking/Definitions/Parameters/.clang-format-ignore b/GPU/GPUTracking/Definitions/Parameters/.clang-format-ignore deleted file mode 100644 index 5ffee2498bd7e..0000000000000 --- a/GPU/GPUTracking/Definitions/Parameters/.clang-format-ignore +++ /dev/null @@ -1 +0,0 @@ -GPUParameters.json diff --git a/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json b/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json index 674efc9ea0912..285919559c04c 100644 --- a/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json +++ b/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json @@ -1,11 +1,17 @@ { "CORE": { "WARP_SIZE": { - "default": 32, - "MI100": 64, - "VEGA": 64, - "AMPERE": 32, - "TURING": 32 + "default": 32, + "default_cpu": 1, + "MI100": 64, + "VEGA": 64, + "TAHITI": 32, + "TESLA": 32, + "FERMI": 32, + "PASCAL": 32, + "KEPLER": 32, + "AMPERE": 32, + "TURING": 32 }, "THREAD_COUNT_DEFAULT": { "default": 256, @@ -27,6 +33,11 @@ "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] }, @@ -34,6 +45,11 @@ "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] }, @@ -41,6 +57,11 @@ "default": 256, "MI100": [192, 8], "VEGA": [960, 8], + "TAHITI": 256, + "TESLA": 256, + "FERMI": 256, + "PASCAL": 512, + "KEPLER": 256, "AMPERE": [640, 1], "TURING": [640, 1] }, @@ -48,6 +69,11 @@ "default": 256, "MI100": [128, 5], "VEGA": [384, 9], + "TAHITI": 256, + "TESLA": 256, + "FERMI": 256, + "PASCAL": 256, + "KEPLER": 256, "AMPERE": 512, "TURING": 512 }, @@ -327,6 +353,11 @@ "default": 256, "MI100": [1024, 2], "VEGA": [1024, 7], + "TAHITI": 256, + "TESLA": 256, + "PASCAL": 256, + "FERMI": 256, + "KEPLER": 256, "AMPERE": 512, "TURING": 512 }, @@ -334,6 +365,11 @@ "default": 256, "MI100": [1024, 5], "VEGA": [512, 7], + "TAHITI": 256, + "TESLA": 256, + "PASCAL": 256, + "FERMI": 256, + "KEPLER": 256, "AMPERE": [512, 1], "TURING": [512, 1] }, From 024cbcab8238cc0a7c4a6259d518fabbfc498b91 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 5 Feb 2026 10:29:50 +0100 Subject: [PATCH 210/701] Don't use 'No CUDA devices found' as CUDA architecture --- dependencies/FindO2GPU.cmake | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/dependencies/FindO2GPU.cmake b/dependencies/FindO2GPU.cmake index e02bf932ab784..ec6b7323ad5d1 100644 --- a/dependencies/FindO2GPU.cmake +++ b/dependencies/FindO2GPU.cmake @@ -10,7 +10,7 @@ # or submit itself to any jurisdiction. # NOTE!!!! - Whenever this file is changed, move it over to alidist/resources -# FindO2GPU.cmake Version 10 +# FindO2GPU.cmake Version 11 set(CUDA_COMPUTETARGET_DEFAULT_FULL 80-real 86-real 89-real 120-real 75-virtual) set(HIP_AMDGPUTARGET_DEFAULT_FULL gfx906;gfx908) @@ -173,9 +173,7 @@ 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} - PATH_SUFFIXES "" cccl - 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, looked under: ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES}") set(CMAKE_CUDA_COMPILER OFF) @@ -188,7 +186,7 @@ if(ENABLE_CUDA) endif() endif() if(NOT CMAKE_CUDA_ARCHITECTURES OR O2_GPU_CUDA_UPDATE_NATIVE_ARCHITECTURE) - if(NOT CMAKE_CUDA_ARCHITECTURES_NATIVE STREQUAL "") + 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}) From 9e910d691cb379f93c7c1dc7a09ee9193cddaa06 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 5 Feb 2026 15:59:08 +0100 Subject: [PATCH 211/701] GPU: Add converter scripts for CSV parameter file to JSON and vice versa --- .../Base/cuda/GPUReconstructionCUDA.cu | 2 +- .../Definitions/Parameters/csv_to_json.sh | 46 +++++++++++++++++ .../Definitions/Parameters/json_to_csv.python | 50 +++++++++++++++++++ 3 files changed, 97 insertions(+), 1 deletion(-) create mode 100755 GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh create mode 100755 GPU/GPUTracking/Definitions/Parameters/json_to_csv.python diff --git a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu index 8e896ca513f53..c919581eefdde 100644 --- a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu +++ b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu @@ -189,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); } } } 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..ae9d3b7704284 --- /dev/null +++ b/GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash + +[[ -z $1 ]] && { echo "Usage: csv_to_json.sh CSV_FILE"; exit 1; } + +awk -vFPAT='([^,]*)|(\"([^\"]|\"\")*\")' \ + '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 "}" + }' \ + $1 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..a6640239604e0 --- /dev/null +++ b/GPU/GPUTracking/Definitions/Parameters/json_to_csv.python @@ -0,0 +1,50 @@ +#!/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 + +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) From c8834dee0b39f86f394dfb07f5a42ab09659dffa Mon Sep 17 00:00:00 2001 From: shahoian Date: Thu, 5 Feb 2026 11:27:52 +0100 Subject: [PATCH 212/701] Promote --ctf-dict from process to workflow level option --- .../Base/include/DetectorsBase/CTFCoderBase.h | 14 ++++---- .../include/CPVReconstruction/CTFCoder.h | 2 +- .../include/CPVWorkflow/EntropyDecoderSpec.h | 4 +-- .../include/CPVWorkflow/EntropyEncoderSpec.h | 4 +-- .../CPV/workflow/src/EntropyDecoderSpec.cxx | 13 +++---- .../CPV/workflow/src/EntropyEncoderSpec.cxx | 13 +++---- .../workflow/src/entropy-encoder-workflow.cxx | 3 +- .../include/CTFWorkflow/CTFReaderSpec.h | 1 + Detectors/CTF/workflow/src/CTFReaderSpec.cxx | 1 - .../CTF/workflow/src/ctf-reader-workflow.cxx | 34 ++++++++++--------- .../include/CTPReconstruction/CTFCoder.h | 2 +- .../include/CTPWorkflow/EntropyDecoderSpec.h | 4 +-- .../include/CTPWorkflow/EntropyEncoderSpec.h | 4 +-- .../CTP/workflow/src/EntropyDecoderSpec.cxx | 16 ++++----- .../CTP/workflow/src/EntropyEncoderSpec.cxx | 16 ++++----- .../workflow/src/entropy-encoder-workflow.cxx | 3 +- .../include/EMCALReconstruction/CTFCoder.h | 2 +- .../EMCALWorkflow/EntropyDecoderSpec.h | 4 +-- .../EMCALWorkflow/EntropyEncoderSpec.h | 4 +-- .../EMCAL/workflow/src/EntropyDecoderSpec.cxx | 16 ++++----- .../EMCAL/workflow/src/EntropyEncoderSpec.cxx | 23 ++++++------- .../workflow/src/entropy-encoder-workflow.cxx | 3 +- .../include/FDDReconstruction/CTFCoder.h | 2 +- .../include/FDDWorkflow/EntropyDecoderSpec.h | 4 +-- .../include/FDDWorkflow/EntropyEncoderSpec.h | 4 +-- .../FDD/workflow/src/EntropyDecoderSpec.cxx | 16 ++++----- .../FDD/workflow/src/EntropyEncoderSpec.cxx | 16 ++++----- .../workflow/src/entropy-encoder-workflow.cxx | 3 +- .../include/FT0Reconstruction/CTFCoder.h | 2 +- .../include/FT0Workflow/EntropyDecoderSpec.h | 4 +-- .../include/FT0Workflow/EntropyEncoderSpec.h | 4 +-- .../FT0/workflow/src/EntropyDecoderSpec.cxx | 15 ++++---- .../FT0/workflow/src/EntropyEncoderSpec.cxx | 16 ++++----- .../workflow/src/entropy-encoder-workflow.cxx | 3 +- .../include/FV0Reconstruction/CTFCoder.h | 2 +- .../include/FV0Workflow/EntropyDecoderSpec.h | 4 +-- .../include/FV0Workflow/EntropyEncoderSpec.h | 4 +-- .../FV0/workflow/src/EntropyDecoderSpec.cxx | 16 ++++----- .../FV0/workflow/src/EntropyEncoderSpec.cxx | 16 ++++----- .../workflow/src/entropy-encoder-workflow.cxx | 3 +- .../include/HMPIDReconstruction/CTFCoder.h | 2 +- .../HMPIDWorkflow/EntropyDecoderSpec.h | 2 +- .../HMPIDWorkflow/EntropyEncoderSpec.h | 2 +- .../HMPID/workflow/src/EntropyDecoderSpec.cxx | 18 +++++----- .../HMPID/workflow/src/EntropyEncoderSpec.cxx | 18 +++++----- .../workflow/src/entropy-encoder-workflow.cxx | 3 +- .../include/ITSMFTReconstruction/CTFCoder.h | 2 +- .../ITSMFTWorkflow/EntropyDecoderSpec.h | 4 +-- .../ITSMFTWorkflow/EntropyEncoderSpec.h | 4 +-- .../workflow/src/EntropyDecoderSpec.cxx | 22 ++++++------ .../workflow/src/EntropyEncoderSpec.cxx | 18 +++++----- .../workflow/src/entropy-encoder-workflow.cxx | 3 +- .../MUON/MCH/CTF/include/MCHCTF/CTFCoder.h | 2 +- .../CTF/include/MCHCTF/EntropyDecoderSpec.h | 2 +- .../MUON/MCH/CTF/src/EntropyDecoderSpec.cxx | 18 +++++----- .../Workflow/src/entropy-encoder-workflow.cxx | 21 ++++++------ .../MUON/MID/CTF/include/MIDCTF/CTFCoder.h | 2 +- .../include/MIDWorkflow/EntropyDecoderSpec.h | 4 +-- .../include/MIDWorkflow/EntropyEncoderSpec.h | 4 +-- .../MID/Workflow/src/EntropyDecoderSpec.cxx | 16 ++++----- .../MID/Workflow/src/EntropyEncoderSpec.cxx | 16 ++++----- .../Workflow/src/entropy-encoder-workflow.cxx | 3 +- .../include/PHOSReconstruction/CTFCoder.h | 2 +- .../include/PHOSWorkflow/EntropyDecoderSpec.h | 4 +-- .../include/PHOSWorkflow/EntropyEncoderSpec.h | 4 +-- .../PHOS/workflow/src/EntropyDecoderSpec.cxx | 16 ++++----- .../PHOS/workflow/src/EntropyEncoderSpec.cxx | 16 ++++----- .../workflow/src/entropy-encoder-workflow.cxx | 3 +- .../include/TOFReconstruction/CTFCoder.h | 2 +- .../TOFWorkflowUtils/EntropyDecoderSpec.h | 4 +-- .../TOFWorkflowUtils/EntropyEncoderSpec.h | 4 +-- .../TOF/workflow/src/EntropyDecoderSpec.cxx | 16 ++++----- .../TOF/workflow/src/EntropyEncoderSpec.cxx | 16 ++++----- .../workflow/src/entropy-encoder-workflow.cxx | 3 +- .../include/TPCReconstruction/CTFCoder.h | 2 +- .../include/TPCWorkflow/EntropyDecoderSpec.h | 4 +-- .../include/TPCWorkflow/EntropyEncoderSpec.h | 4 +-- .../include/TPCWorkflow/RecoWorkflow.h | 1 + .../TPC/workflow/src/EntropyDecoderSpec.cxx | 14 ++++---- .../TPC/workflow/src/EntropyEncoderSpec.cxx | 16 ++++----- Detectors/TPC/workflow/src/RecoWorkflow.cxx | 4 +-- .../workflow/src/entropy-encoder-workflow.cxx | 3 +- .../TPC/workflow/src/tpc-reco-workflow.cxx | 2 ++ .../include/TRDReconstruction/CTFCoder.h | 2 +- .../include/TRDWorkflow/EntropyDecoderSpec.h | 2 +- .../include/TRDWorkflow/EntropyEncoderSpec.h | 2 +- .../TRD/workflow/src/EntropyDecoderSpec.cxx | 18 +++++----- .../TRD/workflow/src/EntropyEncoderSpec.cxx | 18 +++++----- .../workflow/src/entropy-encoder-workflow.cxx | 3 +- .../include/ZDCReconstruction/CTFCoder.h | 2 +- .../include/ZDCWorkflow/EntropyDecoderSpec.h | 4 +-- .../include/ZDCWorkflow/EntropyEncoderSpec.h | 4 +-- .../ZDC/workflow/src/EntropyDecoderSpec.cxx | 16 ++++----- .../ZDC/workflow/src/EntropyEncoderSpec.cxx | 16 ++++----- .../workflow/src/entropy-encoder-workflow.cxx | 3 +- 95 files changed, 378 insertions(+), 358 deletions(-) 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/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h b/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h index ab5082b5c748c..a5f9d0eac90e8 100644 --- a/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h +++ b/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h @@ -35,7 +35,7 @@ namespace cpv 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/reconstruction/include/CTPReconstruction/CTFCoder.h b/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h index 87657f6a6f8c6..b17db0e77be28 100644 --- a/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h +++ b/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h @@ -37,7 +37,7 @@ namespace ctp 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/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h index 23deb75ffb049..6584775057d9f 100644 --- a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h @@ -35,7 +35,7 @@ namespace emcal 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/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/reconstruction/include/FDDReconstruction/CTFCoder.h b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h index 94a0c6f64659d..cb3b13aa9b8e4 100644 --- a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h +++ b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h @@ -36,7 +36,7 @@ namespace fdd 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/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/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/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/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h index 65646c161dde5..5c2e0f0627ef1 100644 --- a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h +++ b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h @@ -37,7 +37,7 @@ namespace ft0 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/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/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/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/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h index 4398e19c0a5ed..fdff035b934ef 100644 --- a/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h +++ b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h @@ -33,7 +33,7 @@ namespace fv0 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/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/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/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/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h index 39242355a3de9..0e6694d2353ac 100644 --- a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h +++ b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h @@ -35,7 +35,7 @@ namespace hmpid 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/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/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h index 94c14424f6ce3..57d989038342a 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h @@ -45,7 +45,7 @@ class CTFCoder final : public o2::ctf::CTFCoderBase using PMatrix = std::array, 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/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/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 2d65cbbaea614..5c9da95a98354 100644 --- a/Detectors/MUON/MCH/CTF/include/MCHCTF/CTFCoder.h +++ b/Detectors/MUON/MCH/CTF/include/MCHCTF/CTFCoder.h @@ -37,7 +37,7 @@ namespace mch 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/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 5afc42550ae3e..defec7207f808 100644 --- a/Detectors/MUON/MID/CTF/include/MIDCTF/CTFCoder.h +++ b/Detectors/MUON/MID/CTF/include/MIDCTF/CTFCoder.h @@ -37,7 +37,7 @@ namespace mid 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/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/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/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/reconstruction/include/PHOSReconstruction/CTFCoder.h b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFCoder.h index 8a7172f634a33..e222328a351c0 100644 --- a/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFCoder.h +++ b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFCoder.h @@ -35,7 +35,7 @@ namespace phos 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/TOF/reconstruction/include/TOFReconstruction/CTFCoder.h b/Detectors/TOF/reconstruction/include/TOFReconstruction/CTFCoder.h index e7a203cfcb25e..53cdf59d08572 100644 --- a/Detectors/TOF/reconstruction/include/TOFReconstruction/CTFCoder.h +++ b/Detectors/TOF/reconstruction/include/TOFReconstruction/CTFCoder.h @@ -34,7 +34,7 @@ namespace tof class CTFCoder final : public o2::ctf::CTFCoderBase { public: - CTFCoder(o2::ctf::CTFCoderBase::OpType op) : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::TOF) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), 1.f, o2::detectors::DetID::TOF, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode clusters to buffer with CTF diff --git a/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyDecoderSpec.h b/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyDecoderSpec.h index c09aa6abc9f7b..714b23d955a78 100644 --- a/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyDecoderSpec.h +++ b/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyDecoderSpec.h @@ -29,7 +29,7 @@ namespace tof class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(int verbosity); + EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt = "none"); ~EntropyDecoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -43,7 +43,7 @@ class EntropyDecoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec); +framework::DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt); } // namespace tof } // namespace o2 diff --git a/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyEncoderSpec.h b/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyEncoderSpec.h index ee0739c076597..27377b6447d1c 100644 --- a/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyEncoderSpec.h +++ b/Detectors/TOF/workflow/include/TOFWorkflowUtils/EntropyEncoderSpec.h @@ -29,7 +29,7 @@ namespace tof class EntropyEncoderSpec : public o2::framework::Task { public: - EntropyEncoderSpec(bool selIR); + EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -43,7 +43,7 @@ class EntropyEncoderSpec : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false); +framework::DataProcessorSpec getEntropyEncoderSpec(bool selIR = false, const std::string& ctfdictOpt = "none"); } // namespace tof } // namespace o2 diff --git a/Detectors/TOF/workflow/src/EntropyDecoderSpec.cxx b/Detectors/TOF/workflow/src/EntropyDecoderSpec.cxx index 400914c64021f..8c0445e3ee3cb 100644 --- a/Detectors/TOF/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/TOF/workflow/src/EntropyDecoderSpec.cxx @@ -25,8 +25,7 @@ namespace o2 { namespace tof { - -EntropyDecoderSpec::EntropyDecoderSpec(int verbosity) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder) +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, ctfdictOpt) { mTimer.Stop(); mTimer.Reset(); @@ -93,7 +92,7 @@ void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) +DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec, const std::string& ctfdictOpt) { std::vector outputs{ OutputSpec{{"digitheader"}, o2::header::gDataOriginTOF, "DIGITHEADER", 0, Lifetime::Timeframe}, @@ -105,17 +104,18 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) std::vector inputs; inputs.emplace_back("ctf_TOF", "TOF", "CTFDATA", sspec, Lifetime::Timeframe); - inputs.emplace_back("ctfdict_TOF", "TOF", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("TOF/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict_TOF", "TOF", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("TOF/Calib/CTFDictionaryTree")); + } inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ "tof-entropy-decoder", inputs, outputs, - AlgorithmSpec{adaptFromTask(verbosity)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; + AlgorithmSpec{adaptFromTask(verbosity, ctfdictOpt)}, + Options{{"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace tof } // namespace o2 diff --git a/Detectors/TOF/workflow/src/EntropyEncoderSpec.cxx b/Detectors/TOF/workflow/src/EntropyEncoderSpec.cxx index 3fc47955f53c0..27d7c162cf670 100644 --- a/Detectors/TOF/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/TOF/workflow/src/EntropyEncoderSpec.cxx @@ -25,8 +25,7 @@ namespace o2 { namespace tof { - -EntropyEncoderSpec::EntropyEncoderSpec(bool selIR) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder), mSelIR(selIR) +EntropyEncoderSpec::EntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, ctfdictOpt), mSelIR(selIR) { mTimer.Stop(); mTimer.Reset(); @@ -71,13 +70,16 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getEntropyEncoderSpec(bool selIR) +DataProcessorSpec getEntropyEncoderSpec(bool selIR, const std::string& ctfdictOpt) { std::vector inputs; inputs.emplace_back("compDigits", o2::header::gDataOriginTOF, "DIGITS", 0, Lifetime::Timeframe); inputs.emplace_back("patterns", o2::header::gDataOriginTOF, "PATTERNS", 0, Lifetime::Timeframe); inputs.emplace_back("ROframes", o2::header::gDataOriginTOF, "READOUTWINDOW", 0, Lifetime::Timeframe); - inputs.emplace_back("ctfdict", "TOF", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("TOF/Calib/CTFDictionaryTree")); + + if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { + inputs.emplace_back("ctfdict", "TOF", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("TOF/Calib/CTFDictionaryTree")); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } @@ -86,14 +88,12 @@ DataProcessorSpec getEntropyEncoderSpec(bool selIR) inputs, Outputs{{o2::header::gDataOriginTOF, "CTFDATA", 0, Lifetime::Timeframe}, {{"ctfrep"}, "TOF", "CTFENCREP", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(selIR)}, - Options{{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, - {"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, + AlgorithmSpec{adaptFromTask(selIR, ctfdictOpt)}, + Options{{"irframe-margin-bwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame lower boundary when selection is requested"}}, {"irframe-margin-fwd", VariantType::UInt32, 0u, {"margin in BC to add to the IRFrame upper boundary when selection is requested"}}, {"irframe-shift", VariantType::Int, o2::tof::Geo::LATENCYWINDOW_IN_BC, {"IRFrame shift to account for latency"}}, {"mem-factor", VariantType::Float, 1.f, {"Memory allocation margin factor"}}, {"ans-version", VariantType::String, {"version of ans entropy coder implementation to use"}}}}; } - } // namespace tof } // namespace o2 diff --git a/Detectors/TOF/workflow/src/entropy-encoder-workflow.cxx b/Detectors/TOF/workflow/src/entropy-encoder-workflow.cxx index 1969672ad3fa3..5cf882e2723d6 100644 --- a/Detectors/TOF/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/TOF/workflow/src/entropy-encoder-workflow.cxx @@ -23,6 +23,7 @@ void customize(std::vector& workflowOptions) // option allowing to set parameters std::vector options{ ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + ConfigParamSpec{"ctf-dict", VariantType::String, "ccdb", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); @@ -37,6 +38,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) WorkflowSpec wf; // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); - wf.emplace_back(o2::tof::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"))); + wf.emplace_back(o2::tof::getEntropyEncoderSpec(cfgc.options().get("select-ir-frames"), cfgc.options().get("ctf-dict"))); return wf; } diff --git a/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h b/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h index 12d66ef6a6e7c..2c6fac7dcde2a 100644 --- a/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h +++ b/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h @@ -122,7 +122,7 @@ struct MergedColumnsDecoder { 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/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/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/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/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/reconstruction/include/TRDReconstruction/CTFCoder.h b/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFCoder.h index 9eeaf19db5025..adb584ef15ec4 100644 --- a/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFCoder.h +++ b/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFCoder.h @@ -36,7 +36,7 @@ namespace trd 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 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/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/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/ZDC/reconstruction/include/ZDCReconstruction/CTFCoder.h b/Detectors/ZDC/reconstruction/include/ZDCReconstruction/CTFCoder.h index f8823e4fc66a5..a299431ef17fc 100644 --- a/Detectors/ZDC/reconstruction/include/ZDCReconstruction/CTFCoder.h +++ b/Detectors/ZDC/reconstruction/include/ZDCReconstruction/CTFCoder.h @@ -35,7 +35,7 @@ namespace zdc 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/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; } From d00ca87143fa6617948d44aff6ea98e75b7ba7a9 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Wed, 4 Feb 2026 08:06:11 +0100 Subject: [PATCH 213/701] Revert "ITS: GPU: more memory clearing in processNeighbours" This reverts commit aa3ef3751f282ee477e0636d6bd5697c43103381. Signed-off-by: Felix Schlepper --- .../ITS/tracking/GPU/cuda/TrackingKernels.cu | 84 ++++++------------- 1 file changed, 26 insertions(+), 58 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu index 50888c676df77..a12237358c8bd 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -39,7 +39,6 @@ // O2 track model #include "ReconstructionDataFormats/Track.h" #include "DetectorsBase/Propagator.h" -#include "utils/strtag.h" using namespace o2::track; namespace o2::its @@ -1107,19 +1106,11 @@ void processNeighboursHandler(const int startLayer, const int nBlocks, const int nThreads) { - constexpr uint64_t Tag = qStr2Tag("ITS_PNH1"); - - // allocators used auto allocInt = gpu::TypedAllocator(alloc); auto allocCellSeed = gpu::TypedAllocator>(alloc); - // use sync_policy, this part cannot be run async but tell thrust to use the allocator - auto sync_policy = THRUST_NAMESPACE::par(gpu::TypedAllocator(alloc)); - - // put initial computation on Tag1 - alloc->pushTagOnStack(Tag); - - // start processing of cells thrust::device_vector> foundSeedsTable(nCells[startLayer] + 1, 0, allocInt); + auto nosync_policy = THRUST_NAMESPACE::par_nosync(gpu::TypedAllocator(alloc)).on(gpu::Stream::DefaultStream); + gpu::processNeighboursKernel<<>>( startLayer, startLevel, @@ -1138,10 +1129,10 @@ void processNeighboursHandler(const int startLayer, maxChi2ClusterAttachment, propagator, matCorrType); - thrust::exclusive_scan(sync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), foundSeedsTable.begin()); - auto foundSeeds{foundSeedsTable.back()}; - thrust::device_vector> updatedCellId(foundSeeds, 0, allocInt); - thrust::device_vector, gpu::TypedAllocator>> updatedCellSeed(foundSeeds, allocCellSeed); + thrust::exclusive_scan(nosync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), foundSeedsTable.begin()); + + thrust::device_vector> updatedCellId(foundSeedsTable.back(), 0, allocInt); + thrust::device_vector, gpu::TypedAllocator>> updatedCellSeed(foundSeedsTable.back(), allocCellSeed); gpu::processNeighboursKernel<<>>( startLayer, startLevel, @@ -1160,41 +1151,20 @@ void processNeighboursHandler(const int startLayer, maxChi2ClusterAttachment, propagator, matCorrType); + GPUChkErrS(cudaStreamSynchronize(gpu::Stream::DefaultStream)); - // now do inward steps until stop is reached int level = startLevel; - - // Host buffers to break dependency - // FIXME: these should be on our memory resource! - std::vector hostCellId; - std::vector> hostCellSeed; - - // inward loop + thrust::device_vector> lastCellId(allocInt); + thrust::device_vector, gpu::TypedAllocator>> lastCellSeed(allocCellSeed); for (int iLayer{startLayer - 1}; iLayer > 0 && level > 2; --iLayer) { - // copy current results to host - hostCellId.resize(updatedCellId.size()); - hostCellSeed.resize(updatedCellSeed.size()); - thrust::copy(updatedCellId.begin(), updatedCellId.end(), hostCellId.begin()); - thrust::copy(updatedCellSeed.begin(), updatedCellSeed.end(), hostCellSeed.begin()); - - auto lastCellSeedSize{hostCellSeed.size()}; - // but before we clear the memory, and immediately start a new block - alloc->popTagOffStack(Tag); - alloc->pushTagOnStack(Tag); - - // based on the previous step's result create new LUT and zero it - thrust::device_vector>(allocInt).swap(foundSeedsTable); - foundSeedsTable.resize(lastCellSeedSize + 1); - thrust::fill(sync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), 0); - - // recreate lastCell vectors from host - thrust::device_vector> lastCellId(hostCellId.begin(), hostCellId.end(), allocInt); - thrust::device_vector, gpu::TypedAllocator>> lastCellSeed(hostCellSeed.begin(), hostCellSeed.end(), allocCellSeed); - // also create new vectors on new block + lastCellSeed.swap(updatedCellSeed); + lastCellId.swap(updatedCellId); thrust::device_vector, gpu::TypedAllocator>>(allocCellSeed).swap(updatedCellSeed); thrust::device_vector>(allocInt).swap(updatedCellId); + auto lastCellSeedSize{lastCellSeed.size()}; + foundSeedsTable.resize(lastCellSeedSize + 1); + thrust::fill(nosync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), 0); - // start step gpu::processNeighboursKernel<<>>( iLayer, --level, @@ -1213,13 +1183,14 @@ void processNeighboursHandler(const int startLayer, maxChi2ClusterAttachment, propagator, matCorrType); - // how many new seeds where found - thrust::exclusive_scan(sync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), foundSeedsTable.begin()); - foundSeeds = foundSeedsTable.back(); - // do a resize, we don't need to set the memory now since we know that all of these are written to - // Note though this does not clear the memory... + thrust::exclusive_scan(nosync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), foundSeedsTable.begin()); + + auto foundSeeds{foundSeedsTable.back()}; updatedCellId.resize(foundSeeds); + thrust::fill(nosync_policy, updatedCellId.begin(), updatedCellId.end(), 0); updatedCellSeed.resize(foundSeeds); + thrust::fill(nosync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), CellSeed()); + gpu::processNeighboursKernel<<>>( iLayer, level, @@ -1239,15 +1210,12 @@ void processNeighboursHandler(const int startLayer, propagator, matCorrType); } - - // final copy of result - const auto selector = gpu::seed_selector(1.e3, maxChi2NDF * ((startLevel + 2) * 2 - 5)); - const auto count = thrust::count_if(sync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), selector); - thrust::device_vector, gpu::TypedAllocator>> outSeeds(count, allocCellSeed); - thrust::copy_if(sync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), outSeeds.begin(), selector); - seedsHost.reserve(seedsHost.size() + count); - thrust::copy(outSeeds.begin(), outSeeds.end(), std::back_inserter(seedsHost)); - alloc->popTagOffStack(Tag); + GPUChkErrS(cudaStreamSynchronize(gpu::Stream::DefaultStream)); + thrust::device_vector, gpu::TypedAllocator>> outSeeds(updatedCellSeed.size(), allocCellSeed); + auto end = thrust::copy_if(nosync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), outSeeds.begin(), gpu::seed_selector(1.e3, maxChi2NDF * ((startLevel + 2) * 2 - 5))); + auto s{end - outSeeds.begin()}; + seedsHost.reserve(seedsHost.size() + s); + thrust::copy(outSeeds.begin(), outSeeds.begin() + s, std::back_inserter(seedsHost)); } template From 19803f64dccc492a0bfd8d528ea481dcc972faf8 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Thu, 5 Feb 2026 17:44:10 +0100 Subject: [PATCH 214/701] ITS: GPU: add minimal version of clearing Signed-off-by: Felix Schlepper --- Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu index a12237358c8bd..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" @@ -1106,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); @@ -1216,6 +1218,7 @@ 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 From 5639312fdb8f49ef5365f32985bf9a53187add07 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 5 Feb 2026 21:27:14 +0100 Subject: [PATCH 215/701] GPU CMake: Avoid repetitive JSON parsing --- GPU/GPUTracking/CMakeLists.txt | 6 ++-- .../Definitions/Parameters/GPUParameters.json | 1 - .../cmake/gpu_param_header_generator.cmake | 31 +++++++++++++------ 3 files changed, 24 insertions(+), 14 deletions(-) diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index 816d578fb31a3..adfb79a78b994 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -410,12 +410,12 @@ target_sources(${targetName} BASE_DIRS ${CMAKE_CURRENT_BINARY_DIR}) make_directory(${CMAKE_CURRENT_BINARY_DIR}/genGPUArch) -set(GPU_CONST_PARAM_ARCHITECTUES AMPERE TURING VEGA MI100) +set(GPU_CONST_PARAM_ARCHITECTUES "AMPERE;TURING;VEGA;MI100") set(GPU_CONST_PARAM_FILES "") +set(GPU_ARCH_PARAMS_HEADER ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch/GPUDefParametersDefaults_OnTheFly.h) +generate_gpu_param_header("${GPU_CONST_PARAM_ARCHITECTUES}" ${GPU_ARCH_PARAMS_HEADER}) foreach(GPU_ARCH ${GPU_CONST_PARAM_ARCHITECTUES}) set(PARAMFILE ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch/gpu_const_param_${GPU_ARCH}.par) - set(GPU_ARCH_PARAMS_HEADER ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch/GPUDefParametersDefaults_${GPU_ARCH}.h) - generate_gpu_param_header(${GPU_ARCH} ${GPU_ARCH_PARAMS_HEADER}) add_custom_command( OUTPUT ${PARAMFILE} COMMAND bash -c diff --git a/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json b/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json index 285919559c04c..3c6f1af1aab2f 100644 --- a/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json +++ b/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json @@ -2,7 +2,6 @@ "CORE": { "WARP_SIZE": { "default": 32, - "default_cpu": 1, "MI100": 64, "VEGA": 64, "TAHITI": 32, diff --git a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake index 3770e30f2583c..526303a353106 100644 --- a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake +++ b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake @@ -12,7 +12,10 @@ # file gpu_param_header_generator.cmake # author Gabriele Cimador -function(generate_macros json_content header types arch_key use_ifndef_guard) +function(generate_macros json_content output types arch_list) + foreach(arch IN LISTS arch_list) + set(${output}_${arch} "") + endforeach() foreach(TYPE IN LISTS types) string(JSON n_params LENGTH "${json_content}" "${TYPE}") math(EXPR last "${n_params} - 1") @@ -22,8 +25,12 @@ function(generate_macros json_content header types arch_key use_ifndef_guard) 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 "${arch_key}") - string(JSON param_values GET "${JSON_CONTENT}" "${TYPE}" "${param_name}" "${arch_key}") + if(arch STREQUAL "default_cpu" AND NOT TYPE STREQUAL "PAR") + message(FATAL_ERROR "Bogus entry ${param_name} for ${arch}") + endif() + list(FIND arch_list "${arch}" list_idx) + 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") @@ -36,16 +43,19 @@ function(generate_macros json_content header types arch_key use_ifndef_guard) string(REGEX REPLACE " *\\]$" "" vals "${vals}") string(REGEX REPLACE "\"" "" vals "${vals}") set(MACRO_DEFINITION "#define ${MACRO_NAME} ${vals}") - if(use_ifndef_guard) + if(arch MATCHES ^default) # fallback defaults are wrapped in #ifndef - file(APPEND "${header}" "#ifndef ${MACRO_NAME}\n ${MACRO_DEFINITION}\n#endif\n\n") + string(APPEND ${output}_${arch} "#ifndef ${MACRO_NAME}\n ${MACRO_DEFINITION}\n#endif\n\n") else() - file(APPEND "${header}" "${MACRO_DEFINITION}\n") + string(APPEND ${output}_${arch} "${MACRO_DEFINITION}\n") endif() endif() endforeach() endforeach() endforeach() + foreach(arch IN LISTS arch_list) + set(${output}_${arch} "${${output}_${arch}}" PARENT_SCOPE) + endforeach() endfunction() function(generate_gpu_param_header GPU_ARCH OUT_HEADER) @@ -68,6 +78,7 @@ function(generate_gpu_param_header GPU_ARCH OUT_HEADER) set(TYPES CORE LB PAR) # Per architecture definitions set(_first TRUE) + generate_macros("${JSON_CONTENT}" TMP_OUTPUT "${TYPES}" "${ARCH_LIST};default;default_cpu") foreach(ARCH IN LISTS ARCH_LIST) if(_first) file(APPEND "${TMP_HEADER}" "#if defined(GPUCA_GPUTYPE_${ARCH})\n\n") @@ -75,7 +86,7 @@ function(generate_gpu_param_header GPU_ARCH OUT_HEADER) else() file(APPEND "${TMP_HEADER}" "#elif defined(GPUCA_GPUTYPE_${ARCH})\n\n") endif() - generate_macros("${JSON_CONTENT}" "${TMP_HEADER}" "${TYPES}" "${ARCH}" "") + file(APPEND "${TMP_HEADER}" ${TMP_OUTPUT_${ARCH}}) endforeach() if(NOT _first) file(APPEND "${TMP_HEADER}" "#else\n#error GPU TYPE NOT SET\n#endif\n") @@ -83,16 +94,16 @@ function(generate_gpu_param_header GPU_ARCH OUT_HEADER) # Default parameters file(APPEND "${TMP_HEADER}" "\n// Default parameters if not defined for the target architecture\n\n") - generate_macros("${JSON_CONTENT}" "${TMP_HEADER}" "${TYPES}" "default" "use_ifndef_guard") + file(APPEND "${TMP_HEADER}" ${TMP_OUTPUT_default}) file(APPEND "${TMP_HEADER}" "#endif // defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_GENRTC) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS)\n\n") # CPU fallback file(APPEND "${TMP_HEADER}" "#ifndef GPUCA_GPUCODE_GENRTC //Defaults for non-LB parameters also for CPU fallback\n\n") - generate_macros("${JSON_CONTENT}" "${TMP_HEADER}" "PAR" "default_cpu" "use_ifndef_guard") + file(APPEND "${TMP_HEADER}" ${TMP_OUTPUT_default_cpu}) file(APPEND "${TMP_HEADER}" "\n#endif // GPUCA_GPUCODE_GENRTC\n") file(APPEND "${TMP_HEADER}" "\n#endif // GPUDEFPARAMETERSDEFAULTS_H\n") file(RENAME "${TMP_HEADER}" "${OUT_HEADER}") message(STATUS "Generated ${OUT_HEADER}") add_custom_target(GPU_PARAM_HEADER_${GPU_ARCH}_ALL ALL DEPENDS ${OUT_HEADER} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/gpu_param_header_generator.cmake ${GPU_PARAM_JSON}) -endfunction() \ No newline at end of file +endfunction() From e7b5a26d2ad81644dc692c3b740d3124f49d71b0 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 5 Feb 2026 22:06:35 +0100 Subject: [PATCH 216/701] GPU CMake: Generate optimized parameter files for all available architectures, not only for hardcoded list --- GPU/GPUTracking/CMakeLists.txt | 3 +- .../cmake/gpu_param_header_generator.cmake | 37 +++++++++++++++---- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index adfb79a78b994..9f349d0e21f4f 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -410,10 +410,9 @@ target_sources(${targetName} BASE_DIRS ${CMAKE_CURRENT_BINARY_DIR}) make_directory(${CMAKE_CURRENT_BINARY_DIR}/genGPUArch) -set(GPU_CONST_PARAM_ARCHITECTUES "AMPERE;TURING;VEGA;MI100") set(GPU_CONST_PARAM_FILES "") set(GPU_ARCH_PARAMS_HEADER ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch/GPUDefParametersDefaults_OnTheFly.h) -generate_gpu_param_header("${GPU_CONST_PARAM_ARCHITECTUES}" ${GPU_ARCH_PARAMS_HEADER}) +generate_gpu_param_header("ALL" ${GPU_ARCH_PARAMS_HEADER} "GPU_CONST_PARAM_ARCHITECTUES") foreach(GPU_ARCH ${GPU_CONST_PARAM_ARCHITECTUES}) set(PARAMFILE ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch/gpu_const_param_${GPU_ARCH}.par) add_custom_command( diff --git a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake index 526303a353106..0c3e905a697c0 100644 --- a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake +++ b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake @@ -12,10 +12,12 @@ # file gpu_param_header_generator.cmake # author Gabriele Cimador -function(generate_macros json_content output types arch_list) +function(generate_macros json_content output types arch_list arch_list_output) foreach(arch IN LISTS arch_list) - set(${output}_${arch} "") + set(OUTPUT_TMP_${arch} "") endforeach() + set(arch_list_output_tmp) + list(FIND arch_list "ALL" do_all_architectures) foreach(TYPE IN LISTS types) string(JSON n_params LENGTH "${json_content}" "${TYPE}") math(EXPR last "${n_params} - 1") @@ -28,7 +30,14 @@ function(generate_macros json_content output types arch_list) if(arch STREQUAL "default_cpu" AND NOT TYPE STREQUAL "PAR") message(FATAL_ERROR "Bogus entry ${param_name} for ${arch}") endif() - list(FIND arch_list "${arch}" list_idx) + if(do_all_architectures GREATER -1) + if(arch_list_output AND NOT arch MATCHES ^default) + list(APPEND arch_list_output_tmp "${arch}") + endif() + set(list_idx 0) + else() + list(FIND arch_list "${arch}" list_idx) + endif() if(list_idx GREATER -1) string(JSON param_values GET "${JSON_CONTENT}" "${TYPE}" "${param_name}" "${arch}") if(TYPE STREQUAL "LB") @@ -45,17 +54,22 @@ function(generate_macros json_content output types arch_list) set(MACRO_DEFINITION "#define ${MACRO_NAME} ${vals}") if(arch MATCHES ^default) # fallback defaults are wrapped in #ifndef - string(APPEND ${output}_${arch} "#ifndef ${MACRO_NAME}\n ${MACRO_DEFINITION}\n#endif\n\n") + string(APPEND OUTPUT_TMP_${arch} "#ifndef ${MACRO_NAME}\n ${MACRO_DEFINITION}\n#endif\n\n") else() - string(APPEND ${output}_${arch} "${MACRO_DEFINITION}\n") + string(APPEND OUTPUT_TMP_${arch} "${MACRO_DEFINITION}\n") endif() endif() endforeach() endforeach() endforeach() foreach(arch IN LISTS arch_list) - set(${output}_${arch} "${${output}_${arch}}" PARENT_SCOPE) + set(${output}_${arch} "${OUTPUT_TMP_${arch}}" PARENT_SCOPE) endforeach() + if(arch_list_output) + list(REMOVE_DUPLICATES arch_list_output_tmp) + list(SORT arch_list_output_tmp) + set(${arch_list_output} "${arch_list_output_tmp}" PARENT_SCOPE) + endif() endfunction() function(generate_gpu_param_header GPU_ARCH OUT_HEADER) @@ -78,7 +92,14 @@ function(generate_gpu_param_header GPU_ARCH OUT_HEADER) set(TYPES CORE LB PAR) # Per architecture definitions set(_first TRUE) - generate_macros("${JSON_CONTENT}" TMP_OUTPUT "${TYPES}" "${ARCH_LIST};default;default_cpu") + generate_macros("${JSON_CONTENT}" TMP_OUTPUT "${TYPES}" "${ARCH_LIST};default;default_cpu" "JSON_ARCHITECTURES") + list(FIND ARCH_LIST "ALL" do_all_architectures) + if(ARGC GREATER 2) + set(${ARGV2} "${JSON_ARCHITECTURES}" PARENT_SCOPE) + endif() + if(do_all_architectures GREATER -1) + set(ARCH_LIST ${JSON_ARCHITECTURES}) + endif() foreach(ARCH IN LISTS ARCH_LIST) if(_first) file(APPEND "${TMP_HEADER}" "#if defined(GPUCA_GPUTYPE_${ARCH})\n\n") @@ -98,7 +119,7 @@ function(generate_gpu_param_header GPU_ARCH OUT_HEADER) file(APPEND "${TMP_HEADER}" "#endif // defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_GENRTC) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS)\n\n") # CPU fallback - file(APPEND "${TMP_HEADER}" "#ifndef GPUCA_GPUCODE_GENRTC //Defaults for non-LB parameters also for CPU fallback\n\n") + file(APPEND "${TMP_HEADER}" "#ifndef GPUCA_GPUCODE_GENRTC // Defaults for non-LB parameters also for CPU fallback\n\n") file(APPEND "${TMP_HEADER}" ${TMP_OUTPUT_default_cpu}) file(APPEND "${TMP_HEADER}" "\n#endif // GPUCA_GPUCODE_GENRTC\n") From 0f4e3152fb0fe97fd29acffbc7954937cf82e381 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 5 Feb 2026 22:36:28 +0100 Subject: [PATCH 217/701] GPU CMake: Make source for GPU parameters configurable, auto-convert CSV to JSON if necessary --- GPU/GPUTracking/CMakeLists.txt | 17 +++ .../Definitions/Parameters/GPUParameters.csv | 113 ++++++++++++++++++ ...meters.json => GPUParameters.json.example} | 0 GPU/GPUTracking/Standalone/cmake/config.cmake | 1 + .../cmake/gpu_param_header_generator.cmake | 1 - 5 files changed, 131 insertions(+), 1 deletion(-) create mode 100644 GPU/GPUTracking/Definitions/Parameters/GPUParameters.csv rename GPU/GPUTracking/Definitions/Parameters/{GPUParameters.json => GPUParameters.json.example} (100%) diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index 9f349d0e21f4f..cd17d8f284b13 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -107,6 +107,23 @@ set(SRCS_NO_H SectorTracker/GPUTPCTrackerDump.cxx Global/GPUChainTrackingDebugAndProfiling.cxx Global/GPUChainTrackingIO.cxx) +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() +get_filename_component(GPU_PARAM_JSON_EXT ${GPU_PARAM_JSON} EXT) +string(TOLOWER "${GPU_PARAM_JSON_EXT}" GPU_PARAM_JSON_EXT) +if(GPU_PARAM_JSON_EXT STREQUAL .csv) + execute_process( + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/Definitions/Parameters/csv_to_json.sh "${GPU_PARAM_JSON}" + OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/gpu_parameters.json + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + message(STATUS "Converted ${GPU_PARAM_JSON} to ${CMAKE_CURRENT_BINARY_DIR}/gpu_parameters.json") + set(GPU_PARAM_JSON ${CMAKE_CURRENT_BINARY_DIR}/gpu_parameters.json) +endif() + set(ON_THE_FLY_DIR ${CMAKE_CURRENT_BINARY_DIR}/include_gpu_onthefly) file(MAKE_DIRECTORY ${ON_THE_FLY_DIR}) include(cmake/gpu_param_header_generator.cmake) diff --git a/GPU/GPUTracking/Definitions/Parameters/GPUParameters.csv b/GPU/GPUTracking/Definitions/Parameters/GPUParameters.csv new file mode 100644 index 0000000000000..5afa99554f5d0 --- /dev/null +++ b/GPU/GPUTracking/Definitions/Parameters/GPUParameters.csv @@ -0,0 +1,113 @@ +Architecture,default,MI100,VEGA,TAHITI,TESLA,FERMI,PASCAL,KEPLER,AMPERE,TURING,default_cpu +,,,,,,,,,,, +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,4,4,,,,,,,,0 +SORT_STARTHITS,1,,,,,,,,,,0 +NEIGHBOURS_FINDER_MAX_NNEIGHUP,6,10,4,,,,,,4,4,0 +NEIGHBOURS_FINDER_UNROLL_GLOBAL,4,4,2,,,,,,,,0 +NEIGHBOURS_FINDER_UNROLL_SHARED,1,0,0,,,,,,,,0 +TRACKLET_SELECTOR_HITS_REG_SIZE,12,9,27,,,,,,20,20,0 +ALTERNATE_BORDER_SORT,0,1,1,,,,,,1,1,0 +SORT_BEFORE_FIT,0,1,1,,,,,,1,1,0 +NO_ATOMIC_PRECHECK,0,1,1,,,,,,1,1,0 +DEDX_STORAGE_TYPE,"""float""","""uint16_t""","""uint16_t""",,,,,,"""uint16_t""","""uint16_t""","""float""" +MERGER_INTERPOLATION_ERROR_TYPE,"""float""","""half""","""half""",,,,,,"""half""","""half""","""float""" +COMP_GATHER_KERNEL,0,4,4,,,,,,4,4,0 +COMP_GATHER_MODE,2,3,3,,,,,,3,3,0 +CF_SCAN_WORKGROUP_SIZE,512,,,,,,,,,,0 diff --git a/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json b/GPU/GPUTracking/Definitions/Parameters/GPUParameters.json.example similarity index 100% rename from GPU/GPUTracking/Definitions/Parameters/GPUParameters.json rename to GPU/GPUTracking/Definitions/Parameters/GPUParameters.json.example diff --git a/GPU/GPUTracking/Standalone/cmake/config.cmake b/GPU/GPUTracking/Standalone/cmake/config.cmake index ca723063b6d3b..9355311db617c 100644 --- a/GPU/GPUTracking/Standalone/cmake/config.cmake +++ b/GPU/GPUTracking/Standalone/cmake/config.cmake @@ -41,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/cmake/gpu_param_header_generator.cmake b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake index 0c3e905a697c0..5bf1454cb31d8 100644 --- a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake +++ b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake @@ -73,7 +73,6 @@ function(generate_macros json_content output types arch_list arch_list_output) endfunction() function(generate_gpu_param_header GPU_ARCH OUT_HEADER) - set(GPU_PARAM_JSON ${CMAKE_CURRENT_SOURCE_DIR}/Definitions/Parameters/GPUParameters.json) set(TARGET_ARCH "UNKNOWN") if(GPU_ARCH STREQUAL "AUTO") detect_gpu_arch("ALL") From fe395fea62618db410e4703f00e9f233e5a6ac44 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 5 Feb 2026 22:54:07 +0100 Subject: [PATCH 218/701] GPU CMake: Use FILE GENERATE to generate Default Parameter Headers to track changes and rerun if necessary --- GPU/GPUTracking/CMakeLists.txt | 2 + .../cmake/gpu_param_header_generator.cmake | 40 ++++++++----------- 2 files changed, 18 insertions(+), 24 deletions(-) diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index cd17d8f284b13..14118d9b71e9c 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -112,6 +112,8 @@ if(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}") + get_filename_component(GPU_PARAM_JSON_EXT ${GPU_PARAM_JSON} EXT) string(TOLOWER "${GPU_PARAM_JSON_EXT}" GPU_PARAM_JSON_EXT) if(GPU_PARAM_JSON_EXT STREQUAL .csv) diff --git a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake index 5bf1454cb31d8..e79a96034103d 100644 --- a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake +++ b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake @@ -80,17 +80,15 @@ function(generate_gpu_param_header GPU_ARCH OUT_HEADER) set(TARGET_ARCH ${GPU_ARCH}) endif() file(READ "${GPU_PARAM_JSON}" JSON_CONTENT) - set(TMP_HEADER "${OUT_HEADER}.tmp") - file(WRITE "${TMP_HEADER}" "#ifndef GPUDEFPARAMETERSDEFAULTS_H\n#define GPUDEFPARAMETERSDEFAULTS_H\n\n") - file(APPEND "${TMP_HEADER}" "// This file is auto-generated from gpu_params.json. Do not edit directly.\n") + set(TMP_HEADER "#ifndef GPUDEFPARAMETERSDEFAULTS_H\n#define GPUDEFPARAMETERSDEFAULTS_H\n\n") + string(APPEND TMP_HEADER "// This file is auto-generated from gpu_params.json. Do not edit directly.\n") string(REPLACE "," ";" ARCH_LIST "${TARGET_ARCH}") - file(APPEND "${TMP_HEADER}" "// Architectures: ${TARGET_ARCH}\n\n") - file(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 "// 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") # Types set(TYPES CORE LB PAR) # Per architecture definitions - set(_first TRUE) generate_macros("${JSON_CONTENT}" TMP_OUTPUT "${TYPES}" "${ARCH_LIST};default;default_cpu" "JSON_ARCHITECTURES") list(FIND ARCH_LIST "ALL" do_all_architectures) if(ARGC GREATER 2) @@ -99,31 +97,25 @@ function(generate_gpu_param_header GPU_ARCH OUT_HEADER) if(do_all_architectures GREATER -1) set(ARCH_LIST ${JSON_ARCHITECTURES}) endif() + string(APPEND TMP_HEADER "#if 0\n") foreach(ARCH IN LISTS ARCH_LIST) - if(_first) - file(APPEND "${TMP_HEADER}" "#if defined(GPUCA_GPUTYPE_${ARCH})\n\n") - set(_first FALSE) - else() - file(APPEND "${TMP_HEADER}" "#elif defined(GPUCA_GPUTYPE_${ARCH})\n\n") - endif() - file(APPEND "${TMP_HEADER}" ${TMP_OUTPUT_${ARCH}}) + string(APPEND TMP_HEADER "\n#elif defined(GPUCA_GPUTYPE_${ARCH})\n") + string(APPEND TMP_HEADER ${TMP_OUTPUT_${ARCH}}) endforeach() - if(NOT _first) - file(APPEND "${TMP_HEADER}" "#else\n#error GPU TYPE NOT SET\n#endif\n") - endif() + string(APPEND TMP_HEADER "#else\n#error GPU TYPE NOT SET\n#endif\n") # Default parameters - file(APPEND "${TMP_HEADER}" "\n// Default parameters if not defined for the target architecture\n\n") - file(APPEND "${TMP_HEADER}" ${TMP_OUTPUT_default}) - file(APPEND "${TMP_HEADER}" "#endif // defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_GENRTC) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS)\n\n") + string(APPEND TMP_HEADER "\n// Default parameters if not defined for the target architecture\n\n") + string(APPEND TMP_HEADER ${TMP_OUTPUT_default}) + string(APPEND TMP_HEADER "#endif // defined(GPUCA_GPUCODE) && !defined(GPUCA_GPUCODE_GENRTC) && !defined(GPUCA_GPUCODE_NO_LAUNCH_BOUNDS)\n\n") # CPU fallback - file(APPEND "${TMP_HEADER}" "#ifndef GPUCA_GPUCODE_GENRTC // Defaults for non-LB parameters also for CPU fallback\n\n") - file(APPEND "${TMP_HEADER}" ${TMP_OUTPUT_default_cpu}) - file(APPEND "${TMP_HEADER}" "\n#endif // GPUCA_GPUCODE_GENRTC\n") + string(APPEND TMP_HEADER "#ifndef GPUCA_GPUCODE_GENRTC // Defaults for non-LB parameters also for CPU fallback\n\n") + string(APPEND TMP_HEADER ${TMP_OUTPUT_default_cpu}) + string(APPEND TMP_HEADER "\n#endif // GPUCA_GPUCODE_GENRTC\n") - file(APPEND "${TMP_HEADER}" "\n#endif // GPUDEFPARAMETERSDEFAULTS_H\n") - file(RENAME "${TMP_HEADER}" "${OUT_HEADER}") + string(APPEND TMP_HEADER "\n#endif // GPUDEFPARAMETERSDEFAULTS_H\n") + file(GENERATE OUTPUT "${OUT_HEADER}" CONTENT "${TMP_HEADER}") message(STATUS "Generated ${OUT_HEADER}") add_custom_target(GPU_PARAM_HEADER_${GPU_ARCH}_ALL ALL DEPENDS ${OUT_HEADER} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/gpu_param_header_generator.cmake ${GPU_PARAM_JSON}) endfunction() From 6c63d01ba5c76408a9000db3a5c45cab39ea4611 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Fri, 6 Feb 2026 09:46:34 +0100 Subject: [PATCH 219/701] GPU CSV to JSON converter: Workaround to be compatible to MacOS --- GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh b/GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh index ae9d3b7704284..373bd18ba7cd4 100755 --- a/GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh +++ b/GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh @@ -2,7 +2,13 @@ [[ -z $1 ]] && { echo "Usage: csv_to_json.sh CSV_FILE"; exit 1; } -awk -vFPAT='([^,]*)|(\"([^\"]|\"\")*\")' \ +DELIM=$'\xFF' +sed -E \ + ':loop + s/^(([^"]*"[^"]*")*[^"]*),/\1'$DELIM'/; + t loop' \ + $1 | \ +awk -F$DELIM \ 'BEGIN { print "{" } { @@ -42,5 +48,4 @@ awk -vFPAT='([^,]*)|(\"([^\"]|\"\")*\")' \ if (paramprinted) print "\n }" if (catprinted) print " }" print "}" - }' \ - $1 + }' From c2cae5e77332edc5f876e2ff8de9d78f494fd795 Mon Sep 17 00:00:00 2001 From: ddobrigk Date: Sat, 7 Feb 2026 13:01:30 +0100 Subject: [PATCH 220/701] Add ability to retain TrackQA for all global tracks (#15010) * Add ability to retain TrackQA for all global tracks * Do check in one go for writeQAData * Change default to false * Change actual defaults to false --- .../AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h | 1 + Detectors/AOD/src/AODProducerWorkflowSpec.cxx | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h index 615a7f96de13e..2d16f343dc1eb 100644 --- a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h +++ b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h @@ -237,6 +237,7 @@ class AODProducerWorkflowDPL : public Task bool mThinTracks{false}; bool mPropTracks{false}; bool mPropMuons{false}; + float mTrackQCKeepGlobalTracks{false}; float mTrackQCFraction{0.00}; int64_t mTrackQCNTrCut{4}; float mTrackQCDCAxy{3.}; diff --git a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx index b18514949114d..6dcb702791b43 100644 --- a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx +++ b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx @@ -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 && mTrackQCKeepGlobalTracks); auto extraInfoHolder = processBarrelTrack(collisionID, collisionBC, trackIndex, data, bcsMap); if (writeQAData) { @@ -1719,6 +1719,7 @@ void AODProducerWorkflowDPL::init(InitContext& ic) LOGP(warn, "Specified non-default empty streamer mask!"); } } + mTrackQCKeepGlobalTracks = ic.options().get("trackqc-keepglobaltracks"); mTrackQCFraction = ic.options().get("trackqc-fraction"); mTrackQCNTrCut = ic.options().get("trackqc-NTrCut"); mTrackQCDCAxy = ic.options().get("trackqc-tpc-dca"); @@ -3348,6 +3349,7 @@ 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-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"}}, From 1dedc84cef1cc35cc858e31d47da7da51d361ecd Mon Sep 17 00:00:00 2001 From: shahor02 Date: Sat, 7 Feb 2026 13:02:06 +0100 Subject: [PATCH 221/701] Store TPC track A/C side info in the AO2D TrackExtra.fFlags unused bits (#15014) * Store TPC track A/C side info in TrackExtra.fFlags unused bits * Add dynamic columns hasTPCSideA/C, hasTPCSideAOnly/COnly, hasTPCBothSides --- Detectors/AOD/src/AODProducerWorkflowSpec.cxx | 6 ++++++ Framework/Core/include/Framework/AnalysisDataModel.h | 10 ++++++++++ Framework/Core/include/Framework/DataTypes.h | 2 ++ 3 files changed, 18 insertions(+) diff --git a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx index 6dcb702791b43..be169ad4be19d 100644 --- a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx +++ b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx @@ -2646,6 +2646,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; 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/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 { From a9e312faaff4b436fdf839ea5bc45ed4c0049a4a Mon Sep 17 00:00:00 2001 From: David Rohr Date: Fri, 6 Feb 2026 10:37:15 +0100 Subject: [PATCH 222/701] GPU: Generate GPU parameter files only if GPU build is actually enabled --- GPU/GPUTracking/CMakeLists.txt | 48 +++++++++---------- .../Standalone/tools/dumpGPUDefParam.C | 2 +- 2 files changed, 24 insertions(+), 26 deletions(-) diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index 14118d9b71e9c..4ff1355672d7c 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -428,31 +428,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_FILES "") -set(GPU_ARCH_PARAMS_HEADER ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch/GPUDefParametersDefaults_OnTheFly.h) -generate_gpu_param_header("ALL" ${GPU_ARCH_PARAMS_HEADER} "GPU_CONST_PARAM_ARCHITECTUES") -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 \"${GPU_ARCH_PARAMS_HEADER}\"\\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_ARCH_PARAMS_HEADER} - ${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") @@ -473,6 +448,29 @@ 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 "") + set(GPU_ARCH_PARAMS_HEADER ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch/GPUDefParametersDefaults_OnTheFly.h) + generate_gpu_param_header("ALL" ${GPU_ARCH_PARAMS_HEADER} "GPU_CONST_PARAM_ARCHITECTUES") + 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 \"${GPU_ARCH_PARAMS_HEADER}\"\\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_ARCH_PARAMS_HEADER} + ${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/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 From acd7f3bcd98bb944f2e0ff44b855c342161cfe2c Mon Sep 17 00:00:00 2001 From: David Rohr Date: Fri, 6 Feb 2026 12:24:28 +0100 Subject: [PATCH 223/701] GPU Parameter CSV: sort such that defaults are first --- .../Definitions/Parameters/GPUParameters.csv | 140 +++++++++--------- .../Definitions/Parameters/json_to_csv.python | 1 + 2 files changed, 71 insertions(+), 70 deletions(-) diff --git a/GPU/GPUTracking/Definitions/Parameters/GPUParameters.csv b/GPU/GPUTracking/Definitions/Parameters/GPUParameters.csv index 5afa99554f5d0..fc27de72ea2f1 100644 --- a/GPU/GPUTracking/Definitions/Parameters/GPUParameters.csv +++ b/GPU/GPUTracking/Definitions/Parameters/GPUParameters.csv @@ -1,72 +1,72 @@ -Architecture,default,MI100,VEGA,TAHITI,TESLA,FERMI,PASCAL,KEPLER,AMPERE,TURING,default_cpu +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, +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]", +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]", +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,,,,,,,, +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,, +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,,,,,,,,,, @@ -92,22 +92,22 @@ 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, +GPUTPCGMMergerFinalize_0,256,,256,,,,,,,256,256 +GPUTPCGMMergerFinalize_1,256,,256,,,,,,,256,256 +GPUTPCGMMergerFinalize_2,256,,256,,,,,,,256,256 ,,,,,,,,,,, PAR:,,,,,,,,,,, -AMD_EUS_PER_CU,0,4,4,,,,,,,,0 -SORT_STARTHITS,1,,,,,,,,,,0 -NEIGHBOURS_FINDER_MAX_NNEIGHUP,6,10,4,,,,,,4,4,0 -NEIGHBOURS_FINDER_UNROLL_GLOBAL,4,4,2,,,,,,,,0 -NEIGHBOURS_FINDER_UNROLL_SHARED,1,0,0,,,,,,,,0 -TRACKLET_SELECTOR_HITS_REG_SIZE,12,9,27,,,,,,20,20,0 -ALTERNATE_BORDER_SORT,0,1,1,,,,,,1,1,0 -SORT_BEFORE_FIT,0,1,1,,,,,,1,1,0 -NO_ATOMIC_PRECHECK,0,1,1,,,,,,1,1,0 -DEDX_STORAGE_TYPE,"""float""","""uint16_t""","""uint16_t""",,,,,,"""uint16_t""","""uint16_t""","""float""" -MERGER_INTERPOLATION_ERROR_TYPE,"""float""","""half""","""half""",,,,,,"""half""","""half""","""float""" -COMP_GATHER_KERNEL,0,4,4,,,,,,4,4,0 -COMP_GATHER_MODE,2,3,3,,,,,,3,3,0 -CF_SCAN_WORKGROUP_SIZE,512,,,,,,,,,,0 +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/json_to_csv.python b/GPU/GPUTracking/Definitions/Parameters/json_to_csv.python index a6640239604e0..1ae15662021a3 100755 --- a/GPU/GPUTracking/Definitions/Parameters/json_to_csv.python +++ b/GPU/GPUTracking/Definitions/Parameters/json_to_csv.python @@ -27,6 +27,7 @@ for cat in data.values(): 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") From f182c291126a2875decfa42eb1bcad743c18f618 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sat, 7 Feb 2026 14:03:20 +0100 Subject: [PATCH 224/701] GPU CMake: Better detection of GPU optimization setting from architecture string --- dependencies/FindO2GPU.cmake | 48 +++++++++++++++++++++++++----------- 1 file changed, 33 insertions(+), 15 deletions(-) diff --git a/dependencies/FindO2GPU.cmake b/dependencies/FindO2GPU.cmake index ec6b7323ad5d1..928454f93b4f8 100644 --- a/dependencies/FindO2GPU.cmake +++ b/dependencies/FindO2GPU.cmake @@ -46,30 +46,48 @@ endif() function(detect_gpu_arch backend) # Detect GPU architecture, optionally filterring by backend - if(CUDA_COMPUTETARGET AND CUDA_COMPUTETARGET MATCHES "86|89") + 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) - message(STATUS "Using optimized CUDA settings for Ampere GPU") - elseif(CUDA_COMPUTETARGET AND CUDA_COMPUTETARGET MATCHES "75") + elseif(CUDA_FIRST_TARGET GREATER_EQUAL 75) set(CUDA_TARGET TURING) - message(STATUS "Using optimized CUDA settings for Turing GPU") + 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() - set(CUDA_TARGET AMPERE) - message(STATUS "Defaulting optimized CUDA settings for Ampere GPU") + set(CUDA_TARGET TESLA) endif() + message(STATUS "Using optimized CUDA settings for ${CUDA_TARGET} GPU (sm_${CUDA_FIRST_TARGET})") - if(HIP_AMDGPUTARGET AND HIP_AMDGPUTARGET MATCHES "gfx906") - set(HIP_TARGET VEGA) - message(STATUS "Using optimized HIP settings for MI50 GPU") - elseif(HIP_AMDGPUTARGET AND HIP_AMDGPUTARGET MATCHES "gfx908") - set(HIP_TARGET MI100) - message(STATUS "Using optimized HIP settings for MI100 GPU") - elseif(HIP_AMDGPUTARGET AND HIP_AMDGPUTARGET MATCHES "gfx90a") + 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) - message(STATUS "Using optimized HIP settings for MI210 GPU") + elseif(HIP_FIRST_TARGET_PADDED STRGREATER_EQUAL "0906") + set(HIP_TARGET VEGA) else() set(HIP_TARGET VEGA) - message(STATUS "Defaulting optimized HIP settings for VEGA GPU") endif() + message(STATUS "Using optimized HIP settings for ${HIP_TARGET} GPU (gfx${HIP_FIRST_TARGET})") if(backend STREQUAL "CUDA") # CUDA filter set(TARGET_ARCH "${CUDA_TARGET}" PARENT_SCOPE) From 6a9fd1e145c46fbe21c5a62999e59c8bc288a4b1 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sun, 8 Feb 2026 00:05:43 +0100 Subject: [PATCH 225/701] GPU CMake: Write separate headers for GPU device and non-device parameters, use same headers for compilation and for parameter file generation --- GPU/GPUTracking/Base/cuda/CMakeLists.txt | 2 +- GPU/GPUTracking/Base/hip/CMakeLists.txt | 2 +- GPU/GPUTracking/CMakeLists.txt | 13 ++- .../cmake/gpu_param_header_generator.cmake | 106 +++++++++--------- dependencies/FindO2GPU.cmake | 17 ++- 5 files changed, 72 insertions(+), 68 deletions(-) diff --git a/GPU/GPUTracking/Base/cuda/CMakeLists.txt b/GPU/GPUTracking/Base/cuda/CMakeLists.txt index 226bacbf88157..27c3d24cd079f 100644 --- a/GPU/GPUTracking/Base/cuda/CMakeLists.txt +++ b/GPU/GPUTracking/Base/cuda/CMakeLists.txt @@ -74,7 +74,7 @@ add_custom_command( COMMAND cat ${GPUDIR}/Base/GPUStdSystemHeaders.h >> ${GPU_RTC_BIN}.src COMMAND ${CMAKE_CUDA_COMPILER} ${GPU_RTC_DEFINES} ${GPU_RTC_INCLUDES} -std=c++${CMAKE_CUDA_STANDARD} -D__CUDA_ARCH__=${RTC_CUDA_ARCH} -Wno-deprecated-gpu-targets -D__CUDACC__ -x c++ -M -MD -MT ${GPU_RTC_BIN}.src -MF ${GPU_RTC_BIN}.src.d ${GPU_RTC_SRC} COMMAND ${CMAKE_CUDA_COMPILER} ${GPU_RTC_DEFINES} ${GPU_RTC_INCLUDES} -std=c++${CMAKE_CUDA_STANDARD} -D__CUDA_ARCH__=${RTC_CUDA_ARCH} -Wno-deprecated-gpu-targets -D__CUDACC__ -x c++ -E -Xcompiler "-nostdinc -P" ${GPU_RTC_SRC} >> ${GPU_RTC_BIN}.src - DEPENDS ${GPU_RTC_SRC} ${GPUDIR}/Base/GPUStdSystemHeaders.h ${GPUDIR}/Base/cuda/GPUReconstructionCUDAIncludesSystem.h ${GPUDIR}/Base/GPUStdSystemHeaders.h GPU_PARAM_HEADER_AUTO_ALL + DEPENDS ${GPU_RTC_SRC} ${GPUDIR}/Base/GPUStdSystemHeaders.h ${GPUDIR}/Base/cuda/GPUReconstructionCUDAIncludesSystem.h ${GPUDIR}/Base/GPUStdSystemHeaders.h GPU_PARAM_HEADER_TARGET DEPFILE ${GPU_RTC_BIN}.src.d COMMAND_EXPAND_LISTS COMMENT "Preparing CUDA RTC source file ${GPU_RTC_BIN}.src" diff --git a/GPU/GPUTracking/Base/hip/CMakeLists.txt b/GPU/GPUTracking/Base/hip/CMakeLists.txt index d148e376abca9..b459a78b5789e 100644 --- a/GPU/GPUTracking/Base/hip/CMakeLists.txt +++ b/GPU/GPUTracking/Base/hip/CMakeLists.txt @@ -125,7 +125,7 @@ add_custom_command( COMMAND cat ${GPUDIR}/Base/hip/GPUReconstructionHIPIncludesSystem.h | grep -v GPUStdSystemHeaders.h >> ${GPU_RTC_BIN}.src COMMAND cat ${GPUDIR}/Base/GPUStdSystemHeaders.h >> ${GPU_RTC_BIN}.src COMMAND ${CMAKE_HIP_COMPILER} ${GPU_RTC_DEFINES} ${GPU_RTC_INCLUDES} -std=c++${CMAKE_HIP_STANDARD} -D__HIPCC__ -D__HIP_DEVICE_COMPILE__ -x c++ -nostdinc -E -P ${GPU_RTC_SRC} -MD -MT ${GPU_RTC_BIN}.src -MF ${GPU_RTC_BIN}.src.d >> ${GPU_RTC_BIN}.src - DEPENDS ${GPU_RTC_SRC} ${GPUDIR}/Base/GPUStdSystemHeaders.h ${GPUDIR}/Base/hip/GPUReconstructionHIPIncludesSystem.h ${GPUDIR}/Base/GPUStdSystemHeaders.h ${MODULE}_HIPIFIED GPU_PARAM_HEADER_AUTO_ALL + DEPENDS ${GPU_RTC_SRC} ${GPUDIR}/Base/GPUStdSystemHeaders.h ${GPUDIR}/Base/hip/GPUReconstructionHIPIncludesSystem.h ${GPUDIR}/Base/GPUStdSystemHeaders.h ${MODULE}_HIPIFIED GPU_PARAM_HEADER_TARGET DEPFILE ${GPU_RTC_BIN}.src.d COMMAND_EXPAND_LISTS COMMENT "Preparing HIP RTC source file ${GPU_RTC_BIN}.src" diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index 4ff1355672d7c..786774c16971b 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -130,7 +130,8 @@ set(ON_THE_FLY_DIR ${CMAKE_CURRENT_BINARY_DIR}/include_gpu_onthefly) file(MAKE_DIRECTORY ${ON_THE_FLY_DIR}) include(cmake/gpu_param_header_generator.cmake) set(GPU_DEFAULT_PARAMS_HEADER ${ON_THE_FLY_DIR}/GPUDefParametersDefaults.h) -generate_gpu_param_header("AUTO" ${GPU_DEFAULT_PARAMS_HEADER}) # generate header with default GPU parameters, arch selected by CMake variables +set(GPU_DEFAULT_PARAMS_HEADER_DEVICE ${ON_THE_FLY_DIR}/GPUDefParametersDefaultsDevice.h) +generate_gpu_param_header("ALL" ${GPU_DEFAULT_PARAMS_HEADER} ${GPU_DEFAULT_PARAMS_HEADER_DEVICE} GPU_CONST_PARAM_ARCHITECTUES) # generate header with default GPU parameters, arch selected by CMake variables set(HDRS_INSTALL ${HDRS_CINT_O2} @@ -161,6 +162,7 @@ set(HDRS_INSTALL Debug/GPUROOTDump.h Definitions/GPUDefConstantsAndSettings.h ${GPU_DEFAULT_PARAMS_HEADER} + ${GPU_DEFAULT_PARAMS_HEADER_DEVICE} Definitions/GPUDefParametersWrapper.h Definitions/GPUDefParametersConstants.h Definitions/GPUDef.h @@ -449,20 +451,19 @@ if(CUDA_ENABLED OR OPENCL_ENABLED OR HIP_ENABLED) message(WARNING "GPU Tracking disabled on MacOS") else() make_directory(${CMAKE_CURRENT_BINARY_DIR}/genGPUArch) - set(GPU_CONST_PARAM_FILES "") - set(GPU_ARCH_PARAMS_HEADER ${CMAKE_CURRENT_BINARY_DIR}/genGPUArch/GPUDefParametersDefaults_OnTheFly.h) - generate_gpu_param_header("ALL" ${GPU_ARCH_PARAMS_HEADER} "GPU_CONST_PARAM_ARCHITECTUES") + 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 \"${GPU_ARCH_PARAMS_HEADER}\"\\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'" + "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_ARCH_PARAMS_HEADER} + 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}") diff --git a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake index e79a96034103d..31d395615a5ed 100644 --- a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake +++ b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake @@ -12,17 +12,31 @@ # file gpu_param_header_generator.cmake # author Gabriele Cimador -function(generate_macros json_content output types arch_list arch_list_output) - foreach(arch IN LISTS arch_list) - set(OUTPUT_TMP_${arch} "") - endforeach() - set(arch_list_output_tmp) - list(FIND arch_list "ALL" do_all_architectures) - foreach(TYPE IN LISTS types) - string(JSON n_params LENGTH "${json_content}" "${TYPE}") +function(generate_gpu_param_header 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() + file(READ "${GPU_PARAM_JSON}" JSON_CONTENT) + + # Types + set(TYPES CORE LB PAR) + set(ARCH_LIST_EXT "${ARCH_LIST};default;default_cpu") + # Per architecture definitions + set(JSON_ARCHITECTURES) + + 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 param_name MEMBER "${JSON_CONTENT}" "${TYPE}" "${i}") string(JSON n_archs LENGTH "${JSON_CONTENT}" "${TYPE}" "${param_name}") math(EXPR last_arch "${n_archs} - 1") foreach(iArch RANGE 0 ${last_arch}) @@ -31,12 +45,12 @@ function(generate_macros json_content output types arch_list arch_list_output) message(FATAL_ERROR "Bogus entry ${param_name} for ${arch}") endif() if(do_all_architectures GREATER -1) - if(arch_list_output AND NOT arch MATCHES ^default) - list(APPEND arch_list_output_tmp "${arch}") + if(NOT arch MATCHES ^default) + list(APPEND JSON_ARCHITECTURES "${arch}") endif() set(list_idx 0) else() - list(FIND arch_list "${arch}" list_idx) + 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}") @@ -54,68 +68,58 @@ function(generate_macros json_content output types arch_list arch_list_output) set(MACRO_DEFINITION "#define ${MACRO_NAME} ${vals}") if(arch MATCHES ^default) # fallback defaults are wrapped in #ifndef - string(APPEND OUTPUT_TMP_${arch} "#ifndef ${MACRO_NAME}\n ${MACRO_DEFINITION}\n#endif\n\n") + string(APPEND generate_gpu_param_header_OUTPUT_TMP_${arch} "#ifndef ${MACRO_NAME}\n ${MACRO_DEFINITION}\n#endif\n\n") else() - string(APPEND OUTPUT_TMP_${arch} "${MACRO_DEFINITION}\n") + string(APPEND generate_gpu_param_header_OUTPUT_TMP_${arch} "${MACRO_DEFINITION}\n") endif() endif() endforeach() endforeach() endforeach() - foreach(arch IN LISTS arch_list) - set(${output}_${arch} "${OUTPUT_TMP_${arch}}" PARENT_SCOPE) - endforeach() - if(arch_list_output) - list(REMOVE_DUPLICATES arch_list_output_tmp) - list(SORT arch_list_output_tmp) - set(${arch_list_output} "${arch_list_output_tmp}" PARENT_SCOPE) - endif() -endfunction() -function(generate_gpu_param_header GPU_ARCH OUT_HEADER) - set(TARGET_ARCH "UNKNOWN") - if(GPU_ARCH STREQUAL "AUTO") - detect_gpu_arch("ALL") - else() - set(TARGET_ARCH ${GPU_ARCH}) + list(REMOVE_DUPLICATES JSON_ARCHITECTURES) + list(SORT JSON_ARCHITECTURES) + if(ARGC GREATER 3) + set(${ARGV3} "${JSON_ARCHITECTURES}" PARENT_SCOPE) endif() - file(READ "${GPU_PARAM_JSON}" JSON_CONTENT) + 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(REPLACE "," ";" ARCH_LIST "${TARGET_ARCH}") - string(APPEND TMP_HEADER "// Architectures: ${TARGET_ARCH}\n\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") - # Types - set(TYPES CORE LB PAR) - # Per architecture definitions - generate_macros("${JSON_CONTENT}" TMP_OUTPUT "${TYPES}" "${ARCH_LIST};default;default_cpu" "JSON_ARCHITECTURES") - list(FIND ARCH_LIST "ALL" do_all_architectures) - if(ARGC GREATER 2) - set(${ARGV2} "${JSON_ARCHITECTURES}" PARENT_SCOPE) - endif() - if(do_all_architectures GREATER -1) - set(ARCH_LIST ${JSON_ARCHITECTURES}) - endif() - string(APPEND TMP_HEADER "#if 0\n") + string(APPEND TMP_HEADER_DEVICE "#if 0\n") foreach(ARCH IN LISTS ARCH_LIST) - string(APPEND TMP_HEADER "\n#elif defined(GPUCA_GPUTYPE_${ARCH})\n") - string(APPEND TMP_HEADER ${TMP_OUTPUT_${ARCH}}) + 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 "#else\n#error GPU TYPE NOT SET\n#endif\n") + 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 ${TMP_OUTPUT_default}) + 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 ${TMP_OUTPUT_default_cpu}) + 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}") - message(STATUS "Generated ${OUT_HEADER}") - add_custom_target(GPU_PARAM_HEADER_${GPU_ARCH}_ALL ALL DEPENDS ${OUT_HEADER} ${CMAKE_CURRENT_SOURCE_DIR}/cmake/gpu_param_header_generator.cmake ${GPU_PARAM_JSON}) + file(GENERATE OUTPUT "${OUT_HEADER_DEVICE}" CONTENT "${TMP_HEADER_DEVICE}") + message(STATUS "Generated ${OUT_HEADER} and ${OUT_HEADER_DEVICE}") + add_custom_target(GPU_PARAM_HEADER_TARGET ALL DEPENDS ${OUT_HEADER} ${OUT_HEADER_DEVICE} ${GPU_PARAM_JSON}) endfunction() diff --git a/dependencies/FindO2GPU.cmake b/dependencies/FindO2GPU.cmake index 928454f93b4f8..42d0162691c37 100644 --- a/dependencies/FindO2GPU.cmake +++ b/dependencies/FindO2GPU.cmake @@ -93,18 +93,17 @@ function(detect_gpu_arch backend) # Detect GPU architecture, optionally filterri set(TARGET_ARCH "${CUDA_TARGET}" PARENT_SCOPE) elseif(backend STREQUAL "HIP") # HIP filter set(TARGET_ARCH "${HIP_TARGET}" PARENT_SCOPE) - elseif(backend STREQUAL "ALL") # Return enabled backends - set(_archs "") - if(CUDA_ENABLED) - list(APPEND _archs "${CUDA_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) - list(APPEND _archs "${HIP_TARGET}") + if(HIP_ENABLED OR backend STREQUAL "ALL") + list(APPEND TARGET_ARCH "${HIP_TARGET}") endif() - if(OPENCL_ENABLED) - list(APPEND _archs "OPENCL") + if(OPENCL_ENABLED OR backend STREQUAL "ALL") + list(APPEND TARGET_ARCH "OPENCL") endif() - list(JOIN _archs "," TARGET_ARCH) set(TARGET_ARCH "${TARGET_ARCH}" PARENT_SCOPE) else() message(FATAL_ERROR "Unknown backend provided: ${backend}") From f4becde9083aa6bb2dda8837a2a254eec4c55bb9 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sun, 8 Feb 2026 23:25:16 +0100 Subject: [PATCH 226/701] GPU CMake: Clean up some targets, fix if JSON contains 0 architectures --- GPU/GPUTracking/Base/cuda/CMakeLists.txt | 2 +- GPU/GPUTracking/Base/hip/CMakeLists.txt | 2 +- GPU/GPUTracking/CMakeLists.txt | 55 ++++++++-------- .../cmake/gpu_param_header_generator.cmake | 65 ++++++++++--------- 4 files changed, 62 insertions(+), 62 deletions(-) diff --git a/GPU/GPUTracking/Base/cuda/CMakeLists.txt b/GPU/GPUTracking/Base/cuda/CMakeLists.txt index 27c3d24cd079f..6e54187332c9b 100644 --- a/GPU/GPUTracking/Base/cuda/CMakeLists.txt +++ b/GPU/GPUTracking/Base/cuda/CMakeLists.txt @@ -74,7 +74,7 @@ add_custom_command( COMMAND cat ${GPUDIR}/Base/GPUStdSystemHeaders.h >> ${GPU_RTC_BIN}.src COMMAND ${CMAKE_CUDA_COMPILER} ${GPU_RTC_DEFINES} ${GPU_RTC_INCLUDES} -std=c++${CMAKE_CUDA_STANDARD} -D__CUDA_ARCH__=${RTC_CUDA_ARCH} -Wno-deprecated-gpu-targets -D__CUDACC__ -x c++ -M -MD -MT ${GPU_RTC_BIN}.src -MF ${GPU_RTC_BIN}.src.d ${GPU_RTC_SRC} COMMAND ${CMAKE_CUDA_COMPILER} ${GPU_RTC_DEFINES} ${GPU_RTC_INCLUDES} -std=c++${CMAKE_CUDA_STANDARD} -D__CUDA_ARCH__=${RTC_CUDA_ARCH} -Wno-deprecated-gpu-targets -D__CUDACC__ -x c++ -E -Xcompiler "-nostdinc -P" ${GPU_RTC_SRC} >> ${GPU_RTC_BIN}.src - DEPENDS ${GPU_RTC_SRC} ${GPUDIR}/Base/GPUStdSystemHeaders.h ${GPUDIR}/Base/cuda/GPUReconstructionCUDAIncludesSystem.h ${GPUDIR}/Base/GPUStdSystemHeaders.h GPU_PARAM_HEADER_TARGET + DEPENDS ${GPU_RTC_SRC} ${GPUDIR}/Base/GPUStdSystemHeaders.h ${GPUDIR}/Base/cuda/GPUReconstructionCUDAIncludesSystem.h ${GPUDIR}/Base/GPUStdSystemHeaders.h DEPFILE ${GPU_RTC_BIN}.src.d COMMAND_EXPAND_LISTS COMMENT "Preparing CUDA RTC source file ${GPU_RTC_BIN}.src" diff --git a/GPU/GPUTracking/Base/hip/CMakeLists.txt b/GPU/GPUTracking/Base/hip/CMakeLists.txt index b459a78b5789e..50d710fd9d557 100644 --- a/GPU/GPUTracking/Base/hip/CMakeLists.txt +++ b/GPU/GPUTracking/Base/hip/CMakeLists.txt @@ -125,7 +125,7 @@ add_custom_command( COMMAND cat ${GPUDIR}/Base/hip/GPUReconstructionHIPIncludesSystem.h | grep -v GPUStdSystemHeaders.h >> ${GPU_RTC_BIN}.src COMMAND cat ${GPUDIR}/Base/GPUStdSystemHeaders.h >> ${GPU_RTC_BIN}.src COMMAND ${CMAKE_HIP_COMPILER} ${GPU_RTC_DEFINES} ${GPU_RTC_INCLUDES} -std=c++${CMAKE_HIP_STANDARD} -D__HIPCC__ -D__HIP_DEVICE_COMPILE__ -x c++ -nostdinc -E -P ${GPU_RTC_SRC} -MD -MT ${GPU_RTC_BIN}.src -MF ${GPU_RTC_BIN}.src.d >> ${GPU_RTC_BIN}.src - DEPENDS ${GPU_RTC_SRC} ${GPUDIR}/Base/GPUStdSystemHeaders.h ${GPUDIR}/Base/hip/GPUReconstructionHIPIncludesSystem.h ${GPUDIR}/Base/GPUStdSystemHeaders.h ${MODULE}_HIPIFIED GPU_PARAM_HEADER_TARGET + DEPENDS ${GPU_RTC_SRC} ${GPUDIR}/Base/GPUStdSystemHeaders.h ${GPUDIR}/Base/hip/GPUReconstructionHIPIncludesSystem.h ${GPUDIR}/Base/GPUStdSystemHeaders.h ${MODULE}_HIPIFIED DEPFILE ${GPU_RTC_BIN}.src.d COMMAND_EXPAND_LISTS COMMENT "Preparing HIP RTC source file ${GPU_RTC_BIN}.src" diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index 786774c16971b..e52fb80113c00 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -107,32 +107,6 @@ set(SRCS_NO_H SectorTracker/GPUTPCTrackerDump.cxx Global/GPUChainTrackingDebugAndProfiling.cxx Global/GPUChainTrackingIO.cxx) -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}") - -get_filename_component(GPU_PARAM_JSON_EXT ${GPU_PARAM_JSON} EXT) -string(TOLOWER "${GPU_PARAM_JSON_EXT}" GPU_PARAM_JSON_EXT) -if(GPU_PARAM_JSON_EXT STREQUAL .csv) - execute_process( - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/Definitions/Parameters/csv_to_json.sh "${GPU_PARAM_JSON}" - OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/gpu_parameters.json - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} - ) - message(STATUS "Converted ${GPU_PARAM_JSON} to ${CMAKE_CURRENT_BINARY_DIR}/gpu_parameters.json") - set(GPU_PARAM_JSON ${CMAKE_CURRENT_BINARY_DIR}/gpu_parameters.json) -endif() - -set(ON_THE_FLY_DIR ${CMAKE_CURRENT_BINARY_DIR}/include_gpu_onthefly) -file(MAKE_DIRECTORY ${ON_THE_FLY_DIR}) -include(cmake/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("ALL" ${GPU_DEFAULT_PARAMS_HEADER} ${GPU_DEFAULT_PARAMS_HEADER_DEVICE} GPU_CONST_PARAM_ARCHITECTUES) # generate header with default GPU parameters, arch selected by CMake variables - set(HDRS_INSTALL ${HDRS_CINT_O2} ${HDRS_CINT_DATATYPES} @@ -161,8 +135,6 @@ set(HDRS_INSTALL DataTypes/GPUO2ExternalUser.h Debug/GPUROOTDump.h Definitions/GPUDefConstantsAndSettings.h - ${GPU_DEFAULT_PARAMS_HEADER} - ${GPU_DEFAULT_PARAMS_HEADER_DEVICE} Definitions/GPUDefParametersWrapper.h Definitions/GPUDefParametersConstants.h Definitions/GPUDef.h @@ -258,6 +230,8 @@ if(ALIGPU_BUILD_TYPE STREQUAL "O2") 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 @@ -288,6 +262,31 @@ 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}") + +get_filename_component(GPU_PARAM_JSON_EXT ${GPU_PARAM_JSON} EXT) +string(TOLOWER "${GPU_PARAM_JSON_EXT}" GPU_PARAM_JSON_EXT) +if(GPU_PARAM_JSON_EXT STREQUAL .csv) + execute_process( + COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/Definitions/Parameters/csv_to_json.sh "${GPU_PARAM_JSON}" + OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/gpu_parameters.json + WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} + ) + message(STATUS "Converted ${GPU_PARAM_JSON} to ${CMAKE_CURRENT_BINARY_DIR}/gpu_parameters.json") + set(GPU_PARAM_JSON ${CMAKE_CURRENT_BINARY_DIR}/gpu_parameters.json) +endif() + +include(cmake/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("ALL" ${GPU_DEFAULT_PARAMS_HEADER} ${GPU_DEFAULT_PARAMS_HEADER_DEVICE} GPU_CONST_PARAM_ARCHITECTUES) # generate header with default GPU parameters, arch selected by CMake variables +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) diff --git a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake index 31d395615a5ed..0a7b234aa6a18 100644 --- a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake +++ b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake @@ -38,42 +38,44 @@ function(generate_gpu_param_header ARCH_LIST OUT_HEADER OUT_HEADER_DEVICE) 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(do_all_architectures GREATER -1) - if(NOT arch MATCHES ^default) - list(APPEND JSON_ARCHITECTURES "${arch}") + 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() - 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}") + if(do_all_architectures GREATER -1) + if(NOT arch MATCHES ^default) + list(APPEND JSON_ARCHITECTURES "${arch}") + endif() + set(list_idx 0) else() - set(MACRO_NAME "GPUCA_${param_name}") + list(FIND ARCH_LIST_EXT "${arch}" list_idx) 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") + 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() - endif() - endforeach() + endforeach() + endif() endforeach() endforeach() @@ -121,5 +123,4 @@ function(generate_gpu_param_header ARCH_LIST OUT_HEADER OUT_HEADER_DEVICE) file(GENERATE OUTPUT "${OUT_HEADER}" CONTENT "${TMP_HEADER}") file(GENERATE OUTPUT "${OUT_HEADER_DEVICE}" CONTENT "${TMP_HEADER_DEVICE}") message(STATUS "Generated ${OUT_HEADER} and ${OUT_HEADER_DEVICE}") - add_custom_target(GPU_PARAM_HEADER_TARGET ALL DEPENDS ${OUT_HEADER} ${OUT_HEADER_DEVICE} ${GPU_PARAM_JSON}) endfunction() From f1175e1181e24441768ca3f97655786fcadee539 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Mon, 9 Feb 2026 00:03:02 +0100 Subject: [PATCH 227/701] GPU Parameters: Support multiple csv/json files, and merge the parameters into the header on the fly --- GPU/GPUTracking/CMakeLists.txt | 37 +++++--- .../cmake/gpu_param_header_generator.cmake | 95 ++++++++++--------- 2 files changed, 75 insertions(+), 57 deletions(-) diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index e52fb80113c00..082dc1f10b1d6 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -263,28 +263,39 @@ 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}) + set(GPU_PARAM_JSON ${GPUCA_OVERRIDE_PARAMETER_FILE}) else() - set(GPU_PARAM_JSON ${CMAKE_CURRENT_SOURCE_DIR}/Definitions/Parameters/GPUParameters.csv) + set(GPU_PARAM_JSON ${CMAKE_CURRENT_SOURCE_DIR}/Definitions/Parameters/GPUParameters.csv) endif() set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS "${GPU_PARAM_JSON}") -get_filename_component(GPU_PARAM_JSON_EXT ${GPU_PARAM_JSON} EXT) -string(TOLOWER "${GPU_PARAM_JSON_EXT}" GPU_PARAM_JSON_EXT) -if(GPU_PARAM_JSON_EXT STREQUAL .csv) - execute_process( - COMMAND ${CMAKE_CURRENT_SOURCE_DIR}/Definitions/Parameters/csv_to_json.sh "${GPU_PARAM_JSON}" - OUTPUT_FILE ${CMAKE_CURRENT_BINARY_DIR}/gpu_parameters.json - WORKING_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR} +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} to ${CMAKE_CURRENT_BINARY_DIR}/gpu_parameters.json") - set(GPU_PARAM_JSON ${CMAKE_CURRENT_BINARY_DIR}/gpu_parameters.json) -endif() + 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(cmake/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("ALL" ${GPU_DEFAULT_PARAMS_HEADER} ${GPU_DEFAULT_PARAMS_HEADER_DEVICE} GPU_CONST_PARAM_ARCHITECTUES) # generate header with default GPU parameters, arch selected by CMake variables +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, arch selected by CMake variables list(APPEND GENERATED_HEADERS_LIST ${GPU_DEFAULT_PARAMS_HEADER} ${GPU_DEFAULT_PARAMS_HEADER_DEVICE}) set(HDRS_INSTALL ${HDRS_INSTALL} ${GENERATED_HEADERS_LIST}) diff --git a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake index 0a7b234aa6a18..383d194aaa717 100644 --- a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake +++ b/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake @@ -12,7 +12,7 @@ # file gpu_param_header_generator.cmake # author Gabriele Cimador -function(generate_gpu_param_header ARCH_LIST OUT_HEADER OUT_HEADER_DEVICE) +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) @@ -24,7 +24,6 @@ function(generate_gpu_param_header ARCH_LIST OUT_HEADER OUT_HEADER_DEVICE) endif() list(APPEND ARCH_LIST ${TARGET_ARCH}) endif() - file(READ "${GPU_PARAM_JSON}" JSON_CONTENT) # Types set(TYPES CORE LB PAR) @@ -32,57 +31,65 @@ function(generate_gpu_param_header ARCH_LIST OUT_HEADER OUT_HEADER_DEVICE) # Per architecture definitions set(JSON_ARCHITECTURES) - 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(do_all_architectures GREATER -1) - if(NOT arch MATCHES ^default) - list(APPEND JSON_ARCHITECTURES "${arch}") + 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() - 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}") + if(arch MATCHES ^default AND GPU_PARAM_JSON_N_FILES GREATER 0) + message(FATAL_ERROR "Defaults must be provided in first parameter file") 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") + if(do_all_architectures GREATER -1) + if(NOT arch MATCHES ^default) + list(APPEND JSON_ARCHITECTURES "${arch}") + endif() + set(list_idx 0) else() - string(APPEND generate_gpu_param_header_OUTPUT_TMP_${arch} "${MACRO_DEFINITION}\n") + 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() - endif() - endforeach() - 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 3) - set(${ARGV3} "${JSON_ARCHITECTURES}" PARENT_SCOPE) + if(ARGC GREATER 4) + set(${ARGV4} "${JSON_ARCHITECTURES}" PARENT_SCOPE) endif() if(do_all_architectures GREATER -1) list(REMOVE_ITEM ARCH_LIST "ALL") From b77438bbd5aa8d07b4458b5eb80bf519ba9f6ec4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Mon, 9 Feb 2026 09:25:15 +0100 Subject: [PATCH 228/701] Utilities: Delete unused files (#15039) --- .../DataCompression/CodingModelDispatcher.h | 380 ------------ .../DataCompression/runtime_container.h | 583 ------------------ .../tpccluster_parameter_model.h | 101 --- .../internal/containers/HistogramInterface.h | 88 --- 4 files changed, 1152 deletions(-) delete mode 100644 Utilities/DataCompression/include/DataCompression/CodingModelDispatcher.h delete mode 100644 Utilities/DataCompression/include/DataCompression/runtime_container.h delete mode 100644 Utilities/DataCompression/tpccluster_parameter_model.h delete mode 100644 Utilities/rANS/include/rANS/internal/containers/HistogramInterface.h 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/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_ */ From c5ead8881e60c064d0c804889da12e1c55edaba4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Mon, 9 Feb 2026 09:46:24 +0100 Subject: [PATCH 229/701] Algorithm: Delete unused files (#15025) --- Algorithm/include/Algorithm/BitstreamReader.h | 290 ------------------ Algorithm/test/test_BitstreamReader.cxx | 121 -------- 2 files changed, 411 deletions(-) delete mode 100644 Algorithm/include/Algorithm/BitstreamReader.h delete mode 100644 Algorithm/test/test_BitstreamReader.cxx 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 From 7830e9c54db7ef479c0c8710c4b934c27312e2d7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Mon, 9 Feb 2026 10:10:31 +0100 Subject: [PATCH 230/701] DataFormats: Delete unused files (#15029) --- .../include/Headers/SubframeMetadata.h | 68 ----- .../HLT/include/AliceHLT/TPCRawCluster.h | 232 ------------------ 2 files changed, 300 deletions(-) delete mode 100644 DataFormats/Headers/include/Headers/SubframeMetadata.h delete mode 100644 DataFormats/Legacy/HLT/include/AliceHLT/TPCRawCluster.h diff --git a/DataFormats/Headers/include/Headers/SubframeMetadata.h b/DataFormats/Headers/include/Headers/SubframeMetadata.h deleted file mode 100644 index 255fa0ceb8db6..0000000000000 --- a/DataFormats/Headers/include/Headers/SubframeMetadata.h +++ /dev/null @@ -1,68 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef SUBFRAMEMETADATA_H -#define SUBFRAMEMETADATA_H - -#include - -namespace o2 -{ -namespace data_flow -{ - -struct SubframeMetadata { - // TODO: replace with timestamp struct - // IDEA: not timeframeID because can be calculcated with helper function - // QUESTION: isn't the duration set to ~22ms? - uint64_t startTime = ~(uint64_t)0; - uint64_t duration = ~(uint64_t)0; - - //further meta data to be added - - // putting data specific to FLP origin - int flpIndex; -}; - -// Helper function to derive the timeframe id from the actual timestamp. -// Timestamp is in nanoseconds. Each Timeframe is ~22ms i.e. 2^17 nanoseconds, -// so we can get a unique id by dividing by the timeframe period and masking -// the lower 16 bits. Overlaps will only happen every ~ 22 minutes. -constexpr uint16_t - timeframeIdFromTimestamp(uint64_t timestamp, uint64_t timeFrameDuration) -{ - return (timestamp / timeFrameDuration) & 0xffff; -} - -// A Mockup class to describe some TPC-like payload -struct TPCTestCluster { - float x = 0.f; - float y = 0.f; - float z = 1.5f; - float q = 0.; - uint64_t timeStamp; // the time this thing was digitized/recorded -}; - -struct TPCTestPayload { - std::vector clusters; -}; - -// a mockup class to describe some "ITS" payload -struct ITSRawData { - float x = -1.; - float y = 1.; - uint64_t timeStamp; -}; - -} // namespace data_flow -} // namespace o2 - -#endif diff --git a/DataFormats/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 From 99d03b950769c309102f0a7d2cb7805e329f15ee Mon Sep 17 00:00:00 2001 From: Ernst Hellbar Date: Fri, 9 Jan 2026 15:15:52 +0000 Subject: [PATCH 231/701] Event Display: remove return statements from handled filesystem exceptions --- EventVisualisation/Base/src/DirectoryLoader.cxx | 3 --- 1 file changed, 3 deletions(-) diff --git a/EventVisualisation/Base/src/DirectoryLoader.cxx b/EventVisualisation/Base/src/DirectoryLoader.cxx index f2f5a421c0ef9..50b3de61295a3 100644 --- a/EventVisualisation/Base/src/DirectoryLoader.cxx +++ b/EventVisualisation/Base/src/DirectoryLoader.cxx @@ -37,7 +37,6 @@ deque DirectoryLoader::load(const std::string& path, const std::string& } } catch (std::filesystem::filesystem_error const& ex) { LOGF(error, "filesystem problem during DirectoryLoader::load: %s", ex.what()); - return result; } // comparison with safety if marker not in the filename (-1+1 gives 0) std::sort(result.begin(), result.end(), @@ -62,7 +61,6 @@ bool DirectoryLoader::canCreateNextFile(const std::vector& paths, c } } catch (std::filesystem::filesystem_error const& ex) { LOGF(error, "filesystem problem during DirectoryLoader::canCreateNextFile: %s", ex.what()); - return false; } } @@ -103,7 +101,6 @@ deque DirectoryLoader::load(const std::vector& paths, const } } catch (std::filesystem::filesystem_error const& ex) { LOGF(error, "filesystem problem during DirectoryLoader::load: %s", ex.what()); - return result; } // comparison with safety if marker not in the filename (-1+1 gives 0) std::sort(result.begin(), result.end(), From 28dcfc4269c76c97a71081f8f4835d0e9ce7e196 Mon Sep 17 00:00:00 2001 From: Ernst Hellbar Date: Thu, 15 Jan 2026 15:17:28 +0100 Subject: [PATCH 232/701] Event Display: add OnlineMode and safety checks --- .../Base/src/DirectoryLoader.cxx | 25 +++++++++++++------ 1 file changed, 17 insertions(+), 8 deletions(-) diff --git a/EventVisualisation/Base/src/DirectoryLoader.cxx b/EventVisualisation/Base/src/DirectoryLoader.cxx index 50b3de61295a3..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 @@ -65,10 +67,13 @@ bool DirectoryLoader::canCreateNextFile(const std::vector& paths, c } // 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) { @@ -113,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; } From 1452f31b25e3ec1633e02e1a8f6151210b6bb70c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Mon, 9 Feb 2026 23:46:22 +0100 Subject: [PATCH 233/701] Framework: Delete unused files (#15038) --- .../Framework/DataProcessingStateManager.h | 35 ---- Framework/Foundation/src/Traits.cxx | 10 -- Framework/TestWorkflows/src/dummy.cxx | 10 -- Framework/TestWorkflows/src/o2_sim_its_ALP3.h | 25 --- Framework/TestWorkflows/src/o2_sim_tpc.cxx | 157 ------------------ Framework/TestWorkflows/src/o2_sim_tpc.h | 25 --- .../src/test_o2ITSCluserizer.cxx | 32 ---- .../src/test_o2TPCSimulation.cxx | 31 ---- Framework/Utils/test/DPLBroadcasterMerger.cxx | 147 ---------------- Framework/Utils/test/DPLBroadcasterMerger.h | 28 ---- Framework/Utils/test/DPLOutputTest.h | 28 ---- .../Utils/test/test_DPLBroadcasterMerger.cxx | 31 ---- Framework/Utils/test/test_DPLOutputTest.cxx | 31 ---- 13 files changed, 590 deletions(-) delete mode 100644 Framework/Core/include/Framework/DataProcessingStateManager.h delete mode 100644 Framework/Foundation/src/Traits.cxx delete mode 100644 Framework/TestWorkflows/src/dummy.cxx delete mode 100644 Framework/TestWorkflows/src/o2_sim_its_ALP3.h delete mode 100644 Framework/TestWorkflows/src/o2_sim_tpc.cxx delete mode 100644 Framework/TestWorkflows/src/o2_sim_tpc.h delete mode 100644 Framework/TestWorkflows/src/test_o2ITSCluserizer.cxx delete mode 100644 Framework/TestWorkflows/src/test_o2TPCSimulation.cxx delete mode 100644 Framework/Utils/test/DPLBroadcasterMerger.cxx delete mode 100644 Framework/Utils/test/DPLBroadcasterMerger.h delete mode 100644 Framework/Utils/test/DPLOutputTest.h delete mode 100644 Framework/Utils/test/test_DPLBroadcasterMerger.cxx delete mode 100644 Framework/Utils/test/test_DPLOutputTest.cxx diff --git a/Framework/Core/include/Framework/DataProcessingStateManager.h b/Framework/Core/include/Framework/DataProcessingStateManager.h deleted file mode 100644 index eaa1c8e4e5501..0000000000000 --- a/Framework/Core/include/Framework/DataProcessingStateManager.h +++ /dev/null @@ -1,35 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#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 = {}; -}; - -#endif diff --git a/Framework/Foundation/src/Traits.cxx b/Framework/Foundation/src/Traits.cxx deleted file mode 100644 index faff430964e73..0000000000000 --- a/Framework/Foundation/src/Traits.cxx +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. diff --git a/Framework/TestWorkflows/src/dummy.cxx b/Framework/TestWorkflows/src/dummy.cxx deleted file mode 100644 index faff430964e73..0000000000000 --- a/Framework/TestWorkflows/src/dummy.cxx +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. diff --git a/Framework/TestWorkflows/src/o2_sim_its_ALP3.h b/Framework/TestWorkflows/src/o2_sim_its_ALP3.h deleted file mode 100644 index f9c465fcf5717..0000000000000 --- a/Framework/TestWorkflows/src/o2_sim_its_ALP3.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_ITS_ALP3 -#define WORKFLOWS_O2_SIM_ITS_ALP3 - -#include "Framework/DataProcessorSpec.h" - -namespace o2 -{ -namespace workflows -{ -o2::framework::DataProcessorSpec sim_its_ALP3(); -} -} // namespace o2 - -#endif // WORKFLOWS_O2_SIM_ITS_ALP3 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/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(); -} From 1fd232899d168aab1eaa344aab5562589f7195a2 Mon Sep 17 00:00:00 2001 From: Stefano Cannito <143754257+scannito@users.noreply.github.com> Date: Tue, 10 Feb 2026 01:50:33 +0100 Subject: [PATCH 234/701] ALICE3 Sensor orientation fix + first try to close in-stave gaps (#15043) --- .../ALICE3/TRK/simulation/src/Detector.cxx | 2 +- .../ALICE3/TRK/simulation/src/TRKLayer.cxx | 35 +++++++++++-------- 2 files changed, 21 insertions(+), 16 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx index e0fc6ef1ed35b..06fd2d9670b67 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx @@ -128,7 +128,7 @@ void Detector::buildTRKMiddleOuterLayers() 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), 19.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); diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx index c4683f28918d0..8d30cf9759e40 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx @@ -168,7 +168,7 @@ TGeoVolume* TRKLayer::createChip(std::string type) TGeoVolume* TRKLayer::createModule(std::string type) { - TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); + TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); std::string moduleName = GeometryTGeo::getTRKModulePattern() + std::to_string(mLayerNumber); TGeoShape* module; @@ -176,7 +176,7 @@ TGeoVolume* TRKLayer::createModule(std::string type) if (type == "cylinder") { module = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, (constants::moduleMLOT::length * mNumberOfModules) / 2); - moduleVol = new TGeoVolume(moduleName.c_str(), module, medAir); + moduleVol = new TGeoVolume(moduleName.c_str(), module, medSi); TGeoVolume* chipVol = createChip("cylinder"); LOGP(debug, "Inserting {} in {} ", chipVol->GetName(), moduleVol->GetName()); @@ -186,7 +186,7 @@ TGeoVolume* TRKLayer::createModule(std::string type) double moduleLength = constants::moduleMLOT::length; module = new TGeoBBox(moduleWidth / 2, mChipThickness / 2, moduleLength / 2); // TO BE CHECKED !!! - moduleVol = new TGeoVolume(moduleName.c_str(), module, medAir); + moduleVol = new TGeoVolume(moduleName.c_str(), module, medSi); for (int iChip = 0; iChip < mHalfNumberOfChips; iChip++) { TGeoVolume* chipVolLeft = createChip("flat"); @@ -223,7 +223,7 @@ TGeoVolume* TRKLayer::createModule(std::string type) TGeoVolume* TRKLayer::createHalfStave(std::string type) { - TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); + TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); std::string halfStaveName = GeometryTGeo::getTRKHalfStavePattern() + std::to_string(mLayerNumber); TGeoShape* halfStave; @@ -231,7 +231,7 @@ TGeoVolume* TRKLayer::createHalfStave(std::string type) if (type == "cylinder") { halfStave = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, mChipLength / 2); - halfStaveVol = new TGeoVolume(halfStaveName.c_str(), halfStave, medAir); + halfStaveVol = new TGeoVolume(halfStaveName.c_str(), halfStave, medSi); TGeoVolume* moduleVol = createModule("cylinder"); LOGP(debug, "Inserting {} in {} ", moduleVol->GetName(), halfStaveVol->GetName()); @@ -242,7 +242,7 @@ TGeoVolume* TRKLayer::createHalfStave(std::string type) double halfStaveLength = constants::moduleMLOT::length * mNumberOfModules; halfStave = new TGeoBBox(halfStaveWidth / 2, mChipThickness / 2, halfStaveLength / 2); - halfStaveVol = new TGeoVolume(halfStaveName.c_str(), halfStave, medAir); + halfStaveVol = new TGeoVolume(halfStaveName.c_str(), halfStave, medSi); for (int iModule = 0; iModule < mNumberOfModules; iModule++) { TGeoVolume* moduleVol = createModule("flat"); @@ -257,6 +257,9 @@ TGeoVolume* TRKLayer::createHalfStave(std::string type) halfStaveVol->AddNode(moduleVol, iModule, trans); } } + + halfStaveVol->SetLineColor(kYellow); + return halfStaveVol; } @@ -296,11 +299,11 @@ TGeoVolume* TRKLayer::createStave(std::string type) staveVol->AddNode(moduleVol, iModule, trans); } } else if (type == "staggered") { - /*double moduleWidth = constants::moduleMLOT::width; - double moduleLength = constants::moduleMLOT::length;*/ + 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::ML::width; - double staveWidth = constants::OT::width; // Each stave has two modules (based on the LOI design) + double halfstaveWidth = constants::OT::halfstave::width; + double staveWidth = constants::OT::width - overlap; double staveLength = constants::moduleMLOT::length * mNumberOfModules; stave = new TGeoBBox(staveWidth / 2, mLogicalVolumeThickness / 2, staveLength / 2); @@ -311,12 +314,12 @@ TGeoVolume* TRKLayer::createStave(std::string type) TGeoVolume* halfStaveVolRight = createHalfStave("flat"); TGeoCombiTrans* transLeft = new TGeoCombiTrans(); - transLeft->SetTranslation(-halfstaveWidth / 2 + 0.05, 0, 0); // TO BE CHECKED !!! 1mm overlap between the modules + 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(halfstaveWidth / 2 - 0.05, 0.2, 0); // TO BE CHECKED !!! 1mm overlap between the modules + 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 { @@ -377,7 +380,7 @@ 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 + 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); @@ -385,13 +388,15 @@ void TRKLayer::createLayer(TGeoVolume* motherVolume) 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 layerLength = constants::moduleMLOT::length * mNumberOfModules; + 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 staveWidth = constants::OT::width; // Each stave has two modules (based on the LOI design) int nStaves = (int)std::ceil(mInnerRadius * 2 * TMath::Pi() / staveWidth); nStaves += nStaves % 2; // Require an even number of staves @@ -410,7 +415,7 @@ 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); From d43ba29f8b05b6cb5e0914686830dd0740c87f80 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Thu, 5 Feb 2026 22:38:04 +0100 Subject: [PATCH 235/701] SimulationDataFormat: Delete unused files --- .../ProcessingEventInfo.h | 36 ------------------- 1 file changed, 36 deletions(-) delete mode 100644 DataFormats/simulation/include/SimulationDataFormat/ProcessingEventInfo.h 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 From 1f4624047d09e5bcf08c1e67c665593e4f178ef8 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Tue, 10 Feb 2026 10:50:56 +0100 Subject: [PATCH 236/701] Update OpenMP detection for macOS (#15040) Updated OpenMP detection for macOS with hints to brew library paths and set required compile flags. --- dependencies/FindOpenMPMacOS.cmake | 47 +++++++++++++++++++++++------- 1 file changed, 36 insertions(+), 11 deletions(-) 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() From a63c9c11727258f79489f5dc8801a55a6483d7ac Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Thu, 5 Feb 2026 22:53:21 +0100 Subject: [PATCH 237/701] TRD: Delete unused files --- .../TRD/base/src/CalSingleChamberStatus.cxx | 154 ------------------ 1 file changed, 154 deletions(-) delete mode 100644 Detectors/TRD/base/src/CalSingleChamberStatus.cxx 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; -} From d9bbbfccdc95fd9779cb9b08b7df78a8271e2565 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Thu, 5 Feb 2026 22:42:28 +0100 Subject: [PATCH 238/701] GlobalTrackingWorkflow: Delete unused files --- .../tofworkflow/src/RecoWorkflowSpec.cxx | 189 ------------------ 1 file changed, 189 deletions(-) delete mode 100644 Detectors/GlobalTrackingWorkflow/tofworkflow/src/RecoWorkflowSpec.cxx 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 From aa7e258b79b263eff919e23579e9455ffe1c9c0b Mon Sep 17 00:00:00 2001 From: David Rohr Date: Mon, 9 Feb 2026 09:48:53 +0100 Subject: [PATCH 239/701] FindO2GPU.cmake: be less verbose --- dependencies/FindO2GPU.cmake | 16 ++++++---------- 1 file changed, 6 insertions(+), 10 deletions(-) diff --git a/dependencies/FindO2GPU.cmake b/dependencies/FindO2GPU.cmake index 42d0162691c37..3e8f012fea4b5 100644 --- a/dependencies/FindO2GPU.cmake +++ b/dependencies/FindO2GPU.cmake @@ -10,7 +10,7 @@ # or submit itself to any jurisdiction. # NOTE!!!! - Whenever this file is changed, move it over to alidist/resources -# FindO2GPU.cmake Version 11 +# 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) @@ -65,7 +65,6 @@ function(detect_gpu_arch backend) # Detect GPU architecture, optionally filterri else() set(CUDA_TARGET TESLA) endif() - message(STATUS "Using optimized CUDA settings for ${CUDA_TARGET} GPU (sm_${CUDA_FIRST_TARGET})") string(REGEX MATCH "^[ \t\r\n]*gfx[0-9]+" HIP_FIRST_TARGET "${HIP_AMDGPUTARGET}") string(STRIP "${HIP_FIRST_TARGET}" HIP_FIRST_TARGET) @@ -87,12 +86,13 @@ function(detect_gpu_arch backend) # Detect GPU architecture, optionally filterri else() set(HIP_TARGET VEGA) endif() - message(STATUS "Using optimized HIP settings for ${HIP_TARGET} GPU (gfx${HIP_FIRST_TARGET})") 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") @@ -194,8 +194,6 @@ if(ENABLE_CUDA) if(THRUST_INCLUDE_DIR STREQUAL "THRUST_INCLUDE_DIR-NOTFOUND") message(${FAILURE_SEVERITY} "CUDA found but thrust not available, looked under: ${CMAKE_CUDA_TOOLKIT_INCLUDE_DIRECTORIES}") set(CMAKE_CUDA_COMPILER OFF) - else() - message(STATUS "Thrust found in the path: ${THRUST_INCLUDE_DIR}") endif() if (NOT CMAKE_CUDA_COMPILER_VERSION VERSION_GREATER_EQUAL "12.8") message(${FAILURE_SEVERITY} "CUDA Version too old: ${CMAKE_CUDA_COMPILER_VERSION}, 12.8 required") @@ -278,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() @@ -347,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!") @@ -373,7 +369,7 @@ if(ENABLE_HIP) if(HIP_AMDGPUTARGET) set(CMAKE_HIP_ARCHITECTURES "${HIP_AMDGPUTARGET}") endif() - message(STATUS "HIP Found (${hip_HIPCC_EXECUTABLE} version ${hip_VERSION}, Architectures ${CMAKE_HIP_ARCHITECTURES})") + message(STATUS "HIP Found (${hip_HIPCC_EXECUTABLE} version ${hip_VERSION}, ${CMAKE_HIP_COMPILER}, Architectures ${CMAKE_HIP_ARCHITECTURES})") else() set(HIP_ENABLED OFF) endif() From 2d96089c502e6c582a108e4a0f0ed1cbb7a21e69 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Mon, 9 Feb 2026 09:49:15 +0100 Subject: [PATCH 240/701] GPU Parameters: Add script to generate parameter file from parameter list csv/json and architecture --- GPU/GPUTracking/CMakeLists.txt | 4 +- .../gpu_param_header_generator.cmake | 4 +- .../Standalone/Benchmark/CMakeLists.txt | 2 + GPU/GPUTracking/Standalone/CMakeLists.txt | 1 - .../Standalone/tools/dumpGPUParamByArch.sh | 60 +++++++++++++++++++ GPU/GPUTracking/display/CMakeLists.txt | 9 ++- .../display/filterMacros/setinclude.sh.in | 2 +- 7 files changed, 72 insertions(+), 10 deletions(-) rename GPU/GPUTracking/{cmake => Definitions/Parameters}/gpu_param_header_generator.cmake (96%) create mode 100755 GPU/GPUTracking/Standalone/tools/dumpGPUParamByArch.sh diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index 082dc1f10b1d6..dfee81b398a79 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -292,10 +292,10 @@ foreach(GPU_PARAM_JSON_FILE IN LISTS GPU_PARAM_JSON) math(EXPR GPU_PARAM_JSON_N_FILES "${GPU_PARAM_JSON_N_FILES} + 1") endforeach() -include(cmake/gpu_param_header_generator.cmake) +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, arch selected by CMake variables +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}) diff --git a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake b/GPU/GPUTracking/Definitions/Parameters/gpu_param_header_generator.cmake similarity index 96% rename from GPU/GPUTracking/cmake/gpu_param_header_generator.cmake rename to GPU/GPUTracking/Definitions/Parameters/gpu_param_header_generator.cmake index 383d194aaa717..d0e36e7f15026 100644 --- a/GPU/GPUTracking/cmake/gpu_param_header_generator.cmake +++ b/GPU/GPUTracking/Definitions/Parameters/gpu_param_header_generator.cmake @@ -110,6 +110,9 @@ function(generate_gpu_param_header GPU_PARAM_JSON_FILES ARCH_LIST OUT_HEADER OUT 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() @@ -129,5 +132,4 @@ function(generate_gpu_param_header GPU_PARAM_JSON_FILES ARCH_LIST OUT_HEADER OUT 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}") - message(STATUS "Generated ${OUT_HEADER} and ${OUT_HEADER_DEVICE}") endfunction() 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/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/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/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/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@" From bf8a4027b3eb50ce57d54dc0b796afa1cb6a2fcf Mon Sep 17 00:00:00 2001 From: David Rohr Date: Tue, 10 Feb 2026 14:21:16 +0100 Subject: [PATCH 241/701] Fix codechecker violation --- Detectors/EMCAL/base/src/ClusterFactory.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Detectors/EMCAL/base/src/ClusterFactory.cxx b/Detectors/EMCAL/base/src/ClusterFactory.cxx index 970f7979ef86d..1752e5c0e98ee 100644 --- a/Detectors/EMCAL/base/src/ClusterFactory.cxx +++ b/Detectors/EMCAL/base/src/ClusterFactory.cxx @@ -528,8 +528,9 @@ void ClusterFactory::evalNExMax(gsl::span inputsIndices, A // loop over all other cells in cluster for (size_t j = 0; j < n; j++) { - if (i == j) + if (i == j) { continue; + } // adjacent cell is any cell with adjacent phi or eta index if (std::abs(rows[i] - rows[j]) <= 1 && From f10bf6ecd0328e6a96700cc6de8d1afd57e66875 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 11 Feb 2026 10:02:55 +0100 Subject: [PATCH 242/701] DPL: oldest possible timeframe triggered CompletionPolicy (#15046) This will trigger the processing whenever a given slot will not receive data anymore in virtue of its timeslice being past the oldest possible timeframe. --- .../Framework/CompletionPolicyHelpers.h | 8 ++++- .../Core/src/CompletionPolicyHelpers.cxx | 29 ++++++++++++++++++- 2 files changed, 35 insertions(+), 2 deletions(-) diff --git a/Framework/Core/include/Framework/CompletionPolicyHelpers.h b/Framework/Core/include/Framework/CompletionPolicyHelpers.h index 7f77e4a96f76f..9fce626854e5b 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. // @@ -54,6 +54,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/src/CompletionPolicyHelpers.cxx b/Framework/Core/src/CompletionPolicyHelpers.cxx index 67c726b7f4368..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" @@ -263,6 +264,32 @@ CompletionPolicy CompletionPolicyHelpers::consumeWhenAnyZeroCount(const char* na return CompletionPolicy{name, matcher, callback, false}; } +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) { auto callback = [](InputSpan const& inputs, std::vector const&, ServiceRegistryRef& ref) -> CompletionPolicy::CompletionOp { From 331f2cc815bd213df04c40ecb9359422f11edd8d Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Wed, 11 Feb 2026 11:18:17 +0100 Subject: [PATCH 243/701] fix topology adjust corner case (#15053) --- Framework/Core/src/ArrowSupport.cxx | 4 +++- run/o2sim_kine_publisher.cxx | 3 +-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/Framework/Core/src/ArrowSupport.cxx b/Framework/Core/src/ArrowSupport.cxx index 31cddc9803d69..450f31f4ba7d3 100644 --- a/Framework/Core/src/ArrowSupport.cxx +++ b/Framework/Core/src/ArrowSupport.cxx @@ -581,7 +581,6 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() 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 reader = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { return spec.name.starts_with("internal-dpl-aod-reader"); }); 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(); @@ -659,6 +658,9 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() 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 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}; } From 970ed8ea7328d3828694ba4770429ea680ff2524 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiktor=20Piero=C5=BCak?= <94726725+wpierozak@users.noreply.github.com> Date: Wed, 11 Feb 2026 14:41:38 +0100 Subject: [PATCH 244/701] EventsPerBC calibration task for FT0 (O2-6563) (#14986) Implements the EventsPerBC calibration task for FT0, which generates a histogram of VTX events above a defined amplitude threshold plotted against the BC. * FT0: created first sketch of implementation of generation of TVX per Event calibration object * FT0: Updated CMakeLists for calibration * FT0: Added missing entry in FT0CalibrationLinkDef.h * FT0 calibration: fixed ROOT directory compilation, fixed CCDB output * FT0: refined logs in EventsPerBc calibration, fixed setting TF info in run method * FT0: Added readme to calibrations * FT0: Changed calibration object name, implemented missing OrbitReset fetching * FT0 EventsPerBc calibration: storing histograms in float format, updated readme * Changed type of EventsPerBc calibration object to std::array * FT0: corrected macro FT0readEventsPerBc, corrected typo in calibration README * Created CCDB object class for EvetnsPerBC calibration * FT0: formatted EvensPerBc.h * FT0: removed amplitudes thresholds from EventsPerBc * FT0: Removed from EventsPerBc calibarion option to define slot lenght in TFs; Small code cleaning * Changed default value of min number of entries in EventsPerBcProcessor from 5000 to 5000u --- DataFormats/Detectors/FIT/FT0/CMakeLists.txt | 1 + .../FT0/include/DataFormatsFT0/EventsPerBc.h | 25 ++++ .../FIT/FT0/src/DataFormatsFT0LinkDef.h | 2 + Detectors/FIT/FT0/calibration/CMakeLists.txt | 70 ++++++---- Detectors/FIT/FT0/calibration/README.md | 62 +++++++++ .../FT0Calibration/EventsPerBcCalibrator.h | 81 ++++++++++++ .../calibration/src/EventsPerBcCalibrator.cxx | 81 ++++++++++++ .../calibration/src/FT0CalibrationLinkDef.h | 4 +- .../FT0EventsPerBcProcessor-Workflow.cxx | 47 +++++++ .../calibration/workflow/FT0EventsPerBcSpec.h | 124 ++++++++++++++++++ Detectors/FIT/FT0/macros/CMakeLists.txt | 19 ++- Detectors/FIT/FT0/macros/FT0readEventsPerBc.C | 52 ++++++++ 12 files changed, 538 insertions(+), 30 deletions(-) create mode 100644 DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/EventsPerBc.h create mode 100644 Detectors/FIT/FT0/calibration/README.md create mode 100644 Detectors/FIT/FT0/calibration/include/FT0Calibration/EventsPerBcCalibrator.h create mode 100644 Detectors/FIT/FT0/calibration/src/EventsPerBcCalibrator.cxx create mode 100644 Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.cxx create mode 100644 Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h create mode 100644 Detectors/FIT/FT0/macros/FT0readEventsPerBc.C diff --git a/DataFormats/Detectors/FIT/FT0/CMakeLists.txt b/DataFormats/Detectors/FIT/FT0/CMakeLists.txt index e5331b7b739b2..f7d6a111f4348 100644 --- a/DataFormats/Detectors/FIT/FT0/CMakeLists.txt +++ b/DataFormats/Detectors/FIT/FT0/CMakeLists.txt @@ -47,4 +47,5 @@ o2_target_root_dictionary(DataFormatsFT0 include/DataFormatsFT0/GlobalOffsetsCalibrationObject.h include/DataFormatsFT0/SpectraInfoObject.h include/DataFormatsFT0/SlewingCoef.h + include/DataFormatsFT0/EventsPerBc.h ) diff --git a/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/EventsPerBc.h b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/EventsPerBc.h new file mode 100644 index 0000000000000..9fcd1318914bd --- /dev/null +++ b/DataFormats/Detectors/FIT/FT0/include/DataFormatsFT0/EventsPerBc.h @@ -0,0 +1,25 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef _FT0_EVENTS_PER_BC_CALIB_OBJECT +#define _FT0_EVENTS_PER_BC_CALIB_OBJECT + +#include "CommonConstants/LHCConstants.h" +#include + +namespace o2::ft0 +{ +struct EventsPerBc { + std::array histogram; + ClassDefNV(EventsPerBc, 1); +}; +} // namespace o2::ft0 +#endif \ No newline at end of file diff --git a/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h b/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h index 0d3491224180c..7f8c17a0cd191 100644 --- a/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h +++ b/DataFormats/Detectors/FIT/FT0/src/DataFormatsFT0LinkDef.h @@ -56,4 +56,6 @@ #pragma link C++ class std::pair < std::vector < double>, std::vector < double>> + ; #pragma link C++ class o2::ft0::SlewingCoef + ; +#pragma link C++ class o2::ft0::EventsPerBc + ; + #endif diff --git a/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..f44824517f258 --- /dev/null +++ b/Detectors/FIT/FT0/calibration/include/FT0Calibration/EventsPerBcCalibrator.h @@ -0,0 +1,81 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef 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) : mMinAmplitudeSideA(minAmplitudeSideA), mMinAmplitudeSideC(minAmplitudeSideC) {} + + 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; + + 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); + + 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; + + 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..a2230f51dc4ea --- /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) { + 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) : mMinNumberOfEntries(minNumberOfEntries), mMinAmplitudeSideA(minAmplitudeSideA), mMinAmplitudeSideC(minAmplitudeSideC) +{ + 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)); + 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..ac7a8e52f53b1 --- /dev/null +++ b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.cxx @@ -0,0 +1,47 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#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"}}}}; + + 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..c587ab58fcd90 --- /dev/null +++ b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h @@ -0,0 +1,124 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#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"); + } + + mCalibrator = std::make_unique(mMinNumberOfEntries, mMinAmplitudeSideA, mMinAmplitudeSideC); + + 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; +}; +} // 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 From 8d3541adfc2ff3fb78615f86cc2a234eb8bd60a5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Thu, 5 Feb 2026 23:27:12 +0100 Subject: [PATCH 245/701] Vertexing: Delete unused files --- .../DetectorsVertexing/FwdDCAFitterN.h | 1297 ----------------- Detectors/Vertexing/src/FwdDCAFitterN.cxx | 33 - 2 files changed, 1330 deletions(-) delete mode 100644 Detectors/Vertexing/include/DetectorsVertexing/FwdDCAFitterN.h delete mode 100644 Detectors/Vertexing/src/FwdDCAFitterN.cxx 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/src/FwdDCAFitterN.cxx b/Detectors/Vertexing/src/FwdDCAFitterN.cxx deleted file mode 100644 index f7176aa5039fd..0000000000000 --- a/Detectors/Vertexing/src/FwdDCAFitterN.cxx +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// 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 - -#include "DetectorsVertexing/FwdDCAFitterN.h" - -namespace o2 -{ -namespace vertexing -{ - -void __test_instance__() -{ - FwdDCAFitter2 ft2; - FwdDCAFitter3 ft3; - o2::track::TrackParCovFwd tr; - ft2.process(tr, tr); - ft3.process(tr, tr, tr); -} - -} // namespace vertexing -} // namespace o2 From 35c99fbf9c4a2c42e6cc220c119441ad22dfd059 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Wed, 11 Feb 2026 18:05:45 +0100 Subject: [PATCH 246/701] ITSMFT: Delete unused files (#15034) --- .../MFTCalibration/NoiseSlotCalibrator.h | 101 - .../MFT/calibration/src/MchAlignment.cxx | 1660 ----------------- .../calibration/src/NoiseSlotCalibrator.cxx | 145 -- 3 files changed, 1906 deletions(-) delete mode 100644 Detectors/ITSMFT/MFT/calibration/include/MFTCalibration/NoiseSlotCalibrator.h delete mode 100644 Detectors/ITSMFT/MFT/calibration/src/MchAlignment.cxx delete mode 100644 Detectors/ITSMFT/MFT/calibration/src/NoiseSlotCalibrator.cxx 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 From 6d11591da421e8c200b3368a41727752882e4db1 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Wed, 11 Feb 2026 10:09:52 +0100 Subject: [PATCH 247/701] ITS: GPU: use mean vertex constraint for gpu processing I noticed that in the Pb-Pb production we did not add the mean vertex constraint to be used. --- prodtests/full-system-test/dpl-workflow.sh | 3 +++ 1 file changed, 3 insertions(+) diff --git a/prodtests/full-system-test/dpl-workflow.sh b/prodtests/full-system-test/dpl-workflow.sh index f559fcdf91cf5..a8f01a3ef1822 100755 --- a/prodtests/full-system-test/dpl-workflow.sh +++ b/prodtests/full-system-test/dpl-workflow.sh @@ -572,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" # --------------------------------------------------------------------------------------------------------------------- From 7c79e17a0c694491c65c3033f976084f85cca8d7 Mon Sep 17 00:00:00 2001 From: Matthias Kleiner Date: Wed, 11 Feb 2026 09:44:56 +0100 Subject: [PATCH 248/701] TPC: time gain calibration optimizations - bug fix: using bin centre of the dE/dx instead of lower bin edge - add option to not perform per sector scaling (needed for MC) --- .../DataFormatsTPC/CalibdEdxCorrection.h | 3 +++ .../Detectors/TPC/src/CalibdEdxCorrection.cxx | 13 +++++++++++++ .../include/TPCCalibration/CalibdEdx.h | 4 +++- Detectors/TPC/calibration/src/CalibdEdx.cxx | 17 +++++++++++------ 4 files changed, 30 insertions(+), 7 deletions(-) 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/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/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/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()); From a1b356bc9af763d7c8bd7893f04b17d7202f8457 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 12 Feb 2026 00:11:38 +0100 Subject: [PATCH 249/701] GPU Vulkan Display: fix DEPFILE path --- GPU/GPUTracking/cmake/vulkan_display.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 ) From bf2a3feb26a00127d99b08b9185b3a7a1fc30e15 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 11 Feb 2026 15:52:03 +0100 Subject: [PATCH 250/701] DCS: Fix undefined behavior and invalid pointer access --- Detectors/DCS/src/DataPointCreator.cxx | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) 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 From 6ef43cf21196279d3c6e7cb507e51e4274b015ac Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Thu, 12 Feb 2026 09:01:29 +0100 Subject: [PATCH 251/701] ITS3: load chip response functions from ccdb (#15051) Signed-off-by: Felix Schlepper --- .../ITS3/macros/test/CheckChipResponseFile.C | 23 ++++++++----------- 1 file changed, 10 insertions(+), 13 deletions(-) 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()); From 93cae7b66005fa79f1ecb5bd58e49d52e7cb33e8 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Thu, 12 Feb 2026 09:01:54 +0100 Subject: [PATCH 252/701] ITS3: split longerons, improving stepping speed (#15052) Signed-off-by: Felix Schlepper --- .../ITS3/base/include/ITS3Base/SpecsV2.h | 2 +- .../include/ITS3Simulation/ITS3Layer.h | 5 +- .../ITS3/simulation/src/ITS3Layer.cxx | 68 +++++++++---------- 3 files changed, 37 insertions(+), 38 deletions(-) diff --git a/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h b/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h index a7422c55e72b8..937fa8d2e982c 100644 --- a/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h +++ b/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h @@ -104,7 +104,7 @@ namespace carbonfoam // TODO: Waiting for the further information from WP5(Corrado) 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 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? 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/ITS3Layer.cxx b/Detectors/Upgrades/ITS3/simulation/src/ITS3Layer.cxx index e0be011096450..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); @@ -296,7 +296,7 @@ void ITS3Layer::createCarbonForm() 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 + // 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); - 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.); + 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(); From f926fb83e768fb8b60656bc8e9ed668f8debb7c8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Thu, 12 Feb 2026 10:34:03 +0100 Subject: [PATCH 253/701] MUON: Delete unused files (#15027) --- .../Detectors/MUON/MCH/src/DsChannelGroup.cxx | 16 - .../SegContour/src/SegmentationSVGWriter.cxx | 117 --- .../Raw/Encoder/Payload/RefBufferCRUBare.cxx | 938 ------------------ .../Encoder/Payload/RefBufferCRUUserLogic.cxx | 67 -- .../Raw/Encoder/Payload/RefBufferGBTBare.cxx | 239 ----- .../Encoder/Payload/RefBufferGBTUserLogic.cxx | 34 - .../MUON/MID/Filtering/test/bench_Filter.cxx | 98 -- .../include/MIDWorkflow/DecodedDataDumpSpec.h | 30 - .../include/MIDWorkflow/RawAggregatorSpec.h | 30 - .../MID/Workflow/src/DecodedDataDumpSpec.cxx | 84 -- .../src/decoded-data-dump-workflow.cxx | 65 -- 11 files changed, 1718 deletions(-) delete mode 100644 DataFormats/Detectors/MUON/MCH/src/DsChannelGroup.cxx delete mode 100644 Detectors/MUON/MCH/Mapping/SegContour/src/SegmentationSVGWriter.cxx delete mode 100644 Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferCRUBare.cxx delete mode 100644 Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferCRUUserLogic.cxx delete mode 100644 Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferGBTBare.cxx delete mode 100644 Detectors/MUON/MCH/Raw/Encoder/Payload/RefBufferGBTUserLogic.cxx delete mode 100644 Detectors/MUON/MID/Filtering/test/bench_Filter.cxx delete mode 100644 Detectors/MUON/MID/Workflow/include/MIDWorkflow/DecodedDataDumpSpec.h delete mode 100644 Detectors/MUON/MID/Workflow/include/MIDWorkflow/RawAggregatorSpec.h delete mode 100644 Detectors/MUON/MID/Workflow/src/DecodedDataDumpSpec.cxx delete mode 100644 Detectors/MUON/MID/Workflow/src/decoded-data-dump-workflow.cxx diff --git a/DataFormats/Detectors/MUON/MCH/src/DsChannelGroup.cxx b/DataFormats/Detectors/MUON/MCH/src/DsChannelGroup.cxx deleted file mode 100644 index bcf10d74c95ff..0000000000000 --- a/DataFormats/Detectors/MUON/MCH/src/DsChannelGroup.cxx +++ /dev/null @@ -1,16 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "DataFormatsMCH/DsChannelGroup.h" - -std::string o2::mch::DsChannelId() const -{ -} diff --git a/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/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/DecodedDataDumpSpec.h b/Detectors/MUON/MID/Workflow/include/MIDWorkflow/DecodedDataDumpSpec.h deleted file mode 100644 index 4d104aacac15c..0000000000000 --- a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/DecodedDataDumpSpec.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/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 - -#include "Framework/DataProcessorSpec.h" - -namespace o2 -{ -namespace mid -{ -framework::DataProcessorSpec getRawDumpSpec(); -} // namespace mid -} // namespace o2 - -#endif // O2_MID_RAWDUMPSPEC_H 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/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; -} From 87fa1f6c3befc55da9d538d04dab3b2811793c91 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Thu, 12 Feb 2026 11:08:37 +0100 Subject: [PATCH 254/701] ITSMFT: scaffolding for staggered clusterization (#15004) * ITS staggered clusterization Signed-off-by: Felix Schlepper * Simplify getMaxBCDiffToSquashBias function --------- Signed-off-by: Felix Schlepper --- .../src/StrangenessTrackingSpec.cxx | 2 - Detectors/ITSMFT/ITS/workflow/CMakeLists.txt | 2 - .../include/ITSWorkflow/ClustererSpec.h | 65 ---- .../ITS/workflow/src/ClusterWriterSpec.cxx | 72 ---- .../workflow/src/ClusterWriterWorkflow.cxx | 4 +- .../ITSMFT/ITS/workflow/src/ClustererSpec.cxx | 218 ------------ .../ITSMFT/ITS/workflow/src/RecoWorkflow.cxx | 8 +- Detectors/ITSMFT/MFT/workflow/CMakeLists.txt | 2 - .../include/MFTWorkflow/ClusterWriterSpec.h | 31 -- .../MFT/workflow/src/ClusterWriterSpec.cxx | 72 ---- .../ITSMFT/MFT/workflow/src/ClustererSpec.cxx | 212 ------------ .../ITSMFT/MFT/workflow/src/RecoWorkflow.cxx | 8 +- .../src/mft-cluster-writer-workflow.cxx | 4 +- .../include/ITSMFTReconstruction/Clusterer.h | 14 +- .../ITSMFTReconstruction/ClustererParam.h | 22 +- .../ITSMFTReconstruction/DigitPixelReader.h | 3 +- .../common/reconstruction/src/Clusterer.cxx | 36 +- .../reconstruction/src/DigitPixelReader.cxx | 11 + .../ITSMFT/common/workflow/CMakeLists.txt | 2 + .../ITSMFTWorkflow/ClusterReaderSpec.h | 52 ++- .../ITSMFTWorkflow}/ClusterWriterSpec.h | 16 +- .../include/ITSMFTWorkflow}/ClustererSpec.h | 28 +- .../common/workflow/src/ClusterReaderSpec.cxx | 166 +++++---- .../common/workflow/src/ClusterWriterSpec.cxx | 107 ++++++ .../common/workflow/src/ClustererSpec.cxx | 325 ++++++++++++++++++ .../ITS3/workflow/src/RecoWorkflow.cxx | 4 +- 26 files changed, 648 insertions(+), 838 deletions(-) delete mode 100644 Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClustererSpec.h delete mode 100644 Detectors/ITSMFT/ITS/workflow/src/ClusterWriterSpec.cxx delete mode 100644 Detectors/ITSMFT/ITS/workflow/src/ClustererSpec.cxx delete mode 100644 Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/ClusterWriterSpec.h delete mode 100644 Detectors/ITSMFT/MFT/workflow/src/ClusterWriterSpec.cxx delete mode 100644 Detectors/ITSMFT/MFT/workflow/src/ClustererSpec.cxx rename Detectors/ITSMFT/{ITS/workflow/include/ITSWorkflow => common/workflow/include/ITSMFTWorkflow}/ClusterWriterSpec.h (73%) rename Detectors/ITSMFT/{MFT/workflow/include/MFTWorkflow => common/workflow/include/ITSMFTWorkflow}/ClustererSpec.h (64%) create mode 100644 Detectors/ITSMFT/common/workflow/src/ClusterWriterSpec.cxx create mode 100644 Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx 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/ITSMFT/ITS/workflow/CMakeLists.txt b/Detectors/ITSMFT/ITS/workflow/CMakeLists.txt index f0d50e59493d4..10e16e49d92b5 100644 --- a/Detectors/ITSMFT/ITS/workflow/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/workflow/CMakeLists.txt @@ -13,8 +13,6 @@ o2_add_library(ITSWorkflow TARGETVARNAME targetName SOURCES src/RecoWorkflow.cxx src/ClusterWriterWorkflow.cxx - src/ClustererSpec.cxx - src/ClusterWriterSpec.cxx src/TrackerSpec.cxx src/TrackWriterSpec.cxx src/TrackReaderSpec.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/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/RecoWorkflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx index 60e28556716f2..9f8cb6c83ef99 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx @@ -12,8 +12,8 @@ /// @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/TrackWriterSpec.h" #include "ITStracking/TrackingConfigParam.h" @@ -43,10 +43,10 @@ framework::WorkflowSpec getWorkflow(bool useMC, 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 (useGPUWF) { 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/include/MFTWorkflow/ClusterWriterSpec.h b/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/ClusterWriterSpec.h deleted file mode 100644 index 51dc5a6481eb5..0000000000000 --- a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/ClusterWriterSpec.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file ClusterWriterSpec.h - -#ifndef O2_MFT_CLUSTERWRITER_H_ -#define O2_MFT_CLUSTERWRITER_H_ - -#include "Framework/DataProcessorSpec.h" - -namespace o2 -{ -namespace mft -{ - -/// create a processor spec -/// write MFT clusters a root file -framework::DataProcessorSpec getClusterWriterSpec(bool useMC); - -} // namespace mft -} // namespace o2 - -#endif /* O2_MFT_CLUSTERWRITER_H */ 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/reconstruction/include/ITSMFTReconstruction/Clusterer.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h index c66468905d0aa..0bdbb701a9356 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h @@ -215,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(); @@ -245,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 @@ -253,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 @@ -288,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 @@ -173,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]; @@ -476,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/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/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/Upgrades/ITS3/workflow/src/RecoWorkflow.cxx b/Detectors/Upgrades/ITS3/workflow/src/RecoWorkflow.cxx index 004c3f6097167..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" @@ -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) { From 4ec73c1134146fdf5648327c62d112625240f3c3 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 12 Feb 2026 13:32:13 +0100 Subject: [PATCH 255/701] DPL: introduce range based views to navigate data model (#15061) --- .../Core/include/Framework/DataModelViews.h | 239 ++++++++++++++++++ 1 file changed, 239 insertions(+) create mode 100644 Framework/Core/include/Framework/DataModelViews.h 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_ From 834cbc5b6f9e0f73b4bdedbb96df73538571571f Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 12 Feb 2026 14:39:27 +0100 Subject: [PATCH 256/701] FST: Make previousOrbit configurable --- prodtests/full_system_test.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/prodtests/full_system_test.sh b/prodtests/full_system_test.sh index e2ecca590140f..82021d6c65e63 100755 --- a/prodtests/full_system_test.sh +++ b/prodtests/full_system_test.sh @@ -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}"} @@ -159,7 +160,7 @@ taskwrapper collcontext.log o2-steer-colcontexttool \ --extract-per-timeframe tf:o2sim \ --with-vertices kCCDB \ --maxCollsPerTF ${NEvents} \ - --orbitsEarly 1 \ + --orbitsEarly ${OrbitsBeforeTf} \ --bcPatternFile ccdb \ ${QEDSPEC} From 23765b5bdef76fb5a3151d01df8f908936aacf94 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Fri, 13 Feb 2026 09:03:17 +0100 Subject: [PATCH 257/701] DPL: Enforce that dpl pipeline length is at least as long as number of TFs in flight (#15048) --- .../Core/include/Framework/DataRelayer.h | 3 ++- .../Core/include/Framework/DefaultsHelpers.h | 10 +++++++- Framework/Core/src/ArrowSupport.cxx | 2 +- Framework/Core/src/CommonServices.cxx | 4 +++- Framework/Core/src/DataProcessingDevice.cxx | 12 ++++++---- Framework/Core/src/DataRelayer.cxx | 17 ++++++++++++-- Framework/Core/src/DefaultsHelpers.cxx | 23 +++++++++++++++---- Framework/Core/src/runDataProcessing.cxx | 5 ++-- Framework/Core/test/benchmark_DataRelayer.cxx | 10 ++++---- Framework/Core/test/test_DataRelayer.cxx | 22 +++++++++--------- 10 files changed, 75 insertions(+), 33 deletions(-) diff --git a/Framework/Core/include/Framework/DataRelayer.h b/Framework/Core/include/Framework/DataRelayer.h index 1e010fc12f3d4..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 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/src/ArrowSupport.cxx b/Framework/Core/src/ArrowSupport.cxx index 450f31f4ba7d3..c5cc021a53478 100644 --- a/Framework/Core/src/ArrowSupport.cxx +++ b/Framework/Core/src/ArrowSupport.cxx @@ -564,7 +564,7 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() 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(); + config->maxTimeframes = readers * DefaultsHelpers::pipelineLength(dc); } static bool once = false; // Until we guarantee this is called only once... diff --git a/Framework/Core/src/CommonServices.cxx b/Framework/Core/src/CommonServices.cxx index f786d99fd2c0d..6486406a06dca 100644 --- a/Framework/Core/src/CommonServices.cxx +++ b/Framework/Core/src/CommonServices.cxx @@ -414,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}; diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index ccfb58db7559a..da04a23e81c0c 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -1483,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; @@ -2259,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'; @@ -2291,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'; diff --git a/Framework/Core/src/DataRelayer.cxx b/Framework/Core/src/DataRelayer.cxx index 05b64b6ed1dad..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" @@ -48,6 +49,7 @@ #include #include #include +#include #include #include #include @@ -70,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}, @@ -81,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); 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/runDataProcessing.cxx b/Framework/Core/src/runDataProcessing.cxx index 166f26878c363..ced884ebaa1ed 100644 --- a/Framework/Core/src/runDataProcessing.cxx +++ b/Framework/Core/src/runDataProcessing.cxx @@ -817,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), @@ -826,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), 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/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"); From ab29595c9106a6f5c28bc1a12cb0402cb7f446b8 Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Fri, 13 Feb 2026 10:47:40 +0100 Subject: [PATCH 258/701] Update examples on AO2D creation from MCTracks (#15056) --- run/SimExamples/HepMC_STARlight/run_HepMCToAOD.sh | 2 +- run/SimExamples/McTracksToAOD/run_O2Kine.sh | 5 +++-- run/SimExamples/McTracksToAOD/run_Pythia8.sh | 2 +- run/SimExamples/McTracksToAOD/run_trigger.sh | 6 +++--- 4 files changed, 8 insertions(+), 7 deletions(-) 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 From 7c503090305ab42c607db29f49917e32dfd28023 Mon Sep 17 00:00:00 2001 From: Stefano Cannito <143754257+scannito@users.noreply.github.com> Date: Fri, 13 Feb 2026 17:44:23 +0100 Subject: [PATCH 259/701] [A3 TRK] Fix kCylinder option + services crossing (#15067) * Fix for kCylinder * Fix services crossing --- .../ALICE3/TRK/simulation/src/TRKLayer.cxx | 32 +++++++++---------- .../ALICE3/TRK/simulation/src/TRKServices.cxx | 4 +-- 2 files changed, 17 insertions(+), 19 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx index 8d30cf9759e40..82b6fbd40af59 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx @@ -122,17 +122,12 @@ TGeoVolume* TRKLayer::createChip(std::string type) chipVol = new TGeoVolume(chipName.c_str(), chip, medSi); sensVol = createSensor("cylinder"); - metalVol = createMetalStack("cylinder"); - - TGeoCombiTrans* transSens = new TGeoCombiTrans(); - transSens->SetTranslation(0, -(mChipThickness - mSensorThickness) / 2, 0); // TO BE CHECKED !!! LOGP(debug, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); - chipVol->AddNode(sensVol, 1, transSens); + chipVol->AddNode(sensVol, 1, nullptr); - TGeoCombiTrans* transMetal = new TGeoCombiTrans(); - transMetal->SetTranslation(0, mSensorThickness / 2, 0); // TO BE CHECKED !!! + metalVol = createMetalStack("cylinder"); LOGP(debug, "Inserting {} in {} ", metalVol->GetName(), chipVol->GetName()); - chipVol->AddNode(metalVol, 1, transMetal); + chipVol->AddNode(metalVol, 1, nullptr); // deadVol = createDeadzone("cylinder"); } else if (type == "flat") { @@ -175,7 +170,9 @@ TGeoVolume* TRKLayer::createModule(std::string type) TGeoVolume* moduleVol; if (type == "cylinder") { - module = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, (constants::moduleMLOT::length * mNumberOfModules) / 2); + 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"); @@ -229,8 +226,10 @@ TGeoVolume* TRKLayer::createHalfStave(std::string type) TGeoShape* halfStave; TGeoVolume* halfStaveVol; + double halfStaveLength = constants::moduleMLOT::length * mNumberOfModules; + if (type == "cylinder") { - halfStave = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, mChipLength / 2); + halfStave = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, halfStaveLength / 2); halfStaveVol = new TGeoVolume(halfStaveName.c_str(), halfStave, medSi); TGeoVolume* moduleVol = createModule("cylinder"); @@ -239,7 +238,6 @@ TGeoVolume* TRKLayer::createHalfStave(std::string type) } else if (type == "flat") { double moduleLength = constants::moduleMLOT::length; double halfStaveWidth = constants::OT::halfstave::width; - double halfStaveLength = constants::moduleMLOT::length * mNumberOfModules; halfStave = new TGeoBBox(halfStaveWidth / 2, mChipThickness / 2, halfStaveLength / 2); halfStaveVol = new TGeoVolume(halfStaveName.c_str(), halfStave, medSi); @@ -271,8 +269,10 @@ TGeoVolume* TRKLayer::createStave(std::string type) TGeoShape* stave; TGeoVolume* staveVol; + double staveLength = constants::moduleMLOT::length * mNumberOfModules; + if (type == "cylinder") { - stave = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, (constants::moduleMLOT::length * mNumberOfModules) / 2); + stave = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, staveLength / 2); staveVol = new TGeoVolume(staveName.c_str(), stave, medAir); TGeoVolume* moduleVol = createModule("cylinder"); @@ -281,7 +281,6 @@ TGeoVolume* TRKLayer::createStave(std::string type) } else if (type == "flat") { double moduleLength = constants::moduleMLOT::length; double staveWidth = constants::ML::width; - double staveLength = constants::moduleMLOT::length * mNumberOfModules; stave = new TGeoBBox(staveWidth / 2, mChipThickness / 2, staveLength / 2); staveVol = new TGeoVolume(staveName.c_str(), stave, medAir); @@ -304,7 +303,6 @@ TGeoVolume* TRKLayer::createStave(std::string type) double halfstaveWidth = constants::OT::halfstave::width; double staveWidth = constants::OT::width - overlap; - double staveLength = constants::moduleMLOT::length * mNumberOfModules; stave = new TGeoBBox(staveWidth / 2, mLogicalVolumeThickness / 2, staveLength / 2); staveVol = new TGeoVolume(staveName.c_str(), stave, medAir); @@ -343,15 +341,16 @@ void TRKLayer::createLayer(TGeoVolume* motherVolume) TGeoTube* layer; TGeoVolume* layerVol; + double layerLength = constants::moduleMLOT::length * mNumberOfModules; + if (mLayout == eLayout::kCylinder) { - layer = new TGeoTube(mInnerRadius - 0.333 * layerThickness, mInnerRadius + 0.667 * layerThickness, (constants::moduleMLOT::length * mNumberOfModules) / 2); + 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) { - double layerLength = constants::moduleMLOT::length * mNumberOfModules; double staveWidth = constants::ML::width; // Each stave has two modules (based on the LOI design) if (mInnerRadius > 25) { @@ -390,7 +389,6 @@ void TRKLayer::createLayer(TGeoVolume* motherVolume) } 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 layerLength = constants::moduleMLOT::length * mNumberOfModules; double staveWidth = constants::OT::width - overlapInStave; layer = new TGeoTube(mInnerRadius - 0.333 * layerThickness, mInnerRadius + 0.667 * layerThickness, layerLength / 2); diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx index 51eea905c436a..cbe00e8fc9e89 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx @@ -264,7 +264,7 @@ 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; + const float zLengthMiddleCarbon = 64.2f; TGeoTube* middleBarrelCarbonSupport = new TGeoTube("TRK_MID_CARBONSUPPORTsh", rMinMiddleCarbonSupport, rMaxMiddleCarbonSupport, zLengthMiddleCarbon); TGeoVolume* middleBarrelCarbonSupportVolume = new TGeoVolume("TRK_MID_CARBONSUPPORT", middleBarrelCarbonSupport, medCFiber); middleBarrelCarbonSupportVolume->SetLineColor(kGray); @@ -318,7 +318,7 @@ 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.2f; 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); From 48c0b5433f9d4f7af17e8ad34ea6e3363fce5963 Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Sun, 8 Feb 2026 19:17:34 +0100 Subject: [PATCH 260/701] Support to plug-and-play external (CAD) geometry This commit adds support for - conversion from CAD geometries (STEP) file to (meshed) TGeo geometry via the tool O2_CADtoTGeo.py - ability to setup and include simulations modules from external geometries via the ExternalModule mechanism - ability to navigate complex TGeoTessellated solids efficiently (will stay here until fully integrated in the official ROOT repo) It includes all the (basic) functionality to pick a detector layout from a CAD design and actually simulate it with o2-sim. Instructions are provided in a dedicated documentation markdown. The commit is related to epic https://its.cern.ch/jira/browse/O2-6616 --- Detectors/Base/CMakeLists.txt | 7 +- .../include/DetectorsBase/O2Tessellated.h | 142 ++ .../include/DetectorsBase/TGeoGeometryUtils.h | 38 + Detectors/Base/src/DetectorsBaseLinkDef.h | 2 + Detectors/Base/src/O2Tessellated.cxx | 1509 +++++++++++++++++ Detectors/Base/src/TGeoGeometryUtils.cxx | 144 ++ Detectors/Base/src/bvh2_extra_kernels.h | 79 + Detectors/Base/src/bvh2_third_party.h | 49 + Detectors/Passive/CMakeLists.txt | 2 + .../include/DetectorsPassive/ExternalModule.h | 64 + Detectors/Passive/src/ExternalModule.cxx | 175 ++ Steer/include/Steer/O2MCApplicationBase.h | 2 + Steer/src/O2MCApplication.cxx | 60 + macro/build_geometry.C | 14 + scripts/geometry/O2_CADtoTGeo.py | 602 +++++++ scripts/geometry/README.md | 27 + scripts/geometry/simulating_CAD_modules.md | 72 + 17 files changed, 2987 insertions(+), 1 deletion(-) create mode 100644 Detectors/Base/include/DetectorsBase/O2Tessellated.h create mode 100644 Detectors/Base/include/DetectorsBase/TGeoGeometryUtils.h create mode 100644 Detectors/Base/src/O2Tessellated.cxx create mode 100644 Detectors/Base/src/TGeoGeometryUtils.cxx create mode 100644 Detectors/Base/src/bvh2_extra_kernels.h create mode 100644 Detectors/Base/src/bvh2_third_party.h create mode 100644 Detectors/Passive/include/DetectorsPassive/ExternalModule.h create mode 100644 Detectors/Passive/src/ExternalModule.cxx create mode 100644 scripts/geometry/O2_CADtoTGeo.py create mode 100644 scripts/geometry/README.md create mode 100644 scripts/geometry/simulating_CAD_modules.md 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/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/TGeoGeometryUtils.h b/Detectors/Base/include/DetectorsBase/TGeoGeometryUtils.h new file mode 100644 index 0000000000000..5ec85f1c14702 --- /dev/null +++ b/Detectors/Base/include/DetectorsBase/TGeoGeometryUtils.h @@ -0,0 +1,38 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TGeoGeometryUtils.h +/// \author Sandro Wenzel (CERN) +/// \brief Collection of utility functions for TGeo + +#ifndef ALICEO2_BASE_TGEOGEOMETRYUTILS_H_ +#define ALICEO2_BASE_TGEOGEOMETRYUTILS_H_ + +class TGeoShape; +class TGeoTessellated; + +namespace o2 +{ +namespace base +{ + +/// A few utility functions to operate on TGeo geometries (transformations, printing, ...) +class TGeoGeometryUtils +{ + public: + ///< Transform any (primitive) TGeoShape to a tessellated representation + static TGeoTessellated* TGeoShapeToTGeoTessellated(TGeoShape const*); +}; + +} // namespace base +} // namespace o2 + +#endif diff --git a/Detectors/Base/src/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/O2Tessellated.cxx b/Detectors/Base/src/O2Tessellated.cxx new file mode 100644 index 0000000000000..256a70e5a697a --- /dev/null +++ b/Detectors/Base/src/O2Tessellated.cxx @@ -0,0 +1,1509 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +// Sandro Wenzel 2026 + +// An implementation of TGeoTessellated augmented with efficient navigation functions. +// Asked for integration into ROOT here https://github.com/root-project/root/pull/21045 +// Will be deleted once we get this from ROOT. + +#include +#include + +#include "TGeoManager.h" +#include "TGeoMatrix.h" +#include "TGeoVolume.h" +#include "TVirtualGeoPainter.h" +#include "DetectorsBase/O2Tessellated.h" +#include "TBuffer3D.h" +#include "TBuffer3DTypes.h" +#include "TMath.h" +#include "TBuffer.h" + +#include +#include + +// THIS IS THIRD PARTY CODE (TO BE PUT IN ROOT) WHICH DOES NOT NEED TO ADHERE TO OUR LINTING +// NOLINTBEGIN + +// include the Third-party BVH headers +#include "bvh2_third_party.h" +// some kernels on top of BVH +#include "bvh2_extra_kernels.h" + +#include +#include + +using namespace o2::base; +ClassImp(O2Tessellated); + +using Vertex_t = Tessellated::Vertex_t; + +//////////////////////////////////////////////////////////////////////////////// +/// Compact consecutive equal vertices + +int TGeoFacet::CompactFacet(Vertex_t* vert, int nvertices) +{ + // Compact the common vertices and return new facet + if (nvertices < 2) + return nvertices; + int nvert = nvertices; + int i = 0; + while (i < nvert) { + if (vert[(i + 1) % nvert] == vert[i]) { + // shift last vertices left by one element + for (int j = i + 2; j < nvert; ++j) + vert[j - 1] = vert[j]; + nvert--; + } + i++; + } + return nvert; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Check if a connected neighbour facet has compatible normal + +bool TGeoFacet::IsNeighbour(const TGeoFacet& other, bool& flip) const +{ + + // Find a connecting segment + bool neighbour = false; + int line1[2], line2[2]; + int npoints = 0; + for (int i = 0; i < fNvert; ++i) { + auto ivert = fIvert[i]; + // Check if the other facet has the same vertex + for (int j = 0; j < other.GetNvert(); ++j) { + if (ivert == other[j]) { + line1[npoints] = i; + line2[npoints] = j; + if (++npoints == 2) { + neighbour = true; + bool order1 = line1[1] == line1[0] + 1; + bool order2 = line2[1] == (line2[0] + 1) % other.GetNvert(); + flip = (order1 == order2); + return neighbour; + } + } + } + } + return neighbour; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Constructor. In case nfacets is zero, it is user's responsibility to +/// call CloseShape once all faces are defined. + +O2Tessellated::O2Tessellated(const char* name, int nfacets) : TGeoBBox(name, 0, 0, 0) +{ + fNfacets = nfacets; + if (nfacets) + fFacets.reserve(nfacets); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Constructor providing directly the array of vertices. Facets have to be added +/// providing vertex indices rather than coordinates. + +O2Tessellated::O2Tessellated(const char* name, const std::vector& vertices) : TGeoBBox(name, 0, 0, 0) +{ + fVertices = vertices; + fNvert = fVertices.size(); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Construct from TGeoTessellated + +O2Tessellated::O2Tessellated(TGeoTessellated const& tsl, bool check) : TGeoBBox(tsl.GetName(), 0, 0, 0) +{ + fNfacets = tsl.GetNfacets(); + fNvert = tsl.GetNvertices(); + fNseg = tsl.GetNsegments(); + + // copy facet and vertex done + fVertices.reserve(fNvert); + fFacets.reserve(fNfacets); + for (int i = 0; i < fNfacets; ++i) { + fFacets.push_back(tsl.GetFacet(i)); + } + for (int i = 0; i < fNvert; ++i) { + fVertices.push_back(tsl.GetVertex(i)); + } + // finish remaining structures + CloseShape(check); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Add a vertex checking for duplicates, returning the vertex index + +int O2Tessellated::AddVertex(Vertex_t const& vert) +{ + constexpr double tolerance = 1.e-10; + auto vertexHash = [&](Vertex_t const& vertex) { + // Compute hash for the vertex + long hash = 0; + // helper function to generate hash from integer numbers + auto hash_combine = [](long seed, const long value) { + return seed ^ (std::hash{}(value) + 0x9e3779b9 + (seed << 6) + (seed >> 2)); + }; + for (int i = 0; i < 3; i++) { + // use tolerance to generate int with the desired precision from a real number for hashing + hash = hash_combine(hash, std::roundl(vertex[i] / tolerance)); + } + return hash; + }; + + auto hash = vertexHash(vert); + bool isAdded = false; + int ivert = -1; + // Get the compatible vertices + auto range = fVerticesMap.equal_range(hash); + for (auto it = range.first; it != range.second; ++it) { + ivert = it->second; + if (fVertices[ivert] == vert) { + isAdded = true; + break; + } + } + if (!isAdded) { + ivert = fVertices.size(); + fVertices.push_back(vert); + fVerticesMap.insert(std::make_pair(hash, ivert)); + } + return ivert; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Adding a triangular facet from vertex positions in absolute coordinates + +bool O2Tessellated::AddFacet(const Vertex_t& pt0, const Vertex_t& pt1, const Vertex_t& pt2) +{ + if (fDefined) { + Error("AddFacet", "Shape %s already fully defined. Not adding", GetName()); + return false; + } + + Vertex_t vert[3]; + vert[0] = pt0; + vert[1] = pt1; + vert[2] = pt2; + int nvert = TGeoFacet::CompactFacet(vert, 3); + if (nvert < 3) { + Error("AddFacet", "Triangular facet at index %d degenerated. Not adding.", GetNfacets()); + return false; + } + int ind[3]; + for (auto i = 0; i < 3; ++i) + ind[i] = AddVertex(vert[i]); + fNseg += 3; + fFacets.emplace_back(ind[0], ind[1], ind[2]); + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Adding a triangular facet from indices of vertices + +bool O2Tessellated::AddFacet(int i0, int i1, int i2) +{ + if (fDefined) { + Error("AddFacet", "Shape %s already fully defined. Not adding", GetName()); + return false; + } + if (fVertices.empty()) { + Error("AddFacet", "Shape %s Cannot add facets by indices without vertices. Not adding", GetName()); + return false; + } + + fNseg += 3; + fFacets.emplace_back(i0, i1, i2); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Adding a quadrilateral facet from vertex positions in absolute coordinates + +bool O2Tessellated::AddFacet(const Vertex_t& pt0, const Vertex_t& pt1, const Vertex_t& pt2, const Vertex_t& pt3) +{ + if (fDefined) { + Error("AddFacet", "Shape %s already fully defined. Not adding", GetName()); + return false; + } + Vertex_t vert[4]; + vert[0] = pt0; + vert[1] = pt1; + vert[2] = pt2; + vert[3] = pt3; + int nvert = TGeoFacet::CompactFacet(vert, 4); + if (nvert < 3) { + Error("AddFacet", "Quadrilateral facet at index %d degenerated. Not adding.", GetNfacets()); + return false; + } + + int ind[4]; + for (auto i = 0; i < nvert; ++i) + ind[i] = AddVertex(vert[i]); + fNseg += nvert; + if (nvert == 3) + fFacets.emplace_back(ind[0], ind[1], ind[2]); + else + fFacets.emplace_back(ind[0], ind[1], ind[2], ind[3]); + + if (fNfacets > 0 && GetNfacets() == fNfacets) + CloseShape(false); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Adding a quadrilateral facet from indices of vertices + +bool O2Tessellated::AddFacet(int i0, int i1, int i2, int i3) +{ + if (fDefined) { + Error("AddFacet", "Shape %s already fully defined. Not adding", GetName()); + return false; + } + if (fVertices.empty()) { + Error("AddFacet", "Shape %s Cannot add facets by indices without vertices. Not adding", GetName()); + return false; + } + + fNseg += 4; + fFacets.emplace_back(i0, i1, i2, i3); + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Compute normal for a given facet + +Vertex_t O2Tessellated::FacetComputeNormal(int ifacet, bool& degenerated) const +{ + // Compute normal using non-zero segments + constexpr double kTolerance = 1.e-20; + auto const& facet = fFacets[ifacet]; + int nvert = facet.GetNvert(); + degenerated = true; + Vertex_t normal; + for (int i = 0; i < nvert - 1; ++i) { + Vertex_t e1 = fVertices[facet[i + 1]] - fVertices[facet[i]]; + if (e1.Mag2() < kTolerance) + continue; + for (int j = i + 1; j < nvert; ++j) { + Vertex_t e2 = fVertices[facet[(j + 1) % nvert]] - fVertices[facet[j]]; + if (e2.Mag2() < kTolerance) + continue; + normal = Vertex_t::Cross(e1, e2); + // e1 and e2 may be colinear + if (normal.Mag2() < kTolerance) + continue; + normal.Normalize(); + degenerated = false; + break; + } + if (!degenerated) + break; + } + return normal; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Check validity of facet + +bool O2Tessellated::FacetCheck(int ifacet) const +{ + constexpr double kTolerance = 1.e-10; + auto const& facet = fFacets[ifacet]; + int nvert = facet.GetNvert(); + bool degenerated = true; + FacetComputeNormal(ifacet, degenerated); + if (degenerated) { + std::cout << "Facet: " << ifacet << " is degenerated\n"; + return false; + } + + // Compute surface area + double surfaceArea = 0.; + for (int i = 1; i < nvert - 1; ++i) { + Vertex_t e1 = fVertices[facet[i]] - fVertices[facet[0]]; + Vertex_t e2 = fVertices[facet[i + 1]] - fVertices[facet[0]]; + surfaceArea += 0.5 * Vertex_t::Cross(e1, e2).Mag(); + } + if (surfaceArea < kTolerance) { + std::cout << "Facet: " << ifacet << " has zero surface area\n"; + return false; + } + + return true; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Close the shape: calculate bounding box and compact vertices + +void O2Tessellated::CloseShape(bool check, bool fixFlipped, bool verbose) +{ + if (fIsClosed && fBVH) { + return; + } + // Compute bounding box + fDefined = true; + fNvert = fVertices.size(); + fNfacets = fFacets.size(); + ComputeBBox(); + + BuildBVH(); + if (fOutwardNormals.size() == 0) { + CalculateNormals(); + } else { + // short check if the normal container is of correct size + if (fOutwardNormals.size() != fFacets.size()) { + std::cerr << "Inconsistency in normal container"; + } + } + fIsClosed = true; + + // Cleanup the vertex map + std::multimap().swap(fVerticesMap); + + if (fVertices.size() > 0) { + if (!check) + return; + + // Check facets + for (auto i = 0; i < fNfacets; ++i) + FacetCheck(i); + + fClosedBody = CheckClosure(fixFlipped, verbose); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Check closure of the solid and check/fix flipped normals + +bool O2Tessellated::CheckClosure(bool fixFlipped, bool verbose) +{ + int* nn = new int[fNfacets]; + bool* flipped = new bool[fNfacets]; + bool hasorphans = false; + bool hasflipped = false; + for (int i = 0; i < fNfacets; ++i) { + nn[i] = 0; + flipped[i] = false; + } + + for (int icrt = 0; icrt < fNfacets; ++icrt) { + // all neighbours checked? + if (nn[icrt] >= fFacets[icrt].GetNvert()) + continue; + for (int i = icrt + 1; i < fNfacets; ++i) { + bool isneighbour = fFacets[icrt].IsNeighbour(fFacets[i], flipped[i]); + if (isneighbour) { + if (flipped[icrt]) + flipped[i] = !flipped[i]; + if (flipped[i]) + hasflipped = true; + nn[icrt]++; + nn[i]++; + if (nn[icrt] == fFacets[icrt].GetNvert()) + break; + } + } + if (nn[icrt] < fFacets[icrt].GetNvert()) + hasorphans = true; + } + + if (hasorphans && verbose) { + Error("Check", "Tessellated solid %s has following not fully connected facets:", GetName()); + for (int icrt = 0; icrt < fNfacets; ++icrt) { + if (nn[icrt] < fFacets[icrt].GetNvert()) + std::cout << icrt << " (" << fFacets[icrt].GetNvert() << " edges, " << nn[icrt] << " neighbours)\n"; + } + } + fClosedBody = !hasorphans; + int nfixed = 0; + if (hasflipped) { + if (verbose) + Warning("Check", "Tessellated solid %s has following facets with flipped normals:", GetName()); + for (int icrt = 0; icrt < fNfacets; ++icrt) { + if (flipped[icrt]) { + if (verbose) + std::cout << icrt << "\n"; + if (fixFlipped) { + fFacets[icrt].Flip(); + nfixed++; + } + } + } + if (nfixed && verbose) + Info("Check", "Automatically flipped %d facets to match first defined facet", nfixed); + } + delete[] nn; + delete[] flipped; + + return !hasorphans; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Compute bounding box + +void O2Tessellated::ComputeBBox() +{ + const double kBig = TGeoShape::Big(); + double vmin[3] = {kBig, kBig, kBig}; + double vmax[3] = {-kBig, -kBig, -kBig}; + for (const auto& facet : fFacets) { + for (int i = 0; i < facet.GetNvert(); ++i) { + for (int j = 0; j < 3; ++j) { + vmin[j] = TMath::Min(vmin[j], fVertices[facet[i]].operator[](j)); + vmax[j] = TMath::Max(vmax[j], fVertices[facet[i]].operator[](j)); + } + } + } + fDX = 0.5 * (vmax[0] - vmin[0]); + fDY = 0.5 * (vmax[1] - vmin[1]); + fDZ = 0.5 * (vmax[2] - vmin[2]); + for (int i = 0; i < 3; ++i) + fOrigin[i] = 0.5 * (vmax[i] + vmin[i]); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Returns numbers of vertices, segments and polygons composing the shape mesh. + +void O2Tessellated::GetMeshNumbers(int& nvert, int& nsegs, int& npols) const +{ + nvert = fNvert; + nsegs = fNseg; + npols = GetNfacets(); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Creates a TBuffer3D describing *this* shape. +/// Coordinates are in local reference frame. + +TBuffer3D* O2Tessellated::MakeBuffer3D() const +{ + const int nvert = fNvert; + const int nsegs = fNseg; + const int npols = GetNfacets(); + auto buff = new TBuffer3D(TBuffer3DTypes::kGeneric, nvert, 3 * nvert, nsegs, 3 * nsegs, npols, 6 * npols); + if (buff) { + SetPoints(buff->fPnts); + SetSegsAndPols(*buff); + } + return buff; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Prints basic info + +void O2Tessellated::Print(Option_t*) const +{ + std::cout << "=== Tessellated shape " << GetName() << " having " << GetNvertices() << " vertices and " + << GetNfacets() << " facets\n"; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Fills TBuffer3D structure for segments and polygons. + +void O2Tessellated::SetSegsAndPols(TBuffer3D& buff) const +{ + const int c = GetBasicColor(); + int* segs = buff.fSegs; + int* pols = buff.fPols; + + int indseg = 0; // segment internal data index + int indpol = 0; // polygon internal data index + int sind = 0; // segment index + for (const auto& facet : fFacets) { + auto nvert = facet.GetNvert(); + pols[indpol++] = c; + pols[indpol++] = nvert; + for (auto j = 0; j < nvert; ++j) { + int k = (j + 1) % nvert; + // segment made by next consecutive points + segs[indseg++] = c; + segs[indseg++] = facet[j]; + segs[indseg++] = facet[k]; + // add segment to current polygon and increment segment index + pols[indpol + nvert - j - 1] = sind++; + } + indpol += nvert; + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Fill tessellated points to an array. + +void O2Tessellated::SetPoints(double* points) const +{ + int ind = 0; + for (const auto& vertex : fVertices) { + vertex.CopyTo(&points[ind]); + ind += 3; + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Fill tessellated points in float. + +void O2Tessellated::SetPoints(Float_t* points) const +{ + int ind = 0; + for (const auto& vertex : fVertices) { + points[ind++] = vertex.x(); + points[ind++] = vertex.y(); + points[ind++] = vertex.z(); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Resize the shape by scaling vertices within maxsize and center to origin + +void O2Tessellated::ResizeCenter(double maxsize) +{ + using Vector3_t = Vertex_t; + + if (!fDefined) { + Error("ResizeCenter", "Not all faces are defined"); + return; + } + Vector3_t origin(fOrigin[0], fOrigin[1], fOrigin[2]); + double maxedge = TMath::Max(TMath::Max(fDX, fDY), fDZ); + double scale = maxsize / maxedge; + for (size_t i = 0; i < fVertices.size(); ++i) { + fVertices[i] = scale * (fVertices[i] - origin); + } + fOrigin[0] = fOrigin[1] = fOrigin[2] = 0; + fDX *= scale; + fDY *= scale; + fDZ *= scale; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Fills a static 3D buffer and returns a reference. + +const TBuffer3D& O2Tessellated::GetBuffer3D(int reqSections, Bool_t localFrame) const +{ + static TBuffer3D buffer(TBuffer3DTypes::kGeneric); + + FillBuffer3D(buffer, reqSections, localFrame); + + const int nvert = fNvert; + const int nsegs = fNseg; + const int npols = GetNfacets(); + + if (reqSections & TBuffer3D::kRawSizes) { + if (buffer.SetRawSizes(nvert, 3 * nvert, nsegs, 3 * nsegs, npols, 6 * npols)) { + buffer.SetSectionsValid(TBuffer3D::kRawSizes); + } + } + if ((reqSections & TBuffer3D::kRaw) && buffer.SectionsValid(TBuffer3D::kRawSizes)) { + SetPoints(buffer.fPnts); + if (!buffer.fLocalFrame) { + TransformPoints(buffer.fPnts, buffer.NbPnts()); + } + + SetSegsAndPols(buffer); + buffer.SetSectionsValid(TBuffer3D::kRaw); + } + + return buffer; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Reads a single tessellated solid from an .obj file. + +O2Tessellated* O2Tessellated::ImportFromObjFormat(const char* objfile, bool check, bool verbose) +{ + using std::vector, std::string, std::ifstream, std::stringstream, std::endl; + + vector vertices; + vector sfacets; + + struct FacetInd_t { + int i0 = -1; + int i1 = -1; + int i2 = -1; + int i3 = -1; + int nvert = 0; + FacetInd_t(int a, int b, int c) + { + i0 = a; + i1 = b; + i2 = c; + nvert = 3; + }; + FacetInd_t(int a, int b, int c, int d) + { + i0 = a; + i1 = b; + i2 = c; + i3 = d; + nvert = 4; + }; + }; + + vector facets; + // List of geometric vertices, with (x, y, z [,w]) coordinates, w is optional and defaults to 1.0. + // struct vtx_t { double x = 0; double y = 0; double z = 0; double w = 1; }; + + // Texture coordinates in u, [,v ,w]) coordinates, these will vary between 0 and 1. v, w are optional and default to + // 0. + // struct tex_t { double u; double v; double w; }; + + // List of vertex normals in (x,y,z) form; normals might not be unit vectors. + // struct vn_t { double x; double y; double z; }; + + // Parameter space vertices in ( u [,v] [,w] ) form; free form geometry statement + // struct vp_t { double u; double v; double w; }; + + // Faces are defined using lists of vertex, texture and normal indices which start at 1. + // Polygons such as quadrilaterals can be defined by using more than three vertex/texture/normal indices. + // f v1//vn1 v2//vn2 v3//vn3 ... + + // Records starting with the letter "l" specify the order of the vertices which build a polyline. + // l v1 v2 v3 v4 v5 v6 ... + + string line; + int ind[4] = {0}; + ifstream file(objfile); + if (!file.is_open()) { + ::Error("O2Tessellated::ImportFromObjFormat", "Unable to open %s", objfile); + return nullptr; + } + + while (getline(file, line)) { + stringstream ss(line); + string tag; + + // We ignore everything which is not a vertex or a face + if (line.rfind('v', 0) == 0 && line.rfind("vt", 0) != 0 && line.rfind("vn", 0) != 0 && line.rfind("vn", 0) != 0) { + // Decode the vertex + double pos[4] = {0, 0, 0, 1}; + ss >> tag >> pos[0] >> pos[1] >> pos[2] >> pos[3]; + vertices.emplace_back(pos[0] * pos[3], pos[1] * pos[3], pos[2] * pos[3]); + } + + else if (line.rfind('f', 0) == 0) { + // Decode the face + ss >> tag; + string word; + sfacets.clear(); + while (ss >> word) + sfacets.push_back(word); + if (sfacets.size() > 4 || sfacets.size() < 3) { + ::Error("O2Tessellated::ImportFromObjFormat", "Detected face having unsupported %zu vertices", + sfacets.size()); + return nullptr; + } + int nvert = 0; + for (auto& sword : sfacets) { + stringstream ssword(sword); + string token; + getline(ssword, token, '/'); // just need the vertex index, which is the first token + // Convert string token to integer + + ind[nvert++] = stoi(token) - 1; + if (ind[nvert - 1] < 0) { + ::Error("O2Tessellated::ImportFromObjFormat", "Unsupported relative vertex index definition in %s", + objfile); + return nullptr; + } + } + if (nvert == 3) + facets.emplace_back(ind[0], ind[1], ind[2]); + else + facets.emplace_back(ind[0], ind[1], ind[2], ind[3]); + } + } + + int nvertices = (int)vertices.size(); + int nfacets = (int)facets.size(); + if (nfacets < 3) { + ::Error("O2Tessellated::ImportFromObjFormat", "Not enough faces detected in %s", objfile); + return nullptr; + } + + string sobjfile(objfile); + if (verbose) + std::cout << "Read " << nvertices << " vertices and " << nfacets << " facets from " << sobjfile << endl; + + auto tsl = new O2Tessellated(sobjfile.erase(sobjfile.find_last_of('.')).c_str(), vertices); + + for (int i = 0; i < nfacets; ++i) { + auto facet = facets[i]; + if (facet.nvert == 3) + tsl->AddFacet(facet.i0, facet.i1, facet.i2); + else + tsl->AddFacet(facet.i0, facet.i1, facet.i2, facet.i3); + } + tsl->CloseShape(check, true, verbose); + tsl->Print(); + return tsl; +} + +// implementation of some geometry helper functions in anonymous namespace +namespace +{ + +using Vertex_t = Tessellated::Vertex_t; +// The classic Moeller-Trumbore ray triangle-intersection kernel: +// - Compute triangle edges e1, e2 +// - Compute determinant det +// - Reject parallel rays +// - Compute barycentric coordinates u, v +// - Compute ray parameter t +double rayTriangle(const Vertex_t& orig, const Vertex_t& dir, const Vertex_t& v0, const Vertex_t& v1, + const Vertex_t& v2, double rayEPS = 1e-8) +{ + constexpr double EPS = 1e-8; + const double INF = std::numeric_limits::infinity(); + Vertex_t e1{v1[0] - v0[0], v1[1] - v0[1], v1[2] - v0[2]}; + Vertex_t e2{v2[0] - v0[0], v2[1] - v0[1], v2[2] - v0[2]}; + auto p = Vertex_t::Cross(dir, e2); + auto det = e1.Dot(p); + if (std::abs(det) <= EPS) { + return INF; + } + + Vertex_t tvec{orig[0] - v0[0], orig[1] - v0[1], orig[2] - v0[2]}; + auto invDet = 1.0 / det; + auto u = tvec.Dot(p) * invDet; + if (u < 0.0 || u > 1.0) { + return INF; + } + auto q = Vertex_t::Cross(tvec, e1); + auto v = dir.Dot(q) * invDet; + if (v < 0.0 || u + v > 1.0) { + return INF; + } + auto t = e2.Dot(q) * invDet; + return (t > rayEPS) ? t : INF; +} + +template +struct Vec3f { + T x, y, z; +}; + +template +inline Vec3f operator-(const Vec3f& a, const Vec3f& b) +{ + return {a.x - b.x, a.y - b.y, a.z - b.z}; +} + +template +inline Vec3f cross(const Vec3f& a, const Vec3f& b) +{ + return {a.y * b.z - a.z * b.y, a.z * b.x - a.x * b.z, a.x * b.y - a.y * b.x}; +} + +template +inline T dot(const Vec3f& a, const Vec3f& b) +{ + return a.x * b.x + a.y * b.y + a.z * b.z; +} + +// Kernel to get closest/shortest distance between a point and a triangl (a,b,c). +// Performed by default in float since Safety can be approximate. +// Project point onto triangle plane +// If projection lies inside → distance to plane +// Otherwise compute min distance to the three edges +// Return squared distance +template +T pointTriangleDistSq(const Vec3f& p, const Vec3f& a, const Vec3f& b, const Vec3f& c) +{ + // Edges + Vec3f ab = b - a; + Vec3f ac = c - a; + Vec3f ap = p - a; + + auto d1 = dot(ab, ap); + auto d2 = dot(ac, ap); + if (d1 <= T(0.0) && d2 <= T(0.0)) { + return dot(ap, ap); // barycentric (1,0,0) + } + + Vec3f bp = p - b; + auto d3 = dot(ab, bp); + auto d4 = dot(ac, bp); + if (d3 >= T(0.0) && d4 <= d3) { + return dot(bp, bp); // (0,1,0) + } + + T vc = d1 * d4 - d3 * d2; + if (vc <= 0.0f && d1 >= 0.0f && d3 <= 0.0f) { + T v = d1 / (d1 - d3); + Vec3f proj = {a.x + v * ab.x, a.y + v * ab.y, a.z + v * ab.z}; + Vec3f d = p - proj; + return dot(d, d); // edge AB + } + + Vec3f cp = p - c; + T d5 = dot(ab, cp); + T d6 = dot(ac, cp); + if (d6 >= T(0.0f) && d5 <= d6) { + return dot(cp, cp); // (0,0,1) + } + + T vb = d5 * d2 - d1 * d6; + if (vb <= 0.0f && d2 >= 0.0f && d6 <= 0.0f) { + T w = d2 / (d2 - d6); + Vec3f proj = {a.x + w * ac.x, a.y + w * ac.y, a.z + w * ac.z}; + Vec3f d = p - proj; + return dot(d, d); // edge AC + } + + T va = d3 * d6 - d5 * d4; + if (va <= 0.0f && (d4 - d3) >= 0.0f && (d5 - d6) >= 0.0f) { + T w = (d4 - d3) / ((d4 - d3) + (d5 - d6)); + Vec3f proj = {b.x + w * (c.x - b.x), b.y + w * (c.y - b.y), b.z + w * (c.z - b.z)}; + Vec3f d = p - proj; + return dot(d, d); // edge BC + } + + // Inside face region + T denom = T(1.0f) / (va + vb + vc); + T v = vb * denom; + T w = vc * denom; + + Vec3f proj = {a.x + ab.x * v + ac.x * w, a.y + ab.y * v + ac.y * w, a.z + ab.z * v + ac.z * w}; + + Vec3f d = p - proj; + return dot(d, d); +} + +template +inline Vec3f normalize(const Vec3f& v) +{ + T len2 = dot(v, v); + if (len2 == T(0.0f)) { + std::cerr << "Degnerate triangle. Cannot determine normal"; + return {0, 0, 0}; + } + T invLen = T(1.0f) / std::sqrt(len2); + return {v.x * invLen, v.y * invLen, v.z * invLen}; +} + +template +inline Vec3f triangleNormal(const Vec3f& a, const Vec3f& b, const Vec3f& c) +{ + const Vec3f e1 = b - a; + const Vec3f e2 = c - a; + return normalize(cross(e1, e2)); +} + +} // end anonymous namespace + +//////////////////////////////////////////////////////////////////////////////// +/// DistFromOutside + +Double_t O2Tessellated::DistFromOutside(const Double_t* point, const Double_t* dir, Int_t /*iact*/, Double_t stepmax, + Double_t* /*safe*/) const +{ + // use the BVH intersector in combination with leaf ray-triangle testing + double local_step = Big(); // we need this otherwise the lambda get's confused + + using Scalar = float; + using Vec3 = bvh::v2::Vec; + using Node = bvh::v2::Node; + using Bvh = bvh::v2::Bvh; + using Ray = bvh::v2::Ray; + + // let's fetch the bvh + auto mybvh = (Bvh*)fBVH; + if (!mybvh) { + assert(false); + return -1.; + } + + auto truncate_roundup = [](double orig) { + float epsilon = std::numeric_limits::epsilon() * std::fabs(orig); + // Add the bias to x before assigning it to y + return static_cast(orig + epsilon); + }; + + // let's do very quick checks against the top node + const auto topnode_bbox = mybvh->get_root().get_bbox(); + if ((-point[0] + topnode_bbox.min[0]) > stepmax) { + return Big(); + } + if ((-point[1] + topnode_bbox.min[1]) > stepmax) { + return Big(); + } + if ((-point[2] + topnode_bbox.min[2]) > stepmax) { + return Big(); + } + if ((point[0] - topnode_bbox.max[0]) > stepmax) { + return Big(); + } + if ((point[1] - topnode_bbox.max[1]) > stepmax) { + return Big(); + } + if ((point[2] - topnode_bbox.max[2]) > stepmax) { + return Big(); + } + + // the ray used for bvh interaction + Ray ray(Vec3(point[0], point[1], point[2]), // origin + Vec3(dir[0], dir[1], dir[2]), // direction + 0.0f, // minimum distance (could give stepmax ?) + truncate_roundup(local_step)); + + static constexpr bool use_robust_traversal = true; + + Vertex_t dir_v{dir[0], dir[1], dir[2]}; + // Traverse the BVH and apply concrete object intersection in BVH leafs + bvh::v2::GrowingStack stack; + mybvh->intersect(ray, mybvh->get_root().index, stack, [&](size_t begin, size_t end) { + for (size_t prim_id = begin; prim_id < end; ++prim_id) { + auto objectid = mybvh->prim_ids[prim_id]; + const auto& facet = fFacets[objectid]; + const auto& n = fOutwardNormals[objectid]; + + // quick normal test. Coming from outside, the dot product must be negative + if (n.Dot(dir_v) > 0.) { + continue; + } + + auto thisdist = rayTriangle(Vertex_t(point[0], point[1], point[2]), dir_v, + fVertices[facet[0]], fVertices[facet[1]], fVertices[facet[2]], 0.); + + if (thisdist < local_step) { + local_step = thisdist; + } + } + return false; // go on after this + }); + + return local_step; +} + +//////////////////////////////////////////////////////////////////////////////// +/// DistFromOutside + +Double_t O2Tessellated::DistFromInside(const Double_t* point, const Double_t* dir, Int_t /*iact*/, Double_t /*stepmax*/, + Double_t* /*safe*/) const +{ + // use the BVH intersector in combination with leaf ray-triangle testing + double local_step = Big(); // we need this otherwise the lambda get's confused + + using Scalar = float; + using Vec3 = bvh::v2::Vec; + using Node = bvh::v2::Node; + using Bvh = bvh::v2::Bvh; + using Ray = bvh::v2::Ray; + + // let's fetch the bvh + auto mybvh = (Bvh*)fBVH; + if (!mybvh) { + assert(false); + return -1.; + } + + auto truncate_roundup = [](double orig) { + float epsilon = std::numeric_limits::epsilon() * std::fabs(orig); + // Add the bias to x before assigning it to y + return static_cast(orig + epsilon); + }; + + // the ray used for bvh interaction + Ray ray(Vec3(point[0], point[1], point[2]), // origin + Vec3(dir[0], dir[1], dir[2]), // direction + 0., // minimum distance (could give stepmax ?) + truncate_roundup(local_step)); + + static constexpr bool use_robust_traversal = true; + + Vertex_t dir_v{dir[0], dir[1], dir[2]}; + // Traverse the BVH and apply concrete object intersection in BVH leafs + bvh::v2::GrowingStack stack; + mybvh->intersect(ray, mybvh->get_root().index, stack, [&](size_t begin, size_t end) { + for (size_t prim_id = begin; prim_id < end; ++prim_id) { + auto objectid = mybvh->prim_ids[prim_id]; + auto facet = fFacets[objectid]; + const auto& n = fOutwardNormals[objectid]; + + // Only exiting surfaces are relevant (from inside--> dot product must be positive) + if (n.Dot(dir_v) <= 0.) { + continue; + } + + const auto& v0 = fVertices[facet[0]]; + const auto& v1 = fVertices[facet[1]]; + const auto& v2 = fVertices[facet[2]]; + + const double t = + rayTriangle(Vertex_t{point[0], point[1], point[2]}, dir_v, v0, v1, v2, 0.); + if (t < local_step) { + local_step = t; + } + } + return false; // go on after this + }); + + return local_step; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Capacity + +Double_t O2Tessellated::Capacity() const +{ + // For explanation of the following algorithm see: + // https://en.wikipedia.org/wiki/Polyhedron#Volume + // http://wwwf.imperial.ac.uk/~rn/centroid.pdf + + double vol = 0.0; + for (size_t i = 0; i < fFacets.size(); ++i) { + auto& facet = fFacets[i]; + auto a = fVertices[facet[0]]; + auto b = fVertices[facet[1]]; + auto c = fVertices[facet[2]]; + vol += + a[0] * (b[1] * c[2] - b[2] * c[1]) + b[0] * (c[1] * a[2] - c[2] * a[1]) + c[0] * (a[1] * b[2] - a[2] * b[1]); + } + return vol / 6.0; +} + +//////////////////////////////////////////////////////////////////////////////// +/// BuildBVH + +void O2Tessellated::BuildBVH() +{ + using Scalar = float; + using BBox = bvh::v2::BBox; + using Vec3 = bvh::v2::Vec; + using Node = bvh::v2::Node; + using Bvh = bvh::v2::Bvh; + + // helper determining axis aligned bounding box from a facet; + auto GetBoundingBox = [this](TGeoFacet const& facet) { +#ifndef NDEBUG + const auto nvertices = facet.GetNvert(); + assert(nvertices == 3); // for now only triangles +#endif + const auto& v1 = fVertices[facet[0]]; + const auto& v2 = fVertices[facet[1]]; + const auto& v3 = fVertices[facet[2]]; + BBox bbox; + bbox.min[0] = std::min(std::min(v1[0], v2[0]), v3[0]) - 0.001f; + bbox.min[1] = std::min(std::min(v1[1], v2[1]), v3[1]) - 0.001f; + bbox.min[2] = std::min(std::min(v1[2], v2[2]), v3[2]) - 0.001f; + bbox.max[0] = std::max(std::max(v1[0], v2[0]), v3[0]) + 0.001f; + bbox.max[1] = std::max(std::max(v1[1], v2[1]), v3[1]) + 0.001f; + bbox.max[2] = std::max(std::max(v1[2], v2[2]), v3[2]) + 0.001f; + return bbox; + }; + + // we need bounding boxes enclosing the primitives and centers of primitives + // (replaced here by centers of bounding boxes) to build the bvh + std::vector bboxes; + std::vector centers; + + // loop over all the triangles/Facets; + int nd = fFacets.size(); + for (int i = 0; i < nd; ++i) { + auto& facet = fFacets[i]; + + // fetch the bounding box of this node and add to the vector of bounding boxes + (bboxes).push_back(GetBoundingBox(facet)); + centers.emplace_back((bboxes).back().get_center()); + } + + // check if some previous object is registered and delete if necessary + if (fBVH) { + delete (Bvh*)fBVH; + fBVH = nullptr; + } + + // create the bvh + typename bvh::v2::DefaultBuilder::Config config; + config.quality = bvh::v2::DefaultBuilder::Quality::High; + auto bvh = bvh::v2::DefaultBuilder::build(bboxes, centers, config); + auto bvhptr = new Bvh; + *bvhptr = std::move(bvh); // copy structure + fBVH = (void*)(bvhptr); + + return; +} + +//////////////////////////////////////////////////////////////////////////////// +/// Contains + +bool O2Tessellated::Contains(Double_t const* point) const +{ + // we do the parity test + using Scalar = float; + using Vec3 = bvh::v2::Vec; + using Node = bvh::v2::Node; + using Bvh = bvh::v2::Bvh; + using Ray = bvh::v2::Ray; + + // let's fetch the bvh + auto mybvh = (Bvh*)fBVH; + if (!mybvh) { + assert(false); + return false; + } + + auto truncate_roundup = [](double orig) { + float epsilon = std::numeric_limits::epsilon() * std::fabs(orig); + // Add the bias to x before assigning it to y + return static_cast(orig + epsilon); + }; + + // let's do very quick checks against the top node + if (!TGeoBBox::Contains(point)) { + return false; + } + + // An arbitrary test direction. + // Doesn't need to be normalized and probes all normals. Also ensuring to be skewed somewhat + // without evident symmetries. + Vertex_t test_dir{1.0, 1.41421356237, 1.73205080757}; + + double local_step = Big(); + // the ray used for bvh interaction + Ray ray(Vec3(point[0], point[1], point[2]), // origin + Vec3(test_dir[0], test_dir[1], test_dir[2]), // direction + 0.0f, // minimum distance (could give stepmax ?) + truncate_roundup(local_step)); + + static constexpr bool use_robust_traversal = true; + + // Traverse the BVH and apply concrete object intersection in BVH leafs + bvh::v2::GrowingStack stack; + size_t crossings = 0; + mybvh->intersect(ray, mybvh->get_root().index, stack, [&](size_t begin, size_t end) { + for (size_t prim_id = begin; prim_id < end; ++prim_id) { + auto objectid = mybvh->prim_ids[prim_id]; + auto& facet = fFacets[objectid]; + + // for the parity test, we probe all crossing surfaces + const auto& v0 = fVertices[facet[0]]; + const auto& v1 = fVertices[facet[1]]; + const auto& v2 = fVertices[facet[2]]; + + const double t = rayTriangle(Vertex_t(point[0], point[1], point[2]), + test_dir, v0, v1, v2, 0.); + + if (t != std::numeric_limits::infinity()) { + ++crossings; + } + } + return false; + }); + + return crossings & 1; +} + +namespace +{ + +// Helper classes/structs used for priority queue - BVH traversal +// structure keeping cost (value) for a BVH index +struct BVHPrioElement { + size_t bvh_node_id; + float value; +}; + +// A priority queue for BVHPrioElement with an additional clear method +// for quick reset. We intentionally derive from std::priority_queue here to expose a +// clear() convenience method via access to the protected container `c`. +// This is internal, non-polymorphic code and relies on standard-library +// implementation details that are stable across supported platforms. +template +class BVHPrioQueue : public std::priority_queue, Comparator> +{ + public: + using std::priority_queue, + Comparator>::priority_queue; // constructor inclusion + + // convenience method to quickly clear/reset the queue (instead of having to pop one by one) + void clear() { this->c.clear(); } +}; + +} // namespace + +/// a reusable safety kernel, which optionally returns the closest face +template +inline Double_t O2Tessellated::SafetyKernel(const Double_t* point, bool in, int* closest_facet_id) const +{ + // This is the classic traversal/pruning of a BVH based on priority queue search + + float smallest_safety_sq = TGeoShape::Big(); + + using Scalar = float; + using Vec3 = bvh::v2::Vec; + using Node = bvh::v2::Node; + using Bvh = bvh::v2::Bvh; + + // let's fetch the bvh + auto mybvh = (Bvh*)fBVH; + + // testpoint object in float for quick BVH interaction + Vec3 testpoint(point[0], point[1], point[2]); + + auto currnode = mybvh->nodes[0]; // we start from the top BVH node + // we do a quick check on the top node (in case we are outside shape) + bool outside_top = false; + if (!in) { + outside_top = !bvh::v2::extra::contains(currnode.get_bbox(), testpoint); + if (outside_top) { + const auto safety_sq_to_top = bvh::v2::extra::SafetySqToNode(currnode.get_bbox(), testpoint); + // we simply return safety to the outer bounding box as an estimate + return std::sqrt(safety_sq_to_top); + } + } + + // comparator bringing out "smallest" value on top + auto cmp = [](BVHPrioElement a, BVHPrioElement b) { return a.value > b.value; }; + static thread_local BVHPrioQueue queue(cmp); + queue.clear(); + + // algorithm is based on standard iterative tree traversal with priority queues + float current_safety_to_node_sq = 0.f; + + if (returnFace) { + *closest_facet_id = -1; + } + + do { + if (currnode.is_leaf()) { + // we are in a leaf node and actually talk to a face/triangular primitive + const auto begin_prim_id = currnode.index.first_id(); + const auto end_prim_id = begin_prim_id + currnode.index.prim_count(); + + for (auto p_id = begin_prim_id; p_id < end_prim_id; p_id++) { + const auto object_id = mybvh->prim_ids[p_id]; + + const auto& facet = fFacets[object_id]; + const auto& v1 = fVertices[facet[0]]; + const auto& v2 = fVertices[facet[1]]; + const auto& v3 = fVertices[facet[2]]; + + auto thissafetySQ = pointTriangleDistSq(Vec3f{point[0], point[1], point[2]}, Vec3f{v1[0], v1[1], v1[2]}, + Vec3f{v2[0], v2[1], v2[2]}, Vec3f{v3[0], v3[1], v3[2]}); + + if (thissafetySQ < smallest_safety_sq) { + smallest_safety_sq = thissafetySQ; + if (returnFace) { + *closest_facet_id = object_id; + } + } + } + } else { + // not a leave node ... for further traversal, + // we inject the children into priority queue based on distance to it's bounding box + const auto leftchild_id = currnode.index.first_id(); + const auto rightchild_id = leftchild_id + 1; + + for (size_t childid : {leftchild_id, rightchild_id}) { + if (childid >= mybvh->nodes.size()) { + continue; + } + + const auto& node = mybvh->nodes[childid]; + const auto inside = bvh::v2::extra::contains(node.get_bbox(), testpoint); + + if (inside) { + // this must be further considered because we are inside the bounding box + queue.push(BVHPrioElement{childid, -1.}); + } else { + auto safety_to_node_square = bvh::v2::extra::SafetySqToNode(node.get_bbox(), testpoint); + if (safety_to_node_square <= smallest_safety_sq) { + // this should be further considered + queue.push(BVHPrioElement{childid, safety_to_node_square}); + } + } + } + } + + if (queue.size() > 0) { + auto currElement = queue.top(); + currnode = mybvh->nodes[currElement.bvh_node_id]; + current_safety_to_node_sq = currElement.value; + queue.pop(); + } else { + break; + } + } while (current_safety_to_node_sq <= smallest_safety_sq); + + return std::nextafter(std::sqrt(smallest_safety_sq), 0.0f); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Safety + +Double_t O2Tessellated::Safety(const Double_t* point, Bool_t in) const +{ + // we could use some caching here (in future) since queries to the solid will likely + // be made with some locality + + // fall-back to precise safety kernel + return SafetyKernel(point, in); +} + +//////////////////////////////////////////////////////////////////////////////// +/// ComputeNormal interface + +void O2Tessellated::ComputeNormal(const Double_t* point, const Double_t* dir, Double_t* norm) const +{ + // We take the approach to identify closest facet to the point via safety + // and returning the normal from this face. + + // TODO: Before doing that we could check for cached points from other queries + + // use safety kernel + int closest_face_id = -1; + SafetyKernel(point, true, &closest_face_id); + + if (closest_face_id < 0) { + norm[0] = 1.; + norm[1] = 0.; + norm[2] = 0.; + return; + } + + const auto& n = fOutwardNormals[closest_face_id]; + norm[0] = n[0]; + norm[1] = n[1]; + norm[2] = n[2]; + + // change sign depending on dir + if (norm[0] * dir[0] + norm[1] * dir[1] + norm[2] * dir[2] < 0) { + norm[0] = -norm[0]; + norm[1] = -norm[1]; + norm[2] = -norm[2]; + } + return; +} + +//////////////////////////////////////////////////////////////////////////////// +/// trivial (non-BVH) DistFromInside function + +Double_t O2Tessellated::DistFromInside_Loop(const Double_t* point, const Double_t* dir) const +{ + Vertex_t p(point[0], point[1], point[2]); + Vertex_t d(dir[0], dir[1], dir[2]); + + double dist = Big(); + for (size_t i = 0; i < fFacets.size(); ++i) { + const auto& facet = fFacets[i]; + const auto& n = fOutwardNormals[i]; + + // Only exiting surfaces are relevant (from inside--> dot product must be positive) + if (n.Dot(d) <= 0.0) { + continue; + } + + const auto& v0 = fVertices[facet[0]]; + const auto& v1 = fVertices[facet[1]]; + const auto& v2 = fVertices[facet[2]]; + + const double t = rayTriangle(p, d, v0, v1, v2, 0.); + + if (t < dist) { + dist = t; + } + } + return dist; +} + +//////////////////////////////////////////////////////////////////////////////// +/// trivial (non-BVH) DistFromOutside function + +Double_t O2Tessellated::DistFromOutside_Loop(const Double_t* point, const Double_t* dir) const +{ + Vertex_t p(point[0], point[1], point[2]); + Vertex_t d(dir[0], dir[1], dir[2]); + + double dist = Big(); + for (size_t i = 0; i < fFacets.size(); ++i) { + const auto& facet = fFacets[i]; + const auto& n = fOutwardNormals[i]; + + // Only exiting surfaces are relevant (from outside, the dot product must be negative) + if (n.Dot(d) > 0.0) { + continue; + } + + const auto& v0 = fVertices[facet[0]]; + const auto& v1 = fVertices[facet[1]]; + const auto& v2 = fVertices[facet[2]]; + + const double t = rayTriangle(p, d, v0, v1, v2, 0.); + + if (t < dist) { + dist = t; + } + } + return dist; +} + +//////////////////////////////////////////////////////////////////////////////// +/// trivial (non-BVH) Contains + +bool O2Tessellated::Contains_Loop(const Double_t* point) const +{ + // Fixed ray direction + const Vertex_t test_dir{1.0, 1.41421356237, 1.73205080757}; + + Vertex_t p(point[0], point[1], point[2]); + + int crossings = 0; + for (size_t i = 0; i < fFacets.size(); ++i) { + const auto& facet = fFacets[i]; + + const auto& v0 = fVertices[facet[0]]; + const auto& v1 = fVertices[facet[1]]; + const auto& v2 = fVertices[facet[2]]; + + const double t = rayTriangle(p, test_dir, v0, v1, v2, 0.); + if (t != std::numeric_limits::infinity()) { + ++crossings; + } + } + return (crossings & 1); +} + +//////////////////////////////////////////////////////////////////////////////// +/// Custom streamer which performs Closing on read. +/// Recalculation of BVH and normals is fast + +void O2Tessellated::Streamer(TBuffer& b) +{ + if (b.IsReading()) { + b.ReadClassBuffer(O2Tessellated::Class(), this); + CloseShape(false); // close shape but do not re-perform checks + } else { + b.WriteClassBuffer(O2Tessellated::Class(), this); + } +} + +//////////////////////////////////////////////////////////////////////////////// +/// Calculate the normals + +void O2Tessellated::CalculateNormals() +{ + fOutwardNormals.clear(); + for (auto& facet : fFacets) { + auto& v1 = fVertices[facet[0]]; + auto& v2 = fVertices[facet[1]]; + auto& v3 = fVertices[facet[2]]; + using Vec3d = Vec3f; + auto norm = triangleNormal(Vec3d{v1[0], v1[1], v1[2]}, Vec3d{v2[0], v2[1], v2[2]}, Vec3d{v3[0], v3[1], v3[2]}); + fOutwardNormals.emplace_back(Vertex_t{norm.x, norm.y, norm.z}); + } +} + +// NOLINTEND \ No newline at end of file diff --git a/Detectors/Base/src/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/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/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/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/O2MCApplication.cxx b/Steer/src/O2MCApplication.cxx index 584598d350581..f832ab70ab121 100644 --- a/Steer/src/O2MCApplication.cxx +++ b/Steer/src/O2MCApplication.cxx @@ -37,6 +37,11 @@ #include "SimConfig/GlobalProcessCutSimParam.h" #include "DetectorsBase/GeometryManagerParam.h" #include +#include +#include +#include +#include +#include namespace o2 { @@ -209,10 +214,65 @@ 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() +{ + // 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/macro/build_geometry.C b/macro/build_geometry.C index 6b13f2eac2766..ccc3b13fe728d 100644 --- a/macro/build_geometry.C +++ b/macro/build_geometry.C @@ -63,6 +63,8 @@ #include #endif +#include + using Return = o2::base::Detector*; void finalize_geometry(FairRunSim* run); @@ -182,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 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 From 17d865e646a90d7e19ae43424d2404dd163885ec Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Fri, 13 Feb 2026 16:34:50 +0100 Subject: [PATCH 261/701] Fix Header info forwarding --- Generators/src/GeneratorHybrid.cxx | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) diff --git a/Generators/src/GeneratorHybrid.cxx b/Generators/src/GeneratorHybrid.cxx index 2a13f9876e717..f853b772e3cd3 100644 --- a/Generators/src/GeneratorHybrid.cxx +++ b/Generators/src/GeneratorHybrid.cxx @@ -420,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(); @@ -441,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++; } @@ -481,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 From 8361c429fd87b9d0cb215b1ac83f5ee1fd162269 Mon Sep 17 00:00:00 2001 From: ddobrigk Date: Sat, 14 Feb 2026 09:47:17 +0100 Subject: [PATCH 262/701] Add option to compress out non-dEdx info in TrackQA table (#15045) * Add compress-out option for non-dEdx info in TrackQA * Add compress-out option for non-dEdx info in TrackQA * Retain also TPC-only tracks used by svtx, strangeness tracking * Please consider the following formatting changes --------- Co-authored-by: ALICE Action Bot --- .../AODProducerWorkflowSpec.h | 1 + Detectors/AOD/src/AODProducerWorkflowSpec.cxx | 34 ++++++++++--------- 2 files changed, 19 insertions(+), 16 deletions(-) diff --git a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h index 2d16f343dc1eb..2c58db42ed856 100644 --- a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h +++ b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h @@ -238,6 +238,7 @@ class AODProducerWorkflowDPL : public Task 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/AODProducerWorkflowSpec.cxx b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx index be169ad4be19d..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)) || (src != GIndex::TPC && mTrackQCKeepGlobalTracks); + 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) { @@ -1720,6 +1720,7 @@ void AODProducerWorkflowDPL::init(InitContext& ic) } } 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"); @@ -3356,6 +3357,7 @@ DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, boo 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"}}, From b87ed891b1f785c042f7cc9f2d490d3f5a6dd3f1 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 15 Feb 2026 14:29:48 +0100 Subject: [PATCH 263/701] DPL Examples: use the new completion policy for parallel processing (#15059) This demonstrates how the new policy can be used in conjunction with wildcards in order to simplify parallelism based on the subSpecification. --- .../Framework/CompletionPolicyHelpers.h | 1 - .../TestWorkflows/src/o2ParallelWorkflow.cxx | 77 ++++++++++++------- 2 files changed, 48 insertions(+), 30 deletions(-) diff --git a/Framework/Core/include/Framework/CompletionPolicyHelpers.h b/Framework/Core/include/Framework/CompletionPolicyHelpers.h index 9fce626854e5b..09ea8b7ea6b61 100644 --- a/Framework/Core/include/Framework/CompletionPolicyHelpers.h +++ b/Framework/Core/include/Framework/CompletionPolicyHelpers.h @@ -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" 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) { }; }}}); From cbace3965ebdcb915f78a5535d447571ae056a68 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sun, 15 Feb 2026 21:48:06 +0100 Subject: [PATCH 264/701] GPU TPC: Assume more sector track hits for low field data --- GPU/GPUTracking/DataTypes/GPUMemorySizeScalers.h | 6 ++++-- GPU/GPUTracking/SectorTracker/GPUTPCTracker.cxx | 5 +++-- 2 files changed, 7 insertions(+), 4 deletions(-) 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/SectorTracker/GPUTPCTracker.cxx b/GPU/GPUTracking/SectorTracker/GPUTPCTracker.cxx index 03931f73a4a12..506f90c55abf3 100644 --- a/GPU/GPUTracking/SectorTracker/GPUTPCTracker.cxx +++ b/GPU/GPUTracking/SectorTracker/GPUTPCTracker.cxx @@ -144,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; From 23a36dfe2517df697788634b9a64088e86c0822b Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Mon, 16 Feb 2026 14:10:23 +0100 Subject: [PATCH 265/701] Add Kine publisher test (#15058) --- prodtests/full_system_test.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/prodtests/full_system_test.sh b/prodtests/full_system_test.sh index 82021d6c65e63..07ccdf01d4566 100755 --- a/prodtests/full_system_test.sh +++ b/prodtests/full_system_test.sh @@ -168,6 +168,13 @@ taskwrapper collcontext.log o2-steer-colcontexttool \ 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_MCHeader.root fi From 75a357d3e2fcb58cadca886055691d53eac62004 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiktor=20Piero=C5=BCak?= <94726725+wpierozak@users.noreply.github.com> Date: Mon, 16 Feb 2026 14:14:28 +0100 Subject: [PATCH 266/701] Afit 124 (#14985) * FV0: included dead channel map in reconstruction * FV0: fixed fetch of dead channel map in reco * FV0:Added debug log when data from dead channel is discard * FV0: changed handling of dead channel in reco * Implementation DeadChannelMap in FDD/FT0 * Updaed FDD reco * FIT:Fixed application of DeadChannelMap in FV0 and FDD reconstruction task * Fixed missing constructor arguments for FDD RecoSepc * Please consider the following formatting changes --------- Co-authored-by: wpierozak Co-authored-by: wpierozak Co-authored-by: ALICE Action Bot --- .../include/FDDReconstruction/Reconstructor.h | 9 ++++++- .../FDD/reconstruction/src/Reconstructor.cxx | 5 ++++ .../include/FDDWorkflow/RecoWorkflow.h | 2 +- .../include/FDDWorkflow/ReconstructorSpec.h | 10 +++++-- .../FIT/FDD/workflow/src/RecoWorkflow.cxx | 4 +-- .../FDD/workflow/src/ReconstructorSpec.cxx | 23 ++++++++++++++-- .../FDD/workflow/src/fdd-reco-workflow.cxx | 4 ++- .../FT0Reconstruction/CollisionTimeRecoTask.h | 7 +++++ .../src/CollisionTimeRecoTask.cxx | 4 +++ .../include/FT0Workflow/RecoWorkflow.h | 2 +- .../include/FT0Workflow/ReconstructionSpec.h | 6 +++-- .../FIT/FT0/workflow/src/RecoWorkflow.cxx | 4 +-- .../FT0/workflow/src/ReconstructionSpec.cxx | 22 +++++++++++++-- .../FT0/workflow/src/ft0-reco-workflow.cxx | 6 +++-- .../include/FV0Reconstruction/BaseRecoTask.h | 6 +++-- .../FV0/reconstruction/src/BaseRecoTask.cxx | 27 +++++++++++-------- .../include/FV0Workflow/RecoWorkflow.h | 2 +- .../include/FV0Workflow/ReconstructionSpec.h | 6 +++-- .../FIT/FV0/workflow/src/RecoWorkflow.cxx | 5 ++-- .../FV0/workflow/src/ReconstructionSpec.cxx | 21 ++++++++++----- .../FV0/workflow/src/fv0-reco-workflow.cxx | 4 ++- 21 files changed, 135 insertions(+), 44 deletions(-) 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/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/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/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/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/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/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/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/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/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/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..b97186bbf81a8 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; @@ -53,18 +54,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 +82,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 +93,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 +103,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 +118,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/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); From 3e6876861dffa4a73c9c7fd8c858d6f7cb3f9554 Mon Sep 17 00:00:00 2001 From: Maximiliano Puccio Date: Mon, 16 Feb 2026 16:03:27 +0100 Subject: [PATCH 267/701] First version of the hit based CA tracker for ALICE3 IT/OT (#15066) * ITSTracking: change visibility of methods Change TimeFrame prepareClusters method visibility to protected Add computeTracksMClabels method to Tracker class * Hit based CA for ALICE3 tracker --- .../tracking/include/ITStracking/TimeFrame.h | 2 +- .../tracking/include/ITStracking/Tracker.h | 2 +- Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt | 3 +- .../ALICE3/TRK/macros/test/CMakeLists.txt | 10 + .../ALICE3/TRK/macros/test/CheckTracksCA.C | 345 ++++++++++++++++++ .../ALICE3/TRK/reconstruction/CMakeLists.txt | 34 ++ .../include/TRKReconstruction/TimeFrame.h | 73 ++++ .../src/TRKReconstructionLinkDef.h | 20 + .../TRK/reconstruction/src/TimeFrame.cxx | 189 ++++++++++ .../ALICE3/TRK/workflow/CMakeLists.txt | 10 +- .../Upgrades/ALICE3/TRK/workflow/README.md | 130 +++++++ .../include/TRKWorkflow/RecoWorkflow.h | 2 + .../include/TRKWorkflow/TrackWriterSpec.h | 31 ++ .../include/TRKWorkflow/TrackerSpec.h | 12 +- .../ALICE3/TRK/workflow/src/RecoWorkflow.cxx | 11 +- .../TRK/workflow/src/TrackWriterSpec.cxx | 57 +++ .../ALICE3/TRK/workflow/src/TrackerSpec.cxx | 336 ++++++++++++++++- .../TRK/workflow/src/trk-reco-workflow.cxx | 4 +- 18 files changed, 1251 insertions(+), 20 deletions(-) create mode 100644 Detectors/Upgrades/ALICE3/TRK/macros/test/CheckTracksCA.C create mode 100644 Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt create mode 100644 Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h create mode 100644 Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h create mode 100644 Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx create mode 100644 Detectors/Upgrades/ALICE3/TRK/workflow/README.md create mode 100644 Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackWriterSpec.h create mode 100644 Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackWriterSpec.cxx diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h index 4dbb9f09f6192..acc884ea68b8b 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h @@ -299,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/Upgrades/ALICE3/TRK/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt index e623239122658..6e3437c9d841b 100644 --- a/Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt @@ -12,4 +12,5 @@ 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/macros/test/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt index 379207eb07481..d9908bbfeb1e5 100644 --- a/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt @@ -18,4 +18,14 @@ o2_add_test_root_macro(CheckDigits.C 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/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/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..d2ca6fba132e1 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h @@ -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. +/// +/// \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); + + private: + ClassDefNV(TimeFrame, 1); +}; + +} // 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..686270826049b --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx @@ -0,0 +1,189 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not 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()); + ++clusterCountPerLayer[layer]; + totalNHits++; + } + trkHit->clear(); + } + + // 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 >= 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/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 98d4154f11dd8..7046955a20c2e 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/RecoWorkflow.h +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/RecoWorkflow.h @@ -14,6 +14,7 @@ #include "Framework/WorkflowSpec.h" #include "GPUDataTypesConfig.h" +#include namespace o2::trk { @@ -21,6 +22,7 @@ namespace reco_workflow { o2::framework::WorkflowSpec getWorkflow(bool useMC, + const std::string& hitRecoConfig, bool upstreamDigits = false, bool upstreamClusters = false, bool disableRootOutput = false, diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackWriterSpec.h b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackWriterSpec.h new file mode 100644 index 0000000000000..105504e7c9fe6 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackWriterSpec.h @@ -0,0 +1,31 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file TrackWriterSpec.h + +#ifndef O2_TRK_TRACKWRITER +#define O2_TRK_TRACKWRITER + +#include "Framework/DataProcessorSpec.h" + +namespace o2 +{ +namespace trk +{ + +/// create a processor spec +/// write TRK tracks to ROOT file +o2::framework::DataProcessorSpec getTrackWriterSpec(bool useMC); + +} // namespace trk +} // namespace o2 + +#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 dac1826e21cf6..33b25737bbc29 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h @@ -19,6 +19,9 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" +#include + +#include "ITStracking/BoundedAllocator.h" #include "ITStracking/TrackingInterface.h" #include "GPUDataTypesConfig.h" @@ -26,6 +29,8 @@ #include "TStopwatch.h" +#include + namespace o2::trk { class TrackerDPL : public framework::Task @@ -33,6 +38,7 @@ class TrackerDPL : public framework::Task public: TrackerDPL(std::shared_ptr gr, bool isMC, + const std::string& hitRecoConfig, gpu::gpudatatypes::DeviceType dType = gpu::gpudatatypes::DeviceType::CPU); ~TrackerDPL() override = default; void init(framework::InitContext& ic) 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 */ diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx index 09d447a576e48..5f6cbe2f96b04 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx @@ -11,12 +11,16 @@ #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, @@ -24,7 +28,12 @@ framework::WorkflowSpec getWorkflow(bool useMC, 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; } 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 868a8acc0fc6e..8f26478f4496e 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx @@ -11,10 +11,24 @@ #include +#include "DetectorsBase/GeometryManager.h" +#include "ITStracking/TimeFrame.h" +#include "ITStracking/Configuration.h" +#include "Field/MagneticField.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 +39,14 @@ using Vertex = o2::dataformats::Vertex>; TrackerDPL::TrackerDPL(std::shared_ptr gr, bool isMC, + 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 +66,288 @@ 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 = o2::field::MagneticField::createNominalField(std::round(mHitRecoConfig["geometry"]["bz"].get()), true); + 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); + + 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); + } + + 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 +362,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 +375,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 +419,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 8f44b01da1c9c..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,6 +67,7 @@ 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 extDigits = configcontext.options().get("digits-from-upstream"); @@ -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); } From 712170392332bf82ef808d71c26d6a805b8e675f Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Tue, 17 Feb 2026 14:47:47 +0100 Subject: [PATCH 268/701] DPL Analysis: cleanup AnalysisTask.h and ASoA.h (#15008) --- Framework/Core/include/Framework/ASoA.h | 62 +++---- .../Core/include/Framework/AnalysisManagers.h | 6 - .../Core/include/Framework/AnalysisTask.h | 163 +++++++++--------- .../Core/include/Framework/Configurable.h | 15 +- Framework/Core/test/test_Concepts.cxx | 1 + .../include/Framework/StructToTuple.h | 46 ++--- 6 files changed, 136 insertions(+), 157 deletions(-) diff --git a/Framework/Core/include/Framework/ASoA.h b/Framework/Core/include/Framework/ASoA.h index 4fd35e0dc5065..7586d6a6d3c63 100644 --- a/Framework/Core/include/Framework/ASoA.h +++ b/Framework/Core/include/Framework/ASoA.h @@ -405,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 @@ -1293,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(); @@ -2724,7 +2727,7 @@ consteval auto getIndexTargets() return !(*mColumnIterator).empty(); \ } \ \ - template \ + template \ auto _Getter_##_as() const \ { \ if (O2_BUILTIN_UNLIKELY(mBinding.ptr == nullptr)) { \ @@ -2734,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)) { \ @@ -2747,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 \ @@ -3090,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; \ } \ diff --git a/Framework/Core/include/Framework/AnalysisManagers.h b/Framework/Core/include/Framework/AnalysisManagers.h index fd41a079c6570..121ce7f4b4a77 100644 --- a/Framework/Core/include/Framework/AnalysisManagers.h +++ b/Framework/Core/include/Framework/AnalysisManagers.h @@ -534,12 +534,6 @@ void bindExternalIndicesPartition(P& partition, T*... tables) } /// Cache handling -template -bool preInitializeCache(InitContext&, T&) -{ - return false; -} - template bool initializeCache(ProcessingContext&, T&) { diff --git a/Framework/Core/include/Framework/AnalysisTask.h b/Framework/Core/include/Framework/AnalysisTask.h index eb98d55cc24b2..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" @@ -66,17 +65,20 @@ 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>()) { Entry e{soa::getLabelFromTypeForKey>(key), soa::getMatcherFromTypeForKey>(key), key, enabled}; if constexpr (o2::soa::is_smallgroups>) { @@ -85,9 +87,9 @@ struct AnalysisDataProcessorBuilder { framework::updatePairList(bk, e); } } - }(), + }(bk, bku, enabled, key), ...); - }(framework::pack{}); + }(framework::pack{}, bk, bku, enabled); } template @@ -171,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. @@ -180,37 +182,37 @@ 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&) { } @@ -285,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); @@ -347,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; @@ -359,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)...); @@ -376,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); @@ -394,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)...); @@ -523,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](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); @@ -541,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); }; @@ -585,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([&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/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/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/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_ From 0355d19f2aad1726cdd020029b5eef7b9bb4625b Mon Sep 17 00:00:00 2001 From: Marco van Leeuwen Date: Tue, 17 Feb 2026 19:12:33 +0100 Subject: [PATCH 269/701] [ALICE3] Fix geometry overlaps in tracker (ML/OT) (#15072) --- .../ALICE3/FT3/simulation/src/FT3Module.cxx | 2 +- .../ALICE3/TRK/simulation/src/TRKServices.cxx | 42 +++++++++---------- 2 files changed, 22 insertions(+), 22 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx index efcad74bc2cb9..9e24247958c06 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx @@ -108,7 +108,7 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double double carbonFiberThickness = 0.01; - double foamSpacingThickness = 0.5; + double foamSpacingThickness = 1.0; int dist_offset = 0; diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx index cbe00e8fc9e89..25c59b3c8fd4a 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx @@ -320,26 +320,26 @@ void TRKServices::createMiddleServices(TGeoVolume* motherVolume) const float rMaxMiddleBarrelDisk = 35.f; const float zLengthMiddleBarrel = 64.2f; 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); @@ -357,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); } @@ -501,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 From e57a6edf96c71b344ede9fe78b4b0e3c69335069 Mon Sep 17 00:00:00 2001 From: Pavel Larionov Date: Tue, 17 Feb 2026 19:15:56 +0100 Subject: [PATCH 270/701] Configurable VD design, set def to IRIS 4, remove IRIS disks (#15055) --- .../TRK/base/include/TRKBase/TRKBaseParam.h | 9 ++++++ .../ALICE3/TRK/simulation/src/Detector.cxx | 28 ++++++++++++++++--- .../TRK/simulation/src/VDGeometryBuilder.cxx | 2 +- 3 files changed, 34 insertions(+), 5 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h index 7f2f7f32b79d9..d5e11313c0f0c 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h @@ -31,6 +31,13 @@ enum eLayout { kStaggered, }; +enum eVDLayout { + kIRIS4 = 0, + kIRISFullCyl, + kIRIS5, + kIRIS4a, +}; + struct TRKBaseParam : public o2::conf::ConfigurableParamHelper { std::string configFile = ""; float serviceTubeX0 = 0.02f; // X0 Al2O3 @@ -40,9 +47,11 @@ struct TRKBaseParam : public o2::conf::ConfigurableParamHelper { 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/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx index 06fd2d9670b67..556b016f22553 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx @@ -261,12 +261,32 @@ void Detector::createGeometry() mServices.createServices(vTRK); // Build the VD using the petal builder - // Choose the VD design (here: IRIS4 by default). - // You can wire this to a parameter in TRKBaseParam if desired. - // Alternatives: createIRIS5Geometry(vTRK); createIRIS4aGeometry(vTRK); + // Choose the VD design based on TRKBaseParam.layoutVD + auto& trkPars = TRKBaseParam::Instance(); o2::trk::clearVDSensorRegistry(); - o2::trk::createIRISGeometryFullCyl(vTRK); + + 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(); diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx index 6ce04bb8443ef..b06faa38211bb 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx @@ -749,7 +749,7 @@ static TGeoVolume* buildPetalAssembly(int nPetals, /*fullCylindricalRadialWalls=*/fullCylinders); addBarrelLayers(petalAsm, nPetals, petalID, rectangularL0, fullCylinders); - addDisks(petalAsm, nPetals, petalID, fullCylinders); + // addDisks(petalAsm, nPetals, petalID, fullCylinders); // disks removed according to the v3b layout addColdPlate(petalAsm, nPetals, petalID, /*fullCylinders=*/false); addIRISServiceModulesSegmented(petalAsm, nPetals); From e5768cde630297876b47f1c24b5da3489f56228a Mon Sep 17 00:00:00 2001 From: Andrea Sofia Triolo Date: Tue, 17 Feb 2026 19:16:53 +0100 Subject: [PATCH 271/701] ALICE3-TRK: adapt ordering key for digits to the large number of columns in the VD (#15070) --- .../simulation/include/TRKSimulation/ChipDigitsContainer.h | 6 ++++++ 1 file changed, 6 insertions(+) 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); }; From 06150434b8a30967fb5449016758b0441748f0b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Tue, 17 Feb 2026 21:54:34 +0100 Subject: [PATCH 272/701] A3: Add geometries for IOTOF (#15073) - fix enabling and disabling of backward TOF - add v3b versions of IOTOF --- .../base/include/IOTOFBase/IOTOFBaseParam.h | 1 + .../include/IOTOFSimulation/Detector.h | 2 +- .../ALICE3/IOTOF/simulation/src/Detector.cxx | 73 ++++++++++++++----- 3 files changed, 56 insertions(+), 20 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h index 10d8c5ced94dd..bf605797cbfe5 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h @@ -25,6 +25,7 @@ struct IOTOFBaseParam : public o2::conf::ConfigurableParamHelper bool enableOuterTOF = true; bool enableForwardTOF = true; bool enableBackwardTOF = true; + std::string detectorPattern = ""; 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..f39a43733ccab 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 = ""); void configServices(); void createMaterials(); diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx index a2bba7cc5fe35..3a971e81a610d 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,9 @@ 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); } Detector::~Detector() @@ -56,19 +56,54 @@ 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) { + + 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; + if (pattern == "") { + } else if (pattern == "v3b") { + LOG(info) << "Configuring IOTOF layers with v3b pattern"; + 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 (itof) { - mITOFLayer = ITOFLayer(std::string{GeometryTGeo::getITOFLayerPattern()}, 19.f, 0.f, 124.f, 0.f, 0.02f, true); // iTOF + mITOFLayer = ITOFLayer(std::string{GeometryTGeo::getITOFLayerPattern()}, radiusInnerTof, 0.f, lengthInnerTof, 0.f, 0.02f, true); // iTOF } if (otof) { - mOTOFLayer = OTOFLayer(std::string{GeometryTGeo::getOTOFLayerPattern()}, 85.f, 0.f, 680.f, 0.f, 0.02f, true); // oTOF + mOTOFLayer = OTOFLayer(std::string{GeometryTGeo::getOTOFLayerPattern()}, radiusOuterTof, 0.f, lengthOuterTof, 0.f, 0.02f, true); // oTOF } 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, false); // 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, false); // bTOF } } @@ -214,28 +249,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 +299,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 From 41d9be4b011d3c9cc04742b271e814661002379d Mon Sep 17 00:00:00 2001 From: Maximiliano Puccio Date: Tue, 17 Feb 2026 22:08:43 +0100 Subject: [PATCH 273/701] [ALICE3] Adapt CA for 2T simulations (#15075) --- .../reconstruction/include/TRKReconstruction/TimeFrame.h | 3 --- .../Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx | 7 ++++++- Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx | 7 ++++++- 3 files changed, 12 insertions(+), 5 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h index d2ca6fba132e1..f42a1c897efb6 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h @@ -62,9 +62,6 @@ class TimeFrame : public o2::its::TimeFrame /// \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); - - private: - ClassDefNV(TimeFrame, 1); }; } // namespace trk diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx index 686270826049b..610a08450d5ee 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx @@ -64,10 +64,12 @@ int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, } int subDetID = gman->getSubDetID(hit.GetDetectorID()); const int layer = startLayer[subDetID] + gman->getLayer(hit.GetDetectorID()); + if (layer >= nLayers) { + continue; + } ++clusterCountPerLayer[layer]; totalNHits++; } - trkHit->clear(); } // Reserve memory for all layers @@ -106,6 +108,9 @@ int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, 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); diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx index 8f26478f4496e..8fc67f0fa5567 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx @@ -10,11 +10,13 @@ // 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" @@ -276,7 +278,7 @@ void TrackerDPL::run(ProcessingContext& pc) itsTrackerTraits.adoptTimeFrame(static_cast*>(&timeFrame)); itsTracker.adoptTimeFrame(timeFrame); itsTrackerTraits.setBz(mHitRecoConfig["geometry"]["bz"].get()); - auto field = o2::field::MagneticField::createNominalField(std::round(mHitRecoConfig["geometry"]["bz"].get()), true); + 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(); @@ -291,6 +293,7 @@ void TrackerDPL::run(ProcessingContext& pc) 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); @@ -304,6 +307,8 @@ void TrackerDPL::run(ProcessingContext& pc) 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(); From d95be4db7b5fe479e7adbe8b45094f628d6d97ea Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Tue, 17 Feb 2026 19:47:42 +0100 Subject: [PATCH 274/701] o2-sim: Possibility to switch between TGeo and Geant4 navigation --- Common/SimConfig/include/SimConfig/G4Params.h | 9 +++++++++ Common/SimConfig/src/SimConfigLinkDef.h | 1 + Detectors/gconfig/g4Config.C | 11 ++++++++++- Steer/src/O2MCApplication.cxx | 6 ++++++ 4 files changed, 26 insertions(+), 1 deletion(-) 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/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/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/Steer/src/O2MCApplication.cxx b/Steer/src/O2MCApplication.cxx index f832ab70ab121..1e3f925042d01 100644 --- a/Steer/src/O2MCApplication.cxx +++ b/Steer/src/O2MCApplication.cxx @@ -42,6 +42,7 @@ #include #include #include +#include "SimConfig/G4Params.h" namespace o2 { @@ -223,6 +224,11 @@ bool O2MCApplicationBase::MisalignGeometry() 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. From d384645a99a311b75d221ca280d924a6bdfbb787 Mon Sep 17 00:00:00 2001 From: Andrea Sofia Triolo Date: Wed, 18 Feb 2026 15:11:43 +0100 Subject: [PATCH 275/701] ALICE3-TRK: fix detector ID assignment to hits (#15074) --- .../Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx index b32c89164f18a..059a35520c1a0 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx @@ -416,15 +416,15 @@ TString GeometryTGeo::getMatrixPath(int index) const // 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_%d/", getTRKPetalAssemblyPattern(), petalcase, petalcase + 1); // PETAL_n + path += Form("%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalDiskPattern(), disk); // PETALCASEx_DISKy_1 + path += Form("%s%d_%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalDiskPattern(), disk, getTRKChipPattern(), disk); // PETALCASEx_DISKy_TRKChipy_1 path += Form("%s%d_%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalDiskPattern(), disk, getTRKSensorPattern(), disk); // PETALCASEx_DISKy_TRKSensory_1 } else if (layer >= 0) { path += Form("%s_%d_%d/", getTRKPetalAssemblyPattern(), petalcase, petalcase + 1); // PETAL_n path += Form("%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalLayerPattern(), layer); // PETALCASEx_LAYERy_1 // path += Form("%s%d_%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalLayerPattern(), layer, getTRKStavePattern(), layer); // PETALCASEx_LAYERy_TRKStavey_1 - // path += Form("%s%d_%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalLayerPattern(), layer, getTRKChipPattern(), layer); // PETALCASEx_LAYERy_TRKChipy_1 + path += Form("%s%d_%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalLayerPattern(), layer, 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 @@ -962,9 +962,9 @@ int GeometryTGeo::extractNumberOfChipsPerPetalVD() const for (int i = 0; i < subNodes->GetEntriesFast(); i++) { auto* subNode = dynamic_cast(subNodes->At(i)); - if (strstr(subNode->GetName(), getTRKSensorPattern()) != nullptr) { + if (strstr(subNode->GetName(), getTRKChipPattern()) != nullptr) { numberOfChips++; - LOGP(debug, "Found sensor in {}: {}", nodeName, subNode->GetName()); + LOGP(debug, "Found chip in {}: {}", nodeName, subNode->GetName()); } } } From 0b483951ab025a97601f15d861ba4df8de3c396e Mon Sep 17 00:00:00 2001 From: altsybee Date: Thu, 19 Feb 2026 07:54:50 +0100 Subject: [PATCH 276/701] [ALICE3] Change to upper-case 'S' in "FT3sensor_*" strings (#15078) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Change to upper-case 'S' in "FT3sensor_*" * Change to upper-case 'S' in "FT3sensor_*" in FT3Module.cxx * Update comments in exportLayout method Clarified comments in exportLayout function. --------- Co-authored-by: Nicolò Jacazio --- Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx | 7 ++++--- .../Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx | 8 ++++---- 2 files changed, 8 insertions(+), 7 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx index 9303979ada930..4b139272834f1 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx @@ -129,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"; @@ -795,8 +796,8 @@ void Detector::defineSensitiveVolumes() 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); + 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); diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx index 9e24247958c06..9318554837706 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx @@ -481,7 +481,7 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double 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); + 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); @@ -495,7 +495,7 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double } else { - std::string sensor_name = "FT3sensor_front_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + 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); @@ -652,7 +652,7 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double if (sensor_width == 2.5) { - std::string sensor_name = "FT3sensor_back_" + std::to_string(layerNumber) + "_" + std::to_string(direction) + "_" + std::to_string(sensor_count); + 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); @@ -666,7 +666,7 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double } 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); + 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); From db8db2f046de80b4b70c4de41d0b05e07568aefd Mon Sep 17 00:00:00 2001 From: Roman Lietava Date: Thu, 19 Feb 2026 10:17:05 +0100 Subject: [PATCH 277/701] Ctpdev: getting list of unmasked inputs (#15082) * dev:CTpCfg list of used inputs * clang --- .../include/DataFormatsCTP/Configuration.h | 3 ++- .../Detectors/CTP/src/Configuration.cxx | 19 +++++++++++++++++-- 2 files changed, 19 insertions(+), 3 deletions(-) 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 From efa08980b26cf9ee523abf6d427b7e41dee1ed0d Mon Sep 17 00:00:00 2001 From: Maximiliano Puccio Date: Thu, 19 Feb 2026 10:18:54 +0100 Subject: [PATCH 278/701] [ITS] Protect ultra low pt selections at the tracklet level (#15079) Checked on Pb-Pb simulation that this does not change the number of reconstructed tracks --- Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx index 70f4e3d1d3fc7..29fb4ac4c69b5 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx @@ -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); } } From 98820e9b681677c61f1acac3e5f768cb806eb319 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Thu, 19 Feb 2026 11:53:32 +0100 Subject: [PATCH 279/701] EMCAL: Delete unused files (#15026) --- .../DataFormatsEMCAL/EMCALChannelData.h | 55 ------ .../Detectors/EMCAL/src/EMCALChannelData.cxx | 19 -- .../reconstruction/run/rawReaderTRUDigits.cxx | 171 ------------------ 3 files changed, 245 deletions(-) delete mode 100644 DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/EMCALChannelData.h delete mode 100644 DataFormats/Detectors/EMCAL/src/EMCALChannelData.cxx delete mode 100644 Detectors/EMCAL/reconstruction/run/rawReaderTRUDigits.cxx diff --git a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/EMCALChannelData.h b/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/EMCALChannelData.h deleted file mode 100644 index 3c014d37e6f9e..0000000000000 --- a/DataFormats/Detectors/EMCAL/include/DataFormatsEMCAL/EMCALChannelData.h +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file EMCALChannelData.h -/// \brief - -/// \class EMCALChannelCalibrator -/// \brief Class to store the data format for calibraton of the EMCal -/// \author Hannah Bossi, Yale University -/// \ingroup DetectorEMCAL -/// \since Feb 11, 2021 - -#ifndef ALICEO2_EMCALCHANNELDATA_H -#define ALICEO2_EMCALCHANNELDATA_H - -#include "Rtypes.h" - -namespace o2 -{ -namespace dataformats -{ -class EMCALChannelData -{ - public: - EMCALChannelData(int cellID, int timestamp, int flags = 0, int events) : mEMCALCellID(cellID), mTimestamp(timestamp), mFlags(flags){}; - EMCALChannelData() = default; - ~EMCALChannelData() = default; - - void setEMCALCellID(int index) { mEMCALCellID = index; } - int getEMCALCellID() const { return mEMCALCellID; } - - void setTimestamp(int ts) { mTimestamp = ts; } - int getTimestamp() const { return mTimestamp; } - - void setFlags(int flags) { mFlags = flags; } - float getFlags() const { return mFlags; } - - private: - int mEMCALCellID; ///< EMCal Cell ID - int mTimestamp; ///< timestamp in seconds - unsigned char mFlags; ///< bit mask with quality flags (to be defined) - - ClassDefNV(EMCALChannelData, 1); -}; -} // namespace dataformats -} // namespace o2 -#endif diff --git a/DataFormats/Detectors/EMCAL/src/EMCALChannelData.cxx b/DataFormats/Detectors/EMCAL/src/EMCALChannelData.cxx deleted file mode 100644 index 8affa29259f7a..0000000000000 --- a/DataFormats/Detectors/EMCAL/src/EMCALChannelData.cxx +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file EMCALChannelData.cxx -/// \brief Class to store the data format for calibraton of the EMCal - -#include "DataFormatsEMCAL/EMCALChannelData.h" - -using namespace o2::dataformats; - -ClassImp(o2::dataformats::EMCALChannelData; diff --git a/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 From 7b4f8f168ca7051f84d73b9ade285882c048348e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Thu, 19 Feb 2026 10:14:24 +0100 Subject: [PATCH 280/701] TPC: Delete unused files --- .../TPCReconstruction/ClusterContainer.h | 65 ------------------- .../src/time-series-merge-integrator.cxx | 34 ---------- .../TPC/workflow/src/time-series-reader.cxx | 25 ------- 3 files changed, 124 deletions(-) delete mode 100644 Detectors/TPC/reconstruction/include/TPCReconstruction/ClusterContainer.h delete mode 100644 Detectors/TPC/workflow/src/time-series-merge-integrator.cxx delete mode 100644 Detectors/TPC/workflow/src/time-series-reader.cxx 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/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/time-series-reader.cxx b/Detectors/TPC/workflow/src/time-series-reader.cxx deleted file mode 100644 index ccedbdf4f9599..0000000000000 --- a/Detectors/TPC/workflow/src/time-series-reader.cxx +++ /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. - -#include "TPCWorkflow/TPCTimeSeriesReaderSpec.h" -#include "CommonUtils/ConfigurableParam.h" -#include "Framework/ConfigParamSpec.h" - -using namespace o2::framework; - -#include "Framework/runDataProcessing.h" - -WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) -{ - WorkflowSpec wf; - wf.emplace_back(o2::tpc::getTPCTimeSeriesReaderSpec()); - return wf; -} From 1d4b3ef48aef20e6866379301de6e9d94ff234c2 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Tue, 17 Feb 2026 22:15:26 +0100 Subject: [PATCH 281/701] DPL: add ability to get the toplevel service registry This will allows non DPL code to still exploit some of the DPL services, like monitoring. --- Framework/Core/CMakeLists.txt | 1 + .../include/Framework/ServiceRegistryRef.h | 2 ++ Framework/Core/src/ServiceRegistryRef.cxx | 25 +++++++++++++++++++ Framework/Core/src/runDataProcessing.cxx | 1 + 4 files changed, 29 insertions(+) create mode 100644 Framework/Core/src/ServiceRegistryRef.cxx diff --git a/Framework/Core/CMakeLists.txt b/Framework/Core/CMakeLists.txt index 7357167a3fcd8..e6a8db1077136 100644 --- a/Framework/Core/CMakeLists.txt +++ b/Framework/Core/CMakeLists.txt @@ -125,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 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/src/ServiceRegistryRef.cxx b/Framework/Core/src/ServiceRegistryRef.cxx new file mode 100644 index 0000000000000..70728ad37eda7 --- /dev/null +++ b/Framework/Core/src/ServiceRegistryRef.cxx @@ -0,0 +1,25 @@ +// 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 "Framework/ServiceRegistryRef.h" +namespace o2::framework { + +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; +} + +} diff --git a/Framework/Core/src/runDataProcessing.cxx b/Framework/Core/src/runDataProcessing.cxx index ced884ebaa1ed..b99b5119e3ce9 100644 --- a/Framework/Core/src/runDataProcessing.cxx +++ b/Framework/Core/src/runDataProcessing.cxx @@ -1429,6 +1429,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(); From 813e416361a029fa023fd3fa6116cbb54a700bba Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Tue, 17 Feb 2026 22:15:26 +0100 Subject: [PATCH 282/701] CCDB: report stats about CCDB fetches / misses to DPL --- CCDB/include/CCDB/BasicCCDBManager.h | 11 ++++++++- CCDB/src/BasicCCDBManager.cxx | 2 ++ .../include/Framework/DataProcessingStats.h | 3 +++ Framework/Core/src/CommonServices.cxx | 24 +++++++++++++++++++ Framework/Core/src/runDataProcessing.cxx | 1 + 5 files changed, 40 insertions(+), 1 deletion(-) diff --git a/CCDB/include/CCDB/BasicCCDBManager.h b/CCDB/include/CCDB/BasicCCDBManager.h index 71287c2f07d76..fd0fe7aa6d05b 100644 --- a/CCDB/include/CCDB/BasicCCDBManager.h +++ b/CCDB/include/CCDB/BasicCCDBManager.h @@ -20,6 +20,8 @@ #include "CommonUtils/NameConf.h" #include "Framework/DataTakingContext.h" #include "Framework/DefaultsHelpers.h" +#include "Framework/ServiceRegistryRef.h" +#include "Framework/DataProcessingStats.h" #include #include #include @@ -340,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; } @@ -391,4 +400,4 @@ class BasicCCDBManager : public CCDBManagerInstance } // namespace o2::ccdb -#endif //O2_BASICCCDBMANAGER_H +#endif // O2_BASICCCDBMANAGER_H 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/Framework/Core/include/Framework/DataProcessingStats.h b/Framework/Core/include/Framework/DataProcessingStats.h index e32523c9abb08..a1f5c0eec5568 100644 --- a/Framework/Core/include/Framework/DataProcessingStats.h +++ b/Framework/Core/include/Framework/DataProcessingStats.h @@ -69,6 +69,9 @@ enum struct ProcessingStatsId : short { RESOURCES_MISSING, RESOURCES_INSUFFICIENT, RESOURCES_SATISFACTORY, + CCDB_CACHE_HIT, + CCDB_CACHE_MISS, + CCDB_CACHE_FAILURE, AVAILABLE_MANAGED_SHM_BASE = 512, }; diff --git a/Framework/Core/src/CommonServices.cxx b/Framework/Core/src/CommonServices.cxx index 6486406a06dca..06bc7969ebf1e 100644 --- a/Framework/Core/src/CommonServices.cxx +++ b/Framework/Core/src/CommonServices.cxx @@ -1152,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) { diff --git a/Framework/Core/src/runDataProcessing.cxx b/Framework/Core/src/runDataProcessing.cxx index b99b5119e3ce9..815fce47544d0 100644 --- a/Framework/Core/src/runDataProcessing.cxx +++ b/Framework/Core/src/runDataProcessing.cxx @@ -1248,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; } From d569998f2457c1eb4f35596b38cf9a4c4bea23a5 Mon Sep 17 00:00:00 2001 From: Francesco Noferini Date: Wed, 18 Feb 2026 17:20:27 +0100 Subject: [PATCH 283/701] path for LHCphase ccdb configurable --- .../calibration/include/TOFCalibration/LHCClockCalibrator.h | 3 +++ Detectors/TOF/calibration/src/LHCClockCalibrator.cxx | 2 +- .../TOF/calibration/testWorkflow/LHCClockCalibratorSpec.h | 4 ++++ 3 files changed, 8 insertions(+), 1 deletion(-) 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 "}}}}; From 67ab6d5a3922f27ce27376993bf5523b8c5d9e5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Thu, 5 Feb 2026 22:44:32 +0100 Subject: [PATCH 284/701] HMPID: Delete unused files --- .../HMPIDReconstruction/HmpidDecodeRawFile.h | 63 - .../HMPIDReconstruction/HmpidDecodeRawMem.h | 73 -- .../reconstruction/src/HmpidDecodeRawFile.cxx | 158 --- .../reconstruction/src/HmpidDecodeRawMem.cxx | 184 --- .../HMPID/reconstruction/src/HmpidDecoder.cxx | 1134 ----------------- .../HMPIDWorkflow/ClusterizerSpec.h_notused.h | 27 - .../HMPIDWorkflow/DigitReaderSpec.h_notused.h | 53 - 7 files changed, 1692 deletions(-) delete mode 100644 Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawFile.h delete mode 100644 Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawMem.h delete mode 100644 Detectors/HMPID/reconstruction/src/HmpidDecodeRawFile.cxx delete mode 100644 Detectors/HMPID/reconstruction/src/HmpidDecodeRawMem.cxx delete mode 100644 Detectors/HMPID/reconstruction/src/HmpidDecoder.cxx delete mode 100644 Detectors/HMPID/workflow/include/HMPIDWorkflow/ClusterizerSpec.h_notused.h delete mode 100644 Detectors/HMPID/workflow/include/HMPIDWorkflow/DigitReaderSpec.h_notused.h diff --git a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawFile.h b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawFile.h deleted file mode 100644 index e92e8375ad0d0..0000000000000 --- a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawFile.h +++ /dev/null @@ -1,63 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file HmpidDecodeRawFile.h -/// \author Antonio Franco - INFN Bari -/// \brief Derived Class for decoding Raw Data File stream -/// \version 1.0 -/// \date 24 set 2020 - -#ifndef COMMON_HMPIDDECODERAWFILE_H_ -#define COMMON_HMPIDDECODERAWFILE_H_ - -#include -#include -#include -#include -#include -#include - -#include "HMPIDReconstruction/HmpidDecoder.h" - -#define MAXFILENAMEBUFFER 512 -#define MAXRAWFILEBUFFER RAWBLOCKDIMENSION_W * 4 + 8 - -namespace o2 -{ -namespace hmpid -{ - -class HmpidDecodeRawFile : public HmpidDecoder -{ - public: - HmpidDecodeRawFile(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments); - HmpidDecodeRawFile(int numOfEquipments); - ~HmpidDecodeRawFile(); - - bool setUpStream(void* InpuFileName, long Size); - - private: - bool getBlockFromStream(uint32_t** streamPtr, uint32_t Size); - bool getHeaderFromStream(uint32_t** streamPtr); - bool getWordFromStream(uint32_t* word); - int fileExists(char* filewithpath); - void setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge); - - private: - FILE* fh; - char mInputFile[MAXFILENAMEBUFFER]; - uint32_t mFileBuffer[MAXRAWFILEBUFFER]; -}; - -} // namespace hmpid -} // namespace o2 -#endif /* COMMON_HMPIDDECODERAWFILE_H_ */ diff --git a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawMem.h b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawMem.h deleted file mode 100644 index d5d82d0f238e9..0000000000000 --- a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/HmpidDecodeRawMem.h +++ /dev/null @@ -1,73 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file HmpidDecodeRawMem.h -/// \author Antonio Franco - INFN Bari -/// \brief Derived Class for decoding Raw Data Memory stream -/// \version 1.0 -/// \date 24 set 2020 - -#ifndef COMMON_HMPIDDECODERAWMEM_H_ -#define COMMON_HMPIDDECODERAWMEM_H_ - -#include -#include -#include -#include -#include -#include - -#include "DataFormatsHMP/Digit.h" -#include "HMPIDBase/Geo.h" -#include "HMPIDReconstruction/HmpidDecoder.h" - -using namespace o2; - -namespace o2 -{ -namespace hmpid -{ - -class HmpidDecodeRawMem : public HmpidDecoder -{ - public: - HmpidDecodeRawMem(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments); - HmpidDecodeRawMem(int numOfEquipments); - ~HmpidDecodeRawMem(); - - bool setUpStream(void* Buffer, long BufferLen) override; - - private: - bool getBlockFromStream(uint32_t** streamPtr, uint32_t Size) override; - bool getHeaderFromStream(uint32_t** streamPtr) override; - bool getWordFromStream(uint32_t* word) override; - void setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge) override; - - private: -}; - -class HmpidDecodeRawDigit : public HmpidDecodeRawMem -{ - public: - HmpidDecodeRawDigit(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments); - HmpidDecodeRawDigit(int numOfEquipments); - ~HmpidDecodeRawDigit(); - - std::vector mDigits; - - private: - void setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge) override; -}; - -} // namespace hmpid -} // namespace o2 -#endif /* COMMON_HMPIDDECODERAWFILE_H_ */ diff --git a/Detectors/HMPID/reconstruction/src/HmpidDecodeRawFile.cxx b/Detectors/HMPID/reconstruction/src/HmpidDecodeRawFile.cxx deleted file mode 100644 index df97a4d2101e0..0000000000000 --- a/Detectors/HMPID/reconstruction/src/HmpidDecodeRawFile.cxx +++ /dev/null @@ -1,158 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file HmpidDecodeRawFile.cxx -/// \author Antonio Franco - INFN Bari -/// \brief Derived Class for decoding Raw Data File stream -/// \version 1.0 -/// \date 24 set 2020 - -/* ------ HISTORY --------- -*/ -#include // for LOG -#include "Framework/Logger.h" - -#include "HMPIDReconstruction/HmpidDecodeRawFile.h" - -using namespace o2::hmpid; - -/// Constructor with the default HMPID equipments map at P2 -/// @param[in] numOfEquipments : number of defined equipments [0..13] -HmpidDecodeRawFile::HmpidDecodeRawFile(int numOfEquipments) - : HmpidDecoder(numOfEquipments) -{ - fh = 0; -} - -/// Constructor with the HMPID address map -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -/// @param[in] *EqIds : the pointer to the Equipments ID array -/// @param[in] *CruIds : the pointer to the CRU ID array -/// @param[in] *LinkIds : the pointer to the Link ID array -HmpidDecodeRawFile::HmpidDecodeRawFile(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments) - : HmpidDecoder(EqIds, CruIds, LinkIds, numOfEquipments) -{ - fh = 0; -} - -/// Destructor -HmpidDecodeRawFile::~HmpidDecodeRawFile() -{ -} - -/// Setup the Input Stream with a File Handle -/// verify the existence and try to open it -/// @param[in] *FileName : the string that contains the File Name -/// @param[in] Size : not used -/// @returns True if the file is opened -/// @throws TH_FILENOTEXISTS Thrown if the file doesn't exists -/// @throws TH_OPENFILE Thrown if Fails to open the file -bool HmpidDecodeRawFile::setUpStream(void* FileName, long Size) -{ - strcpy(mInputFile, (const char*)FileName); - // files section ---- - if (!fileExists(mInputFile)) { - LOG(error) << "The input file " << mInputFile << " does not exist at this time."; - throw TH_FILENOTEXISTS; - } - // open the file - fh = fopen(mInputFile, "rb"); - if (fh == 0) { - LOG(error) << "ERROR to open Input file ! [" << mInputFile << "]"; - throw TH_OPENFILE; - } - - mActualStreamPtr = 0; // sets the pointer to the Buffer - mEndStreamPtr = 0; //sets the End of buffer - mStartStreamPtr = 0; - - return (true); -} - -/// Gets a sized chunk from the stream. Read from the file and update the pointers -/// ATTENTION : in order to optimize the disk accesses the block read pre-load a -/// complete Header+Payload block, the Size parameter is recalculated with the -/// dimension of the pack extract from the header field 'Offeset' -/// -/// verify the existence and try to open it -/// @param[in] **streamPtr : the pointer to the memory buffer -/// @param[in] Size : not used -/// @returns True if the file is opened -/// @throws TH_WRONGFILELEN Thrown if the file doesn't contains enough words -bool HmpidDecodeRawFile::getBlockFromStream(uint32_t** streamPtr, uint32_t Size) -{ - if (Size > MAXRAWFILEBUFFER) - return (false); - int nr = fread(mFileBuffer, sizeof(int32_t), HEADERDIMENSION_W, fh); - if (nr != HEADERDIMENSION_W) { - throw TH_WRONGFILELEN; - } - Size = ((mFileBuffer[2] & 0x0000FFFF) / sizeof(int32_t)) - HEADERDIMENSION_W; - nr = fread(mFileBuffer + HEADERDIMENSION_W, sizeof(int32_t), Size, fh); - LOG(debug) << " getBlockFromStream read " << nr << " of " << Size + HEADERDIMENSION_W << " words !"; - if (nr != Size) { - throw TH_WRONGFILELEN; - } - *streamPtr = mFileBuffer; - mStartStreamPtr = mFileBuffer; - mActualStreamPtr = mFileBuffer; - mEndStreamPtr = mFileBuffer + Size; - return (true); -} - -/// Reads the Header from the file -/// @param[in] **streamPtr : the pointer to the memory buffer -/// @returns True if the header is read -bool HmpidDecodeRawFile::getHeaderFromStream(uint32_t** streamPtr) -{ - bool flag = getBlockFromStream(streamPtr, RAWBLOCKDIMENSION_W); // reads the 8k block - mActualStreamPtr += HEADERDIMENSION_W; // Move forward for the first word - return (flag); -} - -/// Read one word from the pre-load buffer -/// @param[in] *word : the buffer for the read word -/// @returns True every time -bool HmpidDecodeRawFile::getWordFromStream(uint32_t* word) -{ - *word = *mActualStreamPtr; - mActualStreamPtr++; - return (true); -} - -/// ----- Sets the Pad ! ------ -/// this is an overloaded method. In this version the value of the charge -/// is used to update the statistical matrix of the base class -/// -/// @param[in] *eq : the pointer to the Equipment object -/// @param[in] col : the column [0..23] -/// @param[in] dil : the dilogic [0..9] -/// @param[in] ch : the channel [0..47] -/// @param[in] charge : the value of the charge -void HmpidDecodeRawFile::setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge) -{ - eq->setPad(col, dil, ch, charge); - return; -} - -/// Checks if the file exists ! -/// @param[in] *filewithpath : the File Name to check -/// @returns True if the file exists -int HmpidDecodeRawFile::fileExists(char* filewithpath) -{ - if (access(filewithpath, F_OK) != -1) { - return (true); - } else { - return (false); - } -} -o2::hmpid::Digit diff --git a/Detectors/HMPID/reconstruction/src/HmpidDecodeRawMem.cxx b/Detectors/HMPID/reconstruction/src/HmpidDecodeRawMem.cxx deleted file mode 100644 index 5a4f2acbfd97b..0000000000000 --- a/Detectors/HMPID/reconstruction/src/HmpidDecodeRawMem.cxx +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// -/// \file HmpidDecodeRawMem.cxx -/// \author Antonio Franco - INFN Bari -/// \brief Derived Class for decoding Raw Data Memory stream -/// \version 1.0 -/// \date 24 set 2020 - -/* ------ HISTORY --------- -*/ -#include // for LOG -#include "Framework/Logger.h" - -#include "DataFormatsHMP/Digit.h" -#include "HMPIDBase/Geo.h" -#include "HMPIDReconstruction/HmpidDecodeRawMem.h" - -using namespace o2::hmpid; - -/// Constructor : accepts the number of equipments to define -/// The mapping is the default at P2 -/// Allocates instances for all defined equipments -/// normally it is equal to 14 -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -HmpidDecodeRawMem::HmpidDecodeRawMem(int numOfEquipments) - : HmpidDecoder(numOfEquipments) -{ -} - -/// Constructor : accepts the number of equipments to define -/// and their complete address map -/// Allocates instances for all defined equipments -/// -/// The Address map is build from three array -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -/// @param[in] *EqIds : the pointer to the Equipments ID array -/// @param[in] *CruIds : the pointer to the CRU ID array -/// @param[in] *LinkIds : the pointer to the Link ID array -HmpidDecodeRawMem::HmpidDecodeRawMem(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments) - : HmpidDecoder(EqIds, CruIds, LinkIds, numOfEquipments) -{ -} - -/// Destructor -HmpidDecodeRawMem::~HmpidDecodeRawMem() = default; - -/// Setup the Input Stream with a Memory Pointer -/// the buffer length is in byte, some controls are done -/// -/// @param[in] *Buffer : the pointer to Memory buffer -/// @param[in] BufferLen : the length of the buffer (bytes) -/// @returns True if the stream is set -/// @throws TH_NULLBUFFERPOINTER Thrown if the pointer to the buffer is NULL -/// @throws TH_BUFFEREMPTY Thrown if the buffer is empty -/// @throws TH_WRONGBUFFERDIM Thrown if the buffer len is less then one header -bool HmpidDecodeRawMem::setUpStream(void* Buffer, long BufferLen) -{ - long wordsBufferLen = BufferLen / (sizeof(int32_t) / sizeof(char)); // Converts the len in words - if (Buffer == nullptr) { - LOG(error) << "Raw data buffer null Pointer ! "; - throw TH_NULLBUFFERPOINTER; - } - if (wordsBufferLen == 0) { - LOG(error) << "Raw data buffer Empty ! "; - throw TH_BUFFEREMPTY; - } - if (wordsBufferLen < 16) { - LOG(error) << "Raw data buffer less then the Header Dimension = " << wordsBufferLen; - throw TH_WRONGBUFFERDIM; - } - - mActualStreamPtr = (uint32_t*)Buffer; // sets the pointer to the Buffer - mEndStreamPtr = ((uint32_t*)Buffer) + wordsBufferLen; //sets the End of buffer - mStartStreamPtr = ((uint32_t*)Buffer); - // std::cout << " setUpStrem : StPtr=" << mStartStreamPtr << " EndPtr=" << mEndStreamPtr << " Len=" << wordsBufferLen << std::endl; - return (true); -} - -/// Gets a sized chunk from the stream. The stream pointers members are updated -/// @param[in] **streamPtr : the pointer to the memory buffer -/// @param[in] Size : the dimension of the chunk (words) -/// @returns True every time -/// @throw TH_WRONGBUFFERDIM Buffer length shorter then the requested -bool HmpidDecodeRawMem::getBlockFromStream(uint32_t** streamPtr, uint32_t Size) -{ - *streamPtr = mActualStreamPtr; - mActualStreamPtr += Size; - if (mActualStreamPtr > mEndStreamPtr) { - // std::cout << " getBlockFromStream : StPtr=" << mActualStreamPtr << " EndPtr=" << mEndStreamPtr << " Len=" << Size << std::endl; - // std::cout << "Beccato " << std::endl; - // throw TH_WRONGBUFFERDIM; - return (false); - } - return (true); -} - -/// Gets the Header Block from the stream. -/// @param[in] **streamPtr : the pointer to the memory buffer -/// @returns True if the header is read -bool HmpidDecodeRawMem::getHeaderFromStream(uint32_t** streamPtr) -{ - return (getBlockFromStream(streamPtr, mRDHSize)); -} - -/// Gets a Word from the stream. -/// @param[in] *word : the buffer for the read word -/// @returns True if the operation end well -bool HmpidDecodeRawMem::getWordFromStream(uint32_t* word) -{ - uint32_t* appo; - *word = *mActualStreamPtr; - return (getBlockFromStream(&appo, 1)); -} - -/// ----- Sets the Pad ! ------ -/// this is an overloaded method. In this version the value of the charge -/// is used to update the statistical matrix of the base class -/// -/// @param[in] *eq : the pointer to the Equipment object -/// @param[in] col : the column [0..23] -/// @param[in] dil : the dilogic [0..9] -/// @param[in] ch : the channel [0..47] -/// @param[in] charge : the value of the charge -void HmpidDecodeRawMem::setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge) -{ - eq->setPad(col, dil, ch, charge); - return; -} - -// ======================================================================================== - -/// Constructor : accepts the number of equipments to define -/// The mapping is the default at P2 -/// Allocates instances for all defined equipments -/// normally it is equal to 14 -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -HmpidDecodeRawDigit::HmpidDecodeRawDigit(int numOfEquipments) - : HmpidDecodeRawMem(numOfEquipments) -{ -} - -/// Constructor : accepts the number of equipments to define -/// and their complete address map -/// Allocates instances for all defined equipments -/// -/// The Address map is build from three array -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -/// @param[in] *EqIds : the pointer to the Equipments ID array -/// @param[in] *CruIds : the pointer to the CRU ID array -/// @param[in] *LinkIds : the pointer to the Link ID array -HmpidDecodeRawDigit::HmpidDecodeRawDigit(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments) - : HmpidDecodeRawMem(EqIds, CruIds, LinkIds, numOfEquipments) -{ -} - -/// Destructor -HmpidDecodeRawDigit::~HmpidDecodeRawDigit() = default; - -/// ----- Sets the Pad ! ------ -/// this is an overloaded method. In this version the value of the charge -/// is used to update the statistical matrix of the base class -/// -/// @param[in] *eq : the pointer to the Equipment object -/// @param[in] col : the column [0..23] -/// @param[in] dil : the dilogic [0..9] -/// @param[in] ch : the channel [0..47] -/// @param[in] charge : the value of the charge -void HmpidDecodeRawDigit::setPad(HmpidEquipment* eq, int col, int dil, int ch, uint16_t charge) -{ - eq->setPad(col, dil, ch, charge); - mDigits.push_back(o2::hmpid::Digit(charge, eq->getEquipmentId(), col, dil, ch)); - //std::cout << "DI " << mDigits.back() << " "< // for LOG -#include "Framework/Logger.h" -#include "Headers/RAWDataHeader.h" -#include "HMPIDReconstruction/HmpidDecoder.h" -#include "DataFormatsHMP/Digit.h" - -using namespace o2::hmpid; - -// ============= HmpidDecoder Class implementation ======= - -/// Decoding Error Messages Definitions -char HmpidDecoder::sErrorDescription[MAXERRORS][MAXDESCRIPTIONLENGHT] = {"Word that I don't known !", - "Row Marker Word with 0 words", "Duplicated Pad Word !", "Row Marker Wrong/Lost -> to EoE", - "Row Marker Wrong/Lost -> to EoE", "Row Marker reports an ERROR !", "Lost EoE Marker !", "Double EoE marker", - "Wrong size definition in EoE Marker", "Double Mark Word", "Wrong Size in Segment Marker", "Lost EoS Marker !", - "HMPID Header Errors"}; - -/// HMPID Firmware Error Messages Definitions -char HmpidDecoder::sHmpidErrorDescription[MAXHMPIDERRORS][MAXDESCRIPTIONLENGHT] = { - "L0 Missing," - "L1 is received without L0", - "L1A signal arrived before the L1 Latency", "L1A signal arrived after the L1 Latency", - "L1A is missing or L1 timeout", "L1A Message is missing or L1 Message"}; - -/// Constructor : accepts the number of equipments to define -/// The mapping is the default at P2 -/// Allocates instances for all defined equipments -/// normally it is equal to 14 -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -HmpidDecoder::HmpidDecoder(int numOfEquipments) -{ - // The standard definition of HMPID equipments at P2 - int EqIds[] = {0, 1, 2, 3, 4, 5, 8, 9, 6, 7, 10, 11, 12, 13}; - int CruIds[] = {0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 3, 3, 3}; - int LinkIds[] = {0, 1, 2, 3, 0, 1, 2, 3, 0, 1, 2, 0, 1, 2}; - - mNumberOfEquipments = numOfEquipments; - for (int i = 0; i < mNumberOfEquipments; i++) { - mTheEquipments[i] = new HmpidEquipment(EqIds[i], CruIds[i], LinkIds[i]); - } -} - -/// Constructor : accepts the number of equipments to define -/// and their complete address map -/// Allocates instances for all defined equipments -/// -/// The Address map is build from three array -/// @param[in] numOfEquipments : the number of equipments to define [1..14] -/// @param[in] *EqIds : the pointer to the Equipments ID array -/// @param[in] *CruIds : the pointer to the CRU ID array -/// @param[in] *LinkIds : the pointer to the Link ID array -HmpidDecoder::HmpidDecoder(int* EqIds, int* CruIds, int* LinkIds, int numOfEquipments) -{ - mNumberOfEquipments = numOfEquipments; - for (int i = 0; i < mNumberOfEquipments; i++) { - mTheEquipments[i] = new HmpidEquipment(EqIds[i], CruIds[i], LinkIds[i]); - } -} - -/// Destructor : remove the Equipments instances -HmpidDecoder::~HmpidDecoder() -{ - for (int i = 0; i < mNumberOfEquipments; i++) { - delete mTheEquipments[i]; - } -} - -/// Init all the members variables. -void HmpidDecoder::init() -{ - mRDHSize = sizeof(o2::header::RAWDataHeader) / sizeof(uint32_t); - - mVerbose = 0; - mHeEvent = 0; - mHeBusy = 0; - mNumberWordToRead = 0; - mPayloadTail = 0; - - mHeFEEID = 0; - mHeSize = 0; - mHeVer = 0; - mHePrior = 0; - mHeStop = 0; - mHePages = 0; - mEquipment = 0; - - mHeOffsetNewPack = 0; - mHeMemorySize = 0; - - mHeDetectorID = 0; - mHeDW = 0; - mHeCruID = 0; - mHePackNum = 0; - mHePAR = 0; - mHePageNum = 0; - mHeLinkNum = 0; - mHeFirmwareVersion = 0; - mHeHmpidError = 0; - mHeBCDI = 0; - mHeORBIT = 0; - mHeTType = 0; - - mActualStreamPtr = nullptr; - mEndStreamPtr = nullptr; - mStartStreamPtr = nullptr; - - for (int i = 0; i < mNumberOfEquipments; i++) { - mTheEquipments[i]->init(); - } -} - -/// Returns the Equipment Index (Pointer of the array) converting -/// the FLP hardware coords (CRU_Id and Link_Id) -/// @param[in] CruId : the CRU ID [0..3] -> FLP 160 = [0,1] FLP 161 = [2,3] -/// @param[in] LinkId : the Link ID [0..3] -/// @returns EquipmentIndex : the index in the Equipment array [0..13] (-1 := error) -int HmpidDecoder::getEquipmentIndex(int CruId, int LinkId) -{ - for (int i = 0; i < mNumberOfEquipments; i++) { - if (mTheEquipments[i]->getEquipmentId(CruId, LinkId) != -1) { - return (i); - } - } - return (-1); -} - -/// Returns the Equipment Index (Pointer of the array) converting -/// the Equipment_ID (Firmaware defined Id AKA FFEID) -/// @param[in] EquipmentId : the Equipment ID [0..13] -/// @returns EquipmentIndex : the index in the Equipment array [0..13] (-1 := error) -int HmpidDecoder::getEquipmentIndex(int EquipmentId) -{ - for (int i = 0; i < mNumberOfEquipments; i++) { - if (mTheEquipments[i]->getEquipmentId() == EquipmentId) { - return (i); - } - } - return (-1); -} - -/// Returns the Equipment_ID converting the FLP hardware coords -/// @param[in] CruId : the CRU ID [0..3] -> FLP 160 = [0,1] FLP 161 = [2,3] -/// @param[in] LinkId : the Link ID [0..3] -/// @returns EquipmentID : the ID of the Equipment [0..13] (-1 := error) -int HmpidDecoder::getEquipmentID(int CruId, int LinkId) -{ - for (int i = 0; i < mNumberOfEquipments; i++) { - if (mTheEquipments[i]->getEquipmentId(CruId, LinkId) != -1) { - return (mTheEquipments[i]->getEquipmentId()); - } - } - return (-1); -} - -/// Scans the BitMap of Raw Data File word and detect the type -/// and the parameters -/// @param[in] wp : the word to analyze -/// @param[out] *p1 : first parameter extract (if it exists) -/// @param[out] *p2 : second parameter extract (if it exists) -/// @param[out] *p3 : third parameter extract (if it exists) -/// @param[out] *p4 : fourth parameter extract (if it exists) -/// @returns Type of Word : the type of word [0..4] (0 := undetect) -int HmpidDecoder::checkType(uint32_t wp, int* p1, int* p2, int* p3, int* p4) -{ - if ((wp & 0x0000ffff) == 0x000036A8 || (wp & 0x0000ffff) == 0x000032A8 || (wp & 0x0000ffff) == 0x000030A0 || (wp & 0x0800ffff) == 0x080010A0) { - *p2 = (wp & 0x03ff0000) >> 16; // Number of words of row - *p1 = wp & 0x0000ffff; - return (WTYPE_ROW); - } - if ((wp & 0xfff00000) >> 20 == 0xAB0) { - *p2 = (wp & 0x000fff00) >> 8; // Number of words of Segment - *p1 = (wp & 0xfff00000) >> 20; - *p3 = wp & 0x0000000F; - if (*p3 < 4 && *p3 > 0) { - return (WTYPE_EOS); - } - } - // #EX MASK Raul 0x3803FF80 # ex mask 0xF803FF80 - this is EoE marker 0586800B0 - if ((wp & 0x0803FF80) == 0x08000080) { - *p1 = (wp & 0x07c00000) >> 22; - *p2 = (wp & 0x003C0000) >> 18; - *p3 = (wp & 0x0000007F); - if (*p1 < 25 && *p2 < 11) { - return (WTYPE_EOE); - } - } - if ((wp & 0x08000000) == 0) { // # this is a pad - // PAD:0000.0ccc.ccdd.ddnn.nnnn.vvvv.vvvv.vvvv :: c=col,d=dilo,n=chan,v=value - *p1 = (wp & 0x07c00000) >> 22; - *p2 = (wp & 0x003C0000) >> 18; - *p3 = (wp & 0x0003F000) >> 12; - *p4 = (wp & 0x00000FFF); - if (*p1 > 0 && *p1 < 25 && *p2 > 0 && *p2 < 11 && *p3 < 48) { - return (WTYPE_PAD); - } - } else { - return (WTYPE_NONE); - } - return (WTYPE_NONE); -} - -/// Checks if is a Raw Marker and extract the Row Size -/// @param[in] wp : the word to check -/// @param[out] *Err : true if an error is detected -/// @param[out] *rowSize : the number of words of the row -/// @param[out] *mark : the row marker -/// @returns True if Row Marker is detected -bool HmpidDecoder::isRowMarker(uint32_t wp, int* Err, int* rowSize, int* mark) -{ - if ((wp & 0x0000ffff) == 0x36A8 || (wp & 0x0000ffff) == 0x32A8 || (wp & 0x0000ffff) == 0x30A0 || (wp & 0x0800ffff) == 0x080010A0) { - *rowSize = (wp & 0x03ff0000) >> 16; // # Number of words of row - *mark = wp & 0x0000ffff; - *Err = false; - return (true); - } else { - *Err = true; - return (false); - } -} - -/// Checks if is a Segment Marker and extracts the Segment number and the size -/// @param[in] wp : the word to check -/// @param[out] *Err : true if an error is detected -/// @param[out] *segSize : the number of words of the segment -/// @param[out] *Seg : the Segment number [1..3] -/// @param[out] *mark : the Segment Marker -/// @returns True if Segment Marker is detected -bool HmpidDecoder::isSegmentMarker(uint32_t wp, int* Err, int* segSize, int* Seg, int* mark) -{ - *Err = false; - if ((wp & 0xfff00000) >> 20 == 0xAB0) { - *segSize = (wp & 0x000fff00) >> 8; // # Number of words of Segment - *mark = (wp & 0xfff00000) >> 20; - *Seg = wp & 0x0000000F; - if (*Seg > 3 || *Seg < 1) { - LOG(info) << " Wrong segment Marker Word, bad Number of segment" << *Seg << "!"; - *Err = true; - } - return (true); - } else { - return (false); - } -} - -/// Checks if is a PAD Word and extracts all the parameters -/// PAD map : 0000.0ccc.ccdd.ddnn.nnnn.vvvv.vvvv.vvvv :: c=col,d=dilo,n=chan,v=value -/// @param[in] wp : the word to check -/// @param[out] *Err : true if an error is detected -/// @param[out] *Col : the column number [1..24] -/// @param[out] *Dilogic : the dilogic number [1..10] -/// @param[out] *Channel : the channel number [0..47] -/// @param[out] *Charge : the value of Charge [0..4095] -/// @returns True if PAD Word is detected -bool HmpidDecoder::isPadWord(uint32_t wp, int* Err, int* Col, int* Dilogic, int* Channel, int* Charge) -{ - *Err = false; - // if ((wp & 0x08000000) != 0) { - if ((wp & 0x08000000) != 0) { - return (false); - } - *Col = (wp & 0x07c00000) >> 22; - *Dilogic = (wp & 0x003C0000) >> 18; - *Channel = (wp & 0x0003F000) >> 12; - *Charge = (wp & 0x00000FFF); - - if ((wp & 0x0ffff) == 0x036A8 || (wp & 0x0ffff) == 0x032A8 || (wp & 0x0ffff) == 0x030A0 || (wp & 0x0ffff) == 0x010A0) { // # ! this is a pad - if (*Dilogic > 10 || *Channel > 47 || *Dilogic < 1 || *Col > 24 || *Col < 1) { - return (false); - } - } else { - if (*Dilogic > 10 || *Channel > 47 || *Dilogic < 1 || *Col > 24 || *Col < 1) { - // LOG(warning) << " Wrong Pad values Col=" << *Col << " Dilogic=" << *Dilogic << " Channel=" << *Channel << " Charge=" << *Charge << " wp:0x" << std::hex << wp << std::dec; - *Err = true; - return (false); - } - } - return (true); -} - -/// Checks if is a EoE Marker and extracts the Column, Dilogic and the size -/// @param[in] wp : the word to check -/// @param[out] *Err : true if an error is detected -/// @param[out] *Col : the column number [1..24] -/// @param[out] *Dilogic : the dilogic number [1..10] -/// @param[out] *Eoesize : the number of words for dilogic -/// @returns True if EoE marker is detected -bool HmpidDecoder::isEoEmarker(uint32_t wp, int* Err, int* Col, int* Dilogic, int* Eoesize) -{ - *Err = false; - // #EX MASK Raul 0x3803FF80 # ex mask 0xF803FF80 - this is EoE marker 0586800B0 - if ((wp & 0x0803FF80) == 0x08000080) { - *Col = (wp & 0x07c00000) >> 22; - *Dilogic = (wp & 0x003C0000) >> 18; - *Eoesize = (wp & 0x0000007F); - if (*Col > 24 || *Dilogic > 10) { - LOG(info) << " EoE size wrong definition. Col=" << *Col << " Dilogic=" << *Dilogic; - *Err = true; - } - return (true); - } else { - return (false); - } -} - -/// Decode the HMPID error BitMap field (5 bits) and returns true if there are -/// errors and in addition the concat string that contains the error messages -/// ATTENTION : the char * outbuf MUST point to a 250 bytes buffer -/// @param[in] ErrorField : the HMPID Error field -/// @param[out] *outbuf : the output buffer that contains the error description -/// @returns True if EoE marker is detected -bool HmpidDecoder::decodeHmpidError(int ErrorField, char* outbuf) -{ - int res = false; - outbuf[0] = '\0'; - for (int i = 0; i < MAXHMPIDERRORS; i++) { - if ((ErrorField & (0x01 << i)) != 0) { - res = true; - strcat(outbuf, sHmpidErrorDescription[i]); - } - } - return (res); -} - -/// This Decode the Raw Data Header, returns the EquipmentIndex -/// that is obtained with the FLP hardware coords -/// -/// ATTENTION : the 'EquipIndex' parameter and the mEquipment member -/// are different data: the first is the pointer in the Equipments instances -/// array, the second is the FEE_ID number -/// -/// The EVENT_NUMBER : actually is calculated from the ORBIT number -/// -/// @param[in] *streamPtrAdr : the pointer to the Header buffer -/// @param[out] *EquipIndex : the Index to the Equipment Object Array [0..13] -/// @returns True every time -/// @throws TH_WRONGEQUIPINDEX Thrown if the Equipment Index is out of boundary (Equipment not recognized) -int HmpidDecoder::decodeHeader(uint32_t* streamPtrAdr, int* EquipIndex) -{ - uint32_t* buffer = streamPtrAdr; // Sets the pointer to buffer - o2::header::RAWDataHeader* hpt = (o2::header::RAWDataHeader*)buffer; - - /* - mHeFEEID = (buffer[0] & 0x000f0000) >> 16; - mHeSize = (buffer[0] & 0x0000ff00) >> 8; - mHeVer = (buffer[0] & 0x000000ff); - mHePrior = (buffer[1] & 0x000000FF); - mHeDetectorID = (buffer[1] & 0x0000FF00) >> 8; - mHeOffsetNewPack = (buffer[2] & 0x0000FFFF); - mHeMemorySize = (buffer[2] & 0xffff0000) >> 16; - mHeDW = (buffer[3] & 0xF0000000) >> 24; - mHeCruID = (buffer[3] & 0x0FF0000) >> 16; - mHePackNum = (buffer[3] & 0x0000FF00) >> 8; - mHeLinkNum = (buffer[3] & 0x000000FF); - mHeBCDI = (buffer[4] & 0x00000FFF); - mHeORBIT = buffer[5]; - mHeTType = buffer[8]; - mHePageNum = (buffer[9] & 0x0000FFFF); - mHeStop = (buffer[9] & 0x00ff0000) >> 16; - mHeBusy = (buffer[12] & 0xfffffe00) >> 9; - mHeFirmwareVersion = buffer[12] & 0x0000000f; - mHeHmpidError = (buffer[12] & 0x000001F0) >> 4; - mHePAR = buffer[13] & 0x0000FFFF; - */ - mHeFEEID = hpt->feeId; - mHeSize = hpt->headerSize; - mHeVer = hpt->version; - mHePrior = hpt->priority; - mHeDetectorID = hpt->sourceID; - mHeOffsetNewPack = hpt->offsetToNext; - mHeMemorySize = hpt->memorySize; - mHeDW = hpt->endPointID; - mHeCruID = hpt->cruID; - mHePackNum = hpt->packetCounter; - mHeLinkNum = hpt->linkID; - mHeBCDI = hpt->bunchCrossing; - mHeORBIT = hpt->orbit; - mHeTType = hpt->triggerType; - mHePageNum = hpt->pageCnt; - mHeStop = hpt->stop; - mHeBusy = (hpt->detectorField & 0xfffffe00) >> 9; - mHeFirmwareVersion = hpt->detectorField & 0x0000000f; - mHeHmpidError = (hpt->detectorField & 0x000001F0) >> 4; - mHePAR = hpt->detectorPAR; - - *EquipIndex = getEquipmentIndex(mHeCruID, mHeLinkNum); - // mEquipment = (*EquipIndex != -1) ? mTheEquipments[*EquipIndex]->getEquipmentId() : -1; - mEquipment = mHeFEEID & 0x000F; - mNumberWordToRead = ((mHeMemorySize - mHeSize) / sizeof(uint32_t)); - mPayloadTail = ((mHeOffsetNewPack - mHeMemorySize) / sizeof(uint32_t)); - - // ---- Event ID : Actualy based on ORBIT NUMBER and BC - mHeEvent = (mHeORBIT << 12) | mHeBCDI; - - LOG(debug) << "FEE-ID=" << mHeFEEID << " HeSize=" << mHeSize << " HePrior=" << mHePrior << " Det.Id=" << mHeDetectorID << " HeMemorySize=" << mHeMemorySize << " HeOffsetNewPack=" << mHeOffsetNewPack; - LOG(debug) << " Equipment=" << mEquipment << " PakCounter=" << mHePackNum << " Link=" << mHeLinkNum << " CruID=" << mHeCruID << " DW=" << mHeDW << " BC=" << mHeBCDI << " ORBIT=" << mHeORBIT; - LOG(debug) << " TType=" << mHeTType << " HeStop=" << mHeStop << " PagesCounter=" << mHePageNum << " FirmVersion=" << mHeFirmwareVersion << " BusyTime=" << mHeBusy << " Error=" << mHeHmpidError << " PAR=" << mHePAR; - LOG(debug) << " EquIdx = " << *EquipIndex << " Event = " << mHeEvent << " Payload : Words to read=" << mNumberWordToRead << " PailoadTail=" << mPayloadTail; - - if (*EquipIndex == -1) { - LOG(error) << "ERROR ! Bad equipment Number: " << mEquipment; - throw TH_WRONGEQUIPINDEX; - } - // std::cout << "HMPID ! Exit decode header" << std::endl; - return (true); -} - -/// Updates some information related to the Event -/// this function is called at the end of the event -/// @param[in] *eq : the pointer to the Equipment Object -void HmpidDecoder::updateStatistics(HmpidEquipment* eq) -{ - eq->mPadsPerEventAverage = ((eq->mPadsPerEventAverage * (eq->mNumberOfEvents - 1)) + eq->mSampleNumber) / (eq->mNumberOfEvents); - eq->mEventSizeAverage = ((eq->mEventSizeAverage * (eq->mNumberOfEvents - 1)) + eq->mEventSize) / (eq->mNumberOfEvents); - eq->mBusyTimeAverage = ((eq->mBusyTimeAverage * eq->mBusyTimeSamples) + eq->mBusyTimeValue) / (++(eq->mBusyTimeSamples)); - if (eq->mSampleNumber == 0) { - eq->mNumberOfEmptyEvents += 1; - } - if (eq->mErrorsCounter > 0) { - eq->mNumberOfWrongEvents += 1; - } - eq->mTotalPads += eq->mSampleNumber; - eq->mTotalErrors += eq->mErrorsCounter; - - //std::cout << ">>>updateStatistics() >>> "<< eq->getEquipmentId() << "="<< eq->mNumberOfEvents<<" :" << eq->mEventSize <<","<< eq->mTotalPads << ", " << eq->mSampleNumber << std::endl; - - return; -} - -/// Evaluates the content of the header and detect the change of the event -/// with the relevant updates... -/// @param[in] EquipmentIndex : the pointer to the Array of Equipments Array -/// @returns the Pointer to the modified Equipment object -HmpidEquipment* HmpidDecoder::evaluateHeaderContents(int EquipmentIndex) -{ - //std::cout << "Enter evaluateHeaderContents.."; - HmpidEquipment* eq = mTheEquipments[EquipmentIndex]; - if (mHeEvent != eq->mEventNumber) { // Is a new event - if (eq->mEventNumber != OUTRANGEEVENTNUMBER) { // skip the first - updateStatistics(eq); // update previous statistics - } - eq->mNumberOfEvents++; - eq->mEventNumber = mHeEvent; - eq->mBusyTimeValue = mHeBusy * 0.00000005; - eq->mEventSize = 0; // reset the event - eq->mSampleNumber = 0; - eq->mErrorsCounter = 0; - mIntReco = {(uint16_t)mHeBCDI, (uint32_t)mHeORBIT}; - } - eq->mEventSize += mNumberWordToRead * sizeof(uint32_t); // Calculate the size in bytes - if (mHeHmpidError != 0) { - LOG(info) << "HMPID Header reports an error : " << mHeHmpidError; - dumpHmpidError(mHeHmpidError); - eq->setError(ERR_HMPID); - } - // std::cout << ".. end evaluateHeaderContents = " << eq->mEventNumber << std::endl; - return (eq); -} - -/// --------------- Decode One Page from Data Buffer --------------- -/// Read the stream, decode the contents and store resuls. -/// ATTENTION : Assumes that the input stream was set -/// @throws TH_WRONGHEADER Thrown if the Fails to decode the Header -/// @param[in] streamBuf : the pointer to the Pointer of the Stream Buffer -void HmpidDecoder::decodePage(uint32_t** streamBuf) -{ - int equipmentIndex; - try { - getHeaderFromStream(streamBuf); - } catch (int e) { - // The stream end ! - LOG(debug) << "End main decoding loop !"; - throw TH_BUFFEREMPTY; - } - try { - decodeHeader(*streamBuf, &equipmentIndex); - } catch (int e) { - LOG(error) << "Failed to decode the Header !"; - throw TH_WRONGHEADER; - } - - HmpidEquipment* eq = evaluateHeaderContents(equipmentIndex); - - uint32_t wpprev = 0; - uint32_t wp = 0; - int newOne = true; - int p1, p2, p3, p4; - int error; - int type; - bool isIt; - - int payIndex = 0; - while (payIndex < mNumberWordToRead) { //start the payload loop word by word - if (newOne == true) { - wpprev = wp; - if (!getWordFromStream(&wp)) { // end the stream - break; - } - type = checkType(wp, &p1, &p2, &p3, &p4); - if (type == WTYPE_NONE) { - if (eq->mWillBePad == true) { // try to recover the first pad ! - type = checkType((wp & 0xF7FFFFFF), &p1, &p2, &p3, &p4); - if (type == WTYPE_PAD && p3 == 0 && eq->mWordsPerDilogicCounter == 0) { - newOne = false; // # reprocess as pad - continue; - } - } - eq->setError(ERR_NOTKNOWN); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_NOTKNOWN] << " [" << wp << "]"; - eq->mWordsPerRowCounter++; - eq->mWordsPerSegCounter++; - payIndex++; - continue; - } - } - if (mEquipment == 8) { - LOG(info) << "Event" << eq->mEventNumber << " >" << std::hex << wp << std::dec << "<" << type; - } - if (eq->mWillBeRowMarker == true) { // #shoud be a Row Marker - if (type == WTYPE_ROW) { - eq->mColumnCounter++; - eq->mWordsPerSegCounter++; - eq->mRowSize = p2; - switch (p2) { - case 0: // Empty column - eq->setError(ERR_ROWMARKEMPTY); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_ROWMARKEMPTY] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = true; - break; - case 0x3FF: // Error in column - eq->setError(ERR_ROWMARKERROR); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_ROWMARKERROR] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = true; - break; - case 0x3FE: // Masked column - LOG(info) << "Equip=" << mEquipment << "The column=" << (eq->mSegment) * 8 + eq->mColumnCounter << " is Masked !"; - eq->mWillBeRowMarker = true; - break; - default: - eq->mWillBeRowMarker = false; - eq->mWillBePad = true; - break; - } - newOne = true; - } else { - if (wp == wpprev) { - eq->setError(ERR_DUPLICATEPAD); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_DUPLICATEPAD] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - newOne = true; - } else if (type == WTYPE_EOE) { // # Could be a EoE - eq->mColumnCounter++; - eq->setError(ERR_ROWMARKWRONG); - eq->mWillBeRowMarker = false; - eq->mWillBePad = true; - newOne = true; - } else if (type == WTYPE_PAD) { //# Could be a PAD - eq->mColumnCounter++; - eq->setError(ERR_ROWMARKLOST); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_ROWMARKLOST] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = false; - eq->mWillBePad = true; - newOne = true; - } else if (type == WTYPE_EOS) { // # Could be a EoS - eq->mWillBeRowMarker = false; - eq->mWillBeSegmentMarker = true; - newOne = false; - } else { - eq->mColumnCounter++; - eq->setError(ERR_ROWMARKLOST); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_ROWMARKLOST] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = false; - eq->mWillBePad = true; - newOne = true; - } - } - } else if (eq->mWillBePad == true) { // # We expect a pad - //# PAD:0000.0ccc.ccdd.ddnn.nnnn.vvvv.vvvv.vvvv :: c=col,d=dilo,n=chan,v=value - // c = 1..24 d = 1..10 n = 0..47 - if (type == WTYPE_PAD) { - newOne = true; - if (wp == wpprev) { - eq->setError(ERR_DUPLICATEPAD); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_DUPLICATEPAD] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - } else if (p1 != (eq->mSegment * 8 + eq->mColumnCounter)) { // # Manage - // We try to recover the RowMarker misunderstanding - isIt = isRowMarker(wp, &error, &p2, &p1); - if (isIt == true && error == false) { - type = WTYPE_ROW; - newOne = false; - eq->mWillBeEoE = true; - eq->mWillBePad = false; - } else { - LOG(debug) << "Equip=" << mEquipment << " Mismatch in column" - << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mColumnCounter = p1 % 8; - } - } else { - setPad(eq, p1 - 1, p2 - 1, p3, p4); - if (mEquipment == 8) { - LOG(info) << "Event" << eq->mEventNumber << " >" << p1 - 1 << "," << p2 - 1 << "," << p3 << "," << p4; - } - eq->mWordsPerDilogicCounter++; - eq->mSampleNumber++; - if (p3 == 47) { - eq->mWillBeEoE = true; - eq->mWillBePad = false; - } - } - eq->mWordsPerRowCounter++; - eq->mWordsPerSegCounter++; - } else if (type == WTYPE_EOE) { //# the pads are end ok - eq->mWillBeEoE = true; - eq->mWillBePad = false; - newOne = false; - } else if (type == WTYPE_ROW) { // # We Lost the EoE ! - // We try to recover the PAD misunderstanding - isIt = isPadWord(wp, &error, &p1, &p2, &p3, &p4); - if (isIt == true && error == false) { - type = WTYPE_PAD; - newOne = false; // # reprocess as pad - } else { - eq->setError(ERR_LOSTEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOEMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = true; - eq->mWillBePad = false; - newOne = false; - } - } else if (type == WTYPE_EOS) { // # We Lost the EoE ! - eq->setError(ERR_LOSTEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOEMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeSegmentMarker = true; - eq->mWillBePad = false; - newOne = false; - } - } else if (eq->mWillBeEoE == true) { // # We expect a EoE - if (type == WTYPE_EOE) { - eq->mWordsPerRowCounter++; - eq->mWordsPerSegCounter++; - if (wpprev == wp) { - eq->setError(ERR_DOUBLEEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_DOUBLEEOEMARK] << " col=" << p1; - } else if (p3 != eq->mWordsPerDilogicCounter) { - eq->setError(ERR_WRONGSIZEINEOE); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_WRONGSIZEINEOE] << " col=" << p1; - } - eq->mWordsPerDilogicCounter = 0; - if (p2 == 10) { - if (p1 % 8 != 0) { // # we expect the Row Marker - eq->mWillBeRowMarker = true; - } else { - eq->mWillBeSegmentMarker = true; - } - } else { - eq->mWillBePad = true; - } - eq->mWillBeEoE = false; - newOne = true; - } else if (type == WTYPE_EOS) { // We Lost the EoE ! - eq->setError(ERR_LOSTEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOEMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeSegmentMarker = true; - eq->mWillBeEoE = false; - newOne = false; - } else if (type == WTYPE_ROW) { //# We Lost the EoE ! - eq->setError(ERR_LOSTEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOEMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeRowMarker = true; - eq->mWillBeEoE = false; - newOne = false; - } else if (type == WTYPE_PAD) { // # We Lost the EoE ! - int typb, p1b, p2b, p3b, p4b; - typb = checkType((wp | 0x08000000), &p1b, &p2b, &p3b, &p4b); - if (typb == WTYPE_EOE && p3b == 48) { - type = typb; - p1 = p1b; - p2 = p2b; - p3 = p3b; - p4 = p4b; - newOne = false; // # reprocess as EoE - } else { - eq->setError(ERR_LOSTEOEMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOEMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBePad = true; - eq->mWillBeEoE = false; - newOne = false; - } - } - } else if (eq->mWillBeSegmentMarker == true) { // # We expect a EoSegment - if (wpprev == wp) { - eq->setError(ERR_DOUBLEMARKWORD); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_DOUBLEMARKWORD] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - newOne = true; - } else if (type == 2) { - if (abs(eq->mWordsPerSegCounter - p2) > 5) { - eq->setError(ERR_WRONGSIZESEGMENTMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_WRONGSIZESEGMENTMARK] << " Seg=" << p2; - } - eq->mWordsPerSegCounter = 0; - eq->mWordsPerRowCounter = 0; - eq->mColumnCounter = 0; - eq->mSegment = p3 % 3; - eq->mWillBeRowMarker = true; - eq->mWillBeSegmentMarker = false; - newOne = true; - } else { - eq->setError(ERR_LOSTEOSMARK); - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_LOSTEOSMARK] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << p1 << "]"; - eq->mWillBeSegmentMarker = false; - eq->mWillBeRowMarker = true; - newOne = false; - } - } - if (newOne) { - payIndex += 1; - } - } - for (int i = 0; i < mPayloadTail; i++) { // move the pointer to skip the Payload Tail - getWordFromStream(&wp); - } -} - -/// --------------- Read Raw Data Buffer --------------- -/// Read the stream, decode the contents and store resuls. -/// ATTENTION : Assumes that the input stream was set -/// @throws TH_WRONGHEADER Thrown if the Fails to decode the Header -bool HmpidDecoder::decodeBuffer() -{ - // ---------resets the PAdMap----------- - for (int i = 0; i < mNumberOfEquipments; i++) { - mTheEquipments[i]->init(); - mTheEquipments[i]->resetPadMap(); - mTheEquipments[i]->resetErrors(); - } - - int type; - int equipmentIndex = -1; - int isIt; - HmpidEquipment* eq; - uint32_t* streamBuf; - LOG(debug) << "Enter decoding !"; - - // Input Stream Main Loop - while (true) { - try { - decodePage(&streamBuf); - } catch (int e) { - LOG(debug) << "End main buffer decoding loop !"; - break; - } - } // this is the end of stream - - // cycle in order to update info for the last event - for (int i = 0; i < mNumberOfEquipments; i++) { - if (mTheEquipments[i]->mNumberOfEvents > 0) { - updateStatistics(mTheEquipments[i]); - } - } - return (true); -} - -/// --------- Decode One Page from Data Buffer with Fast Decoding -------- -/// Read the stream, decode the contents and store resuls. -/// ATTENTION : Assumes that the input stream was set -/// @throws TH_WRONGHEADER Thrown if the Fails to decode the Header -/// @param[in] streamBuf : the pointer to the Pointer of the Stream Buffer -void HmpidDecoder::decodePageFast(uint32_t** streamBuf) -{ - int equipmentIndex; - try { - getHeaderFromStream(streamBuf); - } catch (int e) { - // The stream end ! - LOG(info) << "End Fast Page decoding loop !"; - throw TH_BUFFEREMPTY; - } - try { - decodeHeader(*streamBuf, &equipmentIndex); - } catch (int e) { - LOG(info) << "Failed to decode the Header !"; - throw TH_WRONGHEADER; - } - HmpidEquipment* eq = evaluateHeaderContents(equipmentIndex); - uint32_t wpprev = 0; - uint32_t wp = 0; - int newOne = true; - int Column, Dilogic, Channel, Charge; - int pwer; - int payIndex = 0; - while (payIndex < mNumberWordToRead) { //start the payload loop word by word - wpprev = wp; - if (!getWordFromStream(&wp)) { // end the stream - break; - } - if (wp == wpprev) { - LOG(debug) << "Equip=" << mEquipment << sErrorDescription[ERR_DUPLICATEPAD] << " col=" << (eq->mSegment) * 8 + eq->mColumnCounter << "[" << Column << "]"; - } else { - if (isPadWord(wp, &pwer, &Column, &Dilogic, &Channel, &Charge) == true) { - if (pwer != true) { - setPad(eq, Column - 1, Dilogic - 1, Channel, Charge); - eq->mSampleNumber++; - } - } - } - payIndex += 1; - } - for (int i = 0; i < mPayloadTail; i++) { // move the pointer to skip the Payload Tail - getWordFromStream(&wp); - } - return; -} -/// ---------- Read Raw Data Buffer with Fast Decoding ---------- -/// Read the stream, decode the contents and store resuls. -/// Fast alghoritm : no parsing of control words ! -/// ATTENTION : Assumes that the input stream was set -/// @throws TH_WRONGHEADER Thrown if the Fails to decode the Header -bool HmpidDecoder::decodeBufferFast() -{ - // ---------resets the PAdMap----------- - for (int i = 0; i < mNumberOfEquipments; i++) { - mTheEquipments[i]->init(); - mTheEquipments[i]->resetPadMap(); - } - - uint32_t* streamBuf; - LOG(info) << "Enter FAST decoding !"; - - // Input Stream Main Loop - while (true) { - try { - decodePageFast(&streamBuf); - } catch (int e) { - LOG(info) << " End Buffer Fast Decoding !"; - break; - } - } // this is the end of stream - - // cycle in order to update info for the last event - for (int i = 0; i < mNumberOfEquipments; i++) { - if (mTheEquipments[i]->mNumberOfEvents > 0) { - updateStatistics(mTheEquipments[i]); - } - } - return (true); -} - -// ========================================================= - -/// Getter method to extract Statistic Data in Digit Coords -/// @param[in] Module : the HMPID Module number [0..6] -/// @param[in] Column : the HMPID Module Column number [0..143] -/// @param[in] Row : the HMPID Module Row number [0..159] -/// @returns The Number of entries for specified pad -uint16_t HmpidDecoder::getPadSamples(int Module, int Row, int Column) -{ - int e, c, d, h; - o2::hmpid::Digit::absolute2Equipment(Module, Row, Column, &e, &c, &d, &h); - int EqInd = getEquipmentIndex(e); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSamples[c][d][h]); -} - -/// Getter method to extract Statistic Data in Digit Coords -/// @param[in] Module : the HMPID Module number [0..6] -/// @param[in] Column : the HMPID Module Column number [0..143] -/// @param[in] Row : the HMPID Module Row number [0..159] -/// @returns The Sum of Charges for specified pad -double HmpidDecoder::getPadSum(int Module, int Row, int Column) -{ - int e, c, d, h; - o2::hmpid::Digit::absolute2Equipment(Module, Row, Column, &e, &c, &d, &h); - int EqInd = getEquipmentIndex(e); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSum[c][d][h]); -} - -/// Getter method to extract Statistic Data in Digit Coords -/// @param[in] Module : the HMPID Module number [0..6] -/// @param[in] Column : the HMPID Module Column number [0..143] -/// @param[in] Row : the HMPID Module Row number [0..159] -/// @returns The Sum of Square Charges for specified pad -double HmpidDecoder::getPadSquares(int Module, int Row, int Column) -{ - int e, c, d, h; - o2::hmpid::Digit::absolute2Equipment(Module, Row, Column, &e, &c, &d, &h); - int EqInd = getEquipmentIndex(e); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSquares[c][d][h]); -} - -/// Getter method to extract Statistic Data in Hardware Coords -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @param[in] Column : the HMPID Module Column number [0..23] -/// @param[in] Dilogic : the HMPID Module Row number [0..9] -/// @param[in] Channel : the HMPID Module Row number [0..47] -/// @returns The Number of Entries for specified pad -uint16_t HmpidDecoder::getChannelSamples(int EquipmId, int Column, int Dilogic, int Channel) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSamples[Column][Dilogic][Channel]); -} - -/// Getter method to extract Statistic Data in Hardware Coords -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @param[in] Column : the HMPID Module Column number [0..23] -/// @param[in] Dilogic : the HMPID Module Row number [0..9] -/// @param[in] Channel : the HMPID Module Row number [0..47] -/// @returns The Sum of Charges for specified pad -double HmpidDecoder::getChannelSum(int EquipmId, int Column, int Dilogic, int Channel) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSum[Column][Dilogic][Channel]); -} - -/// Getter method to extract Statistic Data in Hardware Coords -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @param[in] Column : the HMPID Module Column number [0..23] -/// @param[in] Dilogic : the HMPID Module Row number [0..9] -/// @param[in] Channel : the HMPID Module Row number [0..47] -/// @returns The Sum of Square Charges for specified pad -double HmpidDecoder::getChannelSquare(int EquipmId, int Column, int Dilogic, int Channel) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return (0); - } - return (mTheEquipments[EqInd]->mPadSquares[Column][Dilogic][Channel]); -} - -/// Gets the Average Event Size value -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @returns The Average Event Size value ( 0 for wrong Equipment Id) -float HmpidDecoder::getAverageEventSize(int EquipmId) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return (0.0); - } - return (mTheEquipments[EqInd]->mEventSizeAverage); -} - -/// Gets the Average Busy Time value -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @returns The Average Busy Time value ( 0 for wrong Equipment Id) -float HmpidDecoder::getAverageBusyTime(int EquipmId) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return (0.0); - } - return (mTheEquipments[EqInd]->mBusyTimeAverage); -} - -// =================================================== -// Methods to dump info - -/// Prints on the standard output the table of decoding -/// errors for one equipment -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -void HmpidDecoder::dumpErrors(int EquipmId) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return; - } - std::cout << "Dump Errors for the Equipment = " << EquipmId << std::endl; - for (int i = 0; i < MAXERRORS; i++) { - std::cout << sErrorDescription[i] << " = " << mTheEquipments[EqInd]->mErrors[i] << std::endl; - } - std::cout << " -------- " << std::endl; - return; -} - -/// Prints on the standard output a Table of statistical -/// decoding information for one equipment -/// @param[in] EquipmId : the HMPID EquipmentId [0..13] -/// @type[in] The type of info. 0 = Entries, 1 = Sum, 2 = Sum of squares -void HmpidDecoder::dumpPads(int EquipmId, int type) -{ - int EqInd = getEquipmentIndex(EquipmId); - if (EqInd < 0) { - return; - } - int Module = EquipmId / 2; - int StartRow = (EquipmId % 2 == 1) ? 80 : 0; - int EndRow = (EquipmId % 2 == 1) ? 160 : 80; - std::cout << "Dump Pads for the Equipment = " << EquipmId << std::endl; - for (int c = 0; c < 144; c++) { - for (int r = StartRow; r < EndRow; r++) { - switch (type) { - case 0: - std::cout << getPadSamples(Module, r, c) << ","; - break; - case 1: - std::cout << getPadSum(Module, r, c) << ","; - break; - case 2: - std::cout << getPadSquares(Module, r, c) << ","; - break; - } - } - std::cout << std::endl; - } - std::cout << " -------- " << std::endl; - return; -} - -/// Prints on the standard output the decoded HMPID error field -/// @param[in] ErrorField : the HMPID readout error field -void HmpidDecoder::dumpHmpidError(int ErrorField) -{ - char printbuf[MAXHMPIDERRORS * MAXDESCRIPTIONLENGHT]; - if (decodeHmpidError(ErrorField, printbuf) == true) { - LOG(error) << "HMPID Error field = " << ErrorField << " : " << printbuf; - } - return; -} - -/// Writes in a ASCCI File the complete report of the decoding -/// procedure -/// @param[in] *summaryFileName : the name of the output file -/// @throws TH_CREATEFILE Thrown if was not able to create the file -void HmpidDecoder::writeSummaryFile(char* summaryFileName) -{ - FILE* fs = fopen(summaryFileName, "w"); - if (fs == nullptr) { - printf("Error opening the file %s !\n", summaryFileName); - throw TH_CREATEFILE; - } - - fprintf(fs, "HMPID Readout Raw Data Decoding Summary File\n"); - fprintf(fs, "Equipment Id\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->getEquipmentId()); - } - fprintf(fs, "\n"); - - fprintf(fs, "Number of events\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mNumberOfEvents); - } - fprintf(fs, "\n"); - - fprintf(fs, "Average Event Size\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%f\t", mTheEquipments[i]->mEventSizeAverage); - } - fprintf(fs, "\n"); - - fprintf(fs, "Total pads\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mTotalPads); - } - fprintf(fs, "\n"); - - fprintf(fs, "Average pads per event\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%f\t", mTheEquipments[i]->mPadsPerEventAverage); - } - fprintf(fs, "\n"); - - fprintf(fs, "Busy Time average\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%e\t", mTheEquipments[i]->mBusyTimeAverage); - } - fprintf(fs, "\n"); - - fprintf(fs, "Event rate\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%e\t", 1 / mTheEquipments[i]->mBusyTimeAverage); - } - fprintf(fs, "\n"); - - fprintf(fs, "Number of Empty Events\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mNumberOfEmptyEvents); - } - fprintf(fs, "\n"); - - fprintf(fs, "-------------Errors--------------------\n"); - fprintf(fs, "Wrong events\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mNumberOfWrongEvents); - } - fprintf(fs, "\n"); - - for (int j = 0; j < MAXERRORS; j++) { - fprintf(fs, "%s\t", sErrorDescription[j]); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mErrors[j]); - } - fprintf(fs, "\n"); - } - - fprintf(fs, "Total errors\t"); - for (int i = 0; i < Geo::MAXEQUIPMENTS; i++) { - fprintf(fs, "%d\t", mTheEquipments[i]->mTotalErrors); - } - fprintf(fs, "\n"); - - fclose(fs); - return; -} diff --git a/Detectors/HMPID/workflow/include/HMPIDWorkflow/ClusterizerSpec.h_notused.h b/Detectors/HMPID/workflow/include/HMPIDWorkflow/ClusterizerSpec.h_notused.h deleted file mode 100644 index 6102ec481c97c..0000000000000 --- a/Detectors/HMPID/workflow/include/HMPIDWorkflow/ClusterizerSpec.h_notused.h +++ /dev/null @@ -1,27 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef STEER_DIGITIZERWORKFLOW_HMPIDCLUSTERIZER_H_ -#define STEER_DIGITIZERWORKFLOW_HMPIDCLUSTERIZER_H_ - -#include "Framework/DataProcessorSpec.h" - -namespace o2 -{ -namespace hmpid -{ - -o2::framework::DataProcessorSpec getHMPIDClusterizerSpec(bool useMC); - -} // end namespace hmpid -} // end namespace o2 - -#endif /* STEER_DIGITIZERWORKFLOW_HMPIDCLUSTERIZERSPEC_H_ */ diff --git a/Detectors/HMPID/workflow/include/HMPIDWorkflow/DigitReaderSpec.h_notused.h b/Detectors/HMPID/workflow/include/HMPIDWorkflow/DigitReaderSpec.h_notused.h deleted file mode 100644 index eea9b134bd911..0000000000000 --- a/Detectors/HMPID/workflow/include/HMPIDWorkflow/DigitReaderSpec.h_notused.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file DigitReader.h - -#ifndef O2_HMPID_DIGITREADER -#define O2_HMPID_DIGITREADER - -#include "TFile.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" -#include "DataFormatsHMP/Digit.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" - -namespace o2 -{ -namespace hmpid -{ - -class DigitReader : public o2::framework::Task -{ - public: - DigitReader(bool useMC) : mUseMC(useMC) {} - ~DigitReader() override = default; - void init(o2::framework::InitContext& ic) final; - void run(o2::framework::ProcessingContext& pc) final; - - private: - int mState = 0; - bool mUseMC = true; - std::unique_ptr mFile = nullptr; - - std::vector mDigits, *mPdigits = &mDigits; - - o2::dataformats::MCTruthContainer mLabels, *mPlabels = &mLabels; -}; - -/// read simulated HMPID digits from a root file -framework::DataProcessorSpec getDigitReaderSpec(bool useMC); - -} // namespace hmpid -} // namespace o2 - -#endif /* O2_HMPID_DIGITREADER */ From cb599998aa3f2425b7556f9b97075633065966a6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Fri, 20 Feb 2026 13:16:53 +0100 Subject: [PATCH 285/701] FIT: Delete unused files (#15031) --- .../include/FDDReconstruction/ReadRaw.h | 68 -------- .../include/FDDSimulation/Digits2Raw.h | 74 --------- .../include/FDDWorkflow/RawDataProcessSpec.h | 61 ------- .../include/FDDWorkflow/RawDataReaderSpec.h | 84 ---------- .../include/FDDWorkflow/RawWorkflow.h | 28 ---- .../FDD/workflow/src/RawDataProcessSpec.cxx | 52 ------ .../FDD/workflow/src/RawDataReaderSpec.cxx | 24 --- .../FIT/FDD/workflow/src/RawWorkflow.cxx | 42 ----- .../FT0Workflow/FT0DataProcessDPLSpec.h | 61 ------- .../FT0Workflow/FT0DataReaderDPLSpec.h | 110 ------------ .../include/FT0Workflow/FT0Workflow.h | 28 ---- .../include/FT0Workflow/RawReaderFT0.h | 156 ------------------ .../workflow/src/FT0DataProcessDPLSpec.cxx | 52 ------ .../FT0/workflow/src/FT0DataReaderDPLSpec.cxx | 24 --- .../FIT/FT0/workflow/src/FT0Workflow.cxx | 45 ----- .../FIT/FT0/workflow/src/RawReaderFT0.cxx | 13 -- 16 files changed, 922 deletions(-) delete mode 100644 Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/ReadRaw.h delete mode 100644 Detectors/FIT/FDD/simulation/include/FDDSimulation/Digits2Raw.h delete mode 100644 Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawDataProcessSpec.h delete mode 100644 Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawDataReaderSpec.h delete mode 100644 Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawWorkflow.h delete mode 100644 Detectors/FIT/FDD/workflow/src/RawDataProcessSpec.cxx delete mode 100644 Detectors/FIT/FDD/workflow/src/RawDataReaderSpec.cxx delete mode 100644 Detectors/FIT/FDD/workflow/src/RawWorkflow.cxx delete mode 100644 Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataProcessDPLSpec.h delete mode 100644 Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataReaderDPLSpec.h delete mode 100644 Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0Workflow.h delete mode 100644 Detectors/FIT/FT0/workflow/include/FT0Workflow/RawReaderFT0.h delete mode 100644 Detectors/FIT/FT0/workflow/src/FT0DataProcessDPLSpec.cxx delete mode 100644 Detectors/FIT/FT0/workflow/src/FT0DataReaderDPLSpec.cxx delete mode 100644 Detectors/FIT/FT0/workflow/src/FT0Workflow.cxx delete mode 100644 Detectors/FIT/FT0/workflow/src/RawReaderFT0.cxx 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/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/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/RawWorkflow.h b/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawWorkflow.h deleted file mode 100644 index 3bbab66d16497..0000000000000 --- a/Detectors/FIT/FDD/workflow/include/FDDWorkflow/RawWorkflow.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef O2_FDD_RAWWORKFLOW_H -#define O2_FDD_RAWWORKFLOW_H - -/// @file RawWorkflow.h - -#include "Framework/WorkflowSpec.h" - -namespace o2 -{ -namespace fdd -{ -framework::WorkflowSpec getFDDRawWorkflow(bool useProcess, - bool dumpProcessor, bool dumpReader, - bool disableRootOut); -} // namespace fdd -} // namespace o2 -#endif diff --git a/Detectors/FIT/FDD/workflow/src/RawDataProcessSpec.cxx b/Detectors/FIT/FDD/workflow/src/RawDataProcessSpec.cxx deleted file mode 100644 index bf18db67672c2..0000000000000 --- a/Detectors/FIT/FDD/workflow/src/RawDataProcessSpec.cxx +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file RawDataProcessSpec.cxx - -#include "FDDWorkflow/RawDataProcessSpec.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace fdd -{ -using namespace std; -void RawDataProcessSpec::init(InitContext& ic) -{ -} - -void RawDataProcessSpec::run(ProcessingContext& pc) -{ - LOG(info) << "RawDataProcessSpec running..."; - auto vecDigits = pc.inputs().get>("digits"); - auto vecChannelData = pc.inputs().get>("digch"); - if (mDumpEventBlocks) { - DigitBlockFDD::print(vecDigits, vecChannelData); - } -} - -DataProcessorSpec getFDDRawDataProcessSpec(bool dumpProcessor) -{ - std::vector inputSpec; - inputSpec.emplace_back("digits", o2::header::gDataOriginFDD, "DIGITSBC", 0, Lifetime::Timeframe); - inputSpec.emplace_back("digch", o2::header::gDataOriginFDD, "DIGITSCH", 0, Lifetime::Timeframe); - LOG(info) << "DataProcessorSpec getRawDataProcessSpec"; - return DataProcessorSpec{ - "fdd-dataprocess-dpl-flp", - inputSpec, - Outputs{}, - AlgorithmSpec{adaptFromTask(dumpProcessor)}, - Options{}}; -} - -} // namespace fdd -} // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/src/RawDataReaderSpec.cxx b/Detectors/FIT/FDD/workflow/src/RawDataReaderSpec.cxx deleted file mode 100644 index 631655d3038ec..0000000000000 --- a/Detectors/FIT/FDD/workflow/src/RawDataReaderSpec.cxx +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file RawDataReaderSpec.cxx - -#include "FDDWorkflow/RawDataReaderSpec.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace fdd -{ - -} // namespace fdd -} // namespace o2 diff --git a/Detectors/FIT/FDD/workflow/src/RawWorkflow.cxx b/Detectors/FIT/FDD/workflow/src/RawWorkflow.cxx deleted file mode 100644 index c9e5c5be0c81d..0000000000000 --- a/Detectors/FIT/FDD/workflow/src/RawWorkflow.cxx +++ /dev/null @@ -1,42 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file RawWorkflow.cxx - -#include "FDDWorkflow/RawWorkflow.h" -#include "FDDWorkflow/RawDataProcessSpec.h" -#include "FDDWorkflow/RawDataReaderSpec.h" -#include "FDDWorkflow/DigitWriterSpec.h" -#include "FDDWorkflow/RawReaderFDD.h" -namespace o2 -{ -namespace fdd -{ - -framework::WorkflowSpec getFDDRawWorkflow(bool useProcess, - bool dumpProcessor, bool dumpReader, - bool disableRootOut) -{ - LOG(info) << "framework::WorkflowSpec getFDDWorkflow"; - framework::WorkflowSpec specs; - specs.emplace_back(o2::fdd::getFDDRawDataReaderSpec(RawReaderFDD{dumpReader})); - - if (useProcess) { - specs.emplace_back(o2::fdd::getFDDRawDataProcessSpec(dumpProcessor)); - } - if (!disableRootOut) { - specs.emplace_back(o2::fdd::getFDDDigitWriterSpec(false, false)); - } - return specs; -} - -} // namespace fdd -} // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataProcessDPLSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataProcessDPLSpec.h deleted file mode 100644 index 7b7e98d50368e..0000000000000 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataProcessDPLSpec.h +++ /dev/null @@ -1,61 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file FT0DataProcessDPLSpec.h - -#ifndef O2_FT0DATAPROCESSDPLSPEC_H -#define O2_FT0DATAPROCESSDPLSPEC_H - -#include "Framework/CallbackService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/Lifetime.h" -#include "Framework/Output.h" -#include "Framework/WorkflowSpec.h" -#include "Framework/SerializationMethods.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" - -#include "FT0Raw/DigitBlockFT0.h" -#include "DataFormatsFT0/Digit.h" -#include "DataFormatsFT0/ChannelData.h" - -#include -#include -#include - -using namespace o2::framework; - -namespace o2 -{ -namespace ft0 -{ - -class FT0DataProcessDPLSpec : public Task -{ - public: - FT0DataProcessDPLSpec(bool dumpEventBlocks) : mDumpEventBlocks(dumpEventBlocks) {} - ~FT0DataProcessDPLSpec() override = default; - void init(InitContext& ic) final; - void run(ProcessingContext& pc) final; - - private: - bool mDumpEventBlocks; - - o2::header::DataOrigin mOrigin = o2::header::gDataOriginFT0; -}; - -framework::DataProcessorSpec getFT0DataProcessDPLSpec(bool dumpProcessor); - -} // namespace ft0 -} // namespace o2 - -#endif /* O2_FT0DATAPROCESSDPL_H */ diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataReaderDPLSpec.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataReaderDPLSpec.h deleted file mode 100644 index 9074f4f7f0f34..0000000000000 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0DataReaderDPLSpec.h +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file FT0DataReaderDPLSpec.h - -#ifndef O2_FT0DATAREADERDPLSPEC_H -#define O2_FT0DATAREADERDPLSPEC_H -#include "DataFormatsFT0/LookUpTable.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" -#include "Framework/CallbackService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" -#include "Framework/Lifetime.h" -#include "Framework/Output.h" -#include "Framework/WorkflowSpec.h" -#include "Framework/SerializationMethods.h" -#include "DPLUtils/DPLRawParser.h" -#include "Framework/InputRecordWalker.h" -#include -#include -#include -#include "CommonUtils/VerbosityConfig.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace ft0 -{ -template -class FT0DataReaderDPLSpec : public Task -{ - public: - FT0DataReaderDPLSpec(const RawReader& rawReader) : mRawReader(rawReader) {} - FT0DataReaderDPLSpec() = default; - ~FT0DataReaderDPLSpec() override = default; - typedef RawReader RawReader_t; - void init(InitContext& ic) final { o2::ft0::SingleLUT::Instance().printFullMap(); } - void run(ProcessingContext& pc) final - { - // if we see requested data type input with 0xDEADBEEF subspec and 0 payload this means that the "delayed message" - // mechanism created it in absence of real data from upstream. Processor should send empty output to not block the workflow - { - static size_t contDeadBeef = 0; // number of times 0xDEADBEEF was seen continuously - std::vector dummy{InputSpec{"dummy", ConcreteDataMatcher{o2::header::gDataOriginFT0, o2::header::gDataDescriptionRawData, 0xDEADBEEF}}}; - for (const auto& ref : InputRecordWalker(pc.inputs(), dummy)) { - const auto dh = o2::framework::DataRefUtils::getHeader(ref); - auto payloadSize = DataRefUtils::getPayloadSize(ref); - if (payloadSize == 0) { - auto maxWarn = o2::conf::VerbosityConfig::Instance().maxWarnDeadBeef; - if (++contDeadBeef <= maxWarn) { - LOGP(alarm, "Found input [{}/{}/{:#x}] TF#{} 1st_orbit:{} Payload {} : assuming no payload for all links in this TF{}", - dh->dataOrigin.str, dh->dataDescription.str, dh->subSpecification, dh->tfCounter, dh->firstTForbit, payloadSize, - contDeadBeef == maxWarn ? fmt::format(". {} such inputs in row received, stopping reporting", contDeadBeef) : ""); - } - mRawReader.makeSnapshot(pc); // send empty output - return; - } - } - contDeadBeef = 0; // if good data, reset the counter - } - std::vector filter{InputSpec{"filter", ConcreteDataTypeMatcher{o2::header::gDataOriginFT0, o2::header::gDataDescriptionRawData}, Lifetime::Timeframe}}; - DPLRawParser parser(pc.inputs(), filter); - std::size_t count = 0; - for (auto it = parser.begin(), end = parser.end(); it != end; ++it) { - //Proccessing each page - count++; - auto rdhPtr = reinterpret_cast(it.raw()); - gsl::span payload(it.data(), it.size()); - mRawReader.process(payload, o2::raw::RDHUtils::getLinkID(rdhPtr), o2::raw::RDHUtils::getEndPointID(rdhPtr)); - } - LOG(info) << "Pages: " << count; - mRawReader.accumulateDigits(); - mRawReader.makeSnapshot(pc); - mRawReader.clear(); - } - RawReader_t mRawReader; -}; - -template -framework::DataProcessorSpec getFT0DataReaderDPLSpec(const RawReader& rawReader, bool askSTFDist) -{ - LOG(info) << "DataProcessorSpec initDataProcSpec() for RawReaderFT0"; - std::vector outputSpec; - RawReader::prepareOutputSpec(outputSpec); - std::vector inputSpec{{"STF", ConcreteDataTypeMatcher{o2::header::gDataOriginFT0, "RAWDATA"}, Lifetime::Timeframe}}; - if (askSTFDist) { - inputSpec.emplace_back("STFDist", "FLP", "DISTSUBTIMEFRAME", 0, Lifetime::Timeframe); - } - return DataProcessorSpec{ - "ft0-datareader-dpl", - inputSpec, - outputSpec, - adaptFromTask>(rawReader), - Options{}}; -} - -} // namespace ft0 -} // namespace o2 - -#endif /* O2_FT0DATAREADERDPL_H */ diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0Workflow.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0Workflow.h deleted file mode 100644 index a4988b2c18fc7..0000000000000 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/FT0Workflow.h +++ /dev/null @@ -1,28 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef O2_FIT_FT0WORKFLOW_H -#define O2_FIT_FT0WORKFLOW_H - -/// @file FT0Workflow.h - -#include "Framework/WorkflowSpec.h" - -namespace o2 -{ -namespace ft0 -{ -framework::WorkflowSpec getFT0Workflow(bool isExtendedMode, bool useProcess, - bool dumpProcessor, bool dumpReader, - bool disableRootOut, bool askSTFDist); -} // namespace ft0 -} // namespace o2 -#endif diff --git a/Detectors/FIT/FT0/workflow/include/FT0Workflow/RawReaderFT0.h b/Detectors/FIT/FT0/workflow/include/FT0Workflow/RawReaderFT0.h deleted file mode 100644 index f7729394db652..0000000000000 --- a/Detectors/FIT/FT0/workflow/include/FT0Workflow/RawReaderFT0.h +++ /dev/null @@ -1,156 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -// -//file RawReaderFT0.h class for RAW data reading -// -// Artur.Furs -// afurs@cern.ch -// -//Main purpuse is to decode FT0 data blocks and push them to DigitBlockFT0 for proccess -//TODO: prepare wrappers for containers with digits and combine classes below into one template class? -#ifndef ALICEO2_FIT_RAWREADERFT0_H_ -#define ALICEO2_FIT_RAWREADERFT0_H_ -#include -#include -#include -#include "FT0Raw/RawReaderFT0Base.h" - -#include "DataFormatsFT0/Digit.h" -#include "DataFormatsFT0/ChannelData.h" - -#include "Framework/ProcessingContext.h" -#include "Framework/DataAllocator.h" -#include "Framework/OutputSpec.h" -#include - -namespace o2 -{ -namespace ft0 -{ -//Normal TCM mode -template -class RawReaderFT0 : public RawReaderFT0BaseNorm -{ - public: - RawReaderFT0(bool dumpData) : mDumpData(dumpData) {} - RawReaderFT0(const RawReaderFT0&) = default; - - RawReaderFT0() = default; - ~RawReaderFT0() = default; - static constexpr bool sUseTrgInput = useTrgInput; - void clear() - { - mVecDigits.clear(); - if constexpr (sUseTrgInput) { - mVecTriggerInput.clear(); - } - mVecChannelData.clear(); - } - void accumulateDigits() - { - if constexpr (sUseTrgInput) { - getDigits(mVecDigits, mVecChannelData, mVecTriggerInput); - } else { - getDigits(mVecDigits, mVecChannelData); - } - LOG(info) << "Number of Digits: " << mVecDigits.size(); - LOG(info) << "Number of ChannelData: " << mVecChannelData.size(); - if constexpr (sUseTrgInput) { - LOG(info) << "Number of TriggerInput: " << mVecTriggerInput.size(); - } - if (mDumpData) { - DigitBlockFT0::print(mVecDigits, mVecChannelData); - } - } - static void prepareOutputSpec(std::vector& outputSpec) - { - outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSBC", 0, o2::framework::Lifetime::Timeframe); - outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSCH", 0, o2::framework::Lifetime::Timeframe); - if constexpr (sUseTrgInput) { - outputSpec.emplace_back(o2::header::gDataOriginFT0, "TRIGGERINPUT", 0, o2::framework::Lifetime::Timeframe); - } - } - void makeSnapshot(o2::framework::ProcessingContext& pc) - { - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSBC", 0}, mVecDigits); - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSCH", 0}, mVecChannelData); - if constexpr (sUseTrgInput) { - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "TRIGGERINPUT", 0}, mVecTriggerInput); - } - } - bool mDumpData; - std::vector mVecDigits; - std::vector mVecTriggerInput; - std::vector mVecChannelData; -}; - -//Extended TCM mode (additional raw data struct) -template -class RawReaderFT0ext : public RawReaderFT0BaseExt -{ - public: - RawReaderFT0ext(bool dumpData) : mDumpData(dumpData) {} - RawReaderFT0ext(const RawReaderFT0ext&) = default; - static constexpr bool sUseTrgInput = useTrgInput; - RawReaderFT0ext() = default; - ~RawReaderFT0ext() = default; - void clear() - { - mVecDigits.clear(); - mVecChannelData.clear(); - mVecTrgExt.clear(); - if constexpr (sUseTrgInput) { - mVecTriggerInput.clear(); - } - } - void accumulateDigits() - { - if constexpr (sUseTrgInput) { - getDigits(mVecDigits, mVecChannelData, mVecTrgExt, mVecTriggerInput); - } else { - getDigits(mVecDigits, mVecChannelData, mVecTrgExt); - } - LOG(info) << "Number of Digits: " << mVecDigits.size(); - LOG(info) << "Number of ChannelData: " << mVecChannelData.size(); - LOG(info) << "Number of TriggerExt: " << mVecTrgExt.size(); - if (mDumpData) { - DigitBlockFT0ext::print(mVecDigits, mVecChannelData, mVecTrgExt); - } - } - static void prepareOutputSpec(std::vector& outputSpec) - { - outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSBC", 0, o2::framework::Lifetime::Timeframe); - outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSCH", 0, o2::framework::Lifetime::Timeframe); - outputSpec.emplace_back(o2::header::gDataOriginFT0, "DIGITSTRGEXT", 0, o2::framework::Lifetime::Timeframe); - if constexpr (sUseTrgInput) { - outputSpec.emplace_back(o2::header::gDataOriginFT0, "TRIGGERINPUT", 0, o2::framework::Lifetime::Timeframe); - } - } - void makeSnapshot(o2::framework::ProcessingContext& pc) - { - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSBC", 0}, mVecDigits); - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSCH", 0}, mVecChannelData); - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "DIGITSTRGEXT", 0}, mVecTrgExt); - if constexpr (sUseTrgInput) { - pc.outputs().snapshot(o2::framework::Output{o2::header::gDataOriginFT0, "TRIGGERINPUT", 0}, mVecTriggerInput); - } - } - bool mDumpData; - std::vector mVecDigits; - std::vector mVecChannelData; - std::vector mVecTrgExt; - std::vector mVecTriggerInput; -}; - -} // namespace ft0 -} // namespace o2 - -#endif diff --git a/Detectors/FIT/FT0/workflow/src/FT0DataProcessDPLSpec.cxx b/Detectors/FIT/FT0/workflow/src/FT0DataProcessDPLSpec.cxx deleted file mode 100644 index d7a7a689d402f..0000000000000 --- a/Detectors/FIT/FT0/workflow/src/FT0DataProcessDPLSpec.cxx +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file FT0DataProcessDPLSpec.cxx - -#include "FT0Workflow/FT0DataProcessDPLSpec.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace ft0 -{ -using namespace std; -void FT0DataProcessDPLSpec::init(InitContext& ic) -{ -} - -void FT0DataProcessDPLSpec::run(ProcessingContext& pc) -{ - LOG(info) << "FT0DataProcessDPLSpec running..."; - auto vecDigits = pc.inputs().get>("digits"); - auto vecChannelData = pc.inputs().get>("digch"); - if (mDumpEventBlocks) { - DigitBlockFT0::print(vecDigits, vecChannelData); - } -} - -DataProcessorSpec getFT0DataProcessDPLSpec(bool dumpProcessor) -{ - std::vector inputSpec; - inputSpec.emplace_back("digits", o2::header::gDataOriginFT0, "DIGITSBC", 0, Lifetime::Timeframe); - inputSpec.emplace_back("digch", o2::header::gDataOriginFT0, "DIGITSCH", 0, Lifetime::Timeframe); - LOG(info) << "DataProcessorSpec getFT0DataProcessDPLSpec"; - return DataProcessorSpec{ - "ft0-dataprocess-dpl-flp", - inputSpec, - Outputs{}, - AlgorithmSpec{adaptFromTask(dumpProcessor)}, - Options{}}; -} - -} // namespace ft0 -} // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/FT0DataReaderDPLSpec.cxx b/Detectors/FIT/FT0/workflow/src/FT0DataReaderDPLSpec.cxx deleted file mode 100644 index caa642794b561..0000000000000 --- a/Detectors/FIT/FT0/workflow/src/FT0DataReaderDPLSpec.cxx +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file FT0DataReaderDPLSpec.cxx - -#include "FT0Workflow/FT0DataReaderDPLSpec.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace ft0 -{ - -} // namespace ft0 -} // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/FT0Workflow.cxx b/Detectors/FIT/FT0/workflow/src/FT0Workflow.cxx deleted file mode 100644 index 156feb7dd3e2f..0000000000000 --- a/Detectors/FIT/FT0/workflow/src/FT0Workflow.cxx +++ /dev/null @@ -1,45 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file FT0Workflow.cxx - -#include "FT0Workflow/FT0Workflow.h" -#include "FT0Workflow/FT0DataProcessDPLSpec.h" -#include "FT0Workflow/FT0DataReaderDPLSpec.h" -#include "FT0Workflow/FT0DigitWriterSpec.h" -#include "FT0Workflow/RawReaderFT0.h" -namespace o2 -{ -namespace ft0 -{ - -framework::WorkflowSpec getFT0Workflow(bool isExtendedMode, bool useProcess, - bool dumpProcessor, bool dumpReader, - bool disableRootOut, bool askSTFDist) -{ - LOG(info) << "framework::WorkflowSpec getFT0Workflow"; - framework::WorkflowSpec specs; - if (isExtendedMode) { - specs.emplace_back(o2::ft0::getFT0DataReaderDPLSpec(RawReaderFT0ext{dumpReader}, askSTFDist)); - } else { - specs.emplace_back(o2::ft0::getFT0DataReaderDPLSpec(RawReaderFT0{dumpReader}, askSTFDist)); - } - if (useProcess) { - specs.emplace_back(o2::ft0::getFT0DataProcessDPLSpec(dumpProcessor)); - } - if (!disableRootOut) { - specs.emplace_back(o2::ft0::getFT0DigitWriterSpec(false, false)); - } - return specs; -} - -} // namespace ft0 -} // namespace o2 diff --git a/Detectors/FIT/FT0/workflow/src/RawReaderFT0.cxx b/Detectors/FIT/FT0/workflow/src/RawReaderFT0.cxx deleted file mode 100644 index b2ef17e540112..0000000000000 --- a/Detectors/FIT/FT0/workflow/src/RawReaderFT0.cxx +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "FT0Workflow/RawReaderFT0.h" -using namespace o2::ft0; From ca1c2128b44820ff63518d0d298664c716cd4cb5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Wiktor=20Piero=C5=BCak?= <94726725+wpierozak@users.noreply.github.com> Date: Fri, 20 Feb 2026 17:47:38 +0100 Subject: [PATCH 286/701] FT0: Implemented condition on sum of amplitudes in EventsPerBc calibration (#15084) Co-authored-by: wpierozak --- .../include/FT0Calibration/EventsPerBcCalibrator.h | 6 ++++-- Detectors/FIT/FT0/calibration/src/EventsPerBcCalibrator.cxx | 6 +++--- .../workflow/FT0EventsPerBcProcessor-Workflow.cxx | 3 ++- Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h | 6 +++++- 4 files changed, 14 insertions(+), 7 deletions(-) diff --git a/Detectors/FIT/FT0/calibration/include/FT0Calibration/EventsPerBcCalibrator.h b/Detectors/FIT/FT0/calibration/include/FT0Calibration/EventsPerBcCalibrator.h index f44824517f258..d831cc36201ab 100644 --- a/Detectors/FIT/FT0/calibration/include/FT0Calibration/EventsPerBcCalibrator.h +++ b/Detectors/FIT/FT0/calibration/include/FT0Calibration/EventsPerBcCalibrator.h @@ -31,7 +31,7 @@ namespace o2::ft0 { struct EventsPerBcContainer { - EventsPerBcContainer(int32_t minAmplitudeSideA, int32_t minAmplitudeSideC) : mMinAmplitudeSideA(minAmplitudeSideA), mMinAmplitudeSideC(minAmplitudeSideC) {} + 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; @@ -40,6 +40,7 @@ struct EventsPerBcContainer { const int32_t mMinAmplitudeSideA; const int32_t mMinAmplitudeSideC; + const int32_t mMinSumOfAmplitude; std::array mTvx{0.0}; size_t entries{0}; @@ -56,7 +57,7 @@ class EventsPerBcCalibrator final : public o2::calibration::TimeSlotCalibration< using EventsHistogram = std::array; public: - EventsPerBcCalibrator(uint32_t minNumberOfEntries, int32_t minAmplitudeSideA, int32_t minAmplitudeSideC); + EventsPerBcCalibrator(uint32_t minNumberOfEntries, int32_t minAmplitudeSideA, int32_t minAmplitudeSideC, int32_t minSumOfAmplitude); bool hasEnoughData(const Slot& slot) const override; void initOutput() override; @@ -70,6 +71,7 @@ class EventsPerBcCalibrator final : public o2::calibration::TimeSlotCalibration< const uint32_t mMinNumberOfEntries; const int32_t mMinAmplitudeSideA; const int32_t mMinAmplitudeSideC; + const int32_t mMinSumOfAmplitude; std::vector mTvxPerBcs; std::vector> mTvxPerBcInfos; diff --git a/Detectors/FIT/FT0/calibration/src/EventsPerBcCalibrator.cxx b/Detectors/FIT/FT0/calibration/src/EventsPerBcCalibrator.cxx index a2230f51dc4ea..b17c81213cd08 100644 --- a/Detectors/FIT/FT0/calibration/src/EventsPerBcCalibrator.cxx +++ b/Detectors/FIT/FT0/calibration/src/EventsPerBcCalibrator.cxx @@ -23,7 +23,7 @@ void EventsPerBcContainer::fill(const o2::dataformats::TFIDInfo& ti, const gsl:: { size_t oldEntries = entries; for (const auto& digit : data) { - if (digit.mTriggers.getVertex() && digit.mTriggers.getAmplA() >= mMinAmplitudeSideA && digit.mTriggers.getAmplC() >= mMinAmplitudeSideC) { + if (digit.mTriggers.getVertex() && digit.mTriggers.getAmplA() >= mMinAmplitudeSideA && digit.mTriggers.getAmplC() >= mMinAmplitudeSideC && (digit.mTriggers.getAmplA() + digit.mTriggers.getAmplC()) >= mMinSumOfAmplitude) { mTvx[digit.mIntRecord.bc]++; entries++; } @@ -45,7 +45,7 @@ void EventsPerBcCalibrator::initOutput() mTvxPerBcInfos.clear(); } -EventsPerBcCalibrator::EventsPerBcCalibrator(uint32_t minNumberOfEntries, int32_t minAmplitudeSideA, int32_t minAmplitudeSideC) : mMinNumberOfEntries(minNumberOfEntries), mMinAmplitudeSideA(minAmplitudeSideA), mMinAmplitudeSideC(minAmplitudeSideC) +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; @@ -75,7 +75,7 @@ EventsPerBcCalibrator::Slot& EventsPerBcCalibrator::emplaceNewSlot(bool front, T { auto& cont = getSlots(); auto& slot = front ? cont.emplace_front(tstart, tend) : cont.emplace_back(tstart, tend); - slot.setContainer(std::make_unique(mMinAmplitudeSideA, mMinAmplitudeSideC)); + 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/workflow/FT0EventsPerBcProcessor-Workflow.cxx b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.cxx index ac7a8e52f53b1..5cef707da2cca 100644 --- a/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.cxx +++ b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.cxx @@ -39,7 +39,8 @@ o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext co {"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-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); diff --git a/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h index c587ab58fcd90..d493e2a606613 100644 --- a/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h +++ b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h @@ -50,8 +50,11 @@ class FT0EventsPerBcProcessor final : public o2::framework::Task 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); + mCalibrator = std::make_unique(mMinNumberOfEntries, mMinAmplitudeSideA, mMinAmplitudeSideC, mMinSumOfAmplitude); if (mOneObjectPerRun) { LOG(info) << "Only one object will be created at the end of run"; @@ -119,6 +122,7 @@ class FT0EventsPerBcProcessor final : public o2::framework::Task uint32_t mMinNumberOfEntries; int32_t mMinAmplitudeSideA; int32_t mMinAmplitudeSideC; + int32_t mMinSumOfAmplitude; }; } // namespace o2::calibration #endif \ No newline at end of file From 4728518ad3cee9c3bb9ab424733e7c79d46fc934 Mon Sep 17 00:00:00 2001 From: Stefano Cannito <143754257+scannito@users.noreply.github.com> Date: Fri, 20 Feb 2026 18:13:21 +0100 Subject: [PATCH 287/701] [ALICE] Fix TRK services crossing (#15085) --- .../Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx index 25c59b3c8fd4a..bd27a5bc30f62 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx @@ -264,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 = 64.2f; - 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"); @@ -318,7 +318,7 @@ void TRKServices::createMiddleServices(TGeoVolume* motherVolume) // Middle barrel connection disks const float rMinMiddleBarrelDisk = 5.68f; const float rMaxMiddleBarrelDisk = 35.f; - const float zLengthMiddleBarrel = 64.2f; + 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 / 2.); TGeoTube* middleBarrelConnDiskPE = new TGeoTube(Form("TRK_MIDBARCONN_DISK_PEsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, peFiberThick / 2.); From 16ee3b839f933da6185705bc9476023d84fc8391 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Fri, 20 Feb 2026 21:00:56 +0100 Subject: [PATCH 288/701] [ALICE3] Add proto segmentation of TF3 (#15081) * Use TF3 CAD specifications --- .../base/include/IOTOFBase/IOTOFBaseParam.h | 2 + .../include/IOTOFSimulation/Detector.h | 2 +- .../include/IOTOFSimulation/Layer.h | 22 +- .../ALICE3/IOTOF/simulation/src/Detector.cxx | 44 ++-- .../ALICE3/IOTOF/simulation/src/Layer.cxx | 237 ++++++++++++++---- .../ALICE3/TRK/base/src/GeometryTGeo.cxx | 1 - 6 files changed, 239 insertions(+), 69 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h index bf605797cbfe5..b74fc6d6869dd 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h @@ -26,6 +26,8 @@ struct IOTOFBaseParam : public o2::conf::ConfigurableParamHelper 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 f39a43733ccab..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, std::string pattern = ""); + 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 3a971e81a610d..0742af3a1340a 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx @@ -40,7 +40,8 @@ Detector::Detector(bool active) auto& iotofPars = IOTOFBaseParam::Instance(); configLayers(iotofPars.enableInnerTOF, iotofPars.enableOuterTOF, iotofPars.enableForwardTOF, iotofPars.enableBackwardTOF, - iotofPars.detectorPattern); + iotofPars.detectorPattern, + iotofPars.segmentedInnerTOF, iotofPars.segmentedOuterTOF); } Detector::~Detector() @@ -56,7 +57,7 @@ void Detector::ConstructGeometry() createGeometry(); } -void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::string pattern) +void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::string pattern, bool itofSegmented, bool otofSegmented) { float radiusInnerTof = 19.f; @@ -65,9 +66,10 @@ void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::str 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") { - LOG(info) << "Configuring IOTOF layers with v3b pattern"; ftof = false; btof = false; } else if (pattern == "v3b1a") { @@ -93,17 +95,25 @@ void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::str } else { LOG(fatal) << "IOTOF layer pattern " << pattern << " not recognized, exiting"; } - if (itof) { - mITOFLayer = ITOFLayer(std::string{GeometryTGeo::getITOFLayerPattern()}, radiusInnerTof, 0.f, lengthInnerTof, 0.f, 0.02f, true); // iTOF + 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) { - mOTOFLayer = OTOFLayer(std::string{GeometryTGeo::getOTOFLayerPattern()}, radiusOuterTof, 0.f, lengthOuterTof, 0.f, 0.02f, true); // oTOF + 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()}, radiusRangeDiskTof.first, radiusRangeDiskTof.second, 0.f, zForwardTof, 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()}, radiusRangeDiskTof.first, radiusRangeDiskTof.second, 0.f, -zForwardTof, 0.02f, false); // bTOF + mBTOFLayer = BTOFLayer(std::string{GeometryTGeo::getBTOFLayerPattern()}, radiusRangeDiskTof.first, radiusRangeDiskTof.second, 0.f, -zForwardTof, 0.02f, BTOFLayer::kDisk); // bTOF } } @@ -186,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()); 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/TRK/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx index 059a35520c1a0..d5d37ec00acef 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx @@ -102,7 +102,6 @@ void GeometryTGeo::Build(int loadTrans) 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); From f23aa72dfdb3779502c9510526dab12715756c40 Mon Sep 17 00:00:00 2001 From: shahoian Date: Sat, 21 Feb 2026 16:25:38 +0100 Subject: [PATCH 289/701] Add missing reset for FV0 channels vector at TF start --- Detectors/FIT/FV0/workflow/src/ReconstructionSpec.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/Detectors/FIT/FV0/workflow/src/ReconstructionSpec.cxx b/Detectors/FIT/FV0/workflow/src/ReconstructionSpec.cxx index b97186bbf81a8..cdf297b334588 100644 --- a/Detectors/FIT/FV0/workflow/src/ReconstructionSpec.cxx +++ b/Detectors/FIT/FV0/workflow/src/ReconstructionSpec.cxx @@ -42,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 From 354b12edc5d97890ef6ae952be530323b7c41075 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 19 Feb 2026 19:42:12 +0100 Subject: [PATCH 290/701] GPU: Improve some error messages --- GPU/GPUTracking/DataCompression/TPCClusterDecompressor.cxx | 4 ++-- GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) 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/Global/GPUChainTrackingCompression.cxx b/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx index ca1352b3bda1b..89d47d0e1b86c 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx @@ -273,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; From 480167460a22dfe0a23f8c2285901c739f719c6c Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 15 Oct 2025 09:44:10 +0200 Subject: [PATCH 291/701] GPU: Improve existing debug dumps --- GPU/GPUTracking/Merger/GPUTPCGMMerger.h | 2 + GPU/GPUTracking/Merger/GPUTPCGMMergerDump.cxx | 74 ++++++++++--------- 2 files changed, 40 insertions(+), 36 deletions(-) diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMerger.h b/GPU/GPUTracking/Merger/GPUTPCGMMerger.h index 14974bdec2303..813e3df29e82e 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; 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 "; From a839182762a089481cece2ef7e58a3c7136ac93d Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 19 Feb 2026 22:27:38 +0100 Subject: [PATCH 292/701] GPU TPC: Fix deterministic mode with new cluster removal protection --- GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx b/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx index 260781c17406b..eaf181b741918 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx @@ -1852,7 +1852,7 @@ GPUd() void GPUTPCGMMerger::PrepareForFit1(int32_t nBlocks, int32_t nThreads, in if (CAMath::Abs(trk.GetParam().GetQPt() * Param().qptB5Scaler) <= Param().rec.tpc.rejectQPtB5 && !trk.MergedLooper() && trk.Leg() == 0) { weight |= attachProtect; } - mClusterAttachment[mClusters[trk.FirstClusterRef() + j].num] = weight; + CAMath::AtomicMax(&mClusterAttachment[mClusters[trk.FirstClusterRef() + j].num], weight); CAMath::AtomicAdd(&mSharedCount[mClusters[trk.FirstClusterRef() + j].num], 1u); } if (!trk.CCE() && !trk.MergedLooper()) { From 84a89e877a04d98c1969997a3789449ec26887bb Mon Sep 17 00:00:00 2001 From: David Rohr Date: Fri, 20 Feb 2026 00:03:17 +0100 Subject: [PATCH 293/701] GPU TPC: Do not use fitWithoutProjection for now, sometimes broken --- GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx | 21 +++++++++----------- GPU/GPUTracking/Merger/GPUTPCGMMerger.h | 2 +- GPU/GPUTracking/Merger/GPUTPCGMSectorTrack.h | 3 ++- 3 files changed, 12 insertions(+), 14 deletions(-) diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx b/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx index eaf181b741918..3622e51bd663f 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx @@ -555,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(); @@ -718,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()); @@ -783,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; @@ -809,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; @@ -827,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]; @@ -860,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; @@ -887,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; } @@ -904,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); @@ -1019,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)) { diff --git a/GPU/GPUTracking/Merger/GPUTPCGMMerger.h b/GPU/GPUTracking/Merger/GPUTPCGMMerger.h index 813e3df29e82e..8f554c24c1d8c 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMMerger.h +++ b/GPU/GPUTracking/Merger/GPUTPCGMMerger.h @@ -226,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/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; } From ab73e40320a9d6512588c642d576a80ada959981 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Mon, 23 Feb 2026 10:40:26 +0100 Subject: [PATCH 294/701] ITS3: define alignable volumes (#15050) * ITS3: define alignable volumes Signed-off-by: Felix Schlepper * Add support for ITS3 in Detector class * Please consider the following formatting changes --------- Signed-off-by: Felix Schlepper Co-authored-by: ALICE Action Bot --- .../ITS/base/include/ITSBase/GeometryTGeo.h | 2 +- .../ITSMFT/ITS/base/src/GeometryTGeo.cxx | 8 +--- .../ITSMFT/ITS/simulation/src/Detector.cxx | 21 ++++----- .../DescriptorInnerBarrelITS3.h | 8 ++-- .../src/DescriptorInnerBarrelITS3.cxx | 44 +++++++++++++++++++ 5 files changed, 59 insertions(+), 24 deletions(-) diff --git a/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h b/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h index e236c898851f5..c8ef445e273d3 100644 --- a/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h +++ b/Detectors/ITSMFT/ITS/base/include/ITSBase/GeometryTGeo.h @@ -314,7 +314,7 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo static const char* getITS3PixelArrayPattern(int layer) { return Form("%s%d", getITS3PixelArrayPatternRaw(), layer); }; /// sym name of the layer - static const char* composeSymNameITS(bool isITS3 = false); + static const char* composeSymNameITS(); /// sym name of the layer static const char* composeSymNameLayer(int lr, bool isITS3 = false); diff --git a/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx b/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx index 60570b2f204c5..5dc499d05037e 100644 --- a/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx +++ b/Detectors/ITSMFT/ITS/base/src/GeometryTGeo.cxx @@ -290,14 +290,8 @@ bool GeometryTGeo::getChipId(int index, int& lay, int& hba, int& sta, int& hsta, } //__________________________________________________________________________ -const char* GeometryTGeo::composeSymNameITS(bool isITS3) +const char* GeometryTGeo::composeSymNameITS() { - if (isITS3) { -#ifdef ENABLE_UPGRADES - return o2::detectors::DetID(o2::detectors::DetID::IT3).getName(); -#endif - } - return o2::detectors::DetID(o2::detectors::DetID::ITS).getName(); } diff --git a/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx b/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx index 8cfe13097d581..63d7a8ad8dfa2 100644 --- a/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx +++ b/Detectors/ITSMFT/ITS/simulation/src/Detector.cxx @@ -61,6 +61,7 @@ using Segmentation = o2::itsmft::SegmentationAlpide; using namespace o2::its; #ifdef ENABLE_UPGRADES +#include "ITS3Simulation/DescriptorInnerBarrelITS3.h" using namespace o2::its3; #endif @@ -1106,7 +1107,7 @@ void Detector::addAlignableVolumes() const TString detName = GetName(); TString path = Form("/cave_1/barrel_1/%s_2", GeometryTGeo::getITSVolPattern()); - TString sname = GeometryTGeo::composeSymNameITS((detName == "IT3")); + TString sname = GeometryTGeo::composeSymNameITS(); LOG(debug) << sname << " <-> " << path; @@ -1117,15 +1118,19 @@ void Detector::addAlignableVolumes() const Int_t lastUID = 0; for (Int_t lr = 0; lr < mNumberLayers; lr++) { if (lr < mNumberInnerLayers) { +#ifdef ENABLE_UPGRADES if (detName == "ITS") { ((DescriptorInnerBarrelITS2*)mDescriptorIB.get())->addAlignableVolumesLayer(lr, mWrapperLayerId[lr], path, lastUID); + } else { + ((DescriptorInnerBarrelITS3*)mDescriptorIB.get())->addAlignableVolumesLayer(lr, mWrapperLayerId[lr], path, lastUID); } +#else + ((DescriptorInnerBarrelITS2*)mDescriptorIB.get())->addAlignableVolumesLayer(lr, mWrapperLayerId[lr], path, lastUID); +#endif } else { addAlignableVolumesLayer(lr, path, lastUID); } } - - return; } void Detector::addAlignableVolumesLayer(int lr, TString& parent, Int_t& lastUID) const @@ -1148,8 +1153,6 @@ void Detector::addAlignableVolumesLayer(int lr, TString& parent, Int_t& lastUID) for (Int_t hb = start; hb < nhbarrel; hb++) { addAlignableVolumesHalfBarrel(lr, hb, path, lastUID); } - - return; } void Detector::addAlignableVolumesHalfBarrel(Int_t lr, Int_t hb, TString& parent, Int_t& lastUID) const @@ -1177,8 +1180,6 @@ void Detector::addAlignableVolumesHalfBarrel(Int_t lr, Int_t hb, TString& parent for (int st = 0; st < nstaves; st++) { addAlignableVolumesStave(lr, hb, st, path, lastUID); } - - return; } void Detector::addAlignableVolumesStave(Int_t lr, Int_t hb, Int_t st, TString& parent, Int_t& lastUID) const @@ -1205,8 +1206,6 @@ void Detector::addAlignableVolumesStave(Int_t lr, Int_t hb, Int_t st, TString& p for (Int_t sst = start; sst < nhstave; sst++) { addAlignableVolumesHalfStave(lr, hb, st, sst, path, lastUID); } - - return; } void Detector::addAlignableVolumesHalfStave(Int_t lr, Int_t hb, Int_t st, Int_t hst, TString& parent, Int_t& lastUID) const @@ -1236,8 +1235,6 @@ void Detector::addAlignableVolumesHalfStave(Int_t lr, Int_t hb, Int_t st, Int_t for (Int_t md = start; md < nmodules; md++) { addAlignableVolumesModule(lr, hb, st, hst, md, path, lastUID); } - - return; } void Detector::addAlignableVolumesModule(Int_t lr, Int_t hb, Int_t st, Int_t hst, Int_t md, TString& parent, Int_t& lastUID) const @@ -1266,8 +1263,6 @@ void Detector::addAlignableVolumesModule(Int_t lr, Int_t hb, Int_t st, Int_t hst for (Int_t ic = 0; ic < nchips; ic++) { addAlignableVolumesChip(lr, hb, st, hst, md, ic, path, lastUID); } - - return; } void Detector::addAlignableVolumesChip(Int_t lr, Int_t hb, Int_t st, Int_t hst, Int_t md, Int_t ch, TString& parent, diff --git a/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/DescriptorInnerBarrelITS3.h b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/DescriptorInnerBarrelITS3.h index 80565df55d154..3e230cee474bd 100644 --- a/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/DescriptorInnerBarrelITS3.h +++ b/Detectors/Upgrades/ITS3/simulation/include/ITS3Simulation/DescriptorInnerBarrelITS3.h @@ -40,17 +40,19 @@ class DescriptorInnerBarrelITS3 : public o2::its::DescriptorInnerBarrel void createLayer(int idLayer, TGeoVolume* dest); void createServices(TGeoVolume* dest); void configure() {} + void addAlignableVolumesLayer(int idLayer, int wrapperLayerId, TString& parentPath, int& lastUID) const; protected: - int mNumLayers{constants::nLayers}; - // wrapper volume properties 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 + static constexpr double mWrapperZSpanITS3{(constants::services::length * 2) + mTolerance}; // z length is divided in half private: + void addAlignableVolumesHalfBarrel(int idLayer, int iHB, TString& parentPath, int& lastUID) const; + void addAlignableVolumesChips(int idLayer, int iHalfBarrel, TString& parentPath, int& lastUID) const; + std::array, constants::nLayers> mIBLayers; std::unique_ptr mServices; diff --git a/Detectors/Upgrades/ITS3/simulation/src/DescriptorInnerBarrelITS3.cxx b/Detectors/Upgrades/ITS3/simulation/src/DescriptorInnerBarrelITS3.cxx index 04f244284d5b6..42644fbfe0c38 100644 --- a/Detectors/Upgrades/ITS3/simulation/src/DescriptorInnerBarrelITS3.cxx +++ b/Detectors/Upgrades/ITS3/simulation/src/DescriptorInnerBarrelITS3.cxx @@ -10,6 +10,8 @@ // or submit itself to any jurisdiction. #include "ITS3Simulation/DescriptorInnerBarrelITS3.h" +#include "ITSBase/GeometryTGeo.h" +#include "Framework/Logger.h" using namespace o2::its3; @@ -26,3 +28,45 @@ void DescriptorInnerBarrelITS3::createServices(TGeoVolume* dest) mServices = std::make_unique(); mServices->createCYSSAssembly(dest); } + +void DescriptorInnerBarrelITS3::addAlignableVolumesLayer(int idLayer, int wrapperLayerId, TString& parentPath, int& lastUID) const +{ + TString wrpV = wrapperLayerId != -1 ? Form("%s%d_1", its::GeometryTGeo::getITSWrapVolPattern(), wrapperLayerId) : ""; + TString path = Form("%s/%s/%s%d_0", parentPath.Data(), wrpV.Data(), its::GeometryTGeo::getITS3LayerPattern(), idLayer); + TString sname = its::GeometryTGeo::composeSymNameLayer(idLayer, true); + + for (int iHalfBarrel{0}; iHalfBarrel < 2; ++iHalfBarrel) { + addAlignableVolumesHalfBarrel(idLayer, iHalfBarrel, path, lastUID); + } +} + +void DescriptorInnerBarrelITS3::addAlignableVolumesHalfBarrel(int idLayer, int iHB, TString& parentPath, int& lastUID) const +{ + // for ITS3 smallest alignable volume is the half-barrel (e.g., the carbon-form composite structure with the sensors) + TString path = Form("%s/%s%d_%d", parentPath.Data(), its::GeometryTGeo::getITS3HalfBarrelPattern(), idLayer, iHB); + TString sname = its::GeometryTGeo::composeSymNameHalfBarrel(idLayer, iHB, true); + if (!gGeoManager->SetAlignableEntry(sname.Data(), path.Data())) { + LOG(fatal) << "Unable to set alignable entry ! " << sname << " : " << path; + } + addAlignableVolumesChips(idLayer, iHB, path, lastUID); +} + +void DescriptorInnerBarrelITS3::addAlignableVolumesChips(int idLayer, int iHB, TString& parentPath, int& lastUID) const +{ + for (int seg{0}; seg < constants::nSegments[idLayer]; ++seg) { + for (int rsu{0}; rsu < constants::segment::nRSUs; ++rsu) { + for (int tile{0}; tile < constants::rsu::nTiles; ++tile) { + TString path = parentPath; + path += Form("/%s_0/", its::GeometryTGeo::getITS3ChipPattern(idLayer)); + path += Form("%s_%d/", its::GeometryTGeo::getITS3SegmentPattern(idLayer), seg); + path += Form("%s_%d/", its::GeometryTGeo::getITS3RSUPattern(idLayer), rsu); + path += Form("%s_%d/", its::GeometryTGeo::getITS3TilePattern(idLayer), tile); + TString sname = its::GeometryTGeo::composeSymNameChip(idLayer, iHB, 0, seg, rsu, tile, true); + if (!gGeoManager->SetAlignableEntry(sname.Data(), path.Data())) { + LOG(fatal) << "Unable to set alignable entry ! " << sname << " : " << path; + } + ++lastUID; + } + } + } +} From e75007c89783956185afd405ba03c2f0f47eea51 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sun, 22 Feb 2026 22:36:50 +0100 Subject: [PATCH 295/701] GPU HIP RTC: Check that AMD_EUS_PER_CU is set --- GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAGenRTC.cxx | 7 +++++++ GPU/GPUTracking/Definitions/GPUSettingsList.h | 1 + 2 files changed, 8 insertions(+) diff --git a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAGenRTC.cxx b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAGenRTC.cxx index dba7e680d0b2c..c4e1775e445c3 100644 --- a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAGenRTC.cxx +++ b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDAGenRTC.cxx @@ -74,6 +74,13 @@ int32_t GPUReconstructionCUDA::genRTC(std::string& filename, uint32_t& nCompile) } fclose(fp); } + if constexpr (std::string_view("CUDA") == "HIP") { // Check if we are RTC-compiling for HIP + if (GetProcessingSettings().hipOverrideAMDEUSperCU > 0) { + mParDevice->par_AMD_EUS_PER_CU = GetProcessingSettings().hipOverrideAMDEUSperCU; + } else if (mParDevice->par_AMD_EUS_PER_CU <= 0) { + GPUFatal("AMD_EUS_PER_CU not set in the parameters provided for the AMD GPU, you can override this via --PROChipOverrideAMDEUSperCU [n]"); + } + } 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) { diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index c61056466929e..ea15ecde78c21 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -372,6 +372,7 @@ AddOption(tpcWriteClustersAfterRejection, bool, false, "", 0, "Apply TPC rejecti AddOption(oclPlatformNum, int32_t, -1, "", 0, "Platform to use, in case the backend provides multiple platforms (OpenCL only, -1 = auto-select, -2 query all platforms (also incompatible))") AddOption(oclCompileFromSources, bool, false, "", 0, "Compile OpenCL binary from included source code instead of using included spirv code") AddOption(oclOverrideSourceBuildFlags, std::string, "", "", 0, "Override OCL build flags for compilation from source, put a space for empty options") +AddOption(hipOverrideAMDEUSperCU, int32_t, -1, "", 0, "Override AMD_EUS_PER_CU setting") AddOption(printSettings, bool, false, "", 0, "Print all settings when initializing") AddOption(tpcFreeAllocatedMemoryAfterProcessing, bool, false, "", 0, "Clean all memory allocated by TPC when TPC processing done, only data written to external output resources will remain") AddOption(debugOnFailure, int32_t, 0, "", 0, "Dump raw data in case an error occured, bit 1 enables all dumps, otherwise bitmask for: 2 = signal, 3 = GPUErrorCode", def(1)) From 9a75a460b6a2d6c4c004abc6af64b07d7abf2e1e Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Mon, 23 Feb 2026 23:51:53 +0100 Subject: [PATCH 296/701] o2sim: fix time aggregation in dpl-eventgen (#15091) * Fix timeframe aggregation in dpl-eventgen * precalculate batch size so it does not depend on the event counter updated in the generator loop * use DataAllocator::make instead of snapshot to reduce memory churn --- .../include/Generators/GeneratorService.h | 2 ++ Generators/src/GeneratorService.cxx | 12 +++++++ run/dpl_eventgen.cxx | 36 ++++++++++++------- run/o2sim_mctracks_to_aod.cxx | 3 +- 4 files changed, 38 insertions(+), 15 deletions(-) diff --git a/Generators/include/Generators/GeneratorService.h b/Generators/include/Generators/GeneratorService.h index a0037707bcdd6..13ebe054f2940 100644 --- a/Generators/include/Generators/GeneratorService.h +++ b/Generators/include/Generators/GeneratorService.h @@ -20,6 +20,7 @@ #include #include // could be forward declaration #include +#include namespace o2 { @@ -66,6 +67,7 @@ class GeneratorService std::pair, o2::dataformats::MCEventHeader> generateEvent(); void generateEvent_MCTracks(std::vector& tracks, o2::dataformats::MCEventHeader& header); + void generateEvent_MCTracks(o2::pmr::vector& tracks, o2::dataformats::MCEventHeader& header); void generateEvent_TParticles(std::vector& tparts, o2::dataformats::MCEventHeader& header); private: diff --git a/Generators/src/GeneratorService.cxx b/Generators/src/GeneratorService.cxx index 902b482dc839b..ae0de385a1b23 100644 --- a/Generators/src/GeneratorService.cxx +++ b/Generators/src/GeneratorService.cxx @@ -61,6 +61,18 @@ void GeneratorService::generateEvent_MCTracks(std::vector& tracks, o2:: } } +void GeneratorService::generateEvent_MCTracks(o2::pmr::vector& tracks, o2::dataformats::MCEventHeader& header) +{ + mPrimGen.SetEvent(&header); + mStack.Reset(); + mPrimGen.GenerateEvent(&mStack); // this is the usual FairROOT interface going via stack + + tracks.reserve(mStack.getPrimaries().size()); + for (auto& tparticle : mStack.getPrimaries()) { + tracks.emplace_back(tparticle); + } +} + std::pair, o2::dataformats::MCEventHeader> GeneratorService::generateEvent() { std::vector tracks; diff --git a/run/dpl_eventgen.cxx b/run/dpl_eventgen.cxx index 6202e965f3e8a..3df16ee3e5ebb 100644 --- a/run/dpl_eventgen.cxx +++ b/run/dpl_eventgen.cxx @@ -50,6 +50,9 @@ struct GeneratorTask { std::unique_ptr genservice; TStopwatch timer; + std::vector*> mctracks_vector; + std::vector mcheader_vector; + void init(o2::framework::InitContext& /*ic*/) { genservice.reset(new o2::eventgen::GeneratorService); @@ -85,25 +88,31 @@ struct GeneratorTask { outfile.reset(new TFile(kineoutfilename.c_str(), "RECREATE")); outtree.reset(new TTree("o2sim", "o2sim")); } + + mctracks_vector.reserve(aggregate); + mcheader_vector.reserve(aggregate); } void run(o2::framework::ProcessingContext& pc) { - std::vector mctracks; - o2::dataformats::MCEventHeader mcheader; - auto mctrack_ptr = &mctracks; - if (outfile.get()) { - auto br = o2::base::getOrMakeBranch(*outtree, "MCTrack", &mctrack_ptr); - br->SetAddress(&mctrack_ptr); - } + mctracks_vector.clear(); + mcheader_vector.clear(); - for (auto i = 0; i < std::min((GenCount)aggregate, nEvents - eventCounter); ++i) { - mctracks.clear(); - genservice->generateEvent_MCTracks(mctracks, mcheader); - pc.outputs().snapshot(Output{"MC", "MCHEADER", 0}, mcheader); - pc.outputs().snapshot(Output{"MC", "MCTRACKS", 0}, mctracks); + auto batch = std::min((GenCount)aggregate, nEvents - eventCounter); + for (auto i = 0U; i < batch; ++i) { + mctracks_vector.push_back(&pc.outputs().make>(Output{"MC", "MCTRACKS", 0})); + auto& mctracks = mctracks_vector.back(); + mcheader_vector.push_back(&pc.outputs().make(Output{"MC", "MCHEADER", 0})); + auto& mcheader = mcheader_vector.back(); + genservice->generateEvent_MCTracks(*mctracks, *mcheader); ++eventCounter; + auto mctrack_ptr = mctracks; + if (outfile.get()) { + auto br = o2::base::getOrMakeBranch(*outtree, "MCTrack", &mctrack_ptr); + br->SetAddress(&mctrack_ptr); + } + if (outfile.get() && outtree.get()) { outtree->Fill(); } @@ -112,6 +121,7 @@ struct GeneratorTask { // report number of TFs injected for the rate limiter to work ++tfCounter; pc.services().get().send(o2::monitoring::Metric{(uint64_t)tfCounter, "df-sent"}.addTag(o2::monitoring::tags::Key::Subsystem, o2::monitoring::tags::Value::DPL)); + bool time_expired = false; if (ttl > 0) { timer.Stop(); @@ -125,7 +135,7 @@ struct GeneratorTask { pc.services().get().endOfStream(); pc.services().get().readyToQuit(QuitRequest::Me); - // write out data to disc if asked + // write out data to disk if asked if (outfile.get()) { outtree->SetEntries(eventCounter); outtree->Write(); diff --git a/run/o2sim_mctracks_to_aod.cxx b/run/o2sim_mctracks_to_aod.cxx index f7a85e62a3f9b..124e8aa7b3e42 100644 --- a/run/o2sim_mctracks_to_aod.cxx +++ b/run/o2sim_mctracks_to_aod.cxx @@ -94,9 +94,8 @@ struct MctracksToAod { // TODO: include BC simulation auto bcCounter = 0UL; size_t offset = 0; + LOG(debug) << "--- Loop over " << nParts << " parts ---"; for (auto i = 0U; i < nParts; ++i) { - LOG(debug) << "--- Loop over " << nParts << " parts ---"; - auto record = mSampler.generateCollisionTime(); auto header = pc.inputs().get("mcheader", i); auto tracks = pc.inputs().get("mctracks", i); From 03279d0607888602f7157dee8a2d05b4ac5af1b8 Mon Sep 17 00:00:00 2001 From: shahoian Date: Tue, 24 Feb 2026 08:39:51 +0100 Subject: [PATCH 297/701] Fix missing accumulate of covmatrix in fall-back case --- Common/DCAFitter/include/DCAFitter/DCAFitterN.h | 1 + 1 file changed, 1 insertion(+) diff --git a/Common/DCAFitter/include/DCAFitter/DCAFitterN.h b/Common/DCAFitter/include/DCAFitter/DCAFitterN.h index 1adf7a9ae7329..2641dec84aed9 100644 --- a/Common/DCAFitter/include/DCAFitter/DCAFitterN.h +++ b/Common/DCAFitter/include/DCAFitter/DCAFitterN.h @@ -818,6 +818,7 @@ GPUd() o2::math_utils::SMatrix Date: Mon, 23 Feb 2026 17:37:07 +0100 Subject: [PATCH 298/701] Workaround for HepMC3 bug --- Generators/src/GeneratorHepMC.cxx | 42 +++++++++++++++++++++++++------ 1 file changed, 35 insertions(+), 7 deletions(-) diff --git a/Generators/src/GeneratorHepMC.cxx b/Generators/src/GeneratorHepMC.cxx index 371e0cf1acce1..180a088c02a92 100644 --- a/Generators/src/GeneratorHepMC.cxx +++ b/Generators/src/GeneratorHepMC.cxx @@ -27,6 +27,7 @@ #include #include "FairPrimaryGenerator.h" #include +#include namespace o2 { @@ -420,6 +421,34 @@ void GeneratorHepMC::updateHeader(o2::dataformats::MCEventHeader* eventHeader) auto pdfInfo = mEvent->pdf_info(); auto hiInfo = mEvent->heavy_ion(); + // Workaround for a bug in HepMC3 (3.3.1 on 23/02/2026): GenHeavyIon::from_string() for the "v0" + // format skips reading user_cent_estimate, but to_string() always writes it. + // This shifts all subsequent fields by one, causing a istringstream failure and and heavy_ion() + // to return null even when the attribute is present and well-formed. + // For now we use this manual parser in case the infos are available + if (!hiInfo) { + auto attStr = mEvent->attribute_as_string("GenHeavyIon"); + if (!attStr.empty() && attStr[0] == 'v') { + std::istringstream is(attStr); + std::string version; + is >> version; + if (version == "v0") { + auto hi = std::make_shared(); + double spectNeutrons, spectProtons, eccentricity, userCentEst; + is >> hi->Ncoll_hard >> hi->Npart_proj >> hi->Npart_targ >> hi->Ncoll >> spectNeutrons >> spectProtons // deprecated v0 fields + >> hi->N_Nwounded_collisions >> hi->Nwounded_N_collisions >> hi->Nwounded_Nwounded_collisions >> hi->impact_parameter >> hi->event_plane_angle >> eccentricity // deprecated v0 field + >> hi->sigma_inel_NN >> hi->centrality >> userCentEst // GenHeavyIon::to_string always writes this, but GenHeavyIon::from_string skips it for v0 (HepMC3 bug to fix) + >> hi->Nspec_proj_n >> hi->Nspec_targ_n >> hi->Nspec_proj_p >> hi->Nspec_targ_p; + if (!is.fail()) { + LOG(debug) << "GenHeavyIon: using manual v0 parser (workaround for HepMC3 from_string bug)"; + hiInfo = hi; + } else { + LOG(warn) << "GenHeavyIon: manual v0 parser also failed on: [" << attStr << "]"; + } + } + } + } + // Set default cross-section if (xSection) { eventHeader->putInfo(Key::xSection, xSection->xsec()); @@ -457,8 +486,9 @@ void GeneratorHepMC::updateHeader(o2::dataformats::MCEventHeader* eventHeader) // Set heavy-ion information if (hiInfo) { - eventHeader->putInfo(Key::impactParameter, - hiInfo->impact_parameter); + eventHeader->SetB(hiInfo->impact_parameter); // sets the impact parameter to the FairMCEventHeader field for quick access in the AO2D + eventHeader->putInfo(Key::impactParameter, + hiInfo->impact_parameter); eventHeader->putInfo(Key::nPart, hiInfo->Npart_proj + hiInfo->Npart_targ); eventHeader->putInfo(Key::nPartProjectile, hiInfo->Npart_proj); @@ -471,11 +501,9 @@ void GeneratorHepMC::updateHeader(o2::dataformats::MCEventHeader* eventHeader) hiInfo->Nwounded_N_collisions); eventHeader->putInfo(Key::nCollNWoundedNwounded, hiInfo->Nwounded_Nwounded_collisions); - eventHeader->putInfo(Key::planeAngle, - hiInfo->event_plane_angle); - eventHeader->putInfo(Key::sigmaInelNN, - hiInfo->sigma_inel_NN); - eventHeader->putInfo(Key::centrality, hiInfo->centrality); + eventHeader->putInfo(Key::planeAngle, hiInfo->event_plane_angle); + eventHeader->putInfo(Key::sigmaInelNN, hiInfo->sigma_inel_NN); + eventHeader->putInfo(Key::centrality, hiInfo->centrality); eventHeader->putInfo(Key::nSpecProjectileProton, hiInfo->Nspec_proj_p); eventHeader->putInfo(Key::nSpecProjectileNeutron, hiInfo->Nspec_proj_n); eventHeader->putInfo(Key::nSpecTargetProton, hiInfo->Nspec_targ_p); From 96fafb9e89326b76b1ab86eef6301d59c1e62dba Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Mon, 23 Feb 2026 17:34:44 +0100 Subject: [PATCH 299/701] Update MC header when using event pool generator --- Generators/include/Generators/GeneratorFromFile.h | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Generators/include/Generators/GeneratorFromFile.h b/Generators/include/Generators/GeneratorFromFile.h index 3b469751a4d47..706557ea2484b 100644 --- a/Generators/include/Generators/GeneratorFromFile.h +++ b/Generators/include/Generators/GeneratorFromFile.h @@ -87,11 +87,10 @@ class GeneratorFromO2Kine : public o2::eventgen::Generator void SetStartEvent(int start); void setContinueMode(bool val) { mContinueMode = val; }; - - private: /** methods that can be overridden **/ void updateHeader(o2::dataformats::MCEventHeader* eventHeader) override; + private: TFile* mEventFile = nullptr; //! the file containing the persistent events TBranch* mEventBranch = nullptr; //! the branch containing the persistent events TBranch* mMCHeaderBranch = nullptr; //! branch containing MC event headers @@ -143,6 +142,11 @@ class GeneratorFromEventPool : public o2::eventgen::Generator return import_good; } + void updateHeader(o2::dataformats::MCEventHeader* eventHeader) override + { + mO2KineGenerator->updateHeader(eventHeader); + } + // determine the collection of available files std::vector setupFileUniverse(std::string const& path) const; From 60cea723ef5288c10973f41f4d5fc59e51a7a874 Mon Sep 17 00:00:00 2001 From: glegras <71757105+glegras@users.noreply.github.com> Date: Tue, 24 Feb 2026 19:05:04 +0100 Subject: [PATCH 300/701] TRD: updates in vdrift and ExB calibration + possibility to use slope in chi2 matching (#14989) * updates vd exb calib * TRD: updates in vdrift and ExB calibration + possibility to use slope in chi2 matching * small fix * use lorentz angle to define better fit range * merge with with new error parametrization * clang format * simplify error parametrization * clang format * calculate average only once * clang format --- .../TRD/include/DataFormatsTRD/CalGain.h | 44 ++++++++- .../TRD/include/DataFormatsTRD/CalVdriftExB.h | 94 ++++++++++++++++++- .../TRD/include/DataFormatsTRD/Constants.h | 4 + .../TRD/base/src/TrackletTransformer.cxx | 3 +- Detectors/TRD/calibration/README.md | 6 +- .../TRDCalibration/CalibrationParams.h | 7 +- .../include/TRDCalibration/CalibratorVdExB.h | 4 +- .../TRD/calibration/macros/manualCalibFit.C | 67 +++++++++++-- .../TRD/calibration/src/CalibratorVdExB.cxx | 48 ++++++++-- .../TRD/calibration/src/TrackBasedCalib.cxx | 20 +++- .../include/TRDWorkflow/VdAndExBCalibSpec.h | 2 +- GPU/GPUTracking/DataTypes/GPUTRDRecoParam.cxx | 53 +++-------- GPU/GPUTracking/DataTypes/GPUTRDRecoParam.h | 20 ++-- GPU/GPUTracking/Definitions/GPUSettingsList.h | 1 + .../TRDTracking/GPUTRDInterfaces.h | 1 + GPU/GPUTracking/TRDTracking/GPUTRDTracker.cxx | 87 ++++++++++++++++- GPU/GPUTracking/TRDTracking/GPUTRDTracker.h | 6 +- 17 files changed, 376 insertions(+), 91 deletions(-) diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalGain.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalGain.h index f90101e7a4f21..b4e64db094a5c 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalGain.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalGain.h @@ -33,12 +33,52 @@ class CalGain void setMPVdEdx(int iDet, float mpv) { mMPVdEdx[iDet] = mpv; } - float getMPVdEdx(int iDet) const { return mMPVdEdx[iDet]; } + float getMPVdEdx(int iDet, bool defaultAvg = true) const + { + // if defaultAvg = false, we take the value stored whatever it is + // if defaultAvg = true and we have default value or bad value stored, we take the average on all chambers instead + if (!defaultAvg || isGoodGain(iDet)) + return mMPVdEdx[iDet]; + else { + if (TMath::Abs(mMeanGain + 999.) < 1e-6) + mMeanGain = getAverageGain(); + return mMeanGain; + } + } + + float getAverageGain() const + { + float averageGain = 0.; + int ngood = 0; + + for (int iDet = 0; iDet < constants::MAXCHAMBER; iDet++) { + if (isGoodGain(iDet)) { + // The chamber has correct calibration + ngood++; + averageGain += mMPVdEdx[iDet]; + } + } + if (ngood == 0) { + // we should make sure it never happens + return constants::MPVDEDXDEFAULT; + } + averageGain /= ngood; + return averageGain; + } + + bool isGoodGain(int iDet) const + { + if (TMath::Abs(mMPVdEdx[iDet] - constants::MPVDEDXDEFAULT) > 1e-6) + return true; + else + return false; + } private: std::array mMPVdEdx{}; ///< Most probable value of dEdx distribution per TRD chamber + mutable float mMeanGain{-999.}; ///! average gain, calculated only once - ClassDefNV(CalGain, 1); + ClassDefNV(CalGain, 2); }; } // namespace trd diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalVdriftExB.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalVdriftExB.h index bad9dcfef4e37..65981e928fb39 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalVdriftExB.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalVdriftExB.h @@ -34,14 +34,102 @@ class CalVdriftExB void setVdrift(int iDet, float vd) { mVdrift[iDet] = vd; } void setExB(int iDet, float exb) { mExB[iDet] = exb; } - float getVdrift(int iDet) const { return mVdrift[iDet]; } - float getExB(int iDet) const { return mExB[iDet]; } + float getVdrift(int iDet, bool defaultAvg = true) const + { + // if defaultAvg = false, we take the value stored whatever it is + // if defaultAvg = true and we have default value or bad value stored, we take the average on all chambers instead + if (!defaultAvg || (isGoodExB(iDet) && isGoodVdrift(iDet))) + return mVdrift[iDet]; + else { + if (TMath::Abs(mMeanVdrift + 999.) < 1e-6) + mMeanVdrift = getAverageVdrift(); + return mMeanVdrift; + } + } + float getExB(int iDet, bool defaultAvg = true) const + { + if (!defaultAvg || (isGoodExB(iDet) && isGoodVdrift(iDet))) + return mExB[iDet]; + else { + if (TMath::Abs(mMeanExB + 999.) < 1e-6) + mMeanExB = getAverageExB(); + return mMeanExB; + } + } + + float getAverageVdrift() const + { + float averageVdrift = 0.; + int ngood = 0; + + for (int iDet = 0; iDet < constants::MAXCHAMBER; iDet++) { + if (isGoodExB(iDet) && isGoodVdrift(iDet)) { + // Both values need to be correct to declare a chamber as well calibrated + ngood++; + averageVdrift += mVdrift[iDet]; + } + } + if (ngood == 0) { + // we should make sure it never happens + return constants::VDRIFTDEFAULT; + } + averageVdrift /= ngood; + return averageVdrift; + } + + float getAverageExB() const + { + float averageExB = 0.; + int ngood = 0; + + for (int iDet = 0; iDet < constants::MAXCHAMBER; iDet++) { + if (isGoodExB(iDet) && isGoodVdrift(iDet)) { + // Both values need to be correct to declare a chamber as well calibrated + ngood++; + averageExB += mExB[iDet]; + } + } + if (ngood == 0) { + // we should make sure it never happens + return constants::EXBDEFAULT; + } + averageExB /= ngood; + return averageExB; + } + + bool isGoodExB(int iDet) const + { + // check if value is well calibrated or not + // default calibration if not enough entries + // close to boundaries indicate a failed fit + if (TMath::Abs(mExB[iDet] - constants::EXBDEFAULT) > 1e-6 && + TMath::Abs(mExB[iDet] - constants::EXBMIN) > 0.01 && + TMath::Abs(mExB[iDet] - constants::EXBMAX) > 0.01) + return true; + else + return false; + } + + bool isGoodVdrift(int iDet) const + { + // check if value is well calibrated or not + // default calibration if not enough entries + // close to boundaries indicate a failed fit + if (TMath::Abs(mVdrift[iDet] - constants::VDRIFTDEFAULT) > 1e-6 && + TMath::Abs(mVdrift[iDet] - constants::VDRIFTMIN) > 0.1 && + TMath::Abs(mVdrift[iDet] - constants::VDRIFTMAX) > 0.1) + return true; + else + return false; + } private: std::array mVdrift{}; ///< calibrated drift velocity per TRD chamber std::array mExB{}; ///< calibrated Lorentz angle per TRD chamber + mutable float mMeanVdrift{-999.}; ///! average drift velocity, calculated only once + mutable float mMeanExB{-999.}; ///! average lorentz angle, calculated only once - ClassDefNV(CalVdriftExB, 1); + ClassDefNV(CalVdriftExB, 2); }; } // namespace trd diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/Constants.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/Constants.h index 7a650cf3699cf..9a4da1024e251 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/Constants.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/Constants.h @@ -75,7 +75,11 @@ constexpr int TIMEBINS = 30; ///< the number of time bins constexpr float MAXIMPACTANGLE = 25.f; ///< the maximum impact angle for tracks relative to the TRD detector plane to be considered for vDrift and ExB calibration constexpr int NBINSANGLEDIFF = 25; ///< the number of bins for the track angle used for the vDrift and ExB calibration based on the tracking constexpr double VDRIFTDEFAULT = 1.546; ///< default value for vDrift +constexpr double VDRIFTMIN = 0.4; ///< min value for vDrift +constexpr double VDRIFTMAX = 2.0; ///< max value for vDrift constexpr double EXBDEFAULT = 0.0; ///< default value for LorentzAngle +constexpr double EXBMIN = -0.4; ///< min value for LorentzAngle +constexpr double EXBMAX = 0.4; ///< max value for LorentzAngle constexpr int NBINSGAINCALIB = 320; ///< number of bins in the charge (Q0+Q1+Q2) histogram for gain calibration constexpr float MPVDEDXDEFAULT = 42.; ///< default Most Probable Value of TRD dEdx constexpr float T0DEFAULT = 1.2; ///< default value for t0 diff --git a/Detectors/TRD/base/src/TrackletTransformer.cxx b/Detectors/TRD/base/src/TrackletTransformer.cxx index 58938cfd99161..7f9976b8ce714 100644 --- a/Detectors/TRD/base/src/TrackletTransformer.cxx +++ b/Detectors/TRD/base/src/TrackletTransformer.cxx @@ -50,7 +50,8 @@ float TrackletTransformer::calculateDy(int detector, int slope, const PadPlane* // NOTE: check what drift height is used in calibration code to ensure consistency // NOTE: check sign convention of Lorentz angle // NOTE: confirm the direction in which vDrift is measured/determined. Is it in x or in direction of drift? - double lorentzCorrection = TMath::Tan(exb) * mXAnode; + // The Lorentz correction have to be applied both at the point of entrance and at the end of the drift region + double lorentzCorrection = TMath::Tan(exb) * mGeo->cdrHght(); // assuming angle in Bailhache, fig. 4.17 would be positive in our calibration code double calibratedDy = rawDy - lorentzCorrection; diff --git a/Detectors/TRD/calibration/README.md b/Detectors/TRD/calibration/README.md index bdbfc9e709800..f769fa99b5778 100644 --- a/Detectors/TRD/calibration/README.md +++ b/Detectors/TRD/calibration/README.md @@ -27,15 +27,15 @@ For 'o2-calibration-trd-workflow --vDriftAndExB' there are also the following ke *Hint: You can get information on the meaning of the parameters by running `o2-calibration-trd-workflow --vDriftAndExB -b --help full`* -If you want to run the calibration from a local file with residuals, trdangreshistos.root, you can run: +If you want to run the calibration from a local file with residuals, trdcaliboutput.root, you can run: - o2-calibration-trd-workflow --vDriftAndExB -b --enable-root-input --calib-vdexb-calibration '--tf-per-slot 1' --configKeyValues "TRDCalibParams.minEntriesChamber=100;TRDCalibParams.minEntriesTotal=50000" + o2-calibration-trd-workflow --vDriftAndExB -b --enable-root-input --calib-vdexb-calibration '--tf-per-slot 1' --configKeyValues "TRDCalibParams.minEntriesChamber=100;TRDCalibParams.minEntriesTotal=50000" --trd-calib-infile trdcaliboutput.root Additionally it is possible to perform the calibrations fit manually per chamber if you have TPC-TRD or ITS-TPC-TRD tracks, you can run: o2-trd-global-tracking -b --enable-trackbased-calib -This produces `trdangreshistos.root` which holds the residuals of the angles and differences. +This produces `trdcaliboutput.root` which holds the residuals of the angles and differences. Then run the macro `Detectors/TRD/calibration/macros/manualCalibFit.C`. This produces a file of similar name with the fitted data and prints out the fit results. This is equivalent to running: diff --git a/Detectors/TRD/calibration/include/TRDCalibration/CalibrationParams.h b/Detectors/TRD/calibration/include/TRDCalibration/CalibrationParams.h index 677673a7f85f3..cadd21af2a55d 100644 --- a/Detectors/TRD/calibration/include/TRDCalibration/CalibrationParams.h +++ b/Detectors/TRD/calibration/include/TRDCalibration/CalibrationParams.h @@ -25,12 +25,13 @@ namespace trd /// VDrift and ExB calibration parameters. struct TRDCalibParams : public o2::conf::ConfigurableParamHelper { unsigned int nTrackletsMin = 5; ///< minimum amount of tracklets + unsigned int nTrackletsMinLoose = 4; ///< minimum amount of tracklets if two layers with a large lever arm both have a hit unsigned int chi2RedMax = 6; ///< maximum reduced chi2 acceptable for track quality - size_t minEntriesChamber = 75; ///< minimum number of entries per chamber to fit single time slot - size_t minEntriesTotal = 40'500; ///< minimum total required for meaningful fits + size_t minEntriesChamber = 200; ///< minimum number of entries per chamber to fit single time slot + size_t minEntriesTotal = 400'000; ///< minimum total required for meaningful fits // For gain calibration - unsigned int nTrackletsMinGainCalib = 5; + unsigned int nTrackletsMinGainCalib = 3; size_t minEntriesChamberGainCalib = 500; ///< minimum number of entries per chamber to fit single time slot size_t minEntriesTotalGainCalib = 1'000'000; ///< minimum total required for meaningful fits // Cuts for selecting clean pion candidates for gain calibration diff --git a/Detectors/TRD/calibration/include/TRDCalibration/CalibratorVdExB.h b/Detectors/TRD/calibration/include/TRDCalibration/CalibratorVdExB.h index 7d55850af9fd4..16223f3e78112 100644 --- a/Detectors/TRD/calibration/include/TRDCalibration/CalibratorVdExB.h +++ b/Detectors/TRD/calibration/include/TRDCalibration/CalibratorVdExB.h @@ -88,8 +88,8 @@ class CalibratorVdExB final : public o2::calibration::TimeSlotCalibration mOutFile{nullptr}; ///< output file std::unique_ptr mOutTree{nullptr}; ///< output tree diff --git a/Detectors/TRD/calibration/macros/manualCalibFit.C b/Detectors/TRD/calibration/macros/manualCalibFit.C index d31744a2e727c..877202d4e211e 100644 --- a/Detectors/TRD/calibration/macros/manualCalibFit.C +++ b/Detectors/TRD/calibration/macros/manualCalibFit.C @@ -30,18 +30,19 @@ // O2 header #include +#include "DetectorsBase/Propagator.h" #endif // This root macro reads in 'trdangreshistos.root' and // performs the calibration fits manually as in CalibratorVdExB.cxx // This can be used for checking if the calibration fits make sense. -void manualCalibFit() +void manualCalibFit(int runNumber = 563335, bool usePreCorrFromCCDB = false) { //---------------------------------------------------- // TTree and File //---------------------------------------------------- - std::unique_ptr inFilePtr(TFile::Open("trdangreshistos.root")); + std::unique_ptr inFilePtr(TFile::Open("trdcaliboutput.root")); if (inFilePtr == nullptr) { printf("Input File could not be read!\n'"); return; @@ -60,18 +61,46 @@ void manualCalibFit() tree->SetBranchAddress("mHistogramEntries[13500]", &mHistogramEntries); tree->SetBranchAddress("mNEntriesPerBin[13500]", &mNEntriesPerBin); + // use precorr values from ccdb + // necessary when the angular residuals were calculated already using ccdb calibration (e.g. in a local run) + + o2::trd::CalVdriftExB* calObject; + if (usePreCorrFromCCDB) { + auto& ccdbmgr = o2::ccdb::BasicCCDBManager::instance(); + + o2::ccdb::CcdbApi ccdb; + ccdb.init("http://alice-ccdb.cern.ch"); + auto runDuration = ccdbmgr.getRunDuration(runNumber); + + std::map metadata; + std::map headers; + + calObject = ccdb.retrieveFromTFileAny("TRD/Calib/CalVdriftExB", metadata, runDuration.first + 60000, &headers, "", "", "1689478811721"); + } + //---------------------------------------------------- // Configure Fitter //---------------------------------------------------- o2::trd::FitFunctor mFitFunctor; std::array, 540> profiles; ///< profile histograms for each TRD chamber + int counter = 0; for (int iDet = 0; iDet < 540; ++iDet) { mFitFunctor.profiles[iDet] = std::make_unique(Form("profAngleDiff_%i", iDet), Form("profAngleDiff_%i", iDet), 25, -25.f, 25.f); + if (usePreCorrFromCCDB) { + if (calObject->isGoodExB(iDet)) + counter++; + mFitFunctor.vdPreCorr[iDet] = calObject->getVdrift(iDet, true); + mFitFunctor.laPreCorr[iDet] = calObject->getExB(iDet, true); + } } + std::cout << counter << " good entries in the CCDB " << std::endl; + mFitFunctor.mAnodePlane = 3.35; // don't really care as long as it's not zero, this parameter could be removed mFitFunctor.lowerBoundAngleFit = 80 * TMath::DegToRad(); mFitFunctor.upperBoundAngleFit = 100 * TMath::DegToRad(); - mFitFunctor.vdPreCorr.fill(1.546); - mFitFunctor.laPreCorr.fill(0.0); + if (!usePreCorrFromCCDB) { + mFitFunctor.vdPreCorr.fill(1.546); + mFitFunctor.laPreCorr.fill(0.0); + } //---------------------------------------------------- // Loop @@ -88,15 +117,18 @@ void manualCalibFit() //---------------------------------------------------- // Fill profiles //---------------------------------------------------- + int nEntriesDetTotal[540] = {}; for (int iDet = 0; iDet < 540; ++iDet) { for (int iBin = 0; iBin < 25; ++iBin) { auto angleDiffSum = mHistogramEntriesSum[iDet * 25 + iBin]; auto nEntries = mNEntriesPerBinSum[iDet * 25 + iBin]; + nEntriesDetTotal[iDet] += nEntries; if (nEntries > 0) { // skip entries which have no entries; ? // add to the respective profile for fitting later on mFitFunctor.profiles[iDet]->Fill(2 * iBin - 25.f, angleDiffSum / nEntries, nEntries); } } + printf("Det %d: nEntries=%d \n", iDet, nEntriesDetTotal[iDet]); } //---------------------------------------------------- @@ -105,16 +137,23 @@ void manualCalibFit() printf("-------- Started fits\n"); std::array laFitResults{}; std::array vdFitResults{}; + + TH1F* hVd = new TH1F("hVd", "v drift", 150, 0.5, 2.); + TH1F* hLa = new TH1F("hLa", "lorentz angle", 200, -25., 25.); + o2::trd::CalVdriftExB* calObjectOut = new o2::trd::CalVdriftExB(); + for (int iDet = 0; iDet < 540; ++iDet) { + if (nEntriesDetTotal[iDet] < 75) + continue; mFitFunctor.currDet = iDet; ROOT::Fit::Fitter fitter; double paramsStart[2]; - paramsStart[0] = 0. * TMath::DegToRad(); + paramsStart[0] = 0.; paramsStart[1] = 1.; fitter.SetFCN(2, mFitFunctor, paramsStart); fitter.Config().ParSettings(0).SetLimits(-0.7, 0.7); fitter.Config().ParSettings(0).SetStepSize(.01); - fitter.Config().ParSettings(1).SetLimits(0., 3.); + fitter.Config().ParSettings(1).SetLimits(0.01, 3.); fitter.Config().ParSettings(1).SetStepSize(.01); ROOT::Math::MinimizerOptions opt; opt.SetMinimizerType("Minuit2"); @@ -127,14 +166,28 @@ void manualCalibFit() auto fitResult = fitter.Result(); laFitResults[iDet] = fitResult.Parameter(0); vdFitResults[iDet] = fitResult.Parameter(1); - printf("Det %d: la=%f\tvd=%f\n", iDet, laFitResults[iDet] * TMath::RadToDeg(), vdFitResults[iDet]); + if (fitResult.MinFcnValue() > 0.03) + continue; + printf("Det %d: la=%.3f \tvd=%.3f \t100*minValue=%f \tentries=%d\n", iDet, laFitResults[iDet] * TMath::RadToDeg(), vdFitResults[iDet], 100 * fitResult.MinFcnValue(), nEntriesDetTotal[iDet]); + hVd->Fill(vdFitResults[iDet]); + hLa->Fill(laFitResults[iDet] * TMath::RadToDeg()); + calObjectOut->setVdrift(iDet, vdFitResults[iDet]); + calObjectOut->setExB(iDet, laFitResults[iDet]); } printf("-------- Finished fits\n"); + std::cout << "number of chambers with enough entries: " << hVd->GetEntries() << std::endl; + ; + std::cout << "vdrift mean: " << hVd->GetMean() << " sigma: " << hVd->GetStdDev() << std::endl; + std::cout << "lorentz angle mean: " << hLa->GetMean() << " sigma: " << hLa->GetStdDev() << std::endl; + //---------------------------------------------------- // Write //---------------------------------------------------- std::unique_ptr outFilePtr(TFile::Open("manualCalibFit.root", "RECREATE")); + hVd->Write(); + hLa->Write(); + outFilePtr->WriteObjectAny(calObjectOut, "o2::trd::CalVdriftExB", "calObject"); for (auto& p : mFitFunctor.profiles) p->Write(); } diff --git a/Detectors/TRD/calibration/src/CalibratorVdExB.cxx b/Detectors/TRD/calibration/src/CalibratorVdExB.cxx index 64a8664640e41..fef7bdecef38c 100644 --- a/Detectors/TRD/calibration/src/CalibratorVdExB.cxx +++ b/Detectors/TRD/calibration/src/CalibratorVdExB.cxx @@ -24,6 +24,7 @@ #include "CCDB/BasicCCDBManager.h" #include "CommonUtils/NameConf.h" #include "CommonUtils/MemFileHelper.h" +#include "DetectorsBase/Propagator.h" #include #include @@ -105,17 +106,37 @@ void CalibratorVdExB::initProcessing() return; } - mFitFunctor.lowerBoundAngleFit = 80 * TMath::DegToRad(); - mFitFunctor.upperBoundAngleFit = 100 * TMath::DegToRad(); + // fit is done in region where ion tails are small, close to lorentz angle + // we want an approximate value of the lorentz angle in order to define better fit boundaries, coinciding with profile bin edges + float bz = o2::base::Propagator::Instance()->getNominalBz(); + // default angle with zero field is slightly shifted + float lorentzAngleAvg = -1.f; + if (TMath::Abs(bz - 2) < 0.1f) { + lorentzAngleAvg = 3.f; + } + if (TMath::Abs(bz + 2) < 0.1f) { + lorentzAngleAvg = -5.f; + } + if (TMath::Abs(bz - 5) < 0.1f) { + lorentzAngleAvg = 7.f; + } + if (TMath::Abs(bz + 5) < 0.1f) { + lorentzAngleAvg = -9.f; + } + + LOGP(info, "b field: {} lorentz angle start: {}", bz, lorentzAngleAvg); + + mFitFunctor.lowerBoundAngleFit = (80 + lorentzAngleAvg) * TMath::DegToRad(); + mFitFunctor.upperBoundAngleFit = (100 + lorentzAngleAvg) * TMath::DegToRad(); mFitFunctor.mAnodePlane = GeometryBase::camHght() / (2.f * 100.f); for (int iDet = 0; iDet < MAXCHAMBER; ++iDet) { mFitFunctor.profiles[iDet] = std::make_unique(Form("profAngleDiff_%i", iDet), Form("profAngleDiff_%i", iDet), NBINSANGLEDIFF, -MAXIMPACTANGLE, MAXIMPACTANGLE); } mFitter.SetFCN(2, mFitFunctor, mParamsStart); - mFitter.Config().ParSettings(ParamIndex::LA).SetLimits(-0.7, 0.7); + mFitter.Config().ParSettings(ParamIndex::LA).SetLimits(constants::EXBMIN, constants::EXBMAX); mFitter.Config().ParSettings(ParamIndex::LA).SetStepSize(.01); - mFitter.Config().ParSettings(ParamIndex::VD).SetLimits(0.01, 3.); + mFitter.Config().ParSettings(ParamIndex::VD).SetLimits(constants::VDRIFTMIN, constants::VDRIFTMAX); mFitter.Config().ParSettings(ParamIndex::VD).SetStepSize(.01); ROOT::Math::MinimizerOptions opt; opt.SetMinimizerType("Minuit2"); @@ -184,17 +205,30 @@ void CalibratorVdExB::finalizeSlot(Slot& slot) } // Check if we have the minimum amount of entries if (sumEntries < mMinEntriesChamber) { - LOGF(debug, "Chamber %d did not reach minimum amount of entries for refit", iDet); + LOGF(debug, "Chamber %d did not reach minimum amount of entries for refit: %d", iDet, sumEntries); continue; } + float laPreCorrTemp = mFitFunctor.laPreCorr[iDet]; + float vdPreCorrTemp = mFitFunctor.vdPreCorr[iDet]; + // Here we start from uncalibrated values, otherwise online calibration does not work properly + mFitFunctor.laPreCorr[iDet] = EXBDEFAULT; + mFitFunctor.vdPreCorr[iDet] = VDRIFTDEFAULT; + // Reset Start Parameter mParamsStart[ParamIndex::LA] = 0.0; mParamsStart[ParamIndex::VD] = 1.0; mFitter.FitFCN(); auto fitResult = mFitter.Result(); + if (fitResult.MinFcnValue() > 0.03) { + LOGF(debug, "Chamber %d fit did not converge properly, minimization value too high: %f", iDet, fitResult.MinFcnValue()); + // The fit did not work properly, so we keep previous values + mFitFunctor.laPreCorr[iDet] = laPreCorrTemp; + mFitFunctor.vdPreCorr[iDet] = vdPreCorrTemp; + continue; + } laFitResults[iDet] = fitResult.Parameter(ParamIndex::LA); vdFitResults[iDet] = fitResult.Parameter(ParamIndex::VD); - LOGF(debug, "Fit result for chamber %i: vd=%f, la=%f", iDet, vdFitResults[iDet], laFitResults[iDet] * TMath::RadToDeg()); + LOGF(debug, "Fit result for chamber %i: vd=%f, la=%f, minimizer value=%f", iDet, vdFitResults[iDet], laFitResults[iDet] * TMath::RadToDeg(), fitResult.MinFcnValue()); // Update fit values for next fit mFitFunctor.laPreCorr[iDet] = laFitResults[iDet]; mFitFunctor.vdPreCorr[iDet] = vdFitResults[iDet]; @@ -222,7 +256,7 @@ void CalibratorVdExB::finalizeSlot(Slot& slot) auto flName = o2::ccdb::CcdbApi::generateFileName(clName); std::map metadata; // TODO: do we want to store any meta data? long startValidity = slot.getStartTimeMS() - 10 * o2::ccdb::CcdbObjectInfo::SECOND; - mInfoVector.emplace_back("TRD/Calib/CalVdriftExB", clName, flName, metadata, startValidity, startValidity + o2::ccdb::CcdbObjectInfo::HOUR); + mInfoVector.emplace_back("TRD/Calib/CalVdriftExB", clName, flName, metadata, startValidity, startValidity + 1 * o2::ccdb::CcdbObjectInfo::HOUR); mObjectVector.push_back(calObject); } diff --git a/Detectors/TRD/calibration/src/TrackBasedCalib.cxx b/Detectors/TRD/calibration/src/TrackBasedCalib.cxx index 8fe195f861389..ae1f7b33c6bba 100644 --- a/Detectors/TRD/calibration/src/TrackBasedCalib.cxx +++ b/Detectors/TRD/calibration/src/TrackBasedCalib.cxx @@ -190,7 +190,10 @@ int TrackBasedCalib::doTrdOnlyTrackFits(gsl::span& tracks) for (const auto& trkIn : tracks) { if (trkIn.getNtracklets() < params.nTrackletsMin) { // with less than 3 tracklets the TRD-only refit not meaningful - continue; + if (trkIn.getNtracklets() < params.nTrackletsMinLoose || !((trkIn.getTrackletIndex(0) >= 0 && (trkIn.getTrackletIndex(NLAYER - 1) >= 0 || trkIn.getTrackletIndex(NLAYER - 2) >= 0))) || (trkIn.getTrackletIndex(1) >= 0 && trkIn.getTrackletIndex(NLAYER - 1) >= 0)) { + // we check if we have enough lever arm, i.e. (first and last) or (second and last) or (first and before last) are present + continue; + } } auto trkWork = trkIn; // input is const, so we need to create a copy bool trackFailed = false; @@ -262,9 +265,20 @@ int TrackBasedCalib::doTrdOnlyTrackFits(gsl::span& tracks) } float trkAngle = o2::math_utils::asin(trkWork.getSnp()) * TMath::RadToDeg(); - float trkltAngle = o2::math_utils::atan(mTrackletsCalib[trkWork.getTrackletIndex(iLayer)].getDy() / Geometry::cdrHght()) * TMath::RadToDeg(); + int trkltId = trkWork.getTrackletIndex(iLayer); + // tracklet angle, corrected for pad tilt + const PadPlane* pad = Geometry::instance()->getPadPlane(mTrackletsRaw[trkltId].getDetector()); + float tilt = tan(TMath::DegToRad() * pad->getTiltingAngle()); // tilt is signed! and returned in degrees + float tiltCorrUp = tilt * trkWork.getTgl() * Geometry::cdrHght(); + float padLength = pad->getRowSize(mTrackletsRaw[trkltId].getPadRow()); + if (!((trkWork.getSigmaZ2() < (padLength * padLength / 12.f)) && (std::fabs(mTrackletsCalib[trkltId].getZ() - trkWork.getZ()) < padLength))) { + tiltCorrUp = 0.f; + } + // use uncalibrated dy because online calibration does not work otherwise + float trkltDy = mTrackletsRaw[trkltId].getUncalibratedDy(30.f / o2::trd::constants::VDRIFTDEFAULT) + tiltCorrUp; + float trkltAngle = o2::math_utils::atan(trkltDy / Geometry::cdrHght()) * TMath::RadToDeg(); float angleDeviation = trkltAngle - trkAngle; - if (mAngResHistos.addEntry(angleDeviation, trkAngle, mTrackletsRaw[trkWork.getTrackletIndex(iLayer)].getDetector())) { + if (mAngResHistos.addEntry(angleDeviation, trkAngle, mTrackletsRaw[trkltId].getDetector())) { // track impact angle out of histogram range continue; } diff --git a/Detectors/TRD/workflow/include/TRDWorkflow/VdAndExBCalibSpec.h b/Detectors/TRD/workflow/include/TRDWorkflow/VdAndExBCalibSpec.h index f45b7a1808287..e4d6a1641ed50 100644 --- a/Detectors/TRD/workflow/include/TRDWorkflow/VdAndExBCalibSpec.h +++ b/Detectors/TRD/workflow/include/TRDWorkflow/VdAndExBCalibSpec.h @@ -147,7 +147,7 @@ DataProcessorSpec getTRDVdAndExBCalibSpec() auto ccdbRequest = std::make_shared(true, // orbitResetTime true, // GRPECS=true false, // GRPLHCIF - false, // GRPMagField + true, // GRPMagField false, // askMatLUT o2::base::GRPGeomRequest::None, // geometry inputs); diff --git a/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.cxx b/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.cxx index 70b445f7befc0..674b7a317b477 100644 --- a/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.cxx +++ b/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.cxx @@ -28,64 +28,33 @@ void GPUTRDRecoParam::init(float bz, const GPUSettingsRec* rec) 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); + + mRPhiA2 = resRPhiIdeal2; + mLorentzAngle = -0.02f + 0.13f * bz / 5.f; + + mDyA2 = 6e-3f; + mDyC2 = 0.3f; + mCorrYDyA = 0.27f; + mCorrYDyC = -0.44f; + + LOGP(info, "Loaded parameterizations for Bz={}: PhiRes:[{},{},{}] DyRes:[{},{},{}] CorrYDy:[{},{},{}]", + bz, mRPhiA2, mLorentzAngle, mRPhiC2, mDyA2, mLorentzAngle, mDyC2, mCorrYDyA, mLorentzAngle, mCorrYDyC); } void GPUTRDRecoParam::recalcTrkltCov(const float tilt, const float snp, const float rowSize, float* cov) const diff --git a/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.h b/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.h index ad0285487d3c3..a0a8e71143d94 100644 --- a/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.h +++ b/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.h @@ -19,6 +19,7 @@ #include "GPUCommonDef.h" #include "GPUCommonRtypes.h" #include "GPUCommonArray.h" +#include "GPUCommonMath.h" namespace o2 { @@ -51,31 +52,30 @@ class GPUTRDRecoParam /// 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) + GPUd() float getRPhiRes(float snp) const { return (mRPhiA2 + mRPhiC2 * (snp - mLorentzAngle) * (snp - mLorentzAngle)); } + GPUd() float getDyRes(float snp) const { return mDyA2 + mDyC2 * (snp - mLorentzAngle) * (snp - mLorentzAngle); } // a^2 + c^2 * (snp - b)^2 + GPUd() float convertAngleToDy(float snp) const { return 3.f * snp / CAMath::Sqrt(1 - snp * snp); } // when calibrated, sin(phi) = (dy / xDrift) / sqrt(1+(dy/xDrift)^2) works well + GPUd() float getCorrYDy(float snp) const { return mCorrYDyA + mCorrYDyC * (snp - mLorentzAngle) * (snp - mLorentzAngle); } // a + c * (snp - b)^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 + float mLorentzAngle{0.f}; // 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 + // correlation coefficient between y residual and dy residual + float mCorrYDyA{0.f}; + float mCorrYDyC{0.f}; float mZCorrCoefNRC{1.4f}; ///< tracklet z-position depends linearly on track dip angle - ClassDefNV(GPUTRDRecoParam, 2); + ClassDefNV(GPUTRDRecoParam, 3); }; } // namespace gpu diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index ea15ecde78c21..cf6b913551ab5 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -184,6 +184,7 @@ AddOptionRTC(extraRoadZ, float, 10.f, "", 0, "Addition to search road around tra AddOptionRTC(trkltResRPhiIdeal, float, 1.f, "", 0, "Optimal tracklet rphi resolution in cm (in case phi of track = lorentz angle)") AddOptionRTC(maxChi2Red, float, 99.f, "", 0, "maximum chi2 per attached tracklet for TRD tracks TODO: currently effectively disabled, requires tuning") AddOptionRTC(applyDeflectionCut, uint8_t, 0, "", 0, "Set to 1 to enable tracklet selection based on deflection") +AddOptionRTC(addDeflectionInChi2, uint8_t, 0, "", 0, "Set to 1 to add the deflection in the chi2 calculation for matching") AddOptionRTC(stopTrkAfterNMissLy, uint8_t, 6, "", 0, "Abandon track following after N layers without a TRD match") AddOptionRTC(nTrackletsMin, uint8_t, 3, "", 0, "Tracks with less attached tracklets are discarded after the tracking") AddOptionRTC(matCorrType, uint8_t, 2, "", 0, "Material correction to use: 0 - none, 1 - TGeo, 2 - matLUT") diff --git a/GPU/GPUTracking/TRDTracking/GPUTRDInterfaces.h b/GPU/GPUTracking/TRDTracking/GPUTRDInterfaces.h index d45959f99b056..a7b0166122c1b 100644 --- a/GPU/GPUTracking/TRDTracking/GPUTRDInterfaces.h +++ b/GPU/GPUTracking/TRDTracking/GPUTRDInterfaces.h @@ -151,6 +151,7 @@ class trackInterface : public GPUTPCGMTrackParam GPUd() float getPt() const { return CAMath::Abs(getQ2Pt()) > 0 ? CAMath::Abs(1.f / getQ2Pt()) : 99999.f; } GPUd() float getSigmaY2() const { return GetErr2Y(); } GPUd() float getSigmaZ2() const { return GetErr2Z(); } + GPUd() float getSigmaZY() const { return GetCov(1); } GPUd() const float* getPar() const { return GetPar(); } GPUd() const float* getCov() const { return GetCov(); } diff --git a/GPU/GPUTracking/TRDTracking/GPUTRDTracker.cxx b/GPU/GPUTracking/TRDTracking/GPUTRDTracker.cxx index d5d400e30df53..80098ff151ebe 100644 --- a/GPU/GPUTracking/TRDTracking/GPUTRDTracker.cxx +++ b/GPU/GPUTracking/TRDTracking/GPUTRDTracker.cxx @@ -93,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), mRecoParam(nullptr), 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), mTPCVdrift(2.58f), mTPCTDriftOffset(0.f), mDebug(new GPUTRDTrackerDebug()) { //-------------------------------------------------------------------- // Default constructor @@ -579,12 +579,14 @@ GPUd() bool GPUTRDTracker_t::FollowProlongation(PROP* prop, TRDTRK prop->getPropagatedYZ(spacePoints[trkltIdx].getX(), projY, projZ); // correction for tilted pads (only applied if deltaZ < lPad && track z err << lPad) float tiltCorr = tilt * (spacePoints[trkltIdx].getZ() - projZ); + float dyTiltCorr = tilt * trkWork->getTgl() * mGeo->GetCdrHght(); float lPad = pad->GetRowSize(tracklets[trkltIdx].GetZbin()); if (!((CAMath::Abs(spacePoints[trkltIdx].getZ() - projZ) < lPad) && (trkWork->getSigmaZ2() < (lPad * lPad / 12.f)))) { tiltCorr = 0.f; // will be zero also for TPC tracks which are shifted in z + dyTiltCorr = 0.f; } // correction for mean z position of tracklet (is not the center of the pad if track eta != 0) - float zPosCorr = spacePoints[trkltIdx].getZ() + mZCorrCoefNRC * trkWork->getTgl(); + float zPosCorr = spacePoints[trkltIdx].getZ() + mRecoParam->getZCorrCoeffNRC() * trkWork->getTgl(); float yPosCorr = spacePoints[trkltIdx].getY() - tiltCorr; zPosCorr -= zShiftTrk; // shift tracklet instead of track in order to avoid having to do a re-fit for each collision float deltaY = yPosCorr - projY; @@ -595,8 +597,22 @@ GPUd() bool GPUTRDTracker_t::FollowProlongation(PROP* prop, TRDTRK // tracklet is in windwow: get predicted chi2 for update and store tracklet index if best guess RecalcTrkltCov(tilt, trkWork->getSnp(), pad->GetRowSize(tracklets[trkltIdx].GetZbin()), trkltCovTmp); float chi2 = prop->getPredictedChi2(trkltPosTmpYZ, trkltCovTmp); + if (Param().rec.trd.addDeflectionInChi2 && (trkWork->getSnp() < 1.f - 1e-6f) && (trkWork->getSnp() > -1.f + 1e-6f)) { + // we add the slope in the chi2 calculation + float trkltCovTmpWithDy[6] = {trkltCovTmp[0], trkltCovTmp[1], trkltCovTmp[2], 0.f, 0.f, 0.f}; + RecalcTrkltCovDy(tilt, trkWork->getSnp(), trkltCovTmpWithDy); + trkltCovTmpWithDy[0] += trkWork->getSigmaY2(); + trkltCovTmpWithDy[1] += trkWork->getSigmaZY(); + trkltCovTmpWithDy[2] += trkWork->getSigmaZ2(); + + // For now, dy uncertainty parametrization also includes track uncertainty, so no need to add additional uncertainty + if (InvertCov(trkltCovTmpWithDy)) { + float deltaDy = spacePoints[trkltIdx].getDy() + dyTiltCorr - mRecoParam->convertAngleToDy(trkWork->getSnp()); + chi2 = deltaY * trkltCovTmpWithDy[0] * deltaY + 2 * deltaY * trkltCovTmpWithDy[1] * deltaZ + 2 * deltaY * trkltCovTmpWithDy[3] * deltaDy + deltaZ * trkltCovTmpWithDy[2] * deltaZ + 2 * deltaZ * trkltCovTmpWithDy[4] * deltaDy + deltaDy * trkltCovTmpWithDy[5] * deltaDy; + } + } // TODO cut on angular pull should be made stricter when proper v-drift calibration for the TRD tracklets is implemented - if ((chi2 > Param().rec.trd.maxChi2) || (Param().rec.trd.applyDeflectionCut && CAMath::Abs(GetAngularPull(spacePoints[trkltIdx].getDy(), trkWork->getSnp())) > 4)) { + if ((chi2 > Param().rec.trd.maxChi2) || (Param().rec.trd.applyDeflectionCut && CAMath::Abs(GetAngularPull(spacePoints[trkltIdx].getDy() + dyTiltCorr, trkWork->getSnp())) > 4)) { continue; } Hypothesis hypo(trkWork->getNlayersFindable(), iCandidate, trkltIdx, trkWork->getChi2() + chi2); @@ -674,7 +690,7 @@ GPUd() bool GPUTRDTracker_t::FollowProlongation(PROP* prop, TRDTRK pad = mGeo->GetPadPlane(tracklets[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].GetDetector()); float tiltCorrUp = tilt * (spacePoints[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].getZ() - trkWork->getZ()); - float zPosCorrUp = spacePoints[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].getZ() + mZCorrCoefNRC * trkWork->getTgl(); + float zPosCorrUp = spacePoints[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].getZ() + mRecoParam->getZCorrCoeffNRC() * trkWork->getTgl(); zPosCorrUp -= zShiftTrk; float padLength = pad->GetRowSize(tracklets[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].GetZbin()); if (!((trkWork->getSigmaZ2() < (padLength * padLength / 12.f)) && (CAMath::Abs(spacePoints[mHypothesis[iUpdate + hypothesisIdxOffset].mTrackletId].getZ() - trkWork->getZ()) < padLength))) { @@ -938,6 +954,69 @@ GPUd() void GPUTRDTracker_t::RecalcTrkltCov(const float tilt, cons cov[2] = c2 * (t2 * sy2 + sz2); } +template +GPUd() void GPUTRDTracker_t::RecalcTrkltCovDy(const float tilt, const float snp, float (&cov)[6]) +{ + float t2 = tilt * tilt; // tan^2 (tilt) + float c2 = 1.f / (1.f + t2); // cos^2 (tilt) + float sy2 = mRecoParam->getRPhiRes(snp); + float sdy2 = mRecoParam->getDyRes(snp); + cov[3] = mRecoParam->getCorrYDy(snp) * CAMath::Sqrt(sdy2 * c2 * sy2); + cov[4] = -tilt * mRecoParam->getCorrYDy(snp) * CAMath::Sqrt(sdy2 * c2 * sy2); + cov[5] = sdy2; +} + +template +GPUd() bool GPUTRDTracker_t::InvertCov(float (&cov)[6]) +{ + // invert a 3*3 symmetric matrix. Adapted from https://root.cern.ch/doc/master/TMatrixTSymCramerInv_8cxx_source.html + + float c00 = cov[2] * cov[5] - cov[4] * cov[4]; + float c01 = cov[4] * cov[3] - cov[1] * cov[5]; + float c02 = cov[1] * cov[4] - cov[2] * cov[3]; + float c11 = cov[5] * cov[0] - cov[3] * cov[3]; + float c12 = cov[3] * cov[1] - cov[4] * cov[0]; + float c22 = cov[0] * cov[2] - cov[1] * cov[1]; + + float t0 = CAMath::Abs(cov[0]); + float t1 = CAMath::Abs(cov[1]); + float t2 = CAMath::Abs(cov[3]); + + float det; + float tmp; + + if (t0 >= t1) { + if (t2 >= t0) { + tmp = cov[3]; + det = c12 * c01 - c11 * c02; + } else { + tmp = cov[0]; + det = c11 * c22 - c12 * c12; + } + } else if (t2 >= t1) { + tmp = cov[3]; + det = c12 * c01 - c11 * c02; + } else { + tmp = cov[1]; + det = c02 * c12 - c01 * c22; + } + + if (det == 0 || tmp == 0) { + return false; + } + + float s = tmp / det; + + cov[0] = s * c00; + cov[1] = s * c01; + cov[3] = s * c02; + cov[2] = s * c11; + cov[4] = s * c12; + cov[5] = s * c22; + + return true; +} + template GPUd() float GPUTRDTracker_t::GetAngularPull(float dYtracklet, float snp) const { diff --git a/GPU/GPUTracking/TRDTracking/GPUTRDTracker.h b/GPU/GPUTracking/TRDTracking/GPUTRDTracker.h index 5d7530ccecc11..f698e570d2158 100644 --- a/GPU/GPUTracking/TRDTracking/GPUTRDTracker.h +++ b/GPU/GPUTracking/TRDTracking/GPUTRDTracker.h @@ -117,6 +117,8 @@ class GPUTRDTracker_t : public GPUProcessor GPUd() float GetAlphaOfSector(const int32_t sec) const; 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 RecalcTrkltCovDy(const float tilt, const float snp, float (&cov)[6]); + GPUd() bool InvertCov(float (&cov)[6]); 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; GPUd() bool IsGeoFindable(const TRDTRK* t, const int32_t layer, const float alpha, const float zShiftTrk) const; GPUd() void InsertHypothesis(Hypothesis hypo, int32_t& nCurrHypothesis, int32_t idxOffset); @@ -172,13 +174,11 @@ 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 - const GPUTRDRecoParam* mRecoParam; // TRD RecoParam - /// ---- end error parametrization ---- + const GPUTRDRecoParam* mRecoParam; // TRD RecoParam 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 float mMaxEta; // TPC tracks with higher eta are ignored float mRoadZ; // in z, a constant search road is used - float mZCorrCoefNRC; // tracklet z-position depends linearly on track dip angle float mTPCVdrift; // TPC drift velocity used for shifting TPC tracks along Z float mTPCTDriftOffset; // TPC drift time additive offset GPUTRDTrackerDebug* mDebug; // debug output From 657d53aefcd3d49808f21540ed1d0a371f914fec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Tue, 24 Feb 2026 22:14:52 +0200 Subject: [PATCH 301/701] [ALICE3] update TOF geometry (#15096) - add segmentation into modules, fix overlaps --- .../base/include/IOTOFBase/GeometryTGeo.h | 8 + .../ALICE3/IOTOF/base/src/GeometryTGeo.cxx | 4 + .../include/IOTOFSimulation/Layer.h | 12 +- .../ALICE3/IOTOF/simulation/src/Detector.cxx | 4 +- .../ALICE3/IOTOF/simulation/src/Layer.cxx | 330 +++++++++++++----- 5 files changed, 257 insertions(+), 101 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/GeometryTGeo.h b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/GeometryTGeo.h index 177426e8dba09..577bd1bcabaf1 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/GeometryTGeo.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/GeometryTGeo.h @@ -32,11 +32,15 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache // Inner TOF static const char* getITOFLayerPattern() { return sITOFLayerName.c_str(); } + static const char* getITOFStavePattern() { return sITOFStaveName.c_str(); } + static const char* getITOFModulePattern() { return sITOFModuleName.c_str(); } static const char* getITOFChipPattern() { return sITOFChipName.c_str(); } static const char* getITOFSensorPattern() { return sITOFSensorName.c_str(); } // Outer TOF static const char* getOTOFLayerPattern() { return sOTOFLayerName.c_str(); } + static const char* getOTOFStavePattern() { return sOTOFStaveName.c_str(); } + static const char* getOTOFModulePattern() { return sOTOFModuleName.c_str(); } static const char* getOTOFChipPattern() { return sOTOFChipName.c_str(); } static const char* getOTOFSensorPattern() { return sOTOFSensorName.c_str(); } @@ -81,11 +85,15 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache // Inner TOF static std::string sITOFLayerName; + static std::string sITOFStaveName; + static std::string sITOFModuleName; static std::string sITOFChipName; static std::string sITOFSensorName; // Outer TOF static std::string sOTOFLayerName; + static std::string sOTOFStaveName; + static std::string sOTOFModuleName; static std::string sOTOFChipName; static std::string sOTOFSensorName; diff --git a/Detectors/Upgrades/ALICE3/IOTOF/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/IOTOF/base/src/GeometryTGeo.cxx index 8c29127a5e7d6..f7d0eb135a27a 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/base/src/GeometryTGeo.cxx @@ -23,11 +23,15 @@ std::string GeometryTGeo::sIOTOFVolumeName = "IOTOFV"; // Inner TOF std::string GeometryTGeo::sITOFLayerName = "ITOFLayer"; +std::string GeometryTGeo::sITOFStaveName = "ITOFStave"; +std::string GeometryTGeo::sITOFModuleName = "ITOFModule"; std::string GeometryTGeo::sITOFChipName = "ITOFChip"; std::string GeometryTGeo::sITOFSensorName = "ITOFSensor"; // Outer TOF std::string GeometryTGeo::sOTOFLayerName = "OTOFLayer"; +std::string GeometryTGeo::sOTOFStaveName = "OTOFStave"; +std::string GeometryTGeo::sOTOFModuleName = "OTOFModule"; std::string GeometryTGeo::sOTOFChipName = "OTOFChip"; std::string GeometryTGeo::sOTOFSensorName = "OTOFSensor"; diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Layer.h b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Layer.h index df3687b2b2ea4..29542810b8021 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Layer.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Layer.h @@ -26,7 +26,7 @@ class Layer public: Layer() = default; 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); + int layout = kBarrel, int nStaves = 0, float staveSize = 0.0, double staveTiltAngle = 0.0, int modulesPerStave = 0); ~Layer() = default; auto getInnerRadius() const { return mInnerRadius; } @@ -37,7 +37,7 @@ class Layer auto getChipThickness() const { return mChipThickness; } auto getName() const { return mLayerName; } auto getLayout() const { return mLayout; } - auto getSegments() const { return mSegments; } + auto getSegments() const { return mStaves; } static constexpr int kBarrel = 0; static constexpr int kDisk = 1; static constexpr int kBarrelSegmented = 2; @@ -54,10 +54,10 @@ class Layer float mX2X0; float mChipThickness; 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 + // To be used only in case of the segmented layout, to define the number of staves in phi (for barrel) or in r (for disk) + std::pair mStaves{0, 0.0f}; // Number and size of staves in phi (for barrel) or in r (for disk) in case of segmented layout + int mModulesPerStave{0}; // Number of modules along a stave + double mTiltAngle{0.0}; // Tilt angle in degrees to be applied as a rotation around the local center of the stave }; class ITOFLayer : public Layer diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx index 0742af3a1340a..c056df5fd34ca 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx @@ -98,14 +98,14 @@ void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::str 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) + 24, 5.42, 10.0, 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) + 62, 9.74, 5.0, 54) : OTOFLayer(std::string{GeometryTGeo::getOTOFLayerPattern()}, radiusOuterTof, 0.f, lengthOuterTof, 0.f, 0.02f, OTOFLayer::kBarrel); } diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx index 169a1271da47e..32a24fc46f94c 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx @@ -27,7 +27,8 @@ namespace o2 { namespace iotof { -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) +Layer::Layer(std::string layerName, float rInn, float rOut, float zLength, float zOffset, float layerX2X0, + int layout, int nStaves, float staveSize, double staveTiltAngle, int modulesPerStave) : mLayerName(layerName), mInnerRadius(rInn), mOuterRadius(rOut), @@ -35,9 +36,9 @@ Layer::Layer(std::string layerName, float rInn, float rOut, float zLength, float mZOffset(zOffset), mX2X0(layerX2X0), mLayout(layout), - mSegments(nSegments, segmentSize), - mSensorsPerSegment(nSensorsPerSegment), - mTiltAngle(tiltAngle) + mStaves(nStaves, staveSize), + mModulesPerStave(modulesPerStave), + mTiltAngle(staveTiltAngle) { float Si_X0 = 9.5f; mChipThickness = mX2X0 * Si_X0; @@ -56,32 +57,72 @@ Layer::Layer(std::string layerName, float rInn, float rOut, float zLength, float 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; - } + // Sanity checks + if (mInnerRadius > mOuterRadius) { + LOG(fatal) << "Invalid layer dimensions: rInner " << mInnerRadius << " cm is larger than rOuter " << mOuterRadius << " cm"; + } + if ((mStaves.first != 0 || mStaves.second != 0.0f) && (layout != kBarrelSegmented && layout != kDiskSegmented)) { + LOG(fatal) << "Invalid configuration: number of segments " << mStaves.first << " is set for non-segmented layout " << layout; + } + if ((mStaves.first <= 1 || mStaves.second <= 0.0f) && (layout == kBarrelSegmented || layout == kDiskSegmented)) { + LOG(fatal) << "Invalid configuration: number of segments " << mStaves.first << " must be positive for segmented layout " << layout; + } + if (mModulesPerStave <= 0 && (layout == kBarrelSegmented || layout == kDiskSegmented)) { + LOG(fatal) << "Invalid configuration: number of sensors per segment " << mModulesPerStave << " 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; + } + if ((mTiltAngle < 0.0 || mTiltAngle > 90.0) && (layout == kBarrelSegmented || layout == kDiskSegmented)) { + LOG(fatal) << "Invalid configuration: tilt angle " << mTiltAngle << " is too large, it must be between 0 and 90 degrees"; } LOGP(info, "TOF: Creating {} layer: rInner: {} (cm) rOuter: {} (cm) zLength: {} (cm) zOffset: {} x2X0: {}", name.c_str(), mInnerRadius, mOuterRadius, mZLength, mZOffset, mX2X0); } +void setLayerStyle(TGeoVolume* obj) +{ + obj->SetLineColor(kRed - 7); + obj->SetFillColor(kRed - 7); + obj->SetLineWidth(1); + obj->SetTransparency(70); +} +void setStaveStyle(TGeoVolume* obj) +{ + obj->SetLineColor(kRed - 5); + obj->SetFillColor(kRed - 9); + obj->SetLineWidth(2); + obj->SetTransparency(45); +} +void setModuleStyle(TGeoVolume* obj) +{ + obj->SetLineColor(kRed - 3); + obj->SetFillColor(kRed - 8); + obj->SetLineWidth(2); + obj->SetTransparency(35); +} +void setChipStyle(TGeoVolume* obj) +{ + obj->SetLineColor(kOrange); + obj->SetFillColor(kOrange - 9); + obj->SetLineWidth(3); + obj->SetTransparency(15); +} +void setSensorStyle(TGeoVolume* obj) +{ + obj->SetLineColor(kRed); + obj->SetFillColor(kRed - 9); + obj->SetLineWidth(3); + obj->SetTransparency(5); +} + std::vector ITOFLayer::mRegister; void ITOFLayer::createLayer(TGeoVolume* motherVolume) { - const std::string chipName = o2::iotof::GeometryTGeo::getITOFChipPattern(); - const std::string sensName = o2::iotof::GeometryTGeo::getITOFSensorPattern(); + const char* chipName = o2::iotof::GeometryTGeo::getITOFChipPattern(); + const char* sensName = o2::iotof::GeometryTGeo::getITOFSensorPattern(); + const char* moduleName = o2::iotof::GeometryTGeo::getITOFModulePattern(); + const char* staveName = o2::iotof::GeometryTGeo::getITOFStavePattern(); TGeoMedium* medSi = gGeoManager->GetMedium("TF3_SILICON$"); TGeoMedium* medAir = gGeoManager->GetMedium("TF3_AIR$"); @@ -93,12 +134,12 @@ void ITOFLayer::createLayer(TGeoVolume* motherVolume) 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* sensVol = new TGeoVolume(sensName, sensor, medSi); + TGeoVolume* chipVol = new TGeoVolume(chipName, chip, medSi); TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); - sensVol->SetLineColor(kRed + 3); - chipVol->SetLineColor(kRed + 3); - layerVol->SetLineColor(kRed + 3); + setSensorStyle(sensVol); + setChipStyle(chipVol); + setLayerStyle(layerVol); LOGP(info, "Inserting Barrel {} in {} ", sensVol->GetName(), chipVol->GetName()); ITOFLayer::mRegister.push_back(sensVol->GetName()); @@ -112,40 +153,91 @@ void ITOFLayer::createLayer(TGeoVolume* motherVolume) return; } case kBarrelSegmented: { - const double circumference = TMath::TwoPi() * 0.5 * (mInnerRadius + mOuterRadius); - const double segmentSize = mSegments.second; // cm circumference / mSegments; + // First we create the volume for the whole layer, which will be used as mother volume for the segments const double avgRadius = 0.5 * (mInnerRadius + mOuterRadius); - TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); + const double staveSizeX = mStaves.second; // cm + const double staveSizeY = mOuterRadius - mInnerRadius; // cm + const double staveSizeZ = mZLength; // cm + const double deltaForTilt = 0.5 * (std::sin(TMath::DegToRad() * mTiltAngle) * staveSizeX + std::cos(TMath::DegToRad() * mTiltAngle) * staveSizeY); // we increase the size of the layer to account for the tilt of the staves + TGeoTube* layer = new TGeoTube(mInnerRadius - deltaForTilt, mOuterRadius + deltaForTilt, 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"; + setLayerStyle(layerVol); + + // Now we create the volume for a single stave + TGeoBBox* stave = new TGeoBBox(staveSizeX * 0.5, staveSizeY * 0.5, staveSizeZ * 0.5); + TGeoVolume* staveVol = new TGeoVolume(staveName, stave, medAir); + setStaveStyle(staveVol); + + // Now we create the volume for a single module (sensor + chip) + const int modulesPerStaveX = 1; // we assume that each stave is divided in 2 modules along the x direction + const double moduleSizeX = staveSizeX / modulesPerStaveX; // cm + const double moduleSizeY = staveSizeY; // cm + const double moduleSizeZ = staveSizeZ / mModulesPerStave; // cm + TGeoBBox* module = new TGeoBBox(moduleSizeX * 0.5, moduleSizeY * 0.5, moduleSizeZ * 0.5); + TGeoVolume* moduleVol = new TGeoVolume(moduleName, module, medAir); + setModuleStyle(moduleVol); + + // Now we create the volume of the chip, which is the same for all modules + const int chipsPerModuleX = 2; // we assume that each module is divided in 2 chips along the x direction + const int chipsPerModuleZ = 2; // we assume that each module is divided in 2 chips along the z direction + const double chipSizeX = moduleSizeX / chipsPerModuleX; // cm + const double chipSizeY = moduleSizeY; // cm + const double chipSizeZ = moduleSizeZ / chipsPerModuleZ; // cm + TGeoBBox* chip = new TGeoBBox(chipSizeX * 0.5, chipSizeY * 0.5, chipSizeZ * 0.5); + TGeoVolume* chipVol = new TGeoVolume(chipName, chip, medSi); + setChipStyle(chipVol); + + // Finally we create the volume of the sensor, which is the same for all chips + const int sensorsPerChipX = 2; // we assume that each chip is divided in 2 sensors along the x direction + const int sensorsPerChipZ = 2; // we assume that each chip is divided in 2 sensors along the z direction + const double sensorSizeX = chipSizeX / sensorsPerChipX; // cm + const double sensorSizeY = chipSizeY; // cm + const double sensorSizeZ = chipSizeZ / sensorsPerChipZ; // cm + TGeoBBox* sensor = new TGeoBBox(sensorSizeX * 0.5, sensorSizeY * 0.5, sensorSizeZ * 0.5); + TGeoVolume* sensVol = new TGeoVolume(sensName, sensor, medSi); + setSensorStyle(sensVol); + ITOFLayer::mRegister.push_back(sensVol->GetName()); + + // Now we build a chip from sensors + for (int i = 0; i < sensorsPerChipX; ++i) { + for (int j = 0; j < sensorsPerChipZ; ++j) { + LOGP(info, "iTOF: Creating sensor {}/{} for chip {}/{}", i + 1, sensorsPerChipX, j + 1, sensorsPerChipZ); + auto* translation = new TGeoTranslation((i + 0.5) * sensorSizeX - 0.5 * chipSizeX, + 0, + (j + 0.5) * sensorSizeZ - 0.5 * chipSizeZ); + chipVol->AddNode(sensVol, 1 + i * sensorsPerChipZ + j, translation); + } + } + + // Now we build a module from chips + for (int i = 0; i < chipsPerModuleX; ++i) { + for (int j = 0; j < chipsPerModuleZ; ++j) { + LOGP(info, "iTOF: Creating chip {}/{} for module {}/{}", i + 1, chipsPerModuleX, j + 1, chipsPerModuleZ); + auto* translation = new TGeoTranslation((i + 0.5) * chipSizeX - 0.5 * moduleSizeX, 0, (j + 0.5) * chipSizeZ - 0.5 * moduleSizeZ); + moduleVol->AddNode(chipVol, 1 + i * chipsPerModuleZ + j, translation); + } + } + + // Now we build a stave from modules + for (int i = 0; i < modulesPerStaveX; ++i) { + for (int j = 0; j < mModulesPerStave; ++j) { + LOGP(info, "iTOF: Creating module {}/{} for stave {}/{}", i + 1, modulesPerStaveX, j + 1, mModulesPerStave); + auto* translation = new TGeoTranslation((i + 0.5) * moduleSizeX - 0.5 * staveSizeX, 0, (j + 0.5) * moduleSizeZ - 0.5 * staveSizeZ); + staveVol->AddNode(moduleVol, 1 + i * mModulesPerStave + j, translation); + } + } + + // We finally put all the staves in the layer + for (int i = 0; i < mStaves.first; ++i) { + LOGP(info, "iTOF: Creating stave {}/{} for layer {}", i + 1, mStaves.first, layerVol->GetName()); + const double phi = TMath::TwoPi() * i / mStaves.first; 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* rotation = new TGeoRotation(Form("segmentRot%d", i + 1), phi * TMath::RadToDeg() + 90 + 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); + layerVol->AddNode(staveVol, 1 + i, transformation); } LOGP(info, "Inserting Barrel {} in {} at r={} cm", layerVol->GetName(), motherVolume->GetName(), avgRadius); motherVolume->AddNode(layerVol, 1, nullptr); @@ -159,8 +251,10 @@ void ITOFLayer::createLayer(TGeoVolume* motherVolume) std::vector OTOFLayer::mRegister; void OTOFLayer::createLayer(TGeoVolume* motherVolume) { - std::string chipName = o2::iotof::GeometryTGeo::getOTOFChipPattern(), - sensName = o2::iotof::GeometryTGeo::getOTOFSensorPattern(); + const char* chipName = o2::iotof::GeometryTGeo::getOTOFChipPattern(); + const char* sensName = o2::iotof::GeometryTGeo::getOTOFSensorPattern(); + const char* moduleName = o2::iotof::GeometryTGeo::getOTOFModulePattern(); + const char* staveName = o2::iotof::GeometryTGeo::getOTOFStavePattern(); TGeoMedium* medSi = gGeoManager->GetMedium("TF3_SILICON$"); TGeoMedium* medAir = gGeoManager->GetMedium("TF3_AIR$"); @@ -172,12 +266,12 @@ void OTOFLayer::createLayer(TGeoVolume* motherVolume) 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* sensVol = new TGeoVolume(sensName, sensor, medSi); + TGeoVolume* chipVol = new TGeoVolume(chipName, chip, medSi); TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); - sensVol->SetLineColor(kRed + 3); - chipVol->SetLineColor(kRed + 3); - layerVol->SetLineColor(kRed + 3); + setSensorStyle(sensVol); + setChipStyle(chipVol); + setLayerStyle(layerVol); LOGP(info, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); OTOFLayer::mRegister.push_back(sensVol->GetName()); @@ -191,40 +285,90 @@ void OTOFLayer::createLayer(TGeoVolume* motherVolume) return; } case kBarrelSegmented: { - const double circumference = TMath::TwoPi() * 0.5 * (mInnerRadius + mOuterRadius); - const double segmentSize = mSegments.second; // cm circumference / mSegments; + // First we create the volume for the whole layer, which will be used as mother volume for the segments 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"; + setLayerStyle(layerVol); + + // Now we create the volume for a single stave + const double staveSizeX = mStaves.second; // cm + const double staveSizeY = mOuterRadius - mInnerRadius; // cm + const double staveSizeZ = mZLength; // cm + TGeoBBox* stave = new TGeoBBox(staveSizeX * 0.5, staveSizeY * 0.5, staveSizeZ * 0.5); + TGeoVolume* staveVol = new TGeoVolume(staveName, stave, medAir); + setStaveStyle(staveVol); + + // Now we create the volume for a single module (sensor + chip) + const int modulesPerStaveX = 1; // we assume that each stave is divided in 2 modules along the x direction + const double moduleSizeX = staveSizeX / modulesPerStaveX; // cm + const double moduleSizeY = staveSizeY; // cm + const double moduleSizeZ = staveSizeZ / mModulesPerStave; // cm + TGeoBBox* module = new TGeoBBox(moduleSizeX * 0.5, moduleSizeY * 0.5, moduleSizeZ * 0.5); + TGeoVolume* moduleVol = new TGeoVolume(moduleName, module, medAir); + setModuleStyle(moduleVol); + + // Now we create the volume of the chip, which is the same for all modules + const int chipsPerModuleX = 2; // we assume that each module is divided in 2 chips along the x direction + const int chipsPerModuleZ = 2; // we assume that each module is divided in 2 chips along the z direction + const double chipSizeX = moduleSizeX / chipsPerModuleX; // cm + const double chipSizeY = moduleSizeY; // cm + const double chipSizeZ = moduleSizeZ / chipsPerModuleZ; // cm + TGeoBBox* chip = new TGeoBBox(chipSizeX * 0.5, chipSizeY * 0.5, chipSizeZ * 0.5); + TGeoVolume* chipVol = new TGeoVolume(chipName, chip, medSi); + setChipStyle(chipVol); + + // Finally we create the volume of the sensor, which is the same for all chips + const int sensorsPerChipX = 2; // we assume that each chip is divided in 2 sensors along the x direction + const int sensorsPerChipZ = 2; // we assume that each chip is divided in 2 sensors along the z direction + const double sensorSizeX = chipSizeX / sensorsPerChipX; // cm + const double sensorSizeY = chipSizeY; // cm + const double sensorSizeZ = chipSizeZ / sensorsPerChipZ; // cm + TGeoBBox* sensor = new TGeoBBox(sensorSizeX * 0.5, sensorSizeY * 0.5, sensorSizeZ * 0.5); + TGeoVolume* sensVol = new TGeoVolume(sensName, sensor, medSi); + setSensorStyle(sensVol); + OTOFLayer::mRegister.push_back(sensVol->GetName()); + + // Now we build a chip from sensors + for (int i = 0; i < sensorsPerChipX; ++i) { + for (int j = 0; j < sensorsPerChipZ; ++j) { + LOGP(info, "oTOF: Creating sensor {}/{} for chip {}/{}", i + 1, sensorsPerChipX, j + 1, sensorsPerChipZ); + auto* translation = new TGeoTranslation((i + 0.5) * sensorSizeX - 0.5 * chipSizeX, + 0, + (j + 0.5) * sensorSizeZ - 0.5 * chipSizeZ); + chipVol->AddNode(sensVol, 1 + i * sensorsPerChipZ + j, translation); + } + } + + // Now we build a module from chips + for (int i = 0; i < chipsPerModuleX; ++i) { + for (int j = 0; j < chipsPerModuleZ; ++j) { + LOGP(info, "oTOF: Creating chip {}/{} for module {}/{}", i + 1, chipsPerModuleX, j + 1, chipsPerModuleZ); + auto* translation = new TGeoTranslation((i + 0.5) * chipSizeX - 0.5 * moduleSizeX, 0, (j + 0.5) * chipSizeZ - 0.5 * moduleSizeZ); + moduleVol->AddNode(chipVol, 1 + i * chipsPerModuleZ + j, translation); + } + } + + // Now we build a stave from modules + for (int i = 0; i < modulesPerStaveX; ++i) { + for (int j = 0; j < mModulesPerStave; ++j) { + LOGP(info, "oTOF: Creating module {}/{} for stave {}/{}", i + 1, modulesPerStaveX, j + 1, mModulesPerStave); + auto* translation = new TGeoTranslation((i + 0.5) * moduleSizeX - 0.5 * staveSizeX, 0, (j + 0.5) * moduleSizeZ - 0.5 * staveSizeZ); + staveVol->AddNode(moduleVol, 1 + i * mModulesPerStave + j, translation); + } + } + + // We finally put all the staves in the layer + for (int i = 0; i < mStaves.first; ++i) { + LOGP(info, "oTOF: Creating stave {}/{} for layer {}", i + 1, mStaves.first, layerVol->GetName()); + const double phi = TMath::TwoPi() * i / mStaves.first; 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* rotation = new TGeoRotation(Form("segmentRot%d", i + 1), phi * TMath::RadToDeg() + 90 + 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); + layerVol->AddNode(staveVol, 1 + i, transformation); } LOGP(info, "Inserting Barrel {} in {} at r={} cm", layerVol->GetName(), motherVolume->GetName(), avgRadius); motherVolume->AddNode(layerVol, 1, nullptr); @@ -250,9 +394,9 @@ void FTOFLayer::createLayer(TGeoVolume* motherVolume) 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); + setSensorStyle(sensVol); + setChipStyle(chipVol); + setLayerStyle(layerVol); LOGP(info, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); chipVol->AddNode(sensVol, 1, nullptr); @@ -282,9 +426,9 @@ void BTOFLayer::createLayer(TGeoVolume* motherVolume) 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); + setSensorStyle(sensVol); + setChipStyle(chipVol); + setLayerStyle(layerVol); LOGP(info, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); chipVol->AddNode(sensVol, 1, nullptr); From f65da9abe52beb8fa8368c041bf45ef0e730fdfc Mon Sep 17 00:00:00 2001 From: shahoian Date: Tue, 24 Feb 2026 12:27:51 +0100 Subject: [PATCH 302/701] Fix order of args passed to CTFCoderBase Due to this typo the dictionary, if it was requested, was not propagated to the coder --- .../CPV/reconstruction/include/CPVReconstruction/CTFCoder.h | 2 +- .../CTP/reconstruction/include/CTPReconstruction/CTFCoder.h | 2 +- .../EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h | 2 +- .../FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h | 2 +- .../FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h | 2 +- .../FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h | 2 +- .../HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h | 2 +- .../reconstruction/include/ITSMFTReconstruction/CTFCoder.h | 2 +- Detectors/MUON/MCH/CTF/include/MCHCTF/CTFCoder.h | 2 +- Detectors/MUON/MID/CTF/include/MIDCTF/CTFCoder.h | 2 +- .../PHOS/reconstruction/include/PHOSReconstruction/CTFCoder.h | 2 +- .../TOF/reconstruction/include/TOFReconstruction/CTFCoder.h | 2 +- .../TPC/reconstruction/include/TPCReconstruction/CTFCoder.h | 2 +- .../TRD/reconstruction/include/TRDReconstruction/CTFCoder.h | 2 +- .../ZDC/reconstruction/include/ZDCReconstruction/CTFCoder.h | 2 +- 15 files changed, 15 insertions(+), 15 deletions(-) diff --git a/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h b/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h index a5f9d0eac90e8..4e259c24f44a6 100644 --- a/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h +++ b/Detectors/CPV/reconstruction/include/CPVReconstruction/CTFCoder.h @@ -35,7 +35,7 @@ namespace cpv class CTFCoder final : public o2::ctf::CTFCoderBase { public: - 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(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::CPV, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h b/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h index b17db0e77be28..8dbc5adadbfc5 100644 --- a/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h +++ b/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h @@ -37,7 +37,7 @@ namespace ctp class CTFCoder final : public o2::ctf::CTFCoderBase { public: - 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(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::CTP, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h index 6584775057d9f..706b9bf8138a6 100644 --- a/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h +++ b/Detectors/EMCAL/reconstruction/include/EMCALReconstruction/CTFCoder.h @@ -35,7 +35,7 @@ namespace emcal class CTFCoder final : public o2::ctf::CTFCoderBase { public: - 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(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::EMC, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h index cb3b13aa9b8e4..c62e013447416 100644 --- a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h +++ b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h @@ -36,7 +36,7 @@ namespace fdd class CTFCoder final : public o2::ctf::CTFCoderBase { public: - 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(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::FDD, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode digits to buffer with CTF diff --git a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h index 5c2e0f0627ef1..5dc367204e1a3 100644 --- a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h +++ b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h @@ -37,7 +37,7 @@ namespace ft0 class CTFCoder final : public o2::ctf::CTFCoderBase { public: - 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(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::FT0, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode digits to buffer with CTF diff --git a/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h index fdff035b934ef..80dcd6060455b 100644 --- a/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h +++ b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h @@ -33,7 +33,7 @@ namespace fv0 class CTFCoder final : public o2::ctf::CTFCoderBase { public: - 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(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::FV0, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode digits to buffer with CTF diff --git a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h index 0e6694d2353ac..894c11864f061 100644 --- a/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h +++ b/Detectors/HMPID/reconstruction/include/HMPIDReconstruction/CTFCoder.h @@ -35,7 +35,7 @@ namespace hmpid class CTFCoder final : public o2::ctf::CTFCoderBase { public: - 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(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::HMP, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h index 57d989038342a..7e266052efb3c 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h @@ -45,7 +45,7 @@ class CTFCoder final : public o2::ctf::CTFCoderBase using PMatrix = std::array, ClusterPattern::MaxColSpan + 2>; using RowColBuff = std::vector; - 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(o2::ctf::CTFCoderBase::OpType op, o2::detectors::DetID det, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), det, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode clusters to buffer with CTF diff --git a/Detectors/MUON/MCH/CTF/include/MCHCTF/CTFCoder.h b/Detectors/MUON/MCH/CTF/include/MCHCTF/CTFCoder.h index 5c9da95a98354..a5f2af646c778 100644 --- a/Detectors/MUON/MCH/CTF/include/MCHCTF/CTFCoder.h +++ b/Detectors/MUON/MCH/CTF/include/MCHCTF/CTFCoder.h @@ -37,7 +37,7 @@ namespace mch class CTFCoder final : public o2::ctf::CTFCoderBase { public: - 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(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::MCH, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/MUON/MID/CTF/include/MIDCTF/CTFCoder.h b/Detectors/MUON/MID/CTF/include/MIDCTF/CTFCoder.h index defec7207f808..0a6ee12316921 100644 --- a/Detectors/MUON/MID/CTF/include/MIDCTF/CTFCoder.h +++ b/Detectors/MUON/MID/CTF/include/MIDCTF/CTFCoder.h @@ -37,7 +37,7 @@ namespace mid class CTFCoder final : public o2::ctf::CTFCoderBase { public: - 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(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::MID, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFCoder.h b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFCoder.h index e222328a351c0..8c73114bc53bb 100644 --- a/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFCoder.h +++ b/Detectors/PHOS/reconstruction/include/PHOSReconstruction/CTFCoder.h @@ -35,7 +35,7 @@ namespace phos class CTFCoder final : public o2::ctf::CTFCoderBase { public: - 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(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::PHS, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/TOF/reconstruction/include/TOFReconstruction/CTFCoder.h b/Detectors/TOF/reconstruction/include/TOFReconstruction/CTFCoder.h index 53cdf59d08572..4ae99bae86eba 100644 --- a/Detectors/TOF/reconstruction/include/TOFReconstruction/CTFCoder.h +++ b/Detectors/TOF/reconstruction/include/TOFReconstruction/CTFCoder.h @@ -34,7 +34,7 @@ namespace tof class CTFCoder final : public o2::ctf::CTFCoderBase { public: - 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(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::TOF, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode clusters to buffer with CTF diff --git a/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h b/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h index 2c6fac7dcde2a..0d2d37a73d7db 100644 --- a/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h +++ b/Detectors/TPC/reconstruction/include/TPCReconstruction/CTFCoder.h @@ -122,7 +122,7 @@ struct MergedColumnsDecoder { class CTFCoder final : public o2::ctf::CTFCoderBase { public: - 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(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::TPC, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode compressed clusters to flat buffer diff --git a/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFCoder.h b/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFCoder.h index adb584ef15ec4..b34c2da395c5e 100644 --- a/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFCoder.h +++ b/Detectors/TRD/reconstruction/include/TRDReconstruction/CTFCoder.h @@ -36,7 +36,7 @@ namespace trd class CTFCoder final : public o2::ctf::CTFCoderBase { public: - 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(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::TRD, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF diff --git a/Detectors/ZDC/reconstruction/include/ZDCReconstruction/CTFCoder.h b/Detectors/ZDC/reconstruction/include/ZDCReconstruction/CTFCoder.h index a299431ef17fc..ec80c16af329e 100644 --- a/Detectors/ZDC/reconstruction/include/ZDCReconstruction/CTFCoder.h +++ b/Detectors/ZDC/reconstruction/include/ZDCReconstruction/CTFCoder.h @@ -35,7 +35,7 @@ namespace zdc class CTFCoder final : public o2::ctf::CTFCoderBase { public: - 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(o2::ctf::CTFCoderBase::OpType op, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), o2::detectors::DetID::ZDC, 1.f, ctfdictOpt) {} ~CTFCoder() final = default; /// entropy-encode data to buffer with CTF From c590fd7f2f7dbfed03abe1c3b584d0a2cb2d927f Mon Sep 17 00:00:00 2001 From: shahoian Date: Tue, 24 Feb 2026 13:17:20 +0100 Subject: [PATCH 303/701] Change default ctf-dict of encoders to "none" (per-tf dictionary) --- Detectors/Base/include/DetectorsBase/CTFCoderBase.h | 11 ++++++++--- .../CPV/workflow/src/entropy-encoder-workflow.cxx | 2 +- .../CTP/workflow/src/entropy-encoder-workflow.cxx | 2 +- .../EMCAL/workflow/src/entropy-encoder-workflow.cxx | 2 +- .../FIT/FDD/workflow/src/entropy-encoder-workflow.cxx | 2 +- .../FIT/FT0/workflow/src/entropy-encoder-workflow.cxx | 2 +- .../FIT/FV0/workflow/src/entropy-encoder-workflow.cxx | 2 +- .../HMPID/workflow/src/entropy-encoder-workflow.cxx | 2 +- .../common/workflow/src/entropy-encoder-workflow.cxx | 2 +- .../MCH/Workflow/src/entropy-encoder-workflow.cxx | 2 +- .../MID/Workflow/src/entropy-encoder-workflow.cxx | 2 +- .../PHOS/workflow/src/entropy-encoder-workflow.cxx | 2 +- .../TOF/workflow/src/entropy-encoder-workflow.cxx | 2 +- .../TPC/workflow/src/entropy-encoder-workflow.cxx | 2 +- Detectors/TPC/workflow/src/tpc-reco-workflow.cxx | 2 +- .../TRD/workflow/src/entropy-encoder-workflow.cxx | 2 +- .../ZDC/workflow/src/entropy-encoder-workflow.cxx | 2 +- 17 files changed, 24 insertions(+), 19 deletions(-) diff --git a/Detectors/Base/include/DetectorsBase/CTFCoderBase.h b/Detectors/Base/include/DetectorsBase/CTFCoderBase.h index 593bf37df5879..e94123bb2b7ff 100644 --- a/Detectors/Base/include/DetectorsBase/CTFCoderBase.h +++ b/Detectors/Base/include/DetectorsBase/CTFCoderBase.h @@ -314,6 +314,7 @@ void CTFCoderBase::init(o2::framework::InitContext& ic) if (ic.options().hasOption("irframe-shift")) { mIRFrameSelShift = (long)ic.options().get("irframe-shift"); } + bool ansVersionProvided = false; if (ic.options().hasOption("ans-version")) { if (ic.options().isSet("ans-version")) { const std::string ansVersionString = ic.options().get("ans-version"); @@ -323,6 +324,7 @@ void CTFCoderBase::init(o2::framework::InitContext& ic) if (mANSVersion == ANSVersionUnspecified) { throw std::invalid_argument(fmt::format("Invalid ANS Version {}", ansVersionString)); } + ansVersionProvided = true; } } } @@ -331,9 +333,12 @@ void CTFCoderBase::init(o2::framework::InitContext& ic) } else { if (mDictOpt != "none") { // none means per-CTF dictionary will created on the fly createCodersFromFile(mDictOpt, mOpType); - LOGP(info, "Loaded {} from {}", mExtHeader.asString(), mDictOpt); + LOGP(info, "Loaded {} from {}, ANS Version {}", mExtHeader.asString(), mDictOpt, std::string(mANSVersion)); } else { - LOGP(info, "Internal per-TF CTF Dict will be created"); + if (!ansVersionProvided) { + mANSVersion = ANSVersion1; + } + LOGP(info, "Internal per-TF CTF Dict will be created, ANS Version {}", std::string(mANSVersion)); } mLoadDictFromCCDB = false; // don't try to load from CCDB } @@ -368,7 +373,7 @@ bool CTFCoderBase::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, voi } createCoders(*dict, mOpType); mExtHeader = static_cast(CTF::get(dict->data())->getHeader()); - LOGP(info, "Loaded {} from CCDB", mExtHeader.asString()); + LOGP(info, "Loaded {} from CCDB, ANS Version {}", mExtHeader.asString(), std::string(mANSVersion)); } mLoadDictFromCCDB = false; // we read the dictionary at most once! } else if ((match = (matcher == o2::framework::ConcreteDataMatcher("CTP", "Trig_Offset", 0)))) { diff --git a/Detectors/CPV/workflow/src/entropy-encoder-workflow.cxx b/Detectors/CPV/workflow/src/entropy-encoder-workflow.cxx index 6f9445d9ddd16..90fedb1d5eb83 100644 --- a/Detectors/CPV/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/CPV/workflow/src/entropy-encoder-workflow.cxx @@ -23,7 +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{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); diff --git a/Detectors/CTP/workflow/src/entropy-encoder-workflow.cxx b/Detectors/CTP/workflow/src/entropy-encoder-workflow.cxx index 9057d16df4384..c2b324a4b3bfa 100644 --- a/Detectors/CTP/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/CTP/workflow/src/entropy-encoder-workflow.cxx @@ -23,7 +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{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"no-lumi-input", VariantType::Bool, false, {"Lumi info not available"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; diff --git a/Detectors/EMCAL/workflow/src/entropy-encoder-workflow.cxx b/Detectors/EMCAL/workflow/src/entropy-encoder-workflow.cxx index 953b726fcb971..d5264aabc0566 100644 --- a/Detectors/EMCAL/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/EMCAL/workflow/src/entropy-encoder-workflow.cxx @@ -23,7 +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{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); diff --git a/Detectors/FIT/FDD/workflow/src/entropy-encoder-workflow.cxx b/Detectors/FIT/FDD/workflow/src/entropy-encoder-workflow.cxx index 0e43c6e3c4ba0..b83e25557e760 100644 --- a/Detectors/FIT/FDD/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/FIT/FDD/workflow/src/entropy-encoder-workflow.cxx @@ -23,7 +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{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); diff --git a/Detectors/FIT/FT0/workflow/src/entropy-encoder-workflow.cxx b/Detectors/FIT/FT0/workflow/src/entropy-encoder-workflow.cxx index 2b4a86df0a614..144d27abeda9c 100644 --- a/Detectors/FIT/FT0/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/FIT/FT0/workflow/src/entropy-encoder-workflow.cxx @@ -23,7 +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{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); diff --git a/Detectors/FIT/FV0/workflow/src/entropy-encoder-workflow.cxx b/Detectors/FIT/FV0/workflow/src/entropy-encoder-workflow.cxx index f1b1bfa456316..932e0a37ee376 100644 --- a/Detectors/FIT/FV0/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/FIT/FV0/workflow/src/entropy-encoder-workflow.cxx @@ -23,7 +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{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); diff --git a/Detectors/HMPID/workflow/src/entropy-encoder-workflow.cxx b/Detectors/HMPID/workflow/src/entropy-encoder-workflow.cxx index 76e7eae10508e..4a1883233b605 100644 --- a/Detectors/HMPID/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/HMPID/workflow/src/entropy-encoder-workflow.cxx @@ -23,7 +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{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); diff --git a/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx b/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx index 5f09fd6c69a97..b2a1b6ce75591 100644 --- a/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx @@ -24,7 +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{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); diff --git a/Detectors/MUON/MCH/Workflow/src/entropy-encoder-workflow.cxx b/Detectors/MUON/MCH/Workflow/src/entropy-encoder-workflow.cxx index b5f371edfc759..c66365922233e 100644 --- a/Detectors/MUON/MCH/Workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/MUON/MCH/Workflow/src/entropy-encoder-workflow.cxx @@ -118,7 +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{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); diff --git a/Detectors/MUON/MID/Workflow/src/entropy-encoder-workflow.cxx b/Detectors/MUON/MID/Workflow/src/entropy-encoder-workflow.cxx index 25b038190281a..a109250894ce9 100644 --- a/Detectors/MUON/MID/Workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/MUON/MID/Workflow/src/entropy-encoder-workflow.cxx @@ -23,7 +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{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); diff --git a/Detectors/PHOS/workflow/src/entropy-encoder-workflow.cxx b/Detectors/PHOS/workflow/src/entropy-encoder-workflow.cxx index 41642cd026089..1fc827a5a7525 100644 --- a/Detectors/PHOS/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/PHOS/workflow/src/entropy-encoder-workflow.cxx @@ -23,7 +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{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); diff --git a/Detectors/TOF/workflow/src/entropy-encoder-workflow.cxx b/Detectors/TOF/workflow/src/entropy-encoder-workflow.cxx index 5cf882e2723d6..547b3235ca684 100644 --- a/Detectors/TOF/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/TOF/workflow/src/entropy-encoder-workflow.cxx @@ -23,7 +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{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); diff --git a/Detectors/TPC/workflow/src/entropy-encoder-workflow.cxx b/Detectors/TPC/workflow/src/entropy-encoder-workflow.cxx index c09eb193e0fbf..3256b875447a8 100644 --- a/Detectors/TPC/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/TPC/workflow/src/entropy-encoder-workflow.cxx @@ -23,7 +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{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}, ConfigParamSpec{"inputFromFile", VariantType::Bool, false, {"Expect COMPCLUSTERS from file"}}}; diff --git a/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx b/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx index 07b1c293bff98..f3d4d639ddfd2 100644 --- a/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx +++ b/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx @@ -71,7 +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"}}, + {"ctf-dict", VariantType::String, "none", {"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)"}}, }; diff --git a/Detectors/TRD/workflow/src/entropy-encoder-workflow.cxx b/Detectors/TRD/workflow/src/entropy-encoder-workflow.cxx index 177f6e4913a26..dff3831cd78a5 100644 --- a/Detectors/TRD/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/TRD/workflow/src/entropy-encoder-workflow.cxx @@ -23,7 +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{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); diff --git a/Detectors/ZDC/workflow/src/entropy-encoder-workflow.cxx b/Detectors/ZDC/workflow/src/entropy-encoder-workflow.cxx index 9ab0e10098f43..6f73fa121244f 100644 --- a/Detectors/ZDC/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/ZDC/workflow/src/entropy-encoder-workflow.cxx @@ -23,7 +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{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; std::swap(workflowOptions, options); From d19919cf22ca9fb08dfd71502aa7f4b5e0c96705 Mon Sep 17 00:00:00 2001 From: altsybee Date: Thu, 26 Feb 2026 12:28:48 +0100 Subject: [PATCH 304/701] [ALICE3] Change 'layoutOL' to 'layoutOT', update ALICE3/README with a config table (#15101) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * change 'layoutOL' to 'layoutOT' for consistency * small updates of readme * Update README.md * Document specific detector setup for TRK Added specific detector setup details and configurables for TRK detector. --------- Co-authored-by: Igor Altsybeev Co-authored-by: Nicolò Jacazio --- Detectors/Upgrades/ALICE3/README.md | 17 +++++++++++----- Detectors/Upgrades/ALICE3/TRK/README.md | 20 ++++++++++++++++++- .../TRK/base/include/TRKBase/GeometryTGeo.h | 2 +- .../TRK/base/include/TRKBase/TRKBaseParam.h | 4 ++-- .../ALICE3/TRK/base/src/GeometryTGeo.cxx | 6 +++--- .../ALICE3/TRK/macros/test/run_test.sh | 2 +- .../ALICE3/TRK/simulation/src/Detector.cxx | 8 ++++---- .../Upgrades/ALICE3/TRK/workflow/README.md | 2 +- 8 files changed, 43 insertions(+), 18 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/README.md b/Detectors/Upgrades/ALICE3/README.md index 7e1e1c03718d8..23d45232b71c9 100644 --- a/Detectors/Upgrades/ALICE3/README.md +++ b/Detectors/Upgrades/ALICE3/README.md @@ -21,7 +21,7 @@ The specific modules for Run 5 are enabled by passing their their IDs to the `-m A list of the available DetIDs is reproted in the table below: | Detector ID | Detector description | -|-------------|----------------------------------| +| ----------- | -------------------------------- | | `A3IP` | Beam pipe | | `TRK` | Barrel Tracker | | `TF3` | Time Of Flight detectors | @@ -49,7 +49,7 @@ export ALICE3_MAGFIELD_MACRO=../ALICE3Field.C An exampling macro for a custom magnetic field is stored in `Detectors/Upgrades/macros/ALICE3Field.C`. -### Run a simple simulation for run 5 +### Run a simple simulation for ALICE 3 The simplest command to be run to test the simulation is working is: ```bash @@ -61,13 +61,20 @@ To enable a specific set of modules, e.g. the beampipe and the TOFs one can spec ```bash o2-sim-run5 -n 10 -m A3IP TF3 ``` + +#### Specific detector setups + +Configurables for various sub-detectors are presented in the following Table: + +| Available options | Link to options | +| ----------------- | -------------------------------------------------------------- | +| TKR | [Link to TRK options](./TRK/README.md#specific-detector-setup) | + ### Output of the simulation The simulation will produce a `o2sim_Hits.root` file with a tree with the hits related to that detector. -Currently, hits are produced for: `TRK`, `FT3`, and `TF3`. -More detectors will be included. ## Reconstruction WIP ## Analysis -WIP \ No newline at end of file +WIP diff --git a/Detectors/Upgrades/ALICE3/TRK/README.md b/Detectors/Upgrades/ALICE3/TRK/README.md index 44937fb3663a6..8b3a7984bb233 100644 --- a/Detectors/Upgrades/ALICE3/TRK/README.md +++ b/Detectors/Upgrades/ALICE3/TRK/README.md @@ -6,5 +6,23 @@ This is top page for the TRK detector documentation. + +## Specific detector setup + + +Configurables for various sub-detectors are presented in the following Table: + +| Subsystem | Available options | Comments | +| ------------------ | ------------------------------------------------------- | ---------------------------------------------------------------- | +| `TRKBase.layoutVD` | `kIRIS4` (default), `kIRISFullCyl`, `kIRIS5`, `kIRIS4a` | [link to definitions](./base/include/TRKBase/TRKBaseParam.h) | +| `TRKBase.layoutML` | `kCylinder`, `kTurboStaves` (default), `kStaggered` | | +| `TRKBase.layoutOT` | `kCylinder`, `kTurboStaves`, `kStaggered` (default) | | + +For example, a geometry with fully cylindrical tracker barrel (for all layers in VD, ML and OT) can be obtained by +```bash +o2-sim-serial-run5 -n 1 -g pythia8hi -m A3IP TRK FT3 TF3 \ + --configKeyValues "TRKBase.layoutVD=kIRISFullCyl;TRKBase.layoutML=kCylinder;TRKBase.layoutOL=kCylinder" +``` + \ No newline at end of file +/doxy --> diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h index deec53950cd5f..bb1597f2967e4 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h @@ -223,7 +223,7 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache 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 + eLayout mLayoutOT; // Type of segmentation for the outer layers private: static std::unique_ptr sInstance; diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h index d5e11313c0f0c..f919839b7ed0a 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h @@ -46,11 +46,11 @@ struct TRKBaseParam : public o2::conf::ConfigurableParamHelper { 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 + eLayout layoutOT = 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; } + eLayout getLayoutOT() const { return layoutOT; } 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 d5d37ec00acef..b5535af781910 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx @@ -77,9 +77,9 @@ void GeometryTGeo::Build(int loadTrans) } mLayoutML = o2::trk::TRKBaseParam::Instance().getLayoutML(); - mLayoutOL = o2::trk::TRKBaseParam::Instance().getLayoutOL(); + mLayoutOT = o2::trk::TRKBaseParam::Instance().getLayoutOT(); - LOG(debug) << "Layout ML: " << mLayoutML << ", Layout OL: " << mLayoutOL; + LOG(debug) << "Layout ML: " << mLayoutML << ", Layout OL: " << mLayoutOT; mNumberOfLayersMLOT = extractNumberOfLayersMLOT(); mNumberOfPetalsVD = extractNumberOfPetalsVD(); @@ -405,7 +405,7 @@ TString GeometryTGeo::getMatrixPath(int index) const // 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)) { + if ((layer < 4 && mLayoutML == eLayout::kCylinder) || (layer > 3 && mLayoutOT == eLayout::kCylinder)) { stave = 1; mod = 1; chip = 1; diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/run_test.sh b/Detectors/Upgrades/ALICE3/TRK/macros/test/run_test.sh index 797d1d12af4ab..d0953a342af04 100644 --- a/Detectors/Upgrades/ALICE3/TRK/macros/test/run_test.sh +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/run_test.sh @@ -2,7 +2,7 @@ nEvents=10 # Simulating -o2-sim-serial-run5 -n $nEvents -g pythia8hi -m TRK --configKeyValues "TRKBase.layoutML=kTurboStaves;TRKBase.layoutOL=kStaggered;">& sim_TRK.log +o2-sim-serial-run5 -n $nEvents -g pythia8hi -m TRK --configKeyValues "TRKBase.layoutML=kTurboStaves;TRKBase.layoutOT=kStaggered;">& sim_TRK.log # Digitizing o2-sim-digitizer-workflow -b >& digiTRK.log diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx index 556b016f22553..a2629a101d2d0 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx @@ -146,10 +146,10 @@ void Detector::buildTRKMiddleOuterLayers() mLayers[3].setLayout(trkPars.layoutML); // Outer tracker - mLayers[4].setLayout(trkPars.layoutOL); - mLayers[5].setLayout(trkPars.layoutOL); - mLayers[6].setLayout(trkPars.layoutOL); - mLayers[7].setLayout(trkPars.layoutOL); + mLayers[4].setLayout(trkPars.layoutOT); + mLayers[5].setLayout(trkPars.layoutOT); + mLayers[6].setLayout(trkPars.layoutOT); + mLayers[7].setLayout(trkPars.layoutOT); } void Detector::configFromFile(std::string fileName) diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/README.md b/Detectors/Upgrades/ALICE3/TRK/workflow/README.md index afb30ed6dbdd3..1cdce15b72726 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/README.md +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/README.md @@ -98,7 +98,7 @@ Note that the `trackingparams` field can contain multiple sets of parameters for 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;" +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.layoutOT=kStaggered;" ``` This produces, among other files: From e59f5cb62c966c99b230eb37d6077f66b72b815c Mon Sep 17 00:00:00 2001 From: Stefano Cannito <143754257+scannito@users.noreply.github.com> Date: Fri, 27 Feb 2026 00:00:16 +0100 Subject: [PATCH 305/701] [ALICE3] TRK: fix extrusions and overlaps b/n staves (#15105) --- .../include/TRKSimulation/TRKLayer.h | 19 ++++++++++++------- .../ALICE3/TRK/simulation/src/TRKLayer.cxx | 15 +++++---------- 2 files changed, 17 insertions(+), 17 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h index 0a7a45e87bfd8..39dd7752cc010 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h @@ -51,21 +51,26 @@ class TRKLayer private: // TGeo objects outside logical volumes can cause errors. Only used in case of kStaggered and kTurboStaves layouts - static constexpr float mLogicalVolumeThickness = 1; + static constexpr float mLogicalVolumeThickness = 1.3; + // User defined parameters for the layer, to be set in the constructor int mLayerNumber; - eLayout mLayout; std::string mLayerName; float mInnerRadius; float mOuterRadius; int mNumberOfModules; float mX2X0; - float mChipWidth; - float mChipLength; float mChipThickness; - float mDeadzoneWidth; - float mSensorThickness; - int mHalfNumberOfChips; + + // Fixed parameters for the layer, to be set based on the specifications of the chip and module + eLayout mLayout = kCylinder; + float mChipWidth = constants::moduleMLOT::chip::width; + float mChipLength = constants::moduleMLOT::chip::length; + float mDeadzoneWidth = constants::moduleMLOT::chip::passiveEdgeReadOut; + float mSensorThickness = constants::moduleMLOT::silicon::thickness; + int mHalfNumberOfChips = 4; + + static constexpr float Si_X0 = 9.5f; ClassDef(TRKLayer, 2); }; diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx index 82b6fbd40af59..53cc6ab11850d 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx @@ -26,17 +26,15 @@ namespace o2 namespace trk { 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) + : mLayerNumber(layerNumber), mLayerName(layerName), mInnerRadius(rInn), mOuterRadius(rOut), mNumberOfModules(numberOfModules), mX2X0(layerX2X0) { - float Si_X0 = 9.5f; mChipThickness = mX2X0 * Si_X0; LOGP(info, "Creating layer: id: {} rInner: {} rOuter: {} zLength: {} x2X0: {}", mLayerNumber, mInnerRadius, mOuterRadius, getZ(), mX2X0); } 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) + : mLayerNumber(layerNumber), mLayerName(layerName), mInnerRadius(rInn), mNumberOfModules(numberOfModules), mChipThickness(thick) { - float Si_X0 = 9.5f; mOuterRadius = rInn + thick; mX2X0 = mChipThickness / Si_X0; LOGP(info, "Creating layer: id: {} rInner: {} rOuter: {} zLength: {} x2X0: {}", mLayerNumber, mInnerRadius, mOuterRadius, getZ(), mX2X0); @@ -300,12 +298,9 @@ TGeoVolume* TRKLayer::createStave(std::string type) } else if (type == "staggered") { 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); + staveVol = new TGeoVolumeAssembly(staveName.c_str()); // Put the half staves in the correct position TGeoVolume* halfStaveVolLeft = createHalfStave("flat"); @@ -379,7 +374,7 @@ 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 + 3, 0, 0); + TGeoRotation* rot = new TGeoRotation("rot", theta - 90 + 4, 0, 0); trans->SetRotation(rot); trans->SetTranslation(mInnerRadius * std::cos(2. * TMath::Pi() * iStave / nStaves), mInnerRadius * std::sin(2 * TMath::Pi() * iStave / nStaves), 0); @@ -413,7 +408,7 @@ 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 + 3, 0, 0); + TGeoRotation* rot = new TGeoRotation("rot", theta - 90, 0, 0); trans->SetRotation(rot); trans->SetTranslation(mInnerRadius * std::cos(2. * TMath::Pi() * iStave / nStaves), mInnerRadius * std::sin(2 * TMath::Pi() * iStave / nStaves), 0); From eedf394f1e426515d02297fe43afdf85d1425b3d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Fri, 27 Feb 2026 16:22:28 +0200 Subject: [PATCH 306/701] [ALICE3] Update README.md with additional detector options (#15106) * Update README.md with additional detector options * Add files via upload --- Detectors/Upgrades/ALICE3/IOTOF/README.md | 36 +++++++++++++++++++++++ Detectors/Upgrades/ALICE3/README.md | 7 +++-- 2 files changed, 40 insertions(+), 3 deletions(-) create mode 100644 Detectors/Upgrades/ALICE3/IOTOF/README.md diff --git a/Detectors/Upgrades/ALICE3/IOTOF/README.md b/Detectors/Upgrades/ALICE3/IOTOF/README.md new file mode 100644 index 0000000000000..044798076b485 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/README.md @@ -0,0 +1,36 @@ + + +# ALICE 3 TOF system + +This is top page for the TOF detector documentation. + + +## Specific detector setup + + +Configurables for various sub-detectors are presented in the following Table: + +[link to definitions](./base/include/IOTOFBase/IOTOFBaseParam.h) + +| Options | Choices | Comments | +| ----------------------------- | ---------------------------------------------------------------- | ------------------------------------------- | +| `IOTOFBase.enableInnerTOF` | `true` (default), `false` | Enable inner TOF barrel layer | +| `IOTOFBase.enableOuterTOF` | `true` (default), `false` | Enable outer TOF barrel layer | +| `IOTOFBase.enableForwardTOF` | `true` (default), `false` | Enable forward TOF endcap | +| `IOTOFBase.enableBackwardTOF` | `true` (default), `false` | Enable backward TOF endcap | +| `IOTOFBase.segmentedInnerTOF` | `false` (default), `true` | Use segmented geometry for inner TOF | +| `IOTOFBase.segmentedOuterTOF` | `false` (default), `true` | Use segmented geometry for outer TOF | +| `IOTOFBase.detectorPattern` | ` ` (default), `v3b`, `v3b1a`, `v3b1b`, `v3b2a`, `v3b2b`, `v3b3` | Optional layout pattern | +| ----------------------------- | ------------------------- | ------------------------------------------- | + + +For example, a geometry with fully cylindrical tracker barrel (for all layers in VD, ML and OT) can be obtained by +```bash +o2-sim-serial-run5 -n 1 -g pythia8hi -m A3IP TF3 \ + --configKeyValues "IOTOFBase.detectorPattern=v3b1a;IOTOFBase.segmentedInnerTOF=true;IOTOFBase.segmentedOuterTOF=true;FT3Base.geoModel=1;FT3Base.nLayers=1;IOTOFBase.enableOuterTOF=false;IOTOFBase.enableBackwardTOF=false;IOTOFBase.enableForwardTOF=false;" +``` + + diff --git a/Detectors/Upgrades/ALICE3/README.md b/Detectors/Upgrades/ALICE3/README.md index 23d45232b71c9..44a478b592882 100644 --- a/Detectors/Upgrades/ALICE3/README.md +++ b/Detectors/Upgrades/ALICE3/README.md @@ -66,9 +66,10 @@ o2-sim-run5 -n 10 -m A3IP TF3 Configurables for various sub-detectors are presented in the following Table: -| Available options | Link to options | -| ----------------- | -------------------------------------------------------------- | -| TKR | [Link to TRK options](./TRK/README.md#specific-detector-setup) | +| Available options | Link to options | +| ----------------- | ---------------------------------------------------------------- | +| TRK | [Link to TRK options](./TRK/README.md#specific-detector-setup) | +| TOF | [Link to TOF options](./IOTOF/README.md#specific-detector-setup) | ### Output of the simulation The simulation will produce a `o2sim_Hits.root` file with a tree with the hits related to that detector. From f4f4d35ae108b8b1bf7e1d82e9f004bb69700a3b Mon Sep 17 00:00:00 2001 From: Ernst Hellbar Date: Thu, 26 Feb 2026 12:29:33 +0100 Subject: [PATCH 307/701] DPL: clean up leftovers from input stream after parsing a workflow --- .../Core/src/WorkflowSerializationHelpers.cxx | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/Framework/Core/src/WorkflowSerializationHelpers.cxx b/Framework/Core/src/WorkflowSerializationHelpers.cxx index e20e23f98c90b..9624a2dfd0d3e 100644 --- a/Framework/Core/src/WorkflowSerializationHelpers.cxx +++ b/Framework/Core/src/WorkflowSerializationHelpers.cxx @@ -29,6 +29,7 @@ #include O2_DECLARE_DYNAMIC_LOG(workflow_importer); +O2_DECLARE_DYNAMIC_LOG(post_workflow_importer); namespace o2::framework { @@ -969,7 +970,18 @@ bool WorkflowSerializationHelpers::import(std::istream& s, WorkflowImporter importer{workflow, metadata, command}; bool ok = reader.Parse(isw, importer); if (ok == false) { - throw std::runtime_error("Error while parsing serialised workflow"); + if (s.eof()) { + throw std::runtime_error("Error while parsing serialised workflow"); + } + // clean up possible leftovers at the end of the input stream, e.g. [DEBUG] message from destructors + O2_SIGNPOST_ID_GENERATE(sid, post_workflow_importer); + while (true) { + s.getline(buf, 1024, '\n'); + if (s.eof()) { + break; + } + O2_SIGNPOST_EVENT_EMIT(post_workflow_importer, sid, "post import", "Following leftover line found in input stream after parsing workflow: %{public}s", buf); + } } return true; } From 497abe03821aad1de722f6c5889fe9e0bc338441 Mon Sep 17 00:00:00 2001 From: Kangkan Goswami Date: Sat, 28 Feb 2026 20:21:39 +0530 Subject: [PATCH 308/701] Integrate TRD extra data support in AOD production workflow (#15108) * Add TRD support and local gain/noise handling in AODProducerWorkflowSpec This commit enables the workflow to process TRD-specific calibration and noise information, allowing for more accurate reconstruction and analysis of TRD signals in AOD production. * Implement TRD extra data processing in AODProducerWorkflowSpec.cxx This commit enables the workflow to generate TRD-specific extra tables in the AOD, applying calibration and noise corrections to improve reconstruction fidelity for TRD tracks. * Add TRD extra configuration option to AOD producer workflow This change allows users to enable or disable TRD extra output at runtime via workflow configuration, integrating TRD processing into the AOD production workflow. * Refactor TRD dynamic columns and define TRDsExtra SOA table in AnalysisDataModel.h This update to AnalysisDataModel.h standardizes TRD-related naming and introduces a dedicated SOA table for TRD extra data, allowing calibrated and corrected TRD quantities to be stored within the AOD framework. * fix TRD extra information * clang format * Adjust TRD logic after rebase * Please consider the following formatting changes --------- Co-authored-by: Gauthier Legras Co-authored-by: ALICE Action Bot --- .../AODProducerWorkflowSpec.h | 20 ++- Detectors/AOD/src/AODProducerWorkflowSpec.cxx | 155 +++++++++++++++++- Detectors/AOD/src/aod-producer-workflow.cxx | 4 +- .../include/Framework/AnalysisDataModel.h | 29 +++- 4 files changed, 193 insertions(+), 15 deletions(-) diff --git a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h index 2c58db42ed856..588cd575ee7f5 100644 --- a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h +++ b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h @@ -19,6 +19,10 @@ #include "DataFormatsGlobalTracking/RecoContainer.h" #include "DataFormatsPHOS/Cell.h" #include "DataFormatsTRD/TrackTRD.h" +#include "TRDBase/PadCalibrationsAliases.h" +#include "DataFormatsTRD/NoiseCalibration.h" +#include "DataFormatsTRD/CalGain.h" +#include "DataFormatsTRD/Constants.h" #include "DetectorsBase/GRPGeomHelper.h" #include "DetectorsBase/Propagator.h" #include "Framework/DataProcessorSpec.h" @@ -215,7 +219,7 @@ enum struct AODProducerStreamerFlags : uint8_t { class AODProducerWorkflowDPL : public Task { public: - AODProducerWorkflowDPL(GID::mask_t src, std::shared_ptr dataRequest, std::shared_ptr gr, bool enableSV, bool useMC = true, bool enableFITextra = false) : mUseMC(useMC), mEnableSV(enableSV), mEnableFITextra(enableFITextra), mInputSources(src), mDataRequest(dataRequest), mGGCCDBRequest(gr) {} + AODProducerWorkflowDPL(GID::mask_t src, std::shared_ptr dataRequest, std::shared_ptr gr, bool enableSV, bool useMC = true, bool enableFITextra = false, bool enableTRDextra = false) : mUseMC(useMC), mEnableSV(enableSV), mEnableFITextra(enableFITextra), mEnableTRDextra(enableTRDextra), mInputSources(src), mDataRequest(dataRequest), mGGCCDBRequest(gr) {} ~AODProducerWorkflowDPL() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -250,6 +254,10 @@ class AODProducerWorkflowDPL : public Task o2::dataformats::MeanVertexObject mVtx; float mMaxPropXiu{5.0f}; // max X_IU for which track is to be propagated if mPropTracks is true. (other option: o2::constants::geom::XTPCInnerRef + 0.1f) + const o2::trd::LocalGainFactor* mTRDLocalGain; // TRD local gain factors from krypton calibration + const o2::trd::CalGain* mTRDGainCalib; // TRD time-dependent gain calib at chamber level + const o2::trd::NoiseStatusMCM* mTRDNoiseMap; // TRD noise map + std::unordered_set mGIDUsedBySVtx; std::unordered_set mGIDUsedByStr; @@ -261,6 +269,7 @@ class AODProducerWorkflowDPL : public Task bool mUseSigFiltMC = false; // enable signal filtering for MC with embedding bool mEnableSV = true; // enable secondary vertices bool mEnableFITextra = false; + bool mEnableTRDextra = false; bool mFieldON = false; const float cSpeed = 0.029979246f; // speed of light in TOF units @@ -278,6 +287,7 @@ class AODProducerWorkflowDPL : public Task TStopwatch mTimer; bool mEMCselectLeading{false}; uint64_t mEMCALTrgClassMask = 0; + size_t mCurrentTRDTrigID = 0; // current index of the TRD trigger record, to speed up search // unordered map connects global indices and table indices of barrel tracks std::unordered_map mGIDToTableID; @@ -525,6 +535,9 @@ class AODProducerWorkflowDPL : public Task template void addToTracksQATable(TracksQACursorType& tracksQACursor, TrackQA& trackQAInfoHolder); + template + void addToTRDsExtra(const o2::globaltracking::RecoContainer& recoData, TRDsExtraCursorType& trdExtraCursor, const GIndex& trkIdx, int trkTableIdx); + template void addToMFTTracksTable(mftTracksCursorType& mftTracksCursor, AmbigMFTTracksCursorType& ambigMFTTracksCursor, GIndex trackID, const o2::globaltracking::RecoContainer& data, int collisionID, @@ -544,7 +557,7 @@ class AODProducerWorkflowDPL : public Task // helper for track tables // * fills tables collision by collision // * interaction time is for TOF information - template void fillTrackTablesPerCollision(int collisionID, @@ -556,6 +569,7 @@ class AODProducerWorkflowDPL : public Task TracksCovCursorType& tracksCovCursor, TracksExtraCursorType& tracksExtraCursor, TracksQACursorType& tracksQACursor, + TRDsExtraCursorType& trdsExtraCursor, AmbigTracksCursorType& ambigTracksCursor, MFTTracksCursorType& mftTracksCursor, MFTTracksCovCursorType& mftTracksCovCursor, @@ -680,7 +694,7 @@ class AODProducerWorkflowDPL : public Task }; /// create a processor spec -framework::DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, bool enableST, bool useMC, bool CTPConfigPerRun, bool enableFITextra); +framework::DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, bool enableST, bool useMC, bool CTPConfigPerRun, bool enableFITextra, bool enableTRDextra); // helper interface for calo cells to "befriend" emcal and phos cells class CellHelper diff --git a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx index 852419a9895eb..fcb419d6c441b 100644 --- a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx +++ b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx @@ -33,7 +33,6 @@ #include "DataFormatsPHOS/TriggerRecord.h" #include "DataFormatsPHOS/EventHandler.h" #include "DataFormatsTPC/TrackTPC.h" -#include "DataFormatsTRD/TriggerRecord.h" #include "DataFormatsZDC/BCRecData.h" #include "DataFormatsZDC/ZDCEnergy.h" #include "DataFormatsZDC/ZDCTDCData.h" @@ -45,6 +44,9 @@ #include "CommonDataFormat/InteractionRecord.h" #include "DataFormatsTRD/TrackTRD.h" #include "DataFormatsTRD/TrackTriggerRecord.h" +#include "DataFormatsTRD/CalibratedTracklet.h" +#include "DataFormatsTRD/TriggerRecord.h" +#include "DataFormatsTRD/Tracklet64.h" #include "DataFormatsGlobalTracking/RecoContainer.h" #include "Framework/AnalysisDataModel.h" #include "Framework/ConfigParamRegistry.h" @@ -390,6 +392,121 @@ void AODProducerWorkflowDPL::addToTracksQATable(TracksQACursorType& tracksQACurs mTrackQCRetainOnlydEdx ? std::numeric_limits::min() : trackQAInfoHolder.dTofdZ); } +template +void AODProducerWorkflowDPL::addToTRDsExtra(const o2::globaltracking::RecoContainer& recoData, TRDsExtraCursorType& trdExtraCursor, const GIndex& trkIdx, int trkTableIdx) +{ + int q0s[6] = {-1}, q1s[6] = {-1}, q2s[6] = {-1}; + float q0sCor[6] = {-1}, q1sCor[6] = {-1}, q2sCor[6] = {-1}; + float ttgls[6] = {-999}, tphis[6] = {-999}; + + auto contributorsGID = recoData.getSingleDetectorRefs(trkIdx); + if (!contributorsGID[GIndex::Source::TRD].isIndexSet()) { // should be redunant + return; + } + const auto& trk = recoData.getTrack(contributorsGID[GIndex::Source::TRD]); + o2::track::TrackPar trkC{contributorsGID[GIndex::Source::ITSTPC].isIndexSet() ? recoData.getTPCITSTrack(contributorsGID[GIndex::Source::ITSTPC]).getParamOut() : recoData.getTPCTrack(contributorsGID[GIndex::Source::TPC]).getParamOut()}; + const auto& trklets = recoData.getTRDTracklets(); + const auto& ctrklets = recoData.getTRDCalibratedTracklets(); + for (int iLay{0}; iLay < 6; ++iLay) { + q0s[iLay] = q1s[iLay] = q2s[iLay] = -1; + q0sCor[iLay] = q1sCor[iLay] = q2sCor[iLay] = -1; + tphis[iLay] = ttgls[iLay] = -999; + auto trkltId = trk.getTrackletIndex(iLay); + if (trkltId < 0) { + continue; + } + const auto& tracklet = trklets[trkltId]; + if (mTRDNoiseMap->isTrackletFromNoisyMCM(tracklet)) { + continue; + } + // we need to propagate into TRD local system + int trkltDet = tracklet.getDetector(); + int trkltSec = trkltDet / 30; + if (trkltSec != o2::math_utils::angle2Sector(trkC.getAlpha())) { + if (!trkC.rotate(o2::math_utils::sector2Angle(trkltSec))) { + break; + } + } + if (!o2::base::Propagator::Instance()->PropagateToXBxByBz(trkC, ctrklets[trkltId].getX(), o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, mMatCorr)) { + break; + } + + auto tphi = trkC.getSnp() / std::sqrt((1.f - trkC.getSnp()) * (1.f + trkC.getSnp())); + auto trackletLength = std::sqrt(1.f + tphi * tphi + trkC.getTgl() * trkC.getTgl()); + float cor = mTRDLocalGain->getValue(tracklet.getHCID() / 2, tracklet.getPadCol(), tracklet.getPadRow()) * mTRDGainCalib->getMPVdEdx(tracklet.getDetector()) / o2::trd::constants::MPVDEDXDEFAULT * trackletLength; + q0s[iLay] = tracklet.getQ0(); + q1s[iLay] = tracklet.getQ1(); + q2s[iLay] = tracklet.getQ2(); + q0sCor[iLay] = (float)tracklet.getQ0() / cor; + q1sCor[iLay] = (float)tracklet.getQ1() / cor; + q2sCor[iLay] = (float)tracklet.getQ2() / cor; + ttgls[iLay] = trkC.getTgl(); + tphis[iLay] = tphi; + + // z-row merging, we want to merge only with tracklets from the same trigger record + if (trk.getIsCrossingNeighbor(iLay) && trk.getHasNeighbor()) { + // find the trigger the tracklet belongs to + auto trigsTRD = recoData.getTRDTriggerRecords(); + size_t trdSelID = -1; + + const auto& trig = trigsTRD[mCurrentTRDTrigID]; + bool foundTRDTrigger = false; + // first check current trigger + if (trkltId >= trig.getFirstTracklet() && trkltId < trig.getFirstTracklet() + trig.getNumberOfTracklets()) { + trdSelID = mCurrentTRDTrigID; + foundTRDTrigger = true; + } else { + // then check next trigger + if (mCurrentTRDTrigID < trigsTRD.size() - 1) { + const auto& trig = trigsTRD[mCurrentTRDTrigID + 1]; + if (trkltId >= trig.getFirstTracklet() && trkltId < trig.getFirstTracklet() + trig.getNumberOfTracklets()) { + trdSelID = mCurrentTRDTrigID + 1; + foundTRDTrigger = true; + } + } + } + + size_t low = 0, up = trigsTRD.size() - 1; + + // otherwise binary search + while (low <= up && !foundTRDTrigger) { + trdSelID = low + std::floor((up - low) / 2); + const auto& trig = trigsTRD[trdSelID]; + if (trig.getFirstTracklet() > trkltId) { + up = trdSelID - 1; + } else { + if (trig.getFirstTracklet() + trig.getNumberOfTracklets() <= trkltId) { + low = trdSelID + 1; + } else { + foundTRDTrigger = true; + } + } + } + //------------------- + mCurrentTRDTrigID = trdSelID; + const auto& trigSel = trigsTRD[trdSelID]; + + // loop on other tracklets from the same trigger record + for (const auto& trklt : trklets.subspan(trigSel.getFirstTracklet(), trigSel.getNumberOfTracklets())) { + if (tracklet.getTrackletWord() == trklt.getTrackletWord() || tracklet.getDetector() != trklt.getDetector()) { + continue; + } + if (std::abs(tracklet.getPadCol() - trklt.getPadCol()) <= 1 && std::abs(tracklet.getPadRow() - trklt.getPadRow()) == 1) { + cor = mTRDLocalGain->getValue(trklt.getHCID() / 2, trklt.getPadCol(), trklt.getPadRow()) * mTRDGainCalib->getMPVdEdx(tracklet.getDetector()) / o2::trd::constants::MPVDEDXDEFAULT * trackletLength; + q0s[iLay] += trklt.getQ0(); + q1s[iLay] += trklt.getQ1(); + q2s[iLay] += trklt.getQ2(); + q0sCor[iLay] += (float)trklt.getQ0() / cor; + q1sCor[iLay] += (float)trklt.getQ1() / cor; + q2sCor[iLay] += (float)trklt.getQ2() / cor; + } + } + } + } + + trdExtraCursor(trkTableIdx, q0s, q1s, q2s, q0sCor, q1sCor, q2sCor, ttgls, tphis); +} + template void AODProducerWorkflowDPL::addToMFTTracksTable(mftTracksCursorType& mftTracksCursor, AmbigMFTTracksCursorType& ambigMFTTracksCursor, GIndex trackID, const o2::globaltracking::RecoContainer& data, int collisionID, @@ -430,8 +547,7 @@ void AODProducerWorkflowDPL::addToMFTTracksTable(mftTracksCursorType& mftTracksC ambigMFTTracksCursor(mTableTrMFTID, bcSlice); } } - -template void AODProducerWorkflowDPL::fillTrackTablesPerCollision(int collisionID, @@ -443,6 +559,7 @@ void AODProducerWorkflowDPL::fillTrackTablesPerCollision(int collisionID, TracksCovCursorType& tracksCovCursor, TracksExtraCursorType& tracksExtraCursor, TracksQACursorType& tracksQACursor, + TRDsExtraCursor& trdsExtraCursor, AmbigTracksCursorType& ambigTracksCursor, MFTTracksCursorType& mftTracksCursor, MFTTracksCovCursorType& mftTracksCovCursor, @@ -553,7 +670,9 @@ void AODProducerWorkflowDPL::fillTrackTablesPerCollision(int collisionID, addToTracksTable(tracksCursor, tracksCovCursor, trOrig, collisionID, aod::track::TrackIU); } addToTracksExtraTable(tracksExtraCursor, extraInfoHolder); - + if (mEnableTRDextra && trackIndex.includesDet(GIndex::Source::TRD)) { + addToTRDsExtra(data, trdsExtraCursor, trackIndex, mTableTrID); + } // collecting table indices of barrel tracks for V0s table if (extraInfoHolder.bcSlice[0] >= 0 && collisionID < 0) { ambigTracksCursor(mTableTrID, extraInfoHolder.bcSlice); @@ -1934,6 +2053,12 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) auto cpvClustersCursor = createTableCursor(pc); auto originCursor = createTableCursor(pc); + /// Extra tables + o2::framework::Produces trdExtraCursor; + if (mEnableTRDextra) { + trdExtraCursor = createTableCursor(pc); + } + // Declare MC cursors type without adding the output for a table o2::framework::Produces mcColLabelsCursor; o2::framework::Produces mcCollisionsCursor; @@ -2294,14 +2419,16 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) } } + mCurrentTRDTrigID = 0; // reinitialize index for TRD trigger record search // filling unassigned tracks first // so that all unassigned tracks are stored in the beginning of the table together auto& trackRef = primVer2TRefs.back(); // references to unassigned tracks are at the end // fixme: interaction time is undefined for unassigned tracks (?) - fillTrackTablesPerCollision(-1, std::uint64_t(-1), trackRef, primVerGIs, recoData, tracksCursor, tracksCovCursor, tracksExtraCursor, tracksQACursor, + fillTrackTablesPerCollision(-1, std::uint64_t(-1), trackRef, primVerGIs, recoData, tracksCursor, tracksCovCursor, tracksExtraCursor, tracksQACursor, trdExtraCursor, ambigTracksCursor, mftTracksCursor, mftTracksCovCursor, ambigMFTTracksCursor, fwdTracksCursor, fwdTracksCovCursor, ambigFwdTracksCursor, fwdTrkClsCursor, bcsMap); + mCurrentTRDTrigID = 0; // reinitialize index for TRD trigger record search // filling collisions and tracks into tables collisionID = 0; collisionsCursor.reserve(primVertices.size()); @@ -2340,7 +2467,7 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) auto& trackRef = primVer2TRefs[collisionID]; // passing interaction time in [ps] - fillTrackTablesPerCollision(collisionID, globalBC, trackRef, primVerGIs, recoData, tracksCursor, tracksCovCursor, tracksExtraCursor, tracksQACursor, ambigTracksCursor, + fillTrackTablesPerCollision(collisionID, globalBC, trackRef, primVerGIs, recoData, tracksCursor, tracksCovCursor, tracksExtraCursor, tracksQACursor, trdExtraCursor, ambigTracksCursor, mftTracksCursor, mftTracksCovCursor, ambigMFTTracksCursor, fwdTracksCursor, fwdTracksCovCursor, ambigFwdTracksCursor, fwdTrkClsCursor, bcsMap); collisionID++; @@ -3011,6 +3138,11 @@ void AODProducerWorkflowDPL::updateTimeDependentParams(ProcessingContext& pc) mFieldON = std::abs(o2::base::Propagator::Instance()->getNominalBz()) > 0.01; pc.inputs().get("ctpconfig"); + if (mEnableTRDextra) { + mTRDLocalGain = pc.inputs().get("trdlocalgainfactors").get(); + mTRDNoiseMap = pc.inputs().get("trdnoisemap").get(); + mTRDGainCalib = pc.inputs().get("trdgaincalib").get(); // time dependent gain + } } if (mPropTracks) { pc.inputs().get("meanvtx"); @@ -3222,7 +3354,7 @@ void AODProducerWorkflowDPL::endOfStream(EndOfStreamContext& /*ec*/) mStreamer.reset(); } -DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, bool enableStrangenessTracking, bool useMC, bool CTPConfigPerRun, bool enableFITextra) +DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, bool enableStrangenessTracking, bool useMC, bool CTPConfigPerRun, bool enableFITextra, bool enableTRDextra) { auto dataRequest = std::make_shared(); dataRequest->inputs.emplace_back("ctpconfig", "CTP", "CTPCONFIG", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/Config", CTPConfigPerRun)); @@ -3313,6 +3445,13 @@ DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, boo OutputSpec{"TFF", "TFFilename"}, OutputSpec{"AMD", "AODMetadataKeys"}, OutputSpec{"AMD", "AODMetadataVals"}}; + /// Extra tables + if (enableTRDextra) { + outputs.push_back(OutputForTable::spec()); + dataRequest->inputs.emplace_back("trdlocalgainfactors", "TRD", "LOCALGAINFACTORS", 0, Lifetime::Condition, ccdbParamSpec("TRD/Calib/LocalGainFactor")); + dataRequest->inputs.emplace_back("trdnoisemap", "TRD", "NOISEMAP", 0, Lifetime::Condition, ccdbParamSpec("TRD/Calib/NoiseMapMCM")); + dataRequest->inputs.emplace_back("trdgaincalib", "TRD", "CALGAIN", 0, Lifetime::Condition, ccdbParamSpec("TRD/Calib/CalGain")); + } if (useMC) { outputs.insert(outputs.end(), @@ -3336,7 +3475,7 @@ DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, boo "aod-producer-workflow", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(src, dataRequest, ggRequest, enableSV, useMC, enableFITextra)}, + AlgorithmSpec{adaptFromTask(src, dataRequest, ggRequest, enableSV, useMC, enableFITextra, enableTRDextra)}, Options{ ConfigParamSpec{"run-number", VariantType::Int64, -1L, {"The run-number. If left default we try to get it from DPL header."}}, ConfigParamSpec{"aod-timeframe-id", VariantType::Int64, -1L, {"Set timeframe number"}}, diff --git a/Detectors/AOD/src/aod-producer-workflow.cxx b/Detectors/AOD/src/aod-producer-workflow.cxx index 81e178642e403..f6bfaae170bbd 100644 --- a/Detectors/AOD/src/aod-producer-workflow.cxx +++ b/Detectors/AOD/src/aod-producer-workflow.cxx @@ -38,6 +38,7 @@ void customize(std::vector& workflowOptions) {"disable-secondary-vertices", o2::framework::VariantType::Bool, false, {"disable filling secondary vertices"}}, {"disable-strangeness-tracker", o2::framework::VariantType::Bool, false, {"disable filling strangeness tracking"}}, {"enable-FIT-extra", o2::framework::VariantType::Bool, false, {"enable FIT extra output"}}, + {"enable-TRD-extra", o2::framework::VariantType::Bool, false, {"enable TRD extra output"}}, {"info-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of sources to use"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}, {"combine-source-devices", o2::framework::VariantType::Bool, false, {"merge DPL source devices"}}, @@ -56,6 +57,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) bool enableST = !configcontext.options().get("disable-strangeness-tracker"); bool ctpcfgperrun = !configcontext.options().get("ctpconfig-run-independent"); bool enableFITextra = configcontext.options().get("enable-FIT-extra"); + bool enableTRDextra = configcontext.options().get("enable-TRD-extra"); GID::mask_t allowedSrc = GID::getSourcesMask("ITS,MFT,MCH,MID,MCH-MID,TPC,TRD,ITS-TPC,TPC-TOF,TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD,TPC-TRD-TOF,ITS-TPC-TRD-TOF,MFT-MCH,FT0,FV0,FDD,ZDC,EMC,CTP,PHS,CPV,HMP"); GID::mask_t src = allowedSrc & GID::getSourcesMask(configcontext.options().get("info-sources")); @@ -66,7 +68,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) } WorkflowSpec specs; - specs.emplace_back(o2::aodproducer::getAODProducerWorkflowSpec(src, enableSV, enableST, useMC, ctpcfgperrun, enableFITextra)); + specs.emplace_back(o2::aodproducer::getAODProducerWorkflowSpec(src, enableSV, enableST, useMC, ctpcfgperrun, enableFITextra, enableTRDextra)); auto srcCls = src & ~(GID::getSourceMask(GID::MCH) | GID::getSourceMask(GID::MID)); // Don't read global MID and MCH clusters (those attached to tracks are always read) auto srcMtc = src; diff --git a/Framework/Core/include/Framework/AnalysisDataModel.h b/Framework/Core/include/Framework/AnalysisDataModel.h index e3032830beaac..9f48685820634 100644 --- a/Framework/Core/include/Framework/AnalysisDataModel.h +++ b/Framework/Core/include/Framework/AnalysisDataModel.h @@ -470,13 +470,13 @@ DECLARE_SOA_DYNAMIC_COLUMN(TPCFractionSharedCls, tpcFractionSharedCls, //! Fract return (float)tpcNClsShared / (float)tpcNClsFound; }); -DECLARE_SOA_DYNAMIC_COLUMN(TRDHasNeighbor, trdPattern, //! Flag to check if at least one tracklet of a TRD Track has a neighboring tracklet +DECLARE_SOA_DYNAMIC_COLUMN(TRDHasNeighbor, trdHasNeighbor, //! Flag to check if at least one tracklet of a TRD Track has a neighboring tracklet [](uint8_t trdPattern) -> bool { return trdPattern & o2::aod::track::HasNeighbor; }); -DECLARE_SOA_DYNAMIC_COLUMN(TRDHasCrossing, trdPattern, //! Flag to check if at least one tracklet of a TRD Track crossed a padrow +DECLARE_SOA_DYNAMIC_COLUMN(TRDHasCrossing, trdHasCrossing, //! Flag to check if at least one tracklet of a TRD Track crossed a padrow [](uint8_t trdPattern) -> bool { return trdPattern & o2::aod::track::HasCrossing; }); -DECLARE_SOA_DYNAMIC_COLUMN(TRDNLayers, trdPattern, //! Number of TRD tracklets in a Track +DECLARE_SOA_DYNAMIC_COLUMN(TRDNTracklets, trdNTracklets, //! Number of TRD tracklets in a Track [](uint8_t trdPattern) -> std::size_t { return std::bitset<6>(trdPattern).count(); }); } // namespace track @@ -589,6 +589,7 @@ DECLARE_SOA_TABLE_FULL(StoredTracksExtra_000, "TracksExtra", "AOD", "TRACKEXTRA" track::TPCCrossedRowsOverFindableCls, track::TPCFoundOverFindableCls, track::TPCFractionSharedCls, + track::TRDHasCrossing, track::TRDHasNeighbor, track::TRDNTracklets, track::TrackEtaEMCAL, track::TrackPhiEMCAL, track::TrackTime, track::TrackTimeRes); DECLARE_SOA_TABLE_FULL_VERSIONED(StoredTracksExtra_001, "TracksExtra", "AOD", "TRACKEXTRA", 1, // On disk version of TracksExtra, version 1 @@ -618,6 +619,7 @@ DECLARE_SOA_TABLE_FULL_VERSIONED(StoredTracksExtra_001, "TracksExtra", "AOD", "T track::TPCCrossedRowsOverFindableCls, track::TPCFoundOverFindableCls, track::TPCFractionSharedCls, + track::TRDHasCrossing, track::TRDHasNeighbor, track::TRDNTracklets, track::TrackEtaEMCAL, track::TrackPhiEMCAL, track::TrackTime, track::TrackTimeRes); DECLARE_SOA_TABLE_FULL_VERSIONED(StoredTracksExtra_002, "TracksExtra", "AOD", "TRACKEXTRA", 2, // On disk version of TracksExtra, version 2 @@ -648,6 +650,7 @@ DECLARE_SOA_TABLE_FULL_VERSIONED(StoredTracksExtra_002, "TracksExtra", "AOD", "T track::TPCCrossedRowsOverFindableCls, track::TPCFoundOverFindableCls, track::TPCFractionSharedCls, + track::TRDHasCrossing, track::TRDHasNeighbor, track::TRDNTracklets, track::TrackEtaEMCAL, track::TrackPhiEMCAL, track::TrackTime, track::TrackTimeRes); DECLARE_SOA_EXTENDED_TABLE(TracksExtra_000, StoredTracksExtra_000, "EXTRACKEXTRA", 0, //! Additional track information (clusters, PID, etc.) @@ -1610,6 +1613,26 @@ DECLARE_SOA_TABLE(FDDsExtra, "AOD", "FDDEXTRA", //! FDDsExtra table fdd::TimeFDDA, fdd::TimeFDDC); using FDDExtra = FDDsExtra::iterator; +namespace trd +{ +DECLARE_SOA_INDEX_COLUMN(Track, track); //! Track index +DECLARE_SOA_COLUMN(TRDQ0s, trdQ0s, int[6]); //! Q0 charge (un-corrected) +DECLARE_SOA_COLUMN(TRDQ1s, trdQ1s, int[6]); //! Q1 charge (un-corrected) +DECLARE_SOA_COLUMN(TRDQ2s, trdQ2s, int[6]); //! Q2 charge (un-corrected) +DECLARE_SOA_COLUMN(TRDQ0sCorrected, trdQ0sCorrected, float[6]); //! Q0 charge (corrected) +DECLARE_SOA_COLUMN(TRDQ1sCorrected, trdQ1sCorrected, float[6]); //! Q1 charge (corrected) +DECLARE_SOA_COLUMN(TRDQ2sCorrected, trdQ2sCorrected, float[6]); //! Q2 charge (corrected) +DECLARE_SOA_COLUMN(TRDTgls, trdTgls, float[6]); //! Local tracklet TgL +DECLARE_SOA_COLUMN(TRDPhis, trdPhis, float[6]); //! Local tracklet phi +} // namespace trd + +DECLARE_SOA_TABLE(TRDsExtra, "AOD", "TRDEXTRA", //! TRDExtra table + o2::soa::Index<>, trd::TrackId, + trd::TRDQ0s, trd::TRDQ1s, trd::TRDQ2s, + trd::TRDQ0sCorrected, trd::TRDQ1sCorrected, trd::TRDQ2sCorrected, + trd::TRDTgls, trd::TRDPhis); +using TRDExtra = TRDsExtra::iterator; + namespace v0 { DECLARE_SOA_INDEX_COLUMN_FULL(PosTrack, posTrack, int, Tracks, "_Pos"); //! Positive track From 075c01aa42703316f5fc1ba990e3840305666097 Mon Sep 17 00:00:00 2001 From: shahoian Date: Sat, 28 Feb 2026 17:54:43 +0100 Subject: [PATCH 309/701] Fix in the 3D field propagation final step --- .../Reconstruction/src/TrackParametrization.cxx | 10 +++++----- .../src/TrackParametrizationWithError.cxx | 12 ++++++------ 2 files changed, 11 insertions(+), 11 deletions(-) diff --git a/DataFormats/Reconstruction/src/TrackParametrization.cxx b/DataFormats/Reconstruction/src/TrackParametrization.cxx index 7fe677a6e1c7a..4b850fe14086b 100644 --- a/DataFormats/Reconstruction/src/TrackParametrization.cxx +++ b/DataFormats/Reconstruction/src/TrackParametrization.cxx @@ -314,14 +314,14 @@ GPUd() bool TrackParametrization::propagateParamTo(value_t xk, const di // 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(x - xk) > constants::math::Almost0) { if (gpu::CAMath::Abs(vecLab[3]) < constants::math::Almost0) { return false; } - dx = xk - vecLab[0]; - x += dx; - y += vecLab[4] / vecLab[3] * dx; - z += vecLab[5] / vecLab[3] * dx; + auto dxFin = xk - vecLab[0]; + x += dxFin; + y += vecLab[4] / vecLab[3] * dxFin; + z += vecLab[5] / vecLab[3] * dxFin; } // Calculate the track parameters diff --git a/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx b/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx index 2f8f15f783c60..93ff7e1a2eb82 100644 --- a/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx +++ b/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx @@ -776,14 +776,14 @@ GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, cons // 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(x - xk) > constants::math::Almost0) { if (gpu::CAMath::Abs(vecLab[3]) < constants::math::Almost0) { return false; } - dx = xk - vecLab[0]; - x += dx; - y += vecLab[4] / vecLab[3] * dx; - z += vecLab[5] / vecLab[3] * dx; + auto dxFin = xk - vecLab[0]; + x += dxFin; + y += vecLab[4] / vecLab[3] * dxFin; + z += vecLab[5] / vecLab[3] * dxFin; } // Calculate the track parameters @@ -896,7 +896,7 @@ GPUd() bool TrackParametrizationWithError::propagateTo(value_t xk, Trac // 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(x - xk) > constants::math::Almost0) { if (gpu::CAMath::Abs(vecLab[3]) < constants::math::Almost0) { return false; } From 8c4238634e2ce6d1766cd747962c361337d46f94 Mon Sep 17 00:00:00 2001 From: shahoian Date: Thu, 26 Feb 2026 17:41:35 +0100 Subject: [PATCH 310/701] Extra support for dumping ConfigurableParam to ini/json files --- .../include/CommonUtils/ConfigurableParam.h | 3 ++ Common/Utils/include/CommonUtils/NameConf.h | 3 ++ .../Utils/include/CommonUtils/StringUtils.h | 3 ++ Common/Utils/src/ConfigurableParam.cxx | 23 +++++++++++++-- Common/Utils/src/NameConf.cxx | 9 ++++++ Common/Utils/src/StringUtils.cxx | 29 +++++++++++++++++++ 6 files changed, 68 insertions(+), 2 deletions(-) diff --git a/Common/Utils/include/CommonUtils/ConfigurableParam.h b/Common/Utils/include/CommonUtils/ConfigurableParam.h index 39b24bbbbd57c..b9234926b7c40 100644 --- a/Common/Utils/include/CommonUtils/ConfigurableParam.h +++ b/Common/Utils/include/CommonUtils/ConfigurableParam.h @@ -187,6 +187,9 @@ class ConfigurableParam // writes a human readable INI file of all parameters static void writeINI(std::string const& filename, std::string const& keyOnly = ""); + // writes a human readable INI or JSON file depending on the extension + static void write(std::string const& filename, std::string const& keyOnly = ""); + // can be used instead of using API on concrete child classes template static T getValueAs(std::string key) diff --git a/Common/Utils/include/CommonUtils/NameConf.h b/Common/Utils/include/CommonUtils/NameConf.h index 8a09a903bf32f..fb10f929c9782 100644 --- a/Common/Utils/include/CommonUtils/NameConf.h +++ b/Common/Utils/include/CommonUtils/NameConf.h @@ -103,6 +103,9 @@ class NameConf : public o2::conf::ConfigurableParamHelper // Default CCDB server static std::string getCCDBServer(); + // create name to dump config file + static std::string getConfigOutputFileName(const std::string& procName, const std::string& confName = "", bool json = true); + protected: // helper method to build filenames static std::string buildFileName(const std::string_view prefix, const std::string_view delimiter, const std::string_view defPrefix, const std::string_view defName, diff --git a/Common/Utils/include/CommonUtils/StringUtils.h b/Common/Utils/include/CommonUtils/StringUtils.h index 710632fc7dbfe..cfe29e065a78e 100644 --- a/Common/Utils/include/CommonUtils/StringUtils.h +++ b/Common/Utils/include/CommonUtils/StringUtils.h @@ -136,6 +136,9 @@ struct Str { // return vector of tokens from the string with provided delimiter. If requested, trim the spaces from tokens static std::vector tokenize(const std::string& src, char delim, bool trimToken = true, bool skipEmpty = true); + // return vector of tokens from the string with provided delimiters. If requested, trim the spaces from tokens + static std::vector tokenize(const std::string& src, const std::string& delim, bool trimToken = true, bool skipEmpty = true); + // concatenate arbitrary number of strings template static std::string concat_string(Ts const&... ts) diff --git a/Common/Utils/src/ConfigurableParam.cxx b/Common/Utils/src/ConfigurableParam.cxx index 8497a485fca39..fd69f51402cd5 100644 --- a/Common/Utils/src/ConfigurableParam.cxx +++ b/Common/Utils/src/ConfigurableParam.cxx @@ -192,6 +192,19 @@ int EnumLegalValues::getIntValue(const std::string& value) const // ----------------------------------------------------------------- +void ConfigurableParam::write(std::string const& filename, std::string const& keyOnly) +{ + if (o2::utils::Str::endsWith(filename, ".ini")) { + writeINI(filename, keyOnly); + } else if (o2::utils::Str::endsWith(filename, ".json")) { + writeJSON(filename, keyOnly); + } else { + throw std::invalid_argument(fmt::format("ConfigurabeParam output file name {} extension is neither .json nor .ini", filename)); + } +} + +// ----------------------------------------------------------------- + void ConfigurableParam::writeINI(std::string const& filename, std::string const& keyOnly) { if (sOutputDir == "/dev/null") { @@ -203,7 +216,10 @@ void ConfigurableParam::writeINI(std::string const& filename, std::string const& if (!keyOnly.empty()) { // write ini for selected key only try { boost::property_tree::ptree kTree; - kTree.add_child(keyOnly, sPtree->get_child(keyOnly)); + auto keys = o2::utils::Str::tokenize(keyOnly, " ,;", true, true); + for (const auto& k : keys) { + kTree.add_child(k, sPtree->get_child(k)); + } boost::property_tree::write_ini(outfilename, kTree); } catch (const boost::property_tree::ptree_bad_path& err) { LOG(fatal) << "non-existing key " << keyOnly << " provided to writeINI"; @@ -284,7 +300,10 @@ void ConfigurableParam::writeJSON(std::string const& filename, std::string const if (!keyOnly.empty()) { // write ini for selected key only try { boost::property_tree::ptree kTree; - kTree.add_child(keyOnly, sPtree->get_child(keyOnly)); + auto keys = o2::utils::Str::tokenize(keyOnly, " ,;", true, true); + for (const auto& k : keys) { + kTree.add_child(k, sPtree->get_child(k)); + } boost::property_tree::write_json(outfilename, kTree); } catch (const boost::property_tree::ptree_bad_path& err) { LOG(fatal) << "non-existing key " << keyOnly << " provided to writeJSON"; diff --git a/Common/Utils/src/NameConf.cxx b/Common/Utils/src/NameConf.cxx index 5a5f644f2da39..45646284a878b 100644 --- a/Common/Utils/src/NameConf.cxx +++ b/Common/Utils/src/NameConf.cxx @@ -111,3 +111,12 @@ std::string NameConf::getCCDBServer() { return Instance().mCCDBServer; } + +std::string NameConf::getConfigOutputFileName(const std::string& procName, const std::string& confName, bool json) +{ + std::string nm = procName; + if (!confName.empty()) { + nm += '_' + confName; + } + return fmt::format("ConfigParam_{}.{}", nm, json ? "json" : "ini"); +} diff --git a/Common/Utils/src/StringUtils.cxx b/Common/Utils/src/StringUtils.cxx index 687225d069ed2..29c43ec18375b 100644 --- a/Common/Utils/src/StringUtils.cxx +++ b/Common/Utils/src/StringUtils.cxx @@ -37,6 +37,35 @@ std::vector Str::tokenize(const std::string& src, char delim, bool return tokens; } +std::vector Str::tokenize(const std::string& src, const std::string& delim, bool trimToken, bool skipEmpty) +{ + std::string inptStr{src}; + char* input = inptStr.data(); + auto mystrtok = [&]() -> char* { + input += std::strspn(input, delim.c_str()); + if (*input == '\0') { + return nullptr; + } + char* const token = input; + input += std::strcspn(input, delim.c_str()); + if (*input != '\0') { + *input++ = '\0'; + } + return token; + }; + std::vector tokens; + while (*input != '\0') { + std::string token = mystrtok(); + if (trimToken) { + trim(token); + } + if (!token.empty() || !skipEmpty) { + tokens.push_back(std::move(token)); + } + } + return tokens; +} + // replace all occurencies of from by to, return count int Str::replaceAll(std::string& s, const std::string& from, const std::string& to) { From 18af67d8e54e2489801659be91444b2102bbb2c3 Mon Sep 17 00:00:00 2001 From: shahoian Date: Thu, 26 Feb 2026 17:45:32 +0100 Subject: [PATCH 311/701] Dump uniformly important reco ConfigParams to be collected in AO2D metadata --- .../src/PrimaryVertexingSpec.cxx | 8 ++++++++ .../src/SecondaryVertexingSpec.cxx | 11 +++++++++++ .../GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx | 9 +++++++++ .../src/TPCITSMatchingSpec.cxx | 8 ++++++++ Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx | 9 +++++++++ Detectors/ITSMFT/MFT/workflow/src/TrackerSpec.cxx | 9 +++++++++ .../TRD/workflow/src/TRDGlobalTrackingSpec.cxx | 9 +++++++++ GPU/Workflow/src/GPUWorkflowITS.cxx | 14 +++++++++++++- GPU/Workflow/src/GPUWorkflowSpec.cxx | 7 +++++++ 9 files changed, 83 insertions(+), 1 deletion(-) diff --git a/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexingSpec.cxx index d71a4fad7ab78..dc1107bacb18a 100644 --- a/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexingSpec.cxx @@ -197,6 +197,14 @@ void PrimaryVertexingSpec::run(ProcessingContext& pc) mVertexer.getTimeReAttach().CpuTime(), mVertexer.getTotTrials(), mVertexer.getNTZClusters(), mVertexer.getMaxTrialsPerCluster(), mVertexer.getLongestClusterTimeMS(), mVertexer.getLongestClusterMult(), mVertexer.getNIniFound(), mVertexer.getNKilledBCValid(), mVertexer.getNKilledIntCand(), mVertexer.getNKilledDebris(), mVertexer.getNKilledQuality(), mVertexer.getNKilledITSOnly()); + + static bool first = true; + if (first) { + first = false; + if (pc.services().get().inputTimesliceId == 0) { + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, PVertexerParams::Instance().getName()), PVertexerParams::Instance().getName()); + } + } } void PrimaryVertexingSpec::endOfStream(EndOfStreamContext& ec) diff --git a/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx index ea566f15a0b59..1b55f9c763e7f 100644 --- a/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx @@ -128,6 +128,17 @@ void SecondaryVertexingSpec::run(ProcessingContext& pc) mVertexer.getNV0s(), calls[0] - fitCalls[0], mVertexer.getNCascades(), calls[1] - fitCalls[1], mVertexer.getN3Bodies(), calls[2] - fitCalls[2], mVertexer.getNStrangeTracks(), mTimer.CpuTime() - timeCPU0, mTimer.RealTime() - timeReal0); fitCalls = calls; + + static bool first = true; + if (first) { + first = false; + if (pc.services().get().inputTimesliceId == 0) { + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, SVertexerParams::Instance().getName()), SVertexerParams::Instance().getName()); + if (mEnableStrangenessTracking) { + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, o2::strangeness_tracking::StrangenessTrackingParamConfig::Instance().getName()), o2::strangeness_tracking::StrangenessTrackingParamConfig::Instance().getName()); + } + } + } } void SecondaryVertexingSpec::endOfStream(EndOfStreamContext& ec) diff --git a/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx index 3f6e79e433635..8081c48e390d3 100644 --- a/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx @@ -22,6 +22,7 @@ #include "DataFormatsGlobalTracking/RecoContainer.h" #include "Framework/Task.h" #include "Framework/DataProcessorSpec.h" +#include "Framework/DeviceSpec.h" #include "TPCCalibration/VDriftHelper.h" #include "TPCCalibration/CorrectionMapsLoader.h" @@ -229,6 +230,14 @@ void TOFMatcherSpec::run(ProcessingContext& pc) pc.outputs().snapshot(Output{o2::header::gDataOriginTOF, "MATCHABLES_17", 0}, mMatcher.getMatchedTracksPair(17)); } + static bool first = true; + if (first) { + first = false; + if (pc.services().get().inputTimesliceId == 0) { + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, MatchTOFParams::Instance().getName()), MatchTOFParams::Instance().getName()); + } + } + mTimer.Stop(); } diff --git a/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx index 14af8c12794cc..c333c37ff245b 100644 --- a/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx @@ -130,6 +130,14 @@ void TPCITSMatchingDPL::run(ProcessingContext& pc) mMatching.run(recoData, matchedTracks, ABTrackletRefs, ABTrackletClusterIDs, matchLabels, ABTrackletLabels, calib); + static bool first = true; + if (first) { + first = false; + if (pc.services().get().inputTimesliceId == 0) { + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, MatchTPCITSParams::Instance().getName()), MatchTPCITSParams::Instance().getName()); + } + } + mTimer.Stop(); } diff --git a/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx index 12d84ca7ab6ad..3d07048aaf1e6 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx @@ -14,6 +14,7 @@ #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" +#include "Framework/DeviceSpec.h" #include "ITSWorkflow/TrackerSpec.h" #include "ITStracking/Definitions.h" #include "ITStracking/TrackingConfigParam.h" @@ -60,6 +61,14 @@ void TrackerDPL::run(ProcessingContext& pc) mITSTrackingInterface.run(pc); mTimer.Stop(); LOGP(info, "CPU Reconstruction time for this TF {:.2f} s (cpu), {:.2f} s (wall)", mTimer.CpuTime() - cput, mTimer.RealTime() - realt); + static bool first = true; + if (first) { + first = false; + if (pc.services().get().inputTimesliceId == 0) { + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, o2::its::VertexerParamConfig::Instance().getName()), o2::its::VertexerParamConfig::Instance().getName()); + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, o2::its::TrackerParamConfig::Instance().getName()), o2::its::TrackerParamConfig::Instance().getName()); + } + } } void TrackerDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) diff --git a/Detectors/ITSMFT/MFT/workflow/src/TrackerSpec.cxx b/Detectors/ITSMFT/MFT/workflow/src/TrackerSpec.cxx index d8e15590474ec..3e726fe37c38c 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/TrackerSpec.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/TrackerSpec.cxx @@ -24,6 +24,7 @@ #include "TGeoGlobalMagField.h" +#include "Framework/DeviceSpec.h" #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" @@ -331,6 +332,14 @@ void TrackerDPL::run(ProcessingContext& pc) pc.outputs().snapshot(Output{"MFT", "TRACKSMC2ROF", 0}, mc2rofs); } + static bool first = true; + if (first) { + first = false; + if (pc.services().get().inputTimesliceId == 0) { + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, o2::mft::MFTTrackingParam::Instance().getName()), o2::mft::MFTTrackingParam::Instance().getName()); + } + } + mTimer[SWTot].Stop(); } diff --git a/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx b/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx index f2d4aad829fe5..9e7ef089faeef 100644 --- a/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx +++ b/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx @@ -28,6 +28,7 @@ #include "GPUWorkflowHelper/GPUWorkflowHelper.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" +#include "Framework/DeviceSpec.h" #include "DataFormatsTPC/WorkflowHelper.h" #include "TPCReconstruction/TPCFastTransformHelperO2.h" #include "CommonConstants/GeomConstants.h" @@ -554,6 +555,14 @@ void TRDGlobalTracking::run(ProcessingContext& pc) } } + static bool first = true; + if (first) { + first = false; + if (pc.services().get().inputTimesliceId == 0) { + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, "GPU_rec_trd"), "GPU_rec_trd"); + } + } + mTimer.Stop(); } diff --git a/GPU/Workflow/src/GPUWorkflowITS.cxx b/GPU/Workflow/src/GPUWorkflowITS.cxx index b1c8d619ec736..587b85df98952 100644 --- a/GPU/Workflow/src/GPUWorkflowITS.cxx +++ b/GPU/Workflow/src/GPUWorkflowITS.cxx @@ -18,8 +18,11 @@ #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" - +#include "Framework/DeviceSpec.h" +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/NameConf.h" #include "ITStracking/TrackingInterface.h" +#include "ITStracking/TrackingConfigParam.h" #ifdef ENABLE_UPGRADES #include "ITS3Reconstruction/TrackingInterface.h" @@ -33,6 +36,15 @@ int32_t GPURecoWorkflowSpec::runITSTracking(o2::framework::ProcessingContext& pc mITSTimeFrame->setDevicePropagator(mGPUReco->GetDeviceO2Propagator()); LOGP(debug, "GPUChainITS is giving me device propagator: {}", (void*)mGPUReco->GetDeviceO2Propagator()); mITSTrackingInterface->run(pc); + static bool first = true; + if (first) { + first = false; + if (pc.services().get().inputTimesliceId == 0) { + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, o2::its::VertexerParamConfig::Instance().getName()), o2::its::VertexerParamConfig::Instance().getName()); + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, o2::its::TrackerParamConfig::Instance().getName()), o2::its::TrackerParamConfig::Instance().getName()); + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, o2::its::ITSGpuTrackingParamConfig::Instance().getName()), o2::its::ITSGpuTrackingParamConfig::Instance().getName()); + } + } return 0; } diff --git a/GPU/Workflow/src/GPUWorkflowSpec.cxx b/GPU/Workflow/src/GPUWorkflowSpec.cxx index a8f95841a4dc9..48210c440d01e 100644 --- a/GPU/Workflow/src/GPUWorkflowSpec.cxx +++ b/GPU/Workflow/src/GPUWorkflowSpec.cxx @@ -514,6 +514,13 @@ int32_t GPURecoWorkflowSpec::runMain(o2::framework::ProcessingContext* pc, GPUTr if (retVal == 0 && mSpecConfig.runITSTracking) { retVal = runITSTracking(*pc); } + static bool first = true; + if (first) { + first = false; + if (pc->services().get().inputTimesliceId == 0) { // TPC ConfigurableCarams are somewhat special, need to construct by hand + o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc->services().get().name, "rec_tpc"), "GPU_rec_tpc,GPU_rec,GPU_proc_param,GPU_proc,GPU_global,trackTuneParams"); + } + } } if (!mSpecConfig.enableDoublePipeline) { // TODO: Why is this needed for double-pipeline? From cbdea04a9a190c34a49f64c4a9f949178db39fe3 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Tue, 24 Feb 2026 10:03:21 +0100 Subject: [PATCH 312/701] Add Eigen3 and GBL dependencies Following https://github.com/alisw/alidist/pull/6137, this adds GBL and Eigen3 as dependencies. --- dependencies/O2Dependencies.cmake | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/dependencies/O2Dependencies.cmake b/dependencies/O2Dependencies.cmake index 26f381a4ef79f..8addb87a1a16f 100644 --- a/dependencies/O2Dependencies.cmake +++ b/dependencies/O2Dependencies.cmake @@ -243,4 +243,25 @@ set_package_properties(absl PROPERTIES TYPE REQUIRED) find_package(Vtune) set_package_properties(Vtune PROPERTIES TYPE OPTIONAL) +find_package(Eigen3 QUIET) +if(NOT TARGET Eigen3::Eigen) + # The Eigen3 install only provides the header files, so 'mock' the cmake target + add_library(Eigen3::Eigen INTERFACE IMPORTED) + set_target_properties(Eigen3::Eigen PROPERTIES + INTERFACE_INCLUDE_DIRECTORIES "${EIGEN3_ROOT}/include/eigen3" + ) +endif() + +find_package(GBL) +set_package_properties(GBL PROPERTIES TYPE REQUIRED) +if(GBL_FOUND AND NOT TARGET GBL::GBL) + # As of now, GBL does not provide a cmake target so create a compatibility wrapper + add_library(GBL::GBL INTERFACE IMPORTED) + target_include_directories(GBL::GBL INTERFACE ${GBL_INCLUDE_DIR}) + target_link_libraries(GBL::GBL INTERFACE + ${GBL_LIBRARIES} + Eigen3::Eigen + ) +endif() + feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) From da3f131d129c3661803d439ac9ccf9e2f6bb163c Mon Sep 17 00:00:00 2001 From: shahoian Date: Mon, 2 Mar 2026 15:45:26 +0100 Subject: [PATCH 313/701] Clusterer fix: suppress stray ROFs keeping their clusters unaddressed --- .../common/workflow/src/ClustererSpec.cxx | 78 ++++++++++--------- 1 file changed, 43 insertions(+), 35 deletions(-) diff --git a/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx b/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx index 0b6bb44ee78c8..8de29c62335b6 100644 --- a/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx @@ -125,48 +125,56 @@ void ClustererDPL::run(ProcessingContext& pc) 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); + // It can happen that in the digitization rofs without contributing hits are skipped or there are stray ROFs + // We will preserve the clusters as they are but the stray ROFs will be removed (leaving their clusters unaddressed). + std::vector expClusRofVec(nROFsTF); + for (int iROF{0}; iROF < nROFsTF; ++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(); + if (ir < firstIR) { + LOGP(warn, "Discard ROF {} preceding TF 1st orbit {}, layer:{}", ir.asString(), firstTForbit, iLayer); + continue; + } + const auto irToFirst = ir - firstIR; + const long irROF = irToFirst.toLong() / par.getROFLengthInBC(iLayer); + if (irROF >= nROFsTF) { + LOGP(warn, "Discard ROF {} exceding TF orbit range, layer:{}", ir.asString(), iLayer); + continue; } - 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]; + auto& expROF = expClusRofVec[irROF]; + if (expROF.getNEntries() == 0) { 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()); + } else { + if (expROF.getNEntries() < rof.getNEntries()) { + LOGP(warn, "Repeating ROF {} with {} clusters, prefer to already processed instance with {} clusters", rof.asString(), rof.getNEntries(), expROF.getNEntries()); + expROF.setFirstEntry(rof.getFirstEntry()); + expROF.setNEntries(rof.getNEntries()); + } else { + LOGP(warn, "Repeating ROF {} with {} clusters, discard preferring already processed instance with {} clusters", rof.asString(), rof.getNEntries(), expROF.getNEntries()); } } - int prevFirst{0}; - for (auto& rof : expClusRofVec) { - if (rof.getFirstEntry() < 0) { - rof.setFirstEntry(prevFirst); - } - prevFirst = rof.getFirstEntry(); + } + int prevFirst{0}; + for (auto& rof : expClusRofVec) { + if (rof.getFirstEntry() < 0) { + rof.setFirstEntry(prevFirst); } - nROFs = expClusRofVec.size(); - pc.outputs().snapshot(Output{Origin, "CLUSTERSROF", iLayer}, expClusRofVec); - } else { - pc.outputs().snapshot(Output{Origin, "CLUSTERSROF", iLayer}, clusROFVec); + prevFirst = rof.getFirstEntry(); } + nROFs = expClusRofVec.size(); + pc.outputs().snapshot(Output{Origin, "CLUSTERSROF", iLayer}, expClusRofVec); + pc.outputs().snapshot(Output{Origin, "COMPCLUSTERS", iLayer}, clusCompVec); pc.outputs().snapshot(Output{Origin, "PATTERNS", iLayer}, clusPattVec); From 9b34abbb94ab5792006a25845b042b4c7bec5dc6 Mon Sep 17 00:00:00 2001 From: wiechula Date: Thu, 26 Feb 2026 13:33:27 +0100 Subject: [PATCH 314/701] Add missing test --- Detectors/TPC/qc/CMakeLists.txt | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Detectors/TPC/qc/CMakeLists.txt b/Detectors/TPC/qc/CMakeLists.txt index 60195ed6d451a..ce998eec6475c 100644 --- a/Detectors/TPC/qc/CMakeLists.txt +++ b/Detectors/TPC/qc/CMakeLists.txt @@ -77,6 +77,12 @@ o2_add_test(TrackClusters SOURCES test/test_TrackClusters.cxx LABELS tpc) +o2_add_test(DCSPTemperature + COMPONENT_NAME tpc + PUBLIC_LINK_LIBRARIES O2::TPCQC + SOURCES test/test_DCSPTemperature.cxx + LABELS tpc) + o2_add_test_root_macro(macro/runPID.C PUBLIC_LINK_LIBRARIES O2::TPCQC O2::DataFormatsTPC From 1ffca72c093b391400c44db610e822d23b3ba3b9 Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Wed, 25 Feb 2026 15:28:57 +0100 Subject: [PATCH 315/701] Fix type mismatch --- Generators/src/GeneratorHepMC.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Generators/src/GeneratorHepMC.cxx b/Generators/src/GeneratorHepMC.cxx index 180a088c02a92..faacde7317664 100644 --- a/Generators/src/GeneratorHepMC.cxx +++ b/Generators/src/GeneratorHepMC.cxx @@ -501,7 +501,7 @@ void GeneratorHepMC::updateHeader(o2::dataformats::MCEventHeader* eventHeader) hiInfo->Nwounded_N_collisions); eventHeader->putInfo(Key::nCollNWoundedNwounded, hiInfo->Nwounded_Nwounded_collisions); - eventHeader->putInfo(Key::planeAngle, hiInfo->event_plane_angle); + eventHeader->putInfo(Key::planeAngle, hiInfo->event_plane_angle); eventHeader->putInfo(Key::sigmaInelNN, hiInfo->sigma_inel_NN); eventHeader->putInfo(Key::centrality, hiInfo->centrality); eventHeader->putInfo(Key::nSpecProjectileProton, hiInfo->Nspec_proj_p); From a1d999e775b3da8f2f6099d0fe43eaf6e20a5a81 Mon Sep 17 00:00:00 2001 From: Pavel Larionov Date: Mon, 2 Mar 2026 21:48:08 +0100 Subject: [PATCH 316/701] [ALICE 3] Add IRIS option with inclined walls material (#15098) * Fully cyl IRIS with inclined walls material * Remove white space * Please consider the following formatting changes * Added 3rd segment up to R = 47.5 mm * Petal outer radius 48 mm * Set incl. material length to petal length --------- Co-authored-by: ALICE Action Bot --- .../TRK/base/include/TRKBase/TRKBaseParam.h | 1 + .../include/TRKSimulation/VDGeometryBuilder.h | 1 + .../ALICE3/TRK/simulation/src/Detector.cxx | 4 + .../TRK/simulation/src/VDGeometryBuilder.cxx | 199 +++++++++++++++++- 4 files changed, 200 insertions(+), 5 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h index f919839b7ed0a..232e7e04b09cd 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h @@ -34,6 +34,7 @@ enum eLayout { enum eVDLayout { kIRIS4 = 0, kIRISFullCyl, + kIRISFullCyl3InclinedWalls, kIRIS5, kIRIS4a, }; diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDGeometryBuilder.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDGeometryBuilder.h index c337ddb102147..77b45c97cba84 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDGeometryBuilder.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/VDGeometryBuilder.h @@ -25,6 +25,7 @@ namespace o2::trk // 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 createIRISGeometry3InclinedWalls(TGeoVolume* motherVolume); // Full-cylinder IRIS geometry with 3 inclined 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 diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx index a2629a101d2d0..2ad1d52ba73c4 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx @@ -275,6 +275,10 @@ void Detector::createGeometry() LOG(info) << "Building VD with IRIS fully cylindrical layout"; o2::trk::createIRISGeometryFullCyl(vTRK); break; + case kIRISFullCyl3InclinedWalls: + LOG(info) << "Building VD with IRIS fully cylindrical layout with 3 inclined walls"; + o2::trk::createIRISGeometry3InclinedWalls(vTRK); + break; case kIRIS5: LOG(info) << "Building VD with IRIS5 layout"; o2::trk::createIRIS5Geometry(vTRK); diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx index b06faa38211bb..f487d7602619f 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx @@ -10,7 +10,6 @@ // or submit itself to any jurisdiction. #include "TRKSimulation/VDGeometryBuilder.h" - #include #include #include @@ -19,13 +18,13 @@ #include #include #include - #include "TGeoManager.h" - #include "Framework/Logger.h" #include "TRKBase/GeometryTGeo.h" #include "TRKSimulation/VDLayer.h" #include "TRKSimulation/VDSensorRegistry.h" +#include +#include namespace o2::trk { @@ -82,6 +81,9 @@ inline bool isSolidToCut(const TGeoVolume* v) if (TString(nm).BeginsWith("IRIS_Service_Pos_InVac")) { return true; } + if (TString(nm).BeginsWith("VD_InclinedWall")) { + return true; + } return false; } @@ -252,8 +254,18 @@ static const double diskZ_cm[6] = {-34.0f, -30.0f, -26.0f, 26.0f, 30.0f, 34.0f}; 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; +static constexpr double kOuterWallRadius_cm = 4.8f; // 48 mm (can be changed) +static constexpr double kEps_cm = 2.5e-4f; + +// 3 inclined walls ("walls") specs for the full-cylinder option +// Thickness in-plane (cm). This is the short half-dimension of the TGeoBBox in XY. +static constexpr double kInclinedWallThick_cm = 0.04f; // 0.4 mm +// Layer-shell thickness used for the gap boundaries in the inclined-wall construction (cm) +static constexpr double kSiLayerThick_cm = 0.01f; // 0.1 mm +// Base tangency angle (deg) for the first wall; the other 2 are +120/+240. +// This matches the angle used in the ROOT sketch from our chat. +static constexpr double kInclinedWallPhi0_deg = 27.799f; +static constexpr double kInclinedWallRmax_cm = 4.75f; // 47.5 mm outer extension // Coldplate specs (cm) static constexpr double kColdplateRadius_cm = 2.6f; // 26 mm (outer radius) @@ -806,6 +818,158 @@ static TGeoVolume* buildFullCylAssembly(int petalID, bool withDisks) return petalAsm; } +// Add 3 inclined walls (straight walls) into a full-cylinder petal assembly. +// The walls are implemented as TWO TGeoBBox segments per wall, living in the gaps: +// - segment 01: from tangency at Rtan to inner surface of L1 +// - segment 12: from outer surface of L1 to inner surface of L2 +// The construction accounts for the finite wall thickness (kInclinedWallThick_cm). +static void addInclinedWalls3FullCyl(TGeoVolume* petalAsm, double phi0_deg = kInclinedWallPhi0_deg) +{ + if (!petalAsm) { + LOGP(error, "addInclinedWalls3FullCyl: petalAsm is null"); + return; + } + + auto& matmgr = o2::base::MaterialManager::Instance(); + const TGeoMedium* med = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_ALUMINIUM5083"); + if (!med) { + LOGP(warning, "addInclinedWalls3FullCyl: ALICE3_TRKSERVICES_ALUMINIUM5083 not found, walls not created."); + return; + } + + // Clearance margin from layer/coldplate surfaces (cm) + constexpr double clearanceMargin = 0.010; // 100 microns + + // Geometry inputs (cm) + constexpr double R0 = rL0_cm; + constexpr double R1 = rL1_cm; + constexpr double R2 = rL2_cm; + constexpr double Rmax = kInclinedWallRmax_cm; + + const double wallDy = 0.5 * kInclinedWallThick_cm; + const double shellTh = kSiLayerThick_cm; // 0.1 mm shell thickness for bounds + const double h = 0.5 * shellTh; + const double dz = 0.5 * kPetalZ_cm; // match barrel/coldplate length in full-cyl option + + constexpr int nWalls = 3; + constexpr double dPhi = 360.0 / double(nWalls); + + // Gap boundaries (shell surfaces) + const double R0_out = R0 + h; + const double R1_in = R1 - h; + const double R1_out = R1 + h; + const double R2_in = R2 - h; + const double R2_out = R2 + h; + + // Coldplate outer radius (tube segment is [kColdplateRadius_cm, kColdplateRadius_cm + kColdplateThickness_cm]) + const double Rcold_out = kColdplateRadius_cm + kColdplateThickness_cm; + + // Tangency radius choice (thickness-safe at s=0): need Rtan - wallDy >= R0_out + const double Rtan = R0_out + wallDy + clearanceMargin; + + // For finite-thickness box: + // outermost edge uses Reff_plus, innermost edge uses Reff_minus + const double Reff_plus = Rtan + wallDy + clearanceMargin; + const double Reff_minus = std::max(0.0, Rtan - wallDy - clearanceMargin); + + auto sAt = [](double R, double Reff) -> double { + const double v = R * R - Reff * Reff; + return (v > 0.0) ? std::sqrt(v) : 0.0; + }; + + // Segment bounds in 's' (thickness-safe): + // 01: from tangency to L1 inner surface (outer edge <= R1_in) + const double sa01 = 0.0; + const double sb01 = sAt(R1_in, Reff_plus); + + // 12: from outside L1 to inside L2 + const double sa12 = sAt(R1_out, Reff_minus); // inner edge >= R1_out + const double sb12 = sAt(R2_in, Reff_plus); // outer edge <= R2_in + + // 23: from outside coldplate (and outside L2) to Rmax + const double R23_start = std::max(R2_out, Rcold_out) + clearanceMargin; + const double sa23 = sAt(R23_start, Reff_minus); // inner edge >= start radius + const double sb23 = sAt(Rmax, Reff_plus); // outer edge <= Rmax + + if (!((sb01 > sa01) && (sb12 > sa12) && (sb23 > sa23))) { + LOGP(error, + "addInclinedWalls3FullCyl: invalid bounds. 01:[{},{}] 12:[{},{}] 23:[{},{}] " + "Rtan={} Reff-={} Reff+={} R23_start={}", + sa01, sb01, sa12, sb12, sa23, sb23, + Rtan, Reff_minus, Reff_plus, R23_start); + return; + } + + // Half-lengths and center parameters (s-centers) + const double dx01 = 0.5 * (sb01 - sa01); + const double dx12 = 0.5 * (sb12 - sa12); + const double dx23 = 0.5 * (sb23 - sa23); + + const double sc01 = 0.5 * (sa01 + sb01); + const double sc12 = 0.5 * (sa12 + sb12); + const double sc23 = 0.5 * (sa23 + sb23); + + // Create shapes once, reuse for all walls + auto* sh01 = new TGeoBBox(dx01, wallDy, dz); + auto* sh12 = new TGeoBBox(dx12, wallDy, dz); + auto* sh23 = new TGeoBBox(dx23, wallDy, dz); + sh01->SetName("VD_InclinedWall01_sh"); + sh12->SetName("VD_InclinedWall12_sh"); + sh23->SetName("VD_InclinedWall23_sh"); + + const double phi0_rad = phi0_deg * TMath::DegToRad(); + + for (int i = 0; i < nWalls; ++i) { + const double phi = phi0_rad + i * (dPhi * TMath::DegToRad()); + const double cosPhi = std::cos(phi); + const double sinPhi = std::sin(phi); + + // Tangency point on Rtan + const double xT = Rtan * cosPhi; + const double yT = Rtan * sinPhi; + + // Tangent direction u = (-sin, cos) + const double ux = -sinPhi; + const double uy = cosPhi; + + // Centers (in XY) + const double cx01 = xT + sc01 * ux; + const double cy01 = yT + sc01 * uy; + const double cx12 = xT + sc12 * ux; + const double cy12 = yT + sc12 * uy; + const double cx23 = xT + sc23 * ux; + const double cy23 = yT + sc23 * uy; + + // Rotation: local X along tangent => angle = phi + 90° + const double alpha_deg = phi0_deg + i * dPhi + 90.0; + auto* rot = new TGeoRotation(); + rot->RotateZ(alpha_deg); + + // Create volumes per wall (unique names) + auto* v01 = new TGeoVolume(Form("VD_InclinedWall01_%d", i), sh01, med); + auto* v12 = new TGeoVolume(Form("VD_InclinedWall12_%d", i), sh12, med); + auto* v23 = new TGeoVolume(Form("VD_InclinedWall23_%d", i), sh23, med); + v01->SetLineColor(kOrange + 7); + v12->SetLineColor(kOrange + 7); + v23->SetLineColor(kOrange + 7); + v01->SetTransparency(70); + v12->SetTransparency(70); + v23->SetTransparency(70); + + auto* T01 = new TGeoCombiTrans(cx01, cy01, 0.0, rot); + auto* T12 = new TGeoCombiTrans(cx12, cy12, 0.0, new TGeoRotation(*rot)); + auto* T23 = new TGeoCombiTrans(cx23, cy23, 0.0, new TGeoRotation(*rot)); + + petalAsm->AddNode(v01, 1, T01); + petalAsm->AddNode(v12, 1, T12); + petalAsm->AddNode(v23, 1, T23); + + LOGP(debug, + "InclinedWall {}: 01({:.3f},{:.3f}) 12({:.3f},{:.3f}) 23({:.3f},{:.3f}) angle={:.2f}°", + i, cx01, cy01, cx12, cy12, cx23, cy23, alpha_deg); + } +} + // =================== Public entry points =================== void createIRIS4Geometry(TGeoVolume* motherVolume) @@ -908,6 +1072,31 @@ void createIRISGeometryFullCyl(TGeoVolume* motherVolume) buildIrisCutoutFromPetalSolid(nPetals); } +void createIRISGeometry3InclinedWalls(TGeoVolume* motherVolume) +{ + if (!motherVolume) { + LOGP(error, "createIRISGeometry3InclinedWalls: motherVolume is null"); + return; + } + + clearVDSensorRegistry(); + + constexpr int nPetals = 1; + constexpr int petalID = 0; + + // Start from the same content as createIRISGeometryFullCyl + auto* petal = buildFullCylAssembly(petalID, /*withDisks=*/false); + + // Add the 3 inclined walls into the same assembly + addInclinedWalls3FullCyl(petal, kInclinedWallPhi0_deg); + + motherVolume->AddNode(petal, 1, nullptr); + + // Same cutout pipeline as full-cyl + buildPetalSolidsComposite(petal); + buildIrisCutoutFromPetalSolid(nPetals); +} + void createIRISGeometryFullCylwithDisks(TGeoVolume* motherVolume) { if (!motherVolume) { From 3626eeac1a4c8a21fbafeee349d1bc7576777f33 Mon Sep 17 00:00:00 2001 From: AizatDaribayeva Date: Tue, 3 Mar 2026 10:35:34 +0100 Subject: [PATCH 317/701] [ALICE3] TRK/Geometry: small bug fix (#15112) --- .../TRK/base/include/TRKBase/GeometryTGeo.h | 17 +++++++++++++++-- .../ALICE3/TRK/base/src/GeometryTGeo.cxx | 2 +- 2 files changed, 16 insertions(+), 3 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h index bb1597f2967e4..d4402d66cff7e 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h @@ -104,8 +104,21 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache bool isTrackingFrameCachedMLOT() const { return !mCacheRefXMLOT.empty(); } void fillTrackingFramesCacheMLOT(); - float getSensorRefAlphaMLOT(int index) const { return mCacheRefAlphaMLOT[index]; } - float getSensorXMLOT(int index) const { return mCacheRefXMLOT[index]; } + float getSensorRefAlphaMLOT(int chipId) const + { + assert(getSubDetID(chipId) != 0 && "Called MLOT getter with VD chipId"); + const int local = chipId - getNumberOfActivePartsVD(); + assert(local >= 0 && local < (int)mCacheRefAlphaMLOT.size()); + return mCacheRefAlphaMLOT[local]; + } + + float getSensorXMLOT(int chipId) const + { + assert(getSubDetID(chipId) != 0 && "Called MLOT getter with VD chipId"); + const int local = chipId - getNumberOfActivePartsVD(); + assert(local >= 0 && local < (int)mCacheRefXMLOT.size()); + return mCacheRefXMLOT[local]; + } // create matrix for tracking to local frame for MLOT TGeoHMatrix& createT2LMatrixMLOT(int); diff --git a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx index b5535af781910..7b3d33ca1a75c 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx @@ -127,9 +127,9 @@ void GeometryTGeo::Build(int loadTrans) } setSize(numberOfChipsTotal); - fillMatrixCache(loadTrans); defineMLOTSensors(); fillTrackingFramesCacheMLOT(); + fillMatrixCache(loadTrans); } //__________________________________________________________________________ From afcf287eb39a39188e3017eeda93772f00532aa7 Mon Sep 17 00:00:00 2001 From: Maximiliano Puccio Date: Tue, 3 Mar 2026 10:49:10 +0100 Subject: [PATCH 318/701] [ALICE3] Cluster finding of TRK (#15110) --- .../Detectors/Upgrades/ALICE3/CMakeLists.txt | 1 + .../Upgrades/ALICE3/TRK/CMakeLists.txt | 24 + .../TRK/include/DataFormatsTRK/Cluster.h | 38 ++ .../TRK/include/DataFormatsTRK/ROFRecord.h | 75 ++++ .../Upgrades/ALICE3/TRK/src/Cluster.cxx | 28 ++ .../ALICE3/TRK/src/DataFormatsTRKLinkDef.h | 25 ++ .../Upgrades/ALICE3/TRK/src/ROFRecord.cxx | 29 ++ .../ALICE3/TRK/macros/test/CMakeLists.txt | 10 +- .../ALICE3/TRK/macros/test/CheckClusters.C | 417 +++++++++++++++++ .../ALICE3/TRK/reconstruction/CMakeLists.txt | 3 + .../include/TRKReconstruction/Clusterer.h | 182 ++++++++ .../TRK/reconstruction/src/Clusterer.cxx | 419 ++++++++++++++++++ .../src/TRKReconstructionLinkDef.h | 1 + .../ALICE3/TRK/workflow/CMakeLists.txt | 3 + .../include/TRKWorkflow/ClusterWriterSpec.h | 24 + .../include/TRKWorkflow/ClustererSpec.h | 39 ++ .../TRK/workflow/src/ClusterWriterSpec.cxx | 65 +++ .../ALICE3/TRK/workflow/src/ClustererSpec.cxx | 99 +++++ .../ALICE3/TRK/workflow/src/RecoWorkflow.cxx | 21 +- .../ALICE3/TRK/workflow/src/TrackerSpec.cxx | 10 +- 20 files changed, 1507 insertions(+), 6 deletions(-) create mode 100644 DataFormats/Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt create mode 100644 DataFormats/Detectors/Upgrades/ALICE3/TRK/include/DataFormatsTRK/Cluster.h create mode 100644 DataFormats/Detectors/Upgrades/ALICE3/TRK/include/DataFormatsTRK/ROFRecord.h create mode 100644 DataFormats/Detectors/Upgrades/ALICE3/TRK/src/Cluster.cxx create mode 100644 DataFormats/Detectors/Upgrades/ALICE3/TRK/src/DataFormatsTRKLinkDef.h create mode 100644 DataFormats/Detectors/Upgrades/ALICE3/TRK/src/ROFRecord.cxx create mode 100644 Detectors/Upgrades/ALICE3/TRK/macros/test/CheckClusters.C create mode 100644 Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/Clusterer.h create mode 100644 Detectors/Upgrades/ALICE3/TRK/reconstruction/src/Clusterer.cxx create mode 100644 Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClusterWriterSpec.h create mode 100644 Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClustererSpec.h create mode 100644 Detectors/Upgrades/ALICE3/TRK/workflow/src/ClusterWriterSpec.cxx create mode 100644 Detectors/Upgrades/ALICE3/TRK/workflow/src/ClustererSpec.cxx diff --git a/DataFormats/Detectors/Upgrades/ALICE3/CMakeLists.txt b/DataFormats/Detectors/Upgrades/ALICE3/CMakeLists.txt index b3944c2e502d8..360b50d442d7d 100644 --- a/DataFormats/Detectors/Upgrades/ALICE3/CMakeLists.txt +++ b/DataFormats/Detectors/Upgrades/ALICE3/CMakeLists.txt @@ -10,3 +10,4 @@ # or submit itself to any jurisdiction. add_subdirectory(FD3) +add_subdirectory(TRK) diff --git a/DataFormats/Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt b/DataFormats/Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt new file mode 100644 index 0000000000000..c239a2a36845d --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/TRK/CMakeLists.txt @@ -0,0 +1,24 @@ +# 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. + +o2_add_library(DataFormatsTRK + SOURCES src/Cluster.cxx + src/ROFRecord.cxx + PUBLIC_LINK_LIBRARIES O2::CommonDataFormat + O2::DataFormatsITSMFT + O2::SimulationDataFormat +) + +o2_target_root_dictionary(DataFormatsTRK + HEADERS include/DataFormatsTRK/Cluster.h + include/DataFormatsTRK/ROFRecord.h + LINKDEF src/DataFormatsTRKLinkDef.h +) diff --git a/DataFormats/Detectors/Upgrades/ALICE3/TRK/include/DataFormatsTRK/Cluster.h b/DataFormats/Detectors/Upgrades/ALICE3/TRK/include/DataFormatsTRK/Cluster.h new file mode 100644 index 0000000000000..ec68191b3c43f --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/TRK/include/DataFormatsTRK/Cluster.h @@ -0,0 +1,38 @@ +// 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. + +#ifndef ALICEO2_DATAFORMATSTRK_CLUSTER_H +#define ALICEO2_DATAFORMATSTRK_CLUSTER_H + +#include +#include +#include + +namespace o2::trk +{ + +struct Cluster { + uint16_t chipID = 0; + uint16_t row = 0; + uint16_t col = 0; + uint16_t size = 1; + int16_t subDetID = -1; + int16_t layer = -1; + int16_t disk = -1; + + std::string asString() const; + + ClassDefNV(Cluster, 1); +}; + +} // namespace o2::trk + +#endif diff --git a/DataFormats/Detectors/Upgrades/ALICE3/TRK/include/DataFormatsTRK/ROFRecord.h b/DataFormats/Detectors/Upgrades/ALICE3/TRK/include/DataFormatsTRK/ROFRecord.h new file mode 100644 index 0000000000000..86ee31389fd5f --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/TRK/include/DataFormatsTRK/ROFRecord.h @@ -0,0 +1,75 @@ +// 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. + +#ifndef ALICEO2_DATAFORMATSTRK_ROFRECORD_H +#define ALICEO2_DATAFORMATSTRK_ROFRECORD_H + +#include "CommonDataFormat/InteractionRecord.h" +#include "CommonDataFormat/RangeReference.h" +#include +#include +#include + +namespace o2::trk +{ + +class ROFRecord +{ + public: + using EvIdx = o2::dataformats::RangeReference; + using BCData = o2::InteractionRecord; + using ROFtype = unsigned int; + + ROFRecord() = default; + ROFRecord(const BCData& bc, ROFtype rof, int idx, int n) + : mBCData(bc), mROFEntry(idx, n), mROFrame(rof) {} + + void setBCData(const BCData& bc) { mBCData = bc; } + void setROFrame(ROFtype rof) { mROFrame = rof; } + void setEntry(EvIdx entry) { mROFEntry = entry; } + void setFirstEntry(int idx) { mROFEntry.setFirstEntry(idx); } + void setNEntries(int n) { mROFEntry.setEntries(n); } + + const BCData& getBCData() const { return mBCData; } + BCData& getBCData() { return mBCData; } + EvIdx getEntry() const { return mROFEntry; } + EvIdx& getEntry() { return mROFEntry; } + int getNEntries() const { return mROFEntry.getEntries(); } + int getFirstEntry() const { return mROFEntry.getFirstEntry(); } + ROFtype getROFrame() const { return mROFrame; } + + std::string asString() const; + + private: + o2::InteractionRecord mBCData; + EvIdx mROFEntry; + ROFtype mROFrame = 0; + + ClassDefNV(ROFRecord, 1); +}; + +struct MC2ROFRecord { + using ROFtype = unsigned int; + + int eventRecordID = -1; + int rofRecordID = 0; + ROFtype minROF = 0; + ROFtype maxROF = 0; + + MC2ROFRecord() = default; + MC2ROFRecord(int evID, int rofRecID, ROFtype mnrof, ROFtype mxrof) : eventRecordID(evID), rofRecordID(rofRecID), minROF(mnrof), maxROF(mxrof) {} + + ClassDefNV(MC2ROFRecord, 1); +}; + +} // namespace o2::trk + +#endif diff --git a/DataFormats/Detectors/Upgrades/ALICE3/TRK/src/Cluster.cxx b/DataFormats/Detectors/Upgrades/ALICE3/TRK/src/Cluster.cxx new file mode 100644 index 0000000000000..6c96692ea5a9e --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/TRK/src/Cluster.cxx @@ -0,0 +1,28 @@ +// 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 "DataFormatsTRK/Cluster.h" +#include + +ClassImp(o2::trk::Cluster); + +namespace o2::trk +{ + +std::string Cluster::asString() const +{ + std::ostringstream stream; + stream << "chip=" << chipID << " row=" << row << " col=" << col << " size=" << size + << " subDet=" << subDetID << " layer=" << layer << " disk=" << disk; + return stream.str(); +} + +} // namespace o2::trk diff --git a/DataFormats/Detectors/Upgrades/ALICE3/TRK/src/DataFormatsTRKLinkDef.h b/DataFormats/Detectors/Upgrades/ALICE3/TRK/src/DataFormatsTRKLinkDef.h new file mode 100644 index 0000000000000..36528d9dd2c46 --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/TRK/src/DataFormatsTRKLinkDef.h @@ -0,0 +1,25 @@ +// 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. + +#ifdef __CLING__ + +#pragma link off all globals; +#pragma link off all classes; +#pragma link off all functions; + +#pragma link C++ class o2::trk::Cluster + ; +#pragma link C++ class std::vector < o2::trk::Cluster> + ; +#pragma link C++ class o2::trk::ROFRecord + ; +#pragma link C++ class std::vector < o2::trk::ROFRecord> + ; +#pragma link C++ class o2::trk::MC2ROFRecord + ; +#pragma link C++ class std::vector < o2::trk::MC2ROFRecord> + ; + +#endif diff --git a/DataFormats/Detectors/Upgrades/ALICE3/TRK/src/ROFRecord.cxx b/DataFormats/Detectors/Upgrades/ALICE3/TRK/src/ROFRecord.cxx new file mode 100644 index 0000000000000..79745f9854eb7 --- /dev/null +++ b/DataFormats/Detectors/Upgrades/ALICE3/TRK/src/ROFRecord.cxx @@ -0,0 +1,29 @@ +// 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 "DataFormatsTRK/ROFRecord.h" +#include + +ClassImp(o2::trk::ROFRecord); +ClassImp(o2::trk::MC2ROFRecord); + +namespace o2::trk +{ + +std::string ROFRecord::asString() const +{ + std::ostringstream stream; + stream << "IR=" << mBCData.asString() << " ROFrame=" << mROFrame + << " first=" << mROFEntry.getFirstEntry() << " n=" << mROFEntry.getEntries(); + return stream.str(); +} + +} // namespace o2::trk diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt index d9908bbfeb1e5..edd9c785d89ce 100644 --- a/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt @@ -28,4 +28,12 @@ o2_add_test_root_macro(CheckTracksCA.C O2::TRKBase O2::TRKSimulation O2::Steer - LABELS trk COMPILE_ONLY) \ No newline at end of file + LABELS trk COMPILE_ONLY) + +o2_add_test_root_macro(CheckClusters.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsTRK + O2::SimulationDataFormat + O2::Framework + O2::TRKBase + O2::TRKSimulation + LABELS trk COMPILE_ONLY) diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckClusters.C b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckClusters.C new file mode 100644 index 0000000000000..327577102d86e --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckClusters.C @@ -0,0 +1,417 @@ +// 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. + +/// \file CheckClusters.C +/// \brief Macro to check TRK clusters and compare cluster positions to MC hit positions + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DataFormatsTRK/Cluster.h" +#include "DataFormatsTRK/ROFRecord.h" +#include "TRKBase/GeometryTGeo.h" +#include "TRKBase/SegmentationChip.h" +#include "TRKSimulation/Hit.h" +#include "ITSMFTSimulation/AlpideSimResponse.h" +#include "CCDB/BasicCCDBManager.h" +#include "MathUtils/Cartesian.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" +#include "DetectorsBase/GeometryManager.h" +#include "Framework/Logger.h" +#endif + +void CheckClusters(const std::string& clusfile = "o2clus_trk.root", + const std::string& hitfile = "o2sim_HitsTRK.root", + const std::string& inputGeom = "o2sim_geometry.root", + const std::string& ccdbUrl = "http://alice-ccdb.cern.ch", + long ccdbTimestamp = -1, + bool batch = false) +{ + gROOT->SetBatch(batch); + + using o2::MCCompLabel; + using ROFRec = o2::trk::ROFRecord; + using MC2ROF = o2::trk::MC2ROFRecord; + using HitVec = std::vector; + using MC2HITS_map = std::unordered_map; // maps (trackID << 32) + chipID -> hit index + + // ── Chip response (for hit-segment propagation to charge-collection plane) ── + // Fetches the same AlpideSimResponse from CCDB as the digitizer (IT3/Calib/APTSResponse) + // and computes Y-intersection planes with the same formulas from Digitizer::init() + auto& ccdbMgr = o2::ccdb::BasicCCDBManager::instance(); + ccdbMgr.setURL(ccdbUrl); + if (ccdbTimestamp > 0) { + ccdbMgr.setTimestamp(ccdbTimestamp); + } + auto* alpResp = ccdbMgr.get("IT3/Calib/APTSResponse"); + if (!alpResp) { + LOGP(fatal, "Cannot retrieve AlpideSimResponse from CCDB at {}", ccdbUrl); + return; + } + const float depthMax = alpResp->getDepthMax(); + + // ── Y-plane shifts: why VD and ML/OT need different values ──────────────── + // + // The APTS pixel response (AlpideSimResponse) uses an internal Y axis where: + // + // y = depthMax ── beam-entry (top) surface + // y = 0 ── charge-collection plane ← where clusters form + // y < 0 ── substrate (no response) + // + // The digitizer (Digitizer::init()) brings hit Y coordinates into this frame + // by adding a per-sub-detector shift before querying the response: + // + // y_APTS = y_local + shift [Digitizer.cxx ::processHit] + // + // The collection plane (y_APTS = 0) is therefore at y_local = −shift + // in the detector local frame. That is the Y value used here when + // propagating the MC hit segment to a single representative point. + // + // ── VD (vertex detector – curved sensors) ───────────────────────────────── + // After SegmentationChip::curvedToFlat() (convention: yFlat = dist − R): + // outer face (beam-entry): yFlat = +halfThickVD = +10 µm + // inner face (exit): yFlat = −halfThickVD = −10 µm + // The digitizer uses: + // + // mSimRespVDShift = depthMax − halfThickVD + // + // so the collection plane (y_APTS = 0) corresponds to: + // + // yPlaneVD = alice3resp::responseYShift = +5 µm + // + // i.e. 5 µm inside from the outer (entry) face. ✓ + // + // ── ML/OT (middle/outer tracker – flat sensors) ──────────────────────────── + // The local Y origin is at the GEOMETRIC CENTRE of the sensor volume. + // The outer (entry) surface is at y_local = +SiliconThicknessMLOT/2. + // The digitizer uses: + // + // mSimRespMLOTShift = depthMax − SiliconThicknessMLOT / 2 + // + // so the collection plane (y_APTS = 0) is at: + // + // yPlaneMLOT = SiliconThicknessMLOT/2 − depthMax + // + // ────────────────────────────────────────────────────────────────────────── + const float halfThicknessMLOT = o2::trk::SegmentationChip::SiliconThicknessMLOT / 2.f; + const float yPlaneVD = (float)o2::trk::constants::alice3resp::responseYShift; // VD: collection plane 5 µm inside outer (entry) face in flat local frame + const float yPlaneMLOT = halfThicknessMLOT - depthMax; // MLOT: entry @ +halfThick, collection depthMax below entry + LOGP(info, "Response depthMax = {:.4f} cm | VD Y-plane = {:.4f} cm | ML/OT Y-plane = {:.4f} cm", + depthMax, yPlaneVD, yPlaneMLOT); + + // ── Geometry ─────────────────────────────────────────────────────────────── + o2::base::GeometryManager::loadGeometry(inputGeom); + auto gman = o2::trk::GeometryTGeo::Instance(); + gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); + + // ── Hits ─────────────────────────────────────────────────────────────────── + TFile fileH(hitfile.data()); + auto* hitTree = dynamic_cast(fileH.Get("o2sim")); + if (!hitTree) { + LOGP(error, "Cannot find o2sim tree in {}", hitfile); + return; + } + std::vector mc2hitVec; + std::vector hitVecPool; + mc2hitVec.resize(hitTree->GetEntries()); + hitVecPool.resize(hitTree->GetEntries(), nullptr); + + // ── Clusters ─────────────────────────────────────────────────────────────── + TFile fileC(clusfile.data()); + auto* clusTree = dynamic_cast(fileC.Get("o2sim")); + if (!clusTree) { + LOGP(error, "Cannot find o2sim tree in {}", clusfile); + return; + } + + std::vector* clusArr = nullptr; + std::vector* rofRecVecP = nullptr; + std::vector* patternsPtr = nullptr; + clusTree->SetBranchAddress("TRKClusterComp", &clusArr); + clusTree->SetBranchAddress("TRKClustersROF", &rofRecVecP); + if (clusTree->GetBranch("TRKClusterPatt") != nullptr) { + clusTree->SetBranchAddress("TRKClusterPatt", &patternsPtr); + } + + o2::dataformats::MCTruthContainer* clusLabArr = nullptr; + std::vector mc2rofVec, *mc2rofVecP = &mc2rofVec; + bool hasMC = (clusTree->GetBranch("TRKClusterMCTruth") != nullptr); + if (hasMC) { + clusTree->SetBranchAddress("TRKClusterMCTruth", &clusLabArr); + clusTree->SetBranchAddress("TRKClustersMC2ROF", &mc2rofVecP); + } + + clusTree->GetEntry(0); + const unsigned int nROFRec = rofRecVecP ? (unsigned int)rofRecVecP->size() : 0u; + LOGP(info, "Number of ROF records: {}", nROFRec); + auto pattIt = patternsPtr ? patternsPtr->cbegin() : std::vector::const_iterator{}; + + // ── Build per-ROF MC event range ─────────────────────────────────────────── + std::vector mcEvMin(nROFRec, (int)hitTree->GetEntries()); + std::vector mcEvMax(nROFRec, -1); + if (hasMC) { + for (int imc = (int)mc2rofVec.size(); imc--;) { + const auto& mc2rof = mc2rofVec[imc]; + if (mc2rof.rofRecordID < 0) { + continue; + } + for (unsigned int irfd = mc2rof.maxROF - mc2rof.minROF + 1; irfd--;) { + unsigned int irof = mc2rof.rofRecordID + irfd; + if (irof >= nROFRec) { + continue; + } + if (mcEvMin[irof] > imc) { + mcEvMin[irof] = imc; + } + if (mcEvMax[irof] < imc) { + mcEvMax[irof] = imc; + } + } + } + } + + // ── Output ───────────────────────────────────────────────────────────────── + TFile fout("CheckClusters.root", "recreate"); + // columns: event, MC track label, + // local hit x/z (flat frame), global hit x/y/z (midpoint), + // global cluster x/y/z, local cluster x/z, + // residuals dx/dz (local, cluster - hit), + // ROF frame, cluster size, chipID, layer, disk, subDetID, row, col, pt + TNtuple nt("ntc", "TRK cluster ntuple", + "event:mcTrackID:hitLocX:hitLocZ:hitGlobX:hitGlobY:hitGlobZ:clusGlobX:clusGlobY:clusGlobZ:clusLocX:clusLocZ:rofFrame:clusSize:chipID:layer:disk:subdet:row:col:pt"); + + // ── Counters ─────────────────────────────────────────────────────────────── + long nTot{0}, nInvalidLabel{0}, nNoMCHit{0}, nValid{0}; + + // ── Main loop ────────────────────────────────────────────────────────────── + for (unsigned int irof = 0; irof < nROFRec; irof++) { + const auto& rofRec = (*rofRecVecP)[irof]; + + // Cache MC hit events for this ROF + if (hasMC) { + for (int im = mcEvMin[irof]; im <= mcEvMax[irof]; im++) { + if (hitVecPool[im] == nullptr) { + hitTree->SetBranchAddress("TRKHit", &hitVecPool[im]); + hitTree->GetEntry(im); + auto& mc2hit = mc2hitVec[im]; + const auto* hv = hitVecPool[im]; + for (int ih = (int)hv->size(); ih--;) { + const auto& hit = (*hv)[ih]; + uint64_t key = (uint64_t(hit.GetTrackID()) << 32) + hit.GetDetectorID(); + mc2hit.emplace(key, ih); + } + } + } + } + + for (int icl = 0; icl < rofRec.getNEntries(); icl++) { + const int clEntry = rofRec.getFirstEntry() + icl; + const auto& cluster = (*clusArr)[clEntry]; + nTot++; + + // ── Parse pattern → center-of-gravity within bounding box ────────── + // The cluster stores the bounding-box top-left pixel (row, col). + // The pattern stream encodes [rowSpan, colSpan, bitmap...] for each cluster. + // We accumulate pixel row/col offsets to obtain a sub-pixel CoG correction. + float cogDr{0.f}, cogDc{0.f}; // mean offsets from bbox origin (pixels) + if (patternsPtr) { + const uint8_t rowSpan = *pattIt++; + const uint8_t colSpan = *pattIt++; + const int nBytes = (rowSpan * colSpan + 7) / 8; + int nPix{0}, pixIdx{0}; + for (int ib = 0; ib < nBytes; ib++) { + const uint8_t byte = *pattIt++; + for (int bit = 7; bit >= 0 && pixIdx < rowSpan * colSpan; bit--, pixIdx++) { + if (byte & (1 << bit)) { + cogDr += pixIdx / colSpan; + cogDc += pixIdx % colSpan; + nPix++; + } + } + } + if (nPix > 1) { + cogDr /= nPix; + cogDc /= nPix; + } + } + + // ── Cluster local → global (CoG position) ───────────────────────────── + // Get local coords of the bounding-box corner pixel, then apply the + // fractional CoG displacement using the pixel pitch. + // Formula from detectorToLocalUnchecked: + // VD : xRow = 0.5*(width[lay]-pitchRow) - row*pitchRow → row↑ xRow↓ + // zCol = col*pitchCol + 0.5*(pitchCol-length) → col↑ zCol↑ + // MLOT: same structure with MLOT pitches + float clLocX{0.f}, clLocZ{0.f}; + o2::trk::SegmentationChip::detectorToLocalUnchecked( + cluster.row, cluster.col, clLocX, clLocZ, + cluster.subDetID, cluster.layer, cluster.disk); + const float pitchRow = (cluster.subDetID == 0) + ? o2::trk::SegmentationChip::PitchRowVD + : o2::trk::SegmentationChip::PitchRowMLOT; + const float pitchCol = (cluster.subDetID == 0) + ? o2::trk::SegmentationChip::PitchColVD + : o2::trk::SegmentationChip::PitchColMLOT; + clLocX -= cogDr * pitchRow; // increasing row → decreasing xRow + clLocZ += cogDc * pitchCol; // increasing col → increasing zCol + const float yResponse = (cluster.subDetID == 0) ? yPlaneVD : yPlaneMLOT; + // For VD the L2G matrix is built in the *curved* local frame (quasi-Cartesian, + // origin at the beam axis). Convert flat (clLocX, 0) → curved (xC, yC) first. + // For MLOT (flat sensors) the local frame is already Cartesian: pass directly. + // clLocX is already in the flat frame from detectorToLocalUnchecked + CoG and + // does NOT need any further transformation for the residual comparison. + o2::math_utils::Point3D locC; + if (cluster.subDetID == 0) { + auto cv = o2::trk::SegmentationChip::flatToCurved(cluster.layer, clLocX, 0.f); + locC = {cv.X(), cv.Y(), clLocZ}; + } else { + locC = {clLocX, yResponse, clLocZ}; + } + auto gloC = gman->getMatrixL2G(cluster.chipID)(locC); + + if (!hasMC || clusLabArr == nullptr) { + // No MC info: just fill geometry columns, leave residuals as 0 + std::array data = { + -1.f, -1.f, + 0.f, 0.f, 0.f, 0.f, 0.f, + (float)gloC.X(), (float)gloC.Y(), (float)gloC.Z(), + clLocX, clLocZ, + (float)rofRec.getROFrame(), (float)cluster.size, (float)cluster.chipID, + (float)cluster.layer, (float)cluster.disk, (float)cluster.subDetID, + (float)cluster.row, (float)cluster.col, -1.f}; + nt.Fill(data.data()); + continue; + } + + // ── MC label ─────────────────────────────────────────────────────── + const auto& labels = clusLabArr->getLabels(clEntry); + if (labels.empty() || !labels[0].isValid()) { + nInvalidLabel++; + continue; + } + const auto& lab = labels[0]; + const int trID = lab.getTrackID(); + const int evID = lab.getEventID(); + + // ── Find matching MC hit ──────────────────────────────────────────── + const auto& mc2hit = mc2hitVec[evID]; + uint64_t key = (uint64_t(trID) << 32) + cluster.chipID; + auto hitEntry = mc2hit.find(key); + if (hitEntry == mc2hit.end()) { + nNoMCHit++; + continue; + } + const auto& hit = (*hitVecPool[evID])[hitEntry->second]; + const float pt = TMath::Hypot(hit.GetPx(), hit.GetPy()); + + // ── Hit global midpoint ──────────────────────────────────────────── + const auto& gloHend = hit.GetPos(); + const auto& gloHsta = hit.GetPosStart(); + o2::math_utils::Point3D gloHmid( + 0.5f * (gloHend.X() + gloHsta.X()), + 0.5f * (gloHend.Y() + gloHsta.Y()), + 0.5f * (gloHend.Z() + gloHsta.Z())); + + // ── Hit global → local ───────────────────────────── + o2::math_utils::Point3D locHsta = gman->getMatrixL2G(cluster.chipID) ^ (gloHsta); // inverse L2G + o2::math_utils::Point3D locHend = gman->getMatrixL2G(cluster.chipID) ^ (gloHend); // inverse L2G + + // ── Propagate hit segment to the sensor response surface ─────────────── + // Rather than the geometric midpoint, find where the track segment crosses + // the response plane (y = responseYShift in the flat local frame). + // For VD (curved): convert both endpoints to flat frame first. + // For ML/OT (flat): use local coordinates directly. + float hitLocX{0.f}, hitLocZ{0.f}; + if (cluster.subDetID == 0) { // VD – curved sensor + auto flatSta = o2::trk::SegmentationChip::curvedToFlat(cluster.layer, locHsta.X(), locHsta.Y()); + auto flatEnd = o2::trk::SegmentationChip::curvedToFlat(cluster.layer, locHend.X(), locHend.Y()); + float x0 = flatSta.X(), y0 = flatSta.Y(), z0 = locHsta.Z(); + float dltx = flatEnd.X() - x0, dlty = flatEnd.Y() - y0, dltz = locHend.Z() - z0; + float r = (std::abs(dlty) > 1e-9f) ? (yPlaneVD - y0) / dlty : 0.5f; + hitLocX = x0 + r * dltx; + hitLocZ = z0 + r * dltz; + } else { // ML/OT – flat sensor + float x0 = locHsta.X(), y0 = locHsta.Y(), z0 = locHsta.Z(); + float dltx = locHend.X() - x0, dlty = locHend.Y() - y0, dltz = locHend.Z() - z0; + float r = (std::abs(dlty) > 1e-9f) ? (yPlaneMLOT - y0) / dlty : 0.5f; + hitLocX = x0 + r * dltx; + hitLocZ = z0 + r * dltz; + } + + nValid++; + std::array data = { + (float)evID, (float)trID, + hitLocX, hitLocZ, + (float)gloHmid.X(), (float)gloHmid.Y(), (float)gloHmid.Z(), + (float)gloC.X(), (float)gloC.Y(), (float)gloC.Z(), + clLocX, clLocZ, + (float)rofRec.getROFrame(), (float)cluster.size, (float)cluster.chipID, + (float)cluster.layer, (float)cluster.disk, (float)cluster.subDetID, + (float)cluster.row, (float)cluster.col, pt}; + nt.Fill(data.data()); + } + } + + // ── Summary ──────────────────────────────────────────────────────────────── + LOGP(info, "=== TRK Cluster vs Hit Summary ==="); + LOGP(info, "Total clusters: {}", nTot); + LOGP(info, "Valid (hit matched): {}", nValid); + LOGP(info, "Invalid/noise MC labels: {}", nInvalidLabel); + LOGP(info, "MC hit not found: {}", nNoMCHit); + // ── Visualisation ────────────────────────────────────────────────────────── + auto canvGlobal = new TCanvas("canvGlobal", "Cluster global positions", 1600, 800); + canvGlobal->Divide(2, 1); + canvGlobal->cd(1); + nt.Draw("clusGlobY:clusGlobX>>h_yx(500,-50,50,500,-50,50)", "", "colz"); + canvGlobal->cd(2); + nt.Draw("clusGlobY:clusGlobZ>>h_yz(500,-100,100,500,-50,50)", "", "colz"); + canvGlobal->SaveAs("trk_clusters_global.png"); + + auto canvRes = new TCanvas("canvRes", "Residuals (cluster - hit) [cm]", 1600, 1200); + canvRes->Divide(2, 3); + canvRes->cd(1)->SetLogy(); + nt.Draw("hitLocX-clusLocX>>h_dx_VD(200,-0.02,0.02)", "subdet==0&&event>=0"); + canvRes->cd(2)->SetLogy(); + nt.Draw("hitLocZ-clusLocZ>>h_dz_VD(200,-0.02,0.02)", "subdet==0&&event>=0"); + canvRes->cd(3)->SetLogy(); + nt.Draw("hitLocX-clusLocX>>h_dx_MLOT(200,-0.02,0.02)", "subdet==1&&event>=0"); + canvRes->cd(4)->SetLogy(); + nt.Draw("hitLocZ-clusLocZ>>h_dz_MLOT(200,-0.02,0.02)", "subdet==1&&event>=0"); + canvRes->cd(5)->SetLogz(); + nt.Draw("hitLocX-clusLocX:hitLocZ-clusLocZ>>h_dxdz_VD(200,-0.02,0.02,200,-0.02,0.02)", "subdet==0&&event>=0", "colz"); + canvRes->cd(6); + nt.Draw("hitLocX-clusLocX:hitLocZ-clusLocZ>>h_dxdz_MLOT(200,-0.02,0.02,200,-0.02,0.02)", "subdet==1&&event>=0", "colz"); + canvRes->SaveAs("trk_residuals.png"); + + auto canvResVsLayer = new TCanvas("canvResVsLayer", "Residuals vs layer", 1600, 600); + canvResVsLayer->Divide(2, 1); + canvResVsLayer->cd(1); + nt.Draw("hitLocX-clusLocX:layer>>h_dx_vs_lay(20,0,20,200,-0.02,0.02)", "event>=0", "prof"); + canvResVsLayer->cd(2); + nt.Draw("hitLocZ-clusLocZ:layer>>h_dz_vs_lay(20,0,20,200,-0.02,0.02)", "event>=0", "prof"); + canvResVsLayer->SaveAs("trk_residuals_vs_layer.png"); + + fout.cd(); + nt.Write(); + fout.Close(); + + LOGP(info, "Output saved to CheckClusters.root and PNG files"); +} diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt index 01ddc783d192b..b9866c7d6aa4d 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt @@ -12,12 +12,14 @@ o2_add_library(TRKReconstruction TARGETVARNAME targetName SOURCES src/TimeFrame.cxx + src/Clusterer.cxx PUBLIC_LINK_LIBRARIES O2::ITStracking O2::GPUCommon Microsoft.GSL::GSL O2::CommonConstants O2::DataFormatsITSMFT + O2::DataFormatsTRK O2::SimulationDataFormat O2::ITSBase O2::ITSReconstruction @@ -31,4 +33,5 @@ o2_add_library(TRKReconstruction o2_target_root_dictionary(TRKReconstruction HEADERS include/TRKReconstruction/TimeFrame.h + include/TRKReconstruction/Clusterer.h LINKDEF src/TRKReconstructionLinkDef.h) diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/Clusterer.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/Clusterer.h new file mode 100644 index 0000000000000..abddafa312fb9 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/Clusterer.h @@ -0,0 +1,182 @@ +// 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. + +/// \file Clusterer.h +/// \brief Definition of the TRK cluster finder + +#ifndef ALICEO2_TRK_CLUSTERER_H +#define ALICEO2_TRK_CLUSTERER_H + +// uncomment to allow diagonal clusters, e.g. |* | +// | *| +#define _ALLOW_DIAGONAL_TRK_CLUSTERS_ + +#include "DataFormatsITSMFT/Digit.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "DataFormatsITSMFT/ClusterPattern.h" +#include "DataFormatsTRK/Cluster.h" +#include "DataFormatsTRK/ROFRecord.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" +#include "TRKBase/Specs.h" +#include +#include +#include +#include +#include +#include + +namespace o2::trk +{ + +class GeometryTGeo; + +class Clusterer +{ + public: + static constexpr int MaxLabels = 10; + static constexpr int MaxHugeClusWarn = 5; + + using Digit = o2::itsmft::Digit; + using DigROFRecord = o2::itsmft::ROFRecord; + using DigMC2ROFRecord = o2::itsmft::MC2ROFRecord; + using ClusterTruth = o2::dataformats::MCTruthContainer; + using ConstDigitTruth = o2::dataformats::ConstMCTruthContainerView; + using Label = o2::MCCompLabel; + + //---------------------------------------------- + struct BBox { + uint16_t chipID = 0xffff; + uint16_t rowMin = 0xffff, colMin = 0xffff; + uint16_t rowMax = 0, colMax = 0; + explicit BBox(uint16_t c) : chipID(c) {} + bool isInside(uint16_t r, uint16_t c) const { return r >= rowMin && r <= rowMax && c >= colMin && c <= colMax; } + uint16_t rowSpan() const { return rowMax - rowMin + 1; } + uint16_t colSpan() const { return colMax - colMin + 1; } + bool isAcceptableSize() const + { + return rowSpan() <= o2::itsmft::ClusterPattern::MaxRowSpan && + colSpan() <= o2::itsmft::ClusterPattern::MaxColSpan; + } + void adjust(uint16_t r, uint16_t c) + { + if (r < rowMin) { + rowMin = r; + } + if (r > rowMax) { + rowMax = r; + } + if (c < colMin) { + colMin = c; + } + if (c > colMax) { + colMax = c; + } + } + }; + + //---------------------------------------------- + struct ClustererThread { + Clusterer* parent = nullptr; + // column buffers (pre-cluster state); extra sentinel entries at [0] and [size-1] + int* column1 = nullptr; + int* column2 = nullptr; + int* curr = nullptr; ///< current column pre-cluster indices + int* prev = nullptr; ///< previous column pre-cluster indices + int size = constants::moduleMLOT::chip::nRows + 2; ///< reallocated per chip in initChip + + // pixels[i] = {next_in_chain, global_digit_index} + std::vector> pixels; + std::vector preClusterHeads; + std::vector preClusterIndices; + uint16_t currCol = 0xffff; + bool noLeftCol = true; + + std::array labelsBuff; ///< MC label buffer for one cluster + std::vector> pixArrBuff; ///< (row,col) pixel buffer for pattern + + // per-thread output (accumulated, then merged back by caller) + std::vector clusters; + std::vector patterns; + ClusterTruth labels; + + ///< reset column buffer + void resetColumn(int* buff) const { std::memset(buff, -1, sizeof(int) * (size - 2)); } + ///< swap current and previous column buffers + void swapColumnBuffers() { std::swap(prev, curr); } + + ///< append pixel ip to the pre-cluster headed at preClusterIndex + void expandPreCluster(uint32_t ip, uint16_t row, int preClusterIndex) + { + auto& firstIndex = preClusterHeads[preClusterIndices[preClusterIndex]]; + pixels.emplace_back(firstIndex, ip); + firstIndex = pixels.size() - 1; + curr[row] = preClusterIndex; + } + + ///< start a new pre-cluster with pixel ip at given row + void addNewPreCluster(uint32_t ip, uint16_t row) + { + preClusterHeads.push_back(pixels.size()); + pixels.emplace_back(-1, ip); + int lastIndex = preClusterIndices.size(); + preClusterIndices.push_back(lastIndex); + curr[row] = lastIndex; + } + + void fetchMCLabels(uint32_t digID, const ConstDigitTruth* labelsDig, int& nfilled); + void initChip(gsl::span digits, uint32_t first, GeometryTGeo* geom); + void updateChip(gsl::span digits, uint32_t ip); + void finishChip(gsl::span digits, + const ConstDigitTruth* labelsDigPtr, ClusterTruth* labelsClusPtr, + GeometryTGeo* geom); + void finishChipSingleHitFast(gsl::span digits, uint32_t hit, + const ConstDigitTruth* labelsDigPtr, ClusterTruth* labelsClusPtr, + GeometryTGeo* geom); + void processChip(gsl::span digits, int chipFirst, int chipN, + std::vector* clustersOut, std::vector* patternsOut, + const ConstDigitTruth* labelsDigPtr, ClusterTruth* labelsClusPtr, + GeometryTGeo* geom); + void streamCluster(const BBox& bbox, const std::vector>& pixbuf, + uint32_t totalCharge, bool doLabels, int nlab, + uint16_t chipID, int subDetID, int layer, int disk); + + ~ClustererThread() + { + delete[] column1; + delete[] column2; + } + explicit ClustererThread(Clusterer* par = nullptr) : parent(par) {} + ClustererThread(const ClustererThread&) = delete; + ClustererThread& operator=(const ClustererThread&) = delete; + }; + //---------------------------------------------- + + void process(gsl::span digits, + gsl::span digitROFs, + std::vector& clusters, + std::vector& patterns, + std::vector& clusterROFs, + const ConstDigitTruth* digitLabels = nullptr, + ClusterTruth* clusterLabels = nullptr, + gsl::span digMC2ROFs = {}, + std::vector* clusterMC2ROFs = nullptr); + + private: + int mNHugeClus = 0; + std::unique_ptr mThread; + std::vector mSortIdx; ///< reusable per-ROF sort buffer +}; + +} // namespace o2::trk + +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/Clusterer.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/Clusterer.cxx new file mode 100644 index 0000000000000..bdaa76319c1f2 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/Clusterer.cxx @@ -0,0 +1,419 @@ +// 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. + +/// \file Clusterer.cxx +/// \brief Implementation of the TRK cluster finder + +#include "TRKReconstruction/Clusterer.h" +#include "TRKBase/GeometryTGeo.h" + +#include +#include + +namespace o2::trk +{ + +//__________________________________________________ +void Clusterer::process(gsl::span digits, + gsl::span digitROFs, + std::vector& clusters, + std::vector& patterns, + std::vector& clusterROFs, + const ConstDigitTruth* digitLabels, + ClusterTruth* clusterLabels, + gsl::span digMC2ROFs, + std::vector* clusterMC2ROFs) +{ + if (!mThread) { + mThread = std::make_unique(this); + } + + auto* geom = o2::trk::GeometryTGeo::Instance(); + + for (size_t iROF = 0; iROF < digitROFs.size(); ++iROF) { + const auto& inROF = digitROFs[iROF]; + const auto outFirst = static_cast(clusters.size()); + const int first = inROF.getFirstEntry(); + const int nEntries = inROF.getNEntries(); + + if (nEntries == 0) { + clusterROFs.emplace_back(inROF.getBCData(), inROF.getROFrame(), outFirst, 0); + continue; + } + + // Sort digit indices within this ROF by (chipID, col, row) so we can process + // chip by chip, column by column -- the same ordering the ALPIDE scanner expects. + mSortIdx.resize(nEntries); + std::iota(mSortIdx.begin(), mSortIdx.end(), first); + std::sort(mSortIdx.begin(), mSortIdx.end(), [&digits](int a, int b) { + const auto& da = digits[a]; + const auto& db = digits[b]; + if (da.getChipIndex() != db.getChipIndex()) { + return da.getChipIndex() < db.getChipIndex(); + } + if (da.getColumn() != db.getColumn()) { + return da.getColumn() < db.getColumn(); + } + return da.getRow() < db.getRow(); + }); + + // Process one chip at a time + int sliceStart = 0; + while (sliceStart < nEntries) { + const int chipFirst = sliceStart; + const uint16_t chipID = digits[mSortIdx[sliceStart]].getChipIndex(); + while (sliceStart < nEntries && digits[mSortIdx[sliceStart]].getChipIndex() == chipID) { + ++sliceStart; + } + const int chipN = sliceStart - chipFirst; + + mThread->processChip(digits, chipFirst, chipN, &clusters, &patterns, digitLabels, clusterLabels, geom); + } + + clusterROFs.emplace_back(inROF.getBCData(), inROF.getROFrame(), + outFirst, static_cast(clusters.size()) - outFirst); + } + + if (clusterMC2ROFs && !digMC2ROFs.empty()) { + clusterMC2ROFs->reserve(clusterMC2ROFs->size() + digMC2ROFs.size()); + for (const auto& in : digMC2ROFs) { + clusterMC2ROFs->emplace_back(in.eventRecordID, in.rofRecordID, in.minROF, in.maxROF); + } + } +} + +//__________________________________________________ +void Clusterer::ClustererThread::processChip(gsl::span digits, + int chipFirst, int chipN, + std::vector* clustersOut, + std::vector* patternsOut, + const ConstDigitTruth* labelsDigPtr, + ClusterTruth* labelsClusPtr, + GeometryTGeo* geom) +{ + // chipFirst and chipN are relative to mSortIdx (i.e. mSortIdx[chipFirst..chipFirst+chipN-1] + // are the global digit indices for this chip, already sorted by col then row). + // We use parent->mSortIdx to resolve the global index of each pixel. + const auto& sortIdx = parent->mSortIdx; + + if (chipN == 1) { + finishChipSingleHitFast(digits, sortIdx[chipFirst], labelsDigPtr, labelsClusPtr, geom); + } else { + initChip(digits, sortIdx[chipFirst], geom); + for (int i = chipFirst + 1; i < chipFirst + chipN; ++i) { + updateChip(digits, sortIdx[i]); + } + finishChip(digits, labelsDigPtr, labelsClusPtr, geom); + } + + // Flush per-thread output into the caller's containers + if (!clusters.empty()) { + clustersOut->insert(clustersOut->end(), clusters.begin(), clusters.end()); + clusters.clear(); + } + if (!patterns.empty()) { + patternsOut->insert(patternsOut->end(), patterns.begin(), patterns.end()); + patterns.clear(); + } + if (labelsClusPtr && labels.getNElements()) { + labelsClusPtr->mergeAtBack(labels); + labels.clear(); + } +} + +//__________________________________________________ +void Clusterer::ClustererThread::initChip(gsl::span digits, uint32_t first, GeometryTGeo* geom) +{ + const uint16_t chipID = digits[first].getChipIndex(); + + // Determine the number of rows for this chip's sensor type + size = constants::moduleMLOT::chip::nRows + 2; // default for ML/OT + if (geom) { + if (geom->getSubDetID(chipID) == 0) { // VD + const int layer = geom->getLayer(chipID); + size = constants::VD::petal::layer::nRows[layer] + 2; + } + } + + delete[] column1; + delete[] column2; + column1 = new int[size]; + column2 = new int[size]; + column1[0] = column1[size - 1] = -1; + column2[0] = column2[size - 1] = -1; + prev = column1 + 1; + curr = column2 + 1; + resetColumn(curr); + + pixels.clear(); + preClusterHeads.clear(); + preClusterIndices.clear(); + + const auto& pix = digits[first]; + currCol = pix.getColumn(); + curr[pix.getRow()] = 0; + preClusterHeads.push_back(0); + preClusterIndices.push_back(0); + pixels.emplace_back(-1, first); + noLeftCol = true; +} + +//__________________________________________________ +void Clusterer::ClustererThread::updateChip(gsl::span digits, uint32_t ip) +{ + const auto& pix = digits[ip]; + uint16_t row = pix.getRow(); + + if (currCol != pix.getColumn()) { + swapColumnBuffers(); + resetColumn(curr); + noLeftCol = false; + if (pix.getColumn() > currCol + 1) { + // gap: no connection with previous column + currCol = pix.getColumn(); + addNewPreCluster(ip, row); + noLeftCol = true; + return; + } + currCol = pix.getColumn(); + } + + bool orphan = true; + + if (noLeftCol) { + if (curr[row - 1] >= 0) { + expandPreCluster(ip, row, curr[row - 1]); + return; + } + } else { +#ifdef _ALLOW_DIAGONAL_TRK_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; + } + if (orphan) { + expandPreCluster(ip, row, pci); + orphan = false; + continue; + } + // merge two pre-clusters: assign the smaller index to both + if (preClusterIndices[pci] < preClusterIndices[curr[row]]) { + preClusterIndices[curr[row]] = preClusterIndices[pci]; + } else { + preClusterIndices[pci] = preClusterIndices[curr[row]]; + } + } + } + if (orphan) { + addNewPreCluster(ip, row); + } +} + +//__________________________________________________ +void Clusterer::ClustererThread::finishChip(gsl::span digits, + const ConstDigitTruth* labelsDigPtr, + ClusterTruth* labelsClusPtr, + GeometryTGeo* geom) +{ + const uint16_t chipID = digits[pixels[0].second].getChipIndex(); + + for (size_t i1 = 0; i1 < preClusterHeads.size(); ++i1) { + auto ci = preClusterIndices[i1]; + if (ci < 0) { + continue; + } + BBox bbox(chipID); + int nlab = 0; + uint32_t totalCharge = 0; + pixArrBuff.clear(); + + // Walk the linked list for this pre-cluster head + auto collectPixels = [&](int head) { + int next = head; + while (next >= 0) { + const auto& pixEntry = pixels[next]; + const auto& d = digits[pixEntry.second]; + uint16_t r = d.getRow(), c = d.getColumn(); + pixArrBuff.emplace_back(r, c); + bbox.adjust(r, c); + totalCharge += d.getCharge(); + if (labelsClusPtr) { + fetchMCLabels(pixEntry.second, labelsDigPtr, nlab); + } + next = pixEntry.first; + } + }; + + collectPixels(preClusterHeads[i1]); + preClusterIndices[i1] = -1; + + for (size_t i2 = i1 + 1; i2 < preClusterHeads.size(); ++i2) { + if (preClusterIndices[i2] != ci) { + continue; + } + collectPixels(preClusterHeads[i2]); + preClusterIndices[i2] = -1; + } + + // Determine geometry info + int subDetID = -1, layer = -1, disk = -1; + if (geom) { + subDetID = geom->getSubDetID(chipID); + layer = geom->getLayer(chipID); + disk = geom->getDisk(chipID); + } + + const bool doLabels = (labelsClusPtr != nullptr); + if (bbox.isAcceptableSize()) { + streamCluster(bbox, pixArrBuff, totalCharge, doLabels, nlab, chipID, subDetID, layer, disk); + } else { + // Huge cluster: split into MaxRowSpan x MaxColSpan tiles (same as ITS3) + auto warnLeft = MaxHugeClusWarn - parent->mNHugeClus; + if (warnLeft > 0) { + LOGP(warn, "Splitting huge TRK cluster: chipID {}, rows {}:{} cols {}:{}{}", + chipID, bbox.rowMin, bbox.rowMax, bbox.colMin, bbox.colMax, + warnLeft == 1 ? " (further warnings muted)" : ""); + parent->mNHugeClus++; + } + BBox bboxT(chipID); + bboxT.colMin = bbox.colMin; + do { + bboxT.rowMin = bbox.rowMin; + bboxT.colMax = std::min(bbox.colMax, uint16_t(bboxT.colMin + o2::itsmft::ClusterPattern::MaxColSpan - 1)); + do { + bboxT.rowMax = std::min(bbox.rowMax, uint16_t(bboxT.rowMin + o2::itsmft::ClusterPattern::MaxRowSpan - 1)); + std::vector> subPix; + uint32_t subCharge = 0; + for (const auto& [r, c] : pixArrBuff) { + if (bboxT.isInside(r, c)) { + subPix.emplace_back(r, c); + subCharge += 1; + } + } + if (!subPix.empty()) { + streamCluster(bboxT, subPix, subCharge, doLabels, nlab, chipID, subDetID, layer, disk); + } + bboxT.rowMin = bboxT.rowMax + 1; + } while (bboxT.rowMin <= bbox.rowMax); + bboxT.colMin = bboxT.colMax + 1; + } while (bboxT.colMin <= bbox.colMax); + } + } + // flush per-thread output to the caller via processChip +} + +//__________________________________________________ +void Clusterer::ClustererThread::finishChipSingleHitFast(gsl::span digits, uint32_t hit, + const ConstDigitTruth* labelsDigPtr, + ClusterTruth* labelsClusPtr, + GeometryTGeo* geom) +{ + const auto& d = digits[hit]; + const uint16_t chipID = d.getChipIndex(); + const uint16_t row = d.getRow(); + const uint16_t col = d.getColumn(); + + if (labelsClusPtr) { + int nlab = 0; + fetchMCLabels(hit, labelsDigPtr, nlab); + const auto cnt = static_cast(clusters.size()); + for (int i = nlab; i--;) { + labels.addElement(cnt, labelsBuff[i]); + } + } + + // 1×1 pattern: rowSpan=1, colSpan=1, one byte = 0x80 + patterns.emplace_back(1); + patterns.emplace_back(1); + patterns.emplace_back(0x80); + + Cluster cluster; + cluster.chipID = chipID; + cluster.row = row; + cluster.col = col; + cluster.size = 1; + if (geom) { + cluster.subDetID = geom->getSubDetID(chipID); + cluster.layer = geom->getLayer(chipID); + cluster.disk = geom->getDisk(chipID); + } + clusters.emplace_back(cluster); +} + +//__________________________________________________ +void Clusterer::ClustererThread::streamCluster(const BBox& bbox, + const std::vector>& pixbuf, + uint32_t totalCharge, + bool doLabels, int nlab, + uint16_t chipID, int subDetID, int layer, int disk) +{ + if (doLabels) { + const auto cnt = static_cast(clusters.size()); + for (int i = nlab; i--;) { + labels.addElement(cnt, labelsBuff[i]); // accumulate in thread-local buffer + } + } + + const uint16_t rowSpanW = bbox.rowSpan(); + const uint16_t colSpanW = bbox.colSpan(); + + // Encode the pixel pattern bitmap (rowSpan, colSpan, bytes...) + std::array patt{}; + for (const auto& [r, c] : pixbuf) { + uint32_t ir = r - bbox.rowMin, ic = c - bbox.colMin; + int nbit = ir * colSpanW + ic; + patt[nbit >> 3] |= (0x1 << (7 - (nbit % 8))); + } + patterns.emplace_back(static_cast(rowSpanW)); + patterns.emplace_back(static_cast(colSpanW)); + int nBytes = (rowSpanW * colSpanW + 7) / 8; + patterns.insert(patterns.end(), patt.begin(), patt.begin() + nBytes); + + Cluster cluster; + cluster.chipID = chipID; + cluster.row = bbox.rowMin; + cluster.col = bbox.colMin; + cluster.size = static_cast(pixbuf.size()); + cluster.subDetID = static_cast(subDetID); + cluster.layer = static_cast(layer); + cluster.disk = static_cast(disk); + clusters.emplace_back(cluster); +} + +//__________________________________________________ +void Clusterer::ClustererThread::fetchMCLabels(uint32_t digID, const ConstDigitTruth* labelsDig, int& nfilled) +{ + if (nfilled >= MaxLabels) { + return; + } + if (!labelsDig || digID >= labelsDig->getIndexedSize()) { + return; + } + const auto& lbls = labelsDig->getLabels(digID); + for (int i = lbls.size(); i--;) { + int ic = nfilled; + for (; ic--;) { + if (labelsBuff[ic] == lbls[i]) { + return; // already present + } + } + labelsBuff[nfilled++] = lbls[i]; + if (nfilled >= MaxLabels) { + break; + } + } +} + +} // namespace o2::trk diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h index 09ab598ec626c..4eda22e350852 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h @@ -16,5 +16,6 @@ #pragma link off all functions; #pragma link C++ class o2::trk::TimeFrame < 11> + ; +#pragma link C++ class o2::trk::Clusterer + ; #endif diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/workflow/CMakeLists.txt index d6c8ea85c2bbd..42402fe6b62dc 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/CMakeLists.txt @@ -13,6 +13,8 @@ o2_add_library(TRKWorkflow TARGETVARNAME targetName SOURCES src/DigitReaderSpec.cxx src/DigitWriterSpec.cxx + src/ClustererSpec.cxx + src/ClusterWriterSpec.cxx src/TrackerSpec.cxx src/TrackWriterSpec.cxx src/RecoWorkflow.cxx @@ -20,6 +22,7 @@ o2_add_library(TRKWorkflow O2::GPUWorkflow O2::SimConfig O2::DataFormatsITSMFT + O2::DataFormatsTRK O2::SimulationDataFormat O2::DPLUtils O2::TRKBase diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClusterWriterSpec.h b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClusterWriterSpec.h new file mode 100644 index 0000000000000..50d823b497bb9 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClusterWriterSpec.h @@ -0,0 +1,24 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_TRK_CLUSTERWRITER +#define O2_TRK_CLUSTERWRITER + +#include "Framework/DataProcessorSpec.h" + +namespace o2::trk +{ + +framework::DataProcessorSpec getClusterWriterSpec(bool useMC); + +} // namespace o2::trk + +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClustererSpec.h b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClustererSpec.h new file mode 100644 index 0000000000000..bacc1057c7b07 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClustererSpec.h @@ -0,0 +1,39 @@ +// 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. + +#ifndef O2_TRK_CLUSTERERDPL +#define O2_TRK_CLUSTERERDPL + +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "TRKReconstruction/Clusterer.h" + +namespace o2::trk +{ + +class ClustererDPL : public o2::framework::Task +{ + public: + ClustererDPL(bool useMC) : mUseMC(useMC) {} + void init(o2::framework::InitContext& ic) final; + void run(o2::framework::ProcessingContext& pc) final; + + private: + bool mUseMC = true; + int mNThreads = 1; + o2::trk::Clusterer mClusterer; +}; + +o2::framework::DataProcessorSpec getClustererSpec(bool useMC); + +} // namespace o2::trk + +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/ClusterWriterSpec.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/ClusterWriterSpec.cxx new file mode 100644 index 0000000000000..bc3a75c646198 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/ClusterWriterSpec.cxx @@ -0,0 +1,65 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not 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 "TRKWorkflow/ClusterWriterSpec.h" +#include "DPLUtils/MakeRootTreeWriterSpec.h" +#include "DataFormatsTRK/Cluster.h" +#include "DataFormatsTRK/ROFRecord.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" + +using namespace o2::framework; + +namespace o2::trk +{ + +template +using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; +using ClustersType = std::vector; +using PatternsType = std::vector; +using ROFrameType = std::vector; +using LabelsType = o2::dataformats::MCTruthContainer; +using ROFRecLblType = std::vector; + +DataProcessorSpec getClusterWriterSpec(bool useMC) +{ + auto clustersSize = std::make_shared(0); + auto clustersSizeGetter = [clustersSize](ClustersType const& clusters) { + *clustersSize = clusters.size(); + }; + auto logger = [clustersSize](ROFrameType const& rofs) { + LOG(info) << "TRKClusterWriter pulled " << *clustersSize << " clusters, in " << rofs.size() << " RO frames"; + }; + + return MakeRootTreeWriterSpec("trk-cluster-writer", + "o2clus_trk.root", + MakeRootTreeWriterSpec::TreeAttributes{"o2sim", "Tree with TRK clusters"}, + BranchDefinition{InputSpec{"compclus", "TRK", "COMPCLUSTERS", 0}, + "TRKClusterComp", + clustersSizeGetter}, + BranchDefinition{InputSpec{"patterns", "TRK", "PATTERNS", 0}, + "TRKClusterPatt"}, + BranchDefinition{InputSpec{"ROframes", "TRK", "CLUSTERSROF", 0}, + "TRKClustersROF", + logger}, + BranchDefinition{InputSpec{"labels", "TRK", "CLUSTERSMCTR", 0}, + "TRKClusterMCTruth", + (useMC ? 1 : 0)}, + BranchDefinition{InputSpec{"MC2ROframes", "TRK", "CLUSTERSMC2ROF", 0}, + "TRKClustersMC2ROF", + (useMC ? 1 : 0)})(); +} + +} // namespace o2::trk diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/ClustererSpec.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/ClustererSpec.cxx new file mode 100644 index 0000000000000..8aec63d69206b --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/ClustererSpec.cxx @@ -0,0 +1,99 @@ +// 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 "TRKWorkflow/ClustererSpec.h" +#include "DetectorsBase/GeometryManager.h" +#include "DataFormatsTRK/Cluster.h" +#include "DataFormatsTRK/ROFRecord.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/Logger.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" + +namespace o2::trk +{ + +void ClustererDPL::init(o2::framework::InitContext& ic) +{ + mNThreads = std::max(1, ic.options().get("nthreads")); +} + +void ClustererDPL::run(o2::framework::ProcessingContext& 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); + + std::vector clusters; + std::vector patterns; + std::vector clusterROFs; + std::unique_ptr> clusterLabels; + std::vector clusterMC2ROFs; + if (mUseMC) { + clusterLabels = std::make_unique>(); + } + o2::base::GeometryManager::loadGeometry("o2sim_geometry.root", false, true); + + mClusterer.process(digits, + rofs, + clusters, + patterns, + clusterROFs, + mUseMC ? &labels : nullptr, + clusterLabels.get(), + mc2rofs, + mUseMC ? &clusterMC2ROFs : nullptr); + + pc.outputs().snapshot(o2::framework::Output{"TRK", "COMPCLUSTERS", 0}, clusters); + pc.outputs().snapshot(o2::framework::Output{"TRK", "PATTERNS", 0}, patterns); + pc.outputs().snapshot(o2::framework::Output{"TRK", "CLUSTERSROF", 0}, clusterROFs); + + if (mUseMC) { + pc.outputs().snapshot(o2::framework::Output{"TRK", "CLUSTERSMCTR", 0}, *clusterLabels); + pc.outputs().snapshot(o2::framework::Output{"TRK", "CLUSTERSMC2ROF", 0}, clusterMC2ROFs); + } + + LOGP(info, "TRKClusterer pushed {} clusters in {} ROFs", clusters.size(), clusterROFs.size()); +} + +o2::framework::DataProcessorSpec getClustererSpec(bool useMC) +{ + std::vector inputs; + inputs.emplace_back("digits", "TRK", "DIGITS", 0, o2::framework::Lifetime::Timeframe); + inputs.emplace_back("ROframes", "TRK", "DIGITSROF", 0, o2::framework::Lifetime::Timeframe); + + std::vector outputs; + outputs.emplace_back("TRK", "COMPCLUSTERS", 0, o2::framework::Lifetime::Timeframe); + outputs.emplace_back("TRK", "PATTERNS", 0, o2::framework::Lifetime::Timeframe); + outputs.emplace_back("TRK", "CLUSTERSROF", 0, o2::framework::Lifetime::Timeframe); + + if (useMC) { + inputs.emplace_back("labels", "TRK", "DIGITSMCTR", 0, o2::framework::Lifetime::Timeframe); + inputs.emplace_back("MC2ROframes", "TRK", "DIGITSMC2ROF", 0, o2::framework::Lifetime::Timeframe); + outputs.emplace_back("TRK", "CLUSTERSMCTR", 0, o2::framework::Lifetime::Timeframe); + outputs.emplace_back("TRK", "CLUSTERSMC2ROF", 0, o2::framework::Lifetime::Timeframe); + } + + return o2::framework::DataProcessorSpec{ + "trk-clusterer", + inputs, + outputs, + o2::framework::AlgorithmSpec{o2::framework::adaptFromTask(useMC)}, + o2::framework::Options{{"nthreads", o2::framework::VariantType::Int, 1, {"Number of clustering threads"}}}}; +} + +} // namespace o2::trk diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx index 5f6cbe2f96b04..d10feb4214f38 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/RecoWorkflow.cxx @@ -10,6 +10,9 @@ // or submit itself to any jurisdiction. #include "TRKWorkflow/RecoWorkflow.h" +#include "TRKWorkflow/ClustererSpec.h" +#include "TRKWorkflow/ClusterWriterSpec.h" +#include "TRKWorkflow/DigitReaderSpec.h" #include "TRKWorkflow/TrackerSpec.h" #include "TRKWorkflow/TrackWriterSpec.h" #include "Framework/CCDBParamSpec.h" @@ -28,10 +31,24 @@ framework::WorkflowSpec getWorkflow(bool useMC, o2::gpu::gpudatatypes::DeviceType dtype) { framework::WorkflowSpec specs; - specs.emplace_back(o2::trk::getTrackerSpec(useMC, hitRecoConfig, dtype)); + + if (!(upstreamDigits || upstreamClusters)) { + specs.emplace_back(o2::trk::getTRKDigitReaderSpec(useMC, false, "trkdigits.root")); + } + if (!upstreamClusters) { + specs.emplace_back(o2::trk::getClustererSpec(useMC)); + } if (!disableRootOutput) { - specs.emplace_back(o2::trk::getTrackWriterSpec(useMC)); + specs.emplace_back(o2::trk::getClusterWriterSpec(useMC)); + } + + if (!hitRecoConfig.empty()) { + LOGP(info, "Using hit reco config from file {}", hitRecoConfig); + specs.emplace_back(o2::trk::getTrackerSpec(useMC, hitRecoConfig, dtype)); + if (!disableRootOutput) { + specs.emplace_back(o2::trk::getTrackWriterSpec(useMC)); + } } return specs; diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx index 8fc67f0fa5567..20bd45557dac5 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx @@ -396,9 +396,13 @@ DataProcessorSpec getTrackerSpec(bool useMC, const std::string& hitRecoConfig, o 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); + constexpr bool expectClusterInputs = false; + if (expectClusterInputs) { + inputs.pop_back(); + 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")); From ebaff324a16d3d91da35e5c679cb823469442251 Mon Sep 17 00:00:00 2001 From: shahoian Date: Tue, 3 Mar 2026 11:08:29 +0100 Subject: [PATCH 319/701] Fix EMCAL workflows in case DISABLE_ROOT_OUTPUT=0 --- prodtests/full-system-test/dpl-workflow.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prodtests/full-system-test/dpl-workflow.sh b/prodtests/full-system-test/dpl-workflow.sh index a8f01a3ef1822..9c6c45c049576 100755 --- a/prodtests/full-system-test/dpl-workflow.sh +++ b/prodtests/full-system-test/dpl-workflow.sh @@ -363,7 +363,7 @@ if [[ ${O2_GPU_RTC:-$EPNSYNCMODE} == 1 ]] && [[ ( ${ALICE_O2_FST:-0} == 1 && ${F [[ ${EPN_NODE_MI100:-0} == 1 ]] && GPU_CONFIG_KEY+="GPU_proc_rtctech.overrideArchitecture=--offload-arch=gfx908;" fi -( workflow_has_parameter AOD || [[ -z "$DISABLE_ROOT_OUTPUT" ]] || needs_root_output o2-emcal-cell-writer-workflow ) && has_detector EMC && RAW_EMC_SUBSPEC=" --subspecification 1 " +( workflow_has_parameter AOD || [[ -z "$DISABLE_ROOT_OUTPUT" ]] || needs_root_output o2-emcal-cell-writer-workflow ) && has_detector EMC && RAW_EMC_SUBSPEC=" --subspecificationOut 1 " has_detector_reco MID && has_detector_matching MCHMID && MFTMCHConf="FwdMatching.useMIDMatch=true;" || MFTMCHConf="FwdMatching.useMIDMatch=false;" [[ -n ${MFTMCH_NCANDIDATES_OPT:-} ]] && MFTMCHConf+="${MFTMCH_NCANDIDATES_OPT}" @@ -568,7 +568,7 @@ if [[ $CTFINPUT == 0 && $DIGITINPUT == 0 ]]; then has_detector CTP && ! has_detector_from_global_reader CTP && add_W o2-ctp-reco-workflow "$DISABLE_ROOT_OUTPUT $CTP_CONFIG --ntf-to-average 1 --pipeline $(get_N ctp-raw-decoder CTP RAW 1)" has_detector PHS && ! has_detector_from_global_reader PHS && ! has_detector_flp_processing PHS && add_W o2-phos-reco-workflow "--input-type raw --output-type cells $DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT --pipeline $(get_N PHOSRawToCellConverterSpec PHS REST 1) $DISABLE_MC" has_detector CPV && ! has_detector_from_global_reader CPV && add_W o2-cpv-reco-workflow "--input-type $CPV_INPUT --output-type clusters $DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT --pipeline $(get_N CPVRawToDigitConverterSpec CPV REST 1),$(get_N CPVClusterizerSpec CPV REST 1) $DISABLE_MC" - has_detector EMC && ! has_detector_from_global_reader EMC && ! has_detector_flp_processing EMC && add_W o2-emcal-reco-workflow "--input-type raw --output-type cells ${RAW_EMC_SUBSPEC:-} $EMCRAW2C_CONFIG $DISABLE_ROOT_OUTPUT $DISABLE_MC --pipeline $(get_N EMCALRawToCellConverterSpec EMC REST 1 EMCREC)" + has_detector EMC && ! has_detector_from_global_reader EMC && ! has_detector_flp_processing EMC && add_W o2-emcal-reco-workflow "--input-type raw --output-type cells ${RAW_EMC_SUBSPEC:-} $EMCRAW2C_CONFIG --disable-root-output $DISABLE_MC --pipeline $(get_N EMCALRawToCellConverterSpec EMC REST 1 EMCREC)" fi has_detector_gpu ITS && GPU_INPUT+=",its-clusters" From 6c245eef5377fb70818a2c4bbed9b4ab4807b56e Mon Sep 17 00:00:00 2001 From: shahoian Date: Tue, 3 Mar 2026 13:49:55 +0100 Subject: [PATCH 320/701] Fix in filling empty ROFs 1st entry --- Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx b/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx index 8de29c62335b6..fc0dd5dbae7da 100644 --- a/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx @@ -165,12 +165,12 @@ void ClustererDPL::run(ProcessingContext& pc) } } } - int prevFirst{0}; + int prevLast{0}; for (auto& rof : expClusRofVec) { if (rof.getFirstEntry() < 0) { - rof.setFirstEntry(prevFirst); + rof.setFirstEntry(prevLast); } - prevFirst = rof.getFirstEntry(); + prevLast = rof.getFirstEntry() + rof.getNEntries(); } nROFs = expClusRofVec.size(); pc.outputs().snapshot(Output{Origin, "CLUSTERSROF", iLayer}, expClusRofVec); From 63c7419346ce8da63c3c5c77fab1dbe83b086472 Mon Sep 17 00:00:00 2001 From: shahoian Date: Tue, 3 Mar 2026 16:04:09 +0100 Subject: [PATCH 321/701] Possibility to dump FT0 eventsPerBC to file --- .../FT0EventsPerBcProcessor-Workflow.cxx | 3 ++- .../calibration/workflow/FT0EventsPerBcSpec.h | 22 ++++++++++++++++++- 2 files changed, 23 insertions(+), 2 deletions(-) diff --git a/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.cxx b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.cxx index 5cef707da2cca..38d634c20c828 100644 --- a/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.cxx +++ b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcProcessor-Workflow.cxx @@ -35,6 +35,7 @@ o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext co outputs, AlgorithmSpec(adaptFromTask(ccdbRequest)), Options{ + {"save-to-file", VariantType::Bool, false, {"Save calibration object to local file"}}, {"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"}}, @@ -45,4 +46,4 @@ o2::framework::WorkflowSpec defineDataProcessing(o2::framework::ConfigContext co 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 index d493e2a606613..1d4d4a75842e8 100644 --- a/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h +++ b/Detectors/FIT/FT0/calibration/workflow/FT0EventsPerBcSpec.h @@ -35,6 +35,8 @@ class FT0EventsPerBcProcessor final : public o2::framework::Task void init(o2::framework::InitContext& ic) final { o2::base::GRPGeomHelper::instance().setRequest(mCCDBRequest); + mSaveToFile = ic.options().get("save-to-file"); + if (ic.options().hasOption("slot-len-sec")) { mSlotLenSec = ic.options().get("slot-len-sec"); } @@ -73,6 +75,10 @@ class FT0EventsPerBcProcessor final : public o2::framework::Task void run(o2::framework::ProcessingContext& pc) final { + const auto& tinfo = pc.services().get(); + if (tinfo.globalRunNumberChanged || mRunNoFromDH < 1) { // new run is starting + mRunNoFromDH = tinfo.runNumber; + } o2::base::GRPGeomHelper::instance().checkUpdates(pc); auto digits = pc.inputs().get>("digits"); o2::base::TFIDInfoHelper::fillTFIDInfo(pc, mCalibrator->getCurrentTFInfo()); @@ -107,6 +113,18 @@ class FT0EventsPerBcProcessor final : public o2::framework::Task << " 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 (mSaveToFile) { + std::string fnout = fmt::format("ft0eventsPerBC_run_{}_{}_{}.root", mRunNoFromDH, info->getStartValidityTimestamp(), info->getEndValidityTimestamp()); + try { + TFile flout(fnout.c_str(), "recreate"); + flout.WriteObjectAny(&payload, "o2::ft0::EventsPerBc", o2::ccdb::CcdbApi::CCDBOBJECT_ENTRY); + LOGP(info, R"(Saved to file, can upload as: o2-ccdb-upload -f {} --starttimestamp {} --endtimestamp {} -k "ccdb_object" --path {} -m "runNumber={};AdjustableEOV=true;")", + fnout, info->getStartValidityTimestamp(), info->getEndValidityTimestamp(), info->getPath(), mRunNoFromDH); + flout.Close(); + } catch (const std::exception& ex) { + LOGP(error, "failed to store object to file {}, error: {}", fnout, ex.what()); + } + } } if (tvxHists.size()) { @@ -118,6 +136,8 @@ class FT0EventsPerBcProcessor final : public o2::framework::Task std::shared_ptr mCCDBRequest; std::unique_ptr mCalibrator; bool mOneObjectPerRun; + bool mSaveToFile = false; + int mRunNoFromDH = 0; uint32_t mSlotLenSec; uint32_t mMinNumberOfEntries; int32_t mMinAmplitudeSideA; @@ -125,4 +145,4 @@ class FT0EventsPerBcProcessor final : public o2::framework::Task int32_t mMinSumOfAmplitude; }; } // namespace o2::calibration -#endif \ No newline at end of file +#endif From d142160152169ec9c2d8ed6ff09df7ea793cb2d4 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 4 Mar 2026 08:12:10 +0100 Subject: [PATCH 322/701] Include sstream to avoid implicit instantion of forward-declared object (header anyway only used once --- Framework/Core/src/OptionsHelpers.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Framework/Core/src/OptionsHelpers.h b/Framework/Core/src/OptionsHelpers.h index 578ccc4935e69..3ca2dc1df330e 100644 --- a/Framework/Core/src/OptionsHelpers.h +++ b/Framework/Core/src/OptionsHelpers.h @@ -13,7 +13,7 @@ #define BOOST_BIND_GLOBAL_PLACEHOLDERS #include -#include +#include namespace boost::program_options { From b2575f95b53fbf19755aaba5c2d5c1319e228617 Mon Sep 17 00:00:00 2001 From: Pavel Larionov Date: Wed, 4 Mar 2026 10:56:13 +0100 Subject: [PATCH 323/701] [ALICE 3] Fix VD full cyl building for ACTS (#15116) * Fix full cyl building for ACTS * Please consider the following formatting changes --------- Co-authored-by: ALICE Action Bot --- .../ALICE3/TRK/simulation/src/VDLayer.cxx | 79 +++++++++++++++---- 1 file changed, 63 insertions(+), 16 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx index 411dd485684b9..a92dcd24d6038 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDLayer.cxx @@ -27,6 +27,13 @@ namespace o2 { namespace trk { + +// Helper function for floating point comparison +inline bool isFullCircle(double phiSpanDeg, double epsilon = 0.005) +{ + return (std::fabs(phiSpanDeg - 360.0) < epsilon); +} + // Base layer constructor VDLayer::VDLayer(int layerNumber, const std::string& layerName, double layerX2X0) : mLayerNumber(layerNumber), mLayerName(layerName), mX2X0(layerX2X0), mModuleWidth(4.54) @@ -88,8 +95,13 @@ TGeoVolume* VDCylindricalLayer::createSensor() const 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); + TGeoShape* shape; + if (isFullCircle(mPhiSpanDeg)) { + shape = new TGeoTube(rIn, rOut, halfZ); + } else { + const double halfPhi = 0.5 * mPhiSpanDeg; // degrees + shape = new TGeoTubeSeg(rIn, rOut, halfZ, -halfPhi, +halfPhi); + } auto* vol = new TGeoVolume(sensName.c_str(), shape, medSi); vol->SetLineColor(kYellow); vol->SetTransparency(30); @@ -138,10 +150,15 @@ TGeoVolume* VDDiskLayer::createSensor() const } 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); + TGeoShape* shape; + if (isFullCircle(mPhiSpanDeg)) { + shape = new TGeoTube(mRMin, mRMax, halfThickness); + } else { + const double halfPhi = 0.5 * mPhiSpanDeg; // degrees + shape = new TGeoTubeSeg(mRMin, mRMax, halfThickness, -halfPhi, +halfPhi); + } auto* sensVol = new TGeoVolume(sensName.c_str(), shape, medSi); sensVol->SetLineColor(kYellow); @@ -177,9 +194,14 @@ TGeoVolume* VDCylindricalLayer::createMetalStack() const 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); + TGeoShape* shape; + if (isFullCircle(mPhiSpanDeg)) { + shape = new TGeoTube(rIn, rOut, halfZ); + } else { + const double halfPhi = 0.5 * mPhiSpanDeg; + shape = new TGeoTubeSeg(rIn, rOut, halfZ, -halfPhi, +halfPhi); + } auto* vol = new TGeoVolume(name.c_str(), shape, medSi); vol->SetLineColor(kGray); vol->SetTransparency(30); @@ -244,9 +266,14 @@ TGeoVolume* VDDiskLayer::createMetalStack() const GeometryTGeo::getTRKMetalStackPattern(), mLayerNumber); const double halfThickness = 0.5 * metalT; - const double halfPhi = 0.5 * mPhiSpanDeg; - auto* shape = new TGeoTubeSeg(mRMin, mRMax, halfThickness, -halfPhi, +halfPhi); + TGeoShape* shape; + if (isFullCircle(mPhiSpanDeg)) { + shape = new TGeoTube(mRMin, mRMax, halfThickness); + } else { + const double halfPhi = 0.5 * mPhiSpanDeg; + shape = new TGeoTubeSeg(mRMin, mRMax, halfThickness, -halfPhi, +halfPhi); + } auto* vol = new TGeoVolume(name.c_str(), shape, medSi); vol->SetLineColor(kGray); vol->SetTransparency(30); @@ -275,9 +302,14 @@ TGeoVolume* VDCylindricalLayer::createChip() const 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); + TGeoShape* chipShape; + if (isFullCircle(mPhiSpanDeg)) { + chipShape = new TGeoTube(rIn, rOut, halfZ); + } else { + const double halfPhi = 0.5 * mPhiSpanDeg; + chipShape = new TGeoTubeSeg(rIn, rOut, halfZ, -halfPhi, +halfPhi); + } auto* chipVol = new TGeoVolume(chipName.c_str(), chipShape, medSi); // sensor @@ -361,9 +393,14 @@ TGeoVolume* VDDiskLayer::createChip() const GeometryTGeo::getTRKChipPattern(), mLayerNumber); const double halfThickness = 0.5 * mChipThickness; - const double halfPhi = 0.5 * mPhiSpanDeg; - auto* chipShape = new TGeoTubeSeg(mRMin, mRMax, halfThickness, -halfPhi, +halfPhi); + TGeoShape* chipShape; + if (isFullCircle(mPhiSpanDeg)) { + chipShape = new TGeoTube(mRMin, mRMax, halfThickness); + } else { + const double halfPhi = 0.5 * mPhiSpanDeg; + chipShape = new TGeoTubeSeg(mRMin, mRMax, halfThickness, -halfPhi, +halfPhi); + } auto* chipVol = new TGeoVolume(chipName.c_str(), chipShape, medSi); chipVol->SetLineColor(kYellow); chipVol->SetTransparency(30); @@ -417,9 +454,14 @@ void VDCylindricalLayer::createLayer(TGeoVolume* motherVolume, TGeoMatrix* combi 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); + TGeoShape* layerShape; + if (isFullCircle(mPhiSpanDeg)) { + layerShape = new TGeoTube(rIn, rOut, halfZ); + } else { + const double halfPhi = 0.5 * mPhiSpanDeg; // degrees + layerShape = new TGeoTubeSeg(rIn, rOut, halfZ, -halfPhi, +halfPhi); + } auto* layerVol = new TGeoVolume(mLayerName.c_str(), layerShape, medAir); layerVol->SetLineColor(kYellow); layerVol->SetTransparency(30); @@ -523,10 +565,15 @@ void VDDiskLayer::createLayer(TGeoVolume* motherVolume, TGeoMatrix* combiTrans) // 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); + TGeoShape* layerShape; + if (isFullCircle(mPhiSpanDeg)) { + layerShape = new TGeoTube(mRMin, mRMax, halfThickness); + } else { + const double halfPhi = 0.5 * mPhiSpanDeg; + layerShape = new TGeoTubeSeg(mRMin, mRMax, halfThickness, -halfPhi, +halfPhi); + } auto* layerVol = new TGeoVolume(mLayerName.c_str(), layerShape, medAir); layerVol->SetLineColor(kYellow); layerVol->SetTransparency(30); From 1913a00141d0d4b2eb1dfb594bd02a05c4c1338c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Wed, 4 Mar 2026 16:20:01 +0100 Subject: [PATCH 324/701] [ALICE3] Add possibility to set the chip thickness (#15120) --- .../base/include/IOTOFBase/IOTOFBaseParam.h | 15 +++++------ .../include/IOTOFSimulation/Detector.h | 2 +- .../ALICE3/IOTOF/simulation/src/Detector.cxx | 25 +++++++++++-------- .../ALICE3/IOTOF/simulation/src/Layer.cxx | 2 +- 4 files changed, 24 insertions(+), 20 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h index b74fc6d6869dd..91d005415891d 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h @@ -21,13 +21,14 @@ namespace iotof { struct IOTOFBaseParam : public o2::conf::ConfigurableParamHelper { - bool enableInnerTOF = true; - 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 + bool enableInnerTOF = true; // Enable Inner TOF layer + bool enableOuterTOF = true; // Enable Outer TOF layer + bool enableForwardTOF = true; // Enable Forward TOF layer + bool enableBackwardTOF = true; // Enable Backward TOF layer + std::string detectorPattern = ""; // Layouts of the detector + bool segmentedInnerTOF = false; // If the inner TOF layer is segmented + bool segmentedOuterTOF = false; // If the outer TOF layer is segmented + float x2x0 = 0.02f; // thickness expressed in radiation length, for all layers for the moment 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 f3c4e3ddd6276..acf754e1b1fa8 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, std::string pattern = "", bool itofSegmented = false, bool otofSegmented = false); + void configLayers(bool itof = true, bool otof = true, bool ftof = true, bool btof = true, std::string pattern = "", bool itofSegmented = false, bool otofSegmented = false, const float x2x0 = 0.02f); void configServices(); void createMaterials(); diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx index c056df5fd34ca..d4e34c582bbed 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx @@ -41,7 +41,7 @@ Detector::Detector(bool active) configLayers(iotofPars.enableInnerTOF, iotofPars.enableOuterTOF, iotofPars.enableForwardTOF, iotofPars.enableBackwardTOF, iotofPars.detectorPattern, - iotofPars.segmentedInnerTOF, iotofPars.segmentedOuterTOF); + iotofPars.segmentedInnerTOF, iotofPars.segmentedOuterTOF, iotofPars.x2x0); } Detector::~Detector() @@ -57,12 +57,13 @@ void Detector::ConstructGeometry() createGeometry(); } -void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::string pattern, bool itofSegmented, bool otofSegmented) +void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::string pattern, bool itofSegmented, bool otofSegmented, + const float x2x0) { - float radiusInnerTof = 19.f; - float radiusOuterTof = 85.f; - float lengthInnerTof = 124.f; + const float radiusInnerTof = 19.f; + const float radiusOuterTof = 85.f; + const float lengthInnerTof = 124.f; float lengthOuterTof = 680.f; std::pair radiusRangeDiskTof = {15.f, 100.f}; float zForwardTof = 370.f; @@ -97,23 +98,25 @@ void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::str } if (itof) { // iTOF mITOFLayer = itofSegmented ? ITOFLayer(std::string{GeometryTGeo::getITOFLayerPattern()}, - radiusInnerTof, 0.f, lengthInnerTof, 0.f, 0.02f, ITOFLayer::kBarrelSegmented, + radiusInnerTof, 0.f, lengthInnerTof, 0.f, x2x0, ITOFLayer::kBarrelSegmented, 24, 5.42, 10.0, 10) : ITOFLayer(std::string{GeometryTGeo::getITOFLayerPattern()}, - radiusInnerTof, 0.f, lengthInnerTof, 0.f, 0.02f, ITOFLayer::kBarrel); + radiusInnerTof, 0.f, lengthInnerTof, 0.f, x2x0, ITOFLayer::kBarrel); } if (otof) { // oTOF mOTOFLayer = otofSegmented ? OTOFLayer(std::string{GeometryTGeo::getOTOFLayerPattern()}, - radiusOuterTof, 0.f, lengthOuterTof, 0.f, 0.02f, OTOFLayer::kBarrelSegmented, + radiusOuterTof, 0.f, lengthOuterTof, 0.f, x2x0, OTOFLayer::kBarrelSegmented, 62, 9.74, 5.0, 54) : OTOFLayer(std::string{GeometryTGeo::getOTOFLayerPattern()}, - radiusOuterTof, 0.f, lengthOuterTof, 0.f, 0.02f, OTOFLayer::kBarrel); + radiusOuterTof, 0.f, lengthOuterTof, 0.f, x2x0, OTOFLayer::kBarrel); } if (ftof) { - mFTOFLayer = FTOFLayer(std::string{GeometryTGeo::getFTOFLayerPattern()}, radiusRangeDiskTof.first, radiusRangeDiskTof.second, 0.f, zForwardTof, 0.02f, FTOFLayer::kDisk); // fTOF + mFTOFLayer = FTOFLayer(std::string{GeometryTGeo::getFTOFLayerPattern()}, + radiusRangeDiskTof.first, radiusRangeDiskTof.second, 0.f, zForwardTof, x2x0, FTOFLayer::kDisk); // fTOF } if (btof) { - mBTOFLayer = BTOFLayer(std::string{GeometryTGeo::getBTOFLayerPattern()}, radiusRangeDiskTof.first, radiusRangeDiskTof.second, 0.f, -zForwardTof, 0.02f, BTOFLayer::kDisk); // bTOF + mBTOFLayer = BTOFLayer(std::string{GeometryTGeo::getBTOFLayerPattern()}, + radiusRangeDiskTof.first, radiusRangeDiskTof.second, 0.f, -zForwardTof, x2x0, BTOFLayer::kDisk); // bTOF } } diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx index 32a24fc46f94c..1744e4c4510bb 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx @@ -40,7 +40,7 @@ Layer::Layer(std::string layerName, float rInn, float rOut, float zLength, float mModulesPerStave(modulesPerStave), mTiltAngle(staveTiltAngle) { - float Si_X0 = 9.5f; + const float Si_X0 = 9.5f; mChipThickness = mX2X0 * Si_X0; std::string name = ""; switch (layout) { From d890d412ba9987c024216320be1b444ab4e3210e Mon Sep 17 00:00:00 2001 From: Francesco Noferini Date: Mon, 23 Feb 2026 14:12:04 +0100 Subject: [PATCH 325/701] add treatment of TOF DRM Errors --- .../TOF/include/DataFormatsTOF/Diagnostic.h | 7 + .../TOF/base/include/TOFBase/CalibTOFapi.h | 63 +++++++-- Detectors/TOF/base/include/TOFBase/Digit.h | 14 +- Detectors/TOF/base/src/CalibTOFapi.cxx | 131 +++++++++++++++++- Detectors/TOF/prototyping/CMakeLists.txt | 15 ++ Detectors/TOF/prototyping/checkDRMobj_tof.C | 41 ++++++ Detectors/TOF/prototyping/makeDRMobj_tof.C | 45 ++++++ .../TOF/reconstruction/src/Clusterer.cxx | 4 +- .../include/TOFSimulation/Digitizer.h | 5 +- Detectors/TOF/simulation/src/Detector.cxx | 1 - Detectors/TOF/simulation/src/Digitizer.cxx | 13 +- .../src/SimpleDigitizerWorkflow.cxx | 5 +- .../src/TOFDigitizerSpec.cxx | 25 +++- .../DigitizerWorkflow/src/TOFDigitizerSpec.h | 2 +- 14 files changed, 339 insertions(+), 32 deletions(-) create mode 100644 Detectors/TOF/prototyping/checkDRMobj_tof.C create mode 100644 Detectors/TOF/prototyping/makeDRMobj_tof.C diff --git a/DataFormats/Detectors/TOF/include/DataFormatsTOF/Diagnostic.h b/DataFormats/Detectors/TOF/include/DataFormatsTOF/Diagnostic.h index 8adcdb63e9d21..028da04b3ef70 100644 --- a/DataFormats/Detectors/TOF/include/DataFormatsTOF/Diagnostic.h +++ b/DataFormats/Detectors/TOF/include/DataFormatsTOF/Diagnostic.h @@ -44,6 +44,13 @@ class Diagnostic uint32_t fillEmptyTOF(uint32_t frequency = 1) { return fill(1, frequency); } static ULong64_t getEmptyCrateKey(int crate); static ULong64_t getNoisyChannelKey(int channel); + static ULong64_t getDRMKey(int crate) { return 1000000 + crate * 1000; } + static ULong64_t getDRMerrorKey(int crate, int error) { return getDRMKey(crate) + error; } + uint32_t getFrequencyDRM(int crate) const { return getFrequency(getDRMKey(crate)); } + uint32_t getFrequencyDRMerror(int crate, int error) const { return getFrequency(getDRMerrorKey(crate, error)); } + uint32_t fillDRM(int crate, uint32_t frequency) { return fill(getDRMKey(crate), frequency); } + uint32_t fillDRMerror(int crate, int error, uint32_t frequency) { return fill(getDRMerrorKey(crate, error), frequency); } + static ULong64_t getTRMKey(int crate, int trm); void print(bool longFormat = false) const; void clear() { mVector.clear(); } diff --git a/Detectors/TOF/base/include/TOFBase/CalibTOFapi.h b/Detectors/TOF/base/include/TOFBase/CalibTOFapi.h index c3d39d3e978e1..6fb87e72abf62 100644 --- a/Detectors/TOF/base/include/TOFBase/CalibTOFapi.h +++ b/Detectors/TOF/base/include/TOFBase/CalibTOFapi.h @@ -23,6 +23,9 @@ #include "TOFBase/Geo.h" #include "DataFormatsTOF/Diagnostic.h" #include "DataFormatsTOF/TOFFEElightInfo.h" +#include "DataFormatsTOF/CompressedDataFormat.h" + +class TH2F; namespace o2 { @@ -38,10 +41,12 @@ class CalibTOFapi using CcdbApi = o2::ccdb::CcdbApi; public: + static o2::tof::Diagnostic doDRMerrCalibFromQCHisto(const TH2F* histo, const char* file_output_name); + void resetDia(); CalibTOFapi() = default; CalibTOFapi(const std::string url); - CalibTOFapi(long timestamp, o2::dataformats::CalibLHCphaseTOF* phase, o2::dataformats::CalibTimeSlewingParamTOF* slew, Diagnostic* dia = nullptr) : mTimeStamp(timestamp), mLHCphase(phase), mSlewParam(slew), mDiaFreq(dia) {} + CalibTOFapi(long timestamp, o2::dataformats::CalibLHCphaseTOF* phase, o2::dataformats::CalibTimeSlewingParamTOF* slew, Diagnostic* dia = nullptr, Diagnostic* diaDRM = nullptr) : mTimeStamp(timestamp), mLHCphase(phase), mSlewParam(slew), mDiaFreq(dia), mDiaDRMFreq(diaDRM) {} ~CalibTOFapi() { if (mLHCphase) { @@ -53,6 +58,9 @@ class CalibTOFapi if (mDiaFreq) { // delete mDiaFreq; } + if (mDiaDRMFreq) { + // delete mDiaDRMFreq; + } } void setTimeStamp(long t) @@ -69,6 +77,8 @@ class CalibTOFapi void readTimeSlewingParamFromFile(const char* filename); void readDiagnosticFrequencies(); void loadDiagnosticFrequencies(); + void readDiagnosticDRMFrequencies(); + void loadDiagnosticDRMFrequencies(); void readActiveMap(); void loadActiveMap(TOFFEElightInfo* fee); void writeLHCphase(LhcPhase* phase, std::map metadataLHCphase, uint64_t minTimeSTamp, uint64_t maxTimeStamp); @@ -89,6 +99,8 @@ class CalibTOFapi void setLhcPhase(LhcPhase* obj) { mLHCphase = obj; } Diagnostic* getDiagnostic() { return mDiaFreq; } void setDiagnostic(Diagnostic* obj) { mDiaFreq = obj; } + Diagnostic* getDiagnosticDRM() { return mDiaDRMFreq; } + void setDiagnosticDRM(Diagnostic* obj) { mDiaDRMFreq = obj; } int getNoisyThreshold() const { return mNoisyThreshold; } void setNoisyThreshold(int val) { mNoisyThreshold = val; } @@ -102,12 +114,39 @@ class CalibTOFapi void processError(int crate, int trm, int mask); bool isChannelError(int channel) const; bool checkTRMPolicy(int mask) const; + void resetDRMErrors(); + void processErrorDRM(int crate, int codeErr); + bool isChannelDRMError(int channel) const; + bool checkDRMPolicy(int mask) const; + + void setDRMCriticalErrorMask(uint32_t val) { mDRMCriticalErrorMask = val; } + uint32_t getDRMCriticalErrorMask() const { return mDRMCriticalErrorMask; } + float getDRMprobError(int crate, int type) const { return mErrorInDRM[crate][type]; } + + // DRM error codes inherited by EDRMDiagnostic_t in CompressedDataFormat.h (shifted by 4 bits) + static const int DRM_ERRINDEX_SHIFT = 4; + enum DRMcodes { + DRM_HEADER_MISSING = o2::tof::diagnostic::DRM_HEADER_MISSING >> DRM_ERRINDEX_SHIFT, + DRM_TRAILER_MISSING = o2::tof::diagnostic::DRM_TRAILER_MISSING >> DRM_ERRINDEX_SHIFT, + DRM_FEEID_MISMATCH = o2::tof::diagnostic::DRM_FEEID_MISMATCH >> DRM_ERRINDEX_SHIFT, + DRM_ORBIT_MISMATCH = o2::tof::diagnostic::DRM_ORBIT_MISMATCH >> DRM_ERRINDEX_SHIFT, + DRM_CRC_MISMATCH = o2::tof::diagnostic::DRM_CRC_MISMATCH >> DRM_ERRINDEX_SHIFT, + DRM_ENAPARTMASK_DIFFER = o2::tof::diagnostic::DRM_ENAPARTMASK_DIFFER >> DRM_ERRINDEX_SHIFT, + DRM_CLOCKSTATUS_WRONG = o2::tof::diagnostic::DRM_CLOCKSTATUS_WRONG >> DRM_ERRINDEX_SHIFT, + DRM_FAULTSLOTMASK_NOTZERO = o2::tof::diagnostic::DRM_FAULTSLOTMASK_NOTZERO >> DRM_ERRINDEX_SHIFT, + DRM_READOUTTIMEOUT_NOTZERO = o2::tof::diagnostic::DRM_READOUTTIMEOUT_NOTZERO >> DRM_ERRINDEX_SHIFT, + DRM_EVENTWORDS_MISMATCH = o2::tof::diagnostic::DRM_EVENTWORDS_MISMATCH >> DRM_ERRINDEX_SHIFT, + DRM_DIAGNOSTIC_SPARE1 = o2::tof::diagnostic::DRM_DIAGNOSTIC_SPARE1 >> DRM_ERRINDEX_SHIFT, + DRM_DECODE_ERROR = o2::tof::diagnostic::DRM_DECODE_ERROR >> DRM_ERRINDEX_SHIFT, + N_DRM_ERRORS = 12 + }; private: - long mTimeStamp; ///< timeStamp for queries - LhcPhase* mLHCphase = nullptr; ///< object for LHC phase - SlewParam* mSlewParam = nullptr; ///< object for timeslewing (containing info also for offset and problematic) - Diagnostic* mDiaFreq = nullptr; ///< object for Diagnostic Frequency + long mTimeStamp; ///< timeStamp for queries + LhcPhase* mLHCphase = nullptr; ///< object for LHC phase + SlewParam* mSlewParam = nullptr; ///< object for timeslewing (containing info also for offset and problematic) + Diagnostic* mDiaFreq = nullptr; ///< object for Diagnostic Frequency + Diagnostic* mDiaDRMFreq = nullptr; ///< object for Diagnostic Frequency // info from diagnostic int mNoisyThreshold = 1; ///< threshold to be noisy @@ -116,13 +155,17 @@ class CalibTOFapi std::vector> mNoisy; ///< probTRMerror std::vector> mTRMerrorProb; ///< probTRMerror std::vector mTRMmask; ///< mask error for TRM + float mErrorInDRM[Geo::kNCrate][N_DRM_ERRORS] = {}; ///< probability of DRM error + uint32_t mDRMCriticalErrorMask = 0; ///< bit mask for critical DRM errors (e.g. Orbit Mismatch -> 1 << 7, see DataFormats/Detectors/TOF/include/DataFormatsTOF/CompressedDataFormat.h) - bool mIsErrorCh[Geo::NCHANNELS] = {}; ///< channels in error (TRM) - std::vector mFillErrChannel; ///< last error channels filled - bool mIsOffCh[Geo::NCHANNELS] = {}; ///< channels in error (TRM) - bool mIsNoisy[Geo::NCHANNELS] = {}; ///< noisy channels + bool mIsErrorCh[Geo::NCHANNELS] = {}; ///< channels in error (TRM) + std::vector mFillErrChannel; ///< last error channels filled + bool mIsOffCh[Geo::NCHANNELS] = {}; ///< channels in error (TRM) + bool mIsNoisy[Geo::NCHANNELS] = {}; ///< noisy channels + bool mIsErrorDRMCh[Geo::NCHANNELS] = {}; ///< channels in error (DRM) + std::vector mFillErrDRMChannel; ///< last error channels filled - ClassDefNV(CalibTOFapi, 1); + ClassDefNV(CalibTOFapi, 2); }; } // namespace tof } // namespace o2 diff --git a/Detectors/TOF/base/include/TOFBase/Digit.h b/Detectors/TOF/base/include/TOFBase/Digit.h index eef03ef84b97c..afa5662044a3e 100644 --- a/Detectors/TOF/base/include/TOFBase/Digit.h +++ b/Detectors/TOF/base/include/TOFBase/Digit.h @@ -101,13 +101,13 @@ class Digit private: friend class boost::serialization::access; - Int_t mChannel; ///< TOF channel index - uint16_t mTDC; ///< TDC bin number - uint16_t mTOT; ///< TOT bin number - InteractionRecord mIR{0, 0}; ///< InteractionRecord (orbit and bc) when digit occurs - Int_t mLabel; ///< Index of the corresponding entry in the MC label array - Double_t mCalibratedTime; //!< time of the digits after calibration (not persistent; it will be filled during clusterization) - Int_t mElectronIndex; //!/< index in electronic format + Int_t mChannel; ///< TOF channel index + uint16_t mTDC; ///< TDC bin number + uint16_t mTOT; ///< TOT bin number + InteractionRecord mIR{0, 0}; ///< InteractionRecord (orbit and bc) when digit occurs + Int_t mLabel; ///< Index of the corresponding entry in the MC label array + Double_t mCalibratedTime; //!< time of the digits after calibration (not persistent; it will be filled during clusterization) + Int_t mElectronIndex; //!/< index in electronic format uint32_t mTriggerOrbit = 0; //!< orbit id of trigger event // RS: orbit must be 32bits long uint16_t mTriggerBunch = 0; //!< bunch id of trigger event Bool_t mIsUsedInCluster; //!/< flag to declare that the digit was used to build a cluster diff --git a/Detectors/TOF/base/src/CalibTOFapi.cxx b/Detectors/TOF/base/src/CalibTOFapi.cxx index 281498990a9dd..fdc028bde536c 100644 --- a/Detectors/TOF/base/src/CalibTOFapi.cxx +++ b/Detectors/TOF/base/src/CalibTOFapi.cxx @@ -11,11 +11,40 @@ #include "TOFBase/CalibTOFapi.h" #include // for LOG +#include using namespace o2::tof; ClassImp(o2::tof::CalibTOFapi); +o2::tof::Diagnostic CalibTOFapi::doDRMerrCalibFromQCHisto(const TH2F* histo, const char* file_output_name) +{ + // this is a method which translate the QC output in qc/TOF/MO/TaskRaw/DRMCounter (TH2F) into a Diagnotic object for DRM (patter(crate, error), frequency) + // note that, differently from TRM errors, DRM ones are not stored in CTF by design (since very rare, as expected). Such an info is available only at the level of raw sync QC + o2::tof::Diagnostic drmDia; + + for (int j = 1; j <= Geo::kNCrate; j++) { + drmDia.fillDRM(j - 1, histo->GetBinContent(1, j)); + for (int i = 2; i <= histo->GetXaxis()->GetNbins(); i++) { + if (histo->GetBinContent(1, j)) { + if (histo->GetBinContent(i, j) > 0) { + drmDia.fillDRMerror(j - 1, i - 1, histo->GetBinContent(i, j)); + } + } + } + } + + TFile* fo = new TFile(file_output_name, "RECREATE"); + fo->WriteObjectAny(&drmDia, drmDia.Class_Name(), "ccdb_object"); + fo->Close(); + LOG(debug) << "DRM error ccdb object created in " << file_output_name << " with this content"; + drmDia.print(true); + + return drmDia; +} + +//______________________________________________________________________ + void CalibTOFapi::resetDia() { memset(mEmptyCrateProb, 0., Geo::kNCrate * 4); @@ -38,7 +67,7 @@ void CalibTOFapi::readActiveMap() { auto& mgr = CcdbManager::instance(); long timems = long(mTimeStamp) * 1000; - LOG(info) << "TOF get active map with timestamp (ms) = " << timems; + LOG(debug) << "TOF get active map with timestamp (ms) = " << timems; auto fee = mgr.getForTimeStamp("TOF/Calib/FEELIGHT", timems); loadActiveMap(fee); } @@ -116,11 +145,23 @@ void CalibTOFapi::readDiagnosticFrequencies() { auto& mgr = CcdbManager::instance(); long timems = long(mTimeStamp) * 1000; - LOG(info) << "TOF get Diagnostics with timestamp (ms) = " << timems; + LOG(info) << "TOF get TRM Diagnostics with timestamp (ms) = " << timems; mDiaFreq = mgr.getForTimeStamp("TOF/Calib/Diagnostic", timems); loadDiagnosticFrequencies(); } + +//______________________________________________________________________ + +void CalibTOFapi::readDiagnosticDRMFrequencies() +{ + auto& mgr = CcdbManager::instance(); + long timems = long(mTimeStamp) * 1000; + LOG(info) << "TOF get DRM Diagnostics with timestamp (ms) = " << timems; + mDiaFreq = mgr.getForTimeStamp("TOF/Calib/TRMerrors", timems); + + loadDiagnosticDRMFrequencies(); +} //______________________________________________________________________ void CalibTOFapi::loadDiagnosticFrequencies() @@ -210,6 +251,37 @@ void CalibTOFapi::loadDiagnosticFrequencies() //______________________________________________________________________ +void CalibTOFapi::loadDiagnosticDRMFrequencies() +{ + mDiaDRMFreq->print(); + + for (int ic = 0; ic < Geo::kNCrate; ic++) { // loop over crates + float DRMcounters = mDiaDRMFreq->getFrequencyDRM(ic); + + if (DRMcounters < 1) { + for (int ie = 0; ie < N_DRM_ERRORS; ie++) { + mErrorInDRM[ic][ie] = 0.; + } + continue; + } + + for (int ie = 0; ie < N_DRM_ERRORS; ie++) { // loop over error types + int ie_shifted = ie + DRM_ERRINDEX_SHIFT; + + float frequency = mDiaDRMFreq->getFrequencyDRMerror(ic, ie_shifted) * 1. / DRMcounters; // error frequency + if (frequency > 1) { + frequency = 1.; + } + if (frequency > 1E-6) { + LOG(debug) << "DRMmap: Crate = " << ic << " - error = " << ie << " - frequency = " << frequency; + } + mErrorInDRM[ic][ie] = frequency; + } + } +} + +//______________________________________________________________________ + void CalibTOFapi::writeLHCphase(LhcPhase* phase, std::map metadataLHCphase, uint64_t minTimeStamp, uint64_t maxTimeStamp) { @@ -330,6 +402,17 @@ void CalibTOFapi::resetTRMErrors() //______________________________________________________________________ +void CalibTOFapi::resetDRMErrors() +{ + for (auto index : mFillErrDRMChannel) { + mIsErrorDRMCh[index] = false; + } + + mFillErrDRMChannel.clear(); +} + +//______________________________________________________________________ + void CalibTOFapi::processError(int crate, int trm, int mask) { if (checkTRMPolicy(mask)) { // check the policy of TRM -> true=good TRM @@ -348,6 +431,32 @@ void CalibTOFapi::processError(int crate, int trm, int mask) //______________________________________________________________________ +void CalibTOFapi::processErrorDRM(int crate, int codeErr) +{ + int mask = 1 << codeErr; + + if (checkDRMPolicy(mask)) { + return; + } + + LOG(debug) << "DRMmask: crate = " << crate << " - mask = " << mask << " - critical mask = " << mDRMCriticalErrorMask; + + for (int trm = 3; trm < 13; trm++) { + int ech = (crate << 12) + ((trm - 3) << 8); + for (int i = ech; i < ech + 256; i++) { + int channel = Geo::getCHFromECH(i); + if (channel == -1 || mIsErrorDRMCh[channel] == true) { + continue; + } + + mIsErrorDRMCh[channel] = true; + mFillErrDRMChannel.push_back(channel); + } + } +} + +//______________________________________________________________________ + bool CalibTOFapi::checkTRMPolicy(int mask) const { return false; @@ -355,7 +464,25 @@ bool CalibTOFapi::checkTRMPolicy(int mask) const //______________________________________________________________________ +bool CalibTOFapi::checkDRMPolicy(int mask) const +{ + return !(mDRMCriticalErrorMask & mask); +} + +//______________________________________________________________________ + bool CalibTOFapi::isChannelError(int channel) const { return mIsErrorCh[channel]; } + +//______________________________________________________________________ + +bool CalibTOFapi::isChannelDRMError(int channel) const +{ + if (mIsErrorDRMCh[channel]) { + int detId[5]; + o2::tof::Geo::getVolumeIndices(channel, detId); + } + return mIsErrorDRMCh[channel]; +} diff --git a/Detectors/TOF/prototyping/CMakeLists.txt b/Detectors/TOF/prototyping/CMakeLists.txt index 1ce2268f1358a..7dfc9f8cb7361 100644 --- a/Detectors/TOF/prototyping/CMakeLists.txt +++ b/Detectors/TOF/prototyping/CMakeLists.txt @@ -32,6 +32,16 @@ o2_add_test_root_macro(findLabels.C O2::TOFBase LABELS tof) +o2_add_test_root_macro(makeDRMobj_tof.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsTOF + O2::TOFBase + LABELS tof) + +o2_add_test_root_macro(checkDRMobj_tof.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsTOF + O2::TOFBase + LABELS tof) + o2_add_test_root_macro(findTOFclusterFromLabel.C PUBLIC_LINK_LIBRARIES O2::DataFormatsTOF O2::SimulationDataFormat @@ -59,3 +69,8 @@ o2_add_test_root_macro(macroEvTime.C PUBLIC_LINK_LIBRARIES O2::TOFBase O2::TOFReconstruction LABELS tof) + +install( + FILES makeDRMobj_tof.C + checkDRMobj_tof.C + DESTINATION share/macro/) diff --git a/Detectors/TOF/prototyping/checkDRMobj_tof.C b/Detectors/TOF/prototyping/checkDRMobj_tof.C new file mode 100644 index 0000000000000..9652a4fb9823e --- /dev/null +++ b/Detectors/TOF/prototyping/checkDRMobj_tof.C @@ -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. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "TFile.h" +#include "TH2F.h" +#include "TOFBase/CalibTOFapi.h" +#endif + +void checkDRMobj_tof(const char* fname = "ccdb.root") +{ + TFile* f = new TFile(fname); + + TH2F* hErrors = new TH2F("hDRMerrors", ";error code; frequency", 30, 0, 30, 72, 0, 72); + + o2::tof::Diagnostic* drmDia = (o2::tof::Diagnostic*)f->Get("ccdb_object"); + + for (int j = 1; j <= 72; j++) { + uint32_t patternRDH = o2::tof::Diagnostic::getDRMKey(j - 1); + for (int i = 1; i <= hErrors->GetXaxis()->GetNbins(); i++) { + uint32_t pattern = o2::tof::Diagnostic::getDRMerrorKey(j - 1, i - 1); + if (drmDia->getFrequency(patternRDH)) { + hErrors->SetBinContent(i, j, drmDia->getFrequency(pattern) * 1. / drmDia->getFrequency(patternRDH)); + } + } + } + + TCanvas* c = new TCanvas(); + c->cd(1); + hErrors->Draw("colz"); + + drmDia->print(true); +} diff --git a/Detectors/TOF/prototyping/makeDRMobj_tof.C b/Detectors/TOF/prototyping/makeDRMobj_tof.C new file mode 100644 index 0000000000000..2ae79d501b369 --- /dev/null +++ b/Detectors/TOF/prototyping/makeDRMobj_tof.C @@ -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. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "TFile.h" +#include "TH2F.h" +#include "TOFBase/CalibTOFapi.h" +#endif + +void makeDRMobj_tof(const char* inputfile = "TObject_1764607157510.root", bool dummy = false) +{ + if (dummy) { + o2::tof::Diagnostic drmDia; + for (int j = 1; j <= 72; j++) { + drmDia.fill(o2::tof::Diagnostic::getDRMKey(j - 1)); + } + + TFile* fo = new TFile("ccdb.root", "RECREATE"); + fo->WriteObjectAny(&drmDia, drmDia.Class_Name(), "ccdb_object"); + fo->Close(); + + return; + } + + TFile* f = new TFile(inputfile); + TH2F* h = (TH2F*)f->Get("ccdb_object"); + + float maxVal = h->GetBinContent(h->GetMaximumBin()); + + if (maxVal > 1E6) { // to avoid to pass value causing overflow + h->Scale(1E6 / maxVal); + } + + o2::tof::Diagnostic drmDia; + + drmDia = o2::tof::CalibTOFapi::doDRMerrCalibFromQCHisto(h, "ccdb.root"); +} diff --git a/Detectors/TOF/reconstruction/src/Clusterer.cxx b/Detectors/TOF/reconstruction/src/Clusterer.cxx index 0b393bfd45e78..dbfa472ce4112 100644 --- a/Detectors/TOF/reconstruction/src/Clusterer.cxx +++ b/Detectors/TOF/reconstruction/src/Clusterer.cxx @@ -54,11 +54,9 @@ void Clusterer::calibrateStrip() // LOG(info) << "channel = " << dig->getChannel(); dig->setBC(dig->getBC() - mBCOffset); // RS Don't use raw BC, always start from the beginning of the TF double calib = mCalibApi->getTimeCalibration(dig->getChannel(), dig->getTOT() * Geo::TOTBIN_NS); - //printf("channel %d) isProblematic = %d, fractionUnderPeak = %f\n",dig->getChannel(),mCalibApi->isProblematic(dig->getChannel()),mCalibApi->getFractionUnderPeak(dig->getChannel())); // toberem bool isProbOrError = mAreCalibStored ? mCalibApi->isChannelError(dig->getChannel()) || mCalibApi->isNoisy(dig->getChannel()) : mCalibApi->isChannelError(dig->getChannel()) || mCalibApi->isNoisy(dig->getChannel()) || mCalibApi->isProblematic(dig->getChannel()); dig->setIsProblematic(isProbOrError); - dig->setCalibratedTime(dig->getTDC() * Geo::TDCBIN + dig->getBC() * o2::constants::lhc::LHCBunchSpacingNS * 1E3 - Geo::LATENCYWINDOW * 1E3 - calib); //TODO: to be checked that "-" is correct, and we did not need "+" instead :-) - //printf("calibration correction = %f\n",calib); // toberem + dig->setCalibratedTime(dig->getTDC() * Geo::TDCBIN + dig->getBC() * o2::constants::lhc::LHCBunchSpacingNS * 1E3 - Geo::LATENCYWINDOW * 1E3 - calib); // TODO: to be checked that "-" is correct, and we did not need "+" instead :-) } } diff --git a/Detectors/TOF/simulation/include/TOFSimulation/Digitizer.h b/Detectors/TOF/simulation/include/TOFSimulation/Digitizer.h index 5153f168f176f..4e369cecf6e26 100644 --- a/Detectors/TOF/simulation/include/TOFSimulation/Digitizer.h +++ b/Detectors/TOF/simulation/include/TOFSimulation/Digitizer.h @@ -148,7 +148,10 @@ class Digitizer : public WindowFiller float mTotLastHit[10]; Int_t mXLastShift[10]; Int_t mZLastShift[10]; - ClassDefNV(Digitizer, 1); + + float mIsCrateRDHerr[Geo::kNCrate]; + + ClassDefNV(Digitizer, 2); }; } // namespace tof } // namespace o2 diff --git a/Detectors/TOF/simulation/src/Detector.cxx b/Detectors/TOF/simulation/src/Detector.cxx index 2bfb76613dae5..97d5e03851291 100644 --- a/Detectors/TOF/simulation/src/Detector.cxx +++ b/Detectors/TOF/simulation/src/Detector.cxx @@ -637,7 +637,6 @@ void Detector::makeStripsInModules(Float_t ytof, Float_t zlenA) const // Define MRPC strip volume, called FSTR // Insert FSTR volume in FLTA/B/C volumes // - // ciao Float_t yFLT = ytof * 0.5 - Geo::MODULEWALLTHICKNESS; ///////////////// Detector itself ////////////////////// diff --git a/Detectors/TOF/simulation/src/Digitizer.cxx b/Detectors/TOF/simulation/src/Digitizer.cxx index ec899bd35fbff..e2c4fdcca9abe 100644 --- a/Detectors/TOF/simulation/src/Digitizer.cxx +++ b/Detectors/TOF/simulation/src/Digitizer.cxx @@ -95,7 +95,7 @@ int Digitizer::process(const std::vector* hits, std::vector* dig const double max_hit_time = TOFSimParams::Instance().max_hit_time; // hits array of TOF hits for a given simulated event - // digits passed from external to be filled, in continuous readout mode we will push it on mDigitsPerTimeFrame vector of vectors of digits + // digits passed from external to be filled, in continuous readout mode we will push it on mDigitsPerTimeFrame, final vector of digits // printf("process event time = %f with %ld hits\n",mEventTime.getTimeNS(),hits->size()); @@ -891,6 +891,7 @@ void Digitizer::fillOutputContainer(std::vector& digits) // fill diagnostics mCalibApi->resetTRMErrors(); + mCalibApi->resetDRMErrors(); float p = gRandom->Rndm(); if (mCalibApi->getEmptyTOFProb() > p) { // check empty TOF for (int i = 0; i < Geo::kNCrate; i++) { @@ -906,6 +907,14 @@ void Digitizer::fillOutputContainer(std::vector& digits) info.setEmptyCrate(i); isEmptyCrate[i] = true; } else { // check if filling diagnostic (noisy will be masked in clusterization, then skip here) + // Fill DRM RDH errors + for (int ie = 0; ie < mCalibApi->N_DRM_ERRORS; ie++) { + p = gRandom->Rndm(); + if (mCalibApi->getDRMprobError(i, ie) > p) { + mCalibApi->processErrorDRM(i, ie); + } + } + isEmptyCrate[i] = false; int slotreached = -1; const std::vector>& trmProg = mCalibApi->getTRMerrorProb(); @@ -955,7 +964,7 @@ void Digitizer::fillOutputContainer(std::vector& digits) for (auto [key, dig] : dmap) { int crate = Geo::getCrateFromECH(Geo::getECHFromCH(dig.getChannel())); - if (isEmptyCrate[crate] || mCalibApi->isChannelError(dig.getChannel())) { + if (isEmptyCrate[crate] || mCalibApi->isChannelError(dig.getChannel()) || mCalibApi->isChannelDRMError(dig.getChannel())) { // flag digits to be removed keyToBeRemoved.push_back(key); } diff --git a/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx b/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx index c45c746064101..6f956efe79304 100644 --- a/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx +++ b/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx @@ -53,6 +53,7 @@ // for TOF #include "TOFDigitizerSpec.h" #include "TOFWorkflowIO/TOFDigitWriterSpec.h" +#include "TOFBase/CalibTOFapi.h" // for FT0 #include "FT0DigitizerSpec.h" @@ -202,6 +203,7 @@ void customize(std::vector& workflowOptions) // option to use/not use CCDB for TOF workflowOptions.push_back(ConfigParamSpec{"use-ccdb-tof", o2::framework::VariantType::Bool, false, {"enable access to ccdb tof calibration objects"}}); workflowOptions.push_back(ConfigParamSpec{"ccdb-tof-sa", o2::framework::VariantType::Bool, false, {"enable access to ccdb tof calibration objects via CCDBManager (obsolete remap to use-ccdb-tof)"}}); + workflowOptions.push_back(ConfigParamSpec{"tof-drm-bitmask", o2::framework::VariantType::Int, (int)o2::tof::CalibTOFapi::DRM_ORBIT_MISMATCH, {"bit mask of DRM critical errors"}}); // option to use/not use CCDB for FT0 workflowOptions.push_back(ConfigParamSpec{"use-ccdb-ft0", o2::framework::VariantType::Bool, false, {"enable access to ccdb ft0 calibration objects"}}); @@ -677,10 +679,11 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) auto ccdb_url_tof = o2::base::NameConf::getCCDBServer(); auto timestamp = o2::raw::HBFUtils::Instance().startTime / 1000; detList.emplace_back(o2::detectors::DetID::TOF); + auto maskDRM = (uint32_t)configcontext.options().get("tof-drm-bitmask"); // connect the TOF digitization // printf("TOF Setting: use-ccdb = %d ---- ccdb url=%s ---- timestamp=%ld\n", useCCDB, ccdb_url_tof.c_str(), timestamp); - digitizerSpecs.emplace_back(o2::tof::getTOFDigitizerSpec(fanoutsize++, useCCDB, mctruth, ccdb_url_tof.c_str(), timestamp)); + digitizerSpecs.emplace_back(o2::tof::getTOFDigitizerSpec(fanoutsize++, useCCDB, mctruth, ccdb_url_tof.c_str(), timestamp, maskDRM)); // add TOF digit writer writerSpecs.emplace_back(o2::tof::getTOFDigitWriterSpec(mctruth)); } diff --git a/Steer/DigitizerWorkflow/src/TOFDigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/TOFDigitizerSpec.cxx index e512659686c86..2a7800985fc1f 100644 --- a/Steer/DigitizerWorkflow/src/TOFDigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/TOFDigitizerSpec.cxx @@ -48,7 +48,7 @@ namespace tof class TOFDPLDigitizerTask : public o2::base::BaseDPLDigitizer { public: - TOFDPLDigitizerTask(bool useCCDB, std::string ccdb_url, int timestamp) : mUseCCDB{useCCDB}, mCCDBurl(ccdb_url), mTimestamp(timestamp), o2::base::BaseDPLDigitizer(o2::base::InitServices::FIELD | o2::base::InitServices::GEOM), mPass(o2::conf::DigiParams::Instance().passName){}; + TOFDPLDigitizerTask(bool useCCDB, std::string ccdb_url, int timestamp, uint32_t mask) : mUseCCDB{useCCDB}, mCCDBurl(ccdb_url), mTimestamp(timestamp), o2::base::BaseDPLDigitizer(o2::base::InitServices::FIELD | o2::base::InitServices::GEOM), mPass(o2::conf::DigiParams::Instance().passName), mMaskDRM(mask){}; void initDigitizerTask(framework::InitContext& ic) override { @@ -77,6 +77,10 @@ class TOFDPLDigitizerTask : public o2::base::BaseDPLDigitizer mUpdateCCDB = true; return; } + if (matcher == ConcreteDataMatcher("TOF", "DiagnosticDRM", 0)) { + mUpdateCCDB = true; + return; + } if (matcher == ConcreteDataMatcher("TOF", "LHCphaseCal", 0)) { mUpdateCCDB = true; return; @@ -127,6 +131,7 @@ class TOFDPLDigitizerTask : public o2::base::BaseDPLDigitizer const auto lhcPhaseIn = pc.inputs().get("tofccdbLHCphase"); const auto channelCalibIn = pc.inputs().get("tofccdbChannelCalib"); const auto diagnosticIn = pc.inputs().get("tofccdbDia"); + const auto diagnosticDRM = pc.inputs().get("tofccdbDrm"); const auto statusIn = pc.inputs().get("tofccdbStatus"); const auto tofParams = pc.inputs().get("tofccdbParams"); @@ -165,11 +170,15 @@ class TOFDPLDigitizerTask : public o2::base::BaseDPLDigitizer o2::dataformats::CalibLHCphaseTOF* lhcPhase = new o2::dataformats::CalibLHCphaseTOF(std::move(*lhcPhaseIn)); o2::dataformats::CalibTimeSlewingParamTOF* channelCalib = new o2::dataformats::CalibTimeSlewingParamTOF(std::move(*channelCalibIn)); o2::tof::Diagnostic* diagnostic = new o2::tof::Diagnostic(std::move(*diagnosticIn)); + o2::tof::Diagnostic* diagnosticDRMerr = new o2::tof::Diagnostic(std::move(*diagnosticDRM)); o2::tof::TOFFEElightInfo* status = new o2::tof::TOFFEElightInfo(std::move(*statusIn)); - mCalibApi = new o2::tof::CalibTOFapi(long(0), lhcPhase, channelCalib, diagnostic); + mCalibApi = new o2::tof::CalibTOFapi(long(0), lhcPhase, channelCalib, diagnostic, diagnosticDRMerr); + mCalibApi->setDRMCriticalErrorMask(mMaskDRM); mCalibApi->loadDiagnosticFrequencies(); + mCalibApi->loadDiagnosticDRMFrequencies(); mCalibApi->loadActiveMap(status); + mUpdateCCDB = false; } else { // update if necessary if (mUpdateCCDB) { @@ -178,10 +187,14 @@ class TOFDPLDigitizerTask : public o2::base::BaseDPLDigitizer o2::dataformats::CalibLHCphaseTOF* lhcPhase = new o2::dataformats::CalibLHCphaseTOF(*lhcPhaseIn); o2::dataformats::CalibTimeSlewingParamTOF* channelCalib = new o2::dataformats::CalibTimeSlewingParamTOF(*channelCalibIn); o2::tof::Diagnostic* diagnostic = new o2::tof::Diagnostic(std::move(*diagnosticIn)); + o2::tof::Diagnostic* diagnosticDRMerr = new o2::tof::Diagnostic(std::move(*diagnosticDRM)); o2::tof::TOFFEElightInfo* status = new o2::tof::TOFFEElightInfo(std::move(*statusIn)); mCalibApi = new o2::tof::CalibTOFapi(long(0), lhcPhase, channelCalib, diagnostic); + mCalibApi->setDRMCriticalErrorMask(mMaskDRM); mCalibApi->loadDiagnosticFrequencies(); + mCalibApi->loadDiagnosticDRMFrequencies(); mCalibApi->loadActiveMap(status); + mUpdateCCDB = false; } else { // do nothing @@ -201,10 +214,12 @@ class TOFDPLDigitizerTask : public o2::base::BaseDPLDigitizer channelCalibDummy->setFractionUnderPeak(sector, channelInSector, 1); } mCalibApi = new o2::tof::CalibTOFapi(long(mTimestamp), lhcPhaseDummy, channelCalibDummy); + mCalibApi->setDRMCriticalErrorMask(mMaskDRM); if (mUseCCDB) { mCalibApi->setURL(mCCDBurl); mCalibApi->readDiagnosticFrequencies(); + mCalibApi->readDiagnosticDRMFrequencies(); mCalibApi->readLHCphase(); mCalibApi->readActiveMap(); mCalibApi->readTimeSlewingParam(); @@ -300,9 +315,10 @@ class TOFDPLDigitizerTask : public o2::base::BaseDPLDigitizer bool mUpdateCCDB = false; o2::tof::CalibTOFapi* mCalibApi = nullptr; std::string mPass; + uint32_t mMaskDRM = 0; }; -DataProcessorSpec getTOFDigitizerSpec(int channel, bool useCCDB, bool mctruth, std::string ccdb_url, int timestamp) +DataProcessorSpec getTOFDigitizerSpec(int channel, bool useCCDB, bool mctruth, std::string ccdb_url, int timestamp, uint32_t maskDRM) { // create the full data processor spec using // a name identifier @@ -319,6 +335,7 @@ DataProcessorSpec getTOFDigitizerSpec(int channel, bool useCCDB, bool mctruth, s if (useCCDB) { inputs.emplace_back("tofccdbStatus", "TOF", "StatusTOF", 0, Lifetime::Condition, ccdbParamSpec("TOF/Calib/FEELIGHT")); inputs.emplace_back("tofccdbDia", "TOF", "DiagnosticCal", 0, Lifetime::Condition, ccdbParamSpec("TOF/Calib/Diagnostic")); + inputs.emplace_back("tofccdbDrm", "TOF", "DiagnosticDRM", 0, Lifetime::Condition, ccdbParamSpec("TOF/Calib/DRMerrors")); inputs.emplace_back("tofccdbLHCphase", "TOF", "LHCphaseCal", 0, Lifetime::Condition, ccdbParamSpec("TOF/Calib/LHCphase")); inputs.emplace_back("tofccdbChannelCalib", "TOF", "ChannelCalibCal", 0, Lifetime::Condition, ccdbParamSpec("TOF/Calib/ChannelCalib")); inputs.emplace_back("tofccdbParams", "TOF", "parameters", 0, Lifetime::Condition, ccdbParamSpec("TOF/Calib/Params")); @@ -337,7 +354,7 @@ DataProcessorSpec getTOFDigitizerSpec(int channel, bool useCCDB, bool mctruth, s "TOFDigitizer", inputs, outputs, - AlgorithmSpec{adaptFromTask(useCCDB, ccdb_url, timestamp)}, + AlgorithmSpec{adaptFromTask(useCCDB, ccdb_url, timestamp, maskDRM)}, Options{{"pileup", VariantType::Int, 1, {"whether to run in continuous time mode"}}} // I can't use VariantType::Bool as it seems to have a problem }; diff --git a/Steer/DigitizerWorkflow/src/TOFDigitizerSpec.h b/Steer/DigitizerWorkflow/src/TOFDigitizerSpec.h index f5841313c1cb0..7951b93eb1419 100644 --- a/Steer/DigitizerWorkflow/src/TOFDigitizerSpec.h +++ b/Steer/DigitizerWorkflow/src/TOFDigitizerSpec.h @@ -19,7 +19,7 @@ namespace o2 namespace tof { -o2::framework::DataProcessorSpec getTOFDigitizerSpec(int channel, bool useCCDB, bool mctruth, std::string ccdb_url, int timestamp); +o2::framework::DataProcessorSpec getTOFDigitizerSpec(int channel, bool useCCDB, bool mctruth, std::string ccdb_url, int timestamp, uint32_t maskDRM = 1 << 1); } // end namespace tof } // end namespace o2 From 94d365311d47fb9bca5d7fd8a027a7466d0e399f Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 5 Mar 2026 14:29:46 +0100 Subject: [PATCH 326/701] GPU Parameters: Make conversion more robust and compatible to UTF terminal encoding --- GPU/GPUTracking/CMakeLists.txt | 1 + GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh | 3 +++ 2 files changed, 4 insertions(+) diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index dfee81b398a79..c276bf59af40b 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -283,6 +283,7 @@ foreach(GPU_PARAM_JSON_FILE IN LISTS GPU_PARAM_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} + COMMAND_ERROR_IS_FATAL ANY ) message(STATUS "Converted ${GPU_PARAM_JSON_FILE} to ${CONVOUTFILE}") list(APPEND GPU_PARAM_JSON_FILES ${CMAKE_CURRENT_BINARY_DIR}/${CONVOUTFILE}) diff --git a/GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh b/GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh index 373bd18ba7cd4..d064c4f6b58d9 100755 --- a/GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh +++ b/GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh @@ -2,7 +2,10 @@ [[ -z $1 ]] && { echo "Usage: csv_to_json.sh CSV_FILE"; exit 1; } +LANG=C +LC_ALL=C DELIM=$'\xFF' +set -o pipefail sed -E \ ':loop s/^(([^"]*"[^"]*")*[^"]*),/\1'$DELIM'/; From ca0d86d5cefc9e4b01bda1ea0f57f3f95e618fa4 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 4 Mar 2026 11:15:14 +0100 Subject: [PATCH 327/701] Do not use random 1000 value for auto-setting of 83cm for trackReferenceX as default, just disable as default --- Detectors/GlobalTracking/src/MatchCosmics.cxx | 1 - Detectors/GlobalTracking/src/MatchTPCITS.cxx | 1 - Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx | 1 - Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx | 1 - Detectors/TPC/reconstruction/test/testGPUCATracking.cxx | 2 +- Detectors/TPC/workflow/src/TPCRefitter.cxx | 1 - Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx | 1 - Detectors/Vertexing/src/SVertexer.cxx | 1 - GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.cxx | 3 --- GPU/GPUTracking/Definitions/GPUSettingsList.h | 2 +- GPU/Workflow/src/GPUWorkflowSpec.cxx | 3 +++ 11 files changed, 5 insertions(+), 12 deletions(-) diff --git a/Detectors/GlobalTracking/src/MatchCosmics.cxx b/Detectors/GlobalTracking/src/MatchCosmics.cxx index 90964fb1c05fa..3c20ecad2f36c 100644 --- a/Detectors/GlobalTracking/src/MatchCosmics.cxx +++ b/Detectors/GlobalTracking/src/MatchCosmics.cxx @@ -96,7 +96,6 @@ void MatchCosmics::refitWinners(const o2::globaltracking::RecoContainer& data) mTPCCorrMapsHelper, mBz, tpcClusRefs.data(), 0, tpcClusShMap.data(), tpcClusOccMap.data(), tpcClusOccMap.size(), nullptr, o2::base::Propagator::Instance()); - tpcRefitter->setTrackReferenceX(900); // disable propagation after refit by setting reference to value > 500 } const auto& itsClusters = prepareITSClusters(data); diff --git a/Detectors/GlobalTracking/src/MatchTPCITS.cxx b/Detectors/GlobalTracking/src/MatchTPCITS.cxx index 5f99ad2202073..73216c8ce1eac 100644 --- a/Detectors/GlobalTracking/src/MatchTPCITS.cxx +++ b/Detectors/GlobalTracking/src/MatchTPCITS.cxx @@ -505,7 +505,6 @@ bool MatchTPCITS::prepareTPCData() } mTPCRefitter = std::make_unique(mTPCClusterIdxStruct, mTPCCorrMapsHelper, mBz, mTPCTrackClusIdx.data(), 0, mTPCRefitterShMap.data(), mTPCRefitterOccMap.data(), mTPCRefitterOccMap.size(), nullptr, o2::base::Propagator::Instance()); - mTPCRefitter->setTrackReferenceX(900); // disable propagation after refit by setting reference to value > 500 mNTPCOccBinLength = mTPCRefitter->getParam()->rec.tpc.occupancyMapTimeBins; mTBinClOcc.clear(); if (mNTPCOccBinLength > 1 && mTPCRefitterOccMap.size()) { diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx index 05e6a122adec9..ee475acbbcf70 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx @@ -188,7 +188,6 @@ void TPCTrackStudySpec::process(o2::globaltracking::RecoContainer& recoData) if (mTPCTracksArray.size()) { LOGP(info, "Found {} TPC tracks", mTPCTracksArray.size()); mTPCRefitter = std::make_unique(mTPCClusterIdxStruct, &mTPCCorrMapsLoader, prop->getNominalBz(), mTPCTrackClusIdx.data(), 0, mTPCRefitterShMap.data(), mTPCRefitterOccMap.data(), mTPCRefitterOccMap.size(), nullptr, o2::base::Propagator::Instance()); - mTPCRefitter->setTrackReferenceX(900); // disable propagation after refit by setting reference to value > 500 } float vdriftTB = mTPCVDriftHelper.getVDriftObject().getVDrift() * o2::tpc::ParameterElectronics::Instance().ZbinWidth; // VDrift expressed in cm/TimeBin float tpcTBBias = mTPCVDriftHelper.getVDriftObject().getTimeOffset() / (8 * o2::constants::lhc::LHCBunchSpacingMUS); diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx index b8a8f97737b4d..c68e60059cd3f 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx @@ -163,7 +163,6 @@ void TrackingStudySpec::run(ProcessingContext& pc) mTPCRefitter = std::make_unique(&recoData.inputsTPCclusters->clusterIndex, &mTPCCorrMapsLoader, o2::base::Propagator::Instance()->getNominalBz(), recoData.getTPCTracksClusterRefs().data(), 0, recoData.clusterShMapTPC.data(), recoData.occupancyMapTPC.data(), recoData.occupancyMapTPC.size(), nullptr, o2::base::Propagator::Instance()); - mTPCRefitter->setTrackReferenceX(900); // disable propagation after refit by setting reference to value > 500 mNTPCOccBinLength = mTPCRefitter->getParam()->rec.tpc.occupancyMapTimeBins; mTBinClOccBef.clear(); mTBinClOccAft.clear(); diff --git a/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx b/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx index 5c66e4635987f..0debfa72dd7fa 100644 --- a/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx +++ b/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx @@ -51,7 +51,7 @@ BOOST_AUTO_TEST_CASE(CATracking_test1) GPUO2Interface tracker; float solenoidBz = -5.00668; // B-field - float refX = 1000.; // transport tracks to this x after tracking, >500 for disabling + float refX = 83.; // transport tracks to this x after tracking, >500 for disabling bool continuous = false; // time frame data v.s. triggered events GPUO2InterfaceConfiguration config; diff --git a/Detectors/TPC/workflow/src/TPCRefitter.cxx b/Detectors/TPC/workflow/src/TPCRefitter.cxx index 51ff2516524a9..43a55526246fe 100644 --- a/Detectors/TPC/workflow/src/TPCRefitter.cxx +++ b/Detectors/TPC/workflow/src/TPCRefitter.cxx @@ -336,7 +336,6 @@ void TPCRefitterSpec::process(o2::globaltracking::RecoContainer& recoData) } mTPCRefitter = std::make_unique(mTPCClusterIdxStruct, &mTPCCorrMapsLoader, prop->getNominalBz(), mTPCTrackClusIdx.data(), 0, mTPCRefitterShMap.data(), mTPCRefitterOccMap.data(), mTPCRefitterOccMap.size(), nullptr, prop); - mTPCRefitter->setTrackReferenceX(900); // disable propagation after refit by setting reference to value > 500 mVdriftTB = mTPCVDriftHelper.getVDriftObject().getVDrift() * o2::tpc::ParameterElectronics::Instance().ZbinWidth; // VDrift expressed in cm/TimeBin mTPCTBBias = mTPCVDriftHelper.getVDriftObject().getTimeOffset() / (8 * o2::constants::lhc::LHCBunchSpacingMUS); diff --git a/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx b/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx index 9e7ef089faeef..0f578efd3aa5b 100644 --- a/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx +++ b/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx @@ -294,7 +294,6 @@ void TRDGlobalTracking::run(ProcessingContext& pc) mTPCClusterIdxStruct = &inputTracks.inputsTPCclusters->clusterIndex; mTPCRefitter = std::make_unique(mTPCClusterIdxStruct, &mTPCCorrMapsLoader, o2::base::Propagator::Instance()->getNominalBz(), inputTracks.getTPCTracksClusterRefs().data(), 0, inputTracks.clusterShMapTPC.data(), inputTracks.occupancyMapTPC.data(), inputTracks.occupancyMapTPC.size(), nullptr, o2::base::Propagator::Instance()); - mTPCRefitter->setTrackReferenceX(900); // disable propagation after refit by setting reference to value > 500 auto tmpInputContainer = getRecoInputContainer(pc, &mChainTracking->mIOPtrs, &inputTracks, mUseMC); auto tmpContainer = GPUWorkflowHelper::fillIOPtr(mChainTracking->mIOPtrs, inputTracks, mUseMC, nullptr, GTrackID::getSourcesMask("TRD"), mTrkMask, GTrackID::mask_t{GTrackID::MASK_NONE}); mTrackletsRaw = inputTracks.getTRDTracklets(); diff --git a/Detectors/Vertexing/src/SVertexer.cxx b/Detectors/Vertexing/src/SVertexer.cxx index d9206fe54e068..2c625c9cfaf0a 100644 --- a/Detectors/Vertexing/src/SVertexer.cxx +++ b/Detectors/Vertexing/src/SVertexer.cxx @@ -459,7 +459,6 @@ void SVertexer::buildT2V(const o2::globaltracking::RecoContainer& recoData) // a mTPCRefitterShMap = recoData.clusterShMapTPC; mTPCRefitterOccMap = mRecoCont->occupancyMapTPC; mTPCRefitter = std::make_unique(mTPCClusterIdxStruct, mTPCCorrMapsHelper, o2::base::Propagator::Instance()->getNominalBz(), mTPCTrackClusIdx.data(), 0, mTPCRefitterShMap.data(), mTPCRefitterOccMap.data(), mTPCRefitterOccMap.size(), nullptr, o2::base::Propagator::Instance()); - mTPCRefitter->setTrackReferenceX(900); // disable propagation after refit by setting reference to value > 500 } std::unordered_map> tmap; diff --git a/GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.cxx b/GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.cxx index 60fdbe8042c2d..49cd25624b2c3 100644 --- a/GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.cxx +++ b/GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.cxx @@ -119,9 +119,6 @@ GPUSettingsO2 GPUO2InterfaceConfiguration::ReadConfigurableParam(GPUO2InterfaceC if (global.gpuDisplayfilterMacro != "") { obj.configDisplay.filterMacros.emplace_back(global.gpuDisplayfilterMacro); } - if (obj.configReconstruction.tpc.trackReferenceX == 1000.f) { - obj.configReconstruction.tpc.trackReferenceX = 83.f; - } obj.configDeviceBackend.deviceType = gpudatatypes::GetDeviceType(global.deviceType.c_str()); obj.configDeviceBackend.forceDeviceType = global.forceDeviceType; return global; diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index cf6b913551ab5..e34af48d7a85e 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -66,7 +66,7 @@ AddOptionRTC(sysClusErrorC12Box, float, 1.1e-05f, "", 0, "Systematic cluster for AddOptionRTC(minNClustersTrackSeed, int32_t, -1, "", 0, "required min number of clusters on the track after track following (before merging)") AddOptionRTC(minNClustersFinalTrack, int32_t, -1, "", 0, "required min number of clusters on the final track") 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(trackReferenceX, float, 1000.f, "", 0, "Transport all tracks to this X after tracking (disabled if > 500)") AddOptionRTC(zsThreshold, float, 2.0f, "", 0, "Zero-Suppression threshold") 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)") diff --git a/GPU/Workflow/src/GPUWorkflowSpec.cxx b/GPU/Workflow/src/GPUWorkflowSpec.cxx index 48210c440d01e..7f69513dea1d5 100644 --- a/GPU/Workflow/src/GPUWorkflowSpec.cxx +++ b/GPU/Workflow/src/GPUWorkflowSpec.cxx @@ -281,6 +281,9 @@ void GPURecoWorkflowSpec::init(InitContext& ic) mConfig->configProcessing.willProvideO2PropagatorLate = true; mConfig->configProcessing.o2PropagatorUseGPUField = true; + if (mConfig->configReconstruction.tpc.trackReferenceX == 1000.f) { + mConfig->configReconstruction.tpc.trackReferenceX = 83.f; + } if (mConfParam->printSettings && (mConfParam->printSettings > 1 || ic.services().get().inputTimesliceId == 0)) { mConfig->configProcessing.printSettings = true; From 396dc287d6f513a7f305f20a7b0706350c8f9900 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 4 Mar 2026 11:26:52 +0100 Subject: [PATCH 328/701] Add getter for individual GPU configKeyValues --- .../Interface/GPUO2InterfaceConfiguration.inc | 37 +++++++++++++++++++ 1 file changed, 37 insertions(+) create mode 100644 GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.inc diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.inc b/GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.inc new file mode 100644 index 0000000000000..a8ec49df2a281 --- /dev/null +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.inc @@ -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. + +/// \file GPUO2InterfaceConfiguration.inc +/// \author David Rohr + +// Note, this must potentionally be included first, before other GPU headers! +// Do not include in .h files! + +#ifndef GPUO2INTERFACECONFIGURATIONINC_H +#define GPUO2INTERFACECONFIGURATIONINC_H + +#ifdef GPUCA_STANDALONE +#define GPU_GET_CONFIG(configName) static_assert(false, "GPU_GET_CONFIG not available in standalone benchmark") +#else +#include "GPUO2ExternalUser.h" +#include "GPUCommonRtypes.h" +#include "GPUDefMacros.h" +#include "GPUO2InterfaceConfiguration.h" +#include "GPUO2ConfigurableParam.h" +#include + +#define GPU_GET_CONFIG(configName) []() -> decltype(auto) { \ + static_assert(!std::is_same_v); \ + return o2::gpu::GPUCA_M_CAT(GPUConfigurableParam, configName)::Instance(); \ +}() +#endif + +#endif From f4c528d0452e3e2b98807efce437ad8535aa6cd1 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 4 Mar 2026 13:24:16 +0100 Subject: [PATCH 329/701] Fix missing header --- Common/Utils/src/StringUtils.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/Common/Utils/src/StringUtils.cxx b/Common/Utils/src/StringUtils.cxx index 29c43ec18375b..89f834d9a8f2e 100644 --- a/Common/Utils/src/StringUtils.cxx +++ b/Common/Utils/src/StringUtils.cxx @@ -17,6 +17,7 @@ #include #endif #include +#include using namespace o2::utils; From 8858408fe64b51a5ef214fd7cbf8b5a7a22b727e Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 4 Mar 2026 19:54:12 +0100 Subject: [PATCH 330/701] Fetch only GPUSettingsRecTRD for TRD tasks not all GPU ConfigurableParams --- Detectors/Align/src/AlignableDetectorTRD.cxx | 4 +--- .../SpacePoints/src/TrackInterpolation.cxx | 4 +--- Detectors/TRD/calibration/src/TrackBasedCalib.cxx | 4 +--- Detectors/TRD/qc/src/Tracking.cxx | 4 +--- GPU/GPUTracking/DataTypes/GPUTRDRecoParam.cxx | 12 +++++++++++- 5 files changed, 15 insertions(+), 13 deletions(-) diff --git a/Detectors/Align/src/AlignableDetectorTRD.cxx b/Detectors/Align/src/AlignableDetectorTRD.cxx index 080d0f72b2516..6fe8a60ef90f6 100644 --- a/Detectors/Align/src/AlignableDetectorTRD.cxx +++ b/Detectors/Align/src/AlignableDetectorTRD.cxx @@ -178,9 +178,7 @@ int AlignableDetectorTRD::processPoints(GIndex gid, int npntCut, bool inv) auto propagator = o2::base::Propagator::Instance(); // float version! static bool firstCall = true; if (firstCall) { - o2::gpu::GPUO2InterfaceConfiguration config; - config.ReadConfigurableParam(config); - mRecoParam.init(propagator->getNominalBz(), &config.configReconstruction); + mRecoParam.init(propagator->getNominalBz()); firstCall = false; } const auto* transformer = mController->getTRDTransformer(); diff --git a/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx b/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx index cd5e3960160a6..539ae25862865 100644 --- a/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx +++ b/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx @@ -126,9 +126,7 @@ void TrackInterpolation::init(o2::dataformats::GlobalTrackID::mask_t src, o2::da mFastTransform = std::move(TPCFastTransformHelperO2::instance()->create(0)); mBz = o2::base::Propagator::Instance()->getNominalBz(); - o2::gpu::GPUO2InterfaceConfiguration config; - config.ReadConfigurableParam(config); - mRecoParam.init(mBz, &config.configReconstruction); + mRecoParam.init(mBz); mGeoTRD = o2::trd::Geometry::instance(); mParams = &SpacePointsCalibConfParam::Instance(); diff --git a/Detectors/TRD/calibration/src/TrackBasedCalib.cxx b/Detectors/TRD/calibration/src/TrackBasedCalib.cxx index ae1f7b33c6bba..0d551e7b5f33d 100644 --- a/Detectors/TRD/calibration/src/TrackBasedCalib.cxx +++ b/Detectors/TRD/calibration/src/TrackBasedCalib.cxx @@ -36,9 +36,7 @@ void TrackBasedCalib::reset() void TrackBasedCalib::init() { bz = o2::base::Propagator::Instance()->getNominalBz(); - o2::gpu::GPUO2InterfaceConfiguration config; - config.ReadConfigurableParam(config); - mRecoParam.init(bz, &config.configReconstruction); + mRecoParam.init(bz); } void TrackBasedCalib::setInput(const o2::globaltracking::RecoContainer& input) diff --git a/Detectors/TRD/qc/src/Tracking.cxx b/Detectors/TRD/qc/src/Tracking.cxx index 9a0df7efa323b..da2d05794e2d8 100644 --- a/Detectors/TRD/qc/src/Tracking.cxx +++ b/Detectors/TRD/qc/src/Tracking.cxx @@ -26,9 +26,7 @@ using namespace o2::trd::constants; void Tracking::init() { - o2::gpu::GPUO2InterfaceConfiguration config; - config.ReadConfigurableParam(config); - mRecoParam.init(o2::base::Propagator::Instance()->getNominalBz(), &config.configReconstruction); + mRecoParam.init(o2::base::Propagator::Instance()->getNominalBz()); } void Tracking::setInput(const o2::globaltracking::RecoContainer& input) diff --git a/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.cxx b/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.cxx index 674b7a317b477..f7adc2401df79 100644 --- a/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.cxx +++ b/GPU/GPUTracking/DataTypes/GPUTRDRecoParam.cxx @@ -13,6 +13,7 @@ /// \brief Error parameterizations and helper functions for TRD reconstruction /// \author Ole Schmidt +#include "GPUO2InterfaceConfiguration.inc" #include "GPUSettings.h" #include "GPUTRDRecoParam.h" #include "GPUCommonLogger.h" @@ -23,7 +24,16 @@ 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; + float resRPhiIdeal2 = 1.6e-3f; + if (rec) { + resRPhiIdeal2 = rec->trd.trkltResRPhiIdeal * rec->trd.trkltResRPhiIdeal; + } +#ifndef GPUCA_STANDALONE + else { + const auto& rtrd = GPU_GET_CONFIG(GPUSettingsRecTRD); + resRPhiIdeal2 = rtrd.trkltResRPhiIdeal * rtrd.trkltResRPhiIdeal; + } +#endif if (CAMath::Abs(CAMath::Abs(bz) - 2) < 0.1) { if (bz > 0) { From 23fa9c3c76e73cd2cac18dca94817b29c8b424b9 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 5 Mar 2026 10:27:26 +0100 Subject: [PATCH 331/701] Avoid unintended usage of GPUO2ConfigurableParam.h --- Detectors/TPC/workflow/src/CalibdEdxSpec.cxx | 7 ++-- .../TPC/workflow/src/CalibratordEdxSpec.cxx | 7 ++-- .../DataTypes/GPUO2ConfigurableParam.cxx | 4 +-- .../DataTypes/GPUO2ConfigurableParam.h | 13 ++++++-- .../GPUTrackingLinkDef_O2_DataTypes.h | 32 +++++++++---------- .../Interface/GPUO2InterfaceConfiguration.inc | 2 +- 6 files changed, 37 insertions(+), 28 deletions(-) diff --git a/Detectors/TPC/workflow/src/CalibdEdxSpec.cxx b/Detectors/TPC/workflow/src/CalibdEdxSpec.cxx index 7c2e2db8188e8..15ea241a7b350 100644 --- a/Detectors/TPC/workflow/src/CalibdEdxSpec.cxx +++ b/Detectors/TPC/workflow/src/CalibdEdxSpec.cxx @@ -13,6 +13,7 @@ /// \brief Workflow for time based dE/dx calibration. /// \author Thiago Badaró +#include "GPUO2InterfaceConfiguration.inc" #include "TPCWorkflow/CalibdEdxSpec.h" // o2 includes @@ -26,7 +27,6 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" -#include "GPUO2ConfigurableParam.h" #include "TPCCalibration/CalibdEdx.h" #include "TPCWorkflow/ProcessingHelpers.h" #include "TPCBaseRecSim/CDBTypes.h" @@ -70,8 +70,9 @@ class CalibdEdxDevice : public Task mCalib->setElectronCut(fitThreshold, fitPasses, fitThresholdLowFactor); mCalib->setMaterialType(mMatType); - mCustomdEdxFileName = o2::gpu::GPUConfigurableParamGPUSettingsO2::Instance().dEdxCorrFile; - mDisableTimeGain = o2::gpu::GPUConfigurableParamGPUSettingsO2::Instance().dEdxDisableResidualGain; + const auto& gpuConfig = GPU_GET_CONFIG(GPUSettingsO2); + mCustomdEdxFileName = gpuConfig.dEdxCorrFile; + mDisableTimeGain = gpuConfig.dEdxDisableResidualGain; if (mDisableTimeGain) { LOGP(info, "TimeGain correction was disabled via GPU_global.dEdxDisableResidualGain=1"); diff --git a/Detectors/TPC/workflow/src/CalibratordEdxSpec.cxx b/Detectors/TPC/workflow/src/CalibratordEdxSpec.cxx index 87e339f0643f4..dea1d85899675 100644 --- a/Detectors/TPC/workflow/src/CalibratordEdxSpec.cxx +++ b/Detectors/TPC/workflow/src/CalibratordEdxSpec.cxx @@ -13,6 +13,7 @@ /// \brief Workflow for time based dE/dx calibration. /// \author Thiago Badaró +#include "GPUO2InterfaceConfiguration.inc" #include "TPCWorkflow/CalibratordEdxSpec.h" #include @@ -29,7 +30,6 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" -#include "GPUO2ConfigurableParam.h" #include "TPCCalibration/CalibratordEdx.h" #include "TPCWorkflow/ProcessingHelpers.h" #include "DetectorsBase/GRPGeomHelper.h" @@ -86,8 +86,9 @@ class CalibratordEdxDevice : public Task mCalibrator->setTrackDebug(trackDebug); mCalibrator->setMakeGaussianFits(makeGaussianFits); - mCustomdEdxFileName = o2::gpu::GPUConfigurableParamGPUSettingsO2::Instance().dEdxCorrFile; - mDisableTimeGain = o2::gpu::GPUConfigurableParamGPUSettingsO2::Instance().dEdxDisableResidualGain; + const auto& gpuConfig = GPU_GET_CONFIG(GPUSettingsO2); + mCustomdEdxFileName = gpuConfig.dEdxCorrFile; + mDisableTimeGain = gpuConfig.dEdxDisableResidualGain; if (mDisableTimeGain) { LOGP(info, "TimeGain correction was disabled via GPU_global.dEdxDisableResidualGain=1"); diff --git a/GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.cxx b/GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.cxx index 49cd25624b2c3..f3b7e07c4c43b 100644 --- a/GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.cxx +++ b/GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.cxx @@ -29,7 +29,7 @@ using namespace o2::gpu; #define AddOptionArray(name, type, count, default, optname, optnameshort, help, ...) #define AddOptionArrayRTC(...) AddOptionArray(__VA_ARGS__) #define AddSubConfig(name, instance) -#define BeginSubConfig(name, instance, parent, preoptname, preoptnameshort, descr, o2prefix) O2ParamImpl(GPUCA_M_CAT(GPUConfigurableParam, name)) +#define BeginSubConfig(name, instance, parent, preoptname, preoptnameshort, descr, o2prefix) O2ParamImpl(internal::GPUCA_M_CAT(GPUConfigurableParam, name)) #define BeginHiddenConfig(...) #define EndConfig() #define AddCustomCPP(...) @@ -73,7 +73,7 @@ GPUSettingsO2 GPUO2InterfaceConfiguration::ReadConfigurableParam(GPUO2InterfaceC #define BeginSubConfig(name, instance, parent, preoptname, preoptnameshort, descr, o2prefix) \ name instance; \ { \ - const auto& src = GPUCA_M_CAT(GPUConfigurableParam, name)::Instance(); \ + const auto& src = internal::GPUCA_M_CAT(GPUConfigurableParam, name)::Instance(); \ name& dst = instance; #define BeginHiddenConfig(name, instance) { #define EndConfig() } diff --git a/GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.h b/GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.h index 8bc0d98910f54..503ba3e0d51ae 100644 --- a/GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.h +++ b/GPU/GPUTracking/DataTypes/GPUO2ConfigurableParam.h @@ -29,6 +29,7 @@ #include "GPUDefMacros.h" #include +// clang-format off #define BeginNamespace(name) \ namespace name \ { @@ -42,12 +43,17 @@ #define AddOptionArray(name, type, count, default, optname, optnameshort, help, ...) type name[count] = {GPUCA_M_STRIP(default)}; #define AddSubConfig(name, instance) #define BeginSubConfig(name, instance, parent, preoptname, preoptnameshort, descr, o2prefix) \ + namespace internal \ + { \ 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: -#define BeginHiddenConfig(name, instance) struct GPUCA_M_CAT(GPUConfigurableParam, name) { +#define BeginHiddenConfig(name, instance) \ + namespace internal \ + { \ + struct GPUCA_M_CAT(GPUConfigurableParam, name) { #define EndConfig() \ - } \ - ; + }; \ + } // namespace internal #define AddCustomCPP(...) __VA_ARGS__ #define AddHelp(...) #define AddShortcut(...) @@ -71,5 +77,6 @@ #undef AddCustomCPP #undef AddHelp #undef AddShortcut +// clang-format on #endif diff --git a/GPU/GPUTracking/GPUTrackingLinkDef_O2_DataTypes.h b/GPU/GPUTracking/GPUTrackingLinkDef_O2_DataTypes.h index 7bd2c689c5354..5318e23e7d10f 100644 --- a/GPU/GPUTracking/GPUTrackingLinkDef_O2_DataTypes.h +++ b/GPU/GPUTracking/GPUTrackingLinkDef_O2_DataTypes.h @@ -22,22 +22,22 @@ #pragma link C++ class o2::gpu::GPUTRDTrack_t < o2::gpu::trackInterface < o2::track::TrackParCov>> + ; #pragma link C++ class std::vector < o2::gpu::GPUTRDTrack_t < o2::gpu::trackInterface < o2::track::TrackParCov>>> + ; #ifdef GPUCA_O2_LIB -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsO2 + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsRec + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsRecTPC + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsRecTRD + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsRecDynamic + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsProcessing + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsProcessingParam + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsProcessingRTC + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsProcessingRTCtechnical + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsProcessingNNclusterizer + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsDisplay + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsDisplayLight + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsDisplayHeavy + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsDisplayRenderer + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsDisplayVulkan + ; -#pragma link C++ class o2::gpu::GPUConfigurableParamGPUSettingsQA + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsO2 + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsRec + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsRecTPC + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsRecTRD + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsRecDynamic + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsProcessing + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsProcessingParam + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsProcessingRTC + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsProcessingRTCtechnical + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsProcessingNNclusterizer + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsDisplay + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsDisplayLight + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsDisplayHeavy + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsDisplayRenderer + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsDisplayVulkan + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsQA + ; #endif #pragma link C++ class o2::gpu::GPUTPCGMMergedTrackHit + ; #pragma link C++ class o2::tpc::CalibdEdxTrackTopologyPol + ; diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.inc b/GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.inc index a8ec49df2a281..30f6d56a17f1a 100644 --- a/GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.inc +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceConfiguration.inc @@ -30,7 +30,7 @@ #define GPU_GET_CONFIG(configName) []() -> decltype(auto) { \ static_assert(!std::is_same_v); \ - return o2::gpu::GPUCA_M_CAT(GPUConfigurableParam, configName)::Instance(); \ + return o2::gpu::internal::GPUCA_M_CAT(GPUConfigurableParam, configName)::Instance(); \ }() #endif From 4d2d35ee7f495258fb06c105826a9e841ce00b35 Mon Sep 17 00:00:00 2001 From: shahor02 Date: Fri, 6 Mar 2026 09:50:40 +0100 Subject: [PATCH 332/701] Do not parse/init TPC correction options if TPC is absent (#15123) * Do not parse TPC correction options if TPC is absent * Do not init TPC corr.loader in SVFinder in absence of TPC --- .../GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx | 8 +++++--- prodtests/full-system-test/dpl-workflow.sh | 4 +++- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx index 1b55f9c763e7f..6dfd1cb770d7f 100644 --- a/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx @@ -60,9 +60,11 @@ class SecondaryVertexingSpec : public Task public: SecondaryVertexingSpec(std::shared_ptr dr, std::shared_ptr gr, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, GTrackID::mask_t src, bool enabCasc, bool enable3body, bool enableStrangenessTracking, bool enableCCDBParams, bool useMC) : mDataRequest(dr), mGGCCDBRequest(gr), mSrc(src), mEnableCascades(enabCasc), mEnable3BodyVertices(enable3body), mEnableStrangenessTracking(enableStrangenessTracking), mEnableCCDBParams(enableCCDBParams), mUseMC(useMC) { - mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); - mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); - mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); + if (mSrc[GTrackID::TPC]) { + mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); + mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); + mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); + } } ~SecondaryVertexingSpec() override = default; void init(InitContext& ic) final; diff --git a/prodtests/full-system-test/dpl-workflow.sh b/prodtests/full-system-test/dpl-workflow.sh index 9c6c45c049576..a3048a494796e 100755 --- a/prodtests/full-system-test/dpl-workflow.sh +++ b/prodtests/full-system-test/dpl-workflow.sh @@ -332,7 +332,9 @@ done ! has_detector CTP && [[ ${CTPLUMY_DISABLED:-} != 1 ]] && TPC_CORR_OPT+=" --disable-ctp-lumi-request" } -parse_TPC_CORR_SCALING $TPC_CORR_SCALING +if has_detector TPC; then + parse_TPC_CORR_SCALING $TPC_CORR_SCALING +fi if [[ $GPUTYPE != "CPU" && $(ulimit -e) -ge 25 && ${O2_GPU_WORKFLOW_NICE:-} == 1 ]]; then GPU_CONFIG_SELF+=" --child-driver 'nice -n -5'" From 7ebcfb639db92c6d0a4a0426d9490ccbab7e1211 Mon Sep 17 00:00:00 2001 From: cortesep <57937610+cortesep@users.noreply.github.com> Date: Fri, 6 Mar 2026 09:42:29 +0100 Subject: [PATCH 333/701] Update CODEOWNERS (#92) --- CODEOWNERS | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CODEOWNERS b/CODEOWNERS index 26021d458ad76..369a7cf8a8463 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -43,7 +43,7 @@ /DataFormats/Detectors/TRD @f3sch @bazinski @wille10 /DataFormats/Detectors/Upgrades @mconcas /DataFormats/Detectors/Upgrades/ITS3 @fgrosa @arossi81 -/DataFormats/Detectors/ZDC @coppedis +/DataFormats/Detectors/ZDC @coppedis @cortesep #/DataFormats/Headers #/DataFormats/Legacy @@ -75,7 +75,7 @@ /Detectors/Upgrades @mconcas /Detectors/Upgrades/ALICE3 @mconcas @njacazio /Detectors/Upgrades/ITS3 @fgrosa @arossi81 @mconcas @f3sch -/Detectors/ZDC @coppedis +/Detectors/ZDC @coppedis @cortesep /Detectors/CTF @shahor02 /Detectors/Raw @shahor02 /Detectors/StrangenessTracking @mconcas @mpuccio @fmazzasc From 538bd61b8df51e3265af5f0e84abb4b1cf1328a0 Mon Sep 17 00:00:00 2001 From: Francesco Noferini Date: Sat, 7 Mar 2026 10:20:16 +0100 Subject: [PATCH 334/701] fix include in one TOF macro --- Detectors/TOF/prototyping/checkDRMobj_tof.C | 1 + 1 file changed, 1 insertion(+) diff --git a/Detectors/TOF/prototyping/checkDRMobj_tof.C b/Detectors/TOF/prototyping/checkDRMobj_tof.C index 9652a4fb9823e..81381852b15df 100644 --- a/Detectors/TOF/prototyping/checkDRMobj_tof.C +++ b/Detectors/TOF/prototyping/checkDRMobj_tof.C @@ -12,6 +12,7 @@ #if !defined(__CLING__) || defined(__ROOTCLING__) #include "TFile.h" #include "TH2F.h" +#include "TCanvas.h" #include "TOFBase/CalibTOFapi.h" #endif From d40b3503fd10e4df52f61d100466d84f05ab5d1a Mon Sep 17 00:00:00 2001 From: shahoian Date: Sun, 8 Mar 2026 14:18:37 +0100 Subject: [PATCH 335/701] Protection against null proc.context in GPUWorkflow --- GPU/Workflow/src/GPUWorkflowSpec.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/Workflow/src/GPUWorkflowSpec.cxx b/GPU/Workflow/src/GPUWorkflowSpec.cxx index 7f69513dea1d5..f7fc760b99125 100644 --- a/GPU/Workflow/src/GPUWorkflowSpec.cxx +++ b/GPU/Workflow/src/GPUWorkflowSpec.cxx @@ -520,7 +520,7 @@ int32_t GPURecoWorkflowSpec::runMain(o2::framework::ProcessingContext* pc, GPUTr static bool first = true; if (first) { first = false; - if (pc->services().get().inputTimesliceId == 0) { // TPC ConfigurableCarams are somewhat special, need to construct by hand + if (pc && pc->services().get().inputTimesliceId == 0) { // TPC ConfigurableCarams are somewhat special, need to construct by hand o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc->services().get().name, "rec_tpc"), "GPU_rec_tpc,GPU_rec,GPU_proc_param,GPU_proc,GPU_global,trackTuneParams"); } } From 095ccb47ccf3c623ca2b8b962f3addf087203b9b Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Mon, 9 Mar 2026 11:17:52 +0100 Subject: [PATCH 336/701] DPL Analysis: fix missing sourceMatcher --- Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx index ea13d412cd0b8..413adfddecf04 100644 --- a/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx +++ b/Framework/CCDBSupport/src/AnalysisCCDBHelpers.cxx @@ -84,6 +84,7 @@ AlgorithmSpec AnalysisCCDBHelpers::fetchFromCCDB(ConfigContext const& /*ctx*/) if (m.name.starts_with("input:")) { auto name = m.name.substr(6); schemaMetadata->Append("sourceTable", name); + schemaMetadata->Append("sourceMatcher", DataSpecUtils::describe(std::get(DataSpecUtils::fromMetadataString(m.defaultValue.get()).matcher))); continue; } // Ignore the non ccdb: entries From 94799ffc659dfd699992a7f002fcdeb362248551 Mon Sep 17 00:00:00 2001 From: Andrea Sofia Triolo Date: Mon, 9 Mar 2026 21:13:40 +0100 Subject: [PATCH 337/701] [ALICE3] TRK: fix macro CheckDigits.C (#15136) --- .../ALICE3/TRK/macros/test/CheckDigits.C | 112 +++++++++++++----- 1 file changed, 82 insertions(+), 30 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckDigits.C b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckDigits.C index 5d60592a96f41..618dbe929a943 100644 --- a/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckDigits.C +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckDigits.C @@ -19,11 +19,13 @@ #include #include #include +#include +#include #include "TRKBase/SegmentationChip.h" #include "TRKBase/GeometryTGeo.h" #include "DataFormatsITSMFT/Digit.h" -#include "ITSMFTSimulation/Hit.h" +#include "TRKSimulation/Hit.h" #include "MathUtils/Utils.h" #include "SimulationDataFormat/ConstMCTruthContainer.h" #include "SimulationDataFormat/IOMCTruthContainerView.h" @@ -36,14 +38,50 @@ #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") +void addTLines(float pitch) { + // Add grid lines at multiples of pitch on the current pad + if (!gPad) + return; + + gPad->Update(); + + Double_t xmin = gPad->GetUxmin(); + Double_t xmax = gPad->GetUxmax(); + Double_t ymin = gPad->GetUymin(); + Double_t ymax = gPad->GetUymax(); + + // Calculate the first vertical line position (multiple of pitch) + int nLinesX = 0; + for (float x = xmin; x <= xmax && nLinesX < 1000; x += pitch, nLinesX++) { + TLine* line = new TLine(x, ymin, x, ymax); + line->SetLineStyle(2); + line->SetLineColor(kGray); + line->Draw("same"); + } + + // Calculate the first horizontal line position (multiple of pitch) + int nLinesY = 0; + for (float y = ymin; y <= ymax && nLinesY < 1000; y += pitch, nLinesY++) { + TLine* line = new TLine(xmin, y, xmax, y); + line->SetLineStyle(2); + line->SetLineColor(kGray); + line->Draw("same"); + } + + gPad->Modified(); + gPad->Update(); +} + +void CheckDigits(std::string digifile = "trkdigits.root", std::string hitfile = "o2sim_HitsTRK.root", std::string inputGeom = "o2sim_geometry.root", std::string paramfile = "o2sim_par.root") +{ + gStyle->SetPalette(55); using namespace o2::base; using namespace o2::trk; using o2::itsmft::Digit; - using o2::itsmft::Hit; + using o2::trk::Hit; using o2::trk::SegmentationChip; @@ -64,7 +102,7 @@ void CheckDigits(std::string digifile = "trkdigits.root", std::string hitfile = 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*> hitArray(nevH, nullptr); std::vector> mc2hitVec(nevH); @@ -273,110 +311,124 @@ void CheckDigits(std::string digifile = "trkdigits.root", std::string hitfile = 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"); + nt->Draw("y:x >>h_y_vs_x_VD(1000, -3, 3, 1000, -3, 3)", "id < 12 ", "colz"); canvXY->cd(2); - nt->Draw("y:z>>h_y_vs_z_VD(1000, -26, 26, 1000, -3, 3)", "id < 36 ", "colz"); + nt->Draw("y:z>>h_y_vs_z_VD(1000, -26, 26, 1000, -3, 3)", "id < 12 ", "colz"); canvXY->cd(3); - nt->Draw("y:x>>h_y_vs_x_ML(1000, -25, 25, 1000, -25, 25)", "id >= 36 && id < 106 ", "colz"); + nt->Draw("y:x>>h_y_vs_x_ML(1000, -25, 25, 1000, -25, 25)", "id >= 12 && id < 5132 ", "colz"); canvXY->cd(4); - nt->Draw("y:z>>h_y_vs_z_ML(1000, -70, 70, 1000, -25, 25)", "id >= 36 && id < 106 ", "colz"); + nt->Draw("y:z>>h_y_vs_z_ML(1000, -70, 70, 1000, -25, 25)", "id >= 12 && id < 5132 ", "colz"); canvXY->cd(5); - nt->Draw("y:x>>h_y_vs_x_OT(1000, -85, 85, 1000, -85, 85)", "id >= 106 ", "colz"); + nt->Draw("y:x>>h_y_vs_x_OT(1000, -85, 85, 1000, -85, 85)", "id >= 5132 ", "colz"); canvXY->cd(6); - nt->Draw("y:z>>h_y_vs_z_OT(1000, -85, 85, 1000, -130, 130)", "id >= 106 ", "colz"); + nt->Draw("y:z>>h_y_vs_z_OT(1000, -85, 85, 1000, -130, 130)", "id >= 5132 ", "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 "); + nt->Draw("z>>h_z_VD(500, -26, 26)", "id < 12 "); canvZ->cd(2); - nt->Draw("z>>h_z_ML(500, -70, 70)", "id >= 36 && id < 106 "); + nt->Draw("z>>h_z_ML(500, -70, 70)", "id >= 12 && id < 5132 "); canvZ->cd(3); - nt->Draw("z>>h_z_OT(500, -85, 85)", "id >= 106 "); + nt->Draw("z>>h_z_OT(500, -85, 85)", "id >= 5132 "); 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 "); + nt->Draw("dz>>h_dz_VD(500, -0.05, 0.05)", "id < 12 "); canvdZ->cd(2); - nt->Draw("dz>>h_dz_ML(500, -0.05, 0.05)", "id >= 36 && id < 106 "); + nt->Draw("dz>>h_dz_ML(500, -0.05, 0.05)", "id >= 12 && id < 5132 "); canvdZ->cd(3); - nt->Draw("dz>>h_dz_OT(500, -0.05, 0.05)", "id >= 106 "); + nt->Draw("dz>>h_dz_OT(500, -0.05, 0.05)", "id >= 5132 "); canvdZ->SaveAs("trkdigits_dz.pdf"); + canvdZ->SaveAs("trkdigits_dz.root"); // 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"); + nt->Draw("dx:dz>>h_dx_vs_dz_VD(500, -0.005, 0.005, 500, -0.005, 0.005)", "id < 12", "colz"); + addTLines(o2::trk::SegmentationChip::PitchRowVD); 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"); + nt->Draw("dx:dz>>h_dx_vs_dz_VD_z(500, -0.005, 0.005, 500, -0.005, 0.005)", "id < 12 && abs(z)<0.5", "colz"); + addTLines(o2::trk::SegmentationChip::PitchRowVD); 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); + Info("VD |z|<1", "RMS(dx)=%.1f mu", h->GetRMS(2) * 1e4); + Info("VD |z|<1", "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"); + nt->Draw("dx:dz>>h_dx_vs_dz_ML(600, -0.03, 0.03, 600, -0.03, 0.03)", "id >= 12 && id < 5132", "colz"); + addTLines(o2::trk::SegmentationChip::PitchRowMLOT); 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"); + nt->Draw("dx:dz>>h_dx_vs_dz_ML_z(600, -0.03, 0.03, 600, -0.03, 0.03)", "id >= 12 && id < 5132 && abs(z)<2", "colz"); + addTLines(o2::trk::SegmentationChip::PitchRowMLOT); 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"); + nt->Draw("dx:dz>>h_dx_vs_dz_OT(600, -0.03, 0.03, 600, -0.03, 0.03)", "id >= 5132", "colz"); + addTLines(o2::trk::SegmentationChip::PitchRowMLOT); 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"); + nt->Draw("dx:dz>>h_dx_vs_dz_OT_z(600, -0.03, 0.03, 600, -0.03, 0.03)", "id >= 5132 && abs(z)<2", "colz"); h = (TH2F*)gPad->GetPrimitive("h_dx_vs_dz_OT_z"); + addTLines(o2::trk::SegmentationChip::PitchRowMLOT); 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"); + canvdXdZ->SaveAs("trkdigits_dx_vs_dz.root"); // 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"); + nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_VD(300, -0.03, 0.03, 300, -0.03, 0.03)", "id < 12", "colz"); + addTLines(o2::trk::SegmentationChip::PitchRowVD); 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"); + nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_VD_z(300, -0.03, 0.03, 300, -0.03, 0.03)", "id < 12 && abs(z)<2", "colz"); + addTLines(o2::trk::SegmentationChip::PitchRowVD); 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"); + nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_ML(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 12 && id < 5132", "colz"); + addTLines(o2::trk::SegmentationChip::PitchRowMLOT); 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"); + nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_ML_z(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 12 && id < 5132 && abs(z)<2", "colz"); + addTLines(o2::trk::SegmentationChip::PitchRowMLOT); 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"); + nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_OT(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 5132", "colz"); + addTLines(o2::trk::SegmentationChip::PitchRowMLOT); 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"); + nt2->Draw("dxH:dzH>>h_dxH_vs_dzH_OT_z(300, -0.03, 0.03, 300, -0.03, 0.03)", "id >= 5132 && abs(z)<2", "colz"); + addTLines(o2::trk::SegmentationChip::PitchRowMLOT); 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); From b461fc2e20f45aa66adf0de69aaa6adf45adc3ed Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Mon, 9 Mar 2026 22:12:55 +0100 Subject: [PATCH 338/701] ITS: fix zero field seeding Signed-off-by: Felix Schlepper --- Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu | 5 +++-- Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx | 5 +++-- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu index eacf514c7a91d..353464e10712d 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -143,8 +143,9 @@ GPUdii() o2::track::TrackParCov buildTrackSeed(const Cluster& cluster1, 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); + const float dx = x3 - x1; + const float dy = y3 - y1; + snp = sign * dy / o2::gpu::CAMath::Hypot(dx, dy); q2pt = sign / track::kMostProbablePt; q2pt2 = 1.f; } else { diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx index b4ac847863d51..d46db96339495 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx @@ -1278,8 +1278,9 @@ track::TrackParCov TrackerTraits::buildTrackSeed(const Cluster& cluster float snp, q2pt, q2pt2; if (mIsZeroField) { - const float tgp = o2::gpu::CAMath::ATan2(y3 - y1, x3 - x1); - snp = sign * tgp / o2::gpu::CAMath::Sqrt(1.f + tgp * tgp); + const float dx = x3 - x1; + const float dy = y3 - y1; + snp = sign * dy / o2::gpu::CAMath::Hypot(dx, dy); q2pt = sign / track::kMostProbablePt; q2pt2 = 1.f; } else { From 0b3869219ddb9b0caa8d7ae6ec93586353439e9d Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Fri, 6 Mar 2026 08:08:39 +0100 Subject: [PATCH 339/701] CAD->TGeo: Add first support for material mapping The present commit adds support to complement the geometry created from CAD STEP files with materials. For now the script can process materials in a CSV file, where each line maps the CAD part name to a material string. An example is this ``` ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, CAD,Mechanical/Part,ST1782525_01,AA.04,FIRST PART,1.51881,St. Steel EN 1.4306 (304L) ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, CAD,Mechanical/Part,ST2487461_01,AA.04,SECOND PART,2.344,Alu EN AW-5083 (H116) ,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,, CAD,Mechanical/Part,ST2487721_01,AA.02,THIRD PART,0.313133,Carbon Fiber ``` where `ST2487721_01` are the part names referenced in the STEP file. The conversion script will read the material names (e.g., Alu EN AW-5083 (H116)) and attempt a mapping to known materials in the Geant4 NIST database of materials based on string matching as well as material density. The user should check the emitted materials in the resulting geometry file (geom.C) and possibly edit/correct the code. Another possibility would be to fix the CSV file from the start and only reference Geant4 NIST names. The commit also contains a small utility to extract a JSON G4 NIST database from the Geant4 engine. The resulting JSON file `G4_NIST_DB.json` is also part of the commit and can be extended by the user with further definitions. A complete conversion example, including materials would like this: ```bash python3 O2_CADtoTGeo.py STEPFILE.stp --mesh [--mesh-prec 1.2] \ --out-path tgeo_geometry_output_folder -o geom.C \ --materials-csv MATERIALS.csv \ --g4-nist-json G4_NIST_DB.json ``` Code generated with help of a code assistent. --- scripts/geometry/O2_CADtoTGeo.py | 785 +- .../geometry/g4_nist_database/G4_NIST_DB.json | 7160 +++++++++++++++++ scripts/geometry/g4_nist_database/compile.sh | 11 + .../g4_nist_database/nist_export_all.cxx | 136 + scripts/geometry/simulating_CAD_modules.md | 12 +- 5 files changed, 8080 insertions(+), 24 deletions(-) create mode 100644 scripts/geometry/g4_nist_database/G4_NIST_DB.json create mode 100755 scripts/geometry/g4_nist_database/compile.sh create mode 100644 scripts/geometry/g4_nist_database/nist_export_all.cxx diff --git a/scripts/geometry/O2_CADtoTGeo.py b/scripts/geometry/O2_CADtoTGeo.py index d564cdc6124a8..3de2fd75973df 100644 --- a/scripts/geometry/O2_CADtoTGeo.py +++ b/scripts/geometry/O2_CADtoTGeo.py @@ -4,6 +4,14 @@ 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. +NEW (03/2026): + - Optional material/medium emission from a BOM (bill of materials) CSV file. + The CSV is expected to contain lines like: + CAD, Mechanical/Part, , , , , , ... + - If both a part mass and a CAD volume are available, an effective density is computed + and used in the emitted TGeoMaterial. Otherwise a reasonable default density is used + for a few common materials, or 1.0 g/cm^3 as fallback. + Generates (into --output-folder): - geom.C (small ROOT macro) - facets__.bin for each leaf logical volume (float32 triangles) @@ -28,15 +36,21 @@ Author: - Sandro Wenzel, CERN (02/2026) + - Material/BOM integration patch (03/2026) """ import warnings warnings.filterwarnings("ignore", message=".*all to deprecated function.*", category=DeprecationWarning) import argparse +import csv +import json +import math import re import struct +from dataclasses import dataclass from pathlib import Path as _Path +from typing import Dict, List, Optional, Tuple from OCC.Core.Bnd import Bnd_Box from OCC.Core.BRepBndLib import brepbndlib @@ -55,6 +69,14 @@ from OCC.Core.TCollection import TCollection_AsciiString from OCC.Core.gp import gp_Trsf +# volume properties for density calcs (may not be present in older pythonOCC builds) +try: + from OCC.Core.GProp import GProp_GProps + from OCC.Core.BRepGProp import brepgprop_VolumeProperties + _HAS_VOLPROPS = True +except Exception: + _HAS_VOLPROPS = False + # ------------------------------- # STEP/XCAF loading @@ -221,6 +243,30 @@ def triangulate_CAD_solid(my_solid, meshparam, scale_to_cm: float = 1.0): return _scale_triangles(triangles, scale_to_cm) +# ------------------------------- +# Volume helpers (for density) +# ------------------------------- + +def volume_cm3_of_shape(shape, scale_to_cm: float) -> float: + """Compute CAD solid volume in cm^3 (using STEP->cm scale).""" + if _HAS_VOLPROPS: + try: + props = GProp_GProps() + brepgprop_VolumeProperties(shape, props) + # volume returned in STEP length units^3 + v = float(props.Mass()) + return v * (scale_to_cm ** 3) + except Exception: + pass + + # Fallback: bounding-box volume (rough but always defined) + box = Bnd_Box() + brepbndlib.Add(shape, box) + xmin, ymin, zmin, xmax, ymax, zmax = box.Get() + dx, dy, dz = (xmax - xmin) * scale_to_cm, (ymax - ymin) * scale_to_cm, (zmax - zmin) * scale_to_cm + return max(dx, 0.0) * max(dy, 0.0) * max(dz, 0.0) + + # ------------------------------- # Naming helpers # ------------------------------- @@ -256,6 +302,472 @@ def write_facets_bin(path: _Path, triangles): )) +# ------------------------------- +# BOM / material mapping +# ------------------------------- + +@dataclass(frozen=True) +class BomEntry: + part_number: str + revision: str + name: str + mass_value: float # as in CSV + material: str + + @property + def part_number_key(self) -> str: + return (self.part_number or "").strip() + + @property + def name_key(self) -> str: + return (self.name or "").strip() + + +def _to_float(s: str) -> Optional[float]: + try: + if s is None: + return None + s = str(s).strip() + if not s: + return None + return float(s) + except Exception: + return None + + +def read_bom_csv(csv_path: str) -> List[BomEntry]: + """ + Reads a BOM CSV in the format provided by design team. + + We look for rows whose first column is 'CAD' and second is 'Mechanical/Part'. + Columns (0-based): + 0 CAD + 1 type + 2 part number + 3 revision + 4 name/description + 5 mass + 6 material + """ + entries: List[BomEntry] = [] + with open(csv_path, newline="", encoding="utf-8", errors="ignore") as f: + reader = csv.reader(f) + for row in reader: + if not row: + continue + if len(row) < 7: + continue + if row[0].strip() != "CAD": + continue + if row[1].strip() != "Mechanical/Part": + continue + + part_no = (row[2] or "").strip() + rev = (row[3] or "").strip() + name = (row[4] or "").strip() + mass = _to_float(row[5]) + mat = (row[6] or "").strip() + + if not (part_no or name): + continue + if mass is None: + mass = float("nan") + if not mat: + mat = "Default" + + entries.append(BomEntry(part_no, rev, name, float(mass), mat)) + return entries + + + +def normalize_material_name(mat: str) -> str: + """ + Normalizes a BOM material string for matching / caching. + + Note: We keep the *original* string for ROOT object names; this is only used + internally for robust matching and dictionary keys. + """ + mat = (mat or "Default").strip() + mat = re.sub(r"\s+", " ", mat) + return mat + + +def _norm_tokens(s: str) -> List[str]: + s = (s or "").lower() + # common grade/format noise + s = re.sub(r"\(.*?\)", " ", s) + s = s.replace("en aw", " ") + s = s.replace("en-aw", " ") + s = s.replace("en", " ") + s = s.replace("aw", " ") + s = s.replace("_", " ").replace("-", " ") + s = re.sub(r"[^a-z0-9]+", " ", s) + s = re.sub(r"\s+", " ", s).strip() + if not s: + return [] + toks = s.split(" ") + + # small synonym normalization + syn = { + "alu": "al", + "aluminium": "aluminum", + "silicium": "silicon", + "inox": "stainless", + "ss": "stainless", + "cu": "copper", + "fe": "iron", + "ptfe": "teflon", + "ti": "titanium", + "be": "beryllium", + } + + # Expand common element symbols to names and vice-versa so that e.g. "G4_Si" can match "silicon". + elem_alias = { + "h": "hydrogen", "he": "helium", "c": "carbon", "n": "nitrogen", "o": "oxygen", + "al": "aluminum", "si": "silicon", "fe": "iron", "cu": "copper", "be": "beryllium", + "mg": "magnesium", "mn": "manganese", "cr": "chromium", "ni": "nickel", "zn": "zinc", + "ti": "titanium", "w": "tungsten", "pb": "lead", "sn": "tin", + } + name_to_sym = {v: k for k, v in elem_alias.items()} + + out: List[str] = [] + for t in toks: + t2 = syn.get(t, t) + out.append(t2) + if t2 in elem_alias: + out.append(elem_alias[t2]) + if t2 in name_to_sym: + out.append(name_to_sym[t2]) + + # de-dup while preserving order + seen = set() + out2: List[str] = [] + for t in out: + if t and t not in seen: + seen.add(t) + out2.append(t) + return out2 + + +def _density_score(rho_part: Optional[float], rho_ref: Optional[float]) -> float: + if rho_part is None or rho_ref is None or not (rho_part > 0.0) or not (rho_ref > 0.0): + return 0.0 + # symmetric score in log-space; 1.0 is perfect match + d = abs(math.log(rho_ref / rho_part)) + return 1.0 / (1.0 + d) + + +def _token_score(tokens_a: List[str], tokens_b: List[str]) -> float: + if not tokens_a or not tokens_b: + return 0.0 + sa = set(tokens_a) + sb = set(tokens_b) + inter = len(sa & sb) + union = len(sa | sb) + if union == 0: + return 0.0 + return inter / union + + +def load_g4_nist_db(json_path: str) -> Dict[str, dict]: + """ + Loads a JSON dump created by the 'nist_export_all' tool. + Returns a dict: nist_name -> material record. + """ + with open(json_path, "r", encoding="utf-8") as f: + data = json.load(f) + mats = data.get("materials", {}) + if not isinstance(mats, dict) or not mats: + raise RuntimeError(f"G4 NIST DB JSON seems empty or malformed: {json_path}") + return mats + +# Minimal periodic table for parsing custom alloys not present in NIST. +# Values: Z (atomic number), A (g/mol) +_ELEMENT_TABLE = { + "H": (1, 1.00794), + "C": (6, 12.0107), + "N": (7, 14.0067), + "O": (8, 15.9994), + "Al": (13, 26.9815385), + "Si": (14, 28.0855), + "Fe": (26, 55.845), + "Cu": (29, 63.546), + "Be": (4, 9.0121831), + "Mg": (12, 24.305), + "Mn": (25, 54.938044), + "Cr": (24, 51.9961), + "Ni": (28, 58.6934), + "Zn": (30, 65.38), + "Ti": (22, 47.867), + "W": (74, 183.84), + "Pb": (82, 207.2), + "Sn": (50, 118.71), +} + + +@dataclass +class ResolvedMaterial: + bom_name: str + nist_name: Optional[str] # e.g. "G4_Al" + score: float + rho_used_g_cm3: Optional[float] # density used in ROOT definition + radlen_cm: Optional[float] + intlen_cm: Optional[float] + elements: Optional[List[dict]] # list of {symbol,Z,A_g_mol,mass_fraction} + note: str # for comments in geom.C (warnings/FIXME) + +@dataclass +class MatMatchConfig: + # Minimum combined score to accept a match. + min_score: float = 0.35 + # If (best - second_best) < ambiguity_delta, treat as ambiguous/unresolved. + ambiguity_delta: float = 0.05 + # Weights for the combined score = w_token * token_score + w_density * density_score + w_token: float = 0.75 + w_density: float = 0.25 + # Optional hard filter on density proximity (in log-space). If <=0, disabled. + # Example: max_log_density_diff=0.8 means accept within exp(0.8)~2.2x in either direction. + max_log_density_diff: float = 0.0 + # Penalize compound matches (oxide/dioxide/carbide/...) when BOM doesn't mention those tokens. + compound_penalty: float = 0.25 + + +def resolve_bom_material( + bom_material: str, + rho_part_g_cm3: Optional[float], + g4db: Optional[Dict[str, dict]], + cfg: MatMatchConfig, +) -> ResolvedMaterial: + """ + Resolves an arbitrary BOM material string to a Geant4 NIST material name using: + - exact key match (BOM already uses e.g. "G4_Al") + - token overlap scoring on names + - density proximity scoring (if rho_part_g_cm3 available) + + If unresolved/ambiguous, tries to parse element symbols from the BOM string (e.g. "Cu Be") + and emits a placeholder mixture (equal mass fractions) annotated with FIXME. + """ + raw_bom_material = (bom_material or "").strip() + bom_material = normalize_material_name(bom_material) + + if not g4db: + return ResolvedMaterial( + bom_name=bom_material, + nist_name=None, + score=0.0, + rho_used_g_cm3=rho_part_g_cm3, + radlen_cm=None, + intlen_cm=None, + elements=None, + note="FIXME: No Geant4 NIST DB provided; using dummy material.", + ) + + # Trivial: BOM already provides an exact Geant4 material key + if bom_material in g4db: + rec = g4db[bom_material] + rho_ref = rec.get("density_g_cm3") + # Use NIST density for emission; CAD-derived density is used only for matching. + rho_used = rho_ref + + rad = rec.get("radlen_cm") + itl = rec.get("intlen_cm") + + return ResolvedMaterial( + bom_name=bom_material, + nist_name=bom_material, + score=1.0, + rho_used_g_cm3=rho_used, + radlen_cm=rad, + intlen_cm=itl, + elements=rec.get("elements", []), + note="Resolved by exact Geant4 NIST name from BOM.", + ) + + bom_toks = _norm_tokens(bom_material) + if not bom_toks: + return ResolvedMaterial( + bom_name=bom_material, + nist_name=None, + score=0.0, + rho_used_g_cm3=rho_part_g_cm3, + radlen_cm=None, + intlen_cm=None, + elements=None, + note="FIXME: Empty/unknown BOM material string; using dummy material.", + ) + + def _build_custom_from_elements(note_prefix: str) -> Optional[ResolvedMaterial]: + s = raw_bom_material + if not s: + return None + + symbols = set(re.findall(r"\b([A-Z][a-z]?)\b", s)) + name_to_symbol = { + "aluminum": "Al", "aluminium": "Al", "silicon": "Si", "iron": "Fe", "copper": "Cu", + "beryllium": "Be", "magnesium": "Mg", "manganese": "Mn", "chromium": "Cr", "nickel": "Ni", + "zinc": "Zn", "titanium": "Ti", "tungsten": "W", "lead": "Pb", "tin": "Sn", + } + for t in bom_toks: + if t in name_to_symbol: + symbols.add(name_to_symbol[t]) + + symbols = [sym for sym in sorted(symbols) if sym in _ELEMENT_TABLE] + if not symbols: + return None + + frac = 1.0 / float(len(symbols)) + elems: List[dict] = [] + for sym in symbols: + Z, A = _ELEMENT_TABLE[sym] + elems.append({"symbol": sym, "Z": Z, "A_g_mol": A, "mass_fraction": frac}) + + return ResolvedMaterial( + bom_name=bom_material, + nist_name=None, + score=0.0, + rho_used_g_cm3=rho_part_g_cm3, + radlen_cm=None, + intlen_cm=None, + elements=elems, + note=f"FIXME: {note_prefix} No suitable Geant4 NIST material. Emitting placeholder mixture from parsed elements {symbols} with equal mass fractions; please adjust fractions/material.", + ) + + best = (None, -1.0, 0.0, 0.0) # (nist_name, score, dens_score, token_score) + second = (None, -1.0, 0.0, 0.0) + + bom_has_compound = any(t in bom_toks for t in ( + "oxide", "dioxide", "carbide", "nitride", "fluoride", "chloride", + "sulfate", "phosphate", "glass", "dioxyde" + )) + + for nist_name, rec in g4db.items(): + nist_toks = _norm_tokens(nist_name) + ts = _token_score(bom_toks, nist_toks) + if ts <= 0.0: + continue + + ds = _density_score(rho_part_g_cm3, rec.get("density_g_cm3")) + + # Optional hard density filter + if cfg.max_log_density_diff and cfg.max_log_density_diff > 0.0 and rho_part_g_cm3 and rec.get("density_g_cm3"): + try: + if abs(math.log(float(rec.get("density_g_cm3")) / float(rho_part_g_cm3))) > cfg.max_log_density_diff: + continue + except Exception: + pass + + nist_has_compound = any(t in nist_toks for t in ( + "oxide", "dioxide", "carbide", "nitride", "fluoride", "chloride", + "sulfate", "phosphate", "glass", "dioxyde" + )) + compound_pen = cfg.compound_penalty if (nist_has_compound and not bom_has_compound) else 0.0 + + score = cfg.w_token * ts + cfg.w_density * ds - compound_pen + + if score > best[1]: + second = best + best = (nist_name, score, ds, ts) + elif score > second[1]: + second = (nist_name, score, ds, ts) + + nist_best, score_best, ds_best, ts_best = best + nist_second, score_second, _, _ = second + + if nist_best is None or score_best < cfg.min_score: + custom = _build_custom_from_elements("Could not resolve with enough confidence.") + if custom is not None: + return custom + return ResolvedMaterial( + bom_name=bom_material, + nist_name=None, + score=float(score_best if score_best > 0 else 0.0), + rho_used_g_cm3=rho_part_g_cm3, + radlen_cm=None, + intlen_cm=None, + elements=None, + note="FIXME: Could not resolve BOM material to a Geant4 NIST material with enough confidence; using dummy material.", + ) + + if score_second > 0 and (score_best - score_second) < cfg.ambiguity_delta: + custom = _build_custom_from_elements( + f"Ambiguous material match (best '{nist_best}' score={score_best:.3f}, second '{nist_second}' score={score_second:.3f})." + ) + if custom is not None: + return custom + return ResolvedMaterial( + bom_name=bom_material, + nist_name=None, + score=float(score_best), + rho_used_g_cm3=rho_part_g_cm3, + radlen_cm=None, + intlen_cm=None, + elements=None, + note=f"FIXME: Ambiguous material match (best '{nist_best}' score={score_best:.3f}, second '{nist_second}' score={score_second:.3f}); using dummy material.", + ) + + rec = g4db[nist_best] + rho_ref = rec.get("density_g_cm3") + # Use NIST density for emission; CAD-derived density is used only for matching. + rho_used = rho_ref + + rad = rec.get("radlen_cm") + itl = rec.get("intlen_cm") + + return ResolvedMaterial( + bom_name=bom_material, + nist_name=nist_best, + score=float(score_best), + rho_used_g_cm3=rho_used, + radlen_cm=rad, + intlen_cm=itl, + elements=rec.get("elements", []), + note=f"Resolved to '{nist_best}' (token={ts_best:.3f}, density={ds_best:.3f}, score={score_best:.3f}).", + ) + + +def build_volume_to_material_map( + bom_entries: List[BomEntry], + def_names: Dict[str, str], +) -> Dict[str, BomEntry]: + """ + Builds a mapping def_lid -> BomEntry by matching the XCAF display name to: + - exact part_number match + - exact description/name match + - substring match on part_number within the XCAF name + + This is heuristic; if nothing matches we keep no assignment for that volume. + """ + # lookup tables + by_part: Dict[str, BomEntry] = {} + by_name: Dict[str, BomEntry] = {} + for e in bom_entries: + if e.part_number_key: + by_part[e.part_number_key] = e + if e.name_key and e.name_key not in by_name: + by_name[e.name_key] = e + + out: Dict[str, BomEntry] = {} + for lid, disp in def_names.items(): + key = (disp or "").strip() + if not key: + continue + + # 1) exact part number + if key in by_part: + out[lid] = by_part[key] + continue + # 2) exact name/description + if key in by_name: + out[lid] = by_name[key] + continue + # 3) substring match on any part number + for pn, e in by_part.items(): + if pn and pn in key: + out[lid] = e + break + return out + + # ------------------------------- # C++ emission helpers # ------------------------------- @@ -306,27 +818,100 @@ def emit_cpp_prelude() -> str: """ -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_materials_cpp( + used_materials: Dict[str, ResolvedMaterial], + # key: BOM material string as used in CSV after normalization +) -> Tuple[str, Dict[str, str]]: + """ + Emits C++ code defining TGeoMaterial/TGeoMixture + TGeoMedium for all used materials. + + - If a material resolved to a Geant4 NIST entry, emit a physically correct mixture + (element mass fractions) and set RadLen/IntLen (from Geant4) when available. + - If unresolved/ambiguous, emit a dummy material and annotate with FIXME comments. + """ + cpp: List[str] = [] + cpp.append(" // Default material/medium (placeholder; can be replaced later)") + cpp.append(" TGeoMaterial *mat_Default = new TGeoMaterial(\"Default\", 0., 0., 0.);") + cpp.append(" TGeoMedium *med_Default = new TGeoMedium(\"Default\", 1, mat_Default);") + cpp.append("") + + emitted_el: Dict[str, str] = {} + + def _emit_element(el: dict) -> str: + sym = el.get("symbol", "X") + Z = int(el.get("Z", 0)) + A = float(el.get("A_g_mol", 0.0)) + if sym in emitted_el: + return emitted_el[sym] + safe = sanitize_cpp_name(sym) + var = f"el_{safe}" + cpp.append(f" TGeoElement *{var} = new TGeoElement(\"{sym}\", \"{sym}\", {Z}, {A:.10g});") + emitted_el[sym] = var + return var + + medium_var: Dict[str, str] = {"Default": "med_Default"} + next_id = 2 + + for bom_mat in sorted(used_materials.keys(), key=lambda s: s.lower()): + rm = used_materials[bom_mat] + safe = sanitize_cpp_name(bom_mat) + base = safe + k = 2 + while f"med_{safe}" in medium_var.values(): + safe = f"{base}_{k}" + k += 1 + + rho = rm.rho_used_g_cm3 if (rm.rho_used_g_cm3 and rm.rho_used_g_cm3 > 0.0) else 0.0 + + cpp.append(f" // BOM material: {rm.bom_name}") + cpp.append(f" // {rm.note}") + + if rm.elements: + elems = rm.elements + if len(elems) == 1 and abs(float(elems[0].get('mass_fraction', 1.0)) - 1.0) < 1e-6: + el = elems[0] + A = float(el.get("A_g_mol", 0.0)) + Z = float(el.get("Z", 0)) + cpp.append(f" TGeoMaterial *mat_{safe} = new TGeoMaterial(\"{bom_mat}\", {A:.10g}, {Z:.10g}, {rho:.10g});") + else: + cpp.append(f" TGeoMixture *mat_{safe} = new TGeoMixture(\"{bom_mat}\", {len(elems)}, {rho:.10g});") + for el in elems: + elvar = _emit_element(el) + w = float(el.get("mass_fraction", 0.0)) + cpp.append(f" mat_{safe}->AddElement({elvar}, {w:.10g});") + + if rm.radlen_cm is not None and rm.intlen_cm is not None: + cpp.append(f" mat_{safe}->SetRadLen({float(rm.radlen_cm):.10g}, {float(rm.intlen_cm):.10g});") + elif rm.radlen_cm is not None: + cpp.append(f" mat_{safe}->SetRadLen({float(rm.radlen_cm):.10g});") + else: + cpp.append(" // FIXME: Unresolved material. Replace with a proper TGeoMaterial/TGeoMixture.") + cpp.append(f" TGeoMaterial *mat_{safe} = new TGeoMaterial(\"{bom_mat}\", 0., 0., {rho:.10g});") + + cpp.append(f" TGeoMedium *med_{safe} = new TGeoMedium(\"{bom_mat}\", {next_id}, mat_{safe});") + cpp.append("") + medium_var[bom_mat] = f"med_{safe}" + next_id += 1 + + return "\n".join(cpp), medium_var + + -def emit_tessellated_cpp(lid: str, vol_display_name: str, facet_abspath: str, ntriangles: int) -> str: +def emit_tessellated_cpp(lid: str, vol_display_name: str, facet_abspath: str, ntriangles: int, medium_var: str) -> 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);') + out.append(f' TGeoVolume *vol_{safe} = new TGeoVolume("{shape_name}", solid_{safe}, {medium_var});') 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);') + out.append(f' TGeoVolume *vol_{safe} = new TGeoVolume("{shape_name}", solid_{safe}, {medium_var});') return "\n".join(out) @@ -340,12 +925,13 @@ def emit_assembly_cpp(lid: str, asm_display_name: str) -> str: # 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 +logical_volumes: Dict[str, list] = {} # def_lid -> triangles +def_names: Dict[str, str] = {} # def_lid -> human display name (may be "") +def_volumes_cm3: Dict[str, float] = {} # def_lid -> volume in cm^3 (leaf only) +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: @@ -393,6 +979,13 @@ def expand_definition(def_label: TDF_Label, shape_tool, meshparam=None, scale_to if shape_tool.IsSimpleShape(def_label): if def_lid not in logical_volumes: shape = shape_tool.GetShape(def_label) + + # store volume (for density estimation) + try: + def_volumes_cm3[def_lid] = volume_cm3_of_shape(shape, scale_to_cm=scale_to_cm) + except Exception: + def_volumes_cm3[def_lid] = 0.0 + 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 @@ -401,9 +994,10 @@ def expand_definition(def_label: TDF_Label, shape_tool, meshparam=None, scale_to 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 + global logical_volumes, def_names, def_volumes_cm3, assemblies, placements, top_defs, visited_defs logical_volumes = {} def_names = {} + def_volumes_cm3 = {} assemblies = set() placements = [] top_defs = set() @@ -439,7 +1033,52 @@ def emit_placement_cpp(parent_def: str, child_def: str, trsf: gp_Trsf, 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"): + +def _compute_density_g_cm3( + volume_cm3: float, + mass_value: float, + mass_unit: str, +) -> Tuple[Optional[float], str]: + """ + Computes an effective part density from (mass, CAD volume). + + Returns (rho_g_cm3 or None, comment). If rho is None, caller should fall back + to the Geant4 NIST density (if resolved) or to a dummy density. + """ + if not volume_cm3 or volume_cm3 <= 0: + return None, "no CAD volume available for density" + + if (mass_value is None) or (isinstance(mass_value, float) and math.isnan(mass_value)): + return None, "no BOM mass available for density" + + mass_g = float(mass_value) + mu = (mass_unit or "kg").lower() + if mu == "kg": + mass_g *= 1000.0 + elif mu == "g": + pass + else: + # unknown unit: assume kg + mass_g *= 1000.0 + + rho = mass_g / float(volume_cm3) + # Guard against obvious unit/volume issues + if not (0.01 < rho < 50.0): + return None, f"computed density {rho:.3g} g/cm3 rejected (unit mismatch?)" + + return rho, "density from BOM mass and CAD volume" + + +def emit_root_macro( + step_path: str, + out_folder: _Path, + meshparam=None, + step_unit: str = "auto", + materials_csv: Optional[str] = None, + bom_mass_unit: str = "kg", + g4_nist_json: Optional[str] = None, + mat_cfg: Optional[MatMatchConfig] = None, +): if (step_unit or "auto").lower() == "auto": detected = detect_step_length_unit(step_path) scale_to_cm = step_unit_scale_to_cm(detected) @@ -453,6 +1092,28 @@ def emit_root_macro(step_path: str, out_folder: _Path, meshparam=None, step_unit out_folder = out_folder.expanduser().resolve() out_folder.mkdir(parents=True, exist_ok=True) + + # --- Geant4 NIST material DB (optional but recommended) --- + g4db: Optional[Dict[str, dict]] = None + if g4_nist_json: + g4db = load_g4_nist_db(g4_nist_json) + print(f"Loaded Geant4 NIST DB with {len(g4db)} materials from: {g4_nist_json}") + else: + print("No --g4-nist-json provided: unresolved materials will fall back to dummy ROOT materials.") + mat_cfg = mat_cfg or MatMatchConfig() + + + # --- BOM: map volumes to materials (heuristic) --- + lid_to_bom: Dict[str, BomEntry] = {} + if materials_csv: + bom_entries = read_bom_csv(materials_csv) + lid_to_bom = build_volume_to_material_map(bom_entries, def_names) + print(f"Loaded {len(bom_entries)} BOM entries from: {materials_csv}") + print(f"Matched {len(lid_to_bom)} CAD logical volumes to BOM entries (by name/part-number heuristics)") + else: + print("No --materials-csv provided: emitting Default medium for all logical volumes") + + # --- facet files --- facet_files = {} # def_lid -> absolute path string for lid, tris in logical_volumes.items(): disp = def_names.get(lid, "") @@ -463,16 +1124,63 @@ def emit_root_macro(step_path: str, out_folder: _Path, meshparam=None, step_unit write_facets_bin(fpath, tris) facet_files[lid] = str(fpath).replace("\\", "\\\\") # C++ string literal safety - cpp = [] + # --- which materials do we need to emit? --- + + # --- materials: collect unique BOM material strings actually used by leaf volumes --- + # We resolve each unique BOM string to a Geant4 NIST material using string + density scoring. + used_materials: Dict[str, ResolvedMaterial] = {} + + # Precompute one representative part density per BOM material (first good value wins) + mat_to_rho: Dict[str, Optional[float]] = {} + mat_to_rho_note: Dict[str, str] = {} + + for lid in logical_volumes.keys(): + if lid not in lid_to_bom: + continue + bom = lid_to_bom[lid] + mat_name = normalize_material_name(bom.material) + + if mat_name not in mat_to_rho: + rho_part, rho_note = _compute_density_g_cm3( + def_volumes_cm3.get(lid, 0.0), + bom.mass_value, + bom_mass_unit, + ) + mat_to_rho[mat_name] = rho_part + mat_to_rho_note[mat_name] = rho_note + + for mat_name in sorted(mat_to_rho.keys(), key=lambda s: s.lower()): + rho_part = mat_to_rho.get(mat_name) + rm = resolve_bom_material(mat_name, rho_part, g4db, mat_cfg) + + # Fold density provenance into the note for geom.C comments + rm.note = f"{rm.note} (density: {mat_to_rho_note.get(mat_name, 'n/a')})" + + if rm.nist_name is None: + print(f"WARNING: Unresolved/ambiguous material '{mat_name}'. See FIXME in generated geom.C.") + + used_materials[mat_name] = rm + + materials_cpp, medium_var_map = emit_materials_cpp(used_materials) + + # --- emit C++ macro --- + cpp: List[str] = [] 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()) + cpp.append(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)) + + # choose medium for this volume + med = "med_Default" + if lid in lid_to_bom: + mat_name = normalize_material_name(lid_to_bom[lid].material) + med = medium_var_map.get(mat_name, "med_Default") + + cpp.append(emit_tessellated_cpp(lid, def_names.get(lid, ""), facet_files[lid], ntriangles, med)) for lid in sorted(assemblies): cpp.append(emit_assembly_cpp(lid, def_names.get(lid, ""))) @@ -527,7 +1235,7 @@ def traverse_print(label, shape_tool, depth=0): indent = " " * depth name = label.GetLabelName() entry = label_entry(label) - print(f"{indent}- {name} =>[{entry}]") + print(f"{indent}- {name} =>[{entry}]") if shape_tool.IsReference(label): ref_label = TDF_Label() @@ -569,7 +1277,21 @@ def main(): 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)") + ap.add_argument("--step-unit", default="auto", choices=["auto", "mm", "cm", "m", "in", "ft"], help="STEP length unit override (default: auto-detect); TGeo expects cm") + + # NEW: BOM / material support + ap.add_argument("--materials-csv", default=None, help="BOM CSV file providing material + mass per part (optional)") + ap.add_argument("--bom-mass-unit", default="kg", choices=["kg", "g"], help="Unit of the BOM mass column (default: kg)") + ap.add_argument("--g4-nist-json", default=None, help="Path to Geant4 NIST DB JSON dump (from nist_export_all). Enables TGeoMixture emission + RadLen/IntLen.") + + + # Material matching scoring knobs (only used if --g4-nist-json is provided) + ap.add_argument("--mat-min-score", type=float, default=0.35, help="Minimum combined score to accept a G4 NIST material match (default: 0.35)") + ap.add_argument("--mat-ambiguity-delta", type=float, default=0.05, help="If best-second < delta, treat match as ambiguous/unresolved (default: 0.05)") + ap.add_argument("--mat-w-token", type=float, default=0.75, help="Weight for token/name similarity score (default: 0.75)") + ap.add_argument("--mat-w-density", type=float, default=0.25, help="Weight for density proximity score (default: 0.25)") + ap.add_argument("--mat-max-log-density-diff", type=float, default=0.0, help="Optional hard density filter in log-space (0 disables). Example 0.8 ~ within 2.2x (default: 0.0)") + ap.add_argument("--mat-compound-penalty", type=float, default=0.25, help="Penalty for matching to oxides/carbides/etc. when BOM doesn't mention them (default: 0.25)") args = ap.parse_args() @@ -584,11 +1306,30 @@ def main(): meshparam = {"do_meshing": args.mesh, "lin_defl": args.mesh_prec, "ang_defl": args.mesh_prec} + + mat_cfg = MatMatchConfig( + min_score=args.mat_min_score, + ambiguity_delta=args.mat_ambiguity_delta, + w_token=args.mat_w_token, + w_density=args.mat_w_density, + max_log_density_diff=args.mat_max_log_density_diff, + compound_penalty=args.mat_compound_penalty, + ) + 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) + code = emit_root_macro( + step_path, + out_folder, + meshparam=meshparam, + step_unit=args.step_unit, + materials_csv=args.materials_csv, + bom_mass_unit=args.bom_mass_unit, + g4_nist_json=args.g4_nist_json, + mat_cfg=mat_cfg, + ) out_macro.write_text(code) print(f"Wrote ROOT macro: {out_macro}") diff --git a/scripts/geometry/g4_nist_database/G4_NIST_DB.json b/scripts/geometry/g4_nist_database/G4_NIST_DB.json new file mode 100644 index 0000000000000..3489beb16bad3 --- /dev/null +++ b/scripts/geometry/g4_nist_database/G4_NIST_DB.json @@ -0,0 +1,7160 @@ +{ + "schema": "g4_nist_export_v1", + "count_requested": 309, + "materials": { + "G4_1,2-DICHLOROBENZENE": { + "name": "G4_1,2-DICHLOROBENZENE", + "density_g_cm3": 1.3048000000, + "radlen_cm": 20.7489144362, + "intlen_cm": 69.0775200428, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4902297089 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0274267115 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.4823435796 + } + ] + }, + "G4_1,2-DICHLOROETHANE": { + "name": "G4_1,2-DICHLOROETHANE", + "density_g_cm3": 1.2351000000, + "radlen_cm": 18.6131823209, + "intlen_cm": 77.6700314494, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.2427431829 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0407420059 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.7165148112 + } + ] + }, + "G4_A-150_TISSUE": { + "name": "G4_A-150_TISSUE", + "density_g_cm3": 1.1270000000, + "radlen_cm": 37.1439852154, + "intlen_cm": 63.7486912428, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1013268987 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.7755002245 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0350569649 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.0523159477 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.0174219826 + }, + { + "symbol": "Ca", + "Z": 20, + "A_g_mol": 40.0780316410, + "mass_fraction": 0.0183779816 + } + ] + }, + "G4_ACETONE": { + "name": "G4_ACETONE", + "density_g_cm3": 0.7899000000, + "radlen_cm": 52.2534185395, + "intlen_cm": 91.4814595809, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6203973505 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1041274661 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2754751834 + } + ] + }, + "G4_ACETYLENE": { + "name": "G4_ACETYLENE", + "density_g_cm3": 0.0010967000, + "radlen_cm": 39930.0227629710, + "intlen_cm": 66449.2307704659, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.9225773293 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0774226707 + } + ] + }, + "G4_ADENINE": { + "name": "G4_ADENINE", + "density_g_cm3": 1.3500000000, + "radlen_cm": 30.0581586935, + "intlen_cm": 58.0823779196, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4444232424 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0372959895 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.5182807681 + } + ] + }, + "G4_ADIPOSE_TISSUE_ICRP": { + "name": "G4_ADIPOSE_TISSUE_ICRP", + "density_g_cm3": 0.9500000000, + "radlen_cm": 43.3949297995, + "intlen_cm": 75.2919618730, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1140000000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5980000000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0070000000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2780000000 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0010000000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0010000000 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.0010000000 + } + ] + }, + "G4_AIR": { + "name": "G4_AIR", + "density_g_cm3": 0.0012047900, + "radlen_cm": 30392.0700501740, + "intlen_cm": 71009.5012707064, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.0001240001 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.7552677553 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2317812318 + }, + { + "symbol": "Ar", + "Z": 18, + "A_g_mol": 39.9476933511, + "mass_fraction": 0.0128270128 + } + ] + }, + "G4_ALANINE": { + "name": "G4_ALANINE", + "density_g_cm3": 1.4200000000, + "radlen_cm": 27.7725516260, + "intlen_cm": 53.1723594099, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4044321096 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0791931803 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.1572145382 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3591601720 + } + ] + }, + "G4_ALUMINUM_OXIDE": { + "name": "G4_ALUMINUM_OXIDE", + "density_g_cm3": 3.9700000000, + "radlen_cm": 7.0377543639, + "intlen_cm": 24.2683456290, + "elements": [ + { + "symbol": "Al", + "Z": 13, + "A_g_mol": 26.9815000000, + "mass_fraction": 0.5292504916 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.4707495084 + } + ] + }, + "G4_AMBER": { + "name": "G4_AMBER", + "density_g_cm3": 1.1000000000, + "radlen_cm": 39.1372915409, + "intlen_cm": 64.6512140234, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1059301059 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.7889737890 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1050961051 + } + ] + }, + "G4_AMMONIA": { + "name": "G4_AMMONIA", + "density_g_cm3": 0.0008260190, + "radlen_cm": 49481.0183957974, + "intlen_cm": 81682.0756970836, + "elements": [ + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.8224476051 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1775523949 + } + ] + }, + "G4_ANILINE": { + "name": "G4_ANILINE", + "density_g_cm3": 1.0235000000, + "radlen_cm": 41.9603888047, + "intlen_cm": 71.8321544827, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.7738313735 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0757632320 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.1504053945 + } + ] + }, + "G4_ANTHRACENE": { + "name": "G4_ANTHRACENE", + "density_g_cm3": 1.2830000000, + "radlen_cm": 33.8977543296, + "intlen_cm": 58.2256880136, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.9434470990 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0565529010 + } + ] + }, + "G4_Ac": { + "name": "G4_Ac", + "density_g_cm3": 10.0700000000, + "radlen_cm": 0.6015581907, + "intlen_cm": 21.2030538527, + "elements": [ + { + "symbol": "Ac", + "Z": 89, + "A_g_mol": 227.0280000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Ag": { + "name": "G4_Ag", + "density_g_cm3": 10.5000000000, + "radlen_cm": 0.8542919107, + "intlen_cm": 15.8675527542, + "elements": [ + { + "symbol": "Ag", + "Z": 47, + "A_g_mol": 107.8682200000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Al": { + "name": "G4_Al", + "density_g_cm3": 2.6990000000, + "radlen_cm": 8.8963176127, + "intlen_cm": 38.8944132871, + "elements": [ + { + "symbol": "Al", + "Z": 13, + "A_g_mol": 26.9815000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Am": { + "name": "G4_Am", + "density_g_cm3": 13.6700000000, + "radlen_cm": 0.4243095287, + "intlen_cm": 15.9785730103, + "elements": [ + { + "symbol": "Am", + "Z": 95, + "A_g_mol": 243.0610000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Ar": { + "name": "G4_Ar", + "density_g_cm3": 0.0016620100, + "radlen_cm": 11762.1436719519, + "intlen_cm": 71988.8135208583, + "elements": [ + { + "symbol": "Ar", + "Z": 18, + "A_g_mol": 39.9476933511, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_As": { + "name": "G4_As", + "density_g_cm3": 5.7300000000, + "radlen_cm": 2.0837957777, + "intlen_cm": 25.7503105909, + "elements": [ + { + "symbol": "As", + "Z": 33, + "A_g_mol": 74.9216000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_At": { + "name": "G4_At", + "density_g_cm3": 9.3200000000, + "radlen_cm": 0.6507992580, + "intlen_cm": 22.3211364933, + "elements": [ + { + "symbol": "At", + "Z": 85, + "A_g_mol": 209.9870000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Au": { + "name": "G4_Au", + "density_g_cm3": 19.3200000000, + "radlen_cm": 0.3344364418, + "intlen_cm": 10.5404409730, + "elements": [ + { + "symbol": "Au", + "Z": 79, + "A_g_mol": 196.9670000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_B": { + "name": "G4_B", + "density_g_cm3": 2.3700000000, + "radlen_cm": 22.2307454494, + "intlen_cm": 32.6544150557, + "elements": [ + { + "symbol": "B", + "Z": 5, + "A_g_mol": 10.8110164000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_B-100_BONE": { + "name": "G4_B-100_BONE", + "density_g_cm3": 1.4500000000, + "radlen_cm": 22.1470650946, + "intlen_cm": 55.3715324980, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0654709345 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5369444631 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0214999785 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.0320849679 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.1674108326 + }, + { + "symbol": "Ca", + "Z": 20, + "A_g_mol": 40.0780316410, + "mass_fraction": 0.1765888234 + } + ] + }, + "G4_BAKELITE": { + "name": "G4_BAKELITE", + "density_g_cm3": 1.2500000000, + "radlen_cm": 33.3909731372, + "intlen_cm": 60.5624222030, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0574410000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.7745910000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1679680000 + } + ] + }, + "G4_BARIUM_FLUORIDE": { + "name": "G4_BARIUM_FLUORIDE", + "density_g_cm3": 4.8900000000, + "radlen_cm": 2.0272211742, + "intlen_cm": 30.7133072542, + "elements": [ + { + "symbol": "Ba", + "Z": 56, + "A_g_mol": 137.3267993000, + "mass_fraction": 0.7832761810 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.2167238190 + } + ] + }, + "G4_BARIUM_SULFATE": { + "name": "G4_BARIUM_SULFATE", + "density_g_cm3": 4.5000000000, + "radlen_cm": 2.5872675325, + "intlen_cm": 29.2271363557, + "elements": [ + { + "symbol": "Ba", + "Z": 56, + "A_g_mol": 137.3267993000, + "mass_fraction": 0.5883993303 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.1373925574 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2742081123 + } + ] + }, + "G4_BENZENE": { + "name": "G4_BENZENE", + "density_g_cm3": 0.8786500000, + "radlen_cm": 49.8392488069, + "intlen_cm": 82.9395907198, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.9225773293 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0774226707 + } + ] + }, + "G4_BERYLLIUM_OXIDE": { + "name": "G4_BERYLLIUM_OXIDE", + "density_g_cm3": 3.0100000000, + "radlen_cm": 13.7223989677, + "intlen_cm": 27.2312420821, + "elements": [ + { + "symbol": "Be", + "Z": 4, + "A_g_mol": 9.0121800000, + "mass_fraction": 0.3603204378 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.6396795622 + } + ] + }, + "G4_BGO": { + "name": "G4_BGO", + "density_g_cm3": 7.1300000000, + "radlen_cm": 1.1180299951, + "intlen_cm": 22.7101337225, + "elements": [ + { + "symbol": "Bi", + "Z": 83, + "A_g_mol": 208.9800000000, + "mass_fraction": 0.6710168961 + }, + { + "symbol": "Ge", + "Z": 32, + "A_g_mol": 72.6127869100, + "mass_fraction": 0.1748650836 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1541180203 + } + ] + }, + "G4_BLOOD_ICRP": { + "name": "G4_BLOOD_ICRP", + "density_g_cm3": 1.0600000000, + "radlen_cm": 34.4916255606, + "intlen_cm": 71.4008796952, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1020000000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1100000000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0330000000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.7450000000 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0010000000 + }, + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 0.0010000000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.0030000000 + }, + { + "symbol": "K", + "Z": 19, + "A_g_mol": 39.0982931613, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "Fe", + "Z": 26, + "A_g_mol": 55.8451107980, + "mass_fraction": 0.0010000000 + } + ] + }, + "G4_BONE_COMPACT_ICRU": { + "name": "G4_BONE_COMPACT_ICRU", + "density_g_cm3": 1.8500000000, + "radlen_cm": 16.4792529255, + "intlen_cm": 44.4244163422, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0640000000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.2780000000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0270000000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.4100000000 + }, + { + "symbol": "Mg", + "Z": 12, + "A_g_mol": 24.3050157600, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 0.0700000000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "Ca", + "Z": 20, + "A_g_mol": 40.0780316410, + "mass_fraction": 0.1470000000 + } + ] + }, + "G4_BONE_CORTICAL_ICRP": { + "name": "G4_BONE_CORTICAL_ICRP", + "density_g_cm3": 1.9200000000, + "radlen_cm": 14.0594998747, + "intlen_cm": 46.4710446539, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0340000000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1550000000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0420000000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.4350000000 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0010000000 + }, + { + "symbol": "Mg", + "Z": 12, + "A_g_mol": 24.3050157600, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 0.1030000000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0030000000 + }, + { + "symbol": "Ca", + "Z": 20, + "A_g_mol": 40.0780316410, + "mass_fraction": 0.2250000000 + } + ] + }, + "G4_BORON_CARBIDE": { + "name": "G4_BORON_CARBIDE", + "density_g_cm3": 2.5200000000, + "radlen_cm": 19.8956330875, + "intlen_cm": 30.9425465737, + "elements": [ + { + "symbol": "B", + "Z": 5, + "A_g_mol": 10.8110164000, + "mass_fraction": 0.7826299987 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.2173700013 + } + ] + }, + "G4_BORON_OXIDE": { + "name": "G4_BORON_OXIDE", + "density_g_cm3": 1.8120000000, + "radlen_cm": 21.2007815469, + "intlen_cm": 46.6495645684, + "elements": [ + { + "symbol": "B", + "Z": 5, + "A_g_mol": 10.8110164000, + "mass_fraction": 0.3105712358 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.6894287642 + } + ] + }, + "G4_BRAIN_ICRP": { + "name": "G4_BRAIN_ICRP", + "density_g_cm3": 1.0400000000, + "radlen_cm": 35.4025933979, + "intlen_cm": 72.1555402096, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1070000000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1450000000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0220000000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.7120000000 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 0.0040000000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.0030000000 + }, + { + "symbol": "K", + "Z": 19, + "A_g_mol": 39.0982931613, + "mass_fraction": 0.0030000000 + } + ] + }, + "G4_BRASS": { + "name": "G4_BRASS", + "density_g_cm3": 8.5200000000, + "radlen_cm": 1.3674058172, + "intlen_cm": 16.9476882148, + "elements": [ + { + "symbol": "Cu", + "Z": 29, + "A_g_mol": 63.5456450600, + "mass_fraction": 0.5751304341 + }, + { + "symbol": "Zn", + "Z": 30, + "A_g_mol": 65.3955232900, + "mass_fraction": 0.3341218915 + }, + { + "symbol": "Pb", + "Z": 82, + "A_g_mol": 207.2170000000, + "mass_fraction": 0.0907476744 + } + ] + }, + "G4_BRONZE": { + "name": "G4_BRONZE", + "density_g_cm3": 8.8200000000, + "radlen_cm": 1.3674305230, + "intlen_cm": 16.1768594449, + "elements": [ + { + "symbol": "Cu", + "Z": 29, + "A_g_mol": 63.5456450600, + "mass_fraction": 0.8493676870 + }, + { + "symbol": "Zn", + "Z": 30, + "A_g_mol": 65.3955232900, + "mass_fraction": 0.0883914919 + }, + { + "symbol": "Pb", + "Z": 82, + "A_g_mol": 207.2170000000, + "mass_fraction": 0.0622408211 + } + ] + }, + "G4_BUTANE": { + "name": "G4_BUTANE", + "density_g_cm3": 0.0024934300, + "radlen_cm": 18139.0227601157, + "intlen_cm": 26268.9556216470, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.8265829410 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1734170590 + } + ] + }, + "G4_Ba": { + "name": "G4_Ba", + "density_g_cm3": 3.5000000000, + "radlen_cm": 2.3733248313, + "intlen_cm": 51.5923290927, + "elements": [ + { + "symbol": "Ba", + "Z": 56, + "A_g_mol": 137.3267993000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Be": { + "name": "G4_Be", + "density_g_cm3": 1.8480000000, + "radlen_cm": 35.2759751356, + "intlen_cm": 39.4132938630, + "elements": [ + { + "symbol": "Be", + "Z": 4, + "A_g_mol": 9.0121800000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Bi": { + "name": "G4_Bi", + "density_g_cm3": 9.7470000000, + "radlen_cm": 0.6453882442, + "intlen_cm": 21.3091121330, + "elements": [ + { + "symbol": "Bi", + "Z": 83, + "A_g_mol": 208.9800000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Bk": { + "name": "G4_Bk", + "density_g_cm3": 14.0000000000, + "radlen_cm": 0.4064786913, + "intlen_cm": 15.6872462983, + "elements": [ + { + "symbol": "Bk", + "Z": 97, + "A_g_mol": 247.0700000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Br": { + "name": "G4_Br", + "density_g_cm3": 0.0070721000, + "radlen_cm": 1615.1154699324, + "intlen_cm": 21316.1276450533, + "elements": [ + { + "symbol": "Br", + "Z": 35, + "A_g_mol": 79.9035138000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_C": { + "name": "G4_C", + "density_g_cm3": 2.0000000000, + "radlen_cm": 21.3485184336, + "intlen_cm": 40.0769468390, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_C-552": { + "name": "G4_C-552", + "density_g_cm3": 1.7600000000, + "radlen_cm": 21.3755217174, + "intlen_cm": 47.2178616072, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0246800247 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5016105016 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.0045270045 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.4652094652 + }, + { + "symbol": "Si", + "Z": 14, + "A_g_mol": 28.0853614555, + "mass_fraction": 0.0039730040 + } + ] + }, + "G4_CADMIUM_TELLURIDE": { + "name": "G4_CADMIUM_TELLURIDE", + "density_g_cm3": 6.2000000000, + "radlen_cm": 1.4363029675, + "intlen_cm": 27.8572965803, + "elements": [ + { + "symbol": "Cd", + "Z": 48, + "A_g_mol": 112.4114464000, + "mass_fraction": 0.4683531856 + }, + { + "symbol": "Te", + "Z": 52, + "A_g_mol": 127.6028203000, + "mass_fraction": 0.5316468144 + } + ] + }, + "G4_CADMIUM_TUNGSTATE": { + "name": "G4_CADMIUM_TUNGSTATE", + "density_g_cm3": 7.9000000000, + "radlen_cm": 1.0975367504, + "intlen_cm": 19.6990488848, + "elements": [ + { + "symbol": "Cd", + "Z": 48, + "A_g_mol": 112.4114464000, + "mass_fraction": 0.3120367899 + }, + { + "symbol": "W", + "Z": 74, + "A_g_mol": 183.8416100000, + "mass_fraction": 0.5103158768 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1776473334 + } + ] + }, + "G4_CALCIUM_CARBONATE": { + "name": "G4_CALCIUM_CARBONATE", + "density_g_cm3": 2.8000000000, + "radlen_cm": 8.5806303005, + "intlen_cm": 34.7483442364, + "elements": [ + { + "symbol": "Ca", + "Z": 20, + "A_g_mol": 40.0780316410, + "mass_fraction": 0.4004321837 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1200030341 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.4795647823 + } + ] + }, + "G4_CALCIUM_FLUORIDE": { + "name": "G4_CALCIUM_FLUORIDE", + "density_g_cm3": 3.1800000000, + "radlen_cm": 6.7515445571, + "intlen_cm": 33.1126825134, + "elements": [ + { + "symbol": "Ca", + "Z": 20, + "A_g_mol": 40.0780316410, + "mass_fraction": 0.5133284414 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.4866715586 + } + ] + }, + "G4_CALCIUM_OXIDE": { + "name": "G4_CALCIUM_OXIDE", + "density_g_cm3": 3.3000000000, + "radlen_cm": 5.7605472772, + "intlen_cm": 32.9311668963, + "elements": [ + { + "symbol": "Ca", + "Z": 20, + "A_g_mol": 40.0780316410, + "mass_fraction": 0.7146910499 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2853089501 + } + ] + }, + "G4_CALCIUM_SULFATE": { + "name": "G4_CALCIUM_SULFATE", + "density_g_cm3": 2.9600000000, + "radlen_cm": 7.6700891493, + "intlen_cm": 34.1039310668, + "elements": [ + { + "symbol": "Ca", + "Z": 20, + "A_g_mol": 40.0780316410, + "mass_fraction": 0.2943846699 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.2355348323 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.4700804977 + } + ] + }, + "G4_CALCIUM_TUNGSTATE": { + "name": "G4_CALCIUM_TUNGSTATE", + "density_g_cm3": 6.0620000000, + "radlen_cm": 1.5061183605, + "intlen_cm": 23.9389476671, + "elements": [ + { + "symbol": "Ca", + "Z": 20, + "A_g_mol": 40.0780316410, + "mass_fraction": 0.1391998505 + }, + { + "symbol": "W", + "Z": 74, + "A_g_mol": 183.8416100000, + "mass_fraction": 0.6385224915 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2222776580 + } + ] + }, + "G4_CARBON_DIOXIDE": { + "name": "G4_CARBON_DIOXIDE", + "density_g_cm3": 0.0018421200, + "radlen_cm": 19648.6261057218, + "intlen_cm": 46600.4101036886, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.2729122504 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.7270877496 + } + ] + }, + "G4_CARBON_TETRACHLORIDE": { + "name": "G4_CARBON_TETRACHLORIDE", + "density_g_cm3": 1.5940000000, + "radlen_cm": 12.6353035438, + "intlen_cm": 69.7653934353, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.0780825377 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.9219174623 + } + ] + }, + "G4_CELLULOSE_BUTYRATE": { + "name": "G4_CELLULOSE_BUTYRATE", + "density_g_cm3": 1.2000000000, + "radlen_cm": 33.1272771295, + "intlen_cm": 63.5368792659, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0671250000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5454030000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3874720000 + } + ] + }, + "G4_CELLULOSE_CELLOPHANE": { + "name": "G4_CELLULOSE_CELLOPHANE", + "density_g_cm3": 1.4200000000, + "radlen_cm": 27.2894013482, + "intlen_cm": 54.5257863934, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4444558564 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0621645440 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.4933795996 + } + ] + }, + "G4_CELLULOSE_NITRATE": { + "name": "G4_CELLULOSE_NITRATE", + "density_g_cm3": 1.4900000000, + "radlen_cm": 24.9514919127, + "intlen_cm": 54.9526743102, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0292160000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.2712960000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.1212760000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.5782120000 + } + ] + }, + "G4_CERIC_SULFATE": { + "name": "G4_CERIC_SULFATE", + "density_g_cm3": 1.0300000000, + "radlen_cm": 34.3245544164, + "intlen_cm": 73.8457863342, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1075960000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0008000000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.8749760000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0146270000 + }, + { + "symbol": "Ce", + "Z": 58, + "A_g_mol": 140.1153107700, + "mass_fraction": 0.0020010000 + } + ] + }, + "G4_CESIUM_FLUORIDE": { + "name": "G4_CESIUM_FLUORIDE", + "density_g_cm3": 4.1150000000, + "radlen_cm": 2.2265267957, + "intlen_cm": 38.9591822729, + "elements": [ + { + "symbol": "Cs", + "Z": 55, + "A_g_mol": 132.9050000000, + "mass_fraction": 0.8749310417 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.1250689583 + } + ] + }, + "G4_CESIUM_IODIDE": { + "name": "G4_CESIUM_IODIDE", + "density_g_cm3": 4.5100000000, + "radlen_cm": 1.8602879809, + "intlen_cm": 39.3059850354, + "elements": [ + { + "symbol": "Cs", + "Z": 55, + "A_g_mol": 132.9050000000, + "mass_fraction": 0.5115488686 + }, + { + "symbol": "I", + "Z": 53, + "A_g_mol": 126.9040000000, + "mass_fraction": 0.4884511314 + } + ] + }, + "G4_CHLOROBENZENE": { + "name": "G4_CHLOROBENZENE", + "density_g_cm3": 1.1058000000, + "radlen_cm": 28.2213601504, + "intlen_cm": 75.3196655676, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6402499469 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0447748021 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.3149752510 + } + ] + }, + "G4_CHLOROFORM": { + "name": "G4_CHLOROFORM", + "density_g_cm3": 1.4832000000, + "radlen_cm": 13.8426852338, + "intlen_cm": 72.9258168862, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1006123208 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0084433839 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.8909442953 + } + ] + }, + "G4_CONCRETE": { + "name": "G4_CONCRETE", + "density_g_cm3": 2.3000000000, + "radlen_cm": 11.5527147841, + "intlen_cm": 41.2115550512, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0100000000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.0010000000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.5291070000 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0160000000 + }, + { + "symbol": "Mg", + "Z": 12, + "A_g_mol": 24.3050157600, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "Al", + "Z": 13, + "A_g_mol": 26.9815000000, + "mass_fraction": 0.0338720000 + }, + { + "symbol": "Si", + "Z": 14, + "A_g_mol": 28.0853614555, + "mass_fraction": 0.3370210000 + }, + { + "symbol": "K", + "Z": 19, + "A_g_mol": 39.0982931613, + "mass_fraction": 0.0130000000 + }, + { + "symbol": "Ca", + "Z": 20, + "A_g_mol": 40.0780316410, + "mass_fraction": 0.0440000000 + }, + { + "symbol": "Fe", + "Z": 26, + "A_g_mol": 55.8451107980, + "mass_fraction": 0.0140000000 + } + ] + }, + "G4_CR39": { + "name": "G4_CR39", + "density_g_cm3": 1.3200000000, + "radlen_cm": 29.9630720578, + "intlen_cm": 57.9349397872, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0661505040 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5255046077 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.4083448883 + } + ] + }, + "G4_CYCLOHEXANE": { + "name": "G4_CYCLOHEXANE", + "density_g_cm3": 0.7790000000, + "radlen_cm": 57.4759804747, + "intlen_cm": 86.7995841505, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.8562817123 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1437182877 + } + ] + }, + "G4_CYTOSINE": { + "name": "G4_CYTOSINE", + "density_g_cm3": 1.3000000000, + "radlen_cm": 30.7578506265, + "intlen_cm": 60.0651401038, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0453609120 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4324206194 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.3782125956 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1440058730 + } + ] + }, + "G4_Ca": { + "name": "G4_Ca", + "density_g_cm3": 1.5500000000, + "radlen_cm": 10.4151095198, + "intlen_cm": 77.2749101845, + "elements": [ + { + "symbol": "Ca", + "Z": 20, + "A_g_mol": 40.0780316410, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Cd": { + "name": "G4_Cd", + "density_g_cm3": 8.6500000000, + "radlen_cm": 1.0399387577, + "intlen_cm": 19.5278973588, + "elements": [ + { + "symbol": "Cd", + "Z": 48, + "A_g_mol": 112.4114464000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Ce": { + "name": "G4_Ce", + "density_g_cm3": 6.6570000000, + "radlen_cm": 1.1950616986, + "intlen_cm": 27.3076746781, + "elements": [ + { + "symbol": "Ce", + "Z": 58, + "A_g_mol": 140.1153107700, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Cf": { + "name": "G4_Cf", + "density_g_cm3": 10.0000000000, + "radlen_cm": 0.5683275438, + "intlen_cm": 22.0803245446, + "elements": [ + { + "symbol": "Cf", + "Z": 98, + "A_g_mol": 251.0800000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Cl": { + "name": "G4_Cl", + "density_g_cm3": 0.0029947300, + "radlen_cm": 6437.3408608729, + "intlen_cm": 38393.6729355327, + "elements": [ + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Cm": { + "name": "G4_Cm", + "density_g_cm3": 13.5100000000, + "radlen_cm": 0.4287060756, + "intlen_cm": 16.2562137806, + "elements": [ + { + "symbol": "Cm", + "Z": 96, + "A_g_mol": 247.0700000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Co": { + "name": "G4_Co", + "density_g_cm3": 8.9000000000, + "radlen_cm": 1.5300516989, + "intlen_cm": 15.3037576765, + "elements": [ + { + "symbol": "Co", + "Z": 27, + "A_g_mol": 58.9332000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Cr": { + "name": "G4_Cr", + "density_g_cm3": 7.1800000000, + "radlen_cm": 2.0814040144, + "intlen_cm": 18.1942423649, + "elements": [ + { + "symbol": "Cr", + "Z": 24, + "A_g_mol": 51.9961301370, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Cs": { + "name": "G4_Cs", + "density_g_cm3": 1.8730000000, + "radlen_cm": 4.4342020259, + "intlen_cm": 95.3624518046, + "elements": [ + { + "symbol": "Cs", + "Z": 55, + "A_g_mol": 132.9050000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Cu": { + "name": "G4_Cu", + "density_g_cm3": 8.9600000000, + "radlen_cm": 1.4355780238, + "intlen_cm": 15.5879379043, + "elements": [ + { + "symbol": "Cu", + "Z": 29, + "A_g_mol": 63.5456450600, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_DACRON": { + "name": "G4_DACRON", + "density_g_cm3": 1.4000000000, + "radlen_cm": 28.5364043256, + "intlen_cm": 55.9231513594, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6250108323 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0419607171 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3330284506 + } + ] + }, + "G4_DEOXYRIBOSE": { + "name": "G4_DEOXYRIBOSE", + "density_g_cm3": 1.5000000000, + "radlen_cm": 26.0277793263, + "intlen_cm": 50.7245404788, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0751461910 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4477252695 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.4771285395 + } + ] + }, + "G4_DICHLORODIETHYL_ETHER": { + "name": "G4_DICHLORODIETHYL_ETHER", + "density_g_cm3": 1.2199000000, + "radlen_cm": 21.7159252335, + "intlen_cm": 72.0157030853, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.3359387920 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0563839532 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1118752364 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.4958020185 + } + ] + }, + "G4_DIETHYL_ETHER": { + "name": "G4_DIETHYL_ETHER", + "density_g_cm3": 0.7137800000, + "radlen_cm": 59.2587073492, + "intlen_cm": 97.1612602951, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6481626481 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1359844906 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2158528613 + } + ] + }, + "G4_DIMETHYL_SULFOXIDE": { + "name": "G4_DIMETHYL_SULFOXIDE", + "density_g_cm3": 1.1014000000, + "radlen_cm": 25.6056153635, + "intlen_cm": 75.2873092978, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.3074369871 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0774003171 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2047669780 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.4103957178 + } + ] + }, + "G4_DNA_ADENINE": { + "name": "G4_DNA_ADENINE", + "density_g_cm3": 1.0000000000, + "radlen_cm": 40.4701354302, + "intlen_cm": 79.1489834113, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0300610227 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4477631967 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.5221757806 + } + ] + }, + "G4_DNA_CYTOSINE": { + "name": "G4_DNA_CYTOSINE", + "density_g_cm3": 1.0000000000, + "radlen_cm": 39.8517602235, + "intlen_cm": 78.9747286021, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0366209616 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4363795341 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.3816752229 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1453242814 + } + ] + }, + "G4_DNA_DEOXYRIBOSE": { + "name": "G4_DNA_DEOXYRIBOSE", + "density_g_cm3": 1.0000000000, + "radlen_cm": 41.8531490771, + "intlen_cm": 73.4025938940, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0848959119 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.7225923706 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1925117175 + } + ] + }, + "G4_DNA_GUANINE": { + "name": "G4_DNA_GUANINE", + "density_g_cm3": 1.0000000000, + "radlen_cm": 39.6999832548, + "intlen_cm": 80.0236241154, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0268571707 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4000413663 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.4665231851 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1065782779 + } + ] + }, + "G4_DNA_PHOSPHATE": { + "name": "G4_DNA_PHOSPHATE", + "density_g_cm3": 1.0000000000, + "radlen_cm": 28.5212126943, + "intlen_cm": 94.2697781537, + "elements": [ + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 0.3261383165 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.6738616835 + } + ] + }, + "G4_DNA_THYMINE": { + "name": "G4_DNA_THYMINE", + "density_g_cm3": 1.0000000000, + "radlen_cm": 39.6095818095, + "intlen_cm": 78.7777563069, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0402835649 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4800235304 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.2239189496 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2557739551 + } + ] + }, + "G4_DNA_URACIL": { + "name": "G4_DNA_URACIL", + "density_g_cm3": 1.0000000000, + "radlen_cm": 39.0409633092, + "intlen_cm": 80.4546879807, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0272222464 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4325111686 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.2521945291 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2880720559 + } + ] + }, + "G4_Dy": { + "name": "G4_Dy", + "density_g_cm3": 8.5500000000, + "radlen_cm": 0.8561401252, + "intlen_cm": 22.3383201875, + "elements": [ + { + "symbol": "Dy", + "Z": 66, + "A_g_mol": 162.4971100000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_ETHANE": { + "name": "G4_ETHANE", + "density_g_cm3": 0.0012532400, + "radlen_cm": 36434.2860647397, + "intlen_cm": 50781.0189376312, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.7988752227 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.2011247773 + } + ] + }, + "G4_ETHYLENE": { + "name": "G4_ETHYLENE", + "density_g_cm3": 0.0011749700, + "radlen_cm": 38106.3250889615, + "intlen_cm": 57547.7467962794, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.8562817123 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1437182877 + } + ] + }, + "G4_ETHYL_ALCOHOL": { + "name": "G4_ETHYL_ALCOHOL", + "density_g_cm3": 0.7893000000, + "radlen_cm": 51.8429704156, + "intlen_cm": 89.2594985493, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5214293661 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1312750254 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3472956085 + } + ] + }, + "G4_ETHYL_CELLULOSE": { + "name": "G4_ETHYL_CELLULOSE", + "density_g_cm3": 1.1300000000, + "radlen_cm": 35.9450301822, + "intlen_cm": 65.2831880981, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0900270000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5851820000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3247910000 + } + ] + }, + "G4_EYE_LENS_ICRP": { + "name": "G4_EYE_LENS_ICRP", + "density_g_cm3": 1.0700000000, + "radlen_cm": 34.9413412839, + "intlen_cm": 70.6360062417, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0960000000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1950000000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0570000000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.6460000000 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0010000000 + }, + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 0.0010000000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0030000000 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.0010000000 + } + ] + }, + "G4_Er": { + "name": "G4_Er", + "density_g_cm3": 9.0660000000, + "radlen_cm": 0.7880939310, + "intlen_cm": 21.2705940900, + "elements": [ + { + "symbol": "Er", + "Z": 68, + "A_g_mol": 167.2560232000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Eu": { + "name": "G4_Eu", + "density_g_cm3": 5.2430000000, + "radlen_cm": 1.4186770984, + "intlen_cm": 35.6234054321, + "elements": [ + { + "symbol": "Eu", + "Z": 63, + "A_g_mol": 151.9643219000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_F": { + "name": "G4_F", + "density_g_cm3": 0.0015802900, + "radlen_cm": 20838.1744350084, + "intlen_cm": 59097.6615288435, + "elements": [ + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_FERRIC_OXIDE": { + "name": "G4_FERRIC_OXIDE", + "density_g_cm3": 5.2000000000, + "radlen_cm": 3.2418173343, + "intlen_cm": 22.2675201920, + "elements": [ + { + "symbol": "Fe", + "Z": 26, + "A_g_mol": 55.8451107980, + "mass_fraction": 0.6994260486 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3005739514 + } + ] + }, + "G4_FERROBORIDE": { + "name": "G4_FERROBORIDE", + "density_g_cm3": 7.1500000000, + "radlen_cm": 2.1983578257, + "intlen_cm": 16.7331909572, + "elements": [ + { + "symbol": "Fe", + "Z": 26, + "A_g_mol": 55.8451107980, + "mass_fraction": 0.8378091129 + }, + { + "symbol": "B", + "Z": 5, + "A_g_mol": 10.8110164000, + "mass_fraction": 0.1621908871 + } + ] + }, + "G4_FERROUS_OXIDE": { + "name": "G4_FERROUS_OXIDE", + "density_g_cm3": 5.7000000000, + "radlen_cm": 2.7992227484, + "intlen_cm": 21.0475937407, + "elements": [ + { + "symbol": "Fe", + "Z": 26, + "A_g_mol": 55.8451107980, + "mass_fraction": 0.7773052893 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2226947107 + } + ] + }, + "G4_FERROUS_SULFATE": { + "name": "G4_FERROUS_SULFATE", + "density_g_cm3": 1.0240000000, + "radlen_cm": 34.8125553802, + "intlen_cm": 74.1303341800, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1082590000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0000270000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.8786360000 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0000220000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0129680000 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.0000340000 + }, + { + "symbol": "Fe", + "Z": 26, + "A_g_mol": 55.8451107980, + "mass_fraction": 0.0000540000 + } + ] + }, + "G4_FREON-12": { + "name": "G4_FREON-12", + "density_g_cm3": 1.1200000000, + "radlen_cm": 21.1136459442, + "intlen_cm": 92.0056500582, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.0993350000 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.3142470000 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.5864180000 + } + ] + }, + "G4_FREON-12B2": { + "name": "G4_FREON-12B2", + "density_g_cm3": 1.8000000000, + "radlen_cm": 7.5563082797, + "intlen_cm": 72.0960738549, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.0572450000 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.1810960000 + }, + { + "symbol": "Br", + "Z": 35, + "A_g_mol": 79.9035138000, + "mass_fraction": 0.7616590000 + } + ] + }, + "G4_FREON-13": { + "name": "G4_FREON-13", + "density_g_cm3": 0.9500000000, + "radlen_cm": 28.5519879814, + "intlen_cm": 102.9101489011, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1149828850 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.5456214544 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.3393956606 + } + ] + }, + "G4_FREON-13B1": { + "name": "G4_FREON-13B1", + "density_g_cm3": 1.5000000000, + "radlen_cm": 11.0211416256, + "intlen_cm": 76.9456788799, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.0806579862 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.3827507249 + }, + { + "symbol": "Br", + "Z": 35, + "A_g_mol": 79.9035138000, + "mass_fraction": 0.5365912889 + } + ] + }, + "G4_FREON-13I1": { + "name": "G4_FREON-13I1", + "density_g_cm3": 1.8000000000, + "radlen_cm": 6.4111664137, + "intlen_cm": 73.4578642805, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.0613090000 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.2909240000 + }, + { + "symbol": "I", + "Z": 53, + "A_g_mol": 126.9040000000, + "mass_fraction": 0.6477670000 + } + ] + }, + "G4_Fe": { + "name": "G4_Fe", + "density_g_cm3": 7.8740000000, + "radlen_cm": 1.7574934651, + "intlen_cm": 16.9903002759, + "elements": [ + { + "symbol": "Fe", + "Z": 26, + "A_g_mol": 55.8451107980, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Fr": { + "name": "G4_Fr", + "density_g_cm3": 1.0000000000, + "radlen_cm": 6.1882573776, + "intlen_cm": 212.2508067736, + "elements": [ + { + "symbol": "Fr", + "Z": 87, + "A_g_mol": 223.0200000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_GADOLINIUM_OXYSULFIDE": { + "name": "G4_GADOLINIUM_OXYSULFIDE", + "density_g_cm3": 7.4400000000, + "radlen_cm": 1.1407035079, + "intlen_cm": 21.9702398250, + "elements": [ + { + "symbol": "Gd", + "Z": 64, + "A_g_mol": 157.2521250000, + "mass_fraction": 0.8307709545 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.0845255913 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0847034542 + } + ] + }, + "G4_GALLIUM_ARSENIDE": { + "name": "G4_GALLIUM_ARSENIDE", + "density_g_cm3": 5.3100000000, + "radlen_cm": 2.2959768793, + "intlen_cm": 27.4658727793, + "elements": [ + { + "symbol": "Ga", + "Z": 31, + "A_g_mol": 69.7230809720, + "mass_fraction": 0.4820300374 + }, + { + "symbol": "As", + "Z": 33, + "A_g_mol": 74.9216000000, + "mass_fraction": 0.5179699626 + } + ] + }, + "G4_GEL_PHOTO_EMULSION": { + "name": "G4_GEL_PHOTO_EMULSION", + "density_g_cm3": 1.2914000000, + "radlen_cm": 30.2058086009, + "intlen_cm": 58.4748172458, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0811800000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4160600000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.1112400000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3806400000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0108800000 + } + ] + }, + "G4_GLASS_LEAD": { + "name": "G4_GLASS_LEAD", + "density_g_cm3": 6.2200000000, + "radlen_cm": 1.2655477423, + "intlen_cm": 25.7388388952, + "elements": [ + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1564530000 + }, + { + "symbol": "Si", + "Z": 14, + "A_g_mol": 28.0853614555, + "mass_fraction": 0.0808660000 + }, + { + "symbol": "Ti", + "Z": 22, + "A_g_mol": 47.8667173300, + "mass_fraction": 0.0080920000 + }, + { + "symbol": "As", + "Z": 33, + "A_g_mol": 74.9216000000, + "mass_fraction": 0.0026510000 + }, + { + "symbol": "Pb", + "Z": 82, + "A_g_mol": 207.2170000000, + "mass_fraction": 0.7519380000 + } + ] + }, + "G4_GLASS_PLATE": { + "name": "G4_GLASS_PLATE", + "density_g_cm3": 2.4000000000, + "radlen_cm": 10.6921640656, + "intlen_cm": 40.6857797379, + "elements": [ + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.4598004598 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0964410964 + }, + { + "symbol": "Si", + "Z": 14, + "A_g_mol": 28.0853614555, + "mass_fraction": 0.3365533366 + }, + { + "symbol": "Ca", + "Z": 20, + "A_g_mol": 40.0780316410, + "mass_fraction": 0.1072051072 + } + ] + }, + "G4_GLUTAMINE": { + "name": "G4_GLUTAMINE", + "density_g_cm3": 1.4600000000, + "radlen_cm": 27.0121584298, + "intlen_cm": 52.3124063449, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4109190507 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0689686368 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.1916834413 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3284288712 + } + ] + }, + "G4_GLYCEROL": { + "name": "G4_GLYCEROL", + "density_g_cm3": 1.2613000000, + "radlen_cm": 30.7600170394, + "intlen_cm": 59.6449295333, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.3912550846 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0875576500 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.5211872654 + } + ] + }, + "G4_GRAPHITE": { + "name": "G4_GRAPHITE", + "density_g_cm3": 2.2100000000, + "radlen_cm": 19.3199261842, + "intlen_cm": 36.2687301711, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_GRAPHITE_POROUS": { + "name": "G4_GRAPHITE_POROUS", + "density_g_cm3": 1.7000000000, + "radlen_cm": 25.1159040395, + "intlen_cm": 47.1493492224, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_GUANINE": { + "name": "G4_GUANINE", + "density_g_cm3": 1.5800000000, + "radlen_cm": 25.1887769514, + "intlen_cm": 50.2170220603, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.3973732857 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0333475581 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.4634117033 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1058674529 + } + ] + }, + "G4_GYPSUM": { + "name": "G4_GYPSUM", + "density_g_cm3": 2.3200000000, + "radlen_cm": 10.6092208180, + "intlen_cm": 40.6273547785, + "elements": [ + { + "symbol": "Ca", + "Z": 20, + "A_g_mol": 40.0780316410, + "mass_fraction": 0.2327786930 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.1862443803 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.5575598954 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0234170314 + } + ] + }, + "G4_Ga": { + "name": "G4_Ga", + "density_g_cm3": 5.9040000000, + "radlen_cm": 2.1127975858, + "intlen_cm": 24.3994809094, + "elements": [ + { + "symbol": "Ga", + "Z": 31, + "A_g_mol": 69.7230809720, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Galactic": { + "name": "G4_Galactic", + "density_g_cm3": 0.0000000000, + "radlen_cm": 630435090422683690204135424.0000000000, + "intlen_cm": 350000028082484913811488768.0000000000, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Gd": { + "name": "G4_Gd", + "density_g_cm3": 7.9004000000, + "radlen_cm": 0.9472083827, + "intlen_cm": 23.9121066950, + "elements": [ + { + "symbol": "Gd", + "Z": 64, + "A_g_mol": 157.2521250000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Ge": { + "name": "G4_Ge", + "density_g_cm3": 5.3230000000, + "radlen_cm": 2.3012998808, + "intlen_cm": 27.4314847558, + "elements": [ + { + "symbol": "Ge", + "Z": 32, + "A_g_mol": 72.6127869100, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_H": { + "name": "G4_H", + "density_g_cm3": 0.0000837480, + "radlen_cm": 752776.2936699188, + "intlen_cm": 417920.4614826443, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_He": { + "name": "G4_He", + "density_g_cm3": 0.0001663220, + "radlen_cm": 567113.1420929121, + "intlen_cm": 334118.5985088379, + "elements": [ + { + "symbol": "He", + "Z": 2, + "A_g_mol": 4.0026425851, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Hf": { + "name": "G4_Hf", + "density_g_cm3": 13.3100000000, + "radlen_cm": 0.5177172521, + "intlen_cm": 14.8055339056, + "elements": [ + { + "symbol": "Hf", + "Z": 72, + "A_g_mol": 178.4851746000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Hg": { + "name": "G4_Hg", + "density_g_cm3": 13.5460000000, + "radlen_cm": 0.4752411427, + "intlen_cm": 15.1251608352, + "elements": [ + { + "symbol": "Hg", + "Z": 80, + "A_g_mol": 200.5991002000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Ho": { + "name": "G4_Ho", + "density_g_cm3": 8.7950000000, + "radlen_cm": 0.8224469594, + "intlen_cm": 21.8238878765, + "elements": [ + { + "symbol": "Ho", + "Z": 67, + "A_g_mol": 164.9300000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_I": { + "name": "G4_I", + "density_g_cm3": 4.9300000000, + "radlen_cm": 1.7201640735, + "intlen_cm": 35.6762827299, + "elements": [ + { + "symbol": "I", + "Z": 53, + "A_g_mol": 126.9040000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_In": { + "name": "G4_In", + "density_g_cm3": 7.3100000000, + "radlen_cm": 1.2105450732, + "intlen_cm": 23.2713161873, + "elements": [ + { + "symbol": "In", + "Z": 49, + "A_g_mol": 114.8182000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Ir": { + "name": "G4_Ir", + "density_g_cm3": 22.4200000000, + "radlen_cm": 0.2941415950, + "intlen_cm": 9.0093994089, + "elements": [ + { + "symbol": "Ir", + "Z": 77, + "A_g_mol": 192.2162540000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_K": { + "name": "G4_K", + "density_g_cm3": 0.8620000000, + "radlen_cm": 20.0870675609, + "intlen_cm": 137.8097927497, + "elements": [ + { + "symbol": "K", + "Z": 19, + "A_g_mol": 39.0982931613, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_KAPTON": { + "name": "G4_KAPTON", + "density_g_cm3": 1.4200000000, + "radlen_cm": 28.5747754063, + "intlen_cm": 55.8169092105, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6911278143 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0263633782 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0732713202 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2092374873 + } + ] + }, + "G4_KEVLAR": { + "name": "G4_KEVLAR", + "density_g_cm3": 1.4400000000, + "radlen_cm": 28.6728455313, + "intlen_cm": 53.7041703894, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.7057961409 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0423074270 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1343120694 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.1175843627 + } + ] + }, + "G4_Kr": { + "name": "G4_Kr", + "density_g_cm3": 0.0034783200, + "radlen_cm": 3269.4392743928, + "intlen_cm": 44033.0436533239, + "elements": [ + { + "symbol": "Kr", + "Z": 36, + "A_g_mol": 83.7993175100, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_LANTHANUM_OXYBROMIDE": { + "name": "G4_LANTHANUM_OXYBROMIDE", + "density_g_cm3": 6.2800000000, + "radlen_cm": 1.5241532522, + "intlen_cm": 25.3014351184, + "elements": [ + { + "symbol": "La", + "Z": 57, + "A_g_mol": 138.9051009000, + "mass_fraction": 0.5915688472 + }, + { + "symbol": "Br", + "Z": 35, + "A_g_mol": 79.9035138000, + "mass_fraction": 0.3402929715 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.0681381812 + } + ] + }, + "G4_LANTHANUM_OXYSULFIDE": { + "name": "G4_LANTHANUM_OXYSULFIDE", + "density_g_cm3": 5.8600000000, + "radlen_cm": 1.5889258344, + "intlen_cm": 26.7145523078, + "elements": [ + { + "symbol": "La", + "Z": 57, + "A_g_mol": 138.9051009000, + "mass_fraction": 0.8126073070 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.0935978698 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0937948232 + } + ] + }, + "G4_LEAD_OXIDE": { + "name": "G4_LEAD_OXIDE", + "density_g_cm3": 9.5300000000, + "radlen_cm": 0.7098556946, + "intlen_cm": 19.8173808964, + "elements": [ + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.0716820000 + }, + { + "symbol": "Pb", + "Z": 82, + "A_g_mol": 207.2170000000, + "mass_fraction": 0.9283180000 + } + ] + }, + "G4_LITHIUM_AMIDE": { + "name": "G4_LITHIUM_AMIDE", + "density_g_cm3": 1.1780000000, + "radlen_cm": 40.2298585529, + "intlen_cm": 59.5078880837, + "elements": [ + { + "symbol": "Li", + "Z": 3, + "A_g_mol": 6.9400332080, + "mass_fraction": 0.3022309285 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.6099796156 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0877894559 + } + ] + }, + "G4_LITHIUM_CARBONATE": { + "name": "G4_LITHIUM_CARBONATE", + "density_g_cm3": 2.1100000000, + "radlen_cm": 18.9197918579, + "intlen_cm": 38.8235211926, + "elements": [ + { + "symbol": "Li", + "Z": 3, + "A_g_mol": 6.9400332080, + "mass_fraction": 0.1878503065 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1625511321 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.6495985614 + } + ] + }, + "G4_LITHIUM_FLUORIDE": { + "name": "G4_LITHIUM_FLUORIDE", + "density_g_cm3": 2.6350000000, + "radlen_cm": 14.8973607769, + "intlen_cm": 32.0247499094, + "elements": [ + { + "symbol": "Li", + "Z": 3, + "A_g_mol": 6.9400332080, + "mass_fraction": 0.2675579189 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.7324420811 + } + ] + }, + "G4_LITHIUM_HYDRIDE": { + "name": "G4_LITHIUM_HYDRIDE", + "density_g_cm3": 0.8200000000, + "radlen_cm": 97.0850620364, + "intlen_cm": 73.0132624194, + "elements": [ + { + "symbol": "Li", + "Z": 3, + "A_g_mol": 6.9400332080, + "mass_fraction": 0.8731826806 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1268173194 + } + ] + }, + "G4_LITHIUM_IODIDE": { + "name": "G4_LITHIUM_IODIDE", + "density_g_cm3": 3.4940000000, + "radlen_cm": 2.5456045592, + "intlen_cm": 46.4058302608, + "elements": [ + { + "symbol": "Li", + "Z": 3, + "A_g_mol": 6.9400332080, + "mass_fraction": 0.0518516443 + }, + { + "symbol": "I", + "Z": 53, + "A_g_mol": 126.9040000000, + "mass_fraction": 0.9481483557 + } + ] + }, + "G4_LITHIUM_OXIDE": { + "name": "G4_LITHIUM_OXIDE", + "density_g_cm3": 2.0130000000, + "radlen_cm": 23.3753863602, + "intlen_cm": 38.1260963105, + "elements": [ + { + "symbol": "Li", + "Z": 3, + "A_g_mol": 6.9400332080, + "mass_fraction": 0.4645354330 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.5354645670 + } + ] + }, + "G4_LITHIUM_TETRABORATE": { + "name": "G4_LITHIUM_TETRABORATE", + "density_g_cm3": 2.4400000000, + "radlen_cm": 16.2719894171, + "intlen_cm": 34.0334121576, + "elements": [ + { + "symbol": "Li", + "Z": 3, + "A_g_mol": 6.9400332080, + "mass_fraction": 0.0820723599 + }, + { + "symbol": "B", + "Z": 5, + "A_g_mol": 10.8110164000, + "mass_fraction": 0.2557006868 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.6622269533 + } + ] + }, + "G4_LUCITE": { + "name": "G4_LUCITE", + "density_g_cm3": 1.1900000000, + "radlen_cm": 34.0748652335, + "intlen_cm": 62.6704780225, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0805380000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5998480000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3196140000 + } + ] + }, + "G4_LUNG_ICRP": { + "name": "G4_LUNG_ICRP", + "density_g_cm3": 1.0400000000, + "radlen_cm": 35.0156133645, + "intlen_cm": 72.6726342239, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1050000000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.0830000000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0230000000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.7790000000 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 0.0010000000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.0030000000 + }, + { + "symbol": "K", + "Z": 19, + "A_g_mol": 39.0982931613, + "mass_fraction": 0.0020000000 + } + ] + }, + "G4_La": { + "name": "G4_La", + "density_g_cm3": 6.1540000000, + "radlen_cm": 1.3223836276, + "intlen_cm": 29.4543867747, + "elements": [ + { + "symbol": "La", + "Z": 57, + "A_g_mol": 138.9051009000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Li": { + "name": "G4_Li", + "density_g_cm3": 0.5340000000, + "radlen_cm": 154.9972904774, + "intlen_cm": 125.0203388568, + "elements": [ + { + "symbol": "Li", + "Z": 3, + "A_g_mol": 6.9400332080, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Lu": { + "name": "G4_Lu", + "density_g_cm3": 9.8400000000, + "radlen_cm": 0.7036514007, + "intlen_cm": 19.8941317320, + "elements": [ + { + "symbol": "Lu", + "Z": 71, + "A_g_mol": 174.9669518000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_M3_WAX": { + "name": "G4_M3_WAX", + "density_g_cm3": 1.0500000000, + "radlen_cm": 37.4523271935, + "intlen_cm": 68.7782440198, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1143181143 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6558236558 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.0921830922 + }, + { + "symbol": "Mg", + "Z": 12, + "A_g_mol": 24.3050157600, + "mass_fraction": 0.1347921348 + }, + { + "symbol": "Ca", + "Z": 20, + "A_g_mol": 40.0780316410, + "mass_fraction": 0.0028830029 + } + ] + }, + "G4_MAGNESIUM_CARBONATE": { + "name": "G4_MAGNESIUM_CARBONATE", + "density_g_cm3": 2.9580000000, + "radlen_cm": 10.7392076017, + "intlen_cm": 30.5238324080, + "elements": [ + { + "symbol": "Mg", + "Z": 12, + "A_g_mol": 24.3050157600, + "mass_fraction": 0.2882681150 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1424525855 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.5692792995 + } + ] + }, + "G4_MAGNESIUM_FLUORIDE": { + "name": "G4_MAGNESIUM_FLUORIDE", + "density_g_cm3": 3.0000000000, + "radlen_cm": 9.7736131065, + "intlen_cm": 32.1181946756, + "elements": [ + { + "symbol": "Mg", + "Z": 12, + "A_g_mol": 24.3050157600, + "mass_fraction": 0.3901172937 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.6098827063 + } + ] + }, + "G4_MAGNESIUM_OXIDE": { + "name": "G4_MAGNESIUM_OXIDE", + "density_g_cm3": 3.5800000000, + "radlen_cm": 7.8275822876, + "intlen_cm": 26.7323051305, + "elements": [ + { + "symbol": "Mg", + "Z": 12, + "A_g_mol": 24.3050157600, + "mass_fraction": 0.6030361955 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3969638045 + } + ] + }, + "G4_MAGNESIUM_TETRABORATE": { + "name": "G4_MAGNESIUM_TETRABORATE", + "density_g_cm3": 2.5300000000, + "radlen_cm": 14.0171151884, + "intlen_cm": 34.3098315005, + "elements": [ + { + "symbol": "Mg", + "Z": 12, + "A_g_mol": 24.3050157600, + "mass_fraction": 0.1353701908 + }, + { + "symbol": "B", + "Z": 5, + "A_g_mol": 10.8110164000, + "mass_fraction": 0.2408538825 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.6237759267 + } + ] + }, + "G4_MERCURIC_IODIDE": { + "name": "G4_MERCURIC_IODIDE", + "density_g_cm3": 6.3600000000, + "radlen_cm": 1.1695626925, + "intlen_cm": 29.4979745172, + "elements": [ + { + "symbol": "Hg", + "Z": 80, + "A_g_mol": 200.5991002000, + "mass_fraction": 0.4414523895 + }, + { + "symbol": "I", + "Z": 53, + "A_g_mol": 126.9040000000, + "mass_fraction": 0.5585476105 + } + ] + }, + "G4_METHANE": { + "name": "G4_METHANE", + "density_g_cm3": 0.0006671510, + "radlen_cm": 69648.1895684307, + "intlen_cm": 90727.2666787782, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.7486823647 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.2513176353 + } + ] + }, + "G4_METHANOL": { + "name": "G4_METHANOL", + "density_g_cm3": 0.7914000000, + "radlen_cm": 49.8277776201, + "intlen_cm": 90.6875121309, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.3748448189 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1258278783 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.4993273028 + } + ] + }, + "G4_MIX_D_WAX": { + "name": "G4_MIX_D_WAX", + "density_g_cm3": 0.9900000000, + "radlen_cm": 42.4388764028, + "intlen_cm": 70.0170389774, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1340400000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.7779600000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.0350200000 + }, + { + "symbol": "Mg", + "Z": 12, + "A_g_mol": 24.3050157600, + "mass_fraction": 0.0385940000 + }, + { + "symbol": "Ti", + "Z": 22, + "A_g_mol": 47.8667173300, + "mass_fraction": 0.0143860000 + } + ] + }, + "G4_MS20_TISSUE": { + "name": "G4_MS20_TISSUE", + "density_g_cm3": 1.0000000000, + "radlen_cm": 38.2901787990, + "intlen_cm": 75.6659085615, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0811920000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5834420000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0177980000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1863810000 + }, + { + "symbol": "Mg", + "Z": 12, + "A_g_mol": 24.3050157600, + "mass_fraction": 0.1302870000 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.0009000000 + } + ] + }, + "G4_MUSCLE_SKELETAL_ICRP": { + "name": "G4_MUSCLE_SKELETAL_ICRP", + "density_g_cm3": 1.0500000000, + "radlen_cm": 35.0573564270, + "intlen_cm": 71.8808850701, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1020000000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1430000000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0340000000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.7100000000 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0010000000 + }, + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0030000000 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.0010000000 + }, + { + "symbol": "K", + "Z": 19, + "A_g_mol": 39.0982931613, + "mass_fraction": 0.0040000000 + } + ] + }, + "G4_MUSCLE_STRIATED_ICRU": { + "name": "G4_MUSCLE_STRIATED_ICRU", + "density_g_cm3": 1.0400000000, + "radlen_cm": 35.2882455673, + "intlen_cm": 72.6659485329, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1021021021 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1231231231 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0350350350 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.7297297297 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0010010010 + }, + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 0.0020020020 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0040040040 + }, + { + "symbol": "K", + "Z": 19, + "A_g_mol": 39.0982931613, + "mass_fraction": 0.0030030030 + } + ] + }, + "G4_MUSCLE_WITHOUT_SUCROSE": { + "name": "G4_MUSCLE_WITHOUT_SUCROSE", + "density_g_cm3": 1.0700000000, + "radlen_cm": 34.5507134115, + "intlen_cm": 70.5306459594, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1019690000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1200580000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0354510000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.7425220000 + } + ] + }, + "G4_MUSCLE_WITH_SUCROSE": { + "name": "G4_MUSCLE_WITH_SUCROSE", + "density_g_cm3": 1.1100000000, + "radlen_cm": 33.5030358691, + "intlen_cm": 68.1084691034, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0982340982 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1562141562 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0354510355 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.7101007101 + } + ] + }, + "G4_MYLAR": { + "name": "G4_MYLAR", + "density_g_cm3": 1.4000000000, + "radlen_cm": 28.5364043256, + "intlen_cm": 55.9231513594, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6250108323 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0419607171 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3330284506 + } + ] + }, + "G4_Mg": { + "name": "G4_Mg", + "density_g_cm3": 1.7400000000, + "radlen_cm": 14.3859171086, + "intlen_cm": 58.2663034870, + "elements": [ + { + "symbol": "Mg", + "Z": 12, + "A_g_mol": 24.3050157600, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Mn": { + "name": "G4_Mn", + "density_g_cm3": 7.4400000000, + "radlen_cm": 1.9677221865, + "intlen_cm": 17.8835098670, + "elements": [ + { + "symbol": "Mn", + "Z": 25, + "A_g_mol": 54.9380000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Mo": { + "name": "G4_Mo", + "density_g_cm3": 10.2200000000, + "radlen_cm": 0.9591074077, + "intlen_cm": 15.6772760730, + "elements": [ + { + "symbol": "Mo", + "Z": 42, + "A_g_mol": 95.9312864600, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_N": { + "name": "G4_N", + "density_g_cm3": 0.0011652000, + "radlen_cm": 32602.2350168044, + "intlen_cm": 72406.9506998844, + "elements": [ + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_N,N-DIMETHYL_FORMAMIDE": { + "name": "G4_N,N-DIMETHYL_FORMAMIDE", + "density_g_cm3": 0.9487000000, + "radlen_cm": 42.9986906208, + "intlen_cm": 77.1577428431, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4929574510 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0965276183 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.1916269163 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2188880144 + } + ] + }, + "G4_N-BUTYL_ALCOHOL": { + "name": "G4_N-BUTYL_ALCOHOL", + "density_g_cm3": 0.8098000000, + "radlen_cm": 52.2322550404, + "intlen_cm": 85.6406080185, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6481626481 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1359844906 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2158528613 + } + ] + }, + "G4_N-HEPTANE": { + "name": "G4_N-HEPTANE", + "density_g_cm3": 0.6837600000, + "radlen_cm": 65.8657382833, + "intlen_cm": 97.0698845209, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.8390549213 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1609450787 + } + ] + }, + "G4_N-HEXANE": { + "name": "G4_N-HEXANE", + "density_g_cm3": 0.6603000000, + "radlen_cm": 68.2710686267, + "intlen_cm": 100.2185093356, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.8362509531 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1637490469 + } + ] + }, + "G4_N-PENTANE": { + "name": "G4_N-PENTANE", + "density_g_cm3": 0.6262000000, + "radlen_cm": 72.0844513772, + "intlen_cm": 105.2394466274, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.8323567353 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1676432647 + } + ] + }, + "G4_N-PROPYL_ALCOHOL": { + "name": "G4_N-PROPYL_ALCOHOL", + "density_g_cm3": 0.8035000000, + "radlen_cm": 51.9709503464, + "intlen_cm": 86.8320985664, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5995862193 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1341793689 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2662344118 + } + ] + }, + "G4_NAPHTHALENE": { + "name": "G4_NAPHTHALENE", + "density_g_cm3": 1.1450000000, + "radlen_cm": 38.0628209006, + "intlen_cm": 64.7481877565, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.9370876957 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0629123043 + } + ] + }, + "G4_NEOPRENE": { + "name": "G4_NEOPRENE", + "density_g_cm3": 1.2300000000, + "radlen_cm": 23.6452775257, + "intlen_cm": 68.4404103466, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5426421718 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0569231500 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.4004346781 + } + ] + }, + "G4_NITROBENZENE": { + "name": "G4_NITROBENZENE", + "density_g_cm3": 1.1986700000, + "radlen_cm": 33.4429484913, + "intlen_cm": 65.3377994376, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5853676418 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0409367005 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.1137747242 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2599209335 + } + ] + }, + "G4_NITROUS_OXIDE": { + "name": "G4_NITROUS_OXIDE", + "density_g_cm3": 0.0018309400, + "radlen_cm": 19953.4404326249, + "intlen_cm": 46817.4565215888, + "elements": [ + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.6364843009 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3635156991 + } + ] + }, + "G4_NYLON-11_RILSAN": { + "name": "G4_NYLON-11_RILSAN", + "density_g_cm3": 1.4250000000, + "radlen_cm": 30.1506780146, + "intlen_cm": 49.4620432566, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1154758845 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.7208182792 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0764169236 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.0872889127 + } + ] + }, + "G4_NYLON-6-10": { + "name": "G4_NYLON-6-10", + "density_g_cm3": 1.1400000000, + "radlen_cm": 37.2399939034, + "intlen_cm": 62.6184623928, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1070620000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6804490000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0991890000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1133000000 + } + ] + }, + "G4_NYLON-6-6": { + "name": "G4_NYLON-6-6", + "density_g_cm3": 1.1400000000, + "radlen_cm": 36.7677016597, + "intlen_cm": 63.4952151640, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6368481720 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0979811903 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.1237807148 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1413899228 + } + ] + }, + "G4_NYLON-8062": { + "name": "G4_NYLON-8062", + "density_g_cm3": 1.0800000000, + "radlen_cm": 38.9258725316, + "intlen_cm": 66.5604267341, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1035091035 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6484156484 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0995360995 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1485391485 + } + ] + }, + "G4_Na": { + "name": "G4_Na", + "density_g_cm3": 0.9710000000, + "radlen_cm": 28.5646359402, + "intlen_cm": 102.4929311883, + "elements": [ + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Nb": { + "name": "G4_Nb", + "density_g_cm3": 8.5700000000, + "radlen_cm": 1.1578315182, + "intlen_cm": 18.4970498911, + "elements": [ + { + "symbol": "Nb", + "Z": 41, + "A_g_mol": 92.9064000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Nd": { + "name": "G4_Nd", + "density_g_cm3": 6.9000000000, + "radlen_cm": 1.1166740406, + "intlen_cm": 26.6017647572, + "elements": [ + { + "symbol": "Nd", + "Z": 60, + "A_g_mol": 144.2362360000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Ne": { + "name": "G4_Ne", + "density_g_cm3": 0.0008385050, + "radlen_cm": 34504.7957012515, + "intlen_cm": 113641.3080885588, + "elements": [ + { + "symbol": "Ne", + "Z": 10, + "A_g_mol": 20.1800112800, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Ni": { + "name": "G4_Ni", + "density_g_cm3": 8.9020000000, + "radlen_cm": 1.4242208745, + "intlen_cm": 15.2795322887, + "elements": [ + { + "symbol": "Ni", + "Z": 28, + "A_g_mol": 58.6933251009, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Np": { + "name": "G4_Np", + "density_g_cm3": 20.2500000000, + "radlen_cm": 0.2896763497, + "intlen_cm": 10.6968313874, + "elements": [ + { + "symbol": "Np", + "Z": 93, + "A_g_mol": 237.0480000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_O": { + "name": "G4_O", + "density_g_cm3": 0.0013315100, + "radlen_cm": 25713.7634595345, + "intlen_cm": 66235.5975584616, + "elements": [ + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_OCTADECANOL": { + "name": "G4_OCTADECANOL", + "density_g_cm3": 0.8120000000, + "radlen_cm": 54.2695775063, + "intlen_cm": 83.8467764328, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1415990478 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.7992522572 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.0591486950 + } + ] + }, + "G4_OCTANE": { + "name": "G4_OCTANE", + "density_g_cm3": 0.7026000000, + "radlen_cm": 64.0534438285, + "intlen_cm": 94.6809469695, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.8411702684 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1588297316 + } + ] + }, + "G4_Os": { + "name": "G4_Os", + "density_g_cm3": 22.5700000000, + "radlen_cm": 0.2958609866, + "intlen_cm": 8.9185046979, + "elements": [ + { + "symbol": "Os", + "Z": 76, + "A_g_mol": 190.2245546000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_P": { + "name": "G4_P", + "density_g_cm3": 2.2000000000, + "radlen_cm": 9.6387902637, + "intlen_cm": 49.9624310154, + "elements": [ + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_PARAFFIN": { + "name": "G4_PARAFFIN", + "density_g_cm3": 0.9300000000, + "radlen_cm": 48.2237383379, + "intlen_cm": 72.3210868975, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.8513873152 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1486126848 + } + ] + }, + "G4_PHOSPHORIC_ACID": { + "name": "G4_PHOSPHORIC_ACID", + "density_g_cm3": 1.8700000000, + "radlen_cm": 15.5141283621, + "intlen_cm": 47.9082638597, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0308568456 + }, + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 0.3160747168 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.6530684376 + } + ] + }, + "G4_PHOTO_EMULSION": { + "name": "G4_PHOTO_EMULSION", + "density_g_cm3": 3.8150000000, + "radlen_cm": 2.9706503474, + "intlen_cm": 35.0478714990, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0141000000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.0722610000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0193200000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.0661010000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0018900000 + }, + { + "symbol": "Br", + "Z": 35, + "A_g_mol": 79.9035138000, + "mass_fraction": 0.3491030000 + }, + { + "symbol": "Ag", + "Z": 47, + "A_g_mol": 107.8682200000, + "mass_fraction": 0.4741050000 + }, + { + "symbol": "I", + "Z": 53, + "A_g_mol": 126.9040000000, + "mass_fraction": 0.0031200000 + } + ] + }, + "G4_PLASTIC_SC_VINYLTOLUENE": { + "name": "G4_PLASTIC_SC_VINYLTOLUENE", + "density_g_cm3": 1.0320000000, + "radlen_cm": 42.5441996486, + "intlen_cm": 69.9693874192, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.9147085318 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0852914682 + } + ] + }, + "G4_PLEXIGLASS": { + "name": "G4_PLEXIGLASS", + "density_g_cm3": 1.1900000000, + "radlen_cm": 34.0748806544, + "intlen_cm": 62.6702055110, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5998410709 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0805418407 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3196170884 + } + ] + }, + "G4_PLUTONIUM_DIOXIDE": { + "name": "G4_PLUTONIUM_DIOXIDE", + "density_g_cm3": 11.4600000000, + "radlen_cm": 0.5723242927, + "intlen_cm": 16.2912350677, + "elements": [ + { + "symbol": "Pu", + "Z": 94, + "A_g_mol": 244.0640000000, + "mass_fraction": 0.8840887543 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1159112457 + } + ] + }, + "G4_POLYACRYLONITRILE": { + "name": "G4_POLYACRYLONITRILE", + "density_g_cm3": 1.1700000000, + "radlen_cm": 35.9776646539, + "intlen_cm": 64.6096169435, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6790483898 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0569857271 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.2639658832 + } + ] + }, + "G4_POLYCARBONATE": { + "name": "G4_POLYCARBONATE", + "density_g_cm3": 1.2000000000, + "radlen_cm": 34.5873361608, + "intlen_cm": 63.3495193368, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.7557453702 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0554943691 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1887602607 + } + ] + }, + "G4_POLYCHLOROSTYRENE": { + "name": "G4_POLYCHLOROSTYRENE", + "density_g_cm3": 1.3000000000, + "radlen_cm": 25.3754585368, + "intlen_cm": 62.3930757123, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6932901610 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0509082842 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.2558015548 + } + ] + }, + "G4_POLYETHYLENE": { + "name": "G4_POLYETHYLENE", + "density_g_cm3": 0.9400000000, + "radlen_cm": 47.6316902019, + "intlen_cm": 71.9328468651, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.8562817123 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1437182877 + } + ] + }, + "G4_POLYOXYMETHYLENE": { + "name": "G4_POLYOXYMETHYLENE", + "density_g_cm3": 1.4250000000, + "radlen_cm": 26.9940588984, + "intlen_cm": 54.1869646318, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4000110924 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0671378455 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.5328510621 + } + ] + }, + "G4_POLYPROPYLENE": { + "name": "G4_POLYPROPYLENE", + "density_g_cm3": 0.9000000000, + "radlen_cm": 49.7486542109, + "intlen_cm": 75.1298622814, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.8562817123 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1437182877 + } + ] + }, + "G4_POLYSTYRENE": { + "name": "G4_POLYSTYRENE", + "density_g_cm3": 1.0600000000, + "radlen_cm": 41.3125056266, + "intlen_cm": 68.7498786660, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.9225773293 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0774226707 + } + ] + }, + "G4_POLYTRIFLUOROCHLOROETHYLENE": { + "name": "G4_POLYTRIFLUOROCHLOROETHYLENE", + "density_g_cm3": 2.1000000000, + "radlen_cm": 13.4211774188, + "intlen_cm": 45.5231505449, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.2062473447 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.4893583661 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.3043942892 + } + ] + }, + "G4_POLYVINYLIDENE_CHLORIDE": { + "name": "G4_POLYVINYLIDENE_CHLORIDE", + "density_g_cm3": 1.7000000000, + "radlen_cm": 13.3466912811, + "intlen_cm": 58.5490734291, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.2477909327 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0207946100 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.7314144572 + } + ] + }, + "G4_POLYVINYLIDENE_FLUORIDE": { + "name": "G4_POLYVINYLIDENE_FLUORIDE", + "density_g_cm3": 1.7600000000, + "radlen_cm": 20.8089540539, + "intlen_cm": 47.6128231526, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.3751353170 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0314813482 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.5933833348 + } + ] + }, + "G4_POLYVINYL_ACETATE": { + "name": "G4_POLYVINYL_ACETATE", + "density_g_cm3": 1.1900000000, + "radlen_cm": 33.5589610921, + "intlen_cm": 63.7392761655, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5580589687 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0702484460 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3716925853 + } + ] + }, + "G4_POLYVINYL_ALCOHOL": { + "name": "G4_POLYVINYL_ALCOHOL", + "density_g_cm3": 1.3000000000, + "radlen_cm": 30.9791700346, + "intlen_cm": 56.8283796029, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5452903684 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0915215132 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3631881183 + } + ] + }, + "G4_POLYVINYL_BUTYRAL": { + "name": "G4_POLYVINYL_BUTYRAL", + "density_g_cm3": 1.1200000000, + "radlen_cm": 37.2445284119, + "intlen_cm": 64.6185702523, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6757292578 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0992375747 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2250331675 + } + ] + }, + "G4_POLYVINYL_CHLORIDE": { + "name": "G4_POLYVINYL_CHLORIDE", + "density_g_cm3": 1.3000000000, + "radlen_cm": 19.6259713893, + "intlen_cm": 69.2301257138, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.3843566728 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0483828062 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.5672605210 + } + ] + }, + "G4_POLYVINYL_PYRROLIDONE": { + "name": "G4_POLYVINYL_PYRROLIDONE", + "density_g_cm3": 1.2500000000, + "radlen_cm": 33.3295432994, + "intlen_cm": 59.0516806099, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.6483992503 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0816204778 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.1260258351 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1439544368 + } + ] + }, + "G4_POTASSIUM_IODIDE": { + "name": "G4_POTASSIUM_IODIDE", + "density_g_cm3": 3.1300000000, + "radlen_cm": 3.0794662292, + "intlen_cm": 50.4789672731, + "elements": [ + { + "symbol": "K", + "Z": 19, + "A_g_mol": 39.0982931613, + "mass_fraction": 0.2355286329 + }, + { + "symbol": "I", + "Z": 53, + "A_g_mol": 126.9040000000, + "mass_fraction": 0.7644713671 + } + ] + }, + "G4_POTASSIUM_OXIDE": { + "name": "G4_POTASSIUM_OXIDE", + "density_g_cm3": 2.3200000000, + "radlen_cm": 8.1473889734, + "intlen_cm": 48.3539578260, + "elements": [ + { + "symbol": "K", + "Z": 19, + "A_g_mol": 39.0982931613, + "mass_fraction": 0.8301478368 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1698521632 + } + ] + }, + "G4_PROPANE": { + "name": "G4_PROPANE", + "density_g_cm3": 0.0018793900, + "radlen_cm": 24143.4343892002, + "intlen_cm": 34507.9467355684, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.8171359205 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1828640795 + } + ] + }, + "G4_PYRIDINE": { + "name": "G4_PYRIDINE", + "density_g_cm3": 0.9819000000, + "radlen_cm": 43.4238519327, + "intlen_cm": 76.0528817127, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.7592106765 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0637129445 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.1770763790 + } + ] + }, + "G4_Pa": { + "name": "G4_Pa", + "density_g_cm3": 15.3700000000, + "radlen_cm": 0.3860695338, + "intlen_cm": 13.9729283036, + "elements": [ + { + "symbol": "Pa", + "Z": 91, + "A_g_mol": 231.0360000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Pb": { + "name": "G4_Pb", + "density_g_cm3": 11.3500000000, + "radlen_cm": 0.5612532628, + "intlen_cm": 18.2479470310, + "elements": [ + { + "symbol": "Pb", + "Z": 82, + "A_g_mol": 207.2170000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_PbWO4": { + "name": "G4_PbWO4", + "density_g_cm3": 8.2800000000, + "radlen_cm": 0.8924531919, + "intlen_cm": 20.7397427149, + "elements": [ + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1406366195 + }, + { + "symbol": "Pb", + "Z": 82, + "A_g_mol": 207.2170000000, + "mass_fraction": 0.4553657612 + }, + { + "symbol": "W", + "Z": 74, + "A_g_mol": 183.8416100000, + "mass_fraction": 0.4039976193 + } + ] + }, + "G4_Pd": { + "name": "G4_Pd", + "density_g_cm3": 12.0200000000, + "radlen_cm": 0.7657167657, + "intlen_cm": 13.7984874589, + "elements": [ + { + "symbol": "Pd", + "Z": 46, + "A_g_mol": 106.4151876000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Pm": { + "name": "G4_Pm", + "density_g_cm3": 7.2200000000, + "radlen_cm": 1.0408459865, + "intlen_cm": 25.4624387556, + "elements": [ + { + "symbol": "Pm", + "Z": 61, + "A_g_mol": 144.9130000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Po": { + "name": "G4_Po", + "density_g_cm3": 9.3200000000, + "radlen_cm": 0.6610916001, + "intlen_cm": 22.2854698005, + "elements": [ + { + "symbol": "Po", + "Z": 84, + "A_g_mol": 208.9820000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Pr": { + "name": "G4_Pr", + "density_g_cm3": 6.7100000000, + "radlen_cm": 1.1562026576, + "intlen_cm": 27.1429747446, + "elements": [ + { + "symbol": "Pr", + "Z": 59, + "A_g_mol": 140.9080000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Pt": { + "name": "G4_Pt", + "density_g_cm3": 21.4500000000, + "radlen_cm": 0.3050532706, + "intlen_cm": 9.4633205278, + "elements": [ + { + "symbol": "Pt", + "Z": 78, + "A_g_mol": 195.0780035700, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Pu": { + "name": "G4_Pu", + "density_g_cm3": 19.8400000000, + "radlen_cm": 0.2989048704, + "intlen_cm": 11.0245529144, + "elements": [ + { + "symbol": "Pu", + "Z": 94, + "A_g_mol": 244.0640000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Pyrex_Glass": { + "name": "G4_Pyrex_Glass", + "density_g_cm3": 2.2300000000, + "radlen_cm": 12.6325375693, + "intlen_cm": 42.2910635276, + "elements": [ + { + "symbol": "B", + "Z": 5, + "A_g_mol": 10.8110164000, + "mass_fraction": 0.0400639199 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.5395609209 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0281909436 + }, + { + "symbol": "Al", + "Z": 13, + "A_g_mol": 26.9815000000, + "mass_fraction": 0.0116439767 + }, + { + "symbol": "Si", + "Z": 14, + "A_g_mol": 28.0853614555, + "mass_fraction": 0.3772192456 + }, + { + "symbol": "K", + "Z": 19, + "A_g_mol": 39.0982931613, + "mass_fraction": 0.0033209934 + } + ] + }, + "G4_RUBBER_BUTYL": { + "name": "G4_RUBBER_BUTYL", + "density_g_cm3": 0.9200000000, + "radlen_cm": 48.6670416944, + "intlen_cm": 73.4971873425, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1437110000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.8562890000 + } + ] + }, + "G4_RUBBER_NATURAL": { + "name": "G4_RUBBER_NATURAL", + "density_g_cm3": 0.9200000000, + "radlen_cm": 48.2532262024, + "intlen_cm": 75.5816000621, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1183710000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.8816290000 + } + ] + }, + "G4_RUBBER_NEOPRENE": { + "name": "G4_RUBBER_NEOPRENE", + "density_g_cm3": 1.2300000000, + "radlen_cm": 23.6452744201, + "intlen_cm": 68.4406876941, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0569200000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5426460000 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.4004340000 + } + ] + }, + "G4_Ra": { + "name": "G4_Ra", + "density_g_cm3": 5.0000000000, + "radlen_cm": 1.2298658749, + "intlen_cm": 42.6399710179, + "elements": [ + { + "symbol": "Ra", + "Z": 88, + "A_g_mol": 226.0250000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Rb": { + "name": "G4_Rb", + "density_g_cm3": 1.5320000000, + "radlen_cm": 7.1977407506, + "intlen_cm": 100.6336626726, + "elements": [ + { + "symbol": "Rb", + "Z": 37, + "A_g_mol": 85.4676764200, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Re": { + "name": "G4_Re", + "density_g_cm3": 21.0200000000, + "radlen_cm": 0.3182831922, + "intlen_cm": 9.5082503276, + "elements": [ + { + "symbol": "Re", + "Z": 75, + "A_g_mol": 186.2068780000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Rh": { + "name": "G4_Rh", + "density_g_cm3": 12.4100000000, + "radlen_cm": 0.7466192395, + "intlen_cm": 13.2162992461, + "elements": [ + { + "symbol": "Rh", + "Z": 45, + "A_g_mol": 102.9060000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Rn": { + "name": "G4_Rn", + "density_g_cm3": 0.0090066200, + "radlen_cm": 697.7766671691, + "intlen_cm": 23530.7426900032, + "elements": [ + { + "symbol": "Rn", + "Z": 86, + "A_g_mol": 222.0180000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Ru": { + "name": "G4_Ru", + "density_g_cm3": 12.4100000000, + "radlen_cm": 0.7640666774, + "intlen_cm": 13.1370029745, + "elements": [ + { + "symbol": "Ru", + "Z": 44, + "A_g_mol": 101.0648187900, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_S": { + "name": "G4_S", + "density_g_cm3": 2.0000000000, + "radlen_cm": 9.7482931365, + "intlen_cm": 55.5972779233, + "elements": [ + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_SILICON_DIOXIDE": { + "name": "G4_SILICON_DIOXIDE", + "density_g_cm3": 2.3200000000, + "radlen_cm": 11.6577276645, + "intlen_cm": 41.3174239290, + "elements": [ + { + "symbol": "Si", + "Z": 14, + "A_g_mol": 28.0853614555, + "mass_fraction": 0.4674338418 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.5325661582 + } + ] + }, + "G4_SILVER_BROMIDE": { + "name": "G4_SILVER_BROMIDE", + "density_g_cm3": 6.4730000000, + "radlen_cm": 1.5250930816, + "intlen_cm": 24.6362003753, + "elements": [ + { + "symbol": "Ag", + "Z": 47, + "A_g_mol": 107.8682200000, + "mass_fraction": 0.5744646322 + }, + { + "symbol": "Br", + "Z": 35, + "A_g_mol": 79.9035138000, + "mass_fraction": 0.4255353678 + } + ] + }, + "G4_SILVER_CHLORIDE": { + "name": "G4_SILVER_CHLORIDE", + "density_g_cm3": 5.5600000000, + "radlen_cm": 1.8592358479, + "intlen_cm": 26.9699324435, + "elements": [ + { + "symbol": "Ag", + "Z": 47, + "A_g_mol": 107.8682200000, + "mass_fraction": 0.7526348232 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.2473651768 + } + ] + }, + "G4_SILVER_HALIDES": { + "name": "G4_SILVER_HALIDES", + "density_g_cm3": 6.4700000000, + "radlen_cm": 1.5245239630, + "intlen_cm": 24.6583577333, + "elements": [ + { + "symbol": "Br", + "Z": 35, + "A_g_mol": 79.9035138000, + "mass_fraction": 0.4228950000 + }, + { + "symbol": "Ag", + "Z": 47, + "A_g_mol": 107.8682200000, + "mass_fraction": 0.5737480000 + }, + { + "symbol": "I", + "Z": 53, + "A_g_mol": 126.9040000000, + "mass_fraction": 0.0033570000 + } + ] + }, + "G4_SILVER_IODIDE": { + "name": "G4_SILVER_IODIDE", + "density_g_cm3": 6.0100000000, + "radlen_cm": 1.4473505292, + "intlen_cm": 28.5353855145, + "elements": [ + { + "symbol": "Ag", + "Z": 47, + "A_g_mol": 107.8682200000, + "mass_fraction": 0.4594590450 + }, + { + "symbol": "I", + "Z": 53, + "A_g_mol": 126.9040000000, + "mass_fraction": 0.5405409550 + } + ] + }, + "G4_SKIN_ICRP": { + "name": "G4_SKIN_ICRP", + "density_g_cm3": 1.0900000000, + "radlen_cm": 34.3047957676, + "intlen_cm": 69.0045449724, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1000000000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.2040000000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0420000000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.6450000000 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 0.0010000000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.0030000000 + }, + { + "symbol": "K", + "Z": 19, + "A_g_mol": 39.0982931613, + "mass_fraction": 0.0010000000 + } + ] + }, + "G4_SODIUM_CARBONATE": { + "name": "G4_SODIUM_CARBONATE", + "density_g_cm3": 2.5320000000, + "radlen_cm": 12.5293001203, + "intlen_cm": 36.2077642513, + "elements": [ + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.4338168452 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1133211199 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.4528620349 + } + ] + }, + "G4_SODIUM_IODIDE": { + "name": "G4_SODIUM_IODIDE", + "density_g_cm3": 3.6670000000, + "radlen_cm": 2.5882212769, + "intlen_cm": 42.9136935370, + "elements": [ + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.1533739221 + }, + { + "symbol": "I", + "Z": 53, + "A_g_mol": 126.9040000000, + "mass_fraction": 0.8466260779 + } + ] + }, + "G4_SODIUM_MONOXIDE": { + "name": "G4_SODIUM_MONOXIDE", + "density_g_cm3": 2.2700000000, + "radlen_cm": 12.8484696099, + "intlen_cm": 42.4347669192, + "elements": [ + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.7418578408 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2581421592 + } + ] + }, + "G4_SODIUM_NITRATE": { + "name": "G4_SODIUM_NITRATE", + "density_g_cm3": 2.2610000000, + "radlen_cm": 14.4612324186, + "intlen_cm": 39.9375053268, + "elements": [ + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.2704849729 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.1647957147 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.5647193123 + } + ] + }, + "G4_STAINLESS-STEEL": { + "name": "G4_STAINLESS-STEEL", + "density_g_cm3": 8.0000000000, + "radlen_cm": 1.7380670645, + "intlen_cm": 16.6780570974, + "elements": [ + { + "symbol": "Fe", + "Z": 26, + "A_g_mol": 55.8451107980, + "mass_fraction": 0.7462128746 + }, + { + "symbol": "Cr", + "Z": 24, + "A_g_mol": 51.9961301370, + "mass_fraction": 0.1690010443 + }, + { + "symbol": "Ni", + "Z": 28, + "A_g_mol": 58.6933251009, + "mass_fraction": 0.0847860811 + } + ] + }, + "G4_STILBENE": { + "name": "G4_STILBENE", + "density_g_cm3": 0.9707000000, + "radlen_cm": 44.9595141781, + "intlen_cm": 75.9942943910, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.9328955096 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0671044904 + } + ] + }, + "G4_SUCROSE": { + "name": "G4_SUCROSE", + "density_g_cm3": 1.5805000000, + "radlen_cm": 24.4231162245, + "intlen_cm": 48.9185997692, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4210638981 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0647820686 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.5141540333 + } + ] + }, + "G4_Sb": { + "name": "G4_Sb", + "density_g_cm3": 6.6910000000, + "radlen_cm": 1.3040127628, + "intlen_cm": 25.9265676022, + "elements": [ + { + "symbol": "Sb", + "Z": 51, + "A_g_mol": 121.7598000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Sc": { + "name": "G4_Sc", + "density_g_cm3": 2.9890000000, + "radlen_cm": 5.5354470594, + "intlen_cm": 41.6361977932, + "elements": [ + { + "symbol": "Sc", + "Z": 21, + "A_g_mol": 44.9559000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Se": { + "name": "G4_Se", + "density_g_cm3": 4.5000000000, + "radlen_cm": 2.6462517652, + "intlen_cm": 33.3674841789, + "elements": [ + { + "symbol": "Se", + "Z": 34, + "A_g_mol": 78.9593734300, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Si": { + "name": "G4_Si", + "density_g_cm3": 2.3300000000, + "radlen_cm": 9.3660702922, + "intlen_cm": 45.6603073704, + "elements": [ + { + "symbol": "Si", + "Z": 14, + "A_g_mol": 28.0853614555, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Sm": { + "name": "G4_Sm", + "density_g_cm3": 7.4600000000, + "radlen_cm": 1.0152448223, + "intlen_cm": 24.9485982224, + "elements": [ + { + "symbol": "Sm", + "Z": 62, + "A_g_mol": 150.3663619000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Sn": { + "name": "G4_Sn", + "density_g_cm3": 7.3100000000, + "radlen_cm": 1.2063713058, + "intlen_cm": 23.5313378422, + "elements": [ + { + "symbol": "Sn", + "Z": 50, + "A_g_mol": 118.7101218000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Sr": { + "name": "G4_Sr", + "density_g_cm3": 2.5400000000, + "radlen_cm": 4.2369989713, + "intlen_cm": 61.2016634807, + "elements": [ + { + "symbol": "Sr", + "Z": 38, + "A_g_mol": 87.6166395000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_TEFLON": { + "name": "G4_TEFLON", + "density_g_cm3": 2.2000000000, + "radlen_cm": 15.8385014262, + "intlen_cm": 40.8310561331, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.2401785261 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.7598214739 + } + ] + }, + "G4_TERPHENYL": { + "name": "G4_TERPHENYL", + "density_g_cm3": 1.2400000000, + "radlen_cm": 35.1277340003, + "intlen_cm": 59.9049067935, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.9387281833 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0612718167 + } + ] + }, + "G4_TESTIS_ICRP": { + "name": "G4_TESTIS_ICRP", + "density_g_cm3": 1.0400000000, + "radlen_cm": 35.1692276422, + "intlen_cm": 72.4725513802, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1060000000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.0990000000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0200000000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.7660000000 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 0.0010000000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "K", + "Z": 19, + "A_g_mol": 39.0982931613, + "mass_fraction": 0.0020000000 + } + ] + }, + "G4_TETRACHLOROETHYLENE": { + "name": "G4_TETRACHLOROETHYLENE", + "density_g_cm3": 1.6250000000, + "radlen_cm": 12.8873634663, + "intlen_cm": 66.5667035399, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1448544708 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.8551455292 + } + ] + }, + "G4_THALLIUM_CHLORIDE": { + "name": "G4_THALLIUM_CHLORIDE", + "density_g_cm3": 7.0040000000, + "radlen_cm": 1.0166713986, + "intlen_cm": 26.3467114309, + "elements": [ + { + "symbol": "Tl", + "Z": 81, + "A_g_mol": 204.3829295200, + "mass_fraction": 0.8521796274 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.1478203726 + } + ] + }, + "G4_THYMINE": { + "name": "G4_THYMINE", + "density_g_cm3": 1.4800000000, + "radlen_cm": 26.8429768410, + "intlen_cm": 52.7013719035, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0479539269 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4761870282 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.2221293174 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2537297275 + } + ] + }, + "G4_TISSUE-METHANE": { + "name": "G4_TISSUE-METHANE", + "density_g_cm3": 0.0010640900, + "radlen_cm": 37431.0260905174, + "intlen_cm": 68943.1775981619, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1018690000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4561790000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0351720000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.4067800000 + } + ] + }, + "G4_TISSUE-PROPANE": { + "name": "G4_TISSUE-PROPANE", + "density_g_cm3": 0.0018262800, + "radlen_cm": 22400.6777976263, + "intlen_cm": 39755.8870930692, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1026720000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5689400000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0350220000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2933660000 + } + ] + }, + "G4_TISSUE_SOFT_ICRP": { + "name": "G4_TISSUE_SOFT_ICRP", + "density_g_cm3": 1.0300000000, + "radlen_cm": 36.6945920182, + "intlen_cm": 72.2954858829, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1050000000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.2560000000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0270000000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.6020000000 + }, + { + "symbol": "Na", + "Z": 11, + "A_g_mol": 22.9898000000, + "mass_fraction": 0.0010000000 + }, + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "S", + "Z": 16, + "A_g_mol": 32.0661142600, + "mass_fraction": 0.0030000000 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.0020000000 + }, + { + "symbol": "K", + "Z": 19, + "A_g_mol": 39.0982931613, + "mass_fraction": 0.0020000000 + } + ] + }, + "G4_TISSUE_SOFT_ICRU-4": { + "name": "G4_TISSUE_SOFT_ICRU-4", + "density_g_cm3": 1.0000000000, + "radlen_cm": 36.8431452797, + "intlen_cm": 75.6496725291, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1010000000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1110000000 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.0260000000 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.7620000000 + } + ] + }, + "G4_TITANIUM_DIOXIDE": { + "name": "G4_TITANIUM_DIOXIDE", + "density_g_cm3": 4.2600000000, + "radlen_cm": 4.8120066363, + "intlen_cm": 25.3523138121, + "elements": [ + { + "symbol": "Ti", + "Z": 22, + "A_g_mol": 47.8667173300, + "mass_fraction": 0.5993416236 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.4006583764 + } + ] + }, + "G4_TOLUENE": { + "name": "G4_TOLUENE", + "density_g_cm3": 0.8669000000, + "radlen_cm": 50.6840912009, + "intlen_cm": 83.0802585391, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.9124848982 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0875151018 + } + ] + }, + "G4_TRICHLOROETHYLENE": { + "name": "G4_TRICHLOROETHYLENE", + "density_g_cm3": 1.4600000000, + "radlen_cm": 14.7632677276, + "intlen_cm": 71.7912794212, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1828297192 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0076715332 + }, + { + "symbol": "Cl", + "Z": 17, + "A_g_mol": 35.4525734000, + "mass_fraction": 0.8094987477 + } + ] + }, + "G4_TRIETHYL_PHOSPHATE": { + "name": "G4_TRIETHYL_PHOSPHATE", + "density_g_cm3": 1.0700000000, + "radlen_cm": 32.3802005558, + "intlen_cm": 72.7982710675, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.3956216481 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0830014017 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.3513359494 + }, + { + "symbol": "P", + "Z": 15, + "A_g_mol": 30.9738000000, + "mass_fraction": 0.1700410008 + } + ] + }, + "G4_TUNGSTEN_HEXAFLUORIDE": { + "name": "G4_TUNGSTEN_HEXAFLUORIDE", + "density_g_cm3": 2.4000000000, + "radlen_cm": 4.0495275513, + "intlen_cm": 57.8720027568, + "elements": [ + { + "symbol": "W", + "Z": 74, + "A_g_mol": 183.8416100000, + "mass_fraction": 0.6172661226 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.3827338774 + } + ] + }, + "G4_Ta": { + "name": "G4_Ta", + "density_g_cm3": 16.6540000000, + "radlen_cm": 0.4093920370, + "intlen_cm": 11.8868655868, + "elements": [ + { + "symbol": "Ta", + "Z": 73, + "A_g_mol": 180.9478798800, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Tb": { + "name": "G4_Tb", + "density_g_cm3": 8.2290000000, + "radlen_cm": 0.8939773235, + "intlen_cm": 23.0383704280, + "elements": [ + { + "symbol": "Tb", + "Z": 65, + "A_g_mol": 158.9250000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Tc": { + "name": "G4_Tc", + "density_g_cm3": 11.5000000000, + "radlen_cm": 0.8331486453, + "intlen_cm": 14.0273332814, + "elements": [ + { + "symbol": "Tc", + "Z": 43, + "A_g_mol": 97.9072000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Te": { + "name": "G4_Te", + "density_g_cm3": 6.2400000000, + "radlen_cm": 1.4145736790, + "intlen_cm": 28.2381937661, + "elements": [ + { + "symbol": "Te", + "Z": 52, + "A_g_mol": 127.6028203000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Th": { + "name": "G4_Th", + "density_g_cm3": 11.7200000000, + "radlen_cm": 0.5182303174, + "intlen_cm": 18.3510184568, + "elements": [ + { + "symbol": "Th", + "Z": 90, + "A_g_mol": 232.0380000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Ti": { + "name": "G4_Ti", + "density_g_cm3": 4.5400000000, + "radlen_cm": 3.5601976931, + "intlen_cm": 27.9913240031, + "elements": [ + { + "symbol": "Ti", + "Z": 22, + "A_g_mol": 47.8667173300, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Tl": { + "name": "G4_Tl", + "density_g_cm3": 11.7200000000, + "radlen_cm": 0.5476649213, + "intlen_cm": 17.5909248845, + "elements": [ + { + "symbol": "Tl", + "Z": 81, + "A_g_mol": 204.3829295200, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Tm": { + "name": "G4_Tm", + "density_g_cm3": 9.3210000000, + "radlen_cm": 0.7544283053, + "intlen_cm": 20.7576376405, + "elements": [ + { + "symbol": "Tm", + "Z": 69, + "A_g_mol": 168.9340000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_U": { + "name": "G4_U", + "density_g_cm3": 18.9500000000, + "radlen_cm": 0.3166296193, + "intlen_cm": 11.4463995326, + "elements": [ + { + "symbol": "U", + "Z": 92, + "A_g_mol": 238.0291290500, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_URACIL": { + "name": "G4_URACIL", + "density_g_cm3": 1.3200000000, + "radlen_cm": 29.6780964598, + "intlen_cm": 60.2469223893, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0359699343 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.4286218190 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.2499266740 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2854815727 + } + ] + }, + "G4_URANIUM_DICARBIDE": { + "name": "G4_URANIUM_DICARBIDE", + "density_g_cm3": 11.2800000000, + "radlen_cm": 0.5774187445, + "intlen_cm": 16.6288137602, + "elements": [ + { + "symbol": "U", + "Z": 92, + "A_g_mol": 238.0291290500, + "mass_fraction": 0.9083326938 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.0916673062 + } + ] + }, + "G4_URANIUM_MONOCARBIDE": { + "name": "G4_URANIUM_MONOCARBIDE", + "density_g_cm3": 13.6300000000, + "radlen_cm": 0.4591719671, + "intlen_cm": 14.7086462814, + "elements": [ + { + "symbol": "U", + "Z": 92, + "A_g_mol": 238.0291290500, + "mass_fraction": 0.9519647143 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.0480352857 + } + ] + }, + "G4_URANIUM_OXIDE": { + "name": "G4_URANIUM_OXIDE", + "density_g_cm3": 10.9600000000, + "radlen_cm": 0.6067585859, + "intlen_cm": 16.8728319003, + "elements": [ + { + "symbol": "U", + "Z": 92, + "A_g_mol": 238.0291290500, + "mass_fraction": 0.8814982465 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.1185017535 + } + ] + }, + "G4_UREA": { + "name": "G4_UREA", + "density_g_cm3": 1.3230000000, + "radlen_cm": 29.2864284039, + "intlen_cm": 58.3095550771, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.1999941860 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0671340321 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.4664613838 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2664103982 + } + ] + }, + "G4_V": { + "name": "G4_V", + "density_g_cm3": 6.1100000000, + "radlen_cm": 2.5928540732, + "intlen_cm": 21.2349284162, + "elements": [ + { + "symbol": "V", + "Z": 23, + "A_g_mol": 50.9415080000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_VALINE": { + "name": "G4_VALINE", + "density_g_cm3": 1.2300000000, + "radlen_cm": 33.0046908252, + "intlen_cm": 59.7177245788, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.5126370904 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0946450872 + }, + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 0.1195661791 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.2731516433 + } + ] + }, + "G4_VITON": { + "name": "G4_VITON", + "density_g_cm3": 1.8000000000, + "radlen_cm": 19.6436366979, + "intlen_cm": 48.8530589173, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0094170000 + }, + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.2805550000 + }, + { + "symbol": "F", + "Z": 9, + "A_g_mol": 18.9984000000, + "mass_fraction": 0.7100280000 + } + ] + }, + "G4_W": { + "name": "G4_W", + "density_g_cm3": 19.3000000000, + "radlen_cm": 0.3504180177, + "intlen_cm": 10.3115837893, + "elements": [ + { + "symbol": "W", + "Z": 74, + "A_g_mol": 183.8416100000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_WATER": { + "name": "G4_WATER", + "density_g_cm3": 1.0000000000, + "radlen_cm": 36.0829774640, + "intlen_cm": 75.3747894121, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1118984778 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.8881015222 + } + ] + }, + "G4_WATER_VAPOR": { + "name": "G4_WATER_VAPOR", + "density_g_cm3": 0.0007561820, + "radlen_cm": 47717.3186666997, + "intlen_cm": 99678.1058159553, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1118984778 + }, + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 0.8881015222 + } + ] + }, + "G4_XYLENE": { + "name": "G4_XYLENE", + "density_g_cm3": 0.8700000000, + "radlen_cm": 50.6283508279, + "intlen_cm": 82.0777032085, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.9050593022 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.0949406978 + } + ] + }, + "G4_Xe": { + "name": "G4_Xe", + "density_g_cm3": 0.0054853600, + "radlen_cm": 1546.2047849966, + "intlen_cm": 32429.6946749956, + "elements": [ + { + "symbol": "Xe", + "Z": 54, + "A_g_mol": 131.2924485000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Y": { + "name": "G4_Y", + "density_g_cm3": 4.4690000000, + "radlen_cm": 2.3294263573, + "intlen_cm": 34.9543386305, + "elements": [ + { + "symbol": "Y", + "Z": 39, + "A_g_mol": 88.9058000000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Yb": { + "name": "G4_Yb", + "density_g_cm3": 6.7300000000, + "radlen_cm": 1.0433231211, + "intlen_cm": 28.9800996269, + "elements": [ + { + "symbol": "Yb", + "Z": 70, + "A_g_mol": 173.0376377000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Zn": { + "name": "G4_Zn", + "density_g_cm3": 7.1330000000, + "radlen_cm": 1.7428596191, + "intlen_cm": 19.7687190024, + "elements": [ + { + "symbol": "Zn", + "Z": 30, + "A_g_mol": 65.3955232900, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_Zr": { + "name": "G4_Zr", + "density_g_cm3": 6.5060000000, + "radlen_cm": 1.5670742707, + "intlen_cm": 24.2171559753, + "elements": [ + { + "symbol": "Zr", + "Z": 40, + "A_g_mol": 91.2236313100, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_lAr": { + "name": "G4_lAr", + "density_g_cm3": 1.3960000000, + "radlen_cm": 14.0034386850, + "intlen_cm": 85.7063953867, + "elements": [ + { + "symbol": "Ar", + "Z": 18, + "A_g_mol": 39.9476933511, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_lBr": { + "name": "G4_lBr", + "density_g_cm3": 3.1028000000, + "radlen_cm": 3.6812743699, + "intlen_cm": 48.5850800305, + "elements": [ + { + "symbol": "Br", + "Z": 35, + "A_g_mol": 79.9035138000, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_lH2": { + "name": "G4_lH2", + "density_g_cm3": 0.0708000000, + "radlen_cm": 890.4450429699, + "intlen_cm": 494.3503221504, + "elements": [ + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_lKr": { + "name": "G4_lKr", + "density_g_cm3": 2.4180000000, + "radlen_cm": 4.7031249036, + "intlen_cm": 63.3420249794, + "elements": [ + { + "symbol": "Kr", + "Z": 36, + "A_g_mol": 83.7993175100, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_lN2": { + "name": "G4_lN2", + "density_g_cm3": 0.8070000000, + "radlen_cm": 47.0732642399, + "intlen_cm": 104.5459466611, + "elements": [ + { + "symbol": "N", + "Z": 7, + "A_g_mol": 14.0067689600, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_lO2": { + "name": "G4_lO2", + "density_g_cm3": 1.1410000000, + "radlen_cm": 30.0071281192, + "intlen_cm": 77.2947944830, + "elements": [ + { + "symbol": "O", + "Z": 8, + "A_g_mol": 15.9993904110, + "mass_fraction": 1.0000000000 + } + ] + }, + "G4_lPROPANE": { + "name": "G4_lPROPANE", + "density_g_cm3": 0.4300000000, + "radlen_cm": 105.5230910621, + "intlen_cm": 150.8230000357, + "elements": [ + { + "symbol": "C", + "Z": 6, + "A_g_mol": 12.0107363800, + "mass_fraction": 0.8171359205 + }, + { + "symbol": "H", + "Z": 1, + "A_g_mol": 1.0079407527, + "mass_fraction": 0.1828640795 + } + ] + }, + "G4_lXe": { + "name": "G4_lXe", + "density_g_cm3": 2.9530000000, + "radlen_cm": 2.8721604739, + "intlen_cm": 60.2399424255, + "elements": [ + { + "symbol": "Xe", + "Z": 54, + "A_g_mol": 131.2924485000, + "mass_fraction": 1.0000000000 + } + ] + } + }, + "count_built_ok": 309, + "count_built_fail": 0 +} diff --git a/scripts/geometry/g4_nist_database/compile.sh b/scripts/geometry/g4_nist_database/compile.sh new file mode 100755 index 0000000000000..27d9cb0d87450 --- /dev/null +++ b/scripts/geometry/g4_nist_database/compile.sh @@ -0,0 +1,11 @@ +echo "Compiling using geant4-config..." + +g++ -std=c++20 nist_export_all.cxx \ + $(geant4-config --cflags) \ + $(geant4-config --libs) \ + -O2 -o nist_export_all + +echo "" +echo "Build complete." +echo "Run with:" +echo " ./nist_export_all nist_db_all.json" \ No newline at end of file diff --git a/scripts/geometry/g4_nist_database/nist_export_all.cxx b/scripts/geometry/g4_nist_database/nist_export_all.cxx new file mode 100644 index 0000000000000..709b3da261fbf --- /dev/null +++ b/scripts/geometry/g4_nist_database/nist_export_all.cxx @@ -0,0 +1,136 @@ +#include +#include +#include +#include +#include +#include + +// Geant4 +#include "G4NistManager.hh" +#include "G4Material.hh" +#include "G4Element.hh" +#include "G4SystemOfUnits.hh" + +static std::string json_escape(const std::string& s) +{ + std::string out; + out.reserve(s.size() + 8); + for (char c : s) { + switch (c) { + case '\\': + out += "\\\\"; + break; + case '"': + out += "\\\""; + break; + case '\n': + out += "\\n"; + break; + case '\r': + out += "\\r"; + break; + case '\t': + out += "\\t"; + break; + default: + out += c; + break; + } + } + return out; +} + +int main(int argc, char** argv) +{ + if (argc < 2) { + std::cerr << "Usage:\n " << argv[0] << " out.json\n"; + return 2; + } + + const std::string out_json = argv[1]; + + auto* nist = G4NistManager::Instance(); + + // This returns all known NIST material names. + std::vector names = nist->GetNistMaterialNames(); + std::sort(names.begin(), names.end()); + + std::ofstream out(out_json); + if (!out) { + std::cerr << "Cannot write: " << out_json << "\n"; + return 2; + } + + out << std::fixed << std::setprecision(10); + out << "{\n" + << " \"schema\": \"g4_nist_export_v1\",\n" + << " \"count_requested\": " << names.size() << ",\n" + << " \"materials\": {\n"; + + bool first_mat = true; + size_t built_ok = 0; + size_t built_fail = 0; + + for (const auto& g4name : names) { + // Build the material (some may fail depending on Geant4 build/config). + G4Material* mat = nist->FindOrBuildMaterial(g4name, /*warning=*/false, /*isotopes=*/false); + if (!mat) { + ++built_fail; + continue; + } + ++built_ok; + + const std::string name = g4name; // convert G4String -> std::string + + // Export in convenient units + const double density_g_cm3 = mat->GetDensity() / (g / cm3); + const double radlen_cm = mat->GetRadlen() / cm; + const double intlen_cm = mat->GetNuclearInterLength() / cm; + + const size_t ne = mat->GetNumberOfElements(); + const auto* elems = mat->GetElementVector(); + const auto* fracs = mat->GetFractionVector(); // mass fractions (nullptr for some edge cases) + + if (!first_mat) + out << ",\n"; + first_mat = false; + + out << " \"" << json_escape(name) << "\": {\n"; + out << " \"name\": \"" << json_escape(name) << "\",\n"; + out << " \"density_g_cm3\": " << density_g_cm3 << ",\n"; + out << " \"radlen_cm\": " << radlen_cm << ",\n"; + out << " \"intlen_cm\": " << intlen_cm << ",\n"; + out << " \"elements\": [\n"; + + for (size_t i = 0; i < ne; ++i) { + const G4Element* el = (*elems)[i]; + const int Z = static_cast(el->GetZ()); + const double A_g_mol = el->GetA() / (g / mole); + const double w = fracs ? fracs[i] : 0.0; + + out << " {" + << "\"symbol\": \"" << json_escape(el->GetSymbol()) << "\", " + << "\"Z\": " << Z << ", " + << "\"A_g_mol\": " << A_g_mol << ", " + << "\"mass_fraction\": " << w + << "}"; + + if (i + 1 != ne) + out << ","; + out << "\n"; + } + + out << " ]\n"; + out << " }"; + } + + out << "\n },\n" + << " \"count_built_ok\": " << built_ok << ",\n" + << " \"count_built_fail\": " << built_fail << "\n" + << "}\n"; + + std::cerr << "Wrote: " << out_json << "\n" + << "NIST names: " << names.size() << ", built ok: " << built_ok + << ", failed: " << built_fail << "\n"; + return 0; +} \ No newline at end of file diff --git a/scripts/geometry/simulating_CAD_modules.md b/scripts/geometry/simulating_CAD_modules.md index ccd59a3523781..fe30456332ff6 100644 --- a/scripts/geometry/simulating_CAD_modules.md +++ b/scripts/geometry/simulating_CAD_modules.md @@ -6,7 +6,8 @@ These are a few notes related to the inclusion of external (CAD-described) detec 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. +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. Materials can be treated by providing a CSV file that map STEP part names to a material name. The conversion code will do it's best to find a corresponding material definition from a G4 NIST database JSON file (which can be expanded by users with custom definitions). + 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: @@ -17,7 +18,14 @@ In principle, such integration is now possible and requires the following steps: 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. + When materials are included the conversion process looks like this + ```bash + python O2-CADtoTGeo.py STEP_FILE --output-folder my_detector -o geom.C --mesh \ + --mesh-prec 0.2 \ + --materials-csv MATERIALS.csv \ --g4-nist-json ../g4_nist_database/G4_NIST_DB.json + ``` + +3. Inspection of the created geom.C file and possible manual editing/fixing of the code, in particular materials and medium objects. 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`: From dbdc1df7dbaf74cca5a2f1c755459d7991b57e8f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Tue, 10 Mar 2026 21:29:22 +0100 Subject: [PATCH 340/701] [ALICE3] Update FT3 geometry (#15126) - simplify geometry building - unify indices, variables --- .../FT3/base/include/FT3Base/FT3BaseParam.h | 3 - .../FT3/base/include/FT3Base/GeometryTGeo.h | 3 - .../include/FT3Simulation/Detector.h | 45 +- .../include/FT3Simulation/FT3Layer.h | 20 +- .../ALICE3/FT3/simulation/src/Detector.cxx | 452 ++++++------------ .../ALICE3/FT3/simulation/src/FT3Layer.cxx | 65 ++- .../ALICE3/FT3/simulation/src/FT3Module.cxx | 2 +- 7 files changed, 221 insertions(+), 369 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/FT3/base/include/FT3Base/FT3BaseParam.h b/Detectors/Upgrades/ALICE3/FT3/base/include/FT3Base/FT3BaseParam.h index b286aa068611c..7160067f075f7 100644 --- a/Detectors/Upgrades/ALICE3/FT3/base/include/FT3Base/FT3BaseParam.h +++ b/Detectors/Upgrades/ALICE3/FT3/base/include/FT3Base/FT3BaseParam.h @@ -42,9 +42,6 @@ struct FT3BaseParam : public o2::conf::ConfigurableParamHelper { Float_t etaOut = 1.5; Float_t Layerx2X0 = 0.01; - // FT3Geometry::External file - std::string configFile = ""; // Overrides geoModel parameter when provided - O2ParamDef(FT3BaseParam, "FT3Base"); }; diff --git a/Detectors/Upgrades/ALICE3/FT3/base/include/FT3Base/GeometryTGeo.h b/Detectors/Upgrades/ALICE3/FT3/base/include/FT3Base/GeometryTGeo.h index bb22af7ad7f9d..3c78850dffb55 100644 --- a/Detectors/Upgrades/ALICE3/FT3/base/include/FT3Base/GeometryTGeo.h +++ b/Detectors/Upgrades/ALICE3/FT3/base/include/FT3Base/GeometryTGeo.h @@ -101,9 +101,6 @@ class GeometryTGeo : public o2::itsmft::GeometryTGeo static const char* composeSymNameSensor(Int_t d, Int_t lr); protected: - static constexpr int MAXLAYERS = 15; ///< max number of active layers - - Int_t mNumberOfLayers; ///< number of layers static std::string sInnerVolumeName; ///< Mother inner volume name static std::string sVolumeName; ///< Mother volume name static std::string sLayerName; ///< Layer name diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/Detector.h b/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/Detector.h index a68f8cf7788b6..8bc4b7f634d7c 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/Detector.h +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/Detector.h @@ -25,7 +25,6 @@ #include "TGeoManager.h" // for gGeoManager, TGeoManager (ptr only) #include "TLorentzVector.h" // for TLorentzVector #include "TVector3.h" // for TVector3 -#include "FT3Base/FT3BaseParam.h" class FairVolume; class TGeoVolume; @@ -34,25 +33,10 @@ class TParticle; class TString; -namespace o2 -{ -namespace ft3 +namespace o2::ft3 { class GeometryTGeo; -} -} // namespace o2 -namespace o2 -{ -namespace ft3 -{ -class FT3Layer; -} -} // namespace o2 - -namespace o2 -{ -namespace ft3 -{ +class FT3BaseParam; class FT3Layer; class Detector : public o2::base::DetImpl @@ -108,8 +92,16 @@ class Detector : public o2::base::DetImpl void PostTrack() override { ; } void PreTrack() override { ; } + static constexpr int IdxForwardDisks = 0; + static constexpr int IdxBackwardDisks = 1; /// Returns the number of layers - Int_t getNumberOfLayers() const { return mNumberOfLayers; } + size_t getNumberOfLayers() const + { + if (mLayerName[IdxBackwardDisks].size() != mLayerName[IdxForwardDisks].size()) { + LOG(fatal) << "Number of layers in the two directions are different! Returning 0."; + } + return mLayerName[IdxBackwardDisks].size(); + } void buildBasicFT3(const FT3BaseParam& param); void buildFT3V1(); @@ -117,16 +109,10 @@ class Detector : public o2::base::DetImpl void buildFT3Scoping(); void buildFT3NewVacuumVessel(); void buildFT3ScopingV3(); - void buildFT3FromFile(std::string); - - GeometryTGeo* mGeometryTGeo; //! access to geometry details - - void exportLayout(); protected: std::vector mLayerID; - std::vector> mLayerName; - Int_t mNumberOfLayers; + std::array, 2> mLayerName; // Two sets of layer names, one per direction (forward/backward) private: /// this is transient data about track passing the sensor @@ -154,16 +140,15 @@ class Detector : public o2::base::DetImpl Detector& operator=(const Detector&); - std::vector> mLayers; - bool mIsPipeActivated = true; //! If Alice 3 pipe is present append inner disks to vacuum volume to avoid overlaps + std::array, 2> mLayers; // Two sets of layers, one per direction (forward/backward) + bool mIsPipeActivated = true; //! If Alice 3 pipe is present append inner disks to vacuum volume to avoid overlaps template friend class o2::base::DetImpl; ClassDefOverride(Detector, 1); }; -} // namespace ft3 -} // namespace o2 +} // namespace o2::ft3 #ifdef USESHM namespace o2 diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/FT3Layer.h b/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/FT3Layer.h index 44a0ef0f7d8bc..44fd8eb08e444 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/FT3Layer.h +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/include/FT3Simulation/FT3Layer.h @@ -36,7 +36,7 @@ class FT3Layer : public TObject FT3Layer() = default; // Sample layer constructor - FT3Layer(Int_t layerDirection, Int_t layerNumber, std::string layerName, Float_t z, Float_t rIn, Float_t rOut, Float_t Layerx2X0); + FT3Layer(Int_t layerDirection, Int_t layerNumber, std::string layerName, Float_t z, Float_t rIn, Float_t rOut, Float_t Layerx2X0, bool partOfMiddleLayers); /// Copy constructor FT3Layer(const FT3Layer&) = default; @@ -51,6 +51,7 @@ class FT3Layer : public TObject auto getInnerRadius() const { return mInnerRadius; } auto getOuterRadius() const { return mOuterRadius; } auto getDirection() const { return mDirection; } + bool getIsInMiddleLayer() const { return mIsMiddleLayer; } auto getZ() const { return mZ; } auto getx2X0() const { return mx2X0; } @@ -77,14 +78,15 @@ class FT3Layer : public TObject static TGeoMedium* medFoam; private: - Int_t mLayerNumber = -1; ///< Current layer number - Int_t mDirection; ///< Layer direction 0=Forward 1 = Backward - std::string mLayerName; ///< Current layer name - Double_t mInnerRadius; ///< Inner radius of this layer - Double_t mOuterRadius; ///< Outer radius of this layer - Double_t mZ; ///< Z position of the layer - Double_t mChipThickness; ///< Chip thickness - Double_t mx2X0; ///< Layer material budget x/X0 + Int_t mLayerNumber = -1; ///< Current layer number + Int_t mDirection; ///< Layer direction 0=Forward 1 = Backward + bool mIsMiddleLayer = true; ///< Wether this layer is part of the middle layers + std::string mLayerName; ///< Current layer name + Double_t mInnerRadius; ///< Inner radius of this layer + Double_t mOuterRadius; ///< Outer radius of this layer + Double_t mZ; ///< Z position of the layer + Double_t mChipThickness; ///< Chip thickness + Double_t mx2X0; ///< Layer material budget x/X0 ClassDefOverride(FT3Layer, 0); // ALICE 3 EndCaps geometry }; diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx index 4b139272834f1..0a93a4061ae44 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx @@ -59,97 +59,6 @@ Detector::Detector() { } -//_________________________________________________________________________________________________ -void Detector::buildFT3FromFile(std::string configFileName) -{ - // Geometry description from file. One line per disk - // z_layer r_in r_out Layerx2X0 - // This simple file reader is not failproof. Do not add empty lines! - - /* - # Sample FT3 configuration - # z_layer r_in r_out Layerx2X0 - -45.3 2.5 9.26 0.0042 - -46.7 2.5 9.26 0.0042 - -48.6 2.5 9.8 0.0042 - -50.0 2.5 9.8 0.0042 - -52.4 2.5 10.43 0.0042 - -53.8 2.5 10.43 0.0042 - -67.7 3.82 13.01 0.0042 - -69.1 3.82 13.01 0.0042 - -76.1 3.92 14.35 0.0042 - -77.5 3.92 14.35 0.0042 - */ - - mLayerName.clear(); - mLayers.clear(); - mLayerID.clear(); - mLayerName.resize(1); - mLayers.resize(1); - - LOG(info) << "Building FT3 Detector: From file"; - LOG(info) << " FT3 detector configuration: " << configFileName; - std::ifstream ifs(configFileName.c_str()); - if (!ifs.good()) { - LOG(fatal) << " Invalid FT3Base.configFile!"; - } - std::string tempstr; - float z_layer, r_in, r_out, Layerx2X0; - char delimiter; - int layerNumber = 0; - while (std::getline(ifs, tempstr)) { - if (tempstr[0] == '#') { - LOG(info) << " Comment: " << tempstr; - continue; - } - std::istringstream iss(tempstr); - iss >> z_layer; - iss >> r_in; - iss >> r_out; - iss >> Layerx2X0; - - int direction = 1; // Forwards - if (z_layer < 0) { - // Backwards - direction = 0; - } - - std::string directionName = std::to_string(direction); - std::string layerName = GeometryTGeo::getFT3LayerPattern() + directionName + std::string("_") + std::to_string(layerNumber); - mLayerName[0].push_back(layerName); - LOG(info) << "Adding Layer " << layerName << " at z = " << z_layer << " ; direction = " << direction << " ; r_in = " << r_in << " ; r_out = " << r_out << " x/X0 = " << Layerx2X0; - auto& thisLayer = mLayers[0].emplace_back(direction, layerNumber, layerName, z_layer, r_in, r_out, Layerx2X0); - layerNumber++; - } - - mNumberOfLayers = layerNumber; - LOG(info) << " Loaded FT3 Detector with " << mNumberOfLayers << " layers"; -} - -//_________________________________________________________________________________________________ -void Detector::exportLayout() -{ - // Export FT3 Layout description to file. - // One line per disk: - // z_layer r_in r_out Layerx2X0 - - std::string configFileName = "FT3_layout.cfg"; - - LOG(info) << "Exporting FT3 Detector layout to " << configFileName; - - std::ofstream fOut(configFileName.c_str(), std::ios::out); - if (!fOut) { - printf("Cannot open file\n"); - return; - } - fOut << "# z_layer r_in r_out Layerx2X0" << std::endl; - for (auto layers_dir : mLayers) { - for (auto layer : layers_dir) { - fOut << layer.getZ() << " " << layer.getInnerRadius() << " " << layer.getOuterRadius() << " " << layer.getx2X0() << std::endl; - } - } -} - //_________________________________________________________________________________________________ void Detector::buildBasicFT3(const FT3BaseParam& param) { @@ -158,28 +67,27 @@ void Detector::buildBasicFT3(const FT3BaseParam& param) LOG(info) << "Building FT3 Detector: Conical Telescope"; - auto z_first = param.z0; - auto z_length = param.zLength; - auto etaIn = param.etaIn; - auto etaOut = param.etaOut; - auto Layerx2X0 = param.Layerx2X0; - mNumberOfLayers = param.nLayers; - mLayerName.resize(2); - mLayerName[0].resize(mNumberOfLayers); - mLayerName[1].resize(mNumberOfLayers); + const int numberOfLayers = param.nLayers; + const auto z_first = param.z0; + const auto z_length = param.zLength; + const auto etaIn = param.etaIn; + const auto etaOut = param.etaOut; + const auto Layerx2X0 = param.Layerx2X0; + mLayerName[IdxBackwardDisks].resize(numberOfLayers); + mLayerName[IdxForwardDisks].resize(numberOfLayers); mLayerID.clear(); - mLayers.resize(2); - for (int direction : {0, 1}) { - for (int layerNumber = 0; layerNumber < mNumberOfLayers; layerNumber++) { - std::string layerName = GeometryTGeo::getFT3LayerPattern() + std::to_string(layerNumber + mNumberOfLayers * direction); + for (int direction : {IdxBackwardDisks, IdxForwardDisks}) { + for (int layerNumber = 0; layerNumber < numberOfLayers; layerNumber++) { + std::string layerName = GeometryTGeo::getFT3LayerPattern() + std::to_string(layerNumber + numberOfLayers * direction); mLayerName[direction][layerNumber] = layerName; // Adds evenly spaced layers - float layerZ = z_first + (layerNumber * z_length / (mNumberOfLayers - 1)) * std::copysign(1, z_first); - float rIn = std::abs(layerZ * std::tan(2.f * std::atan(std::exp(-etaIn)))); - float rOut = std::abs(layerZ * std::tan(2.f * std::atan(std::exp(-etaOut)))); - auto& thisLayer = mLayers[direction].emplace_back(direction, layerNumber, layerName, layerZ, rIn, rOut, Layerx2X0); + const float layerZ = z_first + (layerNumber * z_length / numberOfLayers) * std::copysign(1, z_first); + const float rIn = std::abs(layerZ * std::tan(2.f * std::atan(std::exp(-etaIn)))); + const float rOut = std::abs(layerZ * std::tan(2.f * std::atan(std::exp(-etaOut)))); + const bool isMiddleLayer = layerNumber < 3; + auto& thisLayer = mLayers[direction].emplace_back(direction, layerNumber, layerName, layerZ, rIn, rOut, Layerx2X0, isMiddleLayer); } } } @@ -192,10 +100,10 @@ void Detector::buildFT3V1() LOG(info) << "Building FT3 Detector: V1"; - mNumberOfLayers = 10; - float sensorThickness = 30.e-4; - float layersx2X0 = 1.e-2; - std::vector> layersConfig{ + const int numberOfLayers = 10; + const float sensorThickness = 30.e-4; + const float layersx2X0 = 1.e-2; + const std::vector> layersConfig{ {26., .5, 3., 0.1f * layersx2X0}, // {z_layer, r_in, r_out, Layerx2X0} {30., .5, 3., 0.1f * layersx2X0}, {34., .5, 3., 0.1f * layersx2X0}, @@ -207,14 +115,12 @@ void Detector::buildFT3V1() {220., 3.5, 80.f, layersx2X0}, {279., 3.5, 80.f, layersx2X0}}; - mLayerName.resize(2); - mLayerName[0].resize(mNumberOfLayers); - mLayerName[1].resize(mNumberOfLayers); + mLayerName[IdxBackwardDisks].resize(numberOfLayers); + mLayerName[IdxForwardDisks].resize(numberOfLayers); mLayerID.clear(); - mLayers.resize(2); - for (auto direction : {0, 1}) { - for (int layerNumber = 0; layerNumber < mNumberOfLayers; layerNumber++) { + for (auto direction : {IdxBackwardDisks, IdxForwardDisks}) { + for (int layerNumber = 0; layerNumber < numberOfLayers; layerNumber++) { std::string directionName = std::to_string(direction); std::string layerName = GeometryTGeo::getFT3LayerPattern() + directionName + std::string("_") + std::to_string(layerNumber); mLayerName[direction][layerNumber] = layerName; @@ -226,7 +132,8 @@ void Detector::buildFT3V1() LOG(info) << "Adding Layer " << layerName << " at z = " << z; // Add layers - auto& thisLayer = mLayers[direction].emplace_back(direction, layerNumber, layerName, z, rIn, rOut, x0); + const bool isMiddleLayer = layerNumber < 3; + auto& thisLayer = mLayers[direction].emplace_back(direction, layerNumber, layerName, z, rIn, rOut, x0, isMiddleLayer); } } } @@ -239,10 +146,10 @@ void Detector::buildFT3V3b() LOG(info) << "Building FT3 Detector: V3b"; - mNumberOfLayers = 12; + const int numberOfLayers = 12; float sensorThickness = 30.e-4; float layersx2X0 = 1.e-2; - std::vector> layersConfig{ + std::vector> layersConfig{ {26., .5, 3., 0.1f * layersx2X0}, // {z_layer, r_in, r_out, Layerx2X0} {30., .5, 3., 0.1f * layersx2X0}, {34., .5, 3., 0.1f * layersx2X0}, @@ -256,14 +163,12 @@ void Detector::buildFT3V3b() {340., 12.5, 80.f, layersx2X0}, {400., 14.7, 80.f, layersx2X0}}; - mLayerName.resize(2); - mLayerName[0].resize(mNumberOfLayers); - mLayerName[1].resize(mNumberOfLayers); + mLayerName[IdxBackwardDisks].resize(numberOfLayers); + mLayerName[IdxForwardDisks].resize(numberOfLayers); mLayerID.clear(); - mLayers.resize(2); - for (auto direction : {0, 1}) { - for (int layerNumber = 0; layerNumber < mNumberOfLayers; layerNumber++) { + for (auto direction : {IdxBackwardDisks, IdxForwardDisks}) { + for (int layerNumber = 0; layerNumber < numberOfLayers; layerNumber++) { std::string directionName = std::to_string(direction); std::string layerName = GeometryTGeo::getFT3LayerPattern() + directionName + std::string("_") + std::to_string(layerNumber); mLayerName[direction][layerNumber] = layerName; @@ -275,7 +180,8 @@ void Detector::buildFT3V3b() LOG(info) << "Adding Layer " << layerName << " at z = " << z; // Add layers - auto& thisLayer = mLayers[direction].emplace_back(direction, layerNumber, layerName, z, rIn, rOut, x0); + const bool isMiddleLayer = layerNumber < 3; + auto& thisLayer = mLayers[direction].emplace_back(direction, layerNumber, layerName, z, rIn, rOut, x0, isMiddleLayer); } } } @@ -291,10 +197,10 @@ void Detector::buildFT3NewVacuumVessel() LOG(info) << "Building FT3 Detector: After Upgrade Days March 2024 version"; - mNumberOfLayers = 9; - float sensorThickness = 30.e-4; - float layersx2X0 = 1.e-2; - std::vector> layersConfigCSide{ + const int numberOfLayers = 9; + const float sensorThickness = 30.e-4; + const float layersx2X0 = 1.e-2; + const std::vector> layersConfigCSide{ {77., 7.0, 35., layersx2X0}, // {z_layer, r_in, r_out, Layerx2X0} {100., 7.0, 35., layersx2X0}, {122., 7.0, 35., layersx2X0}, @@ -305,7 +211,7 @@ void Detector::buildFT3NewVacuumVessel() {300., 7.0, 68.f, layersx2X0}, {350., 7.0, 68.f, layersx2X0}}; - std::vector> layersConfigASide{ + const std::vector> layersConfigASide{ {77., 5.0, 35., layersx2X0}, // {z_layer, r_in, r_out, Layerx2X0} {100., 5.0, 35., layersx2X0}, {122., 5.0, 35., layersx2X0}, @@ -316,14 +222,12 @@ void Detector::buildFT3NewVacuumVessel() {300., 5.0, 68.f, layersx2X0}, {350., 5.0, 68.f, layersx2X0}}; - mLayerName.resize(2); - mLayerName[0].resize(mNumberOfLayers); - mLayerName[1].resize(mNumberOfLayers); + mLayerName[IdxBackwardDisks].resize(numberOfLayers); + mLayerName[IdxForwardDisks].resize(numberOfLayers); mLayerID.clear(); - mLayers.resize(2); - for (auto direction : {0, 1}) { - for (int layerNumber = 0; layerNumber < mNumberOfLayers; layerNumber++) { + for (auto direction : {IdxBackwardDisks, IdxForwardDisks}) { + for (int layerNumber = 0; layerNumber < numberOfLayers; layerNumber++) { std::string directionName = std::to_string(direction); std::string layerName = GeometryTGeo::getFT3LayerPattern() + directionName + std::string("_") + std::to_string(layerNumber); mLayerName[direction][layerNumber] = layerName; @@ -342,7 +246,8 @@ void Detector::buildFT3NewVacuumVessel() LOG(info) << "Adding Layer " << layerName << " at z = " << z; // Add layers - auto& thisLayer = mLayers[direction].emplace_back(direction, layerNumber, layerName, z, rIn, rOut, x0); + const bool isMiddleLayer = layerNumber < 3; + auto& thisLayer = mLayers[direction].emplace_back(direction, layerNumber, layerName, z, rIn, rOut, x0, isMiddleLayer); } } } @@ -356,52 +261,45 @@ void Detector::buildFT3ScopingV3() 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); + const int numberOfLayers = 6; + const float sensorThickness = 30.e-4; + const float layersx2X0 = 1.e-2; + using LayerConfig = std::array; // {z_layer, r_in, r_out, Layerx2X0} + const std::array layersConfigCSide{LayerConfig{77., 10.0, 35., layersx2X0}, + LayerConfig{100., 10.0, 35., layersx2X0}, + LayerConfig{122., 10.0, 35., layersx2X0}, + LayerConfig{150., 20.0, 68.f, layersx2X0}, + LayerConfig{180., 20.0, 68.f, layersx2X0}, + LayerConfig{220., 20.0, 68.f, layersx2X0}}; + + const std::array layersConfigASide{LayerConfig{77., 10.0, 35., layersx2X0}, + LayerConfig{100., 10.0, 35., layersx2X0}, + LayerConfig{122., 10.0, 35., layersx2X0}, + LayerConfig{150., 20.0, 68.f, layersx2X0}, + LayerConfig{180., 20.0, 68.f, layersx2X0}, + LayerConfig{220., 20.0, 68.f, layersx2X0}}; + const std::array enabled{true, true, true, true, true, true}; // To enable or disable layers for debug purpose + 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]; + for (int direction : {IdxBackwardDisks, IdxForwardDisks}) { + mLayerName[direction].clear(); + const std::array& layerConfig = (direction == IdxBackwardDisks) ? layersConfigCSide : layersConfigASide; + for (int layerNumber = 0; layerNumber < numberOfLayers; layerNumber++) { + if (!enabled[layerNumber]) { + continue; } - - LOG(info) << "Adding Layer " << layerName << " at z = " << z; + const std::string directionName = std::to_string(direction); + const std::string layerName = GeometryTGeo::getFT3LayerPattern() + directionName + std::string("_") + std::to_string(layerNumber); + mLayerName[direction].push_back(layerName.c_str()); + const float z = layerConfig[layerNumber][0]; + const float rIn = layerConfig[layerNumber][1]; + const float rOut = layerConfig[layerNumber][2]; + const float x0 = layerConfig[layerNumber][3]; + LOG(info) << "buildFT3ScopingV3 -> Adding Layer " << layerNumber << "/" << numberOfLayers << " " << layerName << " at z = " << z; // Add layers - auto& thisLayer = mLayers[direction].emplace_back(direction, layerNumber, layerName, z, rIn, rOut, x0); + const bool isMiddleLayer = layerNumber < 3; + auto& thisLayer = mLayers[direction].emplace_back(direction, layerNumber, layerName, z, rIn, rOut, x0, isMiddleLayer); } } } @@ -413,10 +311,10 @@ void Detector::buildFT3Scoping() LOG(info) << "Building FT3 Detector: Scoping document version"; - mNumberOfLayers = 12; - float sensorThickness = 30.e-4; - float layersx2X0 = 1.e-2; - std::vector> layersConfig{ + const int numberOfLayers = 12; + const float sensorThickness = 30.e-4; + const float layersx2X0 = 1.e-2; + const std::vector> layersConfig{ {26., .5, 2.5, 0.1f * layersx2X0}, // {z_layer, r_in, r_out, Layerx2X0} {30., .5, 2.5, 0.1f * layersx2X0}, {34., .5, 2.5, 0.1f * layersx2X0}, @@ -430,26 +328,24 @@ void Detector::buildFT3Scoping() {300., 5.0, 68.f, layersx2X0}, {350., 5.0, 68.f, layersx2X0}}; - mLayerName.resize(2); - mLayerName[0].resize(mNumberOfLayers); - mLayerName[1].resize(mNumberOfLayers); + mLayerName[IdxBackwardDisks].resize(numberOfLayers); + mLayerName[IdxForwardDisks].resize(numberOfLayers); mLayerID.clear(); - mLayers.resize(2); - for (auto direction : {0, 1}) { - for (int layerNumber = 0; layerNumber < mNumberOfLayers; layerNumber++) { + for (auto direction : {IdxBackwardDisks, IdxForwardDisks}) { + for (int layerNumber = 0; layerNumber < numberOfLayers; layerNumber++) { std::string directionName = std::to_string(direction); std::string layerName = GeometryTGeo::getFT3LayerPattern() + directionName + std::string("_") + std::to_string(layerNumber); mLayerName[direction][layerNumber] = layerName; auto& z = layersConfig[layerNumber][0]; - auto& rIn = layersConfig[layerNumber][1]; auto& rOut = layersConfig[layerNumber][2]; auto& x0 = layersConfig[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); + const bool isMiddleLayer = layerNumber < 3; + auto& thisLayer = mLayers[direction].emplace_back(direction, layerNumber, layerName, z, rIn, rOut, x0, isMiddleLayer); } } } @@ -464,24 +360,17 @@ Detector::Detector(bool active) // FT3 Base configuration parameters auto& ft3BaseParam = FT3BaseParam::Instance(); - if (ft3BaseParam.configFile != "") { - LOG(info) << "FT3 Geometry configuration file provided. Overriding FT3Base.geoModel configuration."; - buildFT3FromFile(ft3BaseParam.configFile); - - } else { - switch (ft3BaseParam.geoModel) { - case Default: - buildFT3ScopingV3(); // v3 Dec 25 - break; - case Telescope: - buildBasicFT3(ft3BaseParam); // BasicFT3 = Parametrized telescopic detector (equidistant layers) - break; - default: - LOG(fatal) << "Invalid Geometry.\n"; - break; - } + switch (ft3BaseParam.geoModel) { + case Default: + buildFT3ScopingV3(); // v3 Dec 25 + break; + case Telescope: + buildBasicFT3(ft3BaseParam); // BasicFT3 = Parametrized telescopic detector (equidistant layers) + break; + default: + LOG(fatal) << "Invalid Geometry.\n"; + break; } - exportLayout(); } //_________________________________________________________________________________________________ @@ -494,7 +383,6 @@ Detector::Detector(const Detector& rhs) { mLayerID = rhs.mLayerID; mLayerName = rhs.mLayerName; - mNumberOfLayers = rhs.mNumberOfLayers; } //_________________________________________________________________________________________________ @@ -527,7 +415,6 @@ Detector& Detector::operator=(const Detector& rhs) mLayerID = rhs.mLayerID; mLayerName = rhs.mLayerName; - mNumberOfLayers = rhs.mNumberOfLayers; mLayers = rhs.mLayers; mTrackData = rhs.mTrackData; @@ -543,8 +430,6 @@ void Detector::InitializeO2Detector() // Define the list of sensitive volumes LOG(info) << "Initialize FT3 O2Detector"; - mGeometryTGeo = GeometryTGeo::Instance(); - defineSensitiveVolumes(); } @@ -693,12 +578,10 @@ void Detector::ConstructGeometry() void Detector::createGeometry() { - mGeometryTGeo = GeometryTGeo::Instance(); - TGeoVolume* volFT3 = new TGeoVolumeAssembly(GeometryTGeo::getFT3VolPattern()); TGeoVolume* volIFT3 = new TGeoVolumeAssembly(GeometryTGeo::getFT3InnerVolPattern()); - LOG(info) << "GeometryBuilder::buildGeometry volume name = " << GeometryTGeo::getFT3VolPattern(); + LOG(info) << "FT3: createGeometry volume name = " << GeometryTGeo::getFT3VolPattern(); TGeoVolume* vALIC = gGeoManager->GetVolume("barrel"); if (!vALIC) { @@ -710,69 +593,40 @@ void Detector::createGeometry() LOG(info) << "Running simulation with no beam pipe."; } - LOG(debug) << "FT3 createGeometry: " - << Form("gGeoManager name is %s title is %s", gGeoManager->GetName(), gGeoManager->GetTitle()); - - if (mLayers.size() == 2) { // V1 and telescope - if (!A3IPvac) { - for (int direction : {0, 1}) { // Backward layers at mLayers[0]; Forward layers at mLayers[1] - std::string directionString = direction ? "Forward" : "Backward"; - LOG(info) << "Creating FT3 " << directionString << " layers:"; - for (int iLayer = 0; iLayer < mLayers[direction].size(); iLayer++) { - mLayers[direction][iLayer].createLayer(volFT3); - } - } - vALIC->AddNode(volFT3, 2, new TGeoTranslation(0., 30., 0.)); - } else { // If beampipe is enabled append inner disks to beampipe filling volume, this should be temporary. - for (int direction : {0, 1}) { - std::string directionString = direction ? "Forward" : "Backward"; - LOG(info) << "Creating FT3 " << directionString << " layers:"; - for (int iLayer = 0; iLayer < mLayers[direction].size(); iLayer++) { - if (iLayer < 3) { - mLayers[direction][iLayer].createLayer(volIFT3); - } else { - mLayers[direction][iLayer].createLayer(volFT3); - } - } - } - A3IPvac->AddNode(volIFT3, 2, new TGeoTranslation(0., 0., 0.)); - vALIC->AddNode(volFT3, 2, new TGeoTranslation(0., 30., 0.)); - } - - for (auto direction : {0, 1}) { - std::string directionString = direction ? "Forward" : "Backward"; - LOG(info) << "Registering FT3 " << directionString << " LayerIDs:"; + // This will need to adapt to the new scheme + if (!A3IPvac) { + for (int direction : {IdxBackwardDisks, IdxForwardDisks}) { // Backward layers at mLayers[0]; Forward layers at mLayers[1] + const std::string directionString = direction ? "Forward" : "Backward"; + LOG(info) << " Creating FT3 without beampipe " << directionString << " layers:"; for (int iLayer = 0; iLayer < mLayers[direction].size(); iLayer++) { - auto layerID = gMC ? TVirtualMC::GetMC()->VolId(Form("%s_%d_%d", GeometryTGeo::getFT3SensorPattern(), direction, iLayer)) : 0; - mLayerID.push_back(layerID); - LOG(info) << " " << directionString << " layer " << iLayer << " LayerID " << layerID; + mLayers[direction][iLayer].createLayer(volFT3); } } - } - - if (mLayers.size() == 1) { // All layers registered at mLayers[0], used when building from file - LOG(info) << "Creating FT3 layers:"; - if (A3IPvac) { - for (int iLayer = 0; iLayer < mLayers[0].size(); iLayer++) { - if (std::abs(mLayers[0][iLayer].getZ()) < 25) { - mLayers[0][iLayer].createLayer(volIFT3); + vALIC->AddNode(volFT3, 2, new TGeoTranslation(0., 30., 0.)); + } else { // If beampipe is enabled append inner disks to beampipe filling volume, this should be temporary. + for (int direction : {IdxBackwardDisks, IdxForwardDisks}) { + const std::string directionString = direction ? "Forward" : "Backward"; + LOG(info) << " Creating FT3 " << directionString << " layers:"; + for (int iLayer = 0; iLayer < mLayers[direction].size(); iLayer++) { + LOG(info) << " Creating " << directionString << " layer " << iLayer; + if (mLayers[direction][iLayer].getIsInMiddleLayer()) { // ML disks + mLayers[direction][iLayer].createLayer(volIFT3); } else { - mLayers[0][iLayer].createLayer(volFT3); + mLayers[direction][iLayer].createLayer(volFT3); } } - A3IPvac->AddNode(volIFT3, 2, new TGeoTranslation(0., 0., 0.)); - vALIC->AddNode(volFT3, 2, new TGeoTranslation(0., 30., 0.)); - } else { - for (int iLayer = 0; iLayer < mLayers[0].size(); iLayer++) { - mLayers[0][iLayer].createLayer(volFT3); - } - vALIC->AddNode(volFT3, 2, new TGeoTranslation(0., 30., 0.)); } - LOG(info) << "Registering FT3 LayerIDs:"; - for (int iLayer = 0; iLayer < mLayers[0].size(); iLayer++) { - auto layerID = gMC ? TVirtualMC::GetMC()->VolId(Form("%s_%d_%d", GeometryTGeo::getFT3SensorPattern(), 0, iLayer)) : 0; + A3IPvac->AddNode(volIFT3, 2, new TGeoTranslation(0., 0., 0.)); + vALIC->AddNode(volFT3, 2, new TGeoTranslation(0., 30., 0.)); + } + + for (auto direction : {IdxBackwardDisks, IdxForwardDisks}) { + std::string directionString = direction ? "Forward" : "Backward"; + LOG(info) << " Registering FT3 " << directionString << " LayerIDs for " << mLayers[direction].size() << " layers:"; + for (int iLayer = 0; iLayer < mLayers[direction].size(); iLayer++) { + auto layerID = gMC ? TVirtualMC::GetMC()->VolId(Form("%s_%d_%d", GeometryTGeo::getFT3SensorPattern(), direction, iLayer)) : 0; mLayerID.push_back(layerID); - LOG(info) << " mLayerID[" << iLayer << "] = " << layerID; + LOG(info) << " " << directionString << " layer " << iLayer << " LayerID " << layerID; } } } @@ -786,40 +640,34 @@ void Detector::defineSensitiveVolumes() TString volumeName; LOG(info) << "Adding FT3 Sensitive Volumes"; - // The names of the FT3 sensitive volumes have the format: FT3Sensor_(0,1)_(0...sNumberLayers-1) - if (mLayers.size() == 2) { - for (int direction : {0, 1}) { - for (int iLayer = 0; iLayer < mNumberOfLayers; iLayer++) { - volumeName = o2::ft3::GeometryTGeo::getFT3SensorPattern() + std::to_string(iLayer); - 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); - } + for (int direction : {IdxBackwardDisks, IdxForwardDisks}) { + for (int iLayer = 0; iLayer < getNumberOfLayers(); iLayer++) { + LOG(info) << "Adding FT3 Sensitive Volume for direction " << direction << " layer " << iLayer << "/" << getNumberOfLayers(); + volumeName = o2::ft3::GeometryTGeo::getFT3SensorPattern() + std::to_string(iLayer); + if (mLayers[direction][iLayer].getIsInMiddleLayer()) { // ML disks + const std::string sensorName = Form("%s_%d_%d", GeometryTGeo::getFT3SensorPattern(), direction, iLayer); + v = geoManager->GetVolume(sensorName.c_str()); + if (!v) { + geoManager->GetListOfVolumes()->ls(); + LOG(fatal) << "Could not find volume " << sensorName << " for direction " << direction << " layer " << 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); } } } } } - - if (mLayers.size() == 1) { - for (int iLayer = 0; iLayer < mLayers[0].size(); iLayer++) { - volumeName = o2::ft3::GeometryTGeo::getFT3SensorPattern() + std::to_string(iLayer); - v = geoManager->GetVolume(Form("%s_%d_%d", GeometryTGeo::getFT3SensorPattern(), mLayers[0][iLayer].getDirection(), iLayer)); - LOG(info) << "Adding FT3 Sensitive Volume => " << v->GetName(); - AddSensitiveVolume(v); - } - } } //_________________________________________________________________________________________________ diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx index 97f42eca6143f..1ad4d1aad1eeb 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx @@ -16,7 +16,6 @@ #include "FT3Simulation/FT3Layer.h" #include "FT3Base/GeometryTGeo.h" -#include "FT3Simulation/Detector.h" #include // for LOG @@ -55,20 +54,29 @@ 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) +FT3Layer::FT3Layer(Int_t layerDirection, Int_t layerNumber, std::string layerName, Float_t z, Float_t rIn, Float_t rOut, Float_t Layerx2X0, bool partOfMiddleLayers) { // Creates a simple parametrized EndCap layer covering the given // pseudorapidity range at the z layer position mDirection = layerDirection; mLayerNumber = layerNumber; + mIsMiddleLayer = partOfMiddleLayers; mLayerName = layerName; mZ = layerDirection ? std::abs(z) : -std::abs(z); mx2X0 = Layerx2X0; mInnerRadius = rIn; mOuterRadius = rOut; - auto Si_X0 = 9.5; + const double Si_X0 = 9.5; mChipThickness = Layerx2X0 * Si_X0; + // Sanity checks + if (std::isnan(mZ)) { + LOG(fatal) << "FT3 Layer " << mLayerNumber << " has z = NaN, which is not a valid number."; + } + if (mZ < 0.001 && mZ > -0.001) { + LOG(fatal) << "FT3 Layer " << mLayerNumber << " has z = " << mZ << " cm, which is very close to 0."; + } + LOG(info) << "Creating FT3 Layer " << mLayerNumber << " ; direction " << mDirection; LOG(info) << " Using silicon X0 = " << Si_X0 << " to emulate layer radiation length."; LOG(info) << " Layer z = " << mZ << " ; R_in = " << mInnerRadius << " ; R_out = " << mOuterRadius << " ; x2X0 = " << mx2X0 << " ; ChipThickness = " << mChipThickness; @@ -110,8 +118,8 @@ void FT3Layer::createSeparationLayer_waterCooling(TGeoVolume* motherVolume, cons FT3Layer::initialize_mat(); - double carbonFiberThickness = 0.01; - double foamSpacingThickness = 0.5; + const double carbonFiberThickness = 0.01; // cm + const double foamSpacingThickness = 0.5; // cm TGeoTube* carbonFiberLayer = new TGeoTube(mInnerRadius, mOuterRadius, carbonFiberThickness / 2); @@ -122,15 +130,15 @@ void FT3Layer::createSeparationLayer_waterCooling(TGeoVolume* motherVolume, cons carbonFiberLayerVol1->SetLineColor(kGray + 2); carbonFiberLayerVol2->SetLineColor(kGray + 2); - double zSeparation = foamSpacingThickness / 2.0 + carbonFiberThickness / 2.0; + const 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; + const double pipeOuterRadius = 0.20; + const double kaptonThickness = 0.0025; + const double pipeInnerRadius = pipeOuterRadius - kaptonThickness; + const double pipeMaxLength = mOuterRadius * 2.0; int name_it = 0; @@ -199,8 +207,8 @@ void FT3Layer::createSeparationLayer(TGeoVolume* motherVolume, const std::string FT3Layer::initialize_mat(); - double carbonFiberThickness = 0.01; - double foamSpacingThickness = 1.0; + constexpr double carbonFiberThickness = 0.01; // cm + constexpr double foamSpacingThickness = 1.0; // cm TGeoTube* carbonFiberLayer = new TGeoTube(mInnerRadius, mOuterRadius, carbonFiberThickness / 2); TGeoTube* foamLayer = new TGeoTube(mInnerRadius, mOuterRadius, foamSpacingThickness / 2); @@ -215,16 +223,19 @@ void FT3Layer::createSeparationLayer(TGeoVolume* motherVolume, const std::string foamLayerVol->SetFillColorAlpha(kBlack, 1.0); carbonFiberLayerVol2->SetLineColor(kGray + 2); - double zSeparation = foamSpacingThickness / 2.0 + carbonFiberThickness / 2.0; + const 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)); + motherVolume->AddNode(carbonFiberLayerVol1, 1, new TGeoTranslation(0, 0, 0 - zSeparation)); + motherVolume->AddNode(foamLayerVol, 1, new TGeoTranslation(0, 0, 0)); + motherVolume->AddNode(carbonFiberLayerVol2, 1, new TGeoTranslation(0, 0, 0 + zSeparation)); } void FT3Layer::createLayer(TGeoVolume* motherVolume) { - if (mLayerNumber >= 0 && mLayerNumber < 3) { + if (mLayerNumber < 0) { + LOG(fatal) << "Invalid layer number " << mLayerNumber << " for FT3 layer."; + } + if (mIsMiddleLayer) { // ML disks std::string chipName = o2::ft3::GeometryTGeo::getFT3ChipPattern() + std::to_string(mLayerNumber), sensName = Form("%s_%d_%d", GeometryTGeo::getFT3SensorPattern(), mDirection, mLayerNumber); @@ -255,7 +266,7 @@ void FT3Layer::createLayer(TGeoVolume* motherVolume) LOG(info) << "Inserting " << layerVol->GetName() << " inside " << motherVolume->GetName(); motherVolume->AddNode(layerVol, 1, FwdDiskCombiTrans); - } else if (mLayerNumber >= 3) { + } else { // OT disks FT3Module module; @@ -264,11 +275,23 @@ void FT3Layer::createLayer(TGeoVolume* motherVolume) 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); + TGeoMedium* medAir = gGeoManager->GetMedium("FT3_AIR$"); + TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, 10 * mChipThickness / 2); + TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); + layerVol->SetLineColor(kYellow + 2); + // createSeparationLayer_waterCooling(motherVolume, separationLayerName); - createSeparationLayer(motherVolume, separationLayerName); + createSeparationLayer(layerVol, 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); + module.createModule(0, mLayerNumber, mDirection, mInnerRadius, mOuterRadius, 0., "front", "rectangular", layerVol); + module.createModule(0, mLayerNumber, mDirection, mInnerRadius, mOuterRadius, 0., "back", "rectangular", layerVol); + + // Finally put everything in the mother volume + auto* FwdDiskRotation = new TGeoRotation("FwdDiskRotation", 0, 0, 180); + auto* FwdDiskCombiTrans = new TGeoCombiTrans(0, 0, mZ, FwdDiskRotation); + + LOG(info) << "Inserting " << layerVol->GetName() << " inside " << motherVolume->GetName(); + motherVolume->AddNode(layerVol, 1, FwdDiskCombiTrans); } } diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx index 9318554837706..20a481cb36046 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx @@ -199,7 +199,7 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double bottom_y_pos_value = 3.5; bottom_y_neg_value = -3.5; } else { - std::cout << "Different config - to determine offsets needed." << std::endl; + LOG(warning) << "Different config - to determine offsets needed for " << "Rin = " << Rin << " ; sensor_height = " << sensor_height << " ; sensor_width = " << sensor_width << " layer " << layerNumber; x_condition_min = -Rin; x_condition_max = Rin; adjust_bottom_y_pos = false; From 49b0cb7c06128427a857252be17d7bc77eb227ed Mon Sep 17 00:00:00 2001 From: Pavel Larionov Date: Tue, 10 Mar 2026 22:12:41 +0100 Subject: [PATCH 341/701] [ALICE3] Remove petal Z caps from vacuum vol (#15119) --- .../TRK/simulation/src/VDGeometryBuilder.cxx | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx index f487d7602619f..48cd0f37d2eb5 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx @@ -84,6 +84,9 @@ inline bool isSolidToCut(const TGeoVolume* v) if (TString(nm).BeginsWith("VD_InclinedWall")) { return true; } + if (TString(nm).Contains("_ZCap")) { + return true; + } return false; } @@ -252,10 +255,11 @@ 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 kWallThick_cm = 0.02f; // 0.2 mm static constexpr double kInnerWallRadius_cm = 0.48f; // 4.8 mm (ALWAYS cylindrical) static constexpr double kOuterWallRadius_cm = 4.8f; // 48 mm (can be changed) static constexpr double kEps_cm = 2.5e-4f; +static constexpr double kEps_100um = 0.01f; // 100 microns in cm // 3 inclined walls ("walls") specs for the full-cylinder option // Thickness in-plane (cm). This is the short half-dimension of the TGeoBBox in XY. @@ -604,8 +608,8 @@ static void addIRISServiceModulesSegmented(TGeoVolume* petalAsm, int nPetals) // --- 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) + constexpr double vacuumVesselLength = kPetalZ_cm; // cm + constexpr double vacuumVesselThickness = kWallThick_cm; // cm (0.2 mm) const double halfVess = 0.5 * vacuumVesselLength; // 38.0 cm const double gapStart = halfVess; // 38.00 const double gapEnd = halfVess + vacuumVesselThickness; // 38.08 @@ -783,8 +787,8 @@ static TGeoVolume* buildFullCylAssembly(int petalID, bool withDisks) // --- 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 zMin = -0.5 * kPetalZ_cm - 2 * kWallThick_cm; + const double zMax = +0.5 * kPetalZ_cm + 2 * kWallThick_cm; const double rIn = kInnerWallRadius_cm; const double rOut = kOuterWallRadius_cm + kWallThick_cm; From 79f51afc1674c855775048eaee2f1b794a004bf7 Mon Sep 17 00:00:00 2001 From: Andrea Sofia Triolo Date: Wed, 11 Mar 2026 03:40:28 +0100 Subject: [PATCH 342/701] ALICE3-TRK: fix y-axis orientation in the sensor local coordinate system, keeping the geometry unchanged (#15134) --- .../Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx index 53cc6ab11850d..b5bde06d09484 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx @@ -137,17 +137,17 @@ TGeoVolume* TRKLayer::createChip(std::string type) metalVol = createMetalStack("flat"); TGeoCombiTrans* transSens = new TGeoCombiTrans(); - transSens->SetTranslation(-mDeadzoneWidth / 2, -(mChipThickness - mSensorThickness) / 2, 0); // TO BE CHECKED !!! + 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 !!! + 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 !!! + transMetal->SetTranslation(0, -(mSensorThickness) / 2, 0); // TO BE CHECKED !!! LOGP(debug, "Inserting {} in {} ", metalVol->GetName(), chipVol->GetName()); chipVol->AddNode(metalVol, 1, transMetal); } else { @@ -374,7 +374,7 @@ 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 + 4, 0, 0); + TGeoRotation* rot = new TGeoRotation("rot", theta + 90 + 4, 0, 0); trans->SetRotation(rot); trans->SetTranslation(mInnerRadius * std::cos(2. * TMath::Pi() * iStave / nStaves), mInnerRadius * std::sin(2 * TMath::Pi() * iStave / nStaves), 0); @@ -408,7 +408,7 @@ 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, 0, 0); trans->SetRotation(rot); trans->SetTranslation(mInnerRadius * std::cos(2. * TMath::Pi() * iStave / nStaves), mInnerRadius * std::sin(2 * TMath::Pi() * iStave / nStaves), 0); From 90fbe62a83d5564440ad96b604b38f284178d194 Mon Sep 17 00:00:00 2001 From: Andrea Sofia Triolo Date: Wed, 11 Mar 2026 04:31:07 +0100 Subject: [PATCH 343/701] [ALICE3] TRK: fix orientation of response function both for APTS and ALICE3 response + set reasonable threshold (#15135) * ALICE3-TRK: fix orientation of response function, handling both ALICE3 and APTS response + log messages modification * ALICE3-TRK: set more reasonable threshold for the digitization process --- .../base/include/TRKBase/SegmentationChip.h | 2 +- .../include/TRKSimulation/DPLDigitizerParam.h | 4 +-- .../include/TRKSimulation/DigiParams.h | 4 +-- .../ALICE3/TRK/simulation/src/Digitizer.cxx | 27 ++++++++++--------- 4 files changed, 19 insertions(+), 18 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/SegmentationChip.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/SegmentationChip.h index 8110191931e44..7ee569c9bd8e8 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/SegmentationChip.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/SegmentationChip.h @@ -198,7 +198,7 @@ class SegmentationChip zCol = col * PitchColVD + 0.5 * (PitchColVD - constants::VD::petal::layer::length); } else if (subDetID == 1) { // ML/OT xRow = 0.5 * (constants::moduleMLOT::chip::width - constants::moduleMLOT::chip::passiveEdgeReadOut - PitchRowMLOT) - (row * PitchRowMLOT); - zCol = col * PitchRowMLOT + 0.5 * (PitchRowMLOT - constants::moduleMLOT::chip::length); + zCol = col * PitchColMLOT + 0.5 * (PitchColMLOT - constants::moduleMLOT::chip::length); } } diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DPLDigitizerParam.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DPLDigitizerParam.h index bbafcf3f8f979..15ed63e46e21f 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DPLDigitizerParam.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DPLDigitizerParam.h @@ -37,8 +37,8 @@ struct DPLDigitizerParam : public o2::conf::ConfigurableParamHelpergetDepthMax(); // 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; + mSimRespOrientation = true; /// APTS response function is flipped along x wrt the ones of ALPIDE and ALICE3 } else if (mRespName == "ALICE3") { mSimRespVDScaleX = o2::trk::constants::alice3resp::pitchX / o2::trk::SegmentationChip::PitchRowVD; mSimRespVDScaleZ = o2::trk::constants::alice3resp::pitchZ / o2::trk::SegmentationChip::PitchColVD; @@ -84,7 +86,6 @@ void Digitizer::init() } mSimRespMLOTShift = mChipSimRespMLOT->getDepthMax() - thicknessMLOT / 2.f; // the shift should be done considering the rescaling done to adapt to the wrong silicon thickness. TODO: remove the scaling factor for the depth when the silicon thickness match the simulated response - mSimRespOrientation = false; // importing the parameters from DPLDigitizerParam.h auto& dOptTRK = DPLDigitizerParam::Instance(); @@ -116,11 +117,11 @@ void Digitizer::process(const std::vector* hits, int evID, int srcID) { // digitize single event, the time must have been set beforehand - 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; + LOG(info) << " Digitizing " << mGeometry->getName() << " (ID: " << mGeometry->getDetID() + << ") hits of event " << evID << " from source " << srcID + << " at time " << mEventTime.getTimeNS() << " ROFrame = " << mNewROFrame + << " cont.mode: " << isContinuous() + << " Min/Max ROFrames " << mROFrameMin << "/" << mROFrameMax; std::cout << "Printing segmentation info: " << std::endl; SegmentationChip::Print(); @@ -154,7 +155,7 @@ void Digitizer::process(const std::vector* hits, int evID, int srcID) //_______________________________________________________________________ void Digitizer::setEventTime(const o2::InteractionTimeRecord& irt) { - LOG(info) << "Setting event time "; + LOG(info) << "Setting event time to " << irt.getTimeNS() << " ns after orbit 0 bc 0"; // assign event time in ns mEventTime = irt; if (!mParams.isContinuous()) { @@ -279,7 +280,7 @@ void Digitizer::processHit(const o2::trk::Hit& hit, uint32_t& maxFr, int evID, i return; } float timeInROF = hit.GetTime() * sec2ns; - LOG(debug) << "timeInROF: " << timeInROF; + LOG(debug) << "Hit time: " << timeInROF << " ns"; if (timeInROF > 20e3) { const int maxWarn = 10; static int warnNo = 0; @@ -292,7 +293,7 @@ void Digitizer::processHit(const o2::trk::Hit& hit, uint32_t& maxFr, int evID, i if (isContinuous()) { timeInROF += mCollisionTimeWrtROF; } - if (timeInROF < 0) { + if (mIsBeforeFirstRO && timeInROF < 0) { // disregard this hit because it comes from an event byefore readout starts and it does not effect this RO LOG(debug) << "Ignoring hit with timeInROF = " << timeInROF; return; @@ -468,7 +469,7 @@ void Digitizer::processHit(const o2::trk::Hit& hit, uint32_t& maxFr, int evID, i 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); } } } @@ -501,7 +502,6 @@ void Digitizer::processHit(const o2::trk::Hit& hit, uint32_t& maxFr, int evID, i if (mDeadChanMap && mDeadChanMap->isNoisy(chipID, rowIS, colIS)) { continue; } - registerDigits(chip, roFrameAbs, timeInROF, nFrames, rowIS, colIS, nEle, lbl); } } @@ -517,7 +517,7 @@ void Digitizer::registerDigits(o2::trk::ChipDigitsContainer& chip, uint32_t roFr LOG(debug) << "Registering digits for chip " << chip.getChipIndex() << " at ROFrame " << roFrame << " row " << row << " col " << col << " nEle " << nEle << " label " << lbl; float tStrobe = mParams.getStrobeDelay() - tInROF; // strobe start wrt signal start - for (int i = 0; i < nROF; i++) { + for (int i = 0; i < nROF; i++) { // loop on all the ROFs occupied by the same signal to calculate the charge accumulated in that ROF uint32_t roFr = roFrame + i; int nEleROF = mParams.getSignalShape().getCollectedCharge(nEle, tStrobe, tStrobe + mParams.getStrobeLength()); tStrobe += mParams.getROFrameLength(); // for the next ROF @@ -536,8 +536,9 @@ void Digitizer::registerDigits(o2::trk::ChipDigitsContainer& chip, uint32_t roFr o2::itsmft::PreDigit* pd = chip.findDigit(key); if (!pd) { chip.addDigit(key, roFr, row, col, nEleROF, lbl); - LOG(debug) << "Added digit " << key << " " << roFr << " " << row << " " << col << " " << nEleROF; + LOG(debug) << "Added digit with key: " << key << " ROF: " << roFr << " row: " << row << " col: " << col << " charge: " << nEleROF; } else { // there is already a digit at this slot, account as PreDigitExtra contribution + LOG(debug) << "Added to pre-digit with key: " << key << " ROF: " << roFr << " row: " << row << " col: " << col << " charge: " << nEleROF; pd->charge += nEleROF; if (pd->labelRef.label == lbl) { // don't store the same label twice continue; From 202d71be3c3089d47f3327e1dfde95782a77b044 Mon Sep 17 00:00:00 2001 From: Marco van Leeuwen Date: Wed, 11 Mar 2026 07:36:59 +0100 Subject: [PATCH 344/701] [ALICE 3] Implementation of peacock layour for services (#15122) * [ALICE 3] Implementation of peacock layour for services * Please consider the following formatting changes * [ALICE 3] Fixes for code checker * [ALICE 3] More fixes for code checker * [ALICE 3] Fix one more overlap in the tracker services in the LoI geometry; plus cosmetics --------- Co-authored-by: ALICE Action Bot --- Detectors/Upgrades/ALICE3/TRK/README.md | 1 + .../TRK/base/include/TRKBase/TRKBaseParam.h | 7 + .../include/TRKSimulation/TRKServices.h | 8 +- .../ALICE3/TRK/simulation/src/TRKServices.cxx | 481 +++++++++++++++++- 4 files changed, 480 insertions(+), 17 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/README.md b/Detectors/Upgrades/ALICE3/TRK/README.md index 8b3a7984bb233..9730c6f6efff7 100644 --- a/Detectors/Upgrades/ALICE3/TRK/README.md +++ b/Detectors/Upgrades/ALICE3/TRK/README.md @@ -17,6 +17,7 @@ Configurables for various sub-detectors are presented in the following Table: | `TRKBase.layoutVD` | `kIRIS4` (default), `kIRISFullCyl`, `kIRIS5`, `kIRIS4a` | [link to definitions](./base/include/TRKBase/TRKBaseParam.h) | | `TRKBase.layoutML` | `kCylinder`, `kTurboStaves` (default), `kStaggered` | | | `TRKBase.layoutOT` | `kCylinder`, `kTurboStaves`, `kStaggered` (default) | | +| `TRKBase.layoutSRV` | `kPeacockv1` (default), `kLOISymm` | `kLOISymm` produces radially symmetric service volumes, as used in the LoI | For example, a geometry with fully cylindrical tracker barrel (for all layers in VD, ML and OT) can be obtained by ```bash diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h index 232e7e04b09cd..fb67b90afa7ad 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h @@ -39,6 +39,11 @@ enum eVDLayout { kIRIS4a, }; +enum eSrvLayout { + kPeacockv1 = 0, + kLOISymm, +}; + struct TRKBaseParam : public o2::conf::ConfigurableParamHelper { std::string configFile = ""; float serviceTubeX0 = 0.02f; // X0 Al2O3 @@ -49,10 +54,12 @@ struct TRKBaseParam : public o2::conf::ConfigurableParamHelper { eLayout layoutML = kTurboStaves; // Type of segmentation for the middle layers eLayout layoutOT = kStaggered; // Type of segmentation for the outer layers eVDLayout layoutVD = kIRIS4; // VD detector layout design + eSrvLayout layoutSRV = kPeacockv1; // Layout of services eLayout getLayoutML() const { return layoutML; } eLayout getLayoutOT() const { return layoutOT; } eVDLayout getLayoutVD() const { return layoutVD; } + eSrvLayout getLayoutSRV() const { return layoutSRV; } O2ParamDef(TRKBaseParam, "TRKBase"); }; diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKServices.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKServices.h index 8dd3968743024..79033f48cb0b9 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKServices.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKServices.h @@ -51,6 +51,8 @@ class TRKServices : public FairModule void createMiddleServices(TGeoVolume* motherVolume); void createOuterDisksServices(TGeoVolume* motherVolume); void createOuterBarrelServices(TGeoVolume* motherVolume); + void createMLServicesPeacock(TGeoVolume* motherVolume); + void createOTServicesPeacock(TGeoVolume* motherVolume); void createVacuumCompositeShape(); void excavateFromVacuum(TString shapeToExcavate); void registerVacuum(TGeoVolume* motherVolume); @@ -65,12 +67,14 @@ class TRKServices : public FairModule float mColdPlateX0; // Services + float mFiberArea = 7.1e-2; // cm^2 + float mPowerBundleArea = 1.13; // cm^2 float mFiberComposition[2] = {0.5, 0.5}; // SiO2, PE - float mPowerBundleComposition[2] = {0.09, 0.91}; // Cu, PE + float mPowerBundleComposition[2] = {0.08, 0.92}; // Cu, PE (with jacket) float mPowerBundleJacketComposition[2] = {0.06, 0.94}; // Cu, PE float mWaterBundleComposition[2] = {0.56, 0.44}; // PU, H2O float mWaterBundleDiskComposition[2] = {0.44, 0.56}; // PU, H2O - float mMiddleDiskThickness = 1.0; // cm + // float mMiddleDiskThickness = 1.0; // cm std::vector mCableFanWeights = {0.5, 0.3, 0.2}; // relative weights of the fan layers ClassDefOverride(TRKServices, 1); diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx index bd27a5bc30f62..0394c59780141 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -23,6 +24,8 @@ #include +using std::string; + namespace o2 { namespace trk @@ -126,9 +129,17 @@ void TRKServices::createServices(TGeoVolume* motherVolume) { createMaterials(); createVacuumCompositeShape(); - createMiddleServices(motherVolume); - createOuterDisksServices(motherVolume); - createOuterBarrelServices(motherVolume); + auto& trkPars = TRKBaseParam::Instance(); + if (trkPars.getLayoutSRV() == kLOISymm) { + LOGP(info, "TRK services: LoI version"); + createMiddleServices(motherVolume); + createOuterDisksServices(motherVolume); + createOuterBarrelServices(motherVolume); + } else { + LOGP(info, "TRK services: Peacock layout"); + createMLServicesPeacock(motherVolume); + createOTServicesPeacock(motherVolume); + } } void TRKServices::createVacuumCompositeShape() @@ -320,10 +331,10 @@ void TRKServices::createMiddleServices(TGeoVolume* motherVolume) const float rMaxMiddleBarrelDisk = 35.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 / 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); + TGeoTube* middleBarrelConnDiskSIO2 = new TGeoTube(Form("TRK_MIDBARCONN_DISK_FIBER_SIO2sh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, siO2FiberThick / 2.); + TGeoTube* middleBarrelConnDiskPE = new TGeoTube(Form("TRK_MIDBARCONN_DISK_FIBER_PEsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, peFiberThick / 2.); + TGeoVolume* middleBarrelConnDiskSIO2Volume = new TGeoVolume(Form("TRK_MIDBARCONN_DISK_FIBER_SIO2_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), middleBarrelConnDiskSIO2, medSiO2); + TGeoVolume* middleBarrelConnDiskPEVolume = new TGeoVolume(Form("TRK_MIDBARCONN_DISK_FIBER_PE_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), middleBarrelConnDiskPE, medPE); middleBarrelConnDiskSIO2Volume->SetLineColor(kGray); middleBarrelConnDiskPEVolume->SetLineColor(kGray); auto* rot = new TGeoRotation("", 0, 0, 180); @@ -332,10 +343,10 @@ void TRKServices::createMiddleServices(TGeoVolume* motherVolume) 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 / 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); + TGeoTube* middleBarrelConnDiskCu = new TGeoTube(Form("TRK_MIDBARCONN_DISK_POWER_CUsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, cuPowerThick / 2.); + TGeoTube* middleBarrelConnDiskPEPower = new TGeoTube(Form("TRK_MIDBARCONN_DISK_POWER_PEsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, pePowerThick / 2.); + TGeoVolume* middleBarrelConnDiskCuVolume = new TGeoVolume(Form("TRK_MIDBARCONN_DISK_POWER_CU_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), middleBarrelConnDiskCu, medCu); + TGeoVolume* middleBarrelConnDiskPEPowerVolume = new TGeoVolume(Form("TRK_MIDBARCONN_DISK_POWER_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); @@ -343,14 +354,16 @@ void TRKServices::createMiddleServices(TGeoVolume* motherVolume) motherVolume->AddNode(middleBarrelConnDiskCuVolume, 1, combiTransCu); motherVolume->AddNode(middleBarrelConnDiskPEPowerVolume, 1, combiTransPEPower); - TGeoTube* middleBarrelConnDiskPU = new TGeoTube(Form("TRK_MIDBARCONN_DISK_PUsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, puCoolingThick); - TGeoTube* middleBarrelConnDiskH2O = new TGeoTube(Form("TRK_MIDBARCONN_DISK_H2Osh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, h2oCoolingThick); + TGeoTube* middleBarrelConnDiskPU = new TGeoTube(Form("TRK_MIDBARCONN_DISK_PUsh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, puCoolingThick / 2.); + TGeoTube* middleBarrelConnDiskH2O = new TGeoTube(Form("TRK_MIDBARCONN_DISK_H2Osh_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, h2oCoolingThick / 2.); TGeoVolume* middleBarrelConnDiskPUVolume = new TGeoVolume(Form("TRK_MIDBARCONN_DISK_PU_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), middleBarrelConnDiskPU, medPU); TGeoVolume* middleBarrelConnDiskH2OVolume = new TGeoVolume(Form("TRK_MIDBARCONN_DISK_H2O_%s", orientation == Orientation::kASide ? "bwd" : "fwd"), middleBarrelConnDiskH2O, medH2O); middleBarrelConnDiskPUVolume->SetLineColor(kGray); middleBarrelConnDiskH2OVolume->SetLineColor(kGray); - motherVolume->AddNode(middleBarrelConnDiskPUVolume, 1, combiTransCu); - motherVolume->AddNode(middleBarrelConnDiskH2OVolume, 1, combiTransPEPower); + auto* combiTransPU = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick + puCoolingThick / 2. + zLengthMiddleBarrel), rot); + auto* combiTransH2O = new TGeoCombiTrans(0, 0, (int)orientation * (siO2FiberThick + peFiberThick + cuPowerThick + pePowerThick + puCoolingThick + h2oCoolingThick / 2. + zLengthMiddleBarrel), rot); + motherVolume->AddNode(middleBarrelConnDiskPUVolume, 1, combiTransPU); + motherVolume->AddNode(middleBarrelConnDiskH2OVolume, 1, combiTransH2O); } // Barrel to forward connection disks @@ -448,6 +461,10 @@ void TRKServices::createMiddleServices(TGeoVolume* motherVolume) void TRKServices::createOuterBarrelServices(TGeoVolume* motherVolume) { + // This implements a service barrel around the full outer tracker which is probably not needed: + // power, data and cooling should be implemented on the staves + // Used only for 'LOI' geometry + auto& matmgr = o2::base::MaterialManager::Instance(); TGeoMedium* medSiO2 = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_SILICONDIOXIDE"); @@ -500,5 +517,439 @@ void TRKServices::createOuterBarrelServices(TGeoVolume* motherVolume) motherVolume->AddNode(outerBarrelCoolingPUVolume, 1, nullptr); motherVolume->AddNode(outerBarrelCoolingH2OVolume, 1, nullptr); } + +void TRKServices::createMLServicesPeacock(TGeoVolume* motherVolume) +{ + // This method hardcoes the yellow shape for the middle services + auto& matmgr = o2::base::MaterialManager::Instance(); + + TGeoMedium* medSiO2 = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_SILICONDIOXIDE"); + TGeoMedium* medPE = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_POLYETHYLENE"); + TGeoMedium* medCu = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_COPPER"); + TGeoMedium* medPU = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_POLYURETHANE"); + TGeoMedium* medH2O = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_WATER"); + TGeoMedium* medCFiber = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_CARBONFIBERM55J6K"); + + // Barrel service constants + const int ITBarrelnFiber = 70; + const int ITBarrelnPower = 70; + float siO2FiberAreaB = ITBarrelnFiber * mFiberArea * mFiberComposition[0]; + float peFiberAreaB = ITBarrelnFiber * mFiberArea * mFiberComposition[1]; + + float puCoolingAreaB = 0; + float h2oCoolingAreaB = 0; + float cuPowerAreaB = ITBarrelnPower * mPowerBundleArea * mPowerBundleComposition[0]; + float pePowerAreaB = ITBarrelnPower * mPowerBundleArea * mPowerBundleComposition[1]; + + // Disk service constants + const int ITDisknFiber = 3 * 24; + const int ITDisknPower = 3 * 16; + float siO2FiberAreaD = ITDisknFiber * mFiberArea * mFiberComposition[0]; + float peFiberAreaD = ITDisknFiber * mFiberArea * mFiberComposition[1]; + + float puCoolingAreaD = 0; + float h2oCoolingAreaD = 0; + float cuPowerAreaD = ITDisknPower * mPowerBundleArea * mPowerBundleComposition[0]; + float pePowerAreaD = ITDisknPower * mPowerBundleArea * mPowerBundleComposition[1]; + + // 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 = 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"); + motherVolume->AddNode(middleBarrelCarbonSupportVolume, 1, nullptr); + + // Get geometry information from TRK which is already present + float rMinMiddleServices = 35.f; + float rMinMiddleBarrel = rMinMiddleServices; + const float zLengthCylinderMiddleServices = 40.5f; + const float zLengthMiddleServices = 143.f; + + // Middle layer barrel services are only on A side + rMinMiddleServices = 35.f; + LOGP(info, "Building services for Middle Tracker rminMiddleServices"); + + // Middle barrel connection disks + const float rMinMiddleBarrelDisk = 5.68f; + const float rMaxMiddleBarrelDisk = 35.f; + const float zLengthMiddleBarrel = 64.5f; + auto orientation = Orientation::kASide; + float diskCircumference = rMaxMiddleBarrelDisk * 3.14; // Use only half circumference + + double zCur = zLengthMiddleBarrel; + double dZ = siO2FiberAreaB / diskCircumference / 2.; + TGeoTube* middleBarrelConnDiskSIO2 = new TGeoTube("TRK_MIDBARCONN_DISK_FIBER_SIO2sh", rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, dZ); + TGeoVolume* middleBarrelConnDiskSIO2Volume = new TGeoVolume("TRK_MIDBARCONN_DISK_FIBER_SIO2", middleBarrelConnDiskSIO2, medSiO2); + middleBarrelConnDiskSIO2Volume->SetLineColor(kGray); + auto* rot = new TGeoRotation("", 0, 0, 180); // Why this? + auto* combiTransSIO2 = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), rot); + + zCur += 2. * dZ; + dZ = peFiberAreaB / diskCircumference / 2.; + TGeoTube* middleBarrelConnDiskPE = new TGeoTube("TRK_MIDBARCONN_DISK_FIBER_PEsh", rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, dZ); + TGeoVolume* middleBarrelConnDiskPEVolume = new TGeoVolume("TRK_MIDBARCONN_DISK_FIBER_PE", middleBarrelConnDiskPE, medPE); + middleBarrelConnDiskPEVolume->SetLineColor(kGray); + auto* combiTransPE = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), rot); + + motherVolume->AddNode(middleBarrelConnDiskSIO2Volume, 1, combiTransSIO2); + motherVolume->AddNode(middleBarrelConnDiskPEVolume, 1, combiTransPE); + + zCur += 2. * dZ; + dZ = cuPowerAreaB / diskCircumference / 2.; + TGeoTube* middleBarrelConnDiskCu = new TGeoTube("TRK_MIDBARCONN_DISK_POWER_CUsh", rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, dZ); + TGeoVolume* middleBarrelConnDiskCuVolume = new TGeoVolume("TRK_MIDBARCONN_DISK_POWER_CU", middleBarrelConnDiskCu, medCu); + middleBarrelConnDiskCuVolume->SetLineColor(kGray); + auto* combiTransCu = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), rot); + + zCur += 2. * dZ; + dZ = pePowerAreaB / diskCircumference / 2.; + TGeoTube* middleBarrelConnDiskPEPower = new TGeoTube("TRK_MIDBARCONN_DISK_POWER_PEsh", rMinMiddleBarrelDisk, rMaxMiddleBarrelDisk, dZ); + TGeoVolume* middleBarrelConnDiskPEPowerVolume = new TGeoVolume("TRK_MIDBARCONN_DISK_POWER_PE", middleBarrelConnDiskPEPower, medPE); + middleBarrelConnDiskPEPowerVolume->SetLineColor(kGray); + auto* combiTransPEPower = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), rot); + motherVolume->AddNode(middleBarrelConnDiskCuVolume, 1, combiTransCu); + motherVolume->AddNode(middleBarrelConnDiskPEPowerVolume, 1, combiTransPEPower); + + for (auto& orientation : {Orientation::kASide, Orientation::kCSide}) { + for (int iSide = 0; iSide < 2; iSide++) { // left/right or top/bottom + float refAngle = 0; + string orLabel("A"); + if (orientation == Orientation::kCSide) { + orLabel = "C"; + refAngle = 90; + } + // Add ML Disk services + // create data fiber volumes + double rCur = rMinMiddleServices; + double dR = siO2FiberAreaD / (3.14 * rCur); + TGeoTubeSeg* middleDiskFiberSIO2 = new TGeoTubeSeg(Form("TRK_MLD_FIBER_SIO2sh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthCylinderMiddleServices, -45, 45); + TGeoVolume* middleDiskFiberSIO2Volume = new TGeoVolume(Form("TRK_MLD_FIBER_SIO2_%s%d", orLabel.c_str(), iSide), middleDiskFiberSIO2, medSiO2); + middleDiskFiberSIO2Volume->SetLineColor(kGray); + + rCur += dR; + dR = peFiberAreaD / (3.14 * rCur); + TGeoTubeSeg* middleDiskFiberPE = new TGeoTubeSeg(Form("TRK_MLD_FIBER_PEsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthCylinderMiddleServices, -45, 45); + TGeoVolume* middleDiskFiberPEVolume = new TGeoVolume(Form("TRK_MLD_FIBER_PE_%s%d", orLabel.c_str(), iSide), middleDiskFiberPE, medPE); + middleDiskFiberPEVolume->SetLineColor(kGray); + auto* combiTrans = new TGeoCombiTrans(0, 0, (int)orientation * (zLengthMiddleServices - zLengthCylinderMiddleServices), new TGeoRotation("", refAngle + iSide * 180., 0, 0)); + motherVolume->AddNode(middleDiskFiberSIO2Volume, 1, combiTrans); + motherVolume->AddNode(middleDiskFiberPEVolume, 1, combiTrans); + + // Create powerlines + rCur += dR; + dR = cuPowerAreaD / (3.14 * rCur); + TGeoTubeSeg* middleDiskPowerCu = new TGeoTubeSeg(Form("TRK_MLD_POWER_CUsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthCylinderMiddleServices, -45, 45); + TGeoVolume* middleDiskPowerCuVolume = new TGeoVolume(Form("TRK_MLD_POWER_CU_%s%d", orLabel.c_str(), iSide), middleDiskPowerCu, medCu); + middleDiskPowerCuVolume->SetLineColor(kGray); + + rCur += dR; + dR = pePowerAreaD / (3.14 * rCur); + TGeoTubeSeg* middleDiskPowerPE = new TGeoTubeSeg(Form("TRK_MLD_POWER_PEsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthCylinderMiddleServices, -45, 45); + TGeoVolume* middleDiskPowerPEVolume = new TGeoVolume(Form("TRK_MLD_POWER_PE_%s%d", orLabel.c_str(), iSide), middleDiskPowerPE, medPE); + middleDiskPowerPEVolume->SetLineColor(kGray); + + motherVolume->AddNode(middleDiskPowerCuVolume, 1, combiTrans); + motherVolume->AddNode(middleDiskPowerPEVolume, 1, combiTrans); + + if (orientation == Orientation::kASide) { + // Add Barrel services + // create data fiber volumes + rCur += dR; + dR = siO2FiberAreaB / (3.14 * rCur); + TGeoTubeSeg* middleBarrelFiberSIO2 = new TGeoTubeSeg(Form("TRK_MLB_FIBER_SIO2sh_A%d", iSide), rCur, rCur + dR, zLengthCylinderMiddleServices, -45, 45); + TGeoVolume* middleBarrelFiberSIO2Volume = new TGeoVolume(Form("TRK_MLB_FIBER_SIO2_A%d", iSide), middleBarrelFiberSIO2, medSiO2); + middleBarrelFiberSIO2Volume->SetLineColor(kGray); + + rCur += dR; + dR = peFiberAreaB / (3.14 * rCur); + TGeoTubeSeg* middleBarrelFiberPE = new TGeoTubeSeg(Form("TRK_MLB_FIBER_PEsh_A%d", iSide), rCur, rCur + dR, zLengthCylinderMiddleServices, -45, 45); + TGeoVolume* middleBarrelFiberPEVolume = new TGeoVolume(Form("TRK_MLB_FIBER_PE_A%d", iSide), middleBarrelFiberPE, medPE); + middleBarrelFiberPEVolume->SetLineColor(kGray); + auto* combiTrans = new TGeoCombiTrans(0, 0, (int)orientation * (zLengthMiddleServices - zLengthCylinderMiddleServices), new TGeoRotation(nullptr, refAngle + iSide * 180., 0, 0)); + motherVolume->AddNode(middleBarrelFiberSIO2Volume, 1, combiTrans); + motherVolume->AddNode(middleBarrelFiberPEVolume, 1, combiTrans); + + // Create powerlines + rCur += dR; + dR = cuPowerAreaB / (3.14 * rCur); + TGeoTubeSeg* middleBarrelPowerCu = new TGeoTubeSeg(Form("TRK_MLB_POWER_CUsh_A%d", iSide), rCur, rCur + dR, zLengthCylinderMiddleServices, -45, 45); + TGeoVolume* middleBarrelPowerCuVolume = new TGeoVolume(Form("TRK_MLB_POWER_CU_A%d", iSide), middleBarrelPowerCu, medCu); + middleBarrelPowerCuVolume->SetLineColor(kGray); + + rCur += dR; + dR = pePowerAreaB / (3.14 * rCur); + TGeoTubeSeg* middleBarrelPowerPE = new TGeoTubeSeg(Form("TRK_MLB_POWER_PEsh_A%d", iSide), rCur, rCur + dR, zLengthCylinderMiddleServices, -45, 45); + TGeoVolume* middleBarrelPowerPEVolume = new TGeoVolume(Form("TRK_MLB_POWER_PE_A%d", iSide), middleBarrelPowerPE, medPE); + middleBarrelPowerPEVolume->SetLineColor(kGray); + + motherVolume->AddNode(middleBarrelPowerCuVolume, 1, combiTrans); + motherVolume->AddNode(middleBarrelPowerPEVolume, 1, combiTrans); + + // TODO: add cooling ducts/pipes + } + } + } + + // Barrel to forward connection disks + // A side: barrel + disk services + // C side: only disk services + float rMaxMiddleServicesBarFwd = 74.5f; // TODO: add thickness of service barrels + diskCircumference = rMaxMiddleServicesBarFwd * 3.14; // Only half of the area is used + for (auto& orientation : {Orientation::kASide, Orientation::kCSide}) { + float refAngle = 0; + string orLabel("A"); + if (orientation == Orientation::kCSide) { + refAngle = 90; + orLabel = "C"; + } + double totalThickness = 0; + for (int iSide = 0; iSide < 2; iSide++) { + // Create fibers + double zCur = zLengthMiddleServices; // Change to f + double dZ = siO2FiberAreaD / diskCircumference / 2.; + totalThickness += 2 * dZ; + if (orientation == Orientation::kASide) { + dZ += siO2FiberAreaB / diskCircumference / 2.; + } + TGeoTubeSeg* middleBarFwdFiberSIO2 = new TGeoTubeSeg(Form("TRK_MIDBARFWD_FIBER_SIO2sh_%s%d", orLabel.c_str(), iSide), rMinMiddleBarrel, rMaxMiddleServicesBarFwd, dZ, -45, 45); + TGeoVolume* middleBarFwdFiberSIO2Volume = new TGeoVolume(Form("TRK_MIDBARFWD_FIBER_SIO2_%s%d", orLabel.c_str(), iSide), middleBarFwdFiberSIO2, medSiO2); + auto* rot = new TGeoRotation("", refAngle + iSide * 180., 0, 180.); + auto* combiTransSIO2 = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), rot); + + zCur += 2 * dZ; + dZ = peFiberAreaD / diskCircumference / 2.; + totalThickness += 2 * dZ; + if (orientation == Orientation::kASide) { + dZ += peFiberAreaB / diskCircumference / 2.; + } + TGeoTubeSeg* middleBarFwdFiberPE = new TGeoTubeSeg(Form("TRK_MIDBARFWD_FIBER_PEsh_%s%d", orLabel.c_str(), iSide), rMinMiddleBarrel, rMaxMiddleServicesBarFwd, dZ, -45, 45); + TGeoVolume* middleBarFwdFiberPEVolume = new TGeoVolume(Form("TRK_MIDBARFWD_FIBER_PE_%s%d", orLabel.c_str(), iSide), middleBarFwdFiberPE, medPE); + middleBarFwdFiberSIO2Volume->SetLineColor(kGray); + middleBarFwdFiberPEVolume->SetLineColor(kGray); + auto* combiTransPE = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), rot); + motherVolume->AddNode(middleBarFwdFiberSIO2Volume, 1, combiTransSIO2); + motherVolume->AddNode(middleBarFwdFiberPEVolume, 1, combiTransPE); + + // Create powerlines + zCur += 2 * dZ; + dZ = cuPowerAreaD / diskCircumference / 2.; + totalThickness += 2 * dZ; + if (orientation == Orientation::kASide) { + dZ += cuPowerAreaB / diskCircumference / 2.; + } + TGeoTubeSeg* middleBarFwdPowerCu = new TGeoTubeSeg(Form("TRK_MIDBARFWD_POWER_CUsh_%s%d", orLabel.c_str(), iSide), rMinMiddleBarrel, rMaxMiddleServicesBarFwd, dZ, -45, 45); + TGeoVolume* middleBarFwdPowerCuVolume = new TGeoVolume(Form("TRK_MIDBARFWD_POWER_CU_%s%d", orLabel.c_str(), iSide), middleBarFwdPowerCu, medCu); + auto* combiTransCu = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), rot); + + zCur += 2 * dZ; + dZ = pePowerAreaD / diskCircumference / 2.; + totalThickness += 2 * dZ; + if (orientation == Orientation::kASide) { + dZ += pePowerAreaB / diskCircumference / 2.; + } + TGeoTubeSeg* middleBarFwdPowerPE = new TGeoTubeSeg(Form("TRK_MIDBARFWD_POWER_PEsh_%s%d", orLabel.c_str(), iSide), rMinMiddleBarrel, rMaxMiddleServicesBarFwd, dZ, -45, 45); + TGeoVolume* middleBarFwdPowerPEVolume = new TGeoVolume(Form("TRK_MIDBARFWD_POWER_PE_%s%d", orLabel.c_str(), iSide), middleBarFwdPowerPE, medPE); + middleBarFwdPowerCuVolume->SetLineColor(kGray); + middleBarFwdPowerPEVolume->SetLineColor(kGray); + auto* combiTransPEPower = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), rot); + motherVolume->AddNode(middleBarFwdPowerCuVolume, 1, combiTransCu); + motherVolume->AddNode(middleBarFwdPowerPEVolume, 1, combiTransPEPower); + + // TODO: add cooling ducts/pipes + } + + // Forward part + float zLengthMiddleServicesFwd = 350.f - (143.f + totalThickness); + + for (int iSide = 0; iSide < 2; iSide++) { + // Create fibers + float rMinMiddleServicesFwd = 74.5f; // 74.5cm + + float translation = (int)orientation * (143.f + totalThickness + zLengthMiddleServicesFwd / 2); + + double rCur = rMinMiddleServicesFwd; + double dR = siO2FiberAreaD / (3.14 * rCur); + if (orientation == Orientation::kASide) { + dR += siO2FiberAreaB / (3.14 * rCur); + } + TGeoTubeSeg* middleFwdFiberSIO2 = new TGeoTubeSeg(Form("TRK_MIDFWD_FIBER_SIO2sh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthMiddleServicesFwd / 2, -45, 45); + TGeoVolume* middleFwdFiberSIO2Volume = new TGeoVolume(Form("TRK_MIDFWD_FIBER_SIO2_%s%d", orLabel.c_str(), iSide), middleFwdFiberSIO2, medSiO2); + middleFwdFiberSIO2Volume->SetLineColor(kGray); + + rCur += dR; + dR = peFiberAreaD / (3.14 * rCur); + if (orientation == Orientation::kASide) { + dR += peFiberAreaB / (3.14 * rCur); + } + TGeoTubeSeg* middleFwdFiberPE = new TGeoTubeSeg(Form("TRK_MIDFWD_FIBER_PEsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthMiddleServicesFwd / 2, -45, 45); + TGeoVolume* middleFwdFiberPEVolume = new TGeoVolume(Form("TRK_MIDFWD_FIBER_PE_%s%d", orLabel.c_str(), iSide), middleFwdFiberPE, medPE); + middleFwdFiberPEVolume->SetLineColor(kGray); + + auto* rot = new TGeoRotation("", refAngle + iSide * 180., 0, 0.); + auto* combiTrans = new TGeoCombiTrans(0, 0, translation, rot); + motherVolume->AddNode(middleFwdFiberSIO2Volume, 1, combiTrans); + motherVolume->AddNode(middleFwdFiberPEVolume, 1, combiTrans); + + // Create powerlines + rCur += dR; + dR = cuPowerAreaD / (3.14 * rCur); + if (orientation == Orientation::kASide) { + dR += cuPowerAreaB / (3.14 * rCur); + } + TGeoTubeSeg* middleFwdPowerCu = new TGeoTubeSeg(Form("TRK_MIDFWD_POWER_CUsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthMiddleServicesFwd / 2, -45, 45); + TGeoVolume* middleFwdPowerCuVolume = new TGeoVolume(Form("TRK_MIDFWD_POWER_CU_%s%d", orLabel.c_str(), iSide), middleFwdPowerCu, medCu); + middleFwdPowerCuVolume->SetLineColor(kGray); + + rCur += dR; + dR = pePowerAreaD / (3.14 * rCur); + if (orientation == Orientation::kASide) { + dR += pePowerAreaB / (3.14 * rCur); + } + TGeoTubeSeg* middleFwdPowerPE = new TGeoTubeSeg(Form("TRK_MIDFWD_POWER_PEsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthMiddleServicesFwd / 2, -45, 45); + TGeoVolume* middleFwdPowerPEVolume = new TGeoVolume(Form("TRK_MIDFWD_POWER_PE_%s%d", orLabel.c_str(), iSide), middleFwdPowerPE, medPE); + middleFwdPowerPEVolume->SetLineColor(kGray); + motherVolume->AddNode(middleFwdPowerCuVolume, 1, combiTrans); + motherVolume->AddNode(middleFwdPowerPEVolume, 1, combiTrans); + + // TODO: add cooling ducts/pipes + } + } +} + +void TRKServices::createOTServicesPeacock(TGeoVolume* motherVolume) +{ + // This implments the service barrels for power + data for the OT barrels and disks + // TODO: add cooling + + auto& matmgr = o2::base::MaterialManager::Instance(); + + TGeoMedium* medSiO2 = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_SILICONDIOXIDE"); + TGeoMedium* medPE = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_POLYETHYLENE"); + TGeoMedium* medCu = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_COPPER"); + TGeoMedium* medPU = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_POLYURETHANE"); + TGeoMedium* medH2O = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_WATER"); + TGeoMedium* medCFiber = matmgr.getTGeoMedium("ALICE3_TRKSERVICES_CARBONFIBERM55J6K"); + + // OT Disk service constants + const int OTDisknFiber = 3 * 51; + const int OTDisknPower = 3 * 34; + float siO2FiberAreaD = OTDisknFiber * mFiberArea * mFiberComposition[0]; + float peFiberAreaD = OTDisknFiber * mFiberArea * mFiberComposition[1]; + + float puCoolingAreaD = 0; + float h2oCoolingAreaD = 0; + float cuPowerAreaD = OTDisknPower * mPowerBundleArea * mPowerBundleComposition[0]; + float pePowerAreaD = OTDisknPower * mPowerBundleArea * mPowerBundleComposition[1]; + + // OT Barrel service constants + const int OTBarrelnFiber = 460; + const int OTBarrelnPower = 306; + float siO2FiberAreaB = OTBarrelnFiber * mFiberArea * mFiberComposition[0]; + float peFiberAreaB = OTBarrelnFiber * mFiberArea * mFiberComposition[1]; + + float puCoolingAreaB = 0; + float h2oCoolingAreaB = 0; + float cuPowerAreaB = OTBarrelnPower * mPowerBundleArea * mPowerBundleComposition[0]; + float pePowerAreaB = OTBarrelnPower * mPowerBundleArea * mPowerBundleComposition[1]; + + float rMinOuterServices = 68.5f; // 68.5cm + float zLengthOuterServices = 201.f; // 201cm + + // Carbon Fiber Cylinder support for the middle tracker + float rMinOuterCarbonSupport = 82.0f; // TODO: get more precise location + float rMaxOuterCarbonSupport = 82.4f; // 4 mm of carbon fiber + const float zLengthOuterCarbon = 280.0f; // Rough guess for now + TGeoTube* outerBarrelCarbonSupport = new TGeoTube("TRK_OT_CARBONSUPPORTsh", rMinOuterCarbonSupport, rMaxOuterCarbonSupport, zLengthOuterCarbon / 2.); + TGeoVolume* outerBarrelCarbonSupportVolume = new TGeoVolume("TRK_OT_CARBONSUPPORT", outerBarrelCarbonSupport, medCFiber); + outerBarrelCarbonSupportVolume->SetLineColor(kGray); + LOGP(info, "Creating carbon fiber support for Outer Tracker"); + motherVolume->AddNode(outerBarrelCarbonSupportVolume, 1, nullptr); + + for (auto& orientation : {Orientation::kASide, Orientation::kCSide}) { + string orLabel = "A"; + float refAngle = 0; + if (orientation == Orientation::kCSide) { + orLabel = "C"; + refAngle = 90; + } + // TODO: add cables/connections at ends of OT barrels + // Set rMin, rMax and dZ + + double rMin = 45.0; + double rMax = rMinOuterServices; + double zCur = 145.0; + double dZ = siO2FiberAreaB / (4 * 3.14 * rMax); + TGeoTube* outerBarrelFiberSIO2 = new TGeoTube(Form("TRK_OUTERBARREL_FIBER_SIO2sh_%s", orLabel.c_str()), rMin, rMax, dZ); + TGeoVolume* outerBarrelFiberSIO2Volume = new TGeoVolume(Form("TRK_OUTERBARREL_FIBER_SIO2_%s", orLabel.c_str()), outerBarrelFiberSIO2, medSiO2); + outerBarrelFiberSIO2Volume->SetLineColor(kGray); + auto* combiTrans = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), nullptr); + motherVolume->AddNode(outerBarrelFiberSIO2Volume, 1, combiTrans); + + zCur += 2 * dZ; + dZ = peFiberAreaB / (4 * 3.14 * rMax); + TGeoTube* outerBarrelFiberPE = new TGeoTube(Form("TRK_OUTERBARREL_FIBER_PEsh_%s", orLabel.c_str()), rMin, rMax, dZ); + TGeoVolume* outerBarrelFiberPEVolume = new TGeoVolume(Form("TRK_OUTERBARREL_FIBER_PE_%s", orLabel.c_str()), outerBarrelFiberPE, medPE); + outerBarrelFiberPEVolume->SetLineColor(kGray); + combiTrans = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), nullptr); + motherVolume->AddNode(outerBarrelFiberPEVolume, 1, combiTrans); + + zCur += 2 * dZ; + dZ = cuPowerAreaB / (4 * 3.14 * rMax); + TGeoTube* outerBarrelPowerCu = new TGeoTube(Form("TRK_OUTERBARREL_POWER_CUsh_%s", orLabel.c_str()), rMin, rMax, dZ); + TGeoVolume* outerBarrelPowerCuVolume = new TGeoVolume(Form("TRK_OUTERBARREL_POWER_CU_%s", orLabel.c_str()), outerBarrelPowerCu, medCu); + outerBarrelFiberSIO2Volume->SetLineColor(kGray); + combiTrans = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), nullptr); + motherVolume->AddNode(outerBarrelPowerCuVolume, 1, combiTrans); + + zCur += 2 * dZ; + dZ = pePowerAreaB / (4 * 3.14 * rMax); + TGeoTube* outerBarrelPowerPE = new TGeoTube(Form("TRK_OUTERBARREL_POWER_PEsh_%s", orLabel.c_str()), rMin, rMax, dZ); + TGeoVolume* outerBarrelPowerPEVolume = new TGeoVolume(Form("TRK_OUTERBARREL_POWER_PE_%s", orLabel.c_str()), outerBarrelPowerPE, medPE); + outerBarrelPowerPEVolume->SetLineColor(kGray); + combiTrans = new TGeoCombiTrans(0, 0, (int)orientation * (zCur + dZ), nullptr); + motherVolume->AddNode(outerBarrelPowerPEVolume, 1, combiTrans); + + for (int iSide = 0; iSide < 2; iSide++) { + // Create fibers + double rCur = rMinOuterServices; + double dR = (siO2FiberAreaD + siO2FiberAreaB) / (3.14 * rCur); + TGeoTubeSeg* outerDisksFiberSIO2 = new TGeoTubeSeg(Form("TRK_OUTERDISKS_FIBER_SIO2sh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthOuterServices / 2, -45, 45); + TGeoVolume* outerDisksFiberSIO2Volume = new TGeoVolume(Form("TRK_OUTERDISKS_FIBER_SIO2_%s%d", orLabel.c_str(), iSide), outerDisksFiberSIO2, medSiO2); + outerDisksFiberSIO2Volume->SetLineColor(kGray); + + rCur += dR; + dR = (peFiberAreaD + peFiberAreaB) / (3.14 * rCur); + TGeoTubeSeg* outerDisksFiberPE = new TGeoTubeSeg(Form("TRK_OUTERDISKS_FIBER_PEsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthOuterServices / 2, -45, 45); + TGeoVolume* outerDisksFiberPEVolume = new TGeoVolume(Form("TRK_OUTERDISKS_FIBER_PE_%s%d", orLabel.c_str(), iSide), outerDisksFiberPE, medPE); + outerDisksFiberPEVolume->SetLineColor(kGray); + + float translation = (int)orientation * (149.f + zLengthOuterServices / 2); // ±149cm + auto* combiTrans = new TGeoCombiTrans(0, 0, translation, new TGeoRotation("", refAngle + iSide * 180., 0, 0)); + motherVolume->AddNode(outerDisksFiberSIO2Volume, 1, combiTrans); + motherVolume->AddNode(outerDisksFiberPEVolume, 1, combiTrans); + + // Create power lines + rCur += dR; + dR = (cuPowerAreaD + cuPowerAreaB) / (3.14 * rCur); + TGeoTubeSeg* outerDisksPowerCu = new TGeoTubeSeg(Form("TRK_OUTERDISKS_POWER_CUsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthOuterServices / 2, -45, 45); + TGeoVolume* outerDisksPowerCuVolume = new TGeoVolume(Form("TRK_OUTERDISKS_POWER_CU_%s%d", orLabel.c_str(), iSide), outerDisksPowerCu, medCu); + outerDisksPowerCuVolume->SetLineColor(kGray); + + rCur += dR; + dR = (pePowerAreaD + pePowerAreaB) / (3.14 * rCur); + TGeoTubeSeg* outerDisksPowerPE = new TGeoTubeSeg(Form("TRK_OUTERDISKS_POWER_PEsh_%s%d", orLabel.c_str(), iSide), rCur, rCur + dR, zLengthOuterServices / 2, -45, 45); + TGeoVolume* outerDisksPowerPEVolume = new TGeoVolume(Form("TRK_OUTERDISKS_POWER_PE_%s%d", orLabel.c_str(), iSide), outerDisksPowerPE, medPE); + outerDisksPowerPEVolume->SetLineColor(kGray); + motherVolume->AddNode(outerDisksPowerCuVolume, 1, combiTrans); + motherVolume->AddNode(outerDisksPowerPEVolume, 1, combiTrans); + + // TODO: add cooling ducts/pipes + } + } +} + } // namespace trk } // namespace o2 From d886b772dea2e4b92a43b1ca6b7d44b0ecf0cf20 Mon Sep 17 00:00:00 2001 From: shahoian Date: Tue, 10 Mar 2026 22:39:04 +0100 Subject: [PATCH 345/701] Add getR method to TrackPar --- .../TrackParametrization.h | 16 ++++++++++++++++ 1 file changed, 16 insertions(+) diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h index 1d6c4d9f0e4ea..6389b037c3625 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h @@ -165,6 +165,8 @@ class TrackParametrization GPUd() value_t getTgl() const; GPUhd() value_t getQ2Pt() const; GPUd() value_t getCharge2Pt() const; + GPUd() value_t getR2() const; + GPUd() value_t getR() const; GPUd() int getAbsCharge() const; GPUd() PID getPID() const; GPUd() void setPID(const PID pid, bool passCharge = false); @@ -378,6 +380,20 @@ GPUdi() auto TrackParametrization::getCharge2Pt() const -> value_t return mAbsCharge ? mP[kQ2Pt] : 0.f; } +//____________________________________________________________ +template +GPUdi() auto TrackParametrization::getR2() const -> value_t +{ + return mX * mX + mP[kY] * mP[kY]; +} + +//____________________________________________________________ +template +GPUdi() auto TrackParametrization::getR() const -> value_t +{ + return gpu::CAMath::Sqrt(getR2()); +} + //____________________________________________________________ template GPUdi() int TrackParametrization::getAbsCharge() const From 70b4aaaeb6024370c18969187e1f7da7357f2fa4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Wed, 11 Mar 2026 17:18:40 +0100 Subject: [PATCH 346/701] [ALICE3] Update IOTOF README.md (#15146) --- Detectors/Upgrades/ALICE3/IOTOF/README.md | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/README.md b/Detectors/Upgrades/ALICE3/IOTOF/README.md index 044798076b485..fba4d12252af6 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/README.md +++ b/Detectors/Upgrades/ALICE3/IOTOF/README.md @@ -14,16 +14,16 @@ Configurables for various sub-detectors are presented in the following Table: [link to definitions](./base/include/IOTOFBase/IOTOFBaseParam.h) -| Options | Choices | Comments | -| ----------------------------- | ---------------------------------------------------------------- | ------------------------------------------- | -| `IOTOFBase.enableInnerTOF` | `true` (default), `false` | Enable inner TOF barrel layer | -| `IOTOFBase.enableOuterTOF` | `true` (default), `false` | Enable outer TOF barrel layer | -| `IOTOFBase.enableForwardTOF` | `true` (default), `false` | Enable forward TOF endcap | -| `IOTOFBase.enableBackwardTOF` | `true` (default), `false` | Enable backward TOF endcap | -| `IOTOFBase.segmentedInnerTOF` | `false` (default), `true` | Use segmented geometry for inner TOF | -| `IOTOFBase.segmentedOuterTOF` | `false` (default), `true` | Use segmented geometry for outer TOF | -| `IOTOFBase.detectorPattern` | ` ` (default), `v3b`, `v3b1a`, `v3b1b`, `v3b2a`, `v3b2b`, `v3b3` | Optional layout pattern | -| ----------------------------- | ------------------------- | ------------------------------------------- | +| Options | Choices | Comments | +| ----------------------------- | ---------------------------------------------------------------- | ---------------------------------------------- | +| `IOTOFBase.enableInnerTOF` | `true` (default), `false` | Enable inner TOF barrel layer | +| `IOTOFBase.enableOuterTOF` | `true` (default), `false` | Enable outer TOF barrel layer | +| `IOTOFBase.enableForwardTOF` | `true` (default), `false` | Enable forward TOF endcap | +| `IOTOFBase.enableBackwardTOF` | `true` (default), `false` | Enable backward TOF endcap | +| `IOTOFBase.segmentedInnerTOF` | `false` (default), `true` | Use segmented geometry for inner TOF | +| `IOTOFBase.segmentedOuterTOF` | `false` (default), `true` | Use segmented geometry for outer TOF | +| `IOTOFBase.detectorPattern` | ` ` (default), `v3b`, `v3b1a`, `v3b1b`, `v3b2a`, `v3b2b`, `v3b3` | Optional layout pattern | +| `IOTOFBase.x2x0` | `0.02` (default) | Chip thickness in fractions of the rad. lenght | For example, a geometry with fully cylindrical tracker barrel (for all layers in VD, ML and OT) can be obtained by From 2e80e74b8747160c8755b9fe4699e99a0b18aee7 Mon Sep 17 00:00:00 2001 From: Stefano Cannito <143754257+scannito@users.noreply.github.com> Date: Thu, 12 Mar 2026 04:33:31 +0100 Subject: [PATCH 347/701] [ALICE 3] TRKLayer refactoring (#15145) * TRKLayer refactoring * Naming * Fix * Fix constructors * Andrea's modifications * Naming * Removed header --- .../TRK/base/include/TRKBase/GeometryTGeo.h | 3 +- .../TRK/base/include/TRKBase/TRKBaseParam.h | 29 +- .../ALICE3/TRK/base/src/GeometryTGeo.cxx | 9 +- .../include/TRKSimulation/Detector.h | 8 +- .../include/TRKSimulation/TRKLayer.h | 112 +++- .../ALICE3/TRK/simulation/src/Detector.cxx | 128 ++--- .../ALICE3/TRK/simulation/src/TRKLayer.cxx | 544 ++++++++---------- .../TRK/simulation/src/TRKSimulationLinkDef.h | 5 +- 8 files changed, 392 insertions(+), 446 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h index d4402d66cff7e..21d86378f59ec 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h @@ -235,8 +235,7 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache 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 mLayoutOT; // Type of segmentation for the outer layers + eMLOTLayout mLayoutMLOT; // ML and OT detector layout design private: static std::unique_ptr sInstance; diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h index fb67b90afa7ad..63e961db44505 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/TRKBaseParam.h @@ -19,18 +19,6 @@ 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, @@ -39,6 +27,11 @@ enum eVDLayout { kIRIS4a, }; +enum eMLOTLayout { + kCylindrical = 0, + kSegmented, +}; + enum eSrvLayout { kPeacockv1 = 0, kLOISymm, @@ -49,16 +42,12 @@ struct TRKBaseParam : public o2::conf::ConfigurableParamHelper { float serviceTubeX0 = 0.02f; // X0 Al2O3 Bool_t irisOpen = false; - eOverallGeom overallGeom = kDefaultRadii; // Overall geometry option, to be used in Detector::buildTRKMiddleOuterLayers - - eLayout layoutML = kTurboStaves; // Type of segmentation for the middle layers - eLayout layoutOT = kStaggered; // Type of segmentation for the outer layers - eVDLayout layoutVD = kIRIS4; // VD detector layout design - eSrvLayout layoutSRV = kPeacockv1; // Layout of services + eVDLayout layoutVD = kIRIS4; // VD detector layout design + eMLOTLayout layoutMLOT = kSegmented; // ML and OT detector layout design + eSrvLayout layoutSRV = kPeacockv1; // Layout of services - eLayout getLayoutML() const { return layoutML; } - eLayout getLayoutOT() const { return layoutOT; } eVDLayout getLayoutVD() const { return layoutVD; } + eMLOTLayout getLayoutMLOT() const { return layoutMLOT; } eSrvLayout getLayoutSRV() const { return layoutSRV; } O2ParamDef(TRKBaseParam, "TRKBase"); diff --git a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx index 7b3d33ca1a75c..1a81723a18f63 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx @@ -76,10 +76,9 @@ void GeometryTGeo::Build(int loadTrans) LOGP(fatal, "Geometry is not loaded"); } - mLayoutML = o2::trk::TRKBaseParam::Instance().getLayoutML(); - mLayoutOT = o2::trk::TRKBaseParam::Instance().getLayoutOT(); + mLayoutMLOT = o2::trk::TRKBaseParam::Instance().getLayoutMLOT(); - LOG(debug) << "Layout ML: " << mLayoutML << ", Layout OL: " << mLayoutOT; + LOG(debug) << "Overall layout ML and OT: " << mLayoutMLOT; mNumberOfLayersMLOT = extractNumberOfLayersMLOT(); mNumberOfPetalsVD = extractNumberOfPetalsVD(); @@ -403,9 +402,9 @@ TString GeometryTGeo::getMatrixPath(int index) const 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 + // needed because 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 && mLayoutOT == eLayout::kCylinder)) { + if ((layer < 4 && mLayoutMLOT == eMLOTLayout::kCylindrical) || (layer > 3 && mLayoutMLOT == eMLOTLayout::kCylindrical)) { stave = 1; mod = 1; chip = 1; diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Detector.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Detector.h index 32bdc89109269..9666916800185 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Detector.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Detector.h @@ -32,6 +32,7 @@ class Detector : public o2::base::DetImpl public: Detector(bool active); Detector(); + Detector(const Detector& other); ~Detector(); // Factory method @@ -66,8 +67,7 @@ class Detector : public o2::base::DetImpl return nullptr; } - void configDefault(); - void buildTRKMiddleOuterLayers(); + void configMLOT(); void configFromFile(std::string fileName = "alice3_TRK_layout.txt"); void configToFile(std::string fileName = "alice3_TRK_layout.txt"); @@ -88,8 +88,8 @@ class Detector : public o2::base::DetImpl double mEnergyLoss; // energy loss } mTrackData; //! transient data GeometryTGeo* mGeometryTGeo; //! - std::vector* mHits; // ITSMFT ones for the moment - std::vector mLayers; + std::vector* mHits; // Derived from ITSMFT + std::vector> mLayers; TRKServices mServices; // Houses the services of the TRK, but not the Iris tracker std::vector mFirstOrLastLayers; // Names of the first or last layers diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h index 39dd7752cc010..6077d9e5f9839 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h @@ -22,57 +22,113 @@ namespace o2 { namespace trk { -class TRKLayer +enum class MatBudgetParamMode { + Thickness, + X2X0 +}; + +class TRKCylindricalLayer { public: - TRKLayer() = default; - 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; }; + TRKCylindricalLayer() = default; + TRKCylindricalLayer(int layerNumber, std::string layerName, float rInn, float length, float thickOrX2X0, MatBudgetParamMode mode); + virtual ~TRKCylindricalLayer() = default; auto getInnerRadius() const { return mInnerRadius; } auto getOuterRadius() const { return mOuterRadius; } - auto getZ() const { return constants::moduleMLOT::length * mNumberOfModules; } + auto getZ() const { return mLength; } 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); - 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: - // TGeo objects outside logical volumes can cause errors. Only used in case of kStaggered and kTurboStaves layouts - static constexpr float mLogicalVolumeThickness = 1.3; + virtual TGeoVolume* createSensor(); + virtual TGeoVolume* createMetalStack(); + virtual void createLayer(TGeoVolume* motherVolume); + protected: // User defined parameters for the layer, to be set in the constructor int mLayerNumber; std::string mLayerName; float mInnerRadius; float mOuterRadius; - int mNumberOfModules; + float mLength; float mX2X0; float mChipThickness; // Fixed parameters for the layer, to be set based on the specifications of the chip and module - eLayout mLayout = kCylinder; - float mChipWidth = constants::moduleMLOT::chip::width; - float mChipLength = constants::moduleMLOT::chip::length; - float mDeadzoneWidth = constants::moduleMLOT::chip::passiveEdgeReadOut; - float mSensorThickness = constants::moduleMLOT::silicon::thickness; - int mHalfNumberOfChips = 4; + static constexpr double sSensorThickness = constants::moduleMLOT::silicon::thickness; static constexpr float Si_X0 = 9.5f; - ClassDef(TRKLayer, 2); + ClassDef(TRKCylindricalLayer, 0); +}; + +class TRKSegmentedLayer : public TRKCylindricalLayer +{ + public: + TRKSegmentedLayer() = default; + TRKSegmentedLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode); + ~TRKSegmentedLayer() override = default; + + TGeoVolume* createSensor() override; + TGeoVolume* createDeadzone(); + TGeoVolume* createMetalStack() override; + TGeoVolume* createChip(); + TGeoVolume* createModule(); + virtual TGeoVolume* createStave() = 0; + void createLayer(TGeoVolume* motherVolume) override = 0; + + protected: + int mNumberOfModules; + + // Fixed parameters for the layer, to be set based on the specifications of the chip and module + static constexpr double sChipWidth = constants::moduleMLOT::chip::width; + static constexpr double sChipLength = constants::moduleMLOT::chip::length; + static constexpr double sDeadzoneWidth = constants::moduleMLOT::chip::passiveEdgeReadOut; + static constexpr double sModuleLength = constants::moduleMLOT::length; + static constexpr double sModuleWidth = constants::moduleMLOT::width; + static constexpr int sHalfNumberOfChips = 4; + + // TGeo objects outside logical volumes can cause errors + static constexpr float sLogicalVolumeThickness = 1.3; + + ClassDefOverride(TRKSegmentedLayer, 0); +}; + +class TRKMLLayer : public TRKSegmentedLayer +{ + public: + TRKMLLayer() = default; + TRKMLLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode); + ~TRKMLLayer() override = default; + + TGeoVolume* createStave() override; + void createLayer(TGeoVolume* motherVolume) override; + + private: + static constexpr double sStaveWidth = constants::ML::width; + + ClassDefOverride(TRKMLLayer, 0); +}; + +class TRKOTLayer : public TRKSegmentedLayer +{ + public: + TRKOTLayer() = default; + TRKOTLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode); + ~TRKOTLayer() override = default; + + TGeoVolume* createStave() override; + TGeoVolume* createHalfStave(); + void createLayer(TGeoVolume* motherVolume) override; + + private: + static constexpr double sHalfStaveWidth = constants::OT::halfstave::width; + static constexpr double sInStaveOverlap = constants::moduleMLOT::gaps::outerEdgeLongSide + constants::moduleMLOT::chip::passiveEdgeReadOut + 0.1; // 1.5mm outer-edge + 1mm deadzone + 1mm (true) overlap + static constexpr double sStaveWidth = constants::OT::width - sInStaveOverlap; + + ClassDefOverride(TRKOTLayer, 0) }; } // namespace trk diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx index 2ad1d52ba73c4..8e13d31e7915c 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx @@ -54,17 +54,24 @@ Detector::Detector(bool active) if (trkPars.configFile != "") { configFromFile(trkPars.configFile); } else { - buildTRKMiddleOuterLayers(); + configMLOT(); configToFile(); configServices(); } 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()); + LOGP(info, "Layer: {} name: {} r: {} cm | z: {} cm | thickness: {} cm", layer->getNumber(), layer->getName(), layer->getInnerRadius(), layer->getZ(), layer->getChipThickness()); } } +Detector::Detector(const Detector& other) + : o2::base::DetImpl(other), + mTrackData(), + mHits(o2::utils::createSimVector()) +{ +} + Detector::~Detector() { if (mHits) { @@ -78,78 +85,42 @@ void Detector::ConstructGeometry() createGeometry(); } -void Detector::configDefault() -{ - - // Build TRK detector according to the scoping document - - mLayers.clear(); - - LOGP(warning, "Loading Scoping Document configuration for ALICE3 TRK"); - 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::buildTRKMiddleOuterLayers() +void Detector::configMLOT() { auto& trkPars = TRKBaseParam::Instance(); mLayers.clear(); - 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); + const std::vector rInn{7.f, 9.f, 12.f, 20.f, 30.f, 45.f, 60.f, 80.f}; + const float thick = 100.e-3; + + switch (trkPars.layoutMLOT) { + case kCylindrical: { + const std::vector length{128.35f, 128.35f, 128.35f, 128.35f, 128.35f, 256.7f, 256.7f, 256.7f}; + LOGP(warning, "Loading cylindrical configuration for ALICE3 TRK"); + for (int i{0}; i < 8; ++i) { + std::string name = GeometryTGeo::getTRKLayerPattern() + std::to_string(i); + mLayers.push_back(std::make_unique(i, name, rInn[i], length[i], thick, MatBudgetParamMode::Thickness)); + } 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); + } + case kSegmented: { + const std::vector nMods{10, 10, 10, 10, 10, 20, 20, 20}; + LOGP(warning, "Loading segmented configuration for ALICE3 TRK"); + for (int i{0}; i < 8; ++i) { + std::string name = GeometryTGeo::getTRKLayerPattern() + std::to_string(i); + if (i < 4) { + mLayers.push_back(std::make_unique(i, name, rInn[i], nMods[i], thick, MatBudgetParamMode::Thickness)); + } else { + mLayers.push_back(std::make_unique(i, name, rInn[i], nMods[i], thick, MatBudgetParamMode::Thickness)); + } + } break; + } default: - LOGP(fatal, "Unknown option {} for buildTRKMiddleOuterLayers", static_cast(trkPars.overallGeom)); + LOGP(fatal, "Unknown option {} for configMLOT", static_cast(trkPars.layoutMLOT)); break; } - - // Middle layers - mLayers[0].setLayout(trkPars.layoutML); - mLayers[1].setLayout(trkPars.layoutML); - mLayers[2].setLayout(trkPars.layoutML); - mLayers[3].setLayout(trkPars.layoutML); - - // Outer tracker - mLayers[4].setLayout(trkPars.layoutOT); - mLayers[5].setLayout(trkPars.layoutOT); - mLayers[6].setLayout(trkPars.layoutOT); - mLayers[7].setLayout(trkPars.layoutOT); } void Detector::configFromFile(std::string fileName) @@ -160,6 +131,8 @@ void Detector::configFromFile(std::string fileName) LOGP(fatal, "File {} not found, aborting.", fileName); } + auto& trkPars = TRKBaseParam::Instance(); + mLayers.clear(); LOGP(info, "Overriding geometry of ALICE3 TRK using {} file.", fileName); @@ -178,7 +151,26 @@ void Detector::configFromFile(std::string fileName) while (getline(ss, substr, '\t')) { tmpBuff.push_back(std::stof(substr)); } - mLayers.emplace_back(layerCount, GeometryTGeo::getTRKLayerPattern() + std::to_string(layerCount), tmpBuff[0], tmpBuff[1], tmpBuff[2]); + + std::string name = GeometryTGeo::getTRKLayerPattern() + std::to_string(layerCount); + switch (trkPars.layoutMLOT) { + case kCylindrical: + mLayers.push_back(std::make_unique(layerCount, name, tmpBuff[0], tmpBuff[1], tmpBuff[2], MatBudgetParamMode::Thickness)); + break; + case kSegmented: { + int nMods = static_cast(tmpBuff[1]); + if (layerCount < 4) { + mLayers.push_back(std::make_unique(layerCount, name, tmpBuff[0], nMods, tmpBuff[2], MatBudgetParamMode::Thickness)); + } else { + mLayers.push_back(std::make_unique(layerCount, name, tmpBuff[0], nMods, tmpBuff[2], MatBudgetParamMode::Thickness)); + } + break; + } + default: + LOGP(fatal, "Unknown option {} for configMLOT", static_cast(trkPars.layoutMLOT)); + break; + } + ++layerCount; } } @@ -188,8 +180,8 @@ void Detector::configToFile(std::string fileName) LOGP(info, "Exporting TRK Detector layout to {}", fileName); std::ofstream conFile(fileName.c_str(), std::ios::out); conFile << "/// TRK configuration file: inn_radius z_length lay_thickness" << std::endl; - for (auto layer : mLayers) { - conFile << layer.getInnerRadius() << "\t" << layer.getZ() << "\t" << layer.getChipThickness() << std::endl; + for (const auto& layer : mLayers) { + conFile << layer->getInnerRadius() << "\t" << layer->getZ() << "\t" << layer->getChipThickness() << std::endl; } } @@ -254,7 +246,7 @@ void Detector::createGeometry() vTRK->SetTitle(vstrng); for (auto& layer : mLayers) { - layer.createLayer(vTRK); + layer->createLayer(vTRK); } // Add service for inner tracker diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx index b5bde06d09484..39c7b3598d19b 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx @@ -25,400 +25,308 @@ namespace o2 { namespace trk { -TRKLayer::TRKLayer(int layerNumber, std::string layerName, float rInn, float rOut, int numberOfModules, float layerX2X0) - : mLayerNumber(layerNumber), mLayerName(layerName), mInnerRadius(rInn), mOuterRadius(rOut), mNumberOfModules(numberOfModules), mX2X0(layerX2X0) +TRKCylindricalLayer::TRKCylindricalLayer(int layerNumber, std::string layerName, float rInn, float length, float thickOrX2X0, MatBudgetParamMode mode) + : mLayerNumber(layerNumber), mLayerName(layerName), mInnerRadius(rInn), mLength(length) { - mChipThickness = mX2X0 * Si_X0; - LOGP(info, "Creating layer: id: {} rInner: {} rOuter: {} zLength: {} x2X0: {}", mLayerNumber, mInnerRadius, mOuterRadius, getZ(), mX2X0); -} + if (mode == MatBudgetParamMode::Thickness) { + mChipThickness = thickOrX2X0; + mX2X0 = thickOrX2X0 / Si_X0; + mOuterRadius = rInn + thickOrX2X0; + } else if (mode == MatBudgetParamMode::X2X0) { + mX2X0 = thickOrX2X0; + mChipThickness = thickOrX2X0 * Si_X0; + mOuterRadius = rInn + thickOrX2X0 * Si_X0; + } -TRKLayer::TRKLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thick) - : mLayerNumber(layerNumber), mLayerName(layerName), mInnerRadius(rInn), mNumberOfModules(numberOfModules), mChipThickness(thick) -{ - mOuterRadius = rInn + thick; - mX2X0 = mChipThickness / Si_X0; - LOGP(info, "Creating layer: id: {} rInner: {} rOuter: {} zLength: {} x2X0: {}", mLayerNumber, mInnerRadius, mOuterRadius, getZ(), mX2X0); + LOGP(info, "Creating layer: id: {} rInner: {} rOuter: {} zLength: {} x2X0: {}", mLayerNumber, mInnerRadius, mOuterRadius, mLength, mX2X0); } -TGeoVolume* TRKLayer::createSensor(std::string type) +TGeoVolume* TRKCylindricalLayer::createSensor() { TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); std::string sensName = GeometryTGeo::getTRKSensorPattern() + std::to_string(mLayerNumber); - - TGeoShape* sensor; - - if (type == "cylinder") { - sensor = new TGeoTube(mInnerRadius, mInnerRadius + mSensorThickness, (constants::moduleMLOT::length * mNumberOfModules) / 2); // TO BE CHECKED !!! - } else if (type == "flat") { - sensor = new TGeoBBox((mChipWidth - mDeadzoneWidth) / 2, mSensorThickness / 2, mChipLength / 2); // TO BE CHECKED !!! - } else { - LOGP(fatal, "Sensor of type '{}' is not implemented", type); - } - + TGeoShape* sensor = new TGeoTube(mInnerRadius, mInnerRadius + sSensorThickness, mLength / 2); TGeoVolume* sensVol = new TGeoVolume(sensName.c_str(), sensor, medSi); sensVol->SetLineColor(kYellow); return sensVol; }; -TGeoVolume* TRKLayer::createDeadzone(std::string type) +TGeoVolume* TRKCylindricalLayer::createMetalStack() { TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); - std::string deadName = GeometryTGeo::getTRKDeadzonePattern() + std::to_string(mLayerNumber); + std::string metalName = GeometryTGeo::getTRKMetalStackPattern() + std::to_string(mLayerNumber); + TGeoShape* metalStack = new TGeoTube(mInnerRadius + sSensorThickness, mInnerRadius + mChipThickness, mLength / 2); + TGeoVolume* metalVol = new TGeoVolume(metalName.c_str(), metalStack, medSi); + metalVol->SetLineColor(kGray); - TGeoShape* deadzone; + return metalVol; +}; - 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); - } +void TRKCylindricalLayer::createLayer(TGeoVolume* motherVolume) +{ + TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); + TGeoTube* layer = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, mLength / 2); + TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); + layerVol->SetLineColor(kYellow); + + TGeoVolume* sensVol = createSensor(); + LOGP(debug, "Inserting {} in {} ", sensVol->GetName(), layerVol->GetName()); + layerVol->AddNode(sensVol, 1, nullptr); + + TGeoVolume* metalVol = createMetalStack(); + LOGP(debug, "Inserting {} in {} ", metalVol->GetName(), layerVol->GetName()); + layerVol->AddNode(metalVol, 1, nullptr); + + LOGP(debug, "Inserting {} in {} ", layerVol->GetName(), motherVolume->GetName()); + motherVolume->AddNode(layerVol, 1, nullptr); +} + +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +TRKSegmentedLayer::TRKSegmentedLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode) + : TRKCylindricalLayer(layerNumber, layerName, rInn, numberOfModules * sModuleLength, thickOrX2X0, mode), mNumberOfModules(numberOfModules) +{ +} + +TGeoVolume* TRKSegmentedLayer::createSensor() +{ + TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + std::string sensName = GeometryTGeo::getTRKSensorPattern() + std::to_string(mLayerNumber); + TGeoShape* sensor = new TGeoBBox((sChipWidth - sDeadzoneWidth) / 2, sSensorThickness / 2, sChipLength / 2); + TGeoVolume* sensVol = new TGeoVolume(sensName.c_str(), sensor, medSi); + sensVol->SetLineColor(kYellow); + + return sensVol; +} +TGeoVolume* TRKSegmentedLayer::createDeadzone() +{ + TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + std::string deadName = GeometryTGeo::getTRKDeadzonePattern() + std::to_string(mLayerNumber); + TGeoShape* deadzone = new TGeoBBox(sDeadzoneWidth / 2, sSensorThickness / 2, sChipLength / 2); TGeoVolume* deadVol = new TGeoVolume(deadName.c_str(), deadzone, medSi); deadVol->SetLineColor(kGray); return deadVol; -}; +} -TGeoVolume* TRKLayer::createMetalStack(std::string type) +TGeoVolume* TRKSegmentedLayer::createMetalStack() { 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); - } - + TGeoShape* metalStack = new TGeoBBox(sChipWidth / 2, (mChipThickness - sSensorThickness) / 2, sChipLength / 2); TGeoVolume* metalVol = new TGeoVolume(metalName.c_str(), metalStack, medSi); metalVol->SetLineColor(kGray); return metalVol; -}; +} -TGeoVolume* TRKLayer::createChip(std::string type) +TGeoVolume* TRKSegmentedLayer::createChip() { TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); 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, (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") { - 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); - } - + TGeoShape* chip = new TGeoBBox(sChipWidth / 2, mChipThickness / 2, sChipLength / 2); + TGeoVolume* chipVol = new TGeoVolume(chipName.c_str(), chip, medSi); chipVol->SetLineColor(kYellow); + TGeoVolume* sensVol = createSensor(); + TGeoCombiTrans* transSens = new TGeoCombiTrans(); + // transSens->SetTranslation(-sDeadzoneWidth / 2, -(mChipThickness - sSensorThickness) / 2, 0); + transSens->SetTranslation(-sDeadzoneWidth / 2, (mChipThickness - sSensorThickness) / 2, 0); + LOGP(debug, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); + chipVol->AddNode(sensVol, 1, transSens); + + TGeoVolume* deadVol = createDeadzone(); + TGeoCombiTrans* transDead = new TGeoCombiTrans(); + // transDead->SetTranslation((sChipWidth - sDeadzoneWidth) / 2, -(mChipThickness - sSensorThickness) / 2, 0); + transDead->SetTranslation((sChipWidth - sDeadzoneWidth) / 2, (mChipThickness - sSensorThickness) / 2, 0); + LOGP(debug, "Inserting {} in {} ", deadVol->GetName(), chipVol->GetName()); + chipVol->AddNode(deadVol, 1, transDead); + + TGeoVolume* metalVol = createMetalStack(); + TGeoCombiTrans* transMetal = new TGeoCombiTrans(); + // transMetal->SetTranslation(0, sSensorThickness / 2, 0); + transMetal->SetTranslation(0, -sSensorThickness / 2, 0); + LOGP(debug, "Inserting {} in {} ", metalVol->GetName(), chipVol->GetName()); + chipVol->AddNode(metalVol, 1, transMetal); + return chipVol; } -TGeoVolume* TRKLayer::createModule(std::string type) +TGeoVolume* TRKSegmentedLayer::createModule() { TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); std::string moduleName = GeometryTGeo::getTRKModulePattern() + std::to_string(mLayerNumber); + TGeoShape* module = new TGeoBBox(sModuleWidth / 2, mChipThickness / 2, sModuleLength / 2); + TGeoVolume* moduleVol = new TGeoVolume(moduleName.c_str(), module, medSi); + moduleVol->SetLineColor(kYellow); - 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); + for (int iChip = 0; iChip < sHalfNumberOfChips; iChip++) { + TGeoVolume* chipVolLeft = createChip(); + double xLeft = -sModuleWidth / 2 + constants::moduleMLOT::gaps::outerEdgeLongSide + constants::moduleMLOT::chip::width / 2; + double zLeft = -sModuleLength / 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); + TGeoRotation* rot = new TGeoRotation(); + rot->RotateY(180); + transLeft->SetRotation(rot); + LOGP(debug, "Inserting {} in {} ", chipVolLeft->GetName(), moduleVol->GetName()); + moduleVol->AddNode(chipVolLeft, iChip * 2, transLeft); + + TGeoVolume* chipVolRight = createChip(); + double xRight = +sModuleWidth / 2 - constants::moduleMLOT::gaps::outerEdgeLongSide - constants::moduleMLOT::chip::width / 2; + double zRight = -sModuleLength / 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); + LOGP(debug, "Inserting {} in {} ", chipVolRight->GetName(), moduleVol->GetName()); + moduleVol->AddNode(chipVolRight, iChip * 2 + 1, transRight); } - 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; +TRKMLLayer::TRKMLLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode) + : TRKSegmentedLayer(layerNumber, layerName, rInn, numberOfModules, thickOrX2X0, mode) +{ } -TGeoVolume* TRKLayer::createStave(std::string type) +TGeoVolume* TRKMLLayer::createStave() { TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); std::string staveName = GeometryTGeo::getTRKStavePattern() + std::to_string(mLayerNumber); + TGeoShape* stave = new TGeoBBox(sStaveWidth / 2, mChipThickness / 2, mLength / 2); + TGeoVolume* staveVol = new TGeoVolume(staveName.c_str(), stave, medAir); + staveVol->SetLineColor(kYellow); - TGeoShape* stave; - TGeoVolume* staveVol; - - double staveLength = constants::moduleMLOT::length * mNumberOfModules; - - if (type == "cylinder") { - stave = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, staveLength / 2); - staveVol = new TGeoVolume(staveName.c_str(), stave, medAir); - - TGeoVolume* moduleVol = createModule("cylinder"); + for (int iModule = 0; iModule < mNumberOfModules; iModule++) { + TGeoVolume* moduleVol = createModule(); + double zPos = -0.5 * mNumberOfModules * sModuleLength + (iModule + 0.5) * sModuleLength; + TGeoCombiTrans* trans = new TGeoCombiTrans(); + trans->SetTranslation(0, 0, zPos); LOGP(debug, "Inserting {} in {} ", moduleVol->GetName(), staveVol->GetName()); - staveVol->AddNode(moduleVol, 1, nullptr); - } else if (type == "flat") { - 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); + staveVol->AddNode(moduleVol, iModule, trans); + } - for (int iModule = 0; iModule < mNumberOfModules; iModule++) { - TGeoVolume* moduleVol = createModule("flat"); + return staveVol; +} - // Put the modules in the correct position - double zPos = -0.5 * mNumberOfModules * moduleLength + (iModule + 0.5) * moduleLength; +void TRKMLLayer::createLayer(TGeoVolume* motherVolume) +{ + TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); + TGeoTube* layer = new TGeoTube(mInnerRadius - 0.333 * sLogicalVolumeThickness, mInnerRadius + 0.667 * sLogicalVolumeThickness, mLength / 2); + TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); + layerVol->SetLineColor(kYellow); - TGeoCombiTrans* trans = new TGeoCombiTrans(); - trans->SetTranslation(0, 0, zPos); // TO BE CHECKED !!! + // Compute the number of staves + int nStaves = (int)std::ceil(mInnerRadius * 2 * TMath::Pi() / sStaveWidth); + 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(sStaveWidth / 2 / mInnerRadius); + double st = std::sin(theta); + double ct = std::cos(theta); + double theta2 = std::atan((mInnerRadius * st - sStaveWidth / 2 * ct) / (mInnerRadius * ct + sStaveWidth / 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(); + TGeoCombiTrans* trans = new TGeoCombiTrans(); + double theta = 360. * iStave / nStaves; + // TGeoRotation* rot = new TGeoRotation("rot", theta - 90 + 4, 0, 0); + TGeoRotation* rot = new TGeoRotation("rot", theta + 90 + 4, 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(debug, "Inserting {} in {} ", staveVol->GetName(), layerVol->GetName()); + layerVol->AddNode(staveVol, iStave, trans); + } - LOGP(debug, "Inserting {} in {} ", moduleVol->GetName(), staveVol->GetName()); - staveVol->AddNode(moduleVol, iModule, trans); - } - } else if (type == "staggered") { - 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; + LOGP(debug, "Inserting {} in {} ", layerVol->GetName(), motherVolume->GetName()); + motherVolume->AddNode(layerVol, 1, nullptr); +} - staveVol = new TGeoVolumeAssembly(staveName.c_str()); +///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Put the half staves in the correct position - TGeoVolume* halfStaveVolLeft = createHalfStave("flat"); - TGeoVolume* halfStaveVolRight = createHalfStave("flat"); +TRKOTLayer::TRKOTLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode) + : TRKSegmentedLayer(layerNumber, layerName, rInn, numberOfModules, thickOrX2X0, mode) +{ +} - TGeoCombiTrans* transLeft = new TGeoCombiTrans(); - 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); +TGeoVolume* TRKOTLayer::createHalfStave() +{ + TGeoMedium* medSi = gGeoManager->GetMedium("TRK_SILICON$"); + std::string halfStaveName = GeometryTGeo::getTRKHalfStavePattern() + std::to_string(mLayerNumber); + TGeoShape* halfStave = new TGeoBBox(sHalfStaveWidth / 2, mChipThickness / 2, mLength / 2); + TGeoVolume* halfStaveVol = new TGeoVolume(halfStaveName.c_str(), halfStave, medSi); + halfStaveVol->SetLineColor(kYellow); - TGeoCombiTrans* transRight = new TGeoCombiTrans(); - 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); + for (int iModule = 0; iModule < mNumberOfModules; iModule++) { + TGeoVolume* moduleVol = createModule(); + double zPos = -0.5 * mNumberOfModules * sModuleLength + (iModule + 0.5) * sModuleLength; + TGeoCombiTrans* trans = new TGeoCombiTrans(); + trans->SetTranslation(0, 0, zPos); + LOGP(debug, "Inserting {} in {} ", moduleVol->GetName(), halfStaveVol->GetName()); + halfStaveVol->AddNode(moduleVol, iModule, trans); } - staveVol->SetLineColor(kYellow); - - return staveVol; + return halfStaveVol; } -void TRKLayer::createLayer(TGeoVolume* motherVolume) +TGeoVolume* TRKOTLayer::createStave() { - TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); + std::string staveName = GeometryTGeo::getTRKStavePattern() + std::to_string(mLayerNumber); + TGeoVolume* staveVol = new TGeoVolumeAssembly(staveName.c_str()); - double layerThickness = mChipThickness; - if (mLayout != eLayout::kCylinder) { - layerThickness = mLogicalVolumeThickness; - } + TGeoVolume* halfStaveVolLeft = createHalfStave(); + TGeoCombiTrans* transLeft = new TGeoCombiTrans(); + transLeft->SetTranslation(-(sHalfStaveWidth - sInStaveOverlap) / 2, 0, 0); + LOGP(debug, "Inserting {} in {} ", halfStaveVolLeft->GetName(), staveVol->GetName()); + staveVol->AddNode(halfStaveVolLeft, 0, transLeft); - TGeoTube* layer; - TGeoVolume* layerVol; + TGeoVolume* halfStaveVolRight = createHalfStave(); + TGeoCombiTrans* transRight = new TGeoCombiTrans(); + transRight->SetTranslation((sHalfStaveWidth - sInStaveOverlap) / 2, 0.2, 0); + LOGP(debug, "Inserting {} in {} ", halfStaveVolRight->GetName(), staveVol->GetName()); + staveVol->AddNode(halfStaveVolRight, 1, transRight); - double layerLength = constants::moduleMLOT::length * mNumberOfModules; + return staveVol; +} - if (mLayout == eLayout::kCylinder) { - layer = new TGeoTube(mInnerRadius, mInnerRadius + mChipThickness, layerLength / 2); - layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); +void TRKOTLayer::createLayer(TGeoVolume* motherVolume) +{ + TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); + TGeoTube* layer = new TGeoTube(mInnerRadius - 0.333 * sLogicalVolumeThickness, mInnerRadius + 0.667 * sLogicalVolumeThickness, mLength / 2); + TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); + layerVol->SetLineColor(kYellow); - TGeoVolume* staveVol = createStave("cylinder"); + // Compute the number of staves + int nStaves = (int)std::ceil(mInnerRadius * 2 * TMath::Pi() / sStaveWidth); + 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(sStaveWidth / 2 / mInnerRadius); + double st = std::sin(theta); + double ct = std::cos(theta); + double theta2 = std::atan((mInnerRadius * st - sStaveWidth / 2 * ct) / (mInnerRadius * ct + sStaveWidth / 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(); + 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, 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(debug, "Inserting {} in {} ", staveVol->GetName(), layerVol->GetName()); - layerVol->AddNode(staveVol, 1, nullptr); - } else if (mLayout == eLayout::kTurboStaves) { - double staveWidth = constants::ML::width; // Each stave has two modules (based on the LOI design) - - if (mInnerRadius > 25) { - staveWidth = constants::OT::width; // Outer layers have two modules per stave - } - - 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(staveWidth / 2 / mInnerRadius); - double st = std::sin(theta); - double ct = std::cos(theta); - 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"); - - // 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 + 4, 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(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 - 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(staveWidth / 2 / mInnerRadius); - double st = std::sin(theta); - double ct = std::cos(theta); - 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("staggered"); - - // 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); - trans->SetRotation(rot); - trans->SetTranslation(mInnerRadius * std::cos(2. * TMath::Pi() * iStave / nStaves), mInnerRadius * std::sin(2 * TMath::Pi() * iStave / nStaves), 0); - - LOGP(debug, "Inserting {} in {} ", staveVol->GetName(), layerVol->GetName()); - layerVol->AddNode(staveVol, iStave, trans); - } - } else { - LOGP(fatal, "Layout not implemented"); + layerVol->AddNode(staveVol, iStave, trans); } - layerVol->SetLineColor(kYellow); LOGP(debug, "Inserting {} in {} ", layerVol->GetName(), motherVolume->GetName()); motherVolume->AddNode(layerVol, 1, nullptr); diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h index fec9cb6631a6f..282fc72becc52 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKSimulationLinkDef.h @@ -18,7 +18,10 @@ #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::TRKCylindricalLayer + ; +#pragma link C++ class o2::trk::TRKSegmentedLayer + ; +#pragma link C++ class o2::trk::TRKMLLayer + ; +#pragma link C++ class o2::trk::TRKOTLayer + ; #pragma link C++ class o2::trk::VDLayer + ; #pragma link C++ class o2::trk::TRKServices + ; #pragma link C++ class o2::trk::Detector + ; From b4da18e21dc4928048813bcd57c563bb8de7f165 Mon Sep 17 00:00:00 2001 From: Ernst Hellbar Date: Wed, 11 Mar 2026 19:44:53 +0100 Subject: [PATCH 348/701] full_system_test.sh: add --shm-segment-size to MCTracks-to-AO2D conversion tool workflow --- prodtests/full_system_test.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/prodtests/full_system_test.sh b/prodtests/full_system_test.sh index 07ccdf01d4566..24215276fd463 100755 --- a/prodtests/full_system_test.sh +++ b/prodtests/full_system_test.sh @@ -169,7 +169,7 @@ 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" +taskwrapper kine2aod.log "o2-sim-kine-publisher --shm-segment-size $SHMSIZE -b --kineFileName o2sim --aggregate-timeframe $NEvents | o2-sim-mctracks-to-aod --shm-segment-size $SHMSIZE -b --aod-writer-keep dangling | o2-analysis-mctracks-to-aod-simple-task --shm-segment-size $SHMSIZE -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 From e17ce87255672c961b0a19abc9a40d4198f1b479 Mon Sep 17 00:00:00 2001 From: shahoian Date: Wed, 11 Mar 2026 17:09:30 +0100 Subject: [PATCH 349/701] Methods for Barrel <-> Forward tracks conversion --- .../ReconstructionDataFormats/TrackFwd.h | 12 +++ .../TrackParametrization.h | 4 + .../TrackParametrizationWithError.h | 3 + DataFormats/Reconstruction/src/TrackFwd.cxx | 78 ++++++++++++++++++- .../src/TrackParametrization.cxx | 16 ++++ .../src/TrackParametrizationWithError.cxx | 59 ++++++++++++++ 6 files changed, 171 insertions(+), 1 deletion(-) diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackFwd.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackFwd.h index 50ed36d466d25..fca117583027f 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackFwd.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackFwd.h @@ -31,6 +31,12 @@ using SMatrix55Sym = ROOT::Math::SMatrix; using SMatrix5 = ROOT::Math::SVector; +template +class TrackParametrization; // fwd declaration for conversion method + +template +class TrackParametrizationWithError; // fwd declaration for conversion method + class TrackParFwd { // Forward track parameterization, kinematics only. public: @@ -42,6 +48,9 @@ class TrackParFwd TrackParFwd(TrackParFwd&&) = delete; TrackParFwd& operator=(TrackParFwd&&) = delete; + template + void toBarrelTrackPar(TrackParametrization& t) const; + /// return Z coordinate (cm) Double_t getZ() const { return mZ; } /// set Z coordinate (cm) @@ -145,6 +154,9 @@ class TrackParCovFwd : public TrackParFwd TrackParCovFwd& operator=(const TrackParCovFwd& tpf) = default; TrackParCovFwd(const Double_t z, const SMatrix5& parameters, const SMatrix55Sym& covariances, const Double_t chi2); + template + void toBarrelTrackParCov(TrackParametrizationWithError& t) const; + const SMatrix55Sym& getCovariances() const { return mCovariances; } void setCovariances(const SMatrix55Sym& covariances) { mCovariances = covariances; } void deleteCovariances() { mCovariances = SMatrix55Sym(); } diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h index 6389b037c3625..918633d914230 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrization.h @@ -66,6 +66,9 @@ class DCA; namespace track { + +class TrackParFwd; // fwd declaration for conversion method + // aliases for track elements enum ParLabels : int { kY, kZ, @@ -252,6 +255,7 @@ class TrackParametrization GPUd() void printParam() const; GPUd() void printParamHexadecimal(); #ifndef GPUCA_ALIGPUCODE + void toFwdTrackPar(TrackParFwd& t) const; std::string asString() const; std::string asStringHexadecimal(); size_t hash() const { return hash(getX(), getAlpha(), getY(), getZ(), getSnp(), getTgl(), getQ2Pt()); } diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrizationWithError.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrizationWithError.h index 0fc01e6db61a2..7f7e1e33144b1 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrizationWithError.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/TrackParametrizationWithError.h @@ -25,6 +25,8 @@ namespace o2 namespace track { +class TrackParCovFwd; // fwd declaration for conversion method + template class TrackParametrizationWithError : public TrackParametrization { // track+error parameterization @@ -82,6 +84,7 @@ class TrackParametrizationWithError : public TrackParametrization GPUd() void print() const; GPUd() void printHexadecimal(); #ifndef GPUCA_ALIGPUCODE + bool toFwdTrackParCov(TrackParCovFwd& t) const; std::string asString() const; std::string asStringHexadecimal(); #endif diff --git a/DataFormats/Reconstruction/src/TrackFwd.cxx b/DataFormats/Reconstruction/src/TrackFwd.cxx index dfe72c5b2ccc4..511ba122b2822 100644 --- a/DataFormats/Reconstruction/src/TrackFwd.cxx +++ b/DataFormats/Reconstruction/src/TrackFwd.cxx @@ -10,6 +10,8 @@ // or submit itself to any jurisdiction. #include "ReconstructionDataFormats/TrackFwd.h" +#include "ReconstructionDataFormats/TrackParametrization.h" +#include "ReconstructionDataFormats/TrackParametrizationWithError.h" #include "Math/MatrixFunctions.h" #include @@ -41,7 +43,7 @@ void TrackParFwd::propagateParamToZlinear(double zEnd) auto dZ = (zEnd - getZ()); auto phi0 = getPhi(); auto [sinphi0, cosphi0] = o2::math_utils::sincosd(phi0); - auto invtanl0 = 1.0 / getTanl(); + auto invtanl0 = 1.0 / getTgl(); auto n = dZ * invtanl0; mParameters(0) += n * cosphi0; mParameters(1) += n * sinphi0; @@ -572,5 +574,79 @@ void TrackParCovFwd::propagateToDCAhelix(double zField, const std::array +void TrackParFwd::toBarrelTrackPar(TrackParametrization& t) const +{ + // we select the barrel frame with alpha = phi, then by construction the snp is 0 + auto alpha = getPhi(); + auto csa = TMath::Cos(alpha), sna = TMath::Sin(alpha); + t.setAlpha(alpha); + t.setX(csa * getX() + sna * getY()); + t.setY(-sna * getX() + csa * getY()); + t.setZ(getZ()); + t.setSnp(0); + t.setTgl(getTanl()); + t.setQ2Pt(getInvQPt()); +} + +template +void TrackParCovFwd::toBarrelTrackParCov(TrackParametrizationWithError& t) const +{ + // We select the barrel frame with alpha = phi, then by construction the snp is 0 + auto alpha = getPhi(); + auto csa = TMath::Cos(alpha), sna = TMath::Sin(alpha); + t.setAlpha(alpha); + t.setX(csa * getX() + sna * getY()); + t.setY(-sna * getX() + csa * getY()); + t.setZ(getZ()); + t.setSnp(0); + t.setTgl(getTgl()); + t.setQ2Pt(getInvQPt()); + /* + The standard Jacobian d{barrel_param} / d{fwd_param} should be augmented by the "forward" uncertainty + in X_barrel translated to uncertainty in Z, i.e: + A fwd param variation delta_x, delta_y leads to barrel frame coordinate variaion + delta_xb = csa delta_x + sna delta_y + delta_yb = -sna delta_x + csa delta_y + with dx_b/dz = csp/tgL = 1/tgL, dy_b/dz = snp/tgL = 0 (for phi 0 in the tracking frame) the variation of delta_xb would require + a shift in delta_zb = -tgL delta_xb to stat at the same X. + So, for alpha=phi (-> snp=0) choice the full Jacobian fwd->barrel is: + / -sna csa 0 0 0 \ + | -tgL*csa -tgL*sna 0 0 0 | + | 0 0 1 0 0 | + | 0 0 0 1 0 | + \ 0 0 0 0 1 / + */ + const T a1 = -sna; + const T a2 = csa; + const T b1 = -getTgl() * csa; + const T b2 = -getTgl() * sna; + const T cphi = 1; + const auto& C = getCovariances(); + typename TrackParametrizationWithError::covMat_t covBarrel = { + T(a1 * a1 * C(0, 0) + 2 * a1 * a2 * C(0, 1) + a2 * a2 * C(1, 1)), // kSigY2 + T(a1 * b1 * C(0, 0) + (a1 * b2 + a2 * b1) * C(0, 1) + a2 * b2 * C(1, 1)), // kSigZY + T(b1 * b1 * C(0, 0) + 2 * b1 * b2 * C(0, 1) + b2 * b2 * C(1, 1)), // kSigZ2 + T(csa * (a1 * C(0, 2) + a2 * C(1, 2))), // kSigSnpY + T(csa * (b1 * C(0, 2) + b2 * C(1, 2))), // kSigSnpZ + T(csa * csa * C(2, 2)), // kSigSnp2 + T(a1 * C(0, 3) + a2 * C(1, 3)), // kSigTglY + T(b1 * C(0, 3) + b2 * C(1, 3)), // kSigTglZ + T(csa * C(2, 3)), // kSigTglSnp + T(C(3, 3)), // kSigTgl2 + T(a1 * C(0, 4) + a2 * C(1, 4)), // kSigQ2PtY + T(b1 * C(0, 4) + b2 * C(1, 4)), // kSigQ2PtZ + T(csa * C(2, 4)), // kSigQ2PtSnp + T(C(3, 4)), // kSigQ2PtTgl + T(C(4, 4)) // kSigQ2Pt2 + }; + t.setCov(covBarrel); +} + +template void TrackParFwd::toBarrelTrackPar(TrackParametrization&) const; +template void TrackParFwd::toBarrelTrackPar(TrackParametrization&) const; +template void TrackParCovFwd::toBarrelTrackParCov(TrackParametrizationWithError&) const; +template void TrackParCovFwd::toBarrelTrackParCov(TrackParametrizationWithError&) const; + } // namespace track } // namespace o2 diff --git a/DataFormats/Reconstruction/src/TrackParametrization.cxx b/DataFormats/Reconstruction/src/TrackParametrization.cxx index 4b850fe14086b..c238b087d5086 100644 --- a/DataFormats/Reconstruction/src/TrackParametrization.cxx +++ b/DataFormats/Reconstruction/src/TrackParametrization.cxx @@ -26,6 +26,7 @@ #ifndef GPUCA_ALIGPUCODE #include +#include "ReconstructionDataFormats/TrackFwd.h" #endif using namespace o2::gpu; @@ -985,6 +986,21 @@ GPUd() typename TrackParametrization::value_t TrackParametrization +void TrackParametrization::toFwdTrackPar(TrackParFwd& t) const +{ + auto p = getXYZGlo(); + t.setZ(p.Z()); + t.setX(p.X()); + t.setY(p.Y()); + t.setPhi(getPhi()); + t.setTanl(getTgl()); + t.setInvQPt(getQ2Pt()); +} +#endif + namespace o2::track { #if !defined(GPUCA_GPUCODE) || defined(GPUCA_GPUCODE_DEVICE) // FIXME: DR: WORKAROUND to avoid CUDA bug creating host symbols for device code. diff --git a/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx b/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx index 93ff7e1a2eb82..ee2e96736aa6d 100644 --- a/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx +++ b/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx @@ -21,6 +21,7 @@ #ifndef GPUCA_ALIGPUCODE #include +#include "ReconstructionDataFormats/TrackFwd.h" #endif using namespace o2::track; @@ -1768,6 +1769,64 @@ GPUd() void TrackParametrizationWithError::printHexadecimal() #endif } +#ifndef GPUCA_ALIGPUCODE +//______________________________________________________________ +template +bool TrackParametrizationWithError::toFwdTrackParCov(TrackParCovFwd& t) const +{ + auto p = this->getXYZGlo(); + t.setZ(p.Z()); + t.setX(p.X()); + t.setY(p.Y()); + t.setPhi(this->getPhi()); + t.setTanl(this->getTgl()); + t.setInvQPt(this->getQ2Pt()); + // + if (gpu::CAMath::Abs(this->getSnp()) >= o2::constants::math::Almost1 || + gpu::CAMath::Abs(this->getTgl()) <= o2::constants::math::Almost0) { + return false; + } + value_T csa, sna, csP, snP, csp = gpu::CAMath::Sqrt((1. - this->getSnp()) * (1. + this->getSnp())); + math_utils::detail::sincos(value_T(this->getAlpha()), sna, csa); + math_utils::detail::sincos(value_T(t.getPhi()), snP, csP); + /* + Jacobian is + /-sna -csP/tgL 0 0 0 \ + | csa -snP/tgL 0 0 0 | + | 0 0 1/csp 0 0 | + | 0 0 0 1 0 | + \ 0 0 0 0 1 / + */ + auto tgLI = 1 / this->getTgl(); + const value_T d1 = -sna; + const value_T d2 = -csP * tgLI; + const value_T e1 = csa; + const value_T e2 = -snP * tgLI; + const value_T f1 = 1 / csp; + SMatrix55Sym C; + C(0, 0) = d1 * d1 * getSigmaY2() + 2 * d1 * d2 * getSigmaZY() + d2 * d2 * getSigmaZ2(); + C(0, 1) = d1 * e1 * getSigmaY2() + (d1 * e2 + d2 * e1) * getSigmaZY() + d2 * e2 * getSigmaZ2(); + C(1, 1) = e1 * e1 * getSigmaY2() + 2 * e1 * e2 * getSigmaZY() + e2 * e2 * getSigmaZ2(); + + C(0, 2) = f1 * (d1 * getSigmaSnpY() + d2 * getSigmaSnpZ()); + C(1, 2) = f1 * (e1 * getSigmaSnpY() + e2 * getSigmaSnpZ()); + C(2, 2) = f1 * f1 * getSigmaSnp2(); + + C(0, 3) = d1 * getSigmaTglY() + d2 * getSigmaTglZ(); + C(1, 3) = e1 * getSigmaTglY() + e2 * getSigmaTglZ(); + C(2, 3) = f1 * getSigmaTglSnp(); + C(3, 3) = getSigmaTgl2(); + + C(0, 4) = d1 * getSigma1PtY() + d2 * getSigma1PtZ(); + C(1, 4) = e1 * getSigma1PtY() + e2 * getSigma1PtZ(); + C(2, 4) = f1 * getSigma1PtSnp(); + C(3, 4) = getSigma1PtTgl(); + C(4, 4) = getSigma1Pt2(); + t.setCovariances(C); + return true; +} +#endif + namespace o2::track { #if !defined(GPUCA_GPUCODE) || defined(GPUCA_GPUCODE_DEVICE) // FIXME: DR: WORKAROUND to avoid CUDA bug creating host symbols for device code. From 19ded7aa5f3ed75992e50f9b74a7801bac2229f8 Mon Sep 17 00:00:00 2001 From: Stefano Cannito <143754257+scannito@users.noreply.github.com> Date: Thu, 12 Mar 2026 21:25:37 +0100 Subject: [PATCH 350/701] [ALICE 3] Update TRK README (#15151) * Update TRK README * Update TRK README 2 --- Detectors/Upgrades/ALICE3/TRK/README.md | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/README.md b/Detectors/Upgrades/ALICE3/TRK/README.md index 9730c6f6efff7..a061a06be66f3 100644 --- a/Detectors/Upgrades/ALICE3/TRK/README.md +++ b/Detectors/Upgrades/ALICE3/TRK/README.md @@ -15,14 +15,13 @@ Configurables for various sub-detectors are presented in the following Table: | Subsystem | Available options | Comments | | ------------------ | ------------------------------------------------------- | ---------------------------------------------------------------- | | `TRKBase.layoutVD` | `kIRIS4` (default), `kIRISFullCyl`, `kIRIS5`, `kIRIS4a` | [link to definitions](./base/include/TRKBase/TRKBaseParam.h) | -| `TRKBase.layoutML` | `kCylinder`, `kTurboStaves` (default), `kStaggered` | | -| `TRKBase.layoutOT` | `kCylinder`, `kTurboStaves`, `kStaggered` (default) | | +| `TRKBase.layoutMLOT` | `kCylindrical`, `kSegmented` (default) | `kSegmented` produced a Turbo layout for ML and a Staggered layout for OT | | `TRKBase.layoutSRV` | `kPeacockv1` (default), `kLOISymm` | `kLOISymm` produces radially symmetric service volumes, as used in the LoI | For example, a geometry with fully cylindrical tracker barrel (for all layers in VD, ML and OT) can be obtained by ```bash o2-sim-serial-run5 -n 1 -g pythia8hi -m A3IP TRK FT3 TF3 \ - --configKeyValues "TRKBase.layoutVD=kIRISFullCyl;TRKBase.layoutML=kCylinder;TRKBase.layoutOL=kCylinder" + --configKeyValues "TRKBase.layoutVD=kIRISFullCyl;TRKBase.layoutMLOT=kCylindrical" ``` use Trapezoidal option for ML disks as a simplified segmentation + // To be changed to "true" paving with modules, as for the OT disks + + std::string chipName = o2::ft3::GeometryTGeo::getFT3ChipPattern() + std::to_string(mLayerNumber); + std::string sensName = Form("%s_%d_%d", GeometryTGeo::getFT3SensorPattern(), mDirection, mLayerNumber); + std::string passiveName = o2::ft3::GeometryTGeo::getFT3PassivePattern() + std::to_string(mLayerNumber); + + TGeoMedium* medSi = gGeoManager->GetMedium("FT3_SILICON$"); + TGeoMedium* medAir = gGeoManager->GetMedium("FT3_AIR$"); + + TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, mChipThickness / 2); + TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); + layerVol->SetLineColor(kGray); + + const int NtrapezoidalSegments = ft3Params.nTrapezoidalSegments; + + const double dz = mChipThickness / 2; + const double dzSensor = mSensorThickness / 2; + + const double dphi = 2.0 * TMath::Pi() / NtrapezoidalSegments; + double innerRadiusTrapezoidCorner = mInnerRadius / sin((TMath::Pi() - dphi) / 2); // to ensure that the trapezoid segments do not extend beyond the volume + + const double rc = 0.5 * (innerRadiusTrapezoidCorner + mOuterRadius) * TMath::Cos(0.5 * dphi); // radius of tile center + const double h = 0.5 * (mOuterRadius - innerRadiusTrapezoidCorner) * TMath::Cos(0.5 * dphi); // half radial length + + // chord lengths at inner/outer radii + const double bl = innerRadiusTrapezoidCorner * TMath::Sin(0.5 * dphi); // half lower base + const double tl = mOuterRadius * TMath::Sin(0.5 * dphi); // half upper base + + // create trapezoids + for (int iTr = 0; iTr < NtrapezoidalSegments; ++iTr) { + // chip volume + auto trdShapeChip = new TGeoTrap(dz, + 0.0, 0.0, // theta, phi + h, // h1 + bl, // bl1 + tl, // tl1 + 0.0, // alpha1 + h, // h2 + bl, // bl2 + tl, // tl2 + 0.0); // alpha2 + TGeoVolume* trapezoidChipVolume = new TGeoVolume(chipName.c_str(), trdShapeChip, medSi); + trapezoidChipVolume->SetLineColor(kCyan); + trapezoidChipVolume->SetTransparency(50); + + // sensor volume + auto trdShapeSensor = new TGeoTrap(dzSensor, + 0.0, 0.0, // theta, phi + h, // h1 + bl, // bl1 + tl, // tl1 + 0.0, // alpha1 + h, // h2 + bl, // bl2 + tl, // tl2 + 0.0); // alpha2 + TGeoVolume* trapezoidSensorVolume = new TGeoVolume(sensName.c_str(), trdShapeSensor, medSi); + trapezoidSensorVolume->SetLineColor(kYellow); + + // placing sensor in chip: + const double zSensorInChip = (dz - dzSensor) * (mZ < 0 ? 1 : -1); // place sensor at the outer face of the chip, towards the incoming particles + TGeoCombiTrans* transSens = new TGeoCombiTrans(); + transSens->SetTranslation(0, 0, zSensorInChip); + trapezoidChipVolume->AddNode(trapezoidSensorVolume, iTr, transSens); + + // passive volume + auto trdShapePassive = new TGeoTrap(dz - dzSensor, + 0.0, 0.0, // theta, phi + h, // h1 + bl, // bl1 + tl, // tl1 + 0.0, // alpha1 + h, // h2 + bl, // bl2 + tl, // tl2 + 0.0); // alpha2 + TGeoVolume* trapezoidPassiveVolume = new TGeoVolume(passiveName.c_str(), trdShapePassive, medSi); + trapezoidPassiveVolume->SetLineColor(kGray); + + // placing passive volume in chip: + const double zPassiveInChip = (-dzSensor) * (mZ < 0 ? 1 : -1); // place passive volume at the outer face of the chip, towards the incoming particles + TGeoCombiTrans* transPassive = new TGeoCombiTrans(); + transPassive->SetTranslation(0, 0, zPassiveInChip); + trapezoidChipVolume->AddNode(trapezoidPassiveVolume, iTr, transPassive); + + // prepare placing of chip in layer: + const double phi_c = (iTr + 0.5) * dphi; // sector center + const double phi_deg = phi_c * 180.0 / TMath::Pi(); + + // center of tile + const double x = rc * TMath::Cos(phi_c); + const double y = rc * TMath::Sin(phi_c); + const double z = 0.0; + + // local +Y should point radially outward + auto rot = new TGeoRotation(); + rot->RotateZ(phi_deg - 90.0); + auto transf = new TGeoCombiTrans(x, y, z, rot); + + layerVol->AddNode(trapezoidChipVolume, iTr, transf); + } + + LOG(info) << "Inserting " << NtrapezoidalSegments << " trapezoidal segments (Rmin=" + << mInnerRadius << ", Rmax=" << mOuterRadius << ", z = " << mZ << "cm) inside " << layerVol->GetName(); + + auto* diskRotation = new TGeoRotation("TrapezoidalDiskRotation", 0, 0, 0); + auto* diskCombiTrans = new TGeoCombiTrans(0, 0, mZ, diskRotation); + motherVolume->AddNode(layerVol, 1, diskCombiTrans); + } else if (ft3Params.layoutFT3 == kCylindrical) { + // cylindrical ML+OT disks std::string chipName = o2::ft3::GeometryTGeo::getFT3ChipPattern() + std::to_string(mLayerNumber), sensName = Form("%s_%d_%d", GeometryTGeo::getFT3SensorPattern(), mDirection, mLayerNumber); @@ -265,9 +381,7 @@ void FT3Layer::createLayer(TGeoVolume* motherVolume) LOG(info) << "Inserting " << layerVol->GetName() << " inside " << motherVolume->GetName(); motherVolume->AddNode(layerVol, 1, FwdDiskCombiTrans); - - } else { // OT disks - + } else if (ft3Params.layoutFT3 == kSegmented) { FT3Module module; // layer structure @@ -276,7 +390,7 @@ void FT3Layer::createLayer(TGeoVolume* motherVolume) std::string separationLayerName = "FT3SeparationLayer" + std::to_string(mDirection) + std::to_string(mLayerNumber); TGeoMedium* medAir = gGeoManager->GetMedium("FT3_AIR$"); - TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, 10 * mChipThickness / 2); + TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, 12 * mChipThickness / 2); // additional "thickness factor" is to avoid sub-volumes crossing the mother layer TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); layerVol->SetLineColor(kYellow + 2); @@ -293,5 +407,7 @@ void FT3Layer::createLayer(TGeoVolume* motherVolume) LOG(info) << "Inserting " << layerVol->GetName() << " inside " << motherVolume->GetName(); motherVolume->AddNode(layerVol, 1, FwdDiskCombiTrans); + } else { + LOG(fatal) << "Unknown FT3 layout option: " << static_cast(ft3Params.layoutFT3); } } diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx index 9b097a0243597..0a83c19125b70 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx @@ -96,10 +96,10 @@ void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::str } if (itof) { // iTOF const std::string name = GeometryTGeo::getITOFLayerPattern(); - const int nStaves = itofSegmented ? 24 : 0; // number of staves in segmented case - const double staveWidth = itofSegmented ? 5.42 : 0.0; // cm - const double staveTiltAngle = itofSegmented ? 10.0 : 0.0; // degrees - const int modulesPerStave = itofSegmented ? 10 : 0; // number of modules per stave in segmented case + const int nStaves = itofSegmented ? 24 : 0; // number of staves in segmented case + const double staveWidth = itofSegmented ? 5.42 : 0.0; // cm + const double staveTiltAngle = itofSegmented ? 3.0 : 0.0; // degrees + const int modulesPerStave = itofSegmented ? 10 : 0; // number of modules per stave in segmented case mITOFLayer = ITOFLayer(name, dInnerTof.first, 0.f, dInnerTof.second, 0.f, x2x0, ITOFLayer::kBarrelSegmented, nStaves, staveWidth, staveTiltAngle, modulesPerStave); @@ -108,7 +108,7 @@ void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::str const std::string name = GeometryTGeo::getOTOFLayerPattern(); const int nStaves = otofSegmented ? 62 : 0; // number of staves in segmented case const double staveWidth = otofSegmented ? 9.74 : 0.0; // cm - const double staveTiltAngle = otofSegmented ? 5.0 : 0.0; // degrees + const double staveTiltAngle = otofSegmented ? 3.0 : 0.0; // degrees const int modulesPerStave = otofSegmented ? 54 : 0; // number of modules per stave in segmented case mOTOFLayer = OTOFLayer(name, dOuterTof.first, 0.f, dOuterTof.second, 0.f, x2x0, OTOFLayer::kBarrelSegmented, diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx index 1744e4c4510bb..c3612b0276b2e 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx @@ -155,14 +155,17 @@ void ITOFLayer::createLayer(TGeoVolume* motherVolume) case kBarrelSegmented: { // First we create the volume for the whole layer, which will be used as mother volume for the segments const double avgRadius = 0.5 * (mInnerRadius + mOuterRadius); - const double staveSizeX = mStaves.second; // cm - const double staveSizeY = mOuterRadius - mInnerRadius; // cm - const double staveSizeZ = mZLength; // cm - const double deltaForTilt = 0.5 * (std::sin(TMath::DegToRad() * mTiltAngle) * staveSizeX + std::cos(TMath::DegToRad() * mTiltAngle) * staveSizeY); // we increase the size of the layer to account for the tilt of the staves + const double staveSizeX = mStaves.second; // cm + const double staveSizeY = mOuterRadius - mInnerRadius; // cm + const double staveSizeZ = mZLength; // cm + const double rMargin = 0.2; // cm, a small margin to avoid layer extrusion by sub-volumes + const double deltaForTilt = rMargin + 0.5 * (std::sin(TMath::DegToRad() * mTiltAngle) * staveSizeX + std::cos(TMath::DegToRad() * mTiltAngle) * staveSizeY); // we increase the size of the layer to account for the tilt of the staves TGeoTube* layer = new TGeoTube(mInnerRadius - deltaForTilt, mOuterRadius + deltaForTilt, mZLength / 2); TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); setLayerStyle(layerVol); + LOGP(info, "iTOF kBarrelSegmented layout: stave tilt angle {}, layer tube rMin {}, rMax {}", mTiltAngle, mInnerRadius - deltaForTilt, mOuterRadius + deltaForTilt); + // Now we create the volume for a single stave TGeoBBox* stave = new TGeoBBox(staveSizeX * 0.5, staveSizeY * 0.5, staveSizeZ * 0.5); TGeoVolume* staveVol = new TGeoVolume(staveName, stave, medAir); @@ -287,10 +290,13 @@ void OTOFLayer::createLayer(TGeoVolume* motherVolume) case kBarrelSegmented: { // First we create the volume for the whole layer, which will be used as mother volume for the segments const double avgRadius = 0.5 * (mInnerRadius + mOuterRadius); - TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, mZLength / 2); + const double rMargin = 0.8; // cm, a small margin to avoid layer extrusion by sub-volumes + TGeoTube* layer = new TGeoTube(mInnerRadius - rMargin, mOuterRadius + rMargin, mZLength / 2); TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); setLayerStyle(layerVol); + LOGP(info, "oTOF kBarrelSegmented layout: stave tilt angle {}, layer tube rMin {}, rMax {}", mTiltAngle, mInnerRadius - rMargin, mOuterRadius + rMargin); + // Now we create the volume for a single stave const double staveSizeX = mStaves.second; // cm const double staveSizeY = mOuterRadius - mInnerRadius; // cm From 37f5b04d571fd33dab1775888b039142eadf5489 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sat, 14 Mar 2026 13:32:14 +0100 Subject: [PATCH 357/701] DPL: Fix order of initialisation (#15155) --- Framework/Core/include/Framework/ASoA.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Framework/Core/include/Framework/ASoA.h b/Framework/Core/include/Framework/ASoA.h index 7586d6a6d3c63..475823b150d90 100644 --- a/Framework/Core/include/Framework/ASoA.h +++ b/Framework/Core/include/Framework/ASoA.h @@ -2052,8 +2052,8 @@ class Table Table(std::shared_ptr table, uint64_t offset = 0) : mTable(table), - mEnd{table->num_rows()}, - mOffset(offset) + mOffset(offset), + mEnd{table->num_rows()} { if (mTable->num_rows() == 0) { for (size_t ci = 0; ci < framework::pack_size(columns_t{}); ++ci) { From 44b8e32057b84823444e92323c1de73cc89ac11e Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 15 Mar 2026 19:30:16 +0100 Subject: [PATCH 358/701] DPL: simplify argument passing and do not scatter configuration (#15154) --- .../src/AODJAlienReaderHelpers.cxx | 2 +- .../AnalysisSupport/src/DataInputDirector.cxx | 58 +++++++------------ .../AnalysisSupport/src/DataInputDirector.h | 23 ++++---- .../test/test_DataInputDirector.cxx | 12 ++-- 4 files changed, 40 insertions(+), 55 deletions(-) diff --git a/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx b/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx index cde6c85f2c624..57a397822d167 100644 --- a/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx +++ b/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx @@ -134,7 +134,7 @@ AlgorithmSpec AODJAlienReaderHelpers::rootFileReaderCallback(ConfigContext const auto maxRate = options.get("aod-max-io-rate"); // create a DataInputDirector - auto didir = std::make_shared(filename, &monitoring, parentAccessLevel, parentFileReplacement); + auto didir = std::make_shared(std::vector{filename}, DataInputDirectorContext{&monitoring, parentAccessLevel, parentFileReplacement}); if (options.isSet("aod-reader-json")) { auto jsonFile = options.get("aod-reader-json"); if (!didir->readJson(jsonFile)) { diff --git a/Framework/AnalysisSupport/src/DataInputDirector.cxx b/Framework/AnalysisSupport/src/DataInputDirector.cxx index 2bc6c5613f065..ace4565449c4b 100644 --- a/Framework/AnalysisSupport/src/DataInputDirector.cxx +++ b/Framework/AnalysisSupport/src/DataInputDirector.cxx @@ -57,12 +57,10 @@ FileNameHolder* makeFileNameHolder(std::string fileName) return fileNameHolder; } -DataInputDescriptor::DataInputDescriptor(bool alienSupport, int level, o2::monitoring::Monitoring* monitoring, int allowedParentLevel, std::string parentFileReplacement) +DataInputDescriptor::DataInputDescriptor(bool alienSupport, int level, DataInputDirectorContext& context) : mAlienSupport(alienSupport), - mMonitoring(monitoring), - mAllowedParentLevel(allowedParentLevel), - mParentFileReplacement(std::move(parentFileReplacement)), - mLevel(level) + mLevel(level), + mContext(context) { std::vector capabilitiesSpecs = { "O2Framework:RNTupleObjectReadingCapability", @@ -157,13 +155,13 @@ bool DataInputDescriptor::setFile(int counter, std::string_view origin) // get the parent file map if exists mParentFileMap = (TMap*)rootFS->GetFile()->Get("parentFiles"); // folder name (DF_XXX) --> parent file (absolute path) - if (mParentFileMap && !mParentFileReplacement.empty()) { - auto pos = mParentFileReplacement.find(';'); + if (mParentFileMap && !mContext.parentFileReplacement.empty()) { + auto pos = mContext.parentFileReplacement.find(';'); if (pos == std::string::npos) { - throw std::runtime_error(fmt::format("Invalid syntax in aod-parent-base-path-replacement: \"{}\"", mParentFileReplacement.c_str())); + throw std::runtime_error(fmt::format("Invalid syntax in aod-parent-base-path-replacement: \"{}\"", mContext.parentFileReplacement.c_str())); } - auto from = mParentFileReplacement.substr(0, pos); - auto to = mParentFileReplacement.substr(pos + 1); + auto from = mContext.parentFileReplacement.substr(0, pos); + auto to = mContext.parentFileReplacement.substr(pos + 1); auto it = mParentFileMap->MakeIterator(); while (auto obj = it->Next()) { @@ -280,13 +278,13 @@ DataInputDescriptor* DataInputDescriptor::getParentFile(int counter, int numTF, } } - if (mLevel == mAllowedParentLevel) { - throw std::runtime_error(fmt::format(R"(while looking for tree "{}", the parent file was requested but we are already at level {} of maximal allowed level {} for DF "{}" in file "{}")", treename.c_str(), mLevel, mAllowedParentLevel, folderName.c_str(), + if (mLevel == mContext.allowedParentLevel) { + throw std::runtime_error(fmt::format(R"(while looking for tree "{}", the parent file was requested but we are already at level {} of maximal allowed level {} for DF "{}" in file "{}")", treename.c_str(), mLevel, mContext.allowedParentLevel, folderName.c_str(), rootFS->GetFile()->GetName())); } LOGP(info, "Opening parent file {} for DF {}", parentFileName->GetString().Data(), folderName.c_str()); - mParentFile = new DataInputDescriptor(mAlienSupport, mLevel + 1, mMonitoring, mAllowedParentLevel, mParentFileReplacement); + mParentFile = new DataInputDescriptor(mAlienSupport, mLevel + 1, mContext); mParentFile->mdefaultFilenamesPtr = new std::vector; mParentFile->mdefaultFilenamesPtr->emplace_back(makeFileNameHolder(parentFileName->GetString().Data())); mParentFile->fillInputfiles(); @@ -316,7 +314,7 @@ void DataInputDescriptor::printFileOpening() monitoringInfo += fmt::format(",se={},open_time={:.1f}", alienFile->GetSE(), alienFile->GetElapsed()); } #endif - mMonitoring->send(o2::monitoring::Metric{monitoringInfo, "aod-file-open-info"}.addTag(o2::monitoring::tags::Key::Subsystem, o2::monitoring::tags::Value::DPL)); + mContext.monitoring->send(o2::monitoring::Metric{monitoringInfo, "aod-file-open-info"}.addTag(o2::monitoring::tags::Key::Subsystem, o2::monitoring::tags::Value::DPL)); LOGP(info, "Opening file: {}", monitoringInfo); } @@ -337,7 +335,7 @@ void DataInputDescriptor::printFileStatistics() monitoringInfo += fmt::format(",se={},open_time={:.1f}", alienFile->GetSE(), alienFile->GetElapsed()); } #endif - mMonitoring->send(o2::monitoring::Metric{monitoringInfo, "aod-file-read-info"}.addTag(o2::monitoring::tags::Key::Subsystem, o2::monitoring::tags::Value::DPL)); + mContext.monitoring->send(o2::monitoring::Metric{monitoringInfo, "aod-file-read-info"}.addTag(o2::monitoring::tags::Key::Subsystem, o2::monitoring::tags::Value::DPL)); LOGP(info, "Read info: {}", monitoringInfo); } @@ -524,27 +522,15 @@ bool DataInputDescriptor::readTree(DataAllocator& outputs, header::DataHeader dh return true; } -DataInputDirector::DataInputDirector() +DataInputDirector::DataInputDirector(std::vector inputFiles, DataInputDirectorContext&& context) + : mContext{context} { - createDefaultDataInputDescriptor(); -} - -DataInputDirector::DataInputDirector(std::string inputFile, o2::monitoring::Monitoring* monitoring, int allowedParentLevel, std::string parentFileReplacement) : mMonitoring(monitoring), mAllowedParentLevel(allowedParentLevel), mParentFileReplacement(std::move(parentFileReplacement)) -{ - if (inputFile.size() && inputFile[0] == '@') { - inputFile.erase(0, 1); - setInputfilesFile(inputFile); + if (inputFiles.size() == 1 && !inputFiles[0].empty() && inputFiles[0][0] == '@') { + setInputfilesFile(inputFiles.back().substr(1, -1)); } else { - mdefaultInputFiles.emplace_back(makeFileNameHolder(inputFile)); - } - - createDefaultDataInputDescriptor(); -} - -DataInputDirector::DataInputDirector(std::vector inputFiles, o2::monitoring::Monitoring* monitoring, int allowedParentLevel, std::string parentFileReplacement) : mMonitoring(monitoring), mAllowedParentLevel(allowedParentLevel), mParentFileReplacement(std::move(parentFileReplacement)) -{ - for (auto inputFile : inputFiles) { - mdefaultInputFiles.emplace_back(makeFileNameHolder(inputFile)); + for (auto inputFile : inputFiles) { + mdefaultInputFiles.emplace_back(makeFileNameHolder(inputFile)); + } } createDefaultDataInputDescriptor(); @@ -576,7 +562,7 @@ void DataInputDirector::createDefaultDataInputDescriptor() if (mdefaultDataInputDescriptor) { delete mdefaultDataInputDescriptor; } - mdefaultDataInputDescriptor = new DataInputDescriptor(mAlienSupport, 0, mMonitoring, mAllowedParentLevel, mParentFileReplacement); + mdefaultDataInputDescriptor = new DataInputDescriptor(mAlienSupport, 0, mContext); mdefaultDataInputDescriptor->setInputfilesFile(minputfilesFile); mdefaultDataInputDescriptor->setFilenamesRegex(mFilenameRegex); @@ -700,7 +686,7 @@ bool DataInputDirector::readJsonDocument(Document* jsonDoc) return false; } // create a new dataInputDescriptor - auto didesc = new DataInputDescriptor(mAlienSupport, 0, mMonitoring, mAllowedParentLevel, mParentFileReplacement); + auto didesc = new DataInputDescriptor(mAlienSupport, 0, mContext); didesc->setDefaultInputfiles(&mdefaultInputFiles); itemName = "table"; diff --git a/Framework/AnalysisSupport/src/DataInputDirector.h b/Framework/AnalysisSupport/src/DataInputDirector.h index 61b477bd8522d..2d63a1c71ea77 100644 --- a/Framework/AnalysisSupport/src/DataInputDirector.h +++ b/Framework/AnalysisSupport/src/DataInputDirector.h @@ -37,8 +37,15 @@ struct FileNameHolder { std::vector listOfTimeFrameNumbers; std::vector alreadyRead; }; + FileNameHolder* makeFileNameHolder(std::string fileName); +struct DataInputDirectorContext { + o2::monitoring::Monitoring* monitoring = nullptr; + int allowedParentLevel = 0; + std::string parentFileReplacement = ""; +}; + class DataInputDescriptor { /// Holds information concerning the reading of an aod table. @@ -50,7 +57,7 @@ class DataInputDescriptor std::string treename = ""; std::unique_ptr matcher; - DataInputDescriptor(bool alienSupport, int level, o2::monitoring::Monitoring* monitoring = nullptr, int allowedParentLevel = 0, std::string parentFileReplacement = ""); + DataInputDescriptor(bool alienSupport, int level, DataInputDirectorContext& context); void printOut(); @@ -93,16 +100,13 @@ class DataInputDescriptor std::string* minputfilesFilePtr = nullptr; std::string mFilenameRegex = ""; std::string* mFilenameRegexPtr = nullptr; - int mAllowedParentLevel = 0; - std::string mParentFileReplacement; std::vector mfilenames; std::vector* mdefaultFilenamesPtr = nullptr; std::shared_ptr mCurrentFilesystem; int mCurrentFileID = -1; bool mAlienSupport = false; - o2::monitoring::Monitoring* mMonitoring = nullptr; - + DataInputDirectorContext& mContext; TMap* mParentFileMap = nullptr; DataInputDescriptor* mParentFile = nullptr; int mLevel = 0; // level of parent files @@ -120,9 +124,7 @@ class DataInputDirector /// and the related input files public: - DataInputDirector(); - DataInputDirector(std::string inputFile, o2::monitoring::Monitoring* monitoring = nullptr, int allowedParentLevel = 0, std::string parentFileReplacement = ""); - DataInputDirector(std::vector inputFiles, o2::monitoring::Monitoring* monitoring = nullptr, int allowedParentLevel = 0, std::string parentFileReplacement = ""); + DataInputDirector(std::vector inputFiles, DataInputDirectorContext&& context); ~DataInputDirector(); void reset(); @@ -149,18 +151,15 @@ class DataInputDirector uint64_t getTotalSizeUncompressed(); private: + DataInputDirectorContext mContext; std::string minputfilesFile; std::string* const minputfilesFilePtr = &minputfilesFile; std::string mFilenameRegex; - int mAllowedParentLevel = 0; - std::string mParentFileReplacement; std::string* const mFilenameRegexPtr = &mFilenameRegex; DataInputDescriptor* mdefaultDataInputDescriptor = nullptr; std::vector mdefaultInputFiles; std::vector mdataInputDescriptors; - o2::monitoring::Monitoring* mMonitoring = nullptr; - bool mDebugMode = false; bool mAlienSupport = false; diff --git a/Framework/AnalysisSupport/test/test_DataInputDirector.cxx b/Framework/AnalysisSupport/test/test_DataInputDirector.cxx index cb49ccb83b0b7..6ccaf3a92c0e1 100644 --- a/Framework/AnalysisSupport/test/test_DataInputDirector.cxx +++ b/Framework/AnalysisSupport/test/test_DataInputDirector.cxx @@ -50,7 +50,7 @@ BOOST_AUTO_TEST_CASE(TestDatainputDirector) jf << R"(})" << std::endl; jf.close(); - DataInputDirector didir1; + DataInputDirector didir1({}, {}); BOOST_CHECK(didir1.readJson(jsonFile)); didir1.printOut(); printf("\n\n"); @@ -60,8 +60,8 @@ BOOST_AUTO_TEST_CASE(TestDatainputDirector) auto dh = DataHeader(DataDescription{"DUE"}, DataOrigin{"AOD"}, DataHeader::SubSpecificationType{0}); - //auto [file1, directory1] = didir1.getFileFolder(dh, 1, 0); - //BOOST_CHECK_EQUAL(file1->GetName(), "Bresults_1.root"); + // auto [file1, directory1] = didir1.getFileFolder(dh, 1, 0); + // BOOST_CHECK_EQUAL(file1->GetName(), "Bresults_1.root"); auto didesc = didir1.getDataInputDescriptor(dh); BOOST_CHECK(didesc); @@ -96,13 +96,13 @@ BOOST_AUTO_TEST_CASE(TestDatainputDirector) "Aresults_2.root", "Bresults_1.root", "Bresults_2.root"}; - DataInputDirector didir2(inputFiles); + DataInputDirector didir2(inputFiles, {}); didir2.printOut(); printf("\n\n"); BOOST_CHECK(didir2.readJson(jsonFile)); - //auto [file2, directory2] = didir2.getFileFolder(dh, 1, 0); - //BOOST_CHECK_EQUAL(file2->GetName(), "Bresults_1.root"); + // auto [file2, directory2] = didir2.getFileFolder(dh, 1, 0); + // BOOST_CHECK_EQUAL(file2->GetName(), "Bresults_1.root"); didesc = didir2.getDataInputDescriptor(dh); BOOST_CHECK(didesc); From 2f3b9725962e1d60c2b5be7deb861ab595e5ff75 Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Tue, 10 Mar 2026 18:12:35 +0100 Subject: [PATCH 359/701] Improve Vertex handling in MCEventHeader --- .../include/Generators/GeneratorFromFile.h | 4 +++ .../include/Generators/GeneratorHybrid.h | 3 ++- Generators/src/GeneratorHybrid.cxx | 27 +++++++++---------- 3 files changed, 19 insertions(+), 15 deletions(-) diff --git a/Generators/include/Generators/GeneratorFromFile.h b/Generators/include/Generators/GeneratorFromFile.h index 706557ea2484b..329510f844f05 100644 --- a/Generators/include/Generators/GeneratorFromFile.h +++ b/Generators/include/Generators/GeneratorFromFile.h @@ -144,7 +144,11 @@ class GeneratorFromEventPool : public o2::eventgen::Generator void updateHeader(o2::dataformats::MCEventHeader* eventHeader) override { + // Copy current vertex position from the event header + const double xyz[3] = {eventHeader->GetX(), eventHeader->GetY(), eventHeader->GetZ()}; mO2KineGenerator->updateHeader(eventHeader); + // Event pool uses vertex position from current simulation, only extKinO2 takes the one from the file instead + eventHeader->SetVertex(xyz[0], xyz[1], xyz[2]); } // determine the collection of available files diff --git a/Generators/include/Generators/GeneratorHybrid.h b/Generators/include/Generators/GeneratorHybrid.h index 97422df1c8e2d..b57257747c629 100644 --- a/Generators/include/Generators/GeneratorHybrid.h +++ b/Generators/include/Generators/GeneratorHybrid.h @@ -76,7 +76,7 @@ class GeneratorHybrid : public Generator ~GeneratorHybrid(); o2::eventgen::Generator* currentgen = nullptr; std::vector> gens; - const std::vector generatorNames = {"extkinO2", "evtpool", "boxgen", "external", "hepmc", "pythia8", "pythia8pp", "pythia8hi", "pythia8hf", "pythia8powheg"}; + const std::vector generatorNames = {"evtpool", "boxgen", "external", "hepmc", "pythia8", "pythia8pp", "pythia8hi", "pythia8hf", "pythia8powheg"}; std::vector mInputGens; std::vector mGens; std::vector mConfigs; @@ -120,6 +120,7 @@ class GeneratorHybrid : public Generator bool mIsInitialized = false; o2::dataformats::MCEventHeader mMCEventHeader; // to capture event headers + int mHeaderGeneratorIndex = -1; // index of the generator that updated the header in current event enum class GenMode { kSeq, diff --git a/Generators/src/GeneratorHybrid.cxx b/Generators/src/GeneratorHybrid.cxx index f853b772e3cd3..2741d874b1681 100644 --- a/Generators/src/GeneratorHybrid.cxx +++ b/Generators/src/GeneratorHybrid.cxx @@ -96,10 +96,6 @@ GeneratorHybrid::GeneratorHybrid(const std::string& inputgens) } mConfsPythia8.push_back(mConfigs[index]); mGens.push_back(gen); - } else if (gen.compare("extkinO2") == 0) { - int confO2KineIndex = std::stoi(mConfigs[index].substr(9)); - gens.push_back(std::make_shared(*mO2KineGenConfigs[confO2KineIndex])); - mGens.push_back(gen); } else if (gen.compare("evtpool") == 0) { int confEvtPoolIndex = std::stoi(mConfigs[index].substr(8)); gens.push_back(std::make_shared(mEventPoolConfigs[confEvtPoolIndex])); @@ -417,10 +413,12 @@ bool GeneratorHybrid::importParticles() // Clear particles and event header mParticles.clear(); - mMCEventHeader.clearInfo(); + // event header of underlying generator must be fully reset + // this is important when using event pools where the full header information is forwarded from the generator + // otherwise some events might have mixed header information from different generators + mMCEventHeader.Reset(); 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(); @@ -442,9 +440,10 @@ bool GeneratorHybrid::importParticles() } mParticles.insert(mParticles.end(), subParticles.begin(), subParticles.end()); - if (baseGen) { + // first generator of the cocktail is used as reference to update the event header information + if (mHeaderGeneratorIndex == -1) { gens[subIndex]->updateHeader(&mMCEventHeader); - baseGen = false; + mHeaderGeneratorIndex = subIndex; // store index of generator updating the header } mInputTaskQueue.push(subIndex); mTasksStarted++; @@ -467,6 +466,7 @@ bool GeneratorHybrid::importParticles() // fetch the event Header information from the underlying generator gens[genIndex]->updateHeader(&mMCEventHeader); + mHeaderGeneratorIndex = genIndex; // store index of generator updating the header mInputTaskQueue.push(genIndex); mTasksStarted++; } @@ -484,6 +484,10 @@ bool GeneratorHybrid::importParticles() void GeneratorHybrid::updateHeader(o2::dataformats::MCEventHeader* eventHeader) { if (eventHeader) { + // Overwrite current vertex information to the underlying generator header, + // otherwise the info will be dropped when copying the FairMCEventHeader part of the header + mMCEventHeader.SetVertex(eventHeader->GetX(), eventHeader->GetY(), eventHeader->GetZ()); + mHeaderGeneratorIndex = -1; // reset header generator index for next event // Forward the base class fields from FairMCEventHeader static_cast(*eventHeader) = static_cast(mMCEventHeader); // Copy the key-value store info @@ -518,11 +522,6 @@ Bool_t GeneratorHybrid::confSetter(const auto& gen) auto pythia8Config = TBufferJSON::FromJSON(jsonValueToString(pythia8conf).c_str()); mPythia8GenConfigs.push_back(std::move(pythia8Config)); mConfigs.push_back("pythia8_" + std::to_string(mPythia8GenConfigs.size() - 1)); - } else if (name == "extkinO2") { - const auto& o2kineconf = gen["config"]; - auto o2kineConfig = TBufferJSON::FromJSON(jsonValueToString(o2kineconf).c_str()); - mO2KineGenConfigs.push_back(std::move(o2kineConfig)); - mConfigs.push_back("extkinO2_" + std::to_string(mO2KineGenConfigs.size() - 1)); } else if (name == "evtpool") { const auto& o2kineconf = gen["config"]; auto poolConfig = TBufferJSON::FromJSON(jsonValueToString(o2kineconf).c_str()); @@ -546,7 +545,7 @@ Bool_t GeneratorHybrid::confSetter(const auto& gen) mConfigs.push_back(""); } } else { - if (name == "boxgen" || name == "pythia8" || name == "extkinO2" || name == "external" || name == "hepmc") { + if (name == "boxgen" || name == "pythia8" || name == "external" || name == "hepmc") { LOG(fatal) << "No configuration provided for generator " << name; return false; } else { From 5250f03a9b31e121bbf8400f71dbb5dce75c0459 Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Mon, 16 Mar 2026 11:00:35 +0100 Subject: [PATCH 360/701] O2: Adapt to Arrow 23 (#15152) --- Framework/Core/CMakeLists.txt | 2 +- .../include/Framework/IndexBuilderHelpers.h | 2 +- Framework/Core/src/IndexBuilderHelpers.cxx | 81 +++++++++++++------ dependencies/O2Dependencies.cmake | 32 +++++++- 4 files changed, 89 insertions(+), 28 deletions(-) diff --git a/Framework/Core/CMakeLists.txt b/Framework/Core/CMakeLists.txt index e6a8db1077136..c311ba980a20b 100644 --- a/Framework/Core/CMakeLists.txt +++ b/Framework/Core/CMakeLists.txt @@ -8,7 +8,6 @@ # In applying this license CERN does not waive the privileges and immunities # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. - o2_add_library(Framework SOURCES src/AnalysisHelpers.cxx src/AlgorithmSpec.cxx @@ -178,6 +177,7 @@ o2_add_library(Framework RapidJSON::RapidJSON Arrow::arrow_shared ArrowDataset::arrow_dataset_shared + $<$:ArrowCompute::arrow_compute_shared> Microsoft.GSL::GSL O2::FrameworkLogger Gandiva::gandiva_shared diff --git a/Framework/Core/include/Framework/IndexBuilderHelpers.h b/Framework/Core/include/Framework/IndexBuilderHelpers.h index 30754e62a8dc3..3a23d97a10d83 100644 --- a/Framework/Core/include/Framework/IndexBuilderHelpers.h +++ b/Framework/Core/include/Framework/IndexBuilderHelpers.h @@ -29,7 +29,7 @@ enum struct IndexKind : int { namespace o2::framework { -void cannotBuildAnArray(); +void cannotBuildAnArray(const char* reason); void cannotCreateIndexBuilder(); struct ChunkedArrayIterator { diff --git a/Framework/Core/src/IndexBuilderHelpers.cxx b/Framework/Core/src/IndexBuilderHelpers.cxx index 0943dea42169c..0c06aea11ef2c 100644 --- a/Framework/Core/src/IndexBuilderHelpers.cxx +++ b/Framework/Core/src/IndexBuilderHelpers.cxx @@ -13,17 +13,21 @@ #include "Framework/IndexBuilderHelpers.h" #include "Framework/CompilerBuiltins.h" #include "Framework/VariantHelpers.h" -#include +#include +#if (ARROW_VERSION_MAJOR > 20) +#include +#endif #include +#include #include #include #include namespace o2::framework { -void cannotBuildAnArray() +void cannotBuildAnArray(const char* reason) { - throw framework::runtime_error("Cannot finish an array"); + throw framework::runtime_error_f("Cannot finish an array: %s", reason); } void cannotCreateIndexBuilder() @@ -62,10 +66,10 @@ SelfBuilder::SelfBuilder(arrow::MemoryPool* pool) { auto status = arrow::MakeBuilder(pool, arrow::int32(), &mBuilder); if (!status.ok()) { - throw framework::runtime_error("Cannot create array builder for the self-index!"); + throw framework::runtime_error_f("Cannot create array builder for the self-index: %s", status.ToString().c_str()); } } -// static_cast(this)->reset(pool); + void SelfBuilder::reset(std::shared_ptr) { mBuilder->Reset(); @@ -74,7 +78,10 @@ void SelfBuilder::reset(std::shared_ptr) void SelfBuilder::fill(int idx) { - (void)static_cast(mBuilder.get())->Append(idx); + auto status = static_cast(mBuilder.get())->Append(idx); + if (!status.ok()) { + throw framework::runtime_error_f("Cannot append to self-index array: %s", status.ToString().c_str()); + } } std::shared_ptr SelfBuilder::result() const @@ -82,7 +89,7 @@ std::shared_ptr SelfBuilder::result() const std::shared_ptr array; auto status = static_cast(mBuilder.get())->Finish(&array); if (!status.ok()) { - cannotBuildAnArray(); + cannotBuildAnArray(status.ToString().c_str()); } return std::make_shared(array); @@ -93,7 +100,7 @@ SingleBuilder::SingleBuilder(std::shared_ptr source, arrow: { auto status = arrow::MakeBuilder(pool, arrow::int32(), &mBuilder); if (!status.ok()) { - throw framework::runtime_error("Cannot create array builder for the single-valued index!"); + throw framework::runtime_error_f("Cannot create array builder for the single-valued index: %s", status.ToString().c_str()); } } @@ -126,10 +133,14 @@ bool SingleBuilder::find(int idx) void SingleBuilder::fill(int idx) { + arrow::Status status; if (mPosition < mSourceSize && valueAt(mPosition) == idx) { - (void)static_cast(mBuilder.get())->Append((int)mPosition); + status = static_cast(mBuilder.get())->Append((int)mPosition); } else { - (void)static_cast(mBuilder.get())->Append(-1); + status = static_cast(mBuilder.get())->Append(-1); + } + if (!status.ok()) { + throw framework::runtime_error_f("Cannot append to array: %s", status.ToString().c_str()); } } @@ -138,7 +149,7 @@ std::shared_ptr SingleBuilder::result() const std::shared_ptr array; auto status = static_cast(mBuilder.get())->Finish(&array); if (!status.ok()) { - cannotBuildAnArray(); + cannotBuildAnArray(status.ToString().c_str()); } return std::make_shared(array); } @@ -146,14 +157,15 @@ std::shared_ptr SingleBuilder::result() const 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"); + auto status = preSlice(); + if (!status.ok()) { + throw framework::runtime_error_f("Cannot pre-slice the source for slice-index building: %s", status.ToString().c_str()); } std::unique_ptr builder; - auto status = arrow::MakeBuilder(pool, arrow::int32(), &builder); + status = arrow::MakeBuilder(pool, arrow::int32(), &builder); if (!status.ok()) { - throw framework::runtime_error("Cannot create array for the slice-index builder!"); + throw framework::runtime_error_f("Cannot create array for the slice-index builder: %s", status.ToString().c_str()); } mListBuilder = std::make_unique(pool, std::move(builder), 2); mValueBuilder = static_cast(mListBuilder.get())->value_builder(); @@ -166,8 +178,9 @@ void SliceBuilder::reset(std::shared_ptr source) 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"); + auto status = preSlice(); + if (!status.ok()) { + throw framework::runtime_error_f("Cannot pre-slice the source for slice-index building: %s", status.ToString().c_str()); } } @@ -211,13 +224,21 @@ std::shared_ptr SliceBuilder::result() const std::shared_ptr array; auto status = static_cast(mListBuilder.get())->Finish(&array); if (!status.ok()) { - cannotBuildAnArray(); + cannotBuildAnArray(status.ToString().c_str()); } return std::make_shared(array); } arrow::Status SliceBuilder::SliceBuilder::preSlice() { +#if (ARROW_VERSION_MAJOR > 20) + auto status = arrow::compute::Initialize(); + if (!status.ok()) { + throw framework::runtime_error_f("Cannot initialize arrow compute: %s", status.ToString().c_str()); + } +#else + arrow::Status status; +#endif arrow::Datum value_counts; auto options = arrow::compute::ScalarAggregateOptions::Defaults(); ARROW_ASSIGN_OR_RAISE(value_counts, arrow::compute::CallFunction("value_counts", {mSource}, &options)); @@ -230,14 +251,15 @@ arrow::Status SliceBuilder::SliceBuilder::preSlice() 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"); + auto&& status = preFind(); + if (!status.ok()) { + throw framework::runtime_error_f("Cannot pre-find in a source for array-index building: %s", status.ToString().c_str()); } std::unique_ptr builder; - auto status = arrow::MakeBuilder(pool, arrow::int32(), &builder); + status = arrow::MakeBuilder(pool, arrow::int32(), &builder); if (!status.ok()) { - throw framework::runtime_error("Cannot create array for the array-index builder!"); + throw framework::runtime_error_f("Cannot create array for the array-index builder: %s", status.ToString().c_str()); } mListBuilder = std::make_unique(pool, std::move(builder)); mValueBuilder = static_cast(mListBuilder.get())->value_builder(); @@ -246,8 +268,9 @@ ArrayBuilder::ArrayBuilder(std::shared_ptr source, arrow::M 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"); + auto status = preFind(); + if (!status.ok()) { + throw framework::runtime_error_f("Cannot pre-find in a source for array-index building: %s", status.ToString().c_str()); } mValues.clear(); mIndices.clear(); @@ -274,13 +297,21 @@ std::shared_ptr ArrayBuilder::result() const std::shared_ptr array; auto status = static_cast(mListBuilder.get())->Finish(&array); if (!status.ok()) { - cannotBuildAnArray(); + cannotBuildAnArray(status.ToString().c_str()); } return std::make_shared(array); } arrow::Status ArrayBuilder::preFind() { +#if (ARROW_VERSION_MAJOR > 20) + auto status = arrow::compute::Initialize(); + if (!status.ok()) { + throw framework::runtime_error_f("Cannot initialize arrow compute: %s", status.ToString().c_str()); + } +#else + arrow::Status status; +#endif arrow::Datum max; auto options = arrow::compute::ScalarAggregateOptions::Defaults(); ARROW_ASSIGN_OR_RAISE(max, arrow::compute::CallFunction("max", {mSource}, &options)); diff --git a/dependencies/O2Dependencies.cmake b/dependencies/O2Dependencies.cmake index 8addb87a1a16f..71e9d9907ac28 100644 --- a/dependencies/O2Dependencies.cmake +++ b/dependencies/O2Dependencies.cmake @@ -92,7 +92,37 @@ if(NOT TARGET ArrowAcero::arrow_acero_shared) ) endif() -if (NOT TARGET Gandiva::gandiva_shared) +string(REGEX MATCH "([0-9]+)\.*" ARROW_MAJOR "${ARROW_VERSION}") +if(${ARROW_MAJOR} GREATER 20) + if(NOT TARGET ArrowCompute::arrow_compute_shared) + # ArrowCompute::arrow_compute_shared is linked for no reason to parquet + # so we cannot use it because we do not want to build parquet itself. + # For that reason at the moment we need to do the lookup by hand. + get_target_property(ARROW_SHARED_LOCATION Arrow::arrow_shared LOCATION) + get_filename_component(ARROW_SHARED_DIR ${ARROW_SHARED_LOCATION} DIRECTORY) + + find_library(ARROW_COMPUTE_SHARED arrow_compute + PATHS ${ARROW_SHARED_DIR} + NO_DEFAULT_PATH + ) + + if(ARROW_COMPUTE_SHARED) + message(STATUS + "Found arrow_compute_shared library at: ${ARROW_COMPUTE_SHARED}") + else() + message(FATAL_ERROR + "arrow_compute_shared library not found in ${ARROW_SHARED_DIR}") + endif() + + # Step 3: Create a target for ArrowCompute::arrow_compute_shared + add_library(ArrowCompute::arrow_compute_shared SHARED IMPORTED) + set_target_properties(ArrowCompute::arrow_compute_shared PROPERTIES + IMPORTED_LOCATION ${ARROW_COMPUTE_SHARED} + ) + endif() +endif() + +if(NOT TARGET Gandiva::gandiva_shared) add_library(Gandiva::gandiva_shared ALIAS gandiva_shared) endif() From f9f6b0923525455da80fc75e8d0766624956044e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Mon, 16 Mar 2026 11:46:47 +0100 Subject: [PATCH 361/701] [ALICE3] oTOF: fix missing tilt shift for overlaps (#15159) --- .../ALICE3/IOTOF/simulation/src/Layer.cxx | 23 ++++++++----------- 1 file changed, 9 insertions(+), 14 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx index c3612b0276b2e..0d0983958c46f 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx @@ -155,17 +155,14 @@ void ITOFLayer::createLayer(TGeoVolume* motherVolume) case kBarrelSegmented: { // First we create the volume for the whole layer, which will be used as mother volume for the segments const double avgRadius = 0.5 * (mInnerRadius + mOuterRadius); - const double staveSizeX = mStaves.second; // cm - const double staveSizeY = mOuterRadius - mInnerRadius; // cm - const double staveSizeZ = mZLength; // cm - const double rMargin = 0.2; // cm, a small margin to avoid layer extrusion by sub-volumes - const double deltaForTilt = rMargin + 0.5 * (std::sin(TMath::DegToRad() * mTiltAngle) * staveSizeX + std::cos(TMath::DegToRad() * mTiltAngle) * staveSizeY); // we increase the size of the layer to account for the tilt of the staves + const double staveSizeX = mStaves.second; // cm + const double staveSizeY = mOuterRadius - mInnerRadius; // cm + const double staveSizeZ = mZLength; // cm + const double deltaForTilt = 0.5 * (std::sin(TMath::DegToRad() * mTiltAngle) * staveSizeX + std::cos(TMath::DegToRad() * mTiltAngle) * staveSizeY); // we increase the size of the layer to account for the tilt of the staves TGeoTube* layer = new TGeoTube(mInnerRadius - deltaForTilt, mOuterRadius + deltaForTilt, mZLength / 2); TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); setLayerStyle(layerVol); - LOGP(info, "iTOF kBarrelSegmented layout: stave tilt angle {}, layer tube rMin {}, rMax {}", mTiltAngle, mInnerRadius - deltaForTilt, mOuterRadius + deltaForTilt); - // Now we create the volume for a single stave TGeoBBox* stave = new TGeoBBox(staveSizeX * 0.5, staveSizeY * 0.5, staveSizeZ * 0.5); TGeoVolume* staveVol = new TGeoVolume(staveName, stave, medAir); @@ -290,17 +287,15 @@ void OTOFLayer::createLayer(TGeoVolume* motherVolume) case kBarrelSegmented: { // First we create the volume for the whole layer, which will be used as mother volume for the segments const double avgRadius = 0.5 * (mInnerRadius + mOuterRadius); - const double rMargin = 0.8; // cm, a small margin to avoid layer extrusion by sub-volumes - TGeoTube* layer = new TGeoTube(mInnerRadius - rMargin, mOuterRadius + rMargin, mZLength / 2); + const double staveSizeX = mStaves.second; // cm + const double staveSizeY = mOuterRadius - mInnerRadius; // cm + const double staveSizeZ = mZLength; // cm + const double deltaForTilt = 0.5 * (std::sin(TMath::DegToRad() * mTiltAngle) * staveSizeX + std::cos(TMath::DegToRad() * mTiltAngle) * staveSizeY); // we increase the size of the layer to account for the tilt of the staves + TGeoTube* layer = new TGeoTube(mInnerRadius - deltaForTilt, mOuterRadius + deltaForTilt, mZLength / 2); TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); setLayerStyle(layerVol); - LOGP(info, "oTOF kBarrelSegmented layout: stave tilt angle {}, layer tube rMin {}, rMax {}", mTiltAngle, mInnerRadius - rMargin, mOuterRadius + rMargin); - // Now we create the volume for a single stave - const double staveSizeX = mStaves.second; // cm - const double staveSizeY = mOuterRadius - mInnerRadius; // cm - const double staveSizeZ = mZLength; // cm TGeoBBox* stave = new TGeoBBox(staveSizeX * 0.5, staveSizeY * 0.5, staveSizeZ * 0.5); TGeoVolume* staveVol = new TGeoVolume(staveName, stave, medAir); setStaveStyle(staveVol); From 4112fd1e5aaa3d43b1587c4d4fd4a499a657cbe8 Mon Sep 17 00:00:00 2001 From: shahoian Date: Sat, 14 Mar 2026 22:40:15 +0100 Subject: [PATCH 362/701] Pass missing ctf-dict options --- .../ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx b/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx index b2a1b6ce75591..e0fc23ec70128 100644 --- a/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx @@ -43,7 +43,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) if (cfgc.options().get("runmft")) { wf.emplace_back(o2::itsmft::getEntropyEncoderSpec("MFT", selIR, cfgc.options().get("ctf-dict"))); } else { - wf.emplace_back(o2::itsmft::getEntropyEncoderSpec("ITS", selIR)); + wf.emplace_back(o2::itsmft::getEntropyEncoderSpec("ITS", selIR, cfgc.options().get("ctf-dict"))); } return wf; } From 6c759a2114d7700b851d7339738c53d790822fe7 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Mon, 16 Mar 2026 18:27:22 +0100 Subject: [PATCH 363/701] Export language variables (#15164) Old code only works if the variables were already exported. --- GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh b/GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh index d064c4f6b58d9..a607ea0eb3c8f 100755 --- a/GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh +++ b/GPU/GPUTracking/Definitions/Parameters/csv_to_json.sh @@ -2,9 +2,9 @@ [[ -z $1 ]] && { echo "Usage: csv_to_json.sh CSV_FILE"; exit 1; } -LANG=C -LC_ALL=C -DELIM=$'\xFF' +export LANG=C +export LC_ALL=C +DELIM=$'\x1F' set -o pipefail sed -E \ ':loop From 850e1dc667ac595a1827f0568cf6708380ea12ac Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Mon, 16 Mar 2026 20:41:43 +0100 Subject: [PATCH 364/701] Add some initial skills to improve Claude Code / Codex / whatever experience (#15165) --- .skills/build-software-stack/SKILL.md | 1 + .skills/create-a-new-file/SKILL.md | 20 +++++++++++++++++++ .../o2-copyright-statement.md | 11 ++++++++++ CLAUDE.md | 5 +++++ 4 files changed, 37 insertions(+) create mode 100644 .skills/build-software-stack/SKILL.md create mode 100644 .skills/create-a-new-file/SKILL.md create mode 100644 .skills/create-a-new-file/o2-copyright-statement.md create mode 100644 CLAUDE.md diff --git a/.skills/build-software-stack/SKILL.md b/.skills/build-software-stack/SKILL.md new file mode 100644 index 0000000000000..8b137891791fe --- /dev/null +++ b/.skills/build-software-stack/SKILL.md @@ -0,0 +1 @@ + diff --git a/.skills/create-a-new-file/SKILL.md b/.skills/create-a-new-file/SKILL.md new file mode 100644 index 0000000000000..a7af739d49894 --- /dev/null +++ b/.skills/create-a-new-file/SKILL.md @@ -0,0 +1,20 @@ +--- +name: create-a-new-file +description: describes how to create a new file +--- + +## Copyright statements + +The copyright statement for ALICE / O2 is found in ./o2-copyright-statement.md. It should be at the beginning of +the new file using the proper commenting syntax for the given programming language. For example in C++ it should be commented via +multiline comments: + +``` C++ +// Copyright 2019- CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// ... +``` + +The only part which needs to be adapted by you is the `` which you need to replace with the actual current year. + + diff --git a/.skills/create-a-new-file/o2-copyright-statement.md b/.skills/create-a-new-file/o2-copyright-statement.md new file mode 100644 index 0000000000000..0bc4b092e6ba6 --- /dev/null +++ b/.skills/create-a-new-file/o2-copyright-statement.md @@ -0,0 +1,11 @@ +// Copyright 2019- CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + diff --git a/CLAUDE.md b/CLAUDE.md new file mode 100644 index 0000000000000..9d8753a362a56 --- /dev/null +++ b/CLAUDE.md @@ -0,0 +1,5 @@ +This is the main repository of the ALICE Experiment Simulation, Reconstruction and Analysis Framework. + +The skills specific to developing code for this repository can be found in +the `.skills/` folder of this project. + From b7a497ba51cc0f656867623a45ceea9fc7f5af9a Mon Sep 17 00:00:00 2001 From: shahoian Date: Mon, 16 Mar 2026 15:09:15 +0100 Subject: [PATCH 365/701] fix field/material usage in the propagateToR... methods --- Detectors/Base/src/Propagator.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Detectors/Base/src/Propagator.cxx b/Detectors/Base/src/Propagator.cxx index a5983cab8e257..208b9bf138688 100644 --- a/Detectors/Base/src/Propagator.cxx +++ b/Detectors/Base/src/Propagator.cxx @@ -608,7 +608,7 @@ GPUd() bool PropagatorImpl::propagateToR(track_T& track, value_type r, // case1 if (math_utils::detail::abs(phiLocFin) < MaxPhiLocSafe) { // just 1 step propagation auto deltaX = (math_utils::detail::sin(phiLocFin) - track.getSnp()) / track.getCurvature(bz); - if (!track.propagateTo(track.getX() + deltaX, bz)) { + if (!propagateTo(track, track.getX() + deltaX, bzOnly, maxSnp, maxStep, matCorr, tofInfo, signCorr)) { return false; } break; @@ -631,7 +631,7 @@ GPUd() bool PropagatorImpl::propagateToR(track_T& track, value_type r, // propagate to phiLoc = +-MaxPhiLocSafe auto tgtPhiLoc = deltaPhi > 0 ? MaxPhiLocSafe : -MaxPhiLocSafe; auto deltaX = (math_utils::detail::sin(tgtPhiLoc) - track.getSnp()) / track.getCurvature(bz); - if (!track.propagateTo(track.getX() + deltaX, bz)) { + if (!propagateTo(track, track.getX() + deltaX, bzOnly, maxSnp, maxStep, matCorr, tofInfo, signCorr)) { return false; } deltaPhi -= tgtPhiLoc - phiLoc; From a346105448731a5f50a407fdde887af393ba0da5 Mon Sep 17 00:00:00 2001 From: Stefano Cannito <143754257+scannito@users.noreply.github.com> Date: Tue, 17 Mar 2026 08:22:31 +0100 Subject: [PATCH 366/701] [ALICE 3] Fix cylindrical MLOT layout (#15168) * Fix cylindrical version * Fix print * Remove commented lines --- .../ALICE3/TRK/base/src/GeometryTGeo.cxx | 46 ++++++++-------- .../ALICE3/TRK/simulation/src/Detector.cxx | 54 ++++++++++++------- 2 files changed, 59 insertions(+), 41 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx index 1a81723a18f63..36d26a6344e6c 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx @@ -101,10 +101,17 @@ void GeometryTGeo::Build(int loadTrans) mLastChipIndexMLOT.resize(mNumberOfLayersMLOT); /// ML and OT are part of TRK as the same detector, without disks for (int i = 0; i < mNumberOfLayersMLOT; i++) { - mNumberOfStaves[i] = extractNumberOfStavesMLOT(i); - mNumberOfHalfStaves[i] = extractNumberOfHalfStavesMLOT(i); - mNumberOfModules[i] = extractNumberOfModulesMLOT(i); - mNumberOfChips[i] = extractNumberOfChipsMLOT(i); + if (mLayoutMLOT == eMLOTLayout::kCylindrical) { + mNumberOfStaves[i] = 1; + mNumberOfHalfStaves[i] = 1; + mNumberOfModules[i] = 1; + mNumberOfChips[i] = 1; + } else { + mNumberOfStaves[i] = extractNumberOfStavesMLOT(i); + mNumberOfHalfStaves[i] = extractNumberOfHalfStavesMLOT(i); + mNumberOfModules[i] = extractNumberOfModulesMLOT(i); + mNumberOfChips[i] = extractNumberOfChipsMLOT(i); + } } int numberOfChipsTotal = 0; @@ -398,19 +405,8 @@ TString GeometryTGeo::getMatrixPath(int index) const // 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 because of the different numbering scheme in the geometry for the cylindrical case wrt the staggered and turbo ones - if (subDetID == 1) { - if ((layer < 4 && mLayoutMLOT == eMLOTLayout::kCylindrical) || (layer > 3 && mLayoutMLOT == eMLOTLayout::kCylindrical)) { - stave = 1; - mod = 1; - chip = 1; - } - } - // build the path if (subDetID == 0) { // VD if (disk >= 0) { @@ -425,15 +421,19 @@ TString GeometryTGeo::getMatrixPath(int index) const path += Form("%s%d_%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalLayerPattern(), layer, getTRKChipPattern(), layer); // PETALCASEx_LAYERy_TRKChipy_1 path += Form("%s%d_%s%d_%s%d_1/", getTRKPetalPattern(), petalcase, getTRKPetalLayerPattern(), layer, getTRKSensorPattern(), layer); // PETALCASEx_LAYERy_TRKSensory_1 } - } else if (subDetID == 1) { // MLOT - path += Form("%s%d_1/", getTRKLayerPattern(), layer); // TRKLayerx_1 - path += Form("%s%d_%d/", getTRKStavePattern(), layer, stave); // TRKStavex_y - if (mNumberOfHalfStaves[layer] == 2) { // staggered geometry - path += Form("%s%d_%d/", getTRKHalfStavePattern(), layer, halfstave); // TRKHalfStavex_y + } else if (subDetID == 1) { // MLOT + path += Form("%s%d_1/", getTRKLayerPattern(), layer); // TRKLayerx_1 + if (mLayoutMLOT == eMLOTLayout::kCylindrical) { + path += Form("%s%d_1/", getTRKSensorPattern(), layer); // TRKSensorx_1 + } else { + path += Form("%s%d_%d/", getTRKStavePattern(), layer, stave); // TRKStavex_y + if (mNumberOfHalfStaves[layer] == 2) { // staggered geometry + path += Form("%s%d_%d/", getTRKHalfStavePattern(), layer, halfstave); // TRKHalfStavex_y + } + path += Form("%s%d_%d/", getTRKModulePattern(), layer, mod); // TRKModulx_y + path += Form("%s%d_%d/", getTRKChipPattern(), layer, chip); // TRKChipx_y + path += Form("%s%d_1/", getTRKSensorPattern(), layer); // TRKSensorx_1 } - 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; } diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx index 8e13d31e7915c..66c02a080e0b6 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx @@ -475,18 +475,24 @@ bool Detector::ProcessHits(FairVolume* vol) if (stopHit) { TLorentzVector positionStop; fMC->TrackPosition(positionStop); + // Retrieve the indices with the volume path int stave(0), halfstave(0), mod(0), chip(0); + + auto& trkPars = TRKBaseParam::Instance(); + if (subDetID == 1) { - fMC->CurrentVolOffID(1, chip); - fMC->CurrentVolOffID(2, mod); - if (mGeometryTGeo->getNumberOfHalfStaves(layer) == 2) { - fMC->CurrentVolOffID(3, halfstave); - fMC->CurrentVolOffID(4, stave); - } else if (mGeometryTGeo->getNumberOfHalfStaves(layer) == 1) { - fMC->CurrentVolOffID(3, stave); - } else { - LOGP(fatal, "Wrong number of halfstaves for layer {}", layer); + if (trkPars.layoutMLOT == o2::trk::eMLOTLayout::kSegmented) { + 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 @@ -522,18 +528,30 @@ void Detector::Print(FairVolume* vol, int volume, int subDetID, int layer, int s int currentVol(0); LOG(info) << "Current volume name: " << fMC->CurrentVolName() << " and ID " << fMC->CurrentVolID(currentVol); LOG(info) << "volume: " << volume << "/" << mNumberOfVolumes - 1; - LOG(info) << "off volume name 1 " << fMC->CurrentVolOffName(1) << " chip: " << chip; - LOG(info) << "off volume name 2 " << fMC->CurrentVolOffName(2) << " module: " << mod; - if (subDetID == 1 && mGeometryTGeo->getNumberOfHalfStaves(layer) == 2) { // staggered geometry - LOG(info) << "off volume name 3 " << fMC->CurrentVolOffName(3) << " halfstave: " << halfstave; - LOG(info) << "off volume name 4 " << fMC->CurrentVolOffName(4) << " stave: " << stave; - LOG(info) << "SubDetector ID: " << subDetID << " Layer: " << layer << " staveinLayer: " << stave << " Chip ID: " << chipID; - } else if (subDetID == 1 && mGeometryTGeo->getNumberOfHalfStaves(layer) == 1) { // turbo geometry - LOG(info) << "off volume name 3 " << fMC->CurrentVolOffName(3) << " stave: " << stave; - LOG(info) << "SubDetector ID: " << subDetID << " Layer: " << layer << " staveinLayer: " << stave << " Chip ID: " << chipID; + + auto& trkPars = TRKBaseParam::Instance(); + + if (subDetID == 1) { // MLOT + if (trkPars.layoutMLOT == o2::trk::eMLOTLayout::kCylindrical) { + LOG(info) << "off volume name 1 " << fMC->CurrentVolOffName(1) << " chip: " << chip; + LOG(info) << "SubDetector ID: " << subDetID << " Layer: " << layer << " Chip ID: " << chipID; + } else { + LOG(info) << "off volume name 1 " << fMC->CurrentVolOffName(1) << " chip: " << chip; + LOG(info) << "off volume name 2 " << fMC->CurrentVolOffName(2) << " module: " << mod; + if (mGeometryTGeo->getNumberOfHalfStaves(layer) == 2) { // staggered geometry + LOG(info) << "off volume name 3 " << fMC->CurrentVolOffName(3) << " halfstave: " << halfstave; + LOG(info) << "off volume name 4 " << fMC->CurrentVolOffName(4) << " stave: " << stave; + LOG(info) << "SubDetector ID: " << subDetID << " Layer: " << layer << " staveinLayer: " << stave << " Chip ID: " << chipID; + } else if (mGeometryTGeo->getNumberOfHalfStaves(layer) == 1) { // turbo geometry + LOG(info) << "off volume name 3 " << fMC->CurrentVolOffName(3) << " stave: " << stave; + LOG(info) << "SubDetector ID: " << subDetID << " Layer: " << layer << " staveinLayer: " << stave << " Chip ID: " << chipID; + } + } } else { + // VD LOG(info) << "SubDetector ID: " << subDetID << " Chip ID: " << chipID; } + LOG(info); } From 6808cc40b2abd5baea093ab54b5f96fed6cc1053 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Tue, 17 Mar 2026 09:23:51 +0100 Subject: [PATCH 367/701] GPU TPC: Use stored field value during TPC track model decoding (#15170) --- GPU/GPUTracking/Base/GPUParam.h | 4 ++-- GPU/GPUTracking/Base/GPUReconstruction.cxx | 4 ++-- GPU/GPUTracking/Base/GPUReconstruction.h | 2 +- GPU/GPUTracking/Global/GPUChain.h | 1 + .../Global/GPUChainTrackingCompression.cxx | 19 +++++++++++++++++-- .../Interface/GPUO2InterfaceRefit.cxx | 2 +- .../Interface/GPUO2InterfaceUtils.cxx | 2 +- 7 files changed, 25 insertions(+), 9 deletions(-) diff --git a/GPU/GPUTracking/Base/GPUParam.h b/GPU/GPUTracking/Base/GPUParam.h index 847f3e05ea32e..0716274c5e198 100644 --- a/GPU/GPUTracking/Base/GPUParam.h +++ b/GPU/GPUTracking/Base/GPUParam.h @@ -80,10 +80,10 @@ struct GPUParam_t { struct GPUParam : public internal::GPUParam_t { #ifndef GPUCA_GPUCODE - void SetDefaults(float solenoidBz, bool assumeConstantBz); + void SetDefaults(float solenoidBz, bool assumeConstantBz = false); void SetDefaults(const GPUSettingsGRP* g, const GPUSettingsRec* r = nullptr, const GPUSettingsProcessing* p = nullptr, const GPURecoStepConfiguration* w = nullptr); void UpdateSettings(const GPUSettingsGRP* g, const GPUSettingsProcessing* p = nullptr, const GPURecoStepConfiguration* w = nullptr, const GPUSettingsRecDynamic* d = nullptr); - void UpdateBzOnly(float newSolenoidBz, bool assumeConstantBz); + void UpdateBzOnly(float newSolenoidBz, bool assumeConstantBz = false); void UpdateRun3ClusterErrors(const float* yErrorParam, const float* zErrorParam); #endif diff --git a/GPU/GPUTracking/Base/GPUReconstruction.cxx b/GPU/GPUTracking/Base/GPUReconstruction.cxx index fbbe815f63c33..f34a049a1588d 100644 --- a/GPU/GPUTracking/Base/GPUReconstruction.cxx +++ b/GPU/GPUTracking/Base/GPUReconstruction.cxx @@ -433,11 +433,11 @@ int32_t GPUReconstruction::InitPhaseAfterDevice() return 0; } -void GPUReconstruction::WriteConstantParams() +void GPUReconstruction::WriteConstantParams(int32_t stream) { if (IsGPU()) { const auto threadContext = GetThreadContext(); - WriteToConstantMemory(ptrDiff(&processors()->param, processors()), ¶m(), sizeof(param()), -1); + WriteToConstantMemory(ptrDiff(&processors()->param, processors()), ¶m(), sizeof(param()), stream); } } diff --git a/GPU/GPUTracking/Base/GPUReconstruction.h b/GPU/GPUTracking/Base/GPUReconstruction.h index b5dd29f940143..9a337c02ad26d 100644 --- a/GPU/GPUTracking/Base/GPUReconstruction.h +++ b/GPU/GPUTracking/Base/GPUReconstruction.h @@ -260,7 +260,7 @@ class GPUReconstruction virtual int32_t InitDevice() = 0; int32_t InitPhasePermanentMemory(); int32_t InitPhaseAfterDevice(); - void WriteConstantParams(); + void WriteConstantParams(int32_t stream = -1); virtual int32_t ExitDevice() = 0; virtual size_t WriteToConstantMemory(size_t offset, const void* src, size_t size, int32_t stream = -1, gpu_reconstruction_kernels::deviceEvent* ev = nullptr) = 0; void UpdateMaxMemoryUsed(); diff --git a/GPU/GPUTracking/Global/GPUChain.h b/GPU/GPUTracking/Global/GPUChain.h index 907ed7ea97c12..a524fd9ec3992 100644 --- a/GPU/GPUTracking/Global/GPUChain.h +++ b/GPU/GPUTracking/Global/GPUChain.h @@ -124,6 +124,7 @@ class GPUChain inline void TransferMemoryResourceLinkToGPU(RecoStep step, int16_t res, int32_t stream = -1, deviceEvent* ev = nullptr, deviceEvent* evList = nullptr, int32_t nEvents = 1) { timeCpy(step, true, &GPUReconstructionCPU::TransferMemoryResourceLinkToGPU, res, stream, ev, evList, nEvents); } inline void TransferMemoryResourceLinkToHost(RecoStep step, int16_t res, int32_t stream = -1, deviceEvent* ev = nullptr, deviceEvent* evList = nullptr, int32_t nEvents = 1) { timeCpy(step, false, &GPUReconstructionCPU::TransferMemoryResourceLinkToHost, res, stream, ev, evList, nEvents); } // Todo: retrieve step from proc, move kernelClass->GetStep to retrieve it from GetProcessor + inline void WriteConstantParams(int32_t stream = -1) { mRec->WriteConstantParams(stream); } inline void WriteToConstantMemory(RecoStep step, size_t offset, const void* src, size_t size, int32_t stream = -1, deviceEvent* ev = nullptr) { timeCpy(step, true, &GPUReconstructionCPU::WriteToConstantMemory, offset, src, size, stream, ev); } inline void GPUMemCpy(RecoStep step, void* dst, const void* src, size_t size, int32_t stream, int32_t toGPU, deviceEvent* ev = nullptr, deviceEvent* evList = nullptr, int32_t nEvents = 1) { timeCpy(step, toGPU, &GPUReconstructionCPU::GPUMemCpy, dst, src, size, stream, toGPU, ev, evList, nEvents); } inline void GPUMemCpyAlways(RecoStep step, void* dst, const void* src, size_t size, int32_t stream, int32_t toGPU, deviceEvent* ev = nullptr, deviceEvent* evList = nullptr, int32_t nEvents = 1) diff --git a/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx b/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx index 89d47d0e1b86c..f185348ed9169 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx @@ -225,6 +225,17 @@ int32_t GPUChainTracking::RunTPCDecompression() GPUFatal("tpcApplyCFCutsAtDecoding, tpcApplyClusterFilterOnCPU and tpcCutTimeBin currently require tpcUseOldCPUDecoding"); } + CompressedClusters cmprClsHost = *mIOPtrs.tpcCompressedClusters; + const bool useTemporaryBz = cmprClsHost.nTracks && cmprClsHost.solenoidBz != -1e6f && cmprClsHost.solenoidBz != param().bzkG && !GetProcessingSettings().doublePipeline; + std::unique_ptr tmpParam; + int32_t inputStream = 0; + + if (useTemporaryBz) { + tmpParam = std::make_unique(param()); + SynchronizeGPU(); + param().UpdateBzOnly(cmprClsHost.solenoidBz, mRec->GetGRPSettings().constBz); + WriteConstantParams(inputStream); + } if (GetProcessingSettings().tpcUseOldCPUDecoding) { const bool runFiltering = needFullFiltering || runTimeBinCutFiltering; const auto& threadContext = GetThreadContext(); @@ -268,7 +279,6 @@ int32_t GPUChainTracking::RunTPCDecompression() GPUTPCDecompression& Decompressor = processors()->tpcDecompressor; GPUTPCDecompression& DecompressorShadow = doGPU ? processorsShadow()->tpcDecompressor : Decompressor; const auto& threadContext = GetThreadContext(); - CompressedClusters cmprClsHost = *mIOPtrs.tpcCompressedClusters; CompressedClusters& inputGPU = Decompressor.mInputGPU; CompressedClusters& inputGPUShadow = DecompressorShadow.mInputGPU; @@ -279,7 +289,6 @@ int32_t GPUChainTracking::RunTPCDecompression() 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; int32_t unattachedStream = mRec->NStreams() - 1; inputGPU = cmprClsHost; SetupGPUProcessor(&Decompressor, true); @@ -437,6 +446,12 @@ int32_t GPUChainTracking::RunTPCDecompression() } mRec->PopNonPersistentMemory(RecoStep::TPCDecompression, qStr2Tag("TPCDCMPR")); } + if (useTemporaryBz) { + SynchronizeGPU(); + param() = *tmpParam; + tmpParam.reset(); + WriteConstantParams(); + } DoDebugDump(GPUChainTrackingDebugFlags::TPCDecompressedClusters, &GPUChainTracking::DumpClusters, *mDebugFile, mIOPtrs.clustersNative); return 0; } diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceRefit.cxx b/GPU/GPUTracking/Interface/GPUO2InterfaceRefit.cxx index f09c5d0a4b1cb..10a1a75368c96 100644 --- a/GPU/GPUTracking/Interface/GPUO2InterfaceRefit.cxx +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceRefit.cxx @@ -129,7 +129,7 @@ GPUO2InterfaceRefit::GPUO2InterfaceRefit(const ClusterNativeAccess* cl, const Co void GPUO2InterfaceRefit::updateCalib(const CorrectionMapsHelper* trans, float bzNominalGPU) { - mParam->UpdateBzOnly(bzNominalGPU, false); + mParam->UpdateBzOnly(bzNominalGPU); mRefit->SetFastTransformHelper(trans); } diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.cxx b/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.cxx index 43b8dc21eaf15..7f230ff20eb10 100644 --- a/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.cxx +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.cxx @@ -52,7 +52,7 @@ template <> void GPUO2InterfaceUtils::RunZSEncoder(const DigitArray& in, std::unique_ptr* outBuffer, uint32_t* outSizes, o2::raw::RawFileWriter* raw, const o2::InteractionRecord* ir, int32_t version, bool verify, float threshold, bool padding, std::function&)> digitsFilter) { GPUParam param; - param.SetDefaults(5.00668, false); + param.SetDefaults(5.00668); o2::gpu::GPUReconstructionConvert::RunZSEncoder(in, outBuffer, outSizes, raw, ir, param, version, verify, threshold, padding, digitsFilter); } template <> From 6f830a202b69e489b1bbd4bce4215860cb93b242 Mon Sep 17 00:00:00 2001 From: shahoian Date: Tue, 10 Mar 2026 22:29:56 +0100 Subject: [PATCH 368/701] Fix GPU b-field initialization The 12kA L3 field should be scaled wrt its own nominal rather than 30kA nominal Bz. Keep the old scaling for backward compatibility --- GPU/GPUTracking/Interface/GPUO2InterfaceUtils.cxx | 6 ++++++ GPU/GPUTracking/Interface/GPUO2InterfaceUtils.h | 3 ++- 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.cxx b/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.cxx index 7f230ff20eb10..b6491c72d83f3 100644 --- a/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.cxx +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.cxx @@ -142,3 +142,9 @@ uint32_t GPUO2InterfaceUtils::getTpcMaxTimeBinFromNHbf(uint32_t nHbf) { return (nHbf * o2::constants::lhc::LHCMaxBunches + 2 * o2::tpc::constants::LHCBCPERTIMEBIN - 2) / o2::tpc::constants::LHCBCPERTIMEBIN; } + +float GPUO2InterfaceUtils::getNominalGPUBzFromCurrent(float l3curr) +{ + float al3curr = CAMath::Abs(l3curr); + return (CAMath::Abs(al3curr - 12000) < CAMath::Abs(al3curr - 30000) ? (2.04487f / 12000.f) : (5.00668f / 30000.f)) * l3curr; +} diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.h b/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.h index 0b5d2b5aa3f7a..813444470082e 100644 --- a/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.h +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.h @@ -53,8 +53,9 @@ class GPUO2InterfaceUtils template static float getNominalGPUBz(T& src) { - return (5.00668f / 30000.f) * src.getL3Current(); + return getNominalGPUBzFromCurrent(src.getL3Current()); } + static float getNominalGPUBzFromCurrent(float l3curr); static std::unique_ptr getFullParam(float solenoidBz, uint32_t nHbfPerTf = 0, std::unique_ptr* pConfiguration = nullptr, std::unique_ptr* pO2Settings = nullptr, bool* autoMaxTimeBin = nullptr); static std::shared_ptr getFullParamShared(float solenoidBz, uint32_t nHbfPerTf = 0, std::unique_ptr* pConfiguration = nullptr, std::unique_ptr* pO2Settings = nullptr, bool* autoMaxTimeBin = nullptr); // Return owning pointer static void paramUseExternalOccupancyMap(GPUParam* param, uint32_t nHbfPerTf, const uint32_t* occupancymap, int32_t occupancyMapSize); From b0b8814350a8b4404fbb588766a260ea82ba9e5c Mon Sep 17 00:00:00 2001 From: David Rohr Date: Mon, 16 Mar 2026 20:50:53 +0100 Subject: [PATCH 369/701] GPU: Make memory scaling factors configKeyValues --- GPU/GPUTracking/Base/GPUReconstruction.cxx | 2 +- .../DataTypes/GPUMemorySizeScalers.cxx | 19 ++++++- .../DataTypes/GPUMemorySizeScalers.h | 57 ++++++------------- GPU/GPUTracking/Definitions/GPUSettingsList.h | 23 +++++++- .../GPUTrackingLinkDef_O2_DataTypes.h | 1 + prodtests/full-system-test/dpl-workflow.sh | 2 +- 6 files changed, 60 insertions(+), 44 deletions(-) diff --git a/GPU/GPUTracking/Base/GPUReconstruction.cxx b/GPU/GPUTracking/Base/GPUReconstruction.cxx index f34a049a1588d..f6aa62778a061 100644 --- a/GPU/GPUTracking/Base/GPUReconstruction.cxx +++ b/GPU/GPUTracking/Base/GPUReconstruction.cxx @@ -309,8 +309,8 @@ int32_t GPUReconstruction::InitPhaseBeforeDevice() mProcessingSettings->clusterizerZSSanityCheck = mProcessingSettings->mergerSanityCheck = mProcessingSettings->outputSanityCheck = true; } + static_cast(*mMemoryScalers) = GetProcessingSettings().scaling; mMemoryScalers->scalingFactor = GetProcessingSettings().memoryScalingFactor; - mMemoryScalers->conservative = GetProcessingSettings().conservativeMemoryEstimate; mMemoryScalers->returnMaxVal = GetProcessingSettings().forceMaxMemScalers != 0; if (GetProcessingSettings().forceMaxMemScalers > 1) { mMemoryScalers->rescaleMaxMem(GetProcessingSettings().forceMaxMemScalers); diff --git a/GPU/GPUTracking/DataTypes/GPUMemorySizeScalers.cxx b/GPU/GPUTracking/DataTypes/GPUMemorySizeScalers.cxx index 42ac2e8015f45..e9e1484249399 100644 --- a/GPU/GPUTracking/DataTypes/GPUMemorySizeScalers.cxx +++ b/GPU/GPUTracking/DataTypes/GPUMemorySizeScalers.cxx @@ -39,7 +39,7 @@ void GPUMemorySizeScalers::rescaleMaxMem(size_t newAvailableMemory) availableMemory = newAvailableMemory; } -double GPUMemorySizeScalers::getScalingFactor() +float GPUMemorySizeScalers::getScalingFactor() { if (!doFuzzing) { return scalingFactor; @@ -62,3 +62,20 @@ void GPUMemorySizeScalers::fuzzScalingFactor(uint64_t seed) fuzzSeed = seed; doFuzzing = true; } + +size_t GPUMemorySizeScalers::getValue(size_t maxVal, size_t val) +{ + return returnMaxVal ? maxVal : (std::min(maxVal, offset + val) * (doFuzzing == 0 ? scalingFactor : getScalingFactor()) * temporaryFactor); +} + +size_t GPUMemorySizeScalers::NTPCPeaks(size_t tpcDigits, bool perSector) { return getValue(perSector ? tpcMaxPeaks : (GPUCA_NSECTORS * tpcMaxPeaks), hitOffset + tpcDigits * tpcPeaksPerDigit); } +size_t GPUMemorySizeScalers::NTPCClusters(size_t tpcDigits, bool perSector) { return getValue(perSector ? tpcMaxSectorClusters : tpcMaxClusters, (conservativeMemoryEstimate ? 1.0 : tpcClustersPerPeak) * NTPCPeaks(tpcDigits, perSector)); } +size_t GPUMemorySizeScalers::NTPCStartHits(size_t tpcHits) { return getValue(tpcMaxStartHits, tpcHits * tpcStartHitsPerHit); } +size_t GPUMemorySizeScalers::NTPCRowStartHits(size_t tpcHits) { return getValue(tpcMaxRowStartHits, std::max(NTPCStartHits(tpcHits) * (tpcHits < 30000000 ? 20 : 12) / GPUCA_ROW_COUNT, tpcMinRowStartHits)); } +size_t GPUMemorySizeScalers::NTPCTracklets(size_t tpcHits, bool lowField) { return getValue(tpcMaxTracklets, NTPCStartHits(tpcHits) * (lowField ? tpcTrackletsPerStartHitLowField : tpcTrackletsPerStartHit)); } +size_t GPUMemorySizeScalers::NTPCTrackletHits(size_t tpcHits, bool lowField) { return getValue(tpcMaxTrackletHits, hitOffset + tpcHits * (lowField ? tpcTrackletHitsPerHitLowField : tpcTrackletHitsPerHit)); } +size_t GPUMemorySizeScalers::NTPCSectorTracks(size_t tpcHits) { return getValue(tpcMaxSectorTracks, tpcHits * tpcSectorTracksPerHit); } +size_t GPUMemorySizeScalers::NTPCSectorTrackHits(size_t tpcHits, uint8_t withRejection) { return getValue(tpcMaxSectorTrackHits, tpcHits * (withRejection ? tpcSectorTrackHitsPerHitWithRejection : tpcSectorTrackHitsPerHit)); } +size_t GPUMemorySizeScalers::NTPCMergedTracks(size_t tpcSectorTracks) { return getValue(tpcMaxMergedTracks, tpcSectorTracks * (conservativeMemoryEstimate ? 1.0 : tpcMergedTrackPerSectorTrack)); } +size_t GPUMemorySizeScalers::NTPCMergedTrackHits(size_t tpcSectorTrackHitss) { return getValue(tpcMaxMergedTrackHits, tpcSectorTrackHitss * tpcMergedTrackHitPerSectorHit); } +size_t GPUMemorySizeScalers::NTPCUnattachedHitsBase1024(int32_t type) { return (returnMaxVal || conservativeMemoryEstimate) ? 1024 : std::min(1024, tpcCompressedUnattachedHitsBase1024[type] * (doFuzzing == 0 ? scalingFactor : getScalingFactor()) * temporaryFactor); } diff --git a/GPU/GPUTracking/DataTypes/GPUMemorySizeScalers.h b/GPU/GPUTracking/DataTypes/GPUMemorySizeScalers.h index 067a11817d7ac..a81d296459acd 100644 --- a/GPU/GPUTracking/DataTypes/GPUMemorySizeScalers.h +++ b/GPU/GPUTracking/DataTypes/GPUMemorySizeScalers.h @@ -16,11 +16,12 @@ #define O2_GPU_GPUMEMORYSIZESCALERS_H #include "GPUDef.h" +#include "GPUSettings.h" namespace o2::gpu { -struct GPUMemorySizeScalers { +struct GPUMemorySizeScalers : public GPUSettingsProcessingScaling { // Input sizes size_t nTPCdigits = 0; size_t nTPCHits = 0; @@ -28,30 +29,10 @@ struct GPUMemorySizeScalers { size_t nITSTracks = 0; // General scaling factor - double scalingFactor = 1; + float scalingFactor = 1; uint64_t fuzzSeed = 0; uint64_t fuzzLimit = 0; - double temporaryFactor = 1; - bool conservative = 0; - - // Offset - double offset = 1000.; - double hitOffset = 20000; - - // Scaling Factors - double tpcPeaksPerDigit = 0.2; - 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; - double tpcMergedTrackPerSectorTrack = 1.0; - double tpcMergedTrackHitPerSectorHit = 1.1; - size_t tpcCompressedUnattachedHitsBase1024[3] = {900, 900, 500}; // No ratio, but integer fraction of 1024 for exact computation + float temporaryFactor = 1; // Upper limits size_t tpcMaxPeaks = 20000000; @@ -71,24 +52,20 @@ struct GPUMemorySizeScalers { bool doFuzzing = false; void rescaleMaxMem(size_t newAvailableMemory); - double getScalingFactor(); + float getScalingFactor(); void fuzzScalingFactor(uint64_t seed); - inline size_t getValue(size_t maxVal, size_t val) - { - return returnMaxVal ? maxVal : (std::min(maxVal, offset + val) * (doFuzzing == 0 ? scalingFactor : getScalingFactor()) * temporaryFactor); - } - - inline size_t NTPCPeaks(size_t tpcDigits, bool perSector = false) { return getValue(perSector ? tpcMaxPeaks : (GPUCA_NSECTORS * tpcMaxPeaks), hitOffset + tpcDigits * tpcPeaksPerDigit); } - 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, 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)); } - inline size_t NTPCMergedTrackHits(size_t tpcSectorTrackHitss) { return getValue(tpcMaxMergedTrackHits, tpcSectorTrackHitss * tpcMergedTrackHitPerSectorHit); } - inline size_t NTPCUnattachedHitsBase1024(int32_t type) { return (returnMaxVal || conservative) ? 1024 : std::min(1024, tpcCompressedUnattachedHitsBase1024[type] * (doFuzzing == 0 ? scalingFactor : getScalingFactor()) * temporaryFactor); } + size_t getValue(size_t maxVal, size_t val); + size_t NTPCPeaks(size_t tpcDigits, bool perSector = false); + size_t NTPCClusters(size_t tpcDigits, bool perSector = false); + size_t NTPCStartHits(size_t tpcHits); + size_t NTPCRowStartHits(size_t tpcHits); + size_t NTPCTracklets(size_t tpcHits, bool lowField); + size_t NTPCTrackletHits(size_t tpcHits, bool lowField); + size_t NTPCSectorTracks(size_t tpcHits); + size_t NTPCSectorTrackHits(size_t tpcHits, uint8_t withRejection = 0); + size_t NTPCMergedTracks(size_t tpcSectorTracks); + size_t NTPCMergedTrackHits(size_t tpcSectorTrackHitss); + size_t NTPCUnattachedHitsBase1024(int32_t type); }; } // namespace o2::gpu diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index e34af48d7a85e..57cb1371a4aa0 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -297,6 +297,27 @@ AddOption(nnCCDBInteractionRate, std::string, "500", "", 0, "Distinguishes betwe AddHelp("help", 'h') EndConfig() +// Settings steering the processing of NN Clusterization +BeginSubConfig(GPUSettingsProcessingScaling, scaling, configStandalone.proc, "SCALING", 0, "Processing settings for neural network clusterizer", proc_scaling) +AddOption(offset, float, 1000., "", 0, "Scaling Factor: offset") +AddOption(hitOffset, float, 20000, "", 0, "Scaling Factor: hitOffset") +AddOption(tpcPeaksPerDigit, float, 0.2, "", 0, "Scaling Factor: tpcPeaksPerDigit") +AddOption(tpcClustersPerPeak, float, 0.9, "", 0, "Scaling Factor: tpcClustersPerPeak") +AddOption(tpcStartHitsPerHit, float, 0.08, "", 0, "Scaling Factor: tpcStartHitsPerHit") +AddOption(tpcTrackletsPerStartHit, float, 0.8, "", 0, "Scaling Factor: tpcTrackletsPerStartHit") +AddOption(tpcTrackletsPerStartHitLowField, float, 0.85, "", 0, "Scaling Factor: tpcTrackletsPerStartHitLowField") +AddOption(tpcTrackletHitsPerHit, float, 5, "", 0, "Scaling Factor: tpcTrackletHitsPerHit") +AddOption(tpcTrackletHitsPerHitLowField, float, 7, "", 0, "Scaling Factor: tpcTrackletHitsPerHitLowField") +AddOption(tpcSectorTracksPerHit, float, 0.02, "", 0, "Scaling Factor: tpcSectorTracksPerHit") +AddOption(tpcSectorTrackHitsPerHit, float, 0.8, "", 0, "Scaling Factor: tpcSectorTrackHitsPerHit") +AddOption(tpcSectorTrackHitsPerHitWithRejection, float, 1.0, "", 0, "Scaling Factor: tpcSectorTrackHitsPerHitWithRejection") +AddOption(tpcMergedTrackPerSectorTrack, float, 1.0, "", 0, "Scaling Factor: tpcMergedTrackPerSectorTrack") +AddOption(tpcMergedTrackHitPerSectorHit, float, 1.1, "", 0, "Scaling Factor: tpcMergedTrackHitPerSectorHit") +AddOptionArray(tpcCompressedUnattachedHitsBase1024, int32_t, 3, (900, 900, 500), "", 0, "Scaling Factor: tpcCompressedUnattachedHitsBase1024") +AddOption(conservativeMemoryEstimate, bool, false, "", 0, "Use some more conservative defaults for larger buffers during TPC processing") +AddHelp("help", 'h') +EndConfig() + // Settings steering the processing once the device was selected, only available on the host BeginSubConfig(GPUSettingsProcessing, proc, configStandalone, "PROC", 0, "Processing settings", proc) AddOption(deviceNum, int32_t, -1, "gpuDevice", 0, "Set GPU device to use (-1: automatic, -2: for round-robin usage in timeslice-pipeline)") @@ -323,7 +344,6 @@ AddOption(forceMemoryPoolSize, uint64_t, 1, "memSize", 0, "Force size of allocat AddOption(forceHostMemoryPoolSize, uint64_t, 0, "hostMemSize", 0, "Force size of allocated host page locked host memory (overriding memSize)", min(0ul)) AddOption(memoryScalingFactor, float, 1.f, "", 0, "Factor to apply to all memory scalers") AddOption(memoryScalingFuzz, uint64_t, 0, "", 0, "Fuzz the memoryScalingFactor (0 disable, 1 enable, >1 set seed", def(1)) -AddOption(conservativeMemoryEstimate, bool, false, "", 0, "Use some more conservative defaults for larger buffers during TPC processing") AddOption(tpcInputWithClusterRejection, uint8_t, 0, "", 0, "Indicate whether the TPC input is CTF data with cluster rejection, to tune buffer estimations") AddOption(forceMaxMemScalers, uint64_t, 0, "", 0, "Force using the maximum values for all buffers, Set a value n > 1 to rescale all maximums to a memory size of n") AddOption(registerStandaloneInputMemory, bool, false, "registerInputMemory", 0, "Automatically register input memory buffers for the GPU") @@ -390,6 +410,7 @@ AddSubConfig(GPUSettingsProcessingRTC, rtc) AddSubConfig(GPUSettingsProcessingRTCtechnical, rtctech) AddSubConfig(GPUSettingsProcessingParam, param) AddSubConfig(GPUSettingsProcessingNNclusterizer, nn) +AddSubConfig(GPUSettingsProcessingScaling, scaling) AddHelp("help", 'h') EndConfig() #endif // __OPENCL__ diff --git a/GPU/GPUTracking/GPUTrackingLinkDef_O2_DataTypes.h b/GPU/GPUTracking/GPUTrackingLinkDef_O2_DataTypes.h index 5318e23e7d10f..9265c86f26ec9 100644 --- a/GPU/GPUTracking/GPUTrackingLinkDef_O2_DataTypes.h +++ b/GPU/GPUTracking/GPUTrackingLinkDef_O2_DataTypes.h @@ -32,6 +32,7 @@ #pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsProcessingRTC + ; #pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsProcessingRTCtechnical + ; #pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsProcessingNNclusterizer + ; +#pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsProcessingScaling + ; #pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsDisplay + ; #pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsDisplayLight + ; #pragma link C++ class o2::gpu::internal::GPUConfigurableParamGPUSettingsDisplayHeavy + ; diff --git a/prodtests/full-system-test/dpl-workflow.sh b/prodtests/full-system-test/dpl-workflow.sh index a3048a494796e..5b7ffc3cc6547 100755 --- a/prodtests/full-system-test/dpl-workflow.sh +++ b/prodtests/full-system-test/dpl-workflow.sh @@ -250,7 +250,7 @@ if [[ $SYNCRAWMODE == 1 ]]; then TOF_CONFIG+=" --for-calib" fi if [[ $SYNCRAWMODE == 1 ]] || [[ $SYNCMODE == 0 && $CTFINPUT == 1 && $GPUTYPE != "CPU" ]]; then - GPU_CONFIG_KEY+="GPU_proc.conservativeMemoryEstimate=1;" + GPU_CONFIG_KEY+="GPU_proc_scaling.conservativeMemoryEstimate=1;" fi if [[ $SYNCMODE == 1 && "0${ED_NO_ITS_ROF_FILTER:-}" != "01" && $BEAMTYPE == "PbPb" ]] && has_detector ITS; then From decf5734f5f3787efa512cf4b65d20e2e9844d8f Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Tue, 17 Mar 2026 13:49:44 +0100 Subject: [PATCH 370/701] Workaround for non-null vertexes in event pools (#15169) --- Generators/include/Generators/GeneratorFromFile.h | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/Generators/include/Generators/GeneratorFromFile.h b/Generators/include/Generators/GeneratorFromFile.h index 329510f844f05..9bf1d4911008b 100644 --- a/Generators/include/Generators/GeneratorFromFile.h +++ b/Generators/include/Generators/GeneratorFromFile.h @@ -89,6 +89,7 @@ class GeneratorFromO2Kine : public o2::eventgen::Generator void setContinueMode(bool val) { mContinueMode = val; }; /** methods that can be overridden **/ void updateHeader(o2::dataformats::MCEventHeader* eventHeader) override; + const o2::dataformats::MCEventHeader* getOrigMCEventHeader() const { return mOrigMCEventHeader.get(); } private: TFile* mEventFile = nullptr; //! the file containing the persistent events @@ -138,7 +139,17 @@ class GeneratorFromEventPool : public o2::eventgen::Generator auto import_good = mO2KineGenerator->importParticles(); // transfer the particles (could be avoided) mParticles = mO2KineGenerator->getParticles(); - + auto original_header = mO2KineGenerator->getOrigMCEventHeader(); + // Workaround to fix vertex shifted particles from event pools (valid for builds released before 14 March 2026) + if (original_header) { + double vertex[3] = {original_header->GetX(), original_header->GetY(), original_header->GetZ()}; + if (vertex[0] != 0. || vertex[1] != 0. || vertex[2] != 0.) { + LOG(debug) << "Subtracting shifted vertex from EventPool: (" << vertex[0] << ", " << vertex[1] << ", " << vertex[2] << ")"; + for (auto& p : mParticles) { + p.SetProductionVertex(p.Vx() - vertex[0], p.Vy() - vertex[1], p.Vz() - vertex[2], p.T()); + } + } + } return import_good; } From 66124b288bf794bbc9b550dcdf8dc2e80997429c Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Tue, 17 Mar 2026 17:11:39 +0100 Subject: [PATCH 371/701] Fix boost 1.90.0 / clang issue (#15176) --- Detectors/TPC/calibration/src/CalibdEdx.cxx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Detectors/TPC/calibration/src/CalibdEdx.cxx b/Detectors/TPC/calibration/src/CalibdEdx.cxx index 938ab8ae91065..5205c9348dec7 100644 --- a/Detectors/TPC/calibration/src/CalibdEdx.cxx +++ b/Detectors/TPC/calibration/src/CalibdEdx.cxx @@ -590,7 +590,9 @@ int CalibdEdx::minStackEntries() const auto projection = bh::algorithm::project(mHist, std::vector{Axis::Sector, Axis::Stack, Axis::Charge}); auto dEdxCounts = bh::indexed(projection); // find the stack with the least number of entries - auto min_it = std::min_element(dEdxCounts.begin(), dEdxCounts.end()); + // use explicit int comparator to avoid ambiguous operator< between accessor and unlimited_storage::reference (boost/clang issue) + auto min_it = std::min_element(dEdxCounts.begin(), dEdxCounts.end(), + [](const auto& a, const auto& b) { return static_cast(*a) < static_cast(*b); }); return *min_it; } From 6527904f57954ebe49d6eed18fba021a5cf32ea4 Mon Sep 17 00:00:00 2001 From: altsybee Date: Tue, 17 Mar 2026 21:59:02 +0100 Subject: [PATCH 372/701] [ALICE3] update of readme for FT3 and ALICE3 (#15178) Co-authored-by: Igor Altsybeev --- Detectors/Upgrades/ALICE3/FT3/README.md | 20 ++++++++++++++++++++ Detectors/Upgrades/ALICE3/README.md | 15 +++++++++++++++ 2 files changed, 35 insertions(+) diff --git a/Detectors/Upgrades/ALICE3/FT3/README.md b/Detectors/Upgrades/ALICE3/FT3/README.md index 71cb7a6e63bb9..34a6782a2b0c2 100644 --- a/Detectors/Upgrades/ALICE3/FT3/README.md +++ b/Detectors/Upgrades/ALICE3/FT3/README.md @@ -6,5 +6,25 @@ This is top page for the FT3 detector documentation. +## Specific detector setup + + +Configuration of the endcap disks can be done by setting values for the `FT3Base.layoutFT3` configurable, +the available options are presented in the following Table: + +| Option | Comments | +| ---------------------- | ----------------------------------------------------------------------------------------------------------------- | +| `kSegmented` (default) | Currently, only OT disks have realistic implementation, for ML - simple trapezoids | +| `kTrapezoidal` | Simple trapezoisal disks (in both ML and OT), with `FT3Base.nTrapezoidalSegments=32` | +| `kCylindrical` | Simplest possible disks as TGeoTubes (ML and OT), bad for ACTS (wrong digi due to polar coorinates on disk sides) | + +[ [Link to definitions](./base/include/FT3Base/FT3BaseParam.h) ] + +For example, a geometry with the endcaps-only can be obtained by +```bash +o2-sim-serial-run5 -n 1 -g pythia8hi -m FT3 \ + --configKeyValues "FT3Base.layoutFT3=kTrapezoidal" +``` + diff --git a/Detectors/Upgrades/ALICE3/README.md b/Detectors/Upgrades/ALICE3/README.md index 44a478b592882..6ff034facb546 100644 --- a/Detectors/Upgrades/ALICE3/README.md +++ b/Detectors/Upgrades/ALICE3/README.md @@ -69,8 +69,23 @@ Configurables for various sub-detectors are presented in the following Table: | Available options | Link to options | | ----------------- | ---------------------------------------------------------------- | | TRK | [Link to TRK options](./TRK/README.md#specific-detector-setup) | +| FT3 | [Link to FT3 options](./FT3/README.md#specific-detector-setup) | | TOF | [Link to TOF options](./IOTOF/README.md#specific-detector-setup) | +Example O2 command to create a geometry with **segmented layers for TRK (expect for VD), FT3 and TOF:** + +```bash +o2-sim-serial-run5 -n 1 -g pythia8hi -m A3IP TRK FT3 TF3 \ +--configKeyValues "TRKBase.layoutVD=kIRISFullCyl;TRKBase.layoutMLOT=kSegmented;FT3Base.layoutFT3=kSegmented;IOTOFBase.segmentedInnerTOF=true;IOTOFBase.segmentedOuterTOF=true" +``` + +Example O2 command to create a geometry with **simple (non-segmented) layers for TRK, FT3 and TOF**: + +```bash +o2-sim-serial-run5 -n 1 -g pythia8hi -m A3IP TRK FT3 TF3 \ +--configKeyValues "TRKBase.layoutVD=kIRISFullCyl;TRKBase.layoutMLOT=kCylindrical;FT3Base.layoutFT3=kTrapezoidal;IOTOFBase.segmentedInnerTOF=false;IOTOFBase.segmentedOuterTOF=false" +``` + ### Output of the simulation The simulation will produce a `o2sim_Hits.root` file with a tree with the hits related to that detector. From c14106b3db7cecd2149745d497b5eb81105e3998 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Tue, 17 Mar 2026 17:25:12 +0100 Subject: [PATCH 373/701] DPL: protect against missing monitoring --- Framework/AnalysisSupport/src/DataInputDirector.cxx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/Framework/AnalysisSupport/src/DataInputDirector.cxx b/Framework/AnalysisSupport/src/DataInputDirector.cxx index ace4565449c4b..7027655b7abe7 100644 --- a/Framework/AnalysisSupport/src/DataInputDirector.cxx +++ b/Framework/AnalysisSupport/src/DataInputDirector.cxx @@ -314,7 +314,9 @@ void DataInputDescriptor::printFileOpening() monitoringInfo += fmt::format(",se={},open_time={:.1f}", alienFile->GetSE(), alienFile->GetElapsed()); } #endif - mContext.monitoring->send(o2::monitoring::Metric{monitoringInfo, "aod-file-open-info"}.addTag(o2::monitoring::tags::Key::Subsystem, o2::monitoring::tags::Value::DPL)); + if (mContext.monitoring) { + mContext.monitoring->send(o2::monitoring::Metric{monitoringInfo, "aod-file-open-info"}.addTag(o2::monitoring::tags::Key::Subsystem, o2::monitoring::tags::Value::DPL)); + } LOGP(info, "Opening file: {}", monitoringInfo); } @@ -335,7 +337,9 @@ void DataInputDescriptor::printFileStatistics() monitoringInfo += fmt::format(",se={},open_time={:.1f}", alienFile->GetSE(), alienFile->GetElapsed()); } #endif - mContext.monitoring->send(o2::monitoring::Metric{monitoringInfo, "aod-file-read-info"}.addTag(o2::monitoring::tags::Key::Subsystem, o2::monitoring::tags::Value::DPL)); + if (mContext.monitoring) { + mContext.monitoring->send(o2::monitoring::Metric{monitoringInfo, "aod-file-read-info"}.addTag(o2::monitoring::tags::Key::Subsystem, o2::monitoring::tags::Value::DPL)); + } LOGP(info, "Read info: {}", monitoringInfo); } From 87b5043479fa0db653b18cfdf84b41458acadac3 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Tue, 17 Mar 2026 17:25:12 +0100 Subject: [PATCH 374/701] DPL: allow non-owning TFileFileSystem Given this filesystem is really a virtual entity, it makes sense to allow for actual ownership of the TFile elsewhere. --- .../Core/include/Framework/RootArrowFilesystem.h | 3 ++- Framework/Core/src/RootArrowFilesystem.cxx | 11 +++++++---- 2 files changed, 9 insertions(+), 5 deletions(-) diff --git a/Framework/Core/include/Framework/RootArrowFilesystem.h b/Framework/Core/include/Framework/RootArrowFilesystem.h index 5aceaed077001..07aaa348c220a 100644 --- a/Framework/Core/include/Framework/RootArrowFilesystem.h +++ b/Framework/Core/include/Framework/RootArrowFilesystem.h @@ -146,7 +146,7 @@ class TFileFileSystem : public VirtualRootFileSystemBase public: arrow::Result GetFileInfo(const std::string& path) override; - TFileFileSystem(TDirectoryFile* f, size_t readahead, RootObjectReadingFactory&); + TFileFileSystem(TDirectoryFile* f, size_t readahead, RootObjectReadingFactory&, bool ownsFile = true); ~TFileFileSystem() override; @@ -172,6 +172,7 @@ class TFileFileSystem : public VirtualRootFileSystemBase private: TDirectoryFile* mFile; RootObjectReadingFactory& mObjectFactory; + bool mOwnsFile = true; }; class TBufferFileFS : public VirtualRootFileSystemBase diff --git a/Framework/Core/src/RootArrowFilesystem.cxx b/Framework/Core/src/RootArrowFilesystem.cxx index 403e393ec6090..6976c710062e6 100644 --- a/Framework/Core/src/RootArrowFilesystem.cxx +++ b/Framework/Core/src/RootArrowFilesystem.cxx @@ -34,18 +34,21 @@ namespace o2::framework { using arrow::Status; -TFileFileSystem::TFileFileSystem(TDirectoryFile* f, size_t readahead, RootObjectReadingFactory& factory) +TFileFileSystem::TFileFileSystem(TDirectoryFile* f, size_t readahead, RootObjectReadingFactory& factory, bool ownsFile) : VirtualRootFileSystemBase(), mFile(f), - mObjectFactory(factory) + mObjectFactory(factory), + mOwnsFile(ownsFile) { ((TFile*)mFile)->SetReadaheadSize(50 * 1024 * 1024); } TFileFileSystem::~TFileFileSystem() { - mFile->Close(); - delete mFile; + if (mOwnsFile) { + mFile->Close(); + delete mFile; + } } std::shared_ptr TFileFileSystem::GetObjectHandler(arrow::dataset::FileSource source) From f03975009cbea2a6d30736cf8c25f15711ea1e83 Mon Sep 17 00:00:00 2001 From: Pavel Larionov Date: Wed, 18 Mar 2026 06:35:38 +0100 Subject: [PATCH 375/701] Fix overlap IRIS vacuum (#15185) --- .../Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx index 48cd0f37d2eb5..2f1a83f73bca3 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx @@ -608,8 +608,8 @@ static void addIRISServiceModulesSegmented(TGeoVolume* petalAsm, int nPetals) // --- 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 = kPetalZ_cm; // cm - constexpr double vacuumVesselThickness = kWallThick_cm; // cm (0.2 mm) + 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 From 38ccad5c51bb417ccd978e454f5256396f7c420e Mon Sep 17 00:00:00 2001 From: Andrea Sofia Triolo Date: Wed, 18 Mar 2026 06:39:12 +0100 Subject: [PATCH 376/701] [ALICE3] TRK: add noise to the digitization process (#15167) * ALICE3-TRK: add noise to the digitization process * Please consider the following formatting changes --------- Co-authored-by: ALICE Action Bot --- .../TRKSimulation/ChipDigitsContainer.h | 4 ++ .../include/TRKSimulation/DPLDigitizerParam.h | 2 +- .../include/TRKSimulation/DigiParams.h | 2 +- .../simulation/src/ChipDigitsContainer.cxx | 47 +++++++++++++++++++ .../ALICE3/TRK/simulation/src/Digitizer.cxx | 2 +- 5 files changed, 54 insertions(+), 3 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/ChipDigitsContainer.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/ChipDigitsContainer.h index 73c95b04c45e3..bf28ace0724bc 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/ChipDigitsContainer.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/ChipDigitsContainer.h @@ -35,6 +35,10 @@ class ChipDigitsContainer : public o2::itsmft::ChipDigitsContainer return (static_cast(roframe) << (8 * sizeof(UInt_t))) + (static_cast(col) << (8 * sizeof(Short_t))) + row; } + /// Adds noise digits, deleted the one using the itsmft::DigiParams interface + void addNoise(UInt_t rofMin, UInt_t rofMax, const o2::itsmft::DigiParams* params, int maxRows = o2::itsmft::SegmentationAlpide::NRows, int maxCols = o2::itsmft::SegmentationAlpide::NCols) = delete; + void addNoise(UInt_t rofMin, UInt_t rofMax, const o2::trk::DigiParams* params, int subDetID, int layer); + 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 15ed63e46e21f..168ae172f4b86 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DPLDigitizerParam.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DPLDigitizerParam.h @@ -54,7 +54,7 @@ struct DPLDigitizerParam : public o2::conf::ConfigurableParamHelpergetNoisePerPixel() * maxRows * maxCols; + nel = static_cast(params->getChargeThreshold() * 1.1); + } else { // ML/OT + maxRows = constants::moduleMLOT::chip::nRows; + maxCols = constants::moduleMLOT::chip::nCols; + mean = params->getNoisePerPixel() * maxRows * maxCols; + nel = static_cast(params->getChargeThreshold() * 1.1); + } + + LOG(debug) << "Adding noise for chip " << mChipIndex << " with mean " << mean << " and charge " << nel; + + for (UInt_t rof = rofMin; rof <= rofMax; rof++) { + nhits = gRandom->Poisson(mean); + for (Int_t i = 0; i < nhits; ++i) { + row = gRandom->Integer(maxRows); + col = gRandom->Integer(maxCols); + LOG(debug) << "Generated noise hit at ROF " << rof << ", row " << row << ", col " << col; + if (mNoiseMap && mNoiseMap->isNoisy(mChipIndex, row, col)) { + continue; + } + if (mDeadChanMap && mDeadChanMap->isNoisy(mChipIndex, row, col)) { + continue; + } + auto key = getOrderingKey(rof, row, col); + if (!findDigit(key)) { + addDigit(key, rof, row, col, nel, o2::MCCompLabel(true)); + } + } + } +} diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx index 52eaccfe045f7..31ef19a21cce9 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx @@ -215,7 +215,7 @@ void Digitizer::fillOutputContainer(uint32_t frameLast) if (chip.isDisabled()) { continue; } - // chip.addNoise(mROFrameMin, mROFrameMin, &mParams); /// TODO: add noise + chip.addNoise(mROFrameMin, mROFrameMin, &mParams, mGeometry->getSubDetID(chip.getChipIndex()), mGeometry->getLayer(chip.getChipIndex())); /// TODO: add noise auto& buffer = chip.getPreDigits(); if (buffer.empty()) { continue; From 084a2371c92a8988f37eb45ab3fc1865bca3960b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Wed, 18 Mar 2026 09:05:45 +0100 Subject: [PATCH 377/701] [ALICE3] TRK: allow ACTS clustering (#15174) * [ALICE3] TRK: allow ACTS clustering * Add ACTS clusterer --- .../ALICE3/TRK/reconstruction/CMakeLists.txt | 22 +- .../include/TRKReconstruction/Clusterer.h | 22 +- .../include/TRKReconstruction/ClustererACTS.h | 43 ++ .../TRK/reconstruction/src/ClustererACTS.cxx | 392 ++++++++++++++++++ .../src/TRKReconstructionLinkDef.h | 4 + .../include/TRKWorkflow/ClustererSpec.h | 9 + .../ALICE3/TRK/workflow/src/ClustererSpec.cxx | 45 +- 7 files changed, 514 insertions(+), 23 deletions(-) create mode 100644 Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/ClustererACTS.h create mode 100644 Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt index b9866c7d6aa4d..59a7f47955938 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt @@ -9,10 +9,16 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. +if(Acts_FOUND) + set(actsTarget Acts::Core) +endif() + o2_add_library(TRKReconstruction TARGETVARNAME targetName SOURCES src/TimeFrame.cxx src/Clusterer.cxx + $<$:src/ClustererACTS.cxx> + $<$:src/TrackerACTS.cxx> PUBLIC_LINK_LIBRARIES O2::ITStracking O2::GPUCommon @@ -27,11 +33,23 @@ o2_add_library(TRKReconstruction O2::DataFormatsITS O2::TRKSimulation nlohmann_json::nlohmann_json + ${actsTarget} PRIVATE_LINK_LIBRARIES O2::Steer TBB::tbb) +if(Acts_FOUND) + target_compile_definitions(${targetName} PUBLIC O2_WITH_ACTS) +endif() + +set(dictHeaders include/TRKReconstruction/TimeFrame.h + include/TRKReconstruction/Clusterer.h) + +if(Acts_FOUND) + list(APPEND dictHeaders include/TRKReconstruction/ClustererACTS.h + include/TRKReconstruction/TrackerACTS.h) +endif() + o2_target_root_dictionary(TRKReconstruction - HEADERS include/TRKReconstruction/TimeFrame.h - include/TRKReconstruction/Clusterer.h + HEADERS ${dictHeaders} LINKDEF src/TRKReconstructionLinkDef.h) diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/Clusterer.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/Clusterer.h index abddafa312fb9..70518b2ace593 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/Clusterer.h +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/Clusterer.h @@ -161,17 +161,17 @@ class Clusterer }; //---------------------------------------------- - void process(gsl::span digits, - gsl::span digitROFs, - std::vector& clusters, - std::vector& patterns, - std::vector& clusterROFs, - const ConstDigitTruth* digitLabels = nullptr, - ClusterTruth* clusterLabels = nullptr, - gsl::span digMC2ROFs = {}, - std::vector* clusterMC2ROFs = nullptr); - - private: + virtual void process(gsl::span digits, + gsl::span digitROFs, + std::vector& clusters, + std::vector& patterns, + std::vector& clusterROFs, + const ConstDigitTruth* digitLabels = nullptr, + ClusterTruth* clusterLabels = nullptr, + gsl::span digMC2ROFs = {}, + std::vector* clusterMC2ROFs = nullptr); + + protected: int mNHugeClus = 0; std::unique_ptr mThread; std::vector mSortIdx; ///< reusable per-ROF sort buffer diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/ClustererACTS.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/ClustererACTS.h new file mode 100644 index 0000000000000..4111737d17a9f --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/ClustererACTS.h @@ -0,0 +1,43 @@ +// 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. + +/// \file ClustererACTS.h +/// \brief Definition of the TRK cluster finder + +#ifndef ALICEO2_TRK_CLUSTERERACTS_H +#define ALICEO2_TRK_CLUSTERERACTS_H + +#include "TRKReconstruction/Clusterer.h" + +namespace o2::trk +{ + +class GeometryTGeo; + +class ClustererACTS : public Clusterer +{ + public: + void process(gsl::span digits, + gsl::span digitROFs, + std::vector& clusters, + std::vector& patterns, + std::vector& clusterROFs, + const ConstDigitTruth* digitLabels = nullptr, + ClusterTruth* clusterLabels = nullptr, + gsl::span digMC2ROFs = {}, + std::vector* clusterMC2ROFs = nullptr) override; + + private: +}; + +} // namespace o2::trk + +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx new file mode 100644 index 0000000000000..0cf7c26e0ea41 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx @@ -0,0 +1,392 @@ +// 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. + +/// \file ClustererACTS.cxx +/// \brief Implementation of the TRK cluster finder with the ACTS + +#include "TRKReconstruction/ClustererACTS.h" +#include "TRKBase/GeometryTGeo.h" +#include "DataFormatsITSMFT/ClusterPattern.h" +#include + +#include +#include +#include + +using namespace o2::trk; + +// Data formats for ACTS interface +struct Cell2D { + Cell2D(int rowv, int colv, uint32_t digIdx = 0) : row(rowv), col(colv), digitIdx(digIdx) {} + int row, col; + uint32_t digitIdx; ///< Index of the original digit (for MC label retrieval) + Acts::Ccl::Label label{Acts::Ccl::NO_LABEL}; +}; + +int getCellRow(const Cell2D& cell) +{ + return cell.row; +} + +int getCellColumn(const Cell2D& cell) +{ + return cell.col; +} + +bool operator==(const Cell2D& left, const Cell2D& right) +{ + return left.row == right.row && left.col == right.col; +} + +bool cellComp(const Cell2D& left, const Cell2D& right) +{ + return (left.row == right.row) ? left.col < right.col : left.row < right.row; +} + +struct Cluster2D { + std::vector cells; + std::size_t hash{0}; +}; + +void clusterAddCell(Cluster2D& cl, const Cell2D& cell) +{ + cl.cells.push_back(cell); +} + +void hash(Cluster2D& cl) +{ + std::ranges::sort(cl.cells, cellComp); + cl.hash = 0; + // for (const Cell2D& c : cl.cells) { + // boost::hash_combine(cl.hash, c.col); + // } +} + +bool clHashComp(const Cluster2D& left, const Cluster2D& right) +{ + return left.hash < right.hash; +} + +template +void genclusterw(int x, int y, int x0, int y0, int x1, int y1, + std::vector& cells, RNG& rng, double startp = 0.5, + double decayp = 0.9) +{ + std::vector add; + + auto maybe_add = [&](int x_, int y_) { + Cell2D c(x_, y_); + // if (std::uniform_real_distribution()(rng) < startp && + // !rangeContainsValue(cells, c)) { + // cells.push_back(c); + // add.push_back(c); + // } + }; + + // NORTH + if (y < y1) { + maybe_add(x, y + 1); + } + // NORTHEAST + if (x < x1 && y < y1) { + maybe_add(x + 1, y + 1); + } + // EAST + if (x < x1) { + maybe_add(x + 1, y); + } + // SOUTHEAST + if (x < x1 && y > y0) { + maybe_add(x + 1, y - 1); + } + // SOUTH + if (y > y0) { + maybe_add(x, y - 1); + } + // SOUTHWEST + if (x > x0 && y > y0) { + maybe_add(x - 1, y - 1); + } + // WEST + if (x > x0) { + maybe_add(x - 1, y); + } + // NORTHWEST + if (x > x0 && y < y1) { + maybe_add(x - 1, y + 1); + } + + for (Cell2D& c : add) { + genclusterw(c.row, c.col, x0, y0, x1, y1, cells, rng, startp * decayp, + decayp); + } +} + +template +Cluster2D gencluster(int x0, int y0, int x1, int y1, RNG& rng, + double startp = 0.5, double decayp = 0.9) +{ + int x0_ = x0 + 1; + int x1_ = x1 - 1; + int y0_ = y0 + 1; + int y1_ = y1 - 1; + + int x = std::uniform_int_distribution(x0_, x1_)(rng); + int y = std::uniform_int_distribution(y0_, y1_)(rng); + + std::vector cells = {Cell2D(x, y)}; + genclusterw(x, y, x0_, y0_, x1_, y1_, cells, rng, startp, decayp); + + Cluster2D cl; + cl.cells = std::move(cells); + + return cl; +} + +//__________________________________________________ +void ClustererACTS::process(gsl::span digits, + gsl::span digitROFs, + std::vector& clusters, + std::vector& patterns, + std::vector& clusterROFs, + const ConstDigitTruth* digitLabels, + ClusterTruth* clusterLabels, + gsl::span digMC2ROFs, + std::vector* clusterMC2ROFs) +{ + if (!mThread) { + mThread = std::make_unique(this); + } + + auto* geom = o2::trk::GeometryTGeo::Instance(); + + for (size_t iROF = 0; iROF < digitROFs.size(); ++iROF) { + const auto& inROF = digitROFs[iROF]; + const auto outFirst = static_cast(clusters.size()); + const int first = inROF.getFirstEntry(); + const int nEntries = inROF.getNEntries(); + + if (nEntries == 0) { + clusterROFs.emplace_back(inROF.getBCData(), inROF.getROFrame(), outFirst, 0); + continue; + } + + // Sort digit indices within this ROF by (chipID, col, row) so we can process + // chip by chip, column by column -- the same ordering the ALPIDE scanner expects. + mSortIdx.resize(nEntries); + std::iota(mSortIdx.begin(), mSortIdx.end(), first); + std::sort(mSortIdx.begin(), mSortIdx.end(), [&digits](int a, int b) { + const auto& da = digits[a]; + const auto& db = digits[b]; + if (da.getChipIndex() != db.getChipIndex()) { + return da.getChipIndex() < db.getChipIndex(); + } + if (da.getColumn() != db.getColumn()) { + return da.getColumn() < db.getColumn(); + } + return da.getRow() < db.getRow(); + }); + + // Type aliases for ACTS clustering + using Cell = Cell2D; + using CellCollection = std::vector; + using Cluster = Cluster2D; + using ClusterCollection = std::vector; + static constexpr int GridDim = 2; ///< Dimensionality of the clustering grid (2D for pixel detectors) + + CellCollection cells; // Input collection of cells (pixels) to be clustered + Acts::Ccl::ClusteringData data; // Internal data structure used by ACTS clustering algorithm + ClusterCollection clsCollection; // Output collection of clusters found by the algorithm + + // Process one chip at a time + int sliceStart = 0; + while (sliceStart < nEntries) { + const int chipFirst = sliceStart; + const uint16_t chipID = digits[mSortIdx[sliceStart]].getChipIndex(); + while (sliceStart < nEntries && digits[mSortIdx[sliceStart]].getChipIndex() == chipID) { + ++sliceStart; + } + const int chipN = sliceStart - chipFirst; + + // Fill cells from digits for this chip + cells.clear(); + data.clear(); + clsCollection.clear(); + cells.reserve(chipN); + for (int i = chipFirst; i < chipFirst + chipN; ++i) { + const auto& digit = digits[mSortIdx[i]]; + cells.emplace_back(digit.getRow(), digit.getColumn(), mSortIdx[i]); + } + + LOG(debug) << "Clustering with ACTS on chip " << chipID << " " << cells.size() << " digits"; + Acts::Ccl::createClusters(data, + cells, + clsCollection, + Acts::Ccl::DefaultConnect(false)); + + LOG(debug) << " found " << clsCollection.size() << " clusters"; + + // Convert ACTS clusters to O2 clusters + for (const auto& actsCluster : clsCollection) { + if (actsCluster.cells.empty()) { + continue; + } + + // Calculate bounding box + uint16_t rowMin = static_cast(actsCluster.cells[0].row); + uint16_t rowMax = rowMin; + uint16_t colMin = static_cast(actsCluster.cells[0].col); + uint16_t colMax = colMin; + + for (const auto& cell : actsCluster.cells) { + rowMin = std::min(rowMin, static_cast(cell.row)); + rowMax = std::max(rowMax, static_cast(cell.row)); + colMin = std::min(colMin, static_cast(cell.col)); + colMax = std::max(colMax, static_cast(cell.col)); + } + + const uint16_t rowSpan = rowMax - rowMin + 1; + const uint16_t colSpan = colMax - colMin + 1; + + // Check if cluster needs splitting (too large for pattern encoding) + const bool isHuge = rowSpan > o2::itsmft::ClusterPattern::MaxRowSpan || + colSpan > o2::itsmft::ClusterPattern::MaxColSpan; + + if (isHuge) { + // Split huge cluster into MaxRowSpan x MaxColSpan tiles + LOG(warning) << "Splitting huge TRK cluster: chipID " << chipID + << ", rows " << rowMin << ":" << rowMax + << " cols " << colMin << ":" << colMax; + + for (uint16_t tileColMin = colMin; tileColMin <= colMax; + tileColMin = static_cast(tileColMin + o2::itsmft::ClusterPattern::MaxColSpan)) { + uint16_t tileColMax = std::min(colMax, static_cast(tileColMin + o2::itsmft::ClusterPattern::MaxColSpan - 1)); + + for (uint16_t tileRowMin = rowMin; tileRowMin <= rowMax; + tileRowMin = static_cast(tileRowMin + o2::itsmft::ClusterPattern::MaxRowSpan)) { + uint16_t tileRowMax = std::min(rowMax, static_cast(tileRowMin + o2::itsmft::ClusterPattern::MaxRowSpan - 1)); + + // Collect cells in this tile + std::vector> tileCells; + for (const auto& cell : actsCluster.cells) { + uint16_t r = static_cast(cell.row); + uint16_t c = static_cast(cell.col); + if (r >= tileRowMin && r <= tileRowMax && c >= tileColMin && c <= tileColMax) { + tileCells.emplace_back(r, c); + } + } + + if (tileCells.empty()) { + continue; + } + + uint16_t tileRowSpan = tileRowMax - tileRowMin + 1; + uint16_t tileColSpan = tileColMax - tileColMin + 1; + + // Encode pattern for this tile + std::array patt{}; + for (const auto& [r, c] : tileCells) { + uint32_t ir = r - tileRowMin; + uint32_t ic = c - tileColMin; + int nbit = ir * tileColSpan + ic; + patt[nbit >> 3] |= (0x1 << (7 - (nbit % 8))); + } + patterns.emplace_back(static_cast(tileRowSpan)); + patterns.emplace_back(static_cast(tileColSpan)); + const int nBytes = (tileRowSpan * tileColSpan + 7) / 8; + patterns.insert(patterns.end(), patt.begin(), patt.begin() + nBytes); + + // Handle MC labels for this tile + if (clusterLabels && digitLabels) { + const auto clsIdx = static_cast(clusters.size()); + for (const auto& cell : actsCluster.cells) { + uint16_t r = static_cast(cell.row); + uint16_t c = static_cast(cell.col); + if (r >= tileRowMin && r <= tileRowMax && c >= tileColMin && c <= tileColMax) { + if (cell.digitIdx < digitLabels->getIndexedSize()) { + const auto& lbls = digitLabels->getLabels(cell.digitIdx); + for (const auto& lbl : lbls) { + clusterLabels->addElement(clsIdx, lbl); + } + } + } + } + } + + // Create O2 cluster for this tile + o2::trk::Cluster cluster; + cluster.chipID = chipID; + cluster.row = tileRowMin; + cluster.col = tileColMin; + cluster.size = static_cast(tileCells.size()); + if (geom) { + cluster.subDetID = static_cast(geom->getSubDetID(chipID)); + cluster.layer = static_cast(geom->getLayer(chipID)); + cluster.disk = static_cast(geom->getDisk(chipID)); + } + clusters.emplace_back(cluster); + } + } + } else { + // Normal cluster - encode directly + std::array patt{}; + for (const auto& cell : actsCluster.cells) { + uint32_t ir = static_cast(cell.row - rowMin); + uint32_t ic = static_cast(cell.col - colMin); + int nbit = ir * colSpan + ic; + patt[nbit >> 3] |= (0x1 << (7 - (nbit % 8))); + } + patterns.emplace_back(static_cast(rowSpan)); + patterns.emplace_back(static_cast(colSpan)); + const int nBytes = (rowSpan * colSpan + 7) / 8; + patterns.insert(patterns.end(), patt.begin(), patt.begin() + nBytes); + + // Handle MC labels + if (clusterLabels && digitLabels) { + const auto clsIdx = static_cast(clusters.size()); + for (const auto& cell : actsCluster.cells) { + if (cell.digitIdx < digitLabels->getIndexedSize()) { + const auto& lbls = digitLabels->getLabels(cell.digitIdx); + for (const auto& lbl : lbls) { + clusterLabels->addElement(clsIdx, lbl); + } + } + } + } + + // Create O2 cluster + o2::trk::Cluster cluster; + cluster.chipID = chipID; + cluster.row = rowMin; + cluster.col = colMin; + cluster.size = static_cast(actsCluster.cells.size()); + if (geom) { + cluster.subDetID = static_cast(geom->getSubDetID(chipID)); + cluster.layer = static_cast(geom->getLayer(chipID)); + cluster.disk = static_cast(geom->getDisk(chipID)); + } + clusters.emplace_back(cluster); + } + } + + LOG(debug) << " clusterization of chip " << chipID << " completed!"; + } + clusterROFs.emplace_back(inROF.getBCData(), inROF.getROFrame(), + outFirst, static_cast(clusters.size()) - outFirst); + } + + if (clusterMC2ROFs && !digMC2ROFs.empty()) { + clusterMC2ROFs->reserve(clusterMC2ROFs->size() + digMC2ROFs.size()); + for (const auto& in : digMC2ROFs) { + clusterMC2ROFs->emplace_back(in.eventRecordID, in.rofRecordID, in.minROF, in.maxROF); + } + } +} diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h index 4eda22e350852..1f4c2193b91b1 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h @@ -17,5 +17,9 @@ #pragma link C++ class o2::trk::TimeFrame < 11> + ; #pragma link C++ class o2::trk::Clusterer + ; +#ifdef O2_WITH_ACTS +#pragma link C++ class o2::trk::ClustererACTS + ; + +#endif #endif diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClustererSpec.h b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClustererSpec.h index bacc1057c7b07..9cfab104ecdf9 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClustererSpec.h +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClustererSpec.h @@ -15,6 +15,9 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" #include "TRKReconstruction/Clusterer.h" +#ifdef O2_WITH_ACTS +#include "TRKReconstruction/ClustererACTS.h" +#endif namespace o2::trk { @@ -29,7 +32,13 @@ class ClustererDPL : public o2::framework::Task private: bool mUseMC = true; int mNThreads = 1; +#ifdef O2_WITH_ACTS + bool mUseACTS = false; +#endif o2::trk::Clusterer mClusterer; +#ifdef O2_WITH_ACTS + o2::trk::ClustererACTS mClustererACTS; +#endif }; o2::framework::DataProcessorSpec getClustererSpec(bool useMC); diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/ClustererSpec.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/ClustererSpec.cxx index 8aec63d69206b..5d9ac463b3f54 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/ClustererSpec.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/ClustererSpec.cxx @@ -23,6 +23,9 @@ namespace o2::trk void ClustererDPL::init(o2::framework::InitContext& ic) { mNThreads = std::max(1, ic.options().get("nthreads")); +#ifdef O2_WITH_ACTS + mUseACTS = ic.options().get("useACTS"); +#endif } void ClustererDPL::run(o2::framework::ProcessingContext& pc) @@ -48,15 +51,32 @@ void ClustererDPL::run(o2::framework::ProcessingContext& pc) } o2::base::GeometryManager::loadGeometry("o2sim_geometry.root", false, true); - mClusterer.process(digits, - rofs, - clusters, - patterns, - clusterROFs, - mUseMC ? &labels : nullptr, - clusterLabels.get(), - mc2rofs, - mUseMC ? &clusterMC2ROFs : nullptr); +#ifdef O2_WITH_ACTS + if (mUseACTS) { + LOG(info) << "Running TRKClusterer with ACTS"; + mClustererACTS.process(digits, + rofs, + clusters, + patterns, + clusterROFs, + mUseMC ? &labels : nullptr, + clusterLabels.get(), + mc2rofs, + mUseMC ? &clusterMC2ROFs : nullptr); + } else +#endif + { + LOG(info) << "Running TRKClusterer"; + mClusterer.process(digits, + rofs, + clusters, + patterns, + clusterROFs, + mUseMC ? &labels : nullptr, + clusterLabels.get(), + mc2rofs, + mUseMC ? &clusterMC2ROFs : nullptr); + } pc.outputs().snapshot(o2::framework::Output{"TRK", "COMPCLUSTERS", 0}, clusters); pc.outputs().snapshot(o2::framework::Output{"TRK", "PATTERNS", 0}, patterns); @@ -93,7 +113,12 @@ o2::framework::DataProcessorSpec getClustererSpec(bool useMC) inputs, outputs, o2::framework::AlgorithmSpec{o2::framework::adaptFromTask(useMC)}, - o2::framework::Options{{"nthreads", o2::framework::VariantType::Int, 1, {"Number of clustering threads"}}}}; + o2::framework::Options{{"nthreads", o2::framework::VariantType::Int, 1, {"Number of clustering threads"}} +#ifdef O2_WITH_ACTS + , + {"useACTS", o2::framework::VariantType::Bool, false, {"Use ACTS for clustering"}} +#endif + }}; } } // namespace o2::trk From 8e4cfe1c0a4cb84214defa378de41dfae1c7d411 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Wed, 18 Mar 2026 09:07:51 +0100 Subject: [PATCH 378/701] [ALICE3] Refactor TRK Hit class to rely on ITSMFT (#15173) * [ALICE3] Refactor TRK Hit class to rely on ITSMFT * Refactor Hit class by removing unused code Removed unnecessary includes and simplified the Print method. * Remove Hit class declaration from TimeFrame.h Removed the declaration of the Hit class from TimeFrame.h. --- .../include/TRKReconstruction/TimeFrame.h | 1 - .../simulation/include/TRKSimulation/Hit.h | 136 +----------------- .../ALICE3/TRK/simulation/src/Hit.cxx | 19 --- 3 files changed, 6 insertions(+), 150 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h index f42a1c897efb6..c07767d50b113 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h @@ -33,7 +33,6 @@ namespace o2 { namespace trk { -class Hit; class GeometryTGeo; /// TRK TimeFrame class that extends ITS TimeFrame functionality diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Hit.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Hit.h index a178c30069f14..88afac8682cf4 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Hit.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Hit.h @@ -12,138 +12,14 @@ /// \file Hit.h /// \brief Definition of the TRK Hit class -#ifndef ALICEO2_TRK_POINT_H_ -#define ALICEO2_TRK_POINT_H_ +#ifndef ALICEO2_TRK_HIT_H_ +#define ALICEO2_TRK_HIT_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" +#include "ITSMFTSimulation/Hit.h" -namespace o2 +namespace o2::trk { -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 +using Hit = o2::itsmft::Hit; // For now we rely on the same Hit class as ITSMFT, but we can extend it with TRK-specific information if needed in the future +} // namespace o2::trk #endif diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Hit.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Hit.cxx index fe496bc59692f..1f49b84114b9d 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Hit.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Hit.cxx @@ -13,22 +13,3 @@ /// \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()); -} From ce92d025a59e7253392059ac604d007d99d2a828 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Wed, 18 Mar 2026 09:08:05 +0100 Subject: [PATCH 379/701] [ALICE3] TOF: Update stave tilt angles for iTOF and oTOF layers (#15172) --- .../Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx index 0a83c19125b70..9b097a0243597 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx @@ -96,10 +96,10 @@ void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::str } if (itof) { // iTOF const std::string name = GeometryTGeo::getITOFLayerPattern(); - const int nStaves = itofSegmented ? 24 : 0; // number of staves in segmented case - const double staveWidth = itofSegmented ? 5.42 : 0.0; // cm - const double staveTiltAngle = itofSegmented ? 3.0 : 0.0; // degrees - const int modulesPerStave = itofSegmented ? 10 : 0; // number of modules per stave in segmented case + const int nStaves = itofSegmented ? 24 : 0; // number of staves in segmented case + const double staveWidth = itofSegmented ? 5.42 : 0.0; // cm + const double staveTiltAngle = itofSegmented ? 10.0 : 0.0; // degrees + const int modulesPerStave = itofSegmented ? 10 : 0; // number of modules per stave in segmented case mITOFLayer = ITOFLayer(name, dInnerTof.first, 0.f, dInnerTof.second, 0.f, x2x0, ITOFLayer::kBarrelSegmented, nStaves, staveWidth, staveTiltAngle, modulesPerStave); @@ -108,7 +108,7 @@ void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::str const std::string name = GeometryTGeo::getOTOFLayerPattern(); const int nStaves = otofSegmented ? 62 : 0; // number of staves in segmented case const double staveWidth = otofSegmented ? 9.74 : 0.0; // cm - const double staveTiltAngle = otofSegmented ? 3.0 : 0.0; // degrees + const double staveTiltAngle = otofSegmented ? 5.0 : 0.0; // degrees const int modulesPerStave = otofSegmented ? 54 : 0; // number of modules per stave in segmented case mOTOFLayer = OTOFLayer(name, dOuterTof.first, 0.f, dOuterTof.second, 0.f, x2x0, OTOFLayer::kBarrelSegmented, From 3b7244cf8ff2f0263e06144565452f8fd3ae6f27 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Tue, 17 Mar 2026 23:15:52 +0100 Subject: [PATCH 380/701] Fix warnings --- Framework/Core/include/Framework/Array2D.h | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Framework/Core/include/Framework/Array2D.h b/Framework/Core/include/Framework/Array2D.h index 593a50afd91f6..857e4b3c89f29 100644 --- a/Framework/Core/include/Framework/Array2D.h +++ b/Framework/Core/include/Framework/Array2D.h @@ -166,26 +166,26 @@ class LabeledArray : public LabelMap using element_t = T; LabeledArray() - : values{}, - LabelMap{} + : LabelMap{}, + values{} { } LabeledArray(T const* data, uint32_t rows_, uint32_t cols_, std::vector labels_rows_ = {}, std::vector labels_cols_ = {}) - : values{data, rows_, cols_}, - LabelMap{rows_, cols_, labels_rows_, labels_cols_} + : LabelMap{rows_, cols_, labels_rows_, labels_cols_}, + values{data, rows_, cols_} { } LabeledArray(T const* data, uint32_t size, std::vector labels_ = {}) - : values{data, 1, size}, - LabelMap{size, labels_} + : LabelMap{size, labels_}, + values{data, 1, size} { } LabeledArray(Array2D const& data, std::vector labels_rows_ = {}, std::vector labels_cols_ = {}) - : values{data}, - LabelMap{data.rows, data.cols, labels_rows_, labels_cols_} + : LabelMap{data.rows, data.cols, labels_rows_, labels_cols_}, + values{data} { } From bd173fab42693252ac9851a2a7610a87b957c205 Mon Sep 17 00:00:00 2001 From: shahoian Date: Tue, 17 Mar 2026 17:36:05 +0100 Subject: [PATCH 381/701] Use finer Z bins for mat LUT in 56.5 < R< 76 cm --- Detectors/Base/test/buildMatBudLUT.C | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/Detectors/Base/test/buildMatBudLUT.C b/Detectors/Base/test/buildMatBudLUT.C index 85f8343a2d35d..14f6d078aee90 100644 --- a/Detectors/Base/test/buildMatBudLUT.C +++ b/Detectors/Base/test/buildMatBudLUT.C @@ -29,7 +29,7 @@ o2::base::MatLayerCylSet mbLUT; bool testMBLUT(const std::string& lutFile = "matbud.root"); -bool buildMatBudLUT(int nTst = 30, int maxLr = -1, const std::string& outFile = "matbud.root", const std::string& geomNamePrefix = "o2sim", const std::string& opts = ""); +bool buildMatBudLUT(int nTst = 60, int maxLr = -1, const std::string& outFile = "matbud.root", const std::string& geomName = "o2sim_geometry-aligned.root"); struct LrData { float rMin = 0.f; @@ -306,14 +306,17 @@ void configLayers() zBin = 10.; rphiBin = lrData.back().rMax * TMath::Pi() * 2 / 18; lrData.emplace_back(LrData(lrData.back().rMax, 56.5, zSpanH, zBin, rphiBin)); + + //------------------------------------ + zBin = 1.; 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.; + drStep = 2; + zBin = 1.; do { auto rmean = lrData.back().rMax + drStep / 2; rphiBin = rmean * TMath::Pi() * 2 / (NSect * 2); @@ -321,8 +324,8 @@ void configLayers() } while (lrData.back().rMax < 68.5 - kToler); zSpanH = 250.f; - zBin = 25.; - rphiBin = 5; + zBin = 1.; + rphiBin = 2.5; { auto rmean = (lrData.back().rMax + 76) / 2.; rphiBin = rmean * TMath::Pi() * 2 / (NSect * 2); From 74f713a0c2ea595eb024dbe54076afaf7a8c235c Mon Sep 17 00:00:00 2001 From: Gauthier Legras Date: Tue, 17 Mar 2026 13:40:35 +0100 Subject: [PATCH 382/701] TRD: small fix for gain and VdExB calib --- .../TRD/include/DataFormatsTRD/CalGain.h | 4 ++-- .../TRD/include/DataFormatsTRD/CalVdriftExB.h | 16 ++++++++-------- Detectors/TRD/calibration/src/CalibratorGain.cxx | 4 ++-- .../TRD/calibration/src/CalibratorVdExB.cxx | 8 ++++---- 4 files changed, 16 insertions(+), 16 deletions(-) diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalGain.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalGain.h index b4e64db094a5c..97b3a73a86c03 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalGain.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalGain.h @@ -40,7 +40,7 @@ class CalGain if (!defaultAvg || isGoodGain(iDet)) return mMPVdEdx[iDet]; else { - if (TMath::Abs(mMeanGain + 999.) < 1e-6) + if (std::fabs(mMeanGain + 999.) < 1e-6) mMeanGain = getAverageGain(); return mMeanGain; } @@ -68,7 +68,7 @@ class CalGain bool isGoodGain(int iDet) const { - if (TMath::Abs(mMPVdEdx[iDet] - constants::MPVDEDXDEFAULT) > 1e-6) + if (std::fabs(mMPVdEdx[iDet] - constants::MPVDEDXDEFAULT) > 1e-6) return true; else return false; diff --git a/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalVdriftExB.h b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalVdriftExB.h index 65981e928fb39..280f9d0d4b8a9 100644 --- a/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalVdriftExB.h +++ b/DataFormats/Detectors/TRD/include/DataFormatsTRD/CalVdriftExB.h @@ -41,7 +41,7 @@ class CalVdriftExB if (!defaultAvg || (isGoodExB(iDet) && isGoodVdrift(iDet))) return mVdrift[iDet]; else { - if (TMath::Abs(mMeanVdrift + 999.) < 1e-6) + if (std::fabs(mMeanVdrift + 999.) < 1e-6) mMeanVdrift = getAverageVdrift(); return mMeanVdrift; } @@ -51,7 +51,7 @@ class CalVdriftExB if (!defaultAvg || (isGoodExB(iDet) && isGoodVdrift(iDet))) return mExB[iDet]; else { - if (TMath::Abs(mMeanExB + 999.) < 1e-6) + if (std::fabs(mMeanExB + 999.) < 1e-6) mMeanExB = getAverageExB(); return mMeanExB; } @@ -102,9 +102,9 @@ class CalVdriftExB // check if value is well calibrated or not // default calibration if not enough entries // close to boundaries indicate a failed fit - if (TMath::Abs(mExB[iDet] - constants::EXBDEFAULT) > 1e-6 && - TMath::Abs(mExB[iDet] - constants::EXBMIN) > 0.01 && - TMath::Abs(mExB[iDet] - constants::EXBMAX) > 0.01) + if (std::fabs(mExB[iDet] - constants::EXBDEFAULT) > 1e-6 && + std::fabs(mExB[iDet] - constants::EXBMIN) > 0.01 && + std::fabs(mExB[iDet] - constants::EXBMAX) > 0.01) return true; else return false; @@ -115,9 +115,9 @@ class CalVdriftExB // check if value is well calibrated or not // default calibration if not enough entries // close to boundaries indicate a failed fit - if (TMath::Abs(mVdrift[iDet] - constants::VDRIFTDEFAULT) > 1e-6 && - TMath::Abs(mVdrift[iDet] - constants::VDRIFTMIN) > 0.1 && - TMath::Abs(mVdrift[iDet] - constants::VDRIFTMAX) > 0.1) + if (std::fabs(mVdrift[iDet] - constants::VDRIFTDEFAULT) > 1e-6 && + std::fabs(mVdrift[iDet] - constants::VDRIFTMIN) > 0.1 && + std::fabs(mVdrift[iDet] - constants::VDRIFTMAX) > 0.1) return true; else return false; diff --git a/Detectors/TRD/calibration/src/CalibratorGain.cxx b/Detectors/TRD/calibration/src/CalibratorGain.cxx index 77efeaeb36f1e..c276563cac5fb 100644 --- a/Detectors/TRD/calibration/src/CalibratorGain.cxx +++ b/Detectors/TRD/calibration/src/CalibratorGain.cxx @@ -89,7 +89,7 @@ void CalibratorGain::retrievePrev(o2::framework::ProcessingContext& pc) std::string msg = "Default Object"; // We check if the object we got is the default one by comparing it to the defaults. for (int iDet = 0; iDet < MAXCHAMBER; ++iDet) { - if (dataCalGain->getMPVdEdx(iDet) != constants::MPVDEDXDEFAULT) { + if (dataCalGain->getMPVdEdx(iDet, false) != constants::MPVDEDXDEFAULT) { msg = "Previous Object"; break; } @@ -98,7 +98,7 @@ void CalibratorGain::retrievePrev(o2::framework::ProcessingContext& pc) // Here we set each entry regardless if it is the default or not. for (int iDet = 0; iDet < MAXCHAMBER; ++iDet) { - mFitResults[iDet] = dataCalGain->getMPVdEdx(iDet); + mFitResults[iDet] = dataCalGain->getMPVdEdx(iDet, false); } } diff --git a/Detectors/TRD/calibration/src/CalibratorVdExB.cxx b/Detectors/TRD/calibration/src/CalibratorVdExB.cxx index fef7bdecef38c..961a74eefbe0f 100644 --- a/Detectors/TRD/calibration/src/CalibratorVdExB.cxx +++ b/Detectors/TRD/calibration/src/CalibratorVdExB.cxx @@ -166,8 +166,8 @@ void CalibratorVdExB::retrievePrev(o2::framework::ProcessingContext& pc) std::string msg = "Default Object"; // We check if the object we got is the default one by comparing it to the defaults. for (int iDet = 0; iDet < MAXCHAMBER; ++iDet) { - if (dataCalVdriftExB->getVdrift(iDet) != VDRIFTDEFAULT || - dataCalVdriftExB->getExB(iDet) != EXBDEFAULT) { + if (dataCalVdriftExB->getVdrift(iDet, false) != VDRIFTDEFAULT || + dataCalVdriftExB->getExB(iDet, false) != EXBDEFAULT) { msg = "Previous Object"; break; } @@ -176,8 +176,8 @@ void CalibratorVdExB::retrievePrev(o2::framework::ProcessingContext& pc) // Here we set each entry regardless if it is the default or not. for (int iDet = 0; iDet < MAXCHAMBER; ++iDet) { - mFitFunctor.laPreCorr[iDet] = dataCalVdriftExB->getExB(iDet); - mFitFunctor.vdPreCorr[iDet] = dataCalVdriftExB->getVdrift(iDet); + mFitFunctor.laPreCorr[iDet] = dataCalVdriftExB->getExB(iDet, false); + mFitFunctor.vdPreCorr[iDet] = dataCalVdriftExB->getVdrift(iDet, false); } } From 5b0ada5f3169f594fba3bd3a66d1714c836f067f Mon Sep 17 00:00:00 2001 From: wiechula Date: Wed, 25 Feb 2026 11:45:59 +0100 Subject: [PATCH 383/701] Add treatment of channel saturation to all scenarios --- .../include/TPCSimulation/SAMPAProcessing.h | 34 +++++++------------ 1 file changed, 12 insertions(+), 22 deletions(-) diff --git a/Detectors/TPC/simulation/include/TPCSimulation/SAMPAProcessing.h b/Detectors/TPC/simulation/include/TPCSimulation/SAMPAProcessing.h index be40c8652ad61..3f21b6723be14 100644 --- a/Detectors/TPC/simulation/include/TPCSimulation/SAMPAProcessing.h +++ b/Detectors/TPC/simulation/include/TPCSimulation/SAMPAProcessing.h @@ -158,23 +158,19 @@ template inline float SAMPAProcessing::makeSignal(float ADCcounts, const int sector, const int globalPadInSector, const float commonMode, float& pedestal, float& noise, float tot) { - float signal = ADCcounts; pedestal = getPedestal(sector, globalPadInSector); - float pedestalCRU = getPedestalCRU(sector, globalPadInSector); noise = getNoise(sector, globalPadInSector); + + const float signal = ADCcounts; + const float pedestalCRU = getPedestalCRU(sector, globalPadInSector); + const float fullSignal = signal - commonMode + noise + pedestal + (tot > 0 ? 80 : 0); // TODO: improve to also add tail + switch (MODE) { case DigitzationMode::FullMode: { - signal -= commonMode; - signal += noise; - signal += pedestal; - return getADCSaturation(signal); + return getADCSaturation(fullSignal); break; } case DigitzationMode::ZeroSuppression: { - signal -= commonMode; - signal += noise; - signal += pedestal; - signal += (tot > 0) ? 80 : 0; // TODO: improve to also add tail const float signalSubtractPedestal = getADCSaturation(signal) - pedestalCRU; const float zeroSuppression = getZeroSuppression(sector, globalPadInSector); if (signalSubtractPedestal < zeroSuppression) { @@ -184,10 +180,10 @@ inline float SAMPAProcessing::makeSignal(float ADCcounts, const int sector, cons break; } case DigitzationMode::ZeroSuppressionCMCorr: { - signal += noise; - signal += pedestal; - signal += (tot > 0) ? 80 : 0; // TODO: improve to also add tail - const float signalSubtractPedestal = getADCSaturation(signal) - pedestalCRU; + // TODO: this is not really a common mode correction, since the common mode is simply not treated. + // Instead, the full common mode algorithm should be implemented and used + const float signalNoCM = signal + noise + pedestal + (tot > 0 ? 80 : 0); // TODO: improve to also add tail + const float signalSubtractPedestal = getADCSaturation(signalNoCM) - pedestalCRU; const float zeroSuppression = getZeroSuppression(sector, globalPadInSector); if (signalSubtractPedestal < zeroSuppression) { return 0.f; @@ -196,18 +192,12 @@ inline float SAMPAProcessing::makeSignal(float ADCcounts, const int sector, cons break; } case DigitzationMode::SubtractPedestal: { - signal -= commonMode; - signal += noise; - signal += pedestal; - const float signalSubtractPedestal = getADCSaturation(signal) - pedestalCRU; + const float signalSubtractPedestal = getADCSaturation(fullSignal) - pedestalCRU; return signalSubtractPedestal; break; } case DigitzationMode::NoSaturation: { - signal -= commonMode; - signal += noise; - signal += pedestal; - return signal; + return fullSignal; break; } case DigitzationMode::PropagateADC: { From 8572fc96fcf1ec1dabfc628ad741844f189025b4 Mon Sep 17 00:00:00 2001 From: Ernst Hellbar Date: Tue, 17 Feb 2026 16:25:42 +0100 Subject: [PATCH 384/701] MID: remove input wildcards in MID workflows --- .../MIDWorkflow/ColumnDataSpecsUtils.h | 35 +++++++-------- .../Workflow/src/CalibDataProcessorSpec.cxx | 22 +++------- .../MUON/MID/Workflow/src/ClusterizerSpec.cxx | 2 +- .../MID/Workflow/src/ColumnDataSpecsUtils.cxx | 44 ++++++++++++++----- .../src/DecodedDataAggregatorSpec.cxx | 4 +- .../MID/Workflow/src/EntropyEncoderSpec.cxx | 31 +++++-------- .../MUON/MID/Workflow/src/FilteringBCSpec.cxx | 2 +- .../MUON/MID/Workflow/src/FilteringSpec.cxx | 2 +- .../MUON/MID/Workflow/src/MaskMakerSpec.cxx | 32 ++++---------- .../MUON/MID/Workflow/src/TimingSpec.cxx | 2 +- .../MID/Workflow/src/ZeroSuppressionSpec.cxx | 2 +- 11 files changed, 82 insertions(+), 96 deletions(-) diff --git a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/ColumnDataSpecsUtils.h b/Detectors/MUON/MID/Workflow/include/MIDWorkflow/ColumnDataSpecsUtils.h index d8ec401e6a473..5f0fc43e1afca 100644 --- a/Detectors/MUON/MID/Workflow/include/MIDWorkflow/ColumnDataSpecsUtils.h +++ b/Detectors/MUON/MID/Workflow/include/MIDWorkflow/ColumnDataSpecsUtils.h @@ -42,21 +42,28 @@ namespace mid namespace specs { -/// Returns the input specs for MID Column Data and corresponding ROFs and labels +/// Returns the input specs for MID Column Data and corresponding ROFs and labels for EventType Standard /// \param dataBind Data binding name /// \param dataDesc Input data description /// \param useMC Builds output specs for labels /// \return Vector of input specs -std::vector buildInputSpecs(std::string_view dataBind, std::string_view dataDesc, bool useMC); +std::vector buildStandardInputSpecs(std::string_view dataBind, std::string_view dataDesc, bool useMC); -/// Returns the input specs for MID Column Data and corresponding ROFs and labels +/// Returns the input specs for MID Column Data and corresponding ROFs and labels for EventType Standard /// \param dataBind Data binding name /// \param dataDesc Input data description /// \param rofDesc Input ROF record description /// \param labelsDesc Input MC labels description /// \param useMC Builds output specs for labels /// \return Vector of input specs -std::vector buildInputSpecs(std::string_view dataBind, std::string_view dataDesc, std::string_view rofDesc, std::string_view labelsDesc, bool useMC); +std::vector buildStandardInputSpecs(std::string_view dataBind, std::string_view dataDesc, std::string_view rofDesc, std::string_view labelsDesc, bool useMC); + +/// Returns the input specs for MID Column Data and corresponding ROFs and labels for all three EventTypes +/// \param dataBind Data binding name +/// \param dataDesc Input data description +/// \param rofDesc Input ROF record description +/// \return Vector of input specs +std::vector buildInputSpecs(std::string_view dataBind, std::string_view dataDesc, std::string_view rofDesc); /// Returns the output specs for the different event types /// \param bind Binding name @@ -71,22 +78,14 @@ std::vector buildOutputSpecs(std::string_view bind, std:: /// \return Vector of Output specs std::vector buildStandardOutputSpecs(std::string_view dataBind, std::string_view dataDesc, bool useMC); -/// Returns the inputs for the different event types +/// Returns the input matching a specific binding /// \param pc Processing context /// \param bind Binding name /// \return Array of spans template -std::array, NEvTypes> getInput(framework::ProcessingContext& pc, std::string_view bind) +gsl::span getInput(framework::ProcessingContext& pc, std::string_view bind, int subSpec = -1) { - std::array, 3> data; - for (auto const& inputRef : framework::InputRecordWalker(pc.inputs())) { - auto const* dh = framework::DataRefUtils::getHeader(inputRef); - auto subSpecIdx = static_cast(dh->subSpecification); - if (framework::DataRefUtils::match(inputRef, bind.data())) { - data[subSpecIdx] = pc.inputs().get>(inputRef); - } - } - return data; + return pc.inputs().get>(fmt::format("{}{}", bind.data(), subSpec >= 0 ? fmt::format("_{}", subSpec) : "")); } /// Gets the outputs @@ -94,7 +93,7 @@ std::array, NEvTypes> getInput(framework::ProcessingContext& /// \return vector of outputs std::vector buildOutputs(std::vector outputSpecs); -/// Returns the array of Column Data +/// Returns the array of Column Data for all three EventTypes /// \param pc Processing context /// \param dataBind Data binding name /// \return Array of Column Data spans @@ -107,7 +106,7 @@ std::array, NEvTypes> getData(framework::ProcessingC /// \return Span of ColumnData gsl::span getData(framework::ProcessingContext& pc, std::string_view dataBind, EventType eventType); -/// Returns the array of ROF records +/// Returns the array of ROF records for all three EventTypes /// \param pc Processing context /// \param dataBind Data binding name /// \return Array of ROF Records spans @@ -124,7 +123,7 @@ gsl::span getRofs(framework::ProcessingContext& pc, std::string /// \param pc Processing context /// \param dataBind Data binding name /// \return Pointer to MC labels -std::unique_ptr> getLabels(framework::ProcessingContext& pc, std::string_view dataBind); +std::unique_ptr> getLabels(framework::ProcessingContext& pc, std::string_view dataBind, EventType eventType = EventType::Standard); } // namespace specs } // namespace mid diff --git a/Detectors/MUON/MID/Workflow/src/CalibDataProcessorSpec.cxx b/Detectors/MUON/MID/Workflow/src/CalibDataProcessorSpec.cxx index 1a6f2de9cd886..036178c04e867 100644 --- a/Detectors/MUON/MID/Workflow/src/CalibDataProcessorSpec.cxx +++ b/Detectors/MUON/MID/Workflow/src/CalibDataProcessorSpec.cxx @@ -61,19 +61,9 @@ class CalibDataProcessorDPL std::array, 3> data; std::array, 3> dataRof; - std::vector filter = { - {"check_data", of::ConcreteDataTypeMatcher{header::gDataOriginMID, "DATA"}, of::Lifetime::Timeframe}, - {"check_rof", of::ConcreteDataTypeMatcher{header::gDataOriginMID, "DATAROF"}, of::Lifetime::Timeframe}, - }; - - for (auto const& inputRef : of::InputRecordWalker(pc.inputs(), filter)) { - auto const* dh = framework::DataRefUtils::getHeader(inputRef); - auto subSpecIdx = static_cast(dh->subSpecification); - if (of::DataRefUtils::match(inputRef, "mid_data")) { - data[subSpecIdx] = pc.inputs().get>(inputRef); - } else if (of::DataRefUtils::match(inputRef, "mid_data_rof")) { - dataRof[subSpecIdx] = pc.inputs().get>(inputRef); - } + for (o2::header::DataHeader::SubSpecificationType subSpec = 0; subSpec < NEvTypes; ++subSpec) { + data[subSpec] = pc.inputs().get>(fmt::format("mid_data_{}", subSpec)); + dataRof[subSpec] = pc.inputs().get>(fmt::format("mid_data_rof_{}", subSpec)); } mNoise.clear(); @@ -151,8 +141,10 @@ class CalibDataProcessorDPL of::DataProcessorSpec getCalibDataProcessorSpec(const FEEIdConfig& feeIdConfig, const CrateMasks& crateMasks) { std::vector inputSpecs; - inputSpecs.emplace_back("mid_data", of::ConcreteDataTypeMatcher(header::gDataOriginMID, "DATA"), of::Lifetime::Timeframe); - inputSpecs.emplace_back("mid_data_rof", of::ConcreteDataTypeMatcher(header::gDataOriginMID, "DATAROF"), of::Lifetime::Timeframe); + for (o2::header::DataHeader::SubSpecificationType subSpec = 0; subSpec < NEvTypes; ++subSpec) { + inputSpecs.emplace_back(fmt::format("mid_data_{}", subSpec), header::gDataOriginMID, "DATA", subSpec, of::Lifetime::Timeframe); + inputSpecs.emplace_back(fmt::format("mid_data_rof_{}", subSpec), header::gDataOriginMID, "DATAROF", subSpec, of::Lifetime::Timeframe); + } std::vector outputSpecs; outputSpecs.emplace_back(header::gDataOriginMID, "NOISE", 0); diff --git a/Detectors/MUON/MID/Workflow/src/ClusterizerSpec.cxx b/Detectors/MUON/MID/Workflow/src/ClusterizerSpec.cxx index c544ce19fcdea..bf0d9608a2119 100644 --- a/Detectors/MUON/MID/Workflow/src/ClusterizerSpec.cxx +++ b/Detectors/MUON/MID/Workflow/src/ClusterizerSpec.cxx @@ -132,7 +132,7 @@ framework::DataProcessorSpec getClusterizerSpec(bool isMC, std::string_view inDa if (isMC) { outputSpecs.emplace_back(of::OutputSpec{header::gDataOriginMID, "CLUSTERSLABELS"}); } - auto inputSpecs = specs::buildInputSpecs("mid_cluster_in", inDataDesc, inRofDesc, inLabelsDesc, isMC); + auto inputSpecs = specs::buildStandardInputSpecs("mid_cluster_in", inDataDesc, inRofDesc, inLabelsDesc, isMC); return of::DataProcessorSpec{ "MIDClusterizer", diff --git a/Detectors/MUON/MID/Workflow/src/ColumnDataSpecsUtils.cxx b/Detectors/MUON/MID/Workflow/src/ColumnDataSpecsUtils.cxx index e0d41cd8d91d2..b4884ad68ad15 100644 --- a/Detectors/MUON/MID/Workflow/src/ColumnDataSpecsUtils.cxx +++ b/Detectors/MUON/MID/Workflow/src/ColumnDataSpecsUtils.cxx @@ -83,14 +83,26 @@ std::string buildSelectors(std::string_view dataBind, std::string_view dataDesc, return selector; } -std::vector buildInputSpecs(std::string_view dataBind, std::string_view dataDesc, bool useMC) +std::vector buildInputSpecs(std::string_view dataBind, std::string_view dataDesc, std::string_view rofDesc) { - return buildInputSpecs(dataBind, dataDesc, getROFDescription(dataDesc), getLabelsDescription(dataDesc), useMC); + std::string selector; + for (size_t ievt = 0; ievt < NEvTypes; ++ievt) { + if (!selector.empty()) { + selector += ";"; + } + selector += buildSelectors(dataBind, dataDesc, rofDesc, "", false, ievt); + } + return framework::select(selector.c_str()); +} + +std::vector buildStandardInputSpecs(std::string_view dataBind, std::string_view dataDesc, bool useMC) +{ + return buildStandardInputSpecs(dataBind, dataDesc, getROFDescription(dataDesc), getLabelsDescription(dataDesc), useMC); } -std::vector buildInputSpecs(std::string_view dataBind, std::string_view dataDesc, std::string_view rofDesc, std::string_view labelsDesc, bool useMC) +std::vector buildStandardInputSpecs(std::string_view dataBind, std::string_view dataDesc, std::string_view rofDesc, std::string_view labelsDesc, bool useMC) { - std::string selector = buildSelectors(dataBind, dataDesc, rofDesc, labelsDesc, useMC); + std::string selector = buildSelectors(dataBind, dataDesc, rofDesc, labelsDesc, useMC, 0); return framework::select(selector.c_str()); } @@ -134,29 +146,37 @@ std::vector buildOutputs(std::vector o std::array, NEvTypes> getData(framework::ProcessingContext& pc, std::string_view dataBind) { - return getInput(pc, dataBind); + std::array, 3> data; + for (size_t ievt = 0; ievt < NEvTypes; ++ievt) { + data[ievt] = getInput(pc, dataBind, ievt); + } + + return data; } gsl::span getData(framework::ProcessingContext& pc, std::string_view dataBind, EventType eventType) { - auto idx = static_cast(eventType); - return getData(pc, dataBind)[idx]; + return getInput(pc, dataBind.data(), static_cast(eventType)); } std::array, NEvTypes> getRofs(framework::ProcessingContext& pc, std::string_view dataBind) { - return getInput(pc, getROFBind(dataBind)); + std::array, 3> data; + for (size_t ievt = 0; ievt < NEvTypes; ++ievt) { + data[ievt] = getInput(pc, getROFBind(dataBind).data(), ievt); + } + + return data; } gsl::span getRofs(framework::ProcessingContext& pc, std::string_view dataBind, EventType eventType) { - auto idx = static_cast(eventType); - return getRofs(pc, dataBind)[idx]; + return getInput(pc, getROFBind(dataBind).data(), static_cast(eventType)); } -std::unique_ptr> getLabels(framework::ProcessingContext& pc, std::string_view dataBind) +std::unique_ptr> getLabels(framework::ProcessingContext& pc, std::string_view dataBind, EventType eventType) { - return pc.inputs().get*>(getLabelsBind(dataBind).data()); + return pc.inputs().get*>(fmt::format("{}_{}", getLabelsBind(dataBind).data(), static_cast(eventType))); } } // namespace specs diff --git a/Detectors/MUON/MID/Workflow/src/DecodedDataAggregatorSpec.cxx b/Detectors/MUON/MID/Workflow/src/DecodedDataAggregatorSpec.cxx index 192b4c52be9cc..54b1a458fec0f 100644 --- a/Detectors/MUON/MID/Workflow/src/DecodedDataAggregatorSpec.cxx +++ b/Detectors/MUON/MID/Workflow/src/DecodedDataAggregatorSpec.cxx @@ -58,7 +58,7 @@ class DecodedDataAggregatorDeviceDPL mAggregator.process(data, inROFRecords); mTimerAlgo += std::chrono::high_resolution_clock::now() - tAlgoStart; - for (o2::header::DataHeader::SubSpecificationType subSpec = 0; subSpec < 3; ++subSpec) { + for (o2::header::DataHeader::SubSpecificationType subSpec = 0; subSpec < NEvTypes; ++subSpec) { EventType evtType = static_cast(subSpec); pc.outputs().snapshot(of::Output{o2::header::gDataOriginMID, "DATA", subSpec}, mAggregator.getData(evtType)); pc.outputs().snapshot(of::Output{o2::header::gDataOriginMID, "DATAROF", subSpec}, mAggregator.getROFRecords(evtType)); @@ -79,7 +79,7 @@ framework::DataProcessorSpec getDecodedDataAggregatorSpec() { std::vector inputSpecs{of::InputSpec{"mid_decoded", header::gDataOriginMID, "DECODED"}, of::InputSpec{"mid_decoded_rof", header::gDataOriginMID, "DECODEDROF"}}; std::vector outputSpecs; - for (o2::header::DataHeader::SubSpecificationType subSpec = 0; subSpec < 3; ++subSpec) { + for (o2::header::DataHeader::SubSpecificationType subSpec = 0; subSpec < NEvTypes; ++subSpec) { outputSpecs.emplace_back(of::OutputSpec{header::gDataOriginMID, "DATA", subSpec}); outputSpecs.emplace_back(of::OutputSpec{header::gDataOriginMID, "DATAROF", subSpec}); } diff --git a/Detectors/MUON/MID/Workflow/src/EntropyEncoderSpec.cxx b/Detectors/MUON/MID/Workflow/src/EntropyEncoderSpec.cxx index d75fe3fa6fbf2..f8d9922db25fa 100644 --- a/Detectors/MUON/MID/Workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/MUON/MID/Workflow/src/EntropyEncoderSpec.cxx @@ -56,26 +56,15 @@ void EntropyEncoderSpec::run(ProcessingContext& pc) mTimer.Start(false); mCTFCoder.updateTimeDependentParams(pc, true); CTFHelper::TFData tfData; - std::vector - filter = { - {"check", ConcreteDataTypeMatcher{header::gDataOriginMID, "DATA"}, Lifetime::Timeframe}, - {"check", ConcreteDataTypeMatcher{header::gDataOriginMID, "DATAROF"}, Lifetime::Timeframe}, - }; size_t insize = 0; - for (auto const& inputRef : InputRecordWalker(pc.inputs(), filter)) { - auto const* dh = framework::DataRefUtils::getHeader(inputRef); - if (dh->subSpecification >= NEvTypes) { - throw std::runtime_error(fmt::format("SubSpecification={} does not match EvenTypes for {}", dh->subSpecification, dh->dataDescription.as())); - } - if (DataRefUtils::match(inputRef, "cols")) { - tfData.colData[dh->subSpecification] = pc.inputs().get>(inputRef); - insize += tfData.colData[dh->subSpecification].size() * sizeof(o2::mid::ColumnData); - } - if (DataRefUtils::match(inputRef, "rofs")) { - tfData.rofData[dh->subSpecification] = pc.inputs().get>(inputRef); - insize += tfData.rofData[dh->subSpecification].size() * sizeof(o2::mid::ROFRecord); - } + for (o2::header::DataHeader::SubSpecificationType subSpec = 0; subSpec < NEvTypes; ++subSpec) { + tfData.colData[subSpec] = pc.inputs().get>(fmt::format("cols_{}", subSpec)); + insize += tfData.colData[subSpec].size() * sizeof(o2::mid::ColumnData); + + tfData.rofData[subSpec] = pc.inputs().get>(fmt::format("rofs_{}", subSpec)); + insize += tfData.rofData[subSpec].size() * sizeof(o2::mid::ROFRecord); } + if (mSelIR) { mCTFCoder.setSelectedIRFrames(pc.inputs().get>("selIRFrames")); } @@ -102,8 +91,10 @@ void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) 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); + for (o2::header::DataHeader::SubSpecificationType subSpec = 0; subSpec < NEvTypes; ++subSpec) { + inputs.emplace_back(fmt::format("cols_{}", subSpec), header::gDataOriginMID, "DATA", subSpec, Lifetime::Timeframe); + inputs.emplace_back(fmt::format("rofs_{}", subSpec), header::gDataOriginMID, "DATAROF", subSpec, Lifetime::Timeframe); + } if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { inputs.emplace_back("ctfdict", header::gDataOriginMID, "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("MID/Calib/CTFDictionaryTree")); diff --git a/Detectors/MUON/MID/Workflow/src/FilteringBCSpec.cxx b/Detectors/MUON/MID/Workflow/src/FilteringBCSpec.cxx index 2aadabeab0bed..2d697f4bc5b1d 100644 --- a/Detectors/MUON/MID/Workflow/src/FilteringBCSpec.cxx +++ b/Detectors/MUON/MID/Workflow/src/FilteringBCSpec.cxx @@ -100,7 +100,7 @@ class FilteringBCDeviceDPL of::DataProcessorSpec getFilteringBCSpec(bool useMC, std::string_view inDesc) { - auto inputSpecs = specs::buildInputSpecs("mid_filter_BC_in", inDesc, useMC); + auto inputSpecs = specs::buildStandardInputSpecs("mid_filter_BC_in", inDesc, useMC); auto ggRequest = std::make_shared(false, // orbitResetTime false, // GRPECS=true true, // GRPLHCIF diff --git a/Detectors/MUON/MID/Workflow/src/FilteringSpec.cxx b/Detectors/MUON/MID/Workflow/src/FilteringSpec.cxx index 0ccbbe237b9a5..6ef3424e1ec29 100644 --- a/Detectors/MUON/MID/Workflow/src/FilteringSpec.cxx +++ b/Detectors/MUON/MID/Workflow/src/FilteringSpec.cxx @@ -143,7 +143,7 @@ class FilteringDeviceDPL of::DataProcessorSpec getFilteringSpec(bool useMC, std::string_view inDesc, std::string_view outDesc) { - auto inputSpecs = specs::buildInputSpecs("mid_filter_in", inDesc, useMC); + auto inputSpecs = specs::buildStandardInputSpecs("mid_filter_in", inDesc, useMC); inputSpecs.emplace_back("mid_bad_channels", header::gDataOriginMID, "BAD_CHANNELS", 0, of::Lifetime::Condition, of::ccdbParamSpec("MID/Calib/BadChannels")); inputSpecs.emplace_back("mid_rejectlist", header::gDataOriginMID, "REJECTLIST", 0, of::Lifetime::Condition, of::ccdbParamSpec("MID/Calib/RejectList")); diff --git a/Detectors/MUON/MID/Workflow/src/MaskMakerSpec.cxx b/Detectors/MUON/MID/Workflow/src/MaskMakerSpec.cxx index 28d2ff953ea23..d4b63de7e5d3f 100644 --- a/Detectors/MUON/MID/Workflow/src/MaskMakerSpec.cxx +++ b/Detectors/MUON/MID/Workflow/src/MaskMakerSpec.cxx @@ -69,28 +69,10 @@ class MaskMakerDeviceDPL gsl::span calibData, fetData; gsl::span calibDataRof, fetDataRof; - std::vector filter = { - {"check_data", of::ConcreteDataTypeMatcher{header::gDataOriginMID, "DATA"}, of::Lifetime::Timeframe}, - {"check_rof", of::ConcreteDataTypeMatcher{header::gDataOriginMID, "DATAROF"}, of::Lifetime::Timeframe}, - }; - - for (auto const& inputRef : of::InputRecordWalker(pc.inputs(), filter)) { - auto const* dh = framework::DataRefUtils::getHeader(inputRef); - if (of::DataRefUtils::match(inputRef, "mid_data")) { - if (dh->subSpecification == 1) { - calibData = pc.inputs().get>(inputRef); - } else if (dh->subSpecification == 2) { - fetData = pc.inputs().get>(inputRef); - } - } - if (of::DataRefUtils::match(inputRef, "mid_data_rof")) { - if (dh->subSpecification == 1) { - calibDataRof = pc.inputs().get>(inputRef); - } else if (dh->subSpecification == 2) { - fetDataRof = pc.inputs().get>(inputRef); - } - } - } + calibData = pc.inputs().get>("mid_data_1"); + calibDataRof = pc.inputs().get>("mid_data_rof_1"); + fetData = pc.inputs().get>("mid_data_2"); + fetDataRof = pc.inputs().get>("mid_data_rof_2"); unsigned long nEvents = calibDataRof.size(); if (nEvents == 0) { @@ -145,8 +127,10 @@ class MaskMakerDeviceDPL framework::DataProcessorSpec getMaskMakerSpec(const FEEIdConfig& feeIdConfig, const CrateMasks& crateMasks) { std::vector inputSpecs; - inputSpecs.emplace_back("mid_data", of::ConcreteDataTypeMatcher(header::gDataOriginMID, "DATA"), of::Lifetime::Timeframe); - inputSpecs.emplace_back("mid_data_rof", of::ConcreteDataTypeMatcher(header::gDataOriginMID, "DATAROF"), of::Lifetime::Timeframe); + for (o2::header::DataHeader::SubSpecificationType subSpec = 1; subSpec < NEvTypes; ++subSpec) { + inputSpecs.emplace_back(fmt::format("mid_data_{}", subSpec), o2::header::gDataOriginMID, "DATA", subSpec, of::Lifetime::Timeframe); + inputSpecs.emplace_back(fmt::format("mid_data_rof_{}", subSpec), o2::header::gDataOriginMID, "DATAROF", subSpec, of::Lifetime::Timeframe); + } std::vector outputSpecs{ of::OutputSpec{header::gDataOriginMID, "MASKS", 1}, diff --git a/Detectors/MUON/MID/Workflow/src/TimingSpec.cxx b/Detectors/MUON/MID/Workflow/src/TimingSpec.cxx index 05f669ab76ba4..5a397de9a045f 100644 --- a/Detectors/MUON/MID/Workflow/src/TimingSpec.cxx +++ b/Detectors/MUON/MID/Workflow/src/TimingSpec.cxx @@ -71,7 +71,7 @@ class TimingDeviceDPL of::DataProcessorSpec getTimingSpec(int localToBC, std::string_view inRofDesc) { - auto inputSpecs = specs::buildInputSpecs("mid_timing_in", "", inRofDesc, "", false); + auto inputSpecs = specs::buildInputSpecs("mid_timing_in", "", inRofDesc); auto outputSpecs = specs::buildOutputSpecs("mid_timing_out", "TDATAROF"); return of::DataProcessorSpec{ diff --git a/Detectors/MUON/MID/Workflow/src/ZeroSuppressionSpec.cxx b/Detectors/MUON/MID/Workflow/src/ZeroSuppressionSpec.cxx index 5d89eee81c629..7298ad9e506e3 100644 --- a/Detectors/MUON/MID/Workflow/src/ZeroSuppressionSpec.cxx +++ b/Detectors/MUON/MID/Workflow/src/ZeroSuppressionSpec.cxx @@ -103,7 +103,7 @@ class ZeroSuppressionDeviceDPL framework::DataProcessorSpec getZeroSuppressionSpec(bool useMC, std::string_view dataDesc) { - auto inputSpecs = specs::buildInputSpecs("mid_zs_in", dataDesc, useMC); + auto inputSpecs = specs::buildStandardInputSpecs("mid_zs_in", dataDesc, useMC); auto outputSpecs = specs::buildStandardOutputSpecs("mid_zs_out", "DATA", useMC); return of::DataProcessorSpec{ From 4bfc0a53f34c142e9dbc0ca0135533603d59c8de Mon Sep 17 00:00:00 2001 From: Sergio Garcia Date: Wed, 18 Mar 2026 16:29:47 +0100 Subject: [PATCH 385/701] Update clean PR action --- .github/workflows/clean-test.yml | 8 ++------ 1 file changed, 2 insertions(+), 6 deletions(-) diff --git a/.github/workflows/clean-test.yml b/.github/workflows/clean-test.yml index 0f15301d4eed9..b149ae86b991d 100644 --- a/.github/workflows/clean-test.yml +++ b/.github/workflows/clean-test.yml @@ -19,10 +19,6 @@ name: Clean PR checks # Warning: the check_* keys are magic and must consist of the string # "check_" followed by the applicable check name exactly. The # "description" field is only the human-readable label for the input. - 'check_build/AliceO2/O2/o2/macOS': - description: build/AliceO2/O2/o2/macOS - type: boolean - default: true 'check_build/AliceO2/O2/o2/macOS-arm': description: build/AliceO2/O2/o2/macOS-arm type: boolean @@ -31,8 +27,8 @@ name: Clean PR checks description: build/O2/fullCI type: boolean default: true - 'check_build/O2/o2-dataflow-cs8': - description: build/O2/o2-dataflow-cs8 + 'check_build/O2/o2-dataflow-slc9': + description: build/O2/o2-dataflow-slc9 type: boolean default: true 'check_build/O2/o2/aarch64': From 866b9a243f0ee55c63a41be4f95aa896afa3cf87 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 18 Mar 2026 23:01:10 +0100 Subject: [PATCH 386/701] DPL: allow pipelining of the CCDB fetcher (#15192) --- Framework/Core/src/WorkflowCustomizationHelpers.cxx | 1 + Framework/Core/src/WorkflowHelpers.cxx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/Framework/Core/src/WorkflowCustomizationHelpers.cxx b/Framework/Core/src/WorkflowCustomizationHelpers.cxx index 05abb5dab98cd..2154d0fe26f8d 100644 --- a/Framework/Core/src/WorkflowCustomizationHelpers.cxx +++ b/Framework/Core/src/WorkflowCustomizationHelpers.cxx @@ -42,6 +42,7 @@ namespace o2::framework std::vector WorkflowCustomizationHelpers::requiredWorkflowOptions() { return {{{"readers", VariantType::Int64, 1ll, {"number of parallel readers to use"}}, + {"ccdb-fetchers", VariantType::Int64, 1ll, {"number of parallel ccdb-fetchers to use"}}, {"spawners", VariantType::Int64, 1ll, {"number of parallel spawners to use"}}, {"pipeline", VariantType::String, "", {"override default pipeline size"}}, {"clone", VariantType::String, "", {"clone processors from a template"}}, diff --git a/Framework/Core/src/WorkflowHelpers.cxx b/Framework/Core/src/WorkflowHelpers.cxx index 714706952d26c..abe566e239618 100644 --- a/Framework/Core/src/WorkflowHelpers.cxx +++ b/Framework/Core/src/WorkflowHelpers.cxx @@ -505,7 +505,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext 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); + extraSpecs.push_back(timePipeline(ccdbBackend, ctx.options().get("ccdb-fetchers"))); } 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}); From 2e6b8722d2d4b4926a03bdcd481222afe1641980 Mon Sep 17 00:00:00 2001 From: Piotr Konopka Date: Thu, 19 Mar 2026 08:16:45 +0100 Subject: [PATCH 387/701] Correctly handle errors in merging histograms with kAverage (#15189) As pointed out by Felix Schlepper, TH1::Add reports errors differently than Merge, see https://root.cern.ch/doc/master/classTH1.html#a6e3008f571628f0c9d17d754c8b88730 Fixes QC-1341. --- Utilities/Mergers/src/MergerAlgorithm.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Utilities/Mergers/src/MergerAlgorithm.cxx b/Utilities/Mergers/src/MergerAlgorithm.cxx index 9395dd0a2b3f7..2cd09712e4a81 100644 --- a/Utilities/Mergers/src/MergerAlgorithm.cxx +++ b/Utilities/Mergers/src/MergerAlgorithm.cxx @@ -111,7 +111,7 @@ Long64_t mergeDefault(TObject* const target, TObject* const other) // Merge() does not support averages, we have to use Add() // this will break if collection.size != 1 if (auto otherTH1 = dynamic_cast(otherCollection.First())) { - errorCode = targetTH1->Add(otherTH1); + errorCode = targetTH1->Add(otherTH1) == kFALSE ? -1 : 0; } } else { // Add() does not support histograms with labels, thus we resort to Merge() by default From 20be6e73f6aaf46a30d3e2e21df455891c1f2167 Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Thu, 19 Mar 2026 08:58:05 +0100 Subject: [PATCH 388/701] DPL: Better detection for injected workflows (#15130) * detect tfnsource rather than the converter * fix and update kine- and hepmc-publishers * use detail instead of debug --- Framework/Core/src/ArrowSupport.cxx | 8 ++- Framework/Core/src/WorkflowHelpers.cxx | 15 +++- run/o2sim_hepmc_publisher.cxx | 94 +++++++++++++------------- run/o2sim_kine_publisher.cxx | 3 +- run/o2sim_mctracks_to_aod.cxx | 12 ++-- 5 files changed, 74 insertions(+), 58 deletions(-) diff --git a/Framework/Core/src/ArrowSupport.cxx b/Framework/Core/src/ArrowSupport.cxx index c5cc021a53478..81acc26b1b097 100644 --- a/Framework/Core/src/ArrowSupport.cxx +++ b/Framework/Core/src/ArrowSupport.cxx @@ -680,8 +680,12 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() 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 + auto tfnsource = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { + return std::ranges::any_of(spec.outputs, [](OutputSpec const& output) { + return DataSpecUtils::match(output, "TFN", "TFNumber", 0); + }); + }); + if (tfnsource == 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 } diff --git a/Framework/Core/src/WorkflowHelpers.cxx b/Framework/Core/src/WorkflowHelpers.cxx index abe566e239618..2ef3df9426fde 100644 --- a/Framework/Core/src/WorkflowHelpers.cxx +++ b/Framework/Core/src/WorkflowHelpers.cxx @@ -411,13 +411,17 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext // add the reader if (aodReader.outputs.empty() == false) { - auto mctracks2aod = std::ranges::find_if(workflow, [](auto const& x) { return x.name == "mctracks-to-aod"; }); - if (mctracks2aod == workflow.end()) { + auto tfnsource = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { + return std::ranges::any_of(spec.outputs, [](OutputSpec const& output) { + return DataSpecUtils::match(output, "TFN", "TFNumber", 0); + }); + }); + if (tfnsource == workflow.end()) { // add normal reader aodReader.outputs.emplace_back(OutputSpec{"TFN", "TFNumber"}); aodReader.outputs.emplace_back(OutputSpec{"TFF", "TFFilename"}); } else { - // AODs are being injected on-the-fly, add error-handler reader + // AODs are being injected the tfnsource is the entry point, add error-handler reader aodReader.algorithm = AlgorithmSpec{ adaptStateful( [](DeviceSpec const& spec) { @@ -700,6 +704,11 @@ void WorkflowHelpers::injectAODWriter(WorkflowSpec& workflow, ConfigContext cons return DataSpecUtils::partialMatch(spec, o2::header::DataOrigin("TFN")); }); dec.isDangling[std::distance(dec.outputsInputs.begin(), it)] = false; + + it = std::find_if(dec.outputsInputs.begin(), dec.outputsInputs.end(), [](InputSpec const& spec) -> bool { + return DataSpecUtils::partialMatch(spec, o2::header::DataOrigin("TFF")); + }); + dec.isDangling[std::distance(dec.outputsInputs.begin(), it)] = false; } } diff --git a/run/o2sim_hepmc_publisher.cxx b/run/o2sim_hepmc_publisher.cxx index bf40abacb134f..f255b4a3a4f62 100644 --- a/run/o2sim_hepmc_publisher.cxx +++ b/run/o2sim_hepmc_publisher.cxx @@ -37,7 +37,9 @@ struct O2simHepmcPublisher { int tfCounter = 0; std::shared_ptr hepMCReader; bool eos = false; - std::vector mcTracks; + + std::vector*> mctracks_vector; + std::vector mcheader_vector; void init(o2::framework::InitContext& /*ic*/) { @@ -50,13 +52,19 @@ struct O2simHepmcPublisher { LOGP(fatal, "Cannot open HEPMC kine file {}", (std::string)hepmcFileName); } // allocate the memory upfront to prevent reallocations later - mcTracks.reserve(1e3 * aggregate); + mctracks_vector.reserve(aggregate); + mcheader_vector.reserve(aggregate); } void run(o2::framework::ProcessingContext& pc) { HepMC3::GenEvent event; - for (auto i = 0; i < (int)aggregate; ++i) { + auto batch = maxEvents > 0 ? std::min((int)aggregate, (int)maxEvents - eventCounter) : (int)aggregate; + for (auto i = 0; i < batch; ++i) { + mctracks_vector.push_back(&pc.outputs().make>(Output{"MC", "MCTRACKS", 0})); + auto& mctracks = mctracks_vector.back(); + mcheader_vector.push_back(&pc.outputs().make(Output{"MC", "MCHEADER", 0})); + auto& mcheader = mcheader_vector.back(); // read next entry hepMCReader->read_event(event); if (hepMCReader->failed()) { @@ -66,61 +74,60 @@ struct O2simHepmcPublisher { } // create O2 MCHeader and MCtracks vector out of HEPMC event - o2::dataformats::MCEventHeader mcHeader; - mcHeader.SetEventID(event.event_number()); - mcHeader.SetVertex(event.event_pos().px(), event.event_pos().py(), event.event_pos().pz()); + mcheader->SetEventID(event.event_number()); + mcheader->SetVertex(event.event_pos().px(), event.event_pos().py(), event.event_pos().pz()); auto xsecInfo = event.cross_section(); if (xsecInfo != nullptr) { - mcHeader.putInfo(MCInfoKeys::acceptedEvents, (uint64_t)xsecInfo->get_accepted_events()); - mcHeader.putInfo(MCInfoKeys::attemptedEvents, (uint64_t)xsecInfo->get_attempted_events()); - mcHeader.putInfo(MCInfoKeys::xSection, (float)xsecInfo->xsec()); - mcHeader.putInfo(MCInfoKeys::xSectionError, (float)xsecInfo->xsec_err()); + mcheader->putInfo(MCInfoKeys::acceptedEvents, (uint64_t)xsecInfo->get_accepted_events()); + mcheader->putInfo(MCInfoKeys::attemptedEvents, (uint64_t)xsecInfo->get_attempted_events()); + mcheader->putInfo(MCInfoKeys::xSection, (float)xsecInfo->xsec()); + mcheader->putInfo(MCInfoKeys::xSectionError, (float)xsecInfo->xsec_err()); } auto scale = event.attribute(MCInfoKeys::eventScale); if (scale != nullptr) { - mcHeader.putInfo(MCInfoKeys::eventScale, (float)scale->value()); + mcheader->putInfo(MCInfoKeys::eventScale, (float)scale->value()); } auto nMPI = event.attribute(MCInfoKeys::mpi); if (nMPI != nullptr) { - mcHeader.putInfo(MCInfoKeys::mpi, nMPI->value()); + mcheader->putInfo(MCInfoKeys::mpi, nMPI->value()); } auto sid = event.attribute(MCInfoKeys::processCode); auto scode = event.attribute(MCInfoKeys::processID); // default pythia8 hepmc3 interface uses signal_process_id if (sid != nullptr) { - mcHeader.putInfo(MCInfoKeys::processCode, sid->value()); + mcheader->putInfo(MCInfoKeys::processCode, sid->value()); } else if (scode != nullptr) { - mcHeader.putInfo(MCInfoKeys::processCode, scode->value()); + mcheader->putInfo(MCInfoKeys::processCode, scode->value()); } auto pdfInfo = event.pdf_info(); if (pdfInfo != nullptr) { - mcHeader.putInfo(MCInfoKeys::pdfParton1Id, pdfInfo->parton_id[0]); - mcHeader.putInfo(MCInfoKeys::pdfParton2Id, pdfInfo->parton_id[1]); - mcHeader.putInfo(MCInfoKeys::pdfCode1, pdfInfo->pdf_id[0]); - mcHeader.putInfo(MCInfoKeys::pdfCode2, pdfInfo->pdf_id[1]); - mcHeader.putInfo(MCInfoKeys::pdfX1, (float)pdfInfo->x[0]); - mcHeader.putInfo(MCInfoKeys::pdfX2, (float)pdfInfo->x[1]); - mcHeader.putInfo(MCInfoKeys::pdfScale, (float)pdfInfo->scale); - mcHeader.putInfo(MCInfoKeys::pdfXF1, (float)pdfInfo->xf[0]); - mcHeader.putInfo(MCInfoKeys::pdfXF2, (float)pdfInfo->xf[1]); + mcheader->putInfo(MCInfoKeys::pdfParton1Id, pdfInfo->parton_id[0]); + mcheader->putInfo(MCInfoKeys::pdfParton2Id, pdfInfo->parton_id[1]); + mcheader->putInfo(MCInfoKeys::pdfCode1, pdfInfo->pdf_id[0]); + mcheader->putInfo(MCInfoKeys::pdfCode2, pdfInfo->pdf_id[1]); + mcheader->putInfo(MCInfoKeys::pdfX1, (float)pdfInfo->x[0]); + mcheader->putInfo(MCInfoKeys::pdfX2, (float)pdfInfo->x[1]); + mcheader->putInfo(MCInfoKeys::pdfScale, (float)pdfInfo->scale); + mcheader->putInfo(MCInfoKeys::pdfXF1, (float)pdfInfo->xf[0]); + mcheader->putInfo(MCInfoKeys::pdfXF2, (float)pdfInfo->xf[1]); } auto heavyIon = event.heavy_ion(); if (heavyIon != nullptr) { - mcHeader.putInfo(MCInfoKeys::nCollHard, heavyIon->Ncoll_hard); - mcHeader.putInfo(MCInfoKeys::nPartProjectile, heavyIon->Npart_proj); - mcHeader.putInfo(MCInfoKeys::nPartTarget, heavyIon->Npart_targ); - mcHeader.putInfo(MCInfoKeys::nColl, heavyIon->Ncoll); - mcHeader.putInfo(MCInfoKeys::nCollNNWounded, heavyIon->N_Nwounded_collisions); - mcHeader.putInfo(MCInfoKeys::nCollNWoundedN, heavyIon->Nwounded_N_collisions); - mcHeader.putInfo(MCInfoKeys::nCollNWoundedNwounded, heavyIon->Nwounded_Nwounded_collisions); - mcHeader.putInfo(MCInfoKeys::nSpecProjectileNeutron, heavyIon->Nspec_proj_n); - mcHeader.putInfo(MCInfoKeys::nSpecProjectileProton, heavyIon->Nspec_proj_p); - mcHeader.putInfo(MCInfoKeys::nSpecTargetNeutron, heavyIon->Nspec_targ_n); - mcHeader.putInfo(MCInfoKeys::nSpecTargetProton, heavyIon->Nspec_targ_p); - mcHeader.putInfo(MCInfoKeys::impactParameter, (float)heavyIon->impact_parameter); - mcHeader.putInfo(MCInfoKeys::planeAngle, (float)heavyIon->event_plane_angle); - mcHeader.putInfo("eccentricity", (float)heavyIon->eccentricity); - mcHeader.putInfo(MCInfoKeys::sigmaInelNN, (float)heavyIon->sigma_inel_NN); - mcHeader.putInfo(MCInfoKeys::centrality, (float)heavyIon->centrality); + mcheader->putInfo(MCInfoKeys::nCollHard, heavyIon->Ncoll_hard); + mcheader->putInfo(MCInfoKeys::nPartProjectile, heavyIon->Npart_proj); + mcheader->putInfo(MCInfoKeys::nPartTarget, heavyIon->Npart_targ); + mcheader->putInfo(MCInfoKeys::nColl, heavyIon->Ncoll); + mcheader->putInfo(MCInfoKeys::nCollNNWounded, heavyIon->N_Nwounded_collisions); + mcheader->putInfo(MCInfoKeys::nCollNWoundedN, heavyIon->Nwounded_N_collisions); + mcheader->putInfo(MCInfoKeys::nCollNWoundedNwounded, heavyIon->Nwounded_Nwounded_collisions); + mcheader->putInfo(MCInfoKeys::nSpecProjectileNeutron, heavyIon->Nspec_proj_n); + mcheader->putInfo(MCInfoKeys::nSpecProjectileProton, heavyIon->Nspec_proj_p); + mcheader->putInfo(MCInfoKeys::nSpecTargetNeutron, heavyIon->Nspec_targ_n); + mcheader->putInfo(MCInfoKeys::nSpecTargetProton, heavyIon->Nspec_targ_p); + mcheader->putInfo(MCInfoKeys::impactParameter, (float)heavyIon->impact_parameter); + mcheader->putInfo(MCInfoKeys::planeAngle, (float)heavyIon->event_plane_angle); + mcheader->putInfo("eccentricity", (float)heavyIon->eccentricity); + mcheader->putInfo(MCInfoKeys::sigmaInelNN, (float)heavyIon->sigma_inel_NN); + mcheader->putInfo(MCInfoKeys::centrality, (float)heavyIon->centrality); } auto particles = event.particles(); @@ -131,7 +138,7 @@ struct O2simHepmcPublisher { auto has_children = children.size() > 0; auto p = particle->momentum(); auto v = particle->production_vertex(); - mcTracks.emplace_back( + mctracks->emplace_back( particle->pid(), has_parents ? parents.front()->id() : -1, has_parents ? parents.back()->id() : -1, has_children ? children.front()->id() : -1, has_children ? children.back()->id() : -1, @@ -139,18 +146,13 @@ struct O2simHepmcPublisher { v->position().x(), v->position().y(), v->position().z(), v->position().t(), 0); } - - // add to the message - pc.outputs().snapshot(Output{"MC", "MCHEADER", 0}, mcHeader); - pc.outputs().snapshot(Output{"MC", "MCTRACKS", 0}, mcTracks); - mcTracks.clear(); ++eventCounter; } // report number of TFs injected for the rate limiter to work ++tfCounter; pc.services().get().send(o2::monitoring::Metric{(uint64_t)tfCounter, "df-sent"}.addTag(o2::monitoring::tags::Key::Subsystem, o2::monitoring::tags::Value::DPL)); - if (eos || (maxEvents > 0 && eventCounter == maxEvents)) { + if (eos || (maxEvents > 0 && eventCounter >= maxEvents)) { pc.services().get().endOfStream(); pc.services().get().readyToQuit(QuitRequest::Me); } diff --git a/run/o2sim_kine_publisher.cxx b/run/o2sim_kine_publisher.cxx index cfbea6ae02a5f..5920743c3fafa 100644 --- a/run/o2sim_kine_publisher.cxx +++ b/run/o2sim_kine_publisher.cxx @@ -40,7 +40,8 @@ struct O2simKinePublisher { void run(o2::framework::ProcessingContext& pc) { - for (auto i = 0; i < std::min((int)aggregate, nEvents - eventCounter); ++i) { + auto batch = std::min((int)aggregate, nEvents - eventCounter); + for (auto i = 0; i < batch; ++i) { auto mcevent = mcKinReader->getMCEventHeader(0, eventCounter); auto mctracks = mcKinReader->getTracks(0, eventCounter); pc.outputs().snapshot(Output{"MC", "MCHEADER", 0}, mcevent); diff --git a/run/o2sim_mctracks_to_aod.cxx b/run/o2sim_mctracks_to_aod.cxx index 124e8aa7b3e42..d95a3b33cc38f 100644 --- a/run/o2sim_mctracks_to_aod.cxx +++ b/run/o2sim_mctracks_to_aod.cxx @@ -70,7 +70,7 @@ struct MctracksToAod { /** Run the conversion */ void run(o2::framework::ProcessingContext& pc) { - LOG(debug) << "=== Running extended MC AOD exporter ==="; + LOG(detail) << "=== Running extended MC AOD exporter ==="; using namespace o2::aodmchelpers; using McHeader = o2::dataformats::MCEventHeader; using McTrack = o2::MCTrack; @@ -94,13 +94,13 @@ struct MctracksToAod { // TODO: include BC simulation auto bcCounter = 0UL; size_t offset = 0; - LOG(debug) << "--- Loop over " << nParts << " parts ---"; + LOG(detail) << "--- Loop over " << nParts << " parts ---"; for (auto i = 0U; i < nParts; ++i) { auto record = mSampler.generateCollisionTime(); auto header = pc.inputs().get("mcheader", i); auto tracks = pc.inputs().get("mctracks", i); - LOG(debug) << "Updating collision table"; + LOG(detail) << "Updating collision table"; auto genID = updateMCCollisions(mCollisions.cursor, bcCounter, record.timeInBCNS * 1.e-3, @@ -108,12 +108,12 @@ struct MctracksToAod { 0, i); - LOG(debug) << "Updating HepMC tables"; + LOG(detail) << "Updating HepMC tables"; updateHepMCXSection(mXSections.cursor, bcCounter, genID, *header); updateHepMCPdfInfo(mPdfInfos.cursor, bcCounter, genID, *header); updateHepMCHeavyIon(mHeavyIons.cursor, bcCounter, genID, *header); - LOG(debug) << "Updating particles table"; + LOG(detail) << "Updating particles table"; TrackToIndex preselect; offset = updateParticles(mParticles.cursor, bcCounter, @@ -123,7 +123,7 @@ struct MctracksToAod { (bool)filt, false); - LOG(debug) << "Increment BC counter"; + LOG(detail) << "Increment BC counter"; bcCounter++; } From d2e721631fc6cbee151340f049e4bbdfd1c4d5e4 Mon Sep 17 00:00:00 2001 From: wiechula <11199190+wiechula@users.noreply.github.com> Date: Thu, 19 Mar 2026 14:43:56 +0100 Subject: [PATCH 389/701] TPC: Implement ad-hoc correction for r and z in old SCD map creation (#15103) * Implement ad-hoc correction for r and z * fix warning: usage of abs instead of std::abs --- .../include/SpacePoints/TrackResiduals.h | 8 +++++++ .../SpacePoints/src/TrackInterpolation.cxx | 4 ++-- .../SpacePoints/src/TrackResiduals.cxx | 22 +++++++++++++++++-- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackResiduals.h b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackResiduals.h index e4d0a3a053728..2ade12d951c58 100644 --- a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackResiduals.h +++ b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackResiduals.h @@ -443,6 +443,12 @@ class TrackResiduals /// output tree TTree* getOutputTree() { return mTreeOut.get(); } + /// Ad-hoc radial scaling factor A/C-Side + void setAdhocScalingFactorX(const std::array& scaling) { mAdhocScalingX = scaling; } + + /// Ad-hoc correction of Z/X + void doAdhocCorrectionZ2X(bool corr) { mDoAdhocCorrectionZ2X = corr; } + private: std::bitset mInitResultsContainer{}; @@ -502,6 +508,8 @@ class TrackResiduals std::array, SECTORSPERSIDE * SIDES> mVoxelResults{}; ///< results per sector and per voxel for 3-D distortions VoxRes mVoxelResultsOut{}; ///< the results from mVoxelResults are copied in here to be able to stream them VoxRes* mVoxelResultsOutPtr{&mVoxelResultsOut}; ///< pointer to set the branch address to for the output + std::array mAdhocScalingX{0, 0}; ///< Ad-hoc radial scaling factor + bool mDoAdhocCorrectionZ2X{false}; ///< If to do ad-hoc correction for Z/X ClassDefNV(TrackResiduals, 3); }; diff --git a/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx b/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx index 539ae25862865..76daab93dd8e0 100644 --- a/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx +++ b/Detectors/TPC/calibration/SpacePoints/src/TrackInterpolation.cxx @@ -820,7 +820,7 @@ void TrackInterpolation::interpolateTrack(int iSeed) 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) { + 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(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()) { @@ -1168,7 +1168,7 @@ void TrackInterpolation::extrapolateTrack(int iSeed) 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) { + 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(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()) { diff --git a/Detectors/TPC/calibration/SpacePoints/src/TrackResiduals.cxx b/Detectors/TPC/calibration/SpacePoints/src/TrackResiduals.cxx index 45d7a6ae3c231..d3db11daf9e87 100644 --- a/Detectors/TPC/calibration/SpacePoints/src/TrackResiduals.cxx +++ b/Detectors/TPC/calibration/SpacePoints/src/TrackResiduals.cxx @@ -719,8 +719,26 @@ void TrackResiduals::smooth(int iSec) if (!(resVox.flags & SmoothDone)) { continue; } - resVox.DS[ResZ] += resVox.stat[VoxZ] * resVox.DS[ResX]; // remove slope*dX contribution from dZ - resVox.D[ResZ] += resVox.stat[VoxZ] * resVox.DS[ResX]; // remove slope*dX contribution from dZ + // TODO: Usage of Z/X is bug??? + float z2x = resVox.stat[VoxZ]; + if (mDoAdhocCorrectionZ2X) { + // + const float z = z2x * resVox.stat[VoxX] - resVox.DS[ResZ]; + const float x = resVox.stat[VoxX] - resVox.DS[ResX]; // is subration of DS[ResX] correct? + z2x = z / x; + } + resVox.DS[ResZ] += z2x * resVox.DS[ResX]; // remove slope*dX contribution from dZ + resVox.D[ResZ] += z2x * resVox.DS[ResX]; // remove slope*dX contribution from dZ + // + if (mAdhocScalingX[iSec >= 18] != 0) { + const float aDX = resVox.DS[ResX] * mAdhocScalingX[iSec >= 18]; + resVox.D[ResX] += aDX; + resVox.DS[ResX] += aDX; + resVox.D[ResY] += aDX * resVox.stat[VoxF]; + resVox.DS[ResY] += aDX * resVox.stat[VoxF]; + resVox.D[ResZ] += aDX * z2x; + resVox.DS[ResZ] += aDX * z2x; + } } } } From f67af9e4329d828dc094cd99b5bce638087fa329 Mon Sep 17 00:00:00 2001 From: wiechula Date: Thu, 19 Mar 2026 10:45:18 +0100 Subject: [PATCH 390/701] Change default calibration interval to 5min --- .../tpcinterpolationworkflow/src/TPCInterpolationSpec.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCInterpolationSpec.cxx b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCInterpolationSpec.cxx index 4912a1df36a33..4f17aec3d49d5 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCInterpolationSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/TPCInterpolationSpec.cxx @@ -206,7 +206,7 @@ DataProcessorSpec getTPCInterpolationSpec(GTrackID::mask_t srcCls, GTrackID::mas AlgorithmSpec{adaptFromTask(dataRequest, srcTrk, srcTrkMap, ggRequest, useMC, processITSTPConly, sendTrackData, debugOutput, extDetResid)}, Options{ {"matCorrType", VariantType::Int, 2, {"material correction type (definition in Propagator.h)"}}, - {"sec-per-slot", VariantType::UInt32, 600u, {"number of seconds per calibration time slot (put 0 for infinite slot length)"}}, + {"sec-per-slot", VariantType::UInt32, 300u, {"number of seconds per calibration time slot (put 0 for infinite slot length)"}}, {"process-seeds", VariantType::Bool, false, {"do not remove duplicates, e.g. for ITS-TPC-TRD track also process its seeding ITS-TPC part"}}}}; } From ae084c77dca4654de4ad2d8a6a3b78aadef8df1e Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Fri, 20 Mar 2026 09:00:36 +0100 Subject: [PATCH 391/701] Revert "DPL: Better detection for injected workflows (#15130)" (#15197) This reverts commit 20be6e73f6aaf46a30d3e2e21df455891c1f2167. --- Framework/Core/src/ArrowSupport.cxx | 8 +-- Framework/Core/src/WorkflowHelpers.cxx | 15 +--- run/o2sim_hepmc_publisher.cxx | 94 +++++++++++++------------- run/o2sim_kine_publisher.cxx | 3 +- run/o2sim_mctracks_to_aod.cxx | 12 ++-- 5 files changed, 58 insertions(+), 74 deletions(-) diff --git a/Framework/Core/src/ArrowSupport.cxx b/Framework/Core/src/ArrowSupport.cxx index 81acc26b1b097..c5cc021a53478 100644 --- a/Framework/Core/src/ArrowSupport.cxx +++ b/Framework/Core/src/ArrowSupport.cxx @@ -680,12 +680,8 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() workflow.erase(reader); } else { // load reader algorithm before deployment - auto tfnsource = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { - return std::ranges::any_of(spec.outputs, [](OutputSpec const& output) { - return DataSpecUtils::match(output, "TFN", "TFNumber", 0); - }); - }); - if (tfnsource == workflow.end()) { // add normal reader algorithm only if no on-the-fly generator is injected + 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 } diff --git a/Framework/Core/src/WorkflowHelpers.cxx b/Framework/Core/src/WorkflowHelpers.cxx index 2ef3df9426fde..abe566e239618 100644 --- a/Framework/Core/src/WorkflowHelpers.cxx +++ b/Framework/Core/src/WorkflowHelpers.cxx @@ -411,17 +411,13 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext // add the reader if (aodReader.outputs.empty() == false) { - auto tfnsource = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { - return std::ranges::any_of(spec.outputs, [](OutputSpec const& output) { - return DataSpecUtils::match(output, "TFN", "TFNumber", 0); - }); - }); - if (tfnsource == workflow.end()) { + auto mctracks2aod = std::ranges::find_if(workflow, [](auto const& x) { return x.name == "mctracks-to-aod"; }); + if (mctracks2aod == workflow.end()) { // add normal reader aodReader.outputs.emplace_back(OutputSpec{"TFN", "TFNumber"}); aodReader.outputs.emplace_back(OutputSpec{"TFF", "TFFilename"}); } else { - // AODs are being injected the tfnsource is the entry point, add error-handler reader + // AODs are being injected on-the-fly, add error-handler reader aodReader.algorithm = AlgorithmSpec{ adaptStateful( [](DeviceSpec const& spec) { @@ -704,11 +700,6 @@ void WorkflowHelpers::injectAODWriter(WorkflowSpec& workflow, ConfigContext cons return DataSpecUtils::partialMatch(spec, o2::header::DataOrigin("TFN")); }); dec.isDangling[std::distance(dec.outputsInputs.begin(), it)] = false; - - it = std::find_if(dec.outputsInputs.begin(), dec.outputsInputs.end(), [](InputSpec const& spec) -> bool { - return DataSpecUtils::partialMatch(spec, o2::header::DataOrigin("TFF")); - }); - dec.isDangling[std::distance(dec.outputsInputs.begin(), it)] = false; } } diff --git a/run/o2sim_hepmc_publisher.cxx b/run/o2sim_hepmc_publisher.cxx index f255b4a3a4f62..bf40abacb134f 100644 --- a/run/o2sim_hepmc_publisher.cxx +++ b/run/o2sim_hepmc_publisher.cxx @@ -37,9 +37,7 @@ struct O2simHepmcPublisher { int tfCounter = 0; std::shared_ptr hepMCReader; bool eos = false; - - std::vector*> mctracks_vector; - std::vector mcheader_vector; + std::vector mcTracks; void init(o2::framework::InitContext& /*ic*/) { @@ -52,19 +50,13 @@ struct O2simHepmcPublisher { LOGP(fatal, "Cannot open HEPMC kine file {}", (std::string)hepmcFileName); } // allocate the memory upfront to prevent reallocations later - mctracks_vector.reserve(aggregate); - mcheader_vector.reserve(aggregate); + mcTracks.reserve(1e3 * aggregate); } void run(o2::framework::ProcessingContext& pc) { HepMC3::GenEvent event; - auto batch = maxEvents > 0 ? std::min((int)aggregate, (int)maxEvents - eventCounter) : (int)aggregate; - for (auto i = 0; i < batch; ++i) { - mctracks_vector.push_back(&pc.outputs().make>(Output{"MC", "MCTRACKS", 0})); - auto& mctracks = mctracks_vector.back(); - mcheader_vector.push_back(&pc.outputs().make(Output{"MC", "MCHEADER", 0})); - auto& mcheader = mcheader_vector.back(); + for (auto i = 0; i < (int)aggregate; ++i) { // read next entry hepMCReader->read_event(event); if (hepMCReader->failed()) { @@ -74,60 +66,61 @@ struct O2simHepmcPublisher { } // create O2 MCHeader and MCtracks vector out of HEPMC event - mcheader->SetEventID(event.event_number()); - mcheader->SetVertex(event.event_pos().px(), event.event_pos().py(), event.event_pos().pz()); + o2::dataformats::MCEventHeader mcHeader; + mcHeader.SetEventID(event.event_number()); + mcHeader.SetVertex(event.event_pos().px(), event.event_pos().py(), event.event_pos().pz()); auto xsecInfo = event.cross_section(); if (xsecInfo != nullptr) { - mcheader->putInfo(MCInfoKeys::acceptedEvents, (uint64_t)xsecInfo->get_accepted_events()); - mcheader->putInfo(MCInfoKeys::attemptedEvents, (uint64_t)xsecInfo->get_attempted_events()); - mcheader->putInfo(MCInfoKeys::xSection, (float)xsecInfo->xsec()); - mcheader->putInfo(MCInfoKeys::xSectionError, (float)xsecInfo->xsec_err()); + mcHeader.putInfo(MCInfoKeys::acceptedEvents, (uint64_t)xsecInfo->get_accepted_events()); + mcHeader.putInfo(MCInfoKeys::attemptedEvents, (uint64_t)xsecInfo->get_attempted_events()); + mcHeader.putInfo(MCInfoKeys::xSection, (float)xsecInfo->xsec()); + mcHeader.putInfo(MCInfoKeys::xSectionError, (float)xsecInfo->xsec_err()); } auto scale = event.attribute(MCInfoKeys::eventScale); if (scale != nullptr) { - mcheader->putInfo(MCInfoKeys::eventScale, (float)scale->value()); + mcHeader.putInfo(MCInfoKeys::eventScale, (float)scale->value()); } auto nMPI = event.attribute(MCInfoKeys::mpi); if (nMPI != nullptr) { - mcheader->putInfo(MCInfoKeys::mpi, nMPI->value()); + mcHeader.putInfo(MCInfoKeys::mpi, nMPI->value()); } auto sid = event.attribute(MCInfoKeys::processCode); auto scode = event.attribute(MCInfoKeys::processID); // default pythia8 hepmc3 interface uses signal_process_id if (sid != nullptr) { - mcheader->putInfo(MCInfoKeys::processCode, sid->value()); + mcHeader.putInfo(MCInfoKeys::processCode, sid->value()); } else if (scode != nullptr) { - mcheader->putInfo(MCInfoKeys::processCode, scode->value()); + mcHeader.putInfo(MCInfoKeys::processCode, scode->value()); } auto pdfInfo = event.pdf_info(); if (pdfInfo != nullptr) { - mcheader->putInfo(MCInfoKeys::pdfParton1Id, pdfInfo->parton_id[0]); - mcheader->putInfo(MCInfoKeys::pdfParton2Id, pdfInfo->parton_id[1]); - mcheader->putInfo(MCInfoKeys::pdfCode1, pdfInfo->pdf_id[0]); - mcheader->putInfo(MCInfoKeys::pdfCode2, pdfInfo->pdf_id[1]); - mcheader->putInfo(MCInfoKeys::pdfX1, (float)pdfInfo->x[0]); - mcheader->putInfo(MCInfoKeys::pdfX2, (float)pdfInfo->x[1]); - mcheader->putInfo(MCInfoKeys::pdfScale, (float)pdfInfo->scale); - mcheader->putInfo(MCInfoKeys::pdfXF1, (float)pdfInfo->xf[0]); - mcheader->putInfo(MCInfoKeys::pdfXF2, (float)pdfInfo->xf[1]); + mcHeader.putInfo(MCInfoKeys::pdfParton1Id, pdfInfo->parton_id[0]); + mcHeader.putInfo(MCInfoKeys::pdfParton2Id, pdfInfo->parton_id[1]); + mcHeader.putInfo(MCInfoKeys::pdfCode1, pdfInfo->pdf_id[0]); + mcHeader.putInfo(MCInfoKeys::pdfCode2, pdfInfo->pdf_id[1]); + mcHeader.putInfo(MCInfoKeys::pdfX1, (float)pdfInfo->x[0]); + mcHeader.putInfo(MCInfoKeys::pdfX2, (float)pdfInfo->x[1]); + mcHeader.putInfo(MCInfoKeys::pdfScale, (float)pdfInfo->scale); + mcHeader.putInfo(MCInfoKeys::pdfXF1, (float)pdfInfo->xf[0]); + mcHeader.putInfo(MCInfoKeys::pdfXF2, (float)pdfInfo->xf[1]); } auto heavyIon = event.heavy_ion(); if (heavyIon != nullptr) { - mcheader->putInfo(MCInfoKeys::nCollHard, heavyIon->Ncoll_hard); - mcheader->putInfo(MCInfoKeys::nPartProjectile, heavyIon->Npart_proj); - mcheader->putInfo(MCInfoKeys::nPartTarget, heavyIon->Npart_targ); - mcheader->putInfo(MCInfoKeys::nColl, heavyIon->Ncoll); - mcheader->putInfo(MCInfoKeys::nCollNNWounded, heavyIon->N_Nwounded_collisions); - mcheader->putInfo(MCInfoKeys::nCollNWoundedN, heavyIon->Nwounded_N_collisions); - mcheader->putInfo(MCInfoKeys::nCollNWoundedNwounded, heavyIon->Nwounded_Nwounded_collisions); - mcheader->putInfo(MCInfoKeys::nSpecProjectileNeutron, heavyIon->Nspec_proj_n); - mcheader->putInfo(MCInfoKeys::nSpecProjectileProton, heavyIon->Nspec_proj_p); - mcheader->putInfo(MCInfoKeys::nSpecTargetNeutron, heavyIon->Nspec_targ_n); - mcheader->putInfo(MCInfoKeys::nSpecTargetProton, heavyIon->Nspec_targ_p); - mcheader->putInfo(MCInfoKeys::impactParameter, (float)heavyIon->impact_parameter); - mcheader->putInfo(MCInfoKeys::planeAngle, (float)heavyIon->event_plane_angle); - mcheader->putInfo("eccentricity", (float)heavyIon->eccentricity); - mcheader->putInfo(MCInfoKeys::sigmaInelNN, (float)heavyIon->sigma_inel_NN); - mcheader->putInfo(MCInfoKeys::centrality, (float)heavyIon->centrality); + mcHeader.putInfo(MCInfoKeys::nCollHard, heavyIon->Ncoll_hard); + mcHeader.putInfo(MCInfoKeys::nPartProjectile, heavyIon->Npart_proj); + mcHeader.putInfo(MCInfoKeys::nPartTarget, heavyIon->Npart_targ); + mcHeader.putInfo(MCInfoKeys::nColl, heavyIon->Ncoll); + mcHeader.putInfo(MCInfoKeys::nCollNNWounded, heavyIon->N_Nwounded_collisions); + mcHeader.putInfo(MCInfoKeys::nCollNWoundedN, heavyIon->Nwounded_N_collisions); + mcHeader.putInfo(MCInfoKeys::nCollNWoundedNwounded, heavyIon->Nwounded_Nwounded_collisions); + mcHeader.putInfo(MCInfoKeys::nSpecProjectileNeutron, heavyIon->Nspec_proj_n); + mcHeader.putInfo(MCInfoKeys::nSpecProjectileProton, heavyIon->Nspec_proj_p); + mcHeader.putInfo(MCInfoKeys::nSpecTargetNeutron, heavyIon->Nspec_targ_n); + mcHeader.putInfo(MCInfoKeys::nSpecTargetProton, heavyIon->Nspec_targ_p); + mcHeader.putInfo(MCInfoKeys::impactParameter, (float)heavyIon->impact_parameter); + mcHeader.putInfo(MCInfoKeys::planeAngle, (float)heavyIon->event_plane_angle); + mcHeader.putInfo("eccentricity", (float)heavyIon->eccentricity); + mcHeader.putInfo(MCInfoKeys::sigmaInelNN, (float)heavyIon->sigma_inel_NN); + mcHeader.putInfo(MCInfoKeys::centrality, (float)heavyIon->centrality); } auto particles = event.particles(); @@ -138,7 +131,7 @@ struct O2simHepmcPublisher { auto has_children = children.size() > 0; auto p = particle->momentum(); auto v = particle->production_vertex(); - mctracks->emplace_back( + mcTracks.emplace_back( particle->pid(), has_parents ? parents.front()->id() : -1, has_parents ? parents.back()->id() : -1, has_children ? children.front()->id() : -1, has_children ? children.back()->id() : -1, @@ -146,13 +139,18 @@ struct O2simHepmcPublisher { v->position().x(), v->position().y(), v->position().z(), v->position().t(), 0); } + + // add to the message + pc.outputs().snapshot(Output{"MC", "MCHEADER", 0}, mcHeader); + pc.outputs().snapshot(Output{"MC", "MCTRACKS", 0}, mcTracks); + mcTracks.clear(); ++eventCounter; } // report number of TFs injected for the rate limiter to work ++tfCounter; pc.services().get().send(o2::monitoring::Metric{(uint64_t)tfCounter, "df-sent"}.addTag(o2::monitoring::tags::Key::Subsystem, o2::monitoring::tags::Value::DPL)); - if (eos || (maxEvents > 0 && eventCounter >= maxEvents)) { + if (eos || (maxEvents > 0 && eventCounter == maxEvents)) { pc.services().get().endOfStream(); pc.services().get().readyToQuit(QuitRequest::Me); } diff --git a/run/o2sim_kine_publisher.cxx b/run/o2sim_kine_publisher.cxx index 5920743c3fafa..cfbea6ae02a5f 100644 --- a/run/o2sim_kine_publisher.cxx +++ b/run/o2sim_kine_publisher.cxx @@ -40,8 +40,7 @@ struct O2simKinePublisher { void run(o2::framework::ProcessingContext& pc) { - auto batch = std::min((int)aggregate, nEvents - eventCounter); - for (auto i = 0; i < batch; ++i) { + for (auto i = 0; i < std::min((int)aggregate, nEvents - eventCounter); ++i) { auto mcevent = mcKinReader->getMCEventHeader(0, eventCounter); auto mctracks = mcKinReader->getTracks(0, eventCounter); pc.outputs().snapshot(Output{"MC", "MCHEADER", 0}, mcevent); diff --git a/run/o2sim_mctracks_to_aod.cxx b/run/o2sim_mctracks_to_aod.cxx index d95a3b33cc38f..124e8aa7b3e42 100644 --- a/run/o2sim_mctracks_to_aod.cxx +++ b/run/o2sim_mctracks_to_aod.cxx @@ -70,7 +70,7 @@ struct MctracksToAod { /** Run the conversion */ void run(o2::framework::ProcessingContext& pc) { - LOG(detail) << "=== Running extended MC AOD exporter ==="; + LOG(debug) << "=== Running extended MC AOD exporter ==="; using namespace o2::aodmchelpers; using McHeader = o2::dataformats::MCEventHeader; using McTrack = o2::MCTrack; @@ -94,13 +94,13 @@ struct MctracksToAod { // TODO: include BC simulation auto bcCounter = 0UL; size_t offset = 0; - LOG(detail) << "--- Loop over " << nParts << " parts ---"; + LOG(debug) << "--- Loop over " << nParts << " parts ---"; for (auto i = 0U; i < nParts; ++i) { auto record = mSampler.generateCollisionTime(); auto header = pc.inputs().get("mcheader", i); auto tracks = pc.inputs().get("mctracks", i); - LOG(detail) << "Updating collision table"; + LOG(debug) << "Updating collision table"; auto genID = updateMCCollisions(mCollisions.cursor, bcCounter, record.timeInBCNS * 1.e-3, @@ -108,12 +108,12 @@ struct MctracksToAod { 0, i); - LOG(detail) << "Updating HepMC tables"; + LOG(debug) << "Updating HepMC tables"; updateHepMCXSection(mXSections.cursor, bcCounter, genID, *header); updateHepMCPdfInfo(mPdfInfos.cursor, bcCounter, genID, *header); updateHepMCHeavyIon(mHeavyIons.cursor, bcCounter, genID, *header); - LOG(detail) << "Updating particles table"; + LOG(debug) << "Updating particles table"; TrackToIndex preselect; offset = updateParticles(mParticles.cursor, bcCounter, @@ -123,7 +123,7 @@ struct MctracksToAod { (bool)filt, false); - LOG(detail) << "Increment BC counter"; + LOG(debug) << "Increment BC counter"; bcCounter++; } From 70ca1a2fe8ac4e4bd86ad363541b63f749d96208 Mon Sep 17 00:00:00 2001 From: Fabio Colamaria Date: Fri, 20 Mar 2026 14:32:41 +0100 Subject: [PATCH 392/701] [ALICE3] Updated ALICE 3 IRIS coldplate in O2 geometry (#15198) * Updated ALICE 3 IRIS coldplate in O2 geometry * Please consider the following formatting changes --------- Co-authored-by: fcolamar Co-authored-by: ALICE Action Bot --- Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx | 2 +- .../Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx index 0394c59780141..d8246bcd8640c 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx @@ -108,7 +108,7 @@ void TRKServices::createMaterials() matmgr.Material("ALICE3_TRKSERVICES", 73, "BERYLLIUM", 9.01, 4., 1.848, 35.3, 36.7); // Beryllium - Candidate for IRIS vacuum vessel matmgr.Mixture("ALICE3_TRKSERVICES", 74, "ALUMINIUM5083", aAl5083, zAl5083, dAl5083, 9, wAl5083); // AL5083 - Candidate for IRIS vacuum vessel matmgr.Mixture("ALICE3_TRKSERVICES", 75, "ALUMINIUMBERYLLIUMMETAL", aAlBeMet, zAlBeMet, dAlBeMet, 2, wAlBeMet); // Aluminium-Beryllium metal - Candidate for IRIS vacuum vessel - matmgr.Material("ALICE3_TRKSERVICES", 76, "CARBONFIBERM55J6K", 12.0107, 6, 1.92, 999, 999); // Carbon Fiber M55J + matmgr.Material("ALICE3_TRKSERVICES", 76, "CARBONFIBERM55J6K", 12.0107, 6, 1.92, 22.4, 999); // Carbon Fiber M55J matmgr.Mixture("ALICE3_PIPE", 77, "VACUUM", aAir, zAir, dAir1, 4, wAir); matmgr.Medium("ALICE3_TRKSERVICES", 1, "CERAMIC", 66, 0, ifield, fieldm, tmaxfd, stemax, deemax, epsil, stmin); // Ceramic for cold plate diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx index 2f1a83f73bca3..809923b048234 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/VDGeometryBuilder.cxx @@ -272,8 +272,8 @@ static constexpr double kInclinedWallPhi0_deg = 27.799f; static constexpr double kInclinedWallRmax_cm = 4.75f; // 47.5 mm outer extension // 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 kColdplateRadius_cm = 2.6f; // 26 mm (inner radius) +static constexpr double kColdplateThickness_cm = 0.02f; // 1.5 mm static constexpr double kColdplateZ_cm = 50.0f; // full length // ========== φ-span helpers (gap/arc → degrees) ========== From 43a3732a0f85f14a5795d7831cff5095903935da Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Fri, 20 Mar 2026 13:40:22 +0100 Subject: [PATCH 393/701] AODProducer: Option to specify and forward parent AOD file This is needed in order to link a MC-AOD as "derived" from another AOD, for instance for MC-on-DATA embeddings. --- .../AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h | 1 + Detectors/AOD/src/AODProducerWorkflowSpec.cxx | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h index 588cd575ee7f5..c03c00f977648 100644 --- a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h +++ b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h @@ -283,6 +283,7 @@ class AODProducerWorkflowDPL : public Task TString mAnchorPass{""}; TString mAnchorProd{""}; TString mRecoPass{""}; + std::string mAODParent{""}; // link to possible parent AOD file (MC embedding,...) TString mUser{"aliprod"}; // who created this AOD (aliprod, alidaq, individual users) TStopwatch mTimer; bool mEMCselectLeading{false}; diff --git a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx index fcb419d6c441b..80b9e6ef4b551 100644 --- a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx +++ b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx @@ -1818,6 +1818,7 @@ void AODProducerWorkflowDPL::init(InitContext& ic) mAnchorProd = ic.options().get("anchor-prod"); mUser = ic.options().get("created-by"); mRecoPass = ic.options().get("reco-pass"); + mAODParent = ic.options().get("aod-parent"); mTFNumber = ic.options().get("aod-timeframe-id"); mRecoOnly = ic.options().get("reco-mctracks-only"); mTruncate = ic.options().get("enable-truncation"); @@ -2615,7 +2616,7 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) pc.outputs().snapshot(Output{"AMD", "AODMetadataVals", 0}, mMetaDataVals); pc.outputs().snapshot(Output{"TFN", "TFNumber", 0}, tfNumber); - pc.outputs().snapshot(Output{"TFF", "TFFilename", 0}, ""); + pc.outputs().snapshot(Output{"TFF", "TFFilename", 0}, mAODParent); mTimer.Stop(); } @@ -3485,6 +3486,7 @@ DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, boo ConfigParamSpec{"anchor-pass", VariantType::String, "", {"AnchorPassName"}}, ConfigParamSpec{"anchor-prod", VariantType::String, "", {"AnchorProduction"}}, ConfigParamSpec{"reco-pass", VariantType::String, "", {"RecoPassName"}}, + ConfigParamSpec{"aod-parent", VariantType::String, "", {"Parent AOD file name (if any)"}}, ConfigParamSpec{"created-by", VariantType::String, "", {"Who created this AO2D"}}, ConfigParamSpec{"nthreads", VariantType::Int, std::max(1, int(std::thread::hardware_concurrency() / 2)), {"Number of threads"}}, ConfigParamSpec{"reco-mctracks-only", VariantType::Int, 0, {"Store only reconstructed MC tracks and their mothers/daughters. 0 -- off, != 0 -- on"}}, From 859d892adf57752d6858b51372e61e7e16db35e4 Mon Sep 17 00:00:00 2001 From: wiechula Date: Thu, 19 Mar 2026 14:48:28 +0100 Subject: [PATCH 394/701] Add missing include for gcc 15.2 --- GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.cxx b/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.cxx index a1bee6ce47ebd..506cef9db7248 100644 --- a/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.cxx +++ b/GPU/GPUTracking/display/backend/GPUDisplayBackendVulkan.cxx @@ -23,6 +23,7 @@ VULKAN_HPP_DEFAULT_DISPATCH_LOADER_DYNAMIC_STORAGE #include "GPUParam.h" #include +#include using namespace o2::gpu; From d17d862c1c8f64e12ea96bbb9f5adf74618922e5 Mon Sep 17 00:00:00 2001 From: Maximiliano Puccio Date: Sat, 21 Mar 2026 07:38:43 +0100 Subject: [PATCH 395/701] Fix MLOT digit residuals (#15200) --- .../ALICE3/TRK/macros/test/CheckDigits.C | 36 ++++++++++++++++--- 1 file changed, 32 insertions(+), 4 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckDigits.C b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckDigits.C index 618dbe929a943..ec1adf500f562 100644 --- a/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckDigits.C +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckDigits.C @@ -31,6 +31,8 @@ #include "SimulationDataFormat/IOMCTruthContainerView.h" #include "SimulationDataFormat/MCCompLabel.h" #include "DetectorsBase/GeometryManager.h" +#include "ITSMFTSimulation/AlpideSimResponse.h" +#include "CCDB/BasicCCDBManager.h" #include "DataFormatsITSMFT/ROFRecord.h" @@ -98,6 +100,15 @@ void CheckDigits(std::string digifile = "trkdigits.root", std::string hitfile = SegmentationChip seg; // seg.Print(); + // MLOT response plane: y = halfThickness - depthMax. + float depthMax = (float)o2::trk::constants::apts::thickness; // fallback (no CCDB) + auto& ccdbMgr = o2::ccdb::BasicCCDBManager::instance(); + ccdbMgr.setURL("http://alice-ccdb.cern.ch"); + if (auto* alpResp = ccdbMgr.get("IT3/Calib/APTSResponse")) { + depthMax = alpResp->getDepthMax(); + } + const float yPlaneMLOT = o2::trk::SegmentationChip::SiliconThicknessMLOT / 2.f - depthMax; + const float yPlaneVD = -o2::trk::SegmentationChip::SiliconThicknessVD; // VD reference plane in local flat y // Hits TFile* hitFile = TFile::Open(hitfile.data()); TTree* hitTree = (TTree*)hitFile->Get("o2sim"); @@ -254,23 +265,40 @@ void CheckDigits(std::string digifile = "trkdigits.root", std::string hitfile = 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())); + // Hit local reference: Both VD and MLOT use response-plane interpolation (in flat local frame). + // For VD, transform curved → flat first, then interpolate. + o2::math_utils::Vector3D locH; /// Hit reference (at response plane) 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; + if (subDetID == 0) { + // VD: Interpolate to VD reference plane in flat frame; apply same r to X and Z + auto flatSta = seg.curvedToFlat(layer, locHS.X(), locHS.Y()); + auto flatEnd = seg.curvedToFlat(layer, locHE.X(), locHE.Y()); + float x0 = flatSta.X(), y0 = flatSta.Y(), z0 = locHS.Z(); + float dltx = flatEnd.X() - x0, dlty = flatEnd.Y() - y0, dltz = locHE.Z() - z0; + float r = (std::abs(dlty) > 1e-9f) ? (yPlaneVD - y0) / dlty : 0.5f; + locH.SetCoordinates(x0 + r * dltx, yPlaneVD, z0 + r * dltz); + } else { + // MLOT: Interpolate to response plane + float x0 = locHS.X(), y0 = locHS.Y(), z0 = locHS.Z(); + float dltx = locHE.X() - x0, dlty = locHE.Y() - y0, dltz = locHE.Z() - z0; + float r = (std::abs(dlty) > 1e-9f) ? (yPlaneMLOT - y0) / dlty : 0.5f; + locH.SetCoordinates(x0 + r * dltx, yPlaneMLOT, z0 + r * dltz); + } + 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()); + // locH is already in flat frame from interpolation above; convert digit to flat for comparison 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()); + locHF.SetCoordinates(locH.X(), locH.Y(), locH.Z()); // locH already in flat frame seg.localToDetector(locHF.X(), locHF.Z(), row, col, subDetID, layer, disk); } From 909c5f9a70041c79be4260cba857410814613784 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sat, 21 Mar 2026 09:42:31 +0100 Subject: [PATCH 396/701] DPL: propaedeutic to navigate a MessageSet without caching pairs (#15187) All this should be fairly straight forward changes while still preserving the old API. If something fails at this level it means that even the counting of dataset changes with this included, which it should not be. --- .../Core/include/Framework/DataModelViews.h | 7 +- Framework/Core/include/Framework/MessageSet.h | 22 +-- Framework/Core/test/test_MessageSet.cxx | 168 +++++++++++------- 3 files changed, 115 insertions(+), 82 deletions(-) diff --git a/Framework/Core/include/Framework/DataModelViews.h b/Framework/Core/include/Framework/DataModelViews.h index b7a334454bb6e..f42ef85ec78e1 100644 --- a/Framework/Core/include/Framework/DataModelViews.h +++ b/Framework/Core/include/Framework/DataModelViews.h @@ -206,15 +206,10 @@ struct get_num_payloads { 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; } + requires requires(R r) { requires 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]); diff --git a/Framework/Core/include/Framework/MessageSet.h b/Framework/Core/include/Framework/MessageSet.h index e7ae70e0ea2e5..281f9c42a0773 100644 --- a/Framework/Core/include/Framework/MessageSet.h +++ b/Framework/Core/include/Framework/MessageSet.h @@ -12,13 +12,13 @@ #define FRAMEWORK_MESSAGESET_H #include "Framework/PartRef.h" +#include +#include "Framework/DataModelViews.h" #include #include #include -namespace o2 -{ -namespace framework +namespace o2::framework { /// A set of inflight messages. @@ -83,21 +83,21 @@ struct MessageSet { } /// get number of in-flight O2 messages - size_t size() const + [[nodiscard]] size_t size() const { - return messageMap.size(); + return messages | count_parts{}; } /// get number of header-payload pairs - size_t getNumberOfPairs() const + [[nodiscard]] size_t getNumberOfPairs() const { - return pairMap.size(); + return messages | count_payloads{}; } /// get number of payloads for an in-flight message - size_t getNumberOfPayloads(size_t mi) const + [[nodiscard]] size_t getNumberOfPayloads(size_t mi) const { - return messageMap[mi].size; + return messages | get_num_payloads{mi}; } /// clear the set @@ -179,6 +179,6 @@ struct MessageSet { } }; -} // namespace framework -} // namespace o2 +} // namespace o2::framework + #endif // FRAMEWORK_MESSAGESET_H diff --git a/Framework/Core/test/test_MessageSet.cxx b/Framework/Core/test/test_MessageSet.cxx index d56e32fea1adb..37f823197ef18 100644 --- a/Framework/Core/test/test_MessageSet.cxx +++ b/Framework/Core/test/test_MessageSet.cxx @@ -10,126 +10,164 @@ // or submit itself to any jurisdiction. #include +#include #include "Framework/MessageSet.h" +#include "Framework/DataProcessingHeader.h" +#include "Headers/Stack.h" +#include "MemoryResources/MemoryResources.h" #include using namespace o2::framework; -TEST_CASE("MessageSet") { +TEST_CASE("MessageSet") +{ o2::framework::MessageSet msgSet; - std::vector ptrs; - std::unique_ptr msg(nullptr); + o2::header::DataHeader dh{}; + dh.splitPayloadParts = 0; + dh.splitPayloadIndex = 0; + o2::framework::DataProcessingHeader dph{0, 1}; + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + fair::mq::MessagePtr payload(transport->CreateMessage()); + auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); + fair::mq::MessagePtr header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph}); std::unique_ptr msg2(nullptr); - ptrs.emplace_back(std::move(msg)); + std::vector ptrs; + ptrs.emplace_back(std::move(header)); 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); + REQUIRE((msgSet.messages | count_payloads{}) == 1); + REQUIRE((msgSet.messages | get_dataref_indices{0, 0}).headerIdx == 0); + REQUIRE((msgSet.messages | get_dataref_indices{0, 0}).payloadIdx == 1); + REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); + REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); + CHECK_THROWS((msgSet.messages | get_pair{1})); } -TEST_CASE("MessageSetWithFunction") { +TEST_CASE("MessageSetWithFunction") +{ std::vector ptrs; - std::unique_ptr msg(nullptr); + o2::header::DataHeader dh{}; + dh.splitPayloadParts = 0; + dh.splitPayloadIndex = 0; + o2::framework::DataProcessingHeader dph{0, 1}; + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + fair::mq::MessagePtr payload(transport->CreateMessage()); + auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); + fair::mq::MessagePtr header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph}); std::unique_ptr msg2(nullptr); - ptrs.emplace_back(std::move(msg)); + ptrs.emplace_back(std::move(header)); 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); + REQUIRE((msgSet.messages | count_payloads{}) == 1); + REQUIRE((msgSet.messages | get_dataref_indices{0, 0}).headerIdx == 0); + REQUIRE((msgSet.messages | get_dataref_indices{0, 0}).payloadIdx == 1); + REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); + REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); + CHECK_THROWS((msgSet.messages | get_pair{1})); } -TEST_CASE("MessageSetWithMultipart") { +TEST_CASE("MessageSetWithMultipart") +{ std::vector ptrs; - std::unique_ptr msg(nullptr); + o2::header::DataHeader dh{}; + dh.splitPayloadParts = 2; + dh.splitPayloadIndex = 2; + o2::framework::DataProcessingHeader dph{0, 1}; + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + fair::mq::MessagePtr payload(transport->CreateMessage()); + auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); + fair::mq::MessagePtr header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph}); std::unique_ptr msg2(nullptr); std::unique_ptr msg3(nullptr); - ptrs.emplace_back(std::move(msg)); + ptrs.emplace_back(std::move(header)); 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); + REQUIRE((msgSet.messages | count_payloads{}) == 2); + REQUIRE((msgSet.messages | get_dataref_indices{0, 0}).headerIdx == 0); + REQUIRE((msgSet.messages | get_dataref_indices{0, 0}).payloadIdx == 1); + REQUIRE((msgSet.messages | get_dataref_indices{0, 1}).headerIdx == 0); + REQUIRE((msgSet.messages | get_dataref_indices{0, 1}).payloadIdx == 2); + REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); + REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); + REQUIRE((msgSet.messages | get_pair{1}).headerIdx == 0); + REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 2); + CHECK_THROWS((msgSet.messages | get_pair{2})); } -TEST_CASE("MessageSetAddPartRef") { +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)}; + 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); + o2::header::DataHeader dh1{}; + dh1.splitPayloadParts = 0; + dh1.splitPayloadIndex = 0; + o2::header::DataHeader dh2{}; + dh2.splitPayloadParts = 1; + dh2.splitPayloadIndex = 0; + o2::header::DataHeader dh3{}; + dh3.splitPayloadParts = 2; + dh3.splitPayloadIndex = 2; + o2::framework::DataProcessingHeader dph{0, 1}; + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + fair::mq::MessagePtr payload(transport->CreateMessage()); + auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); + fair::mq::MessagePtr header1 = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh1, dph}); + fair::mq::MessagePtr header2 = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh2, dph}); + fair::mq::MessagePtr header3 = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh3, dph}); + 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)}; + std::unique_ptr msg3(nullptr); + PartRef ref{std::move(header1), std::move(msg2)}; o2::framework::MessageSet msgSet; msgSet.add(std::move(ref)); - PartRef ref2{std::move(msg), std::move(msg2)}; + PartRef ref2{std::move(header2), std::move(msg2)}; msgSet.add(std::move(ref2)); std::vector msgs; - msgs.push_back(std::unique_ptr(nullptr)); + msgs.push_back(std::move(header3)); 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); + }, + 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); + REQUIRE((msgSet.messages | count_payloads{}) == 4); + REQUIRE((msgSet.messages | get_dataref_indices{0, 0}).headerIdx == 0); + REQUIRE((msgSet.messages | get_dataref_indices{0, 0}).payloadIdx == 1); + REQUIRE((msgSet.messages | get_dataref_indices{1, 0}).headerIdx == 2); + REQUIRE((msgSet.messages | get_dataref_indices{1, 0}).payloadIdx == 3); + REQUIRE((msgSet.messages | get_dataref_indices{2, 0}).headerIdx == 4); + REQUIRE((msgSet.messages | get_dataref_indices{2, 0}).payloadIdx == 5); + REQUIRE((msgSet.messages | get_dataref_indices{2, 1}).headerIdx == 4); + REQUIRE((msgSet.messages | get_dataref_indices{2, 1}).payloadIdx == 6); + REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); + REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); + REQUIRE((msgSet.messages | get_pair{1}).headerIdx == 2); + REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 3); + REQUIRE((msgSet.messages | get_pair{2}).headerIdx == 4); + REQUIRE((msgSet.messages | get_pair{2}).payloadIdx == 5); + REQUIRE((msgSet.messages | get_pair{3}).headerIdx == 4); + REQUIRE((msgSet.messages | get_pair{3}).payloadIdx == 6); } From 45753477d82cfe8509cdeb2d45e7d511f789e20d Mon Sep 17 00:00:00 2001 From: Marco van Leeuwen Date: Sun, 22 Mar 2026 07:10:04 +0100 Subject: [PATCH 397/701] [ALICE 3] Properly set FT3 sensitive volumes; improve tiling (#15201) * [ALICE 3] Properly set FT3 sensitive volumes; improve tiling of v3 geometry (Rin = 30 cm) * Please consider the following formatting changes --------- Co-authored-by: ALICE Action Bot --- .../ALICE3/FT3/simulation/src/Detector.cxx | 9 +++- .../ALICE3/FT3/simulation/src/FT3Module.cxx | 52 +++++++++++++++---- 2 files changed, 48 insertions(+), 13 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx index 02aae95daacfe..94d56fd9625a0 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx @@ -630,6 +630,7 @@ void Detector::defineSensitiveVolumes() for (int iLayer = 0; iLayer < getNumberOfLayers(); iLayer++) { LOG(info) << "Adding FT3 Sensitive Volume for direction " << direction << " layer " << iLayer << "/" << getNumberOfLayers(); volumeName = o2::ft3::GeometryTGeo::getFT3SensorPattern() + std::to_string(iLayer); + int iSens = 0; if (mLayers[direction][iLayer].getIsInMiddleLayer()) { // ML disks const std::string sensorName = Form("%s_%d_%d", GeometryTGeo::getFT3SensorPattern(), direction, iLayer); v = geoManager->GetVolume(sensorName.c_str()); @@ -638,20 +639,24 @@ void Detector::defineSensitiveVolumes() LOG(fatal) << "Could not find volume " << sensorName << " for direction " << direction << " layer " << iLayer; } AddSensitiveVolume(v); + iSens++; } 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); + 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); + iSens++; } v = geoManager->GetVolume(sensor_name_back.c_str()); if (v) { AddSensitiveVolume(v); + iSens++; } } } + LOG(info) << iSens << " sensitive volumes added"; } } } diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx index 20a481cb36046..99322aa91f53f 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx @@ -140,6 +140,9 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double double bottom_y_pos_value = 0; double bottom_y_neg_value = 0; + double Rin_offset = (sensor_height == 19.2) ? 1 : 0; + double Rout_offset = (sensor_height == 19.2) ? 1 : 0; + if (Rin == 7 && sensor_height == 9.6 && sensor_width == 5) { x_condition_min = -Rin - 2; x_condition_max = Rin; @@ -198,17 +201,23 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double x_adjust_bottom_y_pos = 5.5; bottom_y_pos_value = 3.5; bottom_y_neg_value = -3.5; + } else if (Rin == 20 && sensor_height == 9.6 && sensor_width == 5.0) { + x_condition_min = -Rin - 4; + x_condition_max = Rin; + dist_offset = 2; + adjust_bottom_y_pos = false; + adjust_bottom_y_neg = false; + x_adjust_bottom_y_pos = 3.5; + bottom_y_pos_value = 3.5; + bottom_y_neg_value = -3.5; } else { LOG(warning) << "Different config - to determine offsets needed for " << "Rin = " << Rin << " ; sensor_height = " << sensor_height << " ; sensor_width = " << sensor_width << " layer " << layerNumber; - x_condition_min = -Rin; + x_condition_min = -Rin - sensor_width; 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; @@ -235,13 +244,34 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double 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 (Rin == 20) { // v3 paving, rough attempt + float overlap = 0.3; + // NB: these are left edges + float X_start = -2.0 - 13.5 * (sensor_width - overlap); + float X_start_pos = 2.0 - 0.5 * (sensor_width - overlap); + if (face == "back") { + X_start += (sensor_width - overlap); + X_start_pos += (sensor_width - overlap); + } + while (X_start < -2) { + X_positions.push_back(X_start); + justSkipped1.push_back(1); + X_start += 2 * (sensor_width - overlap); + } + while (X_start_pos < Rout + x_offset - sensor_width) { + X_positions.push_back(X_start_pos); + justSkipped1.push_back(1); + X_start_pos += 2 * (sensor_width - overlap); + } + } 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}; + } } } From cfb2c3a6559bc8b66d7d3047c1a31041c16557af Mon Sep 17 00:00:00 2001 From: AizatDaribayeva Date: Sun, 22 Mar 2026 11:24:11 +0100 Subject: [PATCH 398/701] [ALICE3] Adding error msg for TGeo features and QA macro for reco (#15183) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Adding error msg for TGeo features and QA macro for reco * Please consider the following formatting changes * Fix range setting for hSigmaVsPt histogram --------- Co-authored-by: ALICE Action Bot Co-authored-by: Nicolò Jacazio --- .../TRK/base/include/TRKBase/GeometryTGeo.h | 14 +- .../ALICE3/TRK/macros/test/CheckTracksCA.C | 577 +++++++++++++----- 2 files changed, 450 insertions(+), 141 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h index 21d86378f59ec..e32a2546c6842 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h @@ -106,17 +106,23 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache float getSensorRefAlphaMLOT(int chipId) const { - assert(getSubDetID(chipId) != 0 && "Called MLOT getter with VD chipId"); + if (getSubDetID(chipId) == 0) { + LOG(error) << "getSensorRefAlphaMLOT(): VD layers are not supported yet! chipID = " << chipId + << "please provide chipId for ML/OT! "; + return std::numeric_limits::quiet_NaN(); + } const int local = chipId - getNumberOfActivePartsVD(); - assert(local >= 0 && local < (int)mCacheRefAlphaMLOT.size()); return mCacheRefAlphaMLOT[local]; } float getSensorXMLOT(int chipId) const { - assert(getSubDetID(chipId) != 0 && "Called MLOT getter with VD chipId"); + if (getSubDetID(chipId) == 0) { + LOG(error) << "getSensorXMLOT(): VD layers are not supported yet! chipID = " << chipId + << "please provide chipId for ML/OT! "; + return std::numeric_limits::quiet_NaN(); + } const int local = chipId - getNumberOfActivePartsVD(); - assert(local >= 0 && local < (int)mCacheRefXMLOT.size()); return mCacheRefXMLOT[local]; } diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckTracksCA.C b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckTracksCA.C index ae75616b7719c..f7917ca4203f1 100644 --- a/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckTracksCA.C +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckTracksCA.C @@ -9,8 +9,8 @@ // 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 +/// \author A. Daribayeva +/// Quality assurance test on reconstructed tracks, producing efficiency plots and performance table #if !defined(__CLING__) || defined(__ROOTCLING__) #include @@ -18,15 +18,24 @@ #include #include #include +#include +#include +#include #include #include -#include +#include +#include #include #include #include #include #include +#include +#include +#include +#include +#include #include "DataFormatsITS/TrackITS.h" #include "SimulationDataFormat/MCCompLabel.h" @@ -41,6 +50,18 @@ using namespace std; using namespace o2; +enum class RangeMode { + ContentOnly, + ContentOrError, + ReferenceContent +}; + +void setAutoXRange(TH1* h, + RangeMode mode = RangeMode::ContentOnly, + const TH1* hRef = nullptr, + double threshold = 0.0, + int marginBins = 1); + /// Structure to track particle hit information struct ParticleHitInfo { std::bitset<11> layerHits; ///< Which layers have hits (11 layers for TRK) @@ -73,25 +94,37 @@ struct ParticleHitInfo { } }; -void CheckTracksCA(std::string tracfile = "o2trac_trk.root", +bool hasConsecutiveLayers(const o2::its::TrackITS& recoTrack, int nClusters) +{ + std::array layers{}; + + for (int i = 0; i < 11; i++) { + layers[i] = recoTrack.hasHitOnLayer(i); + } + + return std::search_n(layers.begin(), layers.end(), nClusters, true) != layers.end(); +} + +void CheckTracksCA(std::string trackfile = "o2trac_trk.root", std::string kinefile = "o2sim_Kine.root", std::string hitsfile = "o2sim_HitsTRK.root", - std::string outputfile = "trk_qa_output.root") + std::string outFile = "RecoPerformanceTable.dat", + std::string outFile1 = "RecoTracksQA.root") { - gStyle->SetOptStat(0); - std::cout << "=== Starting TRK Track Quality Assurance ===" << std::endl; + std::cout << "=== Starting TRK Track Reconstruction Quality Assurance ===" << std::endl; std::cout << "Input files:" << std::endl; - std::cout << " Tracks: " << tracfile << std::endl; + std::cout << " Tracks: " << trackfile << std::endl; std::cout << " Kinematics: " << kinefile << std::endl; std::cout << " Hits: " << hitsfile << std::endl; - std::cout << " Output: " << outputfile << std::endl; - std::cout << std::endl; + std::cout << " Output file with performance table: " << outFile << std::endl; + std::cout << " Output root file with histograms: " << outFile1 << std::endl; + + gROOT->SetBatch(true); // 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"); @@ -99,6 +132,7 @@ void CheckTracksCA(std::string tracfile = "o2trac_trk.root", 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; @@ -106,25 +140,19 @@ void CheckTracksCA(std::string tracfile = "o2trac_trk.root", } // Open reconstructed tracks file - TFile* tracFile = TFile::Open(tracfile.c_str(), "READ"); + TFile* tracFile = TFile::Open(trackfile.c_str(), "READ"); if (!tracFile || tracFile->IsZombie()) { - std::cerr << "ERROR: Cannot open tracks file: " << tracfile << std::endl; + std::cerr << "ERROR: Cannot open tracks file: " << trackfile << 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; - + // ============== MC part =============================== // Analyze hits tree to count hits per particle per layer std::cout << "Analyzing hits from tree..." << std::endl; std::unordered_map particleHitMap; @@ -133,9 +161,6 @@ void CheckTracksCA(std::string tracfile = "o2trac_trk.root", 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); @@ -152,8 +177,7 @@ void CheckTracksCA(std::string tracfile = "o2trac_trk.root", } // Determine layer - int subDetID = gman->getSubDetID(hit.GetDetectorID()); - const int layer = startLayer[subDetID] + gman->getLayer(hit.GetDetectorID()); + const int layer = gman->getBarrelLayer(hit.GetDetectorID()); // Create label for this particle o2::MCCompLabel label(hit.GetTrackID(), static_cast(iEntry), 0); @@ -165,81 +189,132 @@ void CheckTracksCA(std::string tracfile = "o2trac_trk.root", 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); - } + //=========== need to set the min and max ranges for hists + std::vector pTDist; + std::unordered_map MCTrackMap; - TH1D numberOfClustersPerTrack("numberOfClustersPerTrack", - "Number of clusters per track; N_{clusters}; Counts", - 12, -0.5, 11.5); + // counters for general statisics + int counterPrimaries{0}, counterSecondaries{0}; - // 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); + if (mcTrack.isPrimary()) { + counterPrimaries++; } - - // Fill histogram for particles with at least 7 consecutive layer hits - if (hitIt->second.hasConsecutiveLayers(7)) { - genParticlePt7LayersHist.Fill(pt); + if (mcTrack.isSecondary()) { + counterSecondaries++; } + + MCTrackMap.emplace(label, mcTrack); + pTDist.push_back(mcTrack.GetPt()); + } + } + } + + int nBins = 100; + auto [minpT, maxpT] = std::minmax_element(pTDist.begin(), pTDist.end()); + + //=========== histograms ============= + // for exclusive studies + TH1F hPtDenExclusive("", "", nBins, *minpT, *maxpT); + + TH1F hPtNumExclusive("", "", nBins, *minpT, *maxpT); + + TH1F hPtEffExclusive("hPtEffExclusive", + "efficiency (exclusive, good, primaries) vs p_{T}; p_{T} [GeV/c]; Efficiency", + nBins, *minpT, *maxpT); + + // for inclusive studies + TH1F hPtDenInclusive("", "", nBins, *minpT, *maxpT); + + TH1F hPtNumInclusive("", "", nBins, *minpT, *maxpT); + + TH1F hPtEffInclusive("hPtEffInclusive", "", nBins, *minpT, *maxpT); + + // for inclusive studies, fake + TH1F hPtNumInclusiveFake("", "", nBins, *minpT, *maxpT); + TH1F hPtEffInclusiveFake("", "", nBins, *minpT, *maxpT); + + TH2D hPtResVsPt("", "", nBins, *minpT, *maxpT, 100, -0.5, 0.5); + + // for inclusive efficiencies + int counterAll{0}, prim_ge7{0}, sec_ge7{0}; + + // for exclusive studies when we have 7,8,9,10,11 hits + std::array mcExact{}; + + for (const auto& [label, mcTrack] : MCTrackMap) { + + const auto& hitInfo = particleHitMap.at(label); + int nHits = hitInfo.nHits; + + if (nHits < 7 || nHits > 11) { + continue; + } + + float pT = mcTrack.GetPt(); + + bool consecutive7 = hitInfo.hasConsecutiveLayers(7); + + if (mcTrack.isPrimary()) { + + // exclusive - all hits should be on subsequent layers + if (hitInfo.hasConsecutiveLayers(nHits)) { + hPtDenExclusive.Fill(pT); + ++mcExact[nHits]; + } + + // inclusive - it's enough to be on 7 consequtive layers + if (consecutive7) { + hPtDenInclusive.Fill(pT); + ++prim_ge7; + } + + } else if (mcTrack.isSecondary()) { + + if (consecutive7) { + ++sec_ge7; } } } - std::cout << "Generated particles with 11 hits: " << genParticlePtHist.GetEntries() << std::endl; - std::cout << "Generated particles with 7+ consecutive hits: " << genParticlePt7LayersHist.GetEntries() << std::endl; + counterAll = prim_ge7 + sec_ge7; + + //============ reco tracks =============== + + // Reconstructed tracks and labels + std::vector* recTracks = nullptr; + std::vector* trkLabels = nullptr; + std::vector pTResVector; // good, primaries, inclusive + + recTree->SetBranchAddress("TRKTrack", &recTracks); + recTree->SetBranchAddress("TRKTrackMCTruth", &trkLabels); // 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; + int totalTracks{0}; + + // inclusive count + std::unordered_set foundAllGood, foundAllFake; + std::unordered_set foundPrimGood, foundPrimFake; + std::unordered_set foundSecGood, foundSecFake; + + // exclusive count + std::array, 12> foundExclusiveGood, foundExclusiveFake; + std::array, 12> foundWithLessClusters; + + int count7RecoGood{0}; for (int iROF = 0; iROF < nROFs; ++iROF) { recTree->GetEntry(iROF); @@ -258,88 +333,316 @@ void CheckTracksCA(std::string tracfile = "o2trac_trk.root", continue; } - int eventID = label.getEventID(); - int trackID = label.getTrackID(); int nClusters = track.getNumberOfClusters(); + if (nClusters < 7 || nClusters > 11) { + continue; + } + + auto key = o2::MCCompLabel(label.getTrackID(), label.getEventID(), 0); + + auto hitIt = particleHitMap.find(key); + auto mcIt = MCTrackMap.find(key); - // Get MC track info - if (eventID < 0 || eventID >= nEvents) { + if (hitIt == particleHitMap.end() || mcIt == MCTrackMap.end()) { continue; } - const auto& mcTracks = kineReader.getTracks(eventID); - if (trackID < 0 || trackID >= (int)mcTracks.size()) { + int nHits = hitIt->second.nHits; + if (nHits < 7 || nHits > 11) { continue; } - float pt = mcTracks[trackID].GetPt(); + bool mcHasN = hitIt->second.hasConsecutiveLayers(nHits); + bool recoHasN = hasConsecutiveLayers(track, nClusters); - // Fill histograms - numberOfClustersPerTrack.Fill(nClusters); + float mcPt = mcIt->second.GetPt(); + float recoPT = track.getPt(); - auto key = o2::MCCompLabel(trackID, eventID, 0); - if (particleHitMap.find(key) != particleHitMap.end() && particleHitMap[key].hasConsecutiveLayers(11)) { + // inclusive count + if (hitIt->second.hasConsecutiveLayers(7) && hasConsecutiveLayers(track, 7)) { + + // for good tracks + if (label.isCorrect()) { + foundAllGood.insert(key); + + if (mcIt->second.isPrimary()) { + foundPrimGood.insert(key); + hPtNumInclusive.Fill(mcPt); + + float ptRes = (recoPT - mcPt) / mcPt; + pTResVector.push_back(ptRes); + hPtResVsPt.Fill(mcPt, ptRes); + + } else if (mcIt->second.isSecondary()) { + foundSecGood.insert(key); + } + } + + // for fake tracks if (label.isFake()) { - fakeTracks.Fill(pt); - fakeTracksCount++; - if (nClusters >= 7 && nClusters <= 11) { - fakeTracksMatching[nClusters - 7].Fill(pt); + foundAllFake.insert(key); + + if (mcIt->second.isPrimary()) { + foundPrimFake.insert(key); + hPtNumInclusiveFake.Fill(mcPt); + } else if (mcIt->second.isSecondary()) { + foundSecFake.insert(key); } - } else { - goodTracks.Fill(pt); - goodTracksCount++; - if (nClusters >= 7 && nClusters <= 11) { - goodTracksMatching[nClusters - 7].Fill(pt); + } + } + + // exclusive count + if (nHits == nClusters && mcHasN && recoHasN) { + + if (mcIt->second.isPrimary()) { + + if (label.isCorrect()) { + + hPtNumExclusive.Fill(mcPt); + foundExclusiveGood[nHits].insert(key); + } + + if (label.isFake()) { + foundExclusiveFake[nHits].insert(key); } } } + + // counting cluster loss + if (mcIt->second.isPrimary() && mcHasN && recoHasN && + label.isCorrect() && + nClusters < nHits) { + + foundWithLessClusters[nHits].insert(key); + } + + } // end loop over reco tracks + } // end loop over RoFs + + // inclusive efficiencies for Good tracks + float effForAllGood = counterAll > 0 ? 100.f * foundAllGood.size() / counterAll : 0.f; + float effForPrimGood = prim_ge7 > 0 ? 100.f * foundPrimGood.size() / prim_ge7 : 0.f; + float effForSecGood = sec_ge7 > 0 ? 100.f * foundSecGood.size() / sec_ge7 : 0.f; + + // inclusive efficiencies for Fake tracks + float effForAllFake = counterAll > 0 ? 100.f * foundAllFake.size() / counterAll : 0.f; + float effForPrimFake = prim_ge7 > 0 ? 100.f * foundPrimFake.size() / prim_ge7 : 0.f; + float effForSecFake = sec_ge7 > 0 ? 100.f * foundSecFake.size() / sec_ge7 : 0.f; + + // exclusive efficiencies for Good and Fake tracks + std::array effExactAllGood{}, effExactAllFake{}; + + for (int n = 7; n <= 11; ++n) { + effExactAllGood[n] = mcExact[n] > 0 ? 100.f * foundExclusiveGood[n].size() / mcExact[n] : 0.f; + effExactAllFake[n] = mcExact[n] > 0 ? 100.f * foundExclusiveFake[n].size() / mcExact[n] : 0.f; + } + + // cluster loss + std::array fracWithLessClusters{}; + for (int n = 7; n <= 11; ++n) { + fracWithLessClusters[n] = mcExact[n] > 0 ? 100.f * foundWithLessClusters[n].size() / mcExact[n] : 0.f; + } + + // pT vs inclusive & exclusive track efficiencies + hPtEffExclusive.Divide(&hPtNumExclusive, &hPtDenExclusive, 1.0, 1.0, "B"); + hPtEffInclusive.Divide(&hPtNumInclusive, &hPtDenInclusive, 1.0, 1.0, "B"); + hPtEffInclusiveFake.Divide(&hPtNumInclusiveFake, &hPtDenInclusive, 1.0, 1.0, "B"); + + // pT resolution for good inclusive tracks, primaries + auto [minPtRes, maxPtRes] = std::minmax_element(pTResVector.begin(), pTResVector.end()); + TH1F pTResolution("pTResolutionForInclusive", "p_{T} resolution; (p_{T}^{rec}-p_{T}^{MC})/p_{T}^{MC}; Counts", nBins, *minPtRes, *maxPtRes); + for (const auto& pTVal : pTResVector) { + pTResolution.Fill(pTVal); + } + pTResolution.Fit("gaus"); + + TObjArray fitSlices; + hPtResVsPt.FitSlicesY(nullptr, 0, -1, 0, "QNR", &fitSlices); + + TH1D* hSigmaVsPt = nullptr; + + if (fitSlices.GetEntries() > 2 && fitSlices.At(2)) { + hSigmaVsPt = dynamic_cast(fitSlices.At(2)->Clone("hSigmaVsPt")); + if (hSigmaVsPt) { + hSigmaVsPt->SetTitle("#sigma(p_{T} resolution) vs p_{T}; p_{T}^{MC} [GeV/c]; #sigma"); + hSigmaVsPt->GetXaxis()->SetRangeUser(0.5, *maxpT); } } - // Create efficiency histograms - std::cout << "Computing efficiencies..." << std::endl; + // Style + hPtEffInclusive.SetLineColor(kBlue + 1); + hPtEffInclusive.SetMarkerColor(kBlue + 1); + hPtEffInclusive.SetMarkerStyle(20); + hPtEffInclusive.SetMarkerSize(1.0); + hPtEffInclusive.SetLineWidth(2); + + hPtEffInclusiveFake.SetLineColor(kRed + 1); + hPtEffInclusiveFake.SetMarkerColor(kRed + 1); + hPtEffInclusiveFake.SetMarkerStyle(24); + hPtEffInclusiveFake.SetMarkerSize(1.0); + hPtEffInclusiveFake.SetLineWidth(2); + + // Titles and axis labels + hPtEffInclusive.SetTitle("Inclusive tracking performance vs p_{T}"); + hPtEffInclusive.GetXaxis()->SetTitle("p_{T} [GeV/c]"); + hPtEffInclusive.GetYaxis()->SetTitle("Rate"); + + // Canvas + TCanvas* cPtEff = new TCanvas("", "", 900, 700); + + setAutoXRange(&hPtEffInclusive, RangeMode::ReferenceContent, &hPtDenInclusive); + setAutoXRange(&hPtEffInclusiveFake, RangeMode::ReferenceContent, &hPtDenInclusive); + + hPtEffInclusive.Draw("E1"); + hPtEffInclusiveFake.Draw("E1 SAME"); + + // Legend + TLegend* leg = new TLegend(0.60, 0.15, 0.88, 0.35); + leg->SetBorderSize(0); + leg->SetFillStyle(0); + leg->AddEntry(&hPtEffInclusive, "Inclusive good efficiency", "lp"); + leg->AddEntry(&hPtEffInclusiveFake, "Inclusive fake rate", "lp"); + leg->Draw("E1 SAME"); + + setAutoXRange(&hPtEffExclusive, RangeMode::ContentOnly); + + // Writing to output Root file + std::cout << "Writing histograms to " << outFile1 << std::endl; + TFile outFileRoot(outFile1.c_str(), "RECREATE"); + if (hSigmaVsPt) { + hSigmaVsPt->Write(); + } + hPtEffExclusive.Write(); + hPtEffInclusive.Write(); + cPtEff->Write(); + pTResolution.Write(); + outFileRoot.Close(); + + // Building performance table + std::cout << "Building performance table ... " << std::endl; + std::ofstream outFileTxt(outFile.c_str()); + outFileTxt << std::fixed << std::setprecision(2); + + outFileTxt << "This is preliminary reconstruction performance table !!" << std::endl; + outFileTxt << "\nGenerated " << particleHitMap.size() << " unique particles with hits" << std::endl; + outFileTxt << "Among them, N primaries: " << counterPrimaries << " and secondaries: " << counterSecondaries << std::endl; + outFileTxt << "Number of total reconstructed tracks: " << totalTracks << std::endl; + + outFileTxt << "\nReconstruction performance table\n\n"; + + outFileTxt << "| " + << std::left << std::setw(20) << "Track category" + << "| " << std::setw(14) << "Efficiency (%)" + << "| " << std::setw(14) << "Fake rate (%)" + << "| " << std::setw(12) << "MC counts" + << " |\n"; + + outFileTxt << std::string(70, '-') << "\n"; + + outFileTxt << "| " + << std::left << std::setw(20) << "All (prim+sec)" + << "| " << std::setw(14) << effForAllGood + << "| " << std::setw(14) << effForAllFake + << "| " << std::setw(12) << counterAll + << " |\n"; + + outFileTxt << "| " + << std::left << std::setw(20) << "Primaries" + << "| " << std::setw(14) << effForPrimGood + << "| " << std::setw(14) << effForPrimFake + << "| " << std::setw(12) << prim_ge7 + << " |\n"; + + outFileTxt << "| " + << std::left << std::setw(20) << "Secondaries" + << "| " << std::setw(14) << effForSecGood + << "| " << std::setw(14) << effForSecFake + << "| " << std::setw(12) << sec_ge7 + << " |\n"; + + outFileTxt << "\n\nExclusive efficiencies for primaries:\n\n"; + + outFileTxt << "| " + << std::left << std::setw(15) << "Track length" + << "| " << std::setw(14) << "Efficiency (%)" + << "| " << std::setw(14) << "Fake rate (%)" + << "| " << std::setw(14) << "Cluster loss (%)" + << "| " << std::setw(14) << "MC counts" + << " |\n"; + + outFileTxt << std::string(85, '-') << "\n"; + + for (int n = 11; n >= 7; --n) { + outFileTxt << "| " + << std::left << std::setw(15) << (std::to_string(n) + "-hit") + << "| " << std::setw(14) << effExactAllGood[n] + << "| " << std::setw(14) << effExactAllFake[n] + << "| " << std::setw(16) << fracWithLessClusters[n] + << "| " << std::setw(14) << mcExact[n] + << " |\n"; + } + + std::cout << "Analysis complete!" << std::endl; - std::array efficiencyHistograms; - THStack* efficiencyStack = new THStack("efficiencyStack", - "Tracking Efficiency; #it{p}_{T} (GeV/#it{c}); Efficiency"); +} // end of macro - 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); +void setAutoXRange(TH1* h, RangeMode mode, + const TH1* hRef, + double threshold, + int marginBins) +{ + if (!h) + return; - efficiencyHistograms[i].Divide(&goodTracksMatching[i], &genParticlePtHist, 1, 1, "B"); + const TH1* hScan = h; - 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]); + if (mode == RangeMode::ReferenceContent) { + if (!hRef) + return; + hScan = hRef; } - // 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(); + const int nBins = hScan->GetNbinsX(); + int first = -1; + int last = -1; + + auto isUsefulBin = [&](int i) -> bool { + const double content = hScan->GetBinContent(i); + const double error = hScan->GetBinError(i); + + switch (mode) { + case RangeMode::ContentOnly: + return content > threshold; + + case RangeMode::ContentOrError: + return (content > threshold) || (error > 0.0); + + case RangeMode::ReferenceContent: + return content > threshold; + } + return false; + }; + + for (int i = 1; i <= nBins; ++i) { + if (isUsefulBin(i)) { + first = i; + break; + } } - efficiencyStack->Write(); - genParticlePt7LayersHist.Write(); - numberOfClustersPerTrack.Write(); - outFile.Close(); - - // Clean up - hitsFile->Close(); - tracFile->Close(); - delete efficiencyStack; - delete hitsFile; - delete tracFile; + + for (int i = nBins; i >= 1; --i) { + if (isUsefulBin(i)) { + last = i; + break; + } + } + + if (first == -1 || last == -1 || first > last) { + return; + } + + first = std::max(1, first - marginBins); + last = std::min(nBins, last + marginBins); + + h->GetXaxis()->SetRange(first, last); } From 35fc90d93321b4ea400a235e0c0b7d37b4d50afe Mon Sep 17 00:00:00 2001 From: Andrea Sofia Triolo Date: Mon, 23 Mar 2026 18:36:30 +0100 Subject: [PATCH 399/701] [ALICE3] TRK: changed ML/OT pitch to 20 um (#15203) --- Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h index c3c7de9dbe910..91d6f5669ef33 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/Specs.h @@ -79,8 +79,8 @@ namespace chip { constexpr double width{25 * mm}; // width of the chip constexpr double length{32 * mm}; // length of the chip -constexpr double pitchX{50 * mu}; // pitch of the row -constexpr double pitchZ{50 * mu}; // pitch of the column +constexpr double pitchX{20 * mu}; // pitch of the row +constexpr double pitchZ{20 * 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 @@ -138,7 +138,6 @@ 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 From 056b5f4571a3c039e3ccae8cf9c7db19f74230f9 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Tue, 24 Mar 2026 08:48:11 +0100 Subject: [PATCH 400/701] DPL: get rid of the size method of the MessageSet (#15206) One more step in getting rid of the artificial container "MessageSet". By removing the size method, we imply that any sequence of messages can have their number of parts computed, regardless of how we store them and how the ownership of such parts works. --- Framework/Core/include/Framework/MessageSet.h | 6 ----- Framework/Core/src/DataRelayer.cxx | 24 +++++++++---------- Framework/Core/test/benchmark_DataRelayer.cxx | 8 +++---- Framework/Core/test/test_DataRelayer.cxx | 12 +++++----- Framework/Core/test/test_ForwardInputs.cxx | 24 +++++++++---------- 5 files changed, 34 insertions(+), 40 deletions(-) diff --git a/Framework/Core/include/Framework/MessageSet.h b/Framework/Core/include/Framework/MessageSet.h index 281f9c42a0773..166934238d647 100644 --- a/Framework/Core/include/Framework/MessageSet.h +++ b/Framework/Core/include/Framework/MessageSet.h @@ -82,12 +82,6 @@ struct MessageSet { return *this; } - /// get number of in-flight O2 messages - [[nodiscard]] size_t size() const - { - return messages | count_parts{}; - } - /// get number of header-payload pairs [[nodiscard]] size_t getNumberOfPairs() const { diff --git a/Framework/Core/src/DataRelayer.cxx b/Framework/Core/src/DataRelayer.cxx index cece5b343659f..5b85a63bf6c95 100644 --- a/Framework/Core/src/DataRelayer.cxx +++ b/Framework/Core/src/DataRelayer.cxx @@ -184,11 +184,11 @@ DataRelayer::ActivityStats DataRelayer::processDanglingInputs(std::vector 0 && part.header(0) != nullptr) { + if (!part.messages.empty() && part.header(0) != nullptr) { headerPresent++; continue; } - if (part.size() > 0 && part.payload(0) != nullptr) { + if (!part.messages.empty() && part.payload(0) != nullptr) { payloadPresent++; continue; } @@ -213,7 +213,7 @@ DataRelayer::ActivityStats DataRelayer::processDanglingInputs(std::vector 0 && partial[idx].header(part).get()) { + if (!partial[idx].messages.empty() && partial[idx].header(part).get()) { auto header = partial[idx].header(part).get(); auto payload = partial[idx].payload(part).get(); return DataRef{nullptr, @@ -224,7 +224,7 @@ DataRelayer::ActivityStats DataRelayer::processDanglingInputs(std::vector int { auto& header = static_cast(*partial[idx].header(0)); @@ -327,7 +327,7 @@ void DataRelayer::setOldestPossibleInput(TimesliceId proposed, ChannelIndex chan for (size_t mi = 0; mi < mInputs.size(); ++mi) { auto& input = mInputs[mi]; auto& element = mCache[si * mInputs.size() + mi]; - if (element.size() != 0) { + if (!element.messages.empty()) { if (input.lifetime != Lifetime::Condition && mCompletionPolicy.name != "internal-dpl-injected-dummy-sink") { didDrop = true; auto& state = mContext.get(); @@ -353,7 +353,7 @@ void DataRelayer::setOldestPossibleInput(TimesliceId proposed, ChannelIndex chan continue; } auto& element = mCache[si * mInputs.size() + mi]; - if (element.size() == 0) { + if (element.messages.empty()) { auto& state = mContext.get(); if (state.transitionHandling != TransitionHandlingState::NoTransition && DefaultsHelpers::onlineDeploymentMode()) { if (state.allowedProcessing == DeviceState::CalibrationOnly) { @@ -411,11 +411,11 @@ void DataRelayer::pruneCache(TimesliceSlot slot, OnDropCallback onDrop) cachedStateMetrics[cacheId] = CacheEntryStatus::RUNNING; // TODO: in the original implementation of the cache, there have been only two messages per entry, // check if the 2 above corresponds to the number of messages. - if (cache[cacheId].size() > 0) { + if (!cache[cacheId].messages.empty()) { dropped[ai] = std::move(cache[cacheId]); } } - bool anyDropped = std::any_of(dropped.begin(), dropped.end(), [](auto& m) { return m.size(); }); + bool anyDropped = std::any_of(dropped.begin(), dropped.end(), [](auto& m) { return !m.messages.empty(); }); if (anyDropped) { O2_SIGNPOST_ID_GENERATE(aid, data_relayer); O2_SIGNPOST_EVENT_EMIT(data_relayer, aid, "pruneCache", "Dropping stuff from slot %zu with timeslice %zu", slot.index, oldestPossibleTimeslice.timeslice.value); @@ -786,7 +786,7 @@ void DataRelayer::getReadyToProcess(std::vector& comp auto partial = getPartialRecord(li); // TODO: get the data ref from message model auto getter = [&partial](size_t idx, size_t part) { - if (partial[idx].size() > 0 && partial[idx].header(part).get()) { + if (!partial[idx].messages.empty() && partial[idx].header(part).get()) { auto header = partial[idx].header(part).get(); auto payload = partial[idx].payload(part).get(); return DataRef{nullptr, @@ -797,7 +797,7 @@ void DataRelayer::getReadyToProcess(std::vector& comp return DataRef{}; }; auto nPartsGetter = [&partial](size_t idx) { - return partial[idx].size(); + return partial[idx].messages | count_parts{}; }; auto refCountGetter = [&partial](size_t idx) -> int { auto& header = static_cast(*partial[idx].header(0)); @@ -897,7 +897,7 @@ std::vector DataRelayer::consumeAllInputsForTimeslice cachedStateMetrics[cacheId] = CacheEntryStatus::RUNNING; // TODO: in the original implementation of the cache, there have been only two messages per entry, // check if the 2 above corresponds to the number of messages. - if (cache[cacheId].size() > 0) { + if (!cache[cacheId].messages.empty()) { messages[arg] = std::move(cache[cacheId]); } index.markAsInvalid(s); @@ -951,7 +951,7 @@ std::vector DataRelayer::consumeExistingInputsForTime cachedStateMetrics[cacheId] = CacheEntryStatus::RUNNING; // TODO: in the original implementation of the cache, there have been only two messages per entry, // check if the 2 above corresponds to the number of messages. - for (size_t pi = 0; pi < cache[cacheId].size(); pi++) { + for (size_t pi = 0; pi < (cache[cacheId].messages | count_parts{}); pi++) { auto& header = cache[cacheId].header(pi); auto&& newHeader = header->GetTransport()->CreateMessage(); newHeader->Copy(*header); diff --git a/Framework/Core/test/benchmark_DataRelayer.cxx b/Framework/Core/test/benchmark_DataRelayer.cxx index 3c3d2294fdd7e..e983f3604cfab 100644 --- a/Framework/Core/test/benchmark_DataRelayer.cxx +++ b/Framework/Core/test/benchmark_DataRelayer.cxx @@ -96,7 +96,7 @@ static void BM_RelaySingleSlot(benchmark::State& state) assert(ready[0].op == CompletionPolicy::CompletionOp::Consume); auto result = relayer.consumeAllInputsForTimeslice(ready[0].slot); assert(result.size() == 1); - assert(result.at(0).size() == 1); + assert((result.at(0).messages | count_parts{}) == 1); inflightMessages = std::move(result[0].messages); } } @@ -153,7 +153,7 @@ static void BM_RelayMultipleSlots(benchmark::State& state) assert(ready[0].op == CompletionPolicy::CompletionOp::Consume); auto result = relayer.consumeAllInputsForTimeslice(ready[0].slot); assert(result.size() == 1); - assert(result.at(0).size() == 1); + assert((result.at(0).messages | count_parts{}) == 1); inflightMessages = std::move(result[0].messages); } } @@ -228,8 +228,8 @@ static void BM_RelayMultipleRoutes(benchmark::State& state) assert(ready[0].op == CompletionPolicy::CompletionOp::Consume); auto result = relayer.consumeAllInputsForTimeslice(ready[0].slot); assert(result.size() == 2); - assert(result.at(0).size() == 1); - assert(result.at(1).size() == 1); + assert((result.at(0).messages | count_parts{}) == 1); + assert((result.at(1).messages | count_parts{}) == 1); inflightMessages = std::move(result[0].messages); inflightMessages.emplace_back(std::move(result[1].messages[0])); inflightMessages.emplace_back(std::move(result[1].messages[1])); diff --git a/Framework/Core/test/test_DataRelayer.cxx b/Framework/Core/test/test_DataRelayer.cxx index 8957e361cb8a2..e5ca7c5d235e5 100644 --- a/Framework/Core/test/test_DataRelayer.cxx +++ b/Framework/Core/test/test_DataRelayer.cxx @@ -115,7 +115,7 @@ TEST_CASE("DataRelayer") auto result = relayer.consumeAllInputsForTimeslice(ready[0].slot); // one MessageSet with one PartRef with header and payload REQUIRE(result.size() == 1); - REQUIRE(result.at(0).size() == 1); + REQUIRE((result.at(0).messages | count_parts{}) == 1); } // @@ -165,7 +165,7 @@ TEST_CASE("DataRelayer") auto result = relayer.consumeAllInputsForTimeslice(ready[0].slot); // one MessageSet with one PartRef with header and payload REQUIRE(result.size() == 1); - REQUIRE(result.at(0).size() == 1); + REQUIRE((result.at(0).messages | count_parts{}) == 1); } // This test a more complicated set of inputs, and verifies that data is @@ -245,8 +245,8 @@ TEST_CASE("DataRelayer") auto result = relayer.consumeAllInputsForTimeslice(ready[0].slot); // two MessageSets, each with one PartRef REQUIRE(result.size() == 2); - REQUIRE(result.at(0).size() == 1); - REQUIRE(result.at(1).size() == 1); + REQUIRE((result.at(0).messages | count_parts{}) == 1); + REQUIRE((result.at(1).messages | count_parts{}) == 1); } // This test a more complicated set of inputs, and verifies that data is @@ -733,7 +733,7 @@ TEST_CASE("DataRelayer") // we have one input route and thus one message set containing pairs for all // payloads REQUIRE(messageSet.size() == 1); - REQUIRE(messageSet[0].size() == nSplitParts); + REQUIRE((messageSet[0].messages | count_parts{}) == nSplitParts); REQUIRE(messageSet[0].getNumberOfPayloads(0) == 1); } @@ -796,7 +796,7 @@ TEST_CASE("DataRelayer") // we have one input route REQUIRE(messageSet.size() == 1); // one message set containing number of added sequences of messages - REQUIRE(messageSet[0].size() == sequenceSize.size()); + REQUIRE((messageSet[0].messages | count_parts{}) == sequenceSize.size()); size_t counter = 0; for (auto seqid = 0; seqid < sequenceSize.size(); ++seqid) { REQUIRE(messageSet[0].getNumberOfPayloads(seqid) == sequenceSize[seqid]); diff --git a/Framework/Core/test/test_ForwardInputs.cxx b/Framework/Core/test/test_ForwardInputs.cxx index 7081d600080b1..e3031b7e72a69 100644 --- a/Framework/Core/test/test_ForwardInputs.cxx +++ b/Framework/Core/test/test_ForwardInputs.cxx @@ -92,7 +92,7 @@ TEST_CASE("ForwardInputsSingleMessageSingleRoute") 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); + REQUIRE((messageSet.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); @@ -143,7 +143,7 @@ TEST_CASE("ForwardInputsSingleMessageSingleRouteNoConsume") 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); + REQUIRE((messageSet.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, true); @@ -198,7 +198,7 @@ TEST_CASE("ForwardInputsSingleMessageSingleRouteAtEOS") 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); + REQUIRE((messageSet.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); @@ -256,7 +256,7 @@ TEST_CASE("ForwardInputsSingleMessageSingleRouteWithOldestPossible") 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); + REQUIRE((messageSet.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); @@ -321,7 +321,7 @@ TEST_CASE("ForwardInputsSingleMessageMultipleRoutes") 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); + REQUIRE((messageSet.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); @@ -384,7 +384,7 @@ TEST_CASE("ForwardInputsSingleMessageMultipleRoutesExternals") 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); + REQUIRE((messageSet.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); @@ -455,12 +455,12 @@ TEST_CASE("ForwardInputsMultiMessageMultipleRoutes") 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); + REQUIRE((messageSet1.messages | count_parts{}) == 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); + REQUIRE((messageSet2.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet1)); currentSetOfInputs.emplace_back(std::move(messageSet2)); REQUIRE(currentSetOfInputs.size() == 2); @@ -525,7 +525,7 @@ TEST_CASE("ForwardInputsSingleMessageMultipleRoutesOnlyOneMatches") 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); + REQUIRE((messageSet.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); @@ -607,7 +607,7 @@ TEST_CASE("ForwardInputsSplitPayload") PartRef part{std::move(header2), transport->CreateMessage()}; messageSet.add(std::move(part)); - REQUIRE(messageSet.size() == 2); + REQUIRE((messageSet.messages | count_parts{}) == 2); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); @@ -727,7 +727,7 @@ TEST_CASE("ForwardInputEOSSingleRoute") 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); + REQUIRE((messageSet.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); @@ -772,7 +772,7 @@ TEST_CASE("ForwardInputOldestPossibleSingleRoute") 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); + REQUIRE((messageSet.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); From 7cc1eed7a1ee3a3159259b23b5e1932dc7f6e5d6 Mon Sep 17 00:00:00 2001 From: shahoian Date: Mon, 23 Mar 2026 17:24:44 +0100 Subject: [PATCH 401/701] Fix using MeanVertex in the residuals monitoring workflow --- Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx b/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx index e6584a7055446..063edc65d7486 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx @@ -44,7 +44,7 @@ // 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.; +// ;pvertexer.useMeanVertexConstraint=false;pvertexer.meanVertexExtraErrSelection=0.2;pvertexer.iniScale2=100;pvertexer.acceptableScale2=10.; // In any case, it is better to pass ;pvertexer.useMeanVertexConstraint=false; namespace o2::checkresid @@ -152,6 +152,7 @@ void CheckResidSpec::updateTimeDependentParams(ProcessingContext& pc) } if (mMeanVertexUpdated) { mMeanVertexUpdated = false; + mVertexer.setMeanVertex(&mMeanVtx); mVertexer.initMeanVertexConstraint(); } bool updateMaps = false; From 80ca8b8f58e696b08c2865a958b3c84c99f7636d Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Tue, 24 Mar 2026 14:06:48 +0100 Subject: [PATCH 402/701] DPL: fix OutputSpec metadata ignored in workflow deserialization (#15204) --- .../Core/src/WorkflowSerializationHelpers.cxx | 5 +-- .../Core/test/test_WorkflowSerialization.cxx | 33 +++++++++++++++++++ 2 files changed, 36 insertions(+), 2 deletions(-) diff --git a/Framework/Core/src/WorkflowSerializationHelpers.cxx b/Framework/Core/src/WorkflowSerializationHelpers.cxx index 9624a2dfd0d3e..b824e8d0bb424 100644 --- a/Framework/Core/src/WorkflowSerializationHelpers.cxx +++ b/Framework/Core/src/WorkflowSerializationHelpers.cxx @@ -430,10 +430,11 @@ struct WorkflowImporter : public rapidjson::BaseReaderHandler, inputMatcherNodes.push_back(std::move(node)); } else if (in(State::IN_OUTPUT)) { if (outputHasSubSpec) { - dataProcessors.back().outputs.push_back(OutputSpec({binding}, origin, description, subspec, lifetime)); + dataProcessors.back().outputs.push_back(OutputSpec({binding}, origin, description, subspec, lifetime, outputOptions)); } else { - dataProcessors.back().outputs.push_back(OutputSpec({binding}, {origin, description}, lifetime)); + dataProcessors.back().outputs.push_back(OutputSpec({binding}, {origin, description}, lifetime, outputOptions)); } + outputOptions.clear(); outputHasSubSpec = false; } else if (in(State::IN_OPTION)) { std::unique_ptr opt{nullptr}; diff --git a/Framework/Core/test/test_WorkflowSerialization.cxx b/Framework/Core/test/test_WorkflowSerialization.cxx index 298956970713d..791c40c326733 100644 --- a/Framework/Core/test/test_WorkflowSerialization.cxx +++ b/Framework/Core/test/test_WorkflowSerialization.cxx @@ -120,3 +120,36 @@ TEST_CASE("TestVerifyWildcard") // also check if the conversion to ConcreteDataMatcher is working at import // REQUIRE(std::get_if(&w1[0].inputs[0].matcher) != nullptr);; } + +TEST_CASE("TestInputOutputSpecMetadata") +{ + WorkflowSpec wso{ + DataProcessorSpec{ + .name = "S1", + .outputs = {OutputSpec{OutputLabel{"o1"}, o2::header::DataOrigin{"TST"}, "OUTPUT1", 0, Lifetime::Timeframe, {{"param1", VariantType::Bool, true, ConfigParamSpec::HelpString{"\"\""}}, {"param2", VariantType::Bool, true, ConfigParamSpec::HelpString{"\"\""}}}}, + OutputSpec{OutputLabel{"o2"}, o2::header::DataOrigin{"TST"}, "OUTPUT2"}}}}; + + std::vector dataProcessorInfoOut{ + {.name = "S1", .executable = "test_Framework_test_SerializationWorkflow"}, + }; + + CommandInfo commandInfoOut{"o2-dpl-workflow -b"}; + + std::vector dataProcessorInfoIn{}; + CommandInfo commandInfoIn; + + std::ostringstream firstDump; + WorkflowSerializationHelpers::dump(firstDump, wso, dataProcessorInfoOut, commandInfoOut); + std::istringstream is; + is.str(firstDump.str()); + + WorkflowSpec wsi; + WorkflowSerializationHelpers::import(is, wsi, dataProcessorInfoIn, commandInfoIn); + + REQUIRE(wsi[0].outputs[0].metadata.size() == 2); + REQUIRE(wsi[0].outputs[1].metadata.size() == 0); + REQUIRE(wso[0].outputs[0].metadata.size() == wsi[0].outputs[0].metadata.size()); + REQUIRE(wso[0].outputs[1].metadata.size() == wsi[0].outputs[1].metadata.size()); + REQUIRE(wso[0].outputs[0].metadata[0] == wsi[0].outputs[0].metadata[0]); + REQUIRE(wso[0].outputs[0].metadata[1] == wsi[0].outputs[0].metadata[1]); +} From 4edb7a80d92b8c3119e31026ed2adfbfe56f5692 Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Tue, 24 Mar 2026 18:36:39 +0100 Subject: [PATCH 403/701] DPL Analysis: force index to only bind with tables of the same origin (#15177) --- Framework/Core/include/Framework/ASoA.h | 95 +++++++++++++++++++------ 1 file changed, 75 insertions(+), 20 deletions(-) diff --git a/Framework/Core/include/Framework/ASoA.h b/Framework/Core/include/Framework/ASoA.h index 475823b150d90..7a3307ae1a58c 100644 --- a/Framework/Core/include/Framework/ASoA.h +++ b/Framework/Core/include/Framework/ASoA.h @@ -1176,12 +1176,6 @@ struct TableIterator : IP, C... { return *this; } - template - void doSetCurrentIndex(framework::pack, TA* current) - { - (CL::setCurrent(current), ...); - } - template auto getCurrent() const { @@ -1202,7 +1196,18 @@ struct TableIterator : IP, C... { template void bindExternalIndices(TA*... current) { - (doSetCurrentIndex(external_index_columns_t{}, current), ...); + ([this](TA* cur, framework::pack) { + (CCs::setCurrent(cur), ...); + }(current, external_index_columns_t{}), + ...); + } + + template + void bindExternalIndex(TA* current) + { + [this](TA* cur, framework::pack) { + (CCs::setCurrent(cur), ...); + }(current, external_index_columns_t{}); } template @@ -1812,6 +1817,12 @@ consteval auto computeOriginals() return o2::soa::mergeOriginals(); } +template refs> +consteval auto commonOrigin() +{ + return (refs | std::ranges::views::filter([](TableRef const& r) { return (!(r.origin_hash == "DYN"_h || r.origin_hash == "IDX"_h)); })).front().origin_hash; +} + /// A Table class which observes an arrow::Table and provides /// It is templated on a set of Column / DynamicColumn types. template @@ -1823,7 +1834,10 @@ class Table using table_t = self_t; static constexpr const auto originals = computeOriginals(); - static constexpr const auto originalLabels = [] refs, size_t... Is>(std::index_sequence) { return std::array{o2::aod::label()...}; }.template operator()(std::make_index_sequence()); + static constexpr const auto originalLabels = [] refs, size_t... Is>(std::index_sequence) { + return std::array{o2::aod::label()...}; + }.template operator()(std::make_index_sequence()); + static constexpr const uint32_t binding_origin = commonOrigin(); template bindings> requires(ref.origin_hash == "CONC"_h) @@ -1836,10 +1850,10 @@ class Table requires(ref.origin_hash == "JOIN"_h) static consteval auto isIndexTargetOf() { - return std::find_if(self_t::originals.begin(), self_t::originals.end(), - [](TableRef const& r) { - return std::find(bindings.begin(), bindings.end(), r) != bindings.end(); - }) != self_t::originals.end(); + return std::ranges::find_if(self_t::originals, + [](TableRef const& r) { + return std::ranges::find(bindings, r) != bindings.end(); + }) != self_t::originals.end(); } template bindings> @@ -2179,7 +2193,18 @@ class Table template void bindExternalIndices(TA*... current) { - mBegin.bindExternalIndices(current...); + ([this](TA* cur) { + if constexpr (binding_origin == TA::binding_origin) { + mBegin.bindExternalIndex(cur); + } + }(current), + ...); + } + + template + void bindExternalIndex(TA* current) + { + mBegin.bindExternalIndex(current); // unchecked binding for the derived tables } template @@ -3395,6 +3420,18 @@ struct JoinFull : Table, D, o2::aod::Hash<"JOIN"_h>, Ts. } using base::bindExternalIndices; using base::bindInternalIndicesTo; + static constexpr const uint32_t binding_origin = base::binding_origin; + + template + void bindExternalIndices(TA*... current) + { + ([this](TA* cur) { + if constexpr (binding_origin == TA::binding_origin) { + this->bindExternalIndex(cur); + } + }(current), + ...); + } using self_t = JoinFull; using table_t = base; @@ -3524,6 +3561,18 @@ class FilteredBase : public T using self_t = FilteredBase; using table_t = typename T::table_t; using T::originals; + static constexpr const uint32_t binding_origin = T::binding_origin; + template + void bindExternalIndices(TA*... current) + { + ([this](TA* cur) { + if constexpr (binding_origin == TA::binding_origin) { + this->bindExternalIndex(cur); + mFilteredBegin.bindExternalIndex(cur); + } + }(current), + ...); + } using columns_t = typename T::columns_t; using persistent_columns_t = typename T::persistent_columns_t; using external_index_columns_t = typename T::external_index_columns_t; @@ -3645,13 +3694,6 @@ class FilteredBase : public T /// Bind the columns which refer to other tables /// to the associated tables. - template - void bindExternalIndices(TA*... current) - { - table_t::bindExternalIndices(current...); - mFilteredBegin.bindExternalIndices(current...); - } - void bindExternalIndicesRaw(std::vector&& ptrs) { mFilteredBegin.bindExternalIndicesRaw(std::forward>(ptrs)); @@ -4134,6 +4176,19 @@ struct IndexTable : Table { using first_t = typename H::binding_t; using rest_t = framework::pack; + static constexpr const uint32_t binding_origin = Key::binding_origin; + + template + void bindExternalIndices(TA*... current) + { + ([this](TA* cur) { + if constexpr (binding_origin == TA::binding_origin) { + this->bindExternalIndex(cur); + } + }(current), + ...); + } + IndexTable(std::shared_ptr table, uint64_t offset = 0) : base_t{table, offset} { From 78b931b838cbfdcdbbf2789c80b0fa98d7b60f24 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?V=C3=ADt=20Ku=C4=8Dera?= <26327373+vkucera@users.noreply.github.com> Date: Tue, 24 Mar 2026 20:39:06 +0100 Subject: [PATCH 404/701] ZDC: Delete unused files (#15028) --- .../ZDC/include/DataFormatsZDC/FEEConfig.h | 52 ------------------- 1 file changed, 52 deletions(-) delete mode 100644 DataFormats/Detectors/ZDC/include/DataFormatsZDC/FEEConfig.h diff --git a/DataFormats/Detectors/ZDC/include/DataFormatsZDC/FEEConfig.h b/DataFormats/Detectors/ZDC/include/DataFormatsZDC/FEEConfig.h deleted file mode 100644 index b084507b84519..0000000000000 --- a/DataFormats/Detectors/ZDC/include/DataFormatsZDC/FEEConfig.h +++ /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. -// -// DataFormats/Detectors/ZDC/include/DataFormatsZDC/RawEventData.h - -#include "ZDCBase/Constants.h" - -#ifndef ALICEO2_ZDC_FEECONFIG_H -#define ALICEO2_ZDC_FEECONFIG_H - -/// \file FEEConfig.h -/// \brief ZDC FEE configuration -/// \author pietro.cortese@cern.ch - -namespace o2 -{ -namespace zdc -{ - -struct FEEFillingMap { - uint64_t filling[56]; -}; - -struct FEEConfigMap { - uint32_t address[5 * NChPerModule + 3] = {0, 1, 2, 3, - 4, 5, 6, 7, - 8, 9, 10, 11, - 12, 13, 14, 15, - 16, 17, 18, 19, - 76, 77, 78}; - uint64_t delay_sample[NChPerModule] = {6, 6, 6, 6}; // 4 bits - uint64_t delay_coarse[NChPerModule] = {200, 200, 200, 200}; // 8 bits - uint64_t threshold_level[NChPerModule] = {10, 10, 10, 10}; // 12 bits - uint64_t difference_delta[NChPerModule] = {4, 4, 4, 4}; // 3 bits - uint64_t masking_difference[NChPerModule] = {0x00ff00, 0x00ff00, 0x00ff00, 0x00ff00}; // 24 bits - uint64_t masking_alicet = 0x00000010; // 32 bits - uint64_t masking_autot = 0xf; // 4 bits - uint64_t masking_readout = 0xf; // 4 bits -}; - -} // namespace zdc -} // namespace o2 - -#endif From 4afa537700c0f0f89505d07f534a6de6ed3fb07f Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Wed, 25 Mar 2026 13:53:24 +0100 Subject: [PATCH 405/701] Filter raw linker flags from GBL_LIBRARIES (#15162) --- dependencies/O2Dependencies.cmake | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/dependencies/O2Dependencies.cmake b/dependencies/O2Dependencies.cmake index 71e9d9907ac28..850ba0b909acc 100644 --- a/dependencies/O2Dependencies.cmake +++ b/dependencies/O2Dependencies.cmake @@ -286,12 +286,25 @@ find_package(GBL) set_package_properties(GBL PROPERTIES TYPE REQUIRED) if(GBL_FOUND AND NOT TARGET GBL::GBL) # As of now, GBL does not provide a cmake target so create a compatibility wrapper + # also GBL_LIBRARIES contains raw linker flags to ROOT we need to filter out + set(GBL_LIBRARIES_FILTERED "") + set(GBL_LINK_OPTIONS "") + foreach(_lib IN LISTS GBL_LIBRARIES) + if(_lib MATCHES "^-[lL]") + continue() + elseif(_lib MATCHES "^-") + list(APPEND GBL_LINK_OPTIONS "${_lib}") + else() + list(APPEND GBL_LIBRARIES_FILTERED "${_lib}") + endif() + endforeach() add_library(GBL::GBL INTERFACE IMPORTED) target_include_directories(GBL::GBL INTERFACE ${GBL_INCLUDE_DIR}) target_link_libraries(GBL::GBL INTERFACE - ${GBL_LIBRARIES} + ${GBL_LIBRARIES_FILTERED} Eigen3::Eigen ) + target_link_options(GBL::GBL INTERFACE ${GBL_LINK_OPTIONS}) endif() feature_summary(WHAT ALL FATAL_ON_MISSING_REQUIRED_PACKAGES) From a628b60489e45123f21c0eb3b721fc58e255ce9d Mon Sep 17 00:00:00 2001 From: ehellbar Date: Wed, 25 Mar 2026 15:53:54 +0100 Subject: [PATCH 406/701] DPL: use constexpr for data description of EOS data header (#15175) --- DataFormats/Headers/include/Headers/DataHeader.h | 1 + Framework/Core/src/ExternalFairMQDeviceProxy.cxx | 4 ++-- Framework/Core/test/test_ExternalFairMQDeviceWorkflow.cxx | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/DataFormats/Headers/include/Headers/DataHeader.h b/DataFormats/Headers/include/Headers/DataHeader.h index b44f41c5d3cb3..dbcdb8e0bba89 100644 --- a/DataFormats/Headers/include/Headers/DataHeader.h +++ b/DataFormats/Headers/include/Headers/DataHeader.h @@ -600,6 +600,7 @@ constexpr o2::header::DataDescription gDataDescriptionClusters{"CLUSTERS"}; constexpr o2::header::DataDescription gDataDescriptionTracks{"TRACKS"}; constexpr o2::header::DataDescription gDataDescriptionConfig{"CONFIGURATION"}; constexpr o2::header::DataDescription gDataDescriptionInfo{"INFORMATION"}; +constexpr o2::header::DataDescription gDataDescriptionEos{"EOS"}; constexpr o2::header::DataDescription gDataDescriptionROOTStreamers{"ROOT STREAMERS"}; constexpr o2::header::DataDescription gDataDescriptionDISTSTF{"DISTSUBTIMEFRAME"}; /// @} // end of doxygen group diff --git a/Framework/Core/src/ExternalFairMQDeviceProxy.cxx b/Framework/Core/src/ExternalFairMQDeviceProxy.cxx index 3b0275879a158..5867f53af4bd2 100644 --- a/Framework/Core/src/ExternalFairMQDeviceProxy.cxx +++ b/Framework/Core/src/ExternalFairMQDeviceProxy.cxx @@ -1005,7 +1005,7 @@ DataProcessorSpec specifyFairMQDeviceOutputProxy(char const* name, } DataHeader dh; dh.dataOrigin = "DPL"; - dh.dataDescription = "EOS"; + dh.dataDescription = o2::header::gDataDescriptionEos; dh.subSpecification = 0; dh.payloadSize = 0; dh.runNumber = runNumber; @@ -1137,7 +1137,7 @@ DataProcessorSpec specifyFairMQDeviceMultiOutputProxy(char const* name, } DataHeader dh; dh.dataOrigin = "DPL"; - dh.dataDescription = "EOS"; + dh.dataDescription = o2::header::gDataDescriptionEos; dh.subSpecification = 0; dh.payloadSize = 0; dh.payloadSerializationMethod = o2::header::gSerializationMethodNone; diff --git a/Framework/Core/test/test_ExternalFairMQDeviceWorkflow.cxx b/Framework/Core/test/test_ExternalFairMQDeviceWorkflow.cxx index 6c991aba7fff5..b69cae5819fbf 100644 --- a/Framework/Core/test/test_ExternalFairMQDeviceWorkflow.cxx +++ b/Framework/Core/test/test_ExternalFairMQDeviceWorkflow.cxx @@ -243,7 +243,7 @@ std::vector defineDataProcessing(ConfigContext const& config) // since we are sending on the bare channel, also the EOS message needs to be created. DataHeader dhEOS; dhEOS.dataOrigin = "DPL"; - dhEOS.dataDescription = "EOS"; + dhEOS.dataDescription = o2::header::gDataDescriptionEos; dhEOS.subSpecification = 0; dhEOS.payloadSize = 0; dhEOS.payloadSerializationMethod = o2::header::gSerializationMethodNone; From 5768b117ee9eeeb16d1c84e384bb51d74084a293 Mon Sep 17 00:00:00 2001 From: Ernst Hellbar Date: Wed, 25 Mar 2026 14:01:41 +0100 Subject: [PATCH 407/701] start_tmux.sh: remove double-checking for existing shm segments --- prodtests/full-system-test/start_tmux.sh | 9 +-------- 1 file changed, 1 insertion(+), 8 deletions(-) diff --git a/prodtests/full-system-test/start_tmux.sh b/prodtests/full-system-test/start_tmux.sh index fb69cc1e6baec..18f019573904f 100755 --- a/prodtests/full-system-test/start_tmux.sh +++ b/prodtests/full-system-test/start_tmux.sh @@ -12,14 +12,7 @@ if [[ "${FST_RUN_WITHOUT_CHECKS:-0}" != "1" ]]; then 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 + # 2. 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 From 349a25edb8db27f8ca0888a496ed5b2f3eafdc37 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Tue, 24 Mar 2026 14:37:50 +0100 Subject: [PATCH 408/701] DPL: move away from MessageSet::header / payload Abstract header / payload retrieval, with the idea that get_header / get_payload will work on any range of fair::mq::MessagePtrs. For now we only do the first header / payload pair only, to validate the trivial change. --- Framework/Core/include/Framework/DataModelViews.h | 4 ++-- Framework/Core/src/DataProcessingDevice.cxx | 2 +- Framework/Core/src/DataRelayer.cxx | 12 ++++++------ Framework/Core/test/test_DataRelayer.cxx | 8 ++++---- 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/Framework/Core/include/Framework/DataModelViews.h b/Framework/Core/include/Framework/DataModelViews.h index f42ef85ec78e1..73faf7699834d 100644 --- a/Framework/Core/include/Framework/DataModelViews.h +++ b/Framework/Core/include/Framework/DataModelViews.h @@ -153,7 +153,7 @@ struct get_header { // 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) + friend auto& operator|(R&& r, get_header self) { return r[(r | get_dataref_indices{self.id, 0}).headerIdx]; } @@ -165,7 +165,7 @@ struct get_payload { // 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) + friend auto& operator|(R&& r, get_payload self) { return r[(r | get_dataref_indices{self.part, self.subPart}).payloadIdx]; } diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index da04a23e81c0c..0fa70947bf18c 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -2153,7 +2153,7 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v return currentSetOfInputs[i].getNumberOfPairs(); }; auto refCountGetter = [¤tSetOfInputs](size_t idx) -> int { - auto& header = static_cast(*currentSetOfInputs[idx].header(0)); + auto& header = static_cast(*(currentSetOfInputs[idx].messages | get_header{0})); return header.GetRefCount(); }; return InputSpan{getter, nofPartsGetter, refCountGetter, currentSetOfInputs.size()}; diff --git a/Framework/Core/src/DataRelayer.cxx b/Framework/Core/src/DataRelayer.cxx index 5b85a63bf6c95..7eb851e2aadd8 100644 --- a/Framework/Core/src/DataRelayer.cxx +++ b/Framework/Core/src/DataRelayer.cxx @@ -184,11 +184,11 @@ DataRelayer::ActivityStats DataRelayer::processDanglingInputs(std::vector int { - auto& header = static_cast(*partial[idx].header(0)); + auto& header = static_cast(*(partial[idx].messages | get_header{0})); return header.GetRefCount(); }; InputSpan span{getter, nPartsGetter, refCountGetter, static_cast(partial.size())}; @@ -246,8 +246,8 @@ DataRelayer::ActivityStats DataRelayer::processDanglingInputs(std::vector& comp return partial[idx].messages | count_parts{}; }; auto refCountGetter = [&partial](size_t idx) -> int { - auto& header = static_cast(*partial[idx].header(0)); + auto& header = static_cast(*(partial[idx].messages | get_header{0})); return header.GetRefCount(); }; InputSpan span{getter, nPartsGetter, refCountGetter, static_cast(partial.size())}; diff --git a/Framework/Core/test/test_DataRelayer.cxx b/Framework/Core/test/test_DataRelayer.cxx index e5ca7c5d235e5..e4aa35286942d 100644 --- a/Framework/Core/test/test_DataRelayer.cxx +++ b/Framework/Core/test/test_DataRelayer.cxx @@ -798,11 +798,11 @@ TEST_CASE("DataRelayer") // one message set containing number of added sequences of messages REQUIRE((messageSet[0].messages | count_parts{}) == sequenceSize.size()); size_t counter = 0; - for (auto seqid = 0; seqid < sequenceSize.size(); ++seqid) { + for (size_t seqid = 0; seqid < sequenceSize.size(); ++seqid) { REQUIRE(messageSet[0].getNumberOfPayloads(seqid) == sequenceSize[seqid]); - for (auto pi = 0; pi < messageSet[0].getNumberOfPayloads(seqid); ++pi) { - REQUIRE(messageSet[0].payload(seqid, pi)); - auto const* data = messageSet[0].payload(seqid, pi)->GetData(); + for (size_t pi = 0; pi < messageSet[0].getNumberOfPayloads(seqid); ++pi) { + REQUIRE((messageSet[0].messages | get_payload{seqid, pi})); + auto const* data = (messageSet[0].messages | get_payload{seqid, pi})->GetData(); REQUIRE(*(reinterpret_cast(data)) == counter); ++counter; } From 45df7ad30d1515e0f9fa68f321ee91a4536bbbfb Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 26 Mar 2026 03:27:35 +0100 Subject: [PATCH 409/701] DPL: fix DataModelViews behavior (#15212) --- .../Core/include/Framework/DataModelViews.h | 43 +-- Framework/Core/test/test_DataRelayer.cxx | 159 +++++++++++ Framework/Core/test/test_MessageSet.cxx | 250 ++++++++++++++++++ 3 files changed, 435 insertions(+), 17 deletions(-) diff --git a/Framework/Core/include/Framework/DataModelViews.h b/Framework/Core/include/Framework/DataModelViews.h index 73faf7699834d..7c39a94950e9c 100644 --- a/Framework/Core/include/Framework/DataModelViews.h +++ b/Framework/Core/include/Framework/DataModelViews.h @@ -70,7 +70,7 @@ struct count_parts { count += 1; mi += header->splitPayloadParts + 1; } else { - count += header->splitPayloadParts; + count += header->splitPayloadParts ? header->splitPayloadParts : 1; mi += header->splitPayloadParts ? 2 * header->splitPayloadParts : 2; } } @@ -104,11 +104,11 @@ struct get_pair { } mi += header->splitPayloadParts + 1; } else { - count += header->splitPayloadParts ? header->splitPayloadParts : 1; - if (self.pairId < count) { - return {mi, mi + 2 * diff + 1}; + if (self.pairId == count) { + return {mi, mi + 1}; } - mi += header->splitPayloadParts ? 2 * header->splitPayloadParts : 2; + count += 1; + mi += 2; } } throw std::runtime_error("Payload not found"); @@ -138,10 +138,10 @@ struct get_dataref_indices { mi += header->splitPayloadParts + 1; } else { if (self.part == count) { - return {mi, mi + 2 * self.subPart + 1}; + return {mi, mi + self.subPart + 1}; } count += 1; - mi += header->splitPayloadParts ? 2 * header->splitPayloadParts : 2; + mi += 2; } } throw std::runtime_error("Payload not found"); @@ -172,32 +172,41 @@ struct get_payload { }; struct get_num_payloads { - size_t id; - // ends the pipeline, returns the number of parts + size_t n; + // ends the pipeline, returns the number of payloads which are associated + // to the multipart n-th sequence of messages found in the range 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; + // Un 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)) { + if (header->splitPayloadParts > 1 && (header->splitPayloadIndex == header->splitPayloadParts)) { + // This is the case for the new multi payload messages where the number of parts + // is as many as the splitPayloadParts number. + if (self.n == count) { return header->splitPayloadParts; - } else { - return 1; } - } - if (header->splitPayloadParts > 1 && (header->splitPayloadIndex == header->splitPayloadParts)) { + // For multipayload we skip all the parts and their associated header count += 1; mi += header->splitPayloadParts + 1; } else { - count += 1; - mi += header->splitPayloadParts ? 2 * header->splitPayloadParts : 2; + // This is the case of a multipart (header, payload), (header, payload), ... + // sequence where we know how many pairs are there. + // When splitPayloadParts == 0, it means it is a non-multipart (header, payload) + // pair. Each pair has exactly 1 payload. + auto pairs = header->splitPayloadParts ? header->splitPayloadParts : 1; + if (self.n < count + pairs) { + return 1; + } + count += pairs; + mi += 2 * pairs; } } return 0; diff --git a/Framework/Core/test/test_DataRelayer.cxx b/Framework/Core/test/test_DataRelayer.cxx index e4aa35286942d..1f7518860bf57 100644 --- a/Framework/Core/test/test_DataRelayer.cxx +++ b/Framework/Core/test/test_DataRelayer.cxx @@ -26,6 +26,10 @@ #include "Framework/WorkflowSpec.h" #include #include +#include +#include "Framework/FairMQDeviceProxy.h" +#include "Framework/ExpirationHandler.h" +#include "Framework/LifetimeHelpers.h" #include #include #include @@ -808,4 +812,159 @@ TEST_CASE("DataRelayer") } } } + + SECTION("ProcessDanglingInputs") + { + InputSpec spec{"condition", "TST", "COND"}; + std::vector inputs = { + InputRoute{spec, 0, "from_source_to_self", 0}}; + + std::vector infos{1}; + TimesliceIndex index{1, infos}; + ref.registerService(ServiceRegistryHelpers::handleForService(&index)); + + // Bind a fake input channel so FairMQDeviceProxy::getInputChannelIndex works + FairMQDeviceProxy proxy; + std::vector channels{fair::mq::Channel("from_source_to_self")}; + auto findChannel = [&channels](std::string const& name) -> fair::mq::Channel& { + for (auto& ch : channels) { + if (ch.GetName() == name) { + return ch; + } + } + throw std::runtime_error("Channel not found: " + name); + }; + proxy.bind({}, inputs, {}, findChannel, [] { return false; }); + ref.registerService(ServiceRegistryHelpers::handleForService(&proxy)); + + auto policy = CompletionPolicyHelpers::consumeWhenAny(); + DataRelayer relayer(policy, inputs, index, {registry}, -1); + relayer.setPipelineLength(4); + + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); + + DataHeader dh{"COND", "TST", 0}; + dh.splitPayloadParts = 1; + dh.splitPayloadIndex = 0; + DataProcessingHeader dph{0, 1}; + + ExpirationHandler handler; + handler.name = "test-condition"; + handler.routeIndex = RouteIndex{0}; + handler.lifetime = Lifetime::Condition; + + // Creator: claim an empty slot and assign timeslice 0 to it + handler.creator = [](ServiceRegistryRef services, ChannelIndex channelIndex) -> TimesliceSlot { + auto& index = services.get(); + for (size_t si = 0; si < index.size(); si++) { + TimesliceSlot slot{si}; + if (!index.isValid(slot)) { + index.associate(TimesliceId{0}, slot); + (void)index.setOldestPossibleInput({1}, channelIndex); + return slot; + } + } + return TimesliceSlot{TimesliceSlot::INVALID}; + }; + + // Checker: always trigger expiration + handler.checker = LifetimeHelpers::expireAlways(); + + // Handler: materialise a dummy header+payload into the PartRef + handler.handler = [&transport, &channelAlloc, &dh, &dph](ServiceRegistryRef, PartRef& ref, data_matcher::VariableContext&) { + ref.header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph}); + ref.payload = transport->CreateMessage(4); + }; + + std::vector handlers{handler}; + auto activity = relayer.processDanglingInputs(handlers, {registry}, true); + + REQUIRE(activity.newSlots == 1); + REQUIRE(activity.expiredSlots == 1); + + // The materialised data should now be ready to consume + std::vector ready; + relayer.getReadyToProcess(ready); + REQUIRE(ready.size() == 1); + REQUIRE(ready[0].op == CompletionPolicy::CompletionOp::Consume); + + auto result = relayer.consumeAllInputsForTimeslice(ready[0].slot); + REQUIRE(result.size() == 1); + REQUIRE((result.at(0).messages | count_parts{}) == 1); + } + + SECTION("ProcessDanglingInputsSkipsWhenDataPresent") + { + // processDanglingInputs must not overwrite a slot that already has data. + // This is guarded by the (part.messages | get_header{0}) != nullptr check. + InputSpec spec{"condition", "TST", "COND"}; + std::vector inputs = { + InputRoute{spec, 0, "from_source_to_self", 0}}; + + std::vector infos{1}; + TimesliceIndex index{1, infos}; + ref.registerService(ServiceRegistryHelpers::handleForService(&index)); + + FairMQDeviceProxy proxy; + std::vector channels{fair::mq::Channel("from_source_to_self")}; + auto findChannel = [&channels](std::string const& name) -> fair::mq::Channel& { + for (auto& ch : channels) { + if (ch.GetName() == name) { + return ch; + } + } + throw std::runtime_error("Channel not found: " + name); + }; + proxy.bind({}, inputs, {}, findChannel, [] { return false; }); + ref.registerService(ServiceRegistryHelpers::handleForService(&proxy)); + + auto policy = CompletionPolicyHelpers::consumeWhenAny(); + DataRelayer relayer(policy, inputs, index, {registry}, -1); + relayer.setPipelineLength(4); + + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); + + DataHeader dh{"COND", "TST", 0}; + dh.splitPayloadParts = 1; + dh.splitPayloadIndex = 0; + DataProcessingHeader dph{0, 1}; + + // Build an expiration handler that always tries to expire + ExpirationHandler handler; + handler.name = "test-condition"; + handler.routeIndex = RouteIndex{0}; + handler.lifetime = Lifetime::Condition; + handler.creator = [](ServiceRegistryRef services, ChannelIndex channelIndex) -> TimesliceSlot { + auto& index = services.get(); + for (size_t si = 0; si < index.size(); si++) { + TimesliceSlot slot{si}; + if (!index.isValid(slot)) { + index.associate(TimesliceId{0}, slot); + (void)index.setOldestPossibleInput({1}, channelIndex); + return slot; + } + } + return TimesliceSlot{TimesliceSlot::INVALID}; + }; + handler.checker = LifetimeHelpers::expireAlways(); + int handlerCallCount = 0; + handler.handler = [&transport, &channelAlloc, &dh, &dph, &handlerCallCount](ServiceRegistryRef, PartRef& ref, data_matcher::VariableContext&) { + ref.header = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph}); + ref.payload = transport->CreateMessage(4); + handlerCallCount++; + }; + std::vector handlers{handler}; + + // First call: slot is empty, so the handler fires and materialises data + auto activity1 = relayer.processDanglingInputs(handlers, {registry}, true); + REQUIRE(activity1.expiredSlots == 1); + REQUIRE(handlerCallCount == 1); + + // Second call: slot already has data — the handler must NOT fire again + auto activity2 = relayer.processDanglingInputs(handlers, {registry}, false); + REQUIRE(activity2.expiredSlots == 0); + REQUIRE(handlerCallCount == 1); // handler was not called a second time + } } diff --git a/Framework/Core/test/test_MessageSet.cxx b/Framework/Core/test/test_MessageSet.cxx index 37f823197ef18..290e55220d6cb 100644 --- a/Framework/Core/test/test_MessageSet.cxx +++ b/Framework/Core/test/test_MessageSet.cxx @@ -12,8 +12,10 @@ #include #include #include "Framework/MessageSet.h" +#include "Framework/DataModelViews.h" #include "Framework/DataProcessingHeader.h" #include "Headers/Stack.h" +#include "Headers/DataHeader.h" #include "MemoryResources/MemoryResources.h" #include @@ -43,6 +45,12 @@ TEST_CASE("MessageSet") REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); CHECK_THROWS((msgSet.messages | get_pair{1})); + // Validate pipe operators match old API + REQUIRE(&(msgSet.messages | get_header{0}) == &msgSet.header(0)); + REQUIRE(&(msgSet.messages | get_payload{0, 0}) == &msgSet.payload(0)); + REQUIRE((msgSet.messages | get_num_payloads{0}) == msgSet.messageMap[0].size); + REQUIRE((msgSet.messages | count_parts{}) == msgSet.messageMap.size()); + REQUIRE((msgSet.messages | count_payloads{}) == msgSet.pairMap.size()); } TEST_CASE("MessageSetWithFunction") @@ -68,6 +76,11 @@ TEST_CASE("MessageSetWithFunction") REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); CHECK_THROWS((msgSet.messages | get_pair{1})); + REQUIRE(&(msgSet.messages | get_header{0}) == &msgSet.header(0)); + REQUIRE(&(msgSet.messages | get_payload{0, 0}) == &msgSet.payload(0)); + REQUIRE((msgSet.messages | get_num_payloads{0}) == msgSet.messageMap[0].size); + REQUIRE((msgSet.messages | count_parts{}) == msgSet.messageMap.size()); + REQUIRE((msgSet.messages | count_payloads{}) == msgSet.pairMap.size()); } TEST_CASE("MessageSetWithMultipart") @@ -99,6 +112,13 @@ TEST_CASE("MessageSetWithMultipart") REQUIRE((msgSet.messages | get_pair{1}).headerIdx == 0); REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 2); CHECK_THROWS((msgSet.messages | get_pair{2})); + // Validate pipe operators match old API for multi-payload + REQUIRE(&(msgSet.messages | get_header{0}) == &msgSet.header(0)); + REQUIRE(&(msgSet.messages | get_payload{0, 0}) == &msgSet.payload(0, 0)); + REQUIRE(&(msgSet.messages | get_payload{0, 1}) == &msgSet.payload(0, 1)); + REQUIRE((msgSet.messages | get_num_payloads{0}) == msgSet.messageMap[0].size); + REQUIRE((msgSet.messages | count_parts{}) == msgSet.messageMap.size()); + REQUIRE((msgSet.messages | count_payloads{}) == msgSet.pairMap.size()); } TEST_CASE("MessageSetAddPartRef") @@ -170,4 +190,234 @@ TEST_CASE("MessageSetAddMultiple") REQUIRE((msgSet.messages | get_pair{2}).payloadIdx == 5); REQUIRE((msgSet.messages | get_pair{3}).headerIdx == 4); REQUIRE((msgSet.messages | get_pair{3}).payloadIdx == 6); + // Validate pipe operators match old API for mixed modes + for (size_t i = 0; i < 3; ++i) { + REQUIRE(&(msgSet.messages | get_header{i}) == &msgSet.header(i)); + REQUIRE(&(msgSet.messages | get_payload{i, 0}) == &msgSet.payload(i, 0)); + } + // Part 2 has a second payload (multi-payload with splitPayloadParts=2, splitPayloadIndex=2) + REQUIRE(&(msgSet.messages | get_payload{2, 1}) == &msgSet.payload(2, 1)); + for (size_t i = 0; i < 3; ++i) { + REQUIRE((msgSet.messages | get_num_payloads{i}) == msgSet.messageMap[i].size); + } + REQUIRE((msgSet.messages | count_parts{}) == msgSet.messageMap.size()); + REQUIRE((msgSet.messages | count_payloads{}) == msgSet.pairMap.size()); +} + +TEST_CASE("GetHeaderPayloadOperators") +{ + // Validates that get_header{part} / get_payload{part, 0} pipe operators on .messages + // correctly replace the removed header(part) / payload(part) methods, + // including access to parts at index > 0. + o2::framework::DataProcessingHeader dph{0, 1}; + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); + + o2::framework::MessageSet msgSet; + + // Add two separate header-payload pairs + for (size_t part = 0; part < 2; ++part) { + o2::header::DataHeader dh{}; + dh.dataDescription = "CLUSTERS"; + dh.dataOrigin = "TPC"; + dh.subSpecification = part; // use part index as subSpecification to distinguish + dh.splitPayloadParts = 1; + dh.splitPayloadIndex = 0; + std::vector ptrs; + ptrs.emplace_back(o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph})); + ptrs.emplace_back(transport->CreateMessage(100 + part * 100)); // 100 and 200 bytes + msgSet.add([&ptrs](size_t i) -> fair::mq::MessagePtr& { return ptrs[i]; }, 2); + } + + REQUIRE(msgSet.messages.size() == 4); + + // Validate part 0 + auto& hdr0 = msgSet.messages | get_header{0}; + REQUIRE(hdr0.get() != nullptr); + auto* dh0 = o2::header::get(hdr0->GetData()); + REQUIRE(dh0 != nullptr); + REQUIRE(dh0->subSpecification == 0); + auto& pl0 = msgSet.messages | get_payload{0, 0}; + REQUIRE(pl0.get() != nullptr); + REQUIRE(pl0->GetSize() == 100); + + // Validate part 1 + auto& hdr1 = msgSet.messages | get_header{1}; + REQUIRE(hdr1.get() != nullptr); + auto* dh1 = o2::header::get(hdr1->GetData()); + REQUIRE(dh1 != nullptr); + REQUIRE(dh1->subSpecification == 1); + auto& pl1 = msgSet.messages | get_payload{1, 0}; + REQUIRE(pl1.get() != nullptr); + REQUIRE(pl1->GetSize() == 200); +} + +TEST_CASE("GetHeaderPayloadMultiPayload") +{ + // Validates get_header{part} / get_payload{part, subpart} where both + // part and subpart can be non-zero. + // Layout: + // part 0: standard (1 header + 1 payload) → splitPayloadParts=1 + // part 1: multi-payload (1 header + 3 payloads) → splitPayloadParts=3, splitPayloadIndex=3 + o2::framework::DataProcessingHeader dph{0, 1}; + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); + + o2::framework::MessageSet msgSet; + + // Part 0: standard header-payload pair + { + o2::header::DataHeader dh{}; + dh.dataDescription = "CLUSTERS"; + dh.dataOrigin = "TPC"; + dh.subSpecification = 0; + dh.splitPayloadParts = 1; + dh.splitPayloadIndex = 0; + std::vector ptrs; + ptrs.emplace_back(o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph})); + ptrs.emplace_back(transport->CreateMessage(100)); + msgSet.add([&ptrs](size_t i) -> fair::mq::MessagePtr& { return ptrs[i]; }, 2); + } + + // Part 1: one header with 3 payloads (splitPayloadIndex == splitPayloadParts) + { + o2::header::DataHeader dh{}; + dh.dataDescription = "TRACKS"; + dh.dataOrigin = "TPC"; + dh.subSpecification = 1; + dh.splitPayloadParts = 3; + dh.splitPayloadIndex = 3; // signals multi-payload layout + std::vector ptrs; + ptrs.emplace_back(o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph})); + ptrs.emplace_back(transport->CreateMessage(200)); + ptrs.emplace_back(transport->CreateMessage(300)); + ptrs.emplace_back(transport->CreateMessage(400)); + msgSet.add([&ptrs](size_t i) -> fair::mq::MessagePtr& { return ptrs[i]; }, 4); + } + + // messages: [hdr0, pl0, hdr1, pl1_0, pl1_1, pl1_2] + REQUIRE(msgSet.messages.size() == 6); + + // Part 0: standard + auto& hdr0 = msgSet.messages | get_header{0}; + REQUIRE(hdr0.get() != nullptr); + auto* dh0 = o2::header::get(hdr0->GetData()); + REQUIRE(dh0->subSpecification == 0); + auto& pl0 = msgSet.messages | get_payload{0, 0}; + REQUIRE(pl0.get() != nullptr); + REQUIRE(pl0->GetSize() == 100); + + // Part 1: multi-payload header + auto& hdr1 = msgSet.messages | get_header{1}; + REQUIRE(hdr1.get() != nullptr); + auto* dh1 = o2::header::get(hdr1->GetData()); + REQUIRE(dh1->subSpecification == 1); + + // get_payload{1, 0} — first payload of part 1 + auto& pl1_0 = msgSet.messages | get_payload{1, 0}; + REQUIRE(pl1_0.get() != nullptr); + REQUIRE(pl1_0->GetSize() == 200); + + // get_payload{1, 1} — second payload of part 1 (nonzero, nonzero) + auto& pl1_1 = msgSet.messages | get_payload{1, 1}; + REQUIRE(pl1_1.get() != nullptr); + REQUIRE(pl1_1->GetSize() == 300); + + // get_payload{1, 2} — third payload of part 1 (nonzero, nonzero) + auto& pl1_2 = msgSet.messages | get_payload{1, 2}; + REQUIRE(pl1_2.get() != nullptr); + REQUIRE(pl1_2->GetSize() == 400); + + // count_payloads should report 4 total (1 from part 0 + 3 from part 1) + REQUIRE((msgSet.messages | count_payloads{}) == 4); + // count_parts should report 2 (one per header) + REQUIRE((msgSet.messages | count_parts{}) == 2); + // get_num_payloads for part 1 should be 3 + REQUIRE((msgSet.messages | get_num_payloads{1}) == 3); + + // Validate pipe operators match old API for multi-payload (header, pl, pl, pl) + REQUIRE(&(msgSet.messages | get_header{0}) == &msgSet.header(0)); + REQUIRE(&(msgSet.messages | get_header{1}) == &msgSet.header(1)); + REQUIRE(&(msgSet.messages | get_payload{0, 0}) == &msgSet.payload(0, 0)); + REQUIRE(&(msgSet.messages | get_payload{1, 0}) == &msgSet.payload(1, 0)); + REQUIRE(&(msgSet.messages | get_payload{1, 1}) == &msgSet.payload(1, 1)); + REQUIRE(&(msgSet.messages | get_payload{1, 2}) == &msgSet.payload(1, 2)); + for (size_t i = 0; i < 2; ++i) { + REQUIRE((msgSet.messages | get_num_payloads{i}) == msgSet.messageMap[i].size); + } + REQUIRE((msgSet.messages | count_parts{}) == msgSet.messageMap.size()); + REQUIRE((msgSet.messages | count_payloads{}) == msgSet.pairMap.size()); +} + +TEST_CASE("TraditionalSplitParts") +{ + // Validates operators with traditional split parts layout: + // 3 (header, payload) pairs where splitPayloadParts=3, splitPayloadIndex=0,1,2 + // This is ONE logical part with 3 subparts. + // Memory layout: [hdr0, pl0, hdr1, pl1, hdr2, pl2] + o2::framework::DataProcessingHeader dph{0, 1}; + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); + + o2::framework::MessageSet msgSet; + + for (size_t i = 0; i < 3; ++i) { + o2::header::DataHeader dh{}; + dh.dataDescription = "CLUSTERS"; + dh.dataOrigin = "TPC"; + dh.subSpecification = 0; + dh.splitPayloadParts = 3; + dh.splitPayloadIndex = i; + std::vector ptrs; + ptrs.emplace_back(o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph})); + ptrs.emplace_back(transport->CreateMessage(100 * (i + 1))); + msgSet.add([&ptrs](size_t idx) -> fair::mq::MessagePtr& { return ptrs[idx]; }, 2); + } + + REQUIRE(msgSet.messages.size() == 6); + + // count_payloads: 3 traditional split parts = 3 payloads + REQUIRE((msgSet.messages | count_payloads{}) == 3); + // count_parts: one logical entity split into 3 pairs = 3 parts + REQUIRE((msgSet.messages | count_parts{}) == 3); + + // Each traditional split pair is a separate part, matching MessageSet::header(part) semantics + for (size_t i = 0; i < 3; ++i) { + auto& hdr = msgSet.messages | get_header{i}; + REQUIRE(hdr.get() != nullptr); + auto* dh = o2::header::get(hdr->GetData()); + REQUIRE(dh != nullptr); + REQUIRE(dh->splitPayloadIndex == i); + + auto& pl = msgSet.messages | get_payload{i, 0}; + REQUIRE(pl.get() != nullptr); + REQUIRE(pl->GetSize() == 100 * (i + 1)); + } + + // get_dataref_indices: each part maps to its own (header, payload) pair + for (size_t i = 0; i < 3; ++i) { + auto indices = msgSet.messages | get_dataref_indices{i, 0}; + REQUIRE(indices.headerIdx == 2 * i); + REQUIRE(indices.payloadIdx == 2 * i + 1); + } + + // get_pair: same as get_dataref_indices for traditional split + for (size_t i = 0; i < 3; ++i) { + auto indices = msgSet.messages | get_pair{i}; + REQUIRE(indices.headerIdx == 2 * i); + REQUIRE(indices.payloadIdx == 2 * i + 1); + } + + // get_num_payloads: each traditional split pair has 1 payload + for (size_t i = 0; i < 3; ++i) { + REQUIRE((msgSet.messages | get_num_payloads{i}) == msgSet.messageMap[i].size); + } + + // Validate pipe operators match old MessageSet::header()/payload() API + for (size_t i = 0; i < 3; ++i) { + REQUIRE(&(msgSet.messages | get_header{i}) == &msgSet.header(i)); + REQUIRE(&(msgSet.messages | get_payload{i, 0}) == &msgSet.payload(i)); + } + REQUIRE((msgSet.messages | count_parts{}) == msgSet.messageMap.size()); + REQUIRE((msgSet.messages | count_payloads{}) == msgSet.pairMap.size()); } From f7a8f7ca03ef19a0accfd7ea2b0efa5e970d31c0 Mon Sep 17 00:00:00 2001 From: shahoian Date: Wed, 25 Mar 2026 16:54:52 +0100 Subject: [PATCH 410/701] Add missing track->resetCov before refit for revertexing Skip tracks with ill-defined cov. matrix --- .../GlobalTrackingWorkflow/study/src/CheckResid.cxx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx b/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx index 063edc65d7486..ed419700b339b 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx @@ -318,6 +318,7 @@ bool CheckResidSpec::processITSTrack(const o2::its::TrackITS& iTrack, const o2:: auto inv2d = [](float s00, float s11, float s01) -> std::array { auto det = s00 * s11 - s01 * s01; if (det < 1e-16) { + LOGP(error, "Singular det {}, input: {} {} {}", det, s00, s11, s01); return {0.f, 0.f, 0.f}; } det = 1.f / det; @@ -329,11 +330,11 @@ bool CheckResidSpec::processITSTrack(const o2::its::TrackITS& iTrack, const o2:: 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) { + float cosAlp, sinAlp; + pvAlpha = trFitOut.getAlpha(); + o2::math_utils::sincos(trFitOut.getAlpha(), sinAlp, cosAlp); // vertex position rotated to track frame 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()); @@ -400,7 +401,7 @@ bool CheckResidSpec::processITSTrack(const o2::its::TrackITS& iTrack, const o2:: 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; + return false; } std::array wTot = {wInw[0] + wOut[0], wInw[1] + wOut[1], wInw[2] + wOut[2]}; auto cTot = inv2d(wTot[0], wTot[1], wTot[2]); @@ -481,6 +482,8 @@ bool CheckResidSpec::refitITStrack(o2::track::TrackParCov& track, GTrackID gid) const auto& params = CheckResidConfig::Instance(); auto pid = track.getPID(); track = trkITS.getParamOut(); + track.resetCovariance(); + track.setCov(track.getQ2Pt() * track.getQ2Pt() * track.getCov()[14], 14); track.setPID(pid); auto nCl = trkITS.getNumberOfClusters(); auto geom = o2::its::GeometryTGeo::Instance(); From 87b9775293c9734b0be767feb5915e614560a05c Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Thu, 26 Mar 2026 11:29:14 +0100 Subject: [PATCH 411/701] DPL: Better detection for injected workflows (fixed) (#15202) --- Framework/Core/src/ArrowSupport.cxx | 8 ++- Framework/Core/src/WorkflowHelpers.cxx | 15 +++- run/o2sim_hepmc_publisher.cxx | 94 +++++++++++++------------- run/o2sim_kine_publisher.cxx | 3 +- run/o2sim_mctracks_to_aod.cxx | 12 ++-- 5 files changed, 74 insertions(+), 58 deletions(-) diff --git a/Framework/Core/src/ArrowSupport.cxx b/Framework/Core/src/ArrowSupport.cxx index c5cc021a53478..b701ba5f8e01c 100644 --- a/Framework/Core/src/ArrowSupport.cxx +++ b/Framework/Core/src/ArrowSupport.cxx @@ -680,8 +680,12 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() 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 + auto tfnsource = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { + return !spec.name.starts_with("internal-dpl-aod-reader") && std::ranges::any_of(spec.outputs, [](OutputSpec const& output) { + return DataSpecUtils::match(output, "TFN", "TFNumber", 0); + }); + }); + if (tfnsource == 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 } diff --git a/Framework/Core/src/WorkflowHelpers.cxx b/Framework/Core/src/WorkflowHelpers.cxx index abe566e239618..2ef3df9426fde 100644 --- a/Framework/Core/src/WorkflowHelpers.cxx +++ b/Framework/Core/src/WorkflowHelpers.cxx @@ -411,13 +411,17 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext // add the reader if (aodReader.outputs.empty() == false) { - auto mctracks2aod = std::ranges::find_if(workflow, [](auto const& x) { return x.name == "mctracks-to-aod"; }); - if (mctracks2aod == workflow.end()) { + auto tfnsource = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { + return std::ranges::any_of(spec.outputs, [](OutputSpec const& output) { + return DataSpecUtils::match(output, "TFN", "TFNumber", 0); + }); + }); + if (tfnsource == workflow.end()) { // add normal reader aodReader.outputs.emplace_back(OutputSpec{"TFN", "TFNumber"}); aodReader.outputs.emplace_back(OutputSpec{"TFF", "TFFilename"}); } else { - // AODs are being injected on-the-fly, add error-handler reader + // AODs are being injected the tfnsource is the entry point, add error-handler reader aodReader.algorithm = AlgorithmSpec{ adaptStateful( [](DeviceSpec const& spec) { @@ -700,6 +704,11 @@ void WorkflowHelpers::injectAODWriter(WorkflowSpec& workflow, ConfigContext cons return DataSpecUtils::partialMatch(spec, o2::header::DataOrigin("TFN")); }); dec.isDangling[std::distance(dec.outputsInputs.begin(), it)] = false; + + it = std::find_if(dec.outputsInputs.begin(), dec.outputsInputs.end(), [](InputSpec const& spec) -> bool { + return DataSpecUtils::partialMatch(spec, o2::header::DataOrigin("TFF")); + }); + dec.isDangling[std::distance(dec.outputsInputs.begin(), it)] = false; } } diff --git a/run/o2sim_hepmc_publisher.cxx b/run/o2sim_hepmc_publisher.cxx index bf40abacb134f..f255b4a3a4f62 100644 --- a/run/o2sim_hepmc_publisher.cxx +++ b/run/o2sim_hepmc_publisher.cxx @@ -37,7 +37,9 @@ struct O2simHepmcPublisher { int tfCounter = 0; std::shared_ptr hepMCReader; bool eos = false; - std::vector mcTracks; + + std::vector*> mctracks_vector; + std::vector mcheader_vector; void init(o2::framework::InitContext& /*ic*/) { @@ -50,13 +52,19 @@ struct O2simHepmcPublisher { LOGP(fatal, "Cannot open HEPMC kine file {}", (std::string)hepmcFileName); } // allocate the memory upfront to prevent reallocations later - mcTracks.reserve(1e3 * aggregate); + mctracks_vector.reserve(aggregate); + mcheader_vector.reserve(aggregate); } void run(o2::framework::ProcessingContext& pc) { HepMC3::GenEvent event; - for (auto i = 0; i < (int)aggregate; ++i) { + auto batch = maxEvents > 0 ? std::min((int)aggregate, (int)maxEvents - eventCounter) : (int)aggregate; + for (auto i = 0; i < batch; ++i) { + mctracks_vector.push_back(&pc.outputs().make>(Output{"MC", "MCTRACKS", 0})); + auto& mctracks = mctracks_vector.back(); + mcheader_vector.push_back(&pc.outputs().make(Output{"MC", "MCHEADER", 0})); + auto& mcheader = mcheader_vector.back(); // read next entry hepMCReader->read_event(event); if (hepMCReader->failed()) { @@ -66,61 +74,60 @@ struct O2simHepmcPublisher { } // create O2 MCHeader and MCtracks vector out of HEPMC event - o2::dataformats::MCEventHeader mcHeader; - mcHeader.SetEventID(event.event_number()); - mcHeader.SetVertex(event.event_pos().px(), event.event_pos().py(), event.event_pos().pz()); + mcheader->SetEventID(event.event_number()); + mcheader->SetVertex(event.event_pos().px(), event.event_pos().py(), event.event_pos().pz()); auto xsecInfo = event.cross_section(); if (xsecInfo != nullptr) { - mcHeader.putInfo(MCInfoKeys::acceptedEvents, (uint64_t)xsecInfo->get_accepted_events()); - mcHeader.putInfo(MCInfoKeys::attemptedEvents, (uint64_t)xsecInfo->get_attempted_events()); - mcHeader.putInfo(MCInfoKeys::xSection, (float)xsecInfo->xsec()); - mcHeader.putInfo(MCInfoKeys::xSectionError, (float)xsecInfo->xsec_err()); + mcheader->putInfo(MCInfoKeys::acceptedEvents, (uint64_t)xsecInfo->get_accepted_events()); + mcheader->putInfo(MCInfoKeys::attemptedEvents, (uint64_t)xsecInfo->get_attempted_events()); + mcheader->putInfo(MCInfoKeys::xSection, (float)xsecInfo->xsec()); + mcheader->putInfo(MCInfoKeys::xSectionError, (float)xsecInfo->xsec_err()); } auto scale = event.attribute(MCInfoKeys::eventScale); if (scale != nullptr) { - mcHeader.putInfo(MCInfoKeys::eventScale, (float)scale->value()); + mcheader->putInfo(MCInfoKeys::eventScale, (float)scale->value()); } auto nMPI = event.attribute(MCInfoKeys::mpi); if (nMPI != nullptr) { - mcHeader.putInfo(MCInfoKeys::mpi, nMPI->value()); + mcheader->putInfo(MCInfoKeys::mpi, nMPI->value()); } auto sid = event.attribute(MCInfoKeys::processCode); auto scode = event.attribute(MCInfoKeys::processID); // default pythia8 hepmc3 interface uses signal_process_id if (sid != nullptr) { - mcHeader.putInfo(MCInfoKeys::processCode, sid->value()); + mcheader->putInfo(MCInfoKeys::processCode, sid->value()); } else if (scode != nullptr) { - mcHeader.putInfo(MCInfoKeys::processCode, scode->value()); + mcheader->putInfo(MCInfoKeys::processCode, scode->value()); } auto pdfInfo = event.pdf_info(); if (pdfInfo != nullptr) { - mcHeader.putInfo(MCInfoKeys::pdfParton1Id, pdfInfo->parton_id[0]); - mcHeader.putInfo(MCInfoKeys::pdfParton2Id, pdfInfo->parton_id[1]); - mcHeader.putInfo(MCInfoKeys::pdfCode1, pdfInfo->pdf_id[0]); - mcHeader.putInfo(MCInfoKeys::pdfCode2, pdfInfo->pdf_id[1]); - mcHeader.putInfo(MCInfoKeys::pdfX1, (float)pdfInfo->x[0]); - mcHeader.putInfo(MCInfoKeys::pdfX2, (float)pdfInfo->x[1]); - mcHeader.putInfo(MCInfoKeys::pdfScale, (float)pdfInfo->scale); - mcHeader.putInfo(MCInfoKeys::pdfXF1, (float)pdfInfo->xf[0]); - mcHeader.putInfo(MCInfoKeys::pdfXF2, (float)pdfInfo->xf[1]); + mcheader->putInfo(MCInfoKeys::pdfParton1Id, pdfInfo->parton_id[0]); + mcheader->putInfo(MCInfoKeys::pdfParton2Id, pdfInfo->parton_id[1]); + mcheader->putInfo(MCInfoKeys::pdfCode1, pdfInfo->pdf_id[0]); + mcheader->putInfo(MCInfoKeys::pdfCode2, pdfInfo->pdf_id[1]); + mcheader->putInfo(MCInfoKeys::pdfX1, (float)pdfInfo->x[0]); + mcheader->putInfo(MCInfoKeys::pdfX2, (float)pdfInfo->x[1]); + mcheader->putInfo(MCInfoKeys::pdfScale, (float)pdfInfo->scale); + mcheader->putInfo(MCInfoKeys::pdfXF1, (float)pdfInfo->xf[0]); + mcheader->putInfo(MCInfoKeys::pdfXF2, (float)pdfInfo->xf[1]); } auto heavyIon = event.heavy_ion(); if (heavyIon != nullptr) { - mcHeader.putInfo(MCInfoKeys::nCollHard, heavyIon->Ncoll_hard); - mcHeader.putInfo(MCInfoKeys::nPartProjectile, heavyIon->Npart_proj); - mcHeader.putInfo(MCInfoKeys::nPartTarget, heavyIon->Npart_targ); - mcHeader.putInfo(MCInfoKeys::nColl, heavyIon->Ncoll); - mcHeader.putInfo(MCInfoKeys::nCollNNWounded, heavyIon->N_Nwounded_collisions); - mcHeader.putInfo(MCInfoKeys::nCollNWoundedN, heavyIon->Nwounded_N_collisions); - mcHeader.putInfo(MCInfoKeys::nCollNWoundedNwounded, heavyIon->Nwounded_Nwounded_collisions); - mcHeader.putInfo(MCInfoKeys::nSpecProjectileNeutron, heavyIon->Nspec_proj_n); - mcHeader.putInfo(MCInfoKeys::nSpecProjectileProton, heavyIon->Nspec_proj_p); - mcHeader.putInfo(MCInfoKeys::nSpecTargetNeutron, heavyIon->Nspec_targ_n); - mcHeader.putInfo(MCInfoKeys::nSpecTargetProton, heavyIon->Nspec_targ_p); - mcHeader.putInfo(MCInfoKeys::impactParameter, (float)heavyIon->impact_parameter); - mcHeader.putInfo(MCInfoKeys::planeAngle, (float)heavyIon->event_plane_angle); - mcHeader.putInfo("eccentricity", (float)heavyIon->eccentricity); - mcHeader.putInfo(MCInfoKeys::sigmaInelNN, (float)heavyIon->sigma_inel_NN); - mcHeader.putInfo(MCInfoKeys::centrality, (float)heavyIon->centrality); + mcheader->putInfo(MCInfoKeys::nCollHard, heavyIon->Ncoll_hard); + mcheader->putInfo(MCInfoKeys::nPartProjectile, heavyIon->Npart_proj); + mcheader->putInfo(MCInfoKeys::nPartTarget, heavyIon->Npart_targ); + mcheader->putInfo(MCInfoKeys::nColl, heavyIon->Ncoll); + mcheader->putInfo(MCInfoKeys::nCollNNWounded, heavyIon->N_Nwounded_collisions); + mcheader->putInfo(MCInfoKeys::nCollNWoundedN, heavyIon->Nwounded_N_collisions); + mcheader->putInfo(MCInfoKeys::nCollNWoundedNwounded, heavyIon->Nwounded_Nwounded_collisions); + mcheader->putInfo(MCInfoKeys::nSpecProjectileNeutron, heavyIon->Nspec_proj_n); + mcheader->putInfo(MCInfoKeys::nSpecProjectileProton, heavyIon->Nspec_proj_p); + mcheader->putInfo(MCInfoKeys::nSpecTargetNeutron, heavyIon->Nspec_targ_n); + mcheader->putInfo(MCInfoKeys::nSpecTargetProton, heavyIon->Nspec_targ_p); + mcheader->putInfo(MCInfoKeys::impactParameter, (float)heavyIon->impact_parameter); + mcheader->putInfo(MCInfoKeys::planeAngle, (float)heavyIon->event_plane_angle); + mcheader->putInfo("eccentricity", (float)heavyIon->eccentricity); + mcheader->putInfo(MCInfoKeys::sigmaInelNN, (float)heavyIon->sigma_inel_NN); + mcheader->putInfo(MCInfoKeys::centrality, (float)heavyIon->centrality); } auto particles = event.particles(); @@ -131,7 +138,7 @@ struct O2simHepmcPublisher { auto has_children = children.size() > 0; auto p = particle->momentum(); auto v = particle->production_vertex(); - mcTracks.emplace_back( + mctracks->emplace_back( particle->pid(), has_parents ? parents.front()->id() : -1, has_parents ? parents.back()->id() : -1, has_children ? children.front()->id() : -1, has_children ? children.back()->id() : -1, @@ -139,18 +146,13 @@ struct O2simHepmcPublisher { v->position().x(), v->position().y(), v->position().z(), v->position().t(), 0); } - - // add to the message - pc.outputs().snapshot(Output{"MC", "MCHEADER", 0}, mcHeader); - pc.outputs().snapshot(Output{"MC", "MCTRACKS", 0}, mcTracks); - mcTracks.clear(); ++eventCounter; } // report number of TFs injected for the rate limiter to work ++tfCounter; pc.services().get().send(o2::monitoring::Metric{(uint64_t)tfCounter, "df-sent"}.addTag(o2::monitoring::tags::Key::Subsystem, o2::monitoring::tags::Value::DPL)); - if (eos || (maxEvents > 0 && eventCounter == maxEvents)) { + if (eos || (maxEvents > 0 && eventCounter >= maxEvents)) { pc.services().get().endOfStream(); pc.services().get().readyToQuit(QuitRequest::Me); } diff --git a/run/o2sim_kine_publisher.cxx b/run/o2sim_kine_publisher.cxx index cfbea6ae02a5f..5920743c3fafa 100644 --- a/run/o2sim_kine_publisher.cxx +++ b/run/o2sim_kine_publisher.cxx @@ -40,7 +40,8 @@ struct O2simKinePublisher { void run(o2::framework::ProcessingContext& pc) { - for (auto i = 0; i < std::min((int)aggregate, nEvents - eventCounter); ++i) { + auto batch = std::min((int)aggregate, nEvents - eventCounter); + for (auto i = 0; i < batch; ++i) { auto mcevent = mcKinReader->getMCEventHeader(0, eventCounter); auto mctracks = mcKinReader->getTracks(0, eventCounter); pc.outputs().snapshot(Output{"MC", "MCHEADER", 0}, mcevent); diff --git a/run/o2sim_mctracks_to_aod.cxx b/run/o2sim_mctracks_to_aod.cxx index 124e8aa7b3e42..d95a3b33cc38f 100644 --- a/run/o2sim_mctracks_to_aod.cxx +++ b/run/o2sim_mctracks_to_aod.cxx @@ -70,7 +70,7 @@ struct MctracksToAod { /** Run the conversion */ void run(o2::framework::ProcessingContext& pc) { - LOG(debug) << "=== Running extended MC AOD exporter ==="; + LOG(detail) << "=== Running extended MC AOD exporter ==="; using namespace o2::aodmchelpers; using McHeader = o2::dataformats::MCEventHeader; using McTrack = o2::MCTrack; @@ -94,13 +94,13 @@ struct MctracksToAod { // TODO: include BC simulation auto bcCounter = 0UL; size_t offset = 0; - LOG(debug) << "--- Loop over " << nParts << " parts ---"; + LOG(detail) << "--- Loop over " << nParts << " parts ---"; for (auto i = 0U; i < nParts; ++i) { auto record = mSampler.generateCollisionTime(); auto header = pc.inputs().get("mcheader", i); auto tracks = pc.inputs().get("mctracks", i); - LOG(debug) << "Updating collision table"; + LOG(detail) << "Updating collision table"; auto genID = updateMCCollisions(mCollisions.cursor, bcCounter, record.timeInBCNS * 1.e-3, @@ -108,12 +108,12 @@ struct MctracksToAod { 0, i); - LOG(debug) << "Updating HepMC tables"; + LOG(detail) << "Updating HepMC tables"; updateHepMCXSection(mXSections.cursor, bcCounter, genID, *header); updateHepMCPdfInfo(mPdfInfos.cursor, bcCounter, genID, *header); updateHepMCHeavyIon(mHeavyIons.cursor, bcCounter, genID, *header); - LOG(debug) << "Updating particles table"; + LOG(detail) << "Updating particles table"; TrackToIndex preselect; offset = updateParticles(mParticles.cursor, bcCounter, @@ -123,7 +123,7 @@ struct MctracksToAod { (bool)filt, false); - LOG(debug) << "Increment BC counter"; + LOG(detail) << "Increment BC counter"; bcCounter++; } From d1a5e60eeae674c9384bcadde10ac5dae9ce7db9 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 26 Mar 2026 13:53:29 +0100 Subject: [PATCH 412/701] DPL: make sure DataRelayer benchmark works again (#15216) --- Framework/Core/test/benchmark_DataRelayer.cxx | 68 +++++++++++++++---- 1 file changed, 53 insertions(+), 15 deletions(-) diff --git a/Framework/Core/test/benchmark_DataRelayer.cxx b/Framework/Core/test/benchmark_DataRelayer.cxx index e983f3604cfab..312711d73e95e 100644 --- a/Framework/Core/test/benchmark_DataRelayer.cxx +++ b/Framework/Core/test/benchmark_DataRelayer.cxx @@ -15,10 +15,17 @@ #include "Framework/CompletionPolicyHelpers.h" #include "Framework/DataRelayer.h" #include "Framework/DataProcessingHeader.h" +#include "Framework/DataProcessingStates.h" +#include "Framework/DataProcessingStats.h" +#include "Framework/DeviceState.h" +#include "Framework/DriverConfig.h" +#include "Framework/ServiceRegistryHelpers.h" +#include "Framework/TimingHelpers.h" #include #include #include #include +#include using Monitoring = o2::monitoring::Monitoring; using namespace o2::framework; @@ -26,6 +33,42 @@ using DataHeader = o2::header::DataHeader; using Stack = o2::header::Stack; using RecordAction = o2::framework::DataRelayer::RecordAction; +struct BenchmarkServices { + Monitoring monitoring; + const DriverConfig driverConfig{.batch = false}; + DataProcessingStates states{ + TimingHelpers::defaultRealtimeBaseConfigurator(0, uv_default_loop()), + TimingHelpers::defaultCPUTimeConfigurator(uv_default_loop())}; + DataProcessingStats stats{ + TimingHelpers::defaultRealtimeBaseConfigurator(0, uv_default_loop()), + TimingHelpers::defaultCPUTimeConfigurator(uv_default_loop()), + {}}; + DeviceState deviceState; + ServiceRegistry registry; + + ServiceRegistryRef ref() + { + using MetricSpec = DataProcessingStats::MetricSpec; + int quickUpdateInterval = 1; + std::vector specs{ + MetricSpec{.name = "malformed_inputs", .metricId = static_cast(ProcessingStatsId::MALFORMED_INPUTS), .minPublishInterval = quickUpdateInterval}, + MetricSpec{.name = "dropped_computations", .metricId = static_cast(ProcessingStatsId::DROPPED_COMPUTATIONS), .minPublishInterval = quickUpdateInterval}, + MetricSpec{.name = "dropped_incoming_messages", .metricId = static_cast(ProcessingStatsId::DROPPED_INCOMING_MESSAGES), .minPublishInterval = quickUpdateInterval}, + MetricSpec{.name = "relayed_messages", .metricId = static_cast(ProcessingStatsId::RELAYED_MESSAGES), .minPublishInterval = quickUpdateInterval}}; + for (auto& spec : specs) { + stats.registerMetric(spec); + } + + ServiceRegistryRef r{registry}; + r.registerService(ServiceRegistryHelpers::handleForService(&monitoring)); + r.registerService(ServiceRegistryHelpers::handleForService(&states)); + r.registerService(ServiceRegistryHelpers::handleForService(&stats)); + r.registerService(ServiceRegistryHelpers::handleForService(&driverConfig)); + r.registerService(ServiceRegistryHelpers::handleForService(&deviceState)); + return r; + } +}; + // a simple benchmark of the contribution of the pure message creation // this was important when the benchmarks below included the message // creation inside the benchmark loop, its somewhat obsolete now but @@ -54,7 +97,7 @@ BENCHMARK(BM_RelayMessageCreation); // and the subsequent InputRecord is immediately requested. static void BM_RelaySingleSlot(benchmark::State& state) { - Monitoring metrics; + BenchmarkServices services; InputSpec spec{"clusters", "TPC", "CLUSTERS"}; std::vector inputs = { @@ -64,8 +107,7 @@ static void BM_RelaySingleSlot(benchmark::State& state) std::vector infos{1}; TimesliceIndex index{1, infos}; auto policy = CompletionPolicyHelpers::consumeWhenAny(); - ServiceRegistry registry; - DataRelayer relayer(policy, inputs, index, {registry}, -1); + DataRelayer relayer(policy, inputs, index, services.ref(), -1); relayer.setPipelineLength(4); // Let's create a dummy O2 Message with two headers in the stack: @@ -106,7 +148,7 @@ BENCHMARK(BM_RelaySingleSlot); // This one will simulate a single input. static void BM_RelayMultipleSlots(benchmark::State& state) { - Monitoring metrics; + BenchmarkServices services; InputSpec spec{"clusters", "TPC", "CLUSTERS"}; std::vector inputs = { @@ -117,8 +159,7 @@ static void BM_RelayMultipleSlots(benchmark::State& state) TimesliceIndex index{1, infos}; auto policy = CompletionPolicyHelpers::consumeWhenAny(); - ServiceRegistry registry; - DataRelayer relayer(policy, inputs, index, {registry}, -1); + DataRelayer relayer(policy, inputs, index, services.ref(), -1); relayer.setPipelineLength(4); // Let's create a dummy O2 Message with two headers in the stack: @@ -163,7 +204,7 @@ BENCHMARK(BM_RelayMultipleSlots); /// In this case we have a record with two entries static void BM_RelayMultipleRoutes(benchmark::State& state) { - Monitoring metrics; + BenchmarkServices services; InputSpec spec1{"clusters", "TPC", "CLUSTERS"}; InputSpec spec2{"tracks", "TPC", "TRACKS"}; @@ -176,8 +217,7 @@ static void BM_RelayMultipleRoutes(benchmark::State& state) TimesliceIndex index{1, infos}; auto policy = CompletionPolicyHelpers::consumeWhenAny(); - ServiceRegistry registry; - DataRelayer relayer(policy, inputs, index, {registry}, -1); + DataRelayer relayer(policy, inputs, index, services.ref(), -1); relayer.setPipelineLength(4); // Let's create a dummy O2 Message with two headers in the stack: @@ -241,7 +281,7 @@ BENCHMARK(BM_RelayMultipleRoutes); /// In this case we have a record with two entries static void BM_RelaySplitParts(benchmark::State& state) { - Monitoring metrics; + BenchmarkServices services; InputSpec spec1{"clusters", "TPC", "CLUSTERS"}; std::vector inputs = { @@ -253,8 +293,7 @@ static void BM_RelaySplitParts(benchmark::State& state) TimesliceIndex index{1, infos}; auto policy = CompletionPolicyHelpers::consumeWhenAny(); - ServiceRegistry registry; - DataRelayer relayer(policy, inputs, index, {registry}, -1); + DataRelayer relayer(policy, inputs, index, services.ref(), -1); relayer.setPipelineLength(4); // Let's create a dummy O2 Message with two headers in the stack: @@ -301,7 +340,7 @@ BENCHMARK(BM_RelaySplitParts)->Arg(10)->Arg(100)->Arg(1000); static void BM_RelayMultiplePayloads(benchmark::State& state) { - Monitoring metrics; + BenchmarkServices services; InputSpec spec1{"clusters", "TPC", "CLUSTERS"}; std::vector inputs = { @@ -313,8 +352,7 @@ static void BM_RelayMultiplePayloads(benchmark::State& state) TimesliceIndex index{1, infos}; auto policy = CompletionPolicyHelpers::consumeWhenAny(); - ServiceRegistry registry; - DataRelayer relayer(policy, inputs, index, {registry}, -1); + DataRelayer relayer(policy, inputs, index, services.ref(), -1); relayer.setPipelineLength(4); // DataHeader matching the one provided in the input From 9c8c644fb9e6d218c78cadaf7fc6c08ab465f250 Mon Sep 17 00:00:00 2001 From: shahoian Date: Thu, 26 Mar 2026 12:59:35 +0100 Subject: [PATCH 413/701] Set GPU field to 0 if L3 current <= 77A --- GPU/GPUTracking/Interface/GPUO2InterfaceUtils.cxx | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.cxx b/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.cxx index b6491c72d83f3..f0efaf48c8725 100644 --- a/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.cxx +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.cxx @@ -145,6 +145,7 @@ uint32_t GPUO2InterfaceUtils::getTpcMaxTimeBinFromNHbf(uint32_t nHbf) float GPUO2InterfaceUtils::getNominalGPUBzFromCurrent(float l3curr) { + // Field for the current below 77A is treated as 0. float al3curr = CAMath::Abs(l3curr); - return (CAMath::Abs(al3curr - 12000) < CAMath::Abs(al3curr - 30000) ? (2.04487f / 12000.f) : (5.00668f / 30000.f)) * l3curr; + return al3curr <= 77 ? 0 : ((CAMath::Abs(al3curr - 12000) < CAMath::Abs(al3curr - 30000) ? (2.04487f / 12000.f) : (5.00668f / 30000.f)) * l3curr); } From a1fe043d4a82f892069f5d88a612ebe480ff466a Mon Sep 17 00:00:00 2001 From: shahoian Date: Tue, 24 Mar 2026 13:10:57 +0100 Subject: [PATCH 414/701] In absence of svertexing pass --disable-strangeness-tracker to AOD producer --- prodtests/full-system-test/dpl-workflow.sh | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/prodtests/full-system-test/dpl-workflow.sh b/prodtests/full-system-test/dpl-workflow.sh index 5b7ffc3cc6547..1e1ea258d395f 100755 --- a/prodtests/full-system-test/dpl-workflow.sh +++ b/prodtests/full-system-test/dpl-workflow.sh @@ -709,7 +709,10 @@ workflow_has_parameter GPU_DISPLAY && [[ $NUMAID == 0 ]] && add_W o2-gpu-display # --------------------------------------------------------------------------------------------------------------------- # AOD -[[ ${SECTVTX_ON:-} != "1" ]] && AODPROD_OPT+=" --disable-secondary-vertices " +if [[ ${SECTVTX_ON:-} != "1" ]]; then + AODPROD_OPT+=" --disable-secondary-vertices " + [[ "0$STRTRACKING" == "0" ]] && STRTRACKING=" --disable-strangeness-tracker " +fi AODPROD_OPT+=" $STRTRACKING " workflow_has_parameter AOD && [[ -n "$AOD_SOURCES" ]] && add_W o2-aod-producer-workflow "$AODPROD_OPT --info-sources $AOD_SOURCES $DISABLE_ROOT_INPUT --aod-writer-keep dangling --aod-writer-resfile \"AO2D\" --aod-writer-resmode UPDATE $DISABLE_MC --pipeline $(get_N aod-producer-workflow AOD REST 1 AODPROD)" From 3d17e85f23f3a6347f77a240368a73bd77a8e2f0 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Fri, 27 Mar 2026 06:51:13 +0100 Subject: [PATCH 415/701] DPL: move away from MessageSet::header / payload (#15228) Rest of the usecases removed. Abstract header / payload retrieval, with the idea that get_header / get_payload will work on any range of fair::mq::MessagePtrs. --- Framework/Core/src/DataRelayer.cxx | 16 ++++++++-------- Framework/Core/test/test_MessageSet.cxx | 8 ++++++++ 2 files changed, 16 insertions(+), 8 deletions(-) diff --git a/Framework/Core/src/DataRelayer.cxx b/Framework/Core/src/DataRelayer.cxx index 7eb851e2aadd8..4cda75ed001b0 100644 --- a/Framework/Core/src/DataRelayer.cxx +++ b/Framework/Core/src/DataRelayer.cxx @@ -213,9 +213,9 @@ DataRelayer::ActivityStats DataRelayer::processDanglingInputs(std::vector(header->GetData()), reinterpret_cast(payload ? payload->GetData() : nullptr), @@ -786,9 +786,9 @@ void DataRelayer::getReadyToProcess(std::vector& comp auto partial = getPartialRecord(li); // TODO: get the data ref from message model auto getter = [&partial](size_t idx, size_t part) { - if (!partial[idx].messages.empty() && partial[idx].header(part).get()) { - auto header = partial[idx].header(part).get(); - auto payload = partial[idx].payload(part).get(); + if (!partial[idx].messages.empty() && (partial[idx].messages | get_header{part}).get()) { + auto header = (partial[idx].messages | get_header{part}).get(); + auto payload = (partial[idx].messages | get_payload{part, 0}).get(); return DataRef{nullptr, reinterpret_cast(header->GetData()), reinterpret_cast(payload ? payload->GetData() : nullptr), @@ -952,10 +952,10 @@ std::vector DataRelayer::consumeExistingInputsForTime // TODO: in the original implementation of the cache, there have been only two messages per entry, // check if the 2 above corresponds to the number of messages. for (size_t pi = 0; pi < (cache[cacheId].messages | count_parts{}); pi++) { - auto& header = cache[cacheId].header(pi); + auto& header = cache[cacheId].messages | get_header{pi}; auto&& newHeader = header->GetTransport()->CreateMessage(); newHeader->Copy(*header); - messages[arg].add(PartRef{std::move(newHeader), std::move(cache[cacheId].payload(pi))}); + messages[arg].add(PartRef{std::move(newHeader), std::move(cache[cacheId].messages | get_payload{pi, 0})}); } }; diff --git a/Framework/Core/test/test_MessageSet.cxx b/Framework/Core/test/test_MessageSet.cxx index 290e55220d6cb..aa7b49c1d1d3c 100644 --- a/Framework/Core/test/test_MessageSet.cxx +++ b/Framework/Core/test/test_MessageSet.cxx @@ -250,6 +250,14 @@ TEST_CASE("GetHeaderPayloadOperators") auto& pl1 = msgSet.messages | get_payload{1, 0}; REQUIRE(pl1.get() != nullptr); REQUIRE(pl1->GetSize() == 200); + + // Validate pipe operators match old API + for (size_t i = 0; i < 2; ++i) { + REQUIRE(&(msgSet.messages | get_header{i}) == &msgSet.header(i)); + REQUIRE(&(msgSet.messages | get_payload{i, 0}) == &msgSet.payload(i, 0)); + } + REQUIRE((msgSet.messages | count_parts{}) == msgSet.messageMap.size()); + REQUIRE((msgSet.messages | count_payloads{}) == msgSet.pairMap.size()); } TEST_CASE("GetHeaderPayloadMultiPayload") From 6e2f625c53a8ed3f2baf8aa01b4fecd929420ee3 Mon Sep 17 00:00:00 2001 From: Oliver <45767754+OliverRietmann@users.noreply.github.com> Date: Fri, 27 Mar 2026 07:23:53 +0100 Subject: [PATCH 416/701] Write standalone --debug output to CSV file --- GPU/GPUTracking/Base/GPUReconstructionCPU.cxx | 29 ++--- GPU/GPUTracking/Base/GPUReconstructionCPU.h | 13 +++ .../Base/GPUReconstructionDebug.cxx | 107 ++++++++++++++++++ GPU/GPUTracking/Definitions/GPUSettingsList.h | 2 + .../Standalone/Benchmark/standalone.cxx | 8 +- 5 files changed, 140 insertions(+), 19 deletions(-) diff --git a/GPU/GPUTracking/Base/GPUReconstructionCPU.cxx b/GPU/GPUTracking/Base/GPUReconstructionCPU.cxx index 409c28b8bf328..752b5f27ded3f 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionCPU.cxx +++ b/GPU/GPUTracking/Base/GPUReconstructionCPU.cxx @@ -35,6 +35,7 @@ #include #include +#include #ifndef _WIN32 #include @@ -230,6 +231,7 @@ int32_t GPUReconstructionCPU::RunChains() GPUInfo("Allocated memory when starting processing %34s", ""); PrintMemoryOverview(); } + mTimerTotal.Start(); const std::clock_t cpuTimerStart = std::clock(); int32_t retVal = 0; @@ -264,7 +266,10 @@ int32_t GPUReconstructionCPU::RunChains() double kernelTotal = 0; std::vector kernelStepTimes(gpudatatypes::N_RECO_STEPS, 0.); + debugWriter writer(GetProcessingSettings().debugCSV, GetProcessingSettings().debugMarkdown, mStatNEvents); + if (GetProcessingSettings().debugLevel >= 1) { + writer.header(); for (uint32_t i = 0; i < mTimers.size(); i++) { double time = 0; if (mTimers[i] == nullptr) { @@ -284,11 +289,7 @@ int32_t GPUReconstructionCPU::RunChains() int32_t stepNum = getRecoStepNum(mTimers[i]->step); kernelStepTimes[stepNum] += time; } - char bandwidth[256] = ""; - if (mTimers[i]->memSize && mStatNEvents && time != 0.) { - snprintf(bandwidth, 256, " (%8.3f GB/s - %'14zu bytes - %'14zu per call)", mTimers[i]->memSize / time * 1e-9, mTimers[i]->memSize / mStatNEvents, mTimers[i]->memSize / mStatNEvents / mTimers[i]->count); - } - printf("Execution Time: Task (%c %8ux): %50s Time: %'10.0f us%s\n", type == 0 ? 'K' : 'C', mTimers[i]->count, mTimers[i]->name.c_str(), time * 1000000 / mStatNEvents, bandwidth); + writer.row('K', mTimers[i]->count, mTimers[i]->name.c_str(), time, -1.0, -1.0, mTimers[i]->memSize); if (GetProcessingSettings().resetTimers) { mTimers[i]->count = 0; mTimers[i]->memSize = 0; @@ -298,16 +299,13 @@ int32_t GPUReconstructionCPU::RunChains() if (GetProcessingSettings().recoTaskTiming) { 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()); + writer.row(' ', 0, std::string(gpudatatypes::RECO_STEP_NAMES[i]) + " (Tasks)", kernelStepTimes[i], mTimersRecoSteps[i].timerCPU, mTimersRecoSteps[i].timerTotal.GetElapsedTime(), 0); } 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, - mTimersRecoSteps[i].bytesToGPU / mTimersRecoSteps[i].timerToGPU.GetElapsedTime() * 1e-9, mTimersRecoSteps[i].bytesToGPU / mStatNEvents, mTimersRecoSteps[i].bytesToGPU / mTimersRecoSteps[i].countToGPU); + writer.row('D', mTimersRecoSteps[i].countToGPU, std::string(gpudatatypes::RECO_STEP_NAMES[i]) + " (DMA to GPU)", mTimersRecoSteps[i].timerToGPU.GetElapsedTime(), -1.0, -1.0, mTimersRecoSteps[i].bytesToGPU); } 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, - mTimersRecoSteps[i].bytesToHost / mTimersRecoSteps[i].timerToHost.GetElapsedTime() * 1e-9, mTimersRecoSteps[i].bytesToHost / mStatNEvents, mTimersRecoSteps[i].bytesToHost / mTimersRecoSteps[i].countToHost); + writer.row('D', mTimersRecoSteps[i].countToHost, std::string(gpudatatypes::RECO_STEP_NAMES[i]) + " (DMA to Host)", mTimersRecoSteps[i].timerToHost.GetElapsedTime(), -1.0, -1.0, mTimersRecoSteps[i].bytesToHost); } if (GetProcessingSettings().resetTimers) { mTimersRecoSteps[i].bytesToGPU = mTimersRecoSteps[i].bytesToHost = 0; @@ -321,14 +319,11 @@ int32_t GPUReconstructionCPU::RunChains() } 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); + writer.row(' ', 0, gpudatatypes::GENERAL_STEP_NAMES[i], mTimersGeneralSteps[i].GetElapsedTime(), -1.0, -1.0, 0); } } - if (GetProcessingSettings().debugLevel >= 1) { - mStatKernelTime = kernelTotal * 1000000 / mStatNEvents; - printf("Execution Time: Total : %50s Time: %'10.0f us%s\n", "Total Kernel", mStatKernelTime, nEventReport.c_str()); - } - printf("Execution Time: Total : %50s Time: %'10.0f us ( CPU Time : %'10.0f us, %7.2fx ) %s\n", "Total Wall", mStatWallTime, mStatCPUTime * 1000000 / mStatNEvents, mStatCPUTime / mTimerTotal.GetElapsedTime(), nEventReport.c_str()); + double gpu_time = GetProcessingSettings().debugLevel >= 1 ? kernelTotal : -1.0; + writer.row(' ', 0, "Wall", gpu_time, mStatCPUTime, mTimerTotal.GetElapsedTime(), 0, nEventReport); } else if (GetProcessingSettings().debugLevel >= 0) { GPUInfo("Total Wall Time: %10.0f us%s", mStatWallTime, nEventReport.c_str()); } diff --git a/GPU/GPUTracking/Base/GPUReconstructionCPU.h b/GPU/GPUTracking/Base/GPUReconstructionCPU.h index d621d45fcd92b..466ad318bbe3b 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionCPU.h +++ b/GPU/GPUTracking/Base/GPUReconstructionCPU.h @@ -16,7 +16,9 @@ #define GPURECONSTRUCTIONICPU_H #include "GPUReconstructionProcessing.h" +#include #include +#include #include namespace Ort @@ -100,6 +102,17 @@ class GPUReconstructionCPU : public GPUReconstructionProcessing::KernelInterface size_t TransferMemoryResourcesHelper(GPUProcessor* proc, int32_t stream, bool all, bool toGPU); template void runKernelInterface(krnlSetup&& setup, Args const&... args); + + struct debugWriter { + debugWriter(std::string filenameCSV, bool markdown, uint32_t statNEvents); + void header(); + void row(char type, uint32_t count, std::string name, double gpu_time, double cpu_time, double total_time, std::size_t memSize, std::string nEventReport = ""); + + private: + std::ofstream streamCSV; + bool mMarkdown; + uint32_t mStatNEvents; + }; }; } // namespace o2::gpu diff --git a/GPU/GPUTracking/Base/GPUReconstructionDebug.cxx b/GPU/GPUTracking/Base/GPUReconstructionDebug.cxx index c1c31eedde1b2..564c04ba7f745 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionDebug.cxx +++ b/GPU/GPUTracking/Base/GPUReconstructionDebug.cxx @@ -13,6 +13,7 @@ /// \author David Rohr #include "GPUReconstruction.h" +#include "GPUReconstructionCPU.h" #include "GPULogging.h" #include "GPUSettings.h" @@ -23,6 +24,8 @@ #include #include #include +#include +#include using namespace o2::gpu; @@ -186,3 +189,107 @@ bool GPUReconstruction::triggerDebugDump() } return false; } + +GPUReconstructionCPU::debugWriter::debugWriter(std::string filenameCSV, bool markdown, uint32_t statNEvents) : mMarkdown{markdown}, mStatNEvents{statNEvents} +{ + if (!filenameCSV.empty()) { + streamCSV.open(filenameCSV, std::ios::out | std::ios::app); + } +} + +void GPUReconstructionCPU::debugWriter::header() +{ + if (streamCSV.is_open() && !streamCSV.tellp()) { + streamCSV << "type,count,name,gpu (us),cpu (us),cpu/total,total (us),GB/s,bytes,bytes/call\n"; + } + + if (mMarkdown) { + std::cout << "| | count | name | gpu (us) | cpu (us) | cpu/tot | tot (us) | GB/s | bytes | bytes/call |\n"; + std::cout << "|---|--------|-------------------------------------------|-----------|-----------|---------|-----------|-----------|---------------|---------------|\n"; + } +} + +void GPUReconstructionCPU::debugWriter::row(char type, uint32_t count, std::string name, double gpu_time, double cpu_time, double total_time, std::size_t memSize, std::string nEventReport) +{ + double scale = 1000000.0 / mStatNEvents; + + if (streamCSV.is_open()) { + streamCSV << type << ","; + if (count != 0) + streamCSV << count; + streamCSV << "," << name << ","; + if (gpu_time != -1.0) + streamCSV << std::format("{:.0f}", gpu_time * scale); + streamCSV << ","; + if (cpu_time != -1.0) + streamCSV << std::format("{:.0f}", cpu_time * scale); + streamCSV << ","; + if (cpu_time != -1.0 && total_time != -1.0) + streamCSV << std::format("{:.2f}", cpu_time / total_time); + streamCSV << ","; + if (total_time != -1.0) + streamCSV << std::format("{:.0f}", total_time * scale); + streamCSV << ","; + if (memSize != 0 && count != 0) + streamCSV << std::format("{:.3f},{},{}", memSize / gpu_time * 1e-9, memSize / mStatNEvents, memSize / mStatNEvents / count); + else + streamCSV << ",,"; + streamCSV << std::endl; + } + + if (mMarkdown) { + std::cout << "| " << type << " | "; + if (count != 0) + std::cout << std::format("{:6} |", count); + else + std::cout << " |"; + std::cout << std::format(" {:42}|", name); + if (gpu_time != -1.0) + std::cout << std::format("{:10.0f} |", gpu_time * scale); + else + std::cout << " |"; + if (cpu_time != -1.0) + std::cout << std::format("{:10.0f} |", cpu_time * scale); + else + std::cout << " |"; + if (cpu_time != -1.0 && total_time != -1.0) + std::cout << std::format("{:8.2f} |", cpu_time / total_time); + else + std::cout << " |"; + if (total_time != -1.0) + std::cout << std::format("{:10.0f} |", total_time * scale); + else + std::cout << " |"; + if (memSize != 0 && count != 0) + std::cout << std::format("{:10.3f} |{:14} |{:14} |", memSize / gpu_time * 1e-9, memSize / mStatNEvents, memSize / mStatNEvents / count); + else + std::cout << " | | |"; + std::cout << std::endl; + } else { + if (name.substr(0, 3) == "GPU") { + char bandwidth[256] = ""; + if (memSize && mStatNEvents && gpu_time != 0.0) { + snprintf(bandwidth, 256, " (%8.3f GB/s - %'14zu bytes - %'14zu per call)", memSize / gpu_time * 1e-9, memSize / mStatNEvents, memSize / mStatNEvents / count); + } + printf("Execution Time: Task (%c %8ux): %50s Time: %'10.0f us%s\n", type, count, name.c_str(), gpu_time * scale, bandwidth); + } else if (name.substr(0, 3) == "TPC") { + std::size_t n = name.find('('); + std::string basename = name.substr(0, n - 1); + std::string postfix = name.substr(n + 1, name.size() - n - 2); + if (total_time != -1.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", postfix.c_str(), + basename.c_str(), gpu_time * scale, "", total_time * scale, cpu_time * scale, cpu_time / total_time); + } else { + printf("Execution Time: Step (D %8ux): %11s %38s Time: %'10.0f us (%8.3f GB/s - %'14zu bytes - %'14zu per call)\n", count, postfix.c_str(), basename.c_str(), gpu_time * scale, + memSize / gpu_time * 1e-9, memSize / mStatNEvents, memSize / mStatNEvents / count); + } + } else if (name == "Prepare") { + printf("Execution Time: General Step : %50s Time: %'10.0f us\n", name.c_str(), gpu_time * scale); + } else if (name == "Wall") { + if (gpu_time != -1.0) { + printf("Execution Time: Total : %50s Time: %'10.0f us%s\n", "Total Kernel", gpu_time * scale, nEventReport.c_str()); + } + printf("Execution Time: Total : %50s Time: %'10.0f us ( CPU Time : %'10.0f us, %7.2fx ) %s\n", "Total Wall", total_time * scale, cpu_time * scale, cpu_time / total_time, nEventReport.c_str()); + } + } +} diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index 57cb1371a4aa0..606deb44d9528 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -329,6 +329,8 @@ AddOption(debugLevel, int32_t, -1, "debug", 'd', "Set debug level (-2 = silent, AddOption(allocDebugLevel, int32_t, 0, "allocDebug", 0, "Some debug output for memory allocations (without messing with normal debug level)") AddOption(debugMask, uint32_t, (1 << 18) - 1, "debugMask", 0, "Mask for debug output dumps to file") AddOption(debugLogSuffix, std::string, "", "debugSuffix", 0, "Suffix for debug log files with --debug 6") +AddOption(debugCSV, std::string, "", "", 0, "CSV filename to append the benchmark results. Verbosity determined by parameter --debug.") +AddOption(debugMarkdown, bool, false, "", 0, "Print the results of standlaone benchmarks in markdown format") 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 or deterministic compile flag set", def(1)) diff --git a/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx b/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx index a2e74c45fcb86..2a2f7adea8cb9 100644 --- a/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx +++ b/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx @@ -627,7 +627,9 @@ int32_t RunBenchmark(GPUReconstruction* recUse, GPUChainTracking* chainTrackingU if (configStandalone.runs > 1) { printf("Run %d (thread %d)\n", iteration + 1, threadId); } - recUse->SetResetTimers(iRun < configStandalone.runsInit); + if (configStandalone.runsInit > 0 && configStandalone.proc.debugCSV.empty()) { + recUse->SetResetTimers(iRun < configStandalone.runsInit); + } if (configStandalone.outputcontrolmem) { recUse->SetOutputControl(threadId ? outputmemoryPipeline.get() : outputmemory.get(), configStandalone.outputcontrolmem); } @@ -685,7 +687,9 @@ int32_t RunBenchmark(GPUReconstruction* recUse, GPUChainTracking* chainTrackingU chainTrackingAsync->mIOPtrs.nRawClusters[i] = 0; } chainTrackingAsync->mIOPtrs.clustersNative = nullptr; - recAsync->SetResetTimers(iRun < configStandalone.runsInit); + if (configStandalone.runsInit > 0 && configStandalone.proc.debugCSV.empty()) { + recAsync->SetResetTimers(iRun < configStandalone.runsInit); + } tmpRetVal = recAsync->RunChains(); if (tmpRetVal == 0 || tmpRetVal == 2) { OutputStat(chainTrackingAsync, nullptr, nullptr); From 85d4143e39ee608db1a130471cdeb1cbaf09fe52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Fri, 27 Mar 2026 09:08:29 +0100 Subject: [PATCH 417/701] [ALICE3] IOTOF: fix non segmented layers (#15195) --- Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx index 9b097a0243597..61720f2172b92 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx @@ -101,7 +101,7 @@ void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::str const double staveTiltAngle = itofSegmented ? 10.0 : 0.0; // degrees const int modulesPerStave = itofSegmented ? 10 : 0; // number of modules per stave in segmented case mITOFLayer = ITOFLayer(name, - dInnerTof.first, 0.f, dInnerTof.second, 0.f, x2x0, ITOFLayer::kBarrelSegmented, + dInnerTof.first, 0.f, dInnerTof.second, 0.f, x2x0, itofSegmented ? ITOFLayer::kBarrelSegmented : ITOFLayer::kBarrel, nStaves, staveWidth, staveTiltAngle, modulesPerStave); } if (otof) { // oTOF @@ -111,7 +111,7 @@ void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::str const double staveTiltAngle = otofSegmented ? 5.0 : 0.0; // degrees const int modulesPerStave = otofSegmented ? 54 : 0; // number of modules per stave in segmented case mOTOFLayer = OTOFLayer(name, - dOuterTof.first, 0.f, dOuterTof.second, 0.f, x2x0, OTOFLayer::kBarrelSegmented, + dOuterTof.first, 0.f, dOuterTof.second, 0.f, x2x0, otofSegmented ? OTOFLayer::kBarrelSegmented : OTOFLayer::kBarrel, nStaves, staveWidth, staveTiltAngle, modulesPerStave); } if (ftof) { From 081240b92abe7fa9c4110000e82795e641557998 Mon Sep 17 00:00:00 2001 From: shahor02 Date: Fri, 27 Mar 2026 09:25:06 +0100 Subject: [PATCH 418/701] Fix BasicCCDBManager::isCacheValid(ts) method (#15223) --- CCDB/include/CCDB/BasicCCDBManager.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CCDB/include/CCDB/BasicCCDBManager.h b/CCDB/include/CCDB/BasicCCDBManager.h index fd0fe7aa6d05b..b76beb2576eb3 100644 --- a/CCDB/include/CCDB/BasicCCDBManager.h +++ b/CCDB/include/CCDB/BasicCCDBManager.h @@ -64,8 +64,8 @@ class CCDBManagerInstance bool isValid(long ts) { return ts < endvalidity && ts >= startvalidity; } bool isCacheValid(long ts) { - LOGP(debug, "isCacheValid : {} : {} : {} --> {}", cacheValidFrom, ts, cacheValidUntil, ts < cacheValidUntil && ts >= cacheValidFrom); - return ts < cacheValidUntil && ts >= cacheValidFrom; + LOGP(debug, "isCacheValid : {} : {} : {} --> {}", cacheValidFrom, ts, cacheValidUntil, isValid(ts)); + return ts < cacheValidUntil && isValid(ts); } void clear() { From ea49c665efc212679910be88db82651dfec588a9 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Fri, 27 Mar 2026 10:37:28 +0100 Subject: [PATCH 419/701] ITS3: alignment code (#15161) * ITS3: template detector coord conversion on input type Signed-off-by: Felix Schlepper * ITS3: alignment Signed-off-by: Felix Schlepper * Replace math.h with cmath in AlignmentHierarchy.cxx Removed math.h and added cmath for better compatibility. --------- Signed-off-by: Felix Schlepper --- .../Upgrades/ITS3/alignment/CMakeLists.txt | 37 +- Detectors/Upgrades/ITS3/alignment/README.md | 30 + .../include/ITS3Align/AlignmentHierarchy.h | 339 ++++++ .../include/ITS3Align/AlignmentParams.h | 67 ++ .../include/ITS3Align/AlignmentSensors.h | 41 + .../include/ITS3Align/AlignmentSpec.h | 34 + .../include/ITS3Align/AlignmentTypes.h | 64 ++ .../include/ITS3Align/Deformations.h | 84 -- .../include/ITS3Align/MisalignmentHits.h | 216 ---- .../include/ITS3Align/MisalignmentManager.h | 53 - .../ITS3Align/MisalignmentParameters.h | 93 -- .../alignment/include/ITS3Align/TrackFit.h | 175 +++ .../ITS3/alignment/src/AlignmentHierarchy.cxx | 486 ++++++++ .../ITS3/alignment/src/AlignmentParams.cxx | 13 + .../ITS3/alignment/src/AlignmentSensors.cxx | 201 ++++ .../ITS3/alignment/src/AlignmentSpec.cxx | 1003 +++++++++++++++++ .../ITS3/alignment/src/AlignmentTypes.cxx | 24 + .../ITS3/alignment/src/Deformations.cxx | 41 - .../ITS3/alignment/src/ITS3AlignLinkDef.h | 8 +- .../ITS3/alignment/src/MisalignmentHits.cxx | 368 ------ .../alignment/src/MisalignmentManager.cxx | 195 ---- .../alignment/src/MisalignmentParameters.cxx | 80 -- .../ITS3/alignment/src/alignment-workflow.cxx | 71 ++ .../ITS3/base/include/ITS3Base/ITS3Params.h | 5 - .../include/ITS3Base/SegmentationMosaix.h | 19 +- .../ITS3/base/include/ITS3Base/SpecsV2.h | 2 +- .../Upgrades/ITS3/macros/align/CMakeLists.txt | 6 +- .../ITS3/macros/align/CheckHitResiduals.C | 131 +++ .../macros/align/CreateMisalignmentITS3.C | 94 -- .../ITS3/macros/align/MisAlignGeoITS3.notest | 129 --- .../ITS3/macros/align/ShowCoefficients.C | 333 ------ .../ITS3/macros/align/TestLegendrePol.C | 257 ----- .../include/ITS3Reconstruction/IOUtils.h | 23 +- .../ITS3/reconstruction/src/IOUtils.cxx | 46 +- .../reconstruction/src/TopologyDictionary.cxx | 2 + Detectors/Upgrades/ITS3/study/CMakeLists.txt | 6 +- .../ITS3TrackingStudyParam.h | 18 +- .../include/ITS3TrackingStudy/TrackingStudy.h | 2 +- .../Upgrades/ITS3/study/macros/CMakeLists.txt | 8 + .../ITS3/study/macros/PlotMisalignment.C | 228 ++++ .../ITS3/study/macros/PlotResiduals.C | 70 ++ .../Upgrades/ITS3/study/src/TrackingStudy.cxx | 667 ++++++++--- .../src/its3-tracking-study-workflow.cxx | 8 +- Steer/DigitizerWorkflow/CMakeLists.txt | 1 - .../src/ITS3DigitizerSpec.cxx | 6 - 45 files changed, 3627 insertions(+), 2157 deletions(-) create mode 100644 Detectors/Upgrades/ITS3/alignment/README.md create mode 100644 Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentHierarchy.h create mode 100644 Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentParams.h create mode 100644 Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentSensors.h create mode 100644 Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentSpec.h create mode 100644 Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentTypes.h delete mode 100644 Detectors/Upgrades/ITS3/alignment/include/ITS3Align/Deformations.h delete mode 100644 Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentHits.h delete mode 100644 Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentManager.h delete mode 100644 Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentParameters.h create mode 100644 Detectors/Upgrades/ITS3/alignment/include/ITS3Align/TrackFit.h create mode 100644 Detectors/Upgrades/ITS3/alignment/src/AlignmentHierarchy.cxx create mode 100644 Detectors/Upgrades/ITS3/alignment/src/AlignmentParams.cxx create mode 100644 Detectors/Upgrades/ITS3/alignment/src/AlignmentSensors.cxx create mode 100644 Detectors/Upgrades/ITS3/alignment/src/AlignmentSpec.cxx create mode 100644 Detectors/Upgrades/ITS3/alignment/src/AlignmentTypes.cxx delete mode 100644 Detectors/Upgrades/ITS3/alignment/src/Deformations.cxx delete mode 100644 Detectors/Upgrades/ITS3/alignment/src/MisalignmentHits.cxx delete mode 100644 Detectors/Upgrades/ITS3/alignment/src/MisalignmentManager.cxx delete mode 100644 Detectors/Upgrades/ITS3/alignment/src/MisalignmentParameters.cxx create mode 100644 Detectors/Upgrades/ITS3/alignment/src/alignment-workflow.cxx create mode 100644 Detectors/Upgrades/ITS3/macros/align/CheckHitResiduals.C delete mode 100644 Detectors/Upgrades/ITS3/macros/align/CreateMisalignmentITS3.C delete mode 100644 Detectors/Upgrades/ITS3/macros/align/MisAlignGeoITS3.notest delete mode 100644 Detectors/Upgrades/ITS3/macros/align/ShowCoefficients.C delete mode 100644 Detectors/Upgrades/ITS3/macros/align/TestLegendrePol.C create mode 100644 Detectors/Upgrades/ITS3/study/macros/PlotMisalignment.C create mode 100644 Detectors/Upgrades/ITS3/study/macros/PlotResiduals.C diff --git a/Detectors/Upgrades/ITS3/alignment/CMakeLists.txt b/Detectors/Upgrades/ITS3/alignment/CMakeLists.txt index f89ad821c65e7..0bc8080c7a1b8 100644 --- a/Detectors/Upgrades/ITS3/alignment/CMakeLists.txt +++ b/Detectors/Upgrades/ITS3/alignment/CMakeLists.txt @@ -9,18 +9,37 @@ # 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(ITS3Align - SOURCES src/MisalignmentParameters.cxx - src/MisalignmentHits.cxx - src/MisalignmentManager.cxx - src/Deformations.cxx + TARGETVARNAME targetName + SOURCES src/AlignmentHierarchy.cxx + src/AlignmentSensors.cxx + src/AlignmentParams.cxx + src/AlignmentTypes.cxx + src/AlignmentSpec.cxx PUBLIC_LINK_LIBRARIES O2::MathUtils O2::Steer O2::ITSBase - O2::ITSMFTSimulation) + O2::ITSMFTSimulation + O2::ITS3Reconstruction + O2::Framework + O2::GlobalTrackingWorkflowReaders + O2::GlobalTrackingWorkflowHelpers + O2::DataFormatsGlobalTracking + O2::DetectorsVertexing + nlohmann_json::nlohmann_json + GBL::GBL) +if (OpenMP_CXX_FOUND) + target_compile_definitions(${targetName} PRIVATE WITH_OPENMP) + target_link_libraries(${targetName} PRIVATE OpenMP::OpenMP_CXX) +endif() o2_target_root_dictionary(ITS3Align - HEADERS include/ITS3Align/MisalignmentParameters.h - include/ITS3Align/MisalignmentHits.h - include/ITS3Align/MisalignmentHits.h - include/ITS3Align/Deformations.h) + HEADERS include/ITS3Align/AlignmentParams.h + include/ITS3Align/AlignmentTypes.h) + + +o2_add_executable(alignment-workflow + SOURCES src/alignment-workflow.cxx + COMPONENT_NAME its3 + PUBLIC_LINK_LIBRARIES O2::ITS3Align) diff --git a/Detectors/Upgrades/ITS3/alignment/README.md b/Detectors/Upgrades/ITS3/alignment/README.md new file mode 100644 index 0000000000000..62633d1d7d313 --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/README.md @@ -0,0 +1,30 @@ +# Simulate ITS3 misalignment and re-alignment + + +```bash +o2-its3-alignment-workflow --track-sources ITS --output MilleData,MilleSteer --configKeyValues "ITS3AlignmentParams.minPt=0.1;ITS3AlignmentParams.doMisalignmentLeg=true;ITS3AlignmentParams.doMisalignmentRB=true;ITS3AlignmentParams.misAlgJson=test_closure.json;ITS3AlignmentParams.extraClsErrZ[0]=10e-4;ITS3AlignmentParams.extraClsErrY[0]=10e-4;ITS3AlignmentParams.extraClsErrZ[3]=10e-4;ITS3AlignmentParams.extraClsErrY[3]=10e-4;ITS3AlignmentParams.dofConfigJson=dofSet.json" -b --run +``` + +test_closure.json: +```json +[ + { + "id": 0, + "rigidBody": [0.001, 0.0005, 0.0, 0.0, 0.0001, 0.0], + "matrix": [[0.0], [0.0008, 0.0], [0.0, 0.0, 0.0], [0.0, 0.0, 0.0, 0.0]] + } +] +``` + +dofSet.json: +```json +{ + "defaults": { "rigidBody": "fixed" }, + "rules": [ + { + "match": "ITS3Layer0/ITS3CarbonForm0", + "rigidBody": ["TX", "TY", "RY"], + "calib": { "type": "legendre", "order": 1, "fix": [0, 2] } + } + ] +}``` diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentHierarchy.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentHierarchy.h new file mode 100644 index 0000000000000..04b8157084d0a --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentHierarchy.h @@ -0,0 +1,339 @@ +// 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. + +#ifndef O2_ITS3_ALIGNMENT_HIERARCHY_H +#define O2_ITS3_ALIGNMENT_HIERARCHY_H + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include + +namespace o2::its3::align +{ +using Matrix36 = Eigen::Matrix; +using Matrix66 = Eigen::Matrix; + +// indices for rigid body parameters in LOC frame +enum RigidBodyDOF : uint8_t { + TX = 0, + TY, + TZ, + RX, + RY, + RZ, + NDOF, +}; +static constexpr const char* RigidBodyDOFNames[RigidBodyDOF::NDOF] = {"TX", "TY", "TZ", "RX", "RY", "RZ"}; + +// return the rigid body derivatives +// trk has be at in the measurment frame +auto getRigidBodyDerivatives(const auto& trk) +{ + // calculate slopes + const double tgl = trk.getTgl(), snp = trk.getSnp(); + const double csp = 1. / sqrt(1. + (tgl * tgl)); + const double u = trk.getY(), v = trk.getZ(); + const double uP = snp * csp, vP = tgl * csp; + Matrix36 der; + der.setZero(); + // columns: Tt, Tu, Tv, Rt, Ru, Rv + // (X) (Y) (Z) (RX) (RY) (RZ) + der << uP, -1., 0., v, v * uP, -u * uP, + vP, 0., -1., -u, v * vP, -u * vP; + return der; +} + +class DOFSet +{ + public: + enum class Type : uint8_t { RigidBody, + Legendre }; + virtual ~DOFSet() = default; + virtual Type type() const = 0; + int nDOFs() const { return static_cast(mFree.size()); } + virtual std::string dofName(int idx) const = 0; + bool isFree(int idx) const { return mFree[idx]; } + void setFree(int idx, bool f) { mFree[idx] = f; } + void setAllFree(bool f) { std::fill(mFree.begin(), mFree.end(), f); } + int nFreeDOFs() const + { + int n = 0; + for (bool f : mFree) { + n += f; + } + return n; + } + + protected: + DOFSet(int n) : mFree(n, true) {} + std::vector mFree; +}; + +class RigidBodyDOFSet final : public DOFSet +{ + public: + static constexpr int NDOF = RigidBodyDOF::NDOF; + RigidBodyDOFSet() : DOFSet(NDOF) {} + // mask: bitmask of free DOFs (bit i = DOF i is free) + explicit RigidBodyDOFSet(uint8_t mask) : DOFSet(NDOF) + { + for (int i = 0; i < NDOF; ++i) { + mFree[i] = (mask >> i) & 1; + } + } + Type type() const override { return Type::RigidBody; } + std::string dofName(int idx) const override { return RigidBodyDOFNames[idx]; } + uint8_t mask() const + { + uint8_t m = 0; + for (int i = 0; i < NDOF; ++i) { + m |= (uint8_t(mFree[i]) << i); + } + return m; + } +}; + +class LegendreDOFSet final : public DOFSet +{ + public: + explicit LegendreDOFSet(int order) : DOFSet((order + 1) * (order + 2) / 2), mOrder(order) {} + Type type() const override { return Type::Legendre; } + int order() const { return mOrder; } + std::string dofName(int idx) const override + { + int i = 0; + while ((i + 1) * (i + 2) / 2 <= idx) { + ++i; + } + int j = idx - (i * (i + 1) / 2); + return std::format("L({},{})", i, j); + } + + private: + int mOrder; +}; + +class GlobalLabel +{ + // Millepede label is any positive integer [1....) + // Layout: DOF(5) | CALIB(1) | ID(22) | SENS(1) | DET(2) = 31 usable bits (MSB reserved, GBL uses signed int) + public: + using T = uint32_t; + static constexpr int DOF_BITS = 5; // bits 0-4 + static constexpr int CALIB_BITS = 1; // bit 5: 0 = rigid body, 1 = calibration + static constexpr int ID_BITS = 22; // bits 6-27 + static constexpr int SENS_BITS = 1; // bit 28 + static constexpr int TOTAL_BITS = sizeof(T) * 8; + static constexpr int DET_BITS = TOTAL_BITS - (DOF_BITS + CALIB_BITS + ID_BITS + SENS_BITS) - 1; // one less bit since GBL uses int! + static constexpr T bitMask(int b) noexcept + { + return (T(1) << b) - T(1); + } + static constexpr int DOF_SHIFT = 0; + static constexpr T DOF_MAX = (T(1) << DOF_BITS) - T(1); + static constexpr T DOF_MASK = DOF_MAX << DOF_SHIFT; + static constexpr int CALIB_SHIFT = DOF_BITS; + static constexpr T CALIB_MAX = (T(1) << CALIB_BITS) - T(1); + static constexpr T CALIB_MASK = CALIB_MAX << CALIB_SHIFT; + static constexpr int ID_SHIFT = DOF_BITS + CALIB_BITS; + static constexpr T ID_MAX = (T(1) << ID_BITS) - T(1); + static constexpr T ID_MASK = ID_MAX << ID_SHIFT; + static constexpr int SENS_SHIFT = DOF_BITS + CALIB_BITS + ID_BITS; + static constexpr T SENS_MAX = (T(1) << SENS_BITS) - T(1); + static constexpr T SENS_MASK = SENS_MAX << SENS_SHIFT; + static constexpr int DET_SHIFT = DOF_BITS + CALIB_BITS + ID_BITS + SENS_BITS; + static constexpr T DET_MAX = (T(1) << DET_BITS) - T(1); + static constexpr T DET_MASK = DET_MAX << DET_SHIFT; + + GlobalLabel(T det, T id, bool sens, bool calib = false) + : mID((((id + 1) & ID_MAX) << ID_SHIFT) | + ((det & DET_MAX) << DET_SHIFT) | + ((T(sens) & SENS_MAX) << SENS_SHIFT) | + ((T(calib) & CALIB_MAX) << CALIB_SHIFT)) + { + } + + /// produce the raw Millepede label for a given DOF index (rigid body: calib=0 in label) + constexpr T raw(T dof) const noexcept { return (mID & ~DOF_MASK) | ((dof & DOF_MAX) << DOF_SHIFT); } + constexpr int rawGBL(T dof) const noexcept { return static_cast(raw(dof)); } + + /// return a copy of this label with the CALIB bit set (for calibration DOFs on same volume) + GlobalLabel asCalib() const noexcept + { + GlobalLabel c{*this}; + c.mID |= (T(1) << CALIB_SHIFT); + return c; + } + + constexpr T id() const noexcept { return ((mID >> ID_SHIFT) & ID_MAX) - 1; } + constexpr T det() const noexcept { return (mID & DET_MASK) >> DET_SHIFT; } + constexpr bool sens() const noexcept { return (mID & SENS_MASK) >> SENS_SHIFT; } + constexpr bool calib() const noexcept { return (mID & CALIB_MASK) >> CALIB_SHIFT; } + + std::string asString() const + { + return std::format("Det:{} Id:{} Sens:{} Calib:{}", det(), id(), sens(), calib()); + } + + constexpr auto operator<=>(const GlobalLabel&) const noexcept = default; + + private: + T mID{0}; +}; + +class HierarchyConstraint +{ + public: + HierarchyConstraint(std::string name, double value) : mName(std::move(name)), mValue(value) {} + void add(uint32_t lab, double coeff) + { + mLabels.push_back(lab); + mCoeff.push_back(coeff); + } + void write(std::ostream& os) const; + auto getSize() const noexcept { return mLabels.size(); } + + private: + std::string mName; // name of the constraint + double mValue{0.0}; // constraint value + std::vector mLabels; // parameter labels + std::vector mCoeff; // their coefficients +}; + +// --- AlignableVolume --- + +class AlignableVolume +{ + public: + using Ptr = std::unique_ptr; + using SensorMapping = std::map; + + AlignableVolume(const AlignableVolume&) = delete; + AlignableVolume(AlignableVolume&&) = delete; + AlignableVolume& operator=(const AlignableVolume&) = delete; + AlignableVolume& operator=(AlignableVolume&&) = delete; + AlignableVolume(const char* symName, uint32_t label, uint32_t det, bool sens); + AlignableVolume(const char* symName, GlobalLabel label); + virtual ~AlignableVolume() = default; + + void finalise(uint8_t level = 0); + + // steering file output + void writeRigidBodyConstraints(std::ostream& os) const; + void writeParameters(std::ostream& os) const; + void writeTree(std::ostream& os, int indent = 0) const; + + // tree-like + auto getLevel() const noexcept { return mLevel; } + bool isRoot() const noexcept { return mParent == nullptr; } + bool isLeaf() const noexcept { return mChildren.empty(); } + template + requires std::derived_from + AlignableVolume* addChild(const char* symName, uint32_t label, uint32_t det, bool sens) + { + auto c = std::make_unique(symName, label, det, sens); + return setParent(std::move(c)); + } + template + requires std::derived_from + AlignableVolume* addChild(const char* symName, GlobalLabel lbl) + { + auto c = std::make_unique(symName, lbl); + return setParent(std::move(c)); + } + + // bfs traversal + void traverse(const std::function& visitor) + { + visitor(this); + for (auto& c : mChildren) { + c->traverse(visitor); + } + } + + std::string getSymName() const noexcept { return mSymName; } + GlobalLabel getLabel() const noexcept { return mLabel; } + AlignableVolume* getParent() const { return mParent; } + size_t getNChildren() const noexcept { return mChildren.size(); } + + // DOF management + void setRigidBody(std::unique_ptr rb) { mRigidBody = std::move(rb); } + void setCalib(std::unique_ptr cal) { mCalib = std::move(cal); } + DOFSet* getRigidBody() const { return mRigidBody.get(); } + DOFSet* getCalib() const { return mCalib.get(); } + void setPseudo(bool p) noexcept { mIsPseudo = p; } + bool isPseudo() const noexcept { return mIsPseudo; } + void setSensorId(int id) noexcept { mSensorId = id; } + int getSensorId() const noexcept { return mSensorId; } + // true if this volume participates in the hierarchy (has DOFs or is pseudo) + bool isActive() const noexcept { return mRigidBody != nullptr || mIsPseudo; } + + // transformation matrices + virtual void defineMatrixL2G() {} + virtual void defineMatrixT2L() {} + virtual void computeJacobianL2T(const double* pos, Matrix66& jac) const {}; + const TGeoHMatrix& getL2P() const { return mL2P; } + const TGeoHMatrix& getT2L() const { return mT2L; } + const Matrix66& getJL2P() const { return mJL2P; } + const Matrix66& getJP2L() const { return mJP2L; } + + protected: + /// matrices + AlignableVolume* mParent{nullptr}; // parent + TGeoPNEntry* mPNE{nullptr}; // physical entry + TGeoPhysicalNode* mPN{nullptr}; // physical node + TGeoHMatrix mL2G; // (LOC) -> (GLO) + TGeoHMatrix mL2P; // (LOC) -> (PAR) + Matrix66 mJL2P; // jac (LOC) -> (PAR) + Matrix66 mJP2L; // jac (PAR) -> (LOC) + TGeoHMatrix mT2L; // (TRK) -> (LOC) + + private: + std::string mSymName; + GlobalLabel mLabel; + uint8_t mLevel{0}; + bool mIsPseudo{false}; + int mSensorId{-1}; + std::unique_ptr mRigidBody; + std::unique_ptr mCalib; + + AlignableVolume* setParent(Ptr c) + { + c->mParent = this; + mChildren.push_back(std::move(c)); + return mChildren.back().get(); + } + std::vector mChildren; // children + + void init(); +}; + +// apply DOF configuration from a JSON file to the hierarchy +void applyDOFConfig(AlignableVolume* root, const std::string& jsonPath); + +// parse millepede.res and write result.json with fitted parameters for ITS3 half barrels +void writeMillepedeResults(AlignableVolume* root, const std::string& milleResPath, const std::string& outJsonPath, const std::string& injectedJsonPath = ""); + +} // namespace o2::its3::align + +#endif diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentParams.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentParams.h new file mode 100644 index 0000000000000..a7785a2c04e11 --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentParams.h @@ -0,0 +1,67 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#ifndef ALICEO2_ITS3_ALIGNMENTPARAMS_H_ +#define ALICEO2_ITS3_ALIGNMENTPARAMS_H_ + +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" +#include "DetectorsBase/Propagator.h" + +namespace o2::its3::align +{ +struct AlignmentParams : public o2::conf::ConfigurableParamHelper { + // Track selection + float minPt = 1.f; // minimum pt required + int minITSCls = 7; // minimum number of ITS clusters + float maxITSChi2Ndf = 1.2; // maximum ITS track chi2 + + // propagation opt + double maxSnp = 0.85; + double maxStep = 2.0; + // o2::base::PropagatorD::MatCorrType matCorrType = o2::base::PropagatorD::MatCorrType::USEMatCorrTGeo; + o2::base::PropagatorD::MatCorrType corrType = o2::base::PropagatorD::MatCorrType::USEMatCorrLUT; + + bool useStableRef = true; // use input tracks as linearization point + float minMS = 1e-6f; // minimum scattering to account for + float maxChi2Ndf = 10; // maximum Chi2/Ndf allowed for GBL fit + + // per chip extra error + float extraClsErrY[6] = {0}; + float extraClsErrZ[6] = {0}; + + // misalignment simulation + bool doMisalignmentLeg = false; // simulate Legendre deformation on ITS3 layers + bool doMisalignmentRB = false; // simulate rigid body misalignment on ITS3 layers + std::string misAlgJson; // JSON file with deformation and/or rigid body params + + // DOF configuration (JSON file defining which volumes have which DOFs) + std::string dofConfigJson; // if empty, no DOFs are configured + + // Ridder options + int ridderMaxExtrap = 10; + double ridderRelIniStep[5] = {0.01, 0.01, 0.02, 0.02, 0.02}; + double ridderMaxIniStep[5] = {0.1, 0.1, 0.05, 0.05, 0.05}; + double ridderShrinkFac = 2.0; + double ridderEps = 1e-16; + + // MillePede output + std::string milleBinFile = "mp2data.bin"; + std::string milleConFile = "mp2con.txt"; + std::string milleParamFile = "mp2param.txt"; + std::string milleTreeFile = "mp2tree.txt"; + std::string milleResFile = "millepede.res"; + std::string milleResOutJson = "result.json"; + + O2ParamDef(AlignmentParams, "ITS3AlignmentParams"); +}; +} // namespace o2::its3::align + +#endif diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentSensors.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentSensors.h new file mode 100644 index 0000000000000..535f67156a16c --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentSensors.h @@ -0,0 +1,41 @@ +// 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. + +#ifndef O2_ITS3_ALIGNMENT_SENSORS_H +#define O2_ITS3_ALIGNMENT_SENSORS_H + +#include "ITS3Align/AlignmentHierarchy.h" + +namespace o2::its3::align +{ + +AlignableVolume::Ptr buildHierarchyITS(AlignableVolume::SensorMapping& sensorMap); +AlignableVolume::Ptr buildHierarchyIT3(AlignableVolume::SensorMapping& sensorMap); + +class AlignableSensorITS final : public AlignableVolume +{ + using AlignableVolume::AlignableVolume; + void defineMatrixL2G() final; + void defineMatrixT2L() final; + void computeJacobianL2T(const double* pos, Matrix66& jac) const final; +}; + +class AlignableSensorIT3 final : public AlignableVolume +{ + using AlignableVolume::AlignableVolume; + void defineMatrixL2G() final; + void defineMatrixT2L() final; + void computeJacobianL2T(const double* pos, Matrix66& jac) const final; +}; + +} // namespace o2::its3::align + +#endif diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentSpec.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentSpec.h new file mode 100644 index 0000000000000..2344889657558 --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentSpec.h @@ -0,0 +1,34 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_ITS3_ALIGNMENT_H +#define O2_ITS3_ALIGNMENT_H + +#include "ReconstructionDataFormats/GlobalTrackID.h" +#include "Framework/DataProcessorSpec.h" +#include "CommonUtils/EnumFlags.h" + +namespace o2::its3::align +{ + +enum class OutputOpt : uint8_t { + VerboseGBL = 0, + MilleData, + MilleSteer, + MilleRes, + Debug, +}; +using OutputEnum = utils::EnumFlags; + +o2::framework::DataProcessorSpec getAlignmentSpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool useMC, bool withPV, bool withITS3, OutputEnum out); +} // namespace o2::its3::align + +#endif diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentTypes.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentTypes.h new file mode 100644 index 0000000000000..6dc84b2323d35 --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentTypes.h @@ -0,0 +1,64 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_ITS3_ALIGNMENT_TYPES_H +#define O2_ITS3_ALIGNMENT_TYPES_H + +#include +#include "ReconstructionDataFormats/Track.h" +#include "DataFormatsITS/TrackITS.h" + +namespace o2::its3::align +{ + +struct Measurement final { + double dy = 0.f; + double dz = 0.f; + double sig2y = 0.f; + double sig2z = 0.f; + double phi = 0.f; + double z = 0.f; + ClassDefNV(Measurement, 1) +}; + +struct FrameInfoExt final { + int16_t sens = -1; + int8_t lr = -1; // -1 = vtx + double x{-999.f}; + double alpha{-999.f}; + std::array positionTrackingFrame = {999., 999.}; + std::array covarianceTrackingFrame = {999., 999., 999.}; + + std::string asString() const; + + ClassDefNV(FrameInfoExt, 1) +}; + +struct FitInfo final { + float chi2Ndf{-1}; // Chi2/Ndf of track refit + float chi2{-1}; // Chi2 + int ndf{-1}; // ndf + ClassDefNV(FitInfo, 1) +}; + +struct Track { + o2::its::TrackITS its; // original ITS track + o2::track::TrackParCovD track; // track at innermost update point, refitted from outwards seed + FitInfo kfFit; // kf fit information + FitInfo gblFit; // gbl fit information + std::vector points; // measurment point + std::vector info; // frame info + ClassDefNV(Track, 1) +}; + +} // namespace o2::its3::align + +#endif diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/Deformations.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/Deformations.h deleted file mode 100644 index dfaade51e82ff..0000000000000 --- a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/Deformations.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. - -#ifndef ITS3_DEFORMATIONS_H_ -#define ITS3_DEFORMATIONS_H_ - -#include "ITS3Align/MisalignmentParameters.h" -#include "MathUtils/LegendrePols.h" - -#include - -namespace o2::its3::align -{ - -class Deformations -{ - public: - // init deformations from the parameter file - void init(const std::filesystem::path&); - - double getDeformationX(unsigned int id, double u, double v) const { return getDeformation<0>(id, u, v); } - double getDeformationY(unsigned int id, double u, double v) const { return getDeformation<1>(id, u, v); } - double getDeformationZ(unsigned int id, double u, double v) const { return getDeformation<2>(id, u, v); } - double getDeformation(unsigned int id, unsigned int axis, double u, double v) const - { - if (axis == 0) { - return mLegX[id](u, v); - } else if (axis == 1) { - return mLegY[id](u, v); - } else { - return mLegZ[id](u, v); - } - } - std::array getDeformation(unsigned int id, double u, double v) const - { - return {getDeformation<0>(id, u, v), - getDeformation<1>(id, u, v), - getDeformation<2>(id, u, v)}; - } - std::array getOrders(unsigned int id) const - { - return {mLegX[id].NOrder(), mLegY[id].NOrder(), mLegZ[id].NOrder()}; - } - const o2::math_utils::Legendre2DPolynominal& getLegendre(unsigned int id, unsigned int axis) const - { - if (axis == 0) { - return mLegX[id]; - } else if (axis == 1) { - return mLegY[id]; - } else { - return mLegZ[id]; - } - } - - private: - template - double getDeformation(unsigned int id, double u, double v) const - { - if constexpr (axis == 0) { - return mLegX[id](u, v); - } else if constexpr (axis == 1) { - return mLegY[id](u, v); - } else { - return mLegZ[id](u, v); - } - } - - // 3 Legendre polynominals to model deformations in x,y,z; parameterized by normalized phi (u) and z (v) coordinates - std::array mLegX; - std::array mLegY; - std::array mLegZ; -}; - -} // namespace o2::its3::align - -#endif diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentHits.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentHits.h deleted file mode 100644 index 37f5c9fdf701d..0000000000000 --- a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentHits.h +++ /dev/null @@ -1,216 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef ITS3_MISALIGNMENTHITS_H_ -#define ITS3_MISALIGNMENTHITS_H_ - -#include "Math/IFunction.h" -#include "Math/Minimizer.h" - -#include "ReconstructionDataFormats/Track.h" -#include "ITS3Align/Deformations.h" -#include "ITSBase/GeometryTGeo.h" -#include "ITSMFTSimulation/Hit.h" -#include "MathUtils/Cartesian.h" -#include "MathUtils/Utils.h" -#include "Steer/MCKinematicsReader.h" - -#include -#include -#include -#include -#include - -namespace o2::its3::align -{ - -class MisAlignmentHits -{ - public: - enum class PropMethod { - Propagator, - Line, - }; - - void init(); - - std::optional processHit(int iEvent, const o2::itsmft::Hit& hit); - - void resetStats() { mStats.fill(0ull); } - void printStats() const; - - private: - Deformations mDeformations; - std::unique_ptr mMinimizer; - PropMethod mMethod{PropMethod::Line}; - o2::its::GeometryTGeo* mGeo{nullptr}; - std::unique_ptr mMCReader; - - short getDetID(const o2::math_utils::Point3D& point); - short getDetIDFromCords(const o2::math_utils::Point3D& point); - short getDetIDFromPath(const std::string& path) const; - - // We treat each hit as two separate hits', one for the entering and one for the exiting hit - struct WorkingHit { - enum HitType : uint8_t { - kEntering = 0, - kExiting, - kTypes, - }; - - WorkingHit() = default; - - WorkingHit(int eventID, HitType t, const o2::itsmft::Hit& hit) : mEvent(eventID), - mTrackID(hit.GetTrackID()), - mType(t), - mDetID(hit.GetDetectorID()), - mLayerID(constants::detID::getDetID2Layer(mDetID)), - mSensorID(constants::detID::getSensorID(mDetID)) - { - if (mType == kEntering) { - mRadius = constants::radiiInner[mLayerID]; - mPoint = hit.GetPosStart(); - } else { - mRadius = constants::radiiOuter[mLayerID]; - mPoint = hit.GetPos(); - } - - // Pre-calculate the normalized u,v coordinates as starting parameters - const bool isTop = mSensorID % 2 == 0; - mPhi = o2::math_utils::to02Pi(std::atan2(mPoint.Y(), mPoint.X())); - mPhiBorder1 = o2::math_utils::to02Pi(((isTop) ? 0.f : 1.f) * TMath::Pi() + std::asin(constants::equatorialGap / 2.f / mRadius)); - mPhiBorder2 = o2::math_utils::to02Pi(((isTop) ? 1.f : 2.f) * TMath::Pi() - std::asin(constants::equatorialGap / 2.f / mRadius)); - mU = ((mPhi - mPhiBorder1) * 2.f) / (mPhiBorder2 - mPhiBorder1) - 1.f; - mV = (2.f * mPoint.Z() + constants::segment::lengthSensitive) / constants::segment::lengthSensitive - 1.f; - } - - void recalculateIdeal(float phi, float z) - { - mPointDef.SetX(mRadius * std::cos(phi)); - mPointDef.SetY(mRadius * std::sin(phi)); - mPointDef.SetZ(z); - } - - int mEvent; - int mTrackID; - HitType mType; - short mDetID; - int mLayerID; - int mSensorID; - float mRadius; - float mPhi; - o2::math_utils::Point3D mPoint; - o2::math_utils::Point3D mPointDef; - float mU; // u is normalized phi - float mV; // u is normalized z - - float mPhiBorder1; - float mPhiBorder2; - }; - std::array mCurWorkingHits; - o2::itsmft::Hit mCurHit; - - bool deformHit(WorkingHit::HitType t); - - auto getDeformation(unsigned int id, double u, double v) const - { - return mDeformations.getDeformation(id, u, v); - } - - // Mimize function assuming a straight line - // given in the parametric representation by y_v = t * d_x + x_s - // assuming no offset is needed - class StraightLine : public ROOT::Math::IBaseFunctionMultiDim - { - public: - StraightLine(const MisAlignmentHits* m) : mMis(m) {} - - std::array mD; - o2::math_utils::Point3D mStart; - unsigned int mSensorID; - double mRadius; - const MisAlignmentHits* mMis; - - double mPhiTot; - double mPhi1; - - unsigned int NDim() const override { return 3; } - ROOT::Math::IBaseFunctionMultiDim* Clone() const override { return nullptr; } - - private: - double DoEval(const double* x) const override; - }; - StraightLine mLine{this}; - void prepareLineMethod(WorkingHit::HitType from); - - // Mimize function using the MCTrack - class Propagator : public ROOT::Math::IBaseFunctionMultiDim - { - public: - Propagator(const MisAlignmentHits* m) : mMis(m) {} - - o2::track::TrackPar mTrack; - float mBz; - unsigned int mSensorID; - double mRadius; - const MisAlignmentHits* mMis; - - double mPhiTot; - double mPhi1; - - unsigned int NDim() const override { return 3; } - ROOT::Math::IBaseFunctionMultiDim* Clone() const override { return nullptr; } - - private: - double DoEval(const double* x) const override; - }; - Propagator mPropagator{this}; - bool preparePropagatorMethod(WorkingHit::HitType from); - - enum Stats : uint8_t { - kHitTotal = 0, - kHitIsOB, - kHitIsIB, - kHitDead, - kHitAlive, - kHitSuccess, - kHitMigrated, - kHitNotMigrated, - kHitEntBoundary, - kHitExtBoundary, - kHitNoBoundary, - kHitSameBoundary, - kFindNodeFailed, - kFindNodeSuccess, - kProjSensitive, - kProjNonSensitive, - kDetIDOk, - kDetIDBad, - kMinimizerStatusOk, - kMinimizerStatusBad, - kMinimizerValueOk, - kMinimizerValueBad, - kMinimizerConverged, - kMinimizerCovPos, - kMinimizerHesse, - kMinimizerEDM, - kMinimizerLimit, - kMinimizerOther, - kPropTrackNull, - kPropPDGNull, - kALL, - }; - std::array mStats; -}; - -} // namespace o2::its3::align - -#endif diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentManager.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentManager.h deleted file mode 100644 index 0fe972442809d..0000000000000 --- a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentManager.h +++ /dev/null @@ -1,53 +0,0 @@ -// Copyright 2020-2022 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#ifndef ITS3_MISALIGNMENTMANAGER_H_ -#define ITS3_MISALIGNMENTMANAGER_H_ - -#include "Math/Transform3D.h" -#include "Math/Translation3D.h" -#include "Math/Rotation3D.h" -#include "Math/EulerAngles.h" -#include "Math/PositionVector3D.h" -#include "TGeoMatrix.h" - -#include - -namespace o2::its3::align -{ - -/// Collection of static functions and types to perform misalignment studies -struct MisalignmentManager { - using Vector3D = ROOT::Math::DisplacementVector3D, ROOT::Math::DefaultCoordinateSystemTag>; - using Point3D = ROOT::Math::PositionVector3D, ROOT::Math::DefaultCoordinateSystemTag>; - using Trans3D = ROOT::Math::Translation3DF; - using Rot3D = ROOT::Math::Rotation3D; - using Euler3D = ROOT::Math::EulerAngles; - using Trafo3D = ROOT::Math::Transform3DF; - - static void misalignHits(); - - static void createBackup(const std::filesystem::path& src, const std::filesystem::path& dest); - - static std::string appendStem(const std::string& filename, const std::string& add); - - static std::vector split(const std::string& s, char delimiter = '/'); - - static void navigate(const std::string& path); - - static std::string composePathSensor(int sensor); - - static void applyGlobalMatrixVolume(const std::string& path, const TGeoHMatrix& globalMatrix); -}; - -} // namespace o2::its3::align - -#endif diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentParameters.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentParameters.h deleted file mode 100644 index 243623cc650e1..0000000000000 --- a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentParameters.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. - -/// \file MisalignmentParameters.h -/// \brief Definition of the MisalignmentParameters class - -#ifndef ITS3_MISALIGNMENTPARAMETERS_H_ -#define ITS3_MISALIGNMENTPARAMETERS_H_ - -#include "ITS3Base/SpecsV2.h" - -#include "TNamed.h" -#include "TFile.h" -#include "TMatrixD.h" - -#include -#include - -namespace o2::its3::align -{ - -class MisalignmentParameters : public TNamed -{ - public: - MisalignmentParameters(); - - // IO - bool store(const std::string& file) const; - static MisalignmentParameters* load(const std::string& file); - - /// Global getters - double getGloTransX(unsigned int detID) const { return mGloTransX[detID]; } - double getGloTransY(unsigned int detID) const { return mGloTransY[detID]; } - double getGloTransZ(unsigned int detID) const { return mGloTransZ[detID]; } - double getGloRotX(unsigned int detID) const { return mGloRotX[detID]; } - double getGloRotY(unsigned int detID) const { return mGloRotY[detID]; } - double getGloRotZ(unsigned int detID) const { return mGloRotZ[detID]; } - /// Global setters - void setGloTransX(unsigned int detID, double v) { mGloTransX[detID] = v; } - void setGloTransY(unsigned int detID, double v) { mGloTransY[detID] = v; } - void setGloTransZ(unsigned int detID, double v) { mGloTransZ[detID] = v; } - void setGloRotX(unsigned int detID, double v) { mGloRotX[detID] = v; } - void setGloRotY(unsigned int detID, double v) { mGloRotY[detID] = v; } - void setGloRotZ(unsigned int detID, double v) { mGloRotZ[detID] = v; } - - /// Legendre Coeff. getters - const TMatrixD& getLegendreCoeffX(unsigned int sensorID) const { return mLegCoeffX[sensorID]; } - const TMatrixD& getLegendreCoeffY(unsigned int sensorID) const { return mLegCoeffY[sensorID]; } - const TMatrixD& getLegendreCoeffZ(unsigned int sensorID) const { return mLegCoeffZ[sensorID]; } - /// Legendre Coeff. setters - void setLegendreCoeffX(unsigned int sensorID, const TMatrixD& m) { setMatrix(mLegCoeffX[sensorID], m); } - void setLegendreCoeffY(unsigned int sensorID, const TMatrixD& m) { setMatrix(mLegCoeffY[sensorID], m); } - void setLegendreCoeffZ(unsigned int sensorID, const TMatrixD& m) { setMatrix(mLegCoeffZ[sensorID], m); } - - void printParams(unsigned int detID) const; - void printLegendreParams(unsigned int sensorID) const; - - private: - inline void setMatrix(TMatrixD& o, const TMatrixD& n) - { - o.ResizeTo(n.GetNrows(), n.GetNcols()); - o = n; - } - - static constexpr unsigned int nDetectors{constants::detID::nChips}; ///! for now just the IB - - // Global parameters - std::array mGloTransX; ///< Array to hold the global misalignment in x-direction - std::array mGloTransY; ///< Array to hold the global misalignment in y-direction - std::array mGloTransZ; ///< Array to hold the global misalignment in z-direction - std::array mGloRotX; ///< Array to hold the global misalignment in x-direction - std::array mGloRotY; ///< Array to hold the global misalignment in y-direction - std::array mGloRotZ; ///< Array to hold the global misalignment in z-direction - - // Legendre Polynominals coefficients - std::array mLegCoeffX; - std::array mLegCoeffY; - std::array mLegCoeffZ; - - ClassDefOverride(MisalignmentParameters, 1); -}; - -} // namespace o2::its3::align - -#endif diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/TrackFit.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/TrackFit.h new file mode 100644 index 0000000000000..3f36705271c9b --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/TrackFit.h @@ -0,0 +1,175 @@ +// 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. + +#ifndef O2_ITS3_ALIGN_TRACKFIT +#define O2_ITS3_ALIGN_TRACKFIT + +#include + +#include "ITSBase/GeometryTGeo.h" +#include "DetectorsBase/Propagator.h" +#include "ReconstructionDataFormats/Track.h" +#include "DataFormatsITS/TrackITS.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" + +namespace o2::its3::align +{ +using Mat51 = Eigen::Matrix; +using Mat55 = Eigen::Matrix; +using TrackD = o2::track::TrackParCovD; + +template +struct TrackingCluster : public o2::BaseCluster { + using o2::BaseCluster::BaseCluster; + T alpha{}; +}; + +template +track::TrackParametrizationWithError convertTrack(const track::TrackParametrizationWithError& trk) +{ + if constexpr (std::is_same_v) { + return trk; + } + track::TrackParametrizationWithError dst; + dst.setX(trk.getX()); + dst.setAlpha(trk.getAlpha()); + for (int iPar{0}; iPar < track::kNParams; ++iPar) { + dst.setParam(trk.getParam(iPar), iPar); + } + dst.setAbsCharge(trk.getAbsCharge()); + dst.setPID(trk.getPID()); + dst.setUserField(trk.getUserField()); + for (int iCov{0}; iCov < track::kCovMatSize; ++iCov) { + dst.setCov(trk.getCov()[iCov], iCov); + } + return dst; +} + +// Both tracks must be at the same (alpha, x). +// Returns the interpolated track. +template +o2::track::TrackParametrizationWithError interpolateTrackParCov( + const o2::track::TrackParametrizationWithError& tA, + const o2::track::TrackParametrizationWithError& tB) +{ + auto res = tA; + if (!tA.isValid() || !tB.isValid() || tA.getAlpha() != tB.getAlpha() || tA.getX() != tB.getX()) { + res.invalidate(); + return res; + } + auto unpack = [](const std::array& c) { + Mat55 m; + for (int i = 0, k = 0; i < 5; ++i) { + for (int j = 0; j <= i; ++j, ++k) { + m(i, j) = m(j, i) = (double)c[k]; + } + } + return m; + }; + Mat55 cA = unpack(tA.getCov()); + Mat55 cB = unpack(tB.getCov()); + Mat55 wA = cA.inverse(); + Mat55 wB = cB.inverse(); + Mat55 wTot = wA + wB; + Mat55 cTot = wTot.inverse(); + Mat51 pA, pB; + for (int i = 0; i < 5; ++i) { + pA(i) = tA.getParam(i); + pB(i) = tB.getParam(i); + } + Mat51 pTot = cTot * (wA * pA + wB * pB); + // build result - same alpha/x as inputs + for (int i = 0; i < 5; ++i) { + res.setParam(pTot(i), i); + } + for (int i = 0, k = 0; i < 5; ++i) { + for (int j = 0; j <= i; ++j, ++k) { + res.setCov(static_cast(cTot(i, j)), k); + } + } + return res; +} + +// Performs an outward (0->7) and inward (7->0) Kalman refit storing the +// extrapolation *before* the cluster update at each layer. +// cluster array clArr[0] = PV (optional), clArr[1..7] = layers 0-6. +// chi2 is accumulated only for the outward direction +template +bool doBidirRefit( + const o2::its::TrackITS& iTrack, + std::array*, 8>& clArr, + std::array, 8>& extrapOut, + std::array, 8>& extrapInw, + T& chi2, + bool useStableRef, + typename o2::base::PropagatorImpl::MatCorrType corrType) +{ + const auto prop = o2::base::PropagatorImpl::Instance(); + const auto geom = o2::its::GeometryTGeo::Instance(); + const auto bz = prop->getNominalBz(); + + auto rotateTrack = [bz](o2::track::TrackParametrizationWithError& tr, T alpha, o2::track::TrackParametrization* refLin) { + return refLin ? tr.rotate(alpha, *refLin, bz) : tr.rotate(alpha); + }; + auto accountCluster = [&](int i, std::array, 8>& extrapDest, o2::track::TrackParametrizationWithError& tr, o2::track::TrackParametrization* refLin) -> int { + if (clArr[i]) { + bool outward = tr.getX() < clArr[i]->getX(); + if (!rotateTrack(tr, clArr[i]->alpha, refLin) || !prop->propagateTo(tr, refLin, clArr[i]->getX(), false, base::PropagatorImpl::MAX_SIN_PHI, base::PropagatorImpl::MAX_STEP, corrType)) { + return 0; + } + if (outward) { + chi2 += tr.getPredictedChi2Quiet(*clArr[i]); + } + extrapDest[i] = tr; // before update + if (!tr.update(*clArr[i])) { + return 0; + } + } else { + extrapDest[i].invalidate(); + return -1; + } + return 1; + }; + auto trFitInw = convertTrack(iTrack.getParamOut()); + auto trFitOut = convertTrack(iTrack.getParamIn()); + if (clArr[0]) { // propagate outward seed to PV cluster's tracking frame + if (!trFitOut.rotate(clArr[0]->alpha) || !prop->propagateToX(trFitOut, clArr[0]->getX(), bz, base::PropagatorImpl::MAX_SIN_PHI, base::PropagatorImpl::MAX_STEP, corrType)) { + return false; + } + } + // linearization references + o2::track::TrackParametrization refLinInw0, refLinOut0, *refLinOut = nullptr, *refLinInw = nullptr; + if (useStableRef) { + refLinOut = &(refLinOut0 = trFitOut); + refLinInw = &(refLinInw0 = trFitInw); + } + + auto resetTrackCov = [bz](auto& trk) { + trk.resetCovariance(); + float qptB5Scale = std::abs(bz) > 0.1f ? std::abs(bz) / 5.006680f : 1.f; + float q2pt2 = trk.getQ2Pt() * trk.getQ2Pt(), q2pt2Wgh = q2pt2 * qptB5Scale * qptB5Scale; + float err2 = (100.f + q2pt2Wgh) / (1.f + q2pt2Wgh) * q2pt2; // -> 100 for high pTs, -> 1 for low pTs. + trk.setCov(err2, 14); // 100% error + }; + resetTrackCov(trFitOut); + resetTrackCov(trFitInw); + + for (int i = 0; i <= 7; i++) { + if (!accountCluster(i, extrapOut, trFitOut, refLinOut) || !accountCluster(7 - i, extrapInw, trFitInw, refLinInw)) { + return false; + } + } + return true; +} + +} // namespace o2::its3::align + +#endif diff --git a/Detectors/Upgrades/ITS3/alignment/src/AlignmentHierarchy.cxx b/Detectors/Upgrades/ITS3/alignment/src/AlignmentHierarchy.cxx new file mode 100644 index 0000000000000..9170165a36a41 --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/src/AlignmentHierarchy.cxx @@ -0,0 +1,486 @@ +// 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 +#include +#include +#include +#include +#include +#include + +#include "ITS3Align/AlignmentHierarchy.h" +#include "ITSBase/GeometryTGeo.h" +#include "Framework/Logger.h" +#include "MathUtils/Utils.h" + +namespace o2::its3::align +{ + +void HierarchyConstraint::write(std::ostream& os) const +{ + os << "!!! " << mName << '\n'; + os << "Constraint " << mValue << '\n'; + for (size_t i{0}; i < mLabels.size(); ++i) { + os << mLabels[i] << " " << mCoeff[i] << '\n'; + } + os << '\n'; +} + +AlignableVolume::AlignableVolume(const char* symName, uint32_t label, uint32_t det, bool sens) : mSymName(symName), mLabel(det, label, sens) +{ + init(); +} + +AlignableVolume::AlignableVolume(const char* symName, GlobalLabel label) : mSymName(symName), mLabel(label) +{ + init(); +} + +void AlignableVolume::init() +{ + // check if this sym volume actually exists + mPNE = gGeoManager->GetAlignableEntry(mSymName.c_str()); + if (mPNE == nullptr) { + LOGP(fatal, "Symbolic volume '{}' has no corresponding alignable entry!", mSymName); + } + mPN = mPNE->GetPhysicalNode(); + if (mPN == nullptr) { + LOGP(debug, "Adding physical node to {}", mSymName); + mPN = gGeoManager->MakePhysicalNode(mPNE->GetTitle()); + if (mPN == nullptr) { + LOGP(fatal, "Failed to make physical node for {}", mSymName); + } + } +} + +void AlignableVolume::finalise(uint8_t level) +{ + if (level == 0 && !isRoot()) { + LOGP(fatal, "Finalise should be called only from the root node!"); + } + mLevel = level; + if (!isLeaf()) { + // depth first + for (const auto& c : mChildren) { + c->finalise(level + 1); + } + // auto-disable parent RB DOFs if no children are active + if (mRigidBody) { + int nActiveChildren = 0; + for (const auto& c : mChildren) { + if (c->isActive()) { + ++nActiveChildren; + } + } + if (!nActiveChildren) { + for (int iDOF = 0; iDOF < mRigidBody->nDOFs(); ++iDOF) { + if (mRigidBody->isFree(iDOF)) { + LOGP(warn, "Auto-disabling DOF {} for {} since no active children", + mRigidBody->dofName(iDOF), mSymName); + mRigidBody->setFree(iDOF, false); + } + } + } + } + } else { + // for sensors we need also to define the transformation from the measurment (TRK) to the local frame (LOC) + // need to it with including possible pre-alignment to allow for iterative convergence + // (TRK) is defined wrt global z-axis + defineMatrixL2G(); + defineMatrixT2L(); + } + if (!isRoot()) { + // prepare the transformation matrices, e.g. from child frame to parent frame + // this is not necessarily just one level transformation + TGeoHMatrix mat = *mPN->GetMatrix(); // global matrix (including possible pre-alignment) from this volume to the global frame + if (isLeaf()) { + mat = mL2G; // for sensor volumes they might have redefined the L2G definition + } + auto inv = mParent->mPN->GetMatrix()->Inverse(); // global (including possible pre-alignment) from this volume to the global frame + mat.MultiplyLeft(inv); // left mult. effectively subtracts the parent transformation which is included in the the childs + mL2P = mat; // now this is directly the child to the parent transformation (LOC) (including possible pre-alignment) + + // prepare jacobian from child to parent frame + Eigen::Map> rotL2P(mL2P.GetRotationMatrix()); + Eigen::Matrix3d rotInv = rotL2P.transpose(); // parent-to-child rotation + const double* t = mL2P.GetTranslation(); // child origin in parent frame + Eigen::Matrix3d skewT; + skewT << 0, -t[2], t[1], t[2], 0, -t[0], -t[1], t[0], 0; + mJL2P.setZero(); + mJL2P.topLeftCorner<3, 3>() = rotInv; + mJL2P.topRightCorner<3, 3>() = -rotInv * skewT; + mJL2P.bottomRightCorner<3, 3>() = rotInv; + mJP2L = mJL2P.inverse(); + } +} + +void AlignableVolume::writeRigidBodyConstraints(std::ostream& os) const +{ + if (isLeaf() || !mRigidBody) { + // recurse even if this node has no RB DOFs + for (const auto& c : mChildren) { + c->writeRigidBodyConstraints(os); + } + return; + } + + for (int iDOF = 0; iDOF < mRigidBody->nDOFs(); ++iDOF) { + if (!mRigidBody->isFree(iDOF)) { + continue; + } + double nActiveChildren = 0.; + for (const auto& c : mChildren) { + if (c->isActive()) { + ++nActiveChildren; + } + } + if (nActiveChildren == 0.) { + LOGP(fatal, "{} has dof {} active but no active children!", mSymName, mRigidBody->dofName(iDOF)); + } + const double invN = 1.0 / nActiveChildren; + HierarchyConstraint con(std::format("DOF {} for {}", mRigidBody->dofName(iDOF), mSymName), 0.0); + for (const auto& c : mChildren) { + if (!c->mRigidBody) { + continue; + } + for (int jDOF = 0; jDOF < c->mRigidBody->nDOFs(); ++jDOF) { + if (!c->mRigidBody->isFree(jDOF)) { + continue; + } + double coeff = invN * c->getJP2L()(iDOF, jDOF); + if (std::abs(coeff) > 1e-16f) { + con.add(c->getLabel().raw(jDOF), coeff); + } + } + } + + if (con.getSize() > 1) { + con.write(os); + } + } + for (const auto& c : mChildren) { + c->writeRigidBodyConstraints(os); + } +} + +void AlignableVolume::writeParameters(std::ostream& os) const +{ + if (isRoot()) { + os << "Parameter\n"; + } + if (mRigidBody) { + for (int iDOF = 0; iDOF < mRigidBody->nDOFs(); ++iDOF) { + os << std::format("{:<10} {:>+15g} {:>+15g} ! {} {} ", + mLabel.raw(iDOF), 0.0, (mRigidBody->isFree(iDOF) ? 0.0 : -1.0), + (mRigidBody->isFree(iDOF) ? 'V' : 'F'), mRigidBody->dofName(iDOF)) + << mSymName << '\n'; + } + } + if (mCalib) { + auto calibLbl = mLabel.asCalib(); + for (int iDOF = 0; iDOF < mCalib->nDOFs(); ++iDOF) { + os << std::format("{:<10} {:>+15g} {:>+15g} ! {} {} ", + calibLbl.raw(iDOF), 0.0, (mCalib->isFree(iDOF) ? 0.0 : -1.0), + (mCalib->isFree(iDOF) ? 'V' : 'F'), mCalib->dofName(iDOF)) + << mSymName << '\n'; + } + } + for (const auto& c : mChildren) { + c->writeParameters(os); + } +} + +void AlignableVolume::writeTree(std::ostream& os, int indent) const +{ + os << std::string(static_cast(indent * 2), ' ') << mSymName << (mLabel.sens() ? " (sens)" : " (pasv)"); + if (mIsPseudo) { + os << " pseudo"; + } else { + int nFreeDofs{0}; + if (mRigidBody && mRigidBody->nFreeDOFs()) { + nFreeDofs += mRigidBody->nFreeDOFs(); + os << " RB["; + for (int i = 0; i < mRigidBody->nDOFs(); ++i) { + if (mRigidBody->isFree(i)) { + os << " " << mRigidBody->dofName(i) << "(" << mLabel.raw(i) << ")"; + } + } + os << " ]"; + } + if (mCalib && mCalib->nFreeDOFs()) { + nFreeDofs += mCalib->nFreeDOFs(); + os << " CAL["; + auto calibLbl = mLabel.asCalib(); + for (int i = 0; i < mCalib->nDOFs(); ++i) { + if (mCalib->isFree(i)) { + os << " " << mCalib->dofName(i) << "(" << calibLbl.raw(i) << ")"; + } + } + os << " ]"; + } + if (!nFreeDofs) { + os << " no DOFs"; + } + } + os << '\n'; + for (const auto& c : mChildren) { + c->writeTree(os, indent + 2); + } +} + +void applyDOFConfig(AlignableVolume* root, const std::string& jsonPath) +{ + using json = nlohmann::json; + std::ifstream f(jsonPath); + if (!f.is_open()) { + LOGP(fatal, "Cannot open DOF config file: {}", jsonPath); + } + auto data = json::parse(f); + json rules = data.is_array() ? data : data.value("rules", json::array()); + + static const std::map rbNameToIdx = { + {"TX", 0}, {"TY", 1}, {"TZ", 2}, {"RX", 3}, {"RY", 4}, {"RZ", 5}}; + + auto matchPattern = [](const std::string& pattern, const std::string& sym) -> bool { + if (fnmatch(pattern.c_str(), sym.c_str(), 0) == 0) { + return true; + } + std::string prefixed = "*" + pattern; + return fnmatch(prefixed.c_str(), sym.c_str(), 0) == 0; + }; + + if (data.is_object() && data.contains("defaults")) { + json defRule = data["defaults"]; + defRule["match"] = "*"; + rules.insert(rules.begin(), defRule); + } + + root->traverse([&](AlignableVolume* vol) { + const std::string& sym = vol->getSymName(); + for (const auto& rule : rules) { + const auto pattern = rule["match"].get(); + if (!matchPattern(pattern, sym)) { + continue; + } + // rigid body DOFs + if (rule.contains("rigidBody")) { + const auto& rb = rule["rigidBody"]; + if (rb.is_string()) { + auto s = rb.get(); + if (s == "all" || s == "free") { + vol->setRigidBody(std::make_unique()); + } else if (s == "fixed") { + auto dofSet = std::make_unique(); + dofSet->setAllFree(false); + vol->setRigidBody(std::move(dofSet)); + } + } else if (rb.is_array()) { + auto dofSet = std::make_unique(); + dofSet->setAllFree(false); + for (const auto& name : rb) { + auto it = rbNameToIdx.find(name.get()); + if (it != rbNameToIdx.end()) { + dofSet->setFree(it->second, true); + } + } + vol->setRigidBody(std::move(dofSet)); + } else if (rb.is_object()) { + auto dofs = rb.value("dofs", std::string("all")); + bool fixed = rb.value("fixed", false); + if (dofs == "all") { + auto dofSet = std::make_unique(); + if (fixed) { + dofSet->setAllFree(false); + } + vol->setRigidBody(std::move(dofSet)); + } else if (rb["dofs"].is_array()) { + auto dofSet = std::make_unique(); + dofSet->setAllFree(false); + for (const auto& name : rb["dofs"]) { + auto it = rbNameToIdx.find(name.get()); + if (it != rbNameToIdx.end()) { + dofSet->setFree(it->second, !fixed); + } + } + vol->setRigidBody(std::move(dofSet)); + } + } + } + // calibration DOFs + if (rule.contains("calib")) { + const auto& cal = rule["calib"]; + auto calType = cal.value("type", std::string("")); + if (calType == "legendre") { + int order = cal.value("order", 3); + auto dofSet = std::make_unique(order); + bool fixed = cal.value("fixed", false); + if (fixed) { + dofSet->setAllFree(false); + } + // fix/free individual coefficients by name or index + if (cal.contains("free")) { + dofSet->setAllFree(false); + for (const auto& item : cal["free"]) { + if (item.is_number_integer()) { + dofSet->setFree(item.get(), true); + } else if (item.is_string()) { + // match by name e.g. "L(1,0)" + for (int k = 0; k < dofSet->nDOFs(); ++k) { + if (dofSet->dofName(k) == item.get()) { + dofSet->setFree(k, true); + } + } + } + } + } + if (cal.contains("fix")) { + for (const auto& item : cal["fix"]) { + if (item.is_number_integer()) { + dofSet->setFree(item.get(), false); + } else if (item.is_string()) { + for (int k = 0; k < dofSet->nDOFs(); ++k) { + if (dofSet->dofName(k) == item.get()) { + dofSet->setFree(k, false); + } + } + } + } + } + vol->setCalib(std::move(dofSet)); + } + } + } + }); +} + +void writeMillepedeResults(AlignableVolume* root, const std::string& milleResPath, const std::string& outJsonPath, const std::string& injectedJsonPath) +{ + using json = nlohmann::json; + + // parse millepede.res: label fittedValue presigma [...] + std::ifstream fin(milleResPath); + if (!fin.is_open()) { + LOGP(fatal, "Cannot open millepede result file: {}", milleResPath); + } + std::map labelToValue; + std::string line; + while (std::getline(fin, line)) { + if (line.empty() || line[0] == '!' || line[0] == '*') { + continue; + } + if (line.find("Parameter") != std::string::npos) { + continue; + } + std::istringstream iss(line); + uint32_t label = 0; + double value = NAN, presigma = NAN; + if (!(iss >> label >> value >> presigma)) { + continue; + } + if (presigma >= 0.0) { // skip fixed parameters + labelToValue[label] = value; + } + } + fin.close(); + LOGP(info, "Parsed {} not fixed parameters from {}", labelToValue.size(), milleResPath); + + // load injected misalignment if provided (same format as closure test input) + // indexed by sensorID + std::map> injRB; + std::map>> injMatrix; + if (!injectedJsonPath.empty()) { + std::ifstream injFile(injectedJsonPath); + if (injFile.is_open()) { + json injData = json::parse(injFile); + for (const auto& item : injData) { + int id = item["id"].get(); + if (item.contains("rigidBody")) { + injRB[id] = item["rigidBody"].get>(); + } + if (item.contains("matrix")) { + injMatrix[id] = item["matrix"].get>>(); + } + } + LOGP(info, "Loaded injected misalignment for {} sensors", injData.size()); + } else { + LOGP(warn, "Cannot open injected misalignment file: {}, writing absolute values", injectedJsonPath); + } + } + + // collect results per volume that has RB or calib DOFs + json output = json::array(); + root->traverse([&](AlignableVolume* vol) { + auto* rb = vol->getRigidBody(); + auto* cal = vol->getCalib(); + if ((!rb && !cal) || vol->isPseudo()) { + return; + } + int id = vol->getSensorId(); + json entry; + entry["symName"] = vol->getSymName(); + entry["id"] = id; + bool write = false; + + // rigid body parameters + if (rb && rb->nFreeDOFs()) { + write = true; + json rbArr = json::array(); + const auto& inj = injRB.contains(id) ? injRB[id] : std::vector{}; + for (int i = 0; i < rb->nDOFs(); ++i) { + uint32_t raw = vol->getLabel().raw(i); + auto it = labelToValue.find(raw); + double fitted = it != labelToValue.end() ? it->second : 0.0; + double ref = i < static_cast(inj.size()) ? inj[i] : 0.0; + rbArr.push_back(fitted - ref); + } + entry["rigidBody"] = rbArr; + } + + // calibration (Legendre) parameters + if (cal && cal->nFreeDOFs() && cal->type() == DOFSet::Type::Legendre) { + write = true; + auto* leg = dynamic_cast(cal); + int order = leg->order(); + auto calibLbl = vol->getLabel().asCalib(); + const auto& inj = injMatrix.contains(id) ? injMatrix[id] : std::vector>{}; + json matrix = json::array(); + int idx = 0; + for (int i = 0; i <= order; ++i) { + json row = json::array(); + for (int j = 0; j <= i; ++j) { + uint32_t raw = calibLbl.raw(idx); + auto it = labelToValue.find(raw); + double fitted = it != labelToValue.end() ? it->second : 0.0; + double ref = (i < static_cast(inj.size()) && j < static_cast(inj[i].size())) ? inj[i][j] : 0.0; + row.push_back(fitted - ref); + ++idx; + } + matrix.push_back(row); + } + entry["matrix"] = matrix; + } + if (write) { + output.push_back(entry); + } + }); + + std::ofstream fout(outJsonPath); + if (!fout.is_open()) { + LOGP(fatal, "Cannot open output file: {}", outJsonPath); + } + fout << output.dump(2) << '\n'; + fout.close(); + LOGP(info, "Wrote millepede results to {}", outJsonPath); +} + +} // namespace o2::its3::align diff --git a/Detectors/Upgrades/ITS3/alignment/src/AlignmentParams.cxx b/Detectors/Upgrades/ITS3/alignment/src/AlignmentParams.cxx new file mode 100644 index 0000000000000..0d89cb4d4cffd --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/src/AlignmentParams.cxx @@ -0,0 +1,13 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "ITS3Align/AlignmentParams.h" +O2ParamImpl(o2::its3::align::AlignmentParams); diff --git a/Detectors/Upgrades/ITS3/alignment/src/AlignmentSensors.cxx b/Detectors/Upgrades/ITS3/alignment/src/AlignmentSensors.cxx new file mode 100644 index 0000000000000..7644c37107104 --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/src/AlignmentSensors.cxx @@ -0,0 +1,201 @@ +// 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 + +#include "Framework/Logger.h" +#include "ITSMFTBase/SegmentationAlpide.h" +#include "ITS3Align/AlignmentSensors.h" +#include "ITSBase/GeometryTGeo.h" + +namespace o2::its3::align +{ + +AlignableVolume::Ptr buildHierarchyITS(AlignableVolume::SensorMapping& sensorMap) +{ + uint32_t gLbl{0}, det{0}; + auto geom = o2::its::GeometryTGeo::Instance(); + AlignableVolume *volHB{nullptr}, *volSt{nullptr}, *volHSt{nullptr}, *volMod{nullptr}; + std::unordered_map sym2vol; + + auto root = std::make_unique(geom->composeSymNameITS(), gLbl++, det, false); + sym2vol[root->getSymName()] = root.get(); + for (int ilr = 0; ilr < geom->getNumberOfLayers(); ilr++) { + for (int ihb = 0; ihb < geom->getNumberOfHalfBarrels(); ihb++) { + volHB = root->addChild(geom->composeSymNameHalfBarrel(ilr, ihb), gLbl++, det, false); + sym2vol[volHB->getSymName()] = volHB; + int nstavesHB = geom->getNumberOfStaves(ilr) / 2; + for (int ist = 0; ist < nstavesHB; ist++) { + volSt = volHB->addChild(geom->composeSymNameStave(ilr, ihb, ist), gLbl++, det, false); + sym2vol[volSt->getSymName()] = volSt; + for (int ihst = 0; ihst < geom->getNumberOfHalfStaves(ilr); ihst++) { + volHSt = volSt->addChild(geom->composeSymNameHalfStave(ilr, ihb, ist, ihst), gLbl++, det, false); + sym2vol[volHSt->getSymName()] = volHSt; + for (int imd = 0; imd < geom->getNumberOfModules(ilr); imd++) { + volMod = volHSt->addChild(geom->composeSymNameModule(ilr, ihb, ist, ihst, imd), gLbl++, det, false); + sym2vol[volMod->getSymName()] = volMod; + } + } + } + } + } + + // NOTE: for ITS sensors the local x and y are swapped + int lay = 0, hba = 0, sta = 0, ssta = 0, modd = 0, chip = 0; + for (int ich = 0; ich < geom->getNumberOfChips(); ich++) { + geom->getChipId(ich, lay, hba, sta, ssta, modd, chip); + GlobalLabel lbl(det, ich, true); + AlignableVolume* parVol = sym2vol[modd < 0 ? geom->composeSymNameStave(lay, hba, sta) : geom->composeSymNameModule(lay, hba, sta, ssta, modd)]; + if (!parVol) { + LOGP(fatal, "did not find parent for chip {}", ich); + } + int nch = modd < 0 ? geom->getNumberOfChipsPerStave(lay) : geom->getNumberOfChipsPerModule(lay); + int jch = ich % nch; + auto* chip = parVol->addChild(geom->composeSymNameChip(lay, hba, sta, ssta, modd, jch), lbl); + chip->setSensorId(ich); + sensorMap[lbl] = chip; + } + return root; +} + +AlignableVolume::Ptr buildHierarchyIT3(AlignableVolume::SensorMapping& sensorMap) +{ + uint32_t gLbl{0}, det{0}; + auto geom = o2::its::GeometryTGeo::Instance(); + AlignableVolume *volHB{nullptr}, *volSt{nullptr}, *volHSt{nullptr}, *volMod{nullptr}; + std::unordered_map sym2vol; + + auto root = std::make_unique(geom->composeSymNameITS(), gLbl++, det, false); + sym2vol[root->getSymName()] = root.get(); + for (int ilr = 0; ilr < geom->getNumberOfLayers(); ilr++) { + const bool isLayITS3 = (ilr < 3); + for (int ihb = 0; ihb < geom->getNumberOfHalfBarrels(); ihb++) { + volHB = root->addChild(geom->composeSymNameHalfBarrel(ilr, ihb, isLayITS3), gLbl++, det, false); + sym2vol[volHB->getSymName()] = volHB; + if (isLayITS3) { + volHB->setSensorId((2 * ilr) + ihb); + continue; // no deeper hierarchy for ITS3 layers + } + int nstavesHB = geom->getNumberOfStaves(ilr) / 2; + for (int ist = 0; ist < nstavesHB; ist++) { + volSt = volHB->addChild(geom->composeSymNameStave(ilr, ihb, ist), gLbl++, det, false); + sym2vol[volSt->getSymName()] = volSt; + for (int ihst = 0; ihst < geom->getNumberOfHalfStaves(ilr); ihst++) { + volHSt = volSt->addChild(geom->composeSymNameHalfStave(ilr, ihb, ist, ihst), gLbl++, det, false); + sym2vol[volHSt->getSymName()] = volHSt; + for (int imd = 0; imd < geom->getNumberOfModules(ilr); imd++) { + volMod = volHSt->addChild(geom->composeSymNameModule(ilr, ihb, ist, ihst, imd), gLbl++, det, false); + sym2vol[volMod->getSymName()] = volMod; + } + } + } + } + } + + int lay = 0, hba = 0, sta = 0, ssta = 0, modd = 0, chip = 0; + for (int ich = 0; ich < geom->getNumberOfChips(); ich++) { + geom->getChipId(ich, lay, hba, sta, ssta, modd, chip); + const bool isLayITS3 = (lay < 3); + GlobalLabel lbl(det, ich, true); + if (isLayITS3) { + // ITS3 chips by construction do not have any DOFs still add them to have the measurment to alignable layer relation + AlignableVolume* parVol = sym2vol[geom->composeSymNameHalfBarrel(lay, hba, true)]; + if (!parVol) { + LOGP(fatal, "did not find parent for chip {}", ich); + } + auto* tile = parVol->addChild(geom->composeSymNameChip(lay, hba, sta, ssta, modd, chip, true), lbl); + tile->setPseudo(true); + tile->setSensorId(ich); + sensorMap[lbl] = tile; + } else { + AlignableVolume* parVol = sym2vol[modd < 0 ? geom->composeSymNameStave(lay, hba, sta) : geom->composeSymNameModule(lay, hba, sta, ssta, modd)]; + if (!parVol) { + LOGP(fatal, "did not find parent for chip {}", ich); + } + int nch = modd < 0 ? geom->getNumberOfChipsPerStave(lay) : geom->getNumberOfChipsPerModule(lay); + int jch = ich % nch; + auto* chip = parVol->addChild(geom->composeSymNameChip(lay, hba, sta, ssta, modd, jch), lbl); + chip->setSensorId(ich); + sensorMap[lbl] = chip; + } + } + return root; +} + +void AlignableSensorITS::defineMatrixL2G() +{ + // the chip volume is not the measurment plane, need to correct for the epitaxial layer + const auto* chipL2G = mPN->GetMatrix(); + mL2G = *chipL2G; + double delta = itsmft::SegmentationAlpide::SensorLayerThickness - itsmft::SegmentationAlpide::SensorLayerThicknessEff; + TGeoTranslation tra(0., 0.5 * delta, 0.); + mL2G *= tra; +} + +void AlignableSensorITS::defineMatrixT2L() +{ + double locA[3] = {-100., 0., 0.}, locB[3] = {100., 0., 0.}, gloA[3], gloB[3]; + mL2G.LocalToMaster(locA, gloA); + mL2G.LocalToMaster(locB, gloB); + double dx = gloB[0] - gloA[0], dy = gloB[1] - gloA[1]; + double t = (gloB[0] * dx + gloB[1] * dy) / (dx * dx + dy * dy); + double xp = gloB[0] - (dx * t), yp = gloB[1] - (dy * t); + double alp = std::atan2(yp, xp); + o2::math_utils::bringTo02Pid(alp); + mT2L.RotateZ(alp * TMath::RadToDeg()); // mT2L before is identity and afterwards rotated + const TGeoHMatrix l2gI = mL2G.Inverse(); + mT2L.MultiplyLeft(l2gI); +} + +void AlignableSensorITS::computeJacobianL2T(const double* posLoc, Matrix66& jac) const +{ + jac.setZero(); + Eigen::Map> rotT2L(mT2L.GetRotationMatrix()); + Eigen::Matrix3d skew, rotL2T = rotT2L.transpose(); + skew << 0, -posLoc[2], posLoc[1], posLoc[2], 0, -posLoc[0], -posLoc[1], posLoc[0], 0; + jac.topLeftCorner<3, 3>() = rotL2T; + jac.topRightCorner<3, 3>() = -rotL2T * skew; + jac.bottomRightCorner<3, 3>() = rotL2T; +} + +void AlignableSensorIT3::defineMatrixL2G() +{ + mL2G = *mPN->GetMatrix(); +} + +void AlignableSensorIT3::defineMatrixT2L() +{ + double locA[3] = {-100., 0., 0.}, locB[3] = {100., 0., 0.}, gloA[3], gloB[3]; + mL2G.LocalToMaster(locA, gloA); + mL2G.LocalToMaster(locB, gloB); + double dx = gloB[0] - gloA[0], dy = gloB[1] - gloA[1]; + double t = (gloB[0] * dx + gloB[1] * dy) / (dx * dx + dy * dy); + double xp = gloB[0] - (dx * t), yp = gloB[1] - (dy * t); + double alp = std::atan2(yp, xp); + o2::math_utils::bringTo02Pid(alp); + mT2L.RotateZ(alp * TMath::RadToDeg()); + const TGeoHMatrix l2gI = mL2G.Inverse(); + mT2L.MultiplyLeft(l2gI); +} + +void AlignableSensorIT3::computeJacobianL2T(const double* posLoc, Matrix66& jac) const +{ + jac.setZero(); + Eigen::Map> rotT2L(mT2L.GetRotationMatrix()); + Eigen::Matrix3d skew, rotL2T = rotT2L.transpose(); + skew << 0, -posLoc[2], posLoc[1], posLoc[2], 0, -posLoc[0], -posLoc[1], posLoc[0], 0; + jac.topLeftCorner<3, 3>() = rotL2T; + jac.topRightCorner<3, 3>() = -rotL2T * skew; + jac.bottomRightCorner<3, 3>() = rotL2T; +} + +} // namespace o2::its3::align diff --git a/Detectors/Upgrades/ITS3/alignment/src/AlignmentSpec.cxx b/Detectors/Upgrades/ITS3/alignment/src/AlignmentSpec.cxx new file mode 100644 index 0000000000000..d381abc6aa567 --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/src/AlignmentSpec.cxx @@ -0,0 +1,1003 @@ +// 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 +#include + +#ifdef WITH_OPENMP +#include +#endif + +#include +#include +#include +#include +#include +#include +#include + +#include "Framework/ConfigParamRegistry.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/Task.h" +#include "ITSBase/GeometryTGeo.h" +#include "DataFormatsGlobalTracking/RecoContainer.h" +#include "DetectorsCommonDataFormats/DetID.h" +#include "DetectorsBase/Propagator.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "ReconstructionDataFormats/PrimaryVertex.h" +#include "Steer/MCKinematicsReader.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "ReconstructionDataFormats/VtxTrackRef.h" +#include "ITS3Reconstruction/TopologyDictionary.h" +#include "DataFormatsITSMFT/TopologyDictionary.h" +#include "ITStracking/MathUtils.h" +#include "ITStracking/IOUtils.h" +#include "ITS3Reconstruction/IOUtils.h" +#include "ITS3Align/TrackFit.h" +#include "ITS3Align/AlignmentSpec.h" +#include "ITS3Align/AlignmentParams.h" +#include "ITS3Align/AlignmentTypes.h" +#include "ITS3Align/AlignmentHierarchy.h" +#include "ITS3Align/AlignmentSensors.h" +#include "MathUtils/LegendrePols.h" + +namespace o2::its3::align +{ +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 TrackD = o2::track::TrackParCovD; + +namespace +{ +// compute normalized (u,v) in [-1,1] from global position on a sensor +std::pair computeUV(double gloX, double gloY, double gloZ, int sensorID, double radius) +{ + const bool isTop = sensorID % 2 == 0; + const double phi = o2::math_utils::to02Pid(std::atan2(gloY, gloX)); + const double phiBorder1 = o2::math_utils::to02Pid(((isTop ? 0. : 1.) * TMath::Pi()) + std::asin(constants::equatorialGap / 2. / radius)); + const double phiBorder2 = o2::math_utils::to02Pid(((isTop ? 1. : 2.) * TMath::Pi()) - std::asin(constants::equatorialGap / 2. / radius)); + const double u = (((phi - phiBorder1) * 2.) / (phiBorder2 - phiBorder1)) - 1.; + const double v = ((2. * gloZ + constants::segment::lengthSensitive) / constants::segment::lengthSensitive) - 1.; + return {u, v}; +} + +// evaluate Legendre polynomials P_0(x) through P_order(x) via recurrence +std::vector legendrePols(int order, double x) +{ + std::vector p(order + 1); + p[0] = 1.; + if (order > 0) { + p[1] = x; + } + for (int n = 1; n < order; ++n) { + p[n + 1] = ((2 * n + 1) * x * p[n] - n * p[n - 1]) / (n + 1); + } + return p; +} +} // namespace + +class AlignmentSpec final : public Task +{ + public: + ~AlignmentSpec() final = default; + AlignmentSpec(const AlignmentSpec&) = delete; + AlignmentSpec(AlignmentSpec&&) = delete; + AlignmentSpec& operator=(const AlignmentSpec&) = delete; + AlignmentSpec& operator=(AlignmentSpec&&) = delete; + AlignmentSpec(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool useMC, bool withPV, bool withITS, OutputEnum out) + : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mUseMC(useMC), mWithPV(withPV), mIsITS3(!withITS), mOutOpt(out) + { + } + + 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); + void buildHierarchy(); + + // calculate the transport jacobian for points FROM and TO numerically via ridder's method + // this assumes the track is already at point FROM and will be extrapolated to TO's x (xTo) + // method does not modify the original track + bool getTransportJacobian(const TrackD& track, double xTo, double alphaTo, gbl::Matrix5d& jac, gbl::Matrix5d& err); + + // refit ITS track with inward/outward fit (opt. impose pv as additional constraint) + // after this we have the refitted track at the innermost update point + bool prepareITSTrack(int iTrk, const o2::its::TrackITS& itsTrack, Track& resTrack); + + // prepare ITS measuremnt points + void prepareMeasurments(std::span clusters, std::span pattIt); + + // build track to vertex association + void buildT2V(); + + // apply some misalignment on inner ITS3 layers + // it can happen that a measurement is pushed outside of + // ITS3 acceptance so false is to discard track + bool applyMisalignment(Eigen::Vector2d& res, const FrameInfoExt& frame, const TrackD& wTrk, size_t iTrk); + + OutputEnum mOutOpt; + std::unique_ptr mDBGOut; + std::vector mPVs; + std::vector mT2PV; + bool mIsITS3{true}; + const o2::itsmft::TopologyDictionary* mITSDict{nullptr}; + const o2::its3::TopologyDictionary* mIT3Dict{nullptr}; + o2::globaltracking::RecoContainer* mRecoData = nullptr; + std::unique_ptr mcReader; + std::vector mITSTrackingInfo; + std::shared_ptr mDataRequest; + std::shared_ptr mGGCCDBRequest; + std::unique_ptr mHierarchy; // tree-hiearchy + AlignableVolume::SensorMapping mChip2Hiearchy; // global label mapping to leaves in the tree + bool mUseMC{false}; + bool mWithPV{false}; + GTrackID::mask_t mTracksSrc; + int mNThreads{1}; + const AlignmentParams* mParams{nullptr}; + std::array mDeformations; // one per sensorID (0-5) + std::array, 6> mRigidBodyParams; // (dx,dy,dz,rx,ry,rz) in LOC per sensorID +}; + +void AlignmentSpec::init(InitContext& ic) +{ + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + mNThreads = ic.options().get("nthreads"); + if (mOutOpt) { + LOG(info) << mOutOpt.pstring(); + mDBGOut = std::make_unique("its3_debug_alg.root", "recreate"); + } + if (mUseMC) { + mcReader = std::make_unique("collisioncontext.root"); + } +} + +void AlignmentSpec::run(ProcessingContext& pc) +{ + if (mOutOpt[OutputOpt::MilleRes]) { + updateTimeDependentParams(pc); + writeMillepedeResults(mHierarchy.get(), mParams->milleResFile, mParams->milleResOutJson, mParams->misAlgJson); + } else { + o2::globaltracking::RecoContainer recoData; + mRecoData = &recoData; + mRecoData->collectData(pc, *mDataRequest); + updateTimeDependentParams(pc); + process(); + } + mRecoData = nullptr; +} + +void AlignmentSpec::process() +{ + if (!mITSDict && !mIT3Dict) { + LOGP(fatal, "ITS data is not loaded"); + } + auto prop = o2::base::PropagatorD::Instance(); + const auto bz = prop->getNominalBz(); + const auto itsTracks = mRecoData->getITSTracks(); + const auto itsClRefs = mRecoData->getITSTracksClusterRefs(); + const auto clusITS = mRecoData->getITSClusters(); + const auto patterns = mRecoData->getITSClustersPatterns(); + std::span mcLbls; + if (mUseMC) { + mcLbls = mRecoData->getITSTracksMCLabels(); + } + prepareMeasurments(clusITS, patterns); + + if (mWithPV) { + buildT2V(); + } + + LOGP(info, "Starting fits with {} threads", mNThreads); + + // Data + std::vector> gblTrajSlots(mNThreads); + std::vector> resTrackSlots(mNThreads); + + auto timeStart = std::chrono::high_resolution_clock::now(); + int cFailedRefit{0}, cFailedProp{0}, cSelected{0}, cGBLFit{0}, cGBLFitFail{0}, cGBLChi2Rej{0}, cGBLConstruct{0}; + double chi2Sum{0}, lostWeightSum{0}; + int ndfSum{0}; +#ifdef WITH_OPENMP +#pragma omp parallel num_threads(mNThreads) \ + reduction(+ : cFailedRefit) \ + reduction(+ : cFailedProp) \ + reduction(+ : cSelected) \ + reduction(+ : cGBLFit) \ + reduction(+ : cGBLFitFail) \ + reduction(+ : cGBLChi2Rej) \ + reduction(+ : cGBLConstruct) \ + reduction(+ : chi2Sum) \ + reduction(+ : lostWeightSum) \ + reduction(+ : ndfSum) +#endif + { +#ifdef WITH_OPENMP + const int tid = omp_get_thread_num(); +#else + const int tid = 0; +#endif + auto& gblTrajSlot = gblTrajSlots[tid]; + auto& resTrackSlot = resTrackSlots[tid]; + +#ifdef WITH_OPENMP +#pragma omp for schedule(dynamic) +#endif + for (size_t iTrk = 0; iTrk < (int)itsTracks.size(); ++iTrk) { + const auto& trk = itsTracks[iTrk]; + if (trk.getNClusters() < mParams->minITSCls || + (trk.getChi2() / ((float)trk.getNClusters() * 2 - 5)) >= mParams->maxITSChi2Ndf || + trk.getPt() < mParams->minPt || + (mUseMC && (!mcLbls[iTrk].isValid() || !mcLbls[iTrk].isCorrect()))) { + continue; + } + ++cSelected; + Track& resTrack = resTrackSlot.emplace_back(); + if (!prepareITSTrack((int)iTrk, trk, resTrack)) { + ++cFailedRefit; + resTrackSlot.pop_back(); + continue; + } + + o2::track::TrackParD* refLin = nullptr; + if (mParams->useStableRef) { + refLin = &resTrack.track; + } + + // outward stepping from track IU + auto wTrk = resTrack.track; + const bool hasPV = resTrack.info[0].lr == -1; + std::vector points; + bool failed = false; + const int np = (int)resTrack.points.size(); + track::TrackLTIntegral lt; + lt.setTimeNotNeeded(); + constexpr int perm[5] = {4, 2, 3, 0, 1}; // ALICE->GBL: Q/Pt,Snp,Tgl,Y,Z + for (int ip{0}; ip < np; ++ip) { + const auto& frame = resTrack.info[ip]; + gbl::Matrix5d err = gbl::Matrix5d::Identity(), jacALICE = gbl::Matrix5d::Identity(), jacGBL; + float msErr = 0.f; + if (ip) { + // numerically calculates the transport jacobian from prev. point to this point + // then we actually do the step to the point and accumulate the material + if (!getTransportJacobian(wTrk, frame.x, frame.alpha, jacALICE, err) || + !prop->propagateToAlphaX(wTrk, refLin, frame.alpha, frame.x, false, mParams->maxSnp, mParams->maxStep, 1, mParams->corrType, <)) { + ++cFailedProp; + failed = true; + break; + } + msErr = its::math_utils::MSangle(trk.getPID().getMass(), trk.getP(), lt.getX2X0()); + // after computing jac, reorder to GBL convention + for (int i = 0; i < 5; i++) { + for (int j = 0; j < 5; j++) { + jacGBL(i, j) = jacALICE(perm[i], perm[j]); + } + } + } + + // wTrk is now in the measurment frame + gbl::GblPoint point(jacGBL); + // measurement + Eigen::Vector2d res, prec; + res << frame.positionTrackingFrame[0] - wTrk.getY(), frame.positionTrackingFrame[1] - wTrk.getZ(); + + // here we can apply some misalignment on the measurment + if (!applyMisalignment(res, frame, wTrk, iTrk)) { + failed = true; + break; + } + + prec << 1. / resTrack.points[ip].sig2y, 1. / resTrack.points[ip].sig2z; + // the projection matrix is in the tracking frame the idendity so no need to diagonalize it + point.addMeasurement(res, prec); + if (msErr > mParams->minMS && ip < np - 1) { + Eigen::Vector2d scat(0., 0.), scatPrec = Eigen::Vector2d::Constant(1. / (msErr * msErr)); + point.addScatterer(scat, scatPrec); + lt.clearFast(); // clear if accounted + } + + if (frame.lr >= 0) { + GlobalLabel lbl(0, frame.sens, true); + if (mChip2Hiearchy.find(lbl) == mChip2Hiearchy.end()) { + LOGP(fatal, "Cannot find global label: {}", lbl.asString()); + } + + // derivatives for all sensitive volumes and their parents + // this is the derivative in TRK but we want to align in LOC + // so dr/da_(LOC) = dr/da_(TRK) * da_(TRK)/da_(LOC) + const auto* tileVol = mChip2Hiearchy.at(lbl); + Matrix36 der = getRigidBodyDerivatives(wTrk); + + // count rigid body columns: only volumes with real DOFs (not DOFPseudo) + int nColRB{0}; + for (const auto* v = tileVol; v && !v->isRoot(); v = v->getParent()) { + if (v->getRigidBody()) { + nColRB += v->getRigidBody()->nDOFs(); + } + } + + // count calibration columns + const auto* sensorVol = tileVol->getParent(); + const auto* calibSet = sensorVol ? sensorVol->getCalib() : nullptr; + const int nCalib = calibSet ? calibSet->nDOFs() : 0; + const int nCol = nColRB + nCalib; + + std::vector gLabels; + gLabels.reserve(nCol); + Eigen::MatrixXd gDer(3, nCol); + gDer.setZero(); + Eigen::Index curCol{0}; + + // 1) tile: TRK -> LOC via precomputed T2L and J_L2T + const double posTrk[3] = {frame.x, 0., 0.}; + double posLoc[3]; + tileVol->getT2L().LocalToMaster(posTrk, posLoc); + Matrix66 jacL2T; + tileVol->computeJacobianL2T(posLoc, jacL2T); + der *= jacL2T; + if (tileVol->getRigidBody()) { + const int nd = tileVol->getRigidBody()->nDOFs(); + for (int iDOF = 0; iDOF < nd; ++iDOF) { + gLabels.push_back(tileVol->getLabel().rawGBL(iDOF)); + } + gDer.middleCols(curCol, nd) = der; + curCol += nd; + } + + // 2) chain through parents: child's J_L2P + for (const auto* child = tileVol; child->getParent() && !child->getParent()->isRoot(); child = child->getParent()) { + der *= child->getJL2P(); + const auto* parent = child->getParent(); + if (parent->getRigidBody()) { + const int nd = parent->getRigidBody()->nDOFs(); + for (int iDOF = 0; iDOF < nd; ++iDOF) { + gLabels.push_back(parent->getLabel().rawGBL(iDOF)); + } + gDer.middleCols(curCol, nd) = der; + curCol += nd; + } + } + + // 3) calibration derivatives (e.g. Legendre for ITS3 sensors, apply directly on the whole sensor, not on inidividual tiles) + if (calibSet && calibSet->type() == DOFSet::Type::Legendre) { + const auto* legSet = static_cast(calibSet); + const int N = legSet->order(); + const int sensorID = constants::detID::getSensorID(frame.sens); + const int layerID = constants::detID::getDetID2Layer(frame.sens); + + const double r = frame.x; + const double gX = r * std::cos(frame.alpha); + const double gY = r * std::sin(frame.alpha); + const double gZ = frame.positionTrackingFrame[1]; + auto [u, v] = computeUV(gX, gY, gZ, sensorID, constants::radii[layerID]); + + const double snp = wTrk.getSnp(); + const double tgl = wTrk.getTgl(); + const double csci = 1. / std::sqrt(1. - (snp * snp)); + const double dydx = snp * csci; + const double dzdx = tgl * csci; + + auto pu = legendrePols(N, u); + auto pv = legendrePols(N, v); + + int legIdx = 0; + const int legColStart = nColRB; + for (int i = 0; i <= N; ++i) { + for (int j = 0; j <= i; ++j) { + const double basis = pu[j] * pv[i - j]; + gLabels.push_back(sensorVol->getLabel().asCalib().rawGBL(legIdx)); + gDer(0, legColStart + legIdx) = dydx * basis; + gDer(1, legColStart + legIdx) = dzdx * basis; + ++legIdx; + } + } + } + point.addGlobals(gLabels, gDer); + } + + if (mOutOpt[OutputOpt::VerboseGBL]) { + static Eigen::IOFormat fmt(4, 0, ", ", "\n", "[", "]"); + LOGP(info, "WORKING-POINT {}", ip); + LOGP(info, "Track: {}", wTrk.asString()); + LOGP(info, "FrameInfo: {}", frame.asString()); + std::cout << "jacALICE:\n" + << jacALICE.format(fmt) << '\n'; + std::cout << "jacGBL:\n" + << jacGBL.format(fmt) << '\n'; + LOGP(info, "Point {}: GBL res=({}, {}), KF stored res=({}, {})", + ip, res[0], res[1], resTrack.points[ip].dy, resTrack.points[ip].dz); + LOGP(info, "residual: dy={} dz={}", res[0], res[1]); + LOGP(info, "precision: precY={} precZ={}", prec[0], prec[1]); + point.printPoint(5); + } + points.push_back(point); + } + if (!failed) { + gbl::GblTrajectory traj(points, std::abs(bz) > 0.01); + if (traj.isValid()) { + double chi2 = NAN, lostWeight = NAN; + int ndf = 0; + if (auto ierr = traj.fit(chi2, ndf, lostWeight); !ierr) { + if (mOutOpt[OutputOpt::VerboseGBL]) { + LOGP(info, "GBL FIT chi2 {} ndf {}", chi2, ndf); + traj.printTrajectory(5); + } + if (chi2 / ndf > mParams->maxChi2Ndf && cGBLChi2Rej++ < 10) { + LOGP(error, "GBL fit exceeded red chi2 {}", chi2 / ndf); + if (std::abs(resTrack.kfFit.chi2Ndf - 1) < 0.02) { + LOGP(error, "\tGBL is far away from good KF fit!!!!"); + continue; + } + } else { + ++cGBLFit; + chi2Sum += chi2; + lostWeightSum += lostWeight; + ndfSum += ndf; + if (mOutOpt[OutputOpt::MilleData]) { + gblTrajSlot.push_back(traj); + } + FitInfo fit; + fit.ndf = ndf; + fit.chi2 = (float)chi2; + fit.chi2Ndf = (float)chi2 / (float)ndf; + resTrack.gblFit = fit; + } + } else { + ++cGBLFitFail; + } + } else { + ++cGBLConstruct; + } + } + } + } + auto timeEnd = std::chrono::high_resolution_clock::now(); + auto duration = std::chrono::duration_cast(timeEnd - timeStart); + LOGP(info, "Fitted {} tracks out of {} (selected {}) in {} sec", cGBLFit, itsTracks.size(), cSelected, duration.count() / 1e3); + LOGP(info, "\tRefit failed for {} tracks; Failed prop for {} tracks", cFailedRefit, cFailedProp); + LOGP(info, "\tGBL SUMMARY:"); + LOGP(info, "\t\tGBL construction failed {}", cGBLConstruct); + LOGP(info, "\t\tGBL fit failed {}", cGBLFitFail); + LOGP(info, "\t\tGBL chi2Ndf rejected {}", cGBLChi2Rej); + if (!ndfSum) { + LOGP(info, "\t\tGBL Chi2/Ndf = NDF IS 0"); + } else { + LOGP(info, "\t\tGBL Chi2/Ndf = {}", chi2Sum / ndfSum); + } + LOGP(info, "\t\tGBL LostWeight = {}", lostWeightSum); + LOGP(info, "Streaming results to output"); + if (mOutOpt[OutputOpt::MilleData]) { + gbl::MilleBinary mille(mParams->milleBinFile, true); + for (auto& slot : gblTrajSlots) { + for (auto& traj : slot) { + traj.milleOut(mille); + } + } + } + if (mOutOpt[OutputOpt::Debug]) { + for (auto& slot : resTrackSlots) { + for (auto& res : slot) { + (*mDBGOut) << "res" + << "trk=" << res + << "\n"; + } + } + } +} + +void AlignmentSpec::updateTimeDependentParams(ProcessingContext& pc) +{ + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + if (static bool initOnce{false}; !initOnce) { + initOnce = true; + auto geom = o2::its::GeometryTGeo::Instance(); + o2::its::GeometryTGeo::Instance()->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G, o2::math_utils::TransformType::T2G)); + mParams = &AlignmentParams::Instance(); + mParams->printKeyValues(true, true); + + buildHierarchy(); + + if (mParams->doMisalignmentLeg || mParams->doMisalignmentRB) { + TMatrixD null(1, 1); + null(0, 0) = 0; + for (int i = 0; i < 6; ++i) { + mDeformations[i] = o2::math_utils::Legendre2DPolynominal(null); + mRigidBodyParams[i].setZero(); + } + if (!mParams->misAlgJson.empty()) { + using json = nlohmann::json; + std::ifstream f(mParams->misAlgJson); + auto data = json::parse(f); + for (const auto& item : data) { + int id = item["id"].get(); + if (mParams->doMisalignmentLeg && item.contains("matrix")) { + auto v = item["matrix"].get>>(); + TMatrixD m(v.size(), v[v.size() - 1].size()); + for (size_t r{0}; r < v.size(); ++r) { + for (size_t c{0}; c < v[r].size(); ++c) { + m(r, c) = v[r][c]; + } + } + mDeformations[id] = o2::math_utils::Legendre2DPolynominal(m); + } + if (mParams->doMisalignmentRB && item.contains("rigidBody")) { + auto rb = item["rigidBody"].get>(); + for (int k = 0; k < 6 && k < (int)rb.size(); ++k) { + mRigidBodyParams[id](k) = rb[k]; + } + } + } + } + } + } +} + +void AlignmentSpec::buildHierarchy() +{ + if (mIsITS3) { + mHierarchy = buildHierarchyIT3(mChip2Hiearchy); + } else { + mHierarchy = buildHierarchyITS(mChip2Hiearchy); + } + + if (!mParams->dofConfigJson.empty()) { + applyDOFConfig(mHierarchy.get(), mParams->dofConfigJson); + } + + mHierarchy->finalise(); + if (mOutOpt[OutputOpt::MilleSteer]) { + std::ofstream tree(mParams->milleTreeFile); + mHierarchy->writeTree(tree); + std::ofstream cons(mParams->milleConFile); + mHierarchy->writeRigidBodyConstraints(cons); + std::ofstream par(mParams->milleParamFile); + mHierarchy->writeParameters(par); + } +} + +bool AlignmentSpec::getTransportJacobian(const TrackD& track, double xTo, double alphaTo, gbl::Matrix5d& jac, gbl::Matrix5d& err) +{ + auto prop = o2::base::PropagatorD::Instance(); + const auto bz = prop->getNominalBz(); + const auto minStep = std::sqrt(std::numeric_limits::epsilon()); + const gbl::Vector5d x0(track.getParams()); + auto trackC = track; + o2::track::TrackParD* refLin{nullptr}; + if (mParams->useStableRef) { + refLin = &trackC; + } + + auto propagate = [&](gbl::Vector5d& p) -> bool { + TrackD tmp(track); + for (int i{0}; i < track::kNParams; ++i) { + tmp.setParam(p[i], i); + } + if (!prop->propagateToAlphaX(tmp, refLin, alphaTo, xTo, false, mParams->maxSnp, mParams->maxStep, 1, mParams->corrType)) { + return false; + } + p = gbl::Vector5d(tmp.getParams()); + return true; + }; + + for (int iPar{0}; iPar < track::kNParams; ++iPar) { + // step size + double h = std::min(mParams->ridderMaxIniStep[iPar], std::max(minStep, std::abs(track.getParam(iPar)) * mParams->ridderRelIniStep[iPar]) * std::pow(mParams->ridderShrinkFac, mParams->ridderMaxExtrap / 2)); + ; + // romberg tableu + Eigen::MatrixXd cur(track::kNParams, mParams->ridderMaxExtrap); + Eigen::MatrixXd pre(track::kNParams, mParams->ridderMaxExtrap); + double normErr = std::numeric_limits::max(); + gbl::Vector5d bestDeriv = gbl::Vector5d::Constant(std::numeric_limits::max()); + for (int iExt{0}; iExt < mParams->ridderMaxExtrap; ++iExt) { + gbl::Vector5d xPlus = x0, xMinus = x0; + xPlus(iPar) += h; + xMinus(iPar) -= h; + if (!propagate(xPlus) || !propagate(xMinus)) { + return false; + } + cur.col(0) = (xPlus - xMinus) / (2.0 * h); + if (!iExt) { + bestDeriv = cur.col(0); + } + // shrink step in next iteration + h /= mParams->ridderShrinkFac; + // richardson extrapolation + double fac = mParams->ridderShrinkFac * mParams->ridderShrinkFac; + for (int k{1}; k <= iExt; ++k) { + cur.col(k) = (fac * cur.col(k - 1) - pre.col(k - 1)) / (fac - 1.0); + fac *= mParams->ridderShrinkFac * mParams->ridderShrinkFac; + double e = std::max((cur.col(k) - cur.col(k - 1)).norm(), (cur.col(k) - pre.col(k - 1)).norm()); + if (e <= normErr) { + normErr = e; + bestDeriv = cur.col(k); + if (normErr < mParams->ridderEps) { + break; + } + } + } + if (normErr < mParams->ridderEps) { + break; + } + // check stability + if (iExt > 0) { + double tableauErr = (cur.col(iExt) - pre.col(iExt - 1)).norm(); + if (tableauErr >= 2.0 * normErr) { + break; + } + } + std::swap(cur, pre); + } + if (bestDeriv.isApproxToConstant(std::numeric_limits::max())) { + return false; + } + jac.col(iPar) = bestDeriv; + err.col(iPar) = gbl::Vector5d::Constant(normErr); + } + + if (jac.isIdentity(1e-8)) { + LOGP(error, "Near jacobian idendity for taking track from {} to {}", track.getX(), xTo); + return false; + } + + return true; +} + +bool AlignmentSpec::prepareITSTrack(int iTrk, const o2::its::TrackITS& itsTrack, align::Track& resTrack) +{ + const auto itsClRefs = mRecoData->getITSTracksClusterRefs(); + auto trFit = convertTrack(itsTrack.getParamOut()); // take outer track fit as start of refit + auto prop = o2::base::PropagatorD::Instance(); + auto geom = o2::its::GeometryTGeo::Instance(); + const auto bz = prop->getNominalBz(); + std::array frameArr{}; + o2::track::TrackParD trkOut, *refLin = nullptr; + if (mParams->useStableRef) { + refLin = &(trkOut = trFit); + } + + auto accountCluster = [&](int i, TrackD& tr, float& chi2, Measurement& meas, o2::track::TrackParD* refLin) { + if (frameArr[i]) { // update with cluster + if (!prop->propagateToAlphaX(tr, refLin, frameArr[i]->alpha, frameArr[i]->x, false, mParams->maxSnp, mParams->maxStep, 1, mParams->corrType)) { + return 2; + } + meas.dy = frameArr[i]->positionTrackingFrame[0] - tr.getY(); + meas.dz = frameArr[i]->positionTrackingFrame[1] - tr.getZ(); + meas.sig2y = frameArr[i]->covarianceTrackingFrame[0]; + meas.sig2z = frameArr[i]->covarianceTrackingFrame[2]; + meas.z = tr.getZ(); + meas.phi = tr.getPhi(); + o2::math_utils::bringTo02Pid(meas.phi); + chi2 += (float)tr.getPredictedChi2Quiet(frameArr[i]->positionTrackingFrame, frameArr[i]->covarianceTrackingFrame); + if (!tr.update(frameArr[i]->positionTrackingFrame, frameArr[i]->covarianceTrackingFrame)) { + return 2; + } + if (refLin) { // displace the reference to the last updated cluster + refLin->setY(frameArr[i]->positionTrackingFrame[0]); + refLin->setZ(frameArr[i]->positionTrackingFrame[1]); + } + return 0; + } + return 1; + }; + + FrameInfoExt pvInfo; + if (mWithPV) { // add PV as constraint + const int iPV = mT2PV[iTrk]; + if (iPV < 0) { + return false; + } + const auto& pv = mPVs[iPV]; + auto tmp = convertTrack(itsTrack.getParamIn()); + if (!prop->propagateToDCA(pv, tmp, bz)) { + return false; + } + pvInfo.alpha = (float)tmp.getAlpha(); + double ca{0}, sa{0}; + o2::math_utils::bringToPMPid(pvInfo.alpha); + o2::math_utils::sincosd(pvInfo.alpha, sa, ca); + pvInfo.x = tmp.getX(); + pvInfo.positionTrackingFrame[0] = -pv.getX() * sa + pv.getY() * ca; + pvInfo.positionTrackingFrame[1] = pv.getZ(); + pvInfo.covarianceTrackingFrame[0] = 0.5 * (pv.getSigmaX2() + pv.getSigmaY2()); + pvInfo.covarianceTrackingFrame[2] = pv.getSigmaY2(); + pvInfo.sens = -1; + pvInfo.lr = -1; + frameArr[0] = &pvInfo; + } + + // collect all track clusters to array, placing them to layer+1 slot + int nCl = itsTrack.getNClusters(); + for (int i = 0; i < nCl; i++) { // clusters are ordered from the outermost to the innermost + const auto& curInfo = mITSTrackingInfo[itsClRefs[itsTrack.getClusterEntry(i)]]; + frameArr[1 + curInfo.lr] = &curInfo; + } + + // start refit + resTrack.points.clear(); + resTrack.info.clear(); + trFit.resetCovariance(); + trFit.setCov(trFit.getQ2Pt() * trFit.getQ2Pt() * trFit.getCov()[14], 14); + float chi2{0}; + for (int i{7}; i >= 0; --i) { + Measurement point; + int res = accountCluster(i, trFit, chi2, point, refLin); + if (res == 2) { + return false; + } else if (res == 0) { + resTrack.points.push_back(point); + resTrack.info.push_back(*frameArr[i]); + resTrack.track = trFit; // put track to whatever the IU is + } + } + // reverse inserted points so they are in the same order as the track + std::reverse(resTrack.info.begin(), resTrack.info.end()); + std::reverse(resTrack.points.begin(), resTrack.points.end()); + resTrack.kfFit.chi2 = chi2; + resTrack.kfFit.ndf = (int)resTrack.info.size() * 2 - 5; + resTrack.kfFit.chi2Ndf = chi2 / (float)resTrack.kfFit.ndf; + + return true; +} + +void AlignmentSpec::prepareMeasurments(std::span clusters, std::span patterns) +{ + LOGP(info, "Preparing {} measurments", clusters.size()); + auto geom = its::GeometryTGeo::Instance(); + geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); + mITSTrackingInfo.clear(); + mITSTrackingInfo.reserve(clusters.size()); + auto pattIt = patterns.begin(); + for (const auto& cls : clusters) { + const auto sens = cls.getSensorID(); + const auto lay = geom->getLayer(sens); + double sigmaY2{0}, sigmaZ2{0}; + math_utils::Point3D locXYZ; + if (mIsITS3) { + locXYZ = o2::its3::ioutils::extractClusterData(cls, pattIt, mIT3Dict, sigmaY2, sigmaZ2); + } else { + locXYZ = o2::its::ioutils::extractClusterData(cls, pattIt, mITSDict, sigmaY2, sigmaZ2); + } + sigmaY2 += mParams->extraClsErrY[lay] * mParams->extraClsErrY[lay]; + sigmaZ2 += mParams->extraClsErrZ[lay] * mParams->extraClsErrZ[lay]; + // Transformation to the local --> global + const auto gloXYZ = geom->getMatrixL2G(sens) * locXYZ; + // Inverse transformation to the local --> tracking + auto trkXYZf = geom->getMatrixT2L(sens) ^ locXYZ; + o2::math_utils::Point3D trkXYZ; + trkXYZ.SetCoordinates(trkXYZf.X(), trkXYZf.Y(), trkXYZf.Z()); + // Tracking alpha angle + // We want that each cluster rotates its tracking frame to the clusters phi + // that way the track linearization around the measurement is less biases to the arc + // this means automatically that the measurement on the arc is at 0 for the curved layers + double alpha = geom->getSensorRefAlpha(sens); + double x = trkXYZ.x(); + if (mIsITS3 && constants::detID::isDetITS3(sens)) { + trkXYZ.SetY(0.f); + // alpha&x always have to be defined wrt to the global Z axis! + x = std::hypot(gloXYZ.x(), gloXYZ.y()); + trkXYZ.SetX(x); + alpha = std::atan2(gloXYZ.y(), gloXYZ.x()); + auto chip = constants::detID::getSensorID(sens); + sigmaY2 += mParams->extraClsErrY[chip] * mParams->extraClsErrY[chip]; + sigmaZ2 += mParams->extraClsErrZ[chip] * mParams->extraClsErrZ[chip]; + } + math_utils::bringToPMPid(alpha); + mITSTrackingInfo.emplace_back(sens, lay, x, alpha, + std::array{trkXYZ.y(), trkXYZ.z()}, + std::array{sigmaY2, 0., sigmaZ2}); + } +} + +void AlignmentSpec::buildT2V() +{ + const auto& itsTracks = mRecoData->getITSTracks(); + mT2PV.clear(); + mT2PV.resize(itsTracks.size(), -1); + if (mUseMC) { + mPVs.reserve(mcReader->getNEvents(0)); + for (int iEve{0}; iEve < mcReader->getNEvents(0); ++iEve) { + const auto& eve = mcReader->getMCEventHeader(0, iEve); + dataformats::VertexBase vtx; + constexpr float err{22e-4f}; + vtx.setX((float)eve.GetX()); + vtx.setY((float)eve.GetY()); + vtx.setZ((float)eve.GetZ()); + vtx.setSigmaX(err); + vtx.setSigmaY(err); + vtx.setSigmaZ(err); + mPVs.push_back(vtx); + } + const auto& mcLbls = mRecoData->getITSTracksMCLabels(); + for (size_t iTrk{0}; iTrk < mcLbls.size(); ++iTrk) { + const auto& lbl = mcLbls[iTrk]; + if (!lbl.isValid() || !lbl.isCorrect()) { + continue; + } + const auto& mcTrk = mcReader->getTrack(lbl); + if (mcTrk->isPrimary()) { + mT2PV[iTrk] = lbl.getEventID(); + } + } + } else { + LOGP(fatal, "Data PV to track TODO"); + } +} + +bool AlignmentSpec::applyMisalignment(Eigen::Vector2d& res, const FrameInfoExt& frame, const TrackD& wTrk, size_t iTrk) +{ + if (!constants::detID::isDetITS3(frame.sens)) { + return true; + } + + const int sensorID = constants::detID::getSensorID(frame.sens); + const int layerID = constants::detID::getDetID2Layer(frame.sens); + + // --- Legendre deformation (non-rigid-body) --- + if (mParams->doMisalignmentLeg && mIsITS3 && mUseMC) { + const auto prop = o2::base::PropagatorD::Instance(); + + const auto lbl = mRecoData->getITSTracksMCLabels()[iTrk]; + const auto mcTrk = mcReader->getTrack(lbl); + if (!mcTrk) { + return false; + } + std::array xyz{mcTrk->GetStartVertexCoordinatesX(), mcTrk->GetStartVertexCoordinatesY(), mcTrk->GetStartVertexCoordinatesZ()}; + std::array pxyz{mcTrk->GetStartVertexMomentumX(), mcTrk->GetStartVertexMomentumY(), mcTrk->GetStartVertexMomentumZ()}; + TParticlePDG* pPDG = TDatabasePDG::Instance()->GetParticle(mcTrk->GetPdgCode()); + if (!pPDG) { + return false; + } + o2::track::TrackParD mcPar(xyz, pxyz, TMath::Nint(pPDG->Charge() / 3), false); + + const double r = frame.x; + const double gloX = r * std::cos(frame.alpha); + const double gloY = r * std::sin(frame.alpha); + const double gloZ = frame.positionTrackingFrame[1]; + auto [u, v] = computeUV(gloX, gloY, gloZ, sensorID, constants::radii[layerID]); + const double h = mDeformations[sensorID](u, v); + + auto mcAtCl = mcPar; + if (!mcAtCl.rotate(frame.alpha) || !prop->PropagateToXBxByBz(mcAtCl, frame.x)) { + return false; + } + + const double snp = mcAtCl.getSnp(); + const double tgl = mcAtCl.getTgl(); + const double csci = 1. / std::sqrt(1. - (snp * snp)); + const double dydx = snp * csci; + const double dzdx = tgl * csci; + const double dy = dydx * h; + const double dz = dzdx * h; + + const double newGloY = (r * std::sin(frame.alpha)) + (dy * std::cos(frame.alpha)); + const double newGloX = (r * std::cos(frame.alpha)) - (dy * std::sin(frame.alpha)); + const double newGloZ = gloZ + dz; + auto [uNew, vNew] = computeUV(newGloX, newGloY, newGloZ, sensorID, constants::radii[layerID]); + if (std::abs(uNew) > 1. || std::abs(vNew) > 1.) { + return false; + } + + res[0] += dy; + res[1] += dz; + } + + // --- Rigid body misalignment --- + // Must use the same derivative chain as GBL: + // dres/da_parent = dres/da_TRK * J_L2T_tile * J_L2P_tile + // The tile is a pseudo-volume; Millepede fits at the halfBarrel (parent) level. + if (mParams->doMisalignmentRB) { + GlobalLabel lbl(0, frame.sens, true); + if (mChip2Hiearchy.find(lbl) == mChip2Hiearchy.end()) { + return true; // sensor not in hierarchy, skip + } + const auto* tileVol = mChip2Hiearchy.at(lbl); + + // derivative in TRK frame (3x6: rows = dy, dz, dsnp) + Matrix36 der = getRigidBodyDerivatives(wTrk); + + // TRK -> tile LOC + const double posTrk[3] = {frame.x, 0., 0.}; + double posLoc[3]; + tileVol->getT2L().LocalToMaster(posTrk, posLoc); + Matrix66 jacL2T; + tileVol->computeJacobianL2T(posLoc, jacL2T); + der *= jacL2T; + + // tile LOC -> halfBarrel LOC (same chain as GBL hierarchy walk) + der *= tileVol->getJL2P(); + + // apply: delta_res = der * delta_a_halfBarrel + Eigen::Vector3d shift = der * mRigidBodyParams[sensorID]; + res[0] += shift[0]; // dy + res[1] += shift[1]; // dz + } + + return true; +} + +void AlignmentSpec::endOfStream(EndOfStreamContext& /*ec*/) +{ + mDBGOut->Close(); + mDBGOut.reset(); +} + +void AlignmentSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } + if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { + LOG(info) << "its cluster dictionary updated"; + mITSDict = (const o2::itsmft::TopologyDictionary*)obj; + return; + } + if (matcher == ConcreteDataMatcher("IT3", "CLUSDICT", 0)) { + LOG(info) << "it3 cluster dictionary updated"; + mIT3Dict = (const o2::its3::TopologyDictionary*)obj; + return; + } +} + +DataProcessorSpec getAlignmentSpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC, bool withPV, bool withITS, OutputEnum out) +{ + auto dataRequest = std::make_shared(); + std::shared_ptr ggRequest{nullptr}; + if (!out[OutputOpt::MilleRes]) { + dataRequest->requestTracks(srcTracks, useMC); + if (!withITS) { + dataRequest->requestIT3Clusters(useMC); + } else { + dataRequest->requestClusters(srcClusters, useMC); + } + if (withPV && !useMC) { + dataRequest->requestPrimaryVertices(useMC); + } + ggRequest = std::make_shared(false, // orbitResetTime + false, // GRPECS=true + true, // GRPLHCIF + true, // GRPMagField + true, // askMatLUT + o2::base::GRPGeomRequest::Aligned, // geometry + dataRequest->inputs, // inputs + true, // askOnce + true); // propagatorD + } else { + dataRequest->inputs.emplace_back("dummy", "GLO", "DUMMY_OUT", 0); + ggRequest = std::make_shared(false, // orbitResetTime + false, // GRPECS=true + false, // GRPLHCIF + false, // GRPMagField + false, // askMatLUT + o2::base::GRPGeomRequest::Aligned, // geometry + dataRequest->inputs); + } + + Options opts{ + {"nthreads", VariantType::Int, 1, {"number of threads"}}, + }; + + return DataProcessorSpec{ + .name = "its3-alignment", + .inputs = dataRequest->inputs, + .outputs = {}, + .algorithm = AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, useMC, withPV, withITS, out)}, + .options = opts}; +} +} // namespace o2::its3::align diff --git a/Detectors/Upgrades/ITS3/alignment/src/AlignmentTypes.cxx b/Detectors/Upgrades/ITS3/alignment/src/AlignmentTypes.cxx new file mode 100644 index 0000000000000..5ad06a6c78381 --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/src/AlignmentTypes.cxx @@ -0,0 +1,24 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include + +#include "ITS3Align/AlignmentTypes.h" +ClassImp(o2::its3::align::Point); +ClassImp(o2::its3::align::FrameInfoExt); +ClassImp(o2::its3::align::FitInfo); +ClassImp(o2::its3::align::Track); + +std::string o2::its3::align::FrameInfoExt::asString() const +{ + return std::format("Sensor={} Layer={} X={} Alpha={}\n\tMEAS: y={} z={}", sens, lr, x, alpha, positionTrackingFrame[0], positionTrackingFrame[1]); +} diff --git a/Detectors/Upgrades/ITS3/alignment/src/Deformations.cxx b/Detectors/Upgrades/ITS3/alignment/src/Deformations.cxx deleted file mode 100644 index 38a959cf7030f..0000000000000 --- a/Detectors/Upgrades/ITS3/alignment/src/Deformations.cxx +++ /dev/null @@ -1,41 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "ITS3Align/Deformations.h" -#include "ITS3Align/MisalignmentParameters.h" - -#include "Framework/Logger.h" - -#include - -namespace fs = std::filesystem; - -namespace o2::its3::align -{ - -void Deformations::init(const fs::path& path) -{ - if (!fs::exists(path)) { - LOGP(fatal, "File {} does not exists!", path.c_str()); - } - - auto params = MisalignmentParameters::load(path.string()); - LOGP(info, "Loaded Parameters"); - - // Set the legendre pols - for (int iSensor{0}; iSensor < 6; ++iSensor) { - mLegX[iSensor] = o2::math_utils::Legendre2DPolynominal(params->getLegendreCoeffX(iSensor)); - mLegY[iSensor] = o2::math_utils::Legendre2DPolynominal(params->getLegendreCoeffY(iSensor)); - mLegZ[iSensor] = o2::math_utils::Legendre2DPolynominal(params->getLegendreCoeffZ(iSensor)); - } -} - -} // namespace o2::its3::align diff --git a/Detectors/Upgrades/ITS3/alignment/src/ITS3AlignLinkDef.h b/Detectors/Upgrades/ITS3/alignment/src/ITS3AlignLinkDef.h index ef526284f3a58..e6e6a8c2cc73c 100644 --- a/Detectors/Upgrades/ITS3/alignment/src/ITS3AlignLinkDef.h +++ b/Detectors/Upgrades/ITS3/alignment/src/ITS3AlignLinkDef.h @@ -15,6 +15,12 @@ #pragma link off all classes; #pragma link off all functions; -#pragma link C++ class o2::its3::align::MisalignmentParameters + ; +#pragma link C++ struct o2::its3::align::AlignmentParams + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its3::align::AlignmentParams> + ; + +#pragma link C++ struct o2::its3::align::Measurement + ; +#pragma link C++ struct o2::its3::align::FrameInfoExt + ; +#pragma link C++ struct o2::its3::align::FitInfo + ; +#pragma link C++ struct o2::its3::align::Track + ; #endif diff --git a/Detectors/Upgrades/ITS3/alignment/src/MisalignmentHits.cxx b/Detectors/Upgrades/ITS3/alignment/src/MisalignmentHits.cxx deleted file mode 100644 index 66ab4c8090b54..0000000000000 --- a/Detectors/Upgrades/ITS3/alignment/src/MisalignmentHits.cxx +++ /dev/null @@ -1,368 +0,0 @@ -// Copyright 2020-2022 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "ITS3Align/MisalignmentHits.h" -#include "ITS3Base/ITS3Params.h" -#include "SimConfig/DigiParams.h" -#include "DetectorsBase/Propagator.h" -#include "Framework/Logger.h" - -#include "Math/Factory.h" -#include "Math/UnaryOperators.h" -#include "TGeoNode.h" -#include "TGeoBBox.h" -#include "TString.h" - -#include -#include -#include -#include - -namespace o2::its3::align -{ - -void MisAlignmentHits::init() -{ - if (o2::its3::ITS3Params::Instance().misalignmentHitsUseProp) { - mMethod = PropMethod::Propagator; - } else { - mMethod = PropMethod::Line; - } - - mGeo = o2::its::GeometryTGeo::Instance(); - - mMinimizer.reset(ROOT::Math::Factory::CreateMinimizer("Minuit2", "Migrad")); - if (mMinimizer == nullptr) { - LOGP(fatal, "Cannot create minimizer"); - } - mMinimizer->SetMaxFunctionCalls(1'000'000'000); - mMinimizer->SetStrategy(0); - mMinimizer->SetPrintLevel(0); - - if (mMethod == PropMethod::Propagator) { - LOGP(info, "Using propagator to find intersection"); - const auto& prefix = o2::conf::DigiParams::Instance().digitizationgeometry_prefix; - mMCReader = std::make_unique(prefix, o2::steer::MCKinematicsReader::Mode::kMCKine); - mMinimizer->SetFunction(mPropagator); - } else { - LOGP(info, "Using local straight-line to find intersection"); - mMinimizer->SetFunction(mLine); - } - - resetStats(); - - if (auto file = o2::its3::ITS3Params::Instance().misalignmentHitsParams; file.empty()) { - LOGP(fatal, "No parameter file specified"); - } else { - mDeformations.init(file); - } -} - -std::optional MisAlignmentHits::processHit(int iEvent, const o2::itsmft::Hit& hit) -{ - ++mStats[Stats::kHitTotal]; - - if (!constants::detID::isDetITS3(hit.GetDetectorID())) { - ++mStats[Stats::kHitIsOB]; - return hit; - } - ++mStats[Stats::kHitIsIB]; - - // Set the working hits - mCurHit = hit; - mCurWorkingHits[WorkingHit::kEntering] = WorkingHit(iEvent, WorkingHit::kEntering, hit); - mCurWorkingHits[WorkingHit::kExiting] = WorkingHit(iEvent, WorkingHit::kExiting, hit); - - // Do work - if (!deformHit(WorkingHit::kEntering) || !deformHit(WorkingHit::kExiting)) { - ++mStats[Stats::kHitDead]; - return std::nullopt; - } - ++mStats[Stats::kHitAlive]; - - // Set the possibly new detectorIDs with mid point approximation - auto midPointOrig = mCurWorkingHits[WorkingHit::kEntering].mPoint + (mCurWorkingHits[WorkingHit::kExiting].mPoint - mCurWorkingHits[WorkingHit::kEntering].mPoint) * 0.5; - auto midPointDef = mCurWorkingHits[WorkingHit::kEntering].mPointDef + (mCurWorkingHits[WorkingHit::kExiting].mPointDef - mCurWorkingHits[WorkingHit::kEntering].mPointDef) * 0.5; - const short idDef = getDetID(midPointDef), idOrig = getDetID(midPointOrig); - if (idDef == -1) { - return std::nullopt; - } - - if (idDef != idOrig) { - ++mStats[Stats::kHitMigrated]; - } else { - ++mStats[Stats::kHitNotMigrated]; - } - - if constexpr (false) { - /// TODO Does not yet work correctly - /// Check if we crossed a boundary within the entering and exiting hit from the midpoint - bool crossesBoundary{false}; - TGeoNode *nEnt{nullptr}, *nExt{nullptr}; - { - auto dirEnt = mCurWorkingHits[WorkingHit::kEntering].mPointDef - midPointDef; - auto stepEnt = std::min(static_cast(dirEnt.R()), std::abs(dirEnt.R() - 5.e-4)); - auto dirEntU = dirEnt.Unit(); - gGeoManager->SetCurrentPoint(midPointDef.X(), midPointDef.Y(), midPointDef.Z()); - gGeoManager->SetCurrentDirection(dirEntU.X(), dirEntU.Y(), dirEntU.Z()); - nEnt = gGeoManager->FindNextBoundaryAndStep(stepEnt, false); - if (gGeoManager->IsOnBoundary()) { - ++mStats[Stats::kHitEntBoundary]; - crossesBoundary = true; - } - } - { - auto dirExt = midPointDef - mCurWorkingHits[WorkingHit::kEntering].mPointDef; - auto stepExt = std::min(static_cast(dirExt.R()), std::abs(dirExt.R() - 5.e-4)); - auto dirExtU = dirExt.Unit(); - gGeoManager->SetCurrentPoint(midPointDef.X(), midPointDef.Y(), midPointDef.Z()); - gGeoManager->SetCurrentDirection(dirExtU.X(), dirExtU.Y(), dirExtU.Z()); - nExt = gGeoManager->FindNextBoundaryAndStep(stepExt, false); - if (gGeoManager->IsOnBoundary()) { - ++mStats[Stats::kHitExtBoundary]; - crossesBoundary = true; - } - } - - if (crossesBoundary && nEnt != nullptr && nExt != nullptr) { - if (nEnt != nExt) { - return std::nullopt; - } else { - ++mStats[Stats::kHitSameBoundary]; // indicates that the step size is too large and we end up in the mother volume; just pretend that his fine for now - } - } - ++mStats[Stats::kHitNoBoundary]; - } - - // Get new postion - mCurHit.SetPosStart(mCurWorkingHits[WorkingHit::kEntering].mPointDef); - mCurHit.SetPos(mCurWorkingHits[WorkingHit::kExiting].mPointDef); - mCurHit.SetDetectorID(idDef); - - ++mStats[Stats::kHitSuccess]; - return mCurHit; -} - -bool MisAlignmentHits::deformHit(WorkingHit::HitType t) -{ - auto& wHit = mCurWorkingHits[t]; - - mMinimizer->Clear(); // clear for next iteration - constexpr double minStep{1e-5}; - constexpr double zMargin{4.0}; - constexpr double phiMargin{0.4}; - if (mMethod == PropMethod::Line) { - prepareLineMethod(t); - mMinimizer->SetVariable(0, "t", 0.0, minStep); // this is left as a free parameter on since t is very small since start and end of hit are close - } else { - if (!preparePropagatorMethod(t)) { - return false; - } - mMinimizer->SetVariable(0, "r", mPropagator.mTrack.getX(), minStep); // this is left as a free parameter on since t is very small since start and end of hit are close - } - mMinimizer->SetLimitedVariable(1, "phiStar", wHit.mPhi, minStep, - std::max(static_cast(wHit.mPhiBorder1), static_cast(wHit.mPhi) - phiMargin), - std::min(static_cast(wHit.mPhiBorder2), static_cast(wHit.mPhi) + phiMargin)); - mMinimizer->SetLimitedVariable(2, "zStar", wHit.mPoint.Z(), minStep, - std::max(static_cast(-constants::segment::lengthSensitive / 2.f), static_cast(wHit.mPoint.Z()) - zMargin), - std::min(static_cast(constants::segment::lengthSensitive / 2.f), static_cast(wHit.mPoint.Z()) + zMargin)); - - mMinimizer->Minimize(); // perform the actual minimization - - auto ss = mMinimizer->Status(); - if (ss == 1) { - ++mStats[Stats::kMinimizerCovPos]; - } else if (ss == 2) { - ++mStats[Stats::kMinimizerHesse]; - } else if (ss == 3) { - ++mStats[Stats::kMinimizerEDM]; - } else if (ss == 4) { - ++mStats[Stats::kMinimizerLimit]; - } else if (ss == 5) { - ++mStats[Stats::kMinimizerOther]; - } else { - ++mStats[Stats::kMinimizerConverged]; - } - - if (ss == 0 || ss == 1) { // for Minuit2 0=ok, 1=ok with pos. forced hesse - ++mStats[Stats::kMinimizerStatusOk]; - if (mMinimizer->MinValue() < 2e-4) { // within 2 um considering the pixel pitch this good enough - ++mStats[Stats::kMinimizerValueOk]; - } else { - ++mStats[Stats::kMinimizerValueBad]; - return false; - } - } else { - ++mStats[Stats::kMinimizerStatusBad]; - return false; - } - - // Valid solution found; calculate new position on ideal geo - wHit.recalculateIdeal(static_cast(mMinimizer->X()[1]), static_cast(mMinimizer->X()[2])); - - return true; -} - -short MisAlignmentHits::getDetID(const o2::math_utils::Point3D& point) -{ - // Do not modify the path, I do not know if this is needed but lets be safe - gGeoManager->PushPath(); - auto id = getDetIDFromCords(point); - gGeoManager->PopPath(); - return id; -} - -short MisAlignmentHits::getDetIDFromCords(const o2::math_utils::Point3D& point) -{ - // retrive if any the node which constains the point - const auto node = gGeoManager->FindNode(point.X(), point.Y(), point.Z()); - if (node == nullptr) { - ++mStats[Stats::kFindNodeFailed]; - return -1; - } - ++mStats[Stats::kFindNodeSuccess]; - - // check if this node is a sensitive volume - const std::string path = gGeoManager->GetPath(); - if (path.find(o2::its::GeometryTGeo::getITS3SensorPattern()) == std::string::npos) { - ++mStats[Stats::kProjNonSensitive]; - return -1; - } - ++mStats[Stats::kProjSensitive]; - - return getDetIDFromPath(path); -} - -short MisAlignmentHits::getDetIDFromPath(const std::string& path) const -{ - static const std::regex pattern{R"(/cave_1/barrel_1/ITSV_2/ITSUWrapVol0_1/ITS3Layer(\d+)_(\d+)/ITS3CarbonForm(\d+)_(\d+)/ITS3Chip(\d+)_(\d+)/ITS3Segment(\d+)_(\d+)/ITS3RSU(\d+)_(\d+)/ITS3Tile(\d+)_(\d+)/ITS3PixelArray(\d+)_(\d+))"}; - if (std::smatch matches; std::regex_search(path, matches, pattern)) { - if (matches.size() == 15) { - int iLayer = std::stoi(matches[1]); - int iCarbonForm = std::stoi(matches[4]); - int iSegment = std::stoi(matches[8]); - int iRSU = std::stoi(matches[10]); - int iTile = std::stoi(matches[12]); - return mGeo->getChipIndex(iLayer, iCarbonForm, 0, iSegment, iRSU, iTile); - } else { - LOGP(fatal, "Path did not contain expected number of matches ({})!", matches.size()); - } - } else { - LOGP(fatal, "Path was not matched ({})!", path); - } - __builtin_unreachable(); -} - -void MisAlignmentHits::printStats() const -{ - auto makeFraction = [&](Stats n, Stats d) -> float { return static_cast(mStats[n]) / static_cast(mStats[d] + mStats[n]); }; - LOGP(info, "Processed {} Hits (IB:{}; OB:{}) ({:.2f}%):", mStats[Stats::kHitTotal], mStats[Stats::kHitIsIB], mStats[Stats::kHitIsOB], makeFraction(Stats::kHitIsIB, Stats::kHitIsOB)); - LOGP(info, " - Minimizer Status: {} ok {} bad ({:.2f}%)", mStats[Stats::kMinimizerStatusOk], mStats[Stats::kMinimizerStatusBad], makeFraction(Stats::kMinimizerStatusOk, Stats::kMinimizerStatusBad)); - LOGP(info, " - Minimizer Value: {} ok {} bad ({:.2f}%)", mStats[Stats::kMinimizerValueOk], mStats[Stats::kMinimizerValueBad], makeFraction(Stats::kMinimizerValueOk, Stats::kMinimizerValueBad)); - LOGP(info, " - Minimizer Detailed: {} Converged {} pos. forced Hesse ({:.2f}%)", mStats[Stats::kMinimizerConverged], mStats[Stats::kMinimizerHesse], makeFraction(Stats::kMinimizerConverged, Stats::kMinimizerHesse)); - LOGP(info, " - Minimizer Detailed: {} EDM {} call limit {} other ({:.2f}%)", mStats[Stats::kMinimizerEDM], mStats[Stats::kMinimizerLimit], mStats[Stats::kMinimizerOther], makeFraction(Stats::kMinimizerEDM, Stats::kMinimizerLimit)); - LOGP(info, " - FindNode: {} ok {} failed", mStats[Stats::kFindNodeSuccess], mStats[Stats::kFindNodeFailed]); - LOGP(info, " - IsSensitve: {} yes {} no ({:.2f}%)", mStats[Stats::kProjSensitive], mStats[Stats::kProjNonSensitive], makeFraction(Stats::kProjSensitive, Stats::kProjNonSensitive)); - LOGP(info, " - IsAlive: {} yes {} no ({:.2f}%)", mStats[Stats::kHitAlive], mStats[Stats::kHitDead], makeFraction(Stats::kHitAlive, Stats::kHitDead)); - LOGP(info, " - HasMigrated: {} yes {} no ({:.2f}%)", mStats[Stats::kHitMigrated], mStats[Stats::kHitNotMigrated], makeFraction(Stats::kHitMigrated, Stats::kHitNotMigrated)); - // LOGP(info, " - Crosses Boundary: {} entering {} exiting {} same {} no", mStats[Stats::kHitEntBoundary], mStats[Stats::kHitExtBoundary], mStats[Stats::kHitSameBoundary], mStats[Stats::kHitNoBoundary]); - if (mMethod == PropMethod::Propagator) { - LOGP(info, " - Propagator: {} null track {} null pdg", mStats[Stats::kPropTrackNull], mStats[Stats::kPropPDGNull]); - } - LOGP(info, " --> Good Hits {} ({:.2f}%)", mStats[Stats::kHitSuccess], makeFraction(Stats::kHitSuccess, Stats::kHitIsIB)); -} - -void MisAlignmentHits::prepareLineMethod(WorkingHit::HitType from) -{ - // Set the starint point and radius - // always start from the entering hit that way t is always pos. defined - mLine.mStart = mCurWorkingHits[WorkingHit::kEntering].mPoint; - mLine.mRadius = mCurWorkingHits[from].mRadius; - mLine.mSensorID = mCurWorkingHits[from].mSensorID; - mLine.mPhiTot = mCurWorkingHits[from].mPhiBorder2 - mCurWorkingHits[from].mPhiBorder1; - mLine.mPhi1 = mCurWorkingHits[from].mPhiBorder1; - // Calculate the direction vector - mLine.mD[0] = mCurWorkingHits[WorkingHit::kExiting].mPoint.X() - mCurWorkingHits[WorkingHit::kEntering].mPoint.X(); - mLine.mD[1] = mCurWorkingHits[WorkingHit::kExiting].mPoint.Y() - mCurWorkingHits[WorkingHit::kEntering].mPoint.Y(); - mLine.mD[2] = mCurWorkingHits[WorkingHit::kExiting].mPoint.Z() - mCurWorkingHits[WorkingHit::kEntering].mPoint.Z(); -} - -double MisAlignmentHits::StraightLine::DoEval(const double* x) const -{ - const double t = x[0]; - const double phi = x[1]; - const double z = x[2]; - const double nphi = std::clamp((phi - mPhi1) * 2.0 / mPhiTot - 1.0, -1.0, 1.0); - const double nz = std::clamp((z - (-constants::segment::lengthSensitive / 2.0)) * 2.0 / constants::segment::lengthSensitive - 1.0, -1.0, 1.0); - - /// Find the point along the line given current t - double xline = mStart.X() + t * mD[0], - yline = mStart.Y() + t * mD[1], - zline = mStart.Z() + t * mD[2]; - - // Find the point of the deformed geometry given a certain phi' and z' - double xideal = mRadius * std::cos(phi), yideal = mRadius * std::sin(phi), - zideal = z; - const auto [dx, dy, dz] = mMis->getDeformation(mSensorID, nphi, nz); - double xdef = xideal + dx, ydef = yideal + dy, zdef = zideal + dz; - - // Minimize the euclidean distance of the line point and the deformed point - return std::hypot(xline - xdef, yline - ydef, zline - zdef); -} - -bool MisAlignmentHits::preparePropagatorMethod(WorkingHit::HitType from) -{ - mPropagator.mRadius = mCurWorkingHits[from].mRadius; - mPropagator.mSensorID = mCurWorkingHits[from].mSensorID; - mPropagator.mPhiTot = mCurWorkingHits[from].mPhiBorder2 - mCurWorkingHits[from].mPhiBorder1; - mPropagator.mPhi1 = mCurWorkingHits[from].mPhiBorder1; - const auto mcTrack = mMCReader->getTrack(mCurWorkingHits[from].mEvent, mCurWorkingHits[from].mTrackID); - if (mcTrack == nullptr) { - ++mStats[Stats::kPropTrackNull]; - return false; - } - const std::array xyz{(float)mcTrack->GetStartVertexCoordinatesX(), (float)mcTrack->GetStartVertexCoordinatesY(), (float)mcTrack->GetStartVertexCoordinatesZ()}, - pxyz{(float)mcTrack->GetStartVertexMomentumX(), (float)mcTrack->GetStartVertexMomentumY(), (float)mcTrack->GetStartVertexMomentumZ()}; - const TParticlePDG* pPDG = TDatabasePDG::Instance()->GetParticle(mcTrack->GetPdgCode()); - if (pPDG == nullptr) { - ++mStats[Stats::kPropPDGNull]; - return false; - } - mPropagator.mTrack = o2::track::TrackPar(xyz, pxyz, TMath::Nint(pPDG->Charge() / 3), false); - mPropagator.mBz = o2::base::Propagator::Instance()->getNominalBz(); - return true; -} - -double MisAlignmentHits::Propagator::DoEval(const double* x) const -{ - const double r = x[0]; - const double phi = x[1]; - const double z = x[2]; - const double nphi = (phi - mPhi1) * 2.0 / mPhiTot - 1.0; - const double nz = (z - (-constants::segment::lengthSensitive / 2.0)) * 2.0 / constants::segment::lengthSensitive - 1.0; - - auto trc = mTrack; - if (!trc.propagateTo(r, mBz)) { - return 999; - } - const auto glo = trc.getXYZGlo(); - - // Find the point of the deformed geometry given a certain phi' and z' - double xideal = mRadius * std::cos(phi), yideal = mRadius * std::sin(phi), - zideal = z; - const auto [dx, dy, dz] = mMis->getDeformation(mSensorID, nphi, nz); - double xdef = xideal + dx, ydef = yideal + dy, zdef = zideal + dz; - - // Minimize the euclidean distance of the propagator point and the deformed point - return std::hypot(glo.X() - xdef, glo.Y() - ydef, glo.Z() - zdef); -} - -} // namespace o2::its3::align diff --git a/Detectors/Upgrades/ITS3/alignment/src/MisalignmentManager.cxx b/Detectors/Upgrades/ITS3/alignment/src/MisalignmentManager.cxx deleted file mode 100644 index c9d71541bcd0e..0000000000000 --- a/Detectors/Upgrades/ITS3/alignment/src/MisalignmentManager.cxx +++ /dev/null @@ -1,195 +0,0 @@ -// Copyright 2020-2022 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include "Framework/Logger.h" -#include "ITS3Align/MisalignmentManager.h" -#include "ITS3Align/MisalignmentHits.h" -#include "SimConfig/DigiParams.h" - -#include "TFile.h" -#include "TStopwatch.h" -#include "TGeoManager.h" - -#include -#include -#include -#include -#include -#include - -namespace fs = std::filesystem; - -namespace o2::its3::align -{ - -void MisalignmentManager::createBackup(const fs::path& src, const fs::path& dest) -{ - if (fs::exists(dest)) { - LOGP(info, "Previous orignal file found, using this as src"); - } else { - if (!fs::exists(src)) { - LOGP(fatal, "File {} does not exist", src.c_str()); - } - LOGP(info, "Trying to backup file to {}", dest.c_str()); - try { - fs::rename(src, dest); - } catch (const fs::filesystem_error& err) { - LOGP(fatal, "Cannot create backup file for Hit-File: {}", err.what()); - } - } -} - -void MisalignmentManager::misalignHits() -{ - LOGP(info, "{:*^90}", " ITS3 LOCAL MISALIGNMENT START "); - - TStopwatch timer; - timer.Start(); - - MisAlignmentHits MisAligner; - MisAligner.init(); - - const fs::path oldHitFileSrc{fs::current_path().string() + "/" + o2::conf::DigiParams::Instance().digitizationgeometry_prefix + "_HitsIT3.root"}; - const fs::path oldHitFileDest{fs::current_path().string() + "/" + o2::conf::DigiParams::Instance().digitizationgeometry_prefix + "_HitsIT3_Orig.root"}; - createBackup(oldHitFileSrc, oldHitFileDest); - - std::unique_ptr origFile{TFile::Open(oldHitFileDest.c_str(), "READ")}; - if (origFile == nullptr || origFile->IsZombie()) { - LOGP(fatal, "Original file {} cannot be opened", oldHitFileDest.c_str()); - } - - std::unique_ptr origTree{origFile->Get("o2sim")}; - if (origTree == nullptr) { - LOGP(fatal, "Cannot get hit-tree from orignal file"); - } - std::vector origHits, *origHitsPtr{&origHits}; - origTree->SetBranchAddress("IT3Hit", &origHitsPtr); - - std::unique_ptr newFile{TFile::Open(oldHitFileSrc.c_str(), "RECREATE")}; - if (newFile == nullptr || newFile->IsZombie()) { - LOGP(fatal, "New file {} cannot be opened", oldHitFileSrc.c_str()); - } - - auto newTree = std::make_unique("o2sim", "o2sim"); - std::vector newHits, *newHitsPtr{nullptr}; - newTree->Branch("IT3Hit", &newHitsPtr); - - LOGP(info, "Preparations done; starting hit loop"); - auto nEntries = origTree->GetEntries(); - ULong64_t totalOrigHits{0}, totalNewHits{0}; - for (Long64_t iEntry{0}; origTree->LoadTree(iEntry) >= 0; ++iEntry) { - if (origTree->GetEntry(iEntry) <= 0) { - continue; - } - - const auto progress = (iEntry * 100) / nEntries; - LOG_IF(info, progress % 10 == 0) << "Processing event " << iEntry << " / " << nEntries; - - newHits.clear(); - newHits.reserve(origHits.size()); - for (const auto& origHit : origHits) { - if (auto newHit = MisAligner.processHit(iEntry, origHit)) { - newHits.emplace_back(*newHit); - } - } - - newHitsPtr = &newHits; - newTree->Fill(); - - totalNewHits += newHits.size(); - totalOrigHits += origHits.size(); - } - - newFile->WriteTObject(newTree.get()); - - timer.Stop(); - - MisAligner.printStats(); - - auto totalDiscardedHits = totalOrigHits - totalNewHits; - LOGP(info, "Summary: Total orignal Hits {}", totalOrigHits); - LOGP(info, "Summary: Total misaligned Hits {} ({:.2f}%)", totalNewHits, static_cast(totalNewHits) / static_cast(totalOrigHits) * 100); - LOGP(info, "Summary: Total discarded Hits {} ({:.2f}%)", totalDiscardedHits, static_cast(totalDiscardedHits) / static_cast(totalOrigHits) * 100); - LOGP(info, "Summary: Misalignment took {:.2f}s", timer.CpuTime()); - LOGP(info, "{:*^90}", " ITS3 LOCAL MISALIGNMENT END "); -} - -std::string MisalignmentManager::appendStem(const std::string& filename, const std::string& add) -{ - fs::path filepath{filename}; - auto stem = filepath.stem().string(); - auto extension = filepath.extension().string(); - return stem + add + extension; -} - -std::vector MisalignmentManager::split(const std::string& s, char delimiter) -{ - std::vector tokens; - std::string token; - std::istringstream tokenStream(s); - while (std::getline(tokenStream, token, delimiter)) { - if (!token.empty()) { - tokens.push_back(token); - } - } - return tokens; -} - -void MisalignmentManager::navigate(const std::string& path) -{ - if (!gGeoManager->cd(path.c_str())) { - LOGP(fatal, "Cannot navigate to {}", path); - } -} - -std::string MisalignmentManager::composePathSensor(int sensor) -{ - const int layerID{sensor / 2}; - const int sensorID{sensor % 2}; - return fmt::format("/cave/barrel_1/ITSV_2/ITSUWrapVol0_1/ITS3Layer{}_0/ITS3CarbonForm{}_{}", - layerID, layerID, sensorID); -} - -void MisalignmentManager::applyGlobalMatrixVolume(const std::string& path, const TGeoHMatrix& globalMatrix) -{ - gGeoManager->CdTop(); - TGeoHMatrix* pgMatrix{nullptr}; - TGeoHMatrix gAccMatrix; - std::string curPath{}; - for (const auto& comp : split(path)) { - curPath += "/" + comp; - navigate(curPath); - pgMatrix = gGeoManager->GetCurrentMatrix(); - gAccMatrix.Multiply(pgMatrix); - } - navigate(path); - auto node = gGeoManager->GetCurrentNode(); - if (node == nullptr) { - LOGP(fatal, "Nullptr for node at {}", path); - } - auto motherVol = node->GetMotherVolume(); - if (motherVol == nullptr) { - LOGP(fatal, "Nullptr for motherVol at {}", path); - } - // Compute the inverse of the accumulated global transformation matrix - auto gAccMatrix1 = gAccMatrix.Inverse(); - // Compute the relative transformation matrix for the volume - auto relativeMatrix = globalMatrix; - relativeMatrix.MultiplyLeft(gAccMatrix1); - - auto nodemat = dynamic_cast(node); - nodemat->SetMatrix(new TGeoHMatrix(globalMatrix)); - - // Force the container volume of the object to update itself - motherVol->Voxelize(""); -} - -} // namespace o2::its3::align diff --git a/Detectors/Upgrades/ITS3/alignment/src/MisalignmentParameters.cxx b/Detectors/Upgrades/ITS3/alignment/src/MisalignmentParameters.cxx deleted file mode 100644 index 0842b7252486a..0000000000000 --- a/Detectors/Upgrades/ITS3/alignment/src/MisalignmentParameters.cxx +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file MisalignmentParameter.cxx -/// \brief Implementation of the MisalignmentParameter class - -#include "ITS3Align/MisalignmentParameters.h" -#include "Framework/Logger.h" - -#include "TFile.h" - -#include - -ClassImp(o2::its3::align::MisalignmentParameters); - -namespace o2::its3::align -{ - -MisalignmentParameters::MisalignmentParameters() -{ - SetName("MisalignmentParameters"); - SetTitle("ITS3 MisalignmentParameters"); -} - -bool MisalignmentParameters::store(const std::string& file) const -{ - std::unique_ptr fOut(TFile::Open(file.c_str(), "RECREATE")); - if (fOut == nullptr || fOut->IsZombie()) { - LOGP(info, "Unable to save misalignment parameters"); - return false; - } - fOut->WriteObjectAny(this, "o2::its3::align::MisalignmentParameters", "ccdb_object"); - return true; -} - -MisalignmentParameters* MisalignmentParameters::load(const std::string& file) -{ - std::unique_ptr fIn(TFile::Open(file.c_str(), "READ")); - auto p = fIn->Get("ccdb_object"); - if (p == nullptr) { - LOGP(fatal, "Unable to load parameters from file!"); - } - return p; -} - -void MisalignmentParameters::printParams(unsigned int detID) const -{ - LOGP(info, "Parameters for ID={}:", detID); - LOGP(info, " - Global Trans: X={} Y={} Z={}", getGloTransX(detID), getGloTransY(detID), getGloTransZ(detID)); - LOGP(info, " - Global Rots: X={} Y={} Z={}", getGloRotX(detID), getGloRotY(detID), getGloRotZ(detID)); - if (constants::detID::isDetITS3(detID)) { - auto sensorID = constants::detID::getSensorID(detID); - LOGP(info, " - Legendre Pol X:"); - getLegendreCoeffX(sensorID).Print(); - LOGP(info, " - Legendre Pol Y:"); - getLegendreCoeffY(sensorID).Print(); - LOGP(info, " - Legendre Pol Z:"); - getLegendreCoeffZ(sensorID).Print(); - } -} - -void MisalignmentParameters::printLegendreParams(unsigned int sensorID) const -{ - LOGP(info, " - Legendre Pol X:"); - getLegendreCoeffX(sensorID).Print(); - LOGP(info, " - Legendre Pol Y:"); - getLegendreCoeffY(sensorID).Print(); - LOGP(info, " - Legendre Pol Z:"); - getLegendreCoeffZ(sensorID).Print(); -} - -} // namespace o2::its3::align diff --git a/Detectors/Upgrades/ITS3/alignment/src/alignment-workflow.cxx b/Detectors/Upgrades/ITS3/alignment/src/alignment-workflow.cxx new file mode 100644 index 0000000000000..6ec7615885556 --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/src/alignment-workflow.cxx @@ -0,0 +1,71 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "CommonUtils/ConfigurableParam.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/CallbacksPolicy.h" +#include "GlobalTrackingWorkflowHelpers/InputHelper.h" +#include "GlobalTrackingWorkflowHelpers/NoInpDummyOutSpec.h" +#include "DetectorsRaw/HBFUtilsInitializer.h" +#include "ITS3Align/AlignmentSpec.h" + +using namespace o2::framework; +using namespace o2::its3::align; +using GID = o2::dataformats::GlobalTrackID; +using DetID = o2::detectors::DetID; + +void customize(std::vector& policies) +{ + o2::raw::HBFUtilsInitializer::addNewTimeSliceCallback(policies); +} +void customize(std::vector& workflowOptions) +{ + std::vector options{ + {"disable-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"}}, + {"with-its", VariantType::Bool, false, {"ITS alignment mode"}}, + {"without-pv", VariantType::Bool, false, {"Do not use in track refit the PV as an additional constraint"}}, + {"output", VariantType::String, "", {"output steering"}}, + {"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& cfg) +{ + o2::conf::ConfigurableParam::updateFromString(cfg.options().get("configKeyValues")); + const GID::mask_t allowedSourcesTrc = GID::getSourcesMask("ITS,TPC,ITS-TPC,ITS-TPC-TRD,ITS-TPC-TOF,ITS-TPC-TRD-TOF"); + const GID::mask_t allowedSourcesClus = GID::getSourcesMask("ITS"); + const GID::mask_t srcTrc = allowedSourcesTrc & GID::getSourcesMask(cfg.options().get("track-sources")); + const GID::mask_t srcCls = allowedSourcesClus & GID::getSourcesMask(cfg.options().get("cluster-sources")); + const auto useMC = !cfg.options().get("disable-mc"); + const auto withPV = !cfg.options().get("without-pv"); + const auto withITS = cfg.options().get("with-its"); + const OutputEnum output(cfg.options().get("output")); + + WorkflowSpec specs; + if (!output[OutputOpt::MilleRes]) { + o2::globaltracking::InputHelper::addInputSpecs(cfg, specs, srcCls, srcTrc, srcTrc, useMC); + if (withPV && !useMC) { + o2::globaltracking::InputHelper::addInputSpecsPVertex(cfg, specs, useMC); + } + } else { + specs.emplace_back(o2::globaltracking::getNoInpDummyOutSpec(0)); + } + + specs.emplace_back(o2::its3::align::getAlignmentSpec(srcTrc, srcCls, useMC, withPV, withITS, output)); + + o2::raw::HBFUtilsInitializer hbfIni(cfg, specs); + return std::move(specs); +} diff --git a/Detectors/Upgrades/ITS3/base/include/ITS3Base/ITS3Params.h b/Detectors/Upgrades/ITS3/base/include/ITS3Base/ITS3Params.h index 0bd548cef953d..1e3f4f47b8a29 100644 --- a/Detectors/Upgrades/ITS3/base/include/ITS3Base/ITS3Params.h +++ b/Detectors/Upgrades/ITS3/base/include/ITS3Base/ITS3Params.h @@ -19,11 +19,6 @@ namespace o2::its3 { struct ITS3Params : public o2::conf::ConfigurableParamHelper { - // Alignment studies - bool applyMisalignmentHits{false}; // Apply detector misalignment on hit level - std::string misalignmentHitsParams{}; // Path to parameter file for mis-alignment - bool misalignmentHitsUseProp{false}; // Use propagtor for mis-alignment - std::string globalGeoMisAlignerMacro{"${O2_ROOT}/share/macro/MisAlignGeoITS3.C"}; // Path to macro for global geometry mis-alignment // Chip studies bool useDeadChannelMap{false}; // Query for a dead channel map to study disabling individual tiles std::string chipResponseFunction{"APTS"}; // Chip response function one of "Alpide", "APTS" or "Mosaix" (not yet available) diff --git a/Detectors/Upgrades/ITS3/base/include/ITS3Base/SegmentationMosaix.h b/Detectors/Upgrades/ITS3/base/include/ITS3Base/SegmentationMosaix.h index fbf9a59e6da4b..088dd858fff73 100644 --- a/Detectors/Upgrades/ITS3/base/include/ITS3Base/SegmentationMosaix.h +++ b/Detectors/Upgrades/ITS3/base/include/ITS3Base/SegmentationMosaix.h @@ -151,7 +151,8 @@ class SegmentationMosaix } // Same as localToDetector w.o. checks. - constexpr void localToDetectorUnchecked(float const xRow, float const zCol, int& iRow, int& iCol) const noexcept + template + constexpr void localToDetectorUnchecked(T const xRow, T const zCol, int& iRow, int& iCol) const noexcept { iRow = static_cast(std::floor((WidthH - xRow) / PitchRow)); iCol = static_cast(std::floor((zCol + LengthH) / PitchCol)); @@ -167,7 +168,8 @@ class SegmentationMosaix /// center of the sensitive volume. /// If iRow and or iCol is outside of the segmentation range a value of -0.5*Dx() /// or -0.5*Dz() is returned. - bool detectorToLocal(float const row, float const col, float& xRow, float& zCol) const noexcept + template + bool detectorToLocal(T const row, T const col, L& xRow, L& zCol) const noexcept { if (!isValidDet(row, col)) { return false; @@ -178,15 +180,17 @@ class SegmentationMosaix // Same as detectorToLocal w.o. checks. // We position ourself in the middle of the pixel. - void detectorToLocalUnchecked(float const row, float const col, float& xRow, float& zCol) const noexcept + template + void detectorToLocalUnchecked(T const row, T const col, L& xRow, L& zCol) const noexcept { xRow = -(row + 0.5f) * PitchRow + WidthH; zCol = (col + 0.5f) * PitchCol - LengthH; } - bool detectorToLocal(float const row, float const col, math_utils::Point3D& loc) const noexcept + template + bool detectorToLocal(T const row, T const col, math_utils::Point3D& loc) const noexcept { - float xRow{0.}, zCol{0.}; + L xRow{0.}, zCol{0.}; if (!detectorToLocal(row, col, xRow, zCol)) { return false; } @@ -194,9 +198,10 @@ class SegmentationMosaix return true; } - void detectorToLocalUnchecked(float const row, float const col, math_utils::Point3D& loc) const noexcept + template + void detectorToLocalUnchecked(T const row, T const col, math_utils::Point3D& loc) const noexcept { - float xRow{0.}, zCol{0.}; + L xRow{0.}, zCol{0.}; detectorToLocalUnchecked(row, col, xRow, zCol); loc.SetCoordinates(xRow, 0.0f, zCol); } diff --git a/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h b/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h index 937fa8d2e982c..270b1a7148f61 100644 --- a/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h +++ b/Detectors/Upgrades/ITS3/base/include/ITS3Base/SpecsV2.h @@ -209,7 +209,7 @@ inline T getSensorID(T detID) template inline bool isDetITS3(T detID) { - return detID < static_cast(nChips); + return detID < static_cast(nChips) && detID >= 0; } } // namespace detID diff --git a/Detectors/Upgrades/ITS3/macros/align/CMakeLists.txt b/Detectors/Upgrades/ITS3/macros/align/CMakeLists.txt index 86ebd989133e0..1d5c18cc0f4f3 100644 --- a/Detectors/Upgrades/ITS3/macros/align/CMakeLists.txt +++ b/Detectors/Upgrades/ITS3/macros/align/CMakeLists.txt @@ -9,9 +9,5 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. -# install(FILES MisAlignGeoITS3.C DESTINATION share/macro/) -# its3_add_macro(MisAlignGeoITS3.C) -its3_add_macro(TestLegendrePol.C) -its3_add_macro(CreateMisalignmentITS3.C) -its3_add_macro(ShowCoefficients.C) its3_add_macro(CheckResidualsITS3.C) +its3_add_macro(CheckHitResiduals.C) diff --git a/Detectors/Upgrades/ITS3/macros/align/CheckHitResiduals.C b/Detectors/Upgrades/ITS3/macros/align/CheckHitResiduals.C new file mode 100644 index 0000000000000..555ba177a60ce --- /dev/null +++ b/Detectors/Upgrades/ITS3/macros/align/CheckHitResiduals.C @@ -0,0 +1,131 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does 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 "MathUtils/Utils.h" +#include "ITSMFTSimulation/Hit.h" +#include "ITS3Base/SpecsV2.h" +#endif + +void CheckHitResiduals(const std::string& hitOFile = "o2sim_HitsIT3_Orig.root", + const std::string& hitRFile = "o2sim_HitsIT3.root") +{ + gStyle->SetOptStat(0); + + TFile fileO(hitOFile.data()); + auto* hitOTree = dynamic_cast(fileO.Get("o2sim")); + std::vector* hitOArray = nullptr; + hitOTree->SetBranchAddress("IT3Hit", &hitOArray); + + TFile fileR(hitRFile.data()); + auto* hitRTree = dynamic_cast(fileR.Get("o2sim")); + std::vector* hitRArray = nullptr; + hitRTree->SetBranchAddress("IT3Hit", &hitRArray); + + struct Hits { + o2::itsmft::Hit oHit, rHit; + int oEve{-1}, rEve{-1}; + bool hasBoth() const noexcept { return oEve >= 0 && rEve >= 0 && oEve == rEve && oHit.GetDetectorID() == rHit.GetDetectorID() && rHit.GetTrackID() == oHit.GetTrackID(); } + }; + std::unordered_map hits; + + size_t total{0}; + for (int iEntry{0}; iEntry < hitOTree->GetEntries(); ++iEntry) { + hitOTree->GetEntry(iEntry); + total += hitOArray->size(); + for (const auto& h : *hitOArray) { + if (!o2::its3::constants::detID::isDetITS3(h.GetDetectorID())) { + continue; + } + uint64_t key = (uint64_t(iEntry) << 48) | (uint64_t(h.GetTrackID()) << 24) | uint64_t(h.GetDetectorID()); + auto& hh = hits[key]; + hh.oHit = h; + hh.oEve = iEntry; + } + } + printf("placed %zu hits saw %zu\n", hits.size(), total); + for (int iEntry{0}; iEntry < hitRTree->GetEntries(); ++iEntry) { + hitRTree->GetEntry(iEntry); + for (const auto& h : *hitRArray) { + if (!o2::its3::constants::detID::isDetITS3(h.GetDetectorID())) { + continue; + } + uint64_t key = (uint64_t(iEntry) << 48) | (uint64_t(h.GetTrackID()) << 24) | uint64_t(h.GetDetectorID()); + auto& hh = hits[key]; + hh.rHit = h; + hh.rEve = iEntry; + } + } + + // plot the residuals in dRPhi, dZ against ideal phi,z for each layer + std::array mDRPhi{}, mDZ{}; + for (int i{0}; i < 3; ++i) { + mDRPhi[i] = new TProfile2D(Form("hDRPhi_%d", i), Form("#Delta_{r#varphi};z (cm); r#varphi (cm)"), 100, -15, 15, 100, 0, 2 * TMath::Pi()); + mDRPhi[i]->SetDirectory(nullptr); + mDZ[i] = new TProfile2D(Form("hDZ_%d", i), Form("#Delta_{z};z (cm); r#varphi (cm)"), 100, -15, 15, 100, 0, 2 * TMath::Pi()); + mDZ[i]->SetDirectory(nullptr); + } + std::array cIB{}; + for (const auto& [_, h] : hits) { + if (h.hasBoth()) { + int chip = o2::its3::constants::detID::getSensorID(h.oHit.GetDetectorID()) / 2; + ++cIB[chip]; + auto gloO = h.oHit.GetPosStart(); + auto gloR = h.rHit.GetPosStart(); + // ideal (original) cluster: phi, z, rphi + float phiO = std::atan2(gloO.Y(), gloO.X()); + o2::math_utils::bringTo02Pi(phiO); + const float rO = std::hypot(gloO.X(), gloO.Y()); + + // deformed (reconstructed) cluster + float phiR = std::atan2(gloR.Y(), gloR.X()); + o2::math_utils::bringTo02Pi(phiR); + const float rR = std::hypot(gloR.X(), gloR.Y()); + + // residuals + const float dRPhi = rO * (phiR - phiO); // or use average r, doesn't matter at this precision + const float dZ = gloR.Z() - gloO.Z(); + + // fill vs (z, phi) of ideal position + mDRPhi[chip]->Fill(gloO.Z(), phiO, dRPhi * 1e4); + mDZ[chip]->Fill(gloO.Z(), phiO, dZ * 1e4); + } + } + for (int lay{0}; lay < 3; ++lay) { + printf("\t%d has %d\n", lay, cIB[lay]); + } + auto c1 = new TCanvas; + c1->Divide(3, 1); + for (int i{0}; i < 3; ++i) { + c1->cd(1 + i); + gPad->SetRightMargin(3); + mDRPhi[i]->GetZaxis()->SetRangeUser(-200, 200); + mDRPhi[i]->Draw("colz"); + } + c1->Draw(); + c1->SaveAs("its3_clus_res_rphi.pdf"); + auto c2 = new TCanvas; + c2->Divide(3, 1); + for (int i{0}; i < 3; ++i) { + c2->cd(1 + i); + gPad->SetRightMargin(3); + mDZ[i]->GetZaxis()->SetRangeUser(-200, 200); + mDZ[i]->Draw("colz"); + } + c2->Draw(); + c2->SaveAs("its3_clus_res_z.pdf"); +} diff --git a/Detectors/Upgrades/ITS3/macros/align/CreateMisalignmentITS3.C b/Detectors/Upgrades/ITS3/macros/align/CreateMisalignmentITS3.C deleted file mode 100644 index 8df00ee25de00..0000000000000 --- a/Detectors/Upgrades/ITS3/macros/align/CreateMisalignmentITS3.C +++ /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. - -#if !defined(__CLING__) || defined(__ROOTCLING__) -#include "TRandom.h" -#include "TMatrixD.h" - -#include "ITS3Align/MisalignmentParameters.h" -#endif - -void CreateMisalignmentITS3(bool dummy = false, bool manual = false) -{ - gRandom->SetSeed(42); - - // Legendre coeff. - constexpr int nOrder{2}; - auto getRandom = []() { - constexpr double scale{50.e-4}; - return scale * gRandom->Uniform(-1.0, 1.0); - }; - - auto getSign = []() { return gRandom->Uniform() ? -1.0 : 1.0; }; - - o2::its3::align::MisalignmentParameters params; - - if (dummy) { - TMatrixD coeffNull(0 + 1, 0 + 1); - for (int sensorID{0}; sensorID < 6; ++sensorID) { - params.setLegendreCoeffX(sensorID, coeffNull); - params.setLegendreCoeffY(sensorID, coeffNull); - params.setLegendreCoeffZ(sensorID, coeffNull); - } - } else if (manual) { - // (0,0) -> shift - // (1,0) -> - for (int sensorID{0}; sensorID < 6; ++sensorID) { - constexpr double scale{20e-4}; - TMatrixD coeffNull(1, 1); - - TMatrixD coeffMinusX(1 + 1, 1 + 1); - TMatrixD coeffPlusX(1 + 1, 1 + 1); - coeffMinusX(1, 1) = -scale; - coeffPlusX(1, 1) = scale; - - TMatrixD coeffMinusY(4 + 1, 4 + 1); - TMatrixD coeffPlusY(4 + 1, 4 + 1); - coeffMinusY(0, 0) = scale; - coeffPlusY(0, 0) = -scale; - coeffMinusY(4, 4) = -scale; - coeffPlusY(4, 4) = scale; - if (sensorID % 2 == 0) { - params.setLegendreCoeffX(sensorID, coeffPlusX); - params.setLegendreCoeffY(sensorID, coeffPlusY); - params.setLegendreCoeffZ(sensorID, coeffNull); - } else { - params.setLegendreCoeffX(sensorID, coeffMinusX); - params.setLegendreCoeffY(sensorID, coeffMinusY); - params.setLegendreCoeffZ(sensorID, coeffNull); - } - } - } else { - for (int sensorID{0}; sensorID < 6; ++sensorID) { - TMatrixD coeffX(nOrder + 1, nOrder + 1); - TMatrixD coeffY(nOrder + 1, nOrder + 1); - TMatrixD coeffZ(nOrder + 1, nOrder + 1); - for (int i{0}; i <= nOrder; ++i) { - for (int j{0}; j <= i; ++j) { - // some random scaling as higher order parameters have higher influence - coeffX(i, j) = getRandom() / (1.0 + i * j * 2.0); - coeffZ(i, j) = getRandom() / (1.0 + i * j * 2.0); - coeffY(i, j) = getRandom() / (1.0 + i * j * 2.0); - } - } - - params.setLegendreCoeffX(sensorID, coeffX); - params.setLegendreCoeffY(sensorID, coeffY); - params.setLegendreCoeffZ(sensorID, coeffZ); - } - } - - for (int sensorID{0}; sensorID < 6; ++sensorID) { - params.printLegendreParams(sensorID); - } - - params.store("misparams.root"); -} diff --git a/Detectors/Upgrades/ITS3/macros/align/MisAlignGeoITS3.notest b/Detectors/Upgrades/ITS3/macros/align/MisAlignGeoITS3.notest deleted file mode 100644 index 2a4bef978d0da..0000000000000 --- a/Detectors/Upgrades/ITS3/macros/align/MisAlignGeoITS3.notest +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2020-2022 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file MisAlignGeoITS3.C -/// \brief Misalign the global geometry of ITS3 -/// \author felix.schlepper@cern.ch - -#include "ITS3Align/MisalignmentManager.h" - -#include "TGeoManager.h" -#include "TGeoMatrix.h" -#include "TMath.h" - -#include "CommonUtils/ConfigurableParam.h" -#include "CommonUtils/ConfigurableParamHelper.h" -#include "Framework/Logger.h" -#include "ITS3Base/SpecsV2.h" - -#include - -#include "boost/property_tree/ptree.hpp" - -using MM = o2::its3::align::MisalignmentManager; -namespace fs = std::filesystem; -namespace pt = boost::property_tree; -namespace its3c = o2::its3::constants; - -#define DECLARE_SENSOR(id) \ - float Sensor##id##Dx = 0.f; \ - float Sensor##id##Dy = 0.f; \ - float Sensor##id##Dz = 0.f; \ - float Sensor##id##Phi = 0.f; \ - float Sensor##id##Theta = 0.f; \ - float Sensor##id##Psi = 0.f; - -struct MisAlignGlobalParams : public o2::conf::ConfigurableParamHelper { - DECLARE_SENSOR(0) - DECLARE_SENSOR(1) - DECLARE_SENSOR(2) - DECLARE_SENSOR(3) - DECLARE_SENSOR(4) - DECLARE_SENSOR(5) - - O2ParamDef(MisAlignGlobalParams, "MisAlignGlobalParams"); -}; -O2ParamImpl(MisAlignGlobalParams); - -void MisAlignGeoITS3(const std::string& configFilePath = "", bool _export = false, bool draw = false, bool check = false, const std::string& geomFile = "o2sim_geometry-aligned.root") -{ - LOGP(info, "{:*^90}", " ITS3 GLOBAL MISALIGNMENT START "); - auto& params = MisAlignGlobalParams::Instance(); - params.writeINI("default_parameters_global.ini", "MisAlignGlobalParams"); - if (configFilePath.empty()) { - LOGP(info, "No user config provided using defaults"); - } else { - LOGP(info, "User config at {}", configFilePath); - params.updateFromFile(configFilePath); - } - params.writeINI("used_parameters_global.ini", "MisAlignGlobalParams"); - params.printKeyValues(true, true); - - const fs::path srcFile{geomFile}; - const fs::path destFile{MM::appendStem(geomFile, "_Orig")}; - if (gGeoManager == nullptr) { - MM::createBackup(srcFile, destFile); - TGeoManager::Import(destFile.c_str()); - } - - LOGP(info, "Building matrices"); - std::array gRotoTranslations{}; - for (int iSensor{0}; iSensor < (int)its3c::nSensorsIB; ++iSensor) { - auto& mat = gRotoTranslations[iSensor]; - // Phi Z rotation angle (first) defined in [-PI,PI] - // Theta X rotation angle (second) defined only [0,PI] - // Psi Z rotation angle (third) defined in [-PI,PI] - MM::Euler3D euler{ - ((iSensor % 2 == 0) ? 0. : -TMath::Pi()) + - TMath::DegToRad() * params.getValueAs(fmt::format("MisAlignGlobalParams.Sensor{}Phi", iSensor)), - TMath::DegToRad() * params.getValueAs(fmt::format("MisAlignGlobalParams.Sensor{}Theta", iSensor)), - TMath::DegToRad() * params.getValueAs(fmt::format("MisAlignGlobalParams.Sensor{}Psi", iSensor)), - }; - MM::Rot3D rot(euler); - std::array rota; - rot.GetComponents(std::begin(rota)); - mat.SetRotation(rota.data()); - std::array trans{ - params.getValueAs(fmt::format("MisAlignGlobalParams.Sensor{}Dx", iSensor)), - params.getValueAs(fmt::format("MisAlignGlobalParams.Sensor{}Dy", iSensor)), - params.getValueAs(fmt::format("MisAlignGlobalParams.Sensor{}Dz", iSensor)), - }; - mat.SetTranslation(trans.data()); - } - - LOGP(info, "Appying Global RotoTranslations"); - for (int iSensor{0}; iSensor < (int)its3c::nSensorsIB; ++iSensor) { - auto path = MM::composePathSensor(iSensor); - auto& mat = gRotoTranslations[iSensor]; - MM::applyGlobalMatrixVolume(path, mat); - } - - if (_export) { - gGeoManager->Export(srcFile.c_str()); - } - if (draw) { - MM::navigate("cave/barrel_1/ITSV_2/ITSUWrapVol0_1"); - gGeoManager->GetCurrentVolume()->Draw(); - gGeoManager->SetTopVisible(); - gGeoManager->RestoreMasterVolume(); - } - if (check) { - gGeoManager->CdTop(); - gGeoManager->CloseGeometry(); - gGeoManager->CheckGeometryFull(); - gGeoManager->CheckOverlaps(0.1, "s"); - gGeoManager->PrintOverlaps(); - auto overlaps = gGeoManager->GetListOfOverlaps(); - overlaps->At(0)->Print(); - overlaps->At(0)->Draw("ogl"); - } - LOGP(info, "{:*^90}", " ITS3 GLOBAL MISALIGNMENT END "); -} diff --git a/Detectors/Upgrades/ITS3/macros/align/ShowCoefficients.C b/Detectors/Upgrades/ITS3/macros/align/ShowCoefficients.C deleted file mode 100644 index 42749b707e81d..0000000000000 --- a/Detectors/Upgrades/ITS3/macros/align/ShowCoefficients.C +++ /dev/null @@ -1,333 +0,0 @@ -// Copyright 2020-2022 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#if !defined(__CLING__) || defined(__ROOTCLING__) -#include "Math/Factory.h" -#include "Math/Minimizer.h" -#include "TAxis.h" -#include "TCanvas.h" -#include "TGraph.h" -#include "TGraph2D.h" -#include "TLegend.h" -#include "TMarker.h" -#include "TMultiGraph.h" -#include "TRandom.h" -#include "TMath.h" - -#include "MathUtils/LegendrePols.h" -#include "ITS3Align/Deformations.h" -#include "MathUtils/Utils.h" -#endif - -static ROOT::Math::Minimizer* gMin; - -void ShowCoefficients(const std::string& fileName = "misparams.root", bool findMin = false) -{ - constexpr double factor{10}; - o2::its3::align::Deformations def; - def.init(fileName); - - if (findMin) { - gMin = ROOT::Math::Factory::CreateMinimizer("Minuit2", "Migrad"); - if (gMin == nullptr) { - Error("", "Cannot create minimizer !"); - return; - } - gMin->SetMaxFunctionCalls(1000000); // for Minuit/Minuit2 - gMin->SetTolerance(0.00001); - gMin->SetPrintLevel(1); - } - - if constexpr (1) { - constexpr int nPoints{1000}; - const std::array zFix{-12., -8.67, -4.33, 0., 4.33, 8.67, 12.}; - const std::array phiFix{1. / 4. * TMath::Pi(), 0.5 * TMath::Pi(), 3. / 4. * TMath::Pi(), TMath::Pi(), 5. / 4. * TMath::Pi(), 6. / 4. * TMath::Pi(), 7. / 4. * TMath::Pi()}; - const std::array phiFixName{"#frac{1}{4}", "#frac{1}{2}", "#frac{3}{4}", "1", "#frac{5}{4}", "#frac{6}{4}", "#frac{7}{4}"}; - const std::array phiFixTop{true, true, true, false, false, false, false}; - const std::array, 2> sensorN{{{0, 2, 4}, {1, 3, 5}}}; - constexpr double z1{-o2::its3::constants::segment::lengthSensitive / 2.0}, z2{o2::its3::constants::segment::lengthSensitive / 2.0}, zTot{z2 - z1}, zStep{zTot / (nPoints - 1)}; - auto canv = new TCanvas(); - canv->Divide(7, 2); - for (int i{0}; i < 7; ++i) { - std::array xi, yi, xd, yd; - for (int s{0}; s < 6; ++s) { - const bool isTop = s % 2 == 0; - const double radius = o2::its3::constants::radii[s / 2]; - const double nzFix = (zFix[i] - z1) * 2.0 / zTot - 1.0; - const double phi1 = o2::math_utils::to02Pi(((isTop) ? 0.f : 1.f) * TMath::Pi() + std::asin(o2::its3::constants::equatorialGap / 2.f / radius)); - const double phi2 = o2::math_utils::to02Pi(((isTop) ? 1.f : 2.f) * TMath::Pi() - std::asin(o2::its3::constants::equatorialGap / 2.f / radius)); - const double phiTot{phi2 - phi1}, phiStep{phiTot / (nPoints - 1)}; - const double nphiFix = ((phiFix[i]) - phi1) * 2.0 / phiTot - 1.0; - - for (int j{0}; j < nPoints; ++j) { - const int idx = s * nPoints + j; - const double phi = phi1 + j * phiStep; - const double nphi = (phi - phi1) * 2.0 / phiTot - 1.0; - - xi[idx] = radius * std::cos(phi); - yi[idx] = radius * std::sin(phi); - const auto [dxXY, dyXY, _] = def.getDeformation(s, nphi, nzFix); - xd[idx] = xi[idx] + dxXY * factor; - yd[idx] = yi[idx] + dyXY * factor; - } - } - canv->cd(i + 1); - auto gixy = new TGraph(nPoints * 6, xi.data(), yi.data()); - gixy->SetNameTitle(Form("g_i_xy_%d", i), Form("Ideal xy at z=%.2f; x (cm); y (cm)", zFix[i])); - gixy->SetMarkerColor(kBlue); - gixy->Draw("AP"); - auto gdxy = new TGraph(nPoints * 6, xd.data(), yd.data()); - gdxy->SetNameTitle(Form("g_d_xy_%d", i), Form("Deformed (x%.0f) xy at z=%.2f; x (cm); y (cm)", factor, zFix[i])); - gdxy->SetMarkerColor(kRed); - gdxy->Draw("P;same"); - - if (i == 3) { - continue; - } - - std::array zi, ri, zd, rd; - const bool isTop = phiFixTop[i]; - for (const int s : ((isTop) ? sensorN[0] : sensorN[1])) { - const double radius = o2::its3::constants::radii[s / 2]; - const double phi1 = o2::math_utils::to02Pi(((isTop) ? 0.f : 1.f) * TMath::Pi() + std::asin(o2::its3::constants::equatorialGap / 2.f / radius)); - const double phi2 = o2::math_utils::to02Pi(((isTop) ? 1.f : 2.f) * TMath::Pi() - std::asin(o2::its3::constants::equatorialGap / 2.f / radius)); - const double phiTot{phi2 - phi1}, phiStep{phiTot / (nPoints - 1)}; - const double nphiFix = ((phiFix[i]) - phi1) * 2.0 / phiTot - 1.0; - for (int j{0}; j < nPoints; ++j) { - const int idx = (s / 2) * nPoints + j; - const double z = z1 + j * zStep; - const double nz = (z - z1) * 2.0 / zTot - 1.0; - const double xi = radius * std::cos(phiFix[i]), yi = radius * std::sin(phiFix[i]); - ri[idx] = radius; - zi[idx] = z; - const auto [dxZR, dyZR, dzZR] = def.getDeformation(s, nphiFix, nz); - zd[idx] = z + dzZR * factor; - const double xxd = xi + dxZR * factor, yyd = yi + dyZR * factor; - rd[idx] = std::sqrt(xxd * xxd + yyd * yyd); - } - } - canv->cd(i + 8); - auto gizr = new TGraph(nPoints * 3, zi.data(), ri.data()); - gizr->SetNameTitle(Form("g_i_zr_%d", i), Form("Ideal zr at #varphi=%s #Pi; z (cm); r (cm)", phiFixName[i])); - gizr->SetMarkerColor(kBlue); - gizr->Draw("AP"); - auto gdzr = new TGraph(nPoints * 3, zd.data(), rd.data()); - gdzr->SetNameTitle(Form("g_d_zr_%d", i), Form("Deformed (x%0.f) zr at #varphi=%s #Pi; z (cm); r (cm)", factor, phiFixName[i])); - gdzr->SetMarkerColor(kRed); - gdzr->Draw("P;same"); - } - canv->Draw(); - canv->SaveAs("its3_deformation.pdf"); - } - - if constexpr (1) { - const std::array axisName{"x", "y", "z"}; - constexpr int nPoints{100}; - constexpr int nPoints2{nPoints * nPoints}; - constexpr double minX{-1.0}, maxX{1.0}, - stepX{(maxX - minX) / static_cast(nPoints)}; - - for (unsigned int iSensor{0}; iSensor < 6; ++iSensor) { - const auto nOrders = def.getOrders(iSensor); - for (unsigned int iAxis{0}; iAxis < 3; ++iAxis) { - std::array x, y, z; - auto canv = new TCanvas(Form("c_sensor%d_d%s", iSensor, axisName[iAxis]), Form("Legendre Coefficients Sensor %d - Axis %s", iSensor, axisName[iAxis])); - canv->Divide(nOrders[iAxis] + 1, nOrders[iAxis] + 1); - - { // Draw total polynominal - for (int iPoint{0}; iPoint < nPoints; ++iPoint) { - double xcur = minX + iPoint * stepX; - for (int jPoint{0}; jPoint < nPoints; ++jPoint) { - double ycur = minX + jPoint * stepX; - int i = iPoint * nPoints + jPoint; - x[i] = xcur; - y[i] = ycur; - z[i] = def.getDeformation(iSensor, iAxis, xcur, ycur); - } - } - canv->cd(nOrders[iAxis] + 1); - auto g = new TGraph2D(nPoints2, x.data(), y.data(), z.data()); - g->SetTitle(Form("Legendre Polynominal %d-deg Sensor %d #Delta%s;u;v;w", nOrders[iAxis], iSensor, axisName[iAxis])); - g->SetName(Form("g_%d_%s", iSensor, axisName[iAxis])); - g->Draw("surf1"); - g->GetXaxis()->SetRangeUser(minX, maxX); - g->GetYaxis()->SetRangeUser(minX, maxX); - gPad->Update(); - } - - { // Draw decomposition of contributions to polynominal - const auto& leg = def.getLegendre(iSensor, iAxis); - const auto coeff = leg.getCoefficients(); - for (unsigned int iOrder{0}; iOrder <= nOrders[iAxis]; ++iOrder) { - for (unsigned int jOrder{0}; jOrder <= iOrder; ++jOrder) { - for (int iPoint{0}; iPoint < nPoints; ++iPoint) { - double xcur = minX + iPoint * stepX; - for (int jPoint{0}; jPoint < nPoints; ++jPoint) { - double ycur = minX + jPoint * stepX; - int i = iPoint * nPoints + jPoint; - x[i] = xcur; - y[i] = ycur; - z[i] = leg(iOrder, jOrder, xcur, ycur); - } - } - canv->cd(1 + iOrder * (nOrders[iAxis] + 1) + jOrder); - auto g = new TGraph2D(nPoints2, x.data(), y.data(), z.data()); - g->SetTitle(Form("c_{%d,%d}=%.4f;u;v;w", iOrder, jOrder, coeff(iOrder, jOrder))); - g->SetName(Form("g_%d_%d_%d_%d", iSensor, iAxis, iOrder, jOrder)); - if (iOrder == 0 && jOrder == 0) { // Fix display of constant value - g->Draw("P0"); - } else { - g->Draw("surf1"); - } - g->GetXaxis()->SetRangeUser(minX, maxX); - g->GetYaxis()->SetRangeUser(minX, maxX); - gPad->Update(); - } - } - } - - canv->Draw(); - canv->SaveAs(Form("its3_sensor%d_%s.pdf", iSensor, axisName[iAxis])); - } - } - } - - if constexpr (1) { - constexpr int nPoints{50}; - constexpr int nPoints2{nPoints * nPoints}; - constexpr double radL = o2::its3::constants::radii[2] + 0.3, zL = o2::its3::constants::segment::lengthSensitive / 2.0 + 2.0; - - // Plot the whole geometry - std::array gIdeal; - std::array gDeformed; - constexpr double z1{-o2::its3::constants::segment::lengthSensitive / 2.0}, z2{o2::its3::constants::segment::lengthSensitive / 2.0}, zTot{z2 - z1}, zStep{zTot / (nPoints - 1)}; - auto canv = new TCanvas(); - canv->Divide(2, 1); - for (unsigned int iSensor{0}; iSensor < 6; ++iSensor) { - std::array xi, yi, zi, xd, yd, zd; - const double radius = o2::its3::constants::radii[iSensor / 2]; - const bool isTop = iSensor % 2 == 0; - const double phi1 = o2::math_utils::to02Pi(((isTop) ? 0.f : 1.f) * TMath::Pi() + std::asin(o2::its3::constants::equatorialGap / 2.f / radius)); - const double phi2 = o2::math_utils::to02Pi(((isTop) ? 1.f : 2.f) * TMath::Pi() - std::asin(o2::its3::constants::equatorialGap / 2.f / radius)); - const double phiTot{phi2 - phi1}, phiStep{phiTot / (nPoints - 1)}; - for (int iZ{0}; iZ < nPoints; ++iZ) { - double z = z1 + iZ * zStep; - for (int iPhi{0}; iPhi < nPoints; ++iPhi) { - int i = iZ * nPoints + iPhi; - double phi = phi1 + iPhi * phiStep; - - xi[i] = radius * std::cos(phi); - yi[i] = radius * std::sin(phi); - zi[i] = z; - - const double u = ((phi - phi1) * 2.f) / phiTot - 1.f; - const double v = ((z - z1)) / zTot - 1.f; - const auto [dx, dy, dz] = def.getDeformation(iSensor, u, v); - xd[i] = xi[i] + dx * factor; - yd[i] = yi[i] + dy * factor; - zd[i] = zi[i] + dz * factor; - } - } - - canv->cd(1); - auto ideal = new TGraph2D(Form("g_ideal_%d", iSensor), "Ideal Geometry", nPoints2, xi.data(), zi.data(), yi.data()); - ideal->SetMarkerStyle(kFullCircle); - ideal->SetMarkerSize(1); - ideal->SetMarkerColor(kBlue); - ideal->SetLineColor(kBlue); - if (iSensor == 0) { - ideal->Draw("p0"); - } else { - ideal->Draw("p0;same"); - if (iSensor == 5) { - gPad->Update(); - ideal->GetXaxis()->SetTitle("X"); - ideal->GetYaxis()->SetTitle("Z"); - ideal->GetZaxis()->SetTitle("Y"); - ideal->GetXaxis()->SetRangeUser(-radL, radL); - ideal->GetZaxis()->SetRangeUser(-radL, radL); - ideal->GetYaxis()->SetRangeUser(-zL, zL); - } - } - - canv->cd(2); - auto deformed = new TGraph2D(Form("g_def_%d", iSensor), "Deformed Geometry", nPoints2, xd.data(), zd.data(), yd.data()); - deformed->SetMarkerStyle(kFullCircle); - deformed->SetMarkerSize(1); - deformed->SetMarkerColor(kRed); - deformed->SetLineColor(kRed); - if (iSensor == 0) { - deformed->Draw("p0"); - } else { - deformed->Draw("p0;same"); - if (iSensor == 5) { - gPad->Update(); - deformed->GetXaxis()->SetTitle("X"); - deformed->GetYaxis()->SetTitle("Z"); - deformed->GetZaxis()->SetTitle("Y"); - deformed->GetXaxis()->SetRangeUser(-radL, radL); - deformed->GetZaxis()->SetRangeUser(-radL, radL); - deformed->GetYaxis()->SetRangeUser(-zL, zL); - } - } - } - - // Optionally find a deformation - /* if (findMin2D) { */ - /* std::vector cccc(nOrder + 2, 0.0); */ - /* cccc[0] = nOrder; */ - /* for (int i{0}; i <= nOrder; ++i) { */ - /* for (int j{0}; j <= i; ++j) { */ - /* int k = i * (i + 1) / 2 + j; */ - /* cccc[1 + k] = coeff(i, j); */ - /* } */ - /* } */ - /* auto ig = legendre_poly2D_integral(cccc.data()); */ - - /* gMin->Clear(); */ - /* ROOT::Math::Functor fmin(&legendre_poly2D_integral, */ - /* 2 + nOrder * (nOrder + 1) / 2 + nOrder); */ - /* Info("", "ig=%f parameters=%d", ig, */ - /* 2 + nOrder * (nOrder + 1) / 2 + nOrder); */ - /* gMin->SetFunction(fmin); */ - /* constexpr double minStep{0.001}; */ - /* gMin->SetFixedVariable(0, "nOrder", nOrder); */ - /* gMin->SetFixedVariable(1, "c_00", coeff(0, 0)); */ - /* for (int iOrder{1}; iOrder <= nOrder; ++iOrder) { */ - /* for (int jOrder{0}; jOrder <= iOrder; ++jOrder) { */ - /* int k = iOrder * (iOrder + 1) / 2 + jOrder + 1; */ - /* Info("", "Setting parameter %d", k); */ - /* if (getRandom() < 0.0) { */ - /* gMin->SetFixedVariable(k, Form("c_%d_%d", iOrder, jOrder), */ - /* coeff(iOrder, jOrder)); */ - /* } else { */ - /* gMin->SetVariable(k, Form("c_%d_%d", iOrder, jOrder), */ - /* coeff(iOrder, jOrder), minStep); */ - /* } */ - /* } */ - /* } */ - /* gMin->Minimize(); */ - /* return; */ - /* auto stat = gMin->Status(); */ - /* auto min = gMin->MinValue(); */ - /* if ((stat == 0 || stat == 1) && min < 0.01) { */ - /* Info("", "Minimizer converged with %f; using new values!", min); */ - /* const double *cmins = gMin->X(); */ - /* for (int iOrder{1}; iOrder <= nOrder; ++iOrder) { */ - /* for (int jOrder{0}; jOrder <= iOrder; ++jOrder) { */ - /* int k = iOrder * (iOrder + 1) / 2 + jOrder; */ - /* coeff(iOrder, jOrder) = cmins[k + 1]; */ - /* } */ - /* } */ - /* } */ - /* } */ - } -} diff --git a/Detectors/Upgrades/ITS3/macros/align/TestLegendrePol.C b/Detectors/Upgrades/ITS3/macros/align/TestLegendrePol.C deleted file mode 100644 index 720ab80264838..0000000000000 --- a/Detectors/Upgrades/ITS3/macros/align/TestLegendrePol.C +++ /dev/null @@ -1,257 +0,0 @@ -// Copyright 2020-2022 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#if !defined(__CLING__) || defined(__ROOTCLING__) -#include "Math/Factory.h" -#include "Math/Minimizer.h" -#include "TAxis.h" -#include "TCanvas.h" -#include "TGraph.h" -#include "TGraph2D.h" -#include "TLegend.h" -#include "TMarker.h" -#include "TMultiGraph.h" -#include "TRandom.h" - -#include "MathUtils/LegendrePols.h" -#endif - -static ROOT::Math::Minimizer* gMin; - -void TestLegendrePol(bool findMin1D = false, bool findMin2D = false) -{ - constexpr int nMaxOrder{2}; - constexpr int nPoints{100}; - constexpr int nPoints2{nPoints * nPoints}; - constexpr double minX{-1.0}, maxX{1.0}, - stepX{(maxX - minX) / static_cast(nPoints)}; - - gRandom->SetSeed(0); - auto getRandom = []() { - constexpr double scale{80.e-4}; - return scale * gRandom->Uniform(-1.0, 1.0); - }; - - if (findMin1D || findMin2D) { - gMin = ROOT::Math::Factory::CreateMinimizer("Minuit2", "Migrad"); - if (gMin == nullptr) { - Error("", "Cannot create minimizer !"); - return; - } - gMin->SetMaxFunctionCalls(1000000); // for Minuit/Minuit2 - gMin->SetTolerance(0.00001); - gMin->SetPrintLevel(1); - } - - { // 1D - Info("", "---------------- 1D -------------"); - std::array x, y; - for (int nOrder{0}; nOrder <= nMaxOrder; ++nOrder) { - std::vector coeff(nOrder + 1, 0.0); - std::generate(std::begin(coeff), std::end(coeff), getRandom); - - o2::math_utils::Legendre1DPolynominal leg1D(coeff); - - // Optionally find a deformation - if (findMin1D) { - std::vector cccc(nOrder + 2, 0.0); - cccc[0] = nOrder; - for (int iOrder{0}; iOrder <= nOrder; ++iOrder) { - cccc[1 + iOrder] = coeff[iOrder]; - } - - gMin->Clear(); - /* gMin->SetFunction(&leg1D.DoIntegralPar); */ - constexpr double minStep{0.001}; - gMin->SetFixedVariable(0, "c_0", coeff[0]); - for (int iOrder{1}; iOrder <= nOrder; ++iOrder) { - gMin->SetVariable(iOrder, Form("c_%d", iOrder), coeff[iOrder], - minStep); - } - gMin->Minimize(); - auto stat = gMin->Status(); - auto min = gMin->MinValue(); - if ((stat == 0 || stat == 1) && min < 0.01) { - Info("", "Minimizer converged with %f; using new values!", min); - const double* cmins = gMin->X(); - for (int i{0}; i <= nOrder; ++i) { - Info("", "New values are c_%d=%.4f -> %.4f", i, coeff[i], - cmins[1 + i]); - coeff[i] = cmins[1 + i]; - } - } - } - - auto c1d = new TCanvas(Form("c1D_%d", nOrder), - Form("Legendre 1D Order %d", nOrder)); - c1d->Divide(2, 1); - - { // Draw total polynominal - for (int iPoint{0}; iPoint < nPoints; ++iPoint) { - double xcur = minX + iPoint * stepX; - x[iPoint] = xcur; - y[iPoint] = leg1D(xcur); - } - c1d->cd(2); - auto g = new TGraph(nPoints, x.data(), y.data()); - g->SetName(Form("g1d_%d", nOrder)); - g->SetTitle(Form("Legendre Polynominal %d-deg;u;w", nOrder)); - g->Draw(); - } - - { // Draw single contributions - auto mg = new TMultiGraph(); - auto leg = new TLegend(); - for (int iOrder{0}; iOrder <= nOrder; ++iOrder) { - for (int iPoint{0}; iPoint < nPoints; ++iPoint) { - double xcur = minX + iPoint * stepX; - x[iPoint] = xcur; - y[iPoint] = leg1D(iOrder, xcur); - } - auto g = new TGraph(nPoints, x.data(), y.data()); - g->SetName(Form("g1d_%d_%d", nOrder, iOrder)); - g->SetTitle(Form("c_{%d}=%.4f;u;w", iOrder, coeff[iOrder])); - mg->Add(g, "PL"); - leg->AddEntry(g, Form("c_{%d}=%.4f", iOrder, coeff[iOrder]), "lp"); - } - c1d->cd(1); - mg->SetTitle("Contribution decomposition;u;w"); - mg->Draw("A pmc plc"); - leg->Draw(); - gPad->Update(); - } - } - } - - { // 2D - Info("", "---------------- 2D -------------"); - std::array x, y, z; - for (int nOrder{0}; nOrder <= nMaxOrder; ++nOrder) { - auto c2d = new TCanvas(Form("c2D_%d", nOrder), - Form("Legendre 2D Order %d", nOrder)); - c2d->Divide(nOrder + 1, nOrder + 1); - - TMatrixD coeff(nOrder + 1, nOrder + 1); - // Random initialization - for (int i{0}; i <= nOrder; ++i) { - for (int j{0}; j <= i; ++j) { - coeff(i, j) = getRandom(); - } - } - - o2::math_utils::Legendre2DPolynominal leg2D(coeff); - - // Optionally find a deformation - /* if (findMin2D) { */ - /* std::vector cccc(nOrder + 2, 0.0); */ - /* cccc[0] = nOrder; */ - /* for (int i{0}; i <= nOrder; ++i) { */ - /* for (int j{0}; j <= i; ++j) { */ - /* int k = i * (i + 1) / 2 + j; */ - /* cccc[1 + k] = coeff(i, j); */ - /* } */ - /* } */ - /* auto ig = legendre_poly2D_integral(cccc.data()); */ - - /* gMin->Clear(); */ - /* ROOT::Math::Functor fmin(&legendre_poly2D_integral, */ - /* 2 + nOrder * (nOrder + 1) / 2 + nOrder); */ - /* Info("", "ig=%f parameters=%d", ig, */ - /* 2 + nOrder * (nOrder + 1) / 2 + nOrder); */ - /* gMin->SetFunction(fmin); */ - /* constexpr double minStep{0.001}; */ - /* gMin->SetFixedVariable(0, "nOrder", nOrder); */ - /* gMin->SetFixedVariable(1, "c_00", coeff(0, 0)); */ - /* for (int iOrder{1}; iOrder <= nOrder; ++iOrder) { */ - /* for (int jOrder{0}; jOrder <= iOrder; ++jOrder) { */ - /* int k = iOrder * (iOrder + 1) / 2 + jOrder + 1; */ - /* Info("", "Setting parameter %d", k); */ - /* if (getRandom() < 0.0) { */ - /* gMin->SetFixedVariable(k, Form("c_%d_%d", iOrder, jOrder), */ - /* coeff(iOrder, jOrder)); */ - /* } else { */ - /* gMin->SetVariable(k, Form("c_%d_%d", iOrder, jOrder), */ - /* coeff(iOrder, jOrder), minStep); */ - /* } */ - /* } */ - /* } */ - /* gMin->Minimize(); */ - /* return; */ - /* auto stat = gMin->Status(); */ - /* auto min = gMin->MinValue(); */ - /* if ((stat == 0 || stat == 1) && min < 0.01) { */ - /* Info("", "Minimizer converged with %f; using new values!", min); */ - /* const double *cmins = gMin->X(); */ - /* for (int iOrder{1}; iOrder <= nOrder; ++iOrder) { */ - /* for (int jOrder{0}; jOrder <= iOrder; ++jOrder) { */ - /* int k = iOrder * (iOrder + 1) / 2 + jOrder; */ - /* coeff(iOrder, jOrder) = cmins[k + 1]; */ - /* } */ - /* } */ - /* } */ - /* } */ - - leg2D.printCoefficients(); - - { // Draw total polynominal - for (int iPoint{0}; iPoint < nPoints; ++iPoint) { - double xcur = minX + iPoint * stepX; - for (int jPoint{0}; jPoint < nPoints; ++jPoint) { - double ycur = minX + jPoint * stepX; - int i = iPoint * nPoints + jPoint; - x[i] = xcur; - y[i] = ycur; - z[i] = leg2D(xcur, ycur); - } - } - c2d->cd(nOrder + 1); - auto g = new TGraph2D(nPoints2, x.data(), y.data(), z.data()); - g->SetTitle(Form("Legendre Polynominal %d-deg;u;v;w", nOrder)); - g->SetName(Form("g2d_%d", nOrder)); - g->Draw("surf1"); - g->GetXaxis()->SetRangeUser(minX, maxX); - g->GetYaxis()->SetRangeUser(minX, maxX); - gPad->Update(); - } - - { // Draw decomposition of contributions to polynominal - for (int iOrder{0}; iOrder <= nOrder; ++iOrder) { - for (int jOrder{0}; jOrder <= iOrder; ++jOrder) { - for (int iPoint{0}; iPoint < nPoints; ++iPoint) { - double xcur = minX + iPoint * stepX; - for (int jPoint{0}; jPoint < nPoints; ++jPoint) { - double ycur = minX + jPoint * stepX; - int i = iPoint * nPoints + jPoint; - x[i] = xcur; - y[i] = ycur; - z[i] = leg2D(iOrder, jOrder, xcur, ycur); - } - } - c2d->cd(1 + iOrder * (nOrder + 1) + jOrder); - auto g = new TGraph2D(nPoints2, x.data(), y.data(), z.data()); - g->SetTitle(Form("c_{%d,%d}=%.4f;u;v;w", iOrder, jOrder, - coeff(iOrder, jOrder))); - g->SetName(Form("g2d_%d_%d_%d", nOrder, iOrder, jOrder)); - if (iOrder == 0 && jOrder == 0) { // Fix display of constant value - g->Draw("P0"); - } else { - g->Draw("surf1"); - } - g->GetXaxis()->SetRangeUser(minX, maxX); - g->GetYaxis()->SetRangeUser(minX, maxX); - gPad->Update(); - } - } - } - c2d->Draw(); - } - } -} diff --git a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/IOUtils.h b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/IOUtils.h index fa15e73118524..d82cd26e63c56 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/IOUtils.h +++ b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/IOUtils.h @@ -14,12 +14,19 @@ #include "DataFormatsITSMFT/CompCluster.h" #include "DataFormatsITSMFT/ROFRecord.h" #include "ITS3Reconstruction/TopologyDictionary.h" -#include "ITStracking/TimeFrame.h" #include "ITStracking/IOUtils.h" #include "ITS3Base/SegmentationMosaix.h" #include "ITS3Base/SpecsV2.h" -namespace o2::its3::ioutils +namespace o2 +{ +namespace its +{ +template +class TimeFrame; +} + +namespace its3::ioutils { constexpr float DefClusErrorRow = o2::its3::SegmentationMosaix::PitchRow * 0.5; constexpr float DefClusErrorCol = o2::its3::SegmentationMosaix::PitchCol * 0.5; @@ -27,7 +34,7 @@ constexpr float DefClusError2Row = DefClusErrorRow * DefClusErrorRow; constexpr float DefClusError2Col = DefClusErrorCol * DefClusErrorCol; template -o2::math_utils::Point3D extractClusterData(const itsmft::CompClusterExt& c, iterator& iter, const its3::TopologyDictionary* dict, T& sig2y, T& sig2z) +o2::math_utils::Point3D extractClusterData(const itsmft::CompClusterExt& c, iterator& iter, const o2::its3::TopologyDictionary* dict, T& sig2y, T& sig2z) { auto pattID = c.getPatternID(); auto ib = constants::detID::isDetITS3(c.getSensorID()); @@ -50,7 +57,7 @@ o2::math_utils::Point3D extractClusterData(const itsmft::CompClusterExt& c, i } template -o2::math_utils::Point3D extractClusterData(const itsmft::CompClusterExt& c, iterator& iter, const its3::TopologyDictionary* dict, T& sig2y, T& sig2z, uint8_t& cls) +o2::math_utils::Point3D extractClusterData(const itsmft::CompClusterExt& c, iterator& iter, const o2::its3::TopologyDictionary* dict, T& sig2y, T& sig2z, uint8_t& cls) { auto pattID = c.getPatternID(); auto ib = constants::detID::isDetITS3(c.getSensorID()); @@ -69,13 +76,13 @@ o2::math_utils::Point3D extractClusterData(const itsmft::CompClusterExt& c, i void convertCompactClusters(gsl::span clusters, gsl::span::iterator& pattIt, std::vector>& output, - const its3::TopologyDictionary* dict); + const o2::its3::TopologyDictionary* dict); int loadROFrameDataITS3(its::TimeFrame<7>* tf, gsl::span rofs, gsl::span clusters, gsl::span::iterator& pattIt, - const its3::TopologyDictionary* dict, + const o2::its3::TopologyDictionary* dict, const dataformats::MCTruthContainer* mcLabels = nullptr); - -} // namespace o2::its3::ioutils +} // namespace its3::ioutils +} // namespace o2 diff --git a/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx b/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx index d7ba4d48dbce4..0fea07743b3df 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx +++ b/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.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. // @@ -10,18 +10,13 @@ // or submit itself to any jurisdiction. #include "ITS3Reconstruction/IOUtils.h" -#include "ITStracking/IOUtils.h" #include "ITStracking/TimeFrame.h" #include "ITStracking/BoundedAllocator.h" #include "DataFormatsITSMFT/CompCluster.h" #include "DataFormatsITSMFT/ROFRecord.h" #include "ITS3Reconstruction/TopologyDictionary.h" #include "ITSBase/GeometryTGeo.h" -#include "ITS3Base/SpecsV2.h" #include "ITStracking/TrackingConfigParam.h" -#include "Framework/Logger.h" - -#include namespace o2::its3::ioutils { @@ -45,16 +40,19 @@ void convertCompactClusters(gsl::span clusters, } for (auto& c : clusters) { - float sigmaY2, sigmaZ2, sigmaYZ = 0; + float sigmaY2 = NAN, sigmaZ2 = NAN; auto locXYZ = extractClusterData(c, pattIt, dict, sigmaY2, sigmaZ2); const auto detID = c.getSensorID(); + // NOTE: this is not consistent with the TRK definition below! + // There we put the alpha for everything cluster to its phi + // here we extract it from the middle of the tile auto& cl3d = output.emplace_back(detID, geom->getMatrixT2L(detID) ^ locXYZ); // local --> tracking if (applyMisalignment) { - auto lrID = geom->getLayer(detID); + const auto lrID = geom->getLayer(detID); sigmaY2 += conf.sysErrY2[lrID]; sigmaZ2 += conf.sysErrZ2[lrID]; } - cl3d.setErrors(sigmaY2, sigmaZ2, sigmaYZ); + cl3d.setErrors(sigmaY2, sigmaZ2, 0.f); } } @@ -76,26 +74,36 @@ int loadROFrameDataITS3(its::TimeFrame<7>* tf, for (size_t iRof{0}; iRof < rofs.size(); ++iRof) { const auto& rof = rofs[iRof]; for (int clusterId{rof.getFirstEntry()}; clusterId < rof.getFirstEntry() + rof.getNEntries(); ++clusterId) { - auto& c = clusters[clusterId]; - auto sensorID = c.getSensorID(); - auto layer = geom->getLayer(sensorID); + const auto& c = clusters[clusterId]; + const auto sensorID = c.getSensorID(); + const auto layer = geom->getLayer(sensorID); float sigmaY2{0}, sigmaZ2{0}, sigmaYZ{0}; uint8_t clusterSize{0}; - auto locXYZ = extractClusterData(c, pattIt, dict, sigmaY2, sigmaZ2, clusterSize); + const auto locXYZ = extractClusterData(c, pattIt, dict, sigmaY2, sigmaZ2, clusterSize); clusterSizeVec.push_back(clusterSize); // Transformation to the local --> global - auto gloXYZ = geom->getMatrixL2G(sensorID) * locXYZ; + const auto gloXYZ = geom->getMatrixL2G(sensorID) * locXYZ; // Inverse transformation to the local --> tracking - o2::math_utils::Point3D trkXYZ = geom->getMatrixT2L(sensorID) ^ locXYZ; + const o2::math_utils::Point3D trkXYZ = geom->getMatrixT2L(sensorID) ^ locXYZ; // Tracking alpha angle + // We want that each cluster rotates its tracking frame to the clusters phi + // that way the track linearization around the measurement is less biases to the arc + // this means automatically that the measurement on the arc is at 0 for the curved layers float alpha = geom->getSensorRefAlpha(sensorID); - - tf->addTrackingFrameInfoToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), trkXYZ.x(), alpha, - std::array{trkXYZ.y(), trkXYZ.z()}, + float x = trkXYZ.x(), y = trkXYZ.y(); + if (constants::detID::isDetITS3(sensorID)) { + y = 0.f; + x = std::hypot(gloXYZ.x(), gloXYZ.y()); + alpha = std::atan2(gloXYZ.y(), gloXYZ.x()); + } + math_utils::detail::bringToPMPi(alpha); // alpha is defined on -Pi,Pi + + tf->addTrackingFrameInfoToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), x, alpha, + std::array{y, trkXYZ.z()}, std::array{sigmaY2, sigmaYZ, sigmaZ2}); /// Rotate to the global frame @@ -103,7 +111,7 @@ int loadROFrameDataITS3(its::TimeFrame<7>* tf, tf->addClusterExternalIndexToLayer(layer, clusterId); } for (unsigned int iL{0}; iL < tf->getUnsortedClusters().size(); ++iL) { - tf->mROFramesClusters[iL][iRof + 1] = tf->getUnsortedClusters()[iL].size(); + tf->mROFramesClusters[iL][iRof + 1] = (int)tf->getUnsortedClusters()[iL].size(); } } diff --git a/Detectors/Upgrades/ITS3/reconstruction/src/TopologyDictionary.cxx b/Detectors/Upgrades/ITS3/reconstruction/src/TopologyDictionary.cxx index 0d1deb77b7c2e..c7bb4dbe9b6ee 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/src/TopologyDictionary.cxx +++ b/Detectors/Upgrades/ITS3/reconstruction/src/TopologyDictionary.cxx @@ -255,5 +255,7 @@ TopologyDictionary* TopologyDictionary::loadFrom(const std::string& fname, const // Explicitly instaniate templates template math_utils::Point3D TopologyDictionary::getClusterCoordinates(const itsmft::CompClusterExt& cl) const; template math_utils::Point3D TopologyDictionary::getClusterCoordinates(const itsmft::CompClusterExt& cl, const itsmft::ClusterPattern& patt, bool isGroup); +template math_utils::Point3D TopologyDictionary::getClusterCoordinates(const itsmft::CompClusterExt& cl) const; +template math_utils::Point3D TopologyDictionary::getClusterCoordinates(const itsmft::CompClusterExt& cl, const itsmft::ClusterPattern& patt, bool isGroup); } // namespace o2::its3 diff --git a/Detectors/Upgrades/ITS3/study/CMakeLists.txt b/Detectors/Upgrades/ITS3/study/CMakeLists.txt index 4bb1cbca7dcb0..314b21c529ebf 100644 --- a/Detectors/Upgrades/ITS3/study/CMakeLists.txt +++ b/Detectors/Upgrades/ITS3/study/CMakeLists.txt @@ -17,12 +17,16 @@ o2_add_library(ITS3TrackingStudy src/TrackingStudy.cxx src/ParticleInfoExt.cxx PUBLIC_LINK_LIBRARIES O2::ITS3Workflow + O2::ITS3Align O2::GlobalTracking O2::GlobalTrackingWorkflowReaders O2::GlobalTrackingWorkflowHelpers O2::DataFormatsGlobalTracking O2::DetectorsVertexing - O2::SimulationDataFormat) + O2::SimulationDataFormat + Eigen3::Eigen + nlohmann_json::nlohmann_json +) o2_target_root_dictionary(ITS3TrackingStudy HEADERS include/ITS3TrackingStudy/ITS3TrackingStudyParam.h diff --git a/Detectors/Upgrades/ITS3/study/include/ITS3TrackingStudy/ITS3TrackingStudyParam.h b/Detectors/Upgrades/ITS3/study/include/ITS3TrackingStudy/ITS3TrackingStudyParam.h index 2e718622daa90..1150dc19a1162 100644 --- a/Detectors/Upgrades/ITS3/study/include/ITS3TrackingStudy/ITS3TrackingStudyParam.h +++ b/Detectors/Upgrades/ITS3/study/include/ITS3TrackingStudy/ITS3TrackingStudyParam.h @@ -22,7 +22,7 @@ namespace o2::its3::study struct ITS3TrackingStudyParam : o2::conf::ConfigurableParamHelper { /// general track selection float maxChi2{36}; - float maxEta{1.0}; + float maxEta{1.5}; float minPt{0.1}; float maxPt{1e2}; /// PV selection @@ -30,6 +30,8 @@ struct ITS3TrackingStudyParam : o2::conf::ConfigurableParamHelper::MatCorrType CorrType = o2::base::PropagatorImpl::MatCorrType::USEMatCorrLUT; /// studies - bool doDCA = true; - bool doDCARefit = true; - bool doPull = true; + bool doDCA = false; + bool doDCARefit = false; + bool doPull = false; + bool doResid = false; bool doMC = false; + bool doMisalignment = false; + + // misalignment + std::string misAlgJson; // json file containing to be applied misalignment + float misAlgExtCY[6] = {0.f}; // extra uncertainty on y + float misAlgExtCZ[6] = {0.f}; // extra uncertainty on z + O2ParamDef(ITS3TrackingStudyParam, "ITS3TrackingStudyParam"); }; diff --git a/Detectors/Upgrades/ITS3/study/include/ITS3TrackingStudy/TrackingStudy.h b/Detectors/Upgrades/ITS3/study/include/ITS3TrackingStudy/TrackingStudy.h index 065629058fd32..6d55dfc80aa8a 100644 --- a/Detectors/Upgrades/ITS3/study/include/ITS3TrackingStudy/TrackingStudy.h +++ b/Detectors/Upgrades/ITS3/study/include/ITS3TrackingStudy/TrackingStudy.h @@ -18,7 +18,7 @@ namespace o2::its3::study { -o2::framework::DataProcessorSpec getTrackingStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool useMC); +o2::framework::DataProcessorSpec getTrackingStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool useMC, bool withPV); } // namespace o2::its3::study diff --git a/Detectors/Upgrades/ITS3/study/macros/CMakeLists.txt b/Detectors/Upgrades/ITS3/study/macros/CMakeLists.txt index aaf763888c5e0..a772913c51f15 100644 --- a/Detectors/Upgrades/ITS3/study/macros/CMakeLists.txt +++ b/Detectors/Upgrades/ITS3/study/macros/CMakeLists.txt @@ -16,3 +16,11 @@ o2_add_test_root_macro(PlotDCA.C o2_add_test_root_macro(PlotPulls.C PUBLIC_LINK_LIBRARIES O2::ITS3TrackingStudy LABELS its COMPILE_ONLY) + +o2_add_test_root_macro(PlotResiduals.C + PUBLIC_LINK_LIBRARIES O2::ITS3TrackingStudy + LABELS its COMPILE_ONLY) + +o2_add_test_root_macro(PlotMisalignment.C + PUBLIC_LINK_LIBRARIES O2::ITS3TrackingStudy + LABELS its COMPILE_ONLY) diff --git a/Detectors/Upgrades/ITS3/study/macros/PlotMisalignment.C b/Detectors/Upgrades/ITS3/study/macros/PlotMisalignment.C new file mode 100644 index 0000000000000..5b9372bc3402e --- /dev/null +++ b/Detectors/Upgrades/ITS3/study/macros/PlotMisalignment.C @@ -0,0 +1,228 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "ReconstructionDataFormats/Track.h" +#include +#endif + +constexpr int kNLay = 8; // slots: PV(-1) + layers 0-6 +constexpr int kNVar = 2; // dY, dZ +constexpr int kNPar = 5; // Y, Z, Snp, Tgl, Q2Pt +// constexpr int kNPtBins = 6; // integrated + 5 differential +// const float kPtEdges[kNPtBins] = {0., 0.3, 0.8, 2., 5., 10.}; +// const char* kPtLabels[kNPtBins] = {"", "0.0= kPtEdges[i] && pt < kPtEdges[i + 1]) { + return i + 1; // 1-indexed, 0 = integrated + } + } + return -1; +} + +void processTree(TFile* f, const char* treeName) +{ + auto* tree = f->Get(treeName); + if (!tree) { + return; + } + + // branch variables + float dY, dZ, phi, eta; + int lay; + auto* trk = new o2::track::TrackParCov; + auto* mcTrk = new o2::track::TrackPar; + tree->SetBranchAddress("dY", &dY); + tree->SetBranchAddress("dZ", &dZ); + tree->SetBranchAddress("phi", &phi); + tree->SetBranchAddress("eta", &eta); + tree->SetBranchAddress("lay", &lay); + tree->SetBranchAddress("trk", &trk); + tree->SetBranchAddress("mcTrk", &mcTrk); + + // --- book histograms --- + // [ptBin][lay] for each plot type + // dY/dZ vs phi + TH2F* hVsPhi[kNVar][kNPtBins][kNLay]; + // dY/dZ vs eta + TH2F* hVsEta[kNVar][kNPtBins][kNLay]; + // profile2D phi vs eta + TProfile2D* hProf[kNVar][kNPtBins][kNLay]; + // pulls + TH1F* hPull[kNPar][kNPtBins][kNLay]; + + for (int ipt = 0; ipt < kNPtBins; ipt++) { + for (int ilay = 0; ilay < kNLay; ilay++) { + for (int iv = 0; iv < kNVar; iv++) { + hVsPhi[iv][ipt][ilay] = new TH2F( + Form("%s_%s_phi%s_l%d", treeName, kVarNames[iv], kPtTags[ipt], ilay), + Form("Layer %d %s;#phi (rad);%s", ilay - 1, kPtLabels[ipt], kVarTitles[iv]), + 100, 0, 2 * TMath::Pi(), 100, -100, 100); + hVsEta[iv][ipt][ilay] = new TH2F( + Form("%s_%s_eta%s_l%d", treeName, kVarNames[iv], kPtTags[ipt], ilay), + Form("Layer %d %s;#eta;%s", ilay - 1, kPtLabels[ipt], kVarTitles[iv]), + 100, -1.5, 1.5, 100, -100, 100); + hProf[iv][ipt][ilay] = new TProfile2D( + Form("%s_%s_prof%s_l%d", treeName, kVarNames[iv], kPtTags[ipt], ilay), + Form("Layer %d %s;#phi (rad);#eta;#LT%s#GT", ilay - 1, kPtLabels[ipt], kVarTitles[iv]), + 50, 0, 2 * TMath::Pi(), 50, -1.5, 1.5); + } + for (int ip = 0; ip < kNPar; ip++) { + hPull[ip][ipt][ilay] = new TH1F( + Form("%s_pull_%s%s_l%d", treeName, kParNames[ip], kPtTags[ipt], ilay), + Form("Layer %d %s;pull_{%s};counts", ilay - 1, kPtLabels[ipt], kParNames[ip]), + 100, -5, 5); + } + } + } + + // --- fill loop --- + const Long64_t nEntries = tree->GetEntries(); + for (Long64_t i = 0; i < nEntries; i++) { + tree->GetEntry(i); + if (i % 100000 == 0) { + std::cout << "Progress: " << i << "/" << nEntries << " (" << (100.0 * i / nEntries) << "%)" << std::endl; + } + + int ilay = lay + 1; + float pt = trk->getPt(); + float dYum = dY * 10000.f; + float dZum = dZ * 10000.f; + + // integrated (ipt=0) + differential + int iptDiff = getPtBin(pt); + for (int ipt : {0, iptDiff}) { + if (ipt < 0) { + continue; + } + for (int iv = 0; iv < kNVar; iv++) { + float val = (iv == 0) ? dYum : dZum; + hVsPhi[iv][ipt][ilay]->Fill(phi, val); + hVsEta[iv][ipt][ilay]->Fill(eta, val); + hProf[iv][ipt][ilay]->Fill(phi, eta, val); + } + for (int ip = 0; ip < kNPar; ip++) { + float sigma2 = trk->getDiagError2(ip); + if (sigma2 > 0) { + hPull[ip][ipt][ilay]->Fill((trk->getParam(ip) - mcTrk->getParam(ip)) / std::sqrt(sigma2)); + } + } + } + } + + // --- draw & save --- + auto drawSliceFits = [](TH2F* h) { + h->FitSlicesY(nullptr, 0, -1, 5); + auto* hMean = (TH1D*)gDirectory->Get(Form("%s_1", h->GetName())); + auto* hSigma = (TH1D*)gDirectory->Get(Form("%s_2", h->GetName())); + if (hMean && hSigma) { + for (auto* hh : {hMean, hSigma}) { + hh->SetMarkerStyle(20); + hh->SetMarkerSize(0.6); + } + hMean->SetMarkerColor(kRed); + hMean->SetLineColor(kRed); + hSigma->SetMarkerColor(kOrange + 1); + hSigma->SetLineColor(kOrange + 1); + hMean->Draw("same"); + hSigma->Draw("same"); + } + }; + + for (int ipt = 0; ipt < kNPtBins; ipt++) { + // dY/dZ vs phi + for (int iv = 0; iv < kNVar; iv++) { + auto* c = new TCanvas(Form("%s_%s_vs_phi%s", treeName, kVarNames[iv], kPtTags[ipt]), + Form("%s vs #phi %s", kVarNames[iv], kPtLabels[ipt]), 800, 1600); + c->Divide(2, 4); + for (int ilay = 0; ilay < kNLay; ilay++) { + c->cd(ilay + 1); + gPad->SetRightMargin(0.13); + hVsPhi[iv][ipt][ilay]->Draw("col"); + drawSliceFits(hVsPhi[iv][ipt][ilay]); + } + c->SaveAs(Form("%s.png", c->GetName())); + } + + // dY/dZ vs eta + for (int iv = 0; iv < kNVar; iv++) { + auto* c = new TCanvas(Form("%s_%s_vs_eta%s", treeName, kVarNames[iv], kPtTags[ipt]), + Form("%s vs #eta %s", kVarNames[iv], kPtLabels[ipt]), 800, 1600); + c->Divide(2, 4); + for (int ilay = 0; ilay < kNLay; ilay++) { + c->cd(ilay + 1); + gPad->SetRightMargin(0.13); + hVsEta[iv][ipt][ilay]->Draw("col"); + drawSliceFits(hVsEta[iv][ipt][ilay]); + } + c->SaveAs(Form("%s.png", c->GetName())); + } + + // profile2D + for (int iv = 0; iv < kNVar; iv++) { + auto* c = new TCanvas(Form("%s_%s_prof2d%s", treeName, kVarNames[iv], kPtTags[ipt]), + Form("%s #phi vs #eta %s", kVarNames[iv], kPtLabels[ipt]), 800, 1600); + c->Divide(2, 4); + for (int ilay = 0; ilay < kNLay; ilay++) { + c->cd(ilay + 1); + gPad->SetRightMargin(0.15); + hProf[iv][ipt][ilay]->Draw("colz"); + hProf[iv][ipt][ilay]->GetZaxis()->SetRangeUser(-100, 100); + } + c->SaveAs(Form("%s.png", c->GetName())); + } + + // pulls + for (int ilay = 0; ilay < kNLay; ilay++) { + auto* c = new TCanvas(Form("%s_pulls_l%d%s", treeName, ilay, kPtTags[ipt]), + Form("Pulls layer %d %s", ilay - 1, kPtLabels[ipt]), 1200, 800); + c->Divide(3, 2); + for (int ip = 0; ip < kNPar; ip++) { + c->cd(ip + 1); + hPull[ip][ipt][ilay]->Draw(); + if (hPull[ip][ipt][ilay]->GetEntries() > 20) { + hPull[ip][ipt][ilay]->Fit("gaus", "Q"); + } + } + c->SaveAs(Form("%s.png", c->GetName())); + } + } +} + +void PlotMisalignment(const char* fname = "its3TrackStudy.root") +{ + gStyle->SetOptStat(0); + gStyle->SetOptFit(1); + auto f = TFile::Open(fname); + processTree(f, "idealRes"); + processTree(f, "misRes"); +} diff --git a/Detectors/Upgrades/ITS3/study/macros/PlotResiduals.C b/Detectors/Upgrades/ITS3/study/macros/PlotResiduals.C new file mode 100644 index 0000000000000..e35fd4df125e5 --- /dev/null +++ b/Detectors/Upgrades/ITS3/study/macros/PlotResiduals.C @@ -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. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include +#include +#endif + +void PlotResiduals(const char* fname = "its3TrackStudy.root") +{ + auto f = TFile::Open(fname); + auto res = f->Get("res"); + + const int nLay = 8; + const int nVar = 6; + const char* vars[nVar] = {"dYInt", "dZInt", "dYIn", "dZIn", "dYOut", "dZOut"}; + const char* titles[nVar] = {"d_{Y} (#mum) (weighted)", "d_{Z} (#mum) (weighted)", "d_{Y} (#mu) (inward)", "d_{Y} (#mu) (inward)", "d_{Y} (#mu) (outward)", "d_{Z} (#mu) (outward)"}; + + TCanvas* canvs[nVar]; + for (int iv = 0; iv < nVar; iv++) { + canvs[iv] = new TCanvas(vars[iv], Form("%s residuals", vars[iv]), 800, 1600); + canvs[iv]->Divide(2, 4); + } + + for (int iv = 0; iv < nVar; iv++) { + canvs[iv]->cd(0); + for (int lay = -1; lay <= 6; lay++) { + canvs[iv]->cd(lay + 2); + gPad->SetRightMargin(0.13); + + TString hname = Form("h_%s_lay%d", vars[iv], lay + 1); + TString expr = Form("%s*10000:phi>>%s(100,0,6.283,100,-100,100)", vars[iv], hname.Data()); + TString sel = Form("lay==%d", lay); + res->Draw(expr, sel, "col"); + + auto* h = (TH2F*)gDirectory->Get(hname); + h->SetTitle(Form("Layer %d ;#phi (rad);%s", lay, titles[iv])); + h->GetZaxis()->SetLabelSize(0.035); + + // fit y-slices with gaussian + h->FitSlicesY(nullptr, 0, -1, 5); + auto* hMean = (TH1D*)gDirectory->Get(Form("%s_1", hname.Data())); + auto* hSigma = (TH1D*)gDirectory->Get(Form("%s_2", hname.Data())); + + for (auto* hh : {hMean, hSigma}) { + hh->SetMarkerStyle(20); + hh->SetMarkerSize(0.6); + } + hMean->SetMarkerColor(kRed); + hMean->SetLineColor(kRed); + hSigma->SetMarkerColor(kOrange + 1); + hSigma->SetLineColor(kOrange + 1); + hMean->Draw("same"); + hSigma->Draw("same"); + } + canvs[iv]->SaveAs(Form("%s.png", canvs[iv]->GetName())); + } +} diff --git a/Detectors/Upgrades/ITS3/study/src/TrackingStudy.cxx b/Detectors/Upgrades/ITS3/study/src/TrackingStudy.cxx index cb1d7f381983d..4ce2c79cb23f1 100644 --- a/Detectors/Upgrades/ITS3/study/src/TrackingStudy.cxx +++ b/Detectors/Upgrades/ITS3/study/src/TrackingStudy.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,9 +11,12 @@ #include #include +#include #include #include +#include +#include #include "CommonUtils/TreeStreamRedirector.h" #include "DataFormatsGlobalTracking/RecoContainer.h" @@ -35,6 +38,7 @@ #include "ITS3Reconstruction/IOUtils.h" #include "ITS3TrackingStudy/ITS3TrackingStudyParam.h" #include "ITS3TrackingStudy/ParticleInfoExt.h" +#include "ITS3Align/TrackFit.h" #include "ReconstructionDataFormats/DCA.h" #include "ReconstructionDataFormats/GlobalTrackID.h" #include "ReconstructionDataFormats/PrimaryVertex.h" @@ -43,6 +47,8 @@ #include "SimulationDataFormat/MCEventLabel.h" #include "SimulationDataFormat/MCUtils.h" #include "Steer/MCKinematicsReader.h" +#include "MathUtils/LegendrePols.h" +#include "Framework/Logger.h" namespace o2::its3::study { @@ -58,6 +64,10 @@ using T2VMap = std::unordered_map; class TrackingStudySpec : public Task { public: + TrackingStudySpec(const TrackingStudySpec&) = delete; + TrackingStudySpec(TrackingStudySpec&&) = delete; + TrackingStudySpec& operator=(const TrackingStudySpec&) = delete; + TrackingStudySpec& operator=(TrackingStudySpec&&) = delete; 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; @@ -67,16 +77,19 @@ class TrackingStudySpec : public Task void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; private: - void process(o2::globaltracking::RecoContainer& recoData); + void process(); 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); + void prepareITSClusters(); + bool selectTrack(GTrackID trkID, bool checkMCTruth = true) const; + T2VMap buildT2V(bool includeCont = false, bool requireMCMatch = true) const; + bool refitITSPVTrack(o2::track::TrackParCov& trFit, GTrackID gidx); + + void doDCAStudy(); + void doDCARefitStudy(); + void doPullStudy(); + void doMCStudy(); + void doResidStudy(); + void doMisalignmentStudy(); struct TrackCounter { TrackCounter() = default; @@ -126,14 +139,21 @@ class TrackingStudySpec : public Task }; TrackCounter mTrackCounter; + using TrackingCluster = align::TrackingCluster; + std::vector mITScl; + std::span mITSclRef; + + const ITS3TrackingStudyParam* mParams{nullptr}; 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 + o2::steer::MCKinematicsReader mMCReader; // reader of MC information const o2::its3::TopologyDictionary* mITSDict = nullptr; // cluster patterns dictionary + o2::globaltracking::RecoContainer mRecoData; + std::array mDeformations; // one per sensorID (0-5) }; void TrackingStudySpec::init(InitContext& ic) @@ -144,17 +164,16 @@ void TrackingStudySpec::init(InitContext& ic) 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())) { + if (mUseMC && !mMCReader.initFromDigitContext(o2::base::NameConf::getCollisionContextFileName())) { LOGP(fatal, "initialization of MCKinematicsReader failed"); } } void TrackingStudySpec::run(ProcessingContext& pc) { - o2::globaltracking::RecoContainer recoData; - recoData.collectData(pc, *mDataRequest); + mRecoData.collectData(pc, *mDataRequest); updateTimeDependentParams(pc); - process(recoData); + process(); } void TrackingStudySpec::updateTimeDependentParams(ProcessingContext& pc) @@ -165,6 +184,30 @@ void TrackingStudySpec::updateTimeDependentParams(ProcessingContext& pc) 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)); + mParams = &ITS3TrackingStudyParam::Instance(); + if (mParams->doMisalignment) { + TMatrixD null(1, 1); + null(0, 0) = 0; + for (int i = 0; i < 6; ++i) { + mDeformations[i] = o2::math_utils::Legendre2DPolynominal(null); + } + if (!mParams->misAlgJson.empty()) { + using json = nlohmann::json; + std::ifstream f(mParams->misAlgJson); + auto data = json::parse(f); + for (const auto& item : data) { + int id = item["id"].get(); + auto v = item["matrix"].get>>(); + TMatrixD m(v.size(), v[v.size() - 1].size()); + for (size_t r{0}; r < v.size(); ++r) { + for (size_t c{0}; c < v[r].size(); ++c) { + m(r, c) = v[r][c]; + } + } + mDeformations[id] = o2::math_utils::Legendre2DPolynominal(m); + } + } + } } } @@ -185,76 +228,105 @@ void TrackingStudySpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) } } -void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) +void TrackingStudySpec::process() { - const auto& conf = ITS3TrackingStudyParam::Instance(); - if (conf.doDCA) { - doDCAStudy(recoData); + prepareITSClusters(); + if (mParams->doDCA) { + doDCAStudy(); + } + if (mParams->doDCARefit) { + doDCARefitStudy(); + } + if (mUseMC && mParams->doPull) { + doPullStudy(); } - if (conf.doDCARefit) { - doDCARefitStudy(recoData); + if (mUseMC && mParams->doMC) { + doMCStudy(); } - if (mUseMC && conf.doPull) { - doPullStudy(recoData); + if (mParams->doResid) { + doResidStudy(); } - if (mUseMC && conf.doMC) { - doMCStudy(recoData); + if (mUseMC && mParams->doMisalignment) { + doMisalignmentStudy(); } } -std::vector> TrackingStudySpec::prepareITSClusters(const o2::globaltracking::RecoContainer& data) const +void TrackingStudySpec::prepareITSClusters() { - 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); + const auto& clusITS = mRecoData.getITSClusters(); + LOGP(info, "Preparing {} measurments", clusITS.size()); + const auto& patterns = mRecoData.getITSClustersPatterns(); + mITScl.reserve(clusITS.size()); + auto pattIt = patterns.begin(); + auto geom = its::GeometryTGeo::Instance(); + mITSclRef = mRecoData.getITSTracksClusterRefs(); + mITScl.clear(); + mITScl.reserve(clusITS.size()); + for (const auto& cls : clusITS) { + const auto sens = cls.getSensorID(); + float sigmaY2{0}, sigmaZ2{0}; + math_utils::Point3D locXYZ = o2::its3::ioutils::extractClusterData(cls, pattIt, mITSDict, sigmaY2, sigmaZ2); + // Transformation to the local --> global + const auto gloXYZ = geom->getMatrixL2G(sens) * locXYZ; + // Inverse transformation to the local --> tracking + o2::math_utils::Point3D trkXYZ = geom->getMatrixT2L(sens) ^ locXYZ; + // Tracking alpha angle + // We want that each cluster rotates its tracking frame to the clusters phi + // that way the track linearization around the measurement is less biases to the arc + // this means automatically that the measurement on the arc is at 0 for the curved layers + float alpha = geom->getSensorRefAlpha(sens); + if (constants::detID::isDetITS3(sens)) { + trkXYZ.SetY(0.f); + // alpha&x always have to be defined wrt to the global Z axis! + trkXYZ.SetX(std::hypot(gloXYZ.x(), gloXYZ.y())); + alpha = std::atan2(gloXYZ.y(), gloXYZ.x()); + } + auto& cl3d = mITScl.emplace_back(sens, trkXYZ); + cl3d.setErrors(sigmaY2, sigmaZ2, 0.f); + cl3d.alpha = alpha; + math_utils::detail::bringToPMPi(cl3d.alpha); // alpha is defined on -Pi,Pi + } } -bool TrackingStudySpec::selectTrack(GTrackID trkID, o2::globaltracking::RecoContainer& recoData, bool checkMCTruth) const +bool TrackingStudySpec::selectTrack(GTrackID trkID, bool checkMCTruth) const { - const auto& conf = ITS3TrackingStudyParam::Instance(); if (!trkID.includesDet(GTrackID::ITS)) { return false; } - if (!recoData.isTrackSourceLoaded(trkID.getSource())) { + if (!mRecoData.isTrackSourceLoaded(trkID.getSource())) { return false; } - auto contributorsGID = recoData.getSingleDetectorRefs(trkID); + auto contributorsGID = mRecoData.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) { + const auto& itsTrk = mRecoData.getITSTrack(contributorsGID[GTrackID::ITS]); + if (itsTrk.getChi2() > mParams->maxChi2 || itsTrk.getNClusters() < mParams->minITSCls) { return false; } // TPC specific if (contributorsGID[GTrackID::TPC].isIndexSet()) { - const auto& tpcTrk = recoData.getTPCTrack(contributorsGID[GTrackID::TPC]); - if (tpcTrk.getNClusters() < conf.minTPCCls) { + const auto& tpcTrk = mRecoData.getTPCTrack(contributorsGID[GTrackID::TPC]); + if (tpcTrk.getNClusters() < mParams->minTPCCls) { return false; } } // general - const auto& gTrk = recoData.getTrackParam(trkID); - if (gTrk.getPt() < conf.minPt || gTrk.getPt() > conf.maxPt) { + const auto& gTrk = mRecoData.getTrackParam(trkID); + if (gTrk.getPt() < mParams->minPt || gTrk.getPt() > mParams->maxPt) { return false; } - if (std::abs(gTrk.getEta()) > conf.maxEta) { + if (std::abs(gTrk.getEta()) > mParams->maxEta) { return false; } if (mUseMC && checkMCTruth) { - const auto& itsLbl = recoData.getTrackMCLabel(contributorsGID[GTrackID::ITS]); + const auto& itsLbl = mRecoData.getTrackMCLabel(contributorsGID[GTrackID::ITS]); if (!itsLbl.isValid()) { return false; } if (contributorsGID[GTrackID::TPC].isIndexSet()) { - const auto& tpcLbl = recoData.getTrackMCLabel(contributorsGID[GTrackID::TPC]); + const auto& tpcLbl = mRecoData.getTrackMCLabel(contributorsGID[GTrackID::TPC]); if (itsLbl != tpcLbl) { return false; } @@ -263,7 +335,7 @@ bool TrackingStudySpec::selectTrack(GTrackID trkID, o2::globaltracking::RecoCont // TODO } if (contributorsGID[GTrackID::TOF].isIndexSet()) { - const auto& tofLbls = recoData.getTOFClustersMCLabels()->getLabels(contributorsGID[GTrackID::TOF]); + const auto& tofLbls = mRecoData.getTOFClustersMCLabels()->getLabels(contributorsGID[GTrackID::TOF]); for (const auto& lbl : tofLbls) { if (lbl.isValid()) { return true; @@ -274,22 +346,21 @@ bool TrackingStudySpec::selectTrack(GTrackID trkID, o2::globaltracking::RecoCont return true; } -T2VMap TrackingStudySpec::buildT2V(o2::globaltracking::RecoContainer& recoData, bool includeCont, bool requireMCMatch) const +T2VMap TrackingStudySpec::buildT2V(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 + 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 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) { + if (pv.getNContributors() - 1 < mParams->minPVCont) { continue; } if (requireMCMatch) { - auto pvl = recoData.getPrimaryVertexMCLabel(iv); + auto pvl = mRecoData.getPrimaryVertexMCLabel(iv); } const auto& vtxRef = vtxRefs[iv]; int it = vtxRef.getFirstEntry(), itLim = it + vtxRef.getEntries(); @@ -298,26 +369,26 @@ T2VMap TrackingStudySpec::buildT2V(o2::globaltracking::RecoContainer& recoData, if (tvid.isAmbiguous()) { continue; } - if (!recoData.isTrackSourceLoaded(tvid.getSource())) { + if (!mRecoData.isTrackSourceLoaded(tvid.getSource())) { continue; } if (mUseMC && requireMCMatch) { - const auto& pvlbl = recoData.getPrimaryVertexMCLabel(iv); - if (pvlbl.getEventID() != recoData.getTrackMCLabel(tvid).getEventID()) { + const auto& pvlbl = mRecoData.getPrimaryVertexMCLabel(iv); + if (pvlbl.getEventID() != mRecoData.getTrackMCLabel(tvid).getEventID()) { continue; } } t2v[tvid] = iv; if (includeCont) { - auto contributorsGID = recoData.getSingleDetectorRefs(tvid); + auto contributorsGID = mRecoData.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()) { + if (!mRecoData.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()) { + const auto& pvlbl = mRecoData.getPrimaryVertexMCLabel(iv); + if (pvlbl.getEventID() != mRecoData.getTrackMCLabel(contributorsGID[cis]).getEventID()) { continue; } } @@ -329,19 +400,18 @@ T2VMap TrackingStudySpec::buildT2V(o2::globaltracking::RecoContainer& recoData, return std::move(t2v); } -bool TrackingStudySpec::refitITSPVTrack(o2::globaltracking::RecoContainer& recoData, o2::track::TrackParCov& trFit, GTrackID gidx) +bool TrackingStudySpec::refitITSPVTrack(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 auto pvvec = mRecoData.getPrimaryVertices(); + static auto t2v = buildT2V(true, true); static std::vector itsTracksROF; if (static bool done{false}; !done) { done = true; - const auto& itsTracksROFRec = recoData.getITSTracksROFRecords(); - itsTracksROF.resize(recoData.getITSTracks().size()); + const auto& itsTracksROFRec = mRecoData.getITSTracksROFRecords(); + itsTracksROF.resize(mRecoData.getITSTracks().size()); for (unsigned irf = 0, cnt = 0; irf < itsTracksROFRec.size(); irf++) { int ntr = itsTracksROFRec[irf].getNEntries(); for (int itr = 0; itr < ntr; itr++) { @@ -350,20 +420,18 @@ bool TrackingStudySpec::refitITSPVTrack(o2::globaltracking::RecoContainer& recoD } } 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); + std::array clArr{nullptr}; + const auto trkIn = mRecoData.getTrackParam(gidx); + const auto trkOut = mRecoData.getTrackParamOut(gidx); + const auto& itsTrOrig = mRecoData.getITSTrack(gidx); int ncl = itsTrOrig.getNumberOfClusters(), rof = itsTracksROF[gidx.getIndex()]; - const auto& itsTrackClusRefs = recoData.getITSTracksClusterRefs(); + const auto& itsTrackClusRefs = mRecoData.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)) { + if (!prop->propagateToDCA(pv, trkPV, prop->getNominalBz(), 2.0, mParams->CorrType)) { mTrackCounter -= gidx.getSource(); return false; } @@ -371,25 +439,25 @@ bool TrackingStudySpec::refitITSPVTrack(o2::globaltracking::RecoContainer& recoD 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(); + TrackingCluster pvCls; + pvCls.setXYZ((pv.getX() * cosAlp) + (pv.getY() * sinAlp), (-pv.getX() * sinAlp) + (pv.getY() * cosAlp), pv.getZ()); + pvCls.setSigmaY2(0.5f * (pv.getSigmaX2() + pv.getSigmaY2())); + pvCls.setSigmaZ2(pv.getSigmaZ2()); + clArr[0] = &pvCls; 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()); + clArr[ncl - icl] = &mITScl[itsTrackClusRefs[clEntry + icl]]; } // 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)) { + if (!trFit.rotate(clArr[icl]->alpha) || !prop->propagateToX(trFit, clArr[icl]->getX(), prop->getNominalBz(), 0.85, 2.0, mParams->CorrType)) { mTrackCounter -= gidx.getSource(); return false; } - chi2 += trFit.getPredictedChi2(clArr[icl]); - if (!trFit.update(clArr[icl])) { + chi2 += trFit.getPredictedChi2(*clArr[icl]); + if (!trFit.update(*clArr[icl])) { mTrackCounter -= gidx.getSource(); return false; } @@ -398,27 +466,26 @@ bool TrackingStudySpec::refitITSPVTrack(o2::globaltracking::RecoContainer& recoD return true; }; -void TrackingStudySpec::doDCAStudy(o2::globaltracking::RecoContainer& recoData) +void TrackingStudySpec::doDCAStudy() { /// 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 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 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]) { + if (!mRecoData.isTrackSourceLoaded(is) || !dm[DetID::ITS]) { mTrackCounter &= is; continue; } @@ -432,23 +499,23 @@ void TrackingStudySpec::doDCAStudy(o2::globaltracking::RecoContainer& recoData) // 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); + auto contributorsGID = mRecoData.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()) { + if (!mRecoData.isTrackSourceLoaded(cis) || !cdm[DetID::ITS] || !contributorsGID[cis].isIndexSet()) { mTrackCounter &= cis; continue; } - if (!selectTrack(contributorsGID[cis], recoData)) { + if (!selectTrack(contributorsGID[cis])) { mTrackCounter &= vid.getSource(); continue; } o2::dataformats::DCA dcaInfo; - const auto& trk = recoData.getTrackParam(contributorsGID[cis]); + const auto& trk = mRecoData.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])) { + if (mParams->refitITS && cis == GTrackID::ITS && !refitITSPVTrack(trkRefit, contributorsGID[cis])) { mTrackCounter -= cis; continue; } else { @@ -456,7 +523,7 @@ void TrackingStudySpec::doDCAStudy(o2::globaltracking::RecoContainer& recoData) }; auto trkDCA = trk; - if (!prop->propagateToDCABxByBz(pv, trkDCA, 2.f, conf.CorrType, &dcaInfo)) { + if (!prop->propagateToDCABxByBz(pv, trkDCA, 2.f, mParams->CorrType, &dcaInfo)) { mTrackCounter -= cis; ++nDCAFitsFail; continue; @@ -470,19 +537,19 @@ void TrackingStudySpec::doDCAStudy(o2::globaltracking::RecoContainer& recoData) << "dca=" << dcaInfo; if (mUseMC) { - const auto& lbl = recoData.getTrackMCLabel(contributorsGID[cis]); + const auto& lbl = mRecoData.getTrackMCLabel(contributorsGID[cis]); lbl.print(); o2::dataformats::DCA dcaInfoMC; - const auto& eve = mcReader.getMCEventHeader(lbl.getSourceID(), lbl.getEventID()); + const auto& eve = mMCReader.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)) { + if (!prop->propagateToDCABxByBz(mcEve, trkC, 2.f, mParams->CorrType, &dcaInfoMC)) { mTrackCounter -= cis; ++nDCAFitsFail; continue; } - const auto& mcTrk = mcReader.getTrack(lbl); + const auto& mcTrk = mMCReader.getTrack(lbl); if (mcTrk == nullptr) { LOGP(fatal, "mcTrk is null did selection fail?"); } @@ -503,21 +570,20 @@ void TrackingStudySpec::doDCAStudy(o2::globaltracking::RecoContainer& recoData) mTrackCounter.print(); } -void TrackingStudySpec::doDCARefitStudy(o2::globaltracking::RecoContainer& recoData) +void TrackingStudySpec::doDCARefitStudy() { /// 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); + auto pvvec = mRecoData.getPrimaryVertices(); + auto vtxRefs = mRecoData.getPrimaryVertexMatchedTrackRefs(); // references from vertex to these track IDs + auto nv = vtxRefs.size() - 1; // last entry is for unassigned tracks, ignore them + auto t2v = buildT2V(); std::vector> v2t; v2t.resize(nv); auto creator = [&](const auto& trk, GTrackID trkID, float _t0, float terr) -> bool { @@ -531,11 +597,11 @@ void TrackingStudySpec::doDCARefitStudy(o2::globaltracking::RecoContainer& recoD } // general if constexpr (isBarrelTrack()) { - if (trk.getPt() < conf.minPt || trk.getPt() > conf.maxPt) { + if (trk.getPt() < mParams->minPt || trk.getPt() > mParams->maxPt) { mTrackCounter &= trkID.getSource(); return false; } - if (std::abs(trk.getEta()) > conf.maxEta) { + if (std::abs(trk.getEta()) > mParams->maxEta) { mTrackCounter &= trkID.getSource(); return false; } @@ -543,7 +609,7 @@ void TrackingStudySpec::doDCARefitStudy(o2::globaltracking::RecoContainer& recoD mTrackCounter &= trkID.getSource(); return false; } - if (!selectTrack(trkID, recoData, mUseMC)) { + if (!selectTrack(trkID, mUseMC)) { mTrackCounter &= trkID.getSource(); return false; } @@ -551,20 +617,20 @@ void TrackingStudySpec::doDCARefitStudy(o2::globaltracking::RecoContainer& recoD v2t[t2v[trkID]].push_back(trkID); return true; }; - recoData.createTracksVariadic(creator); + mRecoData.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) { + if (trkIDs.size() - 1 < mParams->minPVCont) { continue; } std::vector trks; trks.reserve(trkIDs.size()); for (const auto& trkID : trkIDs) { - trks.push_back(recoData.getTrackParam(trkID)); + trks.push_back(mRecoData.getTrackParam(trkID)); } if (!mVertexer.prepareVertexRefit(trks, pv)) { @@ -585,14 +651,14 @@ void TrackingStudySpec::doDCARefitStudy(o2::globaltracking::RecoContainer& recoD // 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)) { + if (!prop->propagateToDCABxByBz(pv, trkC, 2.f, mParams->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)) { + if (!prop->propagateToDCABxByBz(pv, trkCRefit, 2.f, mParams->CorrType, &dcaInfoRefit)) { mTrackCounter -= trkIDs[it].getSource(); ++nDCAFitsFail; continue; @@ -606,7 +672,7 @@ void TrackingStudySpec::doDCARefitStudy(o2::globaltracking::RecoContainer& recoD << "trkAtPVRefit=" << trkC << "dcaRefit=" << dcaInfoRefit; if (mUseMC) { - const auto& mcTrk = mcReader.getTrack(recoData.getTrackMCLabel(trkIDs[it])); + const auto& mcTrk = mMCReader.getTrack(mRecoData.getTrackMCLabel(trkIDs[it])); if (mcTrk == nullptr) { LOGP(fatal, "mcTrk is null did selection fail?"); } @@ -622,7 +688,7 @@ void TrackingStudySpec::doDCARefitStudy(o2::globaltracking::RecoContainer& recoD mTrackCounter.print(); } -void TrackingStudySpec::doPullStudy(o2::globaltracking::RecoContainer& recoData) +void TrackingStudySpec::doPullStudy() { // check track pulls compared to mc generation LOGP(info, "Doing Pull study"); @@ -631,21 +697,20 @@ void TrackingStudySpec::doPullStudy(o2::globaltracking::RecoContainer& recoData) 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)) { + if (!selectTrack(trkID)) { mTrackCounter &= trkID.getSource(); return; } - const auto mcTrk = mcReader.getTrack(recoData.getTrackMCLabel(trkID)); + const auto mcTrk = mMCReader.getTrack(mRecoData.getTrackMCLabel(trkID)); if (!mcTrk) { return; } - auto trk = recoData.getTrackParam(trkID); + auto trk = mRecoData.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)) { + if (mParams->refitITS && trkID.getSource() == GTrackID::ITS && !refitITSPVTrack(trk, trkID)) { mTrackCounter -= trkID.getSource(); ++nPullsFail; return; @@ -666,8 +731,8 @@ void TrackingStudySpec::doPullStudy(o2::globaltracking::RecoContainer& recoData) ++nPullsFail; return; } - const auto contTrk = recoData.getSingleDetectorRefs(trkID); - const auto& itsTrk = recoData.getITSTrack(contTrk[GTrackID::ITS]); + const auto contTrk = mRecoData.getSingleDetectorRefs(trkID); + const auto& itsTrk = mRecoData.getITSTrack(contTrk[GTrackID::ITS]); (*mDBGOut) << "pull" @@ -681,19 +746,19 @@ void TrackingStudySpec::doPullStudy(o2::globaltracking::RecoContainer& recoData) mTrackCounter += trkID.getSource(); }; - for (size_t iTrk{0}; iTrk < recoData.getITSTracks().size(); ++iTrk) { + for (size_t iTrk{0}; iTrk < mRecoData.getITSTracks().size(); ++iTrk) { checkInTrack(GTrackID(iTrk, GTrackID::ITS)); } - for (size_t iTrk{0}; iTrk < recoData.getTPCITSTracks().size(); ++iTrk) { + for (size_t iTrk{0}; iTrk < mRecoData.getTPCITSTracks().size(); ++iTrk) { checkInTrack(GTrackID(iTrk, GTrackID::ITSTPC)); } - for (size_t iTrk{0}; iTrk < recoData.getITSTPCTRDTracksMCLabels().size(); ++iTrk) { + for (size_t iTrk{0}; iTrk < mRecoData.getITSTPCTRDTracksMCLabels().size(); ++iTrk) { checkInTrack(GTrackID(iTrk, GTrackID::ITSTPCTRD)); } - for (size_t iTrk{0}; iTrk < recoData.getITSTPCTOFMatches().size(); ++iTrk) { + for (size_t iTrk{0}; iTrk < mRecoData.getITSTPCTOFMatches().size(); ++iTrk) { checkInTrack(GTrackID(iTrk, GTrackID::ITSTPCTOF)); } - for (size_t iTrk{0}; iTrk < recoData.getITSTPCTRDTOFMatches().size(); ++iTrk) { + for (size_t iTrk{0}; iTrk < mRecoData.getITSTPCTRDTOFMatches().size(); ++iTrk) { checkInTrack(GTrackID(iTrk, GTrackID::ITSTPCTRDTOF)); } sw.Stop(); @@ -701,7 +766,7 @@ void TrackingStudySpec::doPullStudy(o2::globaltracking::RecoContainer& recoData) mTrackCounter.print(); } -void TrackingStudySpec::doMCStudy(o2::globaltracking::RecoContainer& recoData) +void TrackingStudySpec::doMCStudy() { LOGP(info, "Doing MC study"); mTrackCounter.reset(); @@ -710,12 +775,12 @@ void TrackingStudySpec::doMCStudy(o2::globaltracking::RecoContainer& recoData) int nTracks{0}; const int iSrc{0}; - const int nev = mcReader.getNEvents(iSrc); + const int nev = mMCReader.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); + const auto& mcTrks = mMCReader.getTracks(iSrc, iEve); for (int iTrk{0}; iTrk < mcTrks.size(); ++iTrk) { const auto& mcTrk = mcTrks[iTrk]; const auto pdg = mcTrk.GetPdgCode(); @@ -732,8 +797,8 @@ void TrackingStudySpec::doMCStudy(o2::globaltracking::RecoContainer& recoData) } } LOGP(info, "** Creating particle/clusters correspondence ... "); - const auto& clusters = recoData.getITSClusters(); - const auto& clustersMCLCont = recoData.getITSClustersMCLabels(); + const auto& clusters = mRecoData.getITSClusters(); + const auto& clustersMCLCont = mRecoData.getITSClustersMCLabels(); for (auto iCluster{0}; iCluster < clusters.size(); ++iCluster) { auto labs = clustersMCLCont->getLabels(iCluster); for (auto& lab : labs) { @@ -755,7 +820,7 @@ void TrackingStudySpec::doMCStudy(o2::globaltracking::RecoContainer& recoData) 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]); + const auto& lbl = mRecoData.getTrackMCLabel(contributorsGID[det]); if (lbl.isValid()) { o2::MCCompLabel iLbl(lbl.getTrackID(), lbl.getEventID(), lbl.getSourceID()); if (info.contains(iLbl)) { @@ -776,11 +841,11 @@ void TrackingStudySpec::doMCStudy(o2::globaltracking::RecoContainer& recoData) return false; } // general - auto contributorsGID = recoData.getSingleDetectorRefs(trkID); + auto contributorsGID = mRecoData.getSingleDetectorRefs(trkID); if (!contributorsGID[GTrackID::ITS].isIndexSet()) { // we need of course ITS return false; } - const auto& gLbl = recoData.getTrackMCLabel(trkID); + const auto& gLbl = mRecoData.getTrackMCLabel(trkID); if (!gLbl.isValid()) { return false; } @@ -789,7 +854,7 @@ void TrackingStudySpec::doMCStudy(o2::globaltracking::RecoContainer& recoData) return false; } auto& part = info[iLbl]; - part.recoTrack = recoData.getTrackParam(trkID); + part.recoTrack = mRecoData.getTrackParam(trkID); accountLbl(contributorsGID, DetID::ITS); accountLbl(contributorsGID, DetID::TPC); @@ -799,7 +864,7 @@ void TrackingStudySpec::doMCStudy(o2::globaltracking::RecoContainer& recoData) ++nTracks; return true; }; - recoData.createTracksVariadic(creator); + mRecoData.createTracksVariadic(creator); LOGP(info, "Streaming output to tree"); for (const auto& [_, part] : info) { @@ -812,7 +877,319 @@ void TrackingStudySpec::doMCStudy(o2::globaltracking::RecoContainer& recoData) 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) +void TrackingStudySpec::doResidStudy() +{ + LOGP(info, "Doing residual study"); + const auto geom = o2::its::GeometryTGeo::Instance(); + const auto prop = o2::base::Propagator::Instance(); + const float bz = prop->getNominalBz(); + + int goodRefit{0}, notPassedSel{0}, fitFail{0}; + + auto doRefits = [&](const o2::its::TrackITS& iTrack, const o2::MCCompLabel& lbl) { + std::array cl; + std::array clArr{nullptr}; + if (mParams->addPVAsCluster) { + const auto& eve = mMCReader.getMCEventHeader(lbl.getSourceID(), lbl.getEventID()); + dataformats::VertexBase pv; + auto trFitOut = iTrack.getParamIn(); + pv.setXYZ(eve.GetX(), eve.GetY(), eve.GetZ()); + if (!prop->propagateToDCA(pv, trFitOut, bz, base::Propagator::MAX_STEP, mParams->CorrType)) { + return; + } + pv.setSigmaX(20e-4f); + pv.setSigmaY(20e-4f); + pv.setSigmaZ(20e-4f); + float cosAlp = NAN, sinAlp = NAN; + o2::math_utils::sincos(trFitOut.getAlpha(), sinAlp, cosAlp); + cl[0].alpha = trFitOut.getAlpha(); + cl[0].setXYZ((pv.getX() * cosAlp) + (pv.getY() * sinAlp), (-pv.getX() * sinAlp) + (pv.getY() * cosAlp), pv.getZ()); + cl[0].setSigmaY2(0.5f * (pv.getSigmaX2() + pv.getSigmaY2())); + cl[0].setSigmaZ2(pv.getSigmaZ2()); + cl[0].setSensorID(-1); + clArr[0] = &cl[0]; + } + + // collect track clusters into layer slots + int nCl = iTrack.getNClusters(); + for (int i = 0; i < nCl; i++) { + const auto& curClu = mITScl[mITSclRef[iTrack.getClusterEntry(i)]]; + int sens = curClu.getSensorID(); + int llr = geom->getLayer(sens); + if (clArr[1 + llr]) { + LOGP(fatal, "Cluster at lr {} was already assigned, old sens {}, new sens {}", llr, clArr[1 + llr]->getSensorID(), sens); + } + clArr[1 + llr] = &curClu; + } + + std::array extrapOut, extrapInw; + float chi2{0}; + if (!align::doBidirRefit(iTrack, clArr, extrapOut, extrapInw, chi2, mParams->useStableRef, mParams->CorrType)) { + ++fitFail; + return; + } + + for (int i = 0; i <= 7; i++) { + if (clArr[i]) { + const auto tInt = align::interpolateTrackParCov(extrapInw[i], extrapOut[i]); + if (!tInt.isValid()) { + continue; + } + auto phi = i == 0 ? tInt.getPhi() : tInt.getPhiPos(); + o2::math_utils::bringTo02Pi(phi); + (*mDBGOut) << "res" + << "dYInt=" << clArr[i]->getY() - tInt.getY() + << "dZInt=" << clArr[i]->getZ() - tInt.getZ() + << "dYIn=" << clArr[i]->getY() - extrapInw[i].getY() + << "dZIn=" << clArr[i]->getZ() - extrapInw[i].getZ() + << "dYOut=" << clArr[i]->getY() - extrapOut[i].getY() + << "dZOut=" << clArr[i]->getZ() - extrapOut[i].getZ() + << "chi2=" << chi2 + << "clY=" << clArr[i]->getY() + << "clZ=" << clArr[i]->getZ() + << "clX=" << clArr[i]->getX() + << "alpha=" << clArr[i]->alpha + << "sens=" << clArr[i]->getSensorID() + << "phi=" << phi + << "pt=" << tInt.getPt() + << "chip=" << constants::detID::getSensorID(clArr[i]->getSensorID()) + << "lay=" << i - 1 + << "\n"; + } + } + ++goodRefit; + }; + + const auto itsTracks = mRecoData.getITSTracks(); + const auto itsMC = mRecoData.getITSTracksMCLabels(); + for (size_t iTrk{0}; iTrk < itsTracks.size(); ++iTrk) { + const auto& iTrack = itsTracks[iTrk]; + const auto& lbl = itsMC[iTrk]; + const auto& mc = mMCReader.getTrack(lbl); + if (std::abs(iTrack.getEta()) > mParams->maxEta || iTrack.getChi2() > mParams->maxChi2 || iTrack.getNClusters() < mParams->minITSCls || iTrack.getPt() < mParams->minPt || !lbl.isCorrect() || !mc->isPrimary()) { + ++notPassedSel; + continue; + } + doRefits(iTrack, lbl); + } + + LOGP(info, "\trefitted {} out of {} tracks ({} !sel, {} !fit)", goodRefit, itsTracks.size(), notPassedSel, fitFail); +} + +void TrackingStudySpec::doMisalignmentStudy() +{ + LOGP(info, "Doing misalignment study"); + const auto prop = o2::base::Propagator::Instance(); + const auto geom = o2::its::GeometryTGeo::Instance(); + + int goodRefit{0}, notPassedSel{0}, fitFail{0}, fitFailMis{0}; + + // compute normalized (u,v) in [-1,1] from global position + auto computeUV = [](float gloX, float gloY, float gloZ, int sensorID, float radius) -> std::pair { + const bool isTop = sensorID % 2 == 0; + const double phi = o2::math_utils::to02Pi(std::atan2(gloY, gloX)); + const double phiBorder1 = o2::math_utils::to02Pi(((isTop ? 0. : 1.) * TMath::Pi()) + std::asin(constants::equatorialGap / 2. / radius)); + const double phiBorder2 = o2::math_utils::to02Pi(((isTop ? 1. : 2.) * TMath::Pi()) - std::asin(constants::equatorialGap / 2. / radius)); + const double u = (((phi - phiBorder1) * 2.) / (phiBorder2 - phiBorder1)) - 1.; + const double v = ((2. * gloZ + constants::segment::lengthSensitive) / constants::segment::lengthSensitive) - 1.; + return {u, v}; + }; + + float chi2{0}; + auto writeTree = [&](const char* treeName, + const std::array& clArr, + const std::array& extrapOut, + const std::array& extrapInw, + const o2::MCCompLabel& lbl) { + for (int i = 0; i <= 7; i++) { + if (!clArr[i]) { + continue; + } + // interpolated result + auto tInt = align::interpolateTrackParCov(extrapInw[i], extrapOut[i]); + if (!tInt.isValid()) { + continue; + } + float dY = clArr[i]->getY() - tInt.getY(); + float dZ = clArr[i]->getZ() - tInt.getZ(); + // MC truth at same (alpha, x) + o2::track::TrackPar mcTrkAtX; + const auto mcTrk = mMCReader.getTrack(lbl); + if (mcTrk) { + std::array xyz{(float)mcTrk->GetStartVertexCoordinatesX(), (float)mcTrk->GetStartVertexCoordinatesY(), (float)mcTrk->GetStartVertexCoordinatesZ()}; + std::array pxyz{(float)mcTrk->GetStartVertexMomentumX(), (float)mcTrk->GetStartVertexMomentumY(), (float)mcTrk->GetStartVertexMomentumZ()}; + TParticlePDG* pPDG = TDatabasePDG::Instance()->GetParticle(mcTrk->GetPdgCode()); + if (pPDG) { + mcTrkAtX = o2::track::TrackPar(xyz, pxyz, TMath::Nint(pPDG->Charge() / 3), false); + if (mcTrkAtX.rotate(tInt.getAlpha()) && prop->PropagateToXBxByBz(mcTrkAtX, tInt.getX())) { + auto phi = i == 0 ? tInt.getPhi() : tInt.getPhiPos(); + o2::math_utils::bringTo02Pi(phi); + (*mDBGOut) << treeName + << "trk=" << tInt + << "mcTrk=" << mcTrkAtX + << "chi2=" << chi2 + << "dY=" << dY + << "dZ=" << dZ + << "phi=" << phi + << "eta=" << tInt.getEta() + << "lay=" << i - 1 + << "\n"; + } + } + } + } + }; + + const auto itsTracks = mRecoData.getITSTracks(); + const auto itsMC = mRecoData.getITSTracksMCLabels(); + for (size_t iTrk{0}; iTrk < itsTracks.size(); ++iTrk) { + const auto& iTrack = itsTracks[iTrk]; + if (std::abs(iTrack.getEta()) > mParams->maxEta || iTrack.getChi2() > mParams->maxChi2 || iTrack.getNClusters() < mParams->minITSCls || iTrack.getPt() < mParams->minPt) { + ++notPassedSel; + continue; + } + const auto& lbl = itsMC[iTrk]; + if (!lbl.isCorrect() || !lbl.isValid()) { + ++notPassedSel; + continue; + } + const auto& mc = mMCReader.getTrack(lbl); + if (!mc->isPrimary()) { + ++notPassedSel; + continue; + } + + // ideal clusters + std::array cl; + std::array clArr{nullptr}; + if (mParams->addPVAsCluster) { + const auto& eve = mMCReader.getMCEventHeader(lbl.getSourceID(), lbl.getEventID()); + dataformats::VertexBase pv; + auto trFitOut = iTrack.getParamIn(); + pv.setXYZ(eve.GetX(), eve.GetY(), eve.GetZ()); + if (!prop->propagateToDCA(pv, trFitOut, prop->getNominalBz(), base::Propagator::MAX_STEP, mParams->CorrType)) { + return; + } + pv.setSigmaX(20e-4f); + pv.setSigmaY(20e-4f); + pv.setSigmaZ(20e-4f); + float cosAlp = NAN, sinAlp = NAN; + o2::math_utils::sincos(trFitOut.getAlpha(), sinAlp, cosAlp); + cl[0].alpha = trFitOut.getAlpha(); + cl[0].setXYZ((pv.getX() * cosAlp) + (pv.getY() * sinAlp), (-pv.getX() * sinAlp) + (pv.getY() * cosAlp), pv.getZ()); + cl[0].setSigmaY2(0.5f * (pv.getSigmaX2() + pv.getSigmaY2())); + cl[0].setSigmaZ2(pv.getSigmaZ2()); + cl[0].setSensorID(-1); + clArr[0] = &cl[0]; + } + + // collect track clusters into layer slots + int nCl = iTrack.getNClusters(); + for (int i = 0; i < nCl; i++) { + const auto& curClu = mITScl[mITSclRef[iTrack.getClusterEntry(i)]]; + int sens = curClu.getSensorID(); + int llr = geom->getLayer(sens); + if (clArr[1 + llr]) { + LOGP(fatal, "Cluster at lr {} was already assigned, old sens {}, new sens {}", llr, clArr[1 + llr]->getSensorID(), sens); + } + clArr[1 + llr] = &curClu; + } + std::array extrapOut, extrapInw; + chi2 = 0; + if (!align::doBidirRefit(iTrack, clArr, extrapOut, extrapInw, chi2, mParams->useStableRef, mParams->CorrType)) { + ++fitFail; + continue; + } + writeTree("idealRes", clArr, extrapOut, extrapInw, lbl); + + // Propagate MC truth to each cluster's (alpha, x) to get true track direction, + // then compute dy = dydx*h(u,v), dz = dzdx*h(u,v) - first Newton step. + const auto mcTrk = mMCReader.getTrack(lbl); + if (!mcTrk) { + continue; + } + std::array xyz{(float)mcTrk->GetStartVertexCoordinatesX(), (float)mcTrk->GetStartVertexCoordinatesY(), (float)mcTrk->GetStartVertexCoordinatesZ()}; + std::array pxyz{(float)mcTrk->GetStartVertexMomentumX(), (float)mcTrk->GetStartVertexMomentumY(), (float)mcTrk->GetStartVertexMomentumZ()}; + TParticlePDG* pPDG = TDatabasePDG::Instance()->GetParticle(mcTrk->GetPdgCode()); + if (!pPDG) { + continue; + } + o2::track::TrackPar mcPar(xyz, pxyz, TMath::Nint(pPDG->Charge() / 3), false); + + std::array misClArr; // shifted copies for up to 3 IT3 layers + std::array clArrMis{}; + for (int i = 0; i <= 7; i++) { + clArrMis[i] = clArr[i]; // PV and OB clusters stay the same + } + for (int iLay = 0; iLay < 3; ++iLay) { + if (!clArr[1 + iLay]) { + continue; + } + const auto& orig = *clArr[1 + iLay]; + const int sens = orig.getSensorID(); + if (!constants::detID::isDetITS3(sens)) { + continue; + } + const int sensorID = constants::detID::getSensorID(sens); + const int layerID = constants::detID::getDetID2Layer(sens); + + // compute h(u,v) at this cluster + const float r = orig.getX(); + const float gloX = r * std::cos(orig.alpha); + const float gloY = r * std::sin(orig.alpha); + const float gloZ = orig.getZ(); + auto [u, v] = computeUV(gloX, gloY, gloZ, sensorID, constants::radii[layerID]); + const double h = mDeformations[sensorID](u, v); + + // propagate MC track to cluster's tracking frame to get true slopes + auto mcAtCl = mcPar; + if (!mcAtCl.rotate(orig.alpha) || !prop->PropagateToXBxByBz(mcAtCl, orig.getX())) { + clArrMis[1 + iLay] = nullptr; // can't compute slopes -> drop cluster + continue; + } + const float snp = mcAtCl.getSnp(); + const float tgl = mcAtCl.getTgl(); + const float csci = 1.f / std::sqrt(1.f - (snp * snp)); + const float dydx = snp * csci; + const float dzdx = tgl * csci; + const float dy = dydx * static_cast(h); + const float dz = dzdx * static_cast(h); + + // check if shifted position is still within sensor acceptance + const float newGloY = (r * std::sin(orig.alpha)) + (dy * std::cos(orig.alpha)); + const float newGloX = (r * std::cos(orig.alpha)) - (dy * std::sin(orig.alpha)); + const float newGloZ = gloZ + dz; + auto [uNew, vNew] = computeUV(newGloX, newGloY, newGloZ, sensorID, constants::radii[layerID]); + if (std::abs(uNew) > 1. || std::abs(vNew) > 1.) { + clArrMis[1 + iLay] = nullptr; // shifted outside acceptance + continue; + } + + // create shifted copy: keep x=r (nominal), shift y and z + misClArr[iLay] = orig; + misClArr[iLay].setY(orig.getY() + dy); + misClArr[iLay].setZ(orig.getZ() + dz); + misClArr[iLay].setSigmaY2(orig.getSigmaY2() + (mParams->misAlgExtCY[sensorID] * mParams->misAlgExtCY[sensorID])); + misClArr[iLay].setSigmaZ2(orig.getSigmaZ2() + (mParams->misAlgExtCZ[sensorID] * mParams->misAlgExtCZ[sensorID])); + clArrMis[1 + iLay] = &misClArr[iLay]; + } + + // refit with shifted clusters + chi2 = 0; + if (!align::doBidirRefit(iTrack, clArrMis, extrapOut, extrapInw, chi2, mParams->useStableRef, mParams->CorrType)) { + ++fitFailMis; + ++goodRefit; // ideal still succeeded + continue; + } + writeTree("misRes", clArrMis, extrapOut, extrapInw, lbl); + + ++goodRefit; + } + + LOGP(info, "\tdoMisalignmentStudy: refitted {} out of {} tracks ({} !sel, {} !fit, {} !fitMis)", goodRefit, itsTracks.size(), notPassedSel, fitFail, fitFailMis); +} + +DataProcessorSpec getTrackingStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC, bool withPV) { std::vector outputs; auto dataRequest = std::make_shared(); @@ -820,7 +1197,9 @@ DataProcessorSpec getTrackingStudySpec(GTrackID::mask_t srcTracks, GTrackID::mas dataRequest->requestTracks(srcTracks, useMC); dataRequest->requestIT3Clusters(useMC); dataRequest->requestClusters(srcClusters, useMC); - dataRequest->requestPrimaryVertices(useMC); + if (withPV) { + dataRequest->requestPrimaryVertices(useMC); + } auto ggRequest = std::make_shared(false, // orbitResetTime true, // GRPECS=true true, // GRPLHCIF diff --git a/Detectors/Upgrades/ITS3/study/src/its3-tracking-study-workflow.cxx b/Detectors/Upgrades/ITS3/study/src/its3-tracking-study-workflow.cxx index e0a0aea1c368a..482ef2bb71e1d 100644 --- a/Detectors/Upgrades/ITS3/study/src/its3-tracking-study-workflow.cxx +++ b/Detectors/Upgrades/ITS3/study/src/its3-tracking-study-workflow.cxx @@ -39,6 +39,7 @@ void customize(std::vector& workflowOptions) {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation"}}, {"track-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of track sources to use"}}, {"cluster-sources", VariantType::String, "ITS,TRD,TOF", {"comma-separated list of cluster sources to use"}}, + {"without-pv", VariantType::Bool, false, {"do not use the PV as an additional fit point"}}, {"disable-root-input", VariantType::Bool, false, {"disable root-files input reader"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; o2::raw::HBFUtilsInitializer::addConfigOption(options); @@ -58,14 +59,17 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); auto useMC = !configcontext.options().get("disable-mc"); + auto usePV = !configcontext.options().get("without-pv"); 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); + if (usePV) { + o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); + } - specs.emplace_back(o2::its3::study::getTrackingStudySpec(srcTrc, srcCls, useMC)); + specs.emplace_back(o2::its3::study::getTrackingStudySpec(srcTrc, srcCls, useMC, usePV)); o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); diff --git a/Steer/DigitizerWorkflow/CMakeLists.txt b/Steer/DigitizerWorkflow/CMakeLists.txt index 6b31550c83636..10e8dc2b13995 100644 --- a/Steer/DigitizerWorkflow/CMakeLists.txt +++ b/Steer/DigitizerWorkflow/CMakeLists.txt @@ -68,7 +68,6 @@ o2_add_executable(digitizer-workflow O2::DetectorsRaw $<$:O2::ITS3Simulation> $<$:O2::ITS3Workflow> - $<$:O2::ITS3Align> $<$:O2::TRKSimulation> $<$:O2::TRKWorkflow>) diff --git a/Steer/DigitizerWorkflow/src/ITS3DigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/ITS3DigitizerSpec.cxx index 639203bdd6d38..60a1660288b9d 100644 --- a/Steer/DigitizerWorkflow/src/ITS3DigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/ITS3DigitizerSpec.cxx @@ -31,7 +31,6 @@ #include "ITSMFTBase/DPLAlpideParam.h" #include "ITSBase/GeometryTGeo.h" #include "ITS3Base/ITS3Params.h" -#include "ITS3Align/MisalignmentManager.h" #include #include @@ -79,11 +78,6 @@ class ITS3DPLDigitizerTask : BaseDPLDigitizer } updateTimeDependentParams(pc); - if (ITS3Params::Instance().applyMisalignmentHits) { - LOGP(info, "Applying misalignment to ITS3 Hits"); - o2::its3::align::MisalignmentManager::misalignHits(); - } - // read collision context from input auto context = pc.inputs().get("collisioncontext"); context->initSimChains(mID, mSimChains); From 5ca454afeb0f9689f14dd46c252e7965ddfd6bf5 Mon Sep 17 00:00:00 2001 From: shahoian Date: Fri, 27 Mar 2026 13:46:12 +0100 Subject: [PATCH 420/701] Proter time-slice calibration from stray TFs The TFs with bogus orbit, creation time or orbit(); static int errCount = 0; - if (tinfo.firstTForbit == -1U || tinfo.creation == -1) { - if (errCount++ < 5) { - LOGP(warn, "Ignoring dummy input with orbit {} and creation time {} in fillTFIDInfo", tinfo.firstTForbit, tinfo.creation); - } - return; + ti.fill(tinfo.firstTForbit, tinfo.tfCounter, tinfo.runNumber, tinfo.timeslice, tinfo.creation); + if (ti.discard && errCount++ < 5) { + LOGP(warn, "Bad input with orbit {}, TFcounter {} and creation time {} in fillTFIDInfo", tinfo.firstTForbit, tinfo.tfCounter, tinfo.creation); } - ti.firstTForbit = tinfo.firstTForbit; - ti.tfCounter = tinfo.tfCounter; - ti.runNumber = tinfo.runNumber; - ti.startTime = tinfo.timeslice; - ti.creation = tinfo.creation; + return; } diff --git a/Detectors/Calibration/include/DetectorsCalibration/TimeSlotCalibration.h b/Detectors/Calibration/include/DetectorsCalibration/TimeSlotCalibration.h index 87562afddf2ca..a8be3644619a4 100644 --- a/Detectors/Calibration/include/DetectorsCalibration/TimeSlotCalibration.h +++ b/Detectors/Calibration/include/DetectorsCalibration/TimeSlotCalibration.h @@ -281,6 +281,15 @@ template template bool TimeSlotCalibration::process(const DATA&... data) { + if (mCurrentTFInfo.discard) { + LOGP(warn, "Ignoring TF with discard flag on: Orbit {}, TFcounter {}, Run:{}, StartTime:{} CreationTime {}, ", + mCurrentTFInfo.firstTForbit, + mCurrentTFInfo.tfCounter, + mCurrentTFInfo.runNumber, + mCurrentTFInfo.startTime, + mCurrentTFInfo.creation); + return false; // ignore bad TF + } static bool firstCall = true; if (firstCall) { firstCall = false; diff --git a/Detectors/PHOS/calib/src/PHOSEnergyCalibDevice.cxx b/Detectors/PHOS/calib/src/PHOSEnergyCalibDevice.cxx index 979ca690c03e0..51956a4dbf96e 100644 --- a/Detectors/PHOS/calib/src/PHOSEnergyCalibDevice.cxx +++ b/Detectors/PHOS/calib/src/PHOSEnergyCalibDevice.cxx @@ -133,6 +133,7 @@ void PHOSEnergyCalibDevice::run(o2::framework::ProcessingContext& pc) LOG(warning) << "LHCPeriod is not available, using current month " << mLHCPeriod; } } + o2::base::TFIDInfoHelper::fillTFIDInfo(pc, mCalibrator->getCurrentTFInfo()); mCalibrator->process(tfcounter, clusters, cluelements, cluTR, mOutputDigits); fillOutputTree(); diff --git a/Detectors/PHOS/calib/src/PHOSL1phaseCalibDevice.cxx b/Detectors/PHOS/calib/src/PHOSL1phaseCalibDevice.cxx index c50a3faff4b01..baade755f2adf 100644 --- a/Detectors/PHOS/calib/src/PHOSL1phaseCalibDevice.cxx +++ b/Detectors/PHOS/calib/src/PHOSL1phaseCalibDevice.cxx @@ -30,7 +30,7 @@ void PHOSL1phaseCalibDevice::init(o2::framework::InitContext& ic) void PHOSL1phaseCalibDevice::run(o2::framework::ProcessingContext& pc) { - + o2::base::TFIDInfoHelper::fillTFIDInfo(pc, mCalibrator->getCurrentTFInfo()); o2::base::GRPGeomHelper::instance().checkUpdates(pc); auto crTime = pc.services().get().creation; if (mRunStartTime == 0 || crTime < mRunStartTime) { diff --git a/Detectors/PHOS/calib/src/PHOSRunbyrunCalibDevice.cxx b/Detectors/PHOS/calib/src/PHOSRunbyrunCalibDevice.cxx index cf767cb76c7ad..3c59ed8477940 100644 --- a/Detectors/PHOS/calib/src/PHOSRunbyrunCalibDevice.cxx +++ b/Detectors/PHOS/calib/src/PHOSRunbyrunCalibDevice.cxx @@ -54,6 +54,7 @@ void PHOSRunbyrunCalibDevice::run(o2::framework::ProcessingContext& pc) auto tfcounter = o2::header::get(pc.inputs().get("clusters").header)->tfCounter; auto clusters = pc.inputs().get>("clusters"); auto cluTR = pc.inputs().get>("cluTR"); + o2::base::TFIDInfoHelper::fillTFIDInfo(pc, mCalibrator->getCurrentTFInfo()); LOG(detail) << "Processing TF with " << clusters.size() << " clusters and " << cluTR.size() << " TriggerRecords"; mCalibrator->process(tfcounter, clusters, cluTR); } diff --git a/Detectors/PHOS/calib/src/PHOSTurnonCalibDevice.cxx b/Detectors/PHOS/calib/src/PHOSTurnonCalibDevice.cxx index 52ec8cef0b438..c2b04aea381a3 100644 --- a/Detectors/PHOS/calib/src/PHOSTurnonCalibDevice.cxx +++ b/Detectors/PHOS/calib/src/PHOSTurnonCalibDevice.cxx @@ -49,7 +49,7 @@ void PHOSTurnonCalibDevice::run(o2::framework::ProcessingContext& pc) auto cellTR = pc.inputs().get>("cellTriggerRecords"); auto clusters = pc.inputs().get>("clusters"); auto cluTR = pc.inputs().get>("clusterTriggerRecords"); - + o2::base::TFIDInfoHelper::fillTFIDInfo(pc, mCalibrator->getCurrentTFInfo()); LOG(detail) << "[PHOSTurnonCalibDevice - run] Received " << cells.size() << " cells and " << clusters.size() << " clusters, running calibration"; mCalibrator->process(tfcounter, cells, cellTR, clusters, cluTR); From 462abe14ddecb634a9bb597e3440d022d961a6b7 Mon Sep 17 00:00:00 2001 From: shahoian Date: Fri, 27 Mar 2026 13:54:38 +0100 Subject: [PATCH 421/701] Avoid premature loop termination in ITS vertexer --- Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx index 0c4ecb0b12df1..6d51f7bab5d36 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx @@ -370,7 +370,7 @@ void VertexerTraits::computeVertices(const int iteration) std::array tmpVertex{mTimeFrame->getTrackletClusters(rofId).back().getVertex()}; if (tmpVertex[0] * tmpVertex[0] + tmpVertex[1] * tmpVertex[1] > 4.f) { mTimeFrame->getTrackletClusters(rofId).pop_back(); - break; + continue; } usedTracklets[line1] = true; usedTracklets[line2] = true; From eeffa2752d17c70071bb1b656a0330ca56f293eb Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Fri, 27 Mar 2026 06:51:38 +0100 Subject: [PATCH 422/701] DPL: remove deprecated header / payload code Rest of the usecases removed. Abstract header / payload retrieval, with the idea that get_header / get_payload will work on any range of fair::mq::MessagePtrs. --- Framework/Core/include/Framework/MessageSet.h | 24 ------ Framework/Core/test/test_MessageSet.cxx | 78 +++++-------------- 2 files changed, 20 insertions(+), 82 deletions(-) diff --git a/Framework/Core/include/Framework/MessageSet.h b/Framework/Core/include/Framework/MessageSet.h index 166934238d647..323c0ad4608af 100644 --- a/Framework/Core/include/Framework/MessageSet.h +++ b/Framework/Core/include/Framework/MessageSet.h @@ -136,30 +136,6 @@ struct MessageSet { } } - fair::mq::MessagePtr& header(size_t partIndex) - { - return messages[messageMap[partIndex].position]; - } - - fair::mq::MessagePtr& payload(size_t partIndex, size_t payloadIndex = 0) - { - assert(partIndex < messageMap.size()); - assert(messageMap[partIndex].position + payloadIndex + 1 < messages.size()); - return messages[messageMap[partIndex].position + payloadIndex + 1]; - } - - fair::mq::MessagePtr const& header(size_t partIndex) const - { - return messages[messageMap[partIndex].position]; - } - - fair::mq::MessagePtr const& payload(size_t partIndex, size_t payloadIndex = 0) const - { - assert(partIndex < messageMap.size()); - assert(messageMap[partIndex].position + payloadIndex + 1 < messages.size()); - return messages[messageMap[partIndex].position + payloadIndex + 1]; - } - fair::mq::MessagePtr const& associatedHeader(size_t pos) const { return messages[messageMap[pairMap[pos].partIndex].position]; diff --git a/Framework/Core/test/test_MessageSet.cxx b/Framework/Core/test/test_MessageSet.cxx index aa7b49c1d1d3c..c6d5030cf5e33 100644 --- a/Framework/Core/test/test_MessageSet.cxx +++ b/Framework/Core/test/test_MessageSet.cxx @@ -45,12 +45,8 @@ TEST_CASE("MessageSet") REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); CHECK_THROWS((msgSet.messages | get_pair{1})); - // Validate pipe operators match old API - REQUIRE(&(msgSet.messages | get_header{0}) == &msgSet.header(0)); - REQUIRE(&(msgSet.messages | get_payload{0, 0}) == &msgSet.payload(0)); - REQUIRE((msgSet.messages | get_num_payloads{0}) == msgSet.messageMap[0].size); - REQUIRE((msgSet.messages | count_parts{}) == msgSet.messageMap.size()); - REQUIRE((msgSet.messages | count_payloads{}) == msgSet.pairMap.size()); + REQUIRE((msgSet.messages | get_num_payloads{0}) == 1); + REQUIRE((msgSet.messages | count_parts{}) == 1); } TEST_CASE("MessageSetWithFunction") @@ -76,11 +72,8 @@ TEST_CASE("MessageSetWithFunction") REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); CHECK_THROWS((msgSet.messages | get_pair{1})); - REQUIRE(&(msgSet.messages | get_header{0}) == &msgSet.header(0)); - REQUIRE(&(msgSet.messages | get_payload{0, 0}) == &msgSet.payload(0)); - REQUIRE((msgSet.messages | get_num_payloads{0}) == msgSet.messageMap[0].size); - REQUIRE((msgSet.messages | count_parts{}) == msgSet.messageMap.size()); - REQUIRE((msgSet.messages | count_payloads{}) == msgSet.pairMap.size()); + REQUIRE((msgSet.messages | get_num_payloads{0}) == 1); + REQUIRE((msgSet.messages | count_parts{}) == 1); } TEST_CASE("MessageSetWithMultipart") @@ -112,13 +105,8 @@ TEST_CASE("MessageSetWithMultipart") REQUIRE((msgSet.messages | get_pair{1}).headerIdx == 0); REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 2); CHECK_THROWS((msgSet.messages | get_pair{2})); - // Validate pipe operators match old API for multi-payload - REQUIRE(&(msgSet.messages | get_header{0}) == &msgSet.header(0)); - REQUIRE(&(msgSet.messages | get_payload{0, 0}) == &msgSet.payload(0, 0)); - REQUIRE(&(msgSet.messages | get_payload{0, 1}) == &msgSet.payload(0, 1)); - REQUIRE((msgSet.messages | get_num_payloads{0}) == msgSet.messageMap[0].size); - REQUIRE((msgSet.messages | count_parts{}) == msgSet.messageMap.size()); - REQUIRE((msgSet.messages | count_payloads{}) == msgSet.pairMap.size()); + REQUIRE((msgSet.messages | get_num_payloads{0}) == 2); + REQUIRE((msgSet.messages | count_parts{}) == 1); } TEST_CASE("MessageSetAddPartRef") @@ -190,18 +178,11 @@ TEST_CASE("MessageSetAddMultiple") REQUIRE((msgSet.messages | get_pair{2}).payloadIdx == 5); REQUIRE((msgSet.messages | get_pair{3}).headerIdx == 4); REQUIRE((msgSet.messages | get_pair{3}).payloadIdx == 6); - // Validate pipe operators match old API for mixed modes - for (size_t i = 0; i < 3; ++i) { - REQUIRE(&(msgSet.messages | get_header{i}) == &msgSet.header(i)); - REQUIRE(&(msgSet.messages | get_payload{i, 0}) == &msgSet.payload(i, 0)); - } - // Part 2 has a second payload (multi-payload with splitPayloadParts=2, splitPayloadIndex=2) - REQUIRE(&(msgSet.messages | get_payload{2, 1}) == &msgSet.payload(2, 1)); - for (size_t i = 0; i < 3; ++i) { - REQUIRE((msgSet.messages | get_num_payloads{i}) == msgSet.messageMap[i].size); - } - REQUIRE((msgSet.messages | count_parts{}) == msgSet.messageMap.size()); - REQUIRE((msgSet.messages | count_payloads{}) == msgSet.pairMap.size()); + REQUIRE((msgSet.messages | get_num_payloads{0}) == 1); + REQUIRE((msgSet.messages | get_num_payloads{1}) == 1); + REQUIRE((msgSet.messages | get_num_payloads{2}) == 2); + REQUIRE((msgSet.messages | count_parts{}) == 3); + REQUIRE((msgSet.messages | count_payloads{}) == 4); } TEST_CASE("GetHeaderPayloadOperators") @@ -251,13 +232,8 @@ TEST_CASE("GetHeaderPayloadOperators") REQUIRE(pl1.get() != nullptr); REQUIRE(pl1->GetSize() == 200); - // Validate pipe operators match old API - for (size_t i = 0; i < 2; ++i) { - REQUIRE(&(msgSet.messages | get_header{i}) == &msgSet.header(i)); - REQUIRE(&(msgSet.messages | get_payload{i, 0}) == &msgSet.payload(i, 0)); - } - REQUIRE((msgSet.messages | count_parts{}) == msgSet.messageMap.size()); - REQUIRE((msgSet.messages | count_payloads{}) == msgSet.pairMap.size()); + REQUIRE((msgSet.messages | count_parts{}) == 2); + REQUIRE((msgSet.messages | count_payloads{}) == 2); } TEST_CASE("GetHeaderPayloadMultiPayload") @@ -343,18 +319,10 @@ TEST_CASE("GetHeaderPayloadMultiPayload") // get_num_payloads for part 1 should be 3 REQUIRE((msgSet.messages | get_num_payloads{1}) == 3); - // Validate pipe operators match old API for multi-payload (header, pl, pl, pl) - REQUIRE(&(msgSet.messages | get_header{0}) == &msgSet.header(0)); - REQUIRE(&(msgSet.messages | get_header{1}) == &msgSet.header(1)); - REQUIRE(&(msgSet.messages | get_payload{0, 0}) == &msgSet.payload(0, 0)); - REQUIRE(&(msgSet.messages | get_payload{1, 0}) == &msgSet.payload(1, 0)); - REQUIRE(&(msgSet.messages | get_payload{1, 1}) == &msgSet.payload(1, 1)); - REQUIRE(&(msgSet.messages | get_payload{1, 2}) == &msgSet.payload(1, 2)); - for (size_t i = 0; i < 2; ++i) { - REQUIRE((msgSet.messages | get_num_payloads{i}) == msgSet.messageMap[i].size); - } - REQUIRE((msgSet.messages | count_parts{}) == msgSet.messageMap.size()); - REQUIRE((msgSet.messages | count_payloads{}) == msgSet.pairMap.size()); + REQUIRE((msgSet.messages | get_num_payloads{0}) == 1); + REQUIRE((msgSet.messages | get_num_payloads{1}) == 3); + REQUIRE((msgSet.messages | count_parts{}) == 2); + REQUIRE((msgSet.messages | count_payloads{}) == 4); } TEST_CASE("TraditionalSplitParts") @@ -418,14 +386,8 @@ TEST_CASE("TraditionalSplitParts") // get_num_payloads: each traditional split pair has 1 payload for (size_t i = 0; i < 3; ++i) { - REQUIRE((msgSet.messages | get_num_payloads{i}) == msgSet.messageMap[i].size); + REQUIRE((msgSet.messages | get_num_payloads{i}) == 1); } - - // Validate pipe operators match old MessageSet::header()/payload() API - for (size_t i = 0; i < 3; ++i) { - REQUIRE(&(msgSet.messages | get_header{i}) == &msgSet.header(i)); - REQUIRE(&(msgSet.messages | get_payload{i, 0}) == &msgSet.payload(i)); - } - REQUIRE((msgSet.messages | count_parts{}) == msgSet.messageMap.size()); - REQUIRE((msgSet.messages | count_payloads{}) == msgSet.pairMap.size()); + REQUIRE((msgSet.messages | count_parts{}) == 3); + REQUIRE((msgSet.messages | count_payloads{}) == 3); } From 77efa21500fdad2b21ac56559874b2f25cfd9284 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Fri, 27 Mar 2026 06:51:38 +0100 Subject: [PATCH 423/701] DPL: replace MessageSet::associateHeader / associatePayload --- Framework/Core/src/DataProcessingDevice.cxx | 5 +- Framework/Core/test/test_MessageSet.cxx | 64 +++++++++++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index 0fa70947bf18c..bb6502758a95a 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -2140,8 +2140,9 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v // sequence is the header message // - each part has one or more payload messages // - InputRecord provides all payloads as header-payload pair - auto const& headerMsg = currentSetOfInputs[i].associatedHeader(partindex); - auto const& payloadMsg = currentSetOfInputs[i].associatedPayload(partindex); + auto const indices = currentSetOfInputs[i].messages | get_pair{partindex}; + auto const& headerMsg = currentSetOfInputs[i].messages[indices.headerIdx]; + auto const& payloadMsg = currentSetOfInputs[i].messages[indices.payloadIdx]; headerptr = static_cast(headerMsg->GetData()); payloadptr = payloadMsg ? static_cast(payloadMsg->GetData()) : nullptr; payloadSize = payloadMsg ? payloadMsg->GetSize() : 0; diff --git a/Framework/Core/test/test_MessageSet.cxx b/Framework/Core/test/test_MessageSet.cxx index c6d5030cf5e33..8c9ed4a7cbf1c 100644 --- a/Framework/Core/test/test_MessageSet.cxx +++ b/Framework/Core/test/test_MessageSet.cxx @@ -47,6 +47,14 @@ TEST_CASE("MessageSet") CHECK_THROWS((msgSet.messages | get_pair{1})); REQUIRE((msgSet.messages | get_num_payloads{0}) == 1); REQUIRE((msgSet.messages | count_parts{}) == 1); + // messages: [hdr, pl] — one pair + REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); + REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); + for (size_t i = 0; i < msgSet.pairMap.size(); ++i) { + auto indices = msgSet.messages | get_pair{i}; + REQUIRE(&msgSet.messages[indices.headerIdx] == &msgSet.associatedHeader(i)); + REQUIRE(&msgSet.messages[indices.payloadIdx] == &msgSet.associatedPayload(i)); + } } TEST_CASE("MessageSetWithFunction") @@ -74,6 +82,11 @@ TEST_CASE("MessageSetWithFunction") CHECK_THROWS((msgSet.messages | get_pair{1})); REQUIRE((msgSet.messages | get_num_payloads{0}) == 1); REQUIRE((msgSet.messages | count_parts{}) == 1); + for (size_t i = 0; i < msgSet.pairMap.size(); ++i) { + auto indices = msgSet.messages | get_pair{i}; + REQUIRE(&msgSet.messages[indices.headerIdx] == &msgSet.associatedHeader(i)); + REQUIRE(&msgSet.messages[indices.payloadIdx] == &msgSet.associatedPayload(i)); + } } TEST_CASE("MessageSetWithMultipart") @@ -107,6 +120,16 @@ TEST_CASE("MessageSetWithMultipart") CHECK_THROWS((msgSet.messages | get_pair{2})); REQUIRE((msgSet.messages | get_num_payloads{0}) == 2); REQUIRE((msgSet.messages | count_parts{}) == 1); + // messages: [hdr, pl0, pl1] — one header, two payloads + REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); + REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); + REQUIRE((msgSet.messages | get_pair{1}).headerIdx == 0); + REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 2); + for (size_t i = 0; i < msgSet.pairMap.size(); ++i) { + auto indices = msgSet.messages | get_pair{i}; + REQUIRE(&msgSet.messages[indices.headerIdx] == &msgSet.associatedHeader(i)); + REQUIRE(&msgSet.messages[indices.payloadIdx] == &msgSet.associatedPayload(i)); + } } TEST_CASE("MessageSetAddPartRef") @@ -183,6 +206,11 @@ TEST_CASE("MessageSetAddMultiple") REQUIRE((msgSet.messages | get_num_payloads{2}) == 2); REQUIRE((msgSet.messages | count_parts{}) == 3); REQUIRE((msgSet.messages | count_payloads{}) == 4); + for (size_t i = 0; i < msgSet.pairMap.size(); ++i) { + auto indices = msgSet.messages | get_pair{i}; + REQUIRE(&msgSet.messages[indices.headerIdx] == &msgSet.associatedHeader(i)); + REQUIRE(&msgSet.messages[indices.payloadIdx] == &msgSet.associatedPayload(i)); + } } TEST_CASE("GetHeaderPayloadOperators") @@ -234,6 +262,16 @@ TEST_CASE("GetHeaderPayloadOperators") REQUIRE((msgSet.messages | count_parts{}) == 2); REQUIRE((msgSet.messages | count_payloads{}) == 2); + // messages: [hdr0, pl0, hdr1, pl1] — two standard pairs + REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); + REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); + REQUIRE((msgSet.messages | get_pair{1}).headerIdx == 2); + REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 3); + for (size_t i = 0; i < msgSet.pairMap.size(); ++i) { + auto indices = msgSet.messages | get_pair{i}; + REQUIRE(&msgSet.messages[indices.headerIdx] == &msgSet.associatedHeader(i)); + REQUIRE(&msgSet.messages[indices.payloadIdx] == &msgSet.associatedPayload(i)); + } } TEST_CASE("GetHeaderPayloadMultiPayload") @@ -323,6 +361,20 @@ TEST_CASE("GetHeaderPayloadMultiPayload") REQUIRE((msgSet.messages | get_num_payloads{1}) == 3); REQUIRE((msgSet.messages | count_parts{}) == 2); REQUIRE((msgSet.messages | count_payloads{}) == 4); + // messages: [hdr0, pl0, hdr1, pl1_0, pl1_1, pl1_2] + REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); + REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); + REQUIRE((msgSet.messages | get_pair{1}).headerIdx == 2); + REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 3); + REQUIRE((msgSet.messages | get_pair{2}).headerIdx == 2); + REQUIRE((msgSet.messages | get_pair{2}).payloadIdx == 4); + REQUIRE((msgSet.messages | get_pair{3}).headerIdx == 2); + REQUIRE((msgSet.messages | get_pair{3}).payloadIdx == 5); + for (size_t i = 0; i < msgSet.pairMap.size(); ++i) { + auto indices = msgSet.messages | get_pair{i}; + REQUIRE(&msgSet.messages[indices.headerIdx] == &msgSet.associatedHeader(i)); + REQUIRE(&msgSet.messages[indices.payloadIdx] == &msgSet.associatedPayload(i)); + } } TEST_CASE("TraditionalSplitParts") @@ -390,4 +442,16 @@ TEST_CASE("TraditionalSplitParts") } REQUIRE((msgSet.messages | count_parts{}) == 3); REQUIRE((msgSet.messages | count_payloads{}) == 3); + // messages: [hdr0, pl0, hdr1, pl1, hdr2, pl2] — three traditional split pairs + REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); + REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); + REQUIRE((msgSet.messages | get_pair{1}).headerIdx == 2); + REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 3); + REQUIRE((msgSet.messages | get_pair{2}).headerIdx == 4); + REQUIRE((msgSet.messages | get_pair{2}).payloadIdx == 5); + for (size_t i = 0; i < msgSet.pairMap.size(); ++i) { + auto indices = msgSet.messages | get_pair{i}; + REQUIRE(&msgSet.messages[indices.headerIdx] == &msgSet.associatedHeader(i)); + REQUIRE(&msgSet.messages[indices.payloadIdx] == &msgSet.associatedPayload(i)); + } } From 389aaf1627773d27a3fbfcfdbab8be6932f4efa0 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 25 Mar 2026 14:56:52 +0100 Subject: [PATCH 424/701] DPL: treat --ccdb-fetchers like --readers --- Framework/Core/src/runDataProcessing.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/Framework/Core/src/runDataProcessing.cxx b/Framework/Core/src/runDataProcessing.cxx index 815fce47544d0..98cbf70370c3d 100644 --- a/Framework/Core/src/runDataProcessing.cxx +++ b/Framework/Core/src/runDataProcessing.cxx @@ -2048,6 +2048,7 @@ int runStateMachine(DataProcessorSpecs const& workflow, "--driver-client-backend", "--fairmq-ipc-prefix", "--readers", + "--ccdb-fetchers", "--resources-monitoring", "--resources-monitoring-file", "--resources-monitoring-dump-interval", From 2fd34a0af1eef89c33571e21b4d9ad7f3c87a92b Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sat, 28 Mar 2026 14:57:49 +0100 Subject: [PATCH 425/701] DPL: exponential back-off for missing resources. --- Framework/Core/src/DataProcessingDevice.cxx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index bb6502758a95a..31b7b02172af7 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -1377,6 +1377,7 @@ void DataProcessingDevice::Run() std::atomic numberOfUnscheduledSinceLastScheduled = 0; std::atomic numberOfUnscheduled = 0; std::atomic numberOfScheduled = 0; + std::atomic nextWarnAt = 1; }; static SchedulingStats schedulingStats; O2_SIGNPOST_ID_GENERATE(sid, scheduling); @@ -1387,6 +1388,7 @@ void DataProcessingDevice::Run() schedulingStats.lastScheduled = uv_now(state.loop); schedulingStats.numberOfScheduled++; schedulingStats.numberOfUnscheduledSinceLastScheduled = 0; + schedulingStats.nextWarnAt = 1; O2_SIGNPOST_EVENT_EMIT(scheduling, sid, "Run", "Enough resources to schedule computation on stream %d", streamRef.index); if (dplEnableMultithreding) [[unlikely]] { stream.task = &handle; @@ -1396,12 +1398,12 @@ void DataProcessingDevice::Run() run_completion(&handle, 0); } } else { - if (schedulingStats.numberOfUnscheduledSinceLastScheduled > 100 || - (uv_now(state.loop) - schedulingStats.lastScheduled) > 30000) { + if (schedulingStats.numberOfUnscheduledSinceLastScheduled >= schedulingStats.nextWarnAt) { 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()); + schedulingStats.nextWarnAt = schedulingStats.nextWarnAt * 2; } 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.", From db1ede319f113e56c5fd0fb4131ac1ee276f1b52 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sat, 28 Mar 2026 22:49:56 +0100 Subject: [PATCH 426/701] Revert "DPL: Better detection for injected workflows (fixed) (#15202)" This reverts commit 87b9775293c9734b0be767feb5915e614560a05c. --- Framework/Core/src/ArrowSupport.cxx | 8 +-- Framework/Core/src/WorkflowHelpers.cxx | 15 +--- run/o2sim_hepmc_publisher.cxx | 94 +++++++++++++------------- run/o2sim_kine_publisher.cxx | 3 +- run/o2sim_mctracks_to_aod.cxx | 12 ++-- 5 files changed, 58 insertions(+), 74 deletions(-) diff --git a/Framework/Core/src/ArrowSupport.cxx b/Framework/Core/src/ArrowSupport.cxx index b701ba5f8e01c..c5cc021a53478 100644 --- a/Framework/Core/src/ArrowSupport.cxx +++ b/Framework/Core/src/ArrowSupport.cxx @@ -680,12 +680,8 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() workflow.erase(reader); } else { // load reader algorithm before deployment - auto tfnsource = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { - return !spec.name.starts_with("internal-dpl-aod-reader") && std::ranges::any_of(spec.outputs, [](OutputSpec const& output) { - return DataSpecUtils::match(output, "TFN", "TFNumber", 0); - }); - }); - if (tfnsource == workflow.end()) { // add normal reader algorithm only if no on-the-fly generator is injected + 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 } diff --git a/Framework/Core/src/WorkflowHelpers.cxx b/Framework/Core/src/WorkflowHelpers.cxx index 2ef3df9426fde..abe566e239618 100644 --- a/Framework/Core/src/WorkflowHelpers.cxx +++ b/Framework/Core/src/WorkflowHelpers.cxx @@ -411,17 +411,13 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext // add the reader if (aodReader.outputs.empty() == false) { - auto tfnsource = std::ranges::find_if(workflow, [](DataProcessorSpec const& spec) { - return std::ranges::any_of(spec.outputs, [](OutputSpec const& output) { - return DataSpecUtils::match(output, "TFN", "TFNumber", 0); - }); - }); - if (tfnsource == workflow.end()) { + auto mctracks2aod = std::ranges::find_if(workflow, [](auto const& x) { return x.name == "mctracks-to-aod"; }); + if (mctracks2aod == workflow.end()) { // add normal reader aodReader.outputs.emplace_back(OutputSpec{"TFN", "TFNumber"}); aodReader.outputs.emplace_back(OutputSpec{"TFF", "TFFilename"}); } else { - // AODs are being injected the tfnsource is the entry point, add error-handler reader + // AODs are being injected on-the-fly, add error-handler reader aodReader.algorithm = AlgorithmSpec{ adaptStateful( [](DeviceSpec const& spec) { @@ -704,11 +700,6 @@ void WorkflowHelpers::injectAODWriter(WorkflowSpec& workflow, ConfigContext cons return DataSpecUtils::partialMatch(spec, o2::header::DataOrigin("TFN")); }); dec.isDangling[std::distance(dec.outputsInputs.begin(), it)] = false; - - it = std::find_if(dec.outputsInputs.begin(), dec.outputsInputs.end(), [](InputSpec const& spec) -> bool { - return DataSpecUtils::partialMatch(spec, o2::header::DataOrigin("TFF")); - }); - dec.isDangling[std::distance(dec.outputsInputs.begin(), it)] = false; } } diff --git a/run/o2sim_hepmc_publisher.cxx b/run/o2sim_hepmc_publisher.cxx index f255b4a3a4f62..bf40abacb134f 100644 --- a/run/o2sim_hepmc_publisher.cxx +++ b/run/o2sim_hepmc_publisher.cxx @@ -37,9 +37,7 @@ struct O2simHepmcPublisher { int tfCounter = 0; std::shared_ptr hepMCReader; bool eos = false; - - std::vector*> mctracks_vector; - std::vector mcheader_vector; + std::vector mcTracks; void init(o2::framework::InitContext& /*ic*/) { @@ -52,19 +50,13 @@ struct O2simHepmcPublisher { LOGP(fatal, "Cannot open HEPMC kine file {}", (std::string)hepmcFileName); } // allocate the memory upfront to prevent reallocations later - mctracks_vector.reserve(aggregate); - mcheader_vector.reserve(aggregate); + mcTracks.reserve(1e3 * aggregate); } void run(o2::framework::ProcessingContext& pc) { HepMC3::GenEvent event; - auto batch = maxEvents > 0 ? std::min((int)aggregate, (int)maxEvents - eventCounter) : (int)aggregate; - for (auto i = 0; i < batch; ++i) { - mctracks_vector.push_back(&pc.outputs().make>(Output{"MC", "MCTRACKS", 0})); - auto& mctracks = mctracks_vector.back(); - mcheader_vector.push_back(&pc.outputs().make(Output{"MC", "MCHEADER", 0})); - auto& mcheader = mcheader_vector.back(); + for (auto i = 0; i < (int)aggregate; ++i) { // read next entry hepMCReader->read_event(event); if (hepMCReader->failed()) { @@ -74,60 +66,61 @@ struct O2simHepmcPublisher { } // create O2 MCHeader and MCtracks vector out of HEPMC event - mcheader->SetEventID(event.event_number()); - mcheader->SetVertex(event.event_pos().px(), event.event_pos().py(), event.event_pos().pz()); + o2::dataformats::MCEventHeader mcHeader; + mcHeader.SetEventID(event.event_number()); + mcHeader.SetVertex(event.event_pos().px(), event.event_pos().py(), event.event_pos().pz()); auto xsecInfo = event.cross_section(); if (xsecInfo != nullptr) { - mcheader->putInfo(MCInfoKeys::acceptedEvents, (uint64_t)xsecInfo->get_accepted_events()); - mcheader->putInfo(MCInfoKeys::attemptedEvents, (uint64_t)xsecInfo->get_attempted_events()); - mcheader->putInfo(MCInfoKeys::xSection, (float)xsecInfo->xsec()); - mcheader->putInfo(MCInfoKeys::xSectionError, (float)xsecInfo->xsec_err()); + mcHeader.putInfo(MCInfoKeys::acceptedEvents, (uint64_t)xsecInfo->get_accepted_events()); + mcHeader.putInfo(MCInfoKeys::attemptedEvents, (uint64_t)xsecInfo->get_attempted_events()); + mcHeader.putInfo(MCInfoKeys::xSection, (float)xsecInfo->xsec()); + mcHeader.putInfo(MCInfoKeys::xSectionError, (float)xsecInfo->xsec_err()); } auto scale = event.attribute(MCInfoKeys::eventScale); if (scale != nullptr) { - mcheader->putInfo(MCInfoKeys::eventScale, (float)scale->value()); + mcHeader.putInfo(MCInfoKeys::eventScale, (float)scale->value()); } auto nMPI = event.attribute(MCInfoKeys::mpi); if (nMPI != nullptr) { - mcheader->putInfo(MCInfoKeys::mpi, nMPI->value()); + mcHeader.putInfo(MCInfoKeys::mpi, nMPI->value()); } auto sid = event.attribute(MCInfoKeys::processCode); auto scode = event.attribute(MCInfoKeys::processID); // default pythia8 hepmc3 interface uses signal_process_id if (sid != nullptr) { - mcheader->putInfo(MCInfoKeys::processCode, sid->value()); + mcHeader.putInfo(MCInfoKeys::processCode, sid->value()); } else if (scode != nullptr) { - mcheader->putInfo(MCInfoKeys::processCode, scode->value()); + mcHeader.putInfo(MCInfoKeys::processCode, scode->value()); } auto pdfInfo = event.pdf_info(); if (pdfInfo != nullptr) { - mcheader->putInfo(MCInfoKeys::pdfParton1Id, pdfInfo->parton_id[0]); - mcheader->putInfo(MCInfoKeys::pdfParton2Id, pdfInfo->parton_id[1]); - mcheader->putInfo(MCInfoKeys::pdfCode1, pdfInfo->pdf_id[0]); - mcheader->putInfo(MCInfoKeys::pdfCode2, pdfInfo->pdf_id[1]); - mcheader->putInfo(MCInfoKeys::pdfX1, (float)pdfInfo->x[0]); - mcheader->putInfo(MCInfoKeys::pdfX2, (float)pdfInfo->x[1]); - mcheader->putInfo(MCInfoKeys::pdfScale, (float)pdfInfo->scale); - mcheader->putInfo(MCInfoKeys::pdfXF1, (float)pdfInfo->xf[0]); - mcheader->putInfo(MCInfoKeys::pdfXF2, (float)pdfInfo->xf[1]); + mcHeader.putInfo(MCInfoKeys::pdfParton1Id, pdfInfo->parton_id[0]); + mcHeader.putInfo(MCInfoKeys::pdfParton2Id, pdfInfo->parton_id[1]); + mcHeader.putInfo(MCInfoKeys::pdfCode1, pdfInfo->pdf_id[0]); + mcHeader.putInfo(MCInfoKeys::pdfCode2, pdfInfo->pdf_id[1]); + mcHeader.putInfo(MCInfoKeys::pdfX1, (float)pdfInfo->x[0]); + mcHeader.putInfo(MCInfoKeys::pdfX2, (float)pdfInfo->x[1]); + mcHeader.putInfo(MCInfoKeys::pdfScale, (float)pdfInfo->scale); + mcHeader.putInfo(MCInfoKeys::pdfXF1, (float)pdfInfo->xf[0]); + mcHeader.putInfo(MCInfoKeys::pdfXF2, (float)pdfInfo->xf[1]); } auto heavyIon = event.heavy_ion(); if (heavyIon != nullptr) { - mcheader->putInfo(MCInfoKeys::nCollHard, heavyIon->Ncoll_hard); - mcheader->putInfo(MCInfoKeys::nPartProjectile, heavyIon->Npart_proj); - mcheader->putInfo(MCInfoKeys::nPartTarget, heavyIon->Npart_targ); - mcheader->putInfo(MCInfoKeys::nColl, heavyIon->Ncoll); - mcheader->putInfo(MCInfoKeys::nCollNNWounded, heavyIon->N_Nwounded_collisions); - mcheader->putInfo(MCInfoKeys::nCollNWoundedN, heavyIon->Nwounded_N_collisions); - mcheader->putInfo(MCInfoKeys::nCollNWoundedNwounded, heavyIon->Nwounded_Nwounded_collisions); - mcheader->putInfo(MCInfoKeys::nSpecProjectileNeutron, heavyIon->Nspec_proj_n); - mcheader->putInfo(MCInfoKeys::nSpecProjectileProton, heavyIon->Nspec_proj_p); - mcheader->putInfo(MCInfoKeys::nSpecTargetNeutron, heavyIon->Nspec_targ_n); - mcheader->putInfo(MCInfoKeys::nSpecTargetProton, heavyIon->Nspec_targ_p); - mcheader->putInfo(MCInfoKeys::impactParameter, (float)heavyIon->impact_parameter); - mcheader->putInfo(MCInfoKeys::planeAngle, (float)heavyIon->event_plane_angle); - mcheader->putInfo("eccentricity", (float)heavyIon->eccentricity); - mcheader->putInfo(MCInfoKeys::sigmaInelNN, (float)heavyIon->sigma_inel_NN); - mcheader->putInfo(MCInfoKeys::centrality, (float)heavyIon->centrality); + mcHeader.putInfo(MCInfoKeys::nCollHard, heavyIon->Ncoll_hard); + mcHeader.putInfo(MCInfoKeys::nPartProjectile, heavyIon->Npart_proj); + mcHeader.putInfo(MCInfoKeys::nPartTarget, heavyIon->Npart_targ); + mcHeader.putInfo(MCInfoKeys::nColl, heavyIon->Ncoll); + mcHeader.putInfo(MCInfoKeys::nCollNNWounded, heavyIon->N_Nwounded_collisions); + mcHeader.putInfo(MCInfoKeys::nCollNWoundedN, heavyIon->Nwounded_N_collisions); + mcHeader.putInfo(MCInfoKeys::nCollNWoundedNwounded, heavyIon->Nwounded_Nwounded_collisions); + mcHeader.putInfo(MCInfoKeys::nSpecProjectileNeutron, heavyIon->Nspec_proj_n); + mcHeader.putInfo(MCInfoKeys::nSpecProjectileProton, heavyIon->Nspec_proj_p); + mcHeader.putInfo(MCInfoKeys::nSpecTargetNeutron, heavyIon->Nspec_targ_n); + mcHeader.putInfo(MCInfoKeys::nSpecTargetProton, heavyIon->Nspec_targ_p); + mcHeader.putInfo(MCInfoKeys::impactParameter, (float)heavyIon->impact_parameter); + mcHeader.putInfo(MCInfoKeys::planeAngle, (float)heavyIon->event_plane_angle); + mcHeader.putInfo("eccentricity", (float)heavyIon->eccentricity); + mcHeader.putInfo(MCInfoKeys::sigmaInelNN, (float)heavyIon->sigma_inel_NN); + mcHeader.putInfo(MCInfoKeys::centrality, (float)heavyIon->centrality); } auto particles = event.particles(); @@ -138,7 +131,7 @@ struct O2simHepmcPublisher { auto has_children = children.size() > 0; auto p = particle->momentum(); auto v = particle->production_vertex(); - mctracks->emplace_back( + mcTracks.emplace_back( particle->pid(), has_parents ? parents.front()->id() : -1, has_parents ? parents.back()->id() : -1, has_children ? children.front()->id() : -1, has_children ? children.back()->id() : -1, @@ -146,13 +139,18 @@ struct O2simHepmcPublisher { v->position().x(), v->position().y(), v->position().z(), v->position().t(), 0); } + + // add to the message + pc.outputs().snapshot(Output{"MC", "MCHEADER", 0}, mcHeader); + pc.outputs().snapshot(Output{"MC", "MCTRACKS", 0}, mcTracks); + mcTracks.clear(); ++eventCounter; } // report number of TFs injected for the rate limiter to work ++tfCounter; pc.services().get().send(o2::monitoring::Metric{(uint64_t)tfCounter, "df-sent"}.addTag(o2::monitoring::tags::Key::Subsystem, o2::monitoring::tags::Value::DPL)); - if (eos || (maxEvents > 0 && eventCounter >= maxEvents)) { + if (eos || (maxEvents > 0 && eventCounter == maxEvents)) { pc.services().get().endOfStream(); pc.services().get().readyToQuit(QuitRequest::Me); } diff --git a/run/o2sim_kine_publisher.cxx b/run/o2sim_kine_publisher.cxx index 5920743c3fafa..cfbea6ae02a5f 100644 --- a/run/o2sim_kine_publisher.cxx +++ b/run/o2sim_kine_publisher.cxx @@ -40,8 +40,7 @@ struct O2simKinePublisher { void run(o2::framework::ProcessingContext& pc) { - auto batch = std::min((int)aggregate, nEvents - eventCounter); - for (auto i = 0; i < batch; ++i) { + for (auto i = 0; i < std::min((int)aggregate, nEvents - eventCounter); ++i) { auto mcevent = mcKinReader->getMCEventHeader(0, eventCounter); auto mctracks = mcKinReader->getTracks(0, eventCounter); pc.outputs().snapshot(Output{"MC", "MCHEADER", 0}, mcevent); diff --git a/run/o2sim_mctracks_to_aod.cxx b/run/o2sim_mctracks_to_aod.cxx index d95a3b33cc38f..124e8aa7b3e42 100644 --- a/run/o2sim_mctracks_to_aod.cxx +++ b/run/o2sim_mctracks_to_aod.cxx @@ -70,7 +70,7 @@ struct MctracksToAod { /** Run the conversion */ void run(o2::framework::ProcessingContext& pc) { - LOG(detail) << "=== Running extended MC AOD exporter ==="; + LOG(debug) << "=== Running extended MC AOD exporter ==="; using namespace o2::aodmchelpers; using McHeader = o2::dataformats::MCEventHeader; using McTrack = o2::MCTrack; @@ -94,13 +94,13 @@ struct MctracksToAod { // TODO: include BC simulation auto bcCounter = 0UL; size_t offset = 0; - LOG(detail) << "--- Loop over " << nParts << " parts ---"; + LOG(debug) << "--- Loop over " << nParts << " parts ---"; for (auto i = 0U; i < nParts; ++i) { auto record = mSampler.generateCollisionTime(); auto header = pc.inputs().get("mcheader", i); auto tracks = pc.inputs().get("mctracks", i); - LOG(detail) << "Updating collision table"; + LOG(debug) << "Updating collision table"; auto genID = updateMCCollisions(mCollisions.cursor, bcCounter, record.timeInBCNS * 1.e-3, @@ -108,12 +108,12 @@ struct MctracksToAod { 0, i); - LOG(detail) << "Updating HepMC tables"; + LOG(debug) << "Updating HepMC tables"; updateHepMCXSection(mXSections.cursor, bcCounter, genID, *header); updateHepMCPdfInfo(mPdfInfos.cursor, bcCounter, genID, *header); updateHepMCHeavyIon(mHeavyIons.cursor, bcCounter, genID, *header); - LOG(detail) << "Updating particles table"; + LOG(debug) << "Updating particles table"; TrackToIndex preselect; offset = updateParticles(mParticles.cursor, bcCounter, @@ -123,7 +123,7 @@ struct MctracksToAod { (bool)filt, false); - LOG(detail) << "Increment BC counter"; + LOG(debug) << "Increment BC counter"; bcCounter++; } From 757173be61c6ccf7d962476c222c60c64be2f785 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 29 Mar 2026 08:40:38 +0200 Subject: [PATCH 427/701] DPL: drop MessageSet::associateHeader / associatePayload (#15234) --- Framework/Core/include/Framework/MessageSet.h | 11 ------ Framework/Core/test/test_MessageSet.cxx | 35 ------------------- 2 files changed, 46 deletions(-) diff --git a/Framework/Core/include/Framework/MessageSet.h b/Framework/Core/include/Framework/MessageSet.h index 323c0ad4608af..6ccabd8c5ffb5 100644 --- a/Framework/Core/include/Framework/MessageSet.h +++ b/Framework/Core/include/Framework/MessageSet.h @@ -136,17 +136,6 @@ struct MessageSet { } } - fair::mq::MessagePtr const& associatedHeader(size_t pos) const - { - return messages[messageMap[pairMap[pos].partIndex].position]; - } - - fair::mq::MessagePtr const& associatedPayload(size_t pos) const - { - auto partIndex = pairMap[pos].partIndex; - auto payloadIndex = pairMap[pos].payloadIndex; - return messages[messageMap[partIndex].position + payloadIndex + 1]; - } }; } // namespace o2::framework diff --git a/Framework/Core/test/test_MessageSet.cxx b/Framework/Core/test/test_MessageSet.cxx index 8c9ed4a7cbf1c..bfbffb166da8d 100644 --- a/Framework/Core/test/test_MessageSet.cxx +++ b/Framework/Core/test/test_MessageSet.cxx @@ -50,11 +50,6 @@ TEST_CASE("MessageSet") // messages: [hdr, pl] — one pair REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); - for (size_t i = 0; i < msgSet.pairMap.size(); ++i) { - auto indices = msgSet.messages | get_pair{i}; - REQUIRE(&msgSet.messages[indices.headerIdx] == &msgSet.associatedHeader(i)); - REQUIRE(&msgSet.messages[indices.payloadIdx] == &msgSet.associatedPayload(i)); - } } TEST_CASE("MessageSetWithFunction") @@ -82,11 +77,6 @@ TEST_CASE("MessageSetWithFunction") CHECK_THROWS((msgSet.messages | get_pair{1})); REQUIRE((msgSet.messages | get_num_payloads{0}) == 1); REQUIRE((msgSet.messages | count_parts{}) == 1); - for (size_t i = 0; i < msgSet.pairMap.size(); ++i) { - auto indices = msgSet.messages | get_pair{i}; - REQUIRE(&msgSet.messages[indices.headerIdx] == &msgSet.associatedHeader(i)); - REQUIRE(&msgSet.messages[indices.payloadIdx] == &msgSet.associatedPayload(i)); - } } TEST_CASE("MessageSetWithMultipart") @@ -125,11 +115,6 @@ TEST_CASE("MessageSetWithMultipart") REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); REQUIRE((msgSet.messages | get_pair{1}).headerIdx == 0); REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 2); - for (size_t i = 0; i < msgSet.pairMap.size(); ++i) { - auto indices = msgSet.messages | get_pair{i}; - REQUIRE(&msgSet.messages[indices.headerIdx] == &msgSet.associatedHeader(i)); - REQUIRE(&msgSet.messages[indices.payloadIdx] == &msgSet.associatedPayload(i)); - } } TEST_CASE("MessageSetAddPartRef") @@ -206,11 +191,6 @@ TEST_CASE("MessageSetAddMultiple") REQUIRE((msgSet.messages | get_num_payloads{2}) == 2); REQUIRE((msgSet.messages | count_parts{}) == 3); REQUIRE((msgSet.messages | count_payloads{}) == 4); - for (size_t i = 0; i < msgSet.pairMap.size(); ++i) { - auto indices = msgSet.messages | get_pair{i}; - REQUIRE(&msgSet.messages[indices.headerIdx] == &msgSet.associatedHeader(i)); - REQUIRE(&msgSet.messages[indices.payloadIdx] == &msgSet.associatedPayload(i)); - } } TEST_CASE("GetHeaderPayloadOperators") @@ -267,11 +247,6 @@ TEST_CASE("GetHeaderPayloadOperators") REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); REQUIRE((msgSet.messages | get_pair{1}).headerIdx == 2); REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 3); - for (size_t i = 0; i < msgSet.pairMap.size(); ++i) { - auto indices = msgSet.messages | get_pair{i}; - REQUIRE(&msgSet.messages[indices.headerIdx] == &msgSet.associatedHeader(i)); - REQUIRE(&msgSet.messages[indices.payloadIdx] == &msgSet.associatedPayload(i)); - } } TEST_CASE("GetHeaderPayloadMultiPayload") @@ -370,11 +345,6 @@ TEST_CASE("GetHeaderPayloadMultiPayload") REQUIRE((msgSet.messages | get_pair{2}).payloadIdx == 4); REQUIRE((msgSet.messages | get_pair{3}).headerIdx == 2); REQUIRE((msgSet.messages | get_pair{3}).payloadIdx == 5); - for (size_t i = 0; i < msgSet.pairMap.size(); ++i) { - auto indices = msgSet.messages | get_pair{i}; - REQUIRE(&msgSet.messages[indices.headerIdx] == &msgSet.associatedHeader(i)); - REQUIRE(&msgSet.messages[indices.payloadIdx] == &msgSet.associatedPayload(i)); - } } TEST_CASE("TraditionalSplitParts") @@ -449,9 +419,4 @@ TEST_CASE("TraditionalSplitParts") REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 3); REQUIRE((msgSet.messages | get_pair{2}).headerIdx == 4); REQUIRE((msgSet.messages | get_pair{2}).payloadIdx == 5); - for (size_t i = 0; i < msgSet.pairMap.size(); ++i) { - auto indices = msgSet.messages | get_pair{i}; - REQUIRE(&msgSet.messages[indices.headerIdx] == &msgSet.associatedHeader(i)); - REQUIRE(&msgSet.messages[indices.payloadIdx] == &msgSet.associatedPayload(i)); - } } From cafa4ce8912a88addbce3ad515b6d215d3377923 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 29 Mar 2026 14:46:13 +0200 Subject: [PATCH 428/701] DPL: get rid of MessageSet::pairMap (#15237) Everything calculated on the fly --- Framework/Core/include/Framework/MessageSet.h | 14 +++----------- 1 file changed, 3 insertions(+), 11 deletions(-) diff --git a/Framework/Core/include/Framework/MessageSet.h b/Framework/Core/include/Framework/MessageSet.h index 6ccabd8c5ffb5..bc718ca82714c 100644 --- a/Framework/Core/include/Framework/MessageSet.h +++ b/Framework/Core/include/Framework/MessageSet.h @@ -50,22 +50,21 @@ struct MessageSet { // payload index within the O2 message size_t payloadIndex = 0; }; - std::vector pairMap; MessageSet() - : messages(), messageMap(), pairMap() + : messages(), messageMap() { } template MessageSet(F getter, size_t size) - : messages(), messageMap(), pairMap() + : messages(), messageMap() { add(std::forward(getter), size); } MessageSet(MessageSet&& other) - : messages(std::move(other.messages)), messageMap(std::move(other.messageMap)), pairMap(std::move(other.pairMap)) + : messages(std::move(other.messages)), messageMap(std::move(other.messageMap)) { other.clear(); } @@ -77,7 +76,6 @@ struct MessageSet { } messages = std::move(other.messages); messageMap = std::move(other.messageMap); - pairMap = std::move(other.pairMap); other.clear(); return *this; } @@ -99,7 +97,6 @@ struct MessageSet { { messages.clear(); messageMap.clear(); - pairMap.clear(); } // this is more or less legacy @@ -116,7 +113,6 @@ struct MessageSet { // add content of the part ref void add(PartRef&& ref) { - pairMap.emplace_back(messageMap.size(), 0); messageMap.emplace_back(messages.size(), 1); messages.emplace_back(std::move(ref.header)); messages.emplace_back(std::move(ref.payload)); @@ -126,12 +122,8 @@ struct MessageSet { template void add(F getter, size_t size) { - auto partid = messageMap.size(); messageMap.emplace_back(messages.size(), size - 1); for (size_t i = 0; i < size; ++i) { - if (i > 0) { - pairMap.emplace_back(partid, i - 1); - } messages.emplace_back(std::move(getter(i))); } } From e22690985d4b374e3b82770994e92779bb63b00b Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 29 Mar 2026 14:50:58 +0200 Subject: [PATCH 429/701] DPL: drop MessageSet::messageMap --- Framework/Core/include/Framework/MessageSet.h | 15 +++------------ 1 file changed, 3 insertions(+), 12 deletions(-) diff --git a/Framework/Core/include/Framework/MessageSet.h b/Framework/Core/include/Framework/MessageSet.h index bc718ca82714c..9cc11f0c35ee0 100644 --- a/Framework/Core/include/Framework/MessageSet.h +++ b/Framework/Core/include/Framework/MessageSet.h @@ -38,11 +38,6 @@ struct MessageSet { }; // linear storage of messages std::vector messages; - // message map describes O2 messages consisting of a header message and - // payload message(s), index describes position in the linear storage - std::vector messageMap; - // pair map describes all messages in one sequence of header-payload pairs and - // where in the message index the associated header and payload can be found struct PairMapping { PairMapping(size_t partId, size_t payloadId) : partIndex(partId), payloadIndex(payloadId) {} // O2 message where the pair is located in @@ -52,19 +47,19 @@ struct MessageSet { }; MessageSet() - : messages(), messageMap() + : messages() { } template MessageSet(F getter, size_t size) - : messages(), messageMap() + : messages() { add(std::forward(getter), size); } MessageSet(MessageSet&& other) - : messages(std::move(other.messages)), messageMap(std::move(other.messageMap)) + : messages(std::move(other.messages)) { other.clear(); } @@ -75,7 +70,6 @@ struct MessageSet { return *this; } messages = std::move(other.messages); - messageMap = std::move(other.messageMap); other.clear(); return *this; } @@ -96,7 +90,6 @@ struct MessageSet { void clear() { messages.clear(); - messageMap.clear(); } // this is more or less legacy @@ -113,7 +106,6 @@ struct MessageSet { // add content of the part ref void add(PartRef&& ref) { - messageMap.emplace_back(messages.size(), 1); messages.emplace_back(std::move(ref.header)); messages.emplace_back(std::move(ref.payload)); } @@ -122,7 +114,6 @@ struct MessageSet { template void add(F getter, size_t size) { - messageMap.emplace_back(messages.size(), size - 1); for (size_t i = 0; i < size; ++i) { messages.emplace_back(std::move(getter(i))); } From 4d74d840a04adc3099486ab53f5b0ae097bd26c3 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 29 Mar 2026 14:50:58 +0200 Subject: [PATCH 430/701] DPL: drop get number of pairs --- Framework/Core/include/Framework/MessageSet.h | 6 ------ Framework/Core/src/DataProcessingDevice.cxx | 4 ++-- 2 files changed, 2 insertions(+), 8 deletions(-) diff --git a/Framework/Core/include/Framework/MessageSet.h b/Framework/Core/include/Framework/MessageSet.h index 9cc11f0c35ee0..440b98514eb51 100644 --- a/Framework/Core/include/Framework/MessageSet.h +++ b/Framework/Core/include/Framework/MessageSet.h @@ -74,12 +74,6 @@ struct MessageSet { return *this; } - /// get number of header-payload pairs - [[nodiscard]] size_t getNumberOfPairs() const - { - return messages | count_payloads{}; - } - /// get number of payloads for an in-flight message [[nodiscard]] size_t getNumberOfPayloads(size_t mi) const { diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index 31b7b02172af7..9b6395a02916a 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -2133,7 +2133,7 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v currentSetOfInputs = relayer.consumeExistingInputsForTimeslice(slot); } auto getter = [¤tSetOfInputs](size_t i, size_t partindex) -> DataRef { - if (currentSetOfInputs[i].getNumberOfPairs() > partindex) { + if ((currentSetOfInputs[i].messages | count_payloads{}) > partindex) { const char* headerptr = nullptr; const char* payloadptr = nullptr; size_t payloadSize = 0; @@ -2153,7 +2153,7 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v return DataRef{}; }; auto nofPartsGetter = [¤tSetOfInputs](size_t i) -> size_t { - return currentSetOfInputs[i].getNumberOfPairs(); + return (currentSetOfInputs[i].messages | count_payloads{}); }; auto refCountGetter = [¤tSetOfInputs](size_t idx) -> int { auto& header = static_cast(*(currentSetOfInputs[idx].messages | get_header{0})); From 1175e89185809f040661c9b928427f64fb9bd4a3 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 29 Mar 2026 14:50:58 +0200 Subject: [PATCH 431/701] DPL: fix also getNumberOfPayloads --- Framework/Core/include/Framework/MessageSet.h | 6 ------ Framework/Core/test/test_DataRelayer.cxx | 6 +++--- 2 files changed, 3 insertions(+), 9 deletions(-) diff --git a/Framework/Core/include/Framework/MessageSet.h b/Framework/Core/include/Framework/MessageSet.h index 440b98514eb51..8a5aca854ca82 100644 --- a/Framework/Core/include/Framework/MessageSet.h +++ b/Framework/Core/include/Framework/MessageSet.h @@ -74,12 +74,6 @@ struct MessageSet { return *this; } - /// get number of payloads for an in-flight message - [[nodiscard]] size_t getNumberOfPayloads(size_t mi) const - { - return messages | get_num_payloads{mi}; - } - /// clear the set void clear() { diff --git a/Framework/Core/test/test_DataRelayer.cxx b/Framework/Core/test/test_DataRelayer.cxx index 1f7518860bf57..332a87970eda0 100644 --- a/Framework/Core/test/test_DataRelayer.cxx +++ b/Framework/Core/test/test_DataRelayer.cxx @@ -738,7 +738,7 @@ TEST_CASE("DataRelayer") // payloads REQUIRE(messageSet.size() == 1); REQUIRE((messageSet[0].messages | count_parts{}) == nSplitParts); - REQUIRE(messageSet[0].getNumberOfPayloads(0) == 1); + REQUIRE((messageSet[0].messages | get_num_payloads{0}) == 1); } SECTION("SplitPayloadSequence") @@ -803,8 +803,8 @@ TEST_CASE("DataRelayer") REQUIRE((messageSet[0].messages | count_parts{}) == sequenceSize.size()); size_t counter = 0; for (size_t seqid = 0; seqid < sequenceSize.size(); ++seqid) { - REQUIRE(messageSet[0].getNumberOfPayloads(seqid) == sequenceSize[seqid]); - for (size_t pi = 0; pi < messageSet[0].getNumberOfPayloads(seqid); ++pi) { + REQUIRE((messageSet[0].messages | get_num_payloads{seqid}) == sequenceSize[seqid]); + for (size_t pi = 0; pi < (messageSet[0].messages | get_num_payloads{seqid}); ++pi) { REQUIRE((messageSet[0].messages | get_payload{seqid, pi})); auto const* data = (messageSet[0].messages | get_payload{seqid, pi})->GetData(); REQUIRE(*(reinterpret_cast(data)) == counter); From 3b23b764efc5c6255c2e0e1d40d0dcecdeff81f5 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 29 Mar 2026 14:50:58 +0200 Subject: [PATCH 432/701] Remove unneeded parts in MessageSet --- Framework/Core/include/Framework/MessageSet.h | 12 ------------ 1 file changed, 12 deletions(-) diff --git a/Framework/Core/include/Framework/MessageSet.h b/Framework/Core/include/Framework/MessageSet.h index 8a5aca854ca82..4f5943890ae68 100644 --- a/Framework/Core/include/Framework/MessageSet.h +++ b/Framework/Core/include/Framework/MessageSet.h @@ -31,20 +31,8 @@ namespace o2::framework /// O2 message model. For this purpose, also the pair index is filled and can /// be used to access header and payload associated with a pair struct MessageSet { - struct Index { - Index(size_t p, size_t s) : position(p), size(s) {} - size_t position = 0; - size_t size = 0; - }; // linear storage of messages std::vector messages; - struct PairMapping { - PairMapping(size_t partId, size_t payloadId) : partIndex(partId), payloadIndex(payloadId) {} - // O2 message where the pair is located in - size_t partIndex = 0; - // payload index within the O2 message - size_t payloadIndex = 0; - }; MessageSet() : messages() From 8c44308e7f0cc953fa4e2ece1c31886e6652dec4 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 29 Mar 2026 14:50:58 +0200 Subject: [PATCH 433/701] Drop add, reset --- Framework/Core/include/Framework/MessageSet.h | 32 ++------------- Framework/Core/src/DataRelayer.cxx | 11 +++-- Framework/Core/test/test_ForwardInputs.cxx | 40 +++++++++++++------ Framework/Core/test/test_MessageSet.cxx | 20 ++++++---- 4 files changed, 50 insertions(+), 53 deletions(-) diff --git a/Framework/Core/include/Framework/MessageSet.h b/Framework/Core/include/Framework/MessageSet.h index 4f5943890ae68..233099e67dc0f 100644 --- a/Framework/Core/include/Framework/MessageSet.h +++ b/Framework/Core/include/Framework/MessageSet.h @@ -43,7 +43,9 @@ struct MessageSet { MessageSet(F getter, size_t size) : messages() { - add(std::forward(getter), size); + for (size_t i = 0; i < size; ++i) { + messages.emplace_back(std::move(getter(i))); + } } MessageSet(MessageSet&& other) @@ -67,34 +69,6 @@ struct MessageSet { { messages.clear(); } - - // this is more or less legacy - // PartRef has been earlier used to store fixed header-payload pairs - // reset the set and store content of the part ref - void reset(PartRef&& ref) - { - clear(); - add(std::move(ref)); - } - - // this is more or less legacy - // PartRef has been earlier used to store fixed header-payload pairs - // add content of the part ref - void add(PartRef&& ref) - { - messages.emplace_back(std::move(ref.header)); - messages.emplace_back(std::move(ref.payload)); - } - - /// add an O2 message - template - void add(F getter, size_t size) - { - for (size_t i = 0; i < size; ++i) { - messages.emplace_back(std::move(getter(i))); - } - } - }; } // namespace o2::framework diff --git a/Framework/Core/src/DataRelayer.cxx b/Framework/Core/src/DataRelayer.cxx index 4cda75ed001b0..d34d12b282a9d 100644 --- a/Framework/Core/src/DataRelayer.cxx +++ b/Framework/Core/src/DataRelayer.cxx @@ -242,7 +242,9 @@ DataRelayer::ActivityStats DataRelayer::processDanglingInputs(std::vector(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); + for (size_t i = 0; i < nPayloads + 1; ++i) { + target.messages.emplace_back(std::move(span[i])); + } mi += nPayloads; saved += nPayloads; } @@ -955,7 +959,8 @@ std::vector DataRelayer::consumeExistingInputsForTime auto& header = cache[cacheId].messages | get_header{pi}; auto&& newHeader = header->GetTransport()->CreateMessage(); newHeader->Copy(*header); - messages[arg].add(PartRef{std::move(newHeader), std::move(cache[cacheId].messages | get_payload{pi, 0})}); + messages[arg].messages.emplace_back(std::move(newHeader)); + messages[arg].messages.emplace_back(std::move(cache[cacheId].messages | get_payload{pi, 0})); } }; diff --git a/Framework/Core/test/test_ForwardInputs.cxx b/Framework/Core/test/test_ForwardInputs.cxx index e3031b7e72a69..6da42c5a94aca 100644 --- a/Framework/Core/test/test_ForwardInputs.cxx +++ b/Framework/Core/test/test_ForwardInputs.cxx @@ -91,7 +91,8 @@ TEST_CASE("ForwardInputsSingleMessageSingleRoute") 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)}); + messageSet.messages.emplace_back(std::move(header)); + messageSet.messages.emplace_back(std::move(payload)); REQUIRE((messageSet.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); @@ -142,7 +143,8 @@ TEST_CASE("ForwardInputsSingleMessageSingleRouteNoConsume") 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)}); + messageSet.messages.emplace_back(std::move(header)); + messageSet.messages.emplace_back(std::move(payload)); REQUIRE((messageSet.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); @@ -197,7 +199,8 @@ TEST_CASE("ForwardInputsSingleMessageSingleRouteAtEOS") 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)}); + messageSet.messages.emplace_back(std::move(header)); + messageSet.messages.emplace_back(std::move(payload)); REQUIRE((messageSet.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); @@ -255,7 +258,8 @@ TEST_CASE("ForwardInputsSingleMessageSingleRouteWithOldestPossible") 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)}); + messageSet.messages.emplace_back(std::move(header)); + messageSet.messages.emplace_back(std::move(payload)); REQUIRE((messageSet.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); @@ -320,7 +324,8 @@ TEST_CASE("ForwardInputsSingleMessageMultipleRoutes") 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)}); + messageSet.messages.emplace_back(std::move(header)); + messageSet.messages.emplace_back(std::move(payload)); REQUIRE((messageSet.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); @@ -383,7 +388,8 @@ TEST_CASE("ForwardInputsSingleMessageMultipleRoutesExternals") 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)}); + messageSet.messages.emplace_back(std::move(header)); + messageSet.messages.emplace_back(std::move(payload)); REQUIRE((messageSet.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); @@ -454,12 +460,14 @@ TEST_CASE("ForwardInputsMultiMessageMultipleRoutes") 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)}); + messageSet1.messages.emplace_back(std::move(header1)); + messageSet1.messages.emplace_back(std::move(payload1)); REQUIRE((messageSet1.messages | count_parts{}) == 1); auto header2 = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh2, dph}); MessageSet messageSet2; - messageSet2.add(PartRef{std::move(header2), std::move(payload2)}); + messageSet2.messages.emplace_back(std::move(header2)); + messageSet2.messages.emplace_back(std::move(payload2)); REQUIRE((messageSet2.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet1)); currentSetOfInputs.emplace_back(std::move(messageSet2)); @@ -524,7 +532,8 @@ TEST_CASE("ForwardInputsSingleMessageMultipleRoutesOnlyOneMatches") 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)}); + messageSet.messages.emplace_back(std::move(header)); + messageSet.messages.emplace_back(std::move(payload)); REQUIRE((messageSet.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); @@ -602,10 +611,13 @@ TEST_CASE("ForwardInputsSplitPayload") auto fillMessages = [&messages](size_t t) -> fair::mq::MessagePtr { return std::move(messages[t]); }; - messageSet.add(fillMessages, 3); + for (size_t i = 0; i < 3; ++i) { + messageSet.messages.emplace_back(fillMessages(i)); + } auto header2 = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh2, dph}); PartRef part{std::move(header2), transport->CreateMessage()}; - messageSet.add(std::move(part)); + messageSet.messages.emplace_back(std::move(part.header)); + messageSet.messages.emplace_back(std::move(part.payload)); REQUIRE((messageSet.messages | count_parts{}) == 2); currentSetOfInputs.emplace_back(std::move(messageSet)); @@ -726,7 +738,8 @@ TEST_CASE("ForwardInputEOSSingleRoute") 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)}); + messageSet.messages.emplace_back(std::move(header)); + messageSet.messages.emplace_back(std::move(payload)); REQUIRE((messageSet.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); @@ -771,7 +784,8 @@ TEST_CASE("ForwardInputOldestPossibleSingleRoute") 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)}); + messageSet.messages.emplace_back(std::move(header)); + messageSet.messages.emplace_back(std::move(payload)); REQUIRE((messageSet.messages | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); diff --git a/Framework/Core/test/test_MessageSet.cxx b/Framework/Core/test/test_MessageSet.cxx index bfbffb166da8d..d62a804e7681d 100644 --- a/Framework/Core/test/test_MessageSet.cxx +++ b/Framework/Core/test/test_MessageSet.cxx @@ -36,7 +36,9 @@ TEST_CASE("MessageSet") std::vector ptrs; ptrs.emplace_back(std::move(header)); ptrs.emplace_back(std::move(msg2)); - msgSet.add([&ptrs](size_t i) -> fair::mq::MessagePtr& { return ptrs[i]; }, 2); + for (size_t i = 0; i < 2; ++i) { + msgSet.messages.emplace_back(std::move(ptrs[i])); + } REQUIRE(msgSet.messages.size() == 2); REQUIRE((msgSet.messages | count_payloads{}) == 1); @@ -126,7 +128,8 @@ TEST_CASE("MessageSetAddPartRef") ptrs.emplace_back(std::move(msg2)); PartRef ref{std::move(msg), std::move(msg2)}; o2::framework::MessageSet msgSet; - msgSet.add(std::move(ref)); + msgSet.messages.emplace_back(std::move(ref.header)); + msgSet.messages.emplace_back(std::move(ref.payload)); REQUIRE(msgSet.messages.size() == 2); } @@ -155,17 +158,18 @@ TEST_CASE("MessageSetAddMultiple") std::unique_ptr msg3(nullptr); PartRef ref{std::move(header1), std::move(msg2)}; o2::framework::MessageSet msgSet; - msgSet.add(std::move(ref)); + msgSet.messages.emplace_back(std::move(ref.header)); + msgSet.messages.emplace_back(std::move(ref.payload)); PartRef ref2{std::move(header2), std::move(msg2)}; - msgSet.add(std::move(ref2)); + msgSet.messages.emplace_back(std::move(ref2.header)); + msgSet.messages.emplace_back(std::move(ref2.payload)); std::vector msgs; msgs.push_back(std::move(header3)); 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); + for (size_t i = 0; i < 3; ++i) { + msgSet.messages.emplace_back(std::move(msgs[i])); + } REQUIRE(msgSet.messages.size() == 7); From 65b055fdccb1fd6f8c850cb959a49cb615c3828f Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 29 Mar 2026 14:50:58 +0200 Subject: [PATCH 434/701] Get rid of the clear Method --- Framework/Core/include/Framework/MessageSet.h | 9 ++------- Framework/Core/src/DataProcessingDevice.cxx | 2 +- Framework/Core/src/DataRelayer.cxx | 6 +++--- 3 files changed, 6 insertions(+), 11 deletions(-) diff --git a/Framework/Core/include/Framework/MessageSet.h b/Framework/Core/include/Framework/MessageSet.h index 233099e67dc0f..1da8ca35c46f4 100644 --- a/Framework/Core/include/Framework/MessageSet.h +++ b/Framework/Core/include/Framework/MessageSet.h @@ -51,7 +51,7 @@ struct MessageSet { MessageSet(MessageSet&& other) : messages(std::move(other.messages)) { - other.clear(); + other.messages.clear(); } MessageSet& operator=(MessageSet&& other) @@ -60,15 +60,10 @@ struct MessageSet { return *this; } messages = std::move(other.messages); - other.clear(); + other.messages.clear(); return *this; } - /// clear the set - void clear() - { - messages.clear(); - } }; } // namespace o2::framework diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index 9b6395a02916a..af23219bfb509 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -2215,7 +2215,7 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v continue; } // This will hopefully delete the message. - currentSetOfInputs[ii].clear(); + currentSetOfInputs[ii].messages.clear(); } }; diff --git a/Framework/Core/src/DataRelayer.cxx b/Framework/Core/src/DataRelayer.cxx index d34d12b282a9d..dd051a2189c07 100644 --- a/Framework/Core/src/DataRelayer.cxx +++ b/Framework/Core/src/DataRelayer.cxx @@ -431,7 +431,7 @@ void DataRelayer::pruneCache(TimesliceSlot slot, OnDropCallback onDrop) // will be ignored. assert(numInputTypes * slot.index < cache.size()); for (size_t ai = slot.index * numInputTypes, ae = ai + numInputTypes; ai != ae; ++ai) { - cache[ai].clear(); + cache[ai].messages.clear(); cachedStateMetrics[ai] = CacheEntryStatus::EMPTY; } }; @@ -914,7 +914,7 @@ std::vector DataRelayer::consumeAllInputsForTimeslice auto invalidateCacheFor = [&numInputTypes, &index, &cache](TimesliceSlot s) { for (size_t ai = s.index * numInputTypes, ae = ai + numInputTypes; ai != ae; ++ai) { assert(std::accumulate(cache[ai].messages.begin(), cache[ai].messages.end(), true, [](bool result, auto const& element) { return result && element.get() == nullptr; })); - cache[ai].clear(); + cache[ai].messages.clear(); } index.markAsInvalid(s); }; @@ -978,7 +978,7 @@ void DataRelayer::clear() std::scoped_lock lock(mMutex); for (auto& cache : mCache) { - cache.clear(); + cache.messages.clear(); } for (size_t s = 0; s < mTimesliceIndex.size(); ++s) { mTimesliceIndex.markAsInvalid(TimesliceSlot{s}); From c85788dfb6aa87cf3e7df348d58deddb2ba673ba Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 29 Mar 2026 14:50:58 +0200 Subject: [PATCH 435/701] DPL: migrate away from MessageSet Use a vector of messages instead. To be replaced by a B-Tree which is able to keep track of all inputs / slots in a less rigid manner. --- .../Core/include/Framework/DataModelViews.h | 8 +- .../include/Framework/DataProcessingHelpers.h | 4 +- .../Core/include/Framework/DataRelayer.h | 10 +- Framework/Core/include/Framework/MessageSet.h | 71 ---- Framework/Core/src/DataProcessingDevice.cxx | 27 +- Framework/Core/src/DataProcessingHelpers.cxx | 4 +- Framework/Core/src/DataRelayer.cxx | 79 ++--- Framework/Core/test/benchmark_DataRelayer.cxx | 23 +- Framework/Core/test/test_DataRelayer.cxx | 25 +- Framework/Core/test/test_ForwardInputs.cxx | 125 ++++--- Framework/Core/test/test_MessageSet.cxx | 331 ++++++++---------- 11 files changed, 296 insertions(+), 411 deletions(-) delete mode 100644 Framework/Core/include/Framework/MessageSet.h diff --git a/Framework/Core/include/Framework/DataModelViews.h b/Framework/Core/include/Framework/DataModelViews.h index 7c39a94950e9c..285f5ef15154e 100644 --- a/Framework/Core/include/Framework/DataModelViews.h +++ b/Framework/Core/include/Framework/DataModelViews.h @@ -16,7 +16,9 @@ #include "DomainInfoHeader.h" #include "SourceInfoHeader.h" #include "Headers/DataHeader.h" +#include "Framework/TimesliceSlot.h" #include +#include namespace o2::framework { @@ -213,13 +215,11 @@ struct get_num_payloads { } }; -struct MessageSet; - struct inputs_for_slot { TimesliceSlot slot; template requires requires(R r) { requires std::ranges::random_access_range; } - friend std::span operator|(R&& r, inputs_for_slot self) + friend auto operator|(R&& r, inputs_for_slot self) { return std::span(r.sets[self.slot.index * r.inputsPerSlot]); } @@ -231,7 +231,7 @@ struct messages_for_input { requires std::ranges::random_access_range friend std::span operator|(R&& r, messages_for_input self) { - return r[self.inputIdx].messages; + return std::span(r[self.inputIdx]); } }; diff --git a/Framework/Core/include/Framework/DataProcessingHelpers.h b/Framework/Core/include/Framework/DataProcessingHelpers.h index 87aeeb8922da3..f414e3aa4ae00 100644 --- a/Framework/Core/include/Framework/DataProcessingHelpers.h +++ b/Framework/Core/include/Framework/DataProcessingHelpers.h @@ -15,6 +15,7 @@ #include "Framework/TimesliceSlot.h" #include "Framework/TimesliceIndex.h" #include +#include #include #include @@ -29,7 +30,6 @@ struct OutputChannelState; struct ProcessingPolicies; struct DeviceSpec; struct FairMQDeviceProxy; -struct MessageSet; struct ChannelIndex; enum struct StreamingState; enum struct TransitionHandlingState; @@ -54,7 +54,7 @@ struct DataProcessingHelpers { /// 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, + 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, diff --git a/Framework/Core/include/Framework/DataRelayer.h b/Framework/Core/include/Framework/DataRelayer.h index e5a2aecea1de4..b56a2cb59ff10 100644 --- a/Framework/Core/include/Framework/DataRelayer.h +++ b/Framework/Core/include/Framework/DataRelayer.h @@ -16,7 +16,7 @@ #include "Framework/DataDescriptorMatcher.h" #include "Framework/ForwardRoute.h" #include "Framework/CompletionPolicy.h" -#include "Framework/MessageSet.h" +#include #include "Framework/TimesliceIndex.h" #include "Framework/Tracing.h" #include "Framework/TimesliceSlot.h" @@ -113,7 +113,7 @@ class DataRelayer ActivityStats processDanglingInputs(std::vector const&, ServiceRegistryRef context, bool createNew); - using OnDropCallback = std::function&, TimesliceIndex::OldestOutputInfo info)>; + 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&)>; @@ -156,8 +156,8 @@ class DataRelayer /// Returns an input registry associated to the given timeslice and gives /// ownership to the caller. This is because once the inputs are out of the /// DataRelayer they need to be deleted once the processing is concluded. - std::vector consumeAllInputsForTimeslice(TimesliceSlot id); - std::vector consumeExistingInputsForTimeslice(TimesliceSlot id); + std::vector> consumeAllInputsForTimeslice(TimesliceSlot id); + std::vector> consumeExistingInputsForTimeslice(TimesliceSlot id); /// Returns how many timeslices we can handle in parallel [[nodiscard]] size_t getParallelTimeslices() const; @@ -203,7 +203,7 @@ class DataRelayer /// Notice that we store them as a NxM sized vector, where /// N is the maximum number of inflight timeslices, while /// M is the number of inputs which are requested. - std::vector mCache; + std::vector> mCache; /// This is the index which maps a given timestamp to the associated /// cacheline. diff --git a/Framework/Core/include/Framework/MessageSet.h b/Framework/Core/include/Framework/MessageSet.h deleted file mode 100644 index 1da8ca35c46f4..0000000000000 --- a/Framework/Core/include/Framework/MessageSet.h +++ /dev/null @@ -1,71 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not 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_MESSAGESET_H -#define FRAMEWORK_MESSAGESET_H - -#include "Framework/PartRef.h" -#include -#include "Framework/DataModelViews.h" -#include -#include -#include - -namespace o2::framework -{ - -/// A set of inflight messages. -/// The messages are stored in a linear vector. Originally, an O2 message was -/// comprised of a header-payload pair which makes indexing of pairs in the -/// storage simple. To support O2 messages with multiple payloads in a future -/// update of the data model, a message index is needed to store position in the -/// linear storage and number of messages. -/// DPL InputRecord API is providing refs of header-payload pairs, the original -/// O2 message model. For this purpose, also the pair index is filled and can -/// be used to access header and payload associated with a pair -struct MessageSet { - // linear storage of messages - std::vector messages; - - MessageSet() - : messages() - { - } - - template - MessageSet(F getter, size_t size) - : messages() - { - for (size_t i = 0; i < size; ++i) { - messages.emplace_back(std::move(getter(i))); - } - } - - MessageSet(MessageSet&& other) - : messages(std::move(other.messages)) - { - other.messages.clear(); - } - - MessageSet& operator=(MessageSet&& other) - { - if (&other == this) { - return *this; - } - messages = std::move(other.messages); - other.messages.clear(); - return *this; - } - -}; - -} // namespace o2::framework - -#endif // FRAMEWORK_MESSAGESET_H diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index af23219bfb509..6b90747550278 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -50,6 +50,7 @@ #include "DecongestionService.h" #include "Framework/DataProcessingHelpers.h" +#include "Framework/DataModelViews.h" #include "DataRelayerHelpers.h" #include "Headers/DataHeader.h" #include "Headers/DataHeaderHelpers.h" @@ -585,7 +586,7 @@ auto decongestionCallbackLate = [](AsyncTask& task, size_t aid) -> void { // the inputs which are shared between this device and others // to the next one in the daisy chain. // FIXME: do it in a smarter way than O(N^2) -static auto forwardInputs = [](ServiceRegistryRef registry, TimesliceSlot slot, std::vector& currentSetOfInputs, +static auto forwardInputs = [](ServiceRegistryRef registry, TimesliceSlot slot, std::vector>& currentSetOfInputs, TimesliceIndex::OldestOutputInfo oldestTimeslice, bool copy, bool consume = true) { auto& proxy = registry.get(); @@ -617,7 +618,7 @@ 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, +static auto cleanEarlyForward = [](ServiceRegistryRef registry, TimesliceSlot slot, std::vector>& currentSetOfInputs, TimesliceIndex::OldestOutputInfo oldestTimeslice, bool copy, bool consume = true) { auto& proxy = registry.get(); @@ -627,7 +628,7 @@ static auto cleanEarlyForward = [](ServiceRegistryRef registry, TimesliceSlot sl // 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); + auto span = std::span(currentSetOfInputs[ii]); DataProcessingHelpers::cleanForwardedMessages(span, consume); } @@ -1278,7 +1279,7 @@ void DataProcessingDevice::Run() // - we can trigger further events from the queue // - we can guarantee this is the last thing we do in the loop ( // assuming no one else is adding to the queue before this point). - auto onDrop = [®istry = mServiceRegistry, lid](TimesliceSlot slot, std::vector& dropped, TimesliceIndex::OldestOutputInfo oldestOutputInfo) { + auto onDrop = [®istry = mServiceRegistry, lid](TimesliceSlot slot, std::vector>& dropped, TimesliceIndex::OldestOutputInfo oldestOutputInfo) { O2_SIGNPOST_START(device, lid, "run_loop", "Dropping message from slot %" PRIu64 ". Forwarding as needed.", (uint64_t)slot.index); ServiceRegistryRef ref{registry}; ref.get(); @@ -1944,7 +1945,7 @@ void DataProcessingDevice::handleData(ServiceRegistryRef ref, InputChannelInfo& nPayloadsPerHeader = 1; ii += (nMessages / 2) - 1; } - auto onDrop = [ref](TimesliceSlot slot, std::vector& dropped, TimesliceIndex::OldestOutputInfo oldestOutputInfo) { + auto onDrop = [ref](TimesliceSlot slot, std::vector>& dropped, TimesliceIndex::OldestOutputInfo oldestOutputInfo) { O2_SIGNPOST_ID_GENERATE(cid, async_queue); O2_SIGNPOST_EVENT_EMIT(async_queue, cid, "onDrop", "Dropping message from slot %zu. Forwarding as needed. Timeslice %zu", slot.index, oldestOutputInfo.timeslice.value); @@ -2122,7 +2123,7 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v // want to support multithreaded dispatching of operations, I can simply // move these to some thread local store and the rest of the lambdas // should work just fine. - std::vector currentSetOfInputs; + std::vector> currentSetOfInputs; // auto getInputSpan = [ref, ¤tSetOfInputs](TimesliceSlot slot, bool consume = true) { @@ -2133,7 +2134,7 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v currentSetOfInputs = relayer.consumeExistingInputsForTimeslice(slot); } auto getter = [¤tSetOfInputs](size_t i, size_t partindex) -> DataRef { - if ((currentSetOfInputs[i].messages | count_payloads{}) > partindex) { + if ((currentSetOfInputs[i] | count_payloads{}) > partindex) { const char* headerptr = nullptr; const char* payloadptr = nullptr; size_t payloadSize = 0; @@ -2142,9 +2143,9 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v // sequence is the header message // - each part has one or more payload messages // - InputRecord provides all payloads as header-payload pair - auto const indices = currentSetOfInputs[i].messages | get_pair{partindex}; - auto const& headerMsg = currentSetOfInputs[i].messages[indices.headerIdx]; - auto const& payloadMsg = currentSetOfInputs[i].messages[indices.payloadIdx]; + auto const indices = currentSetOfInputs[i] | get_pair{partindex}; + auto const& headerMsg = currentSetOfInputs[i][indices.headerIdx]; + auto const& payloadMsg = currentSetOfInputs[i][indices.payloadIdx]; headerptr = static_cast(headerMsg->GetData()); payloadptr = payloadMsg ? static_cast(payloadMsg->GetData()) : nullptr; payloadSize = payloadMsg ? payloadMsg->GetSize() : 0; @@ -2153,10 +2154,10 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v return DataRef{}; }; auto nofPartsGetter = [¤tSetOfInputs](size_t i) -> size_t { - return (currentSetOfInputs[i].messages | count_payloads{}); + return (currentSetOfInputs[i] | count_payloads{}); }; auto refCountGetter = [¤tSetOfInputs](size_t idx) -> int { - auto& header = static_cast(*(currentSetOfInputs[idx].messages | get_header{0})); + auto& header = static_cast(*(currentSetOfInputs[idx] | get_header{0})); return header.GetRefCount(); }; return InputSpan{getter, nofPartsGetter, refCountGetter, currentSetOfInputs.size()}; @@ -2215,7 +2216,7 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v continue; } // This will hopefully delete the message. - currentSetOfInputs[ii].messages.clear(); + currentSetOfInputs[ii].clear(); } }; diff --git a/Framework/Core/src/DataProcessingHelpers.cxx b/Framework/Core/src/DataProcessingHelpers.cxx index 334a0fc6045f6..b8399a4c591e7 100644 --- a/Framework/Core/src/DataProcessingHelpers.cxx +++ b/Framework/Core/src/DataProcessingHelpers.cxx @@ -393,14 +393,14 @@ void DataProcessingHelpers::cleanForwardedMessages(std::span& currentSetOfInputs, + 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); + auto span = std::span(currentSetOfInputs[ii]); routeForwardedMessages(proxy, span, forwardedParts, copyByDefault, consume); } return forwardedParts; diff --git a/Framework/Core/src/DataRelayer.cxx b/Framework/Core/src/DataRelayer.cxx index dd051a2189c07..fc9966ffad643 100644 --- a/Framework/Core/src/DataRelayer.cxx +++ b/Framework/Core/src/DataRelayer.cxx @@ -38,6 +38,7 @@ #include "Framework/DataTakingContext.h" #include "Framework/DefaultsHelpers.h" #include "Framework/RawDeviceService.h" +#include "Framework/DataModelViews.h" #include "Headers/DataHeaderHelpers.h" #include "Framework/Formatters.h" @@ -184,11 +185,11 @@ DataRelayer::ActivityStats DataRelayer::processDanglingInputs(std::vector std::span { + auto getPartialRecord = [&cache = mCache, numInputTypes = mDistinctRoutesIndex.size()](int li) -> std::span const> { auto offset = li * numInputTypes; assert(cache.size() >= offset + numInputTypes); auto const start = cache.data() + offset; @@ -213,9 +214,9 @@ DataRelayer::ActivityStats DataRelayer::processDanglingInputs(std::vector(header->GetData()), reinterpret_cast(payload ? payload->GetData() : nullptr), @@ -224,10 +225,10 @@ DataRelayer::ActivityStats DataRelayer::processDanglingInputs(std::vector int { - auto& header = static_cast(*(partial[idx].messages | get_header{0})); + auto& header = static_cast(*(partial[idx] | get_header{0})); return header.GetRefCount(); }; InputSpan span{getter, nPartsGetter, refCountGetter, static_cast(partial.size())}; @@ -242,14 +243,14 @@ DataRelayer::ActivityStats DataRelayer::processDanglingInputs(std::vector(); @@ -355,7 +356,7 @@ void DataRelayer::setOldestPossibleInput(TimesliceId proposed, ChannelIndex chan continue; } auto& element = mCache[si * mInputs.size() + mi]; - if (element.messages.empty()) { + if (element.empty()) { auto& state = mContext.get(); if (state.transitionHandling != TransitionHandlingState::NoTransition && DefaultsHelpers::onlineDeploymentMode()) { if (state.allowedProcessing == DeviceState::CalibrationOnly) { @@ -407,17 +408,17 @@ void DataRelayer::pruneCache(TimesliceSlot slot, OnDropCallback onDrop) if (onDrop) { auto oldestPossibleTimeslice = index.getOldestPossibleOutput(); // State of the computation - std::vector dropped(numInputTypes); + std::vector> dropped(numInputTypes); for (size_t ai = 0, ae = numInputTypes; ai != ae; ++ai) { auto cacheId = slot.index * numInputTypes + ai; cachedStateMetrics[cacheId] = CacheEntryStatus::RUNNING; // TODO: in the original implementation of the cache, there have been only two messages per entry, // check if the 2 above corresponds to the number of messages. - if (!cache[cacheId].messages.empty()) { + if (!cache[cacheId].empty()) { dropped[ai] = std::move(cache[cacheId]); } } - bool anyDropped = std::any_of(dropped.begin(), dropped.end(), [](auto& m) { return !m.messages.empty(); }); + bool anyDropped = std::any_of(dropped.begin(), dropped.end(), [](auto& m) { return !m.empty(); }); if (anyDropped) { O2_SIGNPOST_ID_GENERATE(aid, data_relayer); O2_SIGNPOST_EVENT_EMIT(data_relayer, aid, "pruneCache", "Dropping stuff from slot %zu with timeslice %zu", slot.index, oldestPossibleTimeslice.timeslice.value); @@ -431,7 +432,7 @@ void DataRelayer::pruneCache(TimesliceSlot slot, OnDropCallback onDrop) // will be ignored. assert(numInputTypes * slot.index < cache.size()); for (size_t ai = slot.index * numInputTypes, ae = ai + numInputTypes; ai != ae; ++ai) { - cache[ai].messages.clear(); + cache[ai].clear(); cachedStateMetrics[ai] = CacheEntryStatus::EMPTY; } }; @@ -508,7 +509,7 @@ DataRelayer::RelayChoice timeslice.value, slot.index, info.index.value == ChannelIndex::INVALID ? "invalid" : services.get().getInputChannel(info.index)->GetName().c_str()); auto cacheIdx = numInputTypes * slot.index + input; - MessageSet& target = cache[cacheIdx]; + auto& target = cache[cacheIdx]; cachedStateMetrics[cacheIdx] = CacheEntryStatus::PENDING; // TODO: make sure that multiple parts can only be added within the same call of // DataRelayer::relay @@ -539,7 +540,7 @@ DataRelayer::RelayChoice // Notice this will split [(header, payload), (header, payload)] multiparts // in N different subParts for the message spec. for (size_t i = 0; i < nPayloads + 1; ++i) { - target.messages.emplace_back(std::move(span[i])); + target.emplace_back(std::move(span[i])); } mi += nPayloads; saved += nPayloads; @@ -732,7 +733,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) -> std::span { + auto getPartialRecord = [&cache, &numInputTypes](int li) -> std::span const> { auto offset = li * numInputTypes; assert(cache.size() >= offset + numInputTypes); auto const start = cache.data() + offset; @@ -790,9 +791,9 @@ void DataRelayer::getReadyToProcess(std::vector& comp auto partial = getPartialRecord(li); // TODO: get the data ref from message model auto getter = [&partial](size_t idx, size_t part) { - if (!partial[idx].messages.empty() && (partial[idx].messages | get_header{part}).get()) { - auto header = (partial[idx].messages | get_header{part}).get(); - auto payload = (partial[idx].messages | get_payload{part, 0}).get(); + if (!partial[idx].empty() && (partial[idx] | get_header{part}).get()) { + auto header = (partial[idx] | get_header{part}).get(); + auto payload = (partial[idx] | get_payload{part, 0}).get(); return DataRef{nullptr, reinterpret_cast(header->GetData()), reinterpret_cast(payload ? payload->GetData() : nullptr), @@ -801,10 +802,10 @@ void DataRelayer::getReadyToProcess(std::vector& comp return DataRef{}; }; auto nPartsGetter = [&partial](size_t idx) { - return partial[idx].messages | count_parts{}; + return partial[idx] | count_parts{}; }; auto refCountGetter = [&partial](size_t idx) -> int { - auto& header = static_cast(*(partial[idx].messages | get_header{0})); + auto& header = static_cast(*(partial[idx] | get_header{0})); return header.GetRefCount(); }; InputSpan span{getter, nPartsGetter, refCountGetter, static_cast(partial.size())}; @@ -875,13 +876,13 @@ void DataRelayer::updateCacheStatus(TimesliceSlot slot, CacheEntryStatus oldStat } } -std::vector DataRelayer::consumeAllInputsForTimeslice(TimesliceSlot slot) +std::vector> DataRelayer::consumeAllInputsForTimeslice(TimesliceSlot slot) { std::scoped_lock lock(mMutex); const auto numInputTypes = mDistinctRoutesIndex.size(); // State of the computation - std::vector messages(numInputTypes); + std::vector> messages(numInputTypes); auto& cache = mCache; auto& index = mTimesliceIndex; @@ -901,7 +902,7 @@ std::vector DataRelayer::consumeAllInputsForTimeslice cachedStateMetrics[cacheId] = CacheEntryStatus::RUNNING; // TODO: in the original implementation of the cache, there have been only two messages per entry, // check if the 2 above corresponds to the number of messages. - if (!cache[cacheId].messages.empty()) { + if (!cache[cacheId].empty()) { messages[arg] = std::move(cache[cacheId]); } index.markAsInvalid(s); @@ -913,8 +914,8 @@ std::vector DataRelayer::consumeAllInputsForTimeslice // FIXME: what happens when we have enough timeslices to hit the invalid one? auto invalidateCacheFor = [&numInputTypes, &index, &cache](TimesliceSlot s) { for (size_t ai = s.index * numInputTypes, ae = ai + numInputTypes; ai != ae; ++ai) { - assert(std::accumulate(cache[ai].messages.begin(), cache[ai].messages.end(), true, [](bool result, auto const& element) { return result && element.get() == nullptr; })); - cache[ai].messages.clear(); + assert(std::accumulate(cache[ai].begin(), cache[ai].end(), true, [](bool result, auto const& element) { return result && element.get() == nullptr; })); + cache[ai].clear(); } index.markAsInvalid(s); }; @@ -929,13 +930,13 @@ std::vector DataRelayer::consumeAllInputsForTimeslice return messages; } -std::vector DataRelayer::consumeExistingInputsForTimeslice(TimesliceSlot slot) +std::vector> DataRelayer::consumeExistingInputsForTimeslice(TimesliceSlot slot) { std::scoped_lock lock(mMutex); const auto numInputTypes = mDistinctRoutesIndex.size(); // State of the computation - std::vector messages(numInputTypes); + std::vector> messages(numInputTypes); auto& cache = mCache; auto& index = mTimesliceIndex; @@ -955,12 +956,12 @@ std::vector DataRelayer::consumeExistingInputsForTime cachedStateMetrics[cacheId] = CacheEntryStatus::RUNNING; // TODO: in the original implementation of the cache, there have been only two messages per entry, // check if the 2 above corresponds to the number of messages. - for (size_t pi = 0; pi < (cache[cacheId].messages | count_parts{}); pi++) { - auto& header = cache[cacheId].messages | get_header{pi}; + for (size_t pi = 0; pi < (cache[cacheId] | count_parts{}); pi++) { + auto& header = cache[cacheId] | get_header{pi}; auto&& newHeader = header->GetTransport()->CreateMessage(); newHeader->Copy(*header); - messages[arg].messages.emplace_back(std::move(newHeader)); - messages[arg].messages.emplace_back(std::move(cache[cacheId].messages | get_payload{pi, 0})); + messages[arg].emplace_back(std::move(newHeader)); + messages[arg].emplace_back(std::move(cache[cacheId] | get_payload{pi, 0})); } }; @@ -978,7 +979,7 @@ void DataRelayer::clear() std::scoped_lock lock(mMutex); for (auto& cache : mCache) { - cache.messages.clear(); + cache.clear(); } for (size_t s = 0; s < mTimesliceIndex.size(); ++s) { mTimesliceIndex.markAsInvalid(TimesliceSlot{s}); diff --git a/Framework/Core/test/benchmark_DataRelayer.cxx b/Framework/Core/test/benchmark_DataRelayer.cxx index 312711d73e95e..e7df8fbb2fe9b 100644 --- a/Framework/Core/test/benchmark_DataRelayer.cxx +++ b/Framework/Core/test/benchmark_DataRelayer.cxx @@ -14,6 +14,7 @@ #include "Headers/Stack.h" #include "Framework/CompletionPolicyHelpers.h" #include "Framework/DataRelayer.h" +#include "Framework/DataModelViews.h" #include "Framework/DataProcessingHeader.h" #include "Framework/DataProcessingStates.h" #include "Framework/DataProcessingStats.h" @@ -138,8 +139,8 @@ static void BM_RelaySingleSlot(benchmark::State& state) assert(ready[0].op == CompletionPolicy::CompletionOp::Consume); auto result = relayer.consumeAllInputsForTimeslice(ready[0].slot); assert(result.size() == 1); - assert((result.at(0).messages | count_parts{}) == 1); - inflightMessages = std::move(result[0].messages); + assert((result.at(0) | count_parts{}) == 1); + inflightMessages = std::move(result[0]); } } @@ -194,8 +195,8 @@ static void BM_RelayMultipleSlots(benchmark::State& state) assert(ready[0].op == CompletionPolicy::CompletionOp::Consume); auto result = relayer.consumeAllInputsForTimeslice(ready[0].slot); assert(result.size() == 1); - assert((result.at(0).messages | count_parts{}) == 1); - inflightMessages = std::move(result[0].messages); + assert((result.at(0) | count_parts{}) == 1); + inflightMessages = std::move(result[0]); } } @@ -268,11 +269,11 @@ static void BM_RelayMultipleRoutes(benchmark::State& state) assert(ready[0].op == CompletionPolicy::CompletionOp::Consume); auto result = relayer.consumeAllInputsForTimeslice(ready[0].slot); assert(result.size() == 2); - assert((result.at(0).messages | count_parts{}) == 1); - assert((result.at(1).messages | count_parts{}) == 1); - inflightMessages = std::move(result[0].messages); - inflightMessages.emplace_back(std::move(result[1].messages[0])); - inflightMessages.emplace_back(std::move(result[1].messages[1])); + assert((result.at(0) | count_parts{}) == 1); + assert((result.at(1) | count_parts{}) == 1); + inflightMessages = std::move(result[0]); + inflightMessages.emplace_back(std::move(result[1][0])); + inflightMessages.emplace_back(std::move(result[1][1])); } } @@ -332,7 +333,7 @@ static void BM_RelaySplitParts(benchmark::State& state) relayer.getReadyToProcess(ready); assert(ready.size() == 1); assert(ready[0].op == CompletionPolicy::CompletionOp::Consume); - inflightMessages = std::move(relayer.consumeAllInputsForTimeslice(ready[0].slot)[0].messages); + inflightMessages = std::move(relayer.consumeAllInputsForTimeslice(ready[0].slot)[0]); } } @@ -386,7 +387,7 @@ static void BM_RelayMultiplePayloads(benchmark::State& state) relayer.getReadyToProcess(ready); assert(ready.size() == 1); assert(ready[0].op == CompletionPolicy::CompletionOp::Consume); - inflightMessages = std::move(relayer.consumeAllInputsForTimeslice(ready[0].slot)[0].messages); + inflightMessages = std::move(relayer.consumeAllInputsForTimeslice(ready[0].slot)[0]); } } diff --git a/Framework/Core/test/test_DataRelayer.cxx b/Framework/Core/test/test_DataRelayer.cxx index 332a87970eda0..271b7829a9525 100644 --- a/Framework/Core/test/test_DataRelayer.cxx +++ b/Framework/Core/test/test_DataRelayer.cxx @@ -16,6 +16,7 @@ #include "MemoryResources/MemoryResources.h" #include "Framework/CompletionPolicyHelpers.h" #include "Framework/DataRelayer.h" +#include "Framework/DataModelViews.h" #include "Framework/DataProcessingStats.h" #include "Framework/DataProcessingStates.h" #include "Framework/DriverConfig.h" @@ -119,7 +120,7 @@ TEST_CASE("DataRelayer") auto result = relayer.consumeAllInputsForTimeslice(ready[0].slot); // one MessageSet with one PartRef with header and payload REQUIRE(result.size() == 1); - REQUIRE((result.at(0).messages | count_parts{}) == 1); + REQUIRE((result.at(0) | count_parts{}) == 1); } // @@ -169,7 +170,7 @@ TEST_CASE("DataRelayer") auto result = relayer.consumeAllInputsForTimeslice(ready[0].slot); // one MessageSet with one PartRef with header and payload REQUIRE(result.size() == 1); - REQUIRE((result.at(0).messages | count_parts{}) == 1); + REQUIRE((result.at(0) | count_parts{}) == 1); } // This test a more complicated set of inputs, and verifies that data is @@ -249,8 +250,8 @@ TEST_CASE("DataRelayer") auto result = relayer.consumeAllInputsForTimeslice(ready[0].slot); // two MessageSets, each with one PartRef REQUIRE(result.size() == 2); - REQUIRE((result.at(0).messages | count_parts{}) == 1); - REQUIRE((result.at(1).messages | count_parts{}) == 1); + REQUIRE((result.at(0) | count_parts{}) == 1); + REQUIRE((result.at(1) | count_parts{}) == 1); } // This test a more complicated set of inputs, and verifies that data is @@ -737,8 +738,8 @@ TEST_CASE("DataRelayer") // we have one input route and thus one message set containing pairs for all // payloads REQUIRE(messageSet.size() == 1); - REQUIRE((messageSet[0].messages | count_parts{}) == nSplitParts); - REQUIRE((messageSet[0].messages | get_num_payloads{0}) == 1); + REQUIRE((messageSet[0] | count_parts{}) == nSplitParts); + REQUIRE((messageSet[0] | get_num_payloads{0}) == 1); } SECTION("SplitPayloadSequence") @@ -800,13 +801,13 @@ TEST_CASE("DataRelayer") // we have one input route REQUIRE(messageSet.size() == 1); // one message set containing number of added sequences of messages - REQUIRE((messageSet[0].messages | count_parts{}) == sequenceSize.size()); + REQUIRE((messageSet[0] | count_parts{}) == sequenceSize.size()); size_t counter = 0; for (size_t seqid = 0; seqid < sequenceSize.size(); ++seqid) { - REQUIRE((messageSet[0].messages | get_num_payloads{seqid}) == sequenceSize[seqid]); - for (size_t pi = 0; pi < (messageSet[0].messages | get_num_payloads{seqid}); ++pi) { - REQUIRE((messageSet[0].messages | get_payload{seqid, pi})); - auto const* data = (messageSet[0].messages | get_payload{seqid, pi})->GetData(); + REQUIRE((messageSet[0] | get_num_payloads{seqid}) == sequenceSize[seqid]); + for (size_t pi = 0; pi < (messageSet[0] | get_num_payloads{seqid}); ++pi) { + REQUIRE((messageSet[0] | get_payload{seqid, pi})); + auto const* data = (messageSet[0] | get_payload{seqid, pi})->GetData(); REQUIRE(*(reinterpret_cast(data)) == counter); ++counter; } @@ -891,7 +892,7 @@ TEST_CASE("DataRelayer") auto result = relayer.consumeAllInputsForTimeslice(ready[0].slot); REQUIRE(result.size() == 1); - REQUIRE((result.at(0).messages | count_parts{}) == 1); + REQUIRE((result.at(0) | count_parts{}) == 1); } SECTION("ProcessDanglingInputsSkipsWhenDataPresent") diff --git a/Framework/Core/test/test_ForwardInputs.cxx b/Framework/Core/test/test_ForwardInputs.cxx index 6da42c5a94aca..0263158ee0f9b 100644 --- a/Framework/Core/test/test_ForwardInputs.cxx +++ b/Framework/Core/test/test_ForwardInputs.cxx @@ -16,7 +16,7 @@ #include "Framework/SourceInfoHeader.h" #include "Framework/DomainInfoHeader.h" #include "Framework/Signpost.h" -#include "Framework/MessageSet.h" +#include "Framework/DataModelViews.h" #include "Framework/FairMQDeviceProxy.h" #include "Headers/Stack.h" #include "MemoryResources/MemoryResources.h" @@ -43,7 +43,7 @@ TEST_CASE("ForwardInputsEmpty") bool copyByDefault = true; FairMQDeviceProxy proxy; - std::vector currentSetOfInputs; + std::vector> currentSetOfInputs; auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); REQUIRE(result.empty()); @@ -84,16 +84,16 @@ TEST_CASE("ForwardInputsSingleMessageSingleRoute") proxy.bind({}, {}, routes, findChannelByName, nullptr); - std::vector currentSetOfInputs; - MessageSet messageSet; + std::vector> currentSetOfInputs; + std::vector 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.messages.emplace_back(std::move(header)); - messageSet.messages.emplace_back(std::move(payload)); - REQUIRE((messageSet.messages | count_parts{}) == 1); + messageSet.emplace_back(std::move(header)); + messageSet.emplace_back(std::move(payload)); + REQUIRE((messageSet | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); @@ -135,17 +135,17 @@ TEST_CASE("ForwardInputsSingleMessageSingleRouteNoConsume") proxy.bind({}, {}, routes, findChannelByName, nullptr); - std::vector currentSetOfInputs; - MessageSet messageSet; + std::vector> currentSetOfInputs; + std::vector 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.messages.emplace_back(std::move(header)); - messageSet.messages.emplace_back(std::move(payload)); - REQUIRE((messageSet.messages | count_parts{}) == 1); + messageSet.emplace_back(std::move(header)); + messageSet.emplace_back(std::move(payload)); + REQUIRE((messageSet | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, true); @@ -191,17 +191,17 @@ TEST_CASE("ForwardInputsSingleMessageSingleRouteAtEOS") proxy.bind({}, {}, routes, findChannelByName, nullptr); - std::vector currentSetOfInputs; - MessageSet messageSet; + std::vector> currentSetOfInputs; + std::vector 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.messages.emplace_back(std::move(header)); - messageSet.messages.emplace_back(std::move(payload)); - REQUIRE((messageSet.messages | count_parts{}) == 1); + messageSet.emplace_back(std::move(header)); + messageSet.emplace_back(std::move(payload)); + REQUIRE((messageSet | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); @@ -250,17 +250,17 @@ TEST_CASE("ForwardInputsSingleMessageSingleRouteWithOldestPossible") proxy.bind({}, {}, routes, findChannelByName, nullptr); - std::vector currentSetOfInputs; - MessageSet messageSet; + std::vector> currentSetOfInputs; + std::vector 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.messages.emplace_back(std::move(header)); - messageSet.messages.emplace_back(std::move(payload)); - REQUIRE((messageSet.messages | count_parts{}) == 1); + messageSet.emplace_back(std::move(header)); + messageSet.emplace_back(std::move(payload)); + REQUIRE((messageSet | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); @@ -317,16 +317,16 @@ TEST_CASE("ForwardInputsSingleMessageMultipleRoutes") proxy.bind({}, {}, routes, findChannelByName, nullptr); - std::vector currentSetOfInputs; - MessageSet messageSet; + std::vector> currentSetOfInputs; + std::vector 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.messages.emplace_back(std::move(header)); - messageSet.messages.emplace_back(std::move(payload)); - REQUIRE((messageSet.messages | count_parts{}) == 1); + messageSet.emplace_back(std::move(header)); + messageSet.emplace_back(std::move(payload)); + REQUIRE((messageSet | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); @@ -381,16 +381,16 @@ TEST_CASE("ForwardInputsSingleMessageMultipleRoutesExternals") proxy.bind({}, {}, routes, findChannelByName, nullptr); - std::vector currentSetOfInputs; - MessageSet messageSet; + std::vector> currentSetOfInputs; + std::vector 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.messages.emplace_back(std::move(header)); - messageSet.messages.emplace_back(std::move(payload)); - REQUIRE((messageSet.messages | count_parts{}) == 1); + messageSet.emplace_back(std::move(header)); + messageSet.emplace_back(std::move(payload)); + REQUIRE((messageSet | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); @@ -452,23 +452,23 @@ TEST_CASE("ForwardInputsMultiMessageMultipleRoutes") proxy.bind({}, {}, routes, findChannelByName, nullptr); - std::vector currentSetOfInputs; + 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.messages.emplace_back(std::move(header1)); - messageSet1.messages.emplace_back(std::move(payload1)); - REQUIRE((messageSet1.messages | count_parts{}) == 1); + std::vector messageSet1; + messageSet1.emplace_back(std::move(header1)); + messageSet1.emplace_back(std::move(payload1)); + REQUIRE((messageSet1 | count_parts{}) == 1); auto header2 = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh2, dph}); - MessageSet messageSet2; - messageSet2.messages.emplace_back(std::move(header2)); - messageSet2.messages.emplace_back(std::move(payload2)); - REQUIRE((messageSet2.messages | count_parts{}) == 1); + std::vector messageSet2; + messageSet2.emplace_back(std::move(header2)); + messageSet2.emplace_back(std::move(payload2)); + REQUIRE((messageSet2 | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet1)); currentSetOfInputs.emplace_back(std::move(messageSet2)); REQUIRE(currentSetOfInputs.size() == 2); @@ -525,16 +525,16 @@ TEST_CASE("ForwardInputsSingleMessageMultipleRoutesOnlyOneMatches") proxy.bind({}, {}, routes, findChannelByName, nullptr); - std::vector currentSetOfInputs; - MessageSet messageSet; + std::vector> currentSetOfInputs; + std::vector 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.messages.emplace_back(std::move(header)); - messageSet.messages.emplace_back(std::move(payload)); - REQUIRE((messageSet.messages | count_parts{}) == 1); + messageSet.emplace_back(std::move(header)); + messageSet.emplace_back(std::move(payload)); + REQUIRE((messageSet | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); @@ -596,8 +596,8 @@ TEST_CASE("ForwardInputsSplitPayload") proxy.bind({}, {}, routes, findChannelByName, nullptr); - std::vector currentSetOfInputs; - MessageSet messageSet; + std::vector> currentSetOfInputs; + std::vector messageSet; auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); fair::mq::MessagePtr payload1(transport->CreateMessage()); @@ -612,14 +612,13 @@ TEST_CASE("ForwardInputsSplitPayload") return std::move(messages[t]); }; for (size_t i = 0; i < 3; ++i) { - messageSet.messages.emplace_back(fillMessages(i)); + messageSet.emplace_back(fillMessages(i)); } auto header2 = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh2, dph}); - PartRef part{std::move(header2), transport->CreateMessage()}; - messageSet.messages.emplace_back(std::move(part.header)); - messageSet.messages.emplace_back(std::move(part.payload)); + messageSet.emplace_back(std::move(header2)); + messageSet.emplace_back(transport->CreateMessage()); - REQUIRE((messageSet.messages | count_parts{}) == 2); + REQUIRE((messageSet | count_parts{}) == 2); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); @@ -731,16 +730,16 @@ TEST_CASE("ForwardInputEOSSingleRoute") proxy.bind({}, {}, routes, findChannelByName, nullptr); - std::vector currentSetOfInputs; - MessageSet messageSet; + std::vector> currentSetOfInputs; + std::vector 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.messages.emplace_back(std::move(header)); - messageSet.messages.emplace_back(std::move(payload)); - REQUIRE((messageSet.messages | count_parts{}) == 1); + messageSet.emplace_back(std::move(header)); + messageSet.emplace_back(std::move(payload)); + REQUIRE((messageSet | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); @@ -777,16 +776,16 @@ TEST_CASE("ForwardInputOldestPossibleSingleRoute") proxy.bind({}, {}, routes, findChannelByName, nullptr); - std::vector currentSetOfInputs; - MessageSet messageSet; + std::vector> currentSetOfInputs; + std::vector 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.messages.emplace_back(std::move(header)); - messageSet.messages.emplace_back(std::move(payload)); - REQUIRE((messageSet.messages | count_parts{}) == 1); + messageSet.emplace_back(std::move(header)); + messageSet.emplace_back(std::move(payload)); + REQUIRE((messageSet | count_parts{}) == 1); currentSetOfInputs.emplace_back(std::move(messageSet)); auto result = o2::framework::DataProcessingHelpers::routeForwardedMessageSet(proxy, currentSetOfInputs, copyByDefault, consume); diff --git a/Framework/Core/test/test_MessageSet.cxx b/Framework/Core/test/test_MessageSet.cxx index d62a804e7681d..caa9a60323306 100644 --- a/Framework/Core/test/test_MessageSet.cxx +++ b/Framework/Core/test/test_MessageSet.cxx @@ -11,9 +11,9 @@ #include #include -#include "Framework/MessageSet.h" #include "Framework/DataModelViews.h" #include "Framework/DataProcessingHeader.h" +#include "Framework/PartRef.h" #include "Headers/Stack.h" #include "Headers/DataHeader.h" #include "MemoryResources/MemoryResources.h" @@ -23,7 +23,7 @@ using namespace o2::framework; TEST_CASE("MessageSet") { - o2::framework::MessageSet msgSet; + std::vector messages; o2::header::DataHeader dh{}; dh.splitPayloadParts = 0; dh.splitPayloadIndex = 0; @@ -37,21 +37,18 @@ TEST_CASE("MessageSet") ptrs.emplace_back(std::move(header)); ptrs.emplace_back(std::move(msg2)); for (size_t i = 0; i < 2; ++i) { - msgSet.messages.emplace_back(std::move(ptrs[i])); + messages.emplace_back(std::move(ptrs[i])); } - REQUIRE(msgSet.messages.size() == 2); - REQUIRE((msgSet.messages | count_payloads{}) == 1); - REQUIRE((msgSet.messages | get_dataref_indices{0, 0}).headerIdx == 0); - REQUIRE((msgSet.messages | get_dataref_indices{0, 0}).payloadIdx == 1); - REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); - REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); - CHECK_THROWS((msgSet.messages | get_pair{1})); - REQUIRE((msgSet.messages | get_num_payloads{0}) == 1); - REQUIRE((msgSet.messages | count_parts{}) == 1); - // messages: [hdr, pl] — one pair - REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); - REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); + REQUIRE(messages.size() == 2); + REQUIRE((messages | count_payloads{}) == 1); + REQUIRE((messages | get_dataref_indices{0, 0}).headerIdx == 0); + REQUIRE((messages | get_dataref_indices{0, 0}).payloadIdx == 1); + REQUIRE((messages | get_pair{0}).headerIdx == 0); + REQUIRE((messages | get_pair{0}).payloadIdx == 1); + CHECK_THROWS((messages | get_pair{1})); + REQUIRE((messages | get_num_payloads{0}) == 1); + REQUIRE((messages | count_parts{}) == 1); } TEST_CASE("MessageSetWithFunction") @@ -68,17 +65,20 @@ TEST_CASE("MessageSetWithFunction") std::unique_ptr msg2(nullptr); ptrs.emplace_back(std::move(header)); 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.messages | count_payloads{}) == 1); - REQUIRE((msgSet.messages | get_dataref_indices{0, 0}).headerIdx == 0); - REQUIRE((msgSet.messages | get_dataref_indices{0, 0}).payloadIdx == 1); - REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); - REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); - CHECK_THROWS((msgSet.messages | get_pair{1})); - REQUIRE((msgSet.messages | get_num_payloads{0}) == 1); - REQUIRE((msgSet.messages | count_parts{}) == 1); + std::vector messages; + for (size_t i = 0; i < 2; ++i) { + messages.emplace_back(std::move(ptrs[i])); + } + + REQUIRE(messages.size() == 2); + REQUIRE((messages | count_payloads{}) == 1); + REQUIRE((messages | get_dataref_indices{0, 0}).headerIdx == 0); + REQUIRE((messages | get_dataref_indices{0, 0}).payloadIdx == 1); + REQUIRE((messages | get_pair{0}).headerIdx == 0); + REQUIRE((messages | get_pair{0}).payloadIdx == 1); + CHECK_THROWS((messages | get_pair{1})); + REQUIRE((messages | get_num_payloads{0}) == 1); + REQUIRE((messages | count_parts{}) == 1); } TEST_CASE("MessageSetWithMultipart") @@ -97,46 +97,40 @@ TEST_CASE("MessageSetWithMultipart") ptrs.emplace_back(std::move(header)); 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.messages | count_payloads{}) == 2); - REQUIRE((msgSet.messages | get_dataref_indices{0, 0}).headerIdx == 0); - REQUIRE((msgSet.messages | get_dataref_indices{0, 0}).payloadIdx == 1); - REQUIRE((msgSet.messages | get_dataref_indices{0, 1}).headerIdx == 0); - REQUIRE((msgSet.messages | get_dataref_indices{0, 1}).payloadIdx == 2); - REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); - REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); - REQUIRE((msgSet.messages | get_pair{1}).headerIdx == 0); - REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 2); - CHECK_THROWS((msgSet.messages | get_pair{2})); - REQUIRE((msgSet.messages | get_num_payloads{0}) == 2); - REQUIRE((msgSet.messages | count_parts{}) == 1); - // messages: [hdr, pl0, pl1] — one header, two payloads - REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); - REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); - REQUIRE((msgSet.messages | get_pair{1}).headerIdx == 0); - REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 2); + std::vector messages; + for (size_t i = 0; i < 3; ++i) { + messages.emplace_back(std::move(ptrs[i])); + } + + REQUIRE(messages.size() == 3); + REQUIRE((messages | count_payloads{}) == 2); + REQUIRE((messages | get_dataref_indices{0, 0}).headerIdx == 0); + REQUIRE((messages | get_dataref_indices{0, 0}).payloadIdx == 1); + REQUIRE((messages | get_dataref_indices{0, 1}).headerIdx == 0); + REQUIRE((messages | get_dataref_indices{0, 1}).payloadIdx == 2); + REQUIRE((messages | get_pair{0}).headerIdx == 0); + REQUIRE((messages | get_pair{0}).payloadIdx == 1); + REQUIRE((messages | get_pair{1}).headerIdx == 0); + REQUIRE((messages | get_pair{1}).payloadIdx == 2); + CHECK_THROWS((messages | get_pair{2})); + REQUIRE((messages | get_num_payloads{0}) == 2); + REQUIRE((messages | count_parts{}) == 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.messages.emplace_back(std::move(ref.header)); - msgSet.messages.emplace_back(std::move(ref.payload)); + std::vector messages; + messages.emplace_back(std::move(ref.header)); + messages.emplace_back(std::move(ref.payload)); - REQUIRE(msgSet.messages.size() == 2); + REQUIRE(messages.size() == 2); } TEST_CASE("MessageSetAddMultiple") { - std::vector ptrs; o2::header::DataHeader dh1{}; dh1.splitPayloadParts = 0; dh1.splitPayloadIndex = 0; @@ -148,109 +142,99 @@ TEST_CASE("MessageSetAddMultiple") dh3.splitPayloadIndex = 2; o2::framework::DataProcessingHeader dph{0, 1}; auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); - fair::mq::MessagePtr payload(transport->CreateMessage()); auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); fair::mq::MessagePtr header1 = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh1, dph}); fair::mq::MessagePtr header2 = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh2, dph}); fair::mq::MessagePtr header3 = o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh3, dph}); - std::unique_ptr msg2(nullptr); - std::unique_ptr msg3(nullptr); - PartRef ref{std::move(header1), std::move(msg2)}; - o2::framework::MessageSet msgSet; - msgSet.messages.emplace_back(std::move(ref.header)); - msgSet.messages.emplace_back(std::move(ref.payload)); - PartRef ref2{std::move(header2), std::move(msg2)}; - msgSet.messages.emplace_back(std::move(ref2.header)); - msgSet.messages.emplace_back(std::move(ref2.payload)); - std::vector msgs; - msgs.push_back(std::move(header3)); - msgs.push_back(std::unique_ptr(nullptr)); - msgs.push_back(std::unique_ptr(nullptr)); - for (size_t i = 0; i < 3; ++i) { - msgSet.messages.emplace_back(std::move(msgs[i])); - } - - REQUIRE(msgSet.messages.size() == 7); - - REQUIRE((msgSet.messages | count_payloads{}) == 4); - REQUIRE((msgSet.messages | get_dataref_indices{0, 0}).headerIdx == 0); - REQUIRE((msgSet.messages | get_dataref_indices{0, 0}).payloadIdx == 1); - REQUIRE((msgSet.messages | get_dataref_indices{1, 0}).headerIdx == 2); - REQUIRE((msgSet.messages | get_dataref_indices{1, 0}).payloadIdx == 3); - REQUIRE((msgSet.messages | get_dataref_indices{2, 0}).headerIdx == 4); - REQUIRE((msgSet.messages | get_dataref_indices{2, 0}).payloadIdx == 5); - REQUIRE((msgSet.messages | get_dataref_indices{2, 1}).headerIdx == 4); - REQUIRE((msgSet.messages | get_dataref_indices{2, 1}).payloadIdx == 6); - REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); - REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); - REQUIRE((msgSet.messages | get_pair{1}).headerIdx == 2); - REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 3); - REQUIRE((msgSet.messages | get_pair{2}).headerIdx == 4); - REQUIRE((msgSet.messages | get_pair{2}).payloadIdx == 5); - REQUIRE((msgSet.messages | get_pair{3}).headerIdx == 4); - REQUIRE((msgSet.messages | get_pair{3}).payloadIdx == 6); - REQUIRE((msgSet.messages | get_num_payloads{0}) == 1); - REQUIRE((msgSet.messages | get_num_payloads{1}) == 1); - REQUIRE((msgSet.messages | get_num_payloads{2}) == 2); - REQUIRE((msgSet.messages | count_parts{}) == 3); - REQUIRE((msgSet.messages | count_payloads{}) == 4); + std::vector messages; + // part 0: dh1 (splitPayloadParts=0) — standard pair + messages.emplace_back(std::move(header1)); + messages.emplace_back(std::unique_ptr(nullptr)); + // part 1: dh2 (splitPayloadParts=1) — traditional split, one pair + messages.emplace_back(std::move(header2)); + messages.emplace_back(std::unique_ptr(nullptr)); + // part 2: dh3 (splitPayloadParts=2, splitPayloadIndex=2) — multi-payload, two payloads + messages.emplace_back(std::move(header3)); + messages.emplace_back(std::unique_ptr(nullptr)); + messages.emplace_back(std::unique_ptr(nullptr)); + + REQUIRE(messages.size() == 7); + + REQUIRE((messages | count_payloads{}) == 4); + REQUIRE((messages | get_dataref_indices{0, 0}).headerIdx == 0); + REQUIRE((messages | get_dataref_indices{0, 0}).payloadIdx == 1); + REQUIRE((messages | get_dataref_indices{1, 0}).headerIdx == 2); + REQUIRE((messages | get_dataref_indices{1, 0}).payloadIdx == 3); + REQUIRE((messages | get_dataref_indices{2, 0}).headerIdx == 4); + REQUIRE((messages | get_dataref_indices{2, 0}).payloadIdx == 5); + REQUIRE((messages | get_dataref_indices{2, 1}).headerIdx == 4); + REQUIRE((messages | get_dataref_indices{2, 1}).payloadIdx == 6); + REQUIRE((messages | get_pair{0}).headerIdx == 0); + REQUIRE((messages | get_pair{0}).payloadIdx == 1); + REQUIRE((messages | get_pair{1}).headerIdx == 2); + REQUIRE((messages | get_pair{1}).payloadIdx == 3); + REQUIRE((messages | get_pair{2}).headerIdx == 4); + REQUIRE((messages | get_pair{2}).payloadIdx == 5); + REQUIRE((messages | get_pair{3}).headerIdx == 4); + REQUIRE((messages | get_pair{3}).payloadIdx == 6); + REQUIRE((messages | get_num_payloads{0}) == 1); + REQUIRE((messages | get_num_payloads{1}) == 1); + REQUIRE((messages | get_num_payloads{2}) == 2); + REQUIRE((messages | count_parts{}) == 3); + REQUIRE((messages | count_payloads{}) == 4); } TEST_CASE("GetHeaderPayloadOperators") { - // Validates that get_header{part} / get_payload{part, 0} pipe operators on .messages - // correctly replace the removed header(part) / payload(part) methods, - // including access to parts at index > 0. + // Validates that get_header{part} / get_payload{part, 0} pipe operators + // correctly return the right messages, including access to parts at index > 0. o2::framework::DataProcessingHeader dph{0, 1}; auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); - o2::framework::MessageSet msgSet; + std::vector messages; // Add two separate header-payload pairs for (size_t part = 0; part < 2; ++part) { o2::header::DataHeader dh{}; dh.dataDescription = "CLUSTERS"; dh.dataOrigin = "TPC"; - dh.subSpecification = part; // use part index as subSpecification to distinguish + dh.subSpecification = part; dh.splitPayloadParts = 1; dh.splitPayloadIndex = 0; - std::vector ptrs; - ptrs.emplace_back(o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph})); - ptrs.emplace_back(transport->CreateMessage(100 + part * 100)); // 100 and 200 bytes - msgSet.add([&ptrs](size_t i) -> fair::mq::MessagePtr& { return ptrs[i]; }, 2); + messages.emplace_back(o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph})); + messages.emplace_back(transport->CreateMessage(100 + part * 100)); } - REQUIRE(msgSet.messages.size() == 4); + REQUIRE(messages.size() == 4); // Validate part 0 - auto& hdr0 = msgSet.messages | get_header{0}; + auto& hdr0 = messages | get_header{0}; REQUIRE(hdr0.get() != nullptr); auto* dh0 = o2::header::get(hdr0->GetData()); REQUIRE(dh0 != nullptr); REQUIRE(dh0->subSpecification == 0); - auto& pl0 = msgSet.messages | get_payload{0, 0}; + auto& pl0 = messages | get_payload{0, 0}; REQUIRE(pl0.get() != nullptr); REQUIRE(pl0->GetSize() == 100); // Validate part 1 - auto& hdr1 = msgSet.messages | get_header{1}; + auto& hdr1 = messages | get_header{1}; REQUIRE(hdr1.get() != nullptr); auto* dh1 = o2::header::get(hdr1->GetData()); REQUIRE(dh1 != nullptr); REQUIRE(dh1->subSpecification == 1); - auto& pl1 = msgSet.messages | get_payload{1, 0}; + auto& pl1 = messages | get_payload{1, 0}; REQUIRE(pl1.get() != nullptr); REQUIRE(pl1->GetSize() == 200); - REQUIRE((msgSet.messages | count_parts{}) == 2); - REQUIRE((msgSet.messages | count_payloads{}) == 2); - // messages: [hdr0, pl0, hdr1, pl1] — two standard pairs - REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); - REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); - REQUIRE((msgSet.messages | get_pair{1}).headerIdx == 2); - REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 3); + REQUIRE((messages | count_parts{}) == 2); + REQUIRE((messages | count_payloads{}) == 2); + REQUIRE((messages | get_pair{0}).headerIdx == 0); + REQUIRE((messages | get_pair{0}).payloadIdx == 1); + REQUIRE((messages | get_pair{1}).headerIdx == 2); + REQUIRE((messages | get_pair{1}).payloadIdx == 3); } TEST_CASE("GetHeaderPayloadMultiPayload") @@ -264,7 +248,7 @@ TEST_CASE("GetHeaderPayloadMultiPayload") auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); - o2::framework::MessageSet msgSet; + std::vector messages; // Part 0: standard header-payload pair { @@ -274,10 +258,8 @@ TEST_CASE("GetHeaderPayloadMultiPayload") dh.subSpecification = 0; dh.splitPayloadParts = 1; dh.splitPayloadIndex = 0; - std::vector ptrs; - ptrs.emplace_back(o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph})); - ptrs.emplace_back(transport->CreateMessage(100)); - msgSet.add([&ptrs](size_t i) -> fair::mq::MessagePtr& { return ptrs[i]; }, 2); + messages.emplace_back(o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph})); + messages.emplace_back(transport->CreateMessage(100)); } // Part 1: one header with 3 payloads (splitPayloadIndex == splitPayloadParts) @@ -287,81 +269,67 @@ TEST_CASE("GetHeaderPayloadMultiPayload") dh.dataOrigin = "TPC"; dh.subSpecification = 1; dh.splitPayloadParts = 3; - dh.splitPayloadIndex = 3; // signals multi-payload layout - std::vector ptrs; - ptrs.emplace_back(o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph})); - ptrs.emplace_back(transport->CreateMessage(200)); - ptrs.emplace_back(transport->CreateMessage(300)); - ptrs.emplace_back(transport->CreateMessage(400)); - msgSet.add([&ptrs](size_t i) -> fair::mq::MessagePtr& { return ptrs[i]; }, 4); + dh.splitPayloadIndex = 3; + messages.emplace_back(o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph})); + messages.emplace_back(transport->CreateMessage(200)); + messages.emplace_back(transport->CreateMessage(300)); + messages.emplace_back(transport->CreateMessage(400)); } // messages: [hdr0, pl0, hdr1, pl1_0, pl1_1, pl1_2] - REQUIRE(msgSet.messages.size() == 6); + REQUIRE(messages.size() == 6); - // Part 0: standard - auto& hdr0 = msgSet.messages | get_header{0}; + // Part 0 + auto& hdr0 = messages | get_header{0}; REQUIRE(hdr0.get() != nullptr); auto* dh0 = o2::header::get(hdr0->GetData()); REQUIRE(dh0->subSpecification == 0); - auto& pl0 = msgSet.messages | get_payload{0, 0}; + auto& pl0 = messages | get_payload{0, 0}; REQUIRE(pl0.get() != nullptr); REQUIRE(pl0->GetSize() == 100); // Part 1: multi-payload header - auto& hdr1 = msgSet.messages | get_header{1}; + auto& hdr1 = messages | get_header{1}; REQUIRE(hdr1.get() != nullptr); auto* dh1 = o2::header::get(hdr1->GetData()); REQUIRE(dh1->subSpecification == 1); - // get_payload{1, 0} — first payload of part 1 - auto& pl1_0 = msgSet.messages | get_payload{1, 0}; + auto& pl1_0 = messages | get_payload{1, 0}; REQUIRE(pl1_0.get() != nullptr); REQUIRE(pl1_0->GetSize() == 200); - // get_payload{1, 1} — second payload of part 1 (nonzero, nonzero) - auto& pl1_1 = msgSet.messages | get_payload{1, 1}; + auto& pl1_1 = messages | get_payload{1, 1}; REQUIRE(pl1_1.get() != nullptr); REQUIRE(pl1_1->GetSize() == 300); - // get_payload{1, 2} — third payload of part 1 (nonzero, nonzero) - auto& pl1_2 = msgSet.messages | get_payload{1, 2}; + auto& pl1_2 = messages | get_payload{1, 2}; REQUIRE(pl1_2.get() != nullptr); REQUIRE(pl1_2->GetSize() == 400); - // count_payloads should report 4 total (1 from part 0 + 3 from part 1) - REQUIRE((msgSet.messages | count_payloads{}) == 4); - // count_parts should report 2 (one per header) - REQUIRE((msgSet.messages | count_parts{}) == 2); - // get_num_payloads for part 1 should be 3 - REQUIRE((msgSet.messages | get_num_payloads{1}) == 3); - - REQUIRE((msgSet.messages | get_num_payloads{0}) == 1); - REQUIRE((msgSet.messages | get_num_payloads{1}) == 3); - REQUIRE((msgSet.messages | count_parts{}) == 2); - REQUIRE((msgSet.messages | count_payloads{}) == 4); - // messages: [hdr0, pl0, hdr1, pl1_0, pl1_1, pl1_2] - REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); - REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); - REQUIRE((msgSet.messages | get_pair{1}).headerIdx == 2); - REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 3); - REQUIRE((msgSet.messages | get_pair{2}).headerIdx == 2); - REQUIRE((msgSet.messages | get_pair{2}).payloadIdx == 4); - REQUIRE((msgSet.messages | get_pair{3}).headerIdx == 2); - REQUIRE((msgSet.messages | get_pair{3}).payloadIdx == 5); + REQUIRE((messages | get_num_payloads{0}) == 1); + REQUIRE((messages | get_num_payloads{1}) == 3); + REQUIRE((messages | count_parts{}) == 2); + REQUIRE((messages | count_payloads{}) == 4); + REQUIRE((messages | get_pair{0}).headerIdx == 0); + REQUIRE((messages | get_pair{0}).payloadIdx == 1); + REQUIRE((messages | get_pair{1}).headerIdx == 2); + REQUIRE((messages | get_pair{1}).payloadIdx == 3); + REQUIRE((messages | get_pair{2}).headerIdx == 2); + REQUIRE((messages | get_pair{2}).payloadIdx == 4); + REQUIRE((messages | get_pair{3}).headerIdx == 2); + REQUIRE((messages | get_pair{3}).payloadIdx == 5); } TEST_CASE("TraditionalSplitParts") { // Validates operators with traditional split parts layout: // 3 (header, payload) pairs where splitPayloadParts=3, splitPayloadIndex=0,1,2 - // This is ONE logical part with 3 subparts. // Memory layout: [hdr0, pl0, hdr1, pl1, hdr2, pl2] o2::framework::DataProcessingHeader dph{0, 1}; auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); auto channelAlloc = o2::pmr::getTransportAllocator(transport.get()); - o2::framework::MessageSet msgSet; + std::vector messages; for (size_t i = 0; i < 3; ++i) { o2::header::DataHeader dh{}; @@ -370,57 +338,42 @@ TEST_CASE("TraditionalSplitParts") dh.subSpecification = 0; dh.splitPayloadParts = 3; dh.splitPayloadIndex = i; - std::vector ptrs; - ptrs.emplace_back(o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph})); - ptrs.emplace_back(transport->CreateMessage(100 * (i + 1))); - msgSet.add([&ptrs](size_t idx) -> fair::mq::MessagePtr& { return ptrs[idx]; }, 2); + messages.emplace_back(o2::pmr::getMessage(o2::header::Stack{channelAlloc, dh, dph})); + messages.emplace_back(transport->CreateMessage(100 * (i + 1))); } - REQUIRE(msgSet.messages.size() == 6); + REQUIRE(messages.size() == 6); - // count_payloads: 3 traditional split parts = 3 payloads - REQUIRE((msgSet.messages | count_payloads{}) == 3); - // count_parts: one logical entity split into 3 pairs = 3 parts - REQUIRE((msgSet.messages | count_parts{}) == 3); + REQUIRE((messages | count_payloads{}) == 3); + REQUIRE((messages | count_parts{}) == 3); - // Each traditional split pair is a separate part, matching MessageSet::header(part) semantics for (size_t i = 0; i < 3; ++i) { - auto& hdr = msgSet.messages | get_header{i}; + auto& hdr = messages | get_header{i}; REQUIRE(hdr.get() != nullptr); auto* dh = o2::header::get(hdr->GetData()); REQUIRE(dh != nullptr); REQUIRE(dh->splitPayloadIndex == i); - auto& pl = msgSet.messages | get_payload{i, 0}; + auto& pl = messages | get_payload{i, 0}; REQUIRE(pl.get() != nullptr); REQUIRE(pl->GetSize() == 100 * (i + 1)); } - // get_dataref_indices: each part maps to its own (header, payload) pair for (size_t i = 0; i < 3; ++i) { - auto indices = msgSet.messages | get_dataref_indices{i, 0}; + auto indices = messages | get_dataref_indices{i, 0}; REQUIRE(indices.headerIdx == 2 * i); REQUIRE(indices.payloadIdx == 2 * i + 1); } - // get_pair: same as get_dataref_indices for traditional split for (size_t i = 0; i < 3; ++i) { - auto indices = msgSet.messages | get_pair{i}; + auto indices = messages | get_pair{i}; REQUIRE(indices.headerIdx == 2 * i); REQUIRE(indices.payloadIdx == 2 * i + 1); } - // get_num_payloads: each traditional split pair has 1 payload for (size_t i = 0; i < 3; ++i) { - REQUIRE((msgSet.messages | get_num_payloads{i}) == 1); + REQUIRE((messages | get_num_payloads{i}) == 1); } - REQUIRE((msgSet.messages | count_parts{}) == 3); - REQUIRE((msgSet.messages | count_payloads{}) == 3); - // messages: [hdr0, pl0, hdr1, pl1, hdr2, pl2] — three traditional split pairs - REQUIRE((msgSet.messages | get_pair{0}).headerIdx == 0); - REQUIRE((msgSet.messages | get_pair{0}).payloadIdx == 1); - REQUIRE((msgSet.messages | get_pair{1}).headerIdx == 2); - REQUIRE((msgSet.messages | get_pair{1}).payloadIdx == 3); - REQUIRE((msgSet.messages | get_pair{2}).headerIdx == 4); - REQUIRE((msgSet.messages | get_pair{2}).payloadIdx == 5); + REQUIRE((messages | count_parts{}) == 3); + REQUIRE((messages | count_payloads{}) == 3); } From 33b11d46481332f1817393dad1b040c7c49ad28e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Wed, 25 Mar 2026 11:46:13 +0100 Subject: [PATCH 436/701] Add optional dependency for Acts package --- dependencies/O2Dependencies.cmake | 3 +++ 1 file changed, 3 insertions(+) diff --git a/dependencies/O2Dependencies.cmake b/dependencies/O2Dependencies.cmake index 850ba0b909acc..b46dabb5690ff 100644 --- a/dependencies/O2Dependencies.cmake +++ b/dependencies/O2Dependencies.cmake @@ -143,6 +143,9 @@ set_package_properties(fmt PROPERTIES TYPE REQUIRED) find_package(nlohmann_json) set_package_properties(nlohmann_json PROPERTIES TYPE REQUIRED) +find_package(Acts) +set_package_properties(Acts PROPERTIES TYPE OPTIONAL) + find_package(Boost 1.70 COMPONENTS container thread From 198457e3462747b8eaf485ac86ec7d2617c61e98 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Mon, 30 Mar 2026 17:14:01 +0200 Subject: [PATCH 437/701] Add fcolamar as owner for ALICE3 upgrades (#15243) --- CODEOWNERS | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CODEOWNERS b/CODEOWNERS index 369a7cf8a8463..f54738e2ce4e3 100644 --- a/CODEOWNERS +++ b/CODEOWNERS @@ -73,7 +73,7 @@ /Detectors/TPC @davidrohr @wiechula @shahor02 /Detectors/TRD @f3sch @bazinski @wille10 /Detectors/Upgrades @mconcas -/Detectors/Upgrades/ALICE3 @mconcas @njacazio +/Detectors/Upgrades/ALICE3 @mconcas @njacazio @fcolamar /Detectors/Upgrades/ITS3 @fgrosa @arossi81 @mconcas @f3sch /Detectors/ZDC @coppedis @cortesep /Detectors/CTF @shahor02 From 1b673ecbb297411272924776583a67a7a5335a81 Mon Sep 17 00:00:00 2001 From: Marco van Leeuwen Date: Mon, 30 Mar 2026 17:22:46 +0200 Subject: [PATCH 438/701] [ALICE3] Fix extrusions in forward tracker (#15242) --- Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx | 2 +- Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx index 453d90501802e..333599c85eab6 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx @@ -390,7 +390,7 @@ void FT3Layer::createLayer(TGeoVolume* motherVolume) std::string separationLayerName = "FT3SeparationLayer" + std::to_string(mDirection) + std::to_string(mLayerNumber); TGeoMedium* medAir = gGeoManager->GetMedium("FT3_AIR$"); - TGeoTube* layer = new TGeoTube(mInnerRadius, mOuterRadius, 12 * mChipThickness / 2); // additional "thickness factor" is to avoid sub-volumes crossing the mother layer + TGeoTube* layer = new TGeoTube(mInnerRadius - 0.1, mOuterRadius + 0.1, 1.5); // Add a little additional room in radius; Try with 1.5 cm thickness TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); layerVol->SetLineColor(kYellow + 2); diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx index 0d0983958c46f..118078ebf7100 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx @@ -445,4 +445,4 @@ void BTOFLayer::createLayer(TGeoVolume* motherVolume) } } // namespace iotof -} // namespace o2 \ No newline at end of file +} // namespace o2 From d87ad11b206f555c3e4beab9a5a9823a6471010c Mon Sep 17 00:00:00 2001 From: shahoian Date: Mon, 30 Mar 2026 18:37:13 +0200 Subject: [PATCH 439/701] Fix typo on the TPC A||C side contribution check --- Framework/Core/include/Framework/AnalysisDataModel.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Framework/Core/include/Framework/AnalysisDataModel.h b/Framework/Core/include/Framework/AnalysisDataModel.h index 9f48685820634..2869565454294 100644 --- a/Framework/Core/include/Framework/AnalysisDataModel.h +++ b/Framework/Core/include/Framework/AnalysisDataModel.h @@ -413,7 +413,7 @@ DECLARE_SOA_DYNAMIC_COLUMN(HasTPCSideC, hasTPCSideC, //! Run 3: Has this track T 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); }); + [](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 From aa5d3c6ec3c67a0c15ae53a3705fa58eb6eee2e7 Mon Sep 17 00:00:00 2001 From: spulawsk Date: Mon, 30 Mar 2026 23:00:48 +0200 Subject: [PATCH 440/701] FT0: update Digitizer signal shape and trigger logic; FV0: update trigger logic in digitizer (#15209) * FT0: tune signal shape and trigger tunning for MC * apply clang-format * Update FV0 trigger settings and digitizer logic * FV0: update Digitizer trigger handling * Update parameter comments and default settings for Run 3 * Clang formating * Default trigger settings set for pp. PbPb will be sent via parameters * Default trigger settings set for pp. PbPb will be sent via parameters --------- Co-authored-by: Szymon Pulawski --- .../FT0/base/include/FT0Base/FT0DigParam.h | 9 +- .../FIT/FT0/simulation/src/Digitizer.cxx | 185 ++++++++++++++++-- .../include/FV0Simulation/FV0DigParam.h | 4 + .../FIT/FV0/simulation/src/Digitizer.cxx | 38 ++-- 4 files changed, 203 insertions(+), 33 deletions(-) diff --git a/Detectors/FIT/FT0/base/include/FT0Base/FT0DigParam.h b/Detectors/FIT/FT0/base/include/FT0Base/FT0DigParam.h index 2bf774859aa22..074d91bb04b27 100644 --- a/Detectors/FIT/FT0/base/include/FT0Base/FT0DigParam.h +++ b/Detectors/FIT/FT0/base/include/FT0Base/FT0DigParam.h @@ -31,8 +31,8 @@ struct FT0DigParam : o2::conf::ConfigurableParamHelper { float mAmpRecordUp = 15; // to [ns] float hitTimeOffsetA = 0; ///< hit time offset on the A side [ns] float hitTimeOffsetC = 0; ///< hit time offset on the C side [ns] - int mtrg_central_trh = 600.; // channels - int mtrg_semicentral_trh = 300.; // channels + int mtrg_central_trh = 40; // Tclu units (40 for pp and 1433 for PbPb in Run3) + int mtrg_semicentral_trh = 20; // Tclu units (20 for pp and 35 for PbPb in Run3) float mMip_in_V = 7; // MIP to mV float mPe_in_mip = 0.004; // invserse Np.e. in MIP 1./250. @@ -43,11 +43,12 @@ struct FT0DigParam : o2::conf::ConfigurableParamHelper { float mNoiseVar = 0.1; // noise level float mNoisePeriod = 1 / 0.9; // GHz low frequency noise period; short mTime_trg_gate = 153; // #channels as in TCM as in Pilot beams ('OR gate' setting in TCM tab in ControlServer) + short mTime_trg_vertex_gate = 100; // #channels as in TCM as in Pilot beams ('OR gate' setting in TCM tab in ControlServer) float mAmpThresholdForReco = 5; // only channels with amplitude higher will participate in calibration and collision time: 0.3 MIP short mTimeThresholdForReco = 1000; // only channels with time below will participate in calibration and collision time - float mMV_2_Nchannels = 2.2857143; // amplitude channel 7 mV ->16channels - float mMV_2_NchannelsInverse = 0.437499997; // inverse amplitude channel 7 mV ->16channels + float mMV_2_Nchannels = 2.; // amplitude channel 7 mV ->14channels + float mMV_2_NchannelsInverse = 0.5; // inverse amplitude channel 7 mV ->14channels (nowhere used) O2ParamDef(FT0DigParam, "FT0DigParam"); }; diff --git a/Detectors/FIT/FT0/simulation/src/Digitizer.cxx b/Detectors/FIT/FT0/simulation/src/Digitizer.cxx index aca012f1bc5a9..de432a85765c7 100644 --- a/Detectors/FIT/FT0/simulation/src/Digitizer.cxx +++ b/Detectors/FIT/FT0/simulation/src/Digitizer.cxx @@ -16,6 +16,13 @@ #include "CommonConstants/PhysicsConstants.h" #include "CommonDataFormat/InteractionRecord.h" +#include "DataFormatsFT0/LookUpTable.h" +#include "FT0Base/Constants.h" +#include +#include +#include +#include + #include "TMath.h" #include "TRandom.h" #include @@ -35,24 +42,84 @@ namespace o2::ft0 template Float signalForm_i(Float x) { - using namespace std; - Float const a = -0.45458; - Float const b = -0.83344945; - return x > Float(0) ? -(exp(b * x) - exp(a * x)) / Float(7.8446501) : Float(0); + float p0, p1, p2, p3, p4, p5, p6, p7; + p0 = 1.30853; + p1 = 0.516807; + p2 = 3.36714; + p3 = -1.01206; + p4 = 1.42832; + p5 = 1.1589; + p6 = 1.22019; + p7 = 0.426818; + + Double_t val = 0; + + if (x > p3) { + Double_t x1 = x - p3; + Double_t arg1 = (log(x1) - p0) / p1; + val += p2 * (1.0 / (x1 * p1 * sqrt(2 * TMath::Pi()))) * exp(-0.5 * arg1 * arg1); + } + + if (x > p7) { + Double_t x2 = x - p7; + Double_t arg2 = (log(x2) - p4) / p5; + val += p6 * (1.0 / (x2 * p5 * sqrt(2 * TMath::Pi()))) * exp(-0.5 * arg2 * arg2); + } + + return val; }; // integrated signal shape function inline float signalForm_integral(float x) { - using namespace std; - double const a = -0.45458; - double const b = -0.83344945; - if (x < 0) { - x = 0; + float p0, p1, p2, p3, p4, p5, p6, p7; + p0 = 1.30853; + p1 = 0.516807; + p2 = 3.36714; + p3 = -1.01206; + p4 = 1.42832; + p5 = 1.1589; + p6 = 1.22019; + p7 = 0.426818; + Double_t val = 0; + + if (x > p3) { + Double_t x1 = x - p3; + Double_t z1 = (log(x1) - p0) / (sqrt(2) * p1); + val += p2 * 0.5 * (1 + TMath::Erf(z1)); // norm1 * CDF1 } - return -(exp(b * x) / b - exp(a * x) / a) / 7.8446501; + + if (x > p7) { + Double_t x2 = x - p7; + Double_t z2 = (log(x2) - p4) / (sqrt(2) * p5); + val += p6 * 0.5 * (1 + TMath::Erf(z2)); // norm2 * CDF2 + } + + return val; +}; +/* +// signal shape function +template +Float signalForm_i(Float x) +{ +using namespace std; +Float const a = -0.45458; +Float const b = -0.83344945; +return x > Float(0) ? -(exp(b * x) - exp(a * x)) / Float(7.8446501) : Float(0); }; +// integrated signal shape function +inline float signalForm_integral(float x) +{ +using namespace std; +double const a = -0.45458; +double const b = -0.83344945; +if (x < 0) { + x = 0; +} +return -(exp(b * x) / b - exp(a * x) / a) / 7.8446501; +}; +*/ // SIMD version of the integrated signal shape function inline Vc::float_v signalForm_integralVc(Vc::float_v x) { @@ -249,8 +316,64 @@ void Digitizer::storeBC(BCCache& bc, if (bc.hits.empty()) { return; } + // Initialize mapping channelID -> PM hash and PM side (A/C) using FT0 LUT + static bool pmLutInitialized = false; + static std::array mChID2PMhash{}; + static std::map mMapPMhash2isAside; // hashed PM -> is A side + + if (!pmLutInitialized) { + std::map mapFEE2hash; // module name -> hashed PM id + uint8_t tcmHash = 0; + + const auto& lut = o2::ft0::SingleLUT::Instance().getVecMetadataFEE(); + auto lutSorted = lut; + std::sort(lutSorted.begin(), lutSorted.end(), + [](const auto& first, const auto& second) { return first.mModuleName < second.mModuleName; }); + + uint8_t binPos = 0; + for (const auto& lutEntry : lutSorted) { + const auto& moduleName = lutEntry.mModuleName; + const auto& moduleType = lutEntry.mModuleType; + const auto& strChID = lutEntry.mChannelID; + + auto [it, inserted] = mapFEE2hash.insert({moduleName, binPos}); + if (inserted) { + if (moduleName.find("PMA") != std::string::npos) { + mMapPMhash2isAside.insert({binPos, true}); + } else if (moduleName.find("PMC") != std::string::npos) { + mMapPMhash2isAside.insert({binPos, false}); + } + ++binPos; + } + + if (std::regex_match(strChID, std::regex("^[0-9]{1,3}$"))) { + int chID = std::stoi(strChID); + if (chID < o2::ft0::Constants::sNCHANNELS_PM) { + mChID2PMhash[chID] = mapFEE2hash[moduleName]; + } else { + LOG(fatal) << "Incorrect LUT entry: chID " << strChID << " | " << moduleName; + } + } else if (moduleType != "TCM") { + LOG(fatal) << "Non-TCM module w/o numerical chID: chID " << strChID << " | " << moduleName; + } else { // TCM + tcmHash = mapFEE2hash[moduleName]; + } + } + + pmLutInitialized = true; + } + int n_hit_A = 0, n_hit_C = 0, mean_time_A = 0, mean_time_C = 0; int summ_ampl_A = 0, summ_ampl_C = 0; + int sum_A_ampl = 0, sum_C_ampl = 0; + int nPMTs = mGeometry.NCellsA * 4 + mGeometry.NCellsC * 4; + std::vector sum_ampl_ipmt(nPMTs, 0); + // Per-PM summed charge (like in digits2trgFT0) + std::map mapPMhash2sumAmpl; + for (const auto& entry : mMapPMhash2isAside) { + mapPMhash2sumAmpl.insert({entry.first, 0}); + } + int vertex_time; const auto& params = FT0DigParam::Instance(); int first = digitsCh.size(), nStored = 0; @@ -297,6 +420,10 @@ void Digitizer::storeBC(BCCache& bc, if (is_time_in_signal_gate) { chain |= (1 << o2::ft0::ChannelData::EEventDataBit::kIsCFDinADCgate); chain |= (1 << o2::ft0::ChannelData::EEventDataBit::kIsEventInTVDC); + // Sum channel charge per PM (similar logic as in digits2trgFT0) + if (ipmt < o2::ft0::Constants::sNCHANNELS_PM) { + mapPMhash2sumAmpl[mChID2PMhash[static_cast(ipmt)]] += static_cast(amp); + } } digitsCh.emplace_back(ipmt, smeared_time, int(amp), chain); nStored++; @@ -308,6 +435,8 @@ void Digitizer::storeBC(BCCache& bc, continue; } + sum_ampl_ipmt[ipmt] += amp; + if (is_A_side) { n_hit_A++; summ_ampl_A += amp; @@ -318,17 +447,47 @@ void Digitizer::storeBC(BCCache& bc, mean_time_C += smeared_time; } } + + for (size_t i = 0; i < sum_ampl_ipmt.size(); i++) { + sum_ampl_ipmt[i] = sum_ampl_ipmt[i] >> 3; + if (i < 4 * mGeometry.NCellsA) { + sum_A_ampl += sum_ampl_ipmt[i]; + } else { + sum_C_ampl += sum_ampl_ipmt[i]; + } + } + + // Sum over PMs (using per-PM map) for debug/monitoring + int sum_PM_ampl_debug = 0; + int sum_PM_ampl_A_debug = 0; + int sum_PM_ampl_C_debug = 0; + for (const auto& entry : mapPMhash2sumAmpl) { + int pmAmpl = (entry.second >> 3); + sum_PM_ampl_debug += pmAmpl; + auto itSide = mMapPMhash2isAside.find(entry.first); + if (itSide != mMapPMhash2isAside.end()) { + if (itSide->second) { + sum_PM_ampl_A_debug += pmAmpl; + } else { + sum_PM_ampl_C_debug += pmAmpl; + } + } + } + LOG(debug) << "Sum PM amplitude (LUT-based): total=" << sum_PM_ampl_debug + << " A-side=" << sum_PM_ampl_A_debug + << " C-side=" << sum_PM_ampl_C_debug; + Bool_t is_A, is_C, isVertex, is_Central, is_SemiCentral = 0; is_A = n_hit_A > 0; is_C = n_hit_C > 0; - is_Central = summ_ampl_A + summ_ampl_C >= params.mtrg_central_trh; - is_SemiCentral = summ_ampl_A + summ_ampl_C >= params.mtrg_semicentral_trh; + is_Central = sum_PM_ampl_A_debug + sum_PM_ampl_C_debug >= 2 * params.mtrg_central_trh; + is_SemiCentral = sum_PM_ampl_A_debug + sum_PM_ampl_C_debug >= 2 * params.mtrg_semicentral_trh && !is_Central; uint32_t amplA = is_A ? summ_ampl_A * 0.125 : -5000; // sum amplitude A side / 8 (hardware) uint32_t amplC = is_C ? summ_ampl_C * 0.125 : -5000; // sum amplitude C side / 8 (hardware) int timeA = is_A ? mean_time_A / n_hit_A : -5000; // average time A side int timeC = is_C ? mean_time_C / n_hit_C : -5000; // average time C side vertex_time = (timeC - timeA) * 0.5; - isVertex = is_A && is_C && (vertex_time > -params.mTime_trg_gate && vertex_time < params.mTime_trg_gate); + isVertex = is_A && is_C && (vertex_time > -params.mTime_trg_vertex_gate && vertex_time < params.mTime_trg_vertex_gate); LOG(debug) << " A " << is_A << " timeA " << timeA << " mean_time_A " << mean_time_A << " n_hit_A " << n_hit_A << " C " << is_C << " timeC " << timeC << " mean_time_C " << mean_time_C << " n_hit_C " << n_hit_C << " vertex_time " << vertex_time; Triggers triggers; bool isLaser = false; diff --git a/Detectors/FIT/FV0/simulation/include/FV0Simulation/FV0DigParam.h b/Detectors/FIT/FV0/simulation/include/FV0Simulation/FV0DigParam.h index 383fa4cb494c1..6462323a279b7 100644 --- a/Detectors/FIT/FV0/simulation/include/FV0Simulation/FV0DigParam.h +++ b/Detectors/FIT/FV0/simulation/include/FV0Simulation/FV0DigParam.h @@ -69,6 +69,10 @@ struct FV0DigParam : o2::conf::ConfigurableParamHelper { uint8_t defaultChainQtc = 0x48; // only 2 flags are set by default in simulation: kIsCFDinADCgate and kIsEventInTVDC float mAmpThresholdForReco = 24; // only channels with amplitude higher will participate in calibration and collision time short mTimeThresholdForReco = 1000; // only channels with time below will participate in calibration and collision time + int NchannelsLevel = 2; // trigger Nchannels + float InnerChargeLevel = 4; // InnerRingsChargeLevel + float OuterChargeLevel = 4; // OuterRingsChargeLevel + float ChargeLevel = 8; // ChargeLevel O2ParamDef(FV0DigParam, "FV0DigParam"); }; diff --git a/Detectors/FIT/FV0/simulation/src/Digitizer.cxx b/Detectors/FIT/FV0/simulation/src/Digitizer.cxx index 8c1d2dc8824e2..3237f9bab7879 100644 --- a/Detectors/FIT/FV0/simulation/src/Digitizer.cxx +++ b/Detectors/FIT/FV0/simulation/src/Digitizer.cxx @@ -38,8 +38,8 @@ void Digitizer::clear() void Digitizer::init() { LOG(info) << "init"; - mNBins = FV0DigParam::Instance().waveformNbins; //Will be computed using detector set-up from CDB - mBinSize = FV0DigParam::Instance().waveformBinWidth; //Will be set-up from CDB + mNBins = FV0DigParam::Instance().waveformNbins; // Will be computed using detector set-up from CDB + mBinSize = FV0DigParam::Instance().waveformBinWidth; // Will be set-up from CDB mNTimeBinsPerBC = std::lround(o2::constants::lhc::LHCBunchSpacingNS / mBinSize); // 1920 bins/BC for (Int_t detID = 0; detID < Constants::nFv0Channels; detID++) { @@ -149,8 +149,8 @@ void Digitizer::process(const std::vector& hits, createPulse(mipFraction, hit.GetTrackID(), hitTime, hit.GetPos().R(), cachedIR, nCachedIR, detId); - } //while loop - } //hitloop + } // while loop + } // hitloop } void Digitizer::createPulse(float mipFraction, int parID, const double hitTime, const float hitR, @@ -200,7 +200,7 @@ void Digitizer::createPulse(float mipFraction, int parID, const double hitTime, } added[ir] = true; } - ///Add MC labels to BCs for those contributed to the PMT signal + /// Add MC labels to BCs for those contributed to the PMT signal for (int ir = 0; ir < nCachedIR; ir++) { if (added[ir]) { auto bcCache = getBCCache(cachedIR[ir]); @@ -238,6 +238,8 @@ void Digitizer::storeBC(const BCCache& bc, int8_t nTotFiredCells = 0; int8_t nTrgFiredCells = 0; // number of fired cells, that follow additional trigger conditions (time gate) int totalChargeAllRing = 0; + int totalChargeInnerRing = 0; + int totalChargeOuterRing = 0; int32_t avgTime = 0; double nSignalInner = 0; double nSignalOuter = 0; @@ -285,8 +287,10 @@ void Digitizer::storeBC(const BCCache& bc, avgTime += iCfdZero; if (iPmt < 24) { nSignalInner++; + totalChargeInnerRing += iTotalCharge; } else { nSignalOuter++; + totalChargeOuterRing += iTotalCharge; } } } @@ -300,13 +304,15 @@ void Digitizer::storeBC(const BCCache& bc, } else { avgTime = o2::fit::Triggers::DEFAULT_TIME; } - ///Triggers for FV0 - bool isA, isAIn, isAOut, isCen, isSCen; + /// Triggers for FV0 + bool isA, isNchannels, isAIn, isAOut, isTotalCharge; isA = nTrgFiredCells > 0; - isAIn = nSignalInner > 0; // ring 1,2 and 3 - isAOut = nSignalOuter > 0; // ring 4 and 5 - isCen = totalChargeAllRing > FV0DigParam::Instance().adcChargeCenThr; - isSCen = totalChargeAllRing > FV0DigParam::Instance().adcChargeSCenThr; + isNchannels = nTrgFiredCells > FV0DigParam::Instance().NchannelsLevel; + // isAIn = nSignalInner > FV0DigParam::Instance().NchannelsLevel; // ring 1,2 and 3 + isAIn = 0.125 * totalChargeInnerRing > 2 * FV0DigParam::Instance().InnerChargeLevel; // ring 1,2 and 3 + // isAOut = nSignalOuter > FV0DigParam::Instance().NchannelsLevel; // ring 4 and 5 + isAOut = 0.125 * totalChargeOuterRing > 2 * FV0DigParam::Instance().OuterChargeLevel; // ring 4 and 5 + isTotalCharge = 0.125 * totalChargeAllRing > 2 * FV0DigParam::Instance().ChargeLevel; Triggers triggers; const int unusedCharge = o2::fit::Triggers::DEFAULT_AMP; @@ -314,10 +320,10 @@ void Digitizer::storeBC(const BCCache& bc, const int unusedZero = o2::fit::Triggers::DEFAULT_ZERO; const bool unusedBitsInSim = false; // bits related to laser and data validity const bool bitDataIsValid = true; - triggers.setTriggers(isA, isAIn, isAOut, isCen, isSCen, nTrgFiredCells, (int8_t)unusedZero, + triggers.setTriggers(isA, isAIn, isAOut, isTotalCharge, isNchannels, nTrgFiredCells, (int8_t)unusedZero, (int32_t)(0.125 * totalChargeAllRing), (int32_t)unusedCharge, (int16_t)avgTime, (int16_t)unusedTime, unusedBitsInSim, unusedBitsInSim, bitDataIsValid); digitsBC.emplace_back(first, nTotFiredCells, bc, triggers, mEventId - 1); - digitsTrig.emplace_back(bc, isA, isAIn, isAOut, isCen, isSCen); + digitsTrig.emplace_back(bc, isA, isAIn, isAOut, isTotalCharge, isNchannels); for (auto const& lbl : bc.labels) { labels.addElement(nBC, lbl); } @@ -342,8 +348,8 @@ Int_t Digitizer::SimulateLightYield(Int_t pmt, Int_t nPhot) const //--------------------------------------------------------------------------- Float_t Digitizer::IntegrateCharge(const ChannelDigitF& pulse) const { - int const chargeIntMin = FV0DigParam::Instance().isIntegrateFull ? 0 : (FV0DigParam::Instance().avgCfdTimeForMip - 6.0) / mBinSize; //Charge integration offset (cfd mean time - 6 ns) - int const chargeIntMax = FV0DigParam::Instance().isIntegrateFull ? mNTimeBinsPerBC : (FV0DigParam::Instance().avgCfdTimeForMip + 14.0) / mBinSize; //Charge integration offset (cfd mean time + 14 ns) + int const chargeIntMin = FV0DigParam::Instance().isIntegrateFull ? 0 : (FV0DigParam::Instance().avgCfdTimeForMip - 6.0) / mBinSize; // Charge integration offset (cfd mean time - 6 ns) + int const chargeIntMax = FV0DigParam::Instance().isIntegrateFull ? mNTimeBinsPerBC : (FV0DigParam::Instance().avgCfdTimeForMip + 14.0) / mBinSize; // Charge integration offset (cfd mean time + 14 ns) if (chargeIntMin < 0 || chargeIntMin > mNTimeBinsPerBC || chargeIntMax > mNTimeBinsPerBC) { LOG(fatal) << "invalid indicess: chargeInMin=" << chargeIntMin << " chargeIntMax=" << chargeIntMax; } @@ -400,7 +406,7 @@ float Digitizer::getDistFromCellCenter(UInt_t cellId, double hitx, double hity) double a = -(y0 - pCell->y) / (x0 - pCell->x); double b = 1; double c = -(y0 - a * x0); - //Return the distance from hit to this line + // Return the distance from hit to this line return (a * hitx + b * hity + c) / TMath::Sqrt(a * a + b * b); } From bbb4570480ccb5f89af56913a9b7403e61610778 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Tue, 31 Mar 2026 00:34:34 +0200 Subject: [PATCH 441/701] [ALICE3] IOTOF: Adjust layer radius calculations for stave tilt and chip thickness (#15220) * [ALICE3] IOTOF: Adjust layer radius calculations for stave tilt * Refactor iTOF and oTOF layer initialization logic * Update README.md * Update IOTOFBaseParam.h default thickness * Updated stave tilt angle for segmented iTOF configuration. --- Detectors/Upgrades/ALICE3/IOTOF/README.md | 2 +- .../base/include/IOTOFBase/IOTOFBaseParam.h | 4 ++-- .../ALICE3/IOTOF/simulation/src/Detector.cxx | 8 +++---- .../ALICE3/IOTOF/simulation/src/Layer.cxx | 24 +++++++++++-------- 4 files changed, 21 insertions(+), 17 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/README.md b/Detectors/Upgrades/ALICE3/IOTOF/README.md index fba4d12252af6..d7a954c4718fe 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/README.md +++ b/Detectors/Upgrades/ALICE3/IOTOF/README.md @@ -23,7 +23,7 @@ Configurables for various sub-detectors are presented in the following Table: | `IOTOFBase.segmentedInnerTOF` | `false` (default), `true` | Use segmented geometry for inner TOF | | `IOTOFBase.segmentedOuterTOF` | `false` (default), `true` | Use segmented geometry for outer TOF | | `IOTOFBase.detectorPattern` | ` ` (default), `v3b`, `v3b1a`, `v3b1b`, `v3b2a`, `v3b2b`, `v3b3` | Optional layout pattern | -| `IOTOFBase.x2x0` | `0.02` (default) | Chip thickness in fractions of the rad. lenght | +| `IOTOFBase.x2x0` | `0.000527` (default) | Chip thickness in fractions of the rad. lenght | For example, a geometry with fully cylindrical tracker barrel (for all layers in VD, ML and OT) can be obtained by diff --git a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h index 91d005415891d..1f1a26b79077e 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h @@ -28,7 +28,7 @@ struct IOTOFBaseParam : public o2::conf::ConfigurableParamHelper std::string detectorPattern = ""; // Layouts of the detector bool segmentedInnerTOF = false; // If the inner TOF layer is segmented bool segmentedOuterTOF = false; // If the outer TOF layer is segmented - float x2x0 = 0.02f; // thickness expressed in radiation length, for all layers for the moment + float x2x0 = 0.000527f; // thickness expressed in radiation length, for all layers for the moment O2ParamDef(IOTOFBaseParam, "IOTOFBase"); }; @@ -36,4 +36,4 @@ struct IOTOFBaseParam : public o2::conf::ConfigurableParamHelper } // namespace iotof } // end namespace o2 -#endif \ No newline at end of file +#endif diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx index 61720f2172b92..d6417eba22041 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx @@ -96,10 +96,10 @@ void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::str } if (itof) { // iTOF const std::string name = GeometryTGeo::getITOFLayerPattern(); - const int nStaves = itofSegmented ? 24 : 0; // number of staves in segmented case - const double staveWidth = itofSegmented ? 5.42 : 0.0; // cm - const double staveTiltAngle = itofSegmented ? 10.0 : 0.0; // degrees - const int modulesPerStave = itofSegmented ? 10 : 0; // number of modules per stave in segmented case + const int nStaves = itofSegmented ? 24 : 0; // number of staves in segmented case + const double staveWidth = itofSegmented ? 5.42 : 0.0; // cm + const double staveTiltAngle = itofSegmented ? 3.0 : 0.0; // degrees + const int modulesPerStave = itofSegmented ? 10 : 0; // number of modules per stave in segmented case mITOFLayer = ITOFLayer(name, dInnerTof.first, 0.f, dInnerTof.second, 0.f, x2x0, itofSegmented ? ITOFLayer::kBarrelSegmented : ITOFLayer::kBarrel, nStaves, staveWidth, staveTiltAngle, modulesPerStave); diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx index 118078ebf7100..66d0b2959c8bd 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx @@ -155,11 +155,13 @@ void ITOFLayer::createLayer(TGeoVolume* motherVolume) case kBarrelSegmented: { // First we create the volume for the whole layer, which will be used as mother volume for the segments const double avgRadius = 0.5 * (mInnerRadius + mOuterRadius); - const double staveSizeX = mStaves.second; // cm - const double staveSizeY = mOuterRadius - mInnerRadius; // cm - const double staveSizeZ = mZLength; // cm - const double deltaForTilt = 0.5 * (std::sin(TMath::DegToRad() * mTiltAngle) * staveSizeX + std::cos(TMath::DegToRad() * mTiltAngle) * staveSizeY); // we increase the size of the layer to account for the tilt of the staves - TGeoTube* layer = new TGeoTube(mInnerRadius - deltaForTilt, mOuterRadius + deltaForTilt, mZLength / 2); + const double staveSizeX = mStaves.second; // cm + const double staveSizeY = mOuterRadius - mInnerRadius; // cm + const double staveSizeZ = mZLength; // cm + const double deltaForTilt = 0.5 * (std::sin(TMath::DegToRad() * mTiltAngle) * staveSizeX + std::cos(TMath::DegToRad() * mTiltAngle) * staveSizeY); // we increase the size of the layer to account for the tilt of the staves + const double radiusMax = std::sqrt(avgRadius * avgRadius + 0.25 * staveSizeX * staveSizeX + 0.25 * staveSizeY * staveSizeY + avgRadius * 2. * deltaForTilt); // we increase the outer radius to account for the tilt of the staves + const double radiusMin = std::sqrt(avgRadius * avgRadius + 0.25 * staveSizeX * staveSizeX + 0.25 * staveSizeY * staveSizeY - avgRadius * 2. * deltaForTilt); // we decrease the inner radius to account for the tilt of the staves + TGeoTube* layer = new TGeoTube(radiusMin, radiusMax, mZLength / 2); TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); setLayerStyle(layerVol); @@ -287,11 +289,13 @@ void OTOFLayer::createLayer(TGeoVolume* motherVolume) case kBarrelSegmented: { // First we create the volume for the whole layer, which will be used as mother volume for the segments const double avgRadius = 0.5 * (mInnerRadius + mOuterRadius); - const double staveSizeX = mStaves.second; // cm - const double staveSizeY = mOuterRadius - mInnerRadius; // cm - const double staveSizeZ = mZLength; // cm - const double deltaForTilt = 0.5 * (std::sin(TMath::DegToRad() * mTiltAngle) * staveSizeX + std::cos(TMath::DegToRad() * mTiltAngle) * staveSizeY); // we increase the size of the layer to account for the tilt of the staves - TGeoTube* layer = new TGeoTube(mInnerRadius - deltaForTilt, mOuterRadius + deltaForTilt, mZLength / 2); + const double staveSizeX = mStaves.second; // cm + const double staveSizeY = mOuterRadius - mInnerRadius; // cm + const double staveSizeZ = mZLength; // cm + const double deltaForTilt = 0.5 * (std::sin(TMath::DegToRad() * mTiltAngle) * staveSizeX + std::cos(TMath::DegToRad() * mTiltAngle) * staveSizeY); // we increase the size of the layer to account for the tilt of the staves + const double radiusMax = std::sqrt(avgRadius * avgRadius + 0.25 * staveSizeX * staveSizeX + 0.25 * staveSizeY * staveSizeY + avgRadius * 2. * deltaForTilt); // we increase the outer radius to account for the tilt of the staves + const double radiusMin = std::sqrt(avgRadius * avgRadius + 0.25 * staveSizeX * staveSizeX + 0.25 * staveSizeY * staveSizeY - avgRadius * 2. * deltaForTilt); // we decrease the inner radius to account for the tilt of the staves + TGeoTube* layer = new TGeoTube(radiusMin, radiusMax, mZLength / 2); TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); setLayerStyle(layerVol); From 974bdc0596d1124a6ee69343962a2c6379f68f88 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 29 Mar 2026 11:09:46 +0200 Subject: [PATCH 442/701] Use default constructor --- Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h | 2 +- Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h b/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h index 9aaf6e517336d..7050cc6c69370 100644 --- a/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h +++ b/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h @@ -83,7 +83,7 @@ struct DataPointCompositeObject final { /** * Copy constructor */ - DataPointCompositeObject(const DataPointCompositeObject& src) noexcept : DataPointCompositeObject(src.id, src.data) {} + DataPointCompositeObject(const DataPointCompositeObject& src) noexcept = default; DataPointCompositeObject& operator=(const DataPointCompositeObject& src) noexcept { diff --git a/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h b/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h index 79b7d7cf886c7..be21f71f8f3e3 100644 --- a/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h +++ b/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h @@ -85,7 +85,7 @@ class DataPointIdentifier final /** * A copy constructor for DataPointIdentifier. */ - DataPointIdentifier(const DataPointIdentifier& src) noexcept : DataPointIdentifier(src.pt1, src.pt2, src.pt3, src.pt4, src.pt5, src.pt6, src.pt7, src.pt8) {} + DataPointIdentifier(const DataPointIdentifier& src) noexcept = default; DataPointIdentifier& operator=(const DataPointIdentifier& src) noexcept { From e2e7eb846e103d55ac9ba725d5cb7dbdbb249f50 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 29 Mar 2026 11:17:29 +0200 Subject: [PATCH 443/701] Rework DCS point classes to have default assign operators --- .../DetectorsDCS/DataPointCompositeObject.h | 10 ++----- .../DetectorsDCS/DataPointIdentifier.h | 29 ++++++------------- 2 files changed, 11 insertions(+), 28 deletions(-) diff --git a/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h b/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h index 7050cc6c69370..6ea69f82277bf 100644 --- a/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h +++ b/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h @@ -47,7 +47,7 @@ struct DataPointCompositeObject final { * * @see ADAPRO::ADAPOS::DataPointIdentifier */ - const DataPointIdentifier id; + DataPointIdentifier id; /** * The DataPointValue object, which occupies the last 64 bytes of the @@ -85,13 +85,7 @@ struct DataPointCompositeObject final { */ DataPointCompositeObject(const DataPointCompositeObject& src) noexcept = default; - DataPointCompositeObject& operator=(const DataPointCompositeObject& src) noexcept - { - if (&src != this) { - memcpy(this, &src, sizeof(DataPointCompositeObject)); - } - return *this; - } + DataPointCompositeObject& operator=(const DataPointCompositeObject& src) noexcept = default; /** * Bit-by bit equality comparison of DataPointCompositeObjects. diff --git a/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h b/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h index be21f71f8f3e3..8d156e04ebbca 100644 --- a/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h +++ b/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h @@ -41,19 +41,14 @@ namespace dcs */ class DataPointIdentifier final { - const uint64_t pt1; - const uint64_t pt2; - const uint64_t pt3; - const uint64_t pt4; - const uint64_t pt5; - const uint64_t pt6; - const uint64_t pt7; - const uint64_t pt8; // Contains the last 6 chars of alias and the type. - - DataPointIdentifier( - const uint64_t pt1, const uint64_t pt2, const uint64_t pt3, - const uint64_t pt4, const uint64_t pt5, const uint64_t pt6, - const uint64_t pt7, const uint64_t pt8) noexcept : pt1(pt1), pt2(pt2), pt3(pt3), pt4(pt4), pt5(pt5), pt6(pt6), pt7(pt7), pt8(pt8) {} + uint64_t pt1; + uint64_t pt2; + uint64_t pt3; + uint64_t pt4; + uint64_t pt5; + uint64_t pt6; + uint64_t pt7; + uint64_t pt8; // Contains the last 6 chars of alias and the type. public: /** @@ -87,13 +82,7 @@ class DataPointIdentifier final */ DataPointIdentifier(const DataPointIdentifier& src) noexcept = default; - DataPointIdentifier& operator=(const DataPointIdentifier& src) noexcept - { - if (&src != this) { - memcpy(this, &src, sizeof(DataPointIdentifier)); - } - return *this; - } + DataPointIdentifier& operator=(const DataPointIdentifier& src) noexcept = default; /** * This stati procedure fills the given DataPointIdentifier object with From ae345476bb628d972f7a44142649dffe456d7803 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 1 Apr 2026 12:59:16 +0200 Subject: [PATCH 444/701] Align to coding conventions --- .../Base/GPUReconstructionDebug.cxx | 50 ++++++++++++------- 1 file changed, 31 insertions(+), 19 deletions(-) diff --git a/GPU/GPUTracking/Base/GPUReconstructionDebug.cxx b/GPU/GPUTracking/Base/GPUReconstructionDebug.cxx index 564c04ba7f745..559d1537464ab 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionDebug.cxx +++ b/GPU/GPUTracking/Base/GPUReconstructionDebug.cxx @@ -215,55 +215,67 @@ void GPUReconstructionCPU::debugWriter::row(char type, uint32_t count, std::stri if (streamCSV.is_open()) { streamCSV << type << ","; - if (count != 0) + if (count != 0) { streamCSV << count; + } streamCSV << "," << name << ","; - if (gpu_time != -1.0) + if (gpu_time != -1.0) { streamCSV << std::format("{:.0f}", gpu_time * scale); + } streamCSV << ","; - if (cpu_time != -1.0) + if (cpu_time != -1.0) { streamCSV << std::format("{:.0f}", cpu_time * scale); + } streamCSV << ","; - if (cpu_time != -1.0 && total_time != -1.0) + if (cpu_time != -1.0 && total_time != -1.0) { streamCSV << std::format("{:.2f}", cpu_time / total_time); + } streamCSV << ","; - if (total_time != -1.0) + if (total_time != -1.0) { streamCSV << std::format("{:.0f}", total_time * scale); + } streamCSV << ","; - if (memSize != 0 && count != 0) + if (memSize != 0 && count != 0) { streamCSV << std::format("{:.3f},{},{}", memSize / gpu_time * 1e-9, memSize / mStatNEvents, memSize / mStatNEvents / count); - else + } else { streamCSV << ",,"; + } streamCSV << std::endl; } if (mMarkdown) { std::cout << "| " << type << " | "; - if (count != 0) + if (count != 0) { std::cout << std::format("{:6} |", count); - else + } else { std::cout << " |"; + } std::cout << std::format(" {:42}|", name); - if (gpu_time != -1.0) + if (gpu_time != -1.0) { std::cout << std::format("{:10.0f} |", gpu_time * scale); - else + } else { std::cout << " |"; - if (cpu_time != -1.0) + } + if (cpu_time != -1.0) { std::cout << std::format("{:10.0f} |", cpu_time * scale); - else + } else { std::cout << " |"; - if (cpu_time != -1.0 && total_time != -1.0) + } + if (cpu_time != -1.0 && total_time != -1.0) { std::cout << std::format("{:8.2f} |", cpu_time / total_time); - else + } else { std::cout << " |"; - if (total_time != -1.0) + } + if (total_time != -1.0) { std::cout << std::format("{:10.0f} |", total_time * scale); - else + } else { std::cout << " |"; - if (memSize != 0 && count != 0) + } + if (memSize != 0 && count != 0) { std::cout << std::format("{:10.3f} |{:14} |{:14} |", memSize / gpu_time * 1e-9, memSize / mStatNEvents, memSize / mStatNEvents / count); - else + } else { std::cout << " | | |"; + } std::cout << std::endl; } else { if (name.substr(0, 3) == "GPU") { From 4f64fb9cabd616e2e822d1a683918ba86fba366c Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 25 Mar 2026 15:01:55 +0100 Subject: [PATCH 445/701] Drop async-list-label action My understanding is that this is not actually used, as it is superseded by the two / three tag approach. --- .github/workflows/async-list-label.yml | 19 ------------------- 1 file changed, 19 deletions(-) delete mode 100644 .github/workflows/async-list-label.yml diff --git a/.github/workflows/async-list-label.yml b/.github/workflows/async-list-label.yml deleted file mode 100644 index e0b4185c563b7..0000000000000 --- a/.github/workflows/async-list-label.yml +++ /dev/null @@ -1,19 +0,0 @@ ---- -name: Collect and print async labels - -'on': - pull_request_target: - types: - - opened - - reopened - branches: - - dev - -permissions: {} - -jobs: - list_async_labels: - name: Collect and print async labels - uses: alisw/ali-bot/.github/workflows/async-list-label.yml@master - permissions: - pull-requests: write # to update labels From 74c80492483241afc58a5784cd6ce89e097520af Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Wed, 1 Apr 2026 16:08:26 +0200 Subject: [PATCH 446/701] [ALICE3] IOTOF: allow reduced sensor thickness wihout reducing chip size (#15247) * Enhance Layer class with sensor thickness handling Added sensor thickness parameter and validation checks. * Add sensor thickness to Layer class Added sensor thickness parameter to Layer constructor and updated member variables accordingly. * Modify configLayers to include sensorThickness Added sensorThickness parameter to configLayers function for ITOF and OTOF layers. * Update Detector.h * Modify thickness parameters in IOTOFBaseParam.h Updated the radiation length thickness and added sensor thickness parameter. * Revise options table in README for IOTOF Updated options table with new sensor thickness parameter and corrected default value for x2x0. * Simplify chip size calculation in Layer.cxx Removed sensor thickness adjustment for chip size calculation. * Update sensor size calculation in Layer.cxx --- Detectors/Upgrades/ALICE3/IOTOF/README.md | 22 ++++++++++--------- .../base/include/IOTOFBase/IOTOFBaseParam.h | 3 ++- .../include/IOTOFSimulation/Detector.h | 4 ++-- .../include/IOTOFSimulation/Layer.h | 9 ++++---- .../ALICE3/IOTOF/simulation/src/Detector.cxx | 6 ++--- .../ALICE3/IOTOF/simulation/src/Layer.cxx | 15 +++++++++---- 6 files changed, 35 insertions(+), 24 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/README.md b/Detectors/Upgrades/ALICE3/IOTOF/README.md index d7a954c4718fe..e52b5e2379e9c 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/README.md +++ b/Detectors/Upgrades/ALICE3/IOTOF/README.md @@ -14,16 +14,18 @@ Configurables for various sub-detectors are presented in the following Table: [link to definitions](./base/include/IOTOFBase/IOTOFBaseParam.h) -| Options | Choices | Comments | -| ----------------------------- | ---------------------------------------------------------------- | ---------------------------------------------- | -| `IOTOFBase.enableInnerTOF` | `true` (default), `false` | Enable inner TOF barrel layer | -| `IOTOFBase.enableOuterTOF` | `true` (default), `false` | Enable outer TOF barrel layer | -| `IOTOFBase.enableForwardTOF` | `true` (default), `false` | Enable forward TOF endcap | -| `IOTOFBase.enableBackwardTOF` | `true` (default), `false` | Enable backward TOF endcap | -| `IOTOFBase.segmentedInnerTOF` | `false` (default), `true` | Use segmented geometry for inner TOF | -| `IOTOFBase.segmentedOuterTOF` | `false` (default), `true` | Use segmented geometry for outer TOF | -| `IOTOFBase.detectorPattern` | ` ` (default), `v3b`, `v3b1a`, `v3b1b`, `v3b2a`, `v3b2b`, `v3b3` | Optional layout pattern | -| `IOTOFBase.x2x0` | `0.000527` (default) | Chip thickness in fractions of the rad. lenght | +| Options | Choices | Comments | +| ----------------------------- | ---------------------------------------------------------------- | -------------------------------------------------------------------------- | +| `IOTOFBase.enableInnerTOF` | `true` (default), `false` | Enable inner TOF barrel layer | +| `IOTOFBase.enableOuterTOF` | `true` (default), `false` | Enable outer TOF barrel layer | +| `IOTOFBase.enableForwardTOF` | `true` (default), `false` | Enable forward TOF endcap | +| `IOTOFBase.enableBackwardTOF` | `true` (default), `false` | Enable backward TOF endcap | +| `IOTOFBase.segmentedInnerTOF` | `false` (default), `true` | Use segmented geometry for inner TOF | +| `IOTOFBase.segmentedOuterTOF` | `false` (default), `true` | Use segmented geometry for outer TOF | +| `IOTOFBase.detectorPattern` | ` ` (default), `v3b`, `v3b1a`, `v3b1b`, `v3b2a`, `v3b2b`, `v3b3` | Optional layout pattern | +| `IOTOFBase.x2x0` | `0.02` (default) | Chip thickness in fractions of the rad. lenght | +| `IOTOFBase.sensorThickness` | `0.0050` (default) | Sensor thickness in cm, can be at maximum equivalent to the chip thickness | + For example, a geometry with fully cylindrical tracker barrel (for all layers in VD, ML and OT) can be obtained by diff --git a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h index 1f1a26b79077e..c1a9578484c17 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/base/include/IOTOFBase/IOTOFBaseParam.h @@ -28,7 +28,8 @@ struct IOTOFBaseParam : public o2::conf::ConfigurableParamHelper std::string detectorPattern = ""; // Layouts of the detector bool segmentedInnerTOF = false; // If the inner TOF layer is segmented bool segmentedOuterTOF = false; // If the outer TOF layer is segmented - float x2x0 = 0.000527f; // thickness expressed in radiation length, for all layers for the moment + float x2x0 = 0.02f; // thickness expressed in radiation length, for all layers for the moment + float sensorThickness = 0.0050f; // thickness of the sensor in cm, for all layers for the moment, the default is set to 50 microns 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 acf754e1b1fa8..34097020c42ff 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, std::string pattern = "", bool itofSegmented = false, bool otofSegmented = false, const float x2x0 = 0.02f); + void configLayers(bool itof = true, bool otof = true, bool ftof = true, bool btof = true, std::string pattern = "", bool itofSegmented = false, bool otofSegmented = false, const float x2x0 = 0.02f, const float sensorThickness = 0.0050f); void configServices(); void createMaterials(); @@ -104,4 +104,4 @@ struct UseShm { } // namespace base } // namespace o2 #endif -#endif \ No newline at end of file +#endif diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Layer.h b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Layer.h index 29542810b8021..dc9fedf439a11 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Layer.h +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/include/IOTOFSimulation/Layer.h @@ -26,7 +26,7 @@ class Layer public: Layer() = default; Layer(std::string layerName, float rInn, float rOut, float zLength, float zOffset, float layerX2X0, - int layout = kBarrel, int nStaves = 0, float staveSize = 0.0, double staveTiltAngle = 0.0, int modulesPerStave = 0); + int layout = kBarrel, int nStaves = 0, float staveSize = 0.0, double staveTiltAngle = 0.0, int modulesPerStave = 0, float sensorThickness = 0.0f); ~Layer() = default; auto getInnerRadius() const { return mInnerRadius; } @@ -52,8 +52,9 @@ class Layer float mZLength; float mZOffset{0.f}; // Of use when fwd layers float mX2X0; - float mChipThickness; - int mLayout{kBarrel}; // Identifier of the type of layer layout (barrel, disk, barrel segmented, disk segmented) + float mChipThickness; // Thickness of the chip in cm, derived from mX2X0 and the radiation length of silicon + float mSensorThickness; // Thickness of the sensor in cm, to be subtracted from the chip thickness to get the total module thickness + 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 staves in phi (for barrel) or in r (for disk) std::pair mStaves{0, 0.0f}; // Number and size of staves in phi (for barrel) or in r (for disk) in case of segmented layout int mModulesPerStave{0}; // Number of modules along a stave @@ -92,4 +93,4 @@ class BTOFLayer : public Layer } // namespace iotof } // namespace o2 -#endif // ALICEO2_IOTOF_LAYER_H \ No newline at end of file +#endif // ALICEO2_IOTOF_LAYER_H diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx index d6417eba22041..59b914a3dd076 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Detector.cxx @@ -58,7 +58,7 @@ void Detector::ConstructGeometry() } void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::string pattern, bool itofSegmented, bool otofSegmented, - const float x2x0) + const float x2x0, const float sensorThickness) { const std::pair dInnerTof = {21.f, 129.f}; // Radius and length @@ -102,7 +102,7 @@ void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::str const int modulesPerStave = itofSegmented ? 10 : 0; // number of modules per stave in segmented case mITOFLayer = ITOFLayer(name, dInnerTof.first, 0.f, dInnerTof.second, 0.f, x2x0, itofSegmented ? ITOFLayer::kBarrelSegmented : ITOFLayer::kBarrel, - nStaves, staveWidth, staveTiltAngle, modulesPerStave); + nStaves, staveWidth, staveTiltAngle, modulesPerStave, itofSegmented ? sensorThickness : 0.0f); } if (otof) { // oTOF const std::string name = GeometryTGeo::getOTOFLayerPattern(); @@ -112,7 +112,7 @@ void Detector::configLayers(bool itof, bool otof, bool ftof, bool btof, std::str const int modulesPerStave = otofSegmented ? 54 : 0; // number of modules per stave in segmented case mOTOFLayer = OTOFLayer(name, dOuterTof.first, 0.f, dOuterTof.second, 0.f, x2x0, otofSegmented ? OTOFLayer::kBarrelSegmented : OTOFLayer::kBarrel, - nStaves, staveWidth, staveTiltAngle, modulesPerStave); + nStaves, staveWidth, staveTiltAngle, modulesPerStave, otofSegmented ? sensorThickness : 0.0f); } if (ftof) { const std::string name = GeometryTGeo::getFTOFLayerPattern(); diff --git a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx index 66d0b2959c8bd..b603d2a4a423b 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx +++ b/Detectors/Upgrades/ALICE3/IOTOF/simulation/src/Layer.cxx @@ -28,19 +28,20 @@ namespace o2 namespace iotof { Layer::Layer(std::string layerName, float rInn, float rOut, float zLength, float zOffset, float layerX2X0, - int layout, int nStaves, float staveSize, double staveTiltAngle, int modulesPerStave) + int layout, int nStaves, float staveSize, double staveTiltAngle, int modulesPerStave, float sensorThickness) : mLayerName(layerName), mInnerRadius(rInn), mOuterRadius(rOut), mZLength(zLength), mZOffset(zOffset), + mSensorThickness(sensorThickness), mX2X0(layerX2X0), mLayout(layout), mStaves(nStaves, staveSize), mModulesPerStave(modulesPerStave), mTiltAngle(staveTiltAngle) { - const float Si_X0 = 9.5f; + const float Si_X0 = 9.5f; // cm, radiation length of silicon mChipThickness = mX2X0 * Si_X0; std::string name = ""; switch (layout) { @@ -76,6 +77,12 @@ Layer::Layer(std::string layerName, float rInn, float rOut, float zLength, float if ((mTiltAngle < 0.0 || mTiltAngle > 90.0) && (layout == kBarrelSegmented || layout == kDiskSegmented)) { LOG(fatal) << "Invalid configuration: tilt angle " << mTiltAngle << " is too large, it must be between 0 and 90 degrees"; } + if (mSensorThickness < 0.0f || mSensorThickness > mChipThickness) { + LOG(fatal) << "Invalid configuration: sensor thickness " << mSensorThickness << " cm is out of range (0, " << mChipThickness << ") cm"; + } + if (sensorThickness > 0.0f && (layout == kBarrel || layout == kDisk)) { + LOG(fatal) << "Invalid configuration: sensor thickness " << mSensorThickness << " cm is set for non-segmented layout, it should be 0"; + } LOGP(info, "TOF: Creating {} layer: rInner: {} (cm) rOuter: {} (cm) zLength: {} (cm) zOffset: {} x2X0: {}", name.c_str(), mInnerRadius, mOuterRadius, mZLength, mZOffset, mX2X0); } @@ -193,7 +200,7 @@ void ITOFLayer::createLayer(TGeoVolume* motherVolume) const int sensorsPerChipX = 2; // we assume that each chip is divided in 2 sensors along the x direction const int sensorsPerChipZ = 2; // we assume that each chip is divided in 2 sensors along the z direction const double sensorSizeX = chipSizeX / sensorsPerChipX; // cm - const double sensorSizeY = chipSizeY; // cm + const double sensorSizeY = mSensorThickness; // cm const double sensorSizeZ = chipSizeZ / sensorsPerChipZ; // cm TGeoBBox* sensor = new TGeoBBox(sensorSizeX * 0.5, sensorSizeY * 0.5, sensorSizeZ * 0.5); TGeoVolume* sensVol = new TGeoVolume(sensName, sensor, medSi); @@ -327,7 +334,7 @@ void OTOFLayer::createLayer(TGeoVolume* motherVolume) const int sensorsPerChipX = 2; // we assume that each chip is divided in 2 sensors along the x direction const int sensorsPerChipZ = 2; // we assume that each chip is divided in 2 sensors along the z direction const double sensorSizeX = chipSizeX / sensorsPerChipX; // cm - const double sensorSizeY = chipSizeY; // cm + const double sensorSizeY = mSensorThickness; // cm const double sensorSizeZ = chipSizeZ / sensorsPerChipZ; // cm TGeoBBox* sensor = new TGeoBBox(sensorSizeX * 0.5, sensorSizeY * 0.5, sensorSizeZ * 0.5); TGeoVolume* sensVol = new TGeoVolume(sensName, sensor, medSi); From ea6b15c3746b71a0fd4cf7886caf9921639de65f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Wed, 1 Apr 2026 16:08:40 +0200 Subject: [PATCH 447/701] [ALICE3] TRK: Collect services in a dedicated volume assembly (#15215) * Refactor TRKServices to use volume assembly * Add service volume name to GeometryTGeo * Add getTRKServiceVolPattern method and variable --- .../ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h | 2 ++ .../Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx | 1 + .../ALICE3/TRK/simulation/src/TRKServices.cxx | 13 ++++++++----- 3 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h index e32a2546c6842..576dbf434f757 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/GeometryTGeo.h @@ -43,6 +43,7 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache return sInstance.get(); }; static const char* getTRKVolPattern() { return sVolumeName.c_str(); } + static const char* getTRKServiceVolPattern() { return sServiceVolName.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(); } @@ -198,6 +199,7 @@ class GeometryTGeo : public o2::detectors::DetMatrixCache static constexpr int MAXLAYERS = 20; ///< max number of active layers static std::string sVolumeName; + static std::string sServiceVolName; static std::string sLayerName; static std::string sPetalAssemblyName; static std::string sPetalName; diff --git a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx index 36d26a6344e6c..10c1c63615d35 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/GeometryTGeo.cxx @@ -26,6 +26,7 @@ std::unique_ptr GeometryTGeo::sInstance; // Names std::string GeometryTGeo::sVolumeName = "TRKV"; +std::string GeometryTGeo::sServiceVolName = "TRKService"; std::string GeometryTGeo::sLayerName = "TRKLayer"; std::string GeometryTGeo::sPetalAssemblyName = "PETAL"; std::string GeometryTGeo::sPetalName = "PETALCASE"; diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx index d8246bcd8640c..7cf7dc863607e 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKServices.cxx @@ -127,18 +127,21 @@ void TRKServices::createMaterials() void TRKServices::createServices(TGeoVolume* motherVolume) { + + TGeoVolumeAssembly* vol = new TGeoVolumeAssembly(GeometryTGeo::getTRKServiceVolPattern()); + motherVolume->AddNode(vol, 2, new TGeoTranslation(0, 0., 0)); createMaterials(); createVacuumCompositeShape(); auto& trkPars = TRKBaseParam::Instance(); if (trkPars.getLayoutSRV() == kLOISymm) { LOGP(info, "TRK services: LoI version"); - createMiddleServices(motherVolume); - createOuterDisksServices(motherVolume); - createOuterBarrelServices(motherVolume); + createMiddleServices(vol); + createOuterDisksServices(vol); + createOuterBarrelServices(vol); } else { LOGP(info, "TRK services: Peacock layout"); - createMLServicesPeacock(motherVolume); - createOTServicesPeacock(motherVolume); + createMLServicesPeacock(vol); + createOTServicesPeacock(vol); } } From ebf039321ea3b9851aceb6d333bcb36f9b4cc7fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Wed, 1 Apr 2026 16:09:07 +0200 Subject: [PATCH 448/701] [ALICE3] Copy class of ITSMFT Hit for TRK Hit (#15194) --- Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt | 2 +- .../ALICE3/TRK/simulation/include/TRKSimulation/Hit.h | 6 +++++- 2 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt index 10f117750d793..6d30d8d01bb12 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/CMakeLists.txt @@ -39,4 +39,4 @@ o2_target_root_dictionary(TRKSimulation include/TRKSimulation/VDLayer.h include/TRKSimulation/VDGeometryBuilder.h include/TRKSimulation/VDSensorRegistry.h - include/TRKSimulation/DPLDigitizerParam.h) \ No newline at end of file + include/TRKSimulation/DPLDigitizerParam.h) diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Hit.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Hit.h index 88afac8682cf4..402a343ead472 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Hit.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/Hit.h @@ -19,7 +19,11 @@ namespace o2::trk { -using Hit = o2::itsmft::Hit; // For now we rely on the same Hit class as ITSMFT, but we can extend it with TRK-specific information if needed in the future +class Hit : public o2::itsmft::Hit +{ + public: + using o2::itsmft::Hit::Hit; // Inherit constructors +}; } // namespace o2::trk #endif From 8050f83b9ae9f1963af5bd37c1ad6c4b312a4d77 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Tue, 31 Mar 2026 20:55:54 +0200 Subject: [PATCH 449/701] DPL: add status endpoint to the driver --- Framework/Core/CMakeLists.txt | 1 + Framework/Core/include/Framework/DeviceInfo.h | 3 + .../Core/src/ControlWebSocketHandler.cxx | 5 + Framework/Core/src/DPLWebSocket.cxx | 10 +- Framework/Core/src/DPLWebSocket.h | 1 + Framework/Core/src/DriverServerContext.h | 5 + Framework/Core/src/StatusWebSocketHandler.cxx | 503 ++++++++++++++++++ Framework/Core/src/StatusWebSocketHandler.h | 101 ++++ Framework/Core/src/runDataProcessing.cxx | 7 + 9 files changed, 635 insertions(+), 1 deletion(-) create mode 100644 Framework/Core/src/StatusWebSocketHandler.cxx create mode 100644 Framework/Core/src/StatusWebSocketHandler.h diff --git a/Framework/Core/CMakeLists.txt b/Framework/Core/CMakeLists.txt index c311ba980a20b..0e67e1c0cc623 100644 --- a/Framework/Core/CMakeLists.txt +++ b/Framework/Core/CMakeLists.txt @@ -158,6 +158,7 @@ o2_add_library(Framework src/StepTHn.cxx src/Base64.cxx src/DPLWebSocket.cxx + src/StatusWebSocketHandler.cxx src/TimerParamSpec.cxx test/TestClasses.cxx TARGETVARNAME targetName diff --git a/Framework/Core/include/Framework/DeviceInfo.h b/Framework/Core/include/Framework/DeviceInfo.h index ef93ca83ca03f..bc3e895a3d8ed 100644 --- a/Framework/Core/include/Framework/DeviceInfo.h +++ b/Framework/Core/include/Framework/DeviceInfo.h @@ -61,6 +61,9 @@ struct DeviceInfo { std::string lastError; /// An unterminated string which is not ready to be printed yet std::string unprinted; + /// Total number of log lines ever stored in history (monotonically increasing). + /// Used by status clients to track which lines they have already sent. + size_t logSeq = 0; /// Whether the device is active (running) or not. bool active; /// Whether the device is ready to quit. diff --git a/Framework/Core/src/ControlWebSocketHandler.cxx b/Framework/Core/src/ControlWebSocketHandler.cxx index 6d7926918a8c7..35528a1d6dfec 100644 --- a/Framework/Core/src/ControlWebSocketHandler.cxx +++ b/Framework/Core/src/ControlWebSocketHandler.cxx @@ -11,6 +11,7 @@ #include "ControlWebSocketHandler.h" #include "DriverServerContext.h" +#include "StatusWebSocketHandler.h" #include "Framework/DeviceMetricsHelper.h" #include "Framework/ServiceMetricsInfo.h" #include @@ -83,6 +84,10 @@ void ControlWebSocketHandler::endChunk() for (auto& callback : *mContext.metricProcessingCallbacks) { callback(mContext.registry, ServiceMetricsInfo{*mContext.metrics, *mContext.specs, *mContext.infos, mContext.driver->metrics, *mContext.driver}, timestamp); } + // Notify status clients before changed flags are reset so they can see what changed. + for (auto* statusHandler : mContext.statusHandlers) { + statusHandler->sendUpdate(mIndex); + } for (auto& metricsInfo : *mContext.metrics) { std::fill(metricsInfo.changed.begin(), metricsInfo.changed.end(), false); } diff --git a/Framework/Core/src/DPLWebSocket.cxx b/Framework/Core/src/DPLWebSocket.cxx index d9b6594d5f07c..06de46b387c29 100644 --- a/Framework/Core/src/DPLWebSocket.cxx +++ b/Framework/Core/src/DPLWebSocket.cxx @@ -18,6 +18,7 @@ #include "DriverServerContext.h" #include "DriverClientContext.h" #include "ControlWebSocketHandler.h" +#include "StatusWebSocketHandler.h" #include "HTTPParser.h" #include #include @@ -193,9 +194,10 @@ void WSDPLHandler::method(std::string_view const& s) void WSDPLHandler::target(std::string_view const& s) { - if (s != "/") { + if (s != "/" && s != "/status") { throw WSError{404, "Unknown"}; } + mTarget = s; } void populateHeader(std::map& headers, std::string_view const& k, std::string_view const& v) @@ -294,6 +296,12 @@ void WSDPLHandler::endHeaders() break; } } + } else if (mTarget == "/status" && mServerContext->isDriver) { + LOGP(info, "Status client connected ({} total)", mServerContext->statusHandlers.size() + 1); + auto* statusHandler = new StatusWebSocketHandler(*mServerContext, this); + mServerContext->statusHandlers.push_back(statusHandler); + mHandler = std::unique_ptr(statusHandler); + mHandler->headers(mHeaders); } else { if ((mServerContext->isDriver && getenv("DPL_DRIVER_REMOTE_GUI")) || ((mServerContext->isDriver == false) && getenv("DPL_DEVICE_REMOTE_GUI"))) { LOG(info) << "Connection not bound to a PID"; diff --git a/Framework/Core/src/DPLWebSocket.h b/Framework/Core/src/DPLWebSocket.h index 43ec27a6b54f0..1985c37157d65 100644 --- a/Framework/Core/src/DPLWebSocket.h +++ b/Framework/Core/src/DPLWebSocket.h @@ -62,6 +62,7 @@ struct WSDPLHandler : public HTTPParser { bool mHandshaken = false; uv_stream_t* mStream = nullptr; std::map mHeaders; + std::string mTarget; DriverServerContext* mServerContext; }; diff --git a/Framework/Core/src/DriverServerContext.h b/Framework/Core/src/DriverServerContext.h index 4d25c47bd172b..c9f2c80165d92 100644 --- a/Framework/Core/src/DriverServerContext.h +++ b/Framework/Core/src/DriverServerContext.h @@ -29,6 +29,7 @@ namespace o2::framework struct DriverInfo; struct ServiceRegistry; struct GuiCallbackContext; +struct StatusWebSocketHandler; struct DriverServerContext { ServiceRegistryRef registry; @@ -49,6 +50,10 @@ struct DriverServerContext { /// or something like that. bool isDriver = false; + /// Connected MCP/status clients. Updated by StatusWebSocketHandler + /// on connect/disconnect; notified by ControlWebSocketHandler::endChunk(). + std::vector statusHandlers; + /// The handle to the server component of the /// driver. uv_tcp_t serverHandle; diff --git a/Framework/Core/src/StatusWebSocketHandler.cxx b/Framework/Core/src/StatusWebSocketHandler.cxx new file mode 100644 index 0000000000000..db715eff6592d --- /dev/null +++ b/Framework/Core/src/StatusWebSocketHandler.cxx @@ -0,0 +1,503 @@ +// 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 "StatusWebSocketHandler.h" +#include "DPLWebSocket.h" +#include "DriverServerContext.h" +#include "Framework/DeviceInfo.h" +#include "Framework/DeviceMetricsInfo.h" +#include "Framework/DeviceSpec.h" +#include "Framework/DeviceStateEnums.h" +#include "Framework/LogParsingHelpers.h" +#include +#include +#include +#include +#include + +namespace o2::framework +{ + +namespace +{ + +std::string jsonEscape(std::string_view s) +{ + std::string out; + out.reserve(s.size() + 4); + for (unsigned char c : s) { + switch (c) { + case '"': + out += "\\\""; + break; + case '\\': + out += "\\\\"; + break; + case '\n': + out += "\\n"; + break; + case '\r': + out += "\\r"; + break; + case '\t': + out += "\\t"; + break; + default: + if (c < 0x20) { + char buf[8]; + snprintf(buf, sizeof(buf), "\\u%04x", c); + out += buf; + } else { + out += static_cast(c); + } + } + } + return out; +} + +char const* logLevelName(LogParsingHelpers::LogLevel level) +{ + switch (level) { + case LogParsingHelpers::LogLevel::Debug: + return "debug"; + case LogParsingHelpers::LogLevel::Info: + return "info"; + case LogParsingHelpers::LogLevel::Important: + return "important"; + case LogParsingHelpers::LogLevel::Warning: + return "warning"; + case LogParsingHelpers::LogLevel::Alarm: + return "alarm"; + case LogParsingHelpers::LogLevel::Error: + return "error"; + case LogParsingHelpers::LogLevel::Critical: + return "critical"; + case LogParsingHelpers::LogLevel::Fatal: + return "fatal"; + default: + return "unknown"; + } +} + +char const* streamingStateName(StreamingState s) +{ + switch (s) { + case StreamingState::Streaming: + return "Streaming"; + case StreamingState::EndOfStreaming: + return "EndOfStreaming"; + case StreamingState::Idle: + return "Idle"; + default: + return "Unknown"; + } +} + +void appendMetricValue(std::string& out, DeviceMetricsInfo const& info, size_t mi) +{ + auto const& metric = info.metrics[mi]; + if (metric.pos == 0) { + out += "null"; + return; + } + size_t last = (metric.pos - 1) % metricStorageSize(metric.type); + switch (metric.type) { + case MetricType::Int: + out += std::to_string(info.intMetrics[metric.storeIdx][last]); + break; + case MetricType::Float: { + char buf[32]; + snprintf(buf, sizeof(buf), "%g", static_cast(info.floatMetrics[metric.storeIdx][last])); + out += buf; + break; + } + case MetricType::Uint64: + out += std::to_string(info.uint64Metrics[metric.storeIdx][last]); + break; + default: + out += "null"; + } +} + +/// Extract the value of a simple string field from a flat JSON object. +/// e.g. extractField(R"({"cmd":"subscribe","device":"prod"})", "device") → "prod" +/// Returns empty string_view if not found. +std::string_view extractStringField(std::string_view json, std::string_view key) +{ + std::string needle; + needle += '"'; + needle += key; + needle += "\":"; + auto pos = json.find(needle); + if (pos == std::string_view::npos) { + return {}; + } + pos += needle.size(); + // skip optional whitespace between ':' and '"' + while (pos < json.size() && json[pos] == ' ') { + ++pos; + } + if (pos >= json.size() || json[pos] != '"') { + return {}; + } + ++pos; // skip opening quote + auto end = json.find('"', pos); + if (end == std::string_view::npos) { + return {}; + } + return json.substr(pos, end - pos); +} + +/// Extract the raw value of an array field from a flat JSON object. +/// e.g. extractArrayField(R"({"metrics":["a","b"]})", "metrics") → R"(["a","b"])" +std::string_view extractArrayField(std::string_view json, std::string_view key) +{ + std::string needle; + needle += '"'; + needle += key; + needle += "\":"; + auto pos = json.find(needle); + if (pos == std::string_view::npos) { + return {}; + } + pos += needle.size(); + // skip whitespace + while (pos < json.size() && json[pos] == ' ') { + ++pos; + } + if (pos >= json.size() || json[pos] != '[') { + return {}; + } + auto start = pos; + size_t depth = 0; + while (pos < json.size()) { + if (json[pos] == '[') { + ++depth; + } else if (json[pos] == ']') { + --depth; + if (depth == 0) { + return json.substr(start, pos - start + 1); + } + } + ++pos; + } + return {}; +} + +/// Iterate over the string elements of a JSON array of strings. +/// Calls @a callback for each unescaped string value. +template +void forEachStringInArray(std::string_view arr, F&& callback) +{ + // arr is like ["name1","name2"] + size_t pos = 0; + while (pos < arr.size()) { + auto q = arr.find('"', pos); + if (q == std::string_view::npos) { + break; + } + auto end = arr.find('"', q + 1); + if (end == std::string_view::npos) { + break; + } + callback(arr.substr(q + 1, end - q - 1)); + pos = end + 1; + } +} + +} // anonymous namespace + +StatusWebSocketHandler::StatusWebSocketHandler(DriverServerContext& context, WSDPLHandler* handler) + : mContext{context}, mHandler{handler} +{ +} + +StatusWebSocketHandler::~StatusWebSocketHandler() +{ + auto& handlers = mContext.statusHandlers; + handlers.erase(std::remove(handlers.begin(), handlers.end(), this), handlers.end()); +} + +void StatusWebSocketHandler::headers(std::map const&) +{ + sendSnapshot(); +} + +void StatusWebSocketHandler::frame(char const* data, size_t s) +{ + std::string_view msg{data, s}; + auto cmd = extractStringField(msg, "cmd"); + if (cmd.empty()) { + return; + } + auto deviceName = extractStringField(msg, "device"); + + if (cmd == "list_metrics") { + handleListMetrics(deviceName); + } else if (cmd == "subscribe") { + handleSubscribe(deviceName, extractArrayField(msg, "metrics")); + } else if (cmd == "unsubscribe") { + handleUnsubscribe(deviceName, extractArrayField(msg, "metrics")); + } else if (cmd == "subscribe_logs") { + handleSubscribeLogs(deviceName); + } else if (cmd == "unsubscribe_logs") { + handleUnsubscribeLogs(deviceName); + } +} + +void StatusWebSocketHandler::sendText(std::string const& json) +{ + std::vector outputs; + encode_websocket_frames(outputs, json.data(), json.size(), WebSocketOpCode::Text, 0); + mHandler->write(outputs); +} + +void StatusWebSocketHandler::sendSnapshot() +{ + auto const& specs = *mContext.specs; + auto const& infos = *mContext.infos; + + // Size subscription tables to current device count; grow lazily as needed. + mSubscribedMetrics.resize(specs.size()); + mLastLogSeq.resize(infos.size()); + for (size_t di = 0; di < infos.size(); ++di) { + mLastLogSeq[di] = infos[di].logSeq; + } + + std::string out; + out.reserve(512 + specs.size() * 128); + out += R"({"type":"snapshot","devices":[)"; + for (size_t di = 0; di < specs.size(); ++di) { + if (di > 0) { + out += ','; + } + auto const& info = infos[di]; + out += R"({"name":")"; + out += jsonEscape(specs[di].name); + out += R"(","pid":)"; + out += std::to_string(info.pid); + out += R"(,"active":)"; + out += info.active ? "true" : "false"; + out += R"(,"streamingState":")"; + out += streamingStateName(info.streamingState); + out += R"(","deviceState":")"; + out += jsonEscape(info.deviceState); + out += R"("})"; + } + out += "]}"; + sendText(out); +} + +void StatusWebSocketHandler::sendUpdate(size_t deviceIndex) +{ + auto const& specs = *mContext.specs; + auto const& metrics = *mContext.metrics; + + if (deviceIndex >= specs.size() || deviceIndex >= metrics.size()) { + return; + } + + // Lazily grow the subscription table if new devices were added after snapshot. + if (mSubscribedMetrics.size() <= deviceIndex) { + mSubscribedMetrics.resize(deviceIndex + 1); + } + + auto const& subscribed = mSubscribedMetrics[deviceIndex]; + if (subscribed.empty()) { + return; + } + + auto const& info = metrics[deviceIndex]; + std::string metricsJson; + metricsJson += '{'; + bool first = true; + for (size_t mi = 0; mi < info.metrics.size(); ++mi) { + if (!info.changed[mi]) { + continue; + } + auto const& metric = info.metrics[mi]; + if (metric.type == MetricType::String || + metric.type == MetricType::Enum || + metric.type == MetricType::Unknown) { + continue; + } + auto const& label = info.metricLabels[mi]; + std::string_view labelSV{label.label, label.size}; + if (subscribed.find(std::string(labelSV)) == subscribed.end()) { + continue; + } + if (!first) { + metricsJson += ','; + } + first = false; + metricsJson += '"'; + metricsJson += jsonEscape(labelSV); + metricsJson += "\":"; + appendMetricValue(metricsJson, info, mi); + } + metricsJson += '}'; + + if (first) { + // Nothing subscribed changed in this cycle. + return; + } + + std::string out; + out += R"({"type":"update","device":)"; + out += std::to_string(deviceIndex); + out += R"(,"name":")"; + out += jsonEscape(specs[deviceIndex].name); + out += R"(","metrics":)"; + out += metricsJson; + out += '}'; + sendText(out); +} + +void StatusWebSocketHandler::handleListMetrics(std::string_view deviceName) +{ + size_t di = findDeviceIndex(deviceName); + if (di == SIZE_MAX) { + return; + } + auto const& metrics = *mContext.metrics; + + std::string out; + out += R"({"type":"metrics_list","device":")"; + out += jsonEscape(deviceName); + out += R"(","metrics":[)"; + bool first = true; + if (di < metrics.size()) { + auto const& info = metrics[di]; + for (size_t mi = 0; mi < info.metrics.size(); ++mi) { + auto const& metric = info.metrics[mi]; + if (metric.type == MetricType::String || + metric.type == MetricType::Enum || + metric.type == MetricType::Unknown) { + continue; + } + if (!first) { + out += ','; + } + first = false; + auto const& label = info.metricLabels[mi]; + out += '"'; + out += jsonEscape({label.label, label.size}); + out += '"'; + } + } + out += "]}"; + sendText(out); +} + +void StatusWebSocketHandler::handleSubscribe(std::string_view deviceName, std::string_view metricsArr) +{ + size_t di = findDeviceIndex(deviceName); + if (di == SIZE_MAX || metricsArr.empty()) { + return; + } + if (mSubscribedMetrics.size() <= di) { + mSubscribedMetrics.resize(di + 1); + } + forEachStringInArray(metricsArr, [&](std::string_view name) { + mSubscribedMetrics[di].emplace(name); + }); +} + +void StatusWebSocketHandler::handleUnsubscribe(std::string_view deviceName, std::string_view metricsArr) +{ + size_t di = findDeviceIndex(deviceName); + if (di == SIZE_MAX || metricsArr.empty() || di >= mSubscribedMetrics.size()) { + return; + } + forEachStringInArray(metricsArr, [&](std::string_view name) { + mSubscribedMetrics[di].erase(std::string(name)); + }); +} + +size_t StatusWebSocketHandler::findDeviceIndex(std::string_view name) const +{ + auto const& specs = *mContext.specs; + for (size_t di = 0; di < specs.size(); ++di) { + if (specs[di].name == name) { + return di; + } + } + return SIZE_MAX; +} + +void StatusWebSocketHandler::handleSubscribeLogs(std::string_view deviceName) +{ + size_t di = findDeviceIndex(deviceName); + if (di == SIZE_MAX) { + return; + } + if (mLastLogSeq.size() <= di) { + mLastLogSeq.resize(di + 1, 0); + } + // Start the cursor at the current log position so we only push future lines. + mLastLogSeq[di] = (*mContext.infos)[di].logSeq; + mLogSubscriptions.insert(di); +} + +void StatusWebSocketHandler::handleUnsubscribeLogs(std::string_view deviceName) +{ + size_t di = findDeviceIndex(deviceName); + if (di == SIZE_MAX) { + return; + } + mLogSubscriptions.erase(di); +} + +void StatusWebSocketHandler::sendNewLogs(size_t deviceIndex) +{ + if (mLogSubscriptions.find(deviceIndex) == mLogSubscriptions.end()) { + return; + } + auto const& infos = *mContext.infos; + auto const& specs = *mContext.specs; + if (deviceIndex >= infos.size() || deviceIndex >= specs.size()) { + return; + } + if (mLastLogSeq.size() <= deviceIndex) { + mLastLogSeq.resize(deviceIndex + 1, 0); + } + + auto const& info = infos[deviceIndex]; + size_t newLines = info.logSeq - mLastLogSeq[deviceIndex]; + if (newLines == 0) { + return; + } + // Cap to buffer size to avoid re-reading overwritten entries. + if (newLines > info.history.size()) { + newLines = info.history.size(); + } + + size_t histSize = info.history.size(); + // The oldest unread entry sits at (historyPos - newLines + histSize) % histSize. + size_t startPos = (info.historyPos + histSize - newLines) % histSize; + + std::string_view devName = specs[deviceIndex].name; + for (size_t i = 0; i < newLines; ++i) { + size_t pos = (startPos + i) % histSize; + std::string out; + out += R"({"type":"log","device":")"; + out += jsonEscape(devName); + out += R"(","level":")"; + out += logLevelName(info.historyLevel[pos]); + out += R"(","line":")"; + out += jsonEscape(info.history[pos]); + out += R"("})"; + sendText(out); + } + mLastLogSeq[deviceIndex] = info.logSeq; +} + +} // namespace o2::framework diff --git a/Framework/Core/src/StatusWebSocketHandler.h b/Framework/Core/src/StatusWebSocketHandler.h new file mode 100644 index 0000000000000..86a460e289440 --- /dev/null +++ b/Framework/Core/src/StatusWebSocketHandler.h @@ -0,0 +1,101 @@ +// 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. +#ifndef O2_FRAMEWORK_STATUSWEBSOCKETHANDLER_H_ +#define O2_FRAMEWORK_STATUSWEBSOCKETHANDLER_H_ + +#include "HTTPParser.h" +#include +#include +#include +#include +#include + +namespace o2::framework +{ +struct DriverServerContext; +struct WSDPLHandler; + +/// WebSocket handler for the /status endpoint. +/// +/// Protocol (client → driver): +/// {"cmd":"list_metrics","device":""} +/// → driver replies with {"type":"metrics_list","device":"","metrics":[...]} +/// +/// {"cmd":"subscribe","device":"","metrics":["m1","m2",...]} +/// → driver starts including those metrics in subsequent update frames +/// +/// {"cmd":"unsubscribe","device":"","metrics":["m1","m2",...]} +/// → driver stops sending those metrics +/// +/// {"cmd":"subscribe_logs","device":""} +/// → driver starts pushing new log lines for the device +/// +/// {"cmd":"unsubscribe_logs","device":""} +/// → driver stops pushing log lines for the device +/// +/// Protocol (driver → client): +/// {"type":"snapshot","devices":[{"name","pid","active","streamingState","deviceState"},...]} +/// → sent once on connect; contains no metrics or logs +/// +/// {"type":"update","device":,"name":"","metrics":{}} +/// → sent after each metrics cycle for devices with subscribed metrics that changed +/// +/// {"type":"metrics_list","device":"","metrics":["m1","m2",...]} +/// → reply to list_metrics command +/// +/// {"type":"log","device":"","level":"","line":""} +/// → pushed for each new log line from a subscribed device +struct StatusWebSocketHandler : public WebSocketHandler { + StatusWebSocketHandler(DriverServerContext& context, WSDPLHandler* handler); + ~StatusWebSocketHandler() override; + + /// Sends the minimal snapshot on handshake completion. + void headers(std::map const& headers) override; + /// Handles incoming commands from the MCP client. + void frame(char const* data, size_t s) override; + void beginChunk() override {} + void endChunk() override {} + void beginFragmentation() override {} + void endFragmentation() override {} + void control(char const* frame, size_t s) override {} + + /// Send a minimal JSON snapshot (device list + basic state, no metrics/logs). + void sendSnapshot(); + /// Push an update for device at @a deviceIndex. + /// Only metrics that are both changed[] and subscribed are included. + /// No-op if nothing subscribed or nothing changed for this device. + void sendUpdate(size_t deviceIndex); + /// Push any log lines for @a deviceIndex that arrived since the last call. + /// No-op if the device is not subscribed for logs. + void sendNewLogs(size_t deviceIndex); + + private: + void sendText(std::string const& json); + void handleListMetrics(std::string_view deviceName); + void handleSubscribe(std::string_view deviceName, std::string_view metricsJson); + void handleUnsubscribe(std::string_view deviceName, std::string_view metricsJson); + void handleSubscribeLogs(std::string_view deviceName); + void handleUnsubscribeLogs(std::string_view deviceName); + size_t findDeviceIndex(std::string_view name) const; + + DriverServerContext& mContext; + WSDPLHandler* mHandler; + /// Per-device set of subscribed metric label strings. + /// Sized to specs->size() on sendSnapshot(); grows if new devices appear. + std::vector> mSubscribedMetrics; + /// Per-device log cursor: value of DeviceInfo::logSeq when we last sent logs. + std::vector mLastLogSeq; + /// Set of device indices whose logs are being streamed. + std::unordered_set mLogSubscriptions; +}; + +} // namespace o2::framework +#endif // O2_FRAMEWORK_STATUSWEBSOCKETHANDLER_H_ diff --git a/Framework/Core/src/runDataProcessing.cxx b/Framework/Core/src/runDataProcessing.cxx index 98cbf70370c3d..70f3c8940ef26 100644 --- a/Framework/Core/src/runDataProcessing.cxx +++ b/Framework/Core/src/runDataProcessing.cxx @@ -68,6 +68,7 @@ #include "Framework/DefaultsHelpers.h" #include "ProcessingPoliciesHelpers.h" #include "DriverServerContext.h" +#include "StatusWebSocketHandler.h" #include "HTTPParser.h" #include "DPLWebSocket.h" #include "ArrowSupport.h" @@ -891,6 +892,7 @@ void processChildrenOutput(uv_loop_t* loop, info.history[info.historyPos] = token; info.historyLevel[info.historyPos] = logLevel; info.historyPos = (info.historyPos + 1) % info.history.size(); + info.logSeq++; fmt::print("[{}:{}]: {}\n", info.pid, spec.id, token); } // We keep track of the maximum log error a @@ -1541,6 +1543,11 @@ int runStateMachine(DataProcessorSpecs const& workflow, uv_async_init(loop, serverContext.asyncLogProcessing, [](uv_async_t* handle) { auto* context = (DriverServerContext*)handle->data; processChildrenOutput(context->loop, *context->driver, *context->infos, *context->specs, *context->controls); + for (auto* statusHandler : context->statusHandlers) { + for (size_t di = 0; di < context->infos->size(); ++di) { + statusHandler->sendNewLogs(di); + } + } }); while (true) { From 2b4851c08fec0e0c8d95006801beb984bc7e4869 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Tue, 31 Mar 2026 20:55:54 +0200 Subject: [PATCH 450/701] DPL: add MCP server for DPL Allows debugging a DPL workflow using Claude, ChatGPT or similar tools. --- .../Core/scripts/dpl-mcp-server/README.md | 75 +++++ .../scripts/dpl-mcp-server/dpl_mcp_server.py | 304 ++++++++++++++++++ .../scripts/dpl-mcp-server/pyproject.toml | 19 ++ 3 files changed, 398 insertions(+) create mode 100644 Framework/Core/scripts/dpl-mcp-server/README.md create mode 100644 Framework/Core/scripts/dpl-mcp-server/dpl_mcp_server.py create mode 100644 Framework/Core/scripts/dpl-mcp-server/pyproject.toml diff --git a/Framework/Core/scripts/dpl-mcp-server/README.md b/Framework/Core/scripts/dpl-mcp-server/README.md new file mode 100644 index 0000000000000..65d2378c5d756 --- /dev/null +++ b/Framework/Core/scripts/dpl-mcp-server/README.md @@ -0,0 +1,75 @@ +# DPL Status MCP Server + +An MCP server that connects to a running DPL driver's `/status` WebSocket endpoint and exposes its device state and metrics as tools for an AI assistant (e.g. Claude). + +## Requirements + +```bash +pip install mcp websockets +# or install the package directly: +pip install ./Framework/Core/scripts/dpl-mcp-server/ +``` + +## Running + +The driver port defaults to `8080`. Override with `--port`, `--pid`, or `DPL_STATUS_PORT`: + +```bash +python3 dpl_mcp_server.py --port 8080 +python3 dpl_mcp_server.py --pid 12345 # port = 8080 + pid % 30000 +DPL_STATUS_PORT=8080 python3 dpl_mcp_server.py +``` + +If installed as a package: + +```bash +dpl-mcp-server --pid $(pgrep -f diamond-workflow | head -1) +``` + +## Claude Code integration + +Add to `.mcp.json` in your project (or `~/.claude.json` for global use): + +```json +{ + "mcpServers": { + "dpl": { + "command": "dpl-mcp-server", + "args": ["--pid", "12345"] + } + } +} +``` + +Or with `claude mcp add`: + +```bash +claude mcp add dpl -- dpl-mcp-server --pid 12345 +``` + +## Available tools + +| Tool | Description | +|------|-------------| +| `list_devices` | List all devices with pid, active flag, streaming and device state | +| `list_metrics(device)` | List numeric metrics available for a device | +| `subscribe(device, metrics)` | Subscribe to metrics; driver will push updates when they change | +| `unsubscribe(device, metrics)` | Stop receiving updates for specific metrics | +| `get_updates(max_updates)` | Drain buffered update frames (default: up to 50) | + +## Protocol + +The driver sends a snapshot on connect, then pushes updates only for subscribed metrics that changed each processing cycle. There is no polling — updates arrive in real time as the workflow runs. + +``` +connect → {"type":"snapshot","devices":[{"name","pid","active","streamingState","deviceState"},...]} + +client → {"cmd":"list_metrics","device":"producer"} +driver → {"type":"metrics_list","device":"producer","metrics":["input-parts","output-bytes",...]} + +client → {"cmd":"subscribe","device":"producer","metrics":["output-bytes"]} +driver → {"type":"update","device":0,"name":"producer","metrics":{"output-bytes":1048576}} + (pushed every cycle in which output-bytes changed) + +client → {"cmd":"unsubscribe","device":"producer","metrics":["output-bytes"]} +``` diff --git a/Framework/Core/scripts/dpl-mcp-server/dpl_mcp_server.py b/Framework/Core/scripts/dpl-mcp-server/dpl_mcp_server.py new file mode 100644 index 0000000000000..bc04acf026188 --- /dev/null +++ b/Framework/Core/scripts/dpl-mcp-server/dpl_mcp_server.py @@ -0,0 +1,304 @@ +#!/usr/bin/env python3 +# 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. +"""DPL status MCP server. + +Bridges the DPL driver /status WebSocket endpoint to MCP tools so that an +AI assistant (e.g. Claude) can inspect and monitor a running DPL workflow. + +Usage +----- + python3 dpl_mcp_server.py --port 8080 + python3 dpl_mcp_server.py --pid 12345 # port derived as 8080 + pid % 30000 + DPL_STATUS_PORT=8080 python3 dpl_mcp_server.py + +Wire protocol (client → driver) +-------------------------------- + {"cmd":"list_metrics","device":""} + {"cmd":"subscribe","device":"","metrics":["m1","m2"]} + {"cmd":"unsubscribe","device":"","metrics":["m1"]} + +Wire protocol (driver → client) +-------------------------------- + {"type":"snapshot","devices":[{"name","pid","active","streamingState","deviceState"},...]} + {"type":"update","device":,"name":"","metrics":{}} + {"type":"metrics_list","device":"","metrics":["m1","m2",...]} +""" + +from __future__ import annotations + +import argparse +import asyncio +import json +import os +import sys +from typing import Any + +import websockets +from mcp.server.fastmcp import FastMCP + +# --------------------------------------------------------------------------- +# Global connection state (all access from the single asyncio event loop) +# --------------------------------------------------------------------------- +_port: int = 8080 +_ws: Any = None +_reader_task: asyncio.Task | None = None +_snapshot: dict = {} +_updates: list[dict] = [] +_logs: list[dict] = [] +_metrics_lists: dict[str, list[str]] = {} + + +async def _ensure_connected() -> None: + """Connect (or reconnect) to the driver's /status WebSocket.""" + global _ws, _reader_task + + # Check liveness of existing connection. + if _ws is not None: + try: + pong = await asyncio.wait_for(_ws.ping(), timeout=2.0) + await pong + return + except Exception: + _ws = None + if _reader_task is not None and not _reader_task.done(): + _reader_task.cancel() + _reader_task = None + + url = f"ws://localhost:{_port}/status" + _ws = await websockets.connect(url, subprotocols=["dpl"]) + if _reader_task is None or _reader_task.done(): + _reader_task = asyncio.create_task(_reader()) + + +async def _reader() -> None: + """Background task: read frames from the driver and buffer them.""" + global _ws, _snapshot, _updates, _logs, _metrics_lists + try: + async for raw in _ws: + try: + msg = json.loads(raw) + except json.JSONDecodeError: + continue + t = msg.get("type") + if t == "snapshot": + _snapshot = msg + # Clear stale metric lists from a previous driver instance. + _metrics_lists.clear() + elif t == "update": + _updates.append(msg) + elif t == "log": + _logs.append(msg) + elif t == "metrics_list": + device = msg.get("device", "") + _metrics_lists[device] = msg.get("metrics", []) + except Exception: + pass + finally: + _ws = None + + +async def _send(obj: dict) -> None: + await _ensure_connected() + await _ws.send(json.dumps(obj, separators=(",", ":"))) + + +# --------------------------------------------------------------------------- +# MCP server definition +# --------------------------------------------------------------------------- +mcp = FastMCP("DPL Status") + + +@mcp.tool() +async def list_devices() -> str: + """List all DPL devices with their current status. + + Returns each device's name, PID, active flag, streaming state, and device + state as reported by the driver snapshot. + """ + await _ensure_connected() + if not _snapshot: + return "No snapshot received yet — the driver may still be starting." + devices = _snapshot.get("devices", []) + if not devices: + return "No devices in snapshot." + lines = [] + for d in devices: + lines.append( + f"{d['name']}: pid={d['pid']} active={d['active']} " + f"streaming={d['streamingState']} state={d['deviceState']}" + ) + return "\n".join(lines) + + +@mcp.tool() +async def list_metrics(device: str) -> str: + """List the available numeric metrics for a DPL device. + + Sends a list_metrics command to the driver and waits up to 3 seconds for + the reply. Only numeric metrics (int, float, uint64) are included; string + and enum metrics are excluded. + + Args: + device: Device name exactly as shown by list_devices. + """ + # Remove any stale cached result so we can detect the fresh reply. + _metrics_lists.pop(device, None) + await _send({"cmd": "list_metrics", "device": device}) + for _ in range(60): # up to 3 s + await asyncio.sleep(0.05) + if device in _metrics_lists: + names = _metrics_lists[device] + if not names: + return f"Device '{device}' has no numeric metrics yet." + return f"{len(names)} metric(s): " + ", ".join(names) + return f"No reply from driver for device '{device}' (timeout)." + + +@mcp.tool() +async def subscribe(device: str, metrics: list[str]) -> str: + """Subscribe to one or more metrics for a DPL device. + + After subscribing, the driver will push update frames for the device + whenever any of the subscribed metrics change. Use get_updates to drain + the buffer. + + Args: + device: Device name exactly as shown by list_devices. + metrics: List of metric names to subscribe to (from list_metrics). + """ + await _send({"cmd": "subscribe", "device": device, "metrics": metrics}) + return f"Subscribed to {len(metrics)} metric(s) for '{device}': {', '.join(metrics)}" + + +@mcp.tool() +async def unsubscribe(device: str, metrics: list[str]) -> str: + """Stop receiving updates for specific metrics of a DPL device. + + Args: + device: Device name exactly as shown by list_devices. + metrics: List of metric names to unsubscribe from. + """ + await _send({"cmd": "unsubscribe", "device": device, "metrics": metrics}) + return f"Unsubscribed from {len(metrics)} metric(s) for '{device}'." + + +@mcp.tool() +async def subscribe_logs(device: str) -> str: + """Subscribe to log output for a DPL device. + + After subscribing, new log lines from the device will be buffered and + can be retrieved with get_logs(). + + Args: + device: Device name exactly as shown by list_devices. + """ + await _send({"cmd": "subscribe_logs", "device": device}) + return f"Subscribed to logs for '{device}'." + + +@mcp.tool() +async def unsubscribe_logs(device: str) -> str: + """Stop receiving log output for a DPL device. + + Args: + device: Device name exactly as shown by list_devices. + """ + await _send({"cmd": "unsubscribe_logs", "device": device}) + return f"Unsubscribed from logs for '{device}'." + + +@mcp.tool() +async def get_logs(max_lines: int = 100) -> str: + """Drain and return buffered log lines received since the last call. + + Args: + max_lines: Maximum number of log lines to return (default 100). + """ + await _ensure_connected() + batch = _logs[:max_lines] + del _logs[:max_lines] + if not batch: + return "No buffered log lines." + lines = [] + for entry in batch: + device = entry.get("device", "?") + level = entry.get("level", "?") + line = entry.get("line", "") + lines.append(f"[{device}][{level}] {line}") + return "\n".join(lines) + + +@mcp.tool() +async def get_updates(max_updates: int = 50) -> str: + """Drain and return buffered metric update frames received since the last call. + + Each frame contains the latest values of all subscribed metrics that + changed during that processing cycle. Calling this repeatedly gives a + time-ordered view of metric evolution. + + Args: + max_updates: Maximum number of update frames to return (default 50). + """ + await _ensure_connected() + batch = _updates[:max_updates] + del _updates[:max_updates] + if not batch: + return "No buffered updates." + lines = [] + for upd in batch: + name = upd.get("name") or f"device[{upd.get('device', '?')}]" + metrics = upd.get("metrics", {}) + if metrics: + parts = ", ".join(f"{k}={v}" for k, v in metrics.items()) + lines.append(f"{name}: {parts}") + else: + lines.append(f"{name}: (empty update)") + return "\n".join(lines) + + +# --------------------------------------------------------------------------- +# Entry point +# --------------------------------------------------------------------------- +def main() -> None: + global _port + + parser = argparse.ArgumentParser( + description="DPL status MCP server — expose DPL driver metrics via MCP tools" + ) + group = parser.add_mutually_exclusive_group() + group.add_argument( + "--port", + type=int, + default=None, + help="TCP port of the DPL driver status WebSocket (default: 8080 or DPL_STATUS_PORT env var)", + ) + group.add_argument( + "--pid", + type=int, + default=None, + help="PID of the DPL driver process; port is derived as 8080 + pid %% 30000", + ) + args = parser.parse_args() + + if args.pid is not None: + _port = 8080 + args.pid % 30000 + elif args.port is not None: + _port = args.port + elif "DPL_STATUS_PORT" in os.environ: + _port = int(os.environ["DPL_STATUS_PORT"]) + # else leave _port at the default 8080 + + mcp.run() + + +if __name__ == "__main__": + main() diff --git a/Framework/Core/scripts/dpl-mcp-server/pyproject.toml b/Framework/Core/scripts/dpl-mcp-server/pyproject.toml new file mode 100644 index 0000000000000..f87c1b770c934 --- /dev/null +++ b/Framework/Core/scripts/dpl-mcp-server/pyproject.toml @@ -0,0 +1,19 @@ +[build-system] +requires = ["hatchling"] +build-backend = "hatchling.build" + +[project] +name = "dpl-mcp-server" +version = "0.1.0" +description = "MCP server for monitoring DPL (Data Processing Layer) workflows" +requires-python = ">=3.11" +dependencies = [ + "mcp>=1.0.0", + "websockets>=12.0", +] + +[project.scripts] +dpl-mcp-server = "dpl_mcp_server:main" + +[tool.hatch.build.targets.wheel] +include = ["dpl_mcp_server.py"] From 4f060ca921092cc4090c746b8ab08d3f4a72596a Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Sat, 28 Mar 2026 15:48:51 +0100 Subject: [PATCH 451/701] C++ standard fobids specializations of is_trivially_copyable --- .../MathUtils/include/MathUtils/Cartesian.h | 20 ++++---------- Common/MathUtils/test/testCartesian.cxx | 2 +- .../DetectorsDCS/DataPointCompositeObject.h | 26 ++----------------- .../DetectorsDCS/DataPointIdentifier.h | 23 ++-------------- Detectors/DCS/test/testDataPointTypes.cxx | 4 +-- Framework/Core/include/Framework/TypeTraits.h | 13 +++++++--- 6 files changed, 20 insertions(+), 68 deletions(-) diff --git a/Common/MathUtils/include/MathUtils/Cartesian.h b/Common/MathUtils/include/MathUtils/Cartesian.h index 9b917707835a6..d7e421ecd965b 100644 --- a/Common/MathUtils/include/MathUtils/Cartesian.h +++ b/Common/MathUtils/include/MathUtils/Cartesian.h @@ -284,25 +284,15 @@ GPUdi() SMatrix> Similarity(const SMatrix +struct is_forced_trivially_copyable; -/// Defining Point3D explicitly as trivially copyable -/// -/// std::is_trivially_copyable> fails because the class -/// implements a copy constructor, although it does not much more than the default copy -/// constructor. We need Point3D to fulfill the condition in order to make types -/// inheriting from it or using it as member can be safely detected as messageable. -/// -/// We believe that Point3D is messageable and explicitly specialize the type trait. -/// There is a unit test for checking trivial copy -/// This is a workaround, we will also make suggestions to fix the cause in ROOT itself -/// TODO: delete once it is fixed in ROOT template -struct is_trivially_copyable> : std::true_type { +struct is_forced_trivially_copyable> : std::true_type { }; -} // namespace std +} // namespace o2::framework #endif // Disable for GPU #endif diff --git a/Common/MathUtils/test/testCartesian.cxx b/Common/MathUtils/test/testCartesian.cxx index ec04c34670fc3..9f2b4c912007e 100644 --- a/Common/MathUtils/test/testCartesian.cxx +++ b/Common/MathUtils/test/testCartesian.cxx @@ -77,7 +77,7 @@ BOOST_AUTO_TEST_CASE(Cartesian_test) BOOST_AUTO_TEST_CASE(Point3D_messageable) { using ElementType = math_utils::Point3D; - static_assert(std::is_trivially_copyable::value == true); + static_assert(o2::framework::is_forced_trivially_copyable::value == true); std::vector pts(10); auto makeElement = [](int idx) { return ElementType{idx, idx + 10, idx + 20}; diff --git a/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h b/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h index 6ea69f82277bf..84e15e4656cd2 100644 --- a/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h +++ b/Detectors/DCS/include/DetectorsDCS/DataPointCompositeObject.h @@ -28,9 +28,7 @@ #include "DetectorsDCS/DataPointValue.h" #include "DetectorsDCS/DeliveryType.h" -namespace o2 -{ -namespace dcs +namespace o2::dcs { /** * DataPointCompositeObject is a composition of a DataPointIdentifier and a @@ -291,26 +289,6 @@ struct DataPointCompositeObject final { template T getValue(const DataPointCompositeObject& dpcom); -} // namespace dcs - -/// Defining DataPointCompositeObject explicitly as messageable -namespace framework -{ -template -struct is_messageable; -template <> -struct is_messageable : std::true_type { -}; -} // namespace framework - -} // namespace o2 - -/// Defining DataPointCompositeObject explicitly as copiable -namespace std -{ -template <> -struct is_trivially_copyable : std::true_type { -}; -} // namespace std +} // namespace o2::dcs #endif /* O2_DCS_DATAPOINT_COMPOSITE_OBJECT_H */ diff --git a/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h b/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h index 8d156e04ebbca..faa12a3306d4f 100644 --- a/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h +++ b/Detectors/DCS/include/DetectorsDCS/DataPointIdentifier.h @@ -31,9 +31,7 @@ #include "DetectorsDCS/GenericFunctions.h" #include "DetectorsDCS/DeliveryType.h" -namespace o2 -{ -namespace dcs +namespace o2::dcs { /** * DataPointIdentifier object is responsible for storing the alias and type @@ -208,19 +206,7 @@ struct DPIDHash { return dpid.hash_code(); } }; -} // namespace dcs - -/// Defining DataPointIdentifier explicitly as messageable -namespace framework -{ -template -struct is_messageable; -template <> -struct is_messageable : std::true_type { -}; -} // namespace framework - -} // namespace o2 +} // namespace o2::dcs // specailized std::hash namespace std @@ -232,11 +218,6 @@ struct hash { return std::hash{}(dpid.hash_code()); } }; - -template <> -struct is_trivially_copyable : std::true_type { -}; - } // namespace std #endif /* O2_DCS_DATAPOINT_IDENTIFIER_H */ diff --git a/Detectors/DCS/test/testDataPointTypes.cxx b/Detectors/DCS/test/testDataPointTypes.cxx index 491ebae1f5d00..dd7511f63217c 100644 --- a/Detectors/DCS/test/testDataPointTypes.cxx +++ b/Detectors/DCS/test/testDataPointTypes.cxx @@ -17,8 +17,6 @@ #include #include "DetectorsDCS/DataPointCompositeObject.h" #include "Framework/TypeTraits.h" -#include -#include #include #include @@ -26,7 +24,7 @@ typedef boost::mpl::list::value, true); + BOOST_CHECK_EQUAL(std::is_trivially_copyable_v, true); BOOST_CHECK_EQUAL(std::is_polymorphic::value, false); BOOST_CHECK_EQUAL(std::is_pointer::value, false); BOOST_CHECK_EQUAL(o2::framework::is_forced_non_messageable::value, false); diff --git a/Framework/Core/include/Framework/TypeTraits.h b/Framework/Core/include/Framework/TypeTraits.h index faa9055de3280..bb2d338f42af3 100644 --- a/Framework/Core/include/Framework/TypeTraits.h +++ b/Framework/Core/include/Framework/TypeTraits.h @@ -38,13 +38,18 @@ struct is_forced_non_messageable< typename std::enable_if::value>::type> : public std::true_type { }; +template +struct is_forced_trivially_copyable : std::false_type { +}; + // TODO: extend this to exclude structs with pointer data members // see e.g. https://stackoverflow.com/questions/32880990/how-to-check-if-class-has-pointers-in-c14 template -struct is_messageable : std::conditional::value && // - !std::is_polymorphic::value && // - !std::is_pointer::value && // - !is_forced_non_messageable::value, // +struct is_messageable : std::conditional<(std::is_trivially_copyable::value || // + framework::is_forced_trivially_copyable::value) && // + !std::is_polymorphic::value && // + !std::is_pointer::value && // + !is_forced_non_messageable::value, // std::true_type, std::false_type>::type { }; From 81075c53830501c0b999fee685bac5cba02169c7 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 2 Apr 2026 11:40:20 +0200 Subject: [PATCH 452/701] DPL: fix long standing bug with ArrayString serialization This is actually the reason why tables are lost from the metadata. --- Framework/Core/src/VariantJSONHelpers.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Framework/Core/src/VariantJSONHelpers.cxx b/Framework/Core/src/VariantJSONHelpers.cxx index fbb5abb331867..f3d6061020ea2 100644 --- a/Framework/Core/src/VariantJSONHelpers.cxx +++ b/Framework/Core/src/VariantJSONHelpers.cxx @@ -339,7 +339,7 @@ void writeVariant(std::ostream& o, Variant const& v) rapidjson::Writer w(osw); auto writeArray = [&](auto* values, size_t size) { - using T = std::remove_pointer_t; + using T = std::remove_cv_t>; w.StartArray(); for (auto i = 0u; i < size; ++i) { if constexpr (std::is_same_v) { From 5a5010afd0bd3d49e2f4d749f535d0ad74cad874 Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Thu, 2 Apr 2026 14:43:20 +0200 Subject: [PATCH 453/701] DPL: Update serialization/deserialization test for string array Variant --- Framework/Core/test/test_Variants.cxx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Framework/Core/test/test_Variants.cxx b/Framework/Core/test/test_Variants.cxx index a364b228871da..da1f39f241e96 100644 --- a/Framework/Core/test/test_Variants.cxx +++ b/Framework/Core/test/test_Variants.cxx @@ -327,14 +327,16 @@ TEST_CASE("VariantJSONConversionsTest") std::vector vstrings{"myoption_one", "myoption_two"}; Variant vvstr(vstrings); + REQUIRE(vvstr.size() == 2); std::stringstream osal; - VariantJSONHelpers::write(osal, vvstr); + osal << vvstr; std::stringstream isal; isal.str(osal.str()); auto vvstra = VariantJSONHelpers::read(isal); - for (auto i = 0U; i < vvstra.size(); ++i) { + REQUIRE(vvstr.size() == vvstra.size()); + for (auto i = 0U; i < vstrings.size(); ++i) { REQUIRE(vstrings[i] == vvstra.get()[i]); } } From ddec5e319145c71f2eba11bcc082bcfaef6e8d71 Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Thu, 2 Apr 2026 16:18:51 +0200 Subject: [PATCH 454/701] aodMerger: Special option to merge only folders of the same name (#15205) Adding a special `merge-by-name` option which asks the merger only to merge together dataframe folders of the same name. This is needed only in special situations where we want to enfore the output structure of the AOD to be the same as that of a reference (data) AOD. An example is MC-DATA embedding. --- Framework/AODMerger/src/aodMerger.cxx | 67 ++++++++++++++++----------- 1 file changed, 41 insertions(+), 26 deletions(-) diff --git a/Framework/AODMerger/src/aodMerger.cxx b/Framework/AODMerger/src/aodMerger.cxx index 3ea45e84a39e0..11dae2dc5eac4 100644 --- a/Framework/AODMerger/src/aodMerger.cxx +++ b/Framework/AODMerger/src/aodMerger.cxx @@ -38,6 +38,7 @@ int main(int argc, char* argv[]) long maxDirSize = 100000000; bool skipNonExistingFiles = false; bool skipParentFilesList = false; + bool mergeByName = false; int verbosity = 2; int exitCode = 0; // 0: success, >0: failure int compression = 505; @@ -50,6 +51,7 @@ int main(int argc, char* argv[]) {"skip-non-existing-files", no_argument, nullptr, 3}, {"skip-parent-files-list", no_argument, nullptr, 4}, {"compression", required_argument, nullptr, 5}, + {"merge-by-name", no_argument, nullptr, 6}, {"verbosity", required_argument, nullptr, 'v'}, {"help", no_argument, nullptr, 'h'}, {nullptr, 0, nullptr, 0}}; @@ -70,6 +72,8 @@ int main(int argc, char* argv[]) skipParentFilesList = true; } else if (c == 5) { compression = atoi(optarg); + } else if (c == 6) { + mergeByName = true; } else if (c == 'v') { verbosity = atoi(optarg); } else if (c == 'h') { @@ -80,6 +84,7 @@ int main(int argc, char* argv[]) printf(" --skip-non-existing-files Flag to allow skipping of non-existing files in the input list.\n"); printf(" --skip-parent-files-list Flag to allow skipping the merging of the parent files list.\n"); printf(" --compression Compression algorithm / level to use (default: %d)\n", compression); + printf(" --merge-by-name Only merge TTrees from folders with the same name.\n"); printf(" --verbosity Verbosity of output (default: %d).\n", verbosity); return -1; } else { @@ -94,6 +99,9 @@ int main(int argc, char* argv[]) if (skipNonExistingFiles) { printf(" WARNING: Skipping non-existing files.\n"); } + if (mergeByName) { + printf(" Merging only folders with the same name.\n"); + } std::map trees; std::map sizeCompressed; @@ -112,6 +120,28 @@ int main(int argc, char* argv[]) TMap* parentFiles = nullptr; int totalMergedDFs = 0; int mergedDFs = 0; + + // Write all accumulated trees to outputDir, update stats, and clean up state. + auto flushTrees = [&](bool resetState) { + if (!outputDir) { + return; + } + for (auto const& tree : trees) { + outputDir->cd(); + tree.second->Write(); + sizeCompressed[tree.first] += tree.second->GetZipBytes(); + sizeUncompressed[tree.first] += tree.second->GetTotBytes(); + delete tree.second; + } + if (resetState) { + outputDir = nullptr; + trees.clear(); + offsets.clear(); + mergedDFs = 0; + currentDirSize = 0; + } + }; + while (in.good() && exitCode == 0) { in >> line; @@ -182,6 +212,14 @@ int main(int argc, char* argv[]) auto dfName = ((TObjString*)key1)->GetString().Data(); + // If merge-by-name is active, flush accumulated trees when the folder name changes + if (mergeByName && outputDir && std::string(outputDir->GetName()) != std::string(dfName)) { + if (verbosity > 0) { + printf("Folder name changed: closing folder %s.\n", outputDir->GetName()); + } + flushTrees(true); + } + if (verbosity > 0) { printf(" Processing folder %s\n", dfName); } @@ -396,21 +434,7 @@ int main(int argc, char* argv[]) if (verbosity > 0) { printf("Maximum size reached: %ld. Closing folder %s.\n", currentDirSize, dfName); } - for (auto const& tree : trees) { - // printf("Writing %s\n", tree.first.c_str()); - outputDir->cd(); - tree.second->Write(); - - // stats - sizeCompressed[tree.first] += tree.second->GetZipBytes(); - sizeUncompressed[tree.first] += tree.second->GetTotBytes(); - - delete tree.second; - } - outputDir = nullptr; - trees.clear(); - offsets.clear(); - mergedDFs = 0; + flushTrees(true); } } inputFile->Close(); @@ -421,16 +445,7 @@ int main(int argc, char* argv[]) parentFiles->Write("parentFiles", TObject::kSingleKey); } - for (auto const& tree : trees) { - outputDir->cd(); - tree.second->Write(); - - // stats - sizeCompressed[tree.first] += tree.second->GetZipBytes(); - sizeUncompressed[tree.first] += tree.second->GetTotBytes(); - - delete tree.second; - } + flushTrees(false); outputFile->Write(); outputFile->Close(); @@ -462,4 +477,4 @@ int main(int argc, char* argv[]) printf("\n"); return exitCode; -} +} \ No newline at end of file From c391bc139713ec61256b73b4f32dc25e165782f5 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 2 Apr 2026 12:31:39 +0200 Subject: [PATCH 455/701] DPL: speedup old split parts indexing --- Framework/Core/include/Framework/DataModelViews.h | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Framework/Core/include/Framework/DataModelViews.h b/Framework/Core/include/Framework/DataModelViews.h index 285f5ef15154e..2a8b057525f41 100644 --- a/Framework/Core/include/Framework/DataModelViews.h +++ b/Framework/Core/include/Framework/DataModelViews.h @@ -100,12 +100,22 @@ struct get_pair { } size_t diff = self.pairId - count; if (header->splitPayloadParts > 1 && header->splitPayloadIndex == header->splitPayloadParts) { + // New style: one header followed by splitPayloadParts contiguous payloads. count += header->splitPayloadParts; if (self.pairId < count) { return {mi, mi + 1 + diff}; } mi += header->splitPayloadParts + 1; + } else if (header->splitPayloadParts > 1 && header->splitPayloadIndex != header->splitPayloadParts) { + // Old style multi-part: splitPayloadParts [header, payload] pairs. + // We are at the first pair of the block; jump directly. + if (diff < header->splitPayloadParts) { + return {mi + 2 * diff, mi + 2 * diff + 1}; + } + count += header->splitPayloadParts; + mi += 2 * header->splitPayloadParts; } else { + // Single [header, payload] pair (splitPayloadParts == 0). if (self.pairId == count) { return {mi, mi + 1}; } From 2cec68714384aa7fe0b126a336b30d1da50437df Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Thu, 2 Apr 2026 20:53:43 +0200 Subject: [PATCH 456/701] [ALICE3] Remove TrackerACTS from CMakeLists.txt (#15250) --- Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt index 59a7f47955938..81a75e209124a 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt @@ -18,7 +18,6 @@ o2_add_library(TRKReconstruction SOURCES src/TimeFrame.cxx src/Clusterer.cxx $<$:src/ClustererACTS.cxx> - $<$:src/TrackerACTS.cxx> PUBLIC_LINK_LIBRARIES O2::ITStracking O2::GPUCommon @@ -46,8 +45,7 @@ set(dictHeaders include/TRKReconstruction/TimeFrame.h include/TRKReconstruction/Clusterer.h) if(Acts_FOUND) - list(APPEND dictHeaders include/TRKReconstruction/ClustererACTS.h - include/TRKReconstruction/TrackerACTS.h) + list(APPEND dictHeaders include/TRKReconstruction/ClustererACTS.h) endif() o2_target_root_dictionary(TRKReconstruction From 1d8469de4bf5231211be7a280e16f2eb3ca3fbe3 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 1 Apr 2026 21:41:46 +0200 Subject: [PATCH 457/701] DPL: allow generic control of signposts from the driver --- Framework/Core/src/WSDriverClient.cxx | 66 ++++++++----------- .../src/FrameworkGUIDeviceInspector.cxx | 27 +++++--- 2 files changed, 47 insertions(+), 46 deletions(-) diff --git a/Framework/Core/src/WSDriverClient.cxx b/Framework/Core/src/WSDriverClient.cxx index 43a407536cb59..97ea1b3dbf66a 100644 --- a/Framework/Core/src/WSDriverClient.cxx +++ b/Framework/Core/src/WSDriverClient.cxx @@ -188,48 +188,40 @@ void on_connect(uv_connect_t* connection, int status) state.tracingFlags = tracingFlags; }); - client->observe("/log-streams", [ref = context->ref](std::string_view cmd) { - auto& state = ref.get(); - static constexpr int prefixSize = std::string_view{"/log-streams "}.size(); - if (prefixSize > cmd.size()) { - LOG(error) << "Malformed log-streams request"; + client->observe("/signpost:enable", [](std::string_view cmd) { + static constexpr int prefixSize = std::string_view{"/signpost:enable "}.size(); + if (cmd.size() <= prefixSize) { + LOG(error) << "Malformed /signpost:enable request"; return; } - cmd.remove_prefix(prefixSize); - int logStreams = 0; + std::string name(cmd.substr(prefixSize)); + o2_walk_logs([](char const* logName, void* l, void* context) -> bool { + auto* log = static_cast<_o2_log_t*>(l); + auto* target = static_cast(context); + if (*target == logName) { + _o2_log_set_stacktrace(log, log->defaultStacktrace); + return false; + } + return true; + }, &name); + }); - auto error = std::from_chars(cmd.data(), cmd.data() + cmd.size(), logStreams); - if (error.ec != std::errc()) { - LOG(error) << "Malformed log-streams mask"; + client->observe("/signpost:disable", [](std::string_view cmd) { + static constexpr int prefixSize = std::string_view{"/signpost:disable "}.size(); + if (cmd.size() <= prefixSize) { + LOG(error) << "Malformed /signpost:disable request"; return; } - LOGP(info, "Logstreams flags set to {}", logStreams); - state.logStreams = logStreams; - if ((state.logStreams & DeviceState::LogStreams::DEVICE_LOG) != 0) { - O2_LOG_ENABLE(device); - } else { - O2_LOG_DISABLE(device); - } - if ((state.logStreams & DeviceState::LogStreams::COMPLETION_LOG) != 0) { - O2_LOG_ENABLE(completion); - } else { - O2_LOG_DISABLE(completion); - } - if ((state.logStreams & DeviceState::LogStreams::MONITORING_SERVICE_LOG) != 0) { - O2_LOG_ENABLE(monitoring_service); - } else { - O2_LOG_DISABLE(monitoring_service); - } - if ((state.logStreams & DeviceState::LogStreams::DATA_PROCESSOR_CONTEXT_LOG) != 0) { - O2_LOG_ENABLE(data_processor_context); - } else { - O2_LOG_DISABLE(data_processor_context); - } - if ((state.logStreams & DeviceState::LogStreams::STREAM_CONTEXT_LOG) != 0) { - O2_LOG_ENABLE(stream_context); - } else { - O2_LOG_DISABLE(stream_context); - } + std::string name(cmd.substr(prefixSize)); + o2_walk_logs([](char const* logName, void* l, void* context) -> bool { + auto* log = static_cast<_o2_log_t*>(l); + auto* target = static_cast(context); + if (*target == logName) { + _o2_log_set_stacktrace(log, 0); + return false; + } + return true; + }, &name); }); // Client will be filled in the line after. I can probably have a single diff --git a/Framework/GUISupport/src/FrameworkGUIDeviceInspector.cxx b/Framework/GUISupport/src/FrameworkGUIDeviceInspector.cxx index aa546b8a9ab49..b29e024ec641e 100644 --- a/Framework/GUISupport/src/FrameworkGUIDeviceInspector.cxx +++ b/Framework/GUISupport/src/FrameworkGUIDeviceInspector.cxx @@ -400,16 +400,25 @@ void displayDeviceInspector(DeviceSpec const& spec, } } - bool logsChanged = false; if (ImGui::CollapsingHeader("Signposts", ImGuiTreeNodeFlags_DefaultOpen)) { - logsChanged = ImGui::CheckboxFlags("Device", &control.logStreams, DeviceState::LogStreams::DEVICE_LOG); - logsChanged = ImGui::CheckboxFlags("Completion", &control.logStreams, DeviceState::LogStreams::COMPLETION_LOG); - logsChanged = ImGui::CheckboxFlags("Monitoring", &control.logStreams, DeviceState::LogStreams::MONITORING_SERVICE_LOG); - logsChanged = ImGui::CheckboxFlags("DataProcessorContext", &control.logStreams, DeviceState::LogStreams::DATA_PROCESSOR_CONTEXT_LOG); - logsChanged = ImGui::CheckboxFlags("StreamContext", &control.logStreams, DeviceState::LogStreams::STREAM_CONTEXT_LOG); - if (logsChanged && control.controller) { - std::string cmd = fmt::format("/log-streams {}", control.logStreams); - control.controller->write(cmd.c_str(), cmd.size()); + static const struct { + const char* label; + int bit; + const char* fullName; + } kStreams[] = { + {"Device", DeviceState::LogStreams::DEVICE_LOG, "ch.cern.aliceo2.device"}, + {"Completion", DeviceState::LogStreams::COMPLETION_LOG, "ch.cern.aliceo2.completion"}, + {"Monitoring", DeviceState::LogStreams::MONITORING_SERVICE_LOG, "ch.cern.aliceo2.monitoring_service"}, + {"DataProcessorContext", DeviceState::LogStreams::DATA_PROCESSOR_CONTEXT_LOG, "ch.cern.aliceo2.data_processor_context"}, + {"StreamContext", DeviceState::LogStreams::STREAM_CONTEXT_LOG, "ch.cern.aliceo2.stream_context"}, + }; + for (auto const& s : kStreams) { + if (ImGui::CheckboxFlags(s.label, &control.logStreams, s.bit) && control.controller) { + bool enabled = (control.logStreams & s.bit) != 0; + std::string cmd = enabled ? fmt::format("/signpost:enable {}", s.fullName) + : fmt::format("/signpost:disable {}", s.fullName); + control.controller->write(cmd.c_str(), cmd.size()); + } } } From 401d04c82258cb5270de02dc55f496627f0c9597 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 1 Apr 2026 21:41:46 +0200 Subject: [PATCH 458/701] DPL MCP: allow client to control signposts --- .../scripts/dpl-mcp-server/dpl_mcp_server.py | 31 ++++++++ Framework/Core/src/StatusWebSocketHandler.cxx | 71 +++++++++++++++++++ Framework/Core/src/StatusWebSocketHandler.h | 13 ++++ 3 files changed, 115 insertions(+) diff --git a/Framework/Core/scripts/dpl-mcp-server/dpl_mcp_server.py b/Framework/Core/scripts/dpl-mcp-server/dpl_mcp_server.py index bc04acf026188..febb1278f6045 100644 --- a/Framework/Core/scripts/dpl-mcp-server/dpl_mcp_server.py +++ b/Framework/Core/scripts/dpl-mcp-server/dpl_mcp_server.py @@ -237,6 +237,37 @@ async def get_logs(max_lines: int = 100) -> str: return "\n".join(lines) +@mcp.tool() +async def enable_signpost(device: str, streams: list[str]) -> str: + """Enable one or more signpost log streams for a DPL device. + + Signpost streams produce detailed trace output visible in the device logs. + Use get_logs() after subscribing to see the output. + + Known stream names (full form): ch.cern.aliceo2.device, + ch.cern.aliceo2.completion, ch.cern.aliceo2.monitoring_service, + ch.cern.aliceo2.data_processor_context, ch.cern.aliceo2.stream_context. + + Args: + device: Device name as shown by list_devices, or "" for the driver. + streams: List of full signpost log names to enable. + """ + await _send({"cmd": "enable_signpost", "device": device, "streams": streams}) + return f"Enabled {len(streams)} signpost stream(s) for '{device or 'driver'}': {', '.join(streams)}" + + +@mcp.tool() +async def disable_signpost(device: str, streams: list[str]) -> str: + """Disable one or more signpost log streams for a DPL device. + + Args: + device: Device name as shown by list_devices, or "" for the driver. + streams: List of full signpost log names to disable. + """ + await _send({"cmd": "disable_signpost", "device": device, "streams": streams}) + return f"Disabled {len(streams)} signpost stream(s) for '{device or 'driver'}': {', '.join(streams)}" + + @mcp.tool() async def get_updates(max_updates: int = 50) -> str: """Drain and return buffered metric update frames received since the last call. diff --git a/Framework/Core/src/StatusWebSocketHandler.cxx b/Framework/Core/src/StatusWebSocketHandler.cxx index db715eff6592d..cdf08c4f2f349 100644 --- a/Framework/Core/src/StatusWebSocketHandler.cxx +++ b/Framework/Core/src/StatusWebSocketHandler.cxx @@ -12,11 +12,15 @@ #include "StatusWebSocketHandler.h" #include "DPLWebSocket.h" #include "DriverServerContext.h" +#include "Framework/DeviceControl.h" +#include "Framework/DeviceController.h" #include "Framework/DeviceInfo.h" #include "Framework/DeviceMetricsInfo.h" #include "Framework/DeviceSpec.h" +#include "Framework/DeviceState.h" #include "Framework/DeviceStateEnums.h" #include "Framework/LogParsingHelpers.h" +#include "Framework/Signpost.h" #include #include #include @@ -250,6 +254,10 @@ void StatusWebSocketHandler::frame(char const* data, size_t s) handleSubscribeLogs(deviceName); } else if (cmd == "unsubscribe_logs") { handleUnsubscribeLogs(deviceName); + } else if (cmd == "enable_signpost") { + handleEnableSignpost(deviceName, extractArrayField(msg, "streams")); + } else if (cmd == "disable_signpost") { + handleDisableSignpost(deviceName, extractArrayField(msg, "streams")); } } @@ -433,6 +441,69 @@ size_t StatusWebSocketHandler::findDeviceIndex(std::string_view name) const return SIZE_MAX; } +void StatusWebSocketHandler::handleEnableSignpost(std::string_view deviceName, std::string_view streamsArr) +{ + if (streamsArr.empty()) { + return; + } + if (deviceName.empty()) { + // Driver process — toggle in-process via o2_walk_logs. + forEachStringInArray(streamsArr, [](std::string_view streamName) { + std::string target(streamName); + o2_walk_logs([](char const* name, void* l, void* context) -> bool { + auto* log = static_cast<_o2_log_t*>(l); + if (static_cast(context)->compare(name) == 0) { + _o2_log_set_stacktrace(log, log->defaultStacktrace); + return false; + } + return true; + }, &target); + }); + } else { + size_t di = findDeviceIndex(deviceName); + if (di == SIZE_MAX || di >= mContext.controls->size() || !(*mContext.controls)[di].controller) { + return; + } + auto* controller = (*mContext.controls)[di].controller; + forEachStringInArray(streamsArr, [controller](std::string_view name) { + std::string cmd = "/signpost:enable "; + cmd += name; + controller->write(cmd.c_str(), cmd.size()); + }); + } +} + +void StatusWebSocketHandler::handleDisableSignpost(std::string_view deviceName, std::string_view streamsArr) +{ + if (streamsArr.empty()) { + return; + } + if (deviceName.empty()) { + forEachStringInArray(streamsArr, [](std::string_view streamName) { + std::string target(streamName); + o2_walk_logs([](char const* name, void* l, void* context) -> bool { + auto* log = static_cast<_o2_log_t*>(l); + if (static_cast(context)->compare(name) == 0) { + _o2_log_set_stacktrace(log, 0); + return false; + } + return true; + }, &target); + }); + } else { + size_t di = findDeviceIndex(deviceName); + if (di == SIZE_MAX || di >= mContext.controls->size() || !(*mContext.controls)[di].controller) { + return; + } + auto* controller = (*mContext.controls)[di].controller; + forEachStringInArray(streamsArr, [controller](std::string_view name) { + std::string cmd = "/signpost:disable "; + cmd += name; + controller->write(cmd.c_str(), cmd.size()); + }); + } +} + void StatusWebSocketHandler::handleSubscribeLogs(std::string_view deviceName) { size_t di = findDeviceIndex(deviceName); diff --git a/Framework/Core/src/StatusWebSocketHandler.h b/Framework/Core/src/StatusWebSocketHandler.h index 86a460e289440..3b040d68e26f0 100644 --- a/Framework/Core/src/StatusWebSocketHandler.h +++ b/Framework/Core/src/StatusWebSocketHandler.h @@ -41,6 +41,17 @@ struct WSDPLHandler; /// {"cmd":"unsubscribe_logs","device":""} /// → driver stops pushing log lines for the device /// +/// {"cmd":"enable_signpost","device":"","streams":["device","completion",...]} +/// → enable the named signpost log streams for a device (or the driver if device=="") +/// → known streams: "device","completion","monitoring_service","data_processor_context","stream_context" +/// +/// {"cmd":"disable_signpost","device":"","streams":["device","completion",...]} +/// → disable the named signpost log streams for a device +/// +/// {"cmd":"list_signposts"} +/// → driver replies with {"type":"signposts_list","streams":["device","completion",...]} +/// → lists the known stream names +/// /// Protocol (driver → client): /// {"type":"snapshot","devices":[{"name","pid","active","streamingState","deviceState"},...]} /// → sent once on connect; contains no metrics or logs @@ -84,6 +95,8 @@ struct StatusWebSocketHandler : public WebSocketHandler { void handleUnsubscribe(std::string_view deviceName, std::string_view metricsJson); void handleSubscribeLogs(std::string_view deviceName); void handleUnsubscribeLogs(std::string_view deviceName); + void handleEnableSignpost(std::string_view deviceName, std::string_view streamsArr); + void handleDisableSignpost(std::string_view deviceName, std::string_view streamsArr); size_t findDeviceIndex(std::string_view name) const; DriverServerContext& mContext; From 12611f702b492d69897f8c24b85e1b26a0a336c1 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Fri, 3 Apr 2026 12:12:10 +0200 Subject: [PATCH 459/701] DPL MCP: add ability to start a stopped topology --- Framework/Core/src/StatusWebSocketHandler.cxx | 13 +++++++++++++ Framework/Core/src/StatusWebSocketHandler.h | 1 + 2 files changed, 14 insertions(+) diff --git a/Framework/Core/src/StatusWebSocketHandler.cxx b/Framework/Core/src/StatusWebSocketHandler.cxx index cdf08c4f2f349..065a6f4b05b4a 100644 --- a/Framework/Core/src/StatusWebSocketHandler.cxx +++ b/Framework/Core/src/StatusWebSocketHandler.cxx @@ -13,6 +13,8 @@ #include "DPLWebSocket.h" #include "DriverServerContext.h" #include "Framework/DeviceControl.h" +#include +#include #include "Framework/DeviceController.h" #include "Framework/DeviceInfo.h" #include "Framework/DeviceMetricsInfo.h" @@ -254,6 +256,8 @@ void StatusWebSocketHandler::frame(char const* data, size_t s) handleSubscribeLogs(deviceName); } else if (cmd == "unsubscribe_logs") { handleUnsubscribeLogs(deviceName); + } else if (cmd == "start_devices") { + handleStartDevices(); } else if (cmd == "enable_signpost") { handleEnableSignpost(deviceName, extractArrayField(msg, "streams")); } else if (cmd == "disable_signpost") { @@ -441,6 +445,15 @@ size_t StatusWebSocketHandler::findDeviceIndex(std::string_view name) const return SIZE_MAX; } +void StatusWebSocketHandler::handleStartDevices() +{ + for (auto const& info : *mContext.infos) { + if (info.active) { + kill(info.pid, SIGCONT); + } + } +} + void StatusWebSocketHandler::handleEnableSignpost(std::string_view deviceName, std::string_view streamsArr) { if (streamsArr.empty()) { diff --git a/Framework/Core/src/StatusWebSocketHandler.h b/Framework/Core/src/StatusWebSocketHandler.h index 3b040d68e26f0..fb2f0beebbec2 100644 --- a/Framework/Core/src/StatusWebSocketHandler.h +++ b/Framework/Core/src/StatusWebSocketHandler.h @@ -95,6 +95,7 @@ struct StatusWebSocketHandler : public WebSocketHandler { void handleUnsubscribe(std::string_view deviceName, std::string_view metricsJson); void handleSubscribeLogs(std::string_view deviceName); void handleUnsubscribeLogs(std::string_view deviceName); + void handleStartDevices(); void handleEnableSignpost(std::string_view deviceName, std::string_view streamsArr); void handleDisableSignpost(std::string_view deviceName, std::string_view streamsArr); size_t findDeviceIndex(std::string_view name) const; From 37e43e384b375dde431c1a4f85bee992af94b83b Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Fri, 3 Apr 2026 12:12:10 +0200 Subject: [PATCH 460/701] DPL MCP: add tool to resume a stopped topology --- .../Core/scripts/dpl-mcp-server/dpl_mcp_server.py | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/Framework/Core/scripts/dpl-mcp-server/dpl_mcp_server.py b/Framework/Core/scripts/dpl-mcp-server/dpl_mcp_server.py index febb1278f6045..3900a646632a1 100644 --- a/Framework/Core/scripts/dpl-mcp-server/dpl_mcp_server.py +++ b/Framework/Core/scripts/dpl-mcp-server/dpl_mcp_server.py @@ -237,6 +237,16 @@ async def get_logs(max_lines: int = 100) -> str: return "\n".join(lines) +@mcp.tool() +async def start_devices() -> str: + """Resume all stopped DPL devices (send SIGCONT). + + Use this when the workflow was started with -s (all devices paused). + """ + await _send({"cmd": "start_devices"}) + return "Sent SIGCONT to all active devices." + + @mcp.tool() async def enable_signpost(device: str, streams: list[str]) -> str: """Enable one or more signpost log streams for a DPL device. From 74b32651eba638fb3beb594c6be6b9657a0cf41c Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Fri, 3 Apr 2026 18:27:14 +0200 Subject: [PATCH 461/701] DPL: add formatters for ConfigParamSpec --- Framework/Core/COOKBOOK.md | 40 +++ Framework/Core/scripts/lldb_o2_formatters.py | 296 +++++++++++++++++++ 2 files changed, 336 insertions(+) create mode 100644 Framework/Core/scripts/lldb_o2_formatters.py diff --git a/Framework/Core/COOKBOOK.md b/Framework/Core/COOKBOOK.md index c327651ae53ca..a99890bdcfa85 100644 --- a/Framework/Core/COOKBOOK.md +++ b/Framework/Core/COOKBOOK.md @@ -549,3 +549,43 @@ Streams can be explicitly enabled or disabled in code using the `O2_SIGNPOST_ENA If a process is already running and you wish to enable one or more of its signposts logs, you can do so using the `o2-log` utility, passing the address of the log to enable and the PID of the running process. E.g. `o2-log -p -a `. Finally, on macOS, you can also use Instruments to visualise your Signpost, just like any other macOS application. In order to do so you need to enable the "Signpost" instrument, making sure you add `ch.cern.aliceo2.completion` to the list of loggers to watch. + +## Improving lldb experience + +You can make lldb understand some of the O2 types by having the following +in your `~/.lldbinit` (or `$PWD/.lldbinit`): + +```lldb +command script import Framework/Core/scripts/lldb_o2_formatters.py +``` + + +Before: + +```gdb +(o2::framework::ConfigParamSpec &) 0x0000000774871e20: { + name = "timeframes-rate-limit-ipcid" + type = String + defaultValue = { + mStore = (__data = "\xa0\xae\x80t\a") + mType = String + mSize = 1 + } + help = (str = "Suffix for IPC channel for metric-feedback, -1 = disable") + kind = kGeneric +} +``` + +After: + +```gdb +(o2::framework::ConfigParamSpec &) 0x00000007cac75e20: { + name = "timeframes-rate-limit-ipcid" + type = String + defaultValue = { + value = 0x00000007cac0eea0 "-1" + } + help = (str = "Suffix for IPC channel for metric-feedback, -1 = disable") + kind = kGeneric +} +``` diff --git a/Framework/Core/scripts/lldb_o2_formatters.py b/Framework/Core/scripts/lldb_o2_formatters.py new file mode 100644 index 0000000000000..84bc7cda8dac3 --- /dev/null +++ b/Framework/Core/scripts/lldb_o2_formatters.py @@ -0,0 +1,296 @@ +# 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. +# +# lldb data formatters for o2::framework types. +# +# Usage: add to ~/.lldbinit or a project .lldbinit: +# command script import /path/to/O2/Framework/Core/scripts/lldb_o2_formatters.py + +import lldb + +# o2::framework::VariantType enum values (must match Variant.h) +_VARIANT_TYPE = { + 0: 'Int', + 1: 'Int64', + 2: 'Float', + 3: 'Double', + 4: 'String', + 5: 'Bool', + 6: 'ArrayInt', + 7: 'ArrayFloat', + 8: 'ArrayDouble', + 9: 'ArrayBool', + 10: 'ArrayString', + 11: 'Array2DInt', + 12: 'Array2DFloat', + 13: 'Array2DDouble', + 14: 'LabeledArrayInt', + 15: 'LabeledArrayFloat', + 16: 'LabeledArrayDouble', + 17: 'UInt8', + 18: 'UInt16', + 19: 'UInt32', + 20: 'UInt64', + 21: 'Int8', + 22: 'Int16', + 23: 'LabeledArrayString', + 24: 'Empty', + 25: 'Dict', + 26: 'Unknown', +} + +# Map VariantType value → (C type name for FindFirstType, is_pointer_to_value) +# is_pointer_to_value=True means mStore holds a T* pointing to heap data (arrays) +_SIMPLE_TYPES = { + 0: ('int', False), + 1: ('long long', False), + 2: ('float', False), + 3: ('double', False), + 5: ('bool', False), + 17: ('unsigned char', False), + 18: ('unsigned short',False), + 19: ('unsigned int', False), + 20: ('unsigned long long', False), + 21: ('signed char', False), + 22: ('short', False), +} + +_ARRAY_ELEM_TYPES = { + 6: 'int', + 7: 'float', + 8: 'double', + 9: 'bool', +} + +MAX_ARRAY_DISPLAY = 16 + + +import struct as _struct + + +def _read_pointer(process, addr): + err = lldb.SBError() + ptr_size = process.GetAddressByteSize() + data = process.ReadMemory(addr, ptr_size, err) + if err.Fail() or not data: + return None + fmt = '"' + b = raw if isinstance(raw[0], int) else bytes(ord(c) for c in raw) + if (b[0] & 1) == 0: # short form + size = b[0] >> 1 + text = b[1:1 + size].decode('utf-8', errors='replace') + else: # long form + size = _struct.unpack_from('"' + heap = process.ReadMemory(data_ptr, min(size, 512), err) + if err.Fail() or not heap: + return '""' + h = heap if isinstance(heap[0], int) else bytes(ord(c) for c in heap) + text = h[:size].decode('utf-8', errors='replace') + if size > 512: + text += '...' + return f'"{text}"' + + +def variant_summary(valobj, _internal_dict): + # Use GetNonSyntheticValue() so we see the real struct members even when + # the synthetic provider has replaced the children with decoded values. + raw = valobj.GetNonSyntheticValue() + mType_val = raw.GetChildMemberWithName('mType') + if not mType_val.IsValid(): + return '' + + mType = mType_val.GetValueAsUnsigned(26) # default Unknown + mSize = raw.GetChildMemberWithName('mSize').GetValueAsUnsigned(1) + mStore = raw.GetChildMemberWithName('mStore') + store_addr = mStore.GetLoadAddress() + + type_name = _VARIANT_TYPE.get(mType, f'') + target = valobj.GetTarget() + process = valobj.GetProcess() + + # --- simple scalar types --- + if mType in _SIMPLE_TYPES: + ctype, _ = _SIMPLE_TYPES[mType] + t = target.FindFirstType(ctype) + if t.IsValid(): + v = valobj.CreateValueFromAddress('v', store_addr, t) + return f'{type_name}({v.GetValue()})' + return f'{type_name}(?)' + + # --- String (const char* stored in mStore) --- + if mType == 4: + ptr = _read_pointer(process, store_addr) + if ptr and ptr != 0: + s = _read_cstring(process, ptr) + return f'String("{s}")' + return 'String(null)' + + # --- C-style numeric arrays (int*, float*, double*, bool*) --- + if mType in _ARRAY_ELEM_TYPES: + elem_type_name = _ARRAY_ELEM_TYPES[mType] + ptr = _read_pointer(process, store_addr) + if not ptr or ptr == 0: + return f'{type_name}(null)' + elem_t = target.FindFirstType(elem_type_name) + if not elem_t.IsValid(): + return f'{type_name}(? x {mSize})' + count = min(mSize, MAX_ARRAY_DISPLAY) + items = [] + for i in range(count): + v = valobj.CreateValueFromAddress(f'e{i}', ptr + i * elem_t.GetByteSize(), elem_t) + items.append(v.GetValue() or '?') + result = f'{type_name}([{", ".join(items)}]' + if mSize > MAX_ARRAY_DISPLAY: + result += f', ... ({mSize} total)' + result += ')' + return result + + # --- ArrayString: std::vector stored via placement new in mStore --- + if mType == 10: + # libc++ std::vector layout: __begin_, __end_, __end_cap_ (all pointers) + # libc++ std::string is always 24 bytes on 64-bit (SSO layout) + STR_SIZE = 24 + ptr_size = process.GetAddressByteSize() + begin_ptr = _read_pointer(process, store_addr) + end_ptr = _read_pointer(process, store_addr + ptr_size) + if begin_ptr is None or end_ptr is None: + return 'ArrayString(?)' + + count = (end_ptr - begin_ptr) // STR_SIZE if end_ptr >= begin_ptr else 0 + items = [] + for i in range(min(count, MAX_ARRAY_DISPLAY)): + items.append(_read_libcxx_string(process, begin_ptr + i * STR_SIZE)) + result = f'ArrayString([{", ".join(items)}]' + if count > MAX_ARRAY_DISPLAY: + result += f', ... ({count} total)' + result += ')' + return result + + return f'{type_name}(mSize={mSize})' + + +class VariantSyntheticProvider: + """Synthetic children for o2::framework::Variant — exposes decoded value as child.""" + + def __init__(self, valobj, _internal_dict): + self.valobj = valobj + self.children = [] + + def num_children(self): + return len(self.children) + + def get_child_index(self, name): + for i, (n, _) in enumerate(self.children): + if n == name: + return i + return -1 + + def get_child_at_index(self, index): + if 0 <= index < len(self.children): + return self.children[index][1] + return None + + def update(self): + self.children = [] + # Use GetNonSyntheticValue() to read the real struct members. + raw = self.valobj.GetNonSyntheticValue() + mType = raw.GetChildMemberWithName('mType').GetValueAsUnsigned(26) + mSize = raw.GetChildMemberWithName('mSize').GetValueAsUnsigned(1) + mStore = raw.GetChildMemberWithName('mStore') + store_addr = mStore.GetLoadAddress() + target = self.valobj.GetTarget() + process = self.valobj.GetProcess() + + if mType in _SIMPLE_TYPES: + ctype, _ = _SIMPLE_TYPES[mType] + t = target.FindFirstType(ctype) + if t.IsValid(): + v = self.valobj.CreateValueFromAddress('value', store_addr, t) + self.children.append(('value', v)) + + elif mType == 4: # String + ptr = _read_pointer(process, store_addr) + if ptr and ptr != 0: + char_t = target.FindFirstType('char').GetPointerType() + v = self.valobj.CreateValueFromAddress('value', store_addr, char_t) + self.children.append(('value', v)) + + elif mType in _ARRAY_ELEM_TYPES: + elem_type_name = _ARRAY_ELEM_TYPES[mType] + ptr = _read_pointer(process, store_addr) + if ptr and ptr != 0: + elem_t = target.FindFirstType(elem_type_name) + if elem_t.IsValid(): + for i in range(min(mSize, MAX_ARRAY_DISPLAY)): + v = self.valobj.CreateValueFromAddress(f'[{i}]', ptr + i * elem_t.GetByteSize(), elem_t) + self.children.append((f'[{i}]', v)) + + elif mType == 10: # ArrayString + # std::vector via placement new; std::string = 24 bytes (libc++ 64-bit) + STR_SIZE = 24 + ptr_size = process.GetAddressByteSize() + begin_ptr = _read_pointer(process, store_addr) + end_ptr = _read_pointer(process, store_addr + ptr_size) + char_t = target.FindFirstType('char') + if begin_ptr is not None and end_ptr is not None and end_ptr >= begin_ptr and char_t.IsValid(): + count = (end_ptr - begin_ptr) // STR_SIZE + err = lldb.SBError() + for i in range(min(count, MAX_ARRAY_DISPLAY)): + str_addr = begin_ptr + i * STR_SIZE + raw = process.ReadMemory(str_addr, STR_SIZE, err) + if err.Fail() or not raw: + continue + b = raw if isinstance(raw[0], int) else bytes(ord(c) for c in raw) + if (b[0] & 1) == 0: # short form: data inline at offset 1 + data_addr = str_addr + 1 + sz = max(b[0] >> 1, 1) + else: # long form: data pointer at offset 16 + data_addr = _struct.unpack_from(' 0 + + +def __lldb_init_module(debugger, _internal_dict): + debugger.HandleCommand( + 'type summary add -x "^o2::framework::Variant$" ' + '--python-function lldb_o2_formatters.variant_summary' + ) + debugger.HandleCommand( + 'type synthetic add -x "^o2::framework::Variant$" ' + '--python-class lldb_o2_formatters.VariantSyntheticProvider' + ) + print('o2::framework::Variant formatters loaded.') From bccbaff4e0d76463ca798aa7bc612d2f2f214ebb Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 5 Apr 2026 21:31:13 +0200 Subject: [PATCH 462/701] DPL: add helper get_next_pair This will simplify and speed up iterations over multipart messages. --- Framework/Core/CMakeLists.txt | 1 + .../Core/include/Framework/DataModelViews.h | 37 ++++ Framework/Core/test/test_DataModelViews.cxx | 199 ++++++++++++++++++ 3 files changed, 237 insertions(+) create mode 100644 Framework/Core/test/test_DataModelViews.cxx diff --git a/Framework/Core/CMakeLists.txt b/Framework/Core/CMakeLists.txt index 0e67e1c0cc623..45af3ad6c59cc 100644 --- a/Framework/Core/CMakeLists.txt +++ b/Framework/Core/CMakeLists.txt @@ -238,6 +238,7 @@ add_executable(o2-test-framework-core test/test_IndexBuilder.cxx test/test_InputRecord.cxx test/test_InputRecordWalker.cxx + test/test_DataModelViews.cxx test/test_InputSpan.cxx test/test_InputSpec.cxx test/test_LogParsingHelpers.cxx diff --git a/Framework/Core/include/Framework/DataModelViews.h b/Framework/Core/include/Framework/DataModelViews.h index 2a8b057525f41..53d6e6615b96e 100644 --- a/Framework/Core/include/Framework/DataModelViews.h +++ b/Framework/Core/include/Framework/DataModelViews.h @@ -127,6 +127,43 @@ struct get_pair { } }; +// Advance from a DataRefIndices to the next one in O(1), reading only the +// current header. Intended for use in iterators so that ++ is O(1) rather +// than the O(n) while-loop that get_pair requires. +// +// New-style block (splitPayloadIndex == splitPayloadParts > 1): +// layout: [header, payload_0, payload_1, ..., payload_{N-1}] +// advance within block while payloads remain, then jump to the next block. +// +// Old-style block (splitPayloadIndex != splitPayloadParts, splitPayloadParts > 1) +// or single pair (splitPayloadParts == 0): +// layout: [header, payload] – always advance by two messages. +struct get_next_pair { + DataRefIndices current; + template + requires std::ranges::random_access_range && std::ranges::sized_range + friend DataRefIndices operator|(R&& r, get_next_pair self) + { + size_t hIdx = self.current.headerIdx; + auto* header = o2::header::get(r[hIdx]->GetData()); + if (!header) { + throw std::runtime_error("Not a DataHeader"); + } + if (header->splitPayloadParts > 1 && header->splitPayloadIndex == header->splitPayloadParts) { + // New-style block: one header followed by splitPayloadParts contiguous payloads. + if (self.current.payloadIdx < hIdx + header->splitPayloadParts) { + // More sub-payloads remain in this block. + return {hIdx, self.current.payloadIdx + 1}; + } + // Last sub-payload consumed; move to the first pair of the next block. + size_t nextHIdx = hIdx + header->splitPayloadParts + 1; + return {nextHIdx, nextHIdx + 1}; + } + // Old-style [header, payload] pairs or a single pair: advance by two messages. + return {hIdx + 2, hIdx + 3}; + } +}; + struct get_dataref_indices { size_t part; size_t subPart; diff --git a/Framework/Core/test/test_DataModelViews.cxx b/Framework/Core/test/test_DataModelViews.cxx new file mode 100644 index 0000000000000..37d163e9e41c6 --- /dev/null +++ b/Framework/Core/test/test_DataModelViews.cxx @@ -0,0 +1,199 @@ +// 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 "Framework/DataModelViews.h" +#include "Framework/DataProcessingHeader.h" +#include "Headers/DataHeader.h" +#include "Headers/Stack.h" +#include +#include +#include + +using namespace o2::framework; +using DataHeader = o2::header::DataHeader; +using Stack = o2::header::Stack; + +namespace +{ +// Build a header message containing a DataHeader with the given split-payload fields. +fair::mq::MessagePtr makeHeader(fair::mq::TransportFactory& transport, + uint32_t splitPayloadParts, uint32_t splitPayloadIndex) +{ + DataHeader dh; + dh.dataDescription = "TEST"; + dh.dataOrigin = "TST"; + dh.subSpecification = 0; + dh.splitPayloadParts = splitPayloadParts; + dh.splitPayloadIndex = splitPayloadIndex; + DataProcessingHeader dph{0, 1}; + Stack stack{dh, dph}; + auto msg = transport.CreateMessage(stack.size()); + memcpy(msg->GetData(), stack.data(), stack.size()); + return msg; +} + +fair::mq::MessagePtr makePayload(fair::mq::TransportFactory& transport) +{ + return transport.CreateMessage(4); +} +} // namespace + +// --------------------------------------------------------------------------- +// Single [header, payload] pair (splitPayloadParts == 0) +// --------------------------------------------------------------------------- +TEST_CASE("SinglePair") +{ + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + + std::vector msgs; + msgs.emplace_back(makeHeader(*transport, 0, 0)); + msgs.emplace_back(makePayload(*transport)); + + REQUIRE((msgs | count_parts{}) == 1); + REQUIRE((msgs | count_payloads{}) == 1); + REQUIRE((msgs | get_num_payloads{0}) == 1); + + auto idx = msgs | get_pair{0}; + REQUIRE(idx.headerIdx == 0); + REQUIRE(idx.payloadIdx == 1); + + // Advancing past the only pair goes out of range. + auto next = msgs | get_next_pair{idx}; + REQUIRE(next.headerIdx >= msgs.size()); +} + +// --------------------------------------------------------------------------- +// Old-style multipart: N [header, payload] pairs, each with splitPayloadParts=N +// and splitPayloadIndex running 0..N-1 (0-indexed). +// The new-style sentinel is splitPayloadIndex == splitPayloadParts, which is +// never true for old-style (max index is N-1 < N). +// Layout: [h0,p0, h1,p1, h2,p2] +// count_parts returns N because each [h,p] pair is a separate logical part. +// --------------------------------------------------------------------------- +TEST_CASE("OldStyleMultipart") +{ + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + constexpr uint32_t N = 3; + + std::vector msgs; + for (uint32_t i = 0; i < N; ++i) { + msgs.emplace_back(makeHeader(*transport, N, i)); // 0-indexed + msgs.emplace_back(makePayload(*transport)); + } + + REQUIRE((msgs | count_parts{}) == N); // N separate logical parts + REQUIRE((msgs | count_payloads{}) == N); // one payload each + for (uint32_t i = 0; i < N; ++i) { + REQUIRE((msgs | get_num_payloads{i}) == 1); + } + + // get_pair reaches each sub-part directly. + for (uint32_t i = 0; i < N; ++i) { + auto idx = msgs | get_pair{i}; + REQUIRE(idx.headerIdx == 2 * i); + REQUIRE(idx.payloadIdx == 2 * i + 1); + } + + // get_next_pair advances sequentially through all pairs. + DataRefIndices idx{0, 1}; + for (uint32_t i = 1; i < N; ++i) { + idx = msgs | get_next_pair{idx}; + REQUIRE(idx.headerIdx == 2 * i); + REQUIRE(idx.payloadIdx == 2 * i + 1); + } + // One more step goes out of range. + idx = msgs | get_next_pair{idx}; + REQUIRE(idx.headerIdx >= msgs.size()); +} + +// --------------------------------------------------------------------------- +// New-style multipart: one header followed by N contiguous payloads. +// splitPayloadParts == splitPayloadIndex == N (the sentinel for new style). +// Layout: [h, p0, p1, p2] +// --------------------------------------------------------------------------- +TEST_CASE("NewStyleMultiPayload") +{ + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + constexpr uint32_t N = 3; + + std::vector msgs; + msgs.emplace_back(makeHeader(*transport, N, N)); + for (uint32_t i = 0; i < N; ++i) { + msgs.emplace_back(makePayload(*transport)); + } + + REQUIRE((msgs | count_parts{}) == 1); + REQUIRE((msgs | count_payloads{}) == N); + REQUIRE((msgs | get_num_payloads{0}) == N); // all payloads belong to part 0 + + // get_pair returns the same header for every sub-part, advancing payloadIdx. + for (uint32_t i = 0; i < N; ++i) { + auto idx = msgs | get_pair{i}; + REQUIRE(idx.headerIdx == 0); + REQUIRE(idx.payloadIdx == 1 + i); + } + + // get_next_pair advances payloadIdx within the block, then moves to next block. + DataRefIndices idx{0, 1}; + for (uint32_t i = 1; i < N; ++i) { + idx = msgs | get_next_pair{idx}; + REQUIRE(idx.headerIdx == 0); + REQUIRE(idx.payloadIdx == 1 + i); + } + // One more step exits the block. + idx = msgs | get_next_pair{idx}; + REQUIRE(idx.headerIdx >= msgs.size()); +} + +// --------------------------------------------------------------------------- +// Mixed message set: two routes, one single-pair and one new-style block. +// Layout: [h0, p0, h1, p1_0, p1_1] +// --------------------------------------------------------------------------- +TEST_CASE("MixedLayout") +{ + auto transport = fair::mq::TransportFactory::CreateTransportFactory("zeromq"); + + std::vector msgs; + // Route 0: single pair + msgs.emplace_back(makeHeader(*transport, 0, 0)); + msgs.emplace_back(makePayload(*transport)); + // Route 1: new-style 2-payload block + msgs.emplace_back(makeHeader(*transport, 2, 2)); + msgs.emplace_back(makePayload(*transport)); + msgs.emplace_back(makePayload(*transport)); + + REQUIRE((msgs | count_parts{}) == 2); + REQUIRE((msgs | count_payloads{}) == 3); + + // get_pair across routes + auto idx0 = msgs | get_pair{0}; + REQUIRE(idx0.headerIdx == 0); + REQUIRE(idx0.payloadIdx == 1); + + auto idx1 = msgs | get_pair{1}; + REQUIRE(idx1.headerIdx == 2); + REQUIRE(idx1.payloadIdx == 3); + + auto idx2 = msgs | get_pair{2}; + REQUIRE(idx2.headerIdx == 2); + REQUIRE(idx2.payloadIdx == 4); + + // get_next_pair traversal from the first element + DataRefIndices idx{0, 1}; + idx = msgs | get_next_pair{idx}; + REQUIRE(idx.headerIdx == 2); + REQUIRE(idx.payloadIdx == 3); + idx = msgs | get_next_pair{idx}; + REQUIRE(idx.headerIdx == 2); + REQUIRE(idx.payloadIdx == 4); + idx = msgs | get_next_pair{idx}; + REQUIRE(idx.headerIdx >= msgs.size()); +} From 5cf005b9a2caf9e5c17251a32b0c7b490261e09a Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Tue, 7 Apr 2026 08:47:47 +0200 Subject: [PATCH 463/701] ITS3: add cylinder parameterisation preserving zero gaussian curvature (#15260) * ITS3: exploit symmetric matrices for interpolation Signed-off-by: Felix Schlepper * ITS3: cleanup code Signed-off-by: Felix Schlepper * ITS3: alignment for inextionsial surfaces Signed-off-by: Felix Schlepper * ITS3: harmonize study and alignment wfx for misalignment Signed-off-by: Felix Schlepper --------- Signed-off-by: Felix Schlepper --- .../Upgrades/ITS3/alignment/CMakeLists.txt | 3 + Detectors/Upgrades/ITS3/alignment/README.md | 36 +++- .../include/ITS3Align/AlignmentDOF.h | 176 +++++++++++++++++ .../include/ITS3Align/AlignmentHierarchy.h | 178 +---------------- .../include/ITS3Align/AlignmentLabel.h | 87 +++++++++ .../include/ITS3Align/AlignmentMath.h | 32 +++ .../include/ITS3Align/AlignmentParams.h | 7 +- .../include/ITS3Align/AlignmentSpec.h | 1 + .../include/ITS3Align/MisalignmentUtils.h | 79 ++++++++ .../alignment/include/ITS3Align/TrackFit.h | 8 +- .../ITS3/alignment/src/AlignmentDOF.cxx | 114 +++++++++++ .../ITS3/alignment/src/AlignmentHierarchy.cxx | 127 ++++++++++-- .../ITS3/alignment/src/AlignmentMath.cxx | 54 ++++++ .../ITS3/alignment/src/AlignmentSpec.cxx | 183 ++++++++---------- .../ITS3/alignment/src/MisalignmentUtils.cxx | 151 +++++++++++++++ .../Upgrades/ITS3/study/src/TrackingStudy.cxx | 90 +++------ 16 files changed, 967 insertions(+), 359 deletions(-) create mode 100644 Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentDOF.h create mode 100644 Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentLabel.h create mode 100644 Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentMath.h create mode 100644 Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentUtils.h create mode 100644 Detectors/Upgrades/ITS3/alignment/src/AlignmentDOF.cxx create mode 100644 Detectors/Upgrades/ITS3/alignment/src/AlignmentMath.cxx create mode 100644 Detectors/Upgrades/ITS3/alignment/src/MisalignmentUtils.cxx diff --git a/Detectors/Upgrades/ITS3/alignment/CMakeLists.txt b/Detectors/Upgrades/ITS3/alignment/CMakeLists.txt index 0bc8080c7a1b8..e04dfcbb43963 100644 --- a/Detectors/Upgrades/ITS3/alignment/CMakeLists.txt +++ b/Detectors/Upgrades/ITS3/alignment/CMakeLists.txt @@ -13,6 +13,9 @@ o2_add_library(ITS3Align TARGETVARNAME targetName SOURCES src/AlignmentHierarchy.cxx + src/AlignmentDOF.cxx + src/AlignmentMath.cxx + src/MisalignmentUtils.cxx src/AlignmentSensors.cxx src/AlignmentParams.cxx src/AlignmentTypes.cxx diff --git a/Detectors/Upgrades/ITS3/alignment/README.md b/Detectors/Upgrades/ITS3/alignment/README.md index 62633d1d7d313..80213eb4e03b1 100644 --- a/Detectors/Upgrades/ITS3/alignment/README.md +++ b/Detectors/Upgrades/ITS3/alignment/README.md @@ -27,4 +27,38 @@ dofSet.json: "calib": { "type": "legendre", "order": 1, "fix": [0, 2] } } ] -}``` +} +``` + + +## In-existensional modes +```json +{ + "defaults": { "rigidBody": "fixed" }, + "rules": [ + { + "match": "ITS3Layer1/ITS3CarbonForm0", + "calib": { + "type": "inextensional", + "order": 2, + "free": ["a_2", "b_2", "c_2", "d_2", "alpha", "beta"] + } + } + ] +} +``` + +```json +[ + { + "id": 2, + "inextensional": { + "modes": { + "2": [0.0008, -0.0005, 0.0006, -0.0007] + }, + "alpha": 0.0004, + "beta": -0.0003 + } + } +] +``` diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentDOF.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentDOF.h new file mode 100644 index 0000000000000..3fed9decbd6e7 --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentDOF.h @@ -0,0 +1,176 @@ +// 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. + +#ifndef O2_ITS3_ALIGNMENT_DOF_H +#define O2_ITS3_ALIGNMENT_DOF_H + +#include +#include +#include +#include +#include +#include + +#include + +struct DerivativeContext { + int sensorID{-1}; + int layerID{-1}; + double measX{0.}; + double measAlpha{0.}; + double measZ{0.}; + double trkY{0.}; + double trkZ{0.}; + double snp{0.}; + double tgl{0.}; + double dydx{0.}; + double dzdx{0.}; +}; + +// Generic set of DOF +class DOFSet +{ + public: + enum class Type : uint8_t { + RigidBody, + Legendre, + Inextensional + }; + virtual ~DOFSet() = default; + virtual Type type() const = 0; + int nDOFs() const { return static_cast(mFree.size()); } + virtual std::string dofName(int idx) const = 0; + virtual void fillDerivatives(const DerivativeContext& ctx, Eigen::Ref out) const = 0; + bool isFree(int idx) const { return mFree[idx]; } + void setFree(int idx, bool f) { mFree[idx] = f; } + void setAllFree(bool f) { std::fill(mFree.begin(), mFree.end(), f); } + int nFreeDOFs() const + { + int n = 0; + for (bool f : mFree) { + n += f; + } + return n; + } + + protected: + DOFSet(int n) : mFree(n, true) {} + std::vector mFree; +}; + +// Rigid body set +class RigidBodyDOFSet final : public DOFSet +{ + public: + // indices for rigid body parameters in LOC frame + enum RigidBodyDOF : uint8_t { + TX = 0, + TY, + TZ, + RX, + RY, + RZ, + NDOF, + }; + static constexpr const char* RigidBodyDOFNames[RigidBodyDOF::NDOF] = {"TX", "TY", "TZ", "RX", "RY", "RZ"}; + + RigidBodyDOFSet() : DOFSet(NDOF) {} + // mask: bitmask of free DOFs (bit i = DOF i is free) + explicit RigidBodyDOFSet(uint8_t mask) : DOFSet(NDOF) + { + for (int i = 0; i < NDOF; ++i) { + mFree[i] = (mask >> i) & 1; + } + } + Type type() const override { return Type::RigidBody; } + std::string dofName(int idx) const override { return RigidBodyDOFNames[idx]; } + void fillDerivatives(const DerivativeContext& ctx, Eigen::Ref out) const override; + uint8_t mask() const + { + uint8_t m = 0; + for (int i = 0; i < NDOF; ++i) { + m |= (uint8_t(mFree[i]) << i); + } + return m; + } +}; + +// Legendre DOFs +// Describing radial misplacement +class LegendreDOFSet final : public DOFSet +{ + public: + explicit LegendreDOFSet(int order) : DOFSet((order + 1) * (order + 2) / 2), mOrder(order) {} + Type type() const override { return Type::Legendre; } + int order() const { return mOrder; } + std::string dofName(int idx) const override + { + int i = 0; + while ((i + 1) * (i + 2) / 2 <= idx) { + ++i; + } + int j = idx - (i * (i + 1) / 2); + return std::format("L({},{})", i, j); + } + void fillDerivatives(const DerivativeContext& ctx, Eigen::Ref out) const override; + + private: + int mOrder; +}; + +// In-extensional deformation DOFs for cylindrical half-shells +// Fourier modes n=2..N: 4 params each (a_n, b_n, c_n, d_n) +// Plus 2 non-periodic modes (alpha, beta) for the half-cylinder open edges +// Total: 4*(N-1) + 2 +class InextensionalDOFSet final : public DOFSet +{ + public: + explicit InextensionalDOFSet(int maxOrder) : DOFSet((4 * (maxOrder - 1)) + 2), mMaxOrder(maxOrder) + { + if (maxOrder < 2) { + // the rest is eq. to rigid body + throw std::invalid_argument("InextensionalDOFSet requires maxOrder >= 2"); + } + } + Type type() const override { return Type::Inextensional; } + int maxOrder() const { return mMaxOrder; } + + // number of periodic DOFs (before alpha, beta) + int nPeriodic() const { return 4 * (mMaxOrder - 1); } + + // flat index layout: [a_2, b_2, c_2, d_2, a_3, b_3, c_3, d_3, ..., alpha, beta] + // index of first DOF for mode n + static int modeOffset(int n) { return 4 * (n - 2); } + + // indices of the non-periodic modes + int alphaIdx() const { return nPeriodic(); } + int betaIdx() const { return nPeriodic() + 1; } + + std::string dofName(int idx) const override + { + if (idx == alphaIdx()) { + return "alpha"; + } + if (idx == betaIdx()) { + return "beta"; + } + int n = (idx / 4) + 2; + int sub = idx % 4; + static constexpr const char* subNames[] = {"a", "b", "c", "d"}; + return std::format("{}_{}", subNames[sub], n); + } + void fillDerivatives(const DerivativeContext& ctx, Eigen::Ref out) const override; + + private: + int mMaxOrder; +}; + +#endif diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentHierarchy.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentHierarchy.h index 04b8157084d0a..ae8989deec21b 100644 --- a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentHierarchy.h +++ b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentHierarchy.h @@ -13,14 +13,11 @@ #define O2_ITS3_ALIGNMENT_HIERARCHY_H #include -#include -#include -#include #include #include #include #include -#include +#include #include #include @@ -28,179 +25,14 @@ #include #include +#include "ITS3Align/AlignmentLabel.h" +#include "ITS3Align/AlignmentDOF.h" + namespace o2::its3::align { using Matrix36 = Eigen::Matrix; using Matrix66 = Eigen::Matrix; -// indices for rigid body parameters in LOC frame -enum RigidBodyDOF : uint8_t { - TX = 0, - TY, - TZ, - RX, - RY, - RZ, - NDOF, -}; -static constexpr const char* RigidBodyDOFNames[RigidBodyDOF::NDOF] = {"TX", "TY", "TZ", "RX", "RY", "RZ"}; - -// return the rigid body derivatives -// trk has be at in the measurment frame -auto getRigidBodyDerivatives(const auto& trk) -{ - // calculate slopes - const double tgl = trk.getTgl(), snp = trk.getSnp(); - const double csp = 1. / sqrt(1. + (tgl * tgl)); - const double u = trk.getY(), v = trk.getZ(); - const double uP = snp * csp, vP = tgl * csp; - Matrix36 der; - der.setZero(); - // columns: Tt, Tu, Tv, Rt, Ru, Rv - // (X) (Y) (Z) (RX) (RY) (RZ) - der << uP, -1., 0., v, v * uP, -u * uP, - vP, 0., -1., -u, v * vP, -u * vP; - return der; -} - -class DOFSet -{ - public: - enum class Type : uint8_t { RigidBody, - Legendre }; - virtual ~DOFSet() = default; - virtual Type type() const = 0; - int nDOFs() const { return static_cast(mFree.size()); } - virtual std::string dofName(int idx) const = 0; - bool isFree(int idx) const { return mFree[idx]; } - void setFree(int idx, bool f) { mFree[idx] = f; } - void setAllFree(bool f) { std::fill(mFree.begin(), mFree.end(), f); } - int nFreeDOFs() const - { - int n = 0; - for (bool f : mFree) { - n += f; - } - return n; - } - - protected: - DOFSet(int n) : mFree(n, true) {} - std::vector mFree; -}; - -class RigidBodyDOFSet final : public DOFSet -{ - public: - static constexpr int NDOF = RigidBodyDOF::NDOF; - RigidBodyDOFSet() : DOFSet(NDOF) {} - // mask: bitmask of free DOFs (bit i = DOF i is free) - explicit RigidBodyDOFSet(uint8_t mask) : DOFSet(NDOF) - { - for (int i = 0; i < NDOF; ++i) { - mFree[i] = (mask >> i) & 1; - } - } - Type type() const override { return Type::RigidBody; } - std::string dofName(int idx) const override { return RigidBodyDOFNames[idx]; } - uint8_t mask() const - { - uint8_t m = 0; - for (int i = 0; i < NDOF; ++i) { - m |= (uint8_t(mFree[i]) << i); - } - return m; - } -}; - -class LegendreDOFSet final : public DOFSet -{ - public: - explicit LegendreDOFSet(int order) : DOFSet((order + 1) * (order + 2) / 2), mOrder(order) {} - Type type() const override { return Type::Legendre; } - int order() const { return mOrder; } - std::string dofName(int idx) const override - { - int i = 0; - while ((i + 1) * (i + 2) / 2 <= idx) { - ++i; - } - int j = idx - (i * (i + 1) / 2); - return std::format("L({},{})", i, j); - } - - private: - int mOrder; -}; - -class GlobalLabel -{ - // Millepede label is any positive integer [1....) - // Layout: DOF(5) | CALIB(1) | ID(22) | SENS(1) | DET(2) = 31 usable bits (MSB reserved, GBL uses signed int) - public: - using T = uint32_t; - static constexpr int DOF_BITS = 5; // bits 0-4 - static constexpr int CALIB_BITS = 1; // bit 5: 0 = rigid body, 1 = calibration - static constexpr int ID_BITS = 22; // bits 6-27 - static constexpr int SENS_BITS = 1; // bit 28 - static constexpr int TOTAL_BITS = sizeof(T) * 8; - static constexpr int DET_BITS = TOTAL_BITS - (DOF_BITS + CALIB_BITS + ID_BITS + SENS_BITS) - 1; // one less bit since GBL uses int! - static constexpr T bitMask(int b) noexcept - { - return (T(1) << b) - T(1); - } - static constexpr int DOF_SHIFT = 0; - static constexpr T DOF_MAX = (T(1) << DOF_BITS) - T(1); - static constexpr T DOF_MASK = DOF_MAX << DOF_SHIFT; - static constexpr int CALIB_SHIFT = DOF_BITS; - static constexpr T CALIB_MAX = (T(1) << CALIB_BITS) - T(1); - static constexpr T CALIB_MASK = CALIB_MAX << CALIB_SHIFT; - static constexpr int ID_SHIFT = DOF_BITS + CALIB_BITS; - static constexpr T ID_MAX = (T(1) << ID_BITS) - T(1); - static constexpr T ID_MASK = ID_MAX << ID_SHIFT; - static constexpr int SENS_SHIFT = DOF_BITS + CALIB_BITS + ID_BITS; - static constexpr T SENS_MAX = (T(1) << SENS_BITS) - T(1); - static constexpr T SENS_MASK = SENS_MAX << SENS_SHIFT; - static constexpr int DET_SHIFT = DOF_BITS + CALIB_BITS + ID_BITS + SENS_BITS; - static constexpr T DET_MAX = (T(1) << DET_BITS) - T(1); - static constexpr T DET_MASK = DET_MAX << DET_SHIFT; - - GlobalLabel(T det, T id, bool sens, bool calib = false) - : mID((((id + 1) & ID_MAX) << ID_SHIFT) | - ((det & DET_MAX) << DET_SHIFT) | - ((T(sens) & SENS_MAX) << SENS_SHIFT) | - ((T(calib) & CALIB_MAX) << CALIB_SHIFT)) - { - } - - /// produce the raw Millepede label for a given DOF index (rigid body: calib=0 in label) - constexpr T raw(T dof) const noexcept { return (mID & ~DOF_MASK) | ((dof & DOF_MAX) << DOF_SHIFT); } - constexpr int rawGBL(T dof) const noexcept { return static_cast(raw(dof)); } - - /// return a copy of this label with the CALIB bit set (for calibration DOFs on same volume) - GlobalLabel asCalib() const noexcept - { - GlobalLabel c{*this}; - c.mID |= (T(1) << CALIB_SHIFT); - return c; - } - - constexpr T id() const noexcept { return ((mID >> ID_SHIFT) & ID_MAX) - 1; } - constexpr T det() const noexcept { return (mID & DET_MASK) >> DET_SHIFT; } - constexpr bool sens() const noexcept { return (mID & SENS_MASK) >> SENS_SHIFT; } - constexpr bool calib() const noexcept { return (mID & CALIB_MASK) >> CALIB_SHIFT; } - - std::string asString() const - { - return std::format("Det:{} Id:{} Sens:{} Calib:{}", det(), id(), sens(), calib()); - } - - constexpr auto operator<=>(const GlobalLabel&) const noexcept = default; - - private: - T mID{0}; -}; - class HierarchyConstraint { public: @@ -220,8 +52,6 @@ class HierarchyConstraint std::vector mCoeff; // their coefficients }; -// --- AlignableVolume --- - class AlignableVolume { public: diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentLabel.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentLabel.h new file mode 100644 index 0000000000000..83495491b87e0 --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentLabel.h @@ -0,0 +1,87 @@ +// 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. + +#ifndef O2_ITS3_ALIGNMENT_LABEL_H +#define O2_ITS3_ALIGNMENT_LABEL_H + +#include +#include +#include + +class GlobalLabel +{ + // Millepede label is any positive integer [1....) + // Layout: DOF(5) | CALIB(1) | ID(22) | SENS(1) | DET(2) = 31 usable bits (MSB reserved, GBL uses signed int) + public: + using T = uint32_t; + static constexpr int DOF_BITS = 5; // bits 0-4 + static constexpr int CALIB_BITS = 1; // bit 5: 0 = rigid body, 1 = calibration (only allow for one calibration, could be extended if needed) + static constexpr int ID_BITS = 22; // bits 6-27 + static constexpr int SENS_BITS = 1; // bit 28 + static constexpr int TOTAL_BITS = sizeof(T) * 8; + static constexpr int DET_BITS = TOTAL_BITS - (DOF_BITS + CALIB_BITS + ID_BITS + SENS_BITS) - 1; // one less bit since GBL uses int! + static constexpr T bitMask(int b) noexcept + { + return (T(1) << b) - T(1); + } + static constexpr int DOF_SHIFT = 0; + static constexpr T DOF_MAX = (T(1) << DOF_BITS) - T(1); + static constexpr T DOF_MASK = DOF_MAX << DOF_SHIFT; + static constexpr int CALIB_SHIFT = DOF_BITS; + static constexpr T CALIB_MAX = (T(1) << CALIB_BITS) - T(1); + static constexpr T CALIB_MASK = CALIB_MAX << CALIB_SHIFT; + static constexpr int ID_SHIFT = DOF_BITS + CALIB_BITS; + static constexpr T ID_MAX = (T(1) << ID_BITS) - T(1); + static constexpr T ID_MASK = ID_MAX << ID_SHIFT; + static constexpr int SENS_SHIFT = DOF_BITS + CALIB_BITS + ID_BITS; + static constexpr T SENS_MAX = (T(1) << SENS_BITS) - T(1); + static constexpr T SENS_MASK = SENS_MAX << SENS_SHIFT; + static constexpr int DET_SHIFT = DOF_BITS + CALIB_BITS + ID_BITS + SENS_BITS; + static constexpr T DET_MAX = (T(1) << DET_BITS) - T(1); + static constexpr T DET_MASK = DET_MAX << DET_SHIFT; + + GlobalLabel(T det, T id, bool sens, bool calib = false) + : mID((((id + 1) & ID_MAX) << ID_SHIFT) | + ((det & DET_MAX) << DET_SHIFT) | + ((T(sens) & SENS_MAX) << SENS_SHIFT) | + ((T(calib) & CALIB_MAX) << CALIB_SHIFT)) + { + } + + /// produce the raw Millepede label for a given DOF index (rigid body: calib=0 in label) + constexpr T raw(T dof) const noexcept { return (mID & ~DOF_MASK) | ((dof & DOF_MAX) << DOF_SHIFT); } + constexpr int rawGBL(T dof) const noexcept { return static_cast(raw(dof)); } + + /// return a copy of this label with the CALIB bit set (for calibration DOFs on same volume) + GlobalLabel asCalib() const noexcept + { + GlobalLabel c{*this}; + c.mID |= (T(1) << CALIB_SHIFT); + return c; + } + + constexpr T id() const noexcept { return ((mID >> ID_SHIFT) & ID_MAX) - 1; } + constexpr T det() const noexcept { return (mID & DET_MASK) >> DET_SHIFT; } + constexpr bool sens() const noexcept { return (mID & SENS_MASK) >> SENS_SHIFT; } + constexpr bool calib() const noexcept { return (mID & CALIB_MASK) >> CALIB_SHIFT; } + + std::string asString() const + { + return std::format("Det:{} Id:{} Sens:{} Calib:{}", det(), id(), sens(), calib()); + } + + constexpr auto operator<=>(const GlobalLabel&) const noexcept = default; + + private: + T mID{0}; +}; + +#endif diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentMath.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentMath.h new file mode 100644 index 0000000000000..9409648dca3e4 --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentMath.h @@ -0,0 +1,32 @@ +// 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. + +#ifndef O2_ITS3_ALIGNMENT_MATH_H +#define O2_ITS3_ALIGNMENT_MATH_H + +#include +#include + +namespace o2::its3::align +{ + +struct TrackSlopes { + double dydx{0.}; + double dzdx{0.}; +}; + +std::pair computeUV(double gloX, double gloY, double gloZ, int sensorID, double radius); +TrackSlopes computeTrackSlopes(double snp, double tgl); +std::vector legendrePols(int order, double x); + +} // namespace o2::its3::align + +#endif diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentParams.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentParams.h index a7785a2c04e11..5a11066fd3c3b 100644 --- a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentParams.h +++ b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/AlignmentParams.h @@ -38,9 +38,10 @@ struct AlignmentParams : public o2::conf::ConfigurableParamHelper; diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentUtils.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentUtils.h new file mode 100644 index 0000000000000..457eccaeff4e6 --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/MisalignmentUtils.h @@ -0,0 +1,79 @@ +// 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. + +#ifndef O2_ITS3_ALIGNMENT_MISALIGNMENTUTILS_H +#define O2_ITS3_ALIGNMENT_MISALIGNMENTUTILS_H + +#include +#include +#include +#include +#include + +#include "ITS3Align/AlignmentMath.h" +#include "MathUtils/LegendrePols.h" + +namespace o2::its3::align +{ + +struct InextensionalMisalignment { + std::map> modes; // n -> (a_n, b_n, c_n, d_n) + double alpha{0.}; + double beta{0.}; +}; + +struct SensorMisalignment { + o2::math_utils::Legendre2DPolynominal legendre; + bool hasLegendre{false}; + InextensionalMisalignment inextensional; + bool hasInextensional{false}; + + bool empty() const noexcept { return !hasLegendre && !hasInextensional; } +}; + +struct MisalignmentModel { + static constexpr std::size_t NSensors = 6; + std::array sensors{}; + + bool empty() const noexcept; + const SensorMisalignment& operator[](std::size_t idx) const { return sensors[idx]; } + SensorMisalignment& operator[](std::size_t idx) { return sensors[idx]; } +}; + +struct MisalignmentFrame { + int sensorID{-1}; + int layerID{-1}; + double x{0.}; // tracking-frame X / nominal radius at the measurement + double alpha{0.}; // tracking-frame alpha + double z{0.}; // tracking-frame measurement z +}; + +struct MisalignmentShift { + double dy{0.}; + double dz{0.}; + bool accepted{true}; + + MisalignmentShift& operator+=(const MisalignmentShift& other) + { + dy += other.dy; + dz += other.dz; + accepted = accepted && other.accepted; + return *this; + } +}; + +MisalignmentModel loadMisalignmentModel(const std::string& jsonPath); +MisalignmentShift evaluateLegendreShift(const SensorMisalignment& sensor, const MisalignmentFrame& frame, const TrackSlopes& slopes); +MisalignmentShift evaluateInextensionalShift(const SensorMisalignment& sensor, const MisalignmentFrame& frame, const TrackSlopes& slopes); + +} // namespace o2::its3::align + +#endif diff --git a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/TrackFit.h b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/TrackFit.h index 3f36705271c9b..4625776398c89 100644 --- a/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/TrackFit.h +++ b/Detectors/Upgrades/ITS3/alignment/include/ITS3Align/TrackFit.h @@ -76,10 +76,12 @@ o2::track::TrackParametrizationWithError interpolateTrackParCov( }; Mat55 cA = unpack(tA.getCov()); Mat55 cB = unpack(tB.getCov()); - Mat55 wA = cA.inverse(); - Mat55 wB = cB.inverse(); + Eigen::LLT lltA(cA), lltB(cB); + Mat55 wA = lltA.solve(Mat55::Identity()); + Mat55 wB = lltB.solve(Mat55::Identity()); Mat55 wTot = wA + wB; - Mat55 cTot = wTot.inverse(); + Eigen::LLT lltTot(wTot); + Mat55 cTot = lltTot.solve(Mat55::Identity()); Mat51 pA, pB; for (int i = 0; i < 5; ++i) { pA(i) = tA.getParam(i); diff --git a/Detectors/Upgrades/ITS3/alignment/src/AlignmentDOF.cxx b/Detectors/Upgrades/ITS3/alignment/src/AlignmentDOF.cxx new file mode 100644 index 0000000000000..d2a78dba791e6 --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/src/AlignmentDOF.cxx @@ -0,0 +1,114 @@ +// 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 "ITS3Align/AlignmentDOF.h" + +#include +#include + +#include "ITS3Align/AlignmentMath.h" +#include "ITS3Base/SpecsV2.h" + +namespace +{ + +void validateDerivativeOutput(const DOFSet& dofSet, Eigen::Ref out) +{ + if (out.rows() != 3 || out.cols() != dofSet.nDOFs()) { + throw std::invalid_argument(std::format("Derivative buffer shape {}x{} does not match expected 3x{}", + out.rows(), out.cols(), dofSet.nDOFs())); + } + out.setZero(); +} + +} // namespace + +void RigidBodyDOFSet::fillDerivatives(const DerivativeContext& ctx, Eigen::Ref out) const +{ + validateDerivativeOutput(*this, out); + + const double csp = 1. / std::sqrt(1. + (ctx.tgl * ctx.tgl)); + const double uP = ctx.snp * csp; + const double vP = ctx.tgl * csp; + + out(0, TX) = uP; + out(0, TY) = -1.; + out(0, RX) = ctx.trkZ; + out(0, RY) = ctx.trkZ * uP; + out(0, RZ) = -ctx.trkY * uP; + + out(1, TX) = vP; + out(1, TZ) = -1.; + out(1, RX) = -ctx.trkY; + out(1, RY) = ctx.trkZ * vP; + out(1, RZ) = -ctx.trkY * vP; +} + +void LegendreDOFSet::fillDerivatives(const DerivativeContext& ctx, Eigen::Ref out) const +{ + validateDerivativeOutput(*this, out); + if (ctx.sensorID < 0 || ctx.layerID < 0) { + throw std::invalid_argument("LegendreDOFSet requires an ITS3 measurement context"); + } + + const double gloX = ctx.measX * std::cos(ctx.measAlpha); + const double gloY = ctx.measX * std::sin(ctx.measAlpha); + const auto [u, v] = o2::its3::align::computeUV(gloX, gloY, ctx.measZ, ctx.sensorID, o2::its3::constants::radii[ctx.layerID]); + const auto pu = o2::its3::align::legendrePols(mOrder, u); + const auto pv = o2::its3::align::legendrePols(mOrder, v); + + int idx = 0; + for (int i = 0; i <= mOrder; ++i) { + for (int j = 0; j <= i; ++j) { + const double basis = pu[j] * pv[i - j]; + out(0, idx) = ctx.dydx * basis; + out(1, idx) = ctx.dzdx * basis; + ++idx; + } + } +} + +void InextensionalDOFSet::fillDerivatives(const DerivativeContext& ctx, Eigen::Ref out) const +{ + validateDerivativeOutput(*this, out); + if (ctx.layerID < 0) { + throw std::invalid_argument("InextensionalDOFSet requires an ITS3 measurement context"); + } + + const double r = o2::its3::constants::radii[ctx.layerID]; + const double phi = std::atan2(r * std::sin(ctx.measAlpha), r * std::cos(ctx.measAlpha)); + const double z = ctx.measZ; + + for (int n = 2; n <= mMaxOrder; ++n) { + const double sn = std::sin(n * phi); + const double cn = std::cos(n * phi); + const double n2 = static_cast(n * n); + const int off = modeOffset(n); + + out(0, off + 0) = -(z / r) * (n * sn + ctx.dydx * n2 * cn); + out(1, off + 0) = -cn - ctx.dzdx * (z / r) * n2 * cn; + + out(0, off + 1) = (z / r) * (n * cn - ctx.dydx * n2 * sn); + out(1, off + 1) = -sn * (1. + ctx.dzdx * (z / r) * n2); + + out(0, off + 2) = -cn + ctx.dydx * n * sn; + out(1, off + 2) = ctx.dzdx * n * sn; + + out(0, off + 3) = -sn - ctx.dydx * n * cn; + out(1, off + 3) = -ctx.dzdx * n * cn; + } + + out(0, alphaIdx()) = z / r; + out(1, alphaIdx()) = -phi; + + out(0, betaIdx()) = -phi - ctx.dydx; + out(1, betaIdx()) = -ctx.dzdx; +} diff --git a/Detectors/Upgrades/ITS3/alignment/src/AlignmentHierarchy.cxx b/Detectors/Upgrades/ITS3/alignment/src/AlignmentHierarchy.cxx index 9170165a36a41..938c14c2c4759 100644 --- a/Detectors/Upgrades/ITS3/alignment/src/AlignmentHierarchy.cxx +++ b/Detectors/Upgrades/ITS3/alignment/src/AlignmentHierarchy.cxx @@ -178,21 +178,23 @@ void AlignableVolume::writeParameters(std::ostream& os) const if (isRoot()) { os << "Parameter\n"; } - if (mRigidBody) { - for (int iDOF = 0; iDOF < mRigidBody->nDOFs(); ++iDOF) { - os << std::format("{:<10} {:>+15g} {:>+15g} ! {} {} ", - mLabel.raw(iDOF), 0.0, (mRigidBody->isFree(iDOF) ? 0.0 : -1.0), - (mRigidBody->isFree(iDOF) ? 'V' : 'F'), mRigidBody->dofName(iDOF)) - << mSymName << '\n'; + if (!mIsPseudo) { + if (mRigidBody) { + for (int iDOF = 0; iDOF < mRigidBody->nDOFs(); ++iDOF) { + os << std::format("{:<10} {:>+15g} {:>+15g} ! {} {} ", + mLabel.raw(iDOF), 0.0, (mRigidBody->isFree(iDOF) ? 0.0 : -1.0), + (mRigidBody->isFree(iDOF) ? 'V' : 'F'), mRigidBody->dofName(iDOF)) + << mSymName << '\n'; + } } - } - if (mCalib) { - auto calibLbl = mLabel.asCalib(); - for (int iDOF = 0; iDOF < mCalib->nDOFs(); ++iDOF) { - os << std::format("{:<10} {:>+15g} {:>+15g} ! {} {} ", - calibLbl.raw(iDOF), 0.0, (mCalib->isFree(iDOF) ? 0.0 : -1.0), - (mCalib->isFree(iDOF) ? 'V' : 'F'), mCalib->dofName(iDOF)) - << mSymName << '\n'; + if (mCalib) { + auto calibLbl = mLabel.asCalib(); + for (int iDOF = 0; iDOF < mCalib->nDOFs(); ++iDOF) { + os << std::format("{:<10} {:>+15g} {:>+15g} ! {} {:<5} ", + calibLbl.raw(iDOF), 0.0, (mCalib->isFree(iDOF) ? 0.0 : -1.0), + (mCalib->isFree(iDOF) ? 'V' : 'F'), mCalib->dofName(iDOF)) + << mSymName << '\n'; + } } } for (const auto& c : mChildren) { @@ -266,6 +268,9 @@ void applyDOFConfig(AlignableVolume* root, const std::string& jsonPath) } root->traverse([&](AlignableVolume* vol) { + if (vol->isPseudo()) { + return; + } const std::string& sym = vol->getSymName(); for (const auto& rule : rules) { const auto pattern = rule["match"].get(); @@ -357,6 +362,41 @@ void applyDOFConfig(AlignableVolume* root, const std::string& jsonPath) } } vol->setCalib(std::move(dofSet)); + } else if (calType == "inextensional") { + int maxOrder = cal.value("order", 2); + auto dofSet = std::make_unique(maxOrder); + bool fixed = cal.value("fixed", false); + if (fixed) { + dofSet->setAllFree(false); + } + if (cal.contains("free")) { + dofSet->setAllFree(false); + for (const auto& item : cal["free"]) { + if (item.is_number_integer()) { + dofSet->setFree(item.get(), true); + } else if (item.is_string()) { + for (int k = 0; k < dofSet->nDOFs(); ++k) { + if (dofSet->dofName(k) == item.get()) { + dofSet->setFree(k, true); + } + } + } + } + } + if (cal.contains("fix")) { + for (const auto& item : cal["fix"]) { + if (item.is_number_integer()) { + dofSet->setFree(item.get(), false); + } else if (item.is_string()) { + for (int k = 0; k < dofSet->nDOFs(); ++k) { + if (dofSet->dofName(k) == item.get()) { + dofSet->setFree(k, false); + } + } + } + } + } + vol->setCalib(std::move(dofSet)); } } } @@ -398,6 +438,12 @@ void writeMillepedeResults(AlignableVolume* root, const std::string& milleResPat // indexed by sensorID std::map> injRB; std::map>> injMatrix; + struct InjInex { + std::map> modes; + double alpha{0.}; + double beta{0.}; + }; + std::map injInex; if (!injectedJsonPath.empty()) { std::ifstream injFile(injectedJsonPath); if (injFile.is_open()) { @@ -410,6 +456,22 @@ void writeMillepedeResults(AlignableVolume* root, const std::string& milleResPat if (item.contains("matrix")) { injMatrix[id] = item["matrix"].get>>(); } + if (item.contains("inextensional")) { + InjInex ii; + const auto& inex = item["inextensional"]; + if (inex.contains("modes")) { + for (auto& [key, val] : inex["modes"].items()) { + ii.modes[std::stoi(key)] = val.get>(); + } + } + if (inex.contains("alpha")) { + ii.alpha = inex["alpha"].get(); + } + if (inex.contains("beta")) { + ii.beta = inex["beta"].get(); + } + injInex[id] = ii; + } } LOGP(info, "Loaded injected misalignment for {} sensors", injData.size()); } else { @@ -468,6 +530,43 @@ void writeMillepedeResults(AlignableVolume* root, const std::string& milleResPat matrix.push_back(row); } entry["matrix"] = matrix; + } else if (cal && cal->nFreeDOFs() && cal->type() == DOFSet::Type::Inextensional) { + write = true; + auto* inexSet = static_cast(cal); + int maxN = inexSet->maxOrder(); + auto calibLbl = vol->getLabel().asCalib(); + const auto& inj = injInex.contains(id) ? injInex[id] : InjInex{}; + + json inexEntry; + json modesObj = json::object(); + for (int n = 2; n <= maxN; ++n) { + int off = InextensionalDOFSet::modeOffset(n); + std::array injCoeffs = {0., 0., 0., 0.}; + if (inj.modes.contains(n)) { + injCoeffs = inj.modes.at(n); + } + json modeArr = json::array(); + for (int k = 0; k < 4; ++k) { + uint32_t raw = calibLbl.raw(off + k); + auto it = labelToValue.find(raw); + double fitted = it != labelToValue.end() ? it->second : 0.0; + modeArr.push_back(fitted - injCoeffs[k]); + } + modesObj[std::to_string(n)] = modeArr; + } + inexEntry["modes"] = modesObj; + + // alpha + uint32_t rawAlpha = calibLbl.raw(inexSet->alphaIdx()); + auto itA = labelToValue.find(rawAlpha); + inexEntry["alpha"] = (itA != labelToValue.end() ? itA->second : 0.0) - inj.alpha; + + // beta + uint32_t rawBeta = calibLbl.raw(inexSet->betaIdx()); + auto itB = labelToValue.find(rawBeta); + inexEntry["beta"] = (itB != labelToValue.end() ? itB->second : 0.0) - inj.beta; + + entry["inextensional"] = inexEntry; } if (write) { output.push_back(entry); diff --git a/Detectors/Upgrades/ITS3/alignment/src/AlignmentMath.cxx b/Detectors/Upgrades/ITS3/alignment/src/AlignmentMath.cxx new file mode 100644 index 0000000000000..52e9c03540d4c --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/src/AlignmentMath.cxx @@ -0,0 +1,54 @@ +// 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 "ITS3Align/AlignmentMath.h" + +#include + +#include + +#include "ITS3Base/SpecsV2.h" +#include "MathUtils/Utils.h" + +namespace o2::its3::align +{ + +std::pair computeUV(double gloX, double gloY, double gloZ, int sensorID, double radius) +{ + const bool isTop = sensorID % 2 == 0; + const double phi = o2::math_utils::to02Pid(std::atan2(gloY, gloX)); + const double phiBorder1 = o2::math_utils::to02Pid(((isTop ? 0. : 1.) * TMath::Pi()) + std::asin(constants::equatorialGap / 2. / radius)); + const double phiBorder2 = o2::math_utils::to02Pid(((isTop ? 1. : 2.) * TMath::Pi()) - std::asin(constants::equatorialGap / 2. / radius)); + const double u = (((phi - phiBorder1) * 2.) / (phiBorder2 - phiBorder1)) - 1.; + const double v = ((2. * gloZ + constants::segment::lengthSensitive) / constants::segment::lengthSensitive) - 1.; + return {u, v}; +} + +TrackSlopes computeTrackSlopes(double snp, double tgl) +{ + const double csci = 1. / std::sqrt(1. - (snp * snp)); + return {.dydx = snp * csci, .dzdx = tgl * csci}; +} + +std::vector legendrePols(int order, double x) +{ + std::vector p(order + 1); + p[0] = 1.; + if (order > 0) { + p[1] = x; + } + for (int n = 1; n < order; ++n) { + p[n + 1] = ((2 * n + 1) * x * p[n] - n * p[n - 1]) / (n + 1); + } + return p; +} + +} // namespace o2::its3::align diff --git a/Detectors/Upgrades/ITS3/alignment/src/AlignmentSpec.cxx b/Detectors/Upgrades/ITS3/alignment/src/AlignmentSpec.cxx index d381abc6aa567..72f968bdbf338 100644 --- a/Detectors/Upgrades/ITS3/alignment/src/AlignmentSpec.cxx +++ b/Detectors/Upgrades/ITS3/alignment/src/AlignmentSpec.cxx @@ -10,8 +10,9 @@ // or submit itself to any jurisdiction. #include -#include #include +#include +#include #ifdef WITH_OPENMP #include @@ -43,12 +44,13 @@ #include "ITStracking/IOUtils.h" #include "ITS3Reconstruction/IOUtils.h" #include "ITS3Align/TrackFit.h" +#include "ITS3Align/AlignmentMath.h" #include "ITS3Align/AlignmentSpec.h" #include "ITS3Align/AlignmentParams.h" #include "ITS3Align/AlignmentTypes.h" #include "ITS3Align/AlignmentHierarchy.h" +#include "ITS3Align/MisalignmentUtils.h" #include "ITS3Align/AlignmentSensors.h" -#include "MathUtils/LegendrePols.h" namespace o2::its3::align { @@ -63,30 +65,29 @@ using TrackD = o2::track::TrackParCovD; namespace { -// compute normalized (u,v) in [-1,1] from global position on a sensor -std::pair computeUV(double gloX, double gloY, double gloZ, int sensorID, double radius) +DerivativeContext makeDerivativeContext(const FrameInfoExt& frame, const TrackD& trk) { - const bool isTop = sensorID % 2 == 0; - const double phi = o2::math_utils::to02Pid(std::atan2(gloY, gloX)); - const double phiBorder1 = o2::math_utils::to02Pid(((isTop ? 0. : 1.) * TMath::Pi()) + std::asin(constants::equatorialGap / 2. / radius)); - const double phiBorder2 = o2::math_utils::to02Pid(((isTop ? 1. : 2.) * TMath::Pi()) - std::asin(constants::equatorialGap / 2. / radius)); - const double u = (((phi - phiBorder1) * 2.) / (phiBorder2 - phiBorder1)) - 1.; - const double v = ((2. * gloZ + constants::segment::lengthSensitive) / constants::segment::lengthSensitive) - 1.; - return {u, v}; + const auto slopes = computeTrackSlopes(trk.getSnp(), trk.getTgl()); + const bool isITS3 = constants::detID::isDetITS3(frame.sens); + return {.sensorID = isITS3 ? constants::detID::getSensorID(frame.sens) : -1, + .layerID = isITS3 ? constants::detID::getDetID2Layer(frame.sens) : -1, + .measX = frame.x, + .measAlpha = frame.alpha, + .measZ = frame.positionTrackingFrame[1], + .trkY = trk.getY(), + .trkZ = trk.getZ(), + .snp = trk.getSnp(), + .tgl = trk.getTgl(), + .dydx = slopes.dydx, + .dzdx = slopes.dzdx}; } -// evaluate Legendre polynomials P_0(x) through P_order(x) via recurrence -std::vector legendrePols(int order, double x) +Matrix36 getRigidBodyBaseDerivatives(const DerivativeContext& ctx) { - std::vector p(order + 1); - p[0] = 1.; - if (order > 0) { - p[1] = x; - } - for (int n = 1; n < order; ++n) { - p[n + 1] = ((2 * n + 1) * x * p[n] - n * p[n - 1]) / (n + 1); - } - return p; + static const RigidBodyDOFSet sRigidBodyBasis; + Eigen::MatrixXd dyn(3, sRigidBodyBasis.nDOFs()); + sRigidBodyBasis.fillDerivatives(ctx, dyn); + return dyn; } } // namespace @@ -152,8 +153,8 @@ class AlignmentSpec final : public Task GTrackID::mask_t mTracksSrc; int mNThreads{1}; const AlignmentParams* mParams{nullptr}; - std::array mDeformations; // one per sensorID (0-5) - std::array, 6> mRigidBodyParams; // (dx,dy,dz,rx,ry,rz) in LOC per sensorID + MisalignmentModel mMisalignment; + std::array, 6> mRigidBodyParams; // (dx,dy,dz,rx,ry,rz) in LOC per sensorID }; void AlignmentSpec::init(InitContext& ic) @@ -323,7 +324,8 @@ void AlignmentSpec::process() // this is the derivative in TRK but we want to align in LOC // so dr/da_(LOC) = dr/da_(TRK) * da_(TRK)/da_(LOC) const auto* tileVol = mChip2Hiearchy.at(lbl); - Matrix36 der = getRigidBodyDerivatives(wTrk); + const auto derCtx = makeDerivativeContext(frame, wTrk); + Matrix36 der = getRigidBodyBaseDerivatives(derCtx); // count rigid body columns: only volumes with real DOFs (not DOFPseudo) int nColRB{0}; @@ -375,39 +377,16 @@ void AlignmentSpec::process() } } - // 3) calibration derivatives (e.g. Legendre for ITS3 sensors, apply directly on the whole sensor, not on inidividual tiles) - if (calibSet && calibSet->type() == DOFSet::Type::Legendre) { - const auto* legSet = static_cast(calibSet); - const int N = legSet->order(); - const int sensorID = constants::detID::getSensorID(frame.sens); - const int layerID = constants::detID::getDetID2Layer(frame.sens); - - const double r = frame.x; - const double gX = r * std::cos(frame.alpha); - const double gY = r * std::sin(frame.alpha); - const double gZ = frame.positionTrackingFrame[1]; - auto [u, v] = computeUV(gX, gY, gZ, sensorID, constants::radii[layerID]); - - const double snp = wTrk.getSnp(); - const double tgl = wTrk.getTgl(); - const double csci = 1. / std::sqrt(1. - (snp * snp)); - const double dydx = snp * csci; - const double dzdx = tgl * csci; - - auto pu = legendrePols(N, u); - auto pv = legendrePols(N, v); - - int legIdx = 0; - const int legColStart = nColRB; - for (int i = 0; i <= N; ++i) { - for (int j = 0; j <= i; ++j) { - const double basis = pu[j] * pv[i - j]; - gLabels.push_back(sensorVol->getLabel().asCalib().rawGBL(legIdx)); - gDer(0, legColStart + legIdx) = dydx * basis; - gDer(1, legColStart + legIdx) = dzdx * basis; - ++legIdx; - } + // 3) calibration derivatives (apply directly on the whole sensor, not on individual tiles) + if (calibSet) { + const int nd = calibSet->nDOFs(); + Eigen::MatrixXd calDer(3, nd); + calibSet->fillDerivatives(derCtx, calDer); + for (int iDOF = 0; iDOF < nd; ++iDOF) { + gLabels.push_back(sensorVol->getLabel().asCalib().rawGBL(iDOF)); } + gDer.middleCols(curCol, nd) = calDer; + curCol += nd; } point.addGlobals(gLabels, gDer); } @@ -514,32 +493,24 @@ void AlignmentSpec::updateTimeDependentParams(ProcessingContext& pc) buildHierarchy(); - if (mParams->doMisalignmentLeg || mParams->doMisalignmentRB) { - TMatrixD null(1, 1); - null(0, 0) = 0; - for (int i = 0; i < 6; ++i) { - mDeformations[i] = o2::math_utils::Legendre2DPolynominal(null); - mRigidBodyParams[i].setZero(); + if (mParams->doMisalignmentLeg || mParams->doMisalignmentRB || mParams->doMisalignmentInex) { + mMisalignment = {}; + for (auto& rb : mRigidBodyParams) { + rb.setZero(); } if (!mParams->misAlgJson.empty()) { - using json = nlohmann::json; - std::ifstream f(mParams->misAlgJson); - auto data = json::parse(f); - for (const auto& item : data) { - int id = item["id"].get(); - if (mParams->doMisalignmentLeg && item.contains("matrix")) { - auto v = item["matrix"].get>>(); - TMatrixD m(v.size(), v[v.size() - 1].size()); - for (size_t r{0}; r < v.size(); ++r) { - for (size_t c{0}; c < v[r].size(); ++c) { - m(r, c) = v[r][c]; - } + mMisalignment = loadMisalignmentModel(mParams->misAlgJson); + if (mParams->doMisalignmentRB) { + using json = nlohmann::json; + std::ifstream f(mParams->misAlgJson); + auto data = json::parse(f); + for (const auto& item : data) { + int id = item["id"].get(); + if (!item.contains("rigidBody")) { + continue; } - mDeformations[id] = o2::math_utils::Legendre2DPolynominal(m); - } - if (mParams->doMisalignmentRB && item.contains("rigidBody")) { auto rb = item["rigidBody"].get>(); - for (int k = 0; k < 6 && k < (int)rb.size(); ++k) { + for (int k = 0; k < 6 && k < static_cast(rb.size()); ++k) { mRigidBodyParams[id](k) = rb[k]; } } @@ -848,6 +819,12 @@ bool AlignmentSpec::applyMisalignment(Eigen::Vector2d& res, const FrameInfoExt& const int sensorID = constants::detID::getSensorID(frame.sens); const int layerID = constants::detID::getDetID2Layer(frame.sens); + const MisalignmentFrame misFrame{ + .sensorID = sensorID, + .layerID = layerID, + .x = frame.x, + .alpha = frame.alpha, + .z = frame.positionTrackingFrame[1]}; // --- Legendre deformation (non-rigid-body) --- if (mParams->doMisalignmentLeg && mIsITS3 && mUseMC) { @@ -866,36 +843,18 @@ bool AlignmentSpec::applyMisalignment(Eigen::Vector2d& res, const FrameInfoExt& } o2::track::TrackParD mcPar(xyz, pxyz, TMath::Nint(pPDG->Charge() / 3), false); - const double r = frame.x; - const double gloX = r * std::cos(frame.alpha); - const double gloY = r * std::sin(frame.alpha); - const double gloZ = frame.positionTrackingFrame[1]; - auto [u, v] = computeUV(gloX, gloY, gloZ, sensorID, constants::radii[layerID]); - const double h = mDeformations[sensorID](u, v); - auto mcAtCl = mcPar; if (!mcAtCl.rotate(frame.alpha) || !prop->PropagateToXBxByBz(mcAtCl, frame.x)) { return false; } - const double snp = mcAtCl.getSnp(); - const double tgl = mcAtCl.getTgl(); - const double csci = 1. / std::sqrt(1. - (snp * snp)); - const double dydx = snp * csci; - const double dzdx = tgl * csci; - const double dy = dydx * h; - const double dz = dzdx * h; - - const double newGloY = (r * std::sin(frame.alpha)) + (dy * std::cos(frame.alpha)); - const double newGloX = (r * std::cos(frame.alpha)) - (dy * std::sin(frame.alpha)); - const double newGloZ = gloZ + dz; - auto [uNew, vNew] = computeUV(newGloX, newGloY, newGloZ, sensorID, constants::radii[layerID]); - if (std::abs(uNew) > 1. || std::abs(vNew) > 1.) { + const auto shift = evaluateLegendreShift(mMisalignment[sensorID], misFrame, computeTrackSlopes(mcAtCl.getSnp(), mcAtCl.getTgl())); + if (!shift.accepted) { return false; } - res[0] += dy; - res[1] += dz; + res[0] += shift.dy; + res[1] += shift.dz; } // --- Rigid body misalignment --- @@ -910,7 +869,7 @@ bool AlignmentSpec::applyMisalignment(Eigen::Vector2d& res, const FrameInfoExt& const auto* tileVol = mChip2Hiearchy.at(lbl); // derivative in TRK frame (3x6: rows = dy, dz, dsnp) - Matrix36 der = getRigidBodyDerivatives(wTrk); + Matrix36 der = getRigidBodyBaseDerivatives(makeDerivativeContext(frame, wTrk)); // TRK -> tile LOC const double posTrk[3] = {frame.x, 0., 0.}; @@ -929,6 +888,26 @@ bool AlignmentSpec::applyMisalignment(Eigen::Vector2d& res, const FrameInfoExt& res[1] += shift[1]; // dz } + // --- In-extensional deformation --- + // displacement field u(phi,z) = (u_phi, u_z, u_r) + // dy = -u_phi + y' * u_r, dz = -u_z + z' * u_r + if (mParams->doMisalignmentInex) { + const auto shift = evaluateInextensionalShift(mMisalignment[sensorID], misFrame, computeTrackSlopes(wTrk.getSnp(), wTrk.getTgl())); + res[0] += shift.dy; + res[1] += shift.dz; + } + + if (mOutOpt[OutputOpt::MisRes]) { + (*mDBGOut) << "mis" + << "dy=" << res[0] + << "dz=" << res[1] + << "sens=" << sensorID + << "lay=" << layerID + << "z=" << frame.positionTrackingFrame[1] + << "phi=" << frame.alpha + << "\n"; + } + return true; } diff --git a/Detectors/Upgrades/ITS3/alignment/src/MisalignmentUtils.cxx b/Detectors/Upgrades/ITS3/alignment/src/MisalignmentUtils.cxx new file mode 100644 index 0000000000000..ee5198ee98e0c --- /dev/null +++ b/Detectors/Upgrades/ITS3/alignment/src/MisalignmentUtils.cxx @@ -0,0 +1,151 @@ +// 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 "ITS3Align/MisalignmentUtils.h" + +#include +#include +#include +#include + +#include +#include + +#include "Framework/Logger.h" +#include "ITS3Base/SpecsV2.h" + +namespace o2::its3::align +{ + +bool MisalignmentModel::empty() const noexcept +{ + return std::all_of(sensors.begin(), sensors.end(), [](const auto& sensor) { return sensor.empty(); }); +} + +MisalignmentModel loadMisalignmentModel(const std::string& jsonPath) +{ + MisalignmentModel model; + if (jsonPath.empty()) { + return model; + } + + std::ifstream f(jsonPath); + if (!f.is_open()) { + LOGP(fatal, "Cannot open misalignment JSON file: {}", jsonPath); + } + + using json = nlohmann::json; + const auto data = json::parse(f); + for (const auto& item : data) { + const int id = item["id"].get(); + if (id < 0 || id >= static_cast(MisalignmentModel::NSensors)) { + LOGP(fatal, "Misalignment sensor id {} out of range [0, {}) in {}", id, MisalignmentModel::NSensors, jsonPath); + } + + auto& sensor = model[id]; + if (item.contains("matrix")) { + auto v = item["matrix"].get>>(); + if (v.empty()) { + LOGP(fatal, "Legendre matrix for sensor {} is empty in {}", id, jsonPath); + } + TMatrixD m(v.size(), v.back().size()); + for (std::size_t r{0}; r < v.size(); ++r) { + for (std::size_t c{0}; c < v[r].size(); ++c) { + m(r, c) = v[r][c]; + } + } + sensor.legendre = o2::math_utils::Legendre2DPolynominal(m); + sensor.hasLegendre = true; + } + if (item.contains("inextensional")) { + const auto& inex = item["inextensional"]; + sensor.hasInextensional = true; + if (inex.contains("modes")) { + for (const auto& [key, val] : inex["modes"].items()) { + sensor.inextensional.modes[std::stoi(key)] = val.get>(); + } + } + if (inex.contains("alpha")) { + sensor.inextensional.alpha = inex["alpha"].get(); + } + if (inex.contains("beta")) { + sensor.inextensional.beta = inex["beta"].get(); + } + } + } + + return model; +} + +MisalignmentShift evaluateLegendreShift(const SensorMisalignment& sensor, const MisalignmentFrame& frame, const TrackSlopes& slopes) +{ + MisalignmentShift shift; + if (!sensor.hasLegendre) { + return shift; + } + + const double gloX = frame.x * std::cos(frame.alpha); + const double gloY = frame.x * std::sin(frame.alpha); + const double gloZ = frame.z; + auto [u, v] = computeUV(gloX, gloY, gloZ, frame.sensorID, constants::radii[frame.layerID]); + const double h = sensor.legendre(u, v); + + shift.dy = slopes.dydx * h; + shift.dz = slopes.dzdx * h; + + const double newGloY = gloY + (shift.dy * std::cos(frame.alpha)); + const double newGloX = gloX - (shift.dy * std::sin(frame.alpha)); + const double newGloZ = gloZ + shift.dz; + auto [uNew, vNew] = computeUV(newGloX, newGloY, newGloZ, frame.sensorID, constants::radii[frame.layerID]); + shift.accepted = std::abs(uNew) <= 1. && std::abs(vNew) <= 1.; + return shift; +} + +MisalignmentShift evaluateInextensionalShift(const SensorMisalignment& sensor, const MisalignmentFrame& frame, const TrackSlopes& slopes) +{ + MisalignmentShift shift; + if (!sensor.hasInextensional) { + return shift; + } + + const double r = constants::radii[frame.layerID]; + const double phi = std::atan2(r * std::sin(frame.alpha), r * std::cos(frame.alpha)); + const double z = frame.z; + const auto& inex = sensor.inextensional; + + double uz = 0., uphi = 0., ur = 0.; + for (const auto& [n, coeffs] : inex.modes) { + const double a_n = coeffs[0], b_n = coeffs[1], c_n = coeffs[2], d_n = coeffs[3]; + const double sn = std::sin(n * phi); + const double cn = std::cos(n * phi); + const int n2 = n * n; + + const double fn = (a_n * cn) + (b_n * sn); + const double fpn = (-n * a_n * sn) + (n * b_n * cn); + const double fppn = (-n2 * a_n * cn) - (n2 * b_n * sn); + const double gn = (c_n * cn) + (d_n * sn); + const double gpn = (-n * c_n * sn) + (n * d_n * cn); + + uz += fn; + uphi += -(z / r) * fpn + gn; + ur += (z / r) * fppn - gpn; + } + + uz += inex.alpha * phi; + uphi += -(z / r) * inex.alpha + inex.beta * phi; + ur += -inex.beta; + + shift.dy = -uphi + (slopes.dydx * ur); + shift.dz = -uz + (slopes.dzdx * ur); + return shift; +} + +} // namespace o2::its3::align diff --git a/Detectors/Upgrades/ITS3/study/src/TrackingStudy.cxx b/Detectors/Upgrades/ITS3/study/src/TrackingStudy.cxx index 4ce2c79cb23f1..63a08d1c9c6ab 100644 --- a/Detectors/Upgrades/ITS3/study/src/TrackingStudy.cxx +++ b/Detectors/Upgrades/ITS3/study/src/TrackingStudy.cxx @@ -11,12 +11,10 @@ #include #include -#include #include #include #include -#include #include "CommonUtils/TreeStreamRedirector.h" #include "DataFormatsGlobalTracking/RecoContainer.h" @@ -38,6 +36,7 @@ #include "ITS3Reconstruction/IOUtils.h" #include "ITS3TrackingStudy/ITS3TrackingStudyParam.h" #include "ITS3TrackingStudy/ParticleInfoExt.h" +#include "ITS3Align/MisalignmentUtils.h" #include "ITS3Align/TrackFit.h" #include "ReconstructionDataFormats/DCA.h" #include "ReconstructionDataFormats/GlobalTrackID.h" @@ -47,7 +46,6 @@ #include "SimulationDataFormat/MCEventLabel.h" #include "SimulationDataFormat/MCUtils.h" #include "Steer/MCKinematicsReader.h" -#include "MathUtils/LegendrePols.h" #include "Framework/Logger.h" namespace o2::its3::study @@ -153,7 +151,7 @@ class TrackingStudySpec : public Task o2::steer::MCKinematicsReader mMCReader; // reader of MC information const o2::its3::TopologyDictionary* mITSDict = nullptr; // cluster patterns dictionary o2::globaltracking::RecoContainer mRecoData; - std::array mDeformations; // one per sensorID (0-5) + align::MisalignmentModel mMisalignment; }; void TrackingStudySpec::init(InitContext& ic) @@ -186,26 +184,9 @@ void TrackingStudySpec::updateTimeDependentParams(ProcessingContext& pc) o2::its::GeometryTGeo::Instance()->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G, o2::math_utils::TransformType::T2G)); mParams = &ITS3TrackingStudyParam::Instance(); if (mParams->doMisalignment) { - TMatrixD null(1, 1); - null(0, 0) = 0; - for (int i = 0; i < 6; ++i) { - mDeformations[i] = o2::math_utils::Legendre2DPolynominal(null); - } + mMisalignment = {}; if (!mParams->misAlgJson.empty()) { - using json = nlohmann::json; - std::ifstream f(mParams->misAlgJson); - auto data = json::parse(f); - for (const auto& item : data) { - int id = item["id"].get(); - auto v = item["matrix"].get>>(); - TMatrixD m(v.size(), v[v.size() - 1].size()); - for (size_t r{0}; r < v.size(); ++r) { - for (size_t c{0}; c < v[r].size(); ++c) { - m(r, c) = v[r][c]; - } - } - mDeformations[id] = o2::math_utils::Legendre2DPolynominal(m); - } + mMisalignment = align::loadMisalignmentModel(mParams->misAlgJson); } } } @@ -984,17 +965,6 @@ void TrackingStudySpec::doMisalignmentStudy() int goodRefit{0}, notPassedSel{0}, fitFail{0}, fitFailMis{0}; - // compute normalized (u,v) in [-1,1] from global position - auto computeUV = [](float gloX, float gloY, float gloZ, int sensorID, float radius) -> std::pair { - const bool isTop = sensorID % 2 == 0; - const double phi = o2::math_utils::to02Pi(std::atan2(gloY, gloX)); - const double phiBorder1 = o2::math_utils::to02Pi(((isTop ? 0. : 1.) * TMath::Pi()) + std::asin(constants::equatorialGap / 2. / radius)); - const double phiBorder2 = o2::math_utils::to02Pi(((isTop ? 1. : 2.) * TMath::Pi()) - std::asin(constants::equatorialGap / 2. / radius)); - const double u = (((phi - phiBorder1) * 2.) / (phiBorder2 - phiBorder1)) - 1.; - const double v = ((2. * gloZ + constants::segment::lengthSensitive) / constants::segment::lengthSensitive) - 1.; - return {u, v}; - }; - float chi2{0}; auto writeTree = [&](const char* treeName, const std::array& clArr, @@ -1102,8 +1072,8 @@ void TrackingStudySpec::doMisalignmentStudy() } writeTree("idealRes", clArr, extrapOut, extrapInw, lbl); - // Propagate MC truth to each cluster's (alpha, x) to get true track direction, - // then compute dy = dydx*h(u,v), dz = dzdx*h(u,v) - first Newton step. + // Propagate MC truth to each cluster's (alpha, x) to get true track direction. + // The shared misalignment evaluators then provide the tracking-frame dy/dz shift. const auto mcTrk = mMCReader.getTrack(lbl); if (!mcTrk) { continue; @@ -1132,14 +1102,7 @@ void TrackingStudySpec::doMisalignmentStudy() } const int sensorID = constants::detID::getSensorID(sens); const int layerID = constants::detID::getDetID2Layer(sens); - - // compute h(u,v) at this cluster - const float r = orig.getX(); - const float gloX = r * std::cos(orig.alpha); - const float gloY = r * std::sin(orig.alpha); - const float gloZ = orig.getZ(); - auto [u, v] = computeUV(gloX, gloY, gloZ, sensorID, constants::radii[layerID]); - const double h = mDeformations[sensorID](u, v); + const auto& sensorMis = mMisalignment[sensorID]; // propagate MC track to cluster's tracking frame to get true slopes auto mcAtCl = mcPar; @@ -1147,28 +1110,31 @@ void TrackingStudySpec::doMisalignmentStudy() clArrMis[1 + iLay] = nullptr; // can't compute slopes -> drop cluster continue; } - const float snp = mcAtCl.getSnp(); - const float tgl = mcAtCl.getTgl(); - const float csci = 1.f / std::sqrt(1.f - (snp * snp)); - const float dydx = snp * csci; - const float dzdx = tgl * csci; - const float dy = dydx * static_cast(h); - const float dz = dzdx * static_cast(h); - - // check if shifted position is still within sensor acceptance - const float newGloY = (r * std::sin(orig.alpha)) + (dy * std::cos(orig.alpha)); - const float newGloX = (r * std::cos(orig.alpha)) - (dy * std::sin(orig.alpha)); - const float newGloZ = gloZ + dz; - auto [uNew, vNew] = computeUV(newGloX, newGloY, newGloZ, sensorID, constants::radii[layerID]); - if (std::abs(uNew) > 1. || std::abs(vNew) > 1.) { - clArrMis[1 + iLay] = nullptr; // shifted outside acceptance - continue; + const align::MisalignmentFrame misFrame{ + .sensorID = sensorID, + .layerID = layerID, + .x = orig.getX(), + .alpha = orig.alpha, + .z = orig.getZ()}; + const auto slopes = align::computeTrackSlopes(mcAtCl.getSnp(), mcAtCl.getTgl()); + + align::MisalignmentShift totalShift; + if (sensorMis.hasLegendre) { + const auto shift = align::evaluateLegendreShift(sensorMis, misFrame, slopes); + if (!shift.accepted) { + clArrMis[1 + iLay] = nullptr; // shifted outside acceptance + continue; + } + totalShift += shift; + } + if (sensorMis.hasInextensional) { + totalShift += align::evaluateInextensionalShift(sensorMis, misFrame, slopes); } // create shifted copy: keep x=r (nominal), shift y and z misClArr[iLay] = orig; - misClArr[iLay].setY(orig.getY() + dy); - misClArr[iLay].setZ(orig.getZ() + dz); + misClArr[iLay].setY(orig.getY() + totalShift.dy); + misClArr[iLay].setZ(orig.getZ() + totalShift.dz); misClArr[iLay].setSigmaY2(orig.getSigmaY2() + (mParams->misAlgExtCY[sensorID] * mParams->misAlgExtCY[sensorID])); misClArr[iLay].setSigmaZ2(orig.getSigmaZ2() + (mParams->misAlgExtCZ[sensorID] * mParams->misAlgExtCZ[sensorID])); clArrMis[1 + iLay] = &misClArr[iLay]; From 4b43e4050f38d86147b85fb38aa9337d5d4496ca Mon Sep 17 00:00:00 2001 From: Francesco Noferini Date: Tue, 7 Apr 2026 12:27:18 +0200 Subject: [PATCH 464/701] require min num of entries for TOF Diagnostic calibs (#15249) --- .../include/TOFCalibration/TOFDiagnosticCalibrator.h | 9 ++++++--- .../TOF/calibration/src/TOFDiagnosticCalibrator.cxx | 10 ++++++++++ .../testWorkflow/TOFDiagnosticCalibratorSpec.h | 8 +++++--- .../testWorkflow/tof-diagnostic-workflow.cxx | 4 +++- 4 files changed, 24 insertions(+), 7 deletions(-) diff --git a/Detectors/TOF/calibration/include/TOFCalibration/TOFDiagnosticCalibrator.h b/Detectors/TOF/calibration/include/TOFCalibration/TOFDiagnosticCalibrator.h index 7b23df6040d3d..fccfcf51fdaa9 100644 --- a/Detectors/TOF/calibration/include/TOFCalibration/TOFDiagnosticCalibrator.h +++ b/Detectors/TOF/calibration/include/TOFCalibration/TOFDiagnosticCalibrator.h @@ -31,9 +31,9 @@ class TOFDiagnosticCalibrator final : public o2::calibration::TimeSlotCalibratio int mRunNumber = -1; public: - TOFDiagnosticCalibrator() = default; + TOFDiagnosticCalibrator(int minROwin = 100) : mMinROwin(minROwin) {} ~TOFDiagnosticCalibrator() final = default; - bool hasEnoughData(const Slot& slot) const final { return true; } + bool hasEnoughData(const Slot& slot) const final; void initOutput() final; void finalizeSlot(Slot& slot) final; Slot& emplaceNewSlot(bool front, TFType tstart, TFType tend) final; @@ -43,12 +43,15 @@ class TOFDiagnosticCalibrator final : public o2::calibration::TimeSlotCalibratio const std::vector& getDiagnosticVector() const { return mDiagnosticVector; } const CcdbObjectInfoVector& getDiagnosticInfoVector() const { return mccdbInfoVector; } CcdbObjectInfoVector& getDiagnosticInfoVector() { return mccdbInfoVector; } + int getMinROwin() const { return mMinROwin; } + void setMinROwin(int rowin) { mMinROwin = rowin; } private: CcdbObjectInfoVector mccdbInfoVector; std::vector mDiagnosticVector; + int mMinROwin; // minimal number of readout windows needed to finalize the object - ClassDefOverride(TOFDiagnosticCalibrator, 1); + ClassDefOverride(TOFDiagnosticCalibrator, 2); }; } // end namespace tof diff --git a/Detectors/TOF/calibration/src/TOFDiagnosticCalibrator.cxx b/Detectors/TOF/calibration/src/TOFDiagnosticCalibrator.cxx index 9a4118dbba493..f238d69bb75ed 100644 --- a/Detectors/TOF/calibration/src/TOFDiagnosticCalibrator.cxx +++ b/Detectors/TOF/calibration/src/TOFDiagnosticCalibrator.cxx @@ -28,7 +28,17 @@ void TOFDiagnosticCalibrator::initOutput() mccdbInfoVector.clear(); mDiagnosticVector.clear(); } +//---------------------------------------------------------- +bool TOFDiagnosticCalibrator::hasEnoughData(const Slot& slot) const +{ + const Diagnostic* diag = slot.getContainer(); + if (diag->getFrequencyROW() < mMinROwin) { + return false; + } + + return true; +} //---------------------------------------------------------- void TOFDiagnosticCalibrator::finalizeSlot(Slot& slot) { diff --git a/Detectors/TOF/calibration/testWorkflow/TOFDiagnosticCalibratorSpec.h b/Detectors/TOF/calibration/testWorkflow/TOFDiagnosticCalibratorSpec.h index 7887ff848d544..91b931f3b96b3 100644 --- a/Detectors/TOF/calibration/testWorkflow/TOFDiagnosticCalibratorSpec.h +++ b/Detectors/TOF/calibration/testWorkflow/TOFDiagnosticCalibratorSpec.h @@ -37,7 +37,7 @@ namespace calibration class TOFDiagnosticCalibDevice : public o2::framework::Task { public: - TOFDiagnosticCalibDevice(std::shared_ptr req, int runnumber = -1) : mCCDBRequest(req), mRunNumber(runnumber) {} + TOFDiagnosticCalibDevice(std::shared_ptr req, int runnumber = -1, int rowinMin = 100000) : mCCDBRequest(req), mRunNumber(runnumber), mMinROwin(rowinMin) {} void init(o2::framework::InitContext& ic) final { o2::base::GRPGeomHelper::instance().setRequest(mCCDBRequest); @@ -47,6 +47,7 @@ class TOFDiagnosticCalibDevice : public o2::framework::Task mCalibrator->setSlotLength(slotL); mCalibrator->setMaxSlotsDelay(delay); mCalibrator->setRunNumber(mRunNumber); + mCalibrator->setMinROwin(mMinROwin); } void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) final @@ -75,6 +76,7 @@ class TOFDiagnosticCalibDevice : public o2::framework::Task std::unique_ptr mCalibrator; std::shared_ptr mCCDBRequest; int mRunNumber = -1; + int mMinROwin = 100000; //________________________________________________________________ void sendOutput(DataAllocator& output) @@ -104,7 +106,7 @@ class TOFDiagnosticCalibDevice : public o2::framework::Task namespace framework { -DataProcessorSpec getTOFDiagnosticCalibDeviceSpec(int runnumber) +DataProcessorSpec getTOFDiagnosticCalibDeviceSpec(int runnumber, int rowinMin) { using device = o2::calibration::TOFDiagnosticCalibDevice; using clbUtils = o2::calibration::Utils; @@ -125,7 +127,7 @@ DataProcessorSpec getTOFDiagnosticCalibDeviceSpec(int runnumber) "tof-diagnostic-calibration", inputs, outputs, - AlgorithmSpec{adaptFromTask(ccdbRequest, runnumber)}, + AlgorithmSpec{adaptFromTask(ccdbRequest, runnumber, rowinMin)}, Options{ {"tf-per-slot", VariantType::UInt32, 5u, {"number of TFs per calibration time slot"}}, {"max-delay", VariantType::UInt32, 3u, {"number of slots in past to consider"}}}}; diff --git a/Detectors/TOF/calibration/testWorkflow/tof-diagnostic-workflow.cxx b/Detectors/TOF/calibration/testWorkflow/tof-diagnostic-workflow.cxx index 3cde7be96867a..b45fcc2b5498c 100644 --- a/Detectors/TOF/calibration/testWorkflow/tof-diagnostic-workflow.cxx +++ b/Detectors/TOF/calibration/testWorkflow/tof-diagnostic-workflow.cxx @@ -19,6 +19,7 @@ void customize(std::vector& workflowOptions) { // option allowing to set parameters workflowOptions.push_back(ConfigParamSpec{"tof-dia-run-number", o2::framework::VariantType::Int, -1, {"run number"}}); + workflowOptions.push_back(ConfigParamSpec{"tof-dia-min-rowin", o2::framework::VariantType::Int, 100000, {"min number of TOF Readout Windows, def=100k (3 s)"}}); } // ------------------------------------------------------------------ @@ -29,6 +30,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { WorkflowSpec specs; auto runnumber = configcontext.options().get("tof-dia-run-number"); - specs.emplace_back(getTOFDiagnosticCalibDeviceSpec(runnumber)); + auto rowinMin = configcontext.options().get("tof-dia-min-rowin"); + specs.emplace_back(getTOFDiagnosticCalibDeviceSpec(runnumber, rowinMin)); return specs; } From 5b27406094d6e12bc2daeb32bf6a25bebe1d7f95 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 5 Apr 2026 21:52:34 +0200 Subject: [PATCH 465/701] DPL: refactor InputSpan Now that navigating the MessageSet does not have an index anymore it does not make sense to use a random accessor to implement the InputSpan::Iterator, because that results in an O(N^2) loop for normal linear iterations. It is much better to simply implement a proper advancement operator, so that looping stays O(N). This resumes the performance when iterating on old-style multiparts which are apparently still used in some cases. General modernization of InputSpan also done. --- .../Core/include/Framework/DataModelViews.h | 6 +- Framework/Core/include/Framework/DataRef.h | 10 ++ .../Core/include/Framework/InputRecord.h | 47 +++++-- Framework/Core/include/Framework/InputSpan.h | 130 ++++++++++-------- .../Core/src/CompletionPolicyHelpers.cxx | 6 +- Framework/Core/src/DataProcessingDevice.cxx | 41 +++--- Framework/Core/src/DataRelayer.cxx | 62 +++++---- Framework/Core/src/InputRecord.cxx | 10 ++ Framework/Core/src/InputSpan.cxx | 24 +--- Framework/Core/test/benchmark_InputRecord.cxx | 14 +- Framework/Core/test/test_CompletionPolicy.cxx | 7 +- Framework/Core/test/test_InputRecord.cxx | 12 +- .../Core/test/test_InputRecordWalker.cxx | 16 +-- Framework/Core/test/test_InputSpan.cxx | 12 +- Framework/Utils/test/RawPageTestData.h | 14 +- Framework/Utils/test/test_RootTreeWriter.cxx | 12 +- Utilities/DataSampling/src/Dispatcher.cxx | 2 +- 17 files changed, 253 insertions(+), 172 deletions(-) diff --git a/Framework/Core/include/Framework/DataModelViews.h b/Framework/Core/include/Framework/DataModelViews.h index 53d6e6615b96e..dd8d65ea16459 100644 --- a/Framework/Core/include/Framework/DataModelViews.h +++ b/Framework/Core/include/Framework/DataModelViews.h @@ -16,6 +16,7 @@ #include "DomainInfoHeader.h" #include "SourceInfoHeader.h" #include "Headers/DataHeader.h" +#include "Framework/DataRef.h" #include "Framework/TimesliceSlot.h" #include #include @@ -80,10 +81,7 @@ struct count_parts { } }; -struct DataRefIndices { - size_t headerIdx; - size_t payloadIdx; -}; +// DataRefIndices is defined in Framework/DataRef.h struct get_pair { size_t pairId; diff --git a/Framework/Core/include/Framework/DataRef.h b/Framework/Core/include/Framework/DataRef.h index d4cba88b19333..aad667c8c33bd 100644 --- a/Framework/Core/include/Framework/DataRef.h +++ b/Framework/Core/include/Framework/DataRef.h @@ -12,6 +12,7 @@ #define FRAMEWORK_DATAREF_H #include // for size_t +#include namespace o2 { @@ -29,6 +30,15 @@ struct DataRef { size_t payloadSize = 0; }; +/// Raw indices into the message vector for one (header, payload) pair. +/// Kept in a lightweight header so InputSpan can use it without pulling in FairMQ. +struct DataRefIndices { + size_t headerIdx; + size_t payloadIdx; + bool operator==(const DataRefIndices&) const = default; + auto operator<=>(const DataRefIndices&) const = default; +}; + } // namespace framework } // namespace o2 diff --git a/Framework/Core/include/Framework/InputRecord.h b/Framework/Core/include/Framework/InputRecord.h index 96963f88524be..d2e152c1bcacc 100644 --- a/Framework/Core/include/Framework/InputRecord.h +++ b/Framework/Core/include/Framework/InputRecord.h @@ -13,6 +13,7 @@ #include "Framework/DataRef.h" #include "Framework/DataRefUtils.h" +#include "Framework/InputSpan.h" #include "Framework/InputRoute.h" #include "Framework/TypeTraits.h" #include "Framework/TableConsumer.h" @@ -202,6 +203,15 @@ class InputRecord [[nodiscard]] size_t getNofParts(int pos) const; + /// O(1) access to the part described by @a indices in slot @a pos. + [[nodiscard]] DataRef getAtIndices(int pos, DataRefIndices indices) const; + + /// O(1) advance from @a current to the next part's indices in slot @a pos. + [[nodiscard]] DataRefIndices nextIndices(int pos, DataRefIndices current) const + { + return mSpan.nextIndices(pos, current); + } + // Given a binding by string, return the associated DataRef DataRef getDataRefByString(const char* bindingName, int part = 0) const { @@ -568,8 +578,8 @@ class InputRecord Iterator() = delete; - Iterator(ParentType const* parent, size_t position = 0, size_t size = 0) - : mPosition(position), mSize(size > position ? size : position), mParent(parent), mElement{nullptr, nullptr, nullptr} + Iterator(ParentType const* parent, bool isEnd = false) + : mPosition(isEnd ? parent->size() : 0), mSize(parent->size()), mParent(parent), mElement{nullptr, nullptr, nullptr} { if (mPosition < mSize) { if (mParent->isValid(mPosition)) { @@ -678,18 +688,29 @@ class InputRecord using reference = typename BaseType::reference; using pointer = typename BaseType::pointer; using ElementType = typename std::remove_const::type; - using iterator = Iterator; - using const_iterator = Iterator; + using iterator = InputSpan::Iterator; + using const_iterator = InputSpan::Iterator; + + InputRecordIterator(InputRecord const* parent, bool isEnd = false) + : BaseType(parent, isEnd) + { + } + + /// Initial indices for part-level iteration: first part starts at {headerIdx=0, payloadIdx=1}. + [[nodiscard]] DataRefIndices initialIndices() const { return {0, 1}; } + /// Sentinel used by nextIndicesGetter to signal end-of-slot. + [[nodiscard]] DataRefIndices endIndices() const { return {size_t(-1), size_t(-1)}; } - InputRecordIterator(InputRecord const* parent, size_t position = 0, size_t size = 0) - : BaseType(parent, position, size) + /// Get element at the given raw message indices in O(1). + [[nodiscard]] ElementType getAtIndices(DataRefIndices indices) const { + return this->parent()->getAtIndices(this->position(), indices); } - /// Get element at {slotindex, partindex} - [[nodiscard]] ElementType getByPos(size_t pos) const + /// Advance @a current to the next part's indices in O(1). + [[nodiscard]] DataRefIndices nextIndices(DataRefIndices current) const { - return this->parent()->getByPos(this->position(), pos); + return this->parent()->nextIndices(this->position(), current); } /// Check if slot is valid, index of part is not used @@ -709,12 +730,12 @@ class InputRecord [[nodiscard]] const_iterator begin() const { - return const_iterator(this, 0, size()); + return const_iterator(this, size() == 0); } [[nodiscard]] const_iterator end() const { - return const_iterator(this, size()); + return const_iterator(this, true); } }; @@ -723,12 +744,12 @@ class InputRecord [[nodiscard]] const_iterator begin() const { - return {this, 0, size()}; + return {this, false}; } [[nodiscard]] const_iterator end() const { - return {this, size()}; + return {this, true}; } InputSpan& span() diff --git a/Framework/Core/include/Framework/InputSpan.h b/Framework/Core/include/Framework/InputSpan.h index cf8c8acda6796..dbe270f0e030d 100644 --- a/Framework/Core/include/Framework/InputSpan.h +++ b/Framework/Core/include/Framework/InputSpan.h @@ -14,8 +14,8 @@ #include "Framework/DataRef.h" #include -extern template class std::function; -extern template class std::function; +extern template class std::function; +extern template class std::function; namespace o2::framework { @@ -32,37 +32,48 @@ class InputSpan InputSpan(InputSpan const&) = delete; InputSpan(InputSpan&&) = default; - /// @a getter is the mapping between an element of the span referred by - /// index and the buffer associated. - /// @a size is the number of elements in the span. - InputSpan(std::function getter, size_t size); + /// Navigate the message store via the DataRefIndices protocol. + /// get_next_pair (DataModelViews.h) provides O(1) sequential advancement for nextIndicesGetter. + InputSpan(std::function nofPartsGetter, + std::function refCountGetter, + std::function indicesGetter, + std::function nextIndicesGetter, + size_t size); - /// @a getter is the mapping between an element of the span referred by - /// index and the buffer associated. - /// @a size is the number of elements in the span. - InputSpan(std::function getter, size_t size); + /// @a i-th element of the InputSpan (O(partidx) sequential scan via indices protocol) + [[nodiscard]] DataRef get(size_t i, size_t partidx = 0) const + { + DataRefIndices idx{0, 1}; + for (size_t p = 0; p < partidx; ++p) { + idx = mNextIndicesGetter(i, idx); + } + return mIndicesGetter(i, idx); + } - /// @a getter is the mapping between an element of the span referred by - /// index and the buffer associated. - /// @nofPartsGetter is the getter for the number of parts associated with an index - /// @a size is the number of elements in the span. - InputSpan(std::function getter, std::function nofPartsGetter, std::function refCountGetter, size_t size); + /// Return the DataRef for the part described by @a indices in slot @a slotIdx in O(1). + [[nodiscard]] DataRef getAtIndices(size_t slotIdx, DataRefIndices indices) const + { + return mIndicesGetter(slotIdx, indices); + } - /// @a i-th element of the InputSpan - [[nodiscard]] DataRef get(size_t i, size_t partidx = 0) const + /// Advance from @a current to the indices of the next part in slot @a slotIdx in O(1). + [[nodiscard]] DataRefIndices nextIndices(size_t slotIdx, DataRefIndices current) const { - return mGetter(i, partidx); + return mNextIndicesGetter(slotIdx, current); } + // --- slot-level Iterator protocol (headerIdx doubles as slot position) --- + [[nodiscard]] DataRefIndices initialIndices() const { return {0, 0}; } + [[nodiscard]] DataRefIndices endIndices() const { return {mSize, 0}; } + [[nodiscard]] DataRef getAtIndices(DataRefIndices indices) const { return mIndicesGetter(indices.headerIdx, {0, 1}); } + [[nodiscard]] DataRefIndices nextIndices(DataRefIndices current) const { return {current.headerIdx + 1, 0}; } + /// @a number of parts in the i-th element of the InputSpan [[nodiscard]] size_t getNofParts(size_t i) const { if (i >= mSize) { return 0; } - if (!mNofPartsGetter) { - return 1; - } return mNofPartsGetter(i); } @@ -94,7 +105,8 @@ class InputSpan return get(i).payload; } - /// an iterator class working on position within the a parent class + /// An iterator over the elements of a parent container using the DataRefIndices protocol. + /// ParentT must provide: initialIndices(), getAtIndices(DataRefIndices), nextIndices(DataRefIndices). template class Iterator { @@ -110,23 +122,23 @@ class InputSpan Iterator() = delete; - Iterator(ParentType const* parent, size_t position = 0, size_t size = 0) - : mPosition(position), mSize(size > position ? size : position), mParent(parent), mElement{} + Iterator(ParentType const* parent, bool isEnd = false) + : mParent(parent), + mCurrentIndices(isEnd ? parent->endIndices() : parent->initialIndices()), + mElement{} { - if (mPosition < mSize) { - mElement = mParent->get(mPosition); + if (mCurrentIndices != mParent->endIndices()) { + mElement = mParent->getAtIndices(mCurrentIndices); } } - ~Iterator() = default; - // prefix increment SelfType& operator++() { - if (mPosition < mSize && ++mPosition < mSize) { - mElement = mParent->get(mPosition); + mCurrentIndices = mParent->nextIndices(mCurrentIndices); + if (mCurrentIndices != mParent->endIndices()) { + mElement = mParent->getAtIndices(mCurrentIndices); } else { - // reset the element to the default value of the type mElement = ElementType{}; } return *this; @@ -145,16 +157,14 @@ class InputSpan return mElement; } - // comparison bool operator==(const SelfType& rh) const { - return mPosition == rh.mPosition; + return mCurrentIndices == rh.mCurrentIndices; } - // comparison - bool operator!=(const SelfType& rh) const + auto operator<=>(const SelfType& rh) const { - return mPosition != rh.mPosition; + return mCurrentIndices <=> rh.mCurrentIndices; } // return pointer to parent instance @@ -163,22 +173,21 @@ class InputSpan return mParent; } - // return current position + // return current position (headerIdx serves as the slot index for slot-level iteration) [[nodiscard]] size_t position() const { - return mPosition; + return mCurrentIndices.headerIdx; } private: - size_t mPosition; - size_t mSize; ParentType const* mParent; + DataRefIndices mCurrentIndices; ElementType mElement; }; /// @class InputSpanIterator - /// An iterator over the input slots - /// It supports an iterator interface to access the parts in the slot + /// An iterator over the input slots. + /// It supports an iterator interface to access the parts in the slot. template class InputSpanIterator : public Iterator { @@ -192,24 +201,26 @@ class InputSpan using iterator = Iterator; using const_iterator = Iterator; - InputSpanIterator(InputSpan const* parent, size_t position = 0, size_t size = 0) - : BaseType(parent, position, size) + InputSpanIterator(InputSpan const* parent, bool isEnd = false) + : BaseType(parent, isEnd) { } - /// Get element at {slotindex, partindex} - [[nodiscard]] ElementType get(size_t pos) const + /// Initial indices for part-level iteration: first part starts at {headerIdx=0, payloadIdx=1}. + [[nodiscard]] DataRefIndices initialIndices() const { return {0, 1}; } + /// Sentinel used by nextIndicesGetter to signal end-of-slot. + [[nodiscard]] DataRefIndices endIndices() const { return {size_t(-1), size_t(-1)}; } + + /// Get element at the given raw message indices in O(1). + [[nodiscard]] ElementType getAtIndices(DataRefIndices indices) const { - return this->parent()->get(this->position(), pos); + return this->parent()->getAtIndices(this->position(), indices); } - /// Check if slot is valid, index of part is not used - [[nodiscard]] bool isValid(size_t = 0) const + /// Advance @a current to the next part's indices in O(1). + [[nodiscard]] DataRefIndices nextIndices(DataRefIndices current) const { - if (this->position() < this->parent()->size()) { - return this->parent()->isValid(this->position()); - } - return false; + return this->parent()->nextIndices(this->position(), current); } /// Get number of parts in input slot @@ -218,15 +229,14 @@ class InputSpan return this->parent()->getNofParts(this->position()); } - // iterator for the part access [[nodiscard]] const_iterator begin() const { - return const_iterator(this, 0, size()); + return const_iterator(this, size() == 0); } [[nodiscard]] const_iterator end() const { - return const_iterator(this, size()); + return const_iterator(this, true); } }; @@ -236,19 +246,19 @@ class InputSpan // supporting read-only access and returning const_iterator [[nodiscard]] const_iterator begin() const { - return {this, 0, size()}; + return {this, false}; } - // supporting read-only access and returning const_iterator [[nodiscard]] const_iterator end() const { - return {this, size()}; + return {this, true}; } private: - std::function mGetter; std::function mNofPartsGetter; std::function mRefCountGetter; + std::function mIndicesGetter; + std::function mNextIndicesGetter; size_t mSize; }; diff --git a/Framework/Core/src/CompletionPolicyHelpers.cxx b/Framework/Core/src/CompletionPolicyHelpers.cxx index 2b49b8dfa9acd..cc593ee7a2ed9 100644 --- a/Framework/Core/src/CompletionPolicyHelpers.cxx +++ b/Framework/Core/src/CompletionPolicyHelpers.cxx @@ -325,9 +325,9 @@ CompletionPolicy CompletionPolicyHelpers::consumeWhenAnyWithAllConditions(const // But I don't see any possibility to handle this in a better way. // Iterate on all specs and all inputs simultaneously - for (size_t i = 0; i < inputs.size(); ++i) { - char const* header = inputs.header(i); - auto& spec = specs[i]; + for (auto it = inputs.begin(), end = inputs.end(); it != end; ++it) { + char const* header = (*it).header; + auto& spec = specs[it.position()]; // In case a condition object is not there, we need to wait. if (header != nullptr) { canConsume = true; diff --git a/Framework/Core/src/DataProcessingDevice.cxx b/Framework/Core/src/DataProcessingDevice.cxx index 6b90747550278..b062f2bf68a75 100644 --- a/Framework/Core/src/DataProcessingDevice.cxx +++ b/Framework/Core/src/DataProcessingDevice.cxx @@ -2133,25 +2133,24 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v } else { currentSetOfInputs = relayer.consumeExistingInputsForTimeslice(slot); } - auto getter = [¤tSetOfInputs](size_t i, size_t partindex) -> DataRef { - if ((currentSetOfInputs[i] | count_payloads{}) > partindex) { - const char* headerptr = nullptr; - const char* payloadptr = nullptr; - size_t payloadSize = 0; - // - each input can have multiple parts - // - "part" denotes a sequence of messages belonging together, the first message of the - // sequence is the header message - // - each part has one or more payload messages - // - InputRecord provides all payloads as header-payload pair - auto const indices = currentSetOfInputs[i] | get_pair{partindex}; - auto const& headerMsg = currentSetOfInputs[i][indices.headerIdx]; - auto const& payloadMsg = currentSetOfInputs[i][indices.payloadIdx]; - headerptr = static_cast(headerMsg->GetData()); - payloadptr = payloadMsg ? static_cast(payloadMsg->GetData()) : nullptr; - payloadSize = payloadMsg ? payloadMsg->GetSize() : 0; - return DataRef{nullptr, headerptr, payloadptr, payloadSize}; + // Convert raw message indices directly to a DataRef in O(1). + // Used both by the sequential PartIterator and as the fallback for positional access. + auto indicesGetter = [¤tSetOfInputs](size_t i, DataRefIndices indices) -> DataRef { + auto const& msgs = currentSetOfInputs[i]; + if (msgs.size() <= indices.headerIdx) { + return DataRef{}; } - return DataRef{}; + auto const& headerMsg = msgs[indices.headerIdx]; + char const* payloadData = nullptr; + size_t payloadSize = 0; + if (msgs.size() > indices.payloadIdx && msgs[indices.payloadIdx]) { + payloadData = static_cast(msgs[indices.payloadIdx]->GetData()); + payloadSize = msgs[indices.payloadIdx]->GetSize(); + } + return DataRef{nullptr, + headerMsg ? static_cast(headerMsg->GetData()) : nullptr, + payloadData, + payloadSize}; }; auto nofPartsGetter = [¤tSetOfInputs](size_t i) -> size_t { return (currentSetOfInputs[i] | count_payloads{}); @@ -2160,7 +2159,11 @@ bool DataProcessingDevice::tryDispatchComputation(ServiceRegistryRef ref, std::v auto& header = static_cast(*(currentSetOfInputs[idx] | get_header{0})); return header.GetRefCount(); }; - return InputSpan{getter, nofPartsGetter, refCountGetter, currentSetOfInputs.size()}; + auto nextIndicesGetter = [¤tSetOfInputs](size_t i, DataRefIndices current) -> DataRefIndices { + auto next = currentSetOfInputs[i] | get_next_pair{current}; + return next.headerIdx < currentSetOfInputs[i].size() ? next : DataRefIndices{size_t(-1), size_t(-1)}; + }; + return InputSpan{nofPartsGetter, refCountGetter, indicesGetter, nextIndicesGetter, currentSetOfInputs.size()}; }; auto markInputsAsDone = [ref](TimesliceSlot slot) -> void { diff --git a/Framework/Core/src/DataRelayer.cxx b/Framework/Core/src/DataRelayer.cxx index fc9966ffad643..7adf5b5c97fbb 100644 --- a/Framework/Core/src/DataRelayer.cxx +++ b/Framework/Core/src/DataRelayer.cxx @@ -212,18 +212,6 @@ DataRelayer::ActivityStats DataRelayer::processDanglingInputs(std::vector(header->GetData()), - reinterpret_cast(payload ? payload->GetData() : nullptr), - payload ? payload->GetSize() : 0}; - } - return DataRef{}; - }; auto nPartsGetter = [&partial](size_t idx) { return partial[idx] | count_parts{}; }; @@ -231,7 +219,24 @@ DataRelayer::ActivityStats DataRelayer::processDanglingInputs(std::vector(*(partial[idx] | get_header{0})); return header.GetRefCount(); }; - InputSpan span{getter, nPartsGetter, refCountGetter, static_cast(partial.size())}; + auto indicesGetter = [&partial](size_t idx, DataRefIndices indices) -> DataRef { + if (!partial[idx].empty()) { + auto const& headerMsg = partial[idx][indices.headerIdx]; + auto const& payloadMsg = partial[idx][indices.payloadIdx]; + if (headerMsg) { + return DataRef{nullptr, + reinterpret_cast(headerMsg->GetData()), + payloadMsg ? reinterpret_cast(payloadMsg->GetData()) : nullptr, + payloadMsg ? payloadMsg->GetSize() : 0}; + } + } + return DataRef{}; + }; + auto nextIndicesGetter = [&partial](size_t idx, DataRefIndices current) -> DataRefIndices { + auto next = partial[idx] | get_next_pair{current}; + return next.headerIdx < partial[idx].size() ? next : DataRefIndices{size_t(-1), size_t(-1)}; + }; + InputSpan span{nPartsGetter, refCountGetter, indicesGetter, nextIndicesGetter, static_cast(partial.size())}; // Setup the input span if (expirator.checker(services, timestamp.value, span) == false) { @@ -789,18 +794,6 @@ void DataRelayer::getReadyToProcess(std::vector& comp throw runtime_error_f("Completion police %s has no callback set", mCompletionPolicy.name.c_str()); } auto partial = getPartialRecord(li); - // TODO: get the data ref from message model - auto getter = [&partial](size_t idx, size_t part) { - if (!partial[idx].empty() && (partial[idx] | get_header{part}).get()) { - auto header = (partial[idx] | get_header{part}).get(); - auto payload = (partial[idx] | get_payload{part, 0}).get(); - return DataRef{nullptr, - reinterpret_cast(header->GetData()), - reinterpret_cast(payload ? payload->GetData() : nullptr), - payload ? payload->GetSize() : 0}; - } - return DataRef{}; - }; auto nPartsGetter = [&partial](size_t idx) { return partial[idx] | count_parts{}; }; @@ -808,7 +801,24 @@ void DataRelayer::getReadyToProcess(std::vector& comp auto& header = static_cast(*(partial[idx] | get_header{0})); return header.GetRefCount(); }; - InputSpan span{getter, nPartsGetter, refCountGetter, static_cast(partial.size())}; + auto indicesGetter = [&partial](size_t idx, DataRefIndices indices) -> DataRef { + if (!partial[idx].empty()) { + auto const& headerMsg = partial[idx][indices.headerIdx]; + auto const& payloadMsg = partial[idx][indices.payloadIdx]; + if (headerMsg) { + return DataRef{nullptr, + reinterpret_cast(headerMsg->GetData()), + payloadMsg ? reinterpret_cast(payloadMsg->GetData()) : nullptr, + payloadMsg ? payloadMsg->GetSize() : 0}; + } + } + return DataRef{}; + }; + auto nextIndicesGetter = [&partial](size_t idx, DataRefIndices current) -> DataRefIndices { + auto next = partial[idx] | get_next_pair{current}; + return next.headerIdx < partial[idx].size() ? next : DataRefIndices{size_t(-1), size_t(-1)}; + }; + InputSpan span{nPartsGetter, refCountGetter, indicesGetter, nextIndicesGetter, static_cast(partial.size())}; CompletionPolicy::CompletionOp action = mCompletionPolicy.callbackFull(span, mInputs, mContext); auto& variables = mTimesliceIndex.getVariablesForSlot(slot); diff --git a/Framework/Core/src/InputRecord.cxx b/Framework/Core/src/InputRecord.cxx index 18b341704ffcb..7bc9907b13ba4 100644 --- a/Framework/Core/src/InputRecord.cxx +++ b/Framework/Core/src/InputRecord.cxx @@ -139,6 +139,16 @@ size_t InputRecord::getNofParts(int pos) const } return mSpan.getNofParts(pos); } + +DataRef InputRecord::getAtIndices(int pos, DataRefIndices indices) const +{ + auto ref = mSpan.getAtIndices(pos, indices); + if (pos >= 0 && pos < (int)mInputsSchema.size()) { + ref.spec = &mInputsSchema[pos].matcher; + } + return ref; +} + size_t InputRecord::size() const { return mSpan.size(); diff --git a/Framework/Core/src/InputSpan.cxx b/Framework/Core/src/InputSpan.cxx index d1dffc85602a5..ccea2d1dd66ed 100644 --- a/Framework/Core/src/InputSpan.cxx +++ b/Framework/Core/src/InputSpan.cxx @@ -11,29 +11,17 @@ #include "Framework/InputSpan.h" -template class std::function; -template class std::function; +template class std::function; +template class std::function; namespace o2::framework { -InputSpan::InputSpan(std::function getter, size_t size) - : mGetter{}, mNofPartsGetter{}, mSize{size} -{ - mGetter = [getter](size_t index, size_t) -> DataRef { - return getter(index); - }; -} - -InputSpan::InputSpan(std::function getter, size_t size) - : mGetter{getter}, mNofPartsGetter{}, mSize{size} -{ -} - -InputSpan::InputSpan(std::function getter, - std::function nofPartsGetter, +InputSpan::InputSpan(std::function nofPartsGetter, std::function refCountGetter, + std::function indicesGetter, + std::function nextIndicesGetter, size_t size) - : mGetter{getter}, mNofPartsGetter{nofPartsGetter}, mRefCountGetter(refCountGetter), mSize{size} + : mNofPartsGetter{nofPartsGetter}, mRefCountGetter(refCountGetter), mIndicesGetter{std::move(indicesGetter)}, mNextIndicesGetter{std::move(nextIndicesGetter)}, mSize{size} { } diff --git a/Framework/Core/test/benchmark_InputRecord.cxx b/Framework/Core/test/benchmark_InputRecord.cxx index 69fc3c970c1e1..e3ec00ac815ed 100644 --- a/Framework/Core/test/benchmark_InputRecord.cxx +++ b/Framework/Core/test/benchmark_InputRecord.cxx @@ -47,7 +47,12 @@ static void BM_InputRecordGenericGetters(benchmark::State& state) createRoute("z_source", spec3)}; // First of all we test if an empty registry behaves as expected, raising a // bunch of exceptions. - InputSpan span{[](size_t) { return DataRef{nullptr, nullptr, nullptr}; }, 0}; + InputSpan span{ + [](size_t) -> size_t { return 0; }, + nullptr, + [](size_t, DataRefIndices) { return DataRef{nullptr, nullptr, nullptr}; }, + [](size_t, DataRefIndices) -> DataRefIndices { return {size_t(-1), size_t(-1)}; }, + 0}; ServiceRegistry registry; InputRecord emptyRecord(schema, span, registry); @@ -82,7 +87,12 @@ static void BM_InputRecordGenericGetters(benchmark::State& state) createMessage(dh1, 1); createMessage(dh2, 2); createEmpty(); - InputSpan span2{[&inputs](size_t i) { return DataRef{nullptr, static_cast(inputs[2 * i]), static_cast(inputs[2 * i + 1])}; }, inputs.size() / 2}; + InputSpan span2{ + [](size_t) -> size_t { return 1; }, + nullptr, + [&inputs](size_t i, DataRefIndices idx) { return DataRef{nullptr, static_cast(inputs[2 * i + idx.headerIdx]), static_cast(inputs[2 * i + idx.payloadIdx])}; }, + [](size_t, DataRefIndices) -> DataRefIndices { return {size_t(-1), size_t(-1)}; }, + inputs.size() / 2}; InputRecord record{schema, span2, registry}; for (auto _ : state) { diff --git a/Framework/Core/test/test_CompletionPolicy.cxx b/Framework/Core/test/test_CompletionPolicy.cxx index 059f20b352b3d..cc16ba95ba8f2 100644 --- a/Framework/Core/test/test_CompletionPolicy.cxx +++ b/Framework/Core/test/test_CompletionPolicy.cxx @@ -55,7 +55,12 @@ TEST_CASE("TestCompletionPolicy_callback") std::vector policies{ {"test", matcher, callback}}; CompletionPolicy::InputSetElement ref{nullptr, reinterpret_cast(stack.data()), nullptr}; - InputSpan const& inputs{[&ref](size_t) { return ref; }, 1}; + InputSpan const inputs{ + [](size_t) -> size_t { return 1; }, + nullptr, + [&ref](size_t, DataRefIndices) -> DataRef { return ref; }, + [](size_t, DataRefIndices) -> DataRefIndices { return {size_t(-1), size_t(-1)}; }, + 1}; std::vector specs; ServiceRegistryRef servicesRef{services}; for (auto& policy : policies) { diff --git a/Framework/Core/test/test_InputRecord.cxx b/Framework/Core/test/test_InputRecord.cxx index 4eb1265dcff53..355e52539ea5a 100644 --- a/Framework/Core/test/test_InputRecord.cxx +++ b/Framework/Core/test/test_InputRecord.cxx @@ -47,7 +47,10 @@ TEST_CASE("TestInputRecord") // First of all we test if an empty registry behaves as expected, raising a // bunch of exceptions. InputSpan span{ - [](size_t) { return DataRef{nullptr, nullptr, nullptr}; }, + [](size_t) -> size_t { return 0; }, + nullptr, + [](size_t, DataRefIndices) { return DataRef{nullptr, nullptr, nullptr}; }, + [](size_t, DataRefIndices) -> DataRefIndices { return {size_t(-1), size_t(-1)}; }, 0}; ServiceRegistry registry; InputRecord emptyRecord(schema, span, registry); @@ -91,7 +94,12 @@ TEST_CASE("TestInputRecord") createMessage(dh1, 1); createMessage(dh2, 2); createEmpty(); - InputSpan span2{[&inputs](size_t i) { return DataRef{nullptr, static_cast(inputs[2 * i]), static_cast(inputs[2 * i + 1])}; }, inputs.size() / 2}; + InputSpan span2{ + [](size_t) -> size_t { return 1; }, + nullptr, + [&inputs](size_t i, DataRefIndices idx) { return DataRef{nullptr, static_cast(inputs[2 * i + idx.headerIdx]), static_cast(inputs[2 * i + idx.payloadIdx])}; }, + [](size_t, DataRefIndices) -> DataRefIndices { return {size_t(-1), size_t(-1)}; }, + inputs.size() / 2}; InputRecord record{schema, span2, registry}; // Checking we can get the whole ref by name diff --git a/Framework/Core/test/test_InputRecordWalker.cxx b/Framework/Core/test/test_InputRecordWalker.cxx index 9af3c0dd2dbe2..1fcfea1ba1587 100644 --- a/Framework/Core/test/test_InputRecordWalker.cxx +++ b/Framework/Core/test/test_InputRecordWalker.cxx @@ -35,16 +35,12 @@ struct DataSet { using Messages = std::vector; using CheckType = std::vector; DataSet(std::vector&& s, Messages&& m, CheckType&& v, ServiceRegistryRef registry) - : schema{std::move(s)}, messages{std::move(m)}, span{[this](size_t i, size_t part) { - REQUIRE(i < this->messages.size()); - REQUIRE(part < this->messages[i].second.size() / 2); - auto header = static_cast(this->messages[i].second.at(2 * part)->data()); - auto payload = static_cast(this->messages[i].second.at(2 * part + 1)->data()); - return DataRef{nullptr, header, payload}; - }, - [this](size_t i) { return i < this->messages.size() ? messages[i].second.size() / 2 : 0; }, nullptr, this->messages.size()}, - record{schema, span, registry}, - values{std::move(v)} + : schema{std::move(s)}, messages{std::move(m)}, span{[this](size_t i) { return i < this->messages.size() ? messages[i].second.size() / 2 : 0; }, nullptr, [this](size_t i, DataRefIndices idx) { + auto header = static_cast(this->messages[i].second.at(idx.headerIdx)->data()); + auto payload = static_cast(this->messages[i].second.at(idx.payloadIdx)->data()); + return DataRef{nullptr, header, payload}; }, [this](size_t i, DataRefIndices current) -> DataRefIndices { + size_t next = current.headerIdx + 2; + return next < this->messages[i].second.size() ? DataRefIndices{next, next + 1} : DataRefIndices{size_t(-1), size_t(-1)}; }, this->messages.size()}, record{schema, span, registry}, values{std::move(v)} { REQUIRE(messages.size() == schema.size()); } diff --git a/Framework/Core/test/test_InputSpan.cxx b/Framework/Core/test/test_InputSpan.cxx index c5682aea80b6c..dc31085e741fd 100644 --- a/Framework/Core/test/test_InputSpan.cxx +++ b/Framework/Core/test/test_InputSpan.cxx @@ -30,14 +30,18 @@ TEST_CASE("TestInputSpan") routeNo++; } - auto getter = [&inputs](size_t i, size_t part) { - return DataRef{nullptr, inputs[i].at(part * 2).data(), inputs[i].at(part * 2 + 1).data()}; - }; auto nPartsGetter = [&inputs](size_t i) { return inputs[i].size() / 2; }; + auto indicesGetter = [&inputs](size_t i, DataRefIndices indices) { + return DataRef{nullptr, inputs[i].at(indices.headerIdx).data(), inputs[i].at(indices.payloadIdx).data()}; + }; + auto nextIndicesGetter = [&inputs](size_t i, DataRefIndices current) -> DataRefIndices { + size_t next = current.headerIdx + 2; + return next < inputs[i].size() ? DataRefIndices{next, next + 1} : DataRefIndices{size_t(-1), size_t(-1)}; + }; - InputSpan span{getter, nPartsGetter, nullptr, inputs.size()}; + InputSpan span{nPartsGetter, nullptr, indicesGetter, nextIndicesGetter, inputs.size()}; REQUIRE(span.size() == inputs.size()); routeNo = 0; for (; routeNo < span.size(); ++routeNo) { diff --git a/Framework/Utils/test/RawPageTestData.h b/Framework/Utils/test/RawPageTestData.h index a6b800f7cba32..29ac4eeba6b5b 100644 --- a/Framework/Utils/test/RawPageTestData.h +++ b/Framework/Utils/test/RawPageTestData.h @@ -42,13 +42,17 @@ struct DataSet { DataSet(std::vector&& s, Messages&& m, std::vector&& v, ServiceRegistryRef registry) : schema{std::move(s)}, messages{std::move(m)}, - span{[this](size_t i, size_t part) { - auto header = static_cast(this->messages[i].at(2 * part)->data()); - auto payload = static_cast(this->messages[i].at(2 * part + 1)->data()); + span{[this](size_t i) { return i < this->messages.size() ? messages[i].size() / 2 : 0; }, + nullptr, + [this](size_t i, DataRefIndices idx) { + auto header = static_cast(this->messages[i].at(idx.headerIdx)->data()); + auto payload = static_cast(this->messages[i].at(idx.payloadIdx)->data()); return DataRef{nullptr, header, payload}; }, - [this](size_t i) { return i < this->messages.size() ? messages[i].size() / 2 : 0; }, - nullptr, + [this](size_t i, DataRefIndices current) -> DataRefIndices { + size_t next = current.headerIdx + 2; + return next < this->messages[i].size() ? DataRefIndices{next, next + 1} : DataRefIndices{size_t(-1), size_t(-1)}; + }, this->messages.size()}, record{schema, span, registry}, values{std::move(v)} diff --git a/Framework/Utils/test/test_RootTreeWriter.cxx b/Framework/Utils/test/test_RootTreeWriter.cxx index 62e1eb62cb4f1..e372fb4e1302e 100644 --- a/Framework/Utils/test/test_RootTreeWriter.cxx +++ b/Framework/Utils/test/test_RootTreeWriter.cxx @@ -224,10 +224,14 @@ TEST_CASE("test_RootTreeWriter") {InputSpec{"input8", "TST", "SRLZDVEC"}, 7, "input8", 0}, // }; - auto getter = [&store](size_t i) -> DataRef { - return DataRef{nullptr, static_cast(store[2 * i]->GetData()), static_cast(store[2 * i + 1]->GetData())}; - }; - InputSpan span{getter, store.size() / 2}; + InputSpan span{ + [](size_t) -> size_t { return 1; }, + nullptr, + [&store](size_t i, DataRefIndices idx) -> DataRef { + return DataRef{nullptr, static_cast(store[2 * i + idx.headerIdx]->GetData()), static_cast(store[2 * i + idx.payloadIdx]->GetData())}; + }, + [](size_t, DataRefIndices) -> DataRefIndices { return {size_t(-1), size_t(-1)}; }, + store.size() / 2}; ServiceRegistry registry; InputRecord inputs{ schema, diff --git a/Utilities/DataSampling/src/Dispatcher.cxx b/Utilities/DataSampling/src/Dispatcher.cxx index 3ff0ba661fd93..22dd457a1211a 100644 --- a/Utilities/DataSampling/src/Dispatcher.cxx +++ b/Utilities/DataSampling/src/Dispatcher.cxx @@ -122,7 +122,7 @@ void Dispatcher::run(ProcessingContext& ctx) for (auto inputIt = ctx.inputs().begin(); inputIt != ctx.inputs().end(); inputIt++) { - const DataRef& firstPart = inputIt.getByPos(0); + const DataRef& firstPart = *inputIt; if (firstPart.header == nullptr) { continue; } From 3df669bc765bdca795e17fa23081633d0d146173 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Wed, 8 Apr 2026 10:10:25 +0200 Subject: [PATCH 466/701] [ALICE3] TRK: Add TrackerACTS header for ACTS-based tracking (#15263) * Add ACTS support for tracking in TrackerSpec --- .../ALICE3/TRK/reconstruction/CMakeLists.txt | 4 +- .../include/TRKReconstruction/TrackerACTS.h | 189 +++++++++++ .../TRK/reconstruction/src/TrackerACTS.cxx | 306 ++++++++++++++++++ .../ALICE3/TRK/workflow/src/TrackerSpec.cxx | 30 +- 4 files changed, 524 insertions(+), 5 deletions(-) create mode 100644 Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TrackerACTS.h create mode 100644 Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt index 81a75e209124a..59a7f47955938 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt @@ -18,6 +18,7 @@ o2_add_library(TRKReconstruction SOURCES src/TimeFrame.cxx src/Clusterer.cxx $<$:src/ClustererACTS.cxx> + $<$:src/TrackerACTS.cxx> PUBLIC_LINK_LIBRARIES O2::ITStracking O2::GPUCommon @@ -45,7 +46,8 @@ set(dictHeaders include/TRKReconstruction/TimeFrame.h include/TRKReconstruction/Clusterer.h) if(Acts_FOUND) - list(APPEND dictHeaders include/TRKReconstruction/ClustererACTS.h) + list(APPEND dictHeaders include/TRKReconstruction/ClustererACTS.h + include/TRKReconstruction/TrackerACTS.h) endif() o2_target_root_dictionary(TRKReconstruction diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TrackerACTS.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TrackerACTS.h new file mode 100644 index 0000000000000..2910abf480961 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TrackerACTS.h @@ -0,0 +1,189 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file TrackerACTS.h +/// \brief TRK tracker using ACTS seeding algorithm +/// \author Nicolò Jacazio, Università del Piemonte Orientale (IT) +/// \since 2026-04-01 +/// + +#ifndef ALICE3_INCLUDE_TRACKERACTS_H_ +#define ALICE3_INCLUDE_TRACKERACTS_H_ + +#include "Acts/Definitions/Units.hpp" +#include "Framework/Logger.h" + +#include "ITStracking/TimeFrame.h" +#include "TH2F.h" + +namespace o2::trk +{ + +/// Configuration for the ACTS-based tracker +struct TrackerACTSConfig { + // Seeding parameters + float minPt = 0.4f * Acts::UnitConstants::GeV; ///< Minimum pT for seeds + float maxImpactParameter = 10.f * Acts::UnitConstants::mm; ///< Maximum impact parameter + float cotThetaMax = std::sinh(4.0f); ///< Maximum cot(theta), corresponds to eta ~4 + + // Delta R cuts for doublet/triplet formation + float deltaRMinBottom = 5.f * Acts::UnitConstants::mm; ///< Min deltaR for bottom-middle + float deltaRMaxBottom = 200.f * Acts::UnitConstants::mm; ///< Max deltaR for bottom-middle + float deltaRMinTop = 5.f * Acts::UnitConstants::mm; ///< Min deltaR for middle-top + float deltaRMaxTop = 200.f * Acts::UnitConstants::mm; ///< Max deltaR for middle-top + + // Z cuts + float zMin = -3000.f * Acts::UnitConstants::mm; + float zMax = 3000.f * Acts::UnitConstants::mm; + + // Collision region + float collisionRegionMin = -150.f * Acts::UnitConstants::mm; + float collisionRegionMax = 150.f * Acts::UnitConstants::mm; + + // Quality cuts + float maxSeedsPerMiddleSP = 2; + float deltaPhiMax = 0.1f; ///< Maximum phi difference for doublets +}; + +/// Space point representation for tracking +struct SpacePoint { + float x{0.f}; + float y{0.f}; + float z{0.f}; + int layer{-1}; + int clusterId{-1}; + int rof{-1}; + + // Derived quantities + float r() const { return std::hypot(x, y); } + float radius() const { return r(); } // required by Acts::CylindricalGridElement concept + float phi() const { return std::atan2(y, x); } + + // Variance estimates (can be refined based on cluster properties) + float varianceR{0.01f}; // ~100 um resolution squared + float varianceZ{0.01f}; +}; + +/// Seed (triplet of space points) +struct SeedACTS { + const SpacePoint* bottom{nullptr}; + const SpacePoint* middle{nullptr}; + const SpacePoint* top{nullptr}; + float quality{0.f}; +}; + +/// TRK Tracker using ACTS algorithms for seeding and track finding +template +class TrackerACTS +{ + public: + TrackerACTS(); + ~TrackerACTS() + { + if (mHistSpacePoints) { + mHistSpacePoints->SaveAs("/tmp/mHistSpacePoints.C"); + delete mHistSpacePoints; + mHistSpacePoints = nullptr; + } + } + + /// Adopt a TimeFrame for processing + void adoptTimeFrame(o2::its::TimeFrame& tf); + + /// Main tracking entry point: convert clusters to tracks + void clustersToTracks(); + + /// Configuration + void setConfig(const TrackerACTSConfig& cfg) { mConfig = cfg; } + TrackerACTSConfig& getConfig() { return mConfig; } + const TrackerACTSConfig& getConfig() const { return mConfig; } + + /// Set the magnetic field strength + void setBz(float bz) { mBz = bz; } + + /// Get the magnetic field strength + float getBz() const { return mBz; } + + /// Print tracking summary + void printSummary() const; + + private: + TH2F* mHistSpacePoints = nullptr; + + /// Build space points from clusters in the TimeFrame + void buildSpacePoints(int rof); + + /// Create seeds (triplets) from space points using ACTS SeedFinder + void createSeeds(); + + /// Estimate track parameters from a seed using ACTS + bool estimateTrackParams(const SeedACTS& seed, o2::its::TrackITSExt& track) const; + + /// Run track finding from seeds + void findTracks(); + + /// Assign MC labels to tracks + void computeTracksMClabels(); + + /// Helper: time a task + template + float evaluateTask(Func&& task, std::string_view taskName); + + // Configuration + TrackerACTSConfig mConfig; + + // TimeFrame data + o2::its::TimeFrame* mTimeFrame = nullptr; + + // Space points built from clusters + std::vector mSpacePoints; + std::vector> mSpacePointsPerLayer; + + // Seeds + std::vector mSeeds; + + // Tracking state + float mBz{0.5f}; ///< Magnetic field in Tesla + unsigned int mTimeFrameCounter{0}; + double mTotalTime{0.}; + + // Tracking states for logging + enum State { + SpacePointBuilding = 0, + Seeding, + TrackFinding, + NStates, + }; + State mCurState{SpacePointBuilding}; + static constexpr std::array StateNames{ + "Space point building", + "ACTS seeding", + "Track finding"}; +}; + +template +template +float TrackerACTS::evaluateTask(Func&& task, std::string_view taskName) +{ + LOG(debug) << " + Starting " << taskName; + const auto start = std::chrono::high_resolution_clock::now(); + task(); + const auto end = std::chrono::high_resolution_clock::now(); + std::chrono::duration diff{end - start}; + + LOG(debug) << " - " << taskName << " completed in: " << std::fixed << std::setprecision(2) << diff.count() << " ms"; + return static_cast(diff.count()); +} + +} // namespace o2::trk + +#endif /* ALICE3_INCLUDE_TRACKERACTS_H_ */ diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx new file mode 100644 index 0000000000000..67dcfe25e33bb --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx @@ -0,0 +1,306 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// +/// \file TrackerACTS.cxx +/// \brief TRK tracker using ACTS seeding and track finding +/// \author Nicolò Jacazio, Università del Piemonte Orientale (IT) +/// \since 2026-04-01 +/// + +#include "TRKReconstruction/TrackerACTS.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +namespace o2::trk +{ + +template +TrackerACTS::TrackerACTS() +{ + // Initialize space points storage per layer + mSpacePointsPerLayer.resize(nLayers); + mHistSpacePoints = new TH2F("hSpacePoints", "Space points; x (cm); y (cm)", 200, -100, 100, 200, -100, 100); +} + +template +void TrackerACTS::adoptTimeFrame(o2::its::TimeFrame& tf) +{ + mTimeFrame = &tf; +} + +template +void TrackerACTS::buildSpacePoints(int rof) +{ + mSpacePoints.clear(); + for (auto& layerSPs : mSpacePointsPerLayer) { + layerSPs.clear(); + } + + // Get clusters from the TimeFrame and convert to space points + for (int layer = 0; layer < nLayers; ++layer) { + // For now we take unsorted clusters, as soon as the cluster trackin is in place we can piggy back on it and switch to the clusters + auto clusters = mTimeFrame->getUnsortedClusters()[layer]; + // Resize the clusters to the first 100 clusters for testing + // clusters = clusters.subspan(0, std::min(clusters.size(), 100)); + LOG(debug) << "ACTSTracker: got " << clusters.size() << " clusters"; + + for (size_t iCluster = 0; iCluster < clusters.size(); ++iCluster) { + const auto& cluster = clusters[iCluster]; + + SpacePoint sp; + // Check that these are in global coordinates + sp.x = cluster.xCoordinate * Acts::UnitConstants::cm; + sp.y = cluster.yCoordinate * Acts::UnitConstants::cm; + sp.z = cluster.zCoordinate * Acts::UnitConstants::cm; + + if (mHistSpacePoints) { + mHistSpacePoints->Fill(sp.x / Acts::UnitConstants::cm, sp.y / Acts::UnitConstants::cm); + } + sp.layer = layer; + sp.clusterId = static_cast(iCluster); + sp.rof = rof; + + // Position uncertainties (could be refined based on cluster properties) + sp.varianceR = 0.01f; // ~100 um resolution squared + sp.varianceZ = 0.01f; + + mSpacePoints.push_back(sp); + } + } + + // Build per-layer pointers for seeding + for (auto& sp : mSpacePoints) { + if (sp.layer >= 0 && sp.layer < nLayers) { + mSpacePointsPerLayer[sp.layer].push_back(&sp); + } + } +} + +template +void TrackerACTS::createSeeds() +{ + if (mSpacePoints.empty()) { + LOGF(info, "No space points available for seeding"); + return; + } + mSeeds.clear(); + + // Backend adaptor that exposes mSpacePoints to Acts::SpacePointContainer + struct SpacePointBackend { + using ValueType = SpacePoint; + explicit SpacePointBackend(const std::vector& sps) : m_sps{&sps} {} + std::size_t size_impl() const { return m_sps->size(); } + float x_impl(std::size_t i) const { return (*m_sps)[i].x; } + float y_impl(std::size_t i) const { return (*m_sps)[i].y; } + float z_impl(std::size_t i) const { return (*m_sps)[i].z; } + float varianceR_impl(std::size_t i) const { return (*m_sps)[i].varianceR; } + float varianceZ_impl(std::size_t i) const { return (*m_sps)[i].varianceZ; } + const SpacePoint& get_impl(std::size_t i) const { return (*m_sps)[i]; } + std::any component_impl(Acts::HashedString /*key*/, std::size_t /*i*/) const + { + LOG(fatal) << "No additional components available for space points"; + throw std::runtime_error("SpacePointBackend: no strip component available"); + } + const std::vector* m_sps; + }; + + // Wrap mSpacePoints in an Acts space-point container + SpacePointBackend backend{mSpacePoints}; + + // Configure the ACTS space point container + Acts::SpacePointContainerConfig spContainerConfig; + Acts::SpacePointContainerOptions spContainerOpts; + spContainerOpts.beamPos = {0.f, 0.f}; + Acts::SpacePointContainer spContainer{spContainerConfig, spContainerOpts, backend}; + + // Configure the ACTS seed finder + const unsigned int maxSeeds = static_cast(mConfig.maxSeedsPerMiddleSP); + Acts::SeedFilterConfig filterConfig; + filterConfig.maxSeedsPerSpM = maxSeeds; + + // ACTS requires minPt / bFieldInZ >= rMax / 2 (minHelixRadius >= rMax/2). + // Cap rMax so that the constraint is satisfied for the configured minPt and field. + const float bFieldInZ = mBz * Acts::UnitConstants::T; + const float safeRMax = 1.8f * mConfig.minPt / bFieldInZ; // 10% margin below the hard limit + + using SPProxy = typename Acts::SpacePointContainer::SpacePointProxyType; + Acts::SeedFinderConfig finderConfig; + finderConfig.rMin = 0.f; + finderConfig.rMax = 100.f * Acts::UnitConstants::cm; + finderConfig.zMin = mConfig.zMin; + finderConfig.zMax = mConfig.zMax; + finderConfig.deltaRMin = std::min(mConfig.deltaRMinBottom, mConfig.deltaRMinTop); + finderConfig.deltaRMax = std::max(mConfig.deltaRMaxBottom, mConfig.deltaRMaxTop); + finderConfig.deltaRMinBottomSP = mConfig.deltaRMinBottom; + finderConfig.deltaRMaxBottomSP = mConfig.deltaRMaxBottom; + finderConfig.deltaRMinTopSP = mConfig.deltaRMinTop; + finderConfig.deltaRMaxTopSP = mConfig.deltaRMaxTop; + finderConfig.collisionRegionMin = mConfig.collisionRegionMin; + finderConfig.collisionRegionMax = mConfig.collisionRegionMax; + finderConfig.cotThetaMax = mConfig.cotThetaMax; + finderConfig.minPt = mConfig.minPt; + finderConfig.impactMax = mConfig.maxImpactParameter; + finderConfig.maxSeedsPerSpM = maxSeeds; + finderConfig.sigmaScattering = 5.f; + finderConfig.radLengthPerSeed = 0.05f; + finderConfig.seedFilter = std::make_shared>(filterConfig); + finderConfig = finderConfig.calculateDerivedQuantities(); + Acts::SeedFinder> seedFinder{finderConfig, + Acts::getDefaultLogger("Finder", Acts::Logging::Level::VERBOSE)}; + + // Configure and create the cylindrical space-point grid + Acts::CylindricalSpacePointGridConfig gridConfig; + gridConfig.minPt = finderConfig.minPt; + gridConfig.rMin = finderConfig.rMin; + gridConfig.rMax = finderConfig.rMax; + gridConfig.zMin = finderConfig.zMin; + gridConfig.zMax = finderConfig.zMax; + gridConfig.deltaRMax = finderConfig.deltaRMax; + gridConfig.cotThetaMax = finderConfig.cotThetaMax; + gridConfig.impactMax = finderConfig.impactMax; + + Acts::CylindricalSpacePointGridOptions gridOpts; + gridOpts.bFieldInZ = bFieldInZ; + + Acts::SeedFinderOptions finderOpts; + finderOpts.beamPos = spContainerOpts.beamPos; + finderOpts.bFieldInZ = gridOpts.bFieldInZ; + try { + finderOpts = finderOpts.calculateDerivedQuantities(finderConfig); + } catch (const std::exception& e) { + LOG(fatal) << "Error in seed finder configuration: " << e.what(); + return; + } + + Acts::CylindricalSpacePointGrid grid = Acts::CylindricalSpacePointGridCreator::createGrid(gridConfig, gridOpts); + try { + Acts::CylindricalSpacePointGridCreator::fillGrid(finderConfig, finderOpts, grid, + spContainer.begin(), spContainer.end()); + } catch (const std::exception& e) { + LOG(fatal) << "Error during grid creation/filling: " << e.what(); + return; + } + LOG(debug) << "Grid created with " << grid.dimensions(); + + // Build the binned group and iterate over triplet combinations + Acts::GridBinFinder<3ul> bottomBinFinder{1, std::vector>{}, 0}; + Acts::GridBinFinder<3ul> topBinFinder{1, std::vector>{}, 0}; + Acts::CylindricalBinnedGroup spGroup{std::move(grid), bottomBinFinder, topBinFinder}; + + std::vector>> seedsPerGroup; + typename Acts::SeedFinder>::SeedingState seedingState; + seedingState.spacePointMutableData.resize(spContainer.size()); + const Acts::Range1D rMiddleSPRange; + for (auto [bottom, middle, top] : spGroup) { + auto& v = seedsPerGroup.emplace_back(); + try { + seedFinder.createSeedsForGroup(finderOpts, seedingState, spGroup.grid(), v, bottom, middle, top, rMiddleSPRange); + } catch (const std::exception& e) { + LOG(fatal) << "Error during seed finding for a group: " << e.what(); + return; + } + } + LOG(debug) << "Seed finding completed, found " << seedsPerGroup.size() << " groups with seeds"; + + // Convert Acts seeds to the internal SeedACTS representation + for (const auto& groupSeeds : seedsPerGroup) { + for (const auto& actsSeed : groupSeeds) { + SeedACTS seed; + seed.bottom = &actsSeed.sp()[0]->externalSpacePoint(); + seed.middle = &actsSeed.sp()[1]->externalSpacePoint(); + seed.top = &actsSeed.sp()[2]->externalSpacePoint(); + seed.quality = actsSeed.seedQuality(); + mSeeds.push_back(seed); + } + } + + LOGF(info, "Created %zu seeds from %zu space points", mSeeds.size(), mSpacePoints.size()); +} + +template +bool TrackerACTS::estimateTrackParams(const SeedACTS& seed, o2::its::TrackITSExt& track) const +{ + return true; +} + +template +void TrackerACTS::findTracks() +{ +} + +template +void TrackerACTS::computeTracksMClabels() +{ +} + +template +void TrackerACTS::clustersToTracks() +{ + if (!mTimeFrame) { + LOG(error) << "Cannot run TrackerACTS: No TimeFrame adopted"; + return; + } + + double totalTime = 0.; + LOG(info) << "==== TRK ACTS Tracking ===="; + LOG(info) << "Processing " << mTimeFrame->getNrof() << " ROFs with B = " << mBz << " T"; + + // Process each ROF + for (int iROF = 0; iROF < mTimeFrame->getNrof(); ++iROF) { + LOG(info) << "Processing ROF " << iROF; + // Build space points + mCurState = SpacePointBuilding; + totalTime += evaluateTask([this, iROF]() { buildSpacePoints(iROF); }, + StateNames[mCurState]); + + // Run seeding + mCurState = Seeding; + totalTime += evaluateTask([this]() { createSeeds(); }, + StateNames[mCurState]); + + // Find tracks + mCurState = TrackFinding; + totalTime += evaluateTask([this]() { findTracks(); }, + StateNames[mCurState]); + } + + // MC labeling + if (mTimeFrame->hasMCinformation()) { + computeTracksMClabels(); + } + + LOG(info) << "=== TimeFrame " << mTimeFrameCounter << " completed in: " << totalTime << " ms ==="; + + ++mTimeFrameCounter; + mTotalTime += totalTime; +} + +template +void TrackerACTS::printSummary() const +{ + float avgTF = mTimeFrameCounter > 0 ? static_cast(mTotalTime) / mTimeFrameCounter : 0.f; + LOGP(info, "TrackerACTS summary: Processed {} TFs in TOT={:.2f} ms, AVG/TF={:.2f} ms", + mTimeFrameCounter, mTotalTime, avgTF); +} + +// Explicit template instantiations +template class TrackerACTS<11>; +} // namespace o2::trk diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx index 20bd45557dac5..b587ec24775b4 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx @@ -29,6 +29,10 @@ #include "TRKWorkflow/TrackerSpec.h" #include +#ifdef O2_WITH_ACTS +#include "TRKReconstruction/TrackerACTS.h" +#endif + #include #include @@ -61,6 +65,10 @@ void TrackerDPL::init(InitContext& ic) // mITSTrackingInterface.setTraitsFromProvider(mChainITS->GetITSVertexerTraits(), // mChainITS->GetITSTrackerTraits(), // mChainITS->GetITSTimeframe()); + +#ifdef O2_WITH_ACTS + mUseACTS = ic.options().get("useACTS"); +#endif } void TrackerDPL::stop() @@ -276,14 +284,13 @@ void TrackerDPL::run(ProcessingContext& pc) 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(); + itsTracker.adoptTimeFrame(timeFrame); - int nRofs = timeFrame.loadROFsFromHitTree(hitsTree, gman, mHitRecoConfig); - + const 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 @@ -293,6 +300,16 @@ void TrackerDPL::run(ProcessingContext& pc) itsTrackerTraits.updateTrackingParameters(trackingParams); +#ifdef O2_WITH_ACTS + if (mUseACTS) { + LOG(info) << "Running the tracking with ACTS"; + o2::trk::TrackerACTS<11> actsTracker; + actsTracker.setBz(mHitRecoConfig["geometry"]["bz"].get()); + actsTracker.adoptTimeFrame(timeFrame); + actsTracker.clustersToTracks(); + } +#endif + const auto trackingLoopStart = std::chrono::steady_clock::now(); for (size_t iter{0}; iter < trackingParams.size(); ++iter) { LOGP(info, "{}", trackingParams[iter].asString()); @@ -391,7 +408,12 @@ DataProcessorSpec getTrackerSpec(bool useMC, const std::string& hitRecoConfig, o useMC, hitRecoConfig, dType)}, - Options{ConfigParamSpec{"max-loops", VariantType::Int, 1, {"max number of loops"}}}}; + Options{ConfigParamSpec{"max-loops", VariantType::Int, 1, {"max number of loops"}} +#ifdef O2_WITH_ACTS + , + {"useACTS", o2::framework::VariantType::Bool, false, {"Use ACTS for tracking"}} +#endif + }}; } inputs.emplace_back("dummy", "TRK", "DUMMY", 0, Lifetime::Timeframe); From 6593df3e4e809f9abca321c8f5720c53421530a2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Wed, 8 Apr 2026 19:31:57 +0200 Subject: [PATCH 467/701] [ALICE3] Fix missing bool in ACTS integration (#15264) - specify since for acts integrations --- .../reconstruction/include/TRKReconstruction/ClustererACTS.h | 4 ++++ .../Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx | 4 ++++ .../ALICE3/TRK/workflow/include/TRKWorkflow/ClustererSpec.h | 4 +--- .../ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h | 3 +++ 4 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/ClustererACTS.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/ClustererACTS.h index 4111737d17a9f..37a148aa78afb 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/ClustererACTS.h +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/ClustererACTS.h @@ -9,8 +9,12 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +/// /// \file ClustererACTS.h /// \brief Definition of the TRK cluster finder +/// \author Nicolò Jacazio, Università del Piemonte Orientale (IT) +/// \since 2026-03-01 +/// #ifndef ALICEO2_TRK_CLUSTERERACTS_H #define ALICEO2_TRK_CLUSTERERACTS_H diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx index 0cf7c26e0ea41..2dbf56ae610e3 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/ClustererACTS.cxx @@ -9,8 +9,12 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +/// /// \file ClustererACTS.cxx /// \brief Implementation of the TRK cluster finder with the ACTS +/// \author Nicolò Jacazio, Università del Piemonte Orientale (IT) +/// \since 2026-03-01 +/// #include "TRKReconstruction/ClustererACTS.h" #include "TRKBase/GeometryTGeo.h" diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClustererSpec.h b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClustererSpec.h index 9cfab104ecdf9..18cc6d245025a 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClustererSpec.h +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/ClustererSpec.h @@ -32,11 +32,9 @@ class ClustererDPL : public o2::framework::Task private: bool mUseMC = true; int mNThreads = 1; -#ifdef O2_WITH_ACTS - bool mUseACTS = false; -#endif o2::trk::Clusterer mClusterer; #ifdef O2_WITH_ACTS + bool mUseACTS = false; o2::trk::ClustererACTS mClustererACTS; #endif }; diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h index 33b25737bbc29..304b32041c2dc 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/include/TRKWorkflow/TrackerSpec.h @@ -58,6 +58,9 @@ class TrackerDPL : public framework::Task std::shared_ptr mTaskArena; nlohmann::json mHitRecoConfig; TStopwatch mTimer; +#ifdef O2_WITH_ACTS + bool mUseACTS = false; +#endif }; framework::DataProcessorSpec getTrackerSpec(bool useMC, const std::string& hitRecoConfig, gpu::gpudatatypes::DeviceType dType = gpu::gpudatatypes::DeviceType::CPU); From 9e8f22b292d5709d39b33310d779a990b31f2142 Mon Sep 17 00:00:00 2001 From: Francesco Noferini Date: Wed, 8 Apr 2026 18:54:21 +0200 Subject: [PATCH 468/701] fix wrong long_to_int converstion in TOF readout window indexing --- Detectors/TOF/base/include/TOFBase/WindowFiller.h | 2 +- Detectors/TOF/base/src/WindowFiller.cxx | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Detectors/TOF/base/include/TOFBase/WindowFiller.h b/Detectors/TOF/base/include/TOFBase/WindowFiller.h index 77827ee5e7057..5c9abca6da0ea 100644 --- a/Detectors/TOF/base/include/TOFBase/WindowFiller.h +++ b/Detectors/TOF/base/include/TOFBase/WindowFiller.h @@ -96,7 +96,7 @@ class WindowFiller } std::vector& getPatterns() { return mPatterns; } - void addPattern(const uint32_t val, int icrate, int orbit, int bc) { mCratePatterns.emplace_back(val, icrate, orbit * 3 + (bc + 100) / Geo::BC_IN_WINDOW); } + void addPattern(const uint32_t val, int icrate, int orbit, int bc) { mCratePatterns.emplace_back(val, icrate, ((unsigned long)orbit) * 3 + (bc + 100) / Geo::BC_IN_WINDOW); } void addCrateHeaderData(unsigned long orbit, int crate, int32_t bc, uint32_t eventCounter); Diagnostic& getDiagnosticFrequency() { return mDiagnosticFrequency; } diff --git a/Detectors/TOF/base/src/WindowFiller.cxx b/Detectors/TOF/base/src/WindowFiller.cxx index 0362222b55bf5..35ec27070bda1 100644 --- a/Detectors/TOF/base/src/WindowFiller.cxx +++ b/Detectors/TOF/base/src/WindowFiller.cxx @@ -194,9 +194,9 @@ void WindowFiller::fillOutputContainer(std::vector& digits) int npatterns = 0; // check if patterns are in the current row - unsigned int initrow = mFirstIR.orbit * Geo::NWINDOW_IN_ORBIT; + unsigned long initrow = ((unsigned long)mFirstIR.orbit) * Geo::NWINDOW_IN_ORBIT; for (std::vector::reverse_iterator it = mCratePatterns.rbegin(); it != mCratePatterns.rend(); ++it) { - unsigned int irow = it->row; + unsigned long irow = it->row; // printf("pattern row=%ld (%u - %u) current=%ld\n",irow - initrow,irow,initrow,mReadoutWindowCurrent); if (irow - initrow > mReadoutWindowCurrent) { From a1e83082354311a027b2807ece60d682055d8905 Mon Sep 17 00:00:00 2001 From: Gabriele Cimador Date: Wed, 18 Mar 2026 16:22:37 +0100 Subject: [PATCH 469/701] GPU Framework: fix detection for gfx90a GPU --- dependencies/FindO2GPU.cmake | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/dependencies/FindO2GPU.cmake b/dependencies/FindO2GPU.cmake index 3e8f012fea4b5..ad9cc11d56b40 100644 --- a/dependencies/FindO2GPU.cmake +++ b/dependencies/FindO2GPU.cmake @@ -10,7 +10,7 @@ # or submit itself to any jurisdiction. # NOTE!!!! - Whenever this file is changed, move it over to alidist/resources -# FindO2GPU.cmake Version 13 +# FindO2GPU.cmake Version 14 set(CUDA_COMPUTETARGET_DEFAULT_FULL 80-real 86-real 89-real 120-real 75-virtual) set(HIP_AMDGPUTARGET_DEFAULT_FULL gfx906;gfx908) @@ -66,7 +66,7 @@ function(detect_gpu_arch backend) # Detect GPU architecture, optionally filterri set(CUDA_TARGET TESLA) endif() - string(REGEX MATCH "^[ \t\r\n]*gfx[0-9]+" HIP_FIRST_TARGET "${HIP_AMDGPUTARGET}") + string(REGEX MATCH "^[ \t\r\n]*gfx[0-9a-fA-F]+" 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) From 97a77d9365664c504e4968132c087f89067452f5 Mon Sep 17 00:00:00 2001 From: Ernst Hellbar Date: Fri, 10 Apr 2026 17:22:07 +0200 Subject: [PATCH 470/701] DPL: use new indices methods to navigate through InputRecord in output-proxy --- Framework/Core/src/ExternalFairMQDeviceProxy.cxx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/Framework/Core/src/ExternalFairMQDeviceProxy.cxx b/Framework/Core/src/ExternalFairMQDeviceProxy.cxx index 5867f53af4bd2..d4ee776986184 100644 --- a/Framework/Core/src/ExternalFairMQDeviceProxy.cxx +++ b/Framework/Core/src/ExternalFairMQDeviceProxy.cxx @@ -1027,9 +1027,9 @@ DataProcessorSpec specifyFairMQDeviceOutputProxy(char const* name, callbacks.set(forwardEos); return adaptStateless([lastDataProcessingHeader](InputRecord& inputs) { - for (size_t ii = 0; ii != inputs.size(); ++ii) { - for (size_t pi = 0; pi < inputs.getNofParts(ii); ++pi) { - auto part = inputs.getByPos(ii, pi); + for (auto it = inputs.begin(); it != inputs.end(); it++) { + for (auto indices = it.initialIndices(); indices != it.endIndices(); indices = it.nextIndices(indices)) { + auto part = it.getAtIndices(indices); const auto* dph = o2::header::get(part.header); if (dph) { // FIXME: should we implement an assignment operator for DataProcessingHeader? @@ -1163,9 +1163,9 @@ DataProcessorSpec specifyFairMQDeviceMultiOutputProxy(char const* name, // there is nothing to do if the forwarding is handled on the framework level // as forward routes but we need to keep a copy of the last DataProcessingHeader // for sending the EOS - for (size_t ii = 0; ii != inputs.size(); ++ii) { - for (size_t pi = 0; pi < inputs.getNofParts(ii); ++pi) { - auto part = inputs.getByPos(ii, pi); + for (auto it = inputs.begin(); it != inputs.end(); it++) { + for (auto indices = it.initialIndices(); indices != it.endIndices(); indices = it.nextIndices(indices)) { + auto part = it.getAtIndices(indices); const auto* dph = o2::header::get(part.header); if (dph) { // FIXME: should we implement an assignment operator for DataProcessingHeader? From 85fad0700b377263cbadbf4144a734c2ab79f0e7 Mon Sep 17 00:00:00 2001 From: Maximiliano Puccio Date: Mon, 13 Apr 2026 14:02:25 +0200 Subject: [PATCH 471/701] [ALICE3] TRK: Introduce Almira params and shorten ROF/response for TRK (#15267) * Add Almira params * Use Almira params in the digitisation * Clip number of ROFs to the requested number of orbits * Add macro to check bandwidth for MLOT --- .../Upgrades/ALICE3/TRK/base/CMakeLists.txt | 6 +- .../TRK/base/include/TRKBase/AlmiraParam.h | 48 +++ .../ALICE3/TRK/base/src/AlmiraParam.cxx | 14 + .../ALICE3/TRK/base/src/TRKBaseLinkDef.h | 4 +- .../ALICE3/TRK/macros/test/CMakeLists.txt | 12 + .../ALICE3/TRK/macros/test/CheckBandwidth.C | 299 ++++++++++++++++++ .../include/TRKSimulation/DPLDigitizerParam.h | 6 +- .../include/TRKSimulation/DigiParams.h | 6 +- .../ALICE3/TRK/simulation/src/DigiParams.cxx | 4 +- .../ALICE3/TRK/simulation/src/Digitizer.cxx | 11 +- .../ALICE3/TRK/workflow/src/TrackerSpec.cxx | 2 +- .../src/TRKDigitizerSpec.cxx | 108 +++++-- 12 files changed, 479 insertions(+), 41 deletions(-) create mode 100644 Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/AlmiraParam.h create mode 100644 Detectors/Upgrades/ALICE3/TRK/base/src/AlmiraParam.cxx create mode 100644 Detectors/Upgrades/ALICE3/TRK/macros/test/CheckBandwidth.C diff --git a/Detectors/Upgrades/ALICE3/TRK/base/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/base/CMakeLists.txt index 96ebf4ead4b7b..89775e22ed8d0 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/base/CMakeLists.txt @@ -11,11 +11,13 @@ o2_add_library(TRKBase SOURCES src/GeometryTGeo.cxx + src/AlmiraParam.cxx src/TRKBaseParam.cxx src/SegmentationChip.cxx PUBLIC_LINK_LIBRARIES O2::DetectorsBase) o2_target_root_dictionary(TRKBase - HEADERS include/TRKBase/GeometryTGeo.h + HEADERS include/TRKBase/AlmiraParam.h + include/TRKBase/GeometryTGeo.h include/TRKBase/TRKBaseParam.h - include/TRKBase/SegmentationChip.h) \ No newline at end of file + include/TRKBase/SegmentationChip.h) diff --git a/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/AlmiraParam.h b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/AlmiraParam.h new file mode 100644 index 0000000000000..2048666e21c00 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/base/include/TRKBase/AlmiraParam.h @@ -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. + +#ifndef O2_TRK_ALMIRAPARAM_H +#define O2_TRK_ALMIRAPARAM_H + +#include "CommonConstants/LHCConstants.h" +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" + +namespace o2 +{ +namespace trk +{ +constexpr float DEFAlmiraStrobeDelay = 0.f; ///< default strobe delay in ns wrt ROF start, to be tuned with the real chip response + +struct AlmiraParam : public o2::conf::ConfigurableParamHelper { + int roFrameLengthInBC = o2::constants::lhc::LHCMaxBunches / 198; ///< ROF length in BC for continuous mode + float strobeDelay = DEFAlmiraStrobeDelay; ///< strobe start in ns wrt ROF start + float strobeLengthCont = -1.; ///< if < 0, full ROF length minus delay + int roFrameBiasInBC = 0; ///< ROF start bias in BC wrt orbit start + + O2ParamDef(AlmiraParam, "TRKAlmiraParam"); +}; + +} // namespace trk + +namespace framework +{ +template +struct is_messageable; + +template <> +struct is_messageable : std::true_type { +}; +} // namespace framework + +} // namespace o2 + +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/base/src/AlmiraParam.cxx b/Detectors/Upgrades/ALICE3/TRK/base/src/AlmiraParam.cxx new file mode 100644 index 0000000000000..572c902fb23f1 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/AlmiraParam.cxx @@ -0,0 +1,14 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "TRKBase/AlmiraParam.h" + +O2ParamImpl(o2::trk::AlmiraParam); diff --git a/Detectors/Upgrades/ALICE3/TRK/base/src/TRKBaseLinkDef.h b/Detectors/Upgrades/ALICE3/TRK/base/src/TRKBaseLinkDef.h index eee9a23eaf5e7..e36955cdd150d 100644 --- a/Detectors/Upgrades/ALICE3/TRK/base/src/TRKBaseLinkDef.h +++ b/Detectors/Upgrades/ALICE3/TRK/base/src/TRKBaseLinkDef.h @@ -15,10 +15,12 @@ #pragma link off all classes; #pragma link off all functions; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::trk::AlmiraParam> + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::trk::TRKBaseParam> + ; +#pragma link C++ class o2::trk::AlmiraParam + ; #pragma link C++ class o2::trk::GeometryTGeo + #pragma link C++ class o2::trk::TRKBaseParam + ; #pragma link C++ class o2::trk::SegmentationChip + ; -#endif \ No newline at end of file +#endif diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt index edd9c785d89ce..54e42c6857249 100644 --- a/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt @@ -9,6 +9,18 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. + +o2_add_test_root_macro(CheckBandwidth.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(CheckDigits.C PUBLIC_LINK_LIBRARIES O2::ITSMFTBase O2::ITSMFTSimulation diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckBandwidth.C b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckBandwidth.C new file mode 100644 index 0000000000000..2087f88a87d6b --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckBandwidth.C @@ -0,0 +1,299 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not 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 +#include +#include +#include +#include + +#include "TRKBase/GeometryTGeo.h" +#include "DataFormatsITSMFT/Digit.h" +#include "MathUtils/Utils.h" +#include "DetectorsBase/GeometryManager.h" + +#include "DataFormatsITSMFT/ROFRecord.h" +#include "CommonDataFormat/InteractionRecord.h" +#include "SimulationDataFormat/DigitizationContext.h" + +#endif + +namespace +{ +constexpr double DigitBits = 16.; +constexpr double BunchCrossingNS = 25.; +constexpr int ReadoutCycleBC = 18; +constexpr int ReadoutCycleSimBC = 18; +constexpr double ReadoutCycleSeconds = ReadoutCycleBC * BunchCrossingNS * 1.e-9; +} // namespace + +void CheckBandwidth(std::string digifile = "trkdigits.root", std::string inputGeom = "o2sim_geometry.root", std::string collContextFile = "collisioncontext.root") +{ + gStyle->SetPalette(55); + gStyle->SetOptStat(0); + + auto drawSummary = [](double averageValue, double peakValue, const char* unit) { + TLatex latex; + latex.SetNDC(); + latex.SetTextSize(0.03); + latex.SetTextAlign(13); + latex.DrawLatex(0.04, 0.05, Form("avg: %.3f %s", averageValue, unit)); + latex.DrawLatex(0.34, 0.05, Form("peak: %.3f %s", peakValue, unit)); + }; + + auto drawCollisionSummary = [](double averageValue, double nonEmptyAverageValue, double peakValue) { + TLatex latex; + latex.SetNDC(); + latex.SetTextSize(0.03); + latex.SetTextAlign(13); + latex.DrawLatex(0.04, 0.025, Form("avg: %.3f collisions/ROF", averageValue)); + latex.DrawLatex(0.42, 0.025, Form("peak: %.3f collisions/ROF", peakValue)); + latex.DrawLatex(0.04, 0.06, Form("avg non-empty: %.3f collisions/ROF", nonEmptyAverageValue)); + }; + + using namespace o2::base; + using namespace o2::trk; + + TFile* f = TFile::Open("CheckBandwidth.root", "recreate"); + + // Geometry + o2::base::GeometryManager::loadGeometry(inputGeom); + auto* gman = o2::trk::GeometryTGeo::Instance(); + gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); + + // Collision Context + TFile* ccFile = TFile::Open(collContextFile.data()); + auto* digiContext = (o2::steer::DigitizationContext*)ccFile->Get("DigitizationContext"); + const o2::InteractionRecord firstSampledIR{0, digiContext->getFirstOrbitForSampling()}; + std::vector collisionsPerROF; + + for (const auto& record : digiContext->getEventRecords()) { + auto nbc = record.differenceInBC(firstSampledIR); + if (record.getTimeOffsetWrtBC() < 0. && nbc > 0) { + --nbc; + } + if (nbc < 0) { + continue; + } + + const size_t rofID = nbc / ReadoutCycleSimBC; + if (rofID >= collisionsPerROF.size()) { + collisionsPerROF.resize(rofID + 1, 0u); + } + ++collisionsPerROF[rofID]; + } + + // Digits + TFile* digFile = TFile::Open(digifile.data()); + TTree* digTree = (TTree*)digFile->Get("o2sim"); + const int nDigitTreeEntries = digTree->GetEntries(); + + std::vector* digArr = nullptr; + digTree->SetBranchAddress("TRKDigit", &digArr); + + // Get Read Out Frame arrays + std::vector* ROFRecordArrray = nullptr; + digTree->SetBranchAddress("TRKDigitROF", &ROFRecordArrray); + std::vector& ROFRecordArrrayRef = *ROFRecordArrray; + + digTree->GetEntry(0); + + if (nDigitTreeEntries > 1) { + LOG(warning) << "Digit tree has " << nDigitTreeEntries << " entries, but this macro processes entry 0 only."; + } + + std::vector digitsPerChip(gman->getNumberOfChips(), 0ull); + std::vector maxDigitsPerROFPerChip(gman->getNumberOfChips(), 0u); + std::vector digitsInCurrentROFPerChip(gman->getNumberOfChips(), 0u); + + const int nROFRec = (int)ROFRecordArrrayRef.size(); + const int nCollisionROFBins = std::max(nROFRec, static_cast(collisionsPerROF.size())); + + if (nCollisionROFBins > 0) { + auto* hCollisionsPerROF = new TH1D("h_collisions_per_rof", "Collisions per ROF;ROF id;N collisions", nCollisionROFBins, -0.5, nCollisionROFBins - 0.5); + double totalCollisionsPerROF = 0.; + double peakCollisionsPerROF = 0.; + int nNonEmptyROFs = 0; + + for (int rofID = 0; rofID < nCollisionROFBins; ++rofID) { + const double nCollisions = rofID < static_cast(collisionsPerROF.size()) ? collisionsPerROF[rofID] : 0.; + hCollisionsPerROF->SetBinContent(rofID + 1, nCollisions); + totalCollisionsPerROF += nCollisions; + peakCollisionsPerROF = std::max(peakCollisionsPerROF, nCollisions); + if (nCollisions > 0.) { + ++nNonEmptyROFs; + } + } + + auto* canvCollisionsPerROF = new TCanvas("canvCollisionsPerROF", "Collisions per ROF", 1050, 1050); + canvCollisionsPerROF->SetTopMargin(0.08); + hCollisionsPerROF->Draw("hist"); + drawCollisionSummary(totalCollisionsPerROF / nCollisionROFBins, + nNonEmptyROFs > 0 ? totalCollisionsPerROF / nNonEmptyROFs : 0., + peakCollisionsPerROF); + canvCollisionsPerROF->SaveAs("trk_collisions_per_rof.png"); + } + + unsigned int rofIndex = 0; + unsigned int rofNEntries = 0; + + // LOOP on : ROFRecord array + for (unsigned int iROF = 0; iROF < ROFRecordArrrayRef.size(); iROF++) { + std::vector touchedChips; + + rofIndex = ROFRecordArrrayRef[iROF].getFirstEntry(); + rofNEntries = ROFRecordArrrayRef[iROF].getNEntries(); + + // LOOP on : digits array + for (unsigned int iDigit = rofIndex; iDigit < rofIndex + rofNEntries; iDigit++) { + if (iDigit % 1000 == 0) + std::cout << "Reading digit " << iDigit << " / " << digArr->size() << std::endl; + + Int_t iDetID = (*digArr)[iDigit].getChipIndex(); + Int_t disk = gman->getDisk(iDetID); + Int_t subDetID = gman->getSubDetID(iDetID); + + if (subDetID == 1 && disk == -1) { + if (digitsInCurrentROFPerChip[iDetID] == 0) { + touchedChips.push_back(iDetID); + } + digitsPerChip[iDetID]++; + ++digitsInCurrentROFPerChip[iDetID]; + } + + } // end loop on digits array + + for (const auto chipID : touchedChips) { + maxDigitsPerROFPerChip[chipID] = std::max(maxDigitsPerROFPerChip[chipID], digitsInCurrentROFPerChip[chipID]); + digitsInCurrentROFPerChip[chipID] = 0; + } + + } // end loop on ROFRecords array + + const double rofNorm = nROFRec > 0 ? 1. / nROFRec : 0.; + const double bitsToMbps = ReadoutCycleSeconds > 0. ? DigitBits / ReadoutCycleSeconds / 1.e6 : 0.; + const int nMLOTLayers = gman->getNumberOfLayersMLOT(); + + for (int layer = 0; layer < nMLOTLayers; ++layer) { + int nStaves = gman->extractNumberOfStavesMLOT(layer); + std::map>> chipsPerStave; + std::vector sensorIdPerChip(gman->getNumberOfChips(), -1); + int maxSensorsPerStave = 0; + + for (int chipID = 0; chipID < gman->getNumberOfChips(); ++chipID) { + if (gman->getSubDetID(chipID) != 1 || gman->getLayer(chipID) != layer) { + continue; + } + const int staveID = gman->getStave(chipID); + const auto sensorCenter = gman->getMatrixL2G(chipID)(o2::math_utils::Point3D(0.f, 0.f, 0.f)); + chipsPerStave[staveID].push_back({sensorCenter.Z(), chipID}); + } + + for (auto& [staveID, chips] : chipsPerStave) { + std::sort(chips.begin(), chips.end(), [](const auto& left, const auto& right) { + if (std::abs(left.first - right.first) > 1.e-4) { + return left.first < right.first; + } + return left.second < right.second; + }); + + for (size_t sensorIndex = 0; sensorIndex < chips.size(); ++sensorIndex) { + sensorIdPerChip[chips[sensorIndex].second] = sensorIndex; + } + + maxSensorsPerStave = std::max(maxSensorsPerStave, static_cast(chips.size())); + } + + if (maxSensorsPerStave == 0) { + continue; + } + + auto* hDigitsPerROF = new TH2F(Form("h_digits_per_rof_layer%d", layer), + Form("Layer %d average digits per ROF;stave id;sensor id in stave;digits / ROF", layer), + nStaves, -0.5, nStaves - 0.5, maxSensorsPerStave, -0.5, maxSensorsPerStave - 0.5); + auto* hMaxDigitsPerROF = new TH2F(Form("h_max_digits_per_rof_layer%d", layer), + Form("Layer %d max digits in one ROF;stave id;sensor id in stave;max digits / ROF", layer), + nStaves, -0.5, nStaves - 0.5, maxSensorsPerStave, -0.5, maxSensorsPerStave - 0.5); + auto* hBandwidth = new TH2F(Form("h_bandwidth_layer%d", layer), + Form("Layer %d bandwidth map;stave id;sensor id in stave;bandwidth (Mbit/s)", layer), + nStaves, -0.5, nStaves - 0.5, maxSensorsPerStave, -0.5, maxSensorsPerStave - 0.5); + double totalAvgDigitsPerROF = 0.; + double totalMaxDigitsPerROF = 0.; + double totalBandwidthMbps = 0.; + double peakAvgDigitsPerROF = 0.; + double peakMaxDigitsPerROF = 0.; + double peakBandwidthMbps = 0.; + int nFilledSensors = 0; + + for (int chipID = 0; chipID < gman->getNumberOfChips(); ++chipID) { + if (gman->getSubDetID(chipID) != 1 || gman->getLayer(chipID) != layer) { + continue; + } + + const int staveID = gman->getStave(chipID); + const int sensorID = sensorIdPerChip[chipID]; + const double avgDigitsPerROF = digitsPerChip[chipID] * rofNorm; + const double maxDigitsPerROF = maxDigitsPerROFPerChip[chipID]; + const double bandwidthMbps = avgDigitsPerROF * bitsToMbps; + + if (sensorID >= 0) { + hDigitsPerROF->Fill(staveID, sensorID, avgDigitsPerROF); + hMaxDigitsPerROF->Fill(staveID, sensorID, maxDigitsPerROF); + hBandwidth->Fill(staveID, sensorID, bandwidthMbps); + totalAvgDigitsPerROF += avgDigitsPerROF; + totalMaxDigitsPerROF += maxDigitsPerROF; + totalBandwidthMbps += bandwidthMbps; + peakAvgDigitsPerROF = std::max(peakAvgDigitsPerROF, avgDigitsPerROF); + peakMaxDigitsPerROF = std::max(peakMaxDigitsPerROF, maxDigitsPerROF); + peakBandwidthMbps = std::max(peakBandwidthMbps, bandwidthMbps); + ++nFilledSensors; + } + } + + auto* canvLayer = new TCanvas(Form("canvBandwidthLayer%d", layer), Form("Layer %d bandwidth", layer), 1050, 1050); + canvLayer->SetTopMargin(0.08); + canvLayer->SetRightMargin(0.18); + const double avgDigitsPerROFLayer = nFilledSensors > 0 ? totalAvgDigitsPerROF / nFilledSensors : 0.; + const double avgMaxDigitsPerROFLayer = nFilledSensors > 0 ? totalMaxDigitsPerROF / nFilledSensors : 0.; + const double avgBandwidthMbps = nFilledSensors > 0 ? totalBandwidthMbps / nFilledSensors : 0.; + hBandwidth->GetZaxis()->SetRangeUser(0., avgBandwidthMbps > 0. ? 3. * avgBandwidthMbps : 1.); + hBandwidth->Draw("colz"); + drawSummary(avgBandwidthMbps, peakBandwidthMbps, "Mbit/s"); + canvLayer->SaveAs(Form("trk_layer%d_bandwidth_map.png", layer)); + + auto* canvLayerDigits = new TCanvas(Form("canvDigitsLayer%d", layer), Form("Layer %d digits per ROF", layer), 1050, 1050); + canvLayerDigits->SetTopMargin(0.08); + canvLayerDigits->SetRightMargin(0.18); + hDigitsPerROF->Draw("colz"); + drawSummary(avgDigitsPerROFLayer, peakAvgDigitsPerROF, "digits/ROF"); + canvLayerDigits->SaveAs(Form("trk_layer%d_digits_per_rof_map.png", layer)); + + auto* canvLayerMaxDigits = new TCanvas(Form("canvMaxDigitsLayer%d", layer), Form("Layer %d max digits per ROF", layer), 1050, 1050); + canvLayerMaxDigits->SetTopMargin(0.08); + canvLayerMaxDigits->SetRightMargin(0.18); + hMaxDigitsPerROF->Draw("colz"); + drawSummary(avgMaxDigitsPerROFLayer, peakMaxDigitsPerROF, "digits/ROF"); + canvLayerMaxDigits->SaveAs(Form("trk_layer%d_max_digits_per_rof_map.png", layer)); + } + + f->Write(); + f->Close(); +} diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DPLDigitizerParam.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DPLDigitizerParam.h index 168ae172f4b86..de839b27aefee 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DPLDigitizerParam.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/DPLDigitizerParam.h @@ -32,9 +32,9 @@ struct DPLDigitizerParam : public o2::conf::ConfigurableParamHelper -infTime; } - const o2::trk::ChipSimResponse* getAlpSimResponse() const { return mAlpSimResponse.get(); } - void setAlpSimResponse(const o2::itsmft::AlpideSimResponse*); + const o2::trk::ChipSimResponse* getResponse() const { return mResponse.get(); } + void setResponse(const o2::itsmft::AlpideSimResponse*); const SignalShape& getSignalShape() const { return mSignalShape; } SignalShape& getSignalShape() { return (SignalShape&)mSignalShape; } @@ -123,7 +123,7 @@ class DigiParams o2::itsmft::AlpideSignalTrapezoid mSignalShape; ///< signal timeshape parameterization - std::unique_ptr mAlpSimResponse; //!< pointer on external response + std::unique_ptr mResponse; //!< pointer on external response // auxiliary precalculated parameters float mROFrameLengthInv = 0; ///< inverse length of RO frame in ns diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/DigiParams.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/DigiParams.cxx index e2a78702204e5..d5d47b3658b04 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/DigiParams.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/DigiParams.cxx @@ -72,7 +72,7 @@ void DigiParams::print() const mSignalShape.print(); } -void DigiParams::setAlpSimResponse(const o2::itsmft::AlpideSimResponse* resp) +void DigiParams::setResponse(const o2::itsmft::AlpideSimResponse* resp) { LOG(debug) << "Response function data path: " << resp->getDataPath(); LOG(debug) << "Response function info: "; @@ -80,5 +80,5 @@ void DigiParams::setAlpSimResponse(const o2::itsmft::AlpideSimResponse* resp) if (!resp) { LOGP(fatal, "cannot set response function from null"); } - mAlpSimResponse = std::make_unique(resp); + mResponse = 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 31ef19a21cce9..31b9a25b7e5f8 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Digitizer.cxx @@ -50,7 +50,7 @@ void Digitizer::init() } // setting the correct response function (for the moment, for both VD and MLOT the same response function is used) - mChipSimResp = mParams.getAlpSimResponse(); + mChipSimResp = mParams.getResponse(); mChipSimRespVD = mChipSimResp; /// for the moment considering the same response mChipSimRespMLOT = mChipSimResp; /// for the moment considering the same response @@ -171,7 +171,13 @@ void Digitizer::setEventTime(const o2::InteractionTimeRecord& irt) nbc--; } - mNewROFrame = nbc / mParams.getROFrameLengthInBC(); + if (nbc < 0) { + mNewROFrame = 0; + mIsBeforeFirstRO = true; + } else { + mNewROFrame = nbc / mParams.getROFrameLengthInBC(); + mIsBeforeFirstRO = false; + } LOG(debug) << " NewROFrame " << mNewROFrame << " = " << nbc << "/" << mParams.getROFrameLengthInBC() << " (nbc/mParams.getROFrameLengthInBC()"; @@ -179,6 +185,7 @@ void Digitizer::setEventTime(const o2::InteractionTimeRecord& irt) mCollisionTimeWrtROF += (nbc % mParams.getROFrameLengthInBC()) * o2::constants::lhc::LHCBunchSpacingNS; } else { mNewROFrame = 0; + mIsBeforeFirstRO = false; } if (mNewROFrame < mROFrameMin) { diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx index b587ec24775b4..3801228422a62 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx @@ -427,7 +427,7 @@ DataProcessorSpec getTrackerSpec(bool useMC, const std::string& hitRecoConfig, o } // 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")); + // inputs.emplace_back("TRK_almiraparam", "TRK", "ALMIRAPARAM", 0, Lifetime::Condition, ccdbParamSpec("TRK/Config/AlmiraParam")); // outputs.emplace_back("TRK", "TRACKCLSID", 0, Lifetime::Timeframe); // outputs.emplace_back("TRK", "TRKTrackROF", 0, Lifetime::Timeframe); diff --git a/Steer/DigitizerWorkflow/src/TRKDigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/TRKDigitizerSpec.cxx index 30f9d33983712..8957ebed223b2 100644 --- a/Steer/DigitizerWorkflow/src/TRKDigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/TRKDigitizerSpec.cxx @@ -21,19 +21,21 @@ #include "DataFormatsITSMFT/Digit.h" #include "SimulationDataFormat/ConstMCTruthContainer.h" #include "DetectorsBase/BaseDPLDigitizer.h" +#include "DetectorsRaw/HBFUtils.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsCommonDataFormats/SimTraits.h" #include "DataFormatsParameters/GRPObject.h" #include "DataFormatsITSMFT/ROFRecord.h" #include "TRKSimulation/Digitizer.h" #include "TRKSimulation/DPLDigitizerParam.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "TRKBase/AlmiraParam.h" #include "TRKBase/GeometryTGeo.h" #include "TRKBase/TRKBaseParam.h" #include #include +#include #include #include @@ -77,6 +79,8 @@ class TRKDPLDigitizerTask : BaseDPLDigitizer if (mFinished) { return; } + mFirstOrbitTF = pc.services().get().firstTForbit; + const o2::InteractionRecord firstIR(0, mFirstOrbitTF); updateTimeDependentParams(pc); // read collision context from input @@ -102,6 +106,11 @@ class TRKDPLDigitizerTask : BaseDPLDigitizer // digits are directly put into DPL owned resource auto& digitsAccum = pc.outputs().make>(Output{mOrigin, "DIGITS", 0}); + const int roFrameLengthInBC = mDigitizer.getParams().getROFrameLengthInBC(); + const int nROFsPerOrbit = o2::constants::lhc::LHCMaxBunches / roFrameLengthInBC; + const int nROFsTF = nROFsPerOrbit * raw::HBFUtils::Instance().getNOrbitsPerTF(); + mROFRecordsAccum.reserve(nROFsTF); + auto accumulate = [this, &digitsAccum]() { // accumulate result of single event processing, called after processing every event supplied // AND after the final flushing via digitizer::fillOutputContainer @@ -180,10 +189,62 @@ class TRKDPLDigitizerTask : BaseDPLDigitizer accumulate(); // here we have all digits and labels and we can send them to consumer (aka snapshot it onto output) + std::vector expDigitRofVec(nROFsTF); + for (int iROF = 0; iROF < nROFsTF; ++iROF) { + auto& rof = expDigitRofVec[iROF]; + const int orb = iROF * roFrameLengthInBC / o2::constants::lhc::LHCMaxBunches + mFirstOrbitTF; + const int bc = iROF * roFrameLengthInBC % o2::constants::lhc::LHCMaxBunches; + rof.setBCData(o2::InteractionRecord(bc, orb)); + rof.setROFrame(iROF); + rof.setNEntries(0); + rof.setFirstEntry(-1); + } + + for (const auto& rof : mROFRecordsAccum) { + const auto& ir = rof.getBCData(); + const auto irToFirst = ir - firstIR; + const auto irROF = irToFirst.toLong() / roFrameLengthInBC; + if (irROF < 0 || irROF >= nROFsTF) { + continue; + } + 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()); + } + } - pc.outputs().snapshot(Output{mOrigin, "DIGITSROF", 0}, mROFRecordsAccum); + int prevFirst = 0; + for (auto& rof : expDigitRofVec) { + if (rof.getFirstEntry() < 0) { + rof.setFirstEntry(prevFirst); + } + prevFirst = rof.getFirstEntry(); + } + + pc.outputs().snapshot(Output{mOrigin, "DIGITSROF", 0}, expDigitRofVec); if (mWithMCTruth) { - pc.outputs().snapshot(Output{mOrigin, "DIGITSMC2ROF", 0}, mMC2ROFRecordsAccum); + std::vector clippedMC2ROFRecords; + clippedMC2ROFRecords.reserve(mMC2ROFRecordsAccum.size()); + for (auto mc2rof : mMC2ROFRecordsAccum) { + if (mc2rof.rofRecordID < 0 || mc2rof.minROF >= static_cast(nROFsTF)) { + mc2rof.rofRecordID = -1; + mc2rof.minROF = 0; + mc2rof.maxROF = 0; + } else { + mc2rof.maxROF = std::min(mc2rof.maxROF, nROFsTF - 1); + if (mc2rof.minROF > mc2rof.maxROF) { + mc2rof.rofRecordID = -1; + mc2rof.minROF = 0; + mc2rof.maxROF = 0; + } else { + mc2rof.rofRecordID = mc2rof.minROF; + } + } + clippedMC2ROFRecords.push_back(mc2rof); + } + pc.outputs().snapshot(Output{mOrigin, "DIGITSMC2ROF", 0}, clippedMC2ROFRecords); auto& sharedlabels = pc.outputs().make>(Output{mOrigin, "DIGITSMCTR", 0}); mLabelsAccum.flatten_to(sharedlabels); // free space of existing label containers @@ -208,7 +269,7 @@ class TRKDPLDigitizerTask : BaseDPLDigitizer if (!file) { LOG(fatal) << "Cannot open response file " << mLocalRespFile; } - mDigitizer.getParams().setAlpSimResponse((const o2::itsmft::AlpideSimResponse*)file->Get("response1")); + mDigitizer.getParams().setResponse((const o2::itsmft::AlpideSimResponse*)file->Get("response1")); } void updateTimeDependentParams(ProcessingContext& pc) @@ -225,21 +286,15 @@ class TRKDPLDigitizerTask : BaseDPLDigitizer mDigitizer.setGeometry(geom); const auto& dopt = o2::trk::DPLDigitizerParam::Instance(); - pc.inputs().get*>("ITS_alppar"); - const auto& aopt = o2::itsmft::DPLAlpideParam::Instance(); - digipar.setContinuous(dopt.continuous); + // pc.inputs().get("TRK_almiraparam"); + const auto& aopt = o2::trk::AlmiraParam::Instance(); + auto frameNS = aopt.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingNS; + digipar.setContinuous(true); digipar.setROFrameBiasInBC(aopt.roFrameBiasInBC); - if (dopt.continuous) { - auto frameNS = aopt.roFrameLengthInBC * o2::constants::lhc::LHCBunchSpacingNS; - digipar.setROFrameLengthInBC(aopt.roFrameLengthInBC); - digipar.setROFrameLength(frameNS); // RO frame in ns - digipar.setStrobeDelay(aopt.strobeDelay); // Strobe delay wrt beginning of the RO frame, in ns - digipar.setStrobeLength(aopt.strobeLengthCont > 0 ? aopt.strobeLengthCont : frameNS - aopt.strobeDelay); // Strobe length in ns - } else { - digipar.setROFrameLength(aopt.roFrameLengthTrig); // RO frame in ns - digipar.setStrobeDelay(aopt.strobeDelay); // Strobe delay wrt beginning of the RO frame, in ns - digipar.setStrobeLength(aopt.strobeLengthTrig); // Strobe length in ns - } + digipar.setROFrameLengthInBC(aopt.roFrameLengthInBC); + digipar.setROFrameLength(frameNS); // RO frame in ns + digipar.setStrobeDelay(aopt.strobeDelay); + digipar.setStrobeLength(aopt.strobeLengthCont > 0 ? aopt.strobeLengthCont : frameNS - aopt.strobeDelay); // parameters of signal time response: flat-top duration, max rise time and q @ which rise time is 0 digipar.getSignalShape().setParameters(dopt.strobeFlatTop, dopt.strobeMaxRiseTime, dopt.strobeQRiseTime0); digipar.setChargeThreshold(dopt.chargeThreshold); // charge threshold in electrons @@ -247,10 +302,8 @@ class TRKDPLDigitizerTask : BaseDPLDigitizer digipar.setTimeOffset(dopt.timeOffset); digipar.setNSimSteps(dopt.nSimSteps); - mROMode = digipar.isContinuous() ? o2::parameters::GRPObject::CONTINUOUS : o2::parameters::GRPObject::PRESENT; - LOG(info) << mID.getName() << " simulated in " - << ((mROMode == o2::parameters::GRPObject::CONTINUOUS) ? "CONTINUOUS" : "TRIGGERED") - << " RO mode"; + mROMode = o2::parameters::GRPObject::CONTINUOUS; + LOG(info) << mID.getName() << " simulated in CONTINUOUS RO mode"; // if (oTRKParams::Instance().useDeadChannelMap) { // pc.inputs().get("TRK_dead"); // trigger final ccdb update @@ -265,9 +318,9 @@ class TRKDPLDigitizerTask : BaseDPLDigitizer void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) { - if (matcher == ConcreteDataMatcher(detectors::DetID::ITS, "ALPIDEPARAM", 0)) { - LOG(info) << mID.getName() << " Alpide param updated"; - const auto& par = o2::itsmft::DPLAlpideParam::Instance(); + if (matcher == ConcreteDataMatcher(mOrigin, "ALMIRAPARAM", 0)) { + LOG(info) << mID.getName() << " Almira param updated"; + const auto& par = o2::trk::AlmiraParam::Instance(); par.printKeyValues(); return; } @@ -280,7 +333,7 @@ class TRKDPLDigitizerTask : BaseDPLDigitizer 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.getParams().setResponse((const o2::itsmft::AlpideSimResponse*)obj); mDigitizer.setResponseName("APTS"); } else { LOG(info) << "Response function will be loaded from local file: " << mLocalRespFile; @@ -294,6 +347,7 @@ class TRKDPLDigitizerTask : BaseDPLDigitizer bool mWithMCTruth{true}; bool mFinished{false}; bool mDisableQED{false}; + unsigned long mFirstOrbitTF = 0x0; std::string mLocalRespFile{""}; const o2::detectors::DetID mID{o2::detectors::DetID::TRK}; const o2::header::DataOrigin mOrigin{o2::header::gDataOriginTRK}; @@ -318,7 +372,7 @@ DataProcessorSpec getTRKDigitizerSpec(int channel, bool mctruth) auto detOrig = o2::header::gDataOriginTRK; std::vector inputs; inputs.emplace_back("collisioncontext", "SIM", "COLLISIONCONTEXT", static_cast(channel), Lifetime::Timeframe); - inputs.emplace_back("ITS_alppar", "ITS", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/AlpideParam")); + // inputs.emplace_back("TRK_almiraparam", "TRK", "ALMIRAPARAM", 0, Lifetime::Condition, ccdbParamSpec("TRK/Config/AlmiraParam")); // if (oTRKParams::Instance().useDeadChannelMap) { // inputs.emplace_back("TRK_dead", "TRK", "DEADMAP", 0, Lifetime::Condition, ccdbParamSpec("TRK/Calib/DeadMap")); // } From c9acd57adde48c3bf39e491f29e86083352c282f Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Mon, 13 Apr 2026 15:00:09 +0200 Subject: [PATCH 472/701] ITS: staggering (#15188) * ITS: staggered tracking Signed-off-by: Felix Schlepper * ITS: various fixes also for GPU Signed-off-by: Felix Schlepper * ITS: fix vertexer and move new types Signed-off-by: Felix Schlepper * ITS: format Signed-off-by: Felix Schlepper * ITS: account for layer ROF bias in tracker Signed-off-by: Felix Schlepper * ITS: sort tracks in time by lower edge Signed-off-by: Felix Schlepper * ITS: ensure mc labels are nullptr Signed-off-by: Felix Schlepper * ITSMFT: account for possible delay of received ROFs Signed-off-by: Felix Schlepper * ITS: staggered STF decoder Signed-off-by: Felix Schlepper * ITS: fix track time-assignment Signed-off-by: Felix Schlepper * ITS: output vertices Signed-off-by: Felix Schlepper * ITS: add macro to check staggering in data Signed-off-by: Felix Schlepper * Adapt ITS/MFT CTF machinery to staggered data * Fix compilation of ALICE3 tracking with staggering * ITS: modify staggering macro Signed-off-by: Felix Schlepper * ITSMFT: runtime staggering option Signed-off-by: Felix Schlepper * ITSMFT: fix instantiation in namespace Signed-off-by: Felix Schlepper * ITS3: fix compilation Signed-off-by: Felix Schlepper * Raw,CTF: add option to specify base cache dir for remote files Signed-off-by: Felix Schlepper * ITS: tracking same as dev Signed-off-by: Felix Schlepper * ITS: add back datastreams Signed-off-by: Felix Schlepper * ITSMFT: improve logging Signed-off-by: Felix Schlepper * ITS: add rofs for vertices back Signed-off-by: Felix Schlepper * add copyright to macro Signed-off-by: Felix Schlepper * ITS: hide print functions for device code Signed-off-by: Felix Schlepper * ITSMFT: add shim file for alpide param Signed-off-by: Felix Schlepper * try to fix macro compilation Signed-off-by: Felix Schlepper * Avoid wildcarded subspecs in Digit/ClusterWriter * ITS: fix rof lut to work properly with added errors Signed-off-by: Felix Schlepper * Fix/add some staggering options * Add ITS/MFT staggering options to dpl-workflow.sh To activate ITS or MFT staggering in the topology generation, export ITSSTAGGERED=1 or MFTSTAGGERED=1 respectively * ITS: try fix for QC Signed-off-by: Felix Schlepper * ITS: fix ROFLookpTables warning Signed-off-by: Felix Schlepper * ITS: fix tracklet formatting Signed-off-by: Felix Schlepper * ITS: set BCData properly for ROFs Signed-off-by: Felix Schlepper * ITS: remove deprecated settings Signed-off-by: Felix Schlepper * ITS: fix cluster label access for non-staggered Signed-off-by: Felix Schlepper * ITSMFT: fix staggering wfx option for digit-writer-workflow Signed-off-by: Felix Schlepper * Fix loop condition for ITS tracking layers * Fix/add some staggering options * Add ITS/MFT staggering options to dpl-workflow.sh To activate ITS or MFT staggering in the topology generation, export ITSSTAGGERED=1 or MFTSTAGGERED=1 respectively * ITSMFT: fix staggering wfx option for digit-writer-workflow Signed-off-by: Felix Schlepper * Fix loop condition for ITS tracking layers * Make ITS vertex messageable * remove unused variable * Add/fix staggering options to all workflows reading ITS,MFT clusters To pass the sim-challenge test. W/o this option even -h leads to a crash. Strictly speaking, one could use in the DPLAlpideParamInitializer::isITSStaggeringEnabled and DPLAlpideParamInitializer::isMFTStaggeringEnabled a test ic.options().hasOption(stagITSOpt) and ic.options().hasOption(stagMFTOpt) before testing the option itself. But better to have an explicit detection of missing staggering option. * ITSMFT: fix digit reader Signed-off-by: Felix Schlepper * Remove leftover NROFs configurable from dpl-workflow.sh * ITS: fix time assignments Signed-off-by: Felix Schlepper * ITS: fix degenerate LSE for matrix solving Comparing the output of dev and this PR, I saw plently of cases where the system of equation was fully degenerate and produced to different floating instructions and compiler optimizations slightly different results. The solution is to discard the vertex cand. if the LSE becomes degenerate as not to produce non-sense solutions. Signed-off-by: Felix Schlepper * ITS: fix macro Signed-off-by: Felix Schlepper * MFT: fix track writer Signed-off-by: Felix Schlepper * ITS: fix gpu compile due change in vertexer types Signed-off-by: Felix Schlepper * ITS: move lookup table creation to proper place Signed-off-by: Felix Schlepper * Move FastMultEstimation to ITS tracking library * ITS: add containedIn to TS Signed-off-by: Felix Schlepper * ITS: fix vertexer Signed-off-by: Felix Schlepper * ITS: improve STFDecoder&Clusterer error messages and account for delay longer that ROF Signed-off-by: Felix Schlepper * Implement new kind of multiplicity mask * Adapt GPU code to the new mult mask * ITS: finalize tracking code Signed-off-by: Felix Schlepper * ITS: remove deltaRof for vertexer Signed-off-by: Felix Schlepper * ITS: report current timeslice Signed-off-by: Felix Schlepper * Vertex: also print time error Signed-off-by: Felix Schlepper * ITS: speedup vertexer Signed-off-by: Felix Schlepper --------- Signed-off-by: Felix Schlepper Co-authored-by: shahoian Co-authored-by: Maximiliano Puccio --- .../GlobalTracking/src/RecoContainer.cxx | 2 +- .../Detectors/ITSMFT/ITS/CMakeLists.txt | 6 +- .../ITS/include/DataFormatsITS/TimeEstBC.h | 103 +++ .../ITS/include/DataFormatsITS/TrackITS.h | 26 +- .../ITS/include/DataFormatsITS/Vertex.h | 42 + .../ITSMFT/ITS/src/DataFormatsITSLinkDef.h | 7 + .../Detectors/ITSMFT/ITS/src/TimeEstBC.cxx | 13 + .../Detectors/ITSMFT/common/CMakeLists.txt | 7 +- .../common/include/DataFormatsITSMFT/CTF.h | 4 +- .../DataFormatsITSMFT/DPLAlpideParam.h | 104 +++ .../DPLAlpideParamInitializer.h | 42 + .../ITSMFT/common}/src/DPLAlpideParam.cxx | 11 +- .../common/src/DPLAlpideParamInitializer.cxx | 46 + .../common/src/ITSMFTDataFormatsLinkDef.h | 5 + .../ReconstructionDataFormats/Vertex.h | 25 +- .../include/CommonDataFormat/TimeStamp.h | 16 +- Detectors/AOD/src/AODProducerWorkflowSpec.cxx | 2 +- Detectors/AOD/src/aod-producer-workflow.cxx | 2 + .../Workflow/src/BarrelAlignmentSpec.cxx | 2 +- .../src/barrel-alignment-workflow.cxx | 2 + Detectors/CTF/test/test_ctf_io_itsmft.cxx | 4 +- .../include/CTFWorkflow/CTFReaderSpec.h | 3 + .../include/CTFWorkflow/CTFWriterSpec.h | 11 +- Detectors/CTF/workflow/src/CTFReaderSpec.cxx | 75 +- Detectors/CTF/workflow/src/CTFWriterSpec.cxx | 215 +++-- .../CTF/workflow/src/ctf-reader-workflow.cxx | 12 +- .../CTF/workflow/src/ctf-writer-workflow.cxx | 16 +- Detectors/Filtering/src/FilteringSpec.cxx | 2 +- .../Filtering/src/filtering-workflow.cxx | 2 + .../helpers/src/InputHelper.cxx | 7 +- .../src/CosmicsMatchingSpec.cxx | 2 +- .../src/GlobalFwdMatchingSpec.cxx | 2 +- .../src/PrimaryVertexingSpec.cxx | 2 +- .../src/TPCITSMatchingSpec.cxx | 2 +- .../src/VertexTrackMatcherSpec.cxx | 2 +- .../src/cosmics-match-workflow.cxx | 2 + .../src/globalfwd-matcher-workflow.cxx | 2 + .../src/secondary-vertexing-workflow.cxx | 2 + .../src/strangeness-tracking-workflow.cxx | 2 + .../src/tpcits-match-workflow.cxx | 3 + .../study/src/CheckResid.cxx | 2 +- .../study/src/DumpTracks.cxx | 2 +- .../study/src/SVStudy.cxx | 2 +- .../study/src/TrackMCStudy.cxx | 2 +- .../study/src/TrackingStudy.cxx | 2 +- .../study/src/check-resid-workflow.cxx | 2 + .../study/src/its-offset-study-workflow.cxx | 2 + .../study/src/trackMCStudy-workflow.cxx | 2 + .../study/src/tracking-study-workflow.cxx | 2 + .../src/tpc-interpolation-workflow.cxx | 2 + .../ITSMFT/ITS/macros/test/CMakeLists.txt | 10 + Detectors/ITSMFT/ITS/macros/test/CheckDROF.C | 21 +- .../ITSMFT/ITS/macros/test/CheckStaggering.C | 521 +++++++++++ .../studies/src/ImpactParameter.cxx | 2 +- .../standalone-postprocessing-workflow.cxx | 6 +- .../ITSMFT/ITS/reconstruction/CMakeLists.txt | 6 +- .../include/ITSReconstruction/FastMultEst.h | 70 -- .../ITSReconstruction/TrivialVertexer.h | 70 -- .../ITS/reconstruction/src/FastMultEst.cxx | 189 ---- .../src/ITSReconstructionLinkDef.h | 3 - .../reconstruction/src/TrivialVertexer.cxx | 108 --- Detectors/ITSMFT/ITS/tracking/CMakeLists.txt | 8 +- .../GPU/ITStrackingGPU/ClusterLinesGPU.h | 73 -- .../GPU/ITStrackingGPU/TimeFrameChunk.h | 148 --- .../GPU/ITStrackingGPU/TimeFrameGPU.h | 161 ++-- .../tracking/GPU/ITStrackingGPU/TracerGPU.h | 38 - .../GPU/ITStrackingGPU/TrackerTraitsGPU.h | 15 +- .../GPU/ITStrackingGPU/TrackingKernels.h | 75 +- .../ITS/tracking/GPU/ITStrackingGPU/Utils.h | 6 +- .../GPU/ITStrackingGPU/VertexerTraitsGPU.h | 55 -- .../GPU/ITStrackingGPU/VertexingKernels.h | 115 --- .../ITS/tracking/GPU/cuda/CMakeLists.txt | 19 +- .../ITS/tracking/GPU/cuda/ClusterLinesGPU.cu | 138 --- .../ITS/tracking/GPU/cuda/TimeFrameChunk.cu | 293 ------ .../ITS/tracking/GPU/cuda/TimeFrameGPU.cu | 483 +++++----- .../ITSMFT/ITS/tracking/GPU/cuda/TracerGPU.cu | 48 - .../tracking/GPU/cuda/TrackerTraitsGPU.cxx | 129 ++- .../ITS/tracking/GPU/cuda/TrackingKernels.cu | 289 +++--- .../tracking/GPU/cuda/VertexerTraitsGPU.cxx | 179 ---- .../ITS/tracking/GPU/cuda/VertexingKernels.cu | 660 -------------- .../ITS/tracking/GPU/hip/CMakeLists.txt | 6 +- .../include/ITStracking/BoundedAllocator.h | 3 + .../ITS/tracking/include/ITStracking/Cell.h | 39 +- .../tracking/include/ITStracking/Cluster.h | 5 +- .../include/ITStracking/ClusterLines.h | 221 +---- .../include/ITStracking/Configuration.h | 42 +- .../tracking/include/ITStracking/Constants.h | 2 +- .../include/ITStracking/Definitions.h | 38 +- .../include/ITStracking/FastMultEst.h | 93 ++ .../include/ITStracking}/FastMultEstConfig.h | 30 +- .../tracking/include/ITStracking/MathUtils.h | 33 +- .../include/ITStracking/ROFLookupTables.h | 850 ++++++++++++++++++ .../ITS/tracking/include/ITStracking/Road.h | 72 -- .../tracking/include/ITStracking/Smoother.h | 60 -- .../tracking/include/ITStracking/TimeFrame.h | 480 ++++------ .../tracking/include/ITStracking/Tracker.h | 31 +- .../include/ITStracking/TrackerTraits.h | 50 +- .../include/ITStracking/TrackingConfigParam.h | 28 +- .../include/ITStracking/TrackingInterface.h | 5 + .../tracking/include/ITStracking/Tracklet.h | 46 +- .../tracking/include/ITStracking/Vertexer.h | 15 +- .../include/ITStracking/VertexerTraits.h | 31 +- .../ITSMFT/ITS/tracking/src/ClusterLines.cxx | 453 +++------- .../ITSMFT/ITS/tracking/src/Configuration.cxx | 60 +- .../ITSMFT/ITS/tracking/src/FastMultEst.cxx | 252 ++++++ .../src/FastMultEstConfig.cxx | 2 +- .../ITS/tracking/src/IndexTableUtils.cxx | 49 - .../ITSMFT/ITS/tracking/src/Smoother.cxx | 222 ----- .../ITSMFT/ITS/tracking/src/TimeFrame.cxx | 426 +++------ Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx | 307 ++----- .../ITSMFT/ITS/tracking/src/TrackerTraits.cxx | 576 +++--------- .../ITS/tracking/src/TrackingInterface.cxx | 397 ++++---- .../ITSMFT/ITS/tracking/src/TrackingLinkDef.h | 7 + .../ITSMFT/ITS/tracking/src/Vertexer.cxx | 60 +- .../ITS/tracking/src/VertexerTraits.cxx | 742 +++++---------- .../ITSMFT/ITS/tracking/test/CMakeLists.txt | 8 +- .../ITS/tracking/test/testROFLookupTables.cxx | 744 +++++++++++++++ .../ITSWorkflow/ClusterWriterWorkflow.h | 2 +- .../include/ITSWorkflow/DCSAdaposParserSpec.h | 2 +- .../include/ITSWorkflow/RecoWorkflow.h | 2 +- .../include/ITSWorkflow/TrackReaderSpec.h | 27 +- .../include/ITSWorkflow/TrackerSpec.h | 3 +- .../include/ITSWorkflow/VertexReaderSpec.h | 2 +- .../workflow/src/ClusterWriterWorkflow.cxx | 4 +- .../ITSMFT/ITS/workflow/src/RecoWorkflow.cxx | 11 +- .../ITS/workflow/src/TrackReaderSpec.cxx | 34 +- .../ITS/workflow/src/TrackWriterSpec.cxx | 21 +- .../ITS/workflow/src/TrackWriterWorkflow.cxx | 4 +- .../ITSMFT/ITS/workflow/src/TrackerSpec.cxx | 49 +- .../src/its-cluster-reader-workflow.cxx | 7 +- .../src/its-cluster-writer-workflow.cxx | 8 +- .../ITS/workflow/src/its-reco-workflow.cxx | 25 +- .../src/its-track-writer-workflow.cxx | 3 +- .../calibration/src/NoiseCalibratorSpec.cxx | 2 +- .../include/MFTCondition/DCSConfigReader.h | 2 +- .../include/MFTWorkflow/RecoWorkflow.h | 1 + .../include/MFTWorkflow/TrackerSpec.h | 2 +- .../ITSMFT/MFT/workflow/src/RecoWorkflow.cxx | 7 +- .../MFT/workflow/src/TrackWriterSpec.cxx | 11 +- .../ITSMFT/MFT/workflow/src/TrackerSpec.cxx | 13 +- .../src/mft-cluster-reader-workflow.cxx | 5 +- .../src/mft-cluster-writer-workflow.cxx | 8 +- .../MFT/workflow/src/mft-reco-workflow.cxx | 4 + Detectors/ITSMFT/common/base/CMakeLists.txt | 4 +- .../base/include/ITSMFTBase/DPLAlpideParam.h | 111 +-- .../common/base/src/ITSMFTBaseLinkDef.h | 5 - .../include/ITSMFTReconstruction/CTFCoder.h | 40 +- .../ITSMFTReconstruction/ChipMappingITS.h | 5 +- .../ITSMFTReconstruction/ChipMappingMFT.h | 3 + .../ITSMFTReconstruction/PixelReader.h | 8 +- .../ITSMFTReconstruction/RawPixelDecoder.h | 11 +- .../ITSMFTReconstruction/RawPixelReader.h | 7 +- .../common/reconstruction/src/CTFCoder.cxx | 42 +- .../reconstruction/src/ChipMappingITS.cxx | 24 +- .../reconstruction/src/ChipMappingMFT.cxx | 14 + .../common/reconstruction/src/Clusterer.cxx | 2 +- .../common/reconstruction/src/GBTLink.cxx | 2 +- .../reconstruction/src/RawPixelDecoder.cxx | 43 +- .../include/ITSMFTSimulation/DigiParams.h | 2 +- .../ITSMFTWorkflow/ClusterReaderSpec.h | 41 +- .../ITSMFTWorkflow/ClusterWriterSpec.h | 6 +- .../include/ITSMFTWorkflow/ClustererSpec.h | 11 +- .../include/ITSMFTWorkflow/DigitReaderSpec.h | 47 +- .../include/ITSMFTWorkflow/DigitWriterSpec.h | 4 +- .../ITSMFTWorkflow/EntropyDecoderSpec.h | 17 +- .../ITSMFTWorkflow/EntropyEncoderSpec.h | 16 +- .../include/ITSMFTWorkflow/STFDecoderSpec.h | 30 +- .../common/workflow/src/ClusterReaderSpec.cxx | 47 +- .../common/workflow/src/ClusterWriterSpec.cxx | 57 +- .../common/workflow/src/ClustererSpec.cxx | 81 +- .../common/workflow/src/DigitReaderSpec.cxx | 77 +- .../common/workflow/src/DigitWriterSpec.cxx | 54 +- .../workflow/src/EntropyDecoderSpec.cxx | 172 ++-- .../workflow/src/EntropyEncoderSpec.cxx | 118 ++- .../common/workflow/src/STFDecoderSpec.cxx | 394 +++++--- .../workflow/src/digit-reader-workflow.cxx | 8 +- .../workflow/src/digit-writer-workflow.cxx | 9 +- .../workflow/src/entropy-encoder-workflow.cxx | 9 +- .../workflow/src/stf-decoder-workflow.cxx | 8 +- Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx | 2 +- Detectors/Raw/TFReaderDD/src/TFReaderSpec.h | 1 + .../Raw/TFReaderDD/src/tf-reader-workflow.cxx | 2 + .../workflow/src/trd-tracking-workflow.cxx | 2 + .../include/TRKReconstruction/TimeFrame.h | 9 +- .../TRK/reconstruction/src/TimeFrame.cxx | 93 +- .../ALICE3/TRK/workflow/src/TrackerSpec.cxx | 92 +- .../ITS3Reconstruction/TrackingInterface.h | 1 + .../ITS3/reconstruction/src/IOUtils.cxx | 10 +- .../reconstruction/src/TrackingInterface.cxx | 5 +- .../ITS3/workflow/src/ClustererSpec.cxx | 2 +- .../ITS3/workflow/src/RecoWorkflow.cxx | 2 +- .../ITS3/workflow/src/TrackerSpec.cxx | 4 +- .../include/DetectorsVertexing/PVertexer.h | 2 +- .../Vertexing/src/VertexTrackMatcher.cxx | 2 +- Detectors/Vertexing/test/PVFromPool.C | 13 +- .../Workflow/src/EveWorkflowHelper.cxx | 2 +- .../Workflow/src/O2DPLDisplay.cxx | 3 +- .../Base/GPUReconstructionIncludesITS.h | 5 - .../Base/cuda/GPUReconstructionCUDA.cu | 6 +- GPU/GPUTracking/Global/GPUChainITS.h | 3 - .../display/render/GPUDisplayImportEvent.cxx | 2 +- .../include/GPUWorkflow/GPUWorkflowSpec.h | 1 + GPU/Workflow/src/GPUWorkflowITS.cxx | 9 +- GPU/Workflow/src/GPUWorkflowSpec.cxx | 16 +- GPU/Workflow/src/O2GPUDPLDisplay.cxx | 3 +- GPU/Workflow/src/gpu-reco-workflow.cxx | 3 + .../src/ITS3DigitizerSpec.cxx | 2 +- .../src/ITSMFTDigitizerSpec.cxx | 99 +- .../src/ITSMFTDigitizerSpec.h | 4 +- .../src/SimpleDigitizerWorkflow.cxx | 12 +- doc/data/2021-02-o2_prs.json | 2 +- doc/data/2022-01-o2_prs.json | 2 +- macro/run_rawdecoding_its.C | 21 +- macro/run_rawdecoding_mft.C | 21 +- prodtests/full-system-test/dpl-workflow.sh | 30 +- prodtests/full_system_test.sh | 4 - 216 files changed, 6352 insertions(+), 7206 deletions(-) create mode 100644 DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TimeEstBC.h create mode 100644 DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/Vertex.h create mode 100644 DataFormats/Detectors/ITSMFT/ITS/src/TimeEstBC.cxx create mode 100644 DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/DPLAlpideParam.h create mode 100644 DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/DPLAlpideParamInitializer.h rename {Detectors/ITSMFT/common/base => DataFormats/Detectors/ITSMFT/common}/src/DPLAlpideParam.cxx (82%) create mode 100644 DataFormats/Detectors/ITSMFT/common/src/DPLAlpideParamInitializer.cxx create mode 100644 Detectors/ITSMFT/ITS/macros/test/CheckStaggering.C delete mode 100644 Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEst.h delete mode 100644 Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/TrivialVertexer.h delete mode 100644 Detectors/ITSMFT/ITS/reconstruction/src/FastMultEst.cxx delete mode 100644 Detectors/ITSMFT/ITS/reconstruction/src/TrivialVertexer.cxx delete mode 100644 Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/ClusterLinesGPU.h delete mode 100644 Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameChunk.h delete mode 100644 Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TracerGPU.h delete mode 100644 Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexerTraitsGPU.h delete mode 100644 Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexingKernels.h delete mode 100644 Detectors/ITSMFT/ITS/tracking/GPU/cuda/ClusterLinesGPU.cu delete mode 100644 Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameChunk.cu delete mode 100644 Detectors/ITSMFT/ITS/tracking/GPU/cuda/TracerGPU.cu delete mode 100644 Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexerTraitsGPU.cxx delete mode 100644 Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexingKernels.cu create mode 100644 Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEst.h rename Detectors/ITSMFT/ITS/{reconstruction/include/ITSReconstruction => tracking/include/ITStracking}/FastMultEstConfig.h (58%) create mode 100644 Detectors/ITSMFT/ITS/tracking/include/ITStracking/ROFLookupTables.h delete mode 100644 Detectors/ITSMFT/ITS/tracking/include/ITStracking/Road.h delete mode 100644 Detectors/ITSMFT/ITS/tracking/include/ITStracking/Smoother.h create mode 100644 Detectors/ITSMFT/ITS/tracking/src/FastMultEst.cxx rename Detectors/ITSMFT/ITS/{reconstruction => tracking}/src/FastMultEstConfig.cxx (94%) delete mode 100644 Detectors/ITSMFT/ITS/tracking/src/IndexTableUtils.cxx delete mode 100644 Detectors/ITSMFT/ITS/tracking/src/Smoother.cxx create mode 100644 Detectors/ITSMFT/ITS/tracking/test/testROFLookupTables.cxx diff --git a/DataFormats/Detectors/GlobalTracking/src/RecoContainer.cxx b/DataFormats/Detectors/GlobalTracking/src/RecoContainer.cxx index dd206ffe3b70d..277466fb2e969 100644 --- a/DataFormats/Detectors/GlobalTracking/src/RecoContainer.cxx +++ b/DataFormats/Detectors/GlobalTracking/src/RecoContainer.cxx @@ -34,7 +34,7 @@ #include "ReconstructionDataFormats/TrackMCHMID.h" #include "DataFormatsITSMFT/TrkClusRef.h" #include "DataFormatsITSMFT/TopologyDictionary.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" // FIXME: ideally, the data formats definition should be independent of the framework // collectData is using the input of ProcessingContext to extract the first valid // header and the TF orbit from it diff --git a/DataFormats/Detectors/ITSMFT/ITS/CMakeLists.txt b/DataFormats/Detectors/ITSMFT/ITS/CMakeLists.txt index 5a353881e27ba..f05979d749fc0 100644 --- a/DataFormats/Detectors/ITSMFT/ITS/CMakeLists.txt +++ b/DataFormats/Detectors/ITSMFT/ITS/CMakeLists.txt @@ -11,8 +11,12 @@ o2_add_library(DataFormatsITS SOURCES src/TrackITS.cxx + src/TimeEstBC.cxx PUBLIC_LINK_LIBRARIES O2::ReconstructionDataFormats + O2::SimulationDataFormat O2::DataFormatsITSMFT) o2_target_root_dictionary(DataFormatsITS - HEADERS include/DataFormatsITS/TrackITS.h) + HEADERS include/DataFormatsITS/TrackITS.h + include/DataFormatsITS/Vertex.h + include/DataFormatsITS/TimeEstBC.h) diff --git a/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TimeEstBC.h b/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TimeEstBC.h new file mode 100644 index 0000000000000..695d9aff42858 --- /dev/null +++ b/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TimeEstBC.h @@ -0,0 +1,103 @@ +// 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. + +#ifndef O2_TRACKINGITS_TIMEESTBC_H_ +#define O2_TRACKINGITS_TIMEESTBC_H_ + +#include +#include +#include "CommonDataFormat/TimeStamp.h" +#include "GPUCommonRtypes.h" +#include "GPUCommonDef.h" +#include "GPUCommonMath.h" + +namespace o2::its +{ +// Time estimates are given in BC +// error needs to cover maximum 1 orbit +using TimeStampType = uint32_t; +using TimeStampErrorType = uint16_t; +// this is an symmetric time error [t0-tE, t0+tE] +using TimeStamp = o2::dataformats::TimeStampWithError; +// this is an asymmetric time interval [t0, t0+tE] used for internal calculations +class TimeEstBC : public o2::dataformats::TimeStampWithError +{ + using Base = o2::dataformats::TimeStampWithError; + + public: + GPUhdDefault() TimeEstBC() = default; + GPUhdi() TimeEstBC(TimeStampType t, TimeStampErrorType e) : Base(t, e) {} + + // convert to symmetric center+-half representation + GPUhdi() its::TimeStamp makeSymmetrical() const noexcept + { + const auto start = static_cast(this->getTimeStamp()); + const float half = (float)this->getTimeStampError() / 2.f; + return {start + half, half}; + } + + // check if timestamps overlap within their interval + GPUhdi() bool isCompatible(const TimeEstBC& o) const noexcept + { + return this->upper() > o.lower() && o.upper() > this->lower(); + } + + // check if this time interval is fully contained within o + GPUhdi() bool isContainedIn(const TimeEstBC& o) const noexcept + { + return this->lower() >= o.lower() && this->upper() <= o.upper(); + } + + GPUhdi() TimeEstBC& operator+=(const TimeEstBC& o) noexcept + { + add(o); + return *this; + } + + GPUhdi() TimeEstBC operator+(const TimeEstBC& o) const noexcept + { + TimeEstBC res = *this; + res += o; + return res; + } + + // upper bound of interval t0+tE + GPUhdi() TimeStampType upper() const noexcept + { + TimeStampType t = this->getTimeStamp(); + TimeStampType e = this->getTimeStampError(); + constexpr TimeStampType max = std::numeric_limits::max(); + return (t > (max - e)) ? max : t + e; + } + + // lower bound of interval t0 + GPUhdi() TimeStampType lower() const noexcept + { + return this->getTimeStamp(); + } + + private: + // intersect with the other timestamp + // this assumes already that both overlap + GPUhdi() void add(const TimeEstBC& o) noexcept + { + const TimeStampType lo = o2::gpu::CAMath::Max(this->lower(), o.lower()); + const TimeStampType hi = o2::gpu::CAMath::Min(this->upper(), o.upper()); + this->setTimeStamp(lo); + this->setTimeStampError(static_cast(hi - lo)); + } + + ClassDefNV(TimeEstBC, 1); +}; + +} // namespace o2::its + +#endif diff --git a/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h b/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h index 06d4fba51bd54..5d13ad753b8bc 100644 --- a/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.h +++ b/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/TrackITS.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. // @@ -16,11 +16,12 @@ #ifndef ALICEO2_ITS_TRACKITS_H #define ALICEO2_ITS_TRACKITS_H -#include +#include #include "GPUCommonDef.h" #include "ReconstructionDataFormats/Track.h" #include "CommonDataFormat/RangeReference.h" +#include "DataFormatsITS/TimeEstBC.h" namespace o2 { @@ -35,8 +36,7 @@ namespace its class TrackITS : public o2::track::TrackParCov { enum UserBits { - kNextROF = 1 << 28, - kSharedClusters = 1 << 29 + kSharedClusters = 1 << 28 }; using Cluster = o2::itsmft::Cluster; @@ -93,6 +93,9 @@ class TrackITS : public o2::track::TrackParCov bool isBetter(const TrackITS& best, float maxChi2) const; + GPUhdi() auto& getTimeStamp() { return mTime; } + GPUhdi() const auto& getTimeStamp() const { return mTime; } + GPUhdi() o2::track::TrackParCov& getParamIn() { return *this; } GPUhdi() const o2::track::TrackParCov& getParamIn() const { return *this; } @@ -122,8 +125,6 @@ class TrackITS : public o2::track::TrackParCov } int getNFakeClusters() const; - void setNextROFbit(bool toggle = true) { mClusterSizes = toggle ? (mClusterSizes | kNextROF) : (mClusterSizes & ~kNextROF); } - bool hasHitInNextROF() const { return mClusterSizes & kNextROF; } void setSharedClusters(bool toggle = true) { mClusterSizes = toggle ? (mClusterSizes | kSharedClusters) : (mClusterSizes & ~kSharedClusters); } bool hasSharedClusters() const { return mClusterSizes & kSharedClusters; } @@ -157,9 +158,10 @@ class TrackITS : public o2::track::TrackParCov ClusRefs mClusRef; ///< references on clusters float mChi2 = 0.; ///< Chi2 for this track uint32_t mPattern = 0; ///< layers pattern - unsigned int mClusterSizes = 0u; + uint32_t mClusterSizes = 0u; ///< 4bit packed cluster sizes + TimeStamp mTime; ///< track time stamp with error in BC since start of TF, symmetrical - ClassDefNV(TrackITS, 6); + ClassDefNV(TrackITS, 7); }; class TrackITSExt : public TrackITS @@ -169,15 +171,13 @@ class TrackITSExt : public TrackITS static constexpr int MaxClusters = 16; /// Prepare for overlaps and new detector configurations using TrackITS::TrackITS; // inherit base constructors - GPUh() TrackITSExt(o2::track::TrackParCov&& parCov, short ncl, float chi2, - o2::track::TrackParCov&& outer, std::array cls) + GPUh() TrackITSExt(o2::track::TrackParCov&& parCov, short ncl, float chi2, o2::track::TrackParCov&& outer, std::array cls) : TrackITS(parCov, chi2, outer), mIndex{cls} { setNumberOfClusters(ncl); } - GPUh() TrackITSExt(o2::track::TrackParCov& parCov, short ncl, float chi2, std::uint32_t rof, - o2::track::TrackParCov& outer, std::array cls) + GPUh() TrackITSExt(o2::track::TrackParCov& parCov, short ncl, float chi2, std::uint32_t rof, o2::track::TrackParCov& outer, std::array cls) : TrackITS(parCov, chi2, outer), mIndex{cls} { setNumberOfClusters(ncl); @@ -212,7 +212,7 @@ class TrackITSExt : public TrackITS private: std::array mIndex = {-1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1, -1}; ///< Indices of associated clusters - ClassDefNV(TrackITSExt, 2); + ClassDefNV(TrackITSExt, 3); }; } // namespace its } // namespace o2 diff --git a/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/Vertex.h b/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/Vertex.h new file mode 100644 index 0000000000000..1e4ed03b753eb --- /dev/null +++ b/DataFormats/Detectors/ITSMFT/ITS/include/DataFormatsITS/Vertex.h @@ -0,0 +1,42 @@ +// 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. + +#ifndef O2_TRACKINGITS_VERTEX_H_ +#define O2_TRACKINGITS_VERTEX_H_ + +#include "GPUCommonDef.h" +#ifndef GPUCA_GPUCODE_DEVICE +#include +#endif +#include "ReconstructionDataFormats/Vertex.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "DataFormatsITS/TimeEstBC.h" + +namespace o2::its +{ +// NOTE: this uses the internal asymmetrical time reprenstation! +using Vertex = o2::dataformats::Vertex; +using VertexLabel = std::pair; +} // namespace o2::its + +#ifndef GPUCA_GPUCODE_DEVICE +/// Defining ITS Vertex explicitly as messageable +namespace o2::framework +{ +template +struct is_messageable; +template <> +struct is_messageable> : std::true_type { +}; +} // namespace o2::framework +#endif + +#endif diff --git a/DataFormats/Detectors/ITSMFT/ITS/src/DataFormatsITSLinkDef.h b/DataFormats/Detectors/ITSMFT/ITS/src/DataFormatsITSLinkDef.h index 91a71847148fb..a0d5b25c65b70 100644 --- a/DataFormats/Detectors/ITSMFT/ITS/src/DataFormatsITSLinkDef.h +++ b/DataFormats/Detectors/ITSMFT/ITS/src/DataFormatsITSLinkDef.h @@ -14,7 +14,14 @@ #pragma link off all globals; #pragma link off all classes; #pragma link off all functions; + #pragma link C++ class o2::its::TrackITS + ; #pragma link C++ class std::vector < o2::its::TrackITS> + ; +#pragma link C++ class o2::its::TimeEstBC + ; +#pragma link C++ class std::vector < o2::its::TimeEstBC> + ; + +#pragma link C++ class o2::dataformats::Vertex < o2::its::TimeEstBC> + ; +#pragma link C++ class std::vector < o2::dataformats::Vertex < o2::its::TimeEstBC>> + ; + #endif diff --git a/DataFormats/Detectors/ITSMFT/ITS/src/TimeEstBC.cxx b/DataFormats/Detectors/ITSMFT/ITS/src/TimeEstBC.cxx new file mode 100644 index 0000000000000..3af299cf74d25 --- /dev/null +++ b/DataFormats/Detectors/ITSMFT/ITS/src/TimeEstBC.cxx @@ -0,0 +1,13 @@ +// 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 "DataFormatsITS/TimeEstBC.h" +ClassImp(o2::its::TimeEstBC); \ No newline at end of file diff --git a/DataFormats/Detectors/ITSMFT/common/CMakeLists.txt b/DataFormats/Detectors/ITSMFT/common/CMakeLists.txt index 96d376526a1a4..a619f8ad0081d 100644 --- a/DataFormats/Detectors/ITSMFT/common/CMakeLists.txt +++ b/DataFormats/Detectors/ITSMFT/common/CMakeLists.txt @@ -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. # @@ -20,13 +20,18 @@ o2_add_library(DataFormatsITSMFT src/TopologyDictionary.cxx src/TimeDeadMap.cxx src/CTF.cxx + src/DPLAlpideParam.cxx + src/DPLAlpideParamInitializer.cxx PUBLIC_LINK_LIBRARIES O2::ITSMFTBase + O2::DetectorsCommonDataFormats O2::ReconstructionDataFormats + O2::CommonUtils Microsoft.GSL::GSL) o2_target_root_dictionary(DataFormatsITSMFT HEADERS include/DataFormatsITSMFT/ROFRecord.h include/DataFormatsITSMFT/Digit.h + include/DataFormatsITSMFT/DPLAlpideParam.h include/DataFormatsITSMFT/GBTCalibData.h include/DataFormatsITSMFT/NoiseMap.h include/DataFormatsITSMFT/TimeDeadMap.h diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/CTF.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/CTF.h index 314523aa878ba..0510b6df5225c 100644 --- a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/CTF.h +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/CTF.h @@ -36,7 +36,9 @@ struct CTFHeader : public o2::ctf::CTFDictHeader { uint32_t nPatternBytes = 0; /// number of bytes for explict patterns uint32_t firstOrbit = 0; /// 1st orbit of TF uint16_t firstBC = 0; /// 1st BC of TF - ClassDefNV(CTFHeader, 2); + uint8_t maxStreams = 1; /// Number of streams per TF (== NLayers for staggered ITS/MFT readout, 1 for non-staggered one) + uint8_t streamID = 0; /// ID of the stream (0:maxStreams-1) + ClassDefNV(CTFHeader, 3); }; /// Compressed but not yet entropy-encoded clusters diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/DPLAlpideParam.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/DPLAlpideParam.h new file mode 100644 index 0000000000000..a06ba0745edbd --- /dev/null +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/DPLAlpideParam.h @@ -0,0 +1,104 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef ALICEO2_ITSMFTALPIDEPARAM_H_ +#define ALICEO2_ITSMFTALPIDEPARAM_H_ + +#include "DetectorsCommonDataFormats/DetID.h" +#include "CommonUtils/ConfigurableParam.h" +#include "CommonUtils/ConfigurableParamHelper.h" +#include "CommonConstants/LHCConstants.h" +#include + +namespace o2 +{ +namespace itsmft +{ +constexpr float DEFStrobeDelay = o2::constants::lhc::LHCBunchSpacingNS * 4; // ~100 ns delay + +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 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 + + // get ROF length for any layer + int getROFLengthInBC(int layer) const noexcept { return roFrameLayerLengthInBC[layer] ? roFrameLayerLengthInBC[layer] : roFrameLengthInBC; } + int getROFBiasInBC(int layer) const noexcept { return roFrameLayerBiasInBC[layer] ? roFrameLayerBiasInBC[layer] : roFrameBiasInBC; } + int getROFDelayInBC(int layer) const noexcept { return roFrameLayerDelayInBC[layer] ? roFrameLayerDelayInBC[layer] : 0; } + + // boilerplate stuff + make principal key + O2ParamDef(DPLAlpideParam, getParamName().data()); + + private: + static constexpr std::string_view ParamName[2] = {"ITSAlpideParam", "MFTAlpideParam"}; + + static constexpr int DEFROFLengthBC() + { + // default ROF length in BC for continuous mode + // allowed values: 1,2,3,4,6,9,11,12,18,22,27,33,36 + return N == o2::detectors::DetID::ITS ? o2::constants::lhc::LHCMaxBunches / 4 : o2::constants::lhc::LHCMaxBunches / 18; + } + static constexpr float DEFROFLengthTrig() + { + // length of RO frame in ns for triggered mode + return N == o2::detectors::DetID::ITS ? 6000. : 6000.; + } + + static constexpr int DEFROFBiasInBC() + { + // default ROF length bias in MC, see https://github.com/AliceO2Group/AliceO2/pull/11108 for ITS + return N == o2::detectors::DetID::ITS ? 64 : 60; + } + + static_assert(N == o2::detectors::DetID::ITS || N == o2::detectors::DetID::MFT, "only DetID::ITS orDetID:: MFT are allowed"); + static_assert(o2::constants::lhc::LHCMaxBunches % DEFROFLengthBC() == 0); // make sure ROF length is divisor of the orbit +}; + +template +DPLAlpideParam DPLAlpideParam::sInstance; + +} // namespace itsmft + +namespace framework +{ +template +struct is_messageable; +template <> +struct is_messageable> : std::true_type { +}; +template +struct is_messageable; +template <> +struct is_messageable> : std::true_type { +}; + +} // namespace framework + +} // namespace o2 + +#endif diff --git a/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/DPLAlpideParamInitializer.h b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/DPLAlpideParamInitializer.h new file mode 100644 index 0000000000000..b3ec20f2a68b7 --- /dev/null +++ b/DataFormats/Detectors/ITSMFT/common/include/DataFormatsITSMFT/DPLAlpideParamInitializer.h @@ -0,0 +1,42 @@ +// 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. + +#ifndef ALICEO2_ITSMFTALPIDEPARAM_INITIALIZER_H_ +#define ALICEO2_ITSMFTALPIDEPARAM_INITIALIZER_H_ +#include + +namespace o2 +{ +namespace framework +{ +class ConfigParamSpec; +class ConfigContext; +} // namespace framework +namespace itsmft +{ + +struct DPLAlpideParamInitializer { + static constexpr char stagITSOpt[] = "enable-its-staggering"; + static constexpr char stagMFTOpt[] = "enable-mft-staggering"; + static constexpr bool stagDef = false; + + // DPL workflow options for staggering + static void addConfigOption(std::vector& opts); + static void addITSConfigOption(std::vector& opts); + static bool isITSStaggeringEnabled(o2::framework::ConfigContext const& cfgc); + static void addMFTConfigOption(std::vector& opts); + static bool isMFTStaggeringEnabled(o2::framework::ConfigContext const& cfgc); +}; + +} // namespace itsmft +} // namespace o2 + +#endif diff --git a/Detectors/ITSMFT/common/base/src/DPLAlpideParam.cxx b/DataFormats/Detectors/ITSMFT/common/src/DPLAlpideParam.cxx similarity index 82% rename from Detectors/ITSMFT/common/base/src/DPLAlpideParam.cxx rename to DataFormats/Detectors/ITSMFT/common/src/DPLAlpideParam.cxx index 1cb9bdf997d68..205f8a008a661 100644 --- a/Detectors/ITSMFT/common/base/src/DPLAlpideParam.cxx +++ b/DataFormats/Detectors/ITSMFT/common/src/DPLAlpideParam.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,15 +9,12 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" -namespace o2 -{ -namespace itsmft +namespace o2::itsmft { // this makes sure that the constructor of the parameters is statically called // so that these params are part of the parameter database static auto& sAlpideParamITS = o2::itsmft::DPLAlpideParam::Instance(); static auto& sAlpideParamMFT = o2::itsmft::DPLAlpideParam::Instance(); -} // namespace itsmft -} // namespace o2 +} // namespace o2::itsmft diff --git a/DataFormats/Detectors/ITSMFT/common/src/DPLAlpideParamInitializer.cxx b/DataFormats/Detectors/ITSMFT/common/src/DPLAlpideParamInitializer.cxx new file mode 100644 index 0000000000000..715ec5d90b813 --- /dev/null +++ b/DataFormats/Detectors/ITSMFT/common/src/DPLAlpideParamInitializer.cxx @@ -0,0 +1,46 @@ +// 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 "DataFormatsITSMFT/DPLAlpideParamInitializer.h" +#include "Framework/ConfigParamsHelper.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/ConfigContext.h" + +namespace o2::itsmft +{ + +void DPLAlpideParamInitializer::addConfigOption(std::vector& opts) +{ + addITSConfigOption(opts); + addMFTConfigOption(opts); +} + +void DPLAlpideParamInitializer::addITSConfigOption(std::vector& opts) +{ + o2::framework::ConfigParamsHelper::addOptionIfMissing(opts, {stagITSOpt, o2::framework::VariantType::Bool, stagDef, {"enable per layer ITS in&out-put for staggered readout"}}); +} + +void DPLAlpideParamInitializer::addMFTConfigOption(std::vector& opts) +{ + o2::framework::ConfigParamsHelper::addOptionIfMissing(opts, {stagMFTOpt, o2::framework::VariantType::Bool, stagDef, {"enable per layer MFT in&out-put for staggered readout"}}); +} + +bool DPLAlpideParamInitializer::isITSStaggeringEnabled(const o2::framework::ConfigContext& cfgc) +{ + return cfgc.options().get(stagITSOpt); +} + +bool DPLAlpideParamInitializer::isMFTStaggeringEnabled(const o2::framework::ConfigContext& cfgc) +{ + return cfgc.options().get(stagMFTOpt); +} + +} // namespace o2::itsmft diff --git a/DataFormats/Detectors/ITSMFT/common/src/ITSMFTDataFormatsLinkDef.h b/DataFormats/Detectors/ITSMFT/common/src/ITSMFTDataFormatsLinkDef.h index fc67fdf028436..1b1918b46c9d4 100644 --- a/DataFormats/Detectors/ITSMFT/common/src/ITSMFTDataFormatsLinkDef.h +++ b/DataFormats/Detectors/ITSMFT/common/src/ITSMFTDataFormatsLinkDef.h @@ -15,6 +15,11 @@ #pragma link off all classes; #pragma link off all functions; +#pragma link C++ class o2::itsmft::DPLAlpideParam < o2::detectors::DetID::ITS> + ; +#pragma link C++ class o2::itsmft::DPLAlpideParam < o2::detectors::DetID::MFT> + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::itsmft::DPLAlpideParam < o2::detectors::DetID::ITS>> + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::itsmft::DPLAlpideParam < o2::detectors::DetID::MFT>> + ; + #pragma link C++ class o2::itsmft::Digit + ; #pragma link C++ class o2::itsmft::NoiseMap + ; #pragma link C++ class o2::itsmft::TimeDeadMap + ; diff --git a/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h b/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h index cb1c9d5d87c7f..588a23d25a000 100644 --- a/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h +++ b/DataFormats/Reconstruction/include/ReconstructionDataFormats/Vertex.h @@ -152,7 +152,10 @@ class Vertex : public VertexBase std::string asString() const; #endif - GPUd() ushort getNContributors() const { return mNContributors; } + GPUd() ushort getNContributors() const + { + return mNContributors; + } GPUd() void setNContributors(ushort v) { mNContributors = v; } GPUd() void addContributor() { mNContributors++; } @@ -184,12 +187,26 @@ namespace detail { template concept Streamable = requires(std::ostream& os, const T& a) { - { os << a } -> std::same_as; + { + os << a + } + -> std::same_as; }; template concept HasFormattableTimeStamp = requires(const T& t) { - { fmt::format("{}", t.getTimeStamp()) } -> std::convertible_to; + { + fmt::format("{}", t.getTimeStamp()) + } + -> std::convertible_to; +}; + +template +concept HasFormattableTimeStampWithError = requires(const T& t) { + { + fmt::format("{}+-{}", t.getTimeStamp(), t.getTimeStampError()) + } + -> std::convertible_to; }; } // namespace detail @@ -201,6 +218,8 @@ inline std::string Vertex::asString() const std::ostringstream oss; oss << mTimeStamp; return oss.str(); + } else if constexpr (detail::HasFormattableTimeStampWithError) { + return fmt::format("{}+-{}", mTimeStamp.getTimeStamp(), mTimeStamp.getTimeStampError()); } else if constexpr (detail::HasFormattableTimeStamp) { return fmt::format("{}", mTimeStamp.getTimeStamp()); } else { diff --git a/DataFormats/common/include/CommonDataFormat/TimeStamp.h b/DataFormats/common/include/CommonDataFormat/TimeStamp.h index 56a71414c6b86..709af221c28f8 100644 --- a/DataFormats/common/include/CommonDataFormat/TimeStamp.h +++ b/DataFormats/common/include/CommonDataFormat/TimeStamp.h @@ -27,10 +27,10 @@ class TimeStamp public: GPUhdDefault() TimeStamp() = default; GPUhdDefault() ~TimeStamp() = default; - GPUdi() TimeStamp(T time) { mTimeStamp = time; } + GPUhdi() TimeStamp(T time) { mTimeStamp = time; } GPUhdi() T getTimeStamp() const { return mTimeStamp; } - GPUdi() void setTimeStamp(T t) { mTimeStamp = t; } - GPUdi() bool operator==(const TimeStamp& t) const { return mTimeStamp == t.mTimeStamp; } + GPUhdi() void setTimeStamp(T t) { mTimeStamp = t; } + GPUhdi() bool operator==(const TimeStamp& t) const { return mTimeStamp == t.mTimeStamp; } private: T mTimeStamp = 0; @@ -41,11 +41,11 @@ template class TimeStampWithError : public TimeStamp { public: - GPUdDefault() TimeStampWithError() = default; - GPUd() TimeStampWithError(T t, E te) : TimeStamp(t), mTimeStampError(te) {} - GPUdi() E getTimeStampError() const { return mTimeStampError; } - GPUdi() E getTimeStampError2() const { return mTimeStampError * mTimeStampError; } - GPUdi() void setTimeStampError(E te) { mTimeStampError = te; } + GPUhdDefault() TimeStampWithError() = default; + GPUhd() TimeStampWithError(T t, E te) : TimeStamp(t), mTimeStampError(te) {} + GPUhdi() E getTimeStampError() const { return mTimeStampError; } + GPUhdi() E getTimeStampError2() const { return mTimeStampError * mTimeStampError; } + GPUhdi() void setTimeStampError(E te) { mTimeStampError = te; } private: E mTimeStampError = 0; diff --git a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx index 80b9e6ef4b551..afff39791e4ec 100644 --- a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx +++ b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx @@ -60,7 +60,7 @@ #include "GlobalTracking/MatchGlobalFwd.h" #include "MCHTracking/TrackExtrap.h" #include "MCHTracking/TrackParam.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DetectorsVertexing/PVertexerParams.h" #include "ReconstructionDataFormats/GlobalFwdTrack.h" #include "ReconstructionDataFormats/GlobalTrackID.h" diff --git a/Detectors/AOD/src/aod-producer-workflow.cxx b/Detectors/AOD/src/aod-producer-workflow.cxx index f6bfaae170bbd..d75694f3bd512 100644 --- a/Detectors/AOD/src/aod-producer-workflow.cxx +++ b/Detectors/AOD/src/aod-producer-workflow.cxx @@ -18,6 +18,7 @@ #include "DetectorsRaw/HBFUtilsInitializer.h" #include "Framework/CallbacksPolicy.h" #include "DetectorsBase/DPLWorkflowUtils.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -43,6 +44,7 @@ void customize(std::vector& workflowOptions) {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}, {"combine-source-devices", o2::framework::VariantType::Bool, false, {"merge DPL source devices"}}, {"ctpconfig-run-independent", o2::framework::VariantType::Bool, false, {"Use CTP config w/o runNumber tag"}}}; + o2::itsmft::DPLAlpideParamInitializer::addConfigOption(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } diff --git a/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx b/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx index d4ab53c8181ce..2e63a1a65483c 100644 --- a/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx +++ b/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx @@ -64,7 +64,7 @@ #include "DataFormatsTPC/ClusterNative.h" #include "DataFormatsTPC/WorkflowHelper.h" #include "ITSBase/GeometryTGeo.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" */ using namespace o2::framework; diff --git a/Detectors/Align/Workflow/src/barrel-alignment-workflow.cxx b/Detectors/Align/Workflow/src/barrel-alignment-workflow.cxx index 8df479ba39260..cdd0620affec9 100644 --- a/Detectors/Align/Workflow/src/barrel-alignment-workflow.cxx +++ b/Detectors/Align/Workflow/src/barrel-alignment-workflow.cxx @@ -27,6 +27,7 @@ #include "ReconstructionDataFormats/GlobalTrackID.h" #include "DetectorsCommonDataFormats/DetID.h" #include "GlobalTrackingWorkflowReaders/TrackTPCITSReaderSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "Algorithm/RangeTokenizer.h" #include "DetectorsRaw/HBFUtilsInitializer.h" @@ -59,6 +60,7 @@ void customize(std::vector& workflowOptions) {"enable-cosmic", VariantType::Bool, false, {"enable cosmic tracks)"}}, {"postprocessing", VariantType::Int, 0, {"postprocessing bits: 1 - extract alignment objects, 2 - check constraints, 4 - print mpParams/Constraints, 8 - relabel pede results"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); diff --git a/Detectors/CTF/test/test_ctf_io_itsmft.cxx b/Detectors/CTF/test/test_ctf_io_itsmft.cxx index 13cbdf7745961..7f2ff8ce9f340 100644 --- a/Detectors/CTF/test/test_ctf_io_itsmft.cxx +++ b/Detectors/CTF/test/test_ctf_io_itsmft.cxx @@ -81,7 +81,7 @@ BOOST_DATA_TEST_CASE(CompressedClustersTest, boost_data::make(ANSVersions), ansV sw.Start(); std::vector vec; { - CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder, o2::detectors::DetID::ITS); + CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Encoder, false); coder.setANSVersion(ansVersion); coder.encode(vec, rofRecVec, cclusVec, pattVec, pattIdConverter, 0); // compress } @@ -120,7 +120,7 @@ BOOST_DATA_TEST_CASE(CompressedClustersTest, boost_data::make(ANSVersions), ansV sw.Start(); const auto ctfImage = o2::itsmft::CTF::getImage(vec.data()); { - CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Decoder, o2::detectors::DetID::ITS); + CTFCoder coder(o2::ctf::CTFCoderBase::OpType::Decoder, false); coder.decode(ctfImage, rofRecVecD, cclusVecD, pattVecD, nullptr, clPattLookup); // decompress } sw.Stop(); diff --git a/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h b/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h index 081e6cf4d968a..51f2fca2c8303 100644 --- a/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h +++ b/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h @@ -27,6 +27,7 @@ struct CTFReaderInp { std::string inpdata{}; o2::detectors::DetID::mask_t detMask = o2::detectors::DetID::FullMask; std::string copyCmd{}; + std::string copyDir{}; std::string tffileRegex{}; std::string remoteRegex{}; std::string metricChannel{}; @@ -50,6 +51,8 @@ struct CTFReaderInp { int tfRateLimit = -999; size_t minSHM = 0; bool shuffle{false}; + bool doITSStaggering = false; + bool doMFTStaggering = false; }; /// create a processor spec diff --git a/Detectors/CTF/workflow/include/CTFWorkflow/CTFWriterSpec.h b/Detectors/CTF/workflow/include/CTFWorkflow/CTFWriterSpec.h index 5eb6d65e26cec..12ad483d90881 100644 --- a/Detectors/CTF/workflow/include/CTFWorkflow/CTFWriterSpec.h +++ b/Detectors/CTF/workflow/include/CTFWorkflow/CTFWriterSpec.h @@ -15,16 +15,23 @@ #define O2_CTFWRITER_SPEC #include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" #include "DetectorsCommonDataFormats/DetID.h" namespace o2 { namespace ctf { +struct CTFWriterInp { + o2::detectors::DetID::mask_t detMask = o2::detectors::DetID::FullMask; + int verbosity = 0; + int reportInterval = 200; + std::string outType = ""; + bool doITSStaggering = false; + bool doMFTStaggering = false; +}; /// create a processor spec -framework::DataProcessorSpec getCTFWriterSpec(o2::detectors::DetID::mask_t dets, const std::string& outType, int verbosity, int reportInterval); +framework::DataProcessorSpec getCTFWriterSpec(const o2::ctf::CTFWriterInp& inp); } // namespace ctf } // namespace o2 diff --git a/Detectors/CTF/workflow/src/CTFReaderSpec.cxx b/Detectors/CTF/workflow/src/CTFReaderSpec.cxx index 4100ebb37c61d..9fba8a220be55 100644 --- a/Detectors/CTF/workflow/src/CTFReaderSpec.cxx +++ b/Detectors/CTF/workflow/src/CTFReaderSpec.cxx @@ -35,6 +35,7 @@ #include "CommonUtils/NameConf.h" #include "DetectorsCommonDataFormats/CTFHeader.h" #include "Headers/STFHeader.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DataFormatsITSMFT/CTF.h" #include "DataFormatsTPC/CTF.h" #include "DataFormatsTRD/CTF.h" @@ -170,7 +171,7 @@ void CTFReaderSpec::init(InitContext& ic) mInput.maxTFsPerFile = ic.options().get("max-tf-per-file"); mInput.maxTFsPerFile = mInput.maxTFsPerFile > 0 ? mInput.maxTFsPerFile : 0x7fffffff; mRunning = true; - mFileFetcher = std::make_unique(mInput.inpdata, mInput.tffileRegex, mInput.remoteRegex, mInput.copyCmd); + mFileFetcher = std::make_unique(mInput.inpdata, mInput.tffileRegex, mInput.remoteRegex, mInput.copyCmd, mInput.copyDir); mFileFetcher->setMaxFilesInQueue(mInput.maxFileCache); mFileFetcher->setMaxLoops(mInput.maxLoops); mFileFetcher->setFailThreshold(ic.options().get("fetch-failure-threshold")); @@ -188,6 +189,48 @@ void CTFReaderSpec::init(InitContext& ic) } } +///_______________________________________ +template <> +void CTFReaderSpec::processDetector(DetID det, const CTFHeader& ctfHeader, ProcessingContext& pc) const +{ + if (mInput.detMask[det]) { + std::string lbl = det.getName(); + int nLayers = 1; + if (det == DetID::ITS) { + nLayers = mInput.doITSStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + } else if (det == DetID::MFT) { + nLayers = mInput.doMFTStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + } else { + LOGP(fatal, "This specialization is define only for ITS and MFT detectors, {} provided", det.getName()); + } + for (int iLayer = 0; iLayer < nLayers; iLayer++) { + auto& bufVec = pc.outputs().make>({lbl, mInput.subspec * 100 + iLayer}, ctfHeader.detectors[det] ? sizeof(o2::itsmft::CTF) : 0); + if (ctfHeader.detectors[det]) { + auto brName = nLayers == 1 ? lbl : fmt::format("{}_{}", lbl, iLayer); + o2::itsmft::CTF::readFromTree(bufVec, *(mCTFTree.get()), brName, mCurrTreeEntry); + } else if (!mInput.allowMissingDetectors) { + throw std::runtime_error(fmt::format("Requested detector {} is missing in the CTF", lbl)); + } + } + } +} + +///_______________________________________ +template +void CTFReaderSpec::processDetector(DetID det, const CTFHeader& ctfHeader, ProcessingContext& pc) const +{ + if (mInput.detMask[det]) { + const auto lbl = det.getName(); + auto& bufVec = pc.outputs().make>({lbl, mInput.subspec}, ctfHeader.detectors[det] ? sizeof(C) : 0); + if (ctfHeader.detectors[det]) { + C::readFromTree(bufVec, *(mCTFTree.get()), lbl, mCurrTreeEntry); + } else if (!mInput.allowMissingDetectors) { + throw std::runtime_error(fmt::format("Requested detector {} is missing in the CTF", lbl)); + } + // setMessageHeader(pc, ctfHeader, lbl); + } +} + void CTFReaderSpec::runTimeRangesToIRFrameSelector(const o2::framework::TimingInfo& timingInfo) { // convert entries in the runTimeRanges to IRFrameSelector, if needed, convert time to orbit @@ -562,22 +605,6 @@ void CTFReaderSpec::setMessageHeader(ProcessingContext& pc, const CTFHeader& ctf dph->creation = ctfHeader.creationTime; } -///_______________________________________ -template -void CTFReaderSpec::processDetector(DetID det, const CTFHeader& ctfHeader, ProcessingContext& pc) const -{ - if (mInput.detMask[det]) { - const auto lbl = det.getName(); - auto& bufVec = pc.outputs().make>({lbl, mInput.subspec}, ctfHeader.detectors[det] ? sizeof(C) : 0); - if (ctfHeader.detectors[det]) { - C::readFromTree(bufVec, *(mCTFTree.get()), lbl, mCurrTreeEntry); - } else if (!mInput.allowMissingDetectors) { - throw std::runtime_error(fmt::format("Requested detector {} is missing in the CTF", lbl)); - } - // setMessageHeader(pc, ctfHeader, lbl); - } -} - ///_______________________________________ void CTFReaderSpec::tryToFixCTFHeader(CTFHeader& ctfHeader) const { @@ -636,7 +663,19 @@ DataProcessorSpec getCTFReaderSpec(const CTFReaderInp& inp) for (auto id = DetID::First; id <= DetID::Last; id++) { if (inp.detMask[id]) { DetID det(id); - outputs.emplace_back(OutputLabel{det.getName()}, det.getDataOrigin(), "CTFDATA", inp.subspec, Lifetime::Timeframe); + if (det == DetID::ITS) { + uint32_t nLayers = inp.doITSStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + for (uint32_t iLayer = 0; iLayer < nLayers; iLayer++) { + outputs.emplace_back(OutputLabel{det.getName()}, det.getDataOrigin(), "CTFDATA", inp.subspec * 100 + iLayer, Lifetime::Timeframe); + } + } else if (det == DetID::MFT) { + uint32_t nLayers = inp.doMFTStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + for (uint32_t iLayer = 0; iLayer < nLayers; iLayer++) { + outputs.emplace_back(OutputLabel{det.getName()}, det.getDataOrigin(), "CTFDATA", inp.subspec * 100 + iLayer, Lifetime::Timeframe); + } + } else { + outputs.emplace_back(OutputLabel{det.getName()}, det.getDataOrigin(), "CTFDATA", inp.subspec, Lifetime::Timeframe); + } } } if (!inp.fileIRFrames.empty() || !inp.fileRunTimeSpans.empty()) { diff --git a/Detectors/CTF/workflow/src/CTFWriterSpec.cxx b/Detectors/CTF/workflow/src/CTFWriterSpec.cxx index ba4542969a712..5d6db7d613674 100644 --- a/Detectors/CTF/workflow/src/CTFWriterSpec.cxx +++ b/Detectors/CTF/workflow/src/CTFWriterSpec.cxx @@ -12,11 +12,10 @@ /// @file CTFWriterSpec.cxx #include "Framework/Logger.h" -#include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/InputSpec.h" +#include "Framework/Task.h" #include "Framework/RawDeviceService.h" -#include "Framework/CommonServices.h" #include "Framework/DataTakingContext.h" #include "Framework/TimingInfo.h" #include @@ -29,6 +28,7 @@ #include "DetectorsCommonDataFormats/EncodedBlocks.h" #include "DetectorsCommonDataFormats/FileMetaData.h" #include "CommonUtils/StringUtils.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DataFormatsITSMFT/CTF.h" #include "DataFormatsTPC/CTF.h" #include "DataFormatsTRD/CTF.h" @@ -94,17 +94,19 @@ size_t appendToTree(TTree& tree, const std::string brname, T& ptr) using DetID = o2::detectors::DetID; using FTrans = o2::rans::DenseHistogram; -class CTFWriterSpec : public o2::framework::Task +class CTFWriterSpec final : public o2::framework::Task { public: CTFWriterSpec() = delete; - CTFWriterSpec(DetID::mask_t dm, const std::string& outType, int verbosity, int reportInterval); + CTFWriterSpec(const o2::ctf::CTFWriterInp&); ~CTFWriterSpec() final { finalize(); } void init(o2::framework::InitContext& ic) final; void run(o2::framework::ProcessingContext& pc) final; void endOfStream(o2::framework::EndOfStreamContext& ec) final { finalize(); } void stop() final { finalize(); } - bool isPresent(DetID id) const { return mDets[id]; } + bool isPresent(DetID id) const { return mInput.detMask[id]; } + + static std::string getBinding(const std::string& name, int spec) { return fmt::format("{}_{}", name, spec); } private: void updateTimeDependentParams(ProcessingContext& pc); @@ -121,7 +123,7 @@ class CTFWriterSpec : public o2::framework::Task void removeLockFile(); void finalize(); - DetID::mask_t mDets; // detectors + CTFWriterInp mInput; bool mFinalized = false; bool mWriteCTF = true; bool mCreateDict = false; @@ -130,8 +132,6 @@ class CTFWriterSpec : public o2::framework::Task bool mRejectCurrentTF = false; bool mFallBackDirUsed = false; bool mFallBackDirProvided = false; - int mReportInterval = -1; - int mVerbosity = 0; int mSaveDictAfter = 0; // if positive and mWriteCTF==true, save dictionary after each mSaveDictAfter TFs processed uint32_t mPrevDictTimeStamp = 0; // timestamp of the previously stored dictionary uint32_t mDictTimeStamp = 0; // timestamp of the currently stored dictionary @@ -155,7 +155,6 @@ class CTFWriterSpec : public o2::framework::Task std::vector mTFOrbits{}; // 1st orbits of TF accumulated in current file o2::framework::DataTakingContext mDataTakingContext{}; o2::framework::TimingInfo mTimingInfo{}; - std::string mOutputType{}; // RS FIXME once global/local options clash is solved, --output-type will become device option std::string mDictDir{}; std::string mCTFDir{}; std::string mHostName{}; @@ -190,8 +189,8 @@ class CTFWriterSpec : public o2::framework::Task const std::string CTFWriterSpec::TMPFileEnding{".part"}; //___________________________________________________________________ -CTFWriterSpec::CTFWriterSpec(DetID::mask_t dm, const std::string& outType, int verbosity, int reportInterval) - : mDets(dm), mOutputType(outType), mReportInterval(reportInterval), mVerbosity(verbosity) +CTFWriterSpec::CTFWriterSpec(const o2::ctf::CTFWriterInp& inp) + : mInput(inp) { std::for_each(mIsSaturatedFrequencyTable.begin(), mIsSaturatedFrequencyTable.end(), [](auto& bitset) { bitset.reset(); }); mTimer.Stop(); @@ -202,7 +201,7 @@ CTFWriterSpec::CTFWriterSpec(DetID::mask_t dm, const std::string& outType, int v void CTFWriterSpec::init(InitContext& ic) { // auto outmode = ic.options().get("output-type"); // RS FIXME once global/local options clash is solved, --output-type will become device option - auto outmode = mOutputType; + auto outmode = mInput.outType; if (outmode == "ctf") { mWriteCTF = true; mCreateDict = false; @@ -301,71 +300,82 @@ size_t CTFWriterSpec::processDet(o2::framework::ProcessingContext& pc, DetID det { static bool warnedEmpty = false; size_t sz = 0; - if (!isPresent(det) || !pc.inputs().isValid(det.getName())) { + + if (!isPresent(det) || !pc.inputs().isValid(getBinding(det.getName(), 0))) { mSizeReport += fmt::format(" {}:N/A", det.getName()); return sz; } - auto ctfBuffer = pc.inputs().get>(det.getName()); - const o2::ctf::BufferType* bdata = ctfBuffer.data(); - if (bdata) { - if (warnedEmpty) { - throw std::runtime_error(fmt::format("Non-empty input was seen at {}-th TF after empty one for {}, this will lead to misalignment of detectors in CTF", mNCTF, det.getName())); - } - const auto ctfImage = C::getImage(bdata); - ctfImage.print(o2::utils::Str::concat_string(det.getName(), ": "), mVerbosity); - if (mWriteCTF && !mRejectCurrentTF) { - sz = ctfImage.appendToTree(*tree, det.getName()); - header.detectors.set(det); - } else { - sz = ctfBuffer.size(); - } - if (mCreateDict) { - if (mFreqsAccumulation[det].empty()) { - mFreqsAccumulation[det].resize(C::getNBlocks()); - mFreqsMetaData[det].resize(C::getNBlocks()); + + uint32_t nLayers = 1; + if (det == DetID::ITS) { + nLayers = mInput.doITSStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + } else if (det == DetID::MFT) { + nLayers = mInput.doMFTStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + } + for (uint32_t iLayer = 0; iLayer < nLayers; iLayer++) { + auto binding = getBinding(det.getName(), iLayer); + auto ctfBuffer = pc.inputs().get>(binding); + const o2::ctf::BufferType* bdata = ctfBuffer.data(); + if (bdata) { + if (warnedEmpty) { + throw std::runtime_error(fmt::format("Non-empty input was seen at {}-th TF after empty one for {}, this will lead to misalignment of detectors in CTF", mNCTF, det.getName())); } - if (!mHeaders[det]) { // store 1st header - mHeaders[det] = ctfImage.cloneHeader(); - auto& hb = *static_cast(mHeaders[det].get()); - hb.det = det; + const auto ctfImage = C::getImage(bdata); + ctfImage.print(o2::utils::Str::concat_string(binding, ": "), mInput.verbosity); + if (mWriteCTF && !mRejectCurrentTF) { + sz += ctfImage.appendToTree(*tree, nLayers > 1 ? binding : det.getName()); + header.detectors.set(det); + } else { + sz += ctfBuffer.size(); } - for (int ib = 0; ib < C::getNBlocks(); ib++) { - if (!mIsSaturatedFrequencyTable[det][ib]) { - const auto& bl = ctfImage.getBlock(ib); - if (bl.getNDict()) { - auto freq = mFreqsAccumulation[det][ib]; - auto& mdSave = mFreqsMetaData[det][ib]; - const auto& md = ctfImage.getMetadata(ib); - if ([&, this]() { - try { - freq.addFrequencies(bl.getDict(), bl.getDict() + bl.getNDict(), md.min); - } catch (const std::overflow_error& e) { - LOGP(warning, "unable to add frequency table for {}, block {} due to overflow", det.getName(), ib); - mIsSaturatedFrequencyTable[det][ib] = true; - return false; - } - return true; - }()) { - auto newProbBits = static_cast(o2::rans::compat::computeRenormingPrecision(countNUsedAlphabetSymbols(freq))); - auto histogramView = o2::rans::trim(o2::rans::makeHistogramView(freq)); - mdSave = ctf::detail::makeMetadataRansDict(newProbBits, - static_cast(histogramView.getMin()), - static_cast(histogramView.getMax()), - static_cast(histogramView.size()), - md.opt); - mFreqsAccumulation[det][ib] = std::move(freq); + if (mCreateDict) { // RSTODO + if (mFreqsAccumulation[det].empty()) { + mFreqsAccumulation[det].resize(C::getNBlocks()); + mFreqsMetaData[det].resize(C::getNBlocks()); + } + if (!mHeaders[det]) { // store 1st header + mHeaders[det] = ctfImage.cloneHeader(); + auto& hb = *static_cast(mHeaders[det].get()); + hb.det = det; + } + for (int ib = 0; ib < C::getNBlocks(); ib++) { + if (!mIsSaturatedFrequencyTable[det][ib]) { + const auto& bl = ctfImage.getBlock(ib); + if (bl.getNDict()) { + auto freq = mFreqsAccumulation[det][ib]; + auto& mdSave = mFreqsMetaData[det][ib]; + const auto& md = ctfImage.getMetadata(ib); + if ([&, this]() { + try { + freq.addFrequencies(bl.getDict(), bl.getDict() + bl.getNDict(), md.min); + } catch (const std::overflow_error& e) { + LOGP(warning, "unable to add frequency table for {}, block {} due to overflow", det.getName(), ib); + mIsSaturatedFrequencyTable[det][ib] = true; + return false; + } + return true; + }()) { + auto newProbBits = static_cast(o2::rans::compat::computeRenormingPrecision(countNUsedAlphabetSymbols(freq))); + auto histogramView = o2::rans::trim(o2::rans::makeHistogramView(freq)); + mdSave = ctf::detail::makeMetadataRansDict(newProbBits, + static_cast(histogramView.getMin()), + static_cast(histogramView.getMax()), + static_cast(histogramView.size()), + md.opt); + mFreqsAccumulation[det][ib] = std::move(freq); + } } } } } - } - } else { - if (!warnedEmpty) { - if (mNCTF) { - throw std::runtime_error(fmt::format("Empty input was seen at {}-th TF after non-empty one for {}, this will lead to misalignment of detectors in CTF", mNCTF, det.getName())); + } else { + if (!warnedEmpty) { + if (mNCTF) { + throw std::runtime_error(fmt::format("Empty input was seen at {}-th TF after non-empty one for {}, this will lead to misalignment of detectors in CTF", mNCTF, det.getName())); + } + LOGP(important, "Empty CTF provided for {}, skipping and will not report anymore", det.getName()); + warnedEmpty = true; } - LOGP(important, "Empty CTF provided for {}, skipping and will not report anymore", det.getName()); - warnedEmpty = true; } } mSizeReport += fmt::format(" {}:{}", det.getName(), fmt::group_digits(sz)); @@ -417,10 +427,19 @@ size_t CTFWriterSpec::estimateCTFSize(ProcessingContext& pc) size_t s = 0; for (auto id = DetID::First; id <= DetID::Last; id++) { DetID det(id); - if (!isPresent(det) || !pc.inputs().isValid(det.getName())) { - continue; + uint32_t nLayers = 1; + if (det == DetID::ITS) { + nLayers = mInput.doITSStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + } else if (det == DetID::MFT) { + nLayers = mInput.doMFTStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + } + for (uint32_t iLayer = 0; iLayer < nLayers; iLayer++) { + auto binding = getBinding(det.getName(), iLayer); + if (!isPresent(det) || !pc.inputs().isValid(binding)) { + continue; + } + s += pc.inputs().get>(binding).size(); } - s += pc.inputs().get>(det.getName()).size(); } return s; } @@ -496,7 +515,7 @@ void CTFWriterSpec::run(ProcessingContext& pc) szCTFperDet[DetID::FDD] = processDet(pc, DetID::FDD, header, mCTFTreeOut.get()); szCTFperDet[DetID::CTP] = processDet(pc, DetID::CTP, header, mCTFTreeOut.get()); szCTF = std::accumulate(szCTFperDet.begin(), szCTFperDet.end(), 0); - if (mReportInterval > 0 && (mTimingInfo.tfCounter % mReportInterval) == 0) { + if (mInput.reportInterval > 0 && (mTimingInfo.tfCounter % mInput.reportInterval) == 0) { LOGP(important, "CTF {} size report:{} - Total:{}", mTimingInfo.tfCounter, mSizeReport, fmt::group_digits(szCTF)); } @@ -660,7 +679,7 @@ void CTFWriterSpec::storeDictionaries() // monolitic dictionary in tree format mDictTimeStamp = uint32_t(std::time(nullptr)); auto getFileName = [this](bool curr) { - return fmt::format("{}{}Tree_{}_{}_{}.root", this->mDictDir, o2::base::NameConf::CTFDICT, DetID::getNames(this->mDets, '-'), curr ? this->mDictTimeStamp : this->mPrevDictTimeStamp, curr ? this->mNCTF : this->mNCTFPrevDict); + return fmt::format("{}{}Tree_{}_{}_{}.root", this->mDictDir, o2::base::NameConf::CTFDICT, DetID::getNames(this->mInput.detMask, '-'), curr ? this->mDictTimeStamp : this->mPrevDictTimeStamp, curr ? this->mNCTF : this->mNCTFPrevDict); }; auto dictFileName = getFileName(true); mDictFileOut.reset(TFile::Open(dictFileName.c_str(), "recreate")); @@ -788,13 +807,22 @@ size_t CTFWriterSpec::getAvailableDiskSpace(const std::string& path, int level) } //___________________________________________________________________ -DataProcessorSpec getCTFWriterSpec(DetID::mask_t dets, const std::string& outType, int verbosity, int reportInterval) +DataProcessorSpec getCTFWriterSpec(const o2::ctf::CTFWriterInp& inp) { std::vector inputs; LOG(debug) << "Detectors list:"; for (auto id = DetID::First; id <= DetID::Last; id++) { - if (dets[id]) { - inputs.emplace_back(DetID::getName(id), DetID::getDataOrigin(id), "CTFDATA", 0, Lifetime::Timeframe); + if (inp.detMask[id]) { + uint32_t nLayers = 1; + DetID det{id}; + if (det == DetID::ITS) { + nLayers = inp.doITSStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + } else if (det == DetID::MFT) { + nLayers = inp.doMFTStaggering ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + } + for (uint32_t iLayer = 0; iLayer < nLayers; iLayer++) { + inputs.emplace_back(CTFWriterSpec::getBinding(det.getName(), iLayer), det.getDataOrigin(), "CTFDATA", iLayer, Lifetime::Timeframe); + } LOG(debug) << "Det " << DetID::getName(id) << " added"; } } @@ -803,24 +831,25 @@ DataProcessorSpec getCTFWriterSpec(DetID::mask_t dets, const std::string& outTyp inputs, Outputs{{OutputLabel{"ctfdone"}, "CTF", "DONE", 0, Lifetime::Timeframe}, {"CTF", "SIZES", 0, Lifetime::Timeframe}}, - AlgorithmSpec{adaptFromTask(dets, outType, verbosity, reportInterval)}, // RS FIXME once global/local options clash is solved, --output-type will become device option - Options{ //{"output-type", VariantType::String, "ctf", {"output types: ctf (per TF) or dict (create dictionaries) or both or none"}}, - {"save-ctf-after", VariantType::Int64, 0ll, {"autosave CTF tree with multiple CTFs after every N CTFs if >0 or every -N MBytes if < 0"}}, - {"save-dict-after", VariantType::Int, 0, {"if > 0, in dictionary generation mode save it dictionary after certain number of TFs processed"}}, - {"ctf-dict-dir", VariantType::String, "none", {"CTF dictionary directory, must exist"}}, - {"output-dir", VariantType::String, "none", {"CTF output directory, must exist"}}, - {"output-dir-alt", VariantType::String, "/dev/null", {"Alternative CTF output directory, must exist (if not /dev/null)"}}, - {"meta-output-dir", VariantType::String, "/dev/null", {"CTF metadata output directory, must exist (if not /dev/null)"}}, - {"md5-for-meta", VariantType::Bool, false, {"fill CTF file MD5 sum in the metadata file"}}, - {"min-file-size", VariantType::Int64, 0l, {"accumulate CTFs until given file size reached"}}, - {"max-file-size", VariantType::Int64, 0l, {"if > 0, try to avoid exceeding given file size, also used for space check"}}, - {"max-ctf-per-file", VariantType::Int, 0, {"if > 0, avoid storing more than requested CTFs per file"}}, - {"ctf-rejection", VariantType::Int, 0, {">0: percentage to reject randomly, <0: reject if timeslice%|value|!=0"}}, - {"ctf-file-compression", VariantType::Int, 0, {"if >= 0: impose CTF file compression level"}}, - {"require-free-disk", VariantType::Float, 0.f, {"pause writing op. if available disk space is below this margin, in bytes if >0, as a fraction of total if <0"}}, - {"wait-for-free-disk", VariantType::Float, 10.f, {"if paused due to the low disk space, recheck after this time (in s)"}}, - {"max-wait-for-free-disk", VariantType::Float, 60.f, {"produce fatal if paused due to the low disk space for more than this amount in s."}}, - {"ignore-partition-run-dir", VariantType::Bool, false, {"Do not creare partition-run directory in output-dir"}}}}; + AlgorithmSpec{adaptFromTask(inp)}, + Options{ + //{"output-type", VariantType::String, "ctf", {"output types: ctf (per TF) or dict (create dictionaries) or both or none"}}, + {"save-ctf-after", VariantType::Int64, 0ll, {"autosave CTF tree with multiple CTFs after every N CTFs if >0 or every -N MBytes if < 0"}}, + {"save-dict-after", VariantType::Int, 0, {"if > 0, in dictionary generation mode save it dictionary after certain number of TFs processed"}}, + {"ctf-dict-dir", VariantType::String, "none", {"CTF dictionary directory, must exist"}}, + {"output-dir", VariantType::String, "none", {"CTF output directory, must exist"}}, + {"output-dir-alt", VariantType::String, "/dev/null", {"Alternative CTF output directory, must exist (if not /dev/null)"}}, + {"meta-output-dir", VariantType::String, "/dev/null", {"CTF metadata output directory, must exist (if not /dev/null)"}}, + {"md5-for-meta", VariantType::Bool, false, {"fill CTF file MD5 sum in the metadata file"}}, + {"min-file-size", VariantType::Int64, 0l, {"accumulate CTFs until given file size reached"}}, + {"max-file-size", VariantType::Int64, 0l, {"if > 0, try to avoid exceeding given file size, also used for space check"}}, + {"max-ctf-per-file", VariantType::Int, 0, {"if > 0, avoid storing more than requested CTFs per file"}}, + {"ctf-rejection", VariantType::Int, 0, {">0: percentage to reject randomly, <0: reject if timeslice%|value|!=0"}}, + {"ctf-file-compression", VariantType::Int, 0, {"if >= 0: impose CTF file compression level"}}, + {"require-free-disk", VariantType::Float, 0.f, {"pause writing op. if available disk space is below this margin, in bytes if >0, as a fraction of total if <0"}}, + {"wait-for-free-disk", VariantType::Float, 10.f, {"if paused due to the low disk space, recheck after this time (in s)"}}, + {"max-wait-for-free-disk", VariantType::Float, 60.f, {"produce fatal if paused due to the low disk space for more than this amount in s."}}, + {"ignore-partition-run-dir", VariantType::Bool, false, {"Do not creare partition-run directory in output-dir"}}}}; } } // namespace ctf diff --git a/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx b/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx index fc50c971c5d20..366fa76f74983 100644 --- a/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx +++ b/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx @@ -25,6 +25,7 @@ // Specific detectors specs #include "ITSMFTWorkflow/EntropyDecoderSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "TPCWorkflow/EntropyDecoderSpec.h" #include "TRDWorkflow/EntropyDecoderSpec.h" #include "HMPIDWorkflow/EntropyDecoderSpec.h" @@ -59,6 +60,7 @@ void customize(std::vector& workflowOptions) options.push_back(ConfigParamSpec{"delay", VariantType::Float, 0.f, {"delay in seconds between consecutive TFs sending"}}); options.push_back(ConfigParamSpec{"shuffle", VariantType::Bool, false, {"shuffle TF sending order (for debug)"}}); options.push_back(ConfigParamSpec{"copy-cmd", VariantType::String, "alien_cp ?src file://?dst", {"copy command for remote files or no-copy to avoid copying"}}); // Use "XrdSecPROTOCOL=sss,unix xrdcp -N root://eosaliceo2.cern.ch/?src ?dst" for direct EOS access + options.push_back(ConfigParamSpec{"copy-dir", VariantType::String, "/tmp/", {"copy base directory for remote files"}}); options.push_back(ConfigParamSpec{"ctf-file-regex", VariantType::String, ".*o2_ctf_run.+\\.root$", {"regex string to identify CTF files"}}); options.push_back(ConfigParamSpec{"remote-regex", VariantType::String, "^(alien://|)/alice/data/.+", {"regex string to identify remote files"}}); // Use "^/eos/aliceo2/.+" for direct EOS access options.push_back(ConfigParamSpec{"max-cached-files", VariantType::Int, 3, {"max CTF files queued (copied for remote source)"}}); @@ -80,6 +82,7 @@ void customize(std::vector& workflowOptions) options.push_back(ConfigParamSpec{"timeframes-shm-limit", VariantType::String, "0", {"Minimum amount of SHM required in order to publish data"}}); options.push_back(ConfigParamSpec{"metric-feedback-channel-format", VariantType::String, "name=metric-feedback,type=pull,method=connect,address=ipc://{}metric-feedback-{},transport=shmem,rateLogging=0", {"format for the metric-feedback channel for TF rate limiting"}}); options.push_back(ConfigParamSpec{"combine-devices", VariantType::Bool, false, {"combine multiple DPL devices (entropy decoders)"}}); + o2::itsmft::DPLAlpideParamInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -124,6 +127,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) ctfInput.shuffle = configcontext.options().get("shuffle"); ctfInput.copyCmd = configcontext.options().get("copy-cmd"); + ctfInput.copyDir = configcontext.options().get("copy-dir"); ctfInput.tffileRegex = configcontext.options().get("ctf-file-regex"); ctfInput.remoteRegex = configcontext.options().get("remote-regex"); ctfInput.allowMissingDetectors = configcontext.options().get("allow-missing-detectors"); @@ -147,6 +151,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) if (!ctfInput.fileIRFrames.empty() && !ctfInput.fileRunTimeSpans.empty()) { LOGP(fatal, "One cannot provide --ir-frames-files and --run-time-span-file options simultaneously"); } + ctfInput.doITSStaggering = o2::itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(configcontext); + ctfInput.doMFTStaggering = o2::itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(configcontext); specs.push_back(o2::ctf::getCTFReaderSpec(ctfInput)); @@ -183,10 +189,12 @@ 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, ctfInput.dictOpt)); + bool doStag = o2::itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(configcontext); + addSpecs(o2::itsmft::getITSEntropyDecoderSpec(verbosity, doStag, 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, ctfInput.dictOpt)); + bool doStag = o2::itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(configcontext); + addSpecs(o2::itsmft::getMFTEntropyDecoderSpec(verbosity, doStag, configcontext.options().get("mft-digits"), ctfInput.subspec, ctfInput.dictOpt)); } if (ctfInput.detMask[DetID::TPC]) { addSpecs(o2::tpc::getEntropyDecoderSpec(verbosity, ctfInput.subspec, ctfInput.dictOpt)); diff --git a/Detectors/CTF/workflow/src/ctf-writer-workflow.cxx b/Detectors/CTF/workflow/src/ctf-writer-workflow.cxx index 2757192727521..77dbbd80bc1a7 100644 --- a/Detectors/CTF/workflow/src/ctf-writer-workflow.cxx +++ b/Detectors/CTF/workflow/src/ctf-writer-workflow.cxx @@ -20,6 +20,7 @@ #include "CTFWorkflow/CTFWriterSpec.h" #include "DetectorsCommonDataFormats/DetID.h" #include "CommonUtils/ConfigurableParam.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using DetID = o2::detectors::DetID; @@ -35,6 +36,7 @@ void customize(std::vector& workflowOptions) options.push_back(ConfigParamSpec{"ctf-writer-verbosity", VariantType::Int, 0, {"verbosity level (0: summary per detector, 1: summary per block"}}); options.push_back(ConfigParamSpec{"report-data-size-interval", VariantType::Int, 200, {"report sizes per detector for every N-th timeframe"}}); options.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}); + o2::itsmft::DPLAlpideParamInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -51,7 +53,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { DetID::mask_t dets = 0; o2::conf::ConfigurableParam::updateFromString(configcontext.options().get("configKeyValues")); - std::string outType{}; // RS FIXME once global/local options clash is solved, --output-type will become device option + o2::ctf::CTFWriterInp inp; if (!configcontext.helpOnCommandLine()) { dets.set(); // by default read all auto mskOnly = DetID::getMask(configcontext.options().get("onlyDet")); @@ -64,10 +66,14 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) if (dets.none()) { throw std::invalid_argument("Invalid workflow: no detectors found"); } - outType = configcontext.options().get("output-type"); + inp.detMask = dets; + inp.outType = configcontext.options().get("output-type"); } - WorkflowSpec specs{o2::ctf::getCTFWriterSpec(dets, outType, - configcontext.options().get("ctf-writer-verbosity"), - configcontext.options().get("report-data-size-interval"))}; + inp.verbosity = configcontext.options().get("ctf-writer-verbosity"); + inp.reportInterval = configcontext.options().get("report-data-size-interval"); + inp.doITSStaggering = o2::itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(configcontext); + inp.doMFTStaggering = o2::itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(configcontext); + + WorkflowSpec specs{o2::ctf::getCTFWriterSpec(inp)}; return std::move(specs); } diff --git a/Detectors/Filtering/src/FilteringSpec.cxx b/Detectors/Filtering/src/FilteringSpec.cxx index bcf3c6c3539d4..ea82b1456d955 100644 --- a/Detectors/Filtering/src/FilteringSpec.cxx +++ b/Detectors/Filtering/src/FilteringSpec.cxx @@ -46,7 +46,7 @@ #include "ReconstructionDataFormats/Cascade.h" #include "MCHTracking/TrackExtrap.h" #include "MCHTracking/TrackParam.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DetectorsVertexing/PVertexerParams.h" #include "ReconstructionDataFormats/GlobalTrackID.h" #include "ReconstructionDataFormats/Track.h" diff --git a/Detectors/Filtering/src/filtering-workflow.cxx b/Detectors/Filtering/src/filtering-workflow.cxx index 8e36cfc36b197..faf5463281ed8 100644 --- a/Detectors/Filtering/src/filtering-workflow.cxx +++ b/Detectors/Filtering/src/filtering-workflow.cxx @@ -17,6 +17,7 @@ #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsRaw/HBFUtilsInitializer.h" #include "Framework/CallbacksPolicy.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -36,6 +37,7 @@ void customize(std::vector& workflowOptions) {"disable-secondary-vertices", o2::framework::VariantType::Bool, false, {"disable filling secondary vertices"}}, {"data-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of sources to use"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addConfigOption(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } diff --git a/Detectors/GlobalTrackingWorkflow/helpers/src/InputHelper.cxx b/Detectors/GlobalTrackingWorkflow/helpers/src/InputHelper.cxx index c6c163f4b8911..e4c1e40b3a4d3 100644 --- a/Detectors/GlobalTrackingWorkflow/helpers/src/InputHelper.cxx +++ b/Detectors/GlobalTrackingWorkflow/helpers/src/InputHelper.cxx @@ -14,6 +14,7 @@ #include "GlobalTrackingWorkflowHelpers/InputHelper.h" #include "Framework/ConfigParamRegistry.h" #include "ITSMFTWorkflow/ClusterReaderSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "ITSWorkflow/TrackReaderSpec.h" #include "MFTWorkflow/TrackReaderSpec.h" #include "TPCReaderWorkflow/TrackReaderSpec.h" @@ -79,13 +80,15 @@ int InputHelper::addInputSpecs(const ConfigContext& configcontext, WorkflowSpec& specs.emplace_back(o2::its::getITSTrackReaderSpec(maskTracksMC[GID::ITS])); } if (maskClusters[GID::ITS]) { - specs.emplace_back(o2::itsmft::getITSClusterReaderSpec(maskClustersMC[GID::ITS], true)); + bool doStag = itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(configcontext); + specs.emplace_back(o2::itsmft::getITSClusterReaderSpec(maskClustersMC[GID::ITS], doStag, true)); } if (maskTracks[GID::MFT]) { specs.emplace_back(o2::mft::getMFTTrackReaderSpec(maskTracksMC[GID::MFT])); } if (maskClusters[GID::MFT]) { - specs.emplace_back(o2::itsmft::getMFTClusterReaderSpec(maskClustersMC[GID::MFT], true)); + bool doStag = itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(configcontext); + specs.emplace_back(o2::itsmft::getMFTClusterReaderSpec(maskClustersMC[GID::MFT], doStag, true)); } if (maskTracks[GID::MCH] || maskMatches[GID::MCHMID]) { specs.emplace_back(o2::mch::getTrackReaderSpec(maskTracksMC[GID::MCH] || maskTracksMC[GID::MCHMID])); diff --git a/Detectors/GlobalTrackingWorkflow/src/CosmicsMatchingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/CosmicsMatchingSpec.cxx index 34c41ec234dc5..5bcdded0e1223 100644 --- a/Detectors/GlobalTrackingWorkflow/src/CosmicsMatchingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/CosmicsMatchingSpec.cxx @@ -40,7 +40,7 @@ #include "Headers/DataHeader.h" #include "CommonDataFormat/InteractionRecord.h" #include "ITSBase/GeometryTGeo.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DataFormatsGlobalTracking/RecoContainer.h" #include "Framework/Task.h" #include "Framework/CCDBParamSpec.h" diff --git a/Detectors/GlobalTrackingWorkflow/src/GlobalFwdMatchingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/GlobalFwdMatchingSpec.cxx index 03dc823c62c42..a43a1e8943739 100644 --- a/Detectors/GlobalTrackingWorkflow/src/GlobalFwdMatchingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/GlobalFwdMatchingSpec.cxx @@ -20,7 +20,7 @@ #include "Framework/CCDBParamSpec.h" #include "CommonUtils/StringUtils.h" #include "DetectorsCommonDataFormats/DetectorNameConf.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "SimulationDataFormat/MCCompLabel.h" #include "DataFormatsMFT/TrackMFT.h" #include "DataFormatsITSMFT/Cluster.h" diff --git a/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexingSpec.cxx index dc1107bacb18a..c1d7b62bbf731 100644 --- a/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/PrimaryVertexingSpec.cxx @@ -30,7 +30,7 @@ #include "Framework/CCDBParamSpec.h" #include "Framework/DeviceSpec.h" #include "FT0Reconstruction/InteractionTag.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsVertexing/PVertexer.h" #include "DetectorsBase/GRPGeomHelper.h" diff --git a/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx index c333c37ff245b..cb3384b0631c2 100644 --- a/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx @@ -39,7 +39,7 @@ #include "DetectorsBase/GeometryManager.h" #include "DetectorsBase/Propagator.h" #include "DetectorsBase/GlobalParams.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "ITSBase/GeometryTGeo.h" #include "GlobalTracking/MatchTPCITSParams.h" #include "DetectorsCommonDataFormats/DetectorNameConf.h" diff --git a/Detectors/GlobalTrackingWorkflow/src/VertexTrackMatcherSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/VertexTrackMatcherSpec.cxx index f24e7c13e336f..90e4dd4b0f001 100644 --- a/Detectors/GlobalTrackingWorkflow/src/VertexTrackMatcherSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/VertexTrackMatcherSpec.cxx @@ -23,7 +23,7 @@ #include "TPCBase/ParameterElectronics.h" #include "TPCBase/ParameterDetector.h" #include "TPCCalibration/VDriftHelper.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "TStopwatch.h" using namespace o2::framework; diff --git a/Detectors/GlobalTrackingWorkflow/src/cosmics-match-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/cosmics-match-workflow.cxx index e4082bdd14d86..3f7ecfbbea809 100644 --- a/Detectors/GlobalTrackingWorkflow/src/cosmics-match-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/cosmics-match-workflow.cxx @@ -32,6 +32,7 @@ #include "Framework/CallbacksPolicy.h" #include "GlobalTrackingWorkflowHelpers/InputHelper.h" #include "TPCCalibration/CorrectionMapsLoader.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using DetID = o2::detectors::DetID; @@ -52,6 +53,7 @@ void customize(std::vector& workflowOptions) {"disable-root-output", o2::framework::VariantType::Bool, false, {"disable root-files output writer"}}, {"track-sources", VariantType::String, std::string{GID::ALL}, {"comma-separated list of sources to use"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); diff --git a/Detectors/GlobalTrackingWorkflow/src/globalfwd-matcher-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/globalfwd-matcher-workflow.cxx index 13a842130e5d1..fd90aff5f32ff 100644 --- a/Detectors/GlobalTrackingWorkflow/src/globalfwd-matcher-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/globalfwd-matcher-workflow.cxx @@ -21,6 +21,7 @@ #include "GlobalTrackingWorkflow/MatchedMFTMCHWriterSpec.h" #include "GlobalTrackingWorkflowHelpers/InputHelper.h" #include "GlobalTracking/MatchGlobalFwdParam.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -46,6 +47,7 @@ void customize(std::vector& workflowOptions) {"disable-root-output", o2::framework::VariantType::Bool, false, {"do not write output root files"}}, {"enable-match-output", o2::framework::VariantType::Bool, false, {"stores mftmch matching info on mftmchmatches.root"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addMFTConfigOption(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } diff --git a/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx index 0ac640cbad9fd..9108e8577fd5a 100644 --- a/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx @@ -30,6 +30,7 @@ #include "Framework/CompletionPolicyHelpers.h" #include "DetectorsBase/DPLWorkflowUtils.h" #include "TPCCalibration/CorrectionMapsLoader.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -62,6 +63,7 @@ void customize(std::vector& workflowOptions) {"use-full-geometry", o2::framework::VariantType::Bool, false, {"use full geometry instead of the light-weight ITS part"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}, {"combine-source-devices", o2::framework::VariantType::Bool, false, {"merge DPL source devices"}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); diff --git a/Detectors/GlobalTrackingWorkflow/src/strangeness-tracking-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/strangeness-tracking-workflow.cxx index bdc1af958886c..8c42871ac05bf 100644 --- a/Detectors/GlobalTrackingWorkflow/src/strangeness-tracking-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/strangeness-tracking-workflow.cxx @@ -21,6 +21,7 @@ #include "GlobalTrackingWorkflowReaders/TrackTPCITSReaderSpec.h" #include "GlobalTrackingWorkflowReaders/PrimaryVertexReaderSpec.h" #include "GlobalTrackingWorkflowHelpers/InputHelper.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "DetectorsRaw/HBFUtilsInitializer.h" #include "Framework/CallbacksPolicy.h" @@ -46,6 +47,7 @@ void customize(std::vector& workflowOptions) {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC"}}, {"use-full-geometry", o2::framework::VariantType::Bool, false, {"use full geometry instead of the light-weight ITS part"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } diff --git a/Detectors/GlobalTrackingWorkflow/src/tpcits-match-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/tpcits-match-workflow.cxx index 810c7c564b4a8..17ab2191f0e1e 100644 --- a/Detectors/GlobalTrackingWorkflow/src/tpcits-match-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/tpcits-match-workflow.cxx @@ -25,6 +25,7 @@ #include "Framework/ConfigContext.h" #include "Framework/CompletionPolicyHelpers.h" #include "TPCCalibration/CorrectionMapsLoader.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -46,6 +47,7 @@ void customize(std::vector& workflowOptions) {"produce-calibration-data", o2::framework::VariantType::Bool, false, {"produce output for TPC vdrift calibration"}}, {"use-full-geometry", o2::framework::VariantType::Bool, false, {"use full geometry instead of the light-weight ITS part"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); @@ -76,6 +78,7 @@ WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& configcont GID::mask_t alowedSources = GID::getSourcesMask("ITS,TPC,TPC-TOF"); GID::mask_t src = alowedSources & GID::getSourcesMask(configcontext.options().get("track-sources")); bool needStrictTRDTOF = (src & GID::getSourcesMask("TPC-TRD,TPC-TOF,TPC-TRD-TOF")).any(); + auto doStag = o2::itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(configcontext); // RS at the moment is not passed to the matching w-flow auto sclOpt = o2::tpc::CorrectionMapsLoader::parseGlobalOptions(configcontext.options()); auto useGeom = configcontext.options().get("use-full-geometry"); auto useFT0 = configcontext.options().get("use-ft0"); diff --git a/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx b/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx index ed419700b339b..dc002489c24e2 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/CheckResid.cxx @@ -29,7 +29,7 @@ #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" #include "Framework/DeviceSpec.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "ITSBase/GeometryTGeo.h" #include "ITStracking/IOUtils.h" #include "DetectorsCommonDataFormats/DetID.h" diff --git a/Detectors/GlobalTrackingWorkflow/study/src/DumpTracks.cxx b/Detectors/GlobalTrackingWorkflow/study/src/DumpTracks.cxx index d02f1df3903ec..8c7931d599e93 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/DumpTracks.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/DumpTracks.cxx @@ -21,7 +21,7 @@ #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" #include "FT0Reconstruction/InteractionTag.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsBase/GRPGeomHelper.h" #include "GlobalTrackingStudy/TrackingStudy.h" diff --git a/Detectors/GlobalTrackingWorkflow/study/src/SVStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/SVStudy.cxx index 0129d19b02346..4d0b6bdbdb213 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/SVStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/SVStudy.cxx @@ -29,7 +29,7 @@ #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" #include "FT0Reconstruction/InteractionTag.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsBase/GRPGeomHelper.h" #include "GlobalTrackingStudy/TrackingStudy.h" diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx index 8f6604b029605..1db303d20e5d9 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx @@ -34,7 +34,7 @@ #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" #include "FT0Reconstruction/InteractionTag.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsBase/GRPGeomHelper.h" #include "GlobalTrackingStudy/TrackMCStudy.h" diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx index c68e60059cd3f..a184058a1bfd6 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx @@ -28,7 +28,7 @@ #include "Framework/CCDBParamSpec.h" #include "Framework/DeviceSpec.h" #include "FT0Reconstruction/InteractionTag.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsBase/GRPGeomHelper.h" #include "GlobalTrackingStudy/TrackingStudy.h" diff --git a/Detectors/GlobalTrackingWorkflow/study/src/check-resid-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/check-resid-workflow.cxx index b8230b59405d8..fd4485585103c 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/check-resid-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/check-resid-workflow.cxx @@ -22,6 +22,7 @@ #include "DetectorsRaw/HBFUtilsInitializer.h" #include "TPCCalibration/CorrectionMapsLoader.h" #include "TPCWorkflow/TPCScalerSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -44,6 +45,7 @@ void customize(std::vector& workflowOptions) {"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::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } diff --git a/Detectors/GlobalTrackingWorkflow/study/src/its-offset-study-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/its-offset-study-workflow.cxx index 9638312e13f78..5d89ba9b7eabf 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/its-offset-study-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/its-offset-study-workflow.cxx @@ -20,6 +20,7 @@ #include "DetectorsBase/DPLWorkflowUtils.h" #include "GlobalTrackingWorkflowHelpers/InputHelper.h" #include "DetectorsRaw/HBFUtilsInitializer.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -39,6 +40,7 @@ void customize(std::vector& workflowOptions) {"track-sources", VariantType::String, std::string{"ITS,ITS-TPC-TRD-TOF,ITS-TPC-TOF,ITS-TPC,ITS-TPC-TRD"}, {"comma-separated list of track sources to use"}}, {"disable-root-input", VariantType::Bool, false, {"disable root-files input reader"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); o2::raw::HBFUtilsInitializer::addConfigOption(options, "o2_tfidinfo.root"); std::swap(workflowOptions, options); } diff --git a/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx index 7aa53e2190a9e..9e0055a389bfe 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx @@ -22,6 +22,7 @@ #include "TPCCalibration/CorrectionMapsLoader.h" #include "TPCWorkflow/TPCScalerSpec.h" #include "DetectorsRaw/HBFUtilsInitializer.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -44,6 +45,7 @@ void customize(std::vector& workflowOptions) {"ignore-sv-check", VariantType::Bool, false, {"disable check for SV combinatorics"}}, {"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation, never use it"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); diff --git a/Detectors/GlobalTrackingWorkflow/study/src/tracking-study-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/tracking-study-workflow.cxx index 413d2e63653fc..ae2e3b5301a14 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/tracking-study-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/tracking-study-workflow.cxx @@ -22,6 +22,7 @@ #include "DetectorsRaw/HBFUtilsInitializer.h" #include "TPCCalibration/CorrectionMapsLoader.h" #include "TPCWorkflow/TPCScalerSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -43,6 +44,7 @@ void customize(std::vector& workflowOptions) {"cluster-sources", VariantType::String, "TPC,TOF", {"comma-separated list of cluster sources to use"}}, {"disable-root-input", VariantType::Bool, false, {"disable root-files input reader"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); diff --git a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-interpolation-workflow.cxx b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-interpolation-workflow.cxx index 2f28fc5bb2d34..65a79a8635a49 100644 --- a/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-interpolation-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/tpcinterpolationworkflow/src/tpc-interpolation-workflow.cxx @@ -19,6 +19,7 @@ #include "GlobalTrackingWorkflowHelpers/InputHelper.h" #include "TPCInterpolationWorkflow/TPCInterpolationSpec.h" #include "TPCInterpolationWorkflow/TPCResidualWriterSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using GID = o2::dataformats::GlobalTrackID; @@ -44,6 +45,7 @@ void customize(std::vector& workflowOptions) {"debug-output", VariantType::Bool, false, {"Dump extended tracking information for debugging"}}, {"skip-ext-det-residuals", VariantType::Bool, false, {"Do not produce residuals for external detectors"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } diff --git a/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt b/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt index dd6aacf65db99..a23682b085311 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt @@ -118,3 +118,13 @@ o2_add_test_root_macro(CheckDROF.C PUBLIC_LINK_LIBRARIES O2::DataFormatsITS O2::DataFormatsITSMFT LABELS its) + +o2_add_test_root_macro(CheckStaggering.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsITS + O2::DataFormatsITSMFT + O2::ITSMFTReconstruction + O2::DCAFitter + O2::CCDB + O2::DetectorsVertexing + O2::ReconstructionDataFormats + LABELS its COMPILE_ONLY) diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckDROF.C b/Detectors/ITSMFT/ITS/macros/test/CheckDROF.C index 21428ea4fcbc2..01acd2935981d 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CheckDROF.C +++ b/Detectors/ITSMFT/ITS/macros/test/CheckDROF.C @@ -39,13 +39,13 @@ #include "SimulationDataFormat/MCTruthContainer.h" #include "DataFormatsITSMFT/CompCluster.h" #include "DataFormatsITS/TrackITS.h" +#include "DataFormatsITS/Vertex.h" #include "DataFormatsITSMFT/ROFRecord.h" #include "SimulationDataFormat/DigitizationContext.h" #endif using namespace std; -using Vertex = o2::dataformats::Vertex>; void plotHistos(TFile* fWO, TFile* f, const char* append = ""); @@ -93,9 +93,9 @@ struct ParticleInfo { // particle level information for tracks #pragma link C++ class ParticleInfo + ; #pragma link C++ class std::vector < ParticleInfo> + ; -struct VertexInfo { // Vertex level info - float purity; // fraction of main cont. labels to all - Vertex vertex; // reconstructed vertex +struct VertexInfo { // Vertex level info + float purity; // fraction of main cont. labels to all + o2::its::Vertex vertex; // reconstructed vertex int bcInROF{-1}; int rofId{-1}; int event{-1}; // corresponding MC event @@ -199,7 +199,7 @@ void CheckDROF(bool plot = false, bool write = false, const std::string& tracfil std::vector rofRecVec, *rofRecVecP = &rofRecVec; recTree->SetBranchAddress("ITSTracksROF", &rofRecVecP); // Vertices - std::vector* recVerArr = nullptr; + std::vector* recVerArr = nullptr; recTree->SetBranchAddress("Vertices", &recVerArr); std::vector* recVerROFArr = nullptr; recTree->SetBranchAddress("VerticesROF", &recVerROFArr); @@ -876,11 +876,12 @@ void CheckDROF(bool plot = false, bool write = false, const std::string& tracfil if (!trk.hasHitOnLayer(iL) || !trk.isFakeOnLayer(iL) || (part.clusters & (0x1 << iL)) == 0) { continue; } - if (trk.hasHitInNextROF()) { - hFakMig->Fill(trk.getPt(), trk.getNClusters(), iL); - } else { - hFakVal->Fill(trk.getPt(), trk.getNClusters(), iL); - } + // TODO: figure out how to find hit migration + // if (trk.hasHitInNextROF()) { + // hFakMig->Fill(trk.getPt(), trk.getNClusters(), iL); + // } else { + // hFakVal->Fill(trk.getPt(), trk.getNClusters(), iL); + // } } } } diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckStaggering.C b/Detectors/ITSMFT/ITS/macros/test/CheckStaggering.C new file mode 100644 index 0000000000000..e3a79779a5fb1 --- /dev/null +++ b/Detectors/ITSMFT/ITS/macros/test/CheckStaggering.C @@ -0,0 +1,521 @@ +// 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. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DetectorsBase/Propagator.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITS/TrackITS.h" +#include "DataFormatsITS/Vertex.h" +#include "DataFormatsITS/TimeEstBC.h" +#include "CCDB/CcdbApi.h" +#include "CCDB/BasicCCDBManager.h" +#include "CommonConstants/LHCConstants.h" +#include "DataFormatsParameters/GRPMagField.h" +#include "DataFormatsParameters/GRPLHCIFData.h" +#include "ReconstructionDataFormats/Vertex.h" +#include "DetectorsVertexing/SVertexHypothesis.h" +#include "DCAFitter/DCAFitterN.h" +#endif + +constexpr const char* tracFile = "o2trac_its.root"; +constexpr const char* clsFile = "o2clus_its.root"; + +struct PairCandidate { + int posIdx; + int negIdx; + double dca; + double mass; +}; + +std::vector findDirs(const std::string&); + +void CheckStaggering(int runNumber, int max = -1, const std::string& dir = "") +{ + gStyle->SetOptStat(0); + auto dirs = findDirs(dir); + printf("Will iterate over %zu input dirs", dirs.size()); + if (dirs.empty()) { + printf("No input found"); + return; + } + if (max > 0 && (int)dirs.size() > max) { + std::random_device rd; + std::mt19937 g(rd()); + std::shuffle(dirs.begin(), dirs.end(), g); + dirs.resize(max); + printf("restricting to %ddirs", max); + } + + auto& ccdbmgr = o2::ccdb::BasicCCDBManager::instance(); + ccdbmgr.setURL("https://alice-ccdb.cern.ch"); + auto runDuration = ccdbmgr.getRunDuration(runNumber); + auto tRun = runDuration.first + (runDuration.second - runDuration.first) / 2; // time stamp for the middle of the run duration + ccdbmgr.setTimestamp(tRun); + printf("Run %d has TS %lld", runNumber, tRun); + auto geoAligned = ccdbmgr.get("GLO/Config/GeometryAligned"); + auto magField = ccdbmgr.get("GLO/Config/GRPMagField"); + auto grpLHC = ccdbmgr.get("GLO/Config/GRPLHCIF"); + auto bcFill = grpLHC->getBunchFilling(); + bcFill.print(-1); + const o2::base::MatLayerCylSet* matLut = o2::base::MatLayerCylSet::rectifyPtrFromFile(ccdbmgr.get("GLO/Param/MatLUT")); + o2::base::Propagator::initFieldFromGRP(magField); + auto prop = o2::base::Propagator::Instance(); + prop->setMatLUT(matLut); + const float bz = prop->getNominalBz(); + + auto hNTrkCls = new TH1D("hNTrkCls", "Number of cluster per track;nCls;entries", 4, 3.5, 7.5); + std::array hTrkTS{nullptr}; + for (int i{0}; i < 5; ++i) { + hTrkTS[i] = new TH1D(Form("hTrkTS_%d", i), Form("track time t0 (%s);t0 (BC)", i == 0 ? "all" : Form("NCls=%d", 3 + i)), o2::constants::lhc::LHCMaxBunches, 0, o2::constants::lhc::LHCMaxBunches); + } + auto hTrkTSE = new TH1D("hTrkTSE", "assigned time errors; tE (BC)", 198, -0.5, 198 - 0.5); + + // K0 && Phi-Meson + const float mMinITSPt{0.15}; + // + const int phiMinNCls{7}; + const float phiMaxDCAR2MVTX{0.05}; // max distance to mean vtx + auto hPhiMeson = new TH1D("hPhiMeson", "#phi meson;mass (GeV/c^{2})", 200, 0.96, 1.1); + auto hPhiMesonBkg = new TH1D("hPhiMesonBkg", "#phi meson background;mass (GeV/c^{2})", 200, 0.96, 1.1); + + const int mK0MinNCls{7}; + const float mK0minCosPAXYMeanVertex = 0.98; + const float mK0MaxDCAXY2ToMeanVertex = 0.2; + const float mK0MaxTgl2V0 = 1; + const float mK0MinPt2V0 = 0.3; + const float mK0MinCosPA = 0.999; + o2::vertexing::DCAFitterN<2> k0Ft; + k0Ft.setBz(bz); + k0Ft.setPropagateToPCA(true); // After finding the vertex, propagate tracks to the DCA. This is default anyway + k0Ft.setMaxR(30); + k0Ft.setMaxDZIni(0.1); + k0Ft.setMaxDXYIni(0.1); + k0Ft.setMinParamChange(1e-3); + k0Ft.setMinRelChi2Change(0.9); + k0Ft.setMaxChi2(5); + k0Ft.setUseAbsDCA(true); + auto hK0 = new TH1D("hK0", "K0;mass (GeV/c^{2})", 100, 0.4, 0.6); + o2::vertexing::SVertexHypothesis k0Hyp; + const float k0Par[] = {0., 20, 0., 5.0, 0.0, 1.09004e-03, 2.62291e-04, 8.93179e-03, 2.83121}; + k0Hyp.set(o2::track::PID::K0, o2::track::PID::Pion, o2::track::PID::Pion, k0Par, bz); + + auto hVtxXY = new TH2F("hVtxXY", "seeding vertices XY", 200, -0.3, 0.3, 200, -0.3, 0.3); + auto hVtxZ = new TH1F("hVtxZ", "seeding vertices Z", 200, -16, 16); + auto hVtxNCont = new TH1F("hVtxNCont", "seeding vertices contributors", 100, 0, 100); + auto hVtxZNCont = new TProfile("hVtxZNCont", "seeding vertices z-contributors", 200, -16, 16); + auto hVtxCls = new TProfile("hVtxCls", ";Cls/TF;Cls/Vtx", 400, 20000, 60000); + auto hVtxTS = new TH1D("hVtxTS", "vtx time t0;t0 (BC)", o2::constants::lhc::LHCMaxBunches, 0, o2::constants::lhc::LHCMaxBunches); + + const float minVtxWeight{5}; + float meanVtxWeight{0}; + o2::dataformats::VertexBase meanVtx; + auto accountVtx = [&](o2::its::Vertex const& vtx) { + const float w = vtx.getNContributors(); + if (w >= minVtxWeight) { + meanVtx.setX((meanVtx.getX() * meanVtxWeight + vtx.getX() * w) / (meanVtxWeight + w)); + meanVtx.setY((meanVtx.getY() * meanVtxWeight + vtx.getY() * w) / (meanVtxWeight + w)); + meanVtxWeight += w; + } + }; + + std::vector trkArr, *trkArrPtr{&trkArr}; + std::vector vtxArr, *vtxArrPtr{&vtxArr}; + std::array*, 7> clsArr{nullptr}; + for (size_t iDir{0}; iDir < dirs.size(); ++iDir) { + int progress = static_cast((iDir + 1) * 100 / dirs.size()); + printf("\rProgress: ["); + int barWidth = 50; + int pos = barWidth * progress / 100; + for (int j = 0; j < barWidth; ++j) { + if (j < pos) { + printf("="); + } else if (j == pos) { + printf(">"); + } else { + printf(" "); + } + } + printf("] %d%%", progress); + fflush(stdout); + + const auto& d = dirs[iDir]; + auto fTrks = TFile::Open((d / tracFile).c_str()); + auto fCls = TFile::Open((d / clsFile).c_str()); + if (!fTrks || !fCls || fTrks->IsZombie() || fCls->IsZombie()) { + continue; + } + auto tTrks = fTrks->Get("o2sim"); + auto tCls = fCls->Get("o2sim"); + if (!tTrks || !tCls) { + continue; + } + + tTrks->SetBranchAddress("ITSTrack", &trkArrPtr); + tTrks->SetBranchAddress("Vertices", &vtxArrPtr); + for (int i{0}; i < 7; ++i) { + tCls->SetBranchAddress(Form("ITSClusterComp_%d", i), &clsArr[i]); + } + + for (int iTF{0}; tTrks->LoadTree(iTF) >= 0; ++iTF) { + tTrks->GetEntry(iTF); + tCls->GetEntry(iTF); + + size_t ncls = 0; + for (int i{0}; i < 7; ++i) { + ncls += clsArr[i]->size(); + } + + // for each TF built pool of positive and negaitve tracks + std::vector posPool, negPool; + + for (const auto& trk : trkArr) { + hNTrkCls->Fill(trk.getNClusters()); + hTrkTS[0]->Fill(std::fmod(trk.getTimeStamp().getTimeStamp(), o2::constants::lhc::LHCMaxBunches)); + hTrkTS[trk.getNClusters() - 3]->Fill(std::fmod(trk.getTimeStamp().getTimeStamp(), o2::constants::lhc::LHCMaxBunches)); + hTrkTSE->Fill(trk.getTimeStamp().getTimeStampError()); + + if (trk.getPt() > mMinITSPt) { + if (trk.getCharge() > 0) { + posPool.push_back(&trk); + } else { + negPool.push_back(&trk); + } + } + } + + for (const auto& vtx : vtxArr) { + hVtxXY->Fill(vtx.getX(), vtx.getY()); + hVtxZ->Fill(vtx.getZ()); + hVtxNCont->Fill(vtx.getNContributors()); + hVtxZNCont->Fill(vtx.getZ(), vtx.getNContributors()); + hVtxTS->Fill(vtx.getTimeStamp().getTimeStamp()); + accountVtx(vtx); + } + hVtxCls->Fill(ncls, (float)ncls / (float)vtxArr.size()); + + std::vector k0Cands; + for (int iPos{0}; iPos < (int)posPool.size(); ++iPos) { + const auto pos = posPool[iPos]; + for (int iNeg{0}; iNeg < (int)negPool.size(); ++iNeg) { + const auto neg = negPool[iNeg]; + bool overlap = std::abs(pos->getTimeStamp().getTimeStamp() - neg->getTimeStamp().getTimeStamp()) <= (pos->getTimeStamp().getTimeStampError() + neg->getTimeStamp().getTimeStampError()); + if (!overlap) { + continue; + } + + // phi-meson + if (pos->getNClusters() >= phiMinNCls && neg->getNClusters() >= phiMinNCls) { + o2::dataformats::DCA posDCA, negDCA; + o2::track::TrackParCov posPhi = *pos; + posPhi.setPID(o2::track::PID::Kaon); + o2::track::TrackParCov negPhi = *neg; + negPhi.setPID(o2::track::PID::Kaon); + if (prop->propagateToDCA(meanVtx, posPhi, bz, 2.0, o2::base::Propagator::MatCorrType::USEMatCorrLUT, &posDCA) && prop->propagateToDCA(meanVtx, negPhi, bz, 2.0, o2::base::Propagator::MatCorrType::USEMatCorrLUT, &negDCA)) { + if (posDCA.getR2() < phiMaxDCAR2MVTX && negDCA.getR2() < phiMaxDCAR2MVTX) { + std::array pP{}, pN{}; + posPhi.getPxPyPzGlo(pP); + negPhi.getPxPyPzGlo(pN); + TLorentzVector p1, p2; + p1.SetXYZM(pP[0], pP[1], pP[2], posPhi.getPID().getMass()); + p2.SetXYZM(pN[0], pN[1], pN[2], negPhi.getPID().getMass()); + TLorentzVector mother = p1 + p2; + hPhiMeson->Fill(mother.M()); + // rotate on daughter track to estimate background + for (int i{0}; i < 10; ++i) { + double theta = gRandom->Uniform(165.f, 195.f) * TMath::DegToRad(); + double pxN = pN[0] * cos(theta) - pN[1] * sin(theta); + double pyN = pN[0] * sin(theta) + pN[1] * cos(theta); + double pxP = pP[0] * cos(-theta) - pP[1] * sin(-theta); + double pyP = pP[0] * sin(-theta) + pP[1] * cos(-theta); + p1.SetXYZM(pxP, pyP, pP[2], posPhi.getPID().getMass()); + p2.SetXYZM(pxN, pyN, pN[2], negPhi.getPID().getMass()); + mother = p1 + p2; + hPhiMesonBkg->Fill(mother.M()); + } + } + } + } + // K0 + if (pos->getNClusters() >= mK0MinNCls && neg->getNClusters() >= mK0MinNCls) { + o2::track::TrackParCov posPion = *pos; + posPion.setPID(o2::track::PID::Pion); + o2::track::TrackParCov negPion = *neg; + negPion.setPID(o2::track::PID::Pion); + int ncand = k0Ft.process(posPion, negPion); + const int cand = 0; + if (ncand) { + const auto& v0XYZ = k0Ft.getPCACandidate(); + float dxv0 = v0XYZ[0] - meanVtx.getX(), dyv0 = v0XYZ[1] - meanVtx.getY(), r2v0 = dxv0 * dxv0 + dyv0 * dyv0; + if (!k0Ft.isPropagateTracksToVertexDone(cand) && !k0Ft.propagateTracksToVertex(cand)) { + continue; + } + const auto& trPProp = k0Ft.getTrack(0, cand); + const auto& trNProp = k0Ft.getTrack(1, cand); + std::array pP{}, pN{}; + trPProp.getPxPyPzGlo(pP); + trNProp.getPxPyPzGlo(pN); + // estimate DCA of neutral V0 track to beamline: straight line with parametric equation + // x = X0 + pV0[0]*t, y = Y0 + pV0[1]*t reaches DCA to beamline (Xv, Yv) at + // t = -[ (x0-Xv)*pV0[0] + (y0-Yv)*pV0[1]) ] / ( pT(pV0)^2 ) + // Similar equation for 3D distance involving pV0[2] + std::array pV0 = {pP[0] + pN[0], pP[1] + pN[1], pP[2] + pN[2]}; + float pt2V0 = pV0[0] * pV0[0] + pV0[1] * pV0[1], prodXYv0 = dxv0 * pV0[0] + dyv0 * pV0[1], tDCAXY = prodXYv0 / pt2V0; + if (pt2V0 < mK0MinPt2V0) { // pt cut + continue; + } + if (pV0[2] * pV0[2] / pt2V0 > mK0MaxTgl2V0) { // tgLambda cut + continue; + } + float dcaX = dxv0 - pV0[0] * tDCAXY, dcaY = dyv0 - pV0[1] * tDCAXY, dca2 = dcaX * dcaX + dcaY * dcaY; + float cosPAXY = prodXYv0 / std::sqrt(r2v0 * pt2V0); + if (dca2 > mK0MaxDCAXY2ToMeanVertex || cosPAXY < mK0minCosPAXYMeanVertex) { + continue; + } + float p2V0 = pt2V0 + pV0[2] * pV0[2], ptV0 = std::sqrt(pt2V0); + float p2Pos = pP[0] * pP[0] + pP[1] * pP[1] + pP[2] * pP[2], p2Neg = pN[0] * pN[0] + pN[1] * pN[1] + pN[2] * pN[2]; + if (!k0Hyp.check(p2Pos, p2Neg, p2V0, ptV0)) { + continue; + } + + float bestCosPA = mK0MinCosPA; + bool candFound = false; + for (const auto& vtx : vtxArr) { + if (vtx.getNContributors() > minVtxWeight) { + const auto vtxT = vtx.getTimeStamp().makeSymmetrical(); + bool overlapPos = std::abs(pos->getTimeStamp().getTimeStamp() - vtxT.getTimeStamp()) <= (pos->getTimeStamp().getTimeStampError() + vtxT.getTimeStampError()); + bool overlapNeg = std::abs(neg->getTimeStamp().getTimeStamp() - vtxT.getTimeStamp()) <= (neg->getTimeStamp().getTimeStampError() + vtxT.getTimeStampError()); + if (overlapPos && overlapNeg) { + float dx = v0XYZ[0] - vtx.getX(), dy = v0XYZ[1] - vtx.getY(), dz = v0XYZ[2] - vtx.getZ(), prodXYZv0 = dx * pV0[0] + dy * pV0[1] + dz * pV0[2]; + float cosPA = prodXYZv0 / std::sqrt((dx * dx + dy * dy + dz * dz) * p2V0); + if (cosPA > bestCosPA) { + bestCosPA = cosPA; + candFound = true; + } + } + } + } + if (candFound) { + TLorentzVector p1, p2; + p1.SetXYZM(pP[0], pP[1], pP[2], posPion.getPID().getMass()); + p2.SetXYZM(pN[0], pN[1], pN[2], negPion.getPID().getMass()); + TLorentzVector mother = p1 + p2; + double mass = mother.M(); + k0Cands.emplace_back(iPos, iNeg, k0Ft.getChi2AtPCACandidate(cand), mass); + } + } + } + } + } + + // disambiguiate candidates by using the smallest DCA one + std::sort(k0Cands.begin(), k0Cands.end(), [](const auto& a, const auto& b) { return a.dca < b.dca; }); + std::vector posUsed(posPool.size(), false); + std::vector negUsed(negPool.size(), false); + for (const auto& c : k0Cands) { + if (!posUsed[c.posIdx] && !negUsed[c.negIdx]) { + posUsed[c.posIdx] = true; + negUsed[c.negIdx] = true; + hK0->Fill(c.mass); + } + } + } + + fTrks->Close(); + fCls->Close(); + } + + auto drawBCPattern = [&]() { + gPad->Update(); + // draw BC pattern + double ymin = gPad->GetUymin(); + double ymax = gPad->GetUymax(); + auto interactingBC = bcFill.getPattern(); + TLine* lastLine{nullptr}; + for (int iBC{0}; iBC < (int)interactingBC.size(); ++iBC) { + if (interactingBC.test(iBC)) { + TLine* line = new TLine(iBC, ymin, iBC, ymax); + line->SetLineColor(kRed); + line->SetLineWidth(1); + line->SetLineStyle(kDashed); + line->Draw(); + lastLine = line; + } + } + return lastLine; + }; + + { + TFile out(Form("check_%d.root", runNumber), "RECREATE"); + out.WriteTObject(hNTrkCls); + for (int i{0}; i < 5; ++i) { + out.WriteTObject(hTrkTS[i]); + } + out.WriteTObject(hTrkTSE); + out.WriteTObject(hPhiMeson); + out.WriteTObject(hPhiMesonBkg); + out.WriteTObject(hK0); + out.WriteTObject(hVtxXY); + out.WriteTObject(hVtxZ); + out.WriteTObject(hVtxNCont); + out.WriteTObject(hVtxZNCont); + out.WriteTObject(hVtxTS); + out.WriteTObject(hVtxCls); + } + + // fitK0(hK0, runNumber); + // fitPhiMeson(hPhiMeson, runNumber); + { + auto c = new TCanvas(); + hNTrkCls->Draw(); + c->Draw(); + c->SaveAs(Form("trk_%d.pdf", runNumber)); + } + { + auto c = new TCanvas(); + c->Divide(3, 2); + for (int i{0}; i < 5; ++i) { + c->cd(i + 1); + hTrkTS[i]->Draw(); + auto lastLine = drawBCPattern(); + if (i == 0) { + auto leg = new TLegend(0.6, 0.6, 0.9, 0.9); + leg->AddEntry(lastLine, "Interacting BCs", "l"); + leg->AddEntry(hTrkTS[i], "Track timestamp"); + leg->Draw(); + } + } + c->cd(6); + hTrkTSE->Draw(); + c->Draw(); + c->SaveAs(Form("time_%d.pdf", runNumber)); + } + { + auto c = new TCanvas(); + c->Divide(2, 1); + { + c->cd(1); + hPhiMeson->Draw(); + gPad->Update(); + double ymin = gPad->GetUymin(); + double ymax = gPad->GetUymax(); + const float mass = 1.019461; + TLine* line = new TLine(mass, ymin, mass, ymax); + line->SetLineColor(kRed); + line->SetLineWidth(1); + line->SetLineStyle(kDashed); + line->Draw(); + } + { + c->cd(2); + hK0->Draw(); + gPad->Update(); + double ymin = gPad->GetUymin(); + double ymax = gPad->GetUymax(); + const float mass = 0.497611; + TLine* line = new TLine(mass, ymin, mass, ymax); + line->SetLineColor(kRed); + line->SetLineWidth(1); + line->SetLineStyle(kDashed); + line->Draw(); + } + c->Draw(); + c->SaveAs(Form("mass_%d.pdf", runNumber)); + } + { + auto c = new TCanvas(); + c->Divide(3, 2); + { + c->cd(1); + hVtxXY->Draw("col"); + } + { + c->cd(2); + hVtxZ->Draw(); + } + { + c->cd(3); + hVtxNCont->Draw(); + } + { + c->cd(4); + hVtxZNCont->Draw(); + } + { + c->cd(5); + hVtxCls->Draw(); + } + { + c->cd(6); + hVtxTS->Draw(); + auto lastLine = drawBCPattern(); + auto leg = new TLegend(0.6, 0.6, 0.9, 0.9); + leg->AddEntry(lastLine, "Interacting BCs", "l"); + leg->AddEntry(hVtxTS, "Track timestamp"); + leg->Draw(); + } + c->Draw(); + c->SaveAs(Form("vertex_%d.pdf", runNumber)); + } +} + +std::vector findDirs(const std::string& roots) +{ + std::filesystem::path root; + if (roots.empty()) { + root = std::filesystem::current_path(); + } else { + root = roots; + } + namespace fs = std::filesystem; + std::vector result; + auto has_files = [](const fs::path& dir) { + auto s = dir / tracFile; + return fs::exists(dir / tracFile) && fs::exists(dir / clsFile) && + fs::is_regular_file(dir / tracFile) && fs::is_regular_file(dir / clsFile); + }; + if (fs::is_directory(root) && has_files(root)) { + result.push_back(root); + return result; + } + for (const auto& entry : fs::recursive_directory_iterator(root)) { + if (!entry.is_directory()) { + continue; + } + const fs::path dir = entry.path(); + if (has_files(dir)) { + result.push_back(dir); + } + } + return result; +} diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/ImpactParameter.cxx b/Detectors/ITSMFT/ITS/postprocessing/studies/src/ImpactParameter.cxx index c0aaabddaca1b..bc8b931190ed1 100644 --- a/Detectors/ITSMFT/ITS/postprocessing/studies/src/ImpactParameter.cxx +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/ImpactParameter.cxx @@ -29,7 +29,7 @@ #include "CommonUtils/TreeStreamRedirector.h" #include "DetectorsBase/GRPGeomHelper.h" #include "DataFormatsParameters/GRPECSObject.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DetectorsCommonDataFormats/DetID.h" #include "Framework/DeviceSpec.h" #include "CommonUtils/ConfigurableParam.h" diff --git a/Detectors/ITSMFT/ITS/postprocessing/workflow/standalone-postprocessing-workflow.cxx b/Detectors/ITSMFT/ITS/postprocessing/workflow/standalone-postprocessing-workflow.cxx index 30fb39c77f235..8bcb444f650bd 100644 --- a/Detectors/ITSMFT/ITS/postprocessing/workflow/standalone-postprocessing-workflow.cxx +++ b/Detectors/ITSMFT/ITS/postprocessing/workflow/standalone-postprocessing-workflow.cxx @@ -16,6 +16,7 @@ #include "Framework/CompletionPolicyHelpers.h" #include "GlobalTrackingWorkflowHelpers/InputHelper.h" #include "DetectorsRaw/HBFUtilsInitializer.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" // Include studies hereafter #include "ITSStudies/ImpactParameter.h" @@ -54,6 +55,7 @@ void customize(std::vector& workflowOptions) {"track-extension-study", VariantType::Bool, false, {"Perform the track extension study"}}, {"efficiency-study", VariantType::Bool, false, {"Perform the efficiency study"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); // o2::raw::HBFUtilsInitializer::addConfigOption(options, "o2_tfidinfo.root"); std::swap(workflowOptions, options); } @@ -135,8 +137,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) LOGP(info, "No study selected, dryrunning"); } - o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); - // write the configuration used for the studies workflow + // o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); + // write the configuration used for the studies workflow o2::conf::ConfigurableParam::writeINI("o2_its_standalone_configuration.ini"); return std::move(specs); diff --git a/Detectors/ITSMFT/ITS/reconstruction/CMakeLists.txt b/Detectors/ITSMFT/ITS/reconstruction/CMakeLists.txt index d2126be1da2c6..be42015b95795 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/reconstruction/CMakeLists.txt @@ -11,8 +11,6 @@ o2_add_library(ITSReconstruction SOURCES src/RecoGeomHelper.cxx - src/FastMultEstConfig.cxx - src/FastMultEst.cxx PUBLIC_LINK_LIBRARIES O2::ITSBase O2::ITSMFTReconstruction O2::DataFormatsITS @@ -20,6 +18,4 @@ o2_add_library(ITSReconstruction o2_target_root_dictionary( ITSReconstruction - HEADERS include/ITSReconstruction/RecoGeomHelper.h - include/ITSReconstruction/FastMultEst.h - include/ITSReconstruction/FastMultEstConfig.h) + HEADERS include/ITSReconstruction/RecoGeomHelper.h) diff --git a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEst.h b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEst.h deleted file mode 100644 index 9e8299e89b404..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEst.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file FastMultEst.h -/// \brief Fast multiplicity estimator for ITS -/// \author ruben.shahoyan@cern.ch - -#ifndef ALICEO2_ITS_FASTMULTEST_ -#define ALICEO2_ITS_FASTMULTEST_ - -#include "ITSMFTReconstruction/ChipMappingITS.h" -#include "DataFormatsITSMFT/ROFRecord.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include -#include "ITSReconstruction/FastMultEstConfig.h" -#include -#include - -namespace o2 -{ -namespace its -{ - -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 its -} // namespace o2 - -#endif diff --git a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/TrivialVertexer.h b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/TrivialVertexer.h deleted file mode 100644 index 3eb218dc973f6..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/TrivialVertexer.h +++ /dev/null @@ -1,70 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file TrivialVertexer.h -/// \brief Definition of the ITS trivial vertex finder -#ifndef ALICEO2_ITS_TRIVIALVERTEXER_H -#define ALICEO2_ITS_TRIVIALVERTEXER_H - -#include - -#include "Rtypes.h" // for TrivialVertexer::Class, Double_t, ClassDef, etc - -class TFile; -class TTree; -class FairMCEventHeader; - -namespace o2 -{ -namespace itsmft -{ -class Cluster; -} -} // namespace o2 - -namespace o2 -{ -class MCCompLabel; -namespace dataformats -{ -template -class MCTruthContainer; -} -namespace its -{ -class TrivialVertexer -{ - using Cluster = o2::itsmft::Cluster; - using Label = o2::MCCompLabel; - - public: - TrivialVertexer(); - ~TrivialVertexer(); - - TrivialVertexer(const TrivialVertexer&) = delete; - TrivialVertexer& operator=(const TrivialVertexer&) = delete; - - Bool_t openInputFile(const Char_t*); - - void process(const std::vector& clusters, std::vector>& vertices); - void setMCTruthContainer(const o2::dataformats::MCTruthContainer* truth) { mClsLabels = truth; } - - private: - const o2::dataformats::MCTruthContainer* mClsLabels = nullptr; // Cluster MC labels - - TFile* mFile = nullptr; - TTree* mTree = nullptr; - FairMCEventHeader* mHeader = nullptr; -}; -} // namespace its -} // namespace o2 - -#endif /* ALICEO2_ITS_TRIVIALVERTEXER_H */ diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEst.cxx b/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEst.cxx deleted file mode 100644 index c547996c6f356..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEst.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. - -/// \file FastMultEst.h -/// \brief Fast multiplicity estimator for ITS -/// \author ruben.shahoyan@cern.ch - -#include "ITSReconstruction/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 - // - const auto& conf = FastMultEstConfig::Instance(); - float accSum = 0., accWSum = 0., noiseSum = 0.; - nLayersUsed = 0; - for (int il = conf.firstLayer; il <= conf.lastLayer; il++) { - if (ncl[il] > 0) { - float err = 1. / ncl[il]; - accSum += conf.accCorr[il]; - accWSum += conf.accCorr[il] * conf.accCorr[il] * err; - noiseSum += o2::itsmft::ChipMappingITS::getNChipsPerLr(il) * conf.accCorr[il] * err; - nLayersUsed++; - } - } - mult = 0; - if (nLayersUsed) { - mult = (accSum - noisePerChip * noiseSum) / accWSum; - } - return mult; -} - -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/ITSMFT/ITS/reconstruction/src/ITSReconstructionLinkDef.h b/Detectors/ITSMFT/ITS/reconstruction/src/ITSReconstructionLinkDef.h index 67622303fc840..3bc8ee0f5403b 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/src/ITSReconstructionLinkDef.h +++ b/Detectors/ITSMFT/ITS/reconstruction/src/ITSReconstructionLinkDef.h @@ -16,8 +16,5 @@ #pragma link off all functions; #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> + ; #endif diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/TrivialVertexer.cxx b/Detectors/ITSMFT/ITS/reconstruction/src/TrivialVertexer.cxx deleted file mode 100644 index cb7f1eeacb02e..0000000000000 --- a/Detectors/ITSMFT/ITS/reconstruction/src/TrivialVertexer.cxx +++ /dev/null @@ -1,108 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file TrivialVertexer.cxx -/// \brief Implementation of the ITS trivial vertex finder - -#include - -#include "TFile.h" -#include "TTree.h" - -#include "FairMCEventHeader.h" -#include - -#include "ITSReconstruction/TrivialVertexer.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" - -using namespace o2::itsmft; -using namespace o2::its; - -using Point3Df = o2::math_utils::Point3D; - -TrivialVertexer::TrivialVertexer() = default; - -TrivialVertexer::~TrivialVertexer() -{ - if (mHeader) - delete mHeader; - if (mTree) - delete mTree; - if (mFile) - delete mFile; -} - -Bool_t TrivialVertexer::openInputFile(const Char_t* fname) -{ - mFile = TFile::Open(fname, "old"); - if (!mFile) { - LOG(error) << "TrivialVertexer::openInputFile() : " - << "Cannot open the input file !"; - return kFALSE; - } - mTree = (TTree*)mFile->Get("o2sim"); - if (!mTree) { - LOG(error) << "TrivialVertexer::openInputFile() : " - << "Cannot get the input tree !"; - return kFALSE; - } - Int_t rc = mTree->SetBranchAddress("MCEventHeader.", &mHeader); - if (rc != 0) { - LOG(error) << "TrivialVertexer::openInputFile() : " - << "Cannot get the input branch ! rc=" << rc; - return kFALSE; - } - return kTRUE; -} - -void TrivialVertexer::process(const std::vector& clusters, std::vector>& vertices) -{ - if (mClsLabels == nullptr) { - LOG(info) << "TrivialVertexer::process() : " - << "No cluster labels available ! Running with a default MC vertex..."; - vertices.emplace_back(std::array{0., 0., 0.}); - return; - } - - if (mTree == nullptr) { - LOG(info) << "TrivialVertexer::process() : " - << "No MC information available ! Running with a default MC vertex..."; - vertices.emplace_back(std::array{0., 0., 0.}); - return; - } - - Int_t lastEventID = 0; - Int_t firstEventID = std::numeric_limits::max(); - - // Find the first and last MC event within this TF - for (Int_t i = 0; i < clusters.size(); ++i) { - auto mclab = (mClsLabels->getLabels(i))[0]; - if (mclab.getTrackID() == -1) - continue; // noise - auto id = mclab.getEventID(); - if (id < firstEventID) - firstEventID = id; - if (id > lastEventID) - lastEventID = id; - } - - for (Int_t mcEv = firstEventID; mcEv <= lastEventID; ++mcEv) { - mTree->GetEvent(mcEv); - Double_t vx = mHeader->GetX(); - Double_t vy = mHeader->GetY(); - Double_t vz = mHeader->GetZ(); - vertices.emplace_back(std::array{vx, vy, vz}); - LOG(info) << "TrivialVertexer::process() : " - << "MC event #" << mcEv << " with vertex (" << vx << ',' << vy << ',' << vz << ')'; - } -} diff --git a/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt index 001ee537f50d2..8d8304d16764f 100644 --- a/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt @@ -14,15 +14,15 @@ o2_add_library(ITStracking SOURCES src/ClusterLines.cxx src/Cluster.cxx src/Configuration.cxx + src/FastMultEstConfig.cxx + src/FastMultEst.cxx src/TimeFrame.cxx src/IOUtils.cxx src/Tracker.cxx src/TrackerTraits.cxx src/TrackingConfigParam.cxx - src/ClusterLines.cxx src/Vertexer.cxx src/VertexerTraits.cxx - src/Smoother.cxx PUBLIC_LINK_LIBRARIES O2::GPUCommon Microsoft.GSL::GSL @@ -30,6 +30,7 @@ o2_add_library(ITStracking O2::DataFormatsITSMFT O2::SimulationDataFormat O2::ITSBase + O2::CommonUtils O2::ITSReconstruction O2::ITSMFTReconstruction O2::DataFormatsITS @@ -50,6 +51,9 @@ o2_target_root_dictionary(ITStracking HEADERS include/ITStracking/ClusterLines.h include/ITStracking/Tracklet.h include/ITStracking/Cluster.h + include/ITStracking/Definitions.h + include/ITStracking/FastMultEst.h + include/ITStracking/FastMultEstConfig.h include/ITStracking/TrackingConfigParam.h LINKDEF src/TrackingLinkDef.h) diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/ClusterLinesGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/ClusterLinesGPU.h deleted file mode 100644 index 75d75e0f67700..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/ClusterLinesGPU.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 ClusterLinesGPU.h -/// \brief GPU-compliant version of ClusterLines, for the moment separated, might create a common traits for ClusterLines + later specifications for each arch, later. - -#ifndef ITSTRACKINGGPU_CLUSTERLINESGPU_H_ -#define ITSTRACKINGGPU_CLUSTERLINESGPU_H_ - -#include "GPUCommonDef.h" -#include /// Required to properly compile MathUtils -#include "ITStracking/ClusterLines.h" - -namespace o2 -{ -namespace its -{ -namespace gpu -{ - -struct GPUVertex final { - GPUhd() GPUVertex() : realVertex{false} - { - } - - GPUhd() GPUVertex(float x, float y, float z, float eX, float eY, float eZ, int contrib) : xCoord{x}, - yCoord{y}, - zCoord{z}, - errorX{eZ}, - errorY{eY}, - errorZ{eZ}, - contributors{contrib}, - realVertex{true} - { - } - float xCoord; - float yCoord; - float zCoord; - float errorX; - float errorY; - float errorZ; - int contributors; - int timeStamp; - unsigned char realVertex; -}; - -class ClusterLinesGPU final -{ - public: - GPUd() ClusterLinesGPU(const Line& firstLine, const Line& secondLine); // poor man solution to calculate duplets' centroid - GPUd() void computeClusterCentroid(); - GPUdi() float* getVertex() { return mVertex; } - - private: - float mAMatrix[6]; // AX=B - float mBMatrix[3]; // AX=B - float mVertexCandidate[3]; // vertex candidate - float mWeightMatrix[9]; // weight matrix - float mVertex[3]; // cluster centroid position -}; - -} // namespace gpu -} // namespace its -} // namespace o2 -#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameChunk.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameChunk.h deleted file mode 100644 index 4a028bf12eb40..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameChunk.h +++ /dev/null @@ -1,148 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// - -#ifndef TRACKINGITSGPU_INCLUDE_TIMEFRAMECHUNKGPU_H -#define TRACKINGITSGPU_INCLUDE_TIMEFRAMECHUNKGPU_H - -#include "ITStracking/Configuration.h" -#include "ITStracking/TimeFrame.h" - -#include "ITStrackingGPU/ClusterLinesGPU.h" -#include "ITStrackingGPU/Stream.h" - -#include - -namespace o2::its::gpu -{ -template -struct StaticTrackingParameters { - StaticTrackingParameters& operator=(const StaticTrackingParameters& t) = default; - void set(const TrackingParameters& pars) - { - ClusterSharing = pars.ClusterSharing; - MinTrackLength = pars.MinTrackLength; - NSigmaCut = pars.NSigmaCut; - PVres = pars.PVres; - DeltaROF = pars.DeltaROF; - ZBins = pars.ZBins; - PhiBins = pars.PhiBins; - CellDeltaTanLambdaSigma = pars.CellDeltaTanLambdaSigma; - } - - /// General parameters - int ClusterSharing = 0; - int MinTrackLength = nLayers; - float NSigmaCut = 5; - float PVres = 1.e-2f; - int DeltaROF = 0; - int ZBins{256}; - int PhiBins{128}; - - /// Cell finding cuts - float CellDeltaTanLambdaSigma = 0.007f; -}; - -template -class GpuTimeFrameChunk -{ - public: - static size_t computeScalingSizeBytes(const int, const TimeFrameGPUParameters&); - static size_t computeFixedSizeBytes(const TimeFrameGPUParameters&); - static size_t computeRofPerChunk(const TimeFrameGPUParameters&, const size_t); - - GpuTimeFrameChunk() = delete; - GpuTimeFrameChunk(o2::its::TimeFrame* tf, TimeFrameGPUParameters& conf) - { - mTimeFramePtr = tf; - mTFGPUParams = &conf; - } - ~GpuTimeFrameChunk(); - - /// Most relevant operations - void allocate(const size_t, Stream&); - void reset(const Task, Stream&); - size_t loadDataOnDevice(const size_t, const size_t, const int, Stream&); - - /// Interface - Cluster* getDeviceClusters(const int); - int* getDeviceClusterExternalIndices(const int); - int* getDeviceIndexTables(const int); - Tracklet* getDeviceTracklets(const int); - int* getDeviceTrackletsLookupTables(const int); - CellSeed* getDeviceCells(const int); - int* getDeviceCellsLookupTables(const int); - int* getDeviceRoadsLookupTables(const int); - TimeFrameGPUParameters* getTimeFrameGPUParameters() const { return mTFGPUParams; } - - int* getDeviceCUBTmpBuffer() { return mCUBTmpBufferDevice; } - int* getDeviceFoundTracklets() { return mFoundTrackletsDevice; } - int* getDeviceNFoundCells() { return mNFoundCellsDevice; } - int* getDeviceCellNeigboursLookupTables(const int); - int* getDeviceCellNeighbours(const int); - CellSeed** getDeviceArrayCells() const { return mCellsDeviceArray; } - int** getDeviceArrayNeighboursCell() const { return mNeighboursCellDeviceArray; } - int** getDeviceArrayNeighboursCellLUT() const { return mNeighboursCellLookupTablesDeviceArray; } - - /// Vertexer only - int* getDeviceNTrackletCluster(const int combid) { return mNTrackletsPerClusterDevice[combid]; } - Line* getDeviceLines() { return mLinesDevice; }; - int* getDeviceNFoundLines() { return mNFoundLinesDevice; } - int* getDeviceNExclusiveFoundLines() { return mNExclusiveFoundLinesDevice; } - unsigned char* getDeviceUsedTracklets() { return mUsedTrackletsDevice; } - int* getDeviceClusteredLines() { return mClusteredLinesDevice; } - size_t getNPopulatedRof() const { return mNPopulatedRof; } - - private: - /// Host - std::array, nLayers> mHostClusters; - std::array, nLayers> mHostIndexTables; - - /// Device - std::array mClustersDevice; - std::array mClusterExternalIndicesDevice; - std::array mIndexTablesDevice; - std::array mTrackletsDevice; - std::array mTrackletsLookupTablesDevice; - std::array mCellsDevice; - // Road* mRoadsDevice; - std::array mCellsLookupTablesDevice; - std::array mNeighboursCellDevice; - std::array mNeighboursCellLookupTablesDevice; - std::array mRoadsLookupTablesDevice; - - // These are to make them accessible using layer index - CellSeed** mCellsDeviceArray; - int** mNeighboursCellDeviceArray; - int** mNeighboursCellLookupTablesDeviceArray; - - // Small accessory buffers - int* mCUBTmpBufferDevice; - int* mFoundTrackletsDevice; - int* mNFoundCellsDevice; - - /// Vertexer only - Line* mLinesDevice; - int* mNFoundLinesDevice; - int* mNExclusiveFoundLinesDevice; - unsigned char* mUsedTrackletsDevice; - std::array mNTrackletsPerClusterDevice; - int* mClusteredLinesDevice; - - /// State and configuration - bool mAllocated = false; - size_t mNRof = 0; - size_t mNPopulatedRof = 0; - o2::its::TimeFrame* mTimeFramePtr = nullptr; - TimeFrameGPUParameters* mTFGPUParams = nullptr; -}; -} // namespace o2::its::gpu -#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h index d6d87eb8c1143..cf1295e08bd76 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h @@ -24,40 +24,44 @@ namespace o2::its::gpu { -template -class TimeFrameGPU final : public TimeFrame +template +class TimeFrameGPU final : public TimeFrame { - using typename TimeFrame::CellSeedN; - using typename TimeFrame::IndexTableUtilsN; + using typename TimeFrame::CellSeedN; + using typename TimeFrame::IndexTableUtilsN; + using typename TimeFrame::ROFOverlapTableN; + using typename TimeFrame::ROFVertexLookupTableN; + using typename TimeFrame::ROFMaskTableN; public: TimeFrameGPU() = default; - ~TimeFrameGPU() = default; + ~TimeFrameGPU() final = 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); - void initDevice(IndexTableUtilsN*, const TrackingParameters& trkParam, const TimeFrameGPUParameters&, const int, const int); - void initDeviceSAFitting(); + void initialise(const int, const TrackingParameters&, const int); void loadIndexTableUtils(const int); void loadTrackingFrameInfoDevice(const int, const int); void createTrackingFrameInfoDeviceArray(const int); void loadUnsortedClustersDevice(const int, const int); - void createUnsortedClustersDeviceArray(const int, const int = nLayers); + void createUnsortedClustersDeviceArray(const int, const int = NLayers); void loadClustersDevice(const int, const int); - void createClustersDeviceArray(const int, const int = nLayers); + void createClustersDeviceArray(const int, const int = NLayers); void loadClustersIndexTables(const int, const int); void createClustersIndexTablesArray(const int); void createUsedClustersDevice(const int, const int); - void createUsedClustersDeviceArray(const int, const int = nLayers); + void createUsedClustersDeviceArray(const int, const int = NLayers); void loadUsedClustersDevice(); void loadROFrameClustersDevice(const int, const int); void createROFrameClustersDeviceArray(const int); - void loadMultiplicityCutMask(const int); + void loadROFCutMask(const int); void loadVertices(const int); + void loadROFOverlapTable(const int); + void loadROFVertexLookupTable(const int); + void updateROFVertexLookupTable(const int); /// void createTrackletsLUTDevice(const int, const int); @@ -68,7 +72,6 @@ class TimeFrameGPU final : public TimeFrame void loadCellsLUTDevice(); void loadTrackSeedsDevice(); void loadTrackSeedsChi2Device(); - void loadRoadsDevice(); void loadTrackSeedsDevice(bounded_vector&); void createTrackletsBuffers(const int); void createTrackletsBuffersArray(const int); @@ -87,12 +90,6 @@ class TimeFrameGPU final : public TimeFrame void downloadCellsDevice(); void downloadCellsLUTDevice(); - /// Vertexer - void createVtxTrackletsLUTDevice(const int32_t); - void createVtxTrackletsBuffers(const int32_t); - void createVtxLinesLUTDevice(const int32_t); - void createVtxLinesBuffer(const int32_t); - /// synchronization auto& getStream(const size_t stream) { return mGpuStreams[stream]; } auto& getStreams() { return mGpuStreams; } @@ -100,7 +97,7 @@ class TimeFrameGPU final : public TimeFrame void syncStreams(const bool = true); void waitEvent(const int, const int); void recordEvent(const int); - void recordEvents(const int = 0, const int = nLayers); + void recordEvents(const int = 0, const int = NLayers); /// cleanup virtual void wipe() final; @@ -108,8 +105,10 @@ class TimeFrameGPU final : public TimeFrame /// interface virtual bool isGPU() const noexcept final { return true; } virtual const char* getName() const noexcept { return "GPU"; } - int getNClustersInRofSpan(const int, const int, const int) const; IndexTableUtilsN* getDeviceIndexTableUtils() { return mIndexTableUtilsDevice; } + const auto getDeviceROFOverlapTableView() { return mDeviceROFOverlapTableView; } + const auto getDeviceROFVertexLookupTableView() { return mDeviceROFVertexLookupTableView; } + const auto getDeviceROFMaskTableView() { return mDeviceROFMaskTableView; } int* getDeviceROFramesClusters(const int layer) { return mROFramesClustersDevice[layer]; } auto& getTrackITSExt() { return mTrackITSExt; } Vertex* getDeviceVertices() { return mPrimaryVerticesDevice; } @@ -118,12 +117,11 @@ class TimeFrameGPU final : public TimeFrame const o2::base::Propagator* getChainPropagator(); // Hybrid - Road* getDeviceRoads() { return mRoadsDevice; } TrackITSExt* getDeviceTrackITSExt() { return mTrackITSExtDevice; } int* getDeviceNeighboursLUT(const int layer) { return mNeighboursLUTDevice[layer]; } gsl::span getDeviceNeighboursLUTs() { return mNeighboursLUTDevice; } gpuPair* getDeviceNeighbourPairs(const int layer) { return mNeighbourPairsDevice[layer]; } - std::array& getDeviceNeighboursAll() { return mNeighboursDevice; } + std::array& getDeviceNeighboursAll() { return mNeighboursDevice; } int* getDeviceNeighbours(const int layer) { return mNeighboursDevice[layer]; } int** getDeviceNeighboursArray() { return mNeighboursDevice.data(); } TrackingFrameInfo* getDeviceTrackingFrameInfo(const int); @@ -145,28 +143,14 @@ class TimeFrameGPU final : public TimeFrame o2::track::TrackParCovF** getDeviceArrayTrackSeeds() { return mCellSeedsDeviceArray; } float** getDeviceArrayTrackSeedsChi2() { return mCellSeedsChi2DeviceArray; } int* getDeviceNeighboursIndexTables(const int layer) { return mNeighboursIndexTablesDevice[layer]; } - uint8_t* getDeviceMultCutMask() { return mMultMaskDevice; } - - // Vertexer - auto& getDeviceNTrackletsPerROF() const noexcept { return mNTrackletsPerROFDevice; } - auto& getDeviceNTrackletsPerCluster() const noexcept { return mNTrackletsPerClusterDevice; } - auto& getDeviceNTrackletsPerClusterSum() const noexcept { return mNTrackletsPerClusterSumDevice; } - int32_t** getDeviceArrayNTrackletsPerROF() const noexcept { return mNTrackletsPerROFDeviceArray; } - int32_t** getDeviceArrayNTrackletsPerCluster() const noexcept { return mNTrackletsPerClusterDeviceArray; } - int32_t** getDeviceArrayNTrackletsPerClusterSum() const noexcept { return mNTrackletsPerClusterSumDeviceArray; } - uint8_t* getDeviceUsedTracklets() const noexcept { return mUsedTrackletsDevice; } - int32_t* getDeviceNLinesPerCluster() const noexcept { return mNLinesPerClusterDevice; } - int32_t* getDeviceNLinesPerClusterSum() const noexcept { return mNLinesPerClusterSumDevice; } - Line* getDeviceLines() const noexcept { return mLinesDevice; } - gsl::span getDeviceTrackletsPerROFs() { return mNTrackletsPerROFDevice; } void setDevicePropagator(const o2::base::PropagatorImpl* p) final { this->mPropagatorDevice = p; } // Host-specific getters - gsl::span getNTracklets() { return mNTracklets; } - gsl::span getNCells() { return mNCells; } + gsl::span getNTracklets() { return mNTracklets; } + gsl::span getNCells() { return mNCells; } auto& getArrayNCells() { return mNCells; } - gsl::span getNNeighbours() { return mNNeighbours; } + gsl::span getNNeighbours() { return mNNeighbours; } auto& getArrayNNeighbours() { return mNNeighbours; } // Host-available device getters @@ -176,98 +160,81 @@ class TimeFrameGPU final : public TimeFrame gsl::span getDeviceCells() { return mCellsDevice; } // Overridden getters - int getNumberOfTracklets() const final; - int getNumberOfCells() const final; - int getNumberOfNeighbours() const final; + size_t getNumberOfTracklets() const final; + size_t getNumberOfCells() const final; + size_t getNumberOfNeighbours() const final; private: void allocMemAsync(void**, size_t, Stream&, bool, int32_t = o2::gpu::GPUMemoryResource::MEMORY_GPU); // Abstract owned and unowned memory allocations on specific stream void allocMem(void**, size_t, bool, int32_t = o2::gpu::GPUMemoryResource::MEMORY_GPU); // Abstract owned and unowned memory allocations on default stream - TimeFrameGPUParameters mGpuParams; // Host-available device buffer sizes - std::array mNTracklets; - std::array mNCells; - std::array mNNeighbours; + std::array mNTracklets; + std::array mNCells; + std::array mNNeighbours; // Device pointers IndexTableUtilsN* mIndexTableUtilsDevice; + // device navigation views + ROFOverlapTableN::View mDeviceROFOverlapTableView; + ROFVertexLookupTableN::View mDeviceROFVertexLookupTableView; + ROFMaskTableN::View mDeviceROFMaskTableView; // Hybrid pref - uint8_t* mMultMaskDevice; Vertex* mPrimaryVerticesDevice; int* mROFramesPVDevice; - std::array mClustersDevice; - std::array mUnsortedClustersDevice; - std::array mClustersIndexTablesDevice; - std::array mUsedClustersDevice; - std::array mROFramesClustersDevice; + std::array mClustersDevice; + std::array mUnsortedClustersDevice; + std::array mClustersIndexTablesDevice; + std::array mUsedClustersDevice; + std::array mROFramesClustersDevice; const Cluster** mClustersDeviceArray; const Cluster** mUnsortedClustersDeviceArray; const int** mClustersIndexTablesDeviceArray; uint8_t** mUsedClustersDeviceArray; const int** mROFramesClustersDeviceArray; - std::array mTrackletsDevice; - std::array mTrackletsLUTDevice; - std::array mCellsLUTDevice; - std::array mNeighboursLUTDevice; + std::array mTrackletsDevice; + std::array mTrackletsLUTDevice; + std::array mCellsLUTDevice; + std::array mNeighboursLUTDevice; Tracklet** mTrackletsDeviceArray{nullptr}; int** mCellsLUTDeviceArray{nullptr}; int** mNeighboursCellDeviceArray{nullptr}; int** mNeighboursCellLUTDeviceArray{nullptr}; int** mTrackletsLUTDeviceArray{nullptr}; - std::array mCellsDevice; + std::array mCellsDevice; CellSeedN** mCellsDeviceArray; - std::array mNeighboursIndexTablesDevice; + std::array mNeighboursIndexTablesDevice; CellSeedN* mTrackSeedsDevice{nullptr}; int* mTrackSeedsLUTDevice{nullptr}; unsigned int mNTracks{0}; - std::array mCellSeedsDevice; + std::array mCellSeedsDevice; o2::track::TrackParCovF** mCellSeedsDeviceArray; - std::array mCellSeedsChi2Device; + std::array mCellSeedsChi2Device; float** mCellSeedsChi2DeviceArray; - Road* mRoadsDevice; TrackITSExt* mTrackITSExtDevice; - std::array*, nLayers - 2> mNeighbourPairsDevice; - std::array mNeighboursDevice; - std::array mTrackingFrameInfoDevice; + std::array*, NLayers - 2> mNeighbourPairsDevice; + std::array mNeighboursDevice; + std::array mTrackingFrameInfoDevice; const TrackingFrameInfo** mTrackingFrameInfoDeviceArray; - /// Vertexer - std::array mNTrackletsPerROFDevice; - std::array mNTrackletsPerClusterDevice; - std::array mNTrackletsPerClusterSumDevice; - uint8_t* mUsedTrackletsDevice; - int32_t* mNLinesPerClusterDevice; - int32_t* mNLinesPerClusterSumDevice; - int32_t** mNTrackletsPerROFDeviceArray; - int32_t** mNTrackletsPerClusterDeviceArray; - int32_t** mNTrackletsPerClusterSumDeviceArray; - Line* mLinesDevice; - // State Streams mGpuStreams; - std::bitset mPinnedUnsortedClusters{0}; - std::bitset mPinnedClusters{0}; - std::bitset mPinnedClustersIndexTables{0}; - std::bitset mPinnedUsedClusters{0}; - std::bitset mPinnedROFramesClusters{0}; - std::bitset mPinnedTrackingFrameInfo{0}; + std::bitset mPinnedUnsortedClusters{0}; + std::bitset mPinnedClusters{0}; + std::bitset mPinnedClustersIndexTables{0}; + std::bitset mPinnedUsedClusters{0}; + std::bitset mPinnedROFramesClusters{0}; + std::bitset mPinnedTrackingFrameInfo{0}; // Temporary buffer for storing output tracks from GPU tracking bounded_vector mTrackITSExt; }; -template -inline int TimeFrameGPU::getNClustersInRofSpan(const int rofIdstart, const int rofSpanSize, const int layerId) const -{ - return static_cast(this->mROFramesClusters[layerId][(rofIdstart + rofSpanSize) < this->mROFramesClusters.size() ? rofIdstart + rofSpanSize : this->mROFramesClusters.size() - 1] - this->mROFramesClusters[layerId][rofIdstart]); -} - -template -inline std::vector TimeFrameGPU::getClusterSizes() +template +inline std::vector TimeFrameGPU::getClusterSizes() { std::vector sizes(this->mUnsortedClusters.size()); std::transform(this->mUnsortedClusters.begin(), this->mUnsortedClusters.end(), sizes.begin(), @@ -275,20 +242,20 @@ inline std::vector TimeFrameGPU::getClusterSizes() return sizes; } -template -inline int TimeFrameGPU::getNumberOfTracklets() const +template +inline size_t TimeFrameGPU::getNumberOfTracklets() const { return std::accumulate(mNTracklets.begin(), mNTracklets.end(), 0); } -template -inline int TimeFrameGPU::getNumberOfCells() const +template +inline size_t TimeFrameGPU::getNumberOfCells() const { return std::accumulate(mNCells.begin(), mNCells.end(), 0); } -template -inline int TimeFrameGPU::getNumberOfNeighbours() const +template +inline size_t TimeFrameGPU::getNumberOfNeighbours() const { return std::accumulate(mNNeighbours.begin(), mNNeighbours.end(), 0); } diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TracerGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TracerGPU.h deleted file mode 100644 index e2bd7266caff9..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TracerGPU.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. -/// -#include "ITStracking/Definitions.h" - -#ifndef TRACKINGITSGPU_INCLUDE_TRACER_H -#define TRACKINGITSGPU_INCLUDE_TRACER_H - -#if defined(__CUDACC__) && defined(__USE_GPU_TRACER__) -namespace o2 -{ -namespace its -{ -namespace gpu -{ -class Tracer -{ - public: - Tracer(const char* name, int color_id = 0); - ~Tracer(); -}; -} // namespace gpu -} // namespace its -} // namespace o2 -#define RANGE(name, cid) o2::its::gpu::Tracer tracer(name, cid); -#else -#define RANGE(name, cid) -#endif - -#endif // TRACKINGITSGPU_INCLUDE_TRACER_H \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackerTraitsGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackerTraitsGPU.h index 7d26e74692aa5..38d2a8ad5ddc2 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackerTraitsGPU.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackerTraitsGPU.h @@ -19,26 +19,23 @@ namespace o2::its { -template -class TrackerTraitsGPU final : public TrackerTraits +template +class TrackerTraitsGPU final : public TrackerTraits { - using typename TrackerTraits::IndexTableUtilsN; + using typename TrackerTraits::IndexTableUtilsN; public: TrackerTraitsGPU() = default; ~TrackerTraitsGPU() final = default; - void adoptTimeFrame(TimeFrame* tf) final; + void adoptTimeFrame(TimeFrame* tf) final; void initialiseTimeFrame(const int iteration) final; - void computeLayerTracklets(const int iteration, int, int) final; + void computeLayerTracklets(const int iteration, int) final; void computeLayerCells(const int iteration) final; void findCellsNeighbours(const int iteration) final; void findRoads(const int iteration) final; - bool supportsExtendTracks() const noexcept final { return false; } - bool supportsFindShortPrimaries() const noexcept final { return false; } - void setBz(float) final; const char* getName() const noexcept final { return "GPU"; } @@ -51,7 +48,7 @@ class TrackerTraitsGPU final : public TrackerTraits private: IndexTableUtilsN* mDeviceIndexTableUtils; - gpu::TimeFrameGPU* mTimeFrameGPU; + gpu::TimeFrameGPU* mTimeFrameGPU; }; } // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h index 53992ccf3eb85..a83d9d0d52e8f 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h @@ -16,6 +16,7 @@ #include #include "ITStracking/BoundedAllocator.h" +#include "ITStracking/ROFLookupTables.h" #include "ITStracking/Definitions.h" #include "ITStrackingGPU/Utils.h" #include "DetectorsBase/Propagator.h" @@ -33,18 +34,15 @@ class Cluster; class TrackITSExt; class ExternalAllocator; -template -void countTrackletsInROFsHandler(const IndexTableUtils* utils, - const uint8_t* multMask, +template +void countTrackletsInROFsHandler(const IndexTableUtils* utils, + const typename ROFMaskTable::View& rofMask, const int layer, - const int startROF, - const int endROF, - const int maxROF, - const int deltaROF, + const typename ROFOverlapTable::View& rofOverlaps, + const typename ROFVertexLookupTable::View& vertexLUT, const int vertexId, const Vertex* vertices, const int* rofPV, - const int nVertices, const Cluster** clusters, std::vector nClusters, const int** ROFClusters, @@ -56,8 +54,8 @@ void countTrackletsInROFsHandler(const IndexTableUtils* utils, const float NSigmaCut, bounded_vector& phiCuts, const float resolutionPV, - std::array& minR, - std::array& maxR, + std::array& minR, + std::array& maxR, bounded_vector& resolutions, std::vector& radii, bounded_vector& mulScatAng, @@ -66,18 +64,15 @@ void countTrackletsInROFsHandler(const IndexTableUtils* utils, const int nThreads, gpu::Streams& streams); -template -void computeTrackletsInROFsHandler(const IndexTableUtils* utils, - const uint8_t* multMask, +template +void computeTrackletsInROFsHandler(const IndexTableUtils* utils, + const typename ROFMaskTable::View& rofMask, const int layer, - const int startROF, - const int endROF, - const int maxROF, - const int deltaROF, + const typename ROFOverlapTable::View& rofOverlaps, + const typename ROFVertexLookupTable::View& vertexLUT, const int vertexId, const Vertex* vertices, const int* rofPV, - const int nVertices, const Cluster** clusters, std::vector nClusters, const int** ROFClusters, @@ -92,8 +87,8 @@ void computeTrackletsInROFsHandler(const IndexTableUtils* utils, const float NSigmaCut, bounded_vector& phiCuts, const float resolutionPV, - std::array& minR, - std::array& maxR, + std::array& minR, + std::array& maxR, bounded_vector& resolutions, std::vector& radii, bounded_vector& mulScatAng, @@ -102,7 +97,7 @@ void computeTrackletsInROFsHandler(const IndexTableUtils* utils, const int nThreads, gpu::Streams& streams); -template +template void countCellsHandler(const Cluster** sortedClusters, const Cluster** unsortedClusters, const TrackingFrameInfo** tfInfo, @@ -110,10 +105,9 @@ void countCellsHandler(const Cluster** sortedClusters, int** trackletsLUT, const int nTracklets, const int layer, - CellSeed* cells, + CellSeed* cells, int** cellsLUTsDeviceArray, int* cellsLUTsHost, - const int deltaROF, const float bz, const float maxChi2ClusterAttachment, const float cellDeltaTanLambdaSigma, @@ -123,7 +117,7 @@ void countCellsHandler(const Cluster** sortedClusters, const int nThreads, gpu::Streams& streams); -template +template void computeCellsHandler(const Cluster** sortedClusters, const Cluster** unsortedClusters, const TrackingFrameInfo** tfInfo, @@ -131,10 +125,9 @@ void computeCellsHandler(const Cluster** sortedClusters, int** trackletsLUT, const int nTracklets, const int layer, - CellSeed* cells, + CellSeed* cells, int** cellsLUTsDeviceArray, int* cellsLUTsHost, - const int deltaROF, const float bz, const float maxChi2ClusterAttachment, const float cellDeltaTanLambdaSigma, @@ -143,14 +136,13 @@ void computeCellsHandler(const Cluster** sortedClusters, const int nThreads, gpu::Streams& streams); -template -void countCellNeighboursHandler(CellSeed** cellsLayersDevice, +template +void countCellNeighboursHandler(CellSeed** cellsLayersDevice, int* neighboursLUTs, int** cellsLUTs, gpuPair* cellNeighbours, int* neighboursIndexTable, const Tracklet** tracklets, - const int deltaROF, const float maxChi2ClusterAttachment, const float bz, const int layerIndex, @@ -162,14 +154,13 @@ void countCellNeighboursHandler(CellSeed** cellsLayersDevice, const int nThreads, gpu::Stream& stream); -template -void computeCellNeighboursHandler(CellSeed** cellsLayersDevice, +template +void computeCellNeighboursHandler(CellSeed** cellsLayersDevice, int* neighboursLUTs, int** cellsLUTs, gpuPair* cellNeighbours, int* neighboursIndexTable, const Tracklet** tracklets, - const int deltaROF, const float maxChi2ClusterAttachment, const float bz, const int layerIndex, @@ -186,17 +177,17 @@ int filterCellNeighboursHandler(gpuPair*, gpu::Stream&, o2::its::ExternalAllocator* = nullptr); -template +template void processNeighboursHandler(const int startLayer, const int startLevel, - CellSeed** allCellSeeds, - CellSeed* currentCellSeeds, - std::array& nCells, + CellSeed** allCellSeeds, + CellSeed* currentCellSeeds, + std::array& nCells, const unsigned char** usedClusters, - std::array& neighbours, + std::array& neighbours, gsl::span neighboursDeviceLUTs, const TrackingFrameInfo** foundTrackingFrameInfo, - bounded_vector>& seedsHost, + bounded_vector>& seedsHost, const float bz, const float MaxChi2ClusterAttachment, const float maxChi2NDF, @@ -206,8 +197,8 @@ void processNeighboursHandler(const int startLayer, const int nBlocks, const int nThreads); -template -void countTrackSeedHandler(CellSeed* trackSeeds, +template +void countTrackSeedHandler(CellSeed* trackSeeds, const TrackingFrameInfo** foundTrackingFrameInfo, const Cluster** unsortedClusters, int* seedLUT, @@ -227,8 +218,8 @@ void countTrackSeedHandler(CellSeed* trackSeeds, const int nBlocks, const int nThreads); -template -void computeTrackSeedHandler(CellSeed* trackSeeds, +template +void computeTrackSeedHandler(CellSeed* trackSeeds, const TrackingFrameInfo** foundTrackingFrameInfo, const Cluster** unsortedClusters, o2::its::TrackITSExt* tracks, diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h index ee0a203f32fda..44cd8d7e7492b 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h @@ -38,7 +38,11 @@ #endif #ifdef ITS_GPU_LOG -#define GPULog(...) LOGP(info, __VA_ARGS__) +#define GPULog(...) \ + do { \ + LOGP(info, __VA_ARGS__); \ + GPUChkErrS(cudaDeviceSynchronize()); \ + } while (0) #else #define GPULog(...) #endif diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexerTraitsGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexerTraitsGPU.h deleted file mode 100644 index dddc247466c65..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexerTraitsGPU.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 VertexerTraitsGPU.h -/// \brief -/// \author matteo.concas@cern.ch - -// #define VTX_DEBUG -#ifndef ITSTRACKINGGPU_VERTEXERTRAITSGPU_H_ -#define ITSTRACKINGGPU_VERTEXERTRAITSGPU_H_ - -#include - -#include "ITStracking/VertexerTraits.h" -#include "ITStracking/Configuration.h" -#include "ITStracking/Cluster.h" -#include "ITStracking/Constants.h" -#include "ITStracking/Definitions.h" -#include "ITStracking/Tracklet.h" - -#include "ITStrackingGPU/TimeFrameGPU.h" - -namespace o2::its -{ - -template -class VertexerTraitsGPU final : public VertexerTraits -{ - public: - void initialise(const TrackingParameters&, const int iteration = 0) final; - void adoptTimeFrame(TimeFrame* tf) noexcept final; - void computeTracklets(const int iteration = 0) final; - void computeTrackletMatching(const int iteration = 0) final; - void computeVertices(const int iteration = 0) final; - void updateVertexingParameters(const std::vector&, const TimeFrameGPUParameters&) final; - - bool isGPU() const noexcept final { return true; } - const char* getName() const noexcept final { return "GPU"; } - - protected: - gpu::TimeFrameGPU* mTimeFrameGPU; - TimeFrameGPUParameters mTfGPUParams; -}; - -} // namespace o2::its - -#endif diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexingKernels.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexingKernels.h deleted file mode 100644 index 67f12bad8486c..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/VertexingKernels.h +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// - -#ifndef ITSTRACKINGGPU_VERTEXINGKERNELS_H_ -#define ITSTRACKINGGPU_VERTEXINGKERNELS_H_ - -#include -#include -#include -#include "ITStracking/Tracklet.h" -#include "ITStracking/Cluster.h" -#include "ITStracking/ClusterLines.h" -#include "ITStrackingGPU/Utils.h" - -namespace o2::its -{ - -/// Trackleting -template -void countTrackletsInROFsHandler(const IndexTableUtils* GPUrestrict() utils, - const uint8_t* GPUrestrict() multMask, - const int32_t nRofs, - const int32_t deltaROF, - const int32_t* GPUrestrict() rofPV, - const int32_t vertPerRofThreshold, - const Cluster** GPUrestrict() clusters, - const uint32_t nClusters, - const int32_t** GPUrestrict() ROFClusters, - const uint8_t** GPUrestrict() usedClusters, - const int32_t** GPUrestrict() clustersIndexTables, - int32_t** trackletsPerClusterLUTs, - int32_t** trackletsPerClusterSumLUTs, - int32_t** trackletsPerROF, - const std::array& trackletsPerClusterLUTsHost, - const std::array& trackletsPerClusterSumLUTsHost, - const int32_t iteration, - const float phiCut, - const int32_t maxTrackletsPerCluster, - const int32_t nBlocks, - const int32_t nThreads, - gpu::Streams& streams); - -template -void computeTrackletsInROFsHandler(const IndexTableUtils* GPUrestrict() utils, - const uint8_t* GPUrestrict() multMask, - const int32_t nRofs, - const int32_t deltaROF, - const int32_t* GPUrestrict() rofPV, - const int vertPerRofThreshold, - const Cluster** GPUrestrict() clusters, - const uint32_t nClusters, - const int32_t** GPUrestrict() ROFClusters, - const uint8_t** GPUrestrict() usedClusters, - const int32_t** GPUrestrict() clustersIndexTables, - Tracklet** GPUrestrict() foundTracklets, - const int32_t** GPUrestrict() trackletsPerClusterLUTs, - const int32_t** GPUrestrict() trackletsPerClusterSumLUTs, - const int32_t** GPUrestrict() trackletsPerROF, - const int32_t iteration, - const float phiCut, - const int32_t maxTrackletsPerCluster, - const int32_t nBlocks, - const int32_t nThreads, - gpu::Streams& streams); - -/// Selection -void countTrackletsMatchingInROFsHandler(const int32_t nRofs, - const int32_t deltaROF, - const uint32_t nClusters, - const int32_t** GPUrestrict() ROFClusters, - const Cluster** GPUrestrict() clusters, - uint8_t** GPUrestrict() usedClusters, - const Tracklet** GPUrestrict() foundTracklets, - uint8_t* GPUrestrict() usedTracklets, - const int32_t** GPUrestrict() trackletsPerClusterLUTs, - const int32_t** GPUrestrict() trackletsPerClusterSumLUTs, - int32_t* GPUrestrict() linesPerClusterLUT, - int32_t* GPUrestrict() linesPerClusterSumLUT, - const int32_t iteration, - const float phiCut, - const float tanLambdaCut, - const int32_t nBlocks, - const int32_t nThreads, - gpu::Streams& streams); - -void computeTrackletsMatchingInROFsHandler(const int32_t nRofs, - const int32_t deltaROF, - const uint32_t nClusters, - const int32_t** GPUrestrict() ROFClusters, - const Cluster** GPUrestrict() clusters, - const uint8_t** GPUrestrict() usedClusters, - const Tracklet** GPUrestrict() foundTracklets, - uint8_t* GPUrestrict() usedTracklets, - const int32_t** GPUrestrict() trackletsPerClusterLUTs, - const int32_t** GPUrestrict() trackletsPerClusterSumLUTs, - const int32_t* GPUrestrict() linesPerClusterSumLUT, - Line* GPUrestrict() lines, - const int32_t iteration, - const float phiCut, - const float tanLambdaCut, - const int32_t nBlocks, - const int32_t nThreads, - gpu::Streams& streams); - -} // namespace o2::its -#endif diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt index e38dbb1ef20e8..38f11265682ce 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/CMakeLists.txt @@ -13,19 +13,13 @@ if(CUDA_ENABLED) find_package(CUDAToolkit) message(STATUS "Building ITS CUDA tracker") - # add_compile_options(-O0 -g -lineinfo -fPIC -DGPU_FORCE_DEVICE_ASSERTS=ON) - # add_compile_definitions(ITS_MEASURE_GPU_TIME) - # add_compile_definitions(ITS_GPU_LOG) o2_add_library(ITStrackingCUDA - SOURCES ClusterLinesGPU.cu - TrackerTraitsGPU.cxx + SOURCES TrackerTraitsGPU.cxx TimeFrameGPU.cu - TracerGPU.cu TrackingKernels.cu - VertexingKernels.cu - VertexerTraitsGPU.cxx PUBLIC_INCLUDE_DIRECTORIES ../ PUBLIC_LINK_LIBRARIES O2::ITStracking + O2::MathUtils O2::SimConfig O2::SimulationDataFormat O2::ReconstructionDataFormats @@ -33,7 +27,14 @@ if(CUDA_ENABLED) PRIVATE_LINK_LIBRARIES O2::GPUTrackingCUDAExternalProvider TARGETVARNAME targetName) + set_target_gpu_arch("CUDA" ${targetName}) + # Enable relocatable device code (needed for separable compilation + debugging) set_property(TARGET ${targetName} PROPERTY CUDA_SEPARABLE_COMPILATION ON) + target_compile_options(${targetName} PRIVATE + $<$:-diag-error=20014> + # $<$:-G;-O0;-Xptxas=-O0> + # $<$:-O0;-g> + ) + # target_compile_definitions(${targetName} PRIVATE ITS_MEASURE_GPU_TIME ITS_GPU_LOG) target_compile_definitions(${targetName} PRIVATE $) - set_target_gpu_arch("CUDA" ${targetName}) endif() diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/ClusterLinesGPU.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/ClusterLinesGPU.cu deleted file mode 100644 index 79f4e40dc5f10..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/ClusterLinesGPU.cu +++ /dev/null @@ -1,138 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \author matteo.concas@cern.ch - -#include -#include "ITStrackingGPU/ClusterLinesGPU.h" - -namespace o2 -{ -namespace its -{ -namespace gpu -{ - -GPUd() ClusterLinesGPU::ClusterLinesGPU(const Line& firstLine, const Line& secondLine) -{ - float covarianceFirst[3]; - float covarianceSecond[3]; - - for (int i{0}; i < 3; ++i) { - covarianceFirst[i] = 1.f; - covarianceSecond[i] = 1.f; - } - - double determinantFirst = - firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[0] * covarianceFirst[1] + - firstLine.cosinesDirector[1] * firstLine.cosinesDirector[1] * covarianceFirst[0] * covarianceFirst[2] + - firstLine.cosinesDirector[0] * firstLine.cosinesDirector[0] * covarianceFirst[1] * covarianceFirst[2]; - double determinantSecond = - secondLine.cosinesDirector[2] * secondLine.cosinesDirector[2] * covarianceSecond[0] * covarianceSecond[1] + - secondLine.cosinesDirector[1] * secondLine.cosinesDirector[1] * covarianceSecond[0] * covarianceSecond[2] + - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[0] * covarianceSecond[1] * covarianceSecond[2]; - - mAMatrix[0] = (firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[1] + - firstLine.cosinesDirector[1] * firstLine.cosinesDirector[1] * covarianceFirst[2]) / - determinantFirst + - (secondLine.cosinesDirector[2] * secondLine.cosinesDirector[2] * covarianceSecond[1] + - secondLine.cosinesDirector[1] * secondLine.cosinesDirector[1] * covarianceSecond[2]) / - determinantSecond; - - mAMatrix[1] = -firstLine.cosinesDirector[0] * firstLine.cosinesDirector[1] * covarianceFirst[2] / determinantFirst - - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[1] * covarianceSecond[2] / determinantSecond; - - mAMatrix[2] = -firstLine.cosinesDirector[0] * firstLine.cosinesDirector[2] * covarianceFirst[1] / determinantFirst - - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[2] * covarianceSecond[1] / determinantSecond; - - mAMatrix[3] = (firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[0] + - firstLine.cosinesDirector[0] * firstLine.cosinesDirector[0] * covarianceFirst[2]) / - determinantFirst + - (secondLine.cosinesDirector[2] * secondLine.cosinesDirector[2] * covarianceSecond[0] + - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[0] * covarianceSecond[2]) / - determinantSecond; - - mAMatrix[4] = -firstLine.cosinesDirector[1] * firstLine.cosinesDirector[2] * covarianceFirst[0] / determinantFirst - - secondLine.cosinesDirector[1] * secondLine.cosinesDirector[2] * covarianceSecond[0] / determinantSecond; - - mAMatrix[5] = (firstLine.cosinesDirector[1] * firstLine.cosinesDirector[1] * covarianceFirst[0] + - firstLine.cosinesDirector[0] * firstLine.cosinesDirector[0] * covarianceFirst[1]) / - determinantFirst + - (secondLine.cosinesDirector[1] * secondLine.cosinesDirector[1] * covarianceSecond[0] + - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[0] * covarianceSecond[1]) / - determinantSecond; - - mBMatrix[0] = - (firstLine.cosinesDirector[1] * covarianceFirst[2] * (-firstLine.cosinesDirector[1] * firstLine.originPoint[0] + firstLine.cosinesDirector[0] * firstLine.originPoint[1]) + - firstLine.cosinesDirector[2] * covarianceFirst[1] * (-firstLine.cosinesDirector[2] * firstLine.originPoint[0] + firstLine.cosinesDirector[0] * firstLine.originPoint[2])) / - determinantFirst; - - mBMatrix[0] += - (secondLine.cosinesDirector[1] * covarianceSecond[2] * (-secondLine.cosinesDirector[1] * secondLine.originPoint[0] + secondLine.cosinesDirector[0] * secondLine.originPoint[1]) + - secondLine.cosinesDirector[2] * covarianceSecond[1] * - (-secondLine.cosinesDirector[2] * secondLine.originPoint[0] + - secondLine.cosinesDirector[0] * secondLine.originPoint[2])) / - determinantSecond; - - mBMatrix[1] = - (firstLine.cosinesDirector[0] * covarianceFirst[2] * (-firstLine.cosinesDirector[0] * firstLine.originPoint[1] + firstLine.cosinesDirector[1] * firstLine.originPoint[0]) + - firstLine.cosinesDirector[2] * covarianceFirst[0] * (-firstLine.cosinesDirector[2] * firstLine.originPoint[1] + firstLine.cosinesDirector[1] * firstLine.originPoint[2])) / - determinantFirst; - - mBMatrix[1] += - (secondLine.cosinesDirector[0] * covarianceSecond[2] * (-secondLine.cosinesDirector[0] * secondLine.originPoint[1] + secondLine.cosinesDirector[1] * secondLine.originPoint[0]) + - secondLine.cosinesDirector[2] * covarianceSecond[0] * - (-secondLine.cosinesDirector[2] * secondLine.originPoint[1] + - secondLine.cosinesDirector[1] * secondLine.originPoint[2])) / - determinantSecond; - - mBMatrix[2] = - (firstLine.cosinesDirector[0] * covarianceFirst[1] * (-firstLine.cosinesDirector[0] * firstLine.originPoint[2] + firstLine.cosinesDirector[2] * firstLine.originPoint[0]) + - firstLine.cosinesDirector[1] * covarianceFirst[0] * (-firstLine.cosinesDirector[1] * firstLine.originPoint[2] + firstLine.cosinesDirector[2] * firstLine.originPoint[1])) / - determinantFirst; - - mBMatrix[2] += - (secondLine.cosinesDirector[0] * covarianceSecond[1] * (-secondLine.cosinesDirector[0] * secondLine.originPoint[2] + secondLine.cosinesDirector[2] * secondLine.originPoint[0]) + - secondLine.cosinesDirector[1] * covarianceSecond[0] * - (-secondLine.cosinesDirector[1] * secondLine.originPoint[2] + - secondLine.cosinesDirector[2] * secondLine.originPoint[1])) / - determinantSecond; - - computeClusterCentroid(); -} - -GPUd() void ClusterLinesGPU::computeClusterCentroid() -{ - - double determinant{mAMatrix[0] * (mAMatrix[3] * mAMatrix[5] - mAMatrix[4] * mAMatrix[4]) - - mAMatrix[1] * (mAMatrix[1] * mAMatrix[5] - mAMatrix[4] * mAMatrix[2]) + - mAMatrix[2] * (mAMatrix[1] * mAMatrix[4] - mAMatrix[2] * mAMatrix[3])}; - - if (determinant == 0) { - return; - } - - mVertex[0] = -(mBMatrix[0] * (mAMatrix[3] * mAMatrix[5] - mAMatrix[4] * mAMatrix[4]) - - mAMatrix[1] * (mBMatrix[1] * mAMatrix[5] - mAMatrix[4] * mBMatrix[2]) + - mAMatrix[2] * (mBMatrix[1] * mAMatrix[4] - mBMatrix[2] * mAMatrix[3])) / - determinant; - mVertex[1] = -(mAMatrix[0] * (mBMatrix[1] * mAMatrix[5] - mBMatrix[2] * mAMatrix[4]) - - mBMatrix[0] * (mAMatrix[1] * mAMatrix[5] - mAMatrix[4] * mAMatrix[2]) + - mAMatrix[2] * (mAMatrix[1] * mBMatrix[2] - mAMatrix[2] * mBMatrix[1])) / - determinant; - mVertex[2] = -(mAMatrix[0] * (mAMatrix[3] * mBMatrix[2] - mBMatrix[1] * mAMatrix[4]) - - mAMatrix[1] * (mAMatrix[1] * mBMatrix[2] - mBMatrix[1] * mAMatrix[2]) + - mBMatrix[0] * (mAMatrix[1] * mAMatrix[4] - mAMatrix[2] * mAMatrix[3])) / - determinant; -} -} // namespace gpu -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameChunk.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameChunk.cu deleted file mode 100644 index c8512e667aea8..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameChunk.cu +++ /dev/null @@ -1,293 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include -#include -#include - -#include "ITStracking/Constants.h" - -#include "ITStrackingGPU/Utils.h" -#include "ITStrackingGPU/TracerGPU.h" - -#include "ITStrackingGPU/TimeFrameChunk.h" - -#include -#include - -#include "GPUCommonDef.h" -#include "GPUCommonMath.h" -#include "GPUCommonLogger.h" -#include "GPUCommonHelpers.h" - -#ifndef __HIPCC__ -#define THRUST_NAMESPACE thrust::cuda -#else -#define THRUST_NAMESPACE thrust::hip -#endif - -namespace o2::its -{ -using constants::GB; -using constants::MB; -namespace gpu -{ - -template -GpuTimeFrameChunk::~GpuTimeFrameChunk() -{ - if (mAllocated) { - for (int i = 0; i < nLayers; ++i) { - GPUChkErrS(cudaFree(mClustersDevice[i])); - // GPUChkErrS(cudaFree(mTrackingFrameInfoDevice[i])); - GPUChkErrS(cudaFree(mClusterExternalIndicesDevice[i])); - GPUChkErrS(cudaFree(mIndexTablesDevice[i])); - if (i < nLayers - 1) { - GPUChkErrS(cudaFree(mTrackletsDevice[i])); - GPUChkErrS(cudaFree(mTrackletsLookupTablesDevice[i])); - if (i < nLayers - 2) { - GPUChkErrS(cudaFree(mCellsDevice[i])); - GPUChkErrS(cudaFree(mCellsLookupTablesDevice[i])); - GPUChkErrS(cudaFree(mRoadsLookupTablesDevice[i])); - if (i < nLayers - 3) { - GPUChkErrS(cudaFree(mNeighboursCellLookupTablesDevice[i])); - GPUChkErrS(cudaFree(mNeighboursCellDevice[i])); - } - } - } - } - // GPUChkErrS(cudaFree(mRoadsDevice)); - GPUChkErrS(cudaFree(mCUBTmpBufferDevice)); - GPUChkErrS(cudaFree(mFoundTrackletsDevice)); - GPUChkErrS(cudaFree(mNFoundCellsDevice)); - GPUChkErrS(cudaFree(mCellsDeviceArray)); - GPUChkErrS(cudaFree(mNeighboursCellDeviceArray)); - GPUChkErrS(cudaFree(mNeighboursCellLookupTablesDeviceArray)); - } -} - -template -void GpuTimeFrameChunk::allocate(const size_t nrof, Stream& stream) -{ - RANGE("device_partition_allocation", 2); - mNRof = nrof; - // for (int i = 0; i < nLayers; ++i) { - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mClustersDevice[i])), sizeof(Cluster) * mTFGPUParams->clustersPerROfCapacity * nrof, &stream, true); - // // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mTrackingFrameInfoDevice[i])), sizeof(TrackingFrameInfo) * mTFGPUParams->clustersPerROfCapacity * nrof, &stream, true); - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mClusterExternalIndicesDevice[i])), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * nrof, &stream, true); - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mIndexTablesDevice[i])), sizeof(int) * (256 * 128 + 1) * nrof, &stream, true); - // if (i < nLayers - 1) { - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mTrackletsLookupTablesDevice[i])), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * nrof, &stream, true); - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mTrackletsDevice[i])), sizeof(Tracklet) * mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * nrof, &stream, true); - // if (i < nLayers - 2) { - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mCellsLookupTablesDevice[i])), sizeof(int) * mTFGPUParams->validatedTrackletsCapacity * nrof, &stream, true); - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mCellsDevice[i])), sizeof(CellSeed) * mTFGPUParams->maxNeighboursSize * nrof, &stream, true); - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mRoadsLookupTablesDevice[i]), sizeof(int) * mTFGPUParams->maxNeighboursSize * nrof, &stream, true); - // if (i < nLayers - 3) { - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mNeighboursCellLookupTablesDevice[i])), sizeof(int) * mTFGPUParams->maxNeighboursSize * nrof, &stream, true); - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mNeighboursCellDevice[i])), sizeof(int) * mTFGPUParams->maxNeighboursSize * nrof, &stream, true); - // } - // if (i < 2) { - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&(mNTrackletsPerClusterDevice[i])), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * nrof, &stream, true); - // } - // } - // } - // } - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mCUBTmpBufferDevice), mTFGPUParams->tmpCUBBufferSize * nrof, &stream, true); - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mLinesDevice), sizeof(Line) * mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * nrof, &stream, true); - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mNFoundLinesDevice), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * nrof, &stream, true); - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mNExclusiveFoundLinesDevice), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * nrof + 1, &stream, true); // + 1 for cub::DeviceScan::ExclusiveSum, to cover cases where we have maximum number of clusters per ROF - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mUsedTrackletsDevice), sizeof(unsigned char) * mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * nrof, &stream, true); - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mClusteredLinesDevice), sizeof(int) * mTFGPUParams->clustersPerROfCapacity * mTFGPUParams->maxTrackletsPerCluster * nrof, &stream, true); - // // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mRoadsDevice), sizeof(Road) * mTFGPUParams->maxRoadPerRofSize * nrof, &stream, true); - - // /// Invariant allocations - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mFoundTrackletsDevice), (nLayers - 1) * sizeof(int) * nrof, &stream, true); // No need to reset, we always read it after writing - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mNFoundCellsDevice), (nLayers - 2) * sizeof(int) * nrof, &stream, true); - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mCellsDeviceArray), (nLayers - 2) * sizeof(CellSeed*), &stream, true); - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mNeighboursCellDeviceArray), (nLayers - 3) * sizeof(int*), &stream, true); - // static_cast*>(mTimeFramePtr)->allocMemAsync(reinterpret_cast(&mNeighboursCellLookupTablesDeviceArray), (nLayers - 3) * sizeof(int*), &stream, true); - - // /// Copy pointers of allocated memory to regrouping arrays - // GPUChkErrS(cudaMemcpyAsync(mCellsDeviceArray, mCellsDevice.data(), (nLayers - 2) * sizeof(CellSeed*), cudaMemcpyHostToDevice, stream.get())); - // GPUChkErrS(cudaMemcpyAsync(mNeighboursCellDeviceArray, mNeighboursCellDevice.data(), (nLayers - 3) * sizeof(int*), cudaMemcpyHostToDevice, stream.get())); - // GPUChkErrS(cudaMemcpyAsync(mNeighboursCellLookupTablesDeviceArray, mNeighboursCellLookupTablesDevice.data(), (nLayers - 3) * sizeof(int*), cudaMemcpyHostToDevice, stream.get())); - - mAllocated = true; -} - -template -void GpuTimeFrameChunk::reset(const Task task, Stream& stream) -{ - RANGE("buffer_reset", 0); - // if ((bool)task) { // Vertexer-only initialisation (cannot be constexpr: due to the presence of gpu raw calls can't be put in header) - // for (int i = 0; i < 2; i++) { - // auto thrustTrackletsBegin = thrust::device_ptr(mTrackletsDevice[i]); - // auto thrustTrackletsEnd = thrustTrackletsBegin + mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * mNRof; - // thrust::fill(THRUST_NAMESPACE::par.on(stream.get()), thrustTrackletsBegin, thrustTrackletsEnd, Tracklet{}); - // GPUChkErrS(cudaMemsetAsync(mNTrackletsPerClusterDevice[i], 0, sizeof(int) * mTFGPUParams->clustersPerROfCapacity * mNRof, stream.get())); - // } - // GPUChkErrS(cudaMemsetAsync(mUsedTrackletsDevice, false, sizeof(unsigned char) * mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * mNRof, stream.get())); - // GPUChkErrS(cudaMemsetAsync(mClusteredLinesDevice, -1, sizeof(int) * mTFGPUParams->clustersPerROfCapacity * mTFGPUParams->maxTrackletsPerCluster * mNRof, stream.get())); - // } else { - // for (int i = 0; i < nLayers; ++i) { - // if (i < nLayers - 1) { - // GPUChkErrS(cudaMemsetAsync(mTrackletsLookupTablesDevice[i], 0, sizeof(int) * mTFGPUParams->clustersPerROfCapacity * mNRof, stream.get())); - // auto thrustTrackletsBegin = thrust::device_ptr(mTrackletsDevice[i]); - // auto thrustTrackletsEnd = thrustTrackletsBegin + mTFGPUParams->maxTrackletsPerCluster * mTFGPUParams->clustersPerROfCapacity * mNRof; - // thrust::fill(THRUST_NAMESPACE::par.on(stream.get()), thrustTrackletsBegin, thrustTrackletsEnd, Tracklet{}); - // if (i < nLayers - 2) { - // GPUChkErrS(cudaMemsetAsync(mCellsLookupTablesDevice[i], 0, sizeof(int) * mTFGPUParams->cellsLUTsize * mNRof, stream.get())); - // GPUChkErrS(cudaMemsetAsync(mRoadsLookupTablesDevice[i], 0, sizeof(int) * mTFGPUParams->maxNeighboursSize * mNRof, stream.get())); - // if (i < nLayers - 3) { - // GPUChkErrS(cudaMemsetAsync(mNeighboursCellLookupTablesDevice[i], 0, sizeof(int) * mTFGPUParams->maxNeighboursSize * mNRof, stream.get())); - // GPUChkErrS(cudaMemsetAsync(mNeighboursCellDevice[i], 0, sizeof(int) * mTFGPUParams->maxNeighboursSize * mNRof, stream.get())); - // } - // } - // } - // } - // GPUChkErrS(cudaMemsetAsync(mNFoundCellsDevice, 0, (nLayers - 2) * sizeof(int), stream.get())); - // } -} - -template -size_t GpuTimeFrameChunk::computeScalingSizeBytes(const int nrof, const TimeFrameGPUParameters& config) -{ - size_t rofsize = nLayers * sizeof(int); // number of clusters per ROF - // rofsize += nLayers * sizeof(Cluster) * config.clustersPerROfCapacity; // clusters - // rofsize += nLayers * sizeof(TrackingFrameInfo) * config.clustersPerROfCapacity; // tracking frame info - // rofsize += nLayers * sizeof(int) * config.clustersPerROfCapacity; // external cluster indices - // rofsize += nLayers * sizeof(int) * (256 * 128 + 1); // index tables - // rofsize += (nLayers - 1) * sizeof(int) * config.clustersPerROfCapacity; // tracklets lookup tables - // rofsize += (nLayers - 1) * sizeof(Tracklet) * config.maxTrackletsPerCluster * config.clustersPerROfCapacity; // tracklets - // rofsize += 2 * sizeof(int) * config.clustersPerROfCapacity; // tracklets found per cluster (vertexer) - // rofsize += sizeof(unsigned char) * config.maxTrackletsPerCluster * config.clustersPerROfCapacity; // used tracklets (vertexer) - // rofsize += (nLayers - 2) * sizeof(int) * config.validatedTrackletsCapacity; // cells lookup tables - // rofsize += (nLayers - 2) * sizeof(CellSeed) * config.maxNeighboursSize; // cells - // rofsize += (nLayers - 3) * sizeof(int) * config.maxNeighboursSize; // cell neighbours lookup tables - // rofsize += (nLayers - 3) * sizeof(int) * config.maxNeighboursSize; // cell neighbours - // rofsize += sizeof(Road) * config.maxRoadPerRofSize; // roads - // rofsize += (nLayers - 2) * sizeof(int) * config.maxNeighboursSize; // road LUT - // rofsize += sizeof(Line) * config.maxTrackletsPerCluster * config.clustersPerROfCapacity; // lines - // rofsize += sizeof(int) * config.clustersPerROfCapacity; // found lines - // rofsize += sizeof(int) * config.clustersPerROfCapacity; // found lines exclusive sum - // rofsize += sizeof(int) * config.clustersPerROfCapacity * config.maxTrackletsPerCluster; // lines used in clusterlines - - // rofsize += (nLayers - 1) * sizeof(int); // total found tracklets - // rofsize += (nLayers - 2) * sizeof(int); // total found cells - - return rofsize * nrof; -} - -template -size_t GpuTimeFrameChunk::computeFixedSizeBytes(const TimeFrameGPUParameters& config) -{ - size_t total = config.tmpCUBBufferSize; // CUB tmp buffers - total += sizeof(gpu::StaticTrackingParameters); // static parameters loaded once - return total; -} - -template -size_t GpuTimeFrameChunk::computeRofPerChunk(const TimeFrameGPUParameters& config, const size_t m) -{ - return (m * GB / (float)(config.nTimeFrameChunks) - GpuTimeFrameChunk::computeFixedSizeBytes(config)) / (float)GpuTimeFrameChunk::computeScalingSizeBytes(1, config); -} - -/// Interface -template -Cluster* GpuTimeFrameChunk::getDeviceClusters(const int layer) -{ - return mClustersDevice[layer]; -} - -template -int* GpuTimeFrameChunk::getDeviceClusterExternalIndices(const int layer) -{ - return mClusterExternalIndicesDevice[layer]; -} - -template -int* GpuTimeFrameChunk::getDeviceIndexTables(const int layer) -{ - return mIndexTablesDevice[layer]; -} - -template -Tracklet* GpuTimeFrameChunk::getDeviceTracklets(const int layer) -{ - return mTrackletsDevice[layer]; -} - -template -int* GpuTimeFrameChunk::getDeviceTrackletsLookupTables(const int layer) -{ - return mTrackletsLookupTablesDevice[layer]; -} - -template -CellSeed* GpuTimeFrameChunk::getDeviceCells(const int layer) -{ - return mCellsDevice[layer]; -} - -template -int* GpuTimeFrameChunk::getDeviceCellsLookupTables(const int layer) -{ - return mCellsLookupTablesDevice[layer]; -} - -template -int* GpuTimeFrameChunk::getDeviceCellNeigboursLookupTables(const int layer) -{ - return mNeighboursCellLookupTablesDevice[layer]; -} - -template -int* GpuTimeFrameChunk::getDeviceCellNeighbours(const int layer) -{ - return mNeighboursCellDevice[layer]; -} - -template -int* GpuTimeFrameChunk::getDeviceRoadsLookupTables(const int layer) -{ - return mRoadsLookupTablesDevice[layer]; -} - -// Load data -template -size_t GpuTimeFrameChunk::loadDataOnDevice(const size_t startRof, const size_t maxRof, const int maxLayers, Stream& stream) -{ - RANGE("load_clusters_data", 5); - // auto nRofs = std::min(maxRof - startRof, mNRof); - // mNPopulatedRof = mTimeFramePtr->getNClustersROFrange(startRof, nRofs, 0).size(); - // for (int i = 0; i < maxLayers; ++i) { - // mHostClusters[i] = mTimeFramePtr->getClustersPerROFrange(startRof, nRofs, i); - // mHostIndexTables[i] = mTimeFramePtr->getIndexTablePerROFrange(startRof, nRofs, i); - // if (mHostClusters[i].size() > mTFGPUParams->clustersPerROfCapacity * nRofs) { - // LOGP(warning, "Clusters on layer {} exceed the expected value, resizing to config value: {}, will lose information!", i, mTFGPUParams->clustersPerROfCapacity * nRofs); - // } - // GPUChkErrS(cudaMemcpyAsync(mClustersDevice[i], - // mHostClusters[i].data(), - // (int)std::min(mHostClusters[i].size(), mTFGPUParams->clustersPerROfCapacity * nRofs) * sizeof(Cluster), - // cudaMemcpyHostToDevice, stream.get())); - // if (mHostIndexTables[i].data()) { - // GPUChkErrS(cudaMemcpyAsync(mIndexTablesDevice[i], - // mHostIndexTables[i].data(), - // mHostIndexTables[i].size() * sizeof(int), - // cudaMemcpyHostToDevice, stream.get())); - // } - // } - return mNPopulatedRof; // return the number of ROFs we loaded the data for. -} -template class GpuTimeFrameChunk<7>; -} // namespace gpu -} // namespace o2::its \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu index da0cd51478945..a9b51580f9be7 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu @@ -29,8 +29,8 @@ namespace o2::its::gpu { -template -void TimeFrameGPU::allocMemAsync(void** ptr, size_t size, Stream& stream, bool extAllocator, int32_t type) +template +void TimeFrameGPU::allocMemAsync(void** ptr, size_t size, Stream& stream, bool extAllocator, int32_t type) { if (extAllocator) { *ptr = (this->mExternalAllocator)->allocate(size, type); @@ -40,8 +40,8 @@ void TimeFrameGPU::allocMemAsync(void** ptr, size_t size, Stream& strea } } -template -void TimeFrameGPU::allocMem(void** ptr, size_t size, bool extAllocator, int32_t type) +template +void TimeFrameGPU::allocMem(void** ptr, size_t size, bool extAllocator, int32_t type) { if (extAllocator) { *ptr = (this->mExternalAllocator)->allocate(size, type); @@ -51,8 +51,8 @@ void TimeFrameGPU::allocMem(void** ptr, size_t size, bool extAllocator, } } -template -void TimeFrameGPU::loadIndexTableUtils(const int iteration) +template +void TimeFrameGPU::loadIndexTableUtils(const int iteration) { GPUTimer timer("loading indextable utils"); if (!iteration) { @@ -63,16 +63,16 @@ void TimeFrameGPU::loadIndexTableUtils(const int iteration) GPUChkErrS(cudaMemcpy(mIndexTableUtilsDevice, &(this->mIndexTableUtils), sizeof(IndexTableUtilsN), cudaMemcpyHostToDevice)); } -template -void TimeFrameGPU::createUnsortedClustersDeviceArray(const int iteration, const int maxLayers) +template +void TimeFrameGPU::createUnsortedClustersDeviceArray(const int iteration, const int maxLayers) { if (!iteration) { GPUTimer timer("creating unsorted clusters array"); - allocMem(reinterpret_cast(&mUnsortedClustersDeviceArray), nLayers * sizeof(Cluster*), this->hasFrameworkAllocator()); - GPUChkErrS(cudaHostRegister(mUnsortedClustersDevice.data(), nLayers * sizeof(Cluster*), cudaHostRegisterPortable)); - mPinnedUnsortedClusters.set(nLayers); + allocMem(reinterpret_cast(&mUnsortedClustersDeviceArray), NLayers * sizeof(Cluster*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaHostRegister(mUnsortedClustersDevice.data(), NLayers * sizeof(Cluster*), cudaHostRegisterPortable)); + mPinnedUnsortedClusters.set(NLayers); if (!this->hasFrameworkAllocator()) { - for (auto iLayer{0}; iLayer < o2::gpu::CAMath::Min(maxLayers, nLayers); ++iLayer) { + 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); } @@ -80,8 +80,8 @@ void TimeFrameGPU::createUnsortedClustersDeviceArray(const int iteratio } } -template -void TimeFrameGPU::loadUnsortedClustersDevice(const int iteration, const int layer) +template +void TimeFrameGPU::loadUnsortedClustersDevice(const int iteration, const int layer) { if (!iteration) { GPUTimer timer(mGpuStreams[layer], "loading unsorted clusters", layer); @@ -92,16 +92,16 @@ void TimeFrameGPU::loadUnsortedClustersDevice(const int iteration, cons } } -template -void TimeFrameGPU::createClustersDeviceArray(const int iteration, const int maxLayers) +template +void TimeFrameGPU::createClustersDeviceArray(const int iteration, const int maxLayers) { if (!iteration) { GPUTimer timer("creating sorted clusters array"); - allocMem(reinterpret_cast(&mClustersDeviceArray), nLayers * sizeof(Cluster*), this->hasFrameworkAllocator()); - GPUChkErrS(cudaHostRegister(mClustersDevice.data(), nLayers * sizeof(Cluster*), cudaHostRegisterPortable)); - mPinnedClusters.set(nLayers); + allocMem(reinterpret_cast(&mClustersDeviceArray), NLayers * sizeof(Cluster*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaHostRegister(mClustersDevice.data(), NLayers * sizeof(Cluster*), cudaHostRegisterPortable)); + mPinnedClusters.set(NLayers); if (!this->hasFrameworkAllocator()) { - for (auto iLayer{0}; iLayer < o2::gpu::CAMath::Min(maxLayers, nLayers); ++iLayer) { + 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); } @@ -109,8 +109,8 @@ void TimeFrameGPU::createClustersDeviceArray(const int iteration, const } } -template -void TimeFrameGPU::loadClustersDevice(const int iteration, const int layer) +template +void TimeFrameGPU::loadClustersDevice(const int iteration, const int layer) { if (!iteration) { GPUTimer timer(mGpuStreams[layer], "loading sorted clusters", layer); @@ -121,16 +121,16 @@ void TimeFrameGPU::loadClustersDevice(const int iteration, const int la } } -template -void TimeFrameGPU::createClustersIndexTablesArray(const int iteration) +template +void TimeFrameGPU::createClustersIndexTablesArray(const int iteration) { if (!iteration) { GPUTimer timer("creating clustersindextable array"); - allocMem(reinterpret_cast(&mClustersIndexTablesDeviceArray), nLayers * sizeof(int*), this->hasFrameworkAllocator()); - GPUChkErrS(cudaHostRegister(mClustersIndexTablesDevice.data(), nLayers * sizeof(int*), cudaHostRegisterPortable)); - mPinnedClustersIndexTables.set(nLayers); + allocMem(reinterpret_cast(&mClustersIndexTablesDeviceArray), NLayers * sizeof(int*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaHostRegister(mClustersIndexTablesDevice.data(), NLayers * sizeof(int*), cudaHostRegisterPortable)); + mPinnedClustersIndexTables.set(NLayers); if (!this->hasFrameworkAllocator()) { - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { + for (auto iLayer{0}; iLayer < NLayers; ++iLayer) { GPUChkErrS(cudaHostRegister(this->mIndexTables[iLayer].data(), this->mIndexTables[iLayer].size() * sizeof(int), cudaHostRegisterPortable)); mPinnedClustersIndexTables.set(iLayer); } @@ -138,8 +138,8 @@ void TimeFrameGPU::createClustersIndexTablesArray(const int iteration) } } -template -void TimeFrameGPU::loadClustersIndexTables(const int iteration, const int layer) +template +void TimeFrameGPU::loadClustersIndexTables(const int iteration, const int layer) { if (!iteration) { GPUTimer timer(mGpuStreams[layer], "loading sorted clusters", layer); @@ -150,16 +150,16 @@ void TimeFrameGPU::loadClustersIndexTables(const int iteration, const i } } -template -void TimeFrameGPU::createUsedClustersDeviceArray(const int iteration, const int maxLayers) +template +void TimeFrameGPU::createUsedClustersDeviceArray(const int iteration, const int maxLayers) { if (!iteration) { GPUTimer timer("creating used clusters flags"); - allocMem(reinterpret_cast(&mUsedClustersDeviceArray), nLayers * sizeof(uint8_t*), this->hasFrameworkAllocator()); - GPUChkErrS(cudaHostRegister(mUsedClustersDevice.data(), nLayers * sizeof(uint8_t*), cudaHostRegisterPortable)); - mPinnedUsedClusters.set(nLayers); + allocMem(reinterpret_cast(&mUsedClustersDeviceArray), NLayers * sizeof(uint8_t*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaHostRegister(mUsedClustersDevice.data(), NLayers * sizeof(uint8_t*), cudaHostRegisterPortable)); + mPinnedUsedClusters.set(NLayers); if (!this->hasFrameworkAllocator()) { - for (auto iLayer{0}; iLayer < o2::gpu::CAMath::Min(maxLayers, nLayers); ++iLayer) { + 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); } @@ -167,8 +167,8 @@ void TimeFrameGPU::createUsedClustersDeviceArray(const int iteration, c } } -template -void TimeFrameGPU::createUsedClustersDevice(const int iteration, const int layer) +template +void TimeFrameGPU::createUsedClustersDevice(const int iteration, const int layer) { if (!iteration) { GPUTimer timer(mGpuStreams[layer], "creating used clusters flags", layer); @@ -179,26 +179,26 @@ void TimeFrameGPU::createUsedClustersDevice(const int iteration, const } } -template -void TimeFrameGPU::loadUsedClustersDevice() +template +void TimeFrameGPU::loadUsedClustersDevice() { - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { + for (auto iLayer{0}; iLayer < NLayers; ++iLayer) { GPUTimer timer(mGpuStreams[iLayer], "loading used clusters flags", iLayer); GPULog("gpu-transfer: loading {} used clusters flags on layer {}, for {:.2f} MB.", this->mUsedClusters[iLayer].size(), iLayer, this->mUsedClusters[iLayer].size() * sizeof(unsigned char) / constants::MB); GPUChkErrS(cudaMemcpyAsync(mUsedClustersDevice[iLayer], this->mUsedClusters[iLayer].data(), this->mUsedClusters[iLayer].size() * sizeof(unsigned char), cudaMemcpyHostToDevice, mGpuStreams[iLayer].get())); } } -template -void TimeFrameGPU::createROFrameClustersDeviceArray(const int iteration) +template +void TimeFrameGPU::createROFrameClustersDeviceArray(const int iteration) { if (!iteration) { GPUTimer timer("creating ROFrame clusters array"); - allocMem(reinterpret_cast(&mROFramesClustersDeviceArray), nLayers * sizeof(int*), this->hasFrameworkAllocator()); - GPUChkErrS(cudaHostRegister(mROFramesClustersDevice.data(), nLayers * sizeof(int*), cudaHostRegisterPortable)); - mPinnedROFramesClusters.set(nLayers); + allocMem(reinterpret_cast(&mROFramesClustersDeviceArray), NLayers * sizeof(int*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaHostRegister(mROFramesClustersDevice.data(), NLayers * sizeof(int*), cudaHostRegisterPortable)); + mPinnedROFramesClusters.set(NLayers); if (!this->hasFrameworkAllocator()) { - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { + for (auto iLayer{0}; iLayer < NLayers; ++iLayer) { GPUChkErrS(cudaHostRegister(this->mROFramesClusters[iLayer].data(), this->mROFramesClusters[iLayer].size() * sizeof(int), cudaHostRegisterPortable)); mPinnedROFramesClusters.set(iLayer); } @@ -206,8 +206,8 @@ void TimeFrameGPU::createROFrameClustersDeviceArray(const int iteration } } -template -void TimeFrameGPU::loadROFrameClustersDevice(const int iteration, const int layer) +template +void TimeFrameGPU::loadROFrameClustersDevice(const int iteration, const int layer) { if (!iteration) { GPUTimer timer(mGpuStreams[layer], "loading ROframe clusters", layer); @@ -218,16 +218,16 @@ void TimeFrameGPU::loadROFrameClustersDevice(const int iteration, const } } -template -void TimeFrameGPU::createTrackingFrameInfoDeviceArray(const int iteration) +template +void TimeFrameGPU::createTrackingFrameInfoDeviceArray(const int iteration) { if (!iteration) { GPUTimer timer("creating trackingframeinfo array"); - allocMem(reinterpret_cast(&mTrackingFrameInfoDeviceArray), nLayers * sizeof(TrackingFrameInfo*), this->hasFrameworkAllocator()); - GPUChkErrS(cudaHostRegister(mTrackingFrameInfoDevice.data(), nLayers * sizeof(TrackingFrameInfo*), cudaHostRegisterPortable)); - mPinnedTrackingFrameInfo.set(nLayers); + allocMem(reinterpret_cast(&mTrackingFrameInfoDeviceArray), NLayers * sizeof(TrackingFrameInfo*), this->hasFrameworkAllocator()); + GPUChkErrS(cudaHostRegister(mTrackingFrameInfoDevice.data(), NLayers * sizeof(TrackingFrameInfo*), cudaHostRegisterPortable)); + mPinnedTrackingFrameInfo.set(NLayers); if (!this->hasFrameworkAllocator()) { - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { + for (auto iLayer{0}; iLayer < NLayers; ++iLayer) { GPUChkErrS(cudaHostRegister(this->mTrackingFrameInfo[iLayer].data(), this->mTrackingFrameInfo[iLayer].size() * sizeof(TrackingFrameInfo), cudaHostRegisterPortable)); mPinnedTrackingFrameInfo.set(iLayer); } @@ -235,8 +235,8 @@ void TimeFrameGPU::createTrackingFrameInfoDeviceArray(const int iterati } } -template -void TimeFrameGPU::loadTrackingFrameInfoDevice(const int iteration, const int layer) +template +void TimeFrameGPU::loadTrackingFrameInfoDevice(const int iteration, const int layer) { if (!iteration) { GPUTimer timer(mGpuStreams[layer], "loading trackingframeinfo", layer); @@ -247,43 +247,113 @@ void TimeFrameGPU::loadTrackingFrameInfoDevice(const int iteration, con } } -template -void TimeFrameGPU::loadMultiplicityCutMask(const int iteration) +template +void TimeFrameGPU::loadROFCutMask(const int iteration) { if (!iteration || iteration == 3) { // we need to re-load the swapped mult-mask in upc iteration GPUTimer timer("loading multiplicity cut mask"); - GPULog("gpu-transfer: iteration {} loading multiplicity cut mask with {} elements, for {:.2f} MB.", iteration, this->mMultiplicityCutMask.size(), this->mMultiplicityCutMask.size() * sizeof(uint8_t) / constants::MB); - if (!iteration) { // only allocate on first call - allocMem(reinterpret_cast(&mMultMaskDevice), this->mMultiplicityCutMask.size() * sizeof(uint8_t), this->hasFrameworkAllocator()); - } - GPUChkErrS(cudaMemcpy(mMultMaskDevice, this->mMultiplicityCutMask.data(), this->mMultiplicityCutMask.size() * sizeof(uint8_t), cudaMemcpyHostToDevice)); - } -} - -template -void TimeFrameGPU::loadVertices(const int iteration) + const auto& hostTable = *(this->mROFMask); + const auto hostView = hostTable.getView(); + using TableEntry = ROFMaskTable::TableEntry; + using TableIndex = ROFMaskTable::TableIndex; + TableEntry* d_flatTable{nullptr}; + TableIndex* d_indices{nullptr}; + GPULog("gpu-transfer: iteration {} loading multiplicity cut mask with {} elements, for {:.2f} MB.", + iteration, hostTable.getFlatMaskSize(), hostTable.getFlatMaskSize() * sizeof(TableEntry) / constants::MB); + allocMem(reinterpret_cast(&d_flatTable), hostTable.getFlatMaskSize() * sizeof(TableEntry), this->hasFrameworkAllocator()); + allocMem(reinterpret_cast(&d_indices), NLayers * sizeof(uint32_t), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(d_indices, hostView.mLayerROFOffsets, NLayers * sizeof(TableIndex), cudaMemcpyHostToDevice)); + // Re-copy the flat mask on every qualifying iteration (e.g. after swapMasks() for UPC) + GPUChkErrS(cudaMemcpy(d_flatTable, hostView.mFlatMask, hostTable.getFlatMaskSize() * sizeof(TableEntry), cudaMemcpyHostToDevice)); + mDeviceROFMaskTableView = hostTable.getDeviceView(d_flatTable, d_indices); + } +} + +template +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->hasFrameworkAllocator()); - GPUChkErrS(cudaMemcpy(mROFramesPVDevice, this->mROFramesPV.data(), this->mROFramesPV.size() * sizeof(int), cudaMemcpyHostToDevice)); GPULog("gpu-transfer: loading {} seeding vertices, for {:.2f} MB.", this->mPrimaryVertices.size(), this->mPrimaryVertices.size() * sizeof(Vertex) / constants::MB); allocMem(reinterpret_cast(&mPrimaryVerticesDevice), this->mPrimaryVertices.size() * sizeof(Vertex), this->hasFrameworkAllocator()); GPUChkErrS(cudaMemcpy(mPrimaryVerticesDevice, this->mPrimaryVertices.data(), this->mPrimaryVertices.size() * sizeof(Vertex), cudaMemcpyHostToDevice)); } } -template -void TimeFrameGPU::createTrackletsLUTDeviceArray(const int iteration) +template +void TimeFrameGPU::loadROFOverlapTable(const int iteration) +{ + if (!iteration) { + GPUTimer timer("initialising device view of ROFOverlapTable"); + const auto& hostTable = this->getROFOverlapTable(); + const auto& hostView = this->getROFOverlapTableView(); + using TableEntry = ROFOverlapTable::TableEntry; + using TableIndex = ROFOverlapTable::TableIndex; + using LayerTiming = o2::its::LayerTiming; + TableEntry* d_flatTable{nullptr}; + TableIndex* d_indices{nullptr}; + LayerTiming* d_layers{nullptr}; + size_t flatTableSize = hostTable.getFlatTableSize(); + allocMem(reinterpret_cast(&d_flatTable), flatTableSize * sizeof(TableEntry), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(d_flatTable, hostView.mFlatTable, flatTableSize * sizeof(TableEntry), cudaMemcpyHostToDevice)); + allocMem(reinterpret_cast(&d_indices), hostTable.getIndicesSize() * sizeof(TableIndex), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(d_indices, hostView.mIndices, hostTable.getIndicesSize() * sizeof(TableIndex), cudaMemcpyHostToDevice)); + allocMem(reinterpret_cast(&d_layers), NLayers * sizeof(LayerTiming), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(d_layers, hostView.mLayers, NLayers * sizeof(LayerTiming), cudaMemcpyHostToDevice)); + mDeviceROFOverlapTableView = hostTable.getDeviceView(d_flatTable, d_indices, d_layers); + } +} + +template +void TimeFrameGPU::loadROFVertexLookupTable(const int iteration) +{ + if (!iteration) { + GPUTimer timer("initialising device view of ROFVertexLookupTable"); + const auto& hostTable = this->getROFVertexLookupTable(); + const auto& hostView = this->getROFVertexLookupTableView(); + using TableEntry = ROFVertexLookupTable::TableEntry; + using TableIndex = ROFVertexLookupTable::TableIndex; + using LayerTiming = o2::its::LayerTiming; + TableEntry* d_flatTable{nullptr}; + TableIndex* d_indices{nullptr}; + LayerTiming* d_layers{nullptr}; + size_t flatTableSize = hostTable.getFlatTableSize(); + allocMem(reinterpret_cast(&d_flatTable), flatTableSize * sizeof(TableEntry), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(d_flatTable, hostView.mFlatTable, flatTableSize * sizeof(TableEntry), cudaMemcpyHostToDevice)); + allocMem(reinterpret_cast(&d_indices), hostTable.getIndicesSize() * sizeof(TableIndex), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(d_indices, hostView.mIndices, hostTable.getIndicesSize() * sizeof(TableIndex), cudaMemcpyHostToDevice)); + allocMem(reinterpret_cast(&d_layers), NLayers * sizeof(LayerTiming), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(d_layers, hostView.mLayers, NLayers * sizeof(LayerTiming), cudaMemcpyHostToDevice)); + mDeviceROFVertexLookupTableView = hostTable.getDeviceView(d_flatTable, d_indices, d_layers); + } +} + +template +void TimeFrameGPU::updateROFVertexLookupTable(const int iteration) +{ + const auto& hostTable = this->getROFVertexLookupTable(); + if (!iteration) { + GPUTimer timer("updating device view of ROFVertexLookupTable"); + const auto& hostView = this->getROFVertexLookupTableView(); + using TableEntry = ROFVertexLookupTable::TableEntry; + TableEntry* d_flatTable{nullptr}; + size_t flatTableSize = hostTable.getFlatTableSize(); + allocMem(reinterpret_cast(&d_flatTable), flatTableSize * sizeof(TableEntry), this->hasFrameworkAllocator()); + GPUChkErrS(cudaMemcpy(d_flatTable, hostView.mFlatTable, flatTableSize * sizeof(TableEntry), cudaMemcpyHostToDevice)); + mDeviceROFVertexLookupTableView = hostTable.getDeviceView(d_flatTable, hostView.mIndices, hostView.mLayers); + } +} + +template +void TimeFrameGPU::createTrackletsLUTDeviceArray(const int iteration) { if (!iteration) { - allocMem(reinterpret_cast(&mTrackletsLUTDeviceArray), (nLayers - 1) * sizeof(int*), this->hasFrameworkAllocator()); + allocMem(reinterpret_cast(&mTrackletsLUTDeviceArray), (NLayers - 1) * sizeof(int*), this->hasFrameworkAllocator()); } } -template -void TimeFrameGPU::createTrackletsLUTDevice(const int iteration, const int layer) +template +void TimeFrameGPU::createTrackletsLUTDevice(const int iteration, const int layer) { GPUTimer timer(mGpuStreams[layer], "creating tracklets LUTs", layer); const int ncls = this->mClusters[layer].size() + 1; @@ -295,17 +365,17 @@ void TimeFrameGPU::createTrackletsLUTDevice(const int iteration, const GPUChkErrS(cudaMemsetAsync(mTrackletsLUTDevice[layer], 0, ncls * sizeof(int), mGpuStreams[layer].get())); } -template -void TimeFrameGPU::createTrackletsBuffersArray(const int iteration) +template +void TimeFrameGPU::createTrackletsBuffersArray(const int iteration) { if (!iteration) { GPUTimer timer("creating tracklet buffers array"); - allocMem(reinterpret_cast(&mTrackletsDeviceArray), (nLayers - 1) * sizeof(Tracklet*), this->hasFrameworkAllocator()); + allocMem(reinterpret_cast(&mTrackletsDeviceArray), (NLayers - 1) * sizeof(Tracklet*), this->hasFrameworkAllocator()); } } -template -void TimeFrameGPU::createTrackletsBuffers(const int layer) +template +void TimeFrameGPU::createTrackletsBuffers(const int layer) { GPUTimer timer(mGpuStreams[layer], "creating tracklet buffers", layer); mNTracklets[layer] = 0; @@ -313,34 +383,35 @@ void TimeFrameGPU::createTrackletsBuffers(const int layer) mGpuStreams[layer].sync(); // ensure number of tracklets is correct GPULog("gpu-transfer: creating tracklets buffer for {} elements on layer {}, for {:.2f} MB.", mNTracklets[layer], layer, mNTracklets[layer] * sizeof(Tracklet) / constants::MB); allocMemAsync(reinterpret_cast(&mTrackletsDevice[layer]), mNTracklets[layer] * sizeof(Tracklet), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + GPUChkErrS(cudaMemsetAsync(mTrackletsDevice[layer], 0, mNTracklets[layer] * sizeof(Tracklet), mGpuStreams[layer].get())); GPUChkErrS(cudaMemcpyAsync(&mTrackletsDeviceArray[layer], &mTrackletsDevice[layer], sizeof(Tracklet*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); } -template -void TimeFrameGPU::loadTrackletsDevice() +template +void TimeFrameGPU::loadTrackletsDevice() { - GPUTimer timer(mGpuStreams, "loading tracklets", nLayers - 1); - for (auto iLayer{0}; iLayer < nLayers - 1; ++iLayer) { + GPUTimer timer(mGpuStreams, "loading tracklets", NLayers - 1); + for (auto iLayer{0}; iLayer < NLayers - 1; ++iLayer) { GPULog("gpu-transfer: loading {} tracklets on layer {}, for {:.2f} MB.", this->mTracklets[iLayer].size(), iLayer, this->mTracklets[iLayer].size() * sizeof(Tracklet) / constants::MB); GPUChkErrS(cudaHostRegister(this->mTracklets[iLayer].data(), this->mTracklets[iLayer].size() * sizeof(Tracklet), cudaHostRegisterPortable)); GPUChkErrS(cudaMemcpyAsync(mTrackletsDevice[iLayer], this->mTracklets[iLayer].data(), this->mTracklets[iLayer].size() * sizeof(Tracklet), cudaMemcpyHostToDevice, mGpuStreams[iLayer].get())); } } -template -void TimeFrameGPU::loadTrackletsLUTDevice() +template +void TimeFrameGPU::loadTrackletsLUTDevice() { GPUTimer timer("loading tracklets"); - for (auto iLayer{0}; iLayer < nLayers - 2; ++iLayer) { + for (auto iLayer{0}; iLayer < NLayers - 2; ++iLayer) { GPULog("gpu-transfer: loading tracklets LUT for {} elements on layer {}, for {:.2f} MB", this->mTrackletsLookupTable[iLayer].size(), iLayer + 1, this->mTrackletsLookupTable[iLayer].size() * sizeof(int) / constants::MB); GPUChkErrS(cudaMemcpyAsync(mTrackletsLUTDevice[iLayer + 1], this->mTrackletsLookupTable[iLayer].data(), this->mTrackletsLookupTable[iLayer].size() * sizeof(int), cudaMemcpyHostToDevice, mGpuStreams[iLayer].get())); } mGpuStreams.sync(); - GPUChkErrS(cudaMemcpy(mTrackletsLUTDeviceArray, mTrackletsLUTDevice.data(), (nLayers - 1) * sizeof(int*), cudaMemcpyHostToDevice)); + GPUChkErrS(cudaMemcpy(mTrackletsLUTDeviceArray, mTrackletsLUTDevice.data(), (NLayers - 1) * sizeof(int*), cudaMemcpyHostToDevice)); } -template -void TimeFrameGPU::createNeighboursIndexTablesDevice(const int layer) +template +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); @@ -348,8 +419,8 @@ void TimeFrameGPU::createNeighboursIndexTablesDevice(const int layer) GPUChkErrS(cudaMemsetAsync(mNeighboursIndexTablesDevice[layer], 0, (mNCells[layer] + 1) * sizeof(int), mGpuStreams[layer].get())); } -template -void TimeFrameGPU::createNeighboursLUTDevice(const int layer, const unsigned int nCells) +template +void TimeFrameGPU::createNeighboursLUTDevice(const int layer, const unsigned int nCells) { GPUTimer timer(mGpuStreams[layer], "reserving neighboursLUT"); GPULog("gpu-allocation: reserving neighbours LUT for {} elements on layer {} , for {:.2f} MB.", nCells + 1, layer, (nCells + 1) * sizeof(int) / constants::MB); @@ -357,11 +428,11 @@ void TimeFrameGPU::createNeighboursLUTDevice(const int layer, const uns GPUChkErrS(cudaMemsetAsync(mNeighboursLUTDevice[layer], 0, (nCells + 1) * sizeof(int), mGpuStreams[layer].get())); } -template -void TimeFrameGPU::loadCellsDevice() +template +void TimeFrameGPU::loadCellsDevice() { - GPUTimer timer(mGpuStreams, "loading cell seeds", nLayers - 2); - for (auto iLayer{0}; iLayer < nLayers - 2; ++iLayer) { + GPUTimer timer(mGpuStreams, "loading cell seeds", NLayers - 2); + for (auto iLayer{0}; iLayer < NLayers - 2; ++iLayer) { GPULog("gpu-transfer: loading {} cell seeds on layer {}, for {:.2f} MB.", this->mCells[iLayer].size(), iLayer, this->mCells[iLayer].size() * sizeof(CellSeedN) / constants::MB); allocMemAsync(reinterpret_cast(&mCellsDevice[iLayer]), this->mCells[iLayer].size() * sizeof(CellSeedN), mGpuStreams[iLayer], this->hasFrameworkAllocator()); allocMemAsync(reinterpret_cast(&mNeighboursIndexTablesDevice[iLayer]), (this->mCells[iLayer].size() + 1) * sizeof(int), mGpuStreams[iLayer], this->hasFrameworkAllocator()); // accessory for the neigh. finding. @@ -370,17 +441,17 @@ void TimeFrameGPU::loadCellsDevice() } } -template -void TimeFrameGPU::createCellsLUTDeviceArray(const int iteration) +template +void TimeFrameGPU::createCellsLUTDeviceArray(const int iteration) { if (!iteration) { GPUTimer timer("creating cells LUTs array"); - allocMem(reinterpret_cast(&mCellsLUTDeviceArray), (nLayers - 2) * sizeof(int*), this->hasFrameworkAllocator()); + allocMem(reinterpret_cast(&mCellsLUTDeviceArray), (NLayers - 2) * sizeof(int*), this->hasFrameworkAllocator()); } } -template -void TimeFrameGPU::createCellsLUTDevice(const int layer) +template +void TimeFrameGPU::createCellsLUTDevice(const int layer) { GPUTimer timer(mGpuStreams[layer], "creating cells LUTs", layer); GPULog("gpu-transfer: creating cell LUT for {} elements on layer {}, for {:.2f} MB.", mNTracklets[layer] + 1, layer, (mNTracklets[layer] + 1) * sizeof(int) / constants::MB); @@ -389,18 +460,18 @@ void TimeFrameGPU::createCellsLUTDevice(const int layer) GPUChkErrS(cudaMemcpyAsync(&mCellsLUTDeviceArray[layer], &mCellsLUTDevice[layer], sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); } -template -void TimeFrameGPU::createCellsBuffersArray(const int iteration) +template +void TimeFrameGPU::createCellsBuffersArray(const int iteration) { if (!iteration) { GPUTimer timer("creating cells buffers array"); - allocMem(reinterpret_cast(&mCellsDeviceArray), (nLayers - 2) * sizeof(CellSeedN*), this->hasFrameworkAllocator()); + allocMem(reinterpret_cast(&mCellsDeviceArray), (NLayers - 2) * sizeof(CellSeedN*), this->hasFrameworkAllocator()); GPUChkErrS(cudaMemcpy(mCellsDeviceArray, mCellsDevice.data(), mCellsDevice.size() * sizeof(CellSeedN*), cudaMemcpyHostToDevice)); } } -template -void TimeFrameGPU::createCellsBuffers(const int layer) +template +void TimeFrameGPU::createCellsBuffers(const int layer) { GPUTimer timer(mGpuStreams[layer], "creating cells buffers"); mNCells[layer] = 0; @@ -408,32 +479,23 @@ void TimeFrameGPU::createCellsBuffers(const int layer) mGpuStreams[layer].sync(); // ensure number of cells is correct GPULog("gpu-transfer: creating cell buffer for {} elements on layer {}, for {:.2f} MB.", mNCells[layer], layer, mNCells[layer] * sizeof(CellSeedN) / constants::MB); allocMemAsync(reinterpret_cast(&mCellsDevice[layer]), mNCells[layer] * sizeof(CellSeedN), mGpuStreams[layer], this->hasFrameworkAllocator(), (o2::gpu::GPUMemoryResource::MEMORY_GPU | o2::gpu::GPUMemoryResource::MEMORY_STACK)); + GPUChkErrS(cudaMemsetAsync(mCellsDevice[layer], 0, mNCells[layer] * sizeof(CellSeedN), mGpuStreams[layer].get())); GPUChkErrS(cudaMemcpyAsync(&mCellsDeviceArray[layer], &mCellsDevice[layer], sizeof(CellSeedN*), cudaMemcpyHostToDevice, mGpuStreams[layer].get())); } -template -void TimeFrameGPU::loadCellsLUTDevice() +template +void TimeFrameGPU::loadCellsLUTDevice() { - GPUTimer timer(mGpuStreams, "loading cells LUTs", nLayers - 3); - for (auto iLayer{0}; iLayer < nLayers - 3; ++iLayer) { + GPUTimer timer(mGpuStreams, "loading cells LUTs", NLayers - 3); + for (auto iLayer{0}; iLayer < NLayers - 3; ++iLayer) { GPULog("gpu-transfer: loading cell LUT for {} elements on layer {}, for {:.2f} MB.", this->mCellsLookupTable[iLayer].size(), iLayer, this->mCellsLookupTable[iLayer].size() * sizeof(int) / constants::MB); GPUChkErrS(cudaHostRegister(this->mCellsLookupTable[iLayer].data(), this->mCellsLookupTable[iLayer].size() * sizeof(int), cudaHostRegisterPortable)); GPUChkErrS(cudaMemcpyAsync(mCellsLUTDevice[iLayer + 1], this->mCellsLookupTable[iLayer].data(), this->mCellsLookupTable[iLayer].size() * sizeof(int), cudaMemcpyHostToDevice, mGpuStreams[iLayer].get())); } } -template -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->hasFrameworkAllocator()); - GPUChkErrS(cudaHostRegister(this->mRoads.data(), this->mRoads.size() * sizeof(Road), cudaHostRegisterPortable)); - GPUChkErrS(cudaMemcpy(mRoadsDevice, this->mRoads.data(), this->mRoads.size() * sizeof(Road), cudaMemcpyHostToDevice)); -} - -template -void TimeFrameGPU::loadTrackSeedsDevice(bounded_vector& seeds) +template +void TimeFrameGPU::loadTrackSeedsDevice(bounded_vector& seeds) { GPUTimer timer("loading track seeds"); GPULog("gpu-transfer: loading {} track seeds, for {:.2f} MB.", seeds.size(), seeds.size() * sizeof(CellSeedN) / constants::MB); @@ -444,8 +506,8 @@ void TimeFrameGPU::loadTrackSeedsDevice(bounded_vector& seed GPUChkErrS(cudaMemset(mTrackSeedsLUTDevice, 0, (seeds.size() + 1) * sizeof(int))); } -template -void TimeFrameGPU::createNeighboursDevice(const unsigned int layer) +template +void TimeFrameGPU::createNeighboursDevice(const unsigned int layer) { GPUTimer timer(mGpuStreams[layer], "reserving neighbours", layer); this->mNNeighbours[layer] = 0; @@ -458,8 +520,8 @@ void TimeFrameGPU::createNeighboursDevice(const unsigned int layer) 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(const size_t nSeeds) +template +void TimeFrameGPU::createTrackITSExtDevice(const size_t nSeeds) { GPUTimer timer("reserving tracks"); mNTracks = 0; @@ -470,135 +532,54 @@ void TimeFrameGPU::createTrackITSExtDevice(const size_t nSeeds) GPUChkErrS(cudaMemset(mTrackITSExtDevice, 0, mNTracks * sizeof(o2::its::TrackITSExt))); } -template -void TimeFrameGPU::createVtxTrackletsLUTDevice(const int32_t iteration) -{ - GPUTimer timer("creating vertexer tracklet LUTs"); - const int32_t ncls = this->mClusters[1].size(); - for (int32_t iMode{0}; iMode < 2; ++iMode) { - if (!iteration) { - GPULog("gpu-transfer: creating vertexer tracklets per cluster for {} elements for mode {}, for {:.2f} MB.", ncls, iMode, ncls * sizeof(int32_t) / constants::MB); - allocMemAsync(reinterpret_cast(&mNTrackletsPerClusterDevice[iMode]), ncls * sizeof(int32_t), mGpuStreams[iMode], this->hasFrameworkAllocator()); - - GPULog("gpu-transfer: creating vertexer tracklets per cluster sum for {} elements for mode {}, for {:.2f} MB.", ncls + 1, iMode, (ncls + 1) * sizeof(int32_t) / constants::MB); - allocMemAsync(reinterpret_cast(&mNTrackletsPerClusterSumDevice[iMode]), (ncls + 1) * sizeof(int32_t), mGpuStreams[iMode], this->hasFrameworkAllocator()); - - GPULog("gpu-transfer: creating vertexer tracklets per ROF for {} elements for mode {}, for {:.2f} MB.", this->mNrof + 1, iMode, (this->mNrof + 1) * sizeof(int32_t) / constants::MB); - allocMemAsync(reinterpret_cast(&mNTrackletsPerROFDevice[iMode]), (this->mNrof + 1) * sizeof(int32_t), mGpuStreams[iMode], this->hasFrameworkAllocator()); - } - GPUChkErrS(cudaMemsetAsync(mNTrackletsPerClusterDevice[iMode], 0, ncls * sizeof(int32_t), mGpuStreams[iMode].get())); - GPUChkErrS(cudaMemsetAsync(mNTrackletsPerClusterSumDevice[iMode], 0, (ncls + 1) * sizeof(int32_t), mGpuStreams[iMode].get())); - GPUChkErrS(cudaMemsetAsync(mNTrackletsPerROFDevice[iMode], 0, (this->mNrof + 1) * sizeof(int32_t), mGpuStreams[iMode].get())); - } - mGpuStreams[0].sync(); - mGpuStreams[1].sync(); - if (!iteration) { - allocMem(reinterpret_cast(&mNTrackletsPerClusterDeviceArray), mNTrackletsPerClusterDevice.size() * sizeof(int32_t*), this->hasFrameworkAllocator()); - GPUChkErrS(cudaMemcpy(mNTrackletsPerClusterDeviceArray, mNTrackletsPerClusterDevice.data(), mNTrackletsPerClusterDevice.size() * sizeof(int32_t*), cudaMemcpyHostToDevice)); - - allocMem(reinterpret_cast(&mNTrackletsPerClusterSumDeviceArray), mNTrackletsPerClusterSumDevice.size() * sizeof(int32_t*), this->hasFrameworkAllocator()); - GPUChkErrS(cudaMemcpy(mNTrackletsPerClusterSumDeviceArray, mNTrackletsPerClusterSumDevice.data(), mNTrackletsPerClusterSumDevice.size() * sizeof(int32_t*), cudaMemcpyHostToDevice)); - - allocMem(reinterpret_cast(&mNTrackletsPerROFDeviceArray), mNTrackletsPerROFDevice.size() * sizeof(int32_t*), this->hasFrameworkAllocator()); - GPUChkErrS(cudaMemcpy(mNTrackletsPerROFDeviceArray, mNTrackletsPerROFDevice.data(), mNTrackletsPerROFDevice.size() * sizeof(int32_t*), cudaMemcpyHostToDevice)); - } -} - -template -void TimeFrameGPU::createVtxTrackletsBuffers(const int32_t iteration) -{ - GPUTimer timer("creating vertexer tracklet buffers"); - for (int32_t iMode{0}; iMode < 2; ++iMode) { - this->mTotalTracklets[iMode] = 0; - GPUChkErrS(cudaMemcpyAsync(&(this->mTotalTracklets[iMode]), mNTrackletsPerClusterSumDevice[iMode] + this->mClusters[1].size(), sizeof(int32_t), cudaMemcpyDeviceToHost, mGpuStreams[iMode].get())); - GPULog("gpu-transfer: creating vertexer tracklets buffer for {} elements on layer {}, for {:.2f} MB.", this->mTotalTracklets[iMode], iMode, this->mTotalTracklets[iMode] * sizeof(Tracklet) / constants::MB); - allocMemAsync(reinterpret_cast(&mTrackletsDevice[iMode]), this->mTotalTracklets[iMode] * sizeof(Tracklet), mGpuStreams[iMode], this->hasFrameworkAllocator()); - } - mGpuStreams[0].sync(); - mGpuStreams[1].sync(); - allocMem(reinterpret_cast(&mTrackletsDeviceArray), 2 * sizeof(Tracklet*), this->hasFrameworkAllocator()); - GPUChkErrS(cudaHostRegister(mTrackletsDevice.data(), 2 * sizeof(Tracklet*), cudaHostRegisterPortable)); - GPUChkErrS(cudaMemcpy(mTrackletsDeviceArray, mTrackletsDevice.data(), 2 * sizeof(Tracklet*), cudaMemcpyHostToDevice)); -} - -template -void TimeFrameGPU::createVtxLinesLUTDevice(const int32_t iteration) -{ - GPUTimer timer("creating vertexer lines LUT and used tracklets buffer"); - const int32_t ncls = this->mClusters[1].size(); - - GPULog("gpu-transfer: creating vertexer lines per cluster for {} elements , for {:.2f} MB.", ncls, ncls * sizeof(int32_t) / constants::MB); - allocMem(reinterpret_cast(&mNLinesPerClusterDevice), ncls * sizeof(int32_t), this->hasFrameworkAllocator()); - - GPULog("gpu-transfer: creating vertexer lines per cluster sum for {} elements , for {:.2f} MB.", ncls + 1, (ncls + 1) * sizeof(int32_t) / constants::MB); - allocMem(reinterpret_cast(&mNLinesPerClusterSumDevice), (ncls + 1) * sizeof(int32_t), this->hasFrameworkAllocator()); - - const int32_t ntrkls = this->mTotalTracklets[0]; - GPULog("gpu-transfer: creating vertexer used tracklets for {} elements , for {:.2f} MB.", ntrkls, ntrkls * sizeof(uint8_t) / constants::MB); - allocMem(reinterpret_cast(&mUsedTrackletsDevice), ntrkls * sizeof(uint8_t), this->hasFrameworkAllocator()); -} - -template -void TimeFrameGPU::createVtxLinesBuffer(const int32_t iteration) -{ - GPUTimer timer("creating vertexer lines buffer and resetting used tracklets"); - int32_t nlines = 0; - GPUChkErrS(cudaMemcpy(&nlines, mNLinesPerClusterDevice + this->mClusters[1].size(), sizeof(int32_t), cudaMemcpyDeviceToHost)); - this->mTotalLines = nlines; - GPULog("gpu-transfer: creating vertexer lines for {} elements , for {:.2f} MB.", nlines, nlines * sizeof(Line) / constants::MB); - allocMem(reinterpret_cast(&mLinesDevice), nlines * sizeof(Line), this->hasFrameworkAllocator()); - // reset used tracklets - GPUChkErrS(cudaMemset(mUsedTrackletsDevice, 0, this->mTotalTracklets[0] * sizeof(uint8_t))); -} - -template -void TimeFrameGPU::downloadCellsDevice() +template +void TimeFrameGPU::downloadCellsDevice() { - GPUTimer timer(mGpuStreams, "downloading cells", nLayers - 2); - for (int iLayer{0}; iLayer < nLayers - 2; ++iLayer) { + GPUTimer timer(mGpuStreams, "downloading cells", NLayers - 2); + for (int iLayer{0}; iLayer < NLayers - 2; ++iLayer) { GPULog("gpu-transfer: downloading {} cells on layer: {}, for {:.2f} MB.", mNCells[iLayer], iLayer, mNCells[iLayer] * sizeof(CellSeedN) / constants::MB); this->mCells[iLayer].resize(mNCells[iLayer]); GPUChkErrS(cudaMemcpyAsync(this->mCells[iLayer].data(), this->mCellsDevice[iLayer], mNCells[iLayer] * sizeof(CellSeedN), cudaMemcpyDeviceToHost, mGpuStreams[iLayer].get())); } } -template -void TimeFrameGPU::downloadCellsLUTDevice() +template +void TimeFrameGPU::downloadCellsLUTDevice() { - GPUTimer timer(mGpuStreams, "downloading cell luts", nLayers - 3); - for (auto iLayer{0}; iLayer < nLayers - 3; ++iLayer) { + GPUTimer timer(mGpuStreams, "downloading cell luts", NLayers - 3); + for (auto iLayer{0}; iLayer < NLayers - 3; ++iLayer) { GPULog("gpu-transfer: downloading cells lut on layer {} for {} elements", iLayer, (mNTracklets[iLayer + 1] + 1)); this->mCellsLookupTable[iLayer].resize(mNTracklets[iLayer + 1] + 1); GPUChkErrS(cudaMemcpyAsync(this->mCellsLookupTable[iLayer].data(), mCellsLUTDevice[iLayer + 1], (mNTracklets[iLayer + 1] + 1) * sizeof(int), cudaMemcpyDeviceToHost, mGpuStreams[iLayer].get())); } } -template -void TimeFrameGPU::downloadCellsNeighboursDevice(std::vector>>& neighbours, const int layer) +template +void TimeFrameGPU::downloadCellsNeighboursDevice(std::vector>>& neighbours, const int layer) { GPUTimer timer(mGpuStreams[layer], "downloading neighbours from layer", layer); GPULog("gpu-transfer: downloading {} neighbours, for {:.2f} MB.", neighbours[layer].size(), neighbours[layer].size() * sizeof(std::pair) / constants::MB); GPUChkErrS(cudaMemcpyAsync(neighbours[layer].data(), mNeighbourPairsDevice[layer], neighbours[layer].size() * sizeof(gpuPair), cudaMemcpyDeviceToHost, mGpuStreams[layer].get())); } -template -void TimeFrameGPU::downloadNeighboursLUTDevice(bounded_vector& lut, const int layer) +template +void TimeFrameGPU::downloadNeighboursLUTDevice(bounded_vector& lut, const int layer) { GPUTimer timer(mGpuStreams[layer], "downloading neighbours LUT from layer", layer); GPULog("gpu-transfer: downloading neighbours LUT for {} elements on layer {}, for {:.2f} MB.", lut.size(), layer, lut.size() * sizeof(int) / constants::MB); GPUChkErrS(cudaMemcpyAsync(lut.data(), mNeighboursLUTDevice[layer], lut.size() * sizeof(int), cudaMemcpyDeviceToHost, mGpuStreams[layer].get())); } -template -void TimeFrameGPU::downloadTrackITSExtDevice() +template +void TimeFrameGPU::downloadTrackITSExtDevice() { GPUTimer timer("downloading tracks"); GPULog("gpu-transfer: downloading {} tracks, for {:.2f} MB.", mTrackITSExt.size(), mTrackITSExt.size() * sizeof(o2::its::TrackITSExt) / constants::MB); GPUChkErrS(cudaMemcpy(mTrackITSExt.data(), mTrackITSExtDevice, mTrackITSExt.size() * sizeof(o2::its::TrackITSExt), cudaMemcpyDeviceToHost)); } -template -void TimeFrameGPU::unregisterHostMemory(const int maxLayers) +template +void TimeFrameGPU::unregisterHostMemory(const int maxLayers) { GPUTimer timer("unregistering host memory"); GPULog("unregistering host memory"); @@ -610,13 +591,13 @@ void TimeFrameGPU::unregisterHostMemory(const int maxLayers) } }; auto checkedUnregisterArray = [](auto& bits, auto& vec) { - if (bits.test(nLayers)) { + if (bits.test(NLayers)) { GPUChkErrS(cudaHostUnregister(vec.data())); - bits.reset(nLayers); + bits.reset(NLayers); } }; - for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { + for (auto iLayer{0}; iLayer < NLayers; ++iLayer) { checkedUnregisterEntry(mPinnedUsedClusters, this->mUsedClusters, iLayer); checkedUnregisterEntry(mPinnedUnsortedClusters, this->mUnsortedClusters, iLayer); checkedUnregisterEntry(mPinnedClusters, this->mClusters, iLayer); @@ -650,69 +631,67 @@ constexpr auto makeIterTags(std::index_sequence) constexpr auto kIterTags = makeIterTags(std::make_index_sequence<4>{}); } // namespace detail -template -void TimeFrameGPU::pushMemoryStack(const int iteration) +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) +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, +template +void TimeFrameGPU::initialise(const int iteration, const TrackingParameters& trkParam, - const int maxLayers, - IndexTableUtilsN* utils, - const TimeFrameGPUParameters* gpuParam) + const int maxLayers) { - mGpuStreams.resize(nLayers); - o2::its::TimeFrame::initialise(iteration, trkParam, maxLayers); + mGpuStreams.resize(NLayers); + o2::its::TimeFrame::initialise(iteration, trkParam, maxLayers, false); } -template -void TimeFrameGPU::syncStream(const size_t stream) +template +void TimeFrameGPU::syncStream(const size_t stream) { mGpuStreams[stream].sync(); } -template -void TimeFrameGPU::syncStreams(const bool device) +template +void TimeFrameGPU::syncStreams(const bool device) { mGpuStreams.sync(device); } -template -void TimeFrameGPU::waitEvent(const int stream, const int event) +template +void TimeFrameGPU::waitEvent(const int stream, const int event) { mGpuStreams.waitEvent(stream, event); } -template -void TimeFrameGPU::recordEvent(const int event) +template +void TimeFrameGPU::recordEvent(const int event) { mGpuStreams[event].record(); } -template -void TimeFrameGPU::recordEvents(const int start, const int end) +template +void TimeFrameGPU::recordEvents(const int start, const int end) { for (int i{start}; i < end; ++i) { recordEvent(i); } } -template -void TimeFrameGPU::wipe() +template +void TimeFrameGPU::wipe() { unregisterHostMemory(0); - o2::its::TimeFrame::wipe(); + o2::its::TimeFrame::wipe(); } template class TimeFrameGPU<7>; diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TracerGPU.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TracerGPU.cu deleted file mode 100644 index 7c42658242231..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TracerGPU.cu +++ /dev/null @@ -1,48 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -#include -#include "ITStrackingGPU/TracerGPU.h" - -#if !defined(__HIPCC__) && defined(__USE_GPU_TRACER__) -#include - -constexpr uint32_t colors[] = {0xff00ff00, 0xff0000ff, 0xffffff00, 0xffff00ff, 0xff00ffff, 0xffff0000, 0xffffffff}; -constexpr int num_colors = sizeof(colors) / sizeof(uint32_t); - -namespace o2 -{ -namespace its -{ -namespace gpu -{ -Tracer::Tracer(const char* name, int color_id) -{ - color_id = color_id % num_colors; - nvtxEventAttributes_t eventAttrib = {0}; - eventAttrib.version = NVTX_VERSION; - eventAttrib.size = NVTX_EVENT_ATTRIB_STRUCT_SIZE; - eventAttrib.colorType = NVTX_COLOR_ARGB; - eventAttrib.color = colors[color_id]; - eventAttrib.messageType = NVTX_MESSAGE_TYPE_ASCII; - eventAttrib.message.ascii = name; - nvtxRangePushEx(&eventAttrib); -} - -Tracer::~Tracer() -{ - nvtxRangePop(); -} - -} // namespace gpu -} // namespace its -} // namespace o2 -#endif diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx index 42d2227de60f8..3de2871dd458e 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx @@ -24,14 +24,19 @@ namespace o2::its { -template -void TrackerTraitsGPU::initialiseTimeFrame(const int iteration) +template +void TrackerTraitsGPU::initialiseTimeFrame(const int iteration) { - mTimeFrameGPU->initialise(iteration, this->mTrkParams[iteration], nLayers); + mTimeFrameGPU->initialise(iteration, this->mTrkParams[iteration], NLayers); // on default stream mTimeFrameGPU->loadVertices(iteration); + // TODO these tables can be put in persistent memory + mTimeFrameGPU->loadROFOverlapTable(iteration); // this can be put in constant memory actually + mTimeFrameGPU->loadROFVertexLookupTable(iteration); + // once the tables are in persistent memory just update the vertex one + // mTimeFrameGPU->updateROFVertexLookupTable(iteration); mTimeFrameGPU->loadIndexTableUtils(iteration); - mTimeFrameGPU->loadMultiplicityCutMask(iteration); + mTimeFrameGPU->loadROFCutMask(iteration); // pinned on host mTimeFrameGPU->createUsedClustersDeviceArray(iteration); mTimeFrameGPU->createClustersDeviceArray(iteration); @@ -48,23 +53,20 @@ void TrackerTraitsGPU::initialiseTimeFrame(const int iteration) mTimeFrameGPU->pushMemoryStack(iteration); } -template -void TrackerTraitsGPU::adoptTimeFrame(TimeFrame* tf) +template +void TrackerTraitsGPU::adoptTimeFrame(TimeFrame* tf) { - mTimeFrameGPU = static_cast*>(tf); - this->mTimeFrame = static_cast*>(tf); + mTimeFrameGPU = static_cast*>(tf); + this->mTimeFrame = static_cast*>(tf); } -template -void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int iROFslice, int iVertex) +template +void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int iVertex) { const auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); - int startROF{0}; - int endROF{mTimeFrameGPU->getNrof()}; - // start by queuing loading needed of two last layers - for (int iLayer{nLayers}; iLayer-- > nLayers - 2;) { + for (int iLayer{NLayers}; iLayer-- > NLayers - 2;) { mTimeFrameGPU->createUsedClustersDevice(iteration, iLayer); mTimeFrameGPU->loadClustersDevice(iteration, iLayer); mTimeFrameGPU->loadClustersIndexTables(iteration, iLayer); @@ -82,17 +84,14 @@ void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int i } mTimeFrameGPU->createTrackletsLUTDevice(iteration, iLayer); mTimeFrameGPU->waitEvent(iLayer, iLayer + 1); // wait stream until all data is available - countTrackletsInROFsHandler(mTimeFrameGPU->getDeviceIndexTableUtils(), - mTimeFrameGPU->getDeviceMultCutMask(), + countTrackletsInROFsHandler(mTimeFrameGPU->getDeviceIndexTableUtils(), + mTimeFrameGPU->getDeviceROFMaskTableView(), iLayer, - startROF, - endROF, - mTimeFrameGPU->getNrof(), - this->mTrkParams[iteration].DeltaROF, + mTimeFrameGPU->getDeviceROFOverlapTableView(), + mTimeFrameGPU->getDeviceROFVertexLookupTableView(), iVertex, mTimeFrameGPU->getDeviceVertices(), mTimeFrameGPU->getDeviceROFramesPV(), - mTimeFrameGPU->getPrimaryVerticesNum(), mTimeFrameGPU->getDeviceArrayClusters(), mTimeFrameGPU->getClusterSizes(), mTimeFrameGPU->getDeviceROFrameClusters(), @@ -117,17 +116,14 @@ void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int i if (mTimeFrameGPU->getNTracklets()[iLayer] == 0) { continue; } - computeTrackletsInROFsHandler(mTimeFrameGPU->getDeviceIndexTableUtils(), - mTimeFrameGPU->getDeviceMultCutMask(), + computeTrackletsInROFsHandler(mTimeFrameGPU->getDeviceIndexTableUtils(), + mTimeFrameGPU->getDeviceROFMaskTableView(), iLayer, - startROF, - endROF, - mTimeFrameGPU->getNrof(), - this->mTrkParams[iteration].DeltaROF, + mTimeFrameGPU->getDeviceROFOverlapTableView(), + mTimeFrameGPU->getDeviceROFVertexLookupTableView(), iVertex, mTimeFrameGPU->getDeviceVertices(), mTimeFrameGPU->getDeviceROFramesPV(), - mTimeFrameGPU->getPrimaryVerticesNum(), mTimeFrameGPU->getDeviceArrayClusters(), mTimeFrameGPU->getClusterSizes(), mTimeFrameGPU->getDeviceROFrameClusters(), @@ -154,13 +150,13 @@ void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int i } } -template -void TrackerTraitsGPU::computeLayerCells(const int iteration) +template +void TrackerTraitsGPU::computeLayerCells(const int iteration) { auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); // start by queuing loading needed of three last layers - for (int iLayer{nLayers}; iLayer-- > nLayers - 3;) { + for (int iLayer{NLayers}; iLayer-- > NLayers - 3;) { mTimeFrameGPU->loadUnsortedClustersDevice(iteration, iLayer); mTimeFrameGPU->loadTrackingFrameInfoDevice(iteration, iLayer); mTimeFrameGPU->recordEvent(iLayer); @@ -183,7 +179,7 @@ void TrackerTraitsGPU::computeLayerCells(const int iteration) mTimeFrameGPU->createCellsLUTDevice(iLayer); mTimeFrameGPU->waitEvent(iLayer, iLayer + 1); // wait stream until all data is available mTimeFrameGPU->waitEvent(iLayer, iLayer + 2); // wait stream until all data is available - countCellsHandler(mTimeFrameGPU->getDeviceArrayClusters(), + countCellsHandler(mTimeFrameGPU->getDeviceArrayClusters(), mTimeFrameGPU->getDeviceArrayUnsortedClusters(), mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), mTimeFrameGPU->getDeviceArrayTracklets(), @@ -193,7 +189,6 @@ void TrackerTraitsGPU::computeLayerCells(const int iteration) nullptr, mTimeFrameGPU->getDeviceArrayCellsLUT(), mTimeFrameGPU->getDeviceCellLUTs()[iLayer], - this->mTrkParams[iteration].DeltaROF, this->mBz, this->mTrkParams[iteration].MaxChi2ClusterAttachment, this->mTrkParams[iteration].CellDeltaTanLambdaSigma, @@ -206,7 +201,7 @@ void TrackerTraitsGPU::computeLayerCells(const int iteration) if (mTimeFrameGPU->getNCells()[iLayer] == 0) { continue; } - computeCellsHandler(mTimeFrameGPU->getDeviceArrayClusters(), + computeCellsHandler(mTimeFrameGPU->getDeviceArrayClusters(), mTimeFrameGPU->getDeviceArrayUnsortedClusters(), mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), mTimeFrameGPU->getDeviceArrayTracklets(), @@ -216,7 +211,6 @@ void TrackerTraitsGPU::computeLayerCells(const int iteration) mTimeFrameGPU->getDeviceCells()[iLayer], mTimeFrameGPU->getDeviceArrayCellsLUT(), mTimeFrameGPU->getDeviceCellLUTs()[iLayer], - this->mTrkParams[iteration].DeltaROF, this->mBz, this->mTrkParams[iteration].MaxChi2ClusterAttachment, this->mTrkParams[iteration].CellDeltaTanLambdaSigma, @@ -227,8 +221,8 @@ void TrackerTraitsGPU::computeLayerCells(const int iteration) } } -template -void TrackerTraitsGPU::findCellsNeighbours(const int iteration) +template +void TrackerTraitsGPU::findCellsNeighbours(const int iteration) { const auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); @@ -241,14 +235,13 @@ void TrackerTraitsGPU::findCellsNeighbours(const int iteration) } mTimeFrameGPU->createNeighboursIndexTablesDevice(iLayer); mTimeFrameGPU->createNeighboursLUTDevice(iLayer, nextLayerCellsNum); - countCellNeighboursHandler(mTimeFrameGPU->getDeviceArrayCells(), + countCellNeighboursHandler(mTimeFrameGPU->getDeviceArrayCells(), mTimeFrameGPU->getDeviceNeighboursLUT(iLayer), // LUT is initialised here. mTimeFrameGPU->getDeviceArrayCellsLUT(), mTimeFrameGPU->getDeviceNeighbourPairs(iLayer), mTimeFrameGPU->getDeviceNeighboursIndexTables(iLayer), (const Tracklet**)mTimeFrameGPU->getDeviceArrayTracklets(), - this->mTrkParams[0].DeltaROF, - this->mTrkParams[0].MaxChi2ClusterAttachment, + this->mTrkParams[iteration].MaxChi2ClusterAttachment, this->mBz, iLayer, currentLayerCellsNum, @@ -262,14 +255,13 @@ void TrackerTraitsGPU::findCellsNeighbours(const int iteration) if (mTimeFrameGPU->getNNeighbours()[iLayer] == 0) { continue; } - computeCellNeighboursHandler(mTimeFrameGPU->getDeviceArrayCells(), + computeCellNeighboursHandler(mTimeFrameGPU->getDeviceArrayCells(), mTimeFrameGPU->getDeviceNeighboursLUT(iLayer), mTimeFrameGPU->getDeviceArrayCellsLUT(), mTimeFrameGPU->getDeviceNeighbourPairs(iLayer), mTimeFrameGPU->getDeviceNeighboursIndexTables(iLayer), (const Tracklet**)mTimeFrameGPU->getDeviceArrayTracklets(), - this->mTrkParams[0].DeltaROF, - this->mTrkParams[0].MaxChi2ClusterAttachment, + this->mTrkParams[iteration].MaxChi2ClusterAttachment, this->mBz, iLayer, currentLayerCellsNum, @@ -287,18 +279,18 @@ void TrackerTraitsGPU::findCellsNeighbours(const int iteration) mTimeFrameGPU->syncStreams(false); } -template -void TrackerTraitsGPU::findRoads(const int iteration) +template +void TrackerTraitsGPU::findRoads(const int iteration) { auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); for (int startLevel{this->mTrkParams[iteration].CellsPerRoad()}; startLevel >= this->mTrkParams[iteration].CellMinimumLevel(); --startLevel) { const int minimumLayer{startLevel - 1}; - bounded_vector> trackSeeds(this->getMemoryPool().get()); + bounded_vector> trackSeeds(this->getMemoryPool().get()); for (int startLayer{this->mTrkParams[iteration].CellsPerRoad() - 1}; startLayer >= minimumLayer; --startLayer) { if ((this->mTrkParams[iteration].StartLayerMask & (1 << (startLayer + 2))) == 0) { continue; } - processNeighboursHandler(startLayer, + processNeighboursHandler(startLayer, startLevel, mTimeFrameGPU->getDeviceArrayCells(), mTimeFrameGPU->getDeviceCells()[startLayer], @@ -389,29 +381,28 @@ void TrackerTraitsGPU::findRoads(const int iteration) continue; } - std::array rofs{INT_MAX, INT_MAX, INT_MAX}; + bool firstCls{true}; + TimeEstBC ts; for (int iLayer{0}; iLayer < this->mTrkParams[0].NLayers; ++iLayer) { if (track.getClusterIndex(iLayer) == constants::UnusedIndex) { continue; } mTimeFrameGPU->markUsedCluster(iLayer, track.getClusterIndex(iLayer)); int currentROF = mTimeFrameGPU->getClusterROF(iLayer, track.getClusterIndex(iLayer)); - for (int iR{0}; iR < 3; ++iR) { - if (rofs[iR] == INT_MAX) { - rofs[iR] = currentROF; - } - if (rofs[iR] == currentROF) { - break; + auto rofTS = mTimeFrameGPU->getROFOverlapTableView().getLayer(iLayer).getROFTimeBounds(currentROF, true); + if (firstCls) { + ts = rofTS; + } else { + if (!ts.isCompatible(rofTS)) { + LOGP(fatal, "TS {}+/-{} are incompatible with {}+/-{}, this should not happen!", rofTS.getTimeStamp(), rofTS.getTimeStampError(), ts.getTimeStamp(), ts.getTimeStampError()); } + ts += rofTS; } } - if (rofs[2] != INT_MAX) { - continue; - } - if (rofs[1] != INT_MAX) { - track.setNextROFbit(); - } - mTimeFrameGPU->getTracks(std::min(rofs[0], rofs[1])).emplace_back(track); + track.getTimeStamp() = ts.makeSymmetrical(); + track.setUserField(0); + track.getParamOut().setUserField(0); + mTimeFrameGPU->getTracks().emplace_back(track); } mTimeFrameGPU->loadUsedClustersDevice(); } @@ -419,26 +410,26 @@ void TrackerTraitsGPU::findRoads(const int iteration) mTimeFrameGPU->popMemoryStack(iteration); }; -template -int TrackerTraitsGPU::getTFNumberOfClusters() const +template +int TrackerTraitsGPU::getTFNumberOfClusters() const { return mTimeFrameGPU->getNumberOfClusters(); } -template -int TrackerTraitsGPU::getTFNumberOfTracklets() const +template +int TrackerTraitsGPU::getTFNumberOfTracklets() const { return std::accumulate(mTimeFrameGPU->getNTracklets().begin(), mTimeFrameGPU->getNTracklets().end(), 0); } -template -int TrackerTraitsGPU::getTFNumberOfCells() const +template +int TrackerTraitsGPU::getTFNumberOfCells() const { return mTimeFrameGPU->getNumberOfCells(); } -template -void TrackerTraitsGPU::setBz(float bz) +template +void TrackerTraitsGPU::setBz(float bz) { this->mBz = bz; mTimeFrameGPU->setBz(bz); diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu index 525b37eb52891..795b568f6174d 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -162,8 +162,8 @@ GPUdii() o2::track::TrackParCov buildTrackSeed(const Cluster& cluster1, 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, +template +GPUdii() TrackITSExt seedTrackForRefit(const CellSeed& seed, const TrackingFrameInfo** foundTrackingFrameInfo, const Cluster** unsortedClusters, const float* layerRadii, @@ -171,8 +171,8 @@ GPUdii() TrackITSExt seedTrackForRefit(const CellSeed& seed, const int reseedIfShorter) { TrackITSExt temporaryTrack(seed); - int lrMin = nLayers, lrMax = 0, lrMid = 0; - for (int iL{0}; iL < nLayers; ++iL) { + 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) { @@ -183,9 +183,9 @@ GPUdii() TrackITSExt seedTrackForRefit(const CellSeed& seed, } 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) { + if (ncl == NLayers) { lrMin = 0; - lrMax = nLayers - 1; + lrMax = NLayers - 1; lrMid = (lrMin + lrMax) / 2; } else { lrMid = lrMin + 1; @@ -259,13 +259,13 @@ struct is_valid_pair { } }; -template +template struct seed_selector { float maxQ2Pt; float maxChi2; GPUhd() seed_selector(float maxQ2Pt, float maxChi2) : maxQ2Pt(maxQ2Pt), maxChi2(maxChi2) {} - GPUhd() bool operator()(const CellSeed& seed) const + GPUhd() bool operator()(const CellSeed& seed) const { return !(seed.getQ2Pt() > maxQ2Pt || seed.getChi2() > maxChi2); } @@ -278,9 +278,9 @@ struct compare_track_chi2 { } }; -template +template GPUg() void __launch_bounds__(256, 1) fitTrackSeedsKernel( - CellSeed* trackSeeds, + CellSeed* trackSeeds, const TrackingFrameInfo** foundTrackingFrameInfo, const Cluster** unsortedClusters, o2::its::TrackITSExt* tracks, @@ -306,11 +306,11 @@ GPUg() void __launch_bounds__(256, 1) fitTrackSeedsKernel( } } - TrackITSExt temporaryTrack = seedTrackForRefit(trackSeeds[iCurrentTrackSeedIndex], foundTrackingFrameInfo, unsortedClusters, layerRadii, bz, reseedIfShorter); + 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, + NLayers, // int firstLayer, 1, // int firstCluster, maxChi2ClusterAttachment, // float maxChi2ClusterAttachment, maxChi2NDF, // float maxChi2NDF, @@ -331,7 +331,7 @@ GPUg() void __launch_bounds__(256, 1) fitTrackSeedsKernel( 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, + NLayers - 1, // int lastLayer, -1, // int firstLayer, -1, // int firstCluster, maxChi2ClusterAttachment, // float maxChi2ClusterAttachment, @@ -344,7 +344,7 @@ GPUg() void __launch_bounds__(256, 1) fitTrackSeedsKernel( matCorrType, // o2::base::PropagatorF::MatCorrType matCorrType &linRef, shifRefToCluster); - if (!fitSuccess || temporaryTrack.getPt() < minPts[nLayers - temporaryTrack.getNClusters()]) { + if (!fitSuccess || temporaryTrack.getPt() < minPts[NLayers - temporaryTrack.getNClusters()]) { continue; } if (repeatRefitOut) { // repeat outward refit seeding and linearizing with the stable inward fit result @@ -356,7 +356,7 @@ GPUg() void __launch_bounds__(256, 1) fitTrackSeedsKernel( temporaryTrack.setChi2(0); fitSuccess = fitTrack(temporaryTrack, // TrackITSExt& track, 0, // int lastLayer, - nLayers, // int firstLayer, + NLayers, // int firstLayer, 1, // int firstCluster, maxChi2ClusterAttachment, // float maxChi2ClusterAttachment, maxChi2NDF, // float maxChi2NDF, @@ -384,15 +384,14 @@ GPUg() void __launch_bounds__(256, 1) fitTrackSeedsKernel( } } -template +template GPUg() void __launch_bounds__(256, 1) computeLayerCellNeighboursKernel( - CellSeed** cellSeedArray, + CellSeed** cellSeedArray, int* neighboursLUT, int* neighboursIndexTable, int** cellsLUTs, gpuPair* cellNeighbours, const Tracklet** tracklets, - const int deltaROF, const float maxChi2ClusterAttachment, const float bz, const int layerIndex, @@ -411,30 +410,18 @@ GPUg() void __launch_bounds__(256, 1) computeLayerCellNeighboursKernel( const int nextLayerLastCellIndex{cellsLUTs[layerIndex + 1][nextLayerTrackletIndex + 1]}; int foundNeighbours{0}; for (int iNextCell{nextLayerFirstCellIndex}; iNextCell < nextLayerLastCellIndex; ++iNextCell) { - auto nextCellSeed{cellSeedArray[layerIndex + 1][iNextCell]}; // Copy - if (nextCellSeed.getFirstTrackletIndex() != nextLayerTrackletIndex) { // Check if cells share the same tracklet + auto nextCellSeed{cellSeedArray[layerIndex + 1][iNextCell]}; // Copy + if (nextCellSeed.getFirstTrackletIndex() != nextLayerTrackletIndex || !currentCellSeed.getTimeStamp().isCompatible(nextCellSeed.getTimeStamp())) { break; } - if (deltaROF) { - const auto& trkl00 = tracklets[layerIndex][currentCellSeed.getFirstTrackletIndex()]; - const auto& trkl01 = tracklets[layerIndex + 1][currentCellSeed.getSecondTrackletIndex()]; - const auto& trkl10 = tracklets[layerIndex + 1][nextCellSeed.getFirstTrackletIndex()]; - const auto& trkl11 = tracklets[layerIndex + 2][nextCellSeed.getSecondTrackletIndex()]; - if ((o2::gpu::CAMath::Max(trkl00.getMaxRof(), o2::gpu::CAMath::Max(trkl01.getMaxRof(), o2::gpu::CAMath::Max(trkl10.getMaxRof(), trkl11.getMaxRof()))) - - o2::gpu::CAMath::Min(trkl00.getMinRof(), o2::gpu::CAMath::Min(trkl01.getMinRof(), o2::gpu::CAMath::Min(trkl10.getMinRof(), trkl11.getMinRof())))) > deltaROF) { - continue; - } - } - if (!nextCellSeed.rotate(currentCellSeed.getAlpha()) || !nextCellSeed.propagateTo(currentCellSeed.getX(), bz)) { continue; } float chi2 = currentCellSeed.getPredictedChi2(nextCellSeed); - if (chi2 > maxChi2ClusterAttachment) /// TODO: switch to the chi2 wrt cluster to avoid correlation - { + if (chi2 > maxChi2ClusterAttachment) { continue; } @@ -453,7 +440,7 @@ GPUg() void __launch_bounds__(256, 1) computeLayerCellNeighboursKernel( } } -template +template GPUg() void __launch_bounds__(256, 1) computeLayerCellsKernel( const Cluster** sortedClusters, const Cluster** unsortedClusters, @@ -462,9 +449,8 @@ GPUg() void __launch_bounds__(256, 1) computeLayerCellsKernel( int** trackletsLUT, const int nTrackletsCurrent, const int layer, - CellSeed* cells, + CellSeed* cells, int** cellsLUTs, - const int deltaROF, const float bz, const float maxChi2ClusterAttachment, const float cellDeltaTanLambdaSigma, @@ -490,7 +476,7 @@ GPUg() void __launch_bounds__(256, 1) computeLayerCellsKernel( break; } const Tracklet& nextTracklet = tracklets[layer + 1][iNextTrackletIndex]; - if (deltaROF && currentTracklet.getSpanRof(nextTracklet) > deltaROF) { + if (!currentTracklet.getTimeStamp().isCompatible(nextTracklet.getTimeStamp())) { continue; } const float deltaTanLambda{o2::gpu::CAMath::Abs(currentTracklet.tanLambda - nextTracklet.tanLambda)}; @@ -534,7 +520,9 @@ GPUg() void __launch_bounds__(256, 1) computeLayerCellsKernel( continue; } if constexpr (!initRun) { - new (cells + cellsLUTs[layer][iCurrentTrackletIndex] + foundCells) CellSeed{layer, clusId[0], clusId[1], clusId[2], iCurrentTrackletIndex, iNextTrackletIndex, track, chi2}; + TimeEstBC ts = currentTracklet.getTimeStamp(); + ts += nextTracklet.getTimeStamp(); + new (cells + cellsLUTs[layer][iCurrentTrackletIndex] + foundCells) CellSeed{layer, clusId[0], clusId[1], clusId[2], iCurrentTrackletIndex, iNextTrackletIndex, track, chi2, ts}; } ++foundCells; } @@ -545,24 +533,21 @@ GPUg() void __launch_bounds__(256, 1) computeLayerCellsKernel( } } -template +template GPUg() void __launch_bounds__(256, 1) computeLayerTrackletsMultiROFKernel( - const IndexTableUtils* utils, - const uint8_t* multMask, + const IndexTableUtils* utils, + const typename ROFMaskTable::View rofMask, const int layerIndex, - const int startROF, - const int endROF, - const int totalROFs, - const int deltaROF, + const typename ROFOverlapTable::View rofOverlaps, + const typename ROFVertexLookupTable::View vertexLUT, const Vertex* vertices, const int* rofPV, - const int nVertices, const int vertexId, - const Cluster** clusters, // Input data rof0 - const int** ROFClusters, // Number of clusters on layers per ROF - const unsigned char** usedClusters, // Used clusters - const int** indexTables, // Input data rof0-delta getNphiBins()}; const int zBins{utils->getNzBins()}; const int tableSize{phiBins * zBins + 1}; - for (unsigned int iROF{blockIdx.x}; iROF < endROF - startROF; iROF += gridDim.x) { - const short pivotROF = iROF + startROF; - const short minROF = o2::gpu::CAMath::Max(startROF, static_cast(pivotROF - deltaROF)); - const short maxROF = o2::gpu::CAMath::Min(endROF - 1, static_cast(pivotROF + deltaROF)); - auto primaryVertices = getPrimaryVertices(minROF, maxROF, rofPV, totalROFs, vertices); + const int totalROFs0 = rofOverlaps.getLayer(layerIndex).mNROFsTF; + const int totalROFs1 = rofOverlaps.getLayer(layerIndex + 1).mNROFsTF; + for (unsigned int pivotROF{blockIdx.x}; pivotROF < totalROFs0; pivotROF += gridDim.x) { + if (!rofMask.isROFEnabled(layerIndex, pivotROF)) { + continue; + } + + const auto& pvs = vertexLUT.getVertices(layerIndex, pivotROF); + auto primaryVertices = gpuSpan(&vertices[pvs.getFirstEntry()], pvs.getEntries()); if (primaryVertices.empty()) { continue; } const auto startVtx{vertexId >= 0 ? vertexId : 0}; const auto endVtx{vertexId >= 0 ? o2::gpu::CAMath::Min(vertexId + 1, static_cast(primaryVertices.size())) : static_cast(primaryVertices.size())}; - if ((endVtx - startVtx) <= 0) { + if (endVtx <= startVtx || (vertexId + 1) > primaryVertices.size()) { + continue; + } + + const auto& rofOverlap = rofOverlaps.getOverlap(layerIndex, layerIndex + 1, pivotROF); + if (!rofOverlap.getEntries()) { continue; } - auto clustersCurrentLayer = getClustersOnLayer(pivotROF, totalROFs, layerIndex, ROFClusters, clusters); + auto clustersCurrentLayer = getClustersOnLayer(pivotROF, totalROFs0, layerIndex, ROFClusters, clusters); if (clustersCurrentLayer.empty()) { continue; } @@ -613,6 +607,9 @@ GPUg() void __launch_bounds__(256, 1) computeLayerTrackletsMultiROFKernel( const float inverseR0{1.f / currentCluster.radius}; for (int iV{startVtx}; iV < endVtx; ++iV) { auto& primaryVertex{primaryVertices[iV]}; + if (!vertexLUT.isVertexCompatible(layerIndex, pivotROF, primaryVertex)) { + continue; + } if ((primaryVertex.isFlagSet(Vertex::Flags::UPCMode) && iteration != 3) || (iteration == 3 && !primaryVertex.isFlagSet(Vertex::Flags::UPCMode))) { continue; } @@ -623,7 +620,7 @@ GPUg() void __launch_bounds__(256, 1) computeLayerTrackletsMultiROFKernel( const float zAtRmax{tanLambda * (maxR - currentCluster.radius) + currentCluster.zCoordinate}; const float sqInverseDeltaZ0{1.f / (math_utils::Sq(currentCluster.zCoordinate - primaryVertex.getZ()) + constants::Tolerance)}; /// protecting from overflows adding the detector resolution const float sigmaZ{o2::gpu::CAMath::Sqrt(math_utils::Sq(resolution) * math_utils::Sq(tanLambda) * ((math_utils::Sq(inverseR0) + sqInverseDeltaZ0) * math_utils::Sq(meanDeltaR) + 1.f) + math_utils::Sq(meanDeltaR * MSAngle))}; - const int4 selectedBinsRect{getBinsRect(currentCluster, layerIndex + 1, utils, zAtRmin, zAtRmax, sigmaZ * NSigmaCut, phiCut)}; + const int4 selectedBinsRect{getBinsRect(currentCluster, layerIndex + 1, utils, zAtRmin, zAtRmax, sigmaZ * NSigmaCut, phiCut)}; if (selectedBinsRect.x == 0 && selectedBinsRect.y == 0 && selectedBinsRect.z == 0 && selectedBinsRect.w == 0) { continue; } @@ -633,11 +630,18 @@ GPUg() void __launch_bounds__(256, 1) computeLayerTrackletsMultiROFKernel( phiBinsNum += phiBins; } - for (short targetROF{minROF}; targetROF <= maxROF; ++targetROF) { - auto clustersNextLayer = getClustersOnLayer(targetROF, totalROFs, layerIndex + 1, ROFClusters, clusters); + for (short targetROF = rofOverlap.getFirstEntry(); targetROF < rofOverlap.getEntriesBound(); ++targetROF) { + if (!rofMask.isROFEnabled(layerIndex + 1, pivotROF)) { + continue; + } + auto clustersNextLayer = getClustersOnLayer(targetROF, totalROFs1, layerIndex + 1, ROFClusters, clusters); if (clustersNextLayer.empty()) { continue; } + const auto ts = rofOverlaps.getTimeStamp(layerIndex, pivotROF, layerIndex + 1, targetROF); + if (!ts.isCompatible(primaryVertex.getTimeStamp())) { + continue; + } for (int iPhiCount{0}; iPhiCount < phiBinsNum; iPhiCount++) { int iPhiBin = (selectedBinsRect.y + iPhiCount) % phiBins; const int firstBinIndex{utils->getBinIndex(selectedBinsRect.x, iPhiBin)}; @@ -661,7 +665,7 @@ GPUg() void __launch_bounds__(256, 1) computeLayerTrackletsMultiROFKernel( const float phi{o2::gpu::CAMath::ATan2(currentCluster.yCoordinate - nextCluster.yCoordinate, currentCluster.xCoordinate - nextCluster.xCoordinate)}; const float tanL{(currentCluster.zCoordinate - nextCluster.zCoordinate) / (currentCluster.radius - nextCluster.radius)}; const int nextSortedIndex{ROFClusters[layerIndex + 1][targetROF] + nextClusterIndex}; - new (tracklets[layerIndex] + trackletsLUT[layerIndex][currentSortedIndex] + storedTracklets) Tracklet{currentSortedIndex, nextSortedIndex, tanL, phi, pivotROF, targetROF}; + new (tracklets[layerIndex] + trackletsLUT[layerIndex][currentSortedIndex] + storedTracklets) Tracklet{currentSortedIndex, nextSortedIndex, tanL, phi, ts}; } ++storedTracklets; } @@ -683,15 +687,15 @@ GPUg() void __launch_bounds__(256, 1) compileTrackletsLookupTableKernel( } } -template +template GPUg() void __launch_bounds__(256, 1) processNeighboursKernel( const int layer, const int level, - CellSeed** allCellSeeds, - CellSeed* currentCellSeeds, + CellSeed** allCellSeeds, + CellSeed* currentCellSeeds, const int* currentCellIds, const unsigned int nCurrentCells, - CellSeed* updatedCellSeeds, + CellSeed* updatedCellSeeds, int* updatedCellsIds, int* foundSeedsTable, // auxiliary only in GPU code to compute the number of cells per iteration const unsigned char** usedClusters, // Used clusters @@ -732,12 +736,15 @@ GPUg() void __launch_bounds__(256, 1) processNeighboursKernel( if (neighbourCell.getSecondTrackletIndex() != currentCell.getFirstTrackletIndex()) { continue; } - if (usedClusters[layer - 1][neighbourCell.getFirstClusterIndex()]) { + if (!currentCell.getTimeStamp().isCompatible(neighbourCell.getTimeStamp())) { continue; } if (currentCell.getLevel() - 1 != neighbourCell.getLevel()) { continue; } + if (usedClusters[layer - 1][neighbourCell.getFirstClusterIndex()]) { + continue; + } auto seed{currentCell}; auto& trHit = foundTrackingFrameInfo[layer - 1][neighbourCell.getFirstClusterIndex()]; @@ -780,18 +787,15 @@ GPUg() void __launch_bounds__(256, 1) processNeighboursKernel( } // namespace gpu -template -void countTrackletsInROFsHandler(const IndexTableUtils* utils, - const uint8_t* multMask, +template +void countTrackletsInROFsHandler(const IndexTableUtils* utils, + const typename ROFMaskTable::View& rofMask, const int layer, - const int startROF, - const int endROF, - const int maxROF, - const int deltaROF, + const typename ROFOverlapTable::View& rofOverlaps, + const typename ROFVertexLookupTable::View& vertexLUT, const int vertexId, const Vertex* vertices, const int* rofPV, - const int nVertices, const Cluster** clusters, std::vector nClusters, const int** ROFClusters, @@ -803,8 +807,8 @@ void countTrackletsInROFsHandler(const IndexTableUtils* utils, const float NSigmaCut, bounded_vector& phiCuts, const float resolutionPV, - std::array& minRs, - std::array& maxRs, + std::array& minRs, + std::array& maxRs, bounded_vector& resolutions, std::vector& radii, bounded_vector& mulScatAng, @@ -815,15 +819,12 @@ void countTrackletsInROFsHandler(const IndexTableUtils* utils, { gpu::computeLayerTrackletsMultiROFKernel<<>>( utils, - multMask, + rofMask, layer, - startROF, - endROF, - maxROF, - deltaROF, + rofOverlaps, + vertexLUT, vertices, rofPV, - nVertices, vertexId, clusters, ROFClusters, @@ -844,18 +845,15 @@ void countTrackletsInROFsHandler(const IndexTableUtils* utils, thrust::exclusive_scan(nosync_policy, trackletsLUTsHost[layer], trackletsLUTsHost[layer] + nClusters[layer] + 1, trackletsLUTsHost[layer]); } -template -void computeTrackletsInROFsHandler(const IndexTableUtils* utils, - const uint8_t* multMask, +template +void computeTrackletsInROFsHandler(const IndexTableUtils* utils, + const typename ROFMaskTable::View& rofMask, const int layer, - const int startROF, - const int endROF, - const int maxROF, - const int deltaROF, + const typename ROFOverlapTable::View& rofOverlaps, + const typename ROFVertexLookupTable::View& vertexLUT, const int vertexId, const Vertex* vertices, const int* rofPV, - const int nVertices, const Cluster** clusters, std::vector nClusters, const int** ROFClusters, @@ -870,8 +868,8 @@ void computeTrackletsInROFsHandler(const IndexTableUtils* utils, const float NSigmaCut, bounded_vector& phiCuts, const float resolutionPV, - std::array& minRs, - std::array& maxRs, + std::array& minRs, + std::array& maxRs, bounded_vector& resolutions, std::vector& radii, bounded_vector& mulScatAng, @@ -882,15 +880,12 @@ void computeTrackletsInROFsHandler(const IndexTableUtils* utils, { gpu::computeLayerTrackletsMultiROFKernel<<>>( utils, - multMask, + rofMask, layer, - startROF, - endROF, - maxROF, - deltaROF, + rofOverlaps, + vertexLUT, vertices, rofPV, - nVertices, vertexId, clusters, ROFClusters, @@ -922,7 +917,7 @@ void computeTrackletsInROFsHandler(const IndexTableUtils* utils, } } -template +template void countCellsHandler( const Cluster** sortedClusters, const Cluster** unsortedClusters, @@ -931,10 +926,9 @@ void countCellsHandler( int** trackletsLUT, const int nTracklets, const int layer, - CellSeed* cells, + CellSeed* cells, int** cellsLUTsArrayDevice, int* cellsLUTsHost, - const int deltaROF, const float bz, const float maxChi2ClusterAttachment, const float cellDeltaTanLambdaSigma, @@ -954,7 +948,6 @@ void countCellsHandler( layer, // const int cells, // CellSeed* cellsLUTsArrayDevice, // int** - deltaROF, // const int bz, // const float maxChi2ClusterAttachment, // const float cellDeltaTanLambdaSigma, // const float @@ -963,7 +956,7 @@ void countCellsHandler( thrust::exclusive_scan(nosync_policy, cellsLUTsHost, cellsLUTsHost + nTracklets + 1, cellsLUTsHost); } -template +template void computeCellsHandler( const Cluster** sortedClusters, const Cluster** unsortedClusters, @@ -972,10 +965,9 @@ void computeCellsHandler( int** trackletsLUT, const int nTracklets, const int layer, - CellSeed* cells, + CellSeed* cells, int** cellsLUTsArrayDevice, int* cellsLUTsHost, - const int deltaROF, const float bz, const float maxChi2ClusterAttachment, const float cellDeltaTanLambdaSigma, @@ -994,21 +986,19 @@ void computeCellsHandler( layer, // const int cells, // CellSeed* cellsLUTsArrayDevice, // int** - deltaROF, // const int bz, // const float maxChi2ClusterAttachment, // const float cellDeltaTanLambdaSigma, // const float nSigmaCut); // const float } -template -void countCellNeighboursHandler(CellSeed** cellsLayersDevice, +template +void countCellNeighboursHandler(CellSeed** cellsLayersDevice, int* neighboursLUT, int** cellsLUTs, gpuPair* cellNeighbours, int* neighboursIndexTable, const Tracklet** tracklets, - const int deltaROF, const float maxChi2ClusterAttachment, const float bz, const int layerIndex, @@ -1027,7 +1017,6 @@ void countCellNeighboursHandler(CellSeed** cellsLayersDevice, cellsLUTs, cellNeighbours, tracklets, - deltaROF, maxChi2ClusterAttachment, bz, layerIndex, @@ -1038,14 +1027,13 @@ void countCellNeighboursHandler(CellSeed** cellsLayersDevice, thrust::exclusive_scan(nosync_policy, neighboursIndexTable, neighboursIndexTable + nCells + 1, neighboursIndexTable); } -template -void computeCellNeighboursHandler(CellSeed** cellsLayersDevice, +template +void computeCellNeighboursHandler(CellSeed** cellsLayersDevice, int* neighboursLUT, int** cellsLUTs, gpuPair* cellNeighbours, int* neighboursIndexTable, const Tracklet** tracklets, - const int deltaROF, const float maxChi2ClusterAttachment, const float bz, const int layerIndex, @@ -1063,7 +1051,6 @@ void computeCellNeighboursHandler(CellSeed** cellsLayersDevice, cellsLUTs, cellNeighbours, tracklets, - deltaROF, maxChi2ClusterAttachment, bz, layerIndex, @@ -1087,17 +1074,17 @@ int filterCellNeighboursHandler(gpuPair* cellNeighbourPairs, return newSize; } -template +template void processNeighboursHandler(const int startLayer, const int startLevel, - CellSeed** allCellSeeds, - CellSeed* currentCellSeeds, - std::array& nCells, + CellSeed** allCellSeeds, + CellSeed* currentCellSeeds, + std::array& nCells, const unsigned char** usedClusters, - std::array& neighbours, + std::array& neighbours, gsl::span neighboursDeviceLUTs, const TrackingFrameInfo** foundTrackingFrameInfo, - bounded_vector>& seedsHost, + bounded_vector>& seedsHost, const float bz, const float maxChi2ClusterAttachment, const float maxChi2NDF, @@ -1110,11 +1097,11 @@ void processNeighboursHandler(const int startLayer, constexpr uint64_t Tag = qStr2Tag("ITS_PNH1"); alloc->pushTagOnStack(Tag); auto allocInt = gpu::TypedAllocator(alloc); - auto allocCellSeed = gpu::TypedAllocator>(alloc); + auto allocCellSeed = gpu::TypedAllocator>(alloc); thrust::device_vector> foundSeedsTable(nCells[startLayer] + 1, 0, allocInt); auto nosync_policy = THRUST_NAMESPACE::par_nosync(gpu::TypedAllocator(alloc)).on(gpu::Stream::DefaultStream); - gpu::processNeighboursKernel<<>>( + gpu::processNeighboursKernel<<>>( startLayer, startLevel, allCellSeeds, @@ -1135,8 +1122,8 @@ void processNeighboursHandler(const int startLayer, thrust::exclusive_scan(nosync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), foundSeedsTable.begin()); thrust::device_vector> updatedCellId(foundSeedsTable.back(), 0, allocInt); - thrust::device_vector, gpu::TypedAllocator>> updatedCellSeed(foundSeedsTable.back(), allocCellSeed); - gpu::processNeighboursKernel<<>>( + thrust::device_vector, gpu::TypedAllocator>> updatedCellSeed(foundSeedsTable.back(), allocCellSeed); + gpu::processNeighboursKernel<<>>( startLayer, startLevel, allCellSeeds, @@ -1158,17 +1145,17 @@ void processNeighboursHandler(const int startLayer, int level = startLevel; thrust::device_vector> lastCellId(allocInt); - thrust::device_vector, gpu::TypedAllocator>> lastCellSeed(allocCellSeed); + thrust::device_vector, gpu::TypedAllocator>> lastCellSeed(allocCellSeed); for (int iLayer{startLayer - 1}; iLayer > 0 && level > 2; --iLayer) { lastCellSeed.swap(updatedCellSeed); lastCellId.swap(updatedCellId); - thrust::device_vector, gpu::TypedAllocator>>(allocCellSeed).swap(updatedCellSeed); + thrust::device_vector, gpu::TypedAllocator>>(allocCellSeed).swap(updatedCellSeed); thrust::device_vector>(allocInt).swap(updatedCellId); auto lastCellSeedSize{lastCellSeed.size()}; foundSeedsTable.resize(lastCellSeedSize + 1); thrust::fill(nosync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), 0); - gpu::processNeighboursKernel<<>>( + gpu::processNeighboursKernel<<>>( iLayer, --level, allCellSeeds, @@ -1192,9 +1179,9 @@ void processNeighboursHandler(const int startLayer, updatedCellId.resize(foundSeeds); thrust::fill(nosync_policy, updatedCellId.begin(), updatedCellId.end(), 0); updatedCellSeed.resize(foundSeeds); - thrust::fill(nosync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), CellSeed()); + thrust::fill(nosync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), CellSeed()); - gpu::processNeighboursKernel<<>>( + gpu::processNeighboursKernel<<>>( iLayer, level, allCellSeeds, @@ -1214,16 +1201,16 @@ void processNeighboursHandler(const int startLayer, matCorrType); } GPUChkErrS(cudaStreamSynchronize(gpu::Stream::DefaultStream)); - thrust::device_vector, gpu::TypedAllocator>> outSeeds(updatedCellSeed.size(), allocCellSeed); - auto end = thrust::copy_if(nosync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), outSeeds.begin(), gpu::seed_selector(1.e3, maxChi2NDF * ((startLevel + 2) * 2 - 5))); + thrust::device_vector, gpu::TypedAllocator>> outSeeds(updatedCellSeed.size(), allocCellSeed); + auto end = thrust::copy_if(nosync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), outSeeds.begin(), gpu::seed_selector(1.e3, maxChi2NDF * ((startLevel + 2) * 2 - 5))); auto s{end - outSeeds.begin()}; seedsHost.reserve(seedsHost.size() + s); thrust::copy(outSeeds.begin(), outSeeds.begin() + s, std::back_inserter(seedsHost)); alloc->popTagOffStack(Tag); } -template -void countTrackSeedHandler(CellSeed* trackSeeds, +template +void countTrackSeedHandler(CellSeed* trackSeeds, const TrackingFrameInfo** foundTrackingFrameInfo, const Cluster** unsortedClusters, int* seedLUT, @@ -1248,7 +1235,7 @@ void countTrackSeedHandler(CellSeed* trackSeeds, // small transferes! thrust::device_vector minPts(minPtsHost); thrust::device_vector layerRadii(layerRadiiHost); - gpu::fitTrackSeedsKernel<<>>( + gpu::fitTrackSeedsKernel<<>>( trackSeeds, // CellSeed* foundTrackingFrameInfo, // TrackingFrameInfo** unsortedClusters, // Cluster** @@ -1270,8 +1257,8 @@ void countTrackSeedHandler(CellSeed* trackSeeds, thrust::exclusive_scan(sync_policy, seedLUT, seedLUT + nSeeds + 1, seedLUT); } -template -void computeTrackSeedHandler(CellSeed* trackSeeds, +template +void computeTrackSeedHandler(CellSeed* trackSeeds, const TrackingFrameInfo** foundTrackingFrameInfo, const Cluster** unsortedClusters, o2::its::TrackITSExt* tracks, @@ -1295,7 +1282,7 @@ void computeTrackSeedHandler(CellSeed* trackSeeds, { thrust::device_vector minPts(minPtsHost); thrust::device_vector layerRadii(layerRadiiHost); - gpu::fitTrackSeedsKernel<<>>( + gpu::fitTrackSeedsKernel<<>>( trackSeeds, // CellSeed* foundTrackingFrameInfo, // TrackingFrameInfo** unsortedClusters, // Cluster** @@ -1320,16 +1307,13 @@ void computeTrackSeedHandler(CellSeed* trackSeeds, /// Explicit instantiation of ITS2 handlers template void countTrackletsInROFsHandler<7>(const IndexTableUtils<7>* utils, - const uint8_t* multMask, + const ROFMaskTable<7>::View& rofMask, const int layer, - const int startROF, - const int endROF, - const int maxROF, - const int deltaROF, + const ROFOverlapTable<7>::View& rofOverlaps, + const ROFVertexLookupTable<7>::View& vertexLUT, const int vertexId, const Vertex* vertices, const int* rofPV, - const int nVertices, const Cluster** clusters, std::vector nClusters, const int** ROFClusters, @@ -1352,16 +1336,13 @@ template void countTrackletsInROFsHandler<7>(const IndexTableUtils<7>* utils, gpu::Streams& streams); template void computeTrackletsInROFsHandler<7>(const IndexTableUtils<7>* utils, - const uint8_t* multMask, + const ROFMaskTable<7>::View& rofMask, const int layer, - const int startROF, - const int endROF, - const int maxROF, - const int deltaROF, + const ROFOverlapTable<7>::View& rofOverlaps, + const ROFVertexLookupTable<7>::View& vertexLUT, const int vertexId, const Vertex* vertices, const int* rofPV, - const int nVertices, const Cluster** clusters, std::vector nClusters, const int** ROFClusters, @@ -1396,7 +1377,6 @@ template void countCellsHandler<7>(const Cluster** sortedClusters, CellSeed<7>* cells, int** cellsLUTsArrayDevice, int* cellsLUTsHost, - const int deltaROF, const float bz, const float maxChi2ClusterAttachment, const float cellDeltaTanLambdaSigma, @@ -1416,7 +1396,6 @@ template void computeCellsHandler<7>(const Cluster** sortedClusters, CellSeed<7>* cells, int** cellsLUTsArrayDevice, int* cellsLUTsHost, - const int deltaROF, const float bz, const float maxChi2ClusterAttachment, const float cellDeltaTanLambdaSigma, @@ -1431,7 +1410,6 @@ template void countCellNeighboursHandler<7>(CellSeed<7>** cellsLayersDevice, gpuPair* cellNeighbours, int* neighboursIndexTable, const Tracklet** tracklets, - const int deltaROF, const float maxChi2ClusterAttachment, const float bz, const int layerIndex, @@ -1449,7 +1427,6 @@ template void computeCellNeighboursHandler(CellSeed<7>** cellsLayersDevice, gpuPair* cellNeighbours, int* neighboursIndexTable, const Tracklet** tracklets, - const int deltaROF, const float maxChi2ClusterAttachment, const float bz, const int layerIndex, diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexerTraitsGPU.cxx b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexerTraitsGPU.cxx deleted file mode 100644 index 658d3cf0dfb91..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexerTraitsGPU.cxx +++ /dev/null @@ -1,179 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -// -/// \author matteo.concas@cern.ch - -#include - -#include "ITStracking/TrackingConfigParam.h" -#include "ITStrackingGPU/VertexingKernels.h" -#include "ITStrackingGPU/VertexerTraitsGPU.h" - -namespace o2::its -{ - -template -void VertexerTraitsGPU::initialise(const TrackingParameters& trackingParams, const int iteration) -{ - // FIXME - // Two things to fix here: - // This loads all necessary data for this step at once, can be overlayed with computation - // Also if running with the tracker some data is loaded twice! - mTimeFrameGPU->initialise(0, trackingParams, 3, &this->mIndexTableUtils, &mTfGPUParams); - - // FIXME some of these only need to be created once! - mTimeFrameGPU->loadIndexTableUtils(iteration); - mTimeFrameGPU->createUsedClustersDeviceArray(iteration, 3); - mTimeFrameGPU->createClustersDeviceArray(iteration, 3); - mTimeFrameGPU->createUnsortedClustersDeviceArray(iteration, 3); - mTimeFrameGPU->createClustersIndexTablesArray(iteration); - mTimeFrameGPU->createROFrameClustersDeviceArray(iteration); - for (int iLayer{0}; iLayer < 3; ++iLayer) { - mTimeFrameGPU->loadClustersDevice(iteration, iLayer); - mTimeFrameGPU->loadUnsortedClustersDevice(iteration, iLayer); - mTimeFrameGPU->loadClustersIndexTables(iteration, iLayer); - mTimeFrameGPU->createUsedClustersDevice(iteration, iLayer); - mTimeFrameGPU->loadROFrameClustersDevice(iteration, iLayer); - } -} - -template -void VertexerTraitsGPU::adoptTimeFrame(TimeFrame* tf) noexcept -{ - mTimeFrameGPU = static_cast*>(tf); - this->mTimeFrame = static_cast*>(tf); -} - -template -void VertexerTraitsGPU::updateVertexingParameters(const std::vector& vrtPar, const TimeFrameGPUParameters& tfPar) -{ - this->mVrtParams = vrtPar; - mTfGPUParams = tfPar; - this->mIndexTableUtils.setTrackingParameters(vrtPar[0]); - for (auto& par : this->mVrtParams) { - par.phiSpan = static_cast(std::ceil(this->mIndexTableUtils.getNphiBins() * par.phiCut / o2::constants::math::TwoPI)); - par.zSpan = static_cast(std::ceil(par.zCut * this->mIndexTableUtils.getInverseZCoordinate(0))); - } -} - -template -void VertexerTraitsGPU::computeTracklets(const int iteration) -{ - if (mTimeFrameGPU->getClusters().empty()) { - return; - } - const auto& conf = ITSGpuTrackingParamConfig::Instance(); - - mTimeFrameGPU->createVtxTrackletsLUTDevice(iteration); - countTrackletsInROFsHandler(mTimeFrameGPU->getDeviceIndexTableUtils(), - mTimeFrameGPU->getDeviceMultCutMask(), - mTimeFrameGPU->getNrof(), - this->mVrtParams[iteration].deltaRof, - mTimeFrameGPU->getDeviceROFramesPV(), - this->mVrtParams[iteration].vertPerRofThreshold, - mTimeFrameGPU->getDeviceArrayClusters(), - mTimeFrameGPU->getClusterSizes()[1], - mTimeFrameGPU->getDeviceROFrameClusters(), - (const uint8_t**)mTimeFrameGPU->getDeviceArrayUsedClusters(), - mTimeFrameGPU->getDeviceArrayClustersIndexTables(), - mTimeFrameGPU->getDeviceArrayNTrackletsPerCluster(), - mTimeFrameGPU->getDeviceArrayNTrackletsPerClusterSum(), - mTimeFrameGPU->getDeviceArrayNTrackletsPerROF(), - mTimeFrameGPU->getDeviceNTrackletsPerCluster(), - mTimeFrameGPU->getDeviceNTrackletsPerClusterSum(), - iteration, - this->mVrtParams[iteration].phiCut, - this->mVrtParams[iteration].maxTrackletsPerCluster, - conf.nBlocksVtxComputeTracklets[iteration], - conf.nThreadsVtxComputeTracklets[iteration], - mTimeFrameGPU->getStreams()); - mTimeFrameGPU->createVtxTrackletsBuffers(iteration); - computeTrackletsInROFsHandler(mTimeFrameGPU->getDeviceIndexTableUtils(), - mTimeFrameGPU->getDeviceMultCutMask(), - mTimeFrameGPU->getNrof(), - this->mVrtParams[iteration].deltaRof, - mTimeFrameGPU->getDeviceROFramesPV(), - this->mVrtParams[iteration].vertPerRofThreshold, - mTimeFrameGPU->getDeviceArrayClusters(), - mTimeFrameGPU->getClusterSizes()[1], - mTimeFrameGPU->getDeviceROFrameClusters(), - (const uint8_t**)mTimeFrameGPU->getDeviceArrayUsedClusters(), - mTimeFrameGPU->getDeviceArrayClustersIndexTables(), - mTimeFrameGPU->getDeviceArrayTracklets(), - (const int32_t**)mTimeFrameGPU->getDeviceArrayNTrackletsPerCluster(), - (const int32_t**)mTimeFrameGPU->getDeviceArrayNTrackletsPerClusterSum(), - (const int32_t**)mTimeFrameGPU->getDeviceArrayNTrackletsPerROF(), - iteration, - this->mVrtParams[iteration].phiCut, - this->mVrtParams[iteration].maxTrackletsPerCluster, - conf.nBlocksVtxComputeTracklets[iteration], - conf.nThreadsVtxComputeTracklets[iteration], - mTimeFrameGPU->getStreams()); -} - -template -void VertexerTraitsGPU::computeTrackletMatching(const int iteration) -{ - if (!mTimeFrameGPU->getTotalTrackletsTF(0) || !mTimeFrameGPU->getTotalTrackletsTF(1)) { - return; - } - - const auto& conf = ITSGpuTrackingParamConfig::Instance(); - mTimeFrameGPU->createVtxLinesLUTDevice(iteration); - countTrackletsMatchingInROFsHandler(mTimeFrameGPU->getNrof(), - this->mVrtParams[iteration].deltaRof, - mTimeFrameGPU->getClusterSizes()[1], - mTimeFrameGPU->getDeviceROFrameClusters(), - mTimeFrameGPU->getDeviceArrayClusters(), - mTimeFrameGPU->getDeviceArrayUsedClusters(), - (const Tracklet**)mTimeFrameGPU->getDeviceArrayTracklets(), - mTimeFrameGPU->getDeviceUsedTracklets(), - (const int32_t**)mTimeFrameGPU->getDeviceArrayNTrackletsPerCluster(), - (const int32_t**)mTimeFrameGPU->getDeviceArrayNTrackletsPerClusterSum(), - mTimeFrameGPU->getDeviceNLinesPerCluster(), - mTimeFrameGPU->getDeviceNLinesPerClusterSum(), - iteration, - this->mVrtParams[iteration].phiCut, - this->mVrtParams[iteration].tanLambdaCut, - conf.nBlocksVtxComputeMatching[iteration], - conf.nThreadsVtxComputeMatching[iteration], - mTimeFrameGPU->getStreams()); - mTimeFrameGPU->createVtxLinesBuffer(iteration); - computeTrackletsMatchingInROFsHandler(mTimeFrameGPU->getNrof(), - this->mVrtParams[iteration].deltaRof, - mTimeFrameGPU->getClusterSizes()[1], - mTimeFrameGPU->getDeviceROFrameClusters(), - mTimeFrameGPU->getDeviceArrayClusters(), - nullptr, - (const Tracklet**)mTimeFrameGPU->getDeviceArrayTracklets(), - mTimeFrameGPU->getDeviceUsedTracklets(), - (const int32_t**)mTimeFrameGPU->getDeviceArrayNTrackletsPerCluster(), - (const int32_t**)mTimeFrameGPU->getDeviceArrayNTrackletsPerClusterSum(), - (const int32_t*)mTimeFrameGPU->getDeviceNLinesPerClusterSum(), - mTimeFrameGPU->getDeviceLines(), - iteration, - this->mVrtParams[iteration].phiCut, - this->mVrtParams[iteration].tanLambdaCut, - conf.nBlocksVtxComputeMatching[iteration], - conf.nThreadsVtxComputeMatching[iteration], - mTimeFrameGPU->getStreams()); -} - -template -void VertexerTraitsGPU::computeVertices(const int iteration) -{ - LOGP(fatal, "This step is not implemented yet!"); - mTimeFrameGPU->loadUsedClustersDevice(); -} - -template class VertexerTraitsGPU<7>; - -} // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexingKernels.cu deleted file mode 100644 index a2787bb13598d..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/VertexingKernels.cu +++ /dev/null @@ -1,660 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// - -#include -#include - -#include "ITStrackingGPU/VertexingKernels.h" -#include "ITStracking/Tracklet.h" -#include "ITStracking/IndexTableUtils.h" -#include "ITStracking/ClusterLines.h" - -#include "GPUCommonMath.h" -#include "GPUCommonHelpers.h" -#include "GPUCommonDef.h" - -namespace o2::its -{ - -namespace gpu -{ - -template -GPUg() void computeLayerTrackletMutliROFKernel(const Cluster** GPUrestrict() clusters, - const int32_t** GPUrestrict() rofClusters, - const uint8_t** GPUrestrict() usedClusters, - const int32_t** GPUrestrict() clusterIndexTables, - const float phiCut, - maybe_const** GPUrestrict() tracklets, - maybe_const** GPUrestrict() trackletOffsets, - const IndexTableUtils* GPUrestrict() utils, - const int32_t nRofs, - const int32_t deltaRof, - const int32_t* GPUrestrict() rofPV, - const int32_t iteration, - const int32_t verPerRofThreshold, - const int32_t maxTrackletsPerCluster) -{ - constexpr int32_t iMode = (Mode == TrackletMode::Layer0Layer1) ? 0 : 1; - const int32_t phiBins(utils->getNphiBins()); - const int32_t zBins(utils->getNzBins()); - const int32_t tableSize{phiBins * zBins + 1}; - extern __shared__ uint16_t storedTrackletsShared[]; // each deltaROF needs its own counters - uint16_t* storedTrackletsLocal = storedTrackletsShared + threadIdx.x * (2 * deltaRof + 1); - for (uint32_t pivotRofId{blockIdx.x}; pivotRofId < (uint32_t)nRofs; pivotRofId += gridDim.x) { - if (iteration && rofPV[pivotRofId] > verPerRofThreshold) { - continue; - } - const uint16_t startROF = o2::gpu::CAMath::Max(0, (int)pivotRofId - deltaRof); - const uint16_t endROF = o2::gpu::CAMath::Min(nRofs, (int)pivotRofId + deltaRof + 1); - const auto clustersCurrentLayer = getClustersOnLayer((int32_t)pivotRofId, nRofs, 1, rofClusters, clusters); - if (clustersCurrentLayer.empty()) { - continue; - } - auto trackletsPerCluster = getNTrackletsPerCluster(pivotRofId, nRofs, iMode, rofClusters, trackletOffsets); - for (uint32_t iCurrentLayerClusterIndex{threadIdx.x}; iCurrentLayerClusterIndex < (uint32_t)clustersCurrentLayer.size(); iCurrentLayerClusterIndex += blockDim.x) { - for (int16_t i{0}; i < (int16_t)((2 * deltaRof) + 1); ++i) { - storedTrackletsLocal[i] = 0; - } - const Cluster& GPUrestrict() currentCluster { clustersCurrentLayer[iCurrentLayerClusterIndex] }; - const int4 selectedBinsRect{getBinsRect(currentCluster, (int)Mode, utils, 0.f, 0.f, 50.f, phiCut / 2)}; - if (selectedBinsRect.x != 0 || selectedBinsRect.y != 0 || selectedBinsRect.z != 0 || selectedBinsRect.w != 0) { - int phiBinsNum{selectedBinsRect.w - selectedBinsRect.y + 1}; - if (phiBinsNum < 0) { - phiBinsNum += phiBins; - } - for (int32_t iPhiBin{selectedBinsRect.y}, iPhiCount{0}; iPhiCount < phiBinsNum; iPhiBin = ++iPhiBin == phiBins ? 0 : iPhiBin, iPhiCount++) { - for (uint16_t targetRofId{startROF}; targetRofId < endROF; ++targetRofId) { - uint16_t& storedTracklets = storedTrackletsLocal[pivotRofId - targetRofId + deltaRof]; - const int32_t firstBinIndex{utils->getBinIndex(selectedBinsRect.x, iPhiBin)}; - const int32_t maxBinIndex{firstBinIndex + selectedBinsRect.z - selectedBinsRect.x + 1}; - const int32_t firstRowClusterIndex{clusterIndexTables[(int)Mode][(targetRofId)*tableSize + firstBinIndex]}; - const int32_t maxRowClusterIndex{clusterIndexTables[(int)Mode][(targetRofId)*tableSize + maxBinIndex]}; - auto clustersNextLayer = getClustersOnLayer((int32_t)targetRofId, nRofs, (int32_t)Mode, rofClusters, clusters); - if (clustersNextLayer.empty()) { - continue; - } - for (int32_t iNextLayerClusterIndex{firstRowClusterIndex}; iNextLayerClusterIndex < maxRowClusterIndex && iNextLayerClusterIndex < (int32_t)clustersNextLayer.size(); ++iNextLayerClusterIndex) { - if (iteration && usedClusters[(int32_t)Mode][iNextLayerClusterIndex]) { - continue; - } - const Cluster& GPUrestrict() nextCluster { clustersNextLayer[iNextLayerClusterIndex] }; - if (o2::gpu::GPUCommonMath::Abs(math_utils::smallestAngleDifference(currentCluster.phi, nextCluster.phi)) < phiCut) { - if (storedTracklets < maxTrackletsPerCluster) { - if constexpr (!dryRun) { - if constexpr (Mode == TrackletMode::Layer0Layer1) { - tracklets[0][trackletsPerCluster[iCurrentLayerClusterIndex] + storedTracklets] = Tracklet{iNextLayerClusterIndex, (int)iCurrentLayerClusterIndex, nextCluster, currentCluster, (short)targetRofId, (short)pivotRofId}; - } else { - tracklets[1][trackletsPerCluster[iCurrentLayerClusterIndex] + storedTracklets] = Tracklet{(int)iCurrentLayerClusterIndex, iNextLayerClusterIndex, currentCluster, nextCluster, (short)pivotRofId, (short)targetRofId}; - } - } - ++storedTracklets; - } - } - } - } - } - } - if constexpr (dryRun) { - for (int32_t i{0}; i < (int32_t)((2 * deltaRof) + 1); ++i) { - trackletsPerCluster[iCurrentLayerClusterIndex] += storedTrackletsLocal[i]; - } - } - } - } -} - -template -GPUg() void computeTrackletSelectionMutliROFKernel(const Cluster** GPUrestrict() clusters, - maybe_const** GPUrestrict() usedClusters, - const int32_t** GPUrestrict() rofClusters, - const float phiCut, - const float tanLambdaCut, - const Tracklet** GPUrestrict() tracklets, - uint8_t* GPUrestrict() usedTracklets, - const int32_t** GPUrestrict() trackletOffsets, - const int32_t** GPUrestrict() trackletLUTs, - maybe_const* lineOffsets, - maybe_const* GPUrestrict() lines, - const int32_t nRofs, - const int32_t deltaRof, - const int32_t maxTracklets) -{ - for (uint32_t pivotRofId{blockIdx.x}; pivotRofId < nRofs; pivotRofId += gridDim.x) { - const int16_t startROF = o2::gpu::CAMath::Max(0, (int32_t)pivotRofId - deltaRof); - const int16_t endROF = o2::gpu::CAMath::Min(nRofs, (int32_t)pivotRofId + deltaRof + 1); - - const uint32_t clusterOffset = rofClusters[1][pivotRofId]; - const uint32_t nClustersCurrentLayer = rofClusters[1][pivotRofId + 1] - clusterOffset; - if (nClustersCurrentLayer <= 0) { - continue; - } - - auto linesPerCluster = getNLinesPerCluster(pivotRofId, nRofs, rofClusters, lineOffsets); - auto nTrackletsPerCluster01 = getNTrackletsPerCluster(pivotRofId, nRofs, 0, rofClusters, trackletOffsets); - auto nTrackletsPerCluster12 = getNTrackletsPerCluster(pivotRofId, nRofs, 1, rofClusters, trackletOffsets); - - for (uint32_t iCurrentLayerClusterIndex{threadIdx.x}; iCurrentLayerClusterIndex < nClustersCurrentLayer; iCurrentLayerClusterIndex += blockDim.x) { - int32_t validTracklets{0}; - const int32_t nTracklets01 = nTrackletsPerCluster01[iCurrentLayerClusterIndex]; - const int32_t nTracklets12 = nTrackletsPerCluster12[iCurrentLayerClusterIndex]; - for (int32_t iTracklet12{0}; iTracklet12 < nTracklets12; ++iTracklet12) { - for (int32_t iTracklet01{0}; iTracklet01 < nTracklets01; ++iTracklet01) { - - if (usedTracklets[trackletLUTs[0][clusterOffset + iCurrentLayerClusterIndex] + iTracklet01]) { - continue; - } - - const auto& GPUrestrict() tracklet01 { tracklets[0][trackletLUTs[0][clusterOffset + iCurrentLayerClusterIndex] + iTracklet01] }; - const auto& GPUrestrict() tracklet12 { tracklets[1][trackletLUTs[1][clusterOffset + iCurrentLayerClusterIndex] + iTracklet12] }; - const int16_t rof0 = tracklet01.rof[0]; - const int16_t rof2 = tracklet12.rof[1]; - if (deltaRof > 0 && ((rof0 < startROF) || (rof0 >= endROF) || (rof2 < startROF) || (rof2 >= endROF) || (o2::gpu::CAMath::Abs(rof0 - rof2) > deltaRof))) { - continue; - } - - const float deltaTanLambda{o2::gpu::GPUCommonMath::Abs(tracklet01.tanLambda - tracklet12.tanLambda)}; - const float deltaPhi{o2::gpu::GPUCommonMath::Abs(math_utils::smallestAngleDifference(tracklet01.phi, tracklet12.phi))}; - // - if (deltaTanLambda < tanLambdaCut && deltaPhi < phiCut && validTracklets < maxTracklets) { - // TODO use atomics to avoid race conditions for torn writes but is it needed here? - usedTracklets[trackletLUTs[0][clusterOffset + iCurrentLayerClusterIndex] + iTracklet01] = 1; - if constexpr (dryRun) { - usedClusters[0][rofClusters[0][rof0] + tracklet01.firstClusterIndex] = 1; - usedClusters[2][rofClusters[2][rof2] + tracklet12.secondClusterIndex] = 1; - } else { - const Cluster* clusters0 = clusters[0] + rofClusters[0][tracklet01.rof[0]]; - const Cluster* clusters1 = clusters[1] + rofClusters[1][tracklet01.rof[1]]; - lines[lineOffsets[iCurrentLayerClusterIndex] + validTracklets] = Line(tracklet01, clusters0, clusters1); - } - ++validTracklets; - } - } - } - - if constexpr (dryRun) { - linesPerCluster[iCurrentLayerClusterIndex] = validTracklets; - } - } - } -} - -template -GPUg() void compileTrackletsPerROFKernel(const int32_t nRofs, - int** GPUrestrict() nTrackletsPerROF, - const int32_t** GPUrestrict() rofClusters, - const int32_t** GPUrestrict() nTrackletsPerCluster) -{ - // TODO is this the best reduction kernel? - constexpr int32_t iMode = (Mode == TrackletMode::Layer0Layer1) ? 0 : 1; - extern __shared__ int32_t ssum[]; - for (uint32_t rof = blockIdx.x; rof < (uint32_t)nRofs; rof += gridDim.x) { - const auto& GPUrestrict() currentNTracklets = getNTrackletsPerCluster(rof, nRofs, iMode, rofClusters, nTrackletsPerCluster); - int32_t localSum = 0; - for (uint32_t ci = threadIdx.x; ci < (uint32_t)currentNTracklets.size(); ci += blockDim.x) { - localSum += currentNTracklets[ci]; - } - ssum[threadIdx.x] = localSum; - __syncthreads(); - for (uint32_t stride = blockDim.x / 2; stride > 0; stride >>= 1) { - if (threadIdx.x < stride) { - ssum[threadIdx.x] += ssum[threadIdx.x + stride]; - } - __syncthreads(); - } - if (threadIdx.x == 0) { - nTrackletsPerROF[iMode][rof] = ssum[0]; - } - } -} - -template -GPUhi() void cubExclusiveScan(const T* GPUrestrict() in, T* GPUrestrict() out, int32_t num_items, cudaStream_t stream) -{ - void* d_temp_storage = nullptr; - size_t temp_storage_bytes = 0; - GPUChkErrS(cub::DeviceScan::InclusiveSum(d_temp_storage, temp_storage_bytes, in, out + 1, num_items, stream)); - GPUChkErrS(cudaMallocAsync(&d_temp_storage, temp_storage_bytes, stream)); - GPUChkErrS(cub::DeviceScan::InclusiveSum(d_temp_storage, temp_storage_bytes, in, out + 1, num_items, stream)); - GPUChkErrS(cudaFreeAsync(d_temp_storage, stream)); -} - -} // namespace gpu - -template -void countTrackletsInROFsHandler(const IndexTableUtils* GPUrestrict() utils, - const uint8_t* GPUrestrict() multMask, - const int32_t nRofs, - const int32_t deltaROF, - const int32_t* GPUrestrict() rofPV, - const int32_t vertPerRofThreshold, - const Cluster** GPUrestrict() clusters, - const uint32_t nClusters, - const int32_t** GPUrestrict() ROFClusters, - const uint8_t** GPUrestrict() usedClusters, - const int32_t** GPUrestrict() clustersIndexTables, - int32_t** GPUrestrict() trackletsPerClusterLUTs, - int32_t** GPUrestrict() trackletsPerClusterSumLUTs, - int32_t** GPUrestrict() trackletsPerROF, - const std::array& trackletsPerClusterLUTsHost, - const std::array& trackletsPerClusterSumLUTsHost, - const int32_t iteration, - const float phiCut, - const int32_t maxTrackletsPerCluster, - const int32_t nBlocks, - const int32_t nThreads, - gpu::Streams& streams) -{ - const uint32_t sharedBytes = nThreads * (2 * deltaROF + 1) * sizeof(uint16_t); - gpu::computeLayerTrackletMutliROFKernel<<>>(clusters, - ROFClusters, - usedClusters, - clustersIndexTables, - phiCut, - nullptr, - trackletsPerClusterLUTs, - utils, - nRofs, - deltaROF, - rofPV, - iteration, - vertPerRofThreshold, - maxTrackletsPerCluster); - gpu::compileTrackletsPerROFKernel<<>>(nRofs, trackletsPerROF, ROFClusters, (const int32_t**)trackletsPerClusterLUTs); - gpu::cubExclusiveScan(trackletsPerClusterLUTsHost[0], trackletsPerClusterSumLUTsHost[0], nClusters, streams[0].get()); - - gpu::computeLayerTrackletMutliROFKernel<<>>(clusters, - ROFClusters, - usedClusters, - clustersIndexTables, - phiCut, - nullptr, - trackletsPerClusterLUTs, - utils, - nRofs, - deltaROF, - rofPV, - iteration, - vertPerRofThreshold, - maxTrackletsPerCluster); - gpu::compileTrackletsPerROFKernel<<>>(nRofs, trackletsPerROF, ROFClusters, (const int**)trackletsPerClusterLUTs); - gpu::cubExclusiveScan(trackletsPerClusterLUTsHost[1], trackletsPerClusterSumLUTsHost[1], nClusters, streams[1].get()); -} - -template -void computeTrackletsInROFsHandler(const IndexTableUtils* GPUrestrict() utils, - const uint8_t* GPUrestrict() multMask, - const int32_t nRofs, - const int32_t deltaROF, - const int32_t* GPUrestrict() rofPV, - const int vertPerRofThreshold, - const Cluster** GPUrestrict() clusters, - const uint32_t nClusters, - const int32_t** GPUrestrict() ROFClusters, - const uint8_t** GPUrestrict() usedClusters, - const int32_t** GPUrestrict() clustersIndexTables, - Tracklet** GPUrestrict() foundTracklets, - const int32_t** GPUrestrict() trackletsPerClusterLUTs, - const int32_t** GPUrestrict() trackletsPerClusterSumLUTs, - const int32_t** GPUrestrict() trackletsPerROF, - const int32_t iteration, - const float phiCut, - const int32_t maxTrackletsPerCluster, - const int32_t nBlocks, - const int32_t nThreads, - gpu::Streams& streams) -{ - const uint32_t sharedBytes = nThreads * (2 * deltaROF + 1) * sizeof(uint16_t); - gpu::computeLayerTrackletMutliROFKernel<<>>(clusters, - ROFClusters, - usedClusters, - clustersIndexTables, - phiCut, - foundTracklets, - trackletsPerClusterSumLUTs, - utils, - nRofs, - deltaROF, - rofPV, - iteration, - vertPerRofThreshold, - maxTrackletsPerCluster); - gpu::computeLayerTrackletMutliROFKernel<<>>(clusters, - ROFClusters, - usedClusters, - clustersIndexTables, - phiCut, - foundTracklets, - trackletsPerClusterSumLUTs, - utils, - nRofs, - deltaROF, - rofPV, - iteration, - vertPerRofThreshold, - maxTrackletsPerCluster); -} - -void countTrackletsMatchingInROFsHandler(const int32_t nRofs, - const int32_t deltaROF, - const uint32_t nClusters, - const int32_t** GPUrestrict() ROFClusters, - const Cluster** GPUrestrict() clusters, - uint8_t** GPUrestrict() usedClusters, - const Tracklet** GPUrestrict() foundTracklets, - uint8_t* GPUrestrict() usedTracklets, - const int32_t** GPUrestrict() trackletsPerClusterLUTs, - const int32_t** GPUrestrict() trackletsPerClusterSumLUTs, - int32_t* GPUrestrict() linesPerClusterLUT, - int32_t* GPUrestrict() linesPerClusterSumLUT, - const int32_t iteration, - const float phiCut, - const float tanLambdaCut, - const int32_t nBlocks, - const int32_t nThreads, - gpu::Streams& streams) -{ - streams[1].sync(); // need to make sure that all tracklets are done, since this placed in 0 tracklet01 will be done but tracklet12 needs to be guaranteed - gpu::computeTrackletSelectionMutliROFKernel<<>>(nullptr, - usedClusters, - ROFClusters, - phiCut, - tanLambdaCut, - foundTracklets, - usedTracklets, - trackletsPerClusterLUTs, - trackletsPerClusterSumLUTs, - linesPerClusterLUT, - nullptr, - nRofs, - deltaROF, - 100); - gpu::cubExclusiveScan(linesPerClusterLUT, linesPerClusterSumLUT, nClusters, streams[0].get()); -} - -void computeTrackletsMatchingInROFsHandler(const int32_t nRofs, - const int32_t deltaROF, - const uint32_t nClusters, - const int32_t** GPUrestrict() ROFClusters, - const Cluster** GPUrestrict() clusters, - const uint8_t** GPUrestrict() usedClusters, - const Tracklet** GPUrestrict() foundTracklets, - uint8_t* GPUrestrict() usedTracklets, - const int32_t** GPUrestrict() trackletsPerClusterLUTs, - const int32_t** GPUrestrict() trackletsPerClusterSumLUTs, - const int32_t* GPUrestrict() linesPerClusterSumLUT, - Line* GPUrestrict() lines, - const int32_t iteration, - const float phiCut, - const float tanLambdaCut, - const int32_t nBlocks, - const int32_t nThreads, - gpu::Streams& streams) -{ - gpu::computeTrackletSelectionMutliROFKernel<<>>(clusters, - nullptr, - ROFClusters, - phiCut, - tanLambdaCut, - foundTracklets, - usedTracklets, - trackletsPerClusterLUTs, - trackletsPerClusterSumLUTs, - linesPerClusterSumLUT, - lines, - nRofs, - deltaROF, - 100); -} - -/// Explicit instantiation of ITS2 handlers -template void countTrackletsInROFsHandler<7>(const IndexTableUtils<7>* GPUrestrict() utils, - const uint8_t* GPUrestrict() multMask, - const int32_t nRofs, - const int32_t deltaROF, - const int32_t* GPUrestrict() rofPV, - const int32_t vertPerRofThreshold, - const Cluster** GPUrestrict() clusters, - const uint32_t nClusters, - const int32_t** GPUrestrict() ROFClusters, - const uint8_t** GPUrestrict() usedClusters, - const int32_t** GPUrestrict() clustersIndexTables, - int32_t** trackletsPerClusterLUTs, - int32_t** trackletsPerClusterSumLUTs, - int32_t** trackletsPerROF, - const std::array& trackletsPerClusterLUTsHost, - const std::array& trackletsPerClusterSumLUTsHost, - const int32_t iteration, - const float phiCut, - const int32_t maxTrackletsPerCluster, - const int32_t nBlocks, - const int32_t nThreads, - gpu::Streams& streams); - -template void computeTrackletsInROFsHandler<7>(const IndexTableUtils<7>* GPUrestrict() utils, - const uint8_t* GPUrestrict() multMask, - const int32_t nRofs, - const int32_t deltaROF, - const int32_t* GPUrestrict() rofPV, - const int vertPerRofThreshold, - const Cluster** GPUrestrict() clusters, - const uint32_t nClusters, - const int32_t** GPUrestrict() ROFClusters, - const uint8_t** GPUrestrict() usedClusters, - const int32_t** GPUrestrict() clustersIndexTables, - Tracklet** GPUrestrict() foundTracklets, - const int32_t** GPUrestrict() trackletsPerClusterLUTs, - const int32_t** GPUrestrict() trackletsPerClusterSumLUTs, - const int32_t** GPUrestrict() trackletsPerROF, - const int32_t iteration, - const float phiCut, - const int32_t maxTrackletsPerCluster, - const int32_t nBlocks, - const int32_t nThreads, - gpu::Streams& streams); -/* -GPUg() void lineClustererMultipleRof( - const int* sizeClustersL1, // Number of clusters on layer 1 per ROF - Line* lines, // Lines - int* nFoundLines, // Number of found lines - int* nExclusiveFoundLines, // Number of found lines exclusive scan - int* clusteredLines, // Clustered lines - const unsigned int startRofId, // Starting ROF ID - const unsigned int rofSize, // Number of ROFs to consider // Number of found lines exclusive scan - const float pairCut) // Selection on line pairs -{ - for (unsigned int iRof{threadIdx.x}; iRof < rofSize; iRof += blockDim.x) { - auto rof = iRof + startRofId; - auto clustersL1offsetRof = sizeClustersL1[rof] - sizeClustersL1[startRofId]; // starting cluster offset for this ROF - auto nClustersL1Rof = sizeClustersL1[rof + 1] - sizeClustersL1[rof]; // number of clusters for this ROF - auto linesOffsetRof = nExclusiveFoundLines[clustersL1offsetRof]; // starting line offset for this ROF - // auto* foundLinesRof = nFoundLines + clustersL1offsetRof; - auto nLinesRof = nExclusiveFoundLines[clustersL1offsetRof + nClustersL1Rof] - linesOffsetRof; - // printf("rof: %d -> %d lines.\n", rof, nLinesRof); - for (int iLine1 = 0; iLine1 < nLinesRof; ++iLine1) { - auto absLine1Index = nExclusiveFoundLines[clustersL1offsetRof] + iLine1; - if (clusteredLines[absLine1Index] > -1) { - continue; - } - for (int iLine2 = iLine1 + 1; iLine2 < nLinesRof; ++iLine2) { - auto absLine2Index = nExclusiveFoundLines[clustersL1offsetRof] + iLine2; - if (clusteredLines[absLine2Index] > -1) { - continue; - } - - if (Line::getDCA(lines[absLine1Index], lines[absLine2Index]) < pairCut) { - ClusterLinesGPU tmpClus{lines[absLine1Index], lines[absLine2Index]}; - float tmpVertex[3]; - tmpVertex[0] = tmpClus.getVertex()[0]; - tmpVertex[1] = tmpClus.getVertex()[1]; - tmpVertex[2] = tmpClus.getVertex()[2]; - if (tmpVertex[0] * tmpVertex[0] + tmpVertex[1] * tmpVertex[1] > 4.f) { // outside the beampipe, skip it - break; - } - clusteredLines[absLine1Index] = iLine1; // We set local index of first line to contribute, so we can retrieve the cluster later - clusteredLines[absLine2Index] = iLine1; - for (int iLine3 = 0; iLine3 < nLinesRof; ++iLine3) { - auto absLine3Index = nExclusiveFoundLines[clustersL1offsetRof] + iLine3; - if (clusteredLines[absLine3Index] > -1) { - continue; - } - if (Line::getDistanceFromPoint(lines[absLine3Index], tmpVertex) < pairCut) { - clusteredLines[absLine3Index] = iLine1; - } - } - break; - } - } - } - } // rof loop -} - -GPUg() void computeCentroidsKernel( - Line* lines, - int* nFoundLines, - int* nExclusiveFoundLines, - const unsigned int nClustersMiddleLayer, - float* centroids, - const float lowHistX, - const float highHistX, - const float lowHistY, - const float highHistY, - const float pairCut) -{ - const int nLines = nExclusiveFoundLines[nClustersMiddleLayer - 1] + nFoundLines[nClustersMiddleLayer - 1]; - const int maxIterations{nLines * (nLines - 1) / 2}; - for (unsigned int currentThreadIndex = blockIdx.x * blockDim.x + threadIdx.x; currentThreadIndex < maxIterations; currentThreadIndex += blockDim.x * gridDim.x) { - int iFirstLine = currentThreadIndex / nLines; - int iSecondLine = currentThreadIndex % nLines; - // All unique pairs - if (iSecondLine <= iFirstLine) { - iFirstLine = nLines - iFirstLine - 2; - iSecondLine = nLines - iSecondLine - 1; - } - if (Line::getDCA(lines[iFirstLine], lines[iSecondLine]) < pairCut) { - ClusterLinesGPU cluster{lines[iFirstLine], lines[iSecondLine]}; - if (cluster.getVertex()[0] * cluster.getVertex()[0] + cluster.getVertex()[1] * cluster.getVertex()[1] < 1.98f * 1.98f) { - // printOnThread(0, "xCentr: %f, yCentr: %f \n", cluster.getVertex()[0], cluster.getVertex()[1]); - centroids[2 * currentThreadIndex] = cluster.getVertex()[0]; - centroids[2 * currentThreadIndex + 1] = cluster.getVertex()[1]; - } else { - // write values outside the histogram boundaries, - // default behaviour is not to have them added to histogram later - // (writing zeroes would be problematic) - centroids[2 * currentThreadIndex] = 2 * lowHistX; - centroids[2 * currentThreadIndex + 1] = 2 * lowHistY; - } - } else { - // write values outside the histogram boundaries, - // default behaviour is not to have them added to histogram later - // (writing zeroes would be problematic) - centroids[2 * currentThreadIndex] = 2 * highHistX; - centroids[2 * currentThreadIndex + 1] = 2 * highHistY; - } - } -} - -GPUg() void computeZCentroidsKernel( - const int nLines, - const cub::KeyValuePair* tmpVtX, - float* beamPosition, - Line* lines, - float* centroids, - const int* histX, // X - const float lowHistX, - const float binSizeHistX, - const int nBinsHistX, - const int* histY, // Y - const float lowHistY, - const float binSizeHistY, - const int nBinsHistY, - const float lowHistZ, // Z - const float pairCut, - const int binOpeningX, - const int binOpeningY) -{ - for (unsigned int currentThreadIndex = blockIdx.x * blockDim.x + threadIdx.x; currentThreadIndex < nLines; currentThreadIndex += blockDim.x * gridDim.x) { - if (tmpVtX[0].value || tmpVtX[1].value) { - float tmpX{lowHistX + tmpVtX[0].key * binSizeHistX + binSizeHistX / 2}; - int sumWX{tmpVtX[0].value}; - float wX{tmpX * tmpVtX[0].value}; - for (int iBin{o2::gpu::GPUCommonMath::Max(0, tmpVtX[0].key - binOpeningX)}; iBin < o2::gpu::GPUCommonMath::Min(tmpVtX[0].key + binOpeningX + 1, nBinsHistX - 1); ++iBin) { - if (iBin != tmpVtX[0].key) { - wX += (lowHistX + iBin * binSizeHistX + binSizeHistX / 2) * histX[iBin]; - sumWX += histX[iBin]; - } - } - float tmpY{lowHistY + tmpVtX[1].key * binSizeHistY + binSizeHistY / 2}; - int sumWY{tmpVtX[1].value}; - float wY{tmpY * tmpVtX[1].value}; - for (int iBin{o2::gpu::GPUCommonMath::Max(0, tmpVtX[1].key - binOpeningY)}; iBin < o2::gpu::GPUCommonMath::Min(tmpVtX[1].key + binOpeningY + 1, nBinsHistY - 1); ++iBin) { - if (iBin != tmpVtX[1].key) { - wY += (lowHistY + iBin * binSizeHistY + binSizeHistY / 2) * histY[iBin]; - sumWY += histY[iBin]; - } - } - beamPosition[0] = wX / sumWX; - beamPosition[1] = wY / sumWY; - float mockBeamPoint1[3] = {beamPosition[0], beamPosition[1], -1}; // get two points laying at different z, to create line object - float mockBeamPoint2[3] = {beamPosition[0], beamPosition[1], 1}; - Line pseudoBeam = {mockBeamPoint1, mockBeamPoint2}; - if (Line::getDCA(lines[currentThreadIndex], pseudoBeam) < pairCut) { - ClusterLinesGPU cluster{lines[currentThreadIndex], pseudoBeam}; - centroids[currentThreadIndex] = cluster.getVertex()[2]; - } else { - centroids[currentThreadIndex] = 2 * lowHistZ; - } - } - } -} - -GPUg() void computeVertexKernel( - cub::KeyValuePair* tmpVertexBins, - int* histZ, // Z - const float lowHistZ, - const float binSizeHistZ, - const int nBinsHistZ, - Vertex* vertices, - float* beamPosition, - const int vertIndex, - const int minContributors, - const int binOpeningZ) -{ - for (unsigned int currentThreadIndex = blockIdx.x * blockDim.x + threadIdx.x; currentThreadIndex < binOpeningZ; currentThreadIndex += blockDim.x * gridDim.x) { - if (currentThreadIndex == 0) { - if (tmpVertexBins[2].value > 1 && (tmpVertexBins[0].value || tmpVertexBins[1].value)) { - float z{lowHistZ + tmpVertexBins[2].key * binSizeHistZ + binSizeHistZ / 2}; - float ex{0.f}; - float ey{0.f}; - float ez{0.f}; - int sumWZ{tmpVertexBins[2].value}; - float wZ{z * tmpVertexBins[2].value}; - for (int iBin{o2::gpu::GPUCommonMath::Max(0, tmpVertexBins[2].key - binOpeningZ)}; iBin < o2::gpu::GPUCommonMath::Min(tmpVertexBins[2].key + binOpeningZ + 1, nBinsHistZ - 1); ++iBin) { - if (iBin != tmpVertexBins[2].key) { - wZ += (lowHistZ + iBin * binSizeHistZ + binSizeHistZ / 2) * histZ[iBin]; - sumWZ += histZ[iBin]; - } - histZ[iBin] = 0; - } - if (sumWZ > minContributors || vertIndex == 0) { - new (vertices + vertIndex) Vertex{o2::math_utils::Point3D(beamPosition[0], beamPosition[1], wZ / sumWZ), std::array{ex, 0, ey, 0, 0, ez}, static_cast(sumWZ), 0}; - } else { - new (vertices + vertIndex) Vertex{}; - } - } else { - new (vertices + vertIndex) Vertex{}; - } - } - } -} -*/ -} // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt index a40aac491a386..e28fe04c06772 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/GPU/hip/CMakeLists.txt @@ -16,13 +16,9 @@ if(HIP_ENABLED) # 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 + SOURCES ../cuda/TimeFrameGPU.cu ../cuda/TrackerTraitsGPU.cxx - ../cuda/TracerGPU.cu ../cuda/TrackingKernels.cu - ../cuda/VertexingKernels.cu - ../cuda/VertexerTraitsGPU.cxx PUBLIC_INCLUDE_DIRECTORIES ../ PUBLIC_LINK_LIBRARIES O2::ITStracking O2::GPUTracking diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/BoundedAllocator.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/BoundedAllocator.h index 66634c1a07eea..91d5edeedcdb1 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/BoundedAllocator.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/BoundedAllocator.h @@ -97,6 +97,9 @@ class BoundedMemoryResource final : public std::pmr::memory_resource size_t getMaxMemory() const noexcept { return mMaxMemory; } void setMaxMemory(size_t max) { + if (max == mMaxMemory) { + return; + } size_t used = mUsedMemory.load(std::memory_order_acquire); if (used > max) { ++mCountThrow; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h index 902092a510eb0..d223adcef6214 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cell.h @@ -17,38 +17,19 @@ #define TRACKINGITSU_INCLUDE_CACELL_H_ #include "ITStracking/Constants.h" +#include "DataFormatsITS/TimeEstBC.h" +#include "ReconstructionDataFormats/Track.h" #include "GPUCommonDef.h" namespace o2::its { -class Cell final -{ - public: - GPUhd() int getFirstClusterIndex() const { return mFirstClusterIndex; }; - GPUhd() int getSecondClusterIndex() const { return mSecondClusterIndex; }; - GPUhd() int getThirdClusterIndex() const { return mThirdClusterIndex; }; - GPUhd() int getFirstTrackletIndex() const { return mFirstTrackletIndex; }; - GPUhd() int getSecondTrackletIndex() const { return mSecondTrackletIndex; }; - GPUhd() int getLevel() const { return mLevel; }; - GPUhd() void setLevel(const int level) { mLevel = level; }; - GPUhd() int* getLevelPtr() { return &mLevel; } - - private: - int mFirstClusterIndex{constants::UnusedIndex}; - int mSecondClusterIndex{constants::UnusedIndex}; - int mThirdClusterIndex{constants::UnusedIndex}; - int mFirstTrackletIndex{constants::UnusedIndex}; - int mSecondTrackletIndex{constants::UnusedIndex}; - int mLevel{constants::UnusedIndex}; -}; - -template +template class CellSeed final : public o2::track::TrackParCovF { public: GPUhdDefault() CellSeed() = default; - GPUhd() CellSeed(int innerL, int cl0, int cl1, int cl2, int trkl0, int trkl1, o2::track::TrackParCovF& tpc, float chi2) : o2::track::TrackParCovF(tpc), mChi2(chi2), mLevel(1) + GPUhd() CellSeed(int innerL, int cl0, int cl1, int cl2, int trkl0, int trkl1, o2::track::TrackParCovF& tpc, float chi2, const TimeEstBC& time) : o2::track::TrackParCovF(tpc), mChi2(chi2), mLevel(1), mTime(time) { mClusters.fill(constants::UnusedIndex); setUserField(innerL); @@ -81,20 +62,24 @@ class CellSeed final : public o2::track::TrackParCovF GPUhd() void printCell() const { printf("cell: %d, %d\t lvl: %d\t chi2: %f\tcls: [", mTracklets[0], mTracklets[1], mLevel, mChi2); - for (int i = 0; i < nLayers; ++i) { + for (int i = 0; i < NLayers; ++i) { printf("%d", mClusters[i]); - if (i < nLayers - 1) { + if (i < NLayers - 1) { printf(" | "); } } - printf("]\n"); + printf("]"); + printf(" ts: %u +/- %u\n", mTime.getTimeStamp(), mTime.getTimeStampError()); } + GPUhd() auto& getTimeStamp() noexcept { return mTime; } + GPUhd() const auto& getTimeStamp() const noexcept { return mTime; } private: float mChi2 = -999.f; int mLevel = constants::UnusedIndex; std::array mTracklets = constants::helpers::initArray(); - std::array mClusters = constants::helpers::initArray(); + std::array mClusters = constants::helpers::initArray(); + TimeEstBC mTime; }; } // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h index b96f0558943a6..34014d858648b 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Cluster.h @@ -19,6 +19,7 @@ #include #include "ITStracking/Constants.h" #include "GPUCommonRtypes.h" +#include "GPUCommonDef.h" namespace o2::its { @@ -71,8 +72,8 @@ struct TrackingFrameInfo final { float zCoordinate{-999.f}; float xTrackingFrame{-999.f}; float alphaTrackingFrame{-999.f}; - std::array positionTrackingFrame = {constants::UnusedIndex, constants::UnusedIndex}; - std::array covarianceTrackingFrame = {999., 999., 999.}; + std::array positionTrackingFrame = {-999.f, -999.f}; + std::array covarianceTrackingFrame = {-999.f, -999.f, -999.f}; ClassDefNV(TrackingFrameInfo, 1); }; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h index 0e7ad474ae455..6fbc6d7da7721 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h @@ -14,200 +14,77 @@ #include #include +#include +#include #include "ITStracking/Cluster.h" #include "ITStracking/Constants.h" #include "ITStracking/Tracklet.h" #include "GPUCommonRtypes.h" -#include "GPUCommonMath.h" namespace o2::its { + struct Line final { - GPUhdDefault() Line() = default; - GPUhd() Line(const Line&); - Line(std::array firstPoint, std::array secondPoint); - GPUhd() Line(const Tracklet&, const Cluster*, const Cluster*); +#if !defined(__HIPCC__) && !defined(__CUDACC__) // hide the class completely for gpu-cc + using SVector3f = ROOT::Math::SVector; + using SMatrix3f = ROOT::Math::SMatrix>; + + Line() = default; + Line(const Tracklet&, const Cluster*, const Cluster*); + bool operator==(const Line&) const = default; + static float getDistance2FromPoint(const Line& line, const std::array& point); static float getDistanceFromPoint(const Line& line, const std::array& point); - GPUhd() static float getDistanceFromPoint(const Line& line, const float point[3]); - static std::array getDCAComponents(const Line& line, const std::array point); - GPUhd() static void getDCAComponents(const Line& line, const float point[3], float destArray[6]); - GPUhd() static float getDCA(const Line&, const Line&, const float precision = constants::Tolerance); - static bool areParallel(const Line&, const Line&, const float precision = constants::Tolerance); - GPUhd() unsigned char isEmpty() const { return (originPoint[0] == 0.f && originPoint[1] == 0.f && originPoint[2] == 0.f) && - (cosinesDirector[0] == 0.f && cosinesDirector[1] == 0.f && cosinesDirector[2] == 0.f); } - GPUhdi() auto getDeltaROF() const { return rof[1] - rof[0]; } - GPUhd() void print() const; - bool operator==(const Line&) const; - bool operator!=(const Line&) const; - short getMinROF() const { return rof[0] < rof[1] ? rof[0] : rof[1]; } + static SMatrix3f getDCAComponents(const Line& line, const std::array& point); + static float getDCA2(const Line&, const Line&, const float precision = constants::Tolerance); + static float getDCA(const Line&, const Line&, const float precision = constants::Tolerance); + bool isEmpty() const noexcept; + void print() const; - float originPoint[3] = {0, 0, 0}; - float cosinesDirector[3] = {0, 0, 0}; - // float weightMatrix[6] = {1., 0., 0., 1., 0., 1.}; - // weightMatrix is a symmetric matrix internally stored as - // 0 --> row = 0, col = 0 - // 1 --> 0,1 - // 2 --> 0,2 - // 3 --> 1,1 - // 4 --> 1,2 - // 5 --> 2,2 - short rof[2] = {constants::UnusedIndex, constants::UnusedIndex}; + SVector3f originPoint; + SVector3f cosinesDirector; + TimeEstBC mTime; ClassDefNV(Line, 1); +#endif }; -GPUhdi() Line::Line(const Line& other) -{ - for (int i{0}; i < 3; ++i) { - originPoint[i] = other.originPoint[i]; - cosinesDirector[i] = other.cosinesDirector[i]; - } - // for (int i{0}; i < 6; ++i) { - // weightMatrix[i] = other.weightMatrix[i]; - // } - for (int i{0}; i < 2; ++i) { - rof[i] = other.rof[i]; - } -} - -GPUhdi() Line::Line(const Tracklet& tracklet, const Cluster* innerClusters, const Cluster* outerClusters) -{ - originPoint[0] = innerClusters[tracklet.firstClusterIndex].xCoordinate; - originPoint[1] = innerClusters[tracklet.firstClusterIndex].yCoordinate; - originPoint[2] = innerClusters[tracklet.firstClusterIndex].zCoordinate; - - cosinesDirector[0] = outerClusters[tracklet.secondClusterIndex].xCoordinate - innerClusters[tracklet.firstClusterIndex].xCoordinate; - cosinesDirector[1] = outerClusters[tracklet.secondClusterIndex].yCoordinate - innerClusters[tracklet.firstClusterIndex].yCoordinate; - cosinesDirector[2] = outerClusters[tracklet.secondClusterIndex].zCoordinate - innerClusters[tracklet.firstClusterIndex].zCoordinate; - - float inverseNorm{1.f / o2::gpu::CAMath::Hypot(cosinesDirector[0], cosinesDirector[1], cosinesDirector[2])}; - cosinesDirector[0] *= inverseNorm; - cosinesDirector[1] *= inverseNorm; - cosinesDirector[2] *= inverseNorm; - - rof[0] = tracklet.rof[0]; - rof[1] = tracklet.rof[1]; -} - -// static functions: -inline float Line::getDistanceFromPoint(const Line& line, const std::array& point) -{ - float DCASquared{0}; - float cdelta{0}; - for (int i{0}; i < 3; ++i) { - cdelta -= line.cosinesDirector[i] * (line.originPoint[i] - point[i]); - } - for (int i{0}; i < 3; ++i) { - DCASquared += (line.originPoint[i] - point[i] + line.cosinesDirector[i] * cdelta) * - (line.originPoint[i] - point[i] + line.cosinesDirector[i] * cdelta); - } - return o2::gpu::CAMath::Sqrt(DCASquared); -} - -GPUhdi() float Line::getDistanceFromPoint(const Line& line, const float point[3]) -{ - const float dx = point[0] - line.originPoint[0]; - const float dy = point[1] - line.originPoint[1]; - const float dz = point[2] - line.originPoint[2]; - const float d = (dx * line.cosinesDirector[0]) + (dy * line.cosinesDirector[1]) + (dz * line.cosinesDirector[2]); - - const float vx = dx - (d * line.cosinesDirector[0]); - const float vy = dy - (d * line.cosinesDirector[1]); - const float vz = dz - (d * line.cosinesDirector[2]); - - return o2::gpu::CAMath::Hypot(vx, vy, vz); -} - -GPUhdi() float Line::getDCA(const Line& firstLine, const Line& secondLine, const float precision) -{ - const float nx = (firstLine.cosinesDirector[1] * secondLine.cosinesDirector[2]) - - (firstLine.cosinesDirector[2] * secondLine.cosinesDirector[1]); - const float ny = -(firstLine.cosinesDirector[0] * secondLine.cosinesDirector[2]) + - (firstLine.cosinesDirector[2] * secondLine.cosinesDirector[0]); - const float nz = (firstLine.cosinesDirector[0] * secondLine.cosinesDirector[1]) - - (firstLine.cosinesDirector[1] * secondLine.cosinesDirector[0]); - const float norm2 = (nx * nx) + (ny * ny) + (nz * nz); - - if (norm2 <= precision * precision) { - return getDistanceFromPoint(firstLine, secondLine.originPoint); - } - - const float dx = secondLine.originPoint[0] - firstLine.originPoint[0]; - const float dy = secondLine.originPoint[1] - firstLine.originPoint[1]; - const float dz = secondLine.originPoint[2] - firstLine.originPoint[2]; - const float triple = (dx * nx) + (dy * ny) + (dz * nz); - - return o2::gpu::CAMath::Abs(triple) / o2::gpu::CAMath::Sqrt(norm2); -} - -GPUhdi() void Line::getDCAComponents(const Line& line, const float point[3], float destArray[6]) -{ - float cdelta{0.}; - for (int i{0}; i < 3; ++i) { - cdelta -= line.cosinesDirector[i] * (line.originPoint[i] - point[i]); - } - - destArray[0] = line.originPoint[0] - point[0] + line.cosinesDirector[0] * cdelta; - destArray[3] = line.originPoint[1] - point[1] + line.cosinesDirector[1] * cdelta; - destArray[5] = line.originPoint[2] - point[2] + line.cosinesDirector[2] * cdelta; - destArray[1] = o2::gpu::CAMath::Sqrt(destArray[0] * destArray[0] + destArray[3] * destArray[3]); - destArray[2] = o2::gpu::CAMath::Sqrt(destArray[0] * destArray[0] + destArray[5] * destArray[5]); - destArray[4] = o2::gpu::CAMath::Sqrt(destArray[3] * destArray[3] + destArray[5] * destArray[5]); -} - -inline bool Line::operator==(const Line& rhs) const -{ - bool val{false}; - for (int i{0}; i < 3; ++i) { - val &= this->originPoint[i] == rhs.originPoint[i]; - } - return val; -} - -inline bool Line::operator!=(const Line& rhs) const -{ - return !(*this == rhs); -} - -GPUhdi() void Line::print() const -{ - printf("Line: originPoint = (%f, %f, %f), cosinesDirector = (%f, %f, %f), rofs = (%hd, %hd)\n", - originPoint[0], originPoint[1], originPoint[2], cosinesDirector[0], cosinesDirector[1], cosinesDirector[2], rof[0], rof[1]); -} - class ClusterLines final { +#if !defined(__HIPCC__) && !defined(__CUDACC__) // hide the class completely for gpu-cc + using SMatrix3 = ROOT::Math::SMatrix>; + using SMatrix3f = ROOT::Math::SMatrix>; + using SVector3 = ROOT::Math::SVector; + public: ClusterLines() = default; - ClusterLines(const int firstLabel, const Line& firstLine, const int secondLabel, const Line& secondLine, - const bool weight = false); - ClusterLines(const Line& firstLine, const Line& secondLine); - void add(const int& lineLabel, const Line& line, const bool& weight = false); + ClusterLines(const int firstLabel, const Line& firstLine, const int secondLabel, const Line& secondLine); + void add(const int lineLabel, const Line& line); void computeClusterCentroid(); - void updateROFPoll(const Line&); - inline std::vector& getLabels() - { - return mLabels; - } - inline int getSize() const { return mLabels.size(); } - inline short getROF() const { return mROF; } - inline std::array getVertex() const { return mVertex; } - inline std::array getRMS2() const { return mRMS2; } - inline float getAvgDistance2() const { return mAvgDistance2; } - - bool operator==(const ClusterLines&) const; + void accumulate(const Line& line); + bool isValid() const noexcept { return mIsValid; } + auto const& getVertex() const { return mVertex; } + const float* getRMS2() const { return mRMS2.Array(); } + float getAvgDistance2() const { return mAvgDistance2; } + auto getSize() const noexcept { return mLabels.size(); } + auto& getLabels() noexcept { return mLabels; } + const auto& getTimeStamp() const noexcept { return mTime; } + bool operator==(const ClusterLines& rhs) const noexcept; + float getR2() const noexcept { return (mVertex[0] * mVertex[0]) + (mVertex[1] * mVertex[1]); } + float getR() const noexcept { return std::sqrt(getR2()); } protected: - std::array mAMatrix; // AX=B - std::array mBMatrix; // AX=B - std::vector mLabels; // labels - std::array mWeightMatrix = {0.f}; // weight matrix - std::array mVertex = {0.f}; // cluster centroid position - std::array mRMS2 = {0.f}; // symmetric matrix: diagonal is RMS2 - float mAvgDistance2 = 0.f; // substitute for chi2 - int mROFWeight = 0; // rof weight for voting - short mROF = constants::UnusedIndex; // rof + SMatrix3 mAMatrix; // AX=B, symmetric normal matrix + SVector3 mBMatrix; // AX=B, right-hand side + std::array mVertex = {}; // cluster centroid position + SMatrix3f mRMS2; // symmetric matrix: diagonal is RMS2 + float mAvgDistance2 = 0.f; // substitute for chi2 + bool mIsValid = false; // true if linear system was solved successfully + TimeEstBC mTime; // time stamp + std::vector mLabels; // contributing labels + + ClassDefNV(ClusterLines, 1); +#endif }; } // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h index 10e1681c73e8d..02dbeb8cf3992 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h @@ -16,9 +16,11 @@ #ifndef TRACKINGITSU_INCLUDE_CONFIGURATION_H_ #define TRACKINGITSU_INCLUDE_CONFIGURATION_H_ +#include #ifndef GPUCA_GPUCODE_DEVICE #include #include +#include #include #include #endif @@ -37,7 +39,7 @@ struct TrackingParameters { std::string asString() const; int NLayers = 7; - int DeltaROF = 0; + std::vector AddTimeError = {0, 0, 0, 0, 0, 0, 0}; std::vector LayerZ = {16.333f + 1, 16.333f + 1, 16.333f + 1, 42.140f + 1, 42.140f + 1, 73.745f + 1, 73.745f + 1}; std::vector LayerRadii = {2.33959f, 3.14076f, 3.91924f, 19.6213f, 24.5597f, 34.388f, 39.3329f}; std::vector LayerxX0 = {5.e-3f, 5.e-3f, 5.e-3f, 1.e-2f, 1.e-2f, 1.e-2f, 1.e-2f}; @@ -46,9 +48,9 @@ struct TrackingParameters { std::vector SystErrorZ2 = {0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f}; int ZBins{256}; int PhiBins{128}; - int nROFsPerIterations = -1; bool UseDiamond = false; float Diamond[3] = {0.f, 0.f, 0.f}; + float DiamondCov[6] = {25.e-6f, 0.f, 0.f, 25.e-6f, 0.f, 36.f}; /// General parameters bool AllowSharingFirstCluster = false; @@ -58,10 +60,8 @@ struct TrackingParameters { float PVres = 1.e-2f; /// Trackleting cuts float TrackletMinPt = 0.3f; - float TrackletsPerClusterLimit = 2.f; /// Cell finding cuts float CellDeltaTanLambdaSigma = 0.007f; - float CellsPerClusterLimit = 2.f; /// Fitter parameters o2::base::PropagatorImpl::MatCorrType CorrType = o2::base::PropagatorImpl::MatCorrType::USEMatCorrNONE; float MaxChi2ClusterAttachment = 60.f; @@ -71,18 +71,10 @@ struct TrackingParameters { uint16_t StartLayerMask = 0x7F; bool RepeatRefitOut = false; // repeat outward refit using inward refit as a seed bool ShiftRefToCluster = true; // TrackFit: after update shift the linearization reference to cluster - bool FindShortTracks = false; bool PerPrimaryVertexProcessing = false; bool SaveTimeBenchmarks = false; bool DoUPCIteration = false; bool FataliseUponFailure = true; - /// Cluster attachment - bool UseTrackFollower = false; - bool UseTrackFollowerTop = false; - bool UseTrackFollowerBot = false; - bool UseTrackFollowerMix = false; - float TrackFollowerNSigmaCutZ = 1.f; - float TrackFollowerNSigmaCutPhi = 1.f; bool createArtefactLabels{false}; @@ -94,14 +86,11 @@ struct TrackingParameters { struct VertexingParameters { std::string asString() const; - int nIterations = 1; // Number of vertexing passes to perform - int vertPerRofThreshold = 0; // Maximum number of vertices per ROF to trigger second a round - bool allowSingleContribClusters = false; + int nIterations = 1; // Number of vertexing passes to perform std::vector LayerZ = {16.333f + 1, 16.333f + 1, 16.333f + 1, 42.140f + 1, 42.140f + 1, 73.745f + 1, 73.745f + 1}; std::vector LayerRadii = {2.33959f, 3.14076f, 3.91924f, 19.6213f, 24.5597f, 34.388f, 39.3329f}; int ZBins{1}; int PhiBins{128}; - int deltaRof = 0; float zCut = 0.002f; float phiCut = 0.005f; float pairCut = 0.04f; @@ -120,7 +109,6 @@ struct VertexingParameters { bool SaveTimeBenchmarks = false; bool useTruthSeeding = false; // overwrite found vertices with MC events - bool outputContLabels = false; int nThreads = 1; bool PrintMemory = false; // print allocator usage in epilog report @@ -128,26 +116,6 @@ struct VertexingParameters { bool DropTFUponFailure = false; }; -struct TimeFrameGPUParameters { - std::string asString() const; - - size_t tmpCUBBufferSize = 1e5; // In average in pp events there are required 4096 bytes - size_t maxTrackletsPerCluster = 1e2; - size_t clustersPerLayerCapacity = 2.5e5; - size_t clustersPerROfCapacity = 1.5e3; - size_t validatedTrackletsCapacity = 1e3; - size_t cellsLUTsize = validatedTrackletsCapacity; - size_t maxNeighboursSize = 1e2; - size_t neighboursLUTsize = maxNeighboursSize; - size_t maxRoadPerRofSize = 1e3; // pp! - size_t maxLinesCapacity = 1e2; - size_t maxVerticesCapacity = 5e4; - size_t nMaxROFs = 1e3; - size_t nTimeFrameChunks = 3; - size_t nROFsPerChunk = 768; // pp defaults - int maxGPUMemoryGB = -1; -}; - namespace TrackingMode { enum Type : int8_t { diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h index 22642f2e23229..4b2528b62f057 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h @@ -19,7 +19,7 @@ #include #include -#include "ITStracking/Definitions.h" +#include "GPUCommonDef.h" #include "GPUCommonDefAPI.h" namespace o2::its::constants diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Definitions.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Definitions.h index c3be0de2dade7..8dadf826aa80a 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Definitions.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Definitions.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. // @@ -16,16 +16,7 @@ #define TRACKINGITS_DEFINITIONS_H_ #include - -#include "ReconstructionDataFormats/Vertex.h" - -#ifdef CA_DEBUG -#define CA_DEBUGGER(x) x -#else -#define CA_DEBUGGER(x) \ - do { \ - } while (0) -#endif +#include namespace o2::its { @@ -35,11 +26,30 @@ enum class TrackletMode { Layer1Layer2 = 2 }; -using Vertex = o2::dataformats::Vertex>; - template using maybe_const = typename std::conditional::type; +// simple implemnetion of logging with exp. backoff +struct LogLogThrottler { + uint64_t evCount{0}; + uint64_t nextLog{1}; + int32_t iteration{-1}; + int32_t layer{-1}; + bool needToLog(int32_t iter, int32_t lay) + { + if (iteration != iter || layer != lay) { + iteration = iter; + layer = lay; + evCount = 0; + nextLog = 1; + } + if (++evCount > nextLog) { + nextLog *= 2; + return true; + } + return false; + } +}; } // namespace o2::its -#endif +#endif \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEst.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEst.h new file mode 100644 index 0000000000000..3083a8fe9c2ec --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEst.h @@ -0,0 +1,93 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file FastMultEst.h +/// \brief Fast multiplicity estimator for ITS +/// \author ruben.shahoyan@cern.ch + +#ifndef ALICEO2_ITS_FASTMULTEST_ +#define ALICEO2_ITS_FASTMULTEST_ + +#include "ITSMFTReconstruction/ChipMappingITS.h" +#include "DataFormatsITS/Vertex.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITSMFT/PhysTrigger.h" +#include "ITStracking/FastMultEstConfig.h" +#include "ITStracking/ROFLookupTables.h" +#include +#include + +namespace o2::its +{ + +struct FastMultEst { + + static constexpr int NLayers = o2::itsmft::ChipMappingITS::NLayers; + using ROFOverlapTableN = ROFOverlapTable; + using ROFMaskTableN = ROFMaskTable; + + float mult = 0.; /// estimated signal clusters multiplicity on the selected multiplicity layer + float noisePerChip = 0.; /// imposed noise per chip (when enabled by configuration) + float cov[3] = {0.}; /// retained for compatibility; set to zero in single-layer mode + float chi2 = 0.; /// retained for compatibility; set to zero in single-layer mode + int nLayersUsed = 0; /// number of layers used by estimator (0/1 in single-layer mode) + uint32_t lastRandomSeed = 0; /// state of the gRandom before + FastMultEst(); + + static uint32_t getCurrentRandomSeed(); + int selectROFs(const std::array, NLayers>& rofs, + const std::array, NLayers>& clus, + const gsl::span trig, + uint32_t firstTForbit, + bool doStaggering, + const ROFOverlapTableN::View& overlapView, + ROFMaskTableN& sel); + void selectROFsWithVertices(const auto& vertices, const ROFOverlapTableN::View& overlapView, ROFMaskTableN& sel) const + { + const auto& multEstConf = FastMultEstConfig::Instance(); + if (!multEstConf.isVtxMultCutRequested()) { + return; + } + + for (const auto& vertex : vertices) { + if (!multEstConf.isPassingVtxMultCut(vertex.getNContributors())) { + const auto& timestamp{vertex.getTimeStamp()}; + for (int layer = 0; layer < NLayers; ++layer) { + uint32_t startROF = sel.getLayer(layer).getROF(timestamp.lower()); + uint32_t endROF = sel.getLayer(layer).getROF(timestamp.upper()); + for (uint32_t rof = startROF; rof <= endROF; ++rof) { + sel.setROFsEnabled(layer, rof, 0); + } + } + } + } + } + + int countClustersOnLayer(const gsl::span& clusters) const; + float process(int nClusters) + { + return FastMultEstConfig::Instance().imposeNoisePerChip > 0 ? processNoiseImposed(nClusters) : processNoiseFree(nClusters); + } + float processNoiseFree(int nClusters); + float processNoiseImposed(int nClusters); + float process(const gsl::span& clusters) + { + return process(countClustersOnLayer(clusters)); + } + static bool sSeedSet; + + ClassDefNV(FastMultEst, 1); +}; + +} // namespace o2::its + +#endif diff --git a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEstConfig.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEstConfig.h similarity index 58% rename from Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEstConfig.h rename to Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEstConfig.h index c6bce50995a4b..1ab9796aa8cf6 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEstConfig.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEstConfig.h @@ -20,9 +20,7 @@ #include "CommonUtils/ConfigurableParamHelper.h" #include "ITSMFTReconstruction/ChipMappingITS.h" -namespace o2 -{ -namespace its +namespace o2::its { struct FastMultEstConfig : public o2::conf::ConfigurableParamHelper { static constexpr int NLayers = o2::itsmft::ChipMappingITS::NLayers; @@ -34,16 +32,19 @@ struct FastMultEstConfig : public o2::conf::ConfigurableParamHelper0 : 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; }; + int cutMultClusLayer = NLayers - 1; /// layer used for cluster multiplicity selection (by default the outermost one) + 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 noexcept { return cutMultClusLow >= 0.f && cutMultClusHigh > 0.f; }; + bool isVtxMultCutRequested() const noexcept { return cutMultVtxLow >= 0.f && cutMultVtxHigh > 0.f; }; + bool isRandCutRequested() const noexcept { return cutRandomFraction >= 0.; } + bool isRequested() const noexcept { return isMultCutRequested() || isVtxMultCutRequested() || isRandCutRequested(); } 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); } @@ -51,7 +52,6 @@ struct FastMultEstConfig : public o2::conf::ConfigurableParamHelper z2) ? -1.f : 1.f) * o2::constants::math::VeryBig; @@ -91,11 +89,16 @@ GPUhdi() float smallestAngleDifference(float a, float b) return o2::gpu::CAMath::Remainderf(b - a, o2::constants::math::TwoPI); } -GPUhdi() float Sq(float v) +GPUhdi() constexpr float Sq(float v) { return v * v; } +GPUhdi() constexpr float SqDiff(float x, float y) +{ + return Sq(x - y); +} + GPUhdi() float MSangle(float mass, float p, float xX0) { float beta = p / o2::gpu::CAMath::Hypot(mass, p); diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ROFLookupTables.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ROFLookupTables.h new file mode 100644 index 0000000000000..ce20169e36c64 --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ROFLookupTables.h @@ -0,0 +1,850 @@ +// 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. + +#ifndef TRACKINGITSU_INCLUDE_ROFOVERLAPTABLE_H_ +#define TRACKINGITSU_INCLUDE_ROFOVERLAPTABLE_H_ + +#include +#include +#include +#include +#include +#include + +#ifndef GPUCA_GPUCODE +#include +#include "Framework/Logger.h" +#endif + +#include "CommonConstants/LHCConstants.h" +#include "CommonDataFormat/RangeReference.h" +#include "DataFormatsITS/TimeEstBC.h" +#include "DataFormatsITS/Vertex.h" +#include "GPUCommonMath.h" +#include "GPUCommonDef.h" + +namespace o2::its +{ + +// Layer timing definition +struct LayerTiming { + using BCType = TimeStampType; + BCType mNROFsTF{0}; // number of ROFs per timeframe + BCType mROFLength{0}; // ROF length in BC + BCType mROFDelay{0}; // delay of ROFs wrt start of first orbit in TF in BC + BCType mROFBias{0}; // bias wrt to the LHC clock in BC + BCType mROFAddTimeErr{0}; // additionally imposed uncertainty on ROF time in BC + + // return start of ROF in BC + // this does not account for the opt. error! + GPUhdi() BCType getROFStartInBC(BCType rofId) const noexcept + { + assert(rofId < mNROFsTF && rofId >= 0); + return (mROFLength * rofId) + mROFDelay + mROFBias; + } + + // return end of ROF in BCs + // this does not account for the opt. error! + GPUhdi() BCType getROFEndInBC(BCType rofId) const noexcept + { + assert(rofId < mNROFsTF); + return getROFStartInBC(rofId) + mROFLength; + } + + // return (clamped) time-interval of rof + GPUhdi() TimeEstBC getROFTimeBounds(BCType rofId, bool withError = false) const noexcept + { + if (withError) { + int64_t start = getROFStartInBC(rofId); + int64_t end = getROFEndInBC(rofId); + start = o2::gpu::CAMath::Max(start - mROFAddTimeErr, int64_t(0)); + end += mROFAddTimeErr; + return {static_cast(start), static_cast(end - start)}; + } + return {getROFStartInBC(rofId), static_cast(mROFLength)}; + } + + // return which ROF this BC belongs to + GPUhi() BCType getROF(BCType bc) const noexcept + { + const BCType offset = mROFDelay + mROFBias; + if (bc <= offset) { + return 0; + } + return (bc - offset) / mROFLength; + } + + // return which ROF this timestamp belongs by its lower edge + GPUhi() BCType getROF(TimeStamp ts) const noexcept + { + const BCType offset = mROFDelay + mROFBias; + const BCType bc = (ts.getTimeStamp() < ts.getTimeStampError()) ? BCType(0) : static_cast(o2::gpu::CAMath::Floor(ts.getTimeStamp() - ts.getTimeStampError())); + if (bc <= offset) { + return 0; + } + return (bc - offset) / mROFLength; + } + +#ifndef GPUCA_GPUCODE + GPUh() std::string asString() const + { + return std::format("NROFsPerTF {:4} ROFLength {:4} ({:4} per Orbit) ROFDelay {:4} ROFBias {:4} ROFAddTimeErr {:4}", mNROFsTF, mROFLength, (o2::constants::lhc::LHCMaxBunches / mROFLength), mROFDelay, mROFBias, mROFAddTimeErr); + } + + GPUh() void print() const + { + LOG(info) << asString(); + } +#endif +}; + +// Base class for lookup to define layers +template +class LayerTimingBase +{ + protected: + LayerTiming mLayers[NLayers]; + + public: + using T = LayerTiming::BCType; + LayerTimingBase() = default; + + GPUh() void defineLayer(int32_t layer, T nROFsTF, T rofLength, T rofDelay, T rofBias, T rofTE) + { + assert(layer >= 0 && layer < NLayers); + mLayers[layer] = {nROFsTF, rofLength, rofDelay, rofBias, rofTE}; + } + + GPUh() void defineLayer(int32_t layer, const LayerTiming& timing) + { + assert(layer >= 0 && layer < NLayers); + mLayers[layer] = timing; + } + + GPUhdi() const LayerTiming& getLayer(int32_t layer) const + { + assert(layer >= 0 && layer < NLayers); + return mLayers[layer]; + } + + GPUhdi() constexpr int32_t getEntries() noexcept { return NLayers; } + +#ifndef GPUCA_GPUCODE + GPUh() void print() const + { + LOGP(info, "Imposed time structure:"); + for (int32_t iL{0}; iL < NLayers; ++iL) { + LOGP(info, "\tLayer:{} {}", iL, mLayers[iL].asString()); + } + } +#endif +}; + +// GPU friendly view of the table below +template +struct ROFOverlapTableView { + const TableEntry* mFlatTable{nullptr}; + const TableIndex* mIndices{nullptr}; + const LayerTiming* mLayers{nullptr}; + + GPUhdi() const LayerTiming& getLayer(int32_t layer) const noexcept + { + assert(layer >= 0 && layer < NLayers); + return mLayers[layer]; + } + + GPUh() int32_t getClock() const noexcept + { + // we take the fastest layer as clock + int32_t fastest = 0; + uint32_t maxNROFs{0}; + for (int32_t iL{0}; iL < NLayers; ++iL) { + const auto& layer = getLayer(iL); + // by definition the fastest layer has the most ROFs + // this also solves the problem of a delay large than ROFLength + // if mNROFsTF is correct + if (layer.mNROFsTF > maxNROFs) { + fastest = iL; + maxNROFs = layer.mNROFsTF; + } + } + return fastest; + } + + GPUh() const LayerTiming& getClockLayer() const noexcept + { + return mLayers[getClock()]; + } + + GPUhdi() const TableEntry& getOverlap(int32_t from, int32_t to, size_t rofIdx) const noexcept + { + assert(from < NLayers && to < NLayers); + const size_t linearIdx = (from * NLayers) + to; + const auto& idx = mIndices[linearIdx]; + assert(rofIdx < idx.getEntries()); + return mFlatTable[idx.getFirstEntry() + rofIdx]; + } + + GPUhdi() bool doROFsOverlap(int32_t layer0, size_t rof0, int32_t layer1, size_t rof1) const noexcept + { + if (layer0 == layer1) { // layer is compatible with itself + return rof0 == rof1; + } + + assert(layer0 < NLayers && layer1 < NLayers); + const size_t linearIdx = (layer0 * NLayers) + layer1; + const auto& idx = mIndices[linearIdx]; + + if (rof0 >= idx.getEntries()) { + return false; + } + + const auto& overlap = mFlatTable[idx.getFirstEntry() + rof0]; + + if (overlap.getEntries() == 0) { + return false; + } + + const size_t firstCompatible = overlap.getFirstEntry(); + const size_t lastCompatible = firstCompatible + overlap.getEntries() - 1; + return rof1 >= firstCompatible && rof1 <= lastCompatible; + } + + GPUhdi() TimeEstBC getTimeStamp(int32_t layer0, size_t rof0, int32_t layer1, size_t rof1) const noexcept + { + assert(layer0 < NLayers && layer1 < NLayers); + assert(doROFsOverlap(layer0, rof0, layer1, rof1)); + // retrieves the combined timestamp + // e.g., taking one cluster from rof0 and one from rof1 + // and constructing a tracklet (doublet) what is its time + // this assumes that the rofs overlap, e.g. doROFsOverlap -> true + // get timestamp including margins from rof0 and rof1 + const auto t0 = mLayers[layer0].getROFTimeBounds(rof0, true); + const auto t1 = mLayers[layer1].getROFTimeBounds(rof1, true); + return t0 + t1; + } + +#ifndef GPUCA_GPUCODE + /// Print functions + GPUh() void printAll() const + { + for (int32_t i = 0; i < NLayers; ++i) { + for (int32_t j = 0; j < NLayers; ++j) { + if (i != j) { + printMapping(i, j); + } + } + } + printSummary(); + } + + GPUh() void printMapping(int32_t from, int32_t to) const + { + if (from == to) { + LOGP(error, "No self-lookup supported"); + return; + } + + constexpr int w_index = 10; + constexpr int w_first = 12; + constexpr int w_last = 12; + constexpr int w_count = 10; + + LOGF(info, "Overlap mapping: Layer %d -> Layer %d", from, to); + LOGP(info, "From: {}", mLayers[from].asString()); + LOGP(info, "To : {}", mLayers[to].asString()); + LOGF(info, "%*s | %*s | %*s | %*s", w_index, "ROF.index", w_first, "First.ROF", w_last, "Last.ROF", w_count, "Count"); + LOGF(info, "%.*s-+-%.*s-+-%.*s-+-%.*s", w_index, "----------", w_first, "------------", w_last, "------------", w_count, "----------"); + + const size_t linearIdx = (from * NLayers) + to; + const auto& idx = mIndices[linearIdx]; + for (int32_t i = 0; i < idx.getEntries(); ++i) { + const auto& overlap = getOverlap(from, to, i); + LOGF(info, "%*d | %*d | %*d | %*d", w_index, i, w_first, overlap.getFirstEntry(), w_last, overlap.getEntriesBound() - 1, w_count, overlap.getEntries()); + } + } + + GPUh() void printSummary() const + { + uint32_t totalEntries{0}; + size_t flatTableSize{0}; + + for (int32_t i = 0; i < NLayers; ++i) { + for (int32_t j = 0; j < NLayers; ++j) { + if (i != j) { + const size_t linearIdx = (i * NLayers) + j; + const auto& idx = mIndices[linearIdx]; + totalEntries += idx.getEntries(); + flatTableSize += idx.getEntries(); + } + } + } + + for (int32_t i = 0; i < NLayers; ++i) { + mLayers[i].print(); + } + + const uint32_t totalBytes = (flatTableSize * sizeof(TableEntry)) + (static_cast(NLayers * NLayers) * sizeof(TableIndex)); + LOGF(info, "------------------------------------------------------------"); + LOGF(info, "Total overlap table size: %u entries", totalEntries); + LOGF(info, "Flat table size: %zu entries", flatTableSize); + LOGF(info, "Total view size: %u bytes", totalBytes); + LOGF(info, "------------------------------------------------------------"); + } +#endif +}; + +// Precalculated lookup table to find overlapping ROFs in another layer given a ROF index in the current layer +template +class ROFOverlapTable : public LayerTimingBase +{ + public: + using T = LayerTimingBase::T; + using TableEntry = dataformats::RangeReference; + using TableIndex = dataformats::RangeReference; + + using View = ROFOverlapTableView; + ROFOverlapTable() = default; + + GPUh() void init() + { + std::vector table[NLayers][NLayers]; + for (int32_t i{0}; i < NLayers; ++i) { + for (int32_t j{0}; j < NLayers; ++j) { + if (i != j) { // we do not need self-lookup + buildMapping(i, j, table[i][j]); + } + } + } + flatten(table); + } + + GPUh() View getView() const + { + View view; + view.mFlatTable = mFlatTable.data(); + view.mIndices = mIndices; + view.mLayers = this->mLayers; + return view; + } + + GPUh() View getDeviceView(const TableEntry* deviceFlatTablePtr, const TableIndex* deviceIndicesPtr, const LayerTiming* deviceLayerTimingPtr) const + { + View view; + view.mFlatTable = deviceFlatTablePtr; + view.mIndices = deviceIndicesPtr; + view.mLayers = deviceLayerTimingPtr; + return view; + } + + GPUh() size_t getFlatTableSize() const noexcept { return mFlatTable.size(); } + static GPUh() constexpr size_t getIndicesSize() { return static_cast(NLayers * NLayers); } + + private: + GPUh() void buildMapping(int32_t from, int32_t to, std::vector& table) + { + const auto& layerFrom = this->mLayers[from]; + const auto& layerTo = this->mLayers[to]; + table.resize(layerFrom.mNROFsTF); + + for (int32_t iROF{0}; iROF < layerFrom.mNROFsTF; ++iROF) { + int64_t fromStart = o2::gpu::CAMath::Max((int64_t)layerFrom.getROFStartInBC(iROF) - (int64_t)layerFrom.mROFAddTimeErr, int64_t(0)); + int64_t fromEnd = (int64_t)layerFrom.getROFEndInBC(iROF) + layerFrom.mROFAddTimeErr; + + int32_t firstROFTo = o2::gpu::CAMath::Max(0, (int32_t)((fromStart - (int64_t)layerTo.mROFAddTimeErr - (int64_t)layerTo.mROFDelay - (int64_t)layerTo.mROFBias) / (int64_t)layerTo.mROFLength)); + auto lastROFTo = (int32_t)((fromEnd + (int64_t)layerTo.mROFAddTimeErr - (int64_t)layerTo.mROFDelay - (int64_t)layerTo.mROFBias - 1) / (int64_t)layerTo.mROFLength); + firstROFTo = o2::gpu::CAMath::Max(0, firstROFTo); + lastROFTo = o2::gpu::CAMath::Min((int32_t)layerTo.mNROFsTF - 1, lastROFTo); + + while (firstROFTo <= lastROFTo) { + int64_t toStart = o2::gpu::CAMath::Max((int64_t)layerTo.getROFStartInBC(firstROFTo) - (int64_t)layerTo.mROFAddTimeErr, int64_t(0)); + int64_t toEnd = (int64_t)layerTo.getROFEndInBC(firstROFTo) + layerTo.mROFAddTimeErr; + if (toEnd > fromStart && toStart < fromEnd) { + break; + } + ++firstROFTo; + } + while (lastROFTo >= firstROFTo) { + int64_t toStart = o2::gpu::CAMath::Max((int64_t)layerTo.getROFStartInBC(lastROFTo) - (int64_t)layerTo.mROFAddTimeErr, int64_t(0)); + int64_t toEnd = (int64_t)layerTo.getROFEndInBC(lastROFTo) + layerTo.mROFAddTimeErr; + if (toEnd > fromStart && toStart < fromEnd) { + break; + } + --lastROFTo; + } + int32_t count = (firstROFTo <= lastROFTo) ? (lastROFTo - firstROFTo + 1) : 0; + table[iROF] = {static_cast(firstROFTo), static_cast(count)}; + } + } + + GPUh() void flatten(const std::vector table[NLayers][NLayers]) + { + size_t total{0}; + for (int32_t i{0}; i < NLayers; ++i) { + for (int32_t j{0}; j < NLayers; ++j) { + if (i != j) { // we do not need self-lookup + total += table[i][j].size(); + } + } + } + + mFlatTable.reserve(total); + + for (int32_t i{0}; i < NLayers; ++i) { + for (int32_t j{0}; j < NLayers; ++j) { + size_t idx = (i * NLayers) + j; + if (i != j) { + mIndices[idx].setFirstEntry(static_cast(mFlatTable.size())); + mIndices[idx].setEntries(static_cast(table[i][j].size())); + mFlatTable.insert(mFlatTable.end(), table[i][j].begin(), table[i][j].end()); + } else { + mIndices[idx] = {0, 0}; + } + } + } + } + + TableIndex mIndices[NLayers * NLayers]; + std::vector mFlatTable; +}; + +// GPU friendly view of the table below +template +struct ROFVertexLookupTableView { + const TableEntry* mFlatTable{nullptr}; + const TableIndex* mIndices{nullptr}; + const LayerTiming* mLayers{nullptr}; + + GPUhdi() const LayerTiming& getLayer(int32_t layer) const noexcept + { + assert(layer >= 0 && layer < NLayers); + return mLayers[layer]; + } + + GPUhdi() const TableEntry& getVertices(int32_t layer, size_t rofIdx) const noexcept + { + assert(layer < NLayers); + const auto& idx = mIndices[layer]; + assert(rofIdx < idx.getEntries()); + return mFlatTable[idx.getFirstEntry() + rofIdx]; + } + + GPUh() int32_t getMaxVerticesPerROF() const noexcept + { + int32_t maxCount = 0; + for (int32_t layer = 0; layer < NLayers; ++layer) { + const auto& idx = mIndices[layer]; + for (int32_t i = 0; i < idx.getEntries(); ++i) { + const auto& entry = mFlatTable[idx.getFirstEntry() + i]; + maxCount = o2::gpu::CAMath::Max(maxCount, static_cast(entry.getEntries())); + } + } + return maxCount; + } + + // Check if a specific vertex is compatible with a given ROF + GPUhdi() bool isVertexCompatible(int32_t layer, size_t rofIdx, const Vertex& vertex) const noexcept + { + assert(layer < NLayers); + const auto& layerDef = mLayers[layer]; + int64_t rofLower = o2::gpu::CAMath::Max((int64_t)layerDef.getROFStartInBC(rofIdx) - (int64_t)layerDef.mROFAddTimeErr, int64_t(0)); + int64_t rofUpper = (int64_t)layerDef.getROFEndInBC(rofIdx) + layerDef.mROFAddTimeErr; + auto vLower = (int64_t)vertex.getTimeStamp().lower(); + auto vUpper = (int64_t)vertex.getTimeStamp().upper(); + return vUpper >= rofLower && vLower < rofUpper; + } + +#ifndef GPUCA_GPUCODE + GPUh() void printAll() const + { + for (int32_t i = 0; i < NLayers; ++i) { + printLayer(i); + } + printSummary(); + } + + GPUh() void printLayer(int32_t layer) const + { + constexpr int w_rof = 10; + constexpr int w_first = 12; + constexpr int w_last = 12; + constexpr int w_count = 10; + + LOGF(info, "Vertex lookup: Layer %d", layer); + LOGF(info, "%*s | %*s | %*s | %*s", w_rof, "ROF.index", w_first, "First.Vtx", w_last, "Last.Vtx", w_count, "Count"); + LOGF(info, "%.*s-+-%.*s-+-%.*s-+-%.*s", w_rof, "----------", w_first, "------------", w_last, "------------", w_count, "----------"); + + const auto& idx = mIndices[layer]; + for (int32_t i = 0; i < idx.getEntries(); ++i) { + const auto& entry = mFlatTable[idx.getFirstEntry() + i]; + int first = entry.getFirstEntry(); + int count = entry.getEntries(); + int last = first + count - 1; + LOGF(info, "%*d | %*d | %*d | %*d", w_rof, i, w_first, first, w_last, last, w_count, count); + } + } + + GPUh() void printSummary() const + { + uint32_t totalROFs{0}; + uint32_t totalVertexRefs{0}; + + for (int32_t i = 0; i < NLayers; ++i) { + const auto& idx = mIndices[i]; + totalROFs += idx.getEntries(); + + for (int32_t j = 0; j < idx.getEntries(); ++j) { + const auto& entry = mFlatTable[idx.getFirstEntry() + j]; + totalVertexRefs += entry.getEntries(); + } + } + + const uint32_t totalBytes = (totalROFs * sizeof(TableEntry)) + (NLayers * sizeof(TableIndex)); + LOGF(info, "------------------------------------------------------------"); + LOGF(info, "Total ROFs in table: %u", totalROFs); + LOGF(info, "Total vertex references: %u", totalVertexRefs); + LOGF(info, "Total view size: %u bytes", totalBytes); + LOGF(info, "------------------------------------------------------------"); + } +#endif +}; + +// Precalculated lookup table to find vertices compatible with ROFs +// Given a layer and ROF index, returns the range of vertices that overlap in time. +// The vertex time is defined as symmetrical [t0-e,t0+e] +// It needs to be guaranteed that the input vertices are sorted by their lower-bound! +// additionally compatibliyty has to be queried per vertex! +template +class ROFVertexLookupTable : public LayerTimingBase +{ + public: + using T = LayerTimingBase::T; + using BCType = LayerTiming::BCType; + using TableEntry = dataformats::RangeReference; + using TableIndex = dataformats::RangeReference; + using View = ROFVertexLookupTableView; + + ROFVertexLookupTable() = default; + + GPUh() size_t getFlatTableSize() const noexcept { return mFlatTable.size(); } + static GPUh() constexpr size_t getIndicesSize() { return NLayers; } + + // Build the lookup table given a sorted array of vertices + // vertices must be sorted by timestamp, then by error (secondary) + GPUh() void init(const Vertex* vertices, size_t nVertices) + { + if (nVertices > std::numeric_limits::max()) { + LOGF(fatal, "too many vertices %zu, max supported is %u", nVertices, std::numeric_limits::max()); + } + + std::vector table[NLayers]; + for (int32_t layer{0}; layer < NLayers; ++layer) { + buildMapping(layer, vertices, nVertices, table[layer]); + } + flatten(table); + } + + // Pre-allocated needed memory, then use update(...) + GPUh() void init() + { + size_t total{0}; + for (int32_t layer{0}; layer < NLayers; ++layer) { + total += this->mLayers[layer].mNROFsTF; + } + mFlatTable.resize(total, {0, 0}); + size_t offset = 0; + for (int32_t layer{0}; layer < NLayers; ++layer) { + size_t nROFs = this->mLayers[layer].mNROFsTF; + mIndices[layer].setFirstEntry(static_cast(offset)); + mIndices[layer].setEntries(static_cast(nROFs)); + offset += nROFs; + } + } + + // Recalculate lookup table with new vertices + GPUh() void update(const Vertex* vertices, size_t nVertices) + { + size_t offset = 0; + for (int32_t layer{0}; layer < NLayers; ++layer) { + const auto& idx = mIndices[layer]; + size_t nROFs = idx.getEntries(); + for (size_t iROF = 0; iROF < nROFs; ++iROF) { + updateROFMapping(layer, iROF, vertices, nVertices, offset + iROF); + } + offset += nROFs; + } + } + + GPUh() View getView() const + { + View view; + view.mFlatTable = mFlatTable.data(); + view.mIndices = mIndices; + view.mLayers = this->mLayers; + return view; + } + + GPUh() View getDeviceView(const TableEntry* deviceFlatTablePtr, const TableIndex* deviceIndicesPtr, const LayerTiming* deviceLayerTimingPtr) const + { + View view; + view.mFlatTable = deviceFlatTablePtr; + view.mIndices = deviceIndicesPtr; + view.mLayers = deviceLayerTimingPtr; + return view; + } + + private: + // Build the mapping for one layer + GPUh() void buildMapping(int32_t layer, const Vertex* vertices, size_t nVertices, std::vector& table) + { + const auto& layerDef = this->mLayers[layer]; + table.resize(layerDef.mNROFsTF); + size_t vertexSearchStart = 0; + for (int32_t iROF{0}; iROF < layerDef.mNROFsTF; ++iROF) { + int64_t rofLower = o2::gpu::CAMath::Max((int64_t)layerDef.getROFStartInBC(iROF) - (int64_t)layerDef.mROFAddTimeErr, int64_t(0)); + int64_t rofUpper = (int64_t)layerDef.getROFEndInBC(iROF) + layerDef.mROFAddTimeErr; + size_t lastVertex = binarySearchFirst(vertices, nVertices, vertexSearchStart, rofUpper); + size_t firstVertex = vertexSearchStart; + while (firstVertex < lastVertex) { + auto vUpper = (int64_t)vertices[firstVertex].getTimeStamp().upper(); + if (vUpper > rofLower) { + break; + } + ++firstVertex; + } + size_t count = (lastVertex > firstVertex) ? (lastVertex - firstVertex) : 0; + table[iROF] = {static_cast(firstVertex), static_cast(count)}; + vertexSearchStart = firstVertex; + } + } + + // Update a single ROF's vertex mapping + GPUh() void updateROFMapping(int32_t layer, size_t iROF, const Vertex* vertices, size_t nVertices, size_t flatTableIdx) + { + const auto& layerDef = this->mLayers[layer]; + int64_t rofLower = o2::gpu::CAMath::Max((int64_t)layerDef.getROFStartInBC(iROF) - (int64_t)layerDef.mROFAddTimeErr, int64_t(0)); + int64_t rofUpper = (int64_t)layerDef.getROFEndInBC(iROF) + layerDef.mROFAddTimeErr; + size_t lastVertex = binarySearchFirst(vertices, nVertices, 0, rofUpper); + size_t firstVertex = 0; + while (firstVertex < lastVertex) { + int64_t vUpper = (int64_t)vertices[firstVertex].getTimeStamp().getTimeStamp() + + (int64_t)vertices[firstVertex].getTimeStamp().getTimeStampError(); + if (vUpper > rofLower) { + break; + } + ++firstVertex; + } + size_t count = (lastVertex > firstVertex) ? (lastVertex - firstVertex) : 0; + mFlatTable[flatTableIdx].setFirstEntry(static_cast(firstVertex)); + mFlatTable[flatTableIdx].setEntries(static_cast(count)); + } + + // Binary search for first vertex where maxBC >= targetBC + GPUh() size_t binarySearchFirst(const Vertex* vertices, size_t nVertices, size_t searchStart, BCType targetBC) const + { + size_t left = searchStart; + size_t right = nVertices; + while (left < right) { + size_t mid = left + ((right - left) / 2); + int64_t lower = (int64_t)vertices[mid].getTimeStamp().getTimeStamp() - + (int64_t)vertices[mid].getTimeStamp().getTimeStampError(); + if (lower < targetBC) { + left = mid + 1; + } else { + right = mid; + } + } + return left; + } + + // Compress the temporary table into a single flat table + GPUh() void flatten(const std::vector table[NLayers]) + { + // Count total entries + size_t total{0}; + for (int32_t i{0}; i < NLayers; ++i) { + total += table[i].size(); + } + + mFlatTable.reserve(total); + + // Build flat table and indices + for (int32_t i{0}; i < NLayers; ++i) { + mIndices[i].setFirstEntry(static_cast(mFlatTable.size())); + mIndices[i].setEntries(static_cast(table[i].size())); + mFlatTable.insert(mFlatTable.end(), table[i].begin(), table[i].end()); + } + } + + TableIndex mIndices[NLayers]; + std::vector mFlatTable; +}; + +// GPU-friendly view of the ROF mask table +template +struct ROFMaskTableView { + const TableEntry* mFlatMask{nullptr}; + const TableIndex* mLayerROFOffsets{nullptr}; // size NLayers+1 + + GPUhdi() bool isROFEnabled(int32_t layer, int32_t rofId) const noexcept + { + assert(layer >= 0 && layer < NLayers); + return mFlatMask[mLayerROFOffsets[layer] + rofId] != 0u; + } + +#ifndef GPUCA_GPUCODE + GPUh() void printAll() const + { + for (int32_t i = 0; i < NLayers; ++i) { + printLayer(i); + } + } + + GPUh() void printLayer(int32_t layer) const + { + constexpr int w_rof = 10; + constexpr int w_active = 10; + int32_t nROFs = mLayerROFOffsets[layer + 1] - mLayerROFOffsets[layer]; + LOGF(info, "Mask table: Layer %d", layer); + LOGF(info, "%*s | %*s", w_rof, "ROF", w_active, "Enabled"); + LOGF(info, "%.*s-+-%.*s", w_rof, "----------", w_active, "----------"); + for (int32_t i = 0; i < nROFs; ++i) { + LOGF(info, "%*d | %*d", w_rof, i, w_active, (int)isROFEnabled(layer, i)); + } + } + + GPUh() std::string asString(int32_t layer) const + { + int32_t nROFs = mLayerROFOffsets[layer + 1] - mLayerROFOffsets[layer]; + int32_t enabledROFs = 0; + for (int32_t j = 0; j < nROFs; ++j) { + if (isROFEnabled(layer, j)) { + ++enabledROFs; + } + } + return std::format("ROFMask on Layer {} ROFs enabled: {}/{}", layer, enabledROFs, nROFs); + } + + GPUh() void print(int32_t layer) const + { + LOG(info) << asString(layer); + } +#endif +}; + +// Per-ROF per-layer boolean mask (uint8_t for GPU compatibility). +template +class ROFMaskTable : public LayerTimingBase +{ + public: + using T = LayerTimingBase::T; + using BCRange = dataformats::RangeReference; + using TableIndex = uint32_t; + using TableEntry = uint8_t; + using View = ROFMaskTableView; + + ROFMaskTable() = default; + GPUh() explicit ROFMaskTable(const LayerTimingBase& timingBase) : LayerTimingBase(timingBase) { init(); } + + GPUh() void init() + { + int32_t totalROFs = 0; + for (int32_t layer{0}; layer < NLayers; ++layer) { + mLayerROFOffsets[layer] = totalROFs; + totalROFs += this->getLayer(layer).mNROFsTF; + } + mLayerROFOffsets[NLayers] = totalROFs; // sentinel + mFlatMask.resize(totalROFs, 0u); + } + + GPUh() size_t getFlatMaskSize() const noexcept { return mFlatMask.size(); } + + GPUh() void setROFEnabled(int32_t layer, int32_t rofId, uint8_t state = 1) noexcept + { + assert(layer >= 0 && layer < NLayers); + assert(rofId >= 0 && rofId < mLayerROFOffsets[layer + 1] - mLayerROFOffsets[layer]); + mFlatMask[mLayerROFOffsets[layer] + rofId] = state; + } + + GPUh() void setROFsEnabled(int32_t layer, int32_t firstRof, int32_t nRofs, uint8_t state = 1) noexcept + { + assert(layer >= 0 && layer < NLayers); + assert(firstRof >= 0); + assert(firstRof + nRofs <= mLayerROFOffsets[layer + 1] - mLayerROFOffsets[layer]); + std::memset(mFlatMask.data() + mLayerROFOffsets[layer] + firstRof, state, nRofs); + } + + // Enable all ROFs in all layers that are time-compatible with the given BC range + GPUh() void selectROF(const BCRange& t) + { + const int32_t bcStart = t.getFirstEntry(); + const int32_t bcEnd = t.getEntriesBound(); + for (int32_t layer{0}; layer < NLayers; ++layer) { + const auto& lay = this->getLayer(layer); + const int32_t offset = mLayerROFOffsets[layer]; + for (int32_t rofId{0}; rofId < lay.mNROFsTF; ++rofId) { + if (static_cast(lay.getROFStartInBC(rofId)) < bcEnd && + static_cast(lay.getROFEndInBC(rofId)) > bcStart) { + mFlatMask[offset + rofId] = 1u; + } + } + } + } + + // Reset mask to 0, then enable all ROFs compatible with any of the given BC ranges + GPUh() void selectROFs(const std::vector& ts) + { + resetMask(); + for (const auto& t : ts) { + selectROF(t); + } + } + + GPUh() void resetMask(uint8_t s = 0u) + { + std::memset(mFlatMask.data(), s, mFlatMask.size()); + } + + GPUh() void invertMask() + { + std::ranges::transform(mFlatMask, mFlatMask.begin(), [](uint8_t x) { return 1 - x; }); + } + + GPUh() void swap(ROFMaskTable& other) noexcept + { + std::swap(mFlatMask, other.mFlatMask); + std::swap(mLayerROFOffsets, other.mLayerROFOffsets); + } + + GPUh() View getView() const + { + View view; + view.mFlatMask = mFlatMask.data(); + view.mLayerROFOffsets = mLayerROFOffsets; + return view; + } + + GPUh() View getDeviceView(const TableEntry* deviceFlatMaskPtr, const TableIndex* deviceOffsetPtr) const + { + View view; + view.mFlatMask = deviceFlatMaskPtr; + view.mLayerROFOffsets = deviceOffsetPtr; + return view; + } + + private: + TableIndex mLayerROFOffsets[NLayers + 1] = {0}; + std::vector mFlatMask; +}; + +} // namespace o2::its + +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Road.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Road.h deleted file mode 100644 index 009f3a1b5b146..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Road.h +++ /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 Road.h -/// \brief -/// - -#ifndef TRACKINGCA_INCLUDE_ROAD_H -#define TRACKINGCA_INCLUDE_ROAD_H - -#include - -#include "ITStracking/Constants.h" -#include "GPUCommonDef.h" - -namespace o2::its -{ - -template -class Road final -{ - public: - GPUhdDefault() Road() = default; - GPUhd() Road(int cellLayer, int cellId) : Road() { addCell(cellLayer, cellId); } - - GPUhdDefault() Road(const Road&) = default; - GPUhdDefault() Road(Road&&) noexcept = default; - GPUhdDefault() ~Road() = default; - - GPUhdDefault() Road& operator=(const Road&) = default; - GPUhdDefault() Road& operator=(Road&&) noexcept = default; - - GPUhdi() uint8_t getRoadSize() const { return mRoadSize; } - GPUhdi() bool isFakeRoad() const { return mIsFakeRoad; } - GPUhdi() void setFakeRoad(const bool fake) { mIsFakeRoad = fake; } - GPUhdi() int& operator[](const int& i) { return mCellIds[i]; } - GPUhdi() int operator[](const int& i) const { return mCellIds[i]; } - - GPUhd() void resetRoad() - { - for (int i = 0; i < maxRoadSize; i++) { - mCellIds[i] = constants::UnusedIndex; - } - mRoadSize = 0; - } - - GPUhd() void addCell(int cellLayer, int cellId) - { - if (mCellIds[cellLayer] == constants::UnusedIndex) { - ++mRoadSize; - } - - mCellIds[cellLayer] = cellId; - } - - private: - std::array mCellIds = constants::helpers::initArray(); - unsigned char mRoadSize{0}; - bool mIsFakeRoad{false}; -}; - -} // namespace o2::its - -#endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Smoother.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Smoother.h deleted file mode 100644 index 101f4b8d72601..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Smoother.h +++ /dev/null @@ -1,60 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -// -/// \file Smoother.h -/// \brief Class to handle Kalman smoothing for ITS tracking. -/// Its instance stores the state of the track to the level we want to smooth to avoid multiple re-propagations when testing different clusters. -/// - -#include "ReconstructionDataFormats/Track.h" -#include "DataFormatsITS/TrackITS.h" -#include "DetectorsBase/Propagator.h" - -namespace o2 -{ -namespace its -{ - -template -class Smoother -{ - public: - // Smoother(TrackITSExt& track, size_t layer, const ROframe& event, float bZ, o2::base::PropagatorF::MatCorrType corr); - ~Smoother(); - - bool isValidInit() const - { - return mInitStatus; - } - // bool testCluster(const int clusterId, const ROframe& event); - bool getSmoothedTrack(); - float getChi2() const { return mBestChi2; } - float getLastChi2() const { return mLastChi2; } - - private: - float computeSmoothedPredictedChi2(const o2::track::TrackParCov& outwTrack, - const o2::track::TrackParCov& inwTrack, - const std::array& cls, - const std::array& clCov); - bool smoothTrack(); - - private: - size_t mLayerToSmooth; // Layer to compute smoothing optimization - float mBz; // Magnetic field along Z - bool mInitStatus; // State after the initialization - o2::base::PropagatorF::MatCorrType mCorr; // Type of correction to use - TrackITSExt mInwardsTrack; // outwards track: from innermost cluster to outermost - TrackITSExt mOutwardsTrack; // inwards track: from outermost cluster to innermost - float mBestChi2; // Best value of local smoothed chi2 - float mLastChi2 = 1e8; // Latest computed chi2 -}; -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h index acc884ea68b8b..3dd1b05cf8969 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h @@ -21,6 +21,7 @@ #include #include "DataFormatsITS/TrackITS.h" +#include "DataFormatsITS/Vertex.h" #include "ITStracking/Cell.h" #include "ITStracking/Cluster.h" @@ -28,15 +29,14 @@ #include "ITStracking/Constants.h" #include "ITStracking/ClusterLines.h" #include "ITStracking/Definitions.h" -#include "ITStracking/Road.h" #include "ITStracking/Tracklet.h" #include "ITStracking/IndexTableUtils.h" #include "ITStracking/ExternalAllocator.h" #include "ITStracking/BoundedAllocator.h" +#include "ITStracking/ROFLookupTables.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" -#include "ReconstructionDataFormats/Vertex.h" #include "DetectorsBase/Propagator.h" namespace o2 @@ -62,48 +62,42 @@ template class TimeFrameGPU; } -template +template struct TimeFrame { - using IndexTableUtilsN = IndexTableUtils; - using CellSeedN = CellSeed; - friend class gpu::TimeFrameGPU; + using IndexTableUtilsN = IndexTableUtils; + using ROFOverlapTableN = ROFOverlapTable; + using ROFVertexLookupTableN = ROFVertexLookupTable; + using ROFMaskTableN = ROFMaskTable; + using CellSeedN = CellSeed; + friend class gpu::TimeFrameGPU; TimeFrame() = default; virtual ~TimeFrame() = default; const Vertex& getPrimaryVertex(const int ivtx) const { return mPrimaryVertices[ivtx]; } - gsl::span getPrimaryVertices(int rofId) const; - gsl::span getPrimaryVertices(int romin, int romax) const; - gsl::span> getPrimaryVerticesMCRecInfo(const int rofId) const; - gsl::span getPrimaryVerticesContributors(const int rofId) const; - gsl::span> getPrimaryVerticesXAlpha(int rofId) const; - void fillPrimaryVerticesXandAlpha(); - int getPrimaryVerticesNum(int rofId = -1) const; - void addPrimaryVerticesLabels(bounded_vector>& labels); - void addPrimaryVerticesContributorLabels(bounded_vector& labels); - void addPrimaryVertices(const bounded_vector& vertices, const int iteration); - void addPrimaryVerticesInROF(const bounded_vector& vertices, const int rofId, const int iteration); - void addPrimaryVerticesLabelsInROF(const bounded_vector>& labels, const int rofId); - void addPrimaryVerticesContributorLabelsInROF(const bounded_vector& labels, const int rofId); - void removePrimaryVerticesInROf(const int rofId); - int loadROFrameData(const o2::itsmft::ROFRecord& rof, gsl::span clusters, - const dataformats::MCTruthContainer* mcLabels = nullptr); - - int loadROFrameData(gsl::span rofs, - gsl::span clusters, - gsl::span::iterator& pattIt, - const itsmft::TopologyDictionary* dict, - const dataformats::MCTruthContainer* mcLabels = nullptr); - void resetROFrameData(size_t nROFs); - void prepareROFrameData(gsl::span rofs, - gsl::span clusters); + auto& getPrimaryVertices() { return mPrimaryVertices; }; + auto getPrimaryVerticesNum() { return mPrimaryVertices.size(); }; + const auto& getPrimaryVertices() const { return mPrimaryVertices; }; + auto& getPrimaryVerticesLabels() { return mPrimaryVerticesLabels; }; + gsl::span getPrimaryVertices(int layer, int rofId) const; + void addPrimaryVertex(const Vertex& vertex); + void addPrimaryVertexLabel(const VertexLabel& label) { mPrimaryVerticesLabels.push_back(label); } + + // read-in data + void loadROFrameData(gsl::span rofs, + gsl::span clusters, + gsl::span::iterator& pattIt, + const itsmft::TopologyDictionary* dict, + int layer, + const dataformats::MCTruthContainer* mcLabels = nullptr); + void resetROFrameData(int iLayer); + void prepareROFrameData(gsl::span clusters, int layer); int getTotalClusters() const; - auto& getTotVertIteration() { return mTotVertPerIteration; } bool empty() const { return getTotalClusters() == 0; } int getSortedIndex(int rofId, int layer, int idx) const { return mROFramesClusters[layer][rofId] + idx; } int getSortedStartIndex(const int rofId, const int layer) const { return mROFramesClusters[layer][rofId]; } - int getNrof() const { return mNrof; } + int getNrof(int layer) const { return mROFramesClusters[layer].size() - 1; } void resetBeamXY(const float x, const float y, const float w = 0); void setBeamPosition(const float x, const float y, const float s2, const float base = 50.f, const float systematic = 0.f) @@ -114,6 +108,8 @@ struct TimeFrame { float getBeamX() const { return mBeamPos[0]; } float getBeamY() const { return mBeamPos[1]; } + std::array& getBeamXY() { return mBeamPos; } + auto& getMinRs() { return mMinR; } auto& getMaxRs() { return mMaxR; } float getMinR(int layer) const { return mMinR[layer]; } @@ -134,29 +130,56 @@ struct TimeFrame { gsl::span getROFramesClustersPerROFrange(int rofMin, int range, int layerId) const; gsl::span getROFrameClusters(int layerId) const; gsl::span getNClustersROFrange(int rofMin, int range, int layerId) const; - gsl::span getIndexTablePerROFrange(int rofMin, int range, int layerId) const; gsl::span getIndexTable(int rofId, int layerId); - auto& getIndexTableWhole(int layerId) { return mIndexTables[layerId]; } const auto& getTrackingFrameInfoOnLayer(int layerId) const { return mTrackingFrameInfo[layerId]; } + // navigation tables + const auto& getIndexTableUtils() const { return mIndexTableUtils; } + const auto& getROFOverlapTable() const { return mROFOverlapTable; } + const auto& getROFOverlapTableView() const { return mROFOverlapTableView; } + void setROFOverlapTable(ROFOverlapTableN table) + { + mROFOverlapTable = std::move(table); + mROFOverlapTableView = mROFOverlapTable.getView(); + } + const auto& getROFVertexLookupTable() const { return mROFVertexLookupTable; } + const auto& getROFVertexLookupTableView() const { return mROFVertexLookupTableView; } + void setROFVertexLookupTable(ROFVertexLookupTableN table) + { + mROFVertexLookupTable = std::move(table); + mROFVertexLookupTableView = mROFVertexLookupTable.getView(); + } + void updateROFVertexLookupTable() { mROFVertexLookupTable.update(mPrimaryVertices.data(), mPrimaryVertices.size()); } + void setMultiplicityCutMask(ROFMaskTableN cutMask) + { + mMultiplicityCutMask = std::move(cutMask); + mROFMaskView = mROFMask->getView(); + } + void useMultiplictyMask() noexcept + { + mROFMask = &mMultiplicityCutMask; + mROFMaskView = mROFMask->getView(); + } + void setUPCCutMask(ROFMaskTableN cutMask) { mUPCCutMask = std::move(cutMask); } + void useUPCMask() noexcept + { + mROFMask = &mUPCCutMask; + mROFMaskView = mROFMask->getView(); + } + const auto& getROFMaskView() const { return mROFMaskView; } + const TrackingFrameInfo& getClusterTrackingFrameInfo(int layerId, const Cluster& cl) const; gsl::span getClusterLabels(int layerId, const Cluster& cl) const { return getClusterLabels(layerId, cl.clusterId); } - gsl::span getClusterLabels(int layerId, const int clId) const { return mClusterLabels->getLabels(mClusterExternalIndices[layerId][clId]); } + gsl::span getClusterLabels(int layerId, const int clId) const { return mClusterLabels[((mIsStaggered) ? layerId : 0)]->getLabels(mClusterExternalIndices[layerId][clId]); } int getClusterExternalIndex(int layerId, const int clId) const { return mClusterExternalIndices[layerId][clId]; } - int getClusterSize(int clusterId) const { return mClusterSize[clusterId]; } - void setClusterSize(bounded_vector& v) { mClusterSize = std::move(v); } + int getClusterSize(int layer, int clusterId) const { return mClusterSize[layer][clusterId]; } + void setClusterSize(int layer, bounded_vector& v) { mClusterSize[layer] = std::move(v); } auto& getTrackletsLabel(int layer) { return mTrackletLabels[layer]; } auto& getCellsLabel(int layer) { return mCellLabels[layer]; } - bool hasMCinformation() const { return mClusterLabels; } - void initialise(const int iteration, const TrackingParameters& trkParam, const int maxLayers = 7, bool resetVertices = true); - void resetRofPV() - { - deepVectorClear(mPrimaryVertices); - mROFramesPV.resize(1, 0); - mTotVertPerIteration.resize(1); - } + bool hasMCinformation() const { return mClusterLabels[0] != nullptr; } + void initialise(const int iteration, const TrackingParameters& trkParam, const int maxLayers = NLayers, bool resetVertices = true); bool isClusterUsed(int layer, int clusterId) const { return mUsedClusters[layer][clusterId]; } void markUsedCluster(int layer, int clusterId) { mUsedClusters[layer][clusterId] = true; } @@ -173,20 +196,16 @@ struct TimeFrame { auto& getCellsLookupTable() { return mCellsLookupTable; } auto& getCellsNeighbours() { return mCellsNeighbours; } auto& getCellsNeighboursLUT() { return mCellsNeighboursLUT; } - auto& getRoads() { return mRoads; } - auto& getTracks(int rofId) { return mTracks[rofId]; } - auto& getTracksLabel(const int rofId) { return mTracksLabel[rofId]; } + auto& getTracks() { return mTracks; } + auto& getTracksLabel() { return mTracksLabel; } auto& getLinesLabel(const int rofId) { return mLinesLabels[rofId]; } - auto& getVerticesMCRecInfo() { return mVerticesMCRecInfo; } - int getNumberOfClusters() const; - virtual int getNumberOfCells() const; - virtual int getNumberOfTracklets() const; - virtual int getNumberOfNeighbours() const; + size_t getNumberOfClusters() const; + virtual size_t getNumberOfCells() const; + virtual size_t getNumberOfTracklets() const; + virtual size_t getNumberOfNeighbours() const; size_t getNumberOfTracks() const; size_t getNumberOfUsedClusters() const; - auto getNumberOfExtendedTracks() const { return mNExtendedTracks; } - auto getNumberOfUsedExtendedClusters() const { return mNExtendedUsedClusters; } /// memory management void setMemoryPool(std::shared_ptr pool); @@ -195,10 +214,8 @@ struct TimeFrame { unsigned long getArtefactsMemory() const; void printArtefactsMemory() const; - /// ROF cuts - int getROFCutClusterMult() const { return mCutClusterMult; }; - int getROFCutVertexMult() const { return mCutVertexMult; }; - int getROFCutAllMult() const { return mCutClusterMult + mCutVertexMult; } + /// staggering + void setIsStaggered(bool b) noexcept { mIsStaggered = b; } // Vertexer void computeTrackletsPerROFScans(); @@ -215,20 +232,8 @@ struct TimeFrame { gsl::span getExclusiveNTrackletsCluster(int rofId, int combId); uint32_t getTotalTrackletsTF(const int iLayer) { return mTotalTracklets[iLayer]; } int getTotalClustersPerROFrange(int rofMin, int range, int layerId) const; - std::array& getBeamXY() { return mBeamPos; } - unsigned int& getNoVertexROF() { return mNoVertexROF; } - void insertPastVertex(const Vertex& vertex, const int refROFId); // \Vertexer - void initialiseRoadLabels(); - void setRoadLabel(int i, const unsigned long long& lab, bool fake); - const unsigned long long& getRoadLabel(int i) const { return mRoadLabels[i].first; } - bool isRoadFake(int i) const { return mRoadLabels[i].second; } - - void setMultiplicityCutMask(const std::vector& cutMask) { mMultiplicityCutMask = cutMask; } - void setROFMask(const std::vector& rofMask) { mROFMask = rofMask; } - void swapMasks() { mMultiplicityCutMask.swap(mROFMask); } - int hasBogusClusters() const { return std::accumulate(mBogusClusters.begin(), mBogusClusters.end(), 0); } void setBz(float bz) { mBz = bz; } @@ -252,44 +257,25 @@ struct TimeFrame { void addTrackingFrameInfoToLayer(int layer, T&&... args); void addClusterExternalIndexToLayer(int layer, const int idx) { mClusterExternalIndices[layer].push_back(idx); } - /// Debug and printing - void checkTrackletLUTs(); - void printROFoffsets(); - void printNClsPerROF(); - void printVertices(); - void printTrackletLUTonLayer(int i); - void printCellLUTonLayer(int i); - void printTrackletLUTs(); - void printCellLUTs(); - void printSliceInfo(const int, const int); - - IndexTableUtilsN mIndexTableUtils; - - std::array, nLayers> mClusters; - std::array, nLayers> mTrackingFrameInfo; - std::array, nLayers> mClusterExternalIndices; - std::array, nLayers> mROFramesClusters; - const dataformats::MCTruthContainer* mClusterLabels = nullptr; + std::array, NLayers> mClusters; + std::array, NLayers> mTrackingFrameInfo; + std::array, NLayers> mClusterExternalIndices; + std::array, NLayers> mROFramesClusters; + std::array*, NLayers> mClusterLabels{nullptr}; std::array, 2> mNTrackletsPerCluster; std::array, 2> mNTrackletsPerClusterSum; - std::array, nLayers> mNClustersPerROF; - std::array, nLayers> mIndexTables; + std::array, NLayers> mNClustersPerROF; + std::array, NLayers> mIndexTables; std::vector> mTrackletsLookupTable; - std::array, nLayers> mUsedClusters; - int mNrof = 0; - int mNExtendedTracks{0}; - int mNExtendedUsedClusters{0}; - bounded_vector mROFramesPV; - bounded_vector mPrimaryVertices; + std::array, NLayers> mUsedClusters; - std::array, nLayers> mUnsortedClusters; + std::array, NLayers> mUnsortedClusters; std::vector> mTracklets; std::vector> mCells; - bounded_vector> mRoads; - std::vector> mTracks; + bounded_vector mTracks; + bounded_vector mTracksLabel; std::vector> mCellsNeighbours; std::vector> mCellsLookupTable; - std::vector mMultiplicityCutMask; const o2::base::PropagatorImpl* mPropagatorDevice = nullptr; // Needed only for GPU @@ -300,279 +286,210 @@ struct TimeFrame { virtual const char* getName() const noexcept { return "CPU"; } protected: - void prepareClusters(const TrackingParameters& trkParam, const int maxLayers = nLayers); + void prepareClusters(const TrackingParameters& trkParam, const int maxLayers = NLayers); float mBz = 5.; unsigned int mNTotalLowPtVertices = 0; int mBeamPosWeight = 0; std::array mBeamPos = {0.f, 0.f}; bool isBeamPositionOverridden = false; - std::array mMinR; - std::array mMaxR; + std::array mMinR; + std::array mMaxR; bounded_vector mMSangles; bounded_vector mPhiCuts; bounded_vector mPositionResolution; - bounded_vector mClusterSize; + std::array, NLayers> mClusterSize; - std::vector mROFMask; bounded_vector> mPValphaX; /// PV x and alpha for track propagation std::vector> mTrackletLabels; std::vector> mCellLabels; std::vector> mCellsNeighboursLUT; - std::vector> mTracksLabel; bounded_vector mBogusClusters; /// keep track of clusters with wild coordinates - bounded_vector> mRoadLabels; - int mCutClusterMult{-999}; - int mCutVertexMult{-999}; - // Vertexer + bounded_vector mPrimaryVertices; + bounded_vector mPrimaryVerticesLabels; std::vector> mNTrackletsPerROF; std::vector> mLines; std::vector> mTrackletClusters; std::array, 2> mTrackletsIndexROF; std::vector> mLinesLabels; - std::vector> mVerticesMCRecInfo; - bounded_vector mVerticesContributorLabels; std::array mTotalTracklets = {0, 0}; uint32_t mTotalLines = 0; - unsigned int mNoVertexROF = 0; - bounded_vector mTotVertPerIteration; // \Vertexer - std::shared_ptr mMemoryPool; -}; - -template -inline gsl::span TimeFrame::getPrimaryVertices(int rofId) const -{ - if (mPrimaryVertices.empty()) { - return {}; - } - const int start = mROFramesPV[rofId]; - const int stop_idx = rofId >= mNrof - 1 ? mNrof : rofId + 1; - int delta = mMultiplicityCutMask[rofId] ? mROFramesPV[stop_idx] - start : 0; // return empty span if Rof is excluded - return {&mPrimaryVertices[start], static_cast::size_type>(delta)}; -} + // lookup tables + IndexTableUtilsN mIndexTableUtils; + ROFOverlapTableN mROFOverlapTable; + ROFOverlapTableN::View mROFOverlapTableView; + ROFVertexLookupTableN mROFVertexLookupTable; + ROFVertexLookupTableN::View mROFVertexLookupTableView; + ROFMaskTableN mMultiplicityCutMask; + ROFMaskTableN mUPCCutMask; + ROFMaskTableN* mROFMask = &mMultiplicityCutMask; + ROFMaskTableN::View mROFMaskView; -template -inline gsl::span> TimeFrame::getPrimaryVerticesMCRecInfo(const int rofId) const -{ - const int start = mROFramesPV[rofId]; - const int stop_idx = rofId >= mNrof - 1 ? mNrof : rofId + 1; - int delta = mMultiplicityCutMask[rofId] ? mROFramesPV[stop_idx] - start : 0; // return empty span if Rof is excluded - return {&(mVerticesMCRecInfo[start]), static_cast>::size_type>(delta)}; -} + bool mIsStaggered{false}; -template -inline gsl::span TimeFrame::getPrimaryVerticesContributors(const int rofId) const -{ - // count the number of cont. in rofs before target rof - unsigned int start{0}, delta{0}; - const auto& pvsBefore = getPrimaryVertices(0, rofId - 1); - for (const auto& pv : pvsBefore) { - start += pv.getNContributors(); - } - const auto& pvsIn = getPrimaryVertices(rofId); - for (const auto& pv : pvsIn) { - delta += pv.getNContributors(); - } - return {&(mVerticesContributorLabels[start]), static_cast::size_type>(delta)}; -} + std::shared_ptr mMemoryPool; +}; -template -inline gsl::span TimeFrame::getPrimaryVertices(int romin, int romax) const +template +gsl::span TimeFrame::getPrimaryVertices(int layer, int rofId) const { - if (mPrimaryVertices.empty()) { + if (rofId < 0 || rofId >= getNrof(layer)) { return {}; } - const int stop_idx = romax >= mNrof - 1 ? mNrof : romax + 1; - return {&mPrimaryVertices[mROFramesPV[romin]], static_cast::size_type>(mROFramesPV[stop_idx] - mROFramesPV[romin])}; -} - -template -inline gsl::span> TimeFrame::getPrimaryVerticesXAlpha(int rofId) const -{ - const int start = mROFramesPV[rofId]; - const int stop_idx = rofId >= mNrof - 1 ? mNrof : rofId + 1; - int delta = mMultiplicityCutMask[rofId] ? mROFramesPV[stop_idx] - start : 0; // return empty span if Rof is excluded - return {&(mPValphaX[start]), static_cast>::size_type>(delta)}; -} - -template -inline int TimeFrame::getPrimaryVerticesNum(int rofId) const -{ - return rofId < 0 ? mPrimaryVertices.size() : mROFramesPV[rofId + 1] - mROFramesPV[rofId]; + const auto& entry = mROFVertexLookupTableView.getVertices(layer, rofId); + return {&mPrimaryVertices[entry.getFirstEntry()], static_cast::size_type>(entry.getEntries())}; } -template -inline void TimeFrame::resetBeamXY(const float x, const float y, const float w) +template +inline void TimeFrame::resetBeamXY(const float x, const float y, const float w) { mBeamPos[0] = x; mBeamPos[1] = y; mBeamPosWeight = w; } -template -inline gsl::span TimeFrame::getROFrameClusters(int layerId) const +template +inline gsl::span TimeFrame::getROFrameClusters(int layerId) const { return {&mROFramesClusters[layerId][0], static_cast::size_type>(mROFramesClusters[layerId].size())}; } -template -inline gsl::span TimeFrame::getClustersOnLayer(int rofId, int layerId) +template +inline gsl::span TimeFrame::getClustersOnLayer(int rofId, int layerId) { - if (rofId < 0 || rofId >= mNrof) { + if (rofId < 0 || rofId >= getNrof(layerId)) { return {}; } int startIdx{mROFramesClusters[layerId][rofId]}; return {&mClusters[layerId][startIdx], static_cast::size_type>(mROFramesClusters[layerId][rofId + 1] - startIdx)}; } -template -inline gsl::span TimeFrame::getClustersOnLayer(int rofId, int layerId) const +template +inline gsl::span TimeFrame::getClustersOnLayer(int rofId, int layerId) const { - if (rofId < 0 || rofId >= mNrof) { + if (rofId < 0 || rofId >= getNrof(layerId)) { return {}; } int startIdx{mROFramesClusters[layerId][rofId]}; return {&mClusters[layerId][startIdx], static_cast::size_type>(mROFramesClusters[layerId][rofId + 1] - startIdx)}; } -template -inline gsl::span TimeFrame::getUsedClustersROF(int rofId, int layerId) +template +inline gsl::span TimeFrame::getUsedClustersROF(int rofId, int layerId) { - if (rofId < 0 || rofId >= mNrof) { + if (rofId < 0 || rofId >= getNrof(layerId)) { return {}; } int startIdx{mROFramesClusters[layerId][rofId]}; return {&mUsedClusters[layerId][startIdx], static_cast::size_type>(mROFramesClusters[layerId][rofId + 1] - startIdx)}; } -template -inline gsl::span TimeFrame::getUsedClustersROF(int rofId, int layerId) const +template +inline gsl::span TimeFrame::getUsedClustersROF(int rofId, int layerId) const { - if (rofId < 0 || rofId >= mNrof) { + if (rofId < 0 || rofId >= getNrof(layerId)) { return {}; } int startIdx{mROFramesClusters[layerId][rofId]}; return {&mUsedClusters[layerId][startIdx], static_cast::size_type>(mROFramesClusters[layerId][rofId + 1] - startIdx)}; } -template -inline gsl::span TimeFrame::getClustersPerROFrange(int rofMin, int range, int layerId) const +template +inline gsl::span TimeFrame::getClustersPerROFrange(int rofMin, int range, int layerId) const { - if (rofMin < 0 || rofMin >= mNrof) { + if (rofMin < 0 || rofMin >= getNrof(layerId)) { return {}; } int startIdx{mROFramesClusters[layerId][rofMin]}; // First cluster of rofMin - int endIdx{mROFramesClusters[layerId][o2::gpu::CAMath::Min(rofMin + range, mNrof)]}; + int endIdx{mROFramesClusters[layerId][o2::gpu::CAMath::Min(rofMin + range, getNrof(layerId))]}; return {&mClusters[layerId][startIdx], static_cast::size_type>(endIdx - startIdx)}; } -template -inline gsl::span TimeFrame::getROFramesClustersPerROFrange(int rofMin, int range, int layerId) const +template +inline gsl::span TimeFrame::getROFramesClustersPerROFrange(int rofMin, int range, int layerId) const { - int chkdRange{o2::gpu::CAMath::Min(range, mNrof - rofMin)}; + int chkdRange{o2::gpu::CAMath::Min(range, getNrof(layerId) - rofMin)}; return {&mROFramesClusters[layerId][rofMin], static_cast::size_type>(chkdRange)}; } -template -inline gsl::span TimeFrame::getNClustersROFrange(int rofMin, int range, int layerId) const +template +inline gsl::span TimeFrame::getNClustersROFrange(int rofMin, int range, int layerId) const { - int chkdRange{o2::gpu::CAMath::Min(range, mNrof - rofMin)}; + int chkdRange{o2::gpu::CAMath::Min(range, getNrof(layerId) - rofMin)}; return {&mNClustersPerROF[layerId][rofMin], static_cast::size_type>(chkdRange)}; } -template -inline int TimeFrame::getTotalClustersPerROFrange(int rofMin, int range, int layerId) const +template +inline int TimeFrame::getTotalClustersPerROFrange(int rofMin, int range, int layerId) const { int startIdx{rofMin}; // First cluster of rofMin - int endIdx{o2::gpu::CAMath::Min(rofMin + range, mNrof)}; + int endIdx{o2::gpu::CAMath::Min(rofMin + range, getNrof(layerId))}; return mROFramesClusters[layerId][endIdx] - mROFramesClusters[layerId][startIdx]; } -template -inline gsl::span TimeFrame::getIndexTablePerROFrange(int rofMin, int range, int layerId) const -{ - const int iTableSize{mIndexTableUtils.getNphiBins() * mIndexTableUtils.getNzBins() + 1}; - int chkdRange{o2::gpu::CAMath::Min(range, mNrof - rofMin)}; - return {&mIndexTables[layerId][rofMin * iTableSize], static_cast::size_type>(chkdRange * iTableSize)}; -} - -template -inline int TimeFrame::getClusterROF(int iLayer, int iCluster) +template +inline int TimeFrame::getClusterROF(int iLayer, int iCluster) { return std::lower_bound(mROFramesClusters[iLayer].begin(), mROFramesClusters[iLayer].end(), iCluster + 1) - mROFramesClusters[iLayer].begin() - 1; } -template -inline gsl::span TimeFrame::getUnsortedClustersOnLayer(int rofId, int layerId) const +template +inline gsl::span TimeFrame::getUnsortedClustersOnLayer(int rofId, int layerId) const { - if (rofId < 0 || rofId >= mNrof) { + if (rofId < 0 || rofId >= getNrof(layerId)) { return {}; } int startIdx{mROFramesClusters[layerId][rofId]}; return {&mUnsortedClusters[layerId][startIdx], static_cast::size_type>(mROFramesClusters[layerId][rofId + 1] - startIdx)}; } -template -inline gsl::span TimeFrame::getIndexTable(int rofId, int layer) +template +inline gsl::span TimeFrame::getIndexTable(int rofId, int layer) { - if (rofId < 0 || rofId >= mNrof) { + if (rofId < 0 || rofId >= getNrof(layer)) { return {}; } const int tableSize = mIndexTableUtils.getNphiBins() * mIndexTableUtils.getNzBins() + 1; return {&mIndexTables[layer][rofId * tableSize], static_cast::size_type>(tableSize)}; } -template +template template -void TimeFrame::addClusterToLayer(int layer, T&&... values) +void TimeFrame::addClusterToLayer(int layer, T&&... values) { mUnsortedClusters[layer].emplace_back(std::forward(values)...); } -template +template template -void TimeFrame::addTrackingFrameInfoToLayer(int layer, T&&... values) +void TimeFrame::addTrackingFrameInfoToLayer(int layer, T&&... values) { mTrackingFrameInfo[layer].emplace_back(std::forward(values)...); } -template -inline gsl::span TimeFrame::getUsedClusters(const int layer) +template +inline gsl::span TimeFrame::getUsedClusters(const int layer) { return {&mUsedClusters[layer][0], static_cast::size_type>(mUsedClusters[layer].size())}; } -template -inline void TimeFrame::initialiseRoadLabels() +template +inline gsl::span TimeFrame::getNTrackletsCluster(int rofId, int combId) { - mRoadLabels.clear(); - mRoadLabels.resize(mRoads.size()); -} - -template -inline void TimeFrame::setRoadLabel(int i, const unsigned long long& lab, bool fake) -{ - mRoadLabels[i].first = lab; - mRoadLabels[i].second = fake; -} - -template -inline gsl::span TimeFrame::getNTrackletsCluster(int rofId, int combId) -{ - if (rofId < 0 || rofId >= mNrof) { + if (rofId < 0 || rofId >= getNrof(1)) { return {}; } auto startIdx{mROFramesClusters[1][rofId]}; return {&mNTrackletsPerCluster[combId][startIdx], static_cast::size_type>(mROFramesClusters[1][rofId + 1] - startIdx)}; } -template -inline gsl::span TimeFrame::getExclusiveNTrackletsCluster(int rofId, int combId) +template +inline gsl::span TimeFrame::getExclusiveNTrackletsCluster(int rofId, int combId) { - if (rofId < 0 || rofId >= mNrof) { + if (rofId < 0 || rofId >= getNrof(1)) { return {}; } auto clusStartIdx{mROFramesClusters[1][rofId]}; @@ -580,38 +497,38 @@ inline gsl::span TimeFrame::getExclusiveNTrackletsCluster(int rofI return {&mNTrackletsPerClusterSum[combId][clusStartIdx], static_cast::size_type>(mROFramesClusters[1][rofId + 1] - clusStartIdx)}; } -template -inline gsl::span TimeFrame::getFoundTracklets(int rofId, int combId) +template +inline gsl::span TimeFrame::getFoundTracklets(int rofId, int combId) { - if (rofId < 0 || rofId >= mNrof || mTracklets[combId].empty()) { + if (rofId < 0 || rofId >= getNrof(1) || mTracklets[combId].empty()) { return {}; } auto startIdx{mNTrackletsPerROF[combId][rofId]}; return {&mTracklets[combId][startIdx], static_cast::size_type>(mNTrackletsPerROF[combId][rofId + 1] - startIdx)}; } -template -inline gsl::span TimeFrame::getFoundTracklets(int rofId, int combId) const +template +inline gsl::span TimeFrame::getFoundTracklets(int rofId, int combId) const { - if (rofId < 0 || rofId >= mNrof) { + if (rofId < 0 || rofId >= getNrof(1)) { return {}; } auto startIdx{mNTrackletsPerROF[combId][rofId]}; return {&mTracklets[combId][startIdx], static_cast::size_type>(mNTrackletsPerROF[combId][rofId + 1] - startIdx)}; } -template -inline gsl::span TimeFrame::getLabelsFoundTracklets(int rofId, int combId) const +template +inline gsl::span TimeFrame::getLabelsFoundTracklets(int rofId, int combId) const { - if (rofId < 0 || rofId >= mNrof || !hasMCinformation()) { + if (rofId < 0 || rofId >= getNrof(1) || !hasMCinformation()) { return {}; } auto startIdx{mNTrackletsPerROF[combId][rofId]}; return {&mTrackletLabels[combId][startIdx], static_cast::size_type>(mNTrackletsPerROF[combId][rofId + 1] - startIdx)}; } -template -inline int TimeFrame::getTotalClusters() const +template +inline int TimeFrame::getTotalClusters() const { size_t totalClusters{0}; for (const auto& clusters : mUnsortedClusters) { @@ -620,58 +537,54 @@ inline int TimeFrame::getTotalClusters() const return int(totalClusters); } -template -inline int TimeFrame::getNumberOfClusters() const +template +inline size_t TimeFrame::getNumberOfClusters() const { - int nClusters = 0; + size_t nClusters{0}; for (const auto& layer : mClusters) { nClusters += layer.size(); } return nClusters; } -template -inline int TimeFrame::getNumberOfCells() const +template +inline size_t TimeFrame::getNumberOfCells() const { - int nCells = 0; + size_t nCells{0}; for (const auto& layer : mCells) { nCells += layer.size(); } return nCells; } -template -inline int TimeFrame::getNumberOfTracklets() const +template +inline size_t TimeFrame::getNumberOfTracklets() const { - int nTracklets = 0; + size_t nTracklets{0}; for (const auto& layer : mTracklets) { nTracklets += layer.size(); } return nTracklets; } -template -inline int TimeFrame::getNumberOfNeighbours() const +template +inline size_t TimeFrame::getNumberOfNeighbours() const { - int n{0}; + size_t neigh{0}; for (const auto& l : mCellsNeighbours) { - n += l.size(); + neigh += l.size(); } - return n; + return neigh; } -template -inline size_t TimeFrame::getNumberOfTracks() const +template +inline size_t TimeFrame::getNumberOfTracks() const { - int nTracks = 0; - for (const auto& t : mTracks) { - nTracks += t.size(); - } - return nTracks; + return mTracks.size(); } -template -inline size_t TimeFrame::getNumberOfUsedClusters() const +template +inline size_t TimeFrame::getNumberOfUsedClusters() const { size_t nClusters = 0; for (const auto& layer : mUsedClusters) { @@ -680,17 +593,6 @@ inline size_t TimeFrame::getNumberOfUsedClusters() const return nClusters; } -template -inline void TimeFrame::insertPastVertex(const Vertex& vertex, const int iteration) -{ - int rofId = vertex.getTimeStamp().getTimeStamp(); - mPrimaryVertices.insert(mPrimaryVertices.begin() + mROFramesPV[rofId], vertex); - for (int i = rofId + 1; i < mROFramesPV.size(); ++i) { - mROFramesPV[i]++; - } - mTotVertPerIteration[iteration]++; -} - } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h index 3ea382c626fed..a1a0bf7249a21 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h @@ -30,17 +30,10 @@ #include #include "ITStracking/Configuration.h" -#include "CommonConstants/MathConstants.h" -#include "ITStracking/Definitions.h" -#include "ITStracking/MathUtils.h" #include "ITStracking/TimeFrame.h" #include "ITStracking/TrackerTraits.h" -#include "ITStracking/Road.h" #include "ITStracking/BoundedAllocator.h" -#include "DataFormatsITS/TrackITS.h" -#include "SimulationDataFormat/MCCompLabel.h" - namespace o2 { @@ -51,15 +44,15 @@ class GPUChainITS; namespace its { -template +template class Tracker { using LogFunc = std::function; public: - Tracker(TrackerTraits* traits); + Tracker(TrackerTraits* traits); - void adoptTimeFrame(TimeFrame& tf); + void adoptTimeFrame(TimeFrame& tf); void clustersToTracks( const LogFunc& = [](const std::string& s) { std::cout << s << '\n'; }, @@ -69,33 +62,31 @@ class Tracker void setMemoryPool(std::shared_ptr pool) { mMemoryPool = pool; } std::vector& getParameters() { return mTrkParams; } void setBz(float bz) { mTraits->setBz(bz); } - bool isMatLUT() const { return mTraits->isMatLUT(); } + void setTimeSlice(size_t slice) noexcept { mTimeSlice = slice; } 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); } - void computeTracklets(int iteration, int iROFslice, int iVertex) { mTraits->computeLayerTracklets(iteration, iROFslice, iVertex); } + void computeTracklets(int iteration, int iVertex) { mTraits->computeLayerTracklets(iteration, iVertex); } void computeCells(int iteration) { mTraits->computeLayerCells(iteration); } void findCellsNeighbours(int iteration) { mTraits->findCellsNeighbours(iteration); } void findRoads(int iteration) { mTraits->findRoads(iteration); } - void findShortPrimaries() { mTraits->findShortPrimaries(); } - void extendTracks(int iteration) { mTraits->extendTracks(iteration); } - // MC interaction - void computeRoadsMClabels(); void rectifyClusterIndices(); + void sortTracks(); template float evaluateTask(void (Tracker::*task)(T...), std::string_view taskName, int iteration, LogFunc logger, F&&... args); - TrackerTraits* mTraits = nullptr; /// Observer pointer, not owned by this class - TimeFrame* mTimeFrame = nullptr; /// Observer pointer, not owned by this class + TrackerTraits* mTraits = nullptr; /// Observer pointer, not owned by this class + TimeFrame* mTimeFrame = nullptr; /// Observer pointer, not owned by this class std::vector mTrkParams; o2::gpu::GPUChainITS* mRecoChain = nullptr; + size_t mTimeSlice{0}; // current timeslice unsigned int mNumberOfDroppedTFs{0}; unsigned int mTimeFrameCounter{0}; double mTotalTime{0}; @@ -113,9 +104,9 @@ class Tracker static constexpr std::array StateNames{"TimeFrame initialisation", "Tracklet finding", "Cell finding", "Neighbour finding", "Road finding"}; }; -template +template template -float Tracker::evaluateTask(void (Tracker::*task)(T...), std::string_view taskName, int iteration, LogFunc logger, F&&... args) +float Tracker::evaluateTask(void (Tracker::*task)(T...), std::string_view taskName, int iteration, LogFunc logger, F&&... args) { float diff{0.f}; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h index ddc32ed18cbfe..fd3251a59d835 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h @@ -38,36 +38,28 @@ namespace its { class TrackITSExt; -template +template class TrackerTraits { public: - using IndexTableUtilsN = IndexTableUtils; - using CellSeedN = CellSeed; + using IndexTableUtilsN = IndexTableUtils; + using CellSeedN = CellSeed; virtual ~TrackerTraits() = default; - virtual void adoptTimeFrame(TimeFrame* tf) { mTimeFrame = tf; } - virtual void initialiseTimeFrame(const int iteration) { mTimeFrame->initialise(iteration, mTrkParams[iteration], mTrkParams[iteration].NLayers); } + virtual void adoptTimeFrame(TimeFrame* tf) { mTimeFrame = tf; } + virtual void initialiseTimeFrame(const int iteration) { mTimeFrame->initialise(iteration, mTrkParams[iteration], mTrkParams[iteration].NLayers, false); } - virtual void computeLayerTracklets(const int iteration, int iROFslice, int iVertex); + virtual void computeLayerTracklets(const int iteration, int iVertex); virtual void computeLayerCells(const int iteration); virtual void findCellsNeighbours(const int iteration); virtual void findRoads(const int iteration); - - virtual bool supportsExtendTracks() const noexcept { return true; } - virtual void extendTracks(const int iteration); - virtual bool supportsFindShortPrimaries() const noexcept { return true; } - virtual void findShortPrimaries(); - - virtual bool trackFollowing(TrackITSExt* track, int rof, bool outward, const int iteration); virtual void processNeighbours(int iLayer, int iLevel, const bounded_vector& currentCellSeed, const bounded_vector& currentCellId, bounded_vector& updatedCellSeed, bounded_vector& updatedCellId); void updateTrackingParameters(const std::vector& trkPars) { mTrkParams = trkPars; } - TimeFrame* getTimeFrame() { return mTimeFrame; } + TimeFrame* getTimeFrame() { return mTimeFrame; } virtual void setBz(float bz); float getBz() const { return mBz; } - bool isMatLUT() const; virtual const char* getName() const noexcept { return "CPU"; } virtual bool isGPU() const noexcept { return false; } void setMemoryPool(std::shared_ptr pool) noexcept { mMemoryPool = pool; } @@ -75,17 +67,14 @@ class TrackerTraits // Others GPUhd() static consteval int4 getEmptyBinsRect() { return int4{0, 0, 0, 0}; } - const int4 getBinsRect(int layer, float phi, float maxdeltaphi, float z, float maxdeltaz) const noexcept { return getBinsRect(layer, phi, maxdeltaphi, z, z, maxdeltaz); } - const int4 getBinsRect(const Cluster& cls, int layer, float z1, float z2, float maxdeltaz, float maxdeltaphi) const noexcept { return getBinsRect(layer, cls.phi, maxdeltaphi, z1, z2, maxdeltaz); } - const int4 getBinsRect(int layer, float phi, float maxdeltaphi, float z1, float z2, float maxdeltaz) const noexcept; - void SetRecoChain(o2::gpu::GPUChainITS* chain) { mChain = chain; } - void setSmoothing(bool v) { mApplySmoothing = v; } - bool getSmoothing() const { return mApplySmoothing; } + int4 getBinsRect(const int iteration, int layer, float phi, float maxdeltaphi, float z, float maxdeltaz) + const noexcept { return getBinsRect(iteration, layer, phi, maxdeltaphi, z, z, maxdeltaz); } + int4 getBinsRect(const int iteration, const Cluster& cls, int layer, float z1, float z2, float maxdeltaz, float maxdeltaphi) const noexcept { return getBinsRect(iteration, layer, cls.phi, maxdeltaphi, z1, z2, maxdeltaz); } + const int4 getBinsRect(const int iteration, int layer, float phi, float maxdeltaphi, float z1, float z2, float maxdeltaz) const noexcept; + void setNThreads(int n, std::shared_ptr& arena); int getNThreads() { return mTaskArena->max_concurrency(); } - o2::gpu::GPUChainITS* getChain() const { return mChain; } - // TimeFrame information forwarding virtual int getTFNumberOfClusters() const { return mTimeFrame->getNumberOfClusters(); } virtual int getTFNumberOfTracklets() const { return mTimeFrame->getNumberOfTracklets(); } @@ -96,36 +85,35 @@ class TrackerTraits 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; std::shared_ptr mTaskArena; protected: o2::gpu::GPUChainITS* mChain = nullptr; - TimeFrame* mTimeFrame; + TimeFrame* mTimeFrame; std::vector mTrkParams; float mBz{-999.f}; bool mIsZeroField{false}; }; -template -inline const int4 TrackerTraits::getBinsRect(const int layerIndex, float phi, float maxdeltaphi, float z1, float z2, float maxdeltaz) const noexcept +template +inline const int4 TrackerTraits::getBinsRect(const int iteration, const int layerIndex, float phi, float maxdeltaphi, float z1, float z2, float maxdeltaz) const noexcept { const float zRangeMin = o2::gpu::GPUCommonMath::Min(z1, z2) - maxdeltaz; const float phiRangeMin = (maxdeltaphi > o2::constants::math::PI) ? 0.f : phi - maxdeltaphi; const float zRangeMax = o2::gpu::GPUCommonMath::Max(z1, z2) + maxdeltaz; const float phiRangeMax = (maxdeltaphi > o2::constants::math::PI) ? o2::constants::math::TwoPI : phi + maxdeltaphi; - if (zRangeMax < -mTrkParams[0].LayerZ[layerIndex] || - zRangeMin > mTrkParams[0].LayerZ[layerIndex] || zRangeMin > zRangeMax) { + if (zRangeMax < -mTrkParams[iteration].LayerZ[layerIndex] || + zRangeMin > mTrkParams[iteration].LayerZ[layerIndex] || zRangeMin > zRangeMax) { return getEmptyBinsRect(); } - const IndexTableUtilsN& utils{mTimeFrame->mIndexTableUtils}; + const IndexTableUtilsN& utils{mTimeFrame->getIndexTableUtils()}; return int4{o2::gpu::GPUCommonMath::Max(0, utils.getZBinIndex(layerIndex, zRangeMin)), utils.getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMin)), - o2::gpu::GPUCommonMath::Min(mTrkParams[0].ZBins - 1, utils.getZBinIndex(layerIndex, zRangeMax)), // /!\ trkParams can potentially change across iterations + o2::gpu::GPUCommonMath::Min(mTrkParams[iteration].ZBins - 1, utils.getZBinIndex(layerIndex, zRangeMax)), utils.getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMax))}; } diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h index 0529bd53f2073..e77200a1432d1 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h @@ -22,10 +22,9 @@ namespace o2::its struct VertexerParamConfig : public o2::conf::ConfigurableParamHelper { bool saveTimeBenchmarks = false; // dump metrics on file - int nIterations = 1; // Number of vertexing passes to perform. - int vertPerRofThreshold = 0; // Maximum number of vertices per ROF to trigger second a iteration. - bool allowSingleContribClusters = false; // attempt to find vertices in case of a single tracklet found. - int deltaRof = 0; // Number of ROFs to be considered for the vertexing. + int nIterations = 1; // Number of vertexing passes to perform. + int vertPerRofThreshold = 0; // Maximum number of vertices per ROF to trigger second a iteration. + int deltaRof = 0; // Number of ROFs to be considered for the vertexing. // geometrical cuts for tracklet selection float zCut = 0.002f; @@ -48,8 +47,7 @@ struct VertexerParamConfig : public o2::conf::ConfigurableParamHelper0, otherwise use code defaults uint8_t startLayerMask[MaxIter] = {}; // mask of start layer for this iteration (if >0) float minPtIterLgt[MaxIter * (MaxTrackLength - MinTrackLength + 1)] = {}; // min.pT for given track length at this iteration, used only if >0, otherwise use code defaults @@ -80,16 +78,8 @@ struct TrackerParamConfig : public o2::conf::ConfigurableParamHelper 0 off - float trackFollowerNSigmaZ = 1.f; // sigma in z-cut for track-following search rectangle - float trackFollowerNSigmaPhi = 1.f; // sigma in phi-cut for track-following search rectangle - float cellsPerClusterLimit = -1.f; - float trackletsPerClusterLimit = -1.f; - int findShortTracks = -1; - int nROFsPerIterations = 0; // size of the slice of ROFs to be processed at a time, preferably integer divisors of nROFs per TF, to balance the iterations. - int nOrbitsPerIterations = 0; // not implemented: size of the slice of ROFs to be processed at a time, computed using the number of ROFs per orbit. + float diamondPos[3] = {0.f, 0.f, 0.f}; // override the position of the vertex + bool useDiamond = false; // enable overriding the vertex position bool perPrimaryVertexProcessing = false; // perform the full tracking considering the vertex hypotheses one at the time. bool saveTimeBenchmarks = false; // dump metrics on file bool overrideBeamEstimation = false; // use beam position from meanVertex CCDB object @@ -99,13 +89,13 @@ struct TrackerParamConfig : public o2::conf::ConfigurableParamHelper::max(); bool dropTFUponFailure = false; - bool fataliseUponFailure = true; // granular management of the fatalisation in async mode + bool fataliseUponFailure = true; // granular management of the fatalisation in async mode bool allowSharingFirstCluster = false; // allow first cluster sharing among tracks O2ParamDef(TrackerParamConfig, "ITSCATrackerParam"); diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h index a882ca9b779c4..ac4b99a0a8cd8 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingInterface.h @@ -43,9 +43,11 @@ class ITSTrackingInterface public: ITSTrackingInterface(bool isMC, + bool doStag, int trgType, const bool overrBeamEst) : mIsMC{isMC}, + mDoStaggering(doStag), mUseTriggers{trgType}, mOverrideBeamEstimation{overrBeamEst} {} @@ -81,13 +83,16 @@ class ITSTrackingInterface virtual void loadROF(gsl::span& trackROFspan, gsl::span clusters, gsl::span::iterator& pattIt, + int layer, const dataformats::MCTruthContainer* mcLabels); private: bool mIsMC = false; + bool mDoStaggering = false; bool mRunVertexer = true; bool mCosmicsProcessing = false; int mUseTriggers = 0; + std::vector mFilter; TrackingMode::Type mMode = TrackingMode::Unset; bool mOverrideBeamEstimation = false; const o2::itsmft::TopologyDictionary* mDict = nullptr; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracklet.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracklet.h index e6c9db55198a3..d93a5e1c7d70e 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracklet.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracklet.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. // @@ -17,6 +17,7 @@ #define TRACKINGITS_INCLUDE_TRACKLET_H_ #include "ITStracking/Constants.h" +#include "DataFormatsITS/TimeEstBC.h" #include "ITStracking/Cluster.h" #include "GPUCommonRtypes.h" #include "GPUCommonMath.h" @@ -35,51 +36,50 @@ namespace o2::its struct Tracklet final { GPUhdDefault() Tracklet() = default; - GPUhdi() Tracklet(const int, const int, const Cluster&, const Cluster&, short rof0, short rof1); - GPUhdi() Tracklet(const int, const int, float tanL, float phi, short rof0, short rof1); + GPUhdi() Tracklet(const int, const int, const Cluster&, const Cluster&, const TimeEstBC& t); + GPUhdi() Tracklet(const int, const int, float tanL, float phi, const TimeEstBC& t); GPUhdDefault() bool operator==(const Tracklet&) const = default; GPUhdi() unsigned char isEmpty() const { return firstClusterIndex < 0 || secondClusterIndex < 0; } - GPUhdi() auto getMinRof() const noexcept { return o2::gpu::CAMath::Min(rof[0], rof[1]); } - GPUhdi() auto getMaxRof() const noexcept { return o2::gpu::CAMath::Max(rof[0], rof[1]); } - GPUhdi() auto getDeltaRof() const { return rof[1] - rof[0]; } - GPUhdi() auto getSpanRof(const Tracklet& o) const noexcept { return o2::gpu::CAMath::Max(getMaxRof(), o.getMaxRof()) - o2::gpu::CAMath::Min(getMinRof(), o.getMinRof()); } + GPUhdi() bool isCompatible(const Tracklet& o) const { return mTime.isCompatible(o.mTime); } GPUhdi() unsigned char operator<(const Tracklet&) const; GPUhd() void print() const { - printf("TRKLT: fClIdx:%d fROF:%d sClIdx:%d sROF:%d (DROF:%d) tgl=%f phi=%f\n", firstClusterIndex, rof[0], secondClusterIndex, rof[1], getDeltaRof(), tanLambda, phi); + LOGP(info, "TRKLT: fClIdx:{} sClIdx:{} ts:{}+/-{} TgL={} Phi={}", firstClusterIndex, secondClusterIndex, mTime.getTimeStamp(), mTime.getTimeStampError(), tanLambda, phi); } + GPUhd() auto& getTimeStamp() noexcept { return mTime; } + GPUhd() const auto& getTimeStamp() const noexcept { return mTime; } int firstClusterIndex{constants::UnusedIndex}; int secondClusterIndex{constants::UnusedIndex}; float tanLambda{-999}; float phi{-999}; - short rof[2] = {constants::UnusedIndex, constants::UnusedIndex}; + TimeEstBC mTime; ClassDefNV(Tracklet, 1); }; GPUhdi() Tracklet::Tracklet(const int firstClusterOrderingIndex, const int secondClusterOrderingIndex, - const Cluster& firstCluster, const Cluster& secondCluster, short rof0 = -1, short rof1 = -1) - : firstClusterIndex{firstClusterOrderingIndex}, - secondClusterIndex{secondClusterOrderingIndex}, - tanLambda{(firstCluster.zCoordinate - secondCluster.zCoordinate) / - (firstCluster.radius - secondCluster.radius)}, - phi{o2::gpu::GPUCommonMath::ATan2(firstCluster.yCoordinate - secondCluster.yCoordinate, - firstCluster.xCoordinate - secondCluster.xCoordinate)}, - rof{static_cast(rof0), static_cast(rof1)} + const Cluster& firstCluster, const Cluster& secondCluster, const TimeEstBC& t) + : firstClusterIndex(firstClusterOrderingIndex), + secondClusterIndex(secondClusterOrderingIndex), + tanLambda((firstCluster.zCoordinate - secondCluster.zCoordinate) / + (firstCluster.radius - secondCluster.radius)), + phi(o2::gpu::GPUCommonMath::ATan2(firstCluster.yCoordinate - secondCluster.yCoordinate, + firstCluster.xCoordinate - secondCluster.xCoordinate)), + mTime(t) { // Nothing to do } -GPUhdi() Tracklet::Tracklet(const int idx0, const int idx1, float tanL, float phi, short rof0, short rof1) - : firstClusterIndex{idx0}, - secondClusterIndex{idx1}, - tanLambda{tanL}, - phi{phi}, - rof{static_cast(rof0), static_cast(rof1)} +GPUhdi() Tracklet::Tracklet(const int idx0, const int idx1, float tanL, float phi, const TimeEstBC& t) + : firstClusterIndex(idx0), + secondClusterIndex(idx1), + tanLambda(tanL), + phi(phi), + mTime(t) { // Nothing to do } diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h index d66bcd6ee2358..77218754dbda3 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h @@ -35,11 +35,11 @@ namespace o2::its { -template +template class Vertexer { - using TimeFrameN = TimeFrame; - using VertexerTraitsN = VertexerTraits; + using TimeFrameN = TimeFrame; + using VertexerTraitsN = VertexerTraits; using LogFunc = std::function; public: @@ -54,9 +54,6 @@ class Vertexer const auto& getParameters() const noexcept { return mVertParams; } void setMemoryPool(std::shared_ptr pool) { mMemoryPool = pool; } - std::vector exportVertices(); - VertexerTraitsN* getTraits() const { return mTraits; }; - float clustersToVertices(LogFunc = [](const std::string& s) { std::cout << s << '\n'; }); void filterMCTracklets(); @@ -86,6 +83,8 @@ class Vertexer template void initialiseTimeFrame(T&&... args); + void sortVertices(); + // Utils template float evaluateTask(void (Vertexer::*task)(T...), std::string_view taskName, int iteration, LogFunc& logger, T&&... args); @@ -118,9 +117,9 @@ class Vertexer static constexpr std::array StateNames{"Initialisation", "Tracklet finding", "Tracklet validation", "Vertex finding", "Truth seeding"}; }; -template +template template -float Vertexer::evaluateTask(void (Vertexer::*task)(T...), std::string_view taskName, int iteration, LogFunc& logger, T&&... args) +float Vertexer::evaluateTask(void (Vertexer::*task)(T...), std::string_view taskName, int iteration, LogFunc& logger, T&&... args) { float diff{0.f}; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h index b1422d66e12df..02ecbe2be8eea 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h @@ -43,11 +43,11 @@ class MCCompLabel; namespace its { -template +template class VertexerTraits { - using IndexTableUtilsN = IndexTableUtils; - using TimeFrameN = TimeFrame; + using IndexTableUtilsN = IndexTableUtils; + using TimeFrameN = TimeFrame; public: VertexerTraits() = default; @@ -68,7 +68,7 @@ class VertexerTraits virtual void computeTrackletMatching(const int iteration = 0); virtual void computeVertices(const int iteration = 0); virtual void adoptTimeFrame(TimeFrameN* tf) noexcept { mTimeFrame = tf; } - virtual void updateVertexingParameters(const std::vector& vrtPar, const TimeFrameGPUParameters& gpuTfPar); + virtual void updateVertexingParameters(const std::vector& vrtPar); // truth tracking void addTruthSeedingVertices(); @@ -84,7 +84,7 @@ class VertexerTraits virtual bool usesMemoryPool() const noexcept { return true; } void setMemoryPool(std::shared_ptr pool) { mMemoryPool = pool; } - static std::pair computeMain(const bounded_vector& elements) + static VertexLabel computeMain(const bounded_vector& elements) { // we only care about the source&event of the tracks, not the trackId auto composeVtxLabel = [](const o2::MCCompLabel& lbl) -> o2::MCCompLabel { @@ -114,28 +114,23 @@ class VertexerTraits private: std::shared_ptr mMemoryPool; std::shared_ptr mTaskArena; - - // debug output - void debugComputeTracklets(int iteration); - void debugComputeTrackletMatching(int iteration); - void debugComputeVertices(int iteration); }; -template -inline void VertexerTraits::initialise(const TrackingParameters& trackingParams, const int iteration) +template +inline void VertexerTraits::initialise(const TrackingParameters& trackingParams, const int iteration) { mTimeFrame->initialise(0, trackingParams, 3, (bool)(!iteration)); // iteration for initialisation must be 0 for correctly resetting the frame, we need to pass the non-reset flag for vertices as well, tho. } -template -GPUhdi() const int2 VertexerTraits::getPhiBins(float phi, float dPhi, const IndexTableUtilsN& utils) +template +GPUhdi() const int2 VertexerTraits::getPhiBins(float phi, float dPhi, const IndexTableUtilsN& utils) { return int2{utils.getPhiBinIndex(math_utils::getNormalizedPhi(phi - dPhi)), utils.getPhiBinIndex(math_utils::getNormalizedPhi(phi + dPhi))}; } -template -GPUhdi() const int4 VertexerTraits::getBinsRect(const Cluster& currentCluster, const int layerIndex, +template +GPUhdi() const int4 VertexerTraits::getBinsRect(const Cluster& currentCluster, const int layerIndex, const float directionZIntersection, float maxdeltaz, float maxdeltaphi, const IndexTableUtilsN& utils) { @@ -155,8 +150,8 @@ GPUhdi() const int4 VertexerTraits::getBinsRect(const Cluster& currentC utils.getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMax))}; } -template -GPUhdi() const int4 VertexerTraits::getBinsRect(const Cluster& currentCluster, const int layerIndex, +template +GPUhdi() const int4 VertexerTraits::getBinsRect(const Cluster& currentCluster, const int layerIndex, const float directionZIntersection, float maxdeltaz, float maxdeltaphi) { return VertexerTraits::getBinsRect(currentCluster, layerIndex, directionZIntersection, maxdeltaz, maxdeltaphi, mIndexTableUtils); diff --git a/Detectors/ITSMFT/ITS/tracking/src/ClusterLines.cxx b/Detectors/ITSMFT/ITS/tracking/src/ClusterLines.cxx index 1a0fa1d3908a4..f561fe0436c4a 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/ClusterLines.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/ClusterLines.cxx @@ -10,385 +10,176 @@ // or submit itself to any jurisdiction. #include -#include +#include "Framework/Logger.h" #include "ITStracking/ClusterLines.h" -namespace o2 -{ -namespace its +namespace o2::its { -Line::Line(std::array firstPoint, std::array secondPoint) +Line::Line(const Tracklet& tracklet, const Cluster* innerClusters, const Cluster* outerClusters) : mTime(tracklet.mTime) { - for (int index{0}; index < 3; ++index) { - originPoint[index] = firstPoint.data()[index]; - cosinesDirector[index] = secondPoint[index] - firstPoint[index]; - } + const auto& inner = innerClusters[tracklet.firstClusterIndex]; + const auto& outer = outerClusters[tracklet.secondClusterIndex]; + + originPoint = SVector3f(inner.xCoordinate, inner.yCoordinate, inner.zCoordinate); + cosinesDirector = SVector3f(outer.xCoordinate - inner.xCoordinate, + outer.yCoordinate - inner.yCoordinate, + outer.zCoordinate - inner.zCoordinate); + cosinesDirector /= std::sqrt(ROOT::Math::Dot(cosinesDirector, cosinesDirector)); +} - float inverseNorm{1.f / o2::gpu::CAMath::Sqrt(cosinesDirector[0] * cosinesDirector[0] + cosinesDirector[1] * cosinesDirector[1] + - cosinesDirector[2] * cosinesDirector[2])}; - for (int index{0}; index < 3; ++index) { - cosinesDirector[index] *= inverseNorm; - } +float Line::getDistance2FromPoint(const Line& line, const std::array& point) +{ + const SVector3f p(point.data(), 3); + const SVector3f delta = p - line.originPoint; + const float proj = ROOT::Math::Dot(delta, line.cosinesDirector); + const SVector3f residual = delta - proj * line.cosinesDirector; + return ROOT::Math::Dot(residual, residual); } -bool Line::areParallel(const Line& firstLine, const Line& secondLine, const float precision) +float Line::getDistanceFromPoint(const Line& line, const std::array& point) { - float crossProdX{firstLine.cosinesDirector[1] * secondLine.cosinesDirector[2] - - firstLine.cosinesDirector[2] * secondLine.cosinesDirector[1]}; - float module{std::abs(firstLine.cosinesDirector[1] * secondLine.cosinesDirector[2]) + - std::abs(firstLine.cosinesDirector[2] * secondLine.cosinesDirector[1])}; - if (std::abs(crossProdX) > precision * module) { - return false; - } + return std::sqrt(getDistance2FromPoint(line, point)); +} - float crossProdY{-firstLine.cosinesDirector[0] * secondLine.cosinesDirector[2] + - firstLine.cosinesDirector[2] * secondLine.cosinesDirector[0]}; - module = std::abs(firstLine.cosinesDirector[0] * secondLine.cosinesDirector[2]) + - std::abs(firstLine.cosinesDirector[2] * secondLine.cosinesDirector[0]); - if (std::abs(crossProdY) > precision * module) { - return false; +float Line::getDCA2(const Line& firstLine, const Line& secondLine, const float precision) +{ + const SVector3f n = ROOT::Math::Cross(firstLine.cosinesDirector, secondLine.cosinesDirector); + const float norm2 = ROOT::Math::Dot(n, n); + + if (norm2 <= precision * precision) { + // lines are parallel, fall back to point-to-line distance + const SVector3f d = secondLine.originPoint - firstLine.originPoint; + const float proj = ROOT::Math::Dot(d, firstLine.cosinesDirector); + const SVector3f residual = d - proj * firstLine.cosinesDirector; + return ROOT::Math::Dot(residual, residual); } - float crossProdZ = firstLine.cosinesDirector[0] * secondLine.cosinesDirector[1] - - firstLine.cosinesDirector[1] * secondLine.cosinesDirector[0]; - module = std::abs(firstLine.cosinesDirector[0] * secondLine.cosinesDirector[1]) + - std::abs(firstLine.cosinesDirector[1] * secondLine.cosinesDirector[0]); - if (std::abs(crossProdZ) > precision * module) { - return false; - } + const SVector3f delta = secondLine.originPoint - firstLine.originPoint; + const float numerator = ROOT::Math::Dot(delta, n); + return (numerator * numerator) / norm2; +} - return true; +float Line::getDCA(const Line& firstLine, const Line& secondLine, const float precision) +{ + return std::sqrt(getDCA2(firstLine, secondLine, precision)); } -std::array Line::getDCAComponents(const Line& line, const std::array point) +Line::SMatrix3f Line::getDCAComponents(const Line& line, const std::array& point) { - std::array components{0., 0., 0., 0., 0., 0.}; - float cdelta{0.}; - for (int i{0}; i < 3; ++i) { - cdelta -= line.cosinesDirector[i] * (line.originPoint[i] - point[i]); - } + const SVector3f p(point.data(), 3); + const SVector3f delta = line.originPoint - p; + const float proj = ROOT::Math::Dot(line.cosinesDirector, delta); + const SVector3f residual = delta - proj * line.cosinesDirector; + + // symmetric 3x3: diagonal = residual components, off-diagonal = 2D projected distances + SMatrix3f m; + m(0, 0) = residual(0); + m(1, 1) = residual(1); + m(2, 2) = residual(2); + m(0, 1) = std::hypot(m(0, 0), m(1, 1)); + m(0, 2) = std::hypot(m(0, 0), m(2, 2)); + m(1, 2) = std::hypot(m(1, 1), m(2, 2)); + return m; +} - components[0] = line.originPoint[0] - point[0] + line.cosinesDirector[0] * cdelta; - components[3] = line.originPoint[1] - point[1] + line.cosinesDirector[1] * cdelta; - components[5] = line.originPoint[2] - point[2] + line.cosinesDirector[2] * cdelta; - components[1] = o2::gpu::CAMath::Sqrt(components[0] * components[0] + components[3] * components[3]); - components[2] = o2::gpu::CAMath::Sqrt(components[0] * components[0] + components[5] * components[5]); - components[4] = o2::gpu::CAMath::Sqrt(components[3] * components[3] + components[5] * components[5]); +bool Line::isEmpty() const noexcept +{ + return ROOT::Math::Dot(originPoint, originPoint) == 0.f && + ROOT::Math::Dot(cosinesDirector, cosinesDirector) == 0.f; +} - return components; +void Line::print() const +{ + LOGP(info, "\tLine: originPoint = ({}, {}, {}), cosinesDirector = ({}, {}, {}) ts={}+-{}", + originPoint(0), originPoint(1), originPoint(2), + cosinesDirector(0), cosinesDirector(1), cosinesDirector(2), + mTime.getTimeStamp(), mTime.getTimeStampError()); } -ClusterLines::ClusterLines(const int firstLabel, const Line& firstLine, const int secondLabel, const Line& secondLine, - const bool weight) +// Accumulate the weighted normal equation contributions (A matrix and B vector) +// from a single line into the running sums. The covariance is assumed to be +// diagonal and uniform ({1,1,1}) so the weights simplify accordingly. +// The A matrix entry (i,j) = (delta_ij - d_i*d_j) / det, and the B vector +// entry b_i = sum_j d_j*(d_j*o_i - d_i*o_j) / det, where d = cosinesDirector +// and o = originPoint. +void ClusterLines::accumulate(const Line& line) +{ + const ROOT::Math::SVector d(line.cosinesDirector(0), line.cosinesDirector(1), line.cosinesDirector(2)); + const ROOT::Math::SVector o(line.originPoint(0), line.originPoint(1), line.originPoint(2)); + + // == 1 for normalised directors, kept for generality + const double det = ROOT::Math::Dot(d, d); + // A matrix (symmetric): A_ij = (delta_ij * |d|^2 - d_i * d_j) / det + for (int i = 0; i < 3; ++i) { + for (int j = i; j < 3; ++j) { + mAMatrix(i, j) += ((i == j ? det : 0.) - d(i) * d(j)) / det; + } + } + + // B vector: b_i = (d_i * dot(d,o) - |d|^2 * o_i) / det + const double dDotO = ROOT::Math::Dot(d, o); + for (int i = 0; i < 3; ++i) { + mBMatrix(i) += (d(i) * dDotO - det * o(i)) / det; + } +} + +ClusterLines::ClusterLines(const int firstLabel, const Line& firstLine, const int secondLabel, const Line& secondLine) : mTime(firstLine.mTime) { - updateROFPoll(firstLine); - updateROFPoll(secondLine); + mTime += secondLine.mTime; mLabels.push_back(firstLabel); if (secondLabel > 0) { mLabels.push_back(secondLabel); // don't add info in case of beamline used } - std::array covarianceFirst{1., 1., 1.}; - std::array covarianceSecond{1., 1., 1.}; - - // for (int i{0}; i < 6; ++i) { - // mWeightMatrix[i] = firstLine.weightMatrix[i] + secondLine.weightMatrix[i]; - // } - - float determinantFirst = - firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[0] * covarianceFirst[1] + - firstLine.cosinesDirector[1] * firstLine.cosinesDirector[1] * covarianceFirst[0] * covarianceFirst[2] + - firstLine.cosinesDirector[0] * firstLine.cosinesDirector[0] * covarianceFirst[1] * covarianceFirst[2]; - float determinantSecond = - secondLine.cosinesDirector[2] * secondLine.cosinesDirector[2] * covarianceSecond[0] * covarianceSecond[1] + - secondLine.cosinesDirector[1] * secondLine.cosinesDirector[1] * covarianceSecond[0] * covarianceSecond[2] + - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[0] * covarianceSecond[1] * covarianceSecond[2]; - - mAMatrix[0] = (firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[1] + - firstLine.cosinesDirector[1] * firstLine.cosinesDirector[1] * covarianceFirst[2]) / - determinantFirst + - (secondLine.cosinesDirector[2] * secondLine.cosinesDirector[2] * covarianceSecond[1] + - secondLine.cosinesDirector[1] * secondLine.cosinesDirector[1] * covarianceSecond[2]) / - determinantSecond; - - mAMatrix[1] = -firstLine.cosinesDirector[0] * firstLine.cosinesDirector[1] * covarianceFirst[2] / determinantFirst - - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[1] * covarianceSecond[2] / determinantSecond; - - mAMatrix[2] = -firstLine.cosinesDirector[0] * firstLine.cosinesDirector[2] * covarianceFirst[1] / determinantFirst - - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[2] * covarianceSecond[1] / determinantSecond; - - mAMatrix[3] = (firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[0] + - firstLine.cosinesDirector[0] * firstLine.cosinesDirector[0] * covarianceFirst[2]) / - determinantFirst + - (secondLine.cosinesDirector[2] * secondLine.cosinesDirector[2] * covarianceSecond[0] + - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[0] * covarianceSecond[2]) / - determinantSecond; - - mAMatrix[4] = -firstLine.cosinesDirector[1] * firstLine.cosinesDirector[2] * covarianceFirst[0] / determinantFirst - - secondLine.cosinesDirector[1] * secondLine.cosinesDirector[2] * covarianceSecond[0] / determinantSecond; - - mAMatrix[5] = (firstLine.cosinesDirector[1] * firstLine.cosinesDirector[1] * covarianceFirst[0] + - firstLine.cosinesDirector[0] * firstLine.cosinesDirector[0] * covarianceFirst[1]) / - determinantFirst + - (secondLine.cosinesDirector[1] * secondLine.cosinesDirector[1] * covarianceSecond[0] + - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[0] * covarianceSecond[1]) / - determinantSecond; - - mBMatrix[0] = - (firstLine.cosinesDirector[1] * covarianceFirst[2] * (-firstLine.cosinesDirector[1] * firstLine.originPoint[0] + firstLine.cosinesDirector[0] * firstLine.originPoint[1]) + - firstLine.cosinesDirector[2] * covarianceFirst[1] * (-firstLine.cosinesDirector[2] * firstLine.originPoint[0] + firstLine.cosinesDirector[0] * firstLine.originPoint[2])) / - determinantFirst; - - mBMatrix[0] += - (secondLine.cosinesDirector[1] * covarianceSecond[2] * (-secondLine.cosinesDirector[1] * secondLine.originPoint[0] + secondLine.cosinesDirector[0] * secondLine.originPoint[1]) + - secondLine.cosinesDirector[2] * covarianceSecond[1] * - (-secondLine.cosinesDirector[2] * secondLine.originPoint[0] + - secondLine.cosinesDirector[0] * secondLine.originPoint[2])) / - determinantSecond; - - mBMatrix[1] = - (firstLine.cosinesDirector[0] * covarianceFirst[2] * (-firstLine.cosinesDirector[0] * firstLine.originPoint[1] + firstLine.cosinesDirector[1] * firstLine.originPoint[0]) + - firstLine.cosinesDirector[2] * covarianceFirst[0] * (-firstLine.cosinesDirector[2] * firstLine.originPoint[1] + firstLine.cosinesDirector[1] * firstLine.originPoint[2])) / - determinantFirst; - - mBMatrix[1] += - (secondLine.cosinesDirector[0] * covarianceSecond[2] * (-secondLine.cosinesDirector[0] * secondLine.originPoint[1] + secondLine.cosinesDirector[1] * secondLine.originPoint[0]) + - secondLine.cosinesDirector[2] * covarianceSecond[0] * - (-secondLine.cosinesDirector[2] * secondLine.originPoint[1] + - secondLine.cosinesDirector[1] * secondLine.originPoint[2])) / - determinantSecond; - - mBMatrix[2] = - (firstLine.cosinesDirector[0] * covarianceFirst[1] * (-firstLine.cosinesDirector[0] * firstLine.originPoint[2] + firstLine.cosinesDirector[2] * firstLine.originPoint[0]) + - firstLine.cosinesDirector[1] * covarianceFirst[0] * (-firstLine.cosinesDirector[1] * firstLine.originPoint[2] + firstLine.cosinesDirector[2] * firstLine.originPoint[1])) / - determinantFirst; - - mBMatrix[2] += - (secondLine.cosinesDirector[0] * covarianceSecond[1] * (-secondLine.cosinesDirector[0] * secondLine.originPoint[2] + secondLine.cosinesDirector[2] * secondLine.originPoint[0]) + - secondLine.cosinesDirector[1] * covarianceSecond[0] * - (-secondLine.cosinesDirector[1] * secondLine.originPoint[2] + - secondLine.cosinesDirector[2] * secondLine.originPoint[1])) / - determinantSecond; - + accumulate(firstLine); + accumulate(secondLine); computeClusterCentroid(); - // RMS2 + // RMS2: running mean update mRMS2 = Line::getDCAComponents(firstLine, mVertex); - const std::array tmpRMS2Line2 = Line::getDCAComponents(secondLine, mVertex); - std::transform(mRMS2.begin(), mRMS2.end(), tmpRMS2Line2.begin(), mRMS2.begin(), [&](const float a, const float b) { return a + (b - a) / mLabels.size(); }); + const auto tmpRMS2 = Line::getDCAComponents(secondLine, mVertex); + mRMS2 += (tmpRMS2 - mRMS2) * (1.f / static_cast(getSize())); // AvgDistance2 - mAvgDistance2 = std::move(Line::getDistanceFromPoint(firstLine, mVertex) * Line::getDistanceFromPoint(firstLine, mVertex)); - mAvgDistance2 += (Line::getDistanceFromPoint(secondLine, mVertex) * Line::getDistanceFromPoint(secondLine, mVertex) - mAvgDistance2) / mLabels.size(); + mAvgDistance2 = Line::getDistance2FromPoint(firstLine, mVertex); + mAvgDistance2 += (Line::getDistance2FromPoint(secondLine, mVertex) - mAvgDistance2) / (float)getSize(); } -ClusterLines::ClusterLines(const Line& firstLine, const Line& secondLine) -{ - - std::array covarianceFirst{1., 1., 1.}; - std::array covarianceSecond{1., 1., 1.}; - updateROFPoll(firstLine); - updateROFPoll(secondLine); - // for (int i{0}; i < 6; ++i) { - // mWeightMatrix[i] = firstLine.weightMatrix[i] + secondLine.weightMatrix[i]; - // } - - float determinantFirst = - firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[0] * covarianceFirst[1] + - firstLine.cosinesDirector[1] * firstLine.cosinesDirector[1] * covarianceFirst[0] * covarianceFirst[2] + - firstLine.cosinesDirector[0] * firstLine.cosinesDirector[0] * covarianceFirst[1] * covarianceFirst[2]; - float determinantSecond = - secondLine.cosinesDirector[2] * secondLine.cosinesDirector[2] * covarianceSecond[0] * covarianceSecond[1] + - secondLine.cosinesDirector[1] * secondLine.cosinesDirector[1] * covarianceSecond[0] * covarianceSecond[2] + - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[0] * covarianceSecond[1] * covarianceSecond[2]; - - mAMatrix[0] = (firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[1] + - firstLine.cosinesDirector[1] * firstLine.cosinesDirector[1] * covarianceFirst[2]) / - determinantFirst + - (secondLine.cosinesDirector[2] * secondLine.cosinesDirector[2] * covarianceSecond[1] + - secondLine.cosinesDirector[1] * secondLine.cosinesDirector[1] * covarianceSecond[2]) / - determinantSecond; - - mAMatrix[1] = -firstLine.cosinesDirector[0] * firstLine.cosinesDirector[1] * covarianceFirst[2] / determinantFirst - - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[1] * covarianceSecond[2] / determinantSecond; - - mAMatrix[2] = -firstLine.cosinesDirector[0] * firstLine.cosinesDirector[2] * covarianceFirst[1] / determinantFirst - - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[2] * covarianceSecond[1] / determinantSecond; - - mAMatrix[3] = (firstLine.cosinesDirector[2] * firstLine.cosinesDirector[2] * covarianceFirst[0] + - firstLine.cosinesDirector[0] * firstLine.cosinesDirector[0] * covarianceFirst[2]) / - determinantFirst + - (secondLine.cosinesDirector[2] * secondLine.cosinesDirector[2] * covarianceSecond[0] + - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[0] * covarianceSecond[2]) / - determinantSecond; - - mAMatrix[4] = -firstLine.cosinesDirector[1] * firstLine.cosinesDirector[2] * covarianceFirst[0] / determinantFirst - - secondLine.cosinesDirector[1] * secondLine.cosinesDirector[2] * covarianceSecond[0] / determinantSecond; - - mAMatrix[5] = (firstLine.cosinesDirector[1] * firstLine.cosinesDirector[1] * covarianceFirst[0] + - firstLine.cosinesDirector[0] * firstLine.cosinesDirector[0] * covarianceFirst[1]) / - determinantFirst + - (secondLine.cosinesDirector[1] * secondLine.cosinesDirector[1] * covarianceSecond[0] + - secondLine.cosinesDirector[0] * secondLine.cosinesDirector[0] * covarianceSecond[1]) / - determinantSecond; - - mBMatrix[0] = - (firstLine.cosinesDirector[1] * covarianceFirst[2] * (-firstLine.cosinesDirector[1] * firstLine.originPoint[0] + firstLine.cosinesDirector[0] * firstLine.originPoint[1]) + - firstLine.cosinesDirector[2] * covarianceFirst[1] * (-firstLine.cosinesDirector[2] * firstLine.originPoint[0] + firstLine.cosinesDirector[0] * firstLine.originPoint[2])) / - determinantFirst; - - mBMatrix[0] += - (secondLine.cosinesDirector[1] * covarianceSecond[2] * (-secondLine.cosinesDirector[1] * secondLine.originPoint[0] + secondLine.cosinesDirector[0] * secondLine.originPoint[1]) + - secondLine.cosinesDirector[2] * covarianceSecond[1] * - (-secondLine.cosinesDirector[2] * secondLine.originPoint[0] + - secondLine.cosinesDirector[0] * secondLine.originPoint[2])) / - determinantSecond; - - mBMatrix[1] = - (firstLine.cosinesDirector[0] * covarianceFirst[2] * (-firstLine.cosinesDirector[0] * firstLine.originPoint[1] + firstLine.cosinesDirector[1] * firstLine.originPoint[0]) + - firstLine.cosinesDirector[2] * covarianceFirst[0] * (-firstLine.cosinesDirector[2] * firstLine.originPoint[1] + firstLine.cosinesDirector[1] * firstLine.originPoint[2])) / - determinantFirst; - - mBMatrix[1] += - (secondLine.cosinesDirector[0] * covarianceSecond[2] * (-secondLine.cosinesDirector[0] * secondLine.originPoint[1] + secondLine.cosinesDirector[1] * secondLine.originPoint[0]) + - secondLine.cosinesDirector[2] * covarianceSecond[0] * - (-secondLine.cosinesDirector[2] * secondLine.originPoint[1] + - secondLine.cosinesDirector[1] * secondLine.originPoint[2])) / - determinantSecond; - - mBMatrix[2] = - (firstLine.cosinesDirector[0] * covarianceFirst[1] * (-firstLine.cosinesDirector[0] * firstLine.originPoint[2] + firstLine.cosinesDirector[2] * firstLine.originPoint[0]) + - firstLine.cosinesDirector[1] * covarianceFirst[0] * (-firstLine.cosinesDirector[1] * firstLine.originPoint[2] + firstLine.cosinesDirector[2] * firstLine.originPoint[1])) / - determinantFirst; - - mBMatrix[2] += - (secondLine.cosinesDirector[0] * covarianceSecond[1] * (-secondLine.cosinesDirector[0] * secondLine.originPoint[2] + secondLine.cosinesDirector[2] * secondLine.originPoint[0]) + - secondLine.cosinesDirector[1] * covarianceSecond[0] * - (-secondLine.cosinesDirector[1] * secondLine.originPoint[2] + - secondLine.cosinesDirector[2] * secondLine.originPoint[1])) / - determinantSecond; - - computeClusterCentroid(); -} - -void ClusterLines::add(const int& lineLabel, const Line& line, const bool& weight) +void ClusterLines::add(const int lineLabel, const Line& line) { + mTime += line.mTime; mLabels.push_back(lineLabel); - updateROFPoll(line); - std::array covariance{1., 1., 1.}; - - // for (int i{0}; i < 6; ++i) { - // mWeightMatrix[i] += line.weightMatrix[i]; - // } - // if(weight) line->GetSigma2P0(covariance); - - double determinant{line.cosinesDirector[2] * line.cosinesDirector[2] * covariance[0] * covariance[1] + - line.cosinesDirector[1] * line.cosinesDirector[1] * covariance[0] * covariance[2] + - line.cosinesDirector[0] * line.cosinesDirector[0] * covariance[1] * covariance[2]}; - - mAMatrix[0] += (line.cosinesDirector[2] * line.cosinesDirector[2] * covariance[1] + - line.cosinesDirector[1] * line.cosinesDirector[1] * covariance[2]) / - determinant; - mAMatrix[1] += -line.cosinesDirector[0] * line.cosinesDirector[1] * covariance[2] / determinant; - mAMatrix[2] += -line.cosinesDirector[0] * line.cosinesDirector[2] * covariance[1] / determinant; - mAMatrix[3] += (line.cosinesDirector[2] * line.cosinesDirector[2] * covariance[0] + - line.cosinesDirector[0] * line.cosinesDirector[0] * covariance[2]) / - determinant; - mAMatrix[4] += -line.cosinesDirector[1] * line.cosinesDirector[2] * covariance[0] / determinant; - mAMatrix[5] += (line.cosinesDirector[1] * line.cosinesDirector[1] * covariance[0] + - line.cosinesDirector[0] * line.cosinesDirector[0] * covariance[1]) / - determinant; - - mBMatrix[0] += (line.cosinesDirector[1] * covariance[2] * - (-line.cosinesDirector[1] * line.originPoint[0] + line.cosinesDirector[0] * line.originPoint[1]) + - line.cosinesDirector[2] * covariance[1] * - (-line.cosinesDirector[2] * line.originPoint[0] + line.cosinesDirector[0] * line.originPoint[2])) / - determinant; - mBMatrix[1] += (line.cosinesDirector[0] * covariance[2] * - (-line.cosinesDirector[0] * line.originPoint[1] + line.cosinesDirector[1] * line.originPoint[0]) + - line.cosinesDirector[2] * covariance[0] * - (-line.cosinesDirector[2] * line.originPoint[1] + line.cosinesDirector[1] * line.originPoint[2])) / - determinant; - mBMatrix[2] += (line.cosinesDirector[0] * covariance[1] * - (-line.cosinesDirector[0] * line.originPoint[2] + line.cosinesDirector[2] * line.originPoint[0]) + - line.cosinesDirector[1] * covariance[0] * - (-line.cosinesDirector[1] * line.originPoint[2] + line.cosinesDirector[2] * line.originPoint[1])) / - determinant; + accumulate(line); computeClusterCentroid(); - mAvgDistance2 += (Line::getDistanceFromPoint(line, mVertex) * Line::getDistanceFromPoint(line, mVertex) - mAvgDistance2) / mLabels.size(); + mAvgDistance2 += (Line::getDistance2FromPoint(line, mVertex) - mAvgDistance2) / (float)getSize(); } void ClusterLines::computeClusterCentroid() { - - double determinant{mAMatrix[0] * (mAMatrix[3] * mAMatrix[5] - mAMatrix[4] * mAMatrix[4]) - - mAMatrix[1] * (mAMatrix[1] * mAMatrix[5] - mAMatrix[4] * mAMatrix[2]) + - mAMatrix[2] * (mAMatrix[1] * mAMatrix[4] - mAMatrix[2] * mAMatrix[3])}; - - if (determinant == 0) { + // Solve the 3x3 symmetric linear system AX = -B using SMatrix inversion. + // Invert() returns false if the matrix is singular or ill-conditioned. + SMatrix3 invA{mAMatrix}; + mIsValid = invA.Invert(); + if (!mIsValid) { return; } - mVertex[0] = -(mBMatrix[0] * (mAMatrix[3] * mAMatrix[5] - mAMatrix[4] * mAMatrix[4]) - - mAMatrix[1] * (mBMatrix[1] * mAMatrix[5] - mAMatrix[4] * mBMatrix[2]) + - mAMatrix[2] * (mBMatrix[1] * mAMatrix[4] - mBMatrix[2] * mAMatrix[3])) / - determinant; - mVertex[1] = -(mAMatrix[0] * (mBMatrix[1] * mAMatrix[5] - mBMatrix[2] * mAMatrix[4]) - - mBMatrix[0] * (mAMatrix[1] * mAMatrix[5] - mAMatrix[4] * mAMatrix[2]) + - mAMatrix[2] * (mAMatrix[1] * mBMatrix[2] - mAMatrix[2] * mBMatrix[1])) / - determinant; - mVertex[2] = -(mAMatrix[0] * (mAMatrix[3] * mBMatrix[2] - mBMatrix[1] * mAMatrix[4]) - - mAMatrix[1] * (mAMatrix[1] * mBMatrix[2] - mBMatrix[1] * mAMatrix[2]) + - mBMatrix[0] * (mAMatrix[1] * mAMatrix[4] - mAMatrix[2] * mAMatrix[3])) / - determinant; -} - -bool ClusterLines::operator==(const ClusterLines& rhs) const -{ - bool retval{true}; - for (auto i{0}; i < 6; ++i) { - retval &= this->mRMS2[i] == rhs.mRMS2[i]; - } - for (auto i{0}; i < 3; ++i) { - retval &= this->mVertex[i] == rhs.mVertex[i]; - } - if (this->mLabels.size() != rhs.mLabels.size()) { - retval = false; - } else { - for (size_t i{0}; i < this->mLabels.size(); ++i) { - retval &= this->mLabels[i] == rhs.mLabels[i]; - } - } - return retval && this->mAvgDistance2 == rhs.mAvgDistance2; + SVector3 result = invA * mBMatrix; + mVertex[0] = static_cast(-result(0)); + mVertex[1] = static_cast(-result(1)); + mVertex[2] = static_cast(-result(2)); } -GPUhdi() void ClusterLines::updateROFPoll(const Line& line) +bool ClusterLines::operator==(const ClusterLines& rhs) const noexcept { - // option 1: Boyer-Moore voting for rof label - if (mROFWeight == 0) { - mROF = line.getMinROF(); - mROFWeight = 1; - } else { - if (mROF == line.getMinROF()) { - mROFWeight++; - } else { - mROFWeight--; - } - } - - // option 2 - // if (mROF == -1) { - // mROF = line.getMinROF(); - // } else { - // if (line.getMinROF() < mROF) { - // mROF = line.getMinROF(); - // } - // } + return mRMS2 == rhs.mRMS2 && + mVertex == rhs.mVertex && + mLabels == rhs.mLabels && + mAvgDistance2 == rhs.mAvgDistance2; } -} // namespace its -} // namespace o2 +} // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx b/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx index 202dc87f04237..c447bb6bcc880 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx @@ -24,8 +24,8 @@ using namespace o2::its; std::string TrackingParameters::asString() const { - std::string str = std::format("NZb:{} NPhB:{} NROFIt:{} DRof:{} PerVtx:{} DropFail:{} ClSh:{} TtklMinPt:{:.2f} MinCl:{}", - ZBins, PhiBins, nROFsPerIterations, DeltaROF, PerPrimaryVertexProcessing, DropTFUponFailure, ClusterSharing, TrackletMinPt, MinTrackLength); + std::string str = std::format("NZb:{} NPhB:{} PerVtx:{} DropFail:{} ClSh:{} TtklMinPt:{:.2f} MinCl:{}", + ZBins, PhiBins, PerPrimaryVertexProcessing, DropTFUponFailure, ClusterSharing, TrackletMinPt, MinTrackLength); bool first = true; for (int il = NLayers; il >= MinTrackLength; il--) { int slot = NLayers - il; @@ -37,9 +37,17 @@ std::string TrackingParameters::asString() const str += std::format("L{}:{:.2f} ", il, MinPt[slot]); } } - str += " SystErrY/Z:"; - for (size_t i = 0; i < SystErrorY2.size(); i++) { - str += std::format("{:.2e}/{:.2e} ", SystErrorY2[i], SystErrorZ2[i]); + if (!SystErrorY2.empty() || !SystErrorZ2.empty()) { + str += " SystErrY/Z:"; + for (size_t i = 0; i < SystErrorY2.size(); i++) { + str += std::format("{:.2e}/{:.2e} ", SystErrorY2[i], SystErrorZ2[i]); + } + } + if (!AddTimeError.empty()) { + str += " AddTimeError:"; + for (size_t i = 0; i < AddTimeError.size(); i++) { + str += std::format("{} ", AddTimeError[i]); + } } if (std::numeric_limits::max() != MaxMemory) { str += std::format(" MemLimit {:.2f} GB", double(MaxMemory) / constants::GB); @@ -49,7 +57,7 @@ std::string TrackingParameters::asString() const std::string VertexingParameters::asString() const { - std::string str = std::format("NZb:{} NPhB:{} DRof:{} ClsCont:{} MaxTrkltCls:{} ZCut:{} PhCut:{}", ZBins, PhiBins, deltaRof, clusterContributorsCut, maxTrackletsPerCluster, zCut, phiCut); + std::string str = std::format("NZb:{} NPhB:{} ClsCont:{} MaxTrkltCls:{} ZCut:{} PhCut:{}", ZBins, PhiBins, clusterContributorsCut, maxTrackletsPerCluster, zCut, phiCut); if (std::numeric_limits::max() != MaxMemory) { str += std::format(" MemLimit {:.2f} GB", double(MaxMemory) / constants::GB); } @@ -126,14 +134,11 @@ std::vector TrackingMode::getTrackingParameters(TrackingMode trackParams[3].MinTrackLength = 4; trackParams[3].TrackletMinPt = 0.1f; trackParams[3].CellDeltaTanLambdaSigma *= 4.; - trackParams[3].DeltaROF = 0; // UPC specific setting } for (size_t ip = 0; ip < trackParams.size(); ip++) { auto& param = trackParams[ip]; param.ZBins = 64; param.PhiBins = 32; - param.CellsPerClusterLimit = 1.e3f; - param.TrackletsPerClusterLimit = 1.e3f; // check if something was overridden via configurable params if (ip < tc.MaxIter) { if (tc.startLayerMask[ip] > 0) { @@ -164,19 +169,12 @@ std::vector TrackingMode::getTrackingParameters(TrackingMode trackParams[0].PVres = 1.e5f; trackParams[0].MaxChi2ClusterAttachment = 60.; trackParams[0].MaxChi2NDF = 40.; - trackParams[0].TrackletsPerClusterLimit = 100.; - trackParams[0].CellsPerClusterLimit = 100.; } else { LOGP(fatal, "Unsupported ITS tracking mode {} ", toString(mode)); } float bFactor = std::abs(o2::base::Propagator::Instance()->getNominalBz()) / 5.0066791; float bFactorTracklets = bFactor < 0.01 ? 1. : bFactor; // for tracklets only - int nROFsPerIterations = tc.nROFsPerIterations > 0 ? tc.nROFsPerIterations : -1; - - if (tc.nOrbitsPerIterations > 0) { - /// code to be used when the number of ROFs per orbit is known, this gets priority over the number of ROFs per iteration - } // global parameters set for every iteration for (auto& p : trackParams) { @@ -212,7 +210,9 @@ std::vector TrackingMode::getTrackingParameters(TrackingMode p.SystErrorZ2[i] = tc.sysErrZ2[i] > 0 ? tc.sysErrZ2[i] : p.SystErrorZ2[i]; } } - p.DeltaROF = tc.deltaRof; + for (int i{0}; i < 7; ++i) { + p.AddTimeError[i] = tc.addTimeError[i]; + } p.DoUPCIteration = tc.doUPCIteration; p.MaxChi2ClusterAttachment = tc.maxChi2ClusterAttachment > 0 ? tc.maxChi2ClusterAttachment : p.MaxChi2ClusterAttachment; p.MaxChi2NDF = tc.maxChi2NDF > 0 ? tc.maxChi2NDF : p.MaxChi2NDF; @@ -222,32 +222,11 @@ std::vector TrackingMode::getTrackingParameters(TrackingMode p.NSigmaCut *= tc.nSigmaCut > 0 ? tc.nSigmaCut : 1.f; p.CellDeltaTanLambdaSigma *= tc.deltaTanLres > 0 ? tc.deltaTanLres : 1.f; p.TrackletMinPt *= tc.minPt > 0 ? tc.minPt : 1.f; - p.nROFsPerIterations = nROFsPerIterations; p.PerPrimaryVertexProcessing = tc.perPrimaryVertexProcessing; for (int iD{0}; iD < 3; ++iD) { p.Diamond[iD] = tc.diamondPos[iD]; } p.UseDiamond = tc.useDiamond; - if (tc.useTrackFollower > 0) { - p.UseTrackFollower = true; - // Bit 0: Allow for mixing of top&bot extension --> implies Bits 1&2 set - // Bit 1: Allow for top extension - // Bit 2: Allow for bot extension - p.UseTrackFollowerMix = ((tc.useTrackFollower & (1 << 0)) != 0); - p.UseTrackFollowerTop = ((tc.useTrackFollower & (1 << 1)) != 0); - p.UseTrackFollowerBot = ((tc.useTrackFollower & (1 << 2)) != 0); - p.TrackFollowerNSigmaCutZ = tc.trackFollowerNSigmaZ; - p.TrackFollowerNSigmaCutPhi = tc.trackFollowerNSigmaPhi; - } - if (tc.cellsPerClusterLimit >= 0) { - p.CellsPerClusterLimit = tc.cellsPerClusterLimit; - } - if (tc.trackletsPerClusterLimit >= 0) { - p.TrackletsPerClusterLimit = tc.trackletsPerClusterLimit; - } - if (tc.findShortTracks >= 0) { - p.FindShortTracks = tc.findShortTracks; - } } if (trackParams.size() > tc.nIterations) { @@ -265,8 +244,6 @@ std::vector TrackingMode::getVertexingParameters(TrackingMo vertParams.resize(2); // The number of actual iterations will be set as a configKeyVal to allow for pp/PbPb choice vertParams[1].phiCut = 0.015f; vertParams[1].tanLambdaCut = 0.015f; - vertParams[1].vertPerRofThreshold = 0; - vertParams[1].deltaRof = 0; } else if (mode == TrackingMode::Sync) { vertParams.resize(1); } else if (mode == TrackingMode::Cosmics) { @@ -282,8 +259,6 @@ std::vector TrackingMode::getVertexingParameters(TrackingMo p.MaxMemory = vc.maxMemory; p.DropTFUponFailure = vc.dropTFUponFailure; p.nIterations = vc.nIterations; - p.deltaRof = vc.deltaRof; - p.allowSingleContribClusters = vc.allowSingleContribClusters; p.trackletSigma = vc.trackletSigma; p.maxZPositionAllowed = vc.maxZPositionAllowed; p.clusterContributorsCut = vc.clusterContributorsCut; @@ -293,7 +268,6 @@ std::vector TrackingMode::getVertexingParameters(TrackingMo p.PhiBins = vc.PhiBins; p.useTruthSeeding = vc.useTruthSeeding; - p.outputContLabels = vc.outputContLabels; } // set for now outside to not disturb status quo vertParams[0].vertNsigmaCut = vc.vertNsigmaCut; diff --git a/Detectors/ITSMFT/ITS/tracking/src/FastMultEst.cxx b/Detectors/ITSMFT/ITS/tracking/src/FastMultEst.cxx new file mode 100644 index 0000000000000..cb831d7db71d0 --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/src/FastMultEst.cxx @@ -0,0 +1,252 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file FastMultEst.h +/// \brief Fast multiplicity estimator for ITS +/// \author ruben.shahoyan@cern.ch + +#include "ITStracking/FastMultEst.h" +#include "Framework/Logger.h" +#include +#include +#include +#include + +using namespace o2::its; + +namespace +{ + +// Convert trigger IR to ROF index on a given layer using LayerTiming +int findROFForIR(const o2::InteractionRecord& ir, + const o2::InteractionRecord& tfStartIR, + const LayerTiming& layerTiming) +{ + // Convert IR to BC-from-TF-start, which is the time base expected by LayerTiming. + const int64_t bcFromTFStart = ir.differenceInBC(tfStartIR); + if (bcFromTFStart < 0) { + return -1; + } + return layerTiming.getROF(static_cast(bcFromTFStart)); +} + +template +void enableCompatibleROFs(int baseLayer, + int baseRof, + const typename o2::its::ROFOverlapTable::View& overlapView, + o2::its::ROFMaskTable& sel) +{ + sel.setROFEnabled(baseLayer, baseRof); + for (int layer = 0; layer < NLayers; ++layer) { + if (layer == baseLayer) { + continue; + } + const auto& overlap = overlapView.getOverlap(baseLayer, layer, baseRof); + if (overlap.getEntries() > 0) { + sel.setROFsEnabled(layer, overlap.getFirstEntry(), overlap.getEntries()); + } + } +} + +template +std::vector buildMultiplicityCounts(const std::array, NLayers>& rofs, + const std::array, NLayers>& clus, + bool doStaggering, + int multLayer) +{ + std::vector multCounts; + if (doStaggering) { + multCounts.resize(rofs[multLayer].size()); + for (size_t iRof = 0; iRof < rofs[multLayer].size(); ++iRof) { + multCounts[iRof] = rofs[multLayer][iRof].getNEntries(); + } + return multCounts; + } + + static const o2::itsmft::ChipMappingITS chipMapping; + multCounts.resize(rofs[0].size(), 0); + for (size_t iRof = 0; iRof < rofs[0].size(); ++iRof) { + for (const auto& cluster : rofs[0][iRof].getROFData(clus[0])) { + if (chipMapping.getLayer(cluster.getSensorID()) == multLayer) { + ++multCounts[iRof]; + } + } + } + return multCounts; +} +} // namespace + +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); + } + } +} + +///______________________________________________________ +/// count clusters on the configured multiplicity layer +int FastMultEst::countClustersOnLayer(const gsl::span& clusters) const +{ + const int targetLayer = std::clamp(FastMultEstConfig::Instance().cutMultClusLayer, 0, NLayers - 1); + int count = 0; + int lr = FastMultEst::NLayers - 1; + int nchAcc = o2::itsmft::ChipMappingITS::getNChips() - o2::itsmft::ChipMappingITS::getNChipsPerLr(lr); + 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); + } + if (lr == targetLayer) { + ++count; + } + } + return count; +} + +///______________________________________________________ +/// find multiplicity for given number of clusters per layer +float FastMultEst::processNoiseFree(int nClusters) +{ + // Single-layer regime: estimate multiplicity from one configured layer only. + const auto& conf = FastMultEstConfig::Instance(); + const int layer = std::clamp(conf.cutMultClusLayer, 0, NLayers - 1); + const float acc = conf.accCorr[layer]; + nLayersUsed = nClusters > 0 ? 1 : 0; + noisePerChip = 0.f; + chi2 = 0.f; + cov[0] = cov[1] = cov[2] = 0.f; + if (nLayersUsed == 0 || acc <= 0.f) { + mult = -1.f; + return -1.f; + } + mult = nClusters / acc; + return mult > 0 ? mult : 0; +} + +///______________________________________________________ +/// find multiplicity for given number of clusters per layer with mean noise imposed +float FastMultEst::processNoiseImposed(int nClusters) +{ + // Single-layer regime with imposed noise subtraction. + const auto& conf = FastMultEstConfig::Instance(); + const int layer = std::clamp(conf.cutMultClusLayer, 0, NLayers - 1); + const float acc = conf.accCorr[layer]; + const float nch = static_cast(o2::itsmft::ChipMappingITS::getNChipsPerLr(layer)); + nLayersUsed = nClusters > 0 ? 1 : 0; + chi2 = 0.f; + cov[0] = cov[1] = cov[2] = 0.f; + if (nLayersUsed == 0 || acc <= 0.f) { + mult = -1.f; + return -1.f; + } + mult = (nClusters - noisePerChip * nch) / acc; + return mult; +} + +int FastMultEst::selectROFs(const std::array, NLayers>& rofs, + const std::array, NLayers>& clus, + const gsl::span trig, + uint32_t firstTForbit, + bool doStaggering, + const ROFOverlapTableN::View& overlapView, + ROFMaskTableN& sel) +{ + const auto& multEstConf = FastMultEstConfig::Instance(); // parameters for mult estimation and cuts + const int selectionLayer = overlapView.getClock(); + int multLayer = std::clamp(multEstConf.cutMultClusLayer, 0, NLayers - 1); + if (doStaggering && rofs[multLayer].empty()) { + LOGP(info, "FastMultEst multiplicity layer {} has no ROFs, falling back to selection layer {}", multLayer, selectionLayer); + multLayer = selectionLayer; + } + + const auto multCounts = buildMultiplicityCounts(rofs, clus, doStaggering, multLayer); + const int selectionRofCount = doStaggering ? static_cast(rofs[selectionLayer].size()) : static_cast(rofs[0].size()); + + sel.resetMask(); + lastRandomSeed = gRandom->GetSeed(); + const o2::InteractionRecord tfStartIR{0, firstTForbit}; + + if (!trig.empty()) { + const auto& selectionLayerTiming = overlapView.getLayer(selectionLayer); + const auto& multLayerTiming = overlapView.getLayer(multLayer); + + for (const auto& trigger : trig) { + const int selectionRof = findROFForIR(trigger.ir, tfStartIR, selectionLayerTiming); + if (selectionRof < 0) { + continue; + } + if (multEstConf.cutRandomFraction > 0.f && gRandom->Rndm() < multEstConf.cutRandomFraction) { + continue; + } + if (multEstConf.isMultCutRequested()) { + const int triggerMultRof = doStaggering ? findROFForIR(trigger.ir, tfStartIR, multLayerTiming) : selectionRof; + if (triggerMultRof < 0 || triggerMultRof >= static_cast(multCounts.size())) { + continue; + } + if (!multEstConf.isPassingMultCut(process(multCounts[triggerMultRof]))) { + continue; + } + } + enableCompatibleROFs(selectionLayer, selectionRof, overlapView, sel); + } + } else { + LOGP(info, "FastMultEst received no physics/TRD triggers, falling back to ROF-driven filtering on layer {}", selectionLayer); + for (int selectionRof = 0; selectionRof < selectionRofCount; ++selectionRof) { + if (multEstConf.isMultCutRequested()) { + bool passes = false; + if (!doStaggering || selectionLayer == multLayer) { + if (selectionRof < static_cast(multCounts.size())) { + passes = multEstConf.isPassingMultCut(process(multCounts[selectionRof])); + } + } else { + const auto& overlap = overlapView.getOverlap(selectionLayer, multLayer, selectionRof); + for (int rof = overlap.getFirstEntry(); rof < overlap.getEntriesBound(); ++rof) { + if (rof < static_cast(multCounts.size())) { + if (multEstConf.isPassingMultCut(process(multCounts[rof]))) { + passes = true; + break; + } + } + } + } + if (!passes) { + continue; + } + } + if (multEstConf.cutRandomFraction > 0.f && gRandom->Rndm() < multEstConf.cutRandomFraction) { + continue; + } + enableCompatibleROFs(selectionLayer, selectionRof, overlapView, sel); + } + } + + const auto selView = sel.getView(); + int nsel = 0; + for (int irof = 0; irof < selectionRofCount; ++irof) { + nsel += selView.isROFEnabled(selectionLayer, irof); + } + + if (!trig.empty() && multEstConf.preferTriggered) { + LOGP(debug, "FastMultEst preferTriggered is ignored in trigger-driven mask mode"); + } + + LOGP(debug, "NSel = {} of {} rofs on layer {} Seeds: before {} after {}", nsel, selectionRofCount, selectionLayer, lastRandomSeed, gRandom->GetSeed()); + + return nsel; +} diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEstConfig.cxx b/Detectors/ITSMFT/ITS/tracking/src/FastMultEstConfig.cxx similarity index 94% rename from Detectors/ITSMFT/ITS/reconstruction/src/FastMultEstConfig.cxx rename to Detectors/ITSMFT/ITS/tracking/src/FastMultEstConfig.cxx index 63c43cf26ba15..1568d8ed9f9fb 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEstConfig.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/FastMultEstConfig.cxx @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "ITSReconstruction/FastMultEstConfig.h" +#include "ITStracking/FastMultEstConfig.h" #include "TRandom.h" O2ParamImpl(o2::its::FastMultEstConfig); diff --git a/Detectors/ITSMFT/ITS/tracking/src/IndexTableUtils.cxx b/Detectors/ITSMFT/ITS/tracking/src/IndexTableUtils.cxx deleted file mode 100644 index 7152640e9a70f..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/src/IndexTableUtils.cxx +++ /dev/null @@ -1,49 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. -/// -/// \file IndexTableUtils.cxx -/// \brief -/// - -#include "ITStracking/IndexTableUtils.h" - -namespace o2 -{ -namespace its -{ - -const std::vector> index_table_utils::selectClusters( - const std::array& indexTable, - const std::array& selectedBinsRect) -{ - std::vector> filteredBins{}; - - int phiBinsNum{selectedBinsRect[3] - selectedBinsRect[1] + 1}; - - if (phiBinsNum < 0) { - phiBinsNum += constants::index_table::PhiBins; - } - - filteredBins.reserve(phiBinsNum); - - for (int iPhiBin{selectedBinsRect[1]}, iPhiCount{0}; iPhiCount < phiBinsNum; - iPhiBin = ++iPhiBin == constants::index_table::PhiBins ? 0 : iPhiBin, iPhiCount++) { - - const int firstBinIndex{index_table_utils::getBinIndex(selectedBinsRect[0], iPhiBin)}; - - filteredBins.emplace_back(indexTable[firstBinIndex], - countRowSelectedBins(indexTable, iPhiBin, selectedBinsRect[0], selectedBinsRect[2])); - } - - return filteredBins; -} -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/src/Smoother.cxx b/Detectors/ITSMFT/ITS/tracking/src/Smoother.cxx deleted file mode 100644 index f2f7dbc81398f..0000000000000 --- a/Detectors/ITSMFT/ITS/tracking/src/Smoother.cxx +++ /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. -// -// \author matteo.concas@cern.ch - -#include "ITStracking/Smoother.h" - -namespace o2 -{ -namespace its -{ - -constexpr std::array getInverseSymm2D(const std::array& mat) -{ - const double det = mat[0] * mat[2] - mat[1] * mat[1]; - return std::array{mat[2] / det, -mat[1] / det, mat[0] / det}; -} - -// Smoother -// template -// Smoother::Smoother(TrackITSExt& track, size_t smoothingLayer, const ROframe& event, float bZ, o2::base::PropagatorF::MatCorrType corr) : mLayerToSmooth{smoothingLayer}, -// mBz(bZ), -// mCorr(corr) -// { -// -// auto propInstance = o2::base::Propagator::Instance(); -// const TrackingFrameInfo& originalTf = event.getTrackingFrameInfoOnLayer(mLayerToSmooth).at(track.getClusterIndex(mLayerToSmooth)); -// -// mOutwardsTrack = track; // This track will be propagated outwards inside the smoother! (as last step of fitting did inward propagation) -// mInwardsTrack = {track.getParamOut(), // This track will be propagated inwards inside the smoother! -// static_cast(mOutwardsTrack.getNumberOfClusters()), -999, static_cast(event.getROFrameId()), -// mOutwardsTrack.getParamOut(), mOutwardsTrack.getClusterIndexes()}; -// -// mOutwardsTrack.resetCovariance(); -// mOutwardsTrack.setChi2(0); -// mInwardsTrack.resetCovariance(); -// mInwardsTrack.setChi2(0); -// -// bool statusOutw{false}; -// bool statusInw{false}; -// -// ////////////////////// -// // Outward propagation -// for (size_t iLayer{0}; iLayer < mLayerToSmooth; ++iLayer) { -// if (mOutwardsTrack.getClusterIndex(iLayer) == constants::UnusedIndex) { // Shorter tracks -// continue; -// } -// const TrackingFrameInfo& tF = event.getTrackingFrameInfoOnLayer(iLayer).at(mOutwardsTrack.getClusterIndex(iLayer)); -// statusOutw = mOutwardsTrack.rotate(tF.alphaTrackingFrame); -// statusOutw &= propInstance->propagateToX(mOutwardsTrack, -// tF.xTrackingFrame, -// mBz, -// o2::base::PropagatorImpl::MAX_SIN_PHI, -// o2::base::PropagatorImpl::MAX_STEP, -// mCorr); -// mOutwardsTrack.setChi2(mOutwardsTrack.getChi2() + mOutwardsTrack.getPredictedChi2(tF.positionTrackingFrame, tF.covarianceTrackingFrame)); -// statusOutw &= mOutwardsTrack.o2::track::TrackParCov::update(tF.positionTrackingFrame, tF.covarianceTrackingFrame); -// // LOG(info) << "Outwards loop on inwards track, layer: " << iLayer << " x: " << mOutwardsTrack.getX(); -// } -// -// // Prediction on the previously outwards-propagated track is done on a copy, as the process seems to be not reversible -// auto outwardsClone = mOutwardsTrack; -// statusOutw = outwardsClone.rotate(originalTf.alphaTrackingFrame); -// statusOutw &= propInstance->propagateToX(outwardsClone, -// originalTf.xTrackingFrame, -// mBz, -// o2::base::PropagatorImpl::MAX_SIN_PHI, -// o2::base::PropagatorImpl::MAX_STEP, -// mCorr); -// ///////////////////// -// // Inward propagation -// for (size_t iLayer{D - 1}; iLayer > mLayerToSmooth; --iLayer) { -// if (mInwardsTrack.getClusterIndex(iLayer) == constants::UnusedIndex) { // Shorter tracks -// continue; -// } -// const TrackingFrameInfo& tF = event.getTrackingFrameInfoOnLayer(iLayer).at(mInwardsTrack.getClusterIndex(iLayer)); -// statusInw = mInwardsTrack.rotate(tF.alphaTrackingFrame); -// statusInw &= propInstance->propagateToX(mInwardsTrack, -// tF.xTrackingFrame, -// mBz, -// o2::base::PropagatorImpl::MAX_SIN_PHI, -// o2::base::PropagatorImpl::MAX_STEP, -// mCorr); -// mInwardsTrack.setChi2(mInwardsTrack.getChi2() + mInwardsTrack.getPredictedChi2(tF.positionTrackingFrame, tF.covarianceTrackingFrame)); -// statusInw &= mInwardsTrack.o2::track::TrackParCov::update(tF.positionTrackingFrame, tF.covarianceTrackingFrame); -// // LOG(info) << "Inwards loop on outwards track, layer: " << iLayer << " x: " << mInwardsTrack.getX(); -// } -// -// // Prediction on the previously inwards-propagated track is done on a copy, as the process seems to be not revesible -// auto inwardsClone = mInwardsTrack; -// statusInw = inwardsClone.rotate(originalTf.alphaTrackingFrame); -// statusInw &= propInstance->propagateToX(inwardsClone, -// originalTf.xTrackingFrame, -// mBz, -// o2::base::PropagatorImpl::MAX_SIN_PHI, -// o2::base::PropagatorImpl::MAX_STEP, -// mCorr); -// // Compute weighted local chi2 -// mInitStatus = statusInw && statusOutw; -// if (mInitStatus) { -// mBestChi2 = computeSmoothedPredictedChi2(inwardsClone, outwardsClone, originalTf.positionTrackingFrame, originalTf.covarianceTrackingFrame); -// mLastChi2 = mBestChi2; -// LOG(info) << "Smoothed chi2 on original cluster: " << mBestChi2; -// } -// } - -template -Smoother::~Smoother() = default; - -template -float Smoother::computeSmoothedPredictedChi2(const o2::track::TrackParCov& firstTrack, // outwards track: from innermost cluster to outermost - const o2::track::TrackParCov& secondTrack, // inwards track: from outermost cluster to innermost - const std::array& cls, - const std::array& clCov) -{ - // Tracks need to be already propagated, compute only chi2 - // Symmetric covariances assumed - - if (firstTrack.getX() != secondTrack.getX()) { - LOG(fatal) << "Tracks need to be propagated to the same point! secondTrack.X=" << secondTrack.getX() << " firstTrack.X=" << firstTrack.getX(); - } - - std::array pp1 = {static_cast(firstTrack.getY()), static_cast(firstTrack.getZ())}; // P1: predicted Y,Z points - std::array pp2 = {static_cast(secondTrack.getY()), static_cast(secondTrack.getZ())}; // P2: predicted Y,Z points - - std::array c1 = {static_cast(firstTrack.getSigmaY2()), - static_cast(firstTrack.getSigmaZY()), - static_cast(firstTrack.getSigmaZ2())}; // Cov. track 1 - - std::array c2 = {static_cast(secondTrack.getSigmaY2()), - static_cast(secondTrack.getSigmaZY()), - static_cast(secondTrack.getSigmaZ2())}; // Cov. track 2 - - std::array w1 = getInverseSymm2D(c1); // weight matrices - std::array w2 = getInverseSymm2D(c2); - - std::array w1w2 = {w1[0] + w2[0], w1[1] + w2[1], w1[2] + w2[2]}; // (W1 + W2) - std::array C = getInverseSymm2D(w1w2); // C = (W1+W2)^-1 - - std::array w1pp1 = {w1[0] * pp1[0] + w1[1] * pp1[1], w1[1] * pp1[0] + w1[2] * pp1[1]}; // W1 * P1 - std::array w2pp2 = {w2[0] * pp2[0] + w2[1] * pp2[1], w2[1] * pp2[0] + w2[2] * pp2[1]}; // W2 * P2 - - double Y = C[0] * (w1pp1[0] + w2pp2[0]) + C[1] * (w1pp1[1] + w2pp2[1]); // Pp: weighted normalized combination of the predictions: - double Z = C[1] * (w1pp1[0] + w2pp2[0]) + C[2] * (w1pp1[1] + w2pp2[1]); // Pp = [(W1 * P1) + (W2 * P2)] / (W1 + W2) - - std::array delta = {Y - cls[0], Z - cls[1]}; // Δ = Pp - X, X: space point of cluster (Y,Z) - std::array CCp = {C[0] + static_cast(clCov[0]), C[1] + static_cast(clCov[1]), C[2] + static_cast(clCov[2])}; // Transformation of cluster covmat: CCp = C + Cov - std::array Wp = getInverseSymm2D(CCp); // Get weight matrix: Wp = CCp^-1 - - float chi2 = static_cast(delta[0] * (Wp[0] * delta[0] + Wp[1] * delta[1]) + delta[1] * (Wp[1] * delta[0] + Wp[2] * delta[1])); // chi2 = tΔ * (Wp * Δ) - - // #ifdef CA_DEBUG - LOG(info) << "Cluster_y: " << cls[0] << " Cluster_z: " << cls[1]; - LOG(info) << "\t\t- Covariance cluster: Y2: " << clCov[0] << " YZ: " << clCov[1] << " Z2: " << clCov[2]; - LOG(info) << "\t\t- Propagated t1_y: " << pp1[0] << " t1_z: " << pp1[1]; - LOG(info) << "\t\t- Propagated t2_y: " << pp2[0] << " t2_z: " << pp2[1]; - LOG(info) << "\t\t- Covariance t1: sY2: " << c1[0] << " sYZ: " << c1[1] << " sZ2: " << c1[2]; - LOG(info) << "\t\t- Covariance t2: sY2: " << c2[0] << " sYZ: " << c2[1] << " sZ2: " << c2[2]; - LOG(info) << "Smoother prediction Y: " << Y << " Z: " << Z; - LOG(info) << "\t\t- Delta_y: " << delta[0] << " Delta_z: " << delta[1]; - LOG(info) << "\t\t- Covariance Pr: Y2: " << C[0] << " YZ: " << C[1] << " Z2: " << C[2]; - LOG(info) << "\t\t- predicted chi2 t1: " << firstTrack.getPredictedChi2(cls, clCov); - LOG(info) << "\t\t- predicted chi2 t2: " << secondTrack.getPredictedChi2(cls, clCov); - // #endif - return chi2; -} - -// template -// bool Smoother::testCluster(const int clusterId, const ROframe& event) -// { -// if (!mInitStatus) { -// return false; -// } -// auto propInstance = o2::base::Propagator::Instance(); -// const TrackingFrameInfo& testTf = event.getTrackingFrameInfoOnLayer(mLayerToSmooth).at(clusterId); -// -// bool statusOutw{false}; -// bool statusInw{false}; -// -// // Prediction on the previously outwards-propagated track is done on a copy, as the process seems to be not reversible -// auto outwardsClone = mOutwardsTrack; -// statusOutw = outwardsClone.rotate(testTf.alphaTrackingFrame); -// statusOutw &= propInstance->propagateToX(outwardsClone, -// testTf.xTrackingFrame, -// mBz, -// o2::base::PropagatorImpl::MAX_SIN_PHI, -// o2::base::PropagatorImpl::MAX_STEP, -// mCorr); -// -// // Prediction on the previously inwards-propagated track is done on a copy, as the process seems to be not reversible -// auto inwardsClone = mInwardsTrack; -// statusInw = inwardsClone.rotate(testTf.alphaTrackingFrame); -// statusInw &= propInstance->propagateToX(inwardsClone, -// testTf.xTrackingFrame, -// mBz, -// o2::base::PropagatorImpl::MAX_SIN_PHI, -// o2::base::PropagatorImpl::MAX_STEP, -// mCorr); -// if (!(statusOutw && statusInw)) { -// LOG(warning) << "Failed propagation in smoother!"; -// return false; -// } -// -// // Compute weighted local chi2 -// mLastChi2 = computeSmoothedPredictedChi2(inwardsClone, outwardsClone, testTf.positionTrackingFrame, testTf.covarianceTrackingFrame); -// LOG(info) << "Smoothed chi2 on tested cluster: " << mLastChi2; -// -// return true; -// } - -template class Smoother<7>; - -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx index 29fb4ac4c69b5..5a32b3d3b1a95 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx @@ -46,82 +46,43 @@ constexpr float DefClusErrorCol = o2::itsmft::SegmentationAlpide::PitchCol * 0.5 constexpr float DefClusError2Row = DefClusErrorRow * DefClusErrorRow; constexpr float DefClusError2Col = DefClusErrorCol * DefClusErrorCol; -template -void TimeFrame::addPrimaryVertices(const bounded_vector& vertices, const int iteration) +template +void TimeFrame::addPrimaryVertex(const Vertex& vert) { - for (const auto& vertex : vertices) { - mPrimaryVertices.emplace_back(vertex); // put a copy in the present - mTotVertPerIteration[iteration]++; - if (!isBeamPositionOverridden) { // beam position is updated only at first occurrence of the vertex. A bit sketchy if we have past/future vertices, it should not impact too much. - const float w = vertex.getNContributors(); - mBeamPos[0] = (mBeamPos[0] * mBeamPosWeight + vertex.getX() * w) / (mBeamPosWeight + w); - mBeamPos[1] = (mBeamPos[1] * mBeamPosWeight + vertex.getY() * w) / (mBeamPosWeight + w); - mBeamPosWeight += w; - } - } - mROFramesPV.push_back(mPrimaryVertices.size()); // current rof must have number of vertices up to present -} - -template -void TimeFrame::addPrimaryVerticesLabels(bounded_vector>& labels) -{ - mVerticesMCRecInfo.insert(mVerticesMCRecInfo.end(), labels.begin(), labels.end()); -} - -template -void TimeFrame::addPrimaryVerticesContributorLabels(bounded_vector& labels) -{ - mVerticesContributorLabels.insert(mVerticesContributorLabels.end(), labels.begin(), labels.end()); -} - -template -void TimeFrame::addPrimaryVerticesInROF(const bounded_vector& vertices, const int rofId, const int iteration) -{ - mPrimaryVertices.insert(mPrimaryVertices.begin() + mROFramesPV[rofId], vertices.begin(), vertices.end()); - for (int i = rofId + 1; i < mROFramesPV.size(); ++i) { - mROFramesPV[i] += vertices.size(); + mPrimaryVertices.emplace_back(vert); + if (!isBeamPositionOverridden) { + const float w = vert.getNContributors(); + mBeamPos[0] = (mBeamPos[0] * mBeamPosWeight + vert.getX() * w) / (mBeamPosWeight + w); + mBeamPos[1] = (mBeamPos[1] * mBeamPosWeight + vert.getY() * w) / (mBeamPosWeight + w); + mBeamPosWeight += w; } - mTotVertPerIteration[iteration] += vertices.size(); } -template -void TimeFrame::addPrimaryVerticesLabelsInROF(const bounded_vector>& labels, const int rofId) -{ - mVerticesMCRecInfo.insert(mVerticesMCRecInfo.begin() + mROFramesPV[rofId], labels.begin(), labels.end()); -} - -template -void TimeFrame::addPrimaryVerticesContributorLabelsInROF(const bounded_vector& labels, const int rofId) -{ - // count the number of cont. in rofs before and including the target rof - unsigned int n{0}; - const auto& pvs = getPrimaryVertices(0, rofId); - for (const auto& pv : pvs) { - n += pv.getNContributors(); - } - mVerticesContributorLabels.insert(mVerticesContributorLabels.begin() + n, labels.begin(), labels.end()); -} - -template -int TimeFrame::loadROFrameData(gsl::span rofs, - gsl::span clusters, - gsl::span::iterator& pattIt, - const itsmft::TopologyDictionary* dict, - const dataformats::MCTruthContainer* mcLabels) +template +void TimeFrame::loadROFrameData(gsl::span rofs, + gsl::span clusters, + gsl::span::iterator& pattIt, + const itsmft::TopologyDictionary* dict, + int layer, + const dataformats::MCTruthContainer* mcLabels) { GeometryTGeo* geom = GeometryTGeo::Instance(); geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); + resetROFrameData(layer); + prepareROFrameData(clusters, layer); - resetROFrameData(rofs.size()); - prepareROFrameData(rofs, clusters); + // check for missing/empty/unset rofs + // the code requires consistent monotonically increasing input without gaps + const auto& timing = mROFOverlapTableView.getLayer(layer >= 0 ? layer : 0); + if (timing.mNROFsTF != rofs.size()) { + LOGP(fatal, "Received inconsistent number of rofs on layer:{} expected:{} received:{}", layer, timing.mNROFsTF, rofs.size()); + } - for (size_t iRof{0}; iRof < rofs.size(); ++iRof) { + for (int32_t iRof{0}; iRof < rofs.size(); ++iRof) { const auto& rof = rofs[iRof]; for (int clusterId{rof.getFirstEntry()}; clusterId < rof.getFirstEntry() + rof.getNEntries(); ++clusterId) { const auto& c = clusters[clusterId]; - - int layer = geom->getLayer(c.getSensorID()); - + int lay = geom->getLayer(c.getSensorID()); auto pattID = c.getPatternID(); o2::math_utils::Point3D locXYZ; float sigmaY2 = DefClusError2Row, sigmaZ2 = DefClusError2Col, sigmaYZ = 0; // Dummy COG errors (about half pixel size) @@ -142,85 +103,97 @@ int TimeFrame::loadROFrameData(gsl::span r locXYZ = dict->getClusterCoordinates(c, patt, false); clusterSize = patt.getNPixels(); } - mClusterSize[clusterId] = std::clamp(clusterSize, 0u, 255u); + mClusterSize[layer >= 0 ? layer : 0][clusterId] = std::clamp(clusterSize, 0u, 255u); auto sensorID = c.getSensorID(); // Inverse transformation to the local --> tracking auto trkXYZ = geom->getMatrixT2L(sensorID) ^ locXYZ; // Transformation to the local --> global auto gloXYZ = geom->getMatrixL2G(sensorID) * locXYZ; - - addTrackingFrameInfoToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), trkXYZ.x(), geom->getSensorRefAlpha(sensorID), + addTrackingFrameInfoToLayer(lay, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), trkXYZ.x(), geom->getSensorRefAlpha(sensorID), std::array{trkXYZ.y(), trkXYZ.z()}, std::array{sigmaY2, sigmaYZ, sigmaZ2}); /// Rotate to the global frame - addClusterToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), mUnsortedClusters[layer].size()); - addClusterExternalIndexToLayer(layer, clusterId); + addClusterToLayer(lay, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), mUnsortedClusters[lay].size()); + addClusterExternalIndexToLayer(lay, clusterId); } - for (unsigned int iL{0}; iL < mUnsortedClusters.size(); ++iL) { - mROFramesClusters[iL][iRof + 1] = mUnsortedClusters[iL].size(); // effectively calculating and exclusive sum + // effectively calculating an exclusive sum + if (layer >= 0) { + mROFramesClusters[layer][iRof + 1] = mUnsortedClusters[layer].size(); + } else { + for (unsigned int iL{0}; iL < mUnsortedClusters.size(); ++iL) { + mROFramesClusters[iL][iRof + 1] = mUnsortedClusters[iL].size(); + } } } - for (auto i = 0; i < mNTrackletsPerCluster.size(); ++i) { - mNTrackletsPerCluster[i].resize(mUnsortedClusters[1].size()); - mNTrackletsPerClusterSum[i].resize(mUnsortedClusters[1].size() + 1); // Exc sum "prepends" a 0 + if (layer == 1 || layer == -1) { + for (auto i = 0; i < mNTrackletsPerCluster.size(); ++i) { + mNTrackletsPerCluster[i].resize(mUnsortedClusters[1].size()); + mNTrackletsPerClusterSum[i].resize(mUnsortedClusters[1].size() + 1); + } } if (mcLabels != nullptr) { - mClusterLabels = mcLabels; + mClusterLabels[layer >= 0 ? layer : 0] = mcLabels; + } else { + mClusterLabels[layer >= 0 ? layer : 0] = nullptr; } - - return mNrof; } -template -void TimeFrame::resetROFrameData(size_t nRofs) +template +void TimeFrame::resetROFrameData(int layer) { - for (int iLayer{0}; iLayer < nLayers; ++iLayer) { - deepVectorClear(mUnsortedClusters[iLayer], getMaybeFrameworkHostResource()); - deepVectorClear(mTrackingFrameInfo[iLayer], getMaybeFrameworkHostResource()); - clearResizeBoundedVector(mROFramesClusters[iLayer], nRofs + 1, getMaybeFrameworkHostResource()); - deepVectorClear(mClusterExternalIndices[iLayer], mMemoryPool.get()); - - if (iLayer < 2) { - deepVectorClear(mTrackletsIndexROF[iLayer], mMemoryPool.get()); - deepVectorClear(mNTrackletsPerCluster[iLayer], mMemoryPool.get()); - deepVectorClear(mNTrackletsPerClusterSum[iLayer], mMemoryPool.get()); + if (layer >= 0) { + deepVectorClear(mUnsortedClusters[layer], getMaybeFrameworkHostResource()); + deepVectorClear(mTrackingFrameInfo[layer], getMaybeFrameworkHostResource()); + deepVectorClear(mClusterExternalIndices[layer], mMemoryPool.get()); + clearResizeBoundedVector(mROFramesClusters[layer], mROFOverlapTableView.getLayer(layer).mNROFsTF + 1, getMaybeFrameworkHostResource()); + } else { + for (int iLayer{0}; iLayer < NLayers; ++iLayer) { + deepVectorClear(mUnsortedClusters[iLayer], getMaybeFrameworkHostResource()); + deepVectorClear(mTrackingFrameInfo[iLayer], getMaybeFrameworkHostResource()); + deepVectorClear(mClusterExternalIndices[iLayer], mMemoryPool.get()); + clearResizeBoundedVector(mROFramesClusters[iLayer], mROFOverlapTableView.getLayer(iLayer).mNROFsTF + 1, getMaybeFrameworkHostResource()); } } } -template -void TimeFrame::prepareROFrameData(gsl::span rofs, - gsl::span clusters) +template +void TimeFrame::prepareROFrameData(gsl::span clusters, int layer) { - GeometryTGeo* geom = GeometryTGeo::Instance(); - mNrof = rofs.size(); - clearResizeBoundedVector(mClusterSize, clusters.size(), mMemoryPool.get()); - std::array clusterCountPerLayer{}; - for (const auto& clus : clusters) { - ++clusterCountPerLayer[geom->getLayer(clus.getSensorID())]; - } - for (int iLayer{0}; iLayer < nLayers; ++iLayer) { - mUnsortedClusters[iLayer].reserve(clusterCountPerLayer[iLayer]); - mTrackingFrameInfo[iLayer].reserve(clusterCountPerLayer[iLayer]); - mClusterExternalIndices[iLayer].reserve(clusterCountPerLayer[iLayer]); + if (layer >= 0) { + mUnsortedClusters[layer].reserve(clusters.size()); + mTrackingFrameInfo[layer].reserve(clusters.size()); + mClusterExternalIndices[layer].reserve(clusters.size()); + clearResizeBoundedVector(mClusterSize[layer], clusters.size(), mMemoryPool.get()); + } else { + auto* geom = GeometryTGeo::Instance(); + clearResizeBoundedVector(mClusterSize[0], clusters.size(), mMemoryPool.get()); + std::array clusterCountPerLayer{0}; + for (const auto& cls : clusters) { + ++clusterCountPerLayer[geom->getLayer(cls.getChipID())]; + } + for (int iLayer{0}; iLayer < NLayers; ++iLayer) { + mUnsortedClusters[iLayer].reserve(clusterCountPerLayer[iLayer]); + mTrackingFrameInfo[iLayer].reserve(clusterCountPerLayer[iLayer]); + mClusterExternalIndices[iLayer].reserve(clusterCountPerLayer[iLayer]); + } } } -template -void TimeFrame::prepareClusters(const TrackingParameters& trkParam, const int maxLayers) +template +void TimeFrame::prepareClusters(const TrackingParameters& trkParam, const int maxLayers) { const int numBins{trkParam.PhiBins * trkParam.ZBins}; const int stride{numBins + 1}; bounded_vector cHelper(mMemoryPool.get()); bounded_vector clsPerBin(numBins, 0, mMemoryPool.get()); bounded_vector lutPerBin(numBins, 0, mMemoryPool.get()); - for (int rof{0}; rof < mNrof; ++rof) { - if ((int)mMultiplicityCutMask.size() == mNrof && !mMultiplicityCutMask[rof]) { - continue; - } - for (int iLayer{0}, stopLayer = std::min(trkParam.NLayers, maxLayers); iLayer < stopLayer; ++iLayer) { + for (int iLayer{0}, stopLayer = std::min(trkParam.NLayers, maxLayers); iLayer < stopLayer; ++iLayer) { + for (int rof{0}; rof < getNrof(iLayer); ++rof) { + if (!mROFMaskView.isROFEnabled(iLayer, rof)) { + continue; + } const auto& unsortedClusters{getUnsortedClustersOnLayer(rof, iLayer)}; const int clustersNum{static_cast(unsortedClusters.size())}; auto* tableBase = mIndexTables[iLayer].data() + rof * stride; @@ -270,25 +243,19 @@ void TimeFrame::prepareClusters(const TrackingParameters& trkParam, con } } -template -void TimeFrame::initialise(const int iteration, const TrackingParameters& trkParam, const int maxLayers, bool resetVertices) +template +void TimeFrame::initialise(const int iteration, const TrackingParameters& trkParam, const int maxLayers, bool resetVertices) { if (iteration == 0) { - if (maxLayers < trkParam.NLayers && resetVertices) { - resetRofPV(); - deepVectorClear(mTotVertPerIteration); - } deepVectorClear(mTracks); deepVectorClear(mTracksLabel); deepVectorClear(mLines); deepVectorClear(mLinesLabels); if (resetVertices) { - deepVectorClear(mVerticesMCRecInfo); - deepVectorClear(mVerticesContributorLabels); + deepVectorClear(mPrimaryVertices); + deepVectorClear(mPrimaryVerticesLabels); } - clearResizeBoundedVector(mTracks, mNrof, mMemoryPool.get()); - clearResizeBoundedVector(mTracksLabel, mNrof, mMemoryPool.get()); - clearResizeBoundedVector(mLinesLabels, mNrof, mMemoryPool.get()); + clearResizeBoundedVector(mLinesLabels, getNrof(1), mMemoryPool.get()); clearResizeBoundedVector(mCells, trkParam.CellsPerRoad(), mMemoryPool.get()); clearResizeBoundedVector(mCellsLookupTable, trkParam.CellsPerRoad() - 1, mMemoryPool.get()); clearResizeBoundedVector(mCellsNeighbours, trkParam.CellsPerRoad() - 1, mMemoryPool.get()); @@ -302,14 +269,16 @@ 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(), getMaybeFrameworkHostResource(maxLayers != nLayers)); - clearResizeBoundedVector(mUsedClusters[iLayer], mUnsortedClusters[iLayer].size(), getMaybeFrameworkHostResource(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), getMaybeFrameworkHostResource(maxLayers != nLayers)); - clearResizeBoundedVector(mLines, mNrof, mMemoryPool.get()); - clearResizeBoundedVector(mTrackletClusters, mNrof, mMemoryPool.get()); + clearResizeBoundedVector(mLines, getNrof(1), mMemoryPool.get()); + clearResizeBoundedVector(mTrackletClusters, getNrof(1), mMemoryPool.get()); + for (int iLayer{0}; iLayer < NLayers; ++iLayer) { + clearResizeBoundedVector(mIndexTables[iLayer], getNrof(iLayer) * ((trkParam.ZBins * trkParam.PhiBins) + 1), getMaybeFrameworkHostResource()); + } for (int iLayer{0}; iLayer < trkParam.NLayers; ++iLayer) { if (trkParam.SystErrorY2[iLayer] > 0.f || trkParam.SystErrorZ2[iLayer] > 0.f) { for (auto& tfInfo : mTrackingFrameInfo[iLayer]) { @@ -319,12 +288,13 @@ void TimeFrame::initialise(const int iteration, const TrackingParameter } } } - mMinR.fill(10000.); - mMaxR.fill(-1.); + + mMinR.fill(std::numeric_limits::max()); + mMaxR.fill(std::numeric_limits::min()); } mNTrackletsPerROF.resize(2); for (auto& v : mNTrackletsPerROF) { - v = bounded_vector(mNrof + 1, 0, mMemoryPool.get()); + v = bounded_vector(getNrof(1) + 1, 0, mMemoryPool.get()); } if (iteration == 0 || iteration == 3) { prepareClusters(trkParam, maxLayers); @@ -337,15 +307,10 @@ void TimeFrame::initialise(const int iteration, const TrackingParameter } } - mTotVertPerIteration.resize(1 + iteration); - mNoVertexROF = 0; - deepVectorClear(mRoads); - deepVectorClear(mRoadLabels); - 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) { + for (unsigned int iLayer{0}; iLayer < NLayers; ++iLayer) { mMSangles[iLayer] = math_utils::MSangle(0.14f, trkParam.TrackletMinPt, trkParam.LayerxX0[iLayer]); mPositionResolution[iLayer] = o2::gpu::CAMath::Sqrt(0.5f * (trkParam.SystErrorZ2[iLayer] + trkParam.SystErrorY2[iLayer]) + trkParam.LayerResolution[iLayer] * trkParam.LayerResolution[iLayer]); if (iLayer < mClusters.size() - 1) { @@ -381,8 +346,8 @@ void TimeFrame::initialise(const int iteration, const TrackingParameter } } -template -unsigned long TimeFrame::getArtefactsMemory() const +template +unsigned long TimeFrame::getArtefactsMemory() const { unsigned long size{0}; for (const auto& trkl : mTracklets) { @@ -394,31 +359,21 @@ unsigned long TimeFrame::getArtefactsMemory() const for (const auto& cellsN : mCellsNeighbours) { size += sizeof(int) * cellsN.size(); } - return size + sizeof(Road) * mRoads.size(); + return size; } -template -void TimeFrame::printArtefactsMemory() const +template +void TimeFrame::printArtefactsMemory() const { LOGP(info, "TimeFrame: Artefacts occupy {:.2f} MB", getArtefactsMemory() / constants::MB); } -template -void TimeFrame::fillPrimaryVerticesXandAlpha() -{ - deepVectorClear(mPValphaX); - mPValphaX.reserve(mPrimaryVertices.size()); - for (auto& pv : mPrimaryVertices) { - mPValphaX.emplace_back(std::array{o2::gpu::CAMath::Hypot(pv.getX(), pv.getY()), math_utils::computePhi(pv.getX(), pv.getY())}); - } -} - -template -void TimeFrame::computeTrackletsPerROFScans() +template +void TimeFrame::computeTrackletsPerROFScans() { for (ushort iLayer = 0; iLayer < 2; ++iLayer) { - for (unsigned int iRof{0}; iRof < mNrof; ++iRof) { - if (mMultiplicityCutMask[iRof]) { + for (unsigned int iRof{0}; iRof < getNrof(1); ++iRof) { + if (mROFMaskView.isROFEnabled(1, iRof)) { mTotalTracklets[iLayer] += mNTrackletsPerROF[iLayer][iRof]; } } @@ -427,141 +382,8 @@ void TimeFrame::computeTrackletsPerROFScans() } } -template -void TimeFrame::checkTrackletLUTs() -{ - for (uint32_t iLayer{0}; iLayer < getTracklets().size(); ++iLayer) { - int prev{-1}; - int count{0}; - for (uint32_t iTracklet{0}; iTracklet < getTracklets()[iLayer].size(); ++iTracklet) { - auto& trk = getTracklets()[iLayer][iTracklet]; - int currentId{trk.firstClusterIndex}; - if (currentId < prev) { - LOG(info) << "First Cluster Index not increasing monotonically on L:T:ID:Prev " << iLayer << "\t" << iTracklet << "\t" << currentId << "\t" << prev; - } else if (currentId == prev) { - count++; - } else { - if (iLayer > 0) { - auto& lut{getTrackletsLookupTable()[iLayer - 1]}; - if (count != lut[prev + 1] - lut[prev]) { - LOG(info) << "LUT count broken " << iLayer - 1 << "\t" << prev << "\t" << count << "\t" << lut[prev + 1] << "\t" << lut[prev]; - } - } - count = 1; - } - prev = currentId; - if (iLayer > 0) { - auto& lut{getTrackletsLookupTable()[iLayer - 1]}; - if (iTracklet >= (uint32_t)(lut[currentId + 1]) || iTracklet < (uint32_t)(lut[currentId])) { - LOG(info) << "LUT broken: " << iLayer - 1 << "\t" << currentId << "\t" << iTracklet; - } - } - } - } -} - -template -void TimeFrame::printTrackletLUTonLayer(int i) -{ - LOG(info) << "-------- Tracklet LUT " << i; - std::stringstream s; - for (int j : mTrackletsLookupTable[i]) { - s << j << "\t"; - } - LOG(info) << s.str(); - LOG(info) << "--------"; -} - -template -void TimeFrame::printCellLUTonLayer(int i) -{ - LOG(info) << "-------- Cell LUT " << i; - std::stringstream s; - for (int j : mCellsLookupTable[i]) { - s << j << "\t"; - } - LOG(info) << s.str(); - LOG(info) << "--------"; -} - -template -void TimeFrame::printTrackletLUTs() -{ - for (unsigned int i{0}; i < mTrackletsLookupTable.size(); ++i) { - printTrackletLUTonLayer(i); - } -} - -template -void TimeFrame::printCellLUTs() -{ - for (unsigned int i{0}; i < mCellsLookupTable.size(); ++i) { - printCellLUTonLayer(i); - } -} - -template -void TimeFrame::printVertices() -{ - LOG(info) << "Vertices in ROF (nROF = " << mNrof << ", lut size = " << mROFramesPV.size() << ")"; - for (unsigned int iR{0}; iR < mROFramesPV.size(); ++iR) { - LOG(info) << mROFramesPV[iR] << "\t"; - } - LOG(info) << "\n\n Vertices:"; - for (unsigned int iV{0}; iV < mPrimaryVertices.size(); ++iV) { - LOG(info) << mPrimaryVertices[iV].getX() << "\t" << mPrimaryVertices[iV].getY() << "\t" << mPrimaryVertices[iV].getZ(); - } - LOG(info) << "--------"; -} - -template -void TimeFrame::printROFoffsets() -{ - LOG(info) << "--------"; - for (unsigned int iLayer{0}; iLayer < mROFramesClusters.size(); ++iLayer) { - LOG(info) << "Layer " << iLayer; - std::stringstream s; - for (auto value : mROFramesClusters[iLayer]) { - s << value << "\t"; - } - LOG(info) << s.str(); - } -} - -template -void TimeFrame::printNClsPerROF() -{ - LOG(info) << "--------"; - for (unsigned int iLayer{0}; iLayer < mNClustersPerROF.size(); ++iLayer) { - LOG(info) << "Layer " << iLayer; - std::stringstream s; - for (auto& value : mNClustersPerROF[iLayer]) { - s << value << "\t"; - } - LOG(info) << s.str(); - } -} - -template -void TimeFrame::printSliceInfo(const int startROF, const int sliceSize) -{ - LOG(info) << "Dumping slice of " << sliceSize << " rofs:"; - for (int iROF{startROF}; iROF < startROF + sliceSize; ++iROF) { - LOG(info) << "ROF " << iROF << " dump:"; - for (unsigned int iLayer{0}; iLayer < mClusters.size(); ++iLayer) { - LOG(info) << "Layer " << iLayer << " has: " << getClustersOnLayer(iROF, iLayer).size() << " clusters."; - } - LOG(info) << "Number of seeding vertices: " << getPrimaryVertices(iROF).size(); - int iVertex{0}; - for (auto& v : getPrimaryVertices(iROF)) { - LOG(info) << "\t vertex " << iVertex++ << ": x=" << v.getX() << " " - << " y=" << v.getY() << " z=" << v.getZ() << " has " << v.getNContributors() << " contributors."; - } - } -} - -template -void TimeFrame::setMemoryPool(std::shared_ptr pool) +template +void TimeFrame::setMemoryPool(std::shared_ptr pool) { mMemoryPool = pool; @@ -577,33 +399,29 @@ void TimeFrame::setMemoryPool(std::shared_ptr po }; // these will only reside on the host for the cpu part - initVector(mTotVertPerIteration); initContainers(mClusterExternalIndices); initContainers(mNTrackletsPerCluster); initContainers(mNTrackletsPerClusterSum); initContainers(mNClustersPerROF); - initVector(mROFramesPV); initVector(mPrimaryVertices); - initVector(mRoads); initVector(mMSangles); initVector(mPhiCuts); initVector(mPositionResolution); - initVector(mClusterSize); + initContainers(mClusterSize); initVector(mPValphaX); initVector(mBogusClusters); initContainers(mTrackletsIndexROF); - initContainers(mTracks); + initVector(mTracks); initContainers(mTracklets); initContainers(mCells); initContainers(mCellsNeighbours); initContainers(mCellsLookupTable); // MC info (we don't know if we have MC) - initVector(mVerticesContributorLabels); + initVector(mPrimaryVerticesLabels); initContainers(mLinesLabels); initContainers(mTrackletLabels); initContainers(mCellLabels); - initVector(mRoadLabels); - initContainers(mTracksLabel); + initVector(mTracksLabel); // these will use possibly an externally provided allocator initContainers(mClusters, hasFrameworkAllocator()); initContainers(mUsedClusters, hasFrameworkAllocator()); @@ -613,30 +431,27 @@ void TimeFrame::setMemoryPool(std::shared_ptr po initContainers(mROFramesClusters, hasFrameworkAllocator()); } -template -void TimeFrame::setFrameworkAllocator(ExternalAllocator* ext) +template +void TimeFrame::setFrameworkAllocator(ExternalAllocator* ext) { mExternalAllocator = ext; mExtMemoryPool = std::make_shared(mExternalAllocator); } -template -void TimeFrame::wipe() +template +void TimeFrame::wipe() { deepVectorClear(mTracks); deepVectorClear(mTracklets); deepVectorClear(mCells); - deepVectorClear(mRoads); deepVectorClear(mCellsNeighbours); deepVectorClear(mCellsLookupTable); - deepVectorClear(mTotVertPerIteration); deepVectorClear(mPrimaryVertices); deepVectorClear(mTrackletsLookupTable); deepVectorClear(mClusterExternalIndices); deepVectorClear(mNTrackletsPerCluster); deepVectorClear(mNTrackletsPerClusterSum); deepVectorClear(mNClustersPerROF); - deepVectorClear(mROFramesPV); deepVectorClear(mMSangles); deepVectorClear(mPhiCuts); deepVectorClear(mPositionResolution); @@ -659,10 +474,9 @@ void TimeFrame::wipe() // only needed to clear if we have MC info if (hasMCinformation()) { deepVectorClear(mLinesLabels); - deepVectorClear(mVerticesContributorLabels); + deepVectorClear(mPrimaryVerticesLabels); deepVectorClear(mTrackletLabels); deepVectorClear(mCellLabels); - deepVectorClear(mRoadLabels); deepVectorClear(mTracksLabel); } } diff --git a/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx b/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx index 658a90b37613f..dc032a46213a9 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx @@ -14,28 +14,22 @@ /// #include "ITStracking/Tracker.h" - #include "ITStracking/BoundedAllocator.h" -#include "ITStracking/Cell.h" #include "ITStracking/Constants.h" -#include "ITStracking/IndexTableUtils.h" -#include "ITStracking/Tracklet.h" #include "ITStracking/TrackerTraits.h" #include "ITStracking/TrackingConfigParam.h" -#include "ReconstructionDataFormats/Track.h" #include #include #include #include -#include namespace o2::its { using o2::its::constants::GB; -template -Tracker::Tracker(TrackerTraits* traits) : mTraits(traits) +template +Tracker::Tracker(TrackerTraits* traits) : mTraits(traits) { /// Initialise standard configuration with 1 iteration mTrkParams.resize(1); @@ -45,27 +39,26 @@ Tracker::Tracker(TrackerTraits* traits) : mTraits(traits) } } -template -void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& error) +template +void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& error) { LogFunc evalLog = [](const std::string&) {}; double total{0}; mTraits->updateTrackingParameters(mTrkParams); + mTimeFrame->updateROFVertexLookupTable(); + int maxNvertices{-1}; if (mTrkParams[0].PerPrimaryVertexProcessing) { - for (int iROF{0}; iROF < mTimeFrame->getNrof(); ++iROF) { - int minRof = o2::gpu::CAMath::Max(0, iROF - mTrkParams[0].DeltaROF); - int maxRof = o2::gpu::CAMath::Min(mTimeFrame->getNrof(), iROF + mTrkParams[0].DeltaROF); - maxNvertices = std::max(maxNvertices, (int)mTimeFrame->getPrimaryVertices(minRof, maxRof).size()); - } + maxNvertices = mTimeFrame->getROFVertexLookupTableView().getMaxVerticesPerROF(); } - int iteration{0}, iROFs{0}, iVertex{0}; + int iteration{0}, iVertex{0}; auto handleException = [&](const auto& err) { - LOGP(error, "Too much memory used during {} in iteration {} in ROF span {}-{} iVtx={}: {:.2f} GB. Current limit is {:.2f} GB, check the detector status and/or the selections.", - StateNames[mCurState], iteration, iROFs, iROFs + mTrkParams[iteration].nROFsPerIterations, iVertex, - (double)mTimeFrame->getArtefactsMemory() / GB, (double)mTrkParams[iteration].MaxMemory / GB); + LOGP(error, "Too much memory in {} in iteration {} iVtx={}: {:.2f} GB. Current limit is {:.2f} GB, check the detector status and/or the selections.", + StateNames[mCurState], iteration, iVertex, + (double)mTimeFrame->getArtefactsMemory() / GB, + (double)mTrkParams[iteration].MaxMemory / GB); if (typeid(err) != typeid(std::bad_alloc)) { // only print if the exceptions is different from what is expected LOGP(error, "Exception: {}", err.what()); } @@ -73,7 +66,7 @@ void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& er mMemoryPool->print(); mTimeFrame->wipe(); ++mNumberOfDroppedTFs; - error("...Dropping Timeframe..."); + error(std::format("...Dropping TimeSlice {} (out of {} dropped {})...", mTimeSlice, mTimeFrameCounter, mNumberOfDroppedTFs)); } else { throw err; } @@ -83,61 +76,34 @@ void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& er for (iteration = 0; iteration < (int)mTrkParams.size(); ++iteration) { mMemoryPool->setMaxMemory(mTrkParams[iteration].MaxMemory); if (iteration == 3 && mTrkParams[0].DoUPCIteration) { - mTimeFrame->swapMasks(); + mTimeFrame->useUPCMask(); } - double timeTracklets{0.}, timeCells{0.}, timeNeighbours{0.}, timeRoads{0.}; - int nTracklets{0}, nCells{0}, nNeighbours{0}, nTracks{-static_cast(mTimeFrame->getNumberOfTracks())}; - int nROFsIterations = (mTrkParams[iteration].nROFsPerIterations > 0 && !mTimeFrame->isGPU()) ? mTimeFrame->getNrof() / mTrkParams[iteration].nROFsPerIterations + bool(mTimeFrame->getNrof() % mTrkParams[iteration].nROFsPerIterations) : 1; + float timeTracklets{0.}, timeCells{0.}, timeNeighbours{0.}, timeRoads{0.}; + size_t nTracklets{0}, nCells{0}, nNeighbours{0}; + int nTracks{-static_cast(mTimeFrame->getNumberOfTracks())}; iVertex = std::min(maxNvertices, 0); logger(std::format("==== ITS {} Tracking iteration {} summary ====", mTraits->getName(), iteration)); - total += evaluateTask(&Tracker::initialiseTimeFrame, StateNames[mCurState = TFInit], iteration, logger, iteration); do { - for (iROFs = 0; iROFs < nROFsIterations; ++iROFs) { - timeTracklets += evaluateTask(&Tracker::computeTracklets, StateNames[mCurState = Trackleting], iteration, evalLog, iteration, iROFs, iVertex); - nTracklets += mTraits->getTFNumberOfTracklets(); - float trackletsPerCluster = mTraits->getTFNumberOfClusters() > 0 ? float(mTraits->getTFNumberOfTracklets()) / float(mTraits->getTFNumberOfClusters()) : 0.f; - if (trackletsPerCluster > mTrkParams[iteration].TrackletsPerClusterLimit) { - error(std::format("Too many tracklets per cluster ({}) in iteration {} in ROF span {}-{}:, check the detector status and/or the selections. Current limit is {}", - trackletsPerCluster, iteration, iROFs, iROFs + mTrkParams[iteration].nROFsPerIterations, mTrkParams[iteration].TrackletsPerClusterLimit)); - break; - } - timeCells += evaluateTask(&Tracker::computeCells, StateNames[mCurState = Celling], iteration, evalLog, iteration); - nCells += mTraits->getTFNumberOfCells(); - float cellsPerCluster = mTraits->getTFNumberOfClusters() > 0 ? float(mTraits->getTFNumberOfCells()) / float(mTraits->getTFNumberOfClusters()) : 0.f; - if (cellsPerCluster > mTrkParams[iteration].CellsPerClusterLimit) { - error(std::format("Too many cells per cluster ({}) in iteration {} in ROF span {}-{}, check the detector status and/or the selections. Current limit is {}", - cellsPerCluster, iteration, iROFs, iROFs + mTrkParams[iteration].nROFsPerIterations, mTrkParams[iteration].CellsPerClusterLimit)); - break; - } - timeNeighbours += evaluateTask(&Tracker::findCellsNeighbours, StateNames[mCurState = Neighbouring], iteration, evalLog, iteration); - nNeighbours += mTimeFrame->getNumberOfNeighbours(); - timeRoads += evaluateTask(&Tracker::findRoads, StateNames[mCurState = Roading], iteration, evalLog, iteration); - } + timeTracklets += evaluateTask(&Tracker::computeTracklets, StateNames[mCurState = Trackleting], iteration, evalLog, iteration, iVertex); + nTracklets += mTraits->getTFNumberOfTracklets(); + timeCells += evaluateTask(&Tracker::computeCells, StateNames[mCurState = Celling], iteration, evalLog, iteration); + nCells += mTraits->getTFNumberOfCells(); + timeNeighbours += evaluateTask(&Tracker::findCellsNeighbours, StateNames[mCurState = Neighbouring], iteration, evalLog, iteration); + nNeighbours += mTimeFrame->getNumberOfNeighbours(); + timeRoads += evaluateTask(&Tracker::findRoads, StateNames[mCurState = Roading], iteration, evalLog, iteration); } while (++iVertex < maxNvertices); logger(std::format(" - Tracklet finding: {} tracklets found in {:.2f} ms", nTracklets, timeTracklets)); logger(std::format(" - Cell finding: {} cells found in {:.2f} ms", nCells, timeCells)); logger(std::format(" - Neighbours finding: {} neighbours found in {:.2f} ms", nNeighbours, timeNeighbours)); logger(std::format(" - Track finding: {} tracks found in {:.2f} ms", nTracks + mTimeFrame->getNumberOfTracks(), timeRoads)); total += timeTracklets + timeCells + timeNeighbours + timeRoads; - if (mTraits->supportsExtendTracks() && mTrkParams[iteration].UseTrackFollower) { - int nExtendedTracks{-mTimeFrame->mNExtendedTracks}, nExtendedClusters{-mTimeFrame->mNExtendedUsedClusters}; - auto timeExtending = evaluateTask(&Tracker::extendTracks, "Extending tracks", iteration, evalLog, iteration); - total += timeExtending; - logger(std::format(" - Extending Tracks: {} extended tracks using {} clusters found in {:.2f} ms", nExtendedTracks + mTimeFrame->mNExtendedTracks, nExtendedClusters + mTimeFrame->mNExtendedUsedClusters, timeExtending)); - } if (mTrkParams[iteration].PrintMemory) { mMemoryPool->print(); } } - if (mTraits->supportsFindShortPrimaries() && mTrkParams[0].FindShortTracks) { - auto nTracksB = mTimeFrame->getNumberOfTracks(); - total += evaluateTask(&Tracker::findShortPrimaries, "Short primaries finding", 0, logger); - auto nTracksA = mTimeFrame->getNumberOfTracks(); - logger(std::format(" `-> found {} additional tracks", nTracksA - nTracksB)); - } if constexpr (constants::DoTimeBenchmarks) { - logger(std::format("=== TimeFrame {} processing completed in: {:.2f} ms using {} thread(s) ===", mTimeFrameCounter, total, mTraits->getNThreads())); + logger(std::format("=== TimeSlice {} processing completed in: {:.2f} ms using {} thread(s) ===", mTimeSlice, total, mTraits->getNThreads())); } } catch (const BoundedMemoryResource::MemoryLimitExceeded& err) { handleException(err); @@ -148,9 +114,7 @@ void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& er } 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(); - } + mTimeFrame->getTracks().clear(); return; } @@ -158,6 +122,8 @@ void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& er computeTracksMClabels(); } rectifyClusterIndices(); + sortTracks(); + ++mTimeFrameCounter; mTotalTime += total; @@ -167,88 +133,23 @@ void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& er } } -template -void Tracker::computeRoadsMClabels() +template +void Tracker::computeTracksMClabels() { - /// Moore's Voting Algorithm - if (!mTimeFrame->hasMCinformation()) { - return; - } - - mTimeFrame->initialiseRoadLabels(); - - int roadsNum{static_cast(mTimeFrame->getRoads().size())}; - - for (int iRoad{0}; iRoad < roadsNum; ++iRoad) { - - auto& currentRoad{mTimeFrame->getRoads()[iRoad]}; + for (auto& track : mTimeFrame->getTracks()) { std::vector> occurrences; - bool isFakeRoad{false}; - bool isFirstRoadCell{true}; - - for (int iCell{0}; iCell < mTrkParams[0].CellsPerRoad(); ++iCell) { - const int currentCellIndex{currentRoad[iCell]}; - - if (currentCellIndex == constants::UnusedIndex) { - if (isFirstRoadCell) { - continue; - } else { - break; - } - } - - const auto& currentCell{mTimeFrame->getCells()[iCell][currentCellIndex]}; - - if (isFirstRoadCell) { - - const int cl0index{mTimeFrame->getClusters()[iCell][currentCell.getFirstClusterIndex()].clusterId}; - auto cl0labs{mTimeFrame->getClusterLabels(iCell, cl0index)}; - bool found{false}; - for (size_t iOcc{0}; iOcc < occurrences.size(); ++iOcc) { - std::pair& occurrence = occurrences[iOcc]; - for (const auto& label : cl0labs) { - if (label == occurrence.first) { - ++occurrence.second; - found = true; - // break; // uncomment to stop to the first hit - } - } - } - if (!found) { - for (const auto& label : cl0labs) { - occurrences.emplace_back(label, 1); - } - } + occurrences.clear(); - const int cl1index{mTimeFrame->getClusters()[iCell + 1][currentCell.getSecondClusterIndex()].clusterId}; - - const auto& cl1labs{mTimeFrame->getClusterLabels(iCell + 1, cl1index)}; - found = false; - for (size_t iOcc{0}; iOcc < occurrences.size(); ++iOcc) { - std::pair& occurrence = occurrences[iOcc]; - for (auto& label : cl1labs) { - if (label == occurrence.first) { - ++occurrence.second; - found = true; - // break; // uncomment to stop to the first hit - } - } - } - if (!found) { - for (auto& label : cl1labs) { - occurrences.emplace_back(label, 1); - } - } - - isFirstRoadCell = false; + for (int iCluster = 0; iCluster < TrackITSExt::MaxClusters; ++iCluster) { + const int index = track.getClusterIndex(iCluster); + if (index == constants::UnusedIndex) { + continue; } - - const int cl2index{mTimeFrame->getClusters()[iCell + 2][currentCell.getThirdClusterIndex()].clusterId}; - const auto& cl2labs{mTimeFrame->getClusterLabels(iCell + 2, cl2index)}; + auto labels = mTimeFrame->getClusterLabels(iCluster, index); bool found{false}; for (size_t iOcc{0}; iOcc < occurrences.size(); ++iOcc) { std::pair& occurrence = occurrences[iOcc]; - for (auto& label : cl2labs) { + for (const auto& label : labels) { if (label == occurrence.first) { ++occurrence.second; found = true; @@ -257,104 +158,94 @@ void Tracker::computeRoadsMClabels() } } if (!found) { - for (auto& label : cl2labs) { + for (const auto& label : labels) { occurrences.emplace_back(label, 1); } } } - - std::sort(occurrences.begin(), occurrences.end(), [](auto e1, auto e2) { + std::sort(std::begin(occurrences), std::end(occurrences), [](auto e1, auto e2) { return e1.second > e2.second; }); auto maxOccurrencesValue = occurrences[0].first; - mTimeFrame->setRoadLabel(iRoad, maxOccurrencesValue.getRawValue(), isFakeRoad); - } -} - -template -void Tracker::computeTracksMClabels() -{ - for (int iROF{0}; iROF < mTimeFrame->getNrof(); ++iROF) { - for (auto& track : mTimeFrame->getTracks(iROF)) { - std::vector> occurrences; - occurrences.clear(); - - for (int iCluster = 0; iCluster < TrackITSExt::MaxClusters; ++iCluster) { - const int index = track.getClusterIndex(iCluster); - if (index == constants::UnusedIndex) { - continue; - } - auto labels = mTimeFrame->getClusterLabels(iCluster, index); - bool found{false}; - for (size_t iOcc{0}; iOcc < occurrences.size(); ++iOcc) { - std::pair& occurrence = occurrences[iOcc]; - for (const auto& label : labels) { - if (label == occurrence.first) { - ++occurrence.second; - found = true; - // break; // uncomment to stop to the first hit - } - } - } - if (!found) { - for (const auto& label : labels) { - occurrences.emplace_back(label, 1); + uint32_t pattern = track.getPattern(); + // set fake clusters pattern + for (int ic{TrackITSExt::MaxClusters}; ic--;) { + auto clid = track.getClusterIndex(ic); + if (clid != constants::UnusedIndex) { + auto labelsSpan = mTimeFrame->getClusterLabels(ic, clid); + for (const auto& currentLabel : labelsSpan) { + if (currentLabel == maxOccurrencesValue) { + pattern |= 0x1 << (16 + ic); // set bit if correct + break; } } } - std::sort(std::begin(occurrences), std::end(occurrences), [](auto e1, auto e2) { - return e1.second > e2.second; - }); + } + track.setPattern(pattern); + if (occurrences[0].second < track.getNumberOfClusters()) { + maxOccurrencesValue.setFakeFlag(); + } + mTimeFrame->getTracksLabel().emplace_back(maxOccurrencesValue); + } +} - auto maxOccurrencesValue = occurrences[0].first; - uint32_t pattern = track.getPattern(); - // set fake clusters pattern - for (int ic{TrackITSExt::MaxClusters}; ic--;) { - auto clid = track.getClusterIndex(ic); - if (clid != constants::UnusedIndex) { - auto labelsSpan = mTimeFrame->getClusterLabels(ic, clid); - for (const auto& currentLabel : labelsSpan) { - if (currentLabel == maxOccurrencesValue) { - pattern |= 0x1 << (16 + ic); // set bit if correct - break; - } - } - } - } - track.setPattern(pattern); - if (occurrences[0].second < track.getNumberOfClusters()) { - maxOccurrencesValue.setFakeFlag(); +template +void Tracker::rectifyClusterIndices() +{ + for (auto& track : mTimeFrame->getTracks()) { + for (int iCluster = 0; iCluster < TrackITSExt::MaxClusters; ++iCluster) { + const int index = track.getClusterIndex(iCluster); + if (index != constants::UnusedIndex) { + track.setExternalClusterIndex(iCluster, mTimeFrame->getClusterExternalIndex(iCluster, index)); } - mTimeFrame->getTracksLabel(iROF).emplace_back(maxOccurrencesValue); } } } -template -void Tracker::rectifyClusterIndices() +template +void Tracker::sortTracks() { - for (int iROF{0}; iROF < mTimeFrame->getNrof(); ++iROF) { - for (auto& track : mTimeFrame->getTracks(iROF)) { - for (int iCluster = 0; iCluster < TrackITSExt::MaxClusters; ++iCluster) { - const int index = track.getClusterIndex(iCluster); - if (index != constants::UnusedIndex) { - track.setExternalClusterIndex(iCluster, mTimeFrame->getClusterExternalIndex(iCluster, index)); - } - } + auto& trks = mTimeFrame->getTracks(); + bounded_vector indices(trks.size(), mMemoryPool.get()); + std::iota(indices.begin(), indices.end(), 0); + std::sort(indices.begin(), indices.end(), [&trks](size_t i, size_t j) { + // provide tracks sorted by lower-bound + const auto& a = trks[i]; + const auto& b = trks[j]; + const auto aLower = a.getTimeStamp().getTimeStamp() - a.getTimeStamp().getTimeStampError(); + const auto bLower = b.getTimeStamp().getTimeStamp() - b.getTimeStamp().getTimeStampError(); + if (aLower != bLower) { + return aLower < bLower; + } + return a.isBetter(b, 1e9); // then sort tracks in quality + }); + bounded_vector sortedTrks(mMemoryPool.get()); + sortedTrks.reserve(trks.size()); + for (size_t idx : indices) { + sortedTrks.push_back(trks[idx]); + } + trks.swap(sortedTrks); + if (mTimeFrame->hasMCinformation()) { + auto& trksLabels = mTimeFrame->getTracksLabel(); + bounded_vector sortedLabels(mMemoryPool.get()); + sortedLabels.reserve(trksLabels.size()); + for (size_t idx : indices) { + sortedLabels.push_back(trksLabels[idx]); } + trksLabels.swap(sortedLabels); } } -template -void Tracker::adoptTimeFrame(TimeFrame& tf) +template +void Tracker::adoptTimeFrame(TimeFrame& tf) { mTimeFrame = &tf; mTraits->adoptTimeFrame(&tf); } -template -void Tracker::printSummary() const +template +void Tracker::printSummary() const { auto avgTF = mTotalTime * 1.e-3 / ((mTimeFrameCounter > 0) ? (double)mTimeFrameCounter : -1.0); auto avgTFwithDropped = mTotalTime * 1.e-3 / (((mTimeFrameCounter + mNumberOfDroppedTFs) > 0) ? (double)(mTimeFrameCounter + mNumberOfDroppedTFs) : -1.0); diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx index da7c9afdd3ed6..f996c0d25e7d7 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx @@ -14,27 +14,22 @@ /// #include -#include #include #include #include -#ifdef OPTIMISATION_OUTPUT -#include -#include -#endif - #include #include #include "CommonConstants/MathConstants.h" #include "DetectorsBase/Propagator.h" #include "GPUCommonMath.h" +#include "ITStracking/BoundedAllocator.h" #include "ITStracking/Cell.h" #include "ITStracking/Constants.h" -#include "ITStracking/TrackerTraits.h" -#include "ITStracking/BoundedAllocator.h" #include "ITStracking/IndexTableUtils.h" +#include "ITStracking/ROFLookupTables.h" +#include "ITStracking/TrackerTraits.h" #include "ITStracking/Tracklet.h" #include "ReconstructionDataFormats/Track.h" @@ -49,42 +44,38 @@ struct PassMode { using TwoPassInsert = std::integral_constant; }; -template -void TrackerTraits::computeLayerTracklets(const int iteration, int iROFslice, int iVertex) +template +void TrackerTraits::computeLayerTracklets(const int iteration, int iVertex) { -#ifdef OPTIMISATION_OUTPUT - static int iter{0}; - std::ofstream off(std::format("tracklets{}.txt", iter++)); -#endif - for (int iLayer = 0; iLayer < mTrkParams[iteration].TrackletsPerRoad(); ++iLayer) { mTimeFrame->getTracklets()[iLayer].clear(); mTimeFrame->getTrackletsLabel(iLayer).clear(); if (iLayer > 0) { - std::fill(mTimeFrame->getTrackletsLookupTable()[iLayer - 1].begin(), - mTimeFrame->getTrackletsLookupTable()[iLayer - 1].end(), 0); + std::fill(mTimeFrame->getTrackletsLookupTable()[iLayer - 1].begin(), mTimeFrame->getTrackletsLookupTable()[iLayer - 1].end(), 0); } } - const Vertex diamondVert({mTrkParams[iteration].Diamond[0], mTrkParams[iteration].Diamond[1], mTrkParams[iteration].Diamond[2]}, {25.e-6f, 0.f, 0.f, 25.e-6f, 0.f, 36.f}, 1, 1.f); + const Vertex diamondVert(mTrkParams[iteration].Diamond, mTrkParams[iteration].DiamondCov, 1, 1.f); gsl::span diamondSpan(&diamondVert, 1); - int startROF{mTrkParams[iteration].nROFsPerIterations > 0 ? iROFslice * mTrkParams[iteration].nROFsPerIterations : 0}; - int endROF{o2::gpu::GPUCommonMath::Min(mTrkParams[iteration].nROFsPerIterations > 0 ? (iROFslice + 1) * mTrkParams[iteration].nROFsPerIterations + mTrkParams[iteration].DeltaROF : mTimeFrame->getNrof(), mTimeFrame->getNrof())}; mTaskArena->execute([&] { auto forTracklets = [&](auto Tag, int iLayer, int pivotROF, int base, int& offset) -> int { - if (!mTimeFrame->mMultiplicityCutMask[pivotROF]) { + if (!mTimeFrame->getROFMaskView().isROFEnabled(iLayer, pivotROF)) { return 0; } - int minROF = o2::gpu::CAMath::Max(startROF, pivotROF - mTrkParams[iteration].DeltaROF); - int maxROF = o2::gpu::CAMath::Min(endROF - 1, pivotROF + mTrkParams[iteration].DeltaROF); - gsl::span primaryVertices = mTrkParams[iteration].UseDiamond ? diamondSpan : mTimeFrame->getPrimaryVertices(minROF, maxROF); + gsl::span primaryVertices = mTrkParams[iteration].UseDiamond ? diamondSpan : mTimeFrame->getPrimaryVertices(iLayer, pivotROF); if (primaryVertices.empty()) { return 0; } const int startVtx = iVertex >= 0 ? iVertex : 0; const int endVtx = iVertex >= 0 ? o2::gpu::CAMath::Min(iVertex + 1, int(primaryVertices.size())) : int(primaryVertices.size()); - if (endVtx <= startVtx) { + if (endVtx <= startVtx || (iVertex + 1) > primaryVertices.size()) { + return 0; + } + + // does this layer have any overlap with the next layer + const auto& rofOverlap = mTimeFrame->getROFOverlapTableView().getOverlap(iLayer, iLayer + 1, pivotROF); + if (!rofOverlap.getEntries()) { return 0; } @@ -107,10 +98,12 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iROF for (int iV = startVtx; iV < endVtx; ++iV) { const auto& pv = primaryVertices[iV]; + if (!mTimeFrame->getROFVertexLookupTableView().isVertexCompatible(iLayer, pivotROF, pv)) { + continue; + } if ((pv.isFlagSet(Vertex::Flags::UPCMode) && iteration != 3) || (iteration == 3 && !pv.isFlagSet(Vertex::Flags::UPCMode))) { continue; } - const float resolution = o2::gpu::CAMath::Sqrt(math_utils::Sq(mTimeFrame->getPositionResolution(iLayer)) + math_utils::Sq(mTrkParams[iteration].PVres) / float(pv.getNContributors())); const float tanLambda = (currentCluster.zCoordinate - pv.getZ()) * inverseR0; const float zAtRmin = tanLambda * (mTimeFrame->getMinR(iLayer + 1) - currentCluster.radius) + currentCluster.zCoordinate; @@ -118,8 +111,7 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iROF const float sqInvDeltaZ0 = 1.f / (math_utils::Sq(currentCluster.zCoordinate - pv.getZ()) + constants::Tolerance); const float sigmaZ = o2::gpu::CAMath::Sqrt( math_utils::Sq(resolution) * math_utils::Sq(tanLambda) * ((math_utils::Sq(inverseR0) + sqInvDeltaZ0) * math_utils::Sq(meanDeltaR) + 1.f) + math_utils::Sq(meanDeltaR * mTimeFrame->getMSangle(iLayer))); - - auto bins = getBinsRect(currentCluster, iLayer + 1, zAtRmin, zAtRmax, sigmaZ * mTrkParams[iteration].NSigmaCut, mTimeFrame->getPhiCut(iLayer)); + const auto bins = getBinsRect(iteration, currentCluster, iLayer + 1, zAtRmin, zAtRmax, sigmaZ * mTrkParams[iteration].NSigmaCut, mTimeFrame->getPhiCut(iLayer)); if (bins.x == 0 && bins.y == 0 && bins.z == 0 && bins.w == 0) { continue; } @@ -128,20 +120,26 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iROF phiBinsNum += mTrkParams[iteration].PhiBins; } - for (int targetROF{minROF}; targetROF <= maxROF; ++targetROF) { - if (!mTimeFrame->mMultiplicityCutMask[targetROF]) { + for (int targetROF = rofOverlap.getFirstEntry(); targetROF < rofOverlap.getEntriesBound(); ++targetROF) { + if (!mTimeFrame->getROFMaskView().isROFEnabled(iLayer + 1, targetROF)) { continue; } auto layer1 = mTimeFrame->getClustersOnLayer(targetROF, iLayer + 1); if (layer1.empty()) { continue; } + const auto ts = mTimeFrame->getROFOverlapTableView().getTimeStamp(iLayer, pivotROF, iLayer + 1, targetROF); + if (!ts.isCompatible(pv.getTimeStamp())) { + continue; + } + const auto& targetIndexTable = mTimeFrame->getIndexTable(targetROF, iLayer + 1); + const int zBinRange = (bins.z - bins.x) + 1; for (int iPhi = 0; iPhi < phiBinsNum; ++iPhi) { const int iPhiBin = (bins.y + iPhi) % mTrkParams[iteration].PhiBins; - const int firstBinIdx = mTimeFrame->mIndexTableUtils.getBinIndex(bins.x, iPhiBin); - const int maxBinIdx = firstBinIdx + (bins.z - bins.x) + 1; - const int firstRow = mTimeFrame->getIndexTable(targetROF, iLayer + 1)[firstBinIdx]; - const int lastRow = mTimeFrame->getIndexTable(targetROF, iLayer + 1)[maxBinIdx]; + const int firstBinIdx = mTimeFrame->getIndexTableUtils().getBinIndex(bins.x, iPhiBin); + const int maxBinIdx = firstBinIdx + zBinRange; + const int firstRow = targetIndexTable[firstBinIdx]; + const int lastRow = targetIndexTable[maxBinIdx]; for (int iNext = firstRow; iNext < lastRow; ++iNext) { if (iNext >= int(layer1.size())) { break; @@ -150,38 +148,20 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iROF if (mTimeFrame->isClusterUsed(iLayer + 1, nextCluster.clusterId)) { continue; } - float deltaPhi = o2::gpu::GPUCommonMath::Abs(currentCluster.phi - nextCluster.phi); - float deltaZ = o2::gpu::GPUCommonMath::Abs((tanLambda * (nextCluster.radius - currentCluster.radius)) + currentCluster.zCoordinate - nextCluster.zCoordinate); - -#ifdef OPTIMISATION_OUTPUT - MCCompLabel label; - int currentId{currentCluster.clusterId}; - int nextId{nextCluster.clusterId}; - for (auto& lab1 : mTimeFrame->getClusterLabels(iLayer, currentId)) { - for (auto& lab2 : mTimeFrame->getClusterLabels(iLayer + 1, nextId)) { - if (lab1 == lab2 && lab1.isValid()) { - label = lab1; - break; - } - } - if (label.isValid()) { - break; - } - } - off << std::format("{}\t{:d}\t{}\t{}\t{}\t{}", iLayer, label.isValid(), (tanLambda * (nextCluster.radius - currentCluster.radius) + currentCluster.zCoordinate - nextCluster.zCoordinate) / sigmaZ, tanLambda, resolution, sigmaZ) << std::endl; -#endif + const float deltaPhi = o2::gpu::CAMath::Abs(o2::math_utils::toPMPi(currentCluster.phi - nextCluster.phi)); + const float deltaZ = o2::gpu::CAMath::Abs((tanLambda * (nextCluster.radius - currentCluster.radius)) + currentCluster.zCoordinate - nextCluster.zCoordinate); if (deltaZ / sigmaZ < mTrkParams[iteration].NSigmaCut && ((deltaPhi < mTimeFrame->getPhiCut(iLayer) || o2::gpu::GPUCommonMath::Abs(deltaPhi - o2::constants::math::TwoPI) < mTimeFrame->getPhiCut(iLayer)))) { const float phi{o2::gpu::CAMath::ATan2(currentCluster.yCoordinate - nextCluster.yCoordinate, currentCluster.xCoordinate - nextCluster.xCoordinate)}; const float tanL = (currentCluster.zCoordinate - nextCluster.zCoordinate) / (currentCluster.radius - nextCluster.radius); if constexpr (decltype(Tag)::value == PassMode::OnePass::value) { - tracklets.emplace_back(currentSortedIndex, mTimeFrame->getSortedIndex(targetROF, iLayer + 1, iNext), tanL, phi, pivotROF, targetROF); + tracklets.emplace_back(currentSortedIndex, mTimeFrame->getSortedIndex(targetROF, iLayer + 1, iNext), tanL, phi, ts); } else if constexpr (decltype(Tag)::value == PassMode::TwoPassCount::value) { ++localCount; } else if constexpr (decltype(Tag)::value == PassMode::TwoPassInsert::value) { const int idx = base + offset++; - tracklets[idx] = Tracklet(currentSortedIndex, mTimeFrame->getSortedIndex(targetROF, iLayer + 1, iNext), tanL, phi, pivotROF, targetROF); + tracklets[idx] = Tracklet(currentSortedIndex, mTimeFrame->getSortedIndex(targetROF, iLayer + 1, iNext), tanL, phi, ts); } } } @@ -194,47 +174,34 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iROF int dummy{0}; if (mTaskArena->max_concurrency() <= 1) { - for (int pivotROF{startROF}; pivotROF < endROF; ++pivotROF) { - for (int iLayer{0}; iLayer < mTrkParams[iteration].TrackletsPerRoad(); ++iLayer) { + for (int iLayer{0}; iLayer < mTrkParams[iteration].TrackletsPerRoad(); ++iLayer) { + const int startROF = 0, endROF = mTimeFrame->getROFOverlapTableView().getLayer(iLayer).mNROFsTF; + for (int pivotROF{startROF}; pivotROF < endROF; ++pivotROF) { forTracklets(PassMode::OnePass{}, iLayer, pivotROF, 0, dummy); } } } else { - bounded_vector> perROFCount(mTrkParams[iteration].TrackletsPerRoad(), bounded_vector(endROF - startROF + 1, 0, mMemoryPool.get()), mMemoryPool.get()); - tbb::parallel_for( - tbb::blocked_range2d(0, mTrkParams[iteration].TrackletsPerRoad(), 1, - startROF, endROF, 1), - [&](auto const& Range) { - for (int iLayer{Range.rows().begin()}; iLayer < Range.rows().end(); ++iLayer) { - for (int pivotROF = Range.cols().begin(); pivotROF < Range.cols().end(); ++pivotROF) { - perROFCount[iLayer][pivotROF - startROF] = forTracklets(PassMode::TwoPassCount{}, iLayer, pivotROF, 0, dummy); - } - } - }); - tbb::parallel_for(0, mTrkParams[iteration].TrackletsPerRoad(), [&](const int iLayer) { - std::exclusive_scan(perROFCount[iLayer].begin(), perROFCount[iLayer].end(), perROFCount[iLayer].begin(), 0); - mTimeFrame->getTracklets()[iLayer].resize(perROFCount[iLayer].back()); - }); - - tbb::parallel_for( - tbb::blocked_range2d(0, mTrkParams[iteration].TrackletsPerRoad(), 1, - startROF, endROF, 1), - [&](auto const& Range) { - for (int iLayer{Range.rows().begin()}; iLayer < Range.rows().end(); ++iLayer) { - if (perROFCount[iLayer].back() == 0) { - continue; - } - for (int pivotROF = Range.cols().begin(); pivotROF < Range.cols().end(); ++pivotROF) { - int baseIdx = perROFCount[iLayer][pivotROF - startROF]; - if (baseIdx == perROFCount[iLayer][pivotROF - startROF + 1]) { - continue; - } - int localIdx = 0; - forTracklets(PassMode::TwoPassInsert{}, iLayer, pivotROF, baseIdx, localIdx); - } + const int startROF = 0, endROF = mTimeFrame->getROFOverlapTableView().getLayer(iLayer).mNROFsTF; + bounded_vector perROFCount((endROF - startROF) + 1, mMemoryPool.get()); + tbb::parallel_for(startROF, endROF, [&](const int pivotROF) { + perROFCount[pivotROF - startROF] = forTracklets(PassMode::TwoPassCount{}, iLayer, pivotROF, 0, dummy); + }); + std::exclusive_scan(perROFCount.begin(), perROFCount.end(), perROFCount.begin(), 0); + const int nTracklets = perROFCount.back(); + mTimeFrame->getTracklets()[iLayer].resize(nTracklets); + if (nTracklets == 0) { + return; + } + tbb::parallel_for(startROF, endROF, [&](const int pivotROF) { + int baseIdx = perROFCount[pivotROF - startROF]; + if (baseIdx == perROFCount[pivotROF + 1 - startROF]) { + return; } + int localIdx = 0; + forTracklets(PassMode::TwoPassInsert{}, iLayer, pivotROF, baseIdx, localIdx); }); + }); } tbb::parallel_for(0, mTrkParams[iteration].TrackletsPerRoad(), [&](const int iLayer) { @@ -286,16 +253,11 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iROF }); } }); -} // namespace o2::its +} -template -void TrackerTraits::computeLayerCells(const int iteration) +template +void TrackerTraits::computeLayerCells(const int iteration) { -#ifdef OPTIMISATION_OUTPUT - static int iter{0}; - std::ofstream off(std::format("cells{}.txt", iter++)); -#endif - for (int iLayer = 0; iLayer < mTrkParams[iteration].CellsPerRoad(); ++iLayer) { deepVectorClear(mTimeFrame->getCells()[iLayer]); if (iLayer > 0) { @@ -315,24 +277,15 @@ void TrackerTraits::computeLayerCells(const int iteration) int foundCells{0}; for (int iNextTracklet{nextLayerFirstTrackletIndex}; iNextTracklet < nextLayerLastTrackletIndex; ++iNextTracklet) { const Tracklet& nextTracklet{mTimeFrame->getTracklets()[iLayer + 1][iNextTracklet]}; - const auto& nextLbl = mTimeFrame->getTrackletsLabel(iLayer + 1)[iNextTracklet]; if (mTimeFrame->getTracklets()[iLayer + 1][iNextTracklet].firstClusterIndex != nextLayerClusterIndex) { break; } - if (mTrkParams[iteration].DeltaROF && currentTracklet.getSpanRof(nextTracklet) > mTrkParams[iteration].DeltaROF) { // TODO this has to be improved for the staggering + if (!currentTracklet.getTimeStamp().isCompatible(nextTracklet.getTimeStamp())) { continue; } - const float deltaTanLambda{std::abs(currentTracklet.tanLambda - nextTracklet.tanLambda)}; - -#ifdef OPTIMISATION_OUTPUT - float resolution{o2::gpu::CAMath::Sqrt(0.5f * (mTrkParams[iteration].SystErrorZ2[iLayer] + mTrkParams[iteration].SystErrorZ2[iLayer + 1] + mTrkParams[iteration].SystErrorZ2[iLayer + 2] + mTrkParams[iteration].SystErrorY2[iLayer] + mTrkParams[iteration].SystErrorY2[iLayer + 1] + mTrkParams[iteration].SystErrorY2[iLayer + 2])) / mTrkParams[iteration].LayerResolution[iLayer]}; - resolution = resolution > 1.e-12 ? resolution : 1.f; - bool good{mTimeFrame->getTrackletsLabel(iLayer)[iTracklet] == mTimeFrame->getTrackletsLabel(iLayer + 1)[iNextTracklet]}; - float signedDelta{currentTracklet.tanLambda - nextTracklet.tanLambda}; - off << std::format("{}\t{:d}\t{}\t{}\t{}\t{}", iLayer, good, signedDelta, signedDelta / (mTrkParams[iteration].CellDeltaTanLambdaSigma), tanLambda, resolution) << std::endl; -#endif - if (deltaTanLambda / mTrkParams[iteration].CellDeltaTanLambdaSigma < mTrkParams[iteration].NSigmaCut) { + const float deltaTanLambdaSigma = std::abs(currentTracklet.tanLambda - nextTracklet.tanLambda) / mTrkParams[iteration].CellDeltaTanLambdaSigma; + if (deltaTanLambdaSigma < mTrkParams[iteration].NSigmaCut) { /// Track seed preparation. Clusters are numbered progressively from the innermost going outward. const int clusId[3]{ @@ -374,13 +327,16 @@ void TrackerTraits::computeLayerCells(const int iteration) chi2 += predChi2; } if (good) { + TimeEstBC ts = currentTracklet.getTimeStamp(); + ts += nextTracklet.getTimeStamp(); if constexpr (decltype(Tag)::value == PassMode::OnePass::value) { - layerCells.emplace_back(iLayer, clusId[0], clusId[1], clusId[2], iTracklet, iNextTracklet, track, chi2); + // + layerCells.emplace_back(iLayer, clusId[0], clusId[1], clusId[2], iTracklet, iNextTracklet, track, chi2, ts); ++foundCells; } else if constexpr (decltype(Tag)::value == PassMode::TwoPassCount::value) { ++foundCells; } else if constexpr (decltype(Tag)::value == PassMode::TwoPassInsert::value) { - layerCells[offset++] = CellSeedN(iLayer, clusId[0], clusId[1], clusId[2], iTracklet, iNextTracklet, track, chi2); + layerCells[offset++] = CellSeedN(iLayer, clusId[0], clusId[1], clusId[2], iTracklet, iNextTracklet, track, chi2, ts); } else { static_assert(false, "Unknown mode!"); } @@ -446,13 +402,9 @@ void TrackerTraits::computeLayerCells(const int iteration) }); } -template -void TrackerTraits::findCellsNeighbours(const int iteration) +template +void TrackerTraits::findCellsNeighbours(const int iteration) { -#ifdef OPTIMISATION_OUTPUT - std::ofstream off(std::format("cellneighs{}.txt", iteration)); -#endif - struct Neighbor { int cell{-1}, nextCell{-1}, level{-1}; }; @@ -477,33 +429,17 @@ void TrackerTraits::findCellsNeighbours(const int iteration) int foundNextCells{0}; for (int iNextCell{nextLayerFirstCellIndex}; iNextCell < nextLayerLastCellIndex; ++iNextCell) { auto nextCellSeed{mTimeFrame->getCells()[iLayer + 1][iNextCell]}; /// copy - if (nextCellSeed.getFirstTrackletIndex() != nextLayerTrackletIndex) { + if (nextCellSeed.getFirstTrackletIndex() != nextLayerTrackletIndex || !currentCellSeed.getTimeStamp().isCompatible(nextCellSeed.getTimeStamp())) { break; } - if (mTrkParams[iteration].DeltaROF) { // TODO this has to be improved for the staggering - const auto& trkl00 = mTimeFrame->getTracklets()[iLayer][currentCellSeed.getFirstTrackletIndex()]; - const auto& trkl01 = mTimeFrame->getTracklets()[iLayer + 1][currentCellSeed.getSecondTrackletIndex()]; - const auto& trkl10 = mTimeFrame->getTracklets()[iLayer + 1][nextCellSeed.getFirstTrackletIndex()]; - const auto& trkl11 = mTimeFrame->getTracklets()[iLayer + 2][nextCellSeed.getSecondTrackletIndex()]; - if ((std::max({trkl00.getMaxRof(), trkl01.getMaxRof(), trkl10.getMaxRof(), trkl11.getMaxRof()}) - - std::min({trkl00.getMinRof(), trkl01.getMinRof(), trkl10.getMinRof(), trkl11.getMinRof()})) > mTrkParams[0].DeltaROF) { - continue; - } - } - if (!nextCellSeed.rotate(currentCellSeed.getAlpha()) || !nextCellSeed.propagateTo(currentCellSeed.getX(), getBz())) { continue; } - float chi2 = currentCellSeed.getPredictedChi2(nextCellSeed); /// TODO: switch to the chi2 wrt cluster to avoid correlation -#ifdef OPTIMISATION_OUTPUT - bool good{mTimeFrame->getCellsLabel(iLayer)[iCell] == mTimeFrame->getCellsLabel(iLayer + 1)[iNextCell]}; - off << std::format("{}\t{:d}\t{}", iLayer, good, chi2) << std::endl; -#endif - - if (chi2 > mTrkParams[0].MaxChi2ClusterAttachment) { + float chi2 = currentCellSeed.getPredictedChi2(nextCellSeed); /// TODO: switch to the chi2 wrt cluster to avoid correlation + if (chi2 > mTrkParams[iteration].MaxChi2ClusterAttachment) { continue; } @@ -577,16 +513,11 @@ void TrackerTraits::findCellsNeighbours(const int iteration) }); } -template -void TrackerTraits::processNeighbours(int iLayer, int iLevel, const bounded_vector& currentCellSeed, const bounded_vector& currentCellId, bounded_vector& updatedCellSeeds, bounded_vector& updatedCellsIds) +template +void TrackerTraits::processNeighbours(int iLayer, int iLevel, const bounded_vector& currentCellSeed, const bounded_vector& currentCellId, bounded_vector& updatedCellSeeds, bounded_vector& updatedCellsIds) { - CA_DEBUGGER(std::cout << "Processing neighbours layer " << iLayer << " level " << iLevel << ", size of the cell seeds: " << currentCellSeed.size() << std::endl); auto propagator = o2::base::Propagator::Instance(); -#ifdef CA_DEBUG - int failed[5]{0, 0, 0, 0, 0}, attempts{0}, failedByMismatch{0}; -#endif - mTaskArena->execute([&] { auto forCellNeighbours = [&](auto Tag, int iCell, int offset = 0) -> int { const auto& currentCell{currentCellSeed[iCell]}; @@ -607,32 +538,32 @@ void TrackerTraits::processNeighbours(int iLayer, int iLevel, const bou const int endNeighbourId{mTimeFrame->getCellsNeighboursLUT()[iLayer - 1][cellId]}; int foundSeeds{0}; for (int iNeighbourCell{startNeighbourId}; iNeighbourCell < endNeighbourId; ++iNeighbourCell) { - CA_DEBUGGER(attempts++); const int neighbourCellId = mTimeFrame->getCellsNeighbours()[iLayer - 1][iNeighbourCell]; const auto& neighbourCell = mTimeFrame->getCells()[iLayer - 1][neighbourCellId]; if (neighbourCell.getSecondTrackletIndex() != currentCell.getFirstTrackletIndex()) { - CA_DEBUGGER(failedByMismatch++); continue; } - if (mTimeFrame->isClusterUsed(iLayer - 1, neighbourCell.getFirstClusterIndex())) { + if (!currentCell.getTimeStamp().isCompatible(neighbourCell.getTimeStamp())) { continue; } if (currentCell.getLevel() - 1 != neighbourCell.getLevel()) { - CA_DEBUGGER(failed[0]++); + continue; + } + if (mTimeFrame->isClusterUsed(iLayer - 1, neighbourCell.getFirstClusterIndex())) { continue; } /// Let's start the fitting procedure CellSeedN seed{currentCell}; + seed.getTimeStamp() = currentCell.getTimeStamp(); + seed.getTimeStamp() += neighbourCell.getTimeStamp(); const auto& trHit = mTimeFrame->getTrackingFrameInfoOnLayer(iLayer - 1)[neighbourCell.getFirstClusterIndex()]; if (!seed.rotate(trHit.alphaTrackingFrame)) { - CA_DEBUGGER(failed[1]++); continue; } if (!propagator->propagateToX(seed, trHit.xTrackingFrame, getBz(), o2::base::PropagatorImpl::MAX_SIN_PHI, o2::base::PropagatorImpl::MAX_STEP, mTrkParams[0].CorrType)) { - CA_DEBUGGER(failed[2]++); continue; } @@ -644,12 +575,10 @@ void TrackerTraits::processNeighbours(int iLayer, int iLevel, const bou auto predChi2{seed.getPredictedChi2Quiet(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)}; if ((predChi2 > mTrkParams[0].MaxChi2ClusterAttachment) || predChi2 < 0.f) { - CA_DEBUGGER(failed[3]++); continue; } seed.setChi2(seed.getChi2() + predChi2); if (!seed.o2::track::TrackParCov::update(trHit.positionTrackingFrame, trHit.covarianceTrackingFrame)) { - CA_DEBUGGER(failed[4]++); continue; } @@ -703,20 +632,10 @@ void TrackerTraits::processNeighbours(int iLayer, int iLevel, const bou }); } }); - -#ifdef CA_DEBUG - std::cout << "\t\t- Found " << updatedCellSeeds.size() << " cell seeds out of " << attempts << " attempts" << std::endl; - std::cout << "\t\t\t> " << failed[0] << " failed because of level" << std::endl; - std::cout << "\t\t\t> " << failed[1] << " failed because of rotation" << std::endl; - std::cout << "\t\t\t> " << failed[2] << " failed because of propagation" << std::endl; - std::cout << "\t\t\t> " << failed[3] << " failed because of chi2 cut" << std::endl; - std::cout << "\t\t\t> " << failed[4] << " failed because of update" << std::endl; - std::cout << "\t\t\t> " << failedByMismatch << " failed because of mismatch" << std::endl; -#endif } -template -void TrackerTraits::findRoads(const int iteration) +template +void TrackerTraits::findRoads(const int iteration) { bounded_vector> firstClusters(mTrkParams[iteration].NLayers, bounded_vector(mMemoryPool.get()), mMemoryPool.get()); bounded_vector> sharedFirstClusters(mTrkParams[iteration].NLayers, bounded_vector(mMemoryPool.get()), mMemoryPool.get()); @@ -860,31 +779,29 @@ void TrackerTraits::findRoads(const int iteration) continue; } - std::array rofs{INT_MAX, INT_MAX, INT_MAX}; + bool firstCls{true}; + TimeEstBC ts; for (int iLayer{0}; iLayer < mTrkParams[0].NLayers; ++iLayer) { if (track.getClusterIndex(iLayer) == constants::UnusedIndex) { continue; } mTimeFrame->markUsedCluster(iLayer, track.getClusterIndex(iLayer)); int currentROF = mTimeFrame->getClusterROF(iLayer, track.getClusterIndex(iLayer)); - for (int iR{0}; iR < 3; ++iR) { - if (rofs[iR] == INT_MAX) { - rofs[iR] = currentROF; - } - if (rofs[iR] == currentROF) { - break; + auto rofTS = mTimeFrame->getROFOverlapTableView().getLayer(iLayer).getROFTimeBounds(currentROF, true); + if (firstCls) { + firstCls = false; + ts = rofTS; + } else { + if (!ts.isCompatible(rofTS)) { + LOGP(fatal, "TS {}+/-{} are incompatible with {}+/-{}, this should not happen!", rofTS.getTimeStamp(), rofTS.getTimeStampError(), ts.getTimeStamp(), ts.getTimeStampError()); } + ts += rofTS; } } - if (rofs[2] != INT_MAX) { - continue; - } + track.getTimeStamp() = ts.makeSymmetrical(); track.setUserField(0); track.getParamOut().setUserField(0); - if (rofs[1] != INT_MAX) { - track.setNextROFbit(); - } - mTimeFrame->getTracks(o2::gpu::CAMath::Min(rofs[0], rofs[1])).emplace_back(track); + mTimeFrame->getTracks().emplace_back(track); firstClusters[firstLayer].push_back(firstCluster); if (isFirstShared) { @@ -898,164 +815,24 @@ void TrackerTraits::findRoads(const int iteration) std::sort(sharedFirstClusters[iLayer].begin(), sharedFirstClusters[iLayer].end()); } - for (int iROF{0}; iROF < mTimeFrame->getNrof(); ++iROF) { - for (auto& track : mTimeFrame->getTracks(iROF)) { - int firstLayer{mTrkParams[0].NLayers}, firstCluster{constants::UnusedIndex}; - for (int iLayer{0}; iLayer < mTrkParams[0].NLayers; ++iLayer) { - if (track.getClusterIndex(iLayer) == constants::UnusedIndex) { - continue; - } - firstLayer = iLayer; - firstCluster = track.getClusterIndex(iLayer); - break; - } - if (std::binary_search(sharedFirstClusters[firstLayer].begin(), sharedFirstClusters[firstLayer].end(), firstCluster)) { - track.setSharedClusters(); - } - } - } -} - -template -void TrackerTraits::extendTracks(const int iteration) -{ - for (int rof{0}; rof < mTimeFrame->getNrof(); ++rof) { - for (auto& track : mTimeFrame->getTracks(rof)) { - auto backup{track}; - bool success{false}; - // the order here biases towards top extension, tracks should probably be fitted separately in the directions and then compared. - if ((mTrkParams[iteration].UseTrackFollowerMix || mTrkParams[iteration].UseTrackFollowerTop) && track.getLastClusterLayer() != mTrkParams[iteration].NLayers - 1) { - success = success || trackFollowing(&track, rof, true, iteration); - } - if ((mTrkParams[iteration].UseTrackFollowerMix || (mTrkParams[iteration].UseTrackFollowerBot && !success)) && track.getFirstClusterLayer() != 0) { - success = success || trackFollowing(&track, rof, false, iteration); - } - if (success) { - /// We have to refit the track - track.resetCovariance(); - track.setChi2(0); - bool fitSuccess = fitTrack(track, 0, mTrkParams[iteration].NLayers, 1, mTrkParams[iteration].MaxChi2ClusterAttachment, mTrkParams[0].MaxChi2NDF); - if (!fitSuccess) { - track = backup; - continue; - } - track.getParamOut() = track; - track.resetCovariance(); - track.setChi2(0); - fitSuccess = fitTrack(track, mTrkParams[iteration].NLayers - 1, -1, -1, mTrkParams[iteration].MaxChi2ClusterAttachment, mTrkParams[0].MaxChi2NDF, 50.); - if (!fitSuccess) { - track = backup; - continue; - } - mTimeFrame->mNExtendedTracks++; - mTimeFrame->mNExtendedUsedClusters += track.getNClusters() - backup.getNClusters(); - auto pattern = track.getPattern(); - auto diff = (pattern & ~backup.getPattern()) & 0xff; - pattern |= (diff << 24); - track.setPattern(pattern); - /// Make sure that the newly attached clusters get marked as used - for (int iLayer{0}; iLayer < mTrkParams[iteration].NLayers; ++iLayer) { - if (track.getClusterIndex(iLayer) == constants::UnusedIndex) { - continue; - } - mTimeFrame->markUsedCluster(iLayer, track.getClusterIndex(iLayer)); - } - } - } - } -} - -template -void TrackerTraits::findShortPrimaries() -{ - const auto propagator = o2::base::Propagator::Instance(); - mTimeFrame->fillPrimaryVerticesXandAlpha(); - - for (auto& cell : mTimeFrame->getCells()[0]) { - auto& cluster3_glo = mTimeFrame->getClusters()[2][cell.getThirdClusterIndex()]; - auto& cluster2_glo = mTimeFrame->getClusters()[1][cell.getSecondClusterIndex()]; - auto& cluster1_glo = mTimeFrame->getClusters()[0][cell.getFirstClusterIndex()]; - if (mTimeFrame->isClusterUsed(2, cluster1_glo.clusterId) || - mTimeFrame->isClusterUsed(1, cluster2_glo.clusterId) || - mTimeFrame->isClusterUsed(0, cluster3_glo.clusterId)) { - continue; - } - - std::array rofs{ - mTimeFrame->getClusterROF(2, cluster3_glo.clusterId), - mTimeFrame->getClusterROF(1, cluster2_glo.clusterId), - mTimeFrame->getClusterROF(0, cluster1_glo.clusterId)}; - if (rofs[0] != rofs[1] && rofs[1] != rofs[2] && rofs[0] != rofs[2]) { - continue; - } - - int rof{rofs[0]}; - if (rofs[1] == rofs[2]) { - rof = rofs[2]; - } - - auto pvs{mTimeFrame->getPrimaryVertices(rof)}; - auto pvsXAlpha{mTimeFrame->getPrimaryVerticesXAlpha(rof)}; - - const auto& cluster3_tf = mTimeFrame->getTrackingFrameInfoOnLayer(2)[cluster3_glo.clusterId]; - TrackITSExt temporaryTrack{buildTrackSeed(cluster1_glo, cluster2_glo, cluster3_tf)}; - temporaryTrack.setExternalClusterIndex(0, cluster1_glo.clusterId, true); - temporaryTrack.setExternalClusterIndex(1, cluster2_glo.clusterId, true); - temporaryTrack.setExternalClusterIndex(2, cluster3_glo.clusterId, true); - - /// add propagation to the primary vertices compatible with the ROF(s) of the cell - bool fitSuccess = fitTrack(temporaryTrack, 1, -1, -1); - if (!fitSuccess) { - continue; - } - fitSuccess = false; - - TrackITSExt bestTrack{temporaryTrack}, backup{temporaryTrack}; - float bestChi2{std::numeric_limits::max()}; - for (int iV{0}; iV < (int)pvs.size(); ++iV) { - temporaryTrack = backup; - if (!temporaryTrack.rotate(pvsXAlpha[iV][1])) { - continue; - } - if (!propagator->propagateTo(temporaryTrack, pvsXAlpha[iV][0], true)) { + for (auto& track : mTimeFrame->getTracks()) { + int firstLayer{mTrkParams[0].NLayers}, firstCluster{constants::UnusedIndex}; + for (int iLayer{0}; iLayer < mTrkParams[0].NLayers; ++iLayer) { + if (track.getClusterIndex(iLayer) == constants::UnusedIndex) { continue; } - - float pvRes{mTrkParams[0].PVres / o2::gpu::CAMath::Sqrt(float(pvs[iV].getNContributors()))}; - const float posVtx[2]{0.f, pvs[iV].getZ()}; - const float covVtx[3]{pvRes, 0.f, pvRes}; - float chi2 = temporaryTrack.getPredictedChi2Quiet(posVtx, covVtx); - if (chi2 < bestChi2) { - if (!temporaryTrack.track::TrackParCov::update(posVtx, covVtx)) { - continue; - } - bestTrack = temporaryTrack; - bestChi2 = chi2; - } + firstLayer = iLayer; + firstCluster = track.getClusterIndex(iLayer); + break; } - - bestTrack.resetCovariance(); - bestTrack.setChi2(0.f); - fitSuccess = fitTrack(bestTrack, 0, mTrkParams[0].NLayers, 1, mTrkParams[0].MaxChi2ClusterAttachment, mTrkParams[0].MaxChi2NDF); - if (!fitSuccess) { - continue; - } - bestTrack.getParamOut() = bestTrack; - bestTrack.resetCovariance(); - bestTrack.setChi2(0.f); - fitSuccess = fitTrack(bestTrack, mTrkParams[0].NLayers - 1, -1, -1, mTrkParams[0].MaxChi2ClusterAttachment, mTrkParams[0].MaxChi2NDF, 50.); - if (!fitSuccess) { - continue; + if (std::binary_search(sharedFirstClusters[firstLayer].begin(), sharedFirstClusters[firstLayer].end(), firstCluster)) { + track.setSharedClusters(); } - mTimeFrame->markUsedCluster(0, bestTrack.getClusterIndex(0)); - mTimeFrame->markUsedCluster(1, bestTrack.getClusterIndex(1)); - mTimeFrame->markUsedCluster(2, bestTrack.getClusterIndex(2)); - mTimeFrame->getTracks(rof).emplace_back(bestTrack); } } -template -bool TrackerTraits::fitTrack(TrackITSExt& track, int start, int end, int step, float chi2clcut, float chi2ndfcut, float maxQoverPt, int nCl, o2::track::TrackPar* linRef) +template +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(); @@ -1106,125 +883,13 @@ bool TrackerTraits::fitTrack(TrackITSExt& track, int start, int end, in return std::abs(track.getQ2Pt()) < maxQoverPt && track.getChi2() < chi2ndfcut * (nCl * 2 - 5); } -template -bool TrackerTraits::trackFollowing(TrackITSExt* track, int rof, bool outward, const int iteration) -{ - auto propInstance = o2::base::Propagator::Instance(); - const int step = -1 + outward * 2; - const int end = outward ? mTrkParams[iteration].NLayers - 1 : 0; - bounded_vector hypotheses(1, *track, mMemoryPool.get()); // possibly avoid reallocation - for (size_t iHypo{0}; iHypo < hypotheses.size(); ++iHypo) { - auto hypo{hypotheses[iHypo]}; - int iLayer = static_cast(outward ? hypo.getLastClusterLayer() : hypo.getFirstClusterLayer()); - // per layer we add new hypotheses - while (iLayer != end) { - iLayer += step; // step through all layers until we reach the end, this allows for skipping on empty layers - const float r = mTrkParams[iteration].LayerRadii[iLayer]; - // get an estimate of the trackinf-frame x for the next step - float x{-999}; - if (!hypo.getXatLabR(r, x, mTimeFrame->getBz(), o2::track::DirAuto) || x <= 0.f) { - continue; - } - // estimate hypo's trk parameters at that x - auto& hypoParam{outward ? hypo.getParamOut() : hypo.getParamIn()}; - if (!propInstance->propagateToX(hypoParam, x, mTimeFrame->getBz(), PropagatorF::MAX_SIN_PHI, - PropagatorF::MAX_STEP, mTrkParams[iteration].CorrType)) { - continue; - } - - if (mTrkParams[iteration].CorrType == PropagatorF::MatCorrType::USEMatCorrNONE) { // account for material affects if propagator does not - if (!hypoParam.correctForMaterial(mTrkParams[iteration].LayerxX0[iLayer], mTrkParams[iteration].LayerxX0[iLayer] * constants::Radl * constants::Rho, true)) { - continue; - } - } - - // calculate the search window on this layer - const float phi{hypoParam.getPhi()}; - const float ePhi{o2::gpu::CAMath::Sqrt(hypoParam.getSigmaSnp2() / hypoParam.getCsp2())}; - const float z{hypoParam.getZ()}; - const float eZ{o2::gpu::CAMath::Sqrt(hypoParam.getSigmaZ2())}; - const int4 selectedBinsRect{getBinsRect(iLayer, phi, mTrkParams[iteration].TrackFollowerNSigmaCutPhi * ePhi, z, mTrkParams[iteration].TrackFollowerNSigmaCutZ * eZ)}; - if (selectedBinsRect.x == 0 && selectedBinsRect.y == 0 && selectedBinsRect.z == 0 && selectedBinsRect.w == 0) { - continue; - } - - int phiBinsNum{selectedBinsRect.w - selectedBinsRect.y + 1}; - - if (phiBinsNum < 0) { - phiBinsNum += mTrkParams[iteration].PhiBins; - } - - gsl::span layer1 = mTimeFrame->getClustersOnLayer(rof, iLayer); - if (layer1.empty()) { - continue; - } - - // check all clusters in search windows for possible new hypotheses - for (int iPhiCount = 0; iPhiCount < phiBinsNum; iPhiCount++) { - int iPhiBin = (selectedBinsRect.y + iPhiCount) % mTrkParams[iteration].PhiBins; - const int firstBinIndex{mTimeFrame->mIndexTableUtils.getBinIndex(selectedBinsRect.x, iPhiBin)}; - const int maxBinIndex{firstBinIndex + selectedBinsRect.z - selectedBinsRect.x + 1}; - const int firstRowClusterIndex = mTimeFrame->getIndexTable(rof, iLayer)[firstBinIndex]; - const int maxRowClusterIndex = mTimeFrame->getIndexTable(rof, iLayer)[maxBinIndex]; - - for (int iNextCluster{firstRowClusterIndex}; iNextCluster < maxRowClusterIndex; ++iNextCluster) { - if (iNextCluster >= (int)layer1.size()) { - break; - } - const Cluster& nextCluster{layer1[iNextCluster]}; - - if (mTimeFrame->isClusterUsed(iLayer, nextCluster.clusterId)) { - continue; - } - - const TrackingFrameInfo& trackingHit = mTimeFrame->getTrackingFrameInfoOnLayer(iLayer)[nextCluster.clusterId]; - - auto tbupdated{hypo}; - auto& tbuParams = outward ? tbupdated.getParamOut() : tbupdated.getParamIn(); - if (!tbuParams.rotate(trackingHit.alphaTrackingFrame)) { - continue; - } - - if (!propInstance->propagateToX(tbuParams, trackingHit.xTrackingFrame, mTimeFrame->getBz(), - PropagatorF::MAX_SIN_PHI, PropagatorF::MAX_STEP, PropagatorF::MatCorrType::USEMatCorrNONE)) { - continue; - } - - auto predChi2{tbuParams.getPredictedChi2Quiet(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)}; - if (predChi2 >= track->getChi2() * mTrkParams[iteration].NSigmaCut) { - continue; - } - - if (!tbuParams.o2::track::TrackParCov::update(trackingHit.positionTrackingFrame, trackingHit.covarianceTrackingFrame)) { - continue; - } - tbupdated.setChi2(tbupdated.getChi2() + predChi2); /// This is wrong for outward propagation as the chi2 refers to inward parameters - tbupdated.setExternalClusterIndex(iLayer, nextCluster.clusterId, true); - hypotheses.emplace_back(tbupdated); - } - } - } - } - - TrackITSExt* bestHypo{track}; - bool swapped{false}; - for (auto& hypo : hypotheses) { - if (hypo.isBetter(*bestHypo, track->getChi2() * mTrkParams[iteration].NSigmaCut)) { - bestHypo = &hypo; - swapped = true; - } - } - *track = *bestHypo; - 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) +template +TrackITSExt TrackerTraits::seedTrackForRefit(const CellSeedN& seed) { TrackITSExt temporaryTrack(seed); - int lrMin = nLayers, lrMax = 0, lrMid = 0; - for (int iL = 0; iL < nLayers; ++iL) { + 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) { @@ -1261,8 +926,8 @@ TrackITSExt TrackerTraits::seedTrackForRefit(const CellSeedN& seed) /// 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, bool reverse) +template +track::TrackParCov TrackerTraits::buildTrackSeed(const Cluster& cluster1, const Cluster& cluster2, const TrackingFrameInfo& tf3, bool reverse) { const float sign = reverse ? -1.f : 1.f; @@ -1297,24 +962,18 @@ track::TrackParCov TrackerTraits::buildTrackSeed(const Cluster& cluster 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 -void TrackerTraits::setBz(float bz) +template +void TrackerTraits::setBz(float bz) { mBz = bz; mIsZeroField = std::abs(mBz) < 0.01; mTimeFrame->setBz(bz); } -template -bool TrackerTraits::isMatLUT() const -{ - return o2::base::Propagator::Instance()->getMatLUT() && (mTrkParams[0].CorrType == o2::base::PropagatorImpl::MatCorrType::USEMatCorrLUT); -} - -template -void TrackerTraits::setNThreads(int n, std::shared_ptr& arena) +template +void TrackerTraits::setNThreads(int n, std::shared_ptr& arena) { -#if defined(OPTIMISATION_OUTPUT) || defined(CA_DEBUG) +#if defined(OPTIMISATION_OUTPUT) mTaskArena = std::make_shared(1); #else if (arena == nullptr) { @@ -1322,7 +981,6 @@ void TrackerTraits::setNThreads(int n, std::shared_ptr LOGP(info, "Setting tracker with {} threads.", n); } else { mTaskArena = arena; - LOGP(info, "Attaching tracker to calling thread's arena"); } #endif } diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx index d5f13cd9d25ea..a41560e2e9e9a 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.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,16 +9,18 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#include #include #include -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "ITSBase/GeometryTGeo.h" -#include "ITSReconstruction/FastMultEstConfig.h" -#include "ITSReconstruction/FastMultEst.h" +#include "ITStracking/FastMultEstConfig.h" +#include "ITStracking/FastMultEst.h" +#include "ITStracking/ROFLookupTables.h" #include "ITStracking/TrackingConfigParam.h" #include "ITStracking/TrackingInterface.h" @@ -28,6 +30,8 @@ #include "CommonDataFormat/IRFrame.h" #include "DetectorsBase/GRPGeomHelper.h" #include "ITStracking/BoundedAllocator.h" +#include "Framework/InputRecordWalker.h" +#include "Framework/DataRefUtils.h" #include "Framework/DeviceSpec.h" using namespace o2::framework; @@ -69,21 +73,67 @@ void ITSTrackingInterface::initialise() } mVertexer->setNThreads(vertConf.nThreads, mTaskArena); mTracker->setNThreads(trackConf.nThreads, mTaskArena); + mTimeFrame->setIsStaggered(mDoStaggering); + + // prepare data filter + for (int iLayer = 0; iLayer < ((mDoStaggering) ? NLayers : 1); ++iLayer) { + mFilter.emplace_back("compClusters", "ITS", "COMPCLUSTERS", iLayer, Lifetime::Timeframe); + mFilter.emplace_back("patterns", "ITS", "PATTERNS", iLayer, Lifetime::Timeframe); + mFilter.emplace_back("ROframe", "ITS", "CLUSTERSROF", iLayer, Lifetime::Timeframe); + if (mIsMC) { + mFilter.emplace_back("itsmclabels", "ITS", "CLUSTERSMCTR", iLayer, Lifetime::Timeframe); + } + } } void ITSTrackingInterface::run(framework::ProcessingContext& pc) { - auto compClusters = pc.inputs().get>("compClusters"); - gsl::span patterns = pc.inputs().get>("patterns"); + const auto& par = o2::itsmft::DPLAlpideParam::Instance(); + + // filter input and compose + std::array, NLayers> compClusters; + std::array, NLayers> patterns; + std::array, NLayers> rofsinput; + std::array*, NLayers> labels{}; + for (const DataRef& ref : framework::InputRecordWalker{pc.inputs(), mFilter}) { + auto const* dh = DataRefUtils::getHeader(ref); + if (framework::DataRefUtils::match(ref, {"compClusters", framework::ConcreteDataTypeMatcher{"ITS", "COMPCLUSTERS"}})) { + compClusters[dh->subSpecification] = pc.inputs().get>(ref); + } + if (framework::DataRefUtils::match(ref, {"patterns", framework::ConcreteDataTypeMatcher{"ITS", "PATTERNS"}})) { + patterns[dh->subSpecification] = pc.inputs().get>(ref); + } + if (framework::DataRefUtils::match(ref, {"ROframes", framework::ConcreteDataTypeMatcher{"ITS", "CLUSTERSROF"}})) { + rofsinput[dh->subSpecification] = pc.inputs().get>(ref); + } + if (framework::DataRefUtils::match(ref, {"itsmclabels", framework::ConcreteDataTypeMatcher{"ITS", "CLUSTERSMCTR"}})) { + labels[dh->subSpecification] = pc.inputs().get*>(ref).release(); + } + } + + bool hasClusters = false; + for (int iLayer = 0; iLayer < ((mDoStaggering) ? NLayers : 1); ++iLayer) { + LOGP(info, "ITSTracker{} pulled {} clusters, {} RO frames", ((mDoStaggering) ? std::format(" on layer {}", iLayer) : ""), compClusters[iLayer].size(), rofsinput[iLayer].size()); + if (compClusters[iLayer].empty()) { + LOGP(warn, " -> received no processable data{}", (mDoStaggering) ? std::format(" on layer {}", iLayer) : ""); + } else { + hasClusters = true; + } + if (mIsMC) { + LOG(info) << " -> " << labels[iLayer]->getIndexedSize() << " MC label objects"; + } + } + + const auto& tfInfo = pc.services().get(); gsl::span physTriggers; std::vector fromTRD; if (mUseTriggers == 2) { // use TRD triggers - o2::InteractionRecord ir{0, pc.services().get().firstTForbit}; + o2::InteractionRecord ir{0, tfInfo.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}); + fromTRD.emplace_back(o2::itsmft::PhysTrigger{.ir = ir, .data = 0}); } } physTriggers = gsl::span(fromTRD.data(), fromTRD.size()); @@ -91,43 +141,23 @@ void ITSTrackingInterface::run(framework::ProcessingContext& pc) physTriggers = pc.inputs().get>("phystrig"); } - auto rofsinput = pc.inputs().get>("ROframes"); - auto& trackROFvec = pc.outputs().make>(Output{"ITS", "ITSTrackROF", 0}, rofsinput.begin(), rofsinput.end()); + const int clockLayerId{mDoStaggering ? mTimeFrame->getROFOverlapTableView().getClock() : 0}; auto& irFrames = pc.outputs().make>(Output{"ITS", "IRFRAMES", 0}); - const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); // RS: this should come from CCDB - - irFrames.reserve(trackROFvec.size()); - int nBCPerTF = alpParams.roFrameLengthInBC; - - LOGP(info, "ITSTracker pulled {} clusters, {} RO frames {}", compClusters.size(), trackROFvec.size(), compClusters.empty() ? " -> received no processable data will skip" : ""); - const dataformats::MCTruthContainer* labels = nullptr; - gsl::span mc2rofs; - if (mIsMC) { - labels = pc.inputs().get*>("itsmclabels").release(); - // get the array as read-only span, a snapshot is sent forward - pc.outputs().snapshot(Output{"ITS", "ITSTrackMC2ROF", 0}, pc.inputs().get>("ITSMC2ROframes")); - LOG(info) << labels->getIndexedSize() << " MC label objects , in " << mc2rofs.size() << " MC events"; - } + irFrames.reserve(rofsinput[clockLayerId].size()); auto& allClusIdx = pc.outputs().make>(Output{"ITS", "TRACKCLSID", 0}); auto& allTracks = pc.outputs().make>(Output{"ITS", "TRACKS", 0}); - auto& vertROFvec = pc.outputs().make>(Output{"ITS", "VERTICESROF", 0}); + auto& allTrackROFs = pc.outputs().make>(Output{"ITS", "ITSTrackROF", 0}); auto& vertices = pc.outputs().make>(Output{"ITS", "VERTICES", 0}); + auto& vertROFvec = pc.outputs().make>(Output{"ITS", "VERTICESROF", 0}); // TODO fill this! // MC static pmr::vector dummyMCLabTracks, dummyMCLabVerts; static pmr::vector dummyMCPurVerts; auto& allTrackLabels = mIsMC ? pc.outputs().make>(Output{"ITS", "TRACKSMCTR", 0}) : dummyMCLabTracks; auto& allVerticesLabels = mIsMC ? pc.outputs().make>(Output{"ITS", "VERTICESMCTR", 0}) : dummyMCLabVerts; - bool writeContLabels = mIsMC && o2::its::VertexerParamConfig::Instance().outputContLabels; - auto& allVerticesContLabels = writeContLabels ? pc.outputs().make>(Output{"ITS", "VERTICESMCTRCONT", 0}) : dummyMCLabVerts; auto& allVerticesPurities = mIsMC ? pc.outputs().make>(Output{"ITS", "VERTICESMCPUR", 0}) : dummyMCPurVerts; - std::uint32_t roFrame = 0; - - bool continuous = o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(o2::detectors::DetID::ITS); - LOG(info) << "ITSTracker RO: continuous=" << continuous; - if (mOverrideBeamEstimation) { mTimeFrame->setBeamPosition(mMeanVertex->getX(), mMeanVertex->getY(), @@ -137,52 +167,57 @@ void ITSTrackingInterface::run(framework::ProcessingContext& pc) } mTracker->setBz(o2::base::Propagator::Instance()->getNominalBz()); + mTracker->setTimeSlice(tfInfo.timeslice); - gsl::span::iterator pattIt = patterns.begin(); - gsl::span trackROFspan(trackROFvec); - loadROF(trackROFspan, compClusters, pattIt, labels); - pattIt = patterns.begin(); + for (int iLayer = 0; iLayer < ((mDoStaggering) ? NLayers : 1); ++iLayer) { + gsl::span::iterator pattIt = patterns[iLayer].begin(); + loadROF(rofsinput[iLayer], compClusters[iLayer], pattIt, ((mDoStaggering) ? iLayer : -1), labels[iLayer]); + } auto logger = [&](const std::string& s) { LOG(info) << s; }; auto fatalLogger = [&](const std::string& s) { LOG(fatal) << s; }; auto errorLogger = [&](const std::string& s) { LOG(error) << s; }; FastMultEst multEst; // mult estimator - std::vector processingMask, processUPCMask; - int cutVertexMult{0}, cutUPCVertex{0}, cutRandomMult = int(trackROFvec.size()) - multEst.selectROFs(trackROFvec, compClusters, physTriggers, processingMask); - processUPCMask.resize(processingMask.size(), false); - mTimeFrame->setMultiplicityCutMask(processingMask); + o2::its::ROFMaskTable processMultiplictyMask{mTimeFrame->getROFOverlapTable()}, processUPCMask{mTimeFrame->getROFOverlapTable()}; + multEst.selectROFs(rofsinput, compClusters, physTriggers, tfInfo.firstTForbit, mDoStaggering, mTimeFrame->getROFOverlapTableView(), processMultiplictyMask); + mTimeFrame->setMultiplicityCutMask(processMultiplictyMask); + for (int iLayer = 0; iLayer < ((mDoStaggering) ? NLayers : 1); ++iLayer) { + mTimeFrame->getROFMaskView().print(iLayer); + } + float vertexerElapsedTime{0.f}; if (mRunVertexer) { - vertROFvec.reserve(trackROFvec.size()); // Run seeding vertexer - if (!compClusters.empty()) { - vertexerElapsedTime = mVertexer->clustersToVertices(logger); + vertexerElapsedTime = mVertexer->clustersToVertices(logger); + // FIXME: this is a temporary stop-gap measure until we figure the rest out + const auto& vtx = mTimeFrame->getPrimaryVertices(); + vertices.insert(vertices.begin(), vtx.begin(), vtx.end()); + if (mIsMC) { + allVerticesLabels.reserve(vertices.size()); + allVerticesPurities.reserve(vertices.size()); + for (const auto& lbl : mTimeFrame->getPrimaryVerticesLabels()) { + allVerticesLabels.push_back(lbl.first); + allVerticesPurities.push_back(lbl.second); + } } - } else { // cosmics - mTimeFrame->resetRofPV(); } - const auto& multEstConf = FastMultEstConfig::Instance(); // parameters for mult estimation and cuts - gsl::span> vMCRecInfo; - gsl::span vMCContLabels; - for (auto iRof{0}; iRof < trackROFspan.size(); ++iRof) { + multEst.selectROFsWithVertices(vertices, mTimeFrame->getROFOverlapTableView(), processMultiplictyMask); + + auto clockROFspan = rofsinput[clockLayerId]; + auto clockTiming = mTimeFrame->getROFOverlapTableView().getClockLayer(); + for (auto iRof{0}; iRof < clockROFspan.size(); ++iRof) { bounded_vector vtxVecLoc; - auto& vtxROF = vertROFvec.emplace_back(trackROFspan[iRof]); - vtxROF.setFirstEntry(vertices.size()); + auto& vtxROF = vertROFvec.emplace_back(clockROFspan[iRof]); + vtxROF.setFirstEntry((int)vertices.size()); + if (mRunVertexer) { - auto vtxSpan = mTimeFrame->getPrimaryVertices(iRof); - if (mIsMC) { - vMCRecInfo = mTimeFrame->getPrimaryVerticesMCRecInfo(iRof); - if (o2::its::VertexerParamConfig::Instance().outputContLabels) { - vMCContLabels = mTimeFrame->getPrimaryVerticesContributors(iRof); - } - } + auto vtxSpan = mTimeFrame->getPrimaryVertices(clockLayerId, iRof); if (o2::its::TrackerParamConfig::Instance().doUPCIteration) { if (!vtxSpan.empty()) { if (vtxSpan[0].isFlagSet(Vertex::UPCMode) == 1) { // at least one vertex in this ROF and it is from second vertex iteration LOGP(debug, "ROF {} rejected as vertices are from the UPC iteration", iRof); - processUPCMask[iRof] = true; - cutUPCVertex++; + processUPCMask.selectROF({clockTiming.getROFStartInBC(iRof), clockTiming.getROFEndInBC(iRof)}); vtxROF.setFlag(o2::itsmft::ROFRecord::VtxUPCMode); } else { // in all cases except if as standard mode vertex was found, the ROF was processed with UPC settings vtxROF.setFlag(o2::itsmft::ROFRecord::VtxStdMode); @@ -193,125 +228,129 @@ void ITSTrackingInterface::run(framework::ProcessingContext& pc) } else { vtxROF.setFlag(o2::itsmft::ROFRecord::VtxStdMode); } - vtxROF.setNEntries(vtxSpan.size()); - bool selROF = vtxSpan.empty(); - for (int iV{0}, iVC{0}; iV < vtxSpan.size(); ++iV) { - const auto& v = vtxSpan[iV]; - if (multEstConf.isVtxMultCutRequested() && !multEstConf.isPassingVtxMultCut(v.getNContributors())) { - iVC += v.getNContributors(); - continue; // skip vertex of unwanted multiplicity - } - selROF = true; - vertices.push_back(v); - if (mIsMC && !VertexerParamConfig::Instance().useTruthSeeding) { - allVerticesLabels.push_back(vMCRecInfo[iV].first); - allVerticesPurities.push_back(vMCRecInfo[iV].second); - if (o2::its::VertexerParamConfig::Instance().outputContLabels) { - allVerticesContLabels.insert(allVerticesContLabels.end(), vMCContLabels.begin() + iVC, vMCContLabels.begin() + iVC + v.getNContributors()); - } - } - iVC += v.getNContributors(); - } - if (processingMask[iRof] && !selROF) { // passed selection in clusters and not in vertex multiplicity - LOGP(info, "ROF {} rejected by the vertex multiplicity selection [{},{}]", iRof, multEstConf.cutMultVtxLow, multEstConf.cutMultVtxHigh); - processingMask[iRof] = selROF; - cutVertexMult++; - } - } else { // cosmics - vtxVecLoc.emplace_back(); - vtxVecLoc.back().setNContributors(1); - vtxROF.setNEntries(vtxVecLoc.size()); - for (auto& v : vtxVecLoc) { - vertices.push_back(v); - } - mTimeFrame->addPrimaryVertices(vtxVecLoc, 0); + vtxROF.setNEntries((int)vtxSpan.size()); } } - if (mRunVertexer && !compClusters.empty()) { - LOG(info) << fmt::format(" - Vertex seeding total elapsed time: {} ms for {} ({} + {}) vertices found in {}/{} ROFs", + + if (mRunVertexer && hasClusters) { + LOG(info) << fmt::format(" - Vertex seeding total elapsed time: {} ms for {} vertices found", vertexerElapsedTime, - mTimeFrame->getPrimaryVerticesNum(), - mTimeFrame->getTotVertIteration()[0], - o2::its::VertexerParamConfig::Instance().nIterations > 1 ? mTimeFrame->getTotVertIteration()[1] : 0, - trackROFspan.size() - mTimeFrame->getNoVertexROF(), - trackROFspan.size()); - LOG(info) << fmt::format(" - FastMultEst: rejected {}/{} ROFs: random/mult.sel:{} (seed {}), vtx.sel:{}", cutRandomMult + cutVertexMult, trackROFspan.size(), cutRandomMult, multEst.lastRandomSeed, cutVertexMult); + mTimeFrame->getPrimaryVerticesNum()); } + if (mOverrideBeamEstimation) { LOG(info) << fmt::format(" - Beam position set to: {}, {} from meanvertex object", mTimeFrame->getBeamX(), mTimeFrame->getBeamY()); } else { LOG(info) << fmt::format(" - Beam position computed for the TF: {}, {}", mTimeFrame->getBeamX(), mTimeFrame->getBeamY()); } - if (mCosmicsProcessing && compClusters.size() > 1500 * trackROFspan.size()) { - LOG(error) << "Cosmics processing was requested with an average detector occupancy exceeding 1.e-7, skipping TF processing."; - } else { - if (!compClusters.empty()) { - mTimeFrame->setMultiplicityCutMask(processingMask); - mTimeFrame->setROFMask(processUPCMask); - // Run CA tracker - if (mMode == o2::its::TrackingMode::Async && o2::its::TrackerParamConfig::Instance().fataliseUponFailure) { - mTracker->clustersToTracks(logger, fatalLogger); - } else { - mTracker->clustersToTracks(logger, errorLogger); - } + + if (hasClusters) { + mTimeFrame->setMultiplicityCutMask(processMultiplictyMask); + mTimeFrame->setUPCCutMask(processUPCMask); + // Run CA tracker + if (mMode == o2::its::TrackingMode::Async && o2::its::TrackerParamConfig::Instance().fataliseUponFailure) { + mTracker->clustersToTracks(logger, fatalLogger); + } else { + mTracker->clustersToTracks(logger, errorLogger); } - size_t totTracks{mTimeFrame->getNumberOfTracks()}, totClusIDs{mTimeFrame->getNumberOfUsedClusters()}; - if (totTracks) { - allTracks.reserve(totTracks); - allClusIdx.reserve(totClusIDs); + } - if (mTimeFrame->hasBogusClusters()) { - LOG(warning) << fmt::format(" - The processed timeframe had {} clusters with wild z coordinates, check the dictionaries", mTimeFrame->hasBogusClusters()); - } + size_t totTracks{mTimeFrame->getNumberOfTracks()}, totClusIDs{mTimeFrame->getNumberOfUsedClusters()}; + if (totTracks) { + allTracks.reserve(totTracks); + allClusIdx.reserve(totClusIDs); - for (unsigned int iROF{0}; iROF < trackROFvec.size(); ++iROF) { - auto& tracksROF{trackROFvec[iROF]}; - auto& vtxROF = vertROFvec[iROF]; - auto& tracks = mTimeFrame->getTracks(iROF); - auto number{tracks.size()}; - auto first{allTracks.size()}; - int offset = -tracksROF.getFirstEntry(); // cluster entry!!! - tracksROF.setFirstEntry(first); - tracksROF.setNEntries(number); - tracksROF.setFlags(vtxROF.getFlags()); // copies 0xffffffff if cosmics - if (processingMask[iROF]) { - irFrames.emplace_back(tracksROF.getBCData(), tracksROF.getBCData() + nBCPerTF - 1).info = tracks.size(); + if (mTimeFrame->hasBogusClusters()) { + LOG(warning) << fmt::format(" - The processed timeframe had {} clusters with wild z coordinates, check the dictionaries", mTimeFrame->hasBogusClusters()); + } + + auto& tracks = mTimeFrame->getTracks(); + allTrackLabels.reserve(mTimeFrame->getTracksLabel().size()); // should be 0 if not MC + std::copy(mTimeFrame->getTracksLabel().begin(), mTimeFrame->getTracksLabel().end(), std::back_inserter(allTrackLabels)); + { + // create the track to clock ROF association here + // the clock ROF is just the fastest ROF + // the number of ROFs does not necessarily reflect the actual ROFs + // due to possible delay of other layers, however it is guaranteed to be >=0 + // tracks are guaranteed to be sorted here by their lower edge + const auto& clock = mTimeFrame->getROFOverlapTableView().getClock(); + const auto& clockLayer = mTimeFrame->getROFOverlapTableView().getClockLayer(); + auto setBCData = [&](auto& rofs) { + for (size_t iROF{0}; iROF < rofs.size(); ++iROF) { // set BC data + auto& rof = rofs[iROF]; + int orb = (iROF * par.getROFLengthInBC(clock) / o2::constants::lhc::LHCMaxBunches) + tfInfo.firstTForbit; + int bc = (iROF * par.getROFLengthInBC(clock) % o2::constants::lhc::LHCMaxBunches) + par.getROFDelayInBC(clock); + o2::InteractionRecord ir(bc, orb); + rof.setBCData(ir); + rof.setROFrame(iROF); + rof.setNEntries(0); + rof.setFirstEntry(-1); } - allTrackLabels.reserve(mTimeFrame->getTracksLabel(iROF).size()); // should be 0 if not MC - std::copy(mTimeFrame->getTracksLabel(iROF).begin(), mTimeFrame->getTracksLabel(iROF).end(), std::back_inserter(allTrackLabels)); - // Some conversions that needs to be moved in the tracker internals - for (unsigned int iTrk{0}; iTrk < tracks.size(); ++iTrk) { - auto& trc{tracks[iTrk]}; - trc.setFirstClusterEntry(allClusIdx.size()); // before adding tracks, create final cluster indices - int ncl = trc.getNumberOfClusters(), nclf = 0; - for (int ic = TrackITSExt::MaxClusters; ic--;) { // track internally keeps in->out cluster indices, but we want to store the references as out->in!!! - auto clid = trc.getClusterIndex(ic); - if (clid >= 0) { - trc.setClusterSize(ic, mTimeFrame->getClusterSize(clid)); - allClusIdx.push_back(clid); - nclf++; - } + }; + // we pick whatever is the largest possible number of rofs since there might be tracks/vertices which are beyond + // the clock layer + int highestROF{0}; + for (const auto& trc : tracks) { + highestROF = std::max(highestROF, (int)clockLayer.getROF(trc.getTimeStamp())); + } + for (const auto& vtx : vertices) { + highestROF = std::max(highestROF, (int)clockLayer.getROF(vtx.getTimeStamp().lower())); + } + highestROF = std::max(highestROF, (int)clockLayer.mNROFsTF); + allTrackROFs.resize(highestROF); + vertROFvec.resize(highestROF); + setBCData(allTrackROFs); + setBCData(vertROFvec); + + mTimeFrame->useMultiplictyMask(); // use multiplicty selection for IR frames + + std::vector rofEntries(highestROF + 1, 0); + for (unsigned int iTrk{0}; iTrk < tracks.size(); ++iTrk) { + auto& trc{tracks[iTrk]}; + trc.setFirstClusterEntry((int)allClusIdx.size()); // before adding tracks, create final cluster indices + int ncl = trc.getNumberOfClusters(), nclf = 0; + for (int ic = TrackITSExt::MaxClusters; ic--;) { // track internally keeps in->out cluster indices, but we want to store the references as out->in!!! + auto clid = trc.getClusterIndex(ic); + if (clid >= 0) { + trc.setClusterSize(ic, mTimeFrame->getClusterSize((mDoStaggering) ? ic : 0, clid)); + allClusIdx.push_back(clid); + nclf++; } - assert(ncl == nclf); - allTracks.emplace_back(trc); } + assert(ncl == nclf); + allTracks.emplace_back(trc); + auto rof = clockLayer.getROF(trc.getTimeStamp()); + ++rofEntries[rof]; } - } else { - for (auto& r : trackROFvec) { // reset data copied from the clusters - r.setFirstEntry(0); - r.setNEntries(0); + std::exclusive_scan(rofEntries.begin(), rofEntries.end(), rofEntries.begin(), 0); + for (size_t iROF{0}; iROF < allTrackROFs.size(); ++iROF) { + allTrackROFs[iROF].setFirstEntry(rofEntries[iROF]); + allTrackROFs[iROF].setNEntries(rofEntries[iROF + 1] - rofEntries[iROF]); + if (mTimeFrame->getROFMaskView().isROFEnabled(clockLayerId, (int)iROF)) { + auto& irFrame = irFrames.emplace_back(allTrackROFs[iROF].getBCData(), allTrackROFs[iROF].getBCData() + clockLayer.mROFLength - 1); + irFrame.info = allTrackROFs[iROF].getNEntries(); + } } - } - LOGP(info, "ITSTracker pushed {} tracks and {} vertices", allTracks.size(), vertices.size()); - if (mIsMC) { - LOGP(info, "ITSTracker pushed {} track labels", allTrackLabels.size()); - LOGP(info, "ITSTracker pushed {} vertex labels", allVerticesLabels.size()); - if (!allVerticesContLabels.empty()) { - LOGP(info, "ITSTracker pushed {} vertex contributor labels", allVerticesContLabels.size()); + // same thing for vertices rofs + std::fill(rofEntries.begin(), rofEntries.end(), 0); + for (const auto& vtx : vertices) { + auto rof = clockLayer.getROF(vtx.getTimeStamp().lower()); + ++rofEntries[rof]; + } + std::exclusive_scan(rofEntries.begin(), rofEntries.end(), rofEntries.begin(), 0); + for (size_t iROF{0}; iROF < vertROFvec.size(); ++iROF) { + vertROFvec[iROF].setFirstEntry(rofEntries[iROF]); + vertROFvec[iROF].setNEntries(rofEntries[iROF + 1] - rofEntries[iROF]); } - LOGP(info, "ITSTracker pushed {} vertex purities", allVerticesPurities.size()); } } + + LOGP(info, "ITSTracker pushed {} tracks in {} rofs and {} vertices {}", allTracks.size(), allTrackROFs.size(), vertices.size(), ((mDoStaggering) ? "in staggered-readout mode" : "in normal mode")); + if (mIsMC) { + LOGP(info, "ITSTracker pushed {} track labels", allTrackLabels.size()); + LOGP(info, "ITSTracker pushed {} vertex labels", allVerticesLabels.size()); + LOGP(info, "ITSTracker pushed {} vertex purities", allVerticesPurities.size()); + } mTimeFrame->wipe(); } @@ -334,19 +373,50 @@ void ITSTrackingInterface::updateTimeDependentParams(framework::ProcessingContex initialise(); if (pc.services().get().inputTimesliceId == 0) { // print settings only for the 1st pipeling - o2::its::VertexerParamConfig::Instance().printKeyValues(); - o2::its::TrackerParamConfig::Instance().printKeyValues(); + // print all used settings + if (o2::its::FastMultEstConfig::Instance().isRequested()) { + o2::its::FastMultEstConfig::Instance().printKeyValues(true, true); + } const auto& vtxParams = mVertexer->getParameters(); + if (!vtxParams.empty()) { + o2::its::VertexerParamConfig::Instance().printKeyValues(true, true); + } + const auto& trParams = mTracker->getParameters(); + if (!trParams.empty()) { + o2::its::TrackerParamConfig::Instance().printKeyValues(true, true); + } + // quick summary for (size_t it = 0; it < vtxParams.size(); it++) { const auto& par = vtxParams[it]; LOGP(info, "vtxIter#{} : {}", it, par.asString()); } - const auto& trParams = mTracker->getParameters(); for (size_t it = 0; it < trParams.size(); it++) { const auto& par = trParams[it]; LOGP(info, "recoIter#{} : {}", it, par.asString()); } } + + // prepare rof lookup table(s) + const auto& par = o2::itsmft::DPLAlpideParam::Instance(); + const int nOrbitsPerTF = o2::base::GRPGeomHelper::getNHBFPerTF(); + TimeFrameN::ROFOverlapTableN rofTable; + TimeFrameN::ROFVertexLookupTableN vtxTable; + const auto& trackParams = mTracker->getParameters(); + for (int iLayer = 0; iLayer < NLayers; ++iLayer) { + const unsigned int nROFsPerOrbit = o2::constants::lhc::LHCMaxBunches / par.getROFLengthInBC(iLayer); + const LayerTiming timing{ + .mNROFsTF = (nROFsPerOrbit * nOrbitsPerTF), + .mROFLength = (uint32_t)par.getROFLengthInBC(iLayer), + .mROFDelay = (uint32_t)par.getROFDelayInBC(iLayer), + .mROFBias = (uint32_t)par.getROFBiasInBC(iLayer), + .mROFAddTimeErr = (trackParams.empty() ? o2::its::TrackerParamConfig::Instance().addTimeError[iLayer] : trackParams[0].AddTimeError[iLayer])}; + rofTable.defineLayer(iLayer, timing); + vtxTable.defineLayer(iLayer, timing); + } + rofTable.init(); + mTimeFrame->setROFOverlapTable(rofTable); + vtxTable.init(); + mTimeFrame->setROFVertexLookupTable(vtxTable); } } @@ -408,7 +478,8 @@ void ITSTrackingInterface::setTraitsFromProvider(VertexerTraitsN* vertexerTraits void ITSTrackingInterface::loadROF(gsl::span& trackROFspan, gsl::span clusters, gsl::span::iterator& pattIt, + int layer, const dataformats::MCTruthContainer* mcLabels) { - mTimeFrame->loadROFrameData(trackROFspan, clusters, pattIt, mDict, mcLabels); + mTimeFrame->loadROFrameData(trackROFspan, clusters, pattIt, mDict, layer, mcLabels); } diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h b/Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h index 2fe70e96248f9..9efd6dde0176d 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h @@ -24,6 +24,9 @@ #pragma link C++ class o2::its::TrackingFrameInfo + ; #pragma link C++ class std::vector < o2::its::TrackingFrameInfo> + ; +#pragma link C++ class o2::its::TrackingFrameInfo + ; +#pragma link C++ class std::vector < o2::its::TrackingFrameInfo> + ; + #pragma link C++ class o2::its::Line + ; #pragma link C++ class std::vector < o2::its::Line> + ; @@ -39,4 +42,8 @@ #pragma link C++ class o2::its::ITSGpuTrackingParamConfig + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::ITSGpuTrackingParamConfig> + ; +#pragma link C++ class o2::its::FastMultEst + ; +#pragma link C++ class o2::its::FastMultEstConfig + ; +#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::FastMultEstConfig> + ; + #endif diff --git a/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx b/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx index c4b1fb427513f..222b4801a5767 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx @@ -26,8 +26,8 @@ namespace o2::its { -template -Vertexer::Vertexer(VertexerTraitsN* traits) : mTraits(traits) +template +Vertexer::Vertexer(VertexerTraitsN* traits) : mTraits(traits) { if (!mTraits) { LOG(fatal) << "nullptr passed to ITS vertexer construction."; @@ -35,18 +35,19 @@ Vertexer::Vertexer(VertexerTraitsN* traits) : mTraits(traits) mVertParams.resize(1); } -template -float Vertexer::clustersToVertices(LogFunc logger) +template +float Vertexer::clustersToVertices(LogFunc logger) { LogFunc evalLog = [](const std::string&) {}; if (mTimeFrame->hasMCinformation() && mVertParams[0].useTruthSeeding) { - return evaluateTask(&Vertexer::addTruthSeeds, StateNames[mCurState = TruthSeeding], 0, evalLog); + float t = evaluateTask(&Vertexer::addTruthSeeds, StateNames[mCurState = TruthSeeding], 0, evalLog); + sortVertices(); + return t; } TrackingParameters trkPars; - TimeFrameGPUParameters tfGPUpar; - mTraits->updateVertexingParameters(mVertParams, tfGPUpar); + mTraits->updateVertexingParameters(mVertParams); auto handleException = [&](const auto& err) { LOGP(error, "Encountered critical error in step {}, stopping further processing of this TF: {}", StateNames[mCurState], err.what()); @@ -71,7 +72,7 @@ float Vertexer::clustersToVertices(LogFunc logger) nTracklets12 = mTimeFrame->getTotalTrackletsTF(1); auto timeSelectionIteration = evaluateTask(&Vertexer::validateTracklets, StateNames[mCurState = Validating], iteration, evalLog, iteration); auto timeVertexingIteration = evaluateTask(&Vertexer::findVertices, StateNames[mCurState = Finding], iteration, evalLog, iteration); - printEpilog(logger, nTracklets01, nTracklets12, mTimeFrame->getNLinesTotal(), mTimeFrame->getTotVertIteration()[iteration], timeInitIteration, timeTrackletIteration, timeSelectionIteration, timeVertexingIteration); + printEpilog(logger, nTracklets01, nTracklets12, mTimeFrame->getNLinesTotal(), mTimeFrame->getPrimaryVertices().size(), timeInitIteration, timeTrackletIteration, timeSelectionIteration, timeVertexingIteration); timeInit += timeInitIteration; timeTracklet += timeTrackletIteration; timeSelection += timeSelectionIteration; @@ -85,18 +86,53 @@ float Vertexer::clustersToVertices(LogFunc logger) LOGP(fatal, "Uncaught exception!"); } + sortVertices(); + return timeInit + timeTracklet + timeSelection + timeVertexing; } -template -void Vertexer::adoptTimeFrame(TimeFrameN& tf) +template +void Vertexer::sortVertices() +{ + auto& pvs = mTimeFrame->getPrimaryVertices(); + bounded_vector indices(pvs.size(), mMemoryPool.get()); + std::iota(indices.begin(), indices.end(), 0); + // provide vertices sorted by lower-bound + std::sort(indices.begin(), indices.end(), [&pvs](size_t i, size_t j) { + const auto& a = pvs[i].getTimeStamp(); + const auto& b = pvs[j].getTimeStamp(); + const auto aLower = a.lower(); + const auto bLower = b.lower(); + if (aLower != bLower) { + return aLower < bLower; + } + return pvs[i].getNContributors() > pvs[j].getNContributors(); + }); + bounded_vector sortedVtx(mMemoryPool.get()); + sortedVtx.reserve(pvs.size()); + for (const size_t idx : indices) { + sortedVtx.push_back(pvs[idx]); + } + pvs.swap(sortedVtx); + if (mTimeFrame->hasMCinformation()) { + auto& mc = mTimeFrame->getPrimaryVerticesLabels(); + bounded_vector sortedMC(mMemoryPool.get()); + for (const size_t idx : indices) { + sortedMC.push_back(mc[idx]); + } + mc.swap(sortedMC); + } +} + +template +void Vertexer::adoptTimeFrame(TimeFrameN& tf) { mTimeFrame = &tf; mTraits->adoptTimeFrame(&tf); } -template -void Vertexer::printEpilog(LogFunc& logger, +template +void Vertexer::printEpilog(LogFunc& logger, const unsigned int trackletN01, const unsigned int trackletN12, const unsigned selectedN, const unsigned int vertexN, const float initT, const float trackletT, const float selecT, const float vertexT) diff --git a/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx index 6d51f7bab5d36..5e27e20b3ddee 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx @@ -10,10 +10,9 @@ // or submit itself to any jurisdiction. /// +#include #include #include -#include -#include #include #include @@ -22,17 +21,18 @@ #include "ITStracking/VertexerTraits.h" #include "ITStracking/BoundedAllocator.h" #include "ITStracking/ClusterLines.h" +#include "ITStracking/Definitions.h" #include "ITStracking/Tracklet.h" #include "SimulationDataFormat/DigitizationContext.h" +#include "SimulationDataFormat/O2DatabasePDG.h" #include "Steer/MCKinematicsReader.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DetectorsRaw/HBFUtils.h" -#include "CommonUtils/TreeStreamRedirector.h" namespace o2::its { -template +template static void trackleterKernelHost( const gsl::span& clustersNextLayer, // 0 2 const gsl::span& clustersCurrentLayer, // 1 1 @@ -41,10 +41,11 @@ static void trackleterKernelHost( const float phiCut, bounded_vector& tracklets, gsl::span foundTracklets, - const IndexTableUtils& utils, - const short pivotRof, - const short targetRof, - gsl::span rofFoundTrackletsOffsets, // we want to change those, to keep track of the offset in deltaRof>0 + const IndexTableUtils& utils, + const TimeEstBC& timErr, + gsl::span rofFoundTrackletsOffsets, + const int globalOffsetNextLayer = 0, + const int globalOffsetCurrentLayer = 0, const int maxTrackletsPerCluster = static_cast(2e3)) { const int PhiBins{utils.getNphiBins()}; @@ -53,7 +54,7 @@ static void trackleterKernelHost( for (int iCurrentLayerClusterIndex = 0; iCurrentLayerClusterIndex < clustersCurrentLayer.size(); ++iCurrentLayerClusterIndex) { int storedTracklets{0}; const Cluster& currentCluster{clustersCurrentLayer[iCurrentLayerClusterIndex]}; - const int4 selectedBinsRect{VertexerTraits::getBinsRect(currentCluster, (int)Mode, 0.f, 50.f, phiCut / 2, utils)}; + const int4 selectedBinsRect{VertexerTraits::getBinsRect(currentCluster, (int)Mode, 0.f, 50.f, phiCut / 2, utils)}; if (selectedBinsRect.x != 0 || selectedBinsRect.y != 0 || selectedBinsRect.z != 0 || selectedBinsRect.w != 0) { int phiBinsNum{selectedBinsRect.w - selectedBinsRect.y + 1}; if (phiBinsNum < 0) { @@ -74,9 +75,9 @@ static void trackleterKernelHost( if (storedTracklets < maxTrackletsPerCluster) { if constexpr (!EvalRun) { if constexpr (Mode == TrackletMode::Layer0Layer1) { - tracklets[rofFoundTrackletsOffsets[iCurrentLayerClusterIndex] + storedTracklets] = Tracklet{iNextLayerClusterIndex, iCurrentLayerClusterIndex, nextCluster, currentCluster, targetRof, pivotRof}; + tracklets[rofFoundTrackletsOffsets[iCurrentLayerClusterIndex] + storedTracklets] = Tracklet{globalOffsetNextLayer + iNextLayerClusterIndex, globalOffsetCurrentLayer + iCurrentLayerClusterIndex, nextCluster, currentCluster, timErr}; } else { - tracklets[rofFoundTrackletsOffsets[iCurrentLayerClusterIndex] + storedTracklets] = Tracklet{iCurrentLayerClusterIndex, iNextLayerClusterIndex, currentCluster, nextCluster, pivotRof, targetRof}; + tracklets[rofFoundTrackletsOffsets[iCurrentLayerClusterIndex] + storedTracklets] = Tracklet{globalOffsetCurrentLayer + iCurrentLayerClusterIndex, globalOffsetNextLayer + iNextLayerClusterIndex, currentCluster, nextCluster, timErr}; } } ++storedTracklets; @@ -94,10 +95,10 @@ static void trackleterKernelHost( } static void trackletSelectionKernelHost( - const gsl::span clusters0, // 0 - const gsl::span clusters1, // 1 - gsl::span usedClusters0, // Layer 0 - gsl::span usedClusters2, // Layer 2 + const Cluster* clusters0, // global layer 0 clusters + const Cluster* clusters1, // global layer 1 clusters + gsl::span usedClusters0, // global layer 0 used clusters + gsl::span usedClusters2, // global layer 2 used clusters const gsl::span& tracklets01, const gsl::span& tracklets12, bounded_vector& usedTracklets, @@ -106,15 +107,13 @@ static void trackletSelectionKernelHost( bounded_vector& lines, const gsl::span& trackletLabels, bounded_vector& linesLabels, - const short targetRofId0, - const short targetRofId2, - bool safeWrites = false, + const int nLayer1Clusters, const float tanLambdaCut = 0.025f, const float phiCut = 0.005f, - const int maxTracklets = static_cast(1e2)) + const int maxTracklets = 100) { int offset01{0}, offset12{0}; - for (unsigned int iCurrentLayerClusterIndex{0}; iCurrentLayerClusterIndex < clusters1.size(); ++iCurrentLayerClusterIndex) { + for (int iCurrentLayerClusterIndex{0}; iCurrentLayerClusterIndex < nLayer1Clusters; ++iCurrentLayerClusterIndex) { int validTracklets{0}; for (int iTracklet12{offset12}; iTracklet12 < offset12 + foundTracklets12[iCurrentLayerClusterIndex]; ++iTracklet12) { for (int iTracklet01{offset01}; iTracklet01 < offset01 + foundTracklets01[iCurrentLayerClusterIndex]; ++iTracklet01) { @@ -124,23 +123,17 @@ static void trackletSelectionKernelHost( const auto& tracklet01{tracklets01[iTracklet01]}; const auto& tracklet12{tracklets12[iTracklet12]}; - - if (tracklet01.rof[0] != targetRofId0 || tracklet12.rof[1] != targetRofId2) { + if (!tracklet01.getTimeStamp().isCompatible(tracklet12.getTimeStamp())) { continue; } const float deltaTanLambda{o2::gpu::GPUCommonMath::Abs(tracklet01.tanLambda - tracklet12.tanLambda)}; const float deltaPhi{o2::gpu::GPUCommonMath::Abs(math_utils::smallestAngleDifference(tracklet01.phi, tracklet12.phi))}; if (deltaTanLambda < tanLambdaCut && deltaPhi < phiCut && validTracklets != maxTracklets) { - if (safeWrites) { - __atomic_store_n(&usedClusters0[tracklet01.firstClusterIndex], 1, __ATOMIC_RELAXED); - __atomic_store_n(&usedClusters2[tracklet12.secondClusterIndex], 1, __ATOMIC_RELAXED); - } else { - usedClusters0[tracklet01.firstClusterIndex] = 1; - usedClusters2[tracklet12.secondClusterIndex] = 1; - } + usedClusters0[tracklet01.firstClusterIndex] = 1; + usedClusters2[tracklet12.secondClusterIndex] = 1; usedTracklets[iTracklet01] = true; - lines.emplace_back(tracklet01, clusters0.data(), clusters1.data()); + lines.emplace_back(tracklet01, clusters0, clusters1); if (!trackletLabels.empty()) { linesLabels.emplace_back(trackletLabels[iTracklet01]); } @@ -153,8 +146,8 @@ static void trackletSelectionKernelHost( } } -template -void VertexerTraits::updateVertexingParameters(const std::vector& vrtPar, const TimeFrameGPUParameters& tfPar) +template +void VertexerTraits::updateVertexingParameters(const std::vector& vrtPar) { mVrtParams = vrtPar; mIndexTableUtils.setTrackingParameters(vrtPar[0]); @@ -165,15 +158,15 @@ void VertexerTraits::updateVertexingParameters(const std::vector -void VertexerTraits::computeTracklets(const int iteration) +template +void VertexerTraits::computeTracklets(const int iteration) { mTaskArena->execute([&] { - tbb::parallel_for(0, mTimeFrame->getNrof(), [&](const short pivotRofId) { - bool skipROF = iteration && (int)mTimeFrame->getPrimaryVertices(pivotRofId).size() > mVrtParams[iteration].vertPerRofThreshold; - short startROF{std::max((short)0, static_cast(pivotRofId - mVrtParams[iteration].deltaRof))}; - short endROF{std::min(static_cast(mTimeFrame->getNrof()), static_cast(pivotRofId + mVrtParams[iteration].deltaRof + 1))}; - for (auto targetRofId = startROF; targetRofId < endROF; ++targetRofId) { + tbb::parallel_for(0, mTimeFrame->getNrof(1), [&](const short pivotRofId) { + bool skipROF = !mTimeFrame->getROFMaskView().isROFEnabled(1, pivotRofId); + const auto& rofRange01 = mTimeFrame->getROFOverlapTableView().getOverlap(1, 0, pivotRofId); + for (auto targetRofId = rofRange01.getFirstEntry(); targetRofId < rofRange01.getEntriesBound(); ++targetRofId) { + const auto timeErr = mTimeFrame->getROFOverlapTableView().getTimeStamp(0, targetRofId, 1, pivotRofId); trackleterKernelHost( !skipROF ? mTimeFrame->getClustersOnLayer(targetRofId, 0) : gsl::span(), // Clusters to be matched with the next layer in target rof !skipROF ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), // Clusters to be matched with the current layer in pivot rof @@ -183,10 +176,15 @@ void VertexerTraits::computeTracklets(const int iteration) mTimeFrame->getTracklets()[0], // Flat tracklet buffer mTimeFrame->getNTrackletsCluster(pivotRofId, 0), // Span of the number of tracklets per each cluster in pivot rof mIndexTableUtils, - pivotRofId, - targetRofId, + timeErr, gsl::span(), // Offset in the tracklet buffer + 0, + 0, mVrtParams[iteration].maxTrackletsPerCluster); + } + const auto& rofRange12 = mTimeFrame->getROFOverlapTableView().getOverlap(1, 2, pivotRofId); + for (auto targetRofId = rofRange12.getFirstEntry(); targetRofId < rofRange12.getEntriesBound(); ++targetRofId) { + const auto timeErr = mTimeFrame->getROFOverlapTableView().getTimeStamp(2, targetRofId, 1, pivotRofId); trackleterKernelHost( !skipROF ? mTimeFrame->getClustersOnLayer(targetRofId, 2) : gsl::span(), !skipROF ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), @@ -196,9 +194,10 @@ void VertexerTraits::computeTracklets(const int iteration) mTimeFrame->getTracklets()[1], mTimeFrame->getNTrackletsCluster(pivotRofId, 1), // Span of the number of tracklets per each cluster in pivot rof mIndexTableUtils, - pivotRofId, - targetRofId, + timeErr, gsl::span(), // Offset in the tracklet buffer + 0, + 0, mVrtParams[iteration].maxTrackletsPerCluster); } mTimeFrame->getNTrackletsROF(pivotRofId, 0) = std::accumulate(mTimeFrame->getNTrackletsCluster(pivotRofId, 0).begin(), mTimeFrame->getNTrackletsCluster(pivotRofId, 0).end(), 0); @@ -214,13 +213,12 @@ void VertexerTraits::computeTracklets(const int iteration) mTimeFrame->getTracklets()[1].resize(tot1); } - tbb::parallel_for(0, mTimeFrame->getNrof(), [&](const short pivotRofId) { - bool skipROF = iteration && (int)mTimeFrame->getPrimaryVertices(pivotRofId).size() > mVrtParams[iteration].vertPerRofThreshold; - short startROF{std::max((short)0, static_cast(pivotRofId - mVrtParams[iteration].deltaRof))}; - short endROF{std::min(static_cast(mTimeFrame->getNrof()), static_cast(pivotRofId + mVrtParams[iteration].deltaRof + 1))}; - auto mobileOffset0 = mTimeFrame->getNTrackletsROF(pivotRofId, 0); - auto mobileOffset1 = mTimeFrame->getNTrackletsROF(pivotRofId, 1); - for (auto targetRofId = startROF; targetRofId < endROF; ++targetRofId) { + tbb::parallel_for(0, mTimeFrame->getNrof(1), [&](const short pivotRofId) { + bool skipROF = !mTimeFrame->getROFMaskView().isROFEnabled(1, pivotRofId); + const int globalOffsetPivot = mTimeFrame->getSortedStartIndex(pivotRofId, 1); + const auto& rofRange01 = mTimeFrame->getROFOverlapTableView().getOverlap(1, 0, pivotRofId); + for (auto targetRofId = rofRange01.getFirstEntry(); targetRofId < rofRange01.getEntriesBound(); ++targetRofId) { + const auto timeErr = mTimeFrame->getROFOverlapTableView().getTimeStamp(0, targetRofId, 1, pivotRofId); trackleterKernelHost( !skipROF ? mTimeFrame->getClustersOnLayer(targetRofId, 0) : gsl::span(), !skipROF ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), @@ -230,10 +228,15 @@ void VertexerTraits::computeTracklets(const int iteration) mTimeFrame->getTracklets()[0], mTimeFrame->getNTrackletsCluster(pivotRofId, 0), mIndexTableUtils, - pivotRofId, - targetRofId, + timeErr, mTimeFrame->getExclusiveNTrackletsCluster(pivotRofId, 0), + mTimeFrame->getSortedStartIndex(targetRofId, 0), + globalOffsetPivot, mVrtParams[iteration].maxTrackletsPerCluster); + } + const auto& rofRange12 = mTimeFrame->getROFOverlapTableView().getOverlap(1, 2, pivotRofId); + for (auto targetRofId = rofRange12.getFirstEntry(); targetRofId < rofRange12.getEntriesBound(); ++targetRofId) { + const auto timeErr = mTimeFrame->getROFOverlapTableView().getTimeStamp(2, targetRofId, 1, pivotRofId); trackleterKernelHost( !skipROF ? mTimeFrame->getClustersOnLayer(targetRofId, 2) : gsl::span(), !skipROF ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), @@ -243,9 +246,10 @@ void VertexerTraits::computeTracklets(const int iteration) mTimeFrame->getTracklets()[1], mTimeFrame->getNTrackletsCluster(pivotRofId, 1), mIndexTableUtils, - pivotRofId, - targetRofId, + timeErr, mTimeFrame->getExclusiveNTrackletsCluster(pivotRofId, 1), + mTimeFrame->getSortedStartIndex(targetRofId, 2), + globalOffsetPivot, mVrtParams[iteration].maxTrackletsPerCluster); } }); @@ -256,8 +260,8 @@ void VertexerTraits::computeTracklets(const int iteration) for (const auto& trk : mTimeFrame->getTracklets()[0]) { o2::MCCompLabel label; if (!trk.isEmpty()) { - int sortedId0{mTimeFrame->getSortedIndex(trk.rof[0], 0, trk.firstClusterIndex)}; - int sortedId1{mTimeFrame->getSortedIndex(trk.rof[1], 1, trk.secondClusterIndex)}; + int sortedId0{trk.firstClusterIndex}; + int sortedId1{trk.secondClusterIndex}; for (const auto& lab0 : mTimeFrame->getClusterLabels(0, mTimeFrame->getClusters()[0][sortedId0].clusterId)) { for (const auto& lab1 : mTimeFrame->getClusterLabels(1, mTimeFrame->getClusters()[1][sortedId1].clusterId)) { if (lab0 == lab1 && lab0.isValid()) { @@ -273,570 +277,260 @@ void VertexerTraits::computeTracklets(const int iteration) mTimeFrame->getTrackletsLabel(0).emplace_back(label); } } - -#ifdef VTX_DEBUG - debugComputeTracklets(iteration); -#endif } -template -void VertexerTraits::computeTrackletMatching(const int iteration) +template +void VertexerTraits::computeTrackletMatching(const int iteration) { mTaskArena->execute([&] { tbb::combinable totalLines{0}; tbb::parallel_for( - tbb::blocked_range(0, (short)mTimeFrame->getNrof()), + tbb::blocked_range(0, (short)mTimeFrame->getNrof(1)), [&](const tbb::blocked_range& Rofs) { for (short pivotRofId = Rofs.begin(); pivotRofId < Rofs.end(); ++pivotRofId) { - if (iteration && (int)mTimeFrame->getPrimaryVertices(pivotRofId).size() > mVrtParams[iteration].vertPerRofThreshold) { - continue; - } if (mTimeFrame->getFoundTracklets(pivotRofId, 0).empty()) { continue; } mTimeFrame->getLines(pivotRofId).reserve(mTimeFrame->getNTrackletsCluster(pivotRofId, 0).size()); bounded_vector usedTracklets(mTimeFrame->getFoundTracklets(pivotRofId, 0).size(), false, mMemoryPool.get()); - short startROF{std::max((short)0, static_cast(pivotRofId - mVrtParams[iteration].deltaRof))}; - short endROF{std::min(static_cast(mTimeFrame->getNrof()), static_cast(pivotRofId + mVrtParams[iteration].deltaRof + 1))}; - - // needed only if multi-threaded using deltaRof and only at the overlap edges of the ranges - bool safeWrite = mTaskArena->max_concurrency() > 1 && mVrtParams[iteration].deltaRof != 0 && ((Rofs.begin() - startROF < 0) || (endROF - Rofs.end() > 0)); - - for (short targetRofId0 = startROF; targetRofId0 < endROF; ++targetRofId0) { - for (short targetRofId2 = startROF; targetRofId2 < endROF; ++targetRofId2) { - if (std::abs(targetRofId0 - targetRofId2) > mVrtParams[iteration].deltaRof) { // do not allow over 3 ROFs - continue; - } - trackletSelectionKernelHost( - mTimeFrame->getClustersOnLayer(targetRofId0, 0), - mTimeFrame->getClustersOnLayer(pivotRofId, 1), - mTimeFrame->getUsedClustersROF(targetRofId0, 0), - mTimeFrame->getUsedClustersROF(targetRofId2, 2), - mTimeFrame->getFoundTracklets(pivotRofId, 0), - mTimeFrame->getFoundTracklets(pivotRofId, 1), - usedTracklets, - mTimeFrame->getNTrackletsCluster(pivotRofId, 0), - mTimeFrame->getNTrackletsCluster(pivotRofId, 1), - mTimeFrame->getLines(pivotRofId), - mTimeFrame->getLabelsFoundTracklets(pivotRofId, 0), - mTimeFrame->getLinesLabel(pivotRofId), - targetRofId0, - targetRofId2, - safeWrite, - mVrtParams[iteration].tanLambdaCut, - mVrtParams[iteration].phiCut); + trackletSelectionKernelHost( + mTimeFrame->getClusters()[0].data(), + mTimeFrame->getClusters()[1].data(), + mTimeFrame->getUsedClusters(0), + mTimeFrame->getUsedClusters(2), + mTimeFrame->getFoundTracklets(pivotRofId, 0), + mTimeFrame->getFoundTracklets(pivotRofId, 1), + usedTracklets, + mTimeFrame->getNTrackletsCluster(pivotRofId, 0), + mTimeFrame->getNTrackletsCluster(pivotRofId, 1), + mTimeFrame->getLines(pivotRofId), + mTimeFrame->getLabelsFoundTracklets(pivotRofId, 0), + mTimeFrame->getLinesLabel(pivotRofId), + static_cast(mTimeFrame->getClustersOnLayer(pivotRofId, 1).size()), + mVrtParams[iteration].tanLambdaCut, + mVrtParams[iteration].phiCut); + auto& lines = mTimeFrame->getLines(pivotRofId); + totalLines.local() += lines.size(); + std::stable_sort(lines.begin(), lines.end(), [](const Line& a, const Line& b) { + // sort by lower edge and secondly prefer wider windows + if (a.mTime.lower() != b.mTime.lower()) { + return a.mTime.lower() < b.mTime.lower(); } - } - totalLines.local() += mTimeFrame->getLines(pivotRofId).size(); + return a.mTime.upper() > b.mTime.upper(); + }); } }); mTimeFrame->setNLinesTotal(totalLines.combine(std::plus())); }); -#ifdef VTX_DEBUG - debugComputeTrackletMatching(iteration); -#endif - - // from here on we do not use tracklets from L1-2 anymore, so let's free them - deepVectorClear(mTimeFrame->getTracklets()[1]); + // from here on we do not use tracklets anymore, so let's free them + deepVectorClear(mTimeFrame->getTracklets()); } -template -void VertexerTraits::computeVertices(const int iteration) +template +void VertexerTraits::computeVertices(const int iteration) { - auto nsigmaCut{std::min(mVrtParams[iteration].vertNsigmaCut * mVrtParams[iteration].vertNsigmaCut * (mVrtParams[iteration].vertRadiusSigma * mVrtParams[iteration].vertRadiusSigma + mVrtParams[iteration].trackletSigma * mVrtParams[iteration].trackletSigma), 1.98f)}; - bounded_vector vertices(mMemoryPool.get()); - bounded_vector> polls(mMemoryPool.get()); - bounded_vector contLabels(mMemoryPool.get()); - bounded_vector noClustersVec(mTimeFrame->getNrof(), 0, mMemoryPool.get()); - for (int rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { - if (iteration && (int)mTimeFrame->getPrimaryVertices(rofId).size() > mVrtParams[iteration].vertPerRofThreshold) { - continue; - } - const int numTracklets{static_cast(mTimeFrame->getLines(rofId).size())}; - - bounded_vector usedTracklets(numTracklets, false, mMemoryPool.get()); - for (int line1{0}; line1 < numTracklets; ++line1) { - if (usedTracklets[line1]) { + const auto nsigmaCut{std::min(mVrtParams[iteration].vertNsigmaCut * mVrtParams[iteration].vertNsigmaCut * (mVrtParams[iteration].vertRadiusSigma * mVrtParams[iteration].vertRadiusSigma + mVrtParams[iteration].trackletSigma * mVrtParams[iteration].trackletSigma), 1.98f)}; + const auto pairCut2{mVrtParams[iteration].pairCut * mVrtParams[iteration].pairCut}; + const int nRofs = mTimeFrame->getNrof(1); + const bool hasMC = mTimeFrame->hasMCinformation(); + std::vector> rofVertices(nRofs); + std::vector> rofLabels(nRofs); + + const auto processROF = [&](const int rofId) { + auto& lines = mTimeFrame->getLines(rofId); + const int nLines{static_cast(lines.size())}; + bounded_vector usedTracklets(nLines, 0, mMemoryPool.get()); + auto& clusters = mTimeFrame->getTrackletClusters(rofId); + + for (int iLine1{0}; iLine1 < nLines; ++iLine1) { + if (usedTracklets[iLine1]) { continue; } - for (int line2{line1 + 1}; line2 < numTracklets; ++line2) { - if (usedTracklets[line2]) { + const auto& line1 = lines[iLine1]; + for (int iLine2{iLine1 + 1}; iLine2 < nLines; ++iLine2) { + if (usedTracklets[iLine2]) { + continue; + } + const auto& line2 = lines[iLine2]; + if (!line1.mTime.isCompatible(line2.mTime)) { continue; } - auto dca{Line::getDCA(mTimeFrame->getLines(rofId)[line1], mTimeFrame->getLines(rofId)[line2])}; - if (dca < mVrtParams[iteration].pairCut) { - mTimeFrame->getTrackletClusters(rofId).emplace_back(line1, mTimeFrame->getLines(rofId)[line1], line2, mTimeFrame->getLines(rofId)[line2]); - std::array tmpVertex{mTimeFrame->getTrackletClusters(rofId).back().getVertex()}; - if (tmpVertex[0] * tmpVertex[0] + tmpVertex[1] * tmpVertex[1] > 4.f) { - mTimeFrame->getTrackletClusters(rofId).pop_back(); + auto dca2{Line::getDCA2(line1, line2)}; + if (dca2 < pairCut2) { + auto& cluster = clusters.emplace_back(iLine1, line1, iLine2, line2); + if (!cluster.isValid() || cluster.getR2() > 4.f) { + clusters.pop_back(); continue; } - usedTracklets[line1] = true; - usedTracklets[line2] = true; - for (int tracklet3{0}; tracklet3 < numTracklets; ++tracklet3) { - if (usedTracklets[tracklet3]) { + + usedTracklets[iLine1] = 1; + usedTracklets[iLine2] = 1; + for (int iLine3{0}; iLine3 < nLines; ++iLine3) { + if (usedTracklets[iLine3]) { continue; } - if (Line::getDistanceFromPoint(mTimeFrame->getLines(rofId)[tracklet3], tmpVertex) < mVrtParams[iteration].pairCut) { - mTimeFrame->getTrackletClusters(rofId).back().add(tracklet3, mTimeFrame->getLines(rofId)[tracklet3]); - usedTracklets[tracklet3] = true; - tmpVertex = mTimeFrame->getTrackletClusters(rofId).back().getVertex(); + const auto& line3 = lines[iLine3]; + if (!line3.mTime.isCompatible(cluster.getTimeStamp())) { + continue; + } + const auto distance2 = Line::getDistance2FromPoint(line3, cluster.getVertex()); + if (distance2 < pairCut2) { + cluster.add(iLine3, line3); + usedTracklets[iLine3] = 1; } } break; } } } - if (mVrtParams[iteration].allowSingleContribClusters) { - auto beamLine = Line{{mTimeFrame->getBeamX(), mTimeFrame->getBeamY(), -50.f}, {mTimeFrame->getBeamX(), mTimeFrame->getBeamY(), 50.f}}; // use beam position as contributor - for (size_t iLine{0}; iLine < numTracklets; ++iLine) { - if (!usedTracklets[iLine]) { - auto dca = Line::getDCA(mTimeFrame->getLines(rofId)[iLine], beamLine); - if (dca < mVrtParams[iteration].pairCut) { - mTimeFrame->getTrackletClusters(rofId).emplace_back(iLine, mTimeFrame->getLines(rofId)[iLine], -1, beamLine); // beamline must be passed as second line argument - } - } - } - } // Cluster merging - std::sort(mTimeFrame->getTrackletClusters(rofId).begin(), mTimeFrame->getTrackletClusters(rofId).end(), + std::sort(clusters.begin(), clusters.end(), [](ClusterLines& cluster1, ClusterLines& cluster2) { return cluster1.getSize() > cluster2.getSize(); }); - noClustersVec[rofId] = static_cast(mTimeFrame->getTrackletClusters(rofId).size()); - for (int iCluster1{0}; iCluster1 < noClustersVec[rofId]; ++iCluster1) { - std::array vertex1{mTimeFrame->getTrackletClusters(rofId)[iCluster1].getVertex()}; + int nClusters = static_cast(clusters.size()); + for (int iCluster1{0}; iCluster1 < nClusters; ++iCluster1) { + std::array vertex1{clusters[iCluster1].getVertex()}; std::array vertex2{}; - for (int iCluster2{iCluster1 + 1}; iCluster2 < noClustersVec[rofId]; ++iCluster2) { - vertex2 = mTimeFrame->getTrackletClusters(rofId)[iCluster2].getVertex(); - if (o2::gpu::GPUCommonMath::Abs(vertex1[2] - vertex2[2]) < mVrtParams[iteration].clusterCut) { - float distance{(vertex1[0] - vertex2[0]) * (vertex1[0] - vertex2[0]) + - (vertex1[1] - vertex2[1]) * (vertex1[1] - vertex2[1]) + - (vertex1[2] - vertex2[2]) * (vertex1[2] - vertex2[2])}; - if (distance < mVrtParams[iteration].pairCut * mVrtParams[iteration].pairCut) { - for (auto label : mTimeFrame->getTrackletClusters(rofId)[iCluster2].getLabels()) { - mTimeFrame->getTrackletClusters(rofId)[iCluster1].add(label, mTimeFrame->getLines(rofId)[label]); - vertex1 = mTimeFrame->getTrackletClusters(rofId)[iCluster1].getVertex(); + for (int iCluster2{iCluster1 + 1}; iCluster2 < nClusters; ++iCluster2) { + if (clusters[iCluster1].getTimeStamp().isCompatible(clusters[iCluster2].getTimeStamp())) { + vertex2 = clusters[iCluster2].getVertex(); + if (o2::gpu::GPUCommonMath::Abs(vertex1[2] - vertex2[2]) < mVrtParams[iteration].clusterCut) { + float distance{((vertex1[0] - vertex2[0]) * (vertex1[0] - vertex2[0])) + + ((vertex1[1] - vertex2[1]) * (vertex1[1] - vertex2[1])) + + ((vertex1[2] - vertex2[2]) * (vertex1[2] - vertex2[2]))}; + if (distance < mVrtParams[iteration].pairCut * mVrtParams[iteration].pairCut) { + for (auto label : clusters[iCluster2].getLabels()) { + clusters[iCluster1].add(label, lines[label]); + vertex1 = clusters[iCluster1].getVertex(); + } + clusters.erase(clusters.begin() + iCluster2); + --iCluster2; + --nClusters; } - mTimeFrame->getTrackletClusters(rofId).erase(mTimeFrame->getTrackletClusters(rofId).begin() + iCluster2); - --iCluster2; - --noClustersVec[rofId]; } } } } - } - for (int rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { - std::sort(mTimeFrame->getTrackletClusters(rofId).begin(), mTimeFrame->getTrackletClusters(rofId).end(), - [](const ClusterLines& cluster1, const ClusterLines& cluster2) { return cluster1.getSize() > cluster2.getSize(); }); // ensure clusters are ordered by contributors, so that we can cat after the first. + + // Vertex filtering + std::sort(clusters.begin(), clusters.end(), + [](const ClusterLines& cluster1, const ClusterLines& cluster2) { return cluster1.getSize() > cluster2.getSize(); }); bool atLeastOneFound{false}; - for (int iCluster{0}; iCluster < noClustersVec[rofId]; ++iCluster) { + for (int iCluster{0}; iCluster < nClusters; ++iCluster) { bool lowMultCandidate{false}; - double beamDistance2{(mTimeFrame->getBeamX() - mTimeFrame->getTrackletClusters(rofId)[iCluster].getVertex()[0]) * (mTimeFrame->getBeamX() - mTimeFrame->getTrackletClusters(rofId)[iCluster].getVertex()[0]) + - (mTimeFrame->getBeamY() - mTimeFrame->getTrackletClusters(rofId)[iCluster].getVertex()[1]) * (mTimeFrame->getBeamY() - mTimeFrame->getTrackletClusters(rofId)[iCluster].getVertex()[1])}; - if (atLeastOneFound && (lowMultCandidate = mTimeFrame->getTrackletClusters(rofId)[iCluster].getSize() < mVrtParams[iteration].clusterContributorsCut)) { // We might have pile up with nContr > cut. + double beamDistance2{(mTimeFrame->getBeamX() - clusters[iCluster].getVertex()[0]) * (mTimeFrame->getBeamX() - clusters[iCluster].getVertex()[0]) + + (mTimeFrame->getBeamY() - clusters[iCluster].getVertex()[1]) * (mTimeFrame->getBeamY() - clusters[iCluster].getVertex()[1])}; + if (atLeastOneFound && (lowMultCandidate = clusters[iCluster].getSize() < mVrtParams[iteration].clusterContributorsCut)) { lowMultCandidate &= (beamDistance2 < mVrtParams[iteration].lowMultBeamDistCut * mVrtParams[iteration].lowMultBeamDistCut); - if (!lowMultCandidate) { // Not the first cluster and not a low multiplicity candidate, we can remove it - mTimeFrame->getTrackletClusters(rofId).erase(mTimeFrame->getTrackletClusters(rofId).begin() + iCluster); - noClustersVec[rofId]--; + if (!lowMultCandidate) { + clusters.erase(clusters.begin() + iCluster); + nClusters--; continue; } } - if (beamDistance2 < nsigmaCut && o2::gpu::GPUCommonMath::Abs(mTimeFrame->getTrackletClusters(rofId)[iCluster].getVertex()[2]) < mVrtParams[iteration].maxZPositionAllowed) { + if (beamDistance2 < nsigmaCut && o2::gpu::GPUCommonMath::Abs(clusters[iCluster].getVertex()[2]) < mVrtParams[iteration].maxZPositionAllowed) { atLeastOneFound = true; - auto& vertex = vertices.emplace_back(o2::math_utils::Point3D(mTimeFrame->getTrackletClusters(rofId)[iCluster].getVertex()[0], - mTimeFrame->getTrackletClusters(rofId)[iCluster].getVertex()[1], - mTimeFrame->getTrackletClusters(rofId)[iCluster].getVertex()[2]), - mTimeFrame->getTrackletClusters(rofId)[iCluster].getRMS2(), // Symm matrix. Diagonal: RMS2 components, - // off-diagonal: square mean of projections on planes. - mTimeFrame->getTrackletClusters(rofId)[iCluster].getSize(), // Contributors - mTimeFrame->getTrackletClusters(rofId)[iCluster].getAvgDistance2()); // In place of chi2 + Vertex vertex{clusters[iCluster].getVertex().data(), + clusters[iCluster].getRMS2(), + (ushort)clusters[iCluster].getSize(), + clusters[iCluster].getAvgDistance2()}; if (iteration) { vertex.setFlags(Vertex::UPCMode); } - vertex.setTimeStamp(mTimeFrame->getTrackletClusters(rofId)[iCluster].getROF()); - if (mTimeFrame->hasMCinformation()) { + vertex.setTimeStamp(clusters[iCluster].getTimeStamp()); + rofVertices[rofId].push_back(vertex); + if (hasMC) { bounded_vector labels(mMemoryPool.get()); - for (auto& index : mTimeFrame->getTrackletClusters(rofId)[iCluster].getLabels()) { - labels.push_back(mTimeFrame->getLinesLabel(rofId)[index]); // then we can use nContributors from vertices to get the labels - } - polls.push_back(computeMain(labels)); - if (mVrtParams[iteration].outputContLabels) { - contLabels.insert(contLabels.end(), labels.begin(), labels.end()); + for (auto& index : clusters[iCluster].getLabels()) { + labels.push_back(mTimeFrame->getLinesLabel(rofId)[index]); } + rofLabels[rofId].push_back(computeMain(labels)); } } } - if (!iteration) { - mTimeFrame->addPrimaryVertices(vertices, iteration); - if (mTimeFrame->hasMCinformation()) { - mTimeFrame->addPrimaryVerticesLabels(polls); - if (mVrtParams[iteration].outputContLabels) { - mTimeFrame->addPrimaryVerticesContributorLabels(contLabels); - } - } - } else { - mTimeFrame->addPrimaryVerticesInROF(vertices, rofId, iteration); - if (mTimeFrame->hasMCinformation()) { - mTimeFrame->addPrimaryVerticesLabelsInROF(polls, rofId); - if (mVrtParams[iteration].outputContLabels) { - mTimeFrame->addPrimaryVerticesContributorLabelsInROF(contLabels, rofId); - } - } + }; + + if (mTaskArena->max_concurrency() <= 1) { + for (int rofId{0}; rofId < nRofs; ++rofId) { + processROF(rofId); } - if (vertices.empty() && !(iteration && (int)mTimeFrame->getPrimaryVertices(rofId).size() > mVrtParams[iteration].vertPerRofThreshold)) { - mTimeFrame->getNoVertexROF()++; + } else { + mTaskArena->execute([&] { + tbb::parallel_for(0, nRofs, [&](const int rofId) { + processROF(rofId); + }); + }); + } + // add vertices, these anyways get sorted afterward + for (int rofId{0}; rofId < nRofs; ++rofId) { + for (auto& vertex : rofVertices[rofId]) { + mTimeFrame->addPrimaryVertex(vertex); + } + if (hasMC) { + for (auto& label : rofLabels[rofId]) { + mTimeFrame->addPrimaryVertexLabel(label); + } } - vertices.clear(); - polls.clear(); } - -#ifdef VTX_DEBUG - debugComputeVertices(iteration); -#endif } -template -void VertexerTraits::addTruthSeedingVertices() +template +void VertexerTraits::addTruthSeedingVertices() { LOGP(info, "Using truth seeds as vertices; will skip computations"); - mTimeFrame->resetRofPV(); const auto dc = o2::steer::DigitizationContext::loadFromFile("collisioncontext.root"); const auto irs = dc->getEventRecords(); - int64_t roFrameBiasInBC = o2::itsmft::DPLAlpideParam::Instance().roFrameBiasInBC; - int64_t roFrameLengthInBC = o2::itsmft::DPLAlpideParam::Instance().roFrameLengthInBC; + int64_t roFrameBiasInBC = o2::itsmft::DPLAlpideParam::Instance().getROFBiasInBC(1); + int64_t roFrameLengthInBC = o2::itsmft::DPLAlpideParam::Instance().getROFLengthInBC(1); o2::steer::MCKinematicsReader mcReader(dc); - struct VertInfo { - bounded_vector vertices; - bounded_vector srcs; - bounded_vector events; - }; - std::map vertices; const int iSrc = 0; // take only events from collision generator auto eveId2colId = dc->getCollisionIndicesForSource(iSrc); for (int iEve{0}; iEve < mcReader.getNEvents(iSrc); ++iEve) { const auto& ir = irs[eveId2colId[iEve]]; if (!ir.isDummy()) { // do we need this, is this for diffractive events? const auto& eve = mcReader.getMCEventHeader(iSrc, iEve); - int rofId = ((ir - raw::HBFUtils::Instance().getFirstSampledTFIR()).toLong() - roFrameBiasInBC) / roFrameLengthInBC; - if (!vertices.contains(rofId)) { - vertices[rofId] = { - .vertices = bounded_vector(mMemoryPool.get()), - .srcs = bounded_vector(mMemoryPool.get()), - .events = bounded_vector(mMemoryPool.get()), - }; + auto bc = (ir - raw::HBFUtils::Instance().getFirstSampledTFIR()).toLong() - roFrameBiasInBC; + if (bc < 0) { // event happened before TF + continue; } Vertex vert; - vert.setTimeStamp(rofId); + vert.getTimeStamp().setTimeStamp(bc); + vert.getTimeStamp().setTimeStampError(roFrameLengthInBC / 2); // 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; + if (!trk.isPrimary() || trk.GetPt() < 0.05 || std::abs(trk.GetEta()) > 1.1) { + return false; + } + return o2::O2DatabasePDG::Instance()->GetParticle(trk.GetPdgCode())->Charge() != 0; }))); vert.setXYZ((float)eve.GetX(), (float)eve.GetY(), (float)eve.GetZ()); vert.setChi2(1); // not used as constraint - constexpr float cov = 50e-9; - vert.setCov(cov, cov, cov, cov, cov, cov); - vertices[rofId].vertices.push_back(vert); - vertices[rofId].srcs.push_back(iSrc); - vertices[rofId].events.push_back(iEve); + constexpr float cov = 25e-4; + vert.setSigmaX(cov); + vert.setSigmaY(cov); + vert.setSigmaZ(cov); + mTimeFrame->addPrimaryVertex(vert); + o2::MCCompLabel mcLbl(o2::MCCompLabel::maxTrackID(), iEve, iSrc, false); + VertexLabel lbl(mcLbl, 1.0); + mTimeFrame->addPrimaryVertexLabel(lbl); } mcReader.releaseTracksForSourceAndEvent(iSrc, iEve); } - size_t nVerts{0}; - for (int iROF{0}; iROF < mTimeFrame->getNrof(); ++iROF) { - bounded_vector verts(mMemoryPool.get()); - bounded_vector> polls(mMemoryPool.get()); - if (vertices.contains(iROF)) { - const auto& vertInfo = vertices[iROF]; - verts = vertInfo.vertices; - nVerts += verts.size(); - for (size_t i{0}; i < verts.size(); ++i) { - o2::MCCompLabel lbl(o2::MCCompLabel::maxTrackID(), vertInfo.events[i], vertInfo.srcs[i], false); - polls.emplace_back(lbl, 1.f); - } - } else { - mTimeFrame->getNoVertexROF()++; - } - mTimeFrame->addPrimaryVertices(verts, 0); - mTimeFrame->addPrimaryVerticesLabels(polls); - } - LOGP(info, "Found {}/{} ROFs with {} vertices -> ={:.2f}", vertices.size(), mTimeFrame->getNrof(), nVerts, (float)nVerts / (float)vertices.size()); + LOGP(info, "Imposed {} pv collisions from mc-truth", mTimeFrame->getPrimaryVertices().size()); } -template -void VertexerTraits::setNThreads(int n, std::shared_ptr& arena) +template +void VertexerTraits::setNThreads(int n, std::shared_ptr& arena) { -#if defined(VTX_DEBUG) - LOGP(info, "Vertexer with debug output forcing single thread"); - mTaskArena = std::make_shared(1); -#else if (arena == nullptr) { mTaskArena = std::make_shared(std::abs(n)); LOGP(info, "Setting seeding vertexer with {} threads.", n); } else { mTaskArena = arena; - LOGP(info, "Attaching vertexer to calling thread's arena"); - } -#endif -} - -template -void VertexerTraits::debugComputeTracklets(int iteration) -{ - auto stream = new utils::TreeStreamRedirector("artefacts_tf.root", "recreate"); - LOGP(info, "writing debug output for computeTracklets"); - for (int rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { - const auto& strk0 = mTimeFrame->getFoundTracklets(rofId, 0); - std::vector trk0(strk0.begin(), strk0.end()); - const auto& strk1 = mTimeFrame->getFoundTracklets(rofId, 1); - std::vector trk1(strk1.begin(), strk1.end()); - (*stream) << "tracklets" - << "Tracklets0=" << trk0 - << "Tracklets1=" << trk1 - << "iteration=" << iteration - << "\n"; - } - stream->Close(); - delete stream; -} - -template -void VertexerTraits::debugComputeTrackletMatching(int iteration) -{ - auto stream = new utils::TreeStreamRedirector("artefacts_tf.root", "update"); - LOGP(info, "writing debug output for computeTrackletMatching"); - for (int rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { - (*stream) << "lines" - << "Lines=" << toSTDVector(mTimeFrame->getLines(rofId)) - << "NTrackletCluster01=" << mTimeFrame->getNTrackletsCluster(rofId, 0) - << "NTrackletCluster12=" << mTimeFrame->getNTrackletsCluster(rofId, 1) - << "iteration=" << iteration - << "\n"; - } - - if (mTimeFrame->hasMCinformation()) { - LOGP(info, "\tdumping also MC information"); - const auto dc = o2::steer::DigitizationContext::loadFromFile("collisioncontext.root"); - const auto irs = dc->getEventRecords(); - int64_t roFrameBiasInBC = o2::itsmft::DPLAlpideParam::Instance().roFrameBiasInBC; - int64_t roFrameLengthInBC = o2::itsmft::DPLAlpideParam::Instance().roFrameLengthInBC; - o2::steer::MCKinematicsReader mcReader(dc); - - std::map eve2BcInROF, bcInRofNEve; - for (int iSrc{0}; iSrc < mcReader.getNSources(); ++iSrc) { - auto eveId2colId = dc->getCollisionIndicesForSource(iSrc); - for (int iEve{0}; iEve < mcReader.getNEvents(iSrc); ++iEve) { - const auto& ir = irs[eveId2colId[iEve]]; - if (!ir.isDummy()) { // do we need this, is this for diffractive events? - const auto& eve = mcReader.getMCEventHeader(iSrc, iEve); - const int bcInROF = ((ir - raw::HBFUtils::Instance().getFirstSampledTFIR()).toLong() - roFrameBiasInBC) % roFrameLengthInBC; - eve2BcInROF[iEve] = bcInROF; - ++bcInRofNEve[bcInROF]; - } - } - } - - std::unordered_map bcROFNTracklets01, bcROFNTracklets12; - std::vector> tracklet01BC, tracklet12BC; - for (int rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { - { // 0-1 - const auto& tracklet01 = mTimeFrame->getFoundTracklets(rofId, 0); - const auto& lbls01 = mTimeFrame->getLabelsFoundTracklets(rofId, 0); - auto& trkls01 = tracklet01BC.emplace_back(); - for (int iTrklt{0}; iTrklt < (int)tracklet01.size(); ++iTrklt) { - const auto& tracklet = tracklet01[iTrklt]; - const auto& lbl = lbls01[iTrklt]; - if (lbl.isCorrect()) { - ++bcROFNTracklets01[eve2BcInROF[lbl.getEventID()]]; - trkls01.push_back(eve2BcInROF[lbl.getEventID()]); - } else { - trkls01.push_back(-1); - } - } - } - { // 1-2 computed on the fly! - const auto& tracklet12 = mTimeFrame->getFoundTracklets(rofId, 1); - auto& trkls12 = tracklet12BC.emplace_back(); - for (int iTrklt{0}; iTrklt < (int)tracklet12.size(); ++iTrklt) { - const auto& tracklet = tracklet12[iTrklt]; - o2::MCCompLabel label; - - int sortedId1{mTimeFrame->getSortedIndex(tracklet.rof[0], 1, tracklet.firstClusterIndex)}; - int sortedId2{mTimeFrame->getSortedIndex(tracklet.rof[1], 2, tracklet.secondClusterIndex)}; - for (const auto& lab1 : mTimeFrame->getClusterLabels(1, mTimeFrame->getClusters()[1][sortedId1].clusterId)) { - for (const auto& lab2 : mTimeFrame->getClusterLabels(2, mTimeFrame->getClusters()[2][sortedId2].clusterId)) { - if (lab1 == lab2 && lab1.isValid()) { - label = lab1; - break; - } - } - if (label.isValid()) { - break; - } - } - - if (label.isCorrect()) { - ++bcROFNTracklets12[eve2BcInROF[label.getEventID()]]; - trkls12.push_back(eve2BcInROF[label.getEventID()]); - } else { - trkls12.push_back(-1); - } - } - } - } - LOGP(info, "\tdumping ntracklets/RofBC ({})", bcInRofNEve.size()); - for (const auto& [bcInRof, neve] : bcInRofNEve) { - (*stream) << "ntracklets" - << "bcInROF=" << bcInRof - << "ntrkl01=" << bcROFNTracklets01[bcInRof] - << "ntrkl12=" << bcROFNTracklets12[bcInRof] - << "neve=" << neve - << "iteration=" << iteration - << "\n"; - } - - std::unordered_map bcROFNLines; - for (int rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { - const auto& lines = mTimeFrame->getLines(rofId); - const auto& lbls = mTimeFrame->getLinesLabel(rofId); - for (int iLine{0}; iLine < (int)lines.size(); ++iLine) { - const auto& line = lines[iLine]; - const auto& lbl = lbls[iLine]; - if (lbl.isCorrect()) { - ++bcROFNLines[eve2BcInROF[lbl.getEventID()]]; - } - } - } - - LOGP(info, "\tdumping nlines/RofBC"); - for (const auto& [bcInRof, neve] : bcInRofNEve) { - (*stream) << "nlines" - << "bcInROF=" << bcInRof - << "nline=" << bcROFNLines[bcInRof] - << "neve=" << neve - << "iteration=" << iteration - << "\n"; - } - } - stream->Close(); - delete stream; -} - -template -void VertexerTraits::debugComputeVertices(int iteration) -{ - auto stream = new utils::TreeStreamRedirector("artefacts_tf.root", "update"); - LOGP(info, "writing debug output for computeVertices"); - for (auto rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { - (*stream) << "clusterlines" - << "clines_post=" << toSTDVector(mTimeFrame->getTrackletClusters(rofId)) - << "iteration=" << iteration - << "\n"; - } - - if (mTimeFrame->hasMCinformation()) { - LOGP(info, "\tdumping also MC information"); - const auto dc = o2::steer::DigitizationContext::loadFromFile("collisioncontext.root"); - const auto irs = dc->getEventRecords(); - int64_t roFrameBiasInBC = o2::itsmft::DPLAlpideParam::Instance().roFrameBiasInBC; - int64_t roFrameLengthInBC = o2::itsmft::DPLAlpideParam::Instance().roFrameLengthInBC; - o2::steer::MCKinematicsReader mcReader(dc); - - std::map eve2BcInROF, bcInRofNEve; - for (int iSrc{0}; iSrc < mcReader.getNSources(); ++iSrc) { - auto eveId2colId = dc->getCollisionIndicesForSource(iSrc); - for (int iEve{0}; iEve < mcReader.getNEvents(iSrc); ++iEve) { - const auto& ir = irs[eveId2colId[iEve]]; - if (!ir.isDummy()) { // do we need this, is this for diffractive events? - const auto& eve = mcReader.getMCEventHeader(iSrc, iEve); - const int bcInROF = ((ir - raw::HBFUtils::Instance().getFirstSampledTFIR()).toLong() - roFrameBiasInBC) % roFrameLengthInBC; - eve2BcInROF[iEve] = bcInROF; - ++bcInRofNEve[bcInROF]; - } - } - } - - std::unordered_map bcROFNVtx; - std::unordered_map bcROFNPur; - std::unordered_map uniqueVertices; - for (int rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { - const auto& pvs = mTimeFrame->getPrimaryVertices(rofId); - const auto& lblspv = mTimeFrame->getPrimaryVerticesMCRecInfo(rofId); - for (int i{0}; i < (int)pvs.size(); ++i) { - const auto& pv = pvs[i]; - const auto& [lbl, pur] = lblspv[i]; - if (lbl.isCorrect()) { - ++uniqueVertices[lbl]; - ++bcROFNVtx[eve2BcInROF[lbl.getEventID()]]; - bcROFNPur[eve2BcInROF[lbl.getEventID()]] += pur; - } - } - } - - std::unordered_map bcROFNUVtx, bcROFNCVtx; - for (const auto& [k, _] : eve2BcInROF) { - bcROFNUVtx[k] = bcROFNCVtx[k] = 0; - } - - for (const auto& [lbl, c] : uniqueVertices) { - if (c <= 1) { - ++bcROFNUVtx[eve2BcInROF[lbl.getEventID()]]; - } else { - ++bcROFNCVtx[eve2BcInROF[lbl.getEventID()]]; - } - } - - LOGP(info, "\tdumping nvtx/RofBC"); - for (const auto& [bcInRof, neve] : bcInRofNEve) { - (*stream) << "nvtx" - << "bcInROF=" << bcInRof - << "nvtx=" << bcROFNVtx[bcInRof] // all vertices - << "nuvtx=" << bcROFNUVtx[bcInRof] // unique vertices - << "ncvtx=" << bcROFNCVtx[bcInRof] // cloned vertices - << "npur=" << bcROFNPur[bcInRof] - << "neve=" << neve - << "iteration=" << iteration - << "\n"; - } - - // check dist of clones - std::unordered_map> cVtx; - for (int rofId{0}; rofId < mTimeFrame->getNrof(); ++rofId) { - const auto& pvs = mTimeFrame->getPrimaryVertices(rofId); - const auto& lblspv = mTimeFrame->getPrimaryVerticesMCRecInfo(rofId); - for (int i{0}; i < (int)pvs.size(); ++i) { - const auto& pv = pvs[i]; - const auto& [lbl, pur] = lblspv[i]; - if (lbl.isCorrect() && uniqueVertices.contains(lbl) && uniqueVertices[lbl] > 1) { - if (!cVtx.contains(lbl)) { - cVtx[lbl] = std::vector(); - } - cVtx[lbl].push_back(pv); - } - } - } - - for (auto& [_, vertices] : cVtx) { - std::sort(vertices.begin(), vertices.end(), [](const Vertex& a, const Vertex& b) { return a.getNContributors() > b.getNContributors(); }); - for (int i{0}; i < (int)vertices.size(); ++i) { - const auto vtx = vertices[i]; - (*stream) << "cvtx" - << "vertex=" << vtx - << "i=" << i - << "dx=" << vertices[0].getX() - vtx.getX() - << "dy=" << vertices[0].getY() - vtx.getY() - << "dz=" << vertices[0].getZ() - vtx.getZ() - << "drof=" << vertices[0].getTimeStamp().getTimeStamp() - vtx.getTimeStamp().getTimeStamp() - << "dnc=" << vertices[0].getNContributors() - vtx.getNContributors() - << "iteration=" << iteration - << "\n"; - } - } } - stream->Close(); - delete stream; } template class VertexerTraits<7>; diff --git a/Detectors/ITSMFT/ITS/tracking/test/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/test/CMakeLists.txt index 818ad1d667371..063583b4cfa1b 100644 --- a/Detectors/ITSMFT/ITS/tracking/test/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/test/CMakeLists.txt @@ -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. # @@ -14,3 +14,9 @@ o2_add_test(boundedmemoryresource COMPONENT_NAME its-tracking LABELS "its;tracking" PUBLIC_LINK_LIBRARIES O2::ITStracking) + +o2_add_test(roflookuptables + SOURCES testROFLookupTables.cxx + COMPONENT_NAME its-tracking + LABELS "its;tracking" + PUBLIC_LINK_LIBRARIES O2::ITStracking) diff --git a/Detectors/ITSMFT/ITS/tracking/test/testROFLookupTables.cxx b/Detectors/ITSMFT/ITS/tracking/test/testROFLookupTables.cxx new file mode 100644 index 0000000000000..8594e59149444 --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/test/testROFLookupTables.cxx @@ -0,0 +1,744 @@ +// 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 +#define BOOST_TEST_MODULE ITS ROFLookupTables +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK + +#include +#include "ITStracking/ROFLookupTables.h" + +/// -------- Tests -------- +// LayerTiming +BOOST_AUTO_TEST_CASE(layertiming_basic) +{ + o2::its::ROFOverlapTable<1> table; + table.defineLayer(0, 10, 594, 100, 0, 50); + const auto& layer = table.getLayer(0); + + // test ROF time calculations + auto start0 = layer.getROFStartInBC(0); + BOOST_CHECK_EQUAL(start0, 100); // delay only + + auto end0 = layer.getROFEndInBC(0); + BOOST_CHECK_EQUAL(end0, 100 + 594); + + // test second ROF + auto start1 = layer.getROFStartInBC(1); + BOOST_CHECK_EQUAL(start1, 100 + 594); +} + +BOOST_AUTO_TEST_CASE(layertiming_base) +{ + o2::its::ROFOverlapTable<3> table; + table.defineLayer(0, 10, 500, 0, 0, 0); + table.defineLayer(1, 12, 600, 50, 0, 0); + table.defineLayer(2, 8, 400, 100, 0, 0); + const auto& layer1 = table.getLayer(1); + BOOST_CHECK_EQUAL(layer1.mNROFsTF, 12); + BOOST_CHECK_EQUAL(layer1.mROFLength, 600); +} + +BOOST_AUTO_TEST_CASE(rofmask_construct_from_timing) +{ + o2::its::ROFOverlapTable<2> timing; + timing.defineLayer(0, 3, 100, 0, 0, 0); + timing.defineLayer(1, 4, 50, 25, 0, 0); + + o2::its::ROFMaskTable<2> mask{timing}; + const auto view = mask.getView(); + + BOOST_REQUIRE(view.mFlatMask != nullptr); + BOOST_REQUIRE(view.mLayerROFOffsets != nullptr); + BOOST_CHECK_EQUAL(view.mLayerROFOffsets[0], 0); + BOOST_CHECK_EQUAL(view.mLayerROFOffsets[1], 3); + BOOST_CHECK_EQUAL(view.mLayerROFOffsets[2], 7); + + // by default all rofs are disabled + for (int rof{0}; rof < 3; ++rof) { + BOOST_CHECK(!view.isROFEnabled(0, rof)); + } + for (int rof{0}; rof < 4; ++rof) { + BOOST_CHECK(!view.isROFEnabled(1, rof)); + } + + mask.selectROF({110, 20}); + + BOOST_CHECK(!view.isROFEnabled(0, 0)); + BOOST_CHECK(view.isROFEnabled(0, 1)); + BOOST_CHECK(!view.isROFEnabled(0, 2)); + + BOOST_CHECK(!view.isROFEnabled(1, 0)); + BOOST_CHECK(view.isROFEnabled(1, 1)); + BOOST_CHECK(view.isROFEnabled(1, 2)); + BOOST_CHECK(!view.isROFEnabled(1, 3)); +} + +// ROFOverlapTable +BOOST_AUTO_TEST_CASE(rofoverlap_basic) +{ + // define 2 layers with the same definitions (no staggering) + o2::its::ROFOverlapTable<2> table; + table.defineLayer(0, 12, 594, 0, 0, 0); + table.defineLayer(1, 12, 594, 0, 0, 0); + table.init(); + const auto view = table.getView(); + // each rof in layer 0 should be compatible with its layer 1 equivalent + for (int rof{0}; rof < 12; ++rof) { + BOOST_CHECK(view.doROFsOverlap(0, rof, 1, rof)); + BOOST_CHECK(view.doROFsOverlap(1, rof, 0, rof)); + BOOST_CHECK(view.getOverlap(0, 1, rof).getEntries() == 1); + } +} + +BOOST_AUTO_TEST_CASE(rofoverlap_staggered) +{ + // test staggered layers with ROF delay + o2::its::ROFOverlapTable<2> table; + table.defineLayer(0, 10, 500, 0, 0, 0); + table.defineLayer(1, 10, 500, 250, 0, 0); // 250 BC delay + table.init(); + const auto view = table.getView(); + + // verify overlap range + { // from 0 to 1 + const auto& range = view.getOverlap(0, 1, 0); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 1 to 0 + const auto& range = view.getOverlap(1, 0, 0); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } +} + +BOOST_AUTO_TEST_CASE(rofoverlap_staggered_pp) +{ + const uint32_t rofLen{198}, rofBins{6}; + const uint32_t rofDelay{rofLen / rofBins}; + o2::its::ROFOverlapTable<3> table; + for (uint32_t lay{0}; lay < 3; ++lay) { + table.defineLayer(lay, 6, rofLen, lay * rofDelay, 0, 0); + } + table.init(); + const auto view = table.getView(); + view.printAll(); +} + +BOOST_AUTO_TEST_CASE(rofoverlap_staggered_alllayers) +{ + // test staggered layers with ROF delay + o2::its::ROFOverlapTable<3> table; + table.defineLayer(0, 2, 3, 0, 0, 0); + table.defineLayer(1, 3, 2, 0, 0, 0); + table.defineLayer(2, 6, 1, 0, 0, 0); + table.init(); + const auto view = table.getView(); + // verify overlap range + { // from 0 to 1 rof=0 + const auto& range = view.getOverlap(0, 1, 0); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 0 to 2 rof=0 + const auto& range = view.getOverlap(0, 2, 0); + BOOST_CHECK_EQUAL(range.getEntries(), 3); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 0 to 1 rof=1 + const auto& range = view.getOverlap(0, 1, 1); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 1); + } + { // from 0 to 2 rof=1 + const auto& range = view.getOverlap(0, 2, 1); + BOOST_CHECK_EQUAL(range.getEntries(), 3); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 3); + } + { // from 1 to 2 rof=0 + const auto& range = view.getOverlap(1, 2, 0); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 1 to 0 rof=0 + const auto& range = view.getOverlap(1, 0, 0); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 1 to 2 rof=1 + const auto& range = view.getOverlap(1, 2, 1); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 2); + } + { // from 1 to 0 rof=1 + const auto& range = view.getOverlap(1, 0, 1); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 1 to 2 rof=2 + const auto& range = view.getOverlap(1, 2, 2); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 4); + } + { // from 1 to 0 rof=2 + const auto& range = view.getOverlap(1, 0, 2); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 1); + } + { // from 2 to 1 rof=0 + const auto& range = view.getOverlap(2, 1, 0); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 2 to 1 rof=1 + const auto& range = view.getOverlap(2, 1, 1); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 2 to 1 rof=2 + const auto& range = view.getOverlap(2, 1, 2); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 1); + } + { // from 2 to 1 rof=3 + const auto& range = view.getOverlap(2, 1, 3); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 1); + } + { // from 2 to 1 rof=4 + const auto& range = view.getOverlap(2, 1, 4); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 2); + } + { // from 2 to 1 rof=5 + const auto& range = view.getOverlap(2, 1, 5); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 2); + } + { // from 2 to 0 rof=0 + const auto& range = view.getOverlap(2, 0, 0); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 2 to 0 rof=1 + const auto& range = view.getOverlap(2, 0, 1); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 2 to 0 rof=2 + const auto& range = view.getOverlap(2, 0, 2); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 2 to 0 rof=3 + const auto& range = view.getOverlap(2, 0, 3); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 1); + } + { // from 2 to 0 rof=4 + const auto& range = view.getOverlap(2, 0, 4); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 1); + } + { // from 2 to 0 rof=5 + const auto& range = view.getOverlap(2, 0, 5); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 1); + } +} + +BOOST_AUTO_TEST_CASE(rofoverlap_staggered_alllayers_delay_delta) +{ + // test staggered layers with ROF delay + o2::its::ROFOverlapTable<3> table; + table.defineLayer(0, 2, 3, 0, 0, 0); + table.defineLayer(1, 3, 2, 1, 0, 0); + table.defineLayer(2, 6, 1, 0, 0, 1); + table.init(); + const auto view = table.getView(); + + // verify overlap range + { // from 0 to 1 rof=0 + const auto& range = view.getOverlap(0, 1, 0); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 0 to 2 rof=0 + const auto& range = view.getOverlap(0, 2, 0); + BOOST_CHECK_EQUAL(range.getEntries(), 4); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 0 to 1 rof=1 + const auto& range = view.getOverlap(0, 1, 1); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 1); + } + { // from 0 to 2 rof=1 + const auto& range = view.getOverlap(0, 2, 1); + BOOST_CHECK_EQUAL(range.getEntries(), 4); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 2); + } + { // from 1 to 2 rof=0 + const auto& range = view.getOverlap(1, 2, 0); + BOOST_CHECK_EQUAL(range.getEntries(), 4); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 1 to 0 rof=0 + const auto& range = view.getOverlap(1, 0, 0); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 1 to 2 rof=1 + const auto& range = view.getOverlap(1, 2, 1); + BOOST_CHECK_EQUAL(range.getEntries(), 4); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 2); + } + { // from 1 to 0 rof=1 + const auto& range = view.getOverlap(1, 0, 1); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 1); + } + { // from 1 to 2 rof=2 + const auto& range = view.getOverlap(1, 2, 2); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 4); + } + { // from 1 to 0 rof=2 + const auto& range = view.getOverlap(1, 0, 2); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 1); + } + { // from 2 to 1 rof=0 + const auto& range = view.getOverlap(2, 1, 0); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 2 to 1 rof=1 + const auto& range = view.getOverlap(2, 1, 1); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 2 to 1 rof=2 + const auto& range = view.getOverlap(2, 1, 2); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 2 to 1 rof=3 + const auto& range = view.getOverlap(2, 1, 3); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 2 to 1 rof=4 + const auto& range = view.getOverlap(2, 1, 4); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 1); + } + { // from 2 to 1 rof=5 + const auto& range = view.getOverlap(2, 1, 5); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 1); + } + { // from 2 to 0 rof=0 + const auto& range = view.getOverlap(2, 0, 0); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 2 to 0 rof=1 + const auto& range = view.getOverlap(2, 0, 1); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 2 to 0 rof=2 + const auto& range = view.getOverlap(2, 0, 2); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 2 to 0 rof=3 + const auto& range = view.getOverlap(2, 0, 3); + BOOST_CHECK_EQUAL(range.getEntries(), 2); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 0); + } + { // from 2 to 0 rof=4 + const auto& range = view.getOverlap(2, 0, 4); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 1); + } + { // from 2 to 0 rof=5 + const auto& range = view.getOverlap(2, 0, 5); + BOOST_CHECK_EQUAL(range.getEntries(), 1); + BOOST_CHECK_EQUAL(range.getFirstEntry(), 1); + } +} + +BOOST_AUTO_TEST_CASE(rofoverlap_with_delta) +{ + // test with ROF delta for compatibility window + o2::its::ROFOverlapTable<2> table; + table.defineLayer(0, 8, 600, 0, 0, 100); // +/- 100 BC delta + table.defineLayer(1, 8, 600, 0, 0, 100); + table.init(); + const auto view = table.getView(); + + // with delta, ROFs should have wider compatibility + for (int rof{0}; rof < 8; ++rof) { + auto overlap = view.getOverlap(0, 1, rof); + if (rof == 0 || rof == 7) { + // edges should see only two + BOOST_CHECK_EQUAL(overlap.getEntries(), 2); + } else { + BOOST_CHECK_EQUAL(overlap.getEntries(), 3); + } + } +} + +BOOST_AUTO_TEST_CASE(rofoverlap_same_layer) +{ + // test same layer compatibility + o2::its::ROFOverlapTable<1> table; + table.defineLayer(0, 10, 500, 0, 0, 0); + table.init(); + const auto view = table.getView(); + + // same ROF in same layer should be compatible + BOOST_CHECK(view.doROFsOverlap(0, 5, 0, 5)); + // different ROFs in same layer should not be compatible + BOOST_CHECK(!view.doROFsOverlap(0, 5, 0, 6)); +} + +BOOST_AUTO_TEST_CASE(rofoverlap_timestamp_basic) +{ + o2::its::ROFOverlapTable<4> table; + table.defineLayer(0, 4, 100, 0, 0, 0); + table.defineLayer(1, 4, 100, 0, 0, 0); + table.defineLayer(2, 8, 50, 0, 0, 0); + table.defineLayer(3, 7, 50, 50, 0, 0); + table.init(); + const auto& view = table.getView(); + + const auto t01 = view.getTimeStamp(0, 3, 1, 3); + BOOST_CHECK_EQUAL(t01.getTimeStamp(), 300); + BOOST_CHECK_EQUAL(t01.getTimeStampError(), 100); + + const auto t02 = view.getTimeStamp(0, 1, 2, 3); + BOOST_CHECK_EQUAL(t02.getTimeStamp(), 150); + BOOST_CHECK_EQUAL(t02.getTimeStampError(), 50); + + const auto t03 = view.getTimeStamp(0, 0, 3, 0); + BOOST_CHECK_EQUAL(t03.getTimeStamp(), 50); + BOOST_CHECK_EQUAL(t03.getTimeStampError(), 50); + + const auto t23 = view.getTimeStamp(2, 2, 3, 1); + BOOST_CHECK_EQUAL(t23.getTimeStamp(), 100); + BOOST_CHECK_EQUAL(t23.getTimeStampError(), 50); +} + +BOOST_AUTO_TEST_CASE(rofoverlap_timestamp_complex) +{ + o2::its::ROFOverlapTable<4> table; + table.defineLayer(0, 4, 100, 0, 0, 0); + table.defineLayer(1, 4, 100, 0, 0, 10); + table.defineLayer(2, 8, 50, 0, 0, 0); + table.defineLayer(3, 7, 50, 50, 0, 10); + table.init(); + const auto& view = table.getView(); + view.printMapping(0, 1); + + const auto t010 = view.getTimeStamp(0, 3, 1, 3); + BOOST_CHECK_EQUAL(t010.getTimeStamp(), 300); + BOOST_CHECK_EQUAL(t010.getTimeStampError(), 100); + + const auto t011 = view.getTimeStamp(0, 2, 1, 3); + BOOST_CHECK_EQUAL(t011.getTimeStamp(), 290); + BOOST_CHECK_EQUAL(t011.getTimeStampError(), 10); + + const auto t02 = view.getTimeStamp(0, 1, 2, 3); + BOOST_CHECK_EQUAL(t02.getTimeStamp(), 150); + BOOST_CHECK_EQUAL(t02.getTimeStampError(), 50); + + const auto t03 = view.getTimeStamp(0, 0, 3, 0); + BOOST_CHECK_EQUAL(t03.getTimeStamp(), 40); + BOOST_CHECK_EQUAL(t03.getTimeStampError(), 60); +} + +// ROFVertexLookupTable +BOOST_AUTO_TEST_CASE(rofvertex_basic) +{ + o2::its::ROFVertexLookupTable<1> table; + table.defineLayer(0, 6, 594, 0, 0, 0); + table.init(); + std::vector vertices; + o2::its::Vertex vert0; + vert0.getTimeStamp().setTimeStamp(594); + vert0.getTimeStamp().setTimeStampError(594); + vertices.push_back(vert0); + o2::its::Vertex vert1; + vert1.getTimeStamp().setTimeStamp(2375); + vert1.getTimeStamp().setTimeStampError(594); + vertices.push_back(vert1); + table.update(vertices.data(), vertices.size()); + const auto view = table.getView(); +} + +BOOST_AUTO_TEST_CASE(rofvertex_init_with_vertices) +{ + o2::its::ROFVertexLookupTable<2> table; + table.defineLayer(0, 10, 500, 0, 0, 0); + table.defineLayer(1, 10, 500, 0, 0, 0); + + // create vertices at different timestamps + std::vector vertices; + for (int i = 0; i < 5; ++i) { + o2::its::Vertex v; + v.getTimeStamp().setTimeStamp(i * 1000); + v.getTimeStamp().setTimeStampError(500); + vertices.push_back(v); + } + + table.init(vertices.data(), vertices.size()); + const auto view = table.getView(); + + // verify vertices can be queried + const auto& vtxRange = view.getVertices(0, 0); + BOOST_CHECK_EQUAL(vtxRange.getEntries(), 1); +} + +BOOST_AUTO_TEST_CASE(rofvertex_max_vertices) +{ + o2::its::ROFVertexLookupTable<1> table; + table.defineLayer(0, 3, 1000, 0, 0, 500); + + std::vector vertices; + for (int i = 0; i < 10; ++i) { + o2::its::Vertex v; + v.getTimeStamp().setTimeStamp(500 + i * 100); + v.getTimeStamp().setTimeStampError(50); + vertices.push_back(v); + } + + table.init(vertices.data(), vertices.size()); + const auto view = table.getView(); + + int32_t maxVtx = view.getMaxVerticesPerROF(); + BOOST_CHECK(maxVtx >= 0); +} + +BOOST_AUTO_TEST_CASE(rofvertex_vertex_more) +{ + o2::its::ROFVertexLookupTable<4> table; + table.defineLayer(0, 4, 100, 0, 0, 0); + table.defineLayer(1, 4, 100, 0, 0, 10); + table.defineLayer(2, 8, 50, 0, 0, 0); + table.defineLayer(3, 7, 50, 50, 0, 10); + table.init(); + + std::vector vertices; + { // vertex 0 overlapping + auto& v = vertices.emplace_back(); + v.getTimeStamp().setTimeStamp(100); + v.getTimeStamp().setTimeStampError(10); + } + { // vertex 1 + auto& v = vertices.emplace_back(); + v.getTimeStamp().setTimeStamp(100); + v.getTimeStamp().setTimeStampError(0); + } + { // vertex 2 spanning multiple rofs + auto& v = vertices.emplace_back(); + v.getTimeStamp().setTimeStamp(100); + v.getTimeStamp().setTimeStampError(60); + } + + // sorty vertices by lower bound + std::sort(vertices.begin(), vertices.end(), [](const auto& pvA, const auto& pvB) { + const auto& a = pvA.getTimeStamp(); + const auto& b = pvB.getTimeStamp(); + const auto aLower = a.getTimeStamp() - a.getTimeStampError(); + const auto bLower = b.getTimeStamp() - b.getTimeStampError(); + if (aLower != bLower) { + return aLower < bLower; + } + return pvA.getNContributors() > pvB.getNContributors(); + }); + + table.update(vertices.data(), vertices.size()); + const auto& view = table.getView(); + + const auto& v0 = vertices[0]; // 100+60 + const auto& v1 = vertices[1]; // 100+10 + const auto& v2 = vertices[2]; // 100+0 + + // check for v0 + // layer 0 + BOOST_CHECK(!view.isVertexCompatible(0, 0, v0)); + BOOST_CHECK(view.isVertexCompatible(0, 1, v0)); + BOOST_CHECK(!view.isVertexCompatible(0, 2, v0)); + BOOST_CHECK(!view.isVertexCompatible(0, 3, v0)); + // layer 1 + BOOST_CHECK(view.isVertexCompatible(1, 0, v0)); + BOOST_CHECK(view.isVertexCompatible(1, 1, v0)); + BOOST_CHECK(!view.isVertexCompatible(1, 2, v0)); + BOOST_CHECK(!view.isVertexCompatible(1, 3, v0)); + // layer 2 + BOOST_CHECK(!view.isVertexCompatible(2, 0, v0)); + BOOST_CHECK(!view.isVertexCompatible(2, 1, v0)); + BOOST_CHECK(view.isVertexCompatible(2, 2, v0)); + BOOST_CHECK(view.isVertexCompatible(2, 3, v0)); + BOOST_CHECK(!view.isVertexCompatible(2, 4, v0)); + BOOST_CHECK(!view.isVertexCompatible(2, 5, v0)); + BOOST_CHECK(!view.isVertexCompatible(2, 6, v0)); + BOOST_CHECK(!view.isVertexCompatible(2, 7, v0)); + // layer 3 + BOOST_CHECK(view.isVertexCompatible(3, 0, v0)); + BOOST_CHECK(view.isVertexCompatible(3, 1, v0)); + BOOST_CHECK(view.isVertexCompatible(3, 2, v0)); + BOOST_CHECK(!view.isVertexCompatible(3, 3, v0)); + BOOST_CHECK(!view.isVertexCompatible(3, 4, v0)); + BOOST_CHECK(!view.isVertexCompatible(3, 5, v0)); + BOOST_CHECK(!view.isVertexCompatible(3, 6, v0)); + + // check for v1 + // layer 0 + BOOST_CHECK(!view.isVertexCompatible(0, 0, v1)); + BOOST_CHECK(view.isVertexCompatible(0, 1, v1)); + BOOST_CHECK(!view.isVertexCompatible(0, 2, v1)); + BOOST_CHECK(!view.isVertexCompatible(0, 3, v1)); + // layer 1 + BOOST_CHECK(view.isVertexCompatible(1, 0, v1)); + BOOST_CHECK(view.isVertexCompatible(1, 1, v1)); + BOOST_CHECK(!view.isVertexCompatible(1, 2, v1)); + BOOST_CHECK(!view.isVertexCompatible(1, 3, v1)); + // layer 2 + BOOST_CHECK(!view.isVertexCompatible(2, 0, v1)); + BOOST_CHECK(!view.isVertexCompatible(2, 1, v1)); + BOOST_CHECK(view.isVertexCompatible(2, 2, v1)); + BOOST_CHECK(!view.isVertexCompatible(2, 3, v1)); + BOOST_CHECK(!view.isVertexCompatible(2, 4, v1)); + BOOST_CHECK(!view.isVertexCompatible(2, 5, v1)); + BOOST_CHECK(!view.isVertexCompatible(2, 6, v1)); + BOOST_CHECK(!view.isVertexCompatible(2, 7, v1)); + // layer 3 + BOOST_CHECK(view.isVertexCompatible(3, 0, v1)); + BOOST_CHECK(view.isVertexCompatible(3, 1, v1)); + BOOST_CHECK(!view.isVertexCompatible(3, 2, v1)); + BOOST_CHECK(!view.isVertexCompatible(3, 3, v1)); + BOOST_CHECK(!view.isVertexCompatible(3, 4, v1)); + BOOST_CHECK(!view.isVertexCompatible(3, 5, v1)); + BOOST_CHECK(!view.isVertexCompatible(3, 6, v1)); + + // check for v2 + // layer 0 + BOOST_CHECK(!view.isVertexCompatible(0, 0, v2)); + BOOST_CHECK(view.isVertexCompatible(0, 1, v2)); + BOOST_CHECK(!view.isVertexCompatible(0, 2, v2)); + BOOST_CHECK(!view.isVertexCompatible(0, 3, v2)); + // layer 1 + BOOST_CHECK(view.isVertexCompatible(1, 0, v2)); + BOOST_CHECK(view.isVertexCompatible(1, 1, v2)); + BOOST_CHECK(!view.isVertexCompatible(1, 2, v2)); + BOOST_CHECK(!view.isVertexCompatible(1, 3, v2)); + // layer 2 + BOOST_CHECK(!view.isVertexCompatible(2, 0, v2)); + BOOST_CHECK(!view.isVertexCompatible(2, 1, v2)); + BOOST_CHECK(view.isVertexCompatible(2, 2, v2)); + BOOST_CHECK(!view.isVertexCompatible(2, 3, v2)); + BOOST_CHECK(!view.isVertexCompatible(2, 4, v2)); + BOOST_CHECK(!view.isVertexCompatible(2, 5, v2)); + BOOST_CHECK(!view.isVertexCompatible(2, 6, v2)); + BOOST_CHECK(!view.isVertexCompatible(2, 7, v2)); + // layer 3 + BOOST_CHECK(view.isVertexCompatible(3, 0, v2)); + BOOST_CHECK(view.isVertexCompatible(3, 1, v2)); + BOOST_CHECK(!view.isVertexCompatible(3, 2, v2)); + BOOST_CHECK(!view.isVertexCompatible(3, 3, v2)); + BOOST_CHECK(!view.isVertexCompatible(3, 4, v2)); + BOOST_CHECK(!view.isVertexCompatible(3, 5, v2)); + BOOST_CHECK(!view.isVertexCompatible(3, 6, v2)); +} + +BOOST_AUTO_TEST_CASE(rofvertex_exact_compatibility) +{ + o2::its::ROFVertexLookupTable<4> table; + table.defineLayer(0, 4, 100, 0, 0, 0); + table.defineLayer(1, 4, 100, 0, 0, 10); + table.defineLayer(2, 8, 50, 0, 0, 0); + table.defineLayer(3, 7, 50, 50, 0, 10); + table.init(); + + // sorted by lower bound timestamp + std::vector vertices; + { // idx 0: [40, 160] - wide span + auto& v = vertices.emplace_back(); + v.getTimeStamp().setTimeStamp(100); + v.getTimeStamp().setTimeStampError(60); + } + { // idx 1: [90, 110] + auto& v = vertices.emplace_back(); + v.getTimeStamp().setTimeStamp(100); + v.getTimeStamp().setTimeStampError(10); + } + { // idx 2: [100, 100] - zero width, false-positive prone + auto& v = vertices.emplace_back(); + v.getTimeStamp().setTimeStamp(100); + v.getTimeStamp().setTimeStampError(0); + } + + table.update(vertices.data(), vertices.size()); + const auto& view = table.getView(); + + // Layer 0 ROF 0: [0, 100) + BOOST_CHECK(!view.isVertexCompatible(0, 0, vertices[0])); + BOOST_CHECK(!view.isVertexCompatible(0, 0, vertices[1])); + BOOST_CHECK(!view.isVertexCompatible(0, 0, vertices[2])); + + // Layer 0 ROF 1: [100, 200) - range includes idx 2 as false positive + { + const auto& range = view.getVertices(0, 1); + BOOST_CHECK_EQUAL(range.getEntries(), 3); // superset + + size_t exactCount = 0; + for (size_t i = range.getFirstEntry(); i < range.getEntriesBound(); ++i) { + if (view.isVertexCompatible(0, 1, vertices[i])) { + ++exactCount; + } + } + // BOOST_CHECK_EQUAL(exactCount, 2); // idx 2 filtered out + } + + // Layer 0 ROF 2: [200, 300) - nothing overlaps + BOOST_CHECK(!view.isVertexCompatible(0, 2, vertices[0])); + BOOST_CHECK(!view.isVertexCompatible(0, 2, vertices[1])); + BOOST_CHECK(!view.isVertexCompatible(0, 2, vertices[2])); + + // Layer 2 ROF 0: [0, 50) - only idx 0 + BOOST_CHECK(!view.isVertexCompatible(2, 0, vertices[0])); + BOOST_CHECK(!view.isVertexCompatible(2, 0, vertices[1])); + + // Layer 2 ROF 1: [50, 100) - idx 0 and 1 + BOOST_CHECK(!view.isVertexCompatible(2, 1, vertices[0])); + BOOST_CHECK(!view.isVertexCompatible(2, 1, vertices[1])); + BOOST_CHECK(!view.isVertexCompatible(2, 1, vertices[2])); + + // Layer 2 ROF 3: [150, 200) - only idx 0 + BOOST_CHECK(view.isVertexCompatible(2, 3, vertices[0])); + BOOST_CHECK(!view.isVertexCompatible(2, 3, vertices[1])); + + // Layer 3 ROF 0: [40, 110) - all three genuine + BOOST_CHECK(view.isVertexCompatible(3, 0, vertices[0])); + BOOST_CHECK(view.isVertexCompatible(3, 0, vertices[1])); + BOOST_CHECK(view.isVertexCompatible(3, 0, vertices[2])); + + // Layer 3 ROF 2: [140, 210) - only idx 0 + BOOST_CHECK(view.isVertexCompatible(3, 2, vertices[0])); + BOOST_CHECK(!view.isVertexCompatible(3, 2, vertices[1])); + BOOST_CHECK(!view.isVertexCompatible(3, 2, vertices[2])); +} diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClusterWriterWorkflow.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClusterWriterWorkflow.h index 15c22f9bcf23d..a91038b32a1c1 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClusterWriterWorkflow.h +++ b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClusterWriterWorkflow.h @@ -23,7 +23,7 @@ namespace its namespace cluster_writer_workflow { -framework::WorkflowSpec getWorkflow(bool useMC); +framework::WorkflowSpec getWorkflow(bool useMC, bool doStag); } } // namespace its diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/DCSAdaposParserSpec.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/DCSAdaposParserSpec.h index bcc19ff15b85d..808fef81b586f 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/DCSAdaposParserSpec.h +++ b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/DCSAdaposParserSpec.h @@ -37,7 +37,7 @@ #include "DetectorsDCS/DataPointIdentifier.h" #include "DetectorsDCS/DataPointValue.h" #include "DetectorsDCS/DataPointCompositeObject.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "CCDB/BasicCCDBManager.h" using namespace o2::framework; diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/RecoWorkflow.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/RecoWorkflow.h index 1d5d829a6f79a..bfbde0093d55d 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/RecoWorkflow.h +++ b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/RecoWorkflow.h @@ -26,7 +26,7 @@ namespace its namespace reco_workflow { -framework::WorkflowSpec getWorkflow(bool useMC, TrackingMode::Type trmode, const bool overrideBeamPosition = false, +framework::WorkflowSpec getWorkflow(bool useMC, bool doStag, 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); } diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackReaderSpec.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackReaderSpec.h index 8666864ca1ae9..f4bcba750723f 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackReaderSpec.h +++ b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackReaderSpec.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. // @@ -14,28 +14,26 @@ #ifndef O2_ITS_TRACKREADER #define O2_ITS_TRACKREADER -#include "TFile.h" -#include "TTree.h" +#include +#include #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" #include "Headers/DataHeader.h" -#include "ITStracking/Definitions.h" +#include "DataFormatsITSMFT/ROFRecord.h" #include "DataFormatsITS/TrackITS.h" +#include "DataFormatsITS/Vertex.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" -#include "DataFormatsITSMFT/ROFRecord.h" -namespace o2 -{ -namespace its +namespace o2::its { -class TrackReader : public o2::framework::Task +class TrackReader final : public o2::framework::Task { public: - TrackReader(bool useMC = true); - ~TrackReader() override = default; + TrackReader(bool useMC = true) : mUseMC(useMC) {} + ~TrackReader() final = default; void init(o2::framework::InitContext& ic) final; void run(o2::framework::ProcessingContext& pc) final; @@ -43,9 +41,9 @@ class TrackReader : public o2::framework::Task void connectTree(const std::string& filename); std::vector mROFRec, *mROFRecInp = &mROFRec; - std::vector mVerticesROFRec, *mVerticesROFRecInp = &mVerticesROFRec; std::vector mTracks, *mTracksInp = &mTracks; std::vector mVertices, *mVerticesInp = &mVertices; + std::vector mVerticesROFRec, *mVerticesROFRecInp = &mVerticesROFRec; std::vector mClusInd, *mClusIndInp = &mClusInd; std::vector mMCTruth, *mMCTruthInp = &mMCTruth; std::vector mMCVertTruth, *mMCVTruthInp = &mMCTruth; @@ -56,7 +54,7 @@ class TrackReader : public o2::framework::Task std::unique_ptr mFile; std::unique_ptr mTree; - std::string mInputFileName = ""; + std::string mInputFileName; std::string mTrackTreeName = "o2sim"; std::string mROFBranchName = "ITSTracksROF"; std::string mTrackBranchName = "ITSTrack"; @@ -71,7 +69,6 @@ class TrackReader : public o2::framework::Task /// read ITS track data from a root file framework::DataProcessorSpec getITSTrackReaderSpec(bool useMC = true); -} // namespace its -} // namespace o2 +} // namespace o2::its #endif /* O2_ITS_TRACKREADER */ diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackerSpec.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackerSpec.h index 01eb7cb7b69aa..8ce63efcb7a3b 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackerSpec.h +++ b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/TrackerSpec.h @@ -42,6 +42,7 @@ class TrackerDPL : public framework::Task public: TrackerDPL(std::shared_ptr gr, bool isMC, + bool doStag, int trgType, const TrackingMode::Type trMode = TrackingMode::Unset, const bool overrBeamEst = false, @@ -63,7 +64,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 doStag, bool useGeom, int useTrig, TrackingMode::Type trMode, const bool overrBeamEst = false, o2::gpu::gpudatatypes::DeviceType dType = o2::gpu::gpudatatypes::DeviceType::CPU); } // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/VertexReaderSpec.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/VertexReaderSpec.h index b300967408256..10ee70eeafeea 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/VertexReaderSpec.h +++ b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/VertexReaderSpec.h @@ -19,8 +19,8 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" -#include "ITStracking/Definitions.h" #include "DataFormatsITSMFT/ROFRecord.h" +#include "DataFormatsITS/Vertex.h" namespace o2 { diff --git a/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterWorkflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterWorkflow.cxx index aba468b3e9460..35c911f856436 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterWorkflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterWorkflow.cxx @@ -22,11 +22,11 @@ namespace its namespace cluster_writer_workflow { -framework::WorkflowSpec getWorkflow(bool useMC) +framework::WorkflowSpec getWorkflow(bool useMC, bool doStag) { framework::WorkflowSpec specs; - specs.emplace_back(o2::itsmft::getITSClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getITSClusterWriterSpec(useMC, doStag)); return specs; } diff --git a/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx index 9f8cb6c83ef99..5da4b080995b5 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx @@ -27,7 +27,7 @@ namespace o2::its::reco_workflow { -framework::WorkflowSpec getWorkflow(bool useMC, +framework::WorkflowSpec getWorkflow(bool useMC, bool doStag, TrackingMode::Type trmode, const bool overrideBeamPosition, bool upstreamDigits, @@ -40,13 +40,13 @@ framework::WorkflowSpec getWorkflow(bool useMC, { framework::WorkflowSpec specs; if (!(upstreamDigits || upstreamClusters)) { - specs.emplace_back(o2::itsmft::getITSDigitReaderSpec(useMC, false, true, "itsdigits.root")); + specs.emplace_back(o2::itsmft::getITSDigitReaderSpec(useMC, doStag, false, true, "itsdigits.root")); } if (!upstreamClusters) { - specs.emplace_back(o2::itsmft::getITSClustererSpec(useMC)); + specs.emplace_back(o2::itsmft::getITSClustererSpec(useMC, doStag)); } if (!disableRootOutput) { - specs.emplace_back(o2::itsmft::getITSClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getITSClusterWriterSpec(useMC, doStag)); } if ((trmode != TrackingMode::Off) && (TrackerParamConfig::Instance().trackingMode != TrackingMode::Off)) { if (useGPUWF) { @@ -54,6 +54,7 @@ framework::WorkflowSpec getWorkflow(bool useMC, .itsTriggerType = useTrig, .processMC = useMC, .runITSTracking = true, + .itsStaggered = doStag, .itsOverrBeamEst = overrideBeamPosition, }; @@ -78,7 +79,7 @@ framework::WorkflowSpec getWorkflow(bool useMC, .algorithm = AlgorithmSpec{adoptTask(task)}, .options = taskOptions}); } else { - specs.emplace_back(o2::its::getTrackerSpec(useMC, useGeom, useTrig, trmode, overrideBeamPosition, dtype)); + specs.emplace_back(o2::its::getTrackerSpec(useMC, doStag, useGeom, useTrig, trmode, overrideBeamPosition, dtype)); } if (!disableRootOutput) { specs.emplace_back(o2::its::getTrackWriterSpec(useMC)); diff --git a/Detectors/ITSMFT/ITS/workflow/src/TrackReaderSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/TrackReaderSpec.cxx index 8e72faae9fd37..2f081a11c28b9 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/TrackReaderSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/TrackReaderSpec.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. // @@ -15,21 +15,14 @@ #include #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" +#include "CommonUtils/StringUtils.h" #include "ITSWorkflow/TrackReaderSpec.h" -#include "CommonUtils/NameConf.h" using namespace o2::framework; using namespace o2::its; -namespace o2 +namespace o2::its { -namespace its -{ - -TrackReader::TrackReader(bool useMC) -{ - mUseMC = useMC; -} void TrackReader::init(InitContext& ic) { @@ -43,7 +36,7 @@ void TrackReader::run(ProcessingContext& pc) auto ent = mTree->GetReadEntry() + 1; assert(ent < mTree->GetEntries()); // this should not happen mTree->GetEntry(ent); - LOG(info) << "Pushing " << mTracks.size() << " track in " << mROFRec.size() << " ROFs at entry " << ent; + LOG(info) << "Pushing " << mTracks.size() << " track at entry " << ent; pc.outputs().snapshot(Output{mOrigin, "ITSTrackROF", 0}, mROFRec); pc.outputs().snapshot(Output{mOrigin, "TRACKS", 0}, mTracks); pc.outputs().snapshot(Output{mOrigin, "TRACKCLSID", 0}, mClusInd); @@ -77,12 +70,6 @@ void TrackReader::connectTree(const std::string& filename) } else { mTree->SetBranchAddress(mVertexBranchName.c_str(), &mVerticesInp); } - if (!mTree->GetBranch(mVertexROFBranchName.c_str())) { - LOG(warning) << "No " << mVertexROFBranchName << " branch in " << mTrackTreeName - << " -> vertices ROFrecords will be empty"; - } else { - mTree->SetBranchAddress(mVertexROFBranchName.c_str(), &mVerticesROFRecInp); - } if (mUseMC) { if (mTree->GetBranch(mTrackMCTruthBranchName.c_str())) { mTree->SetBranchAddress(mTrackMCTruthBranchName.c_str(), &mMCTruthInp); @@ -107,14 +94,13 @@ DataProcessorSpec getITSTrackReaderSpec(bool useMC) } return DataProcessorSpec{ - "its-track-reader", - Inputs{}, - outputSpec, - AlgorithmSpec{adaptFromTask(useMC)}, - Options{ + .name = "its-track-reader", + .inputs = Inputs{}, + .outputs = outputSpec, + .algorithm = AlgorithmSpec{adaptFromTask(useMC)}, + .options = Options{ {"its-tracks-infile", VariantType::String, "o2trac_its.root", {"Name of the input track file"}}, {"input-dir", VariantType::String, "none", {"Input directory"}}}}; } -} // namespace its -} // namespace o2 +} // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/workflow/src/TrackWriterSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/TrackWriterSpec.cxx index c10b4aa32f054..84f43ee148302 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/TrackWriterSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/TrackWriterSpec.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. // @@ -19,8 +19,7 @@ #include "DataFormatsITSMFT/ROFRecord.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" -#include "ITStracking/Definitions.h" -#include "ITStracking/TrackingConfigParam.h" +#include "DataFormatsITS/Vertex.h" using namespace o2::framework; @@ -39,8 +38,7 @@ DataProcessorSpec getTrackWriterSpec(bool useMC) { // Spectators for logging // this is only to restore the original behavior - const auto writeContLabels = VertexerParamConfig::Instance().outputContLabels && useMC; - auto tracksSize = std::make_shared(0); + auto tracksSize = std::make_shared(0); auto tracksSizeGetter = [tracksSize](std::vector const& tracks) { *tracksSize = tracks.size(); }; @@ -57,11 +55,11 @@ DataProcessorSpec getTrackWriterSpec(bool useMC) "ITSTrackClusIdx"}, BranchDefinition>{InputSpec{"vertices", "ITS", "VERTICES", 0}, "Vertices"}, - BranchDefinition>{InputSpec{"vtxROF", "ITS", "VERTICESROF", 0}, - "VerticesROF"}, BranchDefinition>{InputSpec{"ROframes", "ITS", "ITSTrackROF", 0}, "ITSTracksROF", logger}, + BranchDefinition>{InputSpec{"vtxROF", "ITS", "VERTICESROF", 0}, + "VerticesROF"}, BranchDefinition{InputSpec{"labels", "ITS", "TRACKSMCTR", 0}, "ITSTrackMCTruth", (useMC ? 1 : 0), // one branch if mc labels enabled @@ -70,15 +68,6 @@ DataProcessorSpec getTrackWriterSpec(bool useMC) "ITSVertexMCTruth", (useMC ? 1 : 0), // one branch if mc labels enabled ""}, - BranchDefinition{InputSpec{"labelsVerticesContributors", "ITS", "VERTICESMCTRCONT", 0}, - "ITSVertexMCTruthCont", - (writeContLabels ? 1 : 0), // one branch if - // requested - ""}, - BranchDefinition{InputSpec{"MC2ROframes", "ITS", "ITSTrackMC2ROF", 0}, - "ITSTracksMC2ROF", - (useMC ? 1 : 0), // one branch if mc labels enabled - ""}, BranchDefinition>{InputSpec{"purityVertices", "ITS", "VERTICESMCPUR", 0}, "ITSVertexMCPurity", (useMC ? 1 : 0), // one branch if mc labels enabled ""})(); diff --git a/Detectors/ITSMFT/ITS/workflow/src/TrackWriterWorkflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/TrackWriterWorkflow.cxx index ae2cb3648ec86..ce1d238188ec5 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/TrackWriterWorkflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/TrackWriterWorkflow.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. // @@ -31,4 +31,4 @@ framework::WorkflowSpec getWorkflow(bool useMC) } // namespace track_writer_workflow } // namespace its -} // namespace o2 \ No newline at end of file +} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx index 3d07048aaf1e6..932c82c2d1ca4 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/TrackerSpec.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. // @@ -15,6 +15,7 @@ #include "Framework/ConfigParamRegistry.h" #include "Framework/CCDBParamSpec.h" #include "Framework/DeviceSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "ITSWorkflow/TrackerSpec.h" #include "ITStracking/Definitions.h" #include "ITStracking/TrackingConfigParam.h" @@ -26,12 +27,13 @@ namespace its { TrackerDPL::TrackerDPL(std::shared_ptr gr, bool isMC, + bool doStag, int trgType, const TrackingMode::Type trMode, const bool overrBeamEst, o2::gpu::gpudatatypes::DeviceType dType) : mGGCCDBRequest(gr), mRecChain{o2::gpu::GPUReconstruction::CreateInstance(dType, true)}, - mITSTrackingInterface{isMC, trgType, overrBeamEst} + mITSTrackingInterface{isMC, doStag, trgType, overrBeamEst} { mITSTrackingInterface.setTrackingMode(trMode); } @@ -87,13 +89,18 @@ 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 doStag, bool useGeom, int trgType, TrackingMode::Type trMode, const bool overrBeamEst, o2::gpu::gpudatatypes::DeviceType dType) { + const int mLayers = doStag ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; 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); + for (int iLayer = 0; iLayer < mLayers; ++iLayer) { + inputs.emplace_back("compClusters", "ITS", "COMPCLUSTERS", iLayer, Lifetime::Timeframe); + inputs.emplace_back("patterns", "ITS", "PATTERNS", iLayer, Lifetime::Timeframe); + inputs.emplace_back("ROframes", "ITS", "CLUSTERSROF", iLayer, Lifetime::Timeframe); + if (useMC) { + inputs.emplace_back("itsmclabels", "ITS", "CLUSTERSMCTR", iLayer, Lifetime::Timeframe); + } + } if (trgType == 1) { inputs.emplace_back("phystrig", "ITS", "PHYSTRIG", 0, Lifetime::Timeframe); } else if (trgType == 2) { @@ -123,30 +130,24 @@ DataProcessorSpec getTrackerSpec(bool useMC, bool useGeom, int trgType, Tracking 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("itsmclabels", "ITS", "CLUSTERSMCTR", 0, Lifetime::Timeframe); - inputs.emplace_back("ITSMC2ROframes", "ITS", "CLUSTERSMC2ROF", 0, Lifetime::Timeframe); outputs.emplace_back("ITS", "VERTICESMCTR", 0, Lifetime::Timeframe); outputs.emplace_back("ITS", "VERTICESMCPUR", 0, Lifetime::Timeframe); outputs.emplace_back("ITS", "TRACKSMCTR", 0, Lifetime::Timeframe); - outputs.emplace_back("ITS", "ITSTrackMC2ROF", 0, Lifetime::Timeframe); - if (VertexerParamConfig::Instance().outputContLabels) { - outputs.emplace_back("ITS", "VERTICESMCTRCONT", 0, Lifetime::Timeframe); - } } return DataProcessorSpec{ - "its-tracker", - inputs, - outputs, - AlgorithmSpec{adaptFromTask(ggRequest, - useMC, - trgType, - trMode, - overrBeamEst, - dType)}, - Options{}}; + .name = "its-tracker", + .inputs = inputs, + .outputs = outputs, + .algorithm = AlgorithmSpec{adaptFromTask(ggRequest, + useMC, + doStag, + trgType, + trMode, + overrBeamEst, + dType)}, + .options = Options{}}; } } // namespace its diff --git a/Detectors/ITSMFT/ITS/workflow/src/its-cluster-reader-workflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/its-cluster-reader-workflow.cxx index da843526f9296..cbbb4bea09f4b 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/its-cluster-reader-workflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/its-cluster-reader-workflow.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. // @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include "Framework/ConfigParamSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "CommonUtils/ConfigurableParam.h" #include "DetectorsRaw/HBFUtilsInitializer.h" #include "Framework/CallbacksPolicy.h" @@ -47,6 +48,7 @@ void customize(std::vector& workflowOptions) VariantType::String, "", {"Semicolon separated key=value strings"}}); + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(workflowOptions); o2::raw::HBFUtilsInitializer::addConfigOption(workflowOptions); } @@ -60,8 +62,9 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cc) auto withTriggers = !cc.options().get("suppress-triggers-output"); auto withMC = cc.options().get("with-mc"); auto withPatterns = !cc.options().get("without-patterns"); + auto doStag = o2::itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(cc); - specs.emplace_back(o2::itsmft::getITSClusterReaderSpec(withMC, withPatterns, withTriggers)); + specs.emplace_back(o2::itsmft::getITSClusterReaderSpec(withMC, doStag, withPatterns, withTriggers)); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(cc, specs); diff --git a/Detectors/ITSMFT/ITS/workflow/src/its-cluster-writer-workflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/its-cluster-writer-workflow.cxx index ad3d8eea6e636..c10a1659d5f76 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/its-cluster-writer-workflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/its-cluster-writer-workflow.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. // @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include "ITSWorkflow/ClusterWriterWorkflow.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "Framework/ConfigParamSpec.h" #include "Framework/CompletionPolicyHelpers.h" @@ -29,13 +30,14 @@ void customize(std::vector& workflowOptions) o2::framework::VariantType::Bool, false, {"disable MC propagation even if available"}}); + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(workflowOptions); } #include "Framework/runDataProcessing.h" -#include "Framework/Logger.h" WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { auto useMC = !configcontext.options().get("disable-mc"); - return std::move(o2::its::cluster_writer_workflow::getWorkflow(useMC)); + auto doStag = o2::itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(configcontext); + return std::move(o2::its::cluster_writer_workflow::getWorkflow(useMC, doStag)); } diff --git a/Detectors/ITSMFT/ITS/workflow/src/its-reco-workflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/its-reco-workflow.cxx index 8080883888d40..bdade0effcbf0 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/its-reco-workflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/its-reco-workflow.cxx @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include "ITSWorkflow/RecoWorkflow.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "CommonUtils/ConfigurableParam.h" #include "ITStracking/Configuration.h" #include "DetectorsRaw/HBFUtilsInitializer.h" @@ -50,6 +51,7 @@ void customize(std::vector& workflowOptions) {"use-full-geometry", o2::framework::VariantType::Bool, false, {"use full geometry instead of the light-weight ITS part"}}, {"use-gpu-workflow", o2::framework::VariantType::Bool, false, {"use GPU workflow (default: false)"}}, {"gpu-device", o2::framework::VariantType::Int, 1, {"use gpu device: CPU=1,CUDA=2,HIP=3 (default: CPU)"}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -72,6 +74,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) auto extClusters = configcontext.options().get("clusters-from-upstream"); auto disableRootOutput = configcontext.options().get("disable-root-output"); auto useGeom = configcontext.options().get("use-full-geometry"); + auto doStag = o2::itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(configcontext); if (configcontext.options().get("disable-tracking")) { trmode = "off"; } @@ -87,16 +90,18 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) LOG(fatal) << "Unknown trigger type requested for events prescaling: " << selTrig; } } - auto wf = o2::its::reco_workflow::getWorkflow(useMC, - o2::its::TrackingMode::fromString(trmode), - beamPosOVerride, - extDigits, - extClusters, - disableRootOutput, - useGeom, - trType, - useGpuWF, - gpuDevice); + auto wf = o2::its::reco_workflow::getWorkflow( + useMC, + doStag, + o2::its::TrackingMode::fromString(trmode), + beamPosOVerride, + extDigits, + extClusters, + disableRootOutput, + useGeom, + trType, + useGpuWF, + gpuDevice); // 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/ITSMFT/ITS/workflow/src/its-track-writer-workflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/its-track-writer-workflow.cxx index d06ab366ef54c..ebd10ab3b16ce 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/its-track-writer-workflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/its-track-writer-workflow.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. // @@ -27,7 +27,6 @@ void customize(std::vector& workflowOptions) } #include "Framework/runDataProcessing.h" -#include "Framework/Logger.h" WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { diff --git a/Detectors/ITSMFT/MFT/calibration/src/NoiseCalibratorSpec.cxx b/Detectors/ITSMFT/MFT/calibration/src/NoiseCalibratorSpec.cxx index 86107106dc2ba..e55e822847177 100644 --- a/Detectors/ITSMFT/MFT/calibration/src/NoiseCalibratorSpec.cxx +++ b/Detectors/ITSMFT/MFT/calibration/src/NoiseCalibratorSpec.cxx @@ -18,7 +18,7 @@ #include "DataFormatsITSMFT/Digit.h" #include "DataFormatsITSMFT/CompCluster.h" #include "DataFormatsITSMFT/ROFRecord.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "ITSMFTReconstruction/ClustererParam.h" #include diff --git a/Detectors/ITSMFT/MFT/condition/include/MFTCondition/DCSConfigReader.h b/Detectors/ITSMFT/MFT/condition/include/MFTCondition/DCSConfigReader.h index efae3104279e1..110465bb92757 100644 --- a/Detectors/ITSMFT/MFT/condition/include/MFTCondition/DCSConfigReader.h +++ b/Detectors/ITSMFT/MFT/condition/include/MFTCondition/DCSConfigReader.h @@ -14,7 +14,7 @@ #include "Rtypes.h" #include "DataFormatsITSMFT/NoiseMap.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "MFTCondition/DCSConfigInfo.h" #include "MFTCondition/DCSConfigUtils.h" #include diff --git a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/RecoWorkflow.h b/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/RecoWorkflow.h index 0e0b8af1da70a..51234e2e8017d 100644 --- a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/RecoWorkflow.h +++ b/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/RecoWorkflow.h @@ -25,6 +25,7 @@ namespace reco_workflow { framework::WorkflowSpec getWorkflow( bool useMC, + bool doStag, bool useGeom, bool upstreamDigits, bool upstreamClusters, diff --git a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/TrackerSpec.h b/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/TrackerSpec.h index 4274710b23867..8bd290caf5a41 100644 --- a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/TrackerSpec.h +++ b/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/TrackerSpec.h @@ -16,7 +16,7 @@ #include "MFTTracking/Tracker.h" #include "DetectorsBase/GRPGeomHelper.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "Framework/DataProcessorSpec.h" #include "MFTTracking/TrackCA.h" diff --git a/Detectors/ITSMFT/MFT/workflow/src/RecoWorkflow.cxx b/Detectors/ITSMFT/MFT/workflow/src/RecoWorkflow.cxx index 5d85c0ef81670..fb99715cae4ee 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/RecoWorkflow.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/RecoWorkflow.cxx @@ -32,6 +32,7 @@ namespace reco_workflow framework::WorkflowSpec getWorkflow( bool useMC, + bool doStag, bool useGeom, bool upstreamDigits, bool upstreamClusters, @@ -45,17 +46,17 @@ framework::WorkflowSpec getWorkflow( framework::WorkflowSpec specs; if (!(upstreamDigits || upstreamClusters)) { - specs.emplace_back(o2::itsmft::getMFTDigitReaderSpec(useMC, false, true, "mftdigits.root")); + specs.emplace_back(o2::itsmft::getMFTDigitReaderSpec(useMC, doStag, false, true, "mftdigits.root")); auto& trackingParam = MFTTrackingParam::Instance(); if (trackingParam.irFramesOnly) { specs.emplace_back(o2::globaltracking::getIRFrameReaderSpec("ITS", 0, "its-irframe-reader", "o2_its_irframe.root")); } } if (!upstreamClusters) { - specs.emplace_back(o2::itsmft::getMFTClustererSpec(useMC)); + specs.emplace_back(o2::itsmft::getMFTClustererSpec(useMC, doStag)); } if (!disableRootOutput) { - specs.emplace_back(o2::itsmft::getMFTClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getMFTClusterWriterSpec(useMC, doStag)); } if (runTracking) { diff --git a/Detectors/ITSMFT/MFT/workflow/src/TrackWriterSpec.cxx b/Detectors/ITSMFT/MFT/workflow/src/TrackWriterSpec.cxx index d9c132c97abdf..f8a848f6fde32 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/TrackWriterSpec.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/TrackWriterSpec.cxx @@ -24,7 +24,6 @@ using namespace o2::framework; using LabelsType = std::vector; -using ROFRecLblT = std::vector; namespace o2 { @@ -44,7 +43,7 @@ DataProcessorSpec getTrackWriterSpec(bool useMC) *tracksSize = tracks.size(); }; auto logger = [tracksSize](std::vector const& rofs) { - LOG(debug) << "MFTTrackWriter pulled " << *tracksSize << " tracks, in " << rofs.size() << " RO frames"; + LOG(info) << "MFTTrackWriter pulled " << *tracksSize << " tracks, in " << rofs.size() << " RO frames"; }; return MakeRootTreeWriterSpec("mft-track-writer", "mfttracks.root", @@ -54,15 +53,11 @@ DataProcessorSpec getTrackWriterSpec(bool useMC) tracksSizeGetter}, BranchDefinition>{InputSpec{"trackClIdx", "MFT", "TRACKCLSID", 0}, "MFTTrackClusIdx"}, - BranchDefinition{InputSpec{"labels", "MFT", "TRACKSMCTR", 0}, - "MFTTrackMCTruth", - (useMC ? 1 : 0), // one branch if mc labels enabled - ""}, BranchDefinition>{InputSpec{"ROframes", "MFT", "MFTTrackROF", 0}, "MFTTracksROF", logger}, - BranchDefinition{InputSpec{"MC2ROframes", "MFT", "TRACKSMC2ROF", 0}, - "MFTTracksMC2ROF", + BranchDefinition{InputSpec{"labels", "MFT", "TRACKSMCTR", 0}, + "MFTTrackMCTruth", (useMC ? 1 : 0), // one branch if mc labels enabled ""})(); } diff --git a/Detectors/ITSMFT/MFT/workflow/src/TrackerSpec.cxx b/Detectors/ITSMFT/MFT/workflow/src/TrackerSpec.cxx index 3e726fe37c38c..a13a3402eb260 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/TrackerSpec.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/TrackerSpec.cxx @@ -45,7 +45,7 @@ namespace o2 { namespace mft { -//#define _TIMING_ +// #define _TIMING_ void TrackerDPL::init(InitContext& ic) { @@ -98,12 +98,6 @@ void TrackerDPL::run(ProcessingContext& pc) } const dataformats::MCTruthContainer* labels = mUseMC ? pc.inputs().get*>("labels").release() : nullptr; - gsl::span mc2rofs; - if (mUseMC) { - // get the array as read-only span, a snapshot of the object is sent forward - mc2rofs = pc.inputs().get>("MC2ROframes"); - LOG(info) << labels->getIndexedSize() << " MC label objects , in " << mc2rofs.size() << " MC events"; - } auto& allClusIdx = pc.outputs().make>(Output{"MFT", "TRACKCLSID", 0}); std::vector trackLabels; @@ -325,11 +319,10 @@ void TrackerDPL::run(ProcessingContext& pc) } } - LOG(info) << "MFTTracker pushed " << allTracksMFT.size() << " tracks"; + LOG(info) << "MFTTracker pushed " << allTracksMFT.size() << " tracks in " << nROFs << " rofs"; if (mUseMC) { pc.outputs().snapshot(Output{"MFT", "TRACKSMCTR", 0}, allTrackLabels); - pc.outputs().snapshot(Output{"MFT", "TRACKSMC2ROF", 0}, mc2rofs); } static bool first = true; @@ -466,9 +459,7 @@ DataProcessorSpec getTrackerSpec(bool useMC, bool useGeom, int nThreads) if (useMC) { inputs.emplace_back("labels", "MFT", "CLUSTERSMCTR", 0, Lifetime::Timeframe); - inputs.emplace_back("MC2ROframes", "MFT", "CLUSTERSMC2ROF", 0, Lifetime::Timeframe); outputs.emplace_back("MFT", "TRACKSMCTR", 0, Lifetime::Timeframe); - outputs.emplace_back("MFT", "TRACKSMC2ROF", 0, Lifetime::Timeframe); } return DataProcessorSpec{ diff --git a/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-reader-workflow.cxx b/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-reader-workflow.cxx index 9907705fb1e7c..eaa525345fd9f 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-reader-workflow.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-reader-workflow.cxx @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include "Framework/ConfigParamSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "DetectorsRaw/HBFUtilsInitializer.h" #include "Framework/CallbacksPolicy.h" @@ -41,6 +42,7 @@ void customize(std::vector& workflowOptions) false, {"do not propagate pixel patterns"}}); workflowOptions.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}); + o2::itsmft::DPLAlpideParamInitializer::addMFTConfigOption(workflowOptions); o2::raw::HBFUtilsInitializer::addConfigOption(workflowOptions); } @@ -53,7 +55,8 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cc) auto withTriggers = !cc.options().get("suppress-triggers-output"); auto withMC = cc.options().get("with-mc"); auto withPatterns = !cc.options().get("without-patterns"); - specs.emplace_back(o2::itsmft::getMFTClusterReaderSpec(withMC, withPatterns, withTriggers)); + auto doStag = o2::itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(cc); + specs.emplace_back(o2::itsmft::getMFTClusterReaderSpec(withMC, doStag, withPatterns, withTriggers)); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(cc, specs); 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 b656970693808..5a5112e03c866 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-writer-workflow.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-writer-workflow.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. // @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include "ITSMFTWorkflow/ClusterWriterSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "Framework/ConfigParamSpec.h" #include "Framework/CompletionPolicyHelpers.h" @@ -25,15 +26,16 @@ void customize(std::vector& workflowOptions) { workflowOptions.push_back( ConfigParamSpec{"disable-mc", o2::framework::VariantType::Bool, false, {"disable MC propagation even if available"}}); + o2::itsmft::DPLAlpideParamInitializer::addMFTConfigOption(workflowOptions); } #include "Framework/runDataProcessing.h" -#include "Framework/Logger.h" WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { auto useMC = !configcontext.options().get("disable-mc"); + auto doStag = o2::itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(configcontext); WorkflowSpec specs; - specs.emplace_back(o2::itsmft::getMFTClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getMFTClusterWriterSpec(useMC, doStag)); return specs; } diff --git a/Detectors/ITSMFT/MFT/workflow/src/mft-reco-workflow.cxx b/Detectors/ITSMFT/MFT/workflow/src/mft-reco-workflow.cxx index 19e41ed984f11..11b4fc233c6b4 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/mft-reco-workflow.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/mft-reco-workflow.cxx @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include "MFTWorkflow/RecoWorkflow.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "CommonUtils/ConfigurableParam.h" #include "DetectorsRaw/HBFUtilsInitializer.h" #include "Framework/CallbacksPolicy.h" @@ -45,6 +46,7 @@ void customize(std::vector& workflowOptions) {"use-full-geometry", o2::framework::VariantType::Bool, false, {"use full geometry instead of the light-weight MFT part"}}, {"run-tracks2records", o2::framework::VariantType::Bool, false, {"run MFT alignment tracks to records workflow"}}}; o2::raw::HBFUtilsInitializer::addConfigOption(options); + o2::itsmft::DPLAlpideParamInitializer::addMFTConfigOption(options); std::swap(workflowOptions, options); } @@ -67,9 +69,11 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) auto nThreads = configcontext.options().get("nThreads"); auto runTracks2Records = configcontext.options().get("run-tracks2records"); auto useGeom = configcontext.options().get("use-full-geometry"); + auto doStag = o2::itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(configcontext); auto wf = o2::mft::reco_workflow::getWorkflow( useMC, + doStag, useGeom, extDigits, extClusters, diff --git a/Detectors/ITSMFT/common/base/CMakeLists.txt b/Detectors/ITSMFT/common/base/CMakeLists.txt index a3e0718d64a6b..43d60f6d2b11d 100644 --- a/Detectors/ITSMFT/common/base/CMakeLists.txt +++ b/Detectors/ITSMFT/common/base/CMakeLists.txt @@ -11,12 +11,10 @@ o2_add_library(ITSMFTBase SOURCES src/SegmentationAlpide.cxx - src/GeometryTGeo.cxx src/DPLAlpideParam.cxx PUBLIC_LINK_LIBRARIES O2::MathUtils O2::DetectorsCommonDataFormats O2::SimConfig) o2_target_root_dictionary(ITSMFTBase HEADERS include/ITSMFTBase/SegmentationAlpide.h - include/ITSMFTBase/GeometryTGeo.h - include/ITSMFTBase/DPLAlpideParam.h) + include/ITSMFTBase/GeometryTGeo.h) diff --git a/Detectors/ITSMFT/common/base/include/ITSMFTBase/DPLAlpideParam.h b/Detectors/ITSMFT/common/base/include/ITSMFTBase/DPLAlpideParam.h index de39bed299634..e217808c06177 100644 --- a/Detectors/ITSMFT/common/base/include/ITSMFTBase/DPLAlpideParam.h +++ b/Detectors/ITSMFT/common/base/include/ITSMFTBase/DPLAlpideParam.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. // @@ -9,110 +9,5 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#ifndef ALICEO2_ITSMFTDPLBASEPARAM_H_ -#define ALICEO2_ITSMFTDPLBASEPARAM_H_ - -#include "DetectorsCommonDataFormats/DetID.h" -#include "CommonUtils/ConfigurableParam.h" -#include "CommonUtils/ConfigurableParamHelper.h" -#include "CommonConstants/LHCConstants.h" -#include - -namespace o2 -{ -namespace itsmft -{ -constexpr float DEFStrobeDelay = o2::constants::lhc::LHCBunchSpacingNS * 4; // ~100 ns delay - -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 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()); - - private: - static constexpr std::string_view ParamName[2] = {"ITSAlpideParam", "MFTAlpideParam"}; - - static constexpr int DEFROFLengthBC() - { - // default ROF length in BC for continuous mode - // allowed values: 1,2,3,4,6,9,11,12,18,22,27,33,36 - return N == o2::detectors::DetID::ITS ? o2::constants::lhc::LHCMaxBunches / 4 : o2::constants::lhc::LHCMaxBunches / 18; - } - static constexpr float DEFROFLengthTrig() - { - // length of RO frame in ns for triggered mode - return N == o2::detectors::DetID::ITS ? 6000. : 6000.; - } - - static constexpr int DEFROFBiasInBC() - { - // default ROF length bias in MC, see https://github.com/AliceO2Group/AliceO2/pull/11108 for ITS - return N == o2::detectors::DetID::ITS ? 64 : 60; - } - - static_assert(N == o2::detectors::DetID::ITS || N == o2::detectors::DetID::MFT, "only DetID::ITS orDetID:: MFT are allowed"); - static_assert(o2::constants::lhc::LHCMaxBunches % DEFROFLengthBC() == 0); // make sure ROF length is divisor of the orbit -}; - -template -DPLAlpideParam DPLAlpideParam::sInstance; - -} // namespace itsmft - -namespace framework -{ -template -struct is_messageable; -template <> -struct is_messageable> : std::true_type { -}; -template -struct is_messageable; -template <> -struct is_messageable> : std::true_type { -}; - -} // namespace framework - -} // namespace o2 - -#endif +// FIXME: temporary shim to no not break O2Physics +#include "DataFormatsITSMFT/DPLAlpideParam.h" diff --git a/Detectors/ITSMFT/common/base/src/ITSMFTBaseLinkDef.h b/Detectors/ITSMFT/common/base/src/ITSMFTBaseLinkDef.h index 6202f372cf2d3..9296c21e81cae 100644 --- a/Detectors/ITSMFT/common/base/src/ITSMFTBaseLinkDef.h +++ b/Detectors/ITSMFT/common/base/src/ITSMFTBaseLinkDef.h @@ -17,11 +17,6 @@ #pragma link C++ class o2::itsmft::SegmentationAlpide + ; -#pragma link C++ class o2::itsmft::DPLAlpideParam < o2::detectors::DetID::ITS> + ; -#pragma link C++ class o2::itsmft::DPLAlpideParam < o2::detectors::DetID::MFT> + ; -#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::itsmft::DPLAlpideParam < o2::detectors::DetID::ITS>> + ; -#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::itsmft::DPLAlpideParam < o2::detectors::DetID::MFT>> + ; - #pragma link C++ class o2::itsmft::GeometryTGeo; #endif diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h index 7e266052efb3c..4f9bc90c1c758 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/CTFCoder.h @@ -27,6 +27,7 @@ #include "ITSMFTReconstruction/LookUp.h" #include "ITSMFTReconstruction/PixelData.h" #include "ITSMFTReconstruction/Clusterer.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsBase/CTFCoderBase.h" @@ -39,19 +40,22 @@ namespace o2 namespace itsmft { +template class CTFCoder final : public o2::ctf::CTFCoderBase { public: + static constexpr o2::detectors::DetID ID{N == o2::detectors::DetID::ITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT}; + using PMatrix = std::array, ClusterPattern::MaxColSpan + 2>; using RowColBuff = std::vector; - CTFCoder(o2::ctf::CTFCoderBase::OpType op, o2::detectors::DetID det, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), det, 1.f, ctfdictOpt) {} + CTFCoder(o2::ctf::CTFCoderBase::OpType op, bool doStag, const std::string& ctfdictOpt = "none") : o2::ctf::CTFCoderBase(op, CTF::getNBlocks(), ID, 1.f, ctfdictOpt), mDoStaggering(doStag) {} ~CTFCoder() final = default; /// entropy-encode clusters to buffer with CTF template o2::ctf::CTFIOSize encode(VEC& buff, const gsl::span& rofRecVec, const gsl::span& cclusVec, - const gsl::span& pattVec, const LookUp& clPattLookup, int strobeLength); + const gsl::span& pattVec, const LookUp& clPattLookup, int layer); /// entropy decode clusters from buffer with CTF template @@ -79,16 +83,21 @@ class CTFCoder final : public o2::ctf::CTFCoderBase template void decompress(const CompressedClusters& compCl, VROF& rofRecVec, VDIG& digVec, const NoiseMap* noiseMap, const LookUp& clPattLookup); - void appendToTree(TTree& tree, CTF& ec); - void readFromTree(TTree& tree, int entry, std::vector& rofRecVec, std::vector& cclusVec, std::vector& pattVec, const NoiseMap* noiseMap, const LookUp& clPattLookup); + void appendToTree(TTree& tree, CTF& ec, int id = -1); + void readFromTree(TTree& tree, int entry, int id, std::vector& rofRecVec, std::vector& cclusVec, std::vector& pattVec, const NoiseMap* noiseMap, const LookUp& clPattLookup); + + bool mDoStaggering{false}; }; /// entropy-encode clusters to buffer with CTF +template template -o2::ctf::CTFIOSize CTFCoder::encode(VEC& buff, const gsl::span& rofRecVec, const gsl::span& cclusVec, - const gsl::span& pattVec, const LookUp& clPattLookup, int strobeLength) +o2::ctf::CTFIOSize CTFCoder::encode(VEC& buff, const gsl::span& rofRecVec, const gsl::span& cclusVec, + const gsl::span& pattVec, const LookUp& clPattLookup, int layer) { using MD = o2::ctf::Metadata::OptStore; + const auto& par = DPLAlpideParam::Instance(); + int strobeLength = mDoStaggering ? par.roFrameLayerLengthInBC[layer] : par.roFrameLengthInBC; // what to do which each field: see o2::ctd::Metadata explanation constexpr MD optField[CTF::getNBlocks()] = { MD::EENCODE_OR_PACK, // BLCfirstChipROF @@ -104,6 +113,8 @@ o2::ctf::CTFIOSize CTFCoder::encode(VEC& buff, const gsl::span& }; CompressedClusters compCl; compress(compCl, rofRecVec, cclusVec, pattVec, clPattLookup, strobeLength); + compCl.header.maxStreams = mDoStaggering ? par.getNLayers() : 1; + compCl.header.streamID = mDoStaggering ? layer : 0; // book output size with some margin auto szIni = estimateCompressedSize(compCl); buff.resize(szIni); @@ -136,19 +147,26 @@ o2::ctf::CTFIOSize CTFCoder::encode(VEC& buff, const gsl::span& } /// decode entropy-encoded clusters to standard compact clusters +template template -o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VROF& rofRecVec, VCLUS& cclusVec, VPAT& pattVec, const NoiseMap* noiseMap, const LookUp& clPattLookup) +o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VROF& rofRecVec, VCLUS& cclusVec, VPAT& pattVec, const NoiseMap* noiseMap, const LookUp& clPattLookup) { o2::ctf::CTFIOSize iosize; auto compCl = decodeCompressedClusters(ec, iosize); + const auto& par = DPLAlpideParam::Instance(); + uint32_t nLayers = mDoStaggering ? par.getNLayers() : 1; + if (compCl.header.maxStreams != nLayers) { + throw std::runtime_error(fmt::format("header maxStreams={} is not the same as NStreams={} in {}staggered mode", compCl.header.maxStreams, nLayers, mDoStaggering ? "" : "non-")); + } decompress(compCl, rofRecVec, cclusVec, pattVec, noiseMap, clPattLookup); iosize.rawIn = rofRecVec.size() * sizeof(ROFRecord) + cclusVec.size() * sizeof(CompClusterExt) + pattVec.size() * sizeof(unsigned char); return iosize; } /// decode entropy-encoded clusters to digits +template template -o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VROF& rofRecVec, VDIG& digVec, const NoiseMap* noiseMap, const LookUp& clPattLookup) +o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VROF& rofRecVec, VDIG& digVec, const NoiseMap* noiseMap, const LookUp& clPattLookup) { o2::ctf::CTFIOSize iosize; auto compCl = decodeCompressedClusters(ec, iosize); @@ -158,8 +176,9 @@ o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VROF& rofRecVec, VDIG& } /// decompress compressed clusters to standard compact clusters +template template -void CTFCoder::decompress(const CompressedClusters& compCl, VROF& rofRecVec, VCLUS& cclusVec, VPAT& pattVec, const NoiseMap* noiseMap, const LookUp& clPattLookup) +void CTFCoder::decompress(const CompressedClusters& compCl, VROF& rofRecVec, VCLUS& cclusVec, VPAT& pattVec, const NoiseMap* noiseMap, const LookUp& clPattLookup) { PMatrix pmat{}; RowColBuff firedPixBuff{}, maskedPixBuff{}; @@ -343,8 +362,9 @@ void CTFCoder::decompress(const CompressedClusters& compCl, VROF& rofRecVec, VCL } /// decompress compressed clusters to digits +template template -void CTFCoder::decompress(const CompressedClusters& compCl, VROF& rofRecVec, VDIG& digVec, const NoiseMap* noiseMap, const LookUp& clPattLookup) +void CTFCoder::decompress(const CompressedClusters& compCl, VROF& rofRecVec, VDIG& digVec, const NoiseMap* noiseMap, const LookUp& clPattLookup) { rofRecVec.resize(compCl.header.nROFs); digVec.reserve(compCl.header.nClusters * 2); diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingITS.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingITS.h index 45668ca507280..6110a8492d416 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingITS.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingITS.h @@ -310,6 +310,9 @@ class ChipMappingITS std::vector getOverlapsInfo() const; + ///< Collect all FEEIDs for one layer (lr>=0) or all (lr==-1) + std::vector getLayer2FEEIDs(int lr); + // sub-barrel types, their number, N layers, Max N GBT Links per RU static constexpr int IB = 0, MB = 1, OB = 2, NSubB = 3, NLayers = 7, NLinks = 3; @@ -395,7 +398,7 @@ class ChipMappingITS std::vector mCablePos[NSubB]; ///< table of cables positions in the ActiveLanes mask for each RU type (sequential numbering) std::vector mCableHWFirstChip[NSubB]; ///< 1st chip of module (relative to the 1st chip of the stave) served by each cable - std::array mCablesOnStaveSB = {0}; ///< pattern of cables per stave of sub-barrel + std::array mCablesOnStaveSB = {0}; ///< pattern of cables per stave of sub-barrel std::array, MaxHWCableID[MB] + 1> HWCableHWChip2ChipOnRU_MB; // mapping from HW cable ID / HW chip ID to Chip on RU, 255 means NA std::array, MaxHWCableID[OB] + 1> HWCableHWChip2ChipOnRU_OB; // mapping from HW cable ID / HW chip ID to Chip on RU, 255 means NA diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingMFT.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingMFT.h index 3fa94c2628f3a..eee9bdbb6a4dc 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingMFT.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ChipMappingMFT.h @@ -266,6 +266,9 @@ class ChipMappingMFT const auto& getModuleMappingData() const { return ModuleMappingData; } + ///< Collect all FEEIDs for one layer (lr>=0) or all (lr==-1) + std::vector getLayer2FEEIDs(int lr); + void print() const; ///< LayerID of each MFT chip diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/PixelReader.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/PixelReader.h index 80ef5ed7abec8..b98abf1d9b2d4 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/PixelReader.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/PixelReader.h @@ -50,11 +50,6 @@ class PixelReader { return nullptr; } - const o2::InteractionRecord& getInteractionRecordHB() const - { - return mInteractionRecordHB; - } - const o2::InteractionRecord& getInteractionRecord() const { return mInteractionRecord; @@ -70,8 +65,7 @@ class PixelReader // protected: // - o2::InteractionRecord mInteractionRecord = {}; // interation record for the trigger - o2::InteractionRecord mInteractionRecordHB = {}; // interation record for the HB + o2::InteractionRecord mInteractionRecord = {}; // interation record for the trigger uint32_t mTrigger = 0; bool mDecodeNextAuto = true; // try to fetch/decode next trigger when getNextChipData does not see any decoded data diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelDecoder.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelDecoder.h index 3a53253da2b42..b10f60c749f7c 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelDecoder.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelDecoder.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. // @@ -14,9 +14,11 @@ #ifndef ALICEO2_ITSMFT_RAWPIXELDECODER_H_ #define ALICEO2_ITSMFT_RAWPIXELDECODER_H_ +#include #include #include #include "Framework/Logger.h" +#include "Framework/InputSpec.h" #include "ITSMFTReconstruction/ChipMappingITS.h" #include "ITSMFTReconstruction/ChipMappingMFT.h" #include "DetectorsRaw/HBFUtils.h" @@ -29,7 +31,6 @@ #include "DataFormatsITSMFT/ROFRecord.h" #include "ITSMFTReconstruction/PixelData.h" #include "ITSMFTReconstruction/GBTWord.h" -#include namespace o2 { @@ -91,6 +92,9 @@ class RawPixelDecoder final : public PixelReader void setVerbosity(int v); int getVerbosity() const { return mVerbosity; } + void setInputFilter(std::vector filter) { mInputFilter = std::move(filter); } + const auto& getInputFilter() const noexcept { return mInputFilter; } + void setAlwaysParseTrigger(bool v) { mAlwaysParseTrigger = v; } bool getAlwaysParseTrigger() const { return mAlwaysParseTrigger; } @@ -138,7 +142,7 @@ class RawPixelDecoder final : public PixelReader void reset(); private: - void setupLinks(o2::framework::InputRecord& inputs); + void setupLinks(o2::framework::InputRecord& inputsm); int getRUEntrySW(int ruSW) const { return mRUEntry[ruSW]; } RUDecodeData* getRUDecode(int ruSW) { return &mRUDecodeVec[mRUEntry[ruSW]]; } GBTLink* getGBTLink(int i) { return i < 0 ? nullptr : &mGBTLinks[i]; } @@ -146,6 +150,7 @@ class RawPixelDecoder final : public PixelReader static constexpr uint16_t NORUDECODED = 0xffff; // this must be > than max N RUs + std::vector mInputFilter; // input spec filter std::vector mGBTLinks; // active links pool std::unordered_map mSubsSpec2LinkID; // link subspec to link entry in the pool mapping std::vector mRUDecodeVec; // set of active RUs diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelReader.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelReader.h index 97716059f12d6..ce6582853788d 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelReader.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/RawPixelReader.h @@ -53,8 +53,8 @@ namespace o2 namespace itsmft { -constexpr int MaxGBTPacketBytes = 8 * 1024; // Max size of GBT packet in bytes (8KB) -constexpr int NCRUPagesPerSuperpage = 256; // Expected max number of CRU pages per superpage +constexpr int MaxGBTPacketBytes = 8 * 1024; // Max size of GBT packet in bytes (8KB) +constexpr int NCRUPagesPerSuperpage = 256; // Expected max number of CRU pages per superpage using RDHUtils = o2::raw::RDHUtils; struct RawDecodingStat { @@ -633,7 +633,6 @@ class RawPixelReader : public PixelReader const auto rdh = reinterpret_cast(link->data.getPtr()); mInteractionRecord = RDHUtils::getTriggerIR(rdh); mTrigger = RDHUtils::getTriggerType(rdh); - mInteractionRecordHB = RDHUtils::getHeartBeatIR(rdh); break; } } @@ -674,7 +673,7 @@ class RawPixelReader : public PixelReader } } if (ruDecData.ruInfo->nCables) { // there are cables with data to decode - decodeAlpideData(ruDecData); // decode Alpide data from the compressed RU Data + decodeAlpideData(ruDecData); // decode Alpide data from the compressed RU Data } return res; } diff --git a/Detectors/ITSMFT/common/reconstruction/src/CTFCoder.cxx b/Detectors/ITSMFT/common/reconstruction/src/CTFCoder.cxx index ec0ee9e3f0f24..4a0c83fd0c859 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/CTFCoder.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/CTFCoder.cxx @@ -14,35 +14,38 @@ /// \brief class for entropy encoding/decoding of ITS/MFT compressmed clusters data #include "ITSMFTReconstruction/CTFCoder.h" -#include "CommonUtils/StringUtils.h" #include -using namespace o2::itsmft; +namespace o2::itsmft +{ ///___________________________________________________________________________________ // Register encoded data in the tree (Fill is not called, will be done by caller) -void CTFCoder::appendToTree(TTree& tree, CTF& ec) +template +void CTFCoder::appendToTree(TTree& tree, CTF& ec, int id) { - ec.appendToTree(tree, mDet.getName()); + ec.appendToTree(tree, id >= 0 ? fmt::format("{}_{}", mDet.getName(), id) : mDet.getName()); } ///___________________________________________________________________________________ // extract and decode data from the tree -void CTFCoder::readFromTree(TTree& tree, int entry, std::vector& rofRecVec, - std::vector& cclusVec, std::vector& pattVec, const NoiseMap* noiseMap, const LookUp& clPattLookup) +template +void CTFCoder::readFromTree(TTree& tree, int entry, int id, std::vector& rofRecVec, + std::vector& cclusVec, std::vector& pattVec, const NoiseMap* noiseMap, const LookUp& clPattLookup) { assert(entry >= 0 && entry < tree.GetEntries()); CTF ec; - ec.readFromTree(tree, mDet.getName(), entry); + ec.readFromTree(tree, id >= 0 ? fmt::format("{}_{}", mDet.getName(), id) : mDet.getName(), entry); decode(ec, rofRecVec, cclusVec, pattVec, noiseMap, clPattLookup); } ///________________________________ -void CTFCoder::compress(CompressedClusters& cc, - const gsl::span& rofRecVec, - const gsl::span& cclusVec, - const gsl::span& pattVec, - const LookUp& clPattLookup, int strobeLength) +template +void CTFCoder::compress(CompressedClusters& cc, + const gsl::span& rofRecVec, + const gsl::span& cclusVec, + const gsl::span& pattVec, + const LookUp& clPattLookup, int strobeLength) { // store in the header the orbit of 1st ROF cc.clear(); @@ -191,11 +194,12 @@ void CTFCoder::compress(CompressedClusters& cc, } ///________________________________ -void CTFCoder::createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBase::OpType op) +template +void CTFCoder::createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBase::OpType op) { const auto ctf = CTF::getImage(bufVec.data()); CompressedClusters cc; // just to get member types -#define MAKECODER(part, slot) createCoder(op, std::get>(ctf.getDictionary(slot, mANSVersion)), int(slot)) +#define MAKECODER(part, slot) createCoder(op, std::get>(ctf.getDictionary(slot, mANSVersion)), int(slot)) // clang-format off MAKECODER(cc.firstChipROF, CTF::BLCfirstChipROF); MAKECODER(cc.bcIncROF, CTF::BLCbcIncROF ); @@ -212,7 +216,8 @@ void CTFCoder::createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBa } ///________________________________ -size_t CTFCoder::estimateCompressedSize(const CompressedClusters& cc) +template +size_t CTFCoder::estimateCompressedSize(const CompressedClusters& cc) { size_t sz = 0; // RS FIXME this is very crude estimate, instead, an empirical values should be used @@ -234,7 +239,8 @@ size_t CTFCoder::estimateCompressedSize(const CompressedClusters& cc) } ///________________________________ -CompressedClusters CTFCoder::decodeCompressedClusters(const CTF::base& ec, o2::ctf::CTFIOSize& iosize) +template +CompressedClusters CTFCoder::decodeCompressedClusters(const CTF::base& ec, o2::ctf::CTFIOSize& iosize) { CompressedClusters cc; cc.header = ec.getHeader(); @@ -256,3 +262,7 @@ CompressedClusters CTFCoder::decodeCompressedClusters(const CTF::base& ec, o2::c // clang-format on return cc; } + +template class CTFCoder; +template class CTFCoder; +} // namespace o2::itsmft diff --git a/Detectors/ITSMFT/common/reconstruction/src/ChipMappingITS.cxx b/Detectors/ITSMFT/common/reconstruction/src/ChipMappingITS.cxx index 7d9733554ef12..f143e4bb23f3d 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/ChipMappingITS.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/ChipMappingITS.cxx @@ -78,14 +78,14 @@ ChipMappingITS::ChipMappingITS() cInfo.moduleSW = 0; cInfo.chipOnModuleSW = i; cInfo.chipOnModuleHW = i; - cInfo.cableHW = i; //1-to-1 mapping - cInfo.cableHWPos = i; //1-to-1 mapping - cInfo.cableSW = i; //1-to-1 mapping - cInfo.chipOnCable = 0; // every chip is master + cInfo.cableHW = i; // 1-to-1 mapping + cInfo.cableHWPos = i; // 1-to-1 mapping + cInfo.cableSW = i; // 1-to-1 mapping + cInfo.chipOnCable = 0; // every chip is master mCableHW2SW[IB][cInfo.cableHW] = cInfo.cableSW; mCableHW2Pos[IB][cInfo.cableHW] = cInfo.cableHWPos; mCablesOnStaveSB[IB] |= 0x1 << cInfo.cableHWPos; // account in lanes pattern - mCableHWFirstChip[IB][i] = 0; // stave and module are the same + mCableHWFirstChip[IB][i] = 0; // stave and module are the same } // [i][j] gives lane id for lowest(i=0) and highest(i=1) 7 chips of HW module (j+1) (1-4 for ML, 1-7 for OL) @@ -289,3 +289,17 @@ std::vector ChipMappingITS::getOverlapsInfo() const } return v; } + +//_____________________________________________________________________________ +std::vector ChipMappingITS::getLayer2FEEIDs(int lr) +{ + std::vector feeIDs; + for (int ilr = (lr >= 0 ? lr : 0); ilr < (lr >= 0 ? lr + 1 : NLayers); ++ilr) { + for (int ist = 0; ist < NStavesOnLr[ilr]; ++ist) { + for (int lnk = 0; lnk < NLinks; ++lnk) { + feeIDs.push_back(composeFEEId(ilr, ist, lnk)); + } + } + } + return feeIDs; +} diff --git a/Detectors/ITSMFT/common/reconstruction/src/ChipMappingMFT.cxx b/Detectors/ITSMFT/common/reconstruction/src/ChipMappingMFT.cxx index 259df62921c8f..de2358469e894 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/ChipMappingMFT.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/ChipMappingMFT.cxx @@ -1753,3 +1753,17 @@ void ChipMappingMFT::print() const ChipMappingData[iChip].chipOnRU); } } + +//_____________________________________________________________________________ +std::vector ChipMappingMFT::getLayer2FEEIDs(int lr) +{ + std::vector feeIDs; + for (int ilr = (lr >= 0 ? lr : 0); ilr < (lr >= 0 ? lr + 1 : NLayers); ++ilr) { + for (int iz = 0; iz < NZonesPerLayer; ++iz) { + for (int lnk = 0; lnk < NLinks; ++lnk) { + feeIDs.push_back(composeFEEId(ilr, iz, lnk)); + } + } + } + return feeIDs; +} diff --git a/Detectors/ITSMFT/common/reconstruction/src/Clusterer.cxx b/Detectors/ITSMFT/common/reconstruction/src/Clusterer.cxx index 3c741321e7780..dcc268a4504a9 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/Clusterer.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/Clusterer.cxx @@ -479,7 +479,7 @@ void Clusterer::print(bool showsTiming) const } 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, "\tClusterizer on layer {} 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); diff --git a/Detectors/ITSMFT/common/reconstruction/src/GBTLink.cxx b/Detectors/ITSMFT/common/reconstruction/src/GBTLink.cxx index af4c8de5caf39..4d336d9adb1ee 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/GBTLink.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/GBTLink.cxx @@ -41,7 +41,7 @@ GBTLink::GBTLink(uint16_t _cru, uint16_t _fee, uint8_t _ep, uint8_t _idInCru, ui /// create string describing the link std::string GBTLink::describe() const { - std::string ss = fmt::format("link cruID:{:#06x}/lID{} feeID:{:#06x}", cruID, int(idInCRU), feeID); + std::string ss = fmt::format("link cruID:{:#06x}/lID{:02} feeID:{:#06x}", cruID, int(idInCRU), feeID); if (lanes) { ss += fmt::format(" lanes {}", std::bitset<28>(lanes).to_string()); } diff --git a/Detectors/ITSMFT/common/reconstruction/src/RawPixelDecoder.cxx b/Detectors/ITSMFT/common/reconstruction/src/RawPixelDecoder.cxx index dc61bea9f406e..7158551e02e20 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/RawPixelDecoder.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/RawPixelDecoder.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. // @@ -40,7 +40,8 @@ RawPixelDecoder::RawPixelDecoder() mTimerDecode.Stop(); mTimerFetchData.Stop(); mSelfName = o2::utils::Str::concat_string(Mapping::getName(), "Decoder"); - DPLRawParser<>::setCheckIncompleteHBF(false); // Disable incomplete HBF checking, see ErrPacketCounterJump check in GBTLink.cxx + DPLRawParser<>::setCheckIncompleteHBF(false); // Disable incomplete HBF checking, see ErrPacketCounterJump check in GBTLink.cxx + mInputFilter = {InputSpec{"filter", ConcreteDataTypeMatcher{Mapping::getOrigin(), o2::header::gDataDescriptionRawData}}}; // by default take all raw data } ///______________________________________________________________ @@ -102,8 +103,7 @@ int RawPixelDecoder::decodeNextTrigger() } #ifdef WITH_OPENMP -#pragma omp parallel for schedule(dynamic) num_threads(mNThreads) reduction(+ \ - : mNChipsFiredROF, mNPixelsFiredROF) +#pragma omp parallel for schedule(dynamic) num_threads(mNThreads) reduction(+ : mNChipsFiredROF, mNPixelsFiredROF) #endif for (int iru = 0; iru < nru; iru++) { auto& ru = mRUDecodeVec[iru]; @@ -186,6 +186,9 @@ bool RawPixelDecoder::doIRMajorityPoll() if (link.statusInTF == GBTLink::DataSeen) { if (link.status == GBTLink::DataSeen || link.status == GBTLink::CachedDataExist) { mIRPoll[link.ir]++; + if (mVerbosity >= GBTLink::Verbosity::VerboseHeaders) { + LOGP(info, "doIRMajorityPoll: {} contributes to poll {}", link.describe(), link.ir.asString()); + } } else if (link.status == GBTLink::StoppedOnEndOfData || link.status == GBTLink::AbortedOnError) { link.statusInTF = GBTLink::StoppedOnEndOfData; if (mVerbosity >= GBTLink::Verbosity::VerboseHeaders) { @@ -195,6 +198,12 @@ bool RawPixelDecoder::doIRMajorityPoll() } } } + if (mNLinksDone == mNLinksInTF) { + if (mVerbosity >= GBTLink::Verbosity::VerboseHeaders) { + LOGP(info, "doIRMajorityPoll: All {} links registered in TF are done", mNLinksInTF); + } + return false; + } int majIR = -1; for (const auto& entIR : mIRPoll) { if (entIR.second > majIR) { @@ -202,16 +211,14 @@ bool RawPixelDecoder::doIRMajorityPoll() mInteractionRecord = entIR.first; } } - mInteractionRecordHB = mInteractionRecord; if (mInteractionRecord.isDummy()) { if (mVerbosity >= GBTLink::Verbosity::VerboseHeaders) { LOG(info) << "doIRMajorityPoll: did not find any valid IR"; } return false; } - mInteractionRecordHB.bc = 0; if (mVerbosity >= GBTLink::Verbosity::VerboseHeaders) { - LOG(info) << "doIRMajorityPoll: " << mInteractionRecordHB.asString() << " majority = " << majIR << " for " << mNLinksInTF << " links seen, LinksDone = " << mNLinksDone; + LOG(info) << "doIRMajorityPoll: " << mInteractionRecord.asString() << " majority = " << majIR << " for " << mNLinksInTF << " links seen, LinksDone = " << mNLinksDone; } return true; } @@ -228,7 +235,14 @@ void RawPixelDecoder::setupLinks(InputRecord& inputs) auto nLinks = mGBTLinks.size(); auto origin = (mUserDataOrigin == o2::header::gDataOriginInvalid) ? mMAP.getOrigin() : mUserDataOrigin; auto datadesc = (mUserDataDescription == o2::header::gDataDescriptionInvalid) ? o2::header::gDataDescriptionRawData : mUserDataDescription; - std::vector filter{InputSpec{"filter", ConcreteDataTypeMatcher{origin, datadesc}}}; + if (mUserDataDescription != o2::header::gDataDescriptionInvalid) { // overwrite data filter origin&descriptions with user defined ones if possible + for (auto& filt : mInputFilter) { + if (std::holds_alternative(filt.matcher)) { + std::get(filt.matcher).origin = origin; + std::get(filt.matcher).description = datadesc; + } + } + } // 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 @@ -251,28 +265,31 @@ void RawPixelDecoder::setupLinks(InputRecord& inputs) contDeadBeef = 0; // if good data, reset the counter } mROFRampUpStage = false; - DPLRawParser parser(inputs, filter, o2::conf::VerbosityConfig::Instance().rawParserSeverity); + DPLRawParser parser(inputs, mInputFilter, o2::conf::VerbosityConfig::Instance().rawParserSeverity); parser.setMaxFailureMessages(o2::conf::VerbosityConfig::Instance().maxWarnRawParser); static size_t cntParserFailures = 0; parser.setExtFailureCounter(&cntParserFailures); uint32_t currSSpec = 0xffffffff; // dummy starting subspec int linksAdded = 0; + uint16_t lr, dummy; // extraxted info from FEEId for (auto it = parser.begin(); it != parser.end(); ++it) { auto const* dh = it.o2DataHeader(); auto& lnkref = mSubsSpec2LinkID[dh->subSpecification]; const auto& rdh = *reinterpret_cast(it.raw()); // RSTODO this is a hack in absence of generic header getter + const auto feeID = RDHUtils::getFEEID(rdh); + mMAP.expandFEEId(feeID, lr, dummy, dummy); if (lnkref.entry == -1) { // new link needs to be added lnkref.entry = int(mGBTLinks.size()); - auto& lnk = mGBTLinks.emplace_back(RDHUtils::getCRUID(rdh), RDHUtils::getFEEID(rdh), RDHUtils::getEndPointID(rdh), RDHUtils::getLinkID(rdh), lnkref.entry); + auto& lnk = mGBTLinks.emplace_back(RDHUtils::getCRUID(rdh), feeID, RDHUtils::getEndPointID(rdh), RDHUtils::getLinkID(rdh), lnkref.entry); lnk.subSpec = dh->subSpecification; lnk.wordLength = (lnk.expectPadding = (RDHUtils::getDataFormat(rdh) == 0)) ? o2::itsmft::GBTPaddedWordLength : o2::itsmft::GBTWordLength; - getCreateRUDecode(mMAP.FEEId2RUSW(RDHUtils::getFEEID(rdh))); // make sure there is a RU for this link + getCreateRUDecode(mMAP.FEEId2RUSW(feeID)); // make sure there is a RU for this link lnk.verbosity = GBTLink::Verbosity(mVerbosity); lnk.alwaysParseTrigger = mAlwaysParseTrigger; if (mVerbosity >= GBTLink::Verbosity::VerboseHeaders) { - LOG(info) << mSelfName << " registered new link " << lnk.describe() << " RUSW=" << int(mMAP.FEEId2RUSW(lnk.feeID)); + LOG(info) << mSelfName << " registered new " << lnk.describe() << " RUSW=" << int(mMAP.FEEId2RUSW(lnk.feeID)); } linksAdded++; } @@ -330,7 +347,7 @@ void RawPixelDecoder::setupLinks(InputRecord& inputs) mMAP.expandFEEId(link.feeID, lr, ruOnLr, linkInRU); if (newLinkAdded) { if (mVerbosity >= GBTLink::Verbosity::VerboseHeaders) { - LOG(info) << mSelfName << " Attaching " << link.describe() << " to RU#" << int(mMAP.FEEId2RUSW(link.feeID)) << " (stave " << ruOnLr << " of layer " << lr << ')'; + LOGP(info, "{} Attaching {} to RU#{:02} (stave {:02} of layer {})", mSelfName, link.describe(), int(mMAP.FEEId2RUSW(link.feeID)), ruOnLr, lr); } } link.idInRU = linkInRU; diff --git a/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/DigiParams.h b/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/DigiParams.h index fa75a65728675..f4482c651b090 100644 --- a/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/DigiParams.h +++ b/Detectors/ITSMFT/common/simulation/include/ITSMFTSimulation/DigiParams.h @@ -19,7 +19,7 @@ #include #include #include "ITSMFTSimulation/AlpideSignalTrapezoid.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" //////////////////////////////////////////////////////////// // // diff --git a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterReaderSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterReaderSpec.h index 82e3890de7475..9d58b6fde16c1 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterReaderSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterReaderSpec.h @@ -14,8 +14,10 @@ #ifndef O2_ITSMFT_CLUSTERREADER #define O2_ITSMFT_CLUSTERREADER -#include "TFile.h" -#include "TTree.h" +#include + +#include +#include #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" @@ -23,7 +25,7 @@ #include "DataFormatsITSMFT/CompCluster.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DataFormatsITSMFT/ROFRecord.h" #include "DetectorsCommonDataFormats/DetID.h" @@ -38,10 +40,9 @@ 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(bool useMC, bool usePatterns = true, bool triggers = true); + ClusterReader(bool useMC = true, bool doStag = false, bool usePatterns = true, bool triggers = true); ~ClusterReader() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -52,18 +53,19 @@ class ClusterReader : public Task void setBranchAddress(const std::string& base, Ptr& addr, int layer); std::string getBranchName(const std::string& base, int index) const; - std::array*, NLayers> mClusROFRec; - std::array*, NLayers> mClusterCompArray; - std::array*, NLayers> mPatternsArray; - std::array*, NLayers> mClusterMCTruth; - std::array*, NLayers> mClusMC2ROFs; + std::vector*> mClusROFRec{nullptr}; + std::vector*> mClusterCompArray{nullptr}; + std::vector*> mPatternsArray{nullptr}; + std::vector*> mClusterMCTruth{nullptr}; std::unique_ptr mFile; std::unique_ptr mTree; - bool mUseMC = true; // use MC truth - bool mUsePatterns = true; // send patterns - bool mTriggerOut = true; // send dummy triggers vector + int mLayers = 1; + bool mUseMC = true; // use MC truth + bool mDoStaggering = false; // read staggered data + bool mUsePatterns = true; // send patterns + bool mTriggerOut = true; // send dummy triggers vector std::string mDetName; std::string mDetNameLC; @@ -73,27 +75,26 @@ class ClusterReader : public Task std::string mClusterPattBranchName = "ClusterPatt"; std::string mClusterCompBranchName = "ClusterComp"; std::string mClustMCTruthBranchName = "ClusterMCTruth"; - std::string mClustMC2ROFBranchName = "ClustersMC2ROF"; }; class ITSClusterReader : public ClusterReader { public: - ITSClusterReader(bool useMC = true, bool usePatterns = true, bool triggerOut = true) - : ClusterReader(useMC, usePatterns, triggerOut) {} + ITSClusterReader(bool useMC = true, bool doStag = false, bool usePatterns = true, bool triggerOut = true) + : ClusterReader(useMC, doStag, usePatterns, triggerOut) {} }; class MFTClusterReader : public ClusterReader { public: - MFTClusterReader(bool useMC = true, bool usePatterns = true, bool triggerOut = true) - : ClusterReader(useMC, usePatterns, triggerOut) {} + MFTClusterReader(bool useMC = true, bool doStag = false, bool usePatterns = true, bool triggerOut = true) + : ClusterReader(useMC, doStag, usePatterns, triggerOut) {} }; /// create a processor spec /// read ITS/MFT cluster data from a root file -framework::DataProcessorSpec getITSClusterReaderSpec(bool useMC = true, bool usePatterns = true, bool useTriggers = true); -framework::DataProcessorSpec getMFTClusterReaderSpec(bool useMC = true, bool usePatterns = true, bool useTriggers = true); +framework::DataProcessorSpec getITSClusterReaderSpec(bool useMC = true, bool doStag = false, bool usePatterns = true, bool useTriggers = true); +framework::DataProcessorSpec getMFTClusterReaderSpec(bool useMC = true, bool doStag = false, bool usePatterns = true, bool useTriggers = true); } // namespace o2::itsmft diff --git a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterWriterSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterWriterSpec.h index 5ae371e7e09c4..6607c05fb141d 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterWriterSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterWriterSpec.h @@ -20,9 +20,9 @@ namespace o2::itsmft { template -framework::DataProcessorSpec getClusterWriterSpec(bool useMC); -framework::DataProcessorSpec getITSClusterWriterSpec(bool useMC); -framework::DataProcessorSpec getMFTClusterWriterSpec(bool useMC); +framework::DataProcessorSpec getClusterWriterSpec(bool useMC, bool doStag); +framework::DataProcessorSpec getITSClusterWriterSpec(bool useMC, bool doStag); +framework::DataProcessorSpec getMFTClusterWriterSpec(bool useMC, bool doStag); } // namespace o2::itsmft diff --git a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClustererSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClustererSpec.h index b6ebc282c2a27..5535ecb42d645 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClustererSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClustererSpec.h @@ -18,7 +18,6 @@ #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" #include "ITSMFTReconstruction/Clusterer.h" -#include "ITSMFTBase/DPLAlpideParam.h" using namespace o2::framework; @@ -30,10 +29,9 @@ 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(std::shared_ptr gr, bool useMC, bool doStag); ~ClustererDPL() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -48,12 +46,13 @@ class ClustererDPL : public Task int mNThreads = 1; std::unique_ptr mClusterer = nullptr; std::shared_ptr mGGCCDBRequest; - int mLayers{NLayers}; + bool mDoStaggering = false; + int mLayers = 1; std::vector mFilter; }; -framework::DataProcessorSpec getITSClustererSpec(bool useMC); -framework::DataProcessorSpec getMFTClustererSpec(bool useMC); +framework::DataProcessorSpec getITSClustererSpec(bool useMC, bool doStag); +framework::DataProcessorSpec getMFTClustererSpec(bool useMC, bool doStag); } // namespace o2::itsmft diff --git a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/DigitReaderSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/DigitReaderSpec.h index 348ba76468144..2954c27af886e 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/DigitReaderSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/DigitReaderSpec.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. // @@ -14,9 +14,12 @@ #ifndef O2_ITSMFT_DIGITREADER #define O2_ITSMFT_DIGITREADER -#include "TFile.h" -#include "TTree.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include + +#include +#include + +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DataFormatsITSMFT/Digit.h" #include "DataFormatsITSMFT/GBTCalibData.h" #include "DataFormatsITSMFT/ROFRecord.h" @@ -41,11 +44,9 @@ 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(bool useMC, bool useCalib, bool triggerOut); + DigitReader(bool useMC, bool doStag, bool useCalib, bool triggerOut); ~DigitReader() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -56,22 +57,23 @@ class DigitReader : public Task void setBranchAddress(const std::string& base, Ptr& addr, int layer = -1); std::string getBranchName(const std::string& base, int index); - std::array*, NLayers> mDigits; + std::vector*> mDigits{nullptr}; std::vector mCalib, *mCalibPtr = &mCalib; - std::array*, NLayers> mDigROFRec; - std::array*, NLayers> mDigMC2ROFs; - std::array, NLayers> mConstLabels; - std::array mPLabels; + std::vector*> mDigROFRec{nullptr}; + std::vector> mConstLabels{}; + std::vector mPLabels{nullptr}; 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 mUseIRFrames = false; // selected IRFrames modes + bool mUseMC = true; // use MC truth + bool mDoStaggering = false; // read staggered data + 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; + int mLayers = 1; std::string mDetName; std::string mDetNameLC; std::string mFileName; @@ -81,27 +83,26 @@ class DigitReader : public Task std::string mCalibBranchName = "Calib"; std::string mDigitMCTruthBranchName = "DigitMCTruth"; - std::string mDigitMC2ROFBranchName = "DigitMC2ROF"; }; class ITSDigitReader : public DigitReader { public: - ITSDigitReader(bool useMC = true, bool useCalib = false, bool useTriggers = true) - : DigitReader(useMC, useCalib, useTriggers) {} + ITSDigitReader(bool useMC = true, bool doStag = false, bool useCalib = false, bool useTriggers = true) + : DigitReader(useMC, doStag, useCalib, useTriggers) {} }; class MFTDigitReader : public DigitReader { public: - MFTDigitReader(bool useMC = true, bool useCalib = false, bool useTriggers = true) - : DigitReader(useMC, useCalib, useTriggers) {} + MFTDigitReader(bool useMC = true, bool doStag = false, bool useCalib = false, bool useTriggers = true) + : DigitReader(useMC, doStag, useCalib, useTriggers) {} }; /// create a processor spec /// read ITS/MFT Digit data from a root file -framework::DataProcessorSpec getITSDigitReaderSpec(bool useMC = true, bool useCalib = false, bool useTriggers = true, std::string defname = "o2_itsdigits.root"); -framework::DataProcessorSpec getMFTDigitReaderSpec(bool useMC = true, bool useCalib = false, bool useTriggers = true, std::string defname = "o2_mftdigits.root"); +framework::DataProcessorSpec getITSDigitReaderSpec(bool useMC = true, bool doStag = false, bool useCalib = false, bool useTriggers = true, std::string defname = "itsdigits.root"); +framework::DataProcessorSpec getMFTDigitReaderSpec(bool useMC = true, bool doStag = false, bool useCalib = false, bool useTriggers = true, std::string defname = "mftdigits.root"); } // namespace itsmft } // namespace o2 diff --git a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/DigitWriterSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/DigitWriterSpec.h index 7bef1643ddcbb..6fde609f1ccb5 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/DigitWriterSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/DigitWriterSpec.h @@ -19,8 +19,8 @@ namespace o2 namespace itsmft { -o2::framework::DataProcessorSpec getITSDigitWriterSpec(bool mctruth = true, bool dec = false, bool calib = false); -o2::framework::DataProcessorSpec getMFTDigitWriterSpec(bool mctruth = true, bool dec = false, bool calib = false); +o2::framework::DataProcessorSpec getITSDigitWriterSpec(bool mctruth = true, bool doStag = false, bool dec = false, bool calib = false); +o2::framework::DataProcessorSpec getMFTDigitWriterSpec(bool mctruth = true, bool doStag = false, bool dec = false, bool calib = false); } // end namespace itsmft } // end namespace o2 diff --git a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyDecoderSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyDecoderSpec.h index a64f2bf8c063c..6862e96c17afe 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyDecoderSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyDecoderSpec.h @@ -29,38 +29,39 @@ namespace o2 namespace itsmft { +template class EntropyDecoderSpec : public o2::framework::Task { public: - EntropyDecoderSpec(o2::header::DataOrigin orig, int verbosity, bool getDigits = false, const std::string& ctfdictOpt = "none"); + EntropyDecoderSpec(int verbosity, bool doStag, 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; void endOfStream(o2::framework::EndOfStreamContext& ec) final; void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) final; - static auto getName(o2::header::DataOrigin orig) { return std::string{orig == o2::header::gDataOriginITS ? ITSDeviceName : MFTDeviceName}; } + static std::string getBinding(const std::string& name, int spec); + 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}; private: void updateTimeDependentParams(o2::framework::ProcessingContext& pc); - static constexpr std::string_view ITSDeviceName = "its-entropy-decoder"; - static constexpr std::string_view MFTDeviceName = "mft-entropy-decoder"; - o2::header::DataOrigin mOrigin = o2::header::gDataOriginInvalid; - o2::itsmft::CTFCoder mCTFCoder; + o2::itsmft::CTFCoder mCTFCoder; const NoiseMap* mNoiseMap = nullptr; LookUp mPattIdConverter; + bool mDoStaggering{false}; bool mGetDigits{false}; bool mMaskNoise{false}; bool mUseClusterDictionary{true}; - std::string mDetPrefix{}; std::string mCTFDictPath{}; TStopwatch mTimer; }; /// create a processor spec -framework::DataProcessorSpec getEntropyDecoderSpec(o2::header::DataOrigin orig, int verbosity, bool getDigits, unsigned int sspec, const std::string& ctfdictOpt); +framework::DataProcessorSpec getITSEntropyDecoderSpec(int verbosity, bool doStag, bool getDigits, unsigned int sspec, const std::string& ctfdictOpt); +framework::DataProcessorSpec getMFTEntropyDecoderSpec(int verbosity, bool doStag, 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 588cae6339489..597c0ca63f489 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyEncoderSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/EntropyEncoderSpec.h @@ -27,10 +27,12 @@ namespace o2 namespace itsmft { +template class EntropyEncoderSpec : public o2::framework::Task { + public: - EntropyEncoderSpec(o2::header::DataOrigin orig, bool selIR, const std::string& ctfdictOpt = "none"); + EntropyEncoderSpec(bool doStag, bool selIR, const std::string& ctfdictOpt = "none"); ~EntropyEncoderSpec() override = default; void run(o2::framework::ProcessingContext& pc) final; void init(o2::framework::InitContext& ic) final; @@ -38,17 +40,21 @@ class EntropyEncoderSpec : public o2::framework::Task void updateTimeDependentParams(o2::framework::ProcessingContext& pc); void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) final; + static std::string getBinding(const std::string& name, int spec); + 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}; + private: - o2::header::DataOrigin mOrigin = o2::header::gDataOriginInvalid; - o2::itsmft::CTFCoder mCTFCoder; + o2::itsmft::CTFCoder mCTFCoder; LookUp mPattIdConverter; - int mStrobeLength = 0; bool mSelIR = false; + bool mDoStaggering = false; TStopwatch mTimer; }; /// create a processor spec -framework::DataProcessorSpec getEntropyEncoderSpec(o2::header::DataOrigin orig, bool selIR = false, const std::string& ctfdictOpt = "none"); +framework::DataProcessorSpec getITSEntropyEncoderSpec(bool doStag = false, bool selIR = false, const std::string& ctfdictOpt = "none"); +framework::DataProcessorSpec getMFTEntropyEncoderSpec(bool doStag = false, bool selIR = false, const std::string& ctfdictOpt = "none"); } // namespace itsmft } // namespace o2 diff --git a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/STFDecoderSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/STFDecoderSpec.h index a6876c456842d..29b9f75bcbc4e 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/STFDecoderSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/STFDecoderSpec.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. // @@ -16,12 +16,14 @@ #ifndef O2_ITSMFT_STFDECODER_ #define O2_ITSMFT_STFDECODER_ +#include +#include +#include #include #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" -#include -#include -#include +#include "DataFormatsITSMFT/DPLAlpideParam.h" +#include "DataFormatsITSMFT/ROFRecord.h" #include "ITSMFTReconstruction/ChipMappingITS.h" #include "ITSMFTReconstruction/ChipMappingMFT.h" #include "ITSMFTReconstruction/RawPixelDecoder.h" @@ -44,6 +46,7 @@ struct STFDecoderInp { bool doDigits = false; bool doCalib = false; bool doSquashing = false; + bool doStaggering = false; bool askSTFDist = true; bool allowReporting = true; bool verifyDecoder = false; @@ -55,6 +58,8 @@ struct STFDecoderInp { template class STFDecoder : public Task { + using AlpideParam = DPLAlpideParam; + public: STFDecoder(const STFDecoderInp& inp, std::shared_ptr gr); STFDecoder() = default; @@ -70,11 +75,14 @@ class STFDecoder : public Task void finalize(); void reset(); std::unique_ptr setupClusterer(const std::string& dictName); + void ensureContinuousROF(const std::vector& in, std::vector& out, int lr, int nROFsTF, const char* name); + TStopwatch mTimer; bool mDoClusters = false; bool mDoPatterns = false; bool mDoDigits = false; bool mDoCalibData = false; + bool mDoStaggering = false; bool mUnmutExtraLanes = false; bool mFinalizeDone = false; bool mAllowReporting = true; @@ -85,18 +93,20 @@ class STFDecoder : public Task int mDumpOnError = 0; int mNThreads = 1; int mVerbosity = 0; + int mLayers = 1; long mROFErrRepIntervalMS = 0; size_t mTFCounter = 0; - size_t mEstNDig = 0; - size_t mEstNClus = 0; - size_t mEstNClusPatt = 0; - size_t mEstNCalib = 0; - size_t mEstNROF = 0; + uint32_t mFirstTFOrbit = 0; + o2::InteractionRecord mFirstIR; + std::vector mEstNDig{0}; + std::vector mEstNClus{0}; + std::vector mEstNClusPatt{0}; + std::vector mEstNCalib{0}; size_t mMaxRawDumpsSize = 0; size_t mRawDumpedSize = 0; std::string mInputSpec; std::string mSelfName; - std::unique_ptr> mDecoder; + std::vector>> mDecoder; std::unique_ptr mClusterer; std::shared_ptr mGGCCDBRequest; }; diff --git a/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx b/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx index bc6418a077810..bd24c9d2591d5 100644 --- a/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx @@ -20,7 +20,7 @@ #include "Framework/ConfigParamRegistry.h" #include "Framework/Logger.h" #include "ITSMFTWorkflow/ClusterReaderSpec.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DataFormatsITSMFT/PhysTrigger.h" #include "CommonUtils/NameConf.h" @@ -33,15 +33,16 @@ namespace itsmft { template -ClusterReader::ClusterReader(bool useMC, bool usePatterns, bool triggerOut) : mUseMC(useMC), mUsePatterns(usePatterns), mTriggerOut(triggerOut), mDetName(Origin.as()), mDetNameLC(mDetName) +ClusterReader::ClusterReader(bool useMC, bool doStag, bool usePatterns, bool triggerOut) : mUseMC(useMC), mUsePatterns(usePatterns), mTriggerOut(triggerOut), mDetName(Origin.as()), mDetNameLC(mDetName) { std::transform(mDetNameLC.begin(), mDetNameLC.end(), mDetNameLC.begin(), ::tolower); - - mClusROFRec.fill(nullptr); - mClusterCompArray.fill(nullptr); - mPatternsArray.fill(nullptr); - mClusterMCTruth.fill(nullptr); - mClusMC2ROFs.fill(nullptr); + if (doStag) { + mLayers = DPLAlpideParam::getNLayers(); + mClusROFRec.resize(mLayers, nullptr); + mClusterCompArray.resize(mLayers, nullptr); + mPatternsArray.resize(mLayers, nullptr); + mClusterMCTruth.resize(mLayers, nullptr); + } } template @@ -59,8 +60,8 @@ void ClusterReader::run(ProcessingContext& pc) assert(ent < mTree->GetEntries()); // this should not happen mTree->GetEntry(ent); - 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; + for (uint32_t iLayer = 0; iLayer < mLayers; ++iLayer) { + LOG(info) << mDetName << "ClusterReader" << (mDoStaggering ? std::format(" on layer {}", 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) { @@ -68,7 +69,6 @@ void ClusterReader::run(ProcessingContext& pc) } if (mUseMC) { pc.outputs().snapshot(Output{Origin, "CLUSTERSMCTR", iLayer}, *mClusterMCTruth[iLayer]); - pc.outputs().snapshot(Output{Origin, "CLUSTERSMC2ROF", iLayer}, *mClusMC2ROFs[iLayer]); } } if (mTriggerOut) { @@ -90,17 +90,15 @@ void ClusterReader::connectTree(const std::string& filename) mTree.reset((TTree*)mFile->Get(mClusTreeName.c_str())); assert(mTree); - for (uint32_t iLayer = 0; iLayer < NLayers; ++iLayer) { + for (uint32_t iLayer = 0; iLayer < mLayers; ++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())) { + if (mTree->GetBranch(getBranchName(mClustMCTruthBranchName, iLayer).c_str())) { setBranchAddress(mClustMCTruthBranchName, mClusterMCTruth[iLayer], iLayer); - setBranchAddress(mClustMC2ROFBranchName, mClusMC2ROFs[iLayer], iLayer); } else { LOG(info) << "MC-truth is missing"; mUseMC = false; @@ -113,7 +111,7 @@ void ClusterReader::connectTree(const std::string& filename) template std::string ClusterReader::getBranchName(const std::string& base, int index) const { - if constexpr (o2::itsmft::DPLAlpideParam::supportsStaggering()) { + if (mDoStaggering) { return mDetName + base + "_" + std::to_string(index); } return mDetName + base; @@ -132,10 +130,10 @@ void ClusterReader::setBranchAddress(const std::string& base, Ptr& addr, int namespace { template -std::vector makeOutChannels(o2::header::DataOrigin detOrig, bool mctruth, bool usePatterns, bool triggerOut) +std::vector makeOutChannels(o2::header::DataOrigin detOrig, bool mctruth, bool doStag, bool usePatterns, bool triggerOut) { std::vector outputs; - for (uint32_t iLayer = 0; iLayer < ((o2::itsmft::DPLAlpideParam::supportsStaggering()) ? o2::itsmft::DPLAlpideParam::getNLayers() : 1); ++iLayer) { + for (uint32_t iLayer = 0; iLayer < ((doStag) ? o2::itsmft::DPLAlpideParam::getNLayers() : 1); ++iLayer) { outputs.emplace_back(detOrig, "CLUSTERSROF", iLayer, Lifetime::Timeframe); outputs.emplace_back(detOrig, "COMPCLUSTERS", iLayer, Lifetime::Timeframe); if (usePatterns) { @@ -143,7 +141,6 @@ std::vector makeOutChannels(o2::header::DataOrigin detOrig, bool mct } if (mctruth) { outputs.emplace_back(detOrig, "CLUSTERSMCTR", iLayer, Lifetime::Timeframe); - outputs.emplace_back(detOrig, "CLUSTERSMC2ROF", iLayer, Lifetime::Timeframe); } } if (triggerOut) { @@ -153,25 +150,25 @@ std::vector makeOutChannels(o2::header::DataOrigin detOrig, bool mct } } // namespace -DataProcessorSpec getITSClusterReaderSpec(bool useMC, bool usePatterns, bool triggerOut) +DataProcessorSpec getITSClusterReaderSpec(bool useMC, bool doStag, bool usePatterns, bool triggerOut) { return DataProcessorSpec{ .name = "its-cluster-reader", .inputs = Inputs{}, - .outputs = makeOutChannels("ITS", useMC, usePatterns, triggerOut), - .algorithm = AlgorithmSpec{adaptFromTask(useMC, usePatterns, triggerOut)}, + .outputs = makeOutChannels("ITS", useMC, doStag, usePatterns, triggerOut), + .algorithm = AlgorithmSpec{adaptFromTask(useMC, doStag, 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) +DataProcessorSpec getMFTClusterReaderSpec(bool useMC, bool doStag, bool usePatterns, bool triggerOut) { return DataProcessorSpec{ .name = "mft-cluster-reader", .inputs = Inputs{}, - .outputs = makeOutChannels("MFT", useMC, usePatterns, triggerOut), - .algorithm = AlgorithmSpec{adaptFromTask(useMC, usePatterns, triggerOut)}, + .outputs = makeOutChannels("MFT", useMC, doStag, usePatterns, triggerOut), + .algorithm = AlgorithmSpec{adaptFromTask(useMC, doStag, 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 index c1900c346133b..e1857cbf2f775 100644 --- a/Detectors/ITSMFT/common/workflow/src/ClusterWriterSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/ClusterWriterSpec.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. // @@ -18,7 +18,7 @@ #include #include "Framework/ConcreteDataMatcher.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "ITSMFTWorkflow/ClusterWriterSpec.h" #include "DPLUtils/MakeRootTreeWriterSpec.h" #include "DataFormatsITSMFT/CompCluster.h" @@ -37,71 +37,78 @@ 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) +DataProcessorSpec getClusterWriterSpec(bool useMC, bool doStag) { 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 int nLayers = (doStag) ? DPLAlpideParam::getNLayers() : 1; const auto detName = Origin.as(); // Spectators for logging - auto compClusterSizes = std::make_shared>(); + auto compClusterSizes = std::make_shared>(nLayers, 0); 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 logger = [detName, compClusterSizes, doStag](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"; + LOG(info) << detName << "ClusterWriter" << ((doStag) ? std::format(" on layer {}", 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()) { + auto getName = [doStag](std::string base, size_t index) -> std::string { + if (doStag) { 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); }); + std::vector vecInpSpecClus, vecInpSpecPatt, vecInpSpecROF, vecInpSpecLbl; + vecInpSpecClus.reserve(nLayers); + vecInpSpecPatt.reserve(nLayers); + vecInpSpecROF.reserve(nLayers); + vecInpSpecLbl.reserve(nLayers); + for (int iLayer = 0; iLayer < nLayers; iLayer++) { + vecInpSpecClus.emplace_back(getName("compclus", iLayer), Origin, "COMPCLUSTERS", iLayer); + vecInpSpecPatt.emplace_back(getName("patterns", iLayer), Origin, "PATTERNS", iLayer); + vecInpSpecROF.emplace_back(getName("ROframes", iLayer), Origin, "CLUSTERSROF", iLayer); + vecInpSpecLbl.emplace_back(getName("labels", iLayer), Origin, "CLUSTERSMCTR", iLayer); + } + 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"}}, + BranchDefinition{vecInpSpecClus, (detName + "ClusterComp").c_str(), "compact-cluster-branch", - NLayers, + nLayers, compClustersSizeGetter, getIndex, getName}, - BranchDefinition{InputSpec{"patterns", ConcreteDataTypeMatcher{Origin, "PATTERNS"}}, + BranchDefinition{vecInpSpecPatt, (detName + "ClusterPatt").c_str(), "cluster-pattern-branch", - NLayers, + nLayers, getIndex, getName}, - BranchDefinition{InputSpec{"ROframes", ConcreteDataTypeMatcher{Origin, "CLUSTERSROF"}}, + BranchDefinition{vecInpSpecROF, (detName + "ClustersROF").c_str(), "cluster-rof-branch", - NLayers, + nLayers, logger, getIndex, getName}, - BranchDefinition{InputSpec{"labels", ConcreteDataTypeMatcher{Origin, "CLUSTERSMCTR"}}, + BranchDefinition{vecInpSpecLbl, (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), + (useMC ? nLayers : 0), getIndex, getName})(); } -framework::DataProcessorSpec getITSClusterWriterSpec(bool useMC) { return getClusterWriterSpec(useMC); } -framework::DataProcessorSpec getMFTClusterWriterSpec(bool useMC) { return getClusterWriterSpec(useMC); } +framework::DataProcessorSpec getITSClusterWriterSpec(bool useMC, bool doStag) { return getClusterWriterSpec(useMC, doStag); } +framework::DataProcessorSpec getMFTClusterWriterSpec(bool useMC, bool doStag) { return getClusterWriterSpec(useMC, doStag); } } // namespace o2::itsmft diff --git a/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx b/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx index fc0dd5dbae7da..0672f7d13bed2 100644 --- a/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/ClustererSpec.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. // @@ -12,6 +12,7 @@ /// @file ClustererSpec.cxx #include +#include #include "ITSMFTWorkflow/ClustererSpec.h" #include "Framework/ControlService.h" @@ -29,7 +30,7 @@ #include "DataFormatsParameters/GRPObject.h" #include "ITSMFTReconstruction/DigitPixelReader.h" #include "DetectorsBase/GeometryManager.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "CommonConstants/LHCConstants.h" #include "DetectorsCommonDataFormats/DetectorNameConf.h" #include "ITSMFTReconstruction/ClustererParam.h" @@ -37,6 +38,14 @@ namespace o2::itsmft { +template +ClustererDPL::ClustererDPL(std::shared_ptr gr, bool useMC, bool doStag) : mGGCCDBRequest(gr), mUseMC(useMC), mDoStaggering(doStag) +{ + if (mDoStaggering) { + mLayers = DPLAlpideParam::getNLayers(); + } +} + template void ClustererDPL::init(InitContext& ic) { @@ -48,12 +57,11 @@ void ClustererDPL::init(InitContext& ic) mDetName = Origin.as(); // prepare data filter - for (int iLayer = 0; iLayer < NLayers; ++iLayer) { + for (int iLayer = 0; iLayer < mLayers; ++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); } } } @@ -64,10 +72,9 @@ 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; + std::vector> digits(mLayers); + std::vector> rofs(mLayers); + std::vector> labelsbuffer(mLayers); for (const DataRef& ref : InputRecordWalker{pc.inputs(), mFilter}) { auto const* dh = DataRefUtils::getHeader(ref); if (DataRefUtils::match(ref, {"digits", ConcreteDataTypeMatcher{Origin, "DIGITS"}})) { @@ -79,9 +86,6 @@ void ClustererDPL::run(ProcessingContext& pc) 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 @@ -93,10 +97,10 @@ void ClustererDPL::run(ProcessingContext& pc) uint64_t nClusters{0}; TStopwatch sw; o2::itsmft::DigitPixelReader reader; - for (uint32_t iLayer{0}; iLayer < NLayers; ++iLayer) { - int layer = (DPLAlpideParam::supportsStaggering()) ? iLayer : -1; + for (uint32_t iLayer{0}; iLayer < mLayers; ++iLayer) { + int layer = (mDoStaggering) ? iLayer : -1; sw.Start(); - LOG(info) << mDetName << "Clusterer:" << layer << " pulled " << digits[iLayer].size() << " digits, in " << rofs[iLayer].size() << " RO frames"; + LOG(info) << mDetName << "Clusterer" << ((mDoStaggering) ? std::format(" on layer {}", layer) : "") << " pulled " << digits[iLayer].size() << " digits, in " << rofs[iLayer].size() << " RO frames"; mClusterer->setMaxROFDepthToSquash(mClusterer->getMaxROFDepthToSquash(layer)); o2::dataformats::ConstMCTruthContainerView labels(labelsbuffer[iLayer]); @@ -106,8 +110,7 @@ void ClustererDPL::run(ProcessingContext& pc) reader.setDigits(digits[iLayer]); reader.setROFRecords(rofs[iLayer]); if (mUseMC) { - reader.setMC2ROFRecords(mc2rofs[iLayer]); - LOG(info) << mDetName << "Clusterer:" << layer << " pulled " << labels.getNElements() << " labels "; + LOG(info) << mDetName << "Clusterer" << ((mDoStaggering) ? std::format(" on layer {}", layer) : "") << " pulled " << labels.getNElements() << " labels "; reader.setDigitsMCTruth(labels.getIndexedSize() > 0 ? &labels : nullptr); } reader.init(); @@ -131,7 +134,7 @@ void ClustererDPL::run(ProcessingContext& pc) for (int iROF{0}; iROF < nROFsTF; ++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; + int bc = iROF * par.getROFLengthInBC(iLayer) % o2::constants::lhc::LHCMaxBunches + par.getROFDelayInBC(iLayer); o2::InteractionRecord ir(bc, orb); rof.setBCData(ir); rof.setROFrame(iROF); @@ -142,13 +145,18 @@ void ClustererDPL::run(ProcessingContext& pc) for (const auto& rof : clusROFVec) { const auto& ir = rof.getBCData(); if (ir < firstIR) { - LOGP(warn, "Discard ROF {} preceding TF 1st orbit {}, layer:{}", ir.asString(), firstTForbit, iLayer); + LOGP(warn, "Discard ROF {} preceding TF 1st orbit {}{}", ir.asString(), firstTForbit, ((mDoStaggering) ? std::format(" on layer {}", layer) : "")); + continue; + } + auto irToFirst = ir - firstIR; + if (irToFirst.toLong() - par.getROFDelayInBC(iLayer) < 0) { + LOGP(warn, "Discard ROF {} preceding TF 1st orbit {} due to imposed ROF delay{}", ir.asString(), firstTForbit, ((mDoStaggering) ? std::format(" on layer {}", iLayer) : "")); continue; } - const auto irToFirst = ir - firstIR; + irToFirst -= par.getROFDelayInBC(iLayer); const long irROF = irToFirst.toLong() / par.getROFLengthInBC(iLayer); if (irROF >= nROFsTF) { - LOGP(warn, "Discard ROF {} exceding TF orbit range, layer:{}", ir.asString(), iLayer); + LOGP(warn, "Discard ROF {} exceeding TF orbit range{}", ir.asString(), ((mDoStaggering) ? std::format(" on layer {}", layer) : "")); continue; } auto& expROF = expClusRofVec[irROF]; @@ -157,11 +165,11 @@ void ClustererDPL::run(ProcessingContext& pc) expROF.setNEntries(rof.getNEntries()); } else { if (expROF.getNEntries() < rof.getNEntries()) { - LOGP(warn, "Repeating ROF {} with {} clusters, prefer to already processed instance with {} clusters", rof.asString(), rof.getNEntries(), expROF.getNEntries()); + LOGP(warn, "Repeating {} with {} clusters, prefer to already processed instance with {} clusters{}", rof.asString(), rof.getNEntries(), expROF.getNEntries(), ((mDoStaggering) ? std::format(" on layer {}", layer) : "")); expROF.setFirstEntry(rof.getFirstEntry()); expROF.setNEntries(rof.getNEntries()); } else { - LOGP(warn, "Repeating ROF {} with {} clusters, discard preferring already processed instance with {} clusters", rof.asString(), rof.getNEntries(), expROF.getNEntries()); + LOGP(warn, "Repeating {} with {} clusters, discard preferring already processed instance with {} clusters{}", rof.asString(), rof.getNEntries(), expROF.getNEntries(), ((mDoStaggering) ? std::format(" on layer {}", layer) : "")); } } } @@ -182,18 +190,11 @@ void ClustererDPL::run(ProcessingContext& pc) 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" << ((mDoStaggering) ? std::format(": {}", iLayer) : "") << " pushed " << clusCompVec.size() << " clusters, in " << nROFs << " RO frames in " << sw.RealTime() << " s"; } LOG(info) << mDetName << "Clusterer produced " << nClusters << " clusters"; @@ -230,9 +231,9 @@ void ClustererDPL::updateTimeDependentParams(ProcessingContext& pc) nROFsToSquash = 2 + int(clParams.maxSOTMUS / (rofBC * o2::constants::lhc::LHCBunchSpacingMUS)); // use squashing } mClusterer->setMaxROFDepthToSquash(nROFsToSquash); - if constexpr (DPLAlpideParam::supportsStaggering()) { + if (mDoStaggering) { if (mClusterer->isContinuousReadOut()) { - for (int iLayer{0}; iLayer < NLayers; ++iLayer) { + for (int iLayer{0}; iLayer < mLayers; ++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); } @@ -275,17 +276,16 @@ void ClustererDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) namespace { template -DataProcessorSpec getClustererSpec(bool useMC) +DataProcessorSpec getClustererSpec(bool useMC, bool doStag) { 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; + uint32_t nLayers = doStag ? 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")); @@ -306,28 +306,27 @@ DataProcessorSpec getClustererSpec(bool useMC) 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)}, + .algorithm = AlgorithmSpec{adaptFromTask>(ggRequest, useMC, doStag)}, .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) +framework::DataProcessorSpec getITSClustererSpec(bool useMC, bool doStag) { - return getClustererSpec(useMC); + return getClustererSpec(useMC, doStag); } -framework::DataProcessorSpec getMFTClustererSpec(bool useMC) +framework::DataProcessorSpec getMFTClustererSpec(bool useMC, bool doStag) { - return getClustererSpec(useMC); + return getClustererSpec(useMC, doStag); } } // namespace o2::itsmft diff --git a/Detectors/ITSMFT/common/workflow/src/DigitReaderSpec.cxx b/Detectors/ITSMFT/common/workflow/src/DigitReaderSpec.cxx index ec86da4833a0d..6a57933f18048 100644 --- a/Detectors/ITSMFT/common/workflow/src/DigitReaderSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/DigitReaderSpec.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. // @@ -13,14 +13,15 @@ #include #include +#include -#include "TTree.h" +#include #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/Logger.h" #include "ITSMFTWorkflow/DigitReaderSpec.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "ITSMFTReconstruction/ChipMappingITS.h" #include "ITSMFTReconstruction/ChipMappingMFT.h" #include "SimulationDataFormat/MCCompLabel.h" @@ -41,22 +42,21 @@ namespace itsmft { template -DigitReader::DigitReader(bool useMC, bool useCalib, bool triggerOut) : mUseMC(useMC), mUseCalib(useCalib), mTriggerOut(triggerOut), mDetNameLC(mDetName = ID.getName()), mDigTreeName("o2sim") +DigitReader::DigitReader(bool useMC, bool doStag, bool useCalib, bool triggerOut) : mUseMC(useMC), mDoStaggering(doStag), mUseCalib(useCalib), mTriggerOut(triggerOut), mDetNameLC(mDetName = ID.getName()), mDigTreeName("o2sim") { mDigitBranchName = mDetName + mDigitBranchName; mDigitROFBranchName = mDetName + mDigitROFBranchName; mCalibBranchName = mDetName + mCalibBranchName; 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; + if (mDoStaggering) { + mLayers = DPLAlpideParam::getNLayers(); + mDigits.resize(mLayers, nullptr); + mDigROFRec.resize(mLayers, nullptr); + mPLabels.resize(mLayers, nullptr); } } @@ -103,8 +103,8 @@ void DigitReader::run(ProcessingContext& pc) ent++; assert(ent < mTree->GetEntries()); // this should not happen mTree->GetEntry(ent); - 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; + for (uint32_t iLayer = 0; iLayer < mLayers; ++iLayer) { + LOG(info) << mDetName << "DigitReader" << ((mDoStaggering) ? std::format(": {}", 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) { @@ -112,7 +112,6 @@ void DigitReader::run(ProcessingContext& pc) mPLabels[iLayer]->copyandflatten(sharedlabels); delete mPLabels[iLayer]; mPLabels[iLayer] = nullptr; - pc.outputs().snapshot(Output{Origin, "DIGITSMC2ROF", iLayer}, *mDigMC2ROFs[iLayer]); } } if (mUseCalib) { @@ -131,7 +130,6 @@ void DigitReader::run(ProcessingContext& pc) std::vector digitsSel; std::vector calibSel; std::vector digROFRecSel; - std::vector digMC2ROFsSel; o2::dataformats::MCTruthContainer digitLabelsSel; if (irFrames.size()) { // we assume the IRFrames are in the increasing order @@ -181,26 +179,6 @@ void DigitReader::run(ProcessingContext& pc) } } } - if (mUseMC) { - digMC2ROFsSel = *mDigMC2ROFs[0]; - for (auto& mc2rof : digMC2ROFsSel) { - if (mc2rof.rofRecordID < 0) { - continue; // did not contribute even to the original data - } - unsigned int mn = 0xffff, mx = 0; - for (int ir = mc2rof.minROF; ir <= mc2rof.maxROF; ir++) { - if (rofOld2New[ir] >= 0) { // used - mx = rofOld2New[ir]; - if (mn > mx) { - mn = mx; - } - } - } - mc2rof.rofRecordID = mn == 0xffff ? -1 : int(mn); - mc2rof.minROF = mn; - mc2rof.maxROF = mx; - } - } if (mDigROFRec[0]->back().getBCData() + mROFLengthInBC - 1 < irMax) { // need to check the next entry ent++; continue; @@ -220,7 +198,6 @@ void DigitReader::run(ProcessingContext& pc) if (mUseMC) { auto& sharedlabels = pc.outputs().make>(Output{Origin, "DIGITSMCTR", 0}); digitLabelsSel.flatten_to(sharedlabels); - pc.outputs().snapshot(Output{Origin, "DIGITSMC2ROF", 0}, digMC2ROFsSel); } if (!irFrames.size() || irFrames.back().isLast()) { @@ -238,14 +215,13 @@ void DigitReader::connectTree(const std::string& filename) assert(mFile && !mFile->IsZombie()); mTree.reset((TTree*)mFile->Get(mDigTreeName.c_str())); assert(mTree); - for (uint32_t iLayer = 0; iLayer < RLayers; ++iLayer) { + for (uint32_t iLayer = 0; iLayer < mLayers; ++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())) { + if (!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); } @@ -263,10 +239,10 @@ void DigitReader::connectTree(const std::string& filename) template std::string DigitReader::getBranchName(const std::string& base, int index) { - if constexpr (!o2::itsmft::DPLAlpideParam::supportsStaggering()) { - return base; + if (mDoStaggering) { + return base + "_" + std::to_string(index); } - return base + "_" + std::to_string(index); + return base; } template @@ -282,16 +258,15 @@ void DigitReader::setBranchAddress(const std::string& base, Ptr& addr, int la namespace { template -std::vector makeOutChannels(bool mctruth, bool useCalib) +std::vector makeOutChannels(bool mctruth, bool doStag, 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) { + int nLayers = doStag ? o2::itsmft::DPLAlpideParam::getNLayers() : 1; + for (int iLayer = 0; iLayer < nLayers; ++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); } } @@ -303,25 +278,25 @@ std::vector makeOutChannels(bool mctruth, bool useCalib) } } // namespace -DataProcessorSpec getITSDigitReaderSpec(bool useMC, bool useCalib, bool useTriggers, std::string defname) +DataProcessorSpec getITSDigitReaderSpec(bool useMC, bool doStag, bool useCalib, bool useTriggers, std::string defname) { return DataProcessorSpec{ .name = "its-digit-reader", .inputs = Inputs{}, - .outputs = makeOutChannels(useMC, useCalib), - .algorithm = AlgorithmSpec{adaptFromTask(useMC, useCalib)}, + .outputs = makeOutChannels(useMC, doStag, useCalib), + .algorithm = AlgorithmSpec{adaptFromTask(useMC, doStag, useCalib, useTriggers)}, .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) +DataProcessorSpec getMFTDigitReaderSpec(bool useMC, bool doStag, bool useCalib, bool useTriggers, std::string defname) { return DataProcessorSpec{ .name = "mft-digit-reader", .inputs = Inputs{}, - .outputs = makeOutChannels(useMC, useCalib), - .algorithm = AlgorithmSpec{adaptFromTask(useMC, useCalib)}, + .outputs = makeOutChannels(useMC, doStag, useCalib), + .algorithm = AlgorithmSpec{adaptFromTask(useMC, doStag, useCalib, useTriggers)}, .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 c4f1e336180c7..d409356c6846f 100644 --- a/Detectors/ITSMFT/common/workflow/src/DigitWriterSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/DigitWriterSpec.cxx @@ -14,7 +14,7 @@ #include "ITSMFTWorkflow/DigitWriterSpec.h" #include "Framework/ConcreteDataMatcher.h" #include "Framework/DataRef.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DPLUtils/MakeRootTreeWriterSpec.h" #include "DataFormatsITSMFT/Digit.h" #include "DataFormatsITSMFT/GBTCalibData.h" @@ -43,20 +43,20 @@ using MCCont = o2::dataformats::ConstMCTruthContainer; /// create the processor spec /// describing a processor receiving digits for ITS/MFT and writing them to file template -DataProcessorSpec getDigitWriterSpec(bool mctruth, bool dec, bool calib) +DataProcessorSpec getDigitWriterSpec(bool mctruth, bool doStag, bool dec, bool calib) { 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; + int mLayers = doStag ? 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 digitSizes = std::make_shared>(); + auto digitSizes = std::make_shared>(mLayers, 0); 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 rofSizes = std::make_shared>(mLayers, 0); auto rofSizeGetter = [rofSizes](std::vector const& inROFs, DataRef const& ref) { auto const* dh = DataRefUtils::getHeader(ref); (*rofSizes)[dh->subSpecification] = inROFs.size(); @@ -84,11 +84,11 @@ DataProcessorSpec getDigitWriterSpec(bool mctruth, bool dec, bool calib) // 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 = [digitSizes, rofSizes](TBranch& branch, std::vector const& labelbuffer, DataRef const& ref) { + auto fillLabels = [detStr, doStag, digitSizes, rofSizes](TBranch& branch, std::vector const& labelbuffer, DataRef const& ref) { o2::dataformats::ConstMCTruthContainerView labels(labelbuffer); 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"; + LOG(info) << detStr << ": WRITING " << labels.getNElements() << " LABELS" << (doStag ? std::format(" FOR LAYER {}", layer) : "") << " WITH " << (*digitSizes)[layer] << " DIGITS IN " << (*rofSizes)[layer] << " ROFS"; o2::dataformats::IOMCTruthContainerView outputcontainer; auto ptr = &outputcontainer; @@ -102,52 +102,58 @@ DataProcessorSpec getDigitWriterSpec(bool mctruth, bool dec, bool calib) 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()) { + auto getName = [doStag](std::string base, size_t index) -> std::string { + if (doStag) { return base += "_" + std::to_string(index); } return base; }; + + std::vector vecInpSpecDig, vecInpSpecROF, vecInpSpecLbl; + vecInpSpecDig.reserve(mLayers); + vecInpSpecROF.reserve(mLayers); + vecInpSpecLbl.reserve(mLayers); + for (int iLayer = 0; iLayer < mLayers; iLayer++) { + vecInpSpecDig.emplace_back(getName(detStr + "digits", iLayer), Origin, "DIGITS", iLayer); + vecInpSpecROF.emplace_back(getName(detStr + "digitsROF", iLayer), Origin, "DIGITSROF", iLayer); + vecInpSpecLbl.emplace_back(getName(detStr + "_digitsMCTR", iLayer), Origin, "DIGITSMCTR", iLayer); + } + return MakeRootTreeWriterSpec((detStr + "DigitWriter" + (dec ? "_dec" : "")).c_str(), (detStrL + "digits.root").c_str(), MakeRootTreeWriterSpec::TreeAttributes{.name = "o2sim", .title = detStr + " Digits tree"}, MakeRootTreeWriterSpec::CustomClose(finishWriting), - BranchDefinition>{InputSpec{detStr + "digits", ConcreteDataTypeMatcher{Origin, "DIGITS"}}, + BranchDefinition>{vecInpSpecDig, detStr + "Digit", "digit-branch", - NLayers, + mLayers, digitSizeGetter, getIndex, getName}, - BranchDefinition>{InputSpec{detStr + "digitsROF", ConcreteDataTypeMatcher{Origin, "DIGITSROF"}}, + BranchDefinition>{vecInpSpecROF, detStr + "DigitROF", "digit-rof-branch", - NLayers, + mLayers, rofSizeGetter, getIndex, getName}, - BranchDefinition>{InputSpec{detStr + "_digitsMCTR", ConcreteDataTypeMatcher{Origin, "DIGITSMCTR"}}, + BranchDefinition>{vecInpSpecLbl, detStr + "DigitMCTruth", "digit-mctruth-branch", - (mctruth ? NLayers : 0), + (mctruth ? mLayers : 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) +DataProcessorSpec getITSDigitWriterSpec(bool mctruth, bool doStag, bool dec, bool calib) { - return getDigitWriterSpec(mctruth, dec, calib); + return getDigitWriterSpec(mctruth, doStag, dec, calib); } -DataProcessorSpec getMFTDigitWriterSpec(bool mctruth, bool dec, bool calib) +DataProcessorSpec getMFTDigitWriterSpec(bool mctruth, bool doStag, bool dec, bool calib) { - return getDigitWriterSpec(mctruth, dec, calib); + return getDigitWriterSpec(mctruth, doStag, dec, calib); } } // end namespace itsmft diff --git a/Detectors/ITSMFT/common/workflow/src/EntropyDecoderSpec.cxx b/Detectors/ITSMFT/common/workflow/src/EntropyDecoderSpec.cxx index f90b708af1996..1107ca2fd34f6 100644 --- a/Detectors/ITSMFT/common/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/EntropyDecoderSpec.cxx @@ -20,6 +20,7 @@ #include "ITSMFTWorkflow/EntropyDecoderSpec.h" #include "ITSMFTReconstruction/ClustererParam.h" #include "DetectorsCommonDataFormats/DetectorNameConf.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DataFormatsITSMFT/PhysTrigger.h" using namespace o2::framework; @@ -28,25 +29,33 @@ namespace o2 { namespace itsmft { -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) + +template +std::string EntropyDecoderSpec::getBinding(const std::string& name, int spec) +{ + return fmt::format("{}_{}", name, spec); +} + +template +EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, bool doStag, bool getDigits, const std::string& ctfdictOpt) + : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Decoder, doStag, ctfdictOpt), mDoStaggering(doStag), mGetDigits(getDigits) { - assert(orig == o2::header::gDataOriginITS || orig == o2::header::gDataOriginMFT); - mDetPrefix = orig == o2::header::gDataOriginITS ? "_ITS" : "_MFT"; mTimer.Stop(); mTimer.Reset(); mCTFCoder.setVerbosity(verbosity); - mCTFCoder.setDictBinding(std::string("ctfdict") + mDetPrefix); + mCTFCoder.setDictBinding(std::string("ctfdict_") + ID.getName()); } -void EntropyDecoderSpec::init(o2::framework::InitContext& ic) +template +void EntropyDecoderSpec::init(o2::framework::InitContext& ic) { - mCTFCoder.init(ic); + mCTFCoder.template init(ic); mMaskNoise = ic.options().get("mask-noise"); mUseClusterDictionary = !ic.options().get("ignore-cluster-dictionary"); } -void EntropyDecoderSpec::run(ProcessingContext& pc) +template +void EntropyDecoderSpec::run(ProcessingContext& pc) { if (pc.services().get().globalRunNumberChanged) { mTimer.Reset(); @@ -54,105 +63,142 @@ void EntropyDecoderSpec::run(ProcessingContext& pc) auto cput = mTimer.CpuTime(); mTimer.Start(false); o2::ctf::CTFIOSize iosize; + size_t ndigcl = 0, nrofs = 0; updateTimeDependentParams(pc); - auto buff = pc.inputs().get>(std::string("ctf") + mDetPrefix); - // since the buff is const, we cannot use EncodedBlocks::relocate directly, instead we wrap its data to another flat object - // const auto ctfImage = o2::itsmft::CTF::getImage(buff.data()); - - // this produces weird memory problems in unrelated devices, to be understood - // auto& trigs = pc.outputs().make>(OutputRef{"phystrig"}); // dummy output - - auto& rofs = pc.outputs().make>(OutputRef{"ROframes"}); - if (mGetDigits) { - auto& digits = pc.outputs().make>(OutputRef{"Digits"}); - if (buff.size()) { - iosize = mCTFCoder.decode(o2::itsmft::CTF::getImage(buff.data()), rofs, digits, mNoiseMap, mPattIdConverter); + std::string nm = ID.getName(); + uint32_t nLayers = mDoStaggering ? DPLAlpideParam::getNLayers() : 1; + for (uint32_t iLayer = 0; iLayer < nLayers; iLayer++) { + auto buff = pc.inputs().get>(getBinding(nm + "CTF", iLayer)); + // since the buff is const, we cannot use EncodedBlocks::relocate directly, instead we wrap its data to another flat object + // const auto ctfImage = o2::itsmft::CTF::getImage(buff.data()); + const auto& ctf = o2::itsmft::CTF::getImage(buff.data()); + if (ctf.getHeader().maxStreams != nLayers) { + LOGP(fatal, "Number of streams {} in the CTF header is not equal to NLayers {} from AlpideParam in {}staggered mode", + ctf.getHeader().maxStreams, nLayers, mDoStaggering ? "" : "non-"); } - mTimer.Stop(); - LOG(info) << "Decoded " << digits.size() << " digits in " << rofs.size() << " RO frames, (" << iosize.asString() << ") in " << mTimer.CpuTime() - cput << " s"; - } else { - auto& compcl = pc.outputs().make>(OutputRef{"compClusters"}); - auto& patterns = pc.outputs().make>(OutputRef{"patterns"}); - if (buff.size()) { - iosize = mCTFCoder.decode(o2::itsmft::CTF::getImage(buff.data()), rofs, compcl, patterns, mNoiseMap, mPattIdConverter); + // this produces weird memory problems in unrelated devices, to be understood + // auto& trigs = pc.outputs().make>(OutputRef{"phystrig"}); // dummy output + auto& rofs = pc.outputs().make>(OutputRef{nm + "ROframes", iLayer}); + if (mGetDigits) { + auto& digits = pc.outputs().make>(OutputRef{nm + "Digits", iLayer}); + if (buff.size()) { + iosize += mCTFCoder.decode(ctf, rofs, digits, mNoiseMap, mPattIdConverter); + } + ndigcl += digits.size(); + nrofs += rofs.size(); + } else { + auto& compcl = pc.outputs().make>(OutputRef{nm + "compClusters", iLayer}); + auto& patterns = pc.outputs().make>(OutputRef{nm + "patterns", iLayer}); + if (buff.size()) { + iosize += mCTFCoder.decode(ctf, rofs, compcl, patterns, mNoiseMap, mPattIdConverter); + } + ndigcl += compcl.size(); } - mTimer.Stop(); - LOG(info) << "Decoded " << compcl.size() << " clusters in " << rofs.size() << " RO frames, (" << iosize.asString() << ") in " << mTimer.CpuTime() - cput << " s"; } - pc.outputs().snapshot({"ctfrep", 0}, iosize); + pc.outputs().snapshot({nm + "ctfrep", 0}, iosize); + mTimer.Stop(); + LOGP(info, "Decoded {} {} in {} ROFs of {} streams ({}) in {}staggerd mode in {} s", ndigcl, mGetDigits ? "digits" : "clusters", + nrofs, nLayers, iosize.asString(), mDoStaggering ? "" : "non-", mTimer.CpuTime() - cput); } -void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) +template +void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) { - LOGF(info, "%s Entropy Decoding total timing: Cpu: %.3e Real: %.3e s in %d slots", - mOrigin.as(), mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); + LOGP(info, "{} Entropy Decoding total timing: Cpu: {:.3e} Real: {:.3e} s in {} slots", + Origin.as(), mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -void EntropyDecoderSpec::updateTimeDependentParams(ProcessingContext& pc) +template +void EntropyDecoderSpec::updateTimeDependentParams(ProcessingContext& pc) { + std::string nm = ID.getName(); if (pc.services().get().globalRunNumberChanged) { // this params need to be queried only once if (mMaskNoise) { - pc.inputs().get(std::string("noise") + mDetPrefix); + pc.inputs().get(nm + "noise"); } if (mGetDigits || mMaskNoise) { - pc.inputs().get(std::string("cldict") + mDetPrefix); + pc.inputs().get(nm + "cldict"); } } + pc.inputs().get*>(nm + "alppar"); mCTFCoder.updateTimeDependentParams(pc, true); } -void EntropyDecoderSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) +template +void EntropyDecoderSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) { - if (matcher == ConcreteDataMatcher(mOrigin, "NOISEMAP", 0)) { + if (matcher == ConcreteDataMatcher(Origin, "NOISEMAP", 0)) { mNoiseMap = (o2::itsmft::NoiseMap*)obj; - LOG(info) << mOrigin.as() << " noise map updated"; + LOG(info) << Origin.as() << " noise map updated"; return; } - if (matcher == ConcreteDataMatcher(mOrigin, "CLUSDICT", 0)) { - LOG(info) << mOrigin.as() << " cluster dictionary updated" << (!mUseClusterDictionary ? " but its using is disabled" : ""); + if (matcher == ConcreteDataMatcher(Origin, "CLUSDICT", 0)) { + LOG(info) << Origin.as() << " cluster dictionary updated" << (!mUseClusterDictionary ? " but its using is disabled" : ""); mPattIdConverter.setDictionary((const TopologyDictionary*)obj); return; } - if (mCTFCoder.finaliseCCDB(matcher, obj)) { + if (matcher == ConcreteDataMatcher(Origin, "ALPIDEPARAM", 0)) { + LOG(info) << "Alpide param updated"; + return; + } + if (mCTFCoder.template finaliseCCDB(matcher, obj)) { return; } } -DataProcessorSpec getEntropyDecoderSpec(o2::header::DataOrigin orig, int verbosity, bool getDigits, unsigned int sspec, const std::string& ctfdictOpt) +template +DataProcessorSpec getEntropyDecoderSpec(int verbosity, bool doStag, bool getDigits, unsigned int sspec, const std::string& ctfdictOpt) { + constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; + constexpr o2::detectors::DetID ID{N == o2::detectors::DetID::ITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT}; + uint32_t nLayers = doStag ? DPLAlpideParam::getNLayers() : 1; + + std::vector inputs; std::vector outputs; - // this is a special dummy input which makes sense only in sync workflows // this produces weird memory problems in unrelated devices, to be understood - // outputs.emplace_back(OutputSpec{{"phystrig"}, orig, "PHYSTRIG", 0, Lifetime::Timeframe}); - - if (getDigits) { - outputs.emplace_back(OutputSpec{{"Digits"}, orig, "DIGITS", 0, Lifetime::Timeframe}); - outputs.emplace_back(OutputSpec{{"ROframes"}, orig, "DIGITSROF", 0, Lifetime::Timeframe}); - } else { - outputs.emplace_back(OutputSpec{{"compClusters"}, orig, "COMPCLUSTERS", 0, Lifetime::Timeframe}); - outputs.emplace_back(OutputSpec{{"ROframes"}, orig, "CLUSTERSROF", 0, Lifetime::Timeframe}); - outputs.emplace_back(OutputSpec{{"patterns"}, orig, "PATTERNS", 0, Lifetime::Timeframe}); + // outputs.emplace_back(OutputSpec{{"phystrig"}, Origin, "PHYSTRIG", 0, Lifetime::Timeframe}); + std::string nm = ID.getName(); + for (uint32_t iLayer = 0; iLayer < nLayers; ++iLayer) { + if (getDigits) { + outputs.emplace_back(OutputSpec{{nm + "Digits"}, Origin, "DIGITS", iLayer, Lifetime::Timeframe}); + outputs.emplace_back(OutputSpec{{nm + "ROframes"}, Origin, "DIGITSROF", iLayer, Lifetime::Timeframe}); + } else { + outputs.emplace_back(OutputSpec{{nm + "compClusters"}, Origin, "COMPCLUSTERS", iLayer, Lifetime::Timeframe}); + outputs.emplace_back(OutputSpec{{nm + "ROframes"}, Origin, "CLUSTERSROF", iLayer, Lifetime::Timeframe}); + outputs.emplace_back(OutputSpec{{nm + "patterns"}, Origin, "PATTERNS", iLayer, Lifetime::Timeframe}); + } + inputs.emplace_back(EntropyDecoderSpec::getBinding(nm + "CTF", iLayer), Origin, "CTFDATA", sspec * 100 + iLayer, Lifetime::Timeframe); } - outputs.emplace_back(OutputSpec{{"ctfrep"}, orig, "CTFDECREP", 0, Lifetime::Timeframe}); - std::string nm = orig == o2::header::gDataOriginITS ? "_ITS" : "_MFT"; - std::vector inputs; - 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()))); + outputs.emplace_back(OutputSpec{{nm + "ctfrep"}, Origin, "CTFDECREP", 0, Lifetime::Timeframe}); + + inputs.emplace_back(nm + "alppar", Origin, "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Config/AlpideParam", Origin.as()))); + inputs.emplace_back(nm + "noise", Origin, "NOISEMAP", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/NoiseMap", Origin.as()))); + inputs.emplace_back(nm + "cldict", Origin, "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/ClusterDictionary", Origin.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{"ctfdict_"} + ID.getName(), Origin, "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/CTFDictionaryTree", Origin.as()))); } - inputs.emplace_back(std::string("trigoffset"), "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); + inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); return DataProcessorSpec{ - EntropyDecoderSpec::getName(orig), + Origin == o2::header::gDataOriginITS ? "its-entropy-decoder" : "mft-entropy-decoder", inputs, outputs, - AlgorithmSpec{adaptFromTask(orig, verbosity, getDigits, ctfdictOpt)}, + AlgorithmSpec{adaptFromTask>(verbosity, doStag, 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"}}}}; } + +framework::DataProcessorSpec getITSEntropyDecoderSpec(int verbosity, bool doStag, bool getDigits, unsigned int sspec, const std::string& ctfdictOpt) +{ + return getEntropyDecoderSpec(verbosity, doStag, getDigits, sspec, ctfdictOpt); +} + +framework::DataProcessorSpec getMFTEntropyDecoderSpec(int verbosity, bool doStag, bool getDigits, unsigned int sspec, const std::string& ctfdictOpt) +{ + return getEntropyDecoderSpec(verbosity, doStag, getDigits, sspec, ctfdictOpt); +} + } // namespace itsmft } // namespace o2 diff --git a/Detectors/ITSMFT/common/workflow/src/EntropyEncoderSpec.cxx b/Detectors/ITSMFT/common/workflow/src/EntropyEncoderSpec.cxx index a824184330547..f80555efed384 100644 --- a/Detectors/ITSMFT/common/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/EntropyEncoderSpec.cxx @@ -18,7 +18,7 @@ #include "Framework/CCDBParamSpec.h" #include "DataFormatsITSMFT/CompCluster.h" #include "ITSMFTWorkflow/EntropyEncoderSpec.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DetectorsCommonDataFormats/DetID.h" using namespace o2::framework; @@ -27,20 +27,31 @@ namespace o2 { namespace itsmft { -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) + +template +std::string EntropyEncoderSpec::getBinding(const std::string& name, int spec) +{ + return fmt::format("{}_{}", name, spec); +} + +template +EntropyEncoderSpec::EntropyEncoderSpec(bool doStag, bool selIR, const std::string& ctfdictOpt) + : mCTFCoder(o2::ctf::CTFCoderBase::OpType::Encoder, doStag, ctfdictOpt), + mSelIR(selIR), + mDoStaggering(doStag) { - assert(orig == o2::header::gDataOriginITS || orig == o2::header::gDataOriginMFT); mTimer.Stop(); mTimer.Reset(); } -void EntropyEncoderSpec::init(o2::framework::InitContext& ic) +template +void EntropyEncoderSpec::init(o2::framework::InitContext& ic) { - mCTFCoder.init(ic); + mCTFCoder.template init(ic); } -void EntropyEncoderSpec::run(ProcessingContext& pc) +template +void EntropyEncoderSpec::run(ProcessingContext& pc) { if (pc.services().get().globalRunNumberChanged) { mTimer.Reset(); @@ -49,14 +60,20 @@ void EntropyEncoderSpec::run(ProcessingContext& pc) mTimer.Start(false); updateTimeDependentParams(pc); - auto compClusters = pc.inputs().get>("compClusters"); - auto pspan = pc.inputs().get>("patterns"); - auto rofs = pc.inputs().get>("ROframes"); + uint32_t nLayers = mDoStaggering ? DPLAlpideParam::getNLayers() : 1; + if (mSelIR) { mCTFCoder.setSelectedIRFrames(pc.inputs().get>("selIRFrames")); } - auto& buffer = pc.outputs().make>(Output{mOrigin, "CTFDATA", 0}); - auto iosize = mCTFCoder.encode(buffer, rofs, compClusters, pspan, mPattIdConverter, mStrobeLength); + o2::ctf::CTFIOSize iosize{}; + for (uint32_t iLayer = 0; iLayer < nLayers; iLayer++) { + auto compClusters = pc.inputs().get>(getBinding("compClusters", iLayer)); + auto pspan = pc.inputs().get>(getBinding("patterns", iLayer)); + auto rofs = pc.inputs().get>(getBinding("ROframes", iLayer)); + + auto& buffer = pc.outputs().make>(Output{Origin, "CTFDATA", iLayer}); + iosize += mCTFCoder.encode(buffer, rofs, compClusters, pspan, mPattIdConverter, iLayer); + } pc.outputs().snapshot({"ctfrep", 0}, iosize); if (mSelIR) { mCTFCoder.getIRFramesSelector().clear(); @@ -65,77 +82,90 @@ void EntropyEncoderSpec::run(ProcessingContext& pc) LOG(info) << iosize.asString() << " in " << mTimer.CpuTime() - cput << " s"; } -void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) +template +void EntropyEncoderSpec::endOfStream(EndOfStreamContext& ec) { - LOGF(info, "%s Entropy Encoding total timing: Cpu: %.3e Real: %.3e s in %d slots", - mOrigin.as(), mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); + LOGP(info, "{} Entropy Encoding total timing: Cpu: {:.3e} Real: {:.3e} s in {} slots", + Origin.as(), mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -void EntropyEncoderSpec::updateTimeDependentParams(ProcessingContext& pc) +template +void EntropyEncoderSpec::updateTimeDependentParams(ProcessingContext& pc) { mCTFCoder.updateTimeDependentParams(pc, true); if (pc.services().get().globalRunNumberChanged) { // this params need to be queried only once if (mSelIR) { pc.inputs().get("cldict"); - if (mOrigin == o2::header::gDataOriginITS) { - pc.inputs().get*>("alppar"); - } else { - pc.inputs().get*>("alppar"); - } } } + pc.inputs().get*>("alppar"); } -void EntropyEncoderSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +template +void EntropyEncoderSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) { - if (matcher == ConcreteDataMatcher(mOrigin, "CLUSDICT", 0)) { - LOG(info) << mOrigin.as() << " cluster dictionary updated"; + if (matcher == ConcreteDataMatcher(Origin, "CLUSDICT", 0)) { + LOG(info) << Origin.as() << " cluster dictionary updated"; mPattIdConverter.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(mOrigin, "ALPIDEPARAM", 0)) { + if (matcher == ConcreteDataMatcher(Origin, "ALPIDEPARAM", 0)) { LOG(info) << "Alpide param updated"; - if (mOrigin == o2::header::gDataOriginITS) { - const auto& par = DPLAlpideParam::Instance(); - mStrobeLength = par.roFrameLengthInBC; - } else { - const auto& par = DPLAlpideParam::Instance(); - mStrobeLength = par.roFrameLengthInBC; - } return; } - if (mCTFCoder.finaliseCCDB(matcher, obj)) { + if (mCTFCoder.template finaliseCCDB(matcher, obj)) { return; } } -DataProcessorSpec getEntropyEncoderSpec(o2::header::DataOrigin orig, bool selIR, const std::string& ctfdictOpt) +template +DataProcessorSpec getEntropyEncoderSpec(bool doStag, bool selIR, const std::string& ctfdictOpt) { + constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; + constexpr o2::detectors::DetID ID{N == o2::detectors::DetID::ITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT}; + const auto& par = DPLAlpideParam::Instance(); + uint32_t nLayers = doStag ? DPLAlpideParam::getNLayers() : 1; + std::vector inputs; - inputs.emplace_back("compClusters", orig, "COMPCLUSTERS", 0, Lifetime::Timeframe); - inputs.emplace_back("patterns", orig, "PATTERNS", 0, Lifetime::Timeframe); - inputs.emplace_back("ROframes", orig, "CLUSTERSROF", 0, Lifetime::Timeframe); + std::vector outputs; + for (uint32_t iLayer = 0; iLayer < nLayers; ++iLayer) { + inputs.emplace_back(EntropyEncoderSpec::getBinding("compClusters", iLayer), Origin, "COMPCLUSTERS", iLayer, Lifetime::Timeframe); + inputs.emplace_back(EntropyEncoderSpec::getBinding("patterns", iLayer), Origin, "PATTERNS", iLayer, Lifetime::Timeframe); + inputs.emplace_back(EntropyEncoderSpec::getBinding("ROframes", iLayer), Origin, "CLUSTERSROF", iLayer, Lifetime::Timeframe); + outputs.emplace_back(Origin, "CTFDATA", iLayer, Lifetime::Timeframe); + } if (selIR) { inputs.emplace_back("selIRFrames", "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); - 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("cldict", Origin, "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/ClusterDictionary", Origin.as()))); } + inputs.emplace_back("alppar", Origin, "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Config/AlpideParam", Origin.as()))); if (ctfdictOpt.empty() || ctfdictOpt == "ccdb") { - inputs.emplace_back("ctfdict", orig, "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/CTFDictionaryTree", orig.as()))); + inputs.emplace_back("ctfdict", Origin, "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec(fmt::format("{}/Calib/CTFDictionaryTree", Origin.as()))); } + outputs.emplace_back(OutputSpec{{"ctfrep"}, Origin, "CTFENCREP", 0, Lifetime::Timeframe}); return DataProcessorSpec{ - orig == o2::header::gDataOriginITS ? "its-entropy-encoder" : "mft-entropy-encoder", + Origin == 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, ctfdictOpt)}, + outputs, + AlgorithmSpec{adaptFromTask>(doStag, 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"}}}}; } + +framework::DataProcessorSpec getITSEntropyEncoderSpec(bool doStag, bool selIR, const std::string& ctfdictOpt) +{ + return getEntropyEncoderSpec(doStag, selIR, ctfdictOpt); +} + +framework::DataProcessorSpec getMFTEntropyEncoderSpec(bool doStag, bool selIR, const std::string& ctfdictOpt) +{ + return getEntropyEncoderSpec(doStag, selIR, ctfdictOpt); +} + } // namespace itsmft } // namespace o2 diff --git a/Detectors/ITSMFT/common/workflow/src/STFDecoderSpec.cxx b/Detectors/ITSMFT/common/workflow/src/STFDecoderSpec.cxx index da1af34376ff1..8fb6ba4e6aa97 100644 --- a/Detectors/ITSMFT/common/workflow/src/STFDecoderSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/STFDecoderSpec.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. // @@ -17,7 +17,6 @@ #include "Framework/WorkflowSpec.h" #include "Framework/ConfigParamRegistry.h" -#include "Framework/ControlService.h" #include "Framework/DeviceSpec.h" #include "Framework/CCDBParamSpec.h" #include "DataFormatsITSMFT/Digit.h" @@ -28,8 +27,7 @@ #include "ITSMFTReconstruction/ClustererParam.h" #include "ITSMFTReconstruction/GBTLink.h" #include "ITSMFTWorkflow/STFDecoderSpec.h" -#include "DetectorsCommonDataFormats/DetectorNameConf.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DataFormatsITSMFT/CompCluster.h" #include "DetectorsCommonDataFormats/DetID.h" #include "CommonUtils/StringUtils.h" @@ -47,11 +45,18 @@ using namespace o2::framework; ///_______________________________________ template STFDecoder::STFDecoder(const STFDecoderInp& inp, std::shared_ptr gr) - : mDoClusters(inp.doClusters), mDoPatterns(inp.doPatterns), mDoDigits(inp.doDigits), mDoCalibData(inp.doCalib), mAllowReporting(inp.allowReporting), mVerifyDecoder(inp.verifyDecoder), mInputSpec(inp.inputSpec), mGGCCDBRequest(gr) + : mDoClusters(inp.doClusters), mDoPatterns(inp.doPatterns), mDoDigits(inp.doDigits), mDoCalibData(inp.doCalib), mDoStaggering(inp.doStaggering), mAllowReporting(inp.allowReporting), mVerifyDecoder(inp.verifyDecoder), mInputSpec(inp.inputSpec), mGGCCDBRequest(gr) { mSelfName = o2::utils::Str::concat_string(Mapping::getName(), "STFDecoder"); mTimer.Stop(); mTimer.Reset(); + if (mDoStaggering) { + mLayers = Mapping::NLayers; + mEstNDig.resize(mLayers, 0); + mEstNClus.resize(mLayers, 0); + mEstNClusPatt.resize(mLayers, 0); + mEstNCalib.resize(mLayers, 0); + } } ///_______________________________________ @@ -60,7 +65,6 @@ void STFDecoder::init(InitContext& ic) { o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); try { - mDecoder = std::make_unique>(); auto v0 = o2::utils::Str::tokenize(mInputSpec, ':'); auto v1 = o2::utils::Str::tokenize(v0[1], '/'); auto v2 = o2::utils::Str::tokenize(v1[1], '?'); @@ -68,9 +72,12 @@ void STFDecoder::init(InitContext& ic) header::DataDescription dataDesc; dataOrig.runtimeInit(v1[0].c_str()); dataDesc.runtimeInit(v2[0].c_str()); - mDecoder->setUserDataOrigin(dataOrig); - mDecoder->setUserDataDescription(dataDesc); - mDecoder->init(); // is this no-op? + for (int iLayer{0}; iLayer < mLayers; ++iLayer) { + auto& dec = mDecoder.emplace_back(std::make_unique>()); + dec->setUserDataOrigin(dataOrig); + dec->setUserDataDescription(dataDesc); + dec->init(); // is this no-op? + } } catch (const std::exception& e) { LOG(error) << "exception was thrown in decoder creation: " << e.what(); throw; @@ -81,10 +88,9 @@ void STFDecoder::init(InitContext& ic) mApplyNoiseMap = !ic.options().get("ignore-noise-map"); mUseClusterDictionary = !ic.options().get("ignore-cluster-dictionary"); try { - float fr = ic.options().get("rof-lenght-error-freq"); + float fr = ic.options().get("rof-length-error-freq"); mROFErrRepIntervalMS = fr <= 0. ? -1 : long(fr * 1e3); mNThreads = std::max(1, ic.options().get("nthreads")); - mDecoder->setNThreads(mNThreads); mUnmutExtraLanes = ic.options().get("unmute-extra-lanes"); mVerbosity = ic.options().get("decoder-verbosity"); auto dmpSz = ic.options().get("stop-raw-data-dumps-after-size"); @@ -103,13 +109,16 @@ void STFDecoder::init(InitContext& ic) if (mDumpOnError != int(GBTLink::RawDataDumps::DUMP_NONE) && (!dumpDir.empty() && !o2::utils::Str::pathIsDirectory(dumpDir))) { throw std::runtime_error(fmt::format("directory {} for raw data dumps does not exist", dumpDir)); } - mDecoder->setAlwaysParseTrigger(ic.options().get("always-parse-trigger")); - mDecoder->setAllowEmptyROFs(ic.options().get("allow-empty-rofs")); - mDecoder->setRawDumpDirectory(dumpDir); - mDecoder->setFillCalibData(mDoCalibData); - mDecoder->setVerifyDecoder(mVerifyDecoder); - bool ignoreRampUp = !ic.options().get("accept-rof-rampup-data"); - mDecoder->setSkipRampUpData(ignoreRampUp); + for (int iLayer{0}; iLayer < mLayers; ++iLayer) { + mDecoder[iLayer]->setNThreads(mNThreads); + mDecoder[iLayer]->setAlwaysParseTrigger(ic.options().get("always-parse-trigger")); + mDecoder[iLayer]->setAllowEmptyROFs(ic.options().get("allow-empty-rofs")); + mDecoder[iLayer]->setRawDumpDirectory(dumpDir); + mDecoder[iLayer]->setFillCalibData(mDoCalibData); + mDecoder[iLayer]->setVerifyDecoder(mVerifyDecoder); + bool ignoreRampUp = !ic.options().get("accept-rof-rampup-data"); + mDecoder[iLayer]->setSkipRampUpData(ignoreRampUp); + } } catch (const std::exception& e) { LOG(error) << "exception was thrown in decoder configuration: " << e.what(); throw; @@ -122,6 +131,17 @@ void STFDecoder::init(InitContext& ic) mClusterer = std::make_unique(); mClusterer->setNChips(Mapping::getNChips()); } + + if (mDoStaggering) { + Mapping map; + for (uint32_t iLayer{0}; iLayer < mLayers; ++iLayer) { + std::vector filter; + for (const auto feeID : map.getLayer2FEEIDs(iLayer)) { + filter.emplace_back("filter", ConcreteDataMatcher{Mapping::getOrigin(), o2::header::gDataDescriptionRawData, (o2::header::DataHeader::SubSpecificationType)feeID}); + } + mDecoder[iLayer]->setInputFilter(filter); + } + } } ///_______________________________________ @@ -135,141 +155,147 @@ void STFDecoder::run(ProcessingContext& pc) } if (firstCall) { firstCall = false; - mDecoder->setInstanceID(pc.services().get().inputTimesliceId); - mDecoder->setNInstances(pc.services().get().maxInputTimeslices); - mDecoder->setVerbosity(mDecoder->getInstanceID() == 0 ? mVerbosity : (mUnmutExtraLanes ? mVerbosity : -1)); - mAllowReporting &= (mDecoder->getInstanceID() == 0) || mUnmutExtraLanes; + for (int iLayer{0}; iLayer < mLayers; ++iLayer) { + mDecoder[iLayer]->setInstanceID(pc.services().get().inputTimesliceId); + mDecoder[iLayer]->setNInstances(pc.services().get().maxInputTimeslices); + mDecoder[iLayer]->setVerbosity(mDecoder[iLayer]->getInstanceID() == 0 ? mVerbosity : (mUnmutExtraLanes ? mVerbosity : -1)); + } + mAllowReporting &= (mDecoder[0]->getInstanceID() == 0) || mUnmutExtraLanes; } int nSlots = pc.inputs().getNofParts(0); double timeCPU0 = mTimer.CpuTime(), timeReal0 = mTimer.RealTime(); mTimer.Start(false); auto orig = Mapping::getOrigin(); - std::vector clusCompVec; - std::vector clusROFVec; - std::vector clusPattVec; - std::vector digVec; - std::vector calVec; - std::vector digROFVec; + // these are accumulated from each layer auto& chipStatus = pc.outputs().make>(Output{orig, "CHIPSSTATUS", 0}, (size_t)Mapping::getNChips()); + auto& linkErrors = pc.outputs().make>(Output{orig, "LinkErrors", 0}); + auto& decErrors = pc.outputs().make>(Output{orig, "ChipErrors", 0}); + auto& errMessages = pc.outputs().make>(Output{orig, "ErrorInfo", 0}); + auto& physTriggers = pc.outputs().make>(Output{orig, "PHYSTRIG", 0}); - try { - mDecoder->startNewTF(pc.inputs()); + for (uint32_t iLayer{0}; iLayer < mLayers; ++iLayer) { + const auto& par = AlpideParam::Instance(); + const int nROFsPerOrbit = o2::constants::lhc::LHCMaxBunches / par.getROFLengthInBC(iLayer); + const int nROFsTF = nROFsPerOrbit * o2::base::GRPGeomHelper::getNHBFPerTF(); + int nLayer = mDoStaggering ? iLayer : -1; + std::vector clusCompVec; + std::vector clusROFVec; + std::vector clusPattVec; + std::vector digVec; + std::vector calVec; + std::vector digROFVec; if (mDoDigits) { - digVec.reserve(mEstNDig); - digROFVec.reserve(mEstNROF); + digVec.reserve(mEstNDig[iLayer]); + digROFVec.reserve(nROFsTF); } if (mDoClusters) { - clusCompVec.reserve(mEstNClus); - clusROFVec.reserve(mEstNROF); - clusPattVec.reserve(mEstNClusPatt); + clusCompVec.reserve(mEstNClus[iLayer]); + clusROFVec.reserve(nROFsTF); + clusPattVec.reserve(mEstNClusPatt[iLayer]); } if (mDoCalibData) { - calVec.reserve(mEstNCalib); + calVec.reserve(mEstNCalib[iLayer]); } - mDecoder->setDecodeNextAuto(false); - o2::InteractionRecord lastIR{}, firstIR{0, pc.services().get().firstTForbit}; - int nTriggersProcessed = mDecoder->getNROFsProcessed(); - static long lastErrReportTS = 0; - while (mDecoder->decodeNextTrigger() >= 0) { - if ((!lastIR.isDummy() && lastIR >= mDecoder->getInteractionRecord()) || firstIR > mDecoder->getInteractionRecord()) { - const int MaxErrLog = 2; - static int errLocCount = 0; - if (errLocCount++ < MaxErrLog) { - LOGP(warn, "Impossible ROF IR {}, previous was {}, TF 1st IR was {}, discarding in decoding", mDecoder->getInteractionRecord().asString(), lastIR.asString(), firstIR.asString()); + try { + mDecoder[iLayer]->startNewTF(pc.inputs()); + mDecoder[iLayer]->setDecodeNextAuto(false); + + o2::InteractionRecord lastIR{}; + int nTriggersProcessed = mDecoder[iLayer]->getNROFsProcessed(); + static long lastErrReportTS = 0; + while (mDecoder[iLayer]->decodeNextTrigger() >= 0) { + if ((!lastIR.isDummy() && lastIR >= mDecoder[iLayer]->getInteractionRecord()) || mFirstIR > mDecoder[iLayer]->getInteractionRecord()) { + const int MaxErrLog = 2; + static int errLocCount = 0; + if (errLocCount++ < MaxErrLog) { + LOGP(warn, "Impossible ROF IR {}{}, previous was {}, TF 1st IR was {}, discarding in decoding", mDecoder[iLayer]->getInteractionRecord().asString(), ((mDoStaggering) ? std::format(" on layer {}", iLayer) : ""), lastIR.asString(), mFirstIR.asString()); + } + nTriggersProcessed = 0x7fffffff; // to account for a problem with event + continue; + } + lastIR = mDecoder[iLayer]->getInteractionRecord(); + mDecoder[iLayer]->fillChipsStatus(chipStatus); + if (mDoDigits || mClusterer->getMaxROFDepthToSquash(nLayer)) { // call before clusterization, since the latter will hide the digits + mDecoder[iLayer]->fillDecodedDigits(digVec, digROFVec); // lot of copying involved + if (mDoCalibData) { + mDecoder[iLayer]->fillCalibData(calVec); + } + } + if (mDoClusters && !mClusterer->getMaxROFDepthToSquash(nLayer)) { // !!! THREADS !!! + mClusterer->process(mNThreads, *mDecoder[iLayer].get(), &clusCompVec, mDoPatterns ? &clusPattVec : nullptr, &clusROFVec); } - nTriggersProcessed = 0x7fffffff; // to account for a problem with event - continue; } - lastIR = mDecoder->getInteractionRecord(); - mDecoder->fillChipsStatus(chipStatus); - if (mDoDigits || mClusterer->getMaxROFDepthToSquash()) { // call before clusterization, since the latter will hide the digits - mDecoder->fillDecodedDigits(digVec, digROFVec); // lot of copying involved - if (mDoCalibData) { - mDecoder->fillCalibData(calVec); + nTriggersProcessed = mDecoder[iLayer]->getNROFsProcessed() - nTriggersProcessed - 1; + + if ((nROFsTF != nTriggersProcessed) && mROFErrRepIntervalMS > 0 && mTFCounter > 1 && nTriggersProcessed > 0) { + long currTS = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + if (currTS - lastErrReportTS > mROFErrRepIntervalMS) { + LOGP(critical, "Inconsistent number of ROF per TF {}{} from parameters. Received {} from readout (muting further reporting for {} ms)", nROFsTF, ((mDoStaggering) ? std::format(" on layer {}", iLayer) : ""), nTriggersProcessed, mROFErrRepIntervalMS); + lastErrReportTS = currTS; } } - if (mDoClusters && !mClusterer->getMaxROFDepthToSquash()) { // !!! THREADS !!! - mClusterer->process(mNThreads, *mDecoder.get(), &clusCompVec, mDoPatterns ? &clusPattVec : nullptr, &clusROFVec); + if (mDoClusters && mClusterer->getMaxROFDepthToSquash(nLayer)) { + // Digits squashing require to run on a batch of digits and uses a digit reader, cannot (?) run with decoder + // - Setup decoder for running on a batch of digits + o2::itsmft::DigitPixelReader reader; + reader.setSquashingDepth(mClusterer->getMaxROFDepthToSquash(nLayer)); + reader.setSquashingDist(mClusterer->getMaxRowColDiffToMask()); // Sharing same parameter/logic with masking + reader.setMaxBCSeparationToSquash(mClusterer->getMaxBCSeparationToSquash(nLayer)); + reader.setDigits(digVec); + reader.setROFRecords(digROFVec); + reader.init(); + mClusterer->setMaxROFDepthToSquash(mClusterer->getMaxROFDepthToSquash(nLayer)); + mClusterer->process(mNThreads, reader, &clusCompVec, mDoPatterns ? &clusPattVec : nullptr, &clusROFVec); + } + } catch (const std::exception& e) { + static size_t nErr = 0; + auto maxWarn = o2::conf::VerbosityConfig::Instance().maxWarnRawParser; + if (++nErr < maxWarn) { + LOGP(alarm, "EXCEPTION {} in raw decoder{}, abandoning TF decoding {}", e.what(), ((mDoStaggering) ? std::format(" on layer {}", iLayer) : ""), nErr == maxWarn ? "(will mute further warnings)" : ""); } } - nTriggersProcessed = mDecoder->getNROFsProcessed() - nTriggersProcessed - 1; - - const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); - int expectedTFSize = static_cast(o2::constants::lhc::LHCMaxBunches * o2::base::GRPGeomHelper::instance().getGRPECS()->getNHBFPerTF() / alpParams.roFrameLengthInBC); // 3564*32 / ROF Length in BS = number of ROFs per TF - if ((expectedTFSize != nTriggersProcessed) && mROFErrRepIntervalMS > 0 && mTFCounter > 1 && nTriggersProcessed > 0) { - long currTS = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); - if (currTS - lastErrReportTS > mROFErrRepIntervalMS) { - LOGP(critical, "Inconsistent number of ROF per TF. From parameters: {} from readout: {} (muting further reporting for {} ms)", expectedTFSize, nTriggersProcessed, mROFErrRepIntervalMS); - lastErrReportTS = currTS; + if (mDoDigits) { + pc.outputs().snapshot(Output{orig, "DIGITS", iLayer}, digVec); + std::vector expDigRofVec(nROFsTF); + ensureContinuousROF(digROFVec, expDigRofVec, iLayer, nROFsTF, "digits"); + pc.outputs().snapshot(Output{orig, "DIGITSROF", iLayer}, digROFVec); + mEstNDig[iLayer] = std::max(mEstNDig[iLayer], size_t(digVec.size() * 1.2)); + if (mDoCalibData) { + pc.outputs().snapshot(Output{orig, "GBTCALIB", iLayer}, calVec); + mEstNCalib[iLayer] = std::max(mEstNCalib[iLayer], size_t(calVec.size() * 1.2)); } + LOG(debug) << mSelfName << " Decoded " << digVec.size() << " Digits in " << digROFVec.size() << " ROFs" << ((mDoStaggering) ? std::format(" on layer {}", iLayer) : ""); } - if (mDoClusters && mClusterer->getMaxROFDepthToSquash()) { - // Digits squashing require to run on a batch of digits and uses a digit reader, cannot (?) run with decoder - // - Setup decoder for running on a batch of digits - o2::itsmft::DigitPixelReader reader; - reader.setSquashingDepth(mClusterer->getMaxROFDepthToSquash()); - reader.setSquashingDist(mClusterer->getMaxRowColDiffToMask()); // Sharing same parameter/logic with masking - reader.setMaxBCSeparationToSquash(mClusterer->getMaxBCSeparationToSquash()); - reader.setDigits(digVec); - reader.setROFRecords(digROFVec); - reader.init(); - mClusterer->process(mNThreads, reader, &clusCompVec, mDoPatterns ? &clusPattVec : nullptr, &clusROFVec); - } - } catch (const std::exception& e) { - static size_t nErr = 0; - auto maxWarn = o2::conf::VerbosityConfig::Instance().maxWarnRawParser; - if (++nErr < maxWarn) { - LOGP(alarm, "EXCEPTION {} in raw decoder, abandoning TF decoding {}", e.what(), nErr == maxWarn ? "(will mute further warnings)" : ""); - } - } - if (mDoDigits) { - pc.outputs().snapshot(Output{orig, "DIGITS", 0}, digVec); - pc.outputs().snapshot(Output{orig, "DIGITSROF", 0}, digROFVec); - mEstNDig = std::max(mEstNDig, size_t(digVec.size() * 1.2)); - mEstNROF = std::max(mEstNROF, size_t(digROFVec.size() * 1.2)); - if (mDoCalibData) { - pc.outputs().snapshot(Output{orig, "GBTCALIB", 0}, calVec); - mEstNCalib = std::max(mEstNCalib, size_t(calVec.size() * 1.2)); + if (mDoClusters) { // we are not obliged to create vectors which are not requested, but other devices might not know the options of this one + std::vector expClusRofVec(nROFsTF); + ensureContinuousROF(clusROFVec, expClusRofVec, iLayer, nROFsTF, "clusters"); + pc.outputs().snapshot(Output{orig, "COMPCLUSTERS", iLayer}, clusCompVec); + pc.outputs().snapshot(Output{orig, "PATTERNS", iLayer}, clusPattVec); + pc.outputs().snapshot(Output{orig, "CLUSTERSROF", iLayer}, expClusRofVec); + mEstNClus[iLayer] = std::max(mEstNClus[iLayer], size_t(clusCompVec.size() * 1.2)); + mEstNClusPatt[iLayer] = std::max(mEstNClusPatt[iLayer], size_t(clusPattVec.size() * 1.2)); + LOG(info) << mSelfName << " Built " << clusCompVec.size() << " clusters in " << expClusRofVec.size() << " ROFs" << ((mDoStaggering) ? std::format(" on layer {}", iLayer) : ""); } - } - if (mDoClusters) { // we are not obliged to create vectors which are not requested, but other devices might not know the options of this one - pc.outputs().snapshot(Output{orig, "COMPCLUSTERS", 0}, clusCompVec); - pc.outputs().snapshot(Output{orig, "PATTERNS", 0}, clusPattVec); - pc.outputs().snapshot(Output{orig, "CLUSTERSROF", 0}, clusROFVec); - mEstNClus = std::max(mEstNClus, size_t(clusCompVec.size() * 1.2)); - mEstNClusPatt = std::max(mEstNClusPatt, size_t(clusPattVec.size() * 1.2)); - mEstNROF = std::max(mEstNROF, size_t(clusROFVec.size() * 1.2)); - } - auto& linkErrors = pc.outputs().make>(Output{orig, "LinkErrors", 0}); - auto& decErrors = pc.outputs().make>(Output{orig, "ChipErrors", 0}); - auto& errMessages = pc.outputs().make>(Output{orig, "ErrorInfo", 0}); - mDecoder->collectDecodingErrors(linkErrors, decErrors, errMessages); + mDecoder[iLayer]->collectDecodingErrors(linkErrors, decErrors, errMessages); + physTriggers.insert(physTriggers.end(), mDecoder[iLayer]->getExternalTriggers().begin(), mDecoder[iLayer]->getExternalTriggers().end()); - pc.outputs().snapshot(Output{orig, "PHYSTRIG", 0}, mDecoder->getExternalTriggers()); - - if (mDumpOnError != int(GBTLink::RawDataDumps::DUMP_NONE) && - (!mDumpFrom1stPipeline || pc.services().get().inputTimesliceId == 0)) { - mRawDumpedSize += mDecoder->produceRawDataDumps(mDumpOnError, pc.services().get()); - if (mRawDumpedSize > mMaxRawDumpsSize && mMaxRawDumpsSize > 0) { - LOGP(info, "Max total dumped size {} MB exceeded allowed limit, disabling further dumping", mRawDumpedSize / (1024 * 1024)); - mDumpOnError = int(GBTLink::RawDataDumps::DUMP_NONE); + if (mDumpOnError != int(GBTLink::RawDataDumps::DUMP_NONE) && + (!mDumpFrom1stPipeline || pc.services().get().inputTimesliceId == 0)) { + mRawDumpedSize += mDecoder[iLayer]->produceRawDataDumps(mDumpOnError, pc.services().get()); + if (mRawDumpedSize > mMaxRawDumpsSize && mMaxRawDumpsSize > 0) { + LOGP(info, "Max total dumped size {} MB exceeded allowed limit, disabling further dumping", mRawDumpedSize / (1024 * 1024)); + mDumpOnError = int(GBTLink::RawDataDumps::DUMP_NONE); + } } } - if (mDoClusters) { - LOG(debug) << mSelfName << " Built " << clusCompVec.size() << " clusters in " << clusROFVec.size() << " ROFs"; - } - if (mDoDigits) { - LOG(debug) << mSelfName << " Decoded " << digVec.size() << " Digits in " << digROFVec.size() << " ROFs"; - } mTimer.Stop(); auto tfID = pc.services().get().tfCounter; - LOG(debug) << mSelfName << " Total time for TF " << tfID << '(' << mTFCounter << ") : CPU: " << mTimer.CpuTime() - timeCPU0 << " Real: " << mTimer.RealTime() - timeReal0; mTFCounter++; } @@ -285,8 +311,11 @@ void STFDecoder::finalize() LOGF(info, "%s statistics:", mSelfName); LOGF(info, "%s Total STF decoding%s timing (w/o disk IO): Cpu: %.3e Real: %.3e s in %d slots", mSelfName, mDoClusters ? "/clustering" : "", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); - if (mDecoder && mAllowReporting) { - mDecoder->printReport(); + for (int iLayer{0}; iLayer < mLayers && mAllowReporting; ++iLayer) { + if (mDecoder[iLayer]) { + LOG_IF(info, mDoStaggering) << "Report for decoder of layer " << iLayer; + mDecoder[iLayer]->printReport(); + } } if (mClusterer) { mClusterer->print(); @@ -326,9 +355,17 @@ void STFDecoder::updateTimeDependentParams(ProcessingContext& pc) nROFsToSquash = 2 + int(clParams.maxSOTMUS / (rofBC * o2::constants::lhc::LHCBunchSpacingMUS)); // use squashing } mClusterer->setMaxROFDepthToSquash(clParams.maxBCDiffToSquashBias > 0 ? nROFsToSquash : 0); - mClusterer->print(); + if (mDoStaggering) { + for (int iLayer{0}; iLayer < mLayers; ++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); } } + mFirstTFOrbit = pc.services().get().firstTForbit; + mFirstIR = o2::InteractionRecord(0, mFirstTFOrbit); } ///_______________________________________ @@ -367,36 +404,105 @@ void STFDecoder::reset() mFinalizeDone = false; mTFCounter = 0; mTimer.Reset(); - if (mDecoder) { - mDecoder->reset(); + for (int iLayer{0}; iLayer < mLayers; ++iLayer) { + if (mDecoder[iLayer]) { + mDecoder[iLayer]->reset(); + } } if (mClusterer) { mClusterer->reset(); } } +///_______________________________________ +template +void STFDecoder::ensureContinuousROF(const std::vector& rofVec, std::vector& expROFVec, int lr, int nROFsTF, const char* name) +{ + const auto& par = AlpideParam::Instance(); + // ensure that the rof output is continuous + // we will preserve the digits/clusters as they are but the stray ROFs will be removed (leaving their clusters/digits unaddressed). + expROFVec.clear(); + expROFVec.resize(nROFsTF); + for (int iROF{0}; iROF < nROFsTF; ++iROF) { + auto& rof = expROFVec[iROF]; + int orb = iROF * par.getROFLengthInBC(lr) / o2::constants::lhc::LHCMaxBunches + mFirstTFOrbit; + int bc = iROF * par.getROFLengthInBC(lr) % o2::constants::lhc::LHCMaxBunches + par.getROFDelayInBC(lr); + 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 : rofVec) { + const auto& ir = rof.getBCData(); + if (ir < mFirstIR) { + LOGP(warn, "Discard ROF {} preceding TF 1st orbit {}{}", ir.asString(), mFirstTFOrbit, ((mDoStaggering) ? std::format(" on layer {}", lr) : "")); + continue; + } + auto irToFirst = ir - mFirstIR; + if (irToFirst.toLong() - par.getROFDelayInBC(lr) < 0) { + LOGP(warn, "Discard ROF {} preceding TF 1st orbit {} due to imposed ROF delay{}", ir.asString(), mFirstTFOrbit, ((mDoStaggering) ? std::format(" on layer {}", lr) : "")); + continue; + } + irToFirst -= par.getROFDelayInBC(lr); + const long irROF = irToFirst.toLong() / par.getROFLengthInBC(lr); + if (irROF >= nROFsTF) { + LOGP(warn, "Discard ROF {} exceeding TF orbit range{}", ir.asString(), ((mDoStaggering) ? std::format(" on layer {}", lr) : "")); + continue; + } + auto& expROF = expROFVec[irROF]; + if (expROF.getNEntries() == 0) { + expROF.setFirstEntry(rof.getFirstEntry()); + expROF.setNEntries(rof.getNEntries()); + } else { + if (expROF.getNEntries() < rof.getNEntries()) { + LOGP(warn, "Repeating {} with {} {}, prefer to already processed instance with {} {}{}", rof.asString(), rof.getNEntries(), name, expROF.getNEntries(), name, ((mDoStaggering) ? std::format(" on layer {}", lr) : "")); + expROF.setFirstEntry(rof.getFirstEntry()); + expROF.setNEntries(rof.getNEntries()); + } else { + LOGP(warn, "Repeating {} with {} {}, discard preferring already processed instance with {} {}{}", rof.asString(), rof.getNEntries(), name, expROF.getNEntries(), name, ((mDoStaggering) ? std::format(" on layer {}", lr) : "")); + } + } + } + int prevFirst{0}; + for (auto& rof : expROFVec) { + if (rof.getFirstEntry() < 0) { + rof.setFirstEntry(prevFirst); + } + prevFirst = rof.getFirstEntry(); + } +} + ///_______________________________________ DataProcessorSpec getSTFDecoderSpec(const STFDecoderInp& inp) { std::vector outputs; auto inputs = o2::framework::select(inp.inputSpec.c_str()); - if (inp.doDigits) { - outputs.emplace_back(inp.origin, "DIGITS", 0, Lifetime::Timeframe); - outputs.emplace_back(inp.origin, "DIGITSROF", 0, Lifetime::Timeframe); - if (inp.doCalib) { - outputs.emplace_back(inp.origin, "GBTCALIB", 0, Lifetime::Timeframe); + uint32_t nLayers = 1; + if (inp.origin == o2::header::gDataOriginITS && inp.doStaggering) { + nLayers = DPLAlpideParam::getNLayers(); + } else if (inp.origin == o2::header::gDataOriginMFT && inp.doStaggering) { + nLayers = DPLAlpideParam::getNLayers(); + } + for (uint32_t iLayer = 0; iLayer < nLayers; ++iLayer) { + if (inp.doDigits) { + outputs.emplace_back(inp.origin, "DIGITS", iLayer, Lifetime::Timeframe); + outputs.emplace_back(inp.origin, "DIGITSROF", iLayer, Lifetime::Timeframe); + } + if (inp.doClusters) { + outputs.emplace_back(inp.origin, "COMPCLUSTERS", iLayer, Lifetime::Timeframe); + outputs.emplace_back(inp.origin, "CLUSTERSROF", iLayer, Lifetime::Timeframe); + // in principle, we don't need to open this input if we don't need to send real data, + // but other devices expecting it do not know about options of this device: problem? + // if (doClusters && doPatterns) + outputs.emplace_back(inp.origin, "PATTERNS", iLayer, Lifetime::Timeframe); } } - if (inp.doClusters) { - outputs.emplace_back(inp.origin, "COMPCLUSTERS", 0, Lifetime::Timeframe); - outputs.emplace_back(inp.origin, "CLUSTERSROF", 0, Lifetime::Timeframe); - // in principle, we don't need to open this input if we don't need to send real data, - // but other devices expecting it do not know about options of this device: problem? - // if (doClusters && doPatterns) - outputs.emplace_back(inp.origin, "PATTERNS", 0, Lifetime::Timeframe); + if (inp.doDigits && inp.doCalib) { + outputs.emplace_back(inp.origin, "GBTCALIB", 0, Lifetime::Timeframe); } outputs.emplace_back(inp.origin, "PHYSTRIG", 0, Lifetime::Timeframe); - outputs.emplace_back(inp.origin, "LinkErrors", 0, Lifetime::Timeframe); outputs.emplace_back(inp.origin, "ChipErrors", 0, Lifetime::Timeframe); outputs.emplace_back(inp.origin, "ErrorInfo", 0, Lifetime::Timeframe); @@ -424,11 +530,11 @@ DataProcessorSpec getSTFDecoderSpec(const STFDecoderInp& inp) true); // query only once all objects except mag.field return DataProcessorSpec{ - inp.deviceName, - inputs, - outputs, - inp.origin == o2::header::gDataOriginITS ? AlgorithmSpec{adaptFromTask>(inp, ggRequest)} : AlgorithmSpec{adaptFromTask>(inp, ggRequest)}, - Options{ + .name = inp.deviceName, + .inputs = inputs, + .outputs = outputs, + .algorithm = inp.origin == o2::header::gDataOriginITS ? AlgorithmSpec{adaptFromTask>(inp, ggRequest)} : AlgorithmSpec{adaptFromTask>(inp, ggRequest)}, + .options = Options{ {"nthreads", VariantType::Int, 1, {"Number of decoding/clustering threads"}}, {"decoder-verbosity", VariantType::Int, 0, {"Verbosity level (-1: silent, 0: errors, 1: headers, 2: data, 3: raw data dump) of 1st lane"}}, {"always-parse-trigger", VariantType::Bool, false, {"parse trigger word even if flags continuation of old trigger"}}, @@ -439,7 +545,7 @@ DataProcessorSpec getSTFDecoderSpec(const STFDecoderInp& inp) {"allow-empty-rofs", VariantType::Bool, false, {"record ROFs w/o any hit"}}, {"ignore-noise-map", VariantType::Bool, false, {"do not mask pixels flagged in the noise map"}}, {"accept-rof-rampup-data", VariantType::Bool, false, {"do not discard data during ROF ramp up"}}, - {"rof-lenght-error-freq", VariantType::Float, 60.f, {"do not report ROF lenght error more frequently than this value, disable if negative"}}, + {"rof-length-error-freq", VariantType::Float, 60.f, {"do not report ROF length error more frequently than this value, disable if negative"}}, {"ignore-cluster-dictionary", VariantType::Bool, false, {"do not use cluster dictionary, always store explicit patterns"}}}}; } diff --git a/Detectors/ITSMFT/common/workflow/src/digit-reader-workflow.cxx b/Detectors/ITSMFT/common/workflow/src/digit-reader-workflow.cxx index 71b4b82a14126..04453abe464b7 100644 --- a/Detectors/ITSMFT/common/workflow/src/digit-reader-workflow.cxx +++ b/Detectors/ITSMFT/common/workflow/src/digit-reader-workflow.cxx @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include "ITSMFTWorkflow/DigitReaderSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "CommonUtils/ConfigurableParam.h" #include "Framework/ConfigParamSpec.h" #include "Framework/CallbacksPolicy.h" @@ -34,6 +35,7 @@ void customize(std::vector& workflowOptions) ConfigParamSpec{"runmft", VariantType::Bool, false, {"expect MFT data"}}, ConfigParamSpec{"suppress-triggers-output", VariantType::Bool, false, {"suppress dummy triggers output"}}, ConfigParamSpec{"configKeyValues", VariantType::String, "", {"semicolon separated key=value strings"}}}; + o2::itsmft::DPLAlpideParamInitializer::addConfigOption(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -52,9 +54,11 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); if (cfgc.options().get("runmft")) { - wf.emplace_back(o2::itsmft::getMFTDigitReaderSpec(useMC, calib, withTriggers)); + bool doStag = o2::itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(cfgc); + wf.emplace_back(o2::itsmft::getMFTDigitReaderSpec(useMC, doStag, calib, withTriggers)); } else { - wf.emplace_back(o2::itsmft::getITSDigitReaderSpec(useMC, calib, withTriggers)); + bool doStag = o2::itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(cfgc); + wf.emplace_back(o2::itsmft::getITSDigitReaderSpec(useMC, doStag, calib, withTriggers)); } o2::raw::HBFUtilsInitializer hbfIni(cfgc, wf); return wf; diff --git a/Detectors/ITSMFT/common/workflow/src/digit-writer-workflow.cxx b/Detectors/ITSMFT/common/workflow/src/digit-writer-workflow.cxx index 2d4fbea9aef6c..98391846c49c8 100644 --- a/Detectors/ITSMFT/common/workflow/src/digit-writer-workflow.cxx +++ b/Detectors/ITSMFT/common/workflow/src/digit-writer-workflow.cxx @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include "ITSMFTWorkflow/DigitWriterSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "CommonUtils/ConfigurableParam.h" #include "Framework/ConfigParamSpec.h" #include "Framework/CompletionPolicyHelpers.h" @@ -32,7 +33,7 @@ void customize(std::vector& workflowOptions) ConfigParamSpec{"enable-calib-data", VariantType::Bool, false, {"enable writing GBT calibration data"}}, ConfigParamSpec{"runmft", VariantType::Bool, false, {"expect MFT data"}}, ConfigParamSpec{"configKeyValues", VariantType::String, "", {"semicolon separated key=value strings"}}}; - + o2::itsmft::DPLAlpideParamInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -49,9 +50,11 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); if (cfgc.options().get("runmft")) { - wf.emplace_back(o2::itsmft::getMFTDigitWriterSpec(useMC, true, calib)); + bool doStag = o2::itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(cfgc); + wf.emplace_back(o2::itsmft::getMFTDigitWriterSpec(useMC, doStag, true, calib)); } else { - wf.emplace_back(o2::itsmft::getITSDigitWriterSpec(useMC, true, calib)); + bool doStag = o2::itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(cfgc); + wf.emplace_back(o2::itsmft::getITSDigitWriterSpec(useMC, doStag, true, calib)); } return wf; } diff --git a/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx b/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx index e0fc23ec70128..fed7268100428 100644 --- a/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx +++ b/Detectors/ITSMFT/common/workflow/src/entropy-encoder-workflow.cxx @@ -10,6 +10,7 @@ // or submit itself to any jurisdiction. #include "ITSMFTWorkflow/EntropyEncoderSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "CommonUtils/ConfigurableParam.h" #include "Framework/ConfigParamSpec.h" @@ -26,7 +27,7 @@ void customize(std::vector& workflowOptions) ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, ConfigParamSpec{"ctf-dict", VariantType::String, "none", {"CTF dictionary: empty or ccdb=CCDB, none=no external dictionary otherwise: local filename"}}, ConfigParamSpec{"select-ir-frames", VariantType::Bool, false, {"Subscribe and filter according to external IR Frames"}}}; - + o2::itsmft::DPLAlpideParamInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -41,9 +42,11 @@ 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, cfgc.options().get("ctf-dict"))); + bool doStag = o2::itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(cfgc); + wf.emplace_back(o2::itsmft::getMFTEntropyEncoderSpec(doStag, selIR, cfgc.options().get("ctf-dict"))); } else { - wf.emplace_back(o2::itsmft::getEntropyEncoderSpec("ITS", selIR, cfgc.options().get("ctf-dict"))); + bool doStag = o2::itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(cfgc); + wf.emplace_back(o2::itsmft::getITSEntropyEncoderSpec(doStag, selIR, cfgc.options().get("ctf-dict"))); } return wf; } diff --git a/Detectors/ITSMFT/common/workflow/src/stf-decoder-workflow.cxx b/Detectors/ITSMFT/common/workflow/src/stf-decoder-workflow.cxx index 7b1b97ec0c4f5..219e8915e11f3 100644 --- a/Detectors/ITSMFT/common/workflow/src/stf-decoder-workflow.cxx +++ b/Detectors/ITSMFT/common/workflow/src/stf-decoder-workflow.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. // @@ -12,6 +12,7 @@ #include "ITSMFTWorkflow/STFDecoderSpec.h" #include "CommonUtils/ConfigurableParam.h" #include "Framework/ConfigParamSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; @@ -33,7 +34,7 @@ void customize(std::vector& workflowOptions) ConfigParamSpec{"dataspec", VariantType::String, "", {"selection string for the input data, if not provided Raw:/RAWDATA with DET=ITS or MFT will be used"}}, ConfigParamSpec{"report-dds-collection-index", VariantType::Int, -1, {"number of dpl collection allowed to produce decoding report (-1 means no limit)"}}, ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; - + o2::itsmft::DPLAlpideParamInitializer::addConfigOption(options); std::swap(workflowOptions, options); } @@ -53,6 +54,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) inp.askSTFDist = !cfgc.options().get("ignore-dist-stf"); inp.verifyDecoder = cfgc.options().get("verify"); inp.inputSpec = cfgc.options().get("dataspec"); + // Update the (declared) parameters if changed from the command line o2::conf::ConfigurableParam::updateFromString(cfgc.options().get("configKeyValues")); @@ -62,12 +64,14 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) } inp.origin = o2::header::gDataOriginMFT; inp.deviceName = "mft-stf-decoder"; + inp.doStaggering = o2::itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(cfgc); } else { if (inp.inputSpec.empty()) { inp.inputSpec = "itsRAW:ITS/RAWDATA"; } inp.origin = o2::header::gDataOriginITS; inp.deviceName = "its-stf-decoder"; + inp.doStaggering = o2::itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(cfgc); } inp.allowReporting = true; diff --git a/Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx b/Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx index 2b8090af42648..919e76083f595 100644 --- a/Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx +++ b/Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx @@ -121,7 +121,7 @@ void TFReaderSpec::init(o2f::InitContext& ic) if (!mInput.fileRunTimeSpans.empty()) { loadRunTimeSpans(mInput.fileRunTimeSpans); } - mFileFetcher = std::make_unique(mInput.inpdata, mInput.tffileRegex, mInput.remoteRegex, mInput.copyCmd); + mFileFetcher = std::make_unique(mInput.inpdata, mInput.tffileRegex, mInput.remoteRegex, mInput.copyCmd, mInput.copyDir); mFileFetcher->setMaxFilesInQueue(mInput.maxFileCache); mFileFetcher->setMaxLoops(mInput.maxLoops); mFileFetcher->setFailThreshold(ic.options().get("fetch-failure-threshold")); diff --git a/Detectors/Raw/TFReaderDD/src/TFReaderSpec.h b/Detectors/Raw/TFReaderDD/src/TFReaderSpec.h index 9db18768c1bfe..2c1c62ecbb414 100644 --- a/Detectors/Raw/TFReaderDD/src/TFReaderSpec.h +++ b/Detectors/Raw/TFReaderDD/src/TFReaderSpec.h @@ -29,6 +29,7 @@ struct TFReaderInp { std::string detListNonRawOnly{}; std::string rawChannelConfig{}; std::string copyCmd{}; + std::string copyDir{}; std::string tffileRegex{}; std::string remoteRegex{}; std::string metricChannel{}; diff --git a/Detectors/Raw/TFReaderDD/src/tf-reader-workflow.cxx b/Detectors/Raw/TFReaderDD/src/tf-reader-workflow.cxx index bc682127b0d3f..b424353531de7 100644 --- a/Detectors/Raw/TFReaderDD/src/tf-reader-workflow.cxx +++ b/Detectors/Raw/TFReaderDD/src/tf-reader-workflow.cxx @@ -31,6 +31,7 @@ void customize(std::vector& workflowOptions) options.push_back(ConfigParamSpec{"loop", VariantType::Int, 0, {"loop N times (-1 = infinite)"}}); options.push_back(ConfigParamSpec{"delay", VariantType::Float, 0.f, {"delay in seconds between consecutive TFs sending"}}); options.push_back(ConfigParamSpec{"copy-cmd", VariantType::String, "alien_cp ?src file://?dst", {"copy command for remote files"}}); // Use "XrdSecPROTOCOL=sss,unix xrdcp -N root://eosaliceo2.cern.ch/?src ?dst" for direct EOS access + options.push_back(ConfigParamSpec{"copy-dir", VariantType::String, "/tmp/", {"copy base directory for remote files"}}); options.push_back(ConfigParamSpec{"tf-file-regex", VariantType::String, ".+\\.tf$", {"regex string to identify TF files"}}); options.push_back(ConfigParamSpec{"remote-regex", VariantType::String, "^(alien://|)/alice/data/.+", {"regex string to identify remote files"}}); // Use "^/eos/aliceo2/.+" for direct EOS access options.push_back(ConfigParamSpec{"tf-reader-verbosity", VariantType::Int, 0, {"verbosity level (1 or 2: check RDH, print DH/DPH for 1st or all slices, >2 print RDH)"}}); @@ -71,6 +72,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) rinp.delay_us = uint64_t(1e6 * configcontext.options().get("delay")); // delay in microseconds rinp.verbosity = configcontext.options().get("tf-reader-verbosity"); rinp.copyCmd = configcontext.options().get("copy-cmd"); + rinp.copyDir = configcontext.options().get("copy-dir"); rinp.tffileRegex = configcontext.options().get("tf-file-regex"); rinp.remoteRegex = configcontext.options().get("remote-regex"); rinp.sendDummyForMissing = !configcontext.options().get("disable-dummy-output"); diff --git a/Detectors/TRD/workflow/src/trd-tracking-workflow.cxx b/Detectors/TRD/workflow/src/trd-tracking-workflow.cxx index 1d8243ff8cbc0..7781b5ed187cb 100644 --- a/Detectors/TRD/workflow/src/trd-tracking-workflow.cxx +++ b/Detectors/TRD/workflow/src/trd-tracking-workflow.cxx @@ -26,6 +26,7 @@ #include "GlobalTrackingWorkflowHelpers/InputHelper.h" #include "TPCCalibration/CorrectionMapsLoader.h" #include "TPCWorkflow/TPCScalerSpec.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using GTrackID = o2::dataformats::GlobalTrackID; @@ -62,6 +63,7 @@ void customize(std::vector& workflowOptions) {"disable-ft0-pileup-tagging", VariantType::Bool, false, {"Do not request FT0 for pile-up determination"}}, {"policy", VariantType::String, "default", {"Pick PID policy (=default)"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h index c07767d50b113..005237fe28839 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/include/TRKReconstruction/TimeFrame.h @@ -37,8 +37,8 @@ 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 +template +class TimeFrame : public o2::its::TimeFrame { public: TimeFrame() = default; @@ -49,8 +49,6 @@ class TimeFrame : public o2::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); @@ -60,7 +58,8 @@ class TimeFrame : public o2::its::TimeFrame /// \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); + /// \param rofLength ROF length in BCs (must match what was used in loadROFsFromHitTree) + void getPrimaryVerticesFromMC(TTree* mcHeaderTree, int nRofs, Long64_t nEvents, int inROFpileup, uint32_t rofLength = 198); }; } // namespace trk diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx index 610a08450d5ee..957560aea8cae 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TimeFrame.cxx @@ -23,11 +23,13 @@ #include #include +using o2::its::clearResizeBoundedVector; + namespace o2::trk { -template -int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, const nlohmann::json& config) +template +int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, const nlohmann::json& config) { constexpr std::array startLayer{0, 3}; const Long64_t nEvents = hitsTree->GetEntries(); @@ -39,23 +41,39 @@ int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, const int inROFpileup{config.contains("inROFpileup") ? config["inROFpileup"].get() : 1}; - // Calculate number of ROFs and initialize data structures - this->mNrof = (nEvents + inROFpileup - 1) / inROFpileup; + // Calculate number of ROFs + const int nRofs = (nEvents + inROFpileup - 1) / inROFpileup; + + // Set up ROF timing for all layers (no staggering in TRK simulation, all layers read out together) + constexpr uint32_t rofLength = 198; // ROF length in BC + o2::its::ROFOverlapTable overlapTable; + for (int iLayer = 0; iLayer < NLayers; ++iLayer) { + overlapTable.defineLayer(iLayer, nRofs, rofLength, 0, 0, 0); + } + overlapTable.init(); + this->setROFOverlapTable(overlapTable); + + // Set up the vertex lookup table timing (pre-allocate, vertices will be filled later) + o2::its::ROFVertexLookupTable vtxLookupTable; + for (int iLayer = 0; iLayer < NLayers; ++iLayer) { + vtxLookupTable.defineLayer(iLayer, nRofs, rofLength, 0, 0, 0); + } + vtxLookupTable.init(); // pre-allocate without vertices + this->setROFVertexLookupTable(vtxLookupTable); // Reset and prepare ROF data structures - for (int iLayer{0}; iLayer < nLayers; ++iLayer) { + 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->mROFramesClusters[iLayer].resize(nRofs + 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{}; + std::array clusterCountPerLayer{}; for (Long64_t iEvent = 0; iEvent < nEvents; ++iEvent) { hitsTree->GetEntry(iEvent); for (const auto& hit : *trkHit) { @@ -64,25 +82,24 @@ int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, } int subDetID = gman->getSubDetID(hit.GetDetectorID()); const int layer = startLayer[subDetID] + gman->getLayer(hit.GetDetectorID()); - if (layer >= nLayers) { + if (layer >= NLayers) { continue; } ++clusterCountPerLayer[layer]; - totalNHits++; } } - // Reserve memory for all layers - for (int iLayer{0}; iLayer < nLayers; ++iLayer) { + // Reserve memory for all layers (mClusterSize is now per-layer) + 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[iLayer], clusterCountPerLayer[iLayer], this->mMemoryPool.get()); } - 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) { + if (config["geometry"]["pitch"].size() == static_cast(NLayers)) { + for (size_t 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); @@ -90,9 +107,10 @@ int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, } LOGP(info, "Number of active parts in VD: {}", gman->getNumberOfActivePartsVD()); - int hitCounter{0}; - auto labels = new dataformats::MCTruthContainer(); + // One shared MC label container for all layers + auto* labels = new dataformats::MCTruthContainer(); + int hitCounter{0}; int iRof{0}; // Current ROF index for (Long64_t iEvent = 0; iEvent < nEvents; ++iEvent) { hitsTree->GetEntry(iEvent); @@ -108,7 +126,7 @@ int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, o2::math_utils::Point3D gloXYZ; o2::math_utils::Point3D trkXYZ; float r{0.f}; - if (layer >= nLayers) { + if (layer >= NLayers) { continue; } if (layer >= 3) { @@ -139,11 +157,12 @@ int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, 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()); + const int clusterIdxInLayer = this->mUnsortedClusters[layer].size(); + this->addClusterToLayer(layer, gloXYZ.x(), gloXYZ.y(), gloXYZ.z(), clusterIdxInLayer); 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 + this->mClusterSize[layer][clusterIdxInLayer] = 1; hitCounter++; } trkHit->clear(); @@ -154,21 +173,23 @@ int TimeFrame::loadROFsFromHitTree(TTree* hitsTree, GeometryTGeo* gman, 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; + + // Set the shared labels container for all layers + for (int iLayer = 0; iLayer < NLayers; ++iLayer) { + this->mClusterLabels[iLayer] = labels; + } + + return nRofs; } -template -void TimeFrame::getPrimaryVerticesFromMC(TTree* mcHeaderTree, int nRofs, Long64_t nEvents, int inROFpileup) +template +void TimeFrame::getPrimaryVerticesFromMC(TTree* mcHeaderTree, int nRofs, Long64_t nEvents, int inROFpileup, uint32_t rofLength) { 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}; @@ -178,14 +199,24 @@ void TimeFrame::getPrimaryVerticesFromMC(TTree* mcHeaderTree, int nRofs 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); + + // Set proper BC timestamp for vertex-ROF compatibility + // The vertex timestamp is set to the center of its ROF with half-ROF as error + const uint32_t rofCenter = static_cast(rofLength * iRof + rofLength / 2); + const uint16_t rofHalf = static_cast(rofLength / 2); + vertex.setTimeStamp({rofCenter, rofHalf}); + + LOGP(debug, "ROF {}: Added primary vertex at ({}, {}, {}) with BC timestamp [{}, +/-{}]", + iRof, mcheader->GetX(), mcheader->GetY(), mcheader->GetZ(), rofCenter, rofHalf); + this->addPrimaryVertex(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. + this->mMultiplicityCutMask.resetMask(1u); /// all ROFs are valid with MC primary vertices. + + // Update the vertex lookup table with the newly added vertices + this->updateROFVertexLookupTable(); } // Explicit template instantiation for TRK with 11 layers diff --git a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx index 3801228422a62..c9d793a3ec78f 100644 --- a/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/workflow/src/TrackerSpec.cxx @@ -41,7 +41,6 @@ namespace o2 using namespace framework; namespace trk { -using Vertex = o2::dataformats::Vertex>; TrackerDPL::TrackerDPL(std::shared_ptr gr, bool isMC, @@ -92,18 +91,12 @@ std::vector TrackerDPL::createTrackingParamsFromCon 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(); } @@ -127,27 +120,21 @@ std::vector TrackerDPL::createTrackingParamsFromCon 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(); - } + // 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")) { @@ -162,9 +149,9 @@ std::vector TrackerDPL::createTrackingParamsFromCon if (paramConfig.contains("ShiftRefToCluster")) { params.ShiftRefToCluster = paramConfig["ShiftRefToCluster"].get(); } - if (paramConfig.contains("FindShortTracks")) { - params.FindShortTracks = paramConfig["FindShortTracks"].get(); - } + // if (paramConfig.contains("FindShortTracks")) { + // params.FindShortTracks = paramConfig["FindShortTracks"].get(); + // } if (paramConfig.contains("PerPrimaryVertexProcessing")) { params.PerPrimaryVertexProcessing = paramConfig["PerPrimaryVertexProcessing"].get(); } @@ -177,18 +164,18 @@ std::vector TrackerDPL::createTrackingParamsFromCon 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("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(); } @@ -314,44 +301,37 @@ void TrackerDPL::run(ProcessingContext& pc) 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); + itsTrackerTraits.computeLayerTracklets(iter, -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); + LOGP(info, "Number of tracks in iteration {}: {}", iter, timeFrame.getNumberOfTracks()); } 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; + // Collect tracks and labels (flat vectors in the new interface) + const auto& tracks = timeFrame.getTracks(); + const auto& labels = timeFrame.getTracksLabel(); - int totalTracks = 0; + // Copy to output vectors (TrackITSExt -> TrackITS slicing for output compatibility) + std::vector allTracks(tracks.begin(), tracks.end()); + std::vector allLabels(labels.begin(), labels.end()); + + int totalTracks = allTracks.size(); 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++; - } + for (const auto& label : allLabels) { + if (label.isFake()) { + fakeTracks++; + } else { + goodTracks++; } } diff --git a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/TrackingInterface.h b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/TrackingInterface.h index 931628f2cf876..3b743c59524d2 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/TrackingInterface.h +++ b/Detectors/Upgrades/ITS3/reconstruction/include/ITS3Reconstruction/TrackingInterface.h @@ -31,6 +31,7 @@ class ITS3TrackingInterface final : public its::ITSTrackingInterface void loadROF(gsl::span& trackROFspan, gsl::span clusters, gsl::span::iterator& pattIt, + int layer, const dataformats::MCTruthContainer* mcLabels) final; private: diff --git a/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx b/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx index 0fea07743b3df..92e36cd2a4b84 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx +++ b/Detectors/Upgrades/ITS3/reconstruction/src/IOUtils.cxx @@ -66,8 +66,8 @@ int loadROFrameDataITS3(its::TimeFrame<7>* tf, auto geom = its::GeometryTGeo::Instance(); geom->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::T2L, o2::math_utils::TransformType::L2G)); - tf->resetROFrameData(rofs.size()); - tf->prepareROFrameData(rofs, clusters); + // tf->resetROFrameData(rofs.size()); // FIXME + // tf->prepareROFrameData(rofs, clusters); FIXME its::bounded_vector clusterSizeVec(clusters.size(), tf->getMemoryPool().get()); @@ -115,7 +115,7 @@ int loadROFrameDataITS3(its::TimeFrame<7>* tf, } } - tf->setClusterSize(clusterSizeVec); + // tf->setClusterSize(clusterSizeVec); FIXME for (auto& v : tf->mNTrackletsPerCluster) { v.resize(tf->getUnsortedClusters()[1].size()); @@ -125,8 +125,8 @@ int loadROFrameDataITS3(its::TimeFrame<7>* tf, } if (mcLabels != nullptr) { - tf->mClusterLabels = mcLabels; + // tf->mClusterLabels = mcLabels; // FIXME } - return tf->mNrof; + return 0; } } // namespace o2::its3::ioutils diff --git a/Detectors/Upgrades/ITS3/reconstruction/src/TrackingInterface.cxx b/Detectors/Upgrades/ITS3/reconstruction/src/TrackingInterface.cxx index 0f5c66a7f9663..9fe6f3735a845 100644 --- a/Detectors/Upgrades/ITS3/reconstruction/src/TrackingInterface.cxx +++ b/Detectors/Upgrades/ITS3/reconstruction/src/TrackingInterface.cxx @@ -13,7 +13,7 @@ #include "ITS3Reconstruction/IOUtils.h" #include "ITSBase/GeometryTGeo.h" #include "ITStracking/TrackingConfigParam.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "DetectorsBase/GRPGeomHelper.h" #include "Framework/DeviceSpec.h" @@ -77,9 +77,10 @@ void ITS3TrackingInterface::finaliseCCDB(framework::ConcreteDataMatcher& matcher void ITS3TrackingInterface::loadROF(gsl::span& trackROFspan, gsl::span clusters, gsl::span::iterator& pattIt, + int layer, const dataformats::MCTruthContainer* mcLabels) { - ioutils::loadROFrameDataITS3(mTimeFrame, trackROFspan, clusters, pattIt, mDict, mcLabels); + // ioutils::loadROFrameDataITS3(mTimeFrame, trackROFspan, clusters, pattIt, mDict, mcLabels); } } // namespace o2::its3 diff --git a/Detectors/Upgrades/ITS3/workflow/src/ClustererSpec.cxx b/Detectors/Upgrades/ITS3/workflow/src/ClustererSpec.cxx index f0238b74a3502..73b5f4650d02d 100644 --- a/Detectors/Upgrades/ITS3/workflow/src/ClustererSpec.cxx +++ b/Detectors/Upgrades/ITS3/workflow/src/ClustererSpec.cxx @@ -27,7 +27,7 @@ #include "DataFormatsITSMFT/ROFRecord.h" #include "DataFormatsParameters/GRPObject.h" #include "ITSMFTReconstruction/DigitPixelReader.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "CommonConstants/LHCConstants.h" using namespace o2::framework; diff --git a/Detectors/Upgrades/ITS3/workflow/src/RecoWorkflow.cxx b/Detectors/Upgrades/ITS3/workflow/src/RecoWorkflow.cxx index 60fe4fabfe481..f27fda19fe00c 100644 --- a/Detectors/Upgrades/ITS3/workflow/src/RecoWorkflow.cxx +++ b/Detectors/Upgrades/ITS3/workflow/src/RecoWorkflow.cxx @@ -40,7 +40,7 @@ framework::WorkflowSpec getWorkflow(bool useMC, its::TrackingMode::Type trmode, } if (!disableRootOutput) { - specs.emplace_back(o2::itsmft::getITSClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getITSClusterWriterSpec(useMC, false)); } if (trmode != its::TrackingMode::Off) { diff --git a/Detectors/Upgrades/ITS3/workflow/src/TrackerSpec.cxx b/Detectors/Upgrades/ITS3/workflow/src/TrackerSpec.cxx index 0326c12f804e0..8db02d7227e7f 100644 --- a/Detectors/Upgrades/ITS3/workflow/src/TrackerSpec.cxx +++ b/Detectors/Upgrades/ITS3/workflow/src/TrackerSpec.cxx @@ -23,7 +23,7 @@ #include "DataFormatsITSMFT/PhysTrigger.h" #include "ITStracking/TrackingConfigParam.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "ITSBase/GeometryTGeo.h" #include "CommonDataFormat/IRFrame.h" @@ -46,7 +46,7 @@ TrackerDPL::TrackerDPL(std::shared_ptr gr, const bool overrBeamEst, o2::gpu::gpudatatypes::DeviceType dType) : mGGCCDBRequest(gr), mRecChain{o2::gpu::GPUReconstruction::CreateInstance(dType, true)}, - mITS3TrackingInterface{isMC, trgType, overrBeamEst} + mITS3TrackingInterface{isMC, false, trgType, overrBeamEst} { mITS3TrackingInterface.setTrackingMode(trMode); } diff --git a/Detectors/Vertexing/include/DetectorsVertexing/PVertexer.h b/Detectors/Vertexing/include/DetectorsVertexing/PVertexer.h index c06c2119b0cd1..cdf83603258cd 100644 --- a/Detectors/Vertexing/include/DetectorsVertexing/PVertexer.h +++ b/Detectors/Vertexing/include/DetectorsVertexing/PVertexer.h @@ -31,7 +31,7 @@ #include "DetectorsVertexing/PVertexerParams.h" #include "ReconstructionDataFormats/GlobalTrackID.h" #include "DataFormatsCalibration/MeanVertexObject.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "gsl/span" #include #include diff --git a/Detectors/Vertexing/src/VertexTrackMatcher.cxx b/Detectors/Vertexing/src/VertexTrackMatcher.cxx index 8612187c0bffc..f66d2b8c4d347 100644 --- a/Detectors/Vertexing/src/VertexTrackMatcher.cxx +++ b/Detectors/Vertexing/src/VertexTrackMatcher.cxx @@ -15,7 +15,7 @@ #include "DataFormatsGlobalTracking/RecoContainerCreateTracksVariadic.h" #include "DetectorsVertexing/VertexTrackMatcher.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include #include diff --git a/Detectors/Vertexing/test/PVFromPool.C b/Detectors/Vertexing/test/PVFromPool.C index 7bca9c03bf42f..248cbda401eca 100644 --- a/Detectors/Vertexing/test/PVFromPool.C +++ b/Detectors/Vertexing/test/PVFromPool.C @@ -1,3 +1,14 @@ +// 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. + #if !defined(__CLING__) || defined(__ROOTCLING__) #include "DetectorsVertexing/PVertexer.h" @@ -11,7 +22,7 @@ #include "DataFormatsParameters/GRPECSObject.h" #include "DataFormatsParameters/GRPMagField.h" #include "DetectorsBase/Propagator.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "CCDB/BasicCCDBManager.h" #include diff --git a/EventVisualisation/Workflow/src/EveWorkflowHelper.cxx b/EventVisualisation/Workflow/src/EveWorkflowHelper.cxx index 2bb3c220d67a0..b4f7655648001 100644 --- a/EventVisualisation/Workflow/src/EveWorkflowHelper.cxx +++ b/EventVisualisation/Workflow/src/EveWorkflowHelper.cxx @@ -32,7 +32,7 @@ #include "MCHTracking/TrackParam.h" #include "MCHTracking/TrackExtrap.h" #include "DataFormatsITSMFT/TrkClusRef.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "CommonDataFormat/IRFrame.h" #include "MFTBase/GeometryTGeo.h" #include "ITSBase/GeometryTGeo.h" diff --git a/EventVisualisation/Workflow/src/O2DPLDisplay.cxx b/EventVisualisation/Workflow/src/O2DPLDisplay.cxx index bd8ab5a664d99..828892ea97406 100644 --- a/EventVisualisation/Workflow/src/O2DPLDisplay.cxx +++ b/EventVisualisation/Workflow/src/O2DPLDisplay.cxx @@ -37,6 +37,7 @@ #include "DataFormatsMCH/ROFRecord.h" #include #include "DataFormatsMCH/Cluster.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include using std::chrono::duration_cast; @@ -78,7 +79,7 @@ void customize(std::vector& workflowOptions) {"configKeyValues", VariantType::String, "", {"semicolon separated key=value strings, e.g. EveConfParam content..."}}, {"skipOnEmptyInput", VariantType::Bool, false, {"don't run the ED when no input is provided"}}, }; - + o2::itsmft::DPLAlpideParamInitializer::addConfigOption(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); std::swap(workflowOptions, options); } diff --git a/GPU/GPUTracking/Base/GPUReconstructionIncludesITS.h b/GPU/GPUTracking/Base/GPUReconstructionIncludesITS.h index 813e0aef2d1aa..36a2b3ebca103 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionIncludesITS.h +++ b/GPU/GPUTracking/Base/GPUReconstructionIncludesITS.h @@ -21,7 +21,6 @@ #include "ITStracking/TimeFrame.h" #if defined(__CUDACC__) || defined(__HIPCC__) #include "ITStrackingGPU/TrackerTraitsGPU.h" -#include "ITStrackingGPU/VertexerTraitsGPU.h" #include "ITStrackingGPU/TimeFrameGPU.h" #endif #else @@ -39,10 +38,6 @@ template class TimeFrame { }; -template -class VertexerTraitsGPU : public VertexerTraits -{ -}; template class TrackerTraitsGPU : public TrackerTraits { diff --git a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu index c919581eefdde..eb49a02fbb946 100644 --- a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu +++ b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu @@ -94,15 +94,13 @@ GPUReconstruction* GPUReconstruction_Create_CUDA(const GPUSettingsDeviceBackend& void GPUReconstructionCUDA::GetITSTraits(std::unique_ptr>* trackerTraits, std::unique_ptr>* vertexerTraits, std::unique_ptr>* timeFrame) { if (trackerTraits) { - trackerTraits->reset(new o2::its::TrackerTraitsGPU); + trackerTraits->reset(new o2::its::TrackerTraitsGPU<7>); } if (vertexerTraits) { vertexerTraits->reset(new o2::its::VertexerTraits<7>); - // TODO gpu-code to be implemented then remove line above and uncomment line below - // vertexerTraits->reset(new o2::its::VertexerTraitsGPU<7>); } if (timeFrame) { - timeFrame->reset(new o2::its::gpu::TimeFrameGPU); + timeFrame->reset(new o2::its::gpu::TimeFrameGPU<7>); } } diff --git a/GPU/GPUTracking/Global/GPUChainITS.h b/GPU/GPUTracking/Global/GPUChainITS.h index 4aa97f3f47784..ee466365a157d 100644 --- a/GPU/GPUTracking/Global/GPUChainITS.h +++ b/GPU/GPUTracking/Global/GPUChainITS.h @@ -19,9 +19,6 @@ namespace o2::its { struct Cluster; -template -class Road; -class Cell; struct TrackingFrameInfo; class TrackITSExt; class GPUFrameworkExternalAllocator; diff --git a/GPU/GPUTracking/display/render/GPUDisplayImportEvent.cxx b/GPU/GPUTracking/display/render/GPUDisplayImportEvent.cxx index 9c516ebb960d7..658cdc46cb6cb 100644 --- a/GPU/GPUTracking/display/render/GPUDisplayImportEvent.cxx +++ b/GPU/GPUTracking/display/render/GPUDisplayImportEvent.cxx @@ -31,7 +31,7 @@ #include "TOFBase/Geo.h" #include "ITSBase/GeometryTGeo.h" #ifdef GPUCA_O2_LIB -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #endif #include diff --git a/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h b/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h index 8dfbdaff7272f..c5e4124c41650 100644 --- a/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h +++ b/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h @@ -132,6 +132,7 @@ class GPURecoWorkflowSpec : public o2::framework::Task int32_t lumiScaleType = 0; // 0=off, 1=CTP, 2=TPC scalers bool outputErrorQA = false; bool runITSTracking = false; + bool itsStaggered = false; bool itsOverrBeamEst = false; bool tpcTriggerHandling = false; bool isITS3 = false; diff --git a/GPU/Workflow/src/GPUWorkflowITS.cxx b/GPU/Workflow/src/GPUWorkflowITS.cxx index 46e1b1578285c..fb27df2ec08b9 100644 --- a/GPU/Workflow/src/GPUWorkflowITS.cxx +++ b/GPU/Workflow/src/GPUWorkflowITS.cxx @@ -52,18 +52,21 @@ void GPURecoWorkflowSpec::initFunctionITS(o2::framework::InitContext& ic) #ifdef ENABLE_UPGRADES if (mSpecConfig.isITS3) { mITSTrackingInterface = std::make_unique(mSpecConfig.processMC, + mSpecConfig.itsStaggered, mSpecConfig.itsTriggerType, mSpecConfig.itsOverrBeamEst); - } else -#endif - { + } else { mITSTrackingInterface = std::make_unique(mSpecConfig.processMC, + mSpecConfig.itsStaggered, mSpecConfig.itsTriggerType, mSpecConfig.itsOverrBeamEst); } +#else mITSTrackingInterface = std::make_unique(mSpecConfig.processMC, + mSpecConfig.itsStaggered, mSpecConfig.itsTriggerType, mSpecConfig.itsOverrBeamEst); +#endif mGPUReco->GetITSTraits(trkTraits, vtxTraits, mITSTimeFrame); mITSTrackingInterface->setTraitsFromProvider(vtxTraits, trkTraits, mITSTimeFrame); } diff --git a/GPU/Workflow/src/GPUWorkflowSpec.cxx b/GPU/Workflow/src/GPUWorkflowSpec.cxx index dbb554a14cea4..4b1aa7fd58bd5 100644 --- a/GPU/Workflow/src/GPUWorkflowSpec.cxx +++ b/GPU/Workflow/src/GPUWorkflowSpec.cxx @@ -1229,9 +1229,14 @@ Inputs GPURecoWorkflowSpec::inputs() } if (mSpecConfig.runITSTracking) { - 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); + for (unsigned int iLay{0}; iLay < (mSpecConfig.itsStaggered ? 7 : 1); ++iLay) { + inputs.emplace_back("compClusters", "ITS", "COMPCLUSTERS", iLay, Lifetime::Timeframe); + inputs.emplace_back("patterns", "ITS", "PATTERNS", iLay, Lifetime::Timeframe); + inputs.emplace_back("ROframes", "ITS", "CLUSTERSROF", iLay, Lifetime::Timeframe); + if (mSpecConfig.processMC) { + inputs.emplace_back("itsmclabels", "ITS", "CLUSTERSMCTR", iLay, Lifetime::Timeframe); + } + } if (mSpecConfig.itsTriggerType == 1) { inputs.emplace_back("phystrig", "ITS", "PHYSTRIG", 0, Lifetime::Timeframe); } else if (mSpecConfig.itsTriggerType == 2) { @@ -1249,10 +1254,6 @@ Inputs GPURecoWorkflowSpec::inputs() inputs.emplace_back("meanvtx", "GLO", "MEANVERTEX", 0, Lifetime::Condition, ccdbParamSpec("GLO/Calib/MeanVertex", {}, 1)); } } - if (mSpecConfig.processMC) { - inputs.emplace_back("itsmclabels", "ITS", "CLUSTERSMCTR", 0, Lifetime::Timeframe); - inputs.emplace_back("ITSMC2ROframes", "ITS", "CLUSTERSMC2ROF", 0, Lifetime::Timeframe); - } } // NN clusterizer @@ -1388,7 +1389,6 @@ Outputs GPURecoWorkflowSpec::outputs() outputSpecs.emplace_back(gDataOriginITS, "VERTICESMCTR", 0, Lifetime::Timeframe); outputSpecs.emplace_back(gDataOriginITS, "VERTICESMCPUR", 0, Lifetime::Timeframe); outputSpecs.emplace_back(gDataOriginITS, "TRACKSMCTR", 0, Lifetime::Timeframe); - outputSpecs.emplace_back(gDataOriginITS, "ITSTrackMC2ROF", 0, Lifetime::Timeframe); } } diff --git a/GPU/Workflow/src/O2GPUDPLDisplay.cxx b/GPU/Workflow/src/O2GPUDPLDisplay.cxx index 8513541bcae43..ed0d522b4d7ea 100644 --- a/GPU/Workflow/src/O2GPUDPLDisplay.cxx +++ b/GPU/Workflow/src/O2GPUDPLDisplay.cxx @@ -34,6 +34,7 @@ #include "GPUWorkflowHelper/GPUWorkflowHelper.h" #include "DataFormatsITSMFT/TopologyDictionary.h" #include "DetectorsRaw/HBFUtils.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" using namespace o2::framework; using namespace o2::dataformats; @@ -53,7 +54,7 @@ void customize(std::vector& workflowOptions) {"read-from-files", o2::framework::VariantType::Bool, false, {"Automatically create readers for input"}}, {"disable-root-input", o2::framework::VariantType::Bool, false, {"Disable root input overriding read-from-files"}}, {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings ..."}}}; - + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); std::swap(workflowOptions, options); } diff --git a/GPU/Workflow/src/gpu-reco-workflow.cxx b/GPU/Workflow/src/gpu-reco-workflow.cxx index e620d013cc925..13e28a1c341b3 100644 --- a/GPU/Workflow/src/gpu-reco-workflow.cxx +++ b/GPU/Workflow/src/gpu-reco-workflow.cxx @@ -29,6 +29,7 @@ #include "GlobalTrackingWorkflowHelpers/InputHelper.h" #include "ReconstructionDataFormats/GlobalTrackID.h" #include "TPCCalibration/CorrectionMapsLoader.h" +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include #include @@ -66,6 +67,7 @@ void customize(std::vector& workflowOptions) }; o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); + o2::itsmft::DPLAlpideParamInitializer::addITSConfigOption(options); std::swap(workflowOptions, options); } @@ -190,6 +192,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& cfgc) cfg.tpcDeadMapSources = cfgc.options().get("tpc-deadMap-sources"); cfg.tpcUseMCTimeGain = cfgc.options().get("tpc-mc-time-gain"); cfg.runITSTracking = isEnabled(outputTypes, ioType::ITSTracks); + cfg.itsStaggered = o2::itsmft::DPLAlpideParamInitializer::isITSStaggeringEnabled(cfgc); cfg.itsOverrBeamEst = isEnabled(inputTypes, ioType::MeanVertex); cfg.useFilteredOutputSpecs = cfgc.options().get("filtered-output-specs"); diff --git a/Steer/DigitizerWorkflow/src/ITS3DigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/ITS3DigitizerSpec.cxx index 60a1660288b9d..19552a407ec57 100644 --- a/Steer/DigitizerWorkflow/src/ITS3DigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/ITS3DigitizerSpec.cxx @@ -28,7 +28,7 @@ #include "ITS3Simulation/Digitizer.h" #include "ITSMFTSimulation/DPLDigitizerParam.h" #include "ITS3Simulation/ITS3DPLDigitizerParam.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "ITSBase/GeometryTGeo.h" #include "ITS3Base/ITS3Params.h" diff --git a/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx b/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx index eafb72c675a58..a4c401bbf8b42 100644 --- a/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx +++ b/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.cxx @@ -13,7 +13,6 @@ #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/DataProcessorSpec.h" -#include "Framework/DataRefUtils.h" #include "Framework/Lifetime.h" #include "Framework/Task.h" #include "Framework/CCDBParamSpec.h" @@ -26,18 +25,16 @@ #include "DetectorsRaw/HBFUtils.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DetectorsCommonDataFormats/SimTraits.h" -#include "DetectorsCommonDataFormats/DetectorNameConf.h" #include "DataFormatsParameters/GRPObject.h" #include "DataFormatsITSMFT/ROFRecord.h" #include "ITSMFTSimulation/Digitizer.h" #include "ITSMFTSimulation/DPLDigitizerParam.h" -#include "ITSMFTBase/DPLAlpideParam.h" +#include "DataFormatsITSMFT/DPLAlpideParam.h" #include "ITSBase/GeometryTGeo.h" #include "MFTBase/GeometryTGeo.h" #include #include #include -#include using namespace o2::framework; using SubSpecificationType = o2::framework::DataAllocator::SubSpecificationType; @@ -52,13 +49,20 @@ 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"); + if (mDoStaggering) { + mLayers = DPLAlpideParam::getNLayers(); + } + mDigits.resize(mLayers); + mROFRecords.resize(mLayers); + mROFRecordsAccum.resize(mLayers); + mLabels.resize(mLayers); + mLabelsAccum.resize(mLayers); } void run(framework::ProcessingContext& pc) @@ -89,9 +93,8 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer } 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; + for (uint32_t iLayer = 0; iLayer < mLayers; ++iLayer) { + const int layer = (mDoStaggering) ? iLayer : -1; mDigitizer.setDigits(&mDigits[iLayer]); mDigitizer.setROFRecords(&mROFRecords[iLayer]); mDigitizer.setMCLabels(&mLabels[iLayer]); @@ -121,25 +124,13 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer 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; + LOG(info) << "Added " << mDigits[iLayer].size() << " digits" << ((mDoStaggering) ? std::format(" on layer {}", iLayer) : ""); // clean containers from already accumulated stuff mLabels[iLayer].clear(); mDigits[iLayer].clear(); @@ -171,7 +162,6 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer mDigitizer.process(&mHits, part.entryID, part.sourceID, layer); // call actual digitization procedure } } - mMC2ROFRecordsAccum[iLayer].emplace_back(collID, -1, mDigitizer.getEventROFrameMin(), mDigitizer.getEventROFrameMax()); accumulate(); } mDigitizer.fillOutputContainer(0xffffffff, layer); @@ -190,7 +180,7 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer 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; + int bc = iROF * DPLAlpideParam::Instance().getROFLengthInBC(iLayer) % o2::constants::lhc::LHCMaxBunches + DPLAlpideParam::Instance().getROFDelayInBC(iLayer); o2::InteractionRecord ir(bc, orb); rof.setBCData(ir); rof.setROFrame(iROF); @@ -200,7 +190,16 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer uint32_t prevEntry{0}; for (const auto& rof : mROFRecordsAccum[iLayer]) { const auto& ir = rof.getBCData(); - const auto irToFirst = ir - firstIR; + if (ir < firstIR) { + LOGP(warn, "Discard ROF {} preceding TF 1st orbit {}{}", ir.asString(), mFirstOrbitTF, ((mDoStaggering) ? std::format(" on layer {}", iLayer) : "")); + continue; + } + auto irToFirst = ir - firstIR; + if (irToFirst.toLong() - DPLAlpideParam::Instance().getROFDelayInBC(iLayer) < 0) { + LOGP(warn, "Discard ROF {} preceding TF 1st orbit {} due to imposed ROF delay{}", ir.asString(), mFirstOrbitTF, ((mDoStaggering) ? std::format(" on layer {}", iLayer) : "")); + continue; + } + irToFirst -= DPLAlpideParam::Instance().getROFDelayInBC(iLayer); const int irROF = irToFirst.toLong() / DPLAlpideParam::Instance().getROFLengthInBC(iLayer); auto& expROF = expDigitRofVec[irROF]; expROF.setFirstEntry(rof.getFirstEntry()); @@ -224,7 +223,6 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer pc.outputs().snapshot(Output{Origin, "DIGITSROF", iLayer}, mROFRecordsAccum[iLayer]); } if (mWithMCTruth) { - 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 @@ -292,7 +290,7 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer } protected: - ITSMFTDPLDigitizerTask(bool mctruth = true) : BaseDPLDigitizer(InitServices::FIELD | InitServices::GEOM), mWithMCTruth(mctruth) {} + ITSMFTDPLDigitizerTask(bool mctruth = true, bool doStag = false) : BaseDPLDigitizer(InitServices::FIELD | InitServices::GEOM), mWithMCTruth(mctruth), mDoStaggering(doStag) {} void updateTimeDependentParams(ProcessingContext& pc) { @@ -331,17 +329,15 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer digipar.setOBVbb(dopt.OBVbb); digipar.setVbb(dopt.Vbb); // staggering parameters - if constexpr (o2::itsmft::DPLAlpideParam::supportsStaggering()) { - const bool withStag = aopt.withStaggering(); + if (mDoStaggering) { 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)); + auto frameNS = aopt.getROFLengthInBC(iLayer) * o2::constants::lhc::LHCBunchSpacingNS; + digipar.addROFrameLayerLengthInBC(aopt.getROFLengthInBC(iLayer)); // NOTE: the rof delay looks from the digitizer like an additional bias - digipar.addROFrameLayerBiasInBC(aopt.getROFBiasInBC(nLayer) + aopt.getROFDelayInBC(nLayer)); + digipar.addROFrameLayerBiasInBC(aopt.getROFBiasInBC(iLayer) + aopt.getROFDelayInBC(iLayer)); digipar.addStrobeDelay(aopt.strobeDelay); digipar.addStrobeLength(aopt.strobeLengthCont > 0 ? aopt.strobeLengthCont : frameNS - aopt.strobeDelay); - digipar.setROFrameLength(aopt.getROFLengthInBC(nLayer) * o2::constants::lhc::LHCBunchSpacingNS, iLayer); + digipar.setROFrameLength(aopt.getROFLengthInBC(iLayer) * o2::constants::lhc::LHCBunchSpacingNS, iLayer); } } @@ -363,22 +359,22 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer } bool mWithMCTruth = true; + bool mDoStaggering = false; bool mFinished = false; bool mDisableQED = false; + int mLayers = 1; unsigned long mFirstOrbitTF = 0x0; o2::itsmft::Digitizer mDigitizer; - std::array, NLayers> mDigits; - std::array, NLayers> mROFRecords; - std::array, NLayers> mROFRecordsAccum; + std::vector> mDigits; + std::vector> mROFRecords; + std::vector> mROFRecordsAccum; std::vector mHits; std::vector* mHitsP = &mHits; - std::array, NLayers> mLabels; - std::array, NLayers> mLabelsAccum; - std::array, NLayers> mMC2ROFRecordsAccum; + std::vector> mLabels; + std::vector> mLabelsAccum; std::vector mSimChains; o2::itsmft::NoiseMap* mDeadMap = nullptr; - 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 }; @@ -387,28 +383,27 @@ class ITSMFTDPLDigitizerTask : BaseDPLDigitizer class ITSDPLDigitizerTask : public ITSMFTDPLDigitizerTask { public: - ITSDPLDigitizerTask(bool mctruth = true) : ITSMFTDPLDigitizerTask(mctruth) {} + ITSDPLDigitizerTask(bool mctruth = true, bool doStag = false) : ITSMFTDPLDigitizerTask(mctruth, doStag) {} }; //_______________________________________________ class MFTDPLDigitizerTask : public ITSMFTDPLDigitizerTask { public: - MFTDPLDigitizerTask(bool mctruth = true) : ITSMFTDPLDigitizerTask(mctruth) {} + MFTDPLDigitizerTask(bool mctruth = true, bool doStag = false) : ITSMFTDPLDigitizerTask(mctruth, doStag) {} }; namespace { template -std::vector makeOutChannels(o2::header::DataOrigin detOrig, bool mctruth) +std::vector makeOutChannels(o2::header::DataOrigin detOrig, bool mctruth, bool doStag) { std::vector outputs; - constexpr uint32_t nLayers = (DPLAlpideParam::supportsStaggering()) ? DPLAlpideParam::getNLayers() : 1; + uint32_t nLayers = doStag ? 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); } } @@ -417,7 +412,7 @@ std::vector makeOutChannels(o2::header::DataOrigin detOrig, bool mct } } // namespace -DataProcessorSpec getITSDigitizerSpec(int channel, bool mctruth) +DataProcessorSpec getITSDigitizerSpec(int channel, bool mctruth, bool doStag) { std::string detStr = o2::detectors::DetID::getName(ITSDPLDigitizerTask::ID); auto detOrig = ITSDPLDigitizerTask::Origin; @@ -431,13 +426,13 @@ DataProcessorSpec getITSDigitizerSpec(int channel, bool mctruth) 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)}, + .outputs = makeOutChannels(detOrig, mctruth, doStag), + .algorithm = AlgorithmSpec{adaptFromTask(mctruth, doStag)}, .options = Options{ {"disable-qed", o2::framework::VariantType::Bool, false, {"disable QED handling"}}}}; } -DataProcessorSpec getMFTDigitizerSpec(int channel, bool mctruth) +DataProcessorSpec getMFTDigitizerSpec(int channel, bool mctruth, bool doStag) { std::string detStr = o2::detectors::DetID::getName(MFTDPLDigitizerTask::ID); auto detOrig = MFTDPLDigitizerTask::Origin; @@ -451,10 +446,10 @@ DataProcessorSpec getMFTDigitizerSpec(int channel, bool mctruth) 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)}, + .outputs = makeOutChannels(detOrig, mctruth, doStag), + .algorithm = AlgorithmSpec{adaptFromTask(mctruth, doStag)}, .options = Options{{"disable-qed", o2::framework::VariantType::Bool, false, {"disable QED handling"}}}}; } } // namespace o2::itsmft - // end namespace o2 +// end namespace o2 diff --git a/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.h b/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.h index 55fd88b1e1f80..e763cfe9565f4 100644 --- a/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.h +++ b/Steer/DigitizerWorkflow/src/ITSMFTDigitizerSpec.h @@ -19,8 +19,8 @@ namespace o2 namespace itsmft { -o2::framework::DataProcessorSpec getITSDigitizerSpec(int channel, bool mctruth = true); -o2::framework::DataProcessorSpec getMFTDigitizerSpec(int channel, bool mctruth = true); +o2::framework::DataProcessorSpec getITSDigitizerSpec(int channel, bool mctruth = true, bool doStag = false); +o2::framework::DataProcessorSpec getMFTDigitizerSpec(int channel, bool mctruth = true, bool doStag = false); } // end namespace itsmft } // end namespace o2 diff --git a/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx b/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx index 6f956efe79304..b4f9c1643d150 100644 --- a/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx +++ b/Steer/DigitizerWorkflow/src/SimpleDigitizerWorkflow.cxx @@ -37,6 +37,7 @@ #include "TPCSimulation/GEMAmplification.h" // for ITSMFT +#include "DataFormatsITSMFT/DPLAlpideParamInitializer.h" #include "ITSMFTDigitizerSpec.h" #include "ITSMFTWorkflow/DigitWriterSpec.h" @@ -225,6 +226,7 @@ void customize(std::vector& workflowOptions) // option to propagate CTP Lumi scaler counts (if >=0) into the CTP digits workflowOptions.push_back(ConfigParamSpec{"store-ctp-lumi", VariantType::Float, -1.f, {"store CTP lumi scaler in CTP digits (if >= 0)"}}); + o2::itsmft::DPLAlpideParamInitializer::addConfigOption(workflowOptions); } void customize(std::vector& policies) @@ -637,10 +639,11 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) // the ITS part if (isEnabled(o2::detectors::DetID::ITS)) { detList.emplace_back(o2::detectors::DetID::ITS); + bool doStag = o2::itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(configcontext); // connect the ITS digitization - digitizerSpecs.emplace_back(o2::itsmft::getITSDigitizerSpec(fanoutsize++, mctruth)); + digitizerSpecs.emplace_back(o2::itsmft::getITSDigitizerSpec(fanoutsize++, mctruth, doStag)); // connect ITS digit writer - writerSpecs.emplace_back(o2::itsmft::getITSDigitWriterSpec(mctruth)); + writerSpecs.emplace_back(o2::itsmft::getITSDigitWriterSpec(mctruth, doStag)); } #ifdef ENABLE_UPGRADES @@ -666,10 +669,11 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) // the MFT part if (isEnabled(o2::detectors::DetID::MFT)) { detList.emplace_back(o2::detectors::DetID::MFT); + bool doStag = o2::itsmft::DPLAlpideParamInitializer::isMFTStaggeringEnabled(configcontext); // connect the MFT digitization - digitizerSpecs.emplace_back(o2::itsmft::getMFTDigitizerSpec(fanoutsize++, mctruth)); + digitizerSpecs.emplace_back(o2::itsmft::getMFTDigitizerSpec(fanoutsize++, mctruth, doStag)); // connect MFT digit writer - writerSpecs.emplace_back(o2::itsmft::getMFTDigitWriterSpec(mctruth)); + writerSpecs.emplace_back(o2::itsmft::getMFTDigitWriterSpec(mctruth, doStag)); } // the TOF part diff --git a/doc/data/2021-02-o2_prs.json b/doc/data/2021-02-o2_prs.json index d36bfadccf499..2bf9b8ff3cb4b 100644 --- a/doc/data/2021-02-o2_prs.json +++ b/doc/data/2021-02-o2_prs.json @@ -2399,7 +2399,7 @@ }, { "node": { - "path": "Detectors/ITSMFT/common/base/include/ITSMFTBase/DPLAlpideParam.h" + "path": "Detectors/ITSMFT/common/base/include/DataFormatsITSMFT/DPLAlpideParam.h" } }, { diff --git a/doc/data/2022-01-o2_prs.json b/doc/data/2022-01-o2_prs.json index 155ab6ed3d8d5..1e21f2e051c5e 100644 --- a/doc/data/2022-01-o2_prs.json +++ b/doc/data/2022-01-o2_prs.json @@ -3475,7 +3475,7 @@ "edges": [ { "node": { - "path": "Detectors/ITSMFT/common/base/include/ITSMFTBase/DPLAlpideParam.h" + "path": "Detectors/ITSMFT/common/base/include/DataFormatsITSMFT/DPLAlpideParam.h" } } ] diff --git a/macro/run_rawdecoding_its.C b/macro/run_rawdecoding_its.C index c5ee6c9b0ff5e..d05681356019a 100644 --- a/macro/run_rawdecoding_its.C +++ b/macro/run_rawdecoding_its.C @@ -1,3 +1,14 @@ +// 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. + #if !defined(__CLING__) || defined(__ROOTCLING__) #include @@ -42,7 +53,7 @@ void run_rawdecoding_its(std::string inpName = "rawits.bin", // input binary dat TStopwatch sw; sw.Start(); uint32_t roFrame = 0; - o2::InteractionRecord irHB, irTrig; + o2::InteractionRecord irTrig; std::vector digits, *digitsPtr = &digits; std::vector rofRecVec, *rofRecVecPtr = &rofRecVec; std::size_t rofEntry = 0, nrofdig = 0; @@ -62,12 +73,11 @@ void run_rawdecoding_its(std::string inpName = "rawits.bin", // input binary dat } if (outTreeDig) { // >> store digits - if (irHB != rawReader.getInteractionRecordHB() || irTrig != rawReader.getInteractionRecord()) { + if (irTrig != rawReader.getInteractionRecord()) { if (!irTrig.isDummy()) { - rofRecVec.emplace_back(irHB, roFrame, rofEntry, nrofdig); // registed finished ROF + rofRecVec.emplace_back(irTrig, roFrame, rofEntry, nrofdig); // registed finished ROF roFrame++; } - irHB = rawReader.getInteractionRecordHB(); irTrig = rawReader.getInteractionRecord(); rofEntry = digits.size(); nrofdig = 0; @@ -79,7 +89,6 @@ void run_rawdecoding_its(std::string inpName = "rawits.bin", // input binary dat } printf("ROF %7d ch: %5d IR: ", roFrame, chipData.getChipID()); - irHB.print(); } // << store digits // @@ -87,7 +96,7 @@ void run_rawdecoding_its(std::string inpName = "rawits.bin", // input binary dat if (outTreeDig) { // register last ROF - rofRecVec.emplace_back(irHB, roFrame, rofEntry, nrofdig); // registed finished ROF + rofRecVec.emplace_back(irTrig, roFrame, rofEntry, nrofdig); // registed finished ROF // fill last (and the only one?) entry outTreeDig->Fill(); diff --git a/macro/run_rawdecoding_mft.C b/macro/run_rawdecoding_mft.C index d8bdb0ce1e2ce..d23668f7e5498 100644 --- a/macro/run_rawdecoding_mft.C +++ b/macro/run_rawdecoding_mft.C @@ -1,3 +1,14 @@ +// 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. + #if !defined(__CLING__) || defined(__ROOTCLING__) #include @@ -42,7 +53,7 @@ void run_rawdecoding_mft(std::string inpName = "06282019_1854_output.bin", // in TStopwatch sw; sw.Start(); uint32_t roFrame = 0; - o2::InteractionRecord irHB, irTrig; + o2::InteractionRecord irTrig; std::vector digits, *digitsPtr = &digits; std::vector rofRecVec, *rofRecVecPtr = &rofRecVec; int rofEntry = 0, nrofdig = 0; @@ -62,12 +73,11 @@ void run_rawdecoding_mft(std::string inpName = "06282019_1854_output.bin", // in } if (outTreeDig) { // >> store digits - if (irHB != rawReader.getInteractionRecordHB() || irTrig != rawReader.getInteractionRecord()) { + if (irTrig != rawReader.getInteractionRecord()) { if (!irTrig.isDummy()) { - rofRecVec.emplace_back(irHB, roFrame, rofEntry, nrofdig); // registed finished ROF + rofRecVec.emplace_back(irTrig, roFrame, rofEntry, nrofdig); // registed finished ROF roFrame++; } - irHB = rawReader.getInteractionRecordHB(); irTrig = rawReader.getInteractionRecord(); rofEntry = digits.size(); nrofdig = 0; @@ -79,7 +89,6 @@ void run_rawdecoding_mft(std::string inpName = "06282019_1854_output.bin", // in } printf("ROF %7d ch: %5d IR: ", roFrame, chipData.getChipID()); - irHB.print(); } // << store digits // @@ -87,7 +96,7 @@ void run_rawdecoding_mft(std::string inpName = "06282019_1854_output.bin", // in if (outTreeDig) { // register last ROF - rofRecVec.emplace_back(irHB, roFrame, rofEntry, nrofdig); // registed finished ROF + rofRecVec.emplace_back(irTrig, roFrame, rofEntry, nrofdig); // registed finished ROF // fill last (and the only one?) entry outTreeDig->Fill(); diff --git a/prodtests/full-system-test/dpl-workflow.sh b/prodtests/full-system-test/dpl-workflow.sh index 1e1ea258d395f..9f982513fdffd 100755 --- a/prodtests/full-system-test/dpl-workflow.sh +++ b/prodtests/full-system-test/dpl-workflow.sh @@ -107,10 +107,16 @@ EVE_OPT=" --jsons-folder $EDJSONS_DIR" : ${AOD_SOURCES:=$TRACK_SOURCES} : ${AODPROD_OPT:=} : ${ALPIDE_ERR_DUMPS:=} +: ${ITSSTAGGERED:=} +: ${MFTSTAGGERED:=} + [[ -z $ALPIDE_ERR_DUMPS ]] && [[ $EPNSYNCMODE == 1 && $RUNTYPE == "PHYSICS" ]] && ALPIDE_ERR_DUMPS=1 || ALPIDE_ERR_DUMPS=0 [[ "0$DISABLE_ROOT_OUTPUT" == "00" ]] && DISABLE_ROOT_OUTPUT= +[[ "0$ITSSTAGGERED" == "01" ]] && ITS_STAGGERED=" --enable-its-staggering " || ITS_STAGGERED= +[[ "0$MFTSTAGGERED" == "01" ]] && MFT_STAGGERED=" --enable-its-staggering " || MFT_STAGGERED= + if [[ $CTFINPUT != 1 ]]; then GPU_OUTPUT+=",tpc-triggers" fi @@ -127,7 +133,6 @@ if [[ $SYNCMODE == 1 ]]; then MCH_CONFIG_KEY="MCHTracking.maxCandidates=20000;MCHTracking.maxTrackingDuration=10;" 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 && [[ $RUNTYPE != "COSMICS" && x"${MFT_DISABLE_ITS_IRFRAMES_SELECTION:-}" != "x1" ]]; then MFT_CONFIG_KEY+="MFTTracking.irFramesOnly=1;" fi @@ -156,7 +161,6 @@ else ITS_CONFIG_KEY+="ITSVertexerParam.phiCut=0.5;ITSVertexerParam.clusterContributorsCut=3;ITSVertexerParam.tanLambdaCut=0.2;" elif [[ $BEAMTYPE == "PbPb" ]]; then ITS_CONFIG_KEY+="ITSVertexerParam.lowMultBeamDistCut=0;" - ! has_detectors_gpu TPC ITS && ITS_CONFIG_KEY+="ITSCATrackerParam.nROFsPerIterations=12;" fi if [[ $IS_SIMULATED_DATA == 0 && $CTFINPUT == 1 ]]; then # Enable fixes to the MCH readout mapping for async processing of real data @@ -463,7 +467,7 @@ if [[ -n $INPUT_DETECTOR_LIST ]]; then if [[ $NTIMEFRAMES == -1 ]]; then NTIMEFRAMES_CMD= ; else NTIMEFRAMES_CMD="--max-tf $NTIMEFRAMES"; fi CTF_EMC_SUBSPEC= ( workflow_has_parameter AOD || [[ -z "$DISABLE_ROOT_OUTPUT" ]] || needs_root_output o2-emcal-cell-writer-workflow ) && has_detector EMC && CTF_EMC_SUBSPEC="--emcal-decoded-subspec 1" - add_W o2-ctf-reader-workflow "$RANS_OPT --delay $TFDELAY --loop $TFLOOP $NTIMEFRAMES_CMD --ctf-input ${CTFName} ${INPUT_FILE_COPY_CMD+--copy-cmd} ${INPUT_FILE_COPY_CMD:-} --onlyDet $INPUT_DETECTOR_LIST $CTF_EMC_SUBSPEC ${TIMEFRAME_SHM_LIMIT+--timeframes-shm-limit} ${TIMEFRAME_SHM_LIMIT:-} --pipeline $(get_N tpc-entropy-decoder TPC REST 1 TPCENTDEC)" + add_W o2-ctf-reader-workflow "$RANS_OPT --delay $TFDELAY --loop $TFLOOP $NTIMEFRAMES_CMD $ITS_STAGGERED $MFT_STAGGERED --ctf-input ${CTFName} ${INPUT_FILE_COPY_CMD+--copy-cmd} ${INPUT_FILE_COPY_CMD:-} --onlyDet $INPUT_DETECTOR_LIST $CTF_EMC_SUBSPEC ${TIMEFRAME_SHM_LIMIT+--timeframes-shm-limit} ${TIMEFRAME_SHM_LIMIT:-} --pipeline $(get_N tpc-entropy-decoder TPC REST 1 TPCENTDEC)" elif [[ $RAWTFINPUT == 1 ]]; then TFName=`ls -t $RAWINPUTDIR/o2_*.tf 2> /dev/null | head -n1` [[ -z $TFName && $WORKFLOWMODE == "print" ]] && TFName='$TFName' @@ -556,8 +560,8 @@ if [[ $CTFINPUT == 0 && $DIGITINPUT == 0 ]]; then add_W o2-tpc-raw-to-digits-workflow "--input-spec \"\" --remove-duplicates $RAWTODIGITOPTIONS --pipeline $(get_N tpc-raw-to-digits-0 TPC RAW 1 TPCRAWDEC)" add_W o2-tpc-reco-workflow "--input-type digitizer --output-type zsraw,disable-writer --pipeline $(get_N tpc-zsEncoder TPC RAW 1 TPCRAWDEC)" "GPU_rec_tpc.zsThreshold=0" fi - has_detector ITS && ! has_detector_from_global_reader ITS && add_W o2-itsmft-stf-decoder-workflow "--nthreads ${NITSDECTHREADS} --raw-data-dumps $ALPIDE_ERR_DUMPS --pipeline $(get_N its-stf-decoder ITS RAW 1 ITSRAWDEC)" "$ITS_STF_DEC_CONFIG;$ITSMFT_STROBES;VerbosityConfig.rawParserSeverity=warn;" - has_detector MFT && ! has_detector_from_global_reader MFT && add_W o2-itsmft-stf-decoder-workflow "--nthreads ${NMFTDECTHREADS} --raw-data-dumps $ALPIDE_ERR_DUMPS --pipeline $(get_N mft-stf-decoder MFT RAW 1 MFTRAWDEC) --runmft true" "$MFT_STF_DEC_CONFIG;$ITSMFT_STROBES;VerbosityConfig.rawParserSeverity=warn;" + has_detector ITS && ! has_detector_from_global_reader ITS && add_W o2-itsmft-stf-decoder-workflow "--nthreads ${NITSDECTHREADS} --raw-data-dumps $ALPIDE_ERR_DUMPS $ITS_STAGGERED --pipeline $(get_N its-stf-decoder ITS RAW 1 ITSRAWDEC)" "$ITS_STF_DEC_CONFIG;$ITSMFT_STROBES;VerbosityConfig.rawParserSeverity=warn;" + has_detector MFT && ! has_detector_from_global_reader MFT && add_W o2-itsmft-stf-decoder-workflow "--nthreads ${NMFTDECTHREADS} --raw-data-dumps $ALPIDE_ERR_DUMPS $MFT_STAGGERED --pipeline $(get_N mft-stf-decoder MFT RAW 1 MFTRAWDEC) --runmft true" "$MFT_STF_DEC_CONFIG;$ITSMFT_STROBES;VerbosityConfig.rawParserSeverity=warn;" has_detector FT0 && ! has_detector_from_global_reader FT0 && ! has_detector_flp_processing FT0 && add_W o2-ft0-flp-dpl-workflow "$DISABLE_ROOT_OUTPUT --pipeline $(get_N ft0-datareader-dpl FT0 RAW 1)" has_detector FV0 && ! has_detector_from_global_reader FV0 && ! has_detector_flp_processing FV0 && add_W o2-fv0-flp-dpl-workflow "$DISABLE_ROOT_OUTPUT --pipeline $(get_N fv0-datareader-dpl FV0 RAW 1)" has_detector MID && ! has_detector_from_global_reader MID && add_W o2-mid-raw-to-digits-workflow "$MIDDEC_CONFIG --pipeline $(get_N MIDRawDecoder MID RAW 1),$(get_N MIDDecodedDataAggregator MID RAW 1)" @@ -581,13 +585,13 @@ has_detector_gpu ITS && GPU_OUTPUT+=",its-tracks" # --------------------------------------------------------------------------------------------------------------------- # Common reconstruction workflows -(has_detector_reco ITS && ! has_detector_gpu ITS) && ! has_detector_from_global_reader ITS && add_W o2-its-reco-workflow "--trackerCA $ITS_CONFIG $DISABLE_MC ${DISABLE_DIGIT_CLUSTER_INPUT:-} $DISABLE_ROOT_OUTPUT --pipeline $(get_N its-tracker ITS REST 1 ITSTRK),$(get_N its-clusterer ITS REST 1 ITSCL)" "$ITS_CONFIG_KEY;$ITSMFT_STROBES;$ITSEXTRAERR" -[[ ${DISABLE_DIGIT_CLUSTER_INPUT:-} =~ "--digits-from-upstream" ]] && has_detector_gpu ITS && ! has_detector_from_global_reader ITS && add_W o2-its-reco-workflow "--disable-tracking ${DISABLE_DIGIT_CLUSTER_INPUT:-} $DISABLE_MC $DISABLE_ROOT_OUTPUT --pipeline $(get_N its-clusterer ITS REST 1 ITSCL)" "$ITS_CONFIG_KEY;$ITSMFT_STROBES;$ITSEXTRAERR" -(has_detector_reco TPC || has_detector_ctf TPC) && ! has_detector_from_global_reader TPC && add_W o2-gpu-reco-workflow "--gpu-reconstruction \"$GPU_CONFIG_SELF\" --input-type=$GPU_INPUT $DISABLE_MC --output-type $GPU_OUTPUT $TPC_CORR_OPT --pipeline gpu-reconstruction:${N_TPCTRK:-1},gpu-reconstruction-prepare:${N_TPCTRK:-1} $GPU_CONFIG" "GPU_global.deviceType=$GPUTYPE;GPU_proc.debugLevel=0;$GPU_CONFIG_KEY;$TRACKTUNETPCINNER;$TPC_CORR_KEY" +(has_detector_reco ITS && ! has_detector_gpu ITS) && ! has_detector_from_global_reader ITS && add_W o2-its-reco-workflow "--trackerCA $ITS_CONFIG $ITS_STAGGERED $DISABLE_MC ${DISABLE_DIGIT_CLUSTER_INPUT:-} $DISABLE_ROOT_OUTPUT --pipeline $(get_N its-tracker ITS REST 1 ITSTRK),$(get_N its-clusterer ITS REST 1 ITSCL)" "$ITS_CONFIG_KEY;$ITSMFT_STROBES;$ITSEXTRAERR" +[[ ${DISABLE_DIGIT_CLUSTER_INPUT:-} =~ "--digits-from-upstream" ]] && has_detector_gpu ITS && ! has_detector_from_global_reader ITS && add_W o2-its-reco-workflow "--disable-tracking ${DISABLE_DIGIT_CLUSTER_INPUT:-} $ITS_STAGGERED $DISABLE_MC $DISABLE_ROOT_OUTPUT --pipeline $(get_N its-clusterer ITS REST 1 ITSCL)" "$ITS_CONFIG_KEY;$ITSMFT_STROBES;$ITSEXTRAERR" +(has_detector_reco TPC || has_detector_ctf TPC) && ! has_detector_from_global_reader TPC && add_W o2-gpu-reco-workflow "--gpu-reconstruction \"$GPU_CONFIG_SELF\" --input-type=$GPU_INPUT $DISABLE_MC --output-type $GPU_OUTPUT $TPC_CORR_OPT $ITS_STAGGERED --pipeline gpu-reconstruction:${N_TPCTRK:-1},gpu-reconstruction-prepare:${N_TPCTRK:-1} $GPU_CONFIG" "GPU_global.deviceType=$GPUTYPE;GPU_proc.debugLevel=0;$GPU_CONFIG_KEY;$TRACKTUNETPCINNER;$TPC_CORR_KEY" (has_detector_reco TOF || has_detector_ctf TOF) && ! has_detector_from_global_reader TOF && add_W o2-tof-reco-workflow "$TOF_CONFIG --input-type $TOF_INPUT --output-type $TOF_OUTPUT $DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC --pipeline $(get_N tof-compressed-decoder TOF RAW 1),$(get_N TOFClusterer TOF REST 1)" has_detector_reco FT0 && ! has_detector_from_global_reader FT0 && add_W o2-ft0-reco-workflow "$DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC --pipeline $(get_N ft0-reconstructor FT0 REST 1)" has_detector_reco TRD && ! has_detector_from_global_reader TRD && add_W o2-trd-tracklet-transformer "--disable-irframe-reader $DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC $TRD_FILTER_CONFIG --pipeline $(get_N TRDTRACKLETTRANSFORMER TRD REST 1 TRDTRKTRANS)" -has_detectors_reco ITS TPC && ! has_detector_from_global_reader_tracks ITS-TPC && has_detector_matching ITSTPC && add_W o2-tpcits-match-workflow "$DISABLE_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC $SEND_ITSTPC_DTGL $TPC_CORR_OPT --nthreads $ITSTPC_THREADS --pipeline $(get_N itstpc-track-matcher MATCH REST $ITSTPC_THREADS TPCITS)" "$ITSTPC_CONFIG_KEY;$INTERACTION_TAG_CONFIG_KEY;$ITSMFT_STROBES;$ITSEXTRAERR;$TPC_CORR_KEY" +has_detectors_reco ITS TPC && ! has_detector_from_global_reader_tracks ITS-TPC && has_detector_matching ITSTPC && add_W o2-tpcits-match-workflow "$DISABLE_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC $ITS_STAGGERED $SEND_ITSTPC_DTGL $TPC_CORR_OPT --nthreads $ITSTPC_THREADS --pipeline $(get_N itstpc-track-matcher MATCH REST $ITSTPC_THREADS TPCITS)" "$ITSTPC_CONFIG_KEY;$INTERACTION_TAG_CONFIG_KEY;$ITSMFT_STROBES;$ITSEXTRAERR;$TPC_CORR_KEY" has_detector_reco TRD && [[ -n "$TRD_SOURCES" ]] && ! has_detector_from_global_reader_tracks "$(echo "$TRD_SOURCES" | cut -d',' -f1)-TRD" && add_W o2-trd-global-tracking "$DISABLE_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC $TRD_CONFIG $TRD_FILTER_CONFIG $TPC_CORR_OPT --track-sources $TRD_SOURCES --pipeline $(get_N trd-globaltracking_TPC_ITS-TPC_ TRD REST 1 TRDTRK),$(get_N trd-globaltracking_TPC_FT0_ITS-TPC_ TRD REST 1 TRDTRK),$(get_N trd-globaltracking_TPC_FT0_ITS-TPC_CTP_ TRD REST 1 TRDTRK)" "$TRD_CONFIG_KEY;$INTERACTION_TAG_CONFIG_KEY;$ITSMFT_STROBES;$ITSEXTRAERR;$TPC_CORR_KEY" has_detector_reco TOF && [[ -n "$TOF_SOURCES" ]] && ! has_detector_from_global_reader_tracks "$(echo "$TOF_SOURCES" | cut -d',' -f1)-TOF" && add_W o2-tof-matcher-workflow "$TOF_MATCH_OPT $DISABLE_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC $TPC_CORR_OPT ${TOFMATCH_THREADS:+--tof-lanes ${TOFMATCH_THREADS}} --track-sources $TOF_SOURCES --pipeline $(get_N tof-matcher TOF REST 1 TOFMATCH)" "$ITSMFT_STROBES;$ITSEXTRAERR;$TPC_CORR_KEY;$INTERACTION_TAG_CONFIG_KEY" has_detectors TPC && [[ -z "$DISABLE_ROOT_OUTPUT" && "${SKIP_TPC_CLUSTERSTRACKS_OUTPUT:-}" != 1 ]] && ! has_detector_from_global_reader TPC && add_W o2-tpc-reco-workflow "--input-type pass-through --output-type clusters,tpc-triggers,tracks,send-clusters-per-sector $DISABLE_MC" @@ -596,7 +600,7 @@ has_detectors TPC && [[ -z "$DISABLE_ROOT_OUTPUT" && "${SKIP_TPC_CLUSTERSTRACKS_ # Reconstruction workflows normally active only in async mode ($LIST_OF_ASYNC_RECO_STEPS), but can be forced via $WORKFLOW_EXTRA_PROCESSING_STEPS has_detector MID && ! has_detector_from_global_reader MID && has_processing_step MID_RECO && add_W o2-mid-reco-workflow "$DISABLE_ROOT_OUTPUT $DISABLE_MC --pipeline $(get_N MIDClusterizer MID REST 1),$(get_N MIDTracker MID REST 1)" has_detector MCH && ! has_detector_from_global_reader MCH && has_processing_step MCH_RECO && add_W o2-mch-reco-workflow "$DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC --pipeline $(get_N mch-track-finder MCH REST 1 MCHTRK),$(get_N mch-cluster-finder MCH REST 1 MCHCL),$(get_N mch-cluster-transformer MCH REST 1)" "$MCH_CONFIG_KEY" -has_detector MFT && ! has_detector_from_global_reader MFT && has_processing_step MFT_RECO && add_W o2-mft-reco-workflow "$DISABLE_MC ${DISABLE_DIGIT_CLUSTER_INPUT:-} $DISABLE_ROOT_OUTPUT $MFT_CONFIG --pipeline $(get_N mft-tracker MFT REST 1 MFTTRK)" "$MFT_CONFIG_KEY;$ITSMFT_STROBES" +has_detector MFT && ! has_detector_from_global_reader MFT && has_processing_step MFT_RECO && add_W o2-mft-reco-workflow "$DISABLE_MC ${DISABLE_DIGIT_CLUSTER_INPUT:-} $DISABLE_ROOT_OUTPUT $MFT_CONFIG $MFT_STAGGERED --pipeline $(get_N mft-tracker MFT REST 1 MFTTRK)" "$MFT_CONFIG_KEY;$ITSMFT_STROBES" has_detector FDD && ! has_detector_from_global_reader FDD && has_processing_step FDD_RECO && add_W o2-fdd-reco-workflow "$DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC" has_detector FV0 && ! has_detector_from_global_reader FV0 && has_processing_step FV0_RECO && add_W o2-fv0-reco-workflow "$DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC" has_detector ZDC && ! has_detector_from_global_reader ZDC && has_processing_step ZDC_RECO && add_W o2-zdc-digits-reco "$DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC" @@ -658,7 +662,7 @@ fi # Entropy encoding / ctf creation workflows - disabled in async mode if has_processing_step ENTROPY_ENCODER && [[ -n "$WORKFLOW_DETECTORS_CTF" ]] && [[ $WORKFLOW_DETECTORS_CTF != "NONE" ]]; then # Entropy encoder workflows - has_detector_ctf MFT && add_W o2-itsmft-entropy-encoder-workflow "$RANS_OPT --mem-factor ${MFT_ENC_MEMFACT:-1.5} --runmft true --pipeline $(get_N mft-entropy-encoder MFT CTF 1)" + has_detector_ctf MFT && add_W o2-itsmft-entropy-encoder-workflow "$RANS_OPT --mem-factor ${MFT_ENC_MEMFACT:-1.5} $MFT_STAGGERED --runmft true --pipeline $(get_N mft-entropy-encoder MFT CTF 1)" has_detector_ctf FT0 && add_W o2-ft0-entropy-encoder-workflow "$RANS_OPT --mem-factor ${FT0_ENC_MEMFACT:-1.5} --pipeline $(get_N ft0-entropy-encoder FT0 CTF 1)" has_detector_ctf FV0 && add_W o2-fv0-entropy-encoder-workflow "$RANS_OPT --mem-factor ${FV0_ENC_MEMFACT:-1.5} --pipeline $(get_N fv0-entropy-encoder FV0 CTF 1)" has_detector_ctf MID && add_W o2-mid-entropy-encoder-workflow "$RANS_OPT --mem-factor ${MID_ENC_MEMFACT:-1.5} --pipeline $(get_N mid-entropy-encoder MID CTF 1)" @@ -670,7 +674,7 @@ if has_processing_step ENTROPY_ENCODER && [[ -n "$WORKFLOW_DETECTORS_CTF" ]] && has_detector_ctf FDD && add_W o2-fdd-entropy-encoder-workflow "$RANS_OPT --mem-factor ${FDD_ENC_MEMFACT:-1.5} --pipeline $(get_N fdd-entropy-encoder FDD CTF 1)" has_detector_ctf HMP && add_W o2-hmpid-entropy-encoder-workflow "$RANS_OPT --mem-factor ${HMP_ENC_MEMFACT:-1.5} --pipeline $(get_N hmpid-entropy-encoder HMP CTF 1)" has_detector_ctf TOF && add_W o2-tof-entropy-encoder-workflow "$RANS_OPT --mem-factor ${TOF_ENC_MEMFACT:-1.5} --pipeline $(get_N tof-entropy-encoder TOF CTF 1)" - has_detector_ctf ITS && add_W o2-itsmft-entropy-encoder-workflow "$RANS_OPT --mem-factor ${ITS_ENC_MEMFACT:-1.5} --pipeline $(get_N its-entropy-encoder ITS CTF 1)" + has_detector_ctf ITS && add_W o2-itsmft-entropy-encoder-workflow "$RANS_OPT --mem-factor ${ITS_ENC_MEMFACT:-1.5} $ITS_STAGGERED --pipeline $(get_N its-entropy-encoder ITS CTF 1)" has_detector_ctf TRD && add_W o2-trd-entropy-encoder-workflow "$RANS_OPT --mem-factor ${TRD_ENC_MEMFACT:-1.5} --pipeline $(get_N trd-entropy-encoder TRD CTF 1 TRDENT)" has_detector_ctf TPC && add_W o2-tpc-reco-workflow " $RANS_OPT --mem-factor ${TPC_ENC_MEMFACT:-1.} --input-type compressed-clusters-flat-for-encode --output-type encoded-clusters,disable-writer --pipeline $(get_N tpc-entropy-encoder TPC CTF 1 TPCENT)" has_detector_ctf CTP && add_W o2-ctp-entropy-encoder-workflow "$RANS_OPT --mem-factor ${CTP_ENC_MEMFACT:-1.5} --pipeline $(get_N its-entropy-encoder CTP CTF 1)" @@ -690,7 +694,7 @@ if has_processing_step ENTROPY_ENCODER && [[ -n "$WORKFLOW_DETECTORS_CTF" ]] && CONFIG_CTF="--output-dir \"$CTF_DIR\" $CTF_CONFIG --output-type $CTF_OUTPUT_TYPE --min-file-size ${CTF_MINSIZE} --max-ctf-per-file ${CTF_MAX_PER_FILE} --onlyDet ${WORKFLOW_DETECTORS_CTF/TST/} --meta-output-dir $EPN2EOS_METAFILES_DIR" if [[ $CREATECTFDICT == 1 ]] && [[ $EXTINPUT == 1 ]]; then CONFIG_CTF+=" --save-dict-after $SAVE_CTFDICT_NTIMEFRAMES"; fi [[ $EPNSYNCMODE == 1 ]] && CONFIG_CTF+=" --require-free-disk 53687091200 --wait-for-free-disk $CTF_FREE_DISK_WAIT --max-wait-for-free-disk $CTF_MAX_FREE_DISK_WAIT" - add_W o2-ctf-writer-workflow "$CONFIG_CTF" + add_W o2-ctf-writer-workflow "$CONFIG_CTF $ITS_STAGGERED $MFT_STAGGERED" fi # --------------------------------------------------------------------------------------------------------------------- diff --git a/prodtests/full_system_test.sh b/prodtests/full_system_test.sh index 8e252c5a8378f..46739e76f103b 100755 --- a/prodtests/full_system_test.sh +++ b/prodtests/full_system_test.sh @@ -321,10 +321,6 @@ for STAGE in $STAGES; do : ${CUT_MULT_MIN_ITS:=-1} : ${CUT_MULT_MAX_ITS:=-1} : ${CUT_MULT_VTX_ITS:=-1} - : ${CUT_TRACKLETSPERCLUSTER_MAX_ITS:=100} - : ${CUT_CELLSPERCLUSTER_MAX_ITS:=100} - export CUT_TRACKLETSPERCLUSTER_MAX_ITS - export CUT_CELLSPERCLUSTER_MAX_ITS export CUT_RANDOM_FRACTION_ITS export CUT_MULT_MIN_ITS export CUT_MULT_MAX_ITS From 14ff7dbba9255ac1b852c4c629364a4f24759768 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tuba=20G=C3=BCndem?= <48834043+tubagundem@users.noreply.github.com> Date: Mon, 13 Apr 2026 15:02:33 +0200 Subject: [PATCH 473/701] TPC: Processing of common mode values in O2 (#15137) * TPC: Processing of common mode values in O2 * Added CMVContainer.cxx, fixed missing links and includes * Fix formatting * Removed unused includes, directly write the TTree object to CCDB without TMem file, small fixes * Changed the decoding and encoding of CMVs, removed grouping per side * Update the dataformat of CMV * Updated the CMVContainer, corrected the timestamp range for CCDB * Fix formatting * Removed factorize workflow, updated the distribute workflow accordingly * Fix formatting * Extend error tracking in CMVToVectorSpec.cxx * Replace CMVPerInterval with per TF TTree accumulation and raw uint16_t storage, fix CCDB timestamp for partial intervals, fix TMemFile padding * Added delta+zigzag+varint compression, added drawCMV.C macro for visualization * Added small value zeroing, added `--use-compression` and `--cmv-zero-threshold` flags to TPCDistributeCMVSpec, updated drawCMV.C macro to auto detect branch format (compressed or not) * Added sparse and HUffman encoding to the CMVContainer, updatet the drawCMV.C macro and TPCDistributeCMVSpec.h accordingly * Added CMVPerTFCombined to CMVContainer to combine sparse encoding with varint/Huffman compression, updated drawing macro and workflow options accordingly * Added gaussian dynamic precision * Refactored CMVContainer, unified CMV compression in a flag based container * Added CMV workflow documentation to README and fixed CMV packet size mismatch handling --- .../TPC/include/DataFormatsTPC/CMV.h | 122 +++ Detectors/TPC/base/include/TPCBase/RDHUtils.h | 3 +- Detectors/TPC/calibration/CMakeLists.txt | 8 +- .../include/TPCCalibration/CMVContainer.h | 141 ++++ Detectors/TPC/calibration/macro/drawCMV.C | 160 ++++ .../TPC/calibration/src/CMVContainer.cxx | 729 ++++++++++++++++++ .../calibration/src/TPCCalibrationLinkDef.h | 4 + Detectors/TPC/workflow/CMakeLists.txt | 18 +- Detectors/TPC/workflow/README.md | 188 +++++ .../include/TPCWorkflow/CMVToVectorSpec.h | 30 + .../TPCWorkflow/TPCDistributeCMVSpec.h | 621 +++++++++++++++ .../include/TPCWorkflow/TPCFLPCMVSpec.h | 172 +++++ .../TPC/workflow/src/CMVToVectorSpec.cxx | 434 +++++++++++ .../TPC/workflow/src/tpc-cmv-to-vector.cxx | 71 ++ .../TPC/workflow/src/tpc-distribute-cmv.cxx | 84 ++ Detectors/TPC/workflow/src/tpc-flp-cmv.cxx | 72 ++ 16 files changed, 2854 insertions(+), 3 deletions(-) create mode 100644 DataFormats/Detectors/TPC/include/DataFormatsTPC/CMV.h create mode 100644 Detectors/TPC/calibration/include/TPCCalibration/CMVContainer.h create mode 100644 Detectors/TPC/calibration/macro/drawCMV.C create mode 100644 Detectors/TPC/calibration/src/CMVContainer.cxx create mode 100644 Detectors/TPC/workflow/include/TPCWorkflow/CMVToVectorSpec.h create mode 100644 Detectors/TPC/workflow/include/TPCWorkflow/TPCDistributeCMVSpec.h create mode 100644 Detectors/TPC/workflow/include/TPCWorkflow/TPCFLPCMVSpec.h create mode 100644 Detectors/TPC/workflow/src/CMVToVectorSpec.cxx create mode 100644 Detectors/TPC/workflow/src/tpc-cmv-to-vector.cxx create mode 100644 Detectors/TPC/workflow/src/tpc-distribute-cmv.cxx create mode 100644 Detectors/TPC/workflow/src/tpc-flp-cmv.cxx diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CMV.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CMV.h new file mode 100644 index 0000000000000..109eff2654466 --- /dev/null +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CMV.h @@ -0,0 +1,122 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file CMV.h +/// @author Tuba Gündem, tuba.gundem@cern.ch +/// @brief Common mode values data format definition + +/// The data is sent by the CRU as 256+16 bit words. The CMV data layout is as follows: +/// - 256-bit Header: [version:8][packetID:8][errorCode:8][magicWord:8][heartbeatOrbit:32][heartbeatBC:16][padding:176] +/// - 16-bit CMV value: [sign:1][I8F7:15] where bit 15 is the sign (1=positive, 0=negative) and the lower 15 bits are a fixed point I8F7 value (8 integer bits, 7 fractional bits) +/// Float conversion: sign ? (value & 0x7FFF) / 128.0 : -(value & 0x7FFF) / 128.0 + +#ifndef ALICEO2_DATAFORMATSTPC_CMV_H +#define ALICEO2_DATAFORMATSTPC_CMV_H + +#include +#include + +namespace o2::tpc::cmv +{ + +static constexpr uint32_t NTimeBinsPerPacket = 3564; ///< number of time bins (covering 8 heartbeats) +static constexpr uint32_t NPacketsPerTFPerCRU = 4; ///< 4 packets per timeframe +static constexpr uint32_t NTimeBinsPerTF = NTimeBinsPerPacket * NPacketsPerTFPerCRU; ///< maximum number of timebins per timeframe (14256) + +/// Data padding: NTimeBinsPerPacket * sizeof(Data) = 3564 * 2 = 7128 bytes +static constexpr uint32_t DataSizeBytes = NTimeBinsPerPacket * sizeof(uint16_t); ///< 7128 bytes +static constexpr uint32_t DataPaddingBytes = (32 - (DataSizeBytes % 32)) % 32; ///< 8 bytes + +/// Header definition of the CMVs +struct Header { + static constexpr uint8_t MagicWord = 0xDC; + union { + uint64_t word0 = 0; ///< bits 0 - 63 + struct { + uint8_t version : 8; ///< version + uint8_t packetID : 8; ///< packet id + uint8_t errorCode : 8; ///< errors + uint8_t magicWord : 8; ///< magic word + uint32_t heartbeatOrbit : 32; ///< first heart beat timing of the package + }; + }; + union { + uint64_t word1 = 0; ///< bits 64 - 127 + struct { + uint16_t heartbeatBC : 16; ///< first BC id of the package + uint16_t unused1 : 16; ///< reserved + uint32_t unused2 : 32; ///< reserved + }; + }; + union { + uint64_t word3 = 0; ///< bits 128 - 191 + struct { + uint64_t unused3 : 64; ///< reserved + }; + }; + union { + uint64_t word4 = 0; ///< bits 192 - 255 + struct { + uint64_t unused4 : 64; ///< reserved + }; + }; +}; + +/// CMV single data container +struct Data { + uint16_t cmv{0}; ///< 16-bit signed fixed point value: bit 15 = sign (1=positive, 0=negative), bits 14-0 = I8F7 magnitude + + uint16_t getCMV() const { return cmv; } ///< raw 16-bit integer representation + void setCMV(uint16_t value) { cmv = value; } ///< set raw 16-bit integer representation + + // Decode to float: sign-magnitude with 7 fractional bits, range ±255.992 + float getCMVFloat() const + { + const bool positive = (cmv >> 15) & 1; // bit 15: sign (1=positive, 0=negative) + const float magnitude = (cmv & 0x7FFF) / 128.f; // lower 15 bits, shift right by 7 (divide by 2^7) + return positive ? magnitude : -magnitude; + } + + // Encode from float: clamps magnitude to 15 bits, range ±255.992 + void setCMVFloat(float value) + { + const bool positive = (value >= 0.f); + const uint16_t magnitude = static_cast(std::abs(value) * 128.f + 0.5f) & 0x7FFF; + cmv = (positive ? 0x8000 : 0x0000) | magnitude; + } +}; + +/// CMV full data container: one packet carries NTimeBinsPerPacket CMV values followed by padding +/// Layout: Header (32 bytes) + Data[NTimeBinsPerPacket] (7128 bytes) + padding (8 bytes) = 7168 bytes total (224 * 32 = 7168) +/// The padding bytes at the end of the data array are rubbish/unused and must not be interpreted as CMV values +struct Container { + Header header; ///< CMV data header + Data data[NTimeBinsPerPacket]; ///< data values + uint8_t padding[DataPaddingBytes]{}; ///< trailing padding to align data to 32-byte boundary + + // Header and data accessors + const Header& getHeader() const { return header; } + Header& getHeader() { return header; } + + const Data* getData() const { return data; } + Data* getData() { return data; } + + // Per timebin CMV accessors + uint16_t getCMV(uint32_t timeBin) const { return data[timeBin].getCMV(); } + void setCMV(uint32_t timeBin, uint16_t value) { data[timeBin].setCMV(value); } + + float getCMVFloat(uint32_t timeBin) const { return data[timeBin].getCMVFloat(); } + void setCMVFloat(uint32_t timeBin, float value) { data[timeBin].setCMVFloat(value); } +}; + +} // namespace o2::tpc::cmv + +#endif \ No newline at end of file diff --git a/Detectors/TPC/base/include/TPCBase/RDHUtils.h b/Detectors/TPC/base/include/TPCBase/RDHUtils.h index adfd94cf6b703..71b5d16b85702 100644 --- a/Detectors/TPC/base/include/TPCBase/RDHUtils.h +++ b/Detectors/TPC/base/include/TPCBase/RDHUtils.h @@ -13,7 +13,7 @@ #define AliceO2_TPC_RDHUtils_H #include "DetectorsRaw/RDHUtils.h" -//#include "Headers/RAWDataHeader.h" +// #include "Headers/RAWDataHeader.h" namespace o2 { @@ -28,6 +28,7 @@ static constexpr FEEIDType UserLogicLinkID = 15; ///< virtual link ID for ZS dat static constexpr FEEIDType IDCLinkID = 20; ///< Identifier for integrated digital currents static constexpr FEEIDType ILBZSLinkID = 21; ///< Identifier for improved link-based ZS static constexpr FEEIDType DLBZSLinkID = 22; ///< Identifier for dense link-based ZS +static constexpr FEEIDType CMVLinkID = 23; ///< Identifier for common mode values static constexpr FEEIDType SACLinkID = 25; ///< Identifier for sampled analog currents /// compose feeid from cru, endpoint and link diff --git a/Detectors/TPC/calibration/CMakeLists.txt b/Detectors/TPC/calibration/CMakeLists.txt index 27f7f0200bb92..a1068b928780d 100644 --- a/Detectors/TPC/calibration/CMakeLists.txt +++ b/Detectors/TPC/calibration/CMakeLists.txt @@ -58,6 +58,7 @@ o2_add_library(TPCCalibration src/DigitAdd.cxx src/CorrectdEdxDistortions.cxx src/PressureTemperatureHelper.cxx + src/CMVContainer.cxx PUBLIC_LINK_LIBRARIES O2::DataFormatsTPC O2::TPCBaseRecSim O2::TPCReconstruction ROOT::Minuit Microsoft.GSL::GSL @@ -115,7 +116,8 @@ o2_target_root_dictionary(TPCCalibration include/TPCCalibration/TPCMShapeCorrection.h include/TPCCalibration/DigitAdd.h include/TPCCalibration/CorrectdEdxDistortions.h - include/TPCCalibration/PressureTemperatureHelper.h) + include/TPCCalibration/PressureTemperatureHelper.h + include/TPCCalibration/CMVContainer.h) o2_add_test_root_macro(macro/comparePedestalsAndNoise.C PUBLIC_LINK_LIBRARIES O2::TPCBaseRecSim @@ -153,6 +155,10 @@ o2_add_test_root_macro(macro/prepareITFiles.C COMPILE_ONLY PUBLIC_LINK_LIBRARIES O2::TPCCalibration LABELS tpc) +o2_add_test_root_macro(macro/drawCMV.C + COMPILE_ONLY + PUBLIC_LINK_LIBRARIES O2::TPCCalibration O2::TPCBase + LABELS tpc) o2_add_test(IDCFourierTransform COMPONENT_NAME calibration diff --git a/Detectors/TPC/calibration/include/TPCCalibration/CMVContainer.h b/Detectors/TPC/calibration/include/TPCCalibration/CMVContainer.h new file mode 100644 index 0000000000000..f1904c3db8f8d --- /dev/null +++ b/Detectors/TPC/calibration/include/TPCCalibration/CMVContainer.h @@ -0,0 +1,141 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file CMVContainer.h +/// @author Tuba Gündem, tuba.gundem@cern.ch +/// @brief Structs for storing CMVs to the CCDB + +#ifndef ALICEO2_TPC_CMVCONTAINER_H_ +#define ALICEO2_TPC_CMVCONTAINER_H_ + +#include +#include +#include +#include + +#include "TTree.h" +#include "TPCBase/CRU.h" +#include "DataFormatsTPC/CMV.h" + +namespace o2::tpc +{ + +struct CMVPerTF; // forward declaration +struct CMVPerTFCompressed; // forward declaration + +/// Bitmask flags describing which encoding stages are applied in CMVPerTFCompressed +struct CMVEncoding { + static constexpr uint8_t kNone = 0x00; ///< No compression — raw uint16 values stored flat + static constexpr uint8_t kSparse = 0x01; ///< Non-zero positions stored sparsely (varint-encoded deltas) + static constexpr uint8_t kDelta = 0x02; ///< Delta coding between consecutive values (dense only) + static constexpr uint8_t kZigzag = 0x04; ///< Zigzag encoding of deltas or signed values + static constexpr uint8_t kVarint = 0x08; ///< Varint compression of the value stream + static constexpr uint8_t kHuffman = 0x10; ///< Canonical Huffman compression of the value stream +}; + +/// Single compressed representation for one TF across all CRUs, stored in a TTree +/// mFlags is a bitmask of CMVEncoding values that fully describes the encoding pipeline +/// mData holds the encoded payload whose binary layout depends on mFlags: +/// +/// Dense path (!kSparse): +/// kZigzag absent → N × uint16_t LE (raw values, CRU-major order) +/// kZigzag + kVarint → N × varint(zigzag(delta(signed(raw)))) +/// kZigzag + kHuffman → [Huffman table] + [bitstream] of zigzag(delta(signed(raw))) +/// +/// Sparse path (kSparse): +/// 4 bytes LE uint32_t : posStreamSize +/// posStream: for each CRU: varint(N), N × varint(tb_delta) +/// valStream (one entry per non-zero): +/// default → uint16_t LE raw value +/// kZigzag + kVarint → varint(zigzag(signed(raw))) +/// kZigzag + kHuffman → [Huffman table] + [bitstream] of zigzag(signed(raw)) +struct CMVPerTFCompressed { + uint32_t firstOrbit{0}; ///< First orbit of this TF + uint16_t firstBC{0}; ///< First bunch crossing of this TF + uint8_t mFlags{0}; ///< Bitmask of CMVEncoding values + + std::vector mData; ///< Encoded payload + + /// Restore a CMVPerTF from this compressed object into *cmv (must not be null) + void decompress(CMVPerTF* cmv) const; + + /// Serialise into a TTree; each Fill() call appends one entry (one TF) + std::unique_ptr toTTree() const; + + private: + /// Decode the sparse position stream; advances ptr past the position block + /// Returns (cru, timeBin) pairs for every non-zero entry, in CRU-major order + static std::vector> decodeSparsePositions(const uint8_t*& ptr, const uint8_t* end); + + /// Decode the value stream into raw uint32_t symbols + /// Dispatches to Huffman, varint, or raw uint16 based on flags + static std::vector decodeValueStream(const uint8_t*& ptr, const uint8_t* end, uint32_t N, uint8_t flags); + + /// Apply inverse zigzag and scatter decoded values into the sparse positions of *cmv + static void decodeSparseValues(const std::vector& symbols, + const std::vector>& positions, + uint8_t flags, CMVPerTF* cmv); + + /// Apply inverse zigzag and inverse delta, then fill the full dense CMV array in *cmv + static void decodeDenseValues(const std::vector& symbols, uint8_t flags, CMVPerTF* cmv); + + public: + ClassDefNV(CMVPerTFCompressed, 1) +}; + +/// CMV data for one TF across all CRUs +/// Raw 16-bit CMV values are stored in a flat C array indexed as [cru * NTimeBinsPerTF + timeBin] +struct CMVPerTF { + uint32_t firstOrbit{0}; ///< First orbit of this TF, from heartbeatOrbit of the first CMV packet + uint16_t firstBC{0}; ///< First bunch crossing of this TF, from heartbeatBC of the first CMV packet + + // Raw 16-bit CMV values, flat array indexed as [cru * NTimeBinsPerTF + timeBin] + uint16_t mDataPerTF[CRU::MaxCRU * cmv::NTimeBinsPerTF]{}; + + /// Return the raw 16-bit CMV value for a given CRU and timebin within this TF + uint16_t getCMV(const int cru, const int timeBin) const; + + /// Return the float CMV value for a given CRU and timebin within this TF + float getCMVFloat(const int cru, const int timeBin) const; + + /// Zero out raw CMV values whose float magnitude is below threshold + void zeroSmallValues(float threshold = 1.0f); + + /// Round values to the nearest integer ADC for all values whose rounded magnitude is <= threshold + void roundToIntegers(uint16_t threshold); + + /// Quantise |v| with a Gaussian-CDF recovery profile: + /// Coarse decimal-style precision below and around mean, then a smooth return to the full native I8F7 precision as the magnitude increases with width sigma + void trimGaussianPrecision(float mean, float sigma); + + /// Compress this object into a CMVPerTFCompressed using the encoding pipeline described by flags + /// Quantisation (trimGaussianPrecision / roundToIntegers / zeroSmallValues) should be applied to this object before calling compress(); it is not part of the flags pipeline + CMVPerTFCompressed compress(uint8_t flags) const; + + /// Serialise into a TTree; each Fill() call appends one entry (one TF) + std::unique_ptr toTTree() const; + + /// Write the TTree to a ROOT file + static void writeToFile(const std::string& filename, const std::unique_ptr& tree); + + private: + static int32_t cmvToSigned(uint16_t raw); ///< Sign-magnitude uint16_t → signed integer + static uint16_t quantizeBelowThreshold(uint16_t raw, float quantizationMean, float quantizationSigma); ///< Quantise sub-threshold values with a Gaussian-shaped recovery to full precision + static uint32_t zigzagEncode(int32_t value); ///< Zigzag encode + static void encodeVarintInto(uint32_t value, std::vector& out); ///< Varint encode + + public: + ClassDefNV(CMVPerTF, 1) +}; + +} // namespace o2::tpc + +#endif // ALICEO2_TPC_CMVCONTAINER_H_ diff --git a/Detectors/TPC/calibration/macro/drawCMV.C b/Detectors/TPC/calibration/macro/drawCMV.C new file mode 100644 index 0000000000000..8a89157b75721 --- /dev/null +++ b/Detectors/TPC/calibration/macro/drawCMV.C @@ -0,0 +1,160 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does 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 "TFile.h" +#include "TParameter.h" +#include "TTree.h" +#include "TH1F.h" +#include "TH2F.h" +#include "TCanvas.h" + +#include "TPCCalibration/CMVContainer.h" +#include "TPCBase/Utils.h" +#endif + +using namespace o2::tpc; + +/// Draw CMV (Common Mode Values) vs timebin from a CCDB TTree file +/// \param filename input ROOT file containing the ccdb_object TTree +/// \param outDir output directory for saved plots; nothing is saved if empty +/// \return array of canvases +TObjArray* drawCMV(std::string_view filename, std::string_view outDir) +{ + TObjArray* arrCanvases = new TObjArray; + arrCanvases->SetName("CMV"); + + // open file + TFile f(filename.data(), "READ"); + if (f.IsZombie()) { + fmt::print("ERROR: cannot open '{}'\n", filename); + return arrCanvases; + } + fmt::print("Opened file: {}\n", filename); + + // get TTree + TTree* tree = nullptr; + f.GetObject("ccdb_object", tree); + if (!tree) { + fmt::print("ERROR: TTree 'ccdb_object' not found\n"); + return arrCanvases; + } + fmt::print("Tree 'ccdb_object' found, entries: {}\n", tree->GetEntries()); + + // read metadata + long firstTF = -1, lastTF = -1; + if (auto* userInfo = tree->GetUserInfo()) { + for (int i = 0; i < userInfo->GetSize(); ++i) { + if (auto* p = dynamic_cast*>(userInfo->At(i))) { + if (std::string(p->GetName()) == "firstTF") + firstTF = p->GetVal(); + if (std::string(p->GetName()) == "lastTF") + lastTF = p->GetVal(); + } + } + } + fmt::print("firstTF: {}, lastTF: {}\n", firstTF, lastTF); + + const int nEntries = tree->GetEntries(); + if (nEntries == 0) { + fmt::print("ERROR: no entries in tree\n"); + return arrCanvases; + } + + constexpr int nCRUs = CRU::MaxCRU; + constexpr int nTimeBins = cmv::NTimeBinsPerTF; + + TH2F* h2d = new TH2F("hCMVvsTimeBin", ";Timebin (200 ns);Common Mode Values (ADC)", + 100, 0, nTimeBins, + 110, -100.5, 9.5); + h2d->SetStats(1); + TH1F* h1d = new TH1F("hCMV", ";Common Mode Values (ADC);Counts", + 1100, -100.5, 9.5); + h1d->SetStats(1); + + // auto-detect branch format: compressed or raw + const bool isCompressed = (tree->GetBranch("CMVPerTFCompressed") != nullptr); + const bool isRaw = (tree->GetBranch("CMVPerTF") != nullptr); + if (!isCompressed && !isRaw) { + fmt::print("ERROR: no recognised branch found (expected 'CMVPerTFCompressed' or 'CMVPerTF')\n"); + return arrCanvases; + } + fmt::print("Branch format: {}\n", isCompressed ? "CMVPerTFCompressed" : "CMVPerTF (raw)"); + + o2::tpc::CMVPerTFCompressed* tfCompressed = nullptr; + o2::tpc::CMVPerTF* tfRaw = nullptr; + CMVPerTF* tfDecoded = isCompressed ? new CMVPerTF() : nullptr; + + if (isCompressed) { + tree->SetBranchAddress("CMVPerTFCompressed", &tfCompressed); + } else { + tree->SetBranchAddress("CMVPerTF", &tfRaw); + } + + long firstOrbit = -1; + + for (int i = 0; i < nEntries; ++i) { + tree->GetEntry(i); + + // Decompress if needed; resolve to a unified CMVPerTF pointer + const CMVPerTF* tf = nullptr; + if (isCompressed) { + tfCompressed->decompress(tfDecoded); + tf = tfDecoded; + } else { + tf = tfRaw; + } + + if (i == 0) { + firstOrbit = tf->firstOrbit; + } + + for (int cru = 0; cru < nCRUs; ++cru) { + for (int tb = 0; tb < nTimeBins; ++tb) { + const float cmvValue = tf->getCMVFloat(cru, tb); + h2d->Fill(tb, cmvValue); + h1d->Fill(cmvValue); + // fmt::print("cru: {}, tb: {}, cmv: {}\n", cru, tb, cmvValue); + } + } + } + + delete tfDecoded; + tree->ResetBranchAddresses(); + delete tfCompressed; + + fmt::print("firstOrbit: {}\n", firstOrbit); + + // draw + auto* c = new TCanvas("cCMVvsTimeBin", ""); + c->SetLogz(); + h2d->Draw("colz"); + + arrCanvases->Add(c); + + auto* c1 = new TCanvas("cCMVDistribution", ""); + c1->SetLogy(); + h1d->Draw(); + + arrCanvases->Add(c1); + + if (outDir.size()) { + utils::saveCanvases(*arrCanvases, outDir, "png,pdf", "CMVCanvases.root"); + } + + f.Close(); + return arrCanvases; +} diff --git a/Detectors/TPC/calibration/src/CMVContainer.cxx b/Detectors/TPC/calibration/src/CMVContainer.cxx new file mode 100644 index 0000000000000..5a3b8f1c63c3a --- /dev/null +++ b/Detectors/TPC/calibration/src/CMVContainer.cxx @@ -0,0 +1,729 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file CMVContainer.cxx +/// @author Tuba Gündem, tuba.gundem@cern.ch + +#include +#include +#include +#include +#include +#include + +#include "TFile.h" + +#include "TPCCalibration/CMVContainer.h" +#include "TPCBase/CRU.h" +#include "DataFormatsTPC/CMV.h" + +namespace o2::tpc +{ + +// CMVPerTF private helpers + +int32_t CMVPerTF::cmvToSigned(uint16_t raw) +{ + const int32_t mag = raw & 0x7FFF; + return (raw >> 15) ? mag : -mag; +} + +uint16_t CMVPerTF::quantizeBelowThreshold(uint16_t raw, float quantizationMean, float quantizationSigma) +{ + if (raw == 0u) { + return raw; + } + + if (quantizationSigma <= 0.f) { + return raw; + } + + const float adc = (raw & 0x7FFFu) / 128.f; + const float distance = (adc - quantizationMean) / quantizationSigma; + const float lossStrength = std::exp(-0.5f * distance * distance); + + // A true Gaussian bell: strongest trimming around the mean, then gradual recovery away from it + float quantizedADC = adc; + if (lossStrength > 0.85f) { + quantizedADC = std::round(adc * 10.f) / 10.f; + } else if (lossStrength > 0.60f) { + quantizedADC = std::round(adc * 100.f) / 100.f; + } else if (lossStrength > 0.30f) { + quantizedADC = std::round(adc * 1000.f) / 1000.f; + } else if (lossStrength > 0.12f) { + quantizedADC = std::round(adc * 10000.f) / 10000.f; + } else if (lossStrength > 0.03f) { + quantizedADC = std::round(adc * 1000000.f) / 1000000.f; + } + + // Snap the chosen decimal-style value back to the nearest raw I8F7 level + const uint16_t quantizedMagnitude = static_cast(std::clamp(std::lround(quantizedADC * 128.f), 0l, 0x7FFFl)); + return static_cast((raw & 0x8000u) | quantizedMagnitude); +} + +uint32_t CMVPerTF::zigzagEncode(int32_t value) +{ + return (static_cast(value) << 1) ^ static_cast(value >> 31); +} + +void CMVPerTF::encodeVarintInto(uint32_t value, std::vector& out) +{ + while (value > 0x7F) { + out.push_back(static_cast((value & 0x7F) | 0x80)); + value >>= 7; + } + out.push_back(static_cast(value)); +} + +// Shared file-local helpers + +namespace +{ + +int32_t zigzagDecodeLocal(uint32_t value) +{ + return static_cast((value >> 1) ^ -(value & 1)); +} + +uint16_t signedToCmvLocal(int32_t val) +{ + const uint16_t mag = static_cast(std::abs(val)) & 0x7FFF; + return static_cast((val >= 0 ? 0x8000u : 0u) | mag); +} + +uint32_t decodeVarintLocal(const uint8_t*& data, const uint8_t* end) +{ + uint32_t value = 0; + int shift = 0; + while (data < end && (*data & 0x80)) { + value |= static_cast(*data & 0x7F) << shift; + shift += 7; + ++data; + } + if (data >= end) { + throw std::runtime_error("decodeVarintLocal: unexpected end of varint data"); + } + value |= static_cast(*data) << shift; + ++data; + return value; +} + +/// Build and serialise a canonical Huffman table + bitstream over `symbols` into `buf` +/// Format: +/// 4 bytes LE uint32_t : numSymbols +/// numSymbols × 5 bytes: symbol (4 bytes LE) + code length (1 byte) +/// 8 bytes LE uint64_t : totalBits +/// ceil(totalBits/8) bytes: MSB-first bitstream +void huffmanEncode(const std::vector& symbols, std::vector& buf) +{ + // Frequency count + std::map freq; + for (const uint32_t z : symbols) { + ++freq[z]; + } + + // Build tree using index-based min-heap + struct HNode { + uint64_t freq{0}; + uint32_t sym{0}; + int left{-1}, right{-1}; + bool isLeaf{true}; + }; + std::vector nodes; + nodes.reserve(freq.size() * 2); + for (const auto& [sym, f] : freq) { + nodes.push_back({f, sym, -1, -1, true}); + } + + auto cmp = [&](int a, int b) { + return nodes[a].freq != nodes[b].freq ? nodes[a].freq > nodes[b].freq : nodes[a].sym > nodes[b].sym; + }; + std::vector heap; + heap.reserve(nodes.size()); + for (int i = 0; i < static_cast(nodes.size()); ++i) { + heap.push_back(i); + } + std::make_heap(heap.begin(), heap.end(), cmp); + + while (heap.size() > 1) { + std::pop_heap(heap.begin(), heap.end(), cmp); + const int a = heap.back(); + heap.pop_back(); + std::pop_heap(heap.begin(), heap.end(), cmp); + const int b = heap.back(); + heap.pop_back(); + nodes.push_back({nodes[a].freq + nodes[b].freq, 0, a, b, false}); + heap.push_back(static_cast(nodes.size()) - 1); + std::push_heap(heap.begin(), heap.end(), cmp); + } + + // Assign code lengths via iterative DFS + std::map codeLens; + { + const int root = heap[0]; + std::vector> stack; + stack.push_back({root, 0}); + while (!stack.empty()) { + auto [idx, depth] = stack.back(); + stack.pop_back(); + if (nodes[idx].isLeaf) { + codeLens[nodes[idx].sym] = static_cast(depth == 0 ? 1 : depth); + } else { + stack.push_back({nodes[idx].left, depth + 1}); + stack.push_back({nodes[idx].right, depth + 1}); + } + } + } + + // Sort by (codeLen ASC, symbol ASC) for canonical assignment + struct SymLen { + uint32_t sym; + uint8_t len; + }; + std::vector symLens; + symLens.reserve(codeLens.size()); + for (const auto& [sym, len] : codeLens) { + symLens.push_back({sym, len}); + } + std::sort(symLens.begin(), symLens.end(), [](const SymLen& a, const SymLen& b) { + return a.len != b.len ? a.len < b.len : a.sym < b.sym; + }); + + // Assign canonical codes + std::map> codeTable; + { + uint32_t code = 0; + uint8_t prevLen = 0; + for (const auto& sl : symLens) { + if (prevLen != 0) { + code = (code + 1) << (sl.len - prevLen); + } + codeTable[sl.sym] = {code, sl.len}; + prevLen = sl.len; + } + } + + // Serialise table header + buf.reserve(buf.size() + 4 + symLens.size() * 5 + 8 + (symbols.size() / 8 + 1)); + const uint32_t numSym = static_cast(symLens.size()); + for (int i = 0; i < 4; ++i) { + buf.push_back(static_cast((numSym >> (8 * i)) & 0xFF)); + } + for (const auto& sl : symLens) { + for (int i = 0; i < 4; ++i) { + buf.push_back(static_cast((sl.sym >> (8 * i)) & 0xFF)); + } + buf.push_back(sl.len); + } + + // Placeholder for totalBits + const size_t totalBitsOffset = buf.size(); + for (int i = 0; i < 8; ++i) { + buf.push_back(0); + } + + // Encode bitstream (MSB-first) + uint64_t totalBits = 0; + uint8_t curByte = 0; + int bitsInByte = 0; + for (const uint32_t z : symbols) { + const auto& [code, len] = codeTable.at(z); + for (int b = static_cast(len) - 1; b >= 0; --b) { + curByte = static_cast(curByte | (((code >> b) & 1u) << (7 - bitsInByte))); + ++bitsInByte; + ++totalBits; + if (bitsInByte == 8) { + buf.push_back(curByte); + curByte = 0; + bitsInByte = 0; + } + } + } + if (bitsInByte > 0) { + buf.push_back(curByte); + } + + // Backfill totalBits + for (int i = 0; i < 8; ++i) { + buf[totalBitsOffset + i] = static_cast((totalBits >> (8 * i)) & 0xFF); + } +} + +/// Decode `N` symbols from a canonical Huffman payload at [ptr, end) +/// `ptr` must point to the start of the Huffman table header (numSymbols field) +/// After return, `ptr` is advanced past the bitstream +std::vector huffmanDecode(const uint8_t*& ptr, const uint8_t* end, uint32_t N) +{ + auto readU32 = [&]() -> uint32_t { + if (ptr + 4 > end) { + throw std::runtime_error("huffmanDecode: unexpected end reading uint32"); + } + const uint32_t v = static_cast(ptr[0]) | (static_cast(ptr[1]) << 8) | + (static_cast(ptr[2]) << 16) | (static_cast(ptr[3]) << 24); + ptr += 4; + return v; + }; + + const uint32_t numSym = readU32(); + struct SymLen { + uint32_t sym; + uint8_t len; + }; + std::vector symLens(numSym); + for (uint32_t i = 0; i < numSym; ++i) { + symLens[i].sym = readU32(); + if (ptr >= end) { + throw std::runtime_error("huffmanDecode: unexpected end reading code length"); + } + symLens[i].len = *ptr++; + } + + std::map firstCode; + std::map> symsByLen; + { + uint32_t code = 0; + uint8_t prevLen = 0; + for (const auto& sl : symLens) { + if (prevLen != 0) { + code = (code + 1) << (sl.len - prevLen); + } + if (!firstCode.count(sl.len)) { + firstCode[sl.len] = code; + } + symsByLen[sl.len].push_back(sl.sym); + prevLen = sl.len; + } + } + + if (ptr + 8 > end) { + throw std::runtime_error("huffmanDecode: unexpected end reading totalBits"); + } + uint64_t totalBits = 0; + for (int i = 0; i < 8; ++i) { + totalBits |= static_cast(ptr[i]) << (8 * i); + } + ptr += 8; + + const uint8_t minLen = symLens.empty() ? 1 : symLens.front().len; + const uint8_t maxLen = symLens.empty() ? 1 : symLens.back().len; + uint64_t bitsRead = 0; + uint8_t curByte = 0; + int bitPos = -1; + + auto nextBit = [&]() -> int { + if (bitPos < 0) { + if (ptr >= end) { + throw std::runtime_error("huffmanDecode: unexpected end of bitstream"); + } + curByte = *ptr++; + bitPos = 7; + } + const int bit = (curByte >> bitPos) & 1; + --bitPos; + return bit; + }; + + std::vector out; + out.reserve(N); + while (out.size() < N) { + uint32_t accum = 0; + bool found = false; + for (uint8_t curLen = 1; curLen <= maxLen; ++curLen) { + if (bitsRead >= totalBits) { + throw std::runtime_error("huffmanDecode: bitstream exhausted before all symbols decoded"); + } + accum = (accum << 1) | static_cast(nextBit()); + ++bitsRead; + if (curLen < minLen) { + continue; + } + const auto fcIt = firstCode.find(curLen); + if (fcIt == firstCode.end()) { + continue; + } + if (accum >= fcIt->second) { + const uint32_t idx = accum - fcIt->second; + const auto& sv = symsByLen.at(curLen); + if (idx < sv.size()) { + out.push_back(sv[idx]); + found = true; + break; + } + } + } + if (!found) { + throw std::runtime_error("huffmanDecode: invalid Huffman code in bitstream"); + } + } + return out; +} + +} // anonymous namespace + +// CMVPerTF public methods + +uint16_t CMVPerTF::getCMV(const int cru, const int timeBin) const +{ + if (cru < 0 || cru >= static_cast(CRU::MaxCRU)) { + throw std::out_of_range(fmt::format("CMVPerTF::getCMV: cru {} out of range [0, {})", cru, static_cast(CRU::MaxCRU))); + } + if (timeBin < 0 || static_cast(timeBin) >= cmv::NTimeBinsPerTF) { + throw std::out_of_range(fmt::format("CMVPerTF::getCMV: timeBin {} out of range [0, {})", timeBin, static_cast(cmv::NTimeBinsPerTF))); + } + return mDataPerTF[cru * cmv::NTimeBinsPerTF + timeBin]; +} + +float CMVPerTF::getCMVFloat(const int cru, const int timeBin) const +{ + const uint16_t raw = getCMV(cru, timeBin); + const uint16_t mag = raw & 0x7FFF; + if (mag == 0) { + return 0.0f; // 0x0000 and 0x8000 both represent zero; return +0 to avoid -0 display + } + const bool positive = (raw >> 15) & 1; // bit 15: sign (1=positive, 0=negative) + return positive ? mag / 128.f : -mag / 128.f; +} + +void CMVPerTF::zeroSmallValues(float threshold) +{ + if (threshold <= 0.f) { + return; + } + for (uint32_t i = 0; i < static_cast(CRU::MaxCRU) * cmv::NTimeBinsPerTF; ++i) { + const float mag = (mDataPerTF[i] & 0x7FFF) / 128.f; + if (mag < threshold) { + mDataPerTF[i] = 0; + } + } +} + +void CMVPerTF::roundToIntegers(uint16_t threshold) +{ + if (threshold == 0) { + return; + } + for (uint32_t i = 0; i < static_cast(CRU::MaxCRU) * cmv::NTimeBinsPerTF; ++i) { + const uint16_t raw = mDataPerTF[i]; + if (raw == 0) { + continue; + } + const uint16_t rounded = static_cast(((raw & 0x7FFFu) + 64u) >> 7); + if (rounded > threshold) { + continue; // above range: keep full precision + } + mDataPerTF[i] = (rounded == 0) ? 0 : static_cast((raw & 0x8000u) | (rounded << 7)); + } +} + +void CMVPerTF::trimGaussianPrecision(float mean, float sigma) +{ + if (sigma <= 0.f) { + return; + } + + for (uint32_t i = 0; i < static_cast(CRU::MaxCRU) * cmv::NTimeBinsPerTF; ++i) { + mDataPerTF[i] = quantizeBelowThreshold(mDataPerTF[i], mean, sigma); + } +} + +CMVPerTFCompressed CMVPerTF::compress(uint8_t flags) const +{ + CMVPerTFCompressed out; + out.firstOrbit = firstOrbit; + out.firstBC = firstBC; + out.mFlags = flags; + + if (flags & CMVEncoding::kSparse) { + // --- Sparse path: position stream + value stream --- + + // Single pass per CRU: build the position stream and collect raw non-zero values. + std::vector posStream; + std::vector rawValues; + + for (int cru = 0; cru < static_cast(CRU::MaxCRU); ++cru) { + struct Entry { + uint32_t tb; + uint16_t val; + }; + std::vector entries; + for (uint32_t tb = 0; tb < cmv::NTimeBinsPerTF; ++tb) { + const uint16_t val = mDataPerTF[cru * cmv::NTimeBinsPerTF + tb]; + if (val != 0) { + entries.push_back({tb, val}); + } + } + + encodeVarintInto(static_cast(entries.size()), posStream); + uint32_t prevTB = 0; + bool first = true; + for (const auto& e : entries) { + encodeVarintInto(first ? e.tb : (e.tb - prevTB), posStream); + rawValues.push_back(e.val); + prevTB = e.tb; + first = false; + } + } + + // Encode the value stream based on flags. + std::vector valStream; + if (flags & CMVEncoding::kZigzag) { + std::vector zigzags; + zigzags.reserve(rawValues.size()); + for (const uint16_t v : rawValues) { + zigzags.push_back(zigzagEncode(cmvToSigned(v))); + } + if (flags & CMVEncoding::kHuffman) { + huffmanEncode(zigzags, valStream); + } else { // kVarint + for (const uint32_t z : zigzags) { + encodeVarintInto(z, valStream); + } + } + } else { + // Raw uint16 LE + for (const uint16_t v : rawValues) { + valStream.push_back(static_cast(v & 0xFF)); + valStream.push_back(static_cast(v >> 8)); + } + } + + // Assemble: [4 bytes posStreamSize][posStream][valStream] + const uint32_t posStreamSize = static_cast(posStream.size()); + out.mData.reserve(4 + posStream.size() + valStream.size()); + for (int i = 0; i < 4; ++i) { + out.mData.push_back(static_cast((posStreamSize >> (8 * i)) & 0xFF)); + } + out.mData.insert(out.mData.end(), posStream.begin(), posStream.end()); + out.mData.insert(out.mData.end(), valStream.begin(), valStream.end()); + + } else { + // --- Dense path: all CRU * TimeBin values --- + const uint32_t total = static_cast(CRU::MaxCRU) * cmv::NTimeBinsPerTF; + + if (!(flags & CMVEncoding::kZigzag)) { + // No encoding: raw uint16 LE + out.mData.reserve(total * 2); + for (uint32_t i = 0; i < total; ++i) { + out.mData.push_back(static_cast(mDataPerTF[i] & 0xFF)); + out.mData.push_back(static_cast(mDataPerTF[i] >> 8)); + } + } else { + // Zigzag + optional delta (CRU-major, time-minor) + const bool useDelta = (flags & CMVEncoding::kDelta) != 0; + std::vector zigzags; + zigzags.reserve(total); + for (int cru = 0; cru < static_cast(CRU::MaxCRU); ++cru) { + int32_t prev = 0; + for (uint32_t tb = 0; tb < cmv::NTimeBinsPerTF; ++tb) { + const int32_t val = cmvToSigned(mDataPerTF[cru * cmv::NTimeBinsPerTF + tb]); + const int32_t encoded = useDelta ? (val - prev) : val; + if (useDelta) { + prev = val; + } + zigzags.push_back(zigzagEncode(encoded)); + } + } + + if (flags & CMVEncoding::kHuffman) { + huffmanEncode(zigzags, out.mData); + } else { // kVarint + for (const uint32_t z : zigzags) { + encodeVarintInto(z, out.mData); + } + } + } + } + + return out; +} + +// CMVPerTFCompressed::decompress staged pipeline + +std::vector> CMVPerTFCompressed::decodeSparsePositions(const uint8_t*& ptr, const uint8_t* end) +{ + // Read 4-byte LE posStreamSize + if (ptr + 4 > end) { + throw std::runtime_error("CMVPerTFCompressed::decompress: truncated position header"); + } + const uint32_t posStreamSize = static_cast(ptr[0]) | (static_cast(ptr[1]) << 8) | + (static_cast(ptr[2]) << 16) | (static_cast(ptr[3]) << 24); + ptr += 4; + + const uint8_t* posEnd = ptr + posStreamSize; + if (posEnd > end) { + throw std::runtime_error("CMVPerTFCompressed::decompress: posStream overflows payload"); + } + + // Decode per-CRU varint(N) + N×varint(tb_delta) + std::vector> positions; + const uint8_t* p = ptr; + for (int cru = 0; cru < static_cast(CRU::MaxCRU); ++cru) { + const uint32_t count = decodeVarintLocal(p, posEnd); + uint32_t tb = 0; + bool first = true; + for (uint32_t i = 0; i < count; ++i) { + const uint32_t delta = decodeVarintLocal(p, posEnd); + tb = first ? delta : (tb + delta); + first = false; + positions.emplace_back(cru, tb); + } + } + ptr = posEnd; // advance past the entire position block + return positions; +} + +std::vector CMVPerTFCompressed::decodeValueStream(const uint8_t*& ptr, const uint8_t* end, uint32_t N, uint8_t flags) +{ + if (flags & CMVEncoding::kHuffman) { + // Huffman-encoded symbols + return huffmanDecode(ptr, end, N); + } + + if (flags & CMVEncoding::kVarint) { + // Varint-encoded symbols + std::vector out; + out.reserve(N); + for (uint32_t i = 0; i < N; ++i) { + out.push_back(decodeVarintLocal(ptr, end)); + } + return out; + } + + // Raw uint16 LE (no value encoding) + std::vector out; + out.reserve(N); + for (uint32_t i = 0; i < N; ++i) { + if (ptr + 2 > end) { + throw std::runtime_error("CMVPerTFCompressed::decompress: unexpected end in raw value stream"); + } + const uint16_t v = static_cast(ptr[0]) | (static_cast(ptr[1]) << 8); + ptr += 2; + out.push_back(v); + } + return out; +} + +void CMVPerTFCompressed::decodeSparseValues(const std::vector& symbols, + const std::vector>& positions, + uint8_t flags, CMVPerTF* cmv) +{ + const bool useZigzag = (flags & CMVEncoding::kZigzag) != 0; + for (uint32_t i = 0; i < static_cast(positions.size()); ++i) { + uint16_t raw; + if (useZigzag) { + raw = signedToCmvLocal(zigzagDecodeLocal(symbols[i])); + } else { + raw = static_cast(symbols[i]); + } + cmv->mDataPerTF[positions[i].first * cmv::NTimeBinsPerTF + positions[i].second] = raw; + } +} + +void CMVPerTFCompressed::decodeDenseValues(const std::vector& symbols, uint8_t flags, CMVPerTF* cmv) +{ + const bool useZigzag = (flags & CMVEncoding::kZigzag) != 0; + const bool useDelta = (flags & CMVEncoding::kDelta) != 0; + + if (!useZigzag) { + // Symbols are raw uint16 values; write directly + for (uint32_t i = 0; i < static_cast(symbols.size()); ++i) { + cmv->mDataPerTF[i] = static_cast(symbols[i]); + } + return; + } + + // Inverse zigzag + optional inverse delta (CRU-major, time-minor) + uint32_t s = 0; + for (int cru = 0; cru < static_cast(CRU::MaxCRU); ++cru) { + int32_t prev = 0; + for (uint32_t tb = 0; tb < cmv::NTimeBinsPerTF; ++tb, ++s) { + int32_t val = zigzagDecodeLocal(symbols[s]); + if (useDelta) { + val += prev; + prev = val; + } + cmv->mDataPerTF[s] = signedToCmvLocal(val); + } + } +} + +void CMVPerTFCompressed::decompress(CMVPerTF* cmv) const +{ + if (!cmv) { + throw std::invalid_argument("CMVPerTFCompressed::decompress: cmv pointer is null"); + } + cmv->firstOrbit = firstOrbit; + cmv->firstBC = firstBC; + std::fill(std::begin(cmv->mDataPerTF), std::end(cmv->mDataPerTF), uint16_t(0)); + + const uint8_t* ptr = mData.data(); + const uint8_t* end = ptr + mData.size(); + + if (mFlags & CMVEncoding::kSparse) { + // Stage 1: decode position stream + auto positions = decodeSparsePositions(ptr, end); + const uint32_t N = static_cast(positions.size()); + + // Stage 2: decode value stream (Huffman / varint / raw) + auto symbols = decodeValueStream(ptr, end, N, mFlags); + + // Stage 3: inverse zigzag and scatter into CMV array + decodeSparseValues(symbols, positions, mFlags, cmv); + } else { + const uint32_t N = static_cast(CRU::MaxCRU) * cmv::NTimeBinsPerTF; + + // Stage 1: decode value stream (Huffman / varint / raw) + auto symbols = decodeValueStream(ptr, end, N, mFlags); + + // Stage 2: inverse zigzag, inverse delta, fill CMV array + decodeDenseValues(symbols, mFlags, cmv); + } +} + +std::unique_ptr CMVPerTF::toTTree() const +{ + auto tree = std::make_unique("ccdb_object", "ccdb_object"); + tree->SetAutoSave(0); + tree->SetDirectory(nullptr); + + const CMVPerTF* ptr = this; + tree->Branch("CMVPerTF", &ptr); + tree->Fill(); + + tree->ResetBranchAddresses(); + return tree; +} + +std::unique_ptr CMVPerTFCompressed::toTTree() const +{ + auto tree = std::make_unique("ccdb_object", "ccdb_object"); + tree->SetAutoSave(0); + tree->SetDirectory(nullptr); + + const CMVPerTFCompressed* ptr = this; + tree->Branch("CMVPerTFCompressed", &ptr); + tree->Fill(); + + tree->ResetBranchAddresses(); + return tree; +} + +void CMVPerTF::writeToFile(const std::string& filename, const std::unique_ptr& tree) +{ + TFile f(filename.c_str(), "RECREATE"); + if (f.IsZombie()) { + throw std::runtime_error(fmt::format("CMVPerTF::writeToFile: cannot open '{}'", filename)); + } + tree->Write(); + f.Close(); +} + +} // namespace o2::tpc diff --git a/Detectors/TPC/calibration/src/TPCCalibrationLinkDef.h b/Detectors/TPC/calibration/src/TPCCalibrationLinkDef.h index 6e15e2dd0427a..14d3d0a8ffb8e 100644 --- a/Detectors/TPC/calibration/src/TPCCalibrationLinkDef.h +++ b/Detectors/TPC/calibration/src/TPCCalibrationLinkDef.h @@ -123,4 +123,8 @@ #pragma link C++ class o2::tpc::DigitAdd + ; #pragma link C++ class std::vector < o2::tpc::DigitAdd> + ; #pragma link C++ class o2::tpc::PressureTemperatureHelper + ; + +#pragma link C++ class o2::tpc::CMVPerTF + ; +#pragma link C++ class o2::tpc::CMVPerTFCompressed + ; + #endif diff --git a/Detectors/TPC/workflow/CMakeLists.txt b/Detectors/TPC/workflow/CMakeLists.txt index 6930f332bfbf1..0f8d73b1cbe7e 100644 --- a/Detectors/TPC/workflow/CMakeLists.txt +++ b/Detectors/TPC/workflow/CMakeLists.txt @@ -25,6 +25,7 @@ o2_add_library(TPCWorkflow src/KryptonRawFilterSpec.cxx src/OccupancyFilterSpec.cxx src/SACProcessorSpec.cxx + src/CMVToVectorSpec.cxx src/IDCToVectorSpec.cxx src/CalibdEdxSpec.cxx src/CalibratordEdxSpec.cxx @@ -288,4 +289,19 @@ o2_add_executable(pressure-temperature SOURCES src/tpc-pressure-temperature.cxx PUBLIC_LINK_LIBRARIES O2::TPCWorkflow) -add_subdirectory(readers) +o2_add_executable(cmv-to-vector + COMPONENT_NAME tpc + SOURCES src/tpc-cmv-to-vector.cxx + PUBLIC_LINK_LIBRARIES O2::TPCWorkflow) + +o2_add_executable(cmv-flp + COMPONENT_NAME tpc + SOURCES src/tpc-flp-cmv.cxx + PUBLIC_LINK_LIBRARIES O2::TPCWorkflow) + +o2_add_executable(cmv-distribute + COMPONENT_NAME tpc + SOURCES src/tpc-distribute-cmv.cxx + PUBLIC_LINK_LIBRARIES O2::TPCWorkflow) + +add_subdirectory(readers) \ No newline at end of file diff --git a/Detectors/TPC/workflow/README.md b/Detectors/TPC/workflow/README.md index e34faa2813edf..b7a19da121e9b 100644 --- a/Detectors/TPC/workflow/README.md +++ b/Detectors/TPC/workflow/README.md @@ -274,3 +274,191 @@ To directly dump the digits to file for inspection use for the reco workflow ```bash | o2-tpc-reco-workflow --input-type digitizer --output-type digits --disable-mc ``` + +## TPC Common Mode Value (CMV) Workflows + +The CMV workflows parse raw TPC data, buffer Common Mode Values per CRU on FLPs, then merge and aggregate them on a calibration node before serializing the CMVContainer in a TTree. The resulting object can be uploaded to the CCDB or written to the disk. + +### Workflow components + +| Executable | Output | Description | +|---|---|---| +| `o2-tpc-cmv-to-vector` | `TPC/CMVVECTOR` | Parses raw TPC data and creates vectors of CMVs per CRU | +| `o2-tpc-cmv-flp` | `TPC/CMVGROUP` | Buffers N TFs per CRU on the FLP and groups them for forwarding | +| `o2-tpc-cmv-distribute` | TTree / CCDB payload | Merges CRUs over N TFs on the calibration node, serializes the CMVContainer into a TTree, and either writes it to disk (`--dump-cmvs`) or forwards it as a CCDB object (`--enable-CCDB-output`) | + +#### `o2-tpc-cmv-to-vector` + +| Option | Default | Description | +|---|---|---| +| `--input-spec` | `A:TPC/RAWDATA` | DPL input spec for raw TPC data | +| `--crus` | `0-359` | CRU range to process, comma-separated ranges | +| `--write-debug` | false | Write a debug output tree every TF | +| `--write-debug-on-error` | false | Write a debug output tree only when decoding errors occur | +| `--debug-file-name` | `/tmp/cmv_vector_debug.{run}.root` | Name of the debug output ROOT file | +| `--write-raw-data-on-error` | false | Dump raw data to file when decoding errors occur | +| `--raw-file-name` | `/tmp/cmv_debug.{run}.{raw_type}` | Name of the raw debug output file | +| `--raw-data-type` | 0 | Raw data format to dump on error: 0 = full TPC with DPL header, 1 = full TPC with DPL header (skip empty), 2 = full TPC no DPL header, 3 = full TPC no DPL header (skip empty), 4 = IDC raw only, 5 = CMV raw only | +| `--check-incomplete-hbf` | false | Check and report incomplete HBFs in the raw parser | + +#### `o2-tpc-cmv-flp` + +| Option | Default | Description | +|---|---|---| +| `--crus` | `0-359` | CRU range handled by this FLP | +| `--lanes` | hw_concurrency/2 | Parallel processing lanes (CRUs split per lane) | +| `--time-lanes` | 1 | Parallel lanes for time-frame splitting | +| `--n-TFs-buffer` | 1 | Number of TFs to buffer before forwarding | +| `--dump-cmvs-flp` | false | Dump raw CMV vectors per CRU to a ROOT file each TF (for debugging) | + +#### `o2-tpc-cmv-distribute` + +| Option | Default | Description | +|---|---|---| +| `--crus` | `0-359` | CRU range expected from upstream | +| `--timeframes` | 2000 | Number of TFs aggregated per calibration interval | +| `--firstTF` | -1 | First time frame index; -1 = auto-detect from first incoming TF; values < -1 set an offset of `\|firstTF\|+1` TFs before the first interval begins | +| `--lanes` | 1 | Number of parallel lanes (CRUs are split evenly across lanes) | +| `--n-TFs-buffer` | 1 | Number of TFs buffered per group in the upstream `o2-tpc-cmv-flp` (must match that workflow's setting) | +| `--enable-CCDB-output` | false | Forward the CMVContainer TTree as a CCDB object to `o2-calibration-ccdb-populator-workflow` | +| `--use-precise-timestamp` | false | Fetch orbit-reset and GRPECS from CCDB to compute a precise CCDB validity timestamp | +| `--dump-cmvs` | false | Write the CMVContainer TTree to a local ROOT file on disk | +| `--use-sparse` | false | Sparse encoding: skip zero time bins (raw uint16 values; combine with `--use-compression-varint` or `--use-compression-huffman` for compressed sparse output) | +| `--use-compression-varint` | false | Delta + zigzag + varint compression over all values; combined with `--use-sparse`: varint-encoded exact values at non-zero positions | +| `--use-compression-huffman` | false | Huffman encoding over all values; combined with `--use-sparse`: Huffman-encoded exact values at non-zero positions | +| `--cmv-zero-threshold` | 0 | Zero out CMV values whose magnitude is below this threshold (ADC) after optional rounding and before compression; 0 disables | +| `--cmv-round-integers-threshold` | 0 | Round values to nearest integer ADC for \|v\| ≤ N ADC before compression; 0 disables | +| `--cmv-dynamic-precision-mean` | 1.0 | Gaussian centre in \|CMV\| (ADC) where the strongest fractional-bit trimming is applied | +| `--cmv-dynamic-precision-sigma` | 0 | Gaussian width (ADC) for smooth CMV fractional-bit trimming; 0 disables | +| `--drop-data-after-nTFs` | 0 | Drop data for a relative TF slot after this many TFs have passed without receiving all CRUs; 0 uses the default derived from `--check-data-every-n` | +| `--check-data-every-n` | 0 | Check for missing CRU data every N invocations of the run function; -1 disables checking, 0 uses the default (timeframes/2) | +| `--nFactorTFs` | 1000 | Number of TFs to skip before flushing the oldest incomplete aggregation interval | + +### Example 1 — Simple usage for testing + +```bash +#!/bin/bash + +hash="test" +MAX_TFS=1 +CRUS="0-359" + +ARGS_ALL="-b --session ${USER}.${hash} --shm-segment-size $((8<<30))" + +o2-raw-tf-reader-workflow $ARGS_ALL \ + --input-data tf.subset.list \ + --max-tf ${MAX_TFS} | +o2-tfidinfo-writer-workflow $ARGS_ALL \ + --early-forward-policy noraw \ + --fairmq-rate-logging 0 \ + --timeframes-rate-limit ${MAX_TFS} \ + --timeframes-rate-limit-ipcid 583693664 | +o2-tpc-cmv-to-vector $ARGS_ALL \ + --input-spec "A:TPC/RAWDATA" \ + --write-debug-on-error \ + --crus ${CRUS} | +o2-tpc-cmv-flp $ARGS_ALL \ + --crus ${CRUS} | +o2-tpc-cmv-distribute $ARGS_ALL \ + --crus ${CRUS} \ + --dump-cmvs \ + --enable-CCDB-output \ + --cmv-zero-threshold 1.0 \ + --cmv-dynamic-precision-mean 1.0 \ + --cmv-dynamic-precision-sigma 8.0 \ + --use-sparse \ + --use-compression-huffman | +o2-calibration-ccdb-populator-workflow $ARGS_ALL \ + --ccdb-path ccdb-test.cern.ch:8080 +``` + +### Example 2 — Bash scripts for more realistic testing + +In a real online setup, multiple FLPs each process their own CRU subset and forward compressed CMV groups to a central aggregator node via ZeroMQ. + +**FLP side (`Send.sh`)** — run one instance per FLP (pass `N_FLPs` as first argument): + +```bash +#!/bin/bash + +# Number of FLPs (passed as first argument, default 1) +N_FLPs=${1:-1} + +hash="test" +MAX_TFS=1 + +minCRU=0 +maxCRU=360 + +ARGS_ALL="-b --shm-segment-size $((8<<30))" + +for ((i = 0; i < ${N_FLPs}; i++)); do + xpos_start=100 + xpos=$((xpos_start + 1000 * $i)) + + let diff=${maxCRU}-${minCRU} + let Start=${minCRU}+$i*${diff}/${N_FLPs} + let End=$Start+${diff}/${N_FLPs}-1 + + crus="$Start-$End" + echo "FLP $i: crus $crus" + + xterm -hold -geometry 150x41+$xpos+300 -e bash -c "unset PYTHONHOME PYTHONPATH; echo FLP $i; + o2-raw-tf-reader-workflow $ARGS_ALL \ + --session ${USER}.${hash}.send.$i \ + --input-data tf.subset.list \ + --max-tf ${MAX_TFS} | + o2-tfidinfo-writer-workflow $ARGS_ALL \ + --session ${USER}.${hash}.send.$i \ + --early-forward-policy noraw \ + --fairmq-rate-logging 0 \ + --timeframes-rate-limit ${MAX_TFS} \ + --timeframes-rate-limit-ipcid $((583693664 + $i)) | + o2-tpc-cmv-to-vector $ARGS_ALL \ + --session ${USER}.${hash}.send.$i \ + --input-spec 'A:TPC/RAWDATA' \ + --write-debug-on-error \ + --crus ${crus} | + o2-tpc-cmv-flp $ARGS_ALL \ + --session ${USER}.${hash}.send.$i \ + --crus ${crus} | + o2-dpl-output-proxy $ARGS_ALL \ + --session ${USER}.${hash}.send.$i \ + --sporadic-inputs \ + --channel-config 'name=downstream,method=connect,address=tcp://localhost:30453,type=push,transport=zeromq' \ + --dataspec 'downstream:TPC/CMVGROUP;downstream:TPC/CMVORBITINFO'; exec bash" & +done +``` + +Each FLP connects to the aggregator's pull socket on port `30453` and pushes `TPC/CMVGROUP` and `TPC/CMVORBITINFO` messages. The CRU range is automatically split evenly across `N_FLPs`. + +**Aggregator side (`Receive.sh`)**: + +```bash +#!/bin/bash + +hash="test" +CRUS="0-359" + +ARGS_ALL="-b --session ${USER}.${hash}.receive --shm-segment-size $((8<<30))" + +# ZeroMQ proxy: pull from all FLPs connecting on port 30453 +configProxy="name=readout-proxy,type=pull,method=bind,address=tcp://localhost:30453,rateLogging=1,transport=zeromq" + +o2-dpl-raw-proxy $ARGS_ALL \ + --channel-config "${configProxy}" \ + --dataspec "A:TPC/CMVGROUP;A:TPC/CMVORBITINFO" | +o2-tpc-cmv-distribute $ARGS_ALL \ + --crus ${CRUS} \ + --dump-cmvs \ + --enable-CCDB-output \ + --cmv-zero-threshold 1.0 \ + --cmv-dynamic-precision-mean 1.0 \ + --cmv-dynamic-precision-sigma 8.0 \ + --use-sparse \ + --use-compression-huffman | +o2-calibration-ccdb-populator-workflow $ARGS_ALL \ + --ccdb-path ccdb-test.cern.ch:8080 +``` + +The aggregator binds the ZeroMQ pull socket and waits for all FLPs to connect. Once `TPC/CMVGROUP` and `TPC/CMVORBITINFO` data arrive, `o2-tpc-cmv-distribute` merges them, applies compression, writes the object to the disk and uploads to the CCDB. diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/CMVToVectorSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/CMVToVectorSpec.h new file mode 100644 index 0000000000000..add37af5706e5 --- /dev/null +++ b/Detectors/TPC/workflow/include/TPCWorkflow/CMVToVectorSpec.h @@ -0,0 +1,30 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file CMVToVectorSpec.h +/// @author Tuba Gündem, tuba.gundem@cern.ch +/// @brief Processor to convert CMVs to a vector in a CRU + +#ifndef TPC_CMVToVectorSpec_H_ +#define TPC_CMVToVectorSpec_H_ + +#include "Framework/DataProcessorSpec.h" + +namespace o2::tpc +{ + +/// create a processor spec +/// convert CMV raw values to a vector in a CRU +o2::framework::DataProcessorSpec getCMVToVectorSpec(const std::string inputSpec, std::vector const& crus); + +} // end namespace o2::tpc + +#endif // TPC_CMVToVectorSpec_H_ diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCDistributeCMVSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCDistributeCMVSpec.h new file mode 100644 index 0000000000000..c1744ce86d3ac --- /dev/null +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCDistributeCMVSpec.h @@ -0,0 +1,621 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file TPCDistributeCMVSpec.h +/// @author Tuba Gündem, tuba.gundem@cern.ch +/// @brief TPC aggregation of grouped CMVs + +#ifndef O2_TPCDISTRIBUTECMVSPEC_H +#define O2_TPCDISTRIBUTECMVSPEC_H + +#include +#include +#include +#include "TParameter.h" +#include "Framework/Task.h" +#include "Framework/ControlService.h" +#include "Framework/Logger.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/InputRecordWalker.h" +#include "Framework/DataTakingContext.h" +#include "Headers/DataHeader.h" +#include "Framework/ConfigParamRegistry.h" +#include "TPCWorkflow/TPCFLPCMVSpec.h" +#include "MemoryResources/MemoryResources.h" +#include "TPCWorkflow/ProcessingHelpers.h" +#include "DetectorsBase/GRPGeomHelper.h" +#include "CommonDataFormat/Pair.h" +#include "TMemFile.h" +#include "CCDB/CcdbApi.h" +#include "CCDB/CcdbObjectInfo.h" +#include "DetectorsCalibration/Utils.h" +#include "TPCCalibration/CMVContainer.h" +#include "DataFormatsTPC/CMV.h" + +using namespace o2::framework; +using o2::header::gDataOriginTPC; +using namespace o2::tpc; + +namespace o2::tpc +{ + +class TPCDistributeCMVSpec : public o2::framework::Task +{ + public: + TPCDistributeCMVSpec(const std::vector& crus, const unsigned int timeframes, const int nTFsBuffer, const int firstTF, const bool sendCCDB, const bool usePreciseTimestamp, std::shared_ptr req) + : mCRUs{crus}, + mTimeFrames{timeframes}, + mNTFsBuffer{nTFsBuffer}, + mProcessedCRU{{std::vector(timeframes), std::vector(timeframes)}}, + mTFStart{{firstTF, firstTF + timeframes}}, + mTFEnd{{firstTF + timeframes - 1, mTFStart[1] + timeframes - 1}}, + mCCDBRequest(req), + mSendCCDB{sendCCDB}, + mUsePreciseTimestamp{usePreciseTimestamp}, + mSendCCDBOutputOrbitReset(1), + mSendCCDBOutputGRPECS(1), + mOrbitInfoForwarded{{std::vector(timeframes, false), std::vector(timeframes, false)}} + { + // sort vector for binary_search + std::sort(mCRUs.begin(), mCRUs.end()); + + for (auto& processedCRUbuffer : mProcessedCRUs) { + processedCRUbuffer.resize(mTimeFrames); + for (auto& crusMap : processedCRUbuffer) { + crusMap.reserve(mCRUs.size()); + for (const auto cruID : mCRUs) { + crusMap.emplace(cruID, false); + } + } + } + + mFilter.emplace_back(InputSpec{"cmvsgroup", ConcreteDataTypeMatcher{gDataOriginTPC, TPCFLPCMVDevice::getDataDescriptionCMVGroup()}, Lifetime::Sporadic}); + mOrbitFilter.emplace_back(InputSpec{"cmvorbit", ConcreteDataTypeMatcher{gDataOriginTPC, TPCFLPCMVDevice::getDataDescriptionCMVOrbitInfo()}, Lifetime::Sporadic}); + + // pre-allocate the accumulator TTree for the current aggregation interval + initIntervalTree(); + }; + + void init(o2::framework::InitContext& ic) final + { + o2::base::GRPGeomHelper::instance().setRequest(mCCDBRequest); + mNFactorTFs = ic.options().get("nFactorTFs"); + mNTFsDataDrop = ic.options().get("drop-data-after-nTFs"); + mCheckEveryNData = ic.options().get("check-data-every-n"); + if (mCheckEveryNData == 0) { + mCheckEveryNData = mTimeFrames / 2; + if (mCheckEveryNData == 0) { + mCheckEveryNData = 1; + } + mNTFsDataDrop = mCheckEveryNData; + } + mDumpCMVs = ic.options().get("dump-cmvs"); + mUseCompressionVarint = ic.options().get("use-compression-varint"); + mUseSparse = ic.options().get("use-sparse"); + mUseCompressionHuffman = ic.options().get("use-compression-huffman"); + mRoundIntegersThreshold = static_cast(ic.options().get("cmv-round-integers-threshold")); + mZeroThreshold = ic.options().get("cmv-zero-threshold"); + mDynamicPrecisionMean = ic.options().get("cmv-dynamic-precision-mean"); + mDynamicPrecisionSigma = ic.options().get("cmv-dynamic-precision-sigma"); + LOGP(info, "CMV compression settings: use-compression-varint={}, use-sparse={}, use-compression-huffman={}, cmv-round-integers-threshold={}, cmv-zero-threshold={}, cmv-dynamic-precision-mean={}, cmv-dynamic-precision-sigma={}", + mUseCompressionVarint, mUseSparse, mUseCompressionHuffman, mRoundIntegersThreshold, mZeroThreshold, mDynamicPrecisionMean, mDynamicPrecisionSigma); + // re-initialise the interval tree now that compression options are known (constructor used the defaults) + initIntervalTree(); + } + + void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final + { + o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj); + if (matcher == ConcreteDataMatcher("CTP", "ORBITRESET", 0)) { + LOGP(info, "Updating ORBITRESET"); + std::fill(mSendCCDBOutputOrbitReset.begin(), mSendCCDBOutputOrbitReset.end(), true); + } else if (matcher == ConcreteDataMatcher("GLO", "GRPECS", 0)) { + // check if received object is valid + if (o2::base::GRPGeomHelper::instance().getGRPECS()->getRun() != 0) { + LOGP(info, "Updating GRPECS"); + std::fill(mSendCCDBOutputGRPECS.begin(), mSendCCDBOutputGRPECS.end(), true); + } else { + LOGP(info, "Detected default GRPECS object"); + } + } + } + + void run(o2::framework::ProcessingContext& pc) final + { + // capture orbit-reset info once for precise CCDB timestamp calculation + if (mCCDBRequest->askTime) { + const bool grpecsValid = pc.inputs().isValid("grpecs"); + const bool orbitResetValid = pc.inputs().isValid("orbitReset"); + if (grpecsValid) { + pc.inputs().get("grpecs"); + } + if (orbitResetValid) { + pc.inputs().get*>("orbitReset"); + } + if (pc.inputs().countValidInputs() == (grpecsValid + orbitResetValid)) { + return; + } + // update mTFInfo from GRPGeomHelper whenever orbit-reset or GRPECS objects are fresh + if (mSendCCDBOutputOrbitReset[0] && mSendCCDBOutputGRPECS[0]) { + mSendCCDBOutputOrbitReset[0] = false; + mSendCCDBOutputGRPECS[0] = false; + mTFInfo = dataformats::Pair{o2::base::GRPGeomHelper::instance().getOrbitResetTimeMS(), o2::base::GRPGeomHelper::instance().getNHBFPerTF()}; + } + } + + const auto tf = processing_helpers::getCurrentTF(pc); + mLastSeenTF = tf; // track for endOfStream flush + + // automatically detect firstTF in case firstTF was not specified + if (mTFStart.front() <= -1) { + const auto firstTF = tf; + const long offsetTF = std::abs(mTFStart.front() + 1); + const auto nTotTFs = getNRealTFs(); + mTFStart = {firstTF + offsetTF, firstTF + offsetTF + nTotTFs}; + mTFEnd = {mTFStart[1] - 1, mTFStart[1] - 1 + nTotTFs}; + LOGP(info, "Setting {} as first TF", mTFStart[0]); + LOGP(info, "Using offset of {} TFs for setting the first TF", offsetTF); + } + + // check which buffer to use for current incoming data + const bool currentBuffer = (tf > mTFEnd[mBuffer]) ? !mBuffer : mBuffer; + if (mTFStart[currentBuffer] > tf) { + LOGP(info, "All CRUs for current TF {} already received. Skipping this TF", tf); + return; + } + + const unsigned int relTF = (tf - mTFStart[currentBuffer]) / mNTFsBuffer; + LOGP(info, "Current TF: {}, relative TF: {}, current buffer: {}, mTFStart: {}", tf, relTF, currentBuffer, mTFStart[currentBuffer]); + + if (relTF >= mProcessedCRU[currentBuffer].size()) { + LOGP(warning, "Skipping tf {}: relative tf {} is larger than size of buffer: {}", tf, relTF, mProcessedCRU[currentBuffer].size()); + + // check number of processed CRUs for previous TFs. If CRUs are missing for them, they are probably lost/not received + mProcessedTotalData = mCheckEveryNData; + checkIntervalsForMissingData(pc, currentBuffer, relTF, tf); + return; + } + + if (mProcessedCRU[currentBuffer][relTF] == mCRUs.size()) { + return; + } + + // record the absolute first TF of this aggregation interval + if (mIntervalTFCount == 0) { + mIntervalFirstTF = tf; + } + + // set CCDB start timestamp once at the start of each aggregation interval + if (mTimestampStart == 0) { + setTimestampCCDB(relTF, pc); + } + + // capture orbit/BC info into the interval once per relTF. + // all CRUs within a TF carry identical timing, so the first one is sufficient. + if (!mOrbitInfoForwarded[currentBuffer][relTF]) { + for (auto& ref : InputRecordWalker(pc.inputs(), mOrbitFilter)) { + auto const* hdr = o2::framework::DataRefUtils::getHeader(ref); + const unsigned int cru = hdr->subSpecification >> 7; + if (std::binary_search(mCRUs.begin(), mCRUs.end(), cru)) { + const auto orbitBC = pc.inputs().get(ref); + if (mCurrentTF.firstOrbit == 0 && mCurrentTF.firstBC == 0) { + mCurrentTF.firstOrbit = static_cast(orbitBC >> 32); + mCurrentTF.firstBC = static_cast(orbitBC & 0xFFFFu); + } + mOrbitInfoForwarded[currentBuffer][relTF] = true; + break; // one per relTF is enough + } + } + } + + for (auto& ref : InputRecordWalker(pc.inputs(), mFilter)) { + auto const* tpcCRUHeader = o2::framework::DataRefUtils::getHeader(ref); + const unsigned int cru = tpcCRUHeader->subSpecification >> 7; + + // check if cru is specified in input cru list + if (!(std::binary_search(mCRUs.begin(), mCRUs.end(), cru))) { + LOGP(info, "Received data from CRU: {} which was not specified as input. Skipping", cru); + continue; + } + + if (mProcessedCRUs[currentBuffer][relTF][cru]) { + continue; + } else { + // count total number of processed CRUs for given TF + ++mProcessedCRU[currentBuffer][relTF]; + + // to keep track of processed CRUs + mProcessedCRUs[currentBuffer][relTF][cru] = true; + } + + // accumulate raw 16-bit CMVs into the flat array for the current TF + auto cmvVec = pc.inputs().get>(ref); + const uint32_t nTimeBins = std::min(static_cast(cmvVec.size()), cmv::NTimeBinsPerTF); + for (uint32_t tb = 0; tb < nTimeBins; ++tb) { + mCurrentTF.mDataPerTF[cru * cmv::NTimeBinsPerTF + tb] = cmvVec[tb]; + } + } + + LOGP(info, "Number of received CRUs for current TF: {} Needed a total number of processed CRUs of: {} Current TF: {}", mProcessedCRU[currentBuffer][relTF], mCRUs.size(), tf); + + // check for missing data if specified + if (mNTFsDataDrop > 0) { + checkIntervalsForMissingData(pc, currentBuffer, relTF, tf); + } + + if (mProcessedCRU[currentBuffer][relTF] == mCRUs.size()) { + ++mProcessedTFs[currentBuffer]; + + // Pre-processing: quantisation / rounding / zeroing (applied before compression) + mCurrentTF.roundToIntegers(mRoundIntegersThreshold); + if (mZeroThreshold > 0.f) { + mCurrentTF.zeroSmallValues(mZeroThreshold); + } + if (mDynamicPrecisionSigma > 0.f) { + mCurrentTF.trimGaussianPrecision(mDynamicPrecisionMean, mDynamicPrecisionSigma); + } + + // Compress; the raw CMVPerTF branch is used when all flags are zero + const uint8_t flags = buildCompressionFlags(); + if (flags != CMVEncoding::kNone) { + mCurrentCompressedTF = mCurrentTF.compress(flags); + } + + mIntervalTree->Fill(); + ++mIntervalTFCount; + mCurrentTF = CMVPerTF{}; + } + + if (mProcessedTFs[currentBuffer] == mTimeFrames) { + sendOutput(pc.outputs(), tf); + finishInterval(pc, currentBuffer, tf); + } + } + + void endOfStream(o2::framework::EndOfStreamContext& ec) final + { + LOGP(info, "End of stream, flushing CMV interval ({} TFs)", mIntervalTFCount); + // correct mTFEnd for the partial last interval so the CCDB validity end timestamp reflects the actual last TF, not the expected interval end + mTFEnd[mBuffer] = mLastSeenTF; + sendOutput(ec.outputs(), mLastSeenTF); + ec.services().get().readyToQuit(QuitRequest::Me); + } + + static constexpr header::DataDescription getDataDescriptionCCDBCMV() { return header::DataDescription{"TPC_CMV"}; } + + /// Return data description for aggregated CMVs for a given lane + static header::DataDescription getDataDescriptionCMV(const unsigned int lane) + { + const std::string name = fmt::format("CMVAGG{}", lane).data(); + header::DataDescription description; + description.runtimeInit(name.substr(0, 16).c_str()); + return description; + } + + /// Return data description for orbit/BC info for a given output lane + static header::DataDescription getDataDescriptionCMVOrbitInfo(const unsigned int lane) + { + const std::string name = fmt::format("CMVORB{}", lane); + header::DataDescription description; + description.runtimeInit(name.substr(0, 16).c_str()); + return description; + } + + static constexpr header::DataDescription getDataDescriptionCMVFirstTF() { return header::DataDescription{"CMVFIRSTTF"}; } + static constexpr header::DataDescription getDataDescriptionCMVOrbitReset() { return header::DataDescription{"CMVORBITRESET"}; } + + private: + std::vector mCRUs{}; ///< CRUs to process in this instance + const unsigned int mTimeFrames{}; ///< number of TFs per aggregation interval + const int mNTFsBuffer{1}; ///< number of TFs for which the CMVs will be buffered + std::array mProcessedTFs{{0, 0}}; ///< number of processed time frames to keep track of when the writing to CCDB will be done + std::array, 2> mProcessedCRU{}; ///< counter of received data from CRUs per TF to merge incoming data from FLPs. Buffer used in case one FLP delivers the TF after the last TF for the current aggregation interval faster then the other FLPs the last TF. + std::array>, 2> mProcessedCRUs{}; ///< to keep track of the already processed CRUs ([buffer][relTF][CRU]) + std::array mTFStart{}; ///< storing of first TF for buffer interval + std::array mTFEnd{}; ///< storing of last TF for buffer interval + std::shared_ptr mCCDBRequest; ///< info for CCDB request + std::vector mSendCCDBOutputOrbitReset{}; ///< flag for received orbit reset time from CCDB + std::vector mSendCCDBOutputGRPECS{}; ///< flag for received orbit GRPECS from CCDB + bool mBuffer{false}; ///< buffer index + bool mSendCCDB{false}; ///< send output to CCDB populator + bool mUsePreciseTimestamp{false}; ///< use precise timestamp from orbit-reset info + bool mDumpCMVs{false}; ///< write a local ROOT debug file + bool mUseCompressionVarint{false}; ///< use delta+zigzag+varint compression (all values, no sparse skip); combined with mUseSparse → SparseV2 mode 1 + bool mUseSparse{false}; ///< sparse encoding; alone = raw uint16 values; combined with varint/Huffman flag → SparseV2 + bool mUseCompressionHuffman{false}; ///< Huffman encoding; combined with mUseSparse → SparseV2 mode 2 + uint16_t mRoundIntegersThreshold{0}; ///< round values to nearest integer ADC for |v| <= N ADC; 0 = disabled + float mZeroThreshold{0.f}; ///< zero out CMV values whose float magnitude is below this threshold; 0 = disabled + float mDynamicPrecisionMean{1.f}; ///< Gaussian centre in |CMV| ADC where the strongest fractional-bit trimming is applied + float mDynamicPrecisionSigma{0.f}; ///< Gaussian width in ADC for the fractional-bit trimming; 0 disables + long mTimestampStart{0}; ///< CCDB validity start timestamp + dataformats::Pair mTFInfo{}; ///< orbit-reset time and NHBFPerTF for precise timestamp + std::unique_ptr mIntervalTree{}; ///< TTree accumulating one entry per completed TF in the current interval + CMVPerTF mCurrentTF{}; ///< staging object filled per CRU before compression + CMVPerTFCompressed mCurrentCompressedTF{}; ///< compressed output for the current TF (used when flags != kNone) + long mIntervalFirstTF{0}; ///< absolute TF counter of the first TF in the current aggregation interval + unsigned int mIntervalTFCount{0}; ///< number of TTree entries filled for the current aggregation interval + int mNFactorTFs{0}; ///< Number of TFs to skip for sending oldest TF + int mNTFsDataDrop{0}; ///< delay for the check if TFs are missing in TF units + std::array mStartNTFsDataDrop{0}; ///< first relative TF to check + long mProcessedTotalData{0}; ///< used to check for dropeed TF data + int mCheckEveryNData{1}; ///< factor after which to check for missing data (in case data missing -> send dummy data) + std::vector mFilter{}; ///< filter for looping over input data + std::vector mOrbitFilter{}; ///< filter for CMVORBITINFO from FLP + std::array, 2> mOrbitInfoForwarded{}; ///< tracks whether orbit/BC has been captured per (buffer, relTF) + uint32_t mLastSeenTF{0}; ///< last TF counter seen in run(), used to set lastTF in endOfStream flush + + /// Returns real number of TFs taking buffer size into account + unsigned int getNRealTFs() const { return mNTFsBuffer * mTimeFrames; } + + /// Build the CMVEncoding bitmask from the current option flags. + uint8_t buildCompressionFlags() const + { + uint8_t flags = CMVEncoding::kNone; + if (mUseSparse) { + flags |= CMVEncoding::kSparse; + } + if (mUseCompressionHuffman) { + flags |= CMVEncoding::kZigzag | CMVEncoding::kHuffman; + } else if (mUseCompressionVarint) { + flags |= CMVEncoding::kZigzag | CMVEncoding::kVarint; + } + // Delta coding is only applied for the dense (non-sparse) path with a value compressor + if (!(flags & CMVEncoding::kSparse) && (flags & (CMVEncoding::kVarint | CMVEncoding::kHuffman))) { + flags |= CMVEncoding::kDelta; + } + return flags; + } + + /// Create a fresh in-memory TTree for the next aggregation interval. + /// Uses a single CMVPerTFCompressed branch whenever any compression is active, + /// or a raw CMVPerTF branch when no compression flags are set. + void initIntervalTree() + { + mIntervalTree = std::make_unique("ccdb_object", "ccdb_object"); + mIntervalTree->SetAutoSave(0); + mIntervalTree->SetDirectory(nullptr); + if (buildCompressionFlags() != CMVEncoding::kNone) { + mIntervalTree->Branch("CMVPerTFCompressed", &mCurrentCompressedTF); + } else { + mIntervalTree->Branch("CMVPerTF", &mCurrentTF); + } + } + + void clearBuffer(const bool currentBuffer) + { + // resetting received CRUs + for (auto& crusMap : mProcessedCRUs[currentBuffer]) { + for (auto& it : crusMap) { + it.second = false; + } + } + + mProcessedTFs[currentBuffer] = 0; // reset processed TFs for next aggregation interval + std::fill(mProcessedCRU[currentBuffer].begin(), mProcessedCRU[currentBuffer].end(), 0); + std::fill(mOrbitInfoForwarded[currentBuffer].begin(), mOrbitInfoForwarded[currentBuffer].end(), false); + + // set integration range for next integration interval + mTFStart[mBuffer] = mTFEnd[!mBuffer] + 1; + mTFEnd[mBuffer] = mTFStart[mBuffer] + getNRealTFs() - 1; + + // switch buffer + mBuffer = !mBuffer; + } + + void checkIntervalsForMissingData(o2::framework::ProcessingContext& pc, const bool currentBuffer, const long relTF, const uint32_t tf) + { + if (!(mProcessedTotalData++ % mCheckEveryNData)) { + LOGP(info, "Checking for dropped packages..."); + + // if last buffer has smaller time range check the whole last buffer + if ((mTFStart[currentBuffer] > mTFStart[!currentBuffer]) && (relTF > mNTFsDataDrop)) { + LOGP(warning, "Checking last buffer from {} to {}", mStartNTFsDataDrop[!currentBuffer], mProcessedCRU[!currentBuffer].size()); + checkMissingData(pc, !currentBuffer, mStartNTFsDataDrop[!currentBuffer], mProcessedCRU[!currentBuffer].size()); + LOGP(info, "All empty TFs for TF {} for current buffer filled with dummy and sent. Clearing buffer", tf); + sendOutput(pc.outputs(), tf); + finishInterval(pc, !currentBuffer, tf); + } + + const int tfEndCheck = std::clamp(static_cast(relTF) - mNTFsDataDrop, 0, static_cast(mProcessedCRU[currentBuffer].size())); + LOGP(info, "Checking current buffer from {} to {}", mStartNTFsDataDrop[currentBuffer], tfEndCheck); + checkMissingData(pc, currentBuffer, mStartNTFsDataDrop[currentBuffer], tfEndCheck); + mStartNTFsDataDrop[currentBuffer] = tfEndCheck; + } + } + + void checkMissingData(o2::framework::ProcessingContext& pc, const bool currentBuffer, const int startTF, const int endTF) + { + for (int iTF = startTF; iTF < endTF; ++iTF) { + if (mProcessedCRU[currentBuffer][iTF] != mCRUs.size()) { + LOGP(warning, "CRUs for rel. TF: {} curr TF {} are missing! Processed {} CRUs out of {}", iTF, mTFStart[currentBuffer] + iTF, mProcessedCRU[currentBuffer][iTF], mCRUs.size()); + ++mProcessedTFs[currentBuffer]; + mProcessedCRU[currentBuffer][iTF] = mCRUs.size(); + + // find missing CRUs and leave their interval slots empty (zero-filled) + for (auto& it : mProcessedCRUs[currentBuffer][iTF]) { + if (!it.second) { + it.second = true; + } + } + + // leave orbit/BC as zero placeholder for missing TFs + mOrbitInfoForwarded[currentBuffer][iTF] = true; + } + } + } + + void finishInterval(o2::framework::ProcessingContext& pc, const bool buffer, const uint32_t tf) + { + if (mNFactorTFs > 0) { + mNFactorTFs = 0; + // ToDo: Find better fix + auto& deviceProxy = pc.services().get(); + if (deviceProxy.getNumOutputChannels() > 0) { + auto& state = deviceProxy.getOutputChannelState({0}); + size_t oldest = std::numeric_limits::max() - 1; // just set to really large value + state.oldestForChannel = {oldest}; + } + } + + LOGP(info, "All TFs {} for current buffer received. Clearing buffer", tf); + clearBuffer(buffer); + mStartNTFsDataDrop[buffer] = 0; + + // reset per-interval state for the next aggregation interval + initIntervalTree(); + mIntervalFirstTF = 0; + mIntervalTFCount = 0; + mCurrentTF = CMVPerTF{}; + mCurrentCompressedTF = CMVPerTFCompressed{}; + mTimestampStart = 0; + LOGP(info, "Everything cleared. Waiting for new data to arrive."); + } + + void setTimestampCCDB(const long relTF, o2::framework::ProcessingContext& pc) + { + if (mUsePreciseTimestamp && !mTFInfo.second) { + return; + } + const auto& tinfo = pc.services().get(); + const auto nOrbitsOffset = (relTF * mNTFsBuffer + (mNTFsBuffer - 1)) * mTFInfo.second; + mTimestampStart = mUsePreciseTimestamp + ? (mTFInfo.first + (tinfo.firstTForbit - nOrbitsOffset) * o2::constants::lhc::LHCOrbitMUS * 0.001) + : tinfo.creation; + LOGP(info, "Setting timestamp reset reference to: {}, at tfCounter: {}, firstTForbit: {}, NHBFPerTF: {}, relTF: {}, nOrbitsOffset: {}", + mTFInfo.first, tinfo.tfCounter, tinfo.firstTForbit, mTFInfo.second, relTF, nOrbitsOffset); + } + + void sendOutput(DataAllocator& output, const uint32_t tf) + { + using timer = std::chrono::high_resolution_clock; + + if (mIntervalTFCount == 0) { + LOGP(warning, "CMV interval is empty at sendOutput, skipping"); + return; + } + + // attach interval metadata to the TTree (stored once per tree) + mIntervalTree->GetUserInfo()->Clear(); + mIntervalTree->GetUserInfo()->Add(new TParameter("firstTF", mIntervalFirstTF)); + mIntervalTree->GetUserInfo()->Add(new TParameter("lastTF", mLastSeenTF)); + + LOGP(info, "CMVPerTF TTree: {} entries, firstTF={}, lastTF={}", mIntervalTFCount, mIntervalFirstTF, mLastSeenTF); + auto start = timer::now(); + + // write local ROOT file for debugging + if (mDumpCMVs) { + const std::string fname = fmt::format("CMV_timestamp{}.root", mTimestampStart); + try { + mCurrentTF.writeToFile(fname, mIntervalTree); + LOGP(info, "CMV debug file written to {}", fname); + } catch (const std::exception& e) { + LOGP(error, "Failed to write CMV debug file: {}", e.what()); + } + } + + if (!mSendCCDB) { + LOGP(warning, "CCDB output disabled, skipping upload!"); + return; + } + + const int nHBFPerTF = o2::base::GRPGeomHelper::instance().getNHBFPerTF(); + // use the actual number of TFs in this interval (mIntervalTFCount) rather than mTimeFrames, so the CCDB validity end is correct for partial last intervals + const long timeStampEnd = mTimestampStart + static_cast(mIntervalTFCount * mNTFsBuffer * nHBFPerTF * o2::constants::lhc::LHCOrbitMUS * 1e-3); + + if (timeStampEnd <= mTimestampStart) { + LOGP(warning, "Invalid CCDB timestamp range start:{} end:{}, skipping upload!", + mTimestampStart, timeStampEnd); + return; + } + + LOGP(info, "CCDB timestamp range start:{} end:{}", mTimestampStart, timeStampEnd); + + o2::ccdb::CcdbObjectInfo ccdbInfoCMV( + "TPC/Calib/CMV", + "TTree", + "CMV.root", + {}, + mTimestampStart, + timeStampEnd); + + auto image = o2::ccdb::CcdbApi::createObjectImage((mIntervalTree.get()), &ccdbInfoCMV); + // trim TMemFile zero-padding: GetSize() is block-rounded, GetEND() is the actual file end + { + TMemFile mf("trim", image->data(), static_cast(image->size()), "READ"); + image->resize(static_cast(mf.GetEND())); + mf.Close(); + } + LOGP(info, "Sending object {} / {} of size {} bytes, valid for {} : {}", + ccdbInfoCMV.getPath(), ccdbInfoCMV.getFileName(), image->size(), + ccdbInfoCMV.getStartValidityTimestamp(), ccdbInfoCMV.getEndValidityTimestamp()); + + output.snapshot(Output{o2::calibration::Utils::gDataOriginCDBPayload, getDataDescriptionCCDBCMV(), 0}, *image); + output.snapshot(Output{o2::calibration::Utils::gDataOriginCDBWrapper, getDataDescriptionCCDBCMV(), 0}, ccdbInfoCMV); + + auto stop = timer::now(); + std::chrono::duration elapsed = stop - start; + LOGP(info, "CMV CCDB serialisation time: {:.3f} s", elapsed.count()); + } +}; + +DataProcessorSpec getTPCDistributeCMVSpec(const int ilane, const std::vector& crus, const unsigned int timeframes, const int firstTF, const bool sendCCDB = false, const bool usePreciseTimestamp = false, const int nTFsBuffer = 1) +{ + std::vector inputSpecs; + inputSpecs.emplace_back(InputSpec{"cmvsgroup", ConcreteDataTypeMatcher{gDataOriginTPC, TPCFLPCMVDevice::getDataDescriptionCMVGroup()}, Lifetime::Sporadic}); + inputSpecs.emplace_back(InputSpec{"cmvorbit", ConcreteDataTypeMatcher{gDataOriginTPC, TPCFLPCMVDevice::getDataDescriptionCMVOrbitInfo()}, Lifetime::Sporadic}); + + std::vector outputSpecs; + if (sendCCDB) { + outputSpecs.emplace_back( + ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBPayload, + TPCDistributeCMVSpec::getDataDescriptionCCDBCMV()}, + Lifetime::Sporadic); + outputSpecs.emplace_back( + ConcreteDataTypeMatcher{o2::calibration::Utils::gDataOriginCDBWrapper, + TPCDistributeCMVSpec::getDataDescriptionCCDBCMV()}, + Lifetime::Sporadic); + } + + const bool fetchCCDB = usePreciseTimestamp; + auto ccdbRequest = std::make_shared(fetchCCDB, // orbitResetTime + fetchCCDB, // GRPECS=true + false, // GRPLHCIF + false, // GRPMagField + false, // askMatLUT + o2::base::GRPGeomRequest::None, // geometry + inputSpecs); + + const std::string type = "cmv"; + const auto id = fmt::format("tpc-distribute-{}-{:02}", type, ilane); + DataProcessorSpec spec{ + id.data(), + inputSpecs, + outputSpecs, + AlgorithmSpec{adaptFromTask(crus, timeframes, nTFsBuffer, firstTF, sendCCDB, usePreciseTimestamp, ccdbRequest)}, + Options{{"drop-data-after-nTFs", VariantType::Int, 0, {"Number of TFs after which to drop the data"}}, + {"check-data-every-n", VariantType::Int, 0, {"Number of run function called after which to check for missing data (-1 for no checking, 0 for default checking)"}}, + {"nFactorTFs", VariantType::Int, 1000, {"Number of TFs to skip for sending oldest TF"}}, + {"dump-cmvs", VariantType::Bool, false, {"Dump CMVs to a local ROOT file for debugging"}}, + {"use-sparse", VariantType::Bool, false, {"Sparse encoding (skip zero time bins). Alone: raw uint16 values. With --use-compression-varint: varint exact values. With --use-compression-huffman: Huffman exact values"}}, + {"use-compression-varint", VariantType::Bool, false, {"Delta+zigzag+varint compression (all values). Combined with --use-sparse: sparse positions + varint encoded exact CMV values"}}, + {"use-compression-huffman", VariantType::Bool, false, {"Huffman encoding. Combined with --use-sparse: sparse positions + Huffman-encoded exact CMV values"}}, + {"cmv-zero-threshold", VariantType::Float, 0.f, {"Zero out CMV values whose float magnitude is below this threshold after optional integer rounding and before compression; 0 disables"}}, + {"cmv-round-integers-threshold", VariantType::Int, 0, {"Round values to nearest integer ADC for |v| <= N ADC before compression; 0 disables"}}, + {"cmv-dynamic-precision-mean", VariantType::Float, 1.f, {"Gaussian centre in |CMV| ADC where the strongest fractional bit trimming is applied"}}, + {"cmv-dynamic-precision-sigma", VariantType::Float, 0.f, {"Gaussian width in ADC for smooth CMV fractional bit trimming; 0 disables"}}}}; // end DataProcessorSpec + + spec.rank = ilane; + return spec; +} + +} // namespace o2::tpc + +#endif diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCFLPCMVSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCFLPCMVSpec.h new file mode 100644 index 0000000000000..9931c27c9d3fa --- /dev/null +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCFLPCMVSpec.h @@ -0,0 +1,172 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file TPCFLPCMVSpec.h +/// @author Tuba Gündem, tuba.gundem@cern.ch +/// @brief TPC device for processing CMVs on FLPs + +#ifndef O2_TPCFLPIDCSPEC_H +#define O2_TPCFLPIDCSPEC_H + +#include +#include +#include +#include "Framework/Task.h" +#include "Framework/ControlService.h" +#include "Framework/Logger.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/InputRecordWalker.h" +#include "Framework/ConfigParamRegistry.h" +#include "Headers/DataHeader.h" +#include "TPCWorkflow/ProcessingHelpers.h" +#include "TPCBase/CRU.h" +#include "TFile.h" + +using namespace o2::framework; +using o2::header::gDataOriginTPC; +using namespace o2::tpc; + +namespace o2::tpc +{ + +class TPCFLPCMVDevice : public o2::framework::Task +{ + public: + TPCFLPCMVDevice(const int lane, const std::vector& crus, const int nTFsBuffer) + : mLane{lane}, mCRUs{crus}, mNTFsBuffer{nTFsBuffer} {} + + void init(o2::framework::InitContext& ic) final + { + mDumpCMVs = ic.options().get("dump-cmvs-flp"); + } + + void run(o2::framework::ProcessingContext& pc) final + { + LOGP(debug, "Processing CMVs for TF {} for CRUs {} to {}", processing_helpers::getCurrentTF(pc), mCRUs.front(), mCRUs.back()); + + ++mCountTFsForBuffer; + + // Capture heartbeatOrbit / heartbeatBC from the first TF in the buffer + if (mCountTFsForBuffer == 1) { + for (auto& ref : InputRecordWalker(pc.inputs(), mOrbitFilter)) { + auto const* hdr = o2::framework::DataRefUtils::getHeader(ref); + const uint32_t cru = hdr->subSpecification >> 7; + if (mFirstOrbitBC.find(cru) == mFirstOrbitBC.end()) { + auto orbitVec = pc.inputs().get>(ref); + if (!orbitVec.empty()) { + mFirstOrbitBC[cru] = orbitVec[0]; // packed: orbit<<32 | bc + } + } + } + } + + for (auto& ref : InputRecordWalker(pc.inputs(), mFilter)) { + auto const* tpcCRUHeader = o2::framework::DataRefUtils::getHeader(ref); + const int cru = tpcCRUHeader->subSpecification >> 7; + auto vecCMVs = pc.inputs().get>(ref); + mCMVs[cru].insert(mCMVs[cru].end(), vecCMVs.begin(), vecCMVs.end()); + } + + if (mCountTFsForBuffer >= mNTFsBuffer) { + mCountTFsForBuffer = 0; + for (const auto cru : mCRUs) { + LOGP(debug, "Sending CMVs of size {} for TF {}", mCMVs[cru].size(), processing_helpers::getCurrentTF(pc)); + sendOutput(pc.outputs(), cru); + } + mFirstOrbitBC.clear(); + } + + if (mDumpCMVs) { + TFile fOut(fmt::format("CMVs_{}_tf_{}.root", mLane, processing_helpers::getCurrentTF(pc)).data(), "RECREATE"); + for (auto& ref : InputRecordWalker(pc.inputs(), mFilter)) { + auto const* tpcCRUHeader = o2::framework::DataRefUtils::getHeader(ref); + const int cru = tpcCRUHeader->subSpecification >> 7; + auto vec = pc.inputs().get>(ref); + fOut.WriteObject(&vec, fmt::format("CRU_{}", cru).data()); + } + } + } + + void endOfStream(o2::framework::EndOfStreamContext& ec) final + { + if (mCountTFsForBuffer > 0) { + LOGP(info, "Flushing remaining {} buffered TFs at end of stream", mCountTFsForBuffer); + for (const auto cru : mCRUs) { + sendOutput(ec.outputs(), cru); + } + } + ec.services().get().readyToQuit(QuitRequest::Me); + } + + static constexpr header::DataDescription getDataDescriptionCMVGroup() { return header::DataDescription{"CMVGROUP"}; } + + /// Data description for the packed (orbit<<32|bc) scalar forwarded alongside each CRU's CMVGROUP. + static constexpr header::DataDescription getDataDescriptionCMVOrbitInfo() { return header::DataDescription{"CMVORBITINFO"}; } + + private: + const int mLane{}; ///< lane number of processor + const std::vector mCRUs{}; ///< CRUs to process in this instance + int mNTFsBuffer{1}; ///< number of TFs to buffer before sending + bool mDumpCMVs{}; ///< dump CMVs to file for debugging + int mCountTFsForBuffer{0}; ///< counts TFs to track when to send output + std::unordered_map> mCMVs{}; ///< buffered raw 16-bit CMV values per CRU + std::unordered_map mFirstOrbitBC{}; ///< first packed orbit/BC per CRU for the current buffer window + + /// Filter for CMV float vectors (one CMVVECTOR message per CRU per TF) + const std::vector mFilter = {{"cmvs", ConcreteDataTypeMatcher{gDataOriginTPC, "CMVVECTOR"}, Lifetime::Timeframe}}; + /// Filter for CMV packet timing info (one CMVORBITS message per CRU per TF, sent by CMVToVectorSpec) + const std::vector mOrbitFilter = {{"cmvorbits", ConcreteDataTypeMatcher{gDataOriginTPC, "CMVORBITS"}, Lifetime::Timeframe}}; + + void sendOutput(DataAllocator& output, const uint32_t cru) + { + const header::DataHeader::SubSpecificationType subSpec{cru << 7}; + + // Forward the first-TF orbit/BC for this CRU (0 if unavailable for any reason) + uint64_t orbitBC = 0; + if (auto it = mFirstOrbitBC.find(cru); it != mFirstOrbitBC.end()) { + orbitBC = it->second; + } + output.snapshot(Output{gDataOriginTPC, getDataDescriptionCMVOrbitInfo(), subSpec}, orbitBC); + + output.adoptContainer(Output{gDataOriginTPC, getDataDescriptionCMVGroup(), subSpec}, std::move(mCMVs[cru])); + } +}; + +DataProcessorSpec getTPCFLPCMVSpec(const int ilane, const std::vector& crus, const int nTFsBuffer = 1) +{ + std::vector outputSpecs; + std::vector inputSpecs; + outputSpecs.reserve(crus.size()); + inputSpecs.reserve(crus.size()); + + for (const auto& cru : crus) { + const header::DataHeader::SubSpecificationType subSpec{cru << 7}; + + // Inputs from CMVToVectorSpec + inputSpecs.emplace_back(InputSpec{"cmvs", gDataOriginTPC, "CMVVECTOR", subSpec, Lifetime::Timeframe}); + inputSpecs.emplace_back(InputSpec{"cmvorbits", gDataOriginTPC, "CMVORBITS", subSpec, Lifetime::Timeframe}); + + // Outputs to TPCDistributeCMVSpec + outputSpecs.emplace_back(ConcreteDataMatcher{gDataOriginTPC, TPCFLPCMVDevice::getDataDescriptionCMVGroup(), subSpec}, Lifetime::Sporadic); + outputSpecs.emplace_back(ConcreteDataMatcher{gDataOriginTPC, TPCFLPCMVDevice::getDataDescriptionCMVOrbitInfo(), subSpec}, Lifetime::Sporadic); + } + + const auto id = fmt::format("tpc-flp-cmv-{:02}", ilane); + return DataProcessorSpec{ + id.data(), + inputSpecs, + outputSpecs, + AlgorithmSpec{adaptFromTask(ilane, crus, nTFsBuffer)}, + Options{{"dump-cmvs-flp", VariantType::Bool, false, {"Dump CMVs to file"}}}}; +} + +} // namespace o2::tpc +#endif \ No newline at end of file diff --git a/Detectors/TPC/workflow/src/CMVToVectorSpec.cxx b/Detectors/TPC/workflow/src/CMVToVectorSpec.cxx new file mode 100644 index 0000000000000..81ce358d1a809 --- /dev/null +++ b/Detectors/TPC/workflow/src/CMVToVectorSpec.cxx @@ -0,0 +1,434 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file CMVToVectorSpec.cxx +/// @author Tuba Gündem, tuba.gundem@cern.ch +/// @brief Processor to convert CMVs to a vector in a CRU + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "TFile.h" +#include "DetectorsRaw/RDHUtils.h" +#include "Framework/Task.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/Logger.h" +#include "Framework/DataProcessorSpec.h" +#include "Framework/WorkflowSpec.h" +#include "Framework/InputRecordWalker.h" +#include "Framework/DataRefUtils.h" +#include "DPLUtils/RawParser.h" +#include "Headers/DataHeader.h" +#include "Headers/DataHeaderHelpers.h" +#include "CommonUtils/TreeStreamRedirector.h" + +#include "DataFormatsTPC/CMV.h" +#include "DataFormatsTPC/RawDataTypes.h" +#include "TPCBase/RDHUtils.h" +#include "TPCBase/Mapper.h" +#include "TPCWorkflow/ProcessingHelpers.h" + +using namespace o2::framework; +using o2::header::gDataOriginTPC; +using RDHUtils = o2::raw::RDHUtils; +using RawDataType = o2::tpc::raw_data_types::Type; + +namespace o2::tpc +{ + +class CMVToVectorDevice : public o2::framework::Task +{ + public: + using FEEIDType = rdh_utils::FEEIDType; + CMVToVectorDevice(const std::vector& crus) : mCRUs(crus) {} + + void init(o2::framework::InitContext& ic) final + { + // set up ADC value filling + mWriteDebug = ic.options().get("write-debug"); + mWriteDebugOnError = ic.options().get("write-debug-on-error"); + mWriteRawDataOnError = ic.options().get("write-raw-data-on-error"); + mRawDataType = ic.options().get("raw-data-type"); + o2::framework::RawParser<>::setCheckIncompleteHBF(ic.options().get("check-incomplete-hbf")); + + mDebugStreamFileName = ic.options().get("debug-file-name").data(); + mRawOutputFileName = ic.options().get("raw-file-name").data(); + + initCMV(); + } + + void run(o2::framework::ProcessingContext& pc) final + { + const auto runNumber = processing_helpers::getRunNumber(pc); + std::vector filter = {{"check", ConcreteDataTypeMatcher{o2::header::gDataOriginTPC, "RAWDATA"}, Lifetime::Timeframe}}; + const auto& mapper = Mapper::instance(); + + // open files if necessary + if ((mWriteDebug || mWriteDebugOnError) && !mDebugStream) { + const auto debugFileName = fmt::format(fmt::runtime(mDebugStreamFileName), fmt::arg("run", runNumber)); + LOGP(info, "Creating debug stream {}", debugFileName); + mDebugStream = std::make_unique(debugFileName.data(), "recreate"); + } + + if (mWriteRawDataOnError && !mRawOutputFile.is_open()) { + std::string_view rawType = (mRawDataType < 2) ? "tf" : "raw"; + if (mRawDataType == 5) { + rawType = "cmv.raw"; + } + const auto rawFileName = fmt::format(fmt::runtime(mRawOutputFileName), fmt::arg("run", runNumber), fmt::arg("raw_type", rawType)); + LOGP(info, "Creating raw debug file {}", rawFileName); + mRawOutputFile.open(rawFileName, std::ios::binary); + } + + uint32_t heartbeatOrbit = 0; + uint16_t heartbeatBC = 0; + uint32_t tfCounter = 0; + bool first = true; + bool hasErrors = false; + + for (auto const& ref : InputRecordWalker(pc.inputs(), filter)) { + const auto* dh = DataRefUtils::getHeader(ref); + tfCounter = dh->tfCounter; + const auto subSpecification = dh->subSpecification; + auto payloadSize = DataRefUtils::getPayloadSize(ref); + LOGP(debug, "Processing TF {}, subSpecification {}, payloadSize {}", tfCounter, subSpecification, payloadSize); + + // ---| data loop |--- + const gsl::span raw = pc.inputs().get>(ref); + try { + o2::framework::RawParser parser(raw.data(), raw.size()); + size_t lastErrorCount = 0; + + for (auto it = parser.begin(), end = parser.end(); it != end; ++it) { + const auto size = it.size(); + + if (parser.getNErrors() > lastErrorCount) { + lastErrorCount = parser.getNErrors(); + hasErrors = true; + } + + // skip empty packages (HBF open) + if (size == 0) { + continue; + } + + auto rdhPtr = reinterpret_cast(it.raw()); + const auto rdhVersion = RDHUtils::getVersion(rdhPtr); + if (!rdhPtr || rdhVersion < 6) { + throw std::runtime_error(fmt::format("could not get RDH from packet, or version {} < 6", rdhVersion).data()); + } + + // ---| extract hardware information to do the processing |--- + const auto feeId = (FEEIDType)RDHUtils::getFEEID(*rdhPtr); + const auto link = rdh_utils::getLink(feeId); + const uint32_t cruID = rdh_utils::getCRU(feeId); + const auto detField = RDHUtils::getDetectorField(*rdhPtr); + + LOGP(debug, "Detected CMV packet: CRU {}, link {}, feeId {}", cruID, link, feeId); + + if ((detField != (decltype(detField))RawDataType::CMV) || (link != rdh_utils::CMVLinkID)) { + LOGP(debug, "Skipping packet: detField {}, (expected RawDataType {}), link {}, (expected CMVLinkID {})", detField, (decltype(detField))RawDataType::CMV, link, rdh_utils::CMVLinkID); + continue; + } + + LOGP(debug, "Processing firstTForbit {:9}, tfCounter {:5}, run {:6}, feeId {:6}, cruID {:3}, link {:2}", dh->firstTForbit, dh->tfCounter, dh->runNumber, feeId, cruID, link); + + if (std::find(mCRUs.begin(), mCRUs.end(), cruID) == mCRUs.end()) { + LOGP(warning, "CMV CRU {:3} not configured in CRUs, skipping", cruID); + continue; + } + + auto& cmvVec = mCMVvectors[cruID]; + auto& infoVec = mCMVInfos[cruID]; + + if (size != sizeof(cmv::Container)) { + LOGP(warning, "CMV packet size mismatch: got {} bytes, expected {} bytes (sizeof cmv::Container). Skipping package.", size, sizeof(cmv::Container)); + hasErrors = true; + continue; + } + auto data = it.data(); + auto& cmvs = *((cmv::Container*)(data)); + const uint32_t orbit = cmvs.header.heartbeatOrbit; + const uint16_t bc = cmvs.header.heartbeatBC; + + // record packet meta and append its CMV vector (3564 TB) + infoVec.emplace_back(orbit, bc); + cmvVec.reserve(cmvVec.size() + cmv::NTimeBinsPerPacket); + for (uint32_t tb = 0; tb < cmv::NTimeBinsPerPacket; ++tb) { + cmvVec.push_back(cmvs.getCMV(tb)); + // LOGP(debug, "Appended CMV {} for timebin {}, CRU {}, orbit {}, bc {}", cmvs.getCMV(tb), tb, cruID, orbit, bc); + } + } + } catch (const std::exception& e) { + // error message throtteling + using namespace std::literals::chrono_literals; + static std::unordered_map nErrorPerSubspec; + static std::chrono::time_point lastReport = std::chrono::steady_clock::now(); + const auto now = std::chrono::steady_clock::now(); + static size_t reportedErrors = 0; + const size_t MAXERRORS = 10; + const auto sleepTime = 10min; + ++nErrorPerSubspec[subSpecification]; + + if ((now - lastReport) < sleepTime) { + if (reportedErrors < MAXERRORS) { + ++reportedErrors; + std::string sleepInfo; + if (reportedErrors == MAXERRORS) { + sleepInfo = fmt::format(", maximum error count ({}) reached, not reporting for the next {}", MAXERRORS, sleepTime); + } + LOGP(alarm, "EXCEPTION in processRawData: {} -> skipping part:{}/{} of spec:{}/{}/{}, size:{}, error count for subspec: {}{}", e.what(), dh->splitPayloadIndex, dh->splitPayloadParts, + dh->dataOrigin, dh->dataDescription, subSpecification, payloadSize, nErrorPerSubspec.at(subSpecification), sleepInfo); + lastReport = now; + } + } else { + lastReport = now; + reportedErrors = 0; + } + continue; + } + } + + hasErrors |= snapshotCMVs(pc.outputs(), tfCounter); + + if (mWriteDebug || (mWriteDebugOnError && hasErrors)) { + writeDebugOutput(tfCounter); + } + + if (mWriteRawDataOnError && hasErrors) { + writeRawData(pc.inputs()); + } + + // clear output + initCMV(); + } + + void closeFiles() + { + LOGP(info, "closeFiles"); + + if (mDebugStream) { + // set some default aliases + auto& stream = (*mDebugStream) << "cmvs"; + auto& tree = stream.getTree(); + tree.SetAlias("sector", "int(cru/10)"); + mDebugStream->Close(); + mDebugStream.reset(nullptr); + mRawOutputFile.close(); + } + } + + void stop() final + { + LOGP(info, "stop"); + closeFiles(); + } + + void endOfStream(o2::framework::EndOfStreamContext& ec) final + { + LOGP(info, "endOfStream"); + // ec.services().get().readyToQuit(QuitRequest::Me); + closeFiles(); + } + + private: + /// CMV information for each cru + struct CMVInfo { + CMVInfo() = default; + CMVInfo(const CMVInfo&) = default; + CMVInfo(uint32_t orbit, uint16_t bc) : heartbeatOrbit(orbit), heartbeatBC(bc) {} + + uint32_t heartbeatOrbit{0}; + uint16_t heartbeatBC{0}; + + bool operator==(const uint32_t orbit) const { return (heartbeatOrbit == orbit); } + bool operator==(const CMVInfo& inf) const { return (inf.heartbeatOrbit == heartbeatOrbit) && (inf.heartbeatBC == heartbeatBC); } + bool matches(uint32_t orbit, int16_t bc) const { return ((heartbeatOrbit == orbit) && (heartbeatBC == bc)); } + }; + + int mRawDataType{0}; ///< type of raw data to dump in case of errors + bool mWriteDebug{false}; ///< write a debug output + bool mWriteDebugOnError{false}; ///< write a debug output in case of errors + bool mWriteRawDataOnError{false}; ///< write raw data in case of errors + std::vector mCRUs; ///< CRUs expected for this device + std::unordered_map> mCMVvectors; ///< raw 16-bit CMV values per cru over all CMV packets in the TF + std::unordered_map> mCMVInfos; ///< CMV packet information within the TF + std::string mDebugStreamFileName; ///< name of the debug stream output file + std::unique_ptr mDebugStream; ///< debug output streamer + std::ofstream mRawOutputFile; ///< raw output file + std::string mRawOutputFileName; ///< name of the raw output file + + //____________________________________________________________________________ + bool snapshotCMVs(DataAllocator& output, uint32_t tfCounter) + { + bool hasErrors = false; + + // send data per CRU with its own orbit/BC vector + for (auto& [cru, cmvVec] : mCMVvectors) { + const header::DataHeader::SubSpecificationType subSpec{cru << 7}; + const auto& infVec = mCMVInfos[cru]; + + if (infVec.size() != 4) { + // LOGP(error, "CRU {:3}: expected 4 packets per TF, got {}", cru, infVec.size()); + hasErrors = true; + } + if (cmvVec.size() != cmv::NTimeBinsPerPacket * infVec.size()) { + // LOGP(error, "CRU {:3}: vector size {} does not match expected {}", cru, cmvVec.size(), cmv::NTimeBinsPerPacket * infVec.size()); + hasErrors = true; + } + + std::vector orbitBCInfo; + orbitBCInfo.reserve(infVec.size()); + for (const auto& inf : infVec) { + orbitBCInfo.emplace_back((uint64_t(inf.heartbeatOrbit) << 32) + uint64_t(inf.heartbeatBC)); + } + + LOGP(debug, "Sending CMVs for CRU {} of size {} ({} packets)", cru, cmvVec.size(), infVec.size()); + output.snapshot(Output{gDataOriginTPC, "CMVVECTOR", subSpec}, cmvVec); + output.snapshot(Output{gDataOriginTPC, "CMVORBITS", subSpec}, orbitBCInfo); + } + + return hasErrors; + } + + //____________________________________________________________________________ + void initCMV() + { + for (const auto cruID : mCRUs) { + auto& cmvVec = mCMVvectors[cruID]; + cmvVec.clear(); + + auto& infosCRU = mCMVInfos[cruID]; + infosCRU.clear(); + } + } + + //____________________________________________________________________________ + void writeDebugOutput(uint32_t tfCounter) + { + const auto& mapper = Mapper::instance(); + + mDebugStream->GetFile()->cd(); + auto& stream = (*mDebugStream) << "cmvs"; + uint32_t seen = 0; + static uint32_t firstOrbit = std::numeric_limits::max(); + + for (auto cru : mCRUs) { + if (mCMVInfos.find(cru) == mCMVInfos.end()) { + continue; + } + + auto& infos = mCMVInfos[cru]; + auto& cmvVec = mCMVvectors[cru]; + + stream << "cru=" << cru + << "tfCounter=" << tfCounter + << "nCMVs=" << cmvVec.size() + << "cmvs=" << cmvVec + << "\n"; + } + } + + void writeRawData(InputRecord& inputs) + { + if (!mRawOutputFile.is_open()) { + return; + } + + using DataHeader = o2::header::DataHeader; + + std::vector filter = {{"check", ConcreteDataTypeMatcher{o2::header::gDataOriginTPC, "RAWDATA"}, Lifetime::Timeframe}}; + for (auto const& ref : InputRecordWalker(inputs, filter)) { + auto dh = DataRefUtils::getHeader(ref); + // LOGP(info, "write header: {}/{}/{}, payload size: {} / {}", dh->dataOrigin, dh->dataDescription, dh->subSpecification, dh->payloadSize, ref.payloadSize); + if (((mRawDataType == 1) || (mRawDataType == 3)) && (dh->payloadSize == 2 * sizeof(o2::header::RAWDataHeader))) { + continue; + } + + if (mRawDataType < 2) { + mRawOutputFile.write(ref.header, sizeof(DataHeader)); + } + if (mRawDataType < 5) { + mRawOutputFile.write(ref.payload, ref.payloadSize); + } + + if (mRawDataType == 5) { + const gsl::span raw = inputs.get>(ref); + try { + o2::framework::RawParser parser(raw.data(), raw.size()); + for (auto it = parser.begin(), end = parser.end(); it != end; ++it) { + const auto size = it.size(); + // skip empty packages (HBF open) + if (size == 0) { + continue; + } + + auto rdhPtr = reinterpret_cast(it.raw()); + const auto rdhVersion = RDHUtils::getVersion(rdhPtr); + if (!rdhPtr || rdhVersion < 6) { + throw std::runtime_error(fmt::format("could not get RDH from packet, or version {} < 6", rdhVersion).data()); + } + + // ---| extract hardware information to do the processing |--- + const auto feeId = (FEEIDType)RDHUtils::getFEEID(*rdhPtr); + const auto link = rdh_utils::getLink(feeId); + const auto detField = RDHUtils::getDetectorField(*rdhPtr); + + // only select CMVs + if ((detField != (decltype(detField))RawDataType::CMV) || (link != rdh_utils::CMVLinkID)) { + continue; + } + + // write out raw data + mRawOutputFile.write((const char*)it.raw(), RDHUtils::getMemorySize(rdhPtr)); + } + } catch (...) { + } + } + } + } +}; + +o2::framework::DataProcessorSpec getCMVToVectorSpec(const std::string inputSpec, std::vector const& crus) +{ + using device = o2::tpc::CMVToVectorDevice; + + std::vector outputs; + for (const uint32_t cru : crus) { + const header::DataHeader::SubSpecificationType subSpec{cru << 7}; + outputs.emplace_back(gDataOriginTPC, "CMVVECTOR", subSpec, Lifetime::Timeframe); + outputs.emplace_back(gDataOriginTPC, "CMVORBITS", subSpec, Lifetime::Timeframe); + } + + return DataProcessorSpec{ + fmt::format("tpc-cmv-to-vector"), + select(inputSpec.data()), + outputs, + AlgorithmSpec{adaptFromTask(crus)}, + Options{ + {"write-debug", VariantType::Bool, false, {"write a debug output tree"}}, + {"write-debug-on-error", VariantType::Bool, false, {"write a debug output tree in case errors occurred"}}, + {"debug-file-name", VariantType::String, "/tmp/cmv_vector_debug.{run}.root", {"name of the debug output file"}}, + {"write-raw-data-on-error", VariantType::Bool, false, {"dump raw data in case errors occurred"}}, + {"raw-file-name", VariantType::String, "/tmp/cmv_debug.{run}.{raw_type}", {"name of the raw output file"}}, + {"raw-data-type", VariantType::Int, 0, {"Which raw data to dump: 0-full TPC with DH, 1-full TPC with DH skip empty, 2-full TPC no DH, 3-full TPC no DH skip empty, 4-IDC raw only 5-CMV raw only"}}, + {"check-incomplete-hbf", VariantType::Bool, false, {"false: don't check; true: check and report"}}, + } // end Options + }; // end DataProcessorSpec +} +} // namespace o2::tpc \ No newline at end of file diff --git a/Detectors/TPC/workflow/src/tpc-cmv-to-vector.cxx b/Detectors/TPC/workflow/src/tpc-cmv-to-vector.cxx new file mode 100644 index 0000000000000..1040b64f98d04 --- /dev/null +++ b/Detectors/TPC/workflow/src/tpc-cmv-to-vector.cxx @@ -0,0 +1,71 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include +#include + +#include "Algorithm/RangeTokenizer.h" +#include "Framework/WorkflowSpec.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/CompletionPolicy.h" +#include "Framework/CompletionPolicyHelpers.h" +#include "CommonUtils/ConfigurableParam.h" +#include "TPCBase/CRU.h" +#include "TPCWorkflow/CMVToVectorSpec.h" + +using namespace o2::framework; +using namespace o2::tpc; + +// customize the completion policy +void customize(std::vector& policies) +{ + using o2::framework::CompletionPolicy; + policies.push_back(CompletionPolicyHelpers::defineByName("tpc-cmv-to-vector", CompletionPolicy::CompletionOp::Consume)); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + std::string crusDefault = "0-" + std::to_string(CRU::MaxCRU - 1); + + std::vector options{ + {"input-spec", VariantType::String, "A:TPC/RAWDATA", {"selection string input specs"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings (e.g.: 'TPCCalibPedestal.FirstTimeBin=10;...')"}}, + {"configFile", VariantType::String, "", {"configuration file for configurable parameters"}}, + {"crus", VariantType::String, crusDefault.c_str(), {"List of TPC crus, comma separated ranges, e.g. 0-3,7,9-15"}}, + }; + + std::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& config) +{ + + using namespace o2::tpc; + + // set up configuration + o2::conf::ConfigurableParam::updateFromFile(config.options().get("configFile")); + o2::conf::ConfigurableParam::updateFromString(config.options().get("configKeyValues")); + o2::conf::ConfigurableParam::writeINI("o2tpccmv_configuration.ini"); + + const std::string inputSpec = config.options().get("input-spec"); + + const auto crus = o2::RangeTokenizer::tokenize(config.options().get("crus")); + + WorkflowSpec workflow; + + workflow.emplace_back(getCMVToVectorSpec(inputSpec, crus)); + + return workflow; +} diff --git a/Detectors/TPC/workflow/src/tpc-distribute-cmv.cxx b/Detectors/TPC/workflow/src/tpc-distribute-cmv.cxx new file mode 100644 index 0000000000000..b6aaaa0a109ad --- /dev/null +++ b/Detectors/TPC/workflow/src/tpc-distribute-cmv.cxx @@ -0,0 +1,84 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include +#include "Algorithm/RangeTokenizer.h" +#include "Framework/WorkflowSpec.h" +#include "Framework/ConfigParamSpec.h" +#include "CommonUtils/ConfigurableParam.h" +#include "TPCWorkflow/TPCDistributeCMVSpec.h" +#include "Framework/CompletionPolicyHelpers.h" + +using namespace o2::framework; + +// customize the completion policy +void customize(std::vector& policies) +{ + using o2::framework::CompletionPolicy; + policies.push_back(CompletionPolicyHelpers::defineByName("tpc-distribute-*.*", CompletionPolicy::CompletionOp::Consume)); +} + +// we need to add workflow options before including Framework/runDataProcessing +void customize(std::vector& workflowOptions) +{ + const std::string cruDefault = "0-" + std::to_string(o2::tpc::CRU::MaxCRU - 1); + + std::vector options{ + {"crus", VariantType::String, cruDefault.c_str(), {"List of CRUs, comma separated ranges, e.g. 0-3,7,9-15"}}, + {"timeframes", VariantType::Int, 2000, {"Number of TFs which will be aggregated per aggregation interval."}}, + {"firstTF", VariantType::Int, -1, {"First time frame index. (if set to -1 the first TF will be automatically detected. Values < -1 are setting an offset for skipping the first TFs)"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, + {"lanes", VariantType::Int, 1, {"Number of lanes of this device (CRUs are split per lane)"}}, + {"use-precise-timestamp", VariantType::Bool, false, {"Use precise timestamp which can be used for writing to CCDB"}}, + {"enable-CCDB-output", VariantType::Bool, false, {"Send output to the CCDB populator"}}, + {"n-TFs-buffer", VariantType::Int, 1, {"Buffer which was defined in the TPCFLPCMVSpec."}}}; + std::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& config) +{ + using namespace o2::tpc; + + // set up configuration + o2::conf::ConfigurableParam::updateFromString(config.options().get("configKeyValues")); + o2::conf::ConfigurableParam::writeINI("o2tpcdistributecmv_configuration.ini"); + + const auto tpcCRUs = o2::RangeTokenizer::tokenize(config.options().get("crus")); + const auto nCRUs = tpcCRUs.size(); + auto timeframes = static_cast(config.options().get("timeframes")); + const auto nLanes = static_cast(config.options().get("lanes")); + const auto firstTF = static_cast(config.options().get("firstTF")); + const bool usePreciseTimestamp = config.options().get("use-precise-timestamp"); + const bool sendCCDB = config.options().get("enable-CCDB-output"); + int nTFsBuffer = config.options().get("n-TFs-buffer"); + if (nTFsBuffer <= 0) { + nTFsBuffer = 1; + } + assert(timeframes >= nTFsBuffer); + timeframes /= nTFsBuffer; + LOGP(info, "Using {} timeframes as each TF contains {} CMVs", timeframes, nTFsBuffer); + const auto crusPerLane = nCRUs / nLanes + ((nCRUs % nLanes) != 0); + WorkflowSpec workflow; + for (int ilane = 0; ilane < nLanes; ++ilane) { + const auto first = tpcCRUs.begin() + ilane * crusPerLane; + if (first >= tpcCRUs.end()) { + break; + } + const auto last = std::min(tpcCRUs.end(), first + crusPerLane); + const std::vector rangeCRUs(first, last); + workflow.emplace_back(getTPCDistributeCMVSpec(ilane, rangeCRUs, timeframes, firstTF, sendCCDB, usePreciseTimestamp, nTFsBuffer)); + } + + return workflow; +} \ No newline at end of file diff --git a/Detectors/TPC/workflow/src/tpc-flp-cmv.cxx b/Detectors/TPC/workflow/src/tpc-flp-cmv.cxx new file mode 100644 index 0000000000000..f41fe5b8fbd15 --- /dev/null +++ b/Detectors/TPC/workflow/src/tpc-flp-cmv.cxx @@ -0,0 +1,72 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include +#include +#include "CommonUtils/ConfigurableParam.h" +#include "Algorithm/RangeTokenizer.h" +#include "Framework/WorkflowSpec.h" +#include "Framework/ConfigParamSpec.h" +#include "TPCWorkflow/TPCFLPCMVSpec.h" +#include "TPCBase/CRU.h" + +using namespace o2::framework; + +void customize(std::vector& workflowOptions) +{ + const std::string cruDefault = "0-" + std::to_string(o2::tpc::CRU::MaxCRU - 1); + const int defaultlanes = std::max(1u, std::thread::hardware_concurrency() / 2); + + std::vector options{ + {"configFile", VariantType::String, "", {"configuration file for configurable parameters"}}, + {"lanes", VariantType::Int, defaultlanes, {"Number of parallel processing lanes (crus are split per device)"}}, + {"time-lanes", VariantType::Int, 1, {"Number of parallel processing lanes (timeframes are split per device)"}}, + {"crus", VariantType::String, cruDefault.c_str(), {"List of CRUs, comma separated ranges, e.g. 0-3,7,9-15"}}, + {"n-TFs-buffer", VariantType::Int, 1, {"Buffer n-TFs before sending output"}}, + {"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}}; + + std::swap(workflowOptions, options); +} + +#include "Framework/runDataProcessing.h" + +WorkflowSpec defineDataProcessing(ConfigContext const& config) +{ + using namespace o2::tpc; + o2::conf::ConfigurableParam::updateFromString(config.options().get("configKeyValues")); + const auto tpcCRUs = o2::RangeTokenizer::tokenize(config.options().get("crus")); + const auto nCRUs = tpcCRUs.size(); + const auto nLanes = std::min(static_cast(config.options().get("lanes")), nCRUs); + const auto time_lanes = static_cast(config.options().get("time-lanes")); + const auto crusPerLane = nCRUs / nLanes + ((nCRUs % nLanes) != 0); + const int nTFsBuffer = config.options().get("n-TFs-buffer"); + + o2::conf::ConfigurableParam::updateFromFile(config.options().get("configFile")); + o2::conf::ConfigurableParam::writeINI("o2tpcflp_configuration.ini"); + + WorkflowSpec workflow; + if (nLanes <= 0) { + return workflow; + } + + for (int ilane = 0; ilane < nLanes; ++ilane) { + const auto first = tpcCRUs.begin() + ilane * crusPerLane; + if (first >= tpcCRUs.end()) { + break; + } + const auto last = std::min(tpcCRUs.end(), first + crusPerLane); + const std::vector rangeCRUs(first, last); + workflow.emplace_back(timePipeline(getTPCFLPCMVSpec(ilane, rangeCRUs, nTFsBuffer), time_lanes)); + } + + return workflow; +} \ No newline at end of file From ca2fdfa6534bb466aa0c62ec4af4274487af4bcc Mon Sep 17 00:00:00 2001 From: shahoian Date: Mon, 13 Apr 2026 15:55:28 +0200 Subject: [PATCH 474/701] Allow BC correction in FIT CTF decoders --- .../FDD/reconstruction/include/FDDReconstruction/CTFCoder.h | 4 +++- Detectors/FIT/FDD/workflow/src/EntropyDecoderSpec.cxx | 1 + .../FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h | 4 +++- Detectors/FIT/FT0/workflow/src/EntropyDecoderSpec.cxx | 1 + .../FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h | 4 +++- Detectors/FIT/FV0/workflow/src/EntropyDecoderSpec.cxx | 1 + 6 files changed, 12 insertions(+), 3 deletions(-) diff --git a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h index c62e013447416..24649f73a4ca3 100644 --- a/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h +++ b/Detectors/FIT/FDD/reconstruction/include/FDDReconstruction/CTFCoder.h @@ -165,7 +165,9 @@ void CTFCoder::decompress(const CompressedDigits& cd, VDIG& digitVec, VCHAN& cha uint32_t firstEntry = 0, clCount = 0, chipCount = 0; o2::InteractionRecord ir(cd.header.firstBC, cd.header.firstOrbit); - + if (mBCShift && ir.toLong() >= mBCShift) { + ir -= mBCShift; + } for (uint32_t idig = 0; idig < cd.header.nTriggers; idig++) { // restore ROFRecord if (cd.orbitInc[idig]) { // non-0 increment => new orbit diff --git a/Detectors/FIT/FDD/workflow/src/EntropyDecoderSpec.cxx b/Detectors/FIT/FDD/workflow/src/EntropyDecoderSpec.cxx index 33c140b5bc198..43615b175734d 100644 --- a/Detectors/FIT/FDD/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/FIT/FDD/workflow/src/EntropyDecoderSpec.cxx @@ -29,6 +29,7 @@ EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdict mTimer.Stop(); mTimer.Reset(); mCTFCoder.setVerbosity(verbosity); + mCTFCoder.setSupportBCShifts(true); mCTFCoder.setDictBinding("ctfdict_FDD"); } diff --git a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h index 5dc367204e1a3..41f11e303db67 100644 --- a/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h +++ b/Detectors/FIT/FT0/reconstruction/include/FT0Reconstruction/CTFCoder.h @@ -165,7 +165,9 @@ void CTFCoder::decompress(const CompressedDigits& cd, VDIG& digitVec, VCHAN& cha uint32_t firstEntry = 0, clCount = 0, chipCount = 0; o2::InteractionRecord ir(cd.header.firstBC, cd.header.firstOrbit); - + if (mBCShift && ir.toLong() >= mBCShift) { + ir -= mBCShift; + } for (uint32_t idig = 0; idig < cd.header.nTriggers; idig++) { // restore ROFRecord if (cd.orbitInc[idig]) { // non-0 increment => new orbit diff --git a/Detectors/FIT/FT0/workflow/src/EntropyDecoderSpec.cxx b/Detectors/FIT/FT0/workflow/src/EntropyDecoderSpec.cxx index 066c5cc547c2e..97ea337705fee 100644 --- a/Detectors/FIT/FT0/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/FIT/FT0/workflow/src/EntropyDecoderSpec.cxx @@ -29,6 +29,7 @@ EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdict mTimer.Stop(); mTimer.Reset(); mCTFCoder.setVerbosity(verbosity); + mCTFCoder.setSupportBCShifts(true); mCTFCoder.setDictBinding("ctfdict_FT0"); } diff --git a/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h index 80dcd6060455b..082fbd93a705a 100644 --- a/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h +++ b/Detectors/FIT/FV0/reconstruction/include/FV0Reconstruction/CTFCoder.h @@ -168,7 +168,9 @@ void CTFCoder::decompress(const CompressedDigits& cd, VDIG& digitVec, VCHAN& cha uint32_t firstEntry = 0, clCount = 0, chipCount = 0; o2::InteractionRecord ir(cd.header.firstBC, cd.header.firstOrbit); - + if (mBCShift && ir.toLong() >= mBCShift) { + ir -= mBCShift; + } for (uint32_t idig = 0; idig < cd.header.nTriggers; idig++) { // restore ROFRecord if (cd.orbitInc[idig]) { // non-0 increment => new orbit diff --git a/Detectors/FIT/FV0/workflow/src/EntropyDecoderSpec.cxx b/Detectors/FIT/FV0/workflow/src/EntropyDecoderSpec.cxx index 7babe9fdea6ed..6cf8043cf683f 100644 --- a/Detectors/FIT/FV0/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/FIT/FV0/workflow/src/EntropyDecoderSpec.cxx @@ -29,6 +29,7 @@ EntropyDecoderSpec::EntropyDecoderSpec(int verbosity, const std::string& ctfdict mTimer.Stop(); mTimer.Reset(); mCTFCoder.setVerbosity(verbosity); + mCTFCoder.setSupportBCShifts(true); mCTFCoder.setDictBinding("ctfdict_FV0"); } From 1010e83338a2f7ee389e99c5655e7f6151ee765c Mon Sep 17 00:00:00 2001 From: Ernst Hellbar Date: Thu, 9 Apr 2026 11:42:50 +0200 Subject: [PATCH 475/701] secondary-vertexing-workflow: request CTP sources only if TPC is included --- .../src/secondary-vertexing-workflow.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx index 9108e8577fd5a..5bc80f527d4d0 100644 --- a/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx @@ -96,9 +96,9 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) } if (src[GID::TPC]) { srcClus |= GID::getSourceMask(GID::TPC); - } - if (sclOpt.requestCTPLumi) { - src = src | GID::getSourcesMask("CTP"); + if (sclOpt.requestCTPLumi) { + src = src | GID::getSourcesMask("CTP"); + } } WorkflowSpec specs; if (sclOpt.needTPCScalersWorkflow() && !configcontext.options().get("disable-root-input")) { From ef2f17856086155b6a4885d3b7c685151d51edde Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Tue, 14 Apr 2026 08:47:10 +0200 Subject: [PATCH 476/701] [ALICE3] IOTOF: Add geometry macros (#15257) --- .../Upgrades/ALICE3/IOTOF/CMakeLists.txt | 3 +- .../ALICE3/IOTOF/macros/CMakeLists.txt | 13 ++ .../ALICE3/IOTOF/macros/defineIOTOFGeo.C | 139 ++++++++++++++++++ 3 files changed, 154 insertions(+), 1 deletion(-) create mode 100644 Detectors/Upgrades/ALICE3/IOTOF/macros/CMakeLists.txt create mode 100644 Detectors/Upgrades/ALICE3/IOTOF/macros/defineIOTOFGeo.C diff --git a/Detectors/Upgrades/ALICE3/IOTOF/CMakeLists.txt b/Detectors/Upgrades/ALICE3/IOTOF/CMakeLists.txt index 83838a01d13f1..808320bf66404 100644 --- a/Detectors/Upgrades/ALICE3/IOTOF/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/IOTOF/CMakeLists.txt @@ -10,4 +10,5 @@ # or submit itself to any jurisdiction. add_subdirectory(base) -add_subdirectory(simulation) \ No newline at end of file +add_subdirectory(simulation) +add_subdirectory(macros) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/macros/CMakeLists.txt b/Detectors/Upgrades/ALICE3/IOTOF/macros/CMakeLists.txt new file mode 100644 index 0000000000000..b2f1857186c0b --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/macros/CMakeLists.txt @@ -0,0 +1,13 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_test_root_macro(defineIOTOFGeo.C + LABELS alice3) diff --git a/Detectors/Upgrades/ALICE3/IOTOF/macros/defineIOTOFGeo.C b/Detectors/Upgrades/ALICE3/IOTOF/macros/defineIOTOFGeo.C new file mode 100644 index 0000000000000..f096fc85aec7a --- /dev/null +++ b/Detectors/Upgrades/ALICE3/IOTOF/macros/defineIOTOFGeo.C @@ -0,0 +1,139 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +void defineIOTOFGeo(const double rAvg = 21, // cm, average radius of the layer (used for stave size calculations) + const int nStaves = 24, // Number of staves + const double staveWidth = 5.42, // cm, Stave width (arc length at avg radius at 0 degrees) + const double staveHeightX2X0 = 0.02, // Stave height (radial at 0 degrees) + const double staveTilt = 10 // Stave tilt angle in degrees +) +{ + const double Si_X0 = 9.5f; // cm, radiation length of silicon + const double staveHeight = staveHeightX2X0 * Si_X0; + + // 1. Define inner and outer radii for the disk. + // The radius corresponds to the distance of the center of the stave to the origin + const double rInner = rAvg - staveHeight / 2.0; + const double rOuter = rAvg + staveHeight / 2.0; + + const double alpha = staveTilt * TMath::DegToRad(); // Tilt angle in radians + const double H = staveHeight; + const double W = staveWidth; + + // 2. Analytical calculation of Inscribed and Outscribed Radii + // We project the global origin (0,0) into the local, unrotated coordinate + // system of a single stave centered at (0,0). + const double u0 = -rAvg * TMath::Cos(alpha); + const double v0 = rAvg * TMath::Sin(alpha); + + // Inscribed Radius: Distance to the closest point on the stave rectangle + const double uc = std::max(-H / 2.0, std::min(H / 2.0, u0)); + const double vc = std::max(-W / 2.0, std::min(W / 2.0, v0)); + const double rInscribed = TMath::Sqrt((uc - u0) * (uc - u0) + (vc - v0) * (vc - v0)); + + // Outscribed Radius: Maximum distance to one of the 4 corners + double rOutscribed = 0; + const double uCorners[4] = {-H / 2.0, H / 2.0, H / 2.0, -H / 2.0}; + const double vCorners[4] = {-W / 2.0, -W / 2.0, W / 2.0, W / 2.0}; + for (int i = 0; i < 4; ++i) { + const double dist = std::hypot(uCorners[i] - u0, vCorners[i] - v0); + if (dist > rOutscribed) { + rOutscribed = dist; + } + } + + // 3. Visualization + new TCanvas("DiskWithStaves", "Disk with Staves", 800, 800); + gPad->SetGrid(); + gPad->SetLeftMargin(0.15); + gPad->SetBottomMargin(0.15); + gPad->SetRightMargin(0.05); + gPad->SetTopMargin(0.05); + + const double maxR = std::max(rOuter, rOutscribed) * 1.5; + gPad->DrawFrame(-maxR, -maxR, maxR, maxR, ";X (cm);Y (cm)"); + + // Draw Inner and Outer Disk Radii (Reference) + TArc* arcInner = new TArc(0, 0, rInner); + arcInner->SetLineStyle(2); + arcInner->SetLineColor(kGray + 1); + arcInner->SetFillStyle(0); + arcInner->Draw("same"); + + TArc* arcOuter = new TArc(0, 0, rOuter); + arcOuter->SetLineStyle(2); + arcOuter->SetLineColor(kGray + 1); + arcOuter->SetFillStyle(0); + arcOuter->Draw("same"); + + // Draw Inscribed and Outscribed circles + TArc* arcInscribed = new TArc(0, 0, rInscribed); + arcInscribed->SetLineColor(kBlue); + arcInscribed->SetLineWidth(2); + arcInscribed->SetFillStyle(0); + arcInscribed->Draw("same"); + + TArc* arcOutscribed = new TArc(0, 0, rOutscribed); + arcOutscribed->SetLineColor(kRed); + arcOutscribed->SetLineWidth(2); + arcOutscribed->SetFillStyle(0); + arcOutscribed->Draw("same"); + + // Generate and Draw Staves + for (int i = 0; i < nStaves; ++i) { + double phi = i * TMath::TwoPi() / nStaves; + double xPts[5], yPts[5]; + for (int j = 0; j < 4; ++j) { + double u = uCorners[j]; + double v = vCorners[j]; + // Apply stave tilt (alpha) around its own center + double uRot = u * TMath::Cos(alpha) - v * TMath::Sin(alpha); + double vRot = u * TMath::Sin(alpha) + v * TMath::Cos(alpha); + // Move stave to rAvg and apply azimuthal rotation (phi) + double x_phi0 = rAvg + uRot; + double y_phi0 = vRot; + xPts[j] = x_phi0 * TMath::Cos(phi) - y_phi0 * TMath::Sin(phi); + yPts[j] = x_phi0 * TMath::Sin(phi) + y_phi0 * TMath::Cos(phi); + } + // Close the geometric polygon + xPts[4] = xPts[0]; + yPts[4] = yPts[0]; + TGraph* gStave = new TGraph(5, xPts, yPts); + gStave->SetFillColorAlpha(kGreen + 2, 0.4); + gStave->SetLineColor(kBlack); + gStave->SetLineWidth(1); + gStave->Draw("f same"); // Fill + gStave->Draw("l same"); // Outline + } + + // 7. Add Legend / Parameter Text + TLatex* tex = new TLatex(); + tex->SetNDC(); + tex->SetTextSize(0.028); + tex->SetTextFont(42); + tex->SetTextColor(kBlack); + tex->DrawLatex(0.12, 0.88, Form("R_{inner} = %.1f, R_{outer} = %.1f", rInner, rOuter)); + tex->DrawLatex(0.12, 0.84, Form("Staves: %d, Tilt: %.1f#circ", nStaves, staveTilt)); + tex->SetTextColor(kBlue); + tex->DrawLatex(0.12, 0.80, Form("Inscribed Radius = %.2f", rInscribed)); + tex->SetTextColor(kRed); + tex->DrawLatex(0.12, 0.76, Form("Outscribed Radius = %.2f", rOutscribed)); +} From 14045caca9a91c39625e97bde4e57131c14b3131 Mon Sep 17 00:00:00 2001 From: shahoian Date: Tue, 14 Apr 2026 14:29:09 +0200 Subject: [PATCH 477/701] Restrict ITS max timestamp uncertainty to shortest ROF --- Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx index f996c0d25e7d7..3cf462206bf94 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx @@ -757,6 +757,7 @@ void TrackerTraits::findRoads(const int iteration) }); }); + const float smallestROFHalf = mTimeFrame->getROFOverlapTableView().getClockLayer().mROFLength * 0.5f; for (auto& track : tracks) { int nShared = 0; bool isFirstShared{false}; @@ -799,6 +800,10 @@ void TrackerTraits::findRoads(const int iteration) } } track.getTimeStamp() = ts.makeSymmetrical(); + if (track.getTimeStamp().getTimeStampError() > smallestROFHalf) { + track.getTimeStamp().setTimeStampError(smallestROFHalf); + } + track.setUserField(0); track.getParamOut().setUserField(0); mTimeFrame->getTracks().emplace_back(track); From 74098a4054b9560c36836a22f405dd4881e2678c Mon Sep 17 00:00:00 2001 From: Felix Weiglhofer <9267733+fweig@users.noreply.github.com> Date: Wed, 15 Apr 2026 10:55:36 +0200 Subject: [PATCH 478/701] GPU/TPC: Simplify pad indexing in noisy-pad filter --- .../DataTypes/CalibdEdxContainer.cxx | 2 +- GPU/GPUTracking/DataTypes/TPCPadBitMap.cxx | 2 +- GPU/GPUTracking/DataTypes/TPCPadBitMap.h | 2 +- GPU/GPUTracking/DataTypes/TPCPadGainCalib.cxx | 2 +- GPU/GPUTracking/DataTypes/TPCPadGainCalib.h | 6 +-- .../DataTypes/TPCZSLinkMapping.cxx | 2 +- .../Definitions/clusterFinderDefs.h | 16 +++--- .../Global/GPUChainTrackingClusterizer.cxx | 4 +- GPU/GPUTracking/TPCClusterFinder/CfArray2D.h | 8 +-- .../TPCClusterFinder/CfChargePos.h | 6 +-- .../GPUTPCCFCheckPadBaseline.cxx | 53 ++++--------------- .../GPUTPCCFCheckPadBaseline.h | 11 +--- .../TPCClusterFinder/GPUTPCCFPeakFinder.cxx | 6 +-- .../TPCClusterFinder/GPUTPCClusterFinder.cxx | 2 +- .../GPUTPCClusterFinderDump.cxx | 4 +- 15 files changed, 44 insertions(+), 82 deletions(-) diff --git a/GPU/GPUTracking/DataTypes/CalibdEdxContainer.cxx b/GPU/GPUTracking/DataTypes/CalibdEdxContainer.cxx index 0b3ee65ef7578..ba4b230e1f6f2 100644 --- a/GPU/GPUTracking/DataTypes/CalibdEdxContainer.cxx +++ b/GPU/GPUTracking/DataTypes/CalibdEdxContainer.cxx @@ -265,7 +265,7 @@ void CalibdEdxContainer::setDefaultZeroSupresssionThreshold() mThresholdMap.setMinCorrectionFactor(defaultVal - 0.1f); mThresholdMap.setMaxCorrectionFactor(defaultVal + 0.1f); for (int32_t sector = 0; sector < o2::tpc::constants::MAXSECTOR; ++sector) { - for (uint16_t globPad = 0; globPad < TPC_PADS_IN_SECTOR; ++globPad) { + for (uint16_t globPad = 0; globPad < TPC_REAL_PADS_IN_SECTOR; ++globPad) { mThresholdMap.setGainCorrection(sector, globPad, defaultVal); } } diff --git a/GPU/GPUTracking/DataTypes/TPCPadBitMap.cxx b/GPU/GPUTracking/DataTypes/TPCPadBitMap.cxx index 0b8e67fbe495e..2d12f98b8cf16 100644 --- a/GPU/GPUTracking/DataTypes/TPCPadBitMap.cxx +++ b/GPU/GPUTracking/DataTypes/TPCPadBitMap.cxx @@ -40,7 +40,7 @@ TPCPadBitMap::TPCPadBitMap(const o2::tpc::CalDet& map) : TPCPadBitMap() void TPCPadBitMap::setFromMap(const o2::tpc::CalDet& map) { for (int32_t sector = 0; sector < o2::tpc::constants::MAXSECTOR; sector++) { - for (int32_t p = 0; p < TPC_PADS_IN_SECTOR; p++) { + for (int32_t p = 0; p < TPC_REAL_PADS_IN_SECTOR; p++) { const auto val = map.getValue(sector, p); mBitMap[sector].set(p, val); } diff --git a/GPU/GPUTracking/DataTypes/TPCPadBitMap.h b/GPU/GPUTracking/DataTypes/TPCPadBitMap.h index 6ddfac8c268ee..299b880fcbcc6 100644 --- a/GPU/GPUTracking/DataTypes/TPCPadBitMap.h +++ b/GPU/GPUTracking/DataTypes/TPCPadBitMap.h @@ -68,7 +68,7 @@ struct TPCPadBitMap { { public: using T = uint32_t; - static constexpr int32_t NWORDS = (TPC_PADS_IN_SECTOR + sizeof(T) * 8 - 1) / sizeof(T); + static constexpr int32_t NWORDS = (TPC_REAL_PADS_IN_SECTOR + sizeof(T) * 8 - 1) / sizeof(T); GPUdi() SectorBitMap() { reset(); diff --git a/GPU/GPUTracking/DataTypes/TPCPadGainCalib.cxx b/GPU/GPUTracking/DataTypes/TPCPadGainCalib.cxx index a20f3dc8aac1d..6cc70c7afa7e1 100644 --- a/GPU/GPUTracking/DataTypes/TPCPadGainCalib.cxx +++ b/GPU/GPUTracking/DataTypes/TPCPadGainCalib.cxx @@ -47,7 +47,7 @@ TPCPadGainCalib::TPCPadGainCalib(const o2::tpc::CalDet& gainMap, const fl void TPCPadGainCalib::setFromMap(const o2::tpc::CalDet& gainMap, const bool inv) { for (int32_t sector = 0; sector < o2::tpc::constants::MAXSECTOR; sector++) { - for (int32_t p = 0; p < TPC_PADS_IN_SECTOR; p++) { + for (int32_t p = 0; p < TPC_REAL_PADS_IN_SECTOR; p++) { const float gainVal = gainMap.getValue(sector, p); inv ? mGainCorrection[sector].set(p, (gainVal > 1.e-5f) ? 1.f / gainVal : 1.f) : mGainCorrection[sector].set(p, gainVal); } diff --git a/GPU/GPUTracking/DataTypes/TPCPadGainCalib.h b/GPU/GPUTracking/DataTypes/TPCPadGainCalib.h index 263956c8b5602..dbea56ee5ea6b 100644 --- a/GPU/GPUTracking/DataTypes/TPCPadGainCalib.h +++ b/GPU/GPUTracking/DataTypes/TPCPadGainCalib.h @@ -120,12 +120,14 @@ struct TPCPadGainCalib { GPUd() void reset() { - for (uint16_t p = 0; p < TPC_PADS_IN_SECTOR; p++) { + for (uint16_t p = 0; p < TPC_REAL_PADS_IN_SECTOR; p++) { set(p, 1.0f); } } private: + T mGainCorrection[TPC_REAL_PADS_IN_SECTOR]; + GPUd() T pack(float f) const { f = CAMath::Clamp(f, mMinCorrectionFactor, mMaxCorrectionFactor); @@ -140,8 +142,6 @@ struct TPCPadGainCalib { return mMinCorrectionFactor + (mMaxCorrectionFactor - mMinCorrectionFactor) * float(c) / float(NumOfSteps); } - T mGainCorrection[TPC_PADS_IN_SECTOR]; - GPUdi() T& at(uint16_t globalPad) { return mGainCorrection[globalPad]; diff --git a/GPU/GPUTracking/DataTypes/TPCZSLinkMapping.cxx b/GPU/GPUTracking/DataTypes/TPCZSLinkMapping.cxx index 60f960d1b25f0..f520282bfa35b 100644 --- a/GPU/GPUTracking/DataTypes/TPCZSLinkMapping.cxx +++ b/GPU/GPUTracking/DataTypes/TPCZSLinkMapping.cxx @@ -27,7 +27,7 @@ TPCZSLinkMapping::TPCZSLinkMapping(o2::tpc::Mapper& mapper) assert(fecToGlobalPad.size() == TPC_FEC_IDS_IN_SECTOR); const auto& globalPadToPadPos = mapper.getMapGlobalPadToPadPos(); - assert(globalPadToPadPos.size() == TPC_PADS_IN_SECTOR); + assert(globalPadToPadPos.size() == TPC_REAL_PADS_IN_SECTOR); for (size_t i = 0; i < TPC_FEC_IDS_IN_SECTOR; i++) { FECIDToPadPos[i] = globalPadToPadPos[fecToGlobalPad[i]]; diff --git a/GPU/GPUTracking/Definitions/clusterFinderDefs.h b/GPU/GPUTracking/Definitions/clusterFinderDefs.h index b36a94fc2bd54..8d9ec60e551b9 100644 --- a/GPU/GPUTracking/Definitions/clusterFinderDefs.h +++ b/GPU/GPUTracking/Definitions/clusterFinderDefs.h @@ -32,15 +32,19 @@ #endif // Padding of 2 and 3 respectively would be enough. But this ensures that -// rows are always aligned along cache lines. Likewise for TPC_PADS_PER_ROW. +// rows are always aligned along cache lines. Likewise for TPC_CLUSTERER_ROW_PAD_CAPACITY. #define GPUCF_PADDING_PAD 8 #define GPUCF_PADDING_TIME 4 -#define TPC_PADS_PER_ROW 144 +// Largest possible number of pads in a TPC row +#define TPC_CLUSTERER_ROW_PAD_CAPACITY 144 -#define TPC_ROWS_PER_CRU 18 -#define TPC_PADS_PER_ROW_PADDED (TPC_PADS_PER_ROW + GPUCF_PADDING_PAD) -#define TPC_NUM_OF_PADS (GPUCA_ROW_COUNT * TPC_PADS_PER_ROW_PADDED + GPUCF_PADDING_PAD) -#define TPC_PADS_IN_SECTOR 14560 +// Stride between rows as stored internally by the clusterizer +#define TPC_CLUSTERER_ROW_STRIDE (TPC_CLUSTERER_ROW_PAD_CAPACITY + GPUCF_PADDING_PAD) +// Number of pads in a sector as stored internally by the clusterizer. +// This includes fake pads for constant strides between rows +#define TPC_CLUSTERER_STRIDED_PAD_COUNT (GPUCA_ROW_COUNT * TPC_CLUSTERER_ROW_STRIDE + GPUCF_PADDING_PAD) +// Real of number of pads in a sector +#define TPC_REAL_PADS_IN_SECTOR 14560 #define TPC_FEC_IDS_IN_SECTOR 23296 #define TPC_MAX_FRAGMENT_LEN_GPU 4000 #define TPC_MAX_FRAGMENT_LEN_HOST 1000 diff --git a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx index bf6577cfd929e..12a12d4c47585 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx @@ -861,7 +861,7 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) runKernel({GetGridAutoStep(lane, RecoStep::TPCClusterFinding)}, clustererShadow.mPchargeMap, TPCMapMemoryLayout::items(GetProcessingSettings().overrideClusterizerFragmentLen) * sizeof(ChargeMapType)); runKernel({GetGridAutoStep(lane, RecoStep::TPCClusterFinding)}, clustererShadow.mPpeakMap, TPCMapMemoryLayout::items(GetProcessingSettings().overrideClusterizerFragmentLen) * sizeof(PeakMapType)); if (fragment.index == 0) { - runKernel({GetGridAutoStep(lane, RecoStep::TPCClusterFinding)}, clustererShadow.mPpadIsNoisy, TPC_PADS_IN_SECTOR * sizeof(*clustererShadow.mPpadIsNoisy)); + runKernel({GetGridAutoStep(lane, RecoStep::TPCClusterFinding)}, clustererShadow.mPpadIsNoisy, TPC_CLUSTERER_STRIDED_PAD_COUNT * sizeof(*clustererShadow.mPpadIsNoisy)); } DoDebugAndDump(RecoStep::TPCClusterFinding, GPUChainTrackingDebugFlags::TPCClustererZeroedCharges, clusterer, &GPUTPCClusterFinder::DumpChargeMap, *mDebugFile, "Zeroed Charges"); @@ -965,7 +965,7 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) 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); + getKernelTimer(RecoStep::TPCClusterFinding, iSector, TPC_REAL_PADS_IN_SECTOR * fragment.lengthWithoutOverlap() * sizeof(PackedCharge), false); } runKernel({GetGrid(clusterer.mPmemory->counters.nPositions, lane), {iSector}}); diff --git a/GPU/GPUTracking/TPCClusterFinder/CfArray2D.h b/GPU/GPUTracking/TPCClusterFinder/CfArray2D.h index 3c8bcf94da4b3..e61ec532bf7e0 100644 --- a/GPU/GPUTracking/TPCClusterFinder/CfArray2D.h +++ b/GPU/GPUTracking/TPCClusterFinder/CfArray2D.h @@ -49,7 +49,7 @@ class TilingLayout enum { Height = Grid::Height, Width = Grid::Width, - WidthInTiles = (TPC_NUM_OF_PADS + Width - 1) / Width, + WidthInTiles = (TPC_CLUSTERER_STRIDED_PAD_COUNT + Width - 1) / Width, }; GPUdi() static tpccf::SizeT idx(const CfChargePos& p) @@ -65,7 +65,7 @@ class TilingLayout GPUd() static size_t items(size_t fragmentLen) { - return (TPC_NUM_OF_PADS + Width - 1) / Width * Width * (TPC_MAX_FRAGMENT_LEN_PADDED(fragmentLen) + Height - 1) / Height * Height; + return (TPC_CLUSTERER_STRIDED_PAD_COUNT + Width - 1) / Width * Width * (TPC_MAX_FRAGMENT_LEN_PADDED(fragmentLen) + Height - 1) / Height * Height; } }; @@ -74,12 +74,12 @@ class LinearLayout public: GPUdi() static tpccf::SizeT idx(const CfChargePos& p) { - return TPC_NUM_OF_PADS * p.timePadded + p.gpad; + return TPC_CLUSTERER_STRIDED_PAD_COUNT * p.timePadded + p.gpad; } GPUd() static size_t items(size_t fragmentLen) { - return TPC_NUM_OF_PADS * TPC_MAX_FRAGMENT_LEN_PADDED(fragmentLen); + return TPC_CLUSTERER_STRIDED_PAD_COUNT * TPC_MAX_FRAGMENT_LEN_PADDED(fragmentLen); } }; diff --git a/GPU/GPUTracking/TPCClusterFinder/CfChargePos.h b/GPU/GPUTracking/TPCClusterFinder/CfChargePos.h index bf6ce2fc804ba..3d853345b8f95 100644 --- a/GPU/GPUTracking/TPCClusterFinder/CfChargePos.h +++ b/GPU/GPUTracking/TPCClusterFinder/CfChargePos.h @@ -42,8 +42,8 @@ struct CfChargePos { GPUdi() bool valid() const { return timePadded >= 0; } - GPUdi() tpccf::Row row() const { return gpad / TPC_PADS_PER_ROW_PADDED; } - GPUdi() tpccf::Pad pad() const { return gpad % TPC_PADS_PER_ROW_PADDED - GPUCF_PADDING_PAD; } + GPUdi() tpccf::Row row() const { return gpad / TPC_CLUSTERER_ROW_STRIDE; } + GPUdi() tpccf::Pad pad() const { return gpad % TPC_CLUSTERER_ROW_STRIDE - GPUCF_PADDING_PAD; } GPUdi() tpccf::TPCFragmentTime time() const { return timePadded - GPUCF_PADDING_TIME; } GPUdi() tpccf::TPCFragmentTime globalTime() const { return timePadded; } @@ -52,7 +52,7 @@ struct CfChargePos { // index between 0 and TPC_NUM_OF_PADS. static constexpr GPUdi() tpccf::GlobalPad tpcGlobalPadIdx(tpccf::Row row, tpccf::Pad pad) { - return TPC_PADS_PER_ROW_PADDED * row + pad + GPUCF_PADDING_PAD; + return TPC_CLUSTERER_ROW_STRIDE * row + pad + GPUCF_PADDING_PAD; } }; diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.cxx index 33ed089890bc4..8cbcf320e2547 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.cxx @@ -50,8 +50,10 @@ GPUd() void GPUTPCCFCheckPadBaseline::CheckBaselineGPU(int32_t nBlocks, int32_t const CfFragment& fragment = clusterer.mPmemory->fragment; CfArray2D chargeMap(reinterpret_cast(clusterer.mPchargeMap)); + constexpr GPUTPCGeometry geo; + const auto iRow = iBlock; - const auto rowinfo = GetRowInfo(iRow); + const auto nPads = geo.NPads(iRow); const CfChargePos basePos{(Row)iRow, 0, 0}; int32_t totalCharges = 0; @@ -62,7 +64,7 @@ GPUd() void GPUTPCCFCheckPadBaseline::CheckBaselineGPU(int32_t nBlocks, int32_t const int16_t iPadOffset = iThread % MaxNPadsPerRow; const int16_t iTimeOffset = iThread / MaxNPadsPerRow; const int16_t iPadHandle = iThread; - const bool handlePad = iPadHandle < rowinfo.nPads; + const bool handlePad = iPadHandle < nPads; const auto firstTB = fragment.firstNonOverlapTimeBin(); const auto lastTB = fragment.lastNonOverlapTimeBin(); @@ -73,7 +75,7 @@ GPUd() void GPUTPCCFCheckPadBaseline::CheckBaselineGPU(int32_t nBlocks, int32_t const CfChargePos pos = basePos.delta({iPadOffset, iTime}); - smem.charges[iTimeOffset][iPadOffset] = iTime < lastTB && iPadOffset < rowinfo.nPads ? chargeMap[pos].unpack() : 0; + smem.charges[iTimeOffset][iPadOffset] = iTime < lastTB && iPadOffset < nPads ? chargeMap[pos].unpack() : 0; GPUbarrier(); @@ -91,7 +93,7 @@ GPUd() void GPUTPCCFCheckPadBaseline::CheckBaselineGPU(int32_t nBlocks, int32_t } if (handlePad) { - updatePadBaseline(rowinfo.globalPadOffset + iPadOffset, clusterer, totalCharges, maxConsecCharges, maxCharge); + updatePadBaseline(basePos.gpad + iPadHandle, clusterer, totalCharges, maxConsecCharges, maxCharge); } #endif } @@ -102,11 +104,10 @@ GPUd() void GPUTPCCFCheckPadBaseline::CheckBaselineCPU(int32_t nBlocks, int32_t 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); + CfChargePos basePos(iBlock * PadsPerCacheline, 0); - if (not basePos.valid()) { + constexpr GPUTPCGeometry geo; + if (basePos.pad() >= geo.NPads(basePos.row())) { return; } @@ -153,45 +154,11 @@ GPUd() void GPUTPCCFCheckPadBaseline::CheckBaselineCPU(int32_t nBlocks, int32_t } for (tpccf::Pad localpad = 0; localpad < PadsPerCacheline; localpad++) { - updatePadBaseline(basePad + localpad, clusterer, totalCharges[localpad], maxConsecCharges[localpad], maxCharge[localpad]); + updatePadBaseline(basePos.gpad + localpad, clusterer, totalCharges[localpad], maxConsecCharges[localpad], maxCharge[localpad]); } #endif } -template -GPUd() CfChargePos GPUTPCCFCheckPadBaseline::padToCfChargePos(int32_t& pad, const GPUTPCClusterFinder& clusterer, int32_t& padsPerRow) -{ - constexpr GPUTPCGeometry geo; - - int32_t padOffset = 0; - for (Row r = 0; r < GPUCA_ROW_COUNT; r++) { - int32_t npads = geo.NPads(r); - int32_t padInRow = pad - padOffset; - 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 a71f1358a73a6..bb44e5e69a9e1 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.h +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFCheckPadBaseline.h @@ -63,7 +63,7 @@ class GPUTPCCFCheckPadBaseline : public GPUKernelTemplate static int32_t GetNBlocks(bool isGPU) { - const int32_t nBlocks = TPC_PADS_IN_SECTOR / PadsPerCacheline; + const int32_t nBlocks = TPC_CLUSTERER_STRIDED_PAD_COUNT / PadsPerCacheline; return isGPU ? GPUCA_ROW_COUNT : nBlocks; } @@ -74,15 +74,6 @@ class GPUTPCCFCheckPadBaseline : public GPUKernelTemplate 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/GPUTPCCFPeakFinder.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFPeakFinder.cxx index 6749ab8e8485e..5d94e36febc0a 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFPeakFinder.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCCFPeakFinder.cxx @@ -105,11 +105,11 @@ GPUd() void GPUTPCCFPeakFinder::findPeaksImpl(int32_t nBlocks, int32_t nThreads, // For certain configurations dummy work items are added, so the total // number of work items is dividable by 64. // These dummy items also compute the last digit but discard the result. - CfChargePos pos = positions[CAMath::Min(idx, (SizeT)(digitnum - 1))]; + CfChargePos pos = positions[CAMath::Min(idx, digitnum - 1)]; Charge charge = pos.valid() ? chargeMap[pos].unpack() : Charge(0); - bool hasLostBaseline = padHasLostBaseline[gainCorrection.globalPad(pos.row(), pos.pad())]; - charge = (hasLostBaseline) ? 0.f : charge; + bool hasLostBaseline = padHasLostBaseline[pos.gpad]; + charge = hasLostBaseline ? 0.f : charge; uint8_t peak = isPeak(smem, charge, pos, SCRATCH_PAD_SEARCH_N, chargeMap, calib, smem.posBcast, smem.buf); diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinder.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinder.cxx index 44b005eb20233..979980f32a479 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinder.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinder.cxx @@ -79,7 +79,7 @@ void* GPUTPCClusterFinder::SetPointersOutput(void* mem) void* GPUTPCClusterFinder::SetPointersScratch(void* mem) { - computePointerWithAlignment(mem, mPpadIsNoisy, TPC_PADS_IN_SECTOR); + computePointerWithAlignment(mem, mPpadIsNoisy, TPC_CLUSTERER_STRIDED_PAD_COUNT); computePointerWithAlignment(mem, mPpositions, mNMaxDigitsFragment); computePointerWithAlignment(mem, mPpeakPositions, mNMaxPeaks); computePointerWithAlignment(mem, mPfilteredPeakPositions, mNMaxClusters); diff --git a/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinderDump.cxx b/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinderDump.cxx index d676cf9cd3887..242f6963a0b50 100644 --- a/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinderDump.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/GPUTPCClusterFinderDump.cxx @@ -48,7 +48,7 @@ void GPUTPCClusterFinder::DumpChargeMap(std::ostream& out, std::string_view titl for (TPCFragmentTime i = start; i < end; i++) { int32_t zeros = 0; - for (GlobalPad j = 0; j < TPC_NUM_OF_PADS; j++) { + for (GlobalPad j = 0; j < TPC_CLUSTERER_STRIDED_PAD_COUNT; j++) { uint16_t q = map[{j, i}]; zeros += (q == 0); if (q != 0) { @@ -84,7 +84,7 @@ void GPUTPCClusterFinder::DumpPeakMap(std::ostream& out, std::string_view title) int32_t zeros = 0; out << i << ":"; - for (GlobalPad j = 0; j < TPC_NUM_OF_PADS; j++) { + for (GlobalPad j = 0; j < TPC_CLUSTERER_STRIDED_PAD_COUNT; j++) { uint8_t q = map[{j, i}]; zeros += (q == 0); if (q != 0) { From b7bfb2c0c2235654afe8a162eca0cf48aed40a0b Mon Sep 17 00:00:00 2001 From: Stefano Cannito <143754257+scannito@users.noreply.github.com> Date: Thu, 16 Apr 2026 10:16:21 +0200 Subject: [PATCH 479/701] [ALICE3] Rough attempt to pave ML disks as done for OT (#15269) * Updated FT3Module * First attempt to pave ML disks --- .../ALICE3/FT3/simulation/src/Detector.cxx | 30 +++++++++---------- .../ALICE3/FT3/simulation/src/FT3Layer.cxx | 2 +- .../ALICE3/FT3/simulation/src/FT3Module.cxx | 16 ++++++---- 3 files changed, 27 insertions(+), 21 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx index 94d56fd9625a0..2a9a9633cdd11 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/Detector.cxx @@ -631,7 +631,7 @@ void Detector::defineSensitiveVolumes() LOG(info) << "Adding FT3 Sensitive Volume for direction " << direction << " layer " << iLayer << "/" << getNumberOfLayers(); volumeName = o2::ft3::GeometryTGeo::getFT3SensorPattern() + std::to_string(iLayer); int iSens = 0; - if (mLayers[direction][iLayer].getIsInMiddleLayer()) { // ML disks + /*if (mLayers[direction][iLayer].getIsInMiddleLayer()) { // ML disks const std::string sensorName = Form("%s_%d_%d", GeometryTGeo::getFT3SensorPattern(), direction, iLayer); v = geoManager->GetVolume(sensorName.c_str()); if (!v) { @@ -640,22 +640,22 @@ void Detector::defineSensitiveVolumes() } AddSensitiveVolume(v); iSens++; - } 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); - iSens++; - } - v = geoManager->GetVolume(sensor_name_back.c_str()); - if (v) { - AddSensitiveVolume(v); - iSens++; - } + } 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); + iSens++; + } + v = geoManager->GetVolume(sensor_name_back.c_str()); + if (v) { + AddSensitiveVolume(v); + iSens++; } } + //} LOG(info) << iSens << " sensitive volumes added"; } } diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx index 333599c85eab6..5be3c7abc30a3 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Layer.cxx @@ -237,7 +237,7 @@ void FT3Layer::createLayer(TGeoVolume* motherVolume) LOG(info) << "FT3: ft3Params.layoutFT3 = " << ft3Params.layoutFT3; // ### options for ML and OT disk layout - if (ft3Params.layoutFT3 == kTrapezoidal || (mIsMiddleLayer && ft3Params.layoutFT3 == kSegmented)) { + if (ft3Params.layoutFT3 == kTrapezoidal /*|| (mIsMiddleLayer && ft3Params.layoutFT3 == kSegmented)*/) { // trapezoidal ML+OT disks // (disks with TGeoTubes doesn'n work properly in ACTS, due to polar coordinates on TGeoTube sides) diff --git a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx index 99322aa91f53f..4ed330c35ae59 100644 --- a/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx +++ b/Detectors/Upgrades/ALICE3/FT3/simulation/src/FT3Module.cxx @@ -124,7 +124,6 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double } 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; @@ -146,14 +145,12 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double if (Rin == 7 && sensor_height == 9.6 && sensor_width == 5) { x_condition_min = -Rin - 2; x_condition_max = Rin; + dist_offset = 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; - - dist_offset = 2; - } else if (Rin == 5 && sensor_height == 9.6 && sensor_width == 5) { x_condition_min = -Rin - 6; x_condition_max = Rin; @@ -201,6 +198,15 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double x_adjust_bottom_y_pos = 5.5; bottom_y_pos_value = 3.5; bottom_y_neg_value = -3.5; + } else if (Rin == 10 && sensor_height == 9.6 && sensor_width == 5.0) { + x_condition_min = -Rin - 4; + x_condition_max = Rin; + dist_offset = 2; + adjust_bottom_y_pos = false; + adjust_bottom_y_neg = false; + x_adjust_bottom_y_pos = 3.5; + bottom_y_pos_value = 3.5; + bottom_y_neg_value = -3.5; } else if (Rin == 20 && sensor_height == 9.6 && sensor_width == 5.0) { x_condition_min = -Rin - 4; x_condition_max = Rin; @@ -244,7 +250,7 @@ void FT3Module::create_layout(double mZ, int layerNumber, int direction, double 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 { - if (Rin == 20) { // v3 paving, rough attempt + if (Rin == 10 || Rin == 20) { // v3 paving, rough attempt float overlap = 0.3; // NB: these are left edges float X_start = -2.0 - 13.5 * (sensor_width - overlap); From af3e63398be353dedec10d97545101eac2fee123 Mon Sep 17 00:00:00 2001 From: Maximiliano Puccio Date: Mon, 13 Apr 2026 15:25:03 +0200 Subject: [PATCH 480/701] FastMultEst does not need to be streamed --- Detectors/ITSMFT/ITS/tracking/CMakeLists.txt | 1 - Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEst.h | 2 -- Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h | 1 - 3 files changed, 4 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt index 8d8304d16764f..c9c5196da617b 100644 --- a/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt @@ -52,7 +52,6 @@ o2_target_root_dictionary(ITStracking include/ITStracking/Tracklet.h include/ITStracking/Cluster.h include/ITStracking/Definitions.h - include/ITStracking/FastMultEst.h include/ITStracking/FastMultEstConfig.h include/ITStracking/TrackingConfigParam.h LINKDEF src/TrackingLinkDef.h) diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEst.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEst.h index 3083a8fe9c2ec..f94c7c2034b46 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEst.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/FastMultEst.h @@ -84,8 +84,6 @@ struct FastMultEst { return process(countClustersOnLayer(clusters)); } static bool sSeedSet; - - ClassDefNV(FastMultEst, 1); }; } // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h b/Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h index 9efd6dde0176d..0640ff98297b9 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h @@ -42,7 +42,6 @@ #pragma link C++ class o2::its::ITSGpuTrackingParamConfig + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::ITSGpuTrackingParamConfig> + ; -#pragma link C++ class o2::its::FastMultEst + ; #pragma link C++ class o2::its::FastMultEstConfig + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::FastMultEstConfig> + ; From 97af52220cb1a28a426b7dec6191e941235e52bb Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Thu, 16 Apr 2026 16:57:53 +0200 Subject: [PATCH 481/701] ITS: speedup final step in vertexer (#15279) * ITS: speedup final step in vertexer Signed-off-by: Felix Schlepper * ITS: suppress low mult 2nd vertices Signed-off-by: Felix Schlepper --------- Signed-off-by: Felix Schlepper --- .../ITSMFT/ITS/macros/test/CMakeLists.txt | 6 + .../ITSMFT/ITS/macros/test/CheckSeeding.C | 706 +++++++++++ Detectors/ITSMFT/ITS/tracking/CMakeLists.txt | 1 + .../include/ITStracking/ClusterLines.h | 6 +- .../include/ITStracking/Configuration.h | 39 +- .../include/ITStracking/LineVertexerHelpers.h | 46 + .../tracking/include/ITStracking/MathUtils.h | 10 + .../include/ITStracking/TrackingConfigParam.h | 32 +- .../include/ITStracking/VertexerTraits.h | 3 + .../ITSMFT/ITS/tracking/src/ClusterLines.cxx | 32 + .../ITSMFT/ITS/tracking/src/Configuration.cxx | 22 +- .../ITS/tracking/src/LineVertexerHelpers.cxx | 1036 +++++++++++++++++ .../ITSMFT/ITS/tracking/src/TimeFrame.cxx | 13 +- .../ITS/tracking/src/TrackingInterface.cxx | 13 +- .../ITS/tracking/src/VertexerTraits.cxx | 309 +++-- prodtests/full-system-test/dpl-workflow.sh | 16 +- prodtests/full_system_test.sh | 2 +- prodtests/sim_challenge.sh | 2 +- 18 files changed, 2121 insertions(+), 173 deletions(-) create mode 100644 Detectors/ITSMFT/ITS/macros/test/CheckSeeding.C create mode 100644 Detectors/ITSMFT/ITS/tracking/include/ITStracking/LineVertexerHelpers.h create mode 100644 Detectors/ITSMFT/ITS/tracking/src/LineVertexerHelpers.cxx diff --git a/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt b/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt index a23682b085311..ffdbdf1990a32 100644 --- a/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/macros/test/CMakeLists.txt @@ -128,3 +128,9 @@ o2_add_test_root_macro(CheckStaggering.C O2::DetectorsVertexing O2::ReconstructionDataFormats LABELS its COMPILE_ONLY) + +o2_add_test_root_macro(CheckSeeding.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsITS + O2::SimulationDataFormat + O2::Steer + LABELS its COMPILE_ONLY) diff --git a/Detectors/ITSMFT/ITS/macros/test/CheckSeeding.C b/Detectors/ITSMFT/ITS/macros/test/CheckSeeding.C new file mode 100644 index 0000000000000..915f2dda75032 --- /dev/null +++ b/Detectors/ITSMFT/ITS/macros/test/CheckSeeding.C @@ -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. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "DataFormatsITS/Vertex.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTrack.h" +#include "SimulationDataFormat/O2DatabasePDG.h" +#include "Steer/MCKinematicsReader.h" +#endif + +constexpr const char* tracFile = "o2trac_its.root"; +constexpr const char* collContextFile = "collisioncontext.root"; + +namespace +{ +namespace fs = std::filesystem; + +constexpr float MinPt = 0.05f; +constexpr float MaxEta = 1.1f; +constexpr int NMultiplicityBins = 11; +constexpr std::array MultiplicityLabels{{"2", "3", "4-5", "6-8", "9-13", "14-21", "22-33", "34-52", "53-83", "84-128", "128+"}}; + +struct TruthInfo { + int multiplicity = 0; + float x = 0.f; + float y = 0.f; + float z = 0.f; +}; + +struct BestRecoInfo { + o2::its::Vertex vertex; + float purity = -1.f; +}; + +struct GaussianSummary { + bool valid = false; + double mean = 0.; + double sigma = 0.; +}; + +bool isTrueVertexLabel(const o2::MCCompLabel& label) +{ + return label.isValid() && !label.isFake() && label.getSourceID() == 0; +} + +bool isChargedPrimary(const o2::MCTrack& track) +{ + if (!track.isPrimary() || track.GetPt() < MinPt || std::abs(track.GetEta()) > MaxEta) { + return false; + } + auto* pdg = o2::O2DatabasePDG::Instance()->GetParticle(track.GetPdgCode()); + return pdg != nullptr && pdg->Charge() != 0.; +} + +bool isBetterReco(const o2::its::Vertex& candidate, float candidatePurity, const o2::its::Vertex& current, float currentPurity) +{ + if (candidatePurity != currentPurity) { + return candidatePurity > currentPurity; + } + if (candidate.getNContributors() != current.getNContributors()) { + return candidate.getNContributors() > current.getNContributors(); + } + return candidate.getChi2() < current.getChi2(); +} + +int getMultiplicityCategory(int multiplicity) +{ + if (multiplicity <= 2) { + return 1; + } + if (multiplicity <= 3) { + return 2; + } + if (multiplicity <= 5) { + return 3; + } + if (multiplicity <= 8) { + return 4; + } + if (multiplicity <= 13) { + return 5; + } + if (multiplicity <= 21) { + return 6; + } + if (multiplicity <= 33) { + return 7; + } + if (multiplicity <= 52) { + return 8; + } + if (multiplicity <= 83) { + return 9; + } + if (multiplicity <= 128) { + return 10; + } + return 11; +} + +void fillMultiplicityHistogram(TH1* hist, int multiplicity) +{ + hist->Fill(getMultiplicityCategory(multiplicity)); +} + +GaussianSummary fitGaussianCore(TH1* hist, const char* funcName) +{ + if (hist == nullptr || hist->GetEntries() < 20) { + return {}; + } + const auto rms = hist->GetRMS(); + if (!(rms > 0.)) { + return {}; + } + + TF1 fit(funcName, "gaus", hist->GetMean() - 2. * rms, hist->GetMean() + 2. * rms); + fit.SetParameters(hist->GetMaximum(), hist->GetMean(), rms); + hist->Fit(&fit, "Q0R"); + + const auto mean = fit.GetParameter(1); + const auto sigma = std::abs(fit.GetParameter(2)); + if (!(sigma > 0.)) { + return {}; + } + + fit.SetRange(mean - 2. * sigma, mean + 2. * sigma); + hist->Fit(&fit, "Q0R"); + return {true, fit.GetParameter(1), std::abs(fit.GetParameter(2))}; +} + +TH1D* makeNormalizedCopy(const TH1D* source, const char* name, const char* title) +{ + auto* copy = static_cast(source->Clone(name)); + copy->SetTitle(title); + const auto integral = copy->Integral("width"); + if (integral > 0.) { + copy->Scale(1. / integral); + } + return copy; +} + +void setMultiplicityBinLabels(TH1* hist) +{ + for (int i = 0; i < NMultiplicityBins; ++i) { + hist->GetXaxis()->SetBinLabel(i + 1, MultiplicityLabels[i]); + } +} + +void setHistogramStyle(TH1* hist, int color, int marker) +{ + hist->SetLineColor(color); + hist->SetMarkerColor(color); + hist->SetMarkerStyle(marker); + hist->SetLineWidth(2); +} + +void printGaussianByMultiplicity(const std::array& summaries, const char* title) +{ + std::printf("%s:\n", title); + for (int i = 0; i < NMultiplicityBins; ++i) { + if (summaries[i].valid) { + std::printf(" %-4s : mean=%.6g sigma=%.6g\n", MultiplicityLabels[i], summaries[i].mean, summaries[i].sigma); + } else { + std::printf(" %-4s : n/a\n", MultiplicityLabels[i]); + } + } +} + +fs::path resolveContextFile(const fs::path& dir) +{ + const std::array candidates{ + dir / collContextFile, + dir.parent_path() / collContextFile, + fs::current_path() / collContextFile}; + for (const auto& candidate : candidates) { + if (!candidate.empty() && fs::exists(candidate) && fs::is_regular_file(candidate)) { + return candidate; + } + } + return {}; +} + +void printBinnedFractions(const TH1* numerator, const TH1* denominator, const char* title) +{ + if (numerator == nullptr || denominator == nullptr) { + return; + } + std::printf("%s:\n", title); + for (int iBin = 1; iBin <= denominator->GetNbinsX(); ++iBin) { + const auto den = denominator->GetBinContent(iBin); + const auto num = numerator->GetBinContent(iBin); + const auto value = den > 0. ? num / den : 0.; + std::printf(" %-4s : %.4f (%g / %g)\n", denominator->GetXaxis()->GetBinLabel(iBin), value, num, den); + } +} + +std::vector findDirs(const std::string& roots) +{ + fs::path root = roots.empty() ? fs::current_path() : fs::path{roots}; + std::vector result; + const auto hasFiles = [](const fs::path& dir) { + const auto tracPath = dir / tracFile; + return fs::exists(tracPath) && fs::is_regular_file(tracPath); + }; + + if (fs::is_directory(root) && hasFiles(root)) { + result.push_back(root); + return result; + } + + for (const auto& entry : fs::recursive_directory_iterator(root)) { + if (entry.is_directory() && hasFiles(entry.path())) { + result.push_back(entry.path()); + } + } + std::sort(result.begin(), result.end()); + return result; +} + +} // namespace + +void CheckSeeding(const std::string& dir = "") +{ + using Vertex = o2::its::Vertex; + const auto cwd = fs::current_path(); + gStyle->SetOptStat(0); + TH1::AddDirectory(kFALSE); + + auto dirs = findDirs(dir); + std::printf("Will iterate over %zu input dirs\n", dirs.size()); + if (dirs.empty()) { + std::printf("No input directories containing %s were found.\n", tracFile); + return; + } + + auto* hTruthMultiplicityFindable = new TH1D("hTruthMultiplicityFindable", + "Findable truth vertices;truth multiplicity bin;vertices", + NMultiplicityBins, 0.5, NMultiplicityBins + 0.5); + auto* hTruthMultiplicityFound = new TH1D("hTruthMultiplicityFound", + "Found truth vertices;truth multiplicity bin;vertices", + NMultiplicityBins, 0.5, NMultiplicityBins + 0.5); + auto* hRecoMultiplicityTrue = new TH1D("hRecoMultiplicityTrue", + "True reconstructed vertices;reco multiplicity bin;vertices", + NMultiplicityBins, 0.5, NMultiplicityBins + 0.5); + auto* hRecoMultiplicityFake = new TH1D("hRecoMultiplicityFake", + "Fake reconstructed vertices;reco multiplicity bin;vertices", + NMultiplicityBins, 0.5, NMultiplicityBins + 0.5); + auto* hDx = new TH1D("hDx", "Matched vertex residuals;x_{reco}-x_{MC} (cm);vertices", 400, -0.02, 0.02); + auto* hDy = new TH1D("hDy", "Matched vertex residuals;y_{reco}-y_{MC} (cm);vertices", 400, -0.02, 0.02); + auto* hDz = new TH1D("hDz", "Matched vertex residuals;z_{reco}-z_{MC} (cm);vertices", 400, -0.02, 0.02); + auto* hPullX = new TH1D("hPullX", "Matched vertex pulls;x pull;vertices", 600, -30., 30.); + auto* hPullY = new TH1D("hPullY", "Matched vertex pulls;y pull;vertices", 600, -30., 30.); + auto* hPullZ = new TH1D("hPullZ", "Matched vertex pulls;z pull;vertices", 600, -30., 30.); + std::array hPullXByMult{}; + std::array hPullYByMult{}; + std::array hPullZByMult{}; + for (int i = 0; i < NMultiplicityBins; ++i) { + const auto nameX = std::string("hPullX_") + std::to_string(i + 1); + const auto nameY = std::string("hPullY_") + std::to_string(i + 1); + const auto nameZ = std::string("hPullZ_") + std::to_string(i + 1); + const auto titleX = std::string("x pull ") + MultiplicityLabels[i] + ";x pull;vertices"; + const auto titleY = std::string("y pull ") + MultiplicityLabels[i] + ";y pull;vertices"; + const auto titleZ = std::string("z pull ") + MultiplicityLabels[i] + ";z pull;vertices"; + hPullXByMult[i] = new TH1D(nameX.c_str(), titleX.c_str(), 600, -30., 30.); + hPullYByMult[i] = new TH1D(nameY.c_str(), titleY.c_str(), 600, -30., 30.); + hPullZByMult[i] = new TH1D(nameZ.c_str(), titleZ.c_str(), 600, -30., 30.); + } + + setMultiplicityBinLabels(hTruthMultiplicityFindable); + setMultiplicityBinLabels(hTruthMultiplicityFound); + setMultiplicityBinLabels(hRecoMultiplicityTrue); + setMultiplicityBinLabels(hRecoMultiplicityFake); + setHistogramStyle(hTruthMultiplicityFindable, kGray + 2, 20); + setHistogramStyle(hTruthMultiplicityFound, kAzure + 2, 20); + setHistogramStyle(hRecoMultiplicityTrue, kAzure + 2, 20); + setHistogramStyle(hRecoMultiplicityFake, kOrange + 7, 24); + setHistogramStyle(hDx, kAzure + 2, 20); + setHistogramStyle(hDy, kGreen + 2, 21); + setHistogramStyle(hDz, kRed + 1, 24); + setHistogramStyle(hPullX, kAzure + 2, 20); + setHistogramStyle(hPullY, kGreen + 2, 21); + setHistogramStyle(hPullZ, kRed + 1, 24); + + size_t findable = 0; + size_t totalFound = 0; + size_t trueFound = 0; + size_t fakeFound = 0; + size_t uniqueTrueReco = 0; + size_t uniqueFindableFound = 0; + size_t sigmaXCount = 0; + size_t sigmaYCount = 0; + size_t sigmaZCount = 0; + double sumSigmaX = 0.; + double sumSigmaY = 0.; + double sumSigmaZ = 0.; + + for (const auto& inputDir : dirs) { + fs::current_path(inputDir); + std::printf("Working on %s\n", inputDir.c_str()); + const auto contextPath = resolveContextFile(inputDir); + if (contextPath.empty()) { + std::printf("Skipping %s: could not locate %s\n", inputDir.c_str(), collContextFile); + continue; + } + + o2::steer::MCKinematicsReader mcReader(contextPath.string()); + if (!mcReader.isInitialized()) { + std::printf("Skipping %s: failed to initialize MCKinematicsReader from %s\n", inputDir.c_str(), contextPath.c_str()); + continue; + } + + std::unordered_map findableTruths; + std::unordered_set uniqueTrueLabelsReco; + std::unordered_set uniqueFindableTruthFound; + std::unordered_map bestRecoByTruth; + + const int iSrc = 0; + const auto nEvents = static_cast(mcReader.getNEvents(iSrc)); + for (int iEve = 0; iEve < nEvents; ++iEve) { + const auto& tracks = mcReader.getTracks(iSrc, iEve); + const auto contributors = static_cast(std::count_if(tracks.begin(), tracks.end(), isChargedPrimary)); + if (contributors >= 2) { + const auto& header = mcReader.getMCEventHeader(iSrc, iEve); + findableTruths.emplace(iEve, TruthInfo{contributors, (float)header.GetX(), (float)header.GetY(), (float)header.GetZ()}); + fillMultiplicityHistogram(hTruthMultiplicityFindable, contributors); + } + mcReader.releaseTracksForSourceAndEvent(iSrc, iEve); + } + + auto* tracFileHandle = TFile::Open((inputDir / tracFile).c_str()); + if (tracFileHandle == nullptr || tracFileHandle->IsZombie()) { + std::printf("Skipping %s: failed to open %s\n", inputDir.c_str(), tracFile); + delete tracFileHandle; + continue; + } + + auto* tracTree = tracFileHandle->Get("o2sim"); + if (tracTree == nullptr) { + std::printf("Skipping %s: missing o2sim tree in %s\n", inputDir.c_str(), tracFile); + tracFileHandle->Close(); + delete tracFileHandle; + continue; + } + + if (tracTree->GetBranch("Vertices") == nullptr || tracTree->GetBranch("ITSVertexMCTruth") == nullptr) { + std::printf("Skipping %s: missing vertex branches in %s\n", inputDir.c_str(), tracFile); + tracFileHandle->Close(); + delete tracFileHandle; + continue; + } + + std::vector* vertices = nullptr; + std::vector* labels = nullptr; + std::vector* purities = nullptr; + const bool hasPurityBranch = tracTree->GetBranch("ITSVertexMCPurity") != nullptr; + + tracTree->SetBranchAddress("Vertices", &vertices); + tracTree->SetBranchAddress("ITSVertexMCTruth", &labels); + if (hasPurityBranch) { + tracTree->SetBranchAddress("ITSVertexMCPurity", &purities); + } + + const auto nEntries = tracTree->GetEntriesFast(); + for (Long64_t iEntry = 0; iEntry < nEntries; ++iEntry) { + tracTree->GetEntry(iEntry); + if (vertices == nullptr || labels == nullptr) { + continue; + } + auto nVertices = std::min(vertices->size(), labels->size()); + if (hasPurityBranch && purities != nullptr) { + nVertices = std::min(nVertices, purities->size()); + } + + for (size_t iVtx = 0; iVtx < nVertices; ++iVtx) { + const auto& vertex = (*vertices)[iVtx]; + const auto& label = (*labels)[iVtx]; + const auto multiplicity = static_cast(vertex.getNContributors()); + ++totalFound; + + if (!isTrueVertexLabel(label)) { + ++fakeFound; + fillMultiplicityHistogram(hRecoMultiplicityFake, multiplicity); + continue; + } + + ++trueFound; + const auto eventID = label.getEventID(); + uniqueTrueLabelsReco.insert(eventID); + fillMultiplicityHistogram(hRecoMultiplicityTrue, multiplicity); + + const auto truthIt = findableTruths.find(eventID); + if (truthIt == findableTruths.end()) { + continue; + } + + uniqueFindableTruthFound.insert(eventID); + const auto purity = (hasPurityBranch && purities != nullptr) ? (*purities)[iVtx] : -1.f; + const auto bestIt = bestRecoByTruth.find(eventID); + if (bestIt == bestRecoByTruth.end() || isBetterReco(vertex, purity, bestIt->second.vertex, bestIt->second.purity)) { + bestRecoByTruth[eventID] = BestRecoInfo{vertex, purity}; + } + } + } + + tracFileHandle->Close(); + delete tracFileHandle; + + findable += findableTruths.size(); + uniqueTrueReco += uniqueTrueLabelsReco.size(); + uniqueFindableFound += uniqueFindableTruthFound.size(); + + for (const auto eventID : uniqueFindableTruthFound) { + const auto truthIt = findableTruths.find(eventID); + if (truthIt != findableTruths.end()) { + fillMultiplicityHistogram(hTruthMultiplicityFound, truthIt->second.multiplicity); + } + } + + for (const auto& [eventID, reco] : bestRecoByTruth) { + const auto truthIt = findableTruths.find(eventID); + if (truthIt == findableTruths.end()) { + continue; + } + const auto dx = reco.vertex.getX() - truthIt->second.x; + const auto dy = reco.vertex.getY() - truthIt->second.y; + const auto dz = reco.vertex.getZ() - truthIt->second.z; + hDx->Fill(dx); + hDy->Fill(dy); + hDz->Fill(dz); + if (reco.vertex.getSigmaX() > 0.f) { + const auto pullX = dx / reco.vertex.getSigmaX(); + hPullX->Fill(pullX); + hPullXByMult[getMultiplicityCategory(reco.vertex.getNContributors()) - 1]->Fill(pullX); + sumSigmaX += reco.vertex.getSigmaX(); + ++sigmaXCount; + } + if (reco.vertex.getSigmaY() > 0.f) { + const auto pullY = dy / reco.vertex.getSigmaY(); + hPullY->Fill(pullY); + hPullYByMult[getMultiplicityCategory(reco.vertex.getNContributors()) - 1]->Fill(pullY); + sumSigmaY += reco.vertex.getSigmaY(); + ++sigmaYCount; + } + if (reco.vertex.getSigmaZ() > 0.f) { + const auto pullZ = dz / reco.vertex.getSigmaZ(); + hPullZ->Fill(pullZ); + hPullZByMult[getMultiplicityCategory(reco.vertex.getNContributors()) - 1]->Fill(pullZ); + sumSigmaZ += reco.vertex.getSigmaZ(); + ++sigmaZCount; + } + } + fs::current_path(cwd); + } + + auto* hTruthMultiplicityEfficiency = static_cast(hTruthMultiplicityFound->Clone("hTruthMultiplicityEfficiency")); + hTruthMultiplicityEfficiency->SetTitle("Unique efficiency vs truth multiplicity;truth multiplicity bin;efficiency"); + hTruthMultiplicityEfficiency->Divide(hTruthMultiplicityFound, hTruthMultiplicityFindable, 1., 1., "B"); + setMultiplicityBinLabels(hTruthMultiplicityEfficiency); + hTruthMultiplicityEfficiency->SetMinimum(0.); + hTruthMultiplicityEfficiency->SetMaximum(1.05); + + auto* hRecoMultiplicityTotal = static_cast(hRecoMultiplicityTrue->Clone("hRecoMultiplicityTotal")); + hRecoMultiplicityTotal->SetTitle("All reconstructed vertices;reco multiplicity bin;vertices"); + hRecoMultiplicityTotal->Add(hRecoMultiplicityFake); + setMultiplicityBinLabels(hRecoMultiplicityTotal); + + auto* hRecoMultiplicityPurity = static_cast(hRecoMultiplicityTrue->Clone("hRecoMultiplicityPurity")); + hRecoMultiplicityPurity->SetTitle("Purity vs reconstructed multiplicity;reco multiplicity bin;purity"); + hRecoMultiplicityPurity->Divide(hRecoMultiplicityTrue, hRecoMultiplicityTotal, 1., 1., "B"); + setMultiplicityBinLabels(hRecoMultiplicityPurity); + hRecoMultiplicityPurity->SetMinimum(0.); + hRecoMultiplicityPurity->SetMaximum(1.05); + + const auto duplicates = trueFound >= uniqueTrueReco ? (trueFound - uniqueTrueReco) : 0UL; + + const double uniqueEfficiency = findable > 0 ? static_cast(uniqueFindableFound) / findable : 0.; + const double purity = totalFound > 0 ? static_cast(trueFound) / totalFound : 0.; + const double fakeRate = totalFound > 0 ? static_cast(fakeFound) / totalFound : 0.; + const double duplicateRate = trueFound > 0 ? static_cast(duplicates) / trueFound : 0.; + const double f1 = (uniqueEfficiency + purity) > 0. ? 2. * uniqueEfficiency * purity / (uniqueEfficiency + purity) : 0.; + + const auto dxFit = fitGaussianCore(hDx, "fitDx"); + const auto dyFit = fitGaussianCore(hDy, "fitDy"); + const auto dzFit = fitGaussianCore(hDz, "fitDz"); + const auto pullXFit = fitGaussianCore(hPullX, "fitPullX"); + const auto pullYFit = fitGaussianCore(hPullY, "fitPullY"); + const auto pullZFit = fitGaussianCore(hPullZ, "fitPullZ"); + std::array pullXByMultFit{}; + std::array pullYByMultFit{}; + std::array pullZByMultFit{}; + for (int i = 0; i < NMultiplicityBins; ++i) { + const auto fitX = std::string("fitPullX_") + std::to_string(i + 1); + const auto fitY = std::string("fitPullY_") + std::to_string(i + 1); + const auto fitZ = std::string("fitPullZ_") + std::to_string(i + 1); + pullXByMultFit[i] = fitGaussianCore(hPullXByMult[i], fitX.c_str()); + pullYByMultFit[i] = fitGaussianCore(hPullYByMult[i], fitY.c_str()); + pullZByMultFit[i] = fitGaussianCore(hPullZByMult[i], fitZ.c_str()); + } + + std::printf("\nVertex validation summary\n"); + std::printf(" findable truth vertices : %zu\n", findable); + std::printf(" total reconstructed vertices : %zu\n", totalFound); + std::printf(" true reconstructed vertices : %zu\n", trueFound); + std::printf(" fake reconstructed vertices : %zu\n", fakeFound); + std::printf(" unique true labels (all) : %zu\n", uniqueTrueReco); + std::printf(" unique findable truth found : %zu\n", uniqueFindableFound); + std::printf(" unique efficiency : %.5f\n", uniqueEfficiency); + std::printf(" purity : %.5f\n", purity); + std::printf(" fake rate : %.5f\n", fakeRate); + std::printf(" duplicate rate : %.5f\n", duplicateRate); + std::printf(" F1(purity,efficiency) : %.5f\n", f1); + std::printf(" mean reported sigma x/y/z : %.6g / %.6g / %.6g cm\n", + sigmaXCount > 0 ? sumSigmaX / sigmaXCount : 0., + sigmaYCount > 0 ? sumSigmaY / sigmaYCount : 0., + sigmaZCount > 0 ? sumSigmaZ / sigmaZCount : 0.); + + if (dxFit.valid) { + std::printf(" x residual Gaussian: mean=%.6g cm sigma=%.6g cm\n", dxFit.mean, dxFit.sigma); + } + if (dyFit.valid) { + std::printf(" y residual Gaussian: mean=%.6g cm sigma=%.6g cm\n", dyFit.mean, dyFit.sigma); + } + if (dzFit.valid) { + std::printf(" z residual Gaussian: mean=%.6g cm sigma=%.6g cm\n", dzFit.mean, dzFit.sigma); + } + if (pullXFit.valid) { + std::printf(" x pull Gaussian : mean=%.6g sigma=%.6g\n", pullXFit.mean, pullXFit.sigma); + } + if (pullYFit.valid) { + std::printf(" y pull Gaussian : mean=%.6g sigma=%.6g\n", pullYFit.mean, pullYFit.sigma); + } + if (pullZFit.valid) { + std::printf(" z pull Gaussian : mean=%.6g sigma=%.6g\n", pullZFit.mean, pullZFit.sigma); + } + printGaussianByMultiplicity(pullXByMultFit, "x pull Gaussian by reconstructed multiplicity"); + printGaussianByMultiplicity(pullYByMultFit, "y pull Gaussian by reconstructed multiplicity"); + printGaussianByMultiplicity(pullZByMultFit, "z pull Gaussian by reconstructed multiplicity"); + + printBinnedFractions(hTruthMultiplicityFound, hTruthMultiplicityFindable, "Efficiency vs truth multiplicity"); + printBinnedFractions(hRecoMultiplicityTrue, hRecoMultiplicityTotal, "Purity vs reconstructed multiplicity"); + + auto* cValidation = new TCanvas("cVertexValidation", "Vertex validation summary", 1800, 1000); + cValidation->Divide(3, 2); + + cValidation->cd(1); + gPad->SetMargin(0.05, 0.05, 0.05, 0.05); + auto* summary = new TPaveText(0.02, 0.02, 0.98, 0.98, "NDC"); + summary->SetBorderSize(0); + summary->SetFillColor(0); + summary->SetTextAlign(12); + summary->SetTextFont(42); + char line[256]; + std::snprintf(line, sizeof(line), "Findable truth vertices : %zu", findable); + summary->AddText(line); + std::snprintf(line, sizeof(line), "Total reconstructed : %zu", totalFound); + summary->AddText(line); + std::snprintf(line, sizeof(line), "True reconstructed : %zu", trueFound); + summary->AddText(line); + std::snprintf(line, sizeof(line), "Fake reconstructed : %zu", fakeFound); + summary->AddText(line); + summary->AddText(""); + std::snprintf(line, sizeof(line), "Unique truth found : %zu", uniqueFindableFound); + summary->AddText(line); + std::snprintf(line, sizeof(line), "Unique efficiency : %.5f", uniqueEfficiency); + summary->AddText(line); + std::snprintf(line, sizeof(line), "Purity : %.5f", purity); + summary->AddText(line); + std::snprintf(line, sizeof(line), "Fake rate : %.5f", fakeRate); + summary->AddText(line); + std::snprintf(line, sizeof(line), "Duplicate rate : %.5f", duplicateRate); + summary->AddText(line); + std::snprintf(line, sizeof(line), "F1 : %.5f", f1); + summary->AddText(line); + std::snprintf(line, sizeof(line), "mean sigma x/y/z cm : %.3g / %.3g / %.3g", + sigmaXCount > 0 ? sumSigmaX / sigmaXCount : 0., + sigmaYCount > 0 ? sumSigmaY / sigmaYCount : 0., + sigmaZCount > 0 ? sumSigmaZ / sigmaZCount : 0.); + summary->AddText(line); + summary->AddText(""); + if (dxFit.valid) { + std::snprintf(line, sizeof(line), "dx fit mean/sigma cm : %.3g / %.3g", dxFit.mean, dxFit.sigma); + summary->AddText(line); + } + if (dyFit.valid) { + std::snprintf(line, sizeof(line), "dy fit mean/sigma cm : %.3g / %.3g", dyFit.mean, dyFit.sigma); + summary->AddText(line); + } + if (dzFit.valid) { + std::snprintf(line, sizeof(line), "dz fit mean/sigma cm : %.3g / %.3g", dzFit.mean, dzFit.sigma); + summary->AddText(line); + } + if (pullXFit.valid) { + std::snprintf(line, sizeof(line), "pull x sigma : %.3g", pullXFit.sigma); + summary->AddText(line); + } + if (pullYFit.valid) { + std::snprintf(line, sizeof(line), "pull y sigma : %.3g", pullYFit.sigma); + summary->AddText(line); + } + if (pullZFit.valid) { + std::snprintf(line, sizeof(line), "pull z sigma : %.3g", pullZFit.sigma); + summary->AddText(line); + } + summary->Draw(); + + cValidation->cd(2); + gPad->SetGridy(); + hTruthMultiplicityEfficiency->Draw("hist e1"); + + cValidation->cd(3); + gPad->SetGridy(); + const auto maxReco = std::max(hRecoMultiplicityTrue->GetMaximum(), hRecoMultiplicityFake->GetMaximum()); + hRecoMultiplicityTrue->SetMaximum(1.2 * std::max(1., maxReco)); + hRecoMultiplicityTrue->Draw("hist e1"); + hRecoMultiplicityFake->Draw("hist e1 same"); + auto hRecoMultiplicitySum = (TH1D*)hRecoMultiplicityTrue->Clone("hRecoMultiplicitySum"); + hRecoMultiplicitySum->Add(hRecoMultiplicityFake); + setHistogramStyle(hRecoMultiplicitySum, kBlack, 23); + hRecoMultiplicitySum->Draw("hist e1 same"); + { + auto* legend = new TLegend(0.58, 0.75, 0.88, 0.88); + legend->SetBorderSize(0); + legend->AddEntry(hRecoMultiplicityTrue, "true", "lep"); + legend->AddEntry(hRecoMultiplicityFake, "fake", "lep"); + legend->AddEntry(hRecoMultiplicitySum, "sum", "lep"); + legend->Draw(); + } + + cValidation->cd(4); + gPad->SetGridy(); + hRecoMultiplicityPurity->Draw("hist e1"); + + cValidation->cd(5); + gPad->SetGridy(); + auto* hDxNorm = makeNormalizedCopy(hDx, "hDxNorm", "Matched vertex residuals;residual (cm);normalized entries"); + auto* hDyNorm = makeNormalizedCopy(hDy, "hDyNorm", "Matched vertex residuals;residual (cm);normalized entries"); + auto* hDzNorm = makeNormalizedCopy(hDz, "hDzNorm", "Matched vertex residuals;residual (cm);normalized entries"); + const auto maxResidual = std::max({hDxNorm->GetMaximum(), hDyNorm->GetMaximum(), hDzNorm->GetMaximum()}); + hDzNorm->SetMaximum(1.2 * std::max(1., maxResidual)); + hDzNorm->Draw("hist"); + hDxNorm->Draw("hist same"); + hDyNorm->Draw("hist same"); + { + auto* legend = new TLegend(0.62, 0.72, 0.88, 0.88); + legend->SetBorderSize(0); + legend->AddEntry(hDxNorm, "dx", "l"); + legend->AddEntry(hDyNorm, "dy", "l"); + legend->AddEntry(hDzNorm, "dz", "l"); + legend->Draw(); + } + + cValidation->cd(6); + gPad->SetGridy(); + auto* hPullXNorm = makeNormalizedCopy(hPullX, "hPullXNorm", "Matched vertex pulls;pull;normalized entries"); + auto* hPullYNorm = makeNormalizedCopy(hPullY, "hPullYNorm", "Matched vertex pulls;pull;normalized entries"); + auto* hPullZNorm = makeNormalizedCopy(hPullZ, "hPullZNorm", "Matched vertex pulls;pull;normalized entries"); + const auto maxPull = std::max({hPullXNorm->GetMaximum(), hPullYNorm->GetMaximum(), hPullZNorm->GetMaximum()}); + hPullZNorm->SetMaximum(1.2 * std::max(1., maxPull)); + hPullZNorm->Draw("hist"); + hPullXNorm->Draw("hist same"); + hPullYNorm->Draw("hist same"); + { + auto* legend = new TLegend(0.62, 0.72, 0.88, 0.88); + legend->SetBorderSize(0); + legend->AddEntry(hPullXNorm, "pull x", "l"); + legend->AddEntry(hPullYNorm, "pull y", "l"); + legend->AddEntry(hPullZNorm, "pull z", "l"); + legend->Draw(); + } + + cValidation->cd(); + cValidation->Update(); + cValidation->SaveAs("checkSeeding.pdf"); +} diff --git a/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt b/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt index c9c5196da617b..1dd64b6f1874b 100644 --- a/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/tracking/CMakeLists.txt @@ -16,6 +16,7 @@ o2_add_library(ITStracking src/Configuration.cxx src/FastMultEstConfig.cxx src/FastMultEst.cxx + src/LineVertexerHelpers.cxx src/TimeFrame.cxx src/IOUtils.cxx src/Tracker.cxx diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h index 6fbc6d7da7721..bcb8a98a62cab 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/ClusterLines.h @@ -12,8 +12,9 @@ #ifndef O2_ITS_CLUSTERLINES_H #define O2_ITS_CLUSTERLINES_H -#include +#include #include +#include #include #include #include "ITStracking/Cluster.h" @@ -59,6 +60,7 @@ class ClusterLines final public: ClusterLines() = default; ClusterLines(const int firstLabel, const Line& firstLine, const int secondLabel, const Line& secondLine); + ClusterLines(gsl::span lineLabels, gsl::span lines); void add(const int lineLabel, const Line& line); void computeClusterCentroid(); void accumulate(const Line& line); @@ -67,7 +69,7 @@ class ClusterLines final const float* getRMS2() const { return mRMS2.Array(); } float getAvgDistance2() const { return mAvgDistance2; } auto getSize() const noexcept { return mLabels.size(); } - auto& getLabels() noexcept { return mLabels; } + auto& getLabels() const noexcept { return mLabels; } const auto& getTimeStamp() const noexcept { return mTime; } bool operator==(const ClusterLines& rhs) const noexcept; float getR2() const noexcept { return (mVertex[0] * mVertex[0]) + (mVertex[1] * mVertex[1]); } diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h index 02dbeb8cf3992..1f55a95ca0d65 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h @@ -18,11 +18,9 @@ #include #ifndef GPUCA_GPUCODE_DEVICE -#include #include #include #include -#include #endif #include "DetectorsBase/Propagator.h" @@ -89,21 +87,28 @@ struct VertexingParameters { int nIterations = 1; // Number of vertexing passes to perform std::vector LayerZ = {16.333f + 1, 16.333f + 1, 16.333f + 1, 42.140f + 1, 42.140f + 1, 73.745f + 1, 73.745f + 1}; std::vector LayerRadii = {2.33959f, 3.14076f, 3.91924f, 19.6213f, 24.5597f, 34.388f, 39.3329f}; - int ZBins{1}; - int PhiBins{128}; - float zCut = 0.002f; - float phiCut = 0.005f; - float pairCut = 0.04f; - float clusterCut = 0.8f; - float histPairCut = 0.04f; - float tanLambdaCut = 0.002f; // tanLambda = deltaZ/deltaR - float lowMultBeamDistCut = 0.1f; // XY cut for low-multiplicity pile up - int vertNsigmaCut = 6; // N sigma cut for vertex XY - float vertRadiusSigma = 0.33f; // sigma of vertex XY - float trackletSigma = 0.01f; // tracklet to vertex sigma - float maxZPositionAllowed = 25.f; - int clusterContributorsCut = 16; - int maxTrackletsPerCluster = 2e3; + int ZBins = 1; + int PhiBins = 128; + float zCut = -1.f; + float phiCut = -1.f; + float pairCut = -1.f; + float clusterCut = -1.f; + float coarseZWindow = -1.f; + float seedDedupZCut = -1.f; + float refitDedupZCut = -1.f; + float duplicateZCut = -1.f; + float finalSelectionZCut = -1.f; + float duplicateDistance2Cut = -1.f; + float tanLambdaCut = -1.f; + float vertNsigmaCut = -1.f; + float vertRadiusSigma = -1.f; + float trackletSigma = -1.f; + float maxZPositionAllowed = -1.f; + int clusterContributorsCut = -1; + int suppressLowMultDebris = -1; + int seedMemberRadiusTime = -1; + int seedMemberRadiusZ = -1; + int maxTrackletsPerCluster = -1; int phiSpan = -1; int zSpan = -1; bool SaveTimeBenchmarks = false; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/LineVertexerHelpers.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/LineVertexerHelpers.h new file mode 100644 index 0000000000000..0e3807aba8efb --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/LineVertexerHelpers.h @@ -0,0 +1,46 @@ +// 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. + +#ifndef O2_ITS_TRACKING_LINE_VERTEXER_HELPERS_H_ +#define O2_ITS_TRACKING_LINE_VERTEXER_HELPERS_H_ + +#include +#include + +#include "ITStracking/BoundedAllocator.h" +#include "ITStracking/ClusterLines.h" + +namespace o2::its::line_vertexer +{ + +struct Settings { + float beamX = 0.f; + float beamY = 0.f; + float pairCut = 0.f; + float pairCut2 = 0.f; + float clusterCut = 0.f; + float coarseZWindow = 0.f; + float seedDedupZCut = 0.f; + float refitDedupZCut = 0.f; + float duplicateZCut = 0.f; + float duplicateDistance2Cut = 0.f; + float finalSelectionZCut = 0.f; + float maxZ = 0.f; + int seedMemberRadiusTime = 1; + int seedMemberRadiusZ = 2; + std::shared_ptr memoryPool; +}; + +bounded_vector buildClusters(std::span lines, const Settings& settings); + +} // namespace o2::its::line_vertexer + +#endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/MathUtils.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/MathUtils.h index 95e0b4554e32c..ab3c7d5d29873 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/MathUtils.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/MathUtils.h @@ -94,6 +94,16 @@ GPUhdi() constexpr float Sq(float v) return v * v; } +GPUhdi() constexpr float SqSum(float v, float w) +{ + return Sq(v) + Sq(w); +} + +GPUhdi() constexpr float SqSum(float u, float v, float w) +{ + return Sq(u) + SqSum(v, w); +} + GPUhdi() constexpr float SqDiff(float x, float y) { return Sq(x - y); diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h index e77200a1432d1..cb291b46f5e44 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h @@ -24,24 +24,30 @@ struct VertexerParamConfig : public o2::conf::ConfigurableParamHelper50% + elem.setFakeFlag(); + } return std::make_pair(elem, static_cast(maxCount) / static_cast(elements.size())); } diff --git a/Detectors/ITSMFT/ITS/tracking/src/ClusterLines.cxx b/Detectors/ITSMFT/ITS/tracking/src/ClusterLines.cxx index f561fe0436c4a..3e3e1b8b46338 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/ClusterLines.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/ClusterLines.cxx @@ -148,6 +148,38 @@ ClusterLines::ClusterLines(const int firstLabel, const Line& firstLine, const in mAvgDistance2 += (Line::getDistance2FromPoint(secondLine, mVertex) - mAvgDistance2) / (float)getSize(); } +ClusterLines::ClusterLines(gsl::span lineLabels, gsl::span lines) +{ + if (lineLabels.size() < 2) { + return; + } + + mLabels.reserve(lineLabels.size()); + mTime = lines[lineLabels[0]].mTime; + for (size_t index = 0; index < lineLabels.size(); ++index) { + const auto lineLabel = lineLabels[index]; + if (index > 0) { + mTime += lines[lineLabel].mTime; + } + mLabels.push_back(lineLabel); + accumulate(lines[lineLabel]); + } + + computeClusterCentroid(); + if (!mIsValid) { + return; + } + + mRMS2 = Line::getDCAComponents(lines[lineLabels[0]], mVertex); + mAvgDistance2 = Line::getDistance2FromPoint(lines[lineLabels[0]], mVertex); + for (size_t index = 1; index < lineLabels.size(); ++index) { + const auto lineLabel = lineLabels[index]; + const auto tmpRMS2 = Line::getDCAComponents(lines[lineLabel], mVertex); + mRMS2 += (tmpRMS2 - mRMS2) * (1.f / static_cast(index + 1)); + mAvgDistance2 += (Line::getDistance2FromPoint(lines[lineLabel], mVertex) - mAvgDistance2) / static_cast(index + 1); + } +} + void ClusterLines::add(const int lineLabel, const Line& line) { mTime += line.mTime; diff --git a/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx b/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx index c447bb6bcc880..6c88b61f2df07 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx @@ -45,8 +45,8 @@ std::string TrackingParameters::asString() const } if (!AddTimeError.empty()) { str += " AddTimeError:"; - for (size_t i = 0; i < AddTimeError.size(); i++) { - str += std::format("{} ", AddTimeError[i]); + for (unsigned int i : AddTimeError) { + str += std::format("{} ", i); } } if (std::numeric_limits::max() != MaxMemory) { @@ -57,7 +57,8 @@ std::string TrackingParameters::asString() const std::string VertexingParameters::asString() const { - std::string str = std::format("NZb:{} NPhB:{} ClsCont:{} MaxTrkltCls:{} ZCut:{} PhCut:{}", ZBins, PhiBins, clusterContributorsCut, maxTrackletsPerCluster, zCut, phiCut); + std::string str = std::format("NZb:{} NPhB:{} MinVtxCont:{} SupLowMultDebris:{} MaxTrkltCls:{} ZCut:{} PhCut:{} PairCut:{} ClCut:{} SeedRad:{}x{}", + ZBins, PhiBins, clusterContributorsCut, suppressLowMultDebris, maxTrackletsPerCluster, zCut, phiCut, pairCut, clusterCut, seedMemberRadiusTime, seedMemberRadiusZ); if (std::numeric_limits::max() != MaxMemory) { str += std::format(" MemLimit {:.2f} GB", double(MaxMemory) / constants::GB); } @@ -173,8 +174,8 @@ std::vector TrackingMode::getTrackingParameters(TrackingMode LOGP(fatal, "Unsupported ITS tracking mode {} ", toString(mode)); } - float bFactor = std::abs(o2::base::Propagator::Instance()->getNominalBz()) / 5.0066791; - float bFactorTracklets = bFactor < 0.01 ? 1. : bFactor; // for tracklets only + float bFactor = std::abs(o2::base::Propagator::Instance()->getNominalBz()) / 5.0066791f; + float bFactorTracklets = bFactor < 0.01f ? 1.f : bFactor; // for tracklets only // global parameters set for every iteration for (auto& p : trackParams) { @@ -262,6 +263,9 @@ std::vector TrackingMode::getVertexingParameters(TrackingMo p.trackletSigma = vc.trackletSigma; p.maxZPositionAllowed = vc.maxZPositionAllowed; p.clusterContributorsCut = vc.clusterContributorsCut; + p.suppressLowMultDebris = vc.suppressLowMultDebris; + p.seedMemberRadiusTime = vc.seedMemberRadiusTime; + p.seedMemberRadiusZ = vc.seedMemberRadiusZ; p.phiSpan = vc.phiSpan; p.nThreads = vc.nThreads; p.ZBins = vc.ZBins; @@ -273,12 +277,16 @@ std::vector TrackingMode::getVertexingParameters(TrackingMo vertParams[0].vertNsigmaCut = vc.vertNsigmaCut; vertParams[0].vertRadiusSigma = vc.vertRadiusSigma; vertParams[0].maxTrackletsPerCluster = vc.maxTrackletsPerCluster; - vertParams[0].lowMultBeamDistCut = vc.lowMultBeamDistCut; vertParams[0].zCut = vc.zCut; vertParams[0].phiCut = vc.phiCut; vertParams[0].pairCut = vc.pairCut; vertParams[0].clusterCut = vc.clusterCut; - vertParams[0].histPairCut = vc.histPairCut; + vertParams[0].coarseZWindow = vc.coarseZWindow; + vertParams[0].seedDedupZCut = vc.seedDedupZCut; + vertParams[0].refitDedupZCut = vc.refitDedupZCut; + vertParams[0].duplicateZCut = vc.duplicateZCut; + vertParams[0].finalSelectionZCut = vc.finalSelectionZCut; + vertParams[0].duplicateDistance2Cut = vc.duplicateDistance2Cut; vertParams[0].tanLambdaCut = vc.tanLambdaCut; return vertParams; diff --git a/Detectors/ITSMFT/ITS/tracking/src/LineVertexerHelpers.cxx b/Detectors/ITSMFT/ITS/tracking/src/LineVertexerHelpers.cxx new file mode 100644 index 0000000000000..592c22dedf347 --- /dev/null +++ b/Detectors/ITSMFT/ITS/tracking/src/LineVertexerHelpers.cxx @@ -0,0 +1,1036 @@ +// 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 +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include "ITStracking/Constants.h" +#include "ITStracking/MathUtils.h" +#include "ITStracking/LineVertexerHelpers.h" + +namespace o2::its::line_vertexer +{ +namespace +{ +using SymMatrix3 = ROOT::Math::SMatrix>; +using SVector3 = ROOT::Math::SVector; + +constexpr float TukeyC = 4.685f; +constexpr float TukeyC2 = TukeyC * TukeyC; +constexpr float InitialScale2 = 5.f; +constexpr float MinScale2 = 1.f; +constexpr float MedianToSigma = 1.4826f; +constexpr float VertexShiftZTol = 0.01f; +constexpr float VertexShiftR2Tol = 1.e-4f; +constexpr int MaxFitIterations = 10; +constexpr int MaxSeedsPerCluster = 32; +constexpr float MinRelativePeakSupport = 0.1f; +constexpr int MaxHistogramBins = 0x7fff; +constexpr float TieTolerance = 1e-5f; + +struct LineRef { + LineRef(const Line& line, const int index, const float beamX, const float beamY, const float maxZ) : lineIndex(index) + { + const auto symTime = line.mTime.makeSymmetrical(); + tCenter = symTime.getTimeStamp(); + tHalfWidth = symTime.getTimeStampError(); + const auto dx = line.originPoint(0) - beamX; + const auto dy = line.originPoint(1) - beamY; + const auto ux = line.cosinesDirector(0); + const auto uy = line.cosinesDirector(1); + const auto uz = line.cosinesDirector(2); + const auto den = math_utils::SqSum(ux, uy); + if (den <= constants::Tolerance) { + lineIndex = constants::UnusedIndex; + return; + } + const auto s0 = -((dx * ux) + (dy * uy)) / den; + const auto xb = dx + (s0 * ux); + const auto yb = dy + (s0 * uy); + zBeam = line.originPoint(2) + s0 * uz; + if (!std::isfinite(zBeam) || o2::gpu::CAMath::Abs(zBeam) > maxZ) { + lineIndex = constants::UnusedIndex; + } + } + bool isDead() const noexcept { return lineIndex == constants::UnusedIndex; } + + int lineIndex = constants::UnusedIndex; + float zBeam = 0.f; + float tCenter = 0.f; + float tHalfWidth = 0.f; +}; + +struct VertexSeed { + explicit VertexSeed(const std::shared_ptr& mr) : contributors(mr.get()), assigned(mr.get()) {} + + std::array vertex = {}; + TimeEstBC time; + float scale2 = InitialScale2; + bounded_vector contributors; + bounded_vector assigned; + bool valid = false; + bool isUsableSeed() const noexcept + { + return valid && contributors.size() >= 2; + } +}; + +void compactSeeds(bounded_vector& seeds) +{ + seeds.erase(std::remove_if(seeds.begin(), seeds.end(), [](const VertexSeed& seed) { + return !seed.isUsableSeed(); + }), + seeds.end()); +} + +struct Histogram2D { + explicit Histogram2D(const std::shared_ptr& mr) : bins(mr.get()) {} + + int nTimeBins = 0; + int nZBins = 0; + float timeMin = 0.f; + float zMin = 0.f; + float timeBinSize = 1.f; + float zBinSize = 1.f; + bounded_vector bins; + + int getIndex(const int tBin, const int zBin) const noexcept + { + return (tBin * nZBins) + zBin; + } + + std::pair decodeIndex(const int index) const noexcept + { + return {index / nZBins, index % nZBins}; + } + + int getTimeBin(const float time) const noexcept + { + if (time < timeMin) { + return -1; + } + const auto bin = static_cast((time - timeMin) / timeBinSize); + return (bin >= 0 && bin < nTimeBins) ? bin : -1; + } + + int getZBin(const float z) const noexcept + { + if (z < zMin) { + return -1; + } + const auto bin = static_cast((z - zMin) / zBinSize); + return (bin >= 0 && bin < nZBins) ? bin : -1; + } + + void fill(const float time, const float z, const float weight) noexcept + { + const auto tBin = getTimeBin(time); + const auto zBin = getZBin(z); + if (tBin < 0 || zBin < 0) { + return; + } + bins[getIndex(tBin, zBin)] += weight; + } + + int findPeakBin() const noexcept + { + float bestWeight = 0.f; + int bestIndex = -1; + for (int index = 0; index < static_cast(bins.size()); ++index) { + if (bins[index] > bestWeight) { + bestWeight = bins[index]; + bestIndex = index; + } + } + return bestIndex; + } + + void suppressBin(const int index) noexcept + { + if (index >= 0 && index < static_cast(bins.size())) { + bins[index] = -1.f; + } + } + + void suppressNeighborhood(const int index, const int radiusTime, const int radiusZ) noexcept + { + if (index < 0) { + return; + } + const auto [tBin, zBin] = decodeIndex(index); + for (int dt = -radiusTime; dt <= radiusTime; ++dt) { + const auto tt = tBin + dt; + if (tt < 0 || tt >= nTimeBins) { + continue; + } + for (int dz = -radiusZ; dz <= radiusZ; ++dz) { + const auto zz = zBin + dz; + if (zz < 0 || zz >= nZBins) { + continue; + } + bins[getIndex(tt, zz)] = -1.f; + } + } + } + + float getNeighborhoodSum(const int index, const int radiusTime, const int radiusZ) const noexcept + { + if (index < 0) { + return 0.f; + } + const auto [tBin, zBin] = decodeIndex(index); + float sum = 0.f; + for (int dt = -radiusTime; dt <= radiusTime; ++dt) { + const auto tt = tBin + dt; + if (tt < 0 || tt >= nTimeBins) { + continue; + } + for (int dz = -radiusZ; dz <= radiusZ; ++dz) { + const auto zz = zBin + dz; + if (zz < 0 || zz >= nZBins) { + continue; + } + const auto value = bins[getIndex(tt, zz)]; + if (value > 0.f) { + sum += value; + } + } + } + return sum; + } + + float getTimeBinCenter(const int tBin) const noexcept + { + return timeMin + ((static_cast(tBin) + 0.5f) * timeBinSize); + } + + float getZBinCenter(const int zBin) const noexcept + { + return zMin + ((static_cast(zBin) + 0.5f) * zBinSize); + } + + TimeEstBC getTimeInterval(const int tBin) const noexcept + { + const auto lowFloat = timeMin + (static_cast(tBin) * timeBinSize); + const auto highFloat = lowFloat + timeBinSize; + const auto low = std::max(0., std::floor(lowFloat)); + const auto high = std::max(low + 1., (double)std::ceil(highFloat)); + constexpr auto maxTS = std::numeric_limits::max(); + const auto clampedLow = std::min(low, maxTS - 1.); + const auto width = std::min(high - clampedLow, std::numeric_limits::max()); + return {static_cast(clampedLow), static_cast(std::max(1., width))}; + } + + TimeEstBC getTimeNeighborhoodInterval(const int tBin, const int radius) const noexcept + { + const auto lowBin = std::max(0, tBin - radius); + const auto highBin = std::min(nTimeBins - 1, tBin + radius); + const auto lowFloat = timeMin + (static_cast(lowBin) * timeBinSize); + const auto highFloat = timeMin + (static_cast(highBin + 1) * timeBinSize); + const auto low = std::max(0., std::floor(lowFloat)); + const auto high = std::max(low + 1., (double)std::ceil(highFloat)); + constexpr auto maxTS = std::numeric_limits::max(); + const auto clampedLow = std::min(low, maxTS - 1.); + const auto width = std::min(high - clampedLow, std::numeric_limits::max()); + return {static_cast(clampedLow), static_cast(std::max(1., width))}; + } +}; + +class SeedHistogram +{ + public: + SeedHistogram(std::span members, + std::span lineRefs, + std::span lines, + const Settings& settings) + : mMembers(members), mLineRefs(lineRefs), mSeedMemberRadiusTime(settings.seedMemberRadiusTime), mSeedMemberRadiusZ(settings.seedMemberRadiusZ), mMemoryPool(settings.memoryPool), mHistogram(mMemoryPool) + { + const auto zBinSize = 0.25f * settings.clusterCut; + const auto timeBinSize = medianTimeError(lines); + + float minZ = std::numeric_limits::max(); + float maxZ = std::numeric_limits::lowest(); + float minTime = std::numeric_limits::max(); + float maxTime = std::numeric_limits::lowest(); + for (const auto lineRefIdx : mMembers) { + minZ = std::min(minZ, mLineRefs[lineRefIdx].zBeam); + maxZ = std::max(maxZ, mLineRefs[lineRefIdx].zBeam); + minTime = std::min(minTime, mLineRefs[lineRefIdx].tCenter); + maxTime = std::max(maxTime, mLineRefs[lineRefIdx].tCenter); + } + + const auto dz = std::max(0.f, maxZ - minZ); + const auto dt = std::max(0.f, maxTime - minTime); + mHistogram.nZBins = 1 + static_cast(dz / zBinSize); + mHistogram.nTimeBins = 1 + static_cast(dt / timeBinSize); + if (mHistogram.nTimeBins * mHistogram.nZBins > MaxHistogramBins) { + if (mHistogram.nTimeBins > mHistogram.nZBins) { + mHistogram.nTimeBins = std::max(1, (MaxHistogramBins - 1) / std::max(1, mHistogram.nZBins)); + } else { + mHistogram.nZBins = std::max(1, (MaxHistogramBins - 1) / std::max(1, mHistogram.nTimeBins)); + } + } + + mHistogram.timeBinSize = std::max(timeBinSize, dt / (float)std::max(1, mHistogram.nTimeBins)); + mHistogram.zBinSize = std::max(zBinSize, dz / (float)std::max(1, mHistogram.nZBins)); + const auto paddedTime = 0.5f * ((float)mHistogram.nTimeBins * mHistogram.timeBinSize - dt); + const auto paddedZ = 0.5f * ((float)mHistogram.nZBins * mHistogram.zBinSize - dz); + mHistogram.timeMin = minTime - paddedTime; + mHistogram.zMin = minZ - paddedZ; + mHistogram.bins.assign((size_t)mHistogram.nTimeBins * (size_t)mHistogram.nZBins, 0.f); + + for (const auto lineRefIdx : mMembers) { + mHistogram.fill(mLineRefs[lineRefIdx].tCenter, mLineRefs[lineRefIdx].zBeam, 1.f); + } + } + + int findPeakBin() const noexcept + { + return mHistogram.findPeakBin(); + } + + float getPeakSupport(const int peakIndex) const noexcept + { + return mHistogram.getNeighborhoodSum(peakIndex, mSeedMemberRadiusTime, mSeedMemberRadiusZ); + } + + bounded_vector collectLocalMembers(const int peakIndex, const int radiusTime, const int radiusZ) const + { + bounded_vector localMembers(mMemoryPool.get()); + localMembers.reserve(mMembers.size()); + const auto [timeBin, zBin] = mHistogram.decodeIndex(peakIndex); + for (const auto lineRefIdx : mMembers) { + const auto memberTimeBin = mHistogram.getTimeBin(mLineRefs[lineRefIdx].tCenter); + const auto memberZBin = mHistogram.getZBin(mLineRefs[lineRefIdx].zBeam); + if (memberTimeBin < 0 || memberZBin < 0) { + continue; + } + if (o2::gpu::GPUCommonMath::Abs(memberTimeBin - timeBin) > radiusTime) { + continue; + } + if (o2::gpu::GPUCommonMath::Abs(memberZBin - zBin) > radiusZ) { + continue; + } + localMembers.push_back(lineRefIdx); + } + return localMembers; + } + + TimeEstBC getPeakTimeInterval(const int peakIndex, const int radius = 0) const noexcept + { + return mHistogram.getTimeNeighborhoodInterval(mHistogram.decodeIndex(peakIndex).first, radius); + } + + float getPeakZCenter(const int peakIndex) const noexcept + { + return mHistogram.getZBinCenter(mHistogram.decodeIndex(peakIndex).second); + } + + void suppressPeak(const int peakIndex) noexcept + { + mHistogram.suppressBin(peakIndex); + } + + void suppressPeakNeighborhood(const int peakIndex) noexcept + { + mHistogram.suppressNeighborhood(peakIndex, mSeedMemberRadiusTime, mSeedMemberRadiusZ); + } + + private: + float medianTimeError(std::span lines) const + { + bounded_vector errors(mMemoryPool.get()); + errors.reserve(mMembers.size()); + for (const auto lineRefIdx : mMembers) { + errors.push_back(static_cast(lines[mLineRefs[lineRefIdx].lineIndex].mTime.getTimeStampError())); + } + std::sort(errors.begin(), errors.end()); + return errors.empty() ? 1.f : std::max(1.f, errors[errors.size() / 2]); + } + + std::span mMembers; + std::span mLineRefs; + int mSeedMemberRadiusTime = 1; + int mSeedMemberRadiusZ = 2; + std::shared_ptr mMemoryPool; + Histogram2D mHistogram; +}; + +float updateScale2(const std::span chi2s, const std::shared_ptr& mr) noexcept +{ + if (chi2s.empty()) { + return MinScale2; + } + + bounded_vector sorted(chi2s.begin(), chi2s.end(), mr.get()); + std::sort(sorted.begin(), sorted.end()); + const auto median = sorted[sorted.size() / 2]; + + for (auto& value : sorted) { + value = o2::gpu::GPUCommonMath::Abs(value - median); + } + std::sort(sorted.begin(), sorted.end()); + const auto mad = sorted[sorted.size() / 2]; + if (!std::isfinite(mad) || mad <= constants::Tolerance) { + return MinScale2; + } + return std::max(MinScale2, MedianToSigma * mad); +} + +class VertexFit +{ + public: + void add(const Line& line, const float weight) noexcept + { + const auto& direction = line.cosinesDirector; + const auto& origin = line.originPoint; + const auto det = ROOT::Math::Dot(direction, direction); + if (det <= constants::Tolerance) { + return; + } + + for (int i = 0; i < 3; ++i) { + for (int j = i; j < 3; ++j) { + mMatrix(i, j) += weight * (((i == j ? det : 0.f) - direction(i) * direction(j)) / det); + } + } + + const auto dDotO = ROOT::Math::Dot(direction, origin); + for (int i = 0; i < 3; ++i) { + mRhs(i) += weight * ((direction(i) * dDotO - det * origin(i)) / det); + } + } + + bool solve(std::array& vertexOut) const noexcept + { + SymMatrix3 inv{mMatrix}; + if (!inv.InvertFast()) { + return false; + } + const auto solution = inv * mRhs; + vertexOut[0] = static_cast(-solution(0)); + vertexOut[1] = static_cast(-solution(1)); + vertexOut[2] = static_cast(-solution(2)); + return std::isfinite(vertexOut[0]) && std::isfinite(vertexOut[1]) && std::isfinite(vertexOut[2]); + } + + private: + SymMatrix3 mMatrix; + SVector3 mRhs; +}; + +VertexSeed fitSeed(const VertexSeed& initialSeed, + std::span members, + std::span lineRefs, + std::span lines, + const std::shared_ptr& mr, + const float pairCut2) +{ + VertexSeed seed{mr}; + seed.vertex = initialSeed.vertex; + seed.time = initialSeed.time; + seed.scale2 = initialSeed.scale2; + seed.valid = false; + seed.contributors.clear(); + seed.assigned.clear(); + if (members.size() < 2) { + return seed; + } + + for (int iteration = 0; iteration < MaxFitIterations; ++iteration) { + VertexFit vertexFit; + TimeEstBC commonTime{}; + bool hasCommonTime = false; + bounded_vector contributors{mr.get()}; + const auto scale2 = std::max(seed.scale2, MinScale2); + const auto tukeyFactor = 1.f / (scale2 * TukeyC2); + + for (const auto lineRefIdx : members) { + const auto lineIdx = lineRefs[lineRefIdx].lineIndex; + const auto& line = lines[lineIdx]; + if (!line.mTime.isCompatible(seed.time)) { + continue; + } + if (hasCommonTime && !line.mTime.isCompatible(commonTime)) { + continue; + } + + const auto chi2 = Line::getDistance2FromPoint(line, seed.vertex) / pairCut2; + auto weight = 1.f - (chi2 * tukeyFactor); + if (weight <= 0.f) { + continue; + } + weight *= weight; + + if (!hasCommonTime) { + commonTime = line.mTime; + hasCommonTime = true; + } else { + commonTime += line.mTime; + } + + contributors.push_back(lineRefIdx); + vertexFit.add(line, weight); + } + + if (!hasCommonTime || contributors.size() < 2) { + return seed; + } + + std::sort(contributors.begin(), contributors.end()); + + std::array updatedVertex{}; + if (!vertexFit.solve(updatedVertex)) { + return seed; + } + + const auto sameContributors = contributors == seed.contributors; + const auto dz = o2::gpu::GPUCommonMath::Abs(updatedVertex[2] - seed.vertex[2]); + const auto oldR2 = (seed.vertex[0] * seed.vertex[0]) + (seed.vertex[1] * seed.vertex[1]); + const auto newR2 = (updatedVertex[0] * updatedVertex[0]) + (updatedVertex[1] * updatedVertex[1]); + const auto dr2 = o2::gpu::GPUCommonMath::Abs(newR2 - oldR2); + + seed.vertex = updatedVertex; + seed.time = commonTime; + bounded_vector updatedChi2s{mr.get()}; + updatedChi2s.reserve(contributors.size()); + for (const auto lineRefIx : contributors) { + updatedChi2s.push_back(Line::getDistance2FromPoint(lines[lineRefs[lineRefIx].lineIndex], seed.vertex) / pairCut2); + } + seed.scale2 = updateScale2(updatedChi2s, mr); + seed.contributors = std::move(contributors); + seed.valid = true; + + if (sameContributors && dz < VertexShiftZTol && dr2 < VertexShiftR2Tol) { + break; + } + } + + return seed; +} + +size_t countSharedContributors(std::span lhs, std::span rhs) noexcept +{ + size_t shared = 0; + auto lhsIt = lhs.begin(); + auto rhsIt = rhs.begin(); + while (lhsIt != lhs.end() && rhsIt != rhs.end()) { + if (*lhsIt == *rhsIt) { + ++shared; + ++lhsIt; + ++rhsIt; + } else if (*lhsIt < *rhsIt) { + ++lhsIt; + } else { + ++rhsIt; + } + } + return shared; +} + +bounded_vector collectCompatibleContributors(const VertexSeed& seed, + std::span members, + std::span lineRefs, + std::span lines, + const std::shared_ptr& mr, + const float pairCut2) +{ + bounded_vector contributors{mr.get()}; + contributors.reserve(members.size()); + for (const auto lineRefIdx : members) { + const auto lineIdx = lineRefs[lineRefIdx].lineIndex; + const auto& line = lines[lineIdx]; + if (!line.mTime.isCompatible(seed.time)) { + continue; + } + if (Line::getDistance2FromPoint(line, seed.vertex) >= pairCut2) { + continue; + } + contributors.push_back(lineRefIdx); + } + std::sort(contributors.begin(), contributors.end()); + return contributors; +} + +void deduplicateSeeds(bounded_vector& seeds, const Settings& settings) +{ + if (seeds.size() < 2) { + return; + } + + std::sort(seeds.begin(), seeds.end(), [](const VertexSeed& lhs, const VertexSeed& rhs) { + if (lhs.contributors.size() != rhs.contributors.size()) { + return lhs.contributors.size() > rhs.contributors.size(); + } + if (o2::gpu::GPUCommonMath::Abs(lhs.scale2 - rhs.scale2) > constants::Tolerance) { + return lhs.scale2 < rhs.scale2; + } + return lhs.vertex[2] < rhs.vertex[2]; + }); + + const auto dedupZCut = settings.seedDedupZCut > 0.f ? settings.seedDedupZCut : 0.25f * settings.clusterCut; + for (size_t i = 0; i < seeds.size(); ++i) { + auto& candidate = seeds[i]; + if (!candidate.isUsableSeed()) { + candidate.valid = false; + continue; + } + bool duplicate = false; + for (size_t j = 0; j < i; ++j) { + const auto& kept = seeds[j]; + if (!kept.isUsableSeed()) { + continue; + } + if (!candidate.time.isCompatible(kept.time)) { + continue; + } + const auto shared = countSharedContributors(candidate.contributors, kept.contributors); + const auto minSize = std::min(candidate.contributors.size(), kept.contributors.size()); + const auto zDelta = o2::gpu::GPUCommonMath::Abs(candidate.vertex[2] - kept.vertex[2]); + const bool clearlyWorse = kept.contributors.size() > candidate.contributors.size() || + kept.scale2 + constants::Tolerance < 0.9f * candidate.scale2; + const bool overlapDuplicate = shared > 0 && shared * 2 >= minSize; + const bool nearbyDuplicate = zDelta < dedupZCut && (shared > 0 || clearlyWorse); + if (overlapDuplicate || nearbyDuplicate) { + duplicate = true; + break; + } + } + if (duplicate) { + candidate.valid = false; + } + } + compactSeeds(seeds); +} + +void deduplicateRefittedSeeds(bounded_vector& seeds, const Settings& settings) +{ + if (seeds.size() < 2) { + return; + } + + std::sort(seeds.begin(), seeds.end(), [](const VertexSeed& lhs, const VertexSeed& rhs) { + if (lhs.contributors.size() != rhs.contributors.size()) { + return lhs.contributors.size() > rhs.contributors.size(); + } + if (o2::gpu::GPUCommonMath::Abs(lhs.scale2 - rhs.scale2) > constants::Tolerance) { + return lhs.scale2 < rhs.scale2; + } + return lhs.vertex[2] < rhs.vertex[2]; + }); + + const auto zCut = settings.refitDedupZCut > 0.f ? settings.refitDedupZCut : 0.25f * settings.clusterCut; + for (size_t i = 0; i < seeds.size(); ++i) { + auto& candidate = seeds[i]; + if (!candidate.isUsableSeed()) { + candidate.valid = false; + continue; + } + bool duplicate = false; + for (size_t j = 0; j < i; ++j) { + const auto& kept = seeds[j]; + if (!kept.isUsableSeed()) { + continue; + } + if (!candidate.time.isCompatible(kept.time)) { + continue; + } + const auto shared = countSharedContributors(candidate.contributors, kept.contributors); + const auto minSize = std::min(candidate.contributors.size(), kept.contributors.size()); + const auto zDelta = o2::gpu::GPUCommonMath::Abs(candidate.vertex[2] - kept.vertex[2]); + const bool overlapDuplicate = shared > 0 && shared * 2 >= minSize; + const bool lowSupportPair = std::min(candidate.contributors.size(), kept.contributors.size()) < 4; + const bool clearlyWorse = kept.contributors.size() > candidate.contributors.size() || + kept.scale2 + constants::Tolerance < 0.9f * candidate.scale2; + const bool geometricDuplicate = zDelta < zCut && (lowSupportPair || clearlyWorse); + if (overlapDuplicate || geometricDuplicate) { + duplicate = true; + break; + } + } + if (duplicate) { + candidate.valid = false; + } + } + compactSeeds(seeds); +} + +struct OrderedComponent { + explicit OrderedComponent(const std::shared_ptr& mr) : members(mr.get()) {} + float center = 0.f; + bounded_vector members; +}; + +bounded_vector> buildCoarseClusters(std::span lineRefs, + std::span lines, + const Settings& settings) +{ + bounded_vector> clusters(settings.memoryPool.get()); + if (lineRefs.size() < 2) { + return clusters; + } + + bounded_vector sortedByLower(lineRefs.size(), settings.memoryPool.get()); + std::iota(sortedByLower.begin(), sortedByLower.end(), 0); + std::sort(sortedByLower.begin(), sortedByLower.end(), [&](const int lhs, const int rhs) { + const auto lhsLower = lines[lineRefs[lhs].lineIndex].mTime.lower(); + const auto rhsLower = lines[lineRefs[rhs].lineIndex].mTime.lower(); + if (lhsLower != rhsLower) { + return lhsLower < rhsLower; + } + return lineRefs[lhs].lineIndex < lineRefs[rhs].lineIndex; + }); + + const auto coarseZWindow = settings.coarseZWindow > 0.f ? settings.coarseZWindow : settings.clusterCut; + bounded_vector parent(lineRefs.size(), settings.memoryPool.get()); + bounded_vector componentSize(lineRefs.size(), 1, settings.memoryPool.get()); + std::iota(parent.begin(), parent.end(), 0); + float minZ = std::numeric_limits::max(); + float maxZ = std::numeric_limits::lowest(); + for (const auto& lineRef : lineRefs) { + minZ = std::min(minZ, lineRef.zBeam); + maxZ = std::max(maxZ, lineRef.zBeam); + } + const auto nZBins = std::max(1, 1 + static_cast((maxZ - minZ) / coarseZWindow)); + auto getZBin = [&](const float z) { + return std::clamp(static_cast((z - minZ) / coarseZWindow), 0, nZBins - 1); + }; + + auto findRoot = [&](int idx) { + int root = idx; + while (parent[root] != root) { + root = parent[root]; + } + while (parent[idx] != idx) { + const auto next = parent[idx]; + parent[idx] = root; + idx = next; + } + return root; + }; + + auto unite = [&](const int lhs, const int rhs) { + auto lhsRoot = findRoot(lhs); + auto rhsRoot = findRoot(rhs); + if (lhsRoot == rhsRoot) { + return; + } + if (componentSize[lhsRoot] < componentSize[rhsRoot]) { + std::swap(lhsRoot, rhsRoot); + } + parent[rhsRoot] = lhsRoot; + componentSize[lhsRoot] += componentSize[rhsRoot]; + }; + + using ActiveEntry = std::pair; + bounded_vector activeEntries(settings.memoryPool.get()); + std::priority_queue, std::greater> activeByUpper(std::greater{}, std::move(activeEntries)); + bounded_vector activeMask(lineRefs.size(), 0, settings.memoryPool.get()); + bounded_vector> activeByZBin(settings.memoryPool.get()); + activeByZBin.reserve(nZBins); + for (int iBin = 0; iBin < nZBins; ++iBin) { + activeByZBin.emplace_back(); + } + for (const auto lineRefIdx : sortedByLower) { + const auto& lineRef = lineRefs[lineRefIdx]; + const auto& line = lines[lineRef.lineIndex]; + const auto currentLower = line.mTime.lower(); + + while (!activeByUpper.empty() && activeByUpper.top().first < currentLower) { + activeMask[activeByUpper.top().second] = 0; + activeByUpper.pop(); + } + + const auto zBin = getZBin(lineRef.zBeam); + for (int neighborBin = std::max(0, zBin - 1); neighborBin <= std::min(nZBins - 1, zBin + 1); ++neighborBin) { + auto& bucket = activeByZBin[neighborBin]; + size_t writePos = 0; + for (size_t readPos = 0; readPos < bucket.size(); ++readPos) { + const auto oLineRefIdx = bucket[readPos]; + if (!activeMask[oLineRefIdx]) { + continue; + } + bucket[writePos++] = oLineRefIdx; + const auto& oLineRef = lineRefs[oLineRefIdx]; + if (o2::gpu::GPUCommonMath::Abs(lineRef.zBeam - oLineRef.zBeam) >= coarseZWindow) { + continue; + } + const auto& otherLine = lines[oLineRef.lineIndex]; + if (line.mTime.isCompatible(otherLine.mTime)) { + unite(lineRefIdx, oLineRefIdx); + } + } + bucket.resize(writePos); + } + + activeMask[lineRefIdx] = 1; + activeByUpper.emplace(line.mTime.upper(), lineRefIdx); + activeByZBin[zBin].push_back(lineRefIdx); + } + + std::unordered_map> components; + components.reserve(lineRefs.size()); + for (int lineRefIdx = 0; lineRefIdx < static_cast(lineRefs.size()); ++lineRefIdx) { + const auto root = findRoot(lineRefIdx); + auto [it, inserted] = components.try_emplace(root, std::pmr::polymorphic_allocator{settings.memoryPool.get()}); + (void)inserted; + it->second.push_back(lineRefIdx); + } + + bounded_vector orderedComponents(settings.memoryPool.get()); + orderedComponents.reserve(components.size()); + for (auto& [root, members] : components) { + (void)root; + if (members.size() < 2) { + continue; + } + std::sort(members.begin(), members.end(), [&](const int lhs, const int rhs) { + const auto lhsLower = lines[lineRefs[lhs].lineIndex].mTime.lower(); + const auto rhsLower = lines[lineRefs[rhs].lineIndex].mTime.lower(); + if (lhsLower != rhsLower) { + return lhsLower < rhsLower; + } + return lineRefs[lhs].lineIndex < lineRefs[rhs].lineIndex; + }); + orderedComponents.emplace_back(settings.memoryPool); + orderedComponents.back().center = lineRefs[members.front()].tCenter; + orderedComponents.back().members = std::move(members); + } + + std::sort(orderedComponents.begin(), orderedComponents.end(), [](const auto& lhs, const auto& rhs) { + if (o2::gpu::GPUCommonMath::Abs(lhs.center - rhs.center) > TieTolerance) { + return lhs.center < rhs.center; + } + return lhs.members.front() < rhs.members.front(); + }); + clusters.reserve(orderedComponents.size()); + for (auto& component : orderedComponents) { + clusters.push_back(std::move(component.members)); + } + return clusters; +} + +bounded_vector buildSeeds(std::span members, + std::span lineRefs, + std::span lines, + const Settings& settings) +{ + SeedHistogram histogram(members, lineRefs, lines, settings); + bounded_vector seeds(settings.memoryPool.get()); + seeds.reserve(MaxSeedsPerCluster); + float leadingPeakSupport = 0.f; + + while (static_cast(seeds.size()) < MaxSeedsPerCluster) { + const auto peak = histogram.findPeakBin(); + if (peak < 0) { + break; + } + const auto peakSupport = histogram.getPeakSupport(peak); + if (peakSupport < 2.f) { + break; + } + if (leadingPeakSupport <= 0.f) { + leadingPeakSupport = peakSupport; + } else if (peakSupport < std::max(2.f, MinRelativePeakSupport * leadingPeakSupport)) { + break; + } + auto localMembers = histogram.collectLocalMembers(peak, 0, 0); + if (localMembers.size() < 2) { + localMembers = histogram.collectLocalMembers(peak, settings.seedMemberRadiusTime, settings.seedMemberRadiusZ); + } + if (localMembers.size() < 2) { + histogram.suppressPeak(peak); + continue; + } + + VertexSeed seed(settings.memoryPool); + seed.vertex = {settings.beamX, settings.beamY, histogram.getPeakZCenter(peak)}; + seed.time = histogram.getPeakTimeInterval(peak); + seed.scale2 = InitialScale2; + + auto fitted = fitSeed(seed, localMembers, lineRefs, lines, settings.memoryPool, settings.pairCut2); + if (fitted.valid && fitted.contributors.size() >= 2) { + seeds.push_back(std::move(fitted)); + histogram.suppressPeakNeighborhood(peak); + } else { + histogram.suppressPeak(peak); + } + } + + return seeds; +} + +void assignLinesToSeeds(bounded_vector& seeds, + std::span members, + std::span lineRefs, + std::span lines, + const float pairCut2) +{ + for (auto& seed : seeds) { + seed.assigned.clear(); + } + + for (const auto lineRefIdx : members) { + const auto lineIdx = lineRefs[lineRefIdx].lineIndex; + const auto& line = lines[lineIdx]; + + int bestSeed = -1; + float bestScore = std::numeric_limits::max(); + size_t bestMult = 0; + float bestZResidual = std::numeric_limits::max(); + + for (int seedIdx = 0; seedIdx < static_cast(seeds.size()); ++seedIdx) { + const auto& seed = seeds[seedIdx]; + if (!seed.valid || seed.contributors.size() < 2) { + continue; + } + if (!line.mTime.isCompatible(seed.time)) { + continue; + } + + const auto distance2 = Line::getDistance2FromPoint(line, seed.vertex); + if (distance2 >= pairCut2) { + continue; + } + + const auto score = distance2 / std::max(seed.scale2, MinScale2); + const auto zResidual = o2::gpu::GPUCommonMath::Abs(lineRefs[lineRefIdx].zBeam - seed.vertex[2]); + const auto multiplicity = seed.contributors.size(); + + const auto betterScore = score + TieTolerance < bestScore; + const auto betterMultiplicity = o2::gpu::GPUCommonMath::Abs(score - bestScore) <= TieTolerance && multiplicity > bestMult; + const auto betterZ = o2::gpu::GPUCommonMath::Abs(score - bestScore) <= TieTolerance && + multiplicity == bestMult && zResidual + constants::Tolerance < bestZResidual; + if (betterScore || betterMultiplicity || betterZ) { + bestSeed = seedIdx; + bestScore = score; + bestMult = multiplicity; + bestZResidual = zResidual; + } + } + + if (bestSeed >= 0) { + seeds[bestSeed].assigned.push_back(lineRefIdx); + } + } +} + +ClusterLines materializeCluster(const VertexSeed& seed, + std::span lineRefs, + std::span lines, + const std::shared_ptr& mr) +{ + bounded_vector lineIndices{mr.get()}; + lineIndices.reserve(seed.contributors.size()); + for (const auto lineRefIdx : seed.contributors) { + lineIndices.push_back(lineRefs[lineRefIdx].lineIndex); + } + std::sort(lineIndices.begin(), lineIndices.end()); + lineIndices.erase(std::unique(lineIndices.begin(), lineIndices.end()), lineIndices.end()); + + if (lineIndices.size() < 2) { + return {}; + } + + return {std::span{lineIndices.data(), lineIndices.size()}, lines}; +} + +} // namespace + +bounded_vector buildClusters(std::span lines, const Settings& settings) +{ + bounded_vector clusters(settings.memoryPool.get()); + if (lines.size() < 2) { + return clusters; + } + + bounded_vector refs(settings.memoryPool.get()); + refs.reserve(lines.size()); + for (int lineIdx = 0; lineIdx < static_cast(lines.size()); ++lineIdx) { + LineRef ref(lines[lineIdx], lineIdx, settings.beamX, settings.beamY, settings.maxZ); + if (!ref.isDead()) { + refs.push_back(ref); + } + } + + if (refs.size() < 2) { + return clusters; + } + + const auto coarseClusters = buildCoarseClusters(refs, lines, settings); + + for (const auto& members : coarseClusters) { + auto seeds = buildSeeds(members, refs, lines, settings); + if (seeds.empty()) { + continue; + } + + for (auto& seed : seeds) { + if (!seed.isUsableSeed()) { + seed.valid = false; + continue; + } + auto contributors = collectCompatibleContributors(seed, members, refs, lines, settings.memoryPool, settings.pairCut2); + if (contributors.size() < 2) { + seed.valid = false; + continue; + } + seed.contributors = std::move(contributors); + } + compactSeeds(seeds); + if (seeds.empty()) { + continue; + } + deduplicateSeeds(seeds, settings); + if (seeds.empty()) { + continue; + } + assignLinesToSeeds(seeds, members, refs, lines, settings.pairCut2); + for (auto& seed : seeds) { + if (seed.assigned.size() < 2) { + seed.valid = false; + continue; + } + seed = fitSeed(seed, seed.assigned, refs, lines, settings.memoryPool, settings.pairCut2); + if (!seed.isUsableSeed()) { + seed.valid = false; + continue; + } + } + compactSeeds(seeds); + deduplicateRefittedSeeds(seeds, settings); + for (auto& refit : seeds) { + auto cluster = materializeCluster(refit, refs, lines, settings.memoryPool); + if (cluster.getSize() < 2) { + continue; + } + if (!cluster.isValid()) { + continue; + } + clusters.push_back(std::move(cluster)); + } + } + + return clusters; +} + +} // namespace o2::its::line_vertexer diff --git a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx index 5a32b3d3b1a95..5b412ea4eea69 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TimeFrame.cxx @@ -14,19 +14,16 @@ /// #include -#include #include "Framework/Logger.h" #include "ITStracking/TimeFrame.h" #include "ITStracking/MathUtils.h" -#include "DataFormatsITSMFT/Cluster.h" #include "DataFormatsITSMFT/CompCluster.h" #include "DataFormatsITSMFT/ROFRecord.h" #include "DataFormatsITSMFT/TopologyDictionary.h" #include "ITSBase/GeometryTGeo.h" #include "ITSMFTBase/SegmentationAlpide.h" #include "ITStracking/BoundedAllocator.h" -#include "ITStracking/TrackingConfigParam.h" namespace { @@ -271,7 +268,7 @@ void TimeFrame::initialise(const int iteration, const TrackingParameter for (unsigned int iLayer{0}; iLayer < std::min((int)mClusters.size(), maxLayers); ++iLayer) { 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]); + mPositionResolution[iLayer] = o2::gpu::CAMath::Sqrt((0.5f * (trkParam.SystErrorZ2[iLayer] + trkParam.SystErrorY2[iLayer])) + (trkParam.LayerResolution[iLayer] * trkParam.LayerResolution[iLayer])); } clearResizeBoundedVector(mLines, getNrof(1), mMemoryPool.get()); clearResizeBoundedVector(mTrackletClusters, getNrof(1), mMemoryPool.get()); @@ -312,17 +309,17 @@ void TimeFrame::initialise(const int iteration, const TrackingParameter 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]); - mPositionResolution[iLayer] = o2::gpu::CAMath::Sqrt(0.5f * (trkParam.SystErrorZ2[iLayer] + trkParam.SystErrorY2[iLayer]) + trkParam.LayerResolution[iLayer] * trkParam.LayerResolution[iLayer]); + mPositionResolution[iLayer] = o2::gpu::CAMath::Sqrt((0.5f * (trkParam.SystErrorZ2[iLayer] + trkParam.SystErrorY2[iLayer])) + (trkParam.LayerResolution[iLayer] * trkParam.LayerResolution[iLayer])); 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; + 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))); + 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); } diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx index a41560e2e9e9a..eb0841888b03e 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx @@ -10,6 +10,8 @@ // or submit itself to any jurisdiction. #include +#include +#include #include #include @@ -128,12 +130,12 @@ void ITSTrackingInterface::run(framework::ProcessingContext& pc) gsl::span physTriggers; std::vector fromTRD; if (mUseTriggers == 2) { // use TRD triggers - o2::InteractionRecord ir{0, tfInfo.firstTForbit}; + o2::InteractionRecord irFirstTF{0, tfInfo.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 = ir, .data = 0}); + if (trig.getBCData() >= irFirstTF && trig.getNumberOfTracklets()) { + irFirstTF = trig.getBCData(); + fromTRD.emplace_back(o2::itsmft::PhysTrigger{.ir = irFirstTF, .data = 0}); } } physTriggers = gsl::span(fromTRD.data(), fromTRD.size()); @@ -215,7 +217,8 @@ void ITSTrackingInterface::run(framework::ProcessingContext& pc) auto vtxSpan = mTimeFrame->getPrimaryVertices(clockLayerId, iRof); if (o2::its::TrackerParamConfig::Instance().doUPCIteration) { if (!vtxSpan.empty()) { - if (vtxSpan[0].isFlagSet(Vertex::UPCMode) == 1) { // at least one vertex in this ROF and it is from second vertex iteration + bool hasUPC = std::any_of(vtxSpan.begin(), vtxSpan.end(), [](const auto& v) { return v.isFlagSet(Vertex::UPCMode); }); + if (hasUPC) { // at least one vertex in this ROF and it is from second vertex iteration LOGP(debug, "ROF {} rejected as vertices are from the UPC iteration", iRof); processUPCMask.selectROF({clockTiming.getROFStartInBC(iRof), clockTiming.getROFEndInBC(iRof)}); vtxROF.setFlag(o2::itsmft::ROFRecord::VtxUPCMode); diff --git a/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx index 5e27e20b3ddee..a22d2d6c60990 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.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. // @@ -13,6 +13,8 @@ #include #include #include +#include +#include #include #include @@ -22,6 +24,7 @@ #include "ITStracking/BoundedAllocator.h" #include "ITStracking/ClusterLines.h" #include "ITStracking/Definitions.h" +#include "ITStracking/LineVertexerHelpers.h" #include "ITStracking/Tracklet.h" #include "SimulationDataFormat/DigitizationContext.h" #include "SimulationDataFormat/O2DatabasePDG.h" @@ -31,13 +34,14 @@ namespace o2::its { - +namespace +{ template -static void trackleterKernelHost( +void trackleterKernelHost( const gsl::span& clustersNextLayer, // 0 2 const gsl::span& clustersCurrentLayer, // 1 1 const gsl::span& usedClustersNextLayer, // 0 2 - int* indexTableNext, + const int* indexTableNext, const float phiCut, bounded_vector& tracklets, gsl::span foundTracklets, @@ -94,7 +98,7 @@ static void trackleterKernelHost( } } -static void trackletSelectionKernelHost( +void trackletSelectionKernelHost( const Cluster* clusters0, // global layer 0 clusters const Cluster* clusters1, // global layer 1 clusters gsl::span usedClusters0, // global layer 0 used clusters @@ -145,6 +149,7 @@ static void trackletSelectionKernelHost( offset12 += foundTracklets12[iCurrentLayerClusterIndex]; } } +} // namespace template void VertexerTraits::updateVertexingParameters(const std::vector& vrtPar) @@ -255,7 +260,7 @@ void VertexerTraits::computeTracklets(const int iteration) }); }); - /// Create tracklets labels for L0-L1, information is as flat as in tracklets vector (no rofId) + /// Create flat L0-L1 tracklet labels (no rofId) if (mTimeFrame->hasMCinformation()) { for (const auto& trk : mTimeFrame->getTracklets()[0]) { o2::MCCompLabel label; @@ -309,15 +314,7 @@ void VertexerTraits::computeTrackletMatching(const int iteration) static_cast(mTimeFrame->getClustersOnLayer(pivotRofId, 1).size()), mVrtParams[iteration].tanLambdaCut, mVrtParams[iteration].phiCut); - auto& lines = mTimeFrame->getLines(pivotRofId); - totalLines.local() += lines.size(); - std::stable_sort(lines.begin(), lines.end(), [](const Line& a, const Line& b) { - // sort by lower edge and secondly prefer wider windows - if (a.mTime.lower() != b.mTime.lower()) { - return a.mTime.lower() < b.mTime.lower(); - } - return a.mTime.upper() > b.mTime.upper(); - }); + totalLines.local() += mTimeFrame->getLines(pivotRofId).size(); } }); mTimeFrame->setNLinesTotal(totalLines.combine(std::plus())); @@ -330,125 +327,214 @@ void VertexerTraits::computeTrackletMatching(const int iteration) template void VertexerTraits::computeVertices(const int iteration) { - const auto nsigmaCut{std::min(mVrtParams[iteration].vertNsigmaCut * mVrtParams[iteration].vertNsigmaCut * (mVrtParams[iteration].vertRadiusSigma * mVrtParams[iteration].vertRadiusSigma + mVrtParams[iteration].trackletSigma * mVrtParams[iteration].trackletSigma), 1.98f)}; - const auto pairCut2{mVrtParams[iteration].pairCut * mVrtParams[iteration].pairCut}; const int nRofs = mTimeFrame->getNrof(1); - const bool hasMC = mTimeFrame->hasMCinformation(); std::vector> rofVertices(nRofs); std::vector> rofLabels(nRofs); + const float nsigmaCut = std::min(mVrtParams[iteration].vertNsigmaCut * mVrtParams[iteration].vertNsigmaCut * (mVrtParams[iteration].vertRadiusSigma * mVrtParams[iteration].vertRadiusSigma + mVrtParams[iteration].trackletSigma * mVrtParams[iteration].trackletSigma), 1.98f); + const float pairCut2 = mVrtParams[iteration].pairCut * mVrtParams[iteration].pairCut; + const float duplicateZCut = mVrtParams[iteration].duplicateZCut > 0.f ? mVrtParams[iteration].duplicateZCut : std::max(4.f * mVrtParams[iteration].pairCut, 0.5f * mVrtParams[iteration].clusterCut); + const float duplicateDistance2Cut = mVrtParams[iteration].duplicateDistance2Cut > 0.f ? mVrtParams[iteration].duplicateDistance2Cut : std::max(16.f * pairCut2, 0.0625f * mVrtParams[iteration].clusterCut * mVrtParams[iteration].clusterCut); + line_vertexer::Settings settings; + settings.beamX = mTimeFrame->getBeamX(); + settings.beamY = mTimeFrame->getBeamY(); + settings.pairCut = mVrtParams[iteration].pairCut; + settings.pairCut2 = pairCut2; + settings.clusterCut = mVrtParams[iteration].clusterCut; + settings.coarseZWindow = mVrtParams[iteration].coarseZWindow; + settings.seedDedupZCut = mVrtParams[iteration].seedDedupZCut; + settings.refitDedupZCut = mVrtParams[iteration].refitDedupZCut; + settings.duplicateZCut = duplicateZCut; + settings.duplicateDistance2Cut = duplicateDistance2Cut; + settings.finalSelectionZCut = mVrtParams[iteration].finalSelectionZCut; + settings.maxZ = mVrtParams[iteration].maxZPositionAllowed; + settings.seedMemberRadiusTime = mVrtParams[iteration].seedMemberRadiusTime; + settings.seedMemberRadiusZ = mVrtParams[iteration].seedMemberRadiusZ; + settings.memoryPool = mMemoryPool; const auto processROF = [&](const int rofId) { auto& lines = mTimeFrame->getLines(rofId); - const int nLines{static_cast(lines.size())}; - bounded_vector usedTracklets(nLines, 0, mMemoryPool.get()); - auto& clusters = mTimeFrame->getTrackletClusters(rofId); - - for (int iLine1{0}; iLine1 < nLines; ++iLine1) { - if (usedTracklets[iLine1]) { - continue; + auto clusters = line_vertexer::buildClusters(std::span{lines.data(), lines.size()}, settings); + deepVectorClear(lines); // not needed after + auto clusterBeamDistance2 = [&](const ClusterLines& cluster) { + return (mTimeFrame->getBeamX() - cluster.getVertex()[0]) * (mTimeFrame->getBeamX() - cluster.getVertex()[0]) + + (mTimeFrame->getBeamY() - cluster.getVertex()[1]) * (mTimeFrame->getBeamY() - cluster.getVertex()[1]); + }; + auto clusterBetter = [&](const ClusterLines& lhs, const ClusterLines& rhs) { + if (lhs.getSize() != rhs.getSize()) { + return lhs.getSize() > rhs.getSize(); } - const auto& line1 = lines[iLine1]; - for (int iLine2{iLine1 + 1}; iLine2 < nLines; ++iLine2) { - if (usedTracklets[iLine2]) { - continue; - } - const auto& line2 = lines[iLine2]; - if (!line1.mTime.isCompatible(line2.mTime)) { + if (o2::gpu::GPUCommonMath::Abs(lhs.getAvgDistance2() - rhs.getAvgDistance2()) > constants::Tolerance) { + return lhs.getAvgDistance2() < rhs.getAvgDistance2(); + } + const auto lhsBeam = clusterBeamDistance2(lhs); + const auto rhsBeam = clusterBeamDistance2(rhs); + if (o2::gpu::GPUCommonMath::Abs(lhsBeam - rhsBeam) > constants::Tolerance) { + return lhsBeam < rhsBeam; + } + return lhs.getVertex()[2] < rhs.getVertex()[2]; + }; + + // Cluster deduplication by local non-maximum suppression in time/space + std::sort(clusters.begin(), clusters.end(), clusterBetter); + float minClusterZ = std::numeric_limits::max(); + for (const auto& cluster : clusters) { + minClusterZ = std::min(minClusterZ, cluster.getVertex()[2]); + } + bounded_vector deduplicated(mMemoryPool.get()); + deduplicated.reserve(clusters.size()); + std::unordered_map> keptByZBin; + for (auto& candidate : clusters) { + bool duplicate = false; + const auto candidateZ = candidate.getVertex()[2]; + const auto zBin = static_cast(std::floor((candidateZ - minClusterZ) / settings.duplicateZCut)); + for (int neighborBin = zBin - 1; neighborBin <= zBin + 1 && !duplicate; ++neighborBin) { + const auto found = keptByZBin.find(neighborBin); + if (found == keptByZBin.end()) { continue; } - auto dca2{Line::getDCA2(line1, line2)}; - if (dca2 < pairCut2) { - auto& cluster = clusters.emplace_back(iLine1, line1, iLine2, line2); - if (!cluster.isValid() || cluster.getR2() > 4.f) { - clusters.pop_back(); + for (const auto ownerId : found->second) { + const auto& owner = deduplicated[ownerId]; + if (!candidate.getTimeStamp().isCompatible(owner.getTimeStamp())) { continue; } - - usedTracklets[iLine1] = 1; - usedTracklets[iLine2] = 1; - for (int iLine3{0}; iLine3 < nLines; ++iLine3) { - if (usedTracklets[iLine3]) { - continue; - } - const auto& line3 = lines[iLine3]; - if (!line3.mTime.isCompatible(cluster.getTimeStamp())) { - continue; - } - const auto distance2 = Line::getDistance2FromPoint(line3, cluster.getVertex()); - if (distance2 < pairCut2) { - cluster.add(iLine3, line3); - usedTracklets[iLine3] = 1; - } + if (o2::gpu::GPUCommonMath::Abs(candidate.getVertex()[2] - owner.getVertex()[2]) >= settings.duplicateZCut) { + continue; + } + const auto dx = candidate.getVertex()[0] - owner.getVertex()[0]; + const auto dy = candidate.getVertex()[1] - owner.getVertex()[1]; + const auto dz = candidate.getVertex()[2] - owner.getVertex()[2]; + const auto distance2 = math_utils::SqSum(dx, dy, dz); + if (distance2 < settings.duplicateDistance2Cut) { + duplicate = true; + break; } - break; } } - } + if (duplicate) { + continue; + } - // Cluster merging - std::sort(clusters.begin(), clusters.end(), - [](ClusterLines& cluster1, ClusterLines& cluster2) { return cluster1.getSize() > cluster2.getSize(); }); + const auto ownerId = static_cast(deduplicated.size()); + keptByZBin[zBin].push_back(ownerId); + deduplicated.push_back(std::move(candidate)); + } + clusters = std::move(deduplicated); int nClusters = static_cast(clusters.size()); - for (int iCluster1{0}; iCluster1 < nClusters; ++iCluster1) { - std::array vertex1{clusters[iCluster1].getVertex()}; - std::array vertex2{}; - for (int iCluster2{iCluster1 + 1}; iCluster2 < nClusters; ++iCluster2) { - if (clusters[iCluster1].getTimeStamp().isCompatible(clusters[iCluster2].getTimeStamp())) { - vertex2 = clusters[iCluster2].getVertex(); - if (o2::gpu::GPUCommonMath::Abs(vertex1[2] - vertex2[2]) < mVrtParams[iteration].clusterCut) { - float distance{((vertex1[0] - vertex2[0]) * (vertex1[0] - vertex2[0])) + - ((vertex1[1] - vertex2[1]) * (vertex1[1] - vertex2[1])) + - ((vertex1[2] - vertex2[2]) * (vertex1[2] - vertex2[2]))}; - if (distance < mVrtParams[iteration].pairCut * mVrtParams[iteration].pairCut) { - for (auto label : clusters[iCluster2].getLabels()) { - clusters[iCluster1].add(label, lines[label]); - vertex1 = clusters[iCluster1].getVertex(); - } - clusters.erase(clusters.begin() + iCluster2); - --iCluster2; - --nClusters; - } - } - } + + // Vertex filtering with score-based local NMS + std::sort(clusters.begin(), clusters.end(), clusterBetter); + std::vector candidateIndices; + candidateIndices.reserve(nClusters); + for (int iCluster{0}; iCluster < nClusters; ++iCluster) { + const bool zCompatible = o2::gpu::GPUCommonMath::Abs(clusters[iCluster].getVertex()[2]) < mVrtParams[iteration].maxZPositionAllowed; + + if (zCompatible) { + candidateIndices.push_back(iCluster); } } - // Vertex filtering - std::sort(clusters.begin(), clusters.end(), - [](const ClusterLines& cluster1, const ClusterLines& cluster2) { return cluster1.getSize() > cluster2.getSize(); }); - bool atLeastOneFound{false}; - for (int iCluster{0}; iCluster < nClusters; ++iCluster) { - bool lowMultCandidate{false}; - double beamDistance2{(mTimeFrame->getBeamX() - clusters[iCluster].getVertex()[0]) * (mTimeFrame->getBeamX() - clusters[iCluster].getVertex()[0]) + - (mTimeFrame->getBeamY() - clusters[iCluster].getVertex()[1]) * (mTimeFrame->getBeamY() - clusters[iCluster].getVertex()[1])}; - if (atLeastOneFound && (lowMultCandidate = clusters[iCluster].getSize() < mVrtParams[iteration].clusterContributorsCut)) { - lowMultCandidate &= (beamDistance2 < mVrtParams[iteration].lowMultBeamDistCut * mVrtParams[iteration].lowMultBeamDistCut); - if (!lowMultCandidate) { - clusters.erase(clusters.begin() + iCluster); - nClusters--; - continue; + if (candidateIndices.empty()) { + return; + } + + auto countSharedLabels = [](const ClusterLines& lhs, const ClusterLines& rhs) { + size_t shared = 0; + auto lhsIt = lhs.getLabels().begin(); + auto rhsIt = rhs.getLabels().begin(); + while (lhsIt != lhs.getLabels().end() && rhsIt != rhs.getLabels().end()) { + if (*lhsIt == *rhsIt) { + ++shared; + ++lhsIt; + ++rhsIt; + } else if (*lhsIt < *rhsIt) { + ++lhsIt; + } else { + ++rhsIt; } } + return shared; + }; - if (beamDistance2 < nsigmaCut && o2::gpu::GPUCommonMath::Abs(clusters[iCluster].getVertex()[2]) < mVrtParams[iteration].maxZPositionAllowed) { - atLeastOneFound = true; - Vertex vertex{clusters[iCluster].getVertex().data(), - clusters[iCluster].getRMS2(), - (ushort)clusters[iCluster].getSize(), - clusters[iCluster].getAvgDistance2()}; - - if (iteration) { - vertex.setFlags(Vertex::UPCMode); + float minCandidateZ = std::numeric_limits::max(); + for (const auto clusterId : candidateIndices) { + minCandidateZ = std::min(minCandidateZ, clusters[clusterId].getVertex()[2]); + } + std::unordered_map> selectedByZBin; + std::vector selectedIndices; + selectedIndices.reserve(candidateIndices.size()); + for (const auto clusterId : candidateIndices) { + const auto& candidate = clusters[clusterId]; + const auto candidateZ = candidate.getVertex()[2]; + const auto zBin = static_cast((candidateZ - minCandidateZ) / settings.finalSelectionZCut); + bool suppressed = false; + for (int neighborBin = zBin - 1; neighborBin <= zBin + 1 && !suppressed; ++neighborBin) { + const auto found = selectedByZBin.find(neighborBin); + if (found == selectedByZBin.end()) { + continue; } - vertex.setTimeStamp(clusters[iCluster].getTimeStamp()); - rofVertices[rofId].push_back(vertex); - if (hasMC) { - bounded_vector labels(mMemoryPool.get()); - for (auto& index : clusters[iCluster].getLabels()) { - labels.push_back(mTimeFrame->getLinesLabel(rofId)[index]); + for (const auto selectedId : found->second) { + const auto& selected = clusters[selectedId]; + if (!candidate.getTimeStamp().isCompatible(selected.getTimeStamp())) { + continue; } - rofLabels[rofId].push_back(computeMain(labels)); + const auto zDelta = o2::gpu::GPUCommonMath::Abs(candidateZ - selected.getVertex()[2]); + const auto sharedLabels = countSharedLabels(candidate, selected); + const auto minSize = std::min(candidate.getSize(), selected.getSize()); + const bool overlapDuplicate = sharedLabels > 0 && sharedLabels * 4 >= minSize; + const bool strongZDuplicate = zDelta < settings.finalSelectionZCut; + const bool clearlyBetterMultiplicity = selected.getSize() >= candidate.getSize() + 3; + const bool clearlyBetterQuality = selected.getSize() > candidate.getSize() && + selected.getAvgDistance2() + constants::Tolerance < 0.8f * candidate.getAvgDistance2(); + const bool weakCandidate = clearlyBetterMultiplicity || clearlyBetterQuality; + if (overlapDuplicate || (strongZDuplicate && weakCandidate)) { + suppressed = true; + break; + } + } + } + if (suppressed) { + continue; + } + selectedByZBin[zBin].push_back(clusterId); + selectedIndices.push_back(clusterId); + } + + // sort vertices by their multiplicity to opt. suppress lower mult. debris + std::vector sortedIndices(selectedIndices.size()); + std::iota(sortedIndices.begin(), sortedIndices.end(), 0); + std::sort(sortedIndices.begin(), sortedIndices.end(), [&selectedIndices, &clusters](int i, int j) { + return clusters[selectedIndices[i]].getSize() > clusters[selectedIndices[j]].getSize(); + }); + for (const auto sortedId : sortedIndices) { + const auto& cluster = clusters[selectedIndices[sortedId]]; + const auto beamDistance2 = clusterBeamDistance2(cluster); + if (!(beamDistance2 < nsigmaCut)) { + continue; + } + if (cluster.getSize() < mVrtParams[iteration].clusterContributorsCut) { + continue; + } + if (!rofVertices[rofId].empty() && cluster.getSize() < mVrtParams[iteration].suppressLowMultDebris) { + continue; + } + + Vertex vertex{cluster.getVertex().data(), + cluster.getRMS2(), + (ushort)cluster.getSize(), + cluster.getAvgDistance2()}; + if (iteration) { + vertex.setFlags(Vertex::UPCMode); + } + vertex.setTimeStamp(cluster.getTimeStamp()); + rofVertices[rofId].push_back(vertex); + if (mTimeFrame->hasMCinformation()) { + auto& lineLabels = mTimeFrame->getLinesLabel(rofId); + bounded_vector labels(mMemoryPool.get()); + for (auto& index : cluster.getLabels()) { + labels.push_back(lineLabels[index]); } + const auto mainLabel = computeMain(labels); + rofLabels[rofId].push_back(mainLabel); } } }; @@ -469,7 +555,7 @@ void VertexerTraits::computeVertices(const int iteration) for (auto& vertex : rofVertices[rofId]) { mTimeFrame->addPrimaryVertex(vertex); } - if (hasMC) { + if (mTimeFrame->hasMCinformation()) { for (auto& label : rofLabels[rofId]) { mTimeFrame->addPrimaryVertexLabel(label); } @@ -504,7 +590,8 @@ void VertexerTraits::addTruthSeedingVertices() if (!trk.isPrimary() || trk.GetPt() < 0.05 || std::abs(trk.GetEta()) > 1.1) { return false; } - return o2::O2DatabasePDG::Instance()->GetParticle(trk.GetPdgCode())->Charge() != 0; + const auto* p = o2::O2DatabasePDG::Instance()->GetParticle(trk.GetPdgCode()); + return (!p) ? false : p->Charge() != 0; }))); vert.setXYZ((float)eve.GetX(), (float)eve.GetY(), (float)eve.GetZ()); vert.setChi2(1); // not used as constraint diff --git a/prodtests/full-system-test/dpl-workflow.sh b/prodtests/full-system-test/dpl-workflow.sh index 9f982513fdffd..d54c05ff0f20e 100755 --- a/prodtests/full-system-test/dpl-workflow.sh +++ b/prodtests/full-system-test/dpl-workflow.sh @@ -117,6 +117,11 @@ EVE_OPT=" --jsons-folder $EDJSONS_DIR" [[ "0$ITSSTAGGERED" == "01" ]] && ITS_STAGGERED=" --enable-its-staggering " || ITS_STAGGERED= [[ "0$MFTSTAGGERED" == "01" ]] && MFT_STAGGERED=" --enable-its-staggering " || MFT_STAGGERED= +# ITS vertexing settings +if [[ $BEAMTYPE == "pp" || $LIGHTNUCLEI == "1" ]]; then + ITS_CONFIG_KEY+=";ITSVertexerParam.pairCut=0.0317563;ITSVertexerParam.clusterCut=0.6640964;ITSVertexerParam.coarseZWindow=0.2049018;ITSVertexerParam.seedDedupZCut=0.0711793;ITSVertexerParam.refitDedupZCut=0.0680009;ITSVertexerParam.duplicateZCut=0.1582193;ITSVertexerParam.finalSelectionZCut=0.1081465;ITSVertexerParam.duplicateDistance2Cut=0.0117033;ITSVertexerParam.clusterContributorsCut=2;ITSVertexerParam.seedMemberRadiusZ=0;ITSVertexerParam.vertNsigmaCut=4.0;ITSVertexerParam.vertRadiusSigma=0.0452309;ITSVertexerParam.trackletSigma=0.0025941;ITSVertexerParam.suppressLowMultDebris=0;" +fi + if [[ $CTFINPUT != 1 ]]; then GPU_OUTPUT+=",tpc-triggers" fi @@ -126,10 +131,10 @@ if [[ $SYNCMODE == 1 ]]; then MFT_STF_DEC_CONFIG+="MFTClustererParam.maxBCDiffToMaskBias=-1;" [[ $BEAMTYPE == "PbPb" || $BEAMTYPE == "pp" || $LIGHTNUCLEI == "1" ]] && MFT_CONFIG_KEY+="MFTTracking.cutMultClusLow=0;MFTTracking.cutMultClusHigh=4000;" if [[ $BEAMTYPE == "PbPb" ]]; then - ITS_CONFIG_KEY+="fastMultConfig.cutMultClusLow=${CUT_MULT_MIN_ITS:-0};fastMultConfig.cutMultClusHigh=${CUT_MULT_MAX_ITS:-400};fastMultConfig.cutMultVtxHigh=${CUT_MULT_VTX_ITS:-20};" + ITS_CONFIG_KEY+=";fastMultConfig.cutMultClusLow=${CUT_MULT_MIN_ITS:-0};fastMultConfig.cutMultClusHigh=${CUT_MULT_MAX_ITS:-400};fastMultConfig.cutMultVtxHigh=${CUT_MULT_VTX_ITS:-20};" MCH_CONFIG_KEY="MCHTracking.maxCandidates=50000;MCHTracking.maxTrackingDuration=20;" elif [[ $BEAMTYPE == "pp" || $LIGHTNUCLEI == "1" ]]; then - ITS_CONFIG_KEY+="fastMultConfig.cutMultClusLow=${CUT_MULT_MIN_ITS:--1};fastMultConfig.cutMultClusHigh=${CUT_MULT_MAX_ITS:--1};fastMultConfig.cutMultVtxHigh=${CUT_MULT_VTX_ITS:--1};ITSVertexerParam.phiCut=0.5;ITSVertexerParam.clusterContributorsCut=3;ITSVertexerParam.tanLambdaCut=0.2;" + ITS_CONFIG_KEY+=";fastMultConfig.cutMultClusLow=${CUT_MULT_MIN_ITS:--1};fastMultConfig.cutMultClusHigh=${CUT_MULT_MAX_ITS:--1};fastMultConfig.cutMultVtxHigh=${CUT_MULT_VTX_ITS:--1};" MCH_CONFIG_KEY="MCHTracking.maxCandidates=20000;MCHTracking.maxTrackingDuration=10;" fi [[ -n ${CUT_RANDOM_FRACTION_ITS:-} ]] && ITS_CONFIG_KEY+="fastMultConfig.cutRandomFraction=$CUT_RANDOM_FRACTION_ITS;" @@ -157,11 +162,6 @@ if [[ $SYNCMODE == 1 ]]; then has_detector ITS && TRD_FILTER_CONFIG+=" --filter-trigrec" else has_detectors_gpu TPC ITS && ITS_CONFIG_KEY+="ITSCATrackerParam.trackingMode=1;" # sets ITS gpu reco to async - if [[ $BEAMTYPE == "pp" || $LIGHTNUCLEI == "1" ]]; then - ITS_CONFIG_KEY+="ITSVertexerParam.phiCut=0.5;ITSVertexerParam.clusterContributorsCut=3;ITSVertexerParam.tanLambdaCut=0.2;" - elif [[ $BEAMTYPE == "PbPb" ]]; then - ITS_CONFIG_KEY+="ITSVertexerParam.lowMultBeamDistCut=0;" - fi if [[ $IS_SIMULATED_DATA == 0 && $CTFINPUT == 1 ]]; then # Enable fixes to the MCH readout mapping for async processing of real data MCH_CONFIG_KEY+="MCHDigitModifier.updateST1=true;MCHDigitModifier.updateST2=true;" @@ -585,7 +585,7 @@ has_detector_gpu ITS && GPU_OUTPUT+=",its-tracks" # --------------------------------------------------------------------------------------------------------------------- # Common reconstruction workflows -(has_detector_reco ITS && ! has_detector_gpu ITS) && ! has_detector_from_global_reader ITS && add_W o2-its-reco-workflow "--trackerCA $ITS_CONFIG $ITS_STAGGERED $DISABLE_MC ${DISABLE_DIGIT_CLUSTER_INPUT:-} $DISABLE_ROOT_OUTPUT --pipeline $(get_N its-tracker ITS REST 1 ITSTRK),$(get_N its-clusterer ITS REST 1 ITSCL)" "$ITS_CONFIG_KEY;$ITSMFT_STROBES;$ITSEXTRAERR" +(has_detector_reco ITS && ! has_detector_gpu ITS) && ! has_detector_from_global_reader ITS && add_W o2-its-reco-workflow "$ITS_CONFIG $ITS_STAGGERED $DISABLE_MC ${DISABLE_DIGIT_CLUSTER_INPUT:-} $DISABLE_ROOT_OUTPUT --pipeline $(get_N its-tracker ITS REST 1 ITSTRK),$(get_N its-clusterer ITS REST 1 ITSCL)" "$ITS_CONFIG_KEY;$ITSMFT_STROBES;$ITSEXTRAERR" [[ ${DISABLE_DIGIT_CLUSTER_INPUT:-} =~ "--digits-from-upstream" ]] && has_detector_gpu ITS && ! has_detector_from_global_reader ITS && add_W o2-its-reco-workflow "--disable-tracking ${DISABLE_DIGIT_CLUSTER_INPUT:-} $ITS_STAGGERED $DISABLE_MC $DISABLE_ROOT_OUTPUT --pipeline $(get_N its-clusterer ITS REST 1 ITSCL)" "$ITS_CONFIG_KEY;$ITSMFT_STROBES;$ITSEXTRAERR" (has_detector_reco TPC || has_detector_ctf TPC) && ! has_detector_from_global_reader TPC && add_W o2-gpu-reco-workflow "--gpu-reconstruction \"$GPU_CONFIG_SELF\" --input-type=$GPU_INPUT $DISABLE_MC --output-type $GPU_OUTPUT $TPC_CORR_OPT $ITS_STAGGERED --pipeline gpu-reconstruction:${N_TPCTRK:-1},gpu-reconstruction-prepare:${N_TPCTRK:-1} $GPU_CONFIG" "GPU_global.deviceType=$GPUTYPE;GPU_proc.debugLevel=0;$GPU_CONFIG_KEY;$TRACKTUNETPCINNER;$TPC_CORR_KEY" (has_detector_reco TOF || has_detector_ctf TOF) && ! has_detector_from_global_reader TOF && add_W o2-tof-reco-workflow "$TOF_CONFIG --input-type $TOF_INPUT --output-type $TOF_OUTPUT $DISABLE_DIGIT_ROOT_INPUT $DISABLE_ROOT_OUTPUT $DISABLE_MC --pipeline $(get_N tof-compressed-decoder TOF RAW 1),$(get_N TOFClusterer TOF REST 1)" diff --git a/prodtests/full_system_test.sh b/prodtests/full_system_test.sh index 46739e76f103b..e89d8ee09dee9 100755 --- a/prodtests/full_system_test.sh +++ b/prodtests/full_system_test.sh @@ -188,7 +188,7 @@ taskwrapper digi.log o2-sim-digitizer-workflow -n $NEvents ${DIGIQED} ${NOMCLABE touch digiTRD.log_done if [[ "0$GENERATE_ITSMFT_DICTIONARIES" == "01" ]]; then - taskwrapper itsmftdict1.log o2-its-reco-workflow --trackerCA --disable-mc --configKeyValues '"fastMultConfig.cutMultClusLow=30000;fastMultConfig.cutMultClusHigh=2000000;fastMultConfig.cutMultVtxHigh=500;"' + taskwrapper itsmftdict1.log o2-its-reco-workflow --disable-mc --configKeyValues '"fastMultConfig.cutMultClusLow=30000;fastMultConfig.cutMultClusHigh=2000000;fastMultConfig.cutMultVtxHigh=500;"' cp ~/alice/O2/Detectors/ITSMFT/ITS/macros/test/CreateDictionaries.C . taskwrapper itsmftdict2.log root -b -q CreateDictionaries.C++ rm -f CreateDictionaries_C* CreateDictionaries.C diff --git a/prodtests/sim_challenge.sh b/prodtests/sim_challenge.sh index 8c7cfb1a024b0..f5bbf8ab74ff8 100755 --- a/prodtests/sim_challenge.sh +++ b/prodtests/sim_challenge.sh @@ -153,7 +153,7 @@ if [ "$doreco" == "1" ]; then echo "Return status of tpcreco: $?" echo "Running ITS reco flow" - taskwrapper itsreco.log o2-its-reco-workflow --trackerCA --tracking-mode async $gloOpt $ITSRecOpt + taskwrapper itsreco.log o2-its-reco-workflow --tracking-mode async $gloOpt $ITSRecOpt echo "Return status of itsreco: $?" # existing checks From c91f52ea802aee9e43fa68e1b7c597e0bfe45df6 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Thu, 16 Apr 2026 17:51:40 +0200 Subject: [PATCH 482/701] ITS: move all pp settings to dpl-workflow.sh Signed-off-by: Felix Schlepper --- .../ITS/tracking/include/ITStracking/TrackingConfigParam.h | 6 +++--- prodtests/full-system-test/dpl-workflow.sh | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h index cb291b46f5e44..5ffd55f715a1a 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h @@ -25,9 +25,9 @@ struct VertexerParamConfig : public o2::conf::ConfigurableParamHelper Date: Mon, 13 Apr 2026 16:34:19 +0200 Subject: [PATCH 483/701] Remove unnecessary dictionaries in ALICE3 --- .../ALICE3/TRK/reconstruction/CMakeLists.txt | 12 --------- .../src/TRKReconstructionLinkDef.h | 25 ------------------- 2 files changed, 37 deletions(-) delete mode 100644 Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt index 59a7f47955938..b8cb6a88f7163 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/CMakeLists.txt @@ -41,15 +41,3 @@ o2_add_library(TRKReconstruction if(Acts_FOUND) target_compile_definitions(${targetName} PUBLIC O2_WITH_ACTS) endif() - -set(dictHeaders include/TRKReconstruction/TimeFrame.h - include/TRKReconstruction/Clusterer.h) - -if(Acts_FOUND) - list(APPEND dictHeaders include/TRKReconstruction/ClustererACTS.h - include/TRKReconstruction/TrackerACTS.h) -endif() - -o2_target_root_dictionary(TRKReconstruction - HEADERS ${dictHeaders} - LINKDEF src/TRKReconstructionLinkDef.h) diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.h deleted file mode 100644 index 1f4c2193b91b1..0000000000000 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TRKReconstructionLinkDef.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. - -#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> + ; -#pragma link C++ class o2::trk::Clusterer + ; -#ifdef O2_WITH_ACTS -#pragma link C++ class o2::trk::ClustererACTS + ; - -#endif - -#endif From b083788580acd997aaced935f2f107daf0917182 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Nicol=C3=B2=20Jacazio?= Date: Fri, 17 Apr 2026 08:43:36 +0200 Subject: [PATCH 484/701] [ALICE3] TRK: Fix getNrof calls to use index in TrackerACTS (#15277) --- .../Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx index 67dcfe25e33bb..732a0acc14b66 100644 --- a/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/reconstruction/src/TrackerACTS.cxx @@ -261,10 +261,10 @@ void TrackerACTS::clustersToTracks() double totalTime = 0.; LOG(info) << "==== TRK ACTS Tracking ===="; - LOG(info) << "Processing " << mTimeFrame->getNrof() << " ROFs with B = " << mBz << " T"; + LOG(info) << "Processing " << mTimeFrame->getNrof(0) << " ROFs with B = " << mBz << " T"; // Process each ROF - for (int iROF = 0; iROF < mTimeFrame->getNrof(); ++iROF) { + for (int iROF = 0; iROF < mTimeFrame->getNrof(0); ++iROF) { LOG(info) << "Processing ROF " << iROF; // Build space points mCurState = SpacePointBuilding; From b50e6f2d05366106888c1f6918d9eb1be6496a4a Mon Sep 17 00:00:00 2001 From: Stefano Cannito <143754257+scannito@users.noreply.github.com> Date: Fri, 17 Apr 2026 08:53:21 +0200 Subject: [PATCH 485/701] [ALICE3] TRK: update geometry, fix in extrusions, cleanup (#15262) * Update TRK * For L6 the nominal radius corresponds to the outer one * Removed unused variable * Fix extrusions for OT layers * Minor * Negative staggering for L6 --- .../include/TRKSimulation/TRKLayer.h | 35 ++- .../ALICE3/TRK/simulation/src/Detector.cxx | 90 ++++++-- .../ALICE3/TRK/simulation/src/TRKLayer.cxx | 217 +++++++++++++++--- 3 files changed, 282 insertions(+), 60 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h index 6077d9e5f9839..ef4d5657a1b4f 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/include/TRKSimulation/TRKLayer.h @@ -12,11 +12,14 @@ #ifndef ALICEO2_TRK_LAYER_H #define ALICEO2_TRK_LAYER_H +#include "TRKBase/Specs.h" +#include "TRKBase/TRKBaseParam.h" #include + #include -#include "TRKBase/TRKBaseParam.h" -#include "TRKBase/Specs.h" +#include +#include namespace o2 { @@ -68,7 +71,7 @@ class TRKSegmentedLayer : public TRKCylindricalLayer { public: TRKSegmentedLayer() = default; - TRKSegmentedLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode); + TRKSegmentedLayer(int layerNumber, std::string layerName, float rInn, float tiltAngle, int numberOfStaves, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode); ~TRKSegmentedLayer() override = default; TGeoVolume* createSensor() override; @@ -80,7 +83,10 @@ class TRKSegmentedLayer : public TRKCylindricalLayer void createLayer(TGeoVolume* motherVolume) override = 0; protected: + float mTiltAngle; int mNumberOfModules; + int mNumberOfStaves; + bool mIsFlipped = false; // Fixed parameters for the layer, to be set based on the specifications of the chip and module static constexpr double sChipWidth = constants::moduleMLOT::chip::width; @@ -93,6 +99,12 @@ class TRKSegmentedLayer : public TRKCylindricalLayer // TGeo objects outside logical volumes can cause errors static constexpr float sLogicalVolumeThickness = 1.3; + // For the segmented layers, because of tilting and staggering the bounding radii can be different + // from the inner radius and inner radius + thickness. + // This function calculates the bounding radii based on the geometry of the stave and the tilt angle, + // to ensure that the layer volume is large enough to contain all the staves without overlaps. + virtual std::pair getBoundingRadii(double staveWidth) const; + ClassDefOverride(TRKSegmentedLayer, 0); }; @@ -100,14 +112,20 @@ class TRKMLLayer : public TRKSegmentedLayer { public: TRKMLLayer() = default; - TRKMLLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode); + TRKMLLayer(int layerNumber, std::string layerName, float rInn, float staggerOffset, float tiltAngle, int numberOfStaves, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode); ~TRKMLLayer() override = default; TGeoVolume* createStave() override; void createLayer(TGeoVolume* motherVolume) override; private: + float mStaggerOffset; + static constexpr double sStaveWidth = constants::ML::width; + static constexpr int sFlippedLayerNumber = 3; + + // Override to account for the staggering offset present in specific ML layers + std::pair getBoundingRadii(double staveWidth) const override; ClassDefOverride(TRKMLLayer, 0); }; @@ -116,7 +134,7 @@ class TRKOTLayer : public TRKSegmentedLayer { public: TRKOTLayer() = default; - TRKOTLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode); + TRKOTLayer(int layerNumber, std::string layerName, float rInn, float tiltAngle, int numberOfStaves, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode); ~TRKOTLayer() override = default; TGeoVolume* createStave() override; @@ -128,9 +146,12 @@ class TRKOTLayer : public TRKSegmentedLayer static constexpr double sInStaveOverlap = constants::moduleMLOT::gaps::outerEdgeLongSide + constants::moduleMLOT::chip::passiveEdgeReadOut + 0.1; // 1.5mm outer-edge + 1mm deadzone + 1mm (true) overlap static constexpr double sStaveWidth = constants::OT::width - sInStaveOverlap; - ClassDefOverride(TRKOTLayer, 0) + // Override to account for the staggering offset present in OT layers + std::pair getBoundingRadii(double staveWidth) const override; + + ClassDefOverride(TRKOTLayer, 0); }; } // namespace trk } // namespace o2 -#endif // ALICEO2_TRK_LAYER_H \ No newline at end of file +#endif // ALICEO2_TRK_LAYER_H diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx index 66c02a080e0b6..66ace4746d399 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/Detector.cxx @@ -9,18 +9,19 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include - -#include -#include -#include +#include "TRKSimulation/Detector.h" #include "DetectorsBase/Stack.h" -#include "TRKSimulation/Hit.h" -#include "TRKSimulation/Detector.h" + #include "TRKBase/TRKBaseParam.h" +#include "TRKSimulation/Hit.h" #include "TRKSimulation/VDGeometryBuilder.h" #include "TRKSimulation/VDSensorRegistry.h" +#include +#include +#include + +#include #include #include @@ -105,14 +106,21 @@ void Detector::configMLOT() break; } case kSegmented: { + const std::vector tiltAngles{11.2f, 11.9f, 11.4f, 0.f, 0.f, 0.f, 0.f, 0.f}; + // const std::vector tiltAngles{10.f, 16.1f, 19.2f, 0.f, 0.f, 0.f, 0.f, 0.f}; + const std::vector nStaves{10, 14, 18, 26, 38, 32, 42, 56}; + // const std::vector nStaves{10, 16, 22, 26, 38, 32, 42, 56}; const std::vector nMods{10, 10, 10, 10, 10, 20, 20, 20}; + + const std::vector stagOffsets{0.f, 0.f, 0.f, 1.17f, 0.89f}; + LOGP(warning, "Loading segmented configuration for ALICE3 TRK"); for (int i{0}; i < 8; ++i) { std::string name = GeometryTGeo::getTRKLayerPattern() + std::to_string(i); - if (i < 4) { - mLayers.push_back(std::make_unique(i, name, rInn[i], nMods[i], thick, MatBudgetParamMode::Thickness)); + if (i < 5) { + mLayers.push_back(std::make_unique(i, name, rInn[i], stagOffsets[i], tiltAngles[i], nStaves[i], nMods[i], thick, MatBudgetParamMode::Thickness)); } else { - mLayers.push_back(std::make_unique(i, name, rInn[i], nMods[i], thick, MatBudgetParamMode::Thickness)); + mLayers.push_back(std::make_unique(i, name, rInn[i], tiltAngles[i], nStaves[i], nMods[i], thick, MatBudgetParamMode::Thickness)); } } break; @@ -153,16 +161,66 @@ void Detector::configFromFile(std::string fileName) } std::string name = GeometryTGeo::getTRKLayerPattern() + std::to_string(layerCount); + switch (trkPars.layoutMLOT) { - case kCylindrical: - mLayers.push_back(std::make_unique(layerCount, name, tmpBuff[0], tmpBuff[1], tmpBuff[2], MatBudgetParamMode::Thickness)); + case kCylindrical: { + // Cylindrical requires at least 3 parameters + if (tmpBuff.size() < 3) { + LOGP(fatal, "Invalid configuration for cylindrical layer {}: insufficient parameters.", layerCount); + } + + // Default mode is Thickness + MatBudgetParamMode mode = MatBudgetParamMode::Thickness; + if (tmpBuff.size() >= 4) { + mode = static_cast(static_cast(tmpBuff[3])); + } + + mLayers.push_back(std::make_unique(layerCount, name, tmpBuff[0], tmpBuff[1], tmpBuff[2], mode)); break; + } case kSegmented: { - int nMods = static_cast(tmpBuff[1]); - if (layerCount < 4) { - mLayers.push_back(std::make_unique(layerCount, name, tmpBuff[0], nMods, tmpBuff[2], MatBudgetParamMode::Thickness)); + // Expected column mapping in the text file (separated by \t): + // tmpBuff[0] = rInn + // tmpBuff[1] = thick + // tmpBuff[2] = tiltAngle + // tmpBuff[3] = nStaves + // tmpBuff[4] = nMods + // tmpBuff[5] = stagOffset (required ONLY for ML) + // tmpBuff[6] = matBudgetMode (optional, default = Thickness) + + // Base parameters for all segmented layers (at least 5 needed) + if (tmpBuff.size() < 5) { + LOGP(fatal, "Invalid configuration for segmented layer {}: missing base parameters.", layerCount); + } + + float rInn = tmpBuff[0]; + float thick = tmpBuff[1]; + float tiltAngle = tmpBuff[2]; + int nStaves = static_cast(tmpBuff[3]); + int nMods = static_cast(tmpBuff[4]); + + // Default mode is Thickness + MatBudgetParamMode mode = MatBudgetParamMode::Thickness; + + if (layerCount < 5) { + // ML layers (0 to 4) require stagOffset (index 5) + if (tmpBuff.size() < 6) { + LOGP(fatal, "Invalid configuration for ML layer {}: stagOffset is missing.", layerCount); + } + float stagOffset = tmpBuff[5]; + + if (tmpBuff.size() >= 7) { + mode = static_cast(static_cast(tmpBuff[6])); + } + + mLayers.push_back(std::make_unique(layerCount, name, rInn, stagOffset, tiltAngle, nStaves, nMods, thick, mode)); } else { - mLayers.push_back(std::make_unique(layerCount, name, tmpBuff[0], nMods, tmpBuff[2], MatBudgetParamMode::Thickness)); + // OT layers (5+) do NOT have stagOffset. The optional mode is at index 5. + if (tmpBuff.size() >= 6) { + mode = static_cast(static_cast(tmpBuff[5])); + } + + mLayers.push_back(std::make_unique(layerCount, name, rInn, tiltAngle, nStaves, nMods, thick, mode)); } break; } diff --git a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx index 39c7b3598d19b..7a4b7bef34e03 100644 --- a/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx +++ b/Detectors/Upgrades/ALICE3/TRK/simulation/src/TRKLayer.cxx @@ -10,17 +10,21 @@ // or submit itself to any jurisdiction. #include "TRKSimulation/TRKLayer.h" -#include "TRKBase/GeometryTGeo.h" -#include "TRKBase/Specs.h" #include "Framework/Logger.h" -#include +#include "TRKBase/GeometryTGeo.h" +#include "TRKBase/Specs.h" #include +#include #include - #include +#include +#include +#include +#include + namespace o2 { namespace trk @@ -84,9 +88,10 @@ void TRKCylindricalLayer::createLayer(TGeoVolume* motherVolume) ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -TRKSegmentedLayer::TRKSegmentedLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode) - : TRKCylindricalLayer(layerNumber, layerName, rInn, numberOfModules * sModuleLength, thickOrX2X0, mode), mNumberOfModules(numberOfModules) +TRKSegmentedLayer::TRKSegmentedLayer(int layerNumber, std::string layerName, float rInn, float tiltAngle, int numberOfStaves, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode) + : TRKCylindricalLayer(layerNumber, layerName, rInn, numberOfModules * sModuleLength, thickOrX2X0, mode), mTiltAngle(tiltAngle), mNumberOfStaves(numberOfStaves), mNumberOfModules(numberOfModules) { + assert(numberOfStaves % 2 == 0 && "Error: numberOfStaves must be even!"); } TGeoVolume* TRKSegmentedLayer::createSensor() @@ -132,22 +137,29 @@ TGeoVolume* TRKSegmentedLayer::createChip() TGeoVolume* sensVol = createSensor(); TGeoCombiTrans* transSens = new TGeoCombiTrans(); - // transSens->SetTranslation(-sDeadzoneWidth / 2, -(mChipThickness - sSensorThickness) / 2, 0); - transSens->SetTranslation(-sDeadzoneWidth / 2, (mChipThickness - sSensorThickness) / 2, 0); - LOGP(debug, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); - chipVol->AddNode(sensVol, 1, transSens); TGeoVolume* deadVol = createDeadzone(); TGeoCombiTrans* transDead = new TGeoCombiTrans(); - // transDead->SetTranslation((sChipWidth - sDeadzoneWidth) / 2, -(mChipThickness - sSensorThickness) / 2, 0); - transDead->SetTranslation((sChipWidth - sDeadzoneWidth) / 2, (mChipThickness - sSensorThickness) / 2, 0); - LOGP(debug, "Inserting {} in {} ", deadVol->GetName(), chipVol->GetName()); - chipVol->AddNode(deadVol, 1, transDead); TGeoVolume* metalVol = createMetalStack(); TGeoCombiTrans* transMetal = new TGeoCombiTrans(); - // transMetal->SetTranslation(0, sSensorThickness / 2, 0); - transMetal->SetTranslation(0, -sSensorThickness / 2, 0); + + if (!mIsFlipped) { + transSens->SetTranslation(-sDeadzoneWidth / 2, (mChipThickness - sSensorThickness) / 2, 0); + transDead->SetTranslation((sChipWidth - sDeadzoneWidth) / 2, (mChipThickness - sSensorThickness) / 2, 0); + transMetal->SetTranslation(0, -sSensorThickness / 2, 0); + } else { + transSens->SetTranslation(-sDeadzoneWidth / 2, -(mChipThickness - sSensorThickness) / 2, 0); + transDead->SetTranslation((sChipWidth - sDeadzoneWidth) / 2, -(mChipThickness - sSensorThickness) / 2, 0); + transMetal->SetTranslation(0, sSensorThickness / 2, 0); + } + + LOGP(debug, "Inserting {} in {} ", sensVol->GetName(), chipVol->GetName()); + chipVol->AddNode(sensVol, 1, transSens); + + LOGP(debug, "Inserting {} in {} ", deadVol->GetName(), chipVol->GetName()); + chipVol->AddNode(deadVol, 1, transDead); + LOGP(debug, "Inserting {} in {} ", metalVol->GetName(), chipVol->GetName()); chipVol->AddNode(metalVol, 1, transMetal); @@ -186,11 +198,59 @@ TGeoVolume* TRKSegmentedLayer::createModule() return moduleVol; } +std::pair TRKSegmentedLayer::getBoundingRadii(double staveWidth) const +{ + const float avgRadius = 0.5 * (mInnerRadius + mOuterRadius); + const float staveSizeX = staveWidth; + const float staveSizeY = mOuterRadius - mInnerRadius; + + /*const float deltaForTilt = 0.5 * (std::sin(TMath::DegToRad() * mTiltAngle) * staveSizeX + std::cos(TMath::DegToRad() * mTiltAngle) * staveSizeY); + + float radiusMin = std::sqrt(avgRadius * avgRadius + 0.25 * staveSizeX * staveSizeX + 0.25 * staveSizeY * staveSizeY - avgRadius * 2. * deltaForTilt); + float radiusMax = std::sqrt(avgRadius * avgRadius + 0.25 * staveSizeX * staveSizeX + 0.25 * staveSizeY * staveSizeY + avgRadius * 2. * deltaForTilt);*/ + + const double alpha = TMath::DegToRad() * std::abs(mTiltAngle); + + // The maximum distance from the center is always the outer top corner + double u_max = avgRadius * std::sin(alpha) + staveSizeX / 2.0; + double v_max = avgRadius * std::cos(alpha) + staveSizeY / 2.0; + double radiusMax = std::sqrt(u_max * u_max + v_max * v_max); + + // The perpendicular distance from the center to the line where the inner face lies + double perpDistance = avgRadius * std::cos(alpha) - staveSizeY / 2.0; + + // The projection of the center along the width of the stave + double projDistance = avgRadius * std::sin(alpha); + + double radiusMin; + if (projDistance <= staveSizeX / 2.0) { + // The center projects directly inside the flat face. + // The closest point is on the face itself, not on the corner + radiusMin = perpDistance; + } else { + // The center projects outside the face. The closest point is the inner corner + double u_min = projDistance - staveSizeX / 2.0; + radiusMin = std::sqrt(u_min * u_min + perpDistance * perpDistance); + } + + // Add a 0.5 mm safety margin to prevent false-positive overlaps in ROOT's geometry checker caused by floating-point inaccuracies + const float precisionMargin = 0.05f; + + return {radiusMin - precisionMargin, radiusMax + precisionMargin}; +} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -TRKMLLayer::TRKMLLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode) - : TRKSegmentedLayer(layerNumber, layerName, rInn, numberOfModules, thickOrX2X0, mode) +TRKMLLayer::TRKMLLayer(int layerNumber, std::string layerName, float rInn, float staggerOffset, float tiltAngle, int numberOfStaves, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode) + : TRKSegmentedLayer(layerNumber, layerName, rInn, tiltAngle, numberOfStaves, numberOfModules, thickOrX2X0, mode), mStaggerOffset(staggerOffset) { + if (mLayerNumber == sFlippedLayerNumber) { + mOuterRadius = rInn; + mInnerRadius = rInn - mChipThickness; + mIsFlipped = true; + mStaggerOffset = -staggerOffset; + LOGP(info, "Layer {} is flipped: sensor and metal stack positions are switched", mLayerNumber); + } } TGeoVolume* TRKMLLayer::createStave() @@ -215,32 +275,43 @@ TGeoVolume* TRKMLLayer::createStave() void TRKMLLayer::createLayer(TGeoVolume* motherVolume) { + // Retrieve exact bounding boundaries and create the logical container volume + auto [rMin, rMax] = getBoundingRadii(sStaveWidth); + TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); - TGeoTube* layer = new TGeoTube(mInnerRadius - 0.333 * sLogicalVolumeThickness, mInnerRadius + 0.667 * sLogicalVolumeThickness, mLength / 2); + // TGeoTube* layer = new TGeoTube(mInnerRadius - 0.333 * sLogicalVolumeThickness, mInnerRadius + 0.667 * sLogicalVolumeThickness, mLength / 2); + TGeoTube* layer = new TGeoTube(rMin, rMax, mLength / 2); TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); layerVol->SetLineColor(kYellow); // Compute the number of staves - int nStaves = (int)std::ceil(mInnerRadius * 2 * TMath::Pi() / sStaveWidth); - nStaves += nStaves % 2; // Require an even number of staves + // int nStaves = (int)std::ceil(mInnerRadius * 2 * TMath::Pi() / sStaveWidth); + // nStaves += nStaves % 2; // Require an even number of staves + + // Nominal average radii used as placement barycenters for the staves + const double avgRadiusInner = 0.5 * (mInnerRadius + mOuterRadius); + const double avgRadiusOuter = avgRadiusInner + mStaggerOffset; // Compute the size of the overlap region - double theta = 2 * TMath::Pi() / nStaves; + double theta = 2. * TMath::Pi() / mNumberOfStaves; double theta1 = std::atan(sStaveWidth / 2 / mInnerRadius); double st = std::sin(theta); double ct = std::cos(theta); double theta2 = std::atan((mInnerRadius * st - sStaveWidth / 2 * ct) / (mInnerRadius * ct + sStaveWidth / 2 * st)); double overlap = (theta1 - theta2) * mInnerRadius; - LOGP(info, "Creating a layer with {} staves and {} mm overlap", nStaves, overlap * 10); + LOGP(info, "Creating a layer with {} staves and {} mm overlap", mNumberOfStaves, overlap * 10); - for (int iStave = 0; iStave < nStaves; iStave++) { + for (int iStave = 0; iStave < mNumberOfStaves; iStave++) { TGeoVolume* staveVol = createStave(); TGeoCombiTrans* trans = new TGeoCombiTrans(); - double theta = 360. * iStave / nStaves; - // TGeoRotation* rot = new TGeoRotation("rot", theta - 90 + 4, 0, 0); - TGeoRotation* rot = new TGeoRotation("rot", theta + 90 + 4, 0, 0); + // If the number of staves is a multiple of 4, rotate by half a stave to avoid having the first one exactly on the x + double phi = (mNumberOfStaves % 4 == 0) ? theta * (iStave + 0.5) : theta * iStave; + double phiDeg = phi * TMath::RadToDeg(); + TGeoRotation* rot = new TGeoRotation("rot", phiDeg + 90 + mTiltAngle, 0, 0); trans->SetRotation(rot); - trans->SetTranslation(mInnerRadius * std::cos(2. * TMath::Pi() * iStave / nStaves), mInnerRadius * std::sin(2 * TMath::Pi() * iStave / nStaves), 0); + // float trueRadius = (mLayerNumber == 3 || mLayerNumber == 4) ? (iStave % 2 == 0 ? mInnerRadius : mInnerRadius + mStaggerOffset) : mInnerRadius; + float trueRadius = (mLayerNumber == 3 || mLayerNumber == 4) ? (iStave % 2 == 0 ? avgRadiusInner : avgRadiusOuter) : avgRadiusInner; + trans->SetTranslation(trueRadius * std::cos(phi), trueRadius * std::sin(phi), 0); LOGP(debug, "Inserting {} in {} ", staveVol->GetName(), layerVol->GetName()); layerVol->AddNode(staveVol, iStave, trans); } @@ -249,10 +320,67 @@ void TRKMLLayer::createLayer(TGeoVolume* motherVolume) motherVolume->AddNode(layerVol, 1, nullptr); } +std::pair TRKMLLayer::getBoundingRadii(double staveWidth) const +{ + // Get the baseline RMin from the base class + auto [defaultRadiusMin, defaultRadiusMax] = TRKSegmentedLayer::getBoundingRadii(staveWidth); + + // If we are not in the staggered layers, return the baseline values + if (mLayerNumber != 3 && mLayerNumber != 4) { + return {defaultRadiusMin, defaultRadiusMax}; + } + + /*// For staggered layers, we must recalculate RMax based on the outer shifted row + const float avgRadiusInner = 0.5 * (mInnerRadius + mOuterRadius); + const float avgRadiusOuter = avgRadiusInner + mStaggerOffset; + + const float staveSizeX = staveWidth; + const float staveSizeY = mOuterRadius - mInnerRadius; + + const float deltaForTiltOuter = 0.5 * (std::sin(TMath::DegToRad() * mTiltAngle) * staveSizeX + std::cos(TMath::DegToRad() * mTiltAngle) * staveSizeY); + + const float radiusMax = std::sqrt(avgRadiusOuter * avgRadiusOuter + 0.25 * staveSizeX * staveSizeX + 0.25 * staveSizeY * staveSizeY + avgRadiusOuter * 2. * deltaForTiltOuter);*/ + + const float avgRadiusInner = 0.5 * (mInnerRadius + mOuterRadius); + const float avgRadiusStaggered = avgRadiusInner + mStaggerOffset; + + const float staveSizeX = staveWidth; + const float staveSizeY = mOuterRadius - mInnerRadius; + const float alpha = TMath::DegToRad() * std::abs(mTiltAngle); + + const float precisionMargin = 0.05f; + + // If the layer is NOT flipped (e.g., Layer 4), the stagger goes outwards + // Therefore, we must recalculate only the maximum radius based on the outer shifted row + if (!mIsFlipped) { + float u_max = avgRadiusStaggered * std::sin(alpha) + staveSizeX / 2.0; + float v_max = avgRadiusStaggered * std::cos(alpha) + staveSizeY / 2.0; + float radiusMax = std::sqrt(u_max * u_max + v_max * v_max); + + return {defaultRadiusMin, radiusMax + precisionMargin}; + } + // If the layer IS flipped (e.g., Layer 3), the stagger goes inwards + // Therefore, we must recalculate only the minimum radius based on the inner shifted row + else { + double perpDistance = avgRadiusStaggered * std::cos(alpha) - staveSizeY / 2.0; + double projDistance = avgRadiusStaggered * std::sin(alpha); + double newRadiusMin; + + if (projDistance <= staveSizeX / 2.0) { + newRadiusMin = perpDistance; + } else { + double u_min = projDistance - staveSizeX / 2.0; + newRadiusMin = std::sqrt(u_min * u_min + perpDistance * perpDistance); + } + + return {newRadiusMin - precisionMargin, defaultRadiusMax}; + } +} + ///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// -TRKOTLayer::TRKOTLayer(int layerNumber, std::string layerName, float rInn, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode) - : TRKSegmentedLayer(layerNumber, layerName, rInn, numberOfModules, thickOrX2X0, mode) +TRKOTLayer::TRKOTLayer(int layerNumber, std::string layerName, float rInn, float tiltAngle, int numberOfStaves, int numberOfModules, float thickOrX2X0, MatBudgetParamMode mode) + : TRKSegmentedLayer(layerNumber, layerName, rInn, tiltAngle, numberOfStaves, numberOfModules, thickOrX2X0, mode) { } @@ -298,8 +426,12 @@ TGeoVolume* TRKOTLayer::createStave() void TRKOTLayer::createLayer(TGeoVolume* motherVolume) { + // Retrieve exact bounding boundaries automatically inherited from TRKSegmentedLayer + auto [rMin, rMax] = getBoundingRadii(sStaveWidth); + TGeoMedium* medAir = gGeoManager->GetMedium("TRK_AIR$"); - TGeoTube* layer = new TGeoTube(mInnerRadius - 0.333 * sLogicalVolumeThickness, mInnerRadius + 0.667 * sLogicalVolumeThickness, mLength / 2); + // TGeoTube* layer = new TGeoTube(mInnerRadius - 0.333 * sLogicalVolumeThickness, mInnerRadius + 0.667 * sLogicalVolumeThickness, mLength / 2); + TGeoTube* layer = new TGeoTube(rMin, rMax, mLength / 2); TGeoVolume* layerVol = new TGeoVolume(mLayerName.c_str(), layer, medAir); layerVol->SetLineColor(kYellow); @@ -307,8 +439,11 @@ void TRKOTLayer::createLayer(TGeoVolume* motherVolume) int nStaves = (int)std::ceil(mInnerRadius * 2 * TMath::Pi() / sStaveWidth); nStaves += nStaves % 2; // Require an even number of staves + // Nominal average radius used as the placement barycenter for all staves + const double avgRadius = 0.5 * (mInnerRadius + mOuterRadius); + // Compute the size of the overlap region - double theta = 2 * TMath::Pi() / nStaves; + double theta = 2. * TMath::Pi() / nStaves; double theta1 = std::atan(sStaveWidth / 2 / mInnerRadius); double st = std::sin(theta); double ct = std::cos(theta); @@ -319,11 +454,12 @@ void TRKOTLayer::createLayer(TGeoVolume* motherVolume) for (int iStave = 0; iStave < nStaves; iStave++) { TGeoVolume* staveVol = createStave(); 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, 0, 0); + double phi = theta * iStave; + double phiDeg = phi * TMath::RadToDeg(); + TGeoRotation* rot = new TGeoRotation("rot", phiDeg + 90 + mTiltAngle, 0, 0); trans->SetRotation(rot); - trans->SetTranslation(mInnerRadius * std::cos(2. * TMath::Pi() * iStave / nStaves), mInnerRadius * std::sin(2 * TMath::Pi() * iStave / nStaves), 0); + // trans->SetTranslation(mInnerRadius * std::cos(phi), mInnerRadius * std::sin(phi), 0); + trans->SetTranslation(avgRadius * std::cos(phi), avgRadius * std::sin(phi), 0); LOGP(debug, "Inserting {} in {} ", staveVol->GetName(), layerVol->GetName()); layerVol->AddNode(staveVol, iStave, trans); } @@ -331,7 +467,14 @@ void TRKOTLayer::createLayer(TGeoVolume* motherVolume) LOGP(debug, "Inserting {} in {} ", layerVol->GetName(), motherVolume->GetName()); motherVolume->AddNode(layerVol, 1, nullptr); } + +std::pair TRKOTLayer::getBoundingRadii(double staveWidth) const +{ + auto [radiusMin, radiusMax] = TRKSegmentedLayer::getBoundingRadii(staveWidth); + + return {radiusMin - 0.201f, radiusMax}; +} // ClassImp(TRKLayer); } // namespace trk -} // namespace o2 \ No newline at end of file +} // namespace o2 From b1bac33d1cd77bfedf9465b4b5e0665bf86c8dc3 Mon Sep 17 00:00:00 2001 From: altsybee Date: Fri, 17 Apr 2026 09:34:38 +0200 Subject: [PATCH 486/701] [ALICE3] TKR: add post CheckClusterSize.C macro vs Eta (#15274) --- .../ALICE3/TRK/macros/test/CMakeLists.txt | 8 + .../TRK/macros/test/postClusterSizeVsEta.C | 199 ++++++++++++++++++ 2 files changed, 207 insertions(+) create mode 100644 Detectors/Upgrades/ALICE3/TRK/macros/test/postClusterSizeVsEta.C diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt b/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt index 54e42c6857249..33d1b4a5afdc6 100644 --- a/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/CMakeLists.txt @@ -49,3 +49,11 @@ o2_add_test_root_macro(CheckClusters.C O2::TRKBase O2::TRKSimulation LABELS trk COMPILE_ONLY) + +o2_add_test_root_macro(postClusterSizeVsEta.C + PUBLIC_LINK_LIBRARIES O2::DataFormatsTRK + O2::SimulationDataFormat + O2::Framework + O2::TRKBase + O2::TRKSimulation + LABELS trk COMPILE_ONLY) diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/postClusterSizeVsEta.C b/Detectors/Upgrades/ALICE3/TRK/macros/test/postClusterSizeVsEta.C new file mode 100644 index 0000000000000..47beaf36f2957 --- /dev/null +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/postClusterSizeVsEta.C @@ -0,0 +1,199 @@ +// 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. + +/// \file postClusterSizeVsEta.C +/// \brief A post-processing macro to draw average cluster size vs eta + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#endif + +using namespace std; + +// ### required input file: CheckClusters.root, which is the output of CheckClusters.C macro +void postClusterSizeVsEta(const std::string& strFileInput = "CheckClusters.root") +{ + gStyle->SetOptStat(0); + + TFile* fileInput = new TFile(strFileInput.c_str()); + TTree* tree = (TTree*)fileInput->Get("ntc"); + std::cout << "Opened tree: " << tree->GetName() << ", entries = " << tree->GetEntries() << std::endl; + + // set branch addresses + Float_t event; + Float_t mcTrackID; + Float_t hitLocX, hitLocZ; + Float_t hitGlobX, hitGlobY, hitGlobZ; + Float_t clusGlobX, clusGlobY, clusGlobZ; + Float_t clusLocX, clusLocZ; + Float_t rofFrame; + Float_t clusSize; + Float_t chipID; + Float_t layer; + Float_t disk; + Float_t subdet; + Float_t row, col; + Float_t pt; + + // set branch addresses + tree->SetBranchAddress("event", &event); + tree->SetBranchAddress("mcTrackID", &mcTrackID); + tree->SetBranchAddress("hitLocX", &hitLocX); + tree->SetBranchAddress("hitLocZ", &hitLocZ); + tree->SetBranchAddress("hitGlobX", &hitGlobX); + tree->SetBranchAddress("hitGlobY", &hitGlobY); + tree->SetBranchAddress("hitGlobZ", &hitGlobZ); + tree->SetBranchAddress("clusGlobX", &clusGlobX); + tree->SetBranchAddress("clusGlobY", &clusGlobY); + tree->SetBranchAddress("clusGlobZ", &clusGlobZ); + tree->SetBranchAddress("clusLocX", &clusLocX); + tree->SetBranchAddress("clusLocZ", &clusLocZ); + tree->SetBranchAddress("rofFrame", &rofFrame); + tree->SetBranchAddress("clusSize", &clusSize); + tree->SetBranchAddress("chipID", &chipID); + tree->SetBranchAddress("layer", &layer); + tree->SetBranchAddress("disk", &disk); + tree->SetBranchAddress("subdet", &subdet); + tree->SetBranchAddress("row", &row); + tree->SetBranchAddress("col", &col); + tree->SetBranchAddress("pt", &pt); + + // Some QA histograms + TH1F* hPt = new TH1F("hPt", "p_{T};p_{T};Entries", 100, 0., 10.); + TH1F* hClusSize = new TH1F("hClusSize", "Cluster size;clusSize;Entries", 20, 0., 20.); + TH1F* hLayer = new TH1F("hLayer", "Layer;layer;Entries", 20, -0.5, 19.5); + TH1F* hDxGlob = new TH1F("hDxGlob", "clusGlobX - hitGlobX;#DeltaX [global];Entries", 200, -1., 1.); + TH1F* hDzGlob = new TH1F("hDzGlob", "clusGlobZ - hitGlobZ;#DeltaZ [global];Entries", 200, -1., 1.); + TH2F* hHitXY = new TH2F("hHitXY", "Hit global XY;hitGlobX;hitGlobY", 200, -20., 20., 200, -20., 20.); + TH2F* hClusVsHitX = new TH2F("hClusVsHitX", "clusGlobX vs hitGlobX;hitGlobX;clusGlobX", 200, -20., 20., 200, -20., 20.); + + // histograms for cluster size vs eta for each barrel layer: + const int nLayers = 11; + TH2F* hClustSizePerLayerVsEta[nLayers]; + for (int i = 0; i < nLayers; i++) { + hClustSizePerLayerVsEta[i] = new TH2F(Form("hClustSizePerLayerVsEta_Lay%d", i), Form("Cluster size vs eta for layer %d;#eta;Cluster size", i), 200, -5, 5, 101, -0.5, 100.5); + } + + // Loop over entries + const Long64_t nEntries = tree->GetEntries(); + for (Long64_t i = 0; i < nEntries; ++i) { + tree->GetEntry(i); + + // Fill QA histograms + float dXGlob = clusGlobX - hitGlobX; + float dZGlob = clusGlobZ - hitGlobZ; + hPt->Fill(pt); + hClusSize->Fill(clusSize); + hLayer->Fill(layer); + hDxGlob->Fill(dXGlob); + hDzGlob->Fill(dZGlob); + hHitXY->Fill(hitGlobX, hitGlobY); + hClusVsHitX->Fill(hitGlobX, clusGlobX); + + // cls size vs eta: + float clustR = sqrt(clusGlobX * clusGlobX + clusGlobY * clusGlobY); + float clustPhi = atan2(clusGlobY, clusGlobX); + float clustTheta = atan2(clustR, clusGlobZ); + float clustEta = -log(tan(clustTheta / 2)); + + // !!! important: to avoid VD layers (numeration for ML starts from 0, while VD layers are also numbered as 0,1,2) + if (clustR > 5) // cm + hClustSizePerLayerVsEta[(int)layer + 3]->Fill(clustEta, clusSize); + else if (layer < 3) // VD layers + hClustSizePerLayerVsEta[(int)layer]->Fill(clustEta, clusSize); + + // progress print + if ((i + 1) % 200000 == 0) { + std::cout << "Processed " << (i + 1) << " / " << nEntries << " entries" << std::endl; + } + } + + // Save histograms to file + TFile* fout = TFile::Open("clusterSizes_vs_eta.root", "RECREATE"); + hPt->Write(); + hClusSize->Write(); + hLayer->Write(); + hDxGlob->Write(); + hDzGlob->Write(); + hHitXY->Write(); + hClusVsHitX->Write(); + + // draw some QA histograms + TCanvas* c1 = new TCanvas("canv_clusters_QA", "Clusters QA", 1200, 800); + c1->Divide(2, 2); + c1->cd(1); + hPt->Draw(); + c1->cd(2); + hClusSize->Draw(); + c1->cd(3); + hDxGlob->Draw(); + c1->cd(4); + hHitXY->Draw("COLZ"); + + int colors[] = {kRed, kBlue + 1, kMagenta + 1, + kRed, kBlue + 1, kMagenta + 1, + kCyan + 1, kGray + 2, kRed, kBlue, kMagenta + 1, kCyan, kAzure + 1, kOrange - 9, kRed + 2, kBlue + 2, kMagenta + 2}; + + TCanvas* canv_clsSize_vs_eta[nLayers]; + TProfile* profPerLayerVsEta[nLayers]; + for (int i = 0; i < nLayers; i++) { + canv_clsSize_vs_eta[i] = new TCanvas(Form("canv_clsSize_vs_eta_Lay%d", i), Form("Cluster size vs eta for layer %d", i), 800, 600); + hClustSizePerLayerVsEta[i]->Draw("COLZ"); + gPad->SetLogz(); + profPerLayerVsEta[i] = hClustSizePerLayerVsEta[i]->ProfileX(); + profPerLayerVsEta[i]->SetLineColor(colors[i]); + profPerLayerVsEta[i]->SetMarkerColor(colors[i]); + profPerLayerVsEta[i]->SetMarkerStyle(i < 8 ? 20 : 24); + profPerLayerVsEta[i]->SetTitle(";#eta;#LTcluster size#GT"); + profPerLayerVsEta[i]->DrawCopy("same"); + + hClustSizePerLayerVsEta[i]->Write(); + profPerLayerVsEta[i]->Write(); + } + + // ### canvas with profiles for 3 VD layers + TCanvas* canv_av_clsSize_vs_eta_VD_layers = new TCanvas("canv_clsSize_vs_eta_VD_layers", "Cluster size vs eta for VD layers", 800, 600); + TLegend* legLayersVD = new TLegend(0.3, 0.72, 0.65, 0.89); + for (int i = 0; i < 3; i++) { + profPerLayerVsEta[i]->GetYaxis()->SetRangeUser(0., 60.); + profPerLayerVsEta[i]->DrawCopy(i == 0 ? "P" : "P same"); + legLayersVD->AddEntry(profPerLayerVsEta[i], Form("VD layer %d", i), "P"); + } + legLayersVD->Draw(); + gPad->SetGrid(); + canv_av_clsSize_vs_eta_VD_layers->SaveAs("clsSize_vs_eta_VD_layers.png"); + canv_av_clsSize_vs_eta_VD_layers->Write(); + + // ### canvas with profiles for MLOT layers + TCanvas* canv_av_clsSize_vs_eta_MLOT_layers = new TCanvas("canv_clsSize_vs_eta_MLOT_layers", "Cluster size vs eta for MLOT layers", 800, 600); + TLegend* legLayersMLOT = new TLegend(0.3, 0.52, 0.65, 0.89); + for (int i = 3; i < nLayers; i++) { + profPerLayerVsEta[i]->GetYaxis()->SetRangeUser(0., 12.5); + profPerLayerVsEta[i]->GetXaxis()->SetRangeUser(-3.5, 3.5); + profPerLayerVsEta[i]->DrawCopy(i == 3 ? "P" : "P same"); + legLayersMLOT->AddEntry(profPerLayerVsEta[i], Form("MLOT layer %d", i), "P"); + } + legLayersMLOT->Draw(); + gPad->SetGrid(); + canv_av_clsSize_vs_eta_MLOT_layers->SaveAs("clsSize_vs_eta_MLOT_layers.png"); + canv_av_clsSize_vs_eta_MLOT_layers->Write(); +} \ No newline at end of file From b61cf4a80bcadf909cae6135b412faa54b92f106 Mon Sep 17 00:00:00 2001 From: shahoian Date: Thu, 16 Apr 2026 17:28:07 +0200 Subject: [PATCH 487/701] Tentative improvement of 3-body decay cov. matrix creation --- DataFormats/Reconstruction/src/Decay3Body.cxx | 9 ++++----- 1 file changed, 4 insertions(+), 5 deletions(-) diff --git a/DataFormats/Reconstruction/src/Decay3Body.cxx b/DataFormats/Reconstruction/src/Decay3Body.cxx index aa071cea675cd..eb1b7ea1fd57d 100644 --- a/DataFormats/Reconstruction/src/Decay3Body.cxx +++ b/DataFormats/Reconstruction/src/Decay3Body.cxx @@ -16,15 +16,14 @@ using namespace o2::dataformats; Decay3Body::Decay3Body(const std::array& xyz, const std::array& pxyz, const std::array& covxyz, const Track& tr0, const Track& tr1, const Track& tr2, o2::track::PID pid) : mProngs{tr0, tr1, tr2} { - std::array cov{}, cov1{}, cov2{}; - tr0.getCovXYZPxPyPzGlo(cov); + std::array cov{}, cov0{}, cov1{}, cov2{}; + tr0.getCovXYZPxPyPzGlo(cov0); tr1.getCovXYZPxPyPzGlo(cov1); tr2.getCovXYZPxPyPzGlo(cov2); - for (int i = 0; i < 21; i++) { - cov[i] += cov1[i] + cov2[i]; - } + constexpr int MomInd[6] = {9, 13, 14, 18, 19, 20}; // cov matrix elements for momentum component for (int i = 0; i < 6; i++) { cov[i] = covxyz[i]; + cov[MomInd[i]] = cov0[MomInd[i]] + cov1[MomInd[i]] + cov2[MomInd[i]]; } this->set(xyz, pxyz, cov, tr0.getCharge() + tr1.getCharge() + tr2.getCharge(), true, pid); } From 1e1c6e59cd667daec5e23981670bd3c767a710c0 Mon Sep 17 00:00:00 2001 From: shahoian Date: Fri, 17 Apr 2026 14:49:54 +0200 Subject: [PATCH 488/701] Fix typo in the TPCTimeSeriesSpec --- Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx b/Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx index ee3acc808ccb7..ac3ff15fd3a29 100644 --- a/Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx +++ b/Detectors/TPC/workflow/src/TPCTimeSeriesSpec.cxx @@ -1377,7 +1377,7 @@ class TPCTimeSeries : public Task if (propTPCOk) { // store delta parameters deltaP0OuterITS = trackTmp.getParam(0) - trackTmpOut.getParam(0); - deltaP1OuterITS = trackTmp.getParam(1) - trackTmpOut.getParam(2); + deltaP1OuterITS = trackTmp.getParam(1) - trackTmpOut.getParam(1); deltaP2OuterITS = trackTmp.getParam(2) - trackTmpOut.getParam(2); deltaP3OuterITS = trackTmp.getParam(3) - trackTmpOut.getParam(3); deltaP4OuterITS = trackTmp.getParam(4) - trackTmpOut.getParam(4); From 8a51b175ab3fd3bf02ced8bd3c6eb837407f1ff1 Mon Sep 17 00:00:00 2001 From: Andreas Morsch Date: Fri, 17 Apr 2026 09:23:37 +0200 Subject: [PATCH 489/701] link to neutron point-like cross-sections needed starting from FLUKA_4.5 --- Detectors/gconfig/src/FlukaConfig.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/Detectors/gconfig/src/FlukaConfig.cxx b/Detectors/gconfig/src/FlukaConfig.cxx index 4723d98244ca9..fe5f73bc7026c 100644 --- a/Detectors/gconfig/src/FlukaConfig.cxx +++ b/Detectors/gconfig/src/FlukaConfig.cxx @@ -42,6 +42,7 @@ void linkFlukaFiles() gSystem->Exec("ln -s $FLUKADATA/random.dat ."); gSystem->Exec("ln -s $FLUKADATA/dnr.dat ."); gSystem->Exec("ln -s $FLUKADATA/nunstab.data ."); + gSystem->Exec("ln -s $FLUKADATA/neutron ."); // Give some meaningfull name to the output gSystem->Exec("ln -s fluka.out fort.11"); gSystem->Exec("ln -s fluka.err fort.15"); From 5500dfcc8141feaf3d86bfb0f43f4b990353edb0 Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Thu, 26 Mar 2026 11:34:49 +0100 Subject: [PATCH 490/701] Cleanup: Remove old version of jobutils and cpulimit tool Remove code no longer used --- Utilities/Tools/CMakeLists.txt | 2 - Utilities/Tools/cpulimit/.clang-format | 2 - Utilities/Tools/cpulimit/CMakeLists.txt | 16 - Utilities/Tools/cpulimit/README | 2 - Utilities/Tools/cpulimit/cpulimit.c | 529 ------------------ Utilities/Tools/cpulimit/list.c | 148 ----- Utilities/Tools/cpulimit/list.h | 138 ----- Utilities/Tools/cpulimit/process_group.c | 219 -------- Utilities/Tools/cpulimit/process_group.h | 55 -- Utilities/Tools/cpulimit/process_iterator.c | 49 -- Utilities/Tools/cpulimit/process_iterator.h | 97 ---- .../Tools/cpulimit/process_iterator_apple.c | 148 ----- .../Tools/cpulimit/process_iterator_freebsd.c | 119 ---- .../Tools/cpulimit/process_iterator_linux.c | 198 ------- prodtests/full_system_test.sh | 2 +- prodtests/full_system_test_ci_extra_tests.sh | 2 +- prodtests/full_system_test_pipeline.sh | 4 +- prodtests/sim_challenge.sh | 2 +- run/SimExamples/SimAsService_biasing1/run.sh | 4 +- 19 files changed, 7 insertions(+), 1729 deletions(-) delete mode 100644 Utilities/Tools/cpulimit/.clang-format delete mode 100644 Utilities/Tools/cpulimit/CMakeLists.txt delete mode 100644 Utilities/Tools/cpulimit/README delete mode 100644 Utilities/Tools/cpulimit/cpulimit.c delete mode 100644 Utilities/Tools/cpulimit/list.c delete mode 100644 Utilities/Tools/cpulimit/list.h delete mode 100644 Utilities/Tools/cpulimit/process_group.c delete mode 100644 Utilities/Tools/cpulimit/process_group.h delete mode 100644 Utilities/Tools/cpulimit/process_iterator.c delete mode 100644 Utilities/Tools/cpulimit/process_iterator.h delete mode 100644 Utilities/Tools/cpulimit/process_iterator_apple.c delete mode 100644 Utilities/Tools/cpulimit/process_iterator_freebsd.c delete mode 100644 Utilities/Tools/cpulimit/process_iterator_linux.c diff --git a/Utilities/Tools/CMakeLists.txt b/Utilities/Tools/CMakeLists.txt index 55048623739b9..04002ad85dae7 100644 --- a/Utilities/Tools/CMakeLists.txt +++ b/Utilities/Tools/CMakeLists.txt @@ -9,9 +9,7 @@ # granted to it by virtue of its status as an Intergovernmental Organization # or submit itself to any jurisdiction. -add_subdirectory(cpulimit) install(PROGRAMS monitor-mem.sh DESTINATION share/scripts/) -install(PROGRAMS jobutils.sh DESTINATION share/scripts/) install(PROGRAMS jobutils2.sh DESTINATION share/scripts/) install(PROGRAMS grid_submit.sh DESTINATION share/scripts/) diff --git a/Utilities/Tools/cpulimit/.clang-format b/Utilities/Tools/cpulimit/.clang-format deleted file mode 100644 index a43d914ec38dd..0000000000000 --- a/Utilities/Tools/cpulimit/.clang-format +++ /dev/null @@ -1,2 +0,0 @@ -DisableFormat: true -SortIncludes: false \ No newline at end of file diff --git a/Utilities/Tools/cpulimit/CMakeLists.txt b/Utilities/Tools/cpulimit/CMakeLists.txt deleted file mode 100644 index f1109c65fdb69..0000000000000 --- a/Utilities/Tools/cpulimit/CMakeLists.txt +++ /dev/null @@ -1,16 +0,0 @@ -# Copyright 2019-2020 CERN and copyright holders of ALICE O2. -# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -# All rights not expressly granted are reserved. -# -# This software is distributed under the terms of the GNU General Public -# License v3 (GPL Version 3), copied verbatim in the file "COPYING". -# -# In applying this license CERN does not waive the privileges and immunities -# granted to it by virtue of its status as an Intergovernmental Organization -# or submit itself to any jurisdiction. - -add_executable(cpulimit - cpulimit.c list.c process_group.c process_iterator.c) -target_compile_definitions(cpulimit PUBLIC _GNU_SOURCE) - -install(TARGETS cpulimit DESTINATION share/scripts/) diff --git a/Utilities/Tools/cpulimit/README b/Utilities/Tools/cpulimit/README deleted file mode 100644 index 20f543f9491ee..0000000000000 --- a/Utilities/Tools/cpulimit/README +++ /dev/null @@ -1,2 +0,0 @@ -These sources have been copied from https://github.com/opsengine/cpulimit -commit f4d2682804931e. \ No newline at end of file diff --git a/Utilities/Tools/cpulimit/cpulimit.c b/Utilities/Tools/cpulimit/cpulimit.c deleted file mode 100644 index e35a1565253e7..0000000000000 --- a/Utilities/Tools/cpulimit/cpulimit.c +++ /dev/null @@ -1,529 +0,0 @@ -/** - * - * cpulimit - a CPU limiter for Linux - * - * Copyright (C) 2005-2012, by: Angelo Marletta - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - ************************************************************** - * - * This is a simple program to limit the cpu usage of a process - * If you modify this code, send me a copy please - * - * Get the latest version at: http://github.com/opsengine/cpulimit - * - */ - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#if defined(__APPLE__) -#include -#endif -#include -#include -#include - -#if defined(__APPLE__) || defined(__FREEBSD__) -#include -#endif - -#include "process_group.h" -#include "list.h" - -#ifdef HAVE_SYS_SYSINFO_H -#include -#endif - -//some useful macro -#ifndef MIN -#define MIN(a,b) (((a)<(b))?(a):(b)) -#endif -#ifndef MAX -#define MAX(a,b) (((a)>(b))?(a):(b)) -#endif - -//control time slot in microseconds -//each slot is splitted in a working slice and a sleeping slice -//TODO: make it adaptive, based on the actual system load -#define TIME_SLOT 100000 - -#define MAX_PRIORITY -10 - -/* GLOBAL VARIABLES */ - -//the "family" -struct process_group pgroup; -//pid of cpulimit -pid_t cpulimit_pid; -//name of this program (maybe cpulimit...) -char *program_name; - -//number of cpu -int NCPU; - -/* CONFIGURATION VARIABLES */ - -//verbose mode -int verbose = 0; -//lazy mode (exits if there is no process) -int lazy = 0; - -//SIGINT and SIGTERM signal handler -static void quit(int sig) -{ - //let all the processes continue if stopped - struct list_node *node = NULL; - if (pgroup.proclist != NULL) - { - for (node = pgroup.proclist->first; node != NULL; node = node->next) { - struct process *p = (struct process*)(node->data); - kill(p->pid, SIGCONT); - } - close_process_group(&pgroup); - } - //fix ^C little problem - printf("\r"); - fflush(stdout); - exit(0); -} - -//return t1-t2 in microseconds (no overflow checks, so better watch out!) -static inline unsigned long timediff(const struct timeval *t1,const struct timeval *t2) -{ - return (t1->tv_sec - t2->tv_sec) * 1000000 + (t1->tv_usec - t2->tv_usec); -} - -static void print_usage(FILE *stream, int exit_code) -{ - fprintf(stream, "Usage: %s [OPTIONS...] TARGET\n", program_name); - fprintf(stream, " OPTIONS\n"); - fprintf(stream, " -l, --limit=N percentage of cpu allowed from 0 to %d (required)\n", 100*NCPU); - fprintf(stream, " -v, --verbose show control statistics\n"); - fprintf(stream, " -z, --lazy exit if there is no target process, or if it dies\n"); - fprintf(stream, " -i, --include-children limit also the children processes\n"); - fprintf(stream, " -h, --help display this help and exit\n"); - fprintf(stream, " TARGET must be exactly one of these:\n"); - fprintf(stream, " -p, --pid=N pid of the process (implies -z)\n"); - fprintf(stream, " -e, --exe=FILE name of the executable program file or path name\n"); - fprintf(stream, " COMMAND [ARGS] run this command and limit it (implies -z)\n"); - fprintf(stream, "\nReport bugs to .\n"); - exit(exit_code); -} - -static void increase_priority() { - //find the best available nice value - int old_priority = getpriority(PRIO_PROCESS, 0); - int priority = old_priority; - while (setpriority(PRIO_PROCESS, 0, priority-1) == 0 && priority>MAX_PRIORITY) { - priority--; - } - if (priority != old_priority) { - if (verbose) { printf("Priority changed to %d\n", priority); } - } - else { - if (verbose) { printf("Warning: Cannot change priority. Run as root or renice for best results.\n"); } - } -} - -/* Get the number of CPUs */ -static int get_ncpu() { - int ncpu; -#ifdef _SC_NPROCESSORS_ONLN - ncpu = sysconf(_SC_NPROCESSORS_ONLN); -#elif defined __APPLE__ - int mib[2] = {CTL_HW, HW_NCPU}; - size_t len = sizeof(ncpu); - sysctl(mib, 2, &ncpu, &len, NULL, 0); -#elif defined _GNU_SOURCE - ncpu = get_nprocs(); -#else - ncpu = -1; -#endif - return ncpu; -} - -int get_pid_max() -{ -#ifdef __linux__ - //read /proc/sys/kernel/pid_max - static char buffer[1024]; - FILE *fd = fopen("/proc/sys/kernel/pid_max", "r"); - if (fd==NULL) { return -1; } - if (fgets(buffer, sizeof(buffer), fd)==NULL) { - fclose(fd); - return -1; - } - fclose(fd); - return atoi(buffer); -#elif defined __FreeBSD__ - return 99998; -#elif defined __APPLE__ - return 99998; -#endif -} - -void limit_process(pid_t pid, double limit, int include_children) -{ - //slice of the slot in which the process is allowed to run - struct timespec twork; - //slice of the slot in which the process is stopped - struct timespec tsleep; - //when the last twork has started - struct timeval startwork; - //when the last twork has finished - struct timeval endwork; - //initialization - memset(&twork, 0, sizeof(struct timespec)); - memset(&tsleep, 0, sizeof(struct timespec)); - memset(&startwork, 0, sizeof(struct timeval)); - memset(&endwork, 0, sizeof(struct timeval)); - //last working time in microseconds - unsigned long workingtime = 0; - //generic list item - struct list_node *node; - //counter - int c = 0; - - //get a better priority - increase_priority(); - - //build the family - init_process_group(&pgroup, pid, include_children); - - if (verbose) { printf("Members in the process group owned by %d: %d\n", pgroup.target_pid, pgroup.proclist->count); } - - //rate at which we are keeping active the processes (range 0-1) - //1 means that the process are using all the twork slice - double workingrate = -1; - while(1) { - update_process_group(&pgroup); - - if (pgroup.proclist->count==0) { - if (verbose) { printf("No more processes.\n"); } - break; - } - - //total cpu actual usage (range 0-1) - //1 means that the processes are using 100% cpu - double pcpu = -1; - - //estimate how much the controlled processes are using the cpu in the working interval - for (node = pgroup.proclist->first; node != NULL; node = node->next) { - struct process *proc = (struct process*)(node->data); - if (proc->cpu_usage < 0) { - continue; - } - if (pcpu < 0) { pcpu = 0; } - pcpu += proc->cpu_usage; - } - - //adjust work and sleep time slices - if (pcpu < 0) { - //it's the 1st cycle, initialize workingrate - pcpu = limit; - workingrate = limit; - twork.tv_nsec = TIME_SLOT * limit * 1000; - } - else { - //adjust workingrate - workingrate = MIN(workingrate / pcpu * limit, 1); - twork.tv_nsec = TIME_SLOT * 1000 * workingrate; - } - tsleep.tv_nsec = TIME_SLOT * 1000 - twork.tv_nsec; - - if (verbose) { - if (c%200==0) { - printf("\n%%CPU\twork quantum\tsleep quantum\tactive rate\n"); - } - if (c%10==0 && c>0) { - printf("%0.2lf%%\t%6ld us\t%6ld us\t%0.2lf%%\n", pcpu*100, twork.tv_nsec/1000, tsleep.tv_nsec/1000, workingrate*100); - } - } - - //resume processes - node = pgroup.proclist->first; - while (node != NULL) - { - struct list_node *next_node = node->next; - struct process *proc = (struct process*)(node->data); - if (kill(proc->pid,SIGCONT) != 0) { - //process is dead, remove it from family - if (verbose) { fprintf(stderr, "SIGCONT failed. Process %d dead!\n", proc->pid); } - //remove process from group - delete_node(pgroup.proclist, node); - remove_process(&pgroup, proc->pid); - } - node = next_node; - } - - //now processes are free to run (same working slice for all) - gettimeofday(&startwork, NULL); - nanosleep(&twork, NULL); - gettimeofday(&endwork, NULL); - workingtime = timediff(&endwork, &startwork); - - long delay = workingtime - twork.tv_nsec/1000; - if (c>0 && delay>10000) { - //delay is too much! signal to user? - //fprintf(stderr, "%d %ld us\n", c, delay); - } - - if (tsleep.tv_nsec>0) { - //stop processes only if tsleep>0 - node = pgroup.proclist->first; - while (node != NULL) - { - struct list_node *next_node = node->next; - struct process *proc = (struct process*)(node->data); - if (kill(proc->pid,SIGSTOP)!=0) { - //process is dead, remove it from family - if (verbose) { fprintf(stderr, "SIGSTOP failed. Process %d dead!\n", proc->pid); } - //remove process from group - delete_node(pgroup.proclist, node); - remove_process(&pgroup, proc->pid); - } - node = next_node; - } - //now the processes are sleeping - nanosleep(&tsleep,NULL); - } - c++; - } - close_process_group(&pgroup); -} - -int main(int argc, char **argv) { - //argument variables - const char *exe = NULL; - int perclimit = 0; - int exe_ok = 0; - int pid_ok = 0; - int limit_ok = 0; - pid_t pid = 0; - int include_children = 0; - - //get program name - char *p = (char*)strrchr(argv[0], '/'); - program_name = p==NULL ? argv[0] : (p+1); - //get current pid - cpulimit_pid = getpid(); - //get cpu count - NCPU = get_ncpu(); - - //parse arguments - int next_option; - int option_index = 0; - //A string listing valid short options letters - const char* short_options = "+p:e:l:vzih"; - //An array describing valid long options - const struct option long_options[] = { - { "pid", required_argument, NULL, 'p' }, - { "exe", required_argument, NULL, 'e' }, - { "limit", required_argument, NULL, 'l' }, - { "verbose", no_argument, NULL, 'v' }, - { "lazy", no_argument, NULL, 'z' }, - { "include-children", no_argument, NULL, 'i' }, - { "help", no_argument, NULL, 'h' }, - { 0, 0, 0, 0 } - }; - - do { - next_option = getopt_long(argc, argv, short_options,long_options, &option_index); - switch(next_option) { - case 'p': - pid = atoi(optarg); - pid_ok = 1; - break; - case 'e': - exe = optarg; - exe_ok = 1; - break; - case 'l': - perclimit = atoi(optarg); - limit_ok = 1; - break; - case 'v': - verbose = 1; - break; - case 'z': - lazy = 1; - break; - case 'i': - include_children = 1; - break; - case 'h': - print_usage(stdout, 1); - break; - case '?': - print_usage(stderr, 1); - break; - case -1: - break; - default: - abort(); - } - } while(next_option != -1); - - if (pid_ok && (pid <= 1 || pid >= get_pid_max())) { - fprintf(stderr,"Error: Invalid value for argument PID\n"); - print_usage(stderr, 1); - exit(1); - } - if (pid != 0) { - lazy = 1; - } - - if (!limit_ok) { - fprintf(stderr,"Error: You must specify a cpu limit percentage\n"); - print_usage(stderr, 1); - exit(1); - } - double limit = perclimit / 100.0; - if (limit<0 || limit >NCPU) { - fprintf(stderr,"Error: limit must be in the range 0-%d00\n", NCPU); - print_usage(stderr, 1); - exit(1); - } - - int command_mode = optind < argc; - if (exe_ok + pid_ok + command_mode == 0) { - fprintf(stderr,"Error: You must specify one target process, either by name, pid, or command line\n"); - print_usage(stderr, 1); - exit(1); - } - - if (exe_ok + pid_ok + command_mode > 1) { - fprintf(stderr,"Error: You must specify exactly one target process, either by name, pid, or command line\n"); - print_usage(stderr, 1); - exit(1); - } - - //all arguments are ok! - signal(SIGINT, quit); - signal(SIGTERM, quit); - - //print the number of available cpu - if (verbose) { printf("%d cpu detected\n", NCPU); } - - if (command_mode) { - int i; - //executable file - const char *cmd = argv[optind]; - //command line arguments - char **cmd_args = (char**)malloc((argc-optind + 1) * sizeof(char*)); - if (cmd_args==NULL) { exit(2); } - for (i=0; i 0) { - //parent - int status_process; - int status_limiter; - waitpid(child, &status_process, 0); - waitpid(limiter, &status_limiter, 0); - if (WIFEXITED(status_process)) { - if (verbose) { printf("Process %d terminated with exit status %d\n", child, (int)WEXITSTATUS(status_process)); } - exit(WEXITSTATUS(status_process)); - } - printf("Process %d terminated abnormally\n", child); - exit(status_process); - } - else { - //limiter code - if (verbose) { printf("Limiting process %d\n",child); } - limit_process(child, limit, include_children); - exit(0); - } - } - } - - while(1) { - //look for the target process..or wait for it - pid_t ret = 0; - if (pid_ok) { - //search by pid - ret = find_process_by_pid(pid); - if (ret == 0) { - printf("No process found\n"); - } - else if (ret < 0) { - printf("Process found but you aren't allowed to control it\n"); - } - } - else { - //search by file or path name - ret = find_process_by_name(exe); - if (ret == 0) { - printf("No process found\n"); - } - else if (ret < 0) { - printf("Process found but you aren't allowed to control it\n"); - } - else { - pid = ret; - } - } - if (ret > 0) { - if (ret == cpulimit_pid) { - printf("Target process %d is cpulimit itself! Aborting because it makes no sense\n", ret); - exit(1); - } - printf("Process %d found\n", pid); - //control - limit_process(pid, limit, include_children); - } - if (lazy) { break; } - sleep(2); - }; - - exit(0); -} diff --git a/Utilities/Tools/cpulimit/list.c b/Utilities/Tools/cpulimit/list.c deleted file mode 100644 index 2ac36708d4a08..0000000000000 --- a/Utilities/Tools/cpulimit/list.c +++ /dev/null @@ -1,148 +0,0 @@ -/** - * - * cpulimit - a CPU limiter for Linux - * - * Copyright (C) 2005-2012, by: Angelo Marletta - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include -#include - -#include "list.h" - -#define EMPTYLIST NULL - -void init_list(struct list *l,int keysize) { - l->first=l->last=NULL; - l->keysize=keysize; - l->count=0; -} - -struct list_node *add_elem(struct list *l,void *elem) { - struct list_node *newnode=(struct list_node*)malloc(sizeof(struct list_node)); - newnode->data=elem; - newnode->previous=l->last; - newnode->next=NULL; - if (l->count==0) { - l->first=l->last=newnode; - } - else { - l->last->next=newnode; - l->last=newnode; - } - l->count++; - return newnode; -} - -void delete_node(struct list *l,struct list_node *node) { - if (l->count==1) { - l->first=l->last=NULL; - } - else if (node==l->first) { - node->next->previous=NULL; - l->first=node->next; - } - else if (node==l->last) { - node->previous->next=NULL; - l->last=node->previous; - } - else { - node->previous->next=node->next; - node->next->previous=node->previous; - } - l->count--; - free(node); -} - -void destroy_node(struct list *l,struct list_node *node) { - free(node->data); - node->data=NULL; - delete_node(l,node); -} - -int is_empty_list(struct list *l) { - return (l->count==0?TRUE:FALSE); -} - -int get_list_count(struct list *l) { - return l->count; -} - -void *first_elem(struct list *l) { - return l->first->data; -} - -struct list_node *first_node(struct list *l) { - return l->first; -} - -void *last_elem(struct list *l) { - return l->last->data; -} - -struct list_node *last_node(struct list *l) { - return l->last; -} - -struct list_node *xlocate_node(struct list *l,void *elem,int offset,int length) { - struct list_node *tmp; - tmp=l->first; - while(tmp!=NULL) { - if(!memcmp((char*)tmp->data+offset,elem,length==0?l->keysize:length)) { return (tmp); } - tmp=tmp->next; - } - return EMPTYLIST; -} - -struct list_node *locate_node(struct list *l,void *elem) { - return(xlocate_node(l,elem,0,0)); -} - -void *xlocate_elem(struct list *l,void *elem,int offset,int length) { - struct list_node *node=xlocate_node(l,elem,offset,length); - return(node==NULL?NULL:node->data); -} - -void *locate_elem(struct list *l,void *elem) { - return(xlocate_elem(l,elem,0,0)); -} - -void clear_list(struct list *l) { - while(l->first!=EMPTYLIST) { - struct list_node *tmp; - tmp=l->first; - l->first=l->first->next; - free(tmp); - tmp=NULL; - } - l->last=EMPTYLIST; - l->count=0; -} - -void destroy_list(struct list *l) { - while(l->first!=EMPTYLIST) { - struct list_node *tmp; - tmp=l->first; - l->first=l->first->next; - free(tmp->data); - tmp->data=NULL; - free(tmp); - tmp=NULL; - } - l->last=EMPTYLIST; - l->count=0; -} diff --git a/Utilities/Tools/cpulimit/list.h b/Utilities/Tools/cpulimit/list.h deleted file mode 100644 index 0b43a2b39c0f3..0000000000000 --- a/Utilities/Tools/cpulimit/list.h +++ /dev/null @@ -1,138 +0,0 @@ -/** - * - * cpulimit - a CPU limiter for Linux - * - * Copyright (C) 2005-2012, by: Angelo Marletta - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef __LIST__ - -#define __LIST__ - -#ifndef TRUE - #define TRUE 1 - #define FALSE 0 -#endif - -struct list_node { - //pointer to the content of the node - void *data; - //pointer to previous node - struct list_node *previous; - //pointer to next node - struct list_node *next; -}; - -struct list { - //first node - struct list_node *first; - //last node - struct list_node *last; - //size of the search key in bytes - int keysize; - //element count - int count; -}; - -/* - * Initialize a list, with a specified key size - */ -void init_list(struct list *l,int keysize); - -/* - * Add a new element at the end of the list - * return the pointer to the new node - */ -struct list_node *add_elem(struct list *l,void *elem); - -/* - * Delete a node - */ -void delete_node(struct list *l,struct list_node *node); - -/* - * Delete a node from the list, even the content pointed by it - * Use only when the content is a dynamically allocated pointer - */ -void destroy_node(struct list *l,struct list_node *node); - -/* - * Check whether a list is empty or not - */ -int is_empty_list(struct list *l); - -/* - * Return the element count of the list - */ -int get_list_count(struct list *l); - -/* - * Return the first element (content of the node) from the list - */ -void *first_elem(struct list *l); - -/* - * Return the first node from the list - */ -struct list_node *first_node(struct list *l); - -/* - * Return the last element (content of the node) from the list - */ -void *last_elem(struct list *l); - -/* - * Return the last node from the list - */ -struct list_node *last_node(struct list *l); - -/* - * Search an element of the list by content - * the comparison is done from the specified offset and for a specified length - * if offset=0, the comparison starts from the address pointed by data - * if length=0, default keysize is used for length - * if the element is found, return the node address - * else return NULL - */ -struct list_node *xlocate_node(struct list *l,void *elem,int offset,int length); - -/* - * The same of xlocate_node(), but return the content of the node - */ -void *xlocate_elem(struct list *l,void *elem,int offset,int length); - -/* - * The same of calling xlocate_node() with offset=0 and length=0 - */ -struct list_node *locate_node(struct list *l,void *elem); - -/* - * The same of locate_node, but return the content of the node - */ -void *locate_elem(struct list *l,void *elem); - -/* - * Delete all the elements in the list - */ -void clear_list(struct list *l); - -/* - * Delete every element in the list, and free the memory pointed by all the node data - */ -void destroy_list(struct list *l); - -#endif diff --git a/Utilities/Tools/cpulimit/process_group.c b/Utilities/Tools/cpulimit/process_group.c deleted file mode 100644 index c5343e32bd9a1..0000000000000 --- a/Utilities/Tools/cpulimit/process_group.c +++ /dev/null @@ -1,219 +0,0 @@ -/** - * - * cpulimit - a CPU limiter for Linux - * - * Copyright (C) 2005-2012, by: Angelo Marletta - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#if defined(__APPLE__) || defined(__FREEBSD__) -#include -#endif - -#include -#include -#include -#include -#include - -#include - -#include "process_iterator.h" -#include "process_group.h" -#include "list.h" - -// look for a process by pid -// search_pid : pid of the wanted process -// return: pid of the found process, if successful -// negative pid, if the process does not exist or if the signal fails -int find_process_by_pid(pid_t pid) -{ - return (kill(pid,0)==0) ? pid : -pid; -} - -// look for a process with a given name -// process: the name of the wanted process. it can be an absolute path name to the executable file -// or just the file name -// return: pid of the found process, if it is found -// 0, if it's not found -// negative pid, if it is found but it's not possible to control it -int find_process_by_name(const char *process_name) -{ - //pid of the target process - pid_t pid = -1; - - //process iterator - struct process_iterator it; - struct process proc; - struct process_filter filter; - filter.pid = 0; - filter.include_children = 0; - init_process_iterator(&it, &filter); - while (get_next_process(&it, &proc) != -1) - { - //process found - if (strncmp(basename(proc.command), process_name, strlen(process_name))==0 && kill(pid,SIGCONT)==0) { - //process is ok! - pid = proc.pid; - break; - } - } - if (close_process_iterator(&it) != 0) { - exit(1); - } - if (pid >= 0) { - //ok, the process was found - return pid; - } - else { - //process not found - return 0; - } -} - -int init_process_group(struct process_group *pgroup, int target_pid, int include_children) -{ - //hashtable initialization - memset(&pgroup->proctable, 0, sizeof(pgroup->proctable)); - pgroup->target_pid = target_pid; - pgroup->include_children = include_children; - pgroup->proclist = (struct list*)malloc(sizeof(struct list)); - init_list(pgroup->proclist, 4); - memset(&pgroup->last_update, 0, sizeof(pgroup->last_update)); - update_process_group(pgroup); - return 0; -} - -int close_process_group(struct process_group *pgroup) -{ - int i; - int size = sizeof(pgroup->proctable) / sizeof(struct process*); - for (i=0; iproctable[i] != NULL) { - //free() history for each process - destroy_list(pgroup->proctable[i]); - free(pgroup->proctable[i]); - pgroup->proctable[i] = NULL; - } - } - clear_list(pgroup->proclist); - free(pgroup->proclist); - pgroup->proclist = NULL; - return 0; -} - -void remove_terminated_processes(struct process_group *pgroup) -{ - //TODO -} - -//return t1-t2 in microseconds (no overflow checks, so better watch out!) -static inline unsigned long timediff(const struct timeval *t1,const struct timeval *t2) -{ - return (t1->tv_sec - t2->tv_sec) * 1000000 + (t1->tv_usec - t2->tv_usec); -} - -//parameter in range 0-1 -#define ALFA 0.08 -#define MIN_DT 20 - -void update_process_group(struct process_group *pgroup) -{ - struct process_iterator it; - struct process tmp_process; - struct process_filter filter; - struct timeval now; - gettimeofday(&now, NULL); - //time elapsed from previous sample (in ms) - long dt = timediff(&now, &pgroup->last_update) / 1000; - filter.pid = pgroup->target_pid; - filter.include_children = pgroup->include_children; - init_process_iterator(&it, &filter); - clear_list(pgroup->proclist); - init_list(pgroup->proclist, 4); - - while (get_next_process(&it, &tmp_process) != -1) - { -// struct timeval t; -// gettimeofday(&t, NULL); -// printf("T=%ld.%ld PID=%d PPID=%d START=%d CPUTIME=%d\n", t.tv_sec, t.tv_usec, tmp_process.pid, tmp_process.ppid, tmp_process.starttime, tmp_process.cputime); - int hashkey = pid_hashfn(tmp_process.pid); - if (pgroup->proctable[hashkey] == NULL) - { - //empty bucket - pgroup->proctable[hashkey] = malloc(sizeof(struct list)); - struct process *new_process = malloc(sizeof(struct process)); - tmp_process.cpu_usage = -1; - memcpy(new_process, &tmp_process, sizeof(struct process)); - init_list(pgroup->proctable[hashkey], 4); - add_elem(pgroup->proctable[hashkey], new_process); - add_elem(pgroup->proclist, new_process); - } - else - { - //existing bucket - struct process *p = (struct process*)locate_elem(pgroup->proctable[hashkey], &tmp_process); - if (p == NULL) - { - //process is new. add it - struct process *new_process = malloc(sizeof(struct process)); - tmp_process.cpu_usage = -1; - memcpy(new_process, &tmp_process, sizeof(struct process)); - add_elem(pgroup->proctable[hashkey], new_process); - add_elem(pgroup->proclist, new_process); - } - else - { - assert(tmp_process.pid == p->pid); - assert(tmp_process.starttime == p->starttime); - add_elem(pgroup->proclist, p); - if (dt < MIN_DT) { - continue; - } - //process exists. update CPU usage - double sample = 1.0 * (tmp_process.cputime - p->cputime) / dt; - if (p->cpu_usage == -1) { - //initialization - p->cpu_usage = sample; - } - else { - //usage adjustment - p->cpu_usage = (1.0-ALFA) * p->cpu_usage + ALFA * sample; - } - p->cputime = tmp_process.cputime; - } - } - } - close_process_iterator(&it); - if (dt < MIN_DT) { - return; - } - pgroup->last_update = now; -} - -int remove_process(struct process_group *pgroup, int pid) -{ - int hashkey = pid_hashfn(pid); - if (pgroup->proctable[hashkey] == NULL) { - return 1; //nothing to delete - } - struct list_node *node = (struct list_node*)locate_node(pgroup->proctable[hashkey], &pid); - if (node == NULL) { - return 2; - } - delete_node(pgroup->proctable[hashkey], node); - return 0; -} diff --git a/Utilities/Tools/cpulimit/process_group.h b/Utilities/Tools/cpulimit/process_group.h deleted file mode 100644 index 5a5b581554a80..0000000000000 --- a/Utilities/Tools/cpulimit/process_group.h +++ /dev/null @@ -1,55 +0,0 @@ -/** - * - * cpulimit - a CPU limiter for Linux - * - * Copyright (C) 2005-2012, by: Angelo Marletta - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef __PROCESS_GROUP_H - -#define __PROCESS_GROUP_H - -#include "process_iterator.h" - -#include "list.h" - -#define PIDHASH_SZ 1024 -#define pid_hashfn(x) ((((x) >> 8) ^ (x)) & (PIDHASH_SZ - 1)) - -struct process_group -{ - //hashtable with all the processes (array of struct list of struct process) - struct list *proctable[PIDHASH_SZ]; - struct list *proclist; - pid_t target_pid; - int include_children; - struct timeval last_update; -}; - -int init_process_group(struct process_group *pgroup, int target_pid, int include_children); - -void update_process_group(struct process_group *pgroup); - -int close_process_group(struct process_group *pgroup); - -int find_process_by_pid(pid_t pid); - -int find_process_by_name(const char *process_name); - -int remove_process(struct process_group *pgroup, int pid); - -#endif diff --git a/Utilities/Tools/cpulimit/process_iterator.c b/Utilities/Tools/cpulimit/process_iterator.c deleted file mode 100644 index 8b4019d237f2b..0000000000000 --- a/Utilities/Tools/cpulimit/process_iterator.c +++ /dev/null @@ -1,49 +0,0 @@ -/** - * - * cpulimit - a CPU limiter for Linux - * - * Copyright (C) 2005-2012, by: Angelo Marletta - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include -#include -#include -#ifndef __APPLE__ -#include -#endif -#include -#include "process_iterator.h" - -//See this link to port to other systems: http://www.steve.org.uk/Reference/Unix/faq_8.html#SEC85 - -#ifdef __linux__ - -#include "process_iterator_linux.c" - -#elif defined __FreeBSD__ - -#include "process_iterator_freebsd.c" - -#elif defined __APPLE__ - -#include "process_iterator_apple.c" - -#else - -#error Platform not supported - -#endif diff --git a/Utilities/Tools/cpulimit/process_iterator.h b/Utilities/Tools/cpulimit/process_iterator.h deleted file mode 100644 index 70520b68a6e88..0000000000000 --- a/Utilities/Tools/cpulimit/process_iterator.h +++ /dev/null @@ -1,97 +0,0 @@ -/** - * - * cpulimit - a CPU limiter for Linux - * - * Copyright (C) 2005-2012, by: Angelo Marletta - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#ifndef __PROCESS_ITERATOR_H - -#define __PROCESS_ITERATOR_H - -#include -#include -#include - -//USER_HZ detection, from openssl code -#ifndef HZ -# if defined(_SC_CLK_TCK) \ - && (!defined(OPENSSL_SYS_VMS) || __CTRL_VER >= 70000000) -# define HZ ((double)sysconf(_SC_CLK_TCK)) -# else -# ifndef CLK_TCK -# ifndef _BSD_CLK_TCK_ /* FreeBSD hack */ -# define HZ 100.0 -# else /* _BSD_CLK_TCK_ */ -# define HZ ((double)_BSD_CLK_TCK_) -# endif -# else /* CLK_TCK */ -# define HZ ((double)CLK_TCK) -# endif -# endif -#endif - -#ifdef __FreeBSD__ -#include -#endif - -// process descriptor -struct process { - //pid of the process - pid_t pid; - //ppid of the process - pid_t ppid; - //start time (unix timestamp) - int starttime; - //cputime used by the process (in milliseconds) - int cputime; - //actual cpu usage estimation (value in range 0-1) - double cpu_usage; - //absolute path of the executable file - char command[PATH_MAX+1]; -}; - -struct process_filter { - int pid; - int include_children; - char program_name[PATH_MAX+1]; -}; - -struct process_iterator { -#ifdef __linux__ - DIR *dip; - int boot_time; -#elif defined __FreeBSD__ - kvm_t *kd; - struct kinfo_proc *procs; - int count; - int i; -#elif defined __APPLE__ - int i; - int count; - int *pidlist; -#endif - struct process_filter *filter; -}; - -int init_process_iterator(struct process_iterator *i, struct process_filter *filter); - -int get_next_process(struct process_iterator *i, struct process *p); - -int close_process_iterator(struct process_iterator *i); - -#endif diff --git a/Utilities/Tools/cpulimit/process_iterator_apple.c b/Utilities/Tools/cpulimit/process_iterator_apple.c deleted file mode 100644 index b878ed8c9a946..0000000000000 --- a/Utilities/Tools/cpulimit/process_iterator_apple.c +++ /dev/null @@ -1,148 +0,0 @@ -/** - * - * cpulimit - a CPU limiter for Linux - * - * Copyright (C) 2005-2012, by: Angelo Marletta - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - * - * Author: Simon Sigurdhsson - * - */ - -#include -#include -#include - -int unique_nonzero_ints(int* arr_in, int len_in, int* arr_out) { - int* source = arr_in; - if (arr_out == NULL) return -1; - if (arr_in == arr_out) { - source = malloc(sizeof(int)*len_in); - memcpy(source, arr_in, sizeof(int)*len_in); - memset(arr_out, -1, sizeof(int)*len_in); - } - int len_out = 0; - int i, j; - for (i=0; ii = 0; - /* Find out how much to allocate for it->pidlist */ - if ((it->count = proc_listpids(PROC_ALL_PIDS, 0, NULL, 0)) <= 0) { - fprintf(stderr, "proc_listpids: %s\n", strerror(errno)); - return -1; - } - /* Allocate and populate it->pidlist */ - if ((it->pidlist = (int *)malloc((it->count)*sizeof(int))) == NULL) { - fprintf(stderr, "malloc: %s\n", strerror(errno)); - } - if ((it->count = proc_listpids(PROC_ALL_PIDS, 0, it->pidlist, it->count)) <= 0) { - fprintf(stderr, "proc_listpids: %s\n", strerror(errno)); - return -1; - } - it->count = unique_nonzero_ints(it->pidlist, it->count, it->pidlist); - it->filter = filter; - return 0; -} - -static int pti2proc(struct proc_taskallinfo *ti, struct process *process) { - int bytes; - process->pid = ti->pbsd.pbi_pid; - process->ppid = ti->pbsd.pbi_ppid; - process->starttime = ti->pbsd.pbi_start_tvsec; - process->cputime = (ti->ptinfo.pti_total_user + ti->ptinfo.pti_total_system) / 1000000; - bytes = strlen(ti->pbsd.pbi_comm); - memcpy(process->command, ti->pbsd.pbi_comm, (bytes < PATH_MAX ? bytes : PATH_MAX) + 1); - return 0; -} - -static int get_process_pti(pid_t pid, struct proc_taskallinfo *ti) { - int bytes; - bytes = proc_pidinfo(pid, PROC_PIDTASKALLINFO, 0, ti, sizeof(*ti)); - if (bytes <= 0) { - if (!(errno & (EPERM | ESRCH))) { - fprintf(stderr, "proc_pidinfo: %s\n", strerror(errno)); - } - return -1; - } else if (bytes < sizeof(ti)) { - fprintf(stderr, "proc_pidinfo: too few bytes; expected %ld, got %d\n", sizeof(ti), bytes); - return -1; - } - return 0; -} - -int get_next_process(struct process_iterator *it, struct process *p) { - if (it->i == it->count) return -1; - if (it->filter->pid != 0 && !it->filter->include_children) { - struct proc_taskallinfo ti; - if (get_process_pti(it->filter->pid, &ti) != 0) { - it->i = it->count = 0; - return -1; - } - it->i = it->count = 1; - return pti2proc(&ti, p); - } - while (it->i < it->count) { - struct proc_taskallinfo ti; - if (get_process_pti(it->pidlist[it->i], &ti) != 0) { - it->i++; - continue; - } - if (ti.pbsd.pbi_flags & PROC_FLAG_SYSTEM) { - it->i++; - continue; - } - if (it->filter->pid != 0 && it->filter->include_children) { - pti2proc(&ti, p); - it->i++; - if (p->pid != it->pidlist[it->i - 1]) // I don't know why this can happen - continue; - if (p->pid != it->filter->pid && p->ppid != it->filter->pid) - continue; - return 0; - } - else if (it->filter->pid == 0) - { - pti2proc(&ti, p); - it->i++; - return 0; - } - } - return -1; -} - -int close_process_iterator(struct process_iterator *it) { - free(it->pidlist); - it->pidlist = NULL; - it->filter = NULL; - it->count = 0; - it->i = 0; - return 0; -} diff --git a/Utilities/Tools/cpulimit/process_iterator_freebsd.c b/Utilities/Tools/cpulimit/process_iterator_freebsd.c deleted file mode 100644 index a6381123e1251..0000000000000 --- a/Utilities/Tools/cpulimit/process_iterator_freebsd.c +++ /dev/null @@ -1,119 +0,0 @@ -/** - * - * cpulimit - a CPU limiter for Linux - * - * Copyright (C) 2005-2012, by: Angelo Marletta - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include -#include -#include -#include - -int init_process_iterator(struct process_iterator *it, struct process_filter *filter) { - char errbuf[_POSIX2_LINE_MAX]; - it->i = 0; - /* Open the kvm interface, get a descriptor */ - if ((it->kd = kvm_openfiles(NULL, _PATH_DEVNULL, NULL, O_RDONLY, errbuf)) == NULL) { - fprintf(stderr, "kvm_open: %s\n", errbuf); - return -1; - } - /* Get the list of processes. */ - if ((it->procs = kvm_getprocs(it->kd, KERN_PROC_PROC, 0, &it->count)) == NULL) { - kvm_close(it->kd); -// fprintf(stderr, "kvm_getprocs: %s\n", kvm_geterr(it->kd)); - return -1; - } - it->filter = filter; - return 0; -} - -static int kproc2proc(kvm_t *kd, struct kinfo_proc *kproc, struct process *proc) -{ - proc->pid = kproc->ki_pid; - proc->ppid = kproc->ki_ppid; - proc->cputime = kproc->ki_runtime / 1000; - proc->starttime = kproc->ki_start.tv_sec; - char **args = kvm_getargv(kd, kproc, sizeof(proc->command)); - if (args == NULL) return -1; - memcpy(proc->command, args[0], strlen(args[0]) + 1); - return 0; -} - -static int get_single_process(kvm_t *kd, pid_t pid, struct process *process) -{ - int count; - struct kinfo_proc *kproc = kvm_getprocs(kd, KERN_PROC_PID, pid, &count); - if (count == 0 || kproc == NULL) - { -// fprintf(stderr, "kvm_getprocs: %s\n", kvm_geterr(kd)); - return -1; - } - kproc2proc(kd, kproc, process); - return 0; -} - -int get_next_process(struct process_iterator *it, struct process *p) { - if (it->i == it->count) - { - return -1; - } - if (it->filter->pid != 0 && !it->filter->include_children) - { - if (get_single_process(it->kd, it->filter->pid, p) != 0) - { - it->i = it->count = 0; - return -1; - } - it->i = it->count = 1; - return 0; - } - while (it->i < it->count) - { - struct kinfo_proc *kproc = &(it->procs[it->i]); - if (kproc->ki_flag & P_SYSTEM) - { - // skip system processes - it->i++; - continue; - } - if (it->filter->pid != 0 && it->filter->include_children) - { - kproc2proc(it->kd, kproc, p); - it->i++; - if (p->pid != it->filter->pid && p->ppid != it->filter->pid) - continue; - return 0; - } - else if (it->filter->pid == 0) - { - kproc2proc(it->kd, kproc, p); - it->i++; - return 0; - } - } - return -1; -} - -int close_process_iterator(struct process_iterator *it) { - if (kvm_close(it->kd) == -1) { - fprintf(stderr, "kvm_getprocs: %s\n", kvm_geterr(it->kd)); - return -1; - } - return 0; -} - diff --git a/Utilities/Tools/cpulimit/process_iterator_linux.c b/Utilities/Tools/cpulimit/process_iterator_linux.c deleted file mode 100644 index d8d2cab3571da..0000000000000 --- a/Utilities/Tools/cpulimit/process_iterator_linux.c +++ /dev/null @@ -1,198 +0,0 @@ -/** - * - * cpulimit - a CPU limiter for Linux - * - * Copyright (C) 2005-2012, by: Angelo Marletta - * - * This program is free software; you can redistribute it and/or - * modify it under the terms of the GNU General Public License - * as published by the Free Software Foundation; either version 2 - * of the License, or (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program; if not, write to the Free Software - * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. - */ - -#include - -static int get_boot_time() -{ - int uptime = 0; - FILE *fp = fopen ("/proc/uptime", "r"); - if (fp != NULL) - { - char buf[BUFSIZ]; - char *b = fgets(buf, BUFSIZ, fp); - if (b == buf) - { - char *end_ptr; - double upsecs = strtod(buf, &end_ptr); - uptime = (int)upsecs; - } - fclose (fp); - } - time_t now = time(NULL); - return now - uptime; -} - -static int check_proc() -{ - struct statfs mnt; - if (statfs("/proc", &mnt) < 0) { - return 0; - } - if (mnt.f_type!=0x9fa0) { - return 0; - } - return 1; -} - -int init_process_iterator(struct process_iterator *it, struct process_filter *filter) -{ - if (!check_proc()) { - fprintf(stderr, "procfs is not mounted!\nAborting\n"); - exit(-2); - } - //open a directory stream to /proc directory - if ((it->dip = opendir("/proc")) == NULL) - { - perror("opendir"); - return -1; - } - it->filter = filter; - it->boot_time = get_boot_time(); - return 0; -} - -static int read_process_info(pid_t pid, struct process *p) -{ - static char buffer[1024]; - static char statfile[32]; - static char exefile[1024]; - p->pid = pid; - //read stat file - sprintf(statfile, "/proc/%d/stat", p->pid); - FILE *fd = fopen(statfile, "r"); - if (fd==NULL) { - return -1; - } - if (fgets(buffer, sizeof(buffer), fd)==NULL) { - fclose(fd); - return -1; - } - fclose(fd); - char *token = strtok(buffer, " "); - int i; - for (i=0; i<3; i++) { - token = strtok(NULL, " "); - } - p->ppid = atoi(token); - for (i=0; i<10; i++) { - token = strtok(NULL, " "); - } - p->cputime = atoi(token) * 1000 / HZ; - token = strtok(NULL, " "); - p->cputime += atoi(token) * 1000 / HZ; - for (i=0; i<7; i++) { - token = strtok(NULL, " "); - } - p->starttime = atoi(token) / sysconf(_SC_CLK_TCK); - //read command line - sprintf(exefile,"/proc/%d/cmdline", p->pid); - fd = fopen(exefile, "r"); - if (fgets(buffer, sizeof(buffer), fd)==NULL) { - fclose(fd); - return -1; - } - fclose(fd); - strcpy(p->command, buffer); - return 0; -} - -static pid_t getppid_of(pid_t pid) -{ - char statfile[20]; - char buffer[1024]; - sprintf(statfile, "/proc/%d/stat", pid); - FILE *fd = fopen(statfile, "r"); - if (fd==NULL) { - return -1; - } - if (fgets(buffer, sizeof(buffer), fd)==NULL) { - fclose(fd); - return -1; - } - fclose(fd); - char *token = strtok(buffer, " "); - int i; - for (i=0; i<3; i++) { - token = strtok(NULL, " "); - } - return atoi(token); -} - -static int is_child_of(pid_t child_pid, pid_t parent_pid) -{ - int ppid = child_pid; - while(ppid > 1 && ppid != parent_pid) { - ppid = getppid_of(ppid); - } - return ppid == parent_pid; -} - -int get_next_process(struct process_iterator *it, struct process *p) -{ - if (it->dip == NULL) - { - //end of processes - return -1; - } - if (it->filter->pid != 0 && !it->filter->include_children) - { - int ret = read_process_info(it->filter->pid, p); - //p->starttime += it->boot_time; - closedir(it->dip); - it->dip = NULL; - if (ret != 0) { - return -1; - } - return 0; - } - struct dirent *dit = NULL; - //read in from /proc and seek for process dirs - while ((dit = readdir(it->dip)) != NULL) { - if(strtok(dit->d_name, "0123456789") != NULL) { - continue; - } - p->pid = atoi(dit->d_name); - if (it->filter->pid != 0 && it->filter->pid != p->pid && !is_child_of(p->pid, it->filter->pid)) { - continue; - } - read_process_info(p->pid, p); - //p->starttime += it->boot_time; - break; - } - if (dit == NULL) - { - //end of processes - closedir(it->dip); - it->dip = NULL; - return -1; - } - return 0; -} - -int close_process_iterator(struct process_iterator *it) { - if (it->dip != NULL && closedir(it->dip) == -1) { - perror("closedir"); - return 1; - } - it->dip = NULL; - return 0; -} diff --git a/prodtests/full_system_test.sh b/prodtests/full_system_test.sh index e89d8ee09dee9..8496a31d577bc 100755 --- a/prodtests/full_system_test.sh +++ b/prodtests/full_system_test.sh @@ -29,7 +29,7 @@ fi # include jobutils, which notably brings # --> the taskwrapper as a simple control and monitoring tool -# (look inside the jobutils.sh file for documentation) +# (look inside the jobutils2.sh file for documentation) # --> utilities to query CPU count . ${O2_ROOT}/share/scripts/jobutils2.sh diff --git a/prodtests/full_system_test_ci_extra_tests.sh b/prodtests/full_system_test_ci_extra_tests.sh index 2d8a165f647fd..d0c4f23ef93c6 100755 --- a/prodtests/full_system_test_ci_extra_tests.sh +++ b/prodtests/full_system_test_ci_extra_tests.sh @@ -3,7 +3,7 @@ # Set of extra tests which may run after the full_system_test.sh # Particularly, they use the files generated by the full_system_test.sh # -. ${O2_ROOT}/share/scripts/jobutils.sh +. ${O2_ROOT}/share/scripts/jobutils2.sh if [ "0$O2_ROOT" == "0" ]; then eval "`alienv shell-helper`" diff --git a/prodtests/full_system_test_pipeline.sh b/prodtests/full_system_test_pipeline.sh index 235a590953d8e..bb29ea263dae0 100755 --- a/prodtests/full_system_test_pipeline.sh +++ b/prodtests/full_system_test_pipeline.sh @@ -14,9 +14,9 @@ # include jobutils, which notably brings # --> the taskwrapper as a simple control and monitoring tool -# (look inside the jobutils.sh file for documentation) +# (look inside the jobutils2.sh file for documentation) # --> utilities to query CPU count -. ${O2_ROOT}/share/scripts/jobutils.sh +. ${O2_ROOT}/share/scripts/jobutils2.sh export NEvents=${NEvents:-10} #550 for full TF (the number of PbPb events) export NEventsQED=${NEventsQED:-1000} #35000 for full TF diff --git a/prodtests/sim_challenge.sh b/prodtests/sim_challenge.sh index f5bbf8ab74ff8..a7c7e7f7993d7 100755 --- a/prodtests/sim_challenge.sh +++ b/prodtests/sim_challenge.sh @@ -8,7 +8,7 @@ # and it is advised to use that one. Some documentation can be found here: https://aliceo2group.github.io/simulation/docs/o2dpgworkflow/ # ------------ LOAD UTILITY FUNCTIONS ---------------------------- -. ${O2_ROOT}/share/scripts/jobutils.sh +. ${O2_ROOT}/share/scripts/jobutils2.sh # ----------- START WITH ACTUAL SCRIPT --------------------------- diff --git a/run/SimExamples/SimAsService_biasing1/run.sh b/run/SimExamples/SimAsService_biasing1/run.sh index 3bf8f51890fec..e038541ff6035 100755 --- a/run/SimExamples/SimAsService_biasing1/run.sh +++ b/run/SimExamples/SimAsService_biasing1/run.sh @@ -83,11 +83,11 @@ o2-sim-client.py --pid ${SERVICE2_PID} --command "--stop 1" sleep 1 # just some tmp safety-net to make sure all processes are really gone -. ${O2_ROOT}/share/scripts/jobutils.sh +. ${O2_ROOT}/share/scripts/jobutils2.sh for p in $(childprocs ${SERVICE1_PID}); do kill -9 ${p} done -. ${O2_ROOT}/share/scripts/jobutils.sh +. ${O2_ROOT}/share/scripts/jobutils2.sh for p in $(childprocs ${SERVICE2_PID}); do kill -9 ${p} done From a105f1ea0a7e8b336921abf0adaa46243690ea9e Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Mon, 20 Apr 2026 12:21:10 +0200 Subject: [PATCH 491/701] DPL Analysis: return span directly if the CCDB column is declared as a span (#15275) --- Framework/Core/include/Framework/ASoA.h | 78 +++++++++++++------------ 1 file changed, 41 insertions(+), 37 deletions(-) diff --git a/Framework/Core/include/Framework/ASoA.h b/Framework/Core/include/Framework/ASoA.h index 7a3307ae1a58c..96a4fe08cdd61 100644 --- a/Framework/Core/include/Framework/ASoA.h +++ b/Framework/Core/include/Framework/ASoA.h @@ -2452,43 +2452,47 @@ consteval static std::string_view namespace_prefix() }; \ [[maybe_unused]] static constexpr o2::framework::expressions::BindingNode _Getter_ { _Label_, _Name_::hash, o2::framework::expressions::selectArrowType<_Type_>() } -#define DECLARE_SOA_CCDB_COLUMN_FULL(_Name_, _Label_, _Getter_, _ConcreteType_, _CCDBQuery_) \ - struct _Name_ : o2::soa::Column, _Name_> { \ - static constexpr const char* mLabel = _Label_; \ - static constexpr const char* query = _CCDBQuery_; \ - static constexpr const uint32_t hash = crc32(namespace_prefix<_Name_>(), std::string_view{#_Getter_}); \ - using base = o2::soa::Column, _Name_>; \ - using type = std::span; \ - using column_t = _Name_; \ - _Name_(arrow::ChunkedArray const* column) \ - : o2::soa::Column, _Name_>(o2::soa::ColumnIterator>(column)) \ - { \ - } \ - \ - _Name_() = default; \ - _Name_(_Name_ const& other) = default; \ - _Name_& operator=(_Name_ const& other) = default; \ - \ - decltype(auto) _Getter_() const \ - { \ - static std::byte* payload = nullptr; \ - static _ConcreteType_* deserialised = nullptr; \ - static TClass* c = TClass::GetClass(#_ConcreteType_); \ - auto span = *mColumnIterator; \ - if (payload != (std::byte*)span.data()) { \ - payload = (std::byte*)span.data(); \ - delete deserialised; \ - TBufferFile f(TBufferFile::EMode::kRead, span.size(), (char*)span.data(), kFALSE); \ - deserialised = (_ConcreteType_*)soa::extractCCDBPayload((char*)payload, span.size(), c, "ccdb_object"); \ - } \ - return *deserialised; \ - } \ - \ - decltype(auto) \ - get() const \ - { \ - return _Getter_(); \ - } \ +#define DECLARE_SOA_CCDB_COLUMN_FULL(_Name_, _Label_, _Getter_, _ConcreteType_, _CCDBQuery_) \ + struct _Name_ : o2::soa::Column, _Name_> { \ + static constexpr const char* mLabel = _Label_; \ + static constexpr const char* query = _CCDBQuery_; \ + static constexpr const uint32_t hash = crc32(namespace_prefix<_Name_>(), std::string_view{#_Getter_}); \ + using base = o2::soa::Column, _Name_>; \ + using type = std::span; \ + using column_t = _Name_; \ + _Name_(arrow::ChunkedArray const* column) \ + : o2::soa::Column, _Name_>(o2::soa::ColumnIterator>(column)) \ + { \ + } \ + \ + _Name_() = default; \ + _Name_(_Name_ const& other) = default; \ + _Name_& operator=(_Name_ const& other) = default; \ + \ + decltype(auto) _Getter_() const \ + { \ + if constexpr (std::same_as<_ConcreteType_, std::span>) { \ + return *mColumnIterator; \ + } else { \ + static std::byte* payload = nullptr; \ + static _ConcreteType_* deserialised = nullptr; \ + static TClass* c = TClass::GetClass(#_ConcreteType_); \ + auto span = *mColumnIterator; \ + if (payload != (std::byte*)span.data()) { \ + payload = (std::byte*)span.data(); \ + delete deserialised; \ + TBufferFile f(TBufferFile::EMode::kRead, span.size(), (char*)span.data(), kFALSE); \ + deserialised = (_ConcreteType_*)soa::extractCCDBPayload((char*)payload, span.size(), c, "ccdb_object"); \ + } \ + return *deserialised; \ + } \ + } \ + \ + decltype(auto) \ + get() const \ + { \ + return _Getter_(); \ + } \ }; #define DECLARE_SOA_CCDB_COLUMN(_Name_, _Getter_, _ConcreteType_, _CCDBQuery_) \ From ded827ee065090f35bdeb91e2e0195b9b8d880ef Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Mon, 20 Apr 2026 14:30:43 +0200 Subject: [PATCH 492/701] ITS: restore previous UPC iteration (#15289) * ITS: fix upc iteration Signed-off-by: Felix Schlepper * ITS: simplify configurables to single one for Vertexer Signed-off-by: Felix Schlepper * ITS: make MaxIter a constant Signed-off-by: Felix Schlepper * ITS: remove GPU params Signed-off-by: Felix Schlepper --------- Signed-off-by: Felix Schlepper --- .../GPU/ITStrackingGPU/TrackingKernels.h | 24 +----- .../ITS/tracking/GPU/cuda/TimeFrameGPU.cu | 3 +- .../tracking/GPU/cuda/TrackerTraitsGPU.cxx | 32 +------- .../ITS/tracking/GPU/cuda/TrackingKernels.cu | 74 +++++-------------- .../include/ITStracking/Configuration.h | 6 +- .../tracking/include/ITStracking/Constants.h | 9 ++- .../include/ITStracking/TrackingConfigParam.h | 67 ++++------------- .../tracking/include/ITStracking/Vertexer.h | 2 +- .../include/ITStracking/VertexerTraits.h | 2 + .../ITSMFT/ITS/tracking/src/Configuration.cxx | 67 +++++++++-------- Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx | 7 -- .../ITS/tracking/src/TrackingConfigParam.cxx | 32 +------- .../ITS/tracking/src/TrackingInterface.cxx | 4 - .../ITSMFT/ITS/tracking/src/TrackingLinkDef.h | 3 - .../ITSMFT/ITS/tracking/src/Vertexer.cxx | 16 ++-- .../ITS/tracking/src/VertexerTraits.cxx | 38 ++++++---- GPU/Workflow/src/GPUWorkflowITS.cxx | 1 - prodtests/full-system-test/dpl-workflow.sh | 2 +- 18 files changed, 118 insertions(+), 271 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h index a83d9d0d52e8f..6a977f8fef21a 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h @@ -60,8 +60,6 @@ void countTrackletsInROFsHandler(const IndexTableUtils* utils, std::vector& radii, bounded_vector& mulScatAng, o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads, gpu::Streams& streams); template @@ -93,8 +91,6 @@ void computeTrackletsInROFsHandler(const IndexTableUtils* utils, std::vector& radii, bounded_vector& mulScatAng, o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads, gpu::Streams& streams); template @@ -113,8 +109,6 @@ void countCellsHandler(const Cluster** sortedClusters, const float cellDeltaTanLambdaSigma, const float nSigmaCut, o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads, gpu::Streams& streams); template @@ -132,8 +126,6 @@ void computeCellsHandler(const Cluster** sortedClusters, const float maxChi2ClusterAttachment, const float cellDeltaTanLambdaSigma, const float nSigmaCut, - const int nBlocks, - const int nThreads, gpu::Streams& streams); template @@ -150,8 +142,6 @@ void countCellNeighboursHandler(CellSeed** cellsLayersDevice, const unsigned int nCellsNext, const int maxCellNeighbours, o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads, gpu::Stream& stream); template @@ -167,8 +157,6 @@ void computeCellNeighboursHandler(CellSeed** cellsLayersDevice, const unsigned int nCells, const unsigned int nCellsNext, const int maxCellNeighbours, - const int nBlocks, - const int nThreads, gpu::Stream& stream); int filterCellNeighboursHandler(gpuPair*, @@ -193,9 +181,7 @@ void processNeighboursHandler(const int startLayer, const float maxChi2NDF, const o2::base::Propagator* propagator, const o2::base::PropagatorF::MatCorrType matCorrType, - o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads); + o2::its::ExternalAllocator* alloc); template void countTrackSeedHandler(CellSeed* trackSeeds, @@ -214,9 +200,7 @@ void countTrackSeedHandler(CellSeed* trackSeeds, const bool shiftRefToCluster, const o2::base::Propagator* propagator, const o2::base::PropagatorF::MatCorrType matCorrType, - o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads); + o2::its::ExternalAllocator* alloc); template void computeTrackSeedHandler(CellSeed* trackSeeds, @@ -237,9 +221,7 @@ void computeTrackSeedHandler(CellSeed* trackSeeds, const bool shiftRefToCluster, const o2::base::Propagator* propagator, const o2::base::PropagatorF::MatCorrType matCorrType, - o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads); + o2::its::ExternalAllocator* alloc); } // namespace o2::its #endif // ITSTRACKINGGPU_TRACKINGKERNELS_H_ diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu index a9b51580f9be7..bd5e7a8bc59f8 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu @@ -627,8 +627,7 @@ 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>{}); +constexpr auto kIterTags = makeIterTags(std::make_index_sequence{}); } // namespace detail template diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx index 3de2871dd458e..f7a416808fec7 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx @@ -18,7 +18,6 @@ #include "ITStrackingGPU/TrackerTraitsGPU.h" #include "ITStrackingGPU/TrackingKernels.h" -#include "ITStracking/TrackingConfigParam.h" #include "ITStracking/Constants.h" namespace o2::its @@ -63,8 +62,6 @@ void TrackerTraitsGPU::adoptTimeFrame(TimeFrame* tf) template void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int iVertex) { - const auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); - // start by queuing loading needed of two last layers for (int iLayer{NLayers}; iLayer-- > NLayers - 2;) { mTimeFrameGPU->createUsedClustersDevice(iteration, iLayer); @@ -109,8 +106,6 @@ void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int i this->mTrkParams[iteration].LayerRadii, mTimeFrameGPU->getMSangles(), mTimeFrameGPU->getFrameworkAllocator(), - conf.nBlocksLayerTracklets[iteration], - conf.nThreadsLayerTracklets[iteration], mTimeFrameGPU->getStreams()); mTimeFrameGPU->createTrackletsBuffers(iLayer); if (mTimeFrameGPU->getNTracklets()[iLayer] == 0) { @@ -144,8 +139,6 @@ void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int i this->mTrkParams[iteration].LayerRadii, mTimeFrameGPU->getMSangles(), mTimeFrameGPU->getFrameworkAllocator(), - conf.nBlocksLayerTracklets[iteration], - conf.nThreadsLayerTracklets[iteration], mTimeFrameGPU->getStreams()); } } @@ -153,8 +146,6 @@ void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int i template void TrackerTraitsGPU::computeLayerCells(const int iteration) { - auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); - // start by queuing loading needed of three last layers for (int iLayer{NLayers}; iLayer-- > NLayers - 3;) { mTimeFrameGPU->loadUnsortedClustersDevice(iteration, iLayer); @@ -194,8 +185,6 @@ void TrackerTraitsGPU::computeLayerCells(const int iteration) this->mTrkParams[iteration].CellDeltaTanLambdaSigma, this->mTrkParams[iteration].NSigmaCut, mTimeFrameGPU->getFrameworkAllocator(), - conf.nBlocksLayerCells[iteration], - conf.nThreadsLayerCells[iteration], mTimeFrameGPU->getStreams()); mTimeFrameGPU->createCellsBuffers(iLayer); if (mTimeFrameGPU->getNCells()[iLayer] == 0) { @@ -215,8 +204,6 @@ void TrackerTraitsGPU::computeLayerCells(const int iteration) this->mTrkParams[iteration].MaxChi2ClusterAttachment, this->mTrkParams[iteration].CellDeltaTanLambdaSigma, this->mTrkParams[iteration].NSigmaCut, - conf.nBlocksLayerCells[iteration], - conf.nThreadsLayerCells[iteration], mTimeFrameGPU->getStreams()); } } @@ -224,8 +211,6 @@ void TrackerTraitsGPU::computeLayerCells(const int iteration) template void TrackerTraitsGPU::findCellsNeighbours(const int iteration) { - const auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); - for (int iLayer{0}; iLayer < this->mTrkParams[iteration].NeighboursPerRoad(); ++iLayer) { const int currentLayerCellsNum{static_cast(mTimeFrameGPU->getNCells()[iLayer])}; const int nextLayerCellsNum{static_cast(mTimeFrameGPU->getNCells()[iLayer + 1])}; @@ -248,8 +233,6 @@ void TrackerTraitsGPU::findCellsNeighbours(const int iteration) nextLayerCellsNum, 1e2, mTimeFrameGPU->getFrameworkAllocator(), - conf.nBlocksFindNeighbours[iteration], - conf.nThreadsFindNeighbours[iteration], mTimeFrameGPU->getStream(iLayer)); mTimeFrameGPU->createNeighboursDevice(iLayer); if (mTimeFrameGPU->getNNeighbours()[iLayer] == 0) { @@ -267,8 +250,6 @@ void TrackerTraitsGPU::findCellsNeighbours(const int iteration) currentLayerCellsNum, nextLayerCellsNum, 1e2, - conf.nBlocksFindNeighbours[iteration], - conf.nThreadsFindNeighbours[iteration], mTimeFrameGPU->getStream(iLayer)); mTimeFrameGPU->getArrayNNeighbours()[iLayer] = filterCellNeighboursHandler(mTimeFrameGPU->getDeviceNeighbourPairs(iLayer), mTimeFrameGPU->getDeviceNeighbours(iLayer), @@ -282,7 +263,6 @@ void TrackerTraitsGPU::findCellsNeighbours(const int iteration) template void TrackerTraitsGPU::findRoads(const int iteration) { - auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); for (int startLevel{this->mTrkParams[iteration].CellsPerRoad()}; startLevel >= this->mTrkParams[iteration].CellMinimumLevel(); --startLevel) { const int minimumLayer{startLevel - 1}; bounded_vector> trackSeeds(this->getMemoryPool().get()); @@ -305,9 +285,7 @@ void TrackerTraitsGPU::findRoads(const int iteration) this->mTrkParams[0].MaxChi2NDF, mTimeFrameGPU->getDevicePropagator(), this->mTrkParams[0].CorrType, - mTimeFrameGPU->getFrameworkAllocator(), - conf.nBlocksProcessNeighbours[iteration], - conf.nThreadsProcessNeighbours[iteration]); + mTimeFrameGPU->getFrameworkAllocator()); } // fixme: I don't want to move tracks back and forth, but I need a way to use a thrust::allocator that is aware of our managed memory. if (trackSeeds.empty()) { @@ -334,9 +312,7 @@ void TrackerTraitsGPU::findRoads(const int iteration) this->mTrkParams[0].ShiftRefToCluster, mTimeFrameGPU->getDevicePropagator(), this->mTrkParams[0].CorrType, - mTimeFrameGPU->getFrameworkAllocator(), - conf.nBlocksTracksSeeds[iteration], - conf.nThreadsTracksSeeds[iteration]); + mTimeFrameGPU->getFrameworkAllocator()); mTimeFrameGPU->createTrackITSExtDevice(trackSeeds.size()); computeTrackSeedHandler(mTimeFrameGPU->getDeviceTrackSeeds(), mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), @@ -356,9 +332,7 @@ void TrackerTraitsGPU::findRoads(const int iteration) this->mTrkParams[0].ShiftRefToCluster, mTimeFrameGPU->getDevicePropagator(), this->mTrkParams[0].CorrType, - mTimeFrameGPU->getFrameworkAllocator(), - conf.nBlocksTracksSeeds[iteration], - conf.nThreadsTracksSeeds[iteration]); + mTimeFrameGPU->getFrameworkAllocator()); mTimeFrameGPU->downloadTrackITSExtDevice(); auto& tracks = mTimeFrameGPU->getTrackITSExt(); diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu index 795b568f6174d..54f92411a3df1 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -813,11 +813,9 @@ void countTrackletsInROFsHandler(const IndexTableUtils* utils, std::vector& radii, bounded_vector& mulScatAng, o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads, gpu::Streams& streams) { - gpu::computeLayerTrackletsMultiROFKernel<<>>( + gpu::computeLayerTrackletsMultiROFKernel<<<60, 256, 0, streams[layer].get()>>>( utils, rofMask, layer, @@ -874,11 +872,9 @@ void computeTrackletsInROFsHandler(const IndexTableUtils* utils, std::vector& radii, bounded_vector& mulScatAng, o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads, gpu::Streams& streams) { - gpu::computeLayerTrackletsMultiROFKernel<<>>( + gpu::computeLayerTrackletsMultiROFKernel<<<60, 256, 0, streams[layer].get()>>>( utils, rofMask, layer, @@ -909,7 +905,7 @@ void computeTrackletsInROFsHandler(const IndexTableUtils* utils, nTracklets[layer] = unique_end - tracklets_ptr; if (layer) { GPUChkErrS(cudaMemsetAsync(trackletsLUTsHost[layer], 0, (nClusters[layer] + 1) * sizeof(int), streams[layer].get())); - gpu::compileTrackletsLookupTableKernel<<>>( + gpu::compileTrackletsLookupTableKernel<<<60, 256, 0, streams[layer].get()>>>( spanTracklets[layer], trackletsLUTsHost[layer], nTracklets[layer]); @@ -934,11 +930,9 @@ void countCellsHandler( const float cellDeltaTanLambdaSigma, const float nSigmaCut, o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads, gpu::Streams& streams) { - gpu::computeLayerCellsKernel<<>>( + gpu::computeLayerCellsKernel<<<60, 256, 0, streams[layer].get()>>>( sortedClusters, // const Cluster** unsortedClusters, // const Cluster** tfInfo, // const TrackingFrameInfo** @@ -972,11 +966,9 @@ void computeCellsHandler( const float maxChi2ClusterAttachment, const float cellDeltaTanLambdaSigma, const float nSigmaCut, - const int nBlocks, - const int nThreads, gpu::Streams& streams) { - gpu::computeLayerCellsKernel<<>>( + gpu::computeLayerCellsKernel<<<60, 256, 0, streams[layer].get()>>>( sortedClusters, // const Cluster** unsortedClusters, // const Cluster** tfInfo, // const TrackingFrameInfo** @@ -1006,11 +998,9 @@ void countCellNeighboursHandler(CellSeed** cellsLayersDevice, const unsigned int nCellsNext, const int maxCellNeighbours, o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads, gpu::Stream& stream) { - gpu::computeLayerCellNeighboursKernel<<>>( + gpu::computeLayerCellNeighboursKernel<<<60, 256, 0, stream.get()>>>( cellsLayersDevice, neighboursLUT, neighboursIndexTable, @@ -1040,11 +1030,9 @@ void computeCellNeighboursHandler(CellSeed** cellsLayersDevice, const unsigned int nCells, const unsigned int nCellsNext, const int maxCellNeighbours, - const int nBlocks, - const int nThreads, gpu::Stream& stream) { - gpu::computeLayerCellNeighboursKernel<<>>( + gpu::computeLayerCellNeighboursKernel<<<60, 256, 0, stream.get()>>>( cellsLayersDevice, neighboursLUT, neighboursIndexTable, @@ -1090,9 +1078,7 @@ void processNeighboursHandler(const int startLayer, const float maxChi2NDF, const o2::base::Propagator* propagator, const o2::base::PropagatorF::MatCorrType matCorrType, - o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads) + o2::its::ExternalAllocator* alloc) { constexpr uint64_t Tag = qStr2Tag("ITS_PNH1"); alloc->pushTagOnStack(Tag); @@ -1101,7 +1087,7 @@ void processNeighboursHandler(const int startLayer, thrust::device_vector> foundSeedsTable(nCells[startLayer] + 1, 0, allocInt); auto nosync_policy = THRUST_NAMESPACE::par_nosync(gpu::TypedAllocator(alloc)).on(gpu::Stream::DefaultStream); - gpu::processNeighboursKernel<<>>( + gpu::processNeighboursKernel<<<60, 256>>>( startLayer, startLevel, allCellSeeds, @@ -1123,7 +1109,7 @@ void processNeighboursHandler(const int startLayer, thrust::device_vector> updatedCellId(foundSeedsTable.back(), 0, allocInt); thrust::device_vector, gpu::TypedAllocator>> updatedCellSeed(foundSeedsTable.back(), allocCellSeed); - gpu::processNeighboursKernel<<>>( + gpu::processNeighboursKernel<<<60, 256>>>( startLayer, startLevel, allCellSeeds, @@ -1155,7 +1141,7 @@ void processNeighboursHandler(const int startLayer, foundSeedsTable.resize(lastCellSeedSize + 1); thrust::fill(nosync_policy, foundSeedsTable.begin(), foundSeedsTable.end(), 0); - gpu::processNeighboursKernel<<>>( + gpu::processNeighboursKernel<<<60, 256>>>( iLayer, --level, allCellSeeds, @@ -1181,7 +1167,7 @@ void processNeighboursHandler(const int startLayer, updatedCellSeed.resize(foundSeeds); thrust::fill(nosync_policy, updatedCellSeed.begin(), updatedCellSeed.end(), CellSeed()); - gpu::processNeighboursKernel<<>>( + gpu::processNeighboursKernel<<<60, 256>>>( iLayer, level, allCellSeeds, @@ -1226,16 +1212,14 @@ void countTrackSeedHandler(CellSeed* trackSeeds, const bool shiftRefToCluster, const o2::base::Propagator* propagator, const o2::base::PropagatorF::MatCorrType matCorrType, - o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads) + o2::its::ExternalAllocator* alloc) { // TODO: the minPts&layerRadii is transfered twice // we should allocate this in constant memory and stop these // small transferes! thrust::device_vector minPts(minPtsHost); thrust::device_vector layerRadii(layerRadiiHost); - gpu::fitTrackSeedsKernel<<>>( + gpu::fitTrackSeedsKernel<<<60, 256>>>( trackSeeds, // CellSeed* foundTrackingFrameInfo, // TrackingFrameInfo** unsortedClusters, // Cluster** @@ -1276,13 +1260,11 @@ void computeTrackSeedHandler(CellSeed* trackSeeds, const bool shiftRefToCluster, const o2::base::Propagator* propagator, const o2::base::PropagatorF::MatCorrType matCorrType, - o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads) + o2::its::ExternalAllocator* alloc) { thrust::device_vector minPts(minPtsHost); thrust::device_vector layerRadii(layerRadiiHost); - gpu::fitTrackSeedsKernel<<>>( + gpu::fitTrackSeedsKernel<<<60, 256>>>( trackSeeds, // CellSeed* foundTrackingFrameInfo, // TrackingFrameInfo** unsortedClusters, // Cluster** @@ -1331,8 +1313,6 @@ template void countTrackletsInROFsHandler<7>(const IndexTableUtils<7>* utils, std::vector& radii, bounded_vector& mulScatAng, o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads, gpu::Streams& streams); template void computeTrackletsInROFsHandler<7>(const IndexTableUtils<7>* utils, @@ -1363,8 +1343,6 @@ template void computeTrackletsInROFsHandler<7>(const IndexTableUtils<7>* utils, std::vector& radii, bounded_vector& mulScatAng, o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads, gpu::Streams& streams); template void countCellsHandler<7>(const Cluster** sortedClusters, @@ -1382,8 +1360,6 @@ template void countCellsHandler<7>(const Cluster** sortedClusters, const float cellDeltaTanLambdaSigma, const float nSigmaCut, o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads, gpu::Streams& streams); template void computeCellsHandler<7>(const Cluster** sortedClusters, @@ -1400,8 +1376,6 @@ template void computeCellsHandler<7>(const Cluster** sortedClusters, const float maxChi2ClusterAttachment, const float cellDeltaTanLambdaSigma, const float nSigmaCut, - const int nBlocks, - const int nThreads, gpu::Streams& streams); template void countCellNeighboursHandler<7>(CellSeed<7>** cellsLayersDevice, @@ -1417,8 +1391,6 @@ template void countCellNeighboursHandler<7>(CellSeed<7>** cellsLayersDevice, const unsigned int nCellsNext, const int maxCellNeighbours, o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads, gpu::Stream& stream); template void computeCellNeighboursHandler(CellSeed<7>** cellsLayersDevice, @@ -1433,8 +1405,6 @@ template void computeCellNeighboursHandler(CellSeed<7>** cellsLayersDevice, const unsigned int nCells, const unsigned int nCellsNext, const int maxCellNeighbours, - const int nBlocks, - const int nThreads, gpu::Stream& stream); template void processNeighboursHandler<7>(const int startLayer, @@ -1452,9 +1422,7 @@ template void processNeighboursHandler<7>(const int startLayer, const float maxChi2NDF, const o2::base::Propagator* propagator, const o2::base::PropagatorF::MatCorrType matCorrType, - o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads); + o2::its::ExternalAllocator* alloc); template void countTrackSeedHandler(CellSeed<7>* trackSeeds, const TrackingFrameInfo** foundTrackingFrameInfo, @@ -1472,9 +1440,7 @@ template void countTrackSeedHandler(CellSeed<7>* trackSeeds, const bool shiftRefToCluster, const o2::base::Propagator* propagator, const o2::base::PropagatorF::MatCorrType matCorrType, - o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads); + o2::its::ExternalAllocator* alloc); template void computeTrackSeedHandler(CellSeed<7>* trackSeeds, const TrackingFrameInfo** foundTrackingFrameInfo, @@ -1494,8 +1460,6 @@ template void computeTrackSeedHandler(CellSeed<7>* trackSeeds, const bool shiftRefToCluster, const o2::base::Propagator* propagator, const o2::base::PropagatorF::MatCorrType matCorrType, - o2::its::ExternalAllocator* alloc, - const int nBlocks, - const int nThreads); + o2::its::ExternalAllocator* alloc); } // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h index 1f55a95ca0d65..dbce5e0dc08a7 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h @@ -84,9 +84,9 @@ struct TrackingParameters { struct VertexingParameters { std::string asString() const; - int nIterations = 1; // Number of vertexing passes to perform std::vector LayerZ = {16.333f + 1, 16.333f + 1, 16.333f + 1, 42.140f + 1, 42.140f + 1, 73.745f + 1, 73.745f + 1}; std::vector LayerRadii = {2.33959f, 3.14076f, 3.91924f, 19.6213f, 24.5597f, 34.388f, 39.3329f}; + int vertPerRofThreshold = 0; // Maximum number of vertices per ROF to trigger second a round int ZBins = 1; int PhiBins = 128; float zCut = -1.f; @@ -100,9 +100,7 @@ struct VertexingParameters { float finalSelectionZCut = -1.f; float duplicateDistance2Cut = -1.f; float tanLambdaCut = -1.f; - float vertNsigmaCut = -1.f; - float vertRadiusSigma = -1.f; - float trackletSigma = -1.f; + float NSigmaCut = -1; float maxZPositionAllowed = -1.f; int clusterContributorsCut = -1; int suppressLowMultDebris = -1; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h index 4b2528b62f057..f8009e3ce8008 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Constants.h @@ -31,12 +31,13 @@ constexpr float GB = MB * KB; constexpr bool DoTimeBenchmarks = true; constexpr bool SaveTimeBenchmarks = false; -GPUconstexpr() float Tolerance{1e-12}; // numerical tolerance -GPUconstexpr() int ClustersPerCell{3}; -GPUconstexpr() int UnusedIndex{-1}; -GPUconstexpr() float Resolution{0.0005f}; +GPUconstexpr() float Tolerance = 1e-12; // numerical tolerance +GPUconstexpr() int ClustersPerCell = 3; +GPUconstexpr() int UnusedIndex = -1; +GPUconstexpr() float Resolution = 0.0005f; GPUconstexpr() float Radl = 9.36f; // Radiation length of Si [cm] GPUconstexpr() float Rho = 2.33f; // Density of Si [g/cm^3] +GPUconstexpr() int MaxIter = 4; // Max. supported iterations namespace helpers { diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h index 5ffd55f715a1a..acb55eb1cf993 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h @@ -15,6 +15,7 @@ #include #include "CommonUtils/ConfigurableParam.h" #include "CommonUtils/ConfigurableParamHelper.h" +#include "ITStracking/Constants.h" namespace o2::its { @@ -36,11 +37,9 @@ struct VertexerParamConfig : public o2::conf::ConfigurableParamHelper { - // Use TGeo for mat. budget - static const int MaxIter = 4; static const int MinTrackLength = 4; static const int MaxTrackLength = 7; - bool useMatCorrTGeo = false; // use full geometry to corect for material budget accounting in the fits. Default is to use the material budget LUT. - bool useFastMaterial = false; // use faster material approximation for material budget accounting in the fits. - int addTimeError[7] = {0}; // configure the width of the window in BC to be considered for the tracking. - int minTrackLgtIter[MaxIter] = {}; // minimum track length at each iteration, used only if >0, otherwise use code defaults - uint8_t startLayerMask[MaxIter] = {}; // mask of start layer for this iteration (if >0) - float minPtIterLgt[MaxIter * (MaxTrackLength - MinTrackLength + 1)] = {}; // min.pT for given track length at this iteration, used only if >0, otherwise use code defaults - float sysErrY2[7] = {0}; // systematic error^2 in Y per layer - float sysErrZ2[7] = {0}; // systematic error^2 in Z per layer + + bool useMatCorrTGeo = false; // use full geometry to corect for material budget accounting in the fits. Default is to use the material budget LUT. + bool useFastMaterial = false; // use faster material approximation for material budget accounting in the fits. + int addTimeError[7] = {0}; // configure the width of the window in BC to be considered for the tracking. + int minTrackLgtIter[constants::MaxIter] = {}; // minimum track length at each iteration, used only if >0, otherwise use code defaults + uint8_t startLayerMask[constants::MaxIter] = {}; // mask of start layer for this iteration (if >0) + float minPtIterLgt[constants::MaxIter * (MaxTrackLength - MinTrackLength + 1)] = {}; // min.pT for given track length at this iteration, used only if >0, otherwise use code defaults + float sysErrY2[7] = {0}; // systematic error^2 in Y per layer + float sysErrZ2[7] = {0}; // systematic error^2 in Z per layer float maxChi2ClusterAttachment = -1.f; float maxChi2NDF = -1.f; float nSigmaCut = -1.f; @@ -91,7 +89,7 @@ struct TrackerParamConfig : public o2::conf::ConfigurableParamHelper { - static constexpr int MaxIter = TrackerParamConfig::MaxIter; - - /// Set nBlocks/nThreads to summarily override all kernel launch parameters in each iteration. - /// Parameters must start with nBlocks/nThreads. - static constexpr int OverrideValue{-1}; - static constexpr char const* BlocksName = "nBlocks"; - static constexpr char const* ThreadsName = "nThreads"; - int nBlocks = OverrideValue; - int nThreads = OverrideValue; - void maybeOverride() const; - - /// Individual kernel launch parameter for each iteration - int nBlocksLayerTracklets[MaxIter] = {60, 60, 60, 60}; - int nThreadsLayerTracklets[MaxIter] = {256, 256, 256, 256}; - - int nBlocksLayerCells[MaxIter] = {60, 60, 60, 60}; - int nThreadsLayerCells[MaxIter] = {256, 256, 256, 256}; - - int nBlocksFindNeighbours[MaxIter] = {60, 60, 60, 60}; - int nThreadsFindNeighbours[MaxIter] = {256, 256, 256, 256}; - - int nBlocksProcessNeighbours[MaxIter] = {60, 60, 60, 60}; - int nThreadsProcessNeighbours[MaxIter] = {256, 256, 256, 256}; - - int nBlocksTracksSeeds[MaxIter] = {60, 60, 60, 60}; - int nThreadsTracksSeeds[MaxIter] = {256, 256, 256, 256}; - - int nBlocksVtxComputeTracklets[2] = {60, 60}; - int nThreadsVtxComputeTracklets[2] = {256, 256}; - - int nBlocksVtxComputeMatching[2] = {60, 60}; - int nThreadsVtxComputeMatching[2] = {256, 256}; - - O2ParamDef(ITSGpuTrackingParamConfig, "ITSGpuTrackingParam"); -}; - } // namespace o2::its #endif diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h index 77218754dbda3..a045ba1639b13 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h @@ -91,7 +91,7 @@ class Vertexer void printEpilog(LogFunc& logger, const unsigned int trackletN01, const unsigned int trackletN12, - const unsigned selectedN, const unsigned int vertexN, const float initT, + const unsigned selectedN, const unsigned int vertexN, const unsigned int totalVertexN, const float trackletT, const float selecT, const float vertexT); void setNThreads(int n, std::shared_ptr& arena) { mTraits->setNThreads(n, arena); } diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h index 5b609c2fa6c85..1adb09551e326 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/VertexerTraits.h @@ -115,6 +115,8 @@ class VertexerTraits // Frame related quantities TimeFrameN* mTimeFrame = nullptr; // observer ptr private: + bool skipROF(int iteration, int rof) const; + std::shared_ptr mMemoryPool; std::shared_ptr mTaskArena; }; diff --git a/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx b/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx index 6c88b61f2df07..49bf9b5b1887d 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Configuration.cxx @@ -136,12 +136,12 @@ std::vector TrackingMode::getTrackingParameters(TrackingMode trackParams[3].TrackletMinPt = 0.1f; trackParams[3].CellDeltaTanLambdaSigma *= 4.; } - for (size_t ip = 0; ip < trackParams.size(); ip++) { + for (int ip = 0; ip < (int)trackParams.size(); ip++) { auto& param = trackParams[ip]; param.ZBins = 64; param.PhiBins = 32; // check if something was overridden via configurable params - if (ip < tc.MaxIter) { + if (ip < constants::MaxIter) { if (tc.startLayerMask[ip] > 0) { trackParams[2].StartLayerMask = tc.startLayerMask[ip]; } @@ -149,7 +149,7 @@ std::vector TrackingMode::getTrackingParameters(TrackingMode param.MinTrackLength = tc.minTrackLgtIter[ip]; } for (int ilg = tc.MaxTrackLength; ilg >= tc.MinTrackLength; ilg--) { - int lslot0 = (tc.MaxTrackLength - ilg), lslot = lslot0 + ip * (tc.MaxTrackLength - tc.MinTrackLength + 1); + int lslot0 = (tc.MaxTrackLength - ilg), lslot = lslot0 + (ip * (tc.MaxTrackLength - tc.MinTrackLength + 1)); if (tc.minPtIterLgt[lslot] > 0.) { param.MinPt[lslot0] = tc.minPtIterLgt[lslot]; } @@ -240,27 +240,15 @@ std::vector TrackingMode::getTrackingParameters(TrackingMode std::vector TrackingMode::getVertexingParameters(TrackingMode::Type mode) { const auto& vc = o2::its::VertexerParamConfig::Instance(); - std::vector vertParams; - if (mode == TrackingMode::Async) { - vertParams.resize(2); // The number of actual iterations will be set as a configKeyVal to allow for pp/PbPb choice - vertParams[1].phiCut = 0.015f; - vertParams[1].tanLambdaCut = 0.015f; - } else if (mode == TrackingMode::Sync) { - vertParams.resize(1); - } else if (mode == TrackingMode::Cosmics) { - vertParams.resize(1); - } else { - LOGP(fatal, "Unsupported ITS vertexing mode {} ", toString(mode)); - } - + std::vector vertParams(2); // The number of actual iterations will be set as a configKeyVal to allow for pp/PbPb choice // global parameters set for every iteration for (auto& p : vertParams) { + p.vertPerRofThreshold = vc.vertPerRofThreshold; p.SaveTimeBenchmarks = vc.saveTimeBenchmarks; p.PrintMemory = vc.printMemory; p.MaxMemory = vc.maxMemory; p.DropTFUponFailure = vc.dropTFUponFailure; - p.nIterations = vc.nIterations; - p.trackletSigma = vc.trackletSigma; + p.NSigmaCut = vc.nSigmaCut; p.maxZPositionAllowed = vc.maxZPositionAllowed; p.clusterContributorsCut = vc.clusterContributorsCut; p.suppressLowMultDebris = vc.suppressLowMultDebris; @@ -270,24 +258,35 @@ std::vector TrackingMode::getVertexingParameters(TrackingMo p.nThreads = vc.nThreads; p.ZBins = vc.ZBins; p.PhiBins = vc.PhiBins; - p.useTruthSeeding = vc.useTruthSeeding; + p.maxTrackletsPerCluster = vc.maxTrackletsPerCluster; + p.zCut = vc.zCut; + p.phiCut = vc.phiCut; + p.pairCut = vc.pairCut; + p.clusterCut = vc.clusterCut; + p.coarseZWindow = vc.coarseZWindow; + p.seedDedupZCut = vc.seedDedupZCut; + p.refitDedupZCut = vc.refitDedupZCut; + p.duplicateZCut = vc.duplicateZCut; + p.finalSelectionZCut = vc.finalSelectionZCut; + p.duplicateDistance2Cut = vc.duplicateDistance2Cut; + p.tanLambdaCut = vc.tanLambdaCut; + } + + if (mode == TrackingMode::Async) { + // relax for UPC iteration + vertParams[1].phiCut = 0.015f; + vertParams[1].tanLambdaCut = 0.015f; + vertParams[1].maxTrackletsPerCluster = 2000; + } else if (mode == TrackingMode::Sync || TrackingMode::Cosmics) { + vertParams.resize(1); + } else { + LOGP(fatal, "Unsupported ITS vertexing mode {} ", toString(mode)); + } + + if (vertParams.size() > vc.nIterations) { + vertParams.resize(vc.nIterations); } - // set for now outside to not disturb status quo - vertParams[0].vertNsigmaCut = vc.vertNsigmaCut; - vertParams[0].vertRadiusSigma = vc.vertRadiusSigma; - vertParams[0].maxTrackletsPerCluster = vc.maxTrackletsPerCluster; - vertParams[0].zCut = vc.zCut; - vertParams[0].phiCut = vc.phiCut; - vertParams[0].pairCut = vc.pairCut; - vertParams[0].clusterCut = vc.clusterCut; - vertParams[0].coarseZWindow = vc.coarseZWindow; - vertParams[0].seedDedupZCut = vc.seedDedupZCut; - vertParams[0].refitDedupZCut = vc.refitDedupZCut; - vertParams[0].duplicateZCut = vc.duplicateZCut; - vertParams[0].finalSelectionZCut = vc.finalSelectionZCut; - vertParams[0].duplicateDistance2Cut = vc.duplicateDistance2Cut; - vertParams[0].tanLambdaCut = vc.tanLambdaCut; return vertParams; } diff --git a/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx b/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx index dc032a46213a9..fa881789af296 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx @@ -31,12 +31,6 @@ using o2::its::constants::GB; template Tracker::Tracker(TrackerTraits* traits) : mTraits(traits) { - /// Initialise standard configuration with 1 iteration - mTrkParams.resize(1); - if (traits->isGPU()) { - ITSGpuTrackingParamConfig::Instance().maybeOverride(); - ITSGpuTrackingParamConfig::Instance().printKeyValues(true, true); - } } template @@ -46,7 +40,6 @@ void Tracker::clustersToTracks(const LogFunc& logger, const LogFunc& er double total{0}; mTraits->updateTrackingParameters(mTrkParams); - mTimeFrame->updateROFVertexLookupTable(); int maxNvertices{-1}; if (mTrkParams[0].PerPrimaryVertexProcessing) { diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackingConfigParam.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackingConfigParam.cxx index 3101c34d4ab8f..47b5f8ffffdb1 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackingConfigParam.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackingConfigParam.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,36 +9,6 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include - -#include "Framework/Logger.h" #include "ITStracking/TrackingConfigParam.h" - O2ParamImpl(o2::its::VertexerParamConfig); O2ParamImpl(o2::its::TrackerParamConfig); -O2ParamImpl(o2::its::ITSGpuTrackingParamConfig); - -namespace o2::its -{ - -void ITSGpuTrackingParamConfig::maybeOverride() const -{ - if (nBlocks == OverrideValue && nThreads == OverrideValue) { - return; - } - const auto name = getName(); - auto members = getDataMembers(); - for (auto member : *members) { - if (!member.name.ends_with(BlocksName) && !member.name.ends_with(ThreadsName)) { - if (nBlocks != OverrideValue && member.name.starts_with(BlocksName) && (member.value != nBlocks)) { - o2::conf::ConfigurableParam::setValue(name, member.name, nBlocks); - } - if (nThreads != OverrideValue && member.name.starts_with(ThreadsName) && (member.value != nThreads)) { - o2::conf::ConfigurableParam::setValue(name, member.name, nThreads); - } - } - } - LOGP(info, "Overwriting gpu threading parameters"); -} // namespace o2::its - -} // namespace o2::its diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx index eb0841888b03e..fcd9024a74709 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx @@ -209,10 +209,7 @@ void ITSTrackingInterface::run(framework::ProcessingContext& pc) auto clockROFspan = rofsinput[clockLayerId]; auto clockTiming = mTimeFrame->getROFOverlapTableView().getClockLayer(); for (auto iRof{0}; iRof < clockROFspan.size(); ++iRof) { - bounded_vector vtxVecLoc; auto& vtxROF = vertROFvec.emplace_back(clockROFspan[iRof]); - vtxROF.setFirstEntry((int)vertices.size()); - if (mRunVertexer) { auto vtxSpan = mTimeFrame->getPrimaryVertices(clockLayerId, iRof); if (o2::its::TrackerParamConfig::Instance().doUPCIteration) { @@ -231,7 +228,6 @@ void ITSTrackingInterface::run(framework::ProcessingContext& pc) } else { vtxROF.setFlag(o2::itsmft::ROFRecord::VtxStdMode); } - vtxROF.setNEntries((int)vtxSpan.size()); } } diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h b/Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h index 0640ff98297b9..46af692fe0c15 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackingLinkDef.h @@ -39,9 +39,6 @@ #pragma link C++ class o2::its::TrackerParamConfig + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::TrackerParamConfig> + ; -#pragma link C++ class o2::its::ITSGpuTrackingParamConfig + ; -#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::ITSGpuTrackingParamConfig> + ; - #pragma link C++ class o2::its::FastMultEstConfig + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::its::FastMultEstConfig> + ; diff --git a/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx b/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx index 222b4801a5767..cbff174634ec8 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx @@ -60,7 +60,7 @@ float Vertexer::clustersToVertices(LogFunc logger) float timeTracklet{0.f}, timeSelection{0.f}, timeVertexing{0.f}, timeInit{0.f}; try { - for (int iteration = 0; iteration < std::min(mVertParams[0].nIterations, (int)mVertParams.size()); ++iteration) { + for (int iteration = 0; iteration < (int)mVertParams.size(); ++iteration) { mMemoryPool->setMaxMemory(mVertParams[iteration].MaxMemory); unsigned int nTracklets01{0}, nTracklets12{0}; logger(fmt::format("=== ITS {} Seeding vertexer iteration {} summary:", mTraits->getName(), iteration)); @@ -71,12 +71,18 @@ float Vertexer::clustersToVertices(LogFunc logger) nTracklets01 = mTimeFrame->getTotalTrackletsTF(0); nTracklets12 = mTimeFrame->getTotalTrackletsTF(1); auto timeSelectionIteration = evaluateTask(&Vertexer::validateTracklets, StateNames[mCurState = Validating], iteration, evalLog, iteration); + const auto nVerticesBefore = mTimeFrame->getPrimaryVertices().size(); auto timeVertexingIteration = evaluateTask(&Vertexer::findVertices, StateNames[mCurState = Finding], iteration, evalLog, iteration); - printEpilog(logger, nTracklets01, nTracklets12, mTimeFrame->getNLinesTotal(), mTimeFrame->getPrimaryVertices().size(), timeInitIteration, timeTrackletIteration, timeSelectionIteration, timeVertexingIteration); + const auto nVerticesAfter = mTimeFrame->getPrimaryVertices().size(); + printEpilog(logger, nTracklets01, nTracklets12, mTimeFrame->getNLinesTotal(), nVerticesAfter - nVerticesBefore, nVerticesAfter, timeTrackletIteration, timeSelectionIteration, timeVertexingIteration); timeInit += timeInitIteration; timeTracklet += timeTrackletIteration; timeSelection += timeSelectionIteration; timeVertexing += timeVertexingIteration; + + // update LUT with all currently found vertices so in second iteration we can check vertPerROFThreshold + sortVertices(); + mTimeFrame->updateROFVertexLookupTable(); } } catch (const BoundedMemoryResource::MemoryLimitExceeded& err) { handleException(err); @@ -86,8 +92,6 @@ float Vertexer::clustersToVertices(LogFunc logger) LOGP(fatal, "Uncaught exception!"); } - sortVertices(); - return timeInit + timeTracklet + timeSelection + timeVertexing; } @@ -134,12 +138,12 @@ void Vertexer::adoptTimeFrame(TimeFrameN& tf) template void Vertexer::printEpilog(LogFunc& logger, const unsigned int trackletN01, const unsigned int trackletN12, - const unsigned selectedN, const unsigned int vertexN, const float initT, + const unsigned selectedN, const unsigned int vertexN, const unsigned int totalVertexN, const float trackletT, const float selecT, const float vertexT) { logger(fmt::format(" - {} Vertexer: found {} | {} tracklets in: {} ms", mTraits->getName(), trackletN01, trackletN12, trackletT)); logger(fmt::format(" - {} Vertexer: selected {} tracklets in: {} ms", mTraits->getName(), selectedN, selecT)); - logger(fmt::format(" - {} Vertexer: found {} vertices in: {} ms", mTraits->getName(), vertexN, vertexT)); + logger(fmt::format(" - {} Vertexer: found {} vertices in: {} ms (total: {})", mTraits->getName(), vertexN, vertexT, totalVertexN)); if (mVertParams[0].PrintMemory) { mTimeFrame->printArtefactsMemory(); mMemoryPool->print(); diff --git a/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx index a22d2d6c60990..d0baa65c49147 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/VertexerTraits.cxx @@ -168,15 +168,15 @@ void VertexerTraits::computeTracklets(const int iteration) { mTaskArena->execute([&] { tbb::parallel_for(0, mTimeFrame->getNrof(1), [&](const short pivotRofId) { - bool skipROF = !mTimeFrame->getROFMaskView().isROFEnabled(1, pivotRofId); + bool skip = skipROF(iteration, pivotRofId); const auto& rofRange01 = mTimeFrame->getROFOverlapTableView().getOverlap(1, 0, pivotRofId); for (auto targetRofId = rofRange01.getFirstEntry(); targetRofId < rofRange01.getEntriesBound(); ++targetRofId) { const auto timeErr = mTimeFrame->getROFOverlapTableView().getTimeStamp(0, targetRofId, 1, pivotRofId); trackleterKernelHost( - !skipROF ? mTimeFrame->getClustersOnLayer(targetRofId, 0) : gsl::span(), // Clusters to be matched with the next layer in target rof - !skipROF ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), // Clusters to be matched with the current layer in pivot rof - mTimeFrame->getUsedClustersROF(targetRofId, 0), // Span of the used clusters in the target rof - mTimeFrame->getIndexTable(targetRofId, 0).data(), // Index table to access the data on the next layer in target rof + !skip ? mTimeFrame->getClustersOnLayer(targetRofId, 0) : gsl::span(), // Clusters to be matched with the next layer in target rof + !skip ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), // Clusters to be matched with the current layer in pivot rof + mTimeFrame->getUsedClustersROF(targetRofId, 0), // Span of the used clusters in the target rof + mTimeFrame->getIndexTable(targetRofId, 0).data(), // Index table to access the data on the next layer in target rof mVrtParams[iteration].phiCut, mTimeFrame->getTracklets()[0], // Flat tracklet buffer mTimeFrame->getNTrackletsCluster(pivotRofId, 0), // Span of the number of tracklets per each cluster in pivot rof @@ -191,8 +191,8 @@ void VertexerTraits::computeTracklets(const int iteration) for (auto targetRofId = rofRange12.getFirstEntry(); targetRofId < rofRange12.getEntriesBound(); ++targetRofId) { const auto timeErr = mTimeFrame->getROFOverlapTableView().getTimeStamp(2, targetRofId, 1, pivotRofId); trackleterKernelHost( - !skipROF ? mTimeFrame->getClustersOnLayer(targetRofId, 2) : gsl::span(), - !skipROF ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), + !skip ? mTimeFrame->getClustersOnLayer(targetRofId, 2) : gsl::span(), + !skip ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), mTimeFrame->getUsedClustersROF(targetRofId, 2), mTimeFrame->getIndexTable(targetRofId, 2).data(), mVrtParams[iteration].phiCut, @@ -219,14 +219,14 @@ void VertexerTraits::computeTracklets(const int iteration) } tbb::parallel_for(0, mTimeFrame->getNrof(1), [&](const short pivotRofId) { - bool skipROF = !mTimeFrame->getROFMaskView().isROFEnabled(1, pivotRofId); + bool skip = skipROF(iteration, pivotRofId); const int globalOffsetPivot = mTimeFrame->getSortedStartIndex(pivotRofId, 1); const auto& rofRange01 = mTimeFrame->getROFOverlapTableView().getOverlap(1, 0, pivotRofId); for (auto targetRofId = rofRange01.getFirstEntry(); targetRofId < rofRange01.getEntriesBound(); ++targetRofId) { const auto timeErr = mTimeFrame->getROFOverlapTableView().getTimeStamp(0, targetRofId, 1, pivotRofId); trackleterKernelHost( - !skipROF ? mTimeFrame->getClustersOnLayer(targetRofId, 0) : gsl::span(), - !skipROF ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), + !skip ? mTimeFrame->getClustersOnLayer(targetRofId, 0) : gsl::span(), + !skip ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), mTimeFrame->getUsedClustersROF(targetRofId, 0), mTimeFrame->getIndexTable(targetRofId, 0).data(), mVrtParams[iteration].phiCut, @@ -243,8 +243,8 @@ void VertexerTraits::computeTracklets(const int iteration) for (auto targetRofId = rofRange12.getFirstEntry(); targetRofId < rofRange12.getEntriesBound(); ++targetRofId) { const auto timeErr = mTimeFrame->getROFOverlapTableView().getTimeStamp(2, targetRofId, 1, pivotRofId); trackleterKernelHost( - !skipROF ? mTimeFrame->getClustersOnLayer(targetRofId, 2) : gsl::span(), - !skipROF ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), + !skip ? mTimeFrame->getClustersOnLayer(targetRofId, 2) : gsl::span(), + !skip ? mTimeFrame->getClustersOnLayer(pivotRofId, 1) : gsl::span(), mTimeFrame->getUsedClustersROF(targetRofId, 2), mTimeFrame->getIndexTable(targetRofId, 2).data(), mVrtParams[iteration].phiCut, @@ -293,7 +293,7 @@ void VertexerTraits::computeTrackletMatching(const int iteration) tbb::blocked_range(0, (short)mTimeFrame->getNrof(1)), [&](const tbb::blocked_range& Rofs) { for (short pivotRofId = Rofs.begin(); pivotRofId < Rofs.end(); ++pivotRofId) { - if (mTimeFrame->getFoundTracklets(pivotRofId, 0).empty()) { + if (mTimeFrame->getFoundTracklets(pivotRofId, 0).empty() || skipROF(iteration, pivotRofId)) { continue; } mTimeFrame->getLines(pivotRofId).reserve(mTimeFrame->getNTrackletsCluster(pivotRofId, 0).size()); @@ -330,7 +330,6 @@ void VertexerTraits::computeVertices(const int iteration) const int nRofs = mTimeFrame->getNrof(1); std::vector> rofVertices(nRofs); std::vector> rofLabels(nRofs); - const float nsigmaCut = std::min(mVrtParams[iteration].vertNsigmaCut * mVrtParams[iteration].vertNsigmaCut * (mVrtParams[iteration].vertRadiusSigma * mVrtParams[iteration].vertRadiusSigma + mVrtParams[iteration].trackletSigma * mVrtParams[iteration].trackletSigma), 1.98f); const float pairCut2 = mVrtParams[iteration].pairCut * mVrtParams[iteration].pairCut; const float duplicateZCut = mVrtParams[iteration].duplicateZCut > 0.f ? mVrtParams[iteration].duplicateZCut : std::max(4.f * mVrtParams[iteration].pairCut, 0.5f * mVrtParams[iteration].clusterCut); const float duplicateDistance2Cut = mVrtParams[iteration].duplicateDistance2Cut > 0.f ? mVrtParams[iteration].duplicateDistance2Cut : std::max(16.f * pairCut2, 0.0625f * mVrtParams[iteration].clusterCut * mVrtParams[iteration].clusterCut); @@ -352,6 +351,9 @@ void VertexerTraits::computeVertices(const int iteration) settings.memoryPool = mMemoryPool; const auto processROF = [&](const int rofId) { + if (skipROF(iteration, rofId)) { + return; + } auto& lines = mTimeFrame->getLines(rofId); auto clusters = line_vertexer::buildClusters(std::span{lines.data(), lines.size()}, settings); deepVectorClear(lines); // not needed after @@ -508,7 +510,7 @@ void VertexerTraits::computeVertices(const int iteration) for (const auto sortedId : sortedIndices) { const auto& cluster = clusters[selectedIndices[sortedId]]; const auto beamDistance2 = clusterBeamDistance2(cluster); - if (!(beamDistance2 < nsigmaCut)) { + if (!(beamDistance2 < mVrtParams[iteration].NSigmaCut)) { continue; } if (cluster.getSize() < mVrtParams[iteration].clusterContributorsCut) { @@ -620,5 +622,11 @@ void VertexerTraits::setNThreads(int n, std::shared_ptr +bool VertexerTraits::skipROF(int iteration, int rof) const +{ + return iteration && (int)mTimeFrame->getROFVertexLookupTableView().getVertices(1, rof).getEntries() > mVrtParams[iteration].vertPerRofThreshold; +} + template class VertexerTraits<7>; } // namespace o2::its diff --git a/GPU/Workflow/src/GPUWorkflowITS.cxx b/GPU/Workflow/src/GPUWorkflowITS.cxx index fb27df2ec08b9..ac9834d3eacd1 100644 --- a/GPU/Workflow/src/GPUWorkflowITS.cxx +++ b/GPU/Workflow/src/GPUWorkflowITS.cxx @@ -40,7 +40,6 @@ int32_t GPURecoWorkflowSpec::runITSTracking(o2::framework::ProcessingContext& pc if (mNTFs == 1 && pc.services().get().inputTimesliceId == 0) { o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, o2::its::VertexerParamConfig::Instance().getName()), o2::its::VertexerParamConfig::Instance().getName()); o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, o2::its::TrackerParamConfig::Instance().getName()), o2::its::TrackerParamConfig::Instance().getName()); - o2::conf::ConfigurableParam::write(o2::base::NameConf::getConfigOutputFileName(pc.services().get().name, o2::its::ITSGpuTrackingParamConfig::Instance().getName()), o2::its::ITSGpuTrackingParamConfig::Instance().getName()); } return 0; } diff --git a/prodtests/full-system-test/dpl-workflow.sh b/prodtests/full-system-test/dpl-workflow.sh index e954f6875eb30..52407cc3a4073 100755 --- a/prodtests/full-system-test/dpl-workflow.sh +++ b/prodtests/full-system-test/dpl-workflow.sh @@ -119,7 +119,7 @@ EVE_OPT=" --jsons-folder $EDJSONS_DIR" # ITS vertexing settings if [[ $BEAMTYPE == "pp" || $LIGHTNUCLEI == "1" ]]; then - ITS_CONFIG_KEY+="ITSVertexerParam.phiCut=0.4;ITSVertexerParam.tanLambdaCut=0.17;ITSVertexerParam.pairCut=0.0317563;ITSVertexerParam.clusterCut=0.6640964;ITSVertexerParam.coarseZWindow=0.2049018;ITSVertexerParam.seedDedupZCut=0.0711793;ITSVertexerParam.refitDedupZCut=0.0680009;ITSVertexerParam.duplicateZCut=0.1582193;ITSVertexerParam.finalSelectionZCut=0.1081465;ITSVertexerParam.duplicateDistance2Cut=0.0117033;ITSVertexerParam.clusterContributorsCut=2;ITSVertexerParam.seedMemberRadiusZ=0;ITSVertexerParam.vertNsigmaCut=4.0;ITSVertexerParam.vertRadiusSigma=0.0452309;ITSVertexerParam.trackletSigma=0.0025941;ITSVertexerParam.suppressLowMultDebris=0;" + ITS_CONFIG_KEY+="ITSVertexerParam.phiCut=0.4;ITSVertexerParam.tanLambdaCut=0.17;ITSVertexerParam.pairCut=0.0317563;ITSVertexerParam.clusterCut=0.6640964;ITSVertexerParam.coarseZWindow=0.2049018;ITSVertexerParam.seedDedupZCut=0.0711793;ITSVertexerParam.refitDedupZCut=0.0680009;ITSVertexerParam.duplicateZCut=0.1582193;ITSVertexerParam.finalSelectionZCut=0.1081465;ITSVertexerParam.duplicateDistance2Cut=0.0117033;ITSVertexerParam.clusterContributorsCut=2;ITSVertexerParam.seedMemberRadiusZ=0;ITSVertexerParam.nSigmaCut=0.032841;ITSVertexerParam.suppressLowMultDebris=0;" fi if [[ $CTFINPUT != 1 ]]; then From 541bbcc89994e8595ec5090d1d9131277ad9a260 Mon Sep 17 00:00:00 2001 From: shahoian Date: Sun, 19 Apr 2026 19:13:34 +0200 Subject: [PATCH 493/701] Modernize to std::greater<> to please code checker --- Detectors/ITSMFT/ITS/tracking/src/LineVertexerHelpers.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Detectors/ITSMFT/ITS/tracking/src/LineVertexerHelpers.cxx b/Detectors/ITSMFT/ITS/tracking/src/LineVertexerHelpers.cxx index 592c22dedf347..cbb8d52571ec9 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/LineVertexerHelpers.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/LineVertexerHelpers.cxx @@ -743,7 +743,7 @@ bounded_vector> buildCoarseClusters(std::span using ActiveEntry = std::pair; bounded_vector activeEntries(settings.memoryPool.get()); - std::priority_queue, std::greater> activeByUpper(std::greater{}, std::move(activeEntries)); + std::priority_queue, std::greater<>> activeByUpper(std::greater<>{}, std::move(activeEntries)); bounded_vector activeMask(lineRefs.size(), 0, settings.memoryPool.get()); bounded_vector> activeByZBin(settings.memoryPool.get()); activeByZBin.reserve(nZBins); From 08549f4ee915deb8b18e205b2dd631a66c7c4114 Mon Sep 17 00:00:00 2001 From: Maximiliano Puccio Date: Tue, 21 Apr 2026 10:40:03 +0200 Subject: [PATCH 494/701] ALICE3: Add VD bandwidth monitoring macro (#15295) --- .../ALICE3/TRK/macros/test/CheckBandwidth.C | 561 +++++++++++++----- 1 file changed, 418 insertions(+), 143 deletions(-) diff --git a/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckBandwidth.C b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckBandwidth.C index 2087f88a87d6b..06d24361c7721 100644 --- a/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckBandwidth.C +++ b/Detectors/Upgrades/ALICE3/TRK/macros/test/CheckBandwidth.C @@ -9,17 +9,19 @@ // 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 +/// \file CheckBandwidth.C +/// \brief Simple macro to check TRK bandwidth #if !defined(__CLING__) || defined(__ROOTCLING__) #include #include #include +#include #include #include #include #include +#include #include #include #include @@ -38,7 +40,7 @@ namespace { -constexpr double DigitBits = 16.; +constexpr double DigitBits = 24.; constexpr double BunchCrossingNS = 25.; constexpr int ReadoutCycleBC = 18; constexpr int ReadoutCycleSimBC = 18; @@ -50,13 +52,15 @@ void CheckBandwidth(std::string digifile = "trkdigits.root", std::string inputGe gStyle->SetPalette(55); gStyle->SetOptStat(0); + // --- Drawing helpers --- + auto drawSummary = [](double averageValue, double peakValue, const char* unit) { TLatex latex; latex.SetNDC(); latex.SetTextSize(0.03); latex.SetTextAlign(13); - latex.DrawLatex(0.04, 0.05, Form("avg: %.3f %s", averageValue, unit)); - latex.DrawLatex(0.34, 0.05, Form("peak: %.3f %s", peakValue, unit)); + latex.DrawLatex(0.04, 0.06, Form("avg: %.3f %s", averageValue, unit)); + latex.DrawLatex(0.04, 0.03, Form("peak: %.3f %s", peakValue, unit)); }; auto drawCollisionSummary = [](double averageValue, double nonEmptyAverageValue, double peakValue) { @@ -69,17 +73,103 @@ void CheckBandwidth(std::string digifile = "trkdigits.root", std::string inputGe latex.DrawLatex(0.04, 0.06, Form("avg non-empty: %.3f collisions/ROF", nonEmptyAverageValue)); }; + auto drawCollisionInfoBox = [](double averageValue) { + const double effectiveIRRateHz = ReadoutCycleSeconds > 0. ? averageValue / ReadoutCycleSeconds : 0.; + TPaveText infoBox(0.55, 0.79, 0.88, 0.9, "NDC"); + infoBox.SetFillColor(0); + infoBox.SetBorderSize(1); + infoBox.SetTextAlign(12); + infoBox.SetTextSize(0.028); + infoBox.AddText(Form("effective IR: %.3f MHz", effectiveIRRateHz * 1.e-6)); + infoBox.AddText(Form("ROF length: %d BC", ReadoutCycleBC)); + infoBox.DrawClone(); + }; + + const TString outputPdf = "trk_bandwidth_report.pdf"; + bool pdfOpened = false; + TCanvas* lastPdfCanvas = nullptr; + auto appendCanvasToPdf = [&](TCanvas* canvas) { + if (!pdfOpened) { + canvas->Print(Form("%s[", outputPdf.Data())); + pdfOpened = true; + } + canvas->Print(outputPdf.Data()); + lastPdfCanvas = canvas; + }; + using namespace o2::base; using namespace o2::trk; TFile* f = TFile::Open("CheckBandwidth.root", "recreate"); - // Geometry + // --- Geometry --- + o2::base::GeometryManager::loadGeometry(inputGeom); auto* gman = o2::trk::GeometryTGeo::Instance(); gman->fillMatrixCache(o2::math_utils::bit2Mask(o2::math_utils::TransformType::L2G)); - // Collision Context + const int nVDPetals = gman->extractNumberOfPetalsVD(); + const int nVDLayers = gman->extractNumberOfLayersVD(); + const int nMLOTLayers = gman->getNumberOfLayersMLOT(); + const int nTotalLayers = nVDLayers + nMLOTLayers; + const int nChips = gman->getNumberOfChips(); + + // Precompute per-chip geometry — centralises all gman queries. + // globalLayer maps VD layers to [0, nVDLayers) and MLOT layers to [nVDLayers, nTotalLayers). + // disk == -1 identifies barrel chips (the only ones that produce digits in this detector). + struct ChipGeom { + int subDetID = -1, localLayer = -1, globalLayer = -1, disk = -1; + int stave = -1, halfStave = -1, petal = -1; + }; + std::vector chipGeom(nChips); + for (int chipID = 0; chipID < nChips; ++chipID) { + auto& g = chipGeom[chipID]; + g.subDetID = gman->getSubDetID(chipID); + g.localLayer = gman->getLayer(chipID); + g.disk = gman->getDisk(chipID); + g.globalLayer = g.localLayer + g.subDetID * nVDLayers; + g.stave = gman->getStave(chipID); + g.halfStave = std::max(0, gman->getHalfStave(chipID)); + g.petal = (g.subDetID == 0) ? gman->getPetalCase(chipID) : -1; + } + + // Number of barrel chips per global layer (used for per-layer bandwidth normalisation). + std::vector chipsPerLayer(nTotalLayers, 0u); + for (int chipID = 0; chipID < nChips; ++chipID) { + const auto& g = chipGeom[chipID]; + if (g.disk != -1 || g.globalLayer < 0 || g.globalLayer >= nTotalLayers) { + continue; + } + ++chipsPerLayer[g.globalLayer]; + } + + // MLOT sensor index within its half-stave, ordered by Z position. + // Precomputed here so the plotting loop only reads results. + std::vector chipSensorIndex(nChips, -1); + std::vector maxSensorsPerHalfStaveMLOT(nMLOTLayers, 0); + for (int layer = 0; layer < nMLOTLayers; ++layer) { + std::map, std::vector>> chipsPerHalfStave; + for (int chipID = 0; chipID < nChips; ++chipID) { + const auto& g = chipGeom[chipID]; + if (g.subDetID != 1 || g.localLayer != layer || g.disk != -1) { + continue; + } + const auto center = gman->getMatrixL2G(chipID)(o2::math_utils::Point3D(0.f, 0.f, 0.f)); + chipsPerHalfStave[{g.stave, g.halfStave}].push_back({center.Z(), chipID}); + } + for (auto& [key, chips] : chipsPerHalfStave) { + std::sort(chips.begin(), chips.end(), [](const auto& a, const auto& b) { + return std::abs(a.first - b.first) > 1.e-4 ? a.first < b.first : a.second < b.second; + }); + for (size_t i = 0; i < chips.size(); ++i) { + chipSensorIndex[chips[i].second] = (int)i; + } + maxSensorsPerHalfStaveMLOT[layer] = std::max(maxSensorsPerHalfStaveMLOT[layer], (int)chips.size()); + } + } + + // --- Collision context --- + TFile* ccFile = TFile::Open(collContextFile.data()); auto* digiContext = (o2::steer::DigitizationContext*)ccFile->Get("DigitizationContext"); const o2::InteractionRecord firstSampledIR{0, digiContext->getFirstOrbitForSampling()}; @@ -93,7 +183,6 @@ void CheckBandwidth(std::string digifile = "trkdigits.root", std::string inputGe if (nbc < 0) { continue; } - const size_t rofID = nbc / ReadoutCycleSimBC; if (rofID >= collisionsPerROF.size()) { collisionsPerROF.resize(rofID + 1, 0u); @@ -101,197 +190,383 @@ void CheckBandwidth(std::string digifile = "trkdigits.root", std::string inputGe ++collisionsPerROF[rofID]; } - // Digits + // --- Digits --- + TFile* digFile = TFile::Open(digifile.data()); TTree* digTree = (TTree*)digFile->Get("o2sim"); const int nDigitTreeEntries = digTree->GetEntries(); std::vector* digArr = nullptr; + std::vector* rofRecords = nullptr; digTree->SetBranchAddress("TRKDigit", &digArr); - - // Get Read Out Frame arrays - std::vector* ROFRecordArrray = nullptr; - digTree->SetBranchAddress("TRKDigitROF", &ROFRecordArrray); - std::vector& ROFRecordArrrayRef = *ROFRecordArrray; + digTree->SetBranchAddress("TRKDigitROF", &rofRecords); digTree->GetEntry(0); - if (nDigitTreeEntries > 1) { LOG(warning) << "Digit tree has " << nDigitTreeEntries << " entries, but this macro processes entry 0 only."; } - std::vector digitsPerChip(gman->getNumberOfChips(), 0ull); - std::vector maxDigitsPerROFPerChip(gman->getNumberOfChips(), 0u); - std::vector digitsInCurrentROFPerChip(gman->getNumberOfChips(), 0u); + const int nROFRec = (int)rofRecords->size(); + if (nROFRec != (int)collisionsPerROF.size()) { + LOG(fatal) << "Mismatch between number of ROF records in digit tree (" << nROFRec + << ") and number of ROFs computed from collisioncontext.root (" << collisionsPerROF.size() + << "). Check input files."; + } + + // --- Accumulate per-chip digit counts across all ROFs --- - const int nROFRec = (int)ROFRecordArrrayRef.size(); - const int nCollisionROFBins = std::max(nROFRec, static_cast(collisionsPerROF.size())); + const double rofNorm = nROFRec > 0 ? 1. / nROFRec : 0.; + const double bitsToGbps = ReadoutCycleSeconds > 0. ? DigitBits / ReadoutCycleSeconds / 1.e9 : 0.; - if (nCollisionROFBins > 0) { - auto* hCollisionsPerROF = new TH1D("h_collisions_per_rof", "Collisions per ROF;ROF id;N collisions", nCollisionROFBins, -0.5, nCollisionROFBins - 0.5); - double totalCollisionsPerROF = 0.; - double peakCollisionsPerROF = 0.; - int nNonEmptyROFs = 0; + std::vector digitsPerChip(nChips, 0ull); + std::vector maxDigitsPerROFPerChip(nChips, 0u); + std::vector digitsInCurrentROFPerChip(nChips, 0u); - for (int rofID = 0; rofID < nCollisionROFBins; ++rofID) { - const double nCollisions = rofID < static_cast(collisionsPerROF.size()) ? collisionsPerROF[rofID] : 0.; - hCollisionsPerROF->SetBinContent(rofID + 1, nCollisions); - totalCollisionsPerROF += nCollisions; - peakCollisionsPerROF = std::max(peakCollisionsPerROF, nCollisions); - if (nCollisions > 0.) { - ++nNonEmptyROFs; + for (unsigned int iROF = 0; iROF < rofRecords->size(); ++iROF) { + std::vector touchedChips; + const unsigned int rofStart = (*rofRecords)[iROF].getFirstEntry(); + const unsigned int rofEnd = rofStart + (*rofRecords)[iROF].getNEntries(); + + for (unsigned int iDigit = rofStart; iDigit < rofEnd; ++iDigit) { + if (iDigit % 1000 == 0) { + std::cout << "Reading digit " << iDigit << " / " << digArr->size() << "\r" << std::flush; } + const int chipID = (*digArr)[iDigit].getChipIndex(); + if (chipGeom[chipID].disk != -1) { + continue; + } + if (digitsInCurrentROFPerChip[chipID] == 0) { + touchedChips.push_back(chipID); + } + ++digitsPerChip[chipID]; + ++digitsInCurrentROFPerChip[chipID]; } - auto* canvCollisionsPerROF = new TCanvas("canvCollisionsPerROF", "Collisions per ROF", 1050, 1050); - canvCollisionsPerROF->SetTopMargin(0.08); - hCollisionsPerROF->Draw("hist"); - drawCollisionSummary(totalCollisionsPerROF / nCollisionROFBins, - nNonEmptyROFs > 0 ? totalCollisionsPerROF / nNonEmptyROFs : 0., - peakCollisionsPerROF); - canvCollisionsPerROF->SaveAs("trk_collisions_per_rof.png"); + for (const int chipID : touchedChips) { + maxDigitsPerROFPerChip[chipID] = std::max(maxDigitsPerROFPerChip[chipID], digitsInCurrentROFPerChip[chipID]); + digitsInCurrentROFPerChip[chipID] = 0; + } } - unsigned int rofIndex = 0; - unsigned int rofNEntries = 0; - - // LOOP on : ROFRecord array - for (unsigned int iROF = 0; iROF < ROFRecordArrrayRef.size(); iROF++) { - std::vector touchedChips; - - rofIndex = ROFRecordArrrayRef[iROF].getFirstEntry(); - rofNEntries = ROFRecordArrrayRef[iROF].getNEntries(); - - // LOOP on : digits array - for (unsigned int iDigit = rofIndex; iDigit < rofIndex + rofNEntries; iDigit++) { - if (iDigit % 1000 == 0) - std::cout << "Reading digit " << iDigit << " / " << digArr->size() << std::endl; + // --- Per-layer bandwidth distribution histograms (second scan over digits) --- - Int_t iDetID = (*digArr)[iDigit].getChipIndex(); - Int_t disk = gman->getDisk(iDetID); - Int_t subDetID = gman->getSubDetID(iDetID); + // Per-layer peak digit count (from per-chip maxima) — drives histogram binning. + std::vector maxDigitsPerLayer(nTotalLayers, 0u); + for (int chipID = 0; chipID < nChips; ++chipID) { + const auto& g = chipGeom[chipID]; + if (g.disk != -1 || g.globalLayer < 0 || g.globalLayer >= nTotalLayers) { + continue; + } + maxDigitsPerLayer[g.globalLayer] = std::max(maxDigitsPerLayer[g.globalLayer], maxDigitsPerROFPerChip[chipID]); + } - if (subDetID == 1 && disk == -1) { - if (digitsInCurrentROFPerChip[iDetID] == 0) { - touchedChips.push_back(iDetID); + std::vector hDigitsDistPerLayer(nTotalLayers, nullptr); + for (int l = 0; l < nTotalLayers; ++l) { + if (chipsPerLayer[l] == 0 || maxDigitsPerLayer[l] == 0) { + continue; + } + const int nBins = std::min((int)maxDigitsPerLayer[l] + 1, 200); + hDigitsDistPerLayer[l] = new TH1D(Form("h_digits_dist_layer%d", l), + Form("Layer %d;Fired pixels / ROF / chip;Probability", l), + nBins, -0.5, (double)maxDigitsPerLayer[l] + 0.5); + } + // digitsInCurrentROFPerChip is all zeros after the first scan — reuse it here. + { + std::vector touchedChips; + for (unsigned int iROF = 0; iROF < rofRecords->size(); ++iROF) { + touchedChips.clear(); + const unsigned int rofStart = (*rofRecords)[iROF].getFirstEntry(); + const unsigned int rofEnd = rofStart + (*rofRecords)[iROF].getNEntries(); + for (unsigned int iDigit = rofStart; iDigit < rofEnd; ++iDigit) { + const int chipID = (*digArr)[iDigit].getChipIndex(); + if (chipGeom[chipID].disk != -1) { + continue; } - digitsPerChip[iDetID]++; - ++digitsInCurrentROFPerChip[iDetID]; + if (digitsInCurrentROFPerChip[chipID] == 0) { + touchedChips.push_back(chipID); + } + ++digitsInCurrentROFPerChip[chipID]; + } + for (const int chipID : touchedChips) { + const int l = chipGeom[chipID].globalLayer; + if (hDigitsDistPerLayer[l]) { + hDigitsDistPerLayer[l]->Fill(digitsInCurrentROFPerChip[chipID]); + } + digitsInCurrentROFPerChip[chipID] = 0; } + } + } - } // end loop on digits array + // --- Per-layer bandwidth statistics, normalised by chips per layer --- + // + // avgDigitsPerROF : mean over chips of (total chip digits / nROFs) + // peakAvgDigitsPerROF : max over chips of (total chip digits / nROFs) + // avgMaxDigitsPerROF : mean over chips of (peak single-ROF digit count) + // peakMaxDigitsPerROF : max over chips of (peak single-ROF digit count) + // avg/peakBandwidthGbps derived from the avg/peak digit quantities above. - for (const auto chipID : touchedChips) { - maxDigitsPerROFPerChip[chipID] = std::max(maxDigitsPerROFPerChip[chipID], digitsInCurrentROFPerChip[chipID]); - digitsInCurrentROFPerChip[chipID] = 0; - } + struct LayerStats { + double avgDigitsPerROF = 0.; + double peakAvgDigitsPerROF = 0.; + double avgMaxDigitsPerROF = 0.; + double peakMaxDigitsPerROF = 0.; + double avgBandwidthGbps = 0.; + double peakBandwidthGbps = 0.; + }; + std::vector layerStats(nTotalLayers); - } // end loop on ROFRecords array + for (int chipID = 0; chipID < nChips; ++chipID) { + const auto& g = chipGeom[chipID]; + if (g.disk != -1 || g.globalLayer < 0 || g.globalLayer >= nTotalLayers) { + continue; + } + const int l = g.globalLayer; + const double avgDigits = digitsPerChip[chipID] * rofNorm; + const double maxDigits = (double)maxDigitsPerROFPerChip[chipID]; + layerStats[l].avgDigitsPerROF += avgDigits; + layerStats[l].avgMaxDigitsPerROF += maxDigits; + layerStats[l].peakAvgDigitsPerROF = std::max(layerStats[l].peakAvgDigitsPerROF, avgDigits); + layerStats[l].peakMaxDigitsPerROF = std::max(layerStats[l].peakMaxDigitsPerROF, maxDigits); + } + for (int l = 0; l < nTotalLayers; ++l) { + if (chipsPerLayer[l] > 0) { + const double norm = 1. / chipsPerLayer[l]; + layerStats[l].avgDigitsPerROF *= norm; + layerStats[l].avgMaxDigitsPerROF *= norm; + } + layerStats[l].avgBandwidthGbps = layerStats[l].avgDigitsPerROF * bitsToGbps; + layerStats[l].peakBandwidthGbps = layerStats[l].peakAvgDigitsPerROF * bitsToGbps; + } - const double rofNorm = nROFRec > 0 ? 1. / nROFRec : 0.; - const double bitsToMbps = ReadoutCycleSeconds > 0. ? DigitBits / ReadoutCycleSeconds / 1.e6 : 0.; - const int nMLOTLayers = gman->getNumberOfLayersMLOT(); + // --- Collision plots --- - for (int layer = 0; layer < nMLOTLayers; ++layer) { - int nStaves = gman->extractNumberOfStavesMLOT(layer); - std::map>> chipsPerStave; - std::vector sensorIdPerChip(gman->getNumberOfChips(), -1); - int maxSensorsPerStave = 0; + if (nROFRec > 0) { + auto* hCollisionsPerROF = new TH1D("h_collisions_per_rof", "Collisions per ROF;ROF id;N collisions", + nROFRec, -0.5, nROFRec - 0.5); + double totalCollisionsPerROF = 0.; + double peakCollisionsPerROF = 0.; + int nNonEmptyROFs = 0; - for (int chipID = 0; chipID < gman->getNumberOfChips(); ++chipID) { - if (gman->getSubDetID(chipID) != 1 || gman->getLayer(chipID) != layer) { - continue; + for (int rofID = 0; rofID < nROFRec; ++rofID) { + const double nColl = collisionsPerROF[rofID]; + hCollisionsPerROF->SetBinContent(rofID + 1, nColl); + totalCollisionsPerROF += nColl; + peakCollisionsPerROF = std::max(peakCollisionsPerROF, nColl); + if (nColl > 0.) { + ++nNonEmptyROFs; } - const int staveID = gman->getStave(chipID); - const auto sensorCenter = gman->getMatrixL2G(chipID)(o2::math_utils::Point3D(0.f, 0.f, 0.f)); - chipsPerStave[staveID].push_back({sensorCenter.Z(), chipID}); } - for (auto& [staveID, chips] : chipsPerStave) { - std::sort(chips.begin(), chips.end(), [](const auto& left, const auto& right) { - if (std::abs(left.first - right.first) > 1.e-4) { - return left.first < right.first; - } - return left.second < right.second; - }); - - for (size_t sensorIndex = 0; sensorIndex < chips.size(); ++sensorIndex) { - sensorIdPerChip[chips[sensorIndex].second] = sensorIndex; - } + const double avgCollisionsPerROF = totalCollisionsPerROF / nROFRec; + auto* canvCollisionsPerROF = new TCanvas("canvCollisionsPerROF", "Collisions per ROF", 1050, 1050); + canvCollisionsPerROF->SetTopMargin(0.08); + hCollisionsPerROF->Draw("hist"); + drawCollisionSummary(avgCollisionsPerROF, + nNonEmptyROFs > 0 ? totalCollisionsPerROF / nNonEmptyROFs : 0., + peakCollisionsPerROF); + drawCollisionInfoBox(avgCollisionsPerROF); + appendCanvasToPdf(canvCollisionsPerROF); + } - maxSensorsPerStave = std::max(maxSensorsPerStave, static_cast(chips.size())); + // --- VD plots --- + + auto* hVDDigitsPerROF = new TH2F("h_digits_per_rof_vd", + "VD average digits per ROF;petal id;layer id;digits / ROF", + nVDPetals, -0.5, nVDPetals - 0.5, nVDLayers, -0.5, nVDLayers - 0.5); + auto* hVDMaxDigitsPerROF = new TH2F("h_max_digits_per_rof_vd", + "VD max digits in one ROF;petal id;layer id;max digits / ROF", + nVDPetals, -0.5, nVDPetals - 0.5, nVDLayers, -0.5, nVDLayers - 0.5); + auto* hVDBandwidth = new TH2F("h_bandwidth_vd", + "VD bandwidth map;petal id;layer id;bandwidth (Gbit/s)", + nVDPetals, -0.5, nVDPetals - 0.5, nVDLayers, -0.5, nVDLayers - 0.5); + + for (auto* hist : {hVDDigitsPerROF, hVDMaxDigitsPerROF, hVDBandwidth}) { + for (int petalID = 0; petalID < nVDPetals; ++petalID) { + hist->GetXaxis()->SetBinLabel(petalID + 1, Form("%d", petalID)); } + for (int layerID = 0; layerID < nVDLayers; ++layerID) { + hist->GetYaxis()->SetBinLabel(layerID + 1, Form("%d", layerID)); + } + hist->GetXaxis()->SetNdivisions(0, kFALSE); + hist->GetYaxis()->SetNdivisions(0, kFALSE); + hist->LabelsOption("h", "X"); + hist->LabelsOption("h", "Y"); + } - if (maxSensorsPerStave == 0) { + double totalVDAvgDigits = 0., peakVDAvgDigits = 0.; + double totalVDMaxDigits = 0., peakVDMaxDigits = 0.; + double totalVDBandwidth = 0., peakVDBandwidth = 0.; + + for (int chipID = 0; chipID < nChips; ++chipID) { + const auto& g = chipGeom[chipID]; + if (g.subDetID != 0 || g.disk != -1 || g.localLayer < 0 || g.localLayer >= nVDLayers) { continue; } + if (g.petal < 0 || g.petal >= nVDPetals) { + continue; + } + const double avgDigits = digitsPerChip[chipID] * rofNorm; + const double maxDigits = (double)maxDigitsPerROFPerChip[chipID]; + const double bandwidth = avgDigits * bitsToGbps; + + hVDDigitsPerROF->SetBinContent(g.petal + 1, g.localLayer + 1, avgDigits); + hVDMaxDigitsPerROF->SetBinContent(g.petal + 1, g.localLayer + 1, maxDigits); + hVDBandwidth->SetBinContent(g.petal + 1, g.localLayer + 1, bandwidth); + + totalVDAvgDigits += avgDigits; + totalVDMaxDigits += maxDigits; + totalVDBandwidth += bandwidth; + peakVDAvgDigits = std::max(peakVDAvgDigits, avgDigits); + peakVDMaxDigits = std::max(peakVDMaxDigits, maxDigits); + peakVDBandwidth = std::max(peakVDBandwidth, bandwidth); + } - auto* hDigitsPerROF = new TH2F(Form("h_digits_per_rof_layer%d", layer), - Form("Layer %d average digits per ROF;stave id;sensor id in stave;digits / ROF", layer), - nStaves, -0.5, nStaves - 0.5, maxSensorsPerStave, -0.5, maxSensorsPerStave - 0.5); - auto* hMaxDigitsPerROF = new TH2F(Form("h_max_digits_per_rof_layer%d", layer), - Form("Layer %d max digits in one ROF;stave id;sensor id in stave;max digits / ROF", layer), - nStaves, -0.5, nStaves - 0.5, maxSensorsPerStave, -0.5, maxSensorsPerStave - 0.5); - auto* hBandwidth = new TH2F(Form("h_bandwidth_layer%d", layer), - Form("Layer %d bandwidth map;stave id;sensor id in stave;bandwidth (Mbit/s)", layer), - nStaves, -0.5, nStaves - 0.5, maxSensorsPerStave, -0.5, maxSensorsPerStave - 0.5); - double totalAvgDigitsPerROF = 0.; - double totalMaxDigitsPerROF = 0.; - double totalBandwidthMbps = 0.; - double peakAvgDigitsPerROF = 0.; - double peakMaxDigitsPerROF = 0.; - double peakBandwidthMbps = 0.; - int nFilledSensors = 0; + const int nVDBarrelChips = std::accumulate(chipsPerLayer.begin(), chipsPerLayer.begin() + nVDLayers, 0); + const double normVD = nVDBarrelChips > 0 ? 1. / nVDBarrelChips : 0.; + const double avgVDAvgDigits = totalVDAvgDigits * normVD; + const double avgVDMaxDigits = totalVDMaxDigits * normVD; + const double avgVDBandwidth = totalVDBandwidth * normVD; + + auto* canvVDBandwidth = new TCanvas("canvBandwidthVD", "VD bandwidth", 1050, 1050); + canvVDBandwidth->SetTopMargin(0.08); + canvVDBandwidth->SetRightMargin(0.18); + hVDBandwidth->GetZaxis()->SetRangeUser(0., avgVDBandwidth > 0. ? 3. * avgVDBandwidth : 1.); + hVDBandwidth->SetMarkerSize(1.8); + hVDBandwidth->Draw("colz text"); + drawSummary(avgVDBandwidth, peakVDBandwidth, "Gbit/s"); + appendCanvasToPdf(canvVDBandwidth); + + auto* canvVDDigits = new TCanvas("canvDigitsVD", "VD digits per ROF", 1050, 1050); + canvVDDigits->SetTopMargin(0.08); + canvVDDigits->SetRightMargin(0.18); + hVDDigitsPerROF->SetMarkerSize(1.8); + hVDDigitsPerROF->Draw("colz text"); + drawSummary(avgVDAvgDigits, peakVDAvgDigits, "digits/ROF"); + appendCanvasToPdf(canvVDDigits); + + auto* canvVDMaxDigits = new TCanvas("canvMaxDigitsVD", "VD max digits per ROF", 1050, 1050); + canvVDMaxDigits->SetTopMargin(0.08); + canvVDMaxDigits->SetRightMargin(0.18); + hVDMaxDigitsPerROF->SetMarkerSize(1.8); + hVDMaxDigitsPerROF->Draw("colz text"); + drawSummary(avgVDMaxDigits, peakVDMaxDigits, "digits/ROF"); + appendCanvasToPdf(canvVDMaxDigits); + + // --- MLOT per-layer plots --- - for (int chipID = 0; chipID < gman->getNumberOfChips(); ++chipID) { - if (gman->getSubDetID(chipID) != 1 || gman->getLayer(chipID) != layer) { + for (int layer = 0; layer < nMLOTLayers; ++layer) { + if (maxSensorsPerHalfStaveMLOT[layer] == 0) { + continue; + } + const int outputLayer = nVDLayers + layer; + const int nStaves = gman->extractNumberOfStavesMLOT(layer); + const int nHalfStaves = std::max(1, gman->getNumberOfHalfStaves(layer)); + const int maxSensors = maxSensorsPerHalfStaveMLOT[layer]; + + auto* hDigitsPerROF = new TH2F(Form("h_digits_per_rof_layer%d", outputLayer), + Form("Layer %d average digits per ROF;stave id / half-stave;sensor id in half-stave;digits / ROF", outputLayer), + nStaves * nHalfStaves, -0.5, nStaves - 0.5, maxSensors, -0.5, maxSensors - 0.5); + auto* hMaxDigitsPerROF = new TH2F(Form("h_max_digits_per_rof_layer%d", outputLayer), + Form("Layer %d max digits in one ROF;stave id / half-stave;sensor id in half-stave;max digits / ROF", outputLayer), + nStaves * nHalfStaves, -0.5, nStaves - 0.5, maxSensors, -0.5, maxSensors - 0.5); + auto* hBandwidth = new TH2F(Form("h_bandwidth_layer%d", outputLayer), + Form("Layer %d bandwidth map;stave id / half-stave;sensor id in half-stave;bandwidth (Gbit/s)", outputLayer), + nStaves * nHalfStaves, -0.5, nStaves - 0.5, maxSensors, -0.5, maxSensors - 0.5); + + for (int chipID = 0; chipID < nChips; ++chipID) { + const auto& g = chipGeom[chipID]; + if (g.subDetID != 1 || g.localLayer != layer || g.disk != -1) { continue; } - - const int staveID = gman->getStave(chipID); - const int sensorID = sensorIdPerChip[chipID]; - const double avgDigitsPerROF = digitsPerChip[chipID] * rofNorm; - const double maxDigitsPerROF = maxDigitsPerROFPerChip[chipID]; - const double bandwidthMbps = avgDigitsPerROF * bitsToMbps; - - if (sensorID >= 0) { - hDigitsPerROF->Fill(staveID, sensorID, avgDigitsPerROF); - hMaxDigitsPerROF->Fill(staveID, sensorID, maxDigitsPerROF); - hBandwidth->Fill(staveID, sensorID, bandwidthMbps); - totalAvgDigitsPerROF += avgDigitsPerROF; - totalMaxDigitsPerROF += maxDigitsPerROF; - totalBandwidthMbps += bandwidthMbps; - peakAvgDigitsPerROF = std::max(peakAvgDigitsPerROF, avgDigitsPerROF); - peakMaxDigitsPerROF = std::max(peakMaxDigitsPerROF, maxDigitsPerROF); - peakBandwidthMbps = std::max(peakBandwidthMbps, bandwidthMbps); - ++nFilledSensors; + const int sensorID = chipSensorIndex[chipID]; + if (sensorID < 0) { + continue; } + const double staveBinX = g.stave + (g.halfStave + 0.5) / nHalfStaves - 0.5; + const double avgDigits = digitsPerChip[chipID] * rofNorm; + const double maxDigits = (double)maxDigitsPerROFPerChip[chipID]; + + hDigitsPerROF->Fill(staveBinX, sensorID, avgDigits); + hMaxDigitsPerROF->Fill(staveBinX, sensorID, maxDigits); + hBandwidth->Fill(staveBinX, sensorID, avgDigits * bitsToGbps); } - auto* canvLayer = new TCanvas(Form("canvBandwidthLayer%d", layer), Form("Layer %d bandwidth", layer), 1050, 1050); + const auto& ls = layerStats[outputLayer]; + + auto* canvLayer = new TCanvas(Form("canvBandwidthLayer%d", outputLayer), Form("Layer %d bandwidth", outputLayer), 1050, 1050); canvLayer->SetTopMargin(0.08); canvLayer->SetRightMargin(0.18); - const double avgDigitsPerROFLayer = nFilledSensors > 0 ? totalAvgDigitsPerROF / nFilledSensors : 0.; - const double avgMaxDigitsPerROFLayer = nFilledSensors > 0 ? totalMaxDigitsPerROF / nFilledSensors : 0.; - const double avgBandwidthMbps = nFilledSensors > 0 ? totalBandwidthMbps / nFilledSensors : 0.; - hBandwidth->GetZaxis()->SetRangeUser(0., avgBandwidthMbps > 0. ? 3. * avgBandwidthMbps : 1.); + hBandwidth->GetZaxis()->SetRangeUser(0., ls.avgBandwidthGbps > 0. ? 3. * ls.avgBandwidthGbps : 1.); hBandwidth->Draw("colz"); - drawSummary(avgBandwidthMbps, peakBandwidthMbps, "Mbit/s"); - canvLayer->SaveAs(Form("trk_layer%d_bandwidth_map.png", layer)); + drawSummary(ls.avgBandwidthGbps, ls.peakBandwidthGbps, "Gbit/s"); + appendCanvasToPdf(canvLayer); - auto* canvLayerDigits = new TCanvas(Form("canvDigitsLayer%d", layer), Form("Layer %d digits per ROF", layer), 1050, 1050); + auto* canvLayerDigits = new TCanvas(Form("canvDigitsLayer%d", outputLayer), Form("Layer %d digits per ROF", outputLayer), 1050, 1050); canvLayerDigits->SetTopMargin(0.08); canvLayerDigits->SetRightMargin(0.18); hDigitsPerROF->Draw("colz"); - drawSummary(avgDigitsPerROFLayer, peakAvgDigitsPerROF, "digits/ROF"); - canvLayerDigits->SaveAs(Form("trk_layer%d_digits_per_rof_map.png", layer)); + drawSummary(ls.avgDigitsPerROF, ls.peakAvgDigitsPerROF, "digits/ROF"); + appendCanvasToPdf(canvLayerDigits); - auto* canvLayerMaxDigits = new TCanvas(Form("canvMaxDigitsLayer%d", layer), Form("Layer %d max digits per ROF", layer), 1050, 1050); + auto* canvLayerMaxDigits = new TCanvas(Form("canvMaxDigitsLayer%d", outputLayer), Form("Layer %d max digits per ROF", outputLayer), 1050, 1050); canvLayerMaxDigits->SetTopMargin(0.08); canvLayerMaxDigits->SetRightMargin(0.18); hMaxDigitsPerROF->Draw("colz"); - drawSummary(avgMaxDigitsPerROFLayer, peakMaxDigitsPerROF, "digits/ROF"); - canvLayerMaxDigits->SaveAs(Form("trk_layer%d_max_digits_per_rof_map.png", layer)); + drawSummary(ls.avgMaxDigitsPerROF, ls.peakMaxDigitsPerROF, "digits/ROF"); + appendCanvasToPdf(canvLayerMaxDigits); + } + + // --- Digits distribution per layer --- + // Each histogram shows the distribution of total-layer bandwidth across ROFs. + + { + const int nCols = std::max(1, (int)std::ceil(std::sqrt((double)nTotalLayers))); + const int nRows = (nTotalLayers + nCols - 1) / nCols; + auto* canvBwDist = new TCanvas("canvDigitsDistPerLayer", "Digits distribution per layer", 350 * nCols, 300 * nRows); + canvBwDist->Divide(nCols, nRows); + for (int layer = 0; layer < nTotalLayers; ++layer) { + if (!hDigitsDistPerLayer[layer]) { + continue; + } + canvBwDist->cd(layer + 1); + gPad->SetLogy(); + gPad->SetTopMargin(0.10); + gPad->SetBottomMargin(0.14); + gPad->SetLeftMargin(0.14); + hDigitsDistPerLayer[layer]->Scale(1. / hDigitsDistPerLayer[layer]->GetEntries()); + hDigitsDistPerLayer[layer]->Draw("hist"); + } + appendCanvasToPdf(canvBwDist); + } + + // --- Summary: bandwidth vs layer --- + + auto* hAvgBandwidthVsLayer = new TH1D("h_avg_bandwidth_vs_layer", + "Average bandwidth by layer;layer id;average bandwidth (Gbit/s)", + nTotalLayers, -0.5, nTotalLayers - 0.5); + auto* hPeakBandwidthVsLayer = new TH1D("h_peak_bandwidth_vs_layer", + "Peak bandwidth by layer;layer id;peak bandwidth (Gbit/s)", + nTotalLayers, -0.5, nTotalLayers - 0.5); + for (int layer = 0; layer < nTotalLayers; ++layer) { + hAvgBandwidthVsLayer->SetBinContent(layer + 1, layerStats[layer].avgBandwidthGbps); + hPeakBandwidthVsLayer->SetBinContent(layer + 1, layerStats[layer].peakBandwidthGbps); + } + + auto* canvBandwidthSummary = new TCanvas("canvBandwidthSummary", "Bandwidth summary by layer", 1050, 1050); + gStyle->SetOptTitle(0); + canvBandwidthSummary->cd(); + canvBandwidthSummary->SetTopMargin(0.08); + canvBandwidthSummary->SetBottomMargin(0.14); + canvBandwidthSummary->SetLogy(); + hAvgBandwidthVsLayer->SetTitle("Average bandwidth by layer;layer id;Bandwidth (Gbit/s)"); + hAvgBandwidthVsLayer->Draw("hist"); + hPeakBandwidthVsLayer->SetLineColor(kRed); + hPeakBandwidthVsLayer->Draw("hist same"); + canvBandwidthSummary->BuildLegend(0.6, 0.75, 0.9, 0.9); + appendCanvasToPdf(canvBandwidthSummary); + + if (lastPdfCanvas != nullptr) { + lastPdfCanvas->Print(Form("%s]", outputPdf.Data())); } f->Write(); From afa69e4b715bf1057de28ac8b85b04b8e3a69e53 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Fri, 15 Mar 2024 15:59:14 +0000 Subject: [PATCH 495/701] TPC Splines: fix initialization of the track residuals in the test macro --- .../macro/TPCFastTransformInit.C | 43 ++++++++++++++++++- 1 file changed, 41 insertions(+), 2 deletions(-) diff --git a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C index 6b3756aca3b73..b13d031d6d10d 100644 --- a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C +++ b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C @@ -21,19 +21,24 @@ /// root -l TPCFastTransformInit.C'("debugVoxRes.root")' /// +#include "Algorithm/RangeTokenizer.h" + #if !defined(__CLING__) || defined(__ROOTCLING__) +#include +#include #include "TFile.h" #include "TSystem.h" #include "TTree.h" #include "TNtuple.h" #include "Riostream.h" +#include "Algorithm/RangeTokenizer.h" +#include "Framework/Logger.h" #include "GPU/TPCFastTransform.h" #include "SpacePoints/TrackResiduals.h" #include "TPCReconstruction/TPCFastTransformHelperO2.h" #include "TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h" - #endif using namespace o2::tpc; @@ -54,6 +59,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", corr->Draw("cx:y:z","iRoc==0&&iRow==10","") grid->Draw("cx:y:z","iRoc==0&&iRow==10","same") vox->Draw("vx:y:z","iRoc==0&&iRow==10","same") + points->Draw("px:y:z","iRoc==0&&iRow==10","same") */ if (gSystem->AccessPathName(fileName)) { @@ -75,15 +81,42 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", return; } + auto userInfo = voxResTree->GetUserInfo(); + + if (!userInfo->FindObject("y2xBinning") || !userInfo->FindObject("z2xBinning")) { + std::cout << "'y2xBinning' or 'z2xBinning' not found in UserInfo, but required to get the correct binning" << std::endl; + return; + } + + userInfo->Print(); + + // required for the binning that was used o2::tpc::TrackResiduals trackResiduals; - trackResiduals.init(); // also initializes the default binning which was used + auto y2xBins = o2::RangeTokenizer::tokenize(userInfo->FindObject("y2xBinning")->GetTitle()); + auto z2xBins = o2::RangeTokenizer::tokenize(userInfo->FindObject("z2xBinning")->GetTitle()); + trackResiduals.setY2XBinning(y2xBins); + trackResiduals.setZ2XBinning(z2xBins); + trackResiduals.init(); + + std::cout << "y2xBins: " << y2xBins.size() << " z2xBins: " << z2xBins.size() << std::endl; + for (auto y2x : y2xBins) { + std::cout << "y2x: " << y2x << std::endl; + } + + std::cout << std::endl; + + for (auto z2x : z2xBins) { + std::cout << "z2x: " << z2x << std::endl; + } std::cout << "create fast transformation ... " << std::endl; auto* helper = o2::tpc::TPCFastTransformHelperO2::instance(); o2::tpc::TPCFastSpaceChargeCorrectionHelper* corrHelper = o2::tpc::TPCFastSpaceChargeCorrectionHelper::instance(); + corrHelper->setNthreadsToMaximum(); + auto corrPtr = corrHelper->createFromTrackResiduals(trackResiduals, voxResTree, useSmoothed, invertSigns); std::unique_ptr fastTransform( @@ -162,6 +195,9 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", branch->SetAddress(&v); branch->SetAutoDelete(kTRUE); + int iRocLast = -1; + int iRowLast = -1; + for (int32_t iVox = 0; iVox < voxResTree->GetEntriesFast(); iVox++) { voxResTree->GetEntry(iVox); @@ -180,6 +216,9 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", int32_t iRoc = (int32_t)v->bsec; int32_t iRow = (int32_t)xBin; + iRocLast = iRoc; + iRowLast = iRow; + double x = trackResiduals.getX(xBin); // radius of the pad row double y2x = trackResiduals.getY2X( From f878d89be6c7b930f8f7c5b65979064d2418d8fb Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Fri, 15 Mar 2024 16:02:00 +0000 Subject: [PATCH 496/701] TPC Splines: fix propagation of the track residual data to the TPC row edges --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 389 +++++++++++++----- 1 file changed, 279 insertions(+), 110 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index e2960c73e4d50..9910e2206ca11 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -118,6 +118,8 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas // calculate correction map: dx,du,dv = ( origTransform() -> x,u,v) - fastTransformNominal:x,u,v // for the future: switch TOF correction off for a while + TStopwatch watch; + if (!mIsInitialized) { initGeometry(); } @@ -176,6 +178,10 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas } // slice + watch.Stop(); + + LOGP(info, "Space charge correction tooks: {}s", watch.RealTime()); + initInverse(correction, 0); } @@ -380,20 +386,14 @@ std::unique_ptr TPCFastSpaceChargeCorrect { // create o2::gpu::TPCFastSpaceChargeCorrection from o2::tpc::TrackResiduals::VoxRes voxel tree - LOG(info) << "fast space charge correction helper: create correction using " << mNthreads << " threads"; + LOG(info) << "fast space charge correction helper: create correction from track residuals using " << mNthreads << " threads"; + + TStopwatch watch1, watch2; std::unique_ptr correctionPtr(new o2::gpu::TPCFastSpaceChargeCorrection); o2::gpu::TPCFastSpaceChargeCorrection& correction = *correctionPtr; - // o2::tpc::TrackResiduals::VoxRes* v = nullptr; - // voxResTree->SetBranchAddress("voxRes", &v); - - o2::tpc::TrackResiduals::VoxRes* v = nullptr; - TBranch* branch = voxResTree->GetBranch("voxRes"); - branch->SetAddress(&v); - branch->SetAutoDelete(kTRUE); - auto* helper = o2::tpc::TPCFastSpaceChargeCorrectionHelper::instance(); const o2::gpu::TPCFastTransformGeo& geo = helper->getGeometry(); @@ -417,9 +417,11 @@ std::unique_ptr TPCFastSpaceChargeCorrect // std::cout << "n knots Y: " << nKnotsY << std::endl; // std::cout << "n knots Z: " << nKnotsZ << std::endl; + const int nRows = geo.getNumberOfRows(); + const int nROCs = geo.getNumberOfSlices(); + { // create the correction object - const int nRows = geo.getNumberOfRows(); const int nCorrectionScenarios = 1; correction.startConstruction(geo, nCorrectionScenarios); @@ -451,131 +453,298 @@ std::unique_ptr TPCFastSpaceChargeCorrect LOG(info) << "fast space charge correction helper: fill data points from track residuals"; - for (int iVox = 0; iVox < voxResTree->GetEntriesFast(); iVox++) { - - voxResTree->GetEntry(iVox); - auto xBin = - v->bvox[o2::tpc::TrackResiduals::VoxX]; // bin number in x (= pad row) - auto y2xBin = - v->bvox[o2::tpc::TrackResiduals::VoxF]; // bin number in y/x 0..14 - auto z2xBin = - v->bvox[o2::tpc::TrackResiduals::VoxZ]; // bin number in z/x 0..4 - - int iRoc = (int)v->bsec; - int iRow = (int)xBin; + // o2::tpc::TrackResiduals::VoxRes* v = nullptr; + // voxResTree->SetBranchAddress("voxRes", &v); - // x,y,z of the voxel in local TPC coordinates + o2::tpc::TrackResiduals::VoxRes* v = nullptr; + TBranch* branch = voxResTree->GetBranch("voxRes"); + branch->SetAddress(&v); + branch->SetAutoDelete(kTRUE); - double x = trackResiduals.getX(xBin); // radius of the pad row - double y2x = trackResiduals.getY2X( - xBin, y2xBin); // y/x coordinate of the bin ~-0.15 ... 0.15 - double z2x = - trackResiduals.getZ2X(z2xBin); // z/x coordinate of the bin 0.1 .. 0.9 - double y = x * y2x; - double z = x * z2x; + // find the first and the last voxel for each ROC + // we assume the data is sorted by ROC, othwerwise it will be read nROCs times - if (iRoc >= geo.getNumberOfSlicesA()) { - z = -z; - // y = -y; - } + std::vector vROCdataFirst(nROCs, -1); + std::vector vROCdataLast(nROCs, -2); - { - float sx, sy, sz; - trackResiduals.getVoxelCoordinates(iRoc, xBin, y2xBin, z2xBin, sx, sy, sz); - sy *= x; - sz *= x; - if (fabs(sx - x) + fabs(sy - y) + fabs(sz - z) > 1.e-4) { - std::cout << "wrong coordinates: " << x << " " << y << " " << z << " / " << sx << " " << sy << " " << sz << std::endl; + { + int iRocLast = -1; + bool isSorted = true; + for (int iVox = 0; iVox < voxResTree->GetEntriesFast(); iVox++) { + voxResTree->GetEntry(iVox); + int iRoc = (int)v->bsec; + // ensure the data is in the expacted order + if (iRoc < iRocLast) { + isSorted = false; + } + iRocLast = iRoc; + if (iRoc < 0 || iRoc >= nROCs) { + LOG(fatal) << "ROC number " << iRoc << " is out of range"; } + if (vROCdataFirst[iRoc] < 0) { + vROCdataFirst[iRoc] = iVox; + } + vROCdataLast[iRoc] = iVox; } - - // skip empty voxels - float voxEntries = v->stat[o2::tpc::TrackResiduals::VoxV]; - if (voxEntries < 1.) { // no statistics - continue; + if (!isSorted) { + LOG(warning) << "Data is not sorted by ROC as expected"; } + } - // double statX = v->stat[o2::tpc::TrackResiduals::VoxX]; // weight - // double statY = v->stat[o2::tpc::TrackResiduals::VoxF]; // weight - // double statZ = v->stat[o2::tpc::TrackResiduals::VoxZ]; // weight - - // double dx = 1. / trackResiduals.getDXI(xBin); - double dy = x / trackResiduals.getDY2XI(xBin, y2xBin); - double dz = x * trackResiduals.getDZ2X(z2xBin); - - double correctionX = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResX] : v->D[o2::tpc::TrackResiduals::ResX]; - double correctionY = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResY] : v->D[o2::tpc::TrackResiduals::ResY]; - double correctionZ = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResZ] : v->D[o2::tpc::TrackResiduals::ResZ]; - if (invertSigns) { - correctionX *= -1.; - correctionY *= -1.; - correctionZ *= -1.; - } - // add one point per voxel + // read the data ROC by ROC - // map.addCorrectionPoint(iRoc, iRow, y, z, correctionX, correctionY, - // correctionZ); + // data in the tree is not sorted by row + // first find which data belong to which row - // add several points per voxel, - // extend values of the edge voxels to the edges of the TPC row - // + struct VoxelData { + int mNentries{0}; // number of entries + float mCx, mCy, mCz; // corrections to the local coordinates + }; - double yFirst = y - dy / 2.; - double yLast = y + dy / 2.; + std::vector vRocData[nRows]; + for (int ir = 0; ir < nRows; ir++) { + vRocData[ir].resize(nY2Xbins * nZ2Xbins); + } - if (y2xBin == 0) { // extend value of the first Y bin to the row edge - float u, v; - if (iRoc < geo.getNumberOfSlicesA()) { - geo.convScaledUVtoUV(iRoc, iRow, 0., 0., u, v); - } else { - geo.convScaledUVtoUV(iRoc, iRow, 1., 0., u, v); + struct Voxel { + float mY, mZ; // not-distorted local coordinates + float mDy, mDz; // bin size + int mSmoothingStep{100}; // is the voxel data original or smoothed at this step + }; + + std::vector vRowVoxels(nY2Xbins * nZ2Xbins); + + for (int iRoc = 0; iRoc < nROCs; iRoc++) { + + for (int ir = 0; ir < nRows; ir++) { + for (int iv = 0; iv < nY2Xbins * nZ2Xbins; iv++) { + vRocData[ir][iv].mNentries = 0; } - float py, pz; - geo.convUVtoLocal(iRoc, u, v, py, pz); - yFirst = py; } - if (y2xBin == trackResiduals.getNY2XBins() - 1) { // extend value of the last Y bin to the row edge - float u, v; - if (iRoc < geo.getNumberOfSlicesA()) { - geo.convScaledUVtoUV(iRoc, iRow, 1., 0., u, v); - } else { - geo.convScaledUVtoUV(iRoc, iRow, 0., 0., u, v); + for (int iVox = vROCdataFirst[iRoc]; iVox <= vROCdataLast[iRoc]; iVox++) { + voxResTree->GetEntry(iVox); + if ((int)v->bsec != iRoc) { + LOG(fatal) << "ROC number " << v->bsec << " is not equal to " << iRoc; + continue; + } + int iRow = (int)v->bvox[o2::tpc::TrackResiduals::VoxX]; // bin number in x (= pad row) + if (iRow < 0 || iRow >= nRows) { + LOG(fatal) << "Row number " << iRow << " is out of range"; } - float py, pz; - geo.convUVtoLocal(iRoc, u, v, py, pz); - yLast = py; + int iy = v->bvox[o2::tpc::TrackResiduals::VoxF]; // bin number in y/x 0..14 + int iz = v->bvox[o2::tpc::TrackResiduals::VoxZ]; // bin number in z/x 0..4 + auto& vox = vRocData[iRow][iy * nZ2Xbins + iz]; + vox.mNentries = (int)v->stat[o2::tpc::TrackResiduals::VoxV]; + vox.mCx = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResX] : v->D[o2::tpc::TrackResiduals::ResX]; + vox.mCy = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResY] : v->D[o2::tpc::TrackResiduals::ResY]; + vox.mCz = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResZ] : v->D[o2::tpc::TrackResiduals::ResZ]; } - double z0 = 0.; - if (iRoc < geo.getNumberOfSlicesA()) { - z0 = geo.getTPCzLengthA(); - } else { - z0 = -geo.getTPCzLengthC(); - } + // now process the data row-by-row + + for (int iRow = 0; iRow < nRows; iRow++) { + + // LOG(info) << "Processing ROC " << iRoc << " row " << iRow; + + // complete the voxel data + { + int xBin = iRow; + double x = trackResiduals.getX(xBin); // radius of the pad row + bool isDataFound = false; + for (int iy = 0; iy < nY2Xbins; iy++) { + for (int iz = 0; iz < nZ2Xbins; iz++) { + auto& data = vRocData[iRow][iy * nZ2Xbins + iz]; + auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; + // y/x coordinate of the bin ~-0.15 ... 0.15 + double y2x = trackResiduals.getY2X(xBin, iy); + // z/x coordinate of the bin 0.1 .. 0.9 + double z2x = trackResiduals.getZ2X(iz); + vox.mY = x * y2x; + vox.mZ = x * z2x; + vox.mDy = x / trackResiduals.getDY2XI(xBin, iy); + vox.mDz = x * trackResiduals.getDZ2X(iz); + if (iRoc >= geo.getNumberOfSlicesA()) { + vox.mZ = -vox.mZ; + } + if (data.mNentries < 1) { // no data + data.mCx = 0.; + data.mCy = 0.; + data.mCz = 0.; + vox.mSmoothingStep = 100; + } else { // voxel contains data + if (invertSigns) { + data.mCx *= -1.; + data.mCy *= -1.; + data.mCz *= -1.; + } + vox.mSmoothingStep = 0; // original data + isDataFound = true; + } + } + } - double yStep = (yLast - yFirst) / 2; + if (!isDataFound) { // fill everything with 0 + for (int iy = 0; iy < nY2Xbins; iy++) { + for (int iz = 0; iz < nZ2Xbins; iz++) { + vRowVoxels[iy * nZ2Xbins + iz].mSmoothingStep = 0; + } + } + } + } // complete the voxel data + + // repare the voxel data: fill empty voxels + + int nRepairs = 0; + + for (int ismooth = 1; ismooth <= 2; ismooth++) { + for (int iy = 0; iy < nY2Xbins; iy++) { + for (int iz = 0; iz < nZ2Xbins; iz++) { + auto& data = vRocData[iRow][iy * nZ2Xbins + iz]; + auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; + if (vox.mSmoothingStep <= ismooth) { // already filled + continue; + } + nRepairs++; + data.mCx = 0.; + data.mCy = 0.; + data.mCz = 0.; + double w = 0.; + bool filled = false; + auto update = [&](int iy1, int iz1) { + auto& data1 = vRocData[iRow][iy1 * nZ2Xbins + iz1]; + auto& vox1 = vRowVoxels[iy1 * nZ2Xbins + iz1]; + if (vox1.mSmoothingStep >= ismooth) { + return false; + } + double w1 = 1. / (abs(iy - iy1) + abs(iz - iz1) + 1); + data.mCx += w1 * data1.mCx; + data.mCy += w1 * data1.mCy; + data.mCz += w1 * data1.mCz; + w += w1; + filled = true; + return true; + }; + + for (int iy1 = iy - 1; iy1 >= 0 && !update(iy1, iz); iy1--) { + } + for (int iy1 = iy + 1; iy1 < nY2Xbins && !update(iy1, iz); iy1++) { + } + for (int iz1 = iz - 1; iz1 >= 0 && !update(iy, iz1); iz1--) { + } + for (int iz1 = iz + 1; iz1 < nZ2Xbins && !update(iy, iz1); iz1++) { + } - for (double py = yFirst; py <= yLast + yStep / 2.; py += yStep) { + if (filled) { + data.mCx /= w; + data.mCy /= w; + data.mCz /= w; + vox.mSmoothingStep = ismooth; + } + } // iz + } // iy + } // ismooth - for (double pz = z - dz / 2.; pz <= z + dz / 2. + 1.e-4; pz += dz / 2.) { - map.addCorrectionPoint(iRoc, iRow, py, pz, correctionX, correctionY, - correctionZ); + if (nRepairs > 0) { + LOG(info) << "ROC " << iRoc << " row " << iRow << ": " << nRepairs << " voxel repairs for " << nY2Xbins * nZ2Xbins << " voxels"; } - if (z2xBin == trackResiduals.getNZ2XBins() - 1) { - // extend value of the first Z bin to the readout, linear decrease of all values to 0. - int nZsteps = 3; - for (int is = 0; is < nZsteps; is++) { - double pz = z + (z0 - z) * (is + 1.) / nZsteps; - double s = (nZsteps - 1. - is) / nZsteps; - map.addCorrectionPoint(iRoc, iRow, py, pz, s * correctionX, - s * correctionY, s * correctionZ); + // feed the row data to the helper + + double yMin = 0., yMax = 0.; + + { + float u, v; + if (iRoc < geo.getNumberOfSlicesA()) { + geo.convScaledUVtoUV(iRoc, iRow, 0., 0., u, v); + } else { + geo.convScaledUVtoUV(iRoc, iRow, 1., 0., u, v); } + float py, pz; + geo.convUVtoLocal(iRoc, u, v, py, pz); + yMin = py; } - } - } + { + float u, v; + if (iRoc < geo.getNumberOfSlicesA()) { + geo.convScaledUVtoUV(iRoc, iRow, 1., 0., u, v); + } else { + geo.convScaledUVtoUV(iRoc, iRow, 0., 0., u, v); + } + float py, pz; + geo.convUVtoLocal(iRoc, u, v, py, pz); + yMax = py; + } + + double zEdge = 0.; + if (iRoc < geo.getNumberOfSlicesA()) { + zEdge = geo.getTPCzLengthA(); + } else { + zEdge = -geo.getTPCzLengthC(); + } + + for (int iy = 0; iy < nY2Xbins; iy++) { + for (int iz = 0; iz < nZ2Xbins; iz++) { + auto& data = vRocData[iRow][iy * nZ2Xbins + iz]; + auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; + if (vox.mSmoothingStep > 2) { + LOG(fatal) << "empty voxel is not repared"; + } + + double y = vox.mY; + double z = vox.mZ; + double dy = vox.mDy; + double dz = vox.mDz; + double correctionX = data.mCx; + double correctionY = data.mCy; + double correctionZ = data.mCz; + + double yFirst = y - dy / 2.; + double yLast = y + dy / 2.; + + if (iy == 0) { // extend value of the first Y bin to the row edge + yFirst = yMin; + } + + if (iy == nY2Xbins - 1) { // extend value of the last Y bin to the row edge + yLast = yMax; + } + + double yStep = (yLast - yFirst) / 2; + + for (double py = yFirst; py <= yLast + yStep / 2.; py += yStep) { + + for (double pz = z - dz / 2.; pz <= z + dz / 2. + 1.e-4; pz += dz / 2.) { + map.addCorrectionPoint(iRoc, iRow, py, pz, correctionX, correctionY, + correctionZ); + } + + if (iz == nZ2Xbins - 1) { + // extend value of the first Z bin to the readout, linear decrease of all values to 0. + int nZsteps = 3; + for (int is = 0; is < nZsteps; is++) { + double pz = z + (zEdge - z) * (is + 1.) / nZsteps; + double s = (nZsteps - 1. - is) / nZsteps; + map.addCorrectionPoint(iRoc, iRow, py, pz, s * correctionX, + s * correctionY, s * correctionZ); + } + } + } + } // iz + } // iy + + } // iRow + + } // iRoc + + LOGP(info, "Reading & reparing of the track residuals tooks: {}s", watch1.RealTime()); + + LOG(info) << "fast space charge correction helper: create space charge from the map of data points.."; + helper->fillSpaceChargeCorrectionFromMap(correction); + + LOGP(info, "Creation from track residuals tooks in total: {}s", watch2.RealTime()); + return std::move(correctionPtr); } @@ -814,7 +983,7 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector Date: Mon, 15 Apr 2024 14:41:19 +0000 Subject: [PATCH 497/701] TPC Splines: non-uniform grid that corresponds to the track residual voxels --- .../include/SpacePoints/TrackResiduals.h | 19 +- .../TPCFastSpaceChargeCorrectionHelper.cxx | 530 ++++++++++-------- .../TPCFastSpaceChargeCorrection.cxx | 181 +++--- .../TPCFastSpaceChargeCorrection.h | 64 +-- .../TPCFastTransformGeo.h | 9 + .../macro/TPCFastTransformInit.C | 203 +++++-- 6 files changed, 619 insertions(+), 387 deletions(-) diff --git a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackResiduals.h b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackResiduals.h index 2ade12d951c58..c9226589ec703 100644 --- a/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackResiduals.h +++ b/Detectors/TPC/calibration/SpacePoints/include/SpacePoints/TrackResiduals.h @@ -318,9 +318,14 @@ class TrackResiduals void getVoxelCoordinates(int isec, int ix, int ip, int iz, float& x, float& p, float& z) const; /// Calculates the x-coordinate for given x bin. - /// \param i Bin index + /// \param ix Bin index in x /// \return Coordinate in X - float getX(int i) const; + float getX(int ix) const; + + /// Calculates the max y/x-coordinate for given x bin taking the dead zone into account. + /// \param ix Bin index in x + /// \return Max coordinate in Y/X + float getMaxY2X(int ix) const; /// Calculates the y/x-coordinate. /// \param ix Bin index in X @@ -560,9 +565,15 @@ inline float TrackResiduals::getDXI(int ix) const } //_____________________________________________________ -inline float TrackResiduals::getX(int i) const +inline float TrackResiduals::getX(int ix) const +{ + return mUniformBins[VoxX] ? param::MinX + (ix + 0.5) * mDX : param::RowX[ix]; +} + +//_____________________________________________________ +inline float TrackResiduals::getMaxY2X(int ix) const { - return mUniformBins[VoxX] ? param::MinX + (i + 0.5) * mDX : param::RowX[i]; + return mMaxY2X[ix]; } //_____________________________________________________ diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index 9910e2206ca11..861cacbe00012 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -29,6 +29,8 @@ #include #include #include "TStopwatch.h" +#include "TTreeReader.h" +#include "TTreeReaderValue.h" using namespace o2::gpu; @@ -154,7 +156,7 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas pointCorr[3 * i + 1] = du; pointCorr[3 * i + 2] = dv; } - helper.approximateDataPoints(spline, splineParameters, 0., spline.getGridX1().getNumberOfKnots() - 1, 0., spline.getGridX2().getNumberOfKnots() - 1, &pointSU[0], + helper.approximateDataPoints(spline, splineParameters, 0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax(), &pointSU[0], &pointSV[0], &pointCorr[0], nDataPoints); } else { for (int i = 0; i < spline.getNumberOfParameters(); i++) { @@ -388,7 +390,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect LOG(info) << "fast space charge correction helper: create correction from track residuals using " << mNthreads << " threads"; - TStopwatch watch1, watch2; + TStopwatch watch, watch1; std::unique_ptr correctionPtr(new o2::gpu::TPCFastSpaceChargeCorrection); @@ -403,17 +405,90 @@ std::unique_ptr TPCFastSpaceChargeCorrect int nY2Xbins = trackResiduals.getNY2XBins(); int nZ2Xbins = trackResiduals.getNZ2XBins(); - int nKnotsY = nY2Xbins / 2; - int nKnotsZ = nZ2Xbins / 2; + std::vector yBinsInt; + { + std::vector yBins; + yBins.reserve(nY2Xbins); + for (int i = 0, j = nY2Xbins - 1; i <= j; i += 2, j -= 2) { + if (i == j) { + yBins.push_back(trackResiduals.getY2X(0, i)); + } else if (i + 1 == j) { + yBins.push_back(trackResiduals.getY2X(0, i)); + } else { + yBins.push_back(trackResiduals.getY2X(0, i)); + yBins.push_back(trackResiduals.getY2X(0, j)); + } + } + std::sort(yBins.begin(), yBins.end()); + double dy = yBins[1] - yBins[0]; + for (int i = 1; i < yBins.size(); i++) { + if (yBins[i] - yBins[i - 1] < dy) { + dy = yBins[i] - yBins[i - 1]; + } + } + yBinsInt.reserve(yBins.size()); + // spline knots must be positioned on the grid with integer internal coordinate + // take the knot position accuracy of 0.1*dy + dy = dy / 10.; + double y0 = yBins[0]; + double y1 = yBins[yBins.size() - 1]; + for (auto& y : yBins) { + y -= y0; + int iy = int(y / dy + 0.5); + yBinsInt.push_back(iy); + double yold = y / (y1 - y0) * 2 - 1.; + y = iy * dy; + y = y / (y1 - y0) * 2 - 1.; + LOG(info) << "convert y bin: " << yold << " -> " << y << " -> " << iy; + } + } + + std::vector zBinsInt; + { + std::vector zBins; + zBins.reserve(nZ2Xbins); + for (int i = 0; i < nZ2Xbins; i += 2) { + zBins.push_back(-trackResiduals.getZ2X(i)); + } + std::sort(zBins.begin(), zBins.end()); + double dz = zBins[1] - zBins[0]; + for (int i = 1; i < zBins.size(); i++) { + if (zBins[i] - zBins[i - 1] < dz) { + dz = zBins[i] - zBins[i - 1]; + } + } + zBinsInt.reserve(zBins.size()); + // spline knots must be positioned on the grid with an integer internal coordinate + // lets copy the knot positions with the accuracy of 0.1*dz + dz = dz / 10.; + double z0 = zBins[0]; + double z1 = zBins[zBins.size() - 1]; + for (auto& z : zBins) { + z -= z0; + int iz = int(z / dz + 0.5); + zBinsInt.push_back(iz); + double zold = z / (z1 - z0); + z = iz * dz; + z = z / (z1 - z0); + LOG(info) << "convert z bin: " << zold << " -> " << z << " -> " << iz; + } + } - if (nKnotsY < 2) { - nKnotsY = 2; + if (yBinsInt.size() < 2) { + yBinsInt.clear(); + yBinsInt.push_back(0); + yBinsInt.push_back(1); } - if (nKnotsZ < 2) { - nKnotsZ = 2; + if (zBinsInt.size() < 2) { + zBinsInt.clear(); + zBinsInt.push_back(0); + zBinsInt.push_back(1); } + int nKnotsY = yBinsInt.size(); + int nKnotsZ = zBinsInt.size(); + // std::cout << "n knots Y: " << nKnotsY << std::endl; // std::cout << "n knots Z: " << nKnotsZ << std::endl; @@ -432,64 +507,42 @@ std::unique_ptr TPCFastSpaceChargeCorrect } { // init spline scenario TPCFastSpaceChargeCorrection::SplineType spline; - spline.recreate(nKnotsY, nKnotsZ); + spline.recreate(nKnotsY, &yBinsInt[0], nKnotsZ, &zBinsInt[0]); correction.setSplineScenario(0, spline); } correction.finishConstruction(); } // .. create the correction object - // set the grid borders in Z to Z/X==1 + // set the grid borders for (int iRoc = 0; iRoc < geo.getNumberOfSlices(); iRoc++) { for (int iRow = 0; iRow < geo.getNumberOfRows(); iRow++) { - auto rowInfo = geo.getRowInfo(iRow); - o2::gpu::TPCFastSpaceChargeCorrection::SliceRowInfo& info = correction.getSliceRowInfo(iRoc, iRow); - double len = geo.getTPCzLength(iRoc); - info.gridV0 = len - rowInfo.x; - if (info.gridV0 < 0.) { - info.gridV0 = 0.; - } + const auto& rowInfo = geo.getRowInfo(iRow); + auto& info = correction.getSliceRowInfo(iRoc, iRow); + const auto& spline = correction.getSpline(iRoc, iRow); + double yMin = rowInfo.x * trackResiduals.getY2X(iRow, 0); + double yMax = rowInfo.x * trackResiduals.getY2X(iRow, trackResiduals.getNY2XBins() - 1); + double zMin = rowInfo.x * trackResiduals.getZ2X(0); + double zMax = rowInfo.x * trackResiduals.getZ2X(trackResiduals.getNZ2XBins() - 1); + double uMin = yMin; + double uMax = yMax; + double vMin = geo.getTPCzLength(iRoc) - zMax; + double vMax = geo.getTPCzLength(iRoc) - zMin; + // std::cout << " uMin: " << uMin << " uMax: " << yuMax << " zMin: " << vMin << " zMax: " << vMax << std::endl; + info.gridU0 = uMin; + info.scaleUtoGrid = spline.getGridX1().getUmax() / (uMax - uMin); + info.gridV0 = vMin; + info.scaleVtoGrid = spline.getGridX2().getUmax() / (vMax - vMin); } } - LOG(info) << "fast space charge correction helper: fill data points from track residuals"; - - // o2::tpc::TrackResiduals::VoxRes* v = nullptr; - // voxResTree->SetBranchAddress("voxRes", &v); + LOG(info) << "fast space charge correction helper: preparation took " << watch1.RealTime() << "s"; - o2::tpc::TrackResiduals::VoxRes* v = nullptr; - TBranch* branch = voxResTree->GetBranch("voxRes"); - branch->SetAddress(&v); - branch->SetAutoDelete(kTRUE); + LOG(info) << "fast space charge correction helper: fill data points from track residuals.. "; - // find the first and the last voxel for each ROC - // we assume the data is sorted by ROC, othwerwise it will be read nROCs times + TStopwatch watch3; - std::vector vROCdataFirst(nROCs, -1); - std::vector vROCdataLast(nROCs, -2); - - { - int iRocLast = -1; - bool isSorted = true; - for (int iVox = 0; iVox < voxResTree->GetEntriesFast(); iVox++) { - voxResTree->GetEntry(iVox); - int iRoc = (int)v->bsec; - // ensure the data is in the expacted order - if (iRoc < iRocLast) { - isSorted = false; - } - iRocLast = iRoc; - if (iRoc < 0 || iRoc >= nROCs) { - LOG(fatal) << "ROC number " << iRoc << " is out of range"; - } - if (vROCdataFirst[iRoc] < 0) { - vROCdataFirst[iRoc] = iVox; - } - vROCdataLast[iRoc] = iVox; - } - if (!isSorted) { - LOG(warning) << "Data is not sorted by ROC as expected"; - } - } + // TTreeProcessorMT treeProcessor(*voxResTree); // multi-threaded tree processor + // treeProcessor.Init(voxResTree); // read the data ROC by ROC @@ -506,14 +559,6 @@ std::unique_ptr TPCFastSpaceChargeCorrect vRocData[ir].resize(nY2Xbins * nZ2Xbins); } - struct Voxel { - float mY, mZ; // not-distorted local coordinates - float mDy, mDz; // bin size - int mSmoothingStep{100}; // is the voxel data original or smoothed at this step - }; - - std::vector vRowVoxels(nY2Xbins * nZ2Xbins); - for (int iRoc = 0; iRoc < nROCs; iRoc++) { for (int ir = 0; ir < nRows; ir++) { @@ -522,10 +567,17 @@ std::unique_ptr TPCFastSpaceChargeCorrect } } - for (int iVox = vROCdataFirst[iRoc]; iVox <= vROCdataLast[iRoc]; iVox++) { - voxResTree->GetEntry(iVox); + const int rocDataStart = iRoc * trackResiduals.getNVoxelsPerSector(); + const int rocDataEnd = rocDataStart + trackResiduals.getNVoxelsPerSector(); + + TTreeReader reader(voxResTree); + reader.SetEntriesRange(rocDataStart, rocDataEnd); + TTreeReaderValue v(reader, "voxRes"); + for (int iVox = rocDataStart; iVox < rocDataEnd; iVox++) { + reader.Next(); + // voxResTree->GetEntry(iVox); if ((int)v->bsec != iRoc) { - LOG(fatal) << "ROC number " << v->bsec << " is not equal to " << iRoc; + LOG(fatal) << "Error reading voxels: voxel ROC number " << v->bsec << " is not equal to the expected " << iRoc; continue; } int iRow = (int)v->bvox[o2::tpc::TrackResiduals::VoxX]; // bin number in x (= pad row) @@ -543,207 +595,237 @@ std::unique_ptr TPCFastSpaceChargeCorrect // now process the data row-by-row - for (int iRow = 0; iRow < nRows; iRow++) { + auto myThread = [&](int iThread, int nTreads) { + struct Voxel { + float mY, mZ; // not-distorted local coordinates + float mDy, mDz; // bin size + int mSmoothingStep{100}; // is the voxel data original or smoothed at this step + }; - // LOG(info) << "Processing ROC " << iRoc << " row " << iRow; + std::vector vRowVoxels(nY2Xbins * nZ2Xbins); - // complete the voxel data - { - int xBin = iRow; - double x = trackResiduals.getX(xBin); // radius of the pad row - bool isDataFound = false; - for (int iy = 0; iy < nY2Xbins; iy++) { - for (int iz = 0; iz < nZ2Xbins; iz++) { - auto& data = vRocData[iRow][iy * nZ2Xbins + iz]; - auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; - // y/x coordinate of the bin ~-0.15 ... 0.15 - double y2x = trackResiduals.getY2X(xBin, iy); - // z/x coordinate of the bin 0.1 .. 0.9 - double z2x = trackResiduals.getZ2X(iz); - vox.mY = x * y2x; - vox.mZ = x * z2x; - vox.mDy = x / trackResiduals.getDY2XI(xBin, iy); - vox.mDz = x * trackResiduals.getDZ2X(iz); - if (iRoc >= geo.getNumberOfSlicesA()) { - vox.mZ = -vox.mZ; - } - if (data.mNentries < 1) { // no data - data.mCx = 0.; - data.mCy = 0.; - data.mCz = 0.; - vox.mSmoothingStep = 100; - } else { // voxel contains data - if (invertSigns) { - data.mCx *= -1.; - data.mCy *= -1.; - data.mCz *= -1.; + for (int iRow = iThread; iRow < nRows; iRow += nTreads) { + // LOG(info) << "Processing ROC " << iRoc << " row " << iRow; + + // complete the voxel data + + { + int xBin = iRow; + double x = trackResiduals.getX(xBin); // radius of the pad row + bool isDataFound = false; + for (int iy = 0; iy < nY2Xbins; iy++) { + for (int iz = 0; iz < nZ2Xbins; iz++) { + auto& data = vRocData[iRow][iy * nZ2Xbins + iz]; + auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; + // y/x coordinate of the bin ~-0.15 ... 0.15 + double y2x = trackResiduals.getY2X(xBin, iy); + // z/x coordinate of the bin 0.1 .. 0.9 + double z2x = trackResiduals.getZ2X(iz); + vox.mY = x * y2x; + vox.mZ = x * z2x; + vox.mDy = x / trackResiduals.getDY2XI(xBin, iy); + vox.mDz = x * trackResiduals.getDZ2X(iz); + if (iRoc >= geo.getNumberOfSlicesA()) { + vox.mZ = -vox.mZ; + } + if (data.mNentries < 1) { // no data + data.mCx = 0.; + data.mCy = 0.; + data.mCz = 0.; + vox.mSmoothingStep = 100; + } else { // voxel contains data + if (invertSigns) { + data.mCx *= -1.; + data.mCy *= -1.; + data.mCz *= -1.; + } + vox.mSmoothingStep = 0; // original data + isDataFound = true; } - vox.mSmoothingStep = 0; // original data - isDataFound = true; } } - } - if (!isDataFound) { // fill everything with 0 - for (int iy = 0; iy < nY2Xbins; iy++) { - for (int iz = 0; iz < nZ2Xbins; iz++) { - vRowVoxels[iy * nZ2Xbins + iz].mSmoothingStep = 0; + if (!isDataFound) { // fill everything with 0 + for (int iy = 0; iy < nY2Xbins; iy++) { + for (int iz = 0; iz < nZ2Xbins; iz++) { + vRowVoxels[iy * nZ2Xbins + iz].mSmoothingStep = 0; + } } } - } - } // complete the voxel data + } // complete the voxel data - // repare the voxel data: fill empty voxels + // repare the voxel data: fill empty voxels - int nRepairs = 0; + int nRepairs = 0; - for (int ismooth = 1; ismooth <= 2; ismooth++) { - for (int iy = 0; iy < nY2Xbins; iy++) { - for (int iz = 0; iz < nZ2Xbins; iz++) { - auto& data = vRocData[iRow][iy * nZ2Xbins + iz]; - auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; - if (vox.mSmoothingStep <= ismooth) { // already filled - continue; - } - nRepairs++; - data.mCx = 0.; - data.mCy = 0.; - data.mCz = 0.; - double w = 0.; - bool filled = false; - auto update = [&](int iy1, int iz1) { - auto& data1 = vRocData[iRow][iy1 * nZ2Xbins + iz1]; - auto& vox1 = vRowVoxels[iy1 * nZ2Xbins + iz1]; - if (vox1.mSmoothingStep >= ismooth) { - return false; + for (int ismooth = 1; ismooth <= 2; ismooth++) { + for (int iy = 0; iy < nY2Xbins; iy++) { + for (int iz = 0; iz < nZ2Xbins; iz++) { + auto& data = vRocData[iRow][iy * nZ2Xbins + iz]; + auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; + if (vox.mSmoothingStep <= ismooth) { // already filled + continue; + } + nRepairs++; + data.mCx = 0.; + data.mCy = 0.; + data.mCz = 0.; + double w = 0.; + bool filled = false; + auto update = [&](int iy1, int iz1) { + auto& data1 = vRocData[iRow][iy1 * nZ2Xbins + iz1]; + auto& vox1 = vRowVoxels[iy1 * nZ2Xbins + iz1]; + if (vox1.mSmoothingStep >= ismooth) { + return false; + } + double w1 = 1. / (abs(iy - iy1) + abs(iz - iz1) + 1); + data.mCx += w1 * data1.mCx; + data.mCy += w1 * data1.mCy; + data.mCz += w1 * data1.mCz; + w += w1; + filled = true; + return true; + }; + + for (int iy1 = iy - 1; iy1 >= 0 && !update(iy1, iz); iy1--) { + } + for (int iy1 = iy + 1; iy1 < nY2Xbins && !update(iy1, iz); iy1++) { + } + for (int iz1 = iz - 1; iz1 >= 0 && !update(iy, iz1); iz1--) { + } + for (int iz1 = iz + 1; iz1 < nZ2Xbins && !update(iy, iz1); iz1++) { } - double w1 = 1. / (abs(iy - iy1) + abs(iz - iz1) + 1); - data.mCx += w1 * data1.mCx; - data.mCy += w1 * data1.mCy; - data.mCz += w1 * data1.mCz; - w += w1; - filled = true; - return true; - }; - - for (int iy1 = iy - 1; iy1 >= 0 && !update(iy1, iz); iy1--) { - } - for (int iy1 = iy + 1; iy1 < nY2Xbins && !update(iy1, iz); iy1++) { - } - for (int iz1 = iz - 1; iz1 >= 0 && !update(iy, iz1); iz1--) { - } - for (int iz1 = iz + 1; iz1 < nZ2Xbins && !update(iy, iz1); iz1++) { - } - if (filled) { - data.mCx /= w; - data.mCy /= w; - data.mCz /= w; - vox.mSmoothingStep = ismooth; - } - } // iz - } // iy - } // ismooth + if (filled) { + data.mCx /= w; + data.mCy /= w; + data.mCz /= w; + vox.mSmoothingStep = ismooth; + } + } // iz + } // iy + } // ismooth - if (nRepairs > 0) { - LOG(info) << "ROC " << iRoc << " row " << iRow << ": " << nRepairs << " voxel repairs for " << nY2Xbins * nZ2Xbins << " voxels"; - } + if (nRepairs > 0) { + LOG(debug) << "ROC " << iRoc << " row " << iRow << ": " << nRepairs << " voxel repairs for " << nY2Xbins * nZ2Xbins << " voxels"; + } - // feed the row data to the helper + // feed the row data to the helper - double yMin = 0., yMax = 0.; + double yMin = 0., yMax = 0.; - { - float u, v; - if (iRoc < geo.getNumberOfSlicesA()) { - geo.convScaledUVtoUV(iRoc, iRow, 0., 0., u, v); - } else { - geo.convScaledUVtoUV(iRoc, iRow, 1., 0., u, v); + { + float u, v; + if (iRoc < geo.getNumberOfSlicesA()) { + geo.convScaledUVtoUV(iRoc, iRow, 0., 0., u, v); + } else { + geo.convScaledUVtoUV(iRoc, iRow, 1., 0., u, v); + } + float py, pz; + geo.convUVtoLocal(iRoc, u, v, py, pz); + yMin = py; } - float py, pz; - geo.convUVtoLocal(iRoc, u, v, py, pz); - yMin = py; - } - { - float u, v; + { + float u, v; + if (iRoc < geo.getNumberOfSlicesA()) { + geo.convScaledUVtoUV(iRoc, iRow, 1., 0., u, v); + } else { + geo.convScaledUVtoUV(iRoc, iRow, 0., 0., u, v); + } + float py, pz; + geo.convUVtoLocal(iRoc, u, v, py, pz); + yMax = py; + } + + double zEdge = 0.; if (iRoc < geo.getNumberOfSlicesA()) { - geo.convScaledUVtoUV(iRoc, iRow, 1., 0., u, v); + zEdge = geo.getTPCzLengthA(); } else { - geo.convScaledUVtoUV(iRoc, iRow, 0., 0., u, v); + zEdge = -geo.getTPCzLengthC(); } - float py, pz; - geo.convUVtoLocal(iRoc, u, v, py, pz); - yMax = py; - } - - double zEdge = 0.; - if (iRoc < geo.getNumberOfSlicesA()) { - zEdge = geo.getTPCzLengthA(); - } else { - zEdge = -geo.getTPCzLengthC(); - } - for (int iy = 0; iy < nY2Xbins; iy++) { - for (int iz = 0; iz < nZ2Xbins; iz++) { - auto& data = vRocData[iRow][iy * nZ2Xbins + iz]; - auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; - if (vox.mSmoothingStep > 2) { - LOG(fatal) << "empty voxel is not repared"; - } + for (int iy = 0; iy < nY2Xbins; iy++) { + for (int iz = 0; iz < nZ2Xbins; iz++) { + auto& data = vRocData[iRow][iy * nZ2Xbins + iz]; + auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; + if (vox.mSmoothingStep > 2) { + LOG(fatal) << "empty voxel is not repared"; + } - double y = vox.mY; - double z = vox.mZ; - double dy = vox.mDy; - double dz = vox.mDz; - double correctionX = data.mCx; - double correctionY = data.mCy; - double correctionZ = data.mCz; + double y = vox.mY; + double z = vox.mZ; + double dy = vox.mDy; + double dz = vox.mDz; + double correctionX = data.mCx; + double correctionY = data.mCy; + double correctionZ = data.mCz; - double yFirst = y - dy / 2.; - double yLast = y + dy / 2.; + double yFirst = y - dy / 2.; + double yLast = y + dy / 2.; - if (iy == 0) { // extend value of the first Y bin to the row edge - yFirst = yMin; - } + if (iy == 0) { // extend value of the first Y bin to the row edge + yFirst = yMin; + } - if (iy == nY2Xbins - 1) { // extend value of the last Y bin to the row edge - yLast = yMax; - } + if (iy == nY2Xbins - 1) { // extend value of the last Y bin to the row edge + yLast = yMax; + } - double yStep = (yLast - yFirst) / 2; + double yStep = (yLast - yFirst) / 2; - for (double py = yFirst; py <= yLast + yStep / 2.; py += yStep) { + for (double py = yFirst; py <= yLast + yStep / 2.; py += yStep) { - for (double pz = z - dz / 2.; pz <= z + dz / 2. + 1.e-4; pz += dz / 2.) { - map.addCorrectionPoint(iRoc, iRow, py, pz, correctionX, correctionY, - correctionZ); - } + for (double pz = z - dz / 2.; pz <= z + dz / 2. + 1.e-4; pz += dz / 2.) { + map.addCorrectionPoint(iRoc, iRow, py, pz, correctionX, correctionY, + correctionZ); + } - if (iz == nZ2Xbins - 1) { - // extend value of the first Z bin to the readout, linear decrease of all values to 0. - int nZsteps = 3; - for (int is = 0; is < nZsteps; is++) { - double pz = z + (zEdge - z) * (is + 1.) / nZsteps; - double s = (nZsteps - 1. - is) / nZsteps; - map.addCorrectionPoint(iRoc, iRow, py, pz, s * correctionX, - s * correctionY, s * correctionZ); + if (iz == nZ2Xbins - 1) { + // extend value of the first Z bin to the readout, linear decrease of all values to 0. + int nZsteps = 3; + for (int is = 0; is < nZsteps; is++) { + double pz = z + (zEdge - z) * (is + 1.) / nZsteps; + double s = (nZsteps - 1. - is) / nZsteps; + map.addCorrectionPoint(iRoc, iRow, py, pz, s * correctionX, + s * correctionY, s * correctionZ); + } } } - } - } // iz - } // iy + } // iz + } // iy + + } // iRow + }; // myThread + + // run n threads + + int nThreads = mNthreads; + // nThreads = 1; + + std::vector threads(nThreads); + + for (int i = 0; i < nThreads; i++) { + threads[i] = std::thread(myThread, i, nThreads); + } - } // iRow + // wait for the threads to finish + for (auto& th : threads) { + th.join(); + } } // iRoc - LOGP(info, "Reading & reparing of the track residuals tooks: {}s", watch1.RealTime()); + LOGP(info, "Reading & reparing of the track residuals tooks: {}s", watch3.RealTime()); LOG(info) << "fast space charge correction helper: create space charge from the map of data points.."; + TStopwatch watch4; + helper->fillSpaceChargeCorrectionFromMap(correction); - LOGP(info, "Creation from track residuals tooks in total: {}s", watch2.RealTime()); + LOG(info) << "fast space charge correction helper: creation from the data map took " << watch4.RealTime() << "s"; + + LOGP(info, "Creation from track residuals tooks in total: {}s", watch.RealTime()); return std::move(correctionPtr); } diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx index 5a7dffd2a753b..35c6e43daa43b 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx @@ -29,12 +29,9 @@ ClassImp(TPCFastSpaceChargeCorrection); TPCFastSpaceChargeCorrection::TPCFastSpaceChargeCorrection() : FlatObject(), - mConstructionRowInfos(nullptr), mConstructionScenarios(nullptr), mNumberOfScenarios(0), mScenarioPtr(nullptr), - mRowInfoPtr(nullptr), - mSliceRowInfoPtr(nullptr), mTimeStamp(-1), mSplineData{nullptr, nullptr, nullptr}, mSliceDataSizeBytes{0, 0, 0} @@ -52,21 +49,16 @@ void TPCFastSpaceChargeCorrection::releaseConstructionMemory() { // release temporary arrays #if !defined(GPUCA_GPUCODE) - delete[] mConstructionRowInfos; delete[] mConstructionScenarios; #endif - mConstructionRowInfos = nullptr; mConstructionScenarios = nullptr; } void TPCFastSpaceChargeCorrection::destroy() { releaseConstructionMemory(); - mConstructionRowInfos = nullptr; mConstructionScenarios = nullptr; mNumberOfScenarios = 0; - mRowInfoPtr = nullptr; - mSliceRowInfoPtr = nullptr; mScenarioPtr = nullptr; mTimeStamp = -1; for (int32_t is = 0; is < 3; is++) { @@ -78,8 +70,6 @@ void TPCFastSpaceChargeCorrection::destroy() void TPCFastSpaceChargeCorrection::relocateBufferPointers(const char* oldBuffer, char* newBuffer) { - mRowInfoPtr = FlatObject::relocatePointer(oldBuffer, newBuffer, mRowInfoPtr); - mSliceRowInfoPtr = FlatObject::relocatePointer(oldBuffer, newBuffer, mSliceRowInfoPtr); mScenarioPtr = FlatObject::relocatePointer(oldBuffer, newBuffer, mScenarioPtr); for (int32_t i = 0; i < mNumberOfScenarios; i++) { @@ -119,13 +109,21 @@ void TPCFastSpaceChargeCorrection::cloneFromObject(const TPCFastSpaceChargeCorre mSliceDataSizeBytes[2] = obj.mSliceDataSizeBytes[2]; // variable-size data - mRowInfoPtr = obj.mRowInfoPtr; - mSliceRowInfoPtr = obj.mSliceRowInfoPtr; mScenarioPtr = obj.mScenarioPtr; mSplineData[0] = obj.mSplineData[0]; mSplineData[1] = obj.mSplineData[1]; mSplineData[2] = obj.mSplineData[2]; + mClassVersion = obj.mClassVersion; + + for (int i = 0; i < TPCFastTransformGeo::getMaxNumberOfRows(); i++) { + mRowInfos[i] = obj.mRowInfos[i]; + } + + for (int i = 0; i < TPCFastTransformGeo::getNumberOfSlices() * TPCFastTransformGeo::getMaxNumberOfRows(); i++) { + mSliceRowInfos[i] = obj.mSliceRowInfos[i]; + } + relocateBufferPointers(oldFlatBufferPtr, mFlatBufferPtr); } @@ -140,19 +138,43 @@ void TPCFastSpaceChargeCorrection::moveBufferTo(char* newFlatBufferPtr) void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBufferPtr) { - /// Sets the actual location of the external flat buffer after it has been moved (i.e. to another maschine) + /// Sets the actual location of the external flat buffer after it has been moved (e.g. to another maschine) + + struct RowInfoVersion3 { + int splineScenarioID{0}; ///< scenario index (which of Spline2D splines to use) + size_t dataOffsetBytes[3]{0}; ///< offset for the spline data withing a TPC slice + }; + + struct RowActiveAreaVersion3 { + float maxDriftLengthCheb[5]{0.f}; + float vMax{0.f}; + float cuMin{0.f}; + float cuMax{0.f}; + float cvMax{0.f}; + }; + + struct SliceRowInfoVersion3 { + float gridV0{0.f}; ///< V coordinate of the V-grid start + float gridCorrU0{0.f}; ///< U coordinate of the U-grid start for corrected U + float gridCorrV0{0.f}; ///< V coordinate of the V-grid start for corrected V + float scaleCorrUtoGrid{0.f}; ///< scale corrected U to U-grid coordinate + float scaleCorrVtoGrid{0.f}; ///< scale corrected V to V-grid coordinate + RowActiveAreaVersion3 activeArea; + }; FlatObject::setActualBufferAddress(actualFlatBufferPtr); size_t rowsOffset = 0; - size_t rowsSize = sizeof(RowInfo) * mGeo.getNumberOfRows(); - - mRowInfoPtr = reinterpret_cast(mFlatBufferPtr + rowsOffset); + size_t rowsSize = 0; + if (mClassVersion == 3) { + rowsSize = sizeof(RowInfoVersion3) * mGeo.getNumberOfRows(); + } size_t sliceRowsOffset = rowsOffset + rowsSize; - size_t sliceRowsSize = sizeof(SliceRowInfo) * mGeo.getNumberOfRows() * mGeo.getNumberOfSlices(); - - mSliceRowInfoPtr = reinterpret_cast(mFlatBufferPtr + sliceRowsOffset); + size_t sliceRowsSize = 0; + if (mClassVersion == 3) { // copy old-format slicerow data from the buffer to the arrays + sliceRowsSize = sizeof(SliceRowInfoVersion3) * mGeo.getNumberOfRows() * mGeo.getNumberOfSlices(); + } size_t scOffset = alignSize(sliceRowsOffset + sliceRowsSize, SplineType::getClassAlignmentBytes()); size_t scSize = sizeof(SplineType) * mNumberOfScenarios; @@ -173,6 +195,53 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer mSplineData[is] = reinterpret_cast(mFlatBufferPtr + sliceDataOffset); bufferSize = sliceDataOffset + mSliceDataSizeBytes[is] * mGeo.getNumberOfSlices(); } + + if (mClassVersion == 3) { // copy old-format slicerow data from the buffer to the arrays + + auto* rowInfosOld = reinterpret_cast(mFlatBufferPtr + rowsOffset); + for (int i = 0; i < mGeo.getNumberOfRows(); i++) { + RowInfoVersion3& infoOld = rowInfosOld[i]; + RowInfo& info = mRowInfos[i]; + info.splineScenarioID = infoOld.splineScenarioID; + for (int is = 0; is < 3; is++) { + info.dataOffsetBytes[is] = infoOld.dataOffsetBytes[is]; + } + } + + for (int is = 0; is < mNumberOfScenarios; is++) { + auto& spline = mScenarioPtr[is]; + spline.setXrange(0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax()); + } + + auto* sliceRowInfosOld = reinterpret_cast(mFlatBufferPtr + sliceRowsOffset); + + for (int slice = 0; slice < mGeo.getNumberOfSlices(); slice++) { + for (int row = 0; row < mGeo.getNumberOfRows(); row++) { + SliceRowInfoVersion3& infoOld = sliceRowInfosOld[mGeo.getNumberOfRows() * slice + row]; + SliceRowInfo& info = getSliceRowInfo(slice, row); + const auto& spline = getSpline(slice, row); + info.gridU0 = mGeo.getRowInfo(row).u0; + info.scaleUtoGrid = spline.getGridX1().getUmax() / mGeo.getRowInfo(row).getUwidth(); + + info.gridV0 = infoOld.gridV0; + info.scaleVtoGrid = spline.getGridX2().getUmax() / (mGeo.getTPCzLength(slice) + 3. - info.gridV0); + + info.gridCorrU0 = infoOld.gridCorrU0; + info.scaleCorrUtoGrid = infoOld.scaleCorrUtoGrid; + + info.gridCorrV0 = infoOld.gridCorrV0; + info.scaleCorrVtoGrid = infoOld.scaleCorrVtoGrid; + + info.activeArea.vMax = infoOld.activeArea.vMax; + info.activeArea.cuMin = infoOld.activeArea.cuMin; + info.activeArea.cuMax = infoOld.activeArea.cuMax; + info.activeArea.cvMax = infoOld.activeArea.cvMax; + for (int i = 0; i < 5; i++) { + info.activeArea.maxDriftLengthCheb[i] = infoOld.activeArea.maxDriftLengthCheb[i]; + } + } + } + } } void TPCFastSpaceChargeCorrection::setFutureBufferAddress(char* futureFlatBufferPtr) @@ -187,10 +256,7 @@ void TPCFastSpaceChargeCorrection::setFutureBufferAddress(char* futureFlatBuffer char* oldBuffer = mFlatBufferPtr; char* newBuffer = futureFlatBufferPtr; - mRowInfoPtr = relocatePointer(oldBuffer, newBuffer, mRowInfoPtr); - mSliceRowInfoPtr = relocatePointer(oldBuffer, newBuffer, mSliceRowInfoPtr); - - for (int32_t i = 0; i < mNumberOfScenarios; i++) { + for (int i = 0; i < mNumberOfScenarios; i++) { SplineType& sp = mScenarioPtr[i]; char* newSplineBuf = relocatePointer(oldBuffer, newBuffer, sp.getFlatBufferPtr()); sp.setFutureBufferAddress(newSplineBuf); @@ -210,10 +276,10 @@ void TPCFastSpaceChargeCorrection::print() const LOG(info) << " mNumberOfScenarios = " << mNumberOfScenarios; LOG(info) << " mTimeStamp = " << mTimeStamp; LOG(info) << " mSliceDataSizeBytes = " << mSliceDataSizeBytes[0] << " " << mSliceDataSizeBytes[1] << " " << mSliceDataSizeBytes[2]; - if (mRowInfoPtr) { + { LOG(info) << " TPC rows: "; - for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { - RowInfo& r = mRowInfoPtr[i]; + for (int i = 0; i < mGeo.getNumberOfRows(); i++) { + const RowInfo& r = mRowInfos[i]; LOG(info) << " tpc row " << i << ": splineScenarioID = " << r.splineScenarioID << " dataOffsetBytes = " << r.dataOffsetBytes; } } @@ -223,7 +289,7 @@ void TPCFastSpaceChargeCorrection::print() const mScenarioPtr[i].print(); } } - if (mRowInfoPtr && mScenarioPtr && mSliceRowInfoPtr) { + if (mScenarioPtr) { LOG(info) << " Spline Data: "; for (int32_t is = 0; is < mGeo.getNumberOfSlices(); is++) { for (int32_t ir = 0; ir < mGeo.getNumberOfRows(); ir++) { @@ -260,15 +326,13 @@ void TPCFastSpaceChargeCorrection::startConstruction(const TPCFastTransformGeo& releaseConstructionMemory(); #if !defined(GPUCA_GPUCODE) - mConstructionRowInfos = new RowInfo[mGeo.getNumberOfRows()]; mConstructionScenarios = new SplineType[mNumberOfScenarios]; #endif - assert(mConstructionRowInfos != nullptr); assert(mConstructionScenarios != nullptr); - for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { - mConstructionRowInfos[i].splineScenarioID = -1; + for (int i = 0; i < mGeo.getNumberOfRows(); i++) { + mRowInfos[i].splineScenarioID = -1; } for (int32_t i = 0; i < mNumberOfScenarios; i++) { @@ -277,13 +341,12 @@ void TPCFastSpaceChargeCorrection::startConstruction(const TPCFastTransformGeo& mTimeStamp = -1; - mRowInfoPtr = nullptr; - mSliceRowInfoPtr = nullptr; mScenarioPtr = nullptr; for (int32_t s = 0; s < 3; s++) { mSplineData[s] = nullptr; mSliceDataSizeBytes[s] = 0; } + mClassVersion = 4; } void TPCFastSpaceChargeCorrection::setRowScenarioID(int32_t iRow, int32_t iScenario) @@ -292,7 +355,7 @@ void TPCFastSpaceChargeCorrection::setRowScenarioID(int32_t iRow, int32_t iScena assert(mConstructionMask & ConstructionState::InProgress); assert(iRow >= 0 && iRow < mGeo.getNumberOfRows() && iScenario >= 0 && iScenario < mNumberOfScenarios); - RowInfo& row = mConstructionRowInfos[iRow]; + RowInfo& row = mRowInfos[iRow]; row.splineScenarioID = iScenario; for (int32_t s = 0; s < 3; s++) { row.dataOffsetBytes[s] = 0; @@ -315,8 +378,8 @@ void TPCFastSpaceChargeCorrection::finishConstruction() assert(mConstructionMask & ConstructionState::InProgress); - for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { - assert(mConstructionRowInfos[i].splineScenarioID >= 0); + for (int i = 0; i < mGeo.getNumberOfRows(); i++) { + assert(mRowInfos[i].splineScenarioID >= 0); } for (int32_t i = 0; i < mNumberOfScenarios; i++) { assert(mConstructionScenarios[i].isConstructed()); @@ -324,13 +387,7 @@ void TPCFastSpaceChargeCorrection::finishConstruction() // organize memory for the flat buffer and caculate its size - size_t rowsOffset = 0; - size_t rowsSize = sizeof(RowInfo) * mGeo.getNumberOfRows(); - - size_t sliceRowsOffset = rowsSize; - size_t sliceRowsSize = sizeof(SliceRowInfo) * mGeo.getNumberOfRows() * mGeo.getNumberOfSlices(); - - size_t scOffset = alignSize(sliceRowsOffset + sliceRowsSize, SplineType::getClassAlignmentBytes()); + size_t scOffset = 0; size_t scSize = sizeof(SplineType) * mNumberOfScenarios; size_t scBufferOffsets[mNumberOfScenarios]; @@ -347,8 +404,8 @@ void TPCFastSpaceChargeCorrection::finishConstruction() for (int32_t is = 0; is < 3; is++) { sliceDataOffset[is] = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); mSliceDataSizeBytes[is] = 0; - for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { - RowInfo& row = mConstructionRowInfos[i]; + for (int i = 0; i < mGeo.getNumberOfRows(); i++) { + RowInfo& row = mRowInfos[i]; SplineType& spline = mConstructionScenarios[row.splineScenarioID]; row.dataOffsetBytes[is] = alignSize(mSliceDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); mSliceDataSizeBytes[is] = row.dataOffsetBytes[is] + spline.getSizeOfParameters(); @@ -359,20 +416,6 @@ void TPCFastSpaceChargeCorrection::finishConstruction() FlatObject::finishConstruction(bufferSize); - mRowInfoPtr = reinterpret_cast(mFlatBufferPtr + rowsOffset); - for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { - mRowInfoPtr[i] = mConstructionRowInfos[i]; - } - - mSliceRowInfoPtr = reinterpret_cast(mFlatBufferPtr + sliceRowsOffset); - for (int32_t s = 0; s < mGeo.getNumberOfSlices(); s++) { - for (int32_t r = 0; r < mGeo.getNumberOfRows(); r++) { - mSliceRowInfoPtr[s * mGeo.getNumberOfRows() + r].gridCorrU0 = 0.; - mSliceRowInfoPtr[s * mGeo.getNumberOfRows() + r].scaleCorrUtoGrid = 0.; - mSliceRowInfoPtr[s * mGeo.getNumberOfRows() + r].scaleCorrVtoGrid = 0.; - } - } - mScenarioPtr = reinterpret_cast(mFlatBufferPtr + scOffset); for (int32_t i = 0; i < mNumberOfScenarios; i++) { @@ -417,20 +460,28 @@ GPUd() void TPCFastSpaceChargeCorrection::setNoCorrection() } SliceRowInfo& info = getSliceRowInfo(slice, row); + + info.gridU0 = mGeo.getRowInfo(row).u0; + info.scaleUtoGrid = spline.getGridX1().getUmax() / mGeo.getRowInfo(row).getUwidth(); + + info.gridV0 = 0.f; + info.scaleVtoGrid = spline.getGridX2().getUmax() / vLength; + + info.gridCorrU0 = info.gridU0; + info.gridCorrV0 = info.gridV0; + info.scaleCorrUtoGrid = info.scaleUtoGrid; + info.scaleCorrVtoGrid = info.scaleVtoGrid; + RowActiveArea& area = info.activeArea; for (int32_t i = 1; i < 5; i++) { area.maxDriftLengthCheb[i] = 0; } area.maxDriftLengthCheb[0] = vLength; - area.cuMin = mGeo.convPadToU(row, 0.f); + area.cuMin = info.gridCorrU0; area.cuMax = -area.cuMin; area.vMax = vLength; area.cvMax = vLength; - info.gridV0 = 0.f; - info.gridCorrU0 = area.cuMin; - info.gridCorrV0 = info.gridV0; - info.scaleCorrUtoGrid = spline.getGridX1().getUmax() / (area.cuMax - area.cuMin); - info.scaleCorrVtoGrid = spline.getGridX2().getUmax() / area.cvMax; + } // row } // slice } diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index 9589ecbfc1fc4..b29d65b98458a 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -58,13 +58,16 @@ class TPCFastSpaceChargeCorrection : public FlatObject }; struct SliceRowInfo { + float gridU0{0.f}; //< U coordinate of the U-grid start + float scaleUtoGrid{0.f}; //< scale U to U-grid coordinate float gridV0{0.f}; ///< V coordinate of the V-grid start + float scaleVtoGrid{0.f}; //< scale V to V-grid coordinate float gridCorrU0{0.f}; ///< U coordinate of the U-grid start for corrected U - float gridCorrV0{0.f}; ///< V coordinate of the V-grid start for corrected V float scaleCorrUtoGrid{0.f}; ///< scale corrected U to U-grid coordinate + float gridCorrV0{0.f}; ///< V coordinate of the V-grid start for corrected V float scaleCorrVtoGrid{0.f}; ///< scale corrected V to V-grid coordinate RowActiveArea activeArea; - ClassDefNV(SliceRowInfo, 1); + ClassDefNV(SliceRowInfo, 2); }; struct SliceInfo { @@ -199,7 +202,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject GPUd() float getInterpolationSafetyMargin() const { return fInterpolationSafetyMargin; } /// Gives TPC row info - GPUd() const RowInfo& getRowInfo(int32_t row) const { return mRowInfoPtr[row]; } + GPUd() const RowInfo& getRowInfo(int row) const { return mRowInfos[row]; } /// Gives TPC slice info GPUd() const SliceInfo& getSliceInfo(int32_t slice) const @@ -216,13 +219,13 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// Gives TPC slice & row info GPUd() const SliceRowInfo& getSliceRowInfo(int32_t slice, int32_t row) const { - return mSliceRowInfoPtr[mGeo.getNumberOfRows() * slice + row]; + return mSliceRowInfos[mGeo.getMaxNumberOfRows() * slice + row]; } /// Gives TPC slice & row info GPUd() SliceRowInfo& getSliceRowInfo(int32_t slice, int32_t row) { - return mSliceRowInfoPtr[mGeo.getNumberOfRows() * slice + row]; + return mSliceRowInfos[mGeo.getMaxNumberOfRows() * slice + row]; } #if !defined(GPUCA_GPUCODE) @@ -244,7 +247,6 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// _______________ Construction control _______________________________________________ - RowInfo* mConstructionRowInfos = nullptr; //! (transient!!) Temporary container of the row infos during construction SplineType* mConstructionScenarios = nullptr; //! (transient!!) Temporary container for spline scenarios /// _______________ Geometry _______________________________________________ @@ -255,9 +257,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject SliceInfo mSliceInfo[TPCFastTransformGeo::getNumberOfSlices()]; ///< SliceInfo array - SplineType* mScenarioPtr; //! (transient!!) pointer to spline scenarios - RowInfo* mRowInfoPtr; //! (transient!!) pointer to RowInfo array inside the mFlatBufferPtr buffer - SliceRowInfo* mSliceRowInfoPtr; //! (transient!!) pointer to SliceRowInfo array inside the mFlatBufferPtr + SplineType* mScenarioPtr; //! (transient!!) pointer to spline scenarios /// _______________ Calibration data _______________________________________________ @@ -269,7 +269,16 @@ class TPCFastSpaceChargeCorrection : public FlatObject float fInterpolationSafetyMargin{0.1f}; // 10% area around the TPC row. Outside of this area the interpolation returns the boundary values. - ClassDefNV(TPCFastSpaceChargeCorrection, 3); + /// Class version. It is used to read older versions from disc. + /// The default version 3 is the one before this field was introduced. + /// The actual version must be set in startConstruction(). + int mClassVersion{3}; + + RowInfo mRowInfos[TPCFastTransformGeo::getMaxNumberOfRows()]; ///< RowInfo array + + SliceRowInfo mSliceRowInfos[TPCFastTransformGeo::getNumberOfSlices() * TPCFastTransformGeo::getMaxNumberOfRows()]; ///< SliceRowInfo array + + ClassDefNV(TPCFastSpaceChargeCorrection, 4); }; /// ==================================================== @@ -279,28 +288,28 @@ class TPCFastSpaceChargeCorrection : public FlatObject GPUdi() const TPCFastSpaceChargeCorrection::SplineType& TPCFastSpaceChargeCorrection::getSpline(int32_t slice, int32_t row) const { /// Gives const pointer to spline - const RowInfo& rowInfo = mRowInfoPtr[row]; + const RowInfo& rowInfo = mRowInfos[row]; return mScenarioPtr[rowInfo.splineScenarioID]; } GPUdi() TPCFastSpaceChargeCorrection::SplineType& TPCFastSpaceChargeCorrection::getSpline(int32_t slice, int32_t row) { /// Gives pointer to spline - const RowInfo& rowInfo = mRowInfoPtr[row]; + const RowInfo& rowInfo = mRowInfos[row]; return mScenarioPtr[rowInfo.splineScenarioID]; } GPUdi() float* TPCFastSpaceChargeCorrection::getSplineData(int32_t slice, int32_t row, int32_t iSpline) { /// Gives pointer to spline data - const RowInfo& rowInfo = mRowInfoPtr[row]; + const RowInfo& rowInfo = mRowInfos[row]; return reinterpret_cast(mSplineData[iSpline] + mSliceDataSizeBytes[iSpline] * slice + rowInfo.dataOffsetBytes[iSpline]); } GPUdi() const float* TPCFastSpaceChargeCorrection::getSplineData(int32_t slice, int32_t row, int32_t iSpline) const { /// Gives pointer to spline data - const RowInfo& rowInfo = mRowInfoPtr[row]; + const RowInfo& rowInfo = mRowInfos[row]; return reinterpret_cast(mSplineData[iSpline] + mSliceDataSizeBytes[iSpline] * slice + rowInfo.dataOffsetBytes[iSpline]); } @@ -356,35 +365,18 @@ GPUdi() void TPCFastSpaceChargeCorrection::schrinkCorrectedUV(int32_t slice, int GPUdi() void TPCFastSpaceChargeCorrection::convUVtoGrid(int32_t slice, int32_t row, float u, float v, float& gu, float& gv) const { - // TODO optimise !!! - gu = 0.f; - gv = 0.f; - schrinkUV(slice, row, u, v); - const SliceRowInfo& info = getSliceRowInfo(slice, row); - const SplineType& spline = getSpline(slice, row); - - float su0 = 0.f, sv0 = 0.f; - mGeo.convUVtoScaledUV(slice, row, u, info.gridV0, su0, sv0); - mGeo.convUVtoScaledUV(slice, row, u, v, gu, gv); - - gv = (gv - sv0) / (1.f - sv0); - gu *= spline.getGridX1().getUmax(); - gv *= spline.getGridX2().getUmax(); + gu = (u - info.gridU0) * info.scaleUtoGrid; + gv = (v - info.gridV0) * info.scaleVtoGrid; } GPUdi() void TPCFastSpaceChargeCorrection::convGridToUV(int32_t slice, int32_t row, float gridU, float gridV, float& u, float& v) const { - // TODO optimise - /// convert u,v to internal grid coordinates - float su0 = 0.f, sv0 = 0.f; + /// convert internal grid coordinates to u,v const SliceRowInfo& info = getSliceRowInfo(slice, row); - const SplineType& spline = getSpline(slice, row); - mGeo.convUVtoScaledUV(slice, row, 0.f, info.gridV0, su0, sv0); - float su = gridU / spline.getGridX1().getUmax(); - float sv = sv0 + gridV / spline.getGridX2().getUmax() * (1.f - sv0); - mGeo.convScaledUVtoUV(slice, row, su, sv, u, v); + u = info.gridU0 + gridU / info.scaleUtoGrid; + v = info.gridV0 + gridV / info.scaleVtoGrid; } GPUdi() void TPCFastSpaceChargeCorrection::convCorrectedUVtoGrid(int32_t slice, int32_t row, float corrU, float corrV, float& gridU, float& gridV) const diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.h b/GPU/TPCFastTransformation/TPCFastTransformGeo.h index 5eddada1e9acc..d20331ba6ab0f 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.h +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.h @@ -50,6 +50,12 @@ class TPCFastTransformGeo float scaleUtoSU; ///< scale for su (scaled u ) coordinate float scaleSUtoU; ///< scale for u coordinate + /// get U min + GPUd() float getUmin() const { return u0; } + + /// get U max + GPUd() float getUmax() const { return -u0; } + /// get width in U GPUd() float getUwidth() const { return -2.f * u0; } ClassDefNV(RowInfo, 1); @@ -110,6 +116,9 @@ class TPCFastTransformGeo /// Gives number of TPC rows GPUd() int32_t getNumberOfRows() const { return mNumberOfRows; } + /// Gives number of TPC rows + GPUd() static constexpr int getMaxNumberOfRows() { return MaxNumberOfRows; } + /// Gives slice info GPUd() const SliceInfo& getSliceInfo(int32_t slice) const; diff --git a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C index b13d031d6d10d..bf3e14d552715 100644 --- a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C +++ b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C @@ -59,6 +59,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", corr->Draw("cx:y:z","iRoc==0&&iRow==10","") grid->Draw("cx:y:z","iRoc==0&&iRow==10","same") vox->Draw("vx:y:z","iRoc==0&&iRow==10","same") + corrvox->Draw("cx:y:z","iRoc==0&&iRow==10","same") points->Draw("px:y:z","iRoc==0&&iRow==10","same") */ @@ -98,17 +99,36 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", trackResiduals.setZ2XBinning(z2xBins); trackResiduals.init(); - std::cout << "y2xBins: " << y2xBins.size() << " z2xBins: " << z2xBins.size() << std::endl; + { + std::cout << "input track residuals: " << std::endl; + std::cout << "voxel tree y2xBins: " << y2xBins.size() << std::endl; - for (auto y2x : y2xBins) { - std::cout << "y2x: " << y2x << std::endl; - } + for (auto y2x : y2xBins) { + std::cout << " y2x: " << y2x << std::endl; + } + std::cout << std::endl; + + int nY2Xbins = trackResiduals.getNY2XBins(); + + std::cout << " TrackResiduals y2x bins: " << nY2Xbins << std::endl; + for (int i = 0; i < nY2Xbins; i++) { + std::cout << "scaled getY2X(bin) : " << trackResiduals.getY2X(0, i) / trackResiduals.getMaxY2X(0) << std::endl; + } + + std::cout << "voxel tree z2xBins: " << z2xBins.size() << std::endl; - std::cout << std::endl; + for (auto z2x : z2xBins) { + std::cout << "z2x: " << z2x << std::endl; + } + std::cout << std::endl; - for (auto z2x : z2xBins) { - std::cout << "z2x: " << z2x << std::endl; + int nZ2Xbins = trackResiduals.getNZ2XBins(); + std::cout << " TrackResiduals z2x bins: " << nZ2Xbins << std::endl; + for (int i = 0; i < nZ2Xbins; i++) { + std::cout << "getZ2X(bin) : " << trackResiduals.getZ2X(i) << std::endl; + } } + std::cout << "create fast transformation ... " << std::endl; auto* helper = o2::tpc::TPCFastTransformHelperO2::instance(); @@ -122,16 +142,47 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", std::unique_ptr fastTransform( helper->create(0, *corrPtr)); - o2::gpu::TPCFastSpaceChargeCorrection& corr = fastTransform->getCorrection(); - std::cout << "... create fast transformation completed " << std::endl; if (*outFileName) { fastTransform->writeToFile(outFileName, "ccdb_object"); } + if (1) { // read transformation from the file + + // const char* fileName = "master/out.root"; + + const char* fileName = outFileName; + + std::cout << "load corrections from file " << fileName << std::endl; + + fastTransform->cloneFromObject(*TPCFastTransform::loadFromFile(fileName, "ccdb_object"), nullptr); + + o2::gpu::TPCFastSpaceChargeCorrection& corr = fastTransform->getCorrection(); + + if (0) { + std::cout << "check the loaded correction ..." << std::endl; + + const o2::gpu::TPCFastTransformGeo& geo = helper->getGeometry(); + + // for (int iRoc = 0; iRoc < geo.getNumberOfSlices(); iRoc++) { + for (int iRoc = 0; iRoc < 1; iRoc++) { + for (int iRow = 0; iRow < geo.getNumberOfRows(); iRow++) { + auto& info = corr.getSliceRowInfo(iRoc, iRow); + std::cout << "roc " << iRoc << " row " << iRow + << " gridV0 " << info.gridV0 << " gridCorrU0 " << info.gridCorrU0 << " gridCorrV0 " << info.gridCorrV0 + << " scaleCorrUtoGrid " << info.scaleCorrUtoGrid << " scaleCorrVtoGrid " << info.scaleCorrVtoGrid + << " gridU0 " << info.gridU0 << " scaleUtoGrid " << info.scaleUtoGrid << " scaleVtoGrid " << info.scaleVtoGrid + << std::endl; + } + } + } + } + std::cout << "verify the results ..." << std::endl; + o2::gpu::TPCFastSpaceChargeCorrection& corr = fastTransform->getCorrection(); + // the difference double maxDiff[3] = {0., 0., 0.}; @@ -158,12 +209,21 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", // ntuple with the input data: voxel corrections debugFile->cd(); TNtuple* debugVox = - new TNtuple("vox", "vox", "iRoc:iRow:x:y:z:vx:vy:vz:cx:cy:cz"); + new TNtuple("vox", "vox", "iRoc:iRow:n:x:y:z:vx:vy:vz:cx:cy:cz"); debugVox->SetMarkerStyle(8); debugVox->SetMarkerSize(0.8); debugVox->SetMarkerColor(kBlue); + // duplicate of debugVox + debugFile->cd(); + TNtuple* debugCorrVox = + new TNtuple("corrvox", "corrvox", "iRoc:iRow:n:x:y:z:vx:vy:vz:cx:cy:cz"); + + debugCorrVox->SetMarkerStyle(8); + debugCorrVox->SetMarkerSize(0.8); + debugCorrVox->SetMarkerColor(kMagenta); + // ntuple with spline grid points debugFile->cd(); TNtuple* debugGrid = new TNtuple("grid", "grid", "iRoc:iRow:x:y:z:cx:cy:cz"); @@ -244,13 +304,6 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", correctionZ *= -1.; } - // TODO: skip empty voxels? - if (voxEntries < 1.) { // no statistics - // std::cout << "Empty Voxel!!! corrections: " << correctionX << " " - // << correctionY << " " << correctionZ << std::endl; - // continue; - } - float u, v, cx, cu, cv, cy, cz; geo.convLocalToUV(iRoc, y, z, u, v); corr.getCorrection(iRoc, iRow, u, v, cx, cu, cv); @@ -258,19 +311,24 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", cy -= y; cz -= z; double d[3] = {cx - correctionX, cy - correctionY, cz - correctionZ}; - for (int32_t i = 0; i < 3; i++) { - if (fabs(maxDiff[i]) < fabs(d[i])) { - maxDiff[i] = d[i]; - maxDiffRoc[i] = iRoc; - maxDiffRow[i] = iRow; - std::cout << " roc " << iRoc << " row " << iRow << " xyz " << i - << " diff " << d[i] << std::endl; + if (voxEntries >= 1.) { + for (int i = 0; i < 3; i++) { + if (fabs(maxDiff[i]) < fabs(d[i])) { + maxDiff[i] = d[i]; + maxDiffRoc[i] = iRoc; + maxDiffRow[i] = iRow; + std::cout << " roc " << iRoc << " row " << iRow << " xyz " << i + << " diff " << d[i] << " entries " << voxEntries << " y " << y2xBin << " z " << z2xBin << std::endl; + } + sumDiff[i] += d[i] * d[i]; } - sumDiff[i] += d[i] * d[i]; + nDiff++; } - nDiff++; - debugVox->Fill(iRoc, iRow, x, y, z, correctionX, correctionY, correctionZ, + + debugVox->Fill(iRoc, iRow, voxEntries, x, y, z, correctionX, correctionY, correctionZ, cx, cy, cz); + debugCorrVox->Fill(iRoc, iRow, voxEntries, x, y, z, correctionX, correctionY, correctionZ, + cx, cy, cz); } std::cout << "create debug ntuples ..." << std::endl; @@ -282,43 +340,71 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", double x = geo.getRowInfo(iRow).x; + // the spline grid + + const auto& gridU = corr.getSpline(iRoc, iRow).getGridX1(); + const auto& gridV = corr.getSpline(iRoc, iRow).getGridX2(); + if (iRoc == 0 && iRow == 0) { + std::cout << "spline scenario " << corr.getRowInfo(iRow).splineScenarioID << std::endl; + std::cout << "spline grid U: u = " << 0 << ".." << gridU.getUmax() << ", x = " << gridU.getXmin() << ".." << gridU.getXmax() << std::endl; + std::cout << "spline grid V: u = " << 0 << ".." << gridV.getUmax() << ", x = " << gridV.getXmin() << ".." << gridV.getXmax() << std::endl; + } + // the correction + { + std::vector p[2], g[2]; - for (double su = 0.; su <= 1.0001; su += 0.01) { - for (double sv = 0.; sv <= 1.0001; sv += 0.1) { + p[0].push_back(geo.getRowInfo(iRow).getUmin()); + for (int iu = 0; iu < gridU.getNumberOfKnots(); iu++) { float u, v; - geo.convScaledUVtoUV(iRoc, iRow, su, sv, u, v); - float y, z; - geo.convUVtoLocal(iRoc, u, v, y, z); - float cx, cu, cv; - corr.getCorrection(iRoc, iRow, u, v, cx, cu, cv); - float cy, cz; - geo.convUVtoLocal(iRoc, u + cu, v + cv, cy, cz); - cy -= y; - cz -= z; - debugCorr->Fill(iRoc, iRow, x, y, z, cx, cy, cz); + corr.convGridToUV(iRoc, iRow, gridU.getKnot(iu).getU(), 0., u, v); + g[0].push_back(u); + p[0].push_back(u); } - } - - // the spline grid + p[0].push_back(geo.getRowInfo(iRow).getUmax()); - const auto& gridU = corr.getSpline(iRoc, iRow).getGridX1(); - const auto& gridV = corr.getSpline(iRoc, iRow).getGridX2(); - for (int32_t iu = 0; iu < gridU.getNumberOfKnots(); iu++) { - double su = gridU.convUtoX(gridU.getKnot(iu).getU()); - for (int32_t iv = 0; iv < gridV.getNumberOfKnots(); iv++) { - double sv = gridV.convUtoX(gridV.getKnot(iv).getU()); + p[1].push_back(0.); + for (int iv = 0; iv < gridV.getNumberOfKnots(); iv++) { float u, v; - corr.convGridToUV(iRoc, iRow, iu, iv, u, v); - float y, z; - geo.convUVtoLocal(iRoc, u, v, y, z); - float cx, cu, cv; - corr.getCorrection(iRoc, iRow, u, v, cx, cu, cv); - float cy, cz; - geo.convUVtoLocal(iRoc, u + cu, v + cv, cy, cz); - cy -= y; - cz -= z; - debugGrid->Fill(iRoc, iRow, x, y, z, cx, cy, cz); + corr.convGridToUV(iRoc, iRow, 0., gridV.getKnot(iv).getU(), u, v); + g[1].push_back(v); + p[1].push_back(v); + } + p[1].push_back(geo.getTPCzLength(iRoc)); + + for (int iuv = 0; iuv < 2; iuv++) { + int n = p[iuv].size(); + for (unsigned int i = 0; i < n - 1; i++) { + double d = (p[iuv][i + 1] - p[iuv][i]) / 10.; + for (int ii = 1; ii < 10; ii++) { + p[iuv].push_back(p[iuv][i] + d * ii); + } + } + std::sort(p[iuv].begin(), p[iuv].end()); + } + + for (int iter = 0; iter < 2; iter++) { + std::vector& pu = ((iter == 0) ? g[0] : p[0]); + std::vector& pv = ((iter == 0) ? g[1] : p[1]); + for (unsigned int iu = 0; iu < pu.size(); iu++) { + for (unsigned int iv = 0; iv < pv.size(); iv++) { + float u = pu[iu]; + float v = pv[iv]; + float x, y, z; + geo.convUVtoLocal(iRoc, u, v, y, z); + float cx, cu, cv; + corr.getCorrection(iRoc, iRow, u, v, cx, cu, cv); + float cy, cz; + geo.convUVtoLocal(iRoc, u + cu, v + cv, cy, cz); + cy -= y; + cz -= z; + if (iter == 0) { + debugGrid->Fill(iRoc, iRow, x, y, z, cx, cy, cz); + } else { + debugCorr->Fill(iRoc, iRow, x, y, z, cx, cy, cz); + } + } + } } } @@ -372,6 +458,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", debugFile->cd(); debugCorr->Write(); debugVox->Write(); + debugCorrVox->Write(); debugGrid->Write(); debugPoints->Write(); debugFile->Close(); From 658149075aa6fb4d8749d0a19dea12e1b792a3f8 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Thu, 18 Apr 2024 00:30:38 +0000 Subject: [PATCH 498/701] TPC Splines: multithreaded reading of the residual tree --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 73 +++++++++---------- 1 file changed, 33 insertions(+), 40 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index 861cacbe00012..ce0954120281f 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -31,6 +31,7 @@ #include "TStopwatch.h" #include "TTreeReader.h" #include "TTreeReaderValue.h" +#include "ROOT/TTreeProcessorMT.hxx" using namespace o2::gpu; @@ -541,9 +542,6 @@ std::unique_ptr TPCFastSpaceChargeCorrect TStopwatch watch3; - // TTreeProcessorMT treeProcessor(*voxResTree); // multi-threaded tree processor - // treeProcessor.Init(voxResTree); - // read the data ROC by ROC // data in the tree is not sorted by row @@ -554,44 +552,40 @@ std::unique_ptr TPCFastSpaceChargeCorrect float mCx, mCy, mCz; // corrections to the local coordinates }; - std::vector vRocData[nRows]; - for (int ir = 0; ir < nRows; ir++) { + std::vector vRocData[nRows * nROCs]; + for (int ir = 0; ir < nRows * nROCs; ir++) { vRocData[ir].resize(nY2Xbins * nZ2Xbins); } - for (int iRoc = 0; iRoc < nROCs; iRoc++) { + { // read data from the tree to vRocData - for (int ir = 0; ir < nRows; ir++) { - for (int iv = 0; iv < nY2Xbins * nZ2Xbins; iv++) { - vRocData[ir][iv].mNentries = 0; - } - } + ROOT::TTreeProcessorMT processor(*voxResTree, mNthreads); - const int rocDataStart = iRoc * trackResiduals.getNVoxelsPerSector(); - const int rocDataEnd = rocDataStart + trackResiduals.getNVoxelsPerSector(); - - TTreeReader reader(voxResTree); - reader.SetEntriesRange(rocDataStart, rocDataEnd); - TTreeReaderValue v(reader, "voxRes"); - for (int iVox = rocDataStart; iVox < rocDataEnd; iVox++) { - reader.Next(); - // voxResTree->GetEntry(iVox); - if ((int)v->bsec != iRoc) { - LOG(fatal) << "Error reading voxels: voxel ROC number " << v->bsec << " is not equal to the expected " << iRoc; - continue; - } - int iRow = (int)v->bvox[o2::tpc::TrackResiduals::VoxX]; // bin number in x (= pad row) - if (iRow < 0 || iRow >= nRows) { - LOG(fatal) << "Row number " << iRow << " is out of range"; + auto myThread = [&](TTreeReader& readerSubRange) { + TTreeReaderValue v(readerSubRange, "voxRes"); + while (readerSubRange.Next()) { + int iRoc = (int)v->bsec; + if (iRoc < 0 || iRoc >= nROCs) { + LOG(fatal) << "Error reading voxels: voxel ROC number " << iRoc << " is out of range"; + continue; + } + int iRow = (int)v->bvox[o2::tpc::TrackResiduals::VoxX]; // bin number in x (= pad row) + if (iRow < 0 || iRow >= nRows) { + LOG(fatal) << "Row number " << iRow << " is out of range"; + } + int iy = v->bvox[o2::tpc::TrackResiduals::VoxF]; // bin number in y/x 0..14 + int iz = v->bvox[o2::tpc::TrackResiduals::VoxZ]; // bin number in z/x 0..4 + auto& vox = vRocData[iRoc * nRows + iRow][iy * nZ2Xbins + iz]; + vox.mNentries = (int)v->stat[o2::tpc::TrackResiduals::VoxV]; + vox.mCx = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResX] : v->D[o2::tpc::TrackResiduals::ResX]; + vox.mCy = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResY] : v->D[o2::tpc::TrackResiduals::ResY]; + vox.mCz = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResZ] : v->D[o2::tpc::TrackResiduals::ResZ]; } - int iy = v->bvox[o2::tpc::TrackResiduals::VoxF]; // bin number in y/x 0..14 - int iz = v->bvox[o2::tpc::TrackResiduals::VoxZ]; // bin number in z/x 0..4 - auto& vox = vRocData[iRow][iy * nZ2Xbins + iz]; - vox.mNentries = (int)v->stat[o2::tpc::TrackResiduals::VoxV]; - vox.mCx = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResX] : v->D[o2::tpc::TrackResiduals::ResX]; - vox.mCy = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResY] : v->D[o2::tpc::TrackResiduals::ResY]; - vox.mCz = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResZ] : v->D[o2::tpc::TrackResiduals::ResZ]; - } + }; + processor.Process(myThread); + } + + for (int iRoc = 0; iRoc < nROCs; iRoc++) { // now process the data row-by-row @@ -615,7 +609,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect bool isDataFound = false; for (int iy = 0; iy < nY2Xbins; iy++) { for (int iz = 0; iz < nZ2Xbins; iz++) { - auto& data = vRocData[iRow][iy * nZ2Xbins + iz]; + auto& data = vRocData[iRoc * nRows + iRow][iy * nZ2Xbins + iz]; auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; // y/x coordinate of the bin ~-0.15 ... 0.15 double y2x = trackResiduals.getY2X(xBin, iy); @@ -661,7 +655,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect for (int ismooth = 1; ismooth <= 2; ismooth++) { for (int iy = 0; iy < nY2Xbins; iy++) { for (int iz = 0; iz < nZ2Xbins; iz++) { - auto& data = vRocData[iRow][iy * nZ2Xbins + iz]; + auto& data = vRocData[iRoc * nRows + iRow][iy * nZ2Xbins + iz]; auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; if (vox.mSmoothingStep <= ismooth) { // already filled continue; @@ -673,7 +667,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect double w = 0.; bool filled = false; auto update = [&](int iy1, int iz1) { - auto& data1 = vRocData[iRow][iy1 * nZ2Xbins + iz1]; + auto& data1 = vRocData[iRoc * nRows + iRow][iy1 * nZ2Xbins + iz1]; auto& vox1 = vRowVoxels[iy1 * nZ2Xbins + iz1]; if (vox1.mSmoothingStep >= ismooth) { return false; @@ -746,7 +740,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect for (int iy = 0; iy < nY2Xbins; iy++) { for (int iz = 0; iz < nZ2Xbins; iz++) { - auto& data = vRocData[iRow][iy * nZ2Xbins + iz]; + auto& data = vRocData[iRoc * nRows + iRow][iy * nZ2Xbins + iz]; auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; if (vox.mSmoothingStep > 2) { LOG(fatal) << "empty voxel is not repared"; @@ -812,7 +806,6 @@ std::unique_ptr TPCFastSpaceChargeCorrect for (auto& th : threads) { th.join(); } - } // iRoc LOGP(info, "Reading & reparing of the track residuals tooks: {}s", watch3.RealTime()); From 2ffa48f319f2f1670ee52bccc6a24968cd3de4f0 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Thu, 20 Jun 2024 15:15:41 +0000 Subject: [PATCH 499/701] TPC Splines: add limits for SP correction values per TPC row --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 18 +++- .../TPCFastSpaceChargeCorrection.h | 96 ++++++++++++++----- .../macro/TPCFastTransformInit.C | 15 +-- 3 files changed, 96 insertions(+), 33 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index ce0954120281f..e71340a555227 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -144,6 +144,9 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas float* splineParameters = correction.getSplineData(slice, row); const std::vector& data = mCorrectionMap.getPoints(slice, row); int nDataPoints = data.size(); + auto& info = correction.getSliceRowInfo(slice, row); + info.resetMaxValues(); + info.resetMaxValuesInv(); if (nDataPoints >= 4) { std::vector pointSU(nDataPoints); std::vector pointSV(nDataPoints); @@ -156,6 +159,8 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas pointCorr[3 * i + 0] = dx; pointCorr[3 * i + 1] = du; pointCorr[3 * i + 2] = dv; + info.updateMaxValues(2. * dx, 2. * du, 2. * dv); + info.updateMaxValuesInv(-2. * dx, -2. * du, -2. * dv); } helper.approximateDataPoints(spline, splineParameters, 0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax(), &pointSU[0], &pointSV[0], &pointCorr[0], nDataPoints); @@ -767,9 +772,20 @@ std::unique_ptr TPCFastSpaceChargeCorrect double yStep = (yLast - yFirst) / 2; + double zFirst = z - dz / 2.; + double zLast = z + dz / 2.; + double zStep = (zLast - zFirst) / 2.; + + if (0) { // no smoothing + yFirst = y; + yLast = y; + zFirst = z; + zLast = z; + } + for (double py = yFirst; py <= yLast + yStep / 2.; py += yStep) { - for (double pz = z - dz / 2.; pz <= z + dz / 2. + 1.e-4; pz += dz / 2.) { + for (double pz = zFirst; pz <= zLast + zStep / 2.; pz += zStep) { map.addCorrectionPoint(iRoc, iRow, py, pz, correctionX, correctionY, correctionZ); } diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index b29d65b98458a..3fdc9b32e640c 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -58,15 +58,64 @@ class TPCFastSpaceChargeCorrection : public FlatObject }; struct SliceRowInfo { - float gridU0{0.f}; //< U coordinate of the U-grid start - float scaleUtoGrid{0.f}; //< scale U to U-grid coordinate - float gridV0{0.f}; ///< V coordinate of the V-grid start - float scaleVtoGrid{0.f}; //< scale V to V-grid coordinate - float gridCorrU0{0.f}; ///< U coordinate of the U-grid start for corrected U - float scaleCorrUtoGrid{0.f}; ///< scale corrected U to U-grid coordinate - float gridCorrV0{0.f}; ///< V coordinate of the V-grid start for corrected V - float scaleCorrVtoGrid{0.f}; ///< scale corrected V to V-grid coordinate + float gridU0{0.f}; //< U coordinate of the U-grid start + float scaleUtoGrid{0.f}; //< scale U to U-grid coordinate + float gridV0{0.f}; ///< V coordinate of the V-grid start + float scaleVtoGrid{0.f}; //< scale V to V-grid coordinate + float gridCorrU0{0.f}; ///< U coordinate of the U-grid start for corrected U + float scaleCorrUtoGrid{0.f}; ///< scale corrected U to U-grid coordinate + float gridCorrV0{0.f}; ///< V coordinate of the V-grid start for corrected V + float scaleCorrVtoGrid{0.f}; ///< scale corrected V to V-grid coordinate + float maxCorr[3]{10.f, 10.f, 10.f}; ///< max correction for dX, dU, dV + float minCorr[3]{-10.f, -10.f, -10.f}; ///< min correction for dX, dU, dV + float maxInvCorr[3]{10.f, 10.f, 10.f}; ///< max inverse correction for dX, dU, dV + float minInvCorr[3]{-10.f, -10.f, -10.f}; ///< min inverse correction for dX, dU, dV RowActiveArea activeArea; + + void resetMaxValues() + { + maxCorr[0] = 1.f; + minCorr[0] = -1.f; + maxCorr[1] = 1.f; + minCorr[1] = -1.f; + maxCorr[2] = 1.f; + minCorr[2] = -1.f; + } + + void updateMaxValues(float dx, float du, float dv) + { + maxCorr[0] = GPUCommonMath::Max(maxCorr[0], dx); + minCorr[0] = GPUCommonMath::Min(minCorr[0], dx); + + maxCorr[1] = GPUCommonMath::Max(maxCorr[1], du); + minCorr[1] = GPUCommonMath::Min(minCorr[1], du); + + maxCorr[2] = GPUCommonMath::Max(maxCorr[2], dv); + minCorr[2] = GPUCommonMath::Min(minCorr[2], dv); + } + + void resetMaxValuesInv() + { + maxInvCorr[0] = 1.f; + minInvCorr[0] = -1.f; + maxInvCorr[1] = 1.f; + minInvCorr[1] = -1.f; + maxInvCorr[2] = 1.f; + minInvCorr[2] = -1.f; + } + + void updateMaxValuesInv(float dx, float du, float dv) + { + maxInvCorr[0] = GPUCommonMath::Max(maxInvCorr[0], dx); + minInvCorr[0] = GPUCommonMath::Min(minInvCorr[0], dx); + + maxInvCorr[1] = GPUCommonMath::Max(maxInvCorr[1], du); + minInvCorr[1] = GPUCommonMath::Min(minInvCorr[1], du); + + maxInvCorr[2] = GPUCommonMath::Max(maxInvCorr[2], dv); + minInvCorr[2] = GPUCommonMath::Min(minInvCorr[2], dv); + } + ClassDefNV(SliceRowInfo, 2); }; @@ -397,12 +446,10 @@ GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrection(int32_t slice, int32 convUVtoGrid(slice, row, u, v, gridU, gridV); float dxuv[3]; spline.interpolateU(splineData, gridU, gridV, dxuv); - if (CAMath::Abs(dxuv[0]) > 100 || CAMath::Abs(dxuv[1]) > 100 || CAMath::Abs(dxuv[2]) > 100) { - dxuv[0] = dxuv[1] = dxuv[2] = 0; - } - dx = dxuv[0]; - du = dxuv[1]; - dv = dxuv[2]; + const auto& info = getSliceRowInfo(slice, row); + dx = GPUCommonMath::Max(info.minCorr[0], GPUCommonMath::Min(info.maxCorr[0], dxuv[0])); + du = GPUCommonMath::Max(info.minCorr[1], GPUCommonMath::Min(info.maxCorr[1], dxuv[1])); + dv = GPUCommonMath::Max(info.minCorr[2], GPUCommonMath::Min(info.maxCorr[2], dxuv[2])); return 0; } @@ -414,12 +461,10 @@ GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrectionOld(int32_t slice, in convUVtoGrid(slice, row, u, v, gridU, gridV); float dxuv[3]; spline.interpolateUold(splineData, gridU, gridV, dxuv); - if (CAMath::Abs(dxuv[0]) > 100 || CAMath::Abs(dxuv[1]) > 100 || CAMath::Abs(dxuv[2]) > 100) { - dxuv[0] = dxuv[1] = dxuv[2] = 0; - } - dx = dxuv[0]; - du = dxuv[1]; - dv = dxuv[2]; + const auto& info = getSliceRowInfo(slice, row); + dx = GPUCommonMath::Max(info.minCorr[0], GPUCommonMath::Min(info.maxCorr[0], dxuv[0])); + du = GPUCommonMath::Max(info.minCorr[1], GPUCommonMath::Min(info.maxCorr[1], dxuv[1])); + dv = GPUCommonMath::Max(info.minCorr[2], GPUCommonMath::Min(info.maxCorr[2], dxuv[2])); return 0; } @@ -433,9 +478,8 @@ GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvCorrectedX( const float* splineData = getSplineData(slice, row, 1); float dx = 0; spline.interpolateU(splineData, gridU, gridV, &dx); - if (CAMath::Abs(dx) > 100) { - dx = 0; - } + const auto& info = getSliceRowInfo(slice, row); + dx = GPUCommonMath::Max(info.minInvCorr[0], GPUCommonMath::Min(info.maxInvCorr[0], dx)); x = mGeo.getRowInfo(row).x + dx; } @@ -450,9 +494,9 @@ GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvUV( float duv[2]; spline.interpolateU(splineData, gridU, gridV, duv); - if (CAMath::Abs(duv[0]) > 100 || CAMath::Abs(duv[1]) > 100) { - duv[0] = duv[1] = 0; - } + const auto& info = getSliceRowInfo(slice, row); + duv[0] = GPUCommonMath::Max(info.minInvCorr[1], GPUCommonMath::Min(info.maxInvCorr[1], duv[0])); + duv[1] = GPUCommonMath::Max(info.minInvCorr[2], GPUCommonMath::Min(info.maxInvCorr[2], duv[1])); nomU = corrU - duv[0]; nomV = corrV - duv[1]; } diff --git a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C index bf3e14d552715..6134f33bcc423 100644 --- a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C +++ b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C @@ -21,8 +21,6 @@ /// root -l TPCFastTransformInit.C'("debugVoxRes.root")' /// -#include "Algorithm/RangeTokenizer.h" - #if !defined(__CLING__) || defined(__ROOTCLING__) #include @@ -41,6 +39,8 @@ #include "TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h" #endif +#include "Algorithm/RangeTokenizer.h" + using namespace o2::tpc; using namespace o2::gpu; @@ -99,8 +99,9 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", trackResiduals.setZ2XBinning(z2xBins); trackResiduals.init(); - { - std::cout << "input track residuals: " << std::endl; + { // debug output + + std::cout << " ===== input track residuals ==== " << std::endl; std::cout << "voxel tree y2xBins: " << y2xBins.size() << std::endl; for (auto y2x : y2xBins) { @@ -127,6 +128,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", for (int i = 0; i < nZ2Xbins; i++) { std::cout << "getZ2X(bin) : " << trackResiduals.getZ2X(i) << std::endl; } + std::cout << " ==================================== " << std::endl; } std::cout << "create fast transformation ... " << std::endl; @@ -310,6 +312,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", geo.convUVtoLocal(iRoc, u + cu, v + cv, cy, cz); cy -= y; cz -= z; + double d[3] = {cx - correctionX, cy - correctionY, cz - correctionZ}; if (voxEntries >= 1.) { for (int i = 0; i < 3; i++) { @@ -317,8 +320,8 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", maxDiff[i] = d[i]; maxDiffRoc[i] = iRoc; maxDiffRow[i] = iRow; - std::cout << " roc " << iRoc << " row " << iRow << " xyz " << i - << " diff " << d[i] << " entries " << voxEntries << " y " << y2xBin << " z " << z2xBin << std::endl; + // std::cout << " roc " << iRoc << " row " << iRow << " xyz " << i + // << " diff " << d[i] << " entries " << voxEntries << " y " << y2xBin << " z " << z2xBin << std::endl; } sumDiff[i] += d[i] * d[i]; } From 552c46fb3fc56278ae4396724567828e6cf740f6 Mon Sep 17 00:00:00 2001 From: sgorbunov Date: Fri, 5 Jul 2024 00:09:05 +0200 Subject: [PATCH 500/701] TPC Splines: disable smoothing --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 106 ++++++++++-------- 1 file changed, 61 insertions(+), 45 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index e71340a555227..acaf9c474e275 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -411,10 +411,14 @@ std::unique_ptr TPCFastSpaceChargeCorrect int nY2Xbins = trackResiduals.getNY2XBins(); int nZ2Xbins = trackResiduals.getNZ2XBins(); + double marginY2X = trackResiduals.getY2X(0, 2) - trackResiduals.getY2X(0, 0); + double marginZ2X = trackResiduals.getZ2X(1) - trackResiduals.getZ2X(0); + std::vector yBinsInt; { std::vector yBins; - yBins.reserve(nY2Xbins); + yBins.reserve(nY2Xbins + 2); + yBins.push_back(trackResiduals.getY2X(0, 0) - marginY2X); for (int i = 0, j = nY2Xbins - 1; i <= j; i += 2, j -= 2) { if (i == j) { yBins.push_back(trackResiduals.getY2X(0, i)); @@ -425,6 +429,8 @@ std::unique_ptr TPCFastSpaceChargeCorrect yBins.push_back(trackResiduals.getY2X(0, j)); } } + yBins.push_back(trackResiduals.getY2X(0, nY2Xbins - 1) + marginY2X); + std::sort(yBins.begin(), yBins.end()); double dy = yBins[1] - yBins[0]; for (int i = 1; i < yBins.size(); i++) { @@ -452,10 +458,13 @@ std::unique_ptr TPCFastSpaceChargeCorrect std::vector zBinsInt; { std::vector zBins; - zBins.reserve(nZ2Xbins); + zBins.reserve(nZ2Xbins + 2); + zBins.push_back(-(trackResiduals.getZ2X(0) - marginZ2X)); for (int i = 0; i < nZ2Xbins; i += 2) { zBins.push_back(-trackResiduals.getZ2X(i)); } + zBins.push_back(-(trackResiduals.getZ2X(nZ2Xbins - 1) + 2. * marginZ2X)); + std::sort(zBins.begin(), zBins.end()); double dz = zBins[1] - zBins[0]; for (int i = 1; i < zBins.size(); i++) { @@ -465,7 +474,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect } zBinsInt.reserve(zBins.size()); // spline knots must be positioned on the grid with an integer internal coordinate - // lets copy the knot positions with the accuracy of 0.1*dz + // lets copy the knot positions with the accuracy of 0.01*dz dz = dz / 10.; double z0 = zBins[0]; double z1 = zBins[zBins.size() - 1]; @@ -525,10 +534,10 @@ std::unique_ptr TPCFastSpaceChargeCorrect const auto& rowInfo = geo.getRowInfo(iRow); auto& info = correction.getSliceRowInfo(iRoc, iRow); const auto& spline = correction.getSpline(iRoc, iRow); - double yMin = rowInfo.x * trackResiduals.getY2X(iRow, 0); - double yMax = rowInfo.x * trackResiduals.getY2X(iRow, trackResiduals.getNY2XBins() - 1); - double zMin = rowInfo.x * trackResiduals.getZ2X(0); - double zMax = rowInfo.x * trackResiduals.getZ2X(trackResiduals.getNZ2XBins() - 1); + double yMin = rowInfo.x * (trackResiduals.getY2X(iRow, 0) - marginY2X); + double yMax = rowInfo.x * (trackResiduals.getY2X(iRow, trackResiduals.getNY2XBins() - 1) + marginY2X); + double zMin = rowInfo.x * (trackResiduals.getZ2X(0) - marginZ2X); + double zMax = rowInfo.x * (trackResiduals.getZ2X(trackResiduals.getNZ2XBins() - 1) + 2. * marginZ2X); double uMin = yMin; double uMax = yMax; double vMin = geo.getTPCzLength(iRoc) - zMax; @@ -585,6 +594,12 @@ std::unique_ptr TPCFastSpaceChargeCorrect vox.mCx = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResX] : v->D[o2::tpc::TrackResiduals::ResX]; vox.mCy = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResY] : v->D[o2::tpc::TrackResiduals::ResY]; vox.mCz = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResZ] : v->D[o2::tpc::TrackResiduals::ResZ]; + if (0 && vox.mNentries < 1) { + vox.mCx = 0.; + vox.mCy = 0.; + vox.mCz = 0.; + vox.mNentries = 1; + } } }; processor.Process(myThread); @@ -711,29 +726,27 @@ std::unique_ptr TPCFastSpaceChargeCorrect // feed the row data to the helper - double yMin = 0., yMax = 0.; + double yMin = 0., yMax = 0., zMin = 0.; + + auto& info = correction.getSliceRowInfo(iRoc, iRow); + const auto& spline = correction.getSpline(iRoc, iRow); { - float u, v; - if (iRoc < geo.getNumberOfSlicesA()) { - geo.convScaledUVtoUV(iRoc, iRow, 0., 0., u, v); - } else { - geo.convScaledUVtoUV(iRoc, iRow, 1., 0., u, v); - } - float py, pz; - geo.convUVtoLocal(iRoc, u, v, py, pz); - yMin = py; - } - { - float u, v; + float u0, u1, v0, v1; + correction.convGridToUV(iRoc, iRow, 0., 0., u0, v0); + correction.convGridToUV(iRoc, iRow, + spline.getGridX1().getUmax(), spline.getGridX2().getUmax(), u1, v1); + float y0, y1, z0, z1; + geo.convUVtoLocal(iRoc, u0, v0, y0, z0); + geo.convUVtoLocal(iRoc, u1, v1, y1, z1); if (iRoc < geo.getNumberOfSlicesA()) { - geo.convScaledUVtoUV(iRoc, iRow, 1., 0., u, v); + yMin = y0; + yMax = y1; } else { - geo.convScaledUVtoUV(iRoc, iRow, 0., 0., u, v); + yMin = y1; + yMax = y0; } - float py, pz; - geo.convUVtoLocal(iRoc, u, v, py, pz); - yMax = py; + zMin = z1; } double zEdge = 0.; @@ -759,28 +772,22 @@ std::unique_ptr TPCFastSpaceChargeCorrect double correctionY = data.mCy; double correctionZ = data.mCz; - double yFirst = y - dy / 2.; - double yLast = y + dy / 2.; + double yStep = dy / 2.; + double zStep = dz / 2.; + + double yFirst = y; + double yLast = y; + double zFirst = z; + double zLast = z; if (iy == 0) { // extend value of the first Y bin to the row edge yFirst = yMin; + yStep = (yLast - yFirst) / 2.; } if (iy == nY2Xbins - 1) { // extend value of the last Y bin to the row edge yLast = yMax; - } - - double yStep = (yLast - yFirst) / 2; - - double zFirst = z - dz / 2.; - double zLast = z + dz / 2.; - double zStep = (zLast - zFirst) / 2.; - - if (0) { // no smoothing - yFirst = y; - yLast = y; - zFirst = z; - zLast = z; + yStep = (yLast - yFirst) / 2.; } for (double py = yFirst; py <= yLast + yStep / 2.; py += yStep) { @@ -790,9 +797,19 @@ std::unique_ptr TPCFastSpaceChargeCorrect correctionZ); } + if (iz == 0) { // extend value of the first Z bin to Z=0. + int nZsteps = 2; + for (int is = 0; is < nZsteps; is++) { + double pz = z + (zMin - z) * (is + 1.) / nZsteps; + double s = 1.; //(nZsteps - 1. - is) / nZsteps; + map.addCorrectionPoint(iRoc, iRow, py, pz, s * correctionX, + s * correctionY, s * correctionZ); + } + } + if (iz == nZ2Xbins - 1) { - // extend value of the first Z bin to the readout, linear decrease of all values to 0. - int nZsteps = 3; + // extend value of the last Z bin to the readout, linear decrease of all values to 0. + int nZsteps = 2; for (int is = 0; is < nZsteps; is++) { double pz = z + (zEdge - z) * (is + 1.) / nZsteps; double s = (nZsteps - 1. - is) / nZsteps; @@ -803,9 +820,8 @@ std::unique_ptr TPCFastSpaceChargeCorrect } } // iz } // iy - - } // iRow - }; // myThread + } // iRow + }; // myThread // run n threads From bc7e81e7766c4390137eb847ba5ab3e2d5c92f75 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Thu, 18 Jul 2024 20:27:35 +0200 Subject: [PATCH 501/701] TPC Splines: smooth to linear edges, crop at grid borders, use mean position of residuals --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 302 ++++++++---------- GPU/TPCFastTransformation/Spline1DSpec.h | 20 +- .../TPCFastSpaceChargeCorrection.cxx | 28 +- .../TPCFastSpaceChargeCorrection.h | 31 +- .../macro/TPCFastTransformInit.C | 44 +-- 5 files changed, 207 insertions(+), 218 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index acaf9c474e275..82a23dfa5242a 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -159,8 +159,8 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas pointCorr[3 * i + 0] = dx; pointCorr[3 * i + 1] = du; pointCorr[3 * i + 2] = dv; - info.updateMaxValues(2. * dx, 2. * du, 2. * dv); - info.updateMaxValuesInv(-2. * dx, -2. * du, -2. * dv); + info.updateMaxValues(20. * dx, 20. * du, 20. * dv); + info.updateMaxValuesInv(-20. * dx, -20. * du, -20. * dv); } helper.approximateDataPoints(spline, splineParameters, 0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax(), &pointSU[0], &pointSV[0], &pointCorr[0], nDataPoints); @@ -411,95 +411,69 @@ std::unique_ptr TPCFastSpaceChargeCorrect int nY2Xbins = trackResiduals.getNY2XBins(); int nZ2Xbins = trackResiduals.getNZ2XBins(); - double marginY2X = trackResiduals.getY2X(0, 2) - trackResiduals.getY2X(0, 0); - double marginZ2X = trackResiduals.getZ2X(1) - trackResiduals.getZ2X(0); + std::vector uvBinsDouble[2]; - std::vector yBinsInt; - { - std::vector yBins; - yBins.reserve(nY2Xbins + 2); - yBins.push_back(trackResiduals.getY2X(0, 0) - marginY2X); - for (int i = 0, j = nY2Xbins - 1; i <= j; i += 2, j -= 2) { - if (i == j) { - yBins.push_back(trackResiduals.getY2X(0, i)); - } else if (i + 1 == j) { - yBins.push_back(trackResiduals.getY2X(0, i)); - } else { - yBins.push_back(trackResiduals.getY2X(0, i)); - yBins.push_back(trackResiduals.getY2X(0, j)); - } + uvBinsDouble[0].reserve(nY2Xbins); + uvBinsDouble[1].reserve(nZ2Xbins); + + for (int i = 0, j = nY2Xbins - 1; i <= j; i += 2, j -= 2) { + uvBinsDouble[0].push_back(trackResiduals.getY2X(0, i)); + if (j >= i + 1) { + uvBinsDouble[0].push_back(trackResiduals.getY2X(0, j)); } - yBins.push_back(trackResiduals.getY2X(0, nY2Xbins - 1) + marginY2X); + } - std::sort(yBins.begin(), yBins.end()); - double dy = yBins[1] - yBins[0]; - for (int i = 1; i < yBins.size(); i++) { - if (yBins[i] - yBins[i - 1] < dy) { - dy = yBins[i] - yBins[i - 1]; + for (int i = 0, j = nZ2Xbins - 1; i <= j; i += 2, j -= 2) { + uvBinsDouble[1].push_back(-trackResiduals.getZ2X(i)); + if (j >= i + 1) { + uvBinsDouble[1].push_back(-trackResiduals.getZ2X(j)); + } + } + + std::vector uvBinsInt[2]; + + for (int iuv = 0; iuv < 2; iuv++) { + auto& bins = uvBinsDouble[iuv]; + std::sort(bins.begin(), bins.end()); + + auto& binsInt = uvBinsInt[iuv]; + binsInt.reserve(bins.size()); + + double dy = bins[1] - bins[0]; + for (int i = 2; i < bins.size(); i++) { + double dd = bins[i] - bins[i - 1]; + if (dd < dy) { + dy = dd; } } - yBinsInt.reserve(yBins.size()); // spline knots must be positioned on the grid with integer internal coordinate // take the knot position accuracy of 0.1*dy dy = dy / 10.; - double y0 = yBins[0]; - double y1 = yBins[yBins.size() - 1]; - for (auto& y : yBins) { + double y0 = bins[0]; + double y1 = bins[bins.size() - 1]; + for (auto& y : bins) { y -= y0; int iy = int(y / dy + 0.5); - yBinsInt.push_back(iy); + binsInt.push_back(iy); double yold = y / (y1 - y0) * 2 - 1.; y = iy * dy; y = y / (y1 - y0) * 2 - 1.; - LOG(info) << "convert y bin: " << yold << " -> " << y << " -> " << iy; - } - } - - std::vector zBinsInt; - { - std::vector zBins; - zBins.reserve(nZ2Xbins + 2); - zBins.push_back(-(trackResiduals.getZ2X(0) - marginZ2X)); - for (int i = 0; i < nZ2Xbins; i += 2) { - zBins.push_back(-trackResiduals.getZ2X(i)); - } - zBins.push_back(-(trackResiduals.getZ2X(nZ2Xbins - 1) + 2. * marginZ2X)); - - std::sort(zBins.begin(), zBins.end()); - double dz = zBins[1] - zBins[0]; - for (int i = 1; i < zBins.size(); i++) { - if (zBins[i] - zBins[i - 1] < dz) { - dz = zBins[i] - zBins[i - 1]; + if (iuv == 0) { + LOG(info) << "convert y bin: " << yold << " -> " << y << " -> " << iy; + } else { + LOG(info) << "convert z bin: " << yold << " -> " << y << " -> " << iy; } } - zBinsInt.reserve(zBins.size()); - // spline knots must be positioned on the grid with an integer internal coordinate - // lets copy the knot positions with the accuracy of 0.01*dz - dz = dz / 10.; - double z0 = zBins[0]; - double z1 = zBins[zBins.size() - 1]; - for (auto& z : zBins) { - z -= z0; - int iz = int(z / dz + 0.5); - zBinsInt.push_back(iz); - double zold = z / (z1 - z0); - z = iz * dz; - z = z / (z1 - z0); - LOG(info) << "convert z bin: " << zold << " -> " << z << " -> " << iz; - } - } - if (yBinsInt.size() < 2) { - yBinsInt.clear(); - yBinsInt.push_back(0); - yBinsInt.push_back(1); + if (binsInt.size() < 2) { + binsInt.clear(); + binsInt.push_back(0); + binsInt.push_back(1); + } } - if (zBinsInt.size() < 2) { - zBinsInt.clear(); - zBinsInt.push_back(0); - zBinsInt.push_back(1); - } + auto& yBinsInt = uvBinsInt[0]; + auto& zBinsInt = uvBinsInt[1]; int nKnotsY = yBinsInt.size(); int nKnotsZ = zBinsInt.size(); @@ -534,10 +508,10 @@ std::unique_ptr TPCFastSpaceChargeCorrect const auto& rowInfo = geo.getRowInfo(iRow); auto& info = correction.getSliceRowInfo(iRoc, iRow); const auto& spline = correction.getSpline(iRoc, iRow); - double yMin = rowInfo.x * (trackResiduals.getY2X(iRow, 0) - marginY2X); - double yMax = rowInfo.x * (trackResiduals.getY2X(iRow, trackResiduals.getNY2XBins() - 1) + marginY2X); - double zMin = rowInfo.x * (trackResiduals.getZ2X(0) - marginZ2X); - double zMax = rowInfo.x * (trackResiduals.getZ2X(trackResiduals.getNZ2XBins() - 1) + 2. * marginZ2X); + double yMin = rowInfo.x * trackResiduals.getY2X(iRow, 0); + double yMax = rowInfo.x * trackResiduals.getY2X(iRow, trackResiduals.getNY2XBins() - 1); + double zMin = rowInfo.x * trackResiduals.getZ2X(0); + double zMax = rowInfo.x * trackResiduals.getZ2X(trackResiduals.getNZ2XBins() - 1); double uMin = yMin; double uMax = yMax; double vMin = geo.getTPCzLength(iRoc) - zMax; @@ -563,6 +537,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect struct VoxelData { int mNentries{0}; // number of entries + float mX, mY, mZ; // mean position in the local coordinates float mCx, mCy, mCz; // corrections to the local coordinates }; @@ -589,16 +564,19 @@ std::unique_ptr TPCFastSpaceChargeCorrect } int iy = v->bvox[o2::tpc::TrackResiduals::VoxF]; // bin number in y/x 0..14 int iz = v->bvox[o2::tpc::TrackResiduals::VoxZ]; // bin number in z/x 0..4 - auto& vox = vRocData[iRoc * nRows + iRow][iy * nZ2Xbins + iz]; - vox.mNentries = (int)v->stat[o2::tpc::TrackResiduals::VoxV]; - vox.mCx = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResX] : v->D[o2::tpc::TrackResiduals::ResX]; - vox.mCy = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResY] : v->D[o2::tpc::TrackResiduals::ResY]; - vox.mCz = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResZ] : v->D[o2::tpc::TrackResiduals::ResZ]; - if (0 && vox.mNentries < 1) { - vox.mCx = 0.; - vox.mCy = 0.; - vox.mCz = 0.; - vox.mNentries = 1; + auto& data = vRocData[iRoc * nRows + iRow][iy * nZ2Xbins + iz]; + data.mNentries = (int)v->stat[o2::tpc::TrackResiduals::VoxV]; + data.mX = v->stat[o2::tpc::TrackResiduals::VoxX]; + data.mY = v->stat[o2::tpc::TrackResiduals::VoxF]; + data.mZ = v->stat[o2::tpc::TrackResiduals::VoxZ]; + data.mCx = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResX] : v->D[o2::tpc::TrackResiduals::ResX]; + data.mCy = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResY] : v->D[o2::tpc::TrackResiduals::ResY]; + data.mCz = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResZ] : v->D[o2::tpc::TrackResiduals::ResZ]; + if (0 && data.mNentries < 1) { + data.mCx = 0.; + data.mCy = 0.; + data.mCz = 0.; + data.mNentries = 1; } } }; @@ -642,10 +620,27 @@ std::unique_ptr TPCFastSpaceChargeCorrect if (iRoc >= geo.getNumberOfSlicesA()) { vox.mZ = -vox.mZ; } + data.mY *= x; + data.mZ *= x; + /* + if ( fabs(x - data.mX) > 0.01 || fabs(vox.mY - data.mY) > 5. || fabs(vox.mZ - data.mZ) > 5.) { + std::cout + << " roc " << iRoc << " row " << iRow + << " voxel x " << x << " y " << vox.mY << " z " << vox.mZ + << " data x " << data.mX << " y " << data.mY << " z " << data.mZ + << std::endl; + } + */ + if (1) { // always use voxel center instead of the mean position + data.mY = vox.mY; + data.mZ = vox.mZ; + } if (data.mNentries < 1) { // no data data.mCx = 0.; data.mCy = 0.; data.mCz = 0.; + data.mY = vox.mY; + data.mZ = vox.mZ; vox.mSmoothingStep = 100; } else { // voxel contains data if (invertSigns) { @@ -726,102 +721,59 @@ std::unique_ptr TPCFastSpaceChargeCorrect // feed the row data to the helper - double yMin = 0., yMax = 0., zMin = 0.; - auto& info = correction.getSliceRowInfo(iRoc, iRow); const auto& spline = correction.getSpline(iRoc, iRow); - { - float u0, u1, v0, v1; - correction.convGridToUV(iRoc, iRow, 0., 0., u0, v0); - correction.convGridToUV(iRoc, iRow, - spline.getGridX1().getUmax(), spline.getGridX2().getUmax(), u1, v1); - float y0, y1, z0, z1; - geo.convUVtoLocal(iRoc, u0, v0, y0, z0); - geo.convUVtoLocal(iRoc, u1, v1, y1, z1); - if (iRoc < geo.getNumberOfSlicesA()) { - yMin = y0; - yMax = y1; - } else { - yMin = y1; - yMax = y0; + auto addEdge = [&](int iy1, int iz1, int iy2, int iz2, int nSteps) { + auto& data1 = vRocData[iRoc * nRows + iRow][iy1 * nZ2Xbins + iz1]; + auto& vox1 = vRowVoxels[iy1 * nZ2Xbins + iz1]; + auto& data2 = vRocData[iRoc * nRows + iRow][iy2 * nZ2Xbins + iz2]; + auto& vox2 = vRowVoxels[iy2 * nZ2Xbins + iz2]; + if (vox1.mSmoothingStep > 2) { + LOG(fatal) << "empty voxel is not repared: y " << iy1 << " z " << iz1; } - zMin = z1; - } - - double zEdge = 0.; - if (iRoc < geo.getNumberOfSlicesA()) { - zEdge = geo.getTPCzLengthA(); - } else { - zEdge = -geo.getTPCzLengthC(); - } + if (vox2.mSmoothingStep > 2) { + LOG(fatal) << "empty voxel is not repared: y " << iy2 << " z " << iz2; + } + double y1 = vox1.mY; + double z1 = vox1.mZ; + double cx1 = data1.mCx; + double cy1 = data1.mCy; + double cz1 = data1.mCz; + double y2 = vox2.mY; + double z2 = vox2.mZ; + double cx2 = data2.mCx; + double cy2 = data2.mCy; + double cz2 = data2.mCz; + + for (int is = 0; is < nSteps; is++) { + double s2 = is / (double)nSteps; + double s1 = 1. - s2; + double y = s1 * y1 + s2 * y2; + double z = s1 * z1 + s2 * z2; + double cx = s1 * cx1 + s2 * cx2; + double cy = s1 * cy1 + s2 * cy2; + double cz = s1 * cz1 + s2 * cz2; + map.addCorrectionPoint(iRoc, iRow, y, z, cx, cy, cz); + } + }; for (int iy = 0; iy < nY2Xbins; iy++) { - for (int iz = 0; iz < nZ2Xbins; iz++) { - auto& data = vRocData[iRoc * nRows + iRow][iy * nZ2Xbins + iz]; - auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; - if (vox.mSmoothingStep > 2) { - LOG(fatal) << "empty voxel is not repared"; - } - - double y = vox.mY; - double z = vox.mZ; - double dy = vox.mDy; - double dz = vox.mDz; - double correctionX = data.mCx; - double correctionY = data.mCy; - double correctionZ = data.mCz; - - double yStep = dy / 2.; - double zStep = dz / 2.; - - double yFirst = y; - double yLast = y; - double zFirst = z; - double zLast = z; - - if (iy == 0) { // extend value of the first Y bin to the row edge - yFirst = yMin; - yStep = (yLast - yFirst) / 2.; - } - - if (iy == nY2Xbins - 1) { // extend value of the last Y bin to the row edge - yLast = yMax; - yStep = (yLast - yFirst) / 2.; - } - - for (double py = yFirst; py <= yLast + yStep / 2.; py += yStep) { - - for (double pz = zFirst; pz <= zLast + zStep / 2.; pz += zStep) { - map.addCorrectionPoint(iRoc, iRow, py, pz, correctionX, correctionY, - correctionZ); - } + for (int iz = 0; iz < nZ2Xbins - 1; iz++) { + addEdge(iy, iz, iy, iz + 1, 3); + } + addEdge(iy, nZ2Xbins - 1, iy, nZ2Xbins - 1, 1); + } - if (iz == 0) { // extend value of the first Z bin to Z=0. - int nZsteps = 2; - for (int is = 0; is < nZsteps; is++) { - double pz = z + (zMin - z) * (is + 1.) / nZsteps; - double s = 1.; //(nZsteps - 1. - is) / nZsteps; - map.addCorrectionPoint(iRoc, iRow, py, pz, s * correctionX, - s * correctionY, s * correctionZ); - } - } + for (int iz = 0; iz < nZ2Xbins; iz++) { + for (int iy = 0; iy < nY2Xbins - 1; iy++) { + addEdge(iy, iz, iy + 1, iz, 3); + } + addEdge(nY2Xbins - 1, iz, nY2Xbins - 1, iz, 1); + } // iy - if (iz == nZ2Xbins - 1) { - // extend value of the last Z bin to the readout, linear decrease of all values to 0. - int nZsteps = 2; - for (int is = 0; is < nZsteps; is++) { - double pz = z + (zEdge - z) * (is + 1.) / nZsteps; - double s = (nZsteps - 1. - is) / nZsteps; - map.addCorrectionPoint(iRoc, iRow, py, pz, s * correctionX, - s * correctionY, s * correctionZ); - } - } - } - } // iz - } // iy - } // iRow - }; // myThread + } // iRow + }; // myThread // run n threads diff --git a/GPU/TPCFastTransformation/Spline1DSpec.h b/GPU/TPCFastTransformation/Spline1DSpec.h index 6462f291d1136..dc59e77e308a1 100644 --- a/GPU/TPCFastTransformation/Spline1DSpec.h +++ b/GPU/TPCFastTransformation/Spline1DSpec.h @@ -313,6 +313,14 @@ class Spline1DSpec : public Spline1DContainer { const auto nYdimTmp = SplineUtil::getNdim(inpYdim); const auto nYdim = nYdimTmp.get(); + + if (u < (DataT)0) { + u = (DataT)0; + } + if (u > (DataT)TBase::getUmax()) { + u = (DataT)TBase::getUmax(); + } + T uu = T(u - knotL.u); T li = T(knotL.Li); T v = uu * li; // scaled u @@ -337,11 +345,19 @@ class Spline1DSpec : public Spline1DContainer } template - GPUd() static void getUderivatives(const Knot& knotL, DataT u, - T& dSl, T& dDl, T& dSr, T& dDr) + GPUd() void getUderivatives(const Knot& knotL, DataT u, + T& dSl, T& dDl, T& dSr, T& dDr) const { /// Get derivatives of the interpolated value {S(u): 1D -> nYdim} at the segment [knotL, next knotR] /// over the spline values Sl, Sr and the slopes Dl, Dr + + if (u < (DataT)0) { + u = (DataT)0; + } + if (u > (DataT)TBase::getUmax()) { + u = (DataT)TBase::getUmax(); + } + u = u - knotL.u; T v = u * T(knotL.Li); // scaled u T vm1 = v - 1.; diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx index 35c6e43daa43b..eb69983cf87ce 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx @@ -116,11 +116,11 @@ void TPCFastSpaceChargeCorrection::cloneFromObject(const TPCFastSpaceChargeCorre mClassVersion = obj.mClassVersion; - for (int i = 0; i < TPCFastTransformGeo::getMaxNumberOfRows(); i++) { + for (int32_t i = 0; i < TPCFastTransformGeo::getMaxNumberOfRows(); i++) { mRowInfos[i] = obj.mRowInfos[i]; } - for (int i = 0; i < TPCFastTransformGeo::getNumberOfSlices() * TPCFastTransformGeo::getMaxNumberOfRows(); i++) { + for (int32_t i = 0; i < TPCFastTransformGeo::getNumberOfSlices() * TPCFastTransformGeo::getMaxNumberOfRows(); i++) { mSliceRowInfos[i] = obj.mSliceRowInfos[i]; } @@ -141,7 +141,7 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer /// Sets the actual location of the external flat buffer after it has been moved (e.g. to another maschine) struct RowInfoVersion3 { - int splineScenarioID{0}; ///< scenario index (which of Spline2D splines to use) + int32_t splineScenarioID{0}; ///< scenario index (which of Spline2D splines to use) size_t dataOffsetBytes[3]{0}; ///< offset for the spline data withing a TPC slice }; @@ -199,24 +199,24 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer if (mClassVersion == 3) { // copy old-format slicerow data from the buffer to the arrays auto* rowInfosOld = reinterpret_cast(mFlatBufferPtr + rowsOffset); - for (int i = 0; i < mGeo.getNumberOfRows(); i++) { + for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { RowInfoVersion3& infoOld = rowInfosOld[i]; RowInfo& info = mRowInfos[i]; info.splineScenarioID = infoOld.splineScenarioID; - for (int is = 0; is < 3; is++) { + for (int32_t is = 0; is < 3; is++) { info.dataOffsetBytes[is] = infoOld.dataOffsetBytes[is]; } } - for (int is = 0; is < mNumberOfScenarios; is++) { + for (int32_t is = 0; is < mNumberOfScenarios; is++) { auto& spline = mScenarioPtr[is]; spline.setXrange(0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax()); } auto* sliceRowInfosOld = reinterpret_cast(mFlatBufferPtr + sliceRowsOffset); - for (int slice = 0; slice < mGeo.getNumberOfSlices(); slice++) { - for (int row = 0; row < mGeo.getNumberOfRows(); row++) { + for (int32_t slice = 0; slice < mGeo.getNumberOfSlices(); slice++) { + for (int32_t row = 0; row < mGeo.getNumberOfRows(); row++) { SliceRowInfoVersion3& infoOld = sliceRowInfosOld[mGeo.getNumberOfRows() * slice + row]; SliceRowInfo& info = getSliceRowInfo(slice, row); const auto& spline = getSpline(slice, row); @@ -236,7 +236,7 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer info.activeArea.cuMin = infoOld.activeArea.cuMin; info.activeArea.cuMax = infoOld.activeArea.cuMax; info.activeArea.cvMax = infoOld.activeArea.cvMax; - for (int i = 0; i < 5; i++) { + for (int32_t i = 0; i < 5; i++) { info.activeArea.maxDriftLengthCheb[i] = infoOld.activeArea.maxDriftLengthCheb[i]; } } @@ -256,7 +256,7 @@ void TPCFastSpaceChargeCorrection::setFutureBufferAddress(char* futureFlatBuffer char* oldBuffer = mFlatBufferPtr; char* newBuffer = futureFlatBufferPtr; - for (int i = 0; i < mNumberOfScenarios; i++) { + for (int32_t i = 0; i < mNumberOfScenarios; i++) { SplineType& sp = mScenarioPtr[i]; char* newSplineBuf = relocatePointer(oldBuffer, newBuffer, sp.getFlatBufferPtr()); sp.setFutureBufferAddress(newSplineBuf); @@ -278,7 +278,7 @@ void TPCFastSpaceChargeCorrection::print() const LOG(info) << " mSliceDataSizeBytes = " << mSliceDataSizeBytes[0] << " " << mSliceDataSizeBytes[1] << " " << mSliceDataSizeBytes[2]; { LOG(info) << " TPC rows: "; - for (int i = 0; i < mGeo.getNumberOfRows(); i++) { + for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { const RowInfo& r = mRowInfos[i]; LOG(info) << " tpc row " << i << ": splineScenarioID = " << r.splineScenarioID << " dataOffsetBytes = " << r.dataOffsetBytes; } @@ -331,7 +331,7 @@ void TPCFastSpaceChargeCorrection::startConstruction(const TPCFastTransformGeo& assert(mConstructionScenarios != nullptr); - for (int i = 0; i < mGeo.getNumberOfRows(); i++) { + for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { mRowInfos[i].splineScenarioID = -1; } @@ -378,7 +378,7 @@ void TPCFastSpaceChargeCorrection::finishConstruction() assert(mConstructionMask & ConstructionState::InProgress); - for (int i = 0; i < mGeo.getNumberOfRows(); i++) { + for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { assert(mRowInfos[i].splineScenarioID >= 0); } for (int32_t i = 0; i < mNumberOfScenarios; i++) { @@ -404,7 +404,7 @@ void TPCFastSpaceChargeCorrection::finishConstruction() for (int32_t is = 0; is < 3; is++) { sliceDataOffset[is] = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); mSliceDataSizeBytes[is] = 0; - for (int i = 0; i < mGeo.getNumberOfRows(); i++) { + for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { RowInfo& row = mRowInfos[i]; SplineType& spline = mConstructionScenarios[row.splineScenarioID]; row.dataOffsetBytes[is] = alignSize(mSliceDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index 3fdc9b32e640c..e69983fab9175 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -251,7 +251,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject GPUd() float getInterpolationSafetyMargin() const { return fInterpolationSafetyMargin; } /// Gives TPC row info - GPUd() const RowInfo& getRowInfo(int row) const { return mRowInfos[row]; } + GPUd() const RowInfo& getRowInfo(int32_t row) const { return mRowInfos[row]; } /// Gives TPC slice info GPUd() const SliceInfo& getSliceInfo(int32_t slice) const @@ -321,7 +321,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// Class version. It is used to read older versions from disc. /// The default version 3 is the one before this field was introduced. /// The actual version must be set in startConstruction(). - int mClassVersion{3}; + int32_t mClassVersion{3}; RowInfo mRowInfos[TPCFastTransformGeo::getMaxNumberOfRows()]; ///< RowInfo array @@ -447,9 +447,17 @@ GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrection(int32_t slice, int32 float dxuv[3]; spline.interpolateU(splineData, gridU, gridV, dxuv); const auto& info = getSliceRowInfo(slice, row); - dx = GPUCommonMath::Max(info.minCorr[0], GPUCommonMath::Min(info.maxCorr[0], dxuv[0])); - du = GPUCommonMath::Max(info.minCorr[1], GPUCommonMath::Min(info.maxCorr[1], dxuv[1])); - dv = GPUCommonMath::Max(info.minCorr[2], GPUCommonMath::Min(info.maxCorr[2], dxuv[2])); + float s = v / info.gridV0; + if (s < 0.) { + s = 0.; + } + if (s > 1.) { + s = 1.; + } + + dx = GPUCommonMath::Max(info.minCorr[0], GPUCommonMath::Min(info.maxCorr[0], s * dxuv[0])); + du = GPUCommonMath::Max(info.minCorr[1], GPUCommonMath::Min(info.maxCorr[1], s * dxuv[1])); + dv = GPUCommonMath::Max(info.minCorr[2], GPUCommonMath::Min(info.maxCorr[2], s * dxuv[2])); return 0; } @@ -462,9 +470,16 @@ GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrectionOld(int32_t slice, in float dxuv[3]; spline.interpolateUold(splineData, gridU, gridV, dxuv); const auto& info = getSliceRowInfo(slice, row); - dx = GPUCommonMath::Max(info.minCorr[0], GPUCommonMath::Min(info.maxCorr[0], dxuv[0])); - du = GPUCommonMath::Max(info.minCorr[1], GPUCommonMath::Min(info.maxCorr[1], dxuv[1])); - dv = GPUCommonMath::Max(info.minCorr[2], GPUCommonMath::Min(info.maxCorr[2], dxuv[2])); + float s = v / info.gridV0; + if (s < 0.) { + s = 0.; + } + if (s > 1.) { + s = 1.; + } + dx = GPUCommonMath::Max(info.minCorr[0], GPUCommonMath::Min(info.maxCorr[0], s * dxuv[0])); + du = GPUCommonMath::Max(info.minCorr[1], GPUCommonMath::Min(info.maxCorr[1], s * dxuv[1])); + dv = GPUCommonMath::Max(info.minCorr[2], GPUCommonMath::Min(info.maxCorr[2], s * dxuv[2])); return 0; } diff --git a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C index 6134f33bcc423..c4b0680f2edd4 100644 --- a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C +++ b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C @@ -109,10 +109,10 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", } std::cout << std::endl; - int nY2Xbins = trackResiduals.getNY2XBins(); + int32_t nY2Xbins = trackResiduals.getNY2XBins(); std::cout << " TrackResiduals y2x bins: " << nY2Xbins << std::endl; - for (int i = 0; i < nY2Xbins; i++) { + for (int32_t i = 0; i < nY2Xbins; i++) { std::cout << "scaled getY2X(bin) : " << trackResiduals.getY2X(0, i) / trackResiduals.getMaxY2X(0) << std::endl; } @@ -123,9 +123,9 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", } std::cout << std::endl; - int nZ2Xbins = trackResiduals.getNZ2XBins(); + int32_t nZ2Xbins = trackResiduals.getNZ2XBins(); std::cout << " TrackResiduals z2x bins: " << nZ2Xbins << std::endl; - for (int i = 0; i < nZ2Xbins; i++) { + for (int32_t i = 0; i < nZ2Xbins; i++) { std::cout << "getZ2X(bin) : " << trackResiduals.getZ2X(i) << std::endl; } std::cout << " ==================================== " << std::endl; @@ -138,6 +138,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", o2::tpc::TPCFastSpaceChargeCorrectionHelper* corrHelper = o2::tpc::TPCFastSpaceChargeCorrectionHelper::instance(); corrHelper->setNthreadsToMaximum(); + // corrHelper->setNthreads(1); auto corrPtr = corrHelper->createFromTrackResiduals(trackResiduals, voxResTree, useSmoothed, invertSigns); @@ -167,9 +168,9 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const o2::gpu::TPCFastTransformGeo& geo = helper->getGeometry(); - // for (int iRoc = 0; iRoc < geo.getNumberOfSlices(); iRoc++) { - for (int iRoc = 0; iRoc < 1; iRoc++) { - for (int iRow = 0; iRow < geo.getNumberOfRows(); iRow++) { + // for (int32_t iRoc = 0; iRoc < geo.getNumberOfSlices(); iRoc++) { + for (int32_t iRoc = 0; iRoc < 1; iRoc++) { + for (int32_t iRow = 0; iRow < geo.getNumberOfRows(); iRow++) { auto& info = corr.getSliceRowInfo(iRoc, iRow); std::cout << "roc " << iRoc << " row " << iRow << " gridV0 " << info.gridV0 << " gridCorrU0 " << info.gridCorrU0 << " gridCorrV0 " << info.gridCorrV0 @@ -257,8 +258,8 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", branch->SetAddress(&v); branch->SetAutoDelete(kTRUE); - int iRocLast = -1; - int iRowLast = -1; + int32_t iRocLast = -1; + int32_t iRowLast = -1; for (int32_t iVox = 0; iVox < voxResTree->GetEntriesFast(); iVox++) { @@ -306,6 +307,11 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", correctionZ *= -1.; } + if (voxEntries > 0.) { // use mean statistical positions instead of the bin centers: + y = x * v->stat[o2::tpc::TrackResiduals::VoxF]; + z = x * v->stat[o2::tpc::TrackResiduals::VoxZ]; + } + float u, v, cx, cu, cv, cy, cz; geo.convLocalToUV(iRoc, y, z, u, v); corr.getCorrection(iRoc, iRow, u, v, cx, cu, cv); @@ -315,7 +321,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", double d[3] = {cx - correctionX, cy - correctionY, cz - correctionZ}; if (voxEntries >= 1.) { - for (int i = 0; i < 3; i++) { + for (int32_t i = 0; i < 3; i++) { if (fabs(maxDiff[i]) < fabs(d[i])) { maxDiff[i] = d[i]; maxDiffRoc[i] = iRoc; @@ -358,7 +364,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", std::vector p[2], g[2]; p[0].push_back(geo.getRowInfo(iRow).getUmin()); - for (int iu = 0; iu < gridU.getNumberOfKnots(); iu++) { + for (int32_t iu = 0; iu < gridU.getNumberOfKnots(); iu++) { float u, v; corr.convGridToUV(iRoc, iRow, gridU.getKnot(iu).getU(), 0., u, v); g[0].push_back(u); @@ -367,7 +373,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", p[0].push_back(geo.getRowInfo(iRow).getUmax()); p[1].push_back(0.); - for (int iv = 0; iv < gridV.getNumberOfKnots(); iv++) { + for (int32_t iv = 0; iv < gridV.getNumberOfKnots(); iv++) { float u, v; corr.convGridToUV(iRoc, iRow, 0., gridV.getKnot(iv).getU(), u, v); g[1].push_back(v); @@ -375,22 +381,22 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", } p[1].push_back(geo.getTPCzLength(iRoc)); - for (int iuv = 0; iuv < 2; iuv++) { - int n = p[iuv].size(); - for (unsigned int i = 0; i < n - 1; i++) { + for (int32_t iuv = 0; iuv < 2; iuv++) { + int32_t n = p[iuv].size(); + for (int32_t i = 0; i < n - 1; i++) { double d = (p[iuv][i + 1] - p[iuv][i]) / 10.; - for (int ii = 1; ii < 10; ii++) { + for (int32_t ii = 1; ii < 10; ii++) { p[iuv].push_back(p[iuv][i] + d * ii); } } std::sort(p[iuv].begin(), p[iuv].end()); } - for (int iter = 0; iter < 2; iter++) { + for (int32_t iter = 0; iter < 2; iter++) { std::vector& pu = ((iter == 0) ? g[0] : p[0]); std::vector& pv = ((iter == 0) ? g[1] : p[1]); - for (unsigned int iu = 0; iu < pu.size(); iu++) { - for (unsigned int iv = 0; iv < pv.size(); iv++) { + for (uint32_t iu = 0; iu < pu.size(); iu++) { + for (uint32_t iv = 0; iv < pv.size(); iv++) { float u = pu[iu]; float v = pv[iv]; float x, y, z; From d15629fbfa3a511c9553cf08d03963a7c6b16a0f Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Thu, 16 Jan 2025 21:06:18 +0000 Subject: [PATCH 502/701] TPC Splines: fix the inverse correction --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 100 ++++++++++-------- .../TPCFastSpaceChargeCorrection.cxx | 70 +++++++----- .../TPCFastSpaceChargeCorrection.h | 45 ++++---- .../macro/TPCFastTransformInit.C | 88 +++++++++------ 4 files changed, 179 insertions(+), 124 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index 82a23dfa5242a..3696df5343ad3 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -146,7 +146,6 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas int nDataPoints = data.size(); auto& info = correction.getSliceRowInfo(slice, row); info.resetMaxValues(); - info.resetMaxValuesInv(); if (nDataPoints >= 4) { std::vector pointSU(nDataPoints); std::vector pointSV(nDataPoints); @@ -160,7 +159,6 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas pointCorr[3 * i + 1] = du; pointCorr[3 * i + 2] = dv; info.updateMaxValues(20. * dx, 20. * du, 20. * dv); - info.updateMaxValuesInv(-20. * dx, -20. * du, -20. * dv); } helper.approximateDataPoints(spline, splineParameters, 0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax(), &pointSU[0], &pointSV[0], &pointCorr[0], nDataPoints); @@ -908,46 +906,60 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector helper; std::vector splineParameters; - ChebyshevFit1D chebFitterX, chebFitterU, chebFitterV; for (int row = iThread; row < mGeo.getNumberOfRows(); row += mNthreads) { TPCFastSpaceChargeCorrection::SplineType spline = correction.getSpline(slice, row); helper.setSpline(spline, 10, 10); - std::vector dataPointCU, dataPointCV, dataPointF; - - float u0, u1, v0, v1; - mGeo.convScaledUVtoUV(slice, row, 0., 0., u0, v0); - mGeo.convScaledUVtoUV(slice, row, 1., 1., u1, v1); double x = mGeo.getRowInfo(row).x; - int nPointsU = (spline.getGridX1().getNumberOfKnots() - 1) * 10; - int nPointsV = (spline.getGridX2().getNumberOfKnots() - 1) * 10; - - double stepU = (u1 - u0) / (nPointsU - 1); - double stepV = (v1 - v0) / (nPointsV - 1); + auto& sliceRowInfo = correction.getSliceRowInfo(slice, row); - if (prn) { - LOG(info) << "u0 " << u0 << " u1 " << u1 << " v0 " << v0 << " v1 " << v1; + std::vector gridU; + { + const auto& grid = spline.getGridX1(); + for (int i = 0; i < grid.getNumberOfKnots(); i++) { + if (i == grid.getNumberOfKnots() - 1) { + gridU.push_back(grid.getKnot(i).u); + break; + } + for (double s = 1.; s > 0.; s -= 0.1) { + gridU.push_back(s * grid.getKnot(i).u + (1. - s) * grid.getKnot(i + 1).u); + } + } + } + std::vector gridV; + { + const auto& grid = spline.getGridX2(); + for (int i = 0; i < grid.getNumberOfKnots(); i++) { + if (i == grid.getNumberOfKnots() - 1) { + gridV.push_back(grid.getKnot(i).u); + break; + } + for (double s = 1.; s > 0.; s -= 0.1) { + gridV.push_back(s * grid.getKnot(i).u + (1. - s) * grid.getKnot(i + 1).u); + } + } } - TPCFastSpaceChargeCorrection::RowActiveArea& area = correction.getSliceRowInfo(slice, row).activeArea; + + std::vector dataPointCU, dataPointCV, dataPointF; + dataPointCU.reserve(gridU.size() * gridV.size()); + dataPointCV.reserve(gridU.size() * gridV.size()); + dataPointF.reserve(gridU.size() * gridV.size()); + + TPCFastSpaceChargeCorrection::RowActiveArea& area = sliceRowInfo.activeArea; area.cuMin = 1.e10; area.cuMax = -1.e10; + double cvMin = 1.e10; - /* - v1 = area.vMax; - stepV = (v1 - v0) / (nPointsU - 1); - if (stepV < 1.f) { - stepV = 1.f; - } - */ + for (int iu = 0; iu < gridU.size(); iu++) { + for (int iv = 0; iv < gridV.size(); iv++) { + float u, v; + correction.convGridToUV(slice, row, gridU[iu], gridV[iv], u, v); - for (double u = u0; u < u1 + stepU; u += stepU) { - for (double v = v0; v < v1 + stepV; v += stepV) { float dx, du, dv; correction.getCorrection(slice, row, u, v, dx, du, dv); dx *= scaling[0]; @@ -976,39 +988,41 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector +#include #include #include "Spline2DHelper.h" #endif @@ -514,15 +515,41 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) tpcR2max = tpcR2max / cos(2 * M_PI / mGeo.getNumberOfSlicesA() / 2) + 1.; tpcR2max = tpcR2max * tpcR2max; - double maxDtpc[3] = {0, 0, 0}; - double maxD = 0; + struct MaxValue { + double V{0.}; + int Roc{-1}; + int Row{-1}; + + void update(double v, int roc, int row) + { + if (fabs(v) > fabs(V)) { + V = v; + Roc = roc; + Row = row; + } + } + void update(const MaxValue& other) + { + update(other.V, other.Roc, other.Row); + } + + std::string toString() + { + std::stringstream ss; + ss << V << "(" << Roc << "," << Row << ")"; + return ss.str(); + } + }; + + MaxValue maxDtpc[3]; + MaxValue maxD; for (int32_t slice = 0; slice < mGeo.getNumberOfSlices(); slice++) { if (prn) { LOG(info) << "check inverse transform for slice " << slice; } - double vLength = (slice < mGeo.getNumberOfSlicesA()) ? mGeo.getTPCzLengthA() : mGeo.getTPCzLengthC(); - double maxDslice[3] = {0, 0, 0}; + double vLength = mGeo.getTPCzLength(slice); + MaxValue maxDslice[3]; for (int32_t row = 0; row < mGeo.getNumberOfRows(); row++) { float u0, u1, v0, v1; mGeo.convScaledUVtoUV(slice, row, 0., 0., u0, v0); @@ -530,9 +557,12 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) double x = mGeo.getRowInfo(row).x; double stepU = (u1 - u0) / 100.; double stepV = (v1 - v0) / 100.; - double maxDrow[3] = {0, 0, 0}; + MaxValue maxDrow[3]; for (double u = u0; u < u1; u += stepU) { for (double v = v0; v < v1; v += stepV) { + if (v < getSliceRowInfo(slice, row).gridV0) { + continue; + } float dx, du, dv; getCorrection(slice, row, u, v, dx, du, dv); double cx = x + dx; @@ -545,11 +575,9 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) float nx, nu, nv; getCorrectionInvCorrectedX(slice, row, cu, cv, nx); getCorrectionInvUV(slice, row, cu, cv, nu, nv); - double d[3] = {nx - cx, nu - u, nv - v}; + double d[3] = {(cx - nx) - dx, (cu - nu) - du, (cv - nv) - dv}; for (int32_t i = 0; i < 3; i++) { - if (fabs(d[i]) > fabs(maxDrow[i])) { - maxDrow[i] = d[i]; - } + maxDrow[i].update(d[i], slice, row); } if (0 && prn && fabs(d[0]) + fabs(d[1]) + fabs(d[2]) > 0.1) { @@ -560,32 +588,26 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) } } } - if (0 && prn) { + if (1 && prn) { LOG(info) << "slice " << slice << " row " << row - << " dx " << maxDrow[0] << " du " << maxDrow[1] << " dv " << maxDrow[2]; + << " dx " << maxDrow[0].V << " du " << maxDrow[1].V << " dv " << maxDrow[2].V; } for (int32_t i = 0; i < 3; i++) { - if (fabs(maxDslice[i]) < fabs(maxDrow[i])) { - maxDslice[i] = maxDrow[i]; - } - if (fabs(maxDtpc[i]) < fabs(maxDrow[i])) { - maxDtpc[i] = maxDrow[i]; - } - if (fabs(maxD) < fabs(maxDrow[i])) { - maxD = maxDrow[i]; - } + maxDslice[i].update(maxDrow[i]); + maxDtpc[i].update(maxDrow[i]); + maxD.update(maxDrow[i]); } } if (prn) { - LOG(info) << "inverse correction: slice " << slice - << " dx " << maxDslice[0] << " du " << maxDslice[1] << " dv " << maxDslice[2]; + LOG(info) << "inverse correction: slice " << slice << ". Max deviations: " + << " dx " << maxDslice[0].toString() << " du " << maxDslice[1].toString() << " dv " << maxDslice[2].toString(); } } // slice LOG(info) << "Test inverse TPC correction. max deviations: " - << " dx " << maxDtpc[0] << " du " << maxDtpc[1] << " dv " << maxDtpc[2] << " cm"; + << " dx " << maxDtpc[0].toString() << " du " << maxDtpc[1].toString() << " dv " << maxDtpc[2].toString() << " cm"; - return maxD; + return maxD.V; } #endif // GPUCA_GPUCODE diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index e69983fab9175..2d2940054023e 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -68,8 +68,6 @@ class TPCFastSpaceChargeCorrection : public FlatObject float scaleCorrVtoGrid{0.f}; ///< scale corrected V to V-grid coordinate float maxCorr[3]{10.f, 10.f, 10.f}; ///< max correction for dX, dU, dV float minCorr[3]{-10.f, -10.f, -10.f}; ///< min correction for dX, dU, dV - float maxInvCorr[3]{10.f, 10.f, 10.f}; ///< max inverse correction for dX, dU, dV - float minInvCorr[3]{-10.f, -10.f, -10.f}; ///< min inverse correction for dX, dU, dV RowActiveArea activeArea; void resetMaxValues() @@ -94,28 +92,6 @@ class TPCFastSpaceChargeCorrection : public FlatObject minCorr[2] = GPUCommonMath::Min(minCorr[2], dv); } - void resetMaxValuesInv() - { - maxInvCorr[0] = 1.f; - minInvCorr[0] = -1.f; - maxInvCorr[1] = 1.f; - minInvCorr[1] = -1.f; - maxInvCorr[2] = 1.f; - minInvCorr[2] = -1.f; - } - - void updateMaxValuesInv(float dx, float du, float dv) - { - maxInvCorr[0] = GPUCommonMath::Max(maxInvCorr[0], dx); - minInvCorr[0] = GPUCommonMath::Min(minInvCorr[0], dx); - - maxInvCorr[1] = GPUCommonMath::Max(maxInvCorr[1], du); - minInvCorr[1] = GPUCommonMath::Min(minInvCorr[1], du); - - maxInvCorr[2] = GPUCommonMath::Max(maxInvCorr[2], dv); - minInvCorr[2] = GPUCommonMath::Min(minInvCorr[2], dv); - } - ClassDefNV(SliceRowInfo, 2); }; @@ -494,7 +470,15 @@ GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvCorrectedX( float dx = 0; spline.interpolateU(splineData, gridU, gridV, &dx); const auto& info = getSliceRowInfo(slice, row); - dx = GPUCommonMath::Max(info.minInvCorr[0], GPUCommonMath::Min(info.maxInvCorr[0], dx)); + + float s = corrV / info.gridCorrV0; + if (s < 0.) { + s = 0.; + } + if (s > 1.) { + s = 1.; + } + dx = GPUCommonMath::Clamp(s * dx, info.minCorr[0], info.maxCorr[0]); x = mGeo.getRowInfo(row).x + dx; } @@ -510,8 +494,15 @@ GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvUV( float duv[2]; spline.interpolateU(splineData, gridU, gridV, duv); const auto& info = getSliceRowInfo(slice, row); - duv[0] = GPUCommonMath::Max(info.minInvCorr[1], GPUCommonMath::Min(info.maxInvCorr[1], duv[0])); - duv[1] = GPUCommonMath::Max(info.minInvCorr[2], GPUCommonMath::Min(info.maxInvCorr[2], duv[1])); + float s = corrV / info.gridCorrV0; + if (s < 0.) { + s = 0.; + } + if (s > 1.) { + s = 1.; + } + duv[0] = GPUCommonMath::Clamp(s * duv[0], info.minCorr[1], info.maxCorr[1]); + duv[1] = GPUCommonMath::Clamp(s * duv[1], info.minCorr[2], info.maxCorr[2]); nomU = corrU - duv[0]; nomV = corrV - duv[1]; } diff --git a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C index c4b0680f2edd4..7e889d5a9e7db 100644 --- a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C +++ b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C @@ -202,41 +202,44 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", TFile* debugFile = new TFile("transformDebug.root", "RECREATE"); debugFile->cd(); - // ntuple with created TPC corrections - TNtuple* debugCorr = new TNtuple("corr", "corr", "iRoc:iRow:x:y:z:cx:cy:cz"); + // debug ntuple with created TPC corrections + // + // measured x,y,z; corrections cx,cy,cz from the measured to the real x,y,z; + // inverse corrections ix,iy,iz at the real position (x+cx,y+cy,z+cz) + // ideally, ix = cx, iy = cy, iz = cz + TNtuple* debugCorr = new TNtuple("corr", "corr", "iRoc:iRow:x:y:z:cx:cy:cz:ix:iy:iz"); debugCorr->SetMarkerStyle(8); debugCorr->SetMarkerSize(0.1); debugCorr->SetMarkerColor(kBlack); - // ntuple with the input data: voxel corrections + // ntuple with the input data: voxels and corrections debugFile->cd(); TNtuple* debugVox = - new TNtuple("vox", "vox", "iRoc:iRow:n:x:y:z:vx:vy:vz:cx:cy:cz"); + new TNtuple("vox", "vox", "iRoc:iRow:n:x:y:z:vx:vy:vz"); debugVox->SetMarkerStyle(8); debugVox->SetMarkerSize(0.8); debugVox->SetMarkerColor(kBlue); - // duplicate of debugVox + // duplicate of debugVox + the spline data at voxels in a different color debugFile->cd(); TNtuple* debugCorrVox = - new TNtuple("corrvox", "corrvox", "iRoc:iRow:n:x:y:z:vx:vy:vz:cx:cy:cz"); + new TNtuple("corrvox", "corrvox", "iRoc:iRow:n:x:y:z:vx:vy:vz:cx:cy:cz:ix:iy:iz"); debugCorrVox->SetMarkerStyle(8); debugCorrVox->SetMarkerSize(0.8); debugCorrVox->SetMarkerColor(kMagenta); - // ntuple with spline grid points + // corrections at the spline grid points debugFile->cd(); - TNtuple* debugGrid = new TNtuple("grid", "grid", "iRoc:iRow:x:y:z:cx:cy:cz"); + TNtuple* debugGrid = new TNtuple("grid", "grid", "iRoc:iRow:x:y:z:cx:cy:cz:ix:iy:iz"); debugGrid->SetMarkerStyle(8); debugGrid->SetMarkerSize(1.2); debugGrid->SetMarkerColor(kBlack); - // ntuple with data points created from voxels (with data smearing and - // extension to the edges) + // ntuple with data points created from voxels (with the data smearing, extension to the edges etc.) debugFile->cd(); TNtuple* debugPoints = new TNtuple("points", "points", "iRoc:iRow:x:y:z:px:py:pz:cx:cy:cz"); @@ -253,6 +256,34 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const o2::gpu::TPCFastTransformGeo& geo = helper->getGeometry(); + auto getAllCorrections = [&](int iRoc, int iRow, float u, float v, float& x, float& y, float& z, float& cx, float& cy, float& cz, float& ix, float& iy, float& iz) { + // define x,y,z + + x = geo.getRowInfo(iRow).x; + geo.convUVtoLocal(iRoc, u, v, y, z); + + // get the corrections cx,cy,cz at x,y,z + float cu, cv; + corr.getCorrection(iRoc, iRow, u, v, cx, cu, cv); + geo.convUVtoLocal(iRoc, cu, cv, cy, cz); + + float corrected_u = u + cu; + float corrected_v = v + cv; + float corrected_x = x + cx; + float corrected_y, corrected_z; + geo.convUVtoLocal(iRoc, corrected_u, corrected_v, corrected_y, corrected_z); + + // get the inverse corrections ix,iy,iz at the corrected x,y,z + float inverted_x, inverted_u, inverted_v, inverted_y, inverted_z; + corr.getCorrectionInvCorrectedX(iRoc, iRow, corrected_u, corrected_v, inverted_x); + corr.getCorrectionInvUV(iRoc, iRow, corrected_u, corrected_v, inverted_u, inverted_v); + geo.convUVtoLocal(iRoc, inverted_u, inverted_v, inverted_y, inverted_z); + + ix = corrected_x - inverted_x; + iy = corrected_y - inverted_y; + iz = corrected_z - inverted_z; + }; + o2::tpc::TrackResiduals::VoxRes* v = nullptr; TBranch* branch = voxResTree->GetBranch("voxRes"); branch->SetAddress(&v); @@ -261,6 +292,8 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", int32_t iRocLast = -1; int32_t iRowLast = -1; + std::cout << "fill debug ntuples at voxels ..." << std::endl; + for (int32_t iVox = 0; iVox < voxResTree->GetEntriesFast(); iVox++) { voxResTree->GetEntry(iVox); @@ -312,12 +345,10 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", z = x * v->stat[o2::tpc::TrackResiduals::VoxZ]; } - float u, v, cx, cu, cv, cy, cz; + float u, v; geo.convLocalToUV(iRoc, y, z, u, v); - corr.getCorrection(iRoc, iRow, u, v, cx, cu, cv); - geo.convUVtoLocal(iRoc, u + cu, v + cv, cy, cz); - cy -= y; - cz -= z; + float x1, y1, z1, cx, cy, cz, ix, iy, iz; + getAllCorrections(iRoc, iRow, u, v, x1, y1, z1, cx, cy, cz, ix, iy, iz); double d[3] = {cx - correctionX, cy - correctionY, cz - correctionZ}; if (voxEntries >= 1.) { @@ -334,13 +365,14 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", nDiff++; } - debugVox->Fill(iRoc, iRow, voxEntries, x, y, z, correctionX, correctionY, correctionZ, - cx, cy, cz); + debugVox->Fill(iRoc, iRow, voxEntries, x, y, z, correctionX, correctionY, correctionZ); + debugCorrVox->Fill(iRoc, iRow, voxEntries, x, y, z, correctionX, correctionY, correctionZ, - cx, cy, cz); + cx, cy, cz, ix, iy, iz); } - std::cout << "create debug ntuples ..." << std::endl; + std::cout + << "fill debug ntuples everywhere .." << std::endl; for (int32_t iRoc = 0; iRoc < geo.getNumberOfSlices(); iRoc++) { // for (int32_t iRoc = 0; iRoc < 1; iRoc++) { @@ -399,18 +431,14 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", for (uint32_t iv = 0; iv < pv.size(); iv++) { float u = pu[iu]; float v = pv[iv]; - float x, y, z; - geo.convUVtoLocal(iRoc, u, v, y, z); - float cx, cu, cv; - corr.getCorrection(iRoc, iRow, u, v, cx, cu, cv); - float cy, cz; - geo.convUVtoLocal(iRoc, u + cu, v + cv, cy, cz); - cy -= y; - cz -= z; + + float x, y, z, cx, cy, cz, ix, iy, iz; + getAllCorrections(iRoc, iRow, u, v, x, y, z, cx, cy, cz, ix, iy, iz); + if (iter == 0) { - debugGrid->Fill(iRoc, iRow, x, y, z, cx, cy, cz); + debugGrid->Fill(iRoc, iRow, x, y, z, cx, cy, cz, ix, iy, iz); } else { - debugCorr->Fill(iRoc, iRow, x, y, z, cx, cy, cz); + debugCorr->Fill(iRoc, iRow, x, y, z, cx, cy, cz, ix, iy, iz); } } } @@ -462,7 +490,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", std::cout << "Mean difference in x,y,z : " << sumDiff[0] << " " << sumDiff[1] << " " << sumDiff[2] << std::endl; - corr.testInverse(0); + corr.testInverse(true); debugFile->cd(); debugCorr->Write(); From 2caa885e6231d3aa71291cd5d451c9f9c8ca78f9 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Mon, 27 Jan 2025 17:35:50 +0000 Subject: [PATCH 503/701] TPC Splines: fix reading track residuals --- .../src/TPCFastSpaceChargeCorrectionHelper.cxx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index 3696df5343ad3..c0bba6f4908a8 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -457,9 +457,9 @@ std::unique_ptr TPCFastSpaceChargeCorrect y = iy * dy; y = y / (y1 - y0) * 2 - 1.; if (iuv == 0) { - LOG(info) << "convert y bin: " << yold << " -> " << y << " -> " << iy; + LOG(info) << "TPC SC splines: convert y bin: " << yold << " -> " << y << " -> " << iy; } else { - LOG(info) << "convert z bin: " << yold << " -> " << y << " -> " << iy; + LOG(info) << "TPC SC splines: convert z bin: " << yold << " -> " << y << " -> " << iy; } } @@ -514,11 +514,12 @@ std::unique_ptr TPCFastSpaceChargeCorrect double uMax = yMax; double vMin = geo.getTPCzLength(iRoc) - zMax; double vMax = geo.getTPCzLength(iRoc) - zMin; - // std::cout << " uMin: " << uMin << " uMax: " << yuMax << " zMin: " << vMin << " zMax: " << vMax << std::endl; info.gridU0 = uMin; info.scaleUtoGrid = spline.getGridX1().getUmax() / (uMax - uMin); info.gridV0 = vMin; info.scaleVtoGrid = spline.getGridX2().getUmax() / (vMax - vMin); + // std::cout << " iRoc " << iRoc << " iRow " << iRow << " uMin: " << uMin << " uMax: " << uMax << " vMin: " << vMin << " vMax: " << vMax + //<< " grid scale u "<< info.scaleUtoGrid << " grid scale v "<< info.scaleVtoGrid<< std::endl; } } @@ -629,7 +630,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect << std::endl; } */ - if (1) { // always use voxel center instead of the mean position + if (0) { // debug: always use voxel center instead of the mean position data.mY = vox.mY; data.mZ = vox.mZ; } From d5fc994e621f559e7d0d063c05c0a0048143b378 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Mon, 27 Jan 2025 17:37:35 +0000 Subject: [PATCH 504/701] TPC Splines: fix scaling splines outside of the measured area --- .../TPCFastSpaceChargeCorrection.h | 152 +++++++----------- 1 file changed, 54 insertions(+), 98 deletions(-) diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index 2d2940054023e..7957d36b494c3 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -58,16 +58,16 @@ class TPCFastSpaceChargeCorrection : public FlatObject }; struct SliceRowInfo { - float gridU0{0.f}; //< U coordinate of the U-grid start - float scaleUtoGrid{0.f}; //< scale U to U-grid coordinate - float gridV0{0.f}; ///< V coordinate of the V-grid start - float scaleVtoGrid{0.f}; //< scale V to V-grid coordinate - float gridCorrU0{0.f}; ///< U coordinate of the U-grid start for corrected U - float scaleCorrUtoGrid{0.f}; ///< scale corrected U to U-grid coordinate - float gridCorrV0{0.f}; ///< V coordinate of the V-grid start for corrected V - float scaleCorrVtoGrid{0.f}; ///< scale corrected V to V-grid coordinate - float maxCorr[3]{10.f, 10.f, 10.f}; ///< max correction for dX, dU, dV - float minCorr[3]{-10.f, -10.f, -10.f}; ///< min correction for dX, dU, dV + float gridU0{0.f}; //< U coordinate of the U-grid start + float scaleUtoGrid{0.f}; //< scale U to U-grid coordinate + float gridV0{0.f}; ///< V coordinate of the V-grid start + float scaleVtoGrid{0.f}; //< scale V to V-grid coordinate + float gridCorrU0{0.f}; ///< U coordinate of the U-grid start for corrected U + float scaleCorrUtoGrid{0.f}; ///< scale corrected U to U-grid coordinate + float gridCorrV0{0.f}; ///< V coordinate of the V-grid start for corrected V + float scaleCorrVtoGrid{0.f}; ///< scale corrected V to V-grid coordinate + float maxCorr[3]{10.f, 10.f, 10.f}; ///< max correction for dX, dU, dV + float minCorr[3]{-10.f, -10.f, -10.f}; ///< min correction for dX, dU, dV RowActiveArea activeArea; void resetMaxValues() @@ -199,12 +199,6 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// _______________ Utilities _______________________________________________ - /// shrink u,v coordinats to the TPC row area +/- fkInterpolationSafetyMargin - GPUd() void schrinkUV(int32_t slice, int32_t row, float& u, float& v) const; - - /// shrink corrected u,v coordinats to the TPC row area +/- fkInterpolationSafetyMargin - GPUd() void schrinkCorrectedUV(int32_t slice, int32_t row, float& corrU, float& corrV) const; - /// convert u,v to internal grid coordinates GPUd() void convUVtoGrid(int32_t slice, int32_t row, float u, float v, float& gridU, float& gridV) const; @@ -338,60 +332,9 @@ GPUdi() const float* TPCFastSpaceChargeCorrection::getSplineData(int32_t slice, return reinterpret_cast(mSplineData[iSpline] + mSliceDataSizeBytes[iSpline] * slice + rowInfo.dataOffsetBytes[iSpline]); } -GPUdi() void TPCFastSpaceChargeCorrection::schrinkUV(int32_t slice, int32_t row, float& u, float& v) const -{ - /// shrink u,v coordinats to the TPC row area +/- fInterpolationSafetyMargin - - const TPCFastTransformGeo::RowInfo& rowInfo = mGeo.getRowInfo(row); - - float uWidth05 = rowInfo.getUwidth() * (0.5f + fInterpolationSafetyMargin); - float vWidth = mGeo.getTPCzLength(slice); - - if (u < -uWidth05) { - u = -uWidth05; - } - if (u > uWidth05) { - u = uWidth05; - } - if (v < -0.1f * vWidth) { - v = -0.1f * vWidth; - } - if (v > 1.1f * vWidth) { - v = 1.1f * vWidth; - } -} - -GPUdi() void TPCFastSpaceChargeCorrection::schrinkCorrectedUV(int32_t slice, int32_t row, float& corrU, float& corrV) const -{ - /// shrink corrected u,v coordinats to the TPC row area +/- fInterpolationSafetyMargin - - const TPCFastTransformGeo::RowInfo& rowInfo = mGeo.getRowInfo(row); - const SliceRowInfo& sliceRowInfo = getSliceRowInfo(slice, row); - - float uMargin = fInterpolationSafetyMargin * rowInfo.getUwidth(); - float vMargin = fInterpolationSafetyMargin * mGeo.getTPCzLength(slice); - - if (corrU < sliceRowInfo.activeArea.cuMin - uMargin) { - corrU = sliceRowInfo.activeArea.cuMin - uMargin; - } - - if (corrU > sliceRowInfo.activeArea.cuMax + uMargin) { - corrU = sliceRowInfo.activeArea.cuMax + uMargin; - } - - if (corrV < 0.f - vMargin) { - corrV = 0.f - vMargin; - } - - if (corrV > sliceRowInfo.activeArea.cvMax + vMargin) { - corrV = sliceRowInfo.activeArea.cvMax + vMargin; - } -} - GPUdi() void TPCFastSpaceChargeCorrection::convUVtoGrid(int32_t slice, int32_t row, float u, float v, float& gu, float& gv) const { - schrinkUV(slice, row, u, v); - const SliceRowInfo& info = getSliceRowInfo(slice, row); + const auto& info = getSliceRowInfo(slice, row); gu = (u - info.gridU0) * info.scaleUtoGrid; gv = (v - info.gridV0) * info.scaleVtoGrid; } @@ -406,34 +349,36 @@ GPUdi() void TPCFastSpaceChargeCorrection::convGridToUV(int32_t slice, int32_t r GPUdi() void TPCFastSpaceChargeCorrection::convCorrectedUVtoGrid(int32_t slice, int32_t row, float corrU, float corrV, float& gridU, float& gridV) const { - schrinkCorrectedUV(slice, row, corrU, corrV); - - const SliceRowInfo& sliceRowInfo = getSliceRowInfo(slice, row); - - gridU = (corrU - sliceRowInfo.gridCorrU0) * sliceRowInfo.scaleCorrUtoGrid; - gridV = (corrV - sliceRowInfo.gridCorrV0) * sliceRowInfo.scaleCorrVtoGrid; + const SliceRowInfo& info = getSliceRowInfo(slice, row); + gridU = (corrU - info.gridCorrU0) * info.scaleCorrUtoGrid; + gridV = (corrV - info.gridCorrV0) * info.scaleCorrVtoGrid; } GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrection(int32_t slice, int32_t row, float u, float v, float& dx, float& du, float& dv) const { + const auto& info = getSliceRowInfo(slice, row); const SplineType& spline = getSpline(slice, row); const float* splineData = getSplineData(slice, row); float gridU = 0, gridV = 0; convUVtoGrid(slice, row, u, v, gridU, gridV); + // shrink to the grid area + gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); + gridV = GPUCommonMath::Clamp(gridV, 0.f, (float)spline.getGridX2().getUmax()); + float dxuv[3]; spline.interpolateU(splineData, gridU, gridV, dxuv); - const auto& info = getSliceRowInfo(slice, row); + float s = v / info.gridV0; - if (s < 0.) { - s = 0.; - } - if (s > 1.) { - s = 1.; + + if (v >= info.gridV0) { + s = 1.f; + } else if (v <= 0.f) { + s = 0.f; } - dx = GPUCommonMath::Max(info.minCorr[0], GPUCommonMath::Min(info.maxCorr[0], s * dxuv[0])); - du = GPUCommonMath::Max(info.minCorr[1], GPUCommonMath::Min(info.maxCorr[1], s * dxuv[1])); - dv = GPUCommonMath::Max(info.minCorr[2], GPUCommonMath::Min(info.maxCorr[2], s * dxuv[2])); + dx = GPUCommonMath::Clamp(s * dxuv[0], info.minCorr[0], info.maxCorr[0]); + du = GPUCommonMath::Clamp(s * dxuv[1], info.minCorr[1], info.maxCorr[1]); + dv = GPUCommonMath::Clamp(s * dxuv[2], info.minCorr[2], info.maxCorr[2]); return 0; } @@ -462,22 +407,28 @@ GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrectionOld(int32_t slice, in GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvCorrectedX( int32_t slice, int32_t row, float corrU, float corrV, float& x) const { + const auto& info = getSliceRowInfo(slice, row); + const Spline2D& spline = reinterpret_cast&>(getSpline(slice, row)); + const float* splineData = getSplineData(slice, row, 1); + float gridU, gridV; convCorrectedUVtoGrid(slice, row, corrU, corrV, gridU, gridV); - const Spline2D& spline = reinterpret_cast&>(getSpline(slice, row)); - const float* splineData = getSplineData(slice, row, 1); + // shrink to the grid area + gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); + gridV = GPUCommonMath::Clamp(gridV, 0.f, (float)spline.getGridX2().getUmax()); + float dx = 0; spline.interpolateU(splineData, gridU, gridV, &dx); - const auto& info = getSliceRowInfo(slice, row); float s = corrV / info.gridCorrV0; - if (s < 0.) { - s = 0.; - } - if (s > 1.) { - s = 1.; + + if (corrV >= info.gridCorrV0) { + s = 1.f; + } else if (corrV <= 0.f) { + s = 0.f; } + dx = GPUCommonMath::Clamp(s * dx, info.minCorr[0], info.maxCorr[0]); x = mGeo.getRowInfo(row).x + dx; } @@ -485,22 +436,27 @@ GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvCorrectedX( GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvUV( int32_t slice, int32_t row, float corrU, float corrV, float& nomU, float& nomV) const { + const Spline2D& spline = reinterpret_cast&>(getSpline(slice, row)); + const float* splineData = getSplineData(slice, row, 2); + float gridU, gridV; convCorrectedUVtoGrid(slice, row, corrU, corrV, gridU, gridV); - const Spline2D& spline = reinterpret_cast&>(getSpline(slice, row)); - const float* splineData = getSplineData(slice, row, 2); + // shrink to the grid area + gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); + gridV = GPUCommonMath::Clamp(gridV, 0.f, (float)spline.getGridX2().getUmax()); float duv[2]; spline.interpolateU(splineData, gridU, gridV, duv); const auto& info = getSliceRowInfo(slice, row); float s = corrV / info.gridCorrV0; - if (s < 0.) { - s = 0.; - } - if (s > 1.) { - s = 1.; + + if (corrV >= info.gridCorrV0) { + s = 1.f; + } else if (corrV <= 0.f) { + s = 0.f; } + duv[0] = GPUCommonMath::Clamp(s * duv[0], info.minCorr[1], info.maxCorr[1]); duv[1] = GPUCommonMath::Clamp(s * duv[1], info.minCorr[2], info.maxCorr[2]); nomU = corrU - duv[0]; From 020b243ecb859232223db86d63b1a97ab1d783c5 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Mon, 27 Jan 2025 19:41:36 +0000 Subject: [PATCH 505/701] TPC Splines: rename Slice -> Roc in geometry --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 110 +++---- .../src/TPCFastTransformHelperO2.cxx | 4 +- .../test/testTPCFastTransform.cxx | 30 +- .../TPCFastSpaceChargeCorrection.cxx | 130 ++++---- .../TPCFastSpaceChargeCorrection.h | 146 ++++----- GPU/TPCFastTransformation/TPCFastTransform.h | 278 +++++++++--------- .../TPCFastTransformGeo.cxx | 24 +- .../TPCFastTransformGeo.h | 107 +++---- .../TPCFastTransformManager.cxx | 26 +- .../TPCFastTransformationLinkDef_O2.h | 6 +- .../macro/generateTPCCorrectionNTuple.C | 24 +- 11 files changed, 443 insertions(+), 442 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index c0bba6f4908a8..bac332a837c55 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -134,17 +134,17 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas LOG(info) << "fast space charge correction helper: init from data points"; - for (int slice = 0; slice < correction.getGeometry().getNumberOfSlices(); slice++) { + for (int roc = 0; roc < correction.getGeometry().getNumberOfRocs(); roc++) { auto myThread = [&](int iThread) { for (int row = iThread; row < correction.getGeometry().getNumberOfRows(); row += mNthreads) { - TPCFastSpaceChargeCorrection::SplineType& spline = correction.getSpline(slice, row); + TPCFastSpaceChargeCorrection::SplineType& spline = correction.getSpline(roc, row); Spline2DHelper helper; - float* splineParameters = correction.getSplineData(slice, row); - const std::vector& data = mCorrectionMap.getPoints(slice, row); + float* splineParameters = correction.getSplineData(roc, row); + const std::vector& data = mCorrectionMap.getPoints(roc, row); int nDataPoints = data.size(); - auto& info = correction.getSliceRowInfo(slice, row); + auto& info = correction.getRocRowInfo(roc, row); info.resetMaxValues(); if (nDataPoints >= 4) { std::vector pointSU(nDataPoints); @@ -152,7 +152,7 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas std::vector pointCorr(3 * nDataPoints); // 3 dimensions for (int i = 0; i < nDataPoints; ++i) { double su, sv, dx, du, dv; - getSpaceChargeCorrection(correction, slice, row, data[i], su, sv, dx, du, dv); + getSpaceChargeCorrection(correction, roc, row, data[i], su, sv, dx, du, dv); pointSU[i] = su; pointSV[i] = sv; pointCorr[3 * i + 0] = dx; @@ -182,7 +182,7 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas th.join(); } - } // slice + } // roc watch.Stop(); @@ -191,7 +191,7 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas initInverse(correction, 0); } -void TPCFastSpaceChargeCorrectionHelper::getSpaceChargeCorrection(const TPCFastSpaceChargeCorrection& correction, int slice, int row, o2::gpu::TPCFastSpaceChargeCorrectionMap::CorrectionPoint p, +void TPCFastSpaceChargeCorrectionHelper::getSpaceChargeCorrection(const TPCFastSpaceChargeCorrection& correction, int roc, int row, o2::gpu::TPCFastSpaceChargeCorrectionMap::CorrectionPoint p, double& su, double& sv, double& dx, double& du, double& dv) { // get space charge correction in internal TPCFastTransform coordinates su,sv->dx,du,dv @@ -202,14 +202,14 @@ void TPCFastSpaceChargeCorrectionHelper::getSpaceChargeCorrection(const TPCFastS // not corrected coordinates in u,v float u = 0.f, v = 0.f, fsu = 0.f, fsv = 0.f; - mGeo.convLocalToUV(slice, p.mY, p.mZ, u, v); - correction.convUVtoGrid(slice, row, u, v, fsu, fsv); - // mGeo.convUVtoScaledUV(slice, row, u, v, fsu, fsv); + mGeo.convLocalToUV(roc, p.mY, p.mZ, u, v); + correction.convUVtoGrid(roc, row, u, v, fsu, fsv); + // mGeo.convUVtoScaledUV(roc, row, u, v, fsu, fsv); su = fsu; sv = fsv; // corrected coordinates in u,v float u1 = 0.f, v1 = 0.f; - mGeo.convLocalToUV(slice, p.mY + p.mDy, p.mZ + p.mDz, u1, v1); + mGeo.convLocalToUV(roc, p.mY + p.mDy, p.mZ + p.mDz, u1, v1); dx = p.mDx; du = u1 - u; @@ -286,7 +286,7 @@ std::unique_ptr TPCFastSpaceChargeCorrectionHelper /// set space charge correction in the local coordinates /// as a continious function - int nRocs = mGeo.getNumberOfSlices(); + int nRocs = mGeo.getNumberOfRocs(); int nRows = mGeo.getNumberOfRows(); mCorrectionMap.init(nRocs, nRows); @@ -337,8 +337,8 @@ void TPCFastSpaceChargeCorrectionHelper::testGeometry(const TPCFastTransformGeo& { const Mapper& mapper = Mapper::instance(); - if (geo.getNumberOfSlices() != Sector::MAXSECTOR) { - LOG(fatal) << "Wrong number of sectors :" << geo.getNumberOfSlices() << " instead of " << Sector::MAXSECTOR << std::endl; + if (geo.getNumberOfRocs() != Sector::MAXSECTOR) { + LOG(fatal) << "Wrong number of sectors :" << geo.getNumberOfRocs() << " instead of " << Sector::MAXSECTOR << std::endl; } if (geo.getNumberOfRows() != mapper.getNumberOfRows()) { @@ -404,7 +404,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect const o2::gpu::TPCFastTransformGeo& geo = helper->getGeometry(); o2::gpu::TPCFastSpaceChargeCorrectionMap& map = helper->getCorrectionMap(); - map.init(geo.getNumberOfSlices(), geo.getNumberOfRows()); + map.init(geo.getNumberOfRocs(), geo.getNumberOfRows()); int nY2Xbins = trackResiduals.getNY2XBins(); int nZ2Xbins = trackResiduals.getNZ2XBins(); @@ -480,7 +480,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect // std::cout << "n knots Z: " << nKnotsZ << std::endl; const int nRows = geo.getNumberOfRows(); - const int nROCs = geo.getNumberOfSlices(); + const int nROCs = geo.getNumberOfRocs(); { // create the correction object @@ -501,10 +501,10 @@ std::unique_ptr TPCFastSpaceChargeCorrect } // .. create the correction object // set the grid borders - for (int iRoc = 0; iRoc < geo.getNumberOfSlices(); iRoc++) { + for (int iRoc = 0; iRoc < geo.getNumberOfRocs(); iRoc++) { for (int iRow = 0; iRow < geo.getNumberOfRows(); iRow++) { const auto& rowInfo = geo.getRowInfo(iRow); - auto& info = correction.getSliceRowInfo(iRoc, iRow); + auto& info = correction.getRocRowInfo(iRoc, iRow); const auto& spline = correction.getSpline(iRoc, iRow); double yMin = rowInfo.x * trackResiduals.getY2X(iRow, 0); double yMax = rowInfo.x * trackResiduals.getY2X(iRow, trackResiduals.getNY2XBins() - 1); @@ -616,7 +616,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect vox.mZ = x * z2x; vox.mDy = x / trackResiduals.getDY2XI(xBin, iy); vox.mDz = x * trackResiduals.getDZ2X(iz); - if (iRoc >= geo.getNumberOfSlicesA()) { + if (iRoc >= geo.getNumberOfRocsA()) { vox.mZ = -vox.mZ; } data.mY *= x; @@ -720,7 +720,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect // feed the row data to the helper - auto& info = correction.getSliceRowInfo(iRoc, iRow); + auto& info = correction.getRocRowInfo(iRoc, iRow); const auto& spline = correction.getSpline(iRoc, iRow); auto addEdge = [&](int iy1, int iz1, int iy2, int iz2, int nSteps) { @@ -813,21 +813,21 @@ void TPCFastSpaceChargeCorrectionHelper::initMaxDriftLength(o2::gpu::TPCFastSpac double tpcR2min = mGeo.getRowInfo(0).x - 1.; tpcR2min = tpcR2min * tpcR2min; double tpcR2max = mGeo.getRowInfo(mGeo.getNumberOfRows() - 1).x; - tpcR2max = tpcR2max / cos(2 * M_PI / mGeo.getNumberOfSlicesA() / 2) + 1.; + tpcR2max = tpcR2max / cos(2 * M_PI / mGeo.getNumberOfRocsA() / 2) + 1.; tpcR2max = tpcR2max * tpcR2max; ChebyshevFit1D chebFitter; - for (int slice = 0; slice < mGeo.getNumberOfSlices(); slice++) { + for (int roc = 0; roc < mGeo.getNumberOfRocs(); roc++) { if (prn) { - LOG(info) << "init MaxDriftLength for slice " << slice; + LOG(info) << "init MaxDriftLength for roc " << roc; } - double vLength = (slice < mGeo.getNumberOfSlicesA()) ? mGeo.getTPCzLengthA() : mGeo.getTPCzLengthC(); - TPCFastSpaceChargeCorrection::SliceInfo& sliceInfo = correction.getSliceInfo(slice); - sliceInfo.vMax = 0.f; + double vLength = (roc < mGeo.getNumberOfRocsA()) ? mGeo.getTPCzLengthA() : mGeo.getTPCzLengthC(); + TPCFastSpaceChargeCorrection::RocInfo& rocInfo = correction.getRocInfo(roc); + rocInfo.vMax = 0.f; for (int row = 0; row < mGeo.getNumberOfRows(); row++) { - TPCFastSpaceChargeCorrection::RowActiveArea& area = correction.getSliceRowInfo(slice, row).activeArea; + TPCFastSpaceChargeCorrection::RowActiveArea& area = correction.getRocRowInfo(roc, row).activeArea; area.cvMax = 0; area.vMax = 0; area.cuMin = mGeo.convPadToU(row, 0.f); @@ -843,7 +843,7 @@ void TPCFastSpaceChargeCorrectionHelper::initMaxDriftLength(o2::gpu::TPCFastSpac while (v1 - v0 > 0.1) { float v = 0.5 * (v0 + v1); float dx, du, dv; - correction.getCorrection(slice, row, u, v, dx, du, dv); + correction.getCorrection(roc, row, u, v, dx, du, dv); double cx = x + dx; double cu = u + du; double cv = v + dv; @@ -872,11 +872,11 @@ void TPCFastSpaceChargeCorrectionHelper::initMaxDriftLength(o2::gpu::TPCFastSpac for (int i = 0; i < 5; i++) { area.maxDriftLengthCheb[i] = chebFitter.getCoefficients()[i]; } - if (sliceInfo.vMax < area.vMax) { - sliceInfo.vMax = area.vMax; + if (rocInfo.vMax < area.vMax) { + rocInfo.vMax = area.vMax; } } // row - } // slice + } // roc } void TPCFastSpaceChargeCorrectionHelper::initInverse(o2::gpu::TPCFastSpaceChargeCorrection& correction, bool prn) @@ -902,22 +902,22 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector helper; std::vector splineParameters; for (int row = iThread; row < mGeo.getNumberOfRows(); row += mNthreads) { - TPCFastSpaceChargeCorrection::SplineType spline = correction.getSpline(slice, row); + TPCFastSpaceChargeCorrection::SplineType spline = correction.getSpline(roc, row); helper.setSpline(spline, 10, 10); double x = mGeo.getRowInfo(row).x; - auto& sliceRowInfo = correction.getSliceRowInfo(slice, row); + auto& rocRowInfo = correction.getRocRowInfo(roc, row); std::vector gridU; { @@ -951,7 +951,7 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vectorgetCorrection(slice, row, u, v, dxTmp, duTmp, dvTmp); + corrections[i]->getCorrection(roc, row, u, v, dxTmp, duTmp, dvTmp); dx += dxTmp * scaling[i]; du += duTmp * scaling[i]; dv += dvTmp * scaling[i]; @@ -1002,28 +1002,28 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vectorgetCorrectionMap(); scData.init(nRocs, nRows); @@ -143,7 +143,7 @@ BOOST_AUTO_TEST_CASE(FastTransform_test_setSpaceChargeCorrection) } } } // row - } // slice + } // roc std::unique_ptr fastTransform(TPCFastTransformHelperO2::instance()->create(0)); @@ -158,12 +158,12 @@ BOOST_AUTO_TEST_CASE(FastTransform_test_setSpaceChargeCorrection) double statDiff = 0., statN = 0.; double statDiffFile = 0., statNFile = 0.; - for (int slice = 0; slice < geo.getNumberOfSlices(); slice += 1) { - //std::cout << "slice " << slice << " ... " << std::endl; + for (int roc = 0; roc < geo.getNumberOfRocs(); roc += 1) { + // std::cout << "roc " << roc << " ... " << std::endl; - const TPCFastTransformGeo::SliceInfo& sliceInfo = geo.getSliceInfo(slice); + const TPCFastTransformGeo::RocInfo& rocInfo = geo.getRocInfo(roc); - float lastTimeBin = fastTransform->getMaxDriftTime(slice, 0.f); + float lastTimeBin = fastTransform->getMaxDriftTime(roc, 0.f); for (int row = 0; row < geo.getNumberOfRows(); row++) { @@ -172,31 +172,31 @@ BOOST_AUTO_TEST_CASE(FastTransform_test_setSpaceChargeCorrection) for (int pad = 0; pad < nPads; pad += 10) { for (float time = 0; time < lastTimeBin; time += 30) { - //std::cout<<"slice "<setApplyCorrectionOff(); float x0, y0, z0; - fastTransform->Transform(slice, row, pad, time, x0, y0, z0); + fastTransform->Transform(roc, row, pad, time, x0, y0, z0); - BOOST_CHECK_EQUAL(geo.test(slice, row, y0, z0), 0); + BOOST_CHECK_EQUAL(geo.test(roc, row, y0, z0), 0); fastTransform->setApplyCorrectionOn(); float x1, y1, z1; - fastTransform->Transform(slice, row, pad, time, x1, y1, z1); + fastTransform->Transform(roc, row, pad, time, x1, y1, z1); // local to UV float u0, v0, u1, v1; - geo.convLocalToUV(slice, y0, z0, u0, v0); - geo.convLocalToUV(slice, y1, z1, u1, v1); + geo.convLocalToUV(roc, y0, z0, u0, v0); + geo.convLocalToUV(roc, y1, z1, u1, v1); double dx, du, dv; - correctionUV(slice, row, u0, v0, dx, du, dv); + correctionUV(roc, row, u0, v0, dx, du, dv); statDiff += fabs((x1 - x0) - dx) + fabs((u1 - u0) - du) + fabs((v1 - v0) - dv); statN += 3; //std::cout << (x1 - x0) - dx << " " << (u1 - u0) - du << " " << (v1 - v0) - dv << std::endl; //": v0 " << v0 <<" z0 "<Transform(slice, row, pad, time, x1f, y1f, z1f); + fromFile->Transform(roc, row, pad, time, x1f, y1f, z1f); statDiffFile += fabs(x1f - x1) + fabs(y1f - y1) + fabs(z1f - z1); statNFile += 3; } diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx index e519716b6eec0..5d3c186a06d42 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx @@ -35,7 +35,7 @@ TPCFastSpaceChargeCorrection::TPCFastSpaceChargeCorrection() mScenarioPtr(nullptr), mTimeStamp(-1), mSplineData{nullptr, nullptr, nullptr}, - mSliceDataSizeBytes{0, 0, 0} + mRocDataSizeBytes{0, 0, 0} { // Default Constructor: creates an empty uninitialized object } @@ -64,7 +64,7 @@ void TPCFastSpaceChargeCorrection::destroy() mTimeStamp = -1; for (int32_t is = 0; is < 3; is++) { mSplineData[is] = nullptr; - mSliceDataSizeBytes[is] = 0; + mRocDataSizeBytes[is] = 0; } FlatObject::destroy(); } @@ -101,13 +101,13 @@ void TPCFastSpaceChargeCorrection::cloneFromObject(const TPCFastSpaceChargeCorre mTimeStamp = obj.mTimeStamp; - for (int32_t i = 0; i < TPCFastTransformGeo::getNumberOfSlices(); ++i) { - mSliceInfo[i] = obj.mSliceInfo[i]; + for (int32_t i = 0; i < TPCFastTransformGeo::getNumberOfRocs(); ++i) { + mRocInfo[i] = obj.mRocInfo[i]; } - mSliceDataSizeBytes[0] = obj.mSliceDataSizeBytes[0]; - mSliceDataSizeBytes[1] = obj.mSliceDataSizeBytes[1]; - mSliceDataSizeBytes[2] = obj.mSliceDataSizeBytes[2]; + mRocDataSizeBytes[0] = obj.mRocDataSizeBytes[0]; + mRocDataSizeBytes[1] = obj.mRocDataSizeBytes[1]; + mRocDataSizeBytes[2] = obj.mRocDataSizeBytes[2]; // variable-size data mScenarioPtr = obj.mScenarioPtr; @@ -121,8 +121,8 @@ void TPCFastSpaceChargeCorrection::cloneFromObject(const TPCFastSpaceChargeCorre mRowInfos[i] = obj.mRowInfos[i]; } - for (int32_t i = 0; i < TPCFastTransformGeo::getNumberOfSlices() * TPCFastTransformGeo::getMaxNumberOfRows(); i++) { - mSliceRowInfos[i] = obj.mSliceRowInfos[i]; + for (int32_t i = 0; i < TPCFastTransformGeo::getNumberOfRocs() * TPCFastTransformGeo::getMaxNumberOfRows(); i++) { + mRocRowInfos[i] = obj.mRocRowInfos[i]; } relocateBufferPointers(oldFlatBufferPtr, mFlatBufferPtr); @@ -143,7 +143,7 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer struct RowInfoVersion3 { int32_t splineScenarioID{0}; ///< scenario index (which of Spline2D splines to use) - size_t dataOffsetBytes[3]{0}; ///< offset for the spline data withing a TPC slice + size_t dataOffsetBytes[3]{0}; ///< offset for the spline data withing a TPC roc }; struct RowActiveAreaVersion3 { @@ -154,7 +154,7 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer float cvMax{0.f}; }; - struct SliceRowInfoVersion3 { + struct RocRowInfoVersion3 { float gridV0{0.f}; ///< V coordinate of the V-grid start float gridCorrU0{0.f}; ///< U coordinate of the U-grid start for corrected U float gridCorrV0{0.f}; ///< V coordinate of the V-grid start for corrected V @@ -171,13 +171,13 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer rowsSize = sizeof(RowInfoVersion3) * mGeo.getNumberOfRows(); } - size_t sliceRowsOffset = rowsOffset + rowsSize; - size_t sliceRowsSize = 0; - if (mClassVersion == 3) { // copy old-format slicerow data from the buffer to the arrays - sliceRowsSize = sizeof(SliceRowInfoVersion3) * mGeo.getNumberOfRows() * mGeo.getNumberOfSlices(); + size_t rocRowsOffset = rowsOffset + rowsSize; + size_t rocRowsSize = 0; + if (mClassVersion == 3) { // copy old-format rocrow data from the buffer to the arrays + rocRowsSize = sizeof(RocRowInfoVersion3) * mGeo.getNumberOfRows() * mGeo.getNumberOfRocs(); } - size_t scOffset = alignSize(sliceRowsOffset + sliceRowsSize, SplineType::getClassAlignmentBytes()); + size_t scOffset = alignSize(rocRowsOffset + rocRowsSize, SplineType::getClassAlignmentBytes()); size_t scSize = sizeof(SplineType) * mNumberOfScenarios; mScenarioPtr = reinterpret_cast(mFlatBufferPtr + scOffset); @@ -192,12 +192,12 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer } size_t bufferSize = scBufferOffset + scBufferSize; for (int32_t is = 0; is < 3; is++) { - size_t sliceDataOffset = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); - mSplineData[is] = reinterpret_cast(mFlatBufferPtr + sliceDataOffset); - bufferSize = sliceDataOffset + mSliceDataSizeBytes[is] * mGeo.getNumberOfSlices(); + size_t rocDataOffset = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); + mSplineData[is] = reinterpret_cast(mFlatBufferPtr + rocDataOffset); + bufferSize = rocDataOffset + mRocDataSizeBytes[is] * mGeo.getNumberOfRocs(); } - if (mClassVersion == 3) { // copy old-format slicerow data from the buffer to the arrays + if (mClassVersion == 3) { // copy old-format rocrow data from the buffer to the arrays auto* rowInfosOld = reinterpret_cast(mFlatBufferPtr + rowsOffset); for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { @@ -214,18 +214,18 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer spline.setXrange(0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax()); } - auto* sliceRowInfosOld = reinterpret_cast(mFlatBufferPtr + sliceRowsOffset); + auto* rocRowInfosOld = reinterpret_cast(mFlatBufferPtr + rocRowsOffset); - for (int32_t slice = 0; slice < mGeo.getNumberOfSlices(); slice++) { + for (int32_t roc = 0; roc < mGeo.getNumberOfRocs(); roc++) { for (int32_t row = 0; row < mGeo.getNumberOfRows(); row++) { - SliceRowInfoVersion3& infoOld = sliceRowInfosOld[mGeo.getNumberOfRows() * slice + row]; - SliceRowInfo& info = getSliceRowInfo(slice, row); - const auto& spline = getSpline(slice, row); + RocRowInfoVersion3& infoOld = rocRowInfosOld[mGeo.getNumberOfRows() * roc + row]; + RocRowInfo& info = getRocRowInfo(roc, row); + const auto& spline = getSpline(roc, row); info.gridU0 = mGeo.getRowInfo(row).u0; info.scaleUtoGrid = spline.getGridX1().getUmax() / mGeo.getRowInfo(row).getUwidth(); info.gridV0 = infoOld.gridV0; - info.scaleVtoGrid = spline.getGridX2().getUmax() / (mGeo.getTPCzLength(slice) + 3. - info.gridV0); + info.scaleVtoGrid = spline.getGridX2().getUmax() / (mGeo.getTPCzLength(roc) + 3. - info.gridV0); info.gridCorrU0 = infoOld.gridCorrU0; info.scaleCorrUtoGrid = infoOld.scaleCorrUtoGrid; @@ -276,7 +276,7 @@ void TPCFastSpaceChargeCorrection::print() const mGeo.print(); LOG(info) << " mNumberOfScenarios = " << mNumberOfScenarios; LOG(info) << " mTimeStamp = " << mTimeStamp; - LOG(info) << " mSliceDataSizeBytes = " << mSliceDataSizeBytes[0] << " " << mSliceDataSizeBytes[1] << " " << mSliceDataSizeBytes[2]; + LOG(info) << " mRocDataSizeBytes = " << mRocDataSizeBytes[0] << " " << mRocDataSizeBytes[1] << " " << mRocDataSizeBytes[2]; { LOG(info) << " TPC rows: "; for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { @@ -292,9 +292,9 @@ void TPCFastSpaceChargeCorrection::print() const } if (mScenarioPtr) { LOG(info) << " Spline Data: "; - for (int32_t is = 0; is < mGeo.getNumberOfSlices(); is++) { + for (int32_t is = 0; is < mGeo.getNumberOfRocs(); is++) { for (int32_t ir = 0; ir < mGeo.getNumberOfRows(); ir++) { - LOG(info) << "slice " << is << " row " << ir << ": "; + LOG(info) << "roc " << is << " row " << ir << ": "; const SplineType& spline = getSpline(is, ir); const float* d = getSplineData(is, ir); int32_t k = 0; @@ -305,8 +305,8 @@ void TPCFastSpaceChargeCorrection::print() const LOG(info) << ""; } } - // LOG(info) << "inverse correction: slice " << slice - // << " dx " << maxDslice[0] << " du " << maxDslice[1] << " dv " << maxDslice[2] ; + // LOG(info) << "inverse correction: roc " << roc + // << " dx " << maxDroc[0] << " du " << maxDroc[1] << " dv " << maxDroc[2] ; } } } @@ -345,7 +345,7 @@ void TPCFastSpaceChargeCorrection::startConstruction(const TPCFastTransformGeo& mScenarioPtr = nullptr; for (int32_t s = 0; s < 3; s++) { mSplineData[s] = nullptr; - mSliceDataSizeBytes[s] = 0; + mRocDataSizeBytes[s] = 0; } mClassVersion = 4; } @@ -401,18 +401,18 @@ void TPCFastSpaceChargeCorrection::finishConstruction() scBufferSize = alignSize(scBufferSize + sp.getFlatBufferSize(), sp.getBufferAlignmentBytes()); } size_t bufferSize = scBufferOffsets[0] + scBufferSize; - size_t sliceDataOffset[3]; + size_t rocDataOffset[3]; for (int32_t is = 0; is < 3; is++) { - sliceDataOffset[is] = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); - mSliceDataSizeBytes[is] = 0; + rocDataOffset[is] = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); + mRocDataSizeBytes[is] = 0; for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { RowInfo& row = mRowInfos[i]; SplineType& spline = mConstructionScenarios[row.splineScenarioID]; - row.dataOffsetBytes[is] = alignSize(mSliceDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); - mSliceDataSizeBytes[is] = row.dataOffsetBytes[is] + spline.getSizeOfParameters(); + row.dataOffsetBytes[is] = alignSize(mRocDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); + mRocDataSizeBytes[is] = row.dataOffsetBytes[is] + spline.getSizeOfParameters(); } - mSliceDataSizeBytes[is] = alignSize(mSliceDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); - bufferSize = sliceDataOffset[is] + mSliceDataSizeBytes[is] * mGeo.getNumberOfSlices(); + mRocDataSizeBytes[is] = alignSize(mRocDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); + bufferSize = rocDataOffset[is] + mRocDataSizeBytes[is] * mGeo.getNumberOfRocs(); } FlatObject::finishConstruction(bufferSize); @@ -427,7 +427,7 @@ void TPCFastSpaceChargeCorrection::finishConstruction() } for (int32_t is = 0; is < 3; is++) { - mSplineData[is] = reinterpret_cast(mFlatBufferPtr + sliceDataOffset[is]); + mSplineData[is] = reinterpret_cast(mFlatBufferPtr + rocDataOffset[is]); } releaseConstructionMemory(); @@ -439,15 +439,15 @@ void TPCFastSpaceChargeCorrection::finishConstruction() GPUd() void TPCFastSpaceChargeCorrection::setNoCorrection() { // initialise all corrections to 0. - for (int32_t slice = 0; slice < mGeo.getNumberOfSlices(); slice++) { - double vLength = (slice < mGeo.getNumberOfSlicesA()) ? mGeo.getTPCzLengthA() : mGeo.getTPCzLengthC(); - SliceInfo& sliceInfo = getSliceInfo(slice); - sliceInfo.vMax = vLength; + for (int32_t roc = 0; roc < mGeo.getNumberOfRocs(); roc++) { + double vLength = (roc < mGeo.getNumberOfRocsA()) ? mGeo.getTPCzLengthA() : mGeo.getTPCzLengthC(); + RocInfo& rocInfo = getRocInfo(roc); + rocInfo.vMax = vLength; for (int32_t row = 0; row < mGeo.getNumberOfRows(); row++) { - const SplineType& spline = getSpline(slice, row); + const SplineType& spline = getSpline(roc, row); for (int32_t is = 0; is < 3; is++) { - float* data = getSplineData(slice, row, is); + float* data = getSplineData(roc, row, is); int32_t nPar = spline.getNumberOfParameters(); if (is == 1) { nPar = nPar / 3; @@ -460,7 +460,7 @@ GPUd() void TPCFastSpaceChargeCorrection::setNoCorrection() } } - SliceRowInfo& info = getSliceRowInfo(slice, row); + RocRowInfo& info = getRocRowInfo(roc, row); info.gridU0 = mGeo.getRowInfo(row).u0; info.scaleUtoGrid = spline.getGridX1().getUmax() / mGeo.getRowInfo(row).getUwidth(); @@ -484,7 +484,7 @@ GPUd() void TPCFastSpaceChargeCorrection::setNoCorrection() area.cvMax = vLength; } // row - } // slice + } // roc } void TPCFastSpaceChargeCorrection::constructWithNoCorrection(const TPCFastTransformGeo& geo) @@ -512,7 +512,7 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) double tpcR2min = mGeo.getRowInfo(0).x - 1.; tpcR2min = tpcR2min * tpcR2min; double tpcR2max = mGeo.getRowInfo(mGeo.getNumberOfRows() - 1).x; - tpcR2max = tpcR2max / cos(2 * M_PI / mGeo.getNumberOfSlicesA() / 2) + 1.; + tpcR2max = tpcR2max / cos(2 * M_PI / mGeo.getNumberOfRocsA() / 2) + 1.; tpcR2max = tpcR2max * tpcR2max; struct MaxValue { @@ -544,27 +544,27 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) MaxValue maxDtpc[3]; MaxValue maxD; - for (int32_t slice = 0; slice < mGeo.getNumberOfSlices(); slice++) { + for (int32_t roc = 0; roc < mGeo.getNumberOfRocs(); roc++) { if (prn) { - LOG(info) << "check inverse transform for slice " << slice; + LOG(info) << "check inverse transform for roc " << roc; } - double vLength = mGeo.getTPCzLength(slice); - MaxValue maxDslice[3]; + double vLength = mGeo.getTPCzLength(roc); + MaxValue maxDroc[3]; for (int32_t row = 0; row < mGeo.getNumberOfRows(); row++) { float u0, u1, v0, v1; - mGeo.convScaledUVtoUV(slice, row, 0., 0., u0, v0); - mGeo.convScaledUVtoUV(slice, row, 1., 1., u1, v1); + mGeo.convScaledUVtoUV(roc, row, 0., 0., u0, v0); + mGeo.convScaledUVtoUV(roc, row, 1., 1., u1, v1); double x = mGeo.getRowInfo(row).x; double stepU = (u1 - u0) / 100.; double stepV = (v1 - v0) / 100.; MaxValue maxDrow[3]; for (double u = u0; u < u1; u += stepU) { for (double v = v0; v < v1; v += stepV) { - if (v < getSliceRowInfo(slice, row).gridV0) { + if (v < getRocRowInfo(roc, row).gridV0) { continue; } float dx, du, dv; - getCorrection(slice, row, u, v, dx, du, dv); + getCorrection(roc, row, u, v, dx, du, dv); double cx = x + dx; double cu = u + du; double cv = v + dv; @@ -573,11 +573,11 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) continue; } float nx, nu, nv; - getCorrectionInvCorrectedX(slice, row, cu, cv, nx); - getCorrectionInvUV(slice, row, cu, cv, nu, nv); + getCorrectionInvCorrectedX(roc, row, cu, cv, nx); + getCorrectionInvUV(roc, row, cu, cv, nu, nv); double d[3] = {(cx - nx) - dx, (cu - nu) - du, (cv - nv) - dv}; for (int32_t i = 0; i < 3; i++) { - maxDrow[i].update(d[i], slice, row); + maxDrow[i].update(d[i], roc, row); } if (0 && prn && fabs(d[0]) + fabs(d[1]) + fabs(d[2]) > 0.1) { @@ -589,20 +589,20 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) } } if (1 && prn) { - LOG(info) << "slice " << slice << " row " << row + LOG(info) << "roc " << roc << " row " << row << " dx " << maxDrow[0].V << " du " << maxDrow[1].V << " dv " << maxDrow[2].V; } for (int32_t i = 0; i < 3; i++) { - maxDslice[i].update(maxDrow[i]); + maxDroc[i].update(maxDrow[i]); maxDtpc[i].update(maxDrow[i]); maxD.update(maxDrow[i]); } } if (prn) { - LOG(info) << "inverse correction: slice " << slice << ". Max deviations: " - << " dx " << maxDslice[0].toString() << " du " << maxDslice[1].toString() << " dv " << maxDslice[2].toString(); + LOG(info) << "inverse correction: roc " << roc << ". Max deviations: " + << " dx " << maxDroc[0].toString() << " du " << maxDroc[1].toString() << " dv " << maxDroc[2].toString(); } - } // slice + } // roc LOG(info) << "Test inverse TPC correction. max deviations: " << " dx " << maxDtpc[0].toString() << " du " << maxDtpc[1].toString() << " dv " << maxDtpc[2].toString() << " cm"; diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index 7957d36b494c3..b6244bfee1e0f 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -44,7 +44,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// struct RowInfo { int32_t splineScenarioID{0}; ///< scenario index (which of Spline2D splines to use) - size_t dataOffsetBytes[3]{0}; ///< offset for the spline data withing a TPC slice + size_t dataOffsetBytes[3]{0}; ///< offset for the spline data withing the TPC roc ClassDefNV(RowInfo, 1); }; @@ -57,7 +57,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject ClassDefNV(RowActiveArea, 1); }; - struct SliceRowInfo { + struct RocRowInfo { float gridU0{0.f}; //< U coordinate of the U-grid start float scaleUtoGrid{0.f}; //< scale U to U-grid coordinate float gridV0{0.f}; ///< V coordinate of the V-grid start @@ -92,12 +92,12 @@ class TPCFastSpaceChargeCorrection : public FlatObject minCorr[2] = GPUCommonMath::Min(minCorr[2], dv); } - ClassDefNV(SliceRowInfo, 2); + ClassDefNV(RocRowInfo, 2); }; - struct SliceInfo { + struct RocInfo { float vMax{0.f}; ///< Max value of V coordinate - ClassDefNV(SliceInfo, 1); + ClassDefNV(RocInfo, 1); }; typedef Spline2D SplineType; @@ -167,46 +167,46 @@ class TPCFastSpaceChargeCorrection : public FlatObject GPUd() void setInterpolationSafetyMargin(float val) { fInterpolationSafetyMargin = val; } /// Gives const pointer to a spline - GPUd() const SplineType& getSpline(int32_t slice, int32_t row) const; + GPUd() const SplineType& getSpline(int32_t roc, int32_t row) const; /// Gives pointer to a spline - GPUd() SplineType& getSpline(int32_t slice, int32_t row); + GPUd() SplineType& getSpline(int32_t roc, int32_t row); /// Gives pointer to spline data - GPUd() float* getSplineData(int32_t slice, int32_t row, int32_t iSpline = 0); + GPUd() float* getSplineData(int32_t roc, int32_t row, int32_t iSpline = 0); /// Gives pointer to spline data - GPUd() const float* getSplineData(int32_t slice, int32_t row, int32_t iSpline = 0) const; + GPUd() const float* getSplineData(int32_t roc, int32_t row, int32_t iSpline = 0) const; /// _______________ The main method: cluster correction _______________________ /// - GPUd() int32_t getCorrection(int32_t slice, int32_t row, float u, float v, float& dx, float& du, float& dv) const; + GPUd() int32_t getCorrection(int32_t roc, int32_t row, float u, float v, float& dx, float& du, float& dv) const; /// inverse correction: Corrected U and V -> coorrected X - GPUd() void getCorrectionInvCorrectedX(int32_t slice, int32_t row, float corrU, float corrV, float& corrX) const; + GPUd() void getCorrectionInvCorrectedX(int32_t roc, int32_t row, float corrU, float corrV, float& corrX) const; /// inverse correction: Corrected U and V -> uncorrected U and V - GPUd() void getCorrectionInvUV(int32_t slice, int32_t row, float corrU, float corrV, float& nomU, float& nomV) const; + GPUd() void getCorrectionInvUV(int32_t roc, int32_t row, float corrU, float corrV, float& nomU, float& nomV) const; /// maximal possible drift length of the active area - GPUd() float getMaxDriftLength(int32_t slice, int32_t row, float pad) const; + GPUd() float getMaxDriftLength(int32_t roc, int32_t row, float pad) const; /// maximal possible drift length of the active area - GPUd() float getMaxDriftLength(int32_t slice, int32_t row) const; + GPUd() float getMaxDriftLength(int32_t roc, int32_t row) const; /// maximal possible drift length of the active area - GPUd() float getMaxDriftLength(int32_t slice) const; + GPUd() float getMaxDriftLength(int32_t roc) const; /// _______________ Utilities _______________________________________________ /// convert u,v to internal grid coordinates - GPUd() void convUVtoGrid(int32_t slice, int32_t row, float u, float v, float& gridU, float& gridV) const; + GPUd() void convUVtoGrid(int32_t roc, int32_t row, float u, float v, float& gridU, float& gridV) const; /// convert u,v to internal grid coordinates - GPUd() void convGridToUV(int32_t slice, int32_t row, float gridU, float gridV, float& u, float& v) const; + GPUd() void convGridToUV(int32_t roc, int32_t row, float gridU, float gridV, float& u, float& v) const; /// convert corrected u,v to internal grid coordinates - GPUd() void convCorrectedUVtoGrid(int32_t slice, int32_t row, float cu, float cv, float& gridU, float& gridV) const; + GPUd() void convCorrectedUVtoGrid(int32_t roc, int32_t row, float cu, float cv, float& gridU, float& gridV) const; /// TPC geometry information GPUd() const TPCFastTransformGeo& getGeometry() const @@ -223,28 +223,28 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// Gives TPC row info GPUd() const RowInfo& getRowInfo(int32_t row) const { return mRowInfos[row]; } - /// Gives TPC slice info - GPUd() const SliceInfo& getSliceInfo(int32_t slice) const + /// Gives TPC roc info + GPUd() const RocInfo& getRocInfo(int32_t roc) const { - return mSliceInfo[slice]; + return mRocInfo[roc]; } - /// Gives TPC slice info - GPUd() SliceInfo& getSliceInfo(int32_t slice) + /// Gives TPC roc info + GPUd() RocInfo& getRocInfo(int32_t roc) { - return mSliceInfo[slice]; + return mRocInfo[roc]; } - /// Gives TPC slice & row info - GPUd() const SliceRowInfo& getSliceRowInfo(int32_t slice, int32_t row) const + /// Gives TPC roc & row info + GPUd() const RocRowInfo& getRocRowInfo(int32_t roc, int32_t row) const { - return mSliceRowInfos[mGeo.getMaxNumberOfRows() * slice + row]; + return mRocRowInfos[mGeo.getMaxNumberOfRows() * roc + row]; } - /// Gives TPC slice & row info - GPUd() SliceRowInfo& getSliceRowInfo(int32_t slice, int32_t row) + /// Gives TPC roc & row info + GPUd() RocRowInfo& getRocRowInfo(int32_t roc, int32_t row) { - return mSliceRowInfos[mGeo.getMaxNumberOfRows() * slice + row]; + return mRocRowInfos[mGeo.getMaxNumberOfRows() * roc + row]; } #if !defined(GPUCA_GPUCODE) @@ -260,7 +260,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject void releaseConstructionMemory(); /// temporary method with the an way of calculating 2D spline - GPUd() int32_t getCorrectionOld(int32_t slice, int32_t row, float u, float v, float& dx, float& du, float& dv) const; + GPUd() int32_t getCorrectionOld(int32_t roc, int32_t row, float u, float v, float& dx, float& du, float& dv) const; /// _______________ Data members _______________________________________________ @@ -274,7 +274,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject int32_t mNumberOfScenarios; ///< Number of approximation spline scenarios - SliceInfo mSliceInfo[TPCFastTransformGeo::getNumberOfSlices()]; ///< SliceInfo array + RocInfo mRocInfo[TPCFastTransformGeo::getNumberOfRocs()]; ///< RocInfo array SplineType* mScenarioPtr; //! (transient!!) pointer to spline scenarios @@ -284,7 +284,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject char* mSplineData[3]; //! (transient!!) pointer to the spline data in the flat buffer - size_t mSliceDataSizeBytes[3]; ///< size of the data for one slice in the flat buffer + size_t mRocDataSizeBytes[3]; ///< size of the data for one roc in the flat buffer float fInterpolationSafetyMargin{0.1f}; // 10% area around the TPC row. Outside of this area the interpolation returns the boundary values. @@ -295,72 +295,72 @@ class TPCFastSpaceChargeCorrection : public FlatObject RowInfo mRowInfos[TPCFastTransformGeo::getMaxNumberOfRows()]; ///< RowInfo array - SliceRowInfo mSliceRowInfos[TPCFastTransformGeo::getNumberOfSlices() * TPCFastTransformGeo::getMaxNumberOfRows()]; ///< SliceRowInfo array + RocRowInfo mRocRowInfos[TPCFastTransformGeo::getNumberOfRocs() * TPCFastTransformGeo::getMaxNumberOfRows()]; ///< RocRowInfo array - ClassDefNV(TPCFastSpaceChargeCorrection, 4); + ClassDefNV(TPCFastSpaceChargeCorrection, 5); }; /// ==================================================== /// Inline implementations of some methods /// ==================================================== -GPUdi() const TPCFastSpaceChargeCorrection::SplineType& TPCFastSpaceChargeCorrection::getSpline(int32_t slice, int32_t row) const +GPUdi() const TPCFastSpaceChargeCorrection::SplineType& TPCFastSpaceChargeCorrection::getSpline(int32_t roc, int32_t row) const { /// Gives const pointer to spline const RowInfo& rowInfo = mRowInfos[row]; return mScenarioPtr[rowInfo.splineScenarioID]; } -GPUdi() TPCFastSpaceChargeCorrection::SplineType& TPCFastSpaceChargeCorrection::getSpline(int32_t slice, int32_t row) +GPUdi() TPCFastSpaceChargeCorrection::SplineType& TPCFastSpaceChargeCorrection::getSpline(int32_t roc, int32_t row) { /// Gives pointer to spline const RowInfo& rowInfo = mRowInfos[row]; return mScenarioPtr[rowInfo.splineScenarioID]; } -GPUdi() float* TPCFastSpaceChargeCorrection::getSplineData(int32_t slice, int32_t row, int32_t iSpline) +GPUdi() float* TPCFastSpaceChargeCorrection::getSplineData(int32_t roc, int32_t row, int32_t iSpline) { /// Gives pointer to spline data const RowInfo& rowInfo = mRowInfos[row]; - return reinterpret_cast(mSplineData[iSpline] + mSliceDataSizeBytes[iSpline] * slice + rowInfo.dataOffsetBytes[iSpline]); + return reinterpret_cast(mSplineData[iSpline] + mRocDataSizeBytes[iSpline] * roc + rowInfo.dataOffsetBytes[iSpline]); } -GPUdi() const float* TPCFastSpaceChargeCorrection::getSplineData(int32_t slice, int32_t row, int32_t iSpline) const +GPUdi() const float* TPCFastSpaceChargeCorrection::getSplineData(int32_t roc, int32_t row, int32_t iSpline) const { /// Gives pointer to spline data const RowInfo& rowInfo = mRowInfos[row]; - return reinterpret_cast(mSplineData[iSpline] + mSliceDataSizeBytes[iSpline] * slice + rowInfo.dataOffsetBytes[iSpline]); + return reinterpret_cast(mSplineData[iSpline] + mRocDataSizeBytes[iSpline] * roc + rowInfo.dataOffsetBytes[iSpline]); } -GPUdi() void TPCFastSpaceChargeCorrection::convUVtoGrid(int32_t slice, int32_t row, float u, float v, float& gu, float& gv) const +GPUdi() void TPCFastSpaceChargeCorrection::convUVtoGrid(int32_t roc, int32_t row, float u, float v, float& gu, float& gv) const { - const auto& info = getSliceRowInfo(slice, row); + const auto& info = getRocRowInfo(roc, row); gu = (u - info.gridU0) * info.scaleUtoGrid; gv = (v - info.gridV0) * info.scaleVtoGrid; } -GPUdi() void TPCFastSpaceChargeCorrection::convGridToUV(int32_t slice, int32_t row, float gridU, float gridV, float& u, float& v) const +GPUdi() void TPCFastSpaceChargeCorrection::convGridToUV(int32_t roc, int32_t row, float gridU, float gridV, float& u, float& v) const { /// convert internal grid coordinates to u,v - const SliceRowInfo& info = getSliceRowInfo(slice, row); + const RocRowInfo& info = getRocRowInfo(roc, row); u = info.gridU0 + gridU / info.scaleUtoGrid; v = info.gridV0 + gridV / info.scaleVtoGrid; } -GPUdi() void TPCFastSpaceChargeCorrection::convCorrectedUVtoGrid(int32_t slice, int32_t row, float corrU, float corrV, float& gridU, float& gridV) const +GPUdi() void TPCFastSpaceChargeCorrection::convCorrectedUVtoGrid(int32_t roc, int32_t row, float corrU, float corrV, float& gridU, float& gridV) const { - const SliceRowInfo& info = getSliceRowInfo(slice, row); + const RocRowInfo& info = getRocRowInfo(roc, row); gridU = (corrU - info.gridCorrU0) * info.scaleCorrUtoGrid; gridV = (corrV - info.gridCorrV0) * info.scaleCorrVtoGrid; } -GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrection(int32_t slice, int32_t row, float u, float v, float& dx, float& du, float& dv) const +GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrection(int32_t roc, int32_t row, float u, float v, float& dx, float& du, float& dv) const { - const auto& info = getSliceRowInfo(slice, row); - const SplineType& spline = getSpline(slice, row); - const float* splineData = getSplineData(slice, row); + const auto& info = getRocRowInfo(roc, row); + const SplineType& spline = getSpline(roc, row); + const float* splineData = getSplineData(roc, row); float gridU = 0, gridV = 0; - convUVtoGrid(slice, row, u, v, gridU, gridV); + convUVtoGrid(roc, row, u, v, gridU, gridV); // shrink to the grid area gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); gridV = GPUCommonMath::Clamp(gridV, 0.f, (float)spline.getGridX2().getUmax()); @@ -382,15 +382,15 @@ GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrection(int32_t slice, int32 return 0; } -GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrectionOld(int32_t slice, int32_t row, float u, float v, float& dx, float& du, float& dv) const +GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrectionOld(int32_t roc, int32_t row, float u, float v, float& dx, float& du, float& dv) const { - const SplineType& spline = getSpline(slice, row); - const float* splineData = getSplineData(slice, row); + const SplineType& spline = getSpline(roc, row); + const float* splineData = getSplineData(roc, row); float gridU = 0, gridV = 0; - convUVtoGrid(slice, row, u, v, gridU, gridV); + convUVtoGrid(roc, row, u, v, gridU, gridV); float dxuv[3]; spline.interpolateUold(splineData, gridU, gridV, dxuv); - const auto& info = getSliceRowInfo(slice, row); + const auto& info = getRocRowInfo(roc, row); float s = v / info.gridV0; if (s < 0.) { s = 0.; @@ -405,14 +405,14 @@ GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrectionOld(int32_t slice, in } GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvCorrectedX( - int32_t slice, int32_t row, float corrU, float corrV, float& x) const + int32_t roc, int32_t row, float corrU, float corrV, float& x) const { - const auto& info = getSliceRowInfo(slice, row); - const Spline2D& spline = reinterpret_cast&>(getSpline(slice, row)); - const float* splineData = getSplineData(slice, row, 1); + const auto& info = getRocRowInfo(roc, row); + const Spline2D& spline = reinterpret_cast&>(getSpline(roc, row)); + const float* splineData = getSplineData(roc, row, 1); float gridU, gridV; - convCorrectedUVtoGrid(slice, row, corrU, corrV, gridU, gridV); + convCorrectedUVtoGrid(roc, row, corrU, corrV, gridU, gridV); // shrink to the grid area gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); @@ -434,13 +434,13 @@ GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvCorrectedX( } GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvUV( - int32_t slice, int32_t row, float corrU, float corrV, float& nomU, float& nomV) const + int32_t roc, int32_t row, float corrU, float corrV, float& nomU, float& nomV) const { - const Spline2D& spline = reinterpret_cast&>(getSpline(slice, row)); - const float* splineData = getSplineData(slice, row, 2); + const Spline2D& spline = reinterpret_cast&>(getSpline(roc, row)); + const float* splineData = getSplineData(roc, row, 2); float gridU, gridV; - convCorrectedUVtoGrid(slice, row, corrU, corrV, gridU, gridV); + convCorrectedUVtoGrid(roc, row, corrU, corrV, gridU, gridV); // shrink to the grid area gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); @@ -448,7 +448,7 @@ GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvUV( float duv[2]; spline.interpolateU(splineData, gridU, gridV, duv); - const auto& info = getSliceRowInfo(slice, row); + const auto& info = getRocRowInfo(roc, row); float s = corrV / info.gridCorrV0; if (corrV >= info.gridCorrV0) { @@ -463,9 +463,9 @@ GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvUV( nomV = corrV - duv[1]; } -GPUdi() float TPCFastSpaceChargeCorrection::getMaxDriftLength(int32_t slice, int32_t row, float pad) const +GPUdi() float TPCFastSpaceChargeCorrection::getMaxDriftLength(int32_t roc, int32_t row, float pad) const { - const RowActiveArea& area = getSliceRowInfo(slice, row).activeArea; + const RowActiveArea& area = getRocRowInfo(roc, row).activeArea; const float* c = area.maxDriftLengthCheb; float x = -1.f + 2.f * pad / mGeo.getRowInfo(row).maxPad; float y = c[0] + c[1] * x; @@ -481,14 +481,14 @@ GPUdi() float TPCFastSpaceChargeCorrection::getMaxDriftLength(int32_t slice, int return y; } -GPUdi() float TPCFastSpaceChargeCorrection::getMaxDriftLength(int32_t slice, int32_t row) const +GPUdi() float TPCFastSpaceChargeCorrection::getMaxDriftLength(int32_t roc, int32_t row) const { - return getSliceRowInfo(slice, row).activeArea.vMax; + return getRocRowInfo(roc, row).activeArea.vMax; } -GPUdi() float TPCFastSpaceChargeCorrection::getMaxDriftLength(int32_t slice) const +GPUdi() float TPCFastSpaceChargeCorrection::getMaxDriftLength(int32_t roc) const { - return getSliceInfo(slice).vMax; + return getRocInfo(roc).vMax; } } // namespace gpu diff --git a/GPU/TPCFastTransformation/TPCFastTransform.h b/GPU/TPCFastTransformation/TPCFastTransform.h index d9e35ba8bf405..8aef1748ebf62 100644 --- a/GPU/TPCFastTransformation/TPCFastTransform.h +++ b/GPU/TPCFastTransformation/TPCFastTransform.h @@ -46,14 +46,14 @@ struct TPCSlowSpaceChargeCorrection { ~TPCSlowSpaceChargeCorrection(); /// getting the corrections for global coordinates - void getCorrections(const float gx, const float gy, const float gz, const int32_t slice, float& gdxC, float& gdyC, float& gdzC) const; + void getCorrections(const float gx, const float gy, const float gz, const int32_t roc, float& gdxC, float& gdyC, float& gdzC) const; o2::tpc::SpaceCharge* mCorr{nullptr}; ///< reference space charge corrections #else ~TPCSlowSpaceChargeCorrection() = default; /// setting dummy corrections for GPU - GPUd() void getCorrections(const float gx, const float gy, const float gz, const int32_t slice, float& gdxC, float& gdyC, float& gdzC) const + GPUd() void getCorrections(const float gx, const float gy, const float gz, const int32_t roc, float& gdxC, float& gdyC, float& gdzC) const { gdxC = 0; gdyC = 0; @@ -182,50 +182,50 @@ class TPCFastTransform : public FlatObject /// _______________ The main method: cluster transformation _______________________ /// - /// Transforms raw TPC coordinates to local XYZ withing a slice + /// Transforms raw TPC coordinates to local XYZ withing a roc /// taking calibration + alignment into account. /// - GPUd() void Transform(int32_t slice, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime = 0, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; - GPUd() void TransformXYZ(int32_t slice, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; + GPUd() void Transform(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime = 0, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; + GPUd() void TransformXYZ(int32_t roc, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; /// Transformation in the time frame - GPUd() void TransformInTimeFrame(int32_t slice, int32_t row, float pad, float time, float& x, float& y, float& z, float maxTimeBin) const; - GPUd() void TransformInTimeFrame(int32_t slice, float time, float& z, float maxTimeBin) const; + GPUd() void TransformInTimeFrame(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float maxTimeBin) const; + GPUd() void TransformInTimeFrame(int32_t roc, float time, float& z, float maxTimeBin) const; /// 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; + GPUd() void InverseTransformInTimeFrame(int32_t roc, int32_t row, float /*x*/, float y, float z, float& pad, float& time, float maxTimeBin) const; + GPUd() float InverseTransformInTimeFrame(int32_t roc, 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; + GPUd() void InverseTransformYZtoX(int32_t roc, 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; /// Inverse transformation: Transformed Y and Z -> Y and Z, transformed w/o space charge correction - GPUd() void InverseTransformYZtoNominalYZ(int32_t slice, int32_t row, float y, float z, float& ny, float& nz, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; + GPUd() void InverseTransformYZtoNominalYZ(int32_t roc, int32_t row, float y, float z, float& ny, float& nz, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; /// Inverse transformation: Transformed X, Y and Z -> X, Y and Z, transformed w/o space charge correction - GPUd() void InverseTransformXYZtoNominalXYZ(int32_t slice, int32_t row, float x, float y, float z, float& nx, float& ny, float& nz, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; + GPUd() void InverseTransformXYZtoNominalXYZ(int32_t roc, int32_t row, float x, float y, float z, float& nx, float& ny, float& nz, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; /// Ideal transformation with Vdrift only - without calibration - GPUd() void TransformIdeal(int32_t slice, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime) const; - GPUd() void TransformIdealZ(int32_t slice, float time, float& z, float vertexTime) const; + GPUd() void TransformIdeal(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime) const; + GPUd() void TransformIdealZ(int32_t roc, float time, float& z, float vertexTime) const; - GPUd() void convPadTimeToUV(int32_t slice, int32_t row, float pad, float time, float& u, float& v, float vertexTime) const; - GPUd() void convPadTimeToUVinTimeFrame(int32_t slice, int32_t row, float pad, float time, float& u, float& v, float maxTimeBin) const; - GPUd() void convTimeToVinTimeFrame(int32_t slice, float time, float& v, float maxTimeBin) const; + GPUd() void convPadTimeToUV(int32_t roc, int32_t row, float pad, float time, float& u, float& v, float vertexTime) const; + GPUd() void convPadTimeToUVinTimeFrame(int32_t roc, int32_t row, float pad, float time, float& u, float& v, float maxTimeBin) const; + GPUd() void convTimeToVinTimeFrame(int32_t roc, float time, float& v, float maxTimeBin) const; - GPUd() void convUVtoPadTime(int32_t slice, int32_t row, float u, float v, float& pad, float& time, float vertexTime) const; - GPUd() void convUVtoPadTimeInTimeFrame(int32_t slice, int32_t row, float u, float v, float& pad, float& time, float maxTimeBin) const; + GPUd() void convUVtoPadTime(int32_t roc, int32_t row, float u, float v, float& pad, float& time, float vertexTime) const; + GPUd() void convUVtoPadTimeInTimeFrame(int32_t roc, int32_t row, float u, float v, float& pad, float& time, float maxTimeBin) const; GPUd() void convVtoTime(float v, float& time, float vertexTime) const; - GPUd() float convTimeToZinTimeFrame(int32_t slice, float time, float maxTimeBin) const; - GPUd() float convZtoTimeInTimeFrame(int32_t slice, float z, float maxTimeBin) const; - GPUd() float convDeltaTimeToDeltaZinTimeFrame(int32_t slice, float deltaTime) const; - GPUd() float convDeltaZtoDeltaTimeInTimeFrame(int32_t slice, float deltaZ) const; + GPUd() float convTimeToZinTimeFrame(int32_t roc, float time, float maxTimeBin) const; + GPUd() float convZtoTimeInTimeFrame(int32_t roc, float z, float maxTimeBin) const; + GPUd() float convDeltaTimeToDeltaZinTimeFrame(int32_t roc, float deltaTime) const; + GPUd() float convDeltaZtoDeltaTimeInTimeFrame(int32_t roc, float deltaZ) const; GPUd() float convDeltaZtoDeltaTimeInTimeFrameAbs(float deltaZ) const; - GPUd() float convZOffsetToVertexTime(int32_t slice, float zOffset, float maxTimeBin) const; - GPUd() float convVertexTimeToZOffset(int32_t slice, float vertexTime, float maxTimeBin) const; + GPUd() float convZOffsetToVertexTime(int32_t roc, float zOffset, float maxTimeBin) const; + GPUd() float convVertexTimeToZOffset(int32_t roc, float vertexTime, float maxTimeBin) const; - GPUd() void getTOFcorrection(int32_t slice, int32_t row, float x, float y, float z, float& dz) const; + GPUd() void getTOFcorrection(int32_t roc, int32_t row, float x, float y, float z, float& dz) const; void setApplyCorrectionOn() { mApplyCorrection = 1; } void setApplyCorrectionOff() { mApplyCorrection = 0; } @@ -276,13 +276,13 @@ class TPCFastTransform : public FlatObject GPUd() float getLumiScaleFactor() const { return mLumiScaleFactor; } /// maximal possible drift time of the active area - GPUd() float getMaxDriftTime(int32_t slice, int32_t row, float pad) const; + GPUd() float getMaxDriftTime(int32_t roc, int32_t row, float pad) const; /// maximal possible drift time of the active area - GPUd() float getMaxDriftTime(int32_t slice, int32_t row) const; + GPUd() float getMaxDriftTime(int32_t roc, int32_t row) const; /// maximal possible drift time of the active area - GPUd() float getMaxDriftTime(int32_t slice) const; + GPUd() float getMaxDriftTime(int32_t roc) const; #if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) @@ -359,7 +359,7 @@ class TPCFastTransform : public FlatObject /// Correction of (x,u,v) with tricubic interpolator on a regular grid TPCSlowSpaceChargeCorrection* mCorrectionSlow{nullptr}; ///< reference space charge corrections - GPUd() void TransformInternal(int32_t slice, int32_t row, float& u, float& v, float& x, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const; + GPUd() void TransformInternal(int32_t roc, int32_t row, float& u, float& v, float& x, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const; ClassDefNV(TPCFastTransform, 4); }; @@ -368,69 +368,69 @@ class TPCFastTransform : public FlatObject // Inline implementations of some methods // ======================================================================= -GPUdi() void TPCFastTransform::convPadTimeToUV(int32_t slice, int32_t row, float pad, float time, float& u, float& v, float vertexTime) const +GPUdi() void TPCFastTransform::convPadTimeToUV(int32_t roc, int32_t row, float pad, float time, float& u, float& v, float vertexTime) const { - bool sideC = (slice >= getGeometry().getNumberOfSlicesA()); + bool sideC = (roc >= getGeometry().getNumberOfRocsA()); const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); - const TPCFastTransformGeo::SliceInfo& sliceInfo = getGeometry().getSliceInfo(slice); + const TPCFastTransformGeo::RocInfo& rocInfo = getGeometry().getRocInfo(roc); float x = rowInfo.x; u = (pad - 0.5f * rowInfo.maxPad) * rowInfo.padWidth; float y = sideC ? -u : u; // pads are mirrorred on C-side - float yLab = y * sliceInfo.cosAlpha + x * sliceInfo.sinAlpha; + float yLab = y * rocInfo.cosAlpha + x * rocInfo.sinAlpha; v = (time - mT0 - vertexTime) * (mVdrift + mVdriftCorrY * yLab) + mLdriftCorr; // drift length cm } -GPUdi() void TPCFastTransform::convTimeToVinTimeFrame(int32_t slice, float time, float& v, float maxTimeBin) const +GPUdi() void TPCFastTransform::convTimeToVinTimeFrame(int32_t roc, float time, float& v, float maxTimeBin) const { v = (time - mT0 - maxTimeBin) * mVdrift + mLdriftCorr; // drift length cm - if (slice < getGeometry().getNumberOfSlicesA()) { + if (roc < getGeometry().getNumberOfRocsA()) { v += getGeometry().getTPCzLengthA(); } else { v += getGeometry().getTPCzLengthC(); } } -GPUdi() void TPCFastTransform::convPadTimeToUVinTimeFrame(int32_t slice, int32_t row, float pad, float time, float& u, float& v, float maxTimeBin) const +GPUdi() void TPCFastTransform::convPadTimeToUVinTimeFrame(int32_t roc, int32_t row, float pad, float time, float& u, float& v, float maxTimeBin) const { const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); u = (pad - 0.5f * rowInfo.maxPad) * rowInfo.padWidth; - convTimeToVinTimeFrame(slice, time, v, maxTimeBin); + convTimeToVinTimeFrame(roc, time, v, maxTimeBin); } -GPUdi() float TPCFastTransform::convZOffsetToVertexTime(int32_t slice, float zOffset, float maxTimeBin) const +GPUdi() float TPCFastTransform::convZOffsetToVertexTime(int32_t roc, float zOffset, float maxTimeBin) const { - if (slice < getGeometry().getNumberOfSlicesA()) { + if (roc < getGeometry().getNumberOfRocsA()) { return maxTimeBin - (getGeometry().getTPCzLengthA() + zOffset) / mVdrift; } else { return maxTimeBin - (getGeometry().getTPCzLengthC() - zOffset) / mVdrift; } } -GPUdi() float TPCFastTransform::convVertexTimeToZOffset(int32_t slice, float vertexTime, float maxTimeBin) const +GPUdi() float TPCFastTransform::convVertexTimeToZOffset(int32_t roc, float vertexTime, float maxTimeBin) const { - if (slice < getGeometry().getNumberOfSlicesA()) { + if (roc < getGeometry().getNumberOfRocsA()) { return (maxTimeBin - vertexTime) * mVdrift - getGeometry().getTPCzLengthA(); } else { return -((maxTimeBin - vertexTime) * mVdrift - getGeometry().getTPCzLengthC()); } } -GPUdi() void TPCFastTransform::convUVtoPadTime(int32_t slice, int32_t row, float u, float v, float& pad, float& time, float vertexTime) const +GPUdi() void TPCFastTransform::convUVtoPadTime(int32_t roc, int32_t row, float u, float v, float& pad, float& time, float vertexTime) const { - bool sideC = (slice >= getGeometry().getNumberOfSlicesA()); + bool sideC = (roc >= getGeometry().getNumberOfRocsA()); const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); - const TPCFastTransformGeo::SliceInfo& sliceInfo = getGeometry().getSliceInfo(slice); + const TPCFastTransformGeo::RocInfo& rocInfo = getGeometry().getRocInfo(roc); pad = u / rowInfo.padWidth + 0.5f * rowInfo.maxPad; float x = rowInfo.x; float y = sideC ? -u : u; // pads are mirrorred on C-side - float yLab = y * sliceInfo.cosAlpha + x * sliceInfo.sinAlpha; + float yLab = y * rocInfo.cosAlpha + x * rocInfo.sinAlpha; time = mT0 + vertexTime + (v - mLdriftCorr) / (mVdrift + mVdriftCorrY * yLab); } @@ -440,9 +440,9 @@ GPUdi() void TPCFastTransform::convVtoTime(float v, float& time, float vertexTim time = mT0 + vertexTime + (v - mLdriftCorr) / (mVdrift + mVdriftCorrY * yLab); } -GPUdi() void TPCFastTransform::convUVtoPadTimeInTimeFrame(int32_t slice, int32_t row, float u, float v, float& pad, float& time, float maxTimeBin) const +GPUdi() void TPCFastTransform::convUVtoPadTimeInTimeFrame(int32_t roc, int32_t row, float u, float v, float& pad, float& time, float maxTimeBin) const { - if (slice < getGeometry().getNumberOfSlicesA()) { + if (roc < getGeometry().getNumberOfRocsA()) { v -= getGeometry().getTPCzLengthA(); } else { v -= getGeometry().getTPCzLengthC(); @@ -452,17 +452,17 @@ GPUdi() void TPCFastTransform::convUVtoPadTimeInTimeFrame(int32_t slice, int32_t time = mT0 + maxTimeBin + (v - mLdriftCorr) / mVdrift; } -GPUdi() void TPCFastTransform::getTOFcorrection(int32_t slice, int32_t /*row*/, float x, float y, float z, float& dz) const +GPUdi() void TPCFastTransform::getTOFcorrection(int32_t roc, int32_t /*row*/, float x, float y, float z, float& dz) const { // calculate time of flight correction for z coordinate - bool sideC = (slice >= getGeometry().getNumberOfSlicesA()); + bool sideC = (roc >= getGeometry().getNumberOfRocsA()); float distZ = z - mPrimVtxZ; float dv = -GPUCommonMath::Sqrt(x * x + y * y + distZ * distZ) * mTOFcorr; dz = sideC ? dv : -dv; } -GPUdi() void TPCFastTransform::TransformInternal(int32_t slice, int32_t row, float& u, float& v, float& x, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const +GPUdi() void TPCFastTransform::TransformInternal(int32_t roc, int32_t row, float& u, float& v, float& x, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const { GPUCA_RTC_SPECIAL_CODE(ref2 = nullptr; scale2 = 0.f;); if (mApplyCorrection) { @@ -471,15 +471,15 @@ GPUdi() void TPCFastTransform::TransformInternal(int32_t slice, int32_t row, flo #ifndef GPUCA_GPUCODE if (mCorrectionSlow) { float ly, lz; - getGeometry().convUVtoLocal(slice, u, v, ly, lz); + getGeometry().convUVtoLocal(roc, u, v, ly, lz); float gx, gy, gz; - getGeometry().convLocalToGlobal(slice, x, ly, lz, gx, gy, gz); + getGeometry().convLocalToGlobal(roc, x, ly, lz, gx, gy, gz); float gdxC, gdyC, gdzC; - mCorrectionSlow->getCorrections(gx, gy, gz, slice, gdxC, gdyC, gdzC); - getGeometry().convGlobalToLocal(slice, gdxC, gdyC, gdzC, dx, du, dv); + mCorrectionSlow->getCorrections(gx, gy, gz, roc, gdxC, gdyC, gdzC); + getGeometry().convGlobalToLocal(roc, gdxC, gdyC, gdzC, dx, du, dv); - if (slice >= 18) { + if (roc >= 18) { du = -du; // mirror for c-Side } else { dv = -dv; // mirror z for A-Side @@ -487,17 +487,17 @@ GPUdi() void TPCFastTransform::TransformInternal(int32_t slice, int32_t row, flo } else #endif // GPUCA_GPUCODE { - mCorrection.getCorrection(slice, row, u, v, dx, du, dv); + mCorrection.getCorrection(roc, row, u, v, dx, du, dv); if (ref) { if ((scale > 0.f) && (scaleMode == 0)) { // scaling was requested float dxRef, duRef, dvRef; - ref->mCorrection.getCorrection(slice, row, u, v, dxRef, duRef, dvRef); + ref->mCorrection.getCorrection(roc, row, u, v, dxRef, duRef, dvRef); dx = (dx - dxRef) * scale + dxRef; du = (du - duRef) * scale + duRef; dv = (dv - dvRef) * scale + dvRef; } else if ((scale != 0.f) && ((scaleMode == 1) || (scaleMode == 2))) { float dxRef, duRef, dvRef; - ref->mCorrection.getCorrection(slice, row, u, v, dxRef, duRef, dvRef); + ref->mCorrection.getCorrection(roc, row, u, v, dxRef, duRef, dvRef); dx = dxRef * scale + dx; du = duRef * scale + du; dv = dvRef * scale + dv; @@ -505,7 +505,7 @@ GPUdi() void TPCFastTransform::TransformInternal(int32_t slice, int32_t row, flo } if (ref2 && (scale2 != 0)) { float dxRef, duRef, dvRef; - ref2->mCorrection.getCorrection(slice, row, u, v, dxRef, duRef, dvRef); + ref2->mCorrection.getCorrection(roc, row, u, v, dxRef, duRef, dvRef); dx = dxRef * scale2 + dx; du = duRef * scale2 + du; dv = dvRef * scale2 + dv; @@ -514,43 +514,43 @@ GPUdi() void TPCFastTransform::TransformInternal(int32_t slice, int32_t row, flo } GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamFastTransform)) { float ly, lz; - getGeometry().convUVtoLocal(slice, u, v, ly, lz); + getGeometry().convUVtoLocal(roc, u, v, ly, lz); float gx, gy, gz; - getGeometry().convLocalToGlobal(slice, x, ly, lz, gx, gy, gz); + getGeometry().convLocalToGlobal(roc, x, ly, lz, gx, gy, gz); float lyT, lzT; float uCorr = u + du; float vCorr = v + dv; float lxT = x + dx; - getGeometry().convUVtoLocal(slice, uCorr, vCorr, lyT, lzT); + getGeometry().convUVtoLocal(roc, uCorr, vCorr, lyT, lzT); float invYZtoXScaled; - InverseTransformYZtoX(slice, row, lyT, lzT, invYZtoXScaled, ref, ref2, scale, scale2, scaleMode); + InverseTransformYZtoX(roc, row, lyT, lzT, invYZtoXScaled, ref, ref2, scale, scale2, scaleMode); float invYZtoX; - InverseTransformYZtoX(slice, row, lyT, lzT, invYZtoX); + InverseTransformYZtoX(roc, row, lyT, lzT, invYZtoX); float YZtoNominalY; float YZtoNominalZ; - InverseTransformYZtoNominalYZ(slice, row, lyT, lzT, YZtoNominalY, YZtoNominalZ); + InverseTransformYZtoNominalYZ(roc, row, lyT, lzT, YZtoNominalY, YZtoNominalZ); float YZtoNominalYScaled; float YZtoNominalZScaled; - InverseTransformYZtoNominalYZ(slice, row, lyT, lzT, YZtoNominalYScaled, YZtoNominalZScaled, ref, ref2, scale, scale2, scaleMode); + InverseTransformYZtoNominalYZ(roc, row, lyT, lzT, YZtoNominalYScaled, YZtoNominalZScaled, ref, ref2, scale, scale2, scaleMode); float dxRef, duRef, dvRef; if (ref) { - ref->mCorrection.getCorrection(slice, row, u, v, dxRef, duRef, dvRef); + ref->mCorrection.getCorrection(roc, row, u, v, dxRef, duRef, dvRef); } float dxRef2, duRef2, dvRef2; if (ref2) { - ref2->mCorrection.getCorrection(slice, row, u, v, dxRef2, duRef2, dvRef2); + ref2->mCorrection.getCorrection(roc, row, u, v, dxRef2, duRef2, dvRef2); } float dxOrig, duOrig, dvOrig; - mCorrection.getCorrection(slice, row, u, v, dxOrig, duOrig, dvOrig); + mCorrection.getCorrection(roc, row, u, v, dxOrig, duOrig, dvOrig); o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_Transform").data() // corrections in x, u, v @@ -569,7 +569,7 @@ GPUdi() void TPCFastTransform::TransformInternal(int32_t slice, int32_t row, flo << "v=" << v << "u=" << u << "row=" << row - << "slice=" << slice + << "roc=" << roc << "scale=" << scale << "scale2=" << scale2 // original local coordinates @@ -601,51 +601,51 @@ GPUdi() void TPCFastTransform::TransformInternal(int32_t slice, int32_t row, flo } } -GPUdi() void TPCFastTransform::TransformXYZ(int32_t slice, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const +GPUdi() void TPCFastTransform::TransformXYZ(int32_t roc, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const { float u, v; - getGeometry().convLocalToUV(slice, y, z, u, v); - TransformInternal(slice, row, u, v, x, ref, ref2, scale, scale2, scaleMode); - getGeometry().convUVtoLocal(slice, u, v, y, z); + getGeometry().convLocalToUV(roc, y, z, u, v); + TransformInternal(roc, row, u, v, x, ref, ref2, scale, scale2, scaleMode); + getGeometry().convUVtoLocal(roc, u, v, y, z); float dzTOF = 0; - getTOFcorrection(slice, row, x, y, z, dzTOF); + getTOFcorrection(roc, row, x, y, z, dzTOF); z += dzTOF; } -GPUdi() void TPCFastTransform::Transform(int32_t slice, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const +GPUdi() void TPCFastTransform::Transform(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const { /// _______________ The main method: cluster transformation _______________________ /// - /// Transforms raw TPC coordinates to local XYZ withing a slice + /// Transforms raw TPC coordinates to local XYZ withing a roc /// taking calibration + alignment into account. /// const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); - // const SliceInfo &sliceInfo = getSliceInfo( slice ); - // bool sideC = ( slice >= NumberOfSlices / 2 ); + // const RocInfo &rocInfo = getRocInfo( roc ); + // bool sideC = ( roc >= NumberOfRocs / 2 ); x = rowInfo.x; float u = 0, v = 0; - convPadTimeToUV(slice, row, pad, time, u, v, vertexTime); + convPadTimeToUV(roc, row, pad, time, u, v, vertexTime); - TransformInternal(slice, row, u, v, x, ref, ref2, scale, scale2, scaleMode); + TransformInternal(roc, row, u, v, x, ref, ref2, scale, scale2, scaleMode); - getGeometry().convUVtoLocal(slice, u, v, y, z); + getGeometry().convUVtoLocal(roc, u, v, y, z); float dzTOF = 0; - getTOFcorrection(slice, row, x, y, z, dzTOF); + getTOFcorrection(roc, row, x, y, z, dzTOF); z += dzTOF; } -GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t slice, float time, float& z, float maxTimeBin) const +GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t roc, float time, float& z, float maxTimeBin) const { float v = 0; - convTimeToVinTimeFrame(slice, time, v, maxTimeBin); - getGeometry().convVtoLocal(slice, v, z); + convTimeToVinTimeFrame(roc, time, v, maxTimeBin); + getGeometry().convVtoLocal(roc, v, z); } -GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t slice, int32_t row, float pad, float time, float& x, float& y, float& z, float maxTimeBin) const +GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float maxTimeBin) const { /// _______________ Special cluster transformation for a time frame _______________________ /// @@ -656,43 +656,43 @@ GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t slice, int32_t row, const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); x = rowInfo.x; float u = 0, v = 0; - convPadTimeToUVinTimeFrame(slice, row, pad, time, u, v, maxTimeBin); - getGeometry().convUVtoLocal(slice, u, v, y, z); + convPadTimeToUVinTimeFrame(roc, row, pad, time, u, v, maxTimeBin); + getGeometry().convUVtoLocal(roc, u, v, y, z); } -GPUdi() void TPCFastTransform::InverseTransformInTimeFrame(int32_t slice, int32_t row, float /*x*/, float y, float z, float& pad, float& time, float maxTimeBin) const +GPUdi() void TPCFastTransform::InverseTransformInTimeFrame(int32_t roc, int32_t row, float /*x*/, float y, float z, float& pad, float& time, float maxTimeBin) const { /// Inverse transformation to TransformInTimeFrame float u = 0, v = 0; - getGeometry().convLocalToUV(slice, y, z, u, v); - convUVtoPadTimeInTimeFrame(slice, row, u, v, pad, time, maxTimeBin); + getGeometry().convLocalToUV(roc, y, z, u, v); + convUVtoPadTimeInTimeFrame(roc, row, u, v, pad, time, maxTimeBin); } -GPUdi() float TPCFastTransform::InverseTransformInTimeFrame(int32_t slice, float z, float maxTimeBin) const +GPUdi() float TPCFastTransform::InverseTransformInTimeFrame(int32_t roc, float z, float maxTimeBin) const { float pad, time; - InverseTransformInTimeFrame(slice, 0, 0, 0, z, pad, time, maxTimeBin); + InverseTransformInTimeFrame(roc, 0, 0, 0, z, pad, time, maxTimeBin); return time; } -GPUdi() void TPCFastTransform::TransformIdealZ(int32_t slice, float time, float& z, float vertexTime) const +GPUdi() void TPCFastTransform::TransformIdealZ(int32_t roc, float time, float& z, float vertexTime) const { /// _______________ The main method: cluster transformation _______________________ /// - /// Transforms time TPC coordinates to local Z withing a slice + /// Transforms time TPC coordinates to local Z withing a roc /// Ideal transformation: only Vdrift from DCS. /// No space charge corrections, no time of flight correction /// float v = (time - mT0 - vertexTime) * mVdrift; // drift length cm - getGeometry().convVtoLocal(slice, v, z); + getGeometry().convVtoLocal(roc, v, z); } -GPUdi() void TPCFastTransform::TransformIdeal(int32_t slice, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime) const +GPUdi() void TPCFastTransform::TransformIdeal(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime) const { /// _______________ The main method: cluster transformation _______________________ /// - /// Transforms raw TPC coordinates to local XYZ withing a slice + /// Transforms raw TPC coordinates to local XYZ withing a roc /// Ideal transformation: only Vdrift from DCS. /// No space charge corrections, no time of flight correction /// @@ -703,10 +703,10 @@ GPUdi() void TPCFastTransform::TransformIdeal(int32_t slice, int32_t row, float float u = (pad - 0.5f * rowInfo.maxPad) * rowInfo.padWidth; float v = (time - mT0 - vertexTime) * mVdrift; // drift length cm - getGeometry().convUVtoLocal(slice, u, v, y, z); + getGeometry().convUVtoLocal(roc, u, v, y, z); } -GPUdi() float TPCFastTransform::convTimeToZinTimeFrame(int32_t slice, float time, float maxTimeBin) const +GPUdi() float TPCFastTransform::convTimeToZinTimeFrame(int32_t roc, float time, float maxTimeBin) const { /// _______________ Special cluster transformation for a time frame _______________________ /// @@ -717,7 +717,7 @@ GPUdi() float TPCFastTransform::convTimeToZinTimeFrame(int32_t slice, float time float v = (time - mT0 - maxTimeBin) * mVdrift + mLdriftCorr; // drift length cm float z = getGeometry().getTPCalignmentZ(); // global TPC alignment - if (slice < getGeometry().getNumberOfSlicesA()) { + if (roc < getGeometry().getNumberOfRocsA()) { z -= v; } else { z += v; @@ -725,11 +725,11 @@ GPUdi() float TPCFastTransform::convTimeToZinTimeFrame(int32_t slice, float time return z; } -GPUdi() float TPCFastTransform::convZtoTimeInTimeFrame(int32_t slice, float z, float maxTimeBin) const +GPUdi() float TPCFastTransform::convZtoTimeInTimeFrame(int32_t roc, float z, float maxTimeBin) const { /// Inverse transformation of convTimeToZinTimeFrame() float v; - if (slice < getGeometry().getNumberOfSlicesA()) { + if (roc < getGeometry().getNumberOfRocsA()) { v = getGeometry().getTPCalignmentZ() - z; } else { v = z - getGeometry().getTPCalignmentZ(); @@ -737,10 +737,10 @@ GPUdi() float TPCFastTransform::convZtoTimeInTimeFrame(int32_t slice, float z, f return mT0 + maxTimeBin + (v - mLdriftCorr) / mVdrift; } -GPUdi() float TPCFastTransform::convDeltaTimeToDeltaZinTimeFrame(int32_t slice, float deltaTime) const +GPUdi() float TPCFastTransform::convDeltaTimeToDeltaZinTimeFrame(int32_t roc, float deltaTime) const { float deltaZ = deltaTime * mVdrift; - return slice < getGeometry().getNumberOfSlicesA() ? -deltaZ : deltaZ; + return roc < getGeometry().getNumberOfRocsA() ? -deltaZ : deltaZ; } GPUdi() float TPCFastTransform::convDeltaZtoDeltaTimeInTimeFrameAbs(float deltaZ) const @@ -748,80 +748,80 @@ GPUdi() float TPCFastTransform::convDeltaZtoDeltaTimeInTimeFrameAbs(float deltaZ return deltaZ / mVdrift; } -GPUdi() float TPCFastTransform::convDeltaZtoDeltaTimeInTimeFrame(int32_t slice, float deltaZ) const +GPUdi() float TPCFastTransform::convDeltaZtoDeltaTimeInTimeFrame(int32_t roc, float deltaZ) const { float deltaT = deltaZ / mVdrift; - return slice < getGeometry().getNumberOfSlicesA() ? -deltaT : deltaT; + return roc < getGeometry().getNumberOfRocsA() ? -deltaT : deltaT; } /* -GPUdi() float TPCFastTransform::getLastCalibratedTimeBin(int32_t slice) const +GPUdi() float TPCFastTransform::getLastCalibratedTimeBin(int32_t roc) const { /// Return a value of the last timebin where correction map is valid float u, v, pad, time; - getGeometry().convScaledUVtoUV(slice, 0, 0.f, 1.f, u, v); - convUVtoPadTime(slice, 0, u, v, pad, time, 0); + getGeometry().convScaledUVtoUV(roc, 0, 0.f, 1.f, u, v); + convUVtoPadTime(roc, 0, u, v, pad, time, 0); return time; } */ -GPUdi() float TPCFastTransform::getMaxDriftTime(int32_t slice, int32_t row, float pad) const +GPUdi() float TPCFastTransform::getMaxDriftTime(int32_t roc, int32_t row, float pad) const { /// maximal possible drift time of the active area - float maxL = mCorrection.getMaxDriftLength(slice, row, pad); + float maxL = mCorrection.getMaxDriftLength(roc, row, pad); - bool sideC = (slice >= getGeometry().getNumberOfSlicesA()); + bool sideC = (roc >= getGeometry().getNumberOfRocsA()); const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); - const TPCFastTransformGeo::SliceInfo& sliceInfo = getGeometry().getSliceInfo(slice); + const TPCFastTransformGeo::RocInfo& rocInfo = getGeometry().getRocInfo(roc); float x = rowInfo.x; float u = (pad - 0.5f * rowInfo.maxPad) * rowInfo.padWidth; float y = sideC ? -u : u; // pads are mirrorred on C-side - float yLab = y * sliceInfo.cosAlpha + x * sliceInfo.sinAlpha; + float yLab = y * rocInfo.cosAlpha + x * rocInfo.sinAlpha; return mT0 + (maxL - mLdriftCorr) / (mVdrift + mVdriftCorrY * yLab); } -GPUdi() float TPCFastTransform::getMaxDriftTime(int32_t slice, int32_t row) const +GPUdi() float TPCFastTransform::getMaxDriftTime(int32_t roc, int32_t row) const { /// maximal possible drift time of the active area - float maxL = mCorrection.getMaxDriftLength(slice, row); + float maxL = mCorrection.getMaxDriftLength(roc, row); float maxTime = 0.f; convVtoTime(maxL, maxTime, 0.f); return maxTime; } -GPUdi() float TPCFastTransform::getMaxDriftTime(int32_t slice) const +GPUdi() float TPCFastTransform::getMaxDriftTime(int32_t roc) const { /// maximal possible drift time of the active area - float maxL = mCorrection.getMaxDriftLength(slice); + float maxL = mCorrection.getMaxDriftLength(roc); float maxTime = 0.f; convVtoTime(maxL, maxTime, 0.f); return maxTime; } -GPUdi() void TPCFastTransform::InverseTransformYZtoX(int32_t slice, int32_t row, float y, float z, float& x, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const +GPUdi() void TPCFastTransform::InverseTransformYZtoX(int32_t roc, int32_t row, float y, float z, float& x, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const { GPUCA_RTC_SPECIAL_CODE(ref2 = nullptr; scale2 = 0.f;); /// Transformation y,z -> x float u = 0, v = 0; - getGeometry().convLocalToUV(slice, y, z, u, v); + getGeometry().convLocalToUV(roc, y, z, u, v); if ((scale >= 0.f) || (scaleMode == 1) || (scaleMode == 2)) { - mCorrection.getCorrectionInvCorrectedX(slice, row, u, v, x); + mCorrection.getCorrectionInvCorrectedX(roc, row, u, v, x); if (ref) { // scaling was requested if (scaleMode == 0 && scale > 0.f) { float xr; - ref->mCorrection.getCorrectionInvCorrectedX(slice, row, u, v, xr); + ref->mCorrection.getCorrectionInvCorrectedX(roc, row, u, v, xr); x = (x - xr) * scale + xr; } else if ((scale != 0) && ((scaleMode == 1) || (scaleMode == 2))) { float xr; - ref->mCorrection.getCorrectionInvCorrectedX(slice, row, u, v, xr); + ref->mCorrection.getCorrectionInvCorrectedX(roc, row, u, v, xr); x = (xr - getGeometry().getRowInfo(row).x) * scale + x; // xr=mGeo.getRowInfo(row).x + dx; } } if (ref2 && (scale2 != 0)) { float xr; - ref2->mCorrection.getCorrectionInvCorrectedX(slice, row, u, v, xr); + ref2->mCorrection.getCorrectionInvCorrectedX(roc, row, u, v, xr); x = (xr - getGeometry().getRowInfo(row).x) * scale2 + x; // xr=mGeo.getRowInfo(row).x + dx; } } else { @@ -829,7 +829,7 @@ GPUdi() void TPCFastTransform::InverseTransformYZtoX(int32_t slice, int32_t row, } GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamFastTransform)) { o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_InverseTransformYZtoX").data() - << "slice=" << slice + << "roc=" << roc << "row=" << row << "scale=" << scale << "y=" << y @@ -841,29 +841,29 @@ GPUdi() void TPCFastTransform::InverseTransformYZtoX(int32_t slice, int32_t row, }) } -GPUdi() void TPCFastTransform::InverseTransformYZtoNominalYZ(int32_t slice, int32_t row, float y, float z, float& ny, float& nz, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const +GPUdi() void TPCFastTransform::InverseTransformYZtoNominalYZ(int32_t roc, int32_t row, float y, float z, float& ny, float& nz, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const { GPUCA_RTC_SPECIAL_CODE(ref2 = nullptr; scale2 = 0.f;); /// Transformation y,z -> x float u = 0, v = 0, un = 0, vn = 0; - getGeometry().convLocalToUV(slice, y, z, u, v); + getGeometry().convLocalToUV(roc, y, z, u, v); if ((scale >= 0.f) || (scaleMode == 1) || (scaleMode == 2)) { - mCorrection.getCorrectionInvUV(slice, row, u, v, un, vn); + mCorrection.getCorrectionInvUV(roc, row, u, v, un, vn); if (ref) { // scaling was requested if (scaleMode == 0 && scale > 0.f) { float unr = 0, vnr = 0; - ref->mCorrection.getCorrectionInvUV(slice, row, u, v, unr, vnr); + ref->mCorrection.getCorrectionInvUV(roc, row, u, v, unr, vnr); un = (un - unr) * scale + unr; vn = (vn - vnr) * scale + vnr; } else if ((scale != 0) && ((scaleMode == 1) || (scaleMode == 2))) { float unr = 0, vnr = 0; - ref->mCorrection.getCorrectionInvUV(slice, row, u, v, unr, vnr); + ref->mCorrection.getCorrectionInvUV(roc, row, u, v, unr, vnr); un = (unr - u) * scale + un; // unr = u - duv[0]; vn = (vnr - v) * scale + vn; } if (ref2 && (scale2 != 0)) { float unr = 0, vnr = 0; - ref2->mCorrection.getCorrectionInvUV(slice, row, u, v, unr, vnr); + ref2->mCorrection.getCorrectionInvUV(roc, row, u, v, unr, vnr); un = (unr - u) * scale2 + un; // unr = u - duv[0]; vn = (vnr - v) * scale2 + vn; } @@ -872,11 +872,11 @@ GPUdi() void TPCFastTransform::InverseTransformYZtoNominalYZ(int32_t slice, int3 un = u; vn = v; } - getGeometry().convUVtoLocal(slice, un, vn, ny, nz); + getGeometry().convUVtoLocal(roc, un, vn, ny, nz); GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamFastTransform)) { o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_InverseTransformYZtoNominalYZ").data() - << "slice=" << slice + << "roc=" << roc << "row=" << row << "scale=" << scale << "y=" << y @@ -891,7 +891,7 @@ GPUdi() void TPCFastTransform::InverseTransformYZtoNominalYZ(int32_t slice, int3 }) } -GPUdi() void TPCFastTransform::InverseTransformXYZtoNominalXYZ(int32_t slice, int32_t row, float x, float y, float z, float& nx, float& ny, float& nz, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const +GPUdi() void TPCFastTransform::InverseTransformXYZtoNominalXYZ(int32_t roc, int32_t row, float x, float y, float z, float& nx, float& ny, float& nz, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const { /// Inverse transformation: Transformed X, Y and Z -> X, Y and Z, transformed w/o space charge correction int32_t row2 = row + 1; @@ -902,8 +902,8 @@ GPUdi() void TPCFastTransform::InverseTransformXYZtoNominalXYZ(int32_t slice, in float nx2, ny2, nz2; // nominal coordinates for row2 nx1 = getGeometry().getRowInfo(row).x; nx2 = getGeometry().getRowInfo(row2).x; - InverseTransformYZtoNominalYZ(slice, row, y, z, ny1, nz1, ref, ref2, scale, scale2, scaleMode); - InverseTransformYZtoNominalYZ(slice, row2, y, z, ny2, nz2, ref, ref2, scale, scale2, scaleMode); + InverseTransformYZtoNominalYZ(roc, row, y, z, ny1, nz1, ref, ref2, scale, scale2, scaleMode); + InverseTransformYZtoNominalYZ(roc, row2, y, z, ny2, nz2, ref, ref2, scale, scale2, scaleMode); float c1 = (nx2 - nx) / (nx2 - nx1); float c2 = (nx - nx1) / (nx2 - nx1); nx = x; diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx b/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx index 3c624b3222d77..b472868fa1071 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx @@ -28,14 +28,14 @@ using namespace o2::gpu; TPCFastTransformGeo::TPCFastTransformGeo() { // Default Constructor: creates an empty uninitialized object - double dAlpha = 2. * M_PI / (NumberOfSlicesA); - for (int32_t i = 0; i < NumberOfSlices; i++) { - SliceInfo& s = mSliceInfos[i]; + double dAlpha = 2. * M_PI / (NumberOfRocsA); + for (int32_t i = 0; i < NumberOfRocs; i++) { + RocInfo& s = mRocInfos[i]; double alpha = dAlpha * (i + 0.5); s.sinAlpha = sin(alpha); s.cosAlpha = cos(alpha); } - mSliceInfos[NumberOfSlices] = SliceInfo{0.f, 0.f}; + mRocInfos[NumberOfRocs] = RocInfo{0.f, 0.f}; for (int32_t i = 0; i < MaxNumberOfRows + 1; i++) { mRowInfos[i] = RowInfo{0.f, -1, 0.f, 0.f, 0.f, 0.f}; @@ -104,7 +104,7 @@ void TPCFastTransformGeo::setTPCrow(int32_t iRow, float x, int32_t nPads, float // Make scaled U = area between the geometrical sector borders - const double sectorAngle = 2. * M_PI / NumberOfSlicesA; + const double sectorAngle = 2. * M_PI / NumberOfRocsA; const double scaleXtoRowWidth = 2. * tan(0.5 * sectorAngle); double uWidth = x * scaleXtoRowWidth; // distance to the sector border @@ -148,7 +148,7 @@ void TPCFastTransformGeo::print() const #endif } -int32_t TPCFastTransformGeo::test(int32_t slice, int32_t row, float ly, float lz) const +int32_t TPCFastTransformGeo::test(int32_t roc, int32_t row, float ly, float lz) const { /// Check consistency of the class @@ -164,16 +164,16 @@ int32_t TPCFastTransformGeo::test(int32_t slice, int32_t row, float ly, float lz float lx1 = 0.f, ly1 = 0.f, lz1 = 0.f; float gx = 0.f, gy = 0.f, gz = 0.f; - convLocalToGlobal(slice, lx, ly, lz, gx, gy, gz); - convGlobalToLocal(slice, gx, gy, gz, lx1, ly1, lz1); + convLocalToGlobal(roc, lx, ly, lz, gx, gy, gz); + convGlobalToLocal(roc, gx, gy, gz, lx1, ly1, lz1); if (fabs(lx1 - lx) > 1.e-4 || fabs(ly1 - ly) > 1.e-4 || fabs(lz1 - lz) > 1.e-7) { LOG(info) << "Error local <-> global: x " << lx << " dx " << lx1 - lx << " y " << ly << " dy " << ly1 - ly << " z " << lz << " dz " << lz1 - lz; error = -3; } float u = 0.f, v = 0.f; - convLocalToUV(slice, ly, lz, u, v); - convUVtoLocal(slice, u, v, ly1, lz1); + convLocalToUV(roc, ly, lz, u, v); + convUVtoLocal(roc, u, v, ly1, lz1); if (fabs(ly1 - ly) + fabs(lz1 - lz) > 1.e-6) { LOG(info) << "Error local <-> UV: y " << ly << " dy " << ly1 - ly << " z " << lz << " dz " << lz1 - lz; @@ -182,7 +182,7 @@ int32_t TPCFastTransformGeo::test(int32_t slice, int32_t row, float ly, float lz float su = 0.f, sv = 0.f; - convUVtoScaledUV(slice, row, u, v, su, sv); + convUVtoScaledUV(roc, row, u, v, su, sv); if (su < 0.f || su > 1.f) { LOG(info) << "Error scaled U range: u " << u << " su " << su; @@ -190,7 +190,7 @@ int32_t TPCFastTransformGeo::test(int32_t slice, int32_t row, float ly, float lz } float u1 = 0.f, v1 = 0.f; - convScaledUVtoUV(slice, row, su, sv, u1, v1); + convScaledUVtoUV(roc, row, su, sv, u1, v1); if (fabs(u1 - u) > 1.e-4 || fabs(v1 - v) > 1.e-4) { LOG(info) << "Error UV<->scaled UV: u " << u << " du " << u1 - u << " v " << v << " dv " << v1 - v; diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.h b/GPU/TPCFastTransformation/TPCFastTransformGeo.h index d20331ba6ab0f..3382d1d926ce2 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.h +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.h @@ -34,11 +34,11 @@ namespace gpu class TPCFastTransformGeo { public: - /// The struct contains necessary info for TPC slice - struct SliceInfo { + /// The struct contains necessary info for TPC ROC + struct RocInfo { float sinAlpha; float cosAlpha; - ClassDefNV(SliceInfo, 1); + ClassDefNV(RocInfo, 1); }; /// The struct contains necessary info about TPC padrow @@ -58,6 +58,7 @@ class TPCFastTransformGeo /// get width in U GPUd() float getUwidth() const { return -2.f * u0; } + ClassDefNV(RowInfo, 1); }; @@ -107,11 +108,11 @@ class TPCFastTransformGeo /// _______________ Getters _________________________________ - /// Gives number of TPC slices - GPUd() static constexpr int32_t getNumberOfSlices() { return NumberOfSlices; } + /// Gives number of TPC ROCs + GPUd() static constexpr int32_t getNumberOfRocs() { return NumberOfRocs; } - /// Gives number of TPC slices in A side - GPUd() static constexpr int32_t getNumberOfSlicesA() { return NumberOfSlicesA; } + /// Gives number of TPC ROCs on the A side + GPUd() static constexpr int32_t getNumberOfRocsA() { return NumberOfRocsA; } /// Gives number of TPC rows GPUd() int32_t getNumberOfRows() const { return mNumberOfRows; } @@ -119,8 +120,8 @@ class TPCFastTransformGeo /// Gives number of TPC rows GPUd() static constexpr int getMaxNumberOfRows() { return MaxNumberOfRows; } - /// Gives slice info - GPUd() const SliceInfo& getSliceInfo(int32_t slice) const; + /// Gives roc info + GPUd() const RocInfo& getRocInfo(int32_t roc) const; /// Gives TPC row info GPUd() const RowInfo& getRowInfo(int32_t row) const; @@ -131,11 +132,11 @@ class TPCFastTransformGeo /// Gives Z length of the TPC, side C GPUd() float getTPCzLengthC() const { return mTPCzLengthC; } - /// Gives Z length of the TPC, depending on the slice - GPUd() float getTPCzLength(int32_t slice) const + /// Gives Z length of the TPC, depending on the roc + GPUd() float getTPCzLength(int32_t roc) const { - return (slice < NumberOfSlicesA) ? mTPCzLengthA - : mTPCzLengthC; + return (roc < NumberOfRocsA) ? mTPCzLengthA + : mTPCzLengthC; } /// Gives TPC alignment in Z @@ -144,26 +145,26 @@ class TPCFastTransformGeo /// _______________ Conversion of coordinate systems __________ /// convert Local -> Global c.s. - GPUd() void convLocalToGlobal(int32_t slice, float lx, float ly, float lz, float& gx, float& gy, float& gz) const; + GPUd() void convLocalToGlobal(int32_t roc, float lx, float ly, float lz, float& gx, float& gy, float& gz) const; /// convert Global->Local c.s. - GPUd() void convGlobalToLocal(int32_t slice, float gx, float gy, float gz, float& lx, float& ly, float& lz) const; + GPUd() void convGlobalToLocal(int32_t roc, float gx, float gy, float gz, float& lx, float& ly, float& lz) const; /// convert UV -> Local c.s. - GPUd() void convUVtoLocal(int32_t slice, float u, float v, float& y, float& z) const; - GPUd() void convVtoLocal(int32_t slice, float v, float& z) const; + GPUd() void convUVtoLocal(int32_t roc, float u, float v, float& y, float& z) const; + GPUd() void convVtoLocal(int32_t roc, float v, float& z) const; /// convert Local-> UV c.s. - GPUd() void convLocalToUV(int32_t slice, float y, float z, float& u, float& v) const; + GPUd() void convLocalToUV(int32_t roc, float y, float z, float& u, float& v) const; /// convert UV -> Scaled UV - GPUd() void convUVtoScaledUV(int32_t slice, int32_t row, float u, float v, float& su, float& sv) const; + GPUd() void convUVtoScaledUV(int32_t roc, int32_t row, float u, float v, float& su, float& sv) const; /// convert Scaled UV -> UV - GPUd() void convScaledUVtoUV(int32_t slice, int32_t row, float su, float sv, float& u, float& v) const; + GPUd() void convScaledUVtoUV(int32_t roc, int32_t row, float su, float sv, float& u, float& v) const; /// convert Scaled UV -> Local c.s. - GPUd() void convScaledUVtoLocal(int32_t slice, int32_t row, float su, float sv, float& ly, float& lz) const; + GPUd() void convScaledUVtoLocal(int32_t roc, int32_t row, float su, float sv, float& ly, float& lz) const; /// convert Pad coordinate -> U GPUd() float convPadToU(int32_t row, float pad) const; @@ -175,7 +176,7 @@ class TPCFastTransformGeo void print() const; /// Method for testing consistency - int32_t test(int32_t slice, int32_t row, float ly, float lz) const; + int32_t test(int32_t roc, int32_t row, float ly, float lz) const; /// Method for testing consistency int32_t test() const; @@ -183,9 +184,9 @@ class TPCFastTransformGeo private: /// _______________ Data members _______________________________________________ - static constexpr int32_t NumberOfSlices = 36; ///< Number of TPC slices ( slice = inner + outer sector ) - static constexpr int32_t NumberOfSlicesA = NumberOfSlices / 2; ///< Number of TPC slices side A - static constexpr int32_t MaxNumberOfRows = 160; ///< Max Number of TPC rows in a slice + static constexpr int32_t NumberOfRocs = 36; ///< Number of TPC rocs ( roc = inner + outer sector ) + static constexpr int32_t NumberOfRocsA = NumberOfRocs / 2; ///< Number of TPC rocs side A + static constexpr int32_t MaxNumberOfRows = 160; ///< Max Number of TPC rows in a roc /// _______________ Construction control _______________________________________________ @@ -211,23 +212,23 @@ class TPCFastTransformGeo float mScaleSVtoVsideA = 0.f; ///< scale for sv->v for TPC side A float mScaleSVtoVsideC = 0.f; ///< scale for sv->v for TPC side C - SliceInfo mSliceInfos[NumberOfSlices + 1]; ///< array of slice information [fixed size] + RocInfo mRocInfos[NumberOfRocs + 1]; ///< array of roc information [fixed size] RowInfo mRowInfos[MaxNumberOfRows + 1]; ///< array of row information [fixed size] - ClassDefNV(TPCFastTransformGeo, 1); + ClassDefNV(TPCFastTransformGeo, 2); }; // ======================================================================= // Inline implementations of some methods // ======================================================================= -GPUdi() const TPCFastTransformGeo::SliceInfo& TPCFastTransformGeo::getSliceInfo(int32_t slice) const +GPUdi() const TPCFastTransformGeo::RocInfo& TPCFastTransformGeo::getRocInfo(int32_t roc) const { - /// Gives slice info - if (slice < 0 || slice >= NumberOfSlices) { // return zero object - slice = NumberOfSlices; + /// Gives roc info + if (roc < 0 || roc >= NumberOfRocs) { // return zero object + roc = NumberOfRocs; } - return mSliceInfos[slice]; + return mRocInfos[roc]; } GPUdi() const TPCFastTransformGeo::RowInfo& TPCFastTransformGeo::getRowInfo(int32_t row) const @@ -239,28 +240,28 @@ GPUdi() const TPCFastTransformGeo::RowInfo& TPCFastTransformGeo::getRowInfo(int3 return mRowInfos[row]; } -GPUdi() void TPCFastTransformGeo::convLocalToGlobal(int32_t slice, float lx, float ly, float lz, float& gx, float& gy, float& gz) const +GPUdi() void TPCFastTransformGeo::convLocalToGlobal(int32_t roc, float lx, float ly, float lz, float& gx, float& gy, float& gz) const { /// convert Local -> Global c.s. - const SliceInfo& sliceInfo = getSliceInfo(slice); - gx = lx * sliceInfo.cosAlpha - ly * sliceInfo.sinAlpha; - gy = lx * sliceInfo.sinAlpha + ly * sliceInfo.cosAlpha; + const RocInfo& rocInfo = getRocInfo(roc); + gx = lx * rocInfo.cosAlpha - ly * rocInfo.sinAlpha; + gy = lx * rocInfo.sinAlpha + ly * rocInfo.cosAlpha; gz = lz; } -GPUdi() void TPCFastTransformGeo::convGlobalToLocal(int32_t slice, float gx, float gy, float gz, float& lx, float& ly, float& lz) const +GPUdi() void TPCFastTransformGeo::convGlobalToLocal(int32_t roc, float gx, float gy, float gz, float& lx, float& ly, float& lz) const { /// convert Global -> Local c.s. - const SliceInfo& sliceInfo = getSliceInfo(slice); - lx = gx * sliceInfo.cosAlpha + gy * sliceInfo.sinAlpha; - ly = -gx * sliceInfo.sinAlpha + gy * sliceInfo.cosAlpha; + const RocInfo& rocInfo = getRocInfo(roc); + lx = gx * rocInfo.cosAlpha + gy * rocInfo.sinAlpha; + ly = -gx * rocInfo.sinAlpha + gy * rocInfo.cosAlpha; lz = gz; } -GPUdi() void TPCFastTransformGeo::convVtoLocal(int32_t slice, float v, float& lz) const +GPUdi() void TPCFastTransformGeo::convVtoLocal(int32_t roc, float v, float& lz) const { /// convert UV -> Local c.s. - if (slice < NumberOfSlicesA) { // TPC side A + if (roc < NumberOfRocsA) { // TPC side A lz = mTPCzLengthA - v; } else { // TPC side C lz = v - mTPCzLengthC; // drift direction is mirrored on C-side @@ -268,10 +269,10 @@ GPUdi() void TPCFastTransformGeo::convVtoLocal(int32_t slice, float v, float& lz lz += mTPCalignmentZ; // global TPC alignment } -GPUdi() void TPCFastTransformGeo::convUVtoLocal(int32_t slice, float u, float v, float& ly, float& lz) const +GPUdi() void TPCFastTransformGeo::convUVtoLocal(int32_t roc, float u, float v, float& ly, float& lz) const { /// convert UV -> Local c.s. - if (slice < NumberOfSlicesA) { // TPC side A + if (roc < NumberOfRocsA) { // TPC side A ly = u; lz = mTPCzLengthA - v; } else { // TPC side C @@ -281,11 +282,11 @@ GPUdi() void TPCFastTransformGeo::convUVtoLocal(int32_t slice, float u, float v, lz += mTPCalignmentZ; // global TPC alignment } -GPUdi() void TPCFastTransformGeo::convLocalToUV(int32_t slice, float ly, float lz, float& u, float& v) const +GPUdi() void TPCFastTransformGeo::convLocalToUV(int32_t roc, float ly, float lz, float& u, float& v) const { /// convert Local-> UV c.s. lz = lz - mTPCalignmentZ; // global TPC alignment - if (slice < NumberOfSlicesA) { // TPC side A + if (roc < NumberOfRocsA) { // TPC side A u = ly; v = mTPCzLengthA - lz; } else { // TPC side C @@ -294,36 +295,36 @@ GPUdi() void TPCFastTransformGeo::convLocalToUV(int32_t slice, float ly, float l } } -GPUdi() void TPCFastTransformGeo::convUVtoScaledUV(int32_t slice, int32_t row, float u, float v, float& su, float& sv) const +GPUdi() void TPCFastTransformGeo::convUVtoScaledUV(int32_t roc, int32_t row, float u, float v, float& su, float& sv) const { /// convert UV -> Scaled UV const RowInfo& rowInfo = getRowInfo(row); su = (u - rowInfo.u0) * rowInfo.scaleUtoSU; - if (slice < NumberOfSlicesA) { + if (roc < NumberOfRocsA) { sv = v * mScaleVtoSVsideA; } else { sv = v * mScaleVtoSVsideC; } } -GPUdi() void TPCFastTransformGeo::convScaledUVtoUV(int32_t slice, int32_t row, float su, float sv, float& u, float& v) const +GPUdi() void TPCFastTransformGeo::convScaledUVtoUV(int32_t roc, int32_t row, float su, float sv, float& u, float& v) const { /// convert Scaled UV -> UV const RowInfo& rowInfo = getRowInfo(row); u = rowInfo.u0 + su * rowInfo.scaleSUtoU; - if (slice < NumberOfSlicesA) { + if (roc < NumberOfRocsA) { v = sv * mScaleSVtoVsideA; } else { v = sv * mScaleSVtoVsideC; } } -GPUdi() void TPCFastTransformGeo::convScaledUVtoLocal(int32_t slice, int32_t row, float su, float sv, float& ly, float& lz) const +GPUdi() void TPCFastTransformGeo::convScaledUVtoLocal(int32_t roc, int32_t row, float su, float sv, float& ly, float& lz) const { /// convert Scaled UV -> Local c.s. float u, v; - convScaledUVtoUV(slice, row, su, sv, u, v); - convUVtoLocal(slice, u, v, ly, lz); + convScaledUVtoUV(roc, row, su, sv, u, v); + convUVtoLocal(roc, u, v, ly, lz); } GPUdi() float TPCFastTransformGeo::convPadToU(int32_t row, float pad) const diff --git a/GPU/TPCFastTransformation/TPCFastTransformManager.cxx b/GPU/TPCFastTransformation/TPCFastTransformManager.cxx index 7d0aa29545578..aa28b6a414876 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformManager.cxx +++ b/GPU/TPCFastTransformation/TPCFastTransformManager.cxx @@ -87,14 +87,14 @@ int32_t TPCFastTransformManager::create(TPCFastTransform& fastTransform, float tpcZlengthSideA = tpcParam->GetZLength(0); float tpcZlengthSideC = - tpcParam->GetZLength(TPCFastTransformGeo::getNumberOfSlices() / 2); + tpcParam->GetZLength(TPCFastTransformGeo::getNumberOfRocs() / 2); geo.setTPCzLength(tpcZlengthSideA, tpcZlengthSideC); geo.setTPCalignmentZ(-mOrigTransform->GetDeltaZCorrTime()); for (int32_t row = 0; row < geo.getNumberOfRows(); row++) { - int32_t slice = 0, sector = 0, secrow = 0; - AliHLTTPCGeometry::Slice2Sector(slice, row, sector, secrow); + int32_t roc = 0, sector = 0, secrow = 0; + AliHLTTPCGeometry::Slice2Sector(roc, row, sector, secrow); Int_t nPads = tpcParam->GetNPads(sector, secrow); float xRow = tpcParam->GetPadRowRadii(sector, secrow); float padWidth = tpcParam->GetInnerPadPitchWidth(); @@ -272,40 +272,40 @@ int32_t TPCFastTransformManager::updateCalibration(TPCFastTransform& fastTransfo recoParam->SetUseTOFCorrection(kFALSE); - for (int32_t slice = 0; slice < geo.getNumberOfSlices(); slice++) { + for (int32_t roc = 0; roc < geo.getNumberOfRocs(); roc++) { for (int32_t row = 0; row < geo.getNumberOfRows(); row++) { const TPCFastTransformGeo::RowInfo& rowInfo = geo.getRowInfo(row); - const TPCFastSpaceChargeCorrection::SplineType& spline = correction.getSpline(slice, row); - float* data = correction.getSplineData(slice, row); + const TPCFastSpaceChargeCorrection::SplineType& spline = correction.getSpline(roc, row); + float* data = correction.getSplineData(roc, row); Spline2DHelper helper; helper.setSpline(spline, 4, 4); auto F = [&](double su, double sv, double dxuv[3]) { float x = rowInfo.x; - // x, u, v cordinates of the knot (local cartesian coord. of slice + // x, u, v cordinates of the knot (local cartesian coord. of roc // towards central electrode ) float u = 0, v = 0; - geo.convScaledUVtoUV(slice, row, su, sv, u, v); + geo.convScaledUVtoUV(roc, row, su, sv, u, v); // row, pad, time coordinates of the knot float vertexTime = 0.f; float pad = 0.f, time = 0.f; - fastTransform.convUVtoPadTime(slice, row, u, v, pad, time, vertexTime); + fastTransform.convUVtoPadTime(roc, row, u, v, pad, time, vertexTime); // nominal x,y,z coordinates of the knot (without corrections and // time-of-flight correction) float y = 0, z = 0; - geo.convUVtoLocal(slice, u, v, y, z); + geo.convUVtoLocal(roc, u, v, y, z); // original TPC transformation (row,pad,time) -> (x,y,z) without // time-of-flight correction float ox = 0, oy = 0, oz = 0; { int32_t sector = 0, secrow = 0; - AliHLTTPCGeometry::Slice2Sector(slice, row, sector, secrow); + AliHLTTPCGeometry::Slice2Sector(roc, row, sector, secrow); int32_t is[] = {sector}; double xx[] = {static_cast(secrow), pad, time}; mOrigTransform->Transform(xx, is, 0, 1); @@ -315,7 +315,7 @@ int32_t TPCFastTransformManager::updateCalibration(TPCFastTransform& fastTransfo } // convert to u,v float ou = 0, ov = 0; - geo.convLocalToUV(slice, oy, oz, ou, ov); + geo.convLocalToUV(roc, oy, oz, ou, ov); // corrections in x,u,v: dxuv[0] = ox - x; @@ -325,7 +325,7 @@ int32_t TPCFastTransformManager::updateCalibration(TPCFastTransform& fastTransfo helper.approximateFunction(data, 0., 1., 0., 1., F); } // row - } // slice + } // roc // set back the time-of-flight correction; diff --git a/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h b/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h index 4421d44aab0c8..7c1ae8fd56800 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h +++ b/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h @@ -59,7 +59,7 @@ #pragma link C++ class o2::gpu::IrregularSpline2D3DCalibrator + ; #pragma link C++ class o2::gpu::TPCFastTransformGeo + ; -#pragma link C++ class o2::gpu::TPCFastTransformGeo::SliceInfo + ; +#pragma link C++ class o2::gpu::TPCFastTransformGeo::RocInfo + ; #pragma link C++ class o2::gpu::TPCFastTransformGeo::RowInfo + ; #pragma link C++ class o2::gpu::TPCFastTransform + ; @@ -68,9 +68,9 @@ #pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::RowInfo + ; #pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection + ; -#pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::SliceInfo + ; +#pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::RocInfo + ; #pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::RowActiveArea + ; -#pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::SliceRowInfo + ; +#pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::RocRowInfo + ; #pragma link C++ class o2::gpu::CorrectionMapsHelper + ; #pragma link C++ struct o2::gpu::MultivariatePolynomialContainer + ; diff --git a/GPU/TPCFastTransformation/macro/generateTPCCorrectionNTuple.C b/GPU/TPCFastTransformation/macro/generateTPCCorrectionNTuple.C index 67a0f09522f60..69b7909cda683 100644 --- a/GPU/TPCFastTransformation/macro/generateTPCCorrectionNTuple.C +++ b/GPU/TPCFastTransformation/macro/generateTPCCorrectionNTuple.C @@ -82,12 +82,12 @@ void generateTPCCorrectionNTuple(const char* path = "InputSCDensityHistograms.ro const o2::gpu::TPCFastTransformGeo& geo = fastTransform->getGeometry(); TFile* f = new TFile("tpcCorrection.root", "RECREATE"); - TNtuple* nt = new TNtuple("dist", "dist", "slice:row:su:sv:dx:du:dv"); + TNtuple* nt = new TNtuple("dist", "dist", "sector:row:su:sv:dx:du:dv"); - int32_t nSlices = 1; // fastTransform->getNumberOfSlices(); - // for( int32_t slice=0; slicegetNumberOfSectors(); + // for( int32_t sector=0; sectorFill(slice, row, su, sv, dx, du, dv); + std::cout << sector << " " << row << " " << su << " " << sv << " " << dx << " " << du << " " << dv << std::endl; + nt->Fill(sector, row, su, sv, dx, du, dv); } } } From 45105a91fc28e811998b96ce64a6ebbe581b83f8 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Wed, 5 Feb 2025 16:19:23 +0000 Subject: [PATCH 506/701] TPC Splines: minimise the amount of transformations --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 20 +- .../src/TPCFastTransformHelperO2.cxx | 25 +- .../test/testTPCFastTransform.cxx | 6 +- .../TPCFastSpaceChargeCorrection.cxx | 16 +- .../TPCFastSpaceChargeCorrection.h | 52 ++- .../TPCFastTransform.cxx | 16 +- GPU/TPCFastTransformation/TPCFastTransform.h | 413 +++++++----------- .../TPCFastTransformGeo.cxx | 71 +-- .../TPCFastTransformGeo.h | 116 +---- .../TPCFastTransformManager.cxx | 1 + 10 files changed, 262 insertions(+), 474 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index bac332a837c55..710a4356dd457 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -63,12 +63,8 @@ void TPCFastSpaceChargeCorrectionHelper::initGeometry() mGeo.startConstruction(nRows); auto& detParam = ParameterDetector::Instance(); - float tpcZlengthSideA = detParam.TPClength; - float tpcZlengthSideC = detParam.TPClength; - mGeo.setTPCzLength(tpcZlengthSideA, tpcZlengthSideC); - - mGeo.setTPCalignmentZ(0.); + mGeo.setTPCzLength(detParam.TPClength); for (int iRow = 0; iRow < mGeo.getNumberOfRows(); iRow++) { Sector sector = 0; @@ -295,7 +291,7 @@ std::unique_ptr TPCFastSpaceChargeCorrectionHelper auto myThread = [&](int iThread) { for (int iRow = iThread; iRow < nRows; iRow += mNthreads) { const auto& info = mGeo.getRowInfo(iRow); - double vMax = mGeo.getTPCzLength(iRoc); + double vMax = mGeo.getTPCzLength(); double dv = vMax / (6. * (nKnotsZ - 1)); double dpad = info.maxPad / (6. * (nKnotsY - 1)); @@ -512,8 +508,8 @@ std::unique_ptr TPCFastSpaceChargeCorrect double zMax = rowInfo.x * trackResiduals.getZ2X(trackResiduals.getNZ2XBins() - 1); double uMin = yMin; double uMax = yMax; - double vMin = geo.getTPCzLength(iRoc) - zMax; - double vMax = geo.getTPCzLength(iRoc) - zMin; + double vMin = geo.getTPCzLength() - zMax; + double vMax = geo.getTPCzLength() - zMin; info.gridU0 = uMin; info.scaleUtoGrid = spline.getGridX1().getUmax() / (uMax - uMin); info.gridV0 = vMin; @@ -822,7 +818,7 @@ void TPCFastSpaceChargeCorrectionHelper::initMaxDriftLength(o2::gpu::TPCFastSpac if (prn) { LOG(info) << "init MaxDriftLength for roc " << roc; } - double vLength = (roc < mGeo.getNumberOfRocsA()) ? mGeo.getTPCzLengthA() : mGeo.getTPCzLengthC(); + double vLength = mGeo.getTPCzLength(); TPCFastSpaceChargeCorrection::RocInfo& rocInfo = correction.getRocInfo(roc); rocInfo.vMax = 0.f; @@ -843,7 +839,7 @@ void TPCFastSpaceChargeCorrectionHelper::initMaxDriftLength(o2::gpu::TPCFastSpac while (v1 - v0 > 0.1) { float v = 0.5 * (v0 + v1); float dx, du, dv; - correction.getCorrection(roc, row, u, v, dx, du, dv); + correction.getCorrectionInternal(roc, row, u, v, dx, du, dv); double cx = x + dx; double cu = u + du; double cv = v + dv; @@ -962,14 +958,14 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vectorgetCorrection(roc, row, u, v, dxTmp, duTmp, dvTmp); + corrections[i]->getCorrectionInternal(roc, row, u, v, dxTmp, duTmp, dvTmp); dx += dxTmp * scaling[i]; du += duTmp * scaling[i]; dv += dvTmp * scaling[i]; diff --git a/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx b/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx index cfa54a12f9f42..c83ee6d0cfa19 100644 --- a/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx +++ b/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx @@ -56,12 +56,7 @@ void TPCFastTransformHelperO2::init() mGeo.startConstruction(nRows); auto& detParam = ParameterDetector::Instance(); - float tpcZlengthSideA = detParam.TPClength; - float tpcZlengthSideC = detParam.TPClength; - - mGeo.setTPCzLength(tpcZlengthSideA, tpcZlengthSideC); - - mGeo.setTPCalignmentZ(0.); + mGeo.setTPCzLength(detParam.TPClength); for (int iRow = 0; iRow < mGeo.getNumberOfRows(); iRow++) { Sector sector = 0; @@ -114,12 +109,8 @@ std::unique_ptr TPCFastTransformHelperO2::create(Long_t TimeSt // set some initial calibration values, will be reinitialised later int updateCalibration() const float t0 = 0.; const float vDrift = 0.f; - const float vdCorrY = 0.; - const float ldCorr = 0.; - const float tofCorr = 0.; - const float primVtxZ = 0.; const long int initTimeStamp = -1; - fastTransform.setCalibration(initTimeStamp, t0, vDrift, vdCorrY, ldCorr, tofCorr, primVtxZ); + fastTransform.setCalibration1(initTimeStamp, t0, vDrift); fastTransform.finishConstruction(); } @@ -171,19 +162,13 @@ int TPCFastTransformHelperO2::updateCalibration(TPCFastTransform& fastTransform, const double vDrift = elParam.ZbinWidth * vDriftRef * vDriftFactor; // cm/timebin // fast transform formula: - // L = (t-t0)*(mVdrift + mVdriftCorrY*yLab ) + mLdriftCorr - // Z = Z(L) + tpcAlignmentZ + // L = (t-t0)*mVdrift + // Z = Z(L) // spline corrections for xyz - // Time-of-flight correction: ldrift += dist-to-vtx*tofCorr const double t0 = (driftTimeOffset + elParam.getAverageShapingTime()) / elParam.ZbinWidth; - const double vdCorrY = 0.; - const double ldCorr = 0.; - const double tofCorr = 0.; - const double primVtxZ = 0.; - - fastTransform.setCalibration(TimeStamp, t0, vDrift, vdCorrY, ldCorr, tofCorr, primVtxZ); + fastTransform.setCalibration1(TimeStamp, t0, vDrift); return 0; } diff --git a/Detectors/TPC/reconstruction/test/testTPCFastTransform.cxx b/Detectors/TPC/reconstruction/test/testTPCFastTransform.cxx index 0141b80819b64..53cfe08f3a7f4 100644 --- a/Detectors/TPC/reconstruction/test/testTPCFastTransform.cxx +++ b/Detectors/TPC/reconstruction/test/testTPCFastTransform.cxx @@ -72,7 +72,7 @@ BOOST_AUTO_TEST_CASE(FastTransform_test1) const GlobalPadNumber p = mapper.globalPadNumber(PadPos(row, pad)); const PadCentre& c = mapper.padCentre(p); float u = 0, v = 0; - fastTransform.convPadTimeToUV(0, row, pad, 0, u, v, 0.); + fastTransform.convPadTimeToUV(row, pad, 0, u, v, 0.); double dx = x - c.X(); double dy = u - (-c.Y()); // diferent sign convention for Y coordinate in the map @@ -192,8 +192,8 @@ BOOST_AUTO_TEST_CASE(FastTransform_test_setSpaceChargeCorrection) correctionUV(roc, row, u0, v0, dx, du, dv); statDiff += fabs((x1 - x0) - dx) + fabs((u1 - u0) - du) + fabs((v1 - v0) - dv); statN += 3; - //std::cout << (x1 - x0) - dx << " " << (u1 - u0) - du << " " << (v1 - v0) - dv << std::endl; //": v0 " << v0 <<" z0 "<Transform(roc, row, pad, time, x1f, y1f, z1f); diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx index 5d3c186a06d42..111e70072c58e 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx @@ -225,7 +225,7 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer info.scaleUtoGrid = spline.getGridX1().getUmax() / mGeo.getRowInfo(row).getUwidth(); info.gridV0 = infoOld.gridV0; - info.scaleVtoGrid = spline.getGridX2().getUmax() / (mGeo.getTPCzLength(roc) + 3. - info.gridV0); + info.scaleVtoGrid = spline.getGridX2().getUmax() / (mGeo.getTPCzLength() + 3. - info.gridV0); info.gridCorrU0 = infoOld.gridCorrU0; info.scaleCorrUtoGrid = infoOld.scaleCorrUtoGrid; @@ -440,7 +440,7 @@ GPUd() void TPCFastSpaceChargeCorrection::setNoCorrection() { // initialise all corrections to 0. for (int32_t roc = 0; roc < mGeo.getNumberOfRocs(); roc++) { - double vLength = (roc < mGeo.getNumberOfRocsA()) ? mGeo.getTPCzLengthA() : mGeo.getTPCzLengthC(); + double vLength = mGeo.getTPCzLength(); RocInfo& rocInfo = getRocInfo(roc); rocInfo.vMax = vLength; for (int32_t row = 0; row < mGeo.getNumberOfRows(); row++) { @@ -548,12 +548,14 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) if (prn) { LOG(info) << "check inverse transform for roc " << roc; } - double vLength = mGeo.getTPCzLength(roc); + double vLength = mGeo.getTPCzLength(); MaxValue maxDroc[3]; for (int32_t row = 0; row < mGeo.getNumberOfRows(); row++) { - float u0, u1, v0, v1; - mGeo.convScaledUVtoUV(roc, row, 0., 0., u0, v0); - mGeo.convScaledUVtoUV(roc, row, 1., 1., u1, v1); + float u0 = mGeo.getRowInfo(row).getUmin(); + float u1 = mGeo.getRowInfo(row).getUmax(); + float v0 = 0.; + float v1 = vLength; + double x = mGeo.getRowInfo(row).x; double stepU = (u1 - u0) / 100.; double stepV = (v1 - v0) / 100.; @@ -564,7 +566,7 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) continue; } float dx, du, dv; - getCorrection(roc, row, u, v, dx, du, dv); + getCorrectionInternal(roc, row, u, v, dx, du, dv); double cx = x + dx; double cu = u + du; double cv = v + dv; diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index b6244bfee1e0f..fa5cf7a1736bd 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -180,7 +180,9 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// _______________ The main method: cluster correction _______________________ /// - GPUd() int32_t getCorrection(int32_t roc, int32_t row, float u, float v, float& dx, float& du, float& dv) const; + GPUd() int32_t getCorrectionInternal(int32_t roc, int32_t row, float u, float v, float& dx, float& du, float& dv) const; + + GPUdi() std::tuple getCorrectionLocal(int32_t roc, int32_t row, float y, float z) const; /// inverse correction: Corrected U and V -> coorrected X GPUd() void getCorrectionInvCorrectedX(int32_t roc, int32_t row, float corrU, float corrV, float& corrX) const; @@ -199,6 +201,10 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// _______________ Utilities _______________________________________________ + /// convert local y, z to internal grid coordinates u,v + /// return values: u, v, scaling factor + GPUd() std::tuple convLocalToGrid(int32_t roc, int32_t row, float y, float z) const; + /// convert u,v to internal grid coordinates GPUd() void convUVtoGrid(int32_t roc, int32_t row, float u, float v, float& gridU, float& gridV) const; @@ -259,9 +265,6 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// release temporary memory used during construction void releaseConstructionMemory(); - /// temporary method with the an way of calculating 2D spline - GPUd() int32_t getCorrectionOld(int32_t roc, int32_t row, float u, float v, float& dx, float& du, float& dv) const; - /// _______________ Data members _______________________________________________ /// _______________ Construction control _______________________________________________ @@ -354,7 +357,7 @@ GPUdi() void TPCFastSpaceChargeCorrection::convCorrectedUVtoGrid(int32_t roc, in gridV = (corrV - info.gridCorrV0) * info.scaleCorrVtoGrid; } -GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrection(int32_t roc, int32_t row, float u, float v, float& dx, float& du, float& dv) const +GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrectionInternal(int32_t roc, int32_t row, float u, float v, float& dx, float& du, float& dv) const { const auto& info = getRocRowInfo(roc, row); const SplineType& spline = getSpline(roc, row); @@ -382,26 +385,41 @@ GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrection(int32_t roc, int32_t return 0; } -GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrectionOld(int32_t roc, int32_t row, float u, float v, float& dx, float& du, float& dv) const +GPUdi() std::tuple TPCFastSpaceChargeCorrection::getCorrectionLocal(int32_t roc, int32_t row, float y, float z) const { + const auto& info = getRocRowInfo(roc, row); const SplineType& spline = getSpline(roc, row); const float* splineData = getSplineData(roc, row); + + float u, v; + + mGeo.convLocalToUV(roc, y, z, u, v); + float gridU = 0, gridV = 0; convUVtoGrid(roc, row, u, v, gridU, gridV); + // shrink to the grid area + gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); + gridV = GPUCommonMath::Clamp(gridV, 0.f, (float)spline.getGridX2().getUmax()); + float dxuv[3]; - spline.interpolateUold(splineData, gridU, gridV, dxuv); - const auto& info = getRocRowInfo(roc, row); + spline.interpolateU(splineData, gridU, gridV, dxuv); + float s = v / info.gridV0; - if (s < 0.) { - s = 0.; - } - if (s > 1.) { - s = 1.; + + if (v >= info.gridV0) { + s = 1.f; + } else if (v <= 0.f) { + s = 0.f; } - dx = GPUCommonMath::Max(info.minCorr[0], GPUCommonMath::Min(info.maxCorr[0], s * dxuv[0])); - du = GPUCommonMath::Max(info.minCorr[1], GPUCommonMath::Min(info.maxCorr[1], s * dxuv[1])); - dv = GPUCommonMath::Max(info.minCorr[2], GPUCommonMath::Min(info.maxCorr[2], s * dxuv[2])); - return 0; + + float dx = GPUCommonMath::Clamp(s * dxuv[0], info.minCorr[0], info.maxCorr[0]); + float du = GPUCommonMath::Clamp(s * dxuv[1], info.minCorr[1], info.maxCorr[1]); + float dv = GPUCommonMath::Clamp(s * dxuv[2], info.minCorr[2], info.maxCorr[2]); + + float dy, dz; + mGeo.convUVtoLocal(roc, du, dv, dy, dz); + + return {dx, dy, dz}; } GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvCorrectedX( diff --git a/GPU/TPCFastTransformation/TPCFastTransform.cxx b/GPU/TPCFastTransformation/TPCFastTransform.cxx index bd29a760615ad..625f70c1710a1 100644 --- a/GPU/TPCFastTransformation/TPCFastTransform.cxx +++ b/GPU/TPCFastTransformation/TPCFastTransform.cxx @@ -37,7 +37,7 @@ using namespace o2::gpu; TPCFastTransform::TPCFastTransform() - : FlatObject(), mTimeStamp(0), mCorrection(), mApplyCorrection(1), mT0(0.f), mVdrift(0.f), mVdriftCorrY(0.f), mLdriftCorr(0.f), mTOFcorr(0.f), mPrimVtxZ(0.f), mLumi(TPCFastTransform::DEFLUMI), mLumiError(0.f), mLumiScaleFactor(1.0f), mIDC(TPCFastTransform::DEFIDC), mIDCError(0.f), mCTP2IDCFallBackThreshold(30.f) + : FlatObject(), mTimeStamp(0), mCorrection(), mApplyCorrection(1), mT0(0.f), mVdrift(0.f), mLumi(TPCFastTransform::DEFLUMI), mLumiError(0.f), mLumiScaleFactor(1.0f), mIDC(TPCFastTransform::DEFIDC), mIDCError(0.f), mCTP2IDCFallBackThreshold(30.f) { // Default Constructor: creates an empty uninitialized object } @@ -54,10 +54,6 @@ void TPCFastTransform::cloneFromObject(const TPCFastTransform& obj, char* newFla mApplyCorrection = obj.mApplyCorrection; mT0 = obj.mT0; mVdrift = obj.mVdrift; - mVdriftCorrY = obj.mVdriftCorrY; - mLdriftCorr = obj.mLdriftCorr; - mTOFcorr = obj.mTOFcorr; - mPrimVtxZ = obj.mPrimVtxZ; mLumi = obj.mLumi; mLumiError = obj.mLumiError; mIDC = obj.mIDC; @@ -123,7 +119,7 @@ void TPCFastTransform::startConstruction(const TPCFastSpaceChargeCorrection& cor mCorrection.cloneFromObject(correction, nullptr); } -void TPCFastTransform::setCalibration(int64_t timeStamp, float t0, float vDrift, float vDriftCorrY, float lDriftCorr, float tofCorr, float primVtxZ) +void TPCFastTransform::setCalibration1(int64_t timeStamp, float t0, float vDrift) { /// Sets all drift calibration parameters and the time stamp /// @@ -133,10 +129,6 @@ void TPCFastTransform::setCalibration(int64_t timeStamp, float t0, float vDrift, mTimeStamp = timeStamp; mT0 = t0; mVdrift = vDrift; - mVdriftCorrY = vDriftCorrY; - mLdriftCorr = lDriftCorr; - mTOFcorr = tofCorr; - mPrimVtxZ = primVtxZ; mConstructionMask |= ConstructionExtraState::CalibrationIsSet; } @@ -160,10 +152,6 @@ void TPCFastTransform::print() const LOG(info) << "mApplyCorrection = " << mApplyCorrection; LOG(info) << "mT0 = " << mT0; LOG(info) << "mVdrift = " << mVdrift; - LOG(info) << "mVdriftCorrY = " << mVdriftCorrY; - LOG(info) << "mLdriftCorr = " << mLdriftCorr; - LOG(info) << "mTOFcorr = " << mTOFcorr; - LOG(info) << "mPrimVtxZ = " << mPrimVtxZ; LOG(info) << "mLumi = " << mLumi; LOG(info) << "mLumiError = " << mLumiError; LOG(info) << "mIDC = " << mIDC; diff --git a/GPU/TPCFastTransformation/TPCFastTransform.h b/GPU/TPCFastTransformation/TPCFastTransform.h index 8aef1748ebf62..1ecd577eb7dac 100644 --- a/GPU/TPCFastTransformation/TPCFastTransform.h +++ b/GPU/TPCFastTransformation/TPCFastTransform.h @@ -159,7 +159,7 @@ class TPCFastTransform : public FlatObject /// /// It must be called once during construction, /// but also may be called afterwards to reset these parameters. - void setCalibration(int64_t timeStamp, float t0, float vDrift, float vDriftCorrY, float lDriftCorr, float tofCorr, float primVtxZ); + void setCalibration1(int64_t timeStamp, float t0, float vDrift); /// Set Lumi info void setLumi(float l) { mLumi = l; } @@ -183,7 +183,7 @@ class TPCFastTransform : public FlatObject /// _______________ The main method: cluster transformation _______________________ /// /// Transforms raw TPC coordinates to local XYZ withing a roc - /// taking calibration + alignment into account. + /// taking calibration into account. /// GPUd() void Transform(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime = 0, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; GPUd() void TransformXYZ(int32_t roc, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; @@ -209,12 +209,12 @@ class TPCFastTransform : public FlatObject GPUd() void TransformIdeal(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime) const; GPUd() void TransformIdealZ(int32_t roc, float time, float& z, float vertexTime) const; - GPUd() void convPadTimeToUV(int32_t roc, int32_t row, float pad, float time, float& u, float& v, float vertexTime) const; - GPUd() void convPadTimeToUVinTimeFrame(int32_t roc, int32_t row, float pad, float time, float& u, float& v, float maxTimeBin) const; - GPUd() void convTimeToVinTimeFrame(int32_t roc, float time, float& v, float maxTimeBin) const; + GPUd() void convPadTimeToUV(int32_t row, float pad, float time, float& u, float& v, float vertexTime) const; + GPUd() void convPadTimeToUVinTimeFrame(int32_t row, float pad, float time, float& u, float& v, float maxTimeBin) const; + GPUd() void convTimeToVinTimeFrame(float time, float& v, float maxTimeBin) const; - GPUd() void convUVtoPadTime(int32_t roc, int32_t row, float u, float v, float& pad, float& time, float vertexTime) const; - GPUd() void convUVtoPadTimeInTimeFrame(int32_t roc, int32_t row, float u, float v, float& pad, float& time, float maxTimeBin) const; + GPUd() void convUVtoPadTime(int32_t row, float u, float v, float& pad, float& time, float vertexTime) const; + GPUd() void convUVtoPadTimeInTimeFrame(int32_t row, float u, float v, float& pad, float& time, float maxTimeBin) const; GPUd() void convVtoTime(float v, float& time, float vertexTime) const; GPUd() float convTimeToZinTimeFrame(int32_t roc, float time, float maxTimeBin) const; @@ -222,10 +222,8 @@ class TPCFastTransform : public FlatObject GPUd() float convDeltaTimeToDeltaZinTimeFrame(int32_t roc, float deltaTime) const; GPUd() float convDeltaZtoDeltaTimeInTimeFrame(int32_t roc, float deltaZ) const; GPUd() float convDeltaZtoDeltaTimeInTimeFrameAbs(float deltaZ) const; - GPUd() float convZOffsetToVertexTime(int32_t roc, float zOffset, float maxTimeBin) const; - GPUd() float convVertexTimeToZOffset(int32_t roc, float vertexTime, float maxTimeBin) const; - - GPUd() void getTOFcorrection(int32_t roc, int32_t row, float x, float y, float z, float& dz) const; + GPUd() float convZOffsetToVertexTime(int32_t sector, float zOffset, float maxTimeBin) const; + GPUd() float convVertexTimeToZOffset(int32_t sector, float vertexTime, float maxTimeBin) const; void setApplyCorrectionOn() { mApplyCorrection = 1; } void setApplyCorrectionOff() { mApplyCorrection = 0; } @@ -245,15 +243,6 @@ class TPCFastTransform : public FlatObject /// Return T0 in time bin units GPUd() float getT0() const { return mT0; } - /// Return VdriftCorrY in time_bin / cn - GPUd() float getVdriftCorrY() const { return mVdriftCorrY; } - - /// Return LdriftCorr offset in cm - GPUd() float getLdriftCorr() const { return mLdriftCorr; } - - /// Return TOF correction (vdrift / C) - GPUd() float getTOFCorr() const { return mLdriftCorr; } - /// Return map lumi GPUd() float getLumi() const { return mLumi; } @@ -330,23 +319,10 @@ class TPCFastTransform : public FlatObject /// /// t = (float) time bin, y = global y /// - /// L(t,y) = (t-mT0)*(mVdrift + mVdriftCorrY*y ) + mLdriftCorr ____ + /// L(t,y) = (t-mT0)*mVdrift ____ /// - float mT0; ///< T0 in [time bin] - float mVdrift; ///< VDrift in [cm/time bin] - float mVdriftCorrY; ///< VDrift correction for global Y[cm] in [1/time bin] - float mLdriftCorr; ///< drift length correction in [cm] - - /// A coefficient for Time-Of-Flight correction: drift length -= EstimatedDistanceToVtx[cm]*mTOFcorr - /// - /// Since this correction requires a knowledge of the spatial position, it is appied after mCorrection, - /// not on the drift length but directly on V coordinate. - /// - /// mTOFcorr == mVdrift/(speed of light) - /// - float mTOFcorr; - - float mPrimVtxZ; ///< Z of the primary vertex, needed for the Time-Of-Flight correction + float mT0; ///< T0 in [time bin] + float mVdrift; ///< VDrift in [cm/time bin] float mLumi; ///< luminosity estimator float mLumiError; ///< error on luminosity @@ -359,7 +335,7 @@ class TPCFastTransform : public FlatObject /// Correction of (x,u,v) with tricubic interpolator on a regular grid TPCSlowSpaceChargeCorrection* mCorrectionSlow{nullptr}; ///< reference space charge corrections - GPUd() void TransformInternal(int32_t roc, int32_t row, float& u, float& v, float& x, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const; + GPUd() void TransformLocal(int32_t roc, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const; ClassDefNV(TPCFastTransform, 4); }; @@ -368,248 +344,195 @@ class TPCFastTransform : public FlatObject // Inline implementations of some methods // ======================================================================= -GPUdi() void TPCFastTransform::convPadTimeToUV(int32_t roc, int32_t row, float pad, float time, float& u, float& v, float vertexTime) const +GPUdi() void TPCFastTransform::convPadTimeToUV(int32_t row, float pad, float time, float& u, float& v, float vertexTime) const { - bool sideC = (roc >= getGeometry().getNumberOfRocsA()); - const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); - const TPCFastTransformGeo::RocInfo& rocInfo = getGeometry().getRocInfo(roc); - float x = rowInfo.x; u = (pad - 0.5f * rowInfo.maxPad) * rowInfo.padWidth; - - float y = sideC ? -u : u; // pads are mirrorred on C-side - float yLab = y * rocInfo.cosAlpha + x * rocInfo.sinAlpha; - - v = (time - mT0 - vertexTime) * (mVdrift + mVdriftCorrY * yLab) + mLdriftCorr; // drift length cm + v = (time - mT0 - vertexTime) * (mVdrift); // drift length cm } -GPUdi() void TPCFastTransform::convTimeToVinTimeFrame(int32_t roc, float time, float& v, float maxTimeBin) const +GPUdi() void TPCFastTransform::convTimeToVinTimeFrame(float time, float& v, float maxTimeBin) const { - v = (time - mT0 - maxTimeBin) * mVdrift + mLdriftCorr; // drift length cm - if (roc < getGeometry().getNumberOfRocsA()) { - v += getGeometry().getTPCzLengthA(); - } else { - v += getGeometry().getTPCzLengthC(); - } + v = (time - mT0 - maxTimeBin) * mVdrift; // drift length cm + v += getGeometry().getTPCzLength(); } -GPUdi() void TPCFastTransform::convPadTimeToUVinTimeFrame(int32_t roc, int32_t row, float pad, float time, float& u, float& v, float maxTimeBin) const +GPUdi() void TPCFastTransform::convPadTimeToUVinTimeFrame(int32_t row, float pad, float time, float& u, float& v, float maxTimeBin) const { const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); u = (pad - 0.5f * rowInfo.maxPad) * rowInfo.padWidth; - convTimeToVinTimeFrame(roc, time, v, maxTimeBin); + convTimeToVinTimeFrame(time, v, maxTimeBin); } -GPUdi() float TPCFastTransform::convZOffsetToVertexTime(int32_t roc, float zOffset, float maxTimeBin) const +GPUdi() float TPCFastTransform::convZOffsetToVertexTime(int32_t sector, float zOffset, float maxTimeBin) const { - if (roc < getGeometry().getNumberOfRocsA()) { - return maxTimeBin - (getGeometry().getTPCzLengthA() + zOffset) / mVdrift; + if (sector < getGeometry().getNumberOfSectorsA()) { + return maxTimeBin - (getGeometry().getTPCzLength() + zOffset) / mVdrift; } else { - return maxTimeBin - (getGeometry().getTPCzLengthC() - zOffset) / mVdrift; + return maxTimeBin - (getGeometry().getTPCzLength() - zOffset) / mVdrift; } } -GPUdi() float TPCFastTransform::convVertexTimeToZOffset(int32_t roc, float vertexTime, float maxTimeBin) const +GPUdi() float TPCFastTransform::convVertexTimeToZOffset(int32_t sector, float vertexTime, float maxTimeBin) const { - if (roc < getGeometry().getNumberOfRocsA()) { - return (maxTimeBin - vertexTime) * mVdrift - getGeometry().getTPCzLengthA(); + if (sector < getGeometry().getNumberOfSectorsA()) { + return (maxTimeBin - vertexTime) * mVdrift - getGeometry().getTPCzLength(); } else { - return -((maxTimeBin - vertexTime) * mVdrift - getGeometry().getTPCzLengthC()); + return -((maxTimeBin - vertexTime) * mVdrift - getGeometry().getTPCzLength()); } } -GPUdi() void TPCFastTransform::convUVtoPadTime(int32_t roc, int32_t row, float u, float v, float& pad, float& time, float vertexTime) const +GPUdi() void TPCFastTransform::convUVtoPadTime(int32_t row, float u, float v, float& pad, float& time, float vertexTime) const { - bool sideC = (roc >= getGeometry().getNumberOfRocsA()); - const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); - const TPCFastTransformGeo::RocInfo& rocInfo = getGeometry().getRocInfo(roc); - pad = u / rowInfo.padWidth + 0.5f * rowInfo.maxPad; - - float x = rowInfo.x; - float y = sideC ? -u : u; // pads are mirrorred on C-side - float yLab = y * rocInfo.cosAlpha + x * rocInfo.sinAlpha; - time = mT0 + vertexTime + (v - mLdriftCorr) / (mVdrift + mVdriftCorrY * yLab); + time = mT0 + vertexTime + v / mVdrift; } GPUdi() void TPCFastTransform::convVtoTime(float v, float& time, float vertexTime) const { - float yLab = 0.f; - time = mT0 + vertexTime + (v - mLdriftCorr) / (mVdrift + mVdriftCorrY * yLab); + time = mT0 + vertexTime + v / mVdrift; } -GPUdi() void TPCFastTransform::convUVtoPadTimeInTimeFrame(int32_t roc, int32_t row, float u, float v, float& pad, float& time, float maxTimeBin) const +GPUdi() void TPCFastTransform::convUVtoPadTimeInTimeFrame(int32_t row, float u, float v, float& pad, float& time, float maxTimeBin) const { - if (roc < getGeometry().getNumberOfRocsA()) { - v -= getGeometry().getTPCzLengthA(); - } else { - v -= getGeometry().getTPCzLengthC(); - } + v -= getGeometry().getTPCzLength(); const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); pad = u / rowInfo.padWidth + 0.5f * rowInfo.maxPad; - time = mT0 + maxTimeBin + (v - mLdriftCorr) / mVdrift; + time = mT0 + maxTimeBin + v / mVdrift; } -GPUdi() void TPCFastTransform::getTOFcorrection(int32_t roc, int32_t /*row*/, float x, float y, float z, float& dz) const +GPUdi() void TPCFastTransform::TransformLocal(int32_t roc, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const { - // calculate time of flight correction for z coordinate + GPUCA_RTC_SPECIAL_CODE(ref2 = nullptr; scale2 = 0.f;); - bool sideC = (roc >= getGeometry().getNumberOfRocsA()); - float distZ = z - mPrimVtxZ; - float dv = -GPUCommonMath::Sqrt(x * x + y * y + distZ * distZ) * mTOFcorr; - dz = sideC ? dv : -dv; -} + if (!mApplyCorrection) { + return; + } -GPUdi() void TPCFastTransform::TransformInternal(int32_t roc, int32_t row, float& u, float& v, float& x, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const -{ - GPUCA_RTC_SPECIAL_CODE(ref2 = nullptr; scale2 = 0.f;); - if (mApplyCorrection) { - float dx = 0.f, du = 0.f, dv = 0.f; - if ((scale >= 0.f) || (scaleMode == 1) || (scaleMode == 2)) { + float dx = 0.f, dy = 0.f, dz = 0.f; + + if ((scale >= 0.f) || (scaleMode == 1) || (scaleMode == 2)) { #ifndef GPUCA_GPUCODE - if (mCorrectionSlow) { - float ly, lz; - getGeometry().convUVtoLocal(roc, u, v, ly, lz); - float gx, gy, gz; - getGeometry().convLocalToGlobal(roc, x, ly, lz, gx, gy, gz); - - float gdxC, gdyC, gdzC; - mCorrectionSlow->getCorrections(gx, gy, gz, roc, gdxC, gdyC, gdzC); - getGeometry().convGlobalToLocal(roc, gdxC, gdyC, gdzC, dx, du, dv); - - if (roc >= 18) { - du = -du; // mirror for c-Side - } else { - dv = -dv; // mirror z for A-Side - } - } else + if (mCorrectionSlow) { + float gx, gy, gz; + getGeometry().convLocalToGlobal(roc, x, y, z, gx, gy, gz); + float gdxC, gdyC, gdzC; + mCorrectionSlow->getCorrections(gx, gy, gz, roc, gdxC, gdyC, gdzC); + getGeometry().convGlobalToLocal(roc, gdxC, gdyC, gdzC, dx, dy, dz); + } else #endif // GPUCA_GPUCODE - { - mCorrection.getCorrection(roc, row, u, v, dx, du, dv); - if (ref) { - if ((scale > 0.f) && (scaleMode == 0)) { // scaling was requested - float dxRef, duRef, dvRef; - ref->mCorrection.getCorrection(roc, row, u, v, dxRef, duRef, dvRef); - dx = (dx - dxRef) * scale + dxRef; - du = (du - duRef) * scale + duRef; - dv = (dv - dvRef) * scale + dvRef; - } else if ((scale != 0.f) && ((scaleMode == 1) || (scaleMode == 2))) { - float dxRef, duRef, dvRef; - ref->mCorrection.getCorrection(roc, row, u, v, dxRef, duRef, dvRef); - dx = dxRef * scale + dx; - du = duRef * scale + du; - dv = dvRef * scale + dv; - } - } - if (ref2 && (scale2 != 0)) { - float dxRef, duRef, dvRef; - ref2->mCorrection.getCorrection(roc, row, u, v, dxRef, duRef, dvRef); - dx = dxRef * scale2 + dx; - du = duRef * scale2 + du; - dv = dvRef * scale2 + dv; + { + std::tie(dx, dy, dz) = mCorrection.getCorrectionLocal(roc, row, y, z); + if (ref) { + if ((scale > 0.f) && (scaleMode == 0)) { // scaling was requested + auto [dxRef, dyRef, dzRef] = ref->mCorrection.getCorrectionLocal(roc, row, y, z); + dx = (dx - dxRef) * scale + dxRef; + dy = (dy - dyRef) * scale + dyRef; + dz = (dz - dzRef) * scale + dzRef; + } else if ((scale != 0.f) && ((scaleMode == 1) || (scaleMode == 2))) { + auto [dxRef, dyRef, dzRef] = ref->mCorrection.getCorrectionLocal(roc, row, y, z); + dx = dxRef * scale + dx; + dy = dyRef * scale + dy; + dz = dzRef * scale + dz; } } + if (ref2 && (scale2 != 0)) { + auto [dxRef, dyRef, dzRef] = ref2->mCorrection.getCorrectionLocal(roc, row, y, z); + dx = dxRef * scale2 + dx; + dy = dyRef * scale2 + dy; + dz = dzRef * scale2 + dz; + } } - GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamFastTransform)) { - float ly, lz; - getGeometry().convUVtoLocal(roc, u, v, ly, lz); + } - float gx, gy, gz; - getGeometry().convLocalToGlobal(roc, x, ly, lz, gx, gy, gz); + GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamFastTransform)) { + float lx = x, ly = y, lz = z; - float lyT, lzT; - float uCorr = u + du; - float vCorr = v + dv; - float lxT = x + dx; - getGeometry().convUVtoLocal(roc, uCorr, vCorr, lyT, lzT); + float gx, gy, gz; + getGeometry().convLocalToGlobal(roc, lx, ly, lz, gx, gy, gz); - float invYZtoXScaled; - InverseTransformYZtoX(roc, row, lyT, lzT, invYZtoXScaled, ref, ref2, scale, scale2, scaleMode); + float lxT = lx + dx; + float lyT = ly + dy; + float lzT = lz + dz; - float invYZtoX; - InverseTransformYZtoX(roc, row, lyT, lzT, invYZtoX); + float invYZtoXScaled; + InverseTransformYZtoX(roc, row, lyT, lzT, invYZtoXScaled, ref, ref2, scale, scale2, scaleMode); - float YZtoNominalY; - float YZtoNominalZ; - InverseTransformYZtoNominalYZ(roc, row, lyT, lzT, YZtoNominalY, YZtoNominalZ); + float invYZtoX; + InverseTransformYZtoX(roc, row, lyT, lzT, invYZtoX); - float YZtoNominalYScaled; - float YZtoNominalZScaled; - InverseTransformYZtoNominalYZ(roc, row, lyT, lzT, YZtoNominalYScaled, YZtoNominalZScaled, ref, ref2, scale, scale2, scaleMode); + float YZtoNominalY; + float YZtoNominalZ; + InverseTransformYZtoNominalYZ(roc, row, lyT, lzT, YZtoNominalY, YZtoNominalZ); - float dxRef, duRef, dvRef; - if (ref) { - ref->mCorrection.getCorrection(roc, row, u, v, dxRef, duRef, dvRef); - } + float YZtoNominalYScaled; + float YZtoNominalZScaled; + InverseTransformYZtoNominalYZ(roc, row, lyT, lzT, YZtoNominalYScaled, YZtoNominalZScaled, ref, ref2, scale, scale2, scaleMode); - float dxRef2, duRef2, dvRef2; - if (ref2) { - ref2->mCorrection.getCorrection(roc, row, u, v, dxRef2, duRef2, dvRef2); - } + float dxRef = 0.f, dyRef = 0.f, dzRef = 0.f; + if (ref) { + std::tie(dxRef, dyRef, dzRef) = ref->mCorrection.getCorrectionLocal(roc, row, y, z); + } - float dxOrig, duOrig, dvOrig; - mCorrection.getCorrection(roc, row, u, v, dxOrig, duOrig, dvOrig); - - o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_Transform").data() - // corrections in x, u, v - << "dxOrig=" << dxOrig - << "duOrig=" << duOrig - << "dvOrig=" << dvOrig - << "dxRef=" << dxRef - << "duRef=" << duRef - << "dvRef=" << dvRef - << "dxRef2=" << dxRef2 - << "duRef2=" << duRef2 - << "dvRef2=" << dvRef2 - << "dx=" << dx - << "du=" << du - << "dv=" << dv - << "v=" << v - << "u=" << u - << "row=" << row - << "roc=" << roc - << "scale=" << scale - << "scale2=" << scale2 - // original local coordinates - << "ly=" << ly - << "lz=" << lz - << "lx=" << x - // corrected local coordinated - << "lxT=" << lxT - << "lyT=" << lyT - << "lzT=" << lzT - // global uncorrected coordinates - << "gx=" << gx - << "gy=" << gy - << "gz=" << gz - // some transformations which are applied - << "invYZtoX=" << invYZtoX - << "invYZtoXScaled=" << invYZtoXScaled - << "YZtoNominalY=" << YZtoNominalY - << "YZtoNominalYScaled=" << YZtoNominalYScaled - << "YZtoNominalZ=" << YZtoNominalZ - << "YZtoNominalZScaled=" << YZtoNominalZScaled - << "scaleMode=" << scaleMode - << "\n"; - }) - - x += dx; - u += du; - v += dv; - } + float dxRef2 = 0.f, duRef2 = 0.f, dvRef2 = 0.f; + if (ref2) { + std::tie(dxRef2, duRef2, dvRef2) = ref2->mCorrection.getCorrectionLocal(roc, row, y, z); + } + + auto [dxOrig, dyOrig, dzOrig] = mCorrection.getCorrectionLocal(roc, row, y, z); + + o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_Transform").data() + // corrections in x, u, v + << "dxOrig=" << dxOrig + << "dyOrig=" << dyOrig + << "dzOrig=" << dzOrig + << "dxRef=" << dxRef + << "dyRef=" << dyRef + << "dzRef=" << dzRef + << "dxRef2=" << dxRef2 + << "dyRef2=" << dyRef2 + << "dzRef2=" << dzRef2 + << "dx=" << dx + << "dy=" << dy + << "dz=" << dz + << "row=" << row + << "roc=" << roc + << "scale=" << scale + << "scale2=" << scale2 + // original local coordinates + << "ly=" << ly + << "lz=" << lz + << "lx=" << lx + // corrected local coordinated + << "lxT=" << lxT + << "lyT=" << lyT + << "lzT=" << lzT + // global uncorrected coordinates + << "gx=" << gx + << "gy=" << gy + << "gz=" << gz + // some transformations which are applied + << "invYZtoX=" << invYZtoX + << "invYZtoXScaled=" << invYZtoXScaled + << "YZtoNominalY=" << YZtoNominalY + << "YZtoNominalYScaled=" << YZtoNominalYScaled + << "YZtoNominalZ=" << YZtoNominalZ + << "YZtoNominalZScaled=" << YZtoNominalZScaled + << "scaleMode=" << scaleMode + << "\n"; + }) + + x += dx; + y += dy; + z += dz; } GPUdi() void TPCFastTransform::TransformXYZ(int32_t roc, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const { - float u, v; - getGeometry().convLocalToUV(roc, y, z, u, v); - TransformInternal(roc, row, u, v, x, ref, ref2, scale, scale2, scaleMode); - getGeometry().convUVtoLocal(roc, u, v, y, z); - float dzTOF = 0; - getTOFcorrection(roc, row, x, y, z, dzTOF); - z += dzTOF; + + TransformLocal(roc, row, x, y, z, ref, ref2, scale, scale2, scaleMode); } GPUdi() void TPCFastTransform::Transform(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const @@ -617,31 +540,23 @@ GPUdi() void TPCFastTransform::Transform(int32_t roc, int32_t row, float pad, fl /// _______________ The main method: cluster transformation _______________________ /// /// Transforms raw TPC coordinates to local XYZ withing a roc - /// taking calibration + alignment into account. + /// taking calibration into account. /// const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); - // const RocInfo &rocInfo = getRocInfo( roc ); - // bool sideC = ( roc >= NumberOfRocs / 2 ); - x = rowInfo.x; float u = 0, v = 0; - convPadTimeToUV(roc, row, pad, time, u, v, vertexTime); - - TransformInternal(roc, row, u, v, x, ref, ref2, scale, scale2, scaleMode); - + convPadTimeToUV(row, pad, time, u, v, vertexTime); getGeometry().convUVtoLocal(roc, u, v, y, z); - float dzTOF = 0; - getTOFcorrection(roc, row, x, y, z, dzTOF); - z += dzTOF; + TransformLocal(roc, row, x, y, z, ref, ref2, scale, scale2, scaleMode); } GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t roc, float time, float& z, float maxTimeBin) const { float v = 0; - convTimeToVinTimeFrame(roc, time, v, maxTimeBin); + convTimeToVinTimeFrame(time, v, maxTimeBin); getGeometry().convVtoLocal(roc, v, z); } @@ -656,7 +571,7 @@ GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t roc, int32_t row, fl const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); x = rowInfo.x; float u = 0, v = 0; - convPadTimeToUVinTimeFrame(roc, row, pad, time, u, v, maxTimeBin); + convPadTimeToUVinTimeFrame(row, pad, time, u, v, maxTimeBin); getGeometry().convUVtoLocal(roc, u, v, y, z); } @@ -665,7 +580,7 @@ GPUdi() void TPCFastTransform::InverseTransformInTimeFrame(int32_t roc, int32_t /// Inverse transformation to TransformInTimeFrame float u = 0, v = 0; getGeometry().convLocalToUV(roc, y, z, u, v); - convUVtoPadTimeInTimeFrame(roc, row, u, v, pad, time, maxTimeBin); + convUVtoPadTimeInTimeFrame(row, u, v, pad, time, maxTimeBin); } GPUdi() float TPCFastTransform::InverseTransformInTimeFrame(int32_t roc, float z, float maxTimeBin) const @@ -715,26 +630,16 @@ GPUdi() float TPCFastTransform::convTimeToZinTimeFrame(int32_t roc, float time, /// Only Z coordinate. /// - float v = (time - mT0 - maxTimeBin) * mVdrift + mLdriftCorr; // drift length cm - float z = getGeometry().getTPCalignmentZ(); // global TPC alignment - if (roc < getGeometry().getNumberOfRocsA()) { - z -= v; - } else { - z += v; - } + float v = (time - mT0 - maxTimeBin) * mVdrift; // drift length cm + float z = (roc < getGeometry().getNumberOfRocsA()) ? -v : v; return z; } GPUdi() float TPCFastTransform::convZtoTimeInTimeFrame(int32_t roc, float z, float maxTimeBin) const { /// Inverse transformation of convTimeToZinTimeFrame() - float v; - if (roc < getGeometry().getNumberOfRocsA()) { - v = getGeometry().getTPCalignmentZ() - z; - } else { - v = z - getGeometry().getTPCalignmentZ(); - } - return mT0 + maxTimeBin + (v - mLdriftCorr) / mVdrift; + float v = (roc < getGeometry().getNumberOfRocsA()) ? -z : z; + return mT0 + maxTimeBin + v / mVdrift; } GPUdi() float TPCFastTransform::convDeltaTimeToDeltaZinTimeFrame(int32_t roc, float deltaTime) const @@ -769,17 +674,7 @@ GPUdi() float TPCFastTransform::getMaxDriftTime(int32_t roc, int32_t row, float { /// maximal possible drift time of the active area float maxL = mCorrection.getMaxDriftLength(roc, row, pad); - - bool sideC = (roc >= getGeometry().getNumberOfRocsA()); - const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); - const TPCFastTransformGeo::RocInfo& rocInfo = getGeometry().getRocInfo(roc); - - float x = rowInfo.x; - float u = (pad - 0.5f * rowInfo.maxPad) * rowInfo.padWidth; - - float y = sideC ? -u : u; // pads are mirrorred on C-side - float yLab = y * rocInfo.cosAlpha + x * rocInfo.sinAlpha; - return mT0 + (maxL - mLdriftCorr) / (mVdrift + mVdriftCorrY * yLab); + return mT0 + maxL / mVdrift; } GPUdi() float TPCFastTransform::getMaxDriftTime(int32_t roc, int32_t row) const diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx b/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx index b472868fa1071..c8982f05d4730 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx @@ -35,10 +35,10 @@ TPCFastTransformGeo::TPCFastTransformGeo() s.sinAlpha = sin(alpha); s.cosAlpha = cos(alpha); } - mRocInfos[NumberOfRocs] = RocInfo{0.f, 0.f}; + mRocInfos[NumberOfRocs] = RocInfo{}; for (int32_t i = 0; i < MaxNumberOfRows + 1; i++) { - mRowInfos[i] = RowInfo{0.f, -1, 0.f, 0.f, 0.f, 0.f}; + mRowInfos[i] = RowInfo{}; } } @@ -51,45 +51,25 @@ void TPCFastTransformGeo::startConstruction(int32_t numberOfRows) mConstructionMask = ConstructionState::InProgress; mNumberOfRows = numberOfRows; - mTPCzLengthA = 0.f; - mTPCzLengthC = 0.f; - mTPCalignmentZ = 0.f; - mScaleVtoSVsideA = 0.f; - mScaleVtoSVsideC = 0.f; - mScaleSVtoVsideA = 0.f; - mScaleSVtoVsideC = 0.f; + mTPCzLength = 0.f; for (int32_t i = 0; i < MaxNumberOfRows; i++) { - mRowInfos[i] = RowInfo{0.f, -1, 0.f, 0.f, 0.f, 0.f}; + mRowInfos[i] = RowInfo{}; } } -void TPCFastTransformGeo::setTPCzLength(float tpcZlengthSideA, float tpcZlengthSideC) +void TPCFastTransformGeo::setTPCzLength(float tpcZlength) { /// Sets TPC z length for both sides assert(mConstructionMask & ConstructionState::InProgress); - assert((tpcZlengthSideA > 0.f) && (tpcZlengthSideC > 0.f)); + assert(tpcZlength > 0.f); - mTPCzLengthA = tpcZlengthSideA; - mTPCzLengthC = tpcZlengthSideC; - mScaleSVtoVsideA = tpcZlengthSideA + 3.; // add some extra possible drift length due to the space charge distortions - mScaleSVtoVsideC = tpcZlengthSideC + 3.; - mScaleVtoSVsideA = 1. / mScaleSVtoVsideA; - mScaleVtoSVsideC = 1. / mScaleSVtoVsideC; + mTPCzLength = tpcZlength; mConstructionMask |= ConstructionState::GeometryIsSet; } -void TPCFastTransformGeo::setTPCalignmentZ(float tpcAlignmentZ) -{ - /// Sets the TPC alignment - assert(mConstructionMask & ConstructionState::InProgress); - - mTPCalignmentZ = tpcAlignmentZ; - mConstructionMask |= ConstructionState::AlignmentIsSet; -} - void TPCFastTransformGeo::setTPCrow(int32_t iRow, float x, int32_t nPads, float padWidth) { /// Initializes a TPC row @@ -113,8 +93,6 @@ void TPCFastTransformGeo::setTPCrow(int32_t iRow, float x, int32_t nPads, float row.maxPad = nPads - 1; row.padWidth = padWidth; row.u0 = -uWidth / 2.; - row.scaleUtoSU = 1. / uWidth; - row.scaleSUtoU = uWidth; } void TPCFastTransformGeo::finishConstruction() @@ -123,7 +101,6 @@ void TPCFastTransformGeo::finishConstruction() assert(mConstructionMask & ConstructionState::InProgress); // construction in process assert(mConstructionMask & ConstructionState::GeometryIsSet); // geometry is set - assert(mConstructionMask & ConstructionState::AlignmentIsSet); // alignment is set for (int32_t i = 0; i < mNumberOfRows; i++) { // all TPC rows are initialized assert(getRowInfo(i).maxPad > 0); @@ -138,9 +115,7 @@ void TPCFastTransformGeo::print() const #if !defined(GPUCA_GPUCODE) LOG(info) << "TPC Fast Transformation Geometry: "; LOG(info) << "mNumberOfRows = " << mNumberOfRows; - LOG(info) << "mTPCzLengthA = " << mTPCzLengthA; - LOG(info) << "mTPCzLengthC = " << mTPCzLengthC; - LOG(info) << "mTPCalignmentZ = " << mTPCalignmentZ; + LOG(info) << "mTPCzLength = " << mTPCzLength; LOG(info) << "TPC Rows : "; for (int32_t i = 0; i < mNumberOfRows; i++) { LOG(info) << " tpc row " << i << ": x = " << mRowInfos[i].x << " maxPad = " << mRowInfos[i].maxPad << " padWidth = " << mRowInfos[i].padWidth; @@ -179,26 +154,26 @@ int32_t TPCFastTransformGeo::test(int32_t roc, int32_t row, float ly, float lz) LOG(info) << "Error local <-> UV: y " << ly << " dy " << ly1 - ly << " z " << lz << " dz " << lz1 - lz; error = -4; } + /* + float su = 0.f, sv = 0.f; - float su = 0.f, sv = 0.f; + convUVtoScaledUV(roc, row, u, v, su, sv); - convUVtoScaledUV(roc, row, u, v, su, sv); + if (su < 0.f || su > 1.f) { + LOG(info) << "Error scaled U range: u " << u << " su " << su; + error = -5; + } - if (su < 0.f || su > 1.f) { - LOG(info) << "Error scaled U range: u " << u << " su " << su; - error = -5; - } - - float u1 = 0.f, v1 = 0.f; - convScaledUVtoUV(roc, row, su, sv, u1, v1); - - if (fabs(u1 - u) > 1.e-4 || fabs(v1 - v) > 1.e-4) { - LOG(info) << "Error UV<->scaled UV: u " << u << " du " << u1 - u << " v " << v << " dv " << v1 - v; - error = -6; - } + float u1 = 0.f, v1 = 0.f; + convScaledUVtoUV(roc, row, su, sv, u1, v1); + if (fabs(u1 - u) > 1.e-4 || fabs(v1 - v) > 1.e-4) { + LOG(info) << "Error UV<->scaled UV: u " << u << " du " << u1 - u << " v " << v << " dv " << v1 - v; + error = -6; + } + */ float pad = convUtoPad(row, u); - u1 = convPadToU(row, pad); + float u1 = convPadToU(row, pad); if (fabs(u1 - u) > 1.e-5) { LOG(info) << "Error U<->Pad: u " << u << " pad " << pad << " du " << u1 - u; diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.h b/GPU/TPCFastTransformation/TPCFastTransformGeo.h index 3382d1d926ce2..a5d642158cd8f 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.h +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.h @@ -36,19 +36,17 @@ class TPCFastTransformGeo public: /// The struct contains necessary info for TPC ROC struct RocInfo { - float sinAlpha; - float cosAlpha; + float sinAlpha{0.f}; ///< sin of the angle between the local x and the global x + float cosAlpha{0.f}; ///< cos of the angle between the local x and the global x ClassDefNV(RocInfo, 1); }; /// The struct contains necessary info about TPC padrow struct RowInfo { - float x; ///< nominal X coordinate of the row [cm] - int32_t maxPad; ///< maximal pad number = n pads - 1 - float padWidth; ///< width of pads [cm] - float u0; ///< min. u coordinate - float scaleUtoSU; ///< scale for su (scaled u ) coordinate - float scaleSUtoU; ///< scale for u coordinate + float x{0.f}; ///< nominal X coordinate of the padrow [cm] + int32_t maxPad{0}; ///< maximal pad number = n pads - 1 + float padWidth{0.f}; ///< width of pads [cm] + float u0{0.f}; ///< min. u coordinate /// get U min GPUd() float getUmin() const { return u0; } @@ -92,13 +90,7 @@ class TPCFastTransformGeo /// Sets TPC geometry /// /// It must be called once during initialization - void setTPCzLength(float tpcZlengthSideA, float tpcZlengthSideC); - - /// Sets all drift calibration parameters and the time stamp - /// - /// It must be called once during construction, - /// but also may be called afterwards to reset these parameters. - void setTPCalignmentZ(float tpcAlignmentZ); + void setTPCzLength(float tpcZlength); /// Finishes initialization: puts everything to the flat buffer, releases temporary memory void finishConstruction(); @@ -126,21 +118,8 @@ class TPCFastTransformGeo /// Gives TPC row info GPUd() const RowInfo& getRowInfo(int32_t row) const; - /// Gives Z length of the TPC, side A - GPUd() float getTPCzLengthA() const { return mTPCzLengthA; } - - /// Gives Z length of the TPC, side C - GPUd() float getTPCzLengthC() const { return mTPCzLengthC; } - - /// Gives Z length of the TPC, depending on the roc - GPUd() float getTPCzLength(int32_t roc) const - { - return (roc < NumberOfRocsA) ? mTPCzLengthA - : mTPCzLengthC; - } - - /// Gives TPC alignment in Z - GPUd() float getTPCalignmentZ() const { return mTPCalignmentZ; } + /// Gives Z length of the TPC, one Z side + GPUd() float getTPCzLength() const { return mTPCzLength; } /// _______________ Conversion of coordinate systems __________ @@ -157,15 +136,6 @@ class TPCFastTransformGeo /// convert Local-> UV c.s. GPUd() void convLocalToUV(int32_t roc, float y, float z, float& u, float& v) const; - /// convert UV -> Scaled UV - GPUd() void convUVtoScaledUV(int32_t roc, int32_t row, float u, float v, float& su, float& sv) const; - - /// convert Scaled UV -> UV - GPUd() void convScaledUVtoUV(int32_t roc, int32_t row, float su, float sv, float& u, float& v) const; - - /// convert Scaled UV -> Local c.s. - GPUd() void convScaledUVtoLocal(int32_t roc, int32_t row, float su, float sv, float& ly, float& lz) const; - /// convert Pad coordinate -> U GPUd() float convPadToU(int32_t row, float pad) const; @@ -196,7 +166,6 @@ class TPCFastTransformGeo Constructed = 0x1, ///< the object is constructed, temporary memory is released InProgress = 0x2, ///< construction started: temporary memory is reserved GeometryIsSet = 0x4, ///< the TPC geometry is set - AlignmentIsSet = 0x8 ///< the TPC alignment is set }; uint32_t mConstructionMask = ConstructionState::NotConstructed; ///< mask for constructed object members, first two bytes are used by this class @@ -204,18 +173,12 @@ class TPCFastTransformGeo /// _______________ Geometry _______________________________________________ int32_t mNumberOfRows = 0; ///< Number of TPC rows. It is different for the Run2 and the Run3 setups - float mTPCzLengthA = 0.f; ///< Z length of the TPC, side A - float mTPCzLengthC = 0.f; ///< Z length of the TPC, side C - float mTPCalignmentZ = 0.f; ///< Global Z shift of the TPC detector. It is applied at the end of the transformation. - float mScaleVtoSVsideA = 0.f; ///< scale for v->sv for TPC side A - float mScaleVtoSVsideC = 0.f; ///< scale for v->sv for TPC side C - float mScaleSVtoVsideA = 0.f; ///< scale for sv->v for TPC side A - float mScaleSVtoVsideC = 0.f; ///< scale for sv->v for TPC side C - - RocInfo mRocInfos[NumberOfRocs + 1]; ///< array of roc information [fixed size] - RowInfo mRowInfos[MaxNumberOfRows + 1]; ///< array of row information [fixed size] - - ClassDefNV(TPCFastTransformGeo, 2); + float mTPCzLength = 0.f; ///< Z length of one TPC side (A or C) + + RocInfo mRocInfos[NumberOfRocs + 1]; ///< array of roc information [fixed size] + RowInfo mRowInfos[MaxNumberOfRows + 1]; ///< array of row information [fixed size] + + ClassDefNV(TPCFastTransformGeo, 3); }; // ======================================================================= @@ -262,11 +225,10 @@ GPUdi() void TPCFastTransformGeo::convVtoLocal(int32_t roc, float v, float& lz) { /// convert UV -> Local c.s. if (roc < NumberOfRocsA) { // TPC side A - lz = mTPCzLengthA - v; + lz = mTPCzLength - v; } else { // TPC side C - lz = v - mTPCzLengthC; // drift direction is mirrored on C-side + lz = v - mTPCzLength; // drift direction is mirrored on C-side } - lz += mTPCalignmentZ; // global TPC alignment } GPUdi() void TPCFastTransformGeo::convUVtoLocal(int32_t roc, float u, float v, float& ly, float& lz) const @@ -274,59 +236,25 @@ GPUdi() void TPCFastTransformGeo::convUVtoLocal(int32_t roc, float u, float v, f /// convert UV -> Local c.s. if (roc < NumberOfRocsA) { // TPC side A ly = u; - lz = mTPCzLengthA - v; + lz = mTPCzLength - v; } else { // TPC side C ly = -u; // pads are mirrorred on C-side - lz = v - mTPCzLengthC; // drift direction is mirrored on C-side + lz = v - mTPCzLength; // drift direction is mirrored on C-side } - lz += mTPCalignmentZ; // global TPC alignment } GPUdi() void TPCFastTransformGeo::convLocalToUV(int32_t roc, float ly, float lz, float& u, float& v) const { /// convert Local-> UV c.s. - lz = lz - mTPCalignmentZ; // global TPC alignment - if (roc < NumberOfRocsA) { // TPC side A + if (roc < NumberOfRocsA) { // TPC side A u = ly; - v = mTPCzLengthA - lz; + v = mTPCzLength - lz; } else { // TPC side C u = -ly; // pads are mirrorred on C-side - v = lz + mTPCzLengthC; // drift direction is mirrored on C-side + v = lz + mTPCzLength; // drift direction is mirrored on C-side } } -GPUdi() void TPCFastTransformGeo::convUVtoScaledUV(int32_t roc, int32_t row, float u, float v, float& su, float& sv) const -{ - /// convert UV -> Scaled UV - const RowInfo& rowInfo = getRowInfo(row); - su = (u - rowInfo.u0) * rowInfo.scaleUtoSU; - if (roc < NumberOfRocsA) { - sv = v * mScaleVtoSVsideA; - } else { - sv = v * mScaleVtoSVsideC; - } -} - -GPUdi() void TPCFastTransformGeo::convScaledUVtoUV(int32_t roc, int32_t row, float su, float sv, float& u, float& v) const -{ - /// convert Scaled UV -> UV - const RowInfo& rowInfo = getRowInfo(row); - u = rowInfo.u0 + su * rowInfo.scaleSUtoU; - if (roc < NumberOfRocsA) { - v = sv * mScaleSVtoVsideA; - } else { - v = sv * mScaleSVtoVsideC; - } -} - -GPUdi() void TPCFastTransformGeo::convScaledUVtoLocal(int32_t roc, int32_t row, float su, float sv, float& ly, float& lz) const -{ - /// convert Scaled UV -> Local c.s. - float u, v; - convScaledUVtoUV(roc, row, su, sv, u, v); - convUVtoLocal(roc, u, v, ly, lz); -} - GPUdi() float TPCFastTransformGeo::convPadToU(int32_t row, float pad) const { /// convert Pad coordinate -> U diff --git a/GPU/TPCFastTransformation/TPCFastTransformManager.cxx b/GPU/TPCFastTransformation/TPCFastTransformManager.cxx index aa28b6a414876..c553d9cc6dac1 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformManager.cxx +++ b/GPU/TPCFastTransformation/TPCFastTransformManager.cxx @@ -22,6 +22,7 @@ #include "AliTPCcalibDB.h" #include "TPCFastTransform.h" #include "Spline2DHelper.h" +blabla using namespace o2::gpu; From 6e876d178f27aa1c8683b14006562317137a5060 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Thu, 6 Mar 2025 23:58:56 +0000 Subject: [PATCH 507/701] TPC Splines: init inverse from the inverse voxel map; rebase --- .../TPCFastSpaceChargeCorrectionHelper.h | 13 +- .../TPCFastSpaceChargeCorrectionHelper.cxx | 921 +++++++++--------- .../src/TPCFastTransformHelperO2.cxx | 4 +- .../test/testTPCFastTransform.cxx | 61 +- GPU/TPCFastTransformation/Spline1DSpec.h | 36 +- .../TPCFastSpaceChargeCorrection.cxx | 201 ++-- .../TPCFastSpaceChargeCorrection.h | 334 +++---- .../TPCFastSpaceChargeCorrectionMap.h | 24 +- GPU/TPCFastTransformation/TPCFastTransform.h | 347 +++---- .../TPCFastTransformGeo.cxx | 55 +- .../TPCFastTransformGeo.h | 160 ++- .../TPCFastTransformManager.cxx | 336 ------- .../TPCFastTransformManager.h | 86 -- .../TPCFastTransformationLinkDef_O2.h | 7 +- .../macro/TPCFastTransformInit.C | 211 ++-- 15 files changed, 1159 insertions(+), 1637 deletions(-) delete mode 100644 GPU/TPCFastTransformation/TPCFastTransformManager.cxx delete mode 100644 GPU/TPCFastTransformation/TPCFastTransformManager.h diff --git a/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h b/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h index eff4972679ed8..abbc5b7116b2d 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h @@ -86,15 +86,14 @@ class TPCFastSpaceChargeCorrectionHelper /// Create SpaceCharge correction out of the voxel tree std::unique_ptr createFromTrackResiduals( - const o2::tpc::TrackResiduals& trackResiduals, TTree* voxResTree, bool useSmoothed = false, bool invertSigns = false); + const o2::tpc::TrackResiduals& trackResiduals, TTree* voxResTree, TTree* voxResTreeInverse, bool useSmoothed, bool invertSigns); + /// _______________ Utilities ________________________ const TPCFastTransformGeo& getGeometry() { return mGeo; } TPCFastSpaceChargeCorrectionMap& getCorrectionMap() { return mCorrectionMap; } - void fillSpaceChargeCorrectionFromMap(TPCFastSpaceChargeCorrection& correction); - void testGeometry(const TPCFastTransformGeo& geo) const; /// initialise inverse transformation @@ -103,15 +102,13 @@ class TPCFastSpaceChargeCorrectionHelper /// initialise inverse transformation from linear combination of several input corrections void initInverse(std::vector& corrections, const std::vector& scaling, bool prn); + void MergeCorrections(std::vector& corrections, const std::vector& scaling, bool prn); + private: /// geometry initialization void initGeometry(); - /// get space charge correction in internal TPCFastTransform coordinates u,v->dx,du,dv - void getSpaceChargeCorrection(const TPCFastSpaceChargeCorrection& correction, int slice, int row, o2::gpu::TPCFastSpaceChargeCorrectionMap::CorrectionPoint p, double& su, double& sv, double& dx, double& du, double& dv); - - /// initialise max drift length - void initMaxDriftLength(o2::gpu::TPCFastSpaceChargeCorrection& correction, bool prn); + void fillSpaceChargeCorrectionFromMap(TPCFastSpaceChargeCorrection& correction, bool processingInverseCorrection); static TPCFastSpaceChargeCorrectionHelper* sInstance; ///< singleton instance bool mIsInitialized = 0; ///< initialization flag diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index 710a4356dd457..92817063831f6 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -32,6 +32,7 @@ #include "TTreeReader.h" #include "TTreeReaderValue.h" #include "ROOT/TTreeProcessorMT.hxx" +#include using namespace o2::gpu; @@ -112,7 +113,7 @@ void TPCFastSpaceChargeCorrectionHelper::setNthreadsToMaximum() } } -void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFastSpaceChargeCorrection& correction) +void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFastSpaceChargeCorrection& correction, bool processingInverseCorrection) { // calculate correction map: dx,du,dv = ( origTransform() -> x,u,v) - fastTransformNominal:x,u,v // for the future: switch TOF correction off for a while @@ -130,39 +131,64 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas LOG(info) << "fast space charge correction helper: init from data points"; - for (int roc = 0; roc < correction.getGeometry().getNumberOfRocs(); roc++) { + for (int sector = 0; sector < correction.getGeometry().getNumberOfSectors(); sector++) { auto myThread = [&](int iThread) { for (int row = iThread; row < correction.getGeometry().getNumberOfRows(); row += mNthreads) { - TPCFastSpaceChargeCorrection::SplineType& spline = correction.getSpline(roc, row); + TPCFastSpaceChargeCorrection::SplineType& spline = correction.getSpline(sector, row); Spline2DHelper helper; - float* splineParameters = correction.getSplineData(roc, row); - const std::vector& data = mCorrectionMap.getPoints(roc, row); + std::vector splineParameters; + splineParameters.resize(spline.getNumberOfParameters()); + + const std::vector& data = mCorrectionMap.getPoints(sector, row); int nDataPoints = data.size(); - auto& info = correction.getRocRowInfo(roc, row); - info.resetMaxValues(); + auto& info = correction.getSectorRowInfo(sector, row); + if (!processingInverseCorrection) { + info.resetMaxValues(); + } if (nDataPoints >= 4) { - std::vector pointSU(nDataPoints); - std::vector pointSV(nDataPoints); + std::vector pointGU(nDataPoints); + std::vector pointGV(nDataPoints); std::vector pointCorr(3 * nDataPoints); // 3 dimensions for (int i = 0; i < nDataPoints; ++i) { - double su, sv, dx, du, dv; - getSpaceChargeCorrection(correction, roc, row, data[i], su, sv, dx, du, dv); - pointSU[i] = su; - pointSV[i] = sv; - pointCorr[3 * i + 0] = dx; - pointCorr[3 * i + 1] = du; - pointCorr[3 * i + 2] = dv; - info.updateMaxValues(20. * dx, 20. * du, 20. * dv); + o2::gpu::TPCFastSpaceChargeCorrectionMap::CorrectionPoint p = data[i]; + // not corrected grid coordinates + auto [gu, gv, scale] = correction.convLocalToGrid(sector, row, p.mY, p.mZ); + if (scale - 1.f > 1.e-6) { // point is outside the grid + continue; + } + pointGU[i] = gu; + pointGV[i] = gv; + pointCorr[3 * i + 0] = p.mDx; + pointCorr[3 * i + 1] = p.mDy; + pointCorr[3 * i + 2] = p.mDz; + if (!processingInverseCorrection) { + info.updateMaxValues(20. * p.mDx, 20. * p.mDy, 20. * p.mDz); + } } - helper.approximateDataPoints(spline, splineParameters, 0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax(), &pointSU[0], - &pointSV[0], &pointCorr[0], nDataPoints); + helper.approximateDataPoints(spline, splineParameters.data(), 0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax(), &pointGU[0], + &pointGV[0], &pointCorr[0], nDataPoints); } else { for (int i = 0; i < spline.getNumberOfParameters(); i++) { splineParameters[i] = 0.f; } } + + if (processingInverseCorrection) { + float* splineX = correction.getSplineData(sector, row, 1); + float* splineYZ = correction.getSplineData(sector, row, 2); + for (int i = 0; i < spline.getNumberOfParameters() / 3; i++) { + splineX[i] = splineParameters[3 * i + 0]; + splineYZ[2 * i + 0] = splineParameters[3 * i + 1]; + splineYZ[2 * i + 1] = splineParameters[3 * i + 2]; + } + } else { + float* splineXYZ = correction.getSplineData(sector, row); + for (int i = 0; i < spline.getNumberOfParameters(); i++) { + splineXYZ[i] = splineParameters[i]; + } + } } // row }; // thread @@ -178,57 +204,30 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas th.join(); } - } // roc + } // sector watch.Stop(); LOGP(info, "Space charge correction tooks: {}s", watch.RealTime()); - - initInverse(correction, 0); -} - -void TPCFastSpaceChargeCorrectionHelper::getSpaceChargeCorrection(const TPCFastSpaceChargeCorrection& correction, int roc, int row, o2::gpu::TPCFastSpaceChargeCorrectionMap::CorrectionPoint p, - double& su, double& sv, double& dx, double& du, double& dv) -{ - // get space charge correction in internal TPCFastTransform coordinates su,sv->dx,du,dv - - if (!mIsInitialized) { - initGeometry(); - } - - // not corrected coordinates in u,v - float u = 0.f, v = 0.f, fsu = 0.f, fsv = 0.f; - mGeo.convLocalToUV(roc, p.mY, p.mZ, u, v); - correction.convUVtoGrid(roc, row, u, v, fsu, fsv); - // mGeo.convUVtoScaledUV(roc, row, u, v, fsu, fsv); - su = fsu; - sv = fsv; - // corrected coordinates in u,v - float u1 = 0.f, v1 = 0.f; - mGeo.convLocalToUV(roc, p.mY + p.mDy, p.mZ + p.mDz, u1, v1); - - dx = p.mDx; - du = u1 - u; - dv = v1 - v; -} +} // fillSpaceChargeCorrectionFromMap std::unique_ptr TPCFastSpaceChargeCorrectionHelper::createFromGlobalCorrection( - std::function correctionGlobal, const int nKnotsY, const int nKnotsZ) { /// creates TPCFastSpaceChargeCorrection object from a continious space charge correction in global coordinates - auto correctionLocal = [&](int roc, int irow, double ly, double lz, + auto correctionLocal = [&](int sector, int irow, double ly, double lz, double& dlx, double& dly, double& dlz) { double lx = mGeo.getRowInfo(irow).x; float gx, gy, gz; - mGeo.convLocalToGlobal(roc, lx, ly, lz, gx, gy, gz); + mGeo.convLocalToGlobal(sector, lx, ly, lz, gx, gy, gz); double dgx, dgy, dgz; - correctionGlobal(roc, gx, gy, gz, dgx, dgy, dgz); + correctionGlobal(sector, gx, gy, gz, dgx, dgy, dgz); float lx1, ly1, lz1; - mGeo.convGlobalToLocal(roc, gx + dgx, gy + dgy, gz + dgz, lx1, ly1, lz1); + mGeo.convGlobalToLocal(sector, gx + dgx, gy + dgy, gz + dgz, lx1, ly1, lz1); dlx = lx1 - lx; dly = ly1 - ly; dlz = lz1 - lz; @@ -237,7 +236,7 @@ std::unique_ptr TPCFastSpaceChargeCorrectionHelper } std::unique_ptr TPCFastSpaceChargeCorrectionHelper::createFromLocalCorrection( - std::function correctionLocal, + std::function correctionLocal, const int nKnotsY, const int nKnotsZ) { /// creates TPCFastSpaceChargeCorrection object from a continious space charge correction in local coordinates @@ -282,28 +281,24 @@ std::unique_ptr TPCFastSpaceChargeCorrectionHelper /// set space charge correction in the local coordinates /// as a continious function - int nRocs = mGeo.getNumberOfRocs(); + int nSectors = mGeo.getNumberOfSectors(); int nRows = mGeo.getNumberOfRows(); - mCorrectionMap.init(nRocs, nRows); + mCorrectionMap.init(nSectors, nRows); - for (int iRoc = 0; iRoc < nRocs; iRoc++) { + for (int iSector = 0; iSector < nSectors; iSector++) { auto myThread = [&](int iThread) { for (int iRow = iThread; iRow < nRows; iRow += mNthreads) { const auto& info = mGeo.getRowInfo(iRow); - double vMax = mGeo.getTPCzLength(); - double dv = vMax / (6. * (nKnotsZ - 1)); - + double dl = mGeo.getTPCzLength() / (6. * (nKnotsZ - 1)); double dpad = info.maxPad / (6. * (nKnotsY - 1)); for (double pad = 0; pad < info.maxPad + .5 * dpad; pad += dpad) { - float u = mGeo.convPadToU(iRow, pad); - for (double v = 0.; v < vMax + .5 * dv; v += dv) { - float ly, lz; - mGeo.convUVtoLocal(iRoc, u, v, ly, lz); + for (double l = 0.; l < mGeo.getTPCzLength() + .5 * dl; l += dl) { + auto [y, z] = mGeo.convPadDriftLengthToLocal(iSector, iRow, pad, l); double dx, dy, dz; - correctionLocal(iRoc, iRow, ly, lz, dx, dy, dz); - mCorrectionMap.addCorrectionPoint(iRoc, iRow, - ly, lz, dx, dy, dz); + correctionLocal(iSector, iRow, y, z, dx, dy, dz); + mCorrectionMap.addCorrectionPoint(iSector, iRow, + y, z, dx, dy, dz); } } } // row @@ -321,20 +316,21 @@ std::unique_ptr TPCFastSpaceChargeCorrectionHelper th.join(); } - } // roc + } // sector - fillSpaceChargeCorrectionFromMap(correction); + fillSpaceChargeCorrectionFromMap(correction, false); + initInverse(correction, false); } return std::move(correctionPtr); -} +} // createFromLocalCorrection void TPCFastSpaceChargeCorrectionHelper::testGeometry(const TPCFastTransformGeo& geo) const { const Mapper& mapper = Mapper::instance(); - if (geo.getNumberOfRocs() != Sector::MAXSECTOR) { - LOG(fatal) << "Wrong number of sectors :" << geo.getNumberOfRocs() << " instead of " << Sector::MAXSECTOR << std::endl; + if (geo.getNumberOfSectors() != Sector::MAXSECTOR) { + LOG(fatal) << "Wrong number of sectors :" << geo.getNumberOfSectors() << " instead of " << Sector::MAXSECTOR << std::endl; } if (geo.getNumberOfRows() != mapper.getNumberOfRows()) { @@ -384,7 +380,7 @@ void TPCFastSpaceChargeCorrectionHelper::testGeometry(const TPCFastTransformGeo& } std::unique_ptr TPCFastSpaceChargeCorrectionHelper::createFromTrackResiduals( - const o2::tpc::TrackResiduals& trackResiduals, TTree* voxResTree, bool useSmoothed, bool invertSigns) + const o2::tpc::TrackResiduals& trackResiduals, TTree* voxResTree, TTree* voxResTreeInverse, bool useSmoothed, bool invertSigns) { // create o2::gpu::TPCFastSpaceChargeCorrection from o2::tpc::TrackResiduals::VoxRes voxel tree @@ -399,9 +395,6 @@ std::unique_ptr TPCFastSpaceChargeCorrect auto* helper = o2::tpc::TPCFastSpaceChargeCorrectionHelper::instance(); const o2::gpu::TPCFastTransformGeo& geo = helper->getGeometry(); - o2::gpu::TPCFastSpaceChargeCorrectionMap& map = helper->getCorrectionMap(); - map.init(geo.getNumberOfRocs(), geo.getNumberOfRows()); - int nY2Xbins = trackResiduals.getNY2XBins(); int nZ2Xbins = trackResiduals.getNZ2XBins(); @@ -476,7 +469,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect // std::cout << "n knots Z: " << nKnotsZ << std::endl; const int nRows = geo.getNumberOfRows(); - const int nROCs = geo.getNumberOfRocs(); + const int nSectors = geo.getNumberOfSectors(); { // create the correction object @@ -497,11 +490,11 @@ std::unique_ptr TPCFastSpaceChargeCorrect } // .. create the correction object // set the grid borders - for (int iRoc = 0; iRoc < geo.getNumberOfRocs(); iRoc++) { + for (int iSector = 0; iSector < geo.getNumberOfSectors(); iSector++) { for (int iRow = 0; iRow < geo.getNumberOfRows(); iRow++) { const auto& rowInfo = geo.getRowInfo(iRow); - auto& info = correction.getRocRowInfo(iRoc, iRow); - const auto& spline = correction.getSpline(iRoc, iRow); + auto& info = correction.getSectorRowInfo(iSector, iRow); + const auto& spline = correction.getSpline(iSector, iRow); double yMin = rowInfo.x * trackResiduals.getY2X(iRow, 0); double yMax = rowInfo.x * trackResiduals.getY2X(iRow, trackResiduals.getNY2XBins() - 1); double zMin = rowInfo.x * trackResiduals.getZ2X(0); @@ -514,366 +507,319 @@ std::unique_ptr TPCFastSpaceChargeCorrect info.scaleUtoGrid = spline.getGridX1().getUmax() / (uMax - uMin); info.gridV0 = vMin; info.scaleVtoGrid = spline.getGridX2().getUmax() / (vMax - vMin); - // std::cout << " iRoc " << iRoc << " iRow " << iRow << " uMin: " << uMin << " uMax: " << uMax << " vMin: " << vMin << " vMax: " << vMax + info.gridCorrU0 = info.gridU0; + info.gridCorrV0 = info.gridV0; + info.scaleCorrUtoGrid = info.scaleUtoGrid; + info.scaleCorrVtoGrid = info.scaleVtoGrid; + + // std::cout << " iSector " << iSector << " iRow " << iRow << " uMin: " << uMin << " uMax: " << uMax << " vMin: " << vMin << " vMax: " << vMax //<< " grid scale u "<< info.scaleUtoGrid << " grid scale v "<< info.scaleVtoGrid<< std::endl; } } LOG(info) << "fast space charge correction helper: preparation took " << watch1.RealTime() << "s"; - LOG(info) << "fast space charge correction helper: fill data points from track residuals.. "; - - TStopwatch watch3; + for (int processingInverseCorrection = 0; processingInverseCorrection < 2; processingInverseCorrection++) { - // read the data ROC by ROC + TTree* currentTree = (processingInverseCorrection) ? voxResTreeInverse : voxResTree; - // data in the tree is not sorted by row - // first find which data belong to which row + if (!currentTree) { + continue; + } - struct VoxelData { - int mNentries{0}; // number of entries - float mX, mY, mZ; // mean position in the local coordinates - float mCx, mCy, mCz; // corrections to the local coordinates - }; + LOG(info) << "fast space charge correction helper: " << ((processingInverseCorrection) ? "inverse" : "direct") + << " : fill data points from track residuals.. "; - std::vector vRocData[nRows * nROCs]; - for (int ir = 0; ir < nRows * nROCs; ir++) { - vRocData[ir].resize(nY2Xbins * nZ2Xbins); - } + TStopwatch watch3; + o2::gpu::TPCFastSpaceChargeCorrectionMap& map = helper->getCorrectionMap(); + map.init(geo.getNumberOfSectors(), geo.getNumberOfRows()); - { // read data from the tree to vRocData + // read the data Sector by Sector - ROOT::TTreeProcessorMT processor(*voxResTree, mNthreads); + // data in the tree is not sorted by row + // first find which data belong to which row - auto myThread = [&](TTreeReader& readerSubRange) { - TTreeReaderValue v(readerSubRange, "voxRes"); - while (readerSubRange.Next()) { - int iRoc = (int)v->bsec; - if (iRoc < 0 || iRoc >= nROCs) { - LOG(fatal) << "Error reading voxels: voxel ROC number " << iRoc << " is out of range"; - continue; - } - int iRow = (int)v->bvox[o2::tpc::TrackResiduals::VoxX]; // bin number in x (= pad row) - if (iRow < 0 || iRow >= nRows) { - LOG(fatal) << "Row number " << iRow << " is out of range"; - } - int iy = v->bvox[o2::tpc::TrackResiduals::VoxF]; // bin number in y/x 0..14 - int iz = v->bvox[o2::tpc::TrackResiduals::VoxZ]; // bin number in z/x 0..4 - auto& data = vRocData[iRoc * nRows + iRow][iy * nZ2Xbins + iz]; - data.mNentries = (int)v->stat[o2::tpc::TrackResiduals::VoxV]; - data.mX = v->stat[o2::tpc::TrackResiduals::VoxX]; - data.mY = v->stat[o2::tpc::TrackResiduals::VoxF]; - data.mZ = v->stat[o2::tpc::TrackResiduals::VoxZ]; - data.mCx = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResX] : v->D[o2::tpc::TrackResiduals::ResX]; - data.mCy = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResY] : v->D[o2::tpc::TrackResiduals::ResY]; - data.mCz = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResZ] : v->D[o2::tpc::TrackResiduals::ResZ]; - if (0 && data.mNentries < 1) { - data.mCx = 0.; - data.mCy = 0.; - data.mCz = 0.; - data.mNentries = 1; - } - } + struct VoxelData { + int mNentries{0}; // number of entries + float mX, mY, mZ; // mean position in the local coordinates + float mCx, mCy, mCz; // corrections to the local coordinates }; - processor.Process(myThread); - } - for (int iRoc = 0; iRoc < nROCs; iRoc++) { + std::vector vSectorData[nRows * nSectors]; + for (int ir = 0; ir < nRows * nSectors; ir++) { + vSectorData[ir].resize(nY2Xbins * nZ2Xbins); + } - // now process the data row-by-row + { // read data from the tree to vSectorData - auto myThread = [&](int iThread, int nTreads) { - struct Voxel { - float mY, mZ; // not-distorted local coordinates - float mDy, mDz; // bin size - int mSmoothingStep{100}; // is the voxel data original or smoothed at this step + ROOT::TTreeProcessorMT processor(*currentTree, mNthreads); + + auto myThread = [&](TTreeReader& readerSubRange) { + TTreeReaderValue v(readerSubRange, "voxRes"); + while (readerSubRange.Next()) { + int iSector = (int)v->bsec; + if (iSector < 0 || iSector >= nSectors) { + LOG(fatal) << "Error reading voxels: voxel Sector number " << iSector << " is out of range"; + continue; + } + int iRow = (int)v->bvox[o2::tpc::TrackResiduals::VoxX]; // bin number in x (= pad row) + if (iRow < 0 || iRow >= nRows) { + LOG(fatal) << "Row number " << iRow << " is out of range"; + } + int iy = v->bvox[o2::tpc::TrackResiduals::VoxF]; // bin number in y/x 0..14 + int iz = v->bvox[o2::tpc::TrackResiduals::VoxZ]; // bin number in z/x 0..4 + auto& data = vSectorData[iSector * nRows + iRow][iy * nZ2Xbins + iz]; + data.mNentries = (int)v->stat[o2::tpc::TrackResiduals::VoxV]; + data.mX = v->stat[o2::tpc::TrackResiduals::VoxX]; + data.mY = v->stat[o2::tpc::TrackResiduals::VoxF]; + data.mZ = v->stat[o2::tpc::TrackResiduals::VoxZ]; + data.mCx = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResX] : v->D[o2::tpc::TrackResiduals::ResX]; + data.mCy = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResY] : v->D[o2::tpc::TrackResiduals::ResY]; + data.mCz = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResZ] : v->D[o2::tpc::TrackResiduals::ResZ]; + if (0 && data.mNentries < 1) { + data.mCx = 0.; + data.mCy = 0.; + data.mCz = 0.; + data.mNentries = 1; + } + } }; + processor.Process(myThread); + } - std::vector vRowVoxels(nY2Xbins * nZ2Xbins); + for (int iSector = 0; iSector < nSectors; iSector++) { - for (int iRow = iThread; iRow < nRows; iRow += nTreads) { - // LOG(info) << "Processing ROC " << iRoc << " row " << iRow; + // now process the data row-by-row - // complete the voxel data + auto myThread = [&](int iThread, int nTreads) { + struct Voxel { + float mY, mZ; // not-distorted local coordinates + float mDy, mDz; // bin size + int mSmoothingStep{100}; // is the voxel data original or smoothed at this step + }; - { - int xBin = iRow; - double x = trackResiduals.getX(xBin); // radius of the pad row - bool isDataFound = false; - for (int iy = 0; iy < nY2Xbins; iy++) { - for (int iz = 0; iz < nZ2Xbins; iz++) { - auto& data = vRocData[iRoc * nRows + iRow][iy * nZ2Xbins + iz]; - auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; - // y/x coordinate of the bin ~-0.15 ... 0.15 - double y2x = trackResiduals.getY2X(xBin, iy); - // z/x coordinate of the bin 0.1 .. 0.9 - double z2x = trackResiduals.getZ2X(iz); - vox.mY = x * y2x; - vox.mZ = x * z2x; - vox.mDy = x / trackResiduals.getDY2XI(xBin, iy); - vox.mDz = x * trackResiduals.getDZ2X(iz); - if (iRoc >= geo.getNumberOfRocsA()) { - vox.mZ = -vox.mZ; - } - data.mY *= x; - data.mZ *= x; - /* - if ( fabs(x - data.mX) > 0.01 || fabs(vox.mY - data.mY) > 5. || fabs(vox.mZ - data.mZ) > 5.) { - std::cout - << " roc " << iRoc << " row " << iRow - << " voxel x " << x << " y " << vox.mY << " z " << vox.mZ - << " data x " << data.mX << " y " << data.mY << " z " << data.mZ - << std::endl; - } - */ - if (0) { // debug: always use voxel center instead of the mean position - data.mY = vox.mY; - data.mZ = vox.mZ; - } - if (data.mNentries < 1) { // no data - data.mCx = 0.; - data.mCy = 0.; - data.mCz = 0.; - data.mY = vox.mY; - data.mZ = vox.mZ; - vox.mSmoothingStep = 100; - } else { // voxel contains data - if (invertSigns) { - data.mCx *= -1.; - data.mCy *= -1.; - data.mCz *= -1.; - } - vox.mSmoothingStep = 0; // original data - isDataFound = true; - } - } - } + std::vector vRowVoxels(nY2Xbins * nZ2Xbins); + + for (int iRow = iThread; iRow < nRows; iRow += nTreads) { + // LOG(info) << "Processing Sector " << iSector << " row " << iRow; - if (!isDataFound) { // fill everything with 0 + // complete the voxel data + + { + int xBin = iRow; + double x = trackResiduals.getX(xBin); // radius of the pad row + bool isDataFound = false; for (int iy = 0; iy < nY2Xbins; iy++) { for (int iz = 0; iz < nZ2Xbins; iz++) { - vRowVoxels[iy * nZ2Xbins + iz].mSmoothingStep = 0; + auto& data = vSectorData[iSector * nRows + iRow][iy * nZ2Xbins + iz]; + auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; + // y/x coordinate of the bin ~-0.15 ... 0.15 + double y2x = trackResiduals.getY2X(xBin, iy); + // z/x coordinate of the bin 0.1 .. 0.9 + double z2x = trackResiduals.getZ2X(iz); + vox.mY = x * y2x; + vox.mZ = x * z2x; + vox.mDy = x / trackResiduals.getDY2XI(xBin, iy); + vox.mDz = x * trackResiduals.getDZ2X(iz); + if (iSector >= geo.getNumberOfSectorsA()) { + vox.mZ = -vox.mZ; + } + data.mY *= x; + data.mZ *= x; + /* + if ( fabs(x - data.mX) > 0.01 || fabs(vox.mY - data.mY) > 5. || fabs(vox.mZ - data.mZ) > 5.) { + std::cout + << " sector " << iSector << " row " << iRow + << " voxel x " << x << " y " << vox.mY << " z " << vox.mZ + << " data x " << data.mX << " y " << data.mY << " z " << data.mZ + << std::endl; + } + */ + if (0) { // debug: always use voxel center instead of the mean position + data.mY = vox.mY; + data.mZ = vox.mZ; + } + if (data.mNentries < 1) { // no data + data.mCx = 0.; + data.mCy = 0.; + data.mCz = 0.; + data.mY = vox.mY; + data.mZ = vox.mZ; + vox.mSmoothingStep = 100; + } else { // voxel contains data + if (invertSigns) { + data.mCx *= -1.; + data.mCy *= -1.; + data.mCz *= -1.; + } + vox.mSmoothingStep = 0; // original data + isDataFound = true; + } } } - } - } // complete the voxel data - // repare the voxel data: fill empty voxels - - int nRepairs = 0; - - for (int ismooth = 1; ismooth <= 2; ismooth++) { - for (int iy = 0; iy < nY2Xbins; iy++) { - for (int iz = 0; iz < nZ2Xbins; iz++) { - auto& data = vRocData[iRoc * nRows + iRow][iy * nZ2Xbins + iz]; - auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; - if (vox.mSmoothingStep <= ismooth) { // already filled - continue; - } - nRepairs++; - data.mCx = 0.; - data.mCy = 0.; - data.mCz = 0.; - double w = 0.; - bool filled = false; - auto update = [&](int iy1, int iz1) { - auto& data1 = vRocData[iRoc * nRows + iRow][iy1 * nZ2Xbins + iz1]; - auto& vox1 = vRowVoxels[iy1 * nZ2Xbins + iz1]; - if (vox1.mSmoothingStep >= ismooth) { - return false; + if (!isDataFound) { // fill everything with 0 + for (int iy = 0; iy < nY2Xbins; iy++) { + for (int iz = 0; iz < nZ2Xbins; iz++) { + vRowVoxels[iy * nZ2Xbins + iz].mSmoothingStep = 0; } - double w1 = 1. / (abs(iy - iy1) + abs(iz - iz1) + 1); - data.mCx += w1 * data1.mCx; - data.mCy += w1 * data1.mCy; - data.mCz += w1 * data1.mCz; - w += w1; - filled = true; - return true; - }; - - for (int iy1 = iy - 1; iy1 >= 0 && !update(iy1, iz); iy1--) { - } - for (int iy1 = iy + 1; iy1 < nY2Xbins && !update(iy1, iz); iy1++) { - } - for (int iz1 = iz - 1; iz1 >= 0 && !update(iy, iz1); iz1--) { - } - for (int iz1 = iz + 1; iz1 < nZ2Xbins && !update(iy, iz1); iz1++) { } + } + } // complete the voxel data - if (filled) { - data.mCx /= w; - data.mCy /= w; - data.mCz /= w; - vox.mSmoothingStep = ismooth; - } - } // iz - } // iy - } // ismooth + // repare the voxel data: fill empty voxels - if (nRepairs > 0) { - LOG(debug) << "ROC " << iRoc << " row " << iRow << ": " << nRepairs << " voxel repairs for " << nY2Xbins * nZ2Xbins << " voxels"; - } + int nRepairs = 0; - // feed the row data to the helper + for (int ismooth = 1; ismooth <= 2; ismooth++) { + for (int iy = 0; iy < nY2Xbins; iy++) { + for (int iz = 0; iz < nZ2Xbins; iz++) { + auto& data = vSectorData[iSector * nRows + iRow][iy * nZ2Xbins + iz]; + auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; + if (vox.mSmoothingStep <= ismooth) { // already filled + continue; + } + nRepairs++; + data.mCx = 0.; + data.mCy = 0.; + data.mCz = 0.; + double w = 0.; + bool filled = false; + auto update = [&](int iy1, int iz1) { + auto& data1 = vSectorData[iSector * nRows + iRow][iy1 * nZ2Xbins + iz1]; + auto& vox1 = vRowVoxels[iy1 * nZ2Xbins + iz1]; + if (vox1.mSmoothingStep >= ismooth) { + return false; + } + double w1 = 1. / (abs(iy - iy1) + abs(iz - iz1) + 1); + data.mCx += w1 * data1.mCx; + data.mCy += w1 * data1.mCy; + data.mCz += w1 * data1.mCz; + w += w1; + filled = true; + return true; + }; + + for (int iy1 = iy - 1; iy1 >= 0 && !update(iy1, iz); iy1--) { + } + for (int iy1 = iy + 1; iy1 < nY2Xbins && !update(iy1, iz); iy1++) { + } + for (int iz1 = iz - 1; iz1 >= 0 && !update(iy, iz1); iz1--) { + } + for (int iz1 = iz + 1; iz1 < nZ2Xbins && !update(iy, iz1); iz1++) { + } - auto& info = correction.getRocRowInfo(iRoc, iRow); - const auto& spline = correction.getSpline(iRoc, iRow); + if (filled) { + data.mCx /= w; + data.mCy /= w; + data.mCz /= w; + vox.mSmoothingStep = ismooth; + } + } // iz + } // iy + } // ismooth - auto addEdge = [&](int iy1, int iz1, int iy2, int iz2, int nSteps) { - auto& data1 = vRocData[iRoc * nRows + iRow][iy1 * nZ2Xbins + iz1]; - auto& vox1 = vRowVoxels[iy1 * nZ2Xbins + iz1]; - auto& data2 = vRocData[iRoc * nRows + iRow][iy2 * nZ2Xbins + iz2]; - auto& vox2 = vRowVoxels[iy2 * nZ2Xbins + iz2]; - if (vox1.mSmoothingStep > 2) { - LOG(fatal) << "empty voxel is not repared: y " << iy1 << " z " << iz1; - } - if (vox2.mSmoothingStep > 2) { - LOG(fatal) << "empty voxel is not repared: y " << iy2 << " z " << iz2; - } - double y1 = vox1.mY; - double z1 = vox1.mZ; - double cx1 = data1.mCx; - double cy1 = data1.mCy; - double cz1 = data1.mCz; - double y2 = vox2.mY; - double z2 = vox2.mZ; - double cx2 = data2.mCx; - double cy2 = data2.mCy; - double cz2 = data2.mCz; - - for (int is = 0; is < nSteps; is++) { - double s2 = is / (double)nSteps; - double s1 = 1. - s2; - double y = s1 * y1 + s2 * y2; - double z = s1 * z1 + s2 * z2; - double cx = s1 * cx1 + s2 * cx2; - double cy = s1 * cy1 + s2 * cy2; - double cz = s1 * cz1 + s2 * cz2; - map.addCorrectionPoint(iRoc, iRow, y, z, cx, cy, cz); + if (nRepairs > 0) { + LOG(debug) << "Sector " << iSector << " row " << iRow << ": " << nRepairs << " voxel repairs for " << nY2Xbins * nZ2Xbins << " voxels"; } - }; - for (int iy = 0; iy < nY2Xbins; iy++) { - for (int iz = 0; iz < nZ2Xbins - 1; iz++) { - addEdge(iy, iz, iy, iz + 1, 3); - } - addEdge(iy, nZ2Xbins - 1, iy, nZ2Xbins - 1, 1); - } + // feed the row data to the helper - for (int iz = 0; iz < nZ2Xbins; iz++) { - for (int iy = 0; iy < nY2Xbins - 1; iy++) { - addEdge(iy, iz, iy + 1, iz, 3); - } - addEdge(nY2Xbins - 1, iz, nY2Xbins - 1, iz, 1); - } // iy + auto& info = correction.getSectorRowInfo(iSector, iRow); + const auto& spline = correction.getSpline(iSector, iRow); - } // iRow - }; // myThread + auto addEdge = [&](int iy1, int iz1, int iy2, int iz2, int nSteps) { + auto& data1 = vSectorData[iSector * nRows + iRow][iy1 * nZ2Xbins + iz1]; + auto& vox1 = vRowVoxels[iy1 * nZ2Xbins + iz1]; + auto& data2 = vSectorData[iSector * nRows + iRow][iy2 * nZ2Xbins + iz2]; + auto& vox2 = vRowVoxels[iy2 * nZ2Xbins + iz2]; + if (vox1.mSmoothingStep > 2) { + LOG(fatal) << "empty voxel is not repared: y " << iy1 << " z " << iz1; + } + if (vox2.mSmoothingStep > 2) { + LOG(fatal) << "empty voxel is not repared: y " << iy2 << " z " << iz2; + } + double y1 = vox1.mY; + double z1 = vox1.mZ; + double cx1 = data1.mCx; + double cy1 = data1.mCy; + double cz1 = data1.mCz; + double y2 = vox2.mY; + double z2 = vox2.mZ; + double cx2 = data2.mCx; + double cy2 = data2.mCy; + double cz2 = data2.mCz; + + for (int is = 0; is < nSteps; is++) { + double s2 = is / (double)nSteps; + double s1 = 1. - s2; + double y = s1 * y1 + s2 * y2; + double z = s1 * z1 + s2 * z2; + double cx = s1 * cx1 + s2 * cx2; + double cy = s1 * cy1 + s2 * cy2; + double cz = s1 * cz1 + s2 * cz2; + map.addCorrectionPoint(iSector, iRow, y, z, cx, cy, cz); + } + }; - // run n threads + for (int iy = 0; iy < nY2Xbins; iy++) { + for (int iz = 0; iz < nZ2Xbins - 1; iz++) { + addEdge(iy, iz, iy, iz + 1, 3); + } + addEdge(iy, nZ2Xbins - 1, iy, nZ2Xbins - 1, 1); + } - int nThreads = mNthreads; - // nThreads = 1; + for (int iz = 0; iz < nZ2Xbins; iz++) { + for (int iy = 0; iy < nY2Xbins - 1; iy++) { + addEdge(iy, iz, iy + 1, iz, 3); + } + addEdge(nY2Xbins - 1, iz, nY2Xbins - 1, iz, 1); + } // iy - std::vector threads(nThreads); + } // iRow + }; // myThread - for (int i = 0; i < nThreads; i++) { - threads[i] = std::thread(myThread, i, nThreads); - } + // run n threads - // wait for the threads to finish - for (auto& th : threads) { - th.join(); - } - } // iRoc + int nThreads = mNthreads; + // nThreads = 1; - LOGP(info, "Reading & reparing of the track residuals tooks: {}s", watch3.RealTime()); + std::vector threads(nThreads); - LOG(info) << "fast space charge correction helper: create space charge from the map of data points.."; + for (int i = 0; i < nThreads; i++) { + threads[i] = std::thread(myThread, i, nThreads); + } - TStopwatch watch4; + // wait for the threads to finish + for (auto& th : threads) { + th.join(); + } + } // iSector - helper->fillSpaceChargeCorrectionFromMap(correction); + LOGP(info, "Reading & reparing of the track residuals tooks: {}s", watch3.RealTime()); - LOG(info) << "fast space charge correction helper: creation from the data map took " << watch4.RealTime() << "s"; + LOG(info) << "fast space charge correction helper: create space charge from the map of data points.."; - LOGP(info, "Creation from track residuals tooks in total: {}s", watch.RealTime()); + TStopwatch watch4; - return std::move(correctionPtr); -} + helper->fillSpaceChargeCorrectionFromMap(correction, processingInverseCorrection); -void TPCFastSpaceChargeCorrectionHelper::initMaxDriftLength(o2::gpu::TPCFastSpaceChargeCorrection& correction, bool prn) -{ - /// initialise max drift length + LOG(info) << "fast space charge correction helper: creation from the data map took " << watch4.RealTime() << "s"; - double tpcR2min = mGeo.getRowInfo(0).x - 1.; - tpcR2min = tpcR2min * tpcR2min; - double tpcR2max = mGeo.getRowInfo(mGeo.getNumberOfRows() - 1).x; - tpcR2max = tpcR2max / cos(2 * M_PI / mGeo.getNumberOfRocsA() / 2) + 1.; - tpcR2max = tpcR2max * tpcR2max; + } // processingInverseCorrection - ChebyshevFit1D chebFitter; + if (voxResTree && !voxResTreeInverse) { + LOG(info) << "fast space charge correction helper: init inverse correction from direct correction.."; + TStopwatch watch4; + helper->initInverse(correction, false); + LOG(info) << "fast space charge correction helper: init inverse correction took " << watch4.RealTime() << "s"; + } - for (int roc = 0; roc < mGeo.getNumberOfRocs(); roc++) { - if (prn) { - LOG(info) << "init MaxDriftLength for roc " << roc; - } - double vLength = mGeo.getTPCzLength(); - TPCFastSpaceChargeCorrection::RocInfo& rocInfo = correction.getRocInfo(roc); - rocInfo.vMax = 0.f; + LOGP(info, "Creation from track residuals tooks in total: {}s", watch.RealTime()); - for (int row = 0; row < mGeo.getNumberOfRows(); row++) { - TPCFastSpaceChargeCorrection::RowActiveArea& area = correction.getRocRowInfo(roc, row).activeArea; - area.cvMax = 0; - area.vMax = 0; - area.cuMin = mGeo.convPadToU(row, 0.f); - area.cuMax = -area.cuMin; - chebFitter.reset(4, 0., mGeo.getRowInfo(row).maxPad); - double x = mGeo.getRowInfo(row).x; - for (int pad = 0; pad < mGeo.getRowInfo(row).maxPad; pad++) { - float u = mGeo.convPadToU(row, (float)pad); - float v0 = 0; - float v1 = 1.1 * vLength; - float vLastValid = -1; - float cvLastValid = -1; - while (v1 - v0 > 0.1) { - float v = 0.5 * (v0 + v1); - float dx, du, dv; - correction.getCorrectionInternal(roc, row, u, v, dx, du, dv); - double cx = x + dx; - double cu = u + du; - double cv = v + dv; - double r2 = cx * cx + cu * cu; - if (cv < 0) { - v0 = v; - } else if (cv <= vLength && r2 >= tpcR2min && r2 <= tpcR2max) { - v0 = v; - vLastValid = v; - cvLastValid = cv; - } else { - v1 = v; - } - } - if (vLastValid > 0.) { - chebFitter.addMeasurement(pad, vLastValid); - } - if (area.vMax < vLastValid) { - area.vMax = vLastValid; - } - if (area.cvMax < cvLastValid) { - area.cvMax = cvLastValid; - } - } - chebFitter.fit(); - for (int i = 0; i < 5; i++) { - area.maxDriftLengthCheb[i] = chebFitter.getCoefficients()[i]; - } - if (rocInfo.vMax < area.vMax) { - rocInfo.vMax = area.vMax; - } - } // row - } // roc -} + return std::move(correctionPtr); + +} // createFromTrackResiduals void TPCFastSpaceChargeCorrectionHelper::initInverse(o2::gpu::TPCFastSpaceChargeCorrection& correction, bool prn) { @@ -893,28 +839,24 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector helper; std::vector splineParameters; for (int row = iThread; row < mGeo.getNumberOfRows(); row += mNthreads) { - TPCFastSpaceChargeCorrection::SplineType spline = correction.getSpline(roc, row); + TPCFastSpaceChargeCorrection::SplineType spline = correction.getSpline(sector, row); helper.setSpline(spline, 10, 10); - double x = mGeo.getRowInfo(row).x; - auto& rocRowInfo = correction.getRocRowInfo(roc, row); - std::vector gridU; { const auto& grid = spline.getGridX1(); @@ -942,95 +884,64 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector dataPointCU, dataPointCV, dataPointF; - dataPointCU.reserve(gridU.size() * gridV.size()); - dataPointCV.reserve(gridU.size() * gridV.size()); - dataPointF.reserve(gridU.size() * gridV.size()); - - TPCFastSpaceChargeCorrection::RowActiveArea& area = rocRowInfo.activeArea; - area.cuMin = 1.e10; - area.cuMax = -1.e10; - double cvMin = 1.e10; + std::vector dataPointGridU, dataPointGridV, dataPointF; + dataPointGridU.reserve(gridU.size() * gridV.size()); + dataPointGridV.reserve(gridU.size() * gridV.size()); + dataPointF.reserve(3 * gridU.size() * gridV.size()); for (int iu = 0; iu < gridU.size(); iu++) { for (int iv = 0; iv < gridV.size(); iv++) { - float u, v; - correction.convGridToUV(roc, row, gridU[iu], gridV[iv], u, v); - - float dx, du, dv; - correction.getCorrectionInternal(roc, row, u, v, dx, du, dv); - dx *= scaling[0]; - du *= scaling[0]; - dv *= scaling[0]; - // add remaining corrections - for (int i = 1; i < corrections.size(); ++i) { - float dxTmp, duTmp, dvTmp; - corrections[i]->getCorrectionInternal(roc, row, u, v, dxTmp, duTmp, dvTmp); + + auto [y, z] = correction.convGridToLocal(sector, row, gridU[iu], gridV[iv]); + double dx = 0, dy = 0, dz = 0; + + // add corrections + for (int i = 0; i < corrections.size(); ++i) { + auto [dxTmp, dyTmp, dzTmp] = corrections[i]->getCorrectionLocal(sector, row, y, z); dx += dxTmp * scaling[i]; - du += duTmp * scaling[i]; - dv += dvTmp * scaling[i]; - } - double cx = x + dx; - double cu = u + du; - double cv = v + dv; - if (cu < area.cuMin) { - area.cuMin = cu; - } - if (cu > area.cuMax) { - area.cuMax = cu; + dy += dyTmp * scaling[i]; + dz += dzTmp * scaling[i]; } - dataPointCU.push_back(cu); - dataPointCV.push_back(cv); + double realY = y + dy; + double realZ = z + dz; + float realU, realV; + mGeo.convLocalToUV1(sector, realY, realZ, realU, realV); + + dataPointGridU.push_back(realU); + dataPointGridV.push_back(realV); dataPointF.push_back(dx); - dataPointF.push_back(du); - dataPointF.push_back(dv); + dataPointF.push_back(dy); + dataPointF.push_back(dz); } } - if (area.cuMax - area.cuMin < 0.2) { - area.cuMax = .1; - area.cuMin = -.1; - } - if (area.cvMax - cvMin < 0.2) { - area.cvMax = .1; - cvMin = -.1; - } - - if (prn) { - LOG(info) << "roc " << roc << " row " << row << " max drift L = " << correction.getMaxDriftLength(roc, row) - << " active area: cuMin " << area.cuMin << " cuMax " << area.cuMax << " vMax " << area.vMax << " cvMax " << area.cvMax; - } - // define the grid for the inverse correction - rocRowInfo.gridCorrU0 = area.cuMin; - rocRowInfo.gridCorrV0 = cvMin; - rocRowInfo.scaleCorrUtoGrid = spline.getGridX1().getUmax() / (area.cuMax - area.cuMin); - rocRowInfo.scaleCorrVtoGrid = spline.getGridX2().getUmax() / area.cvMax; + auto& sectorRowInfo = correction.getSectorRowInfo(sector, row); + + sectorRowInfo.gridCorrU0 = sectorRowInfo.gridU0; + sectorRowInfo.gridCorrV0 = sectorRowInfo.gridV0; + sectorRowInfo.scaleCorrUtoGrid = sectorRowInfo.scaleUtoGrid; + sectorRowInfo.scaleCorrVtoGrid = sectorRowInfo.scaleVtoGrid; - /* - rocRowInfo.gridCorrU0 = rocRowInfo.gridU0; - rocRowInfo.gridCorrV0 = rocRowInfo.gridV0; - rocRowInfo.scaleCorrUtoGrid = rocRowInfo.scaleUtoGrid; - rocRowInfo.scaleCorrVtoGrid = rocRowInfo.scaleVtoGrid; - */ + int nDataPoints = dataPointGridU.size(); - int nDataPoints = dataPointCU.size(); + // convert real Y,Z to grid U,V for (int i = 0; i < nDataPoints; i++) { - dataPointCU[i] = (dataPointCU[i] - rocRowInfo.gridCorrU0) * rocRowInfo.scaleCorrUtoGrid; - dataPointCV[i] = (dataPointCV[i] - rocRowInfo.gridCorrV0) * rocRowInfo.scaleCorrVtoGrid; + dataPointGridU[i] = (dataPointGridU[i] - sectorRowInfo.gridCorrU0) * sectorRowInfo.scaleCorrUtoGrid; + dataPointGridV[i] = (dataPointGridV[i] - sectorRowInfo.gridCorrV0) * sectorRowInfo.scaleCorrVtoGrid; } splineParameters.resize(spline.getNumberOfParameters()); helper.approximateDataPoints(spline, splineParameters.data(), 0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax(), - dataPointCU.data(), dataPointCV.data(), - dataPointF.data(), dataPointCU.size()); + dataPointGridU.data(), dataPointGridV.data(), + dataPointF.data(), nDataPoints); - float* splineX = correction.getSplineData(roc, row, 1); - float* splineUV = correction.getSplineData(roc, row, 2); + float* splineX = correction.getSplineData(sector, row, 1); + float* splineUV = correction.getSplineData(sector, row, 2); for (int i = 0; i < spline.getNumberOfParameters() / 3; i++) { splineX[i] = splineParameters[3 * i + 0]; splineUV[2 * i + 0] = splineParameters[3 * i + 1]; @@ -1051,10 +962,100 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector& corrections, const std::vector& scaling, bool prn) +{ + /// merge several corrections + /* + TStopwatch watch; + LOG(info) << "fast space charge correction helper: Merge corrections"; + + if (corrections.size() != scaling.size()) { + LOGP(error, "Input corrections and scaling values have different size"); + return; + } + + auto& correction = *(corrections.front()); + + for (int sector = 0; sector < mGeo.getNumberOfSectors(); sector++) { + + auto myThread = [&](int iThread) { + for (int row = iThread; row < mGeo.getNumberOfRows(); row += mNthreads) { + TPCFastSpaceChargeCorrection::SplineType spline = correction.getSpline(sector, row); + + std::vector splineParameters(spline.getNumberOfParameters()); + std::vector splineParametersInvX(spline.getNumberOfParameters()); + std::vector splineParametersInvYZ(spline.getNumberOfParameters()); + + const auto& gridU = spline.getGridX1(); + const auto& gridV = spline.getGridX2(); + + for (int iu = 0; iu < gridU.getNumberOfKnots(); iu++) { + double u = gridU.getKnot(iu).u; + for (int iv = 0; iv < gridV.getNumberOfKnots(); iv++) { + int knotIndex = spline.getKnotIndex(iu, iv); + + double v = gridV.getKnot(iu).u; + auto [y, z] = correction.convGridToLocal(sector, row, u, v); + constexpr int nKnotPar1d = 4; + constexpr int nKnotPar2d = nKnotPar1d * 2; + constexpr int nKnotPar3d = nKnotPar1d * 3; + + for (int i = 0; i < corrections.size(); ++i) { + double s = scaling[i]; + auto p = corrections[i]->getCorrectionParameters(sector, row, y, z); + for (int j = 0; j < nKnotPar3d; ++j) { + splineParameters[knotIndex * nKnotPar3d + j] += s * p[j]; + } + auto pInvX = corrections[i]->getCorrectionParametersInvX(sector, row, y, z); + for (int j = 0; j < nKnotPar1d; ++j) { + splineParametersInvX[knotIndex * nKnotPar1d + j] += s * pInvX[j]; + } + auto pInvYZ = corrections[i]->getCorrectionParametersInvYZ(sector, row, y, z); + for (int j = 0; j < nKnotPar2d; ++j) { + splineParametersInvYZ[knotIndex * nKnotPar2d + j] += s * pInvYZ[j]; + } + } + } // iv + } // iu + + float* splineXYZ = correction.getSplineData(sector, row, 0); + float* splineInvX = correction.getSplineData(sector, row, 1); + float* splineInvYZ = correction.getSplineData(sector, row, 2); + + for (int i = 0; i < spline.getNumberOfParameters(); i++) { + splineXYZ[i] = splineParameters[i]; + } + for (int i = 0; i < spline.getNumberOfParameters() / 3; i++) { + splineX[i] = splineParametersInvX[i]; + splineYZ[2 * i + 0] = splineParametersInvYZ[2 * i + 0]; + splineYZ[2 * i + 1] = splineParametersInvYZ[2 * i + 1]; + } + + } // row + }; // thread + + std::vector threads(mNthreads); + + // run n threads + for (int i = 0; i < mNthreads; i++) { + threads[i] = std::thread(myThread, i); + } + + // wait for the threads to finish + for (auto& th : threads) { + th.join(); + } + + } // sector + float duration = watch.RealTime(); + LOGP(info, "Merge of corrections tooks: {}s", duration); + */ +} + } // namespace tpc } // namespace o2 diff --git a/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx b/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx index c83ee6d0cfa19..a6a2c9722caeb 100644 --- a/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx +++ b/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx @@ -177,8 +177,8 @@ void TPCFastTransformHelperO2::testGeometry(const TPCFastTransformGeo& geo) cons { const Mapper& mapper = Mapper::instance(); - if (geo.getNumberOfRocs() != Sector::MAXSECTOR) { - LOG(fatal) << "Wrong number of sectors :" << geo.getNumberOfRocs() << " instead of " << Sector::MAXSECTOR << std::endl; + if (geo.getNumberOfSectors() != Sector::MAXSECTOR) { + LOG(fatal) << "Wrong number of sectors :" << geo.getNumberOfSectors() << " instead of " << Sector::MAXSECTOR << std::endl; } if (geo.getNumberOfRows() != mapper.getNumberOfRows()) { diff --git a/Detectors/TPC/reconstruction/test/testTPCFastTransform.cxx b/Detectors/TPC/reconstruction/test/testTPCFastTransform.cxx index 53cfe08f3a7f4..fee63e9e38bc2 100644 --- a/Detectors/TPC/reconstruction/test/testTPCFastTransform.cxx +++ b/Detectors/TPC/reconstruction/test/testTPCFastTransform.cxx @@ -53,7 +53,7 @@ BOOST_AUTO_TEST_CASE(FastTransform_test1) BOOST_CHECK_EQUAL(geo.test(), 0); - BOOST_CHECK_EQUAL(geo.getNumberOfRocs(), Sector::MAXSECTOR); + BOOST_CHECK_EQUAL(geo.getNumberOfSectors(), Sector::MAXSECTOR); BOOST_CHECK_EQUAL(geo.getNumberOfRows(), mapper.getNumberOfRows()); double maxDx = 0, maxDy = 0; @@ -71,15 +71,16 @@ BOOST_AUTO_TEST_CASE(FastTransform_test1) for (int pad = 0; pad < nPads; pad++) { const GlobalPadNumber p = mapper.globalPadNumber(PadPos(row, pad)); const PadCentre& c = mapper.padCentre(p); - float u = 0, v = 0; - fastTransform.convPadTimeToUV(row, pad, 0, u, v, 0.); - + float y = 0, z = 0; + int sector = 0; + float time = 0.; + fastTransform.convPadTimeToLocal(sector, row, pad, time, y, z, 0.); double dx = x - c.X(); - double dy = u - (-c.Y()); // diferent sign convention for Y coordinate in the map + double dy = y - (-c.Y()); // diferent sign convention for Y coordinate in the map BOOST_CHECK(fabs(dx) < 1.e-6); BOOST_CHECK(fabs(dy) < 1.e-5); if (fabs(dy) >= 1.e-5) { - std::cout << "row " << row << " pad " << pad << " y calc " << u << " y in map " << -c.Y() << " dy " << dy << std::endl; + std::cout << "row " << row << " pad " << pad << " y calc " << y << " y in map " << -c.Y() << " dy " << dy << std::endl; } if (fabs(maxDx) < fabs(dx)) { maxDx = dx; @@ -104,46 +105,46 @@ BOOST_AUTO_TEST_CASE(FastTransform_test_setSpaceChargeCorrection) std::unique_ptr fastTransform0(TPCFastTransformHelperO2::instance()->create(0)); const TPCFastTransformGeo& geo = fastTransform0->getGeometry(); - auto correctionUV = [&](int roc, int /*row*/, const double u, const double v, double& dX, double& dU, double& dV) { + auto correctionUV = [&](int sector, int /*row*/, const double u, const double v, double& dX, double& dU, double& dV) { // float lx = geo.getRowInfo(row).x; dX = 1. + 1 * u + 0.1 * u * u; dU = 2. + 0.2 * u + 0.002 * u * u; // + 0.001 * u * u * u; dV = 3. + 0.1 * v + 0.01 * v * v; //+ 0.0001 * v * v * v; }; - auto correctionLocal = [&](int roc, int row, double ly, double lz, + auto correctionLocal = [&](int sector, int row, double ly, double lz, double& dx, double& dly, double& dlz) { float u, v; - geo.convLocalToUV(roc, ly, lz, u, v); + geo.convLocalToUV(sector, ly, lz, u, v); double du, dv; - correctionUV(roc, row, u, v, dx, du, dv); + correctionUV(sector, row, u, v, dx, du, dv); float ly1, lz1; - geo.convUVtoLocal(roc, u + du, v + dv, ly1, lz1); + geo.convUVtoLocal(sector, u + du, v + dv, ly1, lz1); dly = ly1 - ly; dlz = lz1 - lz; }; - int nRocs = geo.getNumberOfRocs(); + int nSectors = geo.getNumberOfSectors(); int nRows = geo.getNumberOfRows(); TPCFastSpaceChargeCorrectionMap& scData = TPCFastTransformHelperO2::instance()->getCorrectionMap(); - scData.init(nRocs, nRows); + scData.init(nSectors, nRows); - for (int iRoc = 0; iRoc < nRocs; iRoc++) { + for (int iSector = 0; iSector < nSectors; iSector++) { for (int iRow = 0; iRow < nRows; iRow++) { double dsu = 1. / (3 * 8 - 3); double dsv = 1. / (3 * 20 - 3); for (double su = 0.f; su < 1.f + .5 * dsu; su += dsv) { for (double sv = 0.f; sv < 1.f + .5 * dsv; sv += dsv) { float ly = 0.f, lz = 0.f; - geo.convScaledUVtoLocal(iRoc, iRow, su, sv, ly, lz); + geo.convScaledUVtoLocal(iSector, iRow, su, sv, ly, lz); double dx, dy, dz; - correctionLocal(iRoc, iRow, ly, lz, dx, dy, dz); - scData.addCorrectionPoint(iRoc, iRow, + correctionLocal(iSector, iRow, ly, lz, dx, dy, dz); + scData.addCorrectionPoint(iSector, iRow, ly, lz, dx, dy, dz); } } } // row - } // roc + } // sector std::unique_ptr fastTransform(TPCFastTransformHelperO2::instance()->create(0)); @@ -158,12 +159,12 @@ BOOST_AUTO_TEST_CASE(FastTransform_test_setSpaceChargeCorrection) double statDiff = 0., statN = 0.; double statDiffFile = 0., statNFile = 0.; - for (int roc = 0; roc < geo.getNumberOfRocs(); roc += 1) { - // std::cout << "roc " << roc << " ... " << std::endl; + for (int sector = 0; sector < geo.getNumberOfSectors(); sector += 1) { + // std::cout << "sector " << sector << " ... " << std::endl; - const TPCFastTransformGeo::RocInfo& rocInfo = geo.getRocInfo(roc); + const TPCFastTransformGeo::SectorInfo& sectorInfo = geo.getSectorInfo(sector); - float lastTimeBin = fastTransform->getMaxDriftTime(roc, 0.f); + float lastTimeBin = fastTransform->getMaxDriftTime(sector, 0.f); for (int row = 0; row < geo.getNumberOfRows(); row++) { @@ -172,31 +173,31 @@ BOOST_AUTO_TEST_CASE(FastTransform_test_setSpaceChargeCorrection) for (int pad = 0; pad < nPads; pad += 10) { for (float time = 0; time < lastTimeBin; time += 30) { - // std::cout<<"roc "<setApplyCorrectionOff(); float x0, y0, z0; - fastTransform->Transform(roc, row, pad, time, x0, y0, z0); + fastTransform->Transform(sector, row, pad, time, x0, y0, z0); - BOOST_CHECK_EQUAL(geo.test(roc, row, y0, z0), 0); + BOOST_CHECK_EQUAL(geo.test(sector, row, y0, z0), 0); fastTransform->setApplyCorrectionOn(); float x1, y1, z1; - fastTransform->Transform(roc, row, pad, time, x1, y1, z1); + fastTransform->Transform(sector, row, pad, time, x1, y1, z1); // local to UV float u0, v0, u1, v1; - geo.convLocalToUV(roc, y0, z0, u0, v0); - geo.convLocalToUV(roc, y1, z1, u1, v1); + geo.convLocalToUV(sector, y0, z0, u0, v0); + geo.convLocalToUV(sector, y1, z1, u1, v1); double dx, du, dv; - correctionUV(roc, row, u0, v0, dx, du, dv); + correctionUV(sector, row, u0, v0, dx, du, dv); statDiff += fabs((x1 - x0) - dx) + fabs((u1 - u0) - du) + fabs((v1 - v0) - dv); statN += 3; // std::cout << (x1 - x0) - dx << " " << (u1 - u0) - du << " " << (v1 - v0) - dv << std::endl; //": v0 " << v0 <<" z0 "<Transform(roc, row, pad, time, x1f, y1f, z1f); + fromFile->Transform(sector, row, pad, time, x1f, y1f, z1f); statDiffFile += fabs(x1f - x1) + fabs(y1f - y1) + fabs(z1f - z1); statNFile += 3; } diff --git a/GPU/TPCFastTransformation/Spline1DSpec.h b/GPU/TPCFastTransformation/Spline1DSpec.h index dc59e77e308a1..2cc95ebdcab9f 100644 --- a/GPU/TPCFastTransformation/Spline1DSpec.h +++ b/GPU/TPCFastTransformation/Spline1DSpec.h @@ -369,7 +369,41 @@ class Spline1DSpec : public Spline1DContainer dDr = v * a; // F(u) = dSl * Sl + dSr * Sr + dDl * Dl + dDr * Dr; } - + /* + template + GPUd() void getUsecondDerivatives(const Knot& knotL, DataT u, + T& dSl, T& dDl, T& dSr, T& dDr, + T& dSl2, T& dDl2, T& dSr2, T& dDr2) const + { + /// Get derivatives of the interpolated value {S(u): 1D -> nYdim} at the segment [knotL, next knotR] + /// over the spline values Sl, Sr and the slopes Dl, Dr + + if (u < (DataT)0) { + u = (DataT)0; + } + if (u > (DataT)TBase::getUmax()) { + u = (DataT)TBase::getUmax(); + } + + u = u - knotL.u; + T v = u * T(knotL.Li); // scaled u + T vm1 = v - 1.; + T a = u * vm1; + T v2 = v * v; + dSr = v2 * (3. - 2 * v); + dSl = 1. - dSr; + dDl = vm1 * a; + dDr = v * a; + T dv = T(knotL.Li); + dSr2 = 6. * v * (1. - v) * dv; + dSl2 = -dSr2; + dDl2 = (v - 1) * (3 * v - 1); + dDr = u * (v * v - v); + dDr2 = 3.f * v * v - 2.f * v; + // F(u) = dSl * Sl + dSr * Sr + dDl * Dl + dDr * Dr; + // dF(u)/du = dSl2 * Sl + dSr2 * Sr + dDl2 * Dl + dDr2 * Dr; + } + */ using TBase::convXtoU; using TBase::getKnot; using TBase::getKnots; diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx index 111e70072c58e..2921a74b025ce 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx @@ -35,7 +35,7 @@ TPCFastSpaceChargeCorrection::TPCFastSpaceChargeCorrection() mScenarioPtr(nullptr), mTimeStamp(-1), mSplineData{nullptr, nullptr, nullptr}, - mRocDataSizeBytes{0, 0, 0} + mSectorDataSizeBytes{0, 0, 0} { // Default Constructor: creates an empty uninitialized object } @@ -64,7 +64,7 @@ void TPCFastSpaceChargeCorrection::destroy() mTimeStamp = -1; for (int32_t is = 0; is < 3; is++) { mSplineData[is] = nullptr; - mRocDataSizeBytes[is] = 0; + mSectorDataSizeBytes[is] = 0; } FlatObject::destroy(); } @@ -101,13 +101,13 @@ void TPCFastSpaceChargeCorrection::cloneFromObject(const TPCFastSpaceChargeCorre mTimeStamp = obj.mTimeStamp; - for (int32_t i = 0; i < TPCFastTransformGeo::getNumberOfRocs(); ++i) { - mRocInfo[i] = obj.mRocInfo[i]; + for (int32_t i = 0; i < TPCFastTransformGeo::getNumberOfSectors(); ++i) { + mSectorInfo[i] = obj.mSectorInfo[i]; } - mRocDataSizeBytes[0] = obj.mRocDataSizeBytes[0]; - mRocDataSizeBytes[1] = obj.mRocDataSizeBytes[1]; - mRocDataSizeBytes[2] = obj.mRocDataSizeBytes[2]; + mSectorDataSizeBytes[0] = obj.mSectorDataSizeBytes[0]; + mSectorDataSizeBytes[1] = obj.mSectorDataSizeBytes[1]; + mSectorDataSizeBytes[2] = obj.mSectorDataSizeBytes[2]; // variable-size data mScenarioPtr = obj.mScenarioPtr; @@ -121,8 +121,8 @@ void TPCFastSpaceChargeCorrection::cloneFromObject(const TPCFastSpaceChargeCorre mRowInfos[i] = obj.mRowInfos[i]; } - for (int32_t i = 0; i < TPCFastTransformGeo::getNumberOfRocs() * TPCFastTransformGeo::getMaxNumberOfRows(); i++) { - mRocRowInfos[i] = obj.mRocRowInfos[i]; + for (int32_t i = 0; i < TPCFastTransformGeo::getNumberOfSectors() * TPCFastTransformGeo::getMaxNumberOfRows(); i++) { + mSectorRowInfos[i] = obj.mSectorRowInfos[i]; } relocateBufferPointers(oldFlatBufferPtr, mFlatBufferPtr); @@ -143,7 +143,7 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer struct RowInfoVersion3 { int32_t splineScenarioID{0}; ///< scenario index (which of Spline2D splines to use) - size_t dataOffsetBytes[3]{0}; ///< offset for the spline data withing a TPC roc + size_t dataOffsetBytes[3]{0}; ///< offset for the spline data withing a TPC sector }; struct RowActiveAreaVersion3 { @@ -154,7 +154,7 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer float cvMax{0.f}; }; - struct RocRowInfoVersion3 { + struct SectorRowInfoVersion3 { float gridV0{0.f}; ///< V coordinate of the V-grid start float gridCorrU0{0.f}; ///< U coordinate of the U-grid start for corrected U float gridCorrV0{0.f}; ///< V coordinate of the V-grid start for corrected V @@ -171,13 +171,13 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer rowsSize = sizeof(RowInfoVersion3) * mGeo.getNumberOfRows(); } - size_t rocRowsOffset = rowsOffset + rowsSize; - size_t rocRowsSize = 0; - if (mClassVersion == 3) { // copy old-format rocrow data from the buffer to the arrays - rocRowsSize = sizeof(RocRowInfoVersion3) * mGeo.getNumberOfRows() * mGeo.getNumberOfRocs(); + size_t sectorRowsOffset = rowsOffset + rowsSize; + size_t sectorRowsSize = 0; + if (mClassVersion == 3) { // copy old-format sectorrow data from the buffer to the arrays + sectorRowsSize = sizeof(SectorRowInfoVersion3) * mGeo.getNumberOfRows() * mGeo.getNumberOfSectors(); } - size_t scOffset = alignSize(rocRowsOffset + rocRowsSize, SplineType::getClassAlignmentBytes()); + size_t scOffset = alignSize(sectorRowsOffset + sectorRowsSize, SplineType::getClassAlignmentBytes()); size_t scSize = sizeof(SplineType) * mNumberOfScenarios; mScenarioPtr = reinterpret_cast(mFlatBufferPtr + scOffset); @@ -192,12 +192,12 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer } size_t bufferSize = scBufferOffset + scBufferSize; for (int32_t is = 0; is < 3; is++) { - size_t rocDataOffset = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); - mSplineData[is] = reinterpret_cast(mFlatBufferPtr + rocDataOffset); - bufferSize = rocDataOffset + mRocDataSizeBytes[is] * mGeo.getNumberOfRocs(); + size_t sectorDataOffset = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); + mSplineData[is] = reinterpret_cast(mFlatBufferPtr + sectorDataOffset); + bufferSize = sectorDataOffset + mSectorDataSizeBytes[is] * mGeo.getNumberOfSectors(); } - if (mClassVersion == 3) { // copy old-format rocrow data from the buffer to the arrays + if (mClassVersion == 3) { // copy old-format sectorrow data from the buffer to the arrays auto* rowInfosOld = reinterpret_cast(mFlatBufferPtr + rowsOffset); for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { @@ -214,13 +214,13 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer spline.setXrange(0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax()); } - auto* rocRowInfosOld = reinterpret_cast(mFlatBufferPtr + rocRowsOffset); + auto* sectorRowInfosOld = reinterpret_cast(mFlatBufferPtr + sectorRowsOffset); - for (int32_t roc = 0; roc < mGeo.getNumberOfRocs(); roc++) { + for (int32_t sector = 0; sector < mGeo.getNumberOfSectors(); sector++) { for (int32_t row = 0; row < mGeo.getNumberOfRows(); row++) { - RocRowInfoVersion3& infoOld = rocRowInfosOld[mGeo.getNumberOfRows() * roc + row]; - RocRowInfo& info = getRocRowInfo(roc, row); - const auto& spline = getSpline(roc, row); + SectorRowInfoVersion3& infoOld = sectorRowInfosOld[mGeo.getNumberOfRows() * sector + row]; + SectorRowInfo& info = getSectorRowInfo(sector, row); + const auto& spline = getSpline(sector, row); info.gridU0 = mGeo.getRowInfo(row).u0; info.scaleUtoGrid = spline.getGridX1().getUmax() / mGeo.getRowInfo(row).getUwidth(); @@ -232,14 +232,6 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer info.gridCorrV0 = infoOld.gridCorrV0; info.scaleCorrVtoGrid = infoOld.scaleCorrVtoGrid; - - info.activeArea.vMax = infoOld.activeArea.vMax; - info.activeArea.cuMin = infoOld.activeArea.cuMin; - info.activeArea.cuMax = infoOld.activeArea.cuMax; - info.activeArea.cvMax = infoOld.activeArea.cvMax; - for (int32_t i = 0; i < 5; i++) { - info.activeArea.maxDriftLengthCheb[i] = infoOld.activeArea.maxDriftLengthCheb[i]; - } } } } @@ -276,7 +268,7 @@ void TPCFastSpaceChargeCorrection::print() const mGeo.print(); LOG(info) << " mNumberOfScenarios = " << mNumberOfScenarios; LOG(info) << " mTimeStamp = " << mTimeStamp; - LOG(info) << " mRocDataSizeBytes = " << mRocDataSizeBytes[0] << " " << mRocDataSizeBytes[1] << " " << mRocDataSizeBytes[2]; + LOG(info) << " mSectorDataSizeBytes = " << mSectorDataSizeBytes[0] << " " << mSectorDataSizeBytes[1] << " " << mSectorDataSizeBytes[2]; { LOG(info) << " TPC rows: "; for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { @@ -292,9 +284,9 @@ void TPCFastSpaceChargeCorrection::print() const } if (mScenarioPtr) { LOG(info) << " Spline Data: "; - for (int32_t is = 0; is < mGeo.getNumberOfRocs(); is++) { + for (int32_t is = 0; is < mGeo.getNumberOfSectors(); is++) { for (int32_t ir = 0; ir < mGeo.getNumberOfRows(); ir++) { - LOG(info) << "roc " << is << " row " << ir << ": "; + LOG(info) << "sector " << is << " row " << ir << ": "; const SplineType& spline = getSpline(is, ir); const float* d = getSplineData(is, ir); int32_t k = 0; @@ -305,8 +297,8 @@ void TPCFastSpaceChargeCorrection::print() const LOG(info) << ""; } } - // LOG(info) << "inverse correction: roc " << roc - // << " dx " << maxDroc[0] << " du " << maxDroc[1] << " dv " << maxDroc[2] ; + // LOG(info) << "inverse correction: sector " << sector + // << " dx " << maxDsector[0] << " du " << maxDsector[1] << " dv " << maxDsector[2] ; } } } @@ -345,7 +337,7 @@ void TPCFastSpaceChargeCorrection::startConstruction(const TPCFastTransformGeo& mScenarioPtr = nullptr; for (int32_t s = 0; s < 3; s++) { mSplineData[s] = nullptr; - mRocDataSizeBytes[s] = 0; + mSectorDataSizeBytes[s] = 0; } mClassVersion = 4; } @@ -401,18 +393,18 @@ void TPCFastSpaceChargeCorrection::finishConstruction() scBufferSize = alignSize(scBufferSize + sp.getFlatBufferSize(), sp.getBufferAlignmentBytes()); } size_t bufferSize = scBufferOffsets[0] + scBufferSize; - size_t rocDataOffset[3]; + size_t sectorDataOffset[3]; for (int32_t is = 0; is < 3; is++) { - rocDataOffset[is] = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); - mRocDataSizeBytes[is] = 0; + sectorDataOffset[is] = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); + mSectorDataSizeBytes[is] = 0; for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { RowInfo& row = mRowInfos[i]; SplineType& spline = mConstructionScenarios[row.splineScenarioID]; - row.dataOffsetBytes[is] = alignSize(mRocDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); - mRocDataSizeBytes[is] = row.dataOffsetBytes[is] + spline.getSizeOfParameters(); + row.dataOffsetBytes[is] = alignSize(mSectorDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); + mSectorDataSizeBytes[is] = row.dataOffsetBytes[is] + spline.getSizeOfParameters(); } - mRocDataSizeBytes[is] = alignSize(mRocDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); - bufferSize = rocDataOffset[is] + mRocDataSizeBytes[is] * mGeo.getNumberOfRocs(); + mSectorDataSizeBytes[is] = alignSize(mSectorDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); + bufferSize = sectorDataOffset[is] + mSectorDataSizeBytes[is] * mGeo.getNumberOfSectors(); } FlatObject::finishConstruction(bufferSize); @@ -427,7 +419,7 @@ void TPCFastSpaceChargeCorrection::finishConstruction() } for (int32_t is = 0; is < 3; is++) { - mSplineData[is] = reinterpret_cast(mFlatBufferPtr + rocDataOffset[is]); + mSplineData[is] = reinterpret_cast(mFlatBufferPtr + sectorDataOffset[is]); } releaseConstructionMemory(); @@ -439,15 +431,15 @@ void TPCFastSpaceChargeCorrection::finishConstruction() GPUd() void TPCFastSpaceChargeCorrection::setNoCorrection() { // initialise all corrections to 0. - for (int32_t roc = 0; roc < mGeo.getNumberOfRocs(); roc++) { + for (int32_t sector = 0; sector < mGeo.getNumberOfSectors(); sector++) { double vLength = mGeo.getTPCzLength(); - RocInfo& rocInfo = getRocInfo(roc); - rocInfo.vMax = vLength; + SectorInfo& sectorInfo = getSectorInfo(sector); + sectorInfo.vMax = vLength; for (int32_t row = 0; row < mGeo.getNumberOfRows(); row++) { - const SplineType& spline = getSpline(roc, row); + const SplineType& spline = getSpline(sector, row); for (int32_t is = 0; is < 3; is++) { - float* data = getSplineData(roc, row, is); + float* data = getSplineData(sector, row, is); int32_t nPar = spline.getNumberOfParameters(); if (is == 1) { nPar = nPar / 3; @@ -460,7 +452,7 @@ GPUd() void TPCFastSpaceChargeCorrection::setNoCorrection() } } - RocRowInfo& info = getRocRowInfo(roc, row); + SectorRowInfo& info = getSectorRowInfo(sector, row); info.gridU0 = mGeo.getRowInfo(row).u0; info.scaleUtoGrid = spline.getGridX1().getUmax() / mGeo.getRowInfo(row).getUwidth(); @@ -473,18 +465,8 @@ GPUd() void TPCFastSpaceChargeCorrection::setNoCorrection() info.scaleCorrUtoGrid = info.scaleUtoGrid; info.scaleCorrVtoGrid = info.scaleVtoGrid; - RowActiveArea& area = info.activeArea; - for (int32_t i = 1; i < 5; i++) { - area.maxDriftLengthCheb[i] = 0; - } - area.maxDriftLengthCheb[0] = vLength; - area.cuMin = info.gridCorrU0; - area.cuMax = -area.cuMin; - area.vMax = vLength; - area.cvMax = vLength; - } // row - } // roc + } // sector } void TPCFastSpaceChargeCorrection::constructWithNoCorrection(const TPCFastTransformGeo& geo) @@ -512,31 +494,31 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) double tpcR2min = mGeo.getRowInfo(0).x - 1.; tpcR2min = tpcR2min * tpcR2min; double tpcR2max = mGeo.getRowInfo(mGeo.getNumberOfRows() - 1).x; - tpcR2max = tpcR2max / cos(2 * M_PI / mGeo.getNumberOfRocsA() / 2) + 1.; + tpcR2max = tpcR2max / cos(2 * M_PI / mGeo.getNumberOfSectorsA() / 2) + 1.; tpcR2max = tpcR2max * tpcR2max; struct MaxValue { double V{0.}; - int Roc{-1}; + int Sector{-1}; int Row{-1}; - void update(double v, int roc, int row) + void update(double v, int sector, int row) { if (fabs(v) > fabs(V)) { V = v; - Roc = roc; + Sector = sector; Row = row; } } void update(const MaxValue& other) { - update(other.V, other.Roc, other.Row); + update(other.V, other.Sector, other.Row); } std::string toString() { std::stringstream ss; - ss << V << "(" << Roc << "," << Row << ")"; + ss << V << "(" << Sector << "," << Row << ")"; return ss.str(); } }; @@ -544,70 +526,75 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) MaxValue maxDtpc[3]; MaxValue maxD; - for (int32_t roc = 0; roc < mGeo.getNumberOfRocs(); roc++) { + for (int32_t sector = 0; sector < mGeo.getNumberOfSectors(); sector++) { if (prn) { - LOG(info) << "check inverse transform for roc " << roc; + LOG(info) << "check inverse transform for sector " << sector; } double vLength = mGeo.getTPCzLength(); - MaxValue maxDroc[3]; + MaxValue maxDsector[3]; for (int32_t row = 0; row < mGeo.getNumberOfRows(); row++) { - float u0 = mGeo.getRowInfo(row).getUmin(); - float u1 = mGeo.getRowInfo(row).getUmax(); - float v0 = 0.; - float v1 = vLength; - double x = mGeo.getRowInfo(row).x; - double stepU = (u1 - u0) / 100.; - double stepV = (v1 - v0) / 100.; + auto [y0, y1] = mGeo.getRowInfo(row).getYrange(); + auto [z0, z1] = mGeo.getZrange(sector); + + // grid borders + if (sector < mGeo.getNumberOfSectorsA()) { + z1 = vLength - getSectorRowInfo(sector, row).gridV0; + } else { + z0 = getSectorRowInfo(sector, row).gridV0 - vLength; + } + + double stepY = (y1 - y0) / 100.; + double stepZ = (z1 - z0) / 100.; MaxValue maxDrow[3]; - for (double u = u0; u < u1; u += stepU) { - for (double v = v0; v < v1; v += stepV) { - if (v < getRocRowInfo(roc, row).gridV0) { + for (double y = y0; y < y1; y += stepY) { + for (double z = z0; z < z1; z += stepZ) { + auto [dx, dy, dz] = getCorrectionLocal(sector, row, y, z); + double realX = x + dx; + double realY = y + dy; + double realZ = z + dz; + if (!isLocalInsideGrid(sector, row, y, z) || !isLocalInsideGrid(sector, row, realY, realZ)) { continue; } - float dx, du, dv; - getCorrectionInternal(roc, row, u, v, dx, du, dv); - double cx = x + dx; - double cu = u + du; - double cv = v + dv; - double r2 = cx * cx + cu * cu; - if (cv < 0 || cv > vLength || r2 < tpcR2min || r2 > tpcR2max) { + double r2 = realX * realX + realY * realY; + if (realY < y0 || realY > y1 || + realZ < z0 || realZ > z1 || + r2 < tpcR2min || r2 > tpcR2max) { continue; } - float nx, nu, nv; - getCorrectionInvCorrectedX(roc, row, cu, cv, nx); - getCorrectionInvUV(roc, row, cu, cv, nu, nv); - double d[3] = {(cx - nx) - dx, (cu - nu) - du, (cv - nv) - dv}; + float dxr = getCorrectionXatRealYZ(sector, row, realY, realZ); + auto [dyr, dzr] = getCorrectionYZatRealYZ(sector, row, realY, realZ); + double d[3] = {dxr - dx, dyr - dy, dzr - dz}; for (int32_t i = 0; i < 3; i++) { - maxDrow[i].update(d[i], roc, row); + maxDrow[i].update(d[i], sector, row); } if (0 && prn && fabs(d[0]) + fabs(d[1]) + fabs(d[2]) > 0.1) { - LOG(info) << nx - cx << " " << nu - u << " " << nv - v - << " x,u,v " << x << ", " << u << ", " << v - << " dx,du,dv " << cx - x << ", " << cu - u << ", " << cv - v - << " nx,nu,nv " << nx - x << ", " << cu - nu << ", " << cv - nv; + LOG(info) << dxr - dx << " " << dyr - dy << " " << dzr - dz + << " measured xyz " << x << ", " << y << ", " << z + << " dx,dy,dz from measured point " << dx << ", " << dy << ", " << dz + << " dx,dy,dz from real point " << dxr << ", " << dyr << ", " << dzr; } } } - if (1 && prn) { - LOG(info) << "roc " << roc << " row " << row - << " dx " << maxDrow[0].V << " du " << maxDrow[1].V << " dv " << maxDrow[2].V; + if (0 && prn) { + LOG(info) << "sector " << sector << " row " << row + << " dx " << maxDrow[0].V << " dy " << maxDrow[1].V << " dz " << maxDrow[2].V; } for (int32_t i = 0; i < 3; i++) { - maxDroc[i].update(maxDrow[i]); + maxDsector[i].update(maxDrow[i]); maxDtpc[i].update(maxDrow[i]); maxD.update(maxDrow[i]); } } if (prn) { - LOG(info) << "inverse correction: roc " << roc << ". Max deviations: " - << " dx " << maxDroc[0].toString() << " du " << maxDroc[1].toString() << " dv " << maxDroc[2].toString(); + LOG(info) << "inverse correction: sector " << sector << ". Max deviations: " + << " dx " << maxDsector[0].toString() << " dy " << maxDsector[1].toString() << " dz " << maxDsector[2].toString(); } - } // roc + } // sector LOG(info) << "Test inverse TPC correction. max deviations: " - << " dx " << maxDtpc[0].toString() << " du " << maxDtpc[1].toString() << " dv " << maxDtpc[2].toString() << " cm"; + << " dx " << maxDtpc[0].toString() << " dy " << maxDtpc[1].toString() << " dz " << maxDtpc[2].toString() << " cm"; return maxD.V; } diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index fa5cf7a1736bd..f84fde4fffd8c 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -44,20 +44,11 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// struct RowInfo { int32_t splineScenarioID{0}; ///< scenario index (which of Spline2D splines to use) - size_t dataOffsetBytes[3]{0}; ///< offset for the spline data withing the TPC roc + size_t dataOffsetBytes[3]{0}; ///< offset for the spline data withing the TPC sector ClassDefNV(RowInfo, 1); }; - struct RowActiveArea { - float maxDriftLengthCheb[5]{0.f}; - float vMax{0.f}; - float cuMin{0.f}; - float cuMax{0.f}; - float cvMax{0.f}; - ClassDefNV(RowActiveArea, 1); - }; - - struct RocRowInfo { + struct SectorRowInfo { float gridU0{0.f}; //< U coordinate of the U-grid start float scaleUtoGrid{0.f}; //< scale U to U-grid coordinate float gridV0{0.f}; ///< V coordinate of the V-grid start @@ -66,38 +57,37 @@ class TPCFastSpaceChargeCorrection : public FlatObject float scaleCorrUtoGrid{0.f}; ///< scale corrected U to U-grid coordinate float gridCorrV0{0.f}; ///< V coordinate of the V-grid start for corrected V float scaleCorrVtoGrid{0.f}; ///< scale corrected V to V-grid coordinate - float maxCorr[3]{10.f, 10.f, 10.f}; ///< max correction for dX, dU, dV float minCorr[3]{-10.f, -10.f, -10.f}; ///< min correction for dX, dU, dV - RowActiveArea activeArea; + float maxCorr[3]{10.f, 10.f, 10.f}; ///< max correction for dX, dU, dV void resetMaxValues() { - maxCorr[0] = 1.f; minCorr[0] = -1.f; - maxCorr[1] = 1.f; + maxCorr[0] = 1.f; minCorr[1] = -1.f; - maxCorr[2] = 1.f; + maxCorr[1] = 1.f; minCorr[2] = -1.f; + maxCorr[2] = 1.f; } void updateMaxValues(float dx, float du, float dv) { - maxCorr[0] = GPUCommonMath::Max(maxCorr[0], dx); minCorr[0] = GPUCommonMath::Min(minCorr[0], dx); + maxCorr[0] = GPUCommonMath::Max(maxCorr[0], dx); - maxCorr[1] = GPUCommonMath::Max(maxCorr[1], du); minCorr[1] = GPUCommonMath::Min(minCorr[1], du); + maxCorr[1] = GPUCommonMath::Max(maxCorr[1], du); - maxCorr[2] = GPUCommonMath::Max(maxCorr[2], dv); minCorr[2] = GPUCommonMath::Min(minCorr[2], dv); + maxCorr[2] = GPUCommonMath::Max(maxCorr[2], dv); } - ClassDefNV(RocRowInfo, 2); + ClassDefNV(SectorRowInfo, 2); }; - struct RocInfo { + struct SectorInfo { float vMax{0.f}; ///< Max value of V coordinate - ClassDefNV(RocInfo, 1); + ClassDefNV(SectorInfo, 1); }; typedef Spline2D SplineType; @@ -167,52 +157,43 @@ class TPCFastSpaceChargeCorrection : public FlatObject GPUd() void setInterpolationSafetyMargin(float val) { fInterpolationSafetyMargin = val; } /// Gives const pointer to a spline - GPUd() const SplineType& getSpline(int32_t roc, int32_t row) const; + GPUd() const SplineType& getSpline(int32_t sector, int32_t row) const; /// Gives pointer to a spline - GPUd() SplineType& getSpline(int32_t roc, int32_t row); + GPUd() SplineType& getSpline(int32_t sector, int32_t row); /// Gives pointer to spline data - GPUd() float* getSplineData(int32_t roc, int32_t row, int32_t iSpline = 0); + GPUd() float* getSplineData(int32_t sector, int32_t row, int32_t iSpline = 0); /// Gives pointer to spline data - GPUd() const float* getSplineData(int32_t roc, int32_t row, int32_t iSpline = 0) const; + GPUd() const float* getSplineData(int32_t sector, int32_t row, int32_t iSpline = 0) const; /// _______________ The main method: cluster correction _______________________ /// - GPUd() int32_t getCorrectionInternal(int32_t roc, int32_t row, float u, float v, float& dx, float& du, float& dv) const; + // GPUd() int32_t getCorrectionInternal(int32_t sector, int32_t row, float u, float v, float& dx, float& du, float& dv) const; - GPUdi() std::tuple getCorrectionLocal(int32_t roc, int32_t row, float y, float z) const; + GPUdi() std::tuple getCorrectionLocal(int32_t sector, int32_t row, float y, float z) const; /// inverse correction: Corrected U and V -> coorrected X - GPUd() void getCorrectionInvCorrectedX(int32_t roc, int32_t row, float corrU, float corrV, float& corrX) const; + GPUd() float getCorrectionXatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const; /// inverse correction: Corrected U and V -> uncorrected U and V - GPUd() void getCorrectionInvUV(int32_t roc, int32_t row, float corrU, float corrV, float& nomU, float& nomV) const; - - /// maximal possible drift length of the active area - GPUd() float getMaxDriftLength(int32_t roc, int32_t row, float pad) const; - - /// maximal possible drift length of the active area - GPUd() float getMaxDriftLength(int32_t roc, int32_t row) const; - - /// maximal possible drift length of the active area - GPUd() float getMaxDriftLength(int32_t roc) const; + GPUd() std::tuple getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const; /// _______________ Utilities _______________________________________________ /// convert local y, z to internal grid coordinates u,v /// return values: u, v, scaling factor - GPUd() std::tuple convLocalToGrid(int32_t roc, int32_t row, float y, float z) const; - - /// convert u,v to internal grid coordinates - GPUd() void convUVtoGrid(int32_t roc, int32_t row, float u, float v, float& gridU, float& gridV) const; + GPUd() std::tuple convLocalToGrid(int32_t sector, int32_t row, float y, float z) const; - /// convert u,v to internal grid coordinates - GPUd() void convGridToUV(int32_t roc, int32_t row, float gridU, float gridV, float& u, float& v) const; + /// convert internal grid coordinates u,v to local y, z + /// return values: y, z, scaling factor + GPUd() std::tuple convGridToLocal(int32_t sector, int32_t row, float u, float v) const; /// convert corrected u,v to internal grid coordinates - GPUd() void convCorrectedUVtoGrid(int32_t roc, int32_t row, float cu, float cv, float& gridU, float& gridV) const; + GPUd() std::tuple convCorrectedLocalToGrid(int32_t sector, int32_t row, float y, float z) const; + + GPUd() bool isLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const; /// TPC geometry information GPUd() const TPCFastTransformGeo& getGeometry() const @@ -229,28 +210,28 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// Gives TPC row info GPUd() const RowInfo& getRowInfo(int32_t row) const { return mRowInfos[row]; } - /// Gives TPC roc info - GPUd() const RocInfo& getRocInfo(int32_t roc) const + /// Gives TPC sector info + GPUd() const SectorInfo& getSectorInfo(int32_t sector) const { - return mRocInfo[roc]; + return mSectorInfo[sector]; } - /// Gives TPC roc info - GPUd() RocInfo& getRocInfo(int32_t roc) + /// Gives TPC sector info + GPUd() SectorInfo& getSectorInfo(int32_t sector) { - return mRocInfo[roc]; + return mSectorInfo[sector]; } - /// Gives TPC roc & row info - GPUd() const RocRowInfo& getRocRowInfo(int32_t roc, int32_t row) const + /// Gives TPC sector & row info + GPUd() const SectorRowInfo& getSectorRowInfo(int32_t sector, int32_t row) const { - return mRocRowInfos[mGeo.getMaxNumberOfRows() * roc + row]; + return mSectorRowInfos[mGeo.getMaxNumberOfRows() * sector + row]; } - /// Gives TPC roc & row info - GPUd() RocRowInfo& getRocRowInfo(int32_t roc, int32_t row) + /// Gives TPC sector & row info + GPUd() SectorRowInfo& getSectorRowInfo(int32_t sector, int32_t row) { - return mRocRowInfos[mGeo.getMaxNumberOfRows() * roc + row]; + return mSectorRowInfos[mGeo.getMaxNumberOfRows() * sector + row]; } #if !defined(GPUCA_GPUCODE) @@ -277,7 +258,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject int32_t mNumberOfScenarios; ///< Number of approximation spline scenarios - RocInfo mRocInfo[TPCFastTransformGeo::getNumberOfRocs()]; ///< RocInfo array + SectorInfo mSectorInfo[TPCFastTransformGeo::getNumberOfSectors()]; ///< SectorInfo array SplineType* mScenarioPtr; //! (transient!!) pointer to spline scenarios @@ -287,7 +268,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject char* mSplineData[3]; //! (transient!!) pointer to the spline data in the flat buffer - size_t mRocDataSizeBytes[3]; ///< size of the data for one roc in the flat buffer + size_t mSectorDataSizeBytes[3]; ///< size of the data for one sector in the flat buffer float fInterpolationSafetyMargin{0.1f}; // 10% area around the TPC row. Outside of this area the interpolation returns the boundary values. @@ -298,7 +279,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject RowInfo mRowInfos[TPCFastTransformGeo::getMaxNumberOfRows()]; ///< RowInfo array - RocRowInfo mRocRowInfos[TPCFastTransformGeo::getNumberOfRocs() * TPCFastTransformGeo::getMaxNumberOfRows()]; ///< RocRowInfo array + SectorRowInfo mSectorRowInfos[TPCFastTransformGeo::getNumberOfSectors() * TPCFastTransformGeo::getMaxNumberOfRows()]; ///< SectorRowInfo array ClassDefNV(TPCFastSpaceChargeCorrection, 5); }; @@ -307,206 +288,167 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// Inline implementations of some methods /// ==================================================== -GPUdi() const TPCFastSpaceChargeCorrection::SplineType& TPCFastSpaceChargeCorrection::getSpline(int32_t roc, int32_t row) const +GPUdi() const TPCFastSpaceChargeCorrection::SplineType& TPCFastSpaceChargeCorrection::getSpline(int32_t sector, int32_t row) const { /// Gives const pointer to spline const RowInfo& rowInfo = mRowInfos[row]; return mScenarioPtr[rowInfo.splineScenarioID]; } -GPUdi() TPCFastSpaceChargeCorrection::SplineType& TPCFastSpaceChargeCorrection::getSpline(int32_t roc, int32_t row) +GPUdi() TPCFastSpaceChargeCorrection::SplineType& TPCFastSpaceChargeCorrection::getSpline(int32_t sector, int32_t row) { /// Gives pointer to spline const RowInfo& rowInfo = mRowInfos[row]; return mScenarioPtr[rowInfo.splineScenarioID]; } -GPUdi() float* TPCFastSpaceChargeCorrection::getSplineData(int32_t roc, int32_t row, int32_t iSpline) +GPUdi() float* TPCFastSpaceChargeCorrection::getSplineData(int32_t sector, int32_t row, int32_t iSpline) { /// Gives pointer to spline data const RowInfo& rowInfo = mRowInfos[row]; - return reinterpret_cast(mSplineData[iSpline] + mRocDataSizeBytes[iSpline] * roc + rowInfo.dataOffsetBytes[iSpline]); + return reinterpret_cast(mSplineData[iSpline] + mSectorDataSizeBytes[iSpline] * sector + rowInfo.dataOffsetBytes[iSpline]); } -GPUdi() const float* TPCFastSpaceChargeCorrection::getSplineData(int32_t roc, int32_t row, int32_t iSpline) const +GPUdi() const float* TPCFastSpaceChargeCorrection::getSplineData(int32_t sector, int32_t row, int32_t iSpline) const { /// Gives pointer to spline data const RowInfo& rowInfo = mRowInfos[row]; - return reinterpret_cast(mSplineData[iSpline] + mRocDataSizeBytes[iSpline] * roc + rowInfo.dataOffsetBytes[iSpline]); + return reinterpret_cast(mSplineData[iSpline] + mSectorDataSizeBytes[iSpline] * sector + rowInfo.dataOffsetBytes[iSpline]); } -GPUdi() void TPCFastSpaceChargeCorrection::convUVtoGrid(int32_t roc, int32_t row, float u, float v, float& gu, float& gv) const +GPUdi() std::tuple TPCFastSpaceChargeCorrection::convLocalToGrid(int32_t sector, int32_t row, float y, float z) const { - const auto& info = getRocRowInfo(roc, row); - gu = (u - info.gridU0) * info.scaleUtoGrid; - gv = (v - info.gridV0) * info.scaleVtoGrid; -} + /// convert local y, z to internal grid coordinates u,v + /// return values: u, v, scaling factor + const auto& info = getSectorRowInfo(sector, row); + const SplineType& spline = getSpline(sector, row); -GPUdi() void TPCFastSpaceChargeCorrection::convGridToUV(int32_t roc, int32_t row, float gridU, float gridV, float& u, float& v) const -{ - /// convert internal grid coordinates to u,v - const RocRowInfo& info = getRocRowInfo(roc, row); - u = info.gridU0 + gridU / info.scaleUtoGrid; - v = info.gridV0 + gridV / info.scaleVtoGrid; -} + float u, v; + mGeo.convLocalToUV1(sector, y, z, u, v); -GPUdi() void TPCFastSpaceChargeCorrection::convCorrectedUVtoGrid(int32_t roc, int32_t row, float corrU, float corrV, float& gridU, float& gridV) const -{ - const RocRowInfo& info = getRocRowInfo(roc, row); - gridU = (corrU - info.gridCorrU0) * info.scaleCorrUtoGrid; - gridV = (corrV - info.gridCorrV0) * info.scaleCorrVtoGrid; -} + float scale = 1.f; + if (v < 0.f) { + scale = 0.f; + } else if (v < info.gridV0) { + scale = v / info.gridV0; + } + + float gridU = (u - info.gridU0) * info.scaleUtoGrid; + float gridV = (v - info.gridV0) * info.scaleVtoGrid; -GPUdi() int32_t TPCFastSpaceChargeCorrection::getCorrectionInternal(int32_t roc, int32_t row, float u, float v, float& dx, float& du, float& dv) const -{ - const auto& info = getRocRowInfo(roc, row); - const SplineType& spline = getSpline(roc, row); - const float* splineData = getSplineData(roc, row); - float gridU = 0, gridV = 0; - convUVtoGrid(roc, row, u, v, gridU, gridV); // shrink to the grid area gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); gridV = GPUCommonMath::Clamp(gridV, 0.f, (float)spline.getGridX2().getUmax()); - float dxuv[3]; - spline.interpolateU(splineData, gridU, gridV, dxuv); + return {gridU, gridV, scale}; +} - float s = v / info.gridV0; +GPUdi() bool TPCFastSpaceChargeCorrection::isLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const +{ + /// ccheck if local y, z are inside the grid - if (v >= info.gridV0) { - s = 1.f; - } else if (v <= 0.f) { - s = 0.f; - } + const auto& info = getSectorRowInfo(sector, row); + const SplineType& spline = getSpline(sector, row); - dx = GPUCommonMath::Clamp(s * dxuv[0], info.minCorr[0], info.maxCorr[0]); - du = GPUCommonMath::Clamp(s * dxuv[1], info.minCorr[1], info.maxCorr[1]); - dv = GPUCommonMath::Clamp(s * dxuv[2], info.minCorr[2], info.maxCorr[2]); - return 0; + float u, v; + mGeo.convLocalToUV1(sector, y, z, u, v); + + float gridU = (u - info.gridU0) * info.scaleUtoGrid; + float gridV = (v - info.gridV0) * info.scaleVtoGrid; + + // shrink to the grid area + if (gridU < 0.f || gridU > (float)spline.getGridX1().getUmax()) + return false; + if (gridV < 0.f || gridV > (float)spline.getGridX2().getUmax()) + return false; + return true; } -GPUdi() std::tuple TPCFastSpaceChargeCorrection::getCorrectionLocal(int32_t roc, int32_t row, float y, float z) const +GPUdi() std::tuple TPCFastSpaceChargeCorrection::convGridToLocal(int32_t sector, int32_t row, float gridU, float gridV) const { - const auto& info = getRocRowInfo(roc, row); - const SplineType& spline = getSpline(roc, row); - const float* splineData = getSplineData(roc, row); + /// convert internal grid coordinates u,v to local y, z + const SectorRowInfo& info = getSectorRowInfo(sector, row); + float u = info.gridU0 + gridU / info.scaleUtoGrid; + float v = info.gridV0 + gridV / info.scaleVtoGrid; + float y, z; + mGeo.convUVtoLocal1(sector, u, v, y, z); + return {y, z}; +} + +GPUdi() std::tuple TPCFastSpaceChargeCorrection::convCorrectedLocalToGrid(int32_t sector, int32_t row, float y, float z) const +{ + /// convert corrected y, z to the internal grid coordinates + const auto& info = getSectorRowInfo(sector, row); + const Spline2D& spline = reinterpret_cast&>(getSpline(sector, row)); float u, v; + mGeo.convLocalToUV1(sector, y, z, u, v); + + float scale = 1.f; + if (v < 0.f) { + scale = 0.f; + } else if (v < info.gridCorrV0) { + scale = v / info.gridCorrV0; + } - mGeo.convLocalToUV(roc, y, z, u, v); + float gridU = (u - info.gridCorrU0) * info.scaleCorrUtoGrid; + float gridV = (v - info.gridCorrV0) * info.scaleCorrVtoGrid; - float gridU = 0, gridV = 0; - convUVtoGrid(roc, row, u, v, gridU, gridV); // shrink to the grid area gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); gridV = GPUCommonMath::Clamp(gridV, 0.f, (float)spline.getGridX2().getUmax()); - float dxuv[3]; - spline.interpolateU(splineData, gridU, gridV, dxuv); - - float s = v / info.gridV0; + return {gridU, gridV, scale}; +} - if (v >= info.gridV0) { - s = 1.f; - } else if (v <= 0.f) { - s = 0.f; - } +GPUdi() std::tuple TPCFastSpaceChargeCorrection::getCorrectionLocal(int32_t sector, int32_t row, float y, float z) const +{ + const auto& info = getSectorRowInfo(sector, row); + const SplineType& spline = getSpline(sector, row); + const float* splineData = getSplineData(sector, row); - float dx = GPUCommonMath::Clamp(s * dxuv[0], info.minCorr[0], info.maxCorr[0]); - float du = GPUCommonMath::Clamp(s * dxuv[1], info.minCorr[1], info.maxCorr[1]); - float dv = GPUCommonMath::Clamp(s * dxuv[2], info.minCorr[2], info.maxCorr[2]); + auto [gridU, gridV, scale] = convLocalToGrid(sector, row, y, z); - float dy, dz; - mGeo.convUVtoLocal(roc, du, dv, dy, dz); + float dxyz[3]; + spline.interpolateU(splineData, gridU, gridV, dxyz); + float dx = scale * GPUCommonMath::Clamp(dxyz[0], info.minCorr[0], info.maxCorr[0]); + float dy = scale * GPUCommonMath::Clamp(dxyz[1], info.minCorr[1], info.maxCorr[1]); + float dz = scale * GPUCommonMath::Clamp(dxyz[2], info.minCorr[2], info.maxCorr[2]); return {dx, dy, dz}; } -GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvCorrectedX( - int32_t roc, int32_t row, float corrU, float corrV, float& x) const +GPUdi() float TPCFastSpaceChargeCorrection::getCorrectionXatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const { - const auto& info = getRocRowInfo(roc, row); - const Spline2D& spline = reinterpret_cast&>(getSpline(roc, row)); - const float* splineData = getSplineData(roc, row, 1); + const auto& info = getSectorRowInfo(sector, row); + const Spline2D& spline = reinterpret_cast&>(getSpline(sector, row)); + const float* splineData = getSplineData(sector, row, 1); - float gridU, gridV; - convCorrectedUVtoGrid(roc, row, corrU, corrV, gridU, gridV); - - // shrink to the grid area - gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); - gridV = GPUCommonMath::Clamp(gridV, 0.f, (float)spline.getGridX2().getUmax()); + auto [gridU, gridV, scale] = convCorrectedLocalToGrid(sector, row, realY, realZ); float dx = 0; spline.interpolateU(splineData, gridU, gridV, &dx); - float s = corrV / info.gridCorrV0; - - if (corrV >= info.gridCorrV0) { - s = 1.f; - } else if (corrV <= 0.f) { - s = 0.f; - } - - dx = GPUCommonMath::Clamp(s * dx, info.minCorr[0], info.maxCorr[0]); - x = mGeo.getRowInfo(row).x + dx; + dx = scale * GPUCommonMath::Clamp(dx, info.minCorr[0], info.maxCorr[0]); + return dx; } -GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionInvUV( - int32_t roc, int32_t row, float corrU, float corrV, float& nomU, float& nomV) const +GPUdi() std::tuple TPCFastSpaceChargeCorrection::getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const { - const Spline2D& spline = reinterpret_cast&>(getSpline(roc, row)); - const float* splineData = getSplineData(roc, row, 2); - float gridU, gridV; - convCorrectedUVtoGrid(roc, row, corrU, corrV, gridU, gridV); + auto [gridU, gridV, scale] = convCorrectedLocalToGrid(sector, row, realY, realZ); - // shrink to the grid area - gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); - gridV = GPUCommonMath::Clamp(gridV, 0.f, (float)spline.getGridX2().getUmax()); - - float duv[2]; - spline.interpolateU(splineData, gridU, gridV, duv); - const auto& info = getRocRowInfo(roc, row); - float s = corrV / info.gridCorrV0; + const auto& info = getSectorRowInfo(sector, row); + const Spline2D& spline = reinterpret_cast&>(getSpline(sector, row)); + const float* splineData = getSplineData(sector, row, 2); - if (corrV >= info.gridCorrV0) { - s = 1.f; - } else if (corrV <= 0.f) { - s = 0.f; - } + float dyz[2]; + spline.interpolateU(splineData, gridU, gridV, dyz); - duv[0] = GPUCommonMath::Clamp(s * duv[0], info.minCorr[1], info.maxCorr[1]); - duv[1] = GPUCommonMath::Clamp(s * duv[1], info.minCorr[2], info.maxCorr[2]); - nomU = corrU - duv[0]; - nomV = corrV - duv[1]; -} + dyz[0] = scale * GPUCommonMath::Clamp(dyz[0], info.minCorr[1], info.maxCorr[1]); + dyz[1] = scale * GPUCommonMath::Clamp(dyz[1], info.minCorr[2], info.maxCorr[2]); -GPUdi() float TPCFastSpaceChargeCorrection::getMaxDriftLength(int32_t roc, int32_t row, float pad) const -{ - const RowActiveArea& area = getRocRowInfo(roc, row).activeArea; - const float* c = area.maxDriftLengthCheb; - float x = -1.f + 2.f * pad / mGeo.getRowInfo(row).maxPad; - float y = c[0] + c[1] * x; - float f0 = 1.f; - float f1 = x; - x *= 2.f; - for (int32_t i = 2; i < 5; i++) { - double f = x * f1 - f0; - y += c[i] * f; - f0 = f1; - f1 = f; - } - return y; -} - -GPUdi() float TPCFastSpaceChargeCorrection::getMaxDriftLength(int32_t roc, int32_t row) const -{ - return getRocRowInfo(roc, row).activeArea.vMax; -} - -GPUdi() float TPCFastSpaceChargeCorrection::getMaxDriftLength(int32_t roc) const -{ - return getRocInfo(roc).vMax; + return {dyz[0], dyz[1]}; } } // namespace gpu diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrectionMap.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrectionMap.h index 97b824aa6da32..fcee61ff09425 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrectionMap.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrectionMap.h @@ -49,20 +49,20 @@ class TPCFastSpaceChargeCorrectionMap /// _____________ Constructors / destructors __________________________ /// Default constructor: creates an empty uninitialized object - TPCFastSpaceChargeCorrectionMap(int32_t nRocs, int32_t nRows) + TPCFastSpaceChargeCorrectionMap(int32_t nSectors, int32_t nRows) { - init(nRocs, nRows); + init(nSectors, nRows); } /// Destructor ~TPCFastSpaceChargeCorrectionMap() = default; /// (re-)init the map - void init(int32_t nRocs, int32_t nRows) + void init(int32_t nSectors, int32_t nRows) { - mNrocs = nRocs; + mNsectors = nSectors; mNrows = nRows; - int32_t n = mNrocs * mNrows; + int32_t n = mNsectors * mNrows; fDataPoints.resize(n); for (uint32_t i = 0; i < fDataPoints.size(); ++i) { fDataPoints[i].clear(); @@ -70,30 +70,30 @@ class TPCFastSpaceChargeCorrectionMap } /// Starts the construction procedure, reserves temporary memory - void addCorrectionPoint(int32_t iRoc, int32_t iRow, + void addCorrectionPoint(int32_t iSector, int32_t iRow, double y, double z, double dx, double dy, double dz) { - int32_t ind = mNrows * iRoc + iRow; + int32_t ind = mNrows * iSector + iRow; fDataPoints.at(ind).push_back(CorrectionPoint{y, z, dx, dy, dz}); } - const std::vector& getPoints(int32_t iRoc, int32_t iRow) const + const std::vector& getPoints(int32_t iSector, int32_t iRow) const { - int32_t ind = mNrows * iRoc + iRow; + int32_t ind = mNrows * iSector + iRow; return fDataPoints.at(ind); } - int32_t getNrocs() const { return mNrocs; } + int32_t getNsectors() const { return mNsectors; } int32_t getNrows() const { return mNrows; } - bool isInitialized() const { return mNrocs > 0 && mNrows > 0; } + bool isInitialized() const { return mNsectors > 0 && mNrows > 0; } private: /// _______________ Data members _______________________________________________ - int32_t mNrocs{0}; + int32_t mNsectors{0}; int32_t mNrows{0}; std::vector> fDataPoints; //! (transient!!) points with space charge correction diff --git a/GPU/TPCFastTransformation/TPCFastTransform.h b/GPU/TPCFastTransformation/TPCFastTransform.h index 1ecd577eb7dac..03d9eaf43ce9b 100644 --- a/GPU/TPCFastTransformation/TPCFastTransform.h +++ b/GPU/TPCFastTransformation/TPCFastTransform.h @@ -46,14 +46,14 @@ struct TPCSlowSpaceChargeCorrection { ~TPCSlowSpaceChargeCorrection(); /// getting the corrections for global coordinates - void getCorrections(const float gx, const float gy, const float gz, const int32_t roc, float& gdxC, float& gdyC, float& gdzC) const; + void getCorrections(const float gx, const float gy, const float gz, const int32_t sector, float& gdxC, float& gdyC, float& gdzC) const; o2::tpc::SpaceCharge* mCorr{nullptr}; ///< reference space charge corrections #else ~TPCSlowSpaceChargeCorrection() = default; /// setting dummy corrections for GPU - GPUd() void getCorrections(const float gx, const float gy, const float gz, const int32_t roc, float& gdxC, float& gdyC, float& gdzC) const + GPUd() void getCorrections(const float gx, const float gy, const float gz, const int32_t sector, float& gdxC, float& gdyC, float& gdzC) const { gdxC = 0; gdyC = 0; @@ -182,45 +182,43 @@ class TPCFastTransform : public FlatObject /// _______________ The main method: cluster transformation _______________________ /// - /// Transforms raw TPC coordinates to local XYZ withing a roc + /// Transforms raw TPC coordinates to local XYZ withing a sector /// taking calibration into account. /// - GPUd() void Transform(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime = 0, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; - GPUd() void TransformXYZ(int32_t roc, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; + GPUd() void Transform(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime = 0, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; + GPUd() void TransformXYZ(int32_t sector, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; /// Transformation in the time frame - GPUd() void TransformInTimeFrame(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float maxTimeBin) const; - GPUd() void TransformInTimeFrame(int32_t roc, float time, float& z, float maxTimeBin) const; + GPUd() void TransformInTimeFrame(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float maxTimeBin) const; + GPUd() void TransformInTimeFrame(int32_t sector, float time, float& z, float maxTimeBin) const; /// Inverse transformation - GPUd() void InverseTransformInTimeFrame(int32_t roc, int32_t row, float /*x*/, float y, float z, float& pad, float& time, float maxTimeBin) const; - GPUd() float InverseTransformInTimeFrame(int32_t roc, float z, float maxTimeBin) const; + GPUd() void InverseTransformInTimeFrame(int32_t sector, int32_t row, float /*x*/, float y, float z, float& pad, float& time, float maxTimeBin) const; + GPUd() float InverseTransformInTimeFrame(int32_t sector, float z, float maxTimeBin) const; /// Inverse transformation: Transformed Y and Z -> transformed X - GPUd() void InverseTransformYZtoX(int32_t roc, 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; + GPUd() void InverseTransformYZtoX(int32_t sector, 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; /// Inverse transformation: Transformed Y and Z -> Y and Z, transformed w/o space charge correction - GPUd() void InverseTransformYZtoNominalYZ(int32_t roc, int32_t row, float y, float z, float& ny, float& nz, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; + GPUd() void InverseTransformYZtoNominalYZ(int32_t sector, int32_t row, float y, float z, float& ny, float& nz, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; /// Inverse transformation: Transformed X, Y and Z -> X, Y and Z, transformed w/o space charge correction - GPUd() void InverseTransformXYZtoNominalXYZ(int32_t roc, int32_t row, float x, float y, float z, float& nx, float& ny, float& nz, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; + GPUd() void InverseTransformXYZtoNominalXYZ(int32_t sector, int32_t row, float x, float y, float z, float& nx, float& ny, float& nz, const TPCFastTransform* ref = nullptr, const TPCFastTransform* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; /// Ideal transformation with Vdrift only - without calibration - GPUd() void TransformIdeal(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime) const; - GPUd() void TransformIdealZ(int32_t roc, float time, float& z, float vertexTime) const; + GPUd() void TransformIdeal(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime) const; + GPUd() void TransformIdealZ(int32_t sector, float time, float& z, float vertexTime) const; - GPUd() void convPadTimeToUV(int32_t row, float pad, float time, float& u, float& v, float vertexTime) const; - GPUd() void convPadTimeToUVinTimeFrame(int32_t row, float pad, float time, float& u, float& v, float maxTimeBin) const; - GPUd() void convTimeToVinTimeFrame(float time, float& v, float maxTimeBin) const; + GPUd() void convPadTimeToLocal(int32_t sector, int32_t row, float pad, float time, float& y, float& z, float vertexTime) const; + GPUd() void convPadTimeToLocalInTimeFrame(int32_t sector, int32_t row, float pad, float time, float& y, float& z, float maxTimeBin) const; - GPUd() void convUVtoPadTime(int32_t row, float u, float v, float& pad, float& time, float vertexTime) const; - GPUd() void convUVtoPadTimeInTimeFrame(int32_t row, float u, float v, float& pad, float& time, float maxTimeBin) const; - GPUd() void convVtoTime(float v, float& time, float vertexTime) const; + GPUd() void convLocalToPadTime(int32_t sector, int32_t row, float y, float z, float& pad, float& time, float vertexTime) const; + GPUd() void convLocalToPadTimeInTimeFrame(int32_t sector, int32_t row, float y, float z, float& pad, float& time, float maxTimeBin) const; - GPUd() float convTimeToZinTimeFrame(int32_t roc, float time, float maxTimeBin) const; - GPUd() float convZtoTimeInTimeFrame(int32_t roc, float z, float maxTimeBin) const; - GPUd() float convDeltaTimeToDeltaZinTimeFrame(int32_t roc, float deltaTime) const; - GPUd() float convDeltaZtoDeltaTimeInTimeFrame(int32_t roc, float deltaZ) const; + GPUd() float convTimeToZinTimeFrame(int32_t sector, float time, float maxTimeBin) const; + GPUd() float convZtoTimeInTimeFrame(int32_t sector, float z, float maxTimeBin) const; + GPUd() float convDeltaTimeToDeltaZinTimeFrame(int32_t sector, float deltaTime) const; + GPUd() float convDeltaZtoDeltaTimeInTimeFrame(int32_t sector, float deltaZ) const; GPUd() float convDeltaZtoDeltaTimeInTimeFrameAbs(float deltaZ) const; GPUd() float convZOffsetToVertexTime(int32_t sector, float zOffset, float maxTimeBin) const; GPUd() float convVertexTimeToZOffset(int32_t sector, float vertexTime, float maxTimeBin) const; @@ -265,13 +263,13 @@ class TPCFastTransform : public FlatObject GPUd() float getLumiScaleFactor() const { return mLumiScaleFactor; } /// maximal possible drift time of the active area - GPUd() float getMaxDriftTime(int32_t roc, int32_t row, float pad) const; + GPUd() float getMaxDriftTime(int32_t sector, int32_t row, float pad) const; /// maximal possible drift time of the active area - GPUd() float getMaxDriftTime(int32_t roc, int32_t row) const; + GPUd() float getMaxDriftTime(int32_t sector, int32_t row) const; /// maximal possible drift time of the active area - GPUd() float getMaxDriftTime(int32_t roc) const; + GPUd() float getMaxDriftTime(int32_t sector) const; #if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) @@ -292,6 +290,8 @@ class TPCFastTransform : public FlatObject /// Print method void print() const; + GPUd() float convDriftLengthToTime(float driftLength, float vertexTime) const; + private: /// Enumeration of possible initialization states enum ConstructionExtraState : uint32_t { @@ -335,7 +335,7 @@ class TPCFastTransform : public FlatObject /// Correction of (x,u,v) with tricubic interpolator on a regular grid TPCSlowSpaceChargeCorrection* mCorrectionSlow{nullptr}; ///< reference space charge corrections - GPUd() void TransformLocal(int32_t roc, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const; + GPUd() void TransformLocal(int32_t sector, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const; ClassDefNV(TPCFastTransform, 4); }; @@ -344,27 +344,22 @@ class TPCFastTransform : public FlatObject // Inline implementations of some methods // ======================================================================= -GPUdi() void TPCFastTransform::convPadTimeToUV(int32_t row, float pad, float time, float& u, float& v, float vertexTime) const -{ - const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); - float x = rowInfo.x; - u = (pad - 0.5f * rowInfo.maxPad) * rowInfo.padWidth; - v = (time - mT0 - vertexTime) * (mVdrift); // drift length cm -} +// ---------------------------------------------------------------------- -GPUdi() void TPCFastTransform::convTimeToVinTimeFrame(float time, float& v, float maxTimeBin) const +GPUdi() void TPCFastTransform::convPadTimeToLocal(int32_t sector, int32_t row, float pad, float time, float& y, float& z, float vertexTime) const { - v = (time - mT0 - maxTimeBin) * mVdrift; // drift length cm - v += getGeometry().getTPCzLength(); + float l = (time - mT0 - vertexTime) * mVdrift; // drift length [cm] + std::tie(y, z) = getGeometry().convPadDriftLengthToLocal(sector, row, pad, l); } -GPUdi() void TPCFastTransform::convPadTimeToUVinTimeFrame(int32_t row, float pad, float time, float& u, float& v, float maxTimeBin) const +GPUdi() void TPCFastTransform::convPadTimeToLocalInTimeFrame(int32_t sector, int32_t row, float pad, float time, float& y, float& z, float maxTimeBin) const { - const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); - u = (pad - 0.5f * rowInfo.maxPad) * rowInfo.padWidth; - convTimeToVinTimeFrame(time, v, maxTimeBin); + float l = getGeometry().getTPCzLength() + (time - mT0 - maxTimeBin) * mVdrift; // drift length [cm] + std::tie(y, z) = getGeometry().convPadDriftLengthToLocal(sector, row, pad, l); } +// ---------------------------------------------------------------------- + GPUdi() float TPCFastTransform::convZOffsetToVertexTime(int32_t sector, float zOffset, float maxTimeBin) const { if (sector < getGeometry().getNumberOfSectorsA()) { @@ -383,27 +378,30 @@ GPUdi() float TPCFastTransform::convVertexTimeToZOffset(int32_t sector, float ve } } -GPUdi() void TPCFastTransform::convUVtoPadTime(int32_t row, float u, float v, float& pad, float& time, float vertexTime) const +GPUdi() float TPCFastTransform::convDriftLengthToTime(float driftLength, float vertexTime) const { - const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); - pad = u / rowInfo.padWidth + 0.5f * rowInfo.maxPad; - time = mT0 + vertexTime + v / mVdrift; + return (mT0 + vertexTime + driftLength / mVdrift); } -GPUdi() void TPCFastTransform::convVtoTime(float v, float& time, float vertexTime) const +// ---------------------------------------------------------------------- + +GPUdi() void TPCFastTransform::convLocalToPadTime(int32_t sector, int32_t row, float y, float z, float& pad, float& time, float vertexTime) const { - time = mT0 + vertexTime + v / mVdrift; + float l; + std::tie(pad, l) = getGeometry().convLocalToPadDriftLength(sector, row, y, z); + time = convDriftLengthToTime(l, vertexTime); } -GPUdi() void TPCFastTransform::convUVtoPadTimeInTimeFrame(int32_t row, float u, float v, float& pad, float& time, float maxTimeBin) const +GPUdi() void TPCFastTransform::convLocalToPadTimeInTimeFrame(int32_t sector, int32_t row, float y, float z, float& pad, float& time, float maxTimeBin) const { - v -= getGeometry().getTPCzLength(); - const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); - pad = u / rowInfo.padWidth + 0.5f * rowInfo.maxPad; - time = mT0 + maxTimeBin + v / mVdrift; + float l; + std::tie(pad, l) = getGeometry().convLocalToPadDriftLength(sector, row, y, z); + time = convDriftLengthToTime(l, maxTimeBin); } -GPUdi() void TPCFastTransform::TransformLocal(int32_t roc, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const +// ---------------------------------------------------------------------- + +GPUdi() void TPCFastTransform::TransformLocal(int32_t sector, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const { GPUCA_RTC_SPECIAL_CODE(ref2 = nullptr; scale2 = 0.f;); @@ -417,29 +415,29 @@ GPUdi() void TPCFastTransform::TransformLocal(int32_t roc, int32_t row, float& x #ifndef GPUCA_GPUCODE if (mCorrectionSlow) { float gx, gy, gz; - getGeometry().convLocalToGlobal(roc, x, y, z, gx, gy, gz); + getGeometry().convLocalToGlobal(sector, x, y, z, gx, gy, gz); float gdxC, gdyC, gdzC; - mCorrectionSlow->getCorrections(gx, gy, gz, roc, gdxC, gdyC, gdzC); - getGeometry().convGlobalToLocal(roc, gdxC, gdyC, gdzC, dx, dy, dz); + mCorrectionSlow->getCorrections(gx, gy, gz, sector, gdxC, gdyC, gdzC); + getGeometry().convGlobalToLocal(sector, gdxC, gdyC, gdzC, dx, dy, dz); } else #endif // GPUCA_GPUCODE { - std::tie(dx, dy, dz) = mCorrection.getCorrectionLocal(roc, row, y, z); + std::tie(dx, dy, dz) = mCorrection.getCorrectionLocal(sector, row, y, z); if (ref) { if ((scale > 0.f) && (scaleMode == 0)) { // scaling was requested - auto [dxRef, dyRef, dzRef] = ref->mCorrection.getCorrectionLocal(roc, row, y, z); + auto [dxRef, dyRef, dzRef] = ref->mCorrection.getCorrectionLocal(sector, row, y, z); dx = (dx - dxRef) * scale + dxRef; dy = (dy - dyRef) * scale + dyRef; dz = (dz - dzRef) * scale + dzRef; } else if ((scale != 0.f) && ((scaleMode == 1) || (scaleMode == 2))) { - auto [dxRef, dyRef, dzRef] = ref->mCorrection.getCorrectionLocal(roc, row, y, z); + auto [dxRef, dyRef, dzRef] = ref->mCorrection.getCorrectionLocal(sector, row, y, z); dx = dxRef * scale + dx; dy = dyRef * scale + dy; dz = dzRef * scale + dz; } } if (ref2 && (scale2 != 0)) { - auto [dxRef, dyRef, dzRef] = ref2->mCorrection.getCorrectionLocal(roc, row, y, z); + auto [dxRef, dyRef, dzRef] = ref2->mCorrection.getCorrectionLocal(sector, row, y, z); dx = dxRef * scale2 + dx; dy = dyRef * scale2 + dy; dz = dzRef * scale2 + dz; @@ -451,37 +449,37 @@ GPUdi() void TPCFastTransform::TransformLocal(int32_t roc, int32_t row, float& x float lx = x, ly = y, lz = z; float gx, gy, gz; - getGeometry().convLocalToGlobal(roc, lx, ly, lz, gx, gy, gz); + getGeometry().convLocalToGlobal(sector, lx, ly, lz, gx, gy, gz); float lxT = lx + dx; float lyT = ly + dy; float lzT = lz + dz; float invYZtoXScaled; - InverseTransformYZtoX(roc, row, lyT, lzT, invYZtoXScaled, ref, ref2, scale, scale2, scaleMode); + InverseTransformYZtoX(sector, row, lyT, lzT, invYZtoXScaled, ref, ref2, scale, scale2, scaleMode); float invYZtoX; - InverseTransformYZtoX(roc, row, lyT, lzT, invYZtoX); + InverseTransformYZtoX(sector, row, lyT, lzT, invYZtoX); float YZtoNominalY; float YZtoNominalZ; - InverseTransformYZtoNominalYZ(roc, row, lyT, lzT, YZtoNominalY, YZtoNominalZ); + InverseTransformYZtoNominalYZ(sector, row, lyT, lzT, YZtoNominalY, YZtoNominalZ); float YZtoNominalYScaled; float YZtoNominalZScaled; - InverseTransformYZtoNominalYZ(roc, row, lyT, lzT, YZtoNominalYScaled, YZtoNominalZScaled, ref, ref2, scale, scale2, scaleMode); + InverseTransformYZtoNominalYZ(sector, row, lyT, lzT, YZtoNominalYScaled, YZtoNominalZScaled, ref, ref2, scale, scale2, scaleMode); float dxRef = 0.f, dyRef = 0.f, dzRef = 0.f; if (ref) { - std::tie(dxRef, dyRef, dzRef) = ref->mCorrection.getCorrectionLocal(roc, row, y, z); + std::tie(dxRef, dyRef, dzRef) = ref->mCorrection.getCorrectionLocal(sector, row, y, z); } - float dxRef2 = 0.f, duRef2 = 0.f, dvRef2 = 0.f; + float dxRef2 = 0.f, dyRef2 = 0.f, dzRef2 = 0.f; if (ref2) { - std::tie(dxRef2, duRef2, dvRef2) = ref2->mCorrection.getCorrectionLocal(roc, row, y, z); + std::tie(dxRef2, dyRef2, dzRef2) = ref2->mCorrection.getCorrectionLocal(sector, row, y, z); } - auto [dxOrig, dyOrig, dzOrig] = mCorrection.getCorrectionLocal(roc, row, y, z); + auto [dxOrig, dyOrig, dzOrig] = mCorrection.getCorrectionLocal(sector, row, y, z); o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_Transform").data() // corrections in x, u, v @@ -498,7 +496,7 @@ GPUdi() void TPCFastTransform::TransformLocal(int32_t roc, int32_t row, float& x << "dy=" << dy << "dz=" << dz << "row=" << row - << "roc=" << roc + << "sector=" << sector << "scale=" << scale << "scale2=" << scale2 // original local coordinates @@ -529,38 +527,34 @@ GPUdi() void TPCFastTransform::TransformLocal(int32_t roc, int32_t row, float& x z += dz; } -GPUdi() void TPCFastTransform::TransformXYZ(int32_t roc, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const +GPUdi() void TPCFastTransform::TransformXYZ(int32_t sector, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const { - TransformLocal(roc, row, x, y, z, ref, ref2, scale, scale2, scaleMode); + TransformLocal(sector, row, x, y, z, ref, ref2, scale, scale2, scaleMode); } -GPUdi() void TPCFastTransform::Transform(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const +GPUdi() void TPCFastTransform::Transform(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const { /// _______________ The main method: cluster transformation _______________________ /// - /// Transforms raw TPC coordinates to local XYZ withing a roc + /// Transforms raw TPC coordinates to local XYZ withing a sector /// taking calibration into account. /// const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); x = rowInfo.x; - float u = 0, v = 0; - convPadTimeToUV(row, pad, time, u, v, vertexTime); - getGeometry().convUVtoLocal(roc, u, v, y, z); - - TransformLocal(roc, row, x, y, z, ref, ref2, scale, scale2, scaleMode); + convPadTimeToLocal(sector, row, pad, time, y, z, vertexTime); + TransformLocal(sector, row, x, y, z, ref, ref2, scale, scale2, scaleMode); } -GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t roc, float time, float& z, float maxTimeBin) const +GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t sector, float time, float& z, float maxTimeBin) const { - float v = 0; - convTimeToVinTimeFrame(time, v, maxTimeBin); - getGeometry().convVtoLocal(roc, v, z); + float l = (time - mT0 - maxTimeBin) * mVdrift; // drift length cm + z = getGeometry().convDriftLengthToLocal(sector, l); } -GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float maxTimeBin) const +GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float maxTimeBin) const { /// _______________ Special cluster transformation for a time frame _______________________ /// @@ -570,58 +564,50 @@ GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t roc, int32_t row, fl const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); x = rowInfo.x; - float u = 0, v = 0; - convPadTimeToUVinTimeFrame(row, pad, time, u, v, maxTimeBin); - getGeometry().convUVtoLocal(roc, u, v, y, z); + convPadTimeToLocalInTimeFrame(sector, row, pad, time, y, z, maxTimeBin); } -GPUdi() void TPCFastTransform::InverseTransformInTimeFrame(int32_t roc, int32_t row, float /*x*/, float y, float z, float& pad, float& time, float maxTimeBin) const +GPUdi() void TPCFastTransform::InverseTransformInTimeFrame(int32_t sector, int32_t row, float /*x*/, float y, float z, float& pad, float& time, float maxTimeBin) const { /// Inverse transformation to TransformInTimeFrame - float u = 0, v = 0; - getGeometry().convLocalToUV(roc, y, z, u, v); - convUVtoPadTimeInTimeFrame(row, u, v, pad, time, maxTimeBin); + convLocalToPadTimeInTimeFrame(sector, row, y, z, pad, time, maxTimeBin); } -GPUdi() float TPCFastTransform::InverseTransformInTimeFrame(int32_t roc, float z, float maxTimeBin) const +GPUdi() float TPCFastTransform::InverseTransformInTimeFrame(int32_t sector, float z, float maxTimeBin) const { float pad, time; - InverseTransformInTimeFrame(roc, 0, 0, 0, z, pad, time, maxTimeBin); + InverseTransformInTimeFrame(sector, 0, 0, 0, z, pad, time, maxTimeBin); return time; } -GPUdi() void TPCFastTransform::TransformIdealZ(int32_t roc, float time, float& z, float vertexTime) const +GPUdi() void TPCFastTransform::TransformIdealZ(int32_t sector, float time, float& z, float vertexTime) const { /// _______________ The main method: cluster transformation _______________________ /// - /// Transforms time TPC coordinates to local Z withing a roc + /// Transforms time TPC coordinates to local Z withing a sector /// Ideal transformation: only Vdrift from DCS. /// No space charge corrections, no time of flight correction /// - float v = (time - mT0 - vertexTime) * mVdrift; // drift length cm - getGeometry().convVtoLocal(roc, v, z); + float l = (time - mT0 - vertexTime) * mVdrift; // drift length cm + z = getGeometry().convDriftLengthToLocal(sector, l); } -GPUdi() void TPCFastTransform::TransformIdeal(int32_t roc, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime) const +GPUdi() void TPCFastTransform::TransformIdeal(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime) const { /// _______________ The main method: cluster transformation _______________________ /// - /// Transforms raw TPC coordinates to local XYZ withing a roc + /// Transforms raw TPC coordinates to local XYZ withing a sector /// Ideal transformation: only Vdrift from DCS. /// No space charge corrections, no time of flight correction /// - const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); - - x = rowInfo.x; - float u = (pad - 0.5f * rowInfo.maxPad) * rowInfo.padWidth; - float v = (time - mT0 - vertexTime) * mVdrift; // drift length cm - - getGeometry().convUVtoLocal(roc, u, v, y, z); + x = getGeometry().getRowInfo(row).x; + float driftLength = (time - mT0 - vertexTime) * mVdrift; // drift length cm + std::tie(y, z) = getGeometry().convPadDriftLengthToLocal(sector, row, pad, driftLength); } -GPUdi() float TPCFastTransform::convTimeToZinTimeFrame(int32_t roc, float time, float maxTimeBin) const +GPUdi() float TPCFastTransform::convTimeToZinTimeFrame(int32_t sector, float time, float maxTimeBin) const { /// _______________ Special cluster transformation for a time frame _______________________ /// @@ -631,21 +617,21 @@ GPUdi() float TPCFastTransform::convTimeToZinTimeFrame(int32_t roc, float time, /// float v = (time - mT0 - maxTimeBin) * mVdrift; // drift length cm - float z = (roc < getGeometry().getNumberOfRocsA()) ? -v : v; + float z = (sector < getGeometry().getNumberOfSectorsA()) ? -v : v; return z; } -GPUdi() float TPCFastTransform::convZtoTimeInTimeFrame(int32_t roc, float z, float maxTimeBin) const +GPUdi() float TPCFastTransform::convZtoTimeInTimeFrame(int32_t sector, float z, float maxTimeBin) const { /// Inverse transformation of convTimeToZinTimeFrame() - float v = (roc < getGeometry().getNumberOfRocsA()) ? -z : z; + float v = (sector < getGeometry().getNumberOfSectorsA()) ? -z : z; return mT0 + maxTimeBin + v / mVdrift; } -GPUdi() float TPCFastTransform::convDeltaTimeToDeltaZinTimeFrame(int32_t roc, float deltaTime) const +GPUdi() float TPCFastTransform::convDeltaTimeToDeltaZinTimeFrame(int32_t sector, float deltaTime) const { float deltaZ = deltaTime * mVdrift; - return roc < getGeometry().getNumberOfRocsA() ? -deltaZ : deltaZ; + return sector < getGeometry().getNumberOfSectorsA() ? -deltaZ : deltaZ; } GPUdi() float TPCFastTransform::convDeltaZtoDeltaTimeInTimeFrameAbs(float deltaZ) const @@ -653,140 +639,115 @@ GPUdi() float TPCFastTransform::convDeltaZtoDeltaTimeInTimeFrameAbs(float deltaZ return deltaZ / mVdrift; } -GPUdi() float TPCFastTransform::convDeltaZtoDeltaTimeInTimeFrame(int32_t roc, float deltaZ) const +GPUdi() float TPCFastTransform::convDeltaZtoDeltaTimeInTimeFrame(int32_t sector, float deltaZ) const { float deltaT = deltaZ / mVdrift; - return roc < getGeometry().getNumberOfRocsA() ? -deltaT : deltaT; + return sector < getGeometry().getNumberOfSectorsA() ? -deltaT : deltaT; } -/* -GPUdi() float TPCFastTransform::getLastCalibratedTimeBin(int32_t roc) const -{ - /// Return a value of the last timebin where correction map is valid - float u, v, pad, time; - getGeometry().convScaledUVtoUV(roc, 0, 0.f, 1.f, u, v); - convUVtoPadTime(roc, 0, u, v, pad, time, 0); - return time; -} -*/ - -GPUdi() float TPCFastTransform::getMaxDriftTime(int32_t roc, int32_t row, float pad) const +GPUdi() float TPCFastTransform::getMaxDriftTime(int32_t sector, int32_t row, float pad) const { /// maximal possible drift time of the active area - float maxL = mCorrection.getMaxDriftLength(roc, row, pad); - return mT0 + maxL / mVdrift; + return convDriftLengthToTime(getGeometry().getTPCzLength(), 0.f); } -GPUdi() float TPCFastTransform::getMaxDriftTime(int32_t roc, int32_t row) const +GPUdi() float TPCFastTransform::getMaxDriftTime(int32_t sector, int32_t row) const { /// maximal possible drift time of the active area - float maxL = mCorrection.getMaxDriftLength(roc, row); - float maxTime = 0.f; - convVtoTime(maxL, maxTime, 0.f); - return maxTime; + return convDriftLengthToTime(getGeometry().getTPCzLength(), 0.f); } -GPUdi() float TPCFastTransform::getMaxDriftTime(int32_t roc) const +GPUdi() float TPCFastTransform::getMaxDriftTime(int32_t sector) const { /// maximal possible drift time of the active area - float maxL = mCorrection.getMaxDriftLength(roc); - float maxTime = 0.f; - convVtoTime(maxL, maxTime, 0.f); - return maxTime; + return convDriftLengthToTime(getGeometry().getTPCzLength(), 0.f); } -GPUdi() void TPCFastTransform::InverseTransformYZtoX(int32_t roc, int32_t row, float y, float z, float& x, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const +GPUdi() void TPCFastTransform::InverseTransformYZtoX(int32_t sector, int32_t row, float realY, float realZ, float& realX, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const { GPUCA_RTC_SPECIAL_CODE(ref2 = nullptr; scale2 = 0.f;); /// Transformation y,z -> x - float u = 0, v = 0; - getGeometry().convLocalToUV(roc, y, z, u, v); + + float dx = 0.f; + if ((scale >= 0.f) || (scaleMode == 1) || (scaleMode == 2)) { - mCorrection.getCorrectionInvCorrectedX(roc, row, u, v, x); + dx = mCorrection.getCorrectionXatRealYZ(sector, row, realY, realZ); if (ref) { // scaling was requested if (scaleMode == 0 && scale > 0.f) { - float xr; - ref->mCorrection.getCorrectionInvCorrectedX(roc, row, u, v, xr); - x = (x - xr) * scale + xr; + float dxref = ref->mCorrection.getCorrectionXatRealYZ(sector, row, realY, realZ); + dx = (dx - dxref) * scale + dxref; } else if ((scale != 0) && ((scaleMode == 1) || (scaleMode == 2))) { - float xr; - ref->mCorrection.getCorrectionInvCorrectedX(roc, row, u, v, xr); - x = (xr - getGeometry().getRowInfo(row).x) * scale + x; // xr=mGeo.getRowInfo(row).x + dx; + float dxref = ref->mCorrection.getCorrectionXatRealYZ(sector, row, realY, realZ); + dx = dxref * scale + dx; } } if (ref2 && (scale2 != 0)) { - float xr; - ref2->mCorrection.getCorrectionInvCorrectedX(roc, row, u, v, xr); - x = (xr - getGeometry().getRowInfo(row).x) * scale2 + x; // xr=mGeo.getRowInfo(row).x + dx; + float dxref = ref2->mCorrection.getCorrectionXatRealYZ(sector, row, realY, realZ); + dx = dxref * scale2 + dx; } - } else { - x = mCorrection.getGeometry().getRowInfo(row).x; // corrections are disabled } + + realX = mCorrection.getGeometry().getRowInfo(row).x + dx; + GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamFastTransform)) { o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_InverseTransformYZtoX").data() - << "roc=" << roc + << "sector=" << sector << "row=" << row << "scale=" << scale - << "y=" << y - << "z=" << z - << "x=" << x - << "v=" << v - << "u=" << u + << "y=" << realY + << "z=" << realZ + << "x=" << realX << "\n"; }) } -GPUdi() void TPCFastTransform::InverseTransformYZtoNominalYZ(int32_t roc, int32_t row, float y, float z, float& ny, float& nz, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const +GPUdi() void TPCFastTransform::InverseTransformYZtoNominalYZ(int32_t sector, int32_t row, float realY, float realZ, float& measuredY, float& measuredZ, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const { + /// Transformation real y,z -> measured y,z + GPUCA_RTC_SPECIAL_CODE(ref2 = nullptr; scale2 = 0.f;); - /// Transformation y,z -> x - float u = 0, v = 0, un = 0, vn = 0; - getGeometry().convLocalToUV(roc, y, z, u, v); + + float dy = 0; + float dz = 0; + if ((scale >= 0.f) || (scaleMode == 1) || (scaleMode == 2)) { - mCorrection.getCorrectionInvUV(roc, row, u, v, un, vn); + std::tie(dy, dz) = mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); + if (ref) { // scaling was requested if (scaleMode == 0 && scale > 0.f) { - float unr = 0, vnr = 0; - ref->mCorrection.getCorrectionInvUV(roc, row, u, v, unr, vnr); - un = (un - unr) * scale + unr; - vn = (vn - vnr) * scale + vnr; + auto [dyRef, dzRef] = ref->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); + dy = (dy - dyRef) * scale + dyRef; + dz = (dz - dzRef) * scale + dzRef; } else if ((scale != 0) && ((scaleMode == 1) || (scaleMode == 2))) { - float unr = 0, vnr = 0; - ref->mCorrection.getCorrectionInvUV(roc, row, u, v, unr, vnr); - un = (unr - u) * scale + un; // unr = u - duv[0]; - vn = (vnr - v) * scale + vn; + auto [dyRef, dzRef] = ref->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); + dy = dyRef * scale + dy; + dz = dzRef * scale + dz; } if (ref2 && (scale2 != 0)) { - float unr = 0, vnr = 0; - ref2->mCorrection.getCorrectionInvUV(roc, row, u, v, unr, vnr); - un = (unr - u) * scale2 + un; // unr = u - duv[0]; - vn = (vnr - v) * scale2 + vn; + auto [dyRef, dzRef] = ref2->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); + dy = dyRef * scale2 + dy; + dz = dzRef * scale2 + dz; } } - } else { - un = u; - vn = v; } - getGeometry().convUVtoLocal(roc, un, vn, ny, nz); + + measuredY = realY - dy; + measuredZ = realZ - dz; GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamFastTransform)) { o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_InverseTransformYZtoNominalYZ").data() - << "roc=" << roc + << "sector=" << sector << "row=" << row << "scale=" << scale - << "y=" << y - << "z=" << z - << "ny=" << ny - << "nz=" << nz - << "u=" << u - << "v=" << v - << "un=" << un - << "vn=" << vn + << "real y=" << realY + << "real z=" << realZ + << "measured y=" << measuredY + << "measured z=" << measuredZ << "\n"; }) } -GPUdi() void TPCFastTransform::InverseTransformXYZtoNominalXYZ(int32_t roc, int32_t row, float x, float y, float z, float& nx, float& ny, float& nz, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const +GPUdi() void TPCFastTransform::InverseTransformXYZtoNominalXYZ(int32_t sector, int32_t row, float x, float y, float z, float& nx, float& ny, float& nz, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const { /// Inverse transformation: Transformed X, Y and Z -> X, Y and Z, transformed w/o space charge correction int32_t row2 = row + 1; @@ -797,8 +758,8 @@ GPUdi() void TPCFastTransform::InverseTransformXYZtoNominalXYZ(int32_t roc, int3 float nx2, ny2, nz2; // nominal coordinates for row2 nx1 = getGeometry().getRowInfo(row).x; nx2 = getGeometry().getRowInfo(row2).x; - InverseTransformYZtoNominalYZ(roc, row, y, z, ny1, nz1, ref, ref2, scale, scale2, scaleMode); - InverseTransformYZtoNominalYZ(roc, row2, y, z, ny2, nz2, ref, ref2, scale, scale2, scaleMode); + InverseTransformYZtoNominalYZ(sector, row, y, z, ny1, nz1, ref, ref2, scale, scale2, scaleMode); + InverseTransformYZtoNominalYZ(sector, row2, y, z, ny2, nz2, ref, ref2, scale, scale2, scaleMode); float c1 = (nx2 - nx) / (nx2 - nx1); float c2 = (nx - nx1) / (nx2 - nx1); nx = x; diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx b/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx index c8982f05d4730..e7e026f464818 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx @@ -28,14 +28,14 @@ using namespace o2::gpu; TPCFastTransformGeo::TPCFastTransformGeo() { // Default Constructor: creates an empty uninitialized object - double dAlpha = 2. * M_PI / (NumberOfRocsA); - for (int32_t i = 0; i < NumberOfRocs; i++) { - RocInfo& s = mRocInfos[i]; + double dAlpha = 2. * M_PI / (NumberOfSectorsA); + for (int32_t i = 0; i < NumberOfSectors; i++) { + SectorInfo& s = mSectorInfos[i]; double alpha = dAlpha * (i + 0.5); s.sinAlpha = sin(alpha); s.cosAlpha = cos(alpha); } - mRocInfos[NumberOfRocs] = RocInfo{}; + mSectorInfos[NumberOfSectors] = SectorInfo{}; for (int32_t i = 0; i < MaxNumberOfRows + 1; i++) { mRowInfos[i] = RowInfo{}; @@ -84,7 +84,7 @@ void TPCFastTransformGeo::setTPCrow(int32_t iRow, float x, int32_t nPads, float // Make scaled U = area between the geometrical sector borders - const double sectorAngle = 2. * M_PI / NumberOfRocsA; + const double sectorAngle = 2. * M_PI / NumberOfSectorsA; const double scaleXtoRowWidth = 2. * tan(0.5 * sectorAngle); double uWidth = x * scaleXtoRowWidth; // distance to the sector border @@ -99,8 +99,8 @@ void TPCFastTransformGeo::finishConstruction() { /// Finishes initialization: puts everything to the flat buffer, releases temporary memory - assert(mConstructionMask & ConstructionState::InProgress); // construction in process - assert(mConstructionMask & ConstructionState::GeometryIsSet); // geometry is set + assert(mConstructionMask & ConstructionState::InProgress); // construction in process + assert(mConstructionMask & ConstructionState::GeometryIsSet); // geometry is set for (int32_t i = 0; i < mNumberOfRows; i++) { // all TPC rows are initialized assert(getRowInfo(i).maxPad > 0); @@ -123,7 +123,7 @@ void TPCFastTransformGeo::print() const #endif } -int32_t TPCFastTransformGeo::test(int32_t roc, int32_t row, float ly, float lz) const +int32_t TPCFastTransformGeo::test(int32_t sector, int32_t row, float ly, float lz) const { /// Check consistency of the class @@ -139,46 +139,21 @@ int32_t TPCFastTransformGeo::test(int32_t roc, int32_t row, float ly, float lz) float lx1 = 0.f, ly1 = 0.f, lz1 = 0.f; float gx = 0.f, gy = 0.f, gz = 0.f; - convLocalToGlobal(roc, lx, ly, lz, gx, gy, gz); - convGlobalToLocal(roc, gx, gy, gz, lx1, ly1, lz1); + convLocalToGlobal(sector, lx, ly, lz, gx, gy, gz); + convGlobalToLocal(sector, gx, gy, gz, lx1, ly1, lz1); if (fabs(lx1 - lx) > 1.e-4 || fabs(ly1 - ly) > 1.e-4 || fabs(lz1 - lz) > 1.e-7) { LOG(info) << "Error local <-> global: x " << lx << " dx " << lx1 - lx << " y " << ly << " dy " << ly1 - ly << " z " << lz << " dz " << lz1 - lz; error = -3; } - float u = 0.f, v = 0.f; - convLocalToUV(roc, ly, lz, u, v); - convUVtoLocal(roc, u, v, ly1, lz1); - if (fabs(ly1 - ly) + fabs(lz1 - lz) > 1.e-6) { - LOG(info) << "Error local <-> UV: y " << ly << " dy " << ly1 - ly << " z " << lz << " dz " << lz1 - lz; + auto [pad, length] = convLocalToPadDriftLength(sector, 10, ly, lz); + auto [ly2, lz2] = convPadDriftLengthToLocal(sector, 10, pad, length); + + if (fabs(ly2 - ly) + fabs(lz2 - lz) > 1.e-6) { + LOG(info) << "Error local <-> UV: y " << ly << " dy " << ly2 - ly << " z " << lz << " dz " << lz2 - lz; error = -4; } - /* - float su = 0.f, sv = 0.f; - - convUVtoScaledUV(roc, row, u, v, su, sv); - - if (su < 0.f || su > 1.f) { - LOG(info) << "Error scaled U range: u " << u << " su " << su; - error = -5; - } - - float u1 = 0.f, v1 = 0.f; - convScaledUVtoUV(roc, row, su, sv, u1, v1); - - if (fabs(u1 - u) > 1.e-4 || fabs(v1 - v) > 1.e-4) { - LOG(info) << "Error UV<->scaled UV: u " << u << " du " << u1 - u << " v " << v << " dv " << v1 - v; - error = -6; - } - */ - float pad = convUtoPad(row, u); - float u1 = convPadToU(row, pad); - - if (fabs(u1 - u) > 1.e-5) { - LOG(info) << "Error U<->Pad: u " << u << " pad " << pad << " du " << u1 - u; - error = -7; - } #if !defined(GPUCA_GPUCODE) if (error != 0) { diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.h b/GPU/TPCFastTransformation/TPCFastTransformGeo.h index a5d642158cd8f..4072435e948a5 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.h +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.h @@ -34,11 +34,11 @@ namespace gpu class TPCFastTransformGeo { public: - /// The struct contains necessary info for TPC ROC - struct RocInfo { + /// The struct contains necessary info for TPC sector + struct SectorInfo { float sinAlpha{0.f}; ///< sin of the angle between the local x and the global x float cosAlpha{0.f}; ///< cos of the angle between the local x and the global x - ClassDefNV(RocInfo, 1); + ClassDefNV(SectorInfo, 1); }; /// The struct contains necessary info about TPC padrow @@ -54,6 +54,15 @@ class TPCFastTransformGeo /// get U max GPUd() float getUmax() const { return -u0; } + /// get Y min + GPUd() float getYmin() const { return u0; } + + /// get Y max + GPUd() float getYmax() const { return -u0; } + + /// get Y range + GPUd() std::tuple getYrange() const { return {getYmin(), getYmax()}; } + /// get width in U GPUd() float getUwidth() const { return -2.f * u0; } @@ -81,7 +90,7 @@ class TPCFastTransformGeo /// _______________ Construction interface ________________________ - /// Starts the initialization procedure, reserves temporary memory + /// Starts the initialization psectoredure, reserves temporary memory void startConstruction(int32_t numberOfRows); /// Initializes a TPC row @@ -100,11 +109,11 @@ class TPCFastTransformGeo /// _______________ Getters _________________________________ - /// Gives number of TPC ROCs - GPUd() static constexpr int32_t getNumberOfRocs() { return NumberOfRocs; } + /// Gives number of TPC sectors + GPUd() static constexpr int32_t getNumberOfSectors() { return NumberOfSectors; } - /// Gives number of TPC ROCs on the A side - GPUd() static constexpr int32_t getNumberOfRocsA() { return NumberOfRocsA; } + /// Gives number of TPC sectors on the A side + GPUd() static constexpr int32_t getNumberOfSectorsA() { return NumberOfSectorsA; } /// Gives number of TPC rows GPUd() int32_t getNumberOfRows() const { return mNumberOfRows; } @@ -112,8 +121,8 @@ class TPCFastTransformGeo /// Gives number of TPC rows GPUd() static constexpr int getMaxNumberOfRows() { return MaxNumberOfRows; } - /// Gives roc info - GPUd() const RocInfo& getRocInfo(int32_t roc) const; + /// Gives sector info + GPUd() const SectorInfo& getSectorInfo(int32_t sector) const; /// Gives TPC row info GPUd() const RowInfo& getRowInfo(int32_t row) const; @@ -121,20 +130,31 @@ class TPCFastTransformGeo /// Gives Z length of the TPC, one Z side GPUd() float getTPCzLength() const { return mTPCzLength; } + /// Gives Z range for the corresponding TPC side + GPUd() std::tuple getZrange(int32_t sector) const; + /// _______________ Conversion of coordinate systems __________ /// convert Local -> Global c.s. - GPUd() void convLocalToGlobal(int32_t roc, float lx, float ly, float lz, float& gx, float& gy, float& gz) const; + GPUd() void convLocalToGlobal(int32_t sector, float lx, float ly, float lz, float& gx, float& gy, float& gz) const; /// convert Global->Local c.s. - GPUd() void convGlobalToLocal(int32_t roc, float gx, float gy, float gz, float& lx, float& ly, float& lz) const; + GPUd() void convGlobalToLocal(int32_t sector, float gx, float gy, float gz, float& lx, float& ly, float& lz) const; + + /// convert Pad, DriftLength -> Local c.s. + GPUd() std::tuple convPadDriftLengthToLocal(int32_t sector, int32_t row, float pad, float driftLength) const; + + /// convert DriftLength -> Local c.s. + GPUd() float convDriftLengthToLocal(int32_t sector, float driftLength) const; + + /// convert Local c.s. -> Pad, DriftLength + GPUd() std::tuple convLocalToPadDriftLength(int32_t sector, int32_t row, float y, float z) const; /// convert UV -> Local c.s. - GPUd() void convUVtoLocal(int32_t roc, float u, float v, float& y, float& z) const; - GPUd() void convVtoLocal(int32_t roc, float v, float& z) const; + GPUd() void convUVtoLocal1(int32_t sector, float u, float v, float& y, float& z) const; /// convert Local-> UV c.s. - GPUd() void convLocalToUV(int32_t roc, float y, float z, float& u, float& v) const; + GPUd() void convLocalToUV1(int32_t sector, float y, float z, float& u, float& v) const; /// convert Pad coordinate -> U GPUd() float convPadToU(int32_t row, float pad) const; @@ -146,7 +166,7 @@ class TPCFastTransformGeo void print() const; /// Method for testing consistency - int32_t test(int32_t roc, int32_t row, float ly, float lz) const; + int32_t test(int32_t sector, int32_t row, float ly, float lz) const; /// Method for testing consistency int32_t test() const; @@ -154,9 +174,9 @@ class TPCFastTransformGeo private: /// _______________ Data members _______________________________________________ - static constexpr int32_t NumberOfRocs = 36; ///< Number of TPC rocs ( roc = inner + outer sector ) - static constexpr int32_t NumberOfRocsA = NumberOfRocs / 2; ///< Number of TPC rocs side A - static constexpr int32_t MaxNumberOfRows = 160; ///< Max Number of TPC rows in a roc + static constexpr int32_t NumberOfSectors = 36; ///< Number of TPC sectors ( sector = inner + outer sector ) + static constexpr int32_t NumberOfSectorsA = NumberOfSectors / 2; ///< Number of TPC sectors side A + static constexpr int32_t MaxNumberOfRows = 160; ///< Max Number of TPC rows in a sector /// _______________ Construction control _______________________________________________ @@ -172,10 +192,10 @@ class TPCFastTransformGeo /// _______________ Geometry _______________________________________________ - int32_t mNumberOfRows = 0; ///< Number of TPC rows. It is different for the Run2 and the Run3 setups - float mTPCzLength = 0.f; ///< Z length of one TPC side (A or C) + int32_t mNumberOfRows = 0; ///< Number of TPC rows. It is different for the Run2 and the Run3 setups + float mTPCzLength = 0.f; ///< Z length of one TPC side (A or C) - RocInfo mRocInfos[NumberOfRocs + 1]; ///< array of roc information [fixed size] + SectorInfo mSectorInfos[NumberOfSectors + 1]; ///< array of sector information [fixed size] RowInfo mRowInfos[MaxNumberOfRows + 1]; ///< array of row information [fixed size] ClassDefNV(TPCFastTransformGeo, 3); @@ -185,13 +205,13 @@ class TPCFastTransformGeo // Inline implementations of some methods // ======================================================================= -GPUdi() const TPCFastTransformGeo::RocInfo& TPCFastTransformGeo::getRocInfo(int32_t roc) const +GPUdi() const TPCFastTransformGeo::SectorInfo& TPCFastTransformGeo::getSectorInfo(int32_t sector) const { - /// Gives roc info - if (roc < 0 || roc >= NumberOfRocs) { // return zero object - roc = NumberOfRocs; + /// Gives sector info + if (sector < 0 || sector >= NumberOfSectors) { // return zero object + sector = NumberOfSectors; } - return mRocInfos[roc]; + return mSectorInfos[sector]; } GPUdi() const TPCFastTransformGeo::RowInfo& TPCFastTransformGeo::getRowInfo(int32_t row) const @@ -203,55 +223,93 @@ GPUdi() const TPCFastTransformGeo::RowInfo& TPCFastTransformGeo::getRowInfo(int3 return mRowInfos[row]; } -GPUdi() void TPCFastTransformGeo::convLocalToGlobal(int32_t roc, float lx, float ly, float lz, float& gx, float& gy, float& gz) const +GPUdi() void TPCFastTransformGeo::convLocalToGlobal(int32_t sector, float lx, float ly, float lz, float& gx, float& gy, float& gz) const { /// convert Local -> Global c.s. - const RocInfo& rocInfo = getRocInfo(roc); - gx = lx * rocInfo.cosAlpha - ly * rocInfo.sinAlpha; - gy = lx * rocInfo.sinAlpha + ly * rocInfo.cosAlpha; + const SectorInfo& sectorInfo = getSectorInfo(sector); + gx = lx * sectorInfo.cosAlpha - ly * sectorInfo.sinAlpha; + gy = lx * sectorInfo.sinAlpha + ly * sectorInfo.cosAlpha; gz = lz; } -GPUdi() void TPCFastTransformGeo::convGlobalToLocal(int32_t roc, float gx, float gy, float gz, float& lx, float& ly, float& lz) const +GPUdi() void TPCFastTransformGeo::convGlobalToLocal(int32_t sector, float gx, float gy, float gz, float& lx, float& ly, float& lz) const { /// convert Global -> Local c.s. - const RocInfo& rocInfo = getRocInfo(roc); - lx = gx * rocInfo.cosAlpha + gy * rocInfo.sinAlpha; - ly = -gx * rocInfo.sinAlpha + gy * rocInfo.cosAlpha; + const SectorInfo& sectorInfo = getSectorInfo(sector); + lx = gx * sectorInfo.cosAlpha + gy * sectorInfo.sinAlpha; + ly = -gx * sectorInfo.sinAlpha + gy * sectorInfo.cosAlpha; lz = gz; } -GPUdi() void TPCFastTransformGeo::convVtoLocal(int32_t roc, float v, float& lz) const +GPUdi() std::tuple TPCFastTransformGeo::convPadDriftLengthToLocal(int32_t sector, int32_t row, float pad, float driftLength) const { - /// convert UV -> Local c.s. - if (roc < NumberOfRocsA) { // TPC side A - lz = mTPCzLength - v; - } else { // TPC side C - lz = v - mTPCzLength; // drift direction is mirrored on C-side + /// convert Pad, DriftLength -> Local c.s. + const RowInfo& rowInfo = getRowInfo(row); + float u = (pad - 0.5f * rowInfo.maxPad) * rowInfo.padWidth; + float y, z; + if (sector < NumberOfSectorsA) { // TPC side A + y = u; + z = mTPCzLength - driftLength; + } else { // TPC side C + y = -u; // pads are mirrorred on C-side + z = driftLength - mTPCzLength; // drift direction is mirrored on C-side } + return {y, z}; } -GPUdi() void TPCFastTransformGeo::convUVtoLocal(int32_t roc, float u, float v, float& ly, float& lz) const +GPUdi() float TPCFastTransformGeo::convDriftLengthToLocal(int32_t sector, float driftLength) const +{ + /// convert DriftLength -> Local c.s. + return (sector < NumberOfSectorsA) ? (mTPCzLength - driftLength) : (driftLength - mTPCzLength); +} + +GPUdi() void TPCFastTransformGeo::convUVtoLocal1(int32_t sector, float u, float v, float& ly, float& lz) const { /// convert UV -> Local c.s. - if (roc < NumberOfRocsA) { // TPC side A + if (sector < NumberOfSectorsA) { // TPC side A ly = u; lz = mTPCzLength - v; - } else { // TPC side C - ly = -u; // pads are mirrorred on C-side - lz = v - mTPCzLength; // drift direction is mirrored on C-side + } else { // TPC side C + ly = -u; // pads are mirrorred on C-side + lz = v - mTPCzLength; // drift direction is mirrored on C-side + } +} + +GPUdi() std::tuple TPCFastTransformGeo::getZrange(int32_t sector) const +{ + /// z range for the sector + if (sector < NumberOfSectorsA) { // TPC side A + return {0.f, mTPCzLength}; + } else { // TPC side C + return {-mTPCzLength, 0.f}; + } +} + +GPUdi() std::tuple TPCFastTransformGeo::convLocalToPadDriftLength(int32_t sector, int32_t row, float y, float z) const +{ + /// convert Local c.s. -> Pad, DriftLength + float u, l; + if (sector < NumberOfSectorsA) { // TPC side A + u = y; + l = mTPCzLength - z; + } else { // TPC side C + u = -y; // pads are mirrorred on C-side + l = z + mTPCzLength; // drift direction is mirrored on C-side } + const TPCFastTransformGeo::RowInfo& rowInfo = getRowInfo(row); + float pad = u / rowInfo.padWidth + 0.5f * rowInfo.maxPad; + return {pad, l}; } -GPUdi() void TPCFastTransformGeo::convLocalToUV(int32_t roc, float ly, float lz, float& u, float& v) const +GPUdi() void TPCFastTransformGeo::convLocalToUV1(int32_t sector, float ly, float lz, float& u, float& v) const { /// convert Local-> UV c.s. - if (roc < NumberOfRocsA) { // TPC side A + if (sector < NumberOfSectorsA) { // TPC side A u = ly; v = mTPCzLength - lz; - } else { // TPC side C - u = -ly; // pads are mirrorred on C-side - v = lz + mTPCzLength; // drift direction is mirrored on C-side + } else { // TPC side C + u = -ly; // pads are mirrorred on C-side + v = lz + mTPCzLength; // drift direction is mirrored on C-side } } diff --git a/GPU/TPCFastTransformation/TPCFastTransformManager.cxx b/GPU/TPCFastTransformation/TPCFastTransformManager.cxx deleted file mode 100644 index c553d9cc6dac1..0000000000000 --- a/GPU/TPCFastTransformation/TPCFastTransformManager.cxx +++ /dev/null @@ -1,336 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file TPCFastTransformManager.cxx -/// \brief Implementation of TPCFastTransformManager class -/// -/// \author Sergey Gorbunov - -#include "TPCFastTransformManager.h" -#include "AliHLTTPCGeometry.h" -#include "AliTPCParam.h" -#include "AliTPCRecoParam.h" -#include "AliTPCTransform.h" -#include "AliTPCcalibDB.h" -#include "TPCFastTransform.h" -#include "Spline2DHelper.h" -blabla - -using namespace o2::gpu; - -TPCFastTransformManager::TPCFastTransformManager() - : mError(), mOrigTransform(nullptr), fLastTimeBin(0) {} - -int32_t TPCFastTransformManager::create(TPCFastTransform& fastTransform, - AliTPCTransform* transform, - long TimeStamp) -{ - /// Initializes TPCFastTransform object - - AliTPCcalibDB* pCalib = AliTPCcalibDB::Instance(); - if (!pCalib) { - return storeError( - -1, "TPCFastTransformManager::Init: No TPC calibration instance found"); - } - - AliTPCParam* tpcParam = pCalib->GetParameters(); - if (!tpcParam) { - return storeError( - -2, "TPCFastTransformManager::Init: No TPCParam object found"); - } - - if (!transform) { - transform = pCalib->GetTransform(); - } - if (!transform) { - return storeError( - -3, "TPCFastTransformManager::Init: No TPC transformation found"); - } - - mOrigTransform = transform; - - tpcParam->Update(); - tpcParam->ReadGeoMatrices(); - - const AliTPCRecoParam* rec = transform->GetCurrentRecoParam(); - if (!rec) { - return storeError(-5, - "TPCFastTransformManager::Init: No TPC Reco Param " - "set in transformation"); - } - - bool useCorrectionMap = rec->GetUseCorrectionMap(); - - if (useCorrectionMap) { - transform->SetCorrectionMapMode(kTRUE); // If the simulation set this to - // false to simulate corrections, we - // need to reverse it for the - // transformation - } - // find last calibrated time bin - - fLastTimeBin = rec->GetLastBin(); - - const int32_t nRows = tpcParam->GetNRowLow() + tpcParam->GetNRowUp(); - - TPCFastTransformGeo geo; - - { // construct the geometry - geo.startConstruction(nRows); - - float tpcZlengthSideA = tpcParam->GetZLength(0); - float tpcZlengthSideC = - tpcParam->GetZLength(TPCFastTransformGeo::getNumberOfRocs() / 2); - - geo.setTPCzLength(tpcZlengthSideA, tpcZlengthSideC); - geo.setTPCalignmentZ(-mOrigTransform->GetDeltaZCorrTime()); - - for (int32_t row = 0; row < geo.getNumberOfRows(); row++) { - int32_t roc = 0, sector = 0, secrow = 0; - AliHLTTPCGeometry::Slice2Sector(roc, row, sector, secrow); - Int_t nPads = tpcParam->GetNPads(sector, secrow); - float xRow = tpcParam->GetPadRowRadii(sector, secrow); - float padWidth = tpcParam->GetInnerPadPitchWidth(); - if (row >= tpcParam->GetNRowLow()) { - padWidth = tpcParam->GetOuterPadPitchWidth(); - } - geo.setTPCrow(row, xRow, nPads, padWidth); - } - geo.finishConstruction(); - } - - TPCFastSpaceChargeCorrection correction; - - { // create the correction map - - const int32_t nDistortionScenarios = 1; - - correction.startConstruction(geo, nDistortionScenarios); - - TPCFastSpaceChargeCorrection::SplineType spline; - spline.recreate(8, 20); - - int32_t scenario = 0; - correction.setSplineScenario(scenario, spline); - - for (int32_t row = 0; row < geo.getNumberOfRows(); row++) { - correction.setRowScenarioID(row, scenario); - } - - correction.finishConstruction(); - } // .. create the correction map - - { // create the fast transform object - - fastTransform.startConstruction(correction); - - // tell the transformation to apply the space charge corrections - fastTransform.setApplyCorrectionOn(); - - // set some initial calibration values, will be reinitialised later int32_t - // updateCalibration() - const float t0 = 0.; - const float vDrift = 0.f; - const float vdCorrY = 0.; - const float ldCorr = 0.; - const float tofCorr = 0.; - const float primVtxZ = 0.; - const int64_t initTimeStamp = -1; - fastTransform.setCalibration(initTimeStamp, t0, vDrift, vdCorrY, ldCorr, - tofCorr, primVtxZ); - - fastTransform.finishConstruction(); - } - - return updateCalibration(fastTransform, TimeStamp); -} - -int32_t TPCFastTransformManager::updateCalibration(TPCFastTransform& fastTransform, - long TimeStamp) -{ - // Update the calibration with the new time stamp - - long lastTS = fastTransform.getTimeStamp(); - - // deinitialize - - fastTransform.setTimeStamp(-1); - - if (TimeStamp < 0) { - return 0; - } - - // search for the calibration database - - if (!mOrigTransform) { - return storeError(-1, - "TPCFastTransformManager::SetCurrentTimeStamp: TPC " - "transformation has not been set properly"); - } - - AliTPCcalibDB* pCalib = AliTPCcalibDB::Instance(); - if (!pCalib) { - return storeError(-2, - "TPCFastTransformManager::SetCurrentTimeStamp: No " - "TPC calibration found"); - } - - AliTPCParam* tpcParam = pCalib->GetParameters(); - if (!tpcParam) { - return storeError(-3, - "TPCFastTransformManager::SetCurrentTimeStamp: No " - "TPCParam object found"); - } - - AliTPCRecoParam* recoParam = mOrigTransform->GetCurrentRecoParamNonConst(); - if (!recoParam) { - return storeError(-5, - "TPCFastTransformManager::Init: No TPC Reco Param " - "set in transformation"); - } - - // calibration found, set the initialized status back - - fastTransform.setTimeStamp(lastTS); - - // less than 60 seconds from the previois time stamp, don't do anything - - if (lastTS >= 0 && TMath::Abs(lastTS - TimeStamp) < 60) { - return 0; - } - - // start the initialization - - bool useCorrectionMap = recoParam->GetUseCorrectionMap(); - - if (useCorrectionMap) { - // If the simulation set this to false to simulate corrections, we need to - // reverse it for the transformation This is a design feature. Historically - // HLT code runs as a part of simulation, not reconstruction. - mOrigTransform->SetCorrectionMapMode(kTRUE); - } - - // set the current time stamp - - mOrigTransform->SetCurrentTimeStamp(static_cast(TimeStamp)); - fastTransform.setTimeStamp(TimeStamp); - - // find last calibrated time bin - - fLastTimeBin = recoParam->GetLastBin(); - - double t0 = mOrigTransform->GetTBinOffset(); - double driftCorrPT = mOrigTransform->GetDriftCorrPT(); - double vdCorrectionTime = mOrigTransform->GetVDCorrectionTime(); - double vdCorrectionTimeGY = mOrigTransform->GetVDCorrectionTimeGY(); - double time0CorrTime = mOrigTransform->GetTime0CorrTime(); - - // original formula: - // L = (t-t0)*ZWidth*driftCorrPT*vdCorrectionTime*( 1 + - // yLab*vdCorrectionTimeGY ) - time0CorrTime + 3.*tpcParam->GetZSigma(); Z = - // Z(L) - fDeltaZCorrTime chebyshev corrections for xyz Time-of-flight - // correction: ldrift += dist-to-vtx*tofCorr - - // fast transform formula: - // L = (t-t0)*(mVdrift + mVdriftCorrY*yLab ) + mLdriftCorr - // Z = Z(L) + tpcAlignmentZ - // spline corrections for xyz - // Time-of-flight correction: ldrift += dist-to-vtx*tofCorr - - double vDrift = tpcParam->GetZWidth() * driftCorrPT * vdCorrectionTime; - double vdCorrY = vDrift * vdCorrectionTimeGY; - double ldCorr = -time0CorrTime + 3 * tpcParam->GetZSigma(); - - double tofCorr = (0.01 * tpcParam->GetDriftV()) / TMath::C(); - double primVtxZ = mOrigTransform->GetPrimVertex()[2]; - - bool useTOFcorrection = recoParam->GetUseTOFCorrection(); - - if (!useTOFcorrection) { - tofCorr = 0; - } - - fastTransform.setCalibration(TimeStamp, t0, vDrift, vdCorrY, ldCorr, tofCorr, - primVtxZ); - - // now calculate the correction map: dx,du,dv = ( origTransform() -> x,u,v) - - // fastTransformNominal:x,u,v - - const TPCFastTransformGeo& geo = fastTransform.getGeometry(); - - TPCFastSpaceChargeCorrection& correction = - fastTransform.getCorrection(); - - // switch TOF correction off for a while - - recoParam->SetUseTOFCorrection(kFALSE); - - for (int32_t roc = 0; roc < geo.getNumberOfRocs(); roc++) { - - for (int32_t row = 0; row < geo.getNumberOfRows(); row++) { - - const TPCFastTransformGeo::RowInfo& rowInfo = geo.getRowInfo(row); - - const TPCFastSpaceChargeCorrection::SplineType& spline = correction.getSpline(roc, row); - float* data = correction.getSplineData(roc, row); - - Spline2DHelper helper; - helper.setSpline(spline, 4, 4); - auto F = [&](double su, double sv, double dxuv[3]) { - float x = rowInfo.x; - // x, u, v cordinates of the knot (local cartesian coord. of roc - // towards central electrode ) - float u = 0, v = 0; - geo.convScaledUVtoUV(roc, row, su, sv, u, v); - - // row, pad, time coordinates of the knot - float vertexTime = 0.f; - float pad = 0.f, time = 0.f; - fastTransform.convUVtoPadTime(roc, row, u, v, pad, time, vertexTime); - - // nominal x,y,z coordinates of the knot (without corrections and - // time-of-flight correction) - float y = 0, z = 0; - geo.convUVtoLocal(roc, u, v, y, z); - - // original TPC transformation (row,pad,time) -> (x,y,z) without - // time-of-flight correction - float ox = 0, oy = 0, oz = 0; - { - int32_t sector = 0, secrow = 0; - AliHLTTPCGeometry::Slice2Sector(roc, row, sector, secrow); - int32_t is[] = {sector}; - double xx[] = {static_cast(secrow), pad, time}; - mOrigTransform->Transform(xx, is, 0, 1); - ox = xx[0]; - oy = xx[1]; - oz = xx[2]; - } - // convert to u,v - float ou = 0, ov = 0; - geo.convLocalToUV(roc, oy, oz, ou, ov); - - // corrections in x,u,v: - dxuv[0] = ox - x; - dxuv[1] = ou - u; - dxuv[2] = ov - v; - }; - - helper.approximateFunction(data, 0., 1., 0., 1., F); - } // row - } // roc - - // set back the time-of-flight correction; - - recoParam->SetUseTOFCorrection(useTOFcorrection); - - return 0; -} diff --git a/GPU/TPCFastTransformation/TPCFastTransformManager.h b/GPU/TPCFastTransformation/TPCFastTransformManager.h deleted file mode 100644 index f981b05bec241..0000000000000 --- a/GPU/TPCFastTransformation/TPCFastTransformManager.h +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file TPCFastTransformManager.h -/// \brief Definition of TPCFastTransformManager class -/// -/// \author Sergey Gorbunov - -#ifndef ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_TPCFASTTRANSFORMMANAGER_H -#define ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_TPCFASTTRANSFORMMANAGER_H - -#include - -#include "GPUCommonDef.h" -#include "Rtypes.h" -#include "TString.h" -#include "AliTPCTransform.h" - -namespace o2 -{ -namespace gpu -{ -class TPCFastTransform; - -/// -/// The TPCFastTransformManager class is to initialize TPCFastTransformation object -/// - -class TPCFastTransformManager -{ - public: - /// _____________ Constructors / destructors __________________________ - - /// Default constructor - TPCFastTransformManager(); - - /// Copy constructor: disabled - TPCFastTransformManager(const TPCFastTransformManager&) = delete; - - /// Assignment operator: disabled - TPCFastTransformManager& operator=(const TPCFastTransformManager&) = delete; - - /// Destructor - ~TPCFastTransformManager() = default; - - /// _______________ Main functionality ________________________ - - /// Initializes TPCFastTransform object - int32_t create(TPCFastTransform& spline, AliTPCTransform* transform, long TimeStamp); - - /// Updates the transformation with the new time stamp - Int_t updateCalibration(TPCFastTransform& spline, long TimeStamp); - - /// _______________ Utilities ________________________ - - AliTPCTransform* getOriginalTransform() { return mOrigTransform; } - - /// Gives error string - const char* getLastError() const { return mError.Data(); } - - private: - /// Stores an error message - int32_t storeError(Int_t code, const char* msg); - - TString mError; ///< error string - AliTPCTransform* mOrigTransform; ///< transient - int32_t fLastTimeBin; ///< last calibrated time bin -}; - -inline int32_t TPCFastTransformManager::storeError(int32_t code, const char* msg) -{ - mError = msg; - return code; -} -} // namespace gpu -} // namespace o2 - -#endif diff --git a/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h b/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h index 7c1ae8fd56800..fc15506d5397c 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h +++ b/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h @@ -59,7 +59,7 @@ #pragma link C++ class o2::gpu::IrregularSpline2D3DCalibrator + ; #pragma link C++ class o2::gpu::TPCFastTransformGeo + ; -#pragma link C++ class o2::gpu::TPCFastTransformGeo::RocInfo + ; +#pragma link C++ class o2::gpu::TPCFastTransformGeo::SectorInfo + ; #pragma link C++ class o2::gpu::TPCFastTransformGeo::RowInfo + ; #pragma link C++ class o2::gpu::TPCFastTransform + ; @@ -68,9 +68,8 @@ #pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::RowInfo + ; #pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection + ; -#pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::RocInfo + ; -#pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::RowActiveArea + ; -#pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::RocRowInfo + ; +#pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::SectorInfo + ; +#pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::SectorRowInfo + ; #pragma link C++ class o2::gpu::CorrectionMapsHelper + ; #pragma link C++ struct o2::gpu::MultivariatePolynomialContainer + ; diff --git a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C index 7e889d5a9e7db..bee1f9107ddd2 100644 --- a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C +++ b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C @@ -44,7 +44,7 @@ using namespace o2::tpc; using namespace o2::gpu; -void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", +void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* fileNameInv = "debugVoxResInv.root", const char* outFileName = "TPCFastTransform_VoxRes.root", bool useSmoothed = false, bool invertSigns = false) { @@ -56,11 +56,11 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", To visiualise the results: root -l transformDebug.root - corr->Draw("cx:y:z","iRoc==0&&iRow==10","") - grid->Draw("cx:y:z","iRoc==0&&iRow==10","same") - vox->Draw("vx:y:z","iRoc==0&&iRow==10","same") - corrvox->Draw("cx:y:z","iRoc==0&&iRow==10","same") - points->Draw("px:y:z","iRoc==0&&iRow==10","same") + corr->Draw("cx:y:z","iSector==0&&iRow==10","") + grid->Draw("cx:y:z","iSector==0&&iRow==10","same") + vox->Draw("vx:y:z","iSector==0&&iRow==10","same") + corrvox->Draw("cx:y:z","iSector==0&&iRow==10","same") + points->Draw("px:y:z","iSector==0&&iRow==10","same") */ if (gSystem->AccessPathName(fileName)) { @@ -82,6 +82,18 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", return; } + TTree* voxResTreeInverse = nullptr; + std::unique_ptr fileInv; + if (fileNameInv && !std::string(fileNameInv).empty()) { + fileInv = std::unique_ptr(TFile::Open(fileNameInv, "READ")); + if (!fileInv || !fileInv->IsOpen()) { + std::cout << " input file " << fileNameInv << " does not exist!" << std::endl; + return; + } + fileInv->cd(); + gDirectory->GetObject("voxResTree", voxResTreeInverse); + } + auto userInfo = voxResTree->GetUserInfo(); if (!userInfo->FindObject("y2xBinning") || !userInfo->FindObject("z2xBinning")) { @@ -140,7 +152,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", corrHelper->setNthreadsToMaximum(); // corrHelper->setNthreads(1); - auto corrPtr = corrHelper->createFromTrackResiduals(trackResiduals, voxResTree, useSmoothed, invertSigns); + auto corrPtr = corrHelper->createFromTrackResiduals(trackResiduals, voxResTree, voxResTreeInverse, useSmoothed, invertSigns); std::unique_ptr fastTransform( helper->create(0, *corrPtr)); @@ -168,11 +180,11 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const o2::gpu::TPCFastTransformGeo& geo = helper->getGeometry(); - // for (int32_t iRoc = 0; iRoc < geo.getNumberOfSlices(); iRoc++) { - for (int32_t iRoc = 0; iRoc < 1; iRoc++) { + // for (int32_t iSector = 0; iSector < geo.getNumberOfSectors(); iSector++) { + for (int32_t iSector = 0; iSector < 1; iSector++) { for (int32_t iRow = 0; iRow < geo.getNumberOfRows(); iRow++) { - auto& info = corr.getSliceRowInfo(iRoc, iRow); - std::cout << "roc " << iRoc << " row " << iRow + auto& info = corr.getSectorRowInfo(iSector, iRow); + std::cout << "sector " << iSector << " row " << iRow << " gridV0 " << info.gridV0 << " gridCorrU0 " << info.gridCorrU0 << " gridCorrV0 " << info.gridCorrV0 << " scaleCorrUtoGrid " << info.scaleCorrUtoGrid << " scaleCorrVtoGrid " << info.scaleCorrVtoGrid << " gridU0 " << info.gridU0 << " scaleUtoGrid " << info.scaleUtoGrid << " scaleVtoGrid " << info.scaleVtoGrid @@ -189,7 +201,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", // the difference double maxDiff[3] = {0., 0., 0.}; - int32_t maxDiffRoc[3] = {0, 0, 0}; + int32_t maxDiffSector[3] = {0, 0, 0}; int32_t maxDiffRow[3] = {0, 0, 0}; double sumDiff[3] = {0., 0., 0.}; @@ -207,7 +219,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", // measured x,y,z; corrections cx,cy,cz from the measured to the real x,y,z; // inverse corrections ix,iy,iz at the real position (x+cx,y+cy,z+cz) // ideally, ix = cx, iy = cy, iz = cz - TNtuple* debugCorr = new TNtuple("corr", "corr", "iRoc:iRow:x:y:z:cx:cy:cz:ix:iy:iz"); + TNtuple* debugCorr = new TNtuple("corr", "corr", "iSector:iRow:x:y:z:cx:cy:cz:ix:iy:iz"); debugCorr->SetMarkerStyle(8); debugCorr->SetMarkerSize(0.1); @@ -216,7 +228,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", // ntuple with the input data: voxels and corrections debugFile->cd(); TNtuple* debugVox = - new TNtuple("vox", "vox", "iRoc:iRow:n:x:y:z:vx:vy:vz"); + new TNtuple("vox", "vox", "iSector:iRow:n:x:y:z:vx:vy:vz"); debugVox->SetMarkerStyle(8); debugVox->SetMarkerSize(0.8); @@ -225,7 +237,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", // duplicate of debugVox + the spline data at voxels in a different color debugFile->cd(); TNtuple* debugCorrVox = - new TNtuple("corrvox", "corrvox", "iRoc:iRow:n:x:y:z:vx:vy:vz:cx:cy:cz:ix:iy:iz"); + new TNtuple("corrvox", "corrvox", "iSector:iRow:n:x:y:z:vx:vy:vz:cx:cy:cz:ix:iy:iz"); debugCorrVox->SetMarkerStyle(8); debugCorrVox->SetMarkerSize(0.8); @@ -233,7 +245,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", // corrections at the spline grid points debugFile->cd(); - TNtuple* debugGrid = new TNtuple("grid", "grid", "iRoc:iRow:x:y:z:cx:cy:cz:ix:iy:iz"); + TNtuple* debugGrid = new TNtuple("grid", "grid", "iSector:iRow:x:y:z:cx:cy:cz:ix:iy:iz"); debugGrid->SetMarkerStyle(8); debugGrid->SetMarkerSize(1.2); @@ -242,7 +254,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", // ntuple with data points created from voxels (with the data smearing, extension to the edges etc.) debugFile->cd(); TNtuple* debugPoints = - new TNtuple("points", "points", "iRoc:iRow:x:y:z:px:py:pz:cx:cy:cz"); + new TNtuple("points", "points", "iSector:iRow:x:y:z:px:py:pz:cx:cy:cz"); debugPoints->SetMarkerStyle(8); debugPoints->SetMarkerSize(0.4); @@ -256,32 +268,13 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const o2::gpu::TPCFastTransformGeo& geo = helper->getGeometry(); - auto getAllCorrections = [&](int iRoc, int iRow, float u, float v, float& x, float& y, float& z, float& cx, float& cy, float& cz, float& ix, float& iy, float& iz) { - // define x,y,z - - x = geo.getRowInfo(iRow).x; - geo.convUVtoLocal(iRoc, u, v, y, z); - + auto getAllCorrections = [&](int iSector, int iRow, float y, float z, float& cx, float& cy, float& cz, float& ix, float& iy, float& iz) { // get the corrections cx,cy,cz at x,y,z - float cu, cv; - corr.getCorrection(iRoc, iRow, u, v, cx, cu, cv); - geo.convUVtoLocal(iRoc, cu, cv, cy, cz); - - float corrected_u = u + cu; - float corrected_v = v + cv; - float corrected_x = x + cx; - float corrected_y, corrected_z; - geo.convUVtoLocal(iRoc, corrected_u, corrected_v, corrected_y, corrected_z); - - // get the inverse corrections ix,iy,iz at the corrected x,y,z - float inverted_x, inverted_u, inverted_v, inverted_y, inverted_z; - corr.getCorrectionInvCorrectedX(iRoc, iRow, corrected_u, corrected_v, inverted_x); - corr.getCorrectionInvUV(iRoc, iRow, corrected_u, corrected_v, inverted_u, inverted_v); - geo.convUVtoLocal(iRoc, inverted_u, inverted_v, inverted_y, inverted_z); - - ix = corrected_x - inverted_x; - iy = corrected_y - inverted_y; - iz = corrected_z - inverted_z; + std::tie(cx, cy, cz) = corr.getCorrectionLocal(iSector, iRow, y, z); + float realY = y + cy; + float realZ = z + cz; + ix = corr.getCorrectionXatRealYZ(iSector, iRow, realY, realZ); + std::tie(iy, iz) = corr.getCorrectionYZatRealYZ(iSector, iRow, realY, realZ); }; o2::tpc::TrackResiduals::VoxRes* v = nullptr; @@ -289,7 +282,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", branch->SetAddress(&v); branch->SetAutoDelete(kTRUE); - int32_t iRocLast = -1; + int32_t iSectorLast = -1; int32_t iRowLast = -1; std::cout << "fill debug ntuples at voxels ..." << std::endl; @@ -309,10 +302,10 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", int32_t z2xBin = v->bvox[o2::tpc::TrackResiduals::VoxZ]; // bin number in z/x 0..4 - int32_t iRoc = (int32_t)v->bsec; + int32_t iSector = (int32_t)v->bsec; int32_t iRow = (int32_t)xBin; - iRocLast = iRoc; + iSectorLast = iSector; iRowLast = iRow; double x = trackResiduals.getX(xBin); // radius of the pad row @@ -326,7 +319,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", double y = x * y2x; double z = x * z2x; - if (iRoc >= geo.getNumberOfSlicesA()) { + if (iSector >= geo.getNumberOfSectorsA()) { z = -z; } @@ -345,19 +338,18 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", z = x * v->stat[o2::tpc::TrackResiduals::VoxZ]; } - float u, v; - geo.convLocalToUV(iRoc, y, z, u, v); - float x1, y1, z1, cx, cy, cz, ix, iy, iz; - getAllCorrections(iRoc, iRow, u, v, x1, y1, z1, cx, cy, cz, ix, iy, iz); + float cx, cy, cz, ix, iy, iz; + getAllCorrections(iSector, iRow, y, z, cx, cy, cz, ix, iy, iz); - double d[3] = {cx - correctionX, cy - correctionY, cz - correctionZ}; if (voxEntries >= 1.) { + double d[3] = {cx - correctionX, cy - correctionY, cz - correctionZ}; + for (int32_t i = 0; i < 3; i++) { if (fabs(maxDiff[i]) < fabs(d[i])) { maxDiff[i] = d[i]; - maxDiffRoc[i] = iRoc; + maxDiffSector[i] = iSector; maxDiffRow[i] = iRow; - // std::cout << " roc " << iRoc << " row " << iRow << " xyz " << i + // std::cout << " sector " << iSector << " row " << iRow << " xyz " << i // << " diff " << d[i] << " entries " << voxEntries << " y " << y2xBin << " z " << z2xBin << std::endl; } sumDiff[i] += d[i] * d[i]; @@ -365,80 +357,81 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", nDiff++; } - debugVox->Fill(iRoc, iRow, voxEntries, x, y, z, correctionX, correctionY, correctionZ); + debugVox->Fill(iSector, iRow, voxEntries, x, y, z, correctionX, correctionY, correctionZ); - debugCorrVox->Fill(iRoc, iRow, voxEntries, x, y, z, correctionX, correctionY, correctionZ, + debugCorrVox->Fill(iSector, iRow, voxEntries, x, y, z, correctionX, correctionY, correctionZ, cx, cy, cz, ix, iy, iz); } std::cout << "fill debug ntuples everywhere .." << std::endl; - for (int32_t iRoc = 0; iRoc < geo.getNumberOfSlices(); iRoc++) { - // for (int32_t iRoc = 0; iRoc < 1; iRoc++) { - std::cout << "debug ntules for roc " << iRoc << std::endl; + for (int32_t iSector = 0; iSector < geo.getNumberOfSectors(); iSector++) { + // for (int32_t iSector = 0; iSector < 1; iSector++) { + std::cout << "debug ntules for sector " << iSector << std::endl; for (int32_t iRow = 0; iRow < geo.getNumberOfRows(); iRow++) { double x = geo.getRowInfo(iRow).x; // the spline grid - const auto& gridU = corr.getSpline(iRoc, iRow).getGridX1(); - const auto& gridV = corr.getSpline(iRoc, iRow).getGridX2(); - if (iRoc == 0 && iRow == 0) { + const auto& gridY = corr.getSpline(iSector, iRow).getGridX1(); + const auto& gridZ = corr.getSpline(iSector, iRow).getGridX2(); + if (iSector == 0 && iRow == 0) { std::cout << "spline scenario " << corr.getRowInfo(iRow).splineScenarioID << std::endl; - std::cout << "spline grid U: u = " << 0 << ".." << gridU.getUmax() << ", x = " << gridU.getXmin() << ".." << gridU.getXmax() << std::endl; - std::cout << "spline grid V: u = " << 0 << ".." << gridV.getUmax() << ", x = " << gridV.getXmin() << ".." << gridV.getXmax() << std::endl; + std::cout << "spline grid Y: u = " << 0 << ".." << gridY.getUmax() << ", x = " << gridY.getXmin() << ".." << gridY.getXmax() << std::endl; + std::cout << "spline grid Z: u = " << 0 << ".." << gridZ.getUmax() << ", x = " << gridZ.getXmin() << ".." << gridZ.getXmax() << std::endl; } // the correction { - std::vector p[2], g[2]; - - p[0].push_back(geo.getRowInfo(iRow).getUmin()); - for (int32_t iu = 0; iu < gridU.getNumberOfKnots(); iu++) { - float u, v; - corr.convGridToUV(iRoc, iRow, gridU.getKnot(iu).getU(), 0., u, v); - g[0].push_back(u); - p[0].push_back(u); + std::vector points[2], knots[2]; + + auto [yMin, yMax] = geo.getRowInfo(iRow).getYrange(); + auto [zMin, zMax] = geo.getZrange(iSector); + + points[0].push_back(yMin); + points[0].push_back(yMax); + points[1].push_back(zMin); + points[1].push_back(zMax); + + for (int32_t iu = 0; iu < gridY.getNumberOfKnots(); iu++) { + auto [y, z] = corr.convGridToLocal(iSector, iRow, gridY.getKnot(iu).getU(), 0.); + knots[0].push_back(y); + points[0].push_back(y); } - p[0].push_back(geo.getRowInfo(iRow).getUmax()); - - p[1].push_back(0.); - for (int32_t iv = 0; iv < gridV.getNumberOfKnots(); iv++) { - float u, v; - corr.convGridToUV(iRoc, iRow, 0., gridV.getKnot(iv).getU(), u, v); - g[1].push_back(v); - p[1].push_back(v); + for (int32_t iv = 0; iv < gridZ.getNumberOfKnots(); iv++) { + auto [y, z] = corr.convGridToLocal(iSector, iRow, 0., gridZ.getKnot(iv).getU()); + knots[1].push_back(z); + points[1].push_back(z); } - p[1].push_back(geo.getTPCzLength(iRoc)); - for (int32_t iuv = 0; iuv < 2; iuv++) { - int32_t n = p[iuv].size(); + for (int32_t iyz = 0; iyz <= 1; iyz++) { + std::sort(knots[iyz].begin(), knots[iyz].end()); + std::sort(points[iyz].begin(), points[iyz].end()); + int32_t n = points[iyz].size(); for (int32_t i = 0; i < n - 1; i++) { - double d = (p[iuv][i + 1] - p[iuv][i]) / 10.; + double d = (points[iyz][i + 1] - points[iyz][i]) / 10.; for (int32_t ii = 1; ii < 10; ii++) { - p[iuv].push_back(p[iuv][i] + d * ii); + points[iyz].push_back(points[iyz][i] + d * ii); } } - std::sort(p[iuv].begin(), p[iuv].end()); + std::sort(points[iyz].begin(), points[iyz].end()); } for (int32_t iter = 0; iter < 2; iter++) { - std::vector& pu = ((iter == 0) ? g[0] : p[0]); - std::vector& pv = ((iter == 0) ? g[1] : p[1]); - for (uint32_t iu = 0; iu < pu.size(); iu++) { - for (uint32_t iv = 0; iv < pv.size(); iv++) { - float u = pu[iu]; - float v = pv[iv]; - - float x, y, z, cx, cy, cz, ix, iy, iz; - getAllCorrections(iRoc, iRow, u, v, x, y, z, cx, cy, cz, ix, iy, iz); - + std::vector& py = ((iter == 0) ? knots[0] : points[0]); + std::vector& pz = ((iter == 0) ? knots[1] : points[1]); + for (uint32_t iu = 0; iu < py.size(); iu++) { + for (uint32_t iv = 0; iv < pz.size(); iv++) { + float y = py[iu]; + float z = pz[iv]; + float cx, cy, cz, ix, iy, iz; + getAllCorrections(iSector, iRow, y, z, cx, cy, cz, ix, iy, iz); if (iter == 0) { - debugGrid->Fill(iRoc, iRow, x, y, z, cx, cy, cz, ix, iy, iz); + debugGrid->Fill(iSector, iRow, x, y, z, cx, cy, cz, ix, iy, iz); } else { - debugCorr->Fill(iRoc, iRow, x, y, z, cx, cy, cz, ix, iy, iz); + debugCorr->Fill(iSector, iRow, x, y, z, cx, cy, cz, ix, iy, iz); } } } @@ -451,7 +444,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", o2::gpu::TPCFastSpaceChargeCorrectionMap& map = corrHelper->getCorrectionMap(); - auto& points = map.getPoints(iRoc, iRow); + auto& points = map.getPoints(iSector, iRow); for (uint32_t ip = 0; ip < points.size(); ip++) { auto point = points[ip]; @@ -461,14 +454,10 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", float correctionY = point.mDy; float correctionZ = point.mDz; - float u, v, cx, cu, cv, cy, cz; - geo.convLocalToUV(iRoc, y, z, u, v); - corr.getCorrection(iRoc, iRow, u, v, cx, cu, cv); - geo.convUVtoLocal(iRoc, u + cu, v + cv, cy, cz); - cy -= y; - cz -= z; + auto [cx, cy, cz] = + corr.getCorrectionLocal(iSector, iRow, y, z); - debugPoints->Fill(iRoc, iRow, x, y, z, correctionX, correctionY, + debugPoints->Fill(iSector, iRow, x, y, z, correctionX, correctionY, correctionZ, cx, cy, cz); } } @@ -478,14 +467,14 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", sumDiff[i] = sqrt(sumDiff[i]) / nDiff; } - std::cout << "Max difference in x : " << maxDiff[0] << " at ROC " - << maxDiffRoc[0] << " row " << maxDiffRow[0] << std::endl; + std::cout << "Max difference in x : " << maxDiff[0] << " at Sector " + << maxDiffSector[0] << " row " << maxDiffRow[0] << std::endl; - std::cout << "Max difference in y : " << maxDiff[1] << " at ROC " - << maxDiffRoc[1] << " row " << maxDiffRow[1] << std::endl; + std::cout << "Max difference in y : " << maxDiff[1] << " at Sector " + << maxDiffSector[1] << " row " << maxDiffRow[1] << std::endl; - std::cout << "Max difference in z : " << maxDiff[2] << " at ROC " - << maxDiffRoc[2] << " row " << maxDiffRow[2] << std::endl; + std::cout << "Max difference in z : " << maxDiff[2] << " at Sector " + << maxDiffSector[2] << " row " << maxDiffRow[2] << std::endl; std::cout << "Mean difference in x,y,z : " << sumDiff[0] << " " << sumDiff[1] << " " << sumDiff[2] << std::endl; From 458532fa7e75ee72a887b2fbb892e22b1f6d320b Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Sun, 6 Apr 2025 18:43:55 +0000 Subject: [PATCH 508/701] TPC Splines: cleanup --- GPU/TPCFastTransformation/Spline1D.h | 2 +- GPU/TPCFastTransformation/Spline1DSpec.h | 58 ++++++++++--------- GPU/TPCFastTransformation/Spline2DHelper.cxx | 2 +- GPU/TPCFastTransformation/Spline2DSpec.h | 36 ++++++------ GPU/TPCFastTransformation/SplineHelper.cxx | 4 +- GPU/TPCFastTransformation/SplineSpec.h | 20 +++---- .../TPCFastSpaceChargeCorrection.h | 6 +- 7 files changed, 66 insertions(+), 62 deletions(-) diff --git a/GPU/TPCFastTransformation/Spline1D.h b/GPU/TPCFastTransformation/Spline1D.h index ccadaeed23b79..c7a4d927dec1d 100644 --- a/GPU/TPCFastTransformation/Spline1D.h +++ b/GPU/TPCFastTransformation/Spline1D.h @@ -77,7 +77,7 @@ namespace gpu /// One can store all F-dependent spline parameters outside of the spline object /// and provide them at each interpolation call. /// To do so, create a spline with nYdimensions=0; create spline parameters for F via Spline1DHelper class; -/// then use special interpolateU(..) methods for the interpolation. +/// then use special interpolateAtU(..) methods for the interpolation. /// /// This feature allows one to use the same spline object for the approximation of different functions /// on the same grid of knots. diff --git a/GPU/TPCFastTransformation/Spline1DSpec.h b/GPU/TPCFastTransformation/Spline1DSpec.h index 2cc95ebdcab9f..fcabbbad12098 100644 --- a/GPU/TPCFastTransformation/Spline1DSpec.h +++ b/GPU/TPCFastTransformation/Spline1DSpec.h @@ -287,33 +287,38 @@ class Spline1DSpec : public Spline1DContainer /// Get interpolated value S(x) GPUd() void interpolate(DataT x, GPUgeneric() DataT S[/*mYdim*/]) const { - interpolateU(mYdim, mParameters, convXtoU(x), S); + interpolateAtU(mYdim, mParameters, convXtoU(x), S); } /// Get interpolated value for an nYdim-dimensional S(u) using spline parameters Parameters. template - GPUd() void interpolateU(int32_t inpYdim, GPUgeneric() const DataT Parameters[], - DataT u, GPUgeneric() DataT S[/*nYdim*/]) const + GPUd() void interpolateAtU(int32_t inpYdim, GPUgeneric() const DataT Parameters[], + DataT u, GPUgeneric() DataT S[/*nYdim*/]) const { const auto nYdimTmp = SplineUtil::getNdim(inpYdim); const auto nYdim = nYdimTmp.get(); int32_t iknot = TBase::template getLeftKnotIndexForU(u); const DataT* d = Parameters + (2 * nYdim) * iknot; - interpolateU(nYdim, getKnots()[iknot], &(d[0]), &(d[nYdim]), &(d[2 * nYdim]), &(d[3 * nYdim]), u, S); + interpolateAtU(nYdim, getKnots()[iknot], &(d[0]), &(d[nYdim]), &(d[2 * nYdim]), &(d[3 * nYdim]), u, S); } /// The main mathematical utility. /// Get interpolated value {S(u): 1D -> nYdim} at the segment [knotL, next knotR] /// using the spline values Sl, Sr and the slopes Dl, Dr template - GPUd() void interpolateU(int32_t inpYdim, const Knot& knotL, - GPUgeneric() const T Sl[/*mYdim*/], GPUgeneric() const T Dl[/*mYdim*/], - GPUgeneric() const T Sr[/*mYdim*/], GPUgeneric() const T Dr[/*mYdim*/], - DataT u, GPUgeneric() T S[/*mYdim*/]) const + GPUd() void interpolateAtU(int32_t inpYdim, const Knot& knotL, + GPUgeneric() const T Sl[/*mYdim*/], GPUgeneric() const T Dl[/*mYdim*/], + GPUgeneric() const T Sr[/*mYdim*/], GPUgeneric() const T Dr[/*mYdim*/], + DataT u, GPUgeneric() T S[/*mYdim*/]) const { const auto nYdimTmp = SplineUtil::getNdim(inpYdim); const auto nYdim = nYdimTmp.get(); + auto [dSdSl, dSdDl, dSdSr, dSdDr] = getSderivativesOverParsAtU(knotL, u); + for (int32_t dim = 0; dim < nYdim; ++dim) { + S[dim] = dSdSr * Sr[dim] + dSdSl * Sl[dim] + dSdDl * Dl[dim] + dSdDr * Dr[dim]; + } + /* if (u < (DataT)0) { u = (DataT)0; } @@ -330,6 +335,7 @@ class Spline1DSpec : public Spline1DContainer T b = df - Dl[dim] - a; S[dim] = ((a * v + b) * v + Dl[dim]) * uu + Sl[dim]; } + */ /* another way to calculate f(u): T uu = T(u - knotL.u); @@ -345,11 +351,10 @@ class Spline1DSpec : public Spline1DContainer } template - GPUd() void getUderivatives(const Knot& knotL, DataT u, - T& dSl, T& dDl, T& dSr, T& dDr) const + GPUd() std::tuple getSderivativesOverParsAtU(const Knot& knotL, DataT u) const { /// Get derivatives of the interpolated value {S(u): 1D -> nYdim} at the segment [knotL, next knotR] - /// over the spline values Sl, Sr and the slopes Dl, Dr + /// over the spline parameters Sl(eft), Sr(ight) and the slopes Dl, Dr if (u < (DataT)0) { u = (DataT)0; @@ -363,11 +368,12 @@ class Spline1DSpec : public Spline1DContainer T vm1 = v - 1.; T a = u * vm1; T v2 = v * v; - dSr = v2 * (3. - 2 * v); - dSl = 1. - dSr; - dDl = vm1 * a; - dDr = v * a; - // F(u) = dSl * Sl + dSr * Sr + dDl * Dl + dDr * Dr; + T dSdSr = v2 * (3. - 2 * v); + T dSdSl = 1. - dSdSr; + T dSdDl = vm1 * a; + T dSdDr = v * a; + // S(u) = dSdSl * Sl + dSdSr * Sr + dSdDl * Dl + dSdDr * Dr; + return std::make_tuple(dSdSl, dSdDl, dSdSr, dSdDr); } /* template @@ -480,21 +486,21 @@ class Spline1DSpec /// Get interpolated value for an YdimT-dimensional S(u) using spline parameters Parameters. template - GPUd() void interpolateU(GPUgeneric() const DataT Parameters[], - DataT u, GPUgeneric() DataT S[/*nYdim*/]) const + GPUd() void interpolateAtU(GPUgeneric() const DataT Parameters[], + DataT u, GPUgeneric() DataT S[/*nYdim*/]) const { - TBase::template interpolateU(YdimT, Parameters, u, S); + TBase::template interpolateAtU(YdimT, Parameters, u, S); } /// Get interpolated value for an YdimT-dimensional S(u) at the segment [knotL, next knotR] /// using the spline values Sl, Sr and the slopes Dl, Dr template - GPUd() void interpolateU(const typename TBase::Knot& knotL, - GPUgeneric() const T Sl[/*mYdim*/], GPUgeneric() const T Dl[/*mYdim*/], - GPUgeneric() const T Sr[/*mYdim*/], GPUgeneric() const T Dr[/*mYdim*/], - DataT u, GPUgeneric() T S[/*mYdim*/]) const + GPUd() void interpolateAtU(const typename TBase::Knot& knotL, + GPUgeneric() const T Sl[/*mYdim*/], GPUgeneric() const T Dl[/*mYdim*/], + GPUgeneric() const T Sr[/*mYdim*/], GPUgeneric() const T Dr[/*mYdim*/], + DataT u, GPUgeneric() T S[/*mYdim*/]) const { - TBase::interpolateU(YdimT, knotL, Sl, Dl, Sr, Dr, u, S); + TBase::interpolateAtU(YdimT, knotL, Sl, Dl, Sr, Dr, u, S); } using TBase::getNumberOfKnots; @@ -504,7 +510,7 @@ class Spline1DSpec #if !defined(GPUCA_GPUCODE) using TBase::recreate; #endif - using TBase::interpolateU; + using TBase::interpolateAtU; }; /// ================================================================================================== @@ -552,7 +558,7 @@ class Spline1DSpec /// _______ Expert tools: interpolation with given nYdim and external Parameters _______ - using TBase::interpolateU; + using TBase::interpolateAtU; ClassDefNV(Spline1DSpec, 0); }; diff --git a/GPU/TPCFastTransformation/Spline2DHelper.cxx b/GPU/TPCFastTransformation/Spline2DHelper.cxx index 03ecf4a3f1707..bec6fc58ff8d7 100644 --- a/GPU/TPCFastTransformation/Spline2DHelper.cxx +++ b/GPU/TPCFastTransformation/Spline2DHelper.cxx @@ -194,7 +194,7 @@ void Spline2DHelper::approximateFunction( for (int32_t ipu = 0; ipu < nDataPointsU; ipu++) { double splineF[Ndim]; double u = mHelperU1.getDataPoint(ipu).u; - mHelperU1.getSpline().interpolateU(Ndim, parUdbl.get(), u, splineF); + mHelperU1.getSpline().interpolateAtU(Ndim, parUdbl.get(), u, splineF); for (int32_t dim = 0; dim < Ndim; dim++) { rotDataPointF[(ipu * nDataPointsV + ipv) * Ndim + dim] = splineF[dim]; } diff --git a/GPU/TPCFastTransformation/Spline2DSpec.h b/GPU/TPCFastTransformation/Spline2DSpec.h index b4d351e8d0407..d235b7ddfde07 100644 --- a/GPU/TPCFastTransformation/Spline2DSpec.h +++ b/GPU/TPCFastTransformation/Spline2DSpec.h @@ -239,13 +239,13 @@ class Spline2DSpec /// Get interpolated value S(x) GPUd() void interpolate(DataT x1, DataT x2, GPUgeneric() DataT S[/*mYdim*/]) const { - interpolateU(mYdim, mParameters, mGridX1.convXtoU(x1), mGridX2.convXtoU(x2), S); + interpolateAtU(mYdim, mParameters, mGridX1.convXtoU(x1), mGridX2.convXtoU(x2), S); } /// Get interpolated value for an inpYdim-dimensional S(u1,u2) using spline parameters Parameters. template - GPUd() void interpolateUold(int32_t inpYdim, GPUgeneric() const DataT Parameters[], - DataT u1, DataT u2, GPUgeneric() DataT S[/*inpYdim*/]) const + GPUd() void interpolateAtUold(int32_t inpYdim, GPUgeneric() const DataT Parameters[], + DataT u1, DataT u2, GPUgeneric() DataT S[/*inpYdim*/]) const { const auto nYdimTmp = SplineUtil::getNdim(inpYdim); @@ -295,7 +295,7 @@ class Spline2DSpec typedef Spline1DSpec TGridX1; const TGridX1& gridX1 = reinterpret_cast(mGridX1); - gridX1.interpolateU(nYdim4, knotU, Su0, Du0, Su1, Du1, u, parU); + gridX1.interpolateAtU(nYdim4, knotU, Su0, Du0, Su1, Du1, u, parU); const DataT* Sv0 = parU + 0; const DataT* Dv0 = parU + nYdim; @@ -304,13 +304,13 @@ class Spline2DSpec typedef Spline1DSpec TGridX2; const TGridX2& gridX2 = reinterpret_cast(mGridX2); - gridX2.interpolateU(nYdim, knotV, Sv0, Dv0, Sv1, Dv1, v, S); + gridX2.interpolateAtU(nYdim, knotV, Sv0, Dv0, Sv1, Dv1, v, S); } /// Get interpolated value for an inpYdim-dimensional S(u1,u2) using spline parameters Parameters. template - GPUd() void interpolateU(int32_t inpYdim, GPUgeneric() const DataT Parameters[], - DataT u1, DataT u2, GPUgeneric() DataT S[/*inpYdim*/]) const + GPUd() void interpolateAtU(int32_t inpYdim, GPUgeneric() const DataT Parameters[], + DataT u1, DataT u2, GPUgeneric() DataT S[/*inpYdim*/]) const { const auto nYdimTmp = SplineUtil::getNdim(inpYdim); @@ -334,10 +334,8 @@ class Spline2DSpec const DataT* A = Parameters + (nu * iv + iu) * nYdim4; // values { {Y1,Y2,Y3}, {Y1,Y2,Y3}'v, {Y1,Y2,Y3}'u, {Y1,Y2,Y3}''vu } at {u0, v0} const DataT* B = A + nYdim4 * nu; // values { ... } at {u0, v1} - DataT dSl, dDl, dSr, dDr; - mGridX1.getUderivatives(knotU, u, dSl, dDl, dSr, dDr); - DataT dSd, dDd, dSu, dDu; - mGridX2.getUderivatives(knotV, v, dSd, dDd, dSu, dDu); + auto [dSl, dDl, dSr, dDr] = mGridX1.template getSderivativesOverParsAtU(knotU, u); + auto [dSd, dDd, dSu, dDu] = mGridX2.template getSderivativesOverParsAtU(knotV, v); // when nYdim == 1: // S = dSl * (dSd * A[0] + dDd * A[1]) + dDl * (dSd * A[2] + dDd * A[3]) + @@ -430,18 +428,18 @@ class Spline2DSpec /// Get interpolated value for an YdimT-dimensional S(u1,u2) using spline parameters Parameters. template - GPUd() void interpolateU(GPUgeneric() const DataT Parameters[], - DataT u1, DataT u2, GPUgeneric() DataT S[/*nYdim*/]) const + GPUd() void interpolateAtU(GPUgeneric() const DataT Parameters[], + DataT u1, DataT u2, GPUgeneric() DataT S[/*nYdim*/]) const { - TBase::template interpolateU(YdimT, Parameters, u1, u2, S); + TBase::template interpolateAtU(YdimT, Parameters, u1, u2, S); } /// Get interpolated value for an YdimT-dimensional S(u1,u2) using spline parameters Parameters. template - GPUd() void interpolateUold(GPUgeneric() const DataT Parameters[], - DataT u1, DataT u2, GPUgeneric() DataT S[/*nYdim*/]) const + GPUd() void interpolateAtUold(GPUgeneric() const DataT Parameters[], + DataT u1, DataT u2, GPUgeneric() DataT S[/*nYdim*/]) const { - TBase::template interpolateUold(YdimT, Parameters, u1, u2, S); + TBase::template interpolateAtUold(YdimT, Parameters, u1, u2, S); } using TBase::getNumberOfKnots; @@ -451,7 +449,7 @@ class Spline2DSpec #if !defined(GPUCA_GPUCODE) using TBase::recreate; #endif - using TBase::interpolateU; + using TBase::interpolateAtU; }; /// ================================================================================================== @@ -507,7 +505,7 @@ class Spline2DSpec /// _______ Expert tools: interpolation with given nYdim and external Parameters _______ - using TBase::interpolateU; + using TBase::interpolateAtU; }; /// ================================================================================================== diff --git a/GPU/TPCFastTransformation/SplineHelper.cxx b/GPU/TPCFastTransformation/SplineHelper.cxx index 6e1b53510e0d0..af3efb1c4817d 100644 --- a/GPU/TPCFastTransformation/SplineHelper.cxx +++ b/GPU/TPCFastTransformation/SplineHelper.cxx @@ -410,8 +410,8 @@ void SplineHelper::approximateFunction( } double splineF[mFdimensions]; double u = mHelpers[dimension].getDataPoint(i).u; - mHelpers[dimension].getSpline().interpolateU(mFdimensions, parD[dimension].get(), u, splineF); // recalculate at all datapoints of dimension - for (int32_t dim = 0; dim < mFdimensions; dim++) { // writing it in allParameters + mHelpers[dimension].getSpline().interpolateAtU(mFdimensions, parD[dimension].get(), u, splineF); // recalculate at all datapoints of dimension + for (int32_t dim = 0; dim < mFdimensions; dim++) { // writing it in allParameters // LOG(info)< : public SplineContainer for (int32_t i = 0; i < nXdim; i++) { u[i] = mGrid[i].convXtoU(x[i]); } - interpolateU(mXdim, mYdim, mParameters, u, S); + interpolateAtU(mXdim, mYdim, mParameters, u, S); } /// Get interpolated value for S(u):inpXdim->inpYdim using spline parameters Parameters template - GPUd() void interpolateU(int32_t inpXdim, int32_t inpYdim, GPUgeneric() const DataT Parameters[], - const DataT u[/*inpXdim*/], GPUgeneric() DataT S[/*inpYdim*/]) const + GPUd() void interpolateAtU(int32_t inpXdim, int32_t inpYdim, GPUgeneric() const DataT Parameters[], + const DataT u[/*inpXdim*/], GPUgeneric() DataT S[/*inpYdim*/]) const { const auto nXdimTmp = SplineUtil::getNdim(mXdim); const auto nXdim = nXdimTmp.get(); @@ -345,7 +345,7 @@ class SplineSpec : public SplineContainer DataT coordinate = u[d]; typedef Spline1DSpec TGridX; const TGridX& gridX = *((const TGridX*)&(mGrid[d])); - gridX.interpolateU(nInterpolations, knotL, S0, D0, S1, D1, coordinate, iParameters); + gridX.interpolateAtU(nInterpolations, knotL, S0, D0, S1, D1, coordinate, iParameters); nInterpolations /= 4; nKnots /= 2; } // end d (every dimension) @@ -354,7 +354,7 @@ class SplineSpec : public SplineContainer S[i] = iParameters[i]; // write into result-array // LOG(info)< /// Get interpolated value for an YdimT-dimensional S(u1,u2) using spline parameters Parameters. template - GPUd() void interpolateU(GPUgeneric() const DataT Parameters[], - const DataT u[/*XdimT*/], GPUgeneric() DataT S[/*YdimT*/]) const + GPUd() void interpolateAtU(GPUgeneric() const DataT Parameters[], + const DataT u[/*XdimT*/], GPUgeneric() DataT S[/*YdimT*/]) const { - TBase::template interpolateU(XdimT, YdimT, Parameters, u, S); + TBase::template interpolateAtU(XdimT, YdimT, Parameters, u, S); } /// _______________ Suppress some parent class methods ________________________ @@ -432,7 +432,7 @@ class SplineSpec #if !defined(GPUCA_GPUCODE) using TBase::recreate; #endif - using TBase::interpolateU; + using TBase::interpolateAtU; }; /// ================================================================================================== @@ -490,7 +490,7 @@ class SplineSpec /// _______ Expert tools: interpolation with given nYdim and external Parameters _______ - using TBase::interpolateU; + using TBase::interpolateAtU; /// Check dimensions void checkDimensions(int32_t& nXdim, int32_t& nYdim) diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index f84fde4fffd8c..76368efcd8a4f 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -410,7 +410,7 @@ GPUdi() std::tuple TPCFastSpaceChargeCorrection::getCorrect auto [gridU, gridV, scale] = convLocalToGrid(sector, row, y, z); float dxyz[3]; - spline.interpolateU(splineData, gridU, gridV, dxyz); + spline.interpolateAtU(splineData, gridU, gridV, dxyz); float dx = scale * GPUCommonMath::Clamp(dxyz[0], info.minCorr[0], info.maxCorr[0]); float dy = scale * GPUCommonMath::Clamp(dxyz[1], info.minCorr[1], info.maxCorr[1]); @@ -427,7 +427,7 @@ GPUdi() float TPCFastSpaceChargeCorrection::getCorrectionXatRealYZ(int32_t secto auto [gridU, gridV, scale] = convCorrectedLocalToGrid(sector, row, realY, realZ); float dx = 0; - spline.interpolateU(splineData, gridU, gridV, &dx); + spline.interpolateAtU(splineData, gridU, gridV, &dx); dx = scale * GPUCommonMath::Clamp(dx, info.minCorr[0], info.maxCorr[0]); return dx; @@ -443,7 +443,7 @@ GPUdi() std::tuple TPCFastSpaceChargeCorrection::getCorrectionYZat const float* splineData = getSplineData(sector, row, 2); float dyz[2]; - spline.interpolateU(splineData, gridU, gridV, dyz); + spline.interpolateAtU(splineData, gridU, gridV, dyz); dyz[0] = scale * GPUCommonMath::Clamp(dyz[0], info.minCorr[1], info.maxCorr[1]); dyz[1] = scale * GPUCommonMath::Clamp(dyz[1], info.minCorr[2], info.maxCorr[2]); From 47283cd6ac5df3046a2324387b9e776ec54ae367 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Fri, 11 Apr 2025 15:06:35 +0000 Subject: [PATCH 509/701] TPC Splines: fast merge of SC corrections --- .../TPCFastSpaceChargeCorrectionHelper.h | 10 +- .../TPCFastSpaceChargeCorrectionHelper.cxx | 163 ++++++++++++------ GPU/TPCFastTransformation/Spline1DSpec.h | 88 +++++----- GPU/TPCFastTransformation/Spline2DSpec.h | 135 ++++++++++++++- .../TPCFastSpaceChargeCorrection.h | 127 ++++++++++++-- 5 files changed, 401 insertions(+), 122 deletions(-) diff --git a/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h b/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h index abbc5b7116b2d..747ed74c9bcad 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h @@ -102,7 +102,15 @@ class TPCFastSpaceChargeCorrectionHelper /// initialise inverse transformation from linear combination of several input corrections void initInverse(std::vector& corrections, const std::vector& scaling, bool prn); - void MergeCorrections(std::vector& corrections, const std::vector& scaling, bool prn); + /// merge several corrections + /// \param mainCorrection main correction + /// \param scale scaling factor for the main correction + /// \param additionalCorrections vector of pairs of additional corrections and their scaling factors + /// \param prn printout flag + /// \return main correction merged with additional corrections + void MergeCorrections( + o2::gpu::TPCFastSpaceChargeCorrection& mainCorrection, float scale, + const std::vector>& additionalCorrections, bool prn); private: /// geometry initialization diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index 92817063831f6..6ba3d6e12dd9e 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -176,8 +176,8 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas } if (processingInverseCorrection) { - float* splineX = correction.getSplineData(sector, row, 1); - float* splineYZ = correction.getSplineData(sector, row, 2); + float* splineX = correction.getSplineDataInvX(sector, row); + float* splineYZ = correction.getSplineDataInvYZ(sector, row); for (int i = 0; i < spline.getNumberOfParameters() / 3; i++) { splineX[i] = splineParameters[3 * i + 0]; splineYZ[2 * i + 0] = splineParameters[3 * i + 1]; @@ -940,8 +940,8 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector& corrections, const std::vector& scaling, bool prn) +void TPCFastSpaceChargeCorrectionHelper::MergeCorrections( + o2::gpu::TPCFastSpaceChargeCorrection& mainCorrection, float mainScale, + const std::vector>& additionalCorrections, bool /*prn*/) { /// merge several corrections - /* + TStopwatch watch; LOG(info) << "fast space charge correction helper: Merge corrections"; - if (corrections.size() != scaling.size()) { - LOGP(error, "Input corrections and scaling values have different size"); - return; - } - - auto& correction = *(corrections.front()); + const auto& geo = mainCorrection.getGeometry(); - for (int sector = 0; sector < mGeo.getNumberOfSectors(); sector++) { + for (int sector = 0; sector < geo.getNumberOfSectors(); sector++) { auto myThread = [&](int iThread) { - for (int row = iThread; row < mGeo.getNumberOfRows(); row += mNthreads) { - TPCFastSpaceChargeCorrection::SplineType spline = correction.getSpline(sector, row); + for (int row = iThread; row < geo.getNumberOfRows(); row += mNthreads) { + const auto& spline = mainCorrection.getSpline(sector, row); - std::vector splineParameters(spline.getNumberOfParameters()); - std::vector splineParametersInvX(spline.getNumberOfParameters()); - std::vector splineParametersInvYZ(spline.getNumberOfParameters()); + float* splineParameters = mainCorrection.getSplineData(sector, row); + float* splineParametersInvX = mainCorrection.getSplineDataInvX(sector, row); + float* splineParametersInvYZ = mainCorrection.getSplineDataInvYZ(sector, row); - const auto& gridU = spline.getGridX1(); - const auto& gridV = spline.getGridX2(); - - for (int iu = 0; iu < gridU.getNumberOfKnots(); iu++) { - double u = gridU.getKnot(iu).u; - for (int iv = 0; iv < gridV.getNumberOfKnots(); iv++) { - int knotIndex = spline.getKnotIndex(iu, iv); + auto& secRowInfo = mainCorrection.getSectorRowInfo(sector, row); - double v = gridV.getKnot(iu).u; - auto [y, z] = correction.convGridToLocal(sector, row, u, v); - constexpr int nKnotPar1d = 4; - constexpr int nKnotPar2d = nKnotPar1d * 2; - constexpr int nKnotPar3d = nKnotPar1d * 3; + constexpr int nKnotPar1d = 4; + constexpr int nKnotPar2d = nKnotPar1d * 2; + constexpr int nKnotPar3d = nKnotPar1d * 3; - for (int i = 0; i < corrections.size(); ++i) { - double s = scaling[i]; - auto p = corrections[i]->getCorrectionParameters(sector, row, y, z); - for (int j = 0; j < nKnotPar3d; ++j) { - splineParameters[knotIndex * nKnotPar3d + j] += s * p[j]; + { // scale the main correction + for (int i = 0; i < 3; i++) { + secRowInfo.maxCorr[i] *= mainScale; + secRowInfo.minCorr[i] *= mainScale; + } + double parscale[4] = {mainScale, mainScale, mainScale, mainScale * mainScale}; + for (int iknot = 0, ind = 0; iknot < spline.getNumberOfKnots(); iknot++) { + for (int ipar = 0; ipar < nKnotPar1d; ++ipar) { + for (int idim = 0; idim < 3; idim++, ind++) { + splineParameters[ind] *= parscale[ipar]; } - auto pInvX = corrections[i]->getCorrectionParametersInvX(sector, row, y, z); - for (int j = 0; j < nKnotPar1d; ++j) { - splineParametersInvX[knotIndex * nKnotPar1d + j] += s * pInvX[j]; + } + } + for (int iknot = 0, ind = 0; iknot < spline.getNumberOfKnots(); iknot++) { + for (int ipar = 0; ipar < nKnotPar1d; ++ipar) { + for (int idim = 0; idim < 1; idim++, ind++) { + splineParametersInvX[ind] *= parscale[ipar]; } - auto pInvYZ = corrections[i]->getCorrectionParametersInvYZ(sector, row, y, z); - for (int j = 0; j < nKnotPar2d; ++j) { - splineParametersInvYZ[knotIndex * nKnotPar2d + j] += s * pInvYZ[j]; + } + } + for (int iknot = 0, ind = 0; iknot < spline.getNumberOfKnots(); iknot++) { + for (int ipar = 0; ipar < nKnotPar1d; ++ipar) { + for (int idim = 0; idim < 2; idim++, ind++) { + splineParametersInvYZ[ind] *= parscale[ipar]; } } - } // iv - } // iu + } + } - float* splineXYZ = correction.getSplineData(sector, row, 0); - float* splineInvX = correction.getSplineData(sector, row, 1); - float* splineInvYZ = correction.getSplineData(sector, row, 2); + // add the other corrections - for (int i = 0; i < spline.getNumberOfParameters(); i++) { - splineXYZ[i] = splineParameters[i]; - } - for (int i = 0; i < spline.getNumberOfParameters() / 3; i++) { - splineX[i] = splineParametersInvX[i]; - splineYZ[2 * i + 0] = splineParametersInvYZ[2 * i + 0]; - splineYZ[2 * i + 1] = splineParametersInvYZ[2 * i + 1]; - } + const auto& gridU = spline.getGridX1(); + const auto& gridV = spline.getGridX2(); + + for (int icorr = 0; icorr < additionalCorrections.size(); ++icorr) { + const auto& corr = *(additionalCorrections[icorr].first); + double scale = additionalCorrections[icorr].second; + auto& linfo = corr.getSectorRowInfo(sector, row); + secRowInfo.updateMaxValues(linfo.getMaxValues(), scale); + secRowInfo.updateMaxValues(linfo.getMinValues(), scale); + + double scaleU = secRowInfo.scaleUtoGrid / linfo.scaleUtoGrid; + double scaleV = secRowInfo.scaleVtoGrid / linfo.scaleVtoGrid; + + for (int iu = 0; iu < gridU.getNumberOfKnots(); iu++) { + double u = gridU.getKnot(iu).u; + for (int iv = 0; iv < gridV.getNumberOfKnots(); iv++) { + double v = gridV.getKnot(iu).u; + int knotIndex = spline.getKnotIndex(iu, iv); + float P[nKnotPar3d]; + + { // direct correction + auto [y, z] = mainCorrection.convGridToLocal(sector, row, u, v); + // return values: u, v, scaling factor + auto [lu, lv, ls] = corr.convLocalToGrid(sector, row, y, z); + ls *= scale; + double parscale[4] = {ls, ls * scaleU, ls * scaleV, ls * ls * scaleU * scaleV}; + const auto& spl = corr.getSpline(sector, row); + spl.interpolateParametersAtU(corr.getSplineData(sector, row), lu, lv, P); + for (int ipar = 0, ind = 0; ipar < nKnotPar1d; ++ipar) { + for (int idim = 0; idim < 3; idim++, ind++) { + splineParameters[knotIndex * nKnotPar3d + ind] += parscale[ipar] * P[ind]; + } + } + } + + auto [y, z] = mainCorrection.convGridToCorrectedLocal(sector, row, u, v); + // return values: u, v, scaling factor + auto [lu, lv, ls] = corr.convCorrectedLocalToGrid(sector, row, y, z); + ls *= scale; + double parscale[4] = {ls, ls * scaleU, ls * scaleV, ls * ls * scaleU * scaleV}; + + { // inverse X correction + corr.getSplineInvX(sector, row).interpolateParametersAtU(corr.getSplineDataInvX(sector, row), lu, lv, P); + for (int ipar = 0, ind = 0; ipar < nKnotPar1d; ++ipar) { + for (int idim = 0; idim < 1; idim++, ind++) { + splineParametersInvX[knotIndex * nKnotPar1d + ind] += parscale[ipar] * P[ind]; + } + } + } + + { // inverse YZ correction + corr.getSplineInvYZ(sector, row).interpolateParametersAtU(corr.getSplineDataInvYZ(sector, row), lu, lv, P); + for (int ipar = 0, ind = 0; ipar < nKnotPar1d; ++ipar) { + for (int idim = 0; idim < 2; idim++, ind++) { + splineParametersInvYZ[knotIndex * nKnotPar2d + ind] += parscale[ipar] * P[ind]; + } + } + } + + } // iv + } // iu + } // corrections } // row - }; // thread + }; // thread std::vector threads(mNthreads); @@ -1054,7 +1106,6 @@ void TPCFastSpaceChargeCorrectionHelper::MergeCorrections(std::vector : public Spline1DContainer for (int32_t dim = 0; dim < nYdim; ++dim) { S[dim] = dSdSr * Sr[dim] + dSdSl * Sl[dim] + dSdDl * Dl[dim] + dSdDr * Dr[dim]; } + /* + another way to calculate f(u): + if (u < (DataT)0) { u = (DataT)0; } @@ -336,18 +339,6 @@ class Spline1DSpec : public Spline1DContainer S[dim] = ((a * v + b) * v + Dl[dim]) * uu + Sl[dim]; } */ - /* - another way to calculate f(u): - T uu = T(u - knotL.u); - T v = uu * T(knotL.Li); // scaled u - T vm1 = v-1; - T v2 = v * v; - float cSr = v2*(3-2*v); - float cSl = 1-cSr; - float cDl = v*vm1*vm1*knotL.L; - float cDr = v2*vm1*knotL.L; - return cSl*Sl + cSr*Sr + cDl*Dl + cDr*Dr; - */ } template @@ -365,51 +356,50 @@ class Spline1DSpec : public Spline1DContainer u = u - knotL.u; T v = u * T(knotL.Li); // scaled u - T vm1 = v - 1.; + T vm1 = v - T(1.); T a = u * vm1; T v2 = v * v; - T dSdSr = v2 * (3. - 2 * v); - T dSdSl = 1. - dSdSr; + T dSdSr = v2 * (T(3.) - v - v); + T dSdSl = T(1.) - dSdSr; T dSdDl = vm1 * a; T dSdDr = v * a; // S(u) = dSdSl * Sl + dSdSr * Sr + dSdDl * Dl + dSdDr * Dr; return std::make_tuple(dSdSl, dSdDl, dSdSr, dSdDr); } - /* - template - GPUd() void getUsecondDerivatives(const Knot& knotL, DataT u, - T& dSl, T& dDl, T& dSr, T& dDr, - T& dSl2, T& dDl2, T& dSr2, T& dDr2) const - { - /// Get derivatives of the interpolated value {S(u): 1D -> nYdim} at the segment [knotL, next knotR] - /// over the spline values Sl, Sr and the slopes Dl, Dr - - if (u < (DataT)0) { - u = (DataT)0; - } - if (u > (DataT)TBase::getUmax()) { - u = (DataT)TBase::getUmax(); - } - - u = u - knotL.u; - T v = u * T(knotL.Li); // scaled u - T vm1 = v - 1.; - T a = u * vm1; - T v2 = v * v; - dSr = v2 * (3. - 2 * v); - dSl = 1. - dSr; - dDl = vm1 * a; - dDr = v * a; - T dv = T(knotL.Li); - dSr2 = 6. * v * (1. - v) * dv; - dSl2 = -dSr2; - dDl2 = (v - 1) * (3 * v - 1); - dDr = u * (v * v - v); - dDr2 = 3.f * v * v - 2.f * v; - // F(u) = dSl * Sl + dSr * Sr + dDl * Dl + dDr * Dr; - // dF(u)/du = dSl2 * Sl + dSr2 * Sr + dDl2 * Dl + dDr2 * Dr; + + template + GPUd() std::tuple getSDderivativesOverParsAtU(const Knot& knotL, DataT u) const + { + /// Get derivatives of the interpolated value {S(u): 1D -> nYdim} at the segment [knotL, next knotR] + /// over the spline values Sl, Sr and the slopes Dl, Dr + + if (u < (DataT)0) { + u = (DataT)0; } - */ + if (u > (DataT)TBase::getUmax()) { + u = (DataT)TBase::getUmax(); + } + + u = u - knotL.u; + T v = u * T(knotL.Li); // scaled u + T vm1 = v - T(1.); + T a = u * vm1; + T v2 = v * v; + T dSdSr = v2 * (T(3.) - v - v); + T dSdSl = T(1.) - dSdSr; + T dSdDl = vm1 * a; + T dSdDr = v * a; + + T dv = T(knotL.Li); + T dDdSr = 6. * v * (T(1.) - v) * dv; + T dDdSl = -dDdSr; + T dDdDl = vm1 * (v + v + vm1); + T dDdDr = v * (v + vm1 + vm1); + // S(u) = dSdSl * Sl + dSdSr * Sr + dSdDl * Dl + dSdDr * Dr; + // D(u) = dS(u)/du = dDdSl * Sl + dDdSr * Sr + dDdDl * Dl + dDdDr * Dr; + return std::make_tuple(dSdSl, dSdDl, dSdSr, dSdDr, dDdSl, dDdDl, dDdSr, dDdDr); + } + using TBase::convXtoU; using TBase::getKnot; using TBase::getKnots; diff --git a/GPU/TPCFastTransformation/Spline2DSpec.h b/GPU/TPCFastTransformation/Spline2DSpec.h index d235b7ddfde07..987ce1ad5d256 100644 --- a/GPU/TPCFastTransformation/Spline2DSpec.h +++ b/GPU/TPCFastTransformation/Spline2DSpec.h @@ -358,6 +358,132 @@ class Spline2DSpec } } + /// Get interpolated parameters (like parameters stored at knots) for an inpYdim-dimensional S(u1,u2) using spline parameters Parameters. + template + GPUd() void interpolateParametersAtU(int32_t inpYdim, GPUgeneric() const DataT Parameters[], + DataT u1, DataT u2, GPUgeneric() DataT P[/* 4*inpYdim */]) const + { + + const auto nYdimTmp = SplineUtil::getNdim(inpYdim); + const int32_t nYdim = nYdimTmp.get(); + + // const auto maxYdim = SplineUtil::getMaxNdim(inpYdim); + // const int32_t maxYdim4 = 4 * maxYdim.get(); + + // const auto nYdim2 = nYdim * 2; + const auto nYdim4 = nYdim * 4; + + DataT *S = P, + *R = P + nYdim, + *Q = P + nYdim * 2, + *W = P + nYdim * 3; + + const DataT& u = u1; + const DataT& v = u2; + int32_t nu = mGridX1.getNumberOfKnots(); + int32_t iu = mGridX1.template getLeftKnotIndexForU(u); + int32_t iv = mGridX2.template getLeftKnotIndexForU(v); + + const typename TBase::Knot& knotU = mGridX1.template getKnot(iu); + const typename TBase::Knot& knotV = mGridX2.template getKnot(iv); + + const DataT* A = Parameters + (nu * iv + iu) * nYdim4; // values { {Y1,Y2,Y3}, {Y1,Y2,Y3}'v, {Y1,Y2,Y3}'u, {Y1,Y2,Y3}''vu } at {u0, v0} + const DataT* B = A + nYdim4 * nu; // values { ... } at {u0, v1} + + auto [dSdSl, dSdDl, dSdSr, dSdDr, dRdSl, dRdDl, dRdSr, dRdDr] = mGridX1.template getSDderivativesOverParsAtU(knotU, u); + auto [dSdSd, dSdDd, dSdSu, dSdDu, dQdSd, dQdDd, dQdSu, dQdDu] = mGridX2.template getSDderivativesOverParsAtU(knotV, v); + + // when nYdim == 1: + + // Function value S + // S = dSdSl * (dSdSd * A[0] + dSdDd * A[1]) + dSdDl * (dSdSd * A[2] + dSdDd * A[3]) + + // dSdSr * (dSdSd * A[4] + dSdDd * A[5]) + dSdDr * (dSdSd * A[6] + dSdDd * A[7]) + + // dSdSl * (dSdSu * B[0] + dSdDu * B[1]) + dSdDl * (dSdSu * B[2] + dSdDu * B[3]) + + // dSdSr * (dSdSu * B[4] + dSdDu * B[5]) + dSdDr * (dSdSu * B[6] + dSdDu * B[7]); + + { + DataT a[8] = {dSdSl * dSdSd, dSdSl * dSdDd, dSdDl * dSdSd, dSdDl * dSdDd, + dSdSr * dSdSd, dSdSr * dSdDd, dSdDr * dSdSd, dSdDr * dSdDd}; + DataT b[8] = {dSdSl * dSdSu, dSdSl * dSdDu, dSdDl * dSdSu, dSdDl * dSdDu, + dSdSr * dSdSu, dSdSr * dSdDu, dSdDr * dSdSu, dSdDr * dSdDu}; + + // S = sum a[i]*A[i] + b[i]*B[i] + + for (int32_t dim = 0; dim < nYdim; dim++) { + S[dim] = 0; + for (int32_t i = 0; i < 8; i++) { + S[dim] += a[i] * A[nYdim * i + dim] + b[i] * B[nYdim * i + dim]; + } + } + } + + // Derivative R = dS / du + // R = dRdSl * (dSdSd * A[0] + dSdDd * A[1]) + dRdDl * (dSdSd * A[2] + dSdDd * A[3]) + + // dRdSr * (dSdSd * A[4] + dSdDd * A[5]) + dRdDr * (dSdSd * A[6] + dSdDd * A[7]) + + // dRdSl * (dSdSu * B[0] + dSdDu * B[1]) + dRdDl * (dSdSu * B[2] + dSdDu * B[3]) + + // dRdSr * (dSdSu * B[4] + dSdDu * B[5]) + dRdDr * (dSdSu * B[6] + dSdDu * B[7]); + + { + DataT a[8] = {dRdSl * dSdSd, dRdSl * dSdDd, dRdDl * dSdSd, dRdDl * dSdDd, + dRdSr * dSdSd, dRdSr * dSdDd, dRdDr * dSdSd, dRdDr * dSdDd}; + DataT b[8] = {dRdSl * dSdSu, dRdSl * dSdDu, dRdDl * dSdSu, dRdDl * dSdDu, + dRdSr * dSdSu, dRdSr * dSdDu, dRdDr * dSdSu, dRdDr * dSdDu}; + + // R = sum a[i]*A[i] + b[i]*B[i] + + for (int32_t dim = 0; dim < nYdim; dim++) { + R[dim] = 0; + for (int32_t i = 0; i < 8; i++) { + R[dim] += a[i] * A[nYdim * i + dim] + b[i] * B[nYdim * i + dim]; + } + } + } + + // Derivative Q = dS / dv + // Q = dSdSl * (dQdSd * A[0] + dQdDd * A[1]) + dSdDl * (dQdSd * A[2] + dQdDd * A[3]) + + // dSdSr * (dQdSd * A[4] + dQdDd * A[5]) + dSdDr * (dQdSd * A[6] + dQdDd * A[7]) + + // dSdSl * (dQdSu * B[0] + dQdDu * B[1]) + dSdDl * (dQdSu * B[2] + dQdDu * B[3]) + + // dSdSr * (dQdSu * B[4] + dQdDu * B[5]) + dSdDr * (dQdSu * B[6] + dQdDu * B[7]); + + { + DataT a[8] = {dSdSl * dQdSd, dSdSl * dQdDd, dSdDl * dQdSd, dSdDl * dQdDd, + dSdSr * dQdSd, dSdSr * dQdDd, dSdDr * dQdSd, dSdDr * dQdDd}; + DataT b[8] = {dSdSl * dQdSu, dSdSl * dQdDu, dSdDl * dQdSu, dSdDl * dQdDu, + dSdSr * dQdSu, dSdSr * dQdDu, dSdDr * dQdSu, dSdDr * dQdDu}; + + // Q = sum a[i]*A[i] + b[i]*B[i] + + for (int32_t dim = 0; dim < nYdim; dim++) { + Q[dim] = 0; + for (int32_t i = 0; i < 8; i++) { + Q[dim] += a[i] * A[nYdim * i + dim] + b[i] * B[nYdim * i + dim]; + } + } + } + + // cross-derivative W = (dS)^2 / du / dv + // W = dRdSl * (dQdSd * A[0] + dQdDd * A[1]) + dRdDl * (dQdSd * A[2] + dQdDd * A[3]) + + // dRdSr * (dQdSd * A[4] + dQdDd * A[5]) + dRdDr * (dQdSd * A[6] + dQdDd * A[7]) + + // dRdSl * (dQdSu * B[0] + dQdDu * B[1]) + dRdDl * (dQdSu * B[2] + dQdDu * B[3]) + + // dRdSr * (dQdSu * B[4] + dQdDu * B[5]) + dRdDr * (dQdSu * B[6] + dQdDu * B[7]); + + { + DataT a[8] = {dRdSl * dQdSd, dRdSl * dQdDd, dRdDl * dQdSd, dRdDl * dQdDd, + dRdSr * dQdSd, dRdSr * dQdDd, dRdDr * dQdSd, dRdDr * dQdDd}; + DataT b[8] = {dRdSl * dQdSu, dRdSl * dQdDu, dRdDl * dQdSu, dRdDl * dQdDu, + dRdSr * dQdSu, dRdSr * dQdDu, dRdDr * dQdSu, dRdDr * dQdDu}; + + // W = sum a[i]*A[i] + b[i]*B[i] + + for (int32_t dim = 0; dim < nYdim; dim++) { + W[dim] = 0; + for (int32_t i = 0; i < 8; i++) { + W[dim] += a[i] * A[nYdim * i + dim] + b[i] * B[nYdim * i + dim]; + } + } + } + } + protected: using TBase::mGridX1; using TBase::mGridX2; @@ -429,11 +555,18 @@ class Spline2DSpec /// Get interpolated value for an YdimT-dimensional S(u1,u2) using spline parameters Parameters. template GPUd() void interpolateAtU(GPUgeneric() const DataT Parameters[], - DataT u1, DataT u2, GPUgeneric() DataT S[/*nYdim*/]) const + DataT u1, DataT u2, GPUgeneric() DataT S[/*YdimT*/]) const { TBase::template interpolateAtU(YdimT, Parameters, u1, u2, S); } + template + GPUd() void interpolateParametersAtU(GPUgeneric() const DataT Parameters[], + DataT u1, DataT u2, GPUgeneric() DataT P[/* 4*YdimT */]) const + { + TBase::template interpolateParametersAtU(YdimT, Parameters, u1, u2, P); + } + /// Get interpolated value for an YdimT-dimensional S(u1,u2) using spline parameters Parameters. template GPUd() void interpolateAtUold(GPUgeneric() const DataT Parameters[], diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index 76368efcd8a4f..da37409221d6c 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -82,6 +82,24 @@ class TPCFastSpaceChargeCorrection : public FlatObject maxCorr[2] = GPUCommonMath::Max(maxCorr[2], dv); } + void updateMaxValues(std::tuple dxdudv, float scale) + { + float dx = std::get<0>(dxdudv) * scale; + float du = std::get<1>(dxdudv) * scale; + float dv = std::get<2>(dxdudv) * scale; + updateMaxValues(dx, du, dv); + } + + std::tuple getMaxValues() const + { + return std::make_tuple(maxCorr[0], maxCorr[1], maxCorr[2]); + } + + std::tuple getMinValues() const + { + return std::make_tuple(minCorr[0], minCorr[1], minCorr[2]); + } + ClassDefNV(SectorRowInfo, 2); }; @@ -90,7 +108,11 @@ class TPCFastSpaceChargeCorrection : public FlatObject ClassDefNV(SectorInfo, 1); }; - typedef Spline2D SplineType; + typedef Spline2D SplineTypeXYZ; + typedef Spline2D SplineTypeInvX; + typedef Spline2D SplineTypeInvYZ; + + typedef SplineTypeXYZ SplineType; /// _____________ Constructors / destructors __________________________ @@ -168,6 +190,30 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// Gives pointer to spline data GPUd() const float* getSplineData(int32_t sector, int32_t row, int32_t iSpline = 0) const; + /// Gives const pointer to a spline for the inverse X correction + GPUd() const SplineTypeInvX& getSplineInvX(int32_t sector, int32_t row) const; + + /// Gives pointer to a spline for the inverse X correction + GPUd() SplineTypeInvX& getSplineInvX(int32_t sector, int32_t row); + + /// Gives pointer to spline data for the inverse X correction + GPUd() float* getSplineDataInvX(int32_t sector, int32_t row); + + /// Gives pointer to spline data for the inverse X correction + GPUd() const float* getSplineDataInvX(int32_t sector, int32_t row) const; + + /// Gives const pointer to a spline for the inverse YZ correction + GPUd() const SplineTypeInvYZ& getSplineInvYZ(int32_t sector, int32_t row) const; + + /// Gives pointer to a spline for the inverse YZ correction + GPUd() SplineTypeInvYZ& getSplineInvYZ(int32_t sector, int32_t row); + + /// Gives pointer to spline data for the inverse YZ correction + GPUd() float* getSplineDataInvYZ(int32_t sector, int32_t row); + + /// Gives pointer to spline data for the inverse YZ correction + GPUd() const float* getSplineDataInvYZ(int32_t sector, int32_t row) const; + /// _______________ The main method: cluster correction _______________________ /// // GPUd() int32_t getCorrectionInternal(int32_t sector, int32_t row, float u, float v, float& dx, float& du, float& dv) const; @@ -193,6 +239,10 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// convert corrected u,v to internal grid coordinates GPUd() std::tuple convCorrectedLocalToGrid(int32_t sector, int32_t row, float y, float z) const; + /// convert internal grid coordinates to corrected u,v + /// return values: u, v, scaling factor + GPUd() std::tuple convGridToCorrectedLocal(int32_t sector, int32_t row, float u, float v) const; + GPUd() bool isLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const; /// TPC geometry information @@ -316,6 +366,54 @@ GPUdi() const float* TPCFastSpaceChargeCorrection::getSplineData(int32_t sector, return reinterpret_cast(mSplineData[iSpline] + mSectorDataSizeBytes[iSpline] * sector + rowInfo.dataOffsetBytes[iSpline]); } +GPUdi() TPCFastSpaceChargeCorrection::SplineTypeInvX& TPCFastSpaceChargeCorrection::getSplineInvX(int32_t sector, int32_t row) +{ + /// Gives pointer to spline for the inverse X correction + return reinterpret_cast(getSpline(sector, row)); +} + +GPUdi() const TPCFastSpaceChargeCorrection::SplineTypeInvX& TPCFastSpaceChargeCorrection::getSplineInvX(int32_t sector, int32_t row) const +{ + /// Gives const pointer to spline for the inverse X correction + return reinterpret_cast(getSpline(sector, row)); +} + +GPUdi() float* TPCFastSpaceChargeCorrection::getSplineDataInvX(int32_t sector, int32_t row) +{ + /// Gives pointer to spline data for the inverse X correction + return getSplineData(sector, row, 1); +} + +GPUdi() const float* TPCFastSpaceChargeCorrection::getSplineDataInvX(int32_t sector, int32_t row) const +{ + /// Gives pointer to spline data for the inverse X correction + return getSplineData(sector, row, 1); +} + +GPUdi() TPCFastSpaceChargeCorrection::SplineTypeInvYZ& TPCFastSpaceChargeCorrection::getSplineInvYZ(int32_t sector, int32_t row) +{ + /// Gives pointer to spline for the inverse YZ correction + return reinterpret_cast(getSpline(sector, row)); +} + +GPUdi() const TPCFastSpaceChargeCorrection::SplineTypeInvYZ& TPCFastSpaceChargeCorrection::getSplineInvYZ(int32_t sector, int32_t row) const +{ + /// Gives const pointer to spline for the inverse YZ correction + return reinterpret_cast(getSpline(sector, row)); +} + +GPUdi() float* TPCFastSpaceChargeCorrection::getSplineDataInvYZ(int32_t sector, int32_t row) +{ + /// Gives pointer to spline data for the inverse YZ correction + return getSplineData(sector, row, 2); +} + +GPUdi() const float* TPCFastSpaceChargeCorrection::getSplineDataInvYZ(int32_t sector, int32_t row) const +{ + /// Gives pointer to spline data for the inverse YZ correction + return getSplineData(sector, row, 2); +} + GPUdi() std::tuple TPCFastSpaceChargeCorrection::convLocalToGrid(int32_t sector, int32_t row, float y, float z) const { /// convert local y, z to internal grid coordinates u,v @@ -401,6 +499,17 @@ GPUdi() std::tuple TPCFastSpaceChargeCorrection::convCorrec return {gridU, gridV, scale}; } +GPUdi() std::tuple TPCFastSpaceChargeCorrection::convGridToCorrectedLocal(int32_t sector, int32_t row, float gridU, float gridV) const +{ + /// convert internal grid coordinates u,v to corrected y, z + const SectorRowInfo& info = getSectorRowInfo(sector, row); + float u = info.gridCorrU0 + gridU / info.scaleCorrUtoGrid; + float v = info.gridCorrV0 + gridV / info.scaleCorrVtoGrid; + float y, z; + mGeo.convUVtoLocal1(sector, u, v, y, z); + return {y, z}; +} + GPUdi() std::tuple TPCFastSpaceChargeCorrection::getCorrectionLocal(int32_t sector, int32_t row, float y, float z) const { const auto& info = getSectorRowInfo(sector, row); @@ -421,33 +530,21 @@ GPUdi() std::tuple TPCFastSpaceChargeCorrection::getCorrect GPUdi() float TPCFastSpaceChargeCorrection::getCorrectionXatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const { const auto& info = getSectorRowInfo(sector, row); - const Spline2D& spline = reinterpret_cast&>(getSpline(sector, row)); - const float* splineData = getSplineData(sector, row, 1); - auto [gridU, gridV, scale] = convCorrectedLocalToGrid(sector, row, realY, realZ); - float dx = 0; - spline.interpolateAtU(splineData, gridU, gridV, &dx); - + getSplineInvX(sector, row).interpolateAtU(getSplineDataInvX(sector, row), gridU, gridV, &dx); dx = scale * GPUCommonMath::Clamp(dx, info.minCorr[0], info.maxCorr[0]); return dx; } GPUdi() std::tuple TPCFastSpaceChargeCorrection::getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const { - auto [gridU, gridV, scale] = convCorrectedLocalToGrid(sector, row, realY, realZ); - const auto& info = getSectorRowInfo(sector, row); - const Spline2D& spline = reinterpret_cast&>(getSpline(sector, row)); - const float* splineData = getSplineData(sector, row, 2); - float dyz[2]; - spline.interpolateAtU(splineData, gridU, gridV, dyz); - + getSplineInvYZ(sector, row).interpolateAtU(getSplineDataInvYZ(sector, row), gridU, gridV, dyz); dyz[0] = scale * GPUCommonMath::Clamp(dyz[0], info.minCorr[1], info.maxCorr[1]); dyz[1] = scale * GPUCommonMath::Clamp(dyz[1], info.minCorr[2], info.maxCorr[2]); - return {dyz[0], dyz[1]}; } From 98e7ac28070839a7985ee342af2d39824729a627 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Sat, 12 Apr 2025 16:10:13 +0000 Subject: [PATCH 510/701] TPC Splines: get rid of internal UV coordinates --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 59 ++++----- .../src/TPCFastTransformHelperO2.cxx | 7 +- .../TPCFastSpaceChargeCorrection.cxx | 100 ++------------ .../TPCFastSpaceChargeCorrection.h | 125 +++++++++--------- GPU/TPCFastTransformation/TPCFastTransform.h | 4 +- .../TPCFastTransformGeo.cxx | 2 +- .../TPCFastTransformGeo.h | 73 ++-------- .../macro/TPCFastTransformInit.C | 7 +- 8 files changed, 124 insertions(+), 253 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index 6ba3d6e12dd9e..021074c49f21a 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -354,15 +354,14 @@ void TPCFastSpaceChargeCorrectionHelper::testGeometry(const TPCFastTransformGeo& for (int pad = 0; pad < nPads; pad++) { const GlobalPadNumber p = mapper.globalPadNumber(PadPos(row, pad)); const PadCentre& c = mapper.padCentre(p); - double u = geo.convPadToU(row, pad); - + auto [y, z] = geo.convPadDriftLengthToLocal(0, row, pad, 0.); const double dx = x - c.X(); - const double dy = u - (-c.Y()); // diferent sign convention for Y coordinate in the map + const double dy = y - (-c.Y()); // diferent sign convention for Y coordinate in the map if (fabs(dx) >= 1.e-6 || fabs(dy) >= 1.e-5) { LOG(warning) << "wrong calculated pad position:" << " row " << row << " pad " << pad << " x calc " << x << " x in map " << c.X() << " dx " << (x - c.X()) - << " y calc " << u << " y in map " << -c.Y() << " dy " << dy << std::endl; + << " y calc " << y << " y in map " << -c.Y() << " dy " << dy << std::endl; } if (fabs(maxDx) < fabs(dx)) { maxDx = dx; @@ -499,18 +498,14 @@ std::unique_ptr TPCFastSpaceChargeCorrect double yMax = rowInfo.x * trackResiduals.getY2X(iRow, trackResiduals.getNY2XBins() - 1); double zMin = rowInfo.x * trackResiduals.getZ2X(0); double zMax = rowInfo.x * trackResiduals.getZ2X(trackResiduals.getNZ2XBins() - 1); - double uMin = yMin; - double uMax = yMax; - double vMin = geo.getTPCzLength() - zMax; - double vMax = geo.getTPCzLength() - zMin; - info.gridU0 = uMin; - info.scaleUtoGrid = spline.getGridX1().getUmax() / (uMax - uMin); - info.gridV0 = vMin; - info.scaleVtoGrid = spline.getGridX2().getUmax() / (vMax - vMin); - info.gridCorrU0 = info.gridU0; - info.gridCorrV0 = info.gridV0; - info.scaleCorrUtoGrid = info.scaleUtoGrid; - info.scaleCorrVtoGrid = info.scaleVtoGrid; + double lMin = geo.getTPCzLength() - zMax; + double lMax = geo.getTPCzLength() - zMin; + info.gridMeasured.y0 = yMin; + info.gridMeasured.yScale = spline.getGridX1().getUmax() / (yMax - yMin); + info.gridMeasured.l0 = lMin; + info.gridMeasured.lScale = spline.getGridX2().getUmax() / (lMax - lMin); + + info.gridReal = info.gridMeasured; // std::cout << " iSector " << iSector << " iRow " << iRow << " uMin: " << uMin << " uMax: " << uMax << " vMin: " << vMin << " vMax: " << vMax //<< " grid scale u "<< info.scaleUtoGrid << " grid scale v "<< info.scaleVtoGrid<< std::endl; @@ -593,8 +588,8 @@ std::unique_ptr TPCFastSpaceChargeCorrect auto myThread = [&](int iThread, int nTreads) { struct Voxel { - float mY, mZ; // not-distorted local coordinates - float mDy, mDz; // bin size + float mY, mZ; // non-distorted local coordinates + float mDy, mDz; // voxel size int mSmoothingStep{100}; // is the voxel data original or smoothed at this step }; @@ -905,11 +900,10 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector= 1.e-6 || fabs(dy) >= 1.e-5) { LOG(warning) << "wrong calculated pad position:" << " row " << row << " pad " << pad << " x calc " << x << " x in map " << c.X() << " dx " << (x - c.X()) - << " y calc " << u << " y in map " << -c.Y() << " dy " << dy << std::endl; + << " y calc " << y << " y in map " << -c.Y() << " dy " << dy << std::endl; } if (fabs(maxDx) < fabs(dx)) { maxDx = dx; diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx index 2921a74b025ce..1e6d84b7f8dd9 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx @@ -141,48 +141,18 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer { /// Sets the actual location of the external flat buffer after it has been moved (e.g. to another maschine) - struct RowInfoVersion3 { - int32_t splineScenarioID{0}; ///< scenario index (which of Spline2D splines to use) - size_t dataOffsetBytes[3]{0}; ///< offset for the spline data withing a TPC sector - }; - - struct RowActiveAreaVersion3 { - float maxDriftLengthCheb[5]{0.f}; - float vMax{0.f}; - float cuMin{0.f}; - float cuMax{0.f}; - float cvMax{0.f}; - }; - - struct SectorRowInfoVersion3 { - float gridV0{0.f}; ///< V coordinate of the V-grid start - float gridCorrU0{0.f}; ///< U coordinate of the U-grid start for corrected U - float gridCorrV0{0.f}; ///< V coordinate of the V-grid start for corrected V - float scaleCorrUtoGrid{0.f}; ///< scale corrected U to U-grid coordinate - float scaleCorrVtoGrid{0.f}; ///< scale corrected V to V-grid coordinate - RowActiveAreaVersion3 activeArea; - }; - - FlatObject::setActualBufferAddress(actualFlatBufferPtr); - - size_t rowsOffset = 0; - size_t rowsSize = 0; - if (mClassVersion == 3) { - rowsSize = sizeof(RowInfoVersion3) * mGeo.getNumberOfRows(); + if (mClassVersion != 4) { + LOG(error) << "TPCFastSpaceChargeCorrection::setActualBufferAddress() called with class version " << mClassVersion << ". This is not supported."; + return; } - size_t sectorRowsOffset = rowsOffset + rowsSize; - size_t sectorRowsSize = 0; - if (mClassVersion == 3) { // copy old-format sectorrow data from the buffer to the arrays - sectorRowsSize = sizeof(SectorRowInfoVersion3) * mGeo.getNumberOfRows() * mGeo.getNumberOfSectors(); - } + FlatObject::setActualBufferAddress(actualFlatBufferPtr); - size_t scOffset = alignSize(sectorRowsOffset + sectorRowsSize, SplineType::getClassAlignmentBytes()); size_t scSize = sizeof(SplineType) * mNumberOfScenarios; - mScenarioPtr = reinterpret_cast(mFlatBufferPtr + scOffset); + mScenarioPtr = reinterpret_cast(mFlatBufferPtr); - size_t scBufferOffset = alignSize(scOffset + scSize, SplineType::getBufferAlignmentBytes()); + size_t scBufferOffset = alignSize(scSize, SplineType::getBufferAlignmentBytes()); size_t scBufferSize = 0; for (int32_t i = 0; i < mNumberOfScenarios; i++) { @@ -196,45 +166,6 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer mSplineData[is] = reinterpret_cast(mFlatBufferPtr + sectorDataOffset); bufferSize = sectorDataOffset + mSectorDataSizeBytes[is] * mGeo.getNumberOfSectors(); } - - if (mClassVersion == 3) { // copy old-format sectorrow data from the buffer to the arrays - - auto* rowInfosOld = reinterpret_cast(mFlatBufferPtr + rowsOffset); - for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { - RowInfoVersion3& infoOld = rowInfosOld[i]; - RowInfo& info = mRowInfos[i]; - info.splineScenarioID = infoOld.splineScenarioID; - for (int32_t is = 0; is < 3; is++) { - info.dataOffsetBytes[is] = infoOld.dataOffsetBytes[is]; - } - } - - for (int32_t is = 0; is < mNumberOfScenarios; is++) { - auto& spline = mScenarioPtr[is]; - spline.setXrange(0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax()); - } - - auto* sectorRowInfosOld = reinterpret_cast(mFlatBufferPtr + sectorRowsOffset); - - for (int32_t sector = 0; sector < mGeo.getNumberOfSectors(); sector++) { - for (int32_t row = 0; row < mGeo.getNumberOfRows(); row++) { - SectorRowInfoVersion3& infoOld = sectorRowInfosOld[mGeo.getNumberOfRows() * sector + row]; - SectorRowInfo& info = getSectorRowInfo(sector, row); - const auto& spline = getSpline(sector, row); - info.gridU0 = mGeo.getRowInfo(row).u0; - info.scaleUtoGrid = spline.getGridX1().getUmax() / mGeo.getRowInfo(row).getUwidth(); - - info.gridV0 = infoOld.gridV0; - info.scaleVtoGrid = spline.getGridX2().getUmax() / (mGeo.getTPCzLength() + 3. - info.gridV0); - - info.gridCorrU0 = infoOld.gridCorrU0; - info.scaleCorrUtoGrid = infoOld.scaleCorrUtoGrid; - - info.gridCorrV0 = infoOld.gridCorrV0; - info.scaleCorrVtoGrid = infoOld.scaleCorrVtoGrid; - } - } - } } void TPCFastSpaceChargeCorrection::setFutureBufferAddress(char* futureFlatBufferPtr) @@ -454,17 +385,12 @@ GPUd() void TPCFastSpaceChargeCorrection::setNoCorrection() SectorRowInfo& info = getSectorRowInfo(sector, row); - info.gridU0 = mGeo.getRowInfo(row).u0; - info.scaleUtoGrid = spline.getGridX1().getUmax() / mGeo.getRowInfo(row).getUwidth(); - - info.gridV0 = 0.f; - info.scaleVtoGrid = spline.getGridX2().getUmax() / vLength; - - info.gridCorrU0 = info.gridU0; - info.gridCorrV0 = info.gridV0; - info.scaleCorrUtoGrid = info.scaleUtoGrid; - info.scaleCorrVtoGrid = info.scaleVtoGrid; + info.gridMeasured.y0 = mGeo.getRowInfo(row).getYmin(); + info.gridMeasured.yScale = spline.getGridX1().getUmax() / mGeo.getRowInfo(row).getYwidth(); + info.gridMeasured.l0 = 0.f; + info.gridMeasured.lScale = spline.getGridX2().getUmax() / vLength; + info.gridReal = info.gridMeasured; } // row } // sector } @@ -539,9 +465,9 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) // grid borders if (sector < mGeo.getNumberOfSectorsA()) { - z1 = vLength - getSectorRowInfo(sector, row).gridV0; + z1 = vLength - getSectorRowInfo(sector, row).gridMeasured.l0; } else { - z0 = getSectorRowInfo(sector, row).gridV0 - vLength; + z0 = getSectorRowInfo(sector, row).gridMeasured.l0 - vLength; } double stepY = (y1 - y0) / 100.; diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index da37409221d6c..4564d584c8dce 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -48,17 +48,31 @@ class TPCFastSpaceChargeCorrection : public FlatObject ClassDefNV(RowInfo, 1); }; + struct GridInfo { + float y0{0.f}; ///< Y coordinate of the U-grid start + float yScale{0.f}; //< scale Y to U-grid coordinate + float l0{0.f}; ///< Drift Length coordinate of the V-grid start + float lScale{0.f}; //< scale Drift Length to V-grid coordinate + + float getScale(float l) const + { + if (l < 0.f) { // outside of the TPC + return 0.f; + } + if (l < l0) { // between the grid and the readout + return l / l0; + } + return 1.f; // inside the grid + } + ClassDefNV(GridInfo, 1); + }; + struct SectorRowInfo { - float gridU0{0.f}; //< U coordinate of the U-grid start - float scaleUtoGrid{0.f}; //< scale U to U-grid coordinate - float gridV0{0.f}; ///< V coordinate of the V-grid start - float scaleVtoGrid{0.f}; //< scale V to V-grid coordinate - float gridCorrU0{0.f}; ///< U coordinate of the U-grid start for corrected U - float scaleCorrUtoGrid{0.f}; ///< scale corrected U to U-grid coordinate - float gridCorrV0{0.f}; ///< V coordinate of the V-grid start for corrected V - float scaleCorrVtoGrid{0.f}; ///< scale corrected V to V-grid coordinate - float minCorr[3]{-10.f, -10.f, -10.f}; ///< min correction for dX, dU, dV - float maxCorr[3]{10.f, 10.f, 10.f}; ///< max correction for dX, dU, dV + GridInfo gridMeasured; ///< grid info for measured coordinates + GridInfo gridReal; ///< grid info for real coordinates + + float minCorr[3]{-10.f, -10.f, -10.f}; ///< min correction for dX, dY, dZ + float maxCorr[3]{10.f, 10.f, 10.f}; ///< max correction for dX, dY, dZ void resetMaxValues() { @@ -220,10 +234,10 @@ class TPCFastSpaceChargeCorrection : public FlatObject GPUdi() std::tuple getCorrectionLocal(int32_t sector, int32_t row, float y, float z) const; - /// inverse correction: Corrected U and V -> coorrected X + /// inverse correction: Real Y and Z -> Real X GPUd() float getCorrectionXatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const; - /// inverse correction: Corrected U and V -> uncorrected U and V + /// inverse correction: Real Y and Z -> measred Y and Z GPUd() std::tuple getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const; /// _______________ Utilities _______________________________________________ @@ -236,12 +250,13 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// return values: y, z, scaling factor GPUd() std::tuple convGridToLocal(int32_t sector, int32_t row, float u, float v) const; - /// convert corrected u,v to internal grid coordinates - GPUd() std::tuple convCorrectedLocalToGrid(int32_t sector, int32_t row, float y, float z) const; - - /// convert internal grid coordinates to corrected u,v + /// convert real Y, Z to the internal grid coordinates /// return values: u, v, scaling factor - GPUd() std::tuple convGridToCorrectedLocal(int32_t sector, int32_t row, float u, float v) const; + GPUd() std::tuple convRealLocalToGrid(int32_t sector, int32_t row, float y, float z) const; + + /// convert internal grid coordinates to the real Y, Z + /// return values: y, z + GPUd() std::tuple convGridToRealLocal(int32_t sector, int32_t row, float u, float v) const; GPUd() bool isLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const; @@ -421,18 +436,10 @@ GPUdi() std::tuple TPCFastSpaceChargeCorrection::convLocalT const auto& info = getSectorRowInfo(sector, row); const SplineType& spline = getSpline(sector, row); - float u, v; - mGeo.convLocalToUV1(sector, y, z, u, v); - - float scale = 1.f; - if (v < 0.f) { - scale = 0.f; - } else if (v < info.gridV0) { - scale = v / info.gridV0; - } - - float gridU = (u - info.gridU0) * info.scaleUtoGrid; - float gridV = (v - info.gridV0) * info.scaleVtoGrid; + float l = mGeo.convZtoDriftLength(sector, z); + float scale = info.gridMeasured.getScale(l); + float gridU = (y - info.gridMeasured.y0) * info.gridMeasured.yScale; + float gridV = (l - info.gridMeasured.l0) * info.gridMeasured.lScale; // shrink to the grid area gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); @@ -446,19 +453,19 @@ GPUdi() bool TPCFastSpaceChargeCorrection::isLocalInsideGrid(int32_t sector, int /// ccheck if local y, z are inside the grid const auto& info = getSectorRowInfo(sector, row); - const SplineType& spline = getSpline(sector, row); + const auto& spline = getSpline(sector, row); + float l = mGeo.convZtoDriftLength(sector, z); - float u, v; - mGeo.convLocalToUV1(sector, y, z, u, v); - - float gridU = (u - info.gridU0) * info.scaleUtoGrid; - float gridV = (v - info.gridV0) * info.scaleVtoGrid; + float gridU = (y - info.gridMeasured.y0) * info.gridMeasured.yScale; + float gridV = (l - info.gridMeasured.l0) * info.gridMeasured.lScale; // shrink to the grid area - if (gridU < 0.f || gridU > (float)spline.getGridX1().getUmax()) + if (gridU < 0.f || gridU > (float)spline.getGridX1().getUmax()) { return false; - if (gridV < 0.f || gridV > (float)spline.getGridX2().getUmax()) + } + if (gridV < 0.f || gridV > (float)spline.getGridX2().getUmax()) { return false; + } return true; } @@ -466,31 +473,22 @@ GPUdi() std::tuple TPCFastSpaceChargeCorrection::convGridToLocal(i { /// convert internal grid coordinates u,v to local y, z const SectorRowInfo& info = getSectorRowInfo(sector, row); - float u = info.gridU0 + gridU / info.scaleUtoGrid; - float v = info.gridV0 + gridV / info.scaleVtoGrid; - float y, z; - mGeo.convUVtoLocal1(sector, u, v, y, z); + float y = info.gridMeasured.y0 + gridU / info.gridMeasured.yScale; + float l = info.gridMeasured.l0 + gridV / info.gridMeasured.lScale; + float z = mGeo.convDriftLengthToZ(sector, l); return {y, z}; } -GPUdi() std::tuple TPCFastSpaceChargeCorrection::convCorrectedLocalToGrid(int32_t sector, int32_t row, float y, float z) const +GPUdi() std::tuple TPCFastSpaceChargeCorrection::convRealLocalToGrid(int32_t sector, int32_t row, float y, float z) const { - /// convert corrected y, z to the internal grid coordinates + /// convert real y, z to the internal grid coordinates + scale const auto& info = getSectorRowInfo(sector, row); - const Spline2D& spline = reinterpret_cast&>(getSpline(sector, row)); - - float u, v; - mGeo.convLocalToUV1(sector, y, z, u, v); - - float scale = 1.f; - if (v < 0.f) { - scale = 0.f; - } else if (v < info.gridCorrV0) { - scale = v / info.gridCorrV0; - } + const auto& spline = getSpline(sector, row); - float gridU = (u - info.gridCorrU0) * info.scaleCorrUtoGrid; - float gridV = (v - info.gridCorrV0) * info.scaleCorrVtoGrid; + float l = mGeo.convZtoDriftLength(sector, z); + float scale = info.gridReal.getScale(l); + float gridU = (y - info.gridReal.y0) * info.gridReal.yScale; + float gridV = (l - info.gridReal.l0) * info.gridReal.lScale; // shrink to the grid area gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); @@ -499,14 +497,13 @@ GPUdi() std::tuple TPCFastSpaceChargeCorrection::convCorrec return {gridU, gridV, scale}; } -GPUdi() std::tuple TPCFastSpaceChargeCorrection::convGridToCorrectedLocal(int32_t sector, int32_t row, float gridU, float gridV) const +GPUdi() std::tuple TPCFastSpaceChargeCorrection::convGridToRealLocal(int32_t sector, int32_t row, float gridU, float gridV) const { - /// convert internal grid coordinates u,v to corrected y, z + /// convert internal grid coordinates u,v to the real y, z const SectorRowInfo& info = getSectorRowInfo(sector, row); - float u = info.gridCorrU0 + gridU / info.scaleCorrUtoGrid; - float v = info.gridCorrV0 + gridV / info.scaleCorrVtoGrid; - float y, z; - mGeo.convUVtoLocal1(sector, u, v, y, z); + float y = info.gridReal.y0 + gridU / info.gridReal.yScale; + float l = info.gridReal.l0 + gridV / info.gridReal.lScale; + float z = mGeo.convDriftLengthToZ(sector, l); return {y, z}; } @@ -530,7 +527,7 @@ GPUdi() std::tuple TPCFastSpaceChargeCorrection::getCorrect GPUdi() float TPCFastSpaceChargeCorrection::getCorrectionXatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const { const auto& info = getSectorRowInfo(sector, row); - auto [gridU, gridV, scale] = convCorrectedLocalToGrid(sector, row, realY, realZ); + auto [gridU, gridV, scale] = convRealLocalToGrid(sector, row, realY, realZ); float dx = 0; getSplineInvX(sector, row).interpolateAtU(getSplineDataInvX(sector, row), gridU, gridV, &dx); dx = scale * GPUCommonMath::Clamp(dx, info.minCorr[0], info.maxCorr[0]); @@ -539,7 +536,7 @@ GPUdi() float TPCFastSpaceChargeCorrection::getCorrectionXatRealYZ(int32_t secto GPUdi() std::tuple TPCFastSpaceChargeCorrection::getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const { - auto [gridU, gridV, scale] = convCorrectedLocalToGrid(sector, row, realY, realZ); + auto [gridU, gridV, scale] = convRealLocalToGrid(sector, row, realY, realZ); const auto& info = getSectorRowInfo(sector, row); float dyz[2]; getSplineInvYZ(sector, row).interpolateAtU(getSplineDataInvYZ(sector, row), gridU, gridV, dyz); diff --git a/GPU/TPCFastTransformation/TPCFastTransform.h b/GPU/TPCFastTransformation/TPCFastTransform.h index 03d9eaf43ce9b..8807c0e3206f4 100644 --- a/GPU/TPCFastTransformation/TPCFastTransform.h +++ b/GPU/TPCFastTransformation/TPCFastTransform.h @@ -551,7 +551,7 @@ GPUdi() void TPCFastTransform::Transform(int32_t sector, int32_t row, float pad, GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t sector, float time, float& z, float maxTimeBin) const { float l = (time - mT0 - maxTimeBin) * mVdrift; // drift length cm - z = getGeometry().convDriftLengthToLocal(sector, l); + z = getGeometry().convDriftLengthToZ(sector, l); } GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float maxTimeBin) const @@ -590,7 +590,7 @@ GPUdi() void TPCFastTransform::TransformIdealZ(int32_t sector, float time, float /// float l = (time - mT0 - vertexTime) * mVdrift; // drift length cm - z = getGeometry().convDriftLengthToLocal(sector, l); + z = getGeometry().convDriftLengthToZ(sector, l); } GPUdi() void TPCFastTransform::TransformIdeal(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime) const diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx b/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx index e7e026f464818..5b2dcc8da82d5 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx @@ -92,7 +92,7 @@ void TPCFastTransformGeo::setTPCrow(int32_t iRow, float x, int32_t nPads, float row.x = x; row.maxPad = nPads - 1; row.padWidth = padWidth; - row.u0 = -uWidth / 2.; + row.yMin = -uWidth / 2.; } void TPCFastTransformGeo::finishConstruction() diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.h b/GPU/TPCFastTransformation/TPCFastTransformGeo.h index 4072435e948a5..bbb94dcb8bedd 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.h +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.h @@ -46,25 +46,19 @@ class TPCFastTransformGeo float x{0.f}; ///< nominal X coordinate of the padrow [cm] int32_t maxPad{0}; ///< maximal pad number = n pads - 1 float padWidth{0.f}; ///< width of pads [cm] - float u0{0.f}; ///< min. u coordinate - - /// get U min - GPUd() float getUmin() const { return u0; } - - /// get U max - GPUd() float getUmax() const { return -u0; } + float yMin{0.f}; ///< min. y coordinate /// get Y min - GPUd() float getYmin() const { return u0; } + GPUd() float getYmin() const { return yMin; } /// get Y max - GPUd() float getYmax() const { return -u0; } + GPUd() float getYmax() const { return -yMin; } /// get Y range GPUd() std::tuple getYrange() const { return {getYmin(), getYmax()}; } - /// get width in U - GPUd() float getUwidth() const { return -2.f * u0; } + /// get width in Y + GPUd() float getYwidth() const { return -2.f * yMin; } ClassDefNV(RowInfo, 1); }; @@ -145,23 +139,14 @@ class TPCFastTransformGeo GPUd() std::tuple convPadDriftLengthToLocal(int32_t sector, int32_t row, float pad, float driftLength) const; /// convert DriftLength -> Local c.s. - GPUd() float convDriftLengthToLocal(int32_t sector, float driftLength) const; + GPUd() float convDriftLengthToZ(int32_t sector, float driftLength) const; + + /// convert Z to DriftLength + GPUd() float convZtoDriftLength(int32_t sector, float z) const; /// convert Local c.s. -> Pad, DriftLength GPUd() std::tuple convLocalToPadDriftLength(int32_t sector, int32_t row, float y, float z) const; - /// convert UV -> Local c.s. - GPUd() void convUVtoLocal1(int32_t sector, float u, float v, float& y, float& z) const; - - /// convert Local-> UV c.s. - GPUd() void convLocalToUV1(int32_t sector, float y, float z, float& u, float& v) const; - - /// convert Pad coordinate -> U - GPUd() float convPadToU(int32_t row, float pad) const; - - /// convert U -> Pad coordinate - GPUd() float convUtoPad(int32_t row, float u) const; - /// Print method void print() const; @@ -257,22 +242,16 @@ GPUdi() std::tuple TPCFastTransformGeo::convPadDriftLengthToLocal( return {y, z}; } -GPUdi() float TPCFastTransformGeo::convDriftLengthToLocal(int32_t sector, float driftLength) const +GPUdi() float TPCFastTransformGeo::convDriftLengthToZ(int32_t sector, float driftLength) const { /// convert DriftLength -> Local c.s. return (sector < NumberOfSectorsA) ? (mTPCzLength - driftLength) : (driftLength - mTPCzLength); } -GPUdi() void TPCFastTransformGeo::convUVtoLocal1(int32_t sector, float u, float v, float& ly, float& lz) const +GPUdi() float TPCFastTransformGeo::convZtoDriftLength(int32_t sector, float z) const { - /// convert UV -> Local c.s. - if (sector < NumberOfSectorsA) { // TPC side A - ly = u; - lz = mTPCzLength - v; - } else { // TPC side C - ly = -u; // pads are mirrorred on C-side - lz = v - mTPCzLength; // drift direction is mirrored on C-side - } + /// convert Z to DriftLength + return (sector < NumberOfSectorsA) ? (mTPCzLength - z) : (z + mTPCzLength); } GPUdi() std::tuple TPCFastTransformGeo::getZrange(int32_t sector) const @@ -301,32 +280,6 @@ GPUdi() std::tuple TPCFastTransformGeo::convLocalToPadDriftLength( return {pad, l}; } -GPUdi() void TPCFastTransformGeo::convLocalToUV1(int32_t sector, float ly, float lz, float& u, float& v) const -{ - /// convert Local-> UV c.s. - if (sector < NumberOfSectorsA) { // TPC side A - u = ly; - v = mTPCzLength - lz; - } else { // TPC side C - u = -ly; // pads are mirrorred on C-side - v = lz + mTPCzLength; // drift direction is mirrored on C-side - } -} - -GPUdi() float TPCFastTransformGeo::convPadToU(int32_t row, float pad) const -{ - /// convert Pad coordinate -> U - const RowInfo& rowInfo = getRowInfo(row); - return (pad - 0.5f * rowInfo.maxPad) * rowInfo.padWidth; -} - -GPUdi() float TPCFastTransformGeo::convUtoPad(int32_t row, float u) const -{ - /// convert U -> Pad coordinate - const RowInfo& rowInfo = getRowInfo(row); - return u / rowInfo.padWidth + 0.5f * rowInfo.maxPad; -} - } // namespace gpu } // namespace o2 diff --git a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C index bee1f9107ddd2..f0c03d9f5f081 100644 --- a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C +++ b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C @@ -185,9 +185,10 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* for (int32_t iRow = 0; iRow < geo.getNumberOfRows(); iRow++) { auto& info = corr.getSectorRowInfo(iSector, iRow); std::cout << "sector " << iSector << " row " << iRow - << " gridV0 " << info.gridV0 << " gridCorrU0 " << info.gridCorrU0 << " gridCorrV0 " << info.gridCorrV0 - << " scaleCorrUtoGrid " << info.scaleCorrUtoGrid << " scaleCorrVtoGrid " << info.scaleCorrVtoGrid - << " gridU0 " << info.gridU0 << " scaleUtoGrid " << info.scaleUtoGrid << " scaleVtoGrid " << info.scaleVtoGrid + << " gridY0 " << info.gridMeasured.y0 << " gridL0 " << info.gridMeasured.l0 + << " scaleYtoGrid " << info.gridMeasured.yScale << " scaleLtoGrid " << info.gridMeasured.lScale + << " gridRealY0 " << info.gridReal.y0 << " gridRealL0 " << info.gridReal.l0 + << " scaleRealYtoGrid " << info.gridReal.yScale << " scaleRealLtoGrid " << info.gridReal.lScale << std::endl; } } From 0e98f2942391c7977494fa58f384fa3708489ee9 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Mon, 14 Apr 2025 19:01:36 +0000 Subject: [PATCH 511/701] TPC Splines: completely switch to local TPC coordinates in the grid --- .../TPCFastSpaceChargeCorrectionHelper.h | 10 +- .../TPCFastSpaceChargeCorrectionHelper.cxx | 270 +++++++++--------- .../TPCFastSpaceChargeCorrection.cxx | 119 ++++---- .../TPCFastSpaceChargeCorrection.h | 137 ++++----- .../TPCFastTransform.cxx | 4 - GPU/TPCFastTransformation/TPCFastTransform.h | 4 +- .../TPCFastTransformGeo.h | 43 ++- .../TPCFastTransformationLinkDef_O2.h | 1 + .../macro/TPCFastTransformInit.C | 12 +- 9 files changed, 328 insertions(+), 272 deletions(-) diff --git a/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h b/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h index 747ed74c9bcad..e8afd9be97d5f 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h @@ -85,8 +85,16 @@ class TPCFastSpaceChargeCorrectionHelper const int nKnotsY = 10, const int nKnotsZ = 20); /// Create SpaceCharge correction out of the voxel tree + /// \param trackResiduals TrackResiduals object + /// \param voxResTree TTree with voxel residuals + /// \param voxResTreeInverse TTree with inverse voxel residuals + /// \param useSmoothed if true, use smoothed residuals + /// \param invertSigns if true, invert the signs of the residuals + /// \return pointer to the created TPCFastSpaceChargeCorrection object + /// \note voxel trees wont be changed. They are read as non-const because of the ROOT::TTreeProcessorMT interface std::unique_ptr createFromTrackResiduals( - const o2::tpc::TrackResiduals& trackResiduals, TTree* voxResTree, TTree* voxResTreeInverse, bool useSmoothed, bool invertSigns); + const o2::tpc::TrackResiduals& trackResiduals, TTree* voxResTree, TTree* voxResTreeInverse, // + bool useSmoothed, bool invertSigns); /// _______________ Utilities ________________________ diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index 021074c49f21a..d2f6cf57b0de7 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -258,12 +258,14 @@ std::unique_ptr TPCFastSpaceChargeCorrectionHelper correction.startConstruction(mGeo, nCorrectionScenarios); // assign spline type for TPC rows - for (int row = 0; row < mGeo.getNumberOfRows(); row++) { - int scenario = row / 10; - if (scenario >= nCorrectionScenarios) { - scenario = nCorrectionScenarios - 1; + for (int sector = 0; sector < mGeo.getNumberOfSectors(); sector++) { + for (int row = 0; row < mGeo.getNumberOfRows(); row++) { + int scenario = row / 10; + if (scenario >= nCorrectionScenarios) { + scenario = nCorrectionScenarios - 1; + } + correction.setRowScenarioID(sector, row, scenario); } - correction.setRowScenarioID(row, scenario); } for (int scenario = 0; scenario < nCorrectionScenarios; scenario++) { @@ -397,93 +399,100 @@ std::unique_ptr TPCFastSpaceChargeCorrect int nY2Xbins = trackResiduals.getNY2XBins(); int nZ2Xbins = trackResiduals.getNZ2XBins(); - std::vector uvBinsDouble[2]; + std::vector knotsDouble[3]; + + knotsDouble[0].reserve(nY2Xbins); + knotsDouble[1].reserve(nZ2Xbins); + knotsDouble[2].reserve(nZ2Xbins); - uvBinsDouble[0].reserve(nY2Xbins); - uvBinsDouble[1].reserve(nZ2Xbins); + // to get enouth measurements, make a spline knot at every second bin. Boundary bins are always included. for (int i = 0, j = nY2Xbins - 1; i <= j; i += 2, j -= 2) { - uvBinsDouble[0].push_back(trackResiduals.getY2X(0, i)); + knotsDouble[0].push_back(trackResiduals.getY2X(0, i)); if (j >= i + 1) { - uvBinsDouble[0].push_back(trackResiduals.getY2X(0, j)); + knotsDouble[0].push_back(trackResiduals.getY2X(0, j)); } } for (int i = 0, j = nZ2Xbins - 1; i <= j; i += 2, j -= 2) { - uvBinsDouble[1].push_back(-trackResiduals.getZ2X(i)); + knotsDouble[1].push_back(trackResiduals.getZ2X(i)); + knotsDouble[2].push_back(-trackResiduals.getZ2X(i)); if (j >= i + 1) { - uvBinsDouble[1].push_back(-trackResiduals.getZ2X(j)); + knotsDouble[1].push_back(trackResiduals.getZ2X(j)); + knotsDouble[2].push_back(-trackResiduals.getZ2X(j)); } } - std::vector uvBinsInt[2]; - - for (int iuv = 0; iuv < 2; iuv++) { - auto& bins = uvBinsDouble[iuv]; - std::sort(bins.begin(), bins.end()); + std::vector knotsInt[3]; - auto& binsInt = uvBinsInt[iuv]; - binsInt.reserve(bins.size()); + for (int dim = 0; dim < 3; dim++) { + auto& knotsD = knotsDouble[dim]; + std::sort(knotsD.begin(), knotsD.end()); - double dy = bins[1] - bins[0]; - for (int i = 2; i < bins.size(); i++) { - double dd = bins[i] - bins[i - 1]; - if (dd < dy) { - dy = dd; + double pitch = knotsD[1] - knotsD[0]; // min distance between the knots + for (int i = 2; i < knotsD.size(); i++) { + double d = knotsD[i] - knotsD[i - 1]; + if (d < pitch) { + pitch = d; } } - // spline knots must be positioned on the grid with integer internal coordinate - // take the knot position accuracy of 0.1*dy - dy = dy / 10.; - double y0 = bins[0]; - double y1 = bins[bins.size() - 1]; - for (auto& y : bins) { - y -= y0; - int iy = int(y / dy + 0.5); - binsInt.push_back(iy); - double yold = y / (y1 - y0) * 2 - 1.; - y = iy * dy; - y = y / (y1 - y0) * 2 - 1.; - if (iuv == 0) { - LOG(info) << "TPC SC splines: convert y bin: " << yold << " -> " << y << " -> " << iy; - } else { - LOG(info) << "TPC SC splines: convert z bin: " << yold << " -> " << y << " -> " << iy; - } + // spline knots must be positioned on the grid with an integer internal coordinate + // we set the knot positioning accuracy to 0.1*pitch + pitch = 0.1 * pitch; + auto& knotsI = knotsInt[dim]; + knotsI.reserve(knotsD.size()); + double u0 = knotsD[0]; + double u1 = knotsD[knotsD.size() - 1]; + for (auto& u : knotsD) { + u -= u0; + int iu = int(u / pitch + 0.5); + knotsI.push_back(iu); + // debug printout: corrected vs original knot positions, scaled to [-1,1] interval + double uorig = u / (u1 - u0) * 2 - 1.; + u = (iu * pitch) / (u1 - u0) * 2 - 1.; + LOG(info) << "TPC SC splines: convert " << (dim == 0 ? "y" : (dim == 1 ? "z" : "-z")) << " bin to the knot: " << uorig << " -> " << u << " -> " << iu; } - if (binsInt.size() < 2) { - binsInt.clear(); - binsInt.push_back(0); - binsInt.push_back(1); + if (knotsI.size() < 2) { // minimum 2 knots + knotsI.clear(); + knotsI.push_back(0); + knotsI.push_back(1); } } - auto& yBinsInt = uvBinsInt[0]; - auto& zBinsInt = uvBinsInt[1]; + auto& yKnotsInt = knotsInt[0]; + auto& zKnotsIntA = knotsInt[1]; + auto& zKnotsIntC = knotsInt[2]; - int nKnotsY = yBinsInt.size(); - int nKnotsZ = zBinsInt.size(); + int nKnotsY = yKnotsInt.size(); + int nKnotsZA = zKnotsIntA.size(); + int nKnotsZC = zKnotsIntC.size(); // std::cout << "n knots Y: " << nKnotsY << std::endl; - // std::cout << "n knots Z: " << nKnotsZ << std::endl; + // std::cout << "n knots Z: " << nKnotsZA << ", " << nKnotsZC << std::endl; const int nRows = geo.getNumberOfRows(); const int nSectors = geo.getNumberOfSectors(); { // create the correction object - const int nCorrectionScenarios = 1; + const int nCorrectionScenarios = 2; // different grids for TPC A and TPC C sides correction.startConstruction(geo, nCorrectionScenarios); // init rows - for (int row = 0; row < geo.getNumberOfRows(); row++) { - correction.setRowScenarioID(row, 0); + for (int iSector = 0; iSector < nSectors; iSector++) { + int id = iSector < geo.getNumberOfSectorsA() ? 0 : 1; + for (int row = 0; row < geo.getNumberOfRows(); row++) { + correction.setRowScenarioID(iSector, row, id); + } } { // init spline scenario TPCFastSpaceChargeCorrection::SplineType spline; - spline.recreate(nKnotsY, &yBinsInt[0], nKnotsZ, &zBinsInt[0]); + spline.recreate(nKnotsY, &yKnotsInt[0], nKnotsZA, &zKnotsIntA[0]); correction.setSplineScenario(0, spline); + spline.recreate(nKnotsY, &yKnotsInt[0], nKnotsZC, &zKnotsIntC[0]); + correction.setSplineScenario(1, spline); } correction.finishConstruction(); } // .. create the correction object @@ -491,19 +500,23 @@ std::unique_ptr TPCFastSpaceChargeCorrect // set the grid borders for (int iSector = 0; iSector < geo.getNumberOfSectors(); iSector++) { for (int iRow = 0; iRow < geo.getNumberOfRows(); iRow++) { - const auto& rowInfo = geo.getRowInfo(iRow); auto& info = correction.getSectorRowInfo(iSector, iRow); const auto& spline = correction.getSpline(iSector, iRow); - double yMin = rowInfo.x * trackResiduals.getY2X(iRow, 0); - double yMax = rowInfo.x * trackResiduals.getY2X(iRow, trackResiduals.getNY2XBins() - 1); - double zMin = rowInfo.x * trackResiduals.getZ2X(0); - double zMax = rowInfo.x * trackResiduals.getZ2X(trackResiduals.getNZ2XBins() - 1); - double lMin = geo.getTPCzLength() - zMax; - double lMax = geo.getTPCzLength() - zMin; - info.gridMeasured.y0 = yMin; - info.gridMeasured.yScale = spline.getGridX1().getUmax() / (yMax - yMin); - info.gridMeasured.l0 = lMin; - info.gridMeasured.lScale = spline.getGridX2().getUmax() / (lMax - lMin); + double rowX = geo.getRowInfo(iRow).x; + double yMin = rowX * trackResiduals.getY2X(iRow, 0); + double yMax = rowX * trackResiduals.getY2X(iRow, trackResiduals.getNY2XBins() - 1); + double zMin = rowX * trackResiduals.getZ2X(0); + double zMax = rowX * trackResiduals.getZ2X(trackResiduals.getNZ2XBins() - 1); + double zOut = zMax; + if (iSector >= geo.getNumberOfSectorsA()) { + // TPC C side + zOut = -zOut; + zMax = -zMin; + zMin = zOut; + } + info.gridMeasured.set(yMin, spline.getGridX1().getUmax() / (yMax - yMin), // y + zMin, spline.getGridX2().getUmax() / (zMax - zMin), // z + zOut, geo.getZreadout(iSector)); // correction scaling region info.gridReal = info.gridMeasured; @@ -514,16 +527,16 @@ std::unique_ptr TPCFastSpaceChargeCorrect LOG(info) << "fast space charge correction helper: preparation took " << watch1.RealTime() << "s"; - for (int processingInverseCorrection = 0; processingInverseCorrection < 2; processingInverseCorrection++) { + for (int processingInverseCorrection = 0; processingInverseCorrection <= 1; processingInverseCorrection++) { TTree* currentTree = (processingInverseCorrection) ? voxResTreeInverse : voxResTree; if (!currentTree) { continue; } - - LOG(info) << "fast space charge correction helper: " << ((processingInverseCorrection) ? "inverse" : "direct") - << " : fill data points from track residuals.. "; + const char* directionName = (processingInverseCorrection) ? "inverse" : "direct"; + LOG(info) << "\n fast space charge correction helper: Process " << directionName + << " correction: fill data points from track residuals.. "; TStopwatch watch3; o2::gpu::TPCFastSpaceChargeCorrectionMap& map = helper->getCorrectionMap(); @@ -548,34 +561,42 @@ std::unique_ptr TPCFastSpaceChargeCorrect { // read data from the tree to vSectorData ROOT::TTreeProcessorMT processor(*currentTree, mNthreads); - + std::string errMsg = std::string("Error reading ") + directionName + " track residuals: "; auto myThread = [&](TTreeReader& readerSubRange) { TTreeReaderValue v(readerSubRange, "voxRes"); while (readerSubRange.Next()) { int iSector = (int)v->bsec; if (iSector < 0 || iSector >= nSectors) { - LOG(fatal) << "Error reading voxels: voxel Sector number " << iSector << " is out of range"; + LOG(fatal) << errMsg << "Sector number " << iSector << " is out of range"; continue; } int iRow = (int)v->bvox[o2::tpc::TrackResiduals::VoxX]; // bin number in x (= pad row) if (iRow < 0 || iRow >= nRows) { - LOG(fatal) << "Row number " << iRow << " is out of range"; + LOG(fatal) << errMsg << "Row number " << iRow << " is out of range"; } + double rowX = trackResiduals.getX(iRow); // X of the pad row int iy = v->bvox[o2::tpc::TrackResiduals::VoxF]; // bin number in y/x 0..14 int iz = v->bvox[o2::tpc::TrackResiduals::VoxZ]; // bin number in z/x 0..4 auto& data = vSectorData[iSector * nRows + iRow][iy * nZ2Xbins + iz]; data.mNentries = (int)v->stat[o2::tpc::TrackResiduals::VoxV]; data.mX = v->stat[o2::tpc::TrackResiduals::VoxX]; - data.mY = v->stat[o2::tpc::TrackResiduals::VoxF]; - data.mZ = v->stat[o2::tpc::TrackResiduals::VoxZ]; + data.mY = v->stat[o2::tpc::TrackResiduals::VoxF] * rowX; + data.mZ = v->stat[o2::tpc::TrackResiduals::VoxZ] * rowX; data.mCx = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResX] : v->D[o2::tpc::TrackResiduals::ResX]; data.mCy = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResY] : v->D[o2::tpc::TrackResiduals::ResY]; data.mCz = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResZ] : v->D[o2::tpc::TrackResiduals::ResZ]; - if (0 && data.mNentries < 1) { - data.mCx = 0.; - data.mCy = 0.; - data.mCz = 0.; - data.mNentries = 1; + if (invertSigns) { + data.mCx *= -1.; + data.mCy *= -1.; + data.mCz *= -1.; + } + if (data.mNentries > 0) { + if (iSector < geo.getNumberOfSectorsA() && data.mZ < 0) { + LOG(error) << errMsg << "fitted Z coordinate " << data.mZ << " is negative for sector " << iSector; + } + if (iSector >= geo.getNumberOfSectorsA() && data.mZ > 0) { + LOG(error) << errMsg << "fitted Z coordinate " << data.mZ << " is positive for sector " << iSector; + } } } }; @@ -599,7 +620,6 @@ std::unique_ptr TPCFastSpaceChargeCorrect // LOG(info) << "Processing Sector " << iSector << " row " << iRow; // complete the voxel data - { int xBin = iRow; double x = trackResiduals.getX(xBin); // radius of the pad row @@ -619,36 +639,28 @@ std::unique_ptr TPCFastSpaceChargeCorrect if (iSector >= geo.getNumberOfSectorsA()) { vox.mZ = -vox.mZ; } - data.mY *= x; - data.mZ *= x; - /* - if ( fabs(x - data.mX) > 0.01 || fabs(vox.mY - data.mY) > 5. || fabs(vox.mZ - data.mZ) > 5.) { - std::cout - << " sector " << iSector << " row " << iRow - << " voxel x " << x << " y " << vox.mY << " z " << vox.mZ - << " data x " << data.mX << " y " << data.mY << " z " << data.mZ - << std::endl; - } - */ - if (0) { // debug: always use voxel center instead of the mean position - data.mY = vox.mY; - data.mZ = vox.mZ; - } - if (data.mNentries < 1) { // no data + if (data.mNentries > 0) { // voxel contains data + vox.mSmoothingStep = 0; // take original data + isDataFound = true; + if (fabs(x - data.mX) > 1. || fabs(vox.mY - data.mY) > 5. || fabs(vox.mZ - data.mZ) > 5.) { + std::cout << directionName << ": fitted voxel is too far from the nominal position: " + << " sector " << iSector << " row " << iRow + << " center x " << x << " y " << vox.mY << " z " << vox.mZ + << " fitted x " << data.mX << " y " << data.mY << " z " << data.mZ + << std::endl; + } + } else { // no data, take voxel center position data.mCx = 0.; data.mCy = 0.; data.mCz = 0.; + data.mX = x; + data.mY = vox.mY; + data.mZ = vox.mZ; + vox.mSmoothingStep = 100; // fill this data point with smoothed values from the neighbours + } + if (0) { // debug: always use voxel center instead of the mean position data.mY = vox.mY; data.mZ = vox.mZ; - vox.mSmoothingStep = 100; - } else { // voxel contains data - if (invertSigns) { - data.mCx *= -1.; - data.mCy *= -1.; - data.mCz *= -1.; - } - vox.mSmoothingStep = 0; // original data - isDataFound = true; } } } @@ -734,13 +746,13 @@ std::unique_ptr TPCFastSpaceChargeCorrect if (vox2.mSmoothingStep > 2) { LOG(fatal) << "empty voxel is not repared: y " << iy2 << " z " << iz2; } - double y1 = vox1.mY; - double z1 = vox1.mZ; + double y1 = data1.mY; + double z1 = data1.mZ; double cx1 = data1.mCx; double cy1 = data1.mCy; double cz1 = data1.mCz; - double y2 = vox2.mY; - double z2 = vox2.mZ; + double y2 = data2.mY; + double z2 = data2.mZ; double cx2 = data2.mCx; double cy2 = data2.mCy; double cz2 = data2.mCz; @@ -849,6 +861,9 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector splineParameters; for (int row = iThread; row < mGeo.getNumberOfRows(); row += mNthreads) { + auto& sectorRowInfo = correction.getSectorRowInfo(sector, row); + sectorRowInfo.gridReal = sectorRowInfo.gridMeasured; + TPCFastSpaceChargeCorrection::SplineType spline = correction.getSpline(sector, row); helper.setSpline(spline, 10, 10); @@ -897,33 +912,16 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector(mFlatBufferPtr + sectorDataOffset); - bufferSize = sectorDataOffset + mSectorDataSizeBytes[is] * mGeo.getNumberOfSectors(); + size_t splineDataOffset = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); + mSplineData[is] = reinterpret_cast(mFlatBufferPtr + splineDataOffset); + bufferSize = splineDataOffset + mDataSizeBytes[is]; } } @@ -199,14 +199,8 @@ void TPCFastSpaceChargeCorrection::print() const mGeo.print(); LOG(info) << " mNumberOfScenarios = " << mNumberOfScenarios; LOG(info) << " mTimeStamp = " << mTimeStamp; - LOG(info) << " mSectorDataSizeBytes = " << mSectorDataSizeBytes[0] << " " << mSectorDataSizeBytes[1] << " " << mSectorDataSizeBytes[2]; - { - LOG(info) << " TPC rows: "; - for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { - const RowInfo& r = mRowInfos[i]; - LOG(info) << " tpc row " << i << ": splineScenarioID = " << r.splineScenarioID << " dataOffsetBytes = " << r.dataOffsetBytes; - } - } + LOG(info) << " mDataSizeBytes = " << mDataSizeBytes[0] << " " << mDataSizeBytes[1] << " " << mDataSizeBytes[2]; + if (mScenarioPtr) { for (int32_t i = 0; i < mNumberOfScenarios; i++) { LOG(info) << " SplineScenario " << i << ": "; @@ -255,8 +249,23 @@ void TPCFastSpaceChargeCorrection::startConstruction(const TPCFastTransformGeo& assert(mConstructionScenarios != nullptr); - for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { - mRowInfos[i].splineScenarioID = -1; + for (int32_t i = 0; i < mGeo.getNumberOfSectors(); i++) { + mSectorInfo[i].vMax1 = 0.; + for (int32_t j = 0; j < mGeo.getNumberOfRows(); j++) { + auto& row = mSectorRowInfos[mGeo.getMaxNumberOfRows() * i + j]; + row.splineScenarioID = -1; + row.gridReal = {}; + row.gridMeasured = {}; + row.dataOffsetBytes[0] = 0; + row.dataOffsetBytes[1] = 0; + row.dataOffsetBytes[2] = 0; + row.minCorr[0] = 0; + row.minCorr[1] = 0; + row.minCorr[2] = 0; + row.maxCorr[0] = 0; + row.maxCorr[1] = 0; + row.maxCorr[2] = 0; + } } for (int32_t i = 0; i < mNumberOfScenarios; i++) { @@ -268,18 +277,18 @@ void TPCFastSpaceChargeCorrection::startConstruction(const TPCFastTransformGeo& mScenarioPtr = nullptr; for (int32_t s = 0; s < 3; s++) { mSplineData[s] = nullptr; - mSectorDataSizeBytes[s] = 0; + mDataSizeBytes[s] = 0; } mClassVersion = 4; } -void TPCFastSpaceChargeCorrection::setRowScenarioID(int32_t iRow, int32_t iScenario) +void TPCFastSpaceChargeCorrection::setRowScenarioID(int32_t iSector, int32_t iRow, int32_t iScenario) { /// Initializes a TPC row assert(mConstructionMask & ConstructionState::InProgress); + assert(iSector >= 0 && iSector < mGeo.getNumberOfSectors()); assert(iRow >= 0 && iRow < mGeo.getNumberOfRows() && iScenario >= 0 && iScenario < mNumberOfScenarios); - - RowInfo& row = mRowInfos[iRow]; + auto& row = getSectorRowInfo(iSector, iRow); row.splineScenarioID = iScenario; for (int32_t s = 0; s < 3; s++) { row.dataOffsetBytes[s] = 0; @@ -302,9 +311,14 @@ void TPCFastSpaceChargeCorrection::finishConstruction() assert(mConstructionMask & ConstructionState::InProgress); - for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { - assert(mRowInfos[i].splineScenarioID >= 0); + for (int32_t i = 0; i < mGeo.getNumberOfSectors(); i++) { + for (int32_t j = 0; j < mGeo.getNumberOfRows(); j++) { + SectorRowInfo& row = getSectorRowInfo(i, j); + assert(row.splineScenarioID >= 0); + assert(row.splineScenarioID < mNumberOfScenarios); + } } + for (int32_t i = 0; i < mNumberOfScenarios; i++) { assert(mConstructionScenarios[i].isConstructed()); } @@ -324,18 +338,20 @@ void TPCFastSpaceChargeCorrection::finishConstruction() scBufferSize = alignSize(scBufferSize + sp.getFlatBufferSize(), sp.getBufferAlignmentBytes()); } size_t bufferSize = scBufferOffsets[0] + scBufferSize; - size_t sectorDataOffset[3]; + size_t splineDataOffset[3]; for (int32_t is = 0; is < 3; is++) { - sectorDataOffset[is] = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); - mSectorDataSizeBytes[is] = 0; - for (int32_t i = 0; i < mGeo.getNumberOfRows(); i++) { - RowInfo& row = mRowInfos[i]; - SplineType& spline = mConstructionScenarios[row.splineScenarioID]; - row.dataOffsetBytes[is] = alignSize(mSectorDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); - mSectorDataSizeBytes[is] = row.dataOffsetBytes[is] + spline.getSizeOfParameters(); + splineDataOffset[is] = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); + mDataSizeBytes[is] = 0; + for (int32_t i = 0; i < mGeo.getNumberOfSectors(); i++) { + for (int32_t j = 0; j < mGeo.getNumberOfRows(); j++) { + SectorRowInfo& row = getSectorRowInfo(i, j); + SplineType& spline = mConstructionScenarios[row.splineScenarioID]; + row.dataOffsetBytes[is] = alignSize(mDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); + mDataSizeBytes[is] = row.dataOffsetBytes[is] + spline.getSizeOfParameters(); + } } - mSectorDataSizeBytes[is] = alignSize(mSectorDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); - bufferSize = sectorDataOffset[is] + mSectorDataSizeBytes[is] * mGeo.getNumberOfSectors(); + mDataSizeBytes[is] = alignSize(mDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); + bufferSize = splineDataOffset[is] + mDataSizeBytes[is]; } FlatObject::finishConstruction(bufferSize); @@ -350,7 +366,7 @@ void TPCFastSpaceChargeCorrection::finishConstruction() } for (int32_t is = 0; is < 3; is++) { - mSplineData[is] = reinterpret_cast(mFlatBufferPtr + sectorDataOffset[is]); + mSplineData[is] = reinterpret_cast(mFlatBufferPtr + splineDataOffset[is]); } releaseConstructionMemory(); @@ -363,9 +379,9 @@ GPUd() void TPCFastSpaceChargeCorrection::setNoCorrection() { // initialise all corrections to 0. for (int32_t sector = 0; sector < mGeo.getNumberOfSectors(); sector++) { - double vLength = mGeo.getTPCzLength(); - SectorInfo& sectorInfo = getSectorInfo(sector); - sectorInfo.vMax = vLength; + + getSectorInfo(sector).vMax1 = mGeo.getTPCzLength(); + for (int32_t row = 0; row < mGeo.getNumberOfRows(); row++) { const SplineType& spline = getSpline(sector, row); @@ -385,10 +401,12 @@ GPUd() void TPCFastSpaceChargeCorrection::setNoCorrection() SectorRowInfo& info = getSectorRowInfo(sector, row); - info.gridMeasured.y0 = mGeo.getRowInfo(row).getYmin(); - info.gridMeasured.yScale = spline.getGridX1().getUmax() / mGeo.getRowInfo(row).getYwidth(); - info.gridMeasured.l0 = 0.f; - info.gridMeasured.lScale = spline.getGridX2().getUmax() / vLength; + float y0 = mGeo.getRowInfo(row).getYmin(); + float yScale = spline.getGridX1().getUmax() / mGeo.getRowInfo(row).getYwidth(); + float z0 = mGeo.getZmin(sector); + float zScale = spline.getGridX2().getUmax() / mGeo.getTPCzLength(); + float zReadout = mGeo.getZreadout(sector); + info.gridMeasured.set(y0, yScale, z0, zScale, zReadout, zReadout); info.gridReal = info.gridMeasured; } // row @@ -399,8 +417,10 @@ void TPCFastSpaceChargeCorrection::constructWithNoCorrection(const TPCFastTransf { const int32_t nCorrectionScenarios = 1; startConstruction(geo, nCorrectionScenarios); - for (int32_t row = 0; row < geo.getNumberOfRows(); row++) { - setRowScenarioID(row, 0); + for (int32_t sector = 0; sector < geo.getNumberOfSectors(); sector++) { + for (int32_t row = 0; row < geo.getNumberOfRows(); row++) { + setRowScenarioID(sector, row, 0); + } } { TPCFastSpaceChargeCorrection::SplineType spline; @@ -456,20 +476,13 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) if (prn) { LOG(info) << "check inverse transform for sector " << sector; } - double vLength = mGeo.getTPCzLength(); + MaxValue maxDsector[3]; for (int32_t row = 0; row < mGeo.getNumberOfRows(); row++) { double x = mGeo.getRowInfo(row).x; auto [y0, y1] = mGeo.getRowInfo(row).getYrange(); auto [z0, z1] = mGeo.getZrange(sector); - // grid borders - if (sector < mGeo.getNumberOfSectorsA()) { - z1 = vLength - getSectorRowInfo(sector, row).gridMeasured.l0; - } else { - z0 = getSectorRowInfo(sector, row).gridMeasured.l0 - vLength; - } - double stepY = (y1 - y0) / 100.; double stepZ = (z1 - z0) / 100.; MaxValue maxDrow[3]; @@ -479,7 +492,7 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) double realX = x + dx; double realY = y + dy; double realZ = z + dz; - if (!isLocalInsideGrid(sector, row, y, z) || !isLocalInsideGrid(sector, row, realY, realZ)) { + if (!isLocalInsideGrid(sector, row, y, z) || !isRealLocalInsideGrid(sector, row, realY, realZ)) { continue; } double r2 = realX * realX + realY * realY; diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index 4564d584c8dce..7112a04b364c6 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -43,31 +43,58 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// \brief The struct contains necessary info for TPC padrow /// struct RowInfo { - int32_t splineScenarioID{0}; ///< scenario index (which of Spline2D splines to use) - size_t dataOffsetBytes[3]{0}; ///< offset for the spline data withing the TPC sector ClassDefNV(RowInfo, 1); }; struct GridInfo { - float y0{0.f}; ///< Y coordinate of the U-grid start - float yScale{0.f}; //< scale Y to U-grid coordinate - float l0{0.f}; ///< Drift Length coordinate of the V-grid start - float lScale{0.f}; //< scale Drift Length to V-grid coordinate + private: + float y0{0.f}; ///< Y coordinate of the U-grid start + float yScale{0.f}; //< scale Y to U-grid coordinate + float z0{0.f}; ///< Z coordinate of the V-grid start + float zScale{0.f}; //< scale Z to V-grid coordinate + float zOut{0.f}; // outer z of the grid; + float splineScalingWithZ{0.f}; ///< spline scaling factor in the Z region between the zOut and the readout plane + + public: + void set(float y0, float yScale, float z0, float zScale, float zOut, float zReadout) + { + this->y0 = y0; + this->yScale = yScale; + this->z0 = z0; + this->zScale = zScale; + this->zOut = zOut; + // no scaling when the distance to the readout is too small + this->splineScalingWithZ = fabs(zReadout - zOut) > 1. ? 1. / (zReadout - zOut) : 0.; + } + + float getY0() const { return y0; } + float getYscale() const { return yScale; } + float getZ0() const { return z0; } + float getZscale() const { return zScale; } + + float getSpineScaleForZ(float z) const + { + return 1.f - GPUCommonMath::Clamp((z - zOut) * splineScalingWithZ, 0.f, 1.f); + } + + /// convert local y, z to internal grid coordinates u,v, and spline scale + std::tuple convLocalToGridUntruncated(float y, float z) const + { + return {(y - y0) * yScale, (z - z0) * zScale, getSpineScaleForZ(z)}; + } - float getScale(float l) const + /// convert internal grid coordinates u,v to local y, z + std::tuple convGridToLocal(float gridU, float gridV) const { - if (l < 0.f) { // outside of the TPC - return 0.f; - } - if (l < l0) { // between the grid and the readout - return l / l0; - } - return 1.f; // inside the grid + return {y0 + gridU / yScale, z0 + gridV / zScale}; } ClassDefNV(GridInfo, 1); }; struct SectorRowInfo { + int32_t splineScenarioID{0}; ///< scenario index (which of Spline2D splines to use) + size_t dataOffsetBytes[3]{0}; ///< offset for the spline data withing a TPC sector + GridInfo gridMeasured; ///< grid info for measured coordinates GridInfo gridReal; ///< grid info for real coordinates @@ -118,7 +145,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject }; struct SectorInfo { - float vMax{0.f}; ///< Max value of V coordinate + float vMax1{0.f}; ///< Max value of V coordinate ClassDefNV(SectorInfo, 1); }; @@ -170,7 +197,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject void startConstruction(const TPCFastTransformGeo& geo, int32_t numberOfSplineScenarios); /// Initializes a TPC row - void setRowScenarioID(int32_t iRow, int32_t iScenario); + void setRowScenarioID(int32_t iSector, int32_t iRow, int32_t iScenario); /// Sets approximation scenario void setSplineScenario(int32_t scenarioIndex, const SplineType& spline); @@ -259,6 +286,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject GPUd() std::tuple convGridToRealLocal(int32_t sector, int32_t row, float u, float v) const; GPUd() bool isLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const; + GPUd() bool isRealLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const; /// TPC geometry information GPUd() const TPCFastTransformGeo& getGeometry() const @@ -333,7 +361,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject char* mSplineData[3]; //! (transient!!) pointer to the spline data in the flat buffer - size_t mSectorDataSizeBytes[3]; ///< size of the data for one sector in the flat buffer + size_t mDataSizeBytes[3]; ///< size of the data for one sector in the flat buffer float fInterpolationSafetyMargin{0.1f}; // 10% area around the TPC row. Outside of this area the interpolation returns the boundary values. @@ -356,29 +384,25 @@ class TPCFastSpaceChargeCorrection : public FlatObject GPUdi() const TPCFastSpaceChargeCorrection::SplineType& TPCFastSpaceChargeCorrection::getSpline(int32_t sector, int32_t row) const { /// Gives const pointer to spline - const RowInfo& rowInfo = mRowInfos[row]; - return mScenarioPtr[rowInfo.splineScenarioID]; + return mScenarioPtr[getSectorRowInfo(sector, row).splineScenarioID]; } GPUdi() TPCFastSpaceChargeCorrection::SplineType& TPCFastSpaceChargeCorrection::getSpline(int32_t sector, int32_t row) { /// Gives pointer to spline - const RowInfo& rowInfo = mRowInfos[row]; - return mScenarioPtr[rowInfo.splineScenarioID]; + return mScenarioPtr[getSectorRowInfo(sector, row).splineScenarioID]; } GPUdi() float* TPCFastSpaceChargeCorrection::getSplineData(int32_t sector, int32_t row, int32_t iSpline) { /// Gives pointer to spline data - const RowInfo& rowInfo = mRowInfos[row]; - return reinterpret_cast(mSplineData[iSpline] + mSectorDataSizeBytes[iSpline] * sector + rowInfo.dataOffsetBytes[iSpline]); + return reinterpret_cast(mSplineData[iSpline] + getSectorRowInfo(sector, row).dataOffsetBytes[iSpline]); } GPUdi() const float* TPCFastSpaceChargeCorrection::getSplineData(int32_t sector, int32_t row, int32_t iSpline) const { /// Gives pointer to spline data - const RowInfo& rowInfo = mRowInfos[row]; - return reinterpret_cast(mSplineData[iSpline] + mSectorDataSizeBytes[iSpline] * sector + rowInfo.dataOffsetBytes[iSpline]); + return reinterpret_cast(mSplineData[iSpline] + getSectorRowInfo(sector, row).dataOffsetBytes[iSpline]); } GPUdi() TPCFastSpaceChargeCorrection::SplineTypeInvX& TPCFastSpaceChargeCorrection::getSplineInvX(int32_t sector, int32_t row) @@ -433,37 +457,35 @@ GPUdi() std::tuple TPCFastSpaceChargeCorrection::convLocalT { /// convert local y, z to internal grid coordinates u,v /// return values: u, v, scaling factor - const auto& info = getSectorRowInfo(sector, row); const SplineType& spline = getSpline(sector, row); - - float l = mGeo.convZtoDriftLength(sector, z); - float scale = info.gridMeasured.getScale(l); - float gridU = (y - info.gridMeasured.y0) * info.gridMeasured.yScale; - float gridV = (l - info.gridMeasured.l0) * info.gridMeasured.lScale; - - // shrink to the grid area + auto [gridU, gridV, scale] = getSectorRowInfo(sector, row).gridMeasured.convLocalToGridUntruncated(y, z); + // shrink to the grid gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); gridV = GPUCommonMath::Clamp(gridV, 0.f, (float)spline.getGridX2().getUmax()); - return {gridU, gridV, scale}; } GPUdi() bool TPCFastSpaceChargeCorrection::isLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const { - /// ccheck if local y, z are inside the grid - - const auto& info = getSectorRowInfo(sector, row); + /// check if local y, z are inside the grid + auto [gridU, gridV, scale] = getSectorRowInfo(sector, row).gridMeasured.convLocalToGridUntruncated(y, z); const auto& spline = getSpline(sector, row); - float l = mGeo.convZtoDriftLength(sector, z); - - float gridU = (y - info.gridMeasured.y0) * info.gridMeasured.yScale; - float gridV = (l - info.gridMeasured.l0) * info.gridMeasured.lScale; - - // shrink to the grid area - if (gridU < 0.f || gridU > (float)spline.getGridX1().getUmax()) { + // shrink to the grid + if (gridU < 0.f || gridU > (float)spline.getGridX1().getUmax() || // + gridV < 0.f || gridV > (float)spline.getGridX2().getUmax()) { return false; } - if (gridV < 0.f || gridV > (float)spline.getGridX2().getUmax()) { + return true; +} + +GPUdi() bool TPCFastSpaceChargeCorrection::isRealLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const +{ + /// check if local y, z are inside the grid + auto [gridU, gridV, scale] = getSectorRowInfo(sector, row).gridReal.convLocalToGridUntruncated(y, z); + const auto& spline = getSpline(sector, row); + // shrink to the grid + if (gridU < 0.f || gridU > (float)spline.getGridX1().getUmax() || // + gridV < 0.f || gridV > (float)spline.getGridX2().getUmax()) { return false; } return true; @@ -472,39 +494,24 @@ GPUdi() bool TPCFastSpaceChargeCorrection::isLocalInsideGrid(int32_t sector, int GPUdi() std::tuple TPCFastSpaceChargeCorrection::convGridToLocal(int32_t sector, int32_t row, float gridU, float gridV) const { /// convert internal grid coordinates u,v to local y, z - const SectorRowInfo& info = getSectorRowInfo(sector, row); - float y = info.gridMeasured.y0 + gridU / info.gridMeasured.yScale; - float l = info.gridMeasured.l0 + gridV / info.gridMeasured.lScale; - float z = mGeo.convDriftLengthToZ(sector, l); - return {y, z}; + return getSectorRowInfo(sector, row).gridMeasured.convGridToLocal(gridU, gridV); } GPUdi() std::tuple TPCFastSpaceChargeCorrection::convRealLocalToGrid(int32_t sector, int32_t row, float y, float z) const { /// convert real y, z to the internal grid coordinates + scale - const auto& info = getSectorRowInfo(sector, row); - const auto& spline = getSpline(sector, row); - - float l = mGeo.convZtoDriftLength(sector, z); - float scale = info.gridReal.getScale(l); - float gridU = (y - info.gridReal.y0) * info.gridReal.yScale; - float gridV = (l - info.gridReal.l0) * info.gridReal.lScale; - - // shrink to the grid area + const SplineType& spline = getSpline(sector, row); + auto [gridU, gridV, scale] = getSectorRowInfo(sector, row).gridReal.convLocalToGridUntruncated(y, z); + // shrink to the grid gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); gridV = GPUCommonMath::Clamp(gridV, 0.f, (float)spline.getGridX2().getUmax()); - return {gridU, gridV, scale}; } GPUdi() std::tuple TPCFastSpaceChargeCorrection::convGridToRealLocal(int32_t sector, int32_t row, float gridU, float gridV) const { /// convert internal grid coordinates u,v to the real y, z - const SectorRowInfo& info = getSectorRowInfo(sector, row); - float y = info.gridReal.y0 + gridU / info.gridReal.yScale; - float l = info.gridReal.l0 + gridV / info.gridReal.lScale; - float z = mGeo.convDriftLengthToZ(sector, l); - return {y, z}; + return getSectorRowInfo(sector, row).gridReal.convGridToLocal(gridU, gridV); } GPUdi() std::tuple TPCFastSpaceChargeCorrection::getCorrectionLocal(int32_t sector, int32_t row, float y, float z) const diff --git a/GPU/TPCFastTransformation/TPCFastTransform.cxx b/GPU/TPCFastTransformation/TPCFastTransform.cxx index 625f70c1710a1..42c4c57ffa086 100644 --- a/GPU/TPCFastTransformation/TPCFastTransform.cxx +++ b/GPU/TPCFastTransformation/TPCFastTransform.cxx @@ -103,10 +103,6 @@ void TPCFastTransform::startConstruction(const TPCFastSpaceChargeCorrection& cor mApplyCorrection = 1; mT0 = 0.f; mVdrift = 0.f; - mVdriftCorrY = 0.f; - mLdriftCorr = 0.f; - mTOFcorr = 0.f; - mPrimVtxZ = 0.f; mLumi = DEFLUMI; mLumiError = 0.f; mIDC = DEFIDC; diff --git a/GPU/TPCFastTransformation/TPCFastTransform.h b/GPU/TPCFastTransformation/TPCFastTransform.h index 8807c0e3206f4..3b08296525fc7 100644 --- a/GPU/TPCFastTransformation/TPCFastTransform.h +++ b/GPU/TPCFastTransformation/TPCFastTransform.h @@ -551,7 +551,7 @@ GPUdi() void TPCFastTransform::Transform(int32_t sector, int32_t row, float pad, GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t sector, float time, float& z, float maxTimeBin) const { float l = (time - mT0 - maxTimeBin) * mVdrift; // drift length cm - z = getGeometry().convDriftLengthToZ(sector, l); + z = getGeometry().convDriftLengthToZ1(sector, l); } GPUdi() void TPCFastTransform::TransformInTimeFrame(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float maxTimeBin) const @@ -590,7 +590,7 @@ GPUdi() void TPCFastTransform::TransformIdealZ(int32_t sector, float time, float /// float l = (time - mT0 - vertexTime) * mVdrift; // drift length cm - z = getGeometry().convDriftLengthToZ(sector, l); + z = getGeometry().convDriftLengthToZ1(sector, l); } GPUdi() void TPCFastTransform::TransformIdeal(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime) const diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.h b/GPU/TPCFastTransformation/TPCFastTransformGeo.h index bbb94dcb8bedd..89b099ec63127 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.h +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.h @@ -126,6 +126,9 @@ class TPCFastTransformGeo /// Gives Z range for the corresponding TPC side GPUd() std::tuple getZrange(int32_t sector) const; + GPUd() float getZmin(int32_t sector) const; + GPUd() float getZmax(int32_t sector) const; + GPUd() float getZreadout(int32_t sector) const; /// _______________ Conversion of coordinate systems __________ @@ -139,10 +142,10 @@ class TPCFastTransformGeo GPUd() std::tuple convPadDriftLengthToLocal(int32_t sector, int32_t row, float pad, float driftLength) const; /// convert DriftLength -> Local c.s. - GPUd() float convDriftLengthToZ(int32_t sector, float driftLength) const; + GPUd() float convDriftLengthToZ1(int32_t sector, float driftLength) const; /// convert Z to DriftLength - GPUd() float convZtoDriftLength(int32_t sector, float z) const; + GPUd() float convZtoDriftLength1(int32_t sector, float z) const; /// convert Local c.s. -> Pad, DriftLength GPUd() std::tuple convLocalToPadDriftLength(int32_t sector, int32_t row, float y, float z) const; @@ -181,7 +184,7 @@ class TPCFastTransformGeo float mTPCzLength = 0.f; ///< Z length of one TPC side (A or C) SectorInfo mSectorInfos[NumberOfSectors + 1]; ///< array of sector information [fixed size] - RowInfo mRowInfos[MaxNumberOfRows + 1]; ///< array of row information [fixed size] + RowInfo mRowInfos[MaxNumberOfRows + 1]; ///< array of row information [fixed size] ClassDefNV(TPCFastTransformGeo, 3); }; @@ -242,13 +245,13 @@ GPUdi() std::tuple TPCFastTransformGeo::convPadDriftLengthToLocal( return {y, z}; } -GPUdi() float TPCFastTransformGeo::convDriftLengthToZ(int32_t sector, float driftLength) const +GPUdi() float TPCFastTransformGeo::convDriftLengthToZ1(int32_t sector, float driftLength) const { /// convert DriftLength -> Local c.s. return (sector < NumberOfSectorsA) ? (mTPCzLength - driftLength) : (driftLength - mTPCzLength); } -GPUdi() float TPCFastTransformGeo::convZtoDriftLength(int32_t sector, float z) const +GPUdi() float TPCFastTransformGeo::convZtoDriftLength1(int32_t sector, float z) const { /// convert Z to DriftLength return (sector < NumberOfSectorsA) ? (mTPCzLength - z) : (z + mTPCzLength); @@ -264,6 +267,36 @@ GPUdi() std::tuple TPCFastTransformGeo::getZrange(int32_t sector) } } +GPUdi() float TPCFastTransformGeo::getZmin(int32_t sector) const +{ + /// z min for the sector + if (sector < NumberOfSectorsA) { // TPC side A + return 0.f; + } else { // TPC side C + return -mTPCzLength; + } +} + +GPUdi() float TPCFastTransformGeo::getZmax(int32_t sector) const +{ + /// z max for the sector + if (sector < NumberOfSectorsA) { // TPC side A + return mTPCzLength; + } else { // TPC side C + return 0.f; + } +} + +GPUdi() float TPCFastTransformGeo::getZreadout(int32_t sector) const +{ + /// z readout for the sector + if (sector < NumberOfSectorsA) { // TPC side A + return mTPCzLength; + } else { // TPC side C + return -mTPCzLength; + } +} + GPUdi() std::tuple TPCFastTransformGeo::convLocalToPadDriftLength(int32_t sector, int32_t row, float y, float z) const { /// convert Local c.s. -> Pad, DriftLength diff --git a/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h b/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h index fc15506d5397c..916695a3be1c7 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h +++ b/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h @@ -70,6 +70,7 @@ #pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection + ; #pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::SectorInfo + ; #pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::SectorRowInfo + ; +#pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::GridInfo + ; #pragma link C++ class o2::gpu::CorrectionMapsHelper + ; #pragma link C++ struct o2::gpu::MultivariatePolynomialContainer + ; diff --git a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C index f0c03d9f5f081..974582792266b 100644 --- a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C +++ b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C @@ -150,7 +150,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* o2::tpc::TPCFastSpaceChargeCorrectionHelper* corrHelper = o2::tpc::TPCFastSpaceChargeCorrectionHelper::instance(); corrHelper->setNthreadsToMaximum(); - // corrHelper->setNthreads(1); + corrHelper->setNthreads(1); auto corrPtr = corrHelper->createFromTrackResiduals(trackResiduals, voxResTree, voxResTreeInverse, useSmoothed, invertSigns); @@ -185,10 +185,10 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* for (int32_t iRow = 0; iRow < geo.getNumberOfRows(); iRow++) { auto& info = corr.getSectorRowInfo(iSector, iRow); std::cout << "sector " << iSector << " row " << iRow - << " gridY0 " << info.gridMeasured.y0 << " gridL0 " << info.gridMeasured.l0 - << " scaleYtoGrid " << info.gridMeasured.yScale << " scaleLtoGrid " << info.gridMeasured.lScale - << " gridRealY0 " << info.gridReal.y0 << " gridRealL0 " << info.gridReal.l0 - << " scaleRealYtoGrid " << info.gridReal.yScale << " scaleRealLtoGrid " << info.gridReal.lScale + << " gridY0 " << info.gridMeasured.getY0() << " gridZ0 " << info.gridMeasured.getZ0() + << " scaleYtoGrid " << info.gridMeasured.getYscale() << " scaleLtoGrid " << info.gridMeasured.getZscale() + << " gridRealY0 " << info.gridReal.getY0() << " gridRealZ0 " << info.gridReal.getZ0() + << " scaleRealYtoGrid " << info.gridReal.getYscale() << " scaleRealLtoGrid " << info.gridReal.getZscale() << std::endl; } } @@ -379,7 +379,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* const auto& gridY = corr.getSpline(iSector, iRow).getGridX1(); const auto& gridZ = corr.getSpline(iSector, iRow).getGridX2(); if (iSector == 0 && iRow == 0) { - std::cout << "spline scenario " << corr.getRowInfo(iRow).splineScenarioID << std::endl; + std::cout << "spline scenario " << corr.getSectorRowInfo(iSector, iRow).splineScenarioID << std::endl; std::cout << "spline grid Y: u = " << 0 << ".." << gridY.getUmax() << ", x = " << gridY.getXmin() << ".." << gridY.getXmax() << std::endl; std::cout << "spline grid Z: u = " << 0 << ".." << gridZ.getUmax() << ", x = " << gridZ.getXmin() << ".." << gridZ.getXmax() << std::endl; } From 19120c81ab88c5be50b5e32acfde98500a602ab7 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Wed, 20 Aug 2025 07:50:56 +0000 Subject: [PATCH 512/701] TPC Splines: correct biased voxels; features for debugging --- .../TPCFastSpaceChargeCorrectionHelper.h | 32 +- .../TPCFastSpaceChargeCorrectionHelper.cxx | 75 ++- .../macro/TPCFastTransformInit.C | 521 +++++++++++------- 3 files changed, 419 insertions(+), 209 deletions(-) diff --git a/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h b/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h index e8afd9be97d5f..40c5634b4f1e8 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h @@ -90,11 +90,16 @@ class TPCFastSpaceChargeCorrectionHelper /// \param voxResTreeInverse TTree with inverse voxel residuals /// \param useSmoothed if true, use smoothed residuals /// \param invertSigns if true, invert the signs of the residuals + /// \param fitPointsDirect debug: pointer to the data used for the direct correction + /// \param fitPointsInverse debug: pointer to the data used for the inverse correction /// \return pointer to the created TPCFastSpaceChargeCorrection object /// \note voxel trees wont be changed. They are read as non-const because of the ROOT::TTreeProcessorMT interface + /// std::unique_ptr createFromTrackResiduals( const o2::tpc::TrackResiduals& trackResiduals, TTree* voxResTree, TTree* voxResTreeInverse, // - bool useSmoothed, bool invertSigns); + bool useSmoothed, bool invertSigns, // + TPCFastSpaceChargeCorrectionMap* fitPointsDirect = nullptr, + TPCFastSpaceChargeCorrectionMap* fitPointsInverse = nullptr); /// _______________ Utilities ________________________ @@ -116,10 +121,28 @@ class TPCFastSpaceChargeCorrectionHelper /// \param additionalCorrections vector of pairs of additional corrections and their scaling factors /// \param prn printout flag /// \return main correction merged with additional corrections - void MergeCorrections( + void mergeCorrections( o2::gpu::TPCFastSpaceChargeCorrection& mainCorrection, float scale, const std::vector>& additionalCorrections, bool prn); + /// how far the voxel mean is allowed to be outside of the voxel (1.1 means 10%) + void setVoxelMeanValidityRange(double range) + { + mVoxelMeanValidityRange = range; + } + + double getVoxelMeanValidityRange() const { return mVoxelMeanValidityRange; } + + /// debug: if true, use voxel centers instead of the fitted positions for correction + void setDebugUseVoxelCenters(); + + bool isDebugUseVoxelCenters() const { return mDebugUseVoxelCenters; } + + /// debug: if true, mirror the data from the A side to the C side of the TPC + void setDebugMirrorAdata2C(); + + bool isDebugMirrorAdata2C() const { return mDebugMirrorAdata2C; } + private: /// geometry initialization void initGeometry(); @@ -133,6 +156,11 @@ class TPCFastSpaceChargeCorrectionHelper TPCFastSpaceChargeCorrectionMap mCorrectionMap{0, 0}; + double mVoxelMeanValidityRange{1.1}; ///< debug: how far the voxel mean is allowed to be outside of the voxel (1.1 means 10%) + + bool mDebugUseVoxelCenters{false}; ///< debug: if true, use voxel centers instead of the fitted positions for correction + bool mDebugMirrorAdata2C{false}; ///< debug: if true, mirror the data from the A side to the C side of the TPC + ClassDefNV(TPCFastSpaceChargeCorrectionHelper, 0); }; diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index d2f6cf57b0de7..6122c5717fcbb 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -33,6 +33,7 @@ #include "TTreeReaderValue.h" #include "ROOT/TTreeProcessorMT.hxx" #include +#include using namespace o2::gpu; @@ -381,7 +382,9 @@ void TPCFastSpaceChargeCorrectionHelper::testGeometry(const TPCFastTransformGeo& } std::unique_ptr TPCFastSpaceChargeCorrectionHelper::createFromTrackResiduals( - const o2::tpc::TrackResiduals& trackResiduals, TTree* voxResTree, TTree* voxResTreeInverse, bool useSmoothed, bool invertSigns) + const o2::tpc::TrackResiduals& trackResiduals, TTree* voxResTree, TTree* voxResTreeInverse, bool useSmoothed, bool invertSigns, + TPCFastSpaceChargeCorrectionMap* fitPointsDirect, + TPCFastSpaceChargeCorrectionMap* fitPointsInverse) { // create o2::gpu::TPCFastSpaceChargeCorrection from o2::tpc::TrackResiduals::VoxRes voxel tree @@ -603,6 +606,24 @@ std::unique_ptr TPCFastSpaceChargeCorrect processor.Process(myThread); } + // debug: mirror the data for TPC C side + + if (mDebugMirrorAdata2C) { + for (int iSector = 0; iSector < geo.getNumberOfSectorsA(); iSector++) { + for (int iRow = 0; iRow < nRows; iRow++) { + for (int iy = 0; iy < nY2Xbins; iy++) { + for (int iz = 0; iz < nZ2Xbins; iz++) { + auto& dataA = vSectorData[iSector * nRows + iRow][iy * nZ2Xbins + iz]; + auto& dataC = vSectorData[(iSector + geo.getNumberOfSectorsA()) * nRows + iRow][iy * nZ2Xbins + iz]; + dataC = dataA; // copy the data + dataC.mZ = -dataC.mZ; // mirror the Z coordinate + dataC.mCz = -dataC.mCz; // mirror the Z correction + } + } + } + } + } + for (int iSector = 0; iSector < nSectors; iSector++) { // now process the data row-by-row @@ -623,6 +644,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect { int xBin = iRow; double x = trackResiduals.getX(xBin); // radius of the pad row + double dx = 1. / trackResiduals.getDXI(xBin); bool isDataFound = false; for (int iy = 0; iy < nY2Xbins; iy++) { for (int iz = 0; iz < nZ2Xbins; iz++) { @@ -642,13 +664,29 @@ std::unique_ptr TPCFastSpaceChargeCorrect if (data.mNentries > 0) { // voxel contains data vox.mSmoothingStep = 0; // take original data isDataFound = true; - if (fabs(x - data.mX) > 1. || fabs(vox.mY - data.mY) > 5. || fabs(vox.mZ - data.mZ) > 5.) { - std::cout << directionName << ": fitted voxel is too far from the nominal position: " - << " sector " << iSector << " row " << iRow - << " center x " << x << " y " << vox.mY << " z " << vox.mZ - << " fitted x " << data.mX << " y " << data.mY << " z " << data.mZ - << std::endl; + + // correct the mean position if it is outside the voxel + std::stringstream msg; + if (fabs(x - data.mX) > mVoxelMeanValidityRange * dx / 2.) { + msg << "\n x: center " << x << " dx " << data.mX - x << " half bin size: " << dx / 2; + } + + if (fabs(vox.mY - data.mY) > mVoxelMeanValidityRange * vox.mDy / 2.) { + msg << "\n y: center " << vox.mY << " dy " << data.mY - vox.mY << " half bin size: " << vox.mDy / 2; + data.mY = vox.mY; + } + + if (fabs(vox.mZ - data.mZ) > mVoxelMeanValidityRange * vox.mDz / 2.) { + msg << "\n z: center " << vox.mZ << " dz " << data.mZ - vox.mZ << " half bin size: " << vox.mDz / 2; + data.mZ = vox.mZ; } + + if (!msg.str().empty()) { + LOG(warning) << directionName << " correction: fitted voxel position is outside the voxel: " + << " sector " << iSector << " row " << iRow << " bin: " << iy << " " << iz + << msg.str(); + } + } else { // no data, take voxel center position data.mCx = 0.; data.mCy = 0.; @@ -658,7 +696,7 @@ std::unique_ptr TPCFastSpaceChargeCorrect data.mZ = vox.mZ; vox.mSmoothingStep = 100; // fill this data point with smoothed values from the neighbours } - if (0) { // debug: always use voxel center instead of the mean position + if (mDebugUseVoxelCenters) { // debug: always use voxel center instead of the mean position data.mY = vox.mY; data.mZ = vox.mZ; } @@ -809,6 +847,13 @@ std::unique_ptr TPCFastSpaceChargeCorrect TStopwatch watch4; + if (!processingInverseCorrection && fitPointsDirect) { + *fitPointsDirect = helper->getCorrectionMap(); + } + if (processingInverseCorrection && fitPointsInverse) { + *fitPointsInverse = helper->getCorrectionMap(); + } + helper->fillSpaceChargeCorrectionFromMap(correction, processingInverseCorrection); LOG(info) << "fast space charge correction helper: creation from the data map took " << watch4.RealTime() << "s"; @@ -956,7 +1001,7 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector>& additionalCorrections, bool /*prn*/) { @@ -1099,5 +1144,17 @@ void TPCFastSpaceChargeCorrectionHelper::MergeCorrections( LOGP(info, "Merge of corrections tooks: {}s", duration); } +void TPCFastSpaceChargeCorrectionHelper::setDebugUseVoxelCenters() +{ + LOG(info) << "fast space charge correction helper: use voxel centers for correction"; + mDebugUseVoxelCenters = true; +} + +void TPCFastSpaceChargeCorrectionHelper::setDebugMirrorAdata2C() +{ + LOG(info) << "fast space charge correction helper: mirror A data to C data"; + mDebugMirrorAdata2C = true; +} + } // namespace tpc } // namespace o2 diff --git a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C index 974582792266b..50b667bb3e023 100644 --- a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C +++ b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C @@ -56,13 +56,14 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* To visiualise the results: root -l transformDebug.root - corr->Draw("cx:y:z","iSector==0&&iRow==10","") - grid->Draw("cx:y:z","iSector==0&&iRow==10","same") - vox->Draw("vx:y:z","iSector==0&&iRow==10","same") - corrvox->Draw("cx:y:z","iSector==0&&iRow==10","same") - points->Draw("px:y:z","iSector==0&&iRow==10","same") + all->Draw("cx:y:z","sec==0&&iRow==10","") + grid->Draw("cx:y:z","sec==0&&iRow==10","same") + vox->Draw("vx:y:z","sec==0&&iRow==10","same") + points->Draw("px:y:z","sec==0&&row==10","same") */ + const bool debugMirrorAdata2C = 0; + if (gSystem->AccessPathName(fileName)) { std::cout << " input file " << fileName << " does not exist!" << std::endl; return; @@ -152,7 +153,15 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* corrHelper->setNthreadsToMaximum(); corrHelper->setNthreads(1); - auto corrPtr = corrHelper->createFromTrackResiduals(trackResiduals, voxResTree, voxResTreeInverse, useSmoothed, invertSigns); + if (debugMirrorAdata2C) { + corrHelper->setDebugMirrorAdata2C(); + } + // corrHelper->setDebugUseVoxelCenters(); + + o2::gpu::TPCFastSpaceChargeCorrectionMap mapDirect(0, 0), mapInverse(0, 0); + + auto corrPtr = corrHelper->createFromTrackResiduals(trackResiduals, voxResTree, voxResTreeInverse, useSmoothed, invertSigns, + &mapDirect, &mapInverse); std::unique_ptr fastTransform( helper->create(0, *corrPtr)); @@ -199,15 +208,6 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* o2::gpu::TPCFastSpaceChargeCorrection& corr = fastTransform->getCorrection(); - // the difference - - double maxDiff[3] = {0., 0., 0.}; - int32_t maxDiffSector[3] = {0, 0, 0}; - int32_t maxDiffRow[3] = {0, 0, 0}; - - double sumDiff[3] = {0., 0., 0.}; - int64_t nDiff = 0; - // a debug file with some NTuples TDirectory* currDir = gDirectory; @@ -220,46 +220,77 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* // measured x,y,z; corrections cx,cy,cz from the measured to the real x,y,z; // inverse corrections ix,iy,iz at the real position (x+cx,y+cy,z+cz) // ideally, ix = cx, iy = cy, iz = cz - TNtuple* debugCorr = new TNtuple("corr", "corr", "iSector:iRow:x:y:z:cx:cy:cz:ix:iy:iz"); + TNtuple* ntAll = new TNtuple("all", "all", + debugMirrorAdata2C ? "sec:row:x:y:z:cx:cy:cz:ix:iy:iz:cxC:cyC:czC:ixC:iyC:izC" + : "sec:row:x:y:z:cx:cy:cz:ix:iy:iz"); - debugCorr->SetMarkerStyle(8); - debugCorr->SetMarkerSize(0.1); - debugCorr->SetMarkerColor(kBlack); + ntAll->SetMarkerStyle(8); + ntAll->SetMarkerSize(0.1); + ntAll->SetMarkerColor(kBlack); - // ntuple with the input data: voxels and corrections debugFile->cd(); - TNtuple* debugVox = - new TNtuple("vox", "vox", "iSector:iRow:n:x:y:z:vx:vy:vz"); + TNtuple* ntInvAll = new TNtuple("invall", "invall", + debugMirrorAdata2C ? "sec:row:x:y:z:cx:cy:cz:cxC:cyC:czC" + : "sec:row:x:y:z:cx:cy:cz"); - debugVox->SetMarkerStyle(8); - debugVox->SetMarkerSize(0.8); - debugVox->SetMarkerColor(kBlue); + ntInvAll->SetMarkerStyle(8); + ntInvAll->SetMarkerSize(0.1); + ntInvAll->SetMarkerColor(kBlack); // duplicate of debugVox + the spline data at voxels in a different color debugFile->cd(); - TNtuple* debugCorrVox = - new TNtuple("corrvox", "corrvox", "iSector:iRow:n:x:y:z:vx:vy:vz:cx:cy:cz:ix:iy:iz"); + TNtuple* ntVox = + new TNtuple("vox", "vox", + debugMirrorAdata2C ? "sec:row:n:x:y:z:vx:vy:vz:cx:cy:cz:ix:iy:iz:cxC:cyC:czC:ixC:iyC:izC" + : "sec:row:n:x:y:z:vx:vy:vz:cx:cy:cz:ix:iy:iz"); - debugCorrVox->SetMarkerStyle(8); - debugCorrVox->SetMarkerSize(0.8); - debugCorrVox->SetMarkerColor(kMagenta); + ntVox->SetMarkerStyle(8); + ntVox->SetMarkerSize(0.8); + ntVox->SetMarkerColor(kMagenta); + + // duplicate of debugVox + the spline data at voxels in a different color + debugFile->cd(); + TNtuple* ntInvVox = + new TNtuple("invvox", "invvox", + debugMirrorAdata2C ? "sec:row:n:x:y:z:vx:vy:vz:cx:cy:cz:cxC:cyC:czC" + : "sec:row:n:x:y:z:vx:vy:vz:cx:cy:cz"); + + ntInvVox->SetMarkerStyle(8); + ntInvVox->SetMarkerSize(0.8); + ntInvVox->SetMarkerColor(kMagenta); + + // corrections at the spline grid points + debugFile->cd(); + TNtuple* ntGrid = new TNtuple("grid", "grid", "sec:row:x:y:z:cx:cy:cz:ix:iy:iz"); + + ntGrid->SetMarkerStyle(8); + ntGrid->SetMarkerSize(1.2); + ntGrid->SetMarkerColor(kBlack); // corrections at the spline grid points debugFile->cd(); - TNtuple* debugGrid = new TNtuple("grid", "grid", "iSector:iRow:x:y:z:cx:cy:cz:ix:iy:iz"); + TNtuple* ntInvGrid = new TNtuple("invgrid", "invgrid", "sec:row:x:y:z:cx:cy:cz"); - debugGrid->SetMarkerStyle(8); - debugGrid->SetMarkerSize(1.2); - debugGrid->SetMarkerColor(kBlack); + ntInvGrid->SetMarkerStyle(8); + ntInvGrid->SetMarkerSize(1.2); + ntGrid->SetMarkerColor(kBlack); // ntuple with data points created from voxels (with the data smearing, extension to the edges etc.) debugFile->cd(); - TNtuple* debugPoints = - new TNtuple("points", "points", "iSector:iRow:x:y:z:px:py:pz:cx:cy:cz"); + TNtuple* ntFitPoints = + new TNtuple("fitpoints", "fit points", "sec:row:x:y:z:px:py:pz:cx:cy:cz"); - debugPoints->SetMarkerStyle(8); - debugPoints->SetMarkerSize(0.4); - debugPoints->SetMarkerColor(kRed); + ntFitPoints->SetMarkerStyle(8); + ntFitPoints->SetMarkerSize(0.4); + ntFitPoints->SetMarkerColor(kRed); + + debugFile->cd(); + TNtuple* ntInvFitPoints = + new TNtuple("invfitpoints", "fit points", "sec:row:x:y:z:px:py:pz:cx:cy:cz"); + + ntInvFitPoints->SetMarkerStyle(8); + ntInvFitPoints->SetMarkerSize(0.4); + ntInvFitPoints->SetMarkerColor(kRed); currDir->cd(); @@ -269,224 +300,318 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* const o2::gpu::TPCFastTransformGeo& geo = helper->getGeometry(); + auto getInvCorrections = [&](int iSector, int iRow, float realY, float realZ, float& ix, float& iy, float& iz) { + // get the inverse corrections ix, iy, iz at x,y,z + ix = corr.getCorrectionXatRealYZ(iSector, iRow, realY, realZ); + std::tie(iy, iz) = corr.getCorrectionYZatRealYZ(iSector, iRow, realY, realZ); + }; + auto getAllCorrections = [&](int iSector, int iRow, float y, float z, float& cx, float& cy, float& cz, float& ix, float& iy, float& iz) { // get the corrections cx,cy,cz at x,y,z std::tie(cx, cy, cz) = corr.getCorrectionLocal(iSector, iRow, y, z); - float realY = y + cy; - float realZ = z + cz; - ix = corr.getCorrectionXatRealYZ(iSector, iRow, realY, realZ); - std::tie(iy, iz) = corr.getCorrectionYZatRealYZ(iSector, iRow, realY, realZ); + getInvCorrections(iSector, iRow, y + cy, z + cz, ix, iy, iz); }; - o2::tpc::TrackResiduals::VoxRes* v = nullptr; - TBranch* branch = voxResTree->GetBranch("voxRes"); - branch->SetAddress(&v); - branch->SetAutoDelete(kTRUE); + for (int direction = 0; direction < 2; direction++) { // 0 - normal, 1 - inverse + + TTree* currentTree = (direction == 0) ? voxResTree : voxResTreeInverse; + if (!currentTree) { + std::cout << "tree voxResTree does not exist!" << std::endl; + return; + } - int32_t iSectorLast = -1; - int32_t iRowLast = -1; + o2::tpc::TrackResiduals::VoxRes* v = nullptr; + TBranch* branch = currentTree->GetBranch("voxRes"); + branch->SetAddress(&v); + branch->SetAutoDelete(kTRUE); - std::cout << "fill debug ntuples at voxels ..." << std::endl; + int32_t iSectorLast = -1; + int32_t iRowLast = -1; - for (int32_t iVox = 0; iVox < voxResTree->GetEntriesFast(); iVox++) { + // the difference - voxResTree->GetEntry(iVox); + double maxDiff[3] = {0., 0., 0.}; + int32_t maxDiffSector[3] = {0, 0, 0}; + int32_t maxDiffRow[3] = {0, 0, 0}; - float voxEntries = v->stat[o2::tpc::TrackResiduals::VoxV]; + double sumDiff[3] = {0., 0., 0.}; + int64_t nDiff = 0; - int32_t xBin = - v->bvox[o2::tpc::TrackResiduals::VoxX]; // bin number in x (= pad row) + std::cout << "fill debug ntuples at voxels ..." << std::endl; - int32_t y2xBin = - v->bvox[o2::tpc::TrackResiduals::VoxF]; // bin number in y/x 0..14 + for (int32_t iVox = 0; iVox < currentTree->GetEntriesFast(); iVox++) { - int32_t z2xBin = - v->bvox[o2::tpc::TrackResiduals::VoxZ]; // bin number in z/x 0..4 + currentTree->GetEntry(iVox); - int32_t iSector = (int32_t)v->bsec; - int32_t iRow = (int32_t)xBin; + float voxEntries = v->stat[o2::tpc::TrackResiduals::VoxV]; - iSectorLast = iSector; - iRowLast = iRow; + int32_t xBin = + v->bvox[o2::tpc::TrackResiduals::VoxX]; // bin number in x (= pad row) - double x = trackResiduals.getX(xBin); // radius of the pad row + int32_t y2xBin = + v->bvox[o2::tpc::TrackResiduals::VoxF]; // bin number in y/x 0..14 - double y2x = trackResiduals.getY2X( - xBin, y2xBin); // y/x coordinate of the bin ~-0.15 ... 0.15 + int32_t z2xBin = + v->bvox[o2::tpc::TrackResiduals::VoxZ]; // bin number in z/x 0..4 - double z2x = - trackResiduals.getZ2X(z2xBin); // z/x coordinate of the bin 0.1 .. 0.9 + int32_t iSector = (int32_t)v->bsec; + int32_t iRow = (int32_t)xBin; - double y = x * y2x; - double z = x * z2x; + iSectorLast = iSector; + iRowLast = iRow; - if (iSector >= geo.getNumberOfSectorsA()) { - z = -z; - } + double x = trackResiduals.getX(xBin); // radius of the pad row - double correctionX = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResX] : v->D[o2::tpc::TrackResiduals::ResX]; - double correctionY = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResY] : v->D[o2::tpc::TrackResiduals::ResY]; - double correctionZ = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResZ] : v->D[o2::tpc::TrackResiduals::ResZ]; + double y2x = trackResiduals.getY2X( + xBin, y2xBin); // y/x coordinate of the bin ~-0.15 ... 0.15 - if (invertSigns) { - correctionX *= -1.; - correctionY *= -1.; - correctionZ *= -1.; - } + double z2x = + trackResiduals.getZ2X(z2xBin); // z/x coordinate of the bin 0.1 .. 0.9 - if (voxEntries > 0.) { // use mean statistical positions instead of the bin centers: - y = x * v->stat[o2::tpc::TrackResiduals::VoxF]; - z = x * v->stat[o2::tpc::TrackResiduals::VoxZ]; - } + double y = x * y2x; + double z = x * z2x; + + double correctionX = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResX] : v->D[o2::tpc::TrackResiduals::ResX]; + double correctionY = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResY] : v->D[o2::tpc::TrackResiduals::ResY]; + double correctionZ = useSmoothed ? v->DS[o2::tpc::TrackResiduals::ResZ] : v->D[o2::tpc::TrackResiduals::ResZ]; + + double voxelSizeY = x / trackResiduals.getDY2XI(xBin, y2xBin); + double voxelSizeZ = x * trackResiduals.getDZ2X(z2xBin); + + if (invertSigns) { + correctionX *= -1.; + correctionY *= -1.; + correctionZ *= -1.; + } - float cx, cy, cz, ix, iy, iz; - getAllCorrections(iSector, iRow, y, z, cx, cy, cz, ix, iy, iz); + if (!corrHelper->isDebugUseVoxelCenters()) { + if (voxEntries > 0.) { + // use mean statistical positions instead of the bin centers, unless they are wrong + double yFit = x * v->stat[o2::tpc::TrackResiduals::VoxF]; + if (fabs(yFit - y) <= corrHelper->getVoxelMeanValidityRange() * voxelSizeY / 2.) { + y = yFit; + } + double zFit = x * v->stat[o2::tpc::TrackResiduals::VoxZ]; + if (fabs(zFit - z) <= corrHelper->getVoxelMeanValidityRange() * voxelSizeZ / 2.) { + z = zFit; + } + } + } - if (voxEntries >= 1.) { - double d[3] = {cx - correctionX, cy - correctionY, cz - correctionZ}; + int mirrorSector = iSector + geo.getNumberOfSectorsA(); - for (int32_t i = 0; i < 3; i++) { - if (fabs(maxDiff[i]) < fabs(d[i])) { - maxDiff[i] = d[i]; - maxDiffSector[i] = iSector; - maxDiffRow[i] = iRow; - // std::cout << " sector " << iSector << " row " << iRow << " xyz " << i - // << " diff " << d[i] << " entries " << voxEntries << " y " << y2xBin << " z " << z2xBin << std::endl; + if (iSector >= geo.getNumberOfSectorsA()) { + z = -z; + mirrorSector = iSector - geo.getNumberOfSectorsA(); + } + + float cx{0.f}, cy{0.f}, cz{0.f}, ix{0.f}, iy{0.f}, iz{0.f}; + float cxC{0.f}, cyC{0.f}, czC{0.f}, ixC{0.f}, iyC{0.f}, izC{0.f}; + if (direction == 0) { + getAllCorrections(iSector, iRow, y, z, cx, cy, cz, ix, iy, iz); + if (debugMirrorAdata2C) { + getAllCorrections(mirrorSector, iRow, y, -z, cxC, cyC, czC, ixC, iyC, izC); } - sumDiff[i] += d[i] * d[i]; + float ntEntry[] = {(float)iSector, (float)iRow, voxEntries, + (float)x, (float)y, (float)z, + (float)correctionX, (float)correctionY, (float)correctionZ, + (float)cx, (float)cy, (float)cz, + (float)ix, (float)iy, (float)iz, + (float)cxC, (float)cyC, (float)czC, (float)ixC, (float)iyC, (float)izC}; + + // fill the ntuple with the correction at the voxel + ntVox->Fill(ntEntry); + } else { + getInvCorrections(iSector, iRow, y, z, cx, cy, cz); + if (debugMirrorAdata2C) { + getInvCorrections(mirrorSector, iRow, y, -z, cxC, cyC, czC); + } + float ntEntry[] = {(float)iSector, (float)iRow, voxEntries, + (float)x, (float)y, (float)z, + (float)correctionX, (float)correctionY, (float)correctionZ, + (float)cx, (float)cy, (float)cz, + (float)cxC, (float)cyC, (float)czC}; + // fill the ntuple with the correction at the voxel + ntInvVox->Fill(ntEntry); + } + + if (voxEntries >= 1.) { + double d[3] = {cx - correctionX, cy - correctionY, cz - correctionZ}; + + for (int32_t i = 0; i < 3; i++) { + if (fabs(maxDiff[i]) < fabs(d[i])) { + maxDiff[i] = d[i]; + maxDiffSector[i] = iSector; + maxDiffRow[i] = iRow; + // std::cout << " sector " << iSector << " row " << iRow << " xyz " << i + // << " diff " << d[i] << " entries " << voxEntries << " y " << y2xBin << " z " << z2xBin << std::endl; + } + sumDiff[i] += d[i] * d[i]; + } + nDiff++; } - nDiff++; } - debugVox->Fill(iSector, iRow, voxEntries, x, y, z, correctionX, correctionY, correctionZ); + std::cout + << "fill debug ntuples everywhere .." << std::endl; - debugCorrVox->Fill(iSector, iRow, voxEntries, x, y, z, correctionX, correctionY, correctionZ, - cx, cy, cz, ix, iy, iz); - } + for (int32_t iSector = 0; iSector < geo.getNumberOfSectors(); iSector++) { + // for (int32_t iSector = 0; iSector < 1; iSector++) { + std::cout << "debug ntules for sector " << iSector << std::endl; - std::cout - << "fill debug ntuples everywhere .." << std::endl; + int mirrorSector = (iSector >= geo.getNumberOfSectorsA()) ? iSector - geo.getNumberOfSectorsA() : iSector + geo.getNumberOfSectorsA(); - for (int32_t iSector = 0; iSector < geo.getNumberOfSectors(); iSector++) { - // for (int32_t iSector = 0; iSector < 1; iSector++) { - std::cout << "debug ntules for sector " << iSector << std::endl; - for (int32_t iRow = 0; iRow < geo.getNumberOfRows(); iRow++) { + for (int32_t iRow = 0; iRow < geo.getNumberOfRows(); iRow++) { - double x = geo.getRowInfo(iRow).x; + double x = geo.getRowInfo(iRow).x; - // the spline grid + // the spline grid - const auto& gridY = corr.getSpline(iSector, iRow).getGridX1(); - const auto& gridZ = corr.getSpline(iSector, iRow).getGridX2(); - if (iSector == 0 && iRow == 0) { - std::cout << "spline scenario " << corr.getSectorRowInfo(iSector, iRow).splineScenarioID << std::endl; - std::cout << "spline grid Y: u = " << 0 << ".." << gridY.getUmax() << ", x = " << gridY.getXmin() << ".." << gridY.getXmax() << std::endl; - std::cout << "spline grid Z: u = " << 0 << ".." << gridZ.getUmax() << ", x = " << gridZ.getXmin() << ".." << gridZ.getXmax() << std::endl; - } + const auto& gridY = corr.getSpline(iSector, iRow).getGridX1(); + const auto& gridZ = corr.getSpline(iSector, iRow).getGridX2(); + if (iSector == 0 && iRow == 0) { + std::cout << "spline scenario " << corr.getSectorRowInfo(iSector, iRow).splineScenarioID << std::endl; + std::cout << "spline grid Y: u = " << 0 << ".." << gridY.getUmax() << ", x = " << gridY.getXmin() << ".." << gridY.getXmax() << std::endl; + std::cout << "spline grid Z: u = " << 0 << ".." << gridZ.getUmax() << ", x = " << gridZ.getXmin() << ".." << gridZ.getXmax() << std::endl; + } - // the correction - { - std::vector points[2], knots[2]; + // the correction + { + std::vector points[2], knots[2]; - auto [yMin, yMax] = geo.getRowInfo(iRow).getYrange(); - auto [zMin, zMax] = geo.getZrange(iSector); + auto [yMin, yMax] = geo.getRowInfo(iRow).getYrange(); + auto [zMin, zMax] = geo.getZrange(iSector); - points[0].push_back(yMin); - points[0].push_back(yMax); - points[1].push_back(zMin); - points[1].push_back(zMax); + points[0].push_back(yMin); + points[0].push_back(yMax); + points[1].push_back(zMin); + points[1].push_back(zMax); - for (int32_t iu = 0; iu < gridY.getNumberOfKnots(); iu++) { - auto [y, z] = corr.convGridToLocal(iSector, iRow, gridY.getKnot(iu).getU(), 0.); - knots[0].push_back(y); - points[0].push_back(y); - } - for (int32_t iv = 0; iv < gridZ.getNumberOfKnots(); iv++) { - auto [y, z] = corr.convGridToLocal(iSector, iRow, 0., gridZ.getKnot(iv).getU()); - knots[1].push_back(z); - points[1].push_back(z); - } + for (int32_t iu = 0; iu < gridY.getNumberOfKnots(); iu++) { + auto [y, z] = corr.convGridToLocal(iSector, iRow, gridY.getKnot(iu).getU(), 0.); + knots[0].push_back(y); + points[0].push_back(y); + } + for (int32_t iv = 0; iv < gridZ.getNumberOfKnots(); iv++) { + auto [y, z] = corr.convGridToLocal(iSector, iRow, 0., gridZ.getKnot(iv).getU()); + knots[1].push_back(z); + points[1].push_back(z); + } - for (int32_t iyz = 0; iyz <= 1; iyz++) { - std::sort(knots[iyz].begin(), knots[iyz].end()); - std::sort(points[iyz].begin(), points[iyz].end()); - int32_t n = points[iyz].size(); - for (int32_t i = 0; i < n - 1; i++) { - double d = (points[iyz][i + 1] - points[iyz][i]) / 10.; - for (int32_t ii = 1; ii < 10; ii++) { - points[iyz].push_back(points[iyz][i] + d * ii); + for (int32_t iyz = 0; iyz <= 1; iyz++) { + std::sort(knots[iyz].begin(), knots[iyz].end()); + std::sort(points[iyz].begin(), points[iyz].end()); + int32_t n = points[iyz].size(); + for (int32_t i = 0; i < n - 1; i++) { + double d = (points[iyz][i + 1] - points[iyz][i]) / 10.; + for (int32_t ii = 1; ii < 10; ii++) { + points[iyz].push_back(points[iyz][i] + d * ii); + } } + std::sort(points[iyz].begin(), points[iyz].end()); } - std::sort(points[iyz].begin(), points[iyz].end()); - } - for (int32_t iter = 0; iter < 2; iter++) { - std::vector& py = ((iter == 0) ? knots[0] : points[0]); - std::vector& pz = ((iter == 0) ? knots[1] : points[1]); - for (uint32_t iu = 0; iu < py.size(); iu++) { - for (uint32_t iv = 0; iv < pz.size(); iv++) { - float y = py[iu]; - float z = pz[iv]; - float cx, cy, cz, ix, iy, iz; - getAllCorrections(iSector, iRow, y, z, cx, cy, cz, ix, iy, iz); - if (iter == 0) { - debugGrid->Fill(iSector, iRow, x, y, z, cx, cy, cz, ix, iy, iz); - } else { - debugCorr->Fill(iSector, iRow, x, y, z, cx, cy, cz, ix, iy, iz); + for (int32_t iter = 0; iter < 2; iter++) { + std::vector& py = ((iter == 0) ? knots[0] : points[0]); + std::vector& pz = ((iter == 0) ? knots[1] : points[1]); + for (uint32_t iu = 0; iu < py.size(); iu++) { + for (uint32_t iv = 0; iv < pz.size(); iv++) { + float y = py[iu]; + float z = pz[iv]; + float cx{0}, cy{0}, cz{0}, ix{0}, iy{0}, iz{0}; + float cxC{0}, cyC{0}, czC{0}, ixC{0}, iyC{0}, izC{0}; + if (direction == 0) { + getAllCorrections(iSector, iRow, y, z, cx, cy, cz, ix, iy, iz); + if (debugMirrorAdata2C) { + getAllCorrections(mirrorSector, iRow, y, -z, cxC, cyC, czC, ixC, iyC, izC); + } + if (iter == 0) { + ntGrid->Fill(iSector, iRow, x, y, z, cx, cy, cz, ix, iy, iz); + } else { + float ntEntry[] = {(float)iSector, (float)iRow, (float)x, y, z, + cx, cy, cz, ix, iy, iz, + cxC, cyC, czC, ixC, iyC, izC}; + ntAll->Fill(ntEntry); + } + } else { + getInvCorrections(iSector, iRow, y, z, cx, cy, cz); + if (debugMirrorAdata2C) { + getInvCorrections(mirrorSector, iRow, y, -z, cxC, cyC, czC); + } + if (iter == 0) { + ntInvGrid->Fill(iSector, iRow, x, y, z, cx, cy, cz); + } else { + float ntEntry[] = {(float)iSector, (float)iRow, (float)x, y, z, + cx, cy, cz, + cxC, cyC, czC}; + ntInvAll->Fill(ntEntry); + } + } } } } } - } - - // the data points used in spline fit - // (they are kept in - // TPCFastTransformHelperO2::instance()->getCorrectionMap() ) - - o2::gpu::TPCFastSpaceChargeCorrectionMap& map = - corrHelper->getCorrectionMap(); - auto& points = map.getPoints(iSector, iRow); - for (uint32_t ip = 0; ip < points.size(); ip++) { - auto point = points[ip]; - float y = point.mY; - float z = point.mZ; - float correctionX = point.mDx; - float correctionY = point.mDy; - float correctionZ = point.mDz; - - auto [cx, cy, cz] = - corr.getCorrectionLocal(iSector, iRow, y, z); - - debugPoints->Fill(iSector, iRow, x, y, z, correctionX, correctionY, - correctionZ, cx, cy, cz); + // the data points used in spline fit + // (they are kept in + // TPCFastTransformHelperO2::instance()->getCorrectionMap() ) + + o2::gpu::TPCFastSpaceChargeCorrectionMap& map = (direction == 0 ? mapDirect : mapInverse); + + auto& points = map.getPoints(iSector, iRow); + + for (uint32_t ip = 0; ip < points.size(); ip++) { + auto point = points[ip]; + float y = point.mY; + float z = point.mZ; + float correctionX = point.mDx; + float correctionY = point.mDy; + float correctionZ = point.mDz; + if (direction == 0) { + auto [cx, cy, cz] = + corr.getCorrectionLocal(iSector, iRow, y, z); + ntFitPoints->Fill(iSector, iRow, x, y, z, correctionX, correctionY, + correctionZ, cx, cy, cz); + } else { + float cx = + corr.getCorrectionXatRealYZ(iSector, iRow, y, z); + auto [cy, cz] = + corr.getCorrectionYZatRealYZ(iSector, iRow, y, z); + ntInvFitPoints->Fill(iSector, iRow, x, y, z, correctionX, correctionY, + correctionZ, cx, cy, cz); + } + } } } - } - for (int32_t i = 0; i < 3; i++) { - sumDiff[i] = sqrt(sumDiff[i]) / nDiff; - } + for (int32_t i = 0; i < 3; i++) { + sumDiff[i] = sqrt(sumDiff[i]) / nDiff; + } - std::cout << "Max difference in x : " << maxDiff[0] << " at Sector " - << maxDiffSector[0] << " row " << maxDiffRow[0] << std::endl; + std::cout << "Max difference in x : " << maxDiff[0] << " at Sector " + << maxDiffSector[0] << " row " << maxDiffRow[0] << std::endl; - std::cout << "Max difference in y : " << maxDiff[1] << " at Sector " - << maxDiffSector[1] << " row " << maxDiffRow[1] << std::endl; + std::cout << "Max difference in y : " << maxDiff[1] << " at Sector " + << maxDiffSector[1] << " row " << maxDiffRow[1] << std::endl; - std::cout << "Max difference in z : " << maxDiff[2] << " at Sector " - << maxDiffSector[2] << " row " << maxDiffRow[2] << std::endl; + std::cout << "Max difference in z : " << maxDiff[2] << " at Sector " + << maxDiffSector[2] << " row " << maxDiffRow[2] << std::endl; - std::cout << "Mean difference in x,y,z : " << sumDiff[0] << " " << sumDiff[1] - << " " << sumDiff[2] << std::endl; + std::cout << "Mean difference in x,y,z : " << sumDiff[0] << " " << sumDiff[1] + << " " << sumDiff[2] << std::endl; + } // direction corr.testInverse(true); debugFile->cd(); - debugCorr->Write(); - debugVox->Write(); - debugCorrVox->Write(); - debugGrid->Write(); - debugPoints->Write(); + ntAll->Write(); + ntVox->Write(); + ntGrid->Write(); + ntFitPoints->Write(); + ntInvAll->Write(); + ntInvVox->Write(); + ntInvGrid->Write(); + ntInvFitPoints->Write(); + debugFile->Close(); } From 954b28c2823ac6017a38e5e7489f792585e04d14 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Wed, 27 Aug 2025 17:04:52 +0000 Subject: [PATCH 513/701] TPC Splines: replace std::tuple by std::array --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 8 +-- GPU/TPCFastTransformation/Spline1DSpec.h | 8 +-- GPU/TPCFastTransformation/Spline2DSpec.cxx | 2 - .../TPCFastSpaceChargeCorrection.h | 44 ++++++++--------- GPU/TPCFastTransformation/TPCFastTransform.h | 49 +++++++++++++------ .../TPCFastTransformGeo.h | 15 +++--- .../macro/TPCFastTransformInit.C | 9 +++- 7 files changed, 78 insertions(+), 57 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index 6122c5717fcbb..9232598e6a35e 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -668,22 +668,22 @@ std::unique_ptr TPCFastSpaceChargeCorrect // correct the mean position if it is outside the voxel std::stringstream msg; if (fabs(x - data.mX) > mVoxelMeanValidityRange * dx / 2.) { - msg << "\n x: center " << x << " dx " << data.mX - x << " half bin size: " << dx / 2; + msg << "\n x: center " << x << " dx " << data.mX - x << " half bin size " << dx / 2; } if (fabs(vox.mY - data.mY) > mVoxelMeanValidityRange * vox.mDy / 2.) { - msg << "\n y: center " << vox.mY << " dy " << data.mY - vox.mY << " half bin size: " << vox.mDy / 2; + msg << "\n y: center " << vox.mY << " dy " << data.mY - vox.mY << " half bin size " << vox.mDy / 2; data.mY = vox.mY; } if (fabs(vox.mZ - data.mZ) > mVoxelMeanValidityRange * vox.mDz / 2.) { - msg << "\n z: center " << vox.mZ << " dz " << data.mZ - vox.mZ << " half bin size: " << vox.mDz / 2; + msg << "\n z: center " << vox.mZ << " dz " << data.mZ - vox.mZ << " half bin size " << vox.mDz / 2; data.mZ = vox.mZ; } if (!msg.str().empty()) { LOG(warning) << directionName << " correction: fitted voxel position is outside the voxel: " - << " sector " << iSector << " row " << iRow << " bin: " << iy << " " << iz + << " sector " << iSector << " row " << iRow << " bin y " << iy << " bin z " << iz << msg.str(); } diff --git a/GPU/TPCFastTransformation/Spline1DSpec.h b/GPU/TPCFastTransformation/Spline1DSpec.h index 1c591a957847d..28be5dd20e235 100644 --- a/GPU/TPCFastTransformation/Spline1DSpec.h +++ b/GPU/TPCFastTransformation/Spline1DSpec.h @@ -342,7 +342,7 @@ class Spline1DSpec : public Spline1DContainer } template - GPUd() std::tuple getSderivativesOverParsAtU(const Knot& knotL, DataT u) const + GPUd() std::array getSderivativesOverParsAtU(const Knot& knotL, DataT u) const { /// Get derivatives of the interpolated value {S(u): 1D -> nYdim} at the segment [knotL, next knotR] /// over the spline parameters Sl(eft), Sr(ight) and the slopes Dl, Dr @@ -364,11 +364,11 @@ class Spline1DSpec : public Spline1DContainer T dSdDl = vm1 * a; T dSdDr = v * a; // S(u) = dSdSl * Sl + dSdSr * Sr + dSdDl * Dl + dSdDr * Dr; - return std::make_tuple(dSdSl, dSdDl, dSdSr, dSdDr); + return {dSdSl, dSdDl, dSdSr, dSdDr}; } template - GPUd() std::tuple getSDderivativesOverParsAtU(const Knot& knotL, DataT u) const + GPUd() std::array getSDderivativesOverParsAtU(const Knot& knotL, DataT u) const { /// Get derivatives of the interpolated value {S(u): 1D -> nYdim} at the segment [knotL, next knotR] /// over the spline values Sl, Sr and the slopes Dl, Dr @@ -397,7 +397,7 @@ class Spline1DSpec : public Spline1DContainer T dDdDr = v * (v + vm1 + vm1); // S(u) = dSdSl * Sl + dSdSr * Sr + dSdDl * Dl + dSdDr * Dr; // D(u) = dS(u)/du = dDdSl * Sl + dDdSr * Sr + dDdDl * Dl + dDdDr * Dr; - return std::make_tuple(dSdSl, dSdDl, dSdSr, dSdDr, dDdSl, dDdDl, dDdSr, dDdDr); + return {dSdSl, dSdDl, dSdSr, dSdDr, dDdSl, dDdDl, dDdSr, dDdDr}; } using TBase::convXtoU; diff --git a/GPU/TPCFastTransformation/Spline2DSpec.cxx b/GPU/TPCFastTransformation/Spline2DSpec.cxx index 055530b9314c2..4fbd4dc3d0110 100644 --- a/GPU/TPCFastTransformation/Spline2DSpec.cxx +++ b/GPU/TPCFastTransformation/Spline2DSpec.cxx @@ -29,8 +29,6 @@ #include "Riostream.h" #include "TMath.h" #include "Spline2DHelper.h" -#include "TCanvas.h" -#include "TNtuple.h" #include "TFile.h" #include "GPUCommonMath.h" diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index 7112a04b364c6..4ca5b74025743 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -78,13 +78,13 @@ class TPCFastSpaceChargeCorrection : public FlatObject } /// convert local y, z to internal grid coordinates u,v, and spline scale - std::tuple convLocalToGridUntruncated(float y, float z) const + std::array convLocalToGridUntruncated(float y, float z) const { return {(y - y0) * yScale, (z - z0) * zScale, getSpineScaleForZ(z)}; } /// convert internal grid coordinates u,v to local y, z - std::tuple convGridToLocal(float gridU, float gridV) const + std::array convGridToLocal(float gridU, float gridV) const { return {y0 + gridU / yScale, z0 + gridV / zScale}; } @@ -123,22 +123,22 @@ class TPCFastSpaceChargeCorrection : public FlatObject maxCorr[2] = GPUCommonMath::Max(maxCorr[2], dv); } - void updateMaxValues(std::tuple dxdudv, float scale) + void updateMaxValues(std::array dxdudv, float scale) { - float dx = std::get<0>(dxdudv) * scale; - float du = std::get<1>(dxdudv) * scale; - float dv = std::get<2>(dxdudv) * scale; + float dx = dxdudv[0] * scale; + float du = dxdudv[1] * scale; + float dv = dxdudv[2] * scale; updateMaxValues(dx, du, dv); } - std::tuple getMaxValues() const + std::array getMaxValues() const { - return std::make_tuple(maxCorr[0], maxCorr[1], maxCorr[2]); + return {maxCorr[0], maxCorr[1], maxCorr[2]}; } - std::tuple getMinValues() const + std::array getMinValues() const { - return std::make_tuple(minCorr[0], minCorr[1], minCorr[2]); + return {minCorr[0], minCorr[1], minCorr[2]}; } ClassDefNV(SectorRowInfo, 2); @@ -259,31 +259,31 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// // GPUd() int32_t getCorrectionInternal(int32_t sector, int32_t row, float u, float v, float& dx, float& du, float& dv) const; - GPUdi() std::tuple getCorrectionLocal(int32_t sector, int32_t row, float y, float z) const; + GPUdi() std::array getCorrectionLocal(int32_t sector, int32_t row, float y, float z) const; /// inverse correction: Real Y and Z -> Real X GPUd() float getCorrectionXatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const; /// inverse correction: Real Y and Z -> measred Y and Z - GPUd() std::tuple getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const; + GPUd() std::array getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const; /// _______________ Utilities _______________________________________________ /// convert local y, z to internal grid coordinates u,v /// return values: u, v, scaling factor - GPUd() std::tuple convLocalToGrid(int32_t sector, int32_t row, float y, float z) const; + GPUd() std::array convLocalToGrid(int32_t sector, int32_t row, float y, float z) const; /// convert internal grid coordinates u,v to local y, z /// return values: y, z, scaling factor - GPUd() std::tuple convGridToLocal(int32_t sector, int32_t row, float u, float v) const; + GPUd() std::array convGridToLocal(int32_t sector, int32_t row, float u, float v) const; /// convert real Y, Z to the internal grid coordinates /// return values: u, v, scaling factor - GPUd() std::tuple convRealLocalToGrid(int32_t sector, int32_t row, float y, float z) const; + GPUd() std::array convRealLocalToGrid(int32_t sector, int32_t row, float y, float z) const; /// convert internal grid coordinates to the real Y, Z /// return values: y, z - GPUd() std::tuple convGridToRealLocal(int32_t sector, int32_t row, float u, float v) const; + GPUd() std::array convGridToRealLocal(int32_t sector, int32_t row, float u, float v) const; GPUd() bool isLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const; GPUd() bool isRealLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const; @@ -453,7 +453,7 @@ GPUdi() const float* TPCFastSpaceChargeCorrection::getSplineDataInvYZ(int32_t se return getSplineData(sector, row, 2); } -GPUdi() std::tuple TPCFastSpaceChargeCorrection::convLocalToGrid(int32_t sector, int32_t row, float y, float z) const +GPUdi() std::array TPCFastSpaceChargeCorrection::convLocalToGrid(int32_t sector, int32_t row, float y, float z) const { /// convert local y, z to internal grid coordinates u,v /// return values: u, v, scaling factor @@ -491,13 +491,13 @@ GPUdi() bool TPCFastSpaceChargeCorrection::isRealLocalInsideGrid(int32_t sector, return true; } -GPUdi() std::tuple TPCFastSpaceChargeCorrection::convGridToLocal(int32_t sector, int32_t row, float gridU, float gridV) const +GPUdi() std::array TPCFastSpaceChargeCorrection::convGridToLocal(int32_t sector, int32_t row, float gridU, float gridV) const { /// convert internal grid coordinates u,v to local y, z return getSectorRowInfo(sector, row).gridMeasured.convGridToLocal(gridU, gridV); } -GPUdi() std::tuple TPCFastSpaceChargeCorrection::convRealLocalToGrid(int32_t sector, int32_t row, float y, float z) const +GPUdi() std::array TPCFastSpaceChargeCorrection::convRealLocalToGrid(int32_t sector, int32_t row, float y, float z) const { /// convert real y, z to the internal grid coordinates + scale const SplineType& spline = getSpline(sector, row); @@ -508,13 +508,13 @@ GPUdi() std::tuple TPCFastSpaceChargeCorrection::convRealLo return {gridU, gridV, scale}; } -GPUdi() std::tuple TPCFastSpaceChargeCorrection::convGridToRealLocal(int32_t sector, int32_t row, float gridU, float gridV) const +GPUdi() std::array TPCFastSpaceChargeCorrection::convGridToRealLocal(int32_t sector, int32_t row, float gridU, float gridV) const { /// convert internal grid coordinates u,v to the real y, z return getSectorRowInfo(sector, row).gridReal.convGridToLocal(gridU, gridV); } -GPUdi() std::tuple TPCFastSpaceChargeCorrection::getCorrectionLocal(int32_t sector, int32_t row, float y, float z) const +GPUdi() std::array TPCFastSpaceChargeCorrection::getCorrectionLocal(int32_t sector, int32_t row, float y, float z) const { const auto& info = getSectorRowInfo(sector, row); const SplineType& spline = getSpline(sector, row); @@ -541,7 +541,7 @@ GPUdi() float TPCFastSpaceChargeCorrection::getCorrectionXatRealYZ(int32_t secto return dx; } -GPUdi() std::tuple TPCFastSpaceChargeCorrection::getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const +GPUdi() std::array TPCFastSpaceChargeCorrection::getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const { auto [gridU, gridV, scale] = convRealLocalToGrid(sector, row, realY, realZ); const auto& info = getSectorRowInfo(sector, row); diff --git a/GPU/TPCFastTransformation/TPCFastTransform.h b/GPU/TPCFastTransformation/TPCFastTransform.h index 3b08296525fc7..35b94446ce088 100644 --- a/GPU/TPCFastTransformation/TPCFastTransform.h +++ b/GPU/TPCFastTransformation/TPCFastTransform.h @@ -349,13 +349,17 @@ class TPCFastTransform : public FlatObject GPUdi() void TPCFastTransform::convPadTimeToLocal(int32_t sector, int32_t row, float pad, float time, float& y, float& z, float vertexTime) const { float l = (time - mT0 - vertexTime) * mVdrift; // drift length [cm] - std::tie(y, z) = getGeometry().convPadDriftLengthToLocal(sector, row, pad, l); + const auto local = getGeometry().convPadDriftLengthToLocal(sector, row, pad, l); + y = local[0]; + z = local[1]; } GPUdi() void TPCFastTransform::convPadTimeToLocalInTimeFrame(int32_t sector, int32_t row, float pad, float time, float& y, float& z, float maxTimeBin) const { float l = getGeometry().getTPCzLength() + (time - mT0 - maxTimeBin) * mVdrift; // drift length [cm] - std::tie(y, z) = getGeometry().convPadDriftLengthToLocal(sector, row, pad, l); + const auto local = getGeometry().convPadDriftLengthToLocal(sector, row, pad, l); + y = local[0]; + z = local[1]; } // ---------------------------------------------------------------------- @@ -387,16 +391,16 @@ GPUdi() float TPCFastTransform::convDriftLengthToTime(float driftLength, float v GPUdi() void TPCFastTransform::convLocalToPadTime(int32_t sector, int32_t row, float y, float z, float& pad, float& time, float vertexTime) const { - float l; - std::tie(pad, l) = getGeometry().convLocalToPadDriftLength(sector, row, y, z); - time = convDriftLengthToTime(l, vertexTime); + const auto padLength = getGeometry().convLocalToPadDriftLength(sector, row, y, z); + pad = padLength[0]; + time = convDriftLengthToTime(padLength[1], vertexTime); } GPUdi() void TPCFastTransform::convLocalToPadTimeInTimeFrame(int32_t sector, int32_t row, float y, float z, float& pad, float& time, float maxTimeBin) const { - float l; - std::tie(pad, l) = getGeometry().convLocalToPadDriftLength(sector, row, y, z); - time = convDriftLengthToTime(l, maxTimeBin); + const auto padLength = getGeometry().convLocalToPadDriftLength(sector, row, y, z); + pad = padLength[0]; + time = convDriftLengthToTime(padLength[1], maxTimeBin); } // ---------------------------------------------------------------------- @@ -422,7 +426,10 @@ GPUdi() void TPCFastTransform::TransformLocal(int32_t sector, int32_t row, float } else #endif // GPUCA_GPUCODE { - std::tie(dx, dy, dz) = mCorrection.getCorrectionLocal(sector, row, y, z); + const auto corrLocal = mCorrection.getCorrectionLocal(sector, row, y, z); + dx = corrLocal[0]; + dy = corrLocal[1]; + dz = corrLocal[2]; if (ref) { if ((scale > 0.f) && (scaleMode == 0)) { // scaling was requested auto [dxRef, dyRef, dzRef] = ref->mCorrection.getCorrectionLocal(sector, row, y, z); @@ -471,12 +478,18 @@ GPUdi() void TPCFastTransform::TransformLocal(int32_t sector, int32_t row, float float dxRef = 0.f, dyRef = 0.f, dzRef = 0.f; if (ref) { - std::tie(dxRef, dyRef, dzRef) = ref->mCorrection.getCorrectionLocal(sector, row, y, z); + const auto corr = ref->mCorrection.getCorrectionLocal(sector, row, y, z); + dxRef = corr[0]; + dyRef = corr[1]; + dzRef = corr[2]; } float dxRef2 = 0.f, dyRef2 = 0.f, dzRef2 = 0.f; if (ref2) { - std::tie(dxRef2, dyRef2, dzRef2) = ref2->mCorrection.getCorrectionLocal(sector, row, y, z); + const auto corr = ref2->mCorrection.getCorrectionLocal(sector, row, y, z); + dxRef2 = corr[0]; + dyRef2 = corr[1]; + dzRef2 = corr[2]; } auto [dxOrig, dyOrig, dzOrig] = mCorrection.getCorrectionLocal(sector, row, y, z); @@ -604,7 +617,9 @@ GPUdi() void TPCFastTransform::TransformIdeal(int32_t sector, int32_t row, float x = getGeometry().getRowInfo(row).x; float driftLength = (time - mT0 - vertexTime) * mVdrift; // drift length cm - std::tie(y, z) = getGeometry().convPadDriftLengthToLocal(sector, row, pad, driftLength); + const auto local = getGeometry().convPadDriftLengthToLocal(sector, row, pad, driftLength); + y = local[0]; + z = local[1]; } GPUdi() float TPCFastTransform::convTimeToZinTimeFrame(int32_t sector, float time, float maxTimeBin) const @@ -711,20 +726,22 @@ GPUdi() void TPCFastTransform::InverseTransformYZtoNominalYZ(int32_t sector, int float dz = 0; if ((scale >= 0.f) || (scaleMode == 1) || (scaleMode == 2)) { - std::tie(dy, dz) = mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); + const auto corrYZ = mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); + dy = corrYZ[0]; + dz = corrYZ[1]; if (ref) { // scaling was requested if (scaleMode == 0 && scale > 0.f) { - auto [dyRef, dzRef] = ref->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); + const auto [dyRef, dzRef] = ref->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); dy = (dy - dyRef) * scale + dyRef; dz = (dz - dzRef) * scale + dzRef; } else if ((scale != 0) && ((scaleMode == 1) || (scaleMode == 2))) { - auto [dyRef, dzRef] = ref->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); + const auto [dyRef, dzRef] = ref->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); dy = dyRef * scale + dy; dz = dzRef * scale + dz; } if (ref2 && (scale2 != 0)) { - auto [dyRef, dzRef] = ref2->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); + const auto [dyRef, dzRef] = ref2->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); dy = dyRef * scale2 + dy; dz = dzRef * scale2 + dz; } diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.h b/GPU/TPCFastTransformation/TPCFastTransformGeo.h index 89b099ec63127..fc28bbef33602 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.h +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.h @@ -18,6 +18,7 @@ #define ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_TPCFASTTRANSFORMGEO_H #include "GPUCommonDef.h" +#include "GPUCommonArray.h" #ifndef GPUCA_GPUCODE_DEVICE #include #include "GPUCommonRtypes.h" @@ -55,7 +56,7 @@ class TPCFastTransformGeo GPUd() float getYmax() const { return -yMin; } /// get Y range - GPUd() std::tuple getYrange() const { return {getYmin(), getYmax()}; } + GPUd() std::array getYrange() const { return {getYmin(), getYmax()}; } /// get width in Y GPUd() float getYwidth() const { return -2.f * yMin; } @@ -125,7 +126,7 @@ class TPCFastTransformGeo GPUd() float getTPCzLength() const { return mTPCzLength; } /// Gives Z range for the corresponding TPC side - GPUd() std::tuple getZrange(int32_t sector) const; + GPUd() std::array getZrange(int32_t sector) const; GPUd() float getZmin(int32_t sector) const; GPUd() float getZmax(int32_t sector) const; GPUd() float getZreadout(int32_t sector) const; @@ -139,7 +140,7 @@ class TPCFastTransformGeo GPUd() void convGlobalToLocal(int32_t sector, float gx, float gy, float gz, float& lx, float& ly, float& lz) const; /// convert Pad, DriftLength -> Local c.s. - GPUd() std::tuple convPadDriftLengthToLocal(int32_t sector, int32_t row, float pad, float driftLength) const; + GPUd() std::array convPadDriftLengthToLocal(int32_t sector, int32_t row, float pad, float driftLength) const; /// convert DriftLength -> Local c.s. GPUd() float convDriftLengthToZ1(int32_t sector, float driftLength) const; @@ -148,7 +149,7 @@ class TPCFastTransformGeo GPUd() float convZtoDriftLength1(int32_t sector, float z) const; /// convert Local c.s. -> Pad, DriftLength - GPUd() std::tuple convLocalToPadDriftLength(int32_t sector, int32_t row, float y, float z) const; + GPUd() std::array convLocalToPadDriftLength(int32_t sector, int32_t row, float y, float z) const; /// Print method void print() const; @@ -229,7 +230,7 @@ GPUdi() void TPCFastTransformGeo::convGlobalToLocal(int32_t sector, float gx, fl lz = gz; } -GPUdi() std::tuple TPCFastTransformGeo::convPadDriftLengthToLocal(int32_t sector, int32_t row, float pad, float driftLength) const +GPUdi() std::array TPCFastTransformGeo::convPadDriftLengthToLocal(int32_t sector, int32_t row, float pad, float driftLength) const { /// convert Pad, DriftLength -> Local c.s. const RowInfo& rowInfo = getRowInfo(row); @@ -257,7 +258,7 @@ GPUdi() float TPCFastTransformGeo::convZtoDriftLength1(int32_t sector, float z) return (sector < NumberOfSectorsA) ? (mTPCzLength - z) : (z + mTPCzLength); } -GPUdi() std::tuple TPCFastTransformGeo::getZrange(int32_t sector) const +GPUdi() std::array TPCFastTransformGeo::getZrange(int32_t sector) const { /// z range for the sector if (sector < NumberOfSectorsA) { // TPC side A @@ -297,7 +298,7 @@ GPUdi() float TPCFastTransformGeo::getZreadout(int32_t sector) const } } -GPUdi() std::tuple TPCFastTransformGeo::convLocalToPadDriftLength(int32_t sector, int32_t row, float y, float z) const +GPUdi() std::array TPCFastTransformGeo::convLocalToPadDriftLength(int32_t sector, int32_t row, float y, float z) const { /// convert Local c.s. -> Pad, DriftLength float u, l; diff --git a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C index 50b667bb3e023..3cf3b697d2da9 100644 --- a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C +++ b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C @@ -303,12 +303,17 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* auto getInvCorrections = [&](int iSector, int iRow, float realY, float realZ, float& ix, float& iy, float& iz) { // get the inverse corrections ix, iy, iz at x,y,z ix = corr.getCorrectionXatRealYZ(iSector, iRow, realY, realZ); - std::tie(iy, iz) = corr.getCorrectionYZatRealYZ(iSector, iRow, realY, realZ); + const auto c = corr.getCorrectionYZatRealYZ(iSector, iRow, realY, realZ); + iy = c[0]; + iz = c[1]; }; auto getAllCorrections = [&](int iSector, int iRow, float y, float z, float& cx, float& cy, float& cz, float& ix, float& iy, float& iz) { // get the corrections cx,cy,cz at x,y,z - std::tie(cx, cy, cz) = corr.getCorrectionLocal(iSector, iRow, y, z); + const auto c = corr.getCorrectionLocal(iSector, iRow, y, z); + cx = c[0]; + cy = c[1]; + cz = c[2]; getInvCorrections(iSector, iRow, y + cy, z + cz, ix, iy, iz); }; From 4f9c0338ea02e35a3ca915c901765c3b8e5517c2 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Wed, 27 Aug 2025 18:28:46 +0000 Subject: [PATCH 514/701] TPC Splines: better smoothing between the voxels --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 104 +++++++++++------- GPU/TPCFastTransformation/Spline2DHelper.cxx | 14 ++- GPU/TPCFastTransformation/Spline2DHelper.h | 2 +- .../TPCFastSpaceChargeCorrectionMap.h | 9 +- .../macro/TPCFastTransformInit.C | 25 +++-- 5 files changed, 94 insertions(+), 60 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index 9232598e6a35e..02cdefa85e119 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -148,9 +148,13 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas if (!processingInverseCorrection) { info.resetMaxValues(); } + info.updateMaxValues(1., 1., 1.); + info.updateMaxValues(-1., -1., -1.); + if (nDataPoints >= 4) { std::vector pointGU(nDataPoints); std::vector pointGV(nDataPoints); + std::vector pointWeight(nDataPoints); std::vector pointCorr(3 * nDataPoints); // 3 dimensions for (int i = 0; i < nDataPoints; ++i) { o2::gpu::TPCFastSpaceChargeCorrectionMap::CorrectionPoint p = data[i]; @@ -161,15 +165,14 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas } pointGU[i] = gu; pointGV[i] = gv; + pointWeight[i] = p.mWeight; pointCorr[3 * i + 0] = p.mDx; pointCorr[3 * i + 1] = p.mDy; pointCorr[3 * i + 2] = p.mDz; - if (!processingInverseCorrection) { - info.updateMaxValues(20. * p.mDx, 20. * p.mDy, 20. * p.mDz); - } + info.updateMaxValues(5. * p.mDx, 5. * p.mDy, 5. * p.mDz); } - helper.approximateDataPoints(spline, splineParameters.data(), 0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax(), &pointGU[0], - &pointGV[0], &pointCorr[0], nDataPoints); + helper.approximateDataPoints(spline, splineParameters.data(), 0., spline.getGridX1().getUmax(), 0., spline.getGridX2().getUmax(), pointGU.data(), + pointGV.data(), pointCorr.data(), pointWeight.data(), nDataPoints); } else { for (int i = 0; i < spline.getNumberOfParameters(); i++) { splineParameters[i] = 0.f; @@ -301,7 +304,7 @@ std::unique_ptr TPCFastSpaceChargeCorrectionHelper double dx, dy, dz; correctionLocal(iSector, iRow, y, z, dx, dy, dz); mCorrectionMap.addCorrectionPoint(iSector, iRow, - y, z, dx, dy, dz); + y, z, dx, dy, dz, 1.); } } } // row @@ -593,14 +596,6 @@ std::unique_ptr TPCFastSpaceChargeCorrect data.mCy *= -1.; data.mCz *= -1.; } - if (data.mNentries > 0) { - if (iSector < geo.getNumberOfSectorsA() && data.mZ < 0) { - LOG(error) << errMsg << "fitted Z coordinate " << data.mZ << " is negative for sector " << iSector; - } - if (iSector >= geo.getNumberOfSectorsA() && data.mZ > 0) { - LOG(error) << errMsg << "fitted Z coordinate " << data.mZ << " is positive for sector " << iSector; - } - } } }; processor.Process(myThread); @@ -624,6 +619,9 @@ std::unique_ptr TPCFastSpaceChargeCorrect } } + double maxError[3] = {0., 0., 0.}; + int nErrors = 0; + for (int iSector = 0; iSector < nSectors; iSector++) { // now process the data row-by-row @@ -682,9 +680,21 @@ std::unique_ptr TPCFastSpaceChargeCorrect } if (!msg.str().empty()) { - LOG(warning) << directionName << " correction: fitted voxel position is outside the voxel: " - << " sector " << iSector << " row " << iRow << " bin y " << iy << " bin z " << iz - << msg.str(); + bool isMaxErrorExceeded = (fabs(data.mX - x) / dx > maxError[0]) || + (fabs(data.mY - vox.mY) / vox.mDy > maxError[1]) || + (fabs(data.mZ - vox.mZ) / vox.mDz > maxError[2]); + static std::mutex mutex; + mutex.lock(); + nErrors++; + if (nErrors < 20 || isMaxErrorExceeded) { + LOG(warning) << directionName << " correction: error N " << nErrors << "fitted voxel position is outside the voxel: " + << " sector " << iSector << " row " << iRow << " bin y " << iy << " bin z " << iz + << msg.str(); + maxError[0] = GPUCommonMath::Max(maxError[0], fabs(data.mX - x) / dx); + maxError[1] = GPUCommonMath::Max(maxError[1], fabs(data.mY - vox.mY) / vox.mDy); + maxError[2] = GPUCommonMath::Max(maxError[2], fabs(data.mZ - vox.mZ) / vox.mDz); + } + mutex.unlock(); } } else { // no data, take voxel center position @@ -773,17 +783,27 @@ std::unique_ptr TPCFastSpaceChargeCorrect auto& info = correction.getSectorRowInfo(iSector, iRow); const auto& spline = correction.getSpline(iSector, iRow); - auto addEdge = [&](int iy1, int iz1, int iy2, int iz2, int nSteps) { + auto addVoxel = [&](int iy, int iz, double weight) { + auto& vox = vRowVoxels[iy * nZ2Xbins + iz]; + if (vox.mSmoothingStep > 2) { + LOG(fatal) << "empty voxel is not repared: y " << iy << " z " << iz; + } + auto& data = vSectorData[iSector * nRows + iRow][iy * nZ2Xbins + iz]; + map.addCorrectionPoint(iSector, iRow, data.mY, data.mZ, data.mCx, data.mCy, data.mCz, weight); + }; + + auto addEdge = [&](int iy1, int iz1, int iy2, int iz2, int nPoints) { + // add n points on the edge between two voxels excluding the voxel points + if (nPoints < 1) + return; + if (iy1 < 0 || iy1 >= nY2Xbins || iz1 < 0 || iz1 >= nZ2Xbins) + return; + if (iy2 < 0 || iy2 >= nY2Xbins || iz2 < 0 || iz2 >= nZ2Xbins) + return; auto& data1 = vSectorData[iSector * nRows + iRow][iy1 * nZ2Xbins + iz1]; auto& vox1 = vRowVoxels[iy1 * nZ2Xbins + iz1]; auto& data2 = vSectorData[iSector * nRows + iRow][iy2 * nZ2Xbins + iz2]; auto& vox2 = vRowVoxels[iy2 * nZ2Xbins + iz2]; - if (vox1.mSmoothingStep > 2) { - LOG(fatal) << "empty voxel is not repared: y " << iy1 << " z " << iz1; - } - if (vox2.mSmoothingStep > 2) { - LOG(fatal) << "empty voxel is not repared: y " << iy2 << " z " << iz2; - } double y1 = data1.mY; double z1 = data1.mZ; double cx1 = data1.mCx; @@ -795,32 +815,36 @@ std::unique_ptr TPCFastSpaceChargeCorrect double cy2 = data2.mCy; double cz2 = data2.mCz; - for (int is = 0; is < nSteps; is++) { - double s2 = is / (double)nSteps; + for (int is = 1; is <= nPoints; is++) { + double s2 = is / (double)(nPoints + 1); double s1 = 1. - s2; double y = s1 * y1 + s2 * y2; double z = s1 * z1 + s2 * z2; double cx = s1 * cx1 + s2 * cx2; double cy = s1 * cy1 + s2 * cy2; double cz = s1 * cz1 + s2 * cz2; - map.addCorrectionPoint(iSector, iRow, y, z, cx, cy, cz); + map.addCorrectionPoint(iSector, iRow, y, z, cx, cy, cz, 1.); } }; + // original measurements weighted by 8 at each voxel and 8 additional artificial measurements around each voxel + // + // (y+1, z) 8 1 1 8 (y+1, z+1) + // 1 1 1 1 1 + // 1 1 1 1 1 + // (y,z) 8 1 1 8 1 + // 1 1 1 1 1 + for (int iy = 0; iy < nY2Xbins; iy++) { - for (int iz = 0; iz < nZ2Xbins - 1; iz++) { - addEdge(iy, iz, iy, iz + 1, 3); + for (int iz = 0; iz < nZ2Xbins; iz++) { + addVoxel(iy, iz, 8); + addEdge(iy, iz, iy, iz + 1, 2); + addEdge(iy, iz, iy + 1, iz, 2); + addEdge(iy, iz, iy + 1, iz + 1, 2); + addEdge(iy + 1, iz, iy, iz + 1, 2); } - addEdge(iy, nZ2Xbins - 1, iy, nZ2Xbins - 1, 1); } - for (int iz = 0; iz < nZ2Xbins; iz++) { - for (int iy = 0; iy < nY2Xbins - 1; iy++) { - addEdge(iy, iz, iy + 1, iz, 3); - } - addEdge(nY2Xbins - 1, iz, nY2Xbins - 1, iz, 1); - } // iy - } // iRow }; // myThread @@ -939,10 +963,11 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector dataPointGridU, dataPointGridV, dataPointF; + std::vector dataPointGridU, dataPointGridV, dataPointF, dataPointWeight; dataPointGridU.reserve(gridU.size() * gridV.size()); dataPointGridV.reserve(gridU.size() * gridV.size()); dataPointF.reserve(3 * gridU.size() * gridV.size()); + dataPointWeight.reserve(gridU.size() * gridV.size()); for (int iu = 0; iu < gridU.size(); iu++) { for (int iv = 0; iv < gridV.size(); iv++) { @@ -963,6 +988,7 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector::approximateFunctionViaDataPoints( mFdimensions = spline.getYdimensions(); std::vector dataPointX1(getNumberOfDataPoints()); std::vector dataPointX2(getNumberOfDataPoints()); + std::vector dataPointWeight(getNumberOfDataPoints(), 1.); std::vector dataPointF(getNumberOfDataPoints() * mFdimensions); double scaleX1 = (x1Max - x1Min) / ((double)mHelperU1.getSpline().getUmax()); @@ -256,7 +257,8 @@ void Spline2DHelper::approximateFunctionViaDataPoints( F(x1, x2, &dataPointF[ind * mFdimensions]); } } - approximateDataPoints(spline, spline.getParameters(), x1Min, x1Max, x2Min, x2Max, &dataPointX1[0], &dataPointX2[0], &dataPointF[0], getNumberOfDataPoints()); + approximateDataPoints(spline, spline.getParameters(), x1Min, x1Max, x2Min, x2Max, dataPointX1.data(), dataPointX2.data(), dataPointF.data(), + dataPointWeight.data(), getNumberOfDataPoints()); } template @@ -326,7 +328,7 @@ template void Spline2DHelper::approximateDataPoints( Spline2DContainer& spline, DataT* splineParameters, double x1Min, double x1Max, double x2Min, double x2Max, const double dataPointX1[], const double dataPointX2[], const double dataPointF[/*getNumberOfDataPoints() x nFdim*/], - int32_t nDataPoints) + const double dataPointWeight[], int32_t nDataPoints) { /// Create best-fit spline parameters for a given input function F @@ -343,6 +345,10 @@ void Spline2DHelper::approximateDataPoints( for (int32_t iPoint = 0; iPoint < nDataPoints; ++iPoint) { double u = fGridU.convXtoU(dataPointX1[iPoint]); double v = fGridV.convXtoU(dataPointX2[iPoint]); + double weight = dataPointWeight[iPoint]; + if (!(weight > 0.)) { + continue; + } int32_t iu = fGridU.getLeftKnotIndexForU(u); int32_t iv = fGridV.getLeftKnotIndexForU(v); double c[16]; @@ -353,14 +359,14 @@ void Spline2DHelper::approximateDataPoints( for (int32_t i = 0; i < 16; i++) { for (int32_t j = i; j < 16; j++) { - solver.A(ind[i], ind[j]) += c[i] * c[j]; + solver.A(ind[i], ind[j]) += weight * c[i] * c[j]; } } for (int32_t iDim = 0; iDim < nFdim; iDim++) { double f = (double)dataPointF[iPoint * nFdim + iDim]; for (int32_t i = 0; i < 16; i++) { - solver.B(ind[i], iDim) += f * c[i]; + solver.B(ind[i], iDim) += weight * f * c[i]; } } } // data points diff --git a/GPU/TPCFastTransformation/Spline2DHelper.h b/GPU/TPCFastTransformation/Spline2DHelper.h index 7195bab925f85..aa52c306a1a53 100644 --- a/GPU/TPCFastTransformation/Spline2DHelper.h +++ b/GPU/TPCFastTransformation/Spline2DHelper.h @@ -74,7 +74,7 @@ class Spline2DHelper void approximateDataPoints( Spline2DContainer& spline, DataT* splineParameters, double x1Min, double x1Max, double x2Min, double x2Max, const double dataPointX1[/*nDataPoints*/], const double dataPointX2[/*nDataPoints*/], - const double dataPointF[/*nDataPoints x spline.getYdimensions*/], int32_t nDataPoints); + const double dataPointF[/*nDataPoints x spline.getYdimensions*/], const double dataPointWeight[/*nDataPoints*/], int32_t nDataPoints); /// _______________ Interface for a step-wise construction of the best-fit spline ________________________ diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrectionMap.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrectionMap.h index fcee61ff09425..e54cf878ee2ff 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrectionMap.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrectionMap.h @@ -42,8 +42,9 @@ class TPCFastSpaceChargeCorrectionMap /// \brief The struct contains necessary info for TPC padrow /// struct CorrectionPoint { - double mY, mZ; // not-distorted local coordinates - double mDx, mDy, mDz; // corrections to the local coordinates + double mY{0.}, mZ{0.}; // not-distorted local coordinates + double mDx{0.}, mDy{0.}, mDz{0.}; // corrections to the local coordinates + double mWeight{0.}; // weight of the point }; /// _____________ Constructors / destructors __________________________ @@ -72,11 +73,11 @@ class TPCFastSpaceChargeCorrectionMap /// Starts the construction procedure, reserves temporary memory void addCorrectionPoint(int32_t iSector, int32_t iRow, double y, double z, - double dx, double dy, double dz) + double dx, double dy, double dz, double weight) { int32_t ind = mNrows * iSector + iRow; fDataPoints.at(ind).push_back(CorrectionPoint{y, z, - dx, dy, dz}); + dx, dy, dz, weight}); } const std::vector& getPoints(int32_t iSector, int32_t iRow) const diff --git a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C index 3cf3b697d2da9..3cb4812abafc1 100644 --- a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C +++ b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C @@ -319,6 +319,8 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* for (int direction = 0; direction < 2; direction++) { // 0 - normal, 1 - inverse + std::string directionName = (direction == 0) ? "direct" : "inverse"; + TTree* currentTree = (direction == 0) ? voxResTree : voxResTreeInverse; if (!currentTree) { std::cout << "tree voxResTree does not exist!" << std::endl; @@ -342,7 +344,7 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* double sumDiff[3] = {0., 0., 0.}; int64_t nDiff = 0; - std::cout << "fill debug ntuples at voxels ..." << std::endl; + LOG(info) << directionName << " correction: fill debug ntuples at voxels ..."; for (int32_t iVox = 0; iVox < currentTree->GetEntriesFast(); iVox++) { @@ -457,12 +459,11 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* } } - std::cout - << "fill debug ntuples everywhere .." << std::endl; + LOG(info) << directionName << " correction: fill debug ntuples everywhere .."; for (int32_t iSector = 0; iSector < geo.getNumberOfSectors(); iSector++) { // for (int32_t iSector = 0; iSector < 1; iSector++) { - std::cout << "debug ntules for sector " << iSector << std::endl; + LOG(info) << directionName << " correction: fill debug ntuples everywhere in sector " << iSector; int mirrorSector = (iSector >= geo.getNumberOfSectorsA()) ? iSector - geo.getNumberOfSectorsA() : iSector + geo.getNumberOfSectorsA(); @@ -592,17 +593,17 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* for (int32_t i = 0; i < 3; i++) { sumDiff[i] = sqrt(sumDiff[i]) / nDiff; } + LOG(info) << directionName << " correction: max and mean differences between spline and voxel corrections:"; + LOG(info) << "Max difference in x : " << maxDiff[0] << " at Sector " + << maxDiffSector[0] << " row " << maxDiffRow[0]; - std::cout << "Max difference in x : " << maxDiff[0] << " at Sector " - << maxDiffSector[0] << " row " << maxDiffRow[0] << std::endl; - - std::cout << "Max difference in y : " << maxDiff[1] << " at Sector " - << maxDiffSector[1] << " row " << maxDiffRow[1] << std::endl; + LOG(info) << "Max difference in y : " << maxDiff[1] << " at Sector " + << maxDiffSector[1] << " row " << maxDiffRow[1]; - std::cout << "Max difference in z : " << maxDiff[2] << " at Sector " - << maxDiffSector[2] << " row " << maxDiffRow[2] << std::endl; + LOG(info) << "Max difference in z : " << maxDiff[2] << " at Sector " + << maxDiffSector[2] << " row " << maxDiffRow[2]; - std::cout << "Mean difference in x,y,z : " << sumDiff[0] << " " << sumDiff[1] + LOG(info) << "Mean difference in x,y,z : " << sumDiff[0] << " " << sumDiff[1] << " " << sumDiff[2] << std::endl; } // direction From 29a87bf2e97952bfb30a0fdb0fa4f9c1b9e15d6a Mon Sep 17 00:00:00 2001 From: David Rohr Date: Mon, 1 Sep 2025 15:52:24 +0200 Subject: [PATCH 515/701] TPCFastTransform: fix compilation on GPU with the new splines --- GPU/TPCFastTransformation/Spline1DSpec.h | 6 +- GPU/TPCFastTransformation/Spline2DSpec.h | 12 +++- .../TPCFastSpaceChargeCorrection.h | 70 +++++++++---------- GPU/TPCFastTransformation/TPCFastTransform.h | 60 ++++++++-------- 4 files changed, 80 insertions(+), 68 deletions(-) diff --git a/GPU/TPCFastTransformation/Spline1DSpec.h b/GPU/TPCFastTransformation/Spline1DSpec.h index 28be5dd20e235..d72de5a446718 100644 --- a/GPU/TPCFastTransformation/Spline1DSpec.h +++ b/GPU/TPCFastTransformation/Spline1DSpec.h @@ -314,7 +314,11 @@ class Spline1DSpec : public Spline1DContainer const auto nYdimTmp = SplineUtil::getNdim(inpYdim); const auto nYdim = nYdimTmp.get(); - auto [dSdSl, dSdDl, dSdSr, dSdDr] = getSderivativesOverParsAtU(knotL, u); + auto val = getSderivativesOverParsAtU(knotL, u); + const auto& dSdSl = val[0]; + const auto& dSdDl = val[1]; + const auto& dSdSr = val[2]; + const auto& dSdDr = val[3]; for (int32_t dim = 0; dim < nYdim; ++dim) { S[dim] = dSdSr * Sr[dim] + dSdSl * Sl[dim] + dSdDl * Dl[dim] + dSdDr * Dr[dim]; } diff --git a/GPU/TPCFastTransformation/Spline2DSpec.h b/GPU/TPCFastTransformation/Spline2DSpec.h index 987ce1ad5d256..7c34b0890ce50 100644 --- a/GPU/TPCFastTransformation/Spline2DSpec.h +++ b/GPU/TPCFastTransformation/Spline2DSpec.h @@ -334,8 +334,16 @@ class Spline2DSpec const DataT* A = Parameters + (nu * iv + iu) * nYdim4; // values { {Y1,Y2,Y3}, {Y1,Y2,Y3}'v, {Y1,Y2,Y3}'u, {Y1,Y2,Y3}''vu } at {u0, v0} const DataT* B = A + nYdim4 * nu; // values { ... } at {u0, v1} - auto [dSl, dDl, dSr, dDr] = mGridX1.template getSderivativesOverParsAtU(knotU, u); - auto [dSd, dDd, dSu, dDu] = mGridX2.template getSderivativesOverParsAtU(knotV, v); + auto val1 = mGridX1.template getSderivativesOverParsAtU(knotU, u); + auto val2 = mGridX2.template getSderivativesOverParsAtU(knotV, v); + const auto& dSl = val1[0]; + const auto& dDl = val1[1]; + const auto& dSr = val1[2]; + const auto& dDr = val1[3]; + const auto& dSd = val2[0]; + const auto& dDd = val2[1]; + const auto& dSu = val2[2]; + const auto& dDu = val2[3]; // when nYdim == 1: // S = dSl * (dSd * A[0] + dDd * A[1]) + dDl * (dSd * A[2] + dDd * A[3]) + diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index 4ca5b74025743..ffbc8691ea268 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -56,15 +56,15 @@ class TPCFastSpaceChargeCorrection : public FlatObject float splineScalingWithZ{0.f}; ///< spline scaling factor in the Z region between the zOut and the readout plane public: - void set(float y0, float yScale, float z0, float zScale, float zOut, float zReadout) + void set(float y0_, float yScale_, float z0_, float zScale_, float zOut_, float zReadout_) { - this->y0 = y0; - this->yScale = yScale; - this->z0 = z0; - this->zScale = zScale; - this->zOut = zOut; + this->y0 = y0_; + this->yScale = yScale_; + this->z0 = z0_; + this->zScale = zScale_; + this->zOut = zOut_; // no scaling when the distance to the readout is too small - this->splineScalingWithZ = fabs(zReadout - zOut) > 1. ? 1. / (zReadout - zOut) : 0.; + this->splineScalingWithZ = fabs(zReadout_ - zOut_) > 1. ? 1. / (zReadout_ - zOut_) : 0.; } float getY0() const { return y0; } @@ -72,13 +72,13 @@ class TPCFastSpaceChargeCorrection : public FlatObject float getZ0() const { return z0; } float getZscale() const { return zScale; } - float getSpineScaleForZ(float z) const + GPUd() float getSpineScaleForZ(float z) const { return 1.f - GPUCommonMath::Clamp((z - zOut) * splineScalingWithZ, 0.f, 1.f); } /// convert local y, z to internal grid coordinates u,v, and spline scale - std::array convLocalToGridUntruncated(float y, float z) const + GPUd() std::array convLocalToGridUntruncated(float y, float z) const { return {(y - y0) * yScale, (z - z0) * zScale, getSpineScaleForZ(z)}; } @@ -458,21 +458,21 @@ GPUdi() std::array TPCFastSpaceChargeCorrection::convLocalToGrid(int32 /// convert local y, z to internal grid coordinates u,v /// return values: u, v, scaling factor const SplineType& spline = getSpline(sector, row); - auto [gridU, gridV, scale] = getSectorRowInfo(sector, row).gridMeasured.convLocalToGridUntruncated(y, z); + auto val = getSectorRowInfo(sector, row).gridMeasured.convLocalToGridUntruncated(y, z); // shrink to the grid - gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); - gridV = GPUCommonMath::Clamp(gridV, 0.f, (float)spline.getGridX2().getUmax()); - return {gridU, gridV, scale}; + val[0] = GPUCommonMath::Clamp(val[0], 0.f, (float)spline.getGridX1().getUmax()); + val[1] = GPUCommonMath::Clamp(val[1], 0.f, (float)spline.getGridX2().getUmax()); + return val; } GPUdi() bool TPCFastSpaceChargeCorrection::isLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const { /// check if local y, z are inside the grid - auto [gridU, gridV, scale] = getSectorRowInfo(sector, row).gridMeasured.convLocalToGridUntruncated(y, z); + auto val = getSectorRowInfo(sector, row).gridMeasured.convLocalToGridUntruncated(y, z); const auto& spline = getSpline(sector, row); // shrink to the grid - if (gridU < 0.f || gridU > (float)spline.getGridX1().getUmax() || // - gridV < 0.f || gridV > (float)spline.getGridX2().getUmax()) { + if (val[0] < 0.f || val[0] > (float)spline.getGridX1().getUmax() || // + val[1] < 0.f || val[1] > (float)spline.getGridX2().getUmax()) { return false; } return true; @@ -481,11 +481,11 @@ GPUdi() bool TPCFastSpaceChargeCorrection::isLocalInsideGrid(int32_t sector, int GPUdi() bool TPCFastSpaceChargeCorrection::isRealLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const { /// check if local y, z are inside the grid - auto [gridU, gridV, scale] = getSectorRowInfo(sector, row).gridReal.convLocalToGridUntruncated(y, z); + auto val = getSectorRowInfo(sector, row).gridReal.convLocalToGridUntruncated(y, z); const auto& spline = getSpline(sector, row); // shrink to the grid - if (gridU < 0.f || gridU > (float)spline.getGridX1().getUmax() || // - gridV < 0.f || gridV > (float)spline.getGridX2().getUmax()) { + if (val[0] < 0.f || val[0] > (float)spline.getGridX1().getUmax() || // + val[1] < 0.f || val[1] > (float)spline.getGridX2().getUmax()) { return false; } return true; @@ -501,11 +501,11 @@ GPUdi() std::array TPCFastSpaceChargeCorrection::convRealLocalToGrid(i { /// convert real y, z to the internal grid coordinates + scale const SplineType& spline = getSpline(sector, row); - auto [gridU, gridV, scale] = getSectorRowInfo(sector, row).gridReal.convLocalToGridUntruncated(y, z); + auto val = getSectorRowInfo(sector, row).gridReal.convLocalToGridUntruncated(y, z); // shrink to the grid - gridU = GPUCommonMath::Clamp(gridU, 0.f, (float)spline.getGridX1().getUmax()); - gridV = GPUCommonMath::Clamp(gridV, 0.f, (float)spline.getGridX2().getUmax()); - return {gridU, gridV, scale}; + val[0] = GPUCommonMath::Clamp(val[0], 0.f, (float)spline.getGridX1().getUmax()); + val[1] = GPUCommonMath::Clamp(val[1], 0.f, (float)spline.getGridX2().getUmax()); + return val; } GPUdi() std::array TPCFastSpaceChargeCorrection::convGridToRealLocal(int32_t sector, int32_t row, float gridU, float gridV) const @@ -520,35 +520,35 @@ GPUdi() std::array TPCFastSpaceChargeCorrection::getCorrectionLocal(in const SplineType& spline = getSpline(sector, row); const float* splineData = getSplineData(sector, row); - auto [gridU, gridV, scale] = convLocalToGrid(sector, row, y, z); + auto val = convLocalToGrid(sector, row, y, z); float dxyz[3]; - spline.interpolateAtU(splineData, gridU, gridV, dxyz); + spline.interpolateAtU(splineData, val[0], val[1], dxyz); - float dx = scale * GPUCommonMath::Clamp(dxyz[0], info.minCorr[0], info.maxCorr[0]); - float dy = scale * GPUCommonMath::Clamp(dxyz[1], info.minCorr[1], info.maxCorr[1]); - float dz = scale * GPUCommonMath::Clamp(dxyz[2], info.minCorr[2], info.maxCorr[2]); + float dx = val[2] * GPUCommonMath::Clamp(dxyz[0], info.minCorr[0], info.maxCorr[0]); + float dy = val[2] * GPUCommonMath::Clamp(dxyz[1], info.minCorr[1], info.maxCorr[1]); + float dz = val[2] * GPUCommonMath::Clamp(dxyz[2], info.minCorr[2], info.maxCorr[2]); return {dx, dy, dz}; } GPUdi() float TPCFastSpaceChargeCorrection::getCorrectionXatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const { const auto& info = getSectorRowInfo(sector, row); - auto [gridU, gridV, scale] = convRealLocalToGrid(sector, row, realY, realZ); + auto val = convRealLocalToGrid(sector, row, realY, realZ); float dx = 0; - getSplineInvX(sector, row).interpolateAtU(getSplineDataInvX(sector, row), gridU, gridV, &dx); - dx = scale * GPUCommonMath::Clamp(dx, info.minCorr[0], info.maxCorr[0]); + getSplineInvX(sector, row).interpolateAtU(getSplineDataInvX(sector, row), val[0], val[1], &dx); + dx = val[2] * GPUCommonMath::Clamp(dx, info.minCorr[0], info.maxCorr[0]); return dx; } GPUdi() std::array TPCFastSpaceChargeCorrection::getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const { - auto [gridU, gridV, scale] = convRealLocalToGrid(sector, row, realY, realZ); + auto val = convRealLocalToGrid(sector, row, realY, realZ); const auto& info = getSectorRowInfo(sector, row); float dyz[2]; - getSplineInvYZ(sector, row).interpolateAtU(getSplineDataInvYZ(sector, row), gridU, gridV, dyz); - dyz[0] = scale * GPUCommonMath::Clamp(dyz[0], info.minCorr[1], info.maxCorr[1]); - dyz[1] = scale * GPUCommonMath::Clamp(dyz[1], info.minCorr[2], info.maxCorr[2]); + getSplineInvYZ(sector, row).interpolateAtU(getSplineDataInvYZ(sector, row), val[0], val[1], dyz); + dyz[0] = val[2] * GPUCommonMath::Clamp(dyz[0], info.minCorr[1], info.maxCorr[1]); + dyz[1] = val[2] * GPUCommonMath::Clamp(dyz[1], info.minCorr[2], info.maxCorr[2]); return {dyz[0], dyz[1]}; } diff --git a/GPU/TPCFastTransformation/TPCFastTransform.h b/GPU/TPCFastTransformation/TPCFastTransform.h index 35b94446ce088..60f5952e6a1fc 100644 --- a/GPU/TPCFastTransformation/TPCFastTransform.h +++ b/GPU/TPCFastTransformation/TPCFastTransform.h @@ -349,17 +349,17 @@ class TPCFastTransform : public FlatObject GPUdi() void TPCFastTransform::convPadTimeToLocal(int32_t sector, int32_t row, float pad, float time, float& y, float& z, float vertexTime) const { float l = (time - mT0 - vertexTime) * mVdrift; // drift length [cm] - const auto local = getGeometry().convPadDriftLengthToLocal(sector, row, pad, l); - y = local[0]; - z = local[1]; + const auto localval = getGeometry().convPadDriftLengthToLocal(sector, row, pad, l); + y = localval[0]; + z = localval[1]; } GPUdi() void TPCFastTransform::convPadTimeToLocalInTimeFrame(int32_t sector, int32_t row, float pad, float time, float& y, float& z, float maxTimeBin) const { float l = getGeometry().getTPCzLength() + (time - mT0 - maxTimeBin) * mVdrift; // drift length [cm] - const auto local = getGeometry().convPadDriftLengthToLocal(sector, row, pad, l); - y = local[0]; - z = local[1]; + const auto localval = getGeometry().convPadDriftLengthToLocal(sector, row, pad, l); + y = localval[0]; + z = localval[1]; } // ---------------------------------------------------------------------- @@ -432,22 +432,22 @@ GPUdi() void TPCFastTransform::TransformLocal(int32_t sector, int32_t row, float dz = corrLocal[2]; if (ref) { if ((scale > 0.f) && (scaleMode == 0)) { // scaling was requested - auto [dxRef, dyRef, dzRef] = ref->mCorrection.getCorrectionLocal(sector, row, y, z); - dx = (dx - dxRef) * scale + dxRef; - dy = (dy - dyRef) * scale + dyRef; - dz = (dz - dzRef) * scale + dzRef; + auto val = ref->mCorrection.getCorrectionLocal(sector, row, y, z); + dx = (dx - val[0]) * scale + val[0]; + dy = (dy - val[1]) * scale + val[1]; + dz = (dz - val[2]) * scale + val[2]; } else if ((scale != 0.f) && ((scaleMode == 1) || (scaleMode == 2))) { - auto [dxRef, dyRef, dzRef] = ref->mCorrection.getCorrectionLocal(sector, row, y, z); - dx = dxRef * scale + dx; - dy = dyRef * scale + dy; - dz = dzRef * scale + dz; + auto val = ref->mCorrection.getCorrectionLocal(sector, row, y, z); + dx = val[0] * scale + dx; + dy = val[1] * scale + dy; + dz = val[2] * scale + dz; } } if (ref2 && (scale2 != 0)) { - auto [dxRef, dyRef, dzRef] = ref2->mCorrection.getCorrectionLocal(sector, row, y, z); - dx = dxRef * scale2 + dx; - dy = dyRef * scale2 + dy; - dz = dzRef * scale2 + dz; + auto val = ref2->mCorrection.getCorrectionLocal(sector, row, y, z); + dx = val[0] * scale2 + dx; + dy = val[1] * scale2 + dy; + dz = val[2] * scale2 + dz; } } } @@ -617,9 +617,9 @@ GPUdi() void TPCFastTransform::TransformIdeal(int32_t sector, int32_t row, float x = getGeometry().getRowInfo(row).x; float driftLength = (time - mT0 - vertexTime) * mVdrift; // drift length cm - const auto local = getGeometry().convPadDriftLengthToLocal(sector, row, pad, driftLength); - y = local[0]; - z = local[1]; + const auto localval = getGeometry().convPadDriftLengthToLocal(sector, row, pad, driftLength); + y = localval[0]; + z = localval[1]; } GPUdi() float TPCFastTransform::convTimeToZinTimeFrame(int32_t sector, float time, float maxTimeBin) const @@ -732,18 +732,18 @@ GPUdi() void TPCFastTransform::InverseTransformYZtoNominalYZ(int32_t sector, int if (ref) { // scaling was requested if (scaleMode == 0 && scale > 0.f) { - const auto [dyRef, dzRef] = ref->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); - dy = (dy - dyRef) * scale + dyRef; - dz = (dz - dzRef) * scale + dzRef; + const auto val = ref->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); + dy = (dy - val[0]) * scale + val[0]; + dz = (dz - val[1]) * scale + val[1]; } else if ((scale != 0) && ((scaleMode == 1) || (scaleMode == 2))) { - const auto [dyRef, dzRef] = ref->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); - dy = dyRef * scale + dy; - dz = dzRef * scale + dz; + const auto val = ref->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); + dy = val[0] * scale + dy; + dz = val[1] * scale + dz; } if (ref2 && (scale2 != 0)) { - const auto [dyRef, dzRef] = ref2->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); - dy = dyRef * scale2 + dy; - dz = dzRef * scale2 + dz; + const auto val = ref2->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); + dy = val[0] * scale2 + dy; + dz = val[1] * scale2 + dz; } } } From 4879ec57b1f38f4d3bea73be08e3a457a191ee92 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Wed, 17 Sep 2025 17:49:29 +0000 Subject: [PATCH 516/701] TPC Splines: bugfixes in spline merging --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 2 +- GPU/TPCFastTransformation/Spline2DSpec.h | 48 +++++++++---------- 2 files changed, 25 insertions(+), 25 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index 02cdefa85e119..eb7620c358774 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -1103,7 +1103,7 @@ void TPCFastSpaceChargeCorrectionHelper::mergeCorrections( for (int iu = 0; iu < gridU.getNumberOfKnots(); iu++) { double u = gridU.getKnot(iu).u; for (int iv = 0; iv < gridV.getNumberOfKnots(); iv++) { - double v = gridV.getKnot(iu).u; + double v = gridV.getKnot(iv).u; int knotIndex = spline.getKnotIndex(iu, iv); float P[nKnotPar3d]; diff --git a/GPU/TPCFastTransformation/Spline2DSpec.h b/GPU/TPCFastTransformation/Spline2DSpec.h index 7c34b0890ce50..5681de2dc5fe9 100644 --- a/GPU/TPCFastTransformation/Spline2DSpec.h +++ b/GPU/TPCFastTransformation/Spline2DSpec.h @@ -382,8 +382,8 @@ class Spline2DSpec const auto nYdim4 = nYdim * 4; DataT *S = P, - *R = P + nYdim, - *Q = P + nYdim * 2, + *Q = P + nYdim, + *R = P + nYdim * 2, *W = P + nYdim * 3; const DataT& u = u1; @@ -425,28 +425,6 @@ class Spline2DSpec } } - // Derivative R = dS / du - // R = dRdSl * (dSdSd * A[0] + dSdDd * A[1]) + dRdDl * (dSdSd * A[2] + dSdDd * A[3]) + - // dRdSr * (dSdSd * A[4] + dSdDd * A[5]) + dRdDr * (dSdSd * A[6] + dSdDd * A[7]) + - // dRdSl * (dSdSu * B[0] + dSdDu * B[1]) + dRdDl * (dSdSu * B[2] + dSdDu * B[3]) + - // dRdSr * (dSdSu * B[4] + dSdDu * B[5]) + dRdDr * (dSdSu * B[6] + dSdDu * B[7]); - - { - DataT a[8] = {dRdSl * dSdSd, dRdSl * dSdDd, dRdDl * dSdSd, dRdDl * dSdDd, - dRdSr * dSdSd, dRdSr * dSdDd, dRdDr * dSdSd, dRdDr * dSdDd}; - DataT b[8] = {dRdSl * dSdSu, dRdSl * dSdDu, dRdDl * dSdSu, dRdDl * dSdDu, - dRdSr * dSdSu, dRdSr * dSdDu, dRdDr * dSdSu, dRdDr * dSdDu}; - - // R = sum a[i]*A[i] + b[i]*B[i] - - for (int32_t dim = 0; dim < nYdim; dim++) { - R[dim] = 0; - for (int32_t i = 0; i < 8; i++) { - R[dim] += a[i] * A[nYdim * i + dim] + b[i] * B[nYdim * i + dim]; - } - } - } - // Derivative Q = dS / dv // Q = dSdSl * (dQdSd * A[0] + dQdDd * A[1]) + dSdDl * (dQdSd * A[2] + dQdDd * A[3]) + // dSdSr * (dQdSd * A[4] + dQdDd * A[5]) + dSdDr * (dQdSd * A[6] + dQdDd * A[7]) + @@ -469,6 +447,28 @@ class Spline2DSpec } } + // Derivative R = dS / du + // R = dRdSl * (dSdSd * A[0] + dSdDd * A[1]) + dRdDl * (dSdSd * A[2] + dSdDd * A[3]) + + // dRdSr * (dSdSd * A[4] + dSdDd * A[5]) + dRdDr * (dSdSd * A[6] + dSdDd * A[7]) + + // dRdSl * (dSdSu * B[0] + dSdDu * B[1]) + dRdDl * (dSdSu * B[2] + dSdDu * B[3]) + + // dRdSr * (dSdSu * B[4] + dSdDu * B[5]) + dRdDr * (dSdSu * B[6] + dSdDu * B[7]); + + { + DataT a[8] = {dRdSl * dSdSd, dRdSl * dSdDd, dRdDl * dSdSd, dRdDl * dSdDd, + dRdSr * dSdSd, dRdSr * dSdDd, dRdDr * dSdSd, dRdDr * dSdDd}; + DataT b[8] = {dRdSl * dSdSu, dRdSl * dSdDu, dRdDl * dSdSu, dRdDl * dSdDu, + dRdSr * dSdSu, dRdSr * dSdDu, dRdDr * dSdSu, dRdDr * dSdDu}; + + // R = sum a[i]*A[i] + b[i]*B[i] + + for (int32_t dim = 0; dim < nYdim; dim++) { + R[dim] = 0; + for (int32_t i = 0; i < 8; i++) { + R[dim] += a[i] * A[nYdim * i + dim] + b[i] * B[nYdim * i + dim]; + } + } + } + // cross-derivative W = (dS)^2 / du / dv // W = dRdSl * (dQdSd * A[0] + dQdDd * A[1]) + dRdDl * (dQdSd * A[2] + dQdDd * A[3]) + // dRdSr * (dQdSd * A[4] + dQdDd * A[5]) + dRdDr * (dQdSd * A[6] + dQdDd * A[7]) + From a4c73037924841e964da679408d1e66fcca2340e Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Mon, 6 Oct 2025 21:19:19 +0000 Subject: [PATCH 517/701] TPC Splines: add backward compatibility --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 22 +- .../TPCFastSpaceChargeCorrection.cxx | 331 +++++++++++++++--- .../TPCFastSpaceChargeCorrection.h | 90 ++--- GPU/TPCFastTransformation/TPCFastTransform.h | 2 +- .../TPCFastTransformGeo.cxx | 8 +- .../TPCFastTransformGeo.h | 10 +- .../TPCFastTransformationLinkDef_O2.h | 23 +- .../macro/TPCFastTransformInit.C | 92 ++--- 8 files changed, 408 insertions(+), 170 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index eb7620c358774..faba4f2ce065e 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -180,15 +180,15 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas } if (processingInverseCorrection) { - float* splineX = correction.getSplineDataInvX(sector, row); - float* splineYZ = correction.getSplineDataInvYZ(sector, row); + float* splineX = correction.getCorrectionDataInvX(sector, row); + float* splineYZ = correction.getCorrectionDataInvYZ(sector, row); for (int i = 0; i < spline.getNumberOfParameters() / 3; i++) { splineX[i] = splineParameters[3 * i + 0]; splineYZ[2 * i + 0] = splineParameters[3 * i + 1]; splineYZ[2 * i + 1] = splineParameters[3 * i + 2]; } } else { - float* splineXYZ = correction.getSplineData(sector, row); + float* splineXYZ = correction.getCorrectionData(sector, row); for (int i = 0; i < spline.getNumberOfParameters(); i++) { splineXYZ[i] = splineParameters[i]; } @@ -1000,8 +1000,8 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vector(mFlatBufferPtr + splineDataOffset); - bufferSize = splineDataOffset + mDataSizeBytes[is]; + size_t correctionDataOffset = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); + mCorrectionData[is] = reinterpret_cast(mFlatBufferPtr + correctionDataOffset); + bufferSize = correctionDataOffset + mCorrectionDataSize[is]; } } +void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBufferPtr) +{ + /// Sets the actual location of the external flat buffer after it has been moved (e.g. to another maschine) + + if (mClassVersion == 4) { + FlatObject::setActualBufferAddress(actualFlatBufferPtr); + + size_t scSize = sizeof(SplineType) * mNumberOfScenarios; + + mScenarioPtr = reinterpret_cast(mFlatBufferPtr); + + size_t scBufferOffset = alignSize(scSize, SplineType::getBufferAlignmentBytes()); + size_t scBufferSize = 0; + + for (int32_t i = 0; i < mNumberOfScenarios; i++) { + SplineType& sp = mScenarioPtr[i]; + sp.setActualBufferAddress(mFlatBufferPtr + scBufferOffset + scBufferSize); + scBufferSize = alignSize(scBufferSize + sp.getFlatBufferSize(), sp.getBufferAlignmentBytes()); + } + size_t bufferSize = scBufferOffset + scBufferSize; + for (int32_t is = 0; is < 3; is++) { + size_t correctionDataOffset = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); + mCorrectionData[is] = reinterpret_cast(mFlatBufferPtr + correctionDataOffset); + bufferSize = correctionDataOffset + mCorrectionDataSize[is]; + } + return; + } + + if (mClassVersion != 3) { + LOG(fatal) << "TPCFastSpaceChargeCorrection::setActualBufferAddress() called with class version " << mClassVersion << ". This is not supported."; + return; + } + + // Class version 3 + + struct RowInfoVersion3 { + int32_t splineScenarioID{0}; ///< scenario index (which of Spline2D splines to use) + size_t dataOffsetBytes[3]{0}; ///< offset for the spline data withing a TPC sector + }; + + struct RowActiveAreaVersion3 { + float maxDriftLengthCheb[5]{0.f}; + float vMax{0.f}; + float cuMin{0.f}; + float cuMax{0.f}; + float cvMax{0.f}; + }; + + struct SectorRowInfoVersion3 { + float gridV0{0.f}; ///< V coordinate of the V-grid start + float gridCorrU0{0.f}; ///< U coordinate of the U-grid start for corrected U + float gridCorrV0{0.f}; ///< V coordinate of the V-grid start for corrected V + float scaleCorrUtoGrid{0.f}; ///< scale corrected U to U-grid coordinate + float scaleCorrVtoGrid{0.f}; ///< scale corrected V to V-grid coordinate + RowActiveAreaVersion3 activeArea; + }; + + FlatObject::setActualBufferAddress(actualFlatBufferPtr); + + size_t oldRowsOffset = 0; + size_t oldRowsSize = sizeof(RowInfoVersion3) * mGeo.getNumberOfRows(); + + size_t oldSectorRowsOffset = oldRowsOffset + oldRowsSize; + size_t oldSectorRowsSize = sizeof(SectorRowInfoVersion3) * mGeo.getNumberOfRows() * mGeo.getNumberOfSectors(); + + size_t oldScenariosOffset = alignSize(oldSectorRowsOffset + oldSectorRowsSize, SplineType::getClassAlignmentBytes()); + size_t scenariosSize = sizeof(SplineType) * mNumberOfScenarios; + + SplineType* oldScenarioPtr = reinterpret_cast(mFlatBufferPtr + oldScenariosOffset); + + { // copy old-format sector and row parameters from the buffer to the arrays + + auto* oldRowInfos = reinterpret_cast(mFlatBufferPtr + oldRowsOffset); + auto* oldSectorRowInfos = reinterpret_cast(mFlatBufferPtr + oldSectorRowsOffset); + + size_t sectorDataSize[3]; + for (int32_t is = 0; is < 3; is++) { + sectorDataSize[is] = mCorrectionDataSize[is] / mGeo.getNumberOfSectors(); + } + + for (int32_t iSector = 0; iSector < mGeo.getNumberOfSectors(); iSector++) { + + for (int32_t iRow = 0; iRow < mGeo.getNumberOfRows(); iRow++) { + RowInfoVersion3& oldRowInfo = oldRowInfos[iRow]; + SectorRowInfoVersion3& oldSectorRowInfo = oldSectorRowInfos[mGeo.getNumberOfRows() * iSector + iRow]; + + // the spline buffer is not yet initialised, don't try to access knot positions etc + const auto& spline = oldScenarioPtr[oldRowInfo.splineScenarioID]; + + SectorRowInfo& newSectorRow = getSectorRowInfo(iSector, iRow); + + newSectorRow.splineScenarioID = oldRowInfo.splineScenarioID; + for (int32_t is = 0; is < 3; is++) { + newSectorRow.dataOffsetBytes[is] = sectorDataSize[is] * iSector + oldRowInfo.dataOffsetBytes[is]; + } + + { // grid for the measured coordinates + float y0 = mGeo.getRowInfo(iRow).yMin; + float yScale = spline.getGridX1().getUmax() / mGeo.getRowInfo(iRow).getYwidth(); + float zReadout = mGeo.getZreadout(iSector); + float zOut = mGeo.getTPCzLength() - oldSectorRowInfo.gridV0; + float z0 = -3.; + float zScale = spline.getGridX2().getUmax() / (zOut - z0); + if (iSector >= mGeo.getNumberOfSectorsA()) { + zOut = -zOut; + z0 = zOut; + } + newSectorRow.gridMeasured.set(y0, yScale, z0, zScale, zOut, zReadout); + } + + { // grid for the real coordinates + float y0 = oldSectorRowInfo.gridCorrU0; + float yScale = oldSectorRowInfo.scaleCorrUtoGrid; + float zReadout = mGeo.getZreadout(iSector); + float zOut = mGeo.getTPCzLength() - oldSectorRowInfo.gridCorrV0; + float zScale = oldSectorRowInfo.scaleCorrVtoGrid; + float z0 = zOut - spline.getGridX2().getUmax() / zScale; + if (iSector >= mGeo.getNumberOfSectorsA()) { + zOut = -zOut; + z0 = zOut; + } + newSectorRow.gridReal.set(y0, yScale, z0, zScale, zOut, zReadout); + } + + newSectorRow.resetMaxValues(); + newSectorRow.updateMaxValues(-50.f, -50.f, -50.f); + newSectorRow.updateMaxValues(50.f, 50.f, 50.f); + } + } + } + + // move spline scenarios to the new place in the buffer + + mScenarioPtr = reinterpret_cast(mFlatBufferPtr); + memmove(mScenarioPtr, oldScenarioPtr, scenariosSize); + + size_t oldScenariosBufferOffset = alignSize(oldScenariosOffset + scenariosSize, SplineType::getBufferAlignmentBytes()); + size_t scenariosBufferOffset = alignSize(scenariosSize, SplineType::getBufferAlignmentBytes()); + + size_t oldScenariosBufferSize = 0; + size_t scenariosBufferSize = 0; + for (int32_t i = 0; i < mNumberOfScenarios; i++) { + SplineType& sp = mScenarioPtr[i]; + char* oldAddress = mFlatBufferPtr + oldScenariosBufferOffset + oldScenariosBufferSize; + char* newAddress = mFlatBufferPtr + scenariosBufferOffset + scenariosBufferSize; + memmove(newAddress, oldAddress, sp.getFlatBufferSize()); + sp.setActualBufferAddress(newAddress); + oldScenariosBufferSize = alignSize(oldScenariosBufferSize + sp.getFlatBufferSize(), sp.getBufferAlignmentBytes()); + scenariosBufferSize = alignSize(scenariosBufferSize + sp.getFlatBufferSize(), sp.getBufferAlignmentBytes()); + } + + size_t oldBufferSize = oldScenariosBufferOffset + oldScenariosBufferSize; + size_t bufferSize = scenariosBufferOffset + scenariosBufferSize; + + // move spline data to the new place in the buffer + + for (int32_t is = 0; is < 3; is++) { + size_t oldCorrectionDataOffset = alignSize(oldBufferSize, SplineType::getParameterAlignmentBytes()); + size_t correctionDataOffset = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); + mCorrectionData[is] = reinterpret_cast(mFlatBufferPtr + correctionDataOffset); + memmove(mCorrectionData[is], mFlatBufferPtr + oldCorrectionDataOffset, mCorrectionDataSize[is]); + oldBufferSize = oldCorrectionDataOffset + mCorrectionDataSize[is]; + bufferSize = correctionDataOffset + mCorrectionDataSize[is]; + } + + mFlatBufferSize = bufferSize; + + // now convert the spline data to the new format + for (int32_t iSector = 0; iSector < mGeo.getNumberOfSectors(); iSector++) { + bool isAside = (iSector < mGeo.getNumberOfSectorsA()); + for (int32_t iRow = 0; iRow < mGeo.getNumberOfRows(); iRow++) { + + SectorRowInfo& sectorRow = getSectorRowInfo(iSector, iRow); + const auto& spline = mScenarioPtr[sectorRow.splineScenarioID]; + + int nSplineDimensions[3] = {3, 1, 2}; + + for (int iSpline = 0; iSpline < 3; iSpline++) { + int nDim = nSplineDimensions[iSpline]; + int nKnotParameters = 4 * nDim; + auto* data = getCorrectionData(iSector, iRow, iSpline); + + // lambda to swap parameters at two knots + auto swapKnots = [&](int i1, int j1, int i2, int j2) { + auto k1 = spline.getKnotIndex(i1, j1); + auto k2 = spline.getKnotIndex(i2, j2); + for (int ipar = 0; ipar < nKnotParameters; ipar++) { + std::swap(data[nKnotParameters * k1 + ipar], data[nKnotParameters * k2 + ipar]); + } + }; + + // reorder knots for the A side Y == old U, Z == - old V + if (isAside) { + for (int32_t i = 0; i < spline.getGridX1().getNumberOfKnots(); i++) { + for (int32_t j = 0; j < spline.getGridX2().getNumberOfKnots() / 2; j++) { + swapKnots(i, j, i, spline.getGridX2().getNumberOfKnots() - 1 - j); + } + } + } else { // reorder knots for the C side Y == - old U, Z == old V + for (int32_t i = 0; i < spline.getGridX1().getNumberOfKnots() / 2; i++) { + for (int32_t j = 0; j < spline.getGridX2().getNumberOfKnots(); j++) { + swapKnots(i, j, spline.getGridX1().getNumberOfKnots() - 1 - i, j); + } + } + } + + // correct sign of the parameters due to the coordinate swaps + + for (int32_t iKnot = 0; iKnot < spline.getNumberOfKnots(); iKnot++) { + // new grid directions for all corrections + for (int iDim = 0; iDim < nDim; iDim++) { + if (isAside) { + data[nKnotParameters * iKnot + nDim * 1 + iDim] *= -1; // invert Z derivatives on A side + } else { + data[nKnotParameters * iKnot + nDim * 2 + iDim] *= -1; // invert Y derivatives on C side + } + data[nKnotParameters * iKnot + nDim * 3 + iDim] *= -1; // invert cross derivatives on both sides + } + // new correction directions + if (iSpline == 0) { // dX,dU,dV -> dX,dY,dZ + if (isAside) { + data[nKnotParameters * iKnot + nDim * 0 + 2] *= -1; // invert correction in Z + data[nKnotParameters * iKnot + nDim * 1 + 2] *= -1; // invert correction in Z Z-derivative + data[nKnotParameters * iKnot + nDim * 2 + 2] *= -1; // invert correction in Z Y-derivative + data[nKnotParameters * iKnot + nDim * 3 + 2] *= -1; // invert correction in Z cross derivative + } else { + data[nKnotParameters * iKnot + nDim * 0 + 1] *= -1; // invert correction in Y + data[nKnotParameters * iKnot + nDim * 1 + 1] *= -1; // invert correction in Y Z-derivative + data[nKnotParameters * iKnot + nDim * 2 + 1] *= -1; // invert correction in Y Y-derivative + data[nKnotParameters * iKnot + nDim * 3 + 1] *= -1; // invert correction in Y cross derivative + } + } else if (iSpline == 2) { // dU,dV at real U,V -> dY,dZ at real Y,Z + if (isAside) { + data[nKnotParameters * iKnot + nDim * 0 + 1] *= -1; // invert correction in Z + data[nKnotParameters * iKnot + nDim * 1 + 1] *= -1; // invert correction in Z Z-derivative + data[nKnotParameters * iKnot + nDim * 2 + 1] *= -1; // invert correction in Z Y-derivative + data[nKnotParameters * iKnot + nDim * 3 + 1] *= -1; // invert correction in Z cross derivative + } else { + data[nKnotParameters * iKnot + nDim * 0 + 0] *= -1; // invert correction in Y + data[nKnotParameters * iKnot + nDim * 1 + 0] *= -1; // invert correction in Y Z-derivative + data[nKnotParameters * iKnot + nDim * 2 + 0] *= -1; // invert correction in Y Y-derivative + data[nKnotParameters * iKnot + nDim * 3 + 0] *= -1; // invert correction in Y cross derivative + } + } + } + + } // iSpline + } // iRow + } // iSector + + // set the class version to the current one + mClassVersion = 4; +} + void TPCFastSpaceChargeCorrection::setFutureBufferAddress(char* futureFlatBufferPtr) { /// Sets a future location of the external flat buffer before moving it to this location (i.e. when copying to GPU). @@ -186,9 +432,9 @@ void TPCFastSpaceChargeCorrection::setFutureBufferAddress(char* futureFlatBuffer sp.setFutureBufferAddress(newSplineBuf); } mScenarioPtr = relocatePointer(oldBuffer, newBuffer, mScenarioPtr); - mSplineData[0] = relocatePointer(oldBuffer, newBuffer, mSplineData[0]); - mSplineData[1] = relocatePointer(oldBuffer, newBuffer, mSplineData[1]); - mSplineData[2] = relocatePointer(oldBuffer, newBuffer, mSplineData[2]); + mCorrectionData[0] = relocatePointer(oldBuffer, newBuffer, mCorrectionData[0]); + mCorrectionData[1] = relocatePointer(oldBuffer, newBuffer, mCorrectionData[1]); + mCorrectionData[2] = relocatePointer(oldBuffer, newBuffer, mCorrectionData[2]); FlatObject::setFutureBufferAddress(futureFlatBufferPtr); } @@ -199,7 +445,7 @@ void TPCFastSpaceChargeCorrection::print() const mGeo.print(); LOG(info) << " mNumberOfScenarios = " << mNumberOfScenarios; LOG(info) << " mTimeStamp = " << mTimeStamp; - LOG(info) << " mDataSizeBytes = " << mDataSizeBytes[0] << " " << mDataSizeBytes[1] << " " << mDataSizeBytes[2]; + LOG(info) << " mCorrectionDataSize = " << mCorrectionDataSize[0] << " " << mCorrectionDataSize[1] << " " << mCorrectionDataSize[2]; if (mScenarioPtr) { for (int32_t i = 0; i < mNumberOfScenarios; i++) { @@ -213,7 +459,7 @@ void TPCFastSpaceChargeCorrection::print() const for (int32_t ir = 0; ir < mGeo.getNumberOfRows(); ir++) { LOG(info) << "sector " << is << " row " << ir << ": "; const SplineType& spline = getSpline(is, ir); - const float* d = getSplineData(is, ir); + const float* d = getCorrectionData(is, ir); int32_t k = 0; for (int32_t i = 0; i < spline.getGridX1().getNumberOfKnots(); i++) { for (int32_t j = 0; j < spline.getGridX2().getNumberOfKnots(); j++, k++) { @@ -250,7 +496,6 @@ void TPCFastSpaceChargeCorrection::startConstruction(const TPCFastTransformGeo& assert(mConstructionScenarios != nullptr); for (int32_t i = 0; i < mGeo.getNumberOfSectors(); i++) { - mSectorInfo[i].vMax1 = 0.; for (int32_t j = 0; j < mGeo.getNumberOfRows(); j++) { auto& row = mSectorRowInfos[mGeo.getMaxNumberOfRows() * i + j]; row.splineScenarioID = -1; @@ -276,8 +521,8 @@ void TPCFastSpaceChargeCorrection::startConstruction(const TPCFastTransformGeo& mScenarioPtr = nullptr; for (int32_t s = 0; s < 3; s++) { - mSplineData[s] = nullptr; - mDataSizeBytes[s] = 0; + mCorrectionData[s] = nullptr; + mCorrectionDataSize[s] = 0; } mClassVersion = 4; } @@ -338,20 +583,20 @@ void TPCFastSpaceChargeCorrection::finishConstruction() scBufferSize = alignSize(scBufferSize + sp.getFlatBufferSize(), sp.getBufferAlignmentBytes()); } size_t bufferSize = scBufferOffsets[0] + scBufferSize; - size_t splineDataOffset[3]; + size_t correctionDataOffset[3]; for (int32_t is = 0; is < 3; is++) { - splineDataOffset[is] = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); - mDataSizeBytes[is] = 0; + correctionDataOffset[is] = alignSize(bufferSize, SplineType::getParameterAlignmentBytes()); + mCorrectionDataSize[is] = 0; for (int32_t i = 0; i < mGeo.getNumberOfSectors(); i++) { for (int32_t j = 0; j < mGeo.getNumberOfRows(); j++) { SectorRowInfo& row = getSectorRowInfo(i, j); SplineType& spline = mConstructionScenarios[row.splineScenarioID]; - row.dataOffsetBytes[is] = alignSize(mDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); - mDataSizeBytes[is] = row.dataOffsetBytes[is] + spline.getSizeOfParameters(); + row.dataOffsetBytes[is] = alignSize(mCorrectionDataSize[is], SplineType::getParameterAlignmentBytes()); + mCorrectionDataSize[is] = row.dataOffsetBytes[is] + spline.getSizeOfParameters(); } } - mDataSizeBytes[is] = alignSize(mDataSizeBytes[is], SplineType::getParameterAlignmentBytes()); - bufferSize = splineDataOffset[is] + mDataSizeBytes[is]; + mCorrectionDataSize[is] = alignSize(mCorrectionDataSize[is], SplineType::getParameterAlignmentBytes()); + bufferSize = correctionDataOffset[is] + mCorrectionDataSize[is]; } FlatObject::finishConstruction(bufferSize); @@ -366,7 +611,7 @@ void TPCFastSpaceChargeCorrection::finishConstruction() } for (int32_t is = 0; is < 3; is++) { - mSplineData[is] = reinterpret_cast(mFlatBufferPtr + splineDataOffset[is]); + mCorrectionData[is] = reinterpret_cast(mFlatBufferPtr + correctionDataOffset[is]); } releaseConstructionMemory(); @@ -380,13 +625,11 @@ GPUd() void TPCFastSpaceChargeCorrection::setNoCorrection() // initialise all corrections to 0. for (int32_t sector = 0; sector < mGeo.getNumberOfSectors(); sector++) { - getSectorInfo(sector).vMax1 = mGeo.getTPCzLength(); - for (int32_t row = 0; row < mGeo.getNumberOfRows(); row++) { const SplineType& spline = getSpline(sector, row); for (int32_t is = 0; is < 3; is++) { - float* data = getSplineData(sector, row, is); + float* data = getCorrectionData(sector, row, is); int32_t nPar = spline.getNumberOfParameters(); if (is == 1) { nPar = nPar / 3; diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index ffbc8691ea268..b4fab68b91542 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -39,11 +39,9 @@ namespace gpu class TPCFastSpaceChargeCorrection : public FlatObject { public: - /// - /// \brief The struct contains necessary info for TPC padrow - /// - struct RowInfo { - ClassDefNV(RowInfo, 1); + // obsolete structure, declared here only for backward compatibility + struct SliceInfo { + ClassDefNV(SliceInfo, 2); }; struct GridInfo { @@ -144,11 +142,6 @@ class TPCFastSpaceChargeCorrection : public FlatObject ClassDefNV(SectorRowInfo, 2); }; - struct SectorInfo { - float vMax1{0.f}; ///< Max value of V coordinate - ClassDefNV(SectorInfo, 1); - }; - typedef Spline2D SplineTypeXYZ; typedef Spline2D SplineTypeInvX; typedef Spline2D SplineTypeInvYZ; @@ -188,6 +181,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// Moving the class with its external buffer to another location + void setActualBufferAddressOld(char* actualFlatBufferPtr); void setActualBufferAddress(char* actualFlatBufferPtr); void setFutureBufferAddress(char* futureFlatBufferPtr); @@ -215,10 +209,6 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// Sets the time stamp of the current calibaration GPUd() void setTimeStamp(int64_t v) { mTimeStamp = v; } - /// Set safety marging for the interpolation around the TPC row. - /// Outside of this area the interpolation returns the boundary values. - GPUd() void setInterpolationSafetyMargin(float val) { fInterpolationSafetyMargin = val; } - /// Gives const pointer to a spline GPUd() const SplineType& getSpline(int32_t sector, int32_t row) const; @@ -226,10 +216,10 @@ class TPCFastSpaceChargeCorrection : public FlatObject GPUd() SplineType& getSpline(int32_t sector, int32_t row); /// Gives pointer to spline data - GPUd() float* getSplineData(int32_t sector, int32_t row, int32_t iSpline = 0); + GPUd() float* getCorrectionData(int32_t sector, int32_t row, int32_t iSpline = 0); /// Gives pointer to spline data - GPUd() const float* getSplineData(int32_t sector, int32_t row, int32_t iSpline = 0) const; + GPUd() const float* getCorrectionData(int32_t sector, int32_t row, int32_t iSpline = 0) const; /// Gives const pointer to a spline for the inverse X correction GPUd() const SplineTypeInvX& getSplineInvX(int32_t sector, int32_t row) const; @@ -238,10 +228,10 @@ class TPCFastSpaceChargeCorrection : public FlatObject GPUd() SplineTypeInvX& getSplineInvX(int32_t sector, int32_t row); /// Gives pointer to spline data for the inverse X correction - GPUd() float* getSplineDataInvX(int32_t sector, int32_t row); + GPUd() float* getCorrectionDataInvX(int32_t sector, int32_t row); /// Gives pointer to spline data for the inverse X correction - GPUd() const float* getSplineDataInvX(int32_t sector, int32_t row) const; + GPUd() const float* getCorrectionDataInvX(int32_t sector, int32_t row) const; /// Gives const pointer to a spline for the inverse YZ correction GPUd() const SplineTypeInvYZ& getSplineInvYZ(int32_t sector, int32_t row) const; @@ -250,10 +240,10 @@ class TPCFastSpaceChargeCorrection : public FlatObject GPUd() SplineTypeInvYZ& getSplineInvYZ(int32_t sector, int32_t row); /// Gives pointer to spline data for the inverse YZ correction - GPUd() float* getSplineDataInvYZ(int32_t sector, int32_t row); + GPUd() float* getCorrectionDataInvYZ(int32_t sector, int32_t row); /// Gives pointer to spline data for the inverse YZ correction - GPUd() const float* getSplineDataInvYZ(int32_t sector, int32_t row) const; + GPUd() const float* getCorrectionDataInvYZ(int32_t sector, int32_t row) const; /// _______________ The main method: cluster correction _______________________ /// @@ -297,24 +287,6 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// Gives the time stamp of the current calibaration parameters int64_t getTimeStamp() const { return mTimeStamp; } - /// Gives the interpolation safety marging around the TPC row. - GPUd() float getInterpolationSafetyMargin() const { return fInterpolationSafetyMargin; } - - /// Gives TPC row info - GPUd() const RowInfo& getRowInfo(int32_t row) const { return mRowInfos[row]; } - - /// Gives TPC sector info - GPUd() const SectorInfo& getSectorInfo(int32_t sector) const - { - return mSectorInfo[sector]; - } - - /// Gives TPC sector info - GPUd() SectorInfo& getSectorInfo(int32_t sector) - { - return mSectorInfo[sector]; - } - /// Gives TPC sector & row info GPUd() const SectorRowInfo& getSectorRowInfo(int32_t sector, int32_t row) const { @@ -351,30 +323,24 @@ class TPCFastSpaceChargeCorrection : public FlatObject int32_t mNumberOfScenarios; ///< Number of approximation spline scenarios - SectorInfo mSectorInfo[TPCFastTransformGeo::getNumberOfSectors()]; ///< SectorInfo array - SplineType* mScenarioPtr; //! (transient!!) pointer to spline scenarios /// _______________ Calibration data _______________________________________________ int64_t mTimeStamp; ///< time stamp of the current calibration - char* mSplineData[3]; //! (transient!!) pointer to the spline data in the flat buffer + char* mCorrectionData[3]; //! (transient!!) pointer to the spline data in the flat buffer - size_t mDataSizeBytes[3]; ///< size of the data for one sector in the flat buffer - - float fInterpolationSafetyMargin{0.1f}; // 10% area around the TPC row. Outside of this area the interpolation returns the boundary values. + size_t mCorrectionDataSize[3]; ///< size of the data per transformation (direct, inverseX, inverse YZ) in the flat buffer /// Class version. It is used to read older versions from disc. /// The default version 3 is the one before this field was introduced. /// The actual version must be set in startConstruction(). int32_t mClassVersion{3}; - RowInfo mRowInfos[TPCFastTransformGeo::getMaxNumberOfRows()]; ///< RowInfo array - SectorRowInfo mSectorRowInfos[TPCFastTransformGeo::getNumberOfSectors() * TPCFastTransformGeo::getMaxNumberOfRows()]; ///< SectorRowInfo array - ClassDefNV(TPCFastSpaceChargeCorrection, 5); + ClassDefNV(TPCFastSpaceChargeCorrection, 4); }; /// ==================================================== @@ -393,16 +359,16 @@ GPUdi() TPCFastSpaceChargeCorrection::SplineType& TPCFastSpaceChargeCorrection:: return mScenarioPtr[getSectorRowInfo(sector, row).splineScenarioID]; } -GPUdi() float* TPCFastSpaceChargeCorrection::getSplineData(int32_t sector, int32_t row, int32_t iSpline) +GPUdi() float* TPCFastSpaceChargeCorrection::getCorrectionData(int32_t sector, int32_t row, int32_t iSpline) { /// Gives pointer to spline data - return reinterpret_cast(mSplineData[iSpline] + getSectorRowInfo(sector, row).dataOffsetBytes[iSpline]); + return reinterpret_cast(mCorrectionData[iSpline] + getSectorRowInfo(sector, row).dataOffsetBytes[iSpline]); } -GPUdi() const float* TPCFastSpaceChargeCorrection::getSplineData(int32_t sector, int32_t row, int32_t iSpline) const +GPUdi() const float* TPCFastSpaceChargeCorrection::getCorrectionData(int32_t sector, int32_t row, int32_t iSpline) const { /// Gives pointer to spline data - return reinterpret_cast(mSplineData[iSpline] + getSectorRowInfo(sector, row).dataOffsetBytes[iSpline]); + return reinterpret_cast(mCorrectionData[iSpline] + getSectorRowInfo(sector, row).dataOffsetBytes[iSpline]); } GPUdi() TPCFastSpaceChargeCorrection::SplineTypeInvX& TPCFastSpaceChargeCorrection::getSplineInvX(int32_t sector, int32_t row) @@ -417,16 +383,16 @@ GPUdi() const TPCFastSpaceChargeCorrection::SplineTypeInvX& TPCFastSpaceChargeCo return reinterpret_cast(getSpline(sector, row)); } -GPUdi() float* TPCFastSpaceChargeCorrection::getSplineDataInvX(int32_t sector, int32_t row) +GPUdi() float* TPCFastSpaceChargeCorrection::getCorrectionDataInvX(int32_t sector, int32_t row) { /// Gives pointer to spline data for the inverse X correction - return getSplineData(sector, row, 1); + return getCorrectionData(sector, row, 1); } -GPUdi() const float* TPCFastSpaceChargeCorrection::getSplineDataInvX(int32_t sector, int32_t row) const +GPUdi() const float* TPCFastSpaceChargeCorrection::getCorrectionDataInvX(int32_t sector, int32_t row) const { /// Gives pointer to spline data for the inverse X correction - return getSplineData(sector, row, 1); + return getCorrectionData(sector, row, 1); } GPUdi() TPCFastSpaceChargeCorrection::SplineTypeInvYZ& TPCFastSpaceChargeCorrection::getSplineInvYZ(int32_t sector, int32_t row) @@ -441,16 +407,16 @@ GPUdi() const TPCFastSpaceChargeCorrection::SplineTypeInvYZ& TPCFastSpaceChargeC return reinterpret_cast(getSpline(sector, row)); } -GPUdi() float* TPCFastSpaceChargeCorrection::getSplineDataInvYZ(int32_t sector, int32_t row) +GPUdi() float* TPCFastSpaceChargeCorrection::getCorrectionDataInvYZ(int32_t sector, int32_t row) { /// Gives pointer to spline data for the inverse YZ correction - return getSplineData(sector, row, 2); + return getCorrectionData(sector, row, 2); } -GPUdi() const float* TPCFastSpaceChargeCorrection::getSplineDataInvYZ(int32_t sector, int32_t row) const +GPUdi() const float* TPCFastSpaceChargeCorrection::getCorrectionDataInvYZ(int32_t sector, int32_t row) const { /// Gives pointer to spline data for the inverse YZ correction - return getSplineData(sector, row, 2); + return getCorrectionData(sector, row, 2); } GPUdi() std::array TPCFastSpaceChargeCorrection::convLocalToGrid(int32_t sector, int32_t row, float y, float z) const @@ -518,7 +484,7 @@ GPUdi() std::array TPCFastSpaceChargeCorrection::getCorrectionLocal(in { const auto& info = getSectorRowInfo(sector, row); const SplineType& spline = getSpline(sector, row); - const float* splineData = getSplineData(sector, row); + const float* splineData = getCorrectionData(sector, row); auto val = convLocalToGrid(sector, row, y, z); @@ -536,7 +502,7 @@ GPUdi() float TPCFastSpaceChargeCorrection::getCorrectionXatRealYZ(int32_t secto const auto& info = getSectorRowInfo(sector, row); auto val = convRealLocalToGrid(sector, row, realY, realZ); float dx = 0; - getSplineInvX(sector, row).interpolateAtU(getSplineDataInvX(sector, row), val[0], val[1], &dx); + getSplineInvX(sector, row).interpolateAtU(getCorrectionDataInvX(sector, row), val[0], val[1], &dx); dx = val[2] * GPUCommonMath::Clamp(dx, info.minCorr[0], info.maxCorr[0]); return dx; } @@ -546,7 +512,7 @@ GPUdi() std::array TPCFastSpaceChargeCorrection::getCorrectionYZatReal auto val = convRealLocalToGrid(sector, row, realY, realZ); const auto& info = getSectorRowInfo(sector, row); float dyz[2]; - getSplineInvYZ(sector, row).interpolateAtU(getSplineDataInvYZ(sector, row), val[0], val[1], dyz); + getSplineInvYZ(sector, row).interpolateAtU(getCorrectionDataInvYZ(sector, row), val[0], val[1], dyz); dyz[0] = val[2] * GPUCommonMath::Clamp(dyz[0], info.minCorr[1], info.maxCorr[1]); dyz[1] = val[2] * GPUCommonMath::Clamp(dyz[1], info.minCorr[2], info.maxCorr[2]); return {dyz[0], dyz[1]}; diff --git a/GPU/TPCFastTransformation/TPCFastTransform.h b/GPU/TPCFastTransformation/TPCFastTransform.h index 60f5952e6a1fc..64fdba9d94bd3 100644 --- a/GPU/TPCFastTransformation/TPCFastTransform.h +++ b/GPU/TPCFastTransformation/TPCFastTransform.h @@ -337,7 +337,7 @@ class TPCFastTransform : public FlatObject GPUd() void TransformLocal(int32_t sector, int32_t row, float& x, float& y, float& z, const TPCFastTransform* ref, const TPCFastTransform* ref2, float scale, float scale2, int32_t scaleMode) const; - ClassDefNV(TPCFastTransform, 4); + ClassDefNV(TPCFastTransform, 5); }; // ======================================================================= diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx b/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx index 5b2dcc8da82d5..2fe773a76e4d3 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx @@ -28,12 +28,10 @@ using namespace o2::gpu; TPCFastTransformGeo::TPCFastTransformGeo() { // Default Constructor: creates an empty uninitialized object - double dAlpha = 2. * M_PI / (NumberOfSectorsA); for (int32_t i = 0; i < NumberOfSectors; i++) { - SectorInfo& s = mSectorInfos[i]; - double alpha = dAlpha * (i + 0.5); - s.sinAlpha = sin(alpha); - s.cosAlpha = cos(alpha); + double angle = (i + 0.5) * 2. * M_PI / NumberOfSectorsA; + mSectorInfos[i].sinAlpha = sin(angle); + mSectorInfos[i].cosAlpha = cos(angle); } mSectorInfos[NumberOfSectors] = SectorInfo{}; diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.h b/GPU/TPCFastTransformation/TPCFastTransformGeo.h index fc28bbef33602..09793b6677d83 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.h +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.h @@ -19,6 +19,8 @@ #include "GPUCommonDef.h" #include "GPUCommonArray.h" +#include "GPUCommonMath.h" + #ifndef GPUCA_GPUCODE_DEVICE #include #include "GPUCommonRtypes.h" @@ -34,6 +36,7 @@ namespace gpu /// class TPCFastTransformGeo { + public: /// The struct contains necessary info for TPC sector struct SectorInfo { @@ -61,7 +64,7 @@ class TPCFastTransformGeo /// get width in Y GPUd() float getYwidth() const { return -2.f * yMin; } - ClassDefNV(RowInfo, 1); + ClassDefNV(RowInfo, 2); }; /// _____________ Constructors / destructors __________________________ @@ -187,6 +190,11 @@ class TPCFastTransformGeo SectorInfo mSectorInfos[NumberOfSectors + 1]; ///< array of sector information [fixed size] RowInfo mRowInfos[MaxNumberOfRows + 1]; ///< array of row information [fixed size] + public: + struct SliceInfo { // legacy, needed only for schema evolution + ClassDefNV(SliceInfo, 2); + }; + ClassDefNV(TPCFastTransformGeo, 3); }; diff --git a/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h b/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h index 916695a3be1c7..284d5f229d5e0 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h +++ b/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h @@ -58,19 +58,36 @@ #pragma link C++ class o2::gpu::SemiregularSpline2D3D + ; #pragma link C++ class o2::gpu::IrregularSpline2D3DCalibrator + ; -#pragma link C++ class o2::gpu::TPCFastTransformGeo + ; +#pragma link C++ class o2::gpu::TPCFastTransformGeo::SliceInfo + ; #pragma link C++ class o2::gpu::TPCFastTransformGeo::SectorInfo + ; + +#pragma link C++ class o2::gpu::TPCFastTransformGeo + ; +#pragma read \ + sourceClass = "o2::gpu::TPCFastTransformGeo" targetClass = "o2::gpu::TPCFastTransformGeo" source = "float mTPCzLengthA; float mTPCzLengthC; float mTPCalignmentZ; float mScaleVtoSVsideA; float mScaleVtoSVsideC; float mScaleSVtoVsideA; float mScaleSVtoVsideC;" version = "[-1]" target = "mTPCzLength" code = "{ mTPCzLength = onfile.mTPCzLengthA; }"; + +#pragma read \ + sourceClass = "o2::gpu::TPCFastTransformGeo" targetClass = "o2::gpu::TPCFastTransformGeo" source = "o2::gpu::TPCFastTransformGeo::SliceInfo mSliceInfos[37]" version = "[1-]" target = "" code = "{}"; + #pragma link C++ class o2::gpu::TPCFastTransformGeo::RowInfo + ; +#pragma read \ + sourceClass = "o2::gpu::TPCFastTransformGeo::RowInfo" targetClass = "o2::gpu::TPCFastTransformGeo::RowInfo" source = "float u0; float scaleUtoSU; float scaleSUtoU" version = "[-2]" target = "yMin" code = "{ yMin = onfile.u0; }" #pragma link C++ class o2::gpu::TPCFastTransform + ; #pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrectionMap + ; -#pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::RowInfo + ; #pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection + ; -#pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::SectorInfo + ; +#pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::SliceInfo + ; #pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::SectorRowInfo + ; #pragma link C++ class o2::gpu::TPCFastSpaceChargeCorrection::GridInfo + ; +#pragma read \ + sourceClass = "o2::gpu::TPCFastSpaceChargeCorrection" targetClass = "o2::gpu::TPCFastSpaceChargeCorrection" source = "o2::gpu::TPCFastSpaceChargeCorrection::SliceInfo mSliceInfo[36]" version = "[-3]" target = "" code = "{}"; + +#pragma read \ + sourceClass = "o2::gpu::TPCFastSpaceChargeCorrection" targetClass = "o2::gpu::TPCFastSpaceChargeCorrection" source = "size_t mSliceDataSizeBytes[3]" version = "[-3]" target = "mCorrectionDataSize" code = "{ for (int i=0; i<3; i++) mCorrectionDataSize[i] = onfile.mSliceDataSizeBytes[i] * 36; }"; + +#pragma read \ + sourceClass = "o2::gpu::TPCFastSpaceChargeCorrection" targetClass = "o2::gpu::TPCFastSpaceChargeCorrection" source = "float fInterpolationSafetyMargin" version = "[-3]" target = "" code = "{}"; #pragma link C++ class o2::gpu::CorrectionMapsHelper + ; #pragma link C++ struct o2::gpu::MultivariatePolynomialContainer + ; diff --git a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C index 3cb4812abafc1..baaeca90202d5 100644 --- a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C +++ b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C @@ -18,7 +18,7 @@ /// how to run the macro: /// -/// root -l TPCFastTransformInit.C'("debugVoxRes.root")' +/// root -l TPCFastTransformInit.C'("VoxRes.root", "VoxResInv.root")' /// #if !defined(__CLING__) || defined(__ROOTCLING__) @@ -45,7 +45,7 @@ using namespace o2::tpc; using namespace o2::gpu; void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* fileNameInv = "debugVoxResInv.root", - const char* outFileName = "TPCFastTransform_VoxRes.root", bool useSmoothed = false, bool invertSigns = false) + const char* outFileName = "TPCFastTransform_VoxRes.root", bool useSmoothed = false, bool invertSigns = false, bool doDebug = true) { // Initialise TPCFastTransform object from "voxRes" tree of @@ -56,9 +56,9 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* To visiualise the results: root -l transformDebug.root - all->Draw("cx:y:z","sec==0&&iRow==10","") - grid->Draw("cx:y:z","sec==0&&iRow==10","same") - vox->Draw("vx:y:z","sec==0&&iRow==10","same") + all->Draw("cx:y:z","sec==0&&row==10","") + grid->Draw("cx:y:z","sec==0&&row==10","same") + vox->Draw("vx:y:z","sec==0&&row==10","same") points->Draw("px:y:z","sec==0&&row==10","same") */ @@ -112,6 +112,38 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* trackResiduals.setZ2XBinning(z2xBins); trackResiduals.init(); + std::cout << "create fast transformation ... " << std::endl; + + auto* helper = o2::tpc::TPCFastTransformHelperO2::instance(); + + o2::tpc::TPCFastSpaceChargeCorrectionHelper* corrHelper = o2::tpc::TPCFastSpaceChargeCorrectionHelper::instance(); + + corrHelper->setNthreadsToMaximum(); + // corrHelper->setNthreads(1); + + if (debugMirrorAdata2C) { + corrHelper->setDebugMirrorAdata2C(); + } + // corrHelper->setDebugUseVoxelCenters(); + + o2::gpu::TPCFastSpaceChargeCorrectionMap mapDirect(0, 0), mapInverse(0, 0); + + auto corrPtr = corrHelper->createFromTrackResiduals(trackResiduals, voxResTree, voxResTreeInverse, useSmoothed, invertSigns, + &mapDirect, &mapInverse); + + std::unique_ptr fastTransform( + helper->create(0, *corrPtr)); + + std::cout << "... create fast transformation completed " << std::endl; + + if (*outFileName) { + fastTransform->writeToFile(outFileName, "ccdb_object"); + } + + if (!doDebug) { + return; + } + { // debug output std::cout << " ===== input track residuals ==== " << std::endl; @@ -144,40 +176,12 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* std::cout << " ==================================== " << std::endl; } - std::cout << "create fast transformation ... " << std::endl; - - auto* helper = o2::tpc::TPCFastTransformHelperO2::instance(); - - o2::tpc::TPCFastSpaceChargeCorrectionHelper* corrHelper = o2::tpc::TPCFastSpaceChargeCorrectionHelper::instance(); - - corrHelper->setNthreadsToMaximum(); - corrHelper->setNthreads(1); - - if (debugMirrorAdata2C) { - corrHelper->setDebugMirrorAdata2C(); - } - // corrHelper->setDebugUseVoxelCenters(); - - o2::gpu::TPCFastSpaceChargeCorrectionMap mapDirect(0, 0), mapInverse(0, 0); - - auto corrPtr = corrHelper->createFromTrackResiduals(trackResiduals, voxResTree, voxResTreeInverse, useSmoothed, invertSigns, - &mapDirect, &mapInverse); - - std::unique_ptr fastTransform( - helper->create(0, *corrPtr)); - - std::cout << "... create fast transformation completed " << std::endl; - - if (*outFileName) { - fastTransform->writeToFile(outFileName, "ccdb_object"); - } - - if (1) { // read transformation from the file - - // const char* fileName = "master/out.root"; + if (1) { // read transformation from the output file to verify the io const char* fileName = outFileName; + // fileName = "~/test/master/TPCFastTransform_VoxRes.root"; + std::cout << "load corrections from file " << fileName << std::endl; fastTransform->cloneFromObject(*TPCFastTransform::loadFromFile(fileName, "ccdb_object"), nullptr); @@ -488,11 +492,6 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* auto [yMin, yMax] = geo.getRowInfo(iRow).getYrange(); auto [zMin, zMax] = geo.getZrange(iSector); - points[0].push_back(yMin); - points[0].push_back(yMax); - points[1].push_back(zMin); - points[1].push_back(zMax); - for (int32_t iu = 0; iu < gridY.getNumberOfKnots(); iu++) { auto [y, z] = corr.convGridToLocal(iSector, iRow, gridY.getKnot(iu).getU(), 0.); knots[0].push_back(y); @@ -508,12 +507,19 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* std::sort(knots[iyz].begin(), knots[iyz].end()); std::sort(points[iyz].begin(), points[iyz].end()); int32_t n = points[iyz].size(); + int nsteps = (iyz == 0) ? 10 : 5; for (int32_t i = 0; i < n - 1; i++) { - double d = (points[iyz][i + 1] - points[iyz][i]) / 10.; - for (int32_t ii = 1; ii < 10; ii++) { + double d = (points[iyz][i + 1] - points[iyz][i]) / nsteps; + for (int32_t ii = 1; ii < nsteps; ii++) { points[iyz].push_back(points[iyz][i] + d * ii); } } + } + points[0].push_back(yMin); + points[0].push_back(yMax); + points[1].push_back(zMin); + points[1].push_back(zMax); + for (int32_t iyz = 0; iyz <= 1; iyz++) { std::sort(points[iyz].begin(), points[iyz].end()); } From ec3f6ac5a5c209d0167dd68651e57dc6d07f5f03 Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Wed, 15 Oct 2025 16:36:51 +0200 Subject: [PATCH 518/701] Fix compiler-warnings, codechecker violations and compilation Fix compiler warnings on MacOS Fix compiler warning, memmove must only operate on trivial types Fix coding rule violations TPC Splines: compilation fix --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 15 ++++---- .../macro/createTPCSpaceChargeCorrection.C | 34 +++++++++---------- .../TPCFastSpaceChargeCorrection.cxx | 2 +- .../TPCFastTransformationLinkDef_O2.h | 4 +-- .../macro/generateTPCCorrectionNTuple.C | 21 +++++------- 5 files changed, 36 insertions(+), 40 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index faba4f2ce065e..7622c40001e1d 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -690,9 +690,9 @@ std::unique_ptr TPCFastSpaceChargeCorrect LOG(warning) << directionName << " correction: error N " << nErrors << "fitted voxel position is outside the voxel: " << " sector " << iSector << " row " << iRow << " bin y " << iy << " bin z " << iz << msg.str(); - maxError[0] = GPUCommonMath::Max(maxError[0], fabs(data.mX - x) / dx); - maxError[1] = GPUCommonMath::Max(maxError[1], fabs(data.mY - vox.mY) / vox.mDy); - maxError[2] = GPUCommonMath::Max(maxError[2], fabs(data.mZ - vox.mZ) / vox.mDz); + maxError[0] = GPUCommonMath::Max(maxError[0], fabs(data.mX - x) / dx); + maxError[1] = GPUCommonMath::Max(maxError[1], fabs(data.mY - vox.mY) / vox.mDy); + maxError[2] = GPUCommonMath::Max(maxError[2], fabs(data.mZ - vox.mZ) / vox.mDz); } mutex.unlock(); } @@ -794,12 +794,15 @@ std::unique_ptr TPCFastSpaceChargeCorrect auto addEdge = [&](int iy1, int iz1, int iy2, int iz2, int nPoints) { // add n points on the edge between two voxels excluding the voxel points - if (nPoints < 1) + if (nPoints < 1) { return; - if (iy1 < 0 || iy1 >= nY2Xbins || iz1 < 0 || iz1 >= nZ2Xbins) + } + if (iy1 < 0 || iy1 >= nY2Xbins || iz1 < 0 || iz1 >= nZ2Xbins) { return; - if (iy2 < 0 || iy2 >= nY2Xbins || iz2 < 0 || iz2 >= nZ2Xbins) + } + if (iy2 < 0 || iy2 >= nY2Xbins || iz2 < 0 || iz2 >= nZ2Xbins) { return; + } auto& data1 = vSectorData[iSector * nRows + iRow][iy1 * nZ2Xbins + iz1]; auto& vox1 = vRowVoxels[iy1 * nZ2Xbins + iz1]; auto& data2 = vSectorData[iSector * nRows + iRow][iy2 * nZ2Xbins + iz2]; diff --git a/Detectors/TPC/reconstruction/macro/createTPCSpaceChargeCorrection.C b/Detectors/TPC/reconstruction/macro/createTPCSpaceChargeCorrection.C index 723cf2ee30491..af066598d1317 100644 --- a/Detectors/TPC/reconstruction/macro/createTPCSpaceChargeCorrection.C +++ b/Detectors/TPC/reconstruction/macro/createTPCSpaceChargeCorrection.C @@ -397,10 +397,9 @@ void debugInterpolation(utils::TreeStreamRedirector& pcstream, const o2::gpu::TPCFastTransformGeo& geo, TPCFastTransform* fastTransform) { - for (int slice = 0; slice < geo.getNumberOfSlices(); slice += 1) { - // for (int slice = 21; slice < 22; slice += 1) { - std::cout << "debug slice " << slice << " ... " << std::endl; - const o2::gpu::TPCFastTransformGeo::SliceInfo& sliceInfo = geo.getSliceInfo(slice); + for (int sector = 0; sector < geo.getNumberOfSectors(); sector += 1) { + // for (int sector = 21; sector < 22; sector += 1) { + std::cout << "debug sector " << sector << " ... " << std::endl; for (int row = 0; row < geo.getNumberOfRows(); row++) { int nPads = geo.getRowInfo(row).maxPad + 1; @@ -411,28 +410,28 @@ void debugInterpolation(utils::TreeStreamRedirector& pcstream, // non-corrected point fastTransform->setApplyCorrectionOff(); float lx, ly, lz; - fastTransform->Transform(slice, row, pad, time, lx, ly, lz); + fastTransform->Transform(sector, row, pad, time, lx, ly, lz); float gx, gy, gz, r, phi; - geo.convLocalToGlobal(slice, lx, ly, lz, gx, gy, gz); + geo.convLocalToGlobal(sector, lx, ly, lz, gx, gy, gz); r = std::sqrt(lx * lx + ly * ly); phi = std::atan2(gy, gx); fastTransform->setApplyCorrectionOn(); // fast transformation float lxT, lyT, lzT; - fastTransform->Transform(slice, row, pad, time, lxT, lyT, lzT); + fastTransform->Transform(sector, row, pad, time, lxT, lyT, lzT); float gxT, gyT, gzT, rT; - geo.convLocalToGlobal(slice, lxT, lyT, lzT, gxT, gyT, gzT); + geo.convLocalToGlobal(sector, lxT, lyT, lzT, gxT, gyT, gzT); rT = std::sqrt(lxT * lxT + lyT * lyT); // the original correction double gdC[3] = {0, 0, 0}; - Side side = slice < geo.getNumberOfSlicesA() ? Side::A : Side::C; + Side side = sector < geo.getNumberOfSectorsA() ? Side::A : Side::C; if (spaceCharge) { spaceCharge->getCorrections(gx, gy, gz, side, gdC[0], gdC[1], gdC[2]); } float ldxC, ldyC, ldzC; - geo.convGlobalToLocal(slice, gdC[0], gdC[1], gdC[2], ldxC, ldyC, ldzC); + geo.convGlobalToLocal(sector, gdC[0], gdC[1], gdC[2], ldxC, ldyC, ldzC); double rC = std::sqrt((gx + gdC[0]) * (gx + gdC[0]) + (gy + gdC[1]) * (gy + gdC[1])); @@ -466,7 +465,7 @@ void debugInterpolation(utils::TreeStreamRedirector& pcstream, if (spaceChargeExB) { double gdC_ExB[3] = {0, 0, 0}; spaceChargeExB->getCorrections(gx, gy, gz, side, gdC_ExB[0], gdC_ExB[1], gdC_ExB[2]); - geo.convGlobalToLocal(slice, gdC_ExB[0], gdC_ExB[1], gdC_ExB[2], ldxC_ExB, ldyC_ExB, ldzC_ExB); + geo.convGlobalToLocal(sector, gdC_ExB[0], gdC_ExB[1], gdC_ExB[2], ldxC_ExB, ldyC_ExB, ldzC_ExB); } // static distortions @@ -474,18 +473,18 @@ void debugInterpolation(utils::TreeStreamRedirector& pcstream, if (spaceChargeStack) { double gdC_static[3] = {0, 0, 0}; spaceChargeStack->getCorrections(gx, gy, gz, side, gdC_static[0], gdC_static[1], gdC_static[2]); - geo.convGlobalToLocal(slice, gdC_static[0], gdC_static[1], gdC_static[2], ldxC_static, ldyC_static, ldzC_static); + geo.convGlobalToLocal(sector, gdC_static[0], gdC_static[1], gdC_static[2], ldxC_static, ldyC_static, ldzC_static); } // get combined corrections double dx_comb = 0, dy_comb = 0, dz_comb = 0; - getGlobalSpaceChargeCorrectionLinearCombination(slice, gx, gy, gz, dx_comb, dy_comb, dz_comb); + getGlobalSpaceChargeCorrectionLinearCombination(sector, gx, gy, gz, dx_comb, dy_comb, dz_comb); float ldxC_comb, ldyC_comb, ldzC_comb; - geo.convGlobalToLocal(slice, dx_comb, dy_comb, dz_comb, ldxC_comb, ldyC_comb, ldzC_comb); + geo.convGlobalToLocal(sector, dx_comb, dy_comb, dz_comb, ldxC_comb, ldyC_comb, ldzC_comb); pcstream << "fastTransform" // internal coordinates - << "slice=" << slice + << "sector=" << sector << "row=" << row << "pad=" << pad << "time=" << time @@ -613,10 +612,9 @@ void debugGridpoints(utils::TreeStreamRedirector& pcstream, const o2::gpu::TPCFa break; } } - float u = 0.f, v = 0.f; - geo.convLocalToUV(sector, y0, z0, u, v); + float pad = 0.f, time = 0.f; - fastTransform->convUVtoPadTime(sector, row, u, v, pad, time, 0.f); + fastTransform->convLocalToPadTime(sector, row, y0, z0, pad, time, 0.f); if (pad < 0) { continue; } diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx index 5a2cd21deeb2b..5f39e749f73d9 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx @@ -294,7 +294,7 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer // move spline scenarios to the new place in the buffer mScenarioPtr = reinterpret_cast(mFlatBufferPtr); - memmove(mScenarioPtr, oldScenarioPtr, scenariosSize); + memmove((void*)mScenarioPtr, (const void*)oldScenarioPtr, scenariosSize); size_t oldScenariosBufferOffset = alignSize(oldScenariosOffset + scenariosSize, SplineType::getBufferAlignmentBytes()); size_t scenariosBufferOffset = alignSize(scenariosSize, SplineType::getBufferAlignmentBytes()); diff --git a/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h b/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h index 284d5f229d5e0..f1872549a46aa 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h +++ b/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h @@ -66,11 +66,11 @@ sourceClass = "o2::gpu::TPCFastTransformGeo" targetClass = "o2::gpu::TPCFastTransformGeo" source = "float mTPCzLengthA; float mTPCzLengthC; float mTPCalignmentZ; float mScaleVtoSVsideA; float mScaleVtoSVsideC; float mScaleSVtoVsideA; float mScaleSVtoVsideC;" version = "[-1]" target = "mTPCzLength" code = "{ mTPCzLength = onfile.mTPCzLengthA; }"; #pragma read \ - sourceClass = "o2::gpu::TPCFastTransformGeo" targetClass = "o2::gpu::TPCFastTransformGeo" source = "o2::gpu::TPCFastTransformGeo::SliceInfo mSliceInfos[37]" version = "[1-]" target = "" code = "{}"; + sourceClass = "o2::gpu::TPCFastTransformGeo" targetClass = "o2::gpu::TPCFastTransformGeo" source = "o2::gpu::TPCFastTransformGeo::SliceInfo mSliceInfos[37]" version = "[-2]" target = "" code = "{}"; #pragma link C++ class o2::gpu::TPCFastTransformGeo::RowInfo + ; #pragma read \ - sourceClass = "o2::gpu::TPCFastTransformGeo::RowInfo" targetClass = "o2::gpu::TPCFastTransformGeo::RowInfo" source = "float u0; float scaleUtoSU; float scaleSUtoU" version = "[-2]" target = "yMin" code = "{ yMin = onfile.u0; }" + sourceClass = "o2::gpu::TPCFastTransformGeo::RowInfo" targetClass = "o2::gpu::TPCFastTransformGeo::RowInfo" source = "float u0; float scaleUtoSU; float scaleSUtoU" version = "[-1]" target = "yMin" code = "{ yMin = onfile.u0; }" #pragma link C++ class o2::gpu::TPCFastTransform + ; diff --git a/GPU/TPCFastTransformation/macro/generateTPCCorrectionNTuple.C b/GPU/TPCFastTransformation/macro/generateTPCCorrectionNTuple.C index 69b7909cda683..2c8f22e4a3f3b 100644 --- a/GPU/TPCFastTransformation/macro/generateTPCCorrectionNTuple.C +++ b/GPU/TPCFastTransformation/macro/generateTPCCorrectionNTuple.C @@ -82,7 +82,7 @@ void generateTPCCorrectionNTuple(const char* path = "InputSCDensityHistograms.ro const o2::gpu::TPCFastTransformGeo& geo = fastTransform->getGeometry(); TFile* f = new TFile("tpcCorrection.root", "RECREATE"); - TNtuple* nt = new TNtuple("dist", "dist", "sector:row:su:sv:dx:du:dv"); + TNtuple* nt = new TNtuple("dist", "dist", "sector:row:x:y:z:dx:dy:dz"); int32_t nSectors = 1; // fastTransform->getNumberOfSectors(); // for( int32_t sector=0; sectorFill(sector, row, su, sv, dx, du, dv); + float dy = y1 - y; + float dz = z1 - z; + nt->Fill(sector, row, x, y, z, dx, dy, dz); } } } From 1ee89e2ef471b8fa18177ae0873e2185bcff633d Mon Sep 17 00:00:00 2001 From: Sergey Gorbunov Date: Thu, 5 Feb 2026 09:38:08 +0000 Subject: [PATCH 519/701] TPC Splines: keep old cropping scheme --- .../TPCFastSpaceChargeCorrection.cxx | 4 ++-- .../TPCFastSpaceChargeCorrection.h | 11 +++++++++++ 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx index 5f39e749f73d9..241a1fcfc795b 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx @@ -285,8 +285,8 @@ void TPCFastSpaceChargeCorrection::setActualBufferAddress(char* actualFlatBuffer } newSectorRow.resetMaxValues(); - newSectorRow.updateMaxValues(-50.f, -50.f, -50.f); - newSectorRow.updateMaxValues(50.f, 50.f, 50.f); + newSectorRow.updateMaxValues(-100.f, -100.f, -100.f); + newSectorRow.updateMaxValues(100.f, 100.f, 100.f); } } } diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index b4fab68b91542..b1a3d0c35da7c 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -491,9 +491,14 @@ GPUdi() std::array TPCFastSpaceChargeCorrection::getCorrectionLocal(in float dxyz[3]; spline.interpolateAtU(splineData, val[0], val[1], dxyz); + if (CAMath::Abs(dxyz[0]) > 100.f || CAMath::Abs(dxyz[1]) > 100.f || CAMath::Abs(dxyz[2]) > 100.f) { + val[2] = 0.f; // TODO: DR: Protect from FPEs, fix upstream and remove once guaranteed that it is fixed + } + float dx = val[2] * GPUCommonMath::Clamp(dxyz[0], info.minCorr[0], info.maxCorr[0]); float dy = val[2] * GPUCommonMath::Clamp(dxyz[1], info.minCorr[1], info.maxCorr[1]); float dz = val[2] * GPUCommonMath::Clamp(dxyz[2], info.minCorr[2], info.maxCorr[2]); + return {dx, dy, dz}; } @@ -503,6 +508,9 @@ GPUdi() float TPCFastSpaceChargeCorrection::getCorrectionXatRealYZ(int32_t secto auto val = convRealLocalToGrid(sector, row, realY, realZ); float dx = 0; getSplineInvX(sector, row).interpolateAtU(getCorrectionDataInvX(sector, row), val[0], val[1], &dx); + if (CAMath::Abs(dx) > 100.f) { + val[2] = 0.f; // TODO: DR: Protect from FPEs, fix upstream and remove once guaranteed that it is fixed + } dx = val[2] * GPUCommonMath::Clamp(dx, info.minCorr[0], info.maxCorr[0]); return dx; } @@ -513,6 +521,9 @@ GPUdi() std::array TPCFastSpaceChargeCorrection::getCorrectionYZatReal const auto& info = getSectorRowInfo(sector, row); float dyz[2]; getSplineInvYZ(sector, row).interpolateAtU(getCorrectionDataInvYZ(sector, row), val[0], val[1], dyz); + if (CAMath::Abs(dyz[0]) > 100.f || CAMath::Abs(dyz[1]) > 100.f) { + val[2] = 0.f; // TODO: DR: Protect from FPEs, fix upstream and remove once guaranteed that it is fixed + } dyz[0] = val[2] * GPUCommonMath::Clamp(dyz[0], info.minCorr[1], info.maxCorr[1]); dyz[1] = val[2] * GPUCommonMath::Clamp(dyz[1], info.minCorr[2], info.maxCorr[2]); return {dyz[0], dyz[1]}; From 5627eef4497518181f5bb66188602fd717a06740 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sun, 29 Mar 2026 19:10:08 +0200 Subject: [PATCH 520/701] GPU TPC FastTransformation: Do not use std::array --- .../TPCFastSpaceChargeCorrectionHelper.cxx | 31 +++-- .../src/TPCFastTransformHelperO2.cxx | 3 +- GPU/TPCFastTransformation/Spline1DSpec.h | 38 +++--- GPU/TPCFastTransformation/Spline2DSpec.h | 19 ++- .../TPCFastSpaceChargeCorrection.cxx | 6 +- .../TPCFastSpaceChargeCorrection.h | 108 ++++++++++-------- GPU/TPCFastTransformation/TPCFastTransform.h | 94 +++++++-------- .../TPCFastTransformGeo.cxx | 6 +- .../TPCFastTransformGeo.h | 41 ++++--- .../macro/TPCFastTransformInit.C | 6 +- 10 files changed, 178 insertions(+), 174 deletions(-) diff --git a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx index 7622c40001e1d..783c1837590b9 100644 --- a/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx +++ b/Detectors/TPC/calibration/src/TPCFastSpaceChargeCorrectionHelper.cxx @@ -159,7 +159,8 @@ void TPCFastSpaceChargeCorrectionHelper::fillSpaceChargeCorrectionFromMap(TPCFas for (int i = 0; i < nDataPoints; ++i) { o2::gpu::TPCFastSpaceChargeCorrectionMap::CorrectionPoint p = data[i]; // not corrected grid coordinates - auto [gu, gv, scale] = correction.convLocalToGrid(sector, row, p.mY, p.mZ); + float gu, gv, scale; + correction.convLocalToGrid(sector, row, p.mY, p.mZ, gu, gv, scale); if (scale - 1.f > 1.e-6) { // point is outside the grid continue; } @@ -300,7 +301,8 @@ std::unique_ptr TPCFastSpaceChargeCorrectionHelper double dpad = info.maxPad / (6. * (nKnotsY - 1)); for (double pad = 0; pad < info.maxPad + .5 * dpad; pad += dpad) { for (double l = 0.; l < mGeo.getTPCzLength() + .5 * dl; l += dl) { - auto [y, z] = mGeo.convPadDriftLengthToLocal(iSector, iRow, pad, l); + float y, z; + mGeo.convPadDriftLengthToLocal(iSector, iRow, pad, l, y, z); double dx, dy, dz; correctionLocal(iSector, iRow, y, z, dx, dy, dz); mCorrectionMap.addCorrectionPoint(iSector, iRow, @@ -360,7 +362,8 @@ void TPCFastSpaceChargeCorrectionHelper::testGeometry(const TPCFastTransformGeo& for (int pad = 0; pad < nPads; pad++) { const GlobalPadNumber p = mapper.globalPadNumber(PadPos(row, pad)); const PadCentre& c = mapper.padCentre(p); - auto [y, z] = geo.convPadDriftLengthToLocal(0, row, pad, 0.); + float y, z; + geo.convPadDriftLengthToLocal(0, row, pad, 0., y, z); const double dx = x - c.X(); const double dy = y - (-c.Y()); // diferent sign convention for Y coordinate in the map @@ -974,18 +977,20 @@ void TPCFastSpaceChargeCorrectionHelper::initInverse(std::vectorgetCorrectionLocal(sector, row, y, z); + float dxTmp, dyTmp, dzTmp; + corrections[i]->getCorrectionLocal(sector, row, y, z, dxTmp, dyTmp, dzTmp); dx += dxTmp * scaling[i]; dy += dyTmp * scaling[i]; dz += dzTmp * scaling[i]; } - auto [gridU, gridV, scale] = correction.convRealLocalToGrid(sector, row, y + dy, z + dz); + float gridU, gridV, scale; + correction.convRealLocalToGrid(sector, row, y + dy, z + dz, gridU, gridV, scale); dataPointGridU.push_back(gridU); dataPointGridV.push_back(gridV); dataPointF.push_back(scale * dx); @@ -1111,9 +1116,11 @@ void TPCFastSpaceChargeCorrectionHelper::mergeCorrections( float P[nKnotPar3d]; { // direct correction - auto [y, z] = mainCorrection.convGridToLocal(sector, row, u, v); + float y, z; + mainCorrection.convGridToLocal(sector, row, u, v, y, z); // return values: u, v, scaling factor - auto [lu, lv, ls] = corr.convLocalToGrid(sector, row, y, z); + float lu, lv, ls; + corr.convLocalToGrid(sector, row, y, z, lu, lv, ls); ls *= scale; double parscale[4] = {ls, ls * scaleU, ls * scaleV, ls * ls * scaleU * scaleV}; const auto& spl = corr.getSpline(sector, row); @@ -1125,9 +1132,11 @@ void TPCFastSpaceChargeCorrectionHelper::mergeCorrections( } } - auto [y, z] = mainCorrection.convGridToRealLocal(sector, row, u, v); + float y, z; + mainCorrection.convGridToRealLocal(sector, row, u, v, y, z); // return values: u, v, scaling factor - auto [lu, lv, ls] = corr.convRealLocalToGrid(sector, row, y, z); + float lu, lv, ls; + corr.convRealLocalToGrid(sector, row, y, z, lu, lv, ls); ls *= scale; double parscale[4] = {ls, ls * scaleRealU, ls * scaleRealV, ls * ls * scaleRealU * scaleRealV}; diff --git a/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx b/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx index 687d4ce707f11..419ced9fa978e 100644 --- a/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx +++ b/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx @@ -203,7 +203,8 @@ void TPCFastTransformHelperO2::testGeometry(const TPCFastTransformGeo& geo) cons const GlobalPadNumber p = mapper.globalPadNumber(PadPos(row, pad)); const PadCentre& c = mapper.padCentre(p); - auto [y, z] = geo.convPadDriftLengthToLocal(0, row, pad, 0.); + float y, z; + geo.convPadDriftLengthToLocal(0, row, pad, 0., y, z); const double dx = x - c.X(); const double dy = y - (-c.Y()); // diferent sign convention for Y coordinate in the map diff --git a/GPU/TPCFastTransformation/Spline1DSpec.h b/GPU/TPCFastTransformation/Spline1DSpec.h index d72de5a446718..56349ba6f454a 100644 --- a/GPU/TPCFastTransformation/Spline1DSpec.h +++ b/GPU/TPCFastTransformation/Spline1DSpec.h @@ -314,11 +314,9 @@ class Spline1DSpec : public Spline1DContainer const auto nYdimTmp = SplineUtil::getNdim(inpYdim); const auto nYdim = nYdimTmp.get(); - auto val = getSderivativesOverParsAtU(knotL, u); - const auto& dSdSl = val[0]; - const auto& dSdDl = val[1]; - const auto& dSdSr = val[2]; - const auto& dSdDr = val[3]; + T dSdSl, dSdDl, dSdSr, dSdDr; + getSderivativesOverParsAtU(knotL, u, dSdSl, dSdDl, dSdSr, dSdDr); + for (int32_t dim = 0; dim < nYdim; ++dim) { S[dim] = dSdSr * Sr[dim] + dSdSl * Sl[dim] + dSdDl * Dl[dim] + dSdDr * Dr[dim]; } @@ -346,7 +344,7 @@ class Spline1DSpec : public Spline1DContainer } template - GPUd() std::array getSderivativesOverParsAtU(const Knot& knotL, DataT u) const + GPUd() void getSderivativesOverParsAtU(const Knot& knotL, DataT u, T& dSdSl, T& dSdDl, T& dSdSr, T& dSdDr) const { /// Get derivatives of the interpolated value {S(u): 1D -> nYdim} at the segment [knotL, next knotR] /// over the spline parameters Sl(eft), Sr(ight) and the slopes Dl, Dr @@ -363,16 +361,15 @@ class Spline1DSpec : public Spline1DContainer T vm1 = v - T(1.); T a = u * vm1; T v2 = v * v; - T dSdSr = v2 * (T(3.) - v - v); - T dSdSl = T(1.) - dSdSr; - T dSdDl = vm1 * a; - T dSdDr = v * a; + dSdSr = v2 * (T(3.) - v - v); + dSdSl = T(1.) - dSdSr; + dSdDl = vm1 * a; + dSdDr = v * a; // S(u) = dSdSl * Sl + dSdSr * Sr + dSdDl * Dl + dSdDr * Dr; - return {dSdSl, dSdDl, dSdSr, dSdDr}; } template - GPUd() std::array getSDderivativesOverParsAtU(const Knot& knotL, DataT u) const + GPUd() void getSDderivativesOverParsAtU(const Knot& knotL, DataT u, T& dSdSl, T& dSdDl, T& dSdSr, T& dSdDr, T& dDdSl, T& dDdDl, T& dDdSr, T& dDdDr) const { /// Get derivatives of the interpolated value {S(u): 1D -> nYdim} at the segment [knotL, next knotR] /// over the spline values Sl, Sr and the slopes Dl, Dr @@ -389,19 +386,18 @@ class Spline1DSpec : public Spline1DContainer T vm1 = v - T(1.); T a = u * vm1; T v2 = v * v; - T dSdSr = v2 * (T(3.) - v - v); - T dSdSl = T(1.) - dSdSr; - T dSdDl = vm1 * a; - T dSdDr = v * a; + dSdSr = v2 * (T(3.) - v - v); + dSdSl = T(1.) - dSdSr; + dSdDl = vm1 * a; + dSdDr = v * a; T dv = T(knotL.Li); - T dDdSr = 6. * v * (T(1.) - v) * dv; - T dDdSl = -dDdSr; - T dDdDl = vm1 * (v + v + vm1); - T dDdDr = v * (v + vm1 + vm1); + dDdSr = 6. * v * (T(1.) - v) * dv; + dDdSl = -dDdSr; + dDdDl = vm1 * (v + v + vm1); + dDdDr = v * (v + vm1 + vm1); // S(u) = dSdSl * Sl + dSdSr * Sr + dSdDl * Dl + dSdDr * Dr; // D(u) = dS(u)/du = dDdSl * Sl + dDdSr * Sr + dDdDl * Dl + dDdDr * Dr; - return {dSdSl, dSdDl, dSdSr, dSdDr, dDdSl, dDdDl, dDdSr, dDdDr}; } using TBase::convXtoU; diff --git a/GPU/TPCFastTransformation/Spline2DSpec.h b/GPU/TPCFastTransformation/Spline2DSpec.h index 5681de2dc5fe9..fc53767ed6d07 100644 --- a/GPU/TPCFastTransformation/Spline2DSpec.h +++ b/GPU/TPCFastTransformation/Spline2DSpec.h @@ -334,16 +334,9 @@ class Spline2DSpec const DataT* A = Parameters + (nu * iv + iu) * nYdim4; // values { {Y1,Y2,Y3}, {Y1,Y2,Y3}'v, {Y1,Y2,Y3}'u, {Y1,Y2,Y3}''vu } at {u0, v0} const DataT* B = A + nYdim4 * nu; // values { ... } at {u0, v1} - auto val1 = mGridX1.template getSderivativesOverParsAtU(knotU, u); - auto val2 = mGridX2.template getSderivativesOverParsAtU(knotV, v); - const auto& dSl = val1[0]; - const auto& dDl = val1[1]; - const auto& dSr = val1[2]; - const auto& dDr = val1[3]; - const auto& dSd = val2[0]; - const auto& dDd = val2[1]; - const auto& dSu = val2[2]; - const auto& dDu = val2[3]; + DataT dSl, dDl, dSr, dDr, dSd, dDd, dSu, dDu; + mGridX1.template getSderivativesOverParsAtU(knotU, u, dSl, dDl, dSr, dDr); + mGridX2.template getSderivativesOverParsAtU(knotV, v, dSd, dDd, dSu, dDu); // when nYdim == 1: // S = dSl * (dSd * A[0] + dDd * A[1]) + dDl * (dSd * A[2] + dDd * A[3]) + @@ -398,8 +391,10 @@ class Spline2DSpec const DataT* A = Parameters + (nu * iv + iu) * nYdim4; // values { {Y1,Y2,Y3}, {Y1,Y2,Y3}'v, {Y1,Y2,Y3}'u, {Y1,Y2,Y3}''vu } at {u0, v0} const DataT* B = A + nYdim4 * nu; // values { ... } at {u0, v1} - auto [dSdSl, dSdDl, dSdSr, dSdDr, dRdSl, dRdDl, dRdSr, dRdDr] = mGridX1.template getSDderivativesOverParsAtU(knotU, u); - auto [dSdSd, dSdDd, dSdSu, dSdDu, dQdSd, dQdDd, dQdSu, dQdDu] = mGridX2.template getSDderivativesOverParsAtU(knotV, v); + DataT dSdSl, dSdDl, dSdSr, dSdDr, dRdSl, dRdDl, dRdSr, dRdDr; + mGridX1.template getSDderivativesOverParsAtU(knotU, u, dSdSl, dSdDl, dSdSr, dSdDr, dRdSl, dRdDl, dRdSr, dRdDr); + DataT dSdSd, dSdDd, dSdSu, dSdDu, dQdSd, dQdDd, dQdSu, dQdDu; + mGridX2.template getSDderivativesOverParsAtU(knotV, v, dSdSd, dSdDd, dSdSu, dSdDu, dQdSd, dQdDd, dQdSu, dQdDu); // when nYdim == 1: diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx index 241a1fcfc795b..5f5943a00372e 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.cxx @@ -731,7 +731,8 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) MaxValue maxDrow[3]; for (double y = y0; y < y1; y += stepY) { for (double z = z0; z < z1; z += stepZ) { - auto [dx, dy, dz] = getCorrectionLocal(sector, row, y, z); + float dx, dy, dz; + getCorrectionLocal(sector, row, y, z, dx, dy, dz); double realX = x + dx; double realY = y + dy; double realZ = z + dz; @@ -745,7 +746,8 @@ double TPCFastSpaceChargeCorrection::testInverse(bool prn) continue; } float dxr = getCorrectionXatRealYZ(sector, row, realY, realZ); - auto [dyr, dzr] = getCorrectionYZatRealYZ(sector, row, realY, realZ); + float dyr, dzr; + getCorrectionYZatRealYZ(sector, row, realY, realZ, dyr, dzr); double d[3] = {dxr - dx, dyr - dy, dzr - dz}; for (int32_t i = 0; i < 3; i++) { maxDrow[i].update(d[i], sector, row); diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index b1a3d0c35da7c..2a94154591533 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -22,6 +22,9 @@ #include "FlatObject.h" #include "GPUCommonDef.h" #include "GPUCommonMath.h" +#ifndef GPUCA_GPUCODE_DEVICE +#include "GPUCommonArray.h" // Would work on GPU, but yields performance regressions +#endif namespace o2 { @@ -76,15 +79,18 @@ class TPCFastSpaceChargeCorrection : public FlatObject } /// convert local y, z to internal grid coordinates u,v, and spline scale - GPUd() std::array convLocalToGridUntruncated(float y, float z) const + GPUd() void convLocalToGridUntruncated(float y, float z, float& u, float& v, float& s) const { - return {(y - y0) * yScale, (z - z0) * zScale, getSpineScaleForZ(z)}; + u = (y - y0) * yScale; + v = (z - z0) * zScale; + s = getSpineScaleForZ(z); } /// convert internal grid coordinates u,v to local y, z - std::array convGridToLocal(float gridU, float gridV) const + GPUd() void convGridToLocal(float gridU, float gridV, float& y, float& z) const { - return {y0 + gridU / yScale, z0 + gridV / zScale}; + y = y0 + gridU / yScale; + z = z0 + gridV / zScale; } ClassDefNV(GridInfo, 1); }; @@ -121,6 +127,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject maxCorr[2] = GPUCommonMath::Max(maxCorr[2], dv); } +#ifndef GPUCA_GPUCODE_DEVICE void updateMaxValues(std::array dxdudv, float scale) { float dx = dxdudv[0] * scale; @@ -138,6 +145,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject { return {minCorr[0], minCorr[1], minCorr[2]}; } +#endif ClassDefNV(SectorRowInfo, 2); }; @@ -249,31 +257,31 @@ class TPCFastSpaceChargeCorrection : public FlatObject /// // GPUd() int32_t getCorrectionInternal(int32_t sector, int32_t row, float u, float v, float& dx, float& du, float& dv) const; - GPUdi() std::array getCorrectionLocal(int32_t sector, int32_t row, float y, float z) const; + GPUdi() void getCorrectionLocal(int32_t sector, int32_t row, float y, float z, float& dx, float& dy, float& dz) const; /// inverse correction: Real Y and Z -> Real X GPUd() float getCorrectionXatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const; /// inverse correction: Real Y and Z -> measred Y and Z - GPUd() std::array getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const; + GPUd() void getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ, float& y, float& z) const; /// _______________ Utilities _______________________________________________ /// convert local y, z to internal grid coordinates u,v /// return values: u, v, scaling factor - GPUd() std::array convLocalToGrid(int32_t sector, int32_t row, float y, float z) const; + GPUd() void convLocalToGrid(int32_t sector, int32_t row, float y, float z, float& u, float& v, float& s) const; /// convert internal grid coordinates u,v to local y, z /// return values: y, z, scaling factor - GPUd() std::array convGridToLocal(int32_t sector, int32_t row, float u, float v) const; + GPUd() void convGridToLocal(int32_t sector, int32_t row, float u, float v, float& y, float& z) const; /// convert real Y, Z to the internal grid coordinates /// return values: u, v, scaling factor - GPUd() std::array convRealLocalToGrid(int32_t sector, int32_t row, float y, float z) const; + GPUd() void convRealLocalToGrid(int32_t sector, int32_t row, float y, float z, float& u, float& v, float& s) const; /// convert internal grid coordinates to the real Y, Z /// return values: y, z - GPUd() std::array convGridToRealLocal(int32_t sector, int32_t row, float u, float v) const; + GPUd() void convGridToRealLocal(int32_t sector, int32_t row, float u, float v, float& y, float& z) const; GPUd() bool isLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const; GPUd() bool isRealLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const; @@ -419,26 +427,26 @@ GPUdi() const float* TPCFastSpaceChargeCorrection::getCorrectionDataInvYZ(int32_ return getCorrectionData(sector, row, 2); } -GPUdi() std::array TPCFastSpaceChargeCorrection::convLocalToGrid(int32_t sector, int32_t row, float y, float z) const +GPUdi() void TPCFastSpaceChargeCorrection::convLocalToGrid(int32_t sector, int32_t row, float y, float z, float& u, float& v, float& s) const { /// convert local y, z to internal grid coordinates u,v /// return values: u, v, scaling factor const SplineType& spline = getSpline(sector, row); - auto val = getSectorRowInfo(sector, row).gridMeasured.convLocalToGridUntruncated(y, z); + getSectorRowInfo(sector, row).gridMeasured.convLocalToGridUntruncated(y, z, u, v, s); // shrink to the grid - val[0] = GPUCommonMath::Clamp(val[0], 0.f, (float)spline.getGridX1().getUmax()); - val[1] = GPUCommonMath::Clamp(val[1], 0.f, (float)spline.getGridX2().getUmax()); - return val; + u = GPUCommonMath::Clamp(u, 0.f, (float)spline.getGridX1().getUmax()); + v = GPUCommonMath::Clamp(v, 0.f, (float)spline.getGridX2().getUmax()); } GPUdi() bool TPCFastSpaceChargeCorrection::isLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const { /// check if local y, z are inside the grid - auto val = getSectorRowInfo(sector, row).gridMeasured.convLocalToGridUntruncated(y, z); + float u, v, s; + getSectorRowInfo(sector, row).gridMeasured.convLocalToGridUntruncated(y, z, u, v, s); const auto& spline = getSpline(sector, row); // shrink to the grid - if (val[0] < 0.f || val[0] > (float)spline.getGridX1().getUmax() || // - val[1] < 0.f || val[1] > (float)spline.getGridX2().getUmax()) { + if (u < 0.f || u > (float)spline.getGridX1().getUmax() || // + v < 0.f || v > (float)spline.getGridX2().getUmax()) { return false; } return true; @@ -447,86 +455,86 @@ GPUdi() bool TPCFastSpaceChargeCorrection::isLocalInsideGrid(int32_t sector, int GPUdi() bool TPCFastSpaceChargeCorrection::isRealLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const { /// check if local y, z are inside the grid - auto val = getSectorRowInfo(sector, row).gridReal.convLocalToGridUntruncated(y, z); + float u, v, s; + getSectorRowInfo(sector, row).gridReal.convLocalToGridUntruncated(y, z, u, v, s); const auto& spline = getSpline(sector, row); // shrink to the grid - if (val[0] < 0.f || val[0] > (float)spline.getGridX1().getUmax() || // - val[1] < 0.f || val[1] > (float)spline.getGridX2().getUmax()) { + if (u < 0.f || u > (float)spline.getGridX1().getUmax() || // + v < 0.f || v > (float)spline.getGridX2().getUmax()) { return false; } return true; } -GPUdi() std::array TPCFastSpaceChargeCorrection::convGridToLocal(int32_t sector, int32_t row, float gridU, float gridV) const +GPUdi() void TPCFastSpaceChargeCorrection::convGridToLocal(int32_t sector, int32_t row, float gridU, float gridV, float& y, float& z) const { /// convert internal grid coordinates u,v to local y, z - return getSectorRowInfo(sector, row).gridMeasured.convGridToLocal(gridU, gridV); + getSectorRowInfo(sector, row).gridMeasured.convGridToLocal(gridU, gridV, y, z); } -GPUdi() std::array TPCFastSpaceChargeCorrection::convRealLocalToGrid(int32_t sector, int32_t row, float y, float z) const +GPUdi() void TPCFastSpaceChargeCorrection::convRealLocalToGrid(int32_t sector, int32_t row, float y, float z, float& u, float& v, float& s) const { /// convert real y, z to the internal grid coordinates + scale const SplineType& spline = getSpline(sector, row); - auto val = getSectorRowInfo(sector, row).gridReal.convLocalToGridUntruncated(y, z); + getSectorRowInfo(sector, row).gridReal.convLocalToGridUntruncated(y, z, u, v, s); // shrink to the grid - val[0] = GPUCommonMath::Clamp(val[0], 0.f, (float)spline.getGridX1().getUmax()); - val[1] = GPUCommonMath::Clamp(val[1], 0.f, (float)spline.getGridX2().getUmax()); - return val; + u = GPUCommonMath::Clamp(u, 0.f, (float)spline.getGridX1().getUmax()); + v = GPUCommonMath::Clamp(v, 0.f, (float)spline.getGridX2().getUmax()); } -GPUdi() std::array TPCFastSpaceChargeCorrection::convGridToRealLocal(int32_t sector, int32_t row, float gridU, float gridV) const +GPUdi() void TPCFastSpaceChargeCorrection::convGridToRealLocal(int32_t sector, int32_t row, float gridU, float gridV, float& y, float& z) const { /// convert internal grid coordinates u,v to the real y, z - return getSectorRowInfo(sector, row).gridReal.convGridToLocal(gridU, gridV); + getSectorRowInfo(sector, row).gridReal.convGridToLocal(gridU, gridV, y, z); } -GPUdi() std::array TPCFastSpaceChargeCorrection::getCorrectionLocal(int32_t sector, int32_t row, float y, float z) const +GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionLocal(int32_t sector, int32_t row, float y, float z, float& dx, float& dy, float& dz) const { const auto& info = getSectorRowInfo(sector, row); const SplineType& spline = getSpline(sector, row); const float* splineData = getCorrectionData(sector, row); - auto val = convLocalToGrid(sector, row, y, z); + float u, v, s; + convLocalToGrid(sector, row, y, z, u, v, s); float dxyz[3]; - spline.interpolateAtU(splineData, val[0], val[1], dxyz); + spline.interpolateAtU(splineData, u, v, dxyz); if (CAMath::Abs(dxyz[0]) > 100.f || CAMath::Abs(dxyz[1]) > 100.f || CAMath::Abs(dxyz[2]) > 100.f) { - val[2] = 0.f; // TODO: DR: Protect from FPEs, fix upstream and remove once guaranteed that it is fixed + s = 0.f; // TODO: DR: Protect from FPEs, fix upstream and remove once guaranteed that it is fixed } - float dx = val[2] * GPUCommonMath::Clamp(dxyz[0], info.minCorr[0], info.maxCorr[0]); - float dy = val[2] * GPUCommonMath::Clamp(dxyz[1], info.minCorr[1], info.maxCorr[1]); - float dz = val[2] * GPUCommonMath::Clamp(dxyz[2], info.minCorr[2], info.maxCorr[2]); - - return {dx, dy, dz}; + dx = s * GPUCommonMath::Clamp(dxyz[0], info.minCorr[0], info.maxCorr[0]); + dy = s * GPUCommonMath::Clamp(dxyz[1], info.minCorr[1], info.maxCorr[1]); + dz = s * GPUCommonMath::Clamp(dxyz[2], info.minCorr[2], info.maxCorr[2]); } GPUdi() float TPCFastSpaceChargeCorrection::getCorrectionXatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const { const auto& info = getSectorRowInfo(sector, row); - auto val = convRealLocalToGrid(sector, row, realY, realZ); + float u, v, s; + convRealLocalToGrid(sector, row, realY, realZ, u, v, s); float dx = 0; - getSplineInvX(sector, row).interpolateAtU(getCorrectionDataInvX(sector, row), val[0], val[1], &dx); + getSplineInvX(sector, row).interpolateAtU(getCorrectionDataInvX(sector, row), u, v, &dx); if (CAMath::Abs(dx) > 100.f) { - val[2] = 0.f; // TODO: DR: Protect from FPEs, fix upstream and remove once guaranteed that it is fixed + s = 0.f; // TODO: DR: Protect from FPEs, fix upstream and remove once guaranteed that it is fixed } - dx = val[2] * GPUCommonMath::Clamp(dx, info.minCorr[0], info.maxCorr[0]); + dx = s * GPUCommonMath::Clamp(dx, info.minCorr[0], info.maxCorr[0]); return dx; } -GPUdi() std::array TPCFastSpaceChargeCorrection::getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const +GPUdi() void TPCFastSpaceChargeCorrection::getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ, float& y, float& z) const { - auto val = convRealLocalToGrid(sector, row, realY, realZ); + float u, v, s; + convRealLocalToGrid(sector, row, realY, realZ, u, v, s); const auto& info = getSectorRowInfo(sector, row); float dyz[2]; - getSplineInvYZ(sector, row).interpolateAtU(getCorrectionDataInvYZ(sector, row), val[0], val[1], dyz); + getSplineInvYZ(sector, row).interpolateAtU(getCorrectionDataInvYZ(sector, row), u, v, dyz); if (CAMath::Abs(dyz[0]) > 100.f || CAMath::Abs(dyz[1]) > 100.f) { - val[2] = 0.f; // TODO: DR: Protect from FPEs, fix upstream and remove once guaranteed that it is fixed + s = 0.f; // TODO: DR: Protect from FPEs, fix upstream and remove once guaranteed that it is fixed } - dyz[0] = val[2] * GPUCommonMath::Clamp(dyz[0], info.minCorr[1], info.maxCorr[1]); - dyz[1] = val[2] * GPUCommonMath::Clamp(dyz[1], info.minCorr[2], info.maxCorr[2]); - return {dyz[0], dyz[1]}; + y = s * GPUCommonMath::Clamp(dyz[0], info.minCorr[1], info.maxCorr[1]); + z = s * GPUCommonMath::Clamp(dyz[1], info.minCorr[2], info.maxCorr[2]); } } // namespace gpu diff --git a/GPU/TPCFastTransformation/TPCFastTransform.h b/GPU/TPCFastTransformation/TPCFastTransform.h index 64fdba9d94bd3..532ec855c77b0 100644 --- a/GPU/TPCFastTransformation/TPCFastTransform.h +++ b/GPU/TPCFastTransformation/TPCFastTransform.h @@ -349,17 +349,13 @@ class TPCFastTransform : public FlatObject GPUdi() void TPCFastTransform::convPadTimeToLocal(int32_t sector, int32_t row, float pad, float time, float& y, float& z, float vertexTime) const { float l = (time - mT0 - vertexTime) * mVdrift; // drift length [cm] - const auto localval = getGeometry().convPadDriftLengthToLocal(sector, row, pad, l); - y = localval[0]; - z = localval[1]; + getGeometry().convPadDriftLengthToLocal(sector, row, pad, l, y, z); } GPUdi() void TPCFastTransform::convPadTimeToLocalInTimeFrame(int32_t sector, int32_t row, float pad, float time, float& y, float& z, float maxTimeBin) const { float l = getGeometry().getTPCzLength() + (time - mT0 - maxTimeBin) * mVdrift; // drift length [cm] - const auto localval = getGeometry().convPadDriftLengthToLocal(sector, row, pad, l); - y = localval[0]; - z = localval[1]; + getGeometry().convPadDriftLengthToLocal(sector, row, pad, l, y, z); } // ---------------------------------------------------------------------- @@ -391,16 +387,16 @@ GPUdi() float TPCFastTransform::convDriftLengthToTime(float driftLength, float v GPUdi() void TPCFastTransform::convLocalToPadTime(int32_t sector, int32_t row, float y, float z, float& pad, float& time, float vertexTime) const { - const auto padLength = getGeometry().convLocalToPadDriftLength(sector, row, y, z); - pad = padLength[0]; - time = convDriftLengthToTime(padLength[1], vertexTime); + float l; + getGeometry().convLocalToPadDriftLength(sector, row, y, z, pad, l); + time = convDriftLengthToTime(l, vertexTime); } GPUdi() void TPCFastTransform::convLocalToPadTimeInTimeFrame(int32_t sector, int32_t row, float y, float z, float& pad, float& time, float maxTimeBin) const { - const auto padLength = getGeometry().convLocalToPadDriftLength(sector, row, y, z); - pad = padLength[0]; - time = convDriftLengthToTime(padLength[1], maxTimeBin); + float l; + getGeometry().convLocalToPadDriftLength(sector, row, y, z, pad, l); + time = convDriftLengthToTime(l, maxTimeBin); } // ---------------------------------------------------------------------- @@ -426,28 +422,28 @@ GPUdi() void TPCFastTransform::TransformLocal(int32_t sector, int32_t row, float } else #endif // GPUCA_GPUCODE { - const auto corrLocal = mCorrection.getCorrectionLocal(sector, row, y, z); - dx = corrLocal[0]; - dy = corrLocal[1]; - dz = corrLocal[2]; + mCorrection.getCorrectionLocal(sector, row, y, z, dx, dy, dz); if (ref) { if ((scale > 0.f) && (scaleMode == 0)) { // scaling was requested - auto val = ref->mCorrection.getCorrectionLocal(sector, row, y, z); - dx = (dx - val[0]) * scale + val[0]; - dy = (dy - val[1]) * scale + val[1]; - dz = (dz - val[2]) * scale + val[2]; + float dx1, dy1, dz1; + ref->mCorrection.getCorrectionLocal(sector, row, y, z, dx1, dy1, dz1); + dx = (dx - dx1) * scale + dx1; + dy = (dy - dy1) * scale + dy1; + dz = (dz - dz1) * scale + dz1; } else if ((scale != 0.f) && ((scaleMode == 1) || (scaleMode == 2))) { - auto val = ref->mCorrection.getCorrectionLocal(sector, row, y, z); - dx = val[0] * scale + dx; - dy = val[1] * scale + dy; - dz = val[2] * scale + dz; + float dx1, dy1, dz1; + ref->mCorrection.getCorrectionLocal(sector, row, y, z, dx1, dy1, dz1); + dx = dx1 * scale + dx; + dy = dy1 * scale + dy; + dz = dz1 * scale + dz; } } if (ref2 && (scale2 != 0)) { - auto val = ref2->mCorrection.getCorrectionLocal(sector, row, y, z); - dx = val[0] * scale2 + dx; - dy = val[1] * scale2 + dy; - dz = val[2] * scale2 + dz; + float dx1, dy1, dz1; + ref2->mCorrection.getCorrectionLocal(sector, row, y, z, dx1, dy1, dz1); + dx = dx1 * scale2 + dx; + dy = dy1 * scale2 + dy; + dz = dz1 * scale2 + dz; } } } @@ -478,21 +474,16 @@ GPUdi() void TPCFastTransform::TransformLocal(int32_t sector, int32_t row, float float dxRef = 0.f, dyRef = 0.f, dzRef = 0.f; if (ref) { - const auto corr = ref->mCorrection.getCorrectionLocal(sector, row, y, z); - dxRef = corr[0]; - dyRef = corr[1]; - dzRef = corr[2]; + ref->mCorrection.getCorrectionLocal(sector, row, y, z, dxRef, dyRef, dzRef); } float dxRef2 = 0.f, dyRef2 = 0.f, dzRef2 = 0.f; if (ref2) { - const auto corr = ref2->mCorrection.getCorrectionLocal(sector, row, y, z); - dxRef2 = corr[0]; - dyRef2 = corr[1]; - dzRef2 = corr[2]; + ref2->mCorrection.getCorrectionLocal(sector, row, y, z, dxRef2, dyRef2, dzRef2); } - auto [dxOrig, dyOrig, dzOrig] = mCorrection.getCorrectionLocal(sector, row, y, z); + float dxOrig, dyOrig, dzOrig; + mCorrection.getCorrectionLocal(sector, row, y, z, dxOrig, dyOrig, dzOrig); o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_Transform").data() // corrections in x, u, v @@ -617,9 +608,7 @@ GPUdi() void TPCFastTransform::TransformIdeal(int32_t sector, int32_t row, float x = getGeometry().getRowInfo(row).x; float driftLength = (time - mT0 - vertexTime) * mVdrift; // drift length cm - const auto localval = getGeometry().convPadDriftLengthToLocal(sector, row, pad, driftLength); - y = localval[0]; - z = localval[1]; + getGeometry().convPadDriftLengthToLocal(sector, row, pad, driftLength, y, z); } GPUdi() float TPCFastTransform::convTimeToZinTimeFrame(int32_t sector, float time, float maxTimeBin) const @@ -726,24 +715,25 @@ GPUdi() void TPCFastTransform::InverseTransformYZtoNominalYZ(int32_t sector, int float dz = 0; if ((scale >= 0.f) || (scaleMode == 1) || (scaleMode == 2)) { - const auto corrYZ = mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); - dy = corrYZ[0]; - dz = corrYZ[1]; + mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ, dy, dz); if (ref) { // scaling was requested if (scaleMode == 0 && scale > 0.f) { - const auto val = ref->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); - dy = (dy - val[0]) * scale + val[0]; - dz = (dz - val[1]) * scale + val[1]; + float dy1, dz1; + ref->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ, dy1, dz1); + dy = (dy - dy1) * scale + dy1; + dz = (dz - dz1) * scale + dz1; } else if ((scale != 0) && ((scaleMode == 1) || (scaleMode == 2))) { - const auto val = ref->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); - dy = val[0] * scale + dy; - dz = val[1] * scale + dz; + float dy1, dz1; + ref->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ, dy1, dz1); + dy = dy1 * scale + dy; + dz = dz1 * scale + dz; } if (ref2 && (scale2 != 0)) { - const auto val = ref2->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ); - dy = val[0] * scale2 + dy; - dz = val[1] * scale2 + dz; + float dy1, dz1; + ref2->mCorrection.getCorrectionYZatRealYZ(sector, row, realY, realZ, dy1, dz1); + dy = dy1 * scale2 + dy; + dz = dz1 * scale2 + dz; } } } diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx b/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx index 2fe773a76e4d3..c7ed4243d7396 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.cxx @@ -145,8 +145,10 @@ int32_t TPCFastTransformGeo::test(int32_t sector, int32_t row, float ly, float l error = -3; } - auto [pad, length] = convLocalToPadDriftLength(sector, 10, ly, lz); - auto [ly2, lz2] = convPadDriftLengthToLocal(sector, 10, pad, length); + float pad, length; + convLocalToPadDriftLength(sector, 10, ly, lz, pad, length); + float ly2, lz2; + convPadDriftLengthToLocal(sector, 10, pad, length, ly2, lz2); if (fabs(ly2 - ly) + fabs(lz2 - lz) > 1.e-6) { LOG(info) << "Error local <-> UV: y " << ly << " dy " << ly2 - ly << " z " << lz << " dz " << lz2 - lz; diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.h b/GPU/TPCFastTransformation/TPCFastTransformGeo.h index 09793b6677d83..55e36cf6efef7 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.h +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.h @@ -18,12 +18,12 @@ #define ALICEO2_GPUCOMMON_TPCFASTTRANSFORMATION_TPCFASTTRANSFORMGEO_H #include "GPUCommonDef.h" -#include "GPUCommonArray.h" #include "GPUCommonMath.h" #ifndef GPUCA_GPUCODE_DEVICE #include #include "GPUCommonRtypes.h" +#include "GPUCommonArray.h" // Would work on GPU, but yields performance regressions #endif namespace o2 @@ -59,7 +59,9 @@ class TPCFastTransformGeo GPUd() float getYmax() const { return -yMin; } /// get Y range +#ifndef GPUCA_GPUCODE_DEVICE GPUd() std::array getYrange() const { return {getYmin(), getYmax()}; } +#endif /// get width in Y GPUd() float getYwidth() const { return -2.f * yMin; } @@ -129,7 +131,17 @@ class TPCFastTransformGeo GPUd() float getTPCzLength() const { return mTPCzLength; } /// Gives Z range for the corresponding TPC side - GPUd() std::array getZrange(int32_t sector) const; +#ifndef GPUCA_GPUCODE_DEVICE + GPUdi() std::array getZrange(int32_t sector) const + { + /// z range for the sector + if (sector < NumberOfSectorsA) { // TPC side A + return {0.f, mTPCzLength}; + } else { // TPC side C + return {-mTPCzLength, 0.f}; + } + } +#endif GPUd() float getZmin(int32_t sector) const; GPUd() float getZmax(int32_t sector) const; GPUd() float getZreadout(int32_t sector) const; @@ -143,7 +155,7 @@ class TPCFastTransformGeo GPUd() void convGlobalToLocal(int32_t sector, float gx, float gy, float gz, float& lx, float& ly, float& lz) const; /// convert Pad, DriftLength -> Local c.s. - GPUd() std::array convPadDriftLengthToLocal(int32_t sector, int32_t row, float pad, float driftLength) const; + GPUd() void convPadDriftLengthToLocal(int32_t sector, int32_t row, float pad, float driftLength, float& y, float& z) const; /// convert DriftLength -> Local c.s. GPUd() float convDriftLengthToZ1(int32_t sector, float driftLength) const; @@ -152,7 +164,7 @@ class TPCFastTransformGeo GPUd() float convZtoDriftLength1(int32_t sector, float z) const; /// convert Local c.s. -> Pad, DriftLength - GPUd() std::array convLocalToPadDriftLength(int32_t sector, int32_t row, float y, float z) const; + GPUd() void convLocalToPadDriftLength(int32_t sector, int32_t row, float y, float z, float& pad, float& l) const; /// Print method void print() const; @@ -238,12 +250,11 @@ GPUdi() void TPCFastTransformGeo::convGlobalToLocal(int32_t sector, float gx, fl lz = gz; } -GPUdi() std::array TPCFastTransformGeo::convPadDriftLengthToLocal(int32_t sector, int32_t row, float pad, float driftLength) const +GPUdi() void TPCFastTransformGeo::convPadDriftLengthToLocal(int32_t sector, int32_t row, float pad, float driftLength, float& y, float& z) const { /// convert Pad, DriftLength -> Local c.s. const RowInfo& rowInfo = getRowInfo(row); float u = (pad - 0.5f * rowInfo.maxPad) * rowInfo.padWidth; - float y, z; if (sector < NumberOfSectorsA) { // TPC side A y = u; z = mTPCzLength - driftLength; @@ -251,7 +262,6 @@ GPUdi() std::array TPCFastTransformGeo::convPadDriftLengthToLocal(int3 y = -u; // pads are mirrorred on C-side z = driftLength - mTPCzLength; // drift direction is mirrored on C-side } - return {y, z}; } GPUdi() float TPCFastTransformGeo::convDriftLengthToZ1(int32_t sector, float driftLength) const @@ -266,16 +276,6 @@ GPUdi() float TPCFastTransformGeo::convZtoDriftLength1(int32_t sector, float z) return (sector < NumberOfSectorsA) ? (mTPCzLength - z) : (z + mTPCzLength); } -GPUdi() std::array TPCFastTransformGeo::getZrange(int32_t sector) const -{ - /// z range for the sector - if (sector < NumberOfSectorsA) { // TPC side A - return {0.f, mTPCzLength}; - } else { // TPC side C - return {-mTPCzLength, 0.f}; - } -} - GPUdi() float TPCFastTransformGeo::getZmin(int32_t sector) const { /// z min for the sector @@ -306,10 +306,10 @@ GPUdi() float TPCFastTransformGeo::getZreadout(int32_t sector) const } } -GPUdi() std::array TPCFastTransformGeo::convLocalToPadDriftLength(int32_t sector, int32_t row, float y, float z) const +GPUdi() void TPCFastTransformGeo::convLocalToPadDriftLength(int32_t sector, int32_t row, float y, float z, float& pad, float& l) const { /// convert Local c.s. -> Pad, DriftLength - float u, l; + float u; if (sector < NumberOfSectorsA) { // TPC side A u = y; l = mTPCzLength - z; @@ -318,8 +318,7 @@ GPUdi() std::array TPCFastTransformGeo::convLocalToPadDriftLength(int3 l = z + mTPCzLength; // drift direction is mirrored on C-side } const TPCFastTransformGeo::RowInfo& rowInfo = getRowInfo(row); - float pad = u / rowInfo.padWidth + 0.5f * rowInfo.maxPad; - return {pad, l}; + pad = u / rowInfo.padWidth + 0.5f * rowInfo.maxPad; } } // namespace gpu diff --git a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C index baaeca90202d5..bc6fafbaa8bd0 100644 --- a/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C +++ b/GPU/TPCFastTransformation/macro/TPCFastTransformInit.C @@ -493,12 +493,14 @@ void TPCFastTransformInit(const char* fileName = "debugVoxRes.root", const char* auto [zMin, zMax] = geo.getZrange(iSector); for (int32_t iu = 0; iu < gridY.getNumberOfKnots(); iu++) { - auto [y, z] = corr.convGridToLocal(iSector, iRow, gridY.getKnot(iu).getU(), 0.); + float y, z; + corr.convGridToLocal(iSector, iRow, gridY.getKnot(iu).getU(), 0., y, z); knots[0].push_back(y); points[0].push_back(y); } for (int32_t iv = 0; iv < gridZ.getNumberOfKnots(); iv++) { - auto [y, z] = corr.convGridToLocal(iSector, iRow, 0., gridZ.getKnot(iv).getU()); + float y, z; + corr.convGridToLocal(iSector, iRow, 0., gridZ.getKnot(iv).getU(), y, z); knots[1].push_back(z); points[1].push_back(z); } From 124fd41b563e455f82b45daee47c064d87a5d5f8 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Tue, 31 Mar 2026 11:56:27 +0200 Subject: [PATCH 521/701] GPU TPCFastTransformation: Do not use double --- GPU/TPCFastTransformation/Spline1DSpec.h | 4 ++-- GPU/TPCFastTransformation/Spline2DSpec.h | 2 +- GPU/TPCFastTransformation/SplineSpec.h | 2 +- GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h | 2 +- 4 files changed, 5 insertions(+), 5 deletions(-) diff --git a/GPU/TPCFastTransformation/Spline1DSpec.h b/GPU/TPCFastTransformation/Spline1DSpec.h index 56349ba6f454a..3a2b5d0c4ee32 100644 --- a/GPU/TPCFastTransformation/Spline1DSpec.h +++ b/GPU/TPCFastTransformation/Spline1DSpec.h @@ -392,7 +392,7 @@ class Spline1DSpec : public Spline1DContainer dSdDr = v * a; T dv = T(knotL.Li); - dDdSr = 6. * v * (T(1.) - v) * dv; + dDdSr = T(6.) * v * (T(1.) - v) * dv; dDdSl = -dDdSr; dDdDl = vm1 * (v + v + vm1); dDdDr = v * (v + vm1 + vm1); @@ -567,7 +567,7 @@ class Spline1DSpec /// Simplified interface for 1D: return the interpolated value GPUd() DataT interpolate(DataT x) const { - DataT S = 0.; + DataT S = 0; TBase::interpolate(x, &S); return S; } diff --git a/GPU/TPCFastTransformation/Spline2DSpec.h b/GPU/TPCFastTransformation/Spline2DSpec.h index fc53767ed6d07..d0648f2afa22b 100644 --- a/GPU/TPCFastTransformation/Spline2DSpec.h +++ b/GPU/TPCFastTransformation/Spline2DSpec.h @@ -659,7 +659,7 @@ class Spline2DSpec /// Simplified interface for 1D: return the interpolated value GPUd() DataT interpolate(DataT x1, DataT x2) const { - DataT S = 0.; + DataT S = 0; TBase::interpolate(x1, x2, &S); return S; } diff --git a/GPU/TPCFastTransformation/SplineSpec.h b/GPU/TPCFastTransformation/SplineSpec.h index 2102b73e72900..31b6bef22103c 100644 --- a/GPU/TPCFastTransformation/SplineSpec.h +++ b/GPU/TPCFastTransformation/SplineSpec.h @@ -537,7 +537,7 @@ class SplineSpec /// Simplified interface for 1D: return the interpolated value GPUd() DataT interpolate(const DataT x[]) const { - DataT S = 0.; + DataT S = 0; TBase::interpolate(x, &S); return S; } diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index 2a94154591533..12dc5c2fdee54 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -65,7 +65,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject this->zScale = zScale_; this->zOut = zOut_; // no scaling when the distance to the readout is too small - this->splineScalingWithZ = fabs(zReadout_ - zOut_) > 1. ? 1. / (zReadout_ - zOut_) : 0.; + this->splineScalingWithZ = fabs(zReadout_ - zOut_) > 1.f ? 1.f / (zReadout_ - zOut_) : 0.f; } float getY0() const { return y0; } From 8b79a844836b0bc779424a6fa08f36e93432523e Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 1 Apr 2026 12:02:34 +0200 Subject: [PATCH 522/701] GPU TPCFastTransfomration: Add missing inline keywords --- .../TPCFastSpaceChargeCorrection.h | 14 +++++----- GPU/TPCFastTransformation/TPCFastTransform.h | 26 +++++++++---------- .../TPCFastTransformGeo.h | 16 ++++++------ 3 files changed, 28 insertions(+), 28 deletions(-) diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index 12dc5c2fdee54..6dcf30b2991ba 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -73,13 +73,13 @@ class TPCFastSpaceChargeCorrection : public FlatObject float getZ0() const { return z0; } float getZscale() const { return zScale; } - GPUd() float getSpineScaleForZ(float z) const + GPUdi() float getSpineScaleForZ(float z) const { return 1.f - GPUCommonMath::Clamp((z - zOut) * splineScalingWithZ, 0.f, 1.f); } /// convert local y, z to internal grid coordinates u,v, and spline scale - GPUd() void convLocalToGridUntruncated(float y, float z, float& u, float& v, float& s) const + GPUdi() void convLocalToGridUntruncated(float y, float z, float& u, float& v, float& s) const { u = (y - y0) * yScale; v = (z - z0) * zScale; @@ -87,7 +87,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject } /// convert internal grid coordinates u,v to local y, z - GPUd() void convGridToLocal(float gridU, float gridV, float& y, float& z) const + GPUdi() void convGridToLocal(float gridU, float gridV, float& y, float& z) const { y = y0 + gridU / yScale; z = z0 + gridV / zScale; @@ -215,7 +215,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject GPUd() void setNoCorrection(); /// Sets the time stamp of the current calibaration - GPUd() void setTimeStamp(int64_t v) { mTimeStamp = v; } + GPUdi() void setTimeStamp(int64_t v) { mTimeStamp = v; } /// Gives const pointer to a spline GPUd() const SplineType& getSpline(int32_t sector, int32_t row) const; @@ -287,7 +287,7 @@ class TPCFastSpaceChargeCorrection : public FlatObject GPUd() bool isRealLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const; /// TPC geometry information - GPUd() const TPCFastTransformGeo& getGeometry() const + GPUdi() const TPCFastTransformGeo& getGeometry() const { return mGeo; } @@ -296,13 +296,13 @@ class TPCFastSpaceChargeCorrection : public FlatObject int64_t getTimeStamp() const { return mTimeStamp; } /// Gives TPC sector & row info - GPUd() const SectorRowInfo& getSectorRowInfo(int32_t sector, int32_t row) const + GPUdi() const SectorRowInfo& getSectorRowInfo(int32_t sector, int32_t row) const { return mSectorRowInfos[mGeo.getMaxNumberOfRows() * sector + row]; } /// Gives TPC sector & row info - GPUd() SectorRowInfo& getSectorRowInfo(int32_t sector, int32_t row) + GPUdi() SectorRowInfo& getSectorRowInfo(int32_t sector, int32_t row) { return mSectorRowInfos[mGeo.getMaxNumberOfRows() * sector + row]; } diff --git a/GPU/TPCFastTransformation/TPCFastTransform.h b/GPU/TPCFastTransformation/TPCFastTransform.h index 532ec855c77b0..068c85b13836a 100644 --- a/GPU/TPCFastTransformation/TPCFastTransform.h +++ b/GPU/TPCFastTransformation/TPCFastTransform.h @@ -53,7 +53,7 @@ struct TPCSlowSpaceChargeCorrection { ~TPCSlowSpaceChargeCorrection() = default; /// setting dummy corrections for GPU - GPUd() void getCorrections(const float gx, const float gy, const float gz, const int32_t sector, float& gdxC, float& gdyC, float& gdzC) const + GPUdi() void getCorrections(const float gx, const float gy, const float gz, const int32_t sector, float& gdxC, float& gdyC, float& gdzC) const { gdxC = 0; gdyC = 0; @@ -172,7 +172,7 @@ class TPCFastTransform : public FlatObject void setTimeStamp(int64_t v) { mTimeStamp = v; } /// Gives a reference for external initialization of TPC corrections - GPUd() const TPCFastSpaceChargeCorrection& getCorrection() const { return mCorrection; } + GPUdi() const TPCFastSpaceChargeCorrection& getCorrection() const { return mCorrection; } /// Gives a reference for external initialization of TPC corrections TPCFastSpaceChargeCorrection& getCorrection() { return mCorrection; } @@ -230,37 +230,37 @@ class TPCFastTransform : public FlatObject /// _______________ Utilities _______________________________________________ /// TPC geometry information - GPUd() const TPCFastTransformGeo& getGeometry() const { return mCorrection.getGeometry(); } + GPUdi() const TPCFastTransformGeo& getGeometry() const { return mCorrection.getGeometry(); } /// Gives the time stamp of the current calibaration parameters - GPUd() int64_t getTimeStamp() const { return mTimeStamp; } + GPUdi() int64_t getTimeStamp() const { return mTimeStamp; } /// Return mVDrift in cm / time bin - GPUd() float getVDrift() const { return mVdrift; } + GPUdi() float getVDrift() const { return mVdrift; } /// Return T0 in time bin units - GPUd() float getT0() const { return mT0; } + GPUdi() float getT0() const { return mT0; } /// Return map lumi - GPUd() float getLumi() const { return mLumi; } + GPUdi() float getLumi() const { return mLumi; } - GPUd() float isLumiSet() const { return mLumi != DEFLUMI; } + GPUdi() float isLumiSet() const { return mLumi != DEFLUMI; } /// Return map lumi error - GPUd() float getLumiError() const { return mLumiError; } + GPUdi() float getLumiError() const { return mLumiError; } /// Return map lumi GPUd() float getIDC() const; - GPUd() bool isIDCSet() const { return mIDC != DEFIDC; } + GPUdi() bool isIDCSet() const { return mIDC != DEFIDC; } /// Return map lumi error - GPUd() float getIDCError() const { return mIDCError; } + GPUdi() float getIDCError() const { return mIDCError; } - GPUd() float getCTP2IDCFallBackThreshold() const { return mCTP2IDCFallBackThreshold; } + GPUdi() float getCTP2IDCFallBackThreshold() const { return mCTP2IDCFallBackThreshold; } /// Return map user defined lumi scale factor - GPUd() float getLumiScaleFactor() const { return mLumiScaleFactor; } + GPUdi() float getLumiScaleFactor() const { return mLumiScaleFactor; } /// maximal possible drift time of the active area GPUd() float getMaxDriftTime(int32_t sector, int32_t row, float pad) const; diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.h b/GPU/TPCFastTransformation/TPCFastTransformGeo.h index 55e36cf6efef7..31b81e02c2d4c 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.h +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.h @@ -53,10 +53,10 @@ class TPCFastTransformGeo float yMin{0.f}; ///< min. y coordinate /// get Y min - GPUd() float getYmin() const { return yMin; } + GPUdi() float getYmin() const { return yMin; } /// get Y max - GPUd() float getYmax() const { return -yMin; } + GPUdi() float getYmax() const { return -yMin; } /// get Y range #ifndef GPUCA_GPUCODE_DEVICE @@ -64,7 +64,7 @@ class TPCFastTransformGeo #endif /// get width in Y - GPUd() float getYwidth() const { return -2.f * yMin; } + GPUdi() float getYwidth() const { return -2.f * yMin; } ClassDefNV(RowInfo, 2); }; @@ -110,16 +110,16 @@ class TPCFastTransformGeo /// _______________ Getters _________________________________ /// Gives number of TPC sectors - GPUd() static constexpr int32_t getNumberOfSectors() { return NumberOfSectors; } + GPUdi() static constexpr int32_t getNumberOfSectors() { return NumberOfSectors; } /// Gives number of TPC sectors on the A side - GPUd() static constexpr int32_t getNumberOfSectorsA() { return NumberOfSectorsA; } + GPUdi() static constexpr int32_t getNumberOfSectorsA() { return NumberOfSectorsA; } /// Gives number of TPC rows - GPUd() int32_t getNumberOfRows() const { return mNumberOfRows; } + GPUdi() int32_t getNumberOfRows() const { return mNumberOfRows; } /// Gives number of TPC rows - GPUd() static constexpr int getMaxNumberOfRows() { return MaxNumberOfRows; } + GPUdi() static constexpr int getMaxNumberOfRows() { return MaxNumberOfRows; } /// Gives sector info GPUd() const SectorInfo& getSectorInfo(int32_t sector) const; @@ -128,7 +128,7 @@ class TPCFastTransformGeo GPUd() const RowInfo& getRowInfo(int32_t row) const; /// Gives Z length of the TPC, one Z side - GPUd() float getTPCzLength() const { return mTPCzLength; } + GPUdi() float getTPCzLength() const { return mTPCzLength; } /// Gives Z range for the corresponding TPC side #ifndef GPUCA_GPUCODE_DEVICE From bf2d88e5138b05643788eae70492559220995bea Mon Sep 17 00:00:00 2001 From: shahoian Date: Sun, 22 Feb 2026 12:43:33 +0100 Subject: [PATCH 523/701] Add POD version of TPCFastTransform The TPCFastTransformPOD is a pointerless version of the TPCFastTransform. It can be created from the original TPCFastTransform as e.g. auto lold = o2::gpu::TPCFastTransform::loadFromFile("o2-gpu-TPCFastTransform.root","ccdb_object"); // load original transform std::vector v; // one has to provide a vector (could be a std or pmr), which later can be messaged via DPL auto* pod = o2::gpu::TPCFastTransformPOD::create(v, *lold); // pointer pod is just v.data() cast to TPCFastTransformPOD* // run test: pod->test(*lold); [INFO] (ns per call) original this Nmissmatch [INFO] getCorrection 1.330e+02 1.400e+02 0 [INFO] getCorrectionInvCorrectedX 8.856e+01 8.434e+01 0 [INFO] getCorrectionInvUV 6.266e+01 6.142e+01 0 It can be also created directly from the TPCFastSpaceChargeCorrection as TPCFastSpaceChargeCorrection& oldCorr = lold->getCorrection(); auto* pod = o2::gpu::TPCFastTransformPOD::create(v, oldCorr); but in this case one should afterwards set the vdrift and t0 using provided getters. TPCFastTransformPOD replicates all the methods of the TPCFastTransform (and of the TPCFastSpaceChargeCorrection), including those which allow to query rescaled corrections (by providing refernce maps and scaling coefficients). Since the idea of this class is to create a final correction map as a weighted sum of different contribution and to distribute it to consumer processes via shared memory, also the query methods w/o rescaling are added, they have the suffix _new added. Eventually, the scalable legacy methods can be suppressed and the suffix new can be dropped. --- GPU/TPCFastTransformation/CMakeLists.txt | 1 + .../TPCFastSpaceChargeCorrection.h | 2 + .../TPCFastTransformPOD.cxx | 245 +++++ .../TPCFastTransformPOD.h | 916 ++++++++++++++++++ .../TPCFastTransformationLinkDef_O2.h | 1 + 5 files changed, 1165 insertions(+) create mode 100644 GPU/TPCFastTransformation/TPCFastTransformPOD.cxx create mode 100644 GPU/TPCFastTransformation/TPCFastTransformPOD.h diff --git a/GPU/TPCFastTransformation/CMakeLists.txt b/GPU/TPCFastTransformation/CMakeLists.txt index 182a66fb28296..769e9981102ef 100644 --- a/GPU/TPCFastTransformation/CMakeLists.txt +++ b/GPU/TPCFastTransformation/CMakeLists.txt @@ -26,6 +26,7 @@ set(SRCS TPCFastSpaceChargeCorrectionMap.cxx TPCFastTransform.cxx CorrectionMapsHelper.cxx + TPCFastTransformPOD.cxx ) if(NOT ALIGPU_BUILD_TYPE STREQUAL "Standalone") diff --git a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h index 6dcf30b2991ba..aedb2531470fc 100644 --- a/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h +++ b/GPU/TPCFastTransformation/TPCFastSpaceChargeCorrection.h @@ -41,6 +41,8 @@ namespace gpu /// class TPCFastSpaceChargeCorrection : public FlatObject { + friend class TPCFastTransformPOD; + public: // obsolete structure, declared here only for backward compatibility struct SliceInfo { diff --git a/GPU/TPCFastTransformation/TPCFastTransformPOD.cxx b/GPU/TPCFastTransformation/TPCFastTransformPOD.cxx new file mode 100644 index 0000000000000..016eed2e6beb4 --- /dev/null +++ b/GPU/TPCFastTransformation/TPCFastTransformPOD.cxx @@ -0,0 +1,245 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TPCFastTransformPOD.cxx +/// \brief Implementation of POD correction map +/// +/// \author ruben.shahoayn@cern.ch + +/// \brief Implementation of POD correction map +/// +/// \author ruben.shahoayn@cern.ch + +#if !defined(GPUCA_NO_ROOT) && !defined(GPUCA_NO_FMT) && !defined(GPUCA_STANDALONE) +#include +#endif +#include "TPCFastTransformPOD.h" +#include "GPUDebugStreamer.h" + +namespace o2 +{ +namespace gpu +{ + +#if !defined(GPUCA_NO_ROOT) && !defined(GPUCA_NO_FMT) && !defined(GPUCA_STANDALONE) + +size_t TPCFastTransformPOD::estimateSize(const TPCFastSpaceChargeCorrection& origCorr) +{ + // estimate size of own buffer + const size_t selfSizeFix = sizeof(TPCFastTransformPOD); + size_t nextDynOffs = alignOffset(selfSizeFix); + nextDynOffs = alignOffset(nextDynOffs + origCorr.mNumberOfScenarios * sizeof(size_t)); // spline scenarios start here + // space for splines + for (int isc = 0; isc < origCorr.mNumberOfScenarios; isc++) { + const auto& spline = origCorr.mScenarioPtr[isc]; + nextDynOffs = alignOffset(nextDynOffs + sizeof(spline)); + } + // space for splines data + for (int is = 0; is < 3; is++) { + for (int sector = 0; sector < origCorr.mGeo.getNumberOfSectors(); sector++) { + for (int row = 0; row < NROWS; row++) { + const auto& spline = origCorr.getSpline(sector, row); + int nPar = spline.getNumberOfParameters(); + if (is == 1) { + nPar = nPar / 3; + } + if (is == 2) { + nPar = nPar * 2 / 3; + } + nextDynOffs += nPar * sizeof(float); + } + } + } + nextDynOffs = alignOffset(nextDynOffs); + return nextDynOffs; +} + +TPCFastTransformPOD* TPCFastTransformPOD::create(char* buff, size_t buffSize, const TPCFastSpaceChargeCorrection& origCorr) +{ + // instantiate object to already created buffer of the right size + assert(buffSize > sizeof(TPCFastTransformPOD)); + auto& podMap = getNonConst(buff); + podMap.mApplyCorrection = true; // by default always apply corrections + + // copy fixed size data --- start + podMap.mNumberOfScenarios = origCorr.mNumberOfScenarios; + std::memcpy(&podMap.mGeo, &origCorr.mGeo, sizeof(TPCFastTransformGeo)); // copy geometry (fixed size) + for (int sector = 0; sector < TPCFastTransformGeo::getNumberOfSectors(); sector++) { + for (int row = 0; row < NROWS; row++) { + podMap.mSectorRowInfos[NROWS * sector + row] = origCorr.getSectorRowInfo(sector, row); + } + } + podMap.mTimeStamp = origCorr.mTimeStamp; + // + // init data members coming from the TPCFastTrasform + podMap.mVdrift = 0.; + podMap.mT0 = 0.; + // copy fixed size data --- end + + size_t nextDynOffs = alignOffset(sizeof(TPCFastTransformPOD)); + + // copy sector scenarios + podMap.mOffsScenariosOffsets = nextDynOffs; // spline scenarios offsets start here + LOGP(debug, "Set mOffsScenariosOffsets = {}", podMap.mOffsScenariosOffsets); + nextDynOffs = alignOffset(nextDynOffs + podMap.mNumberOfScenarios * sizeof(size_t)); // spline scenarios start here + + // copy spline objects + size_t* scenOffs = reinterpret_cast(buff + podMap.mOffsScenariosOffsets); + for (int isc = 0; isc < origCorr.mNumberOfScenarios; isc++) { + scenOffs[isc] = nextDynOffs; + const auto& spline = origCorr.mScenarioPtr[isc]; + if (buffSize < nextDynOffs + sizeof(spline)) { + throw std::runtime_error(fmt::format("attempt to copy {} bytes for spline for scenario {} to {}, overflowing the buffer of size {}", sizeof(spline), isc, nextDynOffs + sizeof(spline), buffSize)); + } + std::memcpy(buff + scenOffs[isc], &spline, sizeof(spline)); + nextDynOffs = alignOffset(nextDynOffs + sizeof(spline)); + LOGP(debug, "Copy {} bytes for spline scenario {} (ptr:{}) to offsset {}", sizeof(spline), isc, (void*)&spline, scenOffs[isc]); + } + + // copy splines data + for (int is = 0; is < 3; is++) { + float* data = reinterpret_cast(buff + nextDynOffs); + LOGP(debug, "splinID={} start offset {} -> {}", is, nextDynOffs, (void*)data); + for (int sector = 0; sector < origCorr.mGeo.getNumberOfSectors(); sector++) { + podMap.mSplineDataOffsets[sector][is] = nextDynOffs; + size_t rowDataOffs = 0; + for (int row = 0; row < NROWS; row++) { + const auto& spline = origCorr.getSpline(sector, row); + const float* dataOr = origCorr.getCorrectionData(sector, row, is); + int nPar = spline.getNumberOfParameters(); + if (is == 1) { + nPar = nPar / 3; + } + if (is == 2) { + nPar = nPar * 2 / 3; + } + LOGP(debug, "Copying {} floats for spline{} of sector:{} row:{} to offset {}", nPar, is, sector, row, nextDynOffs); + size_t nbcopy = nPar * sizeof(float); + if (buffSize < nextDynOffs + nbcopy) { + throw std::runtime_error(fmt::format("attempt to copy {} bytes of data for spline{} of sector{}/row{} to {}, overflowing the buffer of size {}", nbcopy, is, sector, row, nextDynOffs, buffSize)); + } + std::memcpy(data, dataOr, nbcopy); + podMap.getSectorRowInfo(sector, row).dataOffsetBytes[is] = rowDataOffs; + rowDataOffs += nbcopy; + data += nPar; + nextDynOffs += nbcopy; + } + } + } + podMap.mTotalSize = alignOffset(nextDynOffs); + if (buffSize != podMap.mTotalSize) { + throw std::runtime_error(fmt::format("Estimated buffer size {} differs from filled one {}", buffSize, podMap.mTotalSize)); + } + return &getNonConst(buff); +} + +TPCFastTransformPOD* TPCFastTransformPOD::create(char* buff, size_t buffSize, const TPCFastTransform& src) +{ + // instantiate objec to already created buffer of the right size + auto podMap = create(buff, buffSize, src.getCorrection()); + // set data members of TPCFastTransform + podMap->mVdrift = src.getVDrift(); + podMap->mT0 = src.getT0(); + // copy fixed size data --- end + return podMap; +} + +bool TPCFastTransformPOD::test(const TPCFastSpaceChargeCorrection& origCorr, int npoints) const +{ + if (npoints < 1) { + return false; + } + std::vector sector, row; + std::vector y, z; + std::vector> corr0, corr1; + std::vector> corrInv0, corrInv1; + std::vector corrInvX0, corrInvX1; + + sector.reserve(npoints); + row.reserve(npoints); + y.reserve(npoints); + z.reserve(npoints); + corr0.reserve(npoints); + corr1.reserve(npoints); + corrInv0.reserve(npoints); + corrInv1.reserve(npoints); + corrInvX0.reserve(npoints); + corrInvX1.reserve(npoints); + + for (int i = 0; i < npoints; i++) { + sector.push_back(gRandom->Integer(NSECTORS)); + row.push_back(gRandom->Integer(NROWS)); + y.push_back(2 * (gRandom->Rndm() - 0.5) * mGeo.getRowInfo(row.back()).getYmax()); + z.push_back((sector.back() < NSECTORS / 2 ? 1.f : -1.f) * gRandom->Rndm() * 240); + } + long origStart[3], origEnd[3], thisStart[3], thisEnd[3]; + origStart[0] = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + for (int i = 0; i < npoints; i++) { + std::array val; + origCorr.getCorrectionLocal(sector[i], row[i], y[i], z[i], val[0], val[1], val[2]); + corr0.push_back(val); + } + + origEnd[0] = origStart[1] = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + for (int i = 0; i < npoints; i++) { + std::array val; + origCorr.getCorrectionYZatRealYZ(sector[i], row[i], y[i], z[i], val[0], val[1]); + corrInv0.push_back(val); + } + + origEnd[1] = origStart[2] = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + for (int i = 0; i < npoints; i++) { + corrInvX0.push_back(origCorr.getCorrectionXatRealYZ(sector[i], row[i], y[i], z[i])); + } + // + origEnd[2] = thisStart[0] = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + for (int i = 0; i < npoints; i++) { + std::array val; + this->getCorrectionLocal(sector[i], row[i], y[i], z[i], val[0], val[1], val[2]); + corr1.push_back(val); + } + thisEnd[0] = thisStart[1] = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + for (int i = 0; i < npoints; i++) { + std::array val; + this->getCorrectionYZatRealYZ(sector[i], row[i], y[i], z[i], val[0], val[1]); + corrInv1.push_back(val); + } + + thisEnd[1] = thisStart[2] = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + for (int i = 0; i < npoints; i++) { + corrInvX1.push_back(this->getCorrectionXatRealYZ(sector[i], row[i], y[i], z[i])); + } + thisEnd[2] = std::chrono::time_point_cast(std::chrono::system_clock::now()).time_since_epoch().count(); + // + size_t ndiff[3] = {}; + for (int i = 0; i < npoints; i++) { + if (corr0[i][0] != corr1[i][0] || corr0[i][1] != corr1[i][1] || corr0[i][2] != corr1[i][2]) { + ndiff[0]++; + } + if (corrInv0[i][0] != corrInv1[i][0] || corrInv0[i][1] != corrInv1[i][1]) { + ndiff[1]++; + } + if (corrInvX0[i] != corrInvX1[i]) { + ndiff[2]++; + } + } + // + LOGP(info, " (ns per call) original this Nmissmatch"); + LOGP(info, "getCorrection {:.3e} {:.3e} {}", double(origEnd[0] - origStart[0]) / npoints * 1000., double(thisEnd[0] - thisStart[0]) / npoints * 1000., ndiff[0]); + LOGP(info, "getCorrectionInvCorrectedX {:.3e} {:.3e} {}", double(origEnd[1] - origStart[1]) / npoints * 1000., double(thisEnd[1] - thisStart[1]) / npoints * 1000., ndiff[1]); + LOGP(info, "getCorrectionInvUV {:.3e} {:.3e} {}", double(origEnd[2] - origStart[2]) / npoints * 1000., double(thisEnd[2] - thisStart[2]) / npoints * 1000., ndiff[2]); + return ndiff[0] == 0 && ndiff[1] == 0 && ndiff[2] == 0; +} + +#endif + +} // namespace gpu +} // namespace o2 diff --git a/GPU/TPCFastTransformation/TPCFastTransformPOD.h b/GPU/TPCFastTransformation/TPCFastTransformPOD.h new file mode 100644 index 0000000000000..ca54a74115068 --- /dev/null +++ b/GPU/TPCFastTransformation/TPCFastTransformPOD.h @@ -0,0 +1,916 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file TPCFastTransformPOD.h +/// \brief POD correction map +/// +/// \author ruben.shahoayn@cern.ch + +#ifndef ALICEO2_GPU_TPCFastTransformPOD_H +#define ALICEO2_GPU_TPCFastTransformPOD_H + +#include "GPUCommonRtypes.h" +#include "TPCFastTransform.h" + +/* +Binary buffer should be cast to TPCFastTransformPOD class using static TPCFastTransformPOD& t = get(buffer); method, +so that its head becomes `this` pointer of the object. + +First we have all the fixed size data members mentioned explicitly. Part of them is duplicating fixed size +data members of TPCFastSpaceChargeCorrection but those starting with mOffs... provide the offset in bytes +(wrt this) for dynamic data which cannot be declared as data member explicitly (since we cannot have any +pointer except `this`) but obtained via getters using stored offsets wrt `this`. +This is followed dynamic part itself. + +dynamic part layout: +1) size_t[ mNumberOfScenarios ] array starting at offset mOffsScenariosOffsets, each element is the offset +of distict spline object (scenario in TPCFastSpaceChargeCorrection) +2) size_t[ mNSplineIDs ] array starting at offset mOffsSplineDataOffsets, each element is the offset of the +beginning of splines data for give splineID + +*/ + +namespace o2 +{ +namespace gpu +{ +class TPCFastTransformPOD +{ + public: + using SliceInfo = TPCFastSpaceChargeCorrection::SliceInfo; // obsolete + using GridInfo = TPCFastSpaceChargeCorrection::GridInfo; + using SectorRowInfo = TPCFastSpaceChargeCorrection::SectorRowInfo; + + using SplineTypeXYZ = TPCFastSpaceChargeCorrection::SplineTypeXYZ; + using SplineTypeInvX = TPCFastSpaceChargeCorrection::SplineTypeInvX; + using SplineTypeInvYZ = TPCFastSpaceChargeCorrection::SplineTypeInvYZ; + using SplineType = TPCFastSpaceChargeCorrection::SplineType; + + /// convert prefilled buffer to TPCFastTransformPOD + GPUd() static const TPCFastTransformPOD& get(const char* head) { return *reinterpret_cast(head); } + + /// _______________ high level methods a la TPCFastTransform _______________________ + /// + // Methods taking extra reference transform are legacy compound transforms used to scale corrections. + GPUd() void Transform(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime = 0, const TPCFastTransformPOD* ref = nullptr, const TPCFastTransformPOD* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; + GPUd() void TransformXYZ(int32_t sector, int32_t row, float& x, float& y, float& z, const TPCFastTransformPOD* ref = nullptr, const TPCFastTransformPOD* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; + + GPUd() void Transform_new(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime = 0) const; + GPUd() void TransformXYZ_new(int32_t sector, int32_t row, float& x, float& y, float& z) const; + + /// Transformation in the time frame + GPUd() void TransformInTimeFrame(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float maxTimeBin) const; + GPUd() void TransformInTimeFrame(int32_t sector, float time, float& z, float maxTimeBin) const; + + /// Inverse transformation + GPUd() void InverseTransformInTimeFrame(int32_t sector, int32_t row, float /*x*/, float y, float z, float& pad, float& time, float maxTimeBin) const; + GPUd() float InverseTransformInTimeFrame(int32_t sector, float z, float maxTimeBin) const; + + /// Inverse transformation: Transformed Y and Z -> transformed X + GPUd() void InverseTransformYZtoX(int32_t sector, int32_t row, float y, float z, float& x, const TPCFastTransformPOD* ref = nullptr, const TPCFastTransformPOD* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; + GPUd() void InverseTransformYZtoX_new(int32_t sector, int32_t row, float y, float z, float& x) const; + + /// Inverse transformation: Transformed Y and Z -> Y and Z, transformed w/o space charge correction + GPUd() void InverseTransformYZtoNominalYZ(int32_t sector, int32_t row, float y, float z, float& ny, float& nz, const TPCFastTransformPOD* ref = nullptr, const TPCFastTransformPOD* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; + GPUd() void InverseTransformYZtoNominalYZ_new(int32_t sector, int32_t row, float y, float z, float& ny, float& nz) const; + + /// Inverse transformation: Transformed X, Y and Z -> X, Y and Z, transformed w/o space charge correction + GPUd() void InverseTransformXYZtoNominalXYZ(int32_t sector, int32_t row, float x, float y, float z, float& nx, float& ny, float& nz, const TPCFastTransformPOD* ref = nullptr, const TPCFastTransformPOD* ref2 = nullptr, float scale = 0.f, float scale2 = 0.f, int32_t scaleMode = 0) const; + GPUd() void InverseTransformXYZtoNominalXYZ_new(int32_t sector, int32_t row, float x, float y, float z, float& nx, float& ny, float& nz) const; + + /// Ideal transformation with Vdrift only - without calibration + GPUd() void TransformIdeal(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime) const; + GPUd() void TransformIdealZ(int32_t sector, float time, float& z, float vertexTime) const; + + GPUd() void convPadTimeToLocal(int32_t sector, int32_t row, float pad, float time, float& y, float& z, float vertexTime) const; + GPUd() void convPadTimeToLocalInTimeFrame(int32_t sector, int32_t row, float pad, float time, float& y, float& z, float maxTimeBin) const; + + GPUd() void convLocalToPadTime(int32_t sector, int32_t row, float y, float z, float& pad, float& time, float vertexTime) const; + GPUd() void convLocalToPadTimeInTimeFrame(int32_t sector, int32_t row, float y, float z, float& pad, float& time, float maxTimeBin) const; + + GPUd() float convTimeToZinTimeFrame(int32_t sector, float time, float maxTimeBin) const; + GPUd() float convZtoTimeInTimeFrame(int32_t sector, float z, float maxTimeBin) const; + GPUd() float convDeltaTimeToDeltaZinTimeFrame(int32_t sector, float deltaTime) const; + GPUd() float convDeltaZtoDeltaTimeInTimeFrame(int32_t sector, float deltaZ) const; + GPUd() float convDeltaZtoDeltaTimeInTimeFrameAbs(float deltaZ) const; + GPUd() float convZOffsetToVertexTime(int32_t sector, float zOffset, float maxTimeBin) const; + GPUd() float convVertexTimeToZOffset(int32_t sector, float vertexTime, float maxTimeBin) const; + + /// _______________ methods a la TPCFastSpaceChargeCorrection: cluster correction _______________________ + void setApplyCorrectionOn() { mApplyCorrection = 1; } + void setApplyCorrectionOff() { mApplyCorrection = 0; } + bool isCorrectionApplied() { return mApplyCorrection; } + + /// TPC geometry information + GPUd() const TPCFastTransformGeo& getGeometry() const { return mGeo; } + + /// Gives TPC sector & row info + GPUd() const SectorRowInfo& getSectorRowInfo(int32_t sector, int32_t row) const { return mSectorRowInfos[NROWS * sector + row]; } + + /// Gives TPC sector & row info + GPUd() SectorRowInfo& getSectorRowInfo(int32_t sector, int32_t row) { return mSectorRowInfos[NROWS * sector + row]; } + + /// Gives its own size including dynamic part + GPUd() size_t size() const { return mTotalSize; } + + /// Gives the time stamp of the current calibaration parameters + GPUd() long int getTimeStamp() const { return mTimeStamp; } + + /// Return mVDrift in cm / time bin + GPUd() float getVDrift() const { return mVdrift; } + + /// Return T0 in time bin units + GPUd() float getT0() const { return mT0; } + + /// Return IDC estimator + GPUd() float getIDC() const { return mIDC; } + + /// Return Lumi estimator + GPUd() float getLumi() const { return mLumi; } + + /// maximal possible drift time of the active area + GPUd() float getMaxDriftTime(int32_t sector, int32_t row, float pad) const; + + /// maximal possible drift time of the active area + GPUd() float getMaxDriftTime(int32_t sector, int32_t row) const; + + /// maximal possible drift time of the active area + GPUd() float getMaxDriftTime(int32_t sector) const; + + /// Sets the time stamp of the current calibaration + GPUd() void setTimeStamp(long int v) { mTimeStamp = v; } + + /// Sets current vdrift + GPUd() void setVDrift(float v) { mVdrift = v; } + + /// Sets current T0 + GPUd() void setT0(float v) { mT0 = v; } + + /// Sets IDC estimator + GPUd() void setIDC(float v) { mIDC = v; } + + /// Sets CTP Lumi estimator + GPUd() void setLumi(float v) { mLumi = v; } + + /// Gives a reference to a spline + GPUd() const SplineType& getSpline(int32_t sector, int32_t row) const { return *reinterpret_cast(getThis() + getScenarioOffset(getSectorRowInfo(sector, row).splineScenarioID)); } + + /// Gives pointer to spline data + GPUd() const float* getCorrectionData(int32_t sector, int32_t row, int32_t iSpline = 0) const { return reinterpret_cast(getThis() + mSplineDataOffsets[sector][iSpline] + getSectorRowInfo(sector, row).dataOffsetBytes[iSpline]); } + + /// Gives const pointer to a spline for the inverse X correction + GPUd() const SplineTypeInvX& getSplineInvX(int32_t sector, int32_t row) const { return reinterpret_cast(getSpline(sector, row)); } + + /// Gives pointer to spline data for the inverse X correction + GPUd() const float* getCorrectionDataInvX(int32_t sector, int32_t row) const { return getCorrectionData(sector, row, 1); } + + /// Gives const pointer to a spline for the inverse YZ correction + GPUd() const SplineTypeInvYZ& getSplineInvYZ(int32_t sector, int32_t row) const { return reinterpret_cast(getSpline(sector, row)); } + + /// Gives pointer to spline data for the inverse YZ correction + GPUd() const float* getCorrectionDataInvYZ(int32_t sector, int32_t row) const { return getCorrectionData(sector, row, 2); } + + /// _______________ The main method: cluster correction _______________________ + GPUdi() void getCorrectionLocal(int32_t sector, int32_t row, float y, float z, float& dx, float& dy, float& dz) const; + + /// inverse correction: Real Y and Z -> Real X + GPUd() float getCorrectionXatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const; + + /// inverse correction: Real Y and Z -> measred Y and Z + GPUd() void getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ, float& measuredY, float& measuredZ) const; + + /// transformation in the sector local frame + GPUd() void TransformLocal(int32_t sector, int32_t row, float& x, float& y, float& z, const TPCFastTransformPOD* ref, const TPCFastTransformPOD* ref2, float scale, float scale2, int32_t scaleMode) const; + GPUd() void TransformLocal(int32_t sector, int32_t row, float& x, float& y, float& z) const; + + /// _______________ Utilities _______________________________________________ + + /// convert local y, z to internal grid coordinates u,v + /// return values: u, v, scaling factor + GPUd() void convLocalToGrid(int32_t sector, int32_t row, float y, float z, float& u, float& v, float& s) const; + + /// convert internal grid coordinates u,v to local y, z + /// return values: y, z, scaling factor + GPUd() void convGridToLocal(int32_t sector, int32_t row, float u, float v, float& y, float& z) const; + + /// convert real Y, Z to the internal grid coordinates + /// return values: u, v, scaling factor + GPUd() void convRealLocalToGrid(int32_t sector, int32_t row, float y, float z, float& u, float& v, float& s) const; + + /// convert internal grid coordinates to the real Y, Z + /// return values: y, z + GPUd() void convGridToRealLocal(int32_t sector, int32_t row, float u, float v, float& y, float& z) const; + + GPUd() bool isLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const; + GPUd() bool isRealLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const; + +#if !defined(GPUCA_GPUCODE) + /// Create POD transform from old flat-buffer one. Provided vector will serve as a buffer + template + static TPCFastTransformPOD* create(V& destVector, const TPCFastTransform& src); + + /// create filling only part corresponding to TPCFastSpaceChargeCorrection. Data members coming from TPCFastTransform (e.g. VDrift, T0..) are not set + template + static TPCFastTransformPOD* create(V& destVector, const TPCFastSpaceChargeCorrection& src); + + bool test(const TPCFastTransform& src, int32_t npoints = 100000) const { return test(src.getCorrection(), npoints); } + bool test(const TPCFastSpaceChargeCorrection& origCorr, int32_t npoints = 100000) const; +#endif + + /// Print method + void print() const; + + GPUd() float convDriftLengthToTime(float driftLength, float vertexTime) const; + + static constexpr int NROWS = 152; + static constexpr int NSECTORS = TPCFastTransformGeo::getNumberOfSectors(); + static constexpr int NSplineIDs = 3; ///< number of spline data sets for each sector/row + + private: +#if !defined(GPUCA_GPUCODE) + static constexpr size_t AlignmentBytes = 8; + static size_t alignOffset(size_t offs) + { + auto res = offs % AlignmentBytes; + return res ? offs + (AlignmentBytes - res) : offs; + } + static size_t estimateSize(const TPCFastTransform& src) { return estimateSize(src.getCorrection()); } + static size_t estimateSize(const TPCFastSpaceChargeCorrection& origCorr); + static TPCFastTransformPOD* create(char* buff, size_t buffSize, const TPCFastTransform& src); + static TPCFastTransformPOD* create(char* buff, size_t buffSize, const TPCFastSpaceChargeCorrection& src); + ///< get address to which the offset in bytes must be added to arrive to particular dynamic part + GPUd() const char* getThis() const { return reinterpret_cast(this); } + GPUd() static TPCFastTransformPOD& getNonConst(char* head) { return *reinterpret_cast(head); } +#endif + + ///< return offset of the spline object start (equivalent of mScenarioPtr in the TPCFastSpaceChargeCorrection) + GPUd() size_t getScenarioOffset(int s) const { return (reinterpret_cast(getThis() + mOffsScenariosOffsets))[s]; } + + bool mApplyCorrection{}; ///< flag to apply corrections + int mNumberOfScenarios{}; ///< Number of approximation spline scenarios + size_t mTotalSize{}; ///< total size of the buffer + size_t mOffsScenariosOffsets{}; ///< start of the array of mNumberOfScenarios offsets for each type of spline + size_t mSplineDataOffsets[TPCFastTransformGeo::getNumberOfSectors()][NSplineIDs]; ///< start of data for each sector and iSpline data + long int mTimeStamp{}; ///< time stamp of the current calibration + float mT0; ///< T0 in [time bin] + float mVdrift; ///< VDrift in [cm/time bin] + float mLumi; ///< luminosity estimator (for info only) + float mIDC; ///< IDC estimator (for info only) + + TPCFastTransformGeo mGeo; ///< TPC geometry information + SectorRowInfo mSectorRowInfos[NROWS * TPCFastTransformGeo::getNumberOfSectors()]; + + ClassDefNV(TPCFastTransformPOD, 0); +}; + +GPUdi() void TPCFastTransformPOD::getCorrectionLocal(int32_t sector, int32_t row, float y, float z, float& dx, float& dy, float& dz) const +{ + const auto& info = getSectorRowInfo(sector, row); + const SplineType& spline = getSpline(sector, row); + const float* splineData = getCorrectionData(sector, row); + + float u, v, s; + convLocalToGrid(sector, row, y, z, u, v, s); + + float dxyz[3]; + spline.interpolateAtU(splineData, u, v, dxyz); + + if (CAMath::Abs(dxyz[0]) > 100.f || CAMath::Abs(dxyz[1]) > 100.f || CAMath::Abs(dxyz[2]) > 100.f) { + s = 0.f; // TODO: DR: Protect from FPEs, fix upstream and remove once guaranteed that it is fixed + } + + dx = s * GPUCommonMath::Clamp(dxyz[0], info.minCorr[0], info.maxCorr[0]); + dy = s * GPUCommonMath::Clamp(dxyz[1], info.minCorr[1], info.maxCorr[1]); + dz = s * GPUCommonMath::Clamp(dxyz[2], info.minCorr[2], info.maxCorr[2]); +} + +GPUdi() float TPCFastTransformPOD::getCorrectionXatRealYZ(int32_t sector, int32_t row, float realY, float realZ) const +{ + const auto& info = getSectorRowInfo(sector, row); + float u, v, s; + convRealLocalToGrid(sector, row, realY, realZ, u, v, s); + float dx = 0; + getSplineInvX(sector, row).interpolateAtU(getCorrectionDataInvX(sector, row), u, v, &dx); + if (CAMath::Abs(dx) > 100.f) { + s = 0.f; // TODO: DR: Protect from FPEs, fix upstream and remove once guaranteed that it is fixed + } + dx = s * GPUCommonMath::Clamp(dx, info.minCorr[0], info.maxCorr[0]); + return dx; +} + +GPUdi() void TPCFastTransformPOD::getCorrectionYZatRealYZ(int32_t sector, int32_t row, float realY, float realZ, float& y, float& z) const +{ + float u, v, s; + convRealLocalToGrid(sector, row, realY, realZ, u, v, s); + const auto& info = getSectorRowInfo(sector, row); + float dyz[2]; + getSplineInvYZ(sector, row).interpolateAtU(getCorrectionDataInvYZ(sector, row), u, v, dyz); + if (CAMath::Abs(dyz[0]) > 100.f || CAMath::Abs(dyz[1]) > 100.f) { + s = 0.f; // TODO: DR: Protect from FPEs, fix upstream and remove once guaranteed that it is fixed + } + y = s * GPUCommonMath::Clamp(dyz[0], info.minCorr[1], info.maxCorr[1]); + z = s * GPUCommonMath::Clamp(dyz[1], info.minCorr[2], info.maxCorr[2]); +} + +GPUdi() void TPCFastTransformPOD::convLocalToGrid(int32_t sector, int32_t row, float y, float z, float& u, float& v, float& s) const +{ + /// convert local y, z to internal grid coordinates u,v + /// return values: u, v, scaling factor + const SplineType& spline = getSpline(sector, row); + getSectorRowInfo(sector, row).gridMeasured.convLocalToGridUntruncated(y, z, u, v, s); + // shrink to the grid + u = GPUCommonMath::Clamp(u, 0.f, (float)spline.getGridX1().getUmax()); + v = GPUCommonMath::Clamp(v, 0.f, (float)spline.getGridX2().getUmax()); +} + +GPUdi() void TPCFastTransformPOD::convGridToLocal(int32_t sector, int32_t row, float gridU, float gridV, float& y, float& z) const +{ + /// convert internal grid coordinates u,v to local y, z + getSectorRowInfo(sector, row).gridMeasured.convGridToLocal(gridU, gridV, y, z); +} + +GPUdi() void TPCFastTransformPOD::convRealLocalToGrid(int32_t sector, int32_t row, float y, float z, float& u, float& v, float& s) const +{ + /// convert real y, z to the internal grid coordinates + scale + const SplineType& spline = getSpline(sector, row); + getSectorRowInfo(sector, row).gridReal.convLocalToGridUntruncated(y, z, u, v, s); + // shrink to the grid + u = GPUCommonMath::Clamp(u, 0.f, (float)spline.getGridX1().getUmax()); + v = GPUCommonMath::Clamp(v, 0.f, (float)spline.getGridX2().getUmax()); +} + +GPUdi() void TPCFastTransformPOD::convGridToRealLocal(int32_t sector, int32_t row, float gridU, float gridV, float& y, float& z) const +{ + /// convert internal grid coordinates u,v to the real y, z + getSectorRowInfo(sector, row).gridReal.convGridToLocal(gridU, gridV, y, z); +} + +GPUdi() bool TPCFastTransformPOD::isLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const +{ + /// check if local y, z are inside the grid + float u, v, s; + getSectorRowInfo(sector, row).gridMeasured.convLocalToGridUntruncated(y, z, u, v, s); + const auto& spline = getSpline(sector, row); + // shrink to the grid + if (u < 0.f || u > (float)spline.getGridX1().getUmax() || // + v < 0.f || v > (float)spline.getGridX2().getUmax()) { + return false; + } + return true; +} + +GPUdi() bool TPCFastTransformPOD::isRealLocalInsideGrid(int32_t sector, int32_t row, float y, float z) const +{ + /// check if local y, z are inside the grid + float u, v, s; + getSectorRowInfo(sector, row).gridReal.convLocalToGridUntruncated(y, z, u, v, s); + const auto& spline = getSpline(sector, row); + // shrink to the grid + if (u < 0.f || u > (float)spline.getGridX1().getUmax() || // + v < 0.f || v > (float)spline.getGridX2().getUmax()) { + return false; + } + return true; +} + +#if !defined(GPUCA_GPUCODE) +/// Create POD transform from old flat-buffer one. Provided vector will serve as a buffer +template +TPCFastTransformPOD* TPCFastTransformPOD::create(V& destVector, const TPCFastTransform& src) +{ + const auto& origCorr = src.getCorrection(); + size_t estSize = estimateSize(src); + destVector.resize(estSize); // allocate exact size + LOGP(debug, "OrigCorrSize:{} SelfSize: {} Estimated POS size: {}", src.getCorrection().getFlatBufferSize(), sizeof(TPCFastTransformPOD), estSize); + char* base = destVector.data(); + auto res = create(destVector.data(), destVector.size(), src); + res->setTimeStamp(src.getTimeStamp()); + res->setVDrift(src.getVDrift()); + res->setT0(src.getT0()); + res->setLumi(src.getLumi()); + if (src.isIDCSet()) { + res->setIDC(src.getIDC()); + } + return res; +} + +template +TPCFastTransformPOD* TPCFastTransformPOD::create(V& destVector, const TPCFastSpaceChargeCorrection& origCorr) +{ + // create filling only part corresponding to TPCFastSpaceChargeCorrection. Data members coming from TPCFastTransform (e.g. VDrift, T0..) are not set + size_t estSize = estimateSize(origCorr); + destVector.resize(estSize); // allocate exact size + LOGP(debug, "OrigCorrSize:{} SelfSize: {} Estimated POS size: {}", origCorr.getFlatBufferSize(), sizeof(TPCFastTransformPOD), estSize); + char* base = destVector.data(); + return create(destVector.data(), destVector.size(), origCorr); +} +#endif + +GPUdi() void TPCFastTransformPOD::TransformLocal(int32_t sector, int32_t row, float& x, float& y, float& z, const TPCFastTransformPOD* ref, const TPCFastTransformPOD* ref2, float scale, float scale2, int32_t scaleMode) const +{ + GPUCA_RTC_SPECIAL_CODE(ref2 = nullptr; scale2 = 0.f;); + + if (!mApplyCorrection) { + return; + } + + float dx = 0.f, dy = 0.f, dz = 0.f; + + if ((scale >= 0.f) || (scaleMode == 1) || (scaleMode == 2)) { + getCorrectionLocal(sector, row, y, z, dx, dy, dz); + if (ref) { + if ((scale > 0.f) && (scaleMode == 0)) { // scaling was requested + float val[3]; + ref->getCorrectionLocal(sector, row, y, z, val[0], val[1], val[2]); + dx = (dx - val[0]) * scale + val[0]; + dy = (dy - val[1]) * scale + val[1]; + dz = (dz - val[2]) * scale + val[2]; + } else if ((scale != 0.f) && ((scaleMode == 1) || (scaleMode == 2))) { + float val[3]; + ref->getCorrectionLocal(sector, row, y, z, val[0], val[1], val[2]); + dx = val[0] * scale + dx; + dy = val[1] * scale + dy; + dz = val[2] * scale + dz; + } + } + if (ref2 && (scale2 != 0)) { + float val[3]; + ref2->getCorrectionLocal(sector, row, y, z, val[0], val[1], val[2]); + dx = val[0] * scale2 + dx; + dy = val[1] * scale2 + dy; + dz = val[2] * scale2 + dz; + } + } + + GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamFastTransform)) { + float lx = x, ly = y, lz = z; + + float gx, gy, gz; + getGeometry().convLocalToGlobal(sector, lx, ly, lz, gx, gy, gz); + + float lxT = lx + dx; + float lyT = ly + dy; + float lzT = lz + dz; + + float invYZtoXScaled; + InverseTransformYZtoX(sector, row, lyT, lzT, invYZtoXScaled, ref, ref2, scale, scale2, scaleMode); + + float invYZtoX; + InverseTransformYZtoX(sector, row, lyT, lzT, invYZtoX); + + float YZtoNominalY; + float YZtoNominalZ; + InverseTransformYZtoNominalYZ(sector, row, lyT, lzT, YZtoNominalY, YZtoNominalZ); + + float YZtoNominalYScaled; + float YZtoNominalZScaled; + InverseTransformYZtoNominalYZ(sector, row, lyT, lzT, YZtoNominalYScaled, YZtoNominalZScaled, ref, ref2, scale, scale2, scaleMode); + + float dxRef = 0.f, dyRef = 0.f, dzRef = 0.f; + if (ref) { + ref->getCorrectionLocal(sector, row, y, z, dxRef, dyRef, dzRef); + } + + float dxRef2 = 0.f, dyRef2 = 0.f, dzRef2 = 0.f; + if (ref2) { + ref2->getCorrectionLocal(sector, row, y, z, dxRef2, dyRef2, dzRef2); + } + + float dxOrig, dyOrig, dzOrig; + getCorrectionLocal(sector, row, y, z, dyOrig, dyOrig, dzOrig); + + o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_Transform").data() + // corrections in x, u, v + << "dxOrig=" << dxOrig + << "dyOrig=" << dyOrig + << "dzOrig=" << dzOrig + << "dxRef=" << dxRef + << "dyRef=" << dyRef + << "dzRef=" << dzRef + << "dxRef2=" << dxRef2 + << "dyRef2=" << dyRef2 + << "dzRef2=" << dzRef2 + << "dx=" << dx + << "dy=" << dy + << "dz=" << dz + << "row=" << row + << "sector=" << sector + << "scale=" << scale + << "scale2=" << scale2 + // original local coordinates + << "ly=" << ly + << "lz=" << lz + << "lx=" << lx + // corrected local coordinated + << "lxT=" << lxT + << "lyT=" << lyT + << "lzT=" << lzT + // global uncorrected coordinates + << "gx=" << gx + << "gy=" << gy + << "gz=" << gz + // some transformations which are applied + << "invYZtoX=" << invYZtoX + << "invYZtoXScaled=" << invYZtoXScaled + << "YZtoNominalY=" << YZtoNominalY + << "YZtoNominalYScaled=" << YZtoNominalYScaled + << "YZtoNominalZ=" << YZtoNominalZ + << "YZtoNominalZScaled=" << YZtoNominalZScaled + << "scaleMode=" << scaleMode + << "\n"; + }) + + x += dx; + y += dy; + z += dz; +} + +GPUdi() void TPCFastTransformPOD::TransformLocal(int32_t sector, int32_t row, float& x, float& y, float& z) const +{ + if (!mApplyCorrection) { + return; + } + float dx, dy, dz; + getCorrectionLocal(sector, row, y, z, dx, dy, dz); + + GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamFastTransform)) { + float lx = x, ly = y, lz = z; + float gx, gy, gz; + getGeometry().convLocalToGlobal(sector, lx, ly, lz, gx, gy, gz); + float lxT = lx + dx; + float lyT = ly + dy; + float lzT = lz + dz; + float invYZtoX; + InverseTransformYZtoX_new(sector, row, lyT, lzT, invYZtoX); + + float YZtoNominalY; + float YZtoNominalZ; + InverseTransformYZtoNominalYZ_new(sector, row, lyT, lzT, YZtoNominalY, YZtoNominalZ); + + o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_Transform").data() + // corrections in x, u, v + << "dx=" << dx + << "dy=" << dy + << "dz=" << dz + << "row=" << row + << "sector=" << sector + // original local coordinates + << "ly=" << ly + << "lz=" << lz + << "lx=" << lx + // corrected local coordinated + << "lxT=" << lxT + << "lyT=" << lyT + << "lzT=" << lzT + // global uncorrected coordinates + << "gx=" << gx + << "gy=" << gy + << "gz=" << gz + // some transformations which are applied + << "invYZtoX=" << invYZtoX + << "YZtoNominalY=" << YZtoNominalY + << "YZtoNominalZ=" << YZtoNominalZ + << "\n"; + }) + + x += dx; + y += dy; + z += dz; +} + +GPUdi() void TPCFastTransformPOD::Transform(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime, const TPCFastTransformPOD* ref, const TPCFastTransformPOD* ref2, float scale, float scale2, int32_t scaleMode) const +{ + /// _______________ The main method: cluster transformation _______________________ + /// + /// Transforms raw TPC coordinates to local XYZ withing a sector + /// taking calibration into account. + /// + + const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); + + x = rowInfo.x; + convPadTimeToLocal(sector, row, pad, time, y, z, vertexTime); + TransformLocal(sector, row, x, y, z, ref, ref2, scale, scale2, scaleMode); +} + +GPUdi() void TPCFastTransformPOD::Transform_new(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime) const +{ + /// _______________ The main method: cluster transformation _______________________ + /// + /// Transforms raw TPC coordinates to local XYZ withing a sector + /// taking calibration into account. + /// + + const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); + + x = rowInfo.x; + convPadTimeToLocal(sector, row, pad, time, y, z, vertexTime); + TransformLocal(sector, row, x, y, z); +} + +GPUdi() void TPCFastTransformPOD::TransformXYZ(int32_t sector, int32_t row, float& x, float& y, float& z, const TPCFastTransformPOD* ref, const TPCFastTransformPOD* ref2, float scale, float scale2, int32_t scaleMode) const +{ + + TransformLocal(sector, row, x, y, z, ref, ref2, scale, scale2, scaleMode); +} + +GPUdi() void TPCFastTransformPOD::TransformXYZ_new(int32_t sector, int32_t row, float& x, float& y, float& z) const +{ + + TransformLocal(sector, row, x, y, z); +} + +GPUdi() void TPCFastTransformPOD::TransformInTimeFrame(int32_t sector, float time, float& z, float maxTimeBin) const +{ + float l = (time - mT0 - maxTimeBin) * mVdrift; // drift length cm + z = getGeometry().convDriftLengthToZ1(sector, l); +} + +GPUdi() void TPCFastTransformPOD::TransformInTimeFrame(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float maxTimeBin) const +{ + /// _______________ Special cluster transformation for a time frame _______________________ + /// + /// Same as Transform(), but clusters are shifted in z such, that Z(maxTimeBin)==0 + /// Corrections and Time-Of-Flight correction are not alpplied. + /// + + const TPCFastTransformGeo::RowInfo& rowInfo = getGeometry().getRowInfo(row); + x = rowInfo.x; + convPadTimeToLocalInTimeFrame(sector, row, pad, time, y, z, maxTimeBin); +} + +GPUdi() void TPCFastTransformPOD::InverseTransformInTimeFrame(int32_t sector, int32_t row, float /*x*/, float y, float z, float& pad, float& time, float maxTimeBin) const +{ + /// Inverse transformation to TransformInTimeFrame + convLocalToPadTimeInTimeFrame(sector, row, y, z, pad, time, maxTimeBin); +} + +GPUdi() float TPCFastTransformPOD::InverseTransformInTimeFrame(int32_t sector, float z, float maxTimeBin) const +{ + float pad, time; + InverseTransformInTimeFrame(sector, 0, 0, 0, z, pad, time, maxTimeBin); + return time; +} + +GPUdi() void TPCFastTransformPOD::TransformIdealZ(int32_t sector, float time, float& z, float vertexTime) const +{ + /// _______________ The main method: cluster transformation _______________________ + /// + /// Transforms time TPC coordinates to local Z withing a sector + /// Ideal transformation: only Vdrift from DCS. + /// No space charge corrections, no time of flight correction + /// + + float l = (time - mT0 - vertexTime) * mVdrift; // drift length cm + z = getGeometry().convDriftLengthToZ1(sector, l); +} + +GPUdi() void TPCFastTransformPOD::TransformIdeal(int32_t sector, int32_t row, float pad, float time, float& x, float& y, float& z, float vertexTime) const +{ + /// _______________ The main method: cluster transformation _______________________ + /// + /// Transforms raw TPC coordinates to local XYZ withing a sector + /// Ideal transformation: only Vdrift from DCS. + /// No space charge corrections, no time of flight correction + /// + + x = getGeometry().getRowInfo(row).x; + float driftLength = (time - mT0 - vertexTime) * mVdrift; // drift length cm + getGeometry().convPadDriftLengthToLocal(sector, row, pad, driftLength, y, z); +} + +GPUdi() float TPCFastTransformPOD::convTimeToZinTimeFrame(int32_t sector, float time, float maxTimeBin) const +{ + /// _______________ Special cluster transformation for a time frame _______________________ + /// + /// Same as Transform(), but clusters are shifted in z such, that Z(maxTimeBin)==0 + /// Corrections and Time-Of-Flight correction are not alpplied. + /// Only Z coordinate. + /// + + float v = (time - mT0 - maxTimeBin) * mVdrift; // drift length cm + float z = (sector < getGeometry().getNumberOfSectorsA()) ? -v : v; + return z; +} + +GPUdi() float TPCFastTransformPOD::convZtoTimeInTimeFrame(int32_t sector, float z, float maxTimeBin) const +{ + /// Inverse transformation of convTimeToZinTimeFrame() + float v = (sector < getGeometry().getNumberOfSectorsA()) ? -z : z; + return mT0 + maxTimeBin + v / mVdrift; +} + +GPUdi() float TPCFastTransformPOD::convDeltaTimeToDeltaZinTimeFrame(int32_t sector, float deltaTime) const +{ + float deltaZ = deltaTime * mVdrift; + return sector < getGeometry().getNumberOfSectorsA() ? -deltaZ : deltaZ; +} + +GPUdi() float TPCFastTransformPOD::convDeltaZtoDeltaTimeInTimeFrameAbs(float deltaZ) const +{ + return deltaZ / mVdrift; +} + +GPUdi() float TPCFastTransformPOD::convDeltaZtoDeltaTimeInTimeFrame(int32_t sector, float deltaZ) const +{ + float deltaT = deltaZ / mVdrift; + return sector < getGeometry().getNumberOfSectorsA() ? -deltaT : deltaT; +} + +GPUdi() float TPCFastTransformPOD::getMaxDriftTime(int32_t sector, int32_t row, float pad) const +{ + /// maximal possible drift time of the active area + return convDriftLengthToTime(getGeometry().getTPCzLength(), 0.f); +} + +GPUdi() float TPCFastTransformPOD::getMaxDriftTime(int32_t sector, int32_t row) const +{ + /// maximal possible drift time of the active area + return convDriftLengthToTime(getGeometry().getTPCzLength(), 0.f); +} + +GPUdi() float TPCFastTransformPOD::getMaxDriftTime(int32_t sector) const +{ + /// maximal possible drift time of the active area + return convDriftLengthToTime(getGeometry().getTPCzLength(), 0.f); +} + +GPUdi() void TPCFastTransformPOD::InverseTransformYZtoX(int32_t sector, int32_t row, float realY, float realZ, float& realX, const TPCFastTransformPOD* ref, const TPCFastTransformPOD* ref2, float scale, float scale2, int32_t scaleMode) const +{ + GPUCA_RTC_SPECIAL_CODE(ref2 = nullptr; scale2 = 0.f;); + /// Transformation y,z -> x + + float dx = 0.f; + + if ((scale >= 0.f) || (scaleMode == 1) || (scaleMode == 2)) { + dx = getCorrectionXatRealYZ(sector, row, realY, realZ); + if (ref) { // scaling was requested + if (scaleMode == 0 && scale > 0.f) { + float dxref = ref->getCorrectionXatRealYZ(sector, row, realY, realZ); + dx = (dx - dxref) * scale + dxref; + } else if ((scale != 0) && ((scaleMode == 1) || (scaleMode == 2))) { + float dxref = ref->getCorrectionXatRealYZ(sector, row, realY, realZ); + dx = dxref * scale + dx; + } + } + if (ref2 && (scale2 != 0)) { + float dxref = ref2->getCorrectionXatRealYZ(sector, row, realY, realZ); + dx = dxref * scale2 + dx; + } + } + + realX = getGeometry().getRowInfo(row).x + dx; + + GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamFastTransform)) { + o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_InverseTransformYZtoX").data() + << "sector=" << sector + << "row=" << row + << "scale=" << scale + << "y=" << realY + << "z=" << realZ + << "x=" << realX + << "\n"; + }) +} + +GPUdi() void TPCFastTransformPOD::InverseTransformYZtoX_new(int32_t sector, int32_t row, float realY, float realZ, float& realX) const +{ + /// Transformation y,z -> x + + float dx = 0.f; + + dx = getCorrectionXatRealYZ(sector, row, realY, realZ); + realX = getGeometry().getRowInfo(row).x + dx; + + GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamFastTransform)) { + o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_InverseTransformYZtoX").data() + << "sector=" << sector + << "row=" << row + << "y=" << realY + << "z=" << realZ + << "x=" << realX + << "\n"; + }) +} + +GPUdi() void TPCFastTransformPOD::InverseTransformYZtoNominalYZ(int32_t sector, int32_t row, float realY, float realZ, float& measuredY, float& measuredZ, const TPCFastTransformPOD* ref, const TPCFastTransformPOD* ref2, float scale, float scale2, int32_t scaleMode) const +{ + /// Transformation real y,z -> measured y,z + + GPUCA_RTC_SPECIAL_CODE(ref2 = nullptr; scale2 = 0.f;); + + float dy = 0; + float dz = 0; + + if ((scale >= 0.f) || (scaleMode == 1) || (scaleMode == 2)) { + getCorrectionYZatRealYZ(sector, row, realY, realZ, dy, dz); + + if (ref) { // scaling was requested + if (scaleMode == 0 && scale > 0.f) { + float val[2]; + ref->getCorrectionYZatRealYZ(sector, row, realY, realZ, val[0], val[1]); + dy = (dy - val[0]) * scale + val[0]; + dz = (dz - val[1]) * scale + val[1]; + } else if ((scale != 0) && ((scaleMode == 1) || (scaleMode == 2))) { + float val[2]; + ref->getCorrectionYZatRealYZ(sector, row, realY, realZ, val[0], val[1]); + dy = val[0] * scale + dy; + dz = val[1] * scale + dz; + } + if (ref2 && (scale2 != 0)) { + float val[2]; + ref2->getCorrectionYZatRealYZ(sector, row, realY, realZ, val[0], val[1]); + dy = val[0] * scale2 + dy; + dz = val[1] * scale2 + dz; + } + } + } + + measuredY = realY - dy; + measuredZ = realZ - dz; + + GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamFastTransform)) { + o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_InverseTransformYZtoNominalYZ").data() + << "sector=" << sector + << "row=" << row + << "scale=" << scale + << "real y=" << realY + << "real z=" << realZ + << "measured y=" << measuredY + << "measured z=" << measuredZ + << "\n"; + }) +} + +GPUdi() void TPCFastTransformPOD::InverseTransformYZtoNominalYZ_new(int32_t sector, int32_t row, float realY, float realZ, float& measuredY, float& measuredZ) const +{ + /// Transformation real y,z -> measured y,z + float corrY, corrZ; + getCorrectionYZatRealYZ(sector, row, realY, realZ, corrY, corrZ); + measuredY = realY - corrY; + measuredZ = realZ - corrZ; + + GPUCA_DEBUG_STREAMER_CHECK(if (o2::utils::DebugStreamer::checkStream(o2::utils::StreamFlags::streamFastTransform)) { + o2::utils::DebugStreamer::instance()->getStreamer("debug_fasttransform", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("tree_InverseTransformYZtoNominalYZ").data() + << "sector=" << sector + << "row=" << row + << "real y=" << realY + << "real z=" << realZ + << "measured y=" << measuredY + << "measured z=" << measuredZ + << "\n"; + }) +} + +GPUdi() void TPCFastTransformPOD::InverseTransformXYZtoNominalXYZ(int32_t sector, int32_t row, float x, float y, float z, float& nx, float& ny, float& nz, const TPCFastTransformPOD* ref, const TPCFastTransformPOD* ref2, float scale, float scale2, int32_t scaleMode) const +{ + /// Inverse transformation: Transformed X, Y and Z -> X, Y and Z, transformed w/o space charge correction + int32_t row2 = row + 1; + if (row2 >= getGeometry().getNumberOfRows()) { + row2 = row - 1; + } + float nx1, ny1, nz1; // nominal coordinates for row + float nx2, ny2, nz2; // nominal coordinates for row2 + nx1 = getGeometry().getRowInfo(row).x; + nx2 = getGeometry().getRowInfo(row2).x; + InverseTransformYZtoNominalYZ(sector, row, y, z, ny1, nz1, ref, ref2, scale, scale2, scaleMode); + InverseTransformYZtoNominalYZ(sector, row2, y, z, ny2, nz2, ref, ref2, scale, scale2, scaleMode); + float c1 = (nx2 - nx) / (nx2 - nx1); + float c2 = (nx - nx1) / (nx2 - nx1); + nx = x; + ny = (ny1 * c1 + ny2 * c2); + nz = (nz1 * c1 + nz2 * c2); +} + +GPUdi() void TPCFastTransformPOD::InverseTransformXYZtoNominalXYZ_new(int32_t sector, int32_t row, float x, float y, float z, float& nx, float& ny, float& nz) const +{ + /// Inverse transformation: Transformed X, Y and Z -> X, Y and Z, transformed w/o space charge correction + int32_t row2 = row + 1; + if (row2 >= getGeometry().getNumberOfRows()) { + row2 = row - 1; + } + float nx1, ny1, nz1; // nominal coordinates for row + float nx2, ny2, nz2; // nominal coordinates for row2 + nx1 = getGeometry().getRowInfo(row).x; + nx2 = getGeometry().getRowInfo(row2).x; + InverseTransformYZtoNominalYZ_new(sector, row, y, z, ny1, nz1); + InverseTransformYZtoNominalYZ_new(sector, row2, y, z, ny2, nz2); + float c1 = (nx2 - nx) / (nx2 - nx1); + float c2 = (nx - nx1) / (nx2 - nx1); + nx = x; + ny = (ny1 * c1 + ny2 * c2); + nz = (nz1 * c1 + nz2 * c2); +} + +} // namespace gpu +} // namespace o2 + +#endif diff --git a/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h b/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h index f1872549a46aa..0247bbbfbb65b 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h +++ b/GPU/TPCFastTransformation/TPCFastTransformationLinkDef_O2.h @@ -93,5 +93,6 @@ #pragma link C++ struct o2::gpu::MultivariatePolynomialContainer + ; #pragma link C++ struct o2::gpu::NDPiecewisePolynomialContainer + ; #pragma link C++ struct o2::gpu::TPCSlowSpaceChargeCorrection + ; +#pragma link C++ class o2::gpu::TPCFastTransformPOD + ; #endif From 19d6617a61ce56f268cf373b608f6ff71eef43e2 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Fri, 3 Apr 2026 10:29:35 +0200 Subject: [PATCH 524/701] GPU FlatObject: Make some more functions accessible on GPU --- GPU/TPCFastTransformation/TPCFastTransformGeo.h | 8 ++++---- GPU/Utils/FlatObject.h | 6 +++--- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/GPU/TPCFastTransformation/TPCFastTransformGeo.h b/GPU/TPCFastTransformation/TPCFastTransformGeo.h index 31b81e02c2d4c..6dd0e716c833b 100644 --- a/GPU/TPCFastTransformation/TPCFastTransformGeo.h +++ b/GPU/TPCFastTransformation/TPCFastTransformGeo.h @@ -86,7 +86,7 @@ class TPCFastTransformGeo /// _____________ FlatObject functionality, see FlatObject class for description ____________ /// Gives minimal alignment in bytes required for an object of the class - static constexpr size_t getClassAlignmentBytes() { return 8; } + inline static constexpr size_t getClassAlignmentBytes() { return 8; } /// _______________ Construction interface ________________________ @@ -110,16 +110,16 @@ class TPCFastTransformGeo /// _______________ Getters _________________________________ /// Gives number of TPC sectors - GPUdi() static constexpr int32_t getNumberOfSectors() { return NumberOfSectors; } + inline static constexpr int32_t getNumberOfSectors() { return NumberOfSectors; } /// Gives number of TPC sectors on the A side - GPUdi() static constexpr int32_t getNumberOfSectorsA() { return NumberOfSectorsA; } + inline static constexpr int32_t getNumberOfSectorsA() { return NumberOfSectorsA; } /// Gives number of TPC rows GPUdi() int32_t getNumberOfRows() const { return mNumberOfRows; } /// Gives number of TPC rows - GPUdi() static constexpr int getMaxNumberOfRows() { return MaxNumberOfRows; } + inline static constexpr int getMaxNumberOfRows() { return MaxNumberOfRows; } /// Gives sector info GPUd() const SectorInfo& getSectorInfo(int32_t sector) const; diff --git a/GPU/Utils/FlatObject.h b/GPU/Utils/FlatObject.h index 8e13a8dedb868..46fdec7703823 100644 --- a/GPU/Utils/FlatObject.h +++ b/GPU/Utils/FlatObject.h @@ -253,10 +253,10 @@ class FlatObject void destroy(); /// Gives size of the flat buffer - size_t getFlatBufferSize() const { return mFlatBufferSize; } + GPUdi() size_t getFlatBufferSize() const { return mFlatBufferSize; } /// Gives pointer to the flat buffer - const char* getFlatBufferPtr() const { return mFlatBufferPtr; } + GPUdi() const char* getFlatBufferPtr() const { return mFlatBufferPtr; } /// Tells if the object is constructed bool isConstructed() const { return (mConstructionMask & (uint32_t)ConstructionState::Constructed); } @@ -274,7 +274,7 @@ class FlatObject public: /// Increases given size to achieve required alignment - static size_t alignSize(size_t sizeBytes, size_t alignmentBytes) + static constexpr size_t alignSize(size_t sizeBytes, size_t alignmentBytes) { auto res = sizeBytes % alignmentBytes; return res ? sizeBytes + (alignmentBytes - res) : sizeBytes; From a5b24a599e45470b60b3c94b085e752c3060459d Mon Sep 17 00:00:00 2001 From: Matthias Kleiner Date: Mon, 27 Oct 2025 11:11:59 +0100 Subject: [PATCH 525/701] TPC: centralize correction map building in TPCScalerSpec `TPCScalerSpec` now owns the full correction map pipeline: loads maps from CCDB, applies luminosity scaling and M-shape corrections, applies VDrift into the final map, and publishes a single merged `TPCFastTransformPOD` (`TPC/TPCCORRMAP`) plus instantaneous CTP lumi (`CTP/LUMICTP`) each TF. Downstream consumers (ITS-TPC matcher, TRD, TOF, secondary vertexing, GPU reco, etc.) are simplified: lumi scale options removed from constructors, `corrMapsLoader.updateVDrift()` / `corrMapsLoader.accountCCDBInputs()` / `corrMapsLoader.init()` calls dropped, and `requestCCDBInputs()` replaced by `requestInputs()` subscribing only to the two new outputs. `TPCScalerSpec` is now always added unconditionally. `CorrectionMapsHelper` is reduced to a thin wrapper around `TPCFastTransformPOD*`; full lumi-scaling state moves to the new `CorrectionMapsHelperFull` / `CorrectionMapsLoaderFull` used only inside `TPCScalerSpec`. `TPCFastTransformPOD` gains a flat buffer offset table and `interpolateAtUZeroCopy()` for correct spline evaluation on zero-copy shared memory paths. --- .../AlignmentWorkflow/BarrelAlignmentSpec.h | 2 +- .../Workflow/src/BarrelAlignmentSpec.cxx | 26 +- .../src/barrel-alignment-workflow.cxx | 7 +- Detectors/GlobalTracking/src/MatchTOF.cxx | 1 + Detectors/GlobalTracking/src/MatchTPCITS.cxx | 1 + .../CosmicsMatchingSpec.h | 2 +- .../SecondaryVertexingSpec.h | 2 +- .../GlobalTrackingWorkflow/TOFMatcherSpec.h | 2 +- .../TPCITSMatchingSpec.h | 2 +- .../src/CosmicsMatchingSpec.cxx | 23 +- .../src/SecondaryVertexingSpec.cxx | 26 +- .../src/TOFMatcherSpec.cxx | 25 +- .../src/TPCITSMatchingSpec.cxx | 25 +- .../src/cosmics-match-workflow.cxx | 6 +- .../src/secondary-vertexing-workflow.cxx | 6 +- .../src/tof-matcher-workflow.cxx | 6 +- .../src/tpcits-match-workflow.cxx | 6 +- .../GlobalTrackingStudy/TPCTrackStudy.h | 2 +- .../GlobalTrackingStudy/TrackMCStudy.h | 2 +- .../GlobalTrackingStudy/TrackingStudy.h | 2 +- .../study/src/TPCTrackStudy.cxx | 30 +- .../study/src/TrackMCStudy.cxx | 30 +- .../study/src/TrackingStudy.cxx | 30 +- .../study/src/tpc-track-study-workflow.cxx | 6 +- .../study/src/trackMCStudy-workflow.cxx | 6 +- .../study/src/tracking-study-workflow.cxx | 6 +- Detectors/TPC/calibration/CMakeLists.txt | 2 + .../TPCCalibration/CorrectionMapsLoader.h | 32 +- .../TPCCalibration/CorrectionMapsLoaderFull.h | 64 +++ .../TPC/calibration/src/CalculatedEdx.cxx | 5 +- .../calibration/src/CorrectionMapsLoader.cxx | 369 ++---------------- .../src/CorrectionMapsLoaderFull.cxx | 253 ++++++++++++ Detectors/TPC/calibration/src/TrackDump.cxx | 9 +- .../TPCFastTransformHelperO2.h | 16 +- .../src/TPCFastTransformHelperO2.cxx | 10 +- .../reconstruction/test/testGPUCATracking.cxx | 11 +- .../TPCWorkflow/TPCCalibPadGainTracksSpec.h | 37 +- .../include/TPCWorkflow/TPCRefitter.h | 2 +- .../include/TPCWorkflow/TPCScalerSpec.h | 3 +- Detectors/TPC/workflow/src/RecoWorkflow.cxx | 13 +- Detectors/TPC/workflow/src/TPCRefitter.cxx | 32 +- Detectors/TPC/workflow/src/TPCScalerSpec.cxx | 102 ++++- .../workflow/src/tpc-calib-gainmap-tracks.cxx | 6 +- .../TPC/workflow/src/tpc-reco-workflow.cxx | 3 +- .../workflow/src/tpc-refitter-workflow.cxx | 10 +- Detectors/TPC/workflow/src/tpc-scaler.cxx | 6 +- .../TRDWorkflow/TRDGlobalTrackingSpec.h | 11 +- .../workflow/src/TRDGlobalTrackingSpec.cxx | 14 +- .../workflow/src/trd-tracking-workflow.cxx | 6 +- .../Base/GPUReconstructionConvert.cxx | 4 +- .../Base/GPUReconstructionConvert.h | 4 +- .../Base/GPUReconstructionTimeframe.cxx | 2 +- GPU/GPUTracking/DataTypes/GPUDataTypesIO.h | 6 +- GPU/GPUTracking/Global/GPUChainTracking.cxx | 42 +- GPU/GPUTracking/Global/GPUChainTracking.h | 10 +- GPU/GPUTracking/Global/GPUChainTrackingIO.cxx | 26 +- GPU/GPUTracking/Merger/GPUTPCGMMerger.cxx | 2 +- GPU/GPUTracking/Merger/GPUTPCGMO2Output.cxx | 2 +- GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx | 2 +- .../TPCConvert/GPUTPCConvertImpl.h | 4 +- GPU/GPUTracking/qa/GPUQA.cxx | 2 +- .../CorrectionMapsHelper.cxx | 120 ++---- .../CorrectionMapsHelper.h | 192 +++------ .../CorrectionMapsHelperFull.cxx | 60 +++ .../CorrectionMapsHelperFull.h | 161 ++++++++ GPU/TPCFastTransformation/Spline1DSpec.h | 37 ++ GPU/TPCFastTransformation/Spline2DSpec.h | 96 +++++ .../TPCFastTransformPOD.cxx | 53 ++- .../TPCFastTransformPOD.h | 111 +++++- .../include/GPUWorkflow/GPUWorkflowSpec.h | 12 +- .../include/GPUWorkflow/O2GPUDPLDisplay.h | 3 - GPU/Workflow/src/GPUWorkflowSpec.cxx | 8 +- GPU/Workflow/src/GPUWorkflowTPC.cxx | 77 ++-- GPU/Workflow/src/O2GPUDPLDisplay.cxx | 13 +- GPU/Workflow/src/gpu-reco-workflow.cxx | 8 - prodtests/full-system-test/dpl-workflow.sh | 30 +- 76 files changed, 1260 insertions(+), 1125 deletions(-) create mode 100644 Detectors/TPC/calibration/include/TPCCalibration/CorrectionMapsLoaderFull.h create mode 100644 Detectors/TPC/calibration/src/CorrectionMapsLoaderFull.cxx create mode 100644 GPU/TPCFastTransformation/CorrectionMapsHelperFull.cxx create mode 100644 GPU/TPCFastTransformation/CorrectionMapsHelperFull.h diff --git a/Detectors/Align/Workflow/include/AlignmentWorkflow/BarrelAlignmentSpec.h b/Detectors/Align/Workflow/include/AlignmentWorkflow/BarrelAlignmentSpec.h index 197ace2bd9d20..fd5697a20bc2b 100644 --- a/Detectors/Align/Workflow/include/AlignmentWorkflow/BarrelAlignmentSpec.h +++ b/Detectors/Align/Workflow/include/AlignmentWorkflow/BarrelAlignmentSpec.h @@ -31,7 +31,7 @@ namespace align /// create a processor spec framework::DataProcessorSpec getBarrelAlignmentSpec(o2::dataformats::GlobalTrackID::mask_t srcMP, o2::dataformats::GlobalTrackID::mask_t src, - o2::detectors::DetID::mask_t dets, o2::detectors::DetID::mask_t skipDetClusters, bool enableCosmic, int postproc, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts); + o2::detectors::DetID::mask_t dets, o2::detectors::DetID::mask_t skipDetClusters, bool enableCosmic, int postproc, bool useMC); } // namespace align } // namespace o2 diff --git a/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx b/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx index 2e63a1a65483c..dc952b26e52f0 100644 --- a/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx +++ b/Detectors/Align/Workflow/src/BarrelAlignmentSpec.cxx @@ -86,14 +86,9 @@ class BarrelAlignmentSpec : public Task CheckConstaints = 0x1 << 1, GenPedeFiles = 0x1 << 2, LabelPedeResults = 0x1 << 3 }; - BarrelAlignmentSpec(GTrackID::mask_t srcMP, std::shared_ptr dr, std::shared_ptr ggrec, const o2::tpc::CorrectionMapsLoaderGloOpts& tpcOpt, + BarrelAlignmentSpec(GTrackID::mask_t srcMP, std::shared_ptr dr, std::shared_ptr ggrec, DetID::mask_t detmask, bool cosmic, int postprocess, bool useMC, bool loadTPCCalib) - : mDataRequest(dr), mGRPGeomRequest(ggrec), mMPsrc{srcMP}, mDetMask{detmask}, mCosmic(cosmic), mPostProcessing(postprocess), mUseMC(useMC), mLoadTPCCalib(loadTPCCalib) - { - mTPCCorrMapsLoader.setLumiScaleType(tpcOpt.lumiType); - mTPCCorrMapsLoader.setLumiScaleMode(tpcOpt.lumiMode); - mTPCCorrMapsLoader.setCheckCTPIDCConsistency(tpcOpt.checkCTPIDCconsistency); - } + : mDataRequest(dr), mGRPGeomRequest(ggrec), mMPsrc{srcMP}, mDetMask{detmask}, mCosmic(cosmic), mPostProcessing(postprocess), mUseMC(useMC), mLoadTPCCalib(loadTPCCalib) {} ~BarrelAlignmentSpec() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -184,9 +179,6 @@ void BarrelAlignmentSpec::init(InitContext& ic) } mIgnoreCCDBAlignment = ic.options().get("ignore-ccdb-alignment"); if (!mPostProcessing) { - if (mLoadTPCCalib) { - mTPCCorrMapsLoader.init(ic); - } if (GTrackID::includesDet(DetID::TRD, mMPsrc)) { mTRDTransformer.reset(new o2::trd::TrackletTransformer); if (ic.options().get("apply-xor")) { @@ -278,7 +270,7 @@ void BarrelAlignmentSpec::updateTimeDependentParams(ProcessingContext& pc) mTPCCorrMapsLoader.acknowledgeUpdate(); updateMaps = true; } - mController->setTPCCorrMaps(&mTPCCorrMapsLoader); + // mController->setTPCCorrMaps(&mTPCCorrMapsLoader); if (mTPCVDriftHelper.isUpdated()) { LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, @@ -288,9 +280,6 @@ void BarrelAlignmentSpec::updateTimeDependentParams(ProcessingContext& pc) mTPCVDriftHelper.acknowledgeUpdate(); updateMaps = true; } - if (updateMaps) { - mTPCCorrMapsLoader.updateVDrift(mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().getTimeOffset()); - } } } @@ -314,9 +303,6 @@ void BarrelAlignmentSpec::finaliseCCDB(o2::framework::ConcreteDataMatcher& match return; } - if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { - return; - } } void BarrelAlignmentSpec::run(ProcessingContext& pc) @@ -374,7 +360,7 @@ void BarrelAlignmentSpec::endOfStream(EndOfStreamContext& ec) mDBGOut.reset(); } -DataProcessorSpec getBarrelAlignmentSpec(GTrackID::mask_t srcMP, GTrackID::mask_t src, DetID::mask_t dets, DetID::mask_t skipDetClusters, bool enableCosmic, int postprocess, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts) +DataProcessorSpec getBarrelAlignmentSpec(GTrackID::mask_t srcMP, GTrackID::mask_t src, DetID::mask_t dets, DetID::mask_t skipDetClusters, bool enableCosmic, int postprocess, bool useMC) { std::vector outputs; auto dataRequest = std::make_shared(); @@ -399,7 +385,7 @@ DataProcessorSpec getBarrelAlignmentSpec(GTrackID::mask_t srcMP, GTrackID::mask_ } if (src[DetID::TPC] && !skipDetClusters[DetID::TPC]) { o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); + o2::tpc::CorrectionMapsLoader::requestInputs(dataRequest->inputs, opts); loadTPCCalib = true; } } @@ -417,7 +403,7 @@ DataProcessorSpec getBarrelAlignmentSpec(GTrackID::mask_t srcMP, GTrackID::mask_ "barrel-alignment", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(srcMP, dataRequest, ccdbRequest, sclOpts, dets, enableCosmic, postprocess, useMC, loadTPCCalib)}, + AlgorithmSpec{adaptFromTask(srcMP, dataRequest, ccdbRequest, dets, enableCosmic, postprocess, useMC, loadTPCCalib)}, opts}; } diff --git a/Detectors/Align/Workflow/src/barrel-alignment-workflow.cxx b/Detectors/Align/Workflow/src/barrel-alignment-workflow.cxx index cdd0620affec9..03fc414113114 100644 --- a/Detectors/Align/Workflow/src/barrel-alignment-workflow.cxx +++ b/Detectors/Align/Workflow/src/barrel-alignment-workflow.cxx @@ -20,7 +20,6 @@ #include "TPCReaderWorkflow/ClusterReaderSpec.h" #include "TPCWorkflow/ClusterSharingMapSpec.h" #include "TPCWorkflow/TPCScalerSpec.h" -#include "TPCCalibration/CorrectionMapsLoader.h" #include "TOFWorkflowIO/ClusterReaderSpec.h" #include "TOFWorkflowIO/TOFMatchedReaderSpec.h" #include "TOFWorkflowIO/ClusterReaderSpec.h" @@ -150,11 +149,11 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) o2::conf::ConfigurableParam::writeINI("o2_barrel_alignment_configuration.ini"); } - if (sclOpt.needTPCScalersWorkflow() && !configcontext.options().get("disable-root-input")) { - specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == 2, sclOpt.enableMShapeCorrection)); + if (!configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpt.enableMShapeCorrection, sclOpt)); } - specs.emplace_back(o2::align::getBarrelAlignmentSpec(srcMP, src, dets, skipDetClusters, enableCosmic, postprocess, useMC, sclOpt)); + specs.emplace_back(o2::align::getBarrelAlignmentSpec(srcMP, src, dets, skipDetClusters, enableCosmic, postprocess, useMC)); // RS FIXME: check which clusters are really needed if (!postprocess) { GID::mask_t dummy; diff --git a/Detectors/GlobalTracking/src/MatchTOF.cxx b/Detectors/GlobalTracking/src/MatchTOF.cxx index 6a3486dd12044..ad050309fe030 100644 --- a/Detectors/GlobalTracking/src/MatchTOF.cxx +++ b/Detectors/GlobalTracking/src/MatchTOF.cxx @@ -2087,6 +2087,7 @@ void MatchTOF::updateTimeDependentParams() const auto& trackTune = TrackTuneParams::Instance(); float scale = mTPCCorrMapsHelper->getInstLumiCTP(); if (scale < 0.f) { + LOGP(warning, "Negative scale factor for TPC covariance correction, setting it to zero"); scale = 0.f; } mCovDiagInner = trackTune.getCovInnerTotal(scale); diff --git a/Detectors/GlobalTracking/src/MatchTPCITS.cxx b/Detectors/GlobalTracking/src/MatchTPCITS.cxx index 73216c8ce1eac..9b69397e1f9f6 100644 --- a/Detectors/GlobalTracking/src/MatchTPCITS.cxx +++ b/Detectors/GlobalTracking/src/MatchTPCITS.cxx @@ -288,6 +288,7 @@ void MatchTPCITS::updateTimeDependentParams() const auto& trackTune = TrackTuneParams::Instance(); float scale = mTPCCorrMapsHelper->getInstLumiCTP(); if (scale < 0.f) { + LOGP(warning, "Negative scale factor for TPC covariance correction, setting it to zero"); scale = 0.f; } mCovDiagInner = trackTune.getCovInnerTotal(scale); diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/CosmicsMatchingSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/CosmicsMatchingSpec.h index e0e74c3058086..0633bb6a64a22 100644 --- a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/CosmicsMatchingSpec.h +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/CosmicsMatchingSpec.h @@ -29,7 +29,7 @@ namespace globaltracking { /// create a processor spec -framework::DataProcessorSpec getCosmicsMatchingSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts); +framework::DataProcessorSpec getCosmicsMatchingSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useMC); } // namespace globaltracking } // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/SecondaryVertexingSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/SecondaryVertexingSpec.h index b8071ae83d347..9de5f158a0608 100644 --- a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/SecondaryVertexingSpec.h +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/SecondaryVertexingSpec.h @@ -29,7 +29,7 @@ namespace vertexing { /// create a processor spec -o2::framework::DataProcessorSpec getSecondaryVertexingSpec(o2::dataformats::GlobalTrackID::mask_t src, bool enableCasc, bool enable3body, bool enableStrangenesTracking, bool enableCCDBParams, bool useMC, bool useGeom, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts); +o2::framework::DataProcessorSpec getSecondaryVertexingSpec(o2::dataformats::GlobalTrackID::mask_t src, bool enableCasc, bool enable3body, bool enableStrangenesTracking, bool enableCCDBParams, bool useMC, bool useGeom); } // namespace vertexing } // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TOFMatcherSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TOFMatcherSpec.h index 79a4ee0ce0360..afc70f688a0b5 100644 --- a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TOFMatcherSpec.h +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TOFMatcherSpec.h @@ -29,7 +29,7 @@ namespace globaltracking { /// create a processor spec -framework::DataProcessorSpec getTOFMatcherSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useMC, bool useFIT, bool tpcRefit, bool strict, float extratolerancetrd, bool pushMatchable, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, int nlanes = 1); +framework::DataProcessorSpec getTOFMatcherSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useMC, bool useFIT, bool tpcRefit, bool strict, float extratolerancetrd, bool pushMatchable, int nlanes = 1); } // namespace globaltracking } // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TPCITSMatchingSpec.h b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TPCITSMatchingSpec.h index 4aaed7d64eec5..4f4f13cde72b1 100644 --- a/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TPCITSMatchingSpec.h +++ b/Detectors/GlobalTrackingWorkflow/include/GlobalTrackingWorkflow/TPCITSMatchingSpec.h @@ -27,7 +27,7 @@ struct CorrectionMapsLoaderGloOpts; namespace globaltracking { /// create a processor spec -framework::DataProcessorSpec getTPCITSMatchingSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useFT0, bool calib, bool skipTPCOnly, bool useGeom, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts); +framework::DataProcessorSpec getTPCITSMatchingSpec(o2::dataformats::GlobalTrackID::mask_t src, bool useFT0, bool calib, bool skipTPCOnly, bool useGeom, bool useMC); } // namespace globaltracking } // namespace o2 diff --git a/Detectors/GlobalTrackingWorkflow/src/CosmicsMatchingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/CosmicsMatchingSpec.cxx index 5bcdded0e1223..cf148d47b3d10 100644 --- a/Detectors/GlobalTrackingWorkflow/src/CosmicsMatchingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/CosmicsMatchingSpec.cxx @@ -62,12 +62,7 @@ namespace globaltracking class CosmicsMatchingSpec : public Task { public: - CosmicsMatchingSpec(std::shared_ptr dr, std::shared_ptr gr, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, bool useMC) : mDataRequest(dr), mGGCCDBRequest(gr), mUseMC(useMC) - { - mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); - mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); - mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); - } + CosmicsMatchingSpec(std::shared_ptr dr, std::shared_ptr gr, bool useMC) : mDataRequest(dr), mGGCCDBRequest(gr), mUseMC(useMC) {} ~CosmicsMatchingSpec() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -92,7 +87,6 @@ void CosmicsMatchingSpec::init(InitContext& ic) o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); mMatching.setDebugFlag(ic.options().get("debug-tree-flags")); mMatching.setUseMC(mUseMC); - mTPCCorrMapsLoader.init(ic); // } @@ -132,10 +126,8 @@ void CosmicsMatchingSpec::updateTimeDependentParams(ProcessingContext& pc) } mMatching.init(); } - bool updateMaps = false; if (mTPCCorrMapsLoader.isUpdated()) { mTPCCorrMapsLoader.acknowledgeUpdate(); - updateMaps = true; } mMatching.setTPCCorrMaps(&mTPCCorrMapsLoader); if (mTPCVDriftHelper.isUpdated()) { @@ -145,10 +137,6 @@ void CosmicsMatchingSpec::updateTimeDependentParams(ProcessingContext& pc) mTPCVDriftHelper.getSourceName()); mMatching.setTPCVDrift(mTPCVDriftHelper.getVDriftObject()); mTPCVDriftHelper.acknowledgeUpdate(); - updateMaps = true; - } - if (updateMaps) { - mTPCCorrMapsLoader.updateVDrift(mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().getTimeOffset()); } } @@ -160,9 +148,6 @@ void CosmicsMatchingSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { return; } - if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { - return; - } if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { LOG(info) << "cluster dictionary updated"; mMatching.setITSDict((const o2::itsmft::TopologyDictionary*)obj); @@ -177,7 +162,7 @@ void CosmicsMatchingSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getCosmicsMatchingSpec(GTrackID::mask_t src, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts) +DataProcessorSpec getCosmicsMatchingSpec(GTrackID::mask_t src, bool useMC) { std::vector outputs; Options opts{ @@ -203,13 +188,13 @@ DataProcessorSpec getCosmicsMatchingSpec(GTrackID::mask_t src, bool useMC, const dataRequest->inputs, true); o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); + o2::tpc::CorrectionMapsLoader::requestInputs(dataRequest->inputs, opts); return DataProcessorSpec{ "cosmics-matcher", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, sclOpts, useMC)}, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, useMC)}, opts}; } diff --git a/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx index 6dfd1cb770d7f..fb9fc328175ea 100644 --- a/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/SecondaryVertexingSpec.cxx @@ -58,14 +58,7 @@ namespace o2d = o2::dataformats; class SecondaryVertexingSpec : public Task { public: - SecondaryVertexingSpec(std::shared_ptr dr, std::shared_ptr gr, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, GTrackID::mask_t src, bool enabCasc, bool enable3body, bool enableStrangenessTracking, bool enableCCDBParams, bool useMC) : mDataRequest(dr), mGGCCDBRequest(gr), mSrc(src), mEnableCascades(enabCasc), mEnable3BodyVertices(enable3body), mEnableStrangenessTracking(enableStrangenessTracking), mEnableCCDBParams(enableCCDBParams), mUseMC(useMC) - { - if (mSrc[GTrackID::TPC]) { - mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); - mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); - mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); - } - } + SecondaryVertexingSpec(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool enabCasc, bool enable3body, bool enableStrangenessTracking, bool enableCCDBParams, bool useMC) : mDataRequest(dr), mGGCCDBRequest(gr), mSrc(src), mEnableCascades(enabCasc), mEnable3BodyVertices(enable3body), mEnableStrangenessTracking(enableStrangenessTracking), mEnableCCDBParams(enableCCDBParams), mUseMC(useMC) {} ~SecondaryVertexingSpec() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -107,9 +100,6 @@ void SecondaryVertexingSpec::init(InitContext& ic) mStrTracker.setMCTruthOn(mUseMC); mVertexer.setStrangenessTracker(&mStrTracker); } - if (mSrc[GTrackID::TPC]) { - mTPCCorrMapsLoader.init(ic); - } } void SecondaryVertexingSpec::run(ProcessingContext& pc) @@ -157,9 +147,6 @@ void SecondaryVertexingSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* ob if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { return; } - if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { - return; - } if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { LOG(info) << "cluster dictionary updated"; mStrTracker.setClusterDictionaryITS((const o2::itsmft::TopologyDictionary*)obj); @@ -229,6 +216,7 @@ void SecondaryVertexingSpec::updateTimeDependentParams(ProcessingContext& pc) updateMaps = true; } mVertexer.setTPCCorrMaps(&mTPCCorrMapsLoader); + if (mTPCVDriftHelper.isUpdated()) { LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, @@ -236,10 +224,6 @@ void SecondaryVertexingSpec::updateTimeDependentParams(ProcessingContext& pc) mTPCVDriftHelper.getSourceName()); mVertexer.setTPCVDrift(mTPCVDriftHelper.getVDriftObject()); mTPCVDriftHelper.acknowledgeUpdate(); - updateMaps = true; - } - if (updateMaps) { - mTPCCorrMapsLoader.updateVDrift(mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().getTimeOffset()); } } if (mEnableStrangenessTracking) { @@ -253,7 +237,7 @@ void SecondaryVertexingSpec::updateTimeDependentParams(ProcessingContext& pc) } DataProcessorSpec getSecondaryVertexingSpec(GTrackID::mask_t src, bool enableCasc, bool enable3body, bool enableStrangenesTracking, bool enableCCDBParams, - bool useMC, bool useGeom, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts) + bool useMC, bool useGeom) { std::vector outputs; Options opts{ @@ -297,7 +281,7 @@ DataProcessorSpec getSecondaryVertexingSpec(GTrackID::mask_t src, bool enableCas } if (src[GTrackID::TPC]) { o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); + o2::tpc::CorrectionMapsLoader::requestInputs(dataRequest->inputs, opts); } outputs.emplace_back("GLO", "V0S_IDX", 0, Lifetime::Timeframe); // found V0s indices outputs.emplace_back("GLO", "V0S", 0, Lifetime::Timeframe); // found V0s @@ -324,7 +308,7 @@ DataProcessorSpec getSecondaryVertexingSpec(GTrackID::mask_t src, bool enableCas "secondary-vertexing", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, sclOpts, src, enableCasc, enable3body, enableStrangenesTracking, enableCCDBParams, useMC)}, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, src, enableCasc, enable3body, enableStrangenesTracking, enableCCDBParams, useMC)}, opts}; } diff --git a/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx index 8081c48e390d3..ede3026647b1e 100644 --- a/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/TOFMatcherSpec.cxx @@ -59,12 +59,7 @@ namespace globaltracking class TOFMatcherSpec : public Task { public: - TOFMatcherSpec(std::shared_ptr dr, std::shared_ptr gr, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, bool useMC, bool useFIT, bool tpcRefit, bool strict, bool pushMatchable, int lanes = 1) : mDataRequest(dr), mGGCCDBRequest(gr), mUseMC(useMC), mUseFIT(useFIT), mDoTPCRefit(tpcRefit), mStrict(strict), mPushMatchable(pushMatchable), mNlanes(lanes) - { - mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); - mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); - mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); - } + TOFMatcherSpec(std::shared_ptr dr, std::shared_ptr gr, bool useMC, bool useFIT, bool tpcRefit, bool strict, bool pushMatchable, int lanes = 1) : mDataRequest(dr), mGGCCDBRequest(gr), mUseMC(useMC), mUseFIT(useFIT), mDoTPCRefit(tpcRefit), mStrict(strict), mPushMatchable(pushMatchable), mNlanes(lanes) {} ~TOFMatcherSpec() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -96,7 +91,6 @@ void TOFMatcherSpec::init(InitContext& ic) if (mStrict) { mMatcher.setHighPurity(); } - mTPCCorrMapsLoader.init(ic); mMatcher.storeMatchable(mPushMatchable); mMatcher.setExtraTimeToleranceTRD(mExtraTolTRD); mMatcher.setNlanes(mNlanes); @@ -117,23 +111,17 @@ void TOFMatcherSpec::updateTimeDependentParams(ProcessingContext& pc) // put here init-once stuff } // we may have other params which need to be queried regularly - bool updateMaps = false; if (mTPCCorrMapsLoader.isUpdated()) { mTPCCorrMapsLoader.acknowledgeUpdate(); - updateMaps = true; } mMatcher.setTPCCorrMaps(&mTPCCorrMapsLoader); - if (mTPCVDriftHelper.isUpdated()) { + 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()); mMatcher.setTPCVDrift(mTPCVDriftHelper.getVDriftObject()); mTPCVDriftHelper.acknowledgeUpdate(); - updateMaps = true; - } - if (updateMaps) { - mTPCCorrMapsLoader.updateVDrift(mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().getTimeOffset()); } } @@ -145,9 +133,6 @@ void TOFMatcherSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { return; } - if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { - return; - } } void TOFMatcherSpec::run(ProcessingContext& pc) @@ -247,7 +232,7 @@ void TOFMatcherSpec::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getTOFMatcherSpec(GID::mask_t src, bool useMC, bool useFIT, bool tpcRefit, bool strict, float extratolerancetrd, bool pushMatchable, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, int nlanes) +DataProcessorSpec getTOFMatcherSpec(GID::mask_t src, bool useMC, bool useFIT, bool tpcRefit, bool strict, float extratolerancetrd, bool pushMatchable, int nlanes) { uint32_t ss = o2::globaltracking::getSubSpec(strict ? o2::globaltracking::MatchingType::Strict : o2::globaltracking::MatchingType::Standard); Options opts; @@ -273,7 +258,7 @@ DataProcessorSpec getTOFMatcherSpec(GID::mask_t src, bool useMC, bool useFIT, bo dataRequest->inputs, true); o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); + o2::tpc::CorrectionMapsLoader::requestInputs(dataRequest->inputs, opts); std::vector outputs; if (GID::includesSource(GID::TPC, src)) { outputs.emplace_back(o2::header::gDataOriginTOF, "MTC_TPC", ss, Lifetime::Timeframe); @@ -327,7 +312,7 @@ DataProcessorSpec getTOFMatcherSpec(GID::mask_t src, bool useMC, bool useFIT, bo "tof-matcher", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, sclOpts, useMC, useFIT, tpcRefit, strict, pushMatchable, nlanes)}, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, useMC, useFIT, tpcRefit, strict, pushMatchable, nlanes)}, opts}; } diff --git a/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx index cb3384b0631c2..38b6d6c1efb6e 100644 --- a/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/TPCITSMatchingSpec.cxx @@ -69,14 +69,9 @@ namespace globaltracking class TPCITSMatchingDPL : public Task { public: - TPCITSMatchingDPL(std::shared_ptr dr, std::shared_ptr gr, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, + TPCITSMatchingDPL(std::shared_ptr dr, std::shared_ptr gr, bool useFT0, bool calib, bool skipTPCOnly, bool useMC) - : mDataRequest(dr), mGGCCDBRequest(gr), mUseFT0(useFT0), mCalibMode(calib), mSkipTPCOnly(skipTPCOnly), mUseMC(useMC) - { - mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); - mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); - mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); - } + : mDataRequest(dr), mGGCCDBRequest(gr), mUseFT0(useFT0), mCalibMode(calib), mSkipTPCOnly(skipTPCOnly), mUseMC(useMC) {} ~TPCITSMatchingDPL() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -108,7 +103,6 @@ void TPCITSMatchingDPL::init(InitContext& ic) mMatching.setNThreads(std::max(1, ic.options().get("nthreads"))); mMatching.setUseBCFilling(!ic.options().get("ignore-bc-check")); mMatching.setDebugFlag(ic.options().get("debug-tree-flags")); - mTPCCorrMapsLoader.init(ic); } void TPCITSMatchingDPL::run(ProcessingContext& pc) @@ -157,9 +151,6 @@ void TPCITSMatchingDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { return; } - if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { - return; - } if (matcher == ConcreteDataMatcher("GLO", "ITSTPCPARAM", 0)) { LOG(info) << "ITS-TPC Matching params updated from ccdb"; return; @@ -236,20 +227,16 @@ void TPCITSMatchingDPL::updateTimeDependentParams(ProcessingContext& pc) mMatching.setTPCCorrMaps(&mTPCCorrMapsLoader); if (mTPCVDriftHelper.isUpdated()) { - LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", + LOGP(info, "Updating TPC VDrift: corrFact {} wrt refVDrift {} and DriftTimeOffset correction {} wrt {} from source {}", mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().timeOffsetCorr, mTPCVDriftHelper.getVDriftObject().refTimeOffset, mTPCVDriftHelper.getSourceName()); mMatching.setTPCVDrift(mTPCVDriftHelper.getVDriftObject()); mTPCVDriftHelper.acknowledgeUpdate(); - updateMaps = true; - } - if (updateMaps) { - mTPCCorrMapsLoader.updateVDrift(mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().getTimeOffset()); } } -DataProcessorSpec getTPCITSMatchingSpec(GTrackID::mask_t src, bool useFT0, bool calib, bool skipTPCOnly, bool useGeom, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts) +DataProcessorSpec getTPCITSMatchingSpec(GTrackID::mask_t src, bool useFT0, bool calib, bool skipTPCOnly, bool useGeom, bool useMC) { std::vector outputs; auto dataRequest = std::make_shared(); @@ -305,13 +292,13 @@ DataProcessorSpec getTPCITSMatchingSpec(GTrackID::mask_t src, bool useFT0, bool {"debug-tree-flags", VariantType::Int, 0, {"DebugFlagTypes bit-pattern for debug tree"}}}; o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); + o2::tpc::CorrectionMapsLoader::requestInputs(dataRequest->inputs, opts); return DataProcessorSpec{ "itstpc-track-matcher", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, sclOpts, useFT0, calib, skipTPCOnly, useMC)}, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, useFT0, calib, skipTPCOnly, useMC)}, opts}; } diff --git a/Detectors/GlobalTrackingWorkflow/src/cosmics-match-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/cosmics-match-workflow.cxx index 3f7ecfbbea809..db0e4253a7a51 100644 --- a/Detectors/GlobalTrackingWorkflow/src/cosmics-match-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/cosmics-match-workflow.cxx @@ -104,10 +104,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) } GID::mask_t srcCl = src; GID::mask_t dummy; - if (sclOpt.needTPCScalersWorkflow() && !configcontext.options().get("disable-root-input")) { - specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == 2, sclOpt.enableMShapeCorrection)); + if (!configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpt.enableMShapeCorrection, sclOpt)); } - specs.emplace_back(o2::globaltracking::getCosmicsMatchingSpec(src, useMC, sclOpt)); + specs.emplace_back(o2::globaltracking::getCosmicsMatchingSpec(src, useMC)); o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, src, src, src, useMC, dummy); // clusters MC is not needed diff --git a/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx index 5bc80f527d4d0..6d10fa786b0c9 100644 --- a/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/secondary-vertexing-workflow.cxx @@ -101,10 +101,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) } } WorkflowSpec specs; - if (sclOpt.needTPCScalersWorkflow() && !configcontext.options().get("disable-root-input")) { - specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == 2, sclOpt.enableMShapeCorrection)); + if (!configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpt.enableMShapeCorrection, sclOpt)); } - specs.emplace_back(o2::vertexing::getSecondaryVertexingSpec(src, enableCasc, enable3body, enableStrTr, enableCCDBParams, useMC, useGeom, sclOpt)); + specs.emplace_back(o2::vertexing::getSecondaryVertexingSpec(src, enableCasc, enable3body, enableStrTr, enableCCDBParams, useMC, useGeom)); // only TOF clusters are needed if TOF is involved, no clusters MC needed WorkflowSpec inputspecs; diff --git a/Detectors/GlobalTrackingWorkflow/src/tof-matcher-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/tof-matcher-workflow.cxx index 9a95c83617210..27adab7d50439 100644 --- a/Detectors/GlobalTrackingWorkflow/src/tof-matcher-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/tof-matcher-workflow.cxx @@ -168,10 +168,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) specs.push_back(s); } } - if (sclOpt.needTPCScalersWorkflow() && !configcontext.options().get("disable-root-input")) { - specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == 2, sclOpt.enableMShapeCorrection)); + if (!configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpt.enableMShapeCorrection, sclOpt)); } - specs.emplace_back(o2::globaltracking::getTOFMatcherSpec(src, useMC, useFIT, refitTPCTOF, strict, extratolerancetrd, writeMatchable, sclOpt, nLanes)); // doTPCrefit not yet supported (need to load TPC clusters?) + specs.emplace_back(o2::globaltracking::getTOFMatcherSpec(src, useMC, useFIT, refitTPCTOF, strict, extratolerancetrd, writeMatchable, nLanes)); // doTPCrefit not yet supported (need to load TPC clusters?) if (!disableRootOut) { std::vector writers; diff --git a/Detectors/GlobalTrackingWorkflow/src/tpcits-match-workflow.cxx b/Detectors/GlobalTrackingWorkflow/src/tpcits-match-workflow.cxx index 17ab2191f0e1e..0168c3076261e 100644 --- a/Detectors/GlobalTrackingWorkflow/src/tpcits-match-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/tpcits-match-workflow.cxx @@ -93,10 +93,10 @@ WorkflowSpec defineDataProcessing(o2::framework::ConfigContext const& configcont } o2::framework::WorkflowSpec specs; - if (sclOpt.needTPCScalersWorkflow() && !configcontext.options().get("disable-root-input")) { - specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == 2, sclOpt.enableMShapeCorrection)); + if (!configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpt.enableMShapeCorrection, sclOpt)); } - specs.emplace_back(o2::globaltracking::getTPCITSMatchingSpec(srcL, useFT0, calib, !GID::includesSource(GID::TPC, src), useGeom, useMC, sclOpt)); + specs.emplace_back(o2::globaltracking::getTPCITSMatchingSpec(srcL, useFT0, calib, !GID::includesSource(GID::TPC, src), useGeom, useMC)); if (!configcontext.options().get("disable-root-output")) { specs.emplace_back(o2::globaltracking::getTrackWriterTPCITSSpec(useMC)); diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TPCTrackStudy.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TPCTrackStudy.h index 47385f400ec01..8f95203b52ffd 100644 --- a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TPCTrackStudy.h +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TPCTrackStudy.h @@ -27,7 +27,7 @@ struct CorrectionMapsLoaderGloOpts; namespace o2::trackstudy { /// create a processor spec -o2::framework::DataProcessorSpec getTPCTrackStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts); +o2::framework::DataProcessorSpec getTPCTrackStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool useMC); } // namespace o2::trackstudy diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudy.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudy.h index d1326a47ac909..2fc21c6d7cd1c 100644 --- a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudy.h +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackMCStudy.h @@ -21,7 +21,7 @@ namespace o2::trackstudy { /// create a processor spec -o2::framework::DataProcessorSpec getTrackMCStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, bool checkSV); +o2::framework::DataProcessorSpec getTrackMCStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool checkSV); } // namespace o2::trackstudy diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackingStudy.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackingStudy.h index 7a15c191cbeed..caa50dc1d481a 100644 --- a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackingStudy.h +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackingStudy.h @@ -23,7 +23,7 @@ namespace o2::trackstudy { /// create a processor spec -o2::framework::DataProcessorSpec getTrackingStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts); +o2::framework::DataProcessorSpec getTrackingStudySpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool useMC); } // namespace o2::trackstudy diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx index ee475acbbcf70..072993edfec4a 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TPCTrackStudy.cxx @@ -50,13 +50,8 @@ using timeEst = o2::dataformats::TimeStampWithError; 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) - : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mUseMC(useMC) - { - mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); - mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); - mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); - } + TPCTrackStudySpec(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool useMC) + : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mUseMC(useMC) {} ~TPCTrackStudySpec() final = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -108,7 +103,6 @@ void TPCTrackStudySpec::init(InitContext& ic) if (mXRef < 0.) { mXRef = 0.; } - mTPCCorrMapsLoader.init(ic); mDBGOut = std::make_unique("tpc-trackStudy.root", "recreate"); if (ic.options().get("dump-clusters")) { mDBGOutCl = std::make_unique("tpc-trackStudy-cl.root", "recreate"); @@ -151,17 +145,6 @@ void TPCTrackStudySpec::updateTimeDependentParams(ProcessingContext& pc) 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 TPCTrackStudySpec::process(o2::globaltracking::RecoContainer& recoData) @@ -415,12 +398,9 @@ void TPCTrackStudySpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { return; } - if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { - return; - } } -DataProcessorSpec getTPCTrackStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts) +DataProcessorSpec getTPCTrackStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC) { std::vector outputs; Options opts{ @@ -445,13 +425,13 @@ DataProcessorSpec getTPCTrackStudySpec(GTrackID::mask_t srcTracks, GTrackID::mas dataRequest->inputs, true); o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); + o2::tpc::CorrectionMapsLoader::requestInputs(dataRequest->inputs, opts); return DataProcessorSpec{ "tpc-track-study", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, sclOpts, srcTracks, useMC)}, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, useMC)}, opts}; } diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx index 1db303d20e5d9..01d127c6511bd 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackMCStudy.cxx @@ -86,13 +86,8 @@ using timeEst = o2::dataformats::TimeStampWithError; class TrackMCStudy final : public Task { public: - TrackMCStudy(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, bool checkSV) - : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mCheckSV(checkSV) - { - mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); - mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); - mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); - } + TrackMCStudy(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool checkSV) + : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mCheckSV(checkSV) {} ~TrackMCStudy() final = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -179,7 +174,6 @@ void TrackMCStudy::init(InitContext& ic) mNCheckDecays++; } mDecaysMaps.resize(mNCheckDecays); - mTPCCorrMapsLoader.init(ic); } void TrackMCStudy::run(ProcessingContext& pc) @@ -208,17 +202,6 @@ void TrackMCStudy::updateTimeDependentParams(ProcessingContext& pc) mTPCCorrMapsLoader.acknowledgeUpdate(); updateMaps = true; } - if (mTPCVDriftHelper.isUpdated()) { - LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", - mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, - mTPCVDriftHelper.getVDriftObject().timeOffsetCorr, mTPCVDriftHelper.getVDriftObject().refTimeOffset, - mTPCVDriftHelper.getSourceName()); - mTPCVDriftHelper.acknowledgeUpdate(); - updateMaps = true; - } - if (updateMaps) { - mTPCCorrMapsLoader.updateVDrift(mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().getTimeOffset()); - } static bool initOnceDone = false; if (!initOnceDone) { // this params need to be queried only once initOnceDone = true; @@ -1023,9 +1006,6 @@ void TrackMCStudy::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { return; } - if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { - return; - } if (matcher == ConcreteDataMatcher("ITS", "ALPIDEPARAM", 0)) { LOG(info) << "ITS Alpide param updated"; const auto& par = o2::itsmft::DPLAlpideParam::Instance(); @@ -1371,7 +1351,7 @@ void TrackMCStudy::processITSTracks(const o2::globaltracking::RecoContainer& rec } } -DataProcessorSpec getTrackMCStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, bool checkSV) +DataProcessorSpec getTrackMCStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool checkSV) { std::vector outputs; Options opts{ @@ -1390,7 +1370,7 @@ DataProcessorSpec getTrackMCStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask dataRequest->requestSecondaryVertices(useMC); } o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); + o2::tpc::CorrectionMapsLoader::requestInputs(dataRequest->inputs, opts); auto ggRequest = std::make_shared(false, // orbitResetTime true, // GRPECS=true true, // GRPLHCIF @@ -1404,7 +1384,7 @@ DataProcessorSpec getTrackMCStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask "track-mc-study", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, sclOpts, checkSV)}, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, checkSV)}, opts}; } diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx index a184058a1bfd6..042e884824274 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx @@ -70,13 +70,8 @@ using timeEst = o2::dataformats::TimeStampWithError; 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) - : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mUseMC(useMC) - { - mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); - mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); - mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); - } + 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; @@ -125,7 +120,6 @@ class TrackingStudySpec final : public Task void TrackingStudySpec::init(InitContext& ic) { o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); - mTPCCorrMapsLoader.init(ic); int lane = ic.services().get().inputTimesliceId; int maxLanes = ic.services().get().maxInputTimeslices; std::string dbgnm = maxLanes == 1 ? "trackStudy.root" : fmt::format("trackStudy_{}.root", lane); @@ -232,17 +226,6 @@ void TrackingStudySpec::updateTimeDependentParams(ProcessingContext& pc) 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 TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) @@ -724,9 +707,6 @@ void TrackingStudySpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) 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; @@ -746,7 +726,7 @@ float TrackingStudySpec::getDCAZCut(float pt) const return fun.Eval(pt); } -DataProcessorSpec getTrackingStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts) +DataProcessorSpec getTrackingStudySpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC) { std::vector outputs; auto dataRequest = std::make_shared(); @@ -781,13 +761,13 @@ DataProcessorSpec getTrackingStudySpec(GTrackID::mask_t srcTracks, GTrackID::mas {"min-x-prop", VariantType::Float, 100.f, {"track should be propagated to this X at least"}}, }; o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); + o2::tpc::CorrectionMapsLoader::requestInputs(dataRequest->inputs, opts); return DataProcessorSpec{ "track-study", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, useMC, sclOpts)}, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, useMC)}, opts}; } diff --git a/Detectors/GlobalTrackingWorkflow/study/src/tpc-track-study-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/tpc-track-study-workflow.cxx index 3e92178c81b7d..457ff034fa991 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/tpc-track-study-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/tpc-track-study-workflow.cxx @@ -71,10 +71,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) } o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC); o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); // P-vertex is always needed - if (sclOpt.needTPCScalersWorkflow() && !configcontext.options().get("disable-root-input")) { - specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == 2, sclOpt.enableMShapeCorrection)); + if (!configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpt.enableMShapeCorrection, sclOpt)); } - specs.emplace_back(o2::trackstudy::getTPCTrackStudySpec(srcTrc, srcCls, useMC, sclOpt)); + specs.emplace_back(o2::trackstudy::getTPCTrackStudySpec(srcTrc, srcCls, useMC)); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); diff --git a/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx index 9e0055a389bfe..74add7dfebb51 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx @@ -82,11 +82,11 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) if (checkSV) { o2::globaltracking::InputHelper::addInputSpecsSVertex(configcontext, specs); } - if (sclOpt.needTPCScalersWorkflow() && !configcontext.options().get("disable-root-input")) { - specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == 2, sclOpt.enableMShapeCorrection)); + if (!configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpt.enableMShapeCorrection, sclOpt)); } - specs.emplace_back(o2::trackstudy::getTrackMCStudySpec(srcTrc, srcCls, sclOpt, checkSV)); + specs.emplace_back(o2::trackstudy::getTrackMCStudySpec(srcTrc, srcCls, checkSV)); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); diff --git a/Detectors/GlobalTrackingWorkflow/study/src/tracking-study-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/tracking-study-workflow.cxx index ae2e3b5301a14..932a21b6a902e 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/tracking-study-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/tracking-study-workflow.cxx @@ -71,13 +71,13 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) if (sclOpt.requestCTPLumi) { srcCls = srcCls | GID::getSourcesMask("CTP"); } - if (sclOpt.needTPCScalersWorkflow() && !configcontext.options().get("disable-root-input")) { - specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == 2, sclOpt.enableMShapeCorrection)); + if (!configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpt.enableMShapeCorrection, sclOpt)); } o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC); o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); // P-vertex is always needed - specs.emplace_back(o2::trackstudy::getTrackingStudySpec(srcTrc, srcCls, useMC, sclOpt)); + specs.emplace_back(o2::trackstudy::getTrackingStudySpec(srcTrc, srcCls, useMC)); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); diff --git a/Detectors/TPC/calibration/CMakeLists.txt b/Detectors/TPC/calibration/CMakeLists.txt index a1068b928780d..905aa22e90e3b 100644 --- a/Detectors/TPC/calibration/CMakeLists.txt +++ b/Detectors/TPC/calibration/CMakeLists.txt @@ -59,6 +59,7 @@ o2_add_library(TPCCalibration src/CorrectdEdxDistortions.cxx src/PressureTemperatureHelper.cxx src/CMVContainer.cxx + src/CorrectionMapsLoaderFull.cxx PUBLIC_LINK_LIBRARIES O2::DataFormatsTPC O2::TPCBaseRecSim O2::TPCReconstruction ROOT::Minuit Microsoft.GSL::GSL @@ -118,6 +119,7 @@ o2_target_root_dictionary(TPCCalibration include/TPCCalibration/CorrectdEdxDistortions.h include/TPCCalibration/PressureTemperatureHelper.h include/TPCCalibration/CMVContainer.h) + include/TPCCalibration/CorrectionMapsLoaderFull.h) o2_add_test_root_macro(macro/comparePedestalsAndNoise.C PUBLIC_LINK_LIBRARIES O2::TPCBaseRecSim diff --git a/Detectors/TPC/calibration/include/TPCCalibration/CorrectionMapsLoader.h b/Detectors/TPC/calibration/include/TPCCalibration/CorrectionMapsLoader.h index 5a11ce3ea24e5..5524c1ed1f59c 100644 --- a/Detectors/TPC/calibration/include/TPCCalibration/CorrectionMapsLoader.h +++ b/Detectors/TPC/calibration/include/TPCCalibration/CorrectionMapsLoader.h @@ -27,29 +27,13 @@ namespace o2 namespace framework { class ProcessingContext; -class ConcreteDataMatcher; class InputSpec; class ConfigParamSpec; -class ConfigParamRegistry; -class InitContext; } // namespace framework namespace tpc { -struct CorrectionMapsLoaderGloOpts { - int lumiType = 0; ///< what estimator to used for corrections scaling: 0: no scaling, 1: CTP, 2: IDC - int lumiMode = 0; ///< what corrections method to use: 0: classical scaling, 1: Using of the derivative map, 2: Using of the derivative map for MC - bool enableMShapeCorrection = false; - bool requestCTPLumi = true; //< request CTP Lumi regardless of what is used for corrections scaling - bool checkCTPIDCconsistency = true; //< check the selected CTP or IDC scaling source being consistent with mean scaler of the map - - bool needTPCScalersWorkflow() const - { - return lumiType == 2 || enableMShapeCorrection; - } -}; - class CorrectionMapsLoader : public o2::gpu::CorrectionMapsHelper { public: @@ -58,27 +42,15 @@ class CorrectionMapsLoader : public o2::gpu::CorrectionMapsHelper CorrectionMapsLoader(const CorrectionMapsLoader&) = delete; #ifndef GPUCA_GPUCODE_DEVICE - bool accountCCDBInputs(const o2::framework::ConcreteDataMatcher& matcher, void* obj); void extractCCDBInputs(o2::framework::ProcessingContext& pc); - void updateVDrift(float vdriftCorr, float vdrifRef, float driftTimeOffset = 0); - void init(o2::framework::InitContext& ic); - void copySettings(const CorrectionMapsLoader& src); - void updateInverse(); /// recalculate inverse correction - void checkMeanScaleConsistency(float meanLumi, float threshold) const; - float getMapMeanRate(const o2::gpu::TPCFastTransform* mp, bool lumiOverridden) const; - static void requestCCDBInputs(std::vector& inputs, std::vector& options, const CorrectionMapsLoaderGloOpts& gloOpts); + static void requestInputs(std::vector& inputs, std::vector& options); + // static CorrectionMapsLoaderGloOpts parseGlobalOptions(const o2::framework::ConfigParamRegistry& opts); static void addGlobalOptions(std::vector& options); - static void addOptions(std::vector& options); - static CorrectionMapsLoaderGloOpts parseGlobalOptions(const o2::framework::ConfigParamRegistry& opts); protected: static void addOption(std::vector& options, o2::framework::ConfigParamSpec&& osp); static void addInput(std::vector& inputs, o2::framework::InputSpec&& isp); - - 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/CorrectionMapsLoaderFull.h b/Detectors/TPC/calibration/include/TPCCalibration/CorrectionMapsLoaderFull.h new file mode 100644 index 0000000000000..e60fa874c6d9f --- /dev/null +++ b/Detectors/TPC/calibration/include/TPCCalibration/CorrectionMapsLoaderFull.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. + +/// \file CorrectionMapsLoaderFull.h +/// \brief Helper class to access load maps from CCDB +/// \author matthias.kleiner@cern.ch + +#ifndef TPC_CORRECTION_MAPS_LOADERFULL_H_ +#define TPC_CORRECTION_MAPS_LOADERFULL_H_ + +#include +#include "CorrectionMapsHelperFull.h" +#include "CorrectionMapsHelper.h" + +namespace o2 +{ +namespace framework +{ +class ProcessingContext; +class ConcreteDataMatcher; +class InputSpec; +class ConfigParamSpec; +class InitContext; +} // namespace framework + +namespace tpc +{ + +class CorrectionMapsLoaderFull : public o2::gpu::CorrectionMapsHelperFull +{ + public: + CorrectionMapsLoaderFull() = default; + ~CorrectionMapsLoaderFull() = default; + CorrectionMapsLoaderFull(const CorrectionMapsLoaderFull&) = delete; + + bool accountCCDBInputs(const o2::framework::ConcreteDataMatcher& matcher, void* obj); + void extractCCDBInputs(o2::framework::ProcessingContext& pc, float tpcScaler = -1.f); + void init(o2::framework::InitContext& ic, bool idcsAvailable); + void checkMeanScaleConsistency(float meanLumi, float threshold) const; + + static void requestCCDBInputs(std::vector& inputs, const o2::tpc::CorrectionMapsLoaderGloOpts& gloOpts); + + protected: + static void addOption(std::vector& options, o2::framework::ConfigParamSpec&& osp); + static void addInput(std::vector& inputs, o2::framework::InputSpec&& isp); + + float mInstLumiCTPFactor = 1.0; // multiplicative factor for inst. lumi + int mLumiCTPSource = 0; // 0: main, 1: alternative CTP lumi source + bool mIDC2CTPFallbackActive = false; // flag indicating that fallback from IDC to CTP scaling is active +}; + +} // namespace tpc + +} // namespace o2 + +#endif diff --git a/Detectors/TPC/calibration/src/CalculatedEdx.cxx b/Detectors/TPC/calibration/src/CalculatedEdx.cxx index 478acda1189c2..c0f836e6b8452 100644 --- a/Detectors/TPC/calibration/src/CalculatedEdx.cxx +++ b/Detectors/TPC/calibration/src/CalculatedEdx.cxx @@ -32,8 +32,9 @@ using namespace o2::tpc; CalculatedEdx::CalculatedEdx() { - mTPCCorrMapsHelper.setOwner(true); - mTPCCorrMapsHelper.setCorrMap(TPCFastTransformHelperO2::instance()->create(0)); + std::vector buffer; + gpu::TPCFastTransformPOD::create(buffer, *TPCFastTransformHelperO2::instance()->create(0)); + mTPCCorrMapsHelper.setCorrMap(std::move(buffer)); } void CalculatedEdx::setMembers(std::vector* tpcTrackClIdxVecInput, const o2::tpc::ClusterNativeAccess& clIndex, std::vector* vTPCTracksArrayInp) diff --git a/Detectors/TPC/calibration/src/CorrectionMapsLoader.cxx b/Detectors/TPC/calibration/src/CorrectionMapsLoader.cxx index 038fe3c34e140..f20967f29b9f8 100644 --- a/Detectors/TPC/calibration/src/CorrectionMapsLoader.cxx +++ b/Detectors/TPC/calibration/src/CorrectionMapsLoader.cxx @@ -10,155 +10,74 @@ // or submit itself to any jurisdiction. #include "TPCCalibration/CorrectionMapsLoader.h" -#include "TPCCalibration/CorrMapParam.h" -#include "TPCReconstruction/TPCFastTransformHelperO2.h" -#include "TPCBaseRecSim/CDBInterface.h" #include "Framework/Logger.h" #include "Framework/ProcessingContext.h" -#include "Framework/CCDBParamSpec.h" #include "Framework/InputRecord.h" #include "Framework/ConfigParamSpec.h" -#include "Framework/ConcreteDataMatcher.h" -#include "Framework/InitContext.h" -#include "Framework/DeviceSpec.h" -#include "Framework/ConfigParamRegistry.h" -#include "DataFormatsCTP/LumiInfo.h" -#include "TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h" - +#include "TPCFastTransformPOD.h" using namespace o2::tpc; using namespace o2::framework; #ifndef GPUCA_GPUCODE_DEVICE -//________________________________________________________ -void CorrectionMapsLoader::updateVDrift(float vdriftCorr, float vdrifRef, float driftTimeOffset) -{ - o2::tpc::TPCFastTransformHelperO2::instance()->updateCalibration(*mCorrMap, 0, vdriftCorr, vdrifRef, driftTimeOffset); - if (mCorrMapRef) { - o2::tpc::TPCFastTransformHelperO2::instance()->updateCalibration(*mCorrMapRef, 0, vdriftCorr, vdrifRef, driftTimeOffset); - } - if (mCorrMapMShape) { - o2::tpc::TPCFastTransformHelperO2::instance()->updateCalibration(*mCorrMapMShape, 0, vdriftCorr, vdrifRef, driftTimeOffset); - } -} - //________________________________________________________ void CorrectionMapsLoader::extractCCDBInputs(ProcessingContext& pc) { - pc.inputs().get("tpcCorrPar"); - pc.inputs().get("tpcCorrMap"); - pc.inputs().get("tpcCorrMapRef"); - const int maxDumRep = 5; - int dumRep = 0; - 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"); - } else { - if (dumRep < maxDumRep && lumiPrev.nHBFCounted == 0 && lumiPrev.nHBFCountedFV0 == 0) { - LOGP(alarm, "Previous TF lumi used to substitute dummy input is empty, warning {} of {}", ++dumRep, maxDumRep); - } - lumiObj = lumiPrev; - } - setInstLumiCTP(mInstLumiCTPFactor * (mLumiCTPSource == 0 ? lumiObj.getLumi() : lumiObj.getLumiAlt())); - if (getLumiScaleType() == 1) { - setInstLumi(getInstLumiCTP()); - } + const bool lumiValid = pc.inputs().isValid("lumiCTP"); + if(lumiValid) { + mInstLumiCTP = pc.inputs().get("lumiCTP"); } - if (getUseMShapeCorrection()) { - LOGP(info, "Setting M-Shape map"); - const auto mapMShape = pc.inputs().get("mshape"); - const_cast(mapMShape.get())->rectifyAfterReadingFromFile(); - mCorrMapMShape = std::unique_ptr(new TPCFastTransform); - mCorrMapMShape->cloneFromObject(*(mapMShape.get()), nullptr); - setCorrMapMShape(mCorrMapMShape.get()); - setUpdatedMapMShape(); + const bool mapValid = pc.inputs().isValid("corrMap"); + if(!mapValid) { + LOGP(info, "No correction map found in the input record!"); + return; } - // update inverse in case it is requested - if (!mScaleInverse) { - updateInverse(); - } - reportScaling(); + // get the raw buffer and reinterpret as TPCFastTransformPOD + auto const& raw = pc.inputs().get("corrMap"); + setCorrMap(&gpu::TPCFastTransformPOD::get(raw)); + setUpdatedMap(); } //________________________________________________________ -void CorrectionMapsLoader::requestCCDBInputs(std::vector& inputs, std::vector& options, const CorrectionMapsLoaderGloOpts& gloOpts) +void CorrectionMapsLoader::requestInputs(std::vector& inputs, std::vector& options) { - if (gloOpts.lumiMode == 0) { - addInput(inputs, {"tpcCorrMap", "TPC", "CorrMap", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalCorrMap), {}, 1)}); // time-dependent - addInput(inputs, {"tpcCorrMapRef", "TPC", "CorrMapRef", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalCorrMapRef), {}, 0)}); // load once - } else if (gloOpts.lumiMode == 1) { - addInput(inputs, {"tpcCorrMap", "TPC", "CorrMap", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalCorrMap), {}, 1)}); // time-dependent - addInput(inputs, {"tpcCorrMapRef", "TPC", "CorrMapRef", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalCorrDerivMap), {}, 1)}); // time-dependent - } else if (gloOpts.lumiMode == 2) { - // for MC corrections - addInput(inputs, {"tpcCorrMap", "TPC", "CorrMap", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalCorrMapMC), {}, 1)}); // time-dependent - addInput(inputs, {"tpcCorrMapRef", "TPC", "CorrMapRef", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalCorrDerivMapMC), {}, 1)}); // time-dependent - } else { - LOG(fatal) << "Correction mode unknown! Choose either 0 (default) or 1 (derivative map) for flag corrmap-lumi-mode."; - } - - if (gloOpts.requestCTPLumi) { - addInput(inputs, {"CTPLumi", "CTP", "LUMI", 0, Lifetime::Timeframe}); - } - - if (gloOpts.lumiType == 2) { - addInput(inputs, {"tpcscaler", o2::header::gDataOriginTPC, "TPCSCALER", 0, Lifetime::Timeframe}); - } - - addInput(inputs, {"tpcCorrPar", "TPC", "CorrMapParam", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CorrMapParam), {}, 0)}); // load once - - if (gloOpts.enableMShapeCorrection) { - addInput(inputs, {"mshape", o2::header::gDataOriginTPC, "TPCMSHAPE", 0, Lifetime::Timeframe}); - } - addOptions(options); + addInput(inputs, {"corrMap", o2::header::gDataOriginTPC, "TPCCORRMAP", 0, Lifetime::Timeframe}); + addInput(inputs, {"lumiCTP", o2::header::gDataOriginCTP, "LUMICTP", 0, Lifetime::Timeframe}); } -//________________________________________________________ -void CorrectionMapsLoader::addOptions(std::vector& options) +void CorrectionMapsLoader::addInput(std::vector& inputs, InputSpec&& isp) { - // these are options which should be added at the level of device using TPC corrections - // At the moment - nothing, all options are moved to configurable param CorrMapParam - addOption(options, ConfigParamSpec{"recalculate-inverse-correction", o2::framework::VariantType::Bool, false, {"recalculate the inverse correction in case lumi mode 1 or 2 is used"}}); - addOption(options, ConfigParamSpec{"nthreads-inverse-correction", o2::framework::VariantType::Int, 4, {"Number of threads used for calculating the inverse correction (-1=all threads)"}}); + if (std::find(inputs.begin(), inputs.end(), isp) == inputs.end()) { + inputs.emplace_back(isp); + } } -//________________________________________________________ +// CorrectionMapsLoaderGloOpts CorrectionMapsLoader::parseGlobalOptions(const o2::framework::ConfigParamRegistry& opts) +// { +// CorrectionMapsLoaderGloOpts tpcopt; +// auto lumiTypeVal = opts.get("lumi-type"); +// if (lumiTypeVal < -1 || lumiTypeVal > 2) { +// LOGP(fatal, "Invalid lumi-type value: {}", lumiTypeVal); +// } +// tpcopt.lumiType = static_cast(lumiTypeVal); + +// auto lumiModeVal = opts.get("corrmap-lumi-mode"); +// if (lumiModeVal < -1 || lumiModeVal > 2) { +// LOGP(fatal, "Invalid corrmap-lumi-mode value: {}", lumiModeVal); +// } +// tpcopt.lumiMode = static_cast(lumiModeVal); + +// tpcopt.enableMShapeCorrection = opts.get("enable-M-shape-correction"); +// tpcopt.requestCTPLumi = !opts.get("disable-ctp-lumi-request"); +// tpcopt.checkCTPIDCconsistency = !opts.get("disable-lumi-type-consistency-check"); +// if (!tpcopt.requestCTPLumi && tpcopt.lumiType == LumiScaleType::CTPLumi) { +// LOGP(fatal, "Scaling with CTP Lumi is requested but this input is disabled"); +// } +// return tpcopt; +// } + void CorrectionMapsLoader::addGlobalOptions(std::vector& options) { // these are options which should be added at the workflow level, since they modify the inputs of the devices @@ -169,30 +88,6 @@ void CorrectionMapsLoader::addGlobalOptions(std::vector& option addOption(options, ConfigParamSpec{"disable-lumi-type-consistency-check", o2::framework::VariantType::Bool, false, {"disable check of selected CTP or IDC scaling source being consistent with the map"}}); } -//________________________________________________________ -CorrectionMapsLoaderGloOpts CorrectionMapsLoader::parseGlobalOptions(const o2::framework::ConfigParamRegistry& opts) -{ - CorrectionMapsLoaderGloOpts tpcopt; - tpcopt.lumiType = opts.get("lumi-type"); - tpcopt.lumiMode = opts.get("corrmap-lumi-mode"); - tpcopt.enableMShapeCorrection = opts.get("enable-M-shape-correction"); - tpcopt.requestCTPLumi = !opts.get("disable-ctp-lumi-request"); - tpcopt.checkCTPIDCconsistency = !opts.get("disable-lumi-type-consistency-check"); - if (!tpcopt.requestCTPLumi && tpcopt.lumiType == 1) { - LOGP(fatal, "Scaling with CTP Lumi is requested but this input is disabled"); - } - return tpcopt; -} - -//________________________________________________________ -void CorrectionMapsLoader::addInput(std::vector& inputs, InputSpec&& isp) -{ - if (std::find(inputs.begin(), inputs.end(), isp) == inputs.end()) { - inputs.emplace_back(isp); - } -} - -//________________________________________________________ void CorrectionMapsLoader::addOption(std::vector& options, ConfigParamSpec&& osp) { if (std::find(options.begin(), options.end(), osp) == options.end()) { @@ -200,182 +95,4 @@ void CorrectionMapsLoader::addOption(std::vector& options, Conf } } -//________________________________________________________ -bool CorrectionMapsLoader::accountCCDBInputs(const ConcreteDataMatcher& matcher, void* obj) -{ - if (matcher == ConcreteDataMatcher("TPC", "CorrMap", 0)) { - setCorrMap((o2::gpu::TPCFastTransform*)obj); - mCorrMap->rectifyAfterReadingFromFile(); - mCorrMap->setCTP2IDCFallBackThreshold(o2::tpc::CorrMapParam::Instance().CTP2IDCFallBackThreshold); - if (getMeanLumiOverride() != 0) { - if (getLumiScaleType() == 1) { - mCorrMap->setLumi(getMeanLumiOverride()); - LOGP(info, "CorrMap mean lumi rate is overridden to {}", mCorrMap->getLumi()); - } else if (getLumiScaleType() == 2) { - mCorrMap->setIDC(getMeanLumiOverride()); - LOGP(info, "CorrMap mean IDC rate is overridden to {}", mCorrMap->getIDC()); - } - } - float mapMeanRate = 0; - if (getLumiScaleType() == 1) { - mapMeanRate = mCorrMap->getLumi(); - } else if (getLumiScaleType() == 2) { - mapMeanRate = mCorrMap->getIDC(); - } - if (mCheckCTPIDCConsistency) { - checkMeanScaleConsistency(mapMeanRate, mCorrMap->getCTP2IDCFallBackThreshold()); - } - if (getMeanLumiOverride() == 0 && mapMeanRate > 0.) { - setMeanLumi(mapMeanRate, false); - } - LOGP(debug, "MeanLumiOverride={} MeanLumiMap={} -> meanLumi = {}", getMeanLumiOverride(), mapMeanRate, getMeanLumi()); - setUpdatedMap(); - return true; - } - if (matcher == ConcreteDataMatcher("TPC", "CorrMapRef", 0)) { - setCorrMapRef((o2::gpu::TPCFastTransform*)obj); - mCorrMapRef->rectifyAfterReadingFromFile(); - mCorrMapRef->setCTP2IDCFallBackThreshold(o2::tpc::CorrMapParam::Instance().CTP2IDCFallBackThreshold); - if (getMeanLumiRefOverride() != 0) { - if (getLumiScaleType() == 1) { - mCorrMapRef->setLumi(getMeanLumiRefOverride()); - LOGP(info, "CorrMapRef mean lumi rate is overridden to {}", mCorrMapRef->getLumi()); - } else if (getLumiScaleType() == 2) { - mCorrMapRef->setIDC(getMeanLumiRefOverride()); - LOGP(info, "CorrMapRef mean IDC rate is overridden to {}", mCorrMapRef->getIDC()); - } - } - float mapRefMeanRate = 0; - if (getLumiScaleType() == 1) { - mapRefMeanRate = mCorrMapRef->getLumi(); - } else if (getLumiScaleType() == 2) { - mapRefMeanRate = mCorrMapRef->getIDC(); - } - if (mCheckCTPIDCConsistency) { - checkMeanScaleConsistency(mapRefMeanRate, mCorrMapRef->getCTP2IDCFallBackThreshold()); - } - if (getMeanLumiRefOverride() == 0) { - setMeanLumiRef(mapRefMeanRate); - } - LOGP(debug, "MeanLumiRefOverride={} MeanLumiMap={} -> meanLumi = {}", getMeanLumiRefOverride(), mapRefMeanRate, getMeanLumiRef()); - setUpdatedMapRef(); - return true; - } - if (matcher == ConcreteDataMatcher("TPC", "CorrMapParam", 0)) { - const auto& par = o2::tpc::CorrMapParam::Instance(); - mMeanLumiOverride = par.lumiMean; // negative value switches off corrections !!! - mMeanLumiRefOverride = par.lumiMeanRef; - mInstCTPLumiOverride = par.lumiInst; - mInstLumiCTPFactor = par.lumiInstFactor; - mLumiCTPSource = par.ctpLumiSource; - - if (mMeanLumiOverride != 0.) { - setMeanLumi(mMeanLumiOverride, false); - } - if (mMeanLumiRefOverride != 0.) { - setMeanLumiRef(mMeanLumiRefOverride); - } - if (mInstCTPLumiOverride != 0.) { - setInstLumiCTP(mInstCTPLumiOverride * mInstLumiCTPFactor); - if (getLumiScaleType() == 1) { - setInstLumi(getInstLumiCTP(), false); - } - } - setUpdatedLumi(); - int scaleType = getLumiScaleType(); - const std::array lumiS{"OFF", "CTP", "TPC scaler"}; - if (scaleType >= lumiS.size()) { - LOGP(fatal, "Wrong corrmap-lumi-mode provided!"); - } - - LOGP(info, "TPC correction map params updated: SP corrections: {} (corr.map scaling type={}, override values: lumiMean={} lumiRefMean={} lumiScaleMode={}), CTP Lumi: source={} lumiInstOverride={} , LumiInst scale={} ", - canUseCorrections() ? "ON" : "OFF", - lumiS[scaleType], mMeanLumiOverride, mMeanLumiRefOverride, mLumiScaleMode, mLumiCTPSource, mInstCTPLumiOverride, mInstLumiCTPFactor); - } - return false; -} - -//________________________________________________________ -void CorrectionMapsLoader::init(o2::framework::InitContext& ic) -{ - if (getLumiScaleMode() < 0) { - LOGP(fatal, "TPC correction lumi scaling mode is not set"); - } - const auto& inputRouts = ic.services().get().inputs; - bool foundCTP = false, foundTPCScl = false, foundMShape = false; - for (const auto& route : inputRouts) { - if (route.matcher == InputSpec{"CTPLumi", "CTP", "LUMI", 0, Lifetime::Timeframe}) { - foundCTP = true; - } else if (route.matcher == InputSpec{"tpcscaler", o2::header::gDataOriginTPC, "TPCSCALER", 0, Lifetime::Timeframe}) { - foundTPCScl = true; - } else if (route.matcher == InputSpec{"mshape", o2::header::gDataOriginTPC, "TPCMSHAPE", 0, Lifetime::Timeframe}) { - foundMShape = true; - } - } - setLumiCTPAvailable(foundCTP); - enableMShapeCorrection(foundMShape); - if ((getLumiScaleType() == 1 && !foundCTP) || (getLumiScaleType() == 2 && !foundTPCScl)) { - LOGP(fatal, "Lumi scaling source {}({}) is not available for TPC correction", getLumiScaleType(), getLumiScaleType() == 1 ? "CTP" : "TPCScaler"); - } - - if ((getLumiScaleMode() == 1) || (getLumiScaleMode() == 2)) { - mScaleInverse = !(ic.options().get("recalculate-inverse-correction")); - } else { - mScaleInverse = true; - } - const int nthreadsInv = (ic.options().get("nthreads-inverse-correction")); - (nthreadsInv < 0) ? TPCFastSpaceChargeCorrectionHelper::instance()->setNthreadsToMaximum() : TPCFastSpaceChargeCorrectionHelper::instance()->setNthreads(nthreadsInv); -} - -//________________________________________________________ -void CorrectionMapsLoader::copySettings(const CorrectionMapsLoader& src) -{ - setInstLumi(src.getInstLumi(), false); - setInstLumiCTP(src.getInstLumiCTP()); - setMeanLumi(src.getMeanLumi(), false); - setLumiCTPAvailable(src.getLumiCTPAvailable()); - setMeanLumiRef(src.getMeanLumiRef()); - setLumiScaleType(src.getLumiScaleType()); - setMeanLumiOverride(src.getMeanLumiOverride()); - setMeanLumiRefOverride(src.getMeanLumiRefOverride()); - setInstCTPLumiOverride(src.getInstCTPLumiOverride()); - setLumiScaleMode(src.getLumiScaleMode()); - enableMShapeCorrection(src.getUseMShapeCorrection()); - mInstLumiCTPFactor = src.mInstLumiCTPFactor; - mLumiCTPSource = src.mLumiCTPSource; - mLumiScaleMode = src.mLumiScaleMode; - mScaleInverse = src.getScaleInverse(); - mIDC2CTPFallbackActive = src.mIDC2CTPFallbackActive; -} - -void CorrectionMapsLoader::updateInverse() -{ - if (mLumiScaleMode == 1 || mLumiScaleMode == 2) { - LOGP(info, "Recalculating the inverse correction"); - setUpdatedMap(); - std::vector scaling{1, mLumiScale}; - std::vector corr{&(mCorrMap->getCorrection()), &(mCorrMapRef->getCorrection())}; - if (mCorrMapMShape) { - scaling.emplace_back(1); - corr.emplace_back(&(mCorrMapMShape->getCorrection())); - } - TPCFastSpaceChargeCorrectionHelper::instance()->initInverse(corr, scaling, false); - } else { - LOGP(info, "Reinitializing inverse correction with lumi scale mode {} not supported for now", mLumiScaleMode); - } -} - -void CorrectionMapsLoader::checkMeanScaleConsistency(float meanLumi, float threshold) const -{ - if (getLumiScaleType() == 1) { - if (meanLumi < threshold) { - LOGP(fatal, "CTP Lumi scaling source is requested, but the map mean scale {} is below the threshold {}", meanLumi, threshold); - } - } else if (getLumiScaleType() == 2) { - if (meanLumi > threshold) { - LOGP(fatal, "IDC scaling source is requested, but the map mean scale {} is above the threshold {}", meanLumi, threshold); - } - } -} - #endif // #ifndef GPUCA_GPUCODE_DEVICE diff --git a/Detectors/TPC/calibration/src/CorrectionMapsLoaderFull.cxx b/Detectors/TPC/calibration/src/CorrectionMapsLoaderFull.cxx new file mode 100644 index 0000000000000..717dcaf62779a --- /dev/null +++ b/Detectors/TPC/calibration/src/CorrectionMapsLoaderFull.cxx @@ -0,0 +1,253 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#include "TPCCalibration/CorrectionMapsLoaderFull.h" +#include "TPCCalibration/CorrMapParam.h" +#include "TPCBaseRecSim/CDBTypes.h" +#include "Framework/Logger.h" +#include "Framework/ProcessingContext.h" +#include "Framework/CCDBParamSpec.h" +#include "Framework/ConfigParamSpec.h" +#include "Framework/ConcreteDataMatcher.h" +#include "Framework/InitContext.h" +#include "Framework/DeviceSpec.h" +#include "DataFormatsCTP/LumiInfo.h" + +using namespace o2::tpc; +using namespace o2::framework; +// using namespace o2::gpu; + +//________________________________________________________ +void CorrectionMapsLoaderFull::extractCCDBInputs(ProcessingContext& pc, float tpcScaler) +{ + pc.inputs().get("tpcCorrPar"); + pc.inputs().get("tpcCorrMap"); + pc.inputs().get("tpcCorrMapRef"); + const int maxDumRep = 5; + int dumRep = 0; + o2::ctp::LumiInfo lumiObj; + static o2::ctp::LumiInfo lumiPrev; + + if (getLumiScaleType() == LumiScaleType::TPCScaler || mIDC2CTPFallbackActive) { + // 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(LumiScaleType::CTPLumi); + } 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(LumiScaleType::TPCScaler); + } + // 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"); + } else { + if (dumRep < maxDumRep && lumiPrev.nHBFCounted == 0 && lumiPrev.nHBFCountedFV0 == 0) { + LOGP(alarm, "Previous TF lumi used to substitute dummy input is empty, warning {} of {}", ++dumRep, maxDumRep); + } + lumiObj = lumiPrev; + } + setInstLumiCTP(mInstLumiCTPFactor * (mLumiCTPSource == 0 ? lumiObj.getLumi() : lumiObj.getLumiAlt())); + if (getLumiScaleType() == LumiScaleType::CTPLumi) { + setInstLumi(getInstLumiCTP()); + } + } + + reportScaling(); +} + +//________________________________________________________ +void CorrectionMapsLoaderFull::requestCCDBInputs(std::vector& inputs, const CorrectionMapsLoaderGloOpts& gloOpts) +{ + LOGP(info, "Requesting CCDB inputs for TPC correction maps with lumiType={} and lumiMode={}", static_cast(gloOpts.lumiType), static_cast(gloOpts.lumiMode)); + if (gloOpts.lumiMode == LumiScaleMode::Linear) { + addInput(inputs, {"tpcCorrMap", "TPC", "CorrMap", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalCorrMap), {}, 1)}); // time-dependent + addInput(inputs, {"tpcCorrMapRef", "TPC", "CorrMapRef", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalCorrMapRef), {}, 0)}); // load once + } else if (gloOpts.lumiMode == LumiScaleMode::DerivativeMap) { + addInput(inputs, {"tpcCorrMap", "TPC", "CorrMap", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalCorrMap), {}, 1)}); // time-dependent + addInput(inputs, {"tpcCorrMapRef", "TPC", "CorrMapRef", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalCorrDerivMap), {}, 1)}); // time-dependent + } else if (gloOpts.lumiMode == LumiScaleMode::DerivativeMapMC) { + // for MC corrections + addInput(inputs, {"tpcCorrMap", "TPC", "CorrMap", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalCorrMapMC), {}, 1)}); // time-dependent + addInput(inputs, {"tpcCorrMapRef", "TPC", "CorrMapRef", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalCorrDerivMapMC), {}, 1)}); // time-dependent + } else { + LOG(fatal) << "Correction mode unknown! Choose either 0 (default) or 1 (derivative map) for flag corrmap-lumi-mode."; + } + + if (gloOpts.requestCTPLumi) { + addInput(inputs, {"CTPLumi", "CTP", "LUMI", 0, Lifetime::Timeframe}); + } + + addInput(inputs, {"tpcCorrPar", "TPC", "CorrMapParam", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CorrMapParam), {}, 0)}); // load once +} + +//________________________________________________________ +void CorrectionMapsLoaderFull::addInput(std::vector& inputs, InputSpec&& isp) +{ + if (std::find(inputs.begin(), inputs.end(), isp) == inputs.end()) { + inputs.emplace_back(isp); + } +} + +//________________________________________________________ +void CorrectionMapsLoaderFull::addOption(std::vector& options, ConfigParamSpec&& osp) +{ + if (std::find(options.begin(), options.end(), osp) == options.end()) { + options.emplace_back(osp); + } +} + +//________________________________________________________ +bool CorrectionMapsLoaderFull::accountCCDBInputs(const ConcreteDataMatcher& matcher, void* obj) +{ + if (matcher == ConcreteDataMatcher("TPC", "CorrMap", 0)) { + setCorrMap((o2::gpu::TPCFastTransform*)obj); + mCorrMap->rectifyAfterReadingFromFile(); + mCorrMap->setCTP2IDCFallBackThreshold(o2::tpc::CorrMapParam::Instance().CTP2IDCFallBackThreshold); + if (getMeanLumiOverride() != 0) { + if (getLumiScaleType() == LumiScaleType::CTPLumi) { + mCorrMap->setLumi(getMeanLumiOverride()); + LOGP(info, "CorrMap mean lumi rate is overridden to {}", mCorrMap->getLumi()); + } else if (getLumiScaleType() == LumiScaleType::TPCScaler) { + mCorrMap->setIDC(getMeanLumiOverride()); + LOGP(info, "CorrMap mean IDC rate is overridden to {}", mCorrMap->getIDC()); + } + } + float mapMeanRate = 0; + if (getLumiScaleType() == LumiScaleType::CTPLumi) { + mapMeanRate = mCorrMap->getLumi(); + } else if (getLumiScaleType() == LumiScaleType::TPCScaler) { + mapMeanRate = mCorrMap->getIDC(); + } + if (mCheckCTPIDCConsistency) { + checkMeanScaleConsistency(mapMeanRate, mCorrMap->getCTP2IDCFallBackThreshold()); + } + if (getMeanLumiOverride() == 0 && mapMeanRate > 0.) { + setMeanLumi(mapMeanRate, false); + } + LOGP(debug, "MeanLumiOverride={} MeanLumiMap={} -> meanLumi = {}", getMeanLumiOverride(), mapMeanRate, getMeanLumi()); + setUpdatedMap(); + return true; + } + if (matcher == ConcreteDataMatcher("TPC", "CorrMapRef", 0)) { + setCorrMapRef((o2::gpu::TPCFastTransform*)obj); + mCorrMapRef->rectifyAfterReadingFromFile(); + mCorrMapRef->setCTP2IDCFallBackThreshold(o2::tpc::CorrMapParam::Instance().CTP2IDCFallBackThreshold); + if (getMeanLumiRefOverride() != 0) { + if (getLumiScaleType() == LumiScaleType::CTPLumi) { + mCorrMapRef->setLumi(getMeanLumiRefOverride()); + LOGP(info, "CorrMapRef mean lumi rate is overridden to {}", mCorrMapRef->getLumi()); + } else if (getLumiScaleType() == LumiScaleType::TPCScaler) { + mCorrMapRef->setIDC(getMeanLumiRefOverride()); + LOGP(info, "CorrMapRef mean IDC rate is overridden to {}", mCorrMapRef->getIDC()); + } + } + float mapRefMeanRate = 0; + if (getLumiScaleType() == LumiScaleType::CTPLumi) { + mapRefMeanRate = mCorrMapRef->getLumi(); + } else if (getLumiScaleType() == LumiScaleType::TPCScaler) { + mapRefMeanRate = mCorrMapRef->getIDC(); + } + if (mCheckCTPIDCConsistency) { + checkMeanScaleConsistency(mapRefMeanRate, mCorrMapRef->getCTP2IDCFallBackThreshold()); + } + if (getMeanLumiRefOverride() == 0) { + setMeanLumiRef(mapRefMeanRate); + } + LOGP(debug, "MeanLumiRefOverride={} MeanLumiMap={} -> meanLumi = {}", getMeanLumiRefOverride(), mapRefMeanRate, getMeanLumiRef()); + setUpdatedMapRef(); + return true; + } + if (matcher == ConcreteDataMatcher("TPC", "CorrMapParam", 0)) { + const auto& par = o2::tpc::CorrMapParam::Instance(); + mMeanLumiOverride = par.lumiMean; // negative value switches off corrections !!! + mMeanLumiRefOverride = par.lumiMeanRef; + mInstCTPLumiOverride = par.lumiInst; + mInstLumiCTPFactor = par.lumiInstFactor; + mLumiCTPSource = par.ctpLumiSource; + + if (mMeanLumiOverride != 0.) { + setMeanLumi(mMeanLumiOverride, false); + } + if (mMeanLumiRefOverride != 0.) { + setMeanLumiRef(mMeanLumiRefOverride); + } + if (mInstCTPLumiOverride != 0.) { + setInstLumiCTP(mInstCTPLumiOverride * mInstLumiCTPFactor); + if (getLumiScaleType() == LumiScaleType::CTPLumi) { + setInstLumi(getInstLumiCTP(), false); + } + } + setUpdatedLumi(); + int scaleType = static_cast(getLumiScaleType()); + const std::array lumiS{"OFF", "CTP", "TPC scaler"}; + if (scaleType >= lumiS.size()) { + LOGP(fatal, "Wrong corrmap-lumi-mode provided!"); + } + + LOGP(info, "TPC correction map params updated: SP corrections: {} (corr.map scaling type={}, override values: lumiMean={} lumiRefMean={} lumiScaleMode={}), CTP Lumi: source={} lumiInstOverride={} , LumiInst scale={} ", + canUseCorrections() ? "ON" : "OFF", + lumiS[scaleType], mMeanLumiOverride, mMeanLumiRefOverride, static_cast(getLumiScaleMode()), mLumiCTPSource, mInstCTPLumiOverride, mInstLumiCTPFactor); + } + return false; +} + +//________________________________________________________ +void CorrectionMapsLoaderFull::init(o2::framework::InitContext& ic, bool idcsAvailable) +{ + if (getLumiScaleMode() == LumiScaleMode::Unset) { + LOGP(fatal, "TPC correction lumi scaling mode is not set"); + } + const auto& inputRouts = ic.services().get().inputs; + bool foundCTP = false; + for (const auto& route : inputRouts) { + if (route.matcher == InputSpec{"CTPLumi", "CTP", "LUMI", 0, Lifetime::Timeframe}) { + foundCTP = true; + } + } + setLumiCTPAvailable(foundCTP); + if ((getLumiScaleType() == LumiScaleType::CTPLumi && !foundCTP) || (getLumiScaleType() == LumiScaleType::TPCScaler && !idcsAvailable)) { + LOGP(fatal, "Lumi scaling source {}({}) is not available for TPC correction", static_cast(getLumiScaleType()), getLumiScaleType() == LumiScaleType::CTPLumi ? "CTP" : "TPCScaler"); + } +} + +void CorrectionMapsLoaderFull::checkMeanScaleConsistency(float meanLumi, float threshold) const +{ + if (getLumiScaleType() == LumiScaleType::CTPLumi) { + if (meanLumi < threshold) { + LOGP(fatal, "CTP Lumi scaling source is requested, but the map mean scale {} is below the threshold {}", meanLumi, threshold); + } + } else if (getLumiScaleType() == LumiScaleType::TPCScaler) { + if (meanLumi > threshold) { + LOGP(fatal, "IDC scaling source is requested, but the map mean scale {} is above the threshold {}", meanLumi, threshold); + } + } +} diff --git a/Detectors/TPC/calibration/src/TrackDump.cxx b/Detectors/TPC/calibration/src/TrackDump.cxx index 421750a5cb22b..f78d958a54bd3 100644 --- a/Detectors/TPC/calibration/src/TrackDump.cxx +++ b/Detectors/TPC/calibration/src/TrackDump.cxx @@ -237,9 +237,8 @@ float TrackDump::ClusterNativeAdd::zc(float vertexTime) const void TrackDump::ClusterNativeAdd::loadCorrMaps(std::string_view corrMapFile, std::string_view corrMapFileRef) { - sCorrHelper.setOwner(true); - sCorrHelper.setCorrMap(gpu::TPCFastTransform::loadFromFile(corrMapFile.data())); - if (!corrMapFileRef.empty()) { - sCorrHelper.setCorrMapRef(gpu::TPCFastTransform::loadFromFile(corrMapFileRef.data())); - } + auto fastTransformTmp = gpu::TPCFastTransform::loadFromFile(corrMapFile.data()); + std::vector buffer; + gpu::TPCFastTransformPOD::create(buffer, *fastTransformTmp); + sCorrHelper.setCorrMap(std::move(buffer)); } diff --git a/Detectors/TPC/reconstruction/include/TPCReconstruction/TPCFastTransformHelperO2.h b/Detectors/TPC/reconstruction/include/TPCReconstruction/TPCFastTransformHelperO2.h index f94bff0acc076..aa042ab16b627 100644 --- a/Detectors/TPC/reconstruction/include/TPCReconstruction/TPCFastTransformHelperO2.h +++ b/Detectors/TPC/reconstruction/include/TPCReconstruction/TPCFastTransformHelperO2.h @@ -21,9 +21,8 @@ #ifndef ALICEO2_TPC_TPCFASTTRANSFORMHELPERO2_H_ #define ALICEO2_TPC_TPCFASTTRANSFORMHELPERO2_H_ -#include "TPCFastTransform.h" +#include "TPCFastTransformPOD.h" #include "Rtypes.h" -#include namespace o2 { @@ -61,7 +60,15 @@ class TPCFastTransformHelperO2 std::unique_ptr create(Long_t TimeStamp, const TPCFastSpaceChargeCorrection& correction); /// Updates the transformation with the new time stamp - int updateCalibration(TPCFastTransform& transform, Long_t TimeStamp, float vDriftFactor = 1.f, float vDriftRef = 0.f, float driftTimeOffset = 0.f); + int updateCalibration(TPCFastTransform& fastTransform, Long_t TimeStamp, float vDriftFactor = 1.f, float vDriftRef = 0.f, float driftTimeOffset = 0.f) + { + return updateCalibrationImpl(fastTransform, TimeStamp, vDriftFactor, vDriftRef, driftTimeOffset); + } + + int updateCalibration(TPCFastTransformPOD& fastTransform, Long_t TimeStamp, float vDriftFactor = 1.f, float vDriftRef = 0.f, float driftTimeOffset = 0.f) + { + return updateCalibrationImpl(fastTransform, TimeStamp, vDriftFactor, vDriftRef, driftTimeOffset); + } /// _______________ Utilities ________________________ @@ -73,6 +80,9 @@ class TPCFastTransformHelperO2 /// initialization void init(); + template + int updateCalibrationImpl(T& transform, Long_t TimeStamp, float vDriftFactor, float vDriftRef, float driftTimeOffset); + static TPCFastTransformHelperO2* sInstance; ///< singleton instance bool mIsInitialized = 0; ///< initialization flag TPCFastTransformGeo mGeo; ///< geometry parameters diff --git a/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx b/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx index 419ced9fa978e..6e0bccbdc40fe 100644 --- a/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx +++ b/Detectors/TPC/reconstruction/src/TPCFastTransformHelperO2.cxx @@ -22,8 +22,6 @@ #include "TPCBase/Sector.h" #include "DataFormatsTPC/Defs.h" #include "TPCFastTransform.h" -#include "Spline2DHelper.h" -#include "Riostream.h" #include using namespace o2::gpu; @@ -136,7 +134,8 @@ std::unique_ptr TPCFastTransformHelperO2::create(Long_t TimeSt return create(TimeStamp, correction); } -int TPCFastTransformHelperO2::updateCalibration(TPCFastTransform& fastTransform, Long_t TimeStamp, float vDriftFactor, float vDriftRef, float driftTimeOffset) +template +int TPCFastTransformHelperO2::updateCalibrationImpl(T& fastTransform, Long_t TimeStamp, float vDriftFactor, float vDriftRef, float driftTimeOffset) { // Update the calibration with the new time stamp LOGP(debug, "Updating calibration: timestamp:{} vdriftFactor:{} vdriftRef:{}", TimeStamp, vDriftFactor, vDriftRef); @@ -150,7 +149,6 @@ int TPCFastTransformHelperO2::updateCalibration(TPCFastTransform& fastTransform, // search for the calibration database ... - auto& detParam = ParameterDetector::Instance(); auto& gasParam = ParameterGas::Instance(); auto& elParam = ParameterElectronics::Instance(); // start the initialization @@ -228,5 +226,9 @@ void TPCFastTransformHelperO2::testGeometry(const TPCFastTransformGeo& geo) cons << " max Dx " << maxDx << " max Dy " << maxDy << std::endl; } } + +template int TPCFastTransformHelperO2::updateCalibrationImpl(TPCFastTransform&, Long_t, float, float, float); +template int TPCFastTransformHelperO2::updateCalibrationImpl(TPCFastTransformPOD&, Long_t, float, float, float); + } // namespace tpc } // namespace o2 diff --git a/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx b/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx index 0debfa72dd7fa..2b21053bda1ff 100644 --- a/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx +++ b/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx @@ -25,7 +25,7 @@ #include "TPCReconstruction/TPCFastTransformHelperO2.h" #include "CorrectionMapsHelper.h" -#include "TPCFastTransform.h" +#include "TPCFastTransformPOD.h" #include "GPUO2Interface.h" #include "GPUO2InterfaceUtils.h" #include "GPUO2InterfaceConfiguration.h" @@ -74,10 +74,13 @@ BOOST_AUTO_TEST_CASE(CATracking_test1) config.configWorkflow.inputs.set(gpudatatypes::InOutType::TPCClusters); config.configWorkflow.outputs.set(gpudatatypes::InOutType::TPCMergedTracks); - std::unique_ptr fastTransform(TPCFastTransformHelperO2::instance()->create(0)); + auto fastTransformTmp = TPCFastTransformHelperO2::instance()->create(0); + std::vector fastTransformBuf; + TPCFastTransformPOD::create(fastTransformBuf, *fastTransformTmp); + std::unique_ptr fastTransformHelper(new CorrectionMapsHelper()); - fastTransformHelper->setCorrMap(fastTransform.get()); - config.configCalib.fastTransform = fastTransform.get(); + fastTransformHelper->setCorrMap(std::move(fastTransformBuf)); + config.configCalib.fastTransform = fastTransformHelper->getCorrMap(); config.configCalib.fastTransformHelper = fastTransformHelper.get(); auto dEdxCalibContainer = GPUO2InterfaceUtils::getCalibdEdxContainerDefault(); config.configCalib.dEdxCalibContainer = dEdxCalibContainer.get(); diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCCalibPadGainTracksSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCCalibPadGainTracksSpec.h index 516ea128acfe7..8e88a27d51e7f 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/TPCCalibPadGainTracksSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCCalibPadGainTracksSpec.h @@ -26,7 +26,6 @@ #include "TPCWorkflow/ProcessingHelpers.h" #include "Framework/CCDBParamSpec.h" #include "TPCBaseRecSim/CDBInterface.h" -#include "TPCCalibration/VDriftHelper.h" #include "TPCCalibration/CorrectionMapsLoader.h" #include "DetectorsBase/GRPGeomHelper.h" #include "GPUO2InterfaceUtils.h" @@ -45,22 +44,18 @@ namespace tpc class TPCCalibPadGainTracksDevice : public o2::framework::Task { public: - TPCCalibPadGainTracksDevice(std::shared_ptr dr, std::shared_ptr req, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, const uint32_t publishAfterTFs, const bool debug, const bool useLastExtractedMapAsReference, const std::string polynomialsFile, const bool disablePolynomialsCCDB) : mDataRequest(dr), mPublishAfter(publishAfterTFs), mDebug(debug), mUseLastExtractedMapAsReference(useLastExtractedMapAsReference), mDisablePolynomialsCCDB(disablePolynomialsCCDB), mCCDBRequest(req) + TPCCalibPadGainTracksDevice(std::shared_ptr dr, std::shared_ptr req, const uint32_t publishAfterTFs, const bool debug, const bool useLastExtractedMapAsReference, const std::string polynomialsFile, const bool disablePolynomialsCCDB) : mDataRequest(dr), mPublishAfter(publishAfterTFs), mDebug(debug), mUseLastExtractedMapAsReference(useLastExtractedMapAsReference), mDisablePolynomialsCCDB(disablePolynomialsCCDB), mCCDBRequest(req) { if (!polynomialsFile.empty()) { LOGP(info, "Loading polynomials from file {}", polynomialsFile); mPadGainTracks.loadPolTopologyCorrectionFromFile(polynomialsFile.data()); mDisablePolynomialsCCDB = true; } - mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); - mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); - mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); } void init(o2::framework::InitContext& ic) final { o2::base::GRPGeomHelper::instance().setRequest(mCCDBRequest); - mTPCCorrMapsLoader.init(ic); // setting up the histogram ranges const auto nBins = ic.options().get("nBins"); auto reldEdxMin = ic.options().get("reldEdxMin"); @@ -151,8 +146,6 @@ class TPCCalibPadGainTracksDevice : public o2::framework::Task LOGP(info, "Updating Q topology correction from CCDB"); const auto* topologyCorr = static_cast(obj); mPadGainTracks.setPolTopologyCorrectionFromContainer(*topologyCorr); - } else if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { - } else if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { } else if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { const auto field = o2::gpu::GPUO2InterfaceUtils::getNominalGPUBz(*o2::base::GRPGeomHelper::instance().getGRPMagField()); LOGP(info, "Setting magnetic field to {} kG", field); @@ -188,7 +181,6 @@ class TPCCalibPadGainTracksDevice : public o2::framework::Task LOGP(info, "fetching residual gain map"); pc.inputs().get>*>("tpcresidualgainmap"); } - mTPCVDriftHelper.extractCCDBInputs(pc); mTPCCorrMapsLoader.extractCCDBInputs(pc); bool updateMaps = false; if (mTPCCorrMapsLoader.isUpdated()) { @@ -196,19 +188,6 @@ class TPCCalibPadGainTracksDevice : public o2::framework::Task updateMaps = true; } mPadGainTracks.setTPCCorrMaps(&mTPCCorrMapsLoader); - if (mTPCVDriftHelper.isUpdated()) { - LOGP(info, "Updating TPC fast transform map with new VDrift factor of {} wrt reference {} and DriftTimeOffset correction {} wrt {} from source {}", - mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, - mTPCVDriftHelper.getVDriftObject().timeOffsetCorr, mTPCVDriftHelper.getVDriftObject().refTimeOffset, - mTPCVDriftHelper.getSourceName()); - mPadGainTracks.setTPCVDrift(mTPCVDriftHelper.getVDriftObject()); - mTPCVDriftHelper.acknowledgeUpdate(); - updateMaps = true; - } - if (updateMaps) { - mTPCCorrMapsLoader.updateVDrift(mTPCVDriftHelper.getVDriftObject().corrFact, mTPCVDriftHelper.getVDriftObject().refVDrift, mTPCVDriftHelper.getVDriftObject().getTimeOffset()); - } - mPadGainTracks.setMembers(&tracks, &clRefs, clusters->clusterIndex, recoData.clusterShMapTPC, recoData.occupancyMapTPC); mPadGainTracks.processTracks(mMaxTracksPerTF); ++mProcessedTFs; @@ -237,7 +216,6 @@ class TPCCalibPadGainTracksDevice : public o2::framework::Task unsigned int mUseEveryNthTF{1}; ///< process every Nth TF only unsigned int mFirstTFSend{1}; ///< first TF for which the data will be send (initialized randomly) int mMaxTracksPerTF{-1}; ///< max number of tracks processed per TF - o2::tpc::VDriftHelper mTPCVDriftHelper{}; o2::tpc::CorrectionMapsLoader mTPCCorrMapsLoader{}; void sendOutput(DataAllocator& output) @@ -247,16 +225,16 @@ class TPCCalibPadGainTracksDevice : public o2::framework::Task } }; -DataProcessorSpec getTPCCalibPadGainTracksSpec(const uint32_t publishAfterTFs, const bool debug, const bool useLastExtractedMapAsReference, const std::string polynomialsFile, bool disablePolynomialsCCDB, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts) +DataProcessorSpec getTPCCalibPadGainTracksSpec(const uint32_t publishAfterTFs, const bool debug, const bool useLastExtractedMapAsReference, const std::string polynomialsFile, bool disablePolynomialsCCDB) { std::vector inputs; auto dataRequest = std::make_shared(); dataRequest->requestTracks(o2::dataformats::GlobalTrackID::getSourceMask(o2::dataformats::GlobalTrackID::TPC), false); dataRequest->requestClusters(o2::dataformats::GlobalTrackID::getSourceMask(o2::dataformats::GlobalTrackID::TPC), false); - if (sclOpts.lumiType == 1) { - dataRequest->inputs.emplace_back("CTPLumi", "CTP", "LUMI", 0, Lifetime::Timeframe); - } + // if (sclOpts.lumiType == 1) { + // dataRequest->inputs.emplace_back("CTPLumi", "CTP", "LUMI", 0, Lifetime::Timeframe); + // } if (!polynomialsFile.empty()) { disablePolynomialsCCDB = true; @@ -270,7 +248,6 @@ DataProcessorSpec getTPCCalibPadGainTracksSpec(const uint32_t publishAfterTFs, c dataRequest->inputs.emplace_back("tpcresidualgainmap", gDataOriginTPC, "RESIDUALGAINMAP", 0, Lifetime::Condition, ccdbParamSpec(CDBTypeMap.at(CDBType::CalPadGainResidual))); } - o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); Options opts{ {"nBins", VariantType::Int, 20, {"Number of bins per histogram"}}, {"reldEdxMin", VariantType::Int, 0, {"Minimum x coordinate of the histogram for Q/(dE/dx)"}}, @@ -293,7 +270,7 @@ DataProcessorSpec getTPCCalibPadGainTracksSpec(const uint32_t publishAfterTFs, c {"useEveryNthTF", VariantType::Int, 10, {"Using only a fraction of the data: 1: Use every TF, 10: Use only every tenth TF."}}, {"maxTracksPerTF", VariantType::Int, 10000, {"Maximum number of processed tracks per TF (-1 for processing all tracks)"}}, }; - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); + o2::tpc::CorrectionMapsLoader::requestInputs(dataRequest->inputs, opts); auto ccdbRequest = std::make_shared(false, // orbitResetTime false, // GRPECS=true @@ -310,7 +287,7 @@ DataProcessorSpec getTPCCalibPadGainTracksSpec(const uint32_t publishAfterTFs, c "calib-tpc-gainmap-tracks", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ccdbRequest, sclOpts, publishAfterTFs, debug, useLastExtractedMapAsReference, polynomialsFile, disablePolynomialsCCDB)}, + AlgorithmSpec{adaptFromTask(dataRequest, ccdbRequest, publishAfterTFs, debug, useLastExtractedMapAsReference, polynomialsFile, disablePolynomialsCCDB)}, opts}; // end DataProcessorSpec } diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCRefitter.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCRefitter.h index 31a5ce756142a..e2d8a2de3912d 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/TPCRefitter.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCRefitter.h @@ -23,7 +23,7 @@ struct CorrectionMapsLoaderGloOpts; namespace o2::trackstudy { /// create a processor spec -o2::framework::DataProcessorSpec getTPCRefitterSpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, bool requestCosmics = false); +o2::framework::DataProcessorSpec getTPCRefitterSpec(o2::dataformats::GlobalTrackID::mask_t srcTracks, o2::dataformats::GlobalTrackID::mask_t srcClus, bool useMC, bool requestCosmics = false); } // namespace o2::trackstudy diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCScalerSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCScalerSpec.h index b85a882870ecb..950ef9a248443 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/TPCScalerSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCScalerSpec.h @@ -13,13 +13,14 @@ #define O2_TPC_TPCSCALER_SPEC #include "Framework/DataProcessorSpec.h" +#include "TPCCalibration/CorrectionMapsLoader.h" namespace o2 { namespace tpc { -o2::framework::DataProcessorSpec getTPCScalerSpec(bool enableIDCs, bool enableMShape); +o2::framework::DataProcessorSpec getTPCScalerSpec(bool enableIDCs, bool enableMShape, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts); } // end namespace tpc } // end namespace o2 diff --git a/Detectors/TPC/workflow/src/RecoWorkflow.cxx b/Detectors/TPC/workflow/src/RecoWorkflow.cxx index 3054dd5d61519..fb9b09329bfab 100644 --- a/Detectors/TPC/workflow/src/RecoWorkflow.cxx +++ b/Detectors/TPC/workflow/src/RecoWorkflow.cxx @@ -201,9 +201,7 @@ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vecto laneConfiguration, &hook}, propagateMC)); - if (sclOpts.needTPCScalersWorkflow()) { // for standalone tpc-reco workflow - specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpts.lumiType == 2, sclOpts.enableMShapeCorrection)); - } + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpts.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpts.enableMShapeCorrection, sclOpts)); if (produceTracks && sclOpts.requestCTPLumi) { // need CTP digits (lumi) reader specs.emplace_back(o2::ctp::getDigitsReaderSpec(false)); } @@ -225,9 +223,7 @@ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vecto if (!getenv("DPL_DISABLE_TPC_TRIGGER_READER") || atoi(getenv("DPL_DISABLE_TPC_TRIGGER_READER")) != 1) { specs.emplace_back(o2::tpc::getTPCTriggerReaderSpec()); } - if (sclOpts.needTPCScalersWorkflow()) { // for standalone tpc-reco workflow - specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpts.lumiType == 2, sclOpts.enableMShapeCorrection)); - } + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpts.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpts.enableMShapeCorrection, sclOpts)); if (sclOpts.requestCTPLumi) { // need CTP digits (lumi) reader specs.emplace_back(o2::ctp::getDigitsReaderSpec(false)); } @@ -461,11 +457,6 @@ framework::WorkflowSpec getWorkflow(CompletionPolicyData* policyData, std::vecto if (runGPUReco) { o2::gpu::GPURecoWorkflowSpec::Config cfg; cfg.runTPCTracking = true; - cfg.lumiScaleType = sclOpts.lumiType; - cfg.lumiScaleMode = sclOpts.lumiMode; - cfg.checkCTPIDCconsistency = sclOpts.checkCTPIDCconsistency; - cfg.enableMShape = sclOpts.enableMShapeCorrection; - cfg.enableCTPLumi = sclOpts.requestCTPLumi; cfg.decompressTPC = decompressTPC; cfg.decompressTPCFromROOT = decompressTPC && inputType == InputType::CompClustersRoot; cfg.caClusterer = caClusterer; diff --git a/Detectors/TPC/workflow/src/TPCRefitter.cxx b/Detectors/TPC/workflow/src/TPCRefitter.cxx index 43a55526246fe..0db87bc8edbae 100644 --- a/Detectors/TPC/workflow/src/TPCRefitter.cxx +++ b/Detectors/TPC/workflow/src/TPCRefitter.cxx @@ -63,13 +63,8 @@ class TPCRefitterSpec final : public Task Streamer = 0x1, ///< Write per track streamer information TFVectors = 0x2, ///< Writer vectors per TF }; - TPCRefitterSpec(std::shared_ptr dr, std::shared_ptr gr, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, GTrackID::mask_t src, bool useMC) - : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mUseMC(useMC) - { - mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); - mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); - mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); - } + TPCRefitterSpec(std::shared_ptr dr, std::shared_ptr gr, GTrackID::mask_t src, bool useMC) + : mDataRequest(dr), mGGCCDBRequest(gr), mTracksSrc(src), mUseMC(useMC) {} ~TPCRefitterSpec() final = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; @@ -184,7 +179,6 @@ void TPCRefitterSpec::init(InitContext& ic) mXRef = 0.; } mGenerator = std::mt19937(std::random_device{}()); - mTPCCorrMapsLoader.init(ic); } void TPCRefitterSpec::run(ProcessingContext& pc) @@ -219,21 +213,8 @@ void TPCRefitterSpec::updateTimeDependentParams(ProcessingContext& pc) // none at the moment } // we may have other params which need to be queried regularly - 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()); } } @@ -413,9 +394,6 @@ void TPCRefitterSpec::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { return; } - if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { - return; - } } bool TPCRefitterSpec::getDCAs(const o2::track::TrackPar& track, float& dcar, float& dcaz) @@ -740,7 +718,7 @@ void TPCRefitterSpec::processCosmics(o2::globaltracking::RecoContainer& recoData } } -DataProcessorSpec getTPCRefitterSpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, bool requestCosmics) +DataProcessorSpec getTPCRefitterSpec(GTrackID::mask_t srcTracks, GTrackID::mask_t srcClusters, bool useMC, bool requestCosmics) { std::vector outputs; Options opts{ @@ -778,13 +756,13 @@ DataProcessorSpec getTPCRefitterSpec(GTrackID::mask_t srcTracks, GTrackID::mask_ dataRequest->inputs, true); o2::tpc::VDriftHelper::requestCCDBInputs(dataRequest->inputs); - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(dataRequest->inputs, opts, sclOpts); + o2::tpc::CorrectionMapsLoader::requestInputs(dataRequest->inputs, opts); return DataProcessorSpec{ "tpc-refitter", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, sclOpts, srcTracks, useMC)}, + AlgorithmSpec{adaptFromTask(dataRequest, ggRequest, srcTracks, useMC)}, opts}; } diff --git a/Detectors/TPC/workflow/src/TPCScalerSpec.cxx b/Detectors/TPC/workflow/src/TPCScalerSpec.cxx index f185b5e08c7e7..61cbb988d672e 100644 --- a/Detectors/TPC/workflow/src/TPCScalerSpec.cxx +++ b/Detectors/TPC/workflow/src/TPCScalerSpec.cxx @@ -27,6 +27,8 @@ #include "TPCCalibration/TPCFastSpaceChargeCorrectionHelper.h" #include "TPCSpaceCharge/SpaceCharge.h" #include "CommonUtils/TreeStreamRedirector.h" +#include "TPCCalibration/CorrectionMapsLoaderFull.h" +#include "TPCCalibration/VDriftHelper.h" using namespace o2::framework; @@ -38,7 +40,12 @@ namespace tpc class TPCScalerSpec : public Task { public: - TPCScalerSpec(std::shared_ptr req, bool enableIDCs, bool enableMShape) : mCCDBRequest(req), mEnableIDCs(enableIDCs), mEnableMShape(enableMShape){}; + TPCScalerSpec(std::shared_ptr req, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, bool enableIDCs, bool enableMShape) : mCCDBRequest(req), mEnableIDCs(enableIDCs), mEnableMShape(enableMShape), mGlobOpts(sclOpts) + { + mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); + mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); + mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); + }; void init(framework::InitContext& ic) final { @@ -57,6 +64,7 @@ class TPCScalerSpec : public Task if (enableStreamer) { mStreamer = std::make_unique("M_Shape.root", "recreate"); } + mTPCCorrMapsLoader.init(ic, mEnableIDCs); } void endOfStream(EndOfStreamContext& eos) final @@ -69,6 +77,11 @@ class TPCScalerSpec : public Task void run(ProcessingContext& pc) final { o2::base::GRPGeomHelper::instance().checkUpdates(pc); + mTPCVDriftHelper.extractCCDBInputs(pc); + if (mTPCVDriftHelper.isUpdated()) { + mTPCVDriftHelper.acknowledgeUpdate(); + } + if (mEnableIDCs && pc.inputs().isValid("tpcscaler")) { pc.inputs().get("tpcscaler"); } @@ -122,12 +135,7 @@ class TPCScalerSpec : public Task std::unique_ptr spCorrection = TPCFastSpaceChargeCorrectionHelper::instance()->createFromGlobalCorrection(getCorrections, mKnotsYMshape, mKnotsZMshape); std::unique_ptr fastTransform(TPCFastTransformHelperO2::instance()->create(0, *spCorrection)); - pc.outputs().snapshot(Output{header::gDataOriginTPC, "TPCMSHAPE"}, *fastTransform); - } else { - // send empty dummy object - LOGP(info, "Sending default (no) M-shape correction"); - auto fastTransform = o2::tpc::TPCFastTransformHelperO2::instance()->create(0); - pc.outputs().snapshot(Output{header::gDataOriginTPC, "TPCMSHAPE"}, *fastTransform); + mTPCCorrMapsLoader.setCorrMapMShape(std::move(fastTransform)); } if (mStreamer) { @@ -140,6 +148,7 @@ class TPCScalerSpec : public Task } } + float tpcScaler = -1.f; if (mEnableIDCs) { static int runWarningIDC = -1; if (pc.services().get().runNumber != mTPCScaler.getRun() && runWarningIDC != currRun) { @@ -149,8 +158,7 @@ class TPCScalerSpec : public Task float scalerA = mTPCScaler.getMeanScaler(timestamp, o2::tpc::Side::A); float scalerC = mTPCScaler.getMeanScaler(timestamp, o2::tpc::Side::C); float meanScaler = (scalerA + scalerC) / 2; - LOGP(info, "Publishing TPC scaler: {} for timestamp: {}, firstTFOrbit: {}", meanScaler, timestamp, firstTFOrbit); - pc.outputs().snapshot(Output{header::gDataOriginTPC, "TPCSCALER"}, meanScaler); + tpcScaler = meanScaler; if (mStreamer) { (*mStreamer) << "treeIDC" << "scalerA=" << scalerA @@ -160,11 +168,67 @@ class TPCScalerSpec : public Task << "\n"; } } + // check for Maps update + mTPCCorrMapsLoader.extractCCDBInputs(pc, tpcScaler); + + const float lumiCTP = mTPCCorrMapsLoader.getInstLumiCTP(); + // if CTP lumi was notrequest - defualt of 0 is published, otherwise the value is scaled with the provided factor + LOGP(info, "Publishing CTP Lumi: {} for timestamp: {}, firstTFOrbit: {}", lumiCTP, timestamp, firstTFOrbit); + pc.outputs().snapshot(Output{header::gDataOriginCTP, "LUMICTP"}, lumiCTP); + + buildMap(pc); + } + + void buildMap(ProcessingContext& pc) + { + // reference map + auto* corrMap = mTPCCorrMapsLoader.getCorrMap(); + + // // new correction map + o2::gpu::TPCFastTransform finalMap; + finalMap.cloneFromObject(*corrMap, nullptr); + finalMap.setApplyCorrectionOn(); + + const auto* corrMapRef = mTPCCorrMapsLoader.getCorrMapRef(); + const float lumiScale = mTPCCorrMapsLoader.getLumiScale(); + std::vector> additionalCorrections; + + // if standard scaling is used: map(lumi) = (mean_map - ref_map) * lumiScale + ref_map + if (mTPCCorrMapsLoader.getLumiScaleMode() == LumiScaleMode::Linear) { + const std::vector> step0{{&(corrMapRef->getCorrection()), -1.f}}; + // finalMap = (mean_map - finalMap) + TPCFastSpaceChargeCorrectionHelper::instance()->mergeCorrections(finalMap.getCorrection(), 1, step0, true); + + // finalMap = finalMap * lumiScale + ref_map + const std::vector> step1{{&(corrMapRef->getCorrection()), 1.f}}; + TPCFastSpaceChargeCorrectionHelper::instance()->mergeCorrections(finalMap.getCorrection(), lumiScale, step1, true); + + } else if (mTPCCorrMapsLoader.getLumiScaleMode() == LumiScaleMode::DerivativeMap || mTPCCorrMapsLoader.getLumiScaleMode() == LumiScaleMode::DerivativeMapMC) { + additionalCorrections.emplace_back(&(corrMapRef->getCorrection()), lumiScale); + } + + // if mshape map valid + if (!mTPCCorrMapsLoader.isCorrMapMShapeDummy()) { + LOGP(info, "Adding M-shape correction to the final map with scaling factor {}", mMShapeScalingFac); + additionalCorrections.emplace_back(&(mTPCCorrMapsLoader.getCorrMapMShape()->getCorrection()), 1.f); + } + + if (!additionalCorrections.empty()) { + TPCFastSpaceChargeCorrectionHelper::instance()->mergeCorrections(finalMap.getCorrection(), 1, additionalCorrections, true); + } + + Output corrMapOutput{header::gDataOriginTPC, "TPCCORRMAP", 0}; + auto outputBuffer = o2::pmr::vector(pc.outputs().getMemoryResource(corrMapOutput)); + auto* pod = TPCFastTransformPOD::create(outputBuffer, finalMap.getCorrection()); + const auto& vd = mTPCVDriftHelper.getVDriftObject(); + o2::tpc::TPCFastTransformHelperO2::instance()->updateCalibration(*pod, 0, vd.corrFact, vd.refVDrift, vd.getTimeOffset()); + pc.outputs().adoptContainer(corrMapOutput, std::move(outputBuffer)); } void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) final { o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj); + mTPCVDriftHelper.accountCCDBInputs(matcher, obj); if (matcher == ConcreteDataMatcher(o2::header::gDataOriginTPC, "TPCSCALERCCDB", 0)) { LOGP(info, "Updating TPC scaler"); mTPCScaler.setFromTree(*((TTree*)obj)); @@ -198,12 +262,16 @@ class TPCScalerSpec : public Task LOGP(info, "Loaded default M-Shape correction object from CCDB"); } } + if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { + return; + } } private: std::shared_ptr mCCDBRequest; ///< info for CCDB request const bool mEnableIDCs{true}; ///< enable IDCs const bool mEnableMShape{false}; ///< enable v shape scalers + const o2::tpc::CorrectionMapsLoaderGloOpts mGlobOpts; ///< global options for the correction map loader, needed to decide which maps to load from CCDB bool mEnableWeights{false}; ///< use weights for TPC scalers TPCScalerWeights mScalerWeights{}; ///< scaler weights float mIonDriftTimeMS{-1}; ///< ion drift time @@ -214,6 +282,8 @@ class TPCScalerSpec : public Task int mKnotsYMshape{4}; ///< number of knots used for the spline object for M-Shape distortions int mKnotsZMshape{4}; ///< number of knots used for the spline object for M-Shape distortions std::unique_ptr mStreamer; ///< streamer + o2::tpc::CorrectionMapsLoaderFull mTPCCorrMapsLoader{}; + o2::tpc::VDriftHelper mTPCVDriftHelper{}; ///< helper for v-drift void overWriteIntegrationTime() { @@ -229,7 +299,7 @@ class TPCScalerSpec : public Task } }; -o2::framework::DataProcessorSpec getTPCScalerSpec(bool enableIDCs, bool enableMShape) +o2::framework::DataProcessorSpec getTPCScalerSpec(bool enableIDCs, bool enableMShape, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts) { std::vector inputs; if (enableIDCs) { @@ -251,18 +321,16 @@ o2::framework::DataProcessorSpec getTPCScalerSpec(bool enableIDCs, bool enableMS inputs); std::vector outputs; - if (enableIDCs) { - outputs.emplace_back(o2::header::gDataOriginTPC, "TPCSCALER", 0, Lifetime::Timeframe); - } - if (enableMShape) { - outputs.emplace_back(o2::header::gDataOriginTPC, "TPCMSHAPE", 0, Lifetime::Timeframe); - } + outputs.emplace_back(o2::header::gDataOriginTPC, "TPCCORRMAP", 0, Lifetime::Timeframe); + outputs.emplace_back(o2::header::gDataOriginCTP, "LUMICTP", 0, Lifetime::Timeframe); + o2::tpc::VDriftHelper::requestCCDBInputs(inputs); + o2::tpc::CorrectionMapsLoaderFull::requestCCDBInputs(inputs, sclOpts); return DataProcessorSpec{ "tpc-scaler", inputs, outputs, - AlgorithmSpec{adaptFromTask(ccdbRequest, enableIDCs, enableMShape)}, + AlgorithmSpec{adaptFromTask(ccdbRequest, sclOpts, enableIDCs, enableMShape)}, Options{ {"ion-drift-time", VariantType::Float, -1.f, {"Overwrite ion drift time if a value >0 is provided"}}, {"max-time-for-weights", VariantType::Float, 500.f, {"Maximum possible integration time in ms when weights are used"}}, diff --git a/Detectors/TPC/workflow/src/tpc-calib-gainmap-tracks.cxx b/Detectors/TPC/workflow/src/tpc-calib-gainmap-tracks.cxx index 5475995437113..06f1f2633fb71 100644 --- a/Detectors/TPC/workflow/src/tpc-calib-gainmap-tracks.cxx +++ b/Detectors/TPC/workflow/src/tpc-calib-gainmap-tracks.cxx @@ -65,9 +65,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) const auto disablePolynomialsCCDB = config.options().get("disablePolynomialsCCDB"); const auto sclOpt = o2::tpc::CorrectionMapsLoader::parseGlobalOptions(config.options()); WorkflowSpec workflow; - if (sclOpt.needTPCScalersWorkflow()) { - workflow.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == 2, sclOpt.enableMShapeCorrection)); - } - workflow.emplace_back(o2::tpc::getTPCCalibPadGainTracksSpec(publishAfterTFs, debug, useLastExtractedMapAsReference, polynomialsFile, disablePolynomialsCCDB, sclOpt)); + workflow.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpt.enableMShapeCorrection, sclOpt)); + workflow.emplace_back(o2::tpc::getTPCCalibPadGainTracksSpec(publishAfterTFs, debug, useLastExtractedMapAsReference, polynomialsFile, disablePolynomialsCCDB)); return workflow; } diff --git a/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx b/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx index f3d4d639ddfd2..b9c8cde98273b 100644 --- a/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx +++ b/Detectors/TPC/workflow/src/tpc-reco-workflow.cxx @@ -24,6 +24,7 @@ #include "TPCWorkflow/RecoWorkflow.h" #include "TPCReaderWorkflow/TPCSectorCompletionPolicy.h" #include "TPCCalibration/CorrectionMapsLoader.h" +#include "TPCCalibration/CorrectionMapsLoaderFull.h" #include "Framework/CustomWorkflowTerminationHook.h" #include "DataFormatsTPC/TPCSectorHeader.h" #include "Algorithm/RangeTokenizer.h" @@ -75,8 +76,8 @@ void customize(std::vector& workflowOptions) {"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)"}}, }; - o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); o2::raw::HBFUtilsInitializer::addConfigOption(options); + o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); std::swap(workflowOptions, options); } diff --git a/Detectors/TPC/workflow/src/tpc-refitter-workflow.cxx b/Detectors/TPC/workflow/src/tpc-refitter-workflow.cxx index 78bf63a44d60f..1700750f8aa4b 100644 --- a/Detectors/TPC/workflow/src/tpc-refitter-workflow.cxx +++ b/Detectors/TPC/workflow/src/tpc-refitter-workflow.cxx @@ -79,18 +79,16 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) srcCls = srcCls | GID::getSourcesMask("CTP"); } - if (sclOpt.lumiType == 2) { - const auto enableMShape = configcontext.options().get("enable-M-shape-correction"); - const auto enableIDCs = !configcontext.options().get("disable-IDC-scalers"); - specs.emplace_back(o2::tpc::getTPCScalerSpec(enableIDCs, enableMShape)); - } + const auto enableMShape = configcontext.options().get("enable-M-shape-correction"); + const auto enableIDCs = !configcontext.options().get("disable-IDC-scalers"); + specs.emplace_back(o2::tpc::getTPCScalerSpec(enableIDCs, enableMShape, sclOpt)); o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC); o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, useMC); // P-vertex is always needed if (enableCosmics) { o2::globaltracking::InputHelper::addInputSpecsCosmics(configcontext, specs, useMC); } - specs.emplace_back(o2::trackstudy::getTPCRefitterSpec(srcTrc, srcCls, useMC, sclOpt, enableCosmics)); + specs.emplace_back(o2::trackstudy::getTPCRefitterSpec(srcTrc, srcCls, useMC, enableCosmics)); // configure dpl timer to inject correct firstTForbit: start from the 1st orbit of TF containing 1st sampled orbit o2::raw::HBFUtilsInitializer hbfIni(configcontext, specs); diff --git a/Detectors/TPC/workflow/src/tpc-scaler.cxx b/Detectors/TPC/workflow/src/tpc-scaler.cxx index 598687c7dff41..d4b994f6eb275 100644 --- a/Detectors/TPC/workflow/src/tpc-scaler.cxx +++ b/Detectors/TPC/workflow/src/tpc-scaler.cxx @@ -15,6 +15,7 @@ #include "TPCWorkflow/TPCScalerSpec.h" #include "CommonUtils/ConfigurableParam.h" #include "Framework/ConfigParamSpec.h" +#include "TPCCalibration/CorrectionMapsLoader.h" using namespace o2::framework; @@ -25,7 +26,7 @@ void customize(std::vector& workflowOptions) ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}, {"enable-M-shape-correction", VariantType::Bool, false, {"Enable M-shape distortion correction"}}, {"disable-IDC-scalers", VariantType::Bool, false, {"Disable TPC scalers for space-charge distortion fluctuation correction"}}}; - + o2::tpc::CorrectionMapsLoader::addGlobalOptions(options); std::swap(workflowOptions, options); } @@ -37,6 +38,7 @@ WorkflowSpec defineDataProcessing(ConfigContext const& config) o2::conf::ConfigurableParam::updateFromString(config.options().get("configKeyValues")); const auto enableMShape = config.options().get("enable-M-shape-correction"); const auto enableIDCs = !config.options().get("disable-IDC-scalers"); - workflow.emplace_back(o2::tpc::getTPCScalerSpec(enableIDCs, enableMShape)); + auto sclOpt = o2::tpc::CorrectionMapsLoader::parseGlobalOptions(config.options()); + workflow.emplace_back(o2::tpc::getTPCScalerSpec(enableIDCs, enableMShape, sclOpt)); return workflow; } diff --git a/Detectors/TRD/workflow/include/TRDWorkflow/TRDGlobalTrackingSpec.h b/Detectors/TRD/workflow/include/TRDWorkflow/TRDGlobalTrackingSpec.h index 93f07dd58445e..9a7ac5a161694 100644 --- a/Detectors/TRD/workflow/include/TRDWorkflow/TRDGlobalTrackingSpec.h +++ b/Detectors/TRD/workflow/include/TRDWorkflow/TRDGlobalTrackingSpec.h @@ -52,13 +52,8 @@ namespace trd class TRDGlobalTracking : public o2::framework::Task { public: - TRDGlobalTracking(bool useMC, bool withPID, PIDPolicy policy, std::shared_ptr dataRequest, std::shared_ptr gr, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts, - o2::dataformats::GlobalTrackID::mask_t src, bool trigRecFilterActive, bool strict) : mUseMC(useMC), mWithPID(withPID), mDataRequest(dataRequest), mGGCCDBRequest(gr), mTrkMask(src), mTrigRecFilter(trigRecFilterActive), mStrict(strict), mPolicy(policy) - { - mTPCCorrMapsLoader.setLumiScaleType(sclOpts.lumiType); - mTPCCorrMapsLoader.setLumiScaleMode(sclOpts.lumiMode); - mTPCCorrMapsLoader.setCheckCTPIDCConsistency(sclOpts.checkCTPIDCconsistency); - } + TRDGlobalTracking(bool useMC, bool withPID, PIDPolicy policy, std::shared_ptr dataRequest, std::shared_ptr gr, + o2::dataformats::GlobalTrackID::mask_t src, bool trigRecFilterActive, bool strict) : mUseMC(useMC), mWithPID(withPID), mDataRequest(dataRequest), mGGCCDBRequest(gr), mTrkMask(src), mTrigRecFilter(trigRecFilterActive), mStrict(strict), mPolicy(policy) {} ~TRDGlobalTracking() override = default; void init(o2::framework::InitContext& ic) final; void fillMCTruthInfo(const TrackTRD& trk, o2::MCCompLabel lblSeed, std::vector& lblContainerTrd, std::vector& lblContainerMatch, const o2::dataformats::MCTruthContainer* trkltLabels) const; @@ -115,7 +110,7 @@ class TRDGlobalTracking : public o2::framework::Task }; /// create a processor spec -framework::DataProcessorSpec getTRDGlobalTrackingSpec(bool useMC, o2::dataformats::GlobalTrackID::mask_t src, bool trigRecFilterActive, bool strict /* = false*/, bool withPID /* = false*/, PIDPolicy policy /* = PIDPolicy::DEFAULT*/, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts); +framework::DataProcessorSpec getTRDGlobalTrackingSpec(bool useMC, o2::dataformats::GlobalTrackID::mask_t src, bool trigRecFilterActive, bool strict /* = false*/, bool withPID /* = false*/, PIDPolicy policy /* = PIDPolicy::DEFAULT*/); } // namespace trd } // namespace o2 diff --git a/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx b/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx index 0f578efd3aa5b..a080f85524684 100644 --- a/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx +++ b/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx @@ -82,7 +82,6 @@ using TrackTunePar = o2::globaltracking::TrackTuneParams; void TRDGlobalTracking::init(InitContext& ic) { o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); - mTPCCorrMapsLoader.init(ic); mTimer.Stop(); mTimer.Reset(); } @@ -175,10 +174,6 @@ void TRDGlobalTracking::updateTimeDependentParams(ProcessingContext& pc) mTPCVDriftHelper.acknowledgeUpdate(); updateCalib = true; } - if (updateCalib) { - auto& vd = mTPCVDriftHelper.getVDriftObject(); - mTPCCorrMapsLoader.updateVDrift(vd.corrFact, vd.refVDrift, vd.getTimeOffset()); - } } void TRDGlobalTracking::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) @@ -189,9 +184,6 @@ void TRDGlobalTracking::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) if (mTPCVDriftHelper.accountCCDBInputs(matcher, obj)) { return; } - if (mTPCCorrMapsLoader.accountCCDBInputs(matcher, obj)) { - return; - } if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { LOG(info) << "cluster dictionary updated"; mITSDict = (const o2::itsmft::TopologyDictionary*)obj; @@ -862,7 +854,7 @@ void TRDGlobalTracking::endOfStream(EndOfStreamContext& ec) mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } -DataProcessorSpec getTRDGlobalTrackingSpec(bool useMC, GTrackID::mask_t src, bool trigRecFilterActive, bool strict, bool withPID, PIDPolicy policy, const o2::tpc::CorrectionMapsLoaderGloOpts& sclOpts) +DataProcessorSpec getTRDGlobalTrackingSpec(bool useMC, GTrackID::mask_t src, bool trigRecFilterActive, bool strict, bool withPID, PIDPolicy policy) { std::vector outputs; uint32_t ss = o2::globaltracking::getSubSpec(strict ? o2::globaltracking::MatchingType::Strict : o2::globaltracking::MatchingType::Standard); @@ -899,7 +891,7 @@ DataProcessorSpec getTRDGlobalTrackingSpec(bool useMC, GTrackID::mask_t src, boo true); o2::tpc::VDriftHelper::requestCCDBInputs(inputs); Options opts; - o2::tpc::CorrectionMapsLoader::requestCCDBInputs(inputs, opts, sclOpts); + o2::tpc::CorrectionMapsLoader::requestInputs(inputs, opts); // Request PID policy data if (withPID) { @@ -962,7 +954,7 @@ DataProcessorSpec getTRDGlobalTrackingSpec(bool useMC, GTrackID::mask_t src, boo processorName, inputs, outputs, - AlgorithmSpec{adaptFromTask(useMC, withPID, policy, dataRequest, ggRequest, sclOpts, src, trigRecFilterActive, strict)}, + AlgorithmSpec{adaptFromTask(useMC, withPID, policy, dataRequest, ggRequest, src, trigRecFilterActive, strict)}, opts}; } diff --git a/Detectors/TRD/workflow/src/trd-tracking-workflow.cxx b/Detectors/TRD/workflow/src/trd-tracking-workflow.cxx index 7781b5ed187cb..0bff7dd94d8a2 100644 --- a/Detectors/TRD/workflow/src/trd-tracking-workflow.cxx +++ b/Detectors/TRD/workflow/src/trd-tracking-workflow.cxx @@ -115,10 +115,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) // processing devices o2::framework::WorkflowSpec specs; - if (sclOpt.needTPCScalersWorkflow() && !configcontext.options().get("disable-root-input")) { - specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == 2, sclOpt.enableMShapeCorrection)); + if (!configcontext.options().get("disable-root-input")) { + specs.emplace_back(o2::tpc::getTPCScalerSpec(sclOpt.lumiType == o2::tpc::LumiScaleType::TPCScaler, sclOpt.enableMShapeCorrection, sclOpt)); } - specs.emplace_back(o2::trd::getTRDGlobalTrackingSpec(useMC, srcTRD, trigRecFilterActive, strict, pid, policy, sclOpt)); + specs.emplace_back(o2::trd::getTRDGlobalTrackingSpec(useMC, srcTRD, trigRecFilterActive, strict, pid, policy)); if (vdexb || gain) { specs.emplace_back(o2::trd::getTRDTrackBasedCalibSpec(srcTRD, vdexb, gain)); } diff --git a/GPU/GPUTracking/Base/GPUReconstructionConvert.cxx b/GPU/GPUTracking/Base/GPUReconstructionConvert.cxx index a4b17b81bf5ac..0aabb30f60e1a 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionConvert.cxx +++ b/GPU/GPUTracking/Base/GPUReconstructionConvert.cxx @@ -20,7 +20,7 @@ #endif #include "GPUReconstructionConvert.h" -#include "TPCFastTransform.h" +#include "TPCFastTransformPOD.h" #include "GPUTPCClusterData.h" #include "GPUO2DataTypes.h" #include "GPUDataTypesIO.h" @@ -48,7 +48,7 @@ using namespace o2::tpc; using namespace o2::tpc::constants; using namespace std::string_literals; -void GPUReconstructionConvert::ConvertNativeToClusterData(o2::tpc::ClusterNativeAccess* native, std::unique_ptr* clusters, uint32_t* nClusters, const TPCFastTransform* transform, int32_t continuousMaxTimeBin) +void GPUReconstructionConvert::ConvertNativeToClusterData(o2::tpc::ClusterNativeAccess* native, std::unique_ptr* clusters, uint32_t* nClusters, const TPCFastTransformPOD* transform, int32_t continuousMaxTimeBin) { memset(nClusters, 0, NSECTORS * sizeof(nClusters[0])); uint32_t offset = 0; diff --git a/GPU/GPUTracking/Base/GPUReconstructionConvert.h b/GPU/GPUTracking/Base/GPUReconstructionConvert.h index a24eb52a3a47c..3bb8a2b3df3c2 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionConvert.h +++ b/GPU/GPUTracking/Base/GPUReconstructionConvert.h @@ -41,7 +41,7 @@ namespace o2::gpu { struct GPUParam; struct GPUTPCClusterData; -class TPCFastTransform; +class TPCFastTransformPOD; struct GPUTrackingInOutDigits; struct GPUTrackingInOutZS; @@ -49,7 +49,7 @@ class GPUReconstructionConvert { public: constexpr static uint32_t NSECTORS = GPUCA_NSECTORS; - static void ConvertNativeToClusterData(o2::tpc::ClusterNativeAccess* native, std::unique_ptr* clusters, uint32_t* nClusters, const TPCFastTransform* transform, int32_t continuousMaxTimeBin = 0); + static void ConvertNativeToClusterData(o2::tpc::ClusterNativeAccess* native, std::unique_ptr* clusters, uint32_t* nClusters, const TPCFastTransformPOD* transform, int32_t continuousMaxTimeBin = 0); static void ConvertRun2RawToNative(o2::tpc::ClusterNativeAccess& native, std::unique_ptr& nativeBuffer, const AliHLTTPCRawCluster** rawClusters, uint32_t* nRawClusters); template static void RunZSEncoder(const S& in, std::unique_ptr* outBuffer, uint32_t* outSizes, o2::raw::RawFileWriter* raw, const o2::InteractionRecord* ir, const GPUParam& param, int32_t version, bool verify, float threshold = 0.f, bool padding = false, std::function&)> digitsFilter = nullptr); diff --git a/GPU/GPUTracking/Base/GPUReconstructionTimeframe.cxx b/GPU/GPUTracking/Base/GPUReconstructionTimeframe.cxx index fefcd0ac925fe..b605c99e393b1 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionTimeframe.cxx +++ b/GPU/GPUTracking/Base/GPUReconstructionTimeframe.cxx @@ -22,7 +22,7 @@ #include "GPUTPCMCInfo.h" #include "GPUTPCClusterData.h" #include "AliHLTTPCRawCluster.h" -#include "TPCFastTransform.h" +#include "TPCFastTransformPOD.h" #include "CorrectionMapsHelper.h" #include "GPUO2DataTypes.h" #include "GPUSettings.h" diff --git a/GPU/GPUTracking/DataTypes/GPUDataTypesIO.h b/GPU/GPUTracking/DataTypes/GPUDataTypesIO.h index 76fa569a16824..acd91939245d0 100644 --- a/GPU/GPUTracking/DataTypes/GPUDataTypesIO.h +++ b/GPU/GPUTracking/DataTypes/GPUDataTypesIO.h @@ -91,7 +91,7 @@ class ORTRootSerializer; namespace o2::gpu { class CorrectionMapsHelper; -class TPCFastTransform; +class TPCFastTransformPOD; struct TPCPadGainCalib; struct TPCZSLinkMapping; @@ -125,9 +125,7 @@ struct ConstPtr { template