From 7df15eb7a082aedc353d85852cdf8a113c02cd55 Mon Sep 17 00:00:00 2001 From: shahoian Date: Wed, 6 Nov 2024 18:15:22 +0100 Subject: [PATCH 0001/2180] Update TPC scaling when MeanLumiRef changed --- GPU/TPCFastTransformation/CorrectionMapsHelper.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/GPU/TPCFastTransformation/CorrectionMapsHelper.h b/GPU/TPCFastTransformation/CorrectionMapsHelper.h index e9ee5c793cc3b..7a35077f04aef 100644 --- a/GPU/TPCFastTransformation/CorrectionMapsHelper.h +++ b/GPU/TPCFastTransformation/CorrectionMapsHelper.h @@ -90,10 +90,11 @@ class CorrectionMapsHelper } } - void setMeanLumiRef(float v) + void setMeanLumiRef(float v, bool report = false) { - if (v != mMeanLumi) { + if (v != mMeanLumiRef) { mMeanLumiRef = v; + updateLumiScale(report); } } From 5af5498f16d77025f3b404c2182f70ffa414e459 Mon Sep 17 00:00:00 2001 From: shahoian Date: Thu, 7 Nov 2024 00:19:45 +0100 Subject: [PATCH 0002/2180] Connect CTP digit-reader to MCStudy if TPC corrections asked --- .../study/src/trackMCStudy-workflow.cxx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx b/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx index 7ad11068f8a64..7aa53e2190a9e 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/trackMCStudy-workflow.cxx @@ -71,7 +71,10 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) GID::mask_t srcTrc = allowedSourcesTrc & GID::getSourcesMask(configcontext.options().get("track-sources")); GID::mask_t srcCls = allowedSourcesClus & GID::getSourcesMask(configcontext.options().get("cluster-sources")); srcCls |= GID::getSourcesMask("ITS,TPC"); - + if (sclOpt.requestCTPLumi) { + srcTrc = srcTrc | GID::getSourcesMask("CTP"); + srcCls = srcCls | GID::getSourcesMask("CTP"); + } o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, true); o2::globaltracking::InputHelper::addInputSpecsPVertex(configcontext, specs, true); // P-vertex is always needed if (checkSV) { From 096694b4a397ca5505cc867bd6db79252b2e1c38 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 7 Nov 2024 06:54:40 +0100 Subject: [PATCH 0003/2180] DPL: allow larger grace period for dispatching non-DPL incoming messages (#13639) --- Framework/Core/src/ExternalFairMQDeviceProxy.cxx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Framework/Core/src/ExternalFairMQDeviceProxy.cxx b/Framework/Core/src/ExternalFairMQDeviceProxy.cxx index 5a88be2dde6e1..823ef8f5fd5a0 100644 --- a/Framework/Core/src/ExternalFairMQDeviceProxy.cxx +++ b/Framework/Core/src/ExternalFairMQDeviceProxy.cxx @@ -108,8 +108,8 @@ void sendOnChannel(fair::mq::Device& device, fair::mq::Parts& messages, std::str } // FIXME: we need a better logic for avoiding message spam - if (timeout > 1 && timeout <= maxTimeout) { - LOG(warning) << "dispatching on channel " << channel << " was delayed by " << timeout << " ms"; + if (timeout > 100 && timeout <= maxTimeout) { + LOG(warning) << "dispatching on channel " << channel << " was delayed by " << timeout / 1000.f << " s"; } // TODO: feeling this is a bit awkward, but the interface of fair::mq::Parts does not provide a // method to clear the content. From fec521f180c79e416b137acb8358a624b0e2fd83 Mon Sep 17 00:00:00 2001 From: Matteo Concas Date: Thu, 7 Nov 2024 07:04:40 +0100 Subject: [PATCH 0004/2180] ITSMFT: Protect CCDB querying in case we don't need IRFrames (#13661) --- Detectors/ITSMFT/common/workflow/src/DigitReaderSpec.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Detectors/ITSMFT/common/workflow/src/DigitReaderSpec.cxx b/Detectors/ITSMFT/common/workflow/src/DigitReaderSpec.cxx index 5b0a6204ae3e6..3c7a86fe173d6 100644 --- a/Detectors/ITSMFT/common/workflow/src/DigitReaderSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/DigitReaderSpec.cxx @@ -70,7 +70,7 @@ void DigitReader::init(InitContext& ic) void DigitReader::run(ProcessingContext& pc) { const auto& tinfo = pc.services().get(); - if (tinfo.globalRunNumberChanged) { // new run is starting: 1st call + 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); From 2475cbcb0a4a26c920b4ecfd2eeab91f039695ba Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 7 Nov 2024 09:20:05 +0100 Subject: [PATCH 0005/2180] DPL: allow configuring compression for the AOD writer (#13659) --- .../include/Framework/DataOutputDirector.h | 2 +- Framework/Core/src/AnalysisSupportHelpers.cxx | 18 +++--- Framework/Core/src/AnalysisSupportHelpers.h | 2 +- Framework/Core/src/ArrowSupport.cxx | 7 ++- Framework/Core/src/ConfigParamDiscovery.cxx | 1 + Framework/Core/src/DataOutputDirector.cxx | 4 +- Framework/Core/src/Plugin.cxx | 61 +++++++++++++++++++ Framework/Core/src/WorkflowHelpers.cxx | 6 +- .../TestWorkflows/src/o2TestHistograms.cxx | 17 ++++++ 9 files changed, 103 insertions(+), 15 deletions(-) diff --git a/Framework/Core/include/Framework/DataOutputDirector.h b/Framework/Core/include/Framework/DataOutputDirector.h index bdcb8faf976c0..e2554c9730ba5 100644 --- a/Framework/Core/include/Framework/DataOutputDirector.h +++ b/Framework/Core/include/Framework/DataOutputDirector.h @@ -84,7 +84,7 @@ struct DataOutputDirector { std::vector getDataOutputDescriptors(InputSpec spec); // get the matching TFile - FileAndFolder getFileFolder(DataOutputDescriptor* dodesc, uint64_t folderNumber, std::string parentFileName); + FileAndFolder getFileFolder(DataOutputDescriptor* dodesc, uint64_t folderNumber, std::string parentFileName, int compression); // check file sizes bool checkFileSizes(); diff --git a/Framework/Core/src/AnalysisSupportHelpers.cxx b/Framework/Core/src/AnalysisSupportHelpers.cxx index 3613bfedb887a..e949f27a6eed6 100644 --- a/Framework/Core/src/AnalysisSupportHelpers.cxx +++ b/Framework/Core/src/AnalysisSupportHelpers.cxx @@ -318,10 +318,10 @@ DataProcessorSpec AnalysisSupportHelpers::getOutputObjHistSink(std::vector dod, - std::vector const& outputInputs) + std::vector const& outputInputs, int compressionLevel) { - auto writerFunction = [dod, outputInputs](InitContext& ic) -> std::function { + auto writerFunction = [dod, outputInputs, compressionLevel](InitContext& ic) -> std::function { LOGP(debug, "======== getGlobalAODSink::Init =========="); // find out if any table needs to be saved @@ -363,7 +363,7 @@ DataProcessorSpec std::vector aodMetaDataVals; // this functor is called once per time frame - return [dod, tfNumbers, tfFilenames, aodMetaDataKeys, aodMetaDataVals](ProcessingContext& pc) mutable -> void { + return [dod, tfNumbers, tfFilenames, aodMetaDataKeys, aodMetaDataVals, compressionLevel](ProcessingContext& pc) mutable -> void { LOGP(debug, "======== getGlobalAODSink::processing =========="); LOGP(debug, " processing data set with {} entries", pc.inputs().size()); @@ -457,7 +457,7 @@ DataProcessorSpec // a table can be saved in multiple ways // e.g. different selections of columns to different files for (auto d : ds) { - auto fileAndFolder = dod->getFileFolder(d, tfNumber, aodInputFile); + auto fileAndFolder = dod->getFileFolder(d, tfNumber, aodInputFile, compressionLevel); auto treename = fileAndFolder.folderName + "/" + d->treename; TableToTree ta2tr(table, fileAndFolder.file, @@ -495,11 +495,11 @@ DataProcessorSpec // the command line options relevant for the writer are global // see runDataProcessing.h DataProcessorSpec spec{ - "internal-dpl-aod-writer", - outputInputs, - Outputs{}, - AlgorithmSpec(writerFunction), - {}}; + .name = "internal-dpl-aod-writer", + .inputs = outputInputs, + .outputs = {}, + .algorithm = AlgorithmSpec{writerFunction}, + }; return spec; } diff --git a/Framework/Core/src/AnalysisSupportHelpers.h b/Framework/Core/src/AnalysisSupportHelpers.h index 43ce7ab85b96d..ba5bcedb4bc67 100644 --- a/Framework/Core/src/AnalysisSupportHelpers.h +++ b/Framework/Core/src/AnalysisSupportHelpers.h @@ -78,7 +78,7 @@ struct AnalysisSupportHelpers { std::vector const& tskmap); /// writes inputs of kind AOD to file static DataProcessorSpec getGlobalAODSink(std::shared_ptr dod, - std::vector const& outputInputs); + std::vector const& outputInputs, int compression); }; }; // namespace o2::framework diff --git a/Framework/Core/src/ArrowSupport.cxx b/Framework/Core/src/ArrowSupport.cxx index 5c1a9050c4e40..1a656e4d60080 100644 --- a/Framework/Core/src/ArrowSupport.cxx +++ b/Framework/Core/src/ArrowSupport.cxx @@ -37,6 +37,7 @@ #include "Headers/DataHeader.h" #include "Headers/DataHeaderHelpers.h" +#include #include #include @@ -536,7 +537,11 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() // add TFNumber and TFFilename as input to the writer outputsInputsAOD.emplace_back("tfn", "TFN", "TFNumber"); outputsInputsAOD.emplace_back("tff", "TFF", "TFFilename"); - workflow.push_back(AnalysisSupportHelpers::getGlobalAODSink(dod, outputsInputsAOD)); + int compression = 505; + if (ctx.options().hasOption("aod-writer-compression")) { + compression = ctx.options().get("aod-writer-compression"); + } + workflow.push_back(AnalysisSupportHelpers::getGlobalAODSink(dod, outputsInputsAOD, compression)); } // Move the dummy sink at the end, if needed for (size_t i = 0; i < workflow.size(); ++i) { diff --git a/Framework/Core/src/ConfigParamDiscovery.cxx b/Framework/Core/src/ConfigParamDiscovery.cxx index 6c24a44bac566..9673f77ed0e42 100644 --- a/Framework/Core/src/ConfigParamDiscovery.cxx +++ b/Framework/Core/src/ConfigParamDiscovery.cxx @@ -27,6 +27,7 @@ std::vector ConfigParamDiscovery::discover(ConfigParamRegistry& std::vector capabilitiesSpecs = { "O2Framework:DiscoverMetadataInAODCapability", "O2Framework:DiscoverMetadataInCommandLineCapability", + "O2Framework:DiscoverAODOptionsInCommandLineCapability", }; // Load all the requested plugins and discover what we can do. diff --git a/Framework/Core/src/DataOutputDirector.cxx b/Framework/Core/src/DataOutputDirector.cxx index 4b803e1050817..cfee1a4b8e8a9 100644 --- a/Framework/Core/src/DataOutputDirector.cxx +++ b/Framework/Core/src/DataOutputDirector.cxx @@ -455,7 +455,7 @@ std::vector DataOutputDirector::getDataOutputDescriptors( return result; } -FileAndFolder DataOutputDirector::getFileFolder(DataOutputDescriptor* dodesc, uint64_t folderNumber, std::string parentFileName) +FileAndFolder DataOutputDirector::getFileFolder(DataOutputDescriptor* dodesc, uint64_t folderNumber, std::string parentFileName, int compression) { // initialisation FileAndFolder fileAndFolder; @@ -488,7 +488,7 @@ FileAndFolder DataOutputDirector::getFileFolder(DataOutputDescriptor* dodesc, ui auto fn = resdirname + "/" + mfilenameBases[ind] + ".root"; delete mfilePtrs[ind]; mParentMaps[ind]->Clear(); - mfilePtrs[ind] = TFile::Open(fn.c_str(), mfileMode.c_str(), "", 505); + mfilePtrs[ind] = TFile::Open(fn.c_str(), mfileMode.c_str(), "", compression); } fileAndFolder.file = mfilePtrs[ind]; diff --git a/Framework/Core/src/Plugin.cxx b/Framework/Core/src/Plugin.cxx index 2edf2e62a3633..a98771a913d01 100644 --- a/Framework/Core/src/Plugin.cxx +++ b/Framework/Core/src/Plugin.cxx @@ -15,6 +15,7 @@ #include "Framework/Capability.h" #include "Framework/Signpost.h" #include "Framework/VariantJSONHelpers.h" +#include #include O2_DECLARE_DYNAMIC_LOG(capabilities); @@ -47,6 +48,19 @@ auto lookForCommandLineOptions = [](ConfigParamRegistry& registry, int argc, cha return false; }; +auto lookForCommandLineAODOptions = [](ConfigParamRegistry& registry, int argc, char** argv) -> bool { + O2_SIGNPOST_ID_GENERATE(sid, capabilities); + // If one of the options for aod-writer is specified, we should allow configuring compression. + for (size_t i = 0; i < argc; i++) { + std::string_view arg = argv[i]; + if (arg.starts_with("--aod-writer-")) { + O2_SIGNPOST_EVENT_EMIT(capabilities, sid, "DiscoverAODOptionsInCommandLineCapability", "AOD options found in arguments. Populating from them."); + return true; + } + } + return false; +}; + struct DiscoverMetadataInAODCapability : o2::framework::CapabilityPlugin { Capability* create() override { @@ -68,6 +82,16 @@ struct DiscoverMetadataInCommandLineCapability : o2::framework::CapabilityPlugin } }; +struct DiscoverAODOptionsInCommandLineCapability : o2::framework::CapabilityPlugin { + Capability* create() override + { + return new Capability{ + .name = "DiscoverAODOptionsInCommandLineCapability", + .checkIfNeeded = lookForCommandLineAODOptions, + .requiredPlugin = "O2Framework:DiscoverAODOptionsInCommandLine"}; + } +}; + struct DiscoverMetadataInCommandLine : o2::framework::ConfigDiscoveryPlugin { ConfigDiscovery* create() override { @@ -99,9 +123,46 @@ struct DiscoverMetadataInCommandLine : o2::framework::ConfigDiscoveryPlugin { }}; } }; + +struct DiscoverAODOptionsInCommandLine : o2::framework::ConfigDiscoveryPlugin { + ConfigDiscovery* create() override + { + return new ConfigDiscovery{ + .init = []() {}, + .discover = [](ConfigParamRegistry& registry, int argc, char** argv) -> std::vector { + O2_SIGNPOST_ID_GENERATE(sid, capabilities); + O2_SIGNPOST_EVENT_EMIT(capabilities, sid, "DiscoverAODOptionsInCommandLine", + "Discovering AOD handling related options in commandline arguments."); + std::vector results; + bool injectOption = true; + for (size_t i = 0; i < argc; i++) { + std::string_view arg = argv[i]; + if (!arg.starts_with("--aod-writer-")) { + continue; + } + std::string key = arg.data() + 2; + std::string value = argv[i + 1]; + O2_SIGNPOST_EVENT_EMIT(capabilities, sid, "DiscoverAODOptionsInCommandLine", + "Found %{public}s with value %{public}s.", key.c_str(), value.c_str()); + if (key == "aod-writer-compression") { + int numericValue = std::stoi(value); + results.push_back(ConfigParamSpec{"aod-writer-compression", VariantType::Int, numericValue, {"AOD Compression options"}}); + injectOption = false; + } + } + if (injectOption) { + results.push_back(ConfigParamSpec{"aod-writer-compression", VariantType::Int, 505, {"AOD Compression options"}}); + } + return results; + }}; + } +}; + DEFINE_DPL_PLUGINS_BEGIN DEFINE_DPL_PLUGIN_INSTANCE(DiscoverMetadataInAODCapability, Capability); DEFINE_DPL_PLUGIN_INSTANCE(DiscoverMetadataInCommandLineCapability, Capability); +DEFINE_DPL_PLUGIN_INSTANCE(DiscoverAODOptionsInCommandLineCapability, Capability); DEFINE_DPL_PLUGIN_INSTANCE(DiscoverMetadataInCommandLine, ConfigDiscovery); +DEFINE_DPL_PLUGIN_INSTANCE(DiscoverAODOptionsInCommandLine, ConfigDiscovery); DEFINE_DPL_PLUGINS_END } // namespace o2::framework diff --git a/Framework/Core/src/WorkflowHelpers.cxx b/Framework/Core/src/WorkflowHelpers.cxx index 6349bd5889eba..3fe8fae19a3b5 100644 --- a/Framework/Core/src/WorkflowHelpers.cxx +++ b/Framework/Core/src/WorkflowHelpers.cxx @@ -606,7 +606,11 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext // add TFNumber and TFFilename as input to the writer outputsInputsAOD.emplace_back(InputSpec{"tfn", "TFN", "TFNumber"}); outputsInputsAOD.emplace_back(InputSpec{"tff", "TFF", "TFFilename"}); - auto fileSink = AnalysisSupportHelpers::getGlobalAODSink(dod, outputsInputsAOD); + int compressionLevel = 505; + if (ctx.options().hasOption("aod-writer-compression")) { + compressionLevel = ctx.options().get("aod-writer-compression"); + } + auto fileSink = AnalysisSupportHelpers::getGlobalAODSink(dod, outputsInputsAOD, compressionLevel); extraSpecs.push_back(fileSink); auto it = std::find_if(outputsInputs.begin(), outputsInputs.end(), [](InputSpec& spec) -> bool { diff --git a/Framework/TestWorkflows/src/o2TestHistograms.cxx b/Framework/TestWorkflows/src/o2TestHistograms.cxx index e32149a8bfdc1..9986f52a1d940 100644 --- a/Framework/TestWorkflows/src/o2TestHistograms.cxx +++ b/Framework/TestWorkflows/src/o2TestHistograms.cxx @@ -22,26 +22,43 @@ using namespace o2; using namespace o2::framework; using namespace o2::framework::expressions; +namespace o2::aod +{ +namespace skimmedExampleTrack +{ +DECLARE_SOA_COLUMN(Pt, pt, float); //! +DECLARE_SOA_COLUMN(Eta, eta, float); //! +} // namespace skimmedExampleTrack + +DECLARE_SOA_TABLE(SkimmedExampleTrack, "AOD", "SKIMEXTRK", //! + skimmedExampleTrack::Pt, + skimmedExampleTrack::Eta); +} // namespace o2::aod + struct EtaAndClsHistogramsSimple { OutputObj etaClsH{TH2F("eta_vs_pt", "#eta vs pT", 102, -2.01, 2.01, 100, 0, 10)}; + Produces skimEx; void process(aod::Tracks const& tracks) { LOGP(info, "Invoking the simple one"); for (auto& track : tracks) { etaClsH->Fill(track.eta(), track.pt(), 0); + skimEx(track.pt(), track.eta()); } } }; struct EtaAndClsHistogramsIUSimple { OutputObj etaClsH{TH2F("eta_vs_pt", "#eta vs pT", 102, -2.01, 2.01, 100, 0, 10)}; + Produces skimEx; void process(aod::TracksIU const& tracks) { LOGP(info, "Invoking the simple one"); for (auto& track : tracks) { etaClsH->Fill(track.eta(), track.pt(), 0); + skimEx(track.pt(), track.eta()); } } }; From be3f6b23b6f05e635d8a440cc071e8d33a72f5e1 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 6 Nov 2024 14:33:48 +0100 Subject: [PATCH 0006/2180] GPU: Fix forwarding of exit code when using doublePipelined processing --- GPU/GPUTracking/Base/GPUReconstructionCPU.cxx | 5 +++-- GPU/GPUTracking/Definitions/GPUSettingsList.h | 2 +- GPU/GPUTracking/Global/GPUChainTrackingDebugAndProfiling.cxx | 2 +- GPU/Workflow/src/GPUWorkflowSpec.cxx | 2 +- 4 files changed, 6 insertions(+), 5 deletions(-) diff --git a/GPU/GPUTracking/Base/GPUReconstructionCPU.cxx b/GPU/GPUTracking/Base/GPUReconstructionCPU.cxx index 1acbb99973b43..537c3cf63a628 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionCPU.cxx +++ b/GPU/GPUTracking/Base/GPUReconstructionCPU.cxx @@ -216,8 +216,9 @@ int32_t GPUReconstructionCPU::RunChains() timerTotal.Start(); if (mProcessingSettings.doublePipeline) { - if (EnqueuePipeline()) { - return 1; + int32_t retVal = EnqueuePipeline(); + if (retVal) { + return retVal; } } else { if (mThreadId != GetThread()) { diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index be843b01610e8..224e7c720c334 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -89,7 +89,7 @@ AddOptionRTC(extraClusterErrorSplitPadSharedSingleY2, float, 0.03f, "", 0, "Addi AddOptionRTC(extraClusterErrorFactorSplitPadSharedSingleY2, float, 3.0f, "", 0, "Multiplicative extra cluster error for Y2 if splitpad, shared, or single set") AddOptionRTC(extraClusterErrorSplitTimeSharedSingleZ2, float, 0.03f, "", 0, "Additive extra cluster error for Z2 if splittime, shared, or single set") AddOptionRTC(extraClusterErrorFactorSplitTimeSharedSingleZ2, float, 3.0f, "", 0, "Multiplicative extra cluster error for Z2 if splittime, shared, or single set") -AddOptionArray(errorsCECrossing, float, 5, (0.f, 0.f, 0.f, 0.f, 0.f), "", 0, "Extra errors to add to track when crossing CE, depending on addErrorsCECrossing") // BUG: CUDA cannot yet hand AddOptionArrayRTC +AddOptionArray(errorsCECrossing, float, 5, (0.f, 0.f, 0.f, 0.f, 0.f), "", 0, "Extra errors to add to track when crossing CE, depending on addErrorsCECrossing") // BUG: CUDA cannot yet handle AddOptionArrayRTC AddOptionRTC(globalTrackingYRangeUpper, float, 0.85f, "", 0, "Inner portion of y-range in slice that is not used in searching for global track candidates") AddOptionRTC(globalTrackingYRangeLower, float, 0.85f, "", 0, "Inner portion of y-range in slice that is not used in searching for global track candidates") AddOptionRTC(trackFollowingYFactor, float, 4.f, "", 0, "Weight of y residual vs z residual in tracklet constructor") diff --git a/GPU/GPUTracking/Global/GPUChainTrackingDebugAndProfiling.cxx b/GPU/GPUTracking/Global/GPUChainTrackingDebugAndProfiling.cxx index 7a4aa73ae13d1..f8a64e9d4faaa 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingDebugAndProfiling.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingDebugAndProfiling.cxx @@ -302,7 +302,7 @@ void GPUChainTracking::SanityCheck() void GPUChainTracking::RunTPCClusterFilter(o2::tpc::ClusterNativeAccess* clusters, std::function allocator, bool applyClusterCuts) { GPUTPCClusterFilter clusterFilter(*clusters); - o2::tpc::ClusterNative* outputBuffer; + o2::tpc::ClusterNative* outputBuffer = nullptr; for (int32_t iPhase = 0; iPhase < 2; iPhase++) { uint32_t countTotal = 0; for (uint32_t iSector = 0; iSector < GPUCA_NSLICES; iSector++) { diff --git a/GPU/Workflow/src/GPUWorkflowSpec.cxx b/GPU/Workflow/src/GPUWorkflowSpec.cxx index f16082cb6c0da..4549d895c26b9 100644 --- a/GPU/Workflow/src/GPUWorkflowSpec.cxx +++ b/GPU/Workflow/src/GPUWorkflowSpec.cxx @@ -851,7 +851,7 @@ void GPURecoWorkflowSpec::run(ProcessingContext& pc) } createEmptyOutput = !mConfParam->partialOutputForNonFatalErrors; } else { - throw std::runtime_error("tracker returned error code " + std::to_string(retVal)); + throw std::runtime_error("GPU Reconstruction error: error code " + std::to_string(retVal)); } } From 8be37ef626f3ccae7c760884eef263cb39df123e Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 6 Nov 2024 14:54:13 +0100 Subject: [PATCH 0007/2180] TPC: Make number of lanes and threads for TPC IDC factorize configurable --- prodtests/full-system-test/aggregator-workflow.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/prodtests/full-system-test/aggregator-workflow.sh b/prodtests/full-system-test/aggregator-workflow.sh index 4e5f6f2a4c8ad..4c20e901a2978 100755 --- a/prodtests/full-system-test/aggregator-workflow.sh +++ b/prodtests/full-system-test/aggregator-workflow.sh @@ -295,7 +295,8 @@ fi # TPC IDCs and SAC crus="0-359" # to be used with $AGGREGATOR_TASKS == TPC_IDCBOTH_SAC or ALL -lanesFactorize=10 +lanesFactorize=${O2_TPC_IDC_FACTORIZE_NLANES:-10} +threadFactorize=${O2_TPC_IDC_FACTORIZE_NTHREADS:-8} nTFs=$((1000 * 128 / ${NHBPERTF})) nTFs_SAC=$((1000 * 128 / ${NHBPERTF})) nBuffer=$((100 * 128 / ${NHBPERTF})) @@ -309,7 +310,7 @@ if [[ "${DISABLE_IDC_PAD_MAP_WRITING:-}" == 1 ]]; then TPC_WRITING_PAD_STATUS_MA if ! workflow_has_parameter CALIB_LOCAL_INTEGRATED_AGGREGATOR; then if [[ $CALIB_TPC_IDC == 1 ]] && [[ $AGGREGATOR_TASKS == TPC_IDCBOTH_SAC || $AGGREGATOR_TASKS == ALL ]]; then add_W o2-tpc-idc-distribute "--crus ${crus} --timeframes ${nTFs} --output-lanes ${lanesFactorize} --send-precise-timestamp true --condition-tf-per-query ${nTFs} --n-TFs-buffer ${nBuffer}" - add_W o2-tpc-idc-factorize "--n-TFs-buffer ${nBuffer} --input-lanes ${lanesFactorize} --crus ${crus} --timeframes ${nTFs} --nthreads-grouping 8 --nthreads-IDC-factorization 8 --sendOutputFFT true --enable-CCDB-output true --enablePadStatusMap true ${TPC_WRITING_PAD_STATUS_MAP} --use-precise-timestamp true $IDC_DELTA" "TPCIDCGroupParam.groupPadsSectorEdges=32211" + add_W o2-tpc-idc-factorize "--n-TFs-buffer ${nBuffer} --input-lanes ${lanesFactorize} --crus ${crus} --timeframes ${nTFs} --nthreads-grouping ${threadFactorize} --nthreads-IDC-factorization ${threadFactorize} --sendOutputFFT true --enable-CCDB-output true --enablePadStatusMap true ${TPC_WRITING_PAD_STATUS_MAP} --use-precise-timestamp true $IDC_DELTA" "TPCIDCGroupParam.groupPadsSectorEdges=32211" add_W o2-tpc-idc-ft-aggregator "--rangeIDC 200 --inputLanes ${lanesFactorize} --nFourierCoeff 40 --nthreads 8" fi if [[ $CALIB_TPC_SAC == 1 ]] && [[ $AGGREGATOR_TASKS == TPC_IDCBOTH_SAC || $AGGREGATOR_TASKS == ALL ]]; then From f56b4f89779f67d3306c58ab661acb83e4d90dcc Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 7 Nov 2024 17:45:53 +0100 Subject: [PATCH 0008/2180] DPL: do not leak metadata file descriptor (#13663) --- Framework/AnalysisSupport/src/Plugin.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/Framework/AnalysisSupport/src/Plugin.cxx b/Framework/AnalysisSupport/src/Plugin.cxx index baa1b36dd41a5..32a86d37aebb9 100644 --- a/Framework/AnalysisSupport/src/Plugin.cxx +++ b/Framework/AnalysisSupport/src/Plugin.cxx @@ -142,6 +142,7 @@ struct DiscoverMetadataInAOD : o2::framework::ConfigDiscoveryPlugin { if (tables.empty() == false) { results.push_back(ConfigParamSpec{"aod-metadata-tables", VariantType::ArrayString, tables, {"Tables in first AOD"}}); } + currentFile->Close(); return results; }}; } From 09d925cca5402c41eebf10452add910202ac2ce4 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 7 Nov 2024 15:40:41 +0100 Subject: [PATCH 0009/2180] DPL: pass ConfigContext to the PluginManager when creating AlgorithmSpec This way a plugin can create more complex AlgorithmSpecs which depend on the workflow options. This will be needed to properly read metadata from parent files, and it opens the way to more service devices to be moved in a plugin. --- Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx | 3 ++- Framework/AnalysisSupport/src/AODJAlienReaderHelpers.h | 2 +- Framework/AnalysisSupport/src/Plugin.cxx | 4 ++-- Framework/CCDBSupport/src/Plugin.cxx | 2 +- Framework/Core/include/Framework/AlgorithmSpec.h | 2 +- Framework/Core/include/Framework/PluginManager.h | 5 +++-- Framework/Core/src/PluginManager.cxx | 6 +++--- Framework/Core/src/WorkflowHelpers.cxx | 4 ++-- 8 files changed, 15 insertions(+), 13 deletions(-) diff --git a/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx b/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx index a8b708668dae1..016ed4f1df1ef 100644 --- a/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx +++ b/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx @@ -22,6 +22,7 @@ #include "Framework/DeviceSpec.h" #include "Framework/RawDeviceService.h" #include "Framework/DataSpecUtils.h" +#include "Framework/ConfigContext.h" #include "DataInputDirector.h" #include "Framework/SourceInfoHeader.h" #include "Framework/ChannelInfo.h" @@ -117,7 +118,7 @@ static inline auto extractOriginalsTuple(framework::pack, ProcessingConte return std::make_tuple(extractTypedOriginal(pc)...); } -AlgorithmSpec AODJAlienReaderHelpers::rootFileReaderCallback() +AlgorithmSpec AODJAlienReaderHelpers::rootFileReaderCallback(ConfigContext const& config) { auto callback = AlgorithmSpec{adaptStateful([](ConfigParamRegistry const& options, DeviceSpec const& spec, diff --git a/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.h b/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.h index 4b9fd710aca14..e8d663d8fe0bb 100644 --- a/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.h +++ b/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.h @@ -24,7 +24,7 @@ namespace o2::framework::readers { struct AODJAlienReaderHelpers { - static AlgorithmSpec rootFileReaderCallback(); + static AlgorithmSpec rootFileReaderCallback(ConfigContext const&context); static void dumpFileMetrics(o2::monitoring::Monitoring& monitoring, TFile* currentFile, uint64_t startedAt, uint64_t ioTime, int tfPerFile, int tfRead); }; diff --git a/Framework/AnalysisSupport/src/Plugin.cxx b/Framework/AnalysisSupport/src/Plugin.cxx index 32a86d37aebb9..9ab4dfa0a2a9f 100644 --- a/Framework/AnalysisSupport/src/Plugin.cxx +++ b/Framework/AnalysisSupport/src/Plugin.cxx @@ -26,9 +26,9 @@ O2_DECLARE_DYNAMIC_LOG(analysis_support); struct ROOTFileReader : o2::framework::AlgorithmPlugin { - o2::framework::AlgorithmSpec create() override + o2::framework::AlgorithmSpec create(o2::framework::ConfigContext const& config) override { - return o2::framework::readers::AODJAlienReaderHelpers::rootFileReaderCallback(); + return o2::framework::readers::AODJAlienReaderHelpers::rootFileReaderCallback(config); } }; diff --git a/Framework/CCDBSupport/src/Plugin.cxx b/Framework/CCDBSupport/src/Plugin.cxx index 8769511f4849a..18aabc07ae4a4 100644 --- a/Framework/CCDBSupport/src/Plugin.cxx +++ b/Framework/CCDBSupport/src/Plugin.cxx @@ -13,7 +13,7 @@ #include "CCDBHelpers.h" struct CCDBFetcherPlugin : o2::framework::AlgorithmPlugin { - o2::framework::AlgorithmSpec create() final + o2::framework::AlgorithmSpec create(o2::framework::ConfigContext const&) final { return o2::framework::CCDBHelpers::fetchFromCCDB(); } diff --git a/Framework/Core/include/Framework/AlgorithmSpec.h b/Framework/Core/include/Framework/AlgorithmSpec.h index e08d829e489bd..7d56ba9f6ce68 100644 --- a/Framework/Core/include/Framework/AlgorithmSpec.h +++ b/Framework/Core/include/Framework/AlgorithmSpec.h @@ -81,7 +81,7 @@ struct AlgorithmSpec { /// Helper class for an algorithm which is loaded as a plugin. struct AlgorithmPlugin { - virtual AlgorithmSpec create() = 0; + virtual AlgorithmSpec create(ConfigContext const&) = 0; }; // Allow fetching inputs from the context using a string literal. template diff --git a/Framework/Core/include/Framework/PluginManager.h b/Framework/Core/include/Framework/PluginManager.h index 4c6e965502500..d6b16f01ad713 100644 --- a/Framework/Core/include/Framework/PluginManager.h +++ b/Framework/Core/include/Framework/PluginManager.h @@ -51,8 +51,9 @@ struct PluginManager { /// the DPLPluginHandle provided by the library. static void load(std::vector& infos, const char* dso, std::function& onSuccess); /// Load an called @plugin from a library called @a library and - /// return the associtated AlgorithmSpec. - static auto loadAlgorithmFromPlugin(std::string library, std::string plugin) -> AlgorithmSpec; + /// @return the associated AlgorithmSpec. + /// The config @a context can be used to determine the workflow options which affect such plugin. + static auto loadAlgorithmFromPlugin(std::string library, std::string plugin, ConfigContext const& context) -> AlgorithmSpec; /// Wrap an algorithm with some lambda @wrapper which will be called /// with the original callback and the ProcessingContext. static auto wrapAlgorithm(AlgorithmSpec const& spec, WrapperProcessCallback&& wrapper) -> AlgorithmSpec; diff --git a/Framework/Core/src/PluginManager.cxx b/Framework/Core/src/PluginManager.cxx index 96666722fc169..9faea85ad65e7 100644 --- a/Framework/Core/src/PluginManager.cxx +++ b/Framework/Core/src/PluginManager.cxx @@ -101,10 +101,10 @@ void PluginManager::load(std::vector& libs, const char* dso, std::fu onSuccess(pluginInstance); } -auto PluginManager::loadAlgorithmFromPlugin(std::string library, std::string plugin) -> AlgorithmSpec +auto PluginManager::loadAlgorithmFromPlugin(std::string library, std::string plugin, ConfigContext const& context) -> AlgorithmSpec { std::shared_ptr algorithm{nullptr}; - return AlgorithmSpec{[algorithm, library, plugin](InitContext& ic) mutable -> AlgorithmSpec::ProcessCallback { + return AlgorithmSpec{[algorithm, library, plugin, &context](InitContext& ic) mutable -> AlgorithmSpec::ProcessCallback { if (algorithm.get()) { return algorithm->onInit(ic); } @@ -134,7 +134,7 @@ auto PluginManager::loadAlgorithmFromPlugin(std::string library, std::string plu if (!creator) { LOGP(fatal, "Could not find the {} plugin in {}.", plugin, libName); } - algorithm = std::make_shared(creator->create()); + algorithm = std::make_shared(creator->create(context)); return algorithm->onInit(ic); }}; }; diff --git a/Framework/Core/src/WorkflowHelpers.cxx b/Framework/Core/src/WorkflowHelpers.cxx index 3fe8fae19a3b5..0366e39cf8976 100644 --- a/Framework/Core/src/WorkflowHelpers.cxx +++ b/Framework/Core/src/WorkflowHelpers.cxx @@ -432,7 +432,7 @@ 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"); + auto&& algo = PluginManager::loadAlgorithmFromPlugin("O2FrameworkAnalysisSupport", "ROOTFileReader", ctx); if (internalRateLimiting) { aodReader.algorithm = CommonDataProcessors::wrapWithRateLimiting(algo); } else { @@ -520,7 +520,7 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext } // Load the CCDB backend from the plugin - ccdbBackend.algorithm = PluginManager::loadAlgorithmFromPlugin("O2FrameworkCCDBSupport", "CCDBFetcherPlugin"); + 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 From 8f506c80c893200df1865321da165d4852159b01 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 7 Nov 2024 15:53:03 +0100 Subject: [PATCH 0010/2180] DPL: allow plugins to know about discoveries of other plugins --- Framework/Core/include/Framework/runDataProcessing.h | 1 - Framework/Core/src/ConfigParamDiscovery.cxx | 1 + 2 files changed, 1 insertion(+), 1 deletion(-) diff --git a/Framework/Core/include/Framework/runDataProcessing.h b/Framework/Core/include/Framework/runDataProcessing.h index fbf2843d4db01..eee4c4b6583d3 100644 --- a/Framework/Core/include/Framework/runDataProcessing.h +++ b/Framework/Core/include/Framework/runDataProcessing.h @@ -197,7 +197,6 @@ int mainNoCatch(int argc, char** argv) for (auto& extra : extraOptions) { workflowOptions.push_back(extra); } - workflowOptionsRegistry.loadExtra(extraOptions); ConfigContext configContext(workflowOptionsRegistry, argc, argv); o2::framework::WorkflowSpec specs = defineDataProcessing(configContext); diff --git a/Framework/Core/src/ConfigParamDiscovery.cxx b/Framework/Core/src/ConfigParamDiscovery.cxx index 9673f77ed0e42..63c38b7f0ac6c 100644 --- a/Framework/Core/src/ConfigParamDiscovery.cxx +++ b/Framework/Core/src/ConfigParamDiscovery.cxx @@ -75,6 +75,7 @@ std::vector ConfigParamDiscovery::discover(ConfigParamRegistry& for (auto& extra : extras) { result.push_back(extra); } + registry.loadExtra(extras); } return result; } From 2c1efe45eb36be258aef262bf771daf7a0188671 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 7 Nov 2024 15:53:21 +0100 Subject: [PATCH 0011/2180] DPL: read metadata from parent files If the metadata is not found in the main file and if there is a list of parent files, try those as well. --- .../src/AODJAlienReaderHelpers.cxx | 20 ++-- Framework/AnalysisSupport/src/Plugin.cxx | 102 +++++++++++++----- Framework/Core/src/ConfigParamDiscovery.cxx | 2 +- Framework/Core/src/Plugin.cxx | 9 +- Framework/Core/src/WorkflowHelpers.cxx | 1 - 5 files changed, 96 insertions(+), 38 deletions(-) diff --git a/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx b/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx index 016ed4f1df1ef..90d88cb43626e 100644 --- a/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx +++ b/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx @@ -120,10 +120,17 @@ static inline auto extractOriginalsTuple(framework::pack, ProcessingConte AlgorithmSpec AODJAlienReaderHelpers::rootFileReaderCallback(ConfigContext const& config) { - auto callback = AlgorithmSpec{adaptStateful([](ConfigParamRegistry const& options, - DeviceSpec const& spec, - Monitoring& monitoring, - DataProcessingStats& stats) { + // aod-parent-base-path-replacement is now a workflow option, so it needs to be + // retrieved from the ConfigContext. This is because we do not allow workflow options + // to change over start-stop-start because they can affect the topology generation. + std::string parentFileReplacement; + if (config.options().isSet("aod-parent-base-path-replacement")) { + parentFileReplacement = config.options().get("aod-parent-base-path-replacement"); + } + auto callback = AlgorithmSpec{adaptStateful([parentFileReplacement](ConfigParamRegistry const& options, + DeviceSpec const& spec, + Monitoring& monitoring, + DataProcessingStats& stats) { // FIXME: not actually needed, since data processing stats can specify that we should // send the initial value. stats.updateStats({static_cast(ProcessingStatsId::ARROW_BYTES_CREATED), DataProcessingStats::Op::Set, 0}); @@ -141,11 +148,6 @@ AlgorithmSpec AODJAlienReaderHelpers::rootFileReaderCallback(ConfigContext const auto maxRate = options.get("aod-max-io-rate"); - std::string parentFileReplacement; - if (options.isSet("aod-parent-base-path-replacement")) { - parentFileReplacement = options.get("aod-parent-base-path-replacement"); - } - int parentAccessLevel = 0; if (options.isSet("aod-parent-access-level")) { parentAccessLevel = options.get("aod-parent-access-level"); diff --git a/Framework/AnalysisSupport/src/Plugin.cxx b/Framework/AnalysisSupport/src/Plugin.cxx index 9ab4dfa0a2a9f..b899a52206422 100644 --- a/Framework/AnalysisSupport/src/Plugin.cxx +++ b/Framework/AnalysisSupport/src/Plugin.cxx @@ -22,6 +22,7 @@ #include #include #include +#include O2_DECLARE_DYNAMIC_LOG(analysis_support); @@ -65,7 +66,7 @@ struct RunSummary : o2::framework::ServicePlugin { } }; -std::vector getListOfTables(TFile* f) +std::vector getListOfTables(std::unique_ptr& f) { std::vector r; TList* keyList = f->GetListOfKeys(); @@ -83,6 +84,32 @@ std::vector getListOfTables(TFile* f) } return r; } +auto readMetadata(std::unique_ptr& currentFile) -> std::vector +{ + // Get the metadata, if any + auto m = (TMap*)currentFile->Get("metaData"); + if (!m) { + return {}; + } + std::vector results; + auto it = m->MakeIterator(); + + // Serialise metadata into a ; separated string with : separating key and value + bool first = true; + while (auto obj = it->Next()) { + if (first) { + LOGP(info, "Metadata for file \"{}\":", currentFile->GetName()); + first = false; + } + auto objString = (TObjString*)m->GetValue(obj); + LOGP(info, "- {}: {}", obj->GetName(), objString->String().Data()); + std::string key = "aod-metadata-" + std::string(obj->GetName()); + char const* value = strdup(objString->String()); + results.push_back(ConfigParamSpec{key, VariantType::String, value, {"Metadata in AOD"}}); + } + + return results; +} struct DiscoverMetadataInAOD : o2::framework::ConfigDiscoveryPlugin { ConfigDiscovery* create() override @@ -94,8 +121,6 @@ struct DiscoverMetadataInAOD : o2::framework::ConfigDiscoveryPlugin { if (filename.empty()) { return {}; } - std::vector results; - TFile* currentFile = nullptr; if (filename.at(0) == '@') { filename.erase(0, 1); // read the text file and set filename to the contents of the first line @@ -110,39 +135,64 @@ struct DiscoverMetadataInAOD : o2::framework::ConfigDiscoveryPlugin { TGrid::Connect("alien://"); } LOGP(info, "Loading metadata from file {} in PID {}", filename, getpid()); - currentFile = TFile::Open(filename.c_str()); - if (!currentFile) { + std::unique_ptr currentFile{TFile::Open(filename.c_str())}; + if (currentFile.get() == nullptr) { LOGP(fatal, "Couldn't open file \"{}\"!", filename); } + std::vector results = readMetadata(currentFile); + // Found metadata already in the main file. + if (!results.empty()) { + auto tables = getListOfTables(currentFile); + if (tables.empty() == false) { + results.push_back(ConfigParamSpec{"aod-metadata-tables", VariantType::ArrayString, tables, {"Tables in first AOD"}}); + } + results.push_back(ConfigParamSpec{"aod-metadata-source", VariantType::String, filename, {"File from which the metadata was extracted."}}); + return results; + } - // Get the metadata, if any - auto m = (TMap*)currentFile->Get("metaData"); - if (!m) { + // Lets try in parent files + auto parentFiles = (TMap*)currentFile->Get("parentFiles"); + if (!parentFiles) { LOGP(info, "No metadata found in file \"{}\"", filename); results.push_back(ConfigParamSpec{"aod-metadata-disable", VariantType::String, "1", {"Metadata not found in AOD"}}); return results; } - auto it = m->MakeIterator(); - - // Serialise metadata into a ; separated string with : separating key and value - bool first = true; - while (auto obj = it->Next()) { - if (first) { - LOGP(info, "Metadata for file \"{}\":", filename); - first = false; + for (auto* p : *parentFiles) { + std::string parentFilename = ((TPair*)p)->Value()->GetName(); + // Do the replacement. Notice this will require changing aod-parent-base-path-replacement to be + // a workflow option (because the metadata itself is potentially changing the topology). + if (registry.isSet("aod-parent-base-path-replacement")) { + auto parentFileReplacement = registry.get("aod-parent-base-path-replacement"); + auto pos = parentFileReplacement.find(';'); + if (pos == std::string::npos) { + throw std::runtime_error(fmt::format("Invalid syntax in aod-parent-base-path-replacement: \"{}\"", parentFileReplacement.c_str())); + } + auto from = parentFileReplacement.substr(0, pos); + auto to = parentFileReplacement.substr(pos + 1); + pos = parentFilename.find(from); + if (pos != std::string::npos) { + parentFilename.replace(pos, from.length(), to); + } } - auto objString = (TObjString*)m->GetValue(obj); - LOGP(info, "- {}: {}", obj->GetName(), objString->String().Data()); - std::string key = "aod-metadata-" + std::string(obj->GetName()); - char const* value = strdup(objString->String()); - results.push_back(ConfigParamSpec{key, VariantType::String, value, {"Metadata in AOD"}}); - } - auto tables = getListOfTables(currentFile); - if (tables.empty() == false) { - results.push_back(ConfigParamSpec{"aod-metadata-tables", VariantType::ArrayString, tables, {"Tables in first AOD"}}); + std::unique_ptr parentFile{TFile::Open(parentFilename.c_str())}; + if (parentFile.get() == nullptr) { + LOGP(fatal, "Couldn't open derived file \"{}\"!", parentFilename); + } + results = readMetadata(parentFile); + // Found metadata already in the main file. + if (!results.empty()) { + auto tables = getListOfTables(parentFile); + if (tables.empty() == false) { + results.push_back(ConfigParamSpec{"aod-metadata-tables", VariantType::ArrayString, tables, {"Tables in first AOD"}}); + } + results.push_back(ConfigParamSpec{"aod-metadata-source", VariantType::String, filename, {"File from which the metadata was extracted."}}); + return results; + } + LOGP(info, "No metadata found in file \"{}\" nor in its parent file \"{}\"", filename, parentFilename); + break; } - currentFile->Close(); + results.push_back(ConfigParamSpec{"aod-metadata-disable", VariantType::String, "1", {"Metadata not found in AOD"}}); return results; }}; } diff --git a/Framework/Core/src/ConfigParamDiscovery.cxx b/Framework/Core/src/ConfigParamDiscovery.cxx index 63c38b7f0ac6c..fc8d6f2600bb4 100644 --- a/Framework/Core/src/ConfigParamDiscovery.cxx +++ b/Framework/Core/src/ConfigParamDiscovery.cxx @@ -25,9 +25,9 @@ namespace o2::framework std::vector ConfigParamDiscovery::discover(ConfigParamRegistry& registry, int argc, char** argv) { std::vector capabilitiesSpecs = { + "O2Framework:DiscoverAODOptionsInCommandLineCapability", "O2Framework:DiscoverMetadataInAODCapability", "O2Framework:DiscoverMetadataInCommandLineCapability", - "O2Framework:DiscoverAODOptionsInCommandLineCapability", }; // Load all the requested plugins and discover what we can do. diff --git a/Framework/Core/src/Plugin.cxx b/Framework/Core/src/Plugin.cxx index a98771a913d01..91c74bafff5ad 100644 --- a/Framework/Core/src/Plugin.cxx +++ b/Framework/Core/src/Plugin.cxx @@ -57,6 +57,10 @@ auto lookForCommandLineAODOptions = [](ConfigParamRegistry& registry, int argc, O2_SIGNPOST_EVENT_EMIT(capabilities, sid, "DiscoverAODOptionsInCommandLineCapability", "AOD options found in arguments. Populating from them."); return true; } + if (arg.starts_with("--aod-parent-base-path-replacement")) { + O2_SIGNPOST_EVENT_EMIT(capabilities, sid, "DiscoverAODOptionsInCommandLineCapability", "AOD options found in arguments. Populating from them."); + return true; + } } return false; }; @@ -137,7 +141,7 @@ struct DiscoverAODOptionsInCommandLine : o2::framework::ConfigDiscoveryPlugin { bool injectOption = true; for (size_t i = 0; i < argc; i++) { std::string_view arg = argv[i]; - if (!arg.starts_with("--aod-writer-")) { + if (!arg.starts_with("--aod-writer-") && arg != "--aod-parent-base-path-replacement") { continue; } std::string key = arg.data() + 2; @@ -149,6 +153,9 @@ struct DiscoverAODOptionsInCommandLine : o2::framework::ConfigDiscoveryPlugin { results.push_back(ConfigParamSpec{"aod-writer-compression", VariantType::Int, numericValue, {"AOD Compression options"}}); injectOption = false; } + if (key == "aod-parent-base-path-replacement") { + results.push_back(ConfigParamSpec{"aod-parent-base-path-replacement", VariantType::String, value, {R"(Replace base path of parent files. Syntax: FROM;TO. E.g. "alien:///path/in/alien;/local/path". Enclose in "" on the command line.)"}}); + } } if (injectOption) { results.push_back(ConfigParamSpec{"aod-writer-compression", VariantType::Int, 505, {"AOD Compression options"}}); diff --git a/Framework/Core/src/WorkflowHelpers.cxx b/Framework/Core/src/WorkflowHelpers.cxx index 0366e39cf8976..56e9930e3b655 100644 --- a/Framework/Core/src/WorkflowHelpers.cxx +++ b/Framework/Core/src/WorkflowHelpers.cxx @@ -217,7 +217,6 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext ConfigParamSpec{"aod-max-io-rate", VariantType::Float, 0.f, {"Maximum I/O rate in MB/s"}}, ConfigParamSpec{"aod-reader-json", VariantType::String, {"json configuration file"}}, ConfigParamSpec{"aod-parent-access-level", VariantType::String, {"Allow parent file access up to specified level. Default: no (0)"}}, - ConfigParamSpec{"aod-parent-base-path-replacement", VariantType::String, {R"(Replace base path of parent files. Syntax: FROM;TO. E.g. "alien:///path/in/alien;/local/path". Enclose in "" on the command line.)"}}, ConfigParamSpec{"time-limit", VariantType::Int64, 0ll, {"Maximum run time limit in seconds"}}, ConfigParamSpec{"orbit-offset-enumeration", VariantType::Int64, 0ll, {"initial value for the orbit"}}, ConfigParamSpec{"orbit-multiplier-enumeration", VariantType::Int64, 0ll, {"multiplier to get the orbit from the counter"}}, From 71711a52b76dc181f1b62ac525054246da12d896 Mon Sep 17 00:00:00 2001 From: swenzel Date: Thu, 20 Jun 2024 17:07:43 +0200 Subject: [PATCH 0012/2180] Improvements for CollisionContextTool * important step to create collision contexts for all timeframes in one step - complete digi context is created - individual tf-collisioncontexts can be extracted * needed to have "pregencollcontext" in O2DPG work with embedding * smaller fixes (firstOrbit) --- .../DigitizationContext.h | 12 +- .../simulation/src/DigitizationContext.cxx | 105 +++++++++++++++++- Steer/src/CollisionContextTool.cxx | 81 +++++++++++++- 3 files changed, 187 insertions(+), 11 deletions(-) diff --git a/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h b/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h index de8d89e6b1b72..4149b32683060 100644 --- a/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h +++ b/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h @@ -113,6 +113,11 @@ class DigitizationContext /// Check collision parts for vertex consistency. bool checkVertexCompatibility(bool verbose = false) const; + /// retrieves collision context for a single timeframe-id (which may be needed by simulation) + /// (Only copies collision context without QED information. This can be added to the result with the fillQED method + /// in a second step. As a pre-condition, one should have called finalizeTimeframeStructure) + DigitizationContext extractSingleTimeframe(int timeframeid, std::vector const& sources_to_offset); + /// function reading the hits from a chain (previously initialized with initSimChains /// The hits pointer will be initialized (what to we do about ownership??) template @@ -128,8 +133,9 @@ class DigitizationContext // apply collision number cuts and potential relabeling of eventID void applyMaxCollisionFilter(long startOrbit, long orbitsPerTF, int maxColl); - // finalize timeframe structure (fixes the indices in mTimeFrameStartIndex) - void finalizeTimeframeStructure(long startOrbit, long orbitsPerTF); + /// finalize timeframe structure (fixes the indices in mTimeFrameStartIndex) + // returns the number of timeframes + int finalizeTimeframeStructure(long startOrbit, long orbitsPerTF); // Sample and fix interaction vertices (according to some distribution). Makes sure that same event ids // have to have same vertex, as well as event ids associated to same collision. @@ -173,7 +179,7 @@ class DigitizationContext // for each collision we may record/fix the interaction vertex (to be used in event generation) std::vector> mInteractionVertices; - // the collision records _with_ QED interleaved; + // the collision records **with** QED interleaved; std::vector mEventRecordsWithQED; std::vector> mEventPartsWithQED; diff --git a/DataFormats/simulation/src/DigitizationContext.cxx b/DataFormats/simulation/src/DigitizationContext.cxx index f3f40d77042a5..ba1fda53e179b 100644 --- a/DataFormats/simulation/src/DigitizationContext.cxx +++ b/DataFormats/simulation/src/DigitizationContext.cxx @@ -19,6 +19,7 @@ #include // for iota #include #include +#include using namespace o2::steer; @@ -196,10 +197,52 @@ o2::parameters::GRPObject const& DigitizationContext::getGRP() const void DigitizationContext::saveToFile(std::string_view filename) const { + // checks if the path content of filename exists ... otherwise it is created before creating the ROOT file + auto ensure_path_exists = [](std::string_view filename) { + try { + // Extract the directory path from the filename + std::filesystem::path file_path(filename); + std::filesystem::path dir_path = file_path.parent_path(); + + // Check if the directory path is empty (which means filename was just a name without path) + if (dir_path.empty()) { + // nothing to do + return true; + } + + // Create directories if they do not exist + if (!std::filesystem::exists(dir_path)) { + if (std::filesystem::create_directories(dir_path)) { + // std::cout << "Directories created successfully: " << dir_path.string() << std::endl; + return true; + } else { + std::cerr << "Failed to create directories: " << dir_path.string() << std::endl; + return false; + } + } + return true; + } catch (const std::filesystem::filesystem_error& ex) { + std::cerr << "Filesystem error: " << ex.what() << std::endl; + return false; + } catch (const std::exception& ex) { + std::cerr << "General error: " << ex.what() << std::endl; + return false; + } + }; + + if (!ensure_path_exists(filename)) { + LOG(error) << "Filename contains path component which could not be created"; + return; + } + TFile file(filename.data(), "RECREATE"); - auto cl = TClass::GetClass(typeid(*this)); - file.WriteObjectAny(this, cl, "DigitizationContext"); - file.Close(); + if (file.IsOpen()) { + auto cl = TClass::GetClass(typeid(*this)); + file.WriteObjectAny(this, cl, "DigitizationContext"); + file.Close(); + } else { + LOG(error) << "Could not write to file " << filename.data(); + } } DigitizationContext* DigitizationContext::loadFromFile(std::string_view filename) @@ -391,13 +434,15 @@ void DigitizationContext::applyMaxCollisionFilter(long startOrbit, long orbitsPe mEventParts = newparts; } -void DigitizationContext::finalizeTimeframeStructure(long startOrbit, long orbitsPerTF) +int DigitizationContext::finalizeTimeframeStructure(long startOrbit, long orbitsPerTF) { mTimeFrameStartIndex = getTimeFrameBoundaries(mEventRecords, startOrbit, orbitsPerTF); LOG(info) << "Fixed " << mTimeFrameStartIndex.size() << " timeframes "; for (auto p : mTimeFrameStartIndex) { LOG(info) << p.first << " " << p.second; } + + return mTimeFrameStartIndex.size(); } std::unordered_map DigitizationContext::getCollisionIndicesForSource(int source) const @@ -483,3 +528,55 @@ void DigitizationContext::sampleInteractionVertices(o2::dataformats::MeanVertexO } } } + +DigitizationContext DigitizationContext::extractSingleTimeframe(int timeframeid, std::vector const& sources_to_offset) +{ + DigitizationContext r; // make a return object + if (mTimeFrameStartIndex.size() == 0) { + LOG(error) << "No timeframe structure determined; Returning empty object. Please call ::finalizeTimeframeStructure before calling this function"; + return r; + } + r.mSimPrefixes = mSimPrefixes; + r.mMuBC = mMuBC; + try { + auto startend = mTimeFrameStartIndex.at(timeframeid); + + auto startindex = startend.first; + auto endindex = startend.second; + + std::copy(mEventRecords.begin() + startindex, mEventRecords.begin() + endindex, std::back_inserter(r.mEventRecords)); + std::copy(mEventParts.begin() + startindex, mEventParts.begin() + endindex, std::back_inserter(r.mEventParts)); + if (mInteractionVertices.size() > endindex) { + std::copy(mInteractionVertices.begin() + startindex, mInteractionVertices.begin() + endindex, std::back_inserter(r.mInteractionVertices)); + } + + // let's assume we want to fix the ids for source = source_id + // Then we find the first index that has this source_id and take the corresponding number + // as offset. Thereafter we subtract this offset from all known event parts. + auto perform_offsetting = [&r](int source_id) { + auto indices_for_source = r.getCollisionIndicesForSource(source_id); + int minvalue = std::numeric_limits::max(); + for (auto& p : indices_for_source) { + if (p.first < minvalue) { + minvalue = p.first; + } + } + // now fix them + for (auto& p : indices_for_source) { + auto index_into_mEventParts = p.second; + for (auto& part : r.mEventParts[index_into_mEventParts]) { + if (part.sourceID == source_id) { + part.entryID -= minvalue; + } + } + } + }; + for (auto source_id : sources_to_offset) { + perform_offsetting(source_id); + } + + } catch (std::exception) { + LOG(warn) << "No such timeframe id in collision context. Returing empty object"; + } + return r; +} diff --git a/Steer/src/CollisionContextTool.cxx b/Steer/src/CollisionContextTool.cxx index f94fde22ef8ac..af2f607b88774 100644 --- a/Steer/src/CollisionContextTool.cxx +++ b/Steer/src/CollisionContextTool.cxx @@ -55,6 +55,8 @@ struct Options { bool genVertices = false; // whether to assign vertices to collisions std::string configKeyValues = ""; // string to init config key values long timestamp = -1; // timestamp for CCDB queries + std::string individualTFextraction = ""; // triggers extraction of individuel timeframe components when non-null + // format is path prefix }; enum class InteractionLockMode { @@ -200,7 +202,10 @@ bool parseOptions(int argc, char* argv[], Options& optvalues) "timeframeID", bpo::value(&optvalues.tfid)->default_value(0), "Timeframe id of the first timeframe int this context. Allows to generate contexts for different start orbits")( "first-orbit", bpo::value(&optvalues.firstFractionalOrbit)->default_value(0), "First (fractional) orbit in the run (HBFUtils.firstOrbit + BC from decimal)")( "maxCollsPerTF", bpo::value(&optvalues.maxCollsPerTF)->default_value(-1), "Maximal number of MC collisions to put into one timeframe. By default no constraint.")( - "noEmptyTF", bpo::bool_switch(&optvalues.noEmptyTF), "Enforce to have at least one collision")("configKeyValues", bpo::value(&optvalues.configKeyValues)->default_value(""), "Semicolon separated key=value strings (e.g.: 'TPC.gasDensity=1;...')")("with-vertices", "Assign vertices to collisions.")("timestamp", bpo::value(&optvalues.timestamp)->default_value(-1L), "Timestamp for CCDB queries / anchoring"); + "noEmptyTF", bpo::bool_switch(&optvalues.noEmptyTF), "Enforce to have at least one collision")( + "configKeyValues", bpo::value(&optvalues.configKeyValues)->default_value(""), "Semicolon separated key=value strings (e.g.: 'TPC.gasDensity=1;...')")("with-vertices", "Assign vertices to collisions.")("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]"); options.add_options()("help,h", "Produce help message."); @@ -283,6 +288,8 @@ int main(int argc, char* argv[]) } }; + auto orbitstart = options.firstOrbit + options.tfid * options.orbitsPerTF; + for (int id = 0; id < ispecs.size(); ++id) { auto mode = ispecs[id].syncmode; if (mode == InteractionLockMode::NOLOCK) { @@ -291,7 +298,6 @@ int main(int argc, char* argv[]) if (!options.bcpatternfile.empty()) { setBCFillingHelper(sampler, options.bcpatternfile); } - auto orbitstart = options.firstOrbit + options.tfid * options.orbitsPerTF; o2::InteractionTimeRecord record; // this loop makes sure that the first collision is within the range of orbits asked (if noEmptyTF is enabled) do { @@ -439,9 +445,9 @@ int main(int argc, char* argv[]) digicontext.setSimPrefixes(prefixes); // apply max collision per timeframe filters + reindexing of event id (linearisation and compactification) - digicontext.applyMaxCollisionFilter(options.tfid * options.orbitsPerTF, options.orbitsPerTF, options.maxCollsPerTF); + digicontext.applyMaxCollisionFilter(orbitstart, options.orbitsPerTF, options.maxCollsPerTF); - digicontext.finalizeTimeframeStructure(options.tfid * options.orbitsPerTF, options.orbitsPerTF); + auto numTimeFrames = digicontext.finalizeTimeframeStructure(orbitstart, options.orbitsPerTF); if (options.genVertices) { // TODO: offer option taking meanVertex directly from CCDB ! "GLO/Calib/MeanVertex" @@ -466,5 +472,72 @@ int main(int argc, char* argv[]) } digicontext.saveToFile(options.outfilename); + // extract individual timeframes + if (options.individualTFextraction.size() > 0) { + // we are asked to extract individual timeframe components + + LOG(info) << "Extracting individual timeframe collision contexts"; + // extract prefix path to store these collision contexts + // Function to check the pattern and extract tokens from b + auto check_and_extract_tokens = [](const std::string& input, std::vector& tokens) { + // the regular expression pattern for expected input format + const std::regex pattern(R"(^([a-zA-Z0-9]+)(:([a-zA-Z0-9]+(,[a-zA-Z0-9]+)*))?$)"); + std::smatch matches; + + // Check if the input matches the pattern + if (std::regex_match(input, matches, pattern)) { + // Clear any existing tokens in the vector + tokens.clear(); + + // matches[1] contains the part before the colon which we save first + tokens.push_back(matches[1].str()); + // matches[2] contains the comma-separated list + std::string b = matches[2].str(); + std::regex token_pattern(R"([a-zA-Z0-9]+)"); + auto tokens_begin = std::sregex_iterator(b.begin(), b.end(), token_pattern); + auto tokens_end = std::sregex_iterator(); + + // Iterate over the tokens and add them to the vector + for (std::sregex_iterator i = tokens_begin; i != tokens_end; ++i) { + tokens.push_back((*i).str()); + } + return true; + } + LOG(error) << "Argument for --extract-per-timeframe does not match specification"; + return false; + }; + + std::vector tokens; + if (check_and_extract_tokens(options.individualTFextraction, tokens)) { + auto path_prefix = tokens[0]; + std::vector sources_to_offset{}; + + LOG(info) << "PREFIX is " << path_prefix; + + for (int i = 1; i < tokens.size(); ++i) { + LOG(info) << "Offsetting " << tokens[i]; + sources_to_offset.push_back(digicontext.findSimPrefix(tokens[i])); + } + + // now we are ready to loop over all timeframes + for (int tf_id = 0; tf_id < numTimeFrames; ++tf_id) { + auto copy = digicontext.extractSingleTimeframe(tf_id, sources_to_offset); + + // each individual case gets QED interactions injected + // This should probably be done inside the extraction itself + if (digicontext.isQEDProvided()) { + auto qedSpec = parseInteractionSpec(options.qedInteraction, ispecs, options.useexistingkinematics); + copy.fillQED(qedSpec.name, qedSpec.mcnumberasked, qedSpec.interactionRate); + } + + std::stringstream str; + str << path_prefix << (tf_id + 1) << "/collisioncontext.root"; + copy.saveToFile(str.str()); + LOG(info) << "----"; + copy.printCollisionSummary(options.qedInteraction.size() > 0); + } + } + } + return 0; } From 2a8c9c0c5b39994a598bc7c5ea61dd381ac1b7b9 Mon Sep 17 00:00:00 2001 From: shahoian Date: Thu, 7 Nov 2024 16:30:10 +0100 Subject: [PATCH 0013/2180] Add TPC-only DCA to trackStudy output --- .../GlobalTrackingStudy/TrackInfoExt.h | 1 + .../study/src/TrackingStudy.cxx | 20 +++++++++++++++++++ 2 files changed, 21 insertions(+) diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackInfoExt.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackInfoExt.h index 754c08388abdb..b988eddfa861f 100644 --- a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackInfoExt.h +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackInfoExt.h @@ -27,6 +27,7 @@ namespace dataformats struct TrackInfoExt { o2::track::TrackParCov track; DCA dca{}; + DCA dcaTPC{}; VtxTrackIndex gid; MatchInfoTOF infoTOF; float ttime = 0; diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx index 378d2b9dcfacc..1e605e308f4ab 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx @@ -364,10 +364,30 @@ void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) continue; } { + o2::dataformats::DCA dcaTPC; + dcaTPC.set(-999.f, -999.f); + if (tpcTr) { + if (is == GTrackID::TPC) { + dcaTPC = dca; + } else { + o2::track::TrackParCov tmpTPC(*tpcTr); + if (iv < nv - 1 && is == GTrackID::TPC && tpcTr && !tpcTr->hasBothSidesClusters()) { // for unconstrained TPC tracks correct track Z + float corz = vdrit * (tpcTr->getTime0() * mTPCTBinMUS - pvvec[iv].getTimeStamp().getTimeStamp()); + if (tpcTr->hasASideClustersOnly()) { + corz = -corz; // A-side + } + tmpTPC.setZ(tmpTPC.getZ() + corz); + } + if (!prop->propagateToDCA(iv == nv - 1 ? vtxDummy : pvvec[iv], tmpTPC, prop->getNominalBz(), 2., o2::base::PropagatorF::MatCorrType::USEMatCorrLUT, &dcaTPC)) { + dcaTPC.set(-999.f, -999.f); + } + } + } auto& trcExt = trcExtVec.emplace_back(); recoData.getTrackTime(vid, trcExt.ttime, trcExt.ttimeE); trcExt.track = trc; trcExt.dca = dca; + trcExt.dcaTPC = dcaTPC; trcExt.gid = vid; trcExt.xmin = xmin; auto gidRefs = recoData.getSingleDetectorRefs(vid); From b345d0fd3ff81fe854bc96ea881c3bacf54c2275 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 7 Nov 2024 21:43:29 +0100 Subject: [PATCH 0014/2180] DPL: drop need for has_root_setowner Simple inline constrain is much better. --- Framework/Core/include/Framework/DataRefUtils.h | 4 ++-- Framework/Core/include/Framework/TypeTraits.h | 17 ----------------- 2 files changed, 2 insertions(+), 19 deletions(-) diff --git a/Framework/Core/include/Framework/DataRefUtils.h b/Framework/Core/include/Framework/DataRefUtils.h index c63b06357b8ed..4c1bd0ed7ed10 100644 --- a/Framework/Core/include/Framework/DataRefUtils.h +++ b/Framework/Core/include/Framework/DataRefUtils.h @@ -122,7 +122,7 @@ struct DataRefUtils { // object only depends on the state at serialization of the original object. However, // all objects created during deserialization are new and must be owned by the collection // to avoid memory leak. So we call SetOwner if it is available for the type. - if constexpr (has_root_setowner::value) { + if constexpr (requires(T t) { t.SetOwner(true); }) { result->SetOwner(true); } }); @@ -159,7 +159,7 @@ struct DataRefUtils { throw runtime_error_f("Unable to extract class %s", cl == nullptr ? "" : cl->GetName()); } // workaround for ROOT feature, see above - if constexpr (has_root_setowner::value) { + if constexpr (requires(T t) { t.SetOwner(true); }) { result->SetOwner(true); } }); diff --git a/Framework/Core/include/Framework/TypeTraits.h b/Framework/Core/include/Framework/TypeTraits.h index 19ca548835cdd..faa9055de3280 100644 --- a/Framework/Core/include/Framework/TypeTraits.h +++ b/Framework/Core/include/Framework/TypeTraits.h @@ -147,22 +147,5 @@ class has_root_dictionary::value>::ty { }; -// Detect whether a class is a ROOT class implementing SetOwner -// This member detector idiom is implemented using SFINAE idiom to look for -// a 'SetOwner()' method. -template -struct has_root_setowner : std::false_type { -}; - -template -struct has_root_setowner< - T, - std::conditional_t< - false, - class_member_checker< - decltype(std::declval().SetOwner(true))>, - void>> : public std::true_type { -}; - } // namespace o2::framework #endif // FRAMEWORK_TYPETRAITS_H From d2c347f1fa6abb1339d6bffc93dbd6c75ce9c657 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Fri, 8 Nov 2024 09:57:25 +0100 Subject: [PATCH 0015/2180] DPL Analysis: fix missing connection to the grid It's not a given that parent files and the original data are on the same support, so we need to connect to the grid if needed. --- Framework/AnalysisSupport/src/Plugin.cxx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/Framework/AnalysisSupport/src/Plugin.cxx b/Framework/AnalysisSupport/src/Plugin.cxx index b899a52206422..e2a9a98ab90d3 100644 --- a/Framework/AnalysisSupport/src/Plugin.cxx +++ b/Framework/AnalysisSupport/src/Plugin.cxx @@ -175,6 +175,10 @@ struct DiscoverMetadataInAOD : o2::framework::ConfigDiscoveryPlugin { } } + if (parentFilename.starts_with("alien://")) { + TGrid::Connect("alien://"); + } + std::unique_ptr parentFile{TFile::Open(parentFilename.c_str())}; if (parentFile.get() == nullptr) { LOGP(fatal, "Couldn't open derived file \"{}\"!", parentFilename); From e00fdebb99859c127906a7aea1eac61d6b15607c Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 7 Nov 2024 22:53:19 +0100 Subject: [PATCH 0016/2180] TPC: Fix invalid template keyword with argument list (clang19 complains, remove since not really needed) --- GPU/TPCFastTransformation/Spline1DSpec.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/TPCFastTransformation/Spline1DSpec.h b/GPU/TPCFastTransformation/Spline1DSpec.h index e37ee67581c63..f8af1980d81ae 100644 --- a/GPU/TPCFastTransformation/Spline1DSpec.h +++ b/GPU/TPCFastTransformation/Spline1DSpec.h @@ -448,7 +448,7 @@ class Spline1DSpec GPUgeneric() const T Sr[/*mYdim*/], GPUgeneric() const T Dr[/*mYdim*/], DataT u, GPUgeneric() T S[/*mYdim*/]) const { - TBase::template interpolateU(YdimT, knotL, Sl, Dl, Sr, Dr, u, S); + TBase::interpolateU(YdimT, knotL, Sl, Dl, Sr, Dr, u, S); } using TBase::getNumberOfKnots; From 2ab8d92ee0da7c549814e79d3027177052758221 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Fri, 8 Nov 2024 11:51:34 +0100 Subject: [PATCH 0017/2180] DPL: account for aod-parent-access-level == 0 In the case of self contained derived data, it is ok not to look in the parents for missing metadata. --- .../src/AODJAlienReaderHelpers.cxx | 23 +++++++++---------- Framework/AnalysisSupport/src/Plugin.cxx | 9 +++++++- Framework/Core/src/Plugin.cxx | 5 +++- Framework/Core/src/WorkflowHelpers.cxx | 1 - 4 files changed, 23 insertions(+), 15 deletions(-) diff --git a/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx b/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx index 90d88cb43626e..9c19de85739ce 100644 --- a/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx +++ b/Framework/AnalysisSupport/src/AODJAlienReaderHelpers.cxx @@ -118,19 +118,23 @@ static inline auto extractOriginalsTuple(framework::pack, ProcessingConte return std::make_tuple(extractTypedOriginal(pc)...); } -AlgorithmSpec AODJAlienReaderHelpers::rootFileReaderCallback(ConfigContext const& config) +AlgorithmSpec AODJAlienReaderHelpers::rootFileReaderCallback(ConfigContext const& ctx) { // aod-parent-base-path-replacement is now a workflow option, so it needs to be // retrieved from the ConfigContext. This is because we do not allow workflow options // to change over start-stop-start because they can affect the topology generation. std::string parentFileReplacement; - if (config.options().isSet("aod-parent-base-path-replacement")) { - parentFileReplacement = config.options().get("aod-parent-base-path-replacement"); + if (ctx.options().isSet("aod-parent-base-path-replacement")) { + parentFileReplacement = ctx.options().get("aod-parent-base-path-replacement"); } - auto callback = AlgorithmSpec{adaptStateful([parentFileReplacement](ConfigParamRegistry const& options, - DeviceSpec const& spec, - Monitoring& monitoring, - DataProcessingStats& stats) { + int parentAccessLevel = 0; + if (ctx.options().isSet("aod-parent-access-level")) { + parentAccessLevel = ctx.options().get("aod-parent-access-level"); + } + auto callback = AlgorithmSpec{adaptStateful([parentFileReplacement, parentAccessLevel](ConfigParamRegistry const& options, + DeviceSpec const& spec, + Monitoring& monitoring, + DataProcessingStats& stats) { // FIXME: not actually needed, since data processing stats can specify that we should // send the initial value. stats.updateStats({static_cast(ProcessingStatsId::ARROW_BYTES_CREATED), DataProcessingStats::Op::Set, 0}); @@ -148,11 +152,6 @@ AlgorithmSpec AODJAlienReaderHelpers::rootFileReaderCallback(ConfigContext const auto maxRate = options.get("aod-max-io-rate"); - int parentAccessLevel = 0; - if (options.isSet("aod-parent-access-level")) { - parentAccessLevel = options.get("aod-parent-access-level"); - } - // create a DataInputDirector auto didir = std::make_shared(filename, &monitoring, parentAccessLevel, parentFileReplacement); if (options.isSet("aod-reader-json")) { diff --git a/Framework/AnalysisSupport/src/Plugin.cxx b/Framework/AnalysisSupport/src/Plugin.cxx index e2a9a98ab90d3..bba3499286e08 100644 --- a/Framework/AnalysisSupport/src/Plugin.cxx +++ b/Framework/AnalysisSupport/src/Plugin.cxx @@ -150,13 +150,20 @@ struct DiscoverMetadataInAOD : o2::framework::ConfigDiscoveryPlugin { return results; } - // Lets try in parent files + if (!registry.isSet("aod-parent-access-level") || registry.get("aod-parent-access-level") == 0) { + LOGP(info, "No metadata found in file \"{}\" and parent level 0 prevents further lookup.", filename); + results.push_back(ConfigParamSpec{"aod-metadata-disable", VariantType::String, "1", {"Metadata not found in AOD"}}); + return results; + } + + // Lets try in parent file. auto parentFiles = (TMap*)currentFile->Get("parentFiles"); if (!parentFiles) { LOGP(info, "No metadata found in file \"{}\"", filename); results.push_back(ConfigParamSpec{"aod-metadata-disable", VariantType::String, "1", {"Metadata not found in AOD"}}); return results; } + LOGP(info, "No metadata found in file \"{}\", checking in its parents.", filename); for (auto* p : *parentFiles) { std::string parentFilename = ((TPair*)p)->Value()->GetName(); // Do the replacement. Notice this will require changing aod-parent-base-path-replacement to be diff --git a/Framework/Core/src/Plugin.cxx b/Framework/Core/src/Plugin.cxx index 91c74bafff5ad..726b12ff68365 100644 --- a/Framework/Core/src/Plugin.cxx +++ b/Framework/Core/src/Plugin.cxx @@ -141,7 +141,7 @@ struct DiscoverAODOptionsInCommandLine : o2::framework::ConfigDiscoveryPlugin { bool injectOption = true; for (size_t i = 0; i < argc; i++) { std::string_view arg = argv[i]; - if (!arg.starts_with("--aod-writer-") && arg != "--aod-parent-base-path-replacement") { + if (!arg.starts_with("--aod-writer-") && !arg.starts_with("--aod-parent-")) { continue; } std::string key = arg.data() + 2; @@ -156,6 +156,9 @@ struct DiscoverAODOptionsInCommandLine : o2::framework::ConfigDiscoveryPlugin { if (key == "aod-parent-base-path-replacement") { results.push_back(ConfigParamSpec{"aod-parent-base-path-replacement", VariantType::String, value, {R"(Replace base path of parent files. Syntax: FROM;TO. E.g. "alien:///path/in/alien;/local/path". Enclose in "" on the command line.)"}}); } + if (key == "aod-parent-access-level") { + results.push_back(ConfigParamSpec{"aod-parent-access-level", VariantType::String, value, {"Allow parent file access up to specified level. Default: no (0)"}}); + } } if (injectOption) { results.push_back(ConfigParamSpec{"aod-writer-compression", VariantType::Int, 505, {"AOD Compression options"}}); diff --git a/Framework/Core/src/WorkflowHelpers.cxx b/Framework/Core/src/WorkflowHelpers.cxx index 56e9930e3b655..da9a135dc5eb8 100644 --- a/Framework/Core/src/WorkflowHelpers.cxx +++ b/Framework/Core/src/WorkflowHelpers.cxx @@ -216,7 +216,6 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext .options = {ConfigParamSpec{"aod-file-private", VariantType::String, ctx.options().get("aod-file"), {"AOD file"}}, ConfigParamSpec{"aod-max-io-rate", VariantType::Float, 0.f, {"Maximum I/O rate in MB/s"}}, ConfigParamSpec{"aod-reader-json", VariantType::String, {"json configuration file"}}, - ConfigParamSpec{"aod-parent-access-level", VariantType::String, {"Allow parent file access up to specified level. Default: no (0)"}}, ConfigParamSpec{"time-limit", VariantType::Int64, 0ll, {"Maximum run time limit in seconds"}}, ConfigParamSpec{"orbit-offset-enumeration", VariantType::Int64, 0ll, {"initial value for the orbit"}}, ConfigParamSpec{"orbit-multiplier-enumeration", VariantType::Int64, 0ll, {"multiplier to get the orbit from the counter"}}, From 56ccb8b3156a3ff671fe98e91fde2ba815b03b64 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Fri, 8 Nov 2024 13:55:05 +0100 Subject: [PATCH 0018/2180] GPU: Add overrideNHbfPerTF option --- GPU/GPUTracking/Definitions/GPUSettingsList.h | 1 + GPU/Workflow/src/GPUWorkflowSpec.cxx | 6 +++--- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index 224e7c720c334..b4831c2088dc2 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -526,6 +526,7 @@ BeginSubConfig(GPUSettingsO2, global, configStandalone, "O2", 0, "O2 workflow se AddOption(solenoidBzNominalGPU, float, -1e6f, "", 0, "Field strength of solenoid Bz in kGaus") AddOption(constBz, bool, false, "", 0, "force constant Bz for tests") AddOption(continuousMaxTimeBin, int32_t, 0, "", 0, "maximum time bin of continuous data, 0 for triggered events, -1 for default of 23ms") +AddOption(overrideNHbfPerTF, int32_t, 0, "", 0, "Overrides the number of HBF per TF if != 0") AddOption(deviceType, std::string, "CPU", "", 0, "Device type, CPU | CUDA | HIP | OCL1 | OCL2") AddOption(forceDeviceType, bool, true, "", 0, "force device type, otherwise allows fall-back to CPU") AddOption(synchronousProcessing, bool, false, "", 0, "Apply performance shortcuts for synchronous processing, disable unneeded steps") diff --git a/GPU/Workflow/src/GPUWorkflowSpec.cxx b/GPU/Workflow/src/GPUWorkflowSpec.cxx index 4549d895c26b9..b7bd7b608aaf5 100644 --- a/GPU/Workflow/src/GPUWorkflowSpec.cxx +++ b/GPU/Workflow/src/GPUWorkflowSpec.cxx @@ -156,7 +156,7 @@ void GPURecoWorkflowSpec::init(InitContext& ic) mAutoSolenoidBz = mConfParam->solenoidBzNominalGPU == -1e6f; mAutoContinuousMaxTimeBin = mConfig->configGRP.continuousMaxTimeBin == -1; if (mAutoContinuousMaxTimeBin) { - mConfig->configGRP.continuousMaxTimeBin = (256 * o2::constants::lhc::LHCMaxBunches + 2 * o2::tpc::constants::LHCBCPERTIMEBIN - 2) / o2::tpc::constants::LHCBCPERTIMEBIN; + mConfig->configGRP.continuousMaxTimeBin = ((mConfParam->overrideNHbfPerTF ? mConfParam->overrideNHbfPerTF : 256) * o2::constants::lhc::LHCMaxBunches + 2 * o2::tpc::constants::LHCBCPERTIMEBIN - 2) / o2::tpc::constants::LHCBCPERTIMEBIN; } if (mConfig->configProcessing.deviceNum == -2) { int32_t myId = ic.services().get().inputTimesliceId; @@ -583,7 +583,7 @@ void GPURecoWorkflowSpec::run(ProcessingContext& pc) mTFSettings->tfStartOrbit = tinfo.firstTForbit; mTFSettings->hasTfStartOrbit = 1; mTFSettings->hasNHBFPerTF = 1; - mTFSettings->nHBFPerTF = GRPGeomHelper::instance().getGRPECS()->getNHBFPerTF(); + mTFSettings->nHBFPerTF = mConfParam->overrideNHbfPerTF ? mConfParam->overrideNHbfPerTF : GRPGeomHelper::instance().getGRPECS()->getNHBFPerTF(); mTFSettings->hasRunStartOrbit = 0; if (mVerbosity) { LOG(info) << "TF firstTForbit " << mTFSettings->tfStartOrbit << " nHBF " << mTFSettings->nHBFPerTF << " runStartOrbit " << mTFSettings->runStartOrbit << " simStartOrbit " << mTFSettings->simStartOrbit; @@ -1016,7 +1016,7 @@ void GPURecoWorkflowSpec::doCalibUpdates(o2::framework::ProcessingContext& pc, c mConfig->configGRP.continuousMaxTimeBin = (mTFSettings->nHBFPerTF * o2::constants::lhc::LHCMaxBunches + 2 * o2::tpc::constants::LHCBCPERTIMEBIN - 2) / o2::tpc::constants::LHCBCPERTIMEBIN; newCalibValues.newContinuousMaxTimeBin = true; newCalibValues.continuousMaxTimeBin = mConfig->configGRP.continuousMaxTimeBin; - LOG(info) << "Updating max time bin " << newCalibValues.continuousMaxTimeBin; + LOG(info) << "Updating max time bin " << newCalibValues.continuousMaxTimeBin << " (" << mTFSettings->nHBFPerTF << " orbits)"; } if (!mPropagatorInstanceCreated) { From ac19d82daecd0dd89ee35eb63328a30c102959e3 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Fri, 8 Nov 2024 14:53:33 +0100 Subject: [PATCH 0019/2180] GPU: Rename some continuousMaxTimeBin to avoid confusion, improve defaults, and cleanup --- Detectors/TPC/qc/src/Tracking.cxx | 2 +- .../src/TPCTrackingDigitsPreCheck.cxx | 2 +- .../reconstruction/test/testGPUCATracking.cxx | 2 +- .../TPC/workflow/src/EntropyEncoderSpec.cxx | 2 +- GPU/GPUTracking/Base/GPUParam.cxx | 4 ++-- GPU/GPUTracking/DataTypes/GPUSettings.h | 2 +- GPU/GPUTracking/Definitions/GPUSettingsList.h | 4 ++-- GPU/GPUTracking/Global/GPUChainTracking.cxx | 2 +- .../GPUO2InterfaceConfigurableParam.cxx | 8 ++++---- .../Interface/GPUO2InterfaceUtils.cxx | 20 ++++++++++--------- .../Standalone/Benchmark/standalone.cxx | 16 +++++++-------- GPU/Workflow/src/GPUWorkflowSpec.cxx | 8 ++++---- GPU/Workflow/src/O2GPUDPLDisplay.cxx | 6 +++--- 13 files changed, 40 insertions(+), 38 deletions(-) diff --git a/Detectors/TPC/qc/src/Tracking.cxx b/Detectors/TPC/qc/src/Tracking.cxx index a3ae6320fe9e0..c2bdf2a5c54bf 100644 --- a/Detectors/TPC/qc/src/Tracking.cxx +++ b/Detectors/TPC/qc/src/Tracking.cxx @@ -51,7 +51,7 @@ void Tracking::initialize(outputModes outputMode, bool postprocessOnly) const auto grp = o2::parameters::GRPObject::loadFrom(); if (grp) { mQAConfig->configGRP.solenoidBzNominalGPU = GPUO2InterfaceUtils::getNominalGPUBz(*grp); - mQAConfig->configGRP.continuousMaxTimeBin = grp->isDetContinuousReadOut(o2::detectors::DetID::TPC) ? -1 : 0; + mQAConfig->configGRP.grpContinuousMaxTimeBin = grp->isDetContinuousReadOut(o2::detectors::DetID::TPC) ? -1 : 0; } else { throw std::runtime_error("Failed to initialize run parameters from GRP"); } diff --git a/Detectors/TPC/reconstruction/src/TPCTrackingDigitsPreCheck.cxx b/Detectors/TPC/reconstruction/src/TPCTrackingDigitsPreCheck.cxx index b2a11811c5661..738e6cff20df4 100644 --- a/Detectors/TPC/reconstruction/src/TPCTrackingDigitsPreCheck.cxx +++ b/Detectors/TPC/reconstruction/src/TPCTrackingDigitsPreCheck.cxx @@ -53,7 +53,7 @@ TPCTrackingDigitsPreCheck::precheckModifiedData TPCTrackingDigitsPreCheck::runPr std::unique_ptr retVal = std::make_unique(); retVal->tpcDigitsMap = *ptrs->tpcPackedDigits; const float zsThreshold = config->configReconstruction.tpc.zsThreshold; - const int maxContTimeBin = config->configGRP.continuousMaxTimeBin; + const int maxContTimeBin = config->configGRP.grpContinuousMaxTimeBin; static bool filterOutOfTF = getenv("TPC_WORKFLOW_FILTER_DIGITS_OUTSIDE_OF_TF") && atoi(getenv("TPC_WORKFLOW_FILTER_DIGITS_OUTSIDE_OF_TF")); bool updateDigits = (zsThreshold > 0 || filterOutOfTF) && ptrs->tpcZS == nullptr; const auto& d = ptrs->tpcPackedDigits; diff --git a/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx b/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx index 33537dc373451..6c0ea8b265585 100644 --- a/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx +++ b/Detectors/TPC/reconstruction/test/testGPUCATracking.cxx @@ -63,7 +63,7 @@ BOOST_AUTO_TEST_CASE(CATracking_test1) config.configProcessing.eventDisplay = nullptr; //Ptr to event display backend, for running standalone OpenGL event display config.configGRP.solenoidBzNominalGPU = solenoidBz; - config.configGRP.continuousMaxTimeBin = continuous ? GPUSettings::TPC_MAX_TF_TIME_BIN : 0; //Number of timebins in timeframe if continuous, 0 otherwise + config.configGRP.grpContinuousMaxTimeBin = continuous ? GPUSettings::TPC_MAX_TF_TIME_BIN : 0; // Number of timebins in timeframe if continuous, 0 otherwise config.configReconstruction.tpc.nWays = 3; //Should always be 3! config.configReconstruction.tpc.nWaysOuter = true; //Will create outer param for TRD diff --git a/Detectors/TPC/workflow/src/EntropyEncoderSpec.cxx b/Detectors/TPC/workflow/src/EntropyEncoderSpec.cxx index 35b76715cbc28..8cca67f65f275 100644 --- a/Detectors/TPC/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/TPC/workflow/src/EntropyEncoderSpec.cxx @@ -92,7 +92,7 @@ void EntropyEncoderSpec::run(ProcessingContext& pc) LOG(fatal) << "configKeyValue tpcTriggeredMode does not match GRP isDetContinuousReadOut(TPC) setting"; } - mConfig->configGRP.continuousMaxTimeBin = (GRPGeomHelper::instance().getGRPECS()->getNHBFPerTF() * o2::constants::lhc::LHCMaxBunches + 2 * o2::tpc::constants::LHCBCPERTIMEBIN - 2) / o2::tpc::constants::LHCBCPERTIMEBIN; + mConfig->configGRP.grpContinuousMaxTimeBin = (GRPGeomHelper::instance().getGRPECS()->getNHBFPerTF() * o2::constants::lhc::LHCMaxBunches + 2 * o2::tpc::constants::LHCBCPERTIMEBIN - 2) / o2::tpc::constants::LHCBCPERTIMEBIN; mConfig->configGRP.solenoidBzNominalGPU = GPUO2InterfaceUtils::getNominalGPUBz(*GRPGeomHelper::instance().getGRPMagField()); mParam->UpdateSettings(&mConfig->configGRP); diff --git a/GPU/GPUTracking/Base/GPUParam.cxx b/GPU/GPUTracking/Base/GPUParam.cxx index 8e2daf1a61490..42d4f61f77116 100644 --- a/GPU/GPUTracking/Base/GPUParam.cxx +++ b/GPU/GPUTracking/Base/GPUParam.cxx @@ -130,8 +130,8 @@ void GPUParam::UpdateSettings(const GPUSettingsGRP* g, const GPUSettingsProcessi UpdateBzOnly(g->solenoidBzNominalGPU); par.assumeConstantBz = g->constBz; par.toyMCEventsFlag = g->homemadeEvents; - par.continuousTracking = g->continuousMaxTimeBin != 0; - continuousMaxTimeBin = g->continuousMaxTimeBin == -1 ? GPUSettings::TPC_MAX_TF_TIME_BIN : g->continuousMaxTimeBin; + par.continuousTracking = g->grpContinuousMaxTimeBin != 0; + continuousMaxTimeBin = g->grpContinuousMaxTimeBin == -1 ? GPUSettings::TPC_MAX_TF_TIME_BIN : g->grpContinuousMaxTimeBin; } par.earlyTpcTransform = rec.tpc.forceEarlyTransform == -1 ? (!par.continuousTracking) : rec.tpc.forceEarlyTransform; qptB5Scaler = CAMath::Abs(bzkG) > 0.1f ? CAMath::Abs(bzkG) / 5.006680f : 1.f; // Repeat here, since passing in g is optional diff --git a/GPU/GPUTracking/DataTypes/GPUSettings.h b/GPU/GPUTracking/DataTypes/GPUSettings.h index 69f3ff67cf257..738457ec99d7b 100644 --- a/GPU/GPUTracking/DataTypes/GPUSettings.h +++ b/GPU/GPUTracking/DataTypes/GPUSettings.h @@ -57,7 +57,7 @@ struct GPUSettingsGRP { float solenoidBzNominalGPU = -5.00668f; // solenoid field strength int32_t constBz = 0; // for test-MC events with constant Bz int32_t homemadeEvents = 0; // Toy-MC events - int32_t continuousMaxTimeBin = 0; // 0 for triggered events, -1 for default TF length + int32_t grpContinuousMaxTimeBin = -2; // 0 for triggered events, -1 for automatic setting, -2 invalid default int32_t needsClusterer = 0; // Set to true if the data requires the clusterizer int32_t doCompClusterDecode = 0; // Set to true if the data contains compressed TPC clusters }; diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index b4831c2088dc2..0b2da89b79ad5 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -478,7 +478,7 @@ AddOption(eventsDir, const char*, "pp", "events", 'e', "Directory with events to AddOption(noEvents, bool, false, "", 0, "Run without data (e.g. for field visualization)") AddOption(eventDisplay, int32_t, 0, "display", 'd', "Show standalone event display", def(1)) AddOption(eventGenerator, bool, false, "", 0, "Run event generator") -AddOption(cont, bool, false, "", 0, "Process continuous timeframe data") +AddOption(cont, bool, false, "", 0, "Process continuous timeframe data, even if input is triggered") AddOption(outputcontrolmem, uint64_t, 0, "outputMemory", 0, "Use predefined output buffer of this size", min(0ul), message("Using %s bytes as output memory")) AddOption(inputcontrolmem, uint64_t, 0, "inputMemory", 0, "Use predefined input buffer of this size", min(0ul), message("Using %s bytes as input memory")) AddOption(cpuAffinity, int32_t, -1, "", 0, "Pin CPU affinity to this CPU core", min(-1)) @@ -525,7 +525,7 @@ EndConfig() BeginSubConfig(GPUSettingsO2, global, configStandalone, "O2", 0, "O2 workflow settings", global) AddOption(solenoidBzNominalGPU, float, -1e6f, "", 0, "Field strength of solenoid Bz in kGaus") AddOption(constBz, bool, false, "", 0, "force constant Bz for tests") -AddOption(continuousMaxTimeBin, int32_t, 0, "", 0, "maximum time bin of continuous data, 0 for triggered events, -1 for default of 23ms") +AddOption(setMaxTimeBin, int32_t, -2, "", 0, "maximum time bin of continuous data, 0 for triggered events, -1 for automatic continuous mode, -2 for automatic continuous / triggered") AddOption(overrideNHbfPerTF, int32_t, 0, "", 0, "Overrides the number of HBF per TF if != 0") AddOption(deviceType, std::string, "CPU", "", 0, "Device type, CPU | CUDA | HIP | OCL1 | OCL2") AddOption(forceDeviceType, bool, true, "", 0, "force device type, otherwise allows fall-back to CPU") diff --git a/GPU/GPUTracking/Global/GPUChainTracking.cxx b/GPU/GPUTracking/Global/GPUChainTracking.cxx index 319e57d99fd0e..8c2599604387b 100644 --- a/GPU/GPUTracking/Global/GPUChainTracking.cxx +++ b/GPU/GPUTracking/Global/GPUChainTracking.cxx @@ -639,7 +639,7 @@ int32_t GPUChainTracking::DoQueuedUpdates(int32_t stream, bool updateSlave) grp->solenoidBzNominalGPU = mNewCalibValues->solenoidField; } if (mNewCalibValues->newContinuousMaxTimeBin) { - grp->continuousMaxTimeBin = mNewCalibValues->continuousMaxTimeBin; + grp->grpContinuousMaxTimeBin = mNewCalibValues->continuousMaxTimeBin; } } } diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceConfigurableParam.cxx b/GPU/GPUTracking/Interface/GPUO2InterfaceConfigurableParam.cxx index c3aaec8d9f9a6..86ae8e3457019 100644 --- a/GPU/GPUTracking/Interface/GPUO2InterfaceConfigurableParam.cxx +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceConfigurableParam.cxx @@ -103,11 +103,11 @@ GPUSettingsO2 GPUO2InterfaceConfiguration::ReadConfigurableParam(GPUO2InterfaceC obj.configReconstruction = rec; obj.configDisplay = display; obj.configQA = QA; - if (obj.configGRP.continuousMaxTimeBin == 0 || obj.configGRP.continuousMaxTimeBin == -1) { - if (global.continuousMaxTimeBin) { - obj.configGRP.continuousMaxTimeBin = global.continuousMaxTimeBin; + if (obj.configGRP.grpContinuousMaxTimeBin < 0) { + if (global.setMaxTimeBin != -2) { + obj.configGRP.grpContinuousMaxTimeBin = global.setMaxTimeBin; } else { - obj.configGRP.continuousMaxTimeBin = global.tpcTriggeredMode ? 0 : -1; + obj.configGRP.grpContinuousMaxTimeBin = global.tpcTriggeredMode ? 0 : -1; } } if (global.solenoidBzNominalGPU > -1e6f) { diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.cxx b/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.cxx index 15a5980a47696..c765909fd879f 100644 --- a/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.cxx +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.cxx @@ -83,30 +83,32 @@ std::unique_ptr GPUO2InterfaceUtils::getFullParam(float solenoidBz, ui { std::unique_ptr retVal = std::make_unique(); std::unique_ptr tmpConfig; + std::unique_ptr tmpSettingsO2; if (!pConfiguration) { tmpConfig = std::make_unique(); pConfiguration = &tmpConfig; - (*pConfiguration)->configGRP.continuousMaxTimeBin = -1; + (*pConfiguration)->configGRP.grpContinuousMaxTimeBin = -1; } else if (!*pConfiguration) { *pConfiguration = std::make_unique(); - (*pConfiguration)->configGRP.continuousMaxTimeBin = -1; + (*pConfiguration)->configGRP.grpContinuousMaxTimeBin = -1; } (*pConfiguration)->configGRP.solenoidBzNominalGPU = solenoidBz; if (pO2Settings && *pO2Settings) { **pO2Settings = (*pConfiguration)->ReadConfigurableParam(); - } else if (pO2Settings) { - *pO2Settings = std::make_unique((*pConfiguration)->ReadConfigurableParam()); } else { - (*pConfiguration)->ReadConfigurableParam(); + if (!pO2Settings) { + pO2Settings = &tmpSettingsO2; + } + *pO2Settings = std::make_unique((*pConfiguration)->ReadConfigurableParam()); } if (nHbfPerTf == 0) { - nHbfPerTf = 256; + nHbfPerTf = (*pO2Settings)->overrideNHbfPerTF ? (*pO2Settings)->overrideNHbfPerTF : 256; } if (autoMaxTimeBin) { - *autoMaxTimeBin = (*pConfiguration)->configGRP.continuousMaxTimeBin == -1; + *autoMaxTimeBin = (*pConfiguration)->configGRP.grpContinuousMaxTimeBin == -1; } - if ((*pConfiguration)->configGRP.continuousMaxTimeBin == -1) { - (*pConfiguration)->configGRP.continuousMaxTimeBin = (nHbfPerTf * o2::constants::lhc::LHCMaxBunches + 2 * o2::tpc::constants::LHCBCPERTIMEBIN - 2) / o2::tpc::constants::LHCBCPERTIMEBIN; + if ((*pConfiguration)->configGRP.grpContinuousMaxTimeBin == -1) { + (*pConfiguration)->configGRP.grpContinuousMaxTimeBin = (nHbfPerTf * o2::constants::lhc::LHCMaxBunches + 2 * o2::tpc::constants::LHCBCPERTIMEBIN - 2) / o2::tpc::constants::LHCBCPERTIMEBIN; } retVal->SetDefaults(&(*pConfiguration)->configGRP, &(*pConfiguration)->configReconstruction, &(*pConfiguration)->configProcessing, nullptr); return retVal; diff --git a/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx b/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx index 3626c33dfbb2c..09069ba1d104d 100644 --- a/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx +++ b/GPU/GPUTracking/Standalone/Benchmark/standalone.cxx @@ -302,7 +302,7 @@ int32_t SetupReconstruction() printf("Error reading event config file\n"); return 1; } - printf("Read event settings from dir %s (solenoidBz: %f, home-made events %d, constBz %d, maxTimeBin %d)\n", filename, rec->GetGRPSettings().solenoidBzNominalGPU, (int32_t)rec->GetGRPSettings().homemadeEvents, (int32_t)rec->GetGRPSettings().constBz, rec->GetGRPSettings().continuousMaxTimeBin); + printf("Read event settings from dir %s (solenoidBz: %f, home-made events %d, constBz %d, maxTimeBin %d)\n", filename, rec->GetGRPSettings().solenoidBzNominalGPU, (int32_t)rec->GetGRPSettings().homemadeEvents, (int32_t)rec->GetGRPSettings().constBz, rec->GetGRPSettings().grpContinuousMaxTimeBin); if (configStandalone.testSyncAsync) { recAsync->ReadSettings(filename); } @@ -331,7 +331,7 @@ int32_t SetupReconstruction() grp.constBz = true; } if (configStandalone.TF.nMerge || configStandalone.TF.bunchSim) { - if (grp.continuousMaxTimeBin) { + if (grp.grpContinuousMaxTimeBin) { printf("ERROR: requested to overlay continuous data - not supported\n"); return 1; } @@ -340,11 +340,11 @@ int32_t SetupReconstruction() configStandalone.cont = true; } if (chainTracking->GetTPCTransformHelper()) { - grp.continuousMaxTimeBin = configStandalone.TF.timeFrameLen * ((double)GPUReconstructionTimeframe::TPCZ / (double)GPUReconstructionTimeframe::DRIFT_TIME) / chainTracking->GetTPCTransformHelper()->getCorrMap()->getVDrift(); + grp.grpContinuousMaxTimeBin = configStandalone.TF.timeFrameLen * ((double)GPUReconstructionTimeframe::TPCZ / (double)GPUReconstructionTimeframe::DRIFT_TIME) / chainTracking->GetTPCTransformHelper()->getCorrMap()->getVDrift(); } } - if (configStandalone.cont && grp.continuousMaxTimeBin == 0) { - grp.continuousMaxTimeBin = -1; + if (configStandalone.cont && grp.grpContinuousMaxTimeBin == 0) { + grp.grpContinuousMaxTimeBin = -1; } if (rec->GetDeviceType() == GPUReconstruction::DeviceType::CPU) { printf("Standalone Test Framework for CA Tracker - Using CPU\n"); @@ -904,11 +904,11 @@ int32_t main(int argc, char** argv) if (configStandalone.overrideMaxTimebin && (chainTracking->mIOPtrs.clustersNative || chainTracking->mIOPtrs.tpcPackedDigits || chainTracking->mIOPtrs.tpcZS)) { GPUSettingsGRP grp = rec->GetGRPSettings(); - if (grp.continuousMaxTimeBin == 0) { + if (grp.grpContinuousMaxTimeBin == 0) { printf("Cannot override max time bin for non-continuous data!\n"); } else { - grp.continuousMaxTimeBin = chainTracking->mIOPtrs.tpcZS ? GPUReconstructionConvert::GetMaxTimeBin(*chainTracking->mIOPtrs.tpcZS) : chainTracking->mIOPtrs.tpcPackedDigits ? GPUReconstructionConvert::GetMaxTimeBin(*chainTracking->mIOPtrs.tpcPackedDigits) : GPUReconstructionConvert::GetMaxTimeBin(*chainTracking->mIOPtrs.clustersNative); - printf("Max time bin set to %d\n", (int32_t)grp.continuousMaxTimeBin); + grp.grpContinuousMaxTimeBin = chainTracking->mIOPtrs.tpcZS ? GPUReconstructionConvert::GetMaxTimeBin(*chainTracking->mIOPtrs.tpcZS) : chainTracking->mIOPtrs.tpcPackedDigits ? GPUReconstructionConvert::GetMaxTimeBin(*chainTracking->mIOPtrs.tpcPackedDigits) : GPUReconstructionConvert::GetMaxTimeBin(*chainTracking->mIOPtrs.clustersNative); + printf("Max time bin set to %d\n", grp.grpContinuousMaxTimeBin); rec->UpdateSettings(&grp); if (recAsync) { recAsync->UpdateSettings(&grp); diff --git a/GPU/Workflow/src/GPUWorkflowSpec.cxx b/GPU/Workflow/src/GPUWorkflowSpec.cxx index b7bd7b608aaf5..fcf27074ca717 100644 --- a/GPU/Workflow/src/GPUWorkflowSpec.cxx +++ b/GPU/Workflow/src/GPUWorkflowSpec.cxx @@ -154,9 +154,9 @@ void GPURecoWorkflowSpec::init(InitContext& ic) } mAutoSolenoidBz = mConfParam->solenoidBzNominalGPU == -1e6f; - mAutoContinuousMaxTimeBin = mConfig->configGRP.continuousMaxTimeBin == -1; + mAutoContinuousMaxTimeBin = mConfig->configGRP.grpContinuousMaxTimeBin < 0; if (mAutoContinuousMaxTimeBin) { - mConfig->configGRP.continuousMaxTimeBin = ((mConfParam->overrideNHbfPerTF ? mConfParam->overrideNHbfPerTF : 256) * o2::constants::lhc::LHCMaxBunches + 2 * o2::tpc::constants::LHCBCPERTIMEBIN - 2) / o2::tpc::constants::LHCBCPERTIMEBIN; + mConfig->configGRP.grpContinuousMaxTimeBin = ((mConfParam->overrideNHbfPerTF ? mConfParam->overrideNHbfPerTF : 256) * o2::constants::lhc::LHCMaxBunches + 2 * o2::tpc::constants::LHCBCPERTIMEBIN - 2) / o2::tpc::constants::LHCBCPERTIMEBIN; } if (mConfig->configProcessing.deviceNum == -2) { int32_t myId = ic.services().get().inputTimesliceId; @@ -1013,9 +1013,9 @@ void GPURecoWorkflowSpec::doCalibUpdates(o2::framework::ProcessingContext& pc, c LOG(info) << "Updating solenoid field " << newCalibValues.solenoidField; } if (mAutoContinuousMaxTimeBin) { - mConfig->configGRP.continuousMaxTimeBin = (mTFSettings->nHBFPerTF * o2::constants::lhc::LHCMaxBunches + 2 * o2::tpc::constants::LHCBCPERTIMEBIN - 2) / o2::tpc::constants::LHCBCPERTIMEBIN; + mConfig->configGRP.grpContinuousMaxTimeBin = (mTFSettings->nHBFPerTF * o2::constants::lhc::LHCMaxBunches + 2 * o2::tpc::constants::LHCBCPERTIMEBIN - 2) / o2::tpc::constants::LHCBCPERTIMEBIN; newCalibValues.newContinuousMaxTimeBin = true; - newCalibValues.continuousMaxTimeBin = mConfig->configGRP.continuousMaxTimeBin; + newCalibValues.continuousMaxTimeBin = mConfig->configGRP.grpContinuousMaxTimeBin; LOG(info) << "Updating max time bin " << newCalibValues.continuousMaxTimeBin << " (" << mTFSettings->nHBFPerTF << " orbits)"; } diff --git a/GPU/Workflow/src/O2GPUDPLDisplay.cxx b/GPU/Workflow/src/O2GPUDPLDisplay.cxx index bb46bd440d399..6946d65915503 100644 --- a/GPU/Workflow/src/O2GPUDPLDisplay.cxx +++ b/GPU/Workflow/src/O2GPUDPLDisplay.cxx @@ -90,7 +90,7 @@ void O2GPUDPLDisplaySpec::init(InitContext& ic) mTFSettings->hasSimStartOrbit = 1; auto& hbfu = o2::raw::HBFUtils::Instance(); mTFSettings->simStartOrbit = hbfu.getFirstIRofTF(o2::InteractionRecord(0, hbfu.orbitFirstSampled)).orbit; - mAutoContinuousMaxTimeBin = mConfig->configGRP.continuousMaxTimeBin == -1; + mAutoContinuousMaxTimeBin = mConfig->configGRP.grpContinuousMaxTimeBin < -1; mDisplay.reset(new GPUO2InterfaceDisplay(mConfig.get())); } @@ -108,14 +108,14 @@ void O2GPUDPLDisplaySpec::run(ProcessingContext& pc) mTFSettings->tfStartOrbit = pc.services().get().firstTForbit; mTFSettings->hasTfStartOrbit = 1; mTFSettings->hasNHBFPerTF = 1; - mTFSettings->nHBFPerTF = GRPGeomHelper::instance().getGRPECS()->getNHBFPerTF(); + mTFSettings->nHBFPerTF = mConfParam->overrideNHbfPerTF ? mConfParam->overrideNHbfPerTF : GRPGeomHelper::instance().getGRPECS()->getNHBFPerTF(); mTFSettings->hasRunStartOrbit = 0; if (mGRPGeomUpdated) { mGRPGeomUpdated = false; mConfig->configGRP.solenoidBzNominalGPU = GPUO2InterfaceUtils::getNominalGPUBz(*GRPGeomHelper::instance().getGRPMagField()); if (mAutoContinuousMaxTimeBin) { - mConfig->configGRP.continuousMaxTimeBin = (mTFSettings->nHBFPerTF * o2::constants::lhc::LHCMaxBunches + 2 * o2::tpc::constants::LHCBCPERTIMEBIN - 2) / o2::tpc::constants::LHCBCPERTIMEBIN; + mConfig->configGRP.grpContinuousMaxTimeBin = (mTFSettings->nHBFPerTF * o2::constants::lhc::LHCMaxBunches + 2 * o2::tpc::constants::LHCBCPERTIMEBIN - 2) / o2::tpc::constants::LHCBCPERTIMEBIN; } mDisplay->UpdateGRP(&mConfig->configGRP); if (mGeometryCreated == 0) { From c4f9811d15d072b8c9a6aba226f57b775c21263e Mon Sep 17 00:00:00 2001 From: David Rohr Date: Fri, 8 Nov 2024 15:07:49 +0100 Subject: [PATCH 0020/2180] GPU: Add GPUO2InterfaceUtils::getTpcMaxTimeBinFromNHbf and remove copy&paste --- Detectors/TPC/workflow/src/EntropyEncoderSpec.cxx | 2 +- GPU/GPUTracking/Interface/GPUO2InterfaceUtils.cxx | 7 ++++++- GPU/GPUTracking/Interface/GPUO2InterfaceUtils.h | 1 + GPU/Workflow/src/GPUWorkflowSpec.cxx | 4 ++-- GPU/Workflow/src/O2GPUDPLDisplay.cxx | 2 +- 5 files changed, 11 insertions(+), 5 deletions(-) diff --git a/Detectors/TPC/workflow/src/EntropyEncoderSpec.cxx b/Detectors/TPC/workflow/src/EntropyEncoderSpec.cxx index 8cca67f65f275..b81cb9a802a4a 100644 --- a/Detectors/TPC/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/TPC/workflow/src/EntropyEncoderSpec.cxx @@ -92,7 +92,7 @@ void EntropyEncoderSpec::run(ProcessingContext& pc) LOG(fatal) << "configKeyValue tpcTriggeredMode does not match GRP isDetContinuousReadOut(TPC) setting"; } - mConfig->configGRP.grpContinuousMaxTimeBin = (GRPGeomHelper::instance().getGRPECS()->getNHBFPerTF() * o2::constants::lhc::LHCMaxBunches + 2 * o2::tpc::constants::LHCBCPERTIMEBIN - 2) / o2::tpc::constants::LHCBCPERTIMEBIN; + mConfig->configGRP.grpContinuousMaxTimeBin = GPUO2InterfaceUtils::getTpcMaxTimeBinFromNHbf(GRPGeomHelper::instance().getGRPECS()->getNHBFPerTF()); mConfig->configGRP.solenoidBzNominalGPU = GPUO2InterfaceUtils::getNominalGPUBz(*GRPGeomHelper::instance().getGRPMagField()); mParam->UpdateSettings(&mConfig->configGRP); diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.cxx b/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.cxx index c765909fd879f..fa5705e903d1a 100644 --- a/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.cxx +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.cxx @@ -108,7 +108,7 @@ std::unique_ptr GPUO2InterfaceUtils::getFullParam(float solenoidBz, ui *autoMaxTimeBin = (*pConfiguration)->configGRP.grpContinuousMaxTimeBin == -1; } if ((*pConfiguration)->configGRP.grpContinuousMaxTimeBin == -1) { - (*pConfiguration)->configGRP.grpContinuousMaxTimeBin = (nHbfPerTf * o2::constants::lhc::LHCMaxBunches + 2 * o2::tpc::constants::LHCBCPERTIMEBIN - 2) / o2::tpc::constants::LHCBCPERTIMEBIN; + (*pConfiguration)->configGRP.grpContinuousMaxTimeBin = GPUO2InterfaceUtils::getTpcMaxTimeBinFromNHbf(nHbfPerTf); } retVal->SetDefaults(&(*pConfiguration)->configGRP, &(*pConfiguration)->configReconstruction, &(*pConfiguration)->configProcessing, nullptr); return retVal; @@ -135,3 +135,8 @@ void GPUO2InterfaceUtils::paramUseExternalOccupancyMap(GPUParam* param, uint32_t } } } + +uint32_t GPUO2InterfaceUtils::getTpcMaxTimeBinFromNHbf(uint32_t nHbf) +{ + return (nHbf * o2::constants::lhc::LHCMaxBunches + 2 * o2::tpc::constants::LHCBCPERTIMEBIN - 2) / o2::tpc::constants::LHCBCPERTIMEBIN; +} diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.h b/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.h index 7b96326387f59..0b5d2b5aa3f7a 100644 --- a/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.h +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceUtils.h @@ -58,6 +58,7 @@ class GPUO2InterfaceUtils 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); + static uint32_t getTpcMaxTimeBinFromNHbf(uint32_t nHbf); class GPUReconstructionZSDecoder { diff --git a/GPU/Workflow/src/GPUWorkflowSpec.cxx b/GPU/Workflow/src/GPUWorkflowSpec.cxx index fcf27074ca717..0360a352b0a90 100644 --- a/GPU/Workflow/src/GPUWorkflowSpec.cxx +++ b/GPU/Workflow/src/GPUWorkflowSpec.cxx @@ -156,7 +156,7 @@ void GPURecoWorkflowSpec::init(InitContext& ic) mAutoSolenoidBz = mConfParam->solenoidBzNominalGPU == -1e6f; mAutoContinuousMaxTimeBin = mConfig->configGRP.grpContinuousMaxTimeBin < 0; if (mAutoContinuousMaxTimeBin) { - mConfig->configGRP.grpContinuousMaxTimeBin = ((mConfParam->overrideNHbfPerTF ? mConfParam->overrideNHbfPerTF : 256) * o2::constants::lhc::LHCMaxBunches + 2 * o2::tpc::constants::LHCBCPERTIMEBIN - 2) / o2::tpc::constants::LHCBCPERTIMEBIN; + mConfig->configGRP.grpContinuousMaxTimeBin = GPUO2InterfaceUtils::getTpcMaxTimeBinFromNHbf(mConfParam->overrideNHbfPerTF ? mConfParam->overrideNHbfPerTF : 256); } if (mConfig->configProcessing.deviceNum == -2) { int32_t myId = ic.services().get().inputTimesliceId; @@ -1013,7 +1013,7 @@ void GPURecoWorkflowSpec::doCalibUpdates(o2::framework::ProcessingContext& pc, c LOG(info) << "Updating solenoid field " << newCalibValues.solenoidField; } if (mAutoContinuousMaxTimeBin) { - mConfig->configGRP.grpContinuousMaxTimeBin = (mTFSettings->nHBFPerTF * o2::constants::lhc::LHCMaxBunches + 2 * o2::tpc::constants::LHCBCPERTIMEBIN - 2) / o2::tpc::constants::LHCBCPERTIMEBIN; + mConfig->configGRP.grpContinuousMaxTimeBin = GPUO2InterfaceUtils::getTpcMaxTimeBinFromNHbf(mTFSettings->nHBFPerTF); newCalibValues.newContinuousMaxTimeBin = true; newCalibValues.continuousMaxTimeBin = mConfig->configGRP.grpContinuousMaxTimeBin; LOG(info) << "Updating max time bin " << newCalibValues.continuousMaxTimeBin << " (" << mTFSettings->nHBFPerTF << " orbits)"; diff --git a/GPU/Workflow/src/O2GPUDPLDisplay.cxx b/GPU/Workflow/src/O2GPUDPLDisplay.cxx index 6946d65915503..8513541bcae43 100644 --- a/GPU/Workflow/src/O2GPUDPLDisplay.cxx +++ b/GPU/Workflow/src/O2GPUDPLDisplay.cxx @@ -115,7 +115,7 @@ void O2GPUDPLDisplaySpec::run(ProcessingContext& pc) mGRPGeomUpdated = false; mConfig->configGRP.solenoidBzNominalGPU = GPUO2InterfaceUtils::getNominalGPUBz(*GRPGeomHelper::instance().getGRPMagField()); if (mAutoContinuousMaxTimeBin) { - mConfig->configGRP.grpContinuousMaxTimeBin = (mTFSettings->nHBFPerTF * o2::constants::lhc::LHCMaxBunches + 2 * o2::tpc::constants::LHCBCPERTIMEBIN - 2) / o2::tpc::constants::LHCBCPERTIMEBIN; + mConfig->configGRP.grpContinuousMaxTimeBin = GPUO2InterfaceUtils::getTpcMaxTimeBinFromNHbf(mTFSettings->nHBFPerTF); } mDisplay->UpdateGRP(&mConfig->configGRP); if (mGeometryCreated == 0) { From 5190c05d7125ad6e9e6dc886d9bf9dd52bfb96ea Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sat, 9 Nov 2024 10:35:11 +0100 Subject: [PATCH 0021/2180] DPL: introduce FragmentToBatch --- .../Core/include/Framework/DataAllocator.h | 14 +++++ .../Core/include/Framework/TableTreeHelpers.h | 17 ++++++ Framework/Core/src/DataAllocator.cxx | 60 +++++++++++++++++++ Framework/Core/src/TableTreeHelpers.cxx | 30 +++++++++- 4 files changed, 120 insertions(+), 1 deletion(-) diff --git a/Framework/Core/include/Framework/DataAllocator.h b/Framework/Core/include/Framework/DataAllocator.h index 9c563b06910b5..eb63b5469bb29 100644 --- a/Framework/Core/include/Framework/DataAllocator.h +++ b/Framework/Core/include/Framework/DataAllocator.h @@ -242,6 +242,15 @@ class DataAllocator return t2t; } + template + requires(requires { static_cast(std::declval>()); }) + decltype(auto) make(const Output& spec, Args... args) + { + auto f2b = std::move(LifetimeHolder(new std::decay_t(args...))); + adopt(spec, f2b); + return f2b; + } + template requires is_messageable::value && (!is_specialization_v) decltype(auto) make(const Output& spec) @@ -284,6 +293,11 @@ class DataAllocator void adopt(const Output& spec, LifetimeHolder&); + /// Adopt a Source2Batch in the framework and serialise / send + /// it as an Arrow Dataset to all consumers of @a spec once done + void + adopt(const Output& spec, LifetimeHolder&); + /// Adopt an Arrow table and send it to all consumers of @a spec void adopt(const Output& spec, std::shared_ptr); diff --git a/Framework/Core/include/Framework/TableTreeHelpers.h b/Framework/Core/include/Framework/TableTreeHelpers.h index ccc7035ba3435..9dc7038b83fe1 100644 --- a/Framework/Core/include/Framework/TableTreeHelpers.h +++ b/Framework/Core/include/Framework/TableTreeHelpers.h @@ -11,11 +11,14 @@ #ifndef O2_FRAMEWORK_TABLETREEHELPERS_H_ #define O2_FRAMEWORK_TABLETREEHELPERS_H_ +#include #include "TFile.h" #include "TTreeReader.h" #include "TTreeReaderValue.h" #include "TTreeReaderArray.h" #include "TableBuilder.h" +#include +#include // ============================================================================= namespace o2::framework @@ -140,6 +143,20 @@ class TreeToTable void addReader(TBranch* branch, std::string const& name, bool VLA); }; +class FragmentToBatch +{ + public: + FragmentToBatch(arrow::MemoryPool* pool = arrow::default_memory_pool()); + void setLabel(const char* label); + void fill(std::shared_ptr, std::shared_ptr dataSetSchema, std::shared_ptr); + std::shared_ptr finalize(); + + private: + arrow::MemoryPool* mArrowMemoryPool = nullptr; + std::string mTableLabel; + std::shared_ptr mRecordBatch; +}; + // ----------------------------------------------------------------------------- } // namespace o2::framework diff --git a/Framework/Core/src/DataAllocator.cxx b/Framework/Core/src/DataAllocator.cxx index bae40f2b47947..c310892c4c490 100644 --- a/Framework/Core/src/DataAllocator.cxx +++ b/Framework/Core/src/DataAllocator.cxx @@ -211,6 +211,34 @@ void doWriteTable(std::shared_ptr b, arrow::Table* table) } } +void doWriteBatch(std::shared_ptr b, arrow::RecordBatch* batch) +{ + auto mock = std::make_shared(); + int64_t expectedSize = 0; + auto mockWriter = arrow::ipc::MakeStreamWriter(mock.get(), batch->schema()); + arrow::Status outStatus = mockWriter.ValueOrDie()->WriteRecordBatch(*batch); + + expectedSize = mock->Tell().ValueOrDie(); + auto reserve = b->Reserve(expectedSize); + if (reserve.ok() == false) { + throw std::runtime_error("Unable to reserve memory for table"); + } + + auto stream = std::make_shared(b); + // This is a copy maybe we can finally get rid of it by having using the + // dataset API? + auto outBatch = arrow::ipc::MakeStreamWriter(stream.get(), batch->schema()); + if (outBatch.ok() == false) { + throw ::std::runtime_error("Unable to create batch writer"); + } + + outStatus = outBatch.ValueOrDie()->WriteRecordBatch(*batch); + + if (outStatus.ok() == false) { + throw std::runtime_error("Unable to Write batch"); + } +} + void DataAllocator::adopt(const Output& spec, LifetimeHolder& tb) { auto& timingInfo = mRegistry.get(); @@ -273,6 +301,38 @@ void DataAllocator::adopt(const Output& spec, LifetimeHolder& t2t) context.addBuffer(std::move(header), buffer, std::move(finalizer), routeIndex); } +void DataAllocator::adopt(const Output& spec, LifetimeHolder& f2b) +{ + auto& timingInfo = mRegistry.get(); + RouteIndex routeIndex = matchDataHeader(spec, timingInfo.timeslice); + + auto header = headerMessageFromOutput(spec, routeIndex, o2::header::gSerializationMethodArrow, 0); + auto& context = mRegistry.get(); + + auto creator = [transport = context.proxy().getOutputTransport(routeIndex)](size_t s) -> std::unique_ptr { + return transport->CreateMessage(s); + }; + auto buffer = std::make_shared(creator); + + f2b.callback = [buffer = buffer, transport = context.proxy().getOutputTransport(routeIndex)](FragmentToBatch& source) { + // Serialization happens in here, so that we can + // get rid of the intermediate tree 2 table object, saving memory. + auto batch = source.finalize(); + doWriteBatch(buffer, batch.get()); + // deletion happens in the caller + }; + + /// To finalise this we write the table to the buffer. + /// FIXME: most likely not a great idea. We should probably write to the buffer + /// directly in the TableBuilder, incrementally. + auto finalizer = [](std::shared_ptr b) -> void { + // This is empty because we already serialised the object when + // the LifetimeHolder goes out of scope. + }; + + context.addBuffer(std::move(header), buffer, std::move(finalizer), routeIndex); +} + void DataAllocator::adopt(const Output& spec, std::shared_ptr ptr) { auto& timingInfo = mRegistry.get(); diff --git a/Framework/Core/src/TableTreeHelpers.cxx b/Framework/Core/src/TableTreeHelpers.cxx index 23aa934c2ca8b..c20febaac517d 100644 --- a/Framework/Core/src/TableTreeHelpers.cxx +++ b/Framework/Core/src/TableTreeHelpers.cxx @@ -13,9 +13,13 @@ #include "Framework/Endian.h" #include "arrow/type_traits.h" +#include +#include +#include #include #include +#include #include namespace TableTreeHelpers { @@ -407,7 +411,7 @@ std::shared_ptr TableToTree::process() for (auto& reader : mColumnReaders) { int idealBasketSize = 1024 + reader->fieldSize() * reader->columnEntries(); // minimal additional size needed, otherwise we get 2 baskets - int basketSize = std::max(32000, idealBasketSize); // keep a minimum value + int basketSize = std::max(32000, idealBasketSize); // keep a minimum value // std::cout << "Setting baskets size for " << reader->branchName() << " to " << basketSize << " = 1024 + " // << reader->fieldSize() << " * " << reader->columnEntries() << ". mRows was " << mRows << std::endl; mTree->SetBasketSize(reader->branchName(), basketSize); @@ -555,4 +559,28 @@ std::shared_ptr TreeToTable::finalize() return mTable; } +FragmentToBatch::FragmentToBatch(arrow::MemoryPool* pool) + : mArrowMemoryPool{pool} +{ +} + +void FragmentToBatch::setLabel(const char* label) +{ + mTableLabel = label; +} + +void FragmentToBatch::fill(std::shared_ptr fragment, std::shared_ptr schema, std::shared_ptr format) +{ + auto options = std::make_shared(); + options->dataset_schema = schema; + auto scanner = format->ScanBatchesAsync(options, fragment); + auto batch = (*scanner)(); + mRecordBatch = *batch.result(); +} + +std::shared_ptr FragmentToBatch::finalize() +{ + return mRecordBatch; +} + } // namespace o2::framework From dc77cb3b925e51c004b38b2f98e9003ddace8790 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Sat, 9 Nov 2024 17:51:49 +0100 Subject: [PATCH 0022/2180] SVStudy: Add mc mother pdg code --- .../study/include/GlobalTrackingStudy/V0Ext.h | 10 ++-- .../study/src/SVStudy.cxx | 50 ++++++++++++------- 2 files changed, 37 insertions(+), 23 deletions(-) diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/V0Ext.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/V0Ext.h index 99b35247081e6..79221b893882d 100644 --- a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/V0Ext.h +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/V0Ext.h @@ -17,9 +17,7 @@ #include "ReconstructionDataFormats/V0.h" #include "SimulationDataFormat/MCCompLabel.h" -namespace o2 -{ -namespace dataformats +namespace o2::dataformats { struct ProngInfoExt { @@ -40,10 +38,10 @@ struct V0Ext { V0Index v0ID; std::array prInfo{}; const ProngInfoExt& getPrInfo(int i) const { return prInfo[i]; } - ClassDefNV(V0Ext, 1); + int mcPID = -1; + ClassDefNV(V0Ext, 2); }; -} // namespace dataformats -} // namespace o2 +} // namespace o2::dataformats #endif diff --git a/Detectors/GlobalTrackingWorkflow/study/src/SVStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/SVStudy.cxx index 8ce1c1cec3e01..17b33c86e61ad 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/SVStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/SVStudy.cxx @@ -22,6 +22,7 @@ #include "DetectorsBase/GeometryManager.h" #include "SimulationDataFormat/MCEventLabel.h" #include "SimulationDataFormat/MCUtils.h" +#include "SimulationDataFormat/MCTrack.h" #include "CommonDataFormat/BunchFilling.h" #include "CommonUtils/NameConf.h" #include "DataFormatsFT0/RecPoints.h" @@ -86,7 +87,7 @@ class SVStudySpec : public Task float mBz = 0; GTrackID::mask_t mTracksSrc{}; o2::vertexing::DCAFitterN<2> mFitterV0; - o2::steer::MCKinematicsReader mcReader; // reader of MC information + std::unique_ptr mcReader; // reader of MC information }; void SVStudySpec::init(InitContext& ic) @@ -96,6 +97,9 @@ void SVStudySpec::init(InitContext& ic) mRefit = ic.options().get("refit"); mSelK0 = ic.options().get("sel-k0"); mMaxEta = ic.options().get("max-eta"); + if (mUseMC) { + mcReader = std::make_unique("collisioncontext.root"); + } } void SVStudySpec::run(ProcessingContext& pc) @@ -161,23 +165,24 @@ o2::dataformats::V0Ext SVStudySpec::processV0(int iv, o2::globaltracking::RecoCo v0ext.v0 = v0sel; } v0ext.v0ID = v0id; - o2::MCCompLabel lb; + o2::MCCompLabel lb[2]; + const o2::MCTrack* mcTrks[2]; for (int ip = 0; ip < 2; ip++) { auto& prInfo = v0ext.prInfo[ip]; auto gid = v0ext.v0ID.getProngID(ip); auto gidset = recoData.getSingleDetectorRefs(gid); - lb = recoData.getTrackMCLabel(gid); - if (lb.isValid()) { - prInfo.corrGlo = !lb.isFake(); + lb[ip] = recoData.getTrackMCLabel(gid); + if (lb[ip].isValid()) { + prInfo.corrGlo = !lb[ip].isFake(); } // get TPC tracks, if any if (gidset[GTrackID::TPC].isSourceSet()) { const auto& tpcTr = recoData.getTPCTrack(gidset[GTrackID::TPC]); prInfo.trackTPC = tpcTr; prInfo.nClTPC = tpcTr.getNClusters(); - lb = recoData.getTrackMCLabel(gidset[GTrackID::TPC]); - if (lb.isValid()) { - prInfo.corrTPC = !lb.isFake(); + lb[ip] = recoData.getTrackMCLabel(gidset[GTrackID::TPC]); + if (lb[ip].isValid()) { + prInfo.corrTPC = !lb[ip].isFake(); } } // get ITS tracks, if any @@ -186,9 +191,9 @@ o2::dataformats::V0Ext SVStudySpec::processV0(int iv, o2::globaltracking::RecoCo if (gidset[GTrackID::ITS].isSourceSet()) { const auto& itsTr = recoData.getITSTrack(gidset[GTrackID::ITS]); prInfo.nClITS = itsTr.getNClusters(); - lb = recoData.getTrackMCLabel(gidset[GTrackID::ITS]); - if (lb.isValid()) { - prInfo.corrITS = !lb.isFake(); + lb[ip] = recoData.getTrackMCLabel(gidset[GTrackID::ITS]); + if (lb[ip].isValid()) { + prInfo.corrITS = !lb[ip].isFake(); } for (int il = 0; il < 7; il++) { if (itsTr.hasHitOnLayer(il)) { @@ -198,9 +203,9 @@ o2::dataformats::V0Ext SVStudySpec::processV0(int iv, o2::globaltracking::RecoCo } else { const auto& itsTrf = recoData.getITSABRefs()[gidset[GTrackID::ITSAB]]; prInfo.nClITS = itsTrf.getNClusters(); - lb = recoData.getTrackMCLabel(gidset[GTrackID::ITSAB]); - if (lb.isValid()) { - prInfo.corrITS = !lb.isFake(); + lb[ip] = recoData.getTrackMCLabel(gidset[GTrackID::ITSAB]); + if (lb[ip].isValid()) { + prInfo.corrITS = !lb[ip].isFake(); } for (int il = 0; il < 7; il++) { if (itsTrf.hasHitOnLayer(il)) { @@ -211,13 +216,24 @@ o2::dataformats::V0Ext SVStudySpec::processV0(int iv, o2::globaltracking::RecoCo } if (gidset[GTrackID::ITSTPC].isSourceSet()) { auto mtc = recoData.getTPCITSTrack(gidset[GTrackID::ITSTPC]); - lb = recoData.getTrackMCLabel(gidset[GTrackID::ITSTPC]); + lb[ip] = recoData.getTrackMCLabel(gidset[GTrackID::ITSTPC]); prInfo.chi2ITSTPC = mtc.getChi2Match(); - if (lb.isValid()) { - prInfo.corrITSTPC = !lb.isFake(); + if (lb[ip].isValid()) { + prInfo.corrITSTPC = !lb[ip].isFake(); } } } + if (mUseMC && lb[ip].isValid()) { // temp store of mctrks + mcTrks[ip] = mcReader->getTrack(lb[ip]); + } + } + if (mUseMC && (mcTrks[0] != nullptr) && (mcTrks[1] != nullptr)) { + // check majority vote on mother particle otherwise leave pdg -1 + if (lb[0].getSourceID() == lb[1].getSourceID() && lb[0].getEventID() == lb[1].getEventID() && + mcTrks[0]->getMotherTrackId() == mcTrks[1]->getMotherTrackId() && mcTrks[0]->getMotherTrackId() >= 0) { + const auto mother = mcReader->getTrack(lb[0].getSourceID(), lb[0].getEventID(), mcTrks[0]->getMotherTrackId()); + v0ext.mcPID = mother->GetPdgCode(); + } } return v0ext; } From d0c4891828e5e868dd879d37f670acede41f9217 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 10 Nov 2024 16:47:11 +0100 Subject: [PATCH 0023/2180] DPL: enable the plugin for any parent option If we read metadata from parents, we might be affecting the workflow generation, therefore we must consider all the parent affecting options as workflow ones. --- Framework/Core/src/Plugin.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Framework/Core/src/Plugin.cxx b/Framework/Core/src/Plugin.cxx index 726b12ff68365..0d225b81c0581 100644 --- a/Framework/Core/src/Plugin.cxx +++ b/Framework/Core/src/Plugin.cxx @@ -57,7 +57,7 @@ auto lookForCommandLineAODOptions = [](ConfigParamRegistry& registry, int argc, O2_SIGNPOST_EVENT_EMIT(capabilities, sid, "DiscoverAODOptionsInCommandLineCapability", "AOD options found in arguments. Populating from them."); return true; } - if (arg.starts_with("--aod-parent-base-path-replacement")) { + if (arg.starts_with("--aod-parent-")) { O2_SIGNPOST_EVENT_EMIT(capabilities, sid, "DiscoverAODOptionsInCommandLineCapability", "AOD options found in arguments. Populating from them."); return true; } From adc8cad268949741f71df3e43ab6cfd801a2a562 Mon Sep 17 00:00:00 2001 From: swenzel Date: Fri, 8 Nov 2024 15:06:19 +0100 Subject: [PATCH 0024/2180] Digicontext: Several improvements Work needed for restructuring O2DPG MC workflows towards the use of a globally pre-generated collision context. * fixes for picking up vertices from collision contexts * possibility to generate vertices in CollContextTool from CCDB entry * smaller cleanup relates to https://its.cern.ch/jira/browse/O2-3622 --- .../SimConfig/include/SimConfig/SimConfig.h | 5 ++ Common/SimConfig/src/SimConfig.cxx | 30 +++++++----- .../simulation/src/DigitizationContext.cxx | 2 + Steer/src/CollisionContextTool.cxx | 48 +++++++++++++------ run/O2PrimaryServerDevice.h | 5 +- 5 files changed, 62 insertions(+), 28 deletions(-) diff --git a/Common/SimConfig/include/SimConfig/SimConfig.h b/Common/SimConfig/include/SimConfig/SimConfig.h index da2f978ddf319..d70fca2400399 100644 --- a/Common/SimConfig/include/SimConfig/SimConfig.h +++ b/Common/SimConfig/include/SimConfig/SimConfig.h @@ -83,6 +83,7 @@ struct SimConfigData { bool mNoGeant = false; // if Geant transport should be turned off (when one is only interested in the generated events) bool mIsUpgrade = false; // true if the simulation is for Run 5 std::string mFromCollisionContext = ""; // string denoting a collision context file; If given, this file will be used to determine number of events + // bool mForwardKine = false; // true if tracks and event headers are to be published on a FairMQ channel (for reading by other consumers) bool mWriteToDisc = true; // whether we write simulation products (kine, hits) to disc VertexMode mVertexMode = VertexMode::kDiamondParam; // by default we should use die InteractionDiamond parameter @@ -177,6 +178,10 @@ class SimConfig bool writeToDisc() const { return mConfigData.mWriteToDisc; } VertexMode getVertexMode() const { return mConfigData.mVertexMode; } + // returns the pair of collision context filename as well as event prefix encoded + // in the mFromCollisionContext string. Returns empty string if information is not available or set. + std::pair getCollContextFilenameAndEventPrefix() const; + private: SimConfigData mConfigData; //! diff --git a/Common/SimConfig/src/SimConfig.cxx b/Common/SimConfig/src/SimConfig.cxx index 9a10b26547ce6..be21c38c5efc8 100644 --- a/Common/SimConfig/src/SimConfig.cxx +++ b/Common/SimConfig/src/SimConfig.cxx @@ -76,7 +76,7 @@ void SimConfig::initOptions(boost::program_options::options_description& options "noGeant", bpo::bool_switch(), "prohibits any Geant transport/physics (by using tight cuts)")( "forwardKine", bpo::bool_switch(), "forward kinematics on a FairMQ channel")( "noDiscOutput", bpo::bool_switch(), "switch off writing sim results to disc (useful in combination with forwardKine)"); - options.add_options()("fromCollContext", bpo::value()->default_value(""), "Use a pregenerated collision context to infer number of events to simulate, how to embedd them, the vertex position etc. Takes precedence of other options such as \"--nEvents\"."); + options.add_options()("fromCollContext", bpo::value()->default_value(""), "Use a pregenerated collision context to infer number of events to simulate, how to embedd them, the vertex position etc. Takes precedence of other options such as \"--nEvents\". The format is COLLISIONCONTEXTFILE.root[:SIGNALNAME] where SIGNALNAME is the event part in the context which is relevant."); } void SimConfig::determineActiveModules(std::vector const& inputargs, std::vector const& skippedModules, std::vector& activeModules, bool isUpgrade) @@ -270,6 +270,21 @@ void SimConfig::determineReadoutDetectors(std::vector const& active } } +std::pair SimConfig::getCollContextFilenameAndEventPrefix() const +{ + // we decompose the argument to fetch + // (a) collision contextfilename + // (b) sim prefix to use from the context + auto pos = mConfigData.mFromCollisionContext.find(':'); + std::string collcontextfile{mConfigData.mFromCollisionContext}; + std::string simprefix{mConfigData.mOutputPrefix}; + if (pos != std::string::npos) { + collcontextfile = mConfigData.mFromCollisionContext.substr(0, pos); + simprefix = mConfigData.mFromCollisionContext.substr(pos + 1); + } + return std::make_pair(collcontextfile, simprefix); +} + bool SimConfig::resetFromParsedMap(boost::program_options::variables_map const& vm) { using o2::detectors::DetID; @@ -333,17 +348,8 @@ bool SimConfig::resetFromParsedMap(boost::program_options::variables_map const& mConfigData.mFilterNoHitEvents = true; } mConfigData.mFromCollisionContext = vm["fromCollContext"].as(); - // we decompose the argument to fetch - // (a) collision contextfilename - // (b) sim prefix to use from the context - auto pos = mConfigData.mFromCollisionContext.find(':'); - std::string collcontextfile{mConfigData.mFromCollisionContext}; - std::string simprefix{mConfigData.mOutputPrefix}; - if (pos != std::string::npos) { - collcontextfile = mConfigData.mFromCollisionContext.substr(0, pos); - simprefix = mConfigData.mFromCollisionContext.substr(pos + 1); - } - adjustFromCollContext(collcontextfile, simprefix); + auto collcontext_simprefix = getCollContextFilenameAndEventPrefix(); + adjustFromCollContext(collcontext_simprefix.first, collcontext_simprefix.second); // analyse vertex options if (!parseVertexModeString(vm["vertexMode"].as(), mConfigData.mVertexMode)) { diff --git a/DataFormats/simulation/src/DigitizationContext.cxx b/DataFormats/simulation/src/DigitizationContext.cxx index ba1fda53e179b..3fb6b757aeea3 100644 --- a/DataFormats/simulation/src/DigitizationContext.cxx +++ b/DataFormats/simulation/src/DigitizationContext.cxx @@ -578,5 +578,7 @@ DigitizationContext DigitizationContext::extractSingleTimeframe(int timeframeid, } catch (std::exception) { LOG(warn) << "No such timeframe id in collision context. Returing empty object"; } + // fix number of collisions + r.setNCollisions(r.mEventRecords.size()); return r; } diff --git a/Steer/src/CollisionContextTool.cxx b/Steer/src/CollisionContextTool.cxx index af2f607b88774..3d1dcec29976e 100644 --- a/Steer/src/CollisionContextTool.cxx +++ b/Steer/src/CollisionContextTool.cxx @@ -27,6 +27,7 @@ #include "CommonUtils/ConfigurableParam.h" #include #include "DataFormatsParameters/GRPLHCIFData.h" +#include "SimConfig/SimConfig.h" // // Created by Sandro Wenzel on 13.07.21. @@ -52,11 +53,12 @@ struct Options { bool useexistingkinematics = false; bool noEmptyTF = false; // prevent empty timeframes; the first interaction will be shifted backwards to fall within the range given by Options.orbits int maxCollsPerTF = -1; // the maximal number of hadronic collisions per TF (can be used to constrain number of collisions per timeframe to some maximal value) - bool genVertices = false; // whether to assign vertices to collisions std::string configKeyValues = ""; // string to init config key values long timestamp = -1; // timestamp for CCDB queries std::string individualTFextraction = ""; // triggers extraction of individuel timeframe components when non-null // format is path prefix + std::string vertexModeString{"kNoVertex"}; // Vertex Mode; vertices will be assigned to collisions of mode != kNoVertex + o2::conf::VertexMode vertexMode = o2::conf::VertexMode::kNoVertex; }; enum class InteractionLockMode { @@ -203,7 +205,9 @@ bool parseOptions(int argc, char* argv[], Options& optvalues) "first-orbit", bpo::value(&optvalues.firstFractionalOrbit)->default_value(0), "First (fractional) orbit in the run (HBFUtils.firstOrbit + BC from decimal)")( "maxCollsPerTF", bpo::value(&optvalues.maxCollsPerTF)->default_value(-1), "Maximal number of MC collisions to put into one timeframe. By default no constraint.")( "noEmptyTF", bpo::bool_switch(&optvalues.noEmptyTF), "Enforce to have at least one collision")( - "configKeyValues", bpo::value(&optvalues.configKeyValues)->default_value(""), "Semicolon separated key=value strings (e.g.: 'TPC.gasDensity=1;...')")("with-vertices", "Assign vertices to collisions.")("timestamp", bpo::value(&optvalues.timestamp)->default_value(-1L), "Timestamp for CCDB queries / anchoring")( + "configKeyValues", bpo::value(&optvalues.configKeyValues)->default_value(""), "Semicolon separated key=value strings (e.g.: 'TPC.gasDensity=1;...')")( + "with-vertices", bpo::value(&optvalues.vertexModeString)->default_value("kNoVertex"), "Assign vertices to collisions. Argument is the vertex mode. Defaults to no vertexing applied")( + "timestamp", bpo::value(&optvalues.timestamp)->default_value(-1L), "Timestamp for CCDB queries / anchoring")( "extract-per-timeframe", bpo::value(&optvalues.individualTFextraction)->default_value(""), "Extract individual timeframe contexts. Format required: time_frame_prefix[:comma_separated_list_of_signals_to_offset]"); @@ -225,9 +229,8 @@ bool parseOptions(int argc, char* argv[], Options& optvalues) if (vm.count("use-existing-kine")) { optvalues.useexistingkinematics = true; } - if (vm.count("with-vertices")) { - optvalues.genVertices = true; - } + + o2::conf::SimConfig::parseVertexModeString(optvalues.vertexModeString, optvalues.vertexMode); // fix the first orbit and bunch crossing // auto orbitbcpair = parseOrbitAndBC(optvalues.firstIRString); @@ -277,10 +280,9 @@ int main(int argc, char* argv[]) LOG(info) << "Fetch bcPattern information from CCDB"; // fetch the GRP Object auto& ccdb = o2::ccdb::BasicCCDBManager::instance(); - ccdb.setTimestamp(options.timestamp); ccdb.setCaching(false); ccdb.setLocalObjectValidityChecking(true); - auto grpLHC = ccdb.get("GLO/Config/GRPLHCIF"); + auto grpLHC = ccdb.getForTimeStamp("GLO/Config/GRPLHCIF", options.timestamp); LOG(info) << "Fetched injection scheme " << grpLHC->getInjectionScheme() << " from CCDB"; sampler.setBunchFilling(grpLHC->getBunchFilling()); } else { @@ -449,14 +451,32 @@ int main(int argc, char* argv[]) auto numTimeFrames = digicontext.finalizeTimeframeStructure(orbitstart, options.orbitsPerTF); - if (options.genVertices) { - // TODO: offer option taking meanVertex directly from CCDB ! "GLO/Calib/MeanVertex" - // sample interaction vertices + if (options.vertexMode != o2::conf::VertexMode::kNoVertex) { + switch (options.vertexMode) { + case o2::conf::VertexMode::kCCDB: { + // fetch mean vertex from CCDB + auto meanv = o2::ccdb::BasicCCDBManager::instance().getForTimeStamp("GLO/Calib/MeanVertex", options.timestamp); + if (meanv) { + LOG(info) << "Applying vertexing using CCDB mean vertex " << *meanv; + digicontext.sampleInteractionVertices(*meanv); + } else { + LOG(fatal) << "No vertex available"; + } + break; + } - // init this vertex from CCDB or InteractionDiamond parameter - const auto& dparam = o2::eventgen::InteractionDiamondParam::Instance(); - o2::dataformats::MeanVertexObject meanv(dparam.position[0], dparam.position[1], dparam.position[2], dparam.width[0], dparam.width[1], dparam.width[2], dparam.slopeX, dparam.slopeY); - digicontext.sampleInteractionVertices(meanv); + case o2::conf::VertexMode::kDiamondParam: { + // init this vertex from CCDB or InteractionDiamond parameter + const auto& dparam = o2::eventgen::InteractionDiamondParam::Instance(); + o2::dataformats::MeanVertexObject meanv(dparam.position[0], dparam.position[1], dparam.position[2], dparam.width[0], dparam.width[1], dparam.width[2], dparam.slopeX, dparam.slopeY); + LOG(info) << "Applying vertexing using DiamondParam mean vertex " << meanv; + digicontext.sampleInteractionVertices(meanv); + break; + } + default: { + LOG(error) << "Unknown vertex mode ... Not generating vertices"; + } + } } // we fill QED contributions to the context diff --git a/run/O2PrimaryServerDevice.h b/run/O2PrimaryServerDevice.h index 202e6e8652cc7..53b86d1f23591 100644 --- a/run/O2PrimaryServerDevice.h +++ b/run/O2PrimaryServerDevice.h @@ -138,7 +138,8 @@ class O2PrimaryServerDevice final : public fair::mq::Device mPrimGen->SetEvent(&mEventHeader); // A good moment to couple to collision context - auto collContextFileName = mSimConfig.getConfigData().mFromCollisionContext; + auto collContextFileName_PrefixPair = mSimConfig.getCollContextFilenameAndEventPrefix(); + auto collContextFileName = collContextFileName_PrefixPair.first; if (collContextFileName.size() > 0) { LOG(info) << "Simulation has collission context"; mCollissionContext = o2::steer::DigitizationContext::loadFromFile(collContextFileName); @@ -147,7 +148,7 @@ class O2PrimaryServerDevice final : public fair::mq::Device LOG(info) << "We found " << vertices.size() << " vertices included "; // initialize the eventID to collID mapping - const auto source = mCollissionContext->findSimPrefix(mSimConfig.getOutPrefix()); + const auto source = mCollissionContext->findSimPrefix(collContextFileName_PrefixPair.second); if (source == -1) { LOG(fatal) << "Wrong simulation prefix"; } From 37a1f623d270548827cad7b896e2d1a418733084 Mon Sep 17 00:00:00 2001 From: Diego Stocco Date: Fri, 13 Sep 2024 12:23:01 +0200 Subject: [PATCH 0025/2180] Fix typo and add explanation comments --- Detectors/MUON/MID/Calibration/macros/build_rejectlist.C | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Detectors/MUON/MID/Calibration/macros/build_rejectlist.C b/Detectors/MUON/MID/Calibration/macros/build_rejectlist.C index 0782a08a3822d..685b28fd543d6 100644 --- a/Detectors/MUON/MID/Calibration/macros/build_rejectlist.C +++ b/Detectors/MUON/MID/Calibration/macros/build_rejectlist.C @@ -109,6 +109,12 @@ std::vector findObjectsTSInPeriod(long start, long end, const o2::ccdb::Cc /// @return Pair with first and last time std::pair findTSRange(TCanvas* qcQuality, bool selectBad = true) { + // Gets the plot with the quality flags + // The flag values are: + // Good: 3.5 + // Medium: 2.5 + // Bad: 1.5 + // Null: 0.5 auto* gr = static_cast(qcQuality->GetListOfPrimitives()->FindObject("Graph")); double xp, yp; std::pair range{std::numeric_limits::max(), 0}; @@ -276,7 +282,7 @@ std::vector build_rejectlist(long timestamp, const char* qc return build_rejectlist(timestamp, qcdbApi, ccdbApi, outCCDBApi); } -/// @brief Builds the reject list iin a time range +/// @brief Builds the reject list in a time range /// @param start Start time for query /// @param end End time for query /// @param qcdbUrl QCDB URL From 404b41442fab332901e2cf624e2bc8c883046010 Mon Sep 17 00:00:00 2001 From: Diego Stocco Date: Mon, 30 Sep 2024 15:45:26 +0200 Subject: [PATCH 0026/2180] Add possibility to specify a manual reject list for MID --- .../MUON/MID/Calibration/macros/README.md | 47 ++++ .../MID/Calibration/macros/build_rejectlist.C | 208 ++++++++++++++---- 2 files changed, 212 insertions(+), 43 deletions(-) diff --git a/Detectors/MUON/MID/Calibration/macros/README.md b/Detectors/MUON/MID/Calibration/macros/README.md index 7009e99086419..83e88f18ecf48 100644 --- a/Detectors/MUON/MID/Calibration/macros/README.md +++ b/Detectors/MUON/MID/Calibration/macros/README.md @@ -60,6 +60,53 @@ root -l .x build_rejectlist.C+(1716436103391,1721272208000,"localhost:8083") ``` +### Add custom bad channels + +The macro `build_rejectlist.C` scans the QCDB and the CCDB in search of issues. +However, the QCDB flag is based on local boards with empty signals. +It can happen that a local board is problematic, but not completely dead and, therefore, it is not correctly spotted by the macro. +It is therefore important to have a way to add the issues by hand. +This can be done with a json file in the form: + +```json +{ + "startRun": 557251, + "endRun": 557926, + "rejectList": [ + { + "deId": 4, + "columnId": 2, + "patterns": [ + "0x0", + "0xFFFF", + "0x0", + "0x0", + "0x0" + ] + }, + { + "deId": 13, + "columnId": 2, + "patterns": [ + "0x0", + "0xFFFF", + "0x0", + "0x0", + "0x0" + ] + } + ] +} +``` + +The path to the file is then given to the macro with: + +```shell +.x build_rejectlist.C+(1726299038000,1727386238000,"http://localhost:8083","http://alice-ccdb.cern.ch","http://localhost:8080","rejectlist.json") +``` + +The macro will then merge the manual reject list from the file with the reject list that it finds by scanning the QCDB and CCDB. + ## Running the local CCDB The local CCDB server can be easily built through alibuild. diff --git a/Detectors/MUON/MID/Calibration/macros/build_rejectlist.C b/Detectors/MUON/MID/Calibration/macros/build_rejectlist.C index 685b28fd543d6..7a395d2c099da 100644 --- a/Detectors/MUON/MID/Calibration/macros/build_rejectlist.C +++ b/Detectors/MUON/MID/Calibration/macros/build_rejectlist.C @@ -21,6 +21,8 @@ #include #include #include +#include "rapidjson/document.h" +#include "rapidjson/istreamwrapper.h" #include "TCanvas.h" #include "TH1.h" #include "TGraph.h" @@ -29,15 +31,25 @@ #include "DataFormatsParameters/GRPECSObject.h" #include "DetectorsCommonDataFormats/DetID.h" #include "DataFormatsMID/ColumnData.h" +#include "MIDBase/ColumnDataHandler.h" #include "MIDGlobalMapping/ExtendedMappingInfo.h" #include "MIDGlobalMapping/GlobalMapper.h" #include "MIDFiltering/ChannelMasksHandler.h" + +// ... #if !defined(__CLING__) || defined(__ROOTCLING__) #include "CCDB/BasicCCDBManager.h" #endif static const std::string sPathQCQuality = "qc/MID/MO/MIDQuality/Trends/global/MIDQuality/MIDQuality"; +/// @brief Reject list object +struct RejectListStruct { + long start = 0; /// Start validity + long end = 0; /// End validity + std::vector rejectList{}; /// Bad channels +}; + /// @brief Get timestamp in milliseconds /// @param timestamp Input timestamp (in s or ms) /// @return Timestamp in ms @@ -174,25 +186,38 @@ std::vector getRejectList(std::vector return badChannels; } +/// @brief Gets the run duration with a safety marging +/// @param ccdbApi CCDB api +/// @param marging margin in milliseconds +/// @return Pair with the timestamps of start-margin and end+margin for the run +std::pair getRunDuration(const o2::ccdb::CcdbApi& ccdbApi, int runNumber, int64_t margin = 120000) +{ + auto runRange = o2::ccdb::BasicCCDBManager::getRunDuration(ccdbApi, runNumber); + runRange.first -= margin; // Subtract margin + runRange.second += margin; // Add margin + return runRange; +} + /// @brief Builds the reject list for the selected timestamp /// @param timestamp Timestamp for query /// @param qcdbApi QCDB api /// @param ccdbApi CCDB api /// @param outCCDBApi api of the CCDB where the reject list will be uploaded /// @return Reject list -std::vector build_rejectlist(long timestamp, const o2::ccdb::CcdbApi& qcdbApi, const o2::ccdb::CcdbApi& ccdbApi, const o2::ccdb::CcdbApi& outCCDBApi) +RejectListStruct build_rejectlist(long timestamp, const o2::ccdb::CcdbApi& qcdbApi, const o2::ccdb::CcdbApi& ccdbApi) { std::map metadata; + RejectListStruct rl; auto* qcQuality = qcdbApi.retrieveFromTFileAny(sPathQCQuality, metadata, getTSMS(timestamp)); if (!qcQuality) { std::cerr << "Cannot find QC quality for " << tsToString(timestamp) << std::endl; - return {}; + return rl; } // Find the first and last timestamp where the quality was bad (if any) auto badTSRange = findTSRange(qcQuality); if (badTSRange.second == 0) { std::cout << "All good" << std::endl; - return {}; + return rl; } // Search for the last timestamp for which the run quality was good auto goodTSRange = findTSRange(qcQuality, false); @@ -202,18 +227,15 @@ std::vector build_rejectlist(long timestamp, const o2::ccdb if (!grpecs.isDetReadOut(o2::detectors::DetID::MID)) { std::cout << "Error: we are probably reading a parallel run" << std::endl; grpecs.print(); - return {}; + return rl; } if (grpecs.getRunType() != o2::parameters::GRPECS::PHYSICS) { std::cout << "This is not a physics run: skip" << std::endl; grpecs.print(); - return {}; + return rl; } - auto runRange = o2::ccdb::BasicCCDBManager::getRunDuration(ccdbApi, grpecs.getRun()); - long margin = 120000; // Add a two minutes safety margin - runRange.first -= margin; // Subtract margin - runRange.second += margin; // Add margin + auto runRange = getRunDuration(ccdbApi, grpecs.getRun()); // Search for hits histogram in the period where the QC quality was bad auto tsVector = findObjectsTSInPeriod(badTSRange.first, badTSRange.second, qcdbApi, "qc/MID/MO/QcTaskMIDDigits/Hits"); @@ -227,15 +249,15 @@ std::vector build_rejectlist(long timestamp, const o2::ccdb auto infos = gm.buildStripsInfo(); auto badChannels = findBadChannels(occupancy, infos); auto badChannelsCCDB = *ccdbApi.retrieveFromTFileAny>("MID/Calib/BadChannels", metadata, getTSMS(timestamp)); - auto rejectList = getRejectList(badChannels, badChannelsCCDB); - if (rejectList.empty()) { + rl.rejectList = getRejectList(badChannels, badChannelsCCDB); + if (rl.rejectList.empty()) { std::cout << "Warning: reject list was empty. It probably means that an entire board is already masked in calibration for run " << grpecs.getRun() << std::endl; - return {}; + return rl; } // Print some useful information std::cout << "Reject list:" << std::endl; - for (auto& col : rejectList) { + for (auto& col : rl.rejectList) { std::cout << col << std::endl; } std::cout << "Run number: " << grpecs.getRun() << std::endl; @@ -245,41 +267,120 @@ std::vector build_rejectlist(long timestamp, const o2::ccdb std::cout << "Bad: " << timeRangeToString(badTSRange.first, badTSRange.second) << std::endl; // Set the start of the reject list to the last timestamp in which the occupancy was ok - auto startRL = goodTSRange.second; + rl.start = goodTSRange.second; if (goodTSRange.first == 0) { // If the quality was bad for the full run, set the start of the reject list to the SOR std::cout << "CAVEAT: no good TS found. Will use SOT instead" << std::endl; - startRL = runRange.first; + rl.start = runRange.first; } // Set the end of the reject list to the end of run - auto endRL = runRange.second; - // Ask if you want to upload the object to the CCDB - std::cout << "Upload reject list with validity: " << startRL << " - " << endRL << " to " << outCCDBApi.getURL() << "? [y/n]" << std::endl; - std::string answer; - std::cin >> answer; - if (answer == "y") { - std::cout << "Storing RejectList valid from " << startRL << " to " << endRL << std::endl; - outCCDBApi.storeAsTFileAny(&rejectList, "MID/Calib/RejectList", metadata, startRL, endRL); + rl.end = runRange.second; + return rl; +} + +/// @brief Loads the reject list from a json file +/// @param ccdbApi CCDB api +/// @param filename json filename +/// @return Reject list structure +RejectListStruct load_from_json(const o2::ccdb::CcdbApi& ccdbApi, const char* filename = "rejectlist.json") +{ + // Open the JSON file + std::cout << "Reading reject list from file " << filename << std::endl; + RejectListStruct rl; + std::ifstream inFile(filename); + if (!inFile.is_open()) { + std::cerr << "Could not open the file!" << std::endl; + return rl; } - return rejectList; + + // Create an IStreamWrapper for file input stream + rapidjson::IStreamWrapper isw(inFile); + + rapidjson::Document doc; + if (doc.ParseStream(isw).HasParseError()) { + std::cerr << "Problem parsing " << filename << std::endl; + return rl; + } + auto startRange = getRunDuration(ccdbApi, doc["startRun"].GetInt()); + auto endRange = getRunDuration(ccdbApi, doc["endRun"].GetInt()); + rl.start = startRange.first; + rl.end = endRange.second; + std::cout << "Manual RL validity: " << timeRangeToString(rl.start, rl.end) << std::endl; + auto rlArray = doc["rejectList"].GetArray(); + for (auto& ar : rlArray) { + o2::mid::ColumnData col; + col.deId = ar["deId"].GetInt(); + col.columnId = ar["columnId"].GetInt(); + auto patterns = ar["patterns"].GetArray(); + for (size_t iar = 0; iar < 5; ++iar) { + col.patterns[iar] = std::strtol(patterns[iar].GetString(), NULL, 16); + } + rl.rejectList.emplace_back(col); + std::cout << col << std::endl; + } + return rl; } -/// @brief Builds the reject list for the selected timestamp -/// @param timestamp Timestamp for query -/// @param qcdbUrl QCDB URL -/// @param ccdbUrl CCDB URL -/// @param outCCDBUrl URL of the CCDB where the reject list will be uploaded -/// @return Reject list -std::vector build_rejectlist(long timestamp, const char* qcdbUrl = "http://ali-qcdb-gpn.cern.ch:8083", const char* ccdbUrl = "http://alice-ccdb.cern.ch", const char* outCCDBUrl = "http://localhost:8080") +/// @brief Merges the manual and automatic reject lists +/// @param manualRL Manual reject list from json file +/// @param rls Reject list from QCDB and CCDB +/// @return Merged reject list +std::vector merge_rejectlists(const RejectListStruct& manualRL, const std::vector& rls) { - // Get the QC quality object for the selected timestamp - o2::ccdb::CcdbApi qcdbApi; - qcdbApi.init(qcdbUrl); - o2::ccdb::CcdbApi ccdbApi; - ccdbApi.init(ccdbUrl); - o2::ccdb::CcdbApi outCCDBApi; - outCCDBApi.init(outCCDBUrl); - return build_rejectlist(timestamp, qcdbApi, ccdbApi, outCCDBApi); + std::vector merged; + if (rls.empty()) { + merged.emplace_back(manualRL); + return merged; + } + o2::mid::ColumnDataHandler ch; + RejectListStruct tmpRL; + long lastEnd = manualRL.start; + for (auto& rl : rls) { + std::cout << "Checking rl with validity: " << timeRangeToString(rl.start, rl.end) << std::endl; + if (rl.start >= manualRL.start && rl.end <= manualRL.end) { + // The period is included in the validity of the manual reject list + if (rl.start > lastEnd) { + // Fill holes between periods + tmpRL = manualRL; + tmpRL.start = lastEnd; + tmpRL.end = rl.start; + merged.emplace_back(tmpRL); + std::cout << "Adding manual RL with validity: " << timeRangeToString(tmpRL.start, tmpRL.end) << std::endl; + } + lastEnd = rl.end; + + // merge + ch.clear(); + ch.merge(rl.rejectList); + ch.merge(manualRL.rejectList); + tmpRL = rl; + tmpRL.rejectList = ch.getMerged(); + std::sort(tmpRL.rejectList.begin(), tmpRL.rejectList.end(), [](const o2::mid::ColumnData& col1, const o2::mid::ColumnData& col2) { return o2::mid::getColumnDataUniqueId(col1.deId, col1.columnId) < o2::mid::getColumnDataUniqueId(col2.deId, col2.columnId); }); + merged.emplace_back(tmpRL); + std::cout << "Merging RL with validity: " << timeRangeToString(tmpRL.start, tmpRL.end) << std::endl; + // std::cout << "Before: " << std::endl; + // for (auto& col : rl.rejectList) { + // std::cout << col << std::endl; + // } + // std::cout << "After: " << std::endl; + // for (auto& col : tmpRL.rejectList) { + // std::cout << col << std::endl; + // } + } else { + if (rl.start > manualRL.end && lastEnd < manualRL.end) { + // Close manual period + tmpRL = manualRL; + tmpRL.start = lastEnd; + merged.emplace_back(tmpRL); + std::cout << "Adding manual RL with validity: " << timeRangeToString(tmpRL.start, tmpRL.end) << std::endl; + lastEnd = manualRL.end; + } + // Add current reject list as it is + merged.emplace_back(rl); + std::cout << "Adding RL with validity: " << timeRangeToString(rl.start, rl.end) << std::endl; + } + } + return merged; } /// @brief Builds the reject list in a time range @@ -288,17 +389,38 @@ std::vector build_rejectlist(long timestamp, const char* qc /// @param qcdbUrl QCDB URL /// @param ccdbUrl CCDB URL /// @param outCCDBUrl URL of the CCDB where the reject lists will be uploaded -void build_rejectlist(long start, long end, const char* qcdbUrl = "http://ali-qcdb-gpn.cern.ch:8083", const char* ccdbUrl = "http://alice-ccdb.cern.ch", const char* outCCDBUrl = "http://localhost:8080") +void build_rejectlist(long start, long end, const char* qcdbUrl = "http://ali-qcdb-gpn.cern.ch:8083", const char* ccdbUrl = "http://alice-ccdb.cern.ch", const char* outCCDBUrl = "http://localhost:8080", const char* json_rejectlist = "") { // Query the MID QC quality objects o2::ccdb::CcdbApi qcdbApi; qcdbApi.init(qcdbUrl); o2::ccdb::CcdbApi ccdbApi; ccdbApi.init(ccdbUrl); - o2::ccdb::CcdbApi outCCDBApi; - outCCDBApi.init(outCCDBUrl); + std::vector rls; auto objectsTS = findObjectsTSInPeriod(start, end, qcdbApi, sPathQCQuality.c_str()); for (auto ts : objectsTS) { - build_rejectlist(ts, qcdbApi, ccdbApi, outCCDBApi); + auto rl = build_rejectlist(ts, qcdbApi, ccdbApi); + if (rl.start != rl.end) { + rls.emplace_back(rl); + } + } + + if (!std::string(json_rejectlist).empty()) { + auto rlManual = load_from_json(ccdbApi, json_rejectlist); + rls = merge_rejectlists(rlManual, rls); + } + + o2::ccdb::CcdbApi outCCDBApi; + outCCDBApi.init(outCCDBUrl); + std::map metadata; + for (auto& rl : rls) { + // Ask if you want to upload the object to the CCDB + std::cout << "Upload reject list with validity: " << rl.start << " - " << rl.end << " to " << outCCDBApi.getURL() << "? [y/n]" << std::endl; + std::string answer; + std::cin >> answer; + if (answer == "y") { + std::cout << "Storing RejectList valid from " << rl.start << " to " << rl.end << std::endl; + outCCDBApi.storeAsTFileAny(&rl.rejectList, "MID/Calib/RejectList", metadata, rl.start, rl.end); + } } } \ No newline at end of file From 549537982cb2191c29d0ddacabaa6c241cf723cc Mon Sep 17 00:00:00 2001 From: shahoian Date: Tue, 12 Nov 2024 15:18:22 +0100 Subject: [PATCH 0027/2180] Fix acknowledgment of -h option by HBFUtilsInitializer --- Detectors/Raw/src/HBFUtilsInitializer.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Detectors/Raw/src/HBFUtilsInitializer.cxx b/Detectors/Raw/src/HBFUtilsInitializer.cxx index 1f89d9725b397..e3cc9a8eef414 100644 --- a/Detectors/Raw/src/HBFUtilsInitializer.cxx +++ b/Detectors/Raw/src/HBFUtilsInitializer.cxx @@ -78,7 +78,7 @@ HBFUtilsInitializer::HBFUtilsInitializer(const o2f::ConfigContext& configcontext hbfuInput = optStr; } else if (opt == HBFOpt::ROOT) { rootFileInput = optStr; - } else { + } else if (!helpasked) { LOGP(fatal, "uknown hbfutils-config option {}", optStr); } } From 25896b2f94bea8256d347686f1dc0143ab276826 Mon Sep 17 00:00:00 2001 From: shahoian Date: Wed, 13 Nov 2024 16:33:19 +0100 Subject: [PATCH 0028/2180] TrackStudy stores TPC lowest cluster position --- .../GlobalTrackingStudy/TrackInfoExt.h | 12 ++++- .../study/src/TrackingStudy.cxx | 50 +++++++++---------- 2 files changed, 36 insertions(+), 26 deletions(-) diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackInfoExt.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackInfoExt.h index b988eddfa861f..ea79d5d4a2c92 100644 --- a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackInfoExt.h +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackInfoExt.h @@ -30,6 +30,8 @@ struct TrackInfoExt { DCA dcaTPC{}; VtxTrackIndex gid; MatchInfoTOF infoTOF; + std::array innerTPCPos{}; // innermost cluster position at assigned time + std::array innerTPCPos0{}; // innermost cluster position at nominal time0 float ttime = 0; float ttimeE = 0; float xmin = 0; @@ -44,7 +46,15 @@ struct TrackInfoExt { uint8_t rowMinTPC = 0; uint8_t rowMaxTPC = 0; uint8_t rowCountTPC = 0; - ClassDefNV(TrackInfoExt, 2); + + float getTPCInX() const { return innerTPCPos[0]; } + float getTPCInY() const { return innerTPCPos[1]; } + float getTPCInZ() const { return innerTPCPos[2]; } + float getTPCInX0() const { return innerTPCPos0[0]; } + float getTPCInY0() const { return innerTPCPos0[1]; } + float getTPCInZ0() const { return innerTPCPos0[2]; } + + ClassDefNV(TrackInfoExt, 3); }; } // namespace dataformats diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx index 1e605e308f4ab..5a67bd344f271 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx @@ -248,26 +248,30 @@ void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) auto vdrit = mTPCVDriftHelper.getVDriftObject().getVDrift(); bool tpcTrackOK = recoData.isTrackSourceLoaded(GTrackID::TPC); - auto getTPCClInfo = [&recoData](const o2::tpc::TrackTPC& trc) { + auto fillTPCClInfo = [&recoData, this](const o2::tpc::TrackTPC& trc, o2::dataformats::TrackInfoExt& trExt, float timestampTB = -1e9) { const auto clRefs = recoData.getTPCTracksClusterRefs(); - std::array clinfo = {}; if (recoData.inputsTPCclusters) { uint8_t clSect = 0, clRow = 0, clRowP = -1; uint32_t clIdx = 0; for (int ic = 0; ic < trc.getNClusterReferences(); ic++) { trc.getClusterReference(clRefs, ic, clSect, clRow, clIdx); if (clRow != clRowP) { - clinfo[2]++; + trExt.rowCountTPC++; clRowP = clRow; } } - const auto clRefs = recoData.getTPCTracksClusterRefs(); trc.getClusterReference(clRefs, trc.getNClusterReferences() - 1, clSect, clRow, clIdx); - clinfo[0] = clRow; + trExt.rowMinTPC = clRow; + const auto& clus = recoData.inputsTPCclusters->clusterIndex.clusters[clSect][clRow][clIdx]; + this->mTPCCorrMapsLoader.Transform(clSect, clRow, clus.getPad(), clus.getTime(), trExt.innerTPCPos0[0], trExt.innerTPCPos0[1], trExt.innerTPCPos0[2], trc.getTime0()); // nominal time of the track + if (timestampTB > -1e8) { + this->mTPCCorrMapsLoader.Transform(clSect, clRow, clus.getPad(), clus.getTime(), trExt.innerTPCPos[0], trExt.innerTPCPos[1], trExt.innerTPCPos[2], timestampTB); // time assigned from the global track track + } else { + trExt.innerTPCPos = trExt.innerTPCPos0; + } trc.getClusterReference(clRefs, 0, clSect, clRow, clIdx); - clinfo[1] = clRow; + trExt.rowMaxTPC = clRow; } - return clinfo; }; for (int iv = 0; iv < nv; iv++) { @@ -276,7 +280,6 @@ void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) if (iv != nv - 1) { auto& pve = pveVec[iv]; static_cast(pve) = pvvec[iv]; - // find best matching FT0 signal float bestTimeDiff = 1000, bestTime = -999; int bestFTID = -1; @@ -319,7 +322,6 @@ void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) GTrackID tpcTrID; const o2::tpc::TrackTPC* tpcTr = nullptr; int nclTPC = 0; - std::array tpcClInfo{}; if (dm[DetID::TPC] && tpcTrackOK) { tpcTrID = recoData.getTPCContributorGID(vid); tpcTr = &recoData.getTPCTrack(tpcTrID); @@ -327,7 +329,6 @@ void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) if (nclTPC < mMinTPCClusters) { continue; } - tpcClInfo = getTPCClInfo(*tpcTr); } bool ambig = vid.isAmbiguous(); auto trc = recoData.getTrackParam(vid); @@ -364,11 +365,19 @@ void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) continue; } { - o2::dataformats::DCA dcaTPC; - dcaTPC.set(-999.f, -999.f); + auto& trcExt = trcExtVec.emplace_back(); + recoData.getTrackTime(vid, trcExt.ttime, trcExt.ttimeE); + trcExt.track = trc; + trcExt.dca = dca; + trcExt.gid = vid; + trcExt.xmin = xmin; + trcExt.dcaTPC.set(-999.f, -999.f); + if (tpcTr) { + float tsuse = trcExt.ttime / (8 * o2::constants::lhc::LHCBunchSpacingMUS); if (is == GTrackID::TPC) { - dcaTPC = dca; + trcExt.dcaTPC = dca; + tsuse = -1e9; } else { o2::track::TrackParCov tmpTPC(*tpcTr); if (iv < nv - 1 && is == GTrackID::TPC && tpcTr && !tpcTr->hasBothSidesClusters()) { // for unconstrained TPC tracks correct track Z @@ -378,18 +387,12 @@ void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) } tmpTPC.setZ(tmpTPC.getZ() + corz); } - if (!prop->propagateToDCA(iv == nv - 1 ? vtxDummy : pvvec[iv], tmpTPC, prop->getNominalBz(), 2., o2::base::PropagatorF::MatCorrType::USEMatCorrLUT, &dcaTPC)) { - dcaTPC.set(-999.f, -999.f); + if (!prop->propagateToDCA(iv == nv - 1 ? vtxDummy : pvvec[iv], tmpTPC, prop->getNominalBz(), 2., o2::base::PropagatorF::MatCorrType::USEMatCorrLUT, &trcExt.dcaTPC)) { + trcExt.dcaTPC.set(-999.f, -999.f); } } + fillTPCClInfo(*tpcTr, trcExt, tsuse); } - auto& trcExt = trcExtVec.emplace_back(); - recoData.getTrackTime(vid, trcExt.ttime, trcExt.ttimeE); - trcExt.track = trc; - trcExt.dca = dca; - trcExt.dcaTPC = dcaTPC; - trcExt.gid = vid; - trcExt.xmin = xmin; auto gidRefs = recoData.getSingleDetectorRefs(vid); if (gidRefs[GTrackID::ITS].isIndexSet()) { const auto& itsTr = recoData.getITSTrack(gidRefs[GTrackID::ITS]); @@ -412,9 +415,6 @@ void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) if (gidRefs[GTrackID::TPC].isIndexSet()) { trcExt.q2ptTPC = recoData.getTrackParam(gidRefs[GTrackID::TPC]).getQ2Pt(); trcExt.nClTPC = nclTPC; - trcExt.rowMinTPC = tpcClInfo[0]; - trcExt.rowMaxTPC = tpcClInfo[1]; - trcExt.rowCountTPC = tpcClInfo[2]; } if (gidRefs[GTrackID::ITSTPC].isIndexSet()) { const auto& trTPCITS = recoData.getTPCITSTrack(gidRefs[GTrackID::ITSTPC]); From 4b9ede8b97d5941c353cd4769deb68409e0163be Mon Sep 17 00:00:00 2001 From: shahoian Date: Wed, 13 Nov 2024 17:11:15 +0100 Subject: [PATCH 0029/2180] Protect LT-int.calculation in TRD refit from bad TPC correction --- .../TRD/workflow/src/TRDGlobalTrackingSpec.cxx | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx b/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx index 3773f07ccd1ab..424657ac19426 100644 --- a/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx +++ b/Detectors/TRD/workflow/src/TRDGlobalTrackingSpec.cxx @@ -615,7 +615,6 @@ bool TRDGlobalTracking::refitITSTPCTRDTrack(TrackTRD& trk, float timeTRD, o2::gl LOG(debug) << "TRD refit outwards failed"; return false; } - // refit ITS-TPC-TRD track inwards to innermost ITS cluster // here we also calculate the LT integral for matching to TOF float chi2In = 0.f; @@ -629,6 +628,12 @@ bool TRDGlobalTracking::refitITSTPCTRDTrack(TrackTRD& trk, float timeTRD, o2::gl LOG(debug) << "TPC refit inwards failed"; return false; } + // if for some reason the track was overshoot over the inner field cage, bring it back w/o material correction and LTintegral update + if (trk.getX() < o2::constants::geom::XTPCInnerRef && + !propagator->PropagateToXBxByBz(trk, o2::constants::geom::XTPCInnerRef, o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, o2::base::Propagator::MatCorrType::USEMatCorrNONE)) { + LOG(debug) << "BACK-Propagationto inner boundary failed"; + return false; + } auto posEnd = trk.getXYZGlo(); auto lInt = propagator->estimateLTIncrement(trk, posStart, posEnd); trk.getLTIntegralOut().addStep(lInt, trk.getP2Inv()); @@ -718,7 +723,12 @@ bool TRDGlobalTracking::refitTPCTRDTrack(TrackTRD& trk, float timeTRD, o2::globa if (pileUpOn) { // account pileup time uncertainty in Z errors trk.updateCov(timeZErr, o2::track::CovLabels::kSigZ2); } - + // if for some reason the track was overshoot over the inner field cage, bring it back w/o material correction and LTintegral update + if (trk.getX() < o2::constants::geom::XTPCInnerRef && + !propagator->PropagateToXBxByBz(trk, o2::constants::geom::XTPCInnerRef, o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, o2::base::Propagator::MatCorrType::USEMatCorrNONE)) { + LOG(debug) << "BACK-Propagationto inner boundary failed"; + return false; + } auto posEnd = trk.getXYZGlo(); auto lInt = propagator->estimateLTIncrement(trk, posStart, posEnd); trk.getLTIntegralOut().addStep(lInt, trk.getP2Inv()); From 1fbc2d01eb44dacd021cdc61584f45c732133e7c Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 13 Nov 2024 16:41:19 +0100 Subject: [PATCH 0030/2180] DPL: move writers to plugin This will allow us at some point to remove the dependency on TTree in the framework, hopefully saving memory and allowing us to more easily customize the writing backend. --- Framework/AnalysisSupport/CMakeLists.txt | 1 + .../AnalysisSupport/src/AODWriterHelpers.cxx | 414 +++++++++++++++ .../AnalysisSupport/src/AODWriterHelpers.h | 28 + Framework/AnalysisSupport/src/Plugin.cxx | 17 + .../Core/include/Framework/AnalysisContext.h | 58 +++ .../Framework}/AnalysisSupportHelpers.h | 40 +- .../Core/include/Framework/ConfigContext.h | 15 +- .../include/Framework/runDataProcessing.h | 4 +- Framework/Core/src/AnalysisSupportHelpers.cxx | 484 ++++-------------- Framework/Core/src/ArrowSupport.cxx | 10 +- Framework/Core/src/ConfigContext.cxx | 3 + Framework/Core/src/WorkflowHelpers.cxx | 149 +----- Framework/Core/src/WorkflowHelpers.h | 4 +- Framework/Core/test/Mocking.h | 5 +- .../Core/test/benchmark_WorkflowHelpers.cxx | 3 +- Framework/Core/test/test_OverrideLabels.cxx | 3 +- .../TestWorkflows/src/o2TestHistograms.cxx | 5 +- 17 files changed, 690 insertions(+), 553 deletions(-) create mode 100644 Framework/AnalysisSupport/src/AODWriterHelpers.cxx create mode 100644 Framework/AnalysisSupport/src/AODWriterHelpers.h create mode 100644 Framework/Core/include/Framework/AnalysisContext.h rename Framework/Core/{src => include/Framework}/AnalysisSupportHelpers.h (71%) diff --git a/Framework/AnalysisSupport/CMakeLists.txt b/Framework/AnalysisSupport/CMakeLists.txt index eb5706817704b..5fb1282469711 100644 --- a/Framework/AnalysisSupport/CMakeLists.txt +++ b/Framework/AnalysisSupport/CMakeLists.txt @@ -20,6 +20,7 @@ o2_add_library(FrameworkAnalysisSupport SOURCES src/Plugin.cxx src/DataInputDirector.cxx src/AODJAlienReaderHelpers.cxx + src/AODWriterHelpers.cxx PRIVATE_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_LIST_DIR}/src PUBLIC_LINK_LIBRARIES O2::Framework ${EXTRA_TARGETS} ROOT::TreePlayer) diff --git a/Framework/AnalysisSupport/src/AODWriterHelpers.cxx b/Framework/AnalysisSupport/src/AODWriterHelpers.cxx new file mode 100644 index 0000000000000..fa10d4661f537 --- /dev/null +++ b/Framework/AnalysisSupport/src/AODWriterHelpers.cxx @@ -0,0 +1,414 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not 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/ConfigContext.h" +#include "Framework/ControlService.h" +#include "AODWriterHelpers.h" +#include "Framework/OutputObjHeader.h" +#include "Framework/EndOfStreamContext.h" +#include "Framework/ProcessingContext.h" +#include "Framework/InitContext.h" +#include "Framework/CallbackService.h" +#include "Framework/AnalysisSupportHelpers.h" +#include "Framework/TableConsumer.h" +#include "Framework/DataOutputDirector.h" +#include "Framework/TableTreeHelpers.h" + +#include +#include +#include +#include +#include +#include + +namespace o2::framework::writers +{ + +struct InputObjectRoute { + std::string name; + uint32_t uniqueId; + std::string directory; + uint32_t taskHash; + OutputObjHandlingPolicy policy; + OutputObjSourceType sourceType; +}; + +struct InputObject { + TClass* kind = nullptr; + void* obj = nullptr; + std::string name; + int count = -1; +}; + +const static std::unordered_map ROOTfileNames = {{OutputObjHandlingPolicy::AnalysisObject, "AnalysisResults.root"}, + {OutputObjHandlingPolicy::QAObject, "QAResults.root"}}; + +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 { + LOGP(debug, "======== getGlobalAODSink::Init =========="); + + // find out if any table needs to be saved + bool hasOutputsToWrite = false; + for (auto& outobj : outputInputs) { + auto ds = dod->getDataOutputDescriptors(outobj); + if (ds.size() > 0) { + hasOutputsToWrite = true; + break; + } + } + + // if nothing needs to be saved then return a trivial functor + // this happens when nothing needs to be saved but there are dangling outputs + if (!hasOutputsToWrite) { + return [](ProcessingContext&) mutable -> void { + static bool once = false; + if (!once) { + LOG(info) << "No AODs to be saved."; + once = true; + } + }; + } + + // end of data functor is called at the end of the data stream + auto endofdatacb = [dod](EndOfStreamContext& context) { + dod->closeDataFiles(); + context.services().get().readyToQuit(QuitRequest::Me); + }; + + auto& callbacks = ic.services().get(); + callbacks.set(endofdatacb); + + // prepare map(startTime, tfNumber) + std::map tfNumbers; + std::map tfFilenames; + + std::vector aodMetaDataKeys; + std::vector aodMetaDataVals; + + // this functor is called once per time frame + return [dod, tfNumbers, tfFilenames, aodMetaDataKeys, aodMetaDataVals, compressionLevel](ProcessingContext& pc) mutable -> void { + LOGP(debug, "======== getGlobalAODSink::processing =========="); + LOGP(debug, " processing data set with {} entries", pc.inputs().size()); + + // return immediately if pc.inputs() is empty. This should never happen! + if (pc.inputs().size() == 0) { + LOGP(info, "No inputs available!"); + return; + } + + // update tfNumbers + uint64_t startTime = 0; + uint64_t tfNumber = 0; + auto ref = pc.inputs().get("tfn"); + if (ref.spec && ref.payload) { + startTime = DataRefUtils::getHeader(ref)->startTime; + tfNumber = pc.inputs().get("tfn"); + tfNumbers.insert(std::pair(startTime, tfNumber)); + } + // update tfFilenames + std::string aodInputFile; + auto ref2 = pc.inputs().get("tff"); + if (ref2.spec && ref2.payload) { + startTime = DataRefUtils::getHeader(ref2)->startTime; + aodInputFile = pc.inputs().get("tff"); + tfFilenames.insert(std::pair(startTime, aodInputFile)); + } + + // close all output files if one has reached size limit + dod->checkFileSizes(); + + // loop over the DataRefs which are contained in pc.inputs() + for (const auto& ref : pc.inputs()) { + if (!ref.spec) { + LOGP(debug, "Invalid input will be skipped!"); + continue; + } + + // get metadata + if (DataSpecUtils::partialMatch(*ref.spec, header::DataDescription("AODMetadataKeys"))) { + aodMetaDataKeys = pc.inputs().get>(ref.spec->binding); + } + if (DataSpecUtils::partialMatch(*ref.spec, header::DataDescription("AODMetadataVals"))) { + aodMetaDataVals = pc.inputs().get>(ref.spec->binding); + } + + // skip non-AOD refs + if (!DataSpecUtils::partialMatch(*ref.spec, writableAODOrigins)) { + continue; + } + startTime = DataRefUtils::getHeader(ref)->startTime; + + // does this need to be saved? + auto dh = DataRefUtils::getHeader(ref); + auto tableName = dh->dataDescription.as(); + auto ds = dod->getDataOutputDescriptors(*dh); + if (ds.empty()) { + continue; + } + + // get TF number from startTime + auto it = tfNumbers.find(startTime); + if (it != tfNumbers.end()) { + tfNumber = (it->second / dod->getNumberTimeFramesToMerge()) * dod->getNumberTimeFramesToMerge(); + } else { + LOGP(fatal, "No time frame number found for output with start time {}", startTime); + throw std::runtime_error("Processing is stopped!"); + } + // get aod input file from startTime + auto it2 = tfFilenames.find(startTime); + if (it2 != tfFilenames.end()) { + aodInputFile = it2->second; + } + + // get the TableConsumer and corresponding arrow table + auto msg = pc.inputs().get(ref.spec->binding); + if (msg.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(); + if (!table->Validate().ok()) { + LOGP(warning, "The table \"{}\" is not valid and will not be saved!", tableName); + continue; + } + if (table->schema()->fields().empty()) { + LOGP(debug, "The table \"{}\" is empty but will be saved anyway!", tableName); + } + + // loop over all DataOutputDescriptors + // a table can be saved in multiple ways + // e.g. different selections of columns to different files + for (auto d : ds) { + auto fileAndFolder = dod->getFileFolder(d, tfNumber, aodInputFile, compressionLevel); + auto treename = fileAndFolder.folderName + "/" + d->treename; + TableToTree ta2tr(table, + fileAndFolder.file, + treename.c_str()); + + // update metadata + if (fileAndFolder.file->FindObjectAny("metaData")) { + LOGF(debug, "Metadata: target file %s already has metadata, preserving it", fileAndFolder.file->GetName()); + } else if (!aodMetaDataKeys.empty() && !aodMetaDataVals.empty()) { + TMap aodMetaDataMap; + for (uint32_t imd = 0; imd < aodMetaDataKeys.size(); imd++) { + aodMetaDataMap.Add(new TObjString(aodMetaDataKeys[imd]), new TObjString(aodMetaDataVals[imd])); + } + fileAndFolder.file->WriteObject(&aodMetaDataMap, "metaData", "Overwrite"); + } + + if (!d->colnames.empty()) { + for (auto& cn : d->colnames) { + auto idx = table->schema()->GetFieldIndex(cn); + auto col = table->column(idx); + auto field = table->schema()->field(idx); + if (idx != -1) { + ta2tr.addBranch(col, field); + } + } + } else { + ta2tr.addAllBranches(); + } + ta2tr.process(); + } + } + }; + } + + }; +} + +AlgorithmSpec AODWriterHelpers::getOutputObjHistWriter(ConfigContext const& ctx) +{ + auto& ac = ctx.services().get(); + auto tskmap = ac.outTskMap; + auto objmap = ac.outObjHistMap; + + return AlgorithmSpec{[objmap, tskmap](InitContext& ic) -> std::function { + auto& callbacks = ic.services().get(); + auto inputObjects = std::make_shared>>(); + + static TFile* f[OutputObjHandlingPolicy::numPolicies]; + for (auto i = 0u; i < OutputObjHandlingPolicy::numPolicies; ++i) { + f[i] = nullptr; + } + + static std::string currentDirectory = ""; + static std::string currentFile = ""; + + auto endofdatacb = [inputObjects](EndOfStreamContext& context) { + LOG(debug) << "Writing merged objects and histograms to file"; + if (inputObjects->empty()) { + LOG(error) << "Output object map is empty!"; + context.services().get().readyToQuit(QuitRequest::Me); + return; + } + for (auto i = 0u; i < OutputObjHandlingPolicy::numPolicies; ++i) { + if (f[i] != nullptr) { + f[i]->Close(); + } + } + LOG(debug) << "All outputs merged in their respective target files"; + context.services().get().readyToQuit(QuitRequest::Me); + }; + + callbacks.set(endofdatacb); + return [inputObjects, objmap, tskmap](ProcessingContext& pc) mutable -> void { + auto const& ref = pc.inputs().get("x"); + if (!ref.header) { + LOG(error) << "Header not found"; + return; + } + if (!ref.payload) { + LOG(error) << "Payload not found"; + return; + } + auto datah = o2::header::get(ref.header); + if (!datah) { + LOG(error) << "No data header in stack"; + return; + } + + auto objh = o2::header::get(ref.header); + if (!objh) { + LOG(error) << "No output object header in stack"; + return; + } + + InputObject obj; + FairInputTBuffer tm(const_cast(ref.payload), static_cast(datah->payloadSize)); + tm.InitMap(); + obj.kind = tm.ReadClass(); + tm.SetBufferOffset(0); + tm.ResetMap(); + if (obj.kind == nullptr) { + LOG(error) << "Cannot read class info from buffer."; + return; + } + + auto policy = objh->mPolicy; + auto sourceType = objh->mSourceType; + auto hash = objh->mTaskHash; + + obj.obj = tm.ReadObjectAny(obj.kind); + auto* named = static_cast(obj.obj); + obj.name = named->GetName(); + auto hpos = std::find_if(tskmap.begin(), tskmap.end(), [&](auto&& x) { return x.id == hash; }); + if (hpos == tskmap.end()) { + LOG(error) << "No task found for hash " << hash; + return; + } + auto taskname = hpos->name; + auto opos = std::find_if(objmap.begin(), objmap.end(), [&](auto&& x) { return x.id == hash; }); + if (opos == objmap.end()) { + LOG(error) << "No object list found for task " << taskname << " (hash=" << hash << ")"; + return; + } + auto objects = opos->bindings; + if (std::find(objects.begin(), objects.end(), obj.name) == objects.end()) { + LOG(error) << "No object " << obj.name << " in map for task " << taskname; + return; + } + auto nameHash = runtime_hash(obj.name.c_str()); + InputObjectRoute key{obj.name, nameHash, taskname, hash, policy, sourceType}; + auto existing = std::find_if(inputObjects->begin(), inputObjects->end(), [&](auto&& x) { return (x.first.uniqueId == nameHash) && (x.first.taskHash == hash); }); + // If it's the first one, we just add it to the list. + if (existing == inputObjects->end()) { + obj.count = objh->mPipelineSize; + inputObjects->push_back(std::make_pair(key, obj)); + existing = inputObjects->end() - 1; + } else { + obj.count = existing->second.count; + // Otherwise, we merge it with the existing one. + auto merger = existing->second.kind->GetMerge(); + if (!merger) { + LOG(error) << "Already one unmergeable object found for " << obj.name; + return; + } + TList coll; + coll.Add(static_cast(obj.obj)); + merger(existing->second.obj, &coll, nullptr); + } + // We expect as many objects as the pipeline size, for + // a given object name and task hash. + existing->second.count -= 1; + + if (existing->second.count != 0) { + return; + } + // Write the object here. + auto route = existing->first; + auto entry = existing->second; + auto file = ROOTfileNames.find(route.policy); + if (file == ROOTfileNames.end()) { + return; + } + auto filename = file->second; + if (f[route.policy] == nullptr) { + f[route.policy] = TFile::Open(filename.c_str(), "RECREATE"); + } + auto nextDirectory = route.directory; + if ((nextDirectory != currentDirectory) || (filename != currentFile)) { + if (!f[route.policy]->FindKey(nextDirectory.c_str())) { + f[route.policy]->mkdir(nextDirectory.c_str()); + } + currentDirectory = nextDirectory; + currentFile = filename; + } + + // translate the list-structure created by the registry into a directory structure within the file + std::function writeListToFile; + writeListToFile = [&](TList* list, TDirectory* parentDir) { + TIter next(list); + TObject* object = nullptr; + while ((object = next())) { + if (object->InheritsFrom(TList::Class())) { + writeListToFile(static_cast(object), parentDir->mkdir(object->GetName(), object->GetName(), true)); + } else { + parentDir->WriteObjectAny(object, object->Class(), object->GetName()); + auto* written = list->Remove(object); + delete written; + } + } + }; + + TDirectory* currentDir = f[route.policy]->GetDirectory(currentDirectory.c_str()); + if (route.sourceType == OutputObjSourceType::HistogramRegistrySource) { + auto* outputList = static_cast(entry.obj); + outputList->SetOwner(false); + + // if registry should live in dedicated folder a TNamed object is appended to the list + if (outputList->Last() && outputList->Last()->IsA() == TNamed::Class()) { + delete outputList->Last(); + outputList->RemoveLast(); + currentDir = currentDir->mkdir(outputList->GetName(), outputList->GetName(), true); + } + + writeListToFile(outputList, currentDir); + outputList->SetOwner(); + delete outputList; + entry.obj = nullptr; + } else { + currentDir->WriteObjectAny(entry.obj, entry.kind, entry.name.c_str()); + delete (TObject*)entry.obj; + entry.obj = nullptr; + } + }; + }}; +} +} // namespace o2::framework::writers diff --git a/Framework/AnalysisSupport/src/AODWriterHelpers.h b/Framework/AnalysisSupport/src/AODWriterHelpers.h new file mode 100644 index 0000000000000..7ae59a5cf3b01 --- /dev/null +++ b/Framework/AnalysisSupport/src/AODWriterHelpers.h @@ -0,0 +1,28 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#ifndef O2_FRAMEWORK_AODROOTWRITERHELPERS_H_ +#define O2_FRAMEWORK_AODROOTWRITERHELPERS_H_ + +#include "Framework/AlgorithmSpec.h" +#include + +namespace o2::framework::writers +{ + +struct AODWriterHelpers { + static AlgorithmSpec getOutputObjHistWriter(ConfigContext const& context); + static AlgorithmSpec getOutputTTreeWriter(ConfigContext const& context); +}; + +} // namespace o2::framework::writers + +#endif // O2_FRAMEWORK_AODROOTWRITERHELPERS_H_ diff --git a/Framework/AnalysisSupport/src/Plugin.cxx b/Framework/AnalysisSupport/src/Plugin.cxx index bba3499286e08..52435375d7e9e 100644 --- a/Framework/AnalysisSupport/src/Plugin.cxx +++ b/Framework/AnalysisSupport/src/Plugin.cxx @@ -16,6 +16,7 @@ #include "Framework/Capability.h" #include "Framework/Signpost.h" #include "AODJAlienReaderHelpers.h" +#include "AODWriterHelpers.h" #include #include #include @@ -33,6 +34,20 @@ struct ROOTFileReader : o2::framework::AlgorithmPlugin { } }; +struct ROOTObjWriter : o2::framework::AlgorithmPlugin { + o2::framework::AlgorithmSpec create(o2::framework::ConfigContext const& config) override + { + return o2::framework::writers::AODWriterHelpers::getOutputObjHistWriter(config); + } +}; + +struct ROOTTTreeWriter : o2::framework::AlgorithmPlugin { + o2::framework::AlgorithmSpec create(o2::framework::ConfigContext const& config) override + { + return o2::framework::writers::AODWriterHelpers::getOutputTTreeWriter(config); + } +}; + using namespace o2::framework; struct RunSummary : o2::framework::ServicePlugin { o2::framework::ServiceSpec* create() final @@ -211,6 +226,8 @@ struct DiscoverMetadataInAOD : o2::framework::ConfigDiscoveryPlugin { DEFINE_DPL_PLUGINS_BEGIN DEFINE_DPL_PLUGIN_INSTANCE(ROOTFileReader, CustomAlgorithm); +DEFINE_DPL_PLUGIN_INSTANCE(ROOTObjWriter, CustomAlgorithm); +DEFINE_DPL_PLUGIN_INSTANCE(ROOTTTreeWriter, CustomAlgorithm); DEFINE_DPL_PLUGIN_INSTANCE(RunSummary, CustomService); DEFINE_DPL_PLUGIN_INSTANCE(DiscoverMetadataInAOD, ConfigDiscovery); DEFINE_DPL_PLUGINS_END diff --git a/Framework/Core/include/Framework/AnalysisContext.h b/Framework/Core/include/Framework/AnalysisContext.h new file mode 100644 index 0000000000000..0f62f952d0aaa --- /dev/null +++ b/Framework/Core/include/Framework/AnalysisContext.h @@ -0,0 +1,58 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#ifndef O2_FRAMEWORK_ANALYSISCONTEXT_H_ +#define O2_FRAMEWORK_ANALYSISCONTEXT_H_ + +#include +#include "Framework/InputSpec.h" +#include "Framework/OutputSpec.h" + +namespace o2::framework +{ +class DataOutputDirector; + +struct OutputTaskInfo { + uint32_t id; + std::string name; +}; + +struct OutputObjectInfo { + uint32_t id; + std::vector bindings; +}; + +// +struct AnalysisContext { + std::vector requestedAODs; + std::vector providedAODs; + std::vector requestedDYNs; + std::vector providedDYNs; + std::vector requestedIDXs; + std::vector providedOutputObjHist; + std::vector spawnerInputs; + + // Needed to created the hist writer + std::vector outTskMap; + std::vector outObjHistMap; + + // Needed to create the output director + std::vector outputsInputs; + std::vector isDangling; + + // Needed to create the aod writer + std::vector outputsInputsAOD; +}; +} // namespace o2::framework + +extern template class std::vector; +extern template class std::vector; + +#endif // O2_FRAMEWORK_ANALYSISCONTEXT_H_ diff --git a/Framework/Core/src/AnalysisSupportHelpers.h b/Framework/Core/include/Framework/AnalysisSupportHelpers.h similarity index 71% rename from Framework/Core/src/AnalysisSupportHelpers.h rename to Framework/Core/include/Framework/AnalysisSupportHelpers.h index ba5bcedb4bc67..4ae601dc9e4a2 100644 --- a/Framework/Core/src/AnalysisSupportHelpers.h +++ b/Framework/Core/include/Framework/AnalysisSupportHelpers.h @@ -14,6 +14,7 @@ #include "Framework/OutputSpec.h" #include "Framework/InputSpec.h" #include "Framework/DataProcessorSpec.h" +#include "Framework/AnalysisContext.h" #include "Headers/DataHeader.h" #include @@ -24,36 +25,7 @@ static constexpr std::array extendedAODOrigins{header::Da static constexpr std::array writableAODOrigins{header::DataOrigin{"AOD"}, header::DataOrigin{"AOD1"}, header::DataOrigin{"AOD2"}, header::DataOrigin{"DYN"}}; class DataOutputDirector; - -struct OutputTaskInfo { - uint32_t id; - std::string name; -}; - -struct OutputObjectInfo { - uint32_t id; - std::vector bindings; -}; -} // namespace o2::framework - -extern template class std::vector; -extern template class std::vector; - -namespace o2::framework -{ -// -struct AnalysisContext { - std::vector requestedAODs; - std::vector providedAODs; - std::vector requestedDYNs; - std::vector providedDYNs; - std::vector requestedIDXs; - std::vector providedOutputObjHist; - std::vector spawnerInputs; - - std::vector outTskMap; - std::vector outObjHistMap; -}; +class ConfigContext; // Helper class to be moved in the AnalysisSupport plugin at some point struct AnalysisSupportHelpers { @@ -74,11 +46,11 @@ struct AnalysisSupportHelpers { /// Match all inputs of kind ATSK and write them to a ROOT file, /// one root file per originating task. - static DataProcessorSpec getOutputObjHistSink(std::vector const& objmap, - std::vector const& tskmap); + static DataProcessorSpec getOutputObjHistSink(ConfigContext const&); /// writes inputs of kind AOD to file - static DataProcessorSpec getGlobalAODSink(std::shared_ptr dod, - std::vector const& outputInputs, int compression); + static DataProcessorSpec getGlobalAODSink(ConfigContext const&); + /// Get the data director + static std::shared_ptr getDataOutputDirector(ConfigContext const& ctx); }; }; // namespace o2::framework diff --git a/Framework/Core/include/Framework/ConfigContext.h b/Framework/Core/include/Framework/ConfigContext.h index 5790699fe68bb..87259f0519915 100644 --- a/Framework/Core/include/Framework/ConfigContext.h +++ b/Framework/Core/include/Framework/ConfigContext.h @@ -8,11 +8,11 @@ // In applying this license CERN does 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_CONFIG_CONTEXT_H -#define FRAMEWORK_CONFIG_CONTEXT_H +#ifndef O2_FRAMEWORK_CONFIG_CONTEXT_H_ +#define O2_FRAMEWORK_CONFIG_CONTEXT_H_ #include "Framework/ConfigParamRegistry.h" -#include "Framework/ServiceRegistry.h" +#include "Framework/ServiceRegistryRef.h" namespace o2::framework { @@ -23,9 +23,10 @@ namespace o2::framework class ConfigContext { public: - ConfigContext(ConfigParamRegistry& options, int argc, char** argv) : mOptions{options}, mArgc{argc}, mArgv{argv} {} + ConfigContext(ConfigParamRegistry& options, ServiceRegistryRef services, int argc, char** argv); [[nodiscard]] ConfigParamRegistry& options() const { return mOptions; } + [[nodiscard]] ServiceRegistryRef services() const { return mServices; } [[nodiscard]] bool helpOnCommandLine() const; @@ -34,11 +35,13 @@ class ConfigContext private: ConfigParamRegistry& mOptions; + + ServiceRegistryRef mServices; // additionaly keep information about the original command line int mArgc = 0; char** mArgv = nullptr; }; -} // namespace o2 +} // namespace o2::framework -#endif +#endif // O2_FRAMEWORK_CONFIG_CONTEXT_H_ diff --git a/Framework/Core/include/Framework/runDataProcessing.h b/Framework/Core/include/Framework/runDataProcessing.h index eee4c4b6583d3..8293bf0cf7039 100644 --- a/Framework/Core/include/Framework/runDataProcessing.h +++ b/Framework/Core/include/Framework/runDataProcessing.h @@ -30,6 +30,7 @@ #include "Framework/CheckTypes.h" #include "Framework/StructToTuple.h" #include "Framework/ConfigParamDiscovery.h" +#include "ServiceRegistryRef.h" #include namespace o2::framework @@ -198,7 +199,8 @@ int mainNoCatch(int argc, char** argv) workflowOptions.push_back(extra); } - ConfigContext configContext(workflowOptionsRegistry, argc, argv); + ServiceRegistry configRegistry; + ConfigContext configContext(workflowOptionsRegistry, ServiceRegistryRef{configRegistry}, argc, argv); o2::framework::WorkflowSpec specs = defineDataProcessing(configContext); overrideCloning(configContext, specs); overridePipeline(configContext, specs); diff --git a/Framework/Core/src/AnalysisSupportHelpers.cxx b/Framework/Core/src/AnalysisSupportHelpers.cxx index e949f27a6eed6..eb17566fd6d31 100644 --- a/Framework/Core/src/AnalysisSupportHelpers.cxx +++ b/Framework/Core/src/AnalysisSupportHelpers.cxx @@ -9,18 +9,16 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "AnalysisSupportHelpers.h" +#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/TableTreeHelpers.h" - -#include "TFile.h" -#include "TTree.h" -#include "TMap.h" -#include "TObjString.h" +#include "Framework/PluginManager.h" +#include "Framework/ConfigContext.h" +#include "WorkflowHelpers.h" template class std::vector; template class std::vector; @@ -28,21 +26,105 @@ template class std::vector; namespace o2::framework { -struct InputObjectRoute { - std::string name; - uint32_t uniqueId; - std::string directory; - uint32_t taskHash; - OutputObjHandlingPolicy policy; - OutputObjSourceType sourceType; -}; +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; + + std::shared_ptr dod = std::make_shared(); + + // analyze options and take actions accordingly + // default values + std::string rdn, resdir("./"); + std::string fnb, fnbase("AnalysisResults_trees"); + float mfs, maxfilesize(-1.); + std::string fmo, filemode("RECREATE"); + int ntfm, ntfmerge = 1; + + // values from json + if (options.isSet("aod-writer-json")) { + auto fnjson = options.get("aod-writer-json"); + if (!fnjson.empty()) { + std::tie(rdn, fnb, fmo, mfs, ntfm) = dod->readJson(fnjson); + if (!rdn.empty()) { + resdir = rdn; + } + if (!fnb.empty()) { + fnbase = fnb; + } + if (!fmo.empty()) { + filemode = fmo; + } + if (mfs > 0.) { + maxfilesize = mfs; + } + if (ntfm > 0) { + ntfmerge = ntfm; + } + } + } + + // values from command line options, information from json is overwritten + if (options.isSet("aod-writer-resdir")) { + rdn = options.get("aod-writer-resdir"); + if (!rdn.empty()) { + resdir = rdn; + } + } + if (options.isSet("aod-writer-resfile")) { + fnb = options.get("aod-writer-resfile"); + if (!fnb.empty()) { + fnbase = fnb; + } + } + if (options.isSet("aod-writer-resmode")) { + fmo = options.get("aod-writer-resmode"); + if (!fmo.empty()) { + filemode = fmo; + } + } + if (options.isSet("aod-writer-maxfilesize")) { + mfs = options.get("aod-writer-maxfilesize"); + if (mfs > 0) { + maxfilesize = mfs; + } + } + if (options.isSet("aod-writer-ntfmerge")) { + ntfm = options.get("aod-writer-ntfmerge"); + if (ntfm > 0) { + ntfmerge = ntfm; + } + } + // parse the keepString + if (options.isSet("aod-writer-keep")) { + auto keepString = options.get("aod-writer-keep"); + if (!keepString.empty()) { + dod->reset(); + std::string d("dangling"); + if (d.find(keepString) == 0) { + // use the dangling outputs + std::vector danglingOutputs; + for (auto ii = 0u; ii < OutputsInputs.size(); ii++) { + if (DataSpecUtils::partialMatch(OutputsInputs[ii], writableAODOrigins) && isDangling[ii]) { + danglingOutputs.emplace_back(OutputsInputs[ii]); + } + } + dod->readSpecs(danglingOutputs); + } else { + // use the keep string + dod->readString(keepString); + } + } + } + dod->setResultDir(resdir); + dod->setFilenameBase(fnbase); + dod->setFileMode(filemode); + dod->setMaximumFileSize(maxfilesize); + dod->setNumberTimeFramesToMerge(ntfmerge); -struct InputObject { - TClass* kind = nullptr; - void* obj = nullptr; - std::string name; - int count = -1; -}; + return dod; +} void AnalysisSupportHelpers::addMissingOutputsToReader(std::vector const& providedOutputs, std::vector const& requestedInputs, @@ -125,191 +207,16 @@ void AnalysisSupportHelpers::addMissingOutputsToBuilder(std::vector c } } -const static std::unordered_map ROOTfileNames = {{OutputObjHandlingPolicy::AnalysisObject, "AnalysisResults.root"}, - {OutputObjHandlingPolicy::QAObject, "QAResults.root"}}; - // ============================================================================= -DataProcessorSpec AnalysisSupportHelpers::getOutputObjHistSink(std::vector const& objmap, std::vector const& tskmap) +DataProcessorSpec AnalysisSupportHelpers::getOutputObjHistSink(ConfigContext const& ctx) { - auto writerFunction = [objmap, tskmap](InitContext& ic) -> std::function { - auto& callbacks = ic.services().get(); - auto inputObjects = std::make_shared>>(); - - static TFile* f[OutputObjHandlingPolicy::numPolicies]; - for (auto i = 0u; i < OutputObjHandlingPolicy::numPolicies; ++i) { - f[i] = nullptr; - } - - static std::string currentDirectory = ""; - static std::string currentFile = ""; - - auto endofdatacb = [inputObjects](EndOfStreamContext& context) { - LOG(debug) << "Writing merged objects and histograms to file"; - if (inputObjects->empty()) { - LOG(error) << "Output object map is empty!"; - context.services().get().readyToQuit(QuitRequest::Me); - return; - } - for (auto i = 0u; i < OutputObjHandlingPolicy::numPolicies; ++i) { - if (f[i] != nullptr) { - f[i]->Close(); - } - } - LOG(debug) << "All outputs merged in their respective target files"; - context.services().get().readyToQuit(QuitRequest::Me); - }; - - callbacks.set(endofdatacb); - return [inputObjects, objmap, tskmap](ProcessingContext& pc) mutable -> void { - auto const& ref = pc.inputs().get("x"); - if (!ref.header) { - LOG(error) << "Header not found"; - return; - } - if (!ref.payload) { - LOG(error) << "Payload not found"; - return; - } - auto datah = o2::header::get(ref.header); - if (!datah) { - LOG(error) << "No data header in stack"; - return; - } - - auto objh = o2::header::get(ref.header); - if (!objh) { - LOG(error) << "No output object header in stack"; - return; - } - - InputObject obj; - FairInputTBuffer tm(const_cast(ref.payload), static_cast(datah->payloadSize)); - tm.InitMap(); - obj.kind = tm.ReadClass(); - tm.SetBufferOffset(0); - tm.ResetMap(); - if (obj.kind == nullptr) { - LOG(error) << "Cannot read class info from buffer."; - return; - } - - auto policy = objh->mPolicy; - auto sourceType = objh->mSourceType; - auto hash = objh->mTaskHash; - - obj.obj = tm.ReadObjectAny(obj.kind); - auto* named = static_cast(obj.obj); - obj.name = named->GetName(); - auto hpos = std::find_if(tskmap.begin(), tskmap.end(), [&](auto&& x) { return x.id == hash; }); - if (hpos == tskmap.end()) { - LOG(error) << "No task found for hash " << hash; - return; - } - auto taskname = hpos->name; - auto opos = std::find_if(objmap.begin(), objmap.end(), [&](auto&& x) { return x.id == hash; }); - if (opos == objmap.end()) { - LOG(error) << "No object list found for task " << taskname << " (hash=" << hash << ")"; - return; - } - auto objects = opos->bindings; - if (std::find(objects.begin(), objects.end(), obj.name) == objects.end()) { - LOG(error) << "No object " << obj.name << " in map for task " << taskname; - return; - } - auto nameHash = runtime_hash(obj.name.c_str()); - InputObjectRoute key{obj.name, nameHash, taskname, hash, policy, sourceType}; - auto existing = std::find_if(inputObjects->begin(), inputObjects->end(), [&](auto&& x) { return (x.first.uniqueId == nameHash) && (x.first.taskHash == hash); }); - // If it's the first one, we just add it to the list. - if (existing == inputObjects->end()) { - obj.count = objh->mPipelineSize; - inputObjects->push_back(std::make_pair(key, obj)); - existing = inputObjects->end() - 1; - } else { - obj.count = existing->second.count; - // Otherwise, we merge it with the existing one. - auto merger = existing->second.kind->GetMerge(); - if (!merger) { - LOG(error) << "Already one unmergeable object found for " << obj.name; - return; - } - TList coll; - coll.Add(static_cast(obj.obj)); - merger(existing->second.obj, &coll, nullptr); - } - // We expect as many objects as the pipeline size, for - // a given object name and task hash. - existing->second.count -= 1; - - if (existing->second.count != 0) { - return; - } - // Write the object here. - auto route = existing->first; - auto entry = existing->second; - auto file = ROOTfileNames.find(route.policy); - if (file == ROOTfileNames.end()) { - return; - } - auto filename = file->second; - if (f[route.policy] == nullptr) { - f[route.policy] = TFile::Open(filename.c_str(), "RECREATE"); - } - auto nextDirectory = route.directory; - if ((nextDirectory != currentDirectory) || (filename != currentFile)) { - if (!f[route.policy]->FindKey(nextDirectory.c_str())) { - f[route.policy]->mkdir(nextDirectory.c_str()); - } - currentDirectory = nextDirectory; - currentFile = filename; - } - - // translate the list-structure created by the registry into a directory structure within the file - std::function writeListToFile; - writeListToFile = [&](TList* list, TDirectory* parentDir) { - TIter next(list); - TObject* object = nullptr; - while ((object = next())) { - if (object->InheritsFrom(TList::Class())) { - writeListToFile(static_cast(object), parentDir->mkdir(object->GetName(), object->GetName(), true)); - } else { - parentDir->WriteObjectAny(object, object->Class(), object->GetName()); - auto* written = list->Remove(object); - delete written; - } - } - }; - - TDirectory* currentDir = f[route.policy]->GetDirectory(currentDirectory.c_str()); - if (route.sourceType == OutputObjSourceType::HistogramRegistrySource) { - auto* outputList = static_cast(entry.obj); - outputList->SetOwner(false); - - // if registry should live in dedicated folder a TNamed object is appended to the list - if (outputList->Last() && outputList->Last()->IsA() == TNamed::Class()) { - delete outputList->Last(); - outputList->RemoveLast(); - currentDir = currentDir->mkdir(outputList->GetName(), outputList->GetName(), true); - } - - writeListToFile(outputList, currentDir); - outputList->SetOwner(); - delete outputList; - entry.obj = nullptr; - } else { - currentDir->WriteObjectAny(entry.obj, entry.kind, entry.name.c_str()); - delete (TObject*)entry.obj; - entry.obj = nullptr; - } - }; - }; - - char const* name = "internal-dpl-aod-global-analysis-file-sink"; // Lifetime is sporadic because we do not ask each analysis task to send its // results every timeframe. DataProcessorSpec spec{ - .name = name, + .name = "internal-dpl-aod-global-analysis-file-sink", .inputs = {InputSpec("x", DataSpecUtils::dataDescriptorMatcherFrom(header::DataOrigin{"ATSK"}), Lifetime::Sporadic)}, - .algorithm = {writerFunction}, + .outputs = {}, + .algorithm = PluginManager::loadAlgorithmFromPlugin("O2FrameworkAnalysisSupport", "ROOTObjWriter", ctx), }; return spec; @@ -317,188 +224,17 @@ DataProcessorSpec AnalysisSupportHelpers::getOutputObjHistSink(std::vector dod, - std::vector const& outputInputs, int compressionLevel) + AnalysisSupportHelpers::getGlobalAODSink(ConfigContext const& ctx) { - - auto writerFunction = [dod, outputInputs, compressionLevel](InitContext& ic) -> std::function { - LOGP(debug, "======== getGlobalAODSink::Init =========="); - - // find out if any table needs to be saved - bool hasOutputsToWrite = false; - for (auto& outobj : outputInputs) { - auto ds = dod->getDataOutputDescriptors(outobj); - if (ds.size() > 0) { - hasOutputsToWrite = true; - break; - } - } - - // if nothing needs to be saved then return a trivial functor - // this happens when nothing needs to be saved but there are dangling outputs - if (!hasOutputsToWrite) { - return [](ProcessingContext&) mutable -> void { - static bool once = false; - if (!once) { - LOG(info) << "No AODs to be saved."; - once = true; - } - }; - } - - // end of data functor is called at the end of the data stream - auto endofdatacb = [dod](EndOfStreamContext& context) { - dod->closeDataFiles(); - context.services().get().readyToQuit(QuitRequest::Me); - }; - - auto& callbacks = ic.services().get(); - callbacks.set(endofdatacb); - - // prepare map(startTime, tfNumber) - std::map tfNumbers; - std::map tfFilenames; - - std::vector aodMetaDataKeys; - std::vector aodMetaDataVals; - - // this functor is called once per time frame - return [dod, tfNumbers, tfFilenames, aodMetaDataKeys, aodMetaDataVals, compressionLevel](ProcessingContext& pc) mutable -> void { - LOGP(debug, "======== getGlobalAODSink::processing =========="); - LOGP(debug, " processing data set with {} entries", pc.inputs().size()); - - // return immediately if pc.inputs() is empty. This should never happen! - if (pc.inputs().size() == 0) { - LOGP(info, "No inputs available!"); - return; - } - - // update tfNumbers - uint64_t startTime = 0; - uint64_t tfNumber = 0; - auto ref = pc.inputs().get("tfn"); - if (ref.spec && ref.payload) { - startTime = DataRefUtils::getHeader(ref)->startTime; - tfNumber = pc.inputs().get("tfn"); - tfNumbers.insert(std::pair(startTime, tfNumber)); - } - // update tfFilenames - std::string aodInputFile; - auto ref2 = pc.inputs().get("tff"); - if (ref2.spec && ref2.payload) { - startTime = DataRefUtils::getHeader(ref2)->startTime; - aodInputFile = pc.inputs().get("tff"); - tfFilenames.insert(std::pair(startTime, aodInputFile)); - } - - // close all output files if one has reached size limit - dod->checkFileSizes(); - - // loop over the DataRefs which are contained in pc.inputs() - for (const auto& ref : pc.inputs()) { - if (!ref.spec) { - LOGP(debug, "Invalid input will be skipped!"); - continue; - } - - // get metadata - if (DataSpecUtils::partialMatch(*ref.spec, header::DataDescription("AODMetadataKeys"))) { - aodMetaDataKeys = pc.inputs().get>(ref.spec->binding); - } - if (DataSpecUtils::partialMatch(*ref.spec, header::DataDescription("AODMetadataVals"))) { - aodMetaDataVals = pc.inputs().get>(ref.spec->binding); - } - - // skip non-AOD refs - if (!DataSpecUtils::partialMatch(*ref.spec, writableAODOrigins)) { - continue; - } - startTime = DataRefUtils::getHeader(ref)->startTime; - - // does this need to be saved? - auto dh = DataRefUtils::getHeader(ref); - auto tableName = dh->dataDescription.as(); - auto ds = dod->getDataOutputDescriptors(*dh); - if (ds.empty()) { - continue; - } - - // get TF number from startTime - auto it = tfNumbers.find(startTime); - if (it != tfNumbers.end()) { - tfNumber = (it->second / dod->getNumberTimeFramesToMerge()) * dod->getNumberTimeFramesToMerge(); - } else { - LOGP(fatal, "No time frame number found for output with start time {}", startTime); - throw std::runtime_error("Processing is stopped!"); - } - // get aod input file from startTime - auto it2 = tfFilenames.find(startTime); - if (it2 != tfFilenames.end()) { - aodInputFile = it2->second; - } - - // get the TableConsumer and corresponding arrow table - auto msg = pc.inputs().get(ref.spec->binding); - if (msg.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(); - if (!table->Validate().ok()) { - LOGP(warning, "The table \"{}\" is not valid and will not be saved!", tableName); - continue; - } - if (table->schema()->fields().empty()) { - LOGP(debug, "The table \"{}\" is empty but will be saved anyway!", tableName); - } - - // loop over all DataOutputDescriptors - // a table can be saved in multiple ways - // e.g. different selections of columns to different files - for (auto d : ds) { - auto fileAndFolder = dod->getFileFolder(d, tfNumber, aodInputFile, compressionLevel); - auto treename = fileAndFolder.folderName + "/" + d->treename; - TableToTree ta2tr(table, - fileAndFolder.file, - treename.c_str()); - - // update metadata - if (fileAndFolder.file->FindObjectAny("metaData")) { - LOGF(debug, "Metadata: target file %s already has metadata, preserving it", fileAndFolder.file->GetName()); - } else if (!aodMetaDataKeys.empty() && !aodMetaDataVals.empty()) { - TMap aodMetaDataMap; - for (uint32_t imd = 0; imd < aodMetaDataKeys.size(); imd++) { - aodMetaDataMap.Add(new TObjString(aodMetaDataKeys[imd]), new TObjString(aodMetaDataVals[imd])); - } - fileAndFolder.file->WriteObject(&aodMetaDataMap, "metaData", "Overwrite"); - } - - if (!d->colnames.empty()) { - for (auto& cn : d->colnames) { - auto idx = table->schema()->GetFieldIndex(cn); - auto col = table->column(idx); - auto field = table->schema()->field(idx); - if (idx != -1) { - ta2tr.addBranch(col, field); - } - } - } else { - ta2tr.addAllBranches(); - } - ta2tr.process(); - } - } - }; - }; // end of writerFunction + auto& ac = ctx.services().get(); // the command line options relevant for the writer are global // see runDataProcessing.h DataProcessorSpec spec{ .name = "internal-dpl-aod-writer", - .inputs = outputInputs, + .inputs = ac.outputsInputsAOD, .outputs = {}, - .algorithm = AlgorithmSpec{writerFunction}, + .algorithm = PluginManager::loadAlgorithmFromPlugin("O2FrameworkAnalysisSupport", "ROOTTTreeWriter", ctx), }; return spec; diff --git a/Framework/Core/src/ArrowSupport.cxx b/Framework/Core/src/ArrowSupport.cxx index 1a656e4d60080..230d708b47dc7 100644 --- a/Framework/Core/src/ArrowSupport.cxx +++ b/Framework/Core/src/ArrowSupport.cxx @@ -30,7 +30,7 @@ #include "Framework/ServiceMetricsInfo.h" #include "WorkflowHelpers.h" #include "Framework/WorkflowSpecNode.h" -#include "AnalysisSupportHelpers.h" +#include "Framework/AnalysisSupportHelpers.h" #include "CommonMessageBackendsHelpers.h" #include @@ -516,7 +516,7 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() auto [outputsInputs, isDangling] = WorkflowHelpers::analyzeOutputs(workflow); // create DataOutputDescriptor - std::shared_ptr dod = WorkflowHelpers::getDataOutputDirector(ctx.options(), outputsInputs, isDangling); + 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 @@ -537,11 +537,7 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() // add TFNumber and TFFilename as input to the writer outputsInputsAOD.emplace_back("tfn", "TFN", "TFNumber"); outputsInputsAOD.emplace_back("tff", "TFF", "TFFilename"); - int compression = 505; - if (ctx.options().hasOption("aod-writer-compression")) { - compression = ctx.options().get("aod-writer-compression"); - } - workflow.push_back(AnalysisSupportHelpers::getGlobalAODSink(dod, outputsInputsAOD, compression)); + workflow.push_back(AnalysisSupportHelpers::getGlobalAODSink(ctx)); } // Move the dummy sink at the end, if needed for (size_t i = 0; i < workflow.size(); ++i) { diff --git a/Framework/Core/src/ConfigContext.cxx b/Framework/Core/src/ConfigContext.cxx index 726332e1d0ae3..9b121b1884998 100644 --- a/Framework/Core/src/ConfigContext.cxx +++ b/Framework/Core/src/ConfigContext.cxx @@ -14,6 +14,9 @@ namespace o2::framework { +ConfigContext::ConfigContext(ConfigParamRegistry& options, ServiceRegistryRef services, int argc, char** argv) + : mOptions{options}, mServices{services}, mArgc{argc}, mArgv{argv} {} + bool ConfigContext::helpOnCommandLine() const { bool helpasked = false; diff --git a/Framework/Core/src/WorkflowHelpers.cxx b/Framework/Core/src/WorkflowHelpers.cxx index da9a135dc5eb8..3782c48e81c56 100644 --- a/Framework/Core/src/WorkflowHelpers.cxx +++ b/Framework/Core/src/WorkflowHelpers.cxx @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. #include "WorkflowHelpers.h" -#include "AnalysisSupportHelpers.h" +#include "Framework/AnalysisSupportHelpers.h" #include "Framework/AlgorithmSpec.h" #include "Framework/AODReaderHelpers.h" #include "Framework/ConfigParamSpec.h" @@ -153,7 +153,7 @@ int defaultConditionQueryRateMultiplier() return getenv("DPL_CONDITION_QUERY_RATE_MULTIPLIER") ? std::stoi(getenv("DPL_CONDITION_QUERY_RATE_MULTIPLIER")) : 1; } -void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext const& ctx) +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"; @@ -241,7 +241,9 @@ 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"}}); } - AnalysisContext ac; + ctx.services().registerService(ServiceRegistryHelpers::handleForService(new AnalysisContext)); + auto& ac = ctx.services().get(); + std::vector requestedCCDBs; std::vector providedCCDBs; @@ -573,7 +575,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) { - auto rootSink = AnalysisSupportHelpers::getOutputObjHistSink(ac.outObjHistMap, ac.outTskMap); + auto rootSink = AnalysisSupportHelpers::getOutputObjHistSink(ctx); extraSpecs.push_back(rootSink); } @@ -581,41 +583,38 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext extraSpecs.clear(); /// Analyze all ouputs - auto [outputsInputs, isDangling] = analyzeOutputs(workflow); + auto [outputsInputsTmp, isDanglingTmp] = analyzeOutputs(workflow); + ac.isDangling = isDanglingTmp; + ac.outputsInputs = outputsInputsTmp; // create DataOutputDescriptor - std::shared_ptr dod = getDataOutputDirector(ctx.options(), outputsInputs, isDangling); + 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! - std::vector outputsInputsAOD; - for (auto ii = 0u; ii < outputsInputs.size(); ii++) { - if (DataSpecUtils::partialMatch(outputsInputs[ii], extendedAODOrigins)) { - auto ds = dod->getDataOutputDescriptors(outputsInputs[ii]); - if (ds.size() > 0 || isDangling[ii]) { - outputsInputsAOD.emplace_back(outputsInputs[ii]); + for (auto ii = 0u; ii < ac.outputsInputs.size(); ii++) { + if (DataSpecUtils::partialMatch(ac.outputsInputs[ii], extendedAODOrigins)) { + auto ds = dod->getDataOutputDescriptors(ac.outputsInputs[ii]); + if (ds.size() > 0 || ac.isDangling[ii]) { + ac.outputsInputsAOD.emplace_back(ac.outputsInputs[ii]); } } } // file sink for any AOD output - if (outputsInputsAOD.size() > 0) { + if (ac.outputsInputsAOD.size() > 0) { // add TFNumber and TFFilename as input to the writer - outputsInputsAOD.emplace_back(InputSpec{"tfn", "TFN", "TFNumber"}); - outputsInputsAOD.emplace_back(InputSpec{"tff", "TFF", "TFFilename"}); - int compressionLevel = 505; - if (ctx.options().hasOption("aod-writer-compression")) { - compressionLevel = ctx.options().get("aod-writer-compression"); - } - auto fileSink = AnalysisSupportHelpers::getGlobalAODSink(dod, outputsInputsAOD, compressionLevel); + ac.outputsInputsAOD.emplace_back(InputSpec{"tfn", "TFN", "TFNumber"}); + ac.outputsInputsAOD.emplace_back(InputSpec{"tff", "TFF", "TFFilename"}); + auto fileSink = AnalysisSupportHelpers::getGlobalAODSink(ctx); extraSpecs.push_back(fileSink); - auto it = std::find_if(outputsInputs.begin(), outputsInputs.end(), [](InputSpec& spec) -> bool { + auto it = std::find_if(ac.outputsInputs.begin(), ac.outputsInputs.end(), [](InputSpec& spec) -> bool { return DataSpecUtils::partialMatch(spec, o2::header::DataOrigin("TFN")); }); - size_t ii = std::distance(outputsInputs.begin(), it); - isDangling[ii] = false; + size_t ii = std::distance(ac.outputsInputs.begin(), it); + ac.isDangling[ii] = false; } workflow.insert(workflow.end(), extraSpecs.begin(), extraSpecs.end()); @@ -623,20 +622,20 @@ void WorkflowHelpers::injectServiceDevices(WorkflowSpec& workflow, ConfigContext // Select dangling outputs which are not of type AOD std::vector redirectedOutputsInputs; - for (auto ii = 0u; ii < outputsInputs.size(); ii++) { + for (auto ii = 0u; ii < ac.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 (!isDangling[ii] && (ctx.options().get("forwarding-policy") != "all")) { + if (!ac.isDangling[ii] && (ctx.options().get("forwarding-policy") != "all")) { continue; } // AODs are skipped in any case. - if (DataSpecUtils::partialMatch(outputsInputs[ii], extendedAODOrigins)) { + if (DataSpecUtils::partialMatch(ac.outputsInputs[ii], extendedAODOrigins)) { continue; } - redirectedOutputsInputs.emplace_back(outputsInputs[ii]); + redirectedOutputsInputs.emplace_back(ac.outputsInputs[ii]); } std::vector unmatched; @@ -985,102 +984,6 @@ struct DataMatcherId { size_t id; }; -std::shared_ptr WorkflowHelpers::getDataOutputDirector(ConfigParamRegistry const& options, std::vector const& OutputsInputs, std::vector const& isDangling) -{ - std::shared_ptr dod = std::make_shared(); - - // analyze options and take actions accordingly - // default values - std::string rdn, resdir("./"); - std::string fnb, fnbase("AnalysisResults_trees"); - float mfs, maxfilesize(-1.); - std::string fmo, filemode("RECREATE"); - int ntfm, ntfmerge = 1; - - // values from json - if (options.isSet("aod-writer-json")) { - auto fnjson = options.get("aod-writer-json"); - if (!fnjson.empty()) { - std::tie(rdn, fnb, fmo, mfs, ntfm) = dod->readJson(fnjson); - if (!rdn.empty()) { - resdir = rdn; - } - if (!fnb.empty()) { - fnbase = fnb; - } - if (!fmo.empty()) { - filemode = fmo; - } - if (mfs > 0.) { - maxfilesize = mfs; - } - if (ntfm > 0) { - ntfmerge = ntfm; - } - } - } - - // values from command line options, information from json is overwritten - if (options.isSet("aod-writer-resdir")) { - rdn = options.get("aod-writer-resdir"); - if (!rdn.empty()) { - resdir = rdn; - } - } - if (options.isSet("aod-writer-resfile")) { - fnb = options.get("aod-writer-resfile"); - if (!fnb.empty()) { - fnbase = fnb; - } - } - if (options.isSet("aod-writer-resmode")) { - fmo = options.get("aod-writer-resmode"); - if (!fmo.empty()) { - filemode = fmo; - } - } - if (options.isSet("aod-writer-maxfilesize")) { - mfs = options.get("aod-writer-maxfilesize"); - if (mfs > 0) { - maxfilesize = mfs; - } - } - if (options.isSet("aod-writer-ntfmerge")) { - ntfm = options.get("aod-writer-ntfmerge"); - if (ntfm > 0) { - ntfmerge = ntfm; - } - } - // parse the keepString - if (options.isSet("aod-writer-keep")) { - auto keepString = options.get("aod-writer-keep"); - if (!keepString.empty()) { - dod->reset(); - std::string d("dangling"); - if (d.find(keepString) == 0) { - // use the dangling outputs - std::vector danglingOutputs; - for (auto ii = 0u; ii < OutputsInputs.size(); ii++) { - if (DataSpecUtils::partialMatch(OutputsInputs[ii], writableAODOrigins) && isDangling[ii]) { - danglingOutputs.emplace_back(OutputsInputs[ii]); - } - } - dod->readSpecs(danglingOutputs); - } else { - // use the keep string - dod->readString(keepString); - } - } - } - dod->setResultDir(resdir); - dod->setFilenameBase(fnbase); - dod->setFileMode(filemode); - dod->setMaximumFileSize(maxfilesize); - dod->setNumberTimeFramesToMerge(ntfmerge); - - return dod; -} - std::tuple, std::vector> WorkflowHelpers::analyzeOutputs(WorkflowSpec const& workflow) { // compute total number of input/output diff --git a/Framework/Core/src/WorkflowHelpers.h b/Framework/Core/src/WorkflowHelpers.h index b20249b99edc8..b2a4d4cab55df 100644 --- a/Framework/Core/src/WorkflowHelpers.h +++ b/Framework/Core/src/WorkflowHelpers.h @@ -180,7 +180,7 @@ struct WorkflowHelpers { // dangling inputs are satisfied. // @a workflow the workflow to decorate // @a ctx the context for the configuration phase - static void injectServiceDevices(WorkflowSpec& workflow, ConfigContext const& ctx); + static void injectServiceDevices(WorkflowSpec& workflow, ConfigContext& ctx); // Final adjustments to @a workflow after service devices have been injected. static void adjustTopology(WorkflowSpec& workflow, ConfigContext const& ctx); @@ -204,8 +204,6 @@ struct WorkflowHelpers { const std::vector& edges, const std::vector& index); - static std::shared_ptr getDataOutputDirector(ConfigParamRegistry const& options, std::vector const& OutputsInputs, std::vector const& outputTypes); - /// Given @a workflow it gathers all the OutputSpec and in addition provides /// the information whether and output is dangling and/or of type AOD /// An Output is dangling if it does not have a corresponding InputSpec. diff --git a/Framework/Core/test/Mocking.h b/Framework/Core/test/Mocking.h index b3e48ad3b2d0f..a42a1b30a662f 100644 --- a/Framework/Core/test/Mocking.h +++ b/Framework/Core/test/Mocking.h @@ -34,7 +34,10 @@ std::unique_ptr makeEmptyConfigContext() store->preload(); store->activate(); static ConfigParamRegistry registry(std::move(store)); - auto context = std::make_unique(registry, 0, nullptr); + static std::unique_ptr services; + // We need to reset it because we will inject services into it. + services = std::make_unique(); + auto context = std::make_unique(registry, ServiceRegistryRef{*services}, 0, nullptr); return context; } diff --git a/Framework/Core/test/benchmark_WorkflowHelpers.cxx b/Framework/Core/test/benchmark_WorkflowHelpers.cxx index f1c070d8a0f4e..09a9ae0cca923 100644 --- a/Framework/Core/test/benchmark_WorkflowHelpers.cxx +++ b/Framework/Core/test/benchmark_WorkflowHelpers.cxx @@ -30,7 +30,8 @@ std::unique_ptr makeEmptyConfigContext() store->preload(); store->activate(); static ConfigParamRegistry registry(std::move(store)); - auto context = std::make_unique(registry, 0, nullptr); + static ServiceRegistry services; + auto context = std::make_unique(registry, ServiceRegistryRef{services}, 0, nullptr); return context; } diff --git a/Framework/Core/test/test_OverrideLabels.cxx b/Framework/Core/test/test_OverrideLabels.cxx index 573bd13be797a..c5134c0c169c0 100644 --- a/Framework/Core/test/test_OverrideLabels.cxx +++ b/Framework/Core/test/test_OverrideLabels.cxx @@ -31,7 +31,8 @@ std::unique_ptr mockupLabels(std::string labelArg) store->preload(); store->activate(); registry = ConfigParamRegistry(std::move(store)); - auto context = std::make_unique(registry, 0, nullptr); + static ServiceRegistry services; + auto context = std::make_unique(registry, ServiceRegistryRef{services}, 0, nullptr); return context; } diff --git a/Framework/TestWorkflows/src/o2TestHistograms.cxx b/Framework/TestWorkflows/src/o2TestHistograms.cxx index 9986f52a1d940..efac16f6da4f0 100644 --- a/Framework/TestWorkflows/src/o2TestHistograms.cxx +++ b/Framework/TestWorkflows/src/o2TestHistograms.cxx @@ -17,6 +17,7 @@ #include "Framework/AnalysisTask.h" #include #include +#include using namespace o2; using namespace o2::framework; @@ -43,7 +44,7 @@ struct EtaAndClsHistogramsSimple { { LOGP(info, "Invoking the simple one"); for (auto& track : tracks) { - etaClsH->Fill(track.eta(), track.pt(), 0); + etaClsH->Fill(track.eta(), track.pt()); skimEx(track.pt(), track.eta()); } } @@ -57,7 +58,7 @@ struct EtaAndClsHistogramsIUSimple { { LOGP(info, "Invoking the simple one"); for (auto& track : tracks) { - etaClsH->Fill(track.eta(), track.pt(), 0); + etaClsH->Fill(track.eta(), track.pt()); skimEx(track.pt(), track.eta()); } } From dcb767f01f2fb1a526042375dcd4ab325c96743d Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 13 Nov 2024 09:33:17 +0100 Subject: [PATCH 0031/2180] GPU TPC: Reject clusters with too small radius during refit instead of giving them IFC mask errors --- GPU/GPUTracking/Definitions/GPUSettingsList.h | 1 + GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx | 26 ++++++++++++------- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index 0b2da89b79ad5..106a222862f49 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -131,6 +131,7 @@ AddOptionRTC(cfNoiseSuppressionEpsilonRelative, uint8_t, 76, "", 0, "Cluster Fin AddOptionRTC(nWays, uint8_t, 3, "", 0, "Do N fit passes in final fit of merger") AddOptionRTC(nWaysOuter, int8_t, 0, "", 0, "Store outer param") AddOptionRTC(trackFitRejectMode, int8_t, 5, "", 0, "0: no limit on rejection or missed hits, >0: break after n rejected hits, <0: reject at max -n hits") +AddOptionRTC(rejectIFCLowRadiusCluster, uint8_t, 0, "", 0, "Reject clusters that get the IFC mask error during refit") AddOptionRTC(dEdxTruncLow, uint8_t, 2, "", 0, "Low truncation threshold, fraction of 128") AddOptionRTC(dEdxTruncHigh, uint8_t, 77, "", 0, "High truncation threshold, fraction of 128") AddOptionRTC(globalTracking, int8_t, 1, "", 0, "Enable Global Tracking (prolong tracks to adjacent sectors to find short segments)") diff --git a/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx b/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx index 13244dcb4b621..0b1c282f3b2f0 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx @@ -68,7 +68,7 @@ GPUd() bool GPUTPCGMTrackParam::Fit(GPUTPCGMMerger* GPUrestrict() merger, int32_ GPUTPCGMPropagator prop; gputpcgmmergertypes::InterpolationErrors interpolation; prop.SetMaterialTPC(); - prop.SetPolynomialField(&merger->Param().polynomialField); + prop.SetPolynomialField(¶m.polynomialField); prop.SetMaxSinPhi(maxSinPhi); prop.SetToyMCEventsFlag(param.par.toyMCEventsFlag); if ((clusters[0].slice < 18) == (clusters[N - 1].slice < 18)) { @@ -157,7 +157,7 @@ GPUd() bool GPUTPCGMTrackParam::Fit(GPUTPCGMMerger* GPUrestrict() merger, int32_ uint8_t clusterState = clusters[ihit].state; const float clAlpha = param.Alpha(clusters[ihit].slice); float xx, yy, zz; - if (merger->Param().par.earlyTpcTransform) { + if (param.par.earlyTpcTransform) { const float zOffset = (clusters[ihit].slice < 18) == (clusters[0].slice < 18) ? mTZOffset : -mTZOffset; xx = clustersXYZ[ihit].x; yy = clustersXYZ[ihit].y; @@ -177,6 +177,14 @@ GPUd() bool GPUTPCGMTrackParam::Fit(GPUTPCGMMerger* GPUrestrict() merger, int32_ continue; } + if (param.rec.tpc.rejectIFCLowRadiusCluster) { + const float r2 = xx * xx + yy * yy; + const float rmax = (83.5f + param.rec.tpc.sysClusErrorMinDist); + if (r2 < rmax * rmax) { + MarkClusters(clusters, ihitMergeFirst, ihit, wayDirection, GPUTPCGMMergedTrackHit::flagRejectErr); + } + } + const auto& cluster = clusters[ihit]; bool changeDirection = (cluster.leg - lastLeg) & 1; @@ -212,7 +220,7 @@ GPUd() bool GPUTPCGMTrackParam::Fit(GPUTPCGMMerger* GPUrestrict() merger, int32_ continue; } } else if (allowModification && lastRow != 255 && CAMath::Abs(cluster.row - lastRow) > 1) { - bool dodEdx = merger->Param().par.dodEdx && merger->Param().dodEdxDownscaled && merger->Param().rec.tpc.adddEdxSubThresholdClusters && iWay == nWays - 1 && CAMath::Abs(cluster.row - lastRow) == 2 && cluster.leg == clusters[maxN - 1].leg; + bool dodEdx = param.par.dodEdx && param.dodEdxDownscaled && param.rec.tpc.adddEdxSubThresholdClusters && iWay == nWays - 1 && CAMath::Abs(cluster.row - lastRow) == 2 && cluster.leg == clusters[maxN - 1].leg; dodEdx = AttachClustersPropagate(merger, cluster.slice, lastRow, cluster.row, iTrk, cluster.leg == clusters[maxN - 1].leg, prop, inFlyDirection, GPUCA_MAX_SIN_PHI, dodEdx); if (dodEdx) { dEdx.fillSubThreshold(lastRow - 1, param); @@ -323,7 +331,7 @@ GPUd() bool GPUTPCGMTrackParam::Fit(GPUTPCGMMerger* GPUrestrict() merger, int32_ } #endif GPUCA_DEBUG_STREAMER_CHECK(GPUTPCGMPropagator::DebugStreamerVals debugVals;); - if (merger->Param().rec.tpc.rejectEdgeClustersInTrackFit && uncorrectedY > -1e6f && merger->Param().rejectEdgeClusterByY(uncorrectedY, cluster.row, CAMath::Sqrt(mC[0]))) { // uncorrectedY > -1e6f implies allowModification + if (param.rec.tpc.rejectEdgeClustersInTrackFit && uncorrectedY > -1e6f && param.rejectEdgeClusterByY(uncorrectedY, cluster.row, CAMath::Sqrt(mC[0]))) { // uncorrectedY > -1e6f implies allowModification retVal = GPUTPCGMPropagator::updateErrorEdgeCluster; } else { const float time = merger->GetConstantMem()->ioPtrs.clustersNative ? merger->GetConstantMem()->ioPtrs.clustersNative->clustersLinear[cluster.num].getTime() : -1.f; @@ -358,11 +366,11 @@ GPUd() bool GPUTPCGMTrackParam::Fit(GPUTPCGMMerger* GPUrestrict() merger, int32_ ihitStart = ihit; float dy = mP[0] - prop.Model().Y(); float dz = mP[1] - prop.Model().Z(); - if (CAMath::Abs(mP[4]) * merger->Param().qptB5Scaler > 10 && --resetT0 <= 0 && CAMath::Abs(mP[2]) < 0.15f && dy * dy + dz * dz > 1) { + if (CAMath::Abs(mP[4]) * param.qptB5Scaler > 10 && --resetT0 <= 0 && CAMath::Abs(mP[2]) < 0.15f && dy * dy + dz * dz > 1) { CADEBUG(printf("Reinit linearization\n")); prop.SetTrack(this, prop.GetAlpha()); } - if (merger->Param().par.dodEdx && merger->Param().dodEdxDownscaled && iWay == nWays - 1 && cluster.leg == clusters[maxN - 1].leg && !(clusterState & GPUTPCGMMergedTrackHit::flagEdge)) { + if (param.par.dodEdx && param.dodEdxDownscaled && iWay == nWays - 1 && cluster.leg == clusters[maxN - 1].leg && !(clusterState & GPUTPCGMMergedTrackHit::flagEdge)) { float qtot = 0, qmax = 0, pad = 0, relTime = 0; const int32_t clusterCount = (ihit - ihitMergeFirst) * wayDirection + 1; for (int32_t iTmp = ihitMergeFirst; iTmp != ihit + wayDirection; iTmp += wayDirection) { @@ -404,16 +412,16 @@ GPUd() bool GPUTPCGMTrackParam::Fit(GPUTPCGMMerger* GPUrestrict() merger, int32_ o2::utils::DebugStreamer::instance()->getStreamer("debug_accept_track", "UPDATE") << o2::utils::DebugStreamer::instance()->getUniqueTreeName("debug_accept_track").data() << "iTrk=" << iTrk << "outerParam=" << *outerParam << "track=" << this << "ihitStart=" << ihitStart << "\n"; }) - if (!(N + NTolerated >= GPUCA_TRACKLET_SELECTOR_MIN_HITS_B5(mP[4] * merger->Param().qptB5Scaler) && 2 * NTolerated <= CAMath::Max(10, N) && CheckNumericalQuality(covYYUpd))) { + if (!(N + NTolerated >= GPUCA_TRACKLET_SELECTOR_MIN_HITS_B5(mP[4] * param.qptB5Scaler) && 2 * NTolerated <= CAMath::Max(10, N) && CheckNumericalQuality(covYYUpd))) { return false; // TODO: NTolerated should never become that large, check what is going wrong! } - if (merger->Param().rec.tpc.minNClustersFinalTrack != -1 && N + NTolerated < merger->Param().rec.tpc.minNClustersFinalTrack) { + if (param.rec.tpc.minNClustersFinalTrack != -1 && N + NTolerated < param.rec.tpc.minNClustersFinalTrack) { return false; } // TODO: we have looping tracks here with 0 accepted clusters in the primary leg. In that case we should refit the track using only the primary leg. - if (merger->Param().par.dodEdx && merger->Param().dodEdxDownscaled) { + if (param.par.dodEdx && param.dodEdxDownscaled) { dEdx.computedEdx(merger->OutputTracksdEdx()[iTrk], param); } Alpha = prop.GetAlpha(); From 197384977d7a71e8c2e87e36ee58d977055c048e Mon Sep 17 00:00:00 2001 From: David Rohr Date: Mon, 11 Nov 2024 14:11:45 +0100 Subject: [PATCH 0032/2180] Add empty streaming operator, so that std::cout << SMatrixGPU() does not fail --- Common/MathUtils/include/MathUtils/SMatrixGPU.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Common/MathUtils/include/MathUtils/SMatrixGPU.h b/Common/MathUtils/include/MathUtils/SMatrixGPU.h index 60965a4fa2776..2bfdcf54752b2 100644 --- a/Common/MathUtils/include/MathUtils/SMatrixGPU.h +++ b/Common/MathUtils/include/MathUtils/SMatrixGPU.h @@ -29,6 +29,7 @@ #include "GPUCommonMath.h" #include "GPUCommonAlgorithm.h" #include "GPUCommonLogger.h" +#include "GPUCommonTypeTraits.h" namespace o2::math_utils::detail { @@ -468,6 +469,9 @@ class SMatrixGPU GPUd() const T& operator()(unsigned int i, unsigned int j) const; GPUd() T& operator()(unsigned int i, unsigned int j); + template + GPUd() friend X& operator<<(Y& y, const SMatrixGPU&); + class SMatrixRowGPU { public: @@ -512,6 +516,13 @@ class SMatrixGPU R mRep; }; +template + requires(sizeof(typename X::traits_type::pos_type) != 0) // do not provide a template to fair::Logger, etc... (pos_type is a member type of all std::ostream classes) +GPUd() X& operator<<(Y& y, const SMatrixGPU&) +{ + return y; +} + template GPUdi() SMatrixGPU::SMatrixGPU(SMatrixIdentity) { From 80e298b8712abbac3c55d5cdf0c57a92fc8083f1 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Mon, 11 Nov 2024 14:12:30 +0100 Subject: [PATCH 0033/2180] GPU: Fix includes of certain headers (fix order, avoid ROOT in GPU code) --- GPU/GPUTracking/DataTypes/CalibdEdxContainer.cxx | 5 ++--- GPU/GPUTracking/DataTypes/CalibdEdxTrackTopologyPol.cxx | 1 + GPU/GPUTracking/DataTypes/CalibdEdxTrackTopologySpline.cxx | 3 +-- GPU/GPUTracking/DataTypes/CalibdEdxTrackTopologySpline.h | 2 +- GPU/GPUTracking/Interface/GPUO2InterfaceQA.cxx | 2 +- GPU/GPUTracking/Refit/GPUTrackingRefitKernel.cxx | 2 +- 6 files changed, 7 insertions(+), 8 deletions(-) diff --git a/GPU/GPUTracking/DataTypes/CalibdEdxContainer.cxx b/GPU/GPUTracking/DataTypes/CalibdEdxContainer.cxx index a632bf361498c..002bb1ed9e9d7 100644 --- a/GPU/GPUTracking/DataTypes/CalibdEdxContainer.cxx +++ b/GPU/GPUTracking/DataTypes/CalibdEdxContainer.cxx @@ -12,14 +12,13 @@ /// \file CalibdEdxContainer.cxx /// \author Matthias Kleiner -#include "CalibdEdxContainer.h" - -#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) +#if !defined(GPUCA_STANDALONE) #include "TFile.h" #include "TPCBase/CalDet.h" #include "Framework/Logger.h" #include "clusterFinderDefs.h" #endif +#include "CalibdEdxContainer.h" using namespace GPUCA_NAMESPACE::gpu; using namespace o2::tpc; diff --git a/GPU/GPUTracking/DataTypes/CalibdEdxTrackTopologyPol.cxx b/GPU/GPUTracking/DataTypes/CalibdEdxTrackTopologyPol.cxx index 548bbafae686d..533763e14c6d7 100644 --- a/GPU/GPUTracking/DataTypes/CalibdEdxTrackTopologyPol.cxx +++ b/GPU/GPUTracking/DataTypes/CalibdEdxTrackTopologyPol.cxx @@ -9,6 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. +#include "Rtypes.h" #include "CalibdEdxTrackTopologyPol.h" #include diff --git a/GPU/GPUTracking/DataTypes/CalibdEdxTrackTopologySpline.cxx b/GPU/GPUTracking/DataTypes/CalibdEdxTrackTopologySpline.cxx index 4c6e750355397..3b0e718026536 100644 --- a/GPU/GPUTracking/DataTypes/CalibdEdxTrackTopologySpline.cxx +++ b/GPU/GPUTracking/DataTypes/CalibdEdxTrackTopologySpline.cxx @@ -14,11 +14,10 @@ /// /// \author Matthias Kleiner -#include "CalibdEdxTrackTopologySpline.h" - #if !defined(GPUCA_STANDALONE) #include "TFile.h" #endif +#include "CalibdEdxTrackTopologySpline.h" using namespace GPUCA_NAMESPACE::gpu; using namespace o2::tpc; diff --git a/GPU/GPUTracking/DataTypes/CalibdEdxTrackTopologySpline.h b/GPU/GPUTracking/DataTypes/CalibdEdxTrackTopologySpline.h index 563872fb90d4d..d9d4b9e35592d 100644 --- a/GPU/GPUTracking/DataTypes/CalibdEdxTrackTopologySpline.h +++ b/GPU/GPUTracking/DataTypes/CalibdEdxTrackTopologySpline.h @@ -19,12 +19,12 @@ #include "FlatObject.h" #include "Spline.h" +#include "GPUCommonRtypes.h" #ifdef GPUCA_HAVE_O2HEADERS #include "DataFormatsTPC/Defs.h" #endif #if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) // code invisible on GPU and in the standalone compilation -#include "Rtypes.h" // for ClassDefNV #include #endif diff --git a/GPU/GPUTracking/Interface/GPUO2InterfaceQA.cxx b/GPU/GPUTracking/Interface/GPUO2InterfaceQA.cxx index db6df3f9f1ede..7005fbb3bab25 100644 --- a/GPU/GPUTracking/Interface/GPUO2InterfaceQA.cxx +++ b/GPU/GPUTracking/Interface/GPUO2InterfaceQA.cxx @@ -12,11 +12,11 @@ /// \file GPUO2InterfaceQA.cxx /// \author David Rohr +#include "TGraphAsymmErrors.h" #include "GPUParam.h" #include "GPUQA.h" #include "GPUO2InterfaceConfiguration.h" #include "GPUO2InterfaceQA.h" -#include "TGraphAsymmErrors.h" using namespace o2::gpu; using namespace o2::tpc; diff --git a/GPU/GPUTracking/Refit/GPUTrackingRefitKernel.cxx b/GPU/GPUTracking/Refit/GPUTrackingRefitKernel.cxx index 6baea86f05d36..f7e3bca47a0fc 100644 --- a/GPU/GPUTracking/Refit/GPUTrackingRefitKernel.cxx +++ b/GPU/GPUTracking/Refit/GPUTrackingRefitKernel.cxx @@ -12,9 +12,9 @@ /// \file GPUTrackingRefitKernel.cxx /// \author David Rohr +#include "GPUROOTDump.h" #include "GPUTrackingRefitKernel.h" #include "GPUTrackingRefit.h" -#include "GPUROOTDump.h" using namespace GPUCA_NAMESPACE::gpu; From 4ee9785a941eb11f5ea8f5cb86fa42ce31050b88 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Mon, 11 Nov 2024 14:36:09 +0100 Subject: [PATCH 0034/2180] FST: Force correct number of orbits to gpu-reco --- prodtests/full_system_test.sh | 1 + 1 file changed, 1 insertion(+) diff --git a/prodtests/full_system_test.sh b/prodtests/full_system_test.sh index f8b6d66ce87e4..8d6a0ca3cf1f9 100755 --- a/prodtests/full_system_test.sh +++ b/prodtests/full_system_test.sh @@ -227,6 +227,7 @@ if [[ ${RANS_OPT:-} =~ (--ans-version +)(compat) ]] ; then # for decoding we use either just produced or externally provided common local file export ARGS_EXTRA_PROCESS_o2_ctf_reader_workflow+="--ctf-dict $CTFDICTFILE" fi +export CONFIG_EXTRA_PROCESS_o2_gpu_reco_workflow+="GPU_global.overrideNHbfPerTF=$NHBPERTF;" for STAGE in $STAGES; do logfile=reco_${STAGE}.log From ddfe6d025cc4a9f42ef3ffd0eba95a09059ae4f6 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Mon, 11 Nov 2024 14:36:55 +0100 Subject: [PATCH 0035/2180] Calibration aggregator-workflow.sh: Update default lanes/threads for TPC IDC calib --- prodtests/full-system-test/aggregator-workflow.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/prodtests/full-system-test/aggregator-workflow.sh b/prodtests/full-system-test/aggregator-workflow.sh index 4c20e901a2978..23336cafffab8 100755 --- a/prodtests/full-system-test/aggregator-workflow.sh +++ b/prodtests/full-system-test/aggregator-workflow.sh @@ -295,8 +295,8 @@ fi # TPC IDCs and SAC crus="0-359" # to be used with $AGGREGATOR_TASKS == TPC_IDCBOTH_SAC or ALL -lanesFactorize=${O2_TPC_IDC_FACTORIZE_NLANES:-10} -threadFactorize=${O2_TPC_IDC_FACTORIZE_NTHREADS:-8} +lanesFactorize=${O2_TPC_IDC_FACTORIZE_NLANES:-12} +threadFactorize=${O2_TPC_IDC_FACTORIZE_NTHREADS:-16} nTFs=$((1000 * 128 / ${NHBPERTF})) nTFs_SAC=$((1000 * 128 / ${NHBPERTF})) nBuffer=$((100 * 128 / ${NHBPERTF})) From b8b824ce09d785013f8c216393e2f5267f2aec59 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Mon, 11 Nov 2024 18:06:24 +0100 Subject: [PATCH 0036/2180] GPU Display: Fix race condition --- GPU/GPUTracking/display/render/GPUDisplayDraw.cxx | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/GPU/GPUTracking/display/render/GPUDisplayDraw.cxx b/GPU/GPUTracking/display/render/GPUDisplayDraw.cxx index ab7ebf6811766..746c41938e2e1 100644 --- a/GPU/GPUTracking/display/render/GPUDisplayDraw.cxx +++ b/GPU/GPUTracking/display/render/GPUDisplayDraw.cxx @@ -27,6 +27,7 @@ #include "GPUTPCGMPropagator.h" #include "GPUTPCMCInfo.h" #include "GPUParam.inc" +#include "GPUCommonMath.h" #include @@ -66,8 +67,12 @@ inline void GPUDisplay::insertVertexList(int32_t iSlice, size_t first, size_t la inline void GPUDisplay::drawPointLinestrip(int32_t iSlice, int32_t cid, int32_t id, int32_t id_limit) { mVertexBuffer[iSlice].emplace_back(mGlobalPos[cid].x, mGlobalPos[cid].y * mYFactor, mCfgH.projectXY ? 0 : mGlobalPos[cid].z); - if (mGlobalPos[cid].w < id_limit) { - mGlobalPos[cid].w = id; + float curVal; + while ((curVal = mGlobalPos[cid].w) < id_limit) { + if (GPUCommonMath::AtomicCAS(&mGlobalPos[cid].w, curVal, (float)id)) { + break; + } + curVal = mGlobalPos[cid].w; } } From 98746db30c03bcbbaac83d2fe7b8977f4957e279 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 13 Nov 2024 09:30:47 +0100 Subject: [PATCH 0037/2180] GPU: Split NDPiecewisePolynomials in header and inc file, get rid of ROOT in the header --- .../DataTypes/CalibdEdxTrackTopologyPol.cxx | 1 + .../NDPiecewisePolynomials.h | 351 +++--------------- .../NDPiecewisePolynomials.inc | 276 ++++++++++++++ .../test/testMultivarPolynomials.cxx | 2 +- 4 files changed, 330 insertions(+), 300 deletions(-) create mode 100644 GPU/TPCFastTransformation/NDPiecewisePolynomials.inc diff --git a/GPU/GPUTracking/DataTypes/CalibdEdxTrackTopologyPol.cxx b/GPU/GPUTracking/DataTypes/CalibdEdxTrackTopologyPol.cxx index 533763e14c6d7..47a6e4cff72df 100644 --- a/GPU/GPUTracking/DataTypes/CalibdEdxTrackTopologyPol.cxx +++ b/GPU/GPUTracking/DataTypes/CalibdEdxTrackTopologyPol.cxx @@ -21,6 +21,7 @@ using namespace o2::tpc; #if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) // code invisible on GPU and in the standalone compilation +#include "NDPiecewisePolynomials.inc" void CalibdEdxTrackTopologyPol::dumpToTree(const uint32_t nSamplingPoints[/* Dim */], const char* outName) const { for (uint32_t i = 0; i < FFits; i++) { diff --git a/GPU/TPCFastTransformation/NDPiecewisePolynomials.h b/GPU/TPCFastTransformation/NDPiecewisePolynomials.h index 6de2bc7afbae8..9498645b76220 100644 --- a/GPU/TPCFastTransformation/NDPiecewisePolynomials.h +++ b/GPU/TPCFastTransformation/NDPiecewisePolynomials.h @@ -20,17 +20,12 @@ #include "MultivariatePolynomialHelper.h" #include "GPUCommonMath.h" -#if !defined(GPUCA_GPUCODE) +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) #include -#if !defined(GPUCA_STANDALONE) -#include "TLinearFitter.h" -#ifndef GPUCA_ALIROOT_LIB -#include "CommonUtils/TreeStreamRedirector.h" -#endif -#include -#endif #endif +class TFile; + namespace GPUCA_NAMESPACE::gpu { @@ -81,23 +76,20 @@ template class NDPiecewisePolynomials : public FlatObject { public: -#ifndef GPUCA_GPUCODE +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) /// constructor /// \param min minimum coordinates of the grid /// \param max maximum coordinates of the grid (note: the resulting polynomials can NOT be evaluated at the maximum coordinates: only at min <= X < max) /// \param n number of vertices: defines number of fits per dimension: nFits = n - 1. n should be at least 2 to perform one fit NDPiecewisePolynomials(const float min[/* Dim */], const float max[/* Dim */], const uint32_t n[/* Dim */]) { init(min, max, n); } -#endif -#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) /// constructor construct and object by initializing it from an object stored in a Root file /// \param fileName name of the file /// \param name name of the object NDPiecewisePolynomials(const char* fileName, const char* name) { - TFile f(fileName, "READ"); - loadFromFile(f, name); + loadFromFile(fileName, name); }; -#endif +#endif // !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) /// default constructor NDPiecewisePolynomials() CON_DEFAULT; @@ -115,7 +107,7 @@ class NDPiecewisePolynomials : public FlatObject /// move flat buffer to new location /// \param newBufferPtr new buffer location void moveBufferTo(char* newBufferPtr); -#endif +#endif // !defined(GPUCA_GPUCODE) /// destroy the object (release internal flat buffer) void destroy(); @@ -168,17 +160,16 @@ class NDPiecewisePolynomials : public FlatObject /// \return returns the parameters of the coefficients GPUd() const float* getParams() const { return mParams; } -#if !defined(GPUCA_GPUCODE) - /// Setting directly the parameters of the polynomials - void setParams(const float params[/* getNParameters() */]) { std::copy(params, params + getNParameters(), mParams); } - /// initalize the members /// \param min minimum coordinates of the grid /// \param max maximum coordinates of the grid (note: the resulting polynomials can NOT be evaluated at the maximum coordinates: only at min <= X < max) /// \param n number of vertices: defines number of fits per dimension: nFits = n - 1. n should be at least 2 to perform one fit void init(const float min[/* Dim */], const float max[/* Dim */], const uint32_t n[/* Dim */]); -#ifndef GPUCA_STANDALONE +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) + /// Setting directly the parameters of the polynomials + void setParams(const float params[/* getNParameters() */]) { std::copy(params, params + getNParameters(), mParams); } + /// perform the polynomial fits on the grid /// \param func function which returns for every input x on the defined grid the true value /// \param nAuxiliaryPoints number of points which will be used for the fits (should be at least 2) @@ -194,6 +185,8 @@ class NDPiecewisePolynomials : public FlatObject /// \param name name of the object in the file void loadFromFile(TFile& inpf, const char* name); + void loadFromFile(const char* fileName, const char* name); + /// write parameters to file /// \param outf output file /// \param name name of the output object @@ -211,7 +204,6 @@ class NDPiecewisePolynomials : public FlatObject /// \return returns total number of polynomial fits uint32_t getNPolynomials() const; -#endif /// converts the class to a container which can be written to a root file NDPiecewisePolynomialContainer getContainer() const { return NDPiecewisePolynomialContainer{Dim, Degree, getNParameters(), mParams, InteractionOnly, mMin, mMax, mN}; } @@ -219,10 +211,10 @@ class NDPiecewisePolynomials : public FlatObject /// set the parameters from NDPiecewisePolynomialContainer /// \param container container for the parameters void setFromContainer(const NDPiecewisePolynomialContainer& container); +#endif // !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) /// \return returns the total number of stored parameters uint32_t getNParameters() const { return getNPolynomials() * MultivariatePolynomialParametersHelper::getNParameters(Degree, Dim, InteractionOnly); } -#endif /// \return returns number of dimensions of the polynomials GPUd() static constexpr uint32_t getDim() { return Dim; } @@ -292,15 +284,15 @@ class NDPiecewisePolynomials : public FlatObject /// \param ix index /// \param dim dimension double getVertexPosition(const uint32_t ix, const int32_t dim) const { return ix / static_cast(mInvSpacing[dim]) + mMin[dim]; } -#endif +#endif // !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) #if !defined(GPUCA_GPUCODE) /// \return returns the size of the parameters std::size_t sizeOfParameters() const { return getNParameters() * sizeof(DataTParams); } +#endif // #if !defined(GPUCA_GPUCODE) // construct the object (flatbuffer) void construct(); -#endif #ifndef GPUCA_ALIROOT_LIB ClassDefNV(NDPiecewisePolynomials, 1); @@ -313,20 +305,6 @@ class NDPiecewisePolynomials : public FlatObject #if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) template -void NDPiecewisePolynomials::loadFromFile(TFile& inpf, const char* name) -{ - NDPiecewisePolynomialContainer* gridTmp = nullptr; - inpf.GetObject(name, gridTmp); - if (gridTmp) { - setFromContainer(*gridTmp); - delete gridTmp; - } else { -#ifndef GPUCA_ALIROOT_LIB - LOGP(info, "couldnt load object {} from input file", name); -#endif - } -} -template void NDPiecewisePolynomials::setFromContainer(const NDPiecewisePolynomialContainer& container) { if (Dim != container.mDim) { @@ -350,12 +328,6 @@ void NDPiecewisePolynomials::setFromContainer(cons init(container.mMin.data(), container.mMax.data(), container.mN.data()); setParams(container.mParams.data()); } -template -void NDPiecewisePolynomials::writeToFile(TFile& outf, const char* name) const -{ - const NDPiecewisePolynomialContainer cont = getContainer(); - outf.WriteObject(&cont, name); -} template void NDPiecewisePolynomials::setDefault() @@ -368,7 +340,29 @@ void NDPiecewisePolynomials::setDefault() std::copy(params.begin(), params.end(), &mParams[i * nParamsPerPol]); } } -#endif + +template +uint32_t NDPiecewisePolynomials::getNPolynomials() const +{ + uint32_t nP = getNPolynomials(0); + for (uint32_t i = 1; i < Dim; ++i) { + nP *= getNPolynomials(i); + } + return nP; +} + +template +void NDPiecewisePolynomials::checkPos(const uint32_t iMax[/* Dim */], int32_t pos[/* Dim */]) const +{ + for (uint32_t i = 0; i < Dim; ++i) { + if (pos[i] == int32_t(iMax[i])) { + ++pos[i + 1]; + std::fill_n(pos, i + 1, 0); + } + } +} + +#endif // !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) #ifndef GPUCA_GPUCODE template @@ -405,7 +399,19 @@ void NDPiecewisePolynomials::construct() FlatObject::finishConstruction(flatbufferSize); mParams = reinterpret_cast(mFlatBufferPtr); } -#endif + +template +void NDPiecewisePolynomials::init(const float min[], const float max[], const uint32_t n[]) +{ + for (uint32_t i = 0; i < Dim; ++i) { + mMin[i] = min[i]; + mMax[i] = max[i]; + mN[i] = n[i]; + mInvSpacing[i] = (mN[i] - 1) / (mMax[i] - mMin[i]); + } + construct(); +} +#endif // !GPUCA_GPUCODE template void NDPiecewisePolynomials::destroy() @@ -472,259 +478,6 @@ GPUdi() void NDPiecewisePolynomials::clamp(float x } } -#ifndef GPUCA_GPUCODE -template -void NDPiecewisePolynomials::init(const float min[], const float max[], const uint32_t n[]) -{ - for (uint32_t i = 0; i < Dim; ++i) { - mMin[i] = min[i]; - mMax[i] = max[i]; - mN[i] = n[i]; - mInvSpacing[i] = (mN[i] - 1) / (mMax[i] - mMin[i]); - } - construct(); -} -#endif - -#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) -template -uint32_t NDPiecewisePolynomials::getNPolynomials() const -{ - uint32_t nP = getNPolynomials(0); - for (uint32_t i = 1; i < Dim; ++i) { - nP *= getNPolynomials(i); - } - return nP; -} - -template -void NDPiecewisePolynomials::checkPos(const uint32_t iMax[/* Dim */], int32_t pos[/* Dim */]) const -{ - for (uint32_t i = 0; i < Dim; ++i) { - if (pos[i] == int32_t(iMax[i])) { - ++pos[i + 1]; - std::fill_n(pos, i + 1, 0); - } - } -} - -template -void NDPiecewisePolynomials::performFits(const std::function& func, const uint32_t nAuxiliaryPoints[/* Dim */]) -{ - const int32_t nTotalFits = getNPolynomials(); -#ifndef GPUCA_ALIROOT_LIB - LOGP(info, "Perform fitting of {}D-Polynomials of degree {} for a total of {} fits.", Dim, Degree, nTotalFits); -#endif - - MultivariatePolynomialHelper<0, 0, false> pol(Dim, Degree, InteractionOnly); - TLinearFitter fitter = pol.getTLinearFitter(); - - uint32_t nPoints = 1; - for (uint32_t i = 0; i < Dim; ++i) { - nPoints *= nAuxiliaryPoints[i]; - } - - std::vector xCords; - std::vector response; - xCords.reserve(Dim * nPoints); - response.reserve(nPoints); - - uint32_t nPolynomials[Dim]{0}; - for (uint32_t i = 0; i < Dim; ++i) { - nPolynomials[i] = getNPolynomials(i); - } - - int32_t pos[Dim + 1]{0}; - uint32_t counter = 0; - const int32_t printDebugForNFits = int32_t(nTotalFits / 20) + 1; - - for (;;) { - const bool debug = !(++counter % printDebugForNFits); - if (debug) { -#ifndef GPUCA_ALIROOT_LIB - LOGP(info, "Performing fit {} out of {}", counter, nTotalFits); -#endif - } - - checkPos(nPolynomials, pos); - - if (pos[Dim] == 1) { - break; - } - - xCords.clear(); - response.clear(); - fitInnerGrid(func, nAuxiliaryPoints, pos, fitter, xCords, response); - ++pos[0]; - } -} - -template -void NDPiecewisePolynomials::performFits(const std::vector& x, const std::vector& y) -{ - const int32_t nTotalFits = getNPolynomials(); -#ifndef GPUCA_ALIROOT_LIB - LOGP(info, "Perform fitting of {}D-Polynomials of degree {} for a total of {} fits.", Dim, Degree, nTotalFits); -#endif - - // approximate number of points - uint32_t nPoints = 2 * y.size() / nTotalFits; - - // polynomial index -> indices to datapoints - std::unordered_map> dataPointsIndices; - for (int32_t i = 0; i < nTotalFits; ++i) { - dataPointsIndices[i].reserve(nPoints); - } - - // check for each data point which polynomial to use - for (size_t i = 0; i < y.size(); ++i) { - std::array index; - float xVal[Dim]; - std::copy(x.begin() + i * Dim, x.begin() + i * Dim + Dim, xVal); - setIndex(xVal, index.data()); - - std::array indexClamped{index}; - clamp(xVal, indexClamped.data()); - - // check if data points are in the grid - if (index == indexClamped) { - // index of the polyniomial - const uint32_t idx = getDataIndex(index.data()) / MultivariatePolynomialParametersHelper::getNParameters(Degree, Dim, InteractionOnly); - - // store index to data point - dataPointsIndices[idx].emplace_back(i); - } - } - - // for fitting - MultivariatePolynomialHelper<0, 0, false> pol(Dim, Degree, InteractionOnly); - TLinearFitter fitter = pol.getTLinearFitter(); - - uint32_t counter = 0; - const int32_t printDebugForNFits = int32_t(nTotalFits / 20) + 1; - - // temp storage for x and y values for fitting - std::vector xCords; - std::vector response; - - for (int32_t i = 0; i < nTotalFits; ++i) { - const bool debug = !(++counter % printDebugForNFits); - if (debug) { -#ifndef GPUCA_ALIROOT_LIB - LOGP(info, "Performing fit {} out of {}", counter, nTotalFits); -#endif - } - - // store values for fitting - if (dataPointsIndices[i].empty()) { -#ifndef GPUCA_ALIROOT_LIB - LOGP(info, "No data points to fit"); -#endif - continue; - } - - const auto nP = dataPointsIndices[i].size(); - xCords.reserve(Dim * nP); - response.reserve(nP); - xCords.clear(); - response.clear(); - - // add datapoints to fit - for (size_t j = 0; j < nP; ++j) { - const size_t idxOrig = dataPointsIndices[i][j]; - - // insert x values at the end of xCords - const int32_t idxXStart = idxOrig * Dim; - xCords.insert(xCords.end(), x.begin() + idxXStart, x.begin() + idxXStart + Dim); - response.emplace_back(y[idxOrig]); - } - - // perform the fit on the points TODO make errors configurable - std::vector error; - const auto params = MultivariatePolynomialHelper<0, 0, false>::fit(fitter, xCords, response, error, true); - - // store parameters - std::copy(params.begin(), params.end(), &mParams[i * MultivariatePolynomialParametersHelper::getNParameters(Degree, Dim, InteractionOnly)]); - } -} - -template -void NDPiecewisePolynomials::fitInnerGrid(const std::function& func, const uint32_t nAuxiliaryPoints[/* Dim */], const int32_t currentIndex[/* Dim */], TLinearFitter& fitter, std::vector& xCords, std::vector& response) -{ - int32_t pos[Dim + 1]{0}; - - // add points which will be used for the fit - for (;;) { - checkPos(nAuxiliaryPoints, pos); - - if (pos[Dim] == 1) { - break; - } - - for (uint32_t iDim = 0; iDim < Dim; ++iDim) { - const double stepWidth = getStepWidth(iDim, nAuxiliaryPoints[iDim]); - const double vertexPos = getVertexPosition(currentIndex[iDim], iDim); - const double realPosTmp = vertexPos + pos[iDim] * stepWidth; - xCords.emplace_back(realPosTmp); - } - - // get response for last added points - const double responseTmp = func(&xCords[xCords.size() - Dim]); - response.emplace_back(responseTmp); - ++pos[0]; - } - - // perform the fit on the points TODO make errors configurable - std::vector error; - const auto params = MultivariatePolynomialHelper<0, 0, false>::fit(fitter, xCords, response, error, true); - - // store parameters - const uint32_t index = getDataIndex(currentIndex); - std::copy(params.begin(), params.end(), &mParams[index]); -} - -#ifndef GPUCA_ALIROOT_LIB -template -void NDPiecewisePolynomials::dumpToTree(const uint32_t nSamplingPoints[/* Dim */], const char* outName, const char* treeName, const bool recreateFile) const -{ - o2::utils::TreeStreamRedirector pcstream(outName, recreateFile ? "RECREATE" : "UPDATE"); - - double factor[Dim]{}; - for (uint32_t iDim = 0; iDim < Dim; ++iDim) { - factor[iDim] = (mMax[iDim] - mMin[iDim]) / (nSamplingPoints[iDim] - 1); - } - - std::vector x(Dim); - std::vector ix(Dim); - int32_t pos[Dim + 1]{0}; - - for (;;) { - checkPos(nSamplingPoints, pos); - - if (pos[Dim] == 1) { - break; - } - - for (uint32_t iDim = 0; iDim < Dim; ++iDim) { - ix[iDim] = pos[iDim]; - x[iDim] = mMin[iDim] + pos[iDim] * factor[iDim]; - } - - float value = eval(x.data()); - pcstream << treeName - << "ix=" << ix - << "x=" << x - << "value=" << value - << "\n"; - - ++pos[0]; - } - pcstream.Close(); -} -#endif - -#endif - } // namespace GPUCA_NAMESPACE::gpu #endif diff --git a/GPU/TPCFastTransformation/NDPiecewisePolynomials.inc b/GPU/TPCFastTransformation/NDPiecewisePolynomials.inc new file mode 100644 index 0000000000000..d7bb9d702e96f --- /dev/null +++ b/GPU/TPCFastTransformation/NDPiecewisePolynomials.inc @@ -0,0 +1,276 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file NDPiecewisePolynomials.inc +/// \author Matthias Kleiner + +#ifndef ALICEO2_TPC_NDPIECEWISEPOLYNOMIALS_INC +#define ALICEO2_TPC_NDPIECEWISEPOLYNOMIALS_INC + +#include +#include +#include "CommonUtils/TreeStreamRedirector.h" +#include "NDPiecewisePolynomials.h" + +namespace GPUCA_NAMESPACE::gpu +{ + +#ifndef GPUCA_ALIROOT_LIB +template +void NDPiecewisePolynomials::dumpToTree(const uint32_t nSamplingPoints[/* Dim */], const char* outName, const char* treeName, const bool recreateFile) const +{ + o2::utils::TreeStreamRedirector pcstream(outName, recreateFile ? "RECREATE" : "UPDATE"); + + double factor[Dim]{}; + for (uint32_t iDim = 0; iDim < Dim; ++iDim) { + factor[iDim] = (mMax[iDim] - mMin[iDim]) / (nSamplingPoints[iDim] - 1); + } + + std::vector x(Dim); + std::vector ix(Dim); + int32_t pos[Dim + 1]{0}; + + for (;;) { + checkPos(nSamplingPoints, pos); + + if (pos[Dim] == 1) { + break; + } + + for (uint32_t iDim = 0; iDim < Dim; ++iDim) { + ix[iDim] = pos[iDim]; + x[iDim] = mMin[iDim] + pos[iDim] * factor[iDim]; + } + + float value = eval(x.data()); + pcstream << treeName + << "ix=" << ix + << "x=" << x + << "value=" << value + << "\n"; + + ++pos[0]; + } + pcstream.Close(); +} +#endif // GPUCA_ALIROOT_LIB + +#if !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) + +template +void NDPiecewisePolynomials::loadFromFile(TFile& inpf, const char* name) +{ + NDPiecewisePolynomialContainer* gridTmp = nullptr; + inpf.GetObject(name, gridTmp); + if (gridTmp) { + setFromContainer(*gridTmp); + delete gridTmp; + } else { +#ifndef GPUCA_ALIROOT_LIB + LOGP(info, "couldnt load object {} from input file", name); +#endif + } +} + +template +void NDPiecewisePolynomials::loadFromFile(const char* fileName, const char* name) +{ + TFile f(fileName, "READ"); + loadFromFile(f, name); +} + +template +void NDPiecewisePolynomials::writeToFile(TFile& outf, const char* name) const +{ + const NDPiecewisePolynomialContainer cont = getContainer(); + outf.WriteObject(&cont, name); +} + +template +void NDPiecewisePolynomials::performFits(const std::function& func, const uint32_t nAuxiliaryPoints[/* Dim */]) +{ + const int32_t nTotalFits = getNPolynomials(); +#ifndef GPUCA_ALIROOT_LIB + LOGP(info, "Perform fitting of {}D-Polynomials of degree {} for a total of {} fits.", Dim, Degree, nTotalFits); +#endif + + MultivariatePolynomialHelper<0, 0, false> pol(Dim, Degree, InteractionOnly); + TLinearFitter fitter = pol.getTLinearFitter(); + + uint32_t nPoints = 1; + for (uint32_t i = 0; i < Dim; ++i) { + nPoints *= nAuxiliaryPoints[i]; + } + + std::vector xCords; + std::vector response; + xCords.reserve(Dim * nPoints); + response.reserve(nPoints); + + uint32_t nPolynomials[Dim]{0}; + for (uint32_t i = 0; i < Dim; ++i) { + nPolynomials[i] = getNPolynomials(i); + } + + int32_t pos[Dim + 1]{0}; + uint32_t counter = 0; + const int32_t printDebugForNFits = int32_t(nTotalFits / 20) + 1; + + for (;;) { + const bool debug = !(++counter % printDebugForNFits); + if (debug) { +#ifndef GPUCA_ALIROOT_LIB + LOGP(info, "Performing fit {} out of {}", counter, nTotalFits); +#endif + } + + checkPos(nPolynomials, pos); + + if (pos[Dim] == 1) { + break; + } + + xCords.clear(); + response.clear(); + fitInnerGrid(func, nAuxiliaryPoints, pos, fitter, xCords, response); + ++pos[0]; + } +} + +template +void NDPiecewisePolynomials::performFits(const std::vector& x, const std::vector& y) +{ + const int32_t nTotalFits = getNPolynomials(); +#ifndef GPUCA_ALIROOT_LIB + LOGP(info, "Perform fitting of {}D-Polynomials of degree {} for a total of {} fits.", Dim, Degree, nTotalFits); +#endif + + // approximate number of points + uint32_t nPoints = 2 * y.size() / nTotalFits; + + // polynomial index -> indices to datapoints + std::unordered_map> dataPointsIndices; + for (int32_t i = 0; i < nTotalFits; ++i) { + dataPointsIndices[i].reserve(nPoints); + } + + // check for each data point which polynomial to use + for (size_t i = 0; i < y.size(); ++i) { + std::array index; + float xVal[Dim]; + std::copy(x.begin() + i * Dim, x.begin() + i * Dim + Dim, xVal); + setIndex(xVal, index.data()); + + std::array indexClamped{index}; + clamp(xVal, indexClamped.data()); + + // check if data points are in the grid + if (index == indexClamped) { + // index of the polyniomial + const uint32_t idx = getDataIndex(index.data()) / MultivariatePolynomialParametersHelper::getNParameters(Degree, Dim, InteractionOnly); + + // store index to data point + dataPointsIndices[idx].emplace_back(i); + } + } + + // for fitting + MultivariatePolynomialHelper<0, 0, false> pol(Dim, Degree, InteractionOnly); + TLinearFitter fitter = pol.getTLinearFitter(); + + uint32_t counter = 0; + const int32_t printDebugForNFits = int32_t(nTotalFits / 20) + 1; + + // temp storage for x and y values for fitting + std::vector xCords; + std::vector response; + + for (int32_t i = 0; i < nTotalFits; ++i) { + const bool debug = !(++counter % printDebugForNFits); + if (debug) { +#ifndef GPUCA_ALIROOT_LIB + LOGP(info, "Performing fit {} out of {}", counter, nTotalFits); +#endif + } + + // store values for fitting + if (dataPointsIndices[i].empty()) { +#ifndef GPUCA_ALIROOT_LIB + LOGP(info, "No data points to fit"); +#endif + continue; + } + + const auto nP = dataPointsIndices[i].size(); + xCords.reserve(Dim * nP); + response.reserve(nP); + xCords.clear(); + response.clear(); + + // add datapoints to fit + for (size_t j = 0; j < nP; ++j) { + const size_t idxOrig = dataPointsIndices[i][j]; + + // insert x values at the end of xCords + const int32_t idxXStart = idxOrig * Dim; + xCords.insert(xCords.end(), x.begin() + idxXStart, x.begin() + idxXStart + Dim); + response.emplace_back(y[idxOrig]); + } + + // perform the fit on the points TODO make errors configurable + std::vector error; + const auto params = MultivariatePolynomialHelper<0, 0, false>::fit(fitter, xCords, response, error, true); + + // store parameters + std::copy(params.begin(), params.end(), &mParams[i * MultivariatePolynomialParametersHelper::getNParameters(Degree, Dim, InteractionOnly)]); + } +} + +template +void NDPiecewisePolynomials::fitInnerGrid(const std::function& func, const uint32_t nAuxiliaryPoints[/* Dim */], const int32_t currentIndex[/* Dim */], TLinearFitter& fitter, std::vector& xCords, std::vector& response) +{ + int32_t pos[Dim + 1]{0}; + + // add points which will be used for the fit + for (;;) { + checkPos(nAuxiliaryPoints, pos); + + if (pos[Dim] == 1) { + break; + } + + for (uint32_t iDim = 0; iDim < Dim; ++iDim) { + const double stepWidth = getStepWidth(iDim, nAuxiliaryPoints[iDim]); + const double vertexPos = getVertexPosition(currentIndex[iDim], iDim); + const double realPosTmp = vertexPos + pos[iDim] * stepWidth; + xCords.emplace_back(realPosTmp); + } + + // get response for last added points + const double responseTmp = func(&xCords[xCords.size() - Dim]); + response.emplace_back(responseTmp); + ++pos[0]; + } + + // perform the fit on the points TODO make errors configurable + std::vector error; + const auto params = MultivariatePolynomialHelper<0, 0, false>::fit(fitter, xCords, response, error, true); + + // store parameters + const uint32_t index = getDataIndex(currentIndex); + std::copy(params.begin(), params.end(), &mParams[index]); +} + +} // namespace GPUCA_NAMESPACE::gpu + +#endif // !defined(GPUCA_GPUCODE) && !defined(GPUCA_STANDALONE) + +#endif // ALICEO2_TPC_NDPIECEWISEPOLYNOMIALS_INC diff --git a/GPU/TPCFastTransformation/test/testMultivarPolynomials.cxx b/GPU/TPCFastTransformation/test/testMultivarPolynomials.cxx index c3373cdad63f0..a9c39e8528354 100644 --- a/GPU/TPCFastTransformation/test/testMultivarPolynomials.cxx +++ b/GPU/TPCFastTransformation/test/testMultivarPolynomials.cxx @@ -18,7 +18,7 @@ #include #include "MultivariatePolynomial.h" -#include "NDPiecewisePolynomials.h" +#include "NDPiecewisePolynomials.inc" #include namespace o2::gpu From a1cae4e860986de751bcd2fa221bc69a563a5bcc Mon Sep 17 00:00:00 2001 From: David Rohr Date: Mon, 11 Nov 2024 14:12:16 +0100 Subject: [PATCH 0038/2180] GPU: Some protection so we get a compiler warning when headers are included in wrong order --- GPU/Common/GPUCommonRtypes.h | 4 ++-- GPU/Common/GPUROOTSMatrixFwd.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/GPU/Common/GPUCommonRtypes.h b/GPU/Common/GPUCommonRtypes.h index 5ae2ddbb83b26..7aaf5a36befe2 100644 --- a/GPU/Common/GPUCommonRtypes.h +++ b/GPU/Common/GPUCommonRtypes.h @@ -20,14 +20,14 @@ #if defined(GPUCA_STANDALONE) || (defined(GPUCA_O2_LIB) && !defined(GPUCA_O2_INTERFACE)) || defined(GPUCA_GPUCODE) // clang-format off #if !defined(ROOT_Rtypes) && !defined(__CLING__) #define GPUCOMMONRTYPES_H_ACTIVE + struct MUST_NOT_USE_Rtypes_h {}; + typedef MUST_NOT_USE_Rtypes_h TClass; #define ClassDef(name,id) #define ClassDefNV(name, id) #define ClassDefOverride(name, id) #define ClassImp(name) #define templateClassImp(name) #ifndef GPUCA_GPUCODE_DEVICE -// typedef uint64_t ULong64_t; -// typedef uint32_t UInt_t; #include #endif #endif diff --git a/GPU/Common/GPUROOTSMatrixFwd.h b/GPU/Common/GPUROOTSMatrixFwd.h index a3b5abc55d3bc..44b2254949df2 100644 --- a/GPU/Common/GPUROOTSMatrixFwd.h +++ b/GPU/Common/GPUROOTSMatrixFwd.h @@ -52,7 +52,7 @@ template class MatRepStdGPU; } // namespace detail -#if !defined(GPUCA_STANDALONE) && !defined(GPUCA_GPUCODE) +#if !defined(GPUCA_STANDALONE) && !defined(GPUCA_GPUCODE) && !defined(GPUCOMMONRTYPES_H_ACTIVE) template using SVector = ROOT::Math::SVector; template From bdb39f613566718201c36f0c5310e1eb05d771fb Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 13 Nov 2024 19:37:19 +0100 Subject: [PATCH 0039/2180] GPU: Workaround for OpenCL --- Common/MathUtils/include/MathUtils/SMatrixGPU.h | 2 ++ GPU/GPUTracking/dEdx/GPUdEdx.h | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/Common/MathUtils/include/MathUtils/SMatrixGPU.h b/Common/MathUtils/include/MathUtils/SMatrixGPU.h index 2bfdcf54752b2..5ecdcd75a9906 100644 --- a/Common/MathUtils/include/MathUtils/SMatrixGPU.h +++ b/Common/MathUtils/include/MathUtils/SMatrixGPU.h @@ -516,12 +516,14 @@ class SMatrixGPU R mRep; }; +#ifndef __OPENCL__ // TODO: current C++ for OpenCL 2021 is at C++17, so no concepts. But we don't need this trick for OpenCL anyway, so we can just hide it. template requires(sizeof(typename X::traits_type::pos_type) != 0) // do not provide a template to fair::Logger, etc... (pos_type is a member type of all std::ostream classes) GPUd() X& operator<<(Y& y, const SMatrixGPU&) { return y; } +#endif template GPUdi() SMatrixGPU::SMatrixGPU(SMatrixIdentity) diff --git a/GPU/GPUTracking/dEdx/GPUdEdx.h b/GPU/GPUTracking/dEdx/GPUdEdx.h index 9a1784e2be49a..516d1fced0a20 100644 --- a/GPU/GPUTracking/dEdx/GPUdEdx.h +++ b/GPU/GPUTracking/dEdx/GPUdEdx.h @@ -212,7 +212,7 @@ GPUdi() void GPUdEdx::fillSubThreshold(int32_t padRow, const GPUParam& GPUrestri mNSubThresh++; } -#endif // !GPUCA_HAVE_O2HEADERS || __OPENCL1__ +#endif // !GPUCA_HAVE_O2HEADERS || GPUCA_OPENCL1 } // namespace gpu } // namespace GPUCA_NAMESPACE From 8aeaa54fc122d373dda19448180ce92681981bac Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 13 Nov 2024 20:05:36 +0100 Subject: [PATCH 0040/2180] GPU: Simplify __OPENCL__ macros using __OPENCL1__ --- GPU/Common/GPUCommonConstants.h | 2 +- GPU/Common/GPUCommonDef.h | 4 ++-- GPU/Common/GPUCommonMath.h | 10 +++++----- GPU/Common/GPUCommonTypeTraits.h | 2 +- GPU/GPUTracking/Base/GPUParam.inc | 4 ++-- .../Base/opencl-common/GPUReconstructionOCL.cl | 5 +++++ GPU/GPUTracking/Base/opencl2/CMakeLists.txt | 2 -- GPU/GPUTracking/DataTypes/GPUDataTypes.h | 2 +- GPU/GPUTracking/DataTypes/GPUO2DataTypes.h | 4 ++-- GPU/GPUTracking/DataTypes/GPUSettings.h | 2 +- .../DataTypes/GPUTPCGMPolynomialField.h | 4 ++-- GPU/GPUTracking/DataTypes/GPUTPCGeometry.h | 10 +++++----- .../Definitions/GPUDefConstantsAndSettings.h | 2 +- .../SliceTracker/GPUTPCGlobalTracking.cxx | 4 ++-- .../SliceTracker/GPUTPCGlobalTracking.h | 2 +- GPU/GPUTracking/SliceTracker/GPUTPCSliceOutput.h | 2 +- GPU/GPUTracking/SliceTracker/GPUTPCTracker.cxx | 2 +- GPU/GPUTracking/SliceTracker/GPUTPCTracker.h | 2 +- .../SliceTracker/GPUTPCTrackletConstructor.cxx | 16 ++++++++-------- .../SliceTracker/GPUTPCTrackletConstructor.h | 2 +- 20 files changed, 43 insertions(+), 40 deletions(-) diff --git a/GPU/Common/GPUCommonConstants.h b/GPU/Common/GPUCommonConstants.h index 5744c078dc197..883f64b7bdd12 100644 --- a/GPU/Common/GPUCommonConstants.h +++ b/GPU/Common/GPUCommonConstants.h @@ -17,7 +17,7 @@ #include "GPUCommonDef.h" -#if !defined(__OPENCL__) || defined(__OPENCLCPP__) +#if !defined(__OPENCL1__) namespace GPUCA_NAMESPACE::gpu::gpu_common_constants { static CONSTEXPR const float kCLight = 0.000299792458f; diff --git a/GPU/Common/GPUCommonDef.h b/GPU/Common/GPUCommonDef.h index a8bf772d7aacc..ac3d7279fbaf4 100644 --- a/GPU/Common/GPUCommonDef.h +++ b/GPU/Common/GPUCommonDef.h @@ -30,7 +30,7 @@ //Some GPU configuration settings, must be included first #include "GPUCommonDefSettings.h" -#if (!defined(__OPENCL__) || defined(__OPENCLCPP__)) && (!(defined(__CINT__) || defined(__ROOTCINT__)) || defined(__CLING__)) && defined(__cplusplus) && __cplusplus >= 201103L +#if !defined(__OPENCL1__) && (!(defined(__CINT__) || defined(__ROOTCINT__)) || defined(__CLING__)) && defined(__cplusplus) && __cplusplus >= 201103L #define GPUCA_NOCOMPAT // C++11 + No old ROOT5 + No old OpenCL #ifndef __OPENCL__ #define GPUCA_NOCOMPAT_ALLOPENCL // + No OpenCL at all @@ -82,7 +82,7 @@ #define GPUCA_NAMESPACE o2 #endif -#if (defined(__CUDACC__) && defined(GPUCA_CUDA_NO_CONSTANT_MEMORY)) || (defined(__HIPCC__) && defined(GPUCA_HIP_NO_CONSTANT_MEMORY)) || (defined(__OPENCL__) && !defined(__OPENCLCPP__) && defined(GPUCA_OPENCL_NO_CONSTANT_MEMORY)) || (defined(__OPENCLCPP__) && defined(GPUCA_OPENCLCPP_NO_CONSTANT_MEMORY)) +#if (defined(__CUDACC__) && defined(GPUCA_CUDA_NO_CONSTANT_MEMORY)) || (defined(__HIPCC__) && defined(GPUCA_HIP_NO_CONSTANT_MEMORY)) || (defined(__OPENCL1__) && defined(GPUCA_OPENCL_NO_CONSTANT_MEMORY)) || (defined(__OPENCLCPP__) && defined(GPUCA_OPENCLCPP_NO_CONSTANT_MEMORY)) #define GPUCA_NO_CONSTANT_MEMORY #elif defined(__CUDACC__) || defined(__HIPCC__) #define GPUCA_HAS_GLOBAL_SYMBOL_CONSTANT_MEM diff --git a/GPU/Common/GPUCommonMath.h b/GPU/Common/GPUCommonMath.h index 8b129ff29a987..bc842d00c6568 100644 --- a/GPU/Common/GPUCommonMath.h +++ b/GPU/Common/GPUCommonMath.h @@ -31,7 +31,7 @@ #include #endif -#if !defined(__OPENCL__) || defined(__OPENCLCPP__) +#if !defined(__OPENCL1__) namespace GPUCA_NAMESPACE { namespace gpu @@ -220,7 +220,7 @@ GPUdi() uint32_t GPUCommonMath::Float2UIntReint(const float& x) { #if defined(GPUCA_GPUCODE_DEVICE) && (defined(__CUDACC__) || defined(__HIPCC__)) return __float_as_uint(x); -#elif defined(GPUCA_GPUCODE_DEVICE) && (defined(__OPENCL__) || defined(__OPENCLCPP__)) +#elif defined(GPUCA_GPUCODE_DEVICE) && defined(__OPENCL__) return as_uint(x); #else return reinterpret_cast(x); @@ -289,7 +289,7 @@ GPUhdi() void GPUCommonMath::SinCosd(double x, double& s, double& c) GPUdi() uint32_t GPUCommonMath::Clz(uint32_t x) { -#if (defined(__GNUC__) || defined(__clang__) || defined(__CUDACC__) || defined(__HIPCC__)) && (!defined(__OPENCL__) || defined(__OPENCLCPP__)) +#if (defined(__GNUC__) || defined(__clang__) || defined(__CUDACC__) || defined(__HIPCC__)) && !defined(__OPENCL1__) return x == 0 ? 32 : CHOICE(__builtin_clz(x), __clz(x), __builtin_clz(x)); // use builtin if available #else for (int32_t i = 31; i >= 0; i--) { @@ -303,7 +303,7 @@ GPUdi() uint32_t GPUCommonMath::Clz(uint32_t x) GPUdi() uint32_t GPUCommonMath::Popcount(uint32_t x) { -#if (defined(__GNUC__) || defined(__clang__) || defined(__CUDACC__) || defined(__HIPCC__)) && (!defined(__OPENCL__) /*|| defined(__OPENCLCPP__)*/) // TODO: remove OPENCLCPP workaround when reported SPIR-V bug is fixed +#if (defined(__GNUC__) || defined(__clang__) || defined(__CUDACC__) || defined(__HIPCC__)) && (!defined(__OPENCL__) /* !defined(__OPENCL1__)*/) // TODO: exclude only OPENCLC (not CPP) when reported SPIR-V bug is fixed // use builtin if available return CHOICE(__builtin_popcount(x), __popc(x), __builtin_popcount(x)); #else @@ -563,7 +563,7 @@ GPUdii() void GPUCommonMath::AtomicMinInternal(GPUglobalref() GPUgeneric() GPUAt #undef CHOICE -#if !defined(__OPENCL__) || defined(__OPENCLCPP__) +#if !defined(__OPENCL1__) } } #endif diff --git a/GPU/Common/GPUCommonTypeTraits.h b/GPU/Common/GPUCommonTypeTraits.h index 2ae524f8d1c76..88fcc9b838a65 100644 --- a/GPU/Common/GPUCommonTypeTraits.h +++ b/GPU/Common/GPUCommonTypeTraits.h @@ -21,7 +21,7 @@ #ifndef GPUCA_GPUCODE_COMPILEKERNELS #include #endif -#elif !defined(__OPENCL__) || defined(__OPENCLCPP__) +#elif !defined(__OPENCL1__) // We just reimplement some type traits in std for the GPU namespace std { diff --git a/GPU/GPUTracking/Base/GPUParam.inc b/GPU/GPUTracking/Base/GPUParam.inc index c7c526471d505..41ed3c8f203cb 100644 --- a/GPU/GPUTracking/Base/GPUParam.inc +++ b/GPU/GPUTracking/Base/GPUParam.inc @@ -17,7 +17,7 @@ #include "GPUParam.h" #include "GPUTPCGMMergedTrackHit.h" -#if !defined(__OPENCL__) || defined(__OPENCLCPP__) +#if !defined(__OPENCL1__) #include "GPUTPCClusterOccupancyMap.h" #endif @@ -228,7 +228,7 @@ GPUdi() void MEM_LG(GPUParam)::UpdateClusterError2ByState(int16_t clusterState, MEM_CLASS_PRE() GPUdi() float MEM_LG(GPUParam)::GetUnscaledMult(float time) const { -#if !defined(__OPENCL__) || defined(__OPENCLCPP__) +#if !defined(__OPENCL1__) if (!occupancyMap) { return 0.f; } diff --git a/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.cl b/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.cl index 42a640579e9e3..672c4b63eb476 100644 --- a/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.cl +++ b/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.cl @@ -14,6 +14,11 @@ // clang-format off #define __OPENCL__ +#if defined(__cplusplus) && __cplusplus >= 201703L + #define __OPENCLCPP__ +#else + #define __OPENCL1__ +#endif #define GPUCA_GPUTYPE_OPENCL #ifdef __OPENCLCPP__ diff --git a/GPU/GPUTracking/Base/opencl2/CMakeLists.txt b/GPU/GPUTracking/Base/opencl2/CMakeLists.txt index ec2a4446142c8..0a4168b130766 100644 --- a/GPU/GPUTracking/Base/opencl2/CMakeLists.txt +++ b/GPU/GPUTracking/Base/opencl2/CMakeLists.txt @@ -32,8 +32,6 @@ set(OCL_DEFINECL "-D$ GPUdii() void GPUTPCGlobalTrackingCopyNumbers::Thread<0>(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUsharedref() MEM_LOCAL(GPUSharedMemory) & smem, processorType& GPUrestrict() tracker, int32_t n) diff --git a/GPU/GPUTracking/SliceTracker/GPUTPCGlobalTracking.h b/GPU/GPUTracking/SliceTracker/GPUTPCGlobalTracking.h index 075957ff4c8c8..9d732a582b1c4 100644 --- a/GPU/GPUTracking/SliceTracker/GPUTPCGlobalTracking.h +++ b/GPU/GPUTracking/SliceTracker/GPUTPCGlobalTracking.h @@ -25,7 +25,7 @@ namespace gpu MEM_CLASS_PRE() class GPUTPCTracker; -#if !defined(__OPENCL__) || defined(__OPENCLCPP__) +#if !defined(__OPENCL1__) class GPUTPCGlobalTracking : public GPUKernelTemplate { public: diff --git a/GPU/GPUTracking/SliceTracker/GPUTPCSliceOutput.h b/GPU/GPUTracking/SliceTracker/GPUTPCSliceOutput.h index 8892225f119cd..3ab5b0a331f31 100644 --- a/GPU/GPUTracking/SliceTracker/GPUTPCSliceOutput.h +++ b/GPU/GPUTracking/SliceTracker/GPUTPCSliceOutput.h @@ -44,7 +44,7 @@ class GPUTPCSliceOutput } GPUhd() uint32_t NLocalTracks() const { return mNLocalTracks; } GPUhd() uint32_t NTrackClusters() const { return mNTrackClusters; } -#if !defined(__OPENCL__) || defined(__OPENCLCPP__) +#if !defined(__OPENCL1__) GPUhd() const GPUTPCTrack* GetFirstTrack() const { return (const GPUTPCTrack*)((const char*)this + sizeof(*this)); diff --git a/GPU/GPUTracking/SliceTracker/GPUTPCTracker.cxx b/GPU/GPUTracking/SliceTracker/GPUTPCTracker.cxx index 552d61a88fc39..7428a4ccbd0ed 100644 --- a/GPU/GPUTracking/SliceTracker/GPUTPCTracker.cxx +++ b/GPU/GPUTracking/SliceTracker/GPUTPCTracker.cxx @@ -22,7 +22,7 @@ #include "GPUO2DataTypes.h" #include "GPUTPCTrackParam.h" #include "GPUParam.inc" -#if !defined(__OPENCL__) || defined(__OPENCLCPP__) +#if !defined(__OPENCL1__) #include "GPUTPCConvertImpl.h" #endif diff --git a/GPU/GPUTracking/SliceTracker/GPUTPCTracker.h b/GPU/GPUTracking/SliceTracker/GPUTPCTracker.h index f19b4f0a6c0a7..da8d3d1fb28d4 100644 --- a/GPU/GPUTracking/SliceTracker/GPUTPCTracker.h +++ b/GPU/GPUTracking/SliceTracker/GPUTPCTracker.h @@ -94,7 +94,7 @@ class GPUTPCTracker : public GPUProcessor StructGPUParameters gpuParameters; // GPU parameters }; -#if !defined(__OPENCL__) || defined(__OPENCLCPP__) +#if !defined(__OPENCL1__) GPUhdi() GPUglobalref() const GPUTPCClusterData* ClusterData() const { return mData.ClusterData(); diff --git a/GPU/GPUTracking/SliceTracker/GPUTPCTrackletConstructor.cxx b/GPU/GPUTracking/SliceTracker/GPUTPCTrackletConstructor.cxx index 9d6ed630dee8c..ba17b88436845 100644 --- a/GPU/GPUTracking/SliceTracker/GPUTPCTrackletConstructor.cxx +++ b/GPU/GPUTracking/SliceTracker/GPUTPCTrackletConstructor.cxx @@ -21,7 +21,7 @@ #include "GPUTPCTracker.h" #include "GPUTPCTracklet.h" #include "GPUTPCTrackletConstructor.h" -#if !defined(__OPENCL__) || defined(__OPENCLCPP__) +#if !defined(__OPENCL1__) #include "GPUTPCGlobalTracking.h" #include "CorrectionMapsHelper.h" #ifdef GPUCA_HAVE_O2HEADERS @@ -140,14 +140,14 @@ GPUdic(2, 1) void GPUTPCTrackletConstructor::UpdateTracklet(int32_t /*nBlocks*/, float z = z0 + hh.y * stepZ; if (iRow != r.mStartRow || !tracker.Param().par.continuousTracking) { tParam.ConstrainZ(z, tracker.ISlice(), z0, r.mLastZ); -#if !defined(__OPENCL__) || defined(__OPENCLCPP__) +#if !defined(__OPENCL1__) tracker.GetConstantMem()->calibObjects.fastTransformHelper->TransformXYZ(tracker.ISlice(), iRow, x, y, z); #endif } if (iRow == r.mStartRow) { if (tracker.Param().par.continuousTracking) { float refZ = ((z > 0) ? tracker.Param().rec.tpc.defaultZOffsetOverR : -tracker.Param().rec.tpc.defaultZOffsetOverR) * x; -#if !defined(__OPENCL__) || defined(__OPENCLCPP__) +#if !defined(__OPENCL1__) float zTmp = refZ; tracker.GetConstantMem()->calibObjects.fastTransformHelper->TransformXYZ(tracker.ISlice(), iRow, x, y, zTmp); z += zTmp - refZ; // Add zCorrection (=zTmp - refZ) to z, such that zOffset is set such, that transformed (z - zOffset) becomes refZ @@ -266,7 +266,7 @@ GPUdic(2, 1) void GPUTPCTrackletConstructor::UpdateTracklet(int32_t /*nBlocks*/, r.mNMissed++; float x = row.X(); -#if !defined(__OPENCL__) || defined(__OPENCLCPP__) +#if !defined(__OPENCL1__) { float tmpY, tmpZ; if (!tParam.GetPropagatedYZ(tracker.Param().bzCLight, x, tmpY, tmpZ)) { @@ -299,7 +299,7 @@ GPUdic(2, 1) void GPUTPCTrackletConstructor::UpdateTracklet(int32_t /*nBlocks*/, GPUglobalref() const cahit2* hits = tracker.HitData(row); GPUglobalref() const calink* firsthit = tracker.FirstHitInBin(row); #endif //! GPUCA_TEXTURE_FETCH_CONSTRUCTOR -#if !defined(__OPENCL__) || defined(__OPENCLCPP__) +#if !defined(__OPENCL1__) tracker.GetConstantMem()->calibObjects.fastTransformHelper->InverseTransformYZtoNominalYZ(tracker.ISlice(), iRow, yUncorrected, zUncorrected, yUncorrected, zUncorrected); #endif @@ -391,7 +391,7 @@ GPUdic(2, 1) void GPUTPCTrackletConstructor::UpdateTracklet(int32_t /*nBlocks*/, } } while (false); (void)found; -#if defined(GPUCA_HAVE_O2HEADERS) && (!defined(__OPENCL__) || defined(__OPENCLCPP__)) +#if defined(GPUCA_HAVE_O2HEADERS) && !defined(__OPENCL1__) if (!found && tracker.GetConstantMem()->calibObjects.dEdxCalibContainer) { uint32_t pad = CAMath::Float2UIntRn(tracker.Param().tpcGeometry.LinearY2Pad(tracker.ISlice(), iRow, yUncorrected)); if (pad < tracker.Param().tpcGeometry.NPads(iRow) && tracker.GetConstantMem()->calibObjects.dEdxCalibContainer->isDead(tracker.ISlice(), iRow, pad)) { @@ -461,7 +461,7 @@ GPUdic(2, 1) void GPUTPCTrackletConstructor::DoTracklet(GPUconstantref() MEM_GLO iRow = r.mEndRow; iRowEnd = -1; float x = tracker.Row(r.mEndRow).X(); -#if !defined(__OPENCL__) || defined(__OPENCLCPP__) +#if !defined(__OPENCL1__) { float tmpY, tmpZ; if (tParam.GetPropagatedYZ(tracker.Param().bzCLight, x, tmpY, tmpZ)) { @@ -584,7 +584,7 @@ GPUd() int32_t GPUTPCTrackletConstructor::FetchTracklet(GPUconstantref() MEM_GLO #endif // GPUCA_GPUCODE -#if !defined(__OPENCL__) || defined(__OPENCLCPP__) +#if !defined(__OPENCL1__) template <> GPUd() int32_t GPUTPCTrackletConstructor::GPUTPCTrackletConstructorGlobalTracking(GPUconstantref() MEM_GLOBAL(GPUTPCTracker) & GPUrestrict() tracker, GPUsharedref() GPUTPCGlobalTracking::GPUSharedMemory& sMem, MEM_LG(GPUTPCTrackParam) & GPUrestrict() tParam, int32_t row, int32_t increment, int32_t iTracklet, calink* rowHits) { diff --git a/GPU/GPUTracking/SliceTracker/GPUTPCTrackletConstructor.h b/GPU/GPUTracking/SliceTracker/GPUTPCTrackletConstructor.h index 06dd941ca5cf7..effee4fa757b8 100644 --- a/GPU/GPUTracking/SliceTracker/GPUTPCTrackletConstructor.h +++ b/GPU/GPUTracking/SliceTracker/GPUTPCTrackletConstructor.h @@ -100,7 +100,7 @@ class GPUTPCTrackletConstructor GPUd() static int32_t FetchTracklet(GPUconstantref() MEM_GLOBAL(GPUTPCTracker) & tracker, GPUsharedref() MEM_LOCAL(GPUSharedMemory) & sMem); #endif // GPUCA_GPUCODE -#if !defined(__OPENCL__) || defined(__OPENCLCPP__) +#if !defined(__OPENCL1__) template GPUd() static int32_t GPUTPCTrackletConstructorGlobalTracking(GPUconstantref() MEM_GLOBAL(GPUTPCTracker) & tracker, GPUsharedref() T& sMem, GPUTPCTrackParam& tParam, int32_t startrow, int32_t increment, int32_t iTracklet, calink* rowHits); #endif From 1901f380fba4079c162be24d82c2aa592172c084 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 13 Nov 2024 09:35:07 +0100 Subject: [PATCH 0041/2180] GPU Display: make connecting A and C side segments of a track optional --- GPU/GPUTracking/Definitions/GPUSettingsList.h | 3 +- GPU/GPUTracking/display/GPUDisplay.cxx | 1 + GPU/GPUTracking/display/GPUDisplay.h | 1 + .../display/frontend/GPUDisplayKeys.cxx | 7 ++-- .../display/helpers/GPUDisplayHelpers.cxx | 7 ++++ .../display/render/GPUDisplayDraw.cxx | 33 +++++++++++-------- 6 files changed, 36 insertions(+), 16 deletions(-) diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index 106a222862f49..c4e0dadb87659 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -346,7 +346,8 @@ AddOption(drawTracksAndFilter, bool, false, "", 0, "Use AND filter instead of OR AddOption(propagateLoopers, bool, false, "", 0, "Enabale propagation of loopers") AddOption(clustersOnly, bool, false, "", 0, "Visualize clusters only") AddOption(clustersOnNominalRow, bool, false, "", 0, "Show clusters at nominal x of pad row for early-transformed data") -AddOption(separateGlobalTracks, bool, false, "", 0, "Separate global tracks") +AddOption(separateGlobalTracks, bool, false, "", 0, "Draw track segments propagated to adjacent sectors separately") +AddOption(splitCETracks, int8_t, -1, "", 0, "Split CE tracks when they cross the central electrode (-1 = for triggered data)") AddOption(markClusters, int32_t, 0, "", 0, "Mark clusters") AddOption(markFakeClusters, int32_t, 0, "", 0, "Mark fake clusters") AddOption(markAdjacentClusters, int32_t, 0, "", 0, "Mark adjacent clusters") diff --git a/GPU/GPUTracking/display/GPUDisplay.cxx b/GPU/GPUTracking/display/GPUDisplay.cxx index 74d89fbf6de81..56e59d664491a 100644 --- a/GPU/GPUTracking/display/GPUDisplay.cxx +++ b/GPU/GPUTracking/display/GPUDisplay.cxx @@ -611,6 +611,7 @@ void GPUDisplay::DrawGLScene_internal(float animateTime, bool renderToMixBuffer) bool showTimer = false; bool doScreenshot = (mRequestScreenshot || mAnimateScreenshot) && animateTime < 0; + updateOptions(); if (animateTime < 0 && (mUpdateEventData || mResetScene || mUpdateVertexLists) && mIOPtrs) { disableUnsupportedOptions(); } diff --git a/GPU/GPUTracking/display/GPUDisplay.h b/GPU/GPUTracking/display/GPUDisplay.h index 38dacae60c51a..ab6fe540d01bf 100644 --- a/GPU/GPUTracking/display/GPUDisplay.h +++ b/GPU/GPUTracking/display/GPUDisplay.h @@ -150,6 +150,7 @@ class GPUDisplay : public GPUDisplayInterface void DrawGLScene_drawCommands(); int32_t InitDisplay_internal(); int32_t getNumThreads(); + void updateOptions(); void disableUnsupportedOptions(); int32_t buildTrackFilter(); const GPUTPCTracker& sliceTracker(int32_t iSlice); diff --git a/GPU/GPUTracking/display/frontend/GPUDisplayKeys.cxx b/GPU/GPUTracking/display/frontend/GPUDisplayKeys.cxx index 1842c276a580c..8dccdc60c0d93 100644 --- a/GPU/GPUTracking/display/frontend/GPUDisplayKeys.cxx +++ b/GPU/GPUTracking/display/frontend/GPUDisplayKeys.cxx @@ -35,7 +35,7 @@ const char* HelpText[] = { "[L] / [K] Draw single collisions (next / previous)", "[C] Colorcode clusters of different collisions", "[v] Hide rejected clusters from tracks", - "[j] Show global tracks as additional segments of final tracks", + "[j] Show tracks segments propagated to adjacent sector in different color / splt CE tracks", "[u] Cycle through track filter", "[E] / [G] Extrapolate tracks / loopers", "[t] / [T] Take Screenshot / Record Animation to pictures", @@ -164,8 +164,11 @@ void GPUDisplay::HandleKey(uint8_t key) mPrintInfoText &= 3; SetInfo("Info text display - console: %s, onscreen %s", (mPrintInfoText & 2) ? "enabled" : "disabled", (mPrintInfoText & 1) ? "enabled" : "disabled"); } else if (key == 'j') { + if (mCfgH.separateGlobalTracks) { + mCfgH.splitCETracks ^= 1; + } mCfgH.separateGlobalTracks ^= 1; - SetInfo("Seperated display of global tracks %s", mCfgH.separateGlobalTracks ? "enabled" : "disabled"); + SetInfo("Seperated display of tracks propagated to adjacent sectors %s / of CE tracks %s", mCfgH.separateGlobalTracks ? "enabled" : "disabled", mCfgH.splitCETracks ? "enabled" : "disabled"); } else if (key == 'c') { if (mCfgH.markClusters == 0) { mCfgH.markClusters = 1; diff --git a/GPU/GPUTracking/display/helpers/GPUDisplayHelpers.cxx b/GPU/GPUTracking/display/helpers/GPUDisplayHelpers.cxx index cd73cc0b9b34f..d782898380281 100644 --- a/GPU/GPUTracking/display/helpers/GPUDisplayHelpers.cxx +++ b/GPU/GPUTracking/display/helpers/GPUDisplayHelpers.cxx @@ -36,6 +36,13 @@ int32_t GPUDisplay::getNumThreads() } } +void GPUDisplay::updateOptions() +{ + if (mCfgH.splitCETracks == -1 && mParam) { + mCfgH.splitCETracks = mParam->continuousMaxTimeBin != 0; + } +} + void GPUDisplay::disableUnsupportedOptions() { if (!mIOPtrs->mergedTrackHitAttachment) { diff --git a/GPU/GPUTracking/display/render/GPUDisplayDraw.cxx b/GPU/GPUTracking/display/render/GPUDisplayDraw.cxx index 746c41938e2e1..ffebc373b253f 100644 --- a/GPU/GPUTracking/display/render/GPUDisplayDraw.cxx +++ b/GPU/GPUTracking/display/render/GPUDisplayDraw.cxx @@ -45,7 +45,6 @@ using namespace GPUCA_NAMESPACE::gpu; #define GET_CID(slice, i) (mParam->par.earlyTpcTransform ? mIOPtrs->clusterData[slice][i].id : (mIOPtrs->clustersNative->clusterOffset[slice][0] + i)) -#define SEPERATE_GLOBAL_TRACKS_LIMIT (mCfgH.separateGlobalTracks ? tGLOBALTRACK : TRACK_TYPE_ID_LIMIT) const GPUTRDGeometry* GPUDisplay::trdGeometry() { return (GPUTRDGeometry*)mCalib->trdGeometry; } const GPUTPCTracker& GPUDisplay::sliceTracker(int32_t iSlice) { return mChain->GetTPCSliceTrackers()[iSlice]; } @@ -421,6 +420,8 @@ void GPUDisplay::DrawFinal(int32_t iSlice, int32_t /*iCol*/, GPUTPCGMPropagator* } // Print TPC part of track + int32_t separateGlobalTracksLimit = (mCfgH.separateGlobalTracks ? tGLOBALTRACK : TRACK_TYPE_ID_LIMIT); + uint32_t lastSide = -1; for (int32_t k = 0; k < nClusters; k++) { if constexpr (std::is_same_v) { if (mCfgH.hideRejectedClusters && (mIOPtrs->mergedTrackHits[track->FirstClusterRef() + k].state & GPUTPCGMMergedTrackHit::flagReject)) { @@ -435,9 +436,15 @@ void GPUDisplay::DrawFinal(int32_t iSlice, int32_t /*iCol*/, GPUTPCGMPropagator* } int32_t w = mGlobalPos[cid].w; if (drawing) { - drawPointLinestrip(iSlice, cid, tFINALTRACK, SEPERATE_GLOBAL_TRACKS_LIMIT); + if (mCfgH.splitCETracks && lastSide != (mGlobalPos[cid].z < 0)) { + insertVertexList(vBuf[0], startCountInner, mVertexBuffer[iSlice].size()); + drawing = false; + lastCluster = -1; + } else { + drawPointLinestrip(iSlice, cid, tFINALTRACK, separateGlobalTracksLimit); + } } - if (w == SEPERATE_GLOBAL_TRACKS_LIMIT) { + if (w == separateGlobalTracksLimit) { if (drawing) { insertVertexList(vBuf[0], startCountInner, mVertexBuffer[iSlice].size()); } @@ -445,21 +452,21 @@ void GPUDisplay::DrawFinal(int32_t iSlice, int32_t /*iCol*/, GPUTPCGMPropagator* } else { if (!drawing) { startCountInner = mVertexBuffer[iSlice].size(); - } - if (!drawing) { - drawPointLinestrip(iSlice, cid, tFINALTRACK, SEPERATE_GLOBAL_TRACKS_LIMIT); - } - if (!drawing && lastCluster != -1) { - if constexpr (std::is_same_v) { - cid = mIOPtrs->mergedTrackHits[track->FirstClusterRef() + lastCluster].num; - } else { - cid = &track->getCluster(mIOPtrs->outputClusRefsTPCO2, lastCluster, *mIOPtrs->clustersNative) - mIOPtrs->clustersNative->clustersLinear; + if (lastCluster != -1 && (!mCfgH.splitCETracks || lastSide == (mGlobalPos[cid].z < 0))) { + int32_t lastcid; + if constexpr (std::is_same_v) { + lastcid = mIOPtrs->mergedTrackHits[track->FirstClusterRef() + lastCluster].num; + } else { + lastcid = &track->getCluster(mIOPtrs->outputClusRefsTPCO2, lastCluster, *mIOPtrs->clustersNative) - mIOPtrs->clustersNative->clustersLinear; + } + drawPointLinestrip(iSlice, lastcid, tFINALTRACK, separateGlobalTracksLimit); } - drawPointLinestrip(iSlice, cid, 7, SEPERATE_GLOBAL_TRACKS_LIMIT); + drawPointLinestrip(iSlice, cid, tFINALTRACK, separateGlobalTracksLimit); } drawing = true; } lastCluster = k; + lastSide = mGlobalPos[cid].z < 0; } // Print ITS part of track From 1a86fd1e30ecd77d8dc58c7d5bd2612f6c0efaeb Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 14 Nov 2024 08:57:13 +0100 Subject: [PATCH 0042/2180] DPL: Cleanup unneeded headers --- Framework/Core/src/CommonDataProcessors.cxx | 13 ------------- 1 file changed, 13 deletions(-) diff --git a/Framework/Core/src/CommonDataProcessors.cxx b/Framework/Core/src/CommonDataProcessors.cxx index d893e16513f40..737e1b7e635c8 100644 --- a/Framework/Core/src/CommonDataProcessors.cxx +++ b/Framework/Core/src/CommonDataProcessors.cxx @@ -17,39 +17,26 @@ #include "Framework/DataProcessingHeader.h" #include "Framework/DataDescriptorQueryBuilder.h" #include "Framework/DataDescriptorMatcher.h" -#include "Framework/DataOutputDirector.h" #include "Framework/DataProcessorSpec.h" #include "Framework/DataProcessingStats.h" #include "Framework/DataSpecUtils.h" -#include "Framework/TableBuilder.h" -#include "Framework/EndOfStreamContext.h" #include "Framework/InitContext.h" #include "Framework/InputSpec.h" -#include "Framework/Logger.h" -#include "Framework/OutputSpec.h" #include "Framework/RawDeviceService.h" #include "Framework/TimesliceIndex.h" #include "Framework/Variant.h" -#include "../../../Algorithm/include/Algorithm/HeaderStack.h" -#include "Framework/OutputObjHeader.h" -#include "Framework/StringHelpers.h" #include "Framework/ChannelSpec.h" -#include "Framework/ChannelSpecHelpers.h" #include "Framework/ExternalFairMQDeviceProxy.h" #include "Framework/RuntimeError.h" #include "Framework/RateLimiter.h" #include "Framework/PluginManager.h" -#include "Framework/DeviceSpec.h" -#include "WorkflowHelpers.h" #include #include -#include #include #include #include #include -#include using namespace o2::framework::data_matcher; From e26063964797762f2719dfeadb97c62b335fd9e8 Mon Sep 17 00:00:00 2001 From: Matteo Concas Date: Thu, 14 Nov 2024 12:23:03 +0100 Subject: [PATCH 0043/2180] ITSGPU: disable linter for false positive in CUB calls Reported `0`s are detected as to be used as `nullptr`. This is wrong. See also the signature here: https://rocm.docs.amd.com/projects/hipCUB/en/docs-5.7.0/.doxygen/docBin/html/classhipcub_1_1DeviceScan.html --- .../ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu index e31e3f378298b..73dcf3bcb4894 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -842,14 +842,14 @@ void countCellsHandler( cellsLUTsHost, // d_in cellsLUTsHost, // d_out nTracklets + 1, // num_items - 0)); + 0)); // NOLINT: this is the offset of the sum, not a pointer discardResult(cudaMalloc(&d_temp_storage, temp_storage_bytes)); gpuCheckError(cub::DeviceScan::ExclusiveSum(d_temp_storage, // d_temp_storage temp_storage_bytes, // temp_storage_bytes cellsLUTsHost, // d_in cellsLUTsHost, // d_out nTracklets + 1, // num_items - 0)); + 0)); // NOLINT: this is the offset of the sum, not a pointer // gpu::printBufferLayerOnThread<<<1, 1>>>(layer, cellsLUTsHost, nTracklets + 1); gpuCheckError(cudaFree(d_temp_storage)); } @@ -934,14 +934,14 @@ void countCellNeighboursHandler(CellSeed** cellsLayersDevice, neighboursIndexTable, // d_in neighboursIndexTable, // d_out nCells + 1, // num_items - 0)); + 0)); // NOLINT: this is the offset of the sum, not a pointer discardResult(cudaMalloc(&d_temp_storage_2, temp_storage_bytes_2)); gpuCheckError(cub::DeviceScan::ExclusiveSum(d_temp_storage_2, // d_temp_storage temp_storage_bytes_2, // temp_storage_bytes neighboursIndexTable, // d_in neighboursIndexTable, // d_out nCells + 1, // num_items - 0)); + 0)); // NOLINT: this is the offset of the sum, not a pointer gpuCheckError(cudaFree(d_temp_storage)); gpuCheckError(cudaFree(d_temp_storage_2)); gpuCheckError(cudaPeekAtLastError()); @@ -1032,4 +1032,4 @@ void trackSeedHandler(CellSeed* trackSeeds, gpuCheckError(cudaPeekAtLastError()); gpuCheckError(cudaDeviceSynchronize()); } -} // namespace o2::its \ No newline at end of file +} // namespace o2::its From 3486413113c892d7293ec5dcddac58db5301acc0 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Fri, 15 Nov 2024 10:17:55 +0100 Subject: [PATCH 0044/2180] DPL: Use AnalysisContext also in the case of amended topologies --- Framework/Core/src/ArrowSupport.cxx | 44 +++++++++++++++-------------- 1 file changed, 23 insertions(+), 21 deletions(-) diff --git a/Framework/Core/src/ArrowSupport.cxx b/Framework/Core/src/ArrowSupport.cxx index 230d708b47dc7..e6f8fb90c7af9 100644 --- a/Framework/Core/src/ArrowSupport.cxx +++ b/Framework/Core/src/ArrowSupport.cxx @@ -420,16 +420,18 @@ 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"; }); - std::vector requestedAODs; - std::vector requestedDYNs; - std::vector providedDYNs; + auto &ac = ctx.services().get(); + ac.requestedAODs.clear(); + ac.requestedDYNs.clear(); + ac.providedDYNs.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 - std::vector requestedIDXs; + ac.requestedIDXs.clear(); for (auto& d : workflow) { if (d.name == builder->name) { continue; @@ -437,7 +439,7 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() for (auto& i : d.inputs) { if (DataSpecUtils::partialMatch(i, header::DataOrigin{"IDX"})) { auto copy = i; - DataSpecUtils::updateInputList(requestedIDXs, std::move(copy)); + DataSpecUtils::updateInputList(ac.requestedIDXs, std::move(copy)); } } } @@ -446,8 +448,8 @@ 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(requestedIDXs); - AnalysisSupportHelpers::addMissingOutputsToBuilder(requestedIDXs, requestedAODs, requestedDYNs, *builder); + builder->algorithm = readers::AODReaderHelpers::indexBuilderCallback(ac.requestedIDXs); + AnalysisSupportHelpers::addMissingOutputsToBuilder(ac.requestedIDXs, ac.requestedAODs, ac.requestedDYNs, *builder); } if (spawner != workflow.end()) { @@ -459,20 +461,20 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() for (auto const& i : d.inputs) { if (DataSpecUtils::partialMatch(i, header::DataOrigin{"DYN"})) { auto copy = i; - DataSpecUtils::updateInputList(requestedDYNs, std::move(copy)); + DataSpecUtils::updateInputList(ac.requestedDYNs, std::move(copy)); } } for (auto const& o : d.outputs) { if (DataSpecUtils::partialMatch(o, header::DataOrigin{"DYN"})) { - providedDYNs.emplace_back(o); + ac.providedDYNs.emplace_back(o); } } } - std::sort(requestedDYNs.begin(), requestedDYNs.end(), inputSpecLessThan); - std::sort(providedDYNs.begin(), providedDYNs.end(), outputSpecLessThan); + std::sort(ac.requestedDYNs.begin(), ac.requestedDYNs.end(), inputSpecLessThan); + std::sort(ac.providedDYNs.begin(), ac.providedDYNs.end(), outputSpecLessThan); std::vector spawnerInputs; - for (auto& input : requestedDYNs) { - if (std::none_of(providedDYNs.begin(), providedDYNs.end(), [&input](auto const& x) { return DataSpecUtils::match(input, x); })) { + for (auto& input : ac.requestedDYNs) { + if (std::none_of(ac.providedDYNs.begin(), ac.providedDYNs.end(), [&input](auto const& x) { return DataSpecUtils::match(input, x); })) { spawnerInputs.emplace_back(input); } } @@ -482,7 +484,7 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() // replace AlgorithmSpec // FIXME: it should be made more generic, so it does not need replacement... spawner->algorithm = readers::AODReaderHelpers::aodSpawnerCallback(spawnerInputs); - AnalysisSupportHelpers::addMissingOutputsToSpawner({}, spawnerInputs, requestedAODs, *spawner); + AnalysisSupportHelpers::addMissingOutputsToSpawner({}, spawnerInputs, ac.requestedAODs, *spawner); } if (writer != workflow.end()) { @@ -496,14 +498,14 @@ o2::framework::ServiceSpec ArrowSupport::arrowBackendSpec() for (auto const& i : d.inputs) { if (DataSpecUtils::partialMatch(i, AODOrigins)) { auto copy = i; - DataSpecUtils::updateInputList(requestedAODs, std::move(copy)); + DataSpecUtils::updateInputList(ac.requestedAODs, std::move(copy)); } } } // 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(requestedAODs.begin(), 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(ac.requestedAODs.begin(), ac.requestedAODs.end(), [&](InputSpec const& i) { return DataSpecUtils::match(i, o); }); }); reader->outputs.erase(o_end, reader->outputs.end()); if (reader->outputs.empty()) { @@ -521,22 +523,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! - std::vector outputsInputsAOD; + ac.outputsInputsAOD.clear(); for (auto ii = 0u; ii < outputsInputs.size(); ii++) { if (DataSpecUtils::partialMatch(outputsInputs[ii], extendedAODOrigins)) { auto ds = dod->getDataOutputDescriptors(outputsInputs[ii]); if (!ds.empty() || isDangling[ii]) { - outputsInputsAOD.emplace_back(outputsInputs[ii]); + ac.outputsInputsAOD.emplace_back(outputsInputs[ii]); } } } // file sink for any AOD output - if (!outputsInputsAOD.empty()) { + if (!ac.outputsInputsAOD.empty()) { // add TFNumber and TFFilename as input to the writer - outputsInputsAOD.emplace_back("tfn", "TFN", "TFNumber"); - outputsInputsAOD.emplace_back("tff", "TFF", "TFFilename"); + ac.outputsInputsAOD.emplace_back("tfn", "TFN", "TFNumber"); + ac.outputsInputsAOD.emplace_back("tff", "TFF", "TFFilename"); workflow.push_back(AnalysisSupportHelpers::getGlobalAODSink(ctx)); } // Move the dummy sink at the end, if needed From 66df649cb46568a62fde730fcf3fede0a6e2f27c Mon Sep 17 00:00:00 2001 From: Sean Murray Date: Thu, 14 Nov 2024 16:16:25 +0100 Subject: [PATCH 0045/2180] TRD add less than operator to Tracklet64 to permit std::merge usage --- DataFormats/Detectors/TRD/src/Tracklet64.cxx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/DataFormats/Detectors/TRD/src/Tracklet64.cxx b/DataFormats/Detectors/TRD/src/Tracklet64.cxx index 9245165709979..d7b63cae45354 100644 --- a/DataFormats/Detectors/TRD/src/Tracklet64.cxx +++ b/DataFormats/Detectors/TRD/src/Tracklet64.cxx @@ -40,6 +40,16 @@ std::ostream& operator<<(std::ostream& stream, const Tracklet64& trg) trg.printStream(stream); return stream; } + +bool operator<(const Tracklet64& lhs, const Tracklet64& rhs) +{ + return (lhs.getDetector() < rhs.getDetector()) || + (lhs.getDetector() == rhs.getDetector() && lhs.getROB() < rhs.getROB()) || + (lhs.getDetector() == rhs.getDetector() && lhs.getROB() == rhs.getROB() && lhs.getMCM() < rhs.getMCM()) || + (lhs.getDetector() == rhs.getDetector() && lhs.getROB() == rhs.getROB() && lhs.getMCM() == rhs.getMCM() && lhs.getPadRow() < rhs.getPadRow()) || + (lhs.getDetector() == rhs.getDetector() && lhs.getROB() == rhs.getROB() && lhs.getMCM() == rhs.getMCM() && lhs.getPadRow() == rhs.getPadRow() && lhs.getPadCol() < rhs.getPadCol()); +} + #endif // GPUCA_GPUCODE_DEVICE } // namespace trd From 6b8ca303d7107dc76fcd6492dd3f8da612ae4a69 Mon Sep 17 00:00:00 2001 From: Sawan Date: Thu, 14 Nov 2024 10:06:03 +0530 Subject: [PATCH 0046/2180] added particle a2(1320) in the O2Database for the study of glueball --- .../simulation/include/SimulationDataFormat/O2DatabasePDG.h | 4 ++++ Steer/src/O2MCApplication.cxx | 3 +++ 2 files changed, 7 insertions(+) diff --git a/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h b/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h index 3487f7a0e5aef..229a1a7a8a535 100644 --- a/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h +++ b/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h @@ -500,6 +500,10 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db) } // glueball hunting + ionCode = 115; + if (!db->GetParticle(ionCode)) { + db->AddParticle("a2_1320", "a2_1320", 1.3182, kFALSE, 0.1078, 0, "Resonance", ionCode); + } ionCode = 10221; if (!db->GetParticle(ionCode)) { db->AddParticle("f0_1370", "f0_1370", 1.37, kFALSE, 0.200, 0, "Resonance", ionCode); diff --git a/Steer/src/O2MCApplication.cxx b/Steer/src/O2MCApplication.cxx index 240e56aba28c4..96cc2f2e969db 100644 --- a/Steer/src/O2MCApplication.cxx +++ b/Steer/src/O2MCApplication.cxx @@ -388,6 +388,8 @@ void addSpecialParticles() // Their life times are not known, so we set them to 1e-24 // f0(1370) (PDG: width = 200-500 MeV) Spin/Parity might not be correct TVirtualMC::GetMC()->DefineParticle(10221, "f0_1370", kPTNeutron, 1.37, 0.0, 1e-24, "Hadron", 0.2, 1, 1, 1, 0, 0, 1, 0, 0, kTRUE); + // a2(1320) (PDG: width = 107.8 MeV) (Spin/Parity might not be correct) + TVirtualMC::GetMC()->DefineParticle(115, "a2_1320", kPTNeutron, 1.3182, 0.0, 1e-24, "Hadron", 0.1078, 1, 1, 1, 1, 0, 1, 0, 0, kTRUE); // f0(1500) (PDG: width = 112 MeV) Spin/Parity might not be correct TVirtualMC::GetMC()->DefineParticle(9030221, "f0_1500", kPTNeutron, 1.506, 0.0, 1e-24, "Hadron", 0.112, 0, 1, 1, 0, 0, 1, 0, 0, kTRUE); // f0(1710) (PDG: width = 139 MeV) Spin/Parity might not be correct @@ -1242,6 +1244,7 @@ void addSpecialParticles() TVirtualMC::GetMC()->SetDecayMode(335, bratio, mode); // f2(1525) TVirtualMC::GetMC()->SetDecayMode(10331, bratio, mode); // f0(1710) TVirtualMC::GetMC()->SetDecayMode(10221, bratio, mode); // f0(1370) + TVirtualMC::GetMC()->SetDecayMode(115, bratio, mode); // a2(1320) // Define the 3-body phase space decay for the resonances: f1(1285), f1(1420) for (Int_t kz = 0; kz < 6; kz++) { From 9fa6915dea9e5f948743b41d96b630a25e1db577 Mon Sep 17 00:00:00 2001 From: swenzel Date: Fri, 15 Nov 2024 13:16:02 +0100 Subject: [PATCH 0047/2180] Orbit-early treatment in CollisionContext tool Developments to allow treatment/inclusion of additional orbits before each timeframe start: - A new option `--earlyOrbits x` will prepend x orbits with collisions before the firstOrbit asked - It will also do the same in each individual timeframe collision context extracted from the global context - Collisions falling within the 'earlyOrbit' range are always kept and not filtered out based on a maximal count filter Some cleanup. Some restructuring/simplification of DigitizationContext: - less internal state - timeframe boundary indices are generalized from (start, end) --> (start, end, previous) where previous is the index from which on this timeframe can still be influenced with an earlyOrbit criterion --- .../DigitizationContext.h | 20 +-- .../simulation/src/DigitizationContext.cxx | 151 +++++++++++++++--- Steer/src/CollisionContextTool.cxx | 46 ++++-- 3 files changed, 174 insertions(+), 43 deletions(-) diff --git a/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h b/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h index 4149b32683060..4bd5dfa2ab76c 100644 --- a/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h +++ b/DataFormats/simulation/include/SimulationDataFormat/DigitizationContext.h @@ -115,8 +115,8 @@ class DigitizationContext /// retrieves collision context for a single timeframe-id (which may be needed by simulation) /// (Only copies collision context without QED information. This can be added to the result with the fillQED method - /// in a second step. As a pre-condition, one should have called finalizeTimeframeStructure) - DigitizationContext extractSingleTimeframe(int timeframeid, std::vector const& sources_to_offset); + /// in a second step. Takes as input a timeframe indices collection) + DigitizationContext extractSingleTimeframe(int timeframeid, std::vector> const& timeframeindices, std::vector const& sources_to_offset); /// function reading the hits from a chain (previously initialized with initSimChains /// The hits pointer will be initialized (what to we do about ownership??) @@ -130,12 +130,12 @@ class DigitizationContext /// returns the GRP object associated to this context o2::parameters::GRPObject const& getGRP() const; - // apply collision number cuts and potential relabeling of eventID - void applyMaxCollisionFilter(long startOrbit, long orbitsPerTF, int maxColl); + // apply collision number cuts and potential relabeling of eventID, (keeps collisions which fall into the orbitsEarly range for the next timeframe) + // needs a timeframe index structure (determined by calcTimeframeIndices), which is adjusted during the process to reflect the filtering + void applyMaxCollisionFilter(std::vector>& timeframeindices, long startOrbit, long orbitsPerTF, int maxColl, double orbitsEarly = 0.); - /// finalize timeframe structure (fixes the indices in mTimeFrameStartIndex) - // returns the number of timeframes - int finalizeTimeframeStructure(long startOrbit, long orbitsPerTF); + /// get timeframe structure --> index markers where timeframe starts/ends/is_influenced_by + std::vector> calcTimeframeIndices(long startOrbit, long orbitsPerTF, double orbitsEarly = 0.) const; // Sample and fix interaction vertices (according to some distribution). Makes sure that same event ids // have to have same vertex, as well as event ids associated to same collision. @@ -176,17 +176,13 @@ class DigitizationContext // for each collision we record the constituents (which shall not exceed mMaxPartNumber) std::vector> mEventParts; - // for each collision we may record/fix the interaction vertex (to be used in event generation) + // for each collisionstd::vector> &timeframeindice we may record/fix the interaction vertex (to be used in event generation) std::vector> mInteractionVertices; // the collision records **with** QED interleaved; std::vector mEventRecordsWithQED; std::vector> mEventPartsWithQED; - // timeframe structure - std::vector> mTimeFrameStartIndex; // for each timeframe, the pair of start-index and end-index into mEventParts, mEventRecords - std::vector> mTimeFrameStartIndexQED; // for each timeframe, the pair of start-index and end-index into mEventParts, mEventRecords (QED version) - o2::BunchFilling mBCFilling; // pattern of active BCs std::vector mSimPrefixes; // identifiers to the hit sim products; the key corresponds to the source ID of event record diff --git a/DataFormats/simulation/src/DigitizationContext.cxx b/DataFormats/simulation/src/DigitizationContext.cxx index 3fb6b757aeea3..bbb9b384f65fa 100644 --- a/DataFormats/simulation/src/DigitizationContext.cxx +++ b/DataFormats/simulation/src/DigitizationContext.cxx @@ -380,9 +380,67 @@ std::vector> getTimeFrameBoundaries(std::vector(left, right - 1)); return result; } + +// a common helper for timeframe structure - includes indices for orbits-early (orbits from last timeframe still affecting current one) +std::vector> getTimeFrameBoundaries(std::vector const& irecords, + long startOrbit, + long orbitsPerTF, + float orbitsEarly) +{ + // we could actually use the other method first ... then do another pass to fix the early-index ... or impact index + auto true_indices = getTimeFrameBoundaries(irecords, startOrbit, orbitsPerTF); + + std::vector> indices_with_early{}; + for (int ti = 0; ti < true_indices.size(); ++ti) { + // for each timeframe we copy the true indices + auto& tf_range = true_indices[ti]; + + // init new index without fixing the early index yet + indices_with_early.push_back(std::make_tuple(tf_range.first, tf_range.second, -1)); + + // from the second timeframe on we can determine the index in the previous timeframe + // which matches our criterion + if (orbitsEarly > 0. && ti > 0) { + auto& prev_tf_range = true_indices[ti - 1]; + // in this range search the smallest index which precedes + // timeframe ti by not more than "orbitsEarly" orbits + // (could probably use binary search, in case optimization becomes necessary) + int earlyOrbitIndex = prev_tf_range.second; + + // this is the orbit of the ti-th timeframe start + auto orbit_timeframe_start = startOrbit + ti * orbitsPerTF; + + auto orbit_timeframe_early_fractional = orbit_timeframe_start - orbitsEarly; + auto orbit_timeframe_early_integral = (uint32_t)(orbit_timeframe_early_fractional); + + auto bc_early = (uint32_t)((orbit_timeframe_early_fractional - orbit_timeframe_early_integral) * o2::constants::lhc::LHCMaxBunches); + + // this is the interaction record of the ti-th timeframe start + o2::InteractionRecord timeframe_start_record(0, orbit_timeframe_early_integral); + // this is the interaction record in some previous timeframe after which interactions could still + // influence the ti-th timeframe according to orbitsEarly + o2::InteractionRecord timeframe_early_record(bc_early, orbit_timeframe_early_integral); + + auto differenceInBCNS_max = timeframe_start_record.differenceInBCNS(timeframe_early_record); + + for (int j = prev_tf_range.second; j >= prev_tf_range.first; --j) { + // determine difference in timing in NS; compare that with the limit given by orbitsEarly + auto timediff_NS = timeframe_start_record.differenceInBCNS(irecords[j]); + if (timediff_NS < differenceInBCNS_max) { + earlyOrbitIndex = j; + } else { + break; + } + } + std::get<2>(indices_with_early.back()) = earlyOrbitIndex; + } + } + return indices_with_early; +} + } // namespace -void DigitizationContext::applyMaxCollisionFilter(long startOrbit, long orbitsPerTF, int maxColl) +void DigitizationContext::applyMaxCollisionFilter(std::vector>& timeframeindices, long startOrbit, long orbitsPerTF, int maxColl, double orbitsEarly) { // the idea is to go through each timeframe and throw away collisions beyond a certain count // then the indices should be condensed @@ -390,9 +448,6 @@ void DigitizationContext::applyMaxCollisionFilter(long startOrbit, long orbitsPe std::vector> newparts; std::vector newrecords; - // get a timeframe boundary indexing - auto timeframeindices = getTimeFrameBoundaries(mEventRecords, startOrbit, orbitsPerTF); - std::unordered_map currMaxId; // the max id encountered for a source std::unordered_map> reIndexMap; // for each source, a map of old to new index for the event parts @@ -400,12 +455,51 @@ void DigitizationContext::applyMaxCollisionFilter(long startOrbit, long orbitsPe maxColl = mEventRecords.size(); } + // the actual first actual timeframe + int first_timeframe = orbitsEarly > 0. ? 1 : 0; + + // mapping of old to new indices + std::unordered_map indices_old_to_new; + // now we can go through the structure timeframe by timeframe - for (auto timeframe : timeframeindices) { - auto firstindex = timeframe.first; - auto lastindex = timeframe.second; + for (int tf_id = first_timeframe; tf_id < timeframeindices.size(); ++tf_id) { + auto& tf_indices = timeframeindices[tf_id]; + + auto firstindex = std::get<0>(tf_indices); // .first; + auto lastindex = std::get<1>(tf_indices); // .second; + auto previndex = std::get<2>(tf_indices); + + LOG(info) << "timeframe indices " << previndex << " : " << firstindex << " : " << lastindex; + + int collCount = 0; // counting collisions within timeframe // copy to new structure - for (int index = firstindex; index <= std::min(lastindex, firstindex + maxColl - 1); ++index) { + for (int index = previndex >= 0 ? previndex : firstindex; index <= lastindex; ++index) { + if (collCount >= maxColl) { + continue; + } + + // look if this index was already done? + // avoid duplicate entries in transformed records + if (indices_old_to_new.find(index) != indices_old_to_new.end()) { + continue; + } + + // we put these events under a certain condition + bool keep = index < firstindex || collCount < maxColl; + + if (!keep) { + continue; + } + + if (index >= firstindex) { + collCount++; + } + + // we must also make sure that we don't duplicate the records + // moreover some records are merely put as precoll of tf2 ---> so they shouldn't be part of tf1 in the final + // extraction, ouch ! + // maybe we should combine the filter and individual tf extraction in one step !! + indices_old_to_new[index] = newrecords.size(); newrecords.push_back(mEventRecords[index]); newparts.push_back(mEventParts[index]); @@ -427,6 +521,19 @@ void DigitizationContext::applyMaxCollisionFilter(long startOrbit, long orbitsPe currMaxId[source] += 1; } } + } // ends one timeframe + + // correct the timeframe indices + if (indices_old_to_new.find(firstindex) != indices_old_to_new.end()) { + std::get<0>(tf_indices) = indices_old_to_new[firstindex]; // start + } + if (indices_old_to_new.find(lastindex) != indices_old_to_new.end()) { + std::get<1>(tf_indices) = indices_old_to_new[lastindex]; // end; + } else { + std::get<1>(tf_indices) = newrecords.size(); // end; + } + if (indices_old_to_new.find(previndex) != indices_old_to_new.end()) { + std::get<2>(tf_indices) = indices_old_to_new[previndex]; // previous or "early" index } } // reassignment @@ -434,15 +541,15 @@ void DigitizationContext::applyMaxCollisionFilter(long startOrbit, long orbitsPe mEventParts = newparts; } -int DigitizationContext::finalizeTimeframeStructure(long startOrbit, long orbitsPerTF) +std::vector> DigitizationContext::calcTimeframeIndices(long startOrbit, long orbitsPerTF, double orbitsEarly) const { - mTimeFrameStartIndex = getTimeFrameBoundaries(mEventRecords, startOrbit, orbitsPerTF); - LOG(info) << "Fixed " << mTimeFrameStartIndex.size() << " timeframes "; - for (auto p : mTimeFrameStartIndex) { - LOG(info) << p.first << " " << p.second; + auto timeframeindices = getTimeFrameBoundaries(mEventRecords, startOrbit, orbitsPerTF, orbitsEarly); + LOG(info) << "Fixed " << timeframeindices.size() << " timeframes "; + for (auto p : timeframeindices) { + LOG(info) << std::get<0>(p) << " " << std::get<1>(p) << " " << std::get<2>(p); } - return mTimeFrameStartIndex.size(); + return timeframeindices; } std::unordered_map DigitizationContext::getCollisionIndicesForSource(int source) const @@ -529,21 +636,25 @@ void DigitizationContext::sampleInteractionVertices(o2::dataformats::MeanVertexO } } -DigitizationContext DigitizationContext::extractSingleTimeframe(int timeframeid, std::vector const& sources_to_offset) +DigitizationContext DigitizationContext::extractSingleTimeframe(int timeframeid, std::vector> const& timeframeindices, std::vector const& sources_to_offset) { DigitizationContext r; // make a return object - if (mTimeFrameStartIndex.size() == 0) { - LOG(error) << "No timeframe structure determined; Returning empty object. Please call ::finalizeTimeframeStructure before calling this function"; + if (timeframeindices.size() == 0) { + LOG(error) << "Timeframe index structure empty; Returning empty object."; return r; } r.mSimPrefixes = mSimPrefixes; r.mMuBC = mMuBC; try { - auto startend = mTimeFrameStartIndex.at(timeframeid); + auto tf_ranges = timeframeindices.at(timeframeid); - auto startindex = startend.first; - auto endindex = startend.second; + auto startindex = std::get<0>(tf_ranges); + auto endindex = std::get<1>(tf_ranges); + auto earlyindex = std::get<2>(tf_ranges); + if (earlyindex >= 0) { + startindex = earlyindex; + } std::copy(mEventRecords.begin() + startindex, mEventRecords.begin() + endindex, std::back_inserter(r.mEventRecords)); std::copy(mEventParts.begin() + startindex, mEventParts.begin() + endindex, std::back_inserter(r.mEventParts)); if (mInteractionVertices.size() > endindex) { diff --git a/Steer/src/CollisionContextTool.cxx b/Steer/src/CollisionContextTool.cxx index 3d1dcec29976e..9cb4d401f3851 100644 --- a/Steer/src/CollisionContextTool.cxx +++ b/Steer/src/CollisionContextTool.cxx @@ -40,12 +40,12 @@ struct Options { std::vector interactionRates; std::string qedInteraction; // specification for QED contribution std::string outfilename; // - double timeframelengthinMS; // timeframe length in milliseconds int orbits; // number of orbits to generate (can be a multiple of orbitsPerTF --> determine fraction or multiple of timeframes) long seed; // bool printContext = false; std::string bcpatternfile; int tfid = 0; // tfid -> used to calculate start orbit for collisions + double orbitsEarly = 0.; // how many orbits from a prev timeframe should still be kept in the current timeframe double firstFractionalOrbit; // capture orbit and bunch crossing via decimal number uint32_t firstOrbit = 0; // first orbit in run (orbit offset) uint32_t firstBC = 0; // first bunch crossing (relative to firstOrbit) of the first interaction; @@ -191,7 +191,7 @@ bool parseOptions(int argc, char* argv[], Options& optvalues) options.add_options()( "interactions,i", bpo::value>(&optvalues.interactionRates)->multitoken(), "name,IRate|LockSpecifier")( - "QEDinteraction", bpo::value(&optvalues.qedInteraction)->default_value(""), "Interaction specifyer for QED contribution (name,IRATE,maxeventnumber)")( + "QEDinteraction", bpo::value(&optvalues.qedInteraction)->default_value(""), "Interaction specifier for QED contribution (name,IRATE,maxeventnumber)")( "outfile,o", bpo::value(&optvalues.outfilename)->default_value("collisioncontext.root"), "Outfile of collision context")( "orbits", bpo::value(&optvalues.orbits)->default_value(-1), "Number of orbits to generate maximally (if given, can be used to determine the number of timeframes). " @@ -200,6 +200,7 @@ bool parseOptions(int argc, char* argv[], Options& optvalues) "show-context", "Print generated collision context to terminal.")( "bcPatternFile", bpo::value(&optvalues.bcpatternfile)->default_value(""), "Interacting BC pattern file (e.g. from CreateBCPattern.C); Use \"ccdb\" when fetching from CCDB.")( "orbitsPerTF", bpo::value(&optvalues.orbitsPerTF)->default_value(256), "Orbits per timeframes")( + "orbitsEarly", bpo::value(&optvalues.orbitsEarly)->default_value(0.), "Number of orbits with extra collisions prefixed to each timeframe")( "use-existing-kine", "Read existing kinematics to adjust event counts")( "timeframeID", bpo::value(&optvalues.tfid)->default_value(0), "Timeframe id of the first timeframe int this context. Allows to generate contexts for different start orbits")( "first-orbit", bpo::value(&optvalues.firstFractionalOrbit)->default_value(0), "First (fractional) orbit in the run (HBFUtils.firstOrbit + BC from decimal)")( @@ -273,7 +274,6 @@ int main(int argc, char* argv[]) // now we generate the collision structure (interaction type by interaction type) bool usetimeframelength = options.orbits > 0; - o2::InteractionTimeRecord limitInteraction(0, options.orbits); auto setBCFillingHelper = [&options](auto& sampler, auto& bcPatternString) { if (bcPatternString == "ccdb") { @@ -290,7 +290,14 @@ int main(int argc, char* argv[]) } }; + // this is the starting orbit from which on we construct interactions (it is possibly shifted by one tf to the left + // in order to generate eventual "earlyOrbits" auto orbitstart = options.firstOrbit + options.tfid * options.orbitsPerTF; + auto orbits_total = options.orbits; + if (options.orbitsEarly > 0.) { + orbitstart -= options.orbitsPerTF; + orbits_total += options.orbitsPerTF; + } for (int id = 0; id < ispecs.size(); ++id) { auto mode = ispecs[id].syncmode; @@ -306,10 +313,10 @@ int main(int argc, char* argv[]) sampler.setFirstIR(o2::InteractionRecord(options.firstBC, orbitstart)); sampler.init(); record = sampler.generateCollisionTime(); - } while (options.noEmptyTF && usetimeframelength && record.orbit >= orbitstart + options.orbits); + } while (options.noEmptyTF && usetimeframelength && record.orbit >= orbitstart + orbits_total); int count = 0; do { - if (usetimeframelength && record.orbit >= orbitstart + options.orbits) { + if (usetimeframelength && record.orbit >= orbitstart + orbits_total) { break; } std::vector parts; @@ -320,7 +327,7 @@ int main(int argc, char* argv[]) collisions.insert(iter, insertvalue); record = sampler.generateCollisionTime(); count++; - } while ((ispecs[id].mcnumberasked > 0 && count < ispecs[id].mcnumberasked)); + } while ((ispecs[id].mcnumberasked > 0 && count < ispecs[id].mcnumberasked)); // TODO: this loop should probably be replaced by a condition with usetimeframelength and number of orbits // we support randomization etc on non-injected/embedded interactions // and we can apply them here @@ -446,10 +453,25 @@ int main(int argc, char* argv[]) } digicontext.setSimPrefixes(prefixes); + // <---- at this moment we have a dense collision context (not representing the final output we want) + LOG(info) << "<<------ DENSE CONTEXT ---------"; + if (options.printContext) { + digicontext.printCollisionSummary(options.qedInteraction.size() > 0); + } + LOG(info) << "-------- DENSE CONTEXT ------->>"; + + auto timeframeindices = digicontext.calcTimeframeIndices(orbitstart, options.orbitsPerTF, options.orbitsEarly); // apply max collision per timeframe filters + reindexing of event id (linearisation and compactification) - digicontext.applyMaxCollisionFilter(orbitstart, options.orbitsPerTF, options.maxCollsPerTF); + digicontext.applyMaxCollisionFilter(timeframeindices, orbitstart, options.orbitsPerTF, options.maxCollsPerTF, options.orbitsEarly); + + // <---- at this moment we have a dense collision context (not representing the final output we want) + LOG(info) << "<<------ FILTERED CONTEXT ---------"; + if (options.printContext) { + digicontext.printCollisionSummary(options.qedInteraction.size() > 0); + } + LOG(info) << "-------- FILTERED CONTEXT ------->>"; - auto numTimeFrames = digicontext.finalizeTimeframeStructure(orbitstart, options.orbitsPerTF); + auto numTimeFrames = timeframeindices.size(); // digicontext.finalizeTimeframeStructure(orbitstart, options.orbitsPerTF, options.orbitsEarly); if (options.vertexMode != o2::conf::VertexMode::kNoVertex) { switch (options.vertexMode) { @@ -539,9 +561,11 @@ int main(int argc, char* argv[]) sources_to_offset.push_back(digicontext.findSimPrefix(tokens[i])); } + auto first_timeframe = options.orbitsEarly > 0. ? 1 : 0; // now we are ready to loop over all timeframes - for (int tf_id = 0; tf_id < numTimeFrames; ++tf_id) { - auto copy = digicontext.extractSingleTimeframe(tf_id, sources_to_offset); + int tf_output_counter = 1; + for (int tf_id = first_timeframe; tf_id < numTimeFrames; ++tf_id) { + auto copy = digicontext.extractSingleTimeframe(tf_id, timeframeindices, sources_to_offset); // each individual case gets QED interactions injected // This should probably be done inside the extraction itself @@ -551,7 +575,7 @@ int main(int argc, char* argv[]) } std::stringstream str; - str << path_prefix << (tf_id + 1) << "/collisioncontext.root"; + str << path_prefix << tf_output_counter++ << "/collisioncontext.root"; copy.saveToFile(str.str()); LOG(info) << "----"; copy.printCollisionSummary(options.qedInteraction.size() > 0); From 19fd6971870ff54dda967314c18248cbb25d58d1 Mon Sep 17 00:00:00 2001 From: Matthias Kleiner Date: Thu, 14 Nov 2024 11:36:20 +0100 Subject: [PATCH 0048/2180] TPC: add check for empty data when receiving IDCs --- .../TPCFourierTransformAggregatorSpec.h | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/Detectors/TPC/workflow/include/TPCWorkflow/TPCFourierTransformAggregatorSpec.h b/Detectors/TPC/workflow/include/TPCWorkflow/TPCFourierTransformAggregatorSpec.h index adc43ee6d0258..956e9c899cebc 100644 --- a/Detectors/TPC/workflow/include/TPCWorkflow/TPCFourierTransformAggregatorSpec.h +++ b/Detectors/TPC/workflow/include/TPCWorkflow/TPCFourierTransformAggregatorSpec.h @@ -74,7 +74,12 @@ class TPCFourierTransformAggregatorSpec : public o2::framework::Task return; } - mCCDBBuffer[lane] = pc.inputs().get>("tsccdb"); + const auto tsTmp = pc.inputs().get>("tsccdb"); + if (tsTmp.front() == 0) { + LOGP(warning, "Received dummy data with empty timestamp"); + return; + } + mCCDBBuffer[lane] = tsTmp; if (mProcessedTimeStamp > mCCDBBuffer[lane].front()) { LOGP(warning, "Already received data from a later time stamp {} then the currently received time stamp {}! (This might not be an issue)", mProcessedTimeStamp, mCCDBBuffer[lane].front()); } else { @@ -289,6 +294,7 @@ class TPCFourierTransformAggregatorSpec : public o2::framework::Task if (eos) { // in case of eos write out everything lastValidIdx = times.empty() ? -1 : times.size() - 1; + LOGP(info, "End of stream detected: Creating IDC scalers with {} IDC objects", lastValidIdx); } // create IDC scaler in case index is valid @@ -342,7 +348,13 @@ class TPCFourierTransformAggregatorSpec : public o2::framework::Task const float deltaTime = times[i + 1].first - time.second; // if delta time is too large add dummy values if (deltaTime > (timesDuration / checkGapp)) { - const int nDummyValues = deltaTime / idcIntegrationTime + 0.5; + int nDummyValues = deltaTime / idcIntegrationTime + 0.5; + // restrict dummy values + const int nMaxDummyValues = checkGapp * timesDuration / idcIntegrationTime; + if (nDummyValues > nMaxDummyValues) { + nDummyValues = nMaxDummyValues; + } + // add dummy to A if (idc.idc1[0].size() > 0) { float meanA = std::reduce(idc.idc1[0].begin(), idc.idc1[0].end()) / static_cast(idc.idc1[0].size()); From 13bdce12f4f9ed2fd21f14714d052ef9c538b188 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Fri, 15 Nov 2024 15:40:29 +0100 Subject: [PATCH 0049/2180] HBFUtils: Optional throw on failed parsing of opt MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This also fixed the use of ’--hbfutils-config none’. --- Detectors/Raw/include/DetectorsRaw/HBFUtilsInitializer.h | 2 +- Detectors/Raw/src/HBFUtilsInitializer.cxx | 8 +++----- 2 files changed, 4 insertions(+), 6 deletions(-) diff --git a/Detectors/Raw/include/DetectorsRaw/HBFUtilsInitializer.h b/Detectors/Raw/include/DetectorsRaw/HBFUtilsInitializer.h index 895eff097e7a0..3d44f9f0bb4bb 100644 --- a/Detectors/Raw/include/DetectorsRaw/HBFUtilsInitializer.h +++ b/Detectors/Raw/include/DetectorsRaw/HBFUtilsInitializer.h @@ -64,7 +64,7 @@ struct HBFUtilsInitializer { static o2::dataformats::IRFrame IRFrameSel; // IRFrame selected for the current TF HBFUtilsInitializer(const o2::framework::ConfigContext& configcontext, o2::framework::WorkflowSpec& wf); - static HBFOpt getOptType(const std::string& optString); + static HBFOpt getOptType(const std::string& optString, bool throwOnFailure = true); static std::vector readTFIDInfoVector(const std::string& fname); static void readIRFramesVector(const std::string& fname); static void assignDataHeaderFromTFIDInfo(const std::vector& tfinfoVec, o2::header::DataHeader& dh, o2::framework::DataProcessingHeader& dph); diff --git a/Detectors/Raw/src/HBFUtilsInitializer.cxx b/Detectors/Raw/src/HBFUtilsInitializer.cxx index e3cc9a8eef414..1b0dbdbf3fe30 100644 --- a/Detectors/Raw/src/HBFUtilsInitializer.cxx +++ b/Detectors/Raw/src/HBFUtilsInitializer.cxx @@ -65,7 +65,7 @@ HBFUtilsInitializer::HBFUtilsInitializer(const o2f::ConfigContext& configcontext upstream = true; continue; } - HBFOpt opt = getOptType(optStr); + HBFOpt opt = getOptType(optStr, !helpasked); // do not throw on unknown opt if help-opt was given nopts++; if ((opt == HBFOpt::INI || opt == HBFOpt::JSON) && !helpasked) { o2::conf::ConfigurableParam::updateFromFile(optStr, "HBFUtils", true); // update only those values which were not touched yet (provenance == kCODE) @@ -78,8 +78,6 @@ HBFUtilsInitializer::HBFUtilsInitializer(const o2f::ConfigContext& configcontext hbfuInput = optStr; } else if (opt == HBFOpt::ROOT) { rootFileInput = optStr; - } else if (!helpasked) { - LOGP(fatal, "uknown hbfutils-config option {}", optStr); } } if (!nopts && !helpasked) { @@ -125,7 +123,7 @@ HBFUtilsInitializer::HBFUtilsInitializer(const o2f::ConfigContext& configcontext } //_________________________________________________________ -HBFUtilsInitializer::HBFOpt HBFUtilsInitializer::getOptType(const std::string& optString) +HBFUtilsInitializer::HBFOpt HBFUtilsInitializer::getOptType(const std::string& optString, bool throwOnFailure) { // return type of the file provided via HBFConfOpt HBFOpt opt = HBFOpt::NONE; @@ -138,7 +136,7 @@ HBFUtilsInitializer::HBFOpt HBFUtilsInitializer::getOptType(const std::string& o opt = HBFOpt::ROOT; } else if (optString == HBFUSrc) { opt = HBFOpt::HBFUTILS; - } else if (optString != "none") { + } else if (optString != "none" && throwOnFailure) { throw std::runtime_error(fmt::format("invalid option {} for {}", optString, HBFConfOpt)); } } From adcd323449830b18aa797c136c1e46c43281b70b Mon Sep 17 00:00:00 2001 From: shahoian Date: Fri, 15 Nov 2024 18:37:36 +0100 Subject: [PATCH 0050/2180] Limit TFs extraxted from CTF file with --max-tf-per-file CTF-reader N --- Detectors/CTF/README.md | 5 +++++ Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h | 1 + Detectors/CTF/workflow/src/CTFReaderSpec.cxx | 2 +- Detectors/CTF/workflow/src/ctf-reader-workflow.cxx | 4 ++++ 4 files changed, 11 insertions(+), 1 deletion(-) diff --git a/Detectors/CTF/README.md b/Detectors/CTF/README.md index 48fa78c896a86..e1e65060db523 100644 --- a/Detectors/CTF/README.md +++ b/Detectors/CTF/README.md @@ -100,6 +100,11 @@ comma-separated list of detectors to skip ``` max CTFs to process (<= 0 : infinite) +``` +--max-tf-per-file arg (=-1) +``` +max TFs to process from every CTF file (<= 0 : infinite) + ``` --loop arg (=0) ``` diff --git a/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h b/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h index 501541f25cf27..997572e0371b2 100644 --- a/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h +++ b/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h @@ -40,6 +40,7 @@ struct CTFReaderInp { int64_t delay_us = 0; int maxLoops = 0; int maxTFs = -1; + int maxTFsPerFile = -1; unsigned int subspec = 0; unsigned int decSSpecEMC = 0; int tfRateLimit = -999; diff --git a/Detectors/CTF/workflow/src/CTFReaderSpec.cxx b/Detectors/CTF/workflow/src/CTFReaderSpec.cxx index 681547f9a814c..9b16e65c3a2b7 100644 --- a/Detectors/CTF/workflow/src/CTFReaderSpec.cxx +++ b/Detectors/CTF/workflow/src/CTFReaderSpec.cxx @@ -360,7 +360,7 @@ bool CTFReaderSpec::processTF(ProcessingContext& pc) void CTFReaderSpec::checkTreeEntries() { // check if the tree has entries left, if needed, close current tree/file - if (++mCurrTreeEntry >= mCTFTree->GetEntries()) { // this file is done, check if there are other files + if (++mCurrTreeEntry >= mCTFTree->GetEntries() || (mInput.maxTFsPerFile > 0 && mCurrTreeEntry >= mInput.maxTFsPerFile)) { // this file is done, check if there are other files mCTFTree.reset(); mCTFFile->Close(); mCTFFile.reset(); diff --git a/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx b/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx index ef3a0f8d3c2c4..a12c9c10f9dd8 100644 --- a/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx +++ b/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx @@ -55,6 +55,7 @@ void customize(std::vector& workflowOptions) options.push_back(ConfigParamSpec{"onlyDet", VariantType::String, std::string{DetID::ALL}, {"comma-separated list of detectors to accept. Overrides skipDet"}}); options.push_back(ConfigParamSpec{"skipDet", VariantType::String, std::string{DetID::NONE}, {"comma-separate list of detectors to skip"}}); options.push_back(ConfigParamSpec{"max-tf", VariantType::Int, -1, {"max CTFs to process (<= 0 : infinite)"}}); + options.push_back(ConfigParamSpec{"max-tf-per-file", VariantType::Int, -1, {"max TFs to process per ctf file (<= 0 : infinite)"}}); options.push_back(ConfigParamSpec{"loop", VariantType::Int, 0, {"loop N times (infinite for N<0)"}}); options.push_back(ConfigParamSpec{"delay", VariantType::Float, 0.f, {"delay in seconds between consecutive TFs sending"}}); options.push_back(ConfigParamSpec{"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 @@ -119,6 +120,9 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) int n = configcontext.options().get("max-tf"); ctfInput.maxTFs = n > 0 ? n : 0x7fffffff; + n = configcontext.options().get("max-tf-per-file"); + ctfInput.maxTFsPerFile = n > 0 ? n : 0x7fffffff; + ctfInput.maxFileCache = std::max(1, configcontext.options().get("max-cached-files")); ctfInput.copyCmd = configcontext.options().get("copy-cmd"); From fd4a6d226b440c61e9cd7a9419e268c6b7854c14 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Thu, 14 Nov 2024 18:37:33 +0100 Subject: [PATCH 0051/2180] GPU: Fix debug message --- .../Base/opencl-common/GPUReconstructionOCL.cxx | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.cxx b/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.cxx index 0e1c94eced7e3..de32f03340c03 100644 --- a/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.cxx +++ b/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.cxx @@ -115,16 +115,17 @@ int32_t GPUReconstructionOCL::InitDevice_Runtime() clGetPlatformInfo(mInternals->platforms[i_platform], CL_PLATFORM_VERSION, sizeof(platform_version), platform_version, nullptr); clGetPlatformInfo(mInternals->platforms[i_platform], CL_PLATFORM_NAME, sizeof(platform_name), platform_name, nullptr); clGetPlatformInfo(mInternals->platforms[i_platform], CL_PLATFORM_VENDOR, sizeof(platform_vendor), platform_vendor, nullptr); - if (mProcessingSettings.debugLevel >= 2) { - GPUInfo("Available Platform %d: (%s %s) %s %s", i_platform, platform_profile, platform_version, platform_vendor, platform_name); - } + const char* platformUsageInfo = ""; if (!found && CheckPlatform(i_platform)) { found = true; mInternals->platform = mInternals->platforms[i_platform]; if (mProcessingSettings.debugLevel >= 2) { - GPUInfo(" Using this platform"); + platformUsageInfo = " !!! Using this platform !!!"; } } + if (mProcessingSettings.debugLevel >= 2) { + GPUInfo("Available Platform %d: (%s %s) %s %s%s", i_platform, platform_profile, platform_version, platform_vendor, platform_name, platformUsageInfo); + } } } From cb97e7a4792e70df9e2b32ad9bbaeb013bccb880 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Fri, 15 Nov 2024 23:46:53 +0100 Subject: [PATCH 0052/2180] GPU: Clarify file name --- .../Base/GPUReconstructionIncludesDeviceAll.template.h | 4 ---- ...nels.template.h => GPUReconstructionKernelList.template.h} | 0 GPU/GPUTracking/CMakeLists.txt | 2 +- 3 files changed, 1 insertion(+), 5 deletions(-) rename GPU/GPUTracking/Base/{GPUReconstructionKernels.template.h => GPUReconstructionKernelList.template.h} (100%) diff --git a/GPU/GPUTracking/Base/GPUReconstructionIncludesDeviceAll.template.h b/GPU/GPUTracking/Base/GPUReconstructionIncludesDeviceAll.template.h index 8a23a6792dcd1..4822332a1839c 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionIncludesDeviceAll.template.h +++ b/GPU/GPUTracking/Base/GPUReconstructionIncludesDeviceAll.template.h @@ -25,10 +25,6 @@ namespace gpu } // namespace GPUCA_NAMESPACE using namespace GPUCA_NAMESPACE::gpu; -#if !defined(GPUCA_OPENCL1) && (!defined(GPUCA_ALIROOT_LIB) || !defined(GPUCA_GPUCODE)) -#define GPUCA_KRNL_NOOCL1 -#endif - // clang-format off $>,APPEND,">,PREPEND,#include ">, > diff --git a/GPU/GPUTracking/Base/GPUReconstructionKernels.template.h b/GPU/GPUTracking/Base/GPUReconstructionKernelList.template.h similarity index 100% rename from GPU/GPUTracking/Base/GPUReconstructionKernels.template.h rename to GPU/GPUTracking/Base/GPUReconstructionKernelList.template.h diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index f97e966287d41..937346fe478c3 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -265,7 +265,7 @@ endif() file(MAKE_DIRECTORY ${CMAKE_CURRENT_BINARY_DIR}/include_gpu_onthefly) file(GENERATE OUTPUT include_gpu_onthefly/GPUReconstructionKernelList.h - INPUT Base/GPUReconstructionKernels.template.h + INPUT Base/GPUReconstructionKernelList.template.h ) file(GENERATE OUTPUT include_gpu_onthefly/GPUReconstructionKernelIncludes.h From 4c0671a33e1f518ae11dd21f2f7dd27548afa18a Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sat, 16 Nov 2024 00:13:52 +0100 Subject: [PATCH 0053/2180] GPU OpenCL: subgroup functions not defined for int8 --- GPU/Common/GPUCommonAlgorithm.h | 25 +++++++++++++++++++++++-- 1 file changed, 23 insertions(+), 2 deletions(-) diff --git a/GPU/Common/GPUCommonAlgorithm.h b/GPU/Common/GPUCommonAlgorithm.h index f86bd42fe82f6..e5a963b4c2020 100644 --- a/GPU/Common/GPUCommonAlgorithm.h +++ b/GPU/Common/GPUCommonAlgorithm.h @@ -338,8 +338,29 @@ GPUdi() void GPUCommonAlgorithm::swap(T& a, T& b) // Nothing to do, work_group functions available #pragma OPENCL EXTENSION cl_khr_subgroups : enable -#define warp_scan_inclusive_add(v) sub_group_scan_inclusive_add(v) -#define warp_broadcast(v, i) sub_group_broadcast(v, i) +template +GPUdi() T work_group_scan_inclusive_add_FUNC(T v) +{ + return sub_group_scan_inclusive_add(v); +} +template <> // FIXME: It seems OpenCL does not support 8 and 16 bit subgroup operations +GPUdi() uint8_t work_group_scan_inclusive_add_FUNC(uint8_t v) +{ + return sub_group_scan_inclusive_add((uint32_t)v); +} +template +GPUdi() T work_group_broadcast_FUNC(T v, int32_t i) +{ + return sub_group_broadcast(v, i); +} +template <> +GPUdi() uint8_t work_group_broadcast_FUNC(uint8_t v, int32_t i) +{ + return sub_group_broadcast((uint32_t)v, i); +} + +#define warp_scan_inclusive_add(v) work_group_scan_inclusive_add_FUNC(v) +#define warp_broadcast(v, i) work_group_broadcast_FUNC(v, i) #elif (defined(__CUDACC__) || defined(__HIPCC__)) // CUDA and HIP work the same way using cub, need just different header From 483005418c1ec8e91d98525bc1edfcb21ddc1db6 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sat, 16 Nov 2024 00:43:14 +0100 Subject: [PATCH 0054/2180] GPU OpenCL: Fix RTC source generation --- GPU/GPUTracking/Base/opencl2/CMakeLists.txt | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/GPU/GPUTracking/Base/opencl2/CMakeLists.txt b/GPU/GPUTracking/Base/opencl2/CMakeLists.txt index 0a4168b130766..73062ad82f728 100644 --- a/GPU/GPUTracking/Base/opencl2/CMakeLists.txt +++ b/GPU/GPUTracking/Base/opencl2/CMakeLists.txt @@ -53,7 +53,7 @@ if(OPENCL2_ENABLED_SPIRV) # BUILD OpenCL2 intermediate code for SPIR-V target MAIN_DEPENDENCY ${CL_SRC} IMPLICIT_DEPENDS CXX ${CL_SRC} COMMAND_EXPAND_LISTS - COMMENT "Compiling OpenCL2 CL source file ${CL_SRC} to SPIRV") + COMMENT "Compiling OpenCL2 CL source file ${CL_SRC} to SPIRV ${CL_BIN}.spirv") create_binary_resource(${CL_BIN}.spirv ${CMAKE_CURRENT_BINARY_DIR}/GPUReconstructionOCLCode.spirv.o) set(SRCS ${SRCS} ${CMAKE_CURRENT_BINARY_DIR}/GPUReconstructionOCLCode.spirv.o) @@ -64,12 +64,14 @@ if(OPENCL2_ENABLED) # BUILD OpenCL2 source code for runtime compilation target add_custom_command( OUTPUT ${CL_BIN}.src COMMAND ${LLVM_CLANG} - ${OCL_DEFINECL} -cl-no-stdinc + ${OCL_FLAGS} + ${OCL_DEFINECL} + -cl-no-stdinc -E ${CL_SRC} > ${CL_BIN}.src MAIN_DEPENDENCY ${CL_SRC} IMPLICIT_DEPENDS CXX ${CL_SRC} COMMAND_EXPAND_LISTS - COMMENT "Preparing OpenCL2 CL source file for run time compilation") + COMMENT "Preparing OpenCL2 CL source file for run time compilation ${CL_BIN}.src") create_binary_resource(${CL_BIN}.src ${CMAKE_CURRENT_BINARY_DIR}/GPUReconstructionOCLCode.src.o) set(SRCS ${SRCS} ${CMAKE_CURRENT_BINARY_DIR}/GPUReconstructionOCLCode.src.o) From 76ee600e62e7df99ce76f37979c689536236e546 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sat, 16 Nov 2024 01:11:49 +0100 Subject: [PATCH 0055/2180] GPU OpenCL: OpenCL >=2 should use generic address space for pointer kernel arguments, otherwise clang fails to derive the address space if used in a variadic template --- GPU/GPUTracking/Definitions/GPUDef.h | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/GPU/GPUTracking/Definitions/GPUDef.h b/GPU/GPUTracking/Definitions/GPUDef.h index abc6bb04291d3..38784b1ded80e 100644 --- a/GPU/GPUTracking/Definitions/GPUDef.h +++ b/GPU/GPUTracking/Definitions/GPUDef.h @@ -25,7 +25,11 @@ // Macros for masking ptrs in OpenCL kernel calls as uint64_t (The API only allows us to pass buffer objects) #ifdef __OPENCL__ #define GPUPtr1(a, b) uint64_t b - #define GPUPtr2(a, b) ((__global a) (a) b) + #ifdef __OPENCLCPP__ + #define GPUPtr2(a, b) ((__generic a) (a) b) + #else + #define GPUPtr2(a, b) ((__global a) (a) b) + #endif #else #define GPUPtr1(a, b) a b #define GPUPtr2(a, b) b From 37adc60e71e6b470a1a948ea98c4ef9040d3a388 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sat, 16 Nov 2024 01:41:51 +0100 Subject: [PATCH 0056/2180] GPU OpenCL: Workaround for some clang name mangling issues --- GPU/GPUTracking/Refit/GPUTrackingRefit.cxx | 4 ++-- GPU/GPUTracking/SliceTracker/GPUTPCTrackletConstructor.cxx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/GPU/GPUTracking/Refit/GPUTrackingRefit.cxx b/GPU/GPUTracking/Refit/GPUTrackingRefit.cxx index 25be5b3647d57..8220b743dde0e 100644 --- a/GPU/GPUTracking/Refit/GPUTrackingRefit.cxx +++ b/GPU/GPUTracking/Refit/GPUTrackingRefit.cxx @@ -78,7 +78,7 @@ struct refitTrackTypes { } // anonymous namespace template <> -GPUd() void GPUTrackingRefit::initProp(GPUTPCGMPropagator& prop) +GPUd() void GPUTrackingRefit::initProp(GPUTPCGMPropagator& prop) // FIXME: GPUgeneric() needed to make the clang spirv output link correctly { prop.SetMaterialTPC(); prop.SetMaxSinPhi(GPUCA_MAX_SIN_PHI); @@ -91,7 +91,7 @@ GPUd() void GPUTrackingRefit::initProp(GPUTPCGMPropagator& p } template <> -GPUd() void GPUTrackingRefit::initProp(const Propagator*& prop) +GPUd() void GPUTrackingRefit::initProp(const Propagator*& prop) // FIXME: GPUgeneric() needed to make the clang spirv output link correctly { prop = mPpropagator; } diff --git a/GPU/GPUTracking/SliceTracker/GPUTPCTrackletConstructor.cxx b/GPU/GPUTracking/SliceTracker/GPUTPCTrackletConstructor.cxx index ba17b88436845..05e75232297a3 100644 --- a/GPU/GPUTracking/SliceTracker/GPUTPCTrackletConstructor.cxx +++ b/GPU/GPUTracking/SliceTracker/GPUTPCTrackletConstructor.cxx @@ -585,8 +585,8 @@ GPUd() int32_t GPUTPCTrackletConstructor::FetchTracklet(GPUconstantref() MEM_GLO #endif // GPUCA_GPUCODE #if !defined(__OPENCL1__) -template <> -GPUd() int32_t GPUTPCTrackletConstructor::GPUTPCTrackletConstructorGlobalTracking(GPUconstantref() MEM_GLOBAL(GPUTPCTracker) & GPUrestrict() tracker, GPUsharedref() GPUTPCGlobalTracking::GPUSharedMemory& sMem, MEM_LG(GPUTPCTrackParam) & GPUrestrict() tParam, int32_t row, int32_t increment, int32_t iTracklet, calink* rowHits) +template <> // FIXME: GPUgeneric() needed to make the clang spirv output link correctly +GPUd() int32_t GPUTPCTrackletConstructor::GPUTPCTrackletConstructorGlobalTracking(GPUconstantref() MEM_GLOBAL(GPUTPCTracker) & GPUrestrict() tracker, GPUsharedref() GPUTPCGlobalTracking::GPUSharedMemory& sMem, MEM_LG(GPUTPCTrackParam) & GPUrestrict() tParam, int32_t row, int32_t increment, int32_t iTracklet, calink* rowHits) { GPUTPCThreadMemory rMem; rMem.mISH = iTracklet; From bcec346566f77dd3b386dc399dcb8e4a8ff6a7ff Mon Sep 17 00:00:00 2001 From: swenzel Date: Fri, 15 Nov 2024 18:47:16 +0100 Subject: [PATCH 0057/2180] CollisionContextTool: different QED printing No longer print all QED interactions (may be too long). Rather, give a short summary of QED stats. --- .../simulation/src/DigitizationContext.cxx | 21 +++++++++++++++++++ Steer/src/CollisionContextTool.cxx | 8 +++---- 2 files changed, 25 insertions(+), 4 deletions(-) diff --git a/DataFormats/simulation/src/DigitizationContext.cxx b/DataFormats/simulation/src/DigitizationContext.cxx index bbb9b384f65fa..f3c993c9508b7 100644 --- a/DataFormats/simulation/src/DigitizationContext.cxx +++ b/DataFormats/simulation/src/DigitizationContext.cxx @@ -49,6 +49,27 @@ void DigitizationContext::printCollisionSummary(bool withQED, int truncateOutput } } else { std::cout << "Number of Collisions " << mEventRecords.size() << "\n"; + if (mEventPartsWithQED.size() > 0) { + auto num_qed_events = mEventPartsWithQED.size() - mEventRecords.size(); + if (num_qed_events > 0) { + std::cout << "Number of QED events (but not shown) " << num_qed_events << "\n"; + // find first and last QED collision so that we can give the range in orbits where these + // things are included + auto firstQEDcoll_iter = std::find_if(mEventPartsWithQED.begin(), mEventPartsWithQED.end(), + [](const std::vector& vec) { + return std::find_if(vec.begin(), vec.end(), [](EventPart const& p) { return p.sourceID == 99; }) != vec.end(); + }); + + auto lastColl_iter = std::find_if(mEventPartsWithQED.rbegin(), mEventPartsWithQED.rend(), + [](const std::vector& vec) { + return std::find_if(vec.begin(), vec.end(), [](EventPart const& p) { return p.sourceID == 99; }) != vec.end(); + }); + + auto firstindex = std::distance(mEventPartsWithQED.begin(), firstQEDcoll_iter); + auto lastindex = std::distance(mEventPartsWithQED.begin(), lastColl_iter.base()) - 1; + std::cout << "QED from: " << mEventRecordsWithQED[firstindex] << " ---> " << mEventRecordsWithQED[lastindex] << "\n"; + } + } for (int i = 0; i < mEventRecords.size(); ++i) { if (truncateOutputTo >= 0 && i > truncateOutputTo) { std::cout << "--- Output truncated to " << truncateOutputTo << " ---\n"; diff --git a/Steer/src/CollisionContextTool.cxx b/Steer/src/CollisionContextTool.cxx index 9cb4d401f3851..6dffdc921d651 100644 --- a/Steer/src/CollisionContextTool.cxx +++ b/Steer/src/CollisionContextTool.cxx @@ -456,7 +456,7 @@ int main(int argc, char* argv[]) // <---- at this moment we have a dense collision context (not representing the final output we want) LOG(info) << "<<------ DENSE CONTEXT ---------"; if (options.printContext) { - digicontext.printCollisionSummary(options.qedInteraction.size() > 0); + digicontext.printCollisionSummary(); } LOG(info) << "-------- DENSE CONTEXT ------->>"; @@ -467,7 +467,7 @@ int main(int argc, char* argv[]) // <---- at this moment we have a dense collision context (not representing the final output we want) LOG(info) << "<<------ FILTERED CONTEXT ---------"; if (options.printContext) { - digicontext.printCollisionSummary(options.qedInteraction.size() > 0); + digicontext.printCollisionSummary(); } LOG(info) << "-------- FILTERED CONTEXT ------->>"; @@ -510,7 +510,7 @@ int main(int argc, char* argv[]) } if (options.printContext) { - digicontext.printCollisionSummary(options.qedInteraction.size() > 0); + digicontext.printCollisionSummary(); } digicontext.saveToFile(options.outfilename); @@ -578,7 +578,7 @@ int main(int argc, char* argv[]) str << path_prefix << tf_output_counter++ << "/collisioncontext.root"; copy.saveToFile(str.str()); LOG(info) << "----"; - copy.printCollisionSummary(options.qedInteraction.size() > 0); + copy.printCollisionSummary(); } } } From 62d02b2450704f6face21018879086d551055b89 Mon Sep 17 00:00:00 2001 From: Gabriele Cimador Date: Wed, 6 Nov 2024 09:48:35 +0100 Subject: [PATCH 0058/2180] GPU: TPC Decoding: added variadic templates to decompressTrack() and decompressHits() in GPU code --- .../GPUTPCDecompressionKernels.cxx | 19 +++++++++++++------ .../GPUTPCDecompressionKernels.h | 9 +++++++-- 2 files changed, 20 insertions(+), 8 deletions(-) diff --git a/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.cxx b/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.cxx index 417b109cc80d3..afecb4859f1e3 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.cxx +++ b/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.cxx @@ -31,11 +31,13 @@ GPUdii() void GPUTPCDecompressionKernels::Thread +GPUdii() void GPUTPCDecompressionKernels::decompressTrack(CompressedClusters& cmprClusters, const GPUParam& param, const uint32_t maxTime, const uint32_t trackIndex, uint32_t& clusterOffset, Args&... args) { float zOffset = 0; uint32_t slice = cmprClusters.sliceA[trackIndex]; @@ -96,7 +98,7 @@ GPUdii() void GPUTPCDecompressionKernels::decompressTrack(CompressedClusters& cm time = cmprClusters.timeA[trackIndex]; pad = cmprClusters.padA[trackIndex]; } - const auto cluster = decompressTrackStore(cmprClusters, clusterOffset, slice, row, pad, time, decompressor); + const auto cluster = decompressTrackStore(cmprClusters, clusterOffset, slice, row, pad, time, args...); float y = param.tpcGeometry.LinearPad2Y(slice, row, cluster.getPad()); float z = param.tpcGeometry.LinearTime2Z(slice, cluster.getTime()); if (clusterIndex == 0) { @@ -111,7 +113,7 @@ GPUdii() void GPUTPCDecompressionKernels::decompressTrack(CompressedClusters& cm clusterOffset += cmprClusters.nTrackClusters[trackIndex] - clusterIndex; } -GPUdii() ClusterNative GPUTPCDecompressionKernels::decompressTrackStore(const o2::tpc::CompressedClusters& cmprClusters, const uint32_t clusterOffset, uint32_t slice, uint32_t row, uint32_t pad, uint32_t time, GPUTPCDecompression& decompressor) +GPUdii() ClusterNative GPUTPCDecompressionKernels::decompressTrackStore(const CompressedClusters& cmprClusters, const uint32_t clusterOffset, uint32_t slice, uint32_t row, uint32_t pad, uint32_t time, GPUTPCDecompression& decompressor) { uint32_t tmpBufferIndex = computeLinearTmpBufferIndex(slice, row, decompressor.mMaxNativeClustersPerBuffer); uint32_t currentClusterIndex = CAMath::AtomicAdd(decompressor.mNativeClustersIndex + (slice * GPUCA_ROW_COUNT + row), 1u); @@ -161,7 +163,8 @@ GPUdii() void GPUTPCDecompressionKernels::Thread +GPUdii() void GPUTPCDecompressionKernels::decompressHits(const CompressedClusters& cmprClusters, const uint32_t start, const uint32_t end, Args&... args) { uint32_t time = 0; uint16_t pad = 0; @@ -177,10 +180,14 @@ GPUdii() void GPUTPCDecompressionKernels::decompressHits(const o2::tpc::Compress time = cmprClusters.timeDiffU[k]; pad = cmprClusters.padDiffU[k]; } - *(clusterNativeBuffer++) = ClusterNative(time, cmprClusters.flagsU[k], pad, cmprClusters.sigmaTimeU[k], cmprClusters.sigmaPadU[k], cmprClusters.qMaxU[k], cmprClusters.qTotU[k]); + decompressHitsStore(cmprClusters, k, time, pad, args...); } } +GPUdii() void GPUTPCDecompressionKernels::decompressHitsStore(const CompressedClusters& cmprClusters, uint32_t k, uint32_t time, uint16_t pad, ClusterNative*& clusterNativeBuffer){ + *(clusterNativeBuffer++) = ClusterNative(time, cmprClusters.flagsU[k], pad, cmprClusters.sigmaTimeU[k], cmprClusters.sigmaPadU[k], cmprClusters.qMaxU[k], cmprClusters.qTotU[k]); +} + template GPUdi() void GPUTPCDecompressionKernels::decompressorMemcpyBasic(T* GPUrestrict() dst, const T* GPUrestrict() src, uint32_t size) { diff --git a/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.h b/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.h index 941e745da40d9..ddd9a5629768c 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.h +++ b/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.h @@ -45,9 +45,14 @@ class GPUTPCDecompressionKernels : public GPUKernelTemplate template GPUd() static void Thread(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUsharedref() GPUSharedMemory& smem, processorType& GPUrestrict() processors, Args... args); - GPUd() static void decompressTrack(o2::tpc::CompressedClusters& cmprClusters, const GPUParam& param, const uint32_t maxTime, const uint32_t trackIndex, uint32_t clusterOffset, GPUTPCDecompression& decompressor); + + template + GPUd() static void decompressTrack(o2::tpc::CompressedClusters& cmprClusters, const GPUParam& param, const uint32_t maxTime, const uint32_t trackIndex, uint32_t& clusterOffset, Args&... args); GPUdi() static o2::tpc::ClusterNative decompressTrackStore(const o2::tpc::CompressedClusters& cmprClusters, const uint32_t clusterOffset, uint32_t slice, uint32_t row, uint32_t pad, uint32_t time, GPUTPCDecompression& decompressor); - GPUdi() static void decompressHits(const o2::tpc::CompressedClusters& cmprClusters, const uint32_t start, const uint32_t end, o2::tpc::ClusterNative* clusterNativeBuffer); + + template + GPUdi() static void decompressHits(const o2::tpc::CompressedClusters& cmprClusters, const uint32_t start, const uint32_t end, Args&... args); + GPUdi() static void decompressHitsStore(const o2::tpc::CompressedClusters& cmprClusters, uint32_t k, uint32_t time, uint16_t pad, o2::tpc::ClusterNative*& clusterNativeBuffer); GPUd() static uint32_t computeLinearTmpBufferIndex(uint32_t slice, uint32_t row, uint32_t maxClustersPerBuffer) { From 804d27efa6c98d648e43c9f92aac3a9abbd6e52a Mon Sep 17 00:00:00 2001 From: Gabriele Cimador Date: Fri, 8 Nov 2024 14:47:15 +0100 Subject: [PATCH 0059/2180] GPU: TPC Decoding: add new class TPCClusterDecompressionCore to avoid code duplication for old and new decoding --- GPU/GPUTracking/CMakeLists.txt | 1 + .../DataCompression/GPUTPCDecompression.h | 2 +- .../GPUTPCDecompressionKernels.cxx | 121 +----------- .../GPUTPCDecompressionKernels.h | 4 +- .../TPCClusterDecompressionCore.inc | 185 ++++++++++++++++++ .../TPCClusterDecompressor.cxx | 6 +- .../Global/GPUChainTrackingCompression.cxx | 51 ++++- 7 files changed, 245 insertions(+), 125 deletions(-) create mode 100644 GPU/GPUTracking/DataCompression/TPCClusterDecompressionCore.inc diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index 937346fe478c3..282f8b8f25031 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -122,6 +122,7 @@ set(HDRS_INSTALL DataCompression/GPUTPCClusterRejection.h DataCompression/GPUTPCCompressionKernels.inc DataCompression/TPCClusterDecompressor.inc + DataCompression/TPCClusterDecompressionCore.inc DataTypes/GPUdEdxInfo.h DataTypes/GPUHostDataTypes.h DataTypes/GPUO2DataTypes.h diff --git a/GPU/GPUTracking/DataCompression/GPUTPCDecompression.h b/GPU/GPUTracking/DataCompression/GPUTPCDecompression.h index 49c73e6743d20..038fbd905db4f 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCDecompression.h +++ b/GPU/GPUTracking/DataCompression/GPUTPCDecompression.h @@ -43,7 +43,7 @@ class GPUTPCDecompression : public GPUProcessor friend class GPUTPCDecompressionKernels; friend class GPUTPCDecompressionUtilKernels; friend class GPUChainTracking; - + friend class TPCClusterDecompressionCore; public: #ifndef GPUCA_GPUCODE void InitializeProcessor(); diff --git a/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.cxx b/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.cxx index afecb4859f1e3..2c88ea0079a26 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.cxx +++ b/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.cxx @@ -17,6 +17,7 @@ #include "GPUConstantMem.h" #include "GPUTPCCompressionTrackModel.h" #include "GPUCommonAlgorithm.h" +#include "TPCClusterDecompressionCore.inc" using namespace GPUCA_NAMESPACE::gpu; using namespace o2::tpc; @@ -32,101 +33,10 @@ GPUdii() void GPUTPCDecompressionKernels::Thread -GPUdii() void GPUTPCDecompressionKernels::decompressTrack(CompressedClusters& cmprClusters, const GPUParam& param, const uint32_t maxTime, const uint32_t trackIndex, uint32_t& clusterOffset, Args&... args) -{ - float zOffset = 0; - uint32_t slice = cmprClusters.sliceA[trackIndex]; - uint32_t row = cmprClusters.rowA[trackIndex]; - GPUTPCCompressionTrackModel track; - uint32_t clusterIndex; - for (clusterIndex = 0; clusterIndex < cmprClusters.nTrackClusters[trackIndex]; clusterIndex++) { - uint32_t pad = 0, time = 0; - if (clusterIndex != 0) { - uint8_t tmpSlice = cmprClusters.sliceLegDiffA[clusterOffset - trackIndex - 1]; - bool changeLeg = (tmpSlice >= GPUCA_NSLICES); - if (changeLeg) { - tmpSlice -= GPUCA_NSLICES; - } - if (cmprClusters.nComppressionModes & GPUSettings::CompressionDifferences) { - slice += tmpSlice; - if (slice >= GPUCA_NSLICES) { - slice -= GPUCA_NSLICES; - } - row += cmprClusters.rowDiffA[clusterOffset - trackIndex - 1]; - if (row >= GPUCA_ROW_COUNT) { - row -= GPUCA_ROW_COUNT; - } - } else { - slice = tmpSlice; - row = cmprClusters.rowDiffA[clusterOffset - trackIndex - 1]; - } - if (changeLeg && track.Mirror()) { - break; - } - if (track.Propagate(param.tpcGeometry.Row2X(row), param.SliceParam[slice].Alpha)) { - break; - } - uint32_t timeTmp = cmprClusters.timeResA[clusterOffset - trackIndex - 1]; - if (timeTmp & 800000) { - timeTmp |= 0xFF000000; - } - time = timeTmp + ClusterNative::packTime(CAMath::Max(0.f, param.tpcGeometry.LinearZ2Time(slice, track.Z() + zOffset))); - float tmpPad = CAMath::Max(0.f, CAMath::Min((float)param.tpcGeometry.NPads(GPUCA_ROW_COUNT - 1), param.tpcGeometry.LinearY2Pad(slice, row, track.Y()))); - pad = cmprClusters.padResA[clusterOffset - trackIndex - 1] + ClusterNative::packPad(tmpPad); - time = time & 0xFFFFFF; - pad = (uint16_t)pad; - if (pad >= param.tpcGeometry.NPads(row) * ClusterNative::scalePadPacked) { - if (pad >= 0xFFFF - 11968) { // Constant 11968 = (2^15 - MAX_PADS(138) * scalePadPacked(64)) / 2 - pad = 0; - } else { - pad = param.tpcGeometry.NPads(row) * ClusterNative::scalePadPacked - 1; - } - } - if (param.continuousMaxTimeBin > 0 && time >= maxTime) { - if (time >= 0xFFFFFF - 544768) { // Constant 544768 = (2^23 - LHCMAXBUNCHES(3564) * MAXORBITS(256) * scaleTimePacked(64) / BCPERTIMEBIN(8)) / 2) - time = 0; - } else { - time = maxTime; - } - } - } else { - time = cmprClusters.timeA[trackIndex]; - pad = cmprClusters.padA[trackIndex]; - } - const auto cluster = decompressTrackStore(cmprClusters, clusterOffset, slice, row, pad, time, args...); - float y = param.tpcGeometry.LinearPad2Y(slice, row, cluster.getPad()); - float z = param.tpcGeometry.LinearTime2Z(slice, cluster.getTime()); - if (clusterIndex == 0) { - zOffset = z; - track.Init(param.tpcGeometry.Row2X(row), y, z - zOffset, param.SliceParam[slice].Alpha, cmprClusters.qPtA[trackIndex], param); - } - if (clusterIndex + 1 < cmprClusters.nTrackClusters[trackIndex] && track.Filter(y, z - zOffset, row)) { - break; - } - clusterOffset++; - } - clusterOffset += cmprClusters.nTrackClusters[trackIndex] - clusterIndex; -} - -GPUdii() ClusterNative GPUTPCDecompressionKernels::decompressTrackStore(const CompressedClusters& cmprClusters, const uint32_t clusterOffset, uint32_t slice, uint32_t row, uint32_t pad, uint32_t time, GPUTPCDecompression& decompressor) -{ - uint32_t tmpBufferIndex = computeLinearTmpBufferIndex(slice, row, decompressor.mMaxNativeClustersPerBuffer); - uint32_t currentClusterIndex = CAMath::AtomicAdd(decompressor.mNativeClustersIndex + (slice * GPUCA_ROW_COUNT + row), 1u); - const ClusterNative c(time, cmprClusters.flagsA[clusterOffset], pad, cmprClusters.sigmaTimeA[clusterOffset], cmprClusters.sigmaPadA[clusterOffset], cmprClusters.qMaxA[clusterOffset], cmprClusters.qTotA[clusterOffset]); - if (currentClusterIndex < decompressor.mMaxNativeClustersPerBuffer) { - decompressor.mTmpNativeClusters[tmpBufferIndex + currentClusterIndex] = c; - } else { - decompressor.raiseError(GPUErrors::ERROR_DECOMPRESSION_ATTACHED_CLUSTER_OVERFLOW, slice * 1000 + row, currentClusterIndex, decompressor.mMaxNativeClustersPerBuffer); - CAMath::AtomicExch(decompressor.mNativeClustersIndex + (slice * GPUCA_ROW_COUNT + row), decompressor.mMaxNativeClustersPerBuffer); - } - return c; -} - template <> GPUdii() void GPUTPCDecompressionKernels::Thread(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUsharedref() GPUSharedMemory& smem, processorType& processors, int32_t sliceStart, int32_t nSlices) { @@ -146,7 +56,7 @@ GPUdii() void GPUTPCDecompressionKernels::Thread= decompressor.mInputGPU.nSliceRows) ? 0 : decompressor.mInputGPU.nSliceRowClusters[linearIndex]); - decompressHits(cmprClusters, offsets[linearIndex], end, clout); + TPCClusterDecompressionCore::decompressHits(cmprClusters, offsets[linearIndex], end, clout); if (processors.param.rec.tpc.clustersShiftTimebins != 0.f) { for (uint32_t k = 0; k < outputAccess->nClusters[iSlice][iRow]; k++) { auto& cl = buffer[k]; @@ -163,31 +73,6 @@ GPUdii() void GPUTPCDecompressionKernels::Thread -GPUdii() void GPUTPCDecompressionKernels::decompressHits(const CompressedClusters& cmprClusters, const uint32_t start, const uint32_t end, Args&... args) -{ - uint32_t time = 0; - uint16_t pad = 0; - for (uint32_t k = start; k < end; k++) { - if (cmprClusters.nComppressionModes & GPUSettings::CompressionDifferences) { - uint32_t timeTmp = cmprClusters.timeDiffU[k]; - if (timeTmp & 800000) { - timeTmp |= 0xFF000000; - } - time += timeTmp; - pad += cmprClusters.padDiffU[k]; - } else { - time = cmprClusters.timeDiffU[k]; - pad = cmprClusters.padDiffU[k]; - } - decompressHitsStore(cmprClusters, k, time, pad, args...); - } -} - -GPUdii() void GPUTPCDecompressionKernels::decompressHitsStore(const CompressedClusters& cmprClusters, uint32_t k, uint32_t time, uint16_t pad, ClusterNative*& clusterNativeBuffer){ - *(clusterNativeBuffer++) = ClusterNative(time, cmprClusters.flagsU[k], pad, cmprClusters.sigmaTimeU[k], cmprClusters.sigmaPadU[k], cmprClusters.qMaxU[k], cmprClusters.qTotU[k]); -} - template GPUdi() void GPUTPCDecompressionKernels::decompressorMemcpyBasic(T* GPUrestrict() dst, const T* GPUrestrict() src, uint32_t size) { diff --git a/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.h b/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.h index ddd9a5629768c..cfa3589dd21f7 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.h +++ b/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.h @@ -46,13 +46,13 @@ class GPUTPCDecompressionKernels : public GPUKernelTemplate template GPUd() static void Thread(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUsharedref() GPUSharedMemory& smem, processorType& GPUrestrict() processors, Args... args); - template + /*template GPUd() static void decompressTrack(o2::tpc::CompressedClusters& cmprClusters, const GPUParam& param, const uint32_t maxTime, const uint32_t trackIndex, uint32_t& clusterOffset, Args&... args); GPUdi() static o2::tpc::ClusterNative decompressTrackStore(const o2::tpc::CompressedClusters& cmprClusters, const uint32_t clusterOffset, uint32_t slice, uint32_t row, uint32_t pad, uint32_t time, GPUTPCDecompression& decompressor); template GPUdi() static void decompressHits(const o2::tpc::CompressedClusters& cmprClusters, const uint32_t start, const uint32_t end, Args&... args); - GPUdi() static void decompressHitsStore(const o2::tpc::CompressedClusters& cmprClusters, uint32_t k, uint32_t time, uint16_t pad, o2::tpc::ClusterNative*& clusterNativeBuffer); + GPUdi() static void decompressHitsStore(const o2::tpc::CompressedClusters& cmprClusters, uint32_t k, uint32_t time, uint16_t pad, o2::tpc::ClusterNative*& clusterNativeBuffer);*/ GPUd() static uint32_t computeLinearTmpBufferIndex(uint32_t slice, uint32_t row, uint32_t maxClustersPerBuffer) { diff --git a/GPU/GPUTracking/DataCompression/TPCClusterDecompressionCore.inc b/GPU/GPUTracking/DataCompression/TPCClusterDecompressionCore.inc new file mode 100644 index 0000000000000..1aee6677edcfc --- /dev/null +++ b/GPU/GPUTracking/DataCompression/TPCClusterDecompressionCore.inc @@ -0,0 +1,185 @@ +// 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. + +/// \file TPCCLusterDecompressionCore.inc +/// \author Gabriele Cimador + +#ifndef TPCCLUSTERDECOMPRESSOR_INC +#define TPCCLUSTERDECOMPRESSOR_INC + +#include "GPUTPCDecompression.h" +#include "GPUConstantMem.h" +#include "GPUTPCCompressionTrackModel.h" +#include "GPUCommonAlgorithm.h" +#include "GPUO2DataTypes.h" + +using namespace o2::tpc; + +namespace GPUCA_NAMESPACE::gpu +{ + +class TPCClusterDecompressionCore{ + public: + +#ifndef GPUCA_GPUCODE +GPUhi() static auto decompressTrackStore(const CompressedClusters& clustersCompressed, const uint32_t offset, uint32_t slice, uint32_t row, uint32_t pad, uint32_t time, std::function func) +{ + const auto cluster = ClusterNative(time, clustersCompressed.flagsA[offset], pad, clustersCompressed.sigmaTimeA[offset], clustersCompressed.sigmaPadA[offset], clustersCompressed.qMaxA[offset], clustersCompressed.qTotA[offset]); + func(cluster, offset); + return cluster; +} + +GPUhi() static const auto& decompressTrackStore(const CompressedClusters& clustersCompressed, const uint32_t offset, uint32_t slice, uint32_t row, uint32_t pad, uint32_t time, std::vector& clusterVector) +{ + clusterVector.emplace_back(time, clustersCompressed.flagsA[offset], pad, clustersCompressed.sigmaTimeA[offset], clustersCompressed.sigmaPadA[offset], clustersCompressed.qMaxA[offset], clustersCompressed.qTotA[offset]); + return clusterVector.back(); +} + +GPUhi() static auto decompressTrackStore(const CompressedClusters& clustersCompressed, const uint32_t offset, uint32_t slice, uint32_t row, uint32_t pad, uint32_t time, std::vector (&clusters)[GPUCA_NSLICES][GPUCA_ROW_COUNT], std::atomic_flag (&locks)[GPUCA_NSLICES][GPUCA_ROW_COUNT]) +{ + std::vector& clusterVector = clusters[slice][row]; + auto& lock = locks[slice][row]; + while (lock.test_and_set(std::memory_order_acquire)) { + } + ClusterNative retVal = decompressTrackStore(clustersCompressed, offset, slice, row, pad, time, clusterVector); + lock.clear(std::memory_order_release); + return retVal; +} +#endif + +GPUdii() static ClusterNative decompressTrackStore(const CompressedClusters& cmprClusters, const uint32_t clusterOffset, uint32_t slice, uint32_t row, uint32_t pad, uint32_t time, GPUTPCDecompression& decompressor) +{ + uint32_t tmpBufferIndex = slice * (GPUCA_ROW_COUNT * decompressor.mMaxNativeClustersPerBuffer) + row * decompressor.mMaxNativeClustersPerBuffer; + uint32_t currentClusterIndex = CAMath::AtomicAdd(decompressor.mNativeClustersIndex + (slice * GPUCA_ROW_COUNT + row), 1u); + const ClusterNative c(time, cmprClusters.flagsA[clusterOffset], pad, cmprClusters.sigmaTimeA[clusterOffset], cmprClusters.sigmaPadA[clusterOffset], cmprClusters.qMaxA[clusterOffset], cmprClusters.qTotA[clusterOffset]); + if (currentClusterIndex < decompressor.mMaxNativeClustersPerBuffer) { + decompressor.mTmpNativeClusters[tmpBufferIndex + currentClusterIndex] = c; + } else { + decompressor.raiseError(GPUErrors::ERROR_DECOMPRESSION_ATTACHED_CLUSTER_OVERFLOW, slice * 1000 + row, currentClusterIndex, decompressor.mMaxNativeClustersPerBuffer); + CAMath::AtomicExch(decompressor.mNativeClustersIndex + (slice * GPUCA_ROW_COUNT + row), decompressor.mMaxNativeClustersPerBuffer); + } + return c; +} + +template +GPUhdi() static void decompressTrack(const CompressedClusters& cmprClusters, const GPUParam& param, const uint32_t maxTime, const uint32_t trackIndex, uint32_t& clusterOffset, Args&... args) +{ + float zOffset = 0; + uint32_t slice = cmprClusters.sliceA[trackIndex]; + uint32_t row = cmprClusters.rowA[trackIndex]; + GPUTPCCompressionTrackModel track; + uint32_t clusterIndex; + for (clusterIndex = 0; clusterIndex < cmprClusters.nTrackClusters[trackIndex]; clusterIndex++) { + uint32_t pad = 0, time = 0; + if (clusterIndex != 0) { + uint8_t tmpSlice = cmprClusters.sliceLegDiffA[clusterOffset - trackIndex - 1]; + bool changeLeg = (tmpSlice >= GPUCA_NSLICES); + if (changeLeg) { + tmpSlice -= GPUCA_NSLICES; + } + if (cmprClusters.nComppressionModes & GPUSettings::CompressionDifferences) { + slice += tmpSlice; + if (slice >= GPUCA_NSLICES) { + slice -= GPUCA_NSLICES; + } + row += cmprClusters.rowDiffA[clusterOffset - trackIndex - 1]; + if (row >= GPUCA_ROW_COUNT) { + row -= GPUCA_ROW_COUNT; + } + } else { + slice = tmpSlice; + row = cmprClusters.rowDiffA[clusterOffset - trackIndex - 1]; + } + if (changeLeg && track.Mirror()) { + break; + } + if (track.Propagate(param.tpcGeometry.Row2X(row), param.SliceParam[slice].Alpha)) { + break; + } + uint32_t timeTmp = cmprClusters.timeResA[clusterOffset - trackIndex - 1]; + if (timeTmp & 800000) { + timeTmp |= 0xFF000000; + } + time = timeTmp + ClusterNative::packTime(CAMath::Max(0.f, param.tpcGeometry.LinearZ2Time(slice, track.Z() + zOffset))); + float tmpPad = CAMath::Max(0.f, CAMath::Min((float)param.tpcGeometry.NPads(GPUCA_ROW_COUNT - 1), param.tpcGeometry.LinearY2Pad(slice, row, track.Y()))); + pad = cmprClusters.padResA[clusterOffset - trackIndex - 1] + ClusterNative::packPad(tmpPad); + time = time & 0xFFFFFF; + pad = (uint16_t)pad; + if (pad >= param.tpcGeometry.NPads(row) * ClusterNative::scalePadPacked) { + if (pad >= 0xFFFF - 11968) { // Constant 11968 = (2^15 - MAX_PADS(138) * scalePadPacked(64)) / 2 + pad = 0; + } else { + pad = param.tpcGeometry.NPads(row) * ClusterNative::scalePadPacked - 1; + } + } + if (param.continuousMaxTimeBin > 0 && time >= maxTime) { + if (time >= 0xFFFFFF - 544768) { // Constant 544768 = (2^23 - LHCMAXBUNCHES(3564) * MAXORBITS(256) * scaleTimePacked(64) / BCPERTIMEBIN(8)) / 2) + time = 0; + } else { + time = maxTime; + } + } + } else { + time = cmprClusters.timeA[trackIndex]; + pad = cmprClusters.padA[trackIndex]; + } + const auto cluster = decompressTrackStore(cmprClusters, clusterOffset, slice, row, pad, time, args...); + float y = param.tpcGeometry.LinearPad2Y(slice, row, cluster.getPad()); + float z = param.tpcGeometry.LinearTime2Z(slice, cluster.getTime()); + if (clusterIndex == 0) { + zOffset = z; + track.Init(param.tpcGeometry.Row2X(row), y, z - zOffset, param.SliceParam[slice].Alpha, cmprClusters.qPtA[trackIndex], param); + } + if (clusterIndex + 1 < cmprClusters.nTrackClusters[trackIndex] && track.Filter(y, z - zOffset, row)) { + break; + } + clusterOffset++; + } + clusterOffset += cmprClusters.nTrackClusters[trackIndex] - clusterIndex; +} + +GPUhdi() static const auto& decompressHitsStore(const CompressedClusters& cmprClusters, uint32_t k, uint32_t time, uint16_t pad, ClusterNative*& clusterBuffer) +{ + return ((*(clusterBuffer++) = ClusterNative(time, cmprClusters.flagsU[k], pad, cmprClusters.sigmaTimeU[k], cmprClusters.sigmaPadU[k], cmprClusters.qMaxU[k], cmprClusters.qTotU[k]))); +} + +GPUhdi() static auto decompressHitsStore(const CompressedClusters& cmprClusters, uint32_t k, uint32_t time, uint16_t pad, std::function func) +{ + const auto cluster = ClusterNative(time, cmprClusters.flagsU[k], pad, cmprClusters.sigmaTimeU[k], cmprClusters.sigmaPadU[k], cmprClusters.qMaxU[k], cmprClusters.qTotU[k]); + func(cluster, k); + return cluster; +} + +template +GPUdii() static void decompressHits(const CompressedClusters& cmprClusters, const uint32_t start, const uint32_t end, Args&... args) +{ + uint32_t time = 0; + uint16_t pad = 0; + for (uint32_t k = start; k < end; k++) { + if (cmprClusters.nComppressionModes & GPUSettings::CompressionDifferences) { + uint32_t timeTmp = cmprClusters.timeDiffU[k]; + if (timeTmp & 800000) { + timeTmp |= 0xFF000000; + } + time += timeTmp; + pad += cmprClusters.padDiffU[k]; + } else { + time = cmprClusters.timeDiffU[k]; + pad = cmprClusters.padDiffU[k]; + } + decompressHitsStore(cmprClusters, k, time, pad, args...); + } +} + +}; +} + +#endif \ No newline at end of file diff --git a/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.cxx b/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.cxx index 503f09c22af5c..4a38c2e0d57e5 100644 --- a/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.cxx +++ b/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.cxx @@ -20,7 +20,7 @@ #include #include #include -#include "TPCClusterDecompressor.inc" +#include "TPCClusterDecompressionCore.inc" using namespace GPUCA_NAMESPACE::gpu; using namespace o2::tpc; @@ -62,7 +62,7 @@ int32_t TPCClusterDecompressor::decompress(const CompressedClusters* clustersCom offset += clustersCompressed->nTrackClusters[lasti++]; } lasti++; - decompressTrack(clustersCompressed, param, maxTime, i, offset, clusters, locks); + TPCClusterDecompressionCore::decompressTrack(*clustersCompressed, param, maxTime, i, offset, clusters, locks); } size_t nTotalClusters = clustersCompressed->nAttachedClusters + clustersCompressed->nUnattachedClusters; ClusterNative* clusterBuffer = allocator(nTotalClusters); @@ -91,7 +91,7 @@ int32_t TPCClusterDecompressor::decompress(const CompressedClusters* clustersCom } ClusterNative* clout = buffer + clusters[i][j].size(); uint32_t end = offsets[i][j] + ((i * GPUCA_ROW_COUNT + j >= clustersCompressed->nSliceRows) ? 0 : clustersCompressed->nSliceRowClusters[i * GPUCA_ROW_COUNT + j]); - decompressHits(clustersCompressed, offsets[i][j], end, clout); + TPCClusterDecompressionCore::decompressHits(*clustersCompressed, offsets[i][j], end, clout); if (param.rec.tpc.clustersShiftTimebins != 0.f) { for (uint32_t k = 0; k < clustersNative.nClusters[i][j]; k++) { auto& cl = buffer[k]; diff --git a/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx b/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx index c0679c090c20c..b2203ec8de999 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx @@ -205,6 +205,12 @@ int32_t GPUChainTracking::RunTPCCompression() int32_t GPUChainTracking::RunTPCDecompression() { + ClusterNativeAccess* original = new ClusterNativeAccess; + original->clustersLinear = new ClusterNative[mIOPtrs.clustersNative->nClustersTotal]; + memcpy((void*)original->clustersLinear, mIOPtrs.clustersNative->clustersLinear, mIOPtrs.clustersNative->nClustersTotal * sizeof(mIOPtrs.clustersNative->clustersLinear[0])); + memcpy((void*)original->nClusters, mIOPtrs.clustersNative->nClusters, NSLICES * GPUCA_ROW_COUNT * sizeof(mIOPtrs.clustersNative->nClusters[0][0])); + original->setOffsetPtrs(); + #ifdef GPUCA_HAVE_O2HEADERS if (GetProcessingSettings().tpcUseOldCPUDecoding) { const auto& threadContext = GetThreadContext(); @@ -374,7 +380,7 @@ int32_t GPUChainTracking::RunTPCDecompression() } SynchronizeGPU(); - if (GetProcessingSettings().deterministicGPUReconstruction || GetProcessingSettings().debugLevel >= 4) { + if (1 || GetProcessingSettings().deterministicGPUReconstruction || GetProcessingSettings().debugLevel >= 4) { runKernel(GetGridAutoStep(unattachedStream, RecoStep::TPCDecompression)); const ClusterNativeAccess* decoded = mIOPtrs.clustersNative; if (doGPU) { @@ -387,6 +393,49 @@ int32_t GPUChainTracking::RunTPCDecompression() } } } + + const ClusterNativeAccess* decoded = mIOPtrs.clustersNative; + unsigned int decodingErrors = 0; + std::vector tmpClusters; + if (param().rec.tpc.rejectionStrategy == GPUSettings::RejectionNone) { + for (unsigned int i = 0; i < NSLICES; i++) { + for (unsigned int j = 0; j < GPUCA_ROW_COUNT; j++) { + if (original->nClusters[i][j] != decoded->nClusters[i][j]) { + GPUError("Number of clusters mismatch slice %u row %u: expected %d v.s. decoded %d", i, j, original->nClusters[i][j], decoded->nClusters[i][j]); + decodingErrors++; + continue; + } + tmpClusters.resize(original->nClusters[i][j]); + for (unsigned int k = 0; k < original->nClusters[i][j]; k++) { + tmpClusters[k] = original->clusters[i][j][k]; + if (param().rec.tpc.compressionTypeMask & GPUSettings::CompressionTruncate) { + GPUTPCCompression::truncateSignificantBitsChargeMax(tmpClusters[k].qMax, param()); + GPUTPCCompression::truncateSignificantBitsCharge(tmpClusters[k].qTot, param()); + GPUTPCCompression::truncateSignificantBitsWidth(tmpClusters[k].sigmaPadPacked, param()); + GPUTPCCompression::truncateSignificantBitsWidth(tmpClusters[k].sigmaTimePacked, param()); + } + } + std::sort(tmpClusters.begin(), tmpClusters.end()); + for (unsigned int k = 0; k < original->nClusters[i][j]; k++) { + const o2::tpc::ClusterNative& c1 = tmpClusters[k]; + const o2::tpc::ClusterNative& c2 = decoded->clusters[i][j][k]; + if (!(c1 == c2)) { + if (decodingErrors++ < 100) { + GPUWarning("Cluster mismatch: slice %2u row %3u hit %5u: %6d %3d %4d %3d %3d %4d %4d", i, j, k, (int)c1.getTimePacked(), (int)c1.getFlags(), (int)c1.padPacked, (int)c1.sigmaTimePacked, (int)c1.sigmaPadPacked, (int)c1.qMax, (int)c1.qTot); + GPUWarning("%45s %6d %3d %4d %3d %3d %4d %4d", "", (int)c2.getTimePacked(), (int)c2.getFlags(), (int)c2.padPacked, (int)c2.sigmaTimePacked, (int)c2.sigmaPadPacked, (int)c2.qMax, (int)c2.qTot); + } + } + } + } + } +if (decodingErrors) { + GPUWarning("Errors during cluster decoding %u\n", decodingErrors); + } else { + GPUInfo("Cluster decoding verification on GPU: PASSED"); + } +} + delete[] original->clustersLinear; + delete original; mRec->PopNonPersistentMemory(RecoStep::TPCDecompression, qStr2Tag("TPCDCMPR")); } #endif From 5695338ef4a7c5c2d2f80f0848b096de229e1bf6 Mon Sep 17 00:00:00 2001 From: Gabriele Cimador Date: Mon, 11 Nov 2024 16:17:54 +0100 Subject: [PATCH 0060/2180] GPU: TPC Decoding: integrated new decoding class stucture into TPC/.../EntropyEncoderSpec.cxx --- .../TPC/workflow/src/EntropyEncoderSpec.cxx | 6 +-- .../TPCClusterDecompressionCore.inc | 4 +- .../Global/GPUChainTrackingCompression.cxx | 51 +------------------ 3 files changed, 6 insertions(+), 55 deletions(-) diff --git a/Detectors/TPC/workflow/src/EntropyEncoderSpec.cxx b/Detectors/TPC/workflow/src/EntropyEncoderSpec.cxx index b81cb9a802a4a..294a93709e863 100644 --- a/Detectors/TPC/workflow/src/EntropyEncoderSpec.cxx +++ b/Detectors/TPC/workflow/src/EntropyEncoderSpec.cxx @@ -25,7 +25,7 @@ #include "GPUO2InterfaceUtils.h" #include "GPUParam.h" #include "DataFormatsTPC/ClusterNative.h" -#include "TPCClusterDecompressor.inc" +#include "TPCClusterDecompressionCore.inc" #include "GPUTPCCompressionKernels.inc" #include "TPCCalibration/VDriftHelper.h" #include "DetectorsBase/GRPGeomHelper.h" @@ -183,7 +183,7 @@ void EntropyEncoderSpec::run(ProcessingContext& pc) offset += clusters.nTrackClusters[lasti++]; } lasti++; - o2::gpu::TPCClusterDecompressor::decompressTrack(&clusters, *mParam, maxTime, i, offset, checker); + o2::gpu::TPCClusterDecompressionCore::decompressTrack(clusters, *mParam, maxTime, i, offset, checker); const float tMin = o2::tpc::ClusterNative::unpackTime(tMinP), tMax = o2::tpc::ClusterNative::unpackTime(tMaxP); const auto chkVal = firstIR + (tMin * constants::LHCBCPERTIMEBIN); const auto chkExt = totalT > tMax - tMin ? ((totalT - (tMax - tMin)) * constants::LHCBCPERTIMEBIN + 1) : 0; @@ -255,7 +255,7 @@ void EntropyEncoderSpec::run(ProcessingContext& pc) } }; unsigned int end = offsets[i][j] + clusters.nSliceRowClusters[i * GPUCA_ROW_COUNT + j]; - o2::gpu::TPCClusterDecompressor::decompressHits(&clusters, offsets[i][j], end, checker); + o2::gpu::TPCClusterDecompressionCore::decompressHits(clusters, offsets[i][j], end, checker); } tmpBuffer[0].first.reserve(clustersFiltered.nUnattachedClusters); tmpBuffer[0].second.reserve(clustersFiltered.nUnattachedClusters); diff --git a/GPU/GPUTracking/DataCompression/TPCClusterDecompressionCore.inc b/GPU/GPUTracking/DataCompression/TPCClusterDecompressionCore.inc index 1aee6677edcfc..5a2fc1e85c71d 100644 --- a/GPU/GPUTracking/DataCompression/TPCClusterDecompressionCore.inc +++ b/GPU/GPUTracking/DataCompression/TPCClusterDecompressionCore.inc @@ -55,7 +55,7 @@ GPUhi() static auto decompressTrackStore(const CompressedClusters& clustersCompr } #endif -GPUdii() static ClusterNative decompressTrackStore(const CompressedClusters& cmprClusters, const uint32_t clusterOffset, uint32_t slice, uint32_t row, uint32_t pad, uint32_t time, GPUTPCDecompression& decompressor) +GPUdi() static ClusterNative decompressTrackStore(const CompressedClusters& cmprClusters, const uint32_t clusterOffset, uint32_t slice, uint32_t row, uint32_t pad, uint32_t time, GPUTPCDecompression& decompressor) { uint32_t tmpBufferIndex = slice * (GPUCA_ROW_COUNT * decompressor.mMaxNativeClustersPerBuffer) + row * decompressor.mMaxNativeClustersPerBuffer; uint32_t currentClusterIndex = CAMath::AtomicAdd(decompressor.mNativeClustersIndex + (slice * GPUCA_ROW_COUNT + row), 1u); @@ -159,7 +159,7 @@ GPUhdi() static auto decompressHitsStore(const CompressedClusters& cmprClusters, } template -GPUdii() static void decompressHits(const CompressedClusters& cmprClusters, const uint32_t start, const uint32_t end, Args&... args) +GPUdi() static void decompressHits(const CompressedClusters& cmprClusters, const uint32_t start, const uint32_t end, Args&... args) { uint32_t time = 0; uint16_t pad = 0; diff --git a/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx b/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx index b2203ec8de999..c0679c090c20c 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx @@ -205,12 +205,6 @@ int32_t GPUChainTracking::RunTPCCompression() int32_t GPUChainTracking::RunTPCDecompression() { - ClusterNativeAccess* original = new ClusterNativeAccess; - original->clustersLinear = new ClusterNative[mIOPtrs.clustersNative->nClustersTotal]; - memcpy((void*)original->clustersLinear, mIOPtrs.clustersNative->clustersLinear, mIOPtrs.clustersNative->nClustersTotal * sizeof(mIOPtrs.clustersNative->clustersLinear[0])); - memcpy((void*)original->nClusters, mIOPtrs.clustersNative->nClusters, NSLICES * GPUCA_ROW_COUNT * sizeof(mIOPtrs.clustersNative->nClusters[0][0])); - original->setOffsetPtrs(); - #ifdef GPUCA_HAVE_O2HEADERS if (GetProcessingSettings().tpcUseOldCPUDecoding) { const auto& threadContext = GetThreadContext(); @@ -380,7 +374,7 @@ int32_t GPUChainTracking::RunTPCDecompression() } SynchronizeGPU(); - if (1 || GetProcessingSettings().deterministicGPUReconstruction || GetProcessingSettings().debugLevel >= 4) { + if (GetProcessingSettings().deterministicGPUReconstruction || GetProcessingSettings().debugLevel >= 4) { runKernel(GetGridAutoStep(unattachedStream, RecoStep::TPCDecompression)); const ClusterNativeAccess* decoded = mIOPtrs.clustersNative; if (doGPU) { @@ -393,49 +387,6 @@ int32_t GPUChainTracking::RunTPCDecompression() } } } - - const ClusterNativeAccess* decoded = mIOPtrs.clustersNative; - unsigned int decodingErrors = 0; - std::vector tmpClusters; - if (param().rec.tpc.rejectionStrategy == GPUSettings::RejectionNone) { - for (unsigned int i = 0; i < NSLICES; i++) { - for (unsigned int j = 0; j < GPUCA_ROW_COUNT; j++) { - if (original->nClusters[i][j] != decoded->nClusters[i][j]) { - GPUError("Number of clusters mismatch slice %u row %u: expected %d v.s. decoded %d", i, j, original->nClusters[i][j], decoded->nClusters[i][j]); - decodingErrors++; - continue; - } - tmpClusters.resize(original->nClusters[i][j]); - for (unsigned int k = 0; k < original->nClusters[i][j]; k++) { - tmpClusters[k] = original->clusters[i][j][k]; - if (param().rec.tpc.compressionTypeMask & GPUSettings::CompressionTruncate) { - GPUTPCCompression::truncateSignificantBitsChargeMax(tmpClusters[k].qMax, param()); - GPUTPCCompression::truncateSignificantBitsCharge(tmpClusters[k].qTot, param()); - GPUTPCCompression::truncateSignificantBitsWidth(tmpClusters[k].sigmaPadPacked, param()); - GPUTPCCompression::truncateSignificantBitsWidth(tmpClusters[k].sigmaTimePacked, param()); - } - } - std::sort(tmpClusters.begin(), tmpClusters.end()); - for (unsigned int k = 0; k < original->nClusters[i][j]; k++) { - const o2::tpc::ClusterNative& c1 = tmpClusters[k]; - const o2::tpc::ClusterNative& c2 = decoded->clusters[i][j][k]; - if (!(c1 == c2)) { - if (decodingErrors++ < 100) { - GPUWarning("Cluster mismatch: slice %2u row %3u hit %5u: %6d %3d %4d %3d %3d %4d %4d", i, j, k, (int)c1.getTimePacked(), (int)c1.getFlags(), (int)c1.padPacked, (int)c1.sigmaTimePacked, (int)c1.sigmaPadPacked, (int)c1.qMax, (int)c1.qTot); - GPUWarning("%45s %6d %3d %4d %3d %3d %4d %4d", "", (int)c2.getTimePacked(), (int)c2.getFlags(), (int)c2.padPacked, (int)c2.sigmaTimePacked, (int)c2.sigmaPadPacked, (int)c2.qMax, (int)c2.qTot); - } - } - } - } - } -if (decodingErrors) { - GPUWarning("Errors during cluster decoding %u\n", decodingErrors); - } else { - GPUInfo("Cluster decoding verification on GPU: PASSED"); - } -} - delete[] original->clustersLinear; - delete original; mRec->PopNonPersistentMemory(RecoStep::TPCDecompression, qStr2Tag("TPCDCMPR")); } #endif From 9b43d33e91c5263fb50c47cccc53b2becbc5d851 Mon Sep 17 00:00:00 2001 From: Gabriele Cimador Date: Thu, 14 Nov 2024 16:37:43 +0100 Subject: [PATCH 0061/2180] Removed TPCCluterDecompressor.inc --- GPU/GPUTracking/CMakeLists.txt | 1 - .../DataCompression/GPUTPCDecompression.h | 1 + .../GPUTPCDecompressionKernels.h | 8 - .../TPCClusterDecompressionCore.inc | 267 +++++++++--------- .../DataCompression/TPCClusterDecompressor.h | 5 - .../TPCClusterDecompressor.inc | 164 ----------- 6 files changed, 137 insertions(+), 309 deletions(-) delete mode 100644 GPU/GPUTracking/DataCompression/TPCClusterDecompressor.inc diff --git a/GPU/GPUTracking/CMakeLists.txt b/GPU/GPUTracking/CMakeLists.txt index 282f8b8f25031..6400fbc65dc61 100644 --- a/GPU/GPUTracking/CMakeLists.txt +++ b/GPU/GPUTracking/CMakeLists.txt @@ -121,7 +121,6 @@ set(HDRS_INSTALL Base/GPUReconstructionKernels.h DataCompression/GPUTPCClusterRejection.h DataCompression/GPUTPCCompressionKernels.inc - DataCompression/TPCClusterDecompressor.inc DataCompression/TPCClusterDecompressionCore.inc DataTypes/GPUdEdxInfo.h DataTypes/GPUHostDataTypes.h diff --git a/GPU/GPUTracking/DataCompression/GPUTPCDecompression.h b/GPU/GPUTracking/DataCompression/GPUTPCDecompression.h index 038fbd905db4f..d9871613d8401 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCDecompression.h +++ b/GPU/GPUTracking/DataCompression/GPUTPCDecompression.h @@ -44,6 +44,7 @@ class GPUTPCDecompression : public GPUProcessor friend class GPUTPCDecompressionUtilKernels; friend class GPUChainTracking; friend class TPCClusterDecompressionCore; + public: #ifndef GPUCA_GPUCODE void InitializeProcessor(); diff --git a/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.h b/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.h index cfa3589dd21f7..622e1fd984fa7 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.h +++ b/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.h @@ -45,14 +45,6 @@ class GPUTPCDecompressionKernels : public GPUKernelTemplate template GPUd() static void Thread(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUsharedref() GPUSharedMemory& smem, processorType& GPUrestrict() processors, Args... args); - - /*template - GPUd() static void decompressTrack(o2::tpc::CompressedClusters& cmprClusters, const GPUParam& param, const uint32_t maxTime, const uint32_t trackIndex, uint32_t& clusterOffset, Args&... args); - GPUdi() static o2::tpc::ClusterNative decompressTrackStore(const o2::tpc::CompressedClusters& cmprClusters, const uint32_t clusterOffset, uint32_t slice, uint32_t row, uint32_t pad, uint32_t time, GPUTPCDecompression& decompressor); - - template - GPUdi() static void decompressHits(const o2::tpc::CompressedClusters& cmprClusters, const uint32_t start, const uint32_t end, Args&... args); - GPUdi() static void decompressHitsStore(const o2::tpc::CompressedClusters& cmprClusters, uint32_t k, uint32_t time, uint16_t pad, o2::tpc::ClusterNative*& clusterNativeBuffer);*/ GPUd() static uint32_t computeLinearTmpBufferIndex(uint32_t slice, uint32_t row, uint32_t maxClustersPerBuffer) { diff --git a/GPU/GPUTracking/DataCompression/TPCClusterDecompressionCore.inc b/GPU/GPUTracking/DataCompression/TPCClusterDecompressionCore.inc index 5a2fc1e85c71d..73352182328d5 100644 --- a/GPU/GPUTracking/DataCompression/TPCClusterDecompressionCore.inc +++ b/GPU/GPUTracking/DataCompression/TPCClusterDecompressionCore.inc @@ -21,165 +21,170 @@ #include "GPUCommonAlgorithm.h" #include "GPUO2DataTypes.h" +#ifndef GPUCA_GPUCODE +#include +#endif + using namespace o2::tpc; namespace GPUCA_NAMESPACE::gpu { -class TPCClusterDecompressionCore{ - public: - -#ifndef GPUCA_GPUCODE -GPUhi() static auto decompressTrackStore(const CompressedClusters& clustersCompressed, const uint32_t offset, uint32_t slice, uint32_t row, uint32_t pad, uint32_t time, std::function func) +class TPCClusterDecompressionCore { - const auto cluster = ClusterNative(time, clustersCompressed.flagsA[offset], pad, clustersCompressed.sigmaTimeA[offset], clustersCompressed.sigmaPadA[offset], clustersCompressed.qMaxA[offset], clustersCompressed.qTotA[offset]); - func(cluster, offset); - return cluster; -} + public: +#ifndef GPUCA_GPUCODE + GPUhi() static auto decompressTrackStore(const CompressedClusters& clustersCompressed, const uint32_t offset, uint32_t slice, uint32_t row, uint32_t pad, uint32_t time, std::function func) + { + const auto cluster = ClusterNative(time, clustersCompressed.flagsA[offset], pad, clustersCompressed.sigmaTimeA[offset], clustersCompressed.sigmaPadA[offset], clustersCompressed.qMaxA[offset], clustersCompressed.qTotA[offset]); + func(cluster, offset); + return cluster; + } -GPUhi() static const auto& decompressTrackStore(const CompressedClusters& clustersCompressed, const uint32_t offset, uint32_t slice, uint32_t row, uint32_t pad, uint32_t time, std::vector& clusterVector) -{ - clusterVector.emplace_back(time, clustersCompressed.flagsA[offset], pad, clustersCompressed.sigmaTimeA[offset], clustersCompressed.sigmaPadA[offset], clustersCompressed.qMaxA[offset], clustersCompressed.qTotA[offset]); - return clusterVector.back(); -} + GPUhi() static const auto& decompressTrackStore(const CompressedClusters& clustersCompressed, const uint32_t offset, uint32_t slice, uint32_t row, uint32_t pad, uint32_t time, std::vector& clusterVector) + { + clusterVector.emplace_back(time, clustersCompressed.flagsA[offset], pad, clustersCompressed.sigmaTimeA[offset], clustersCompressed.sigmaPadA[offset], clustersCompressed.qMaxA[offset], clustersCompressed.qTotA[offset]); + return clusterVector.back(); + } -GPUhi() static auto decompressTrackStore(const CompressedClusters& clustersCompressed, const uint32_t offset, uint32_t slice, uint32_t row, uint32_t pad, uint32_t time, std::vector (&clusters)[GPUCA_NSLICES][GPUCA_ROW_COUNT], std::atomic_flag (&locks)[GPUCA_NSLICES][GPUCA_ROW_COUNT]) -{ - std::vector& clusterVector = clusters[slice][row]; - auto& lock = locks[slice][row]; - while (lock.test_and_set(std::memory_order_acquire)) { + GPUhi() static auto decompressTrackStore(const CompressedClusters& clustersCompressed, const uint32_t offset, uint32_t slice, uint32_t row, uint32_t pad, uint32_t time, std::vector (&clusters)[GPUCA_NSLICES][GPUCA_ROW_COUNT], std::atomic_flag (&locks)[GPUCA_NSLICES][GPUCA_ROW_COUNT]) + { + std::vector& clusterVector = clusters[slice][row]; + auto& lock = locks[slice][row]; + while (lock.test_and_set(std::memory_order_acquire)) { + } + ClusterNative retVal = decompressTrackStore(clustersCompressed, offset, slice, row, pad, time, clusterVector); + lock.clear(std::memory_order_release); + return retVal; } - ClusterNative retVal = decompressTrackStore(clustersCompressed, offset, slice, row, pad, time, clusterVector); - lock.clear(std::memory_order_release); - return retVal; -} #endif -GPUdi() static ClusterNative decompressTrackStore(const CompressedClusters& cmprClusters, const uint32_t clusterOffset, uint32_t slice, uint32_t row, uint32_t pad, uint32_t time, GPUTPCDecompression& decompressor) -{ - uint32_t tmpBufferIndex = slice * (GPUCA_ROW_COUNT * decompressor.mMaxNativeClustersPerBuffer) + row * decompressor.mMaxNativeClustersPerBuffer; - uint32_t currentClusterIndex = CAMath::AtomicAdd(decompressor.mNativeClustersIndex + (slice * GPUCA_ROW_COUNT + row), 1u); - const ClusterNative c(time, cmprClusters.flagsA[clusterOffset], pad, cmprClusters.sigmaTimeA[clusterOffset], cmprClusters.sigmaPadA[clusterOffset], cmprClusters.qMaxA[clusterOffset], cmprClusters.qTotA[clusterOffset]); - if (currentClusterIndex < decompressor.mMaxNativeClustersPerBuffer) { - decompressor.mTmpNativeClusters[tmpBufferIndex + currentClusterIndex] = c; - } else { - decompressor.raiseError(GPUErrors::ERROR_DECOMPRESSION_ATTACHED_CLUSTER_OVERFLOW, slice * 1000 + row, currentClusterIndex, decompressor.mMaxNativeClustersPerBuffer); - CAMath::AtomicExch(decompressor.mNativeClustersIndex + (slice * GPUCA_ROW_COUNT + row), decompressor.mMaxNativeClustersPerBuffer); + GPUdi() static ClusterNative decompressTrackStore(const CompressedClusters& cmprClusters, const uint32_t clusterOffset, uint32_t slice, uint32_t row, uint32_t pad, uint32_t time, GPUTPCDecompression& decompressor) + { + uint32_t tmpBufferIndex = slice * (GPUCA_ROW_COUNT * decompressor.mMaxNativeClustersPerBuffer) + row * decompressor.mMaxNativeClustersPerBuffer; + uint32_t currentClusterIndex = CAMath::AtomicAdd(decompressor.mNativeClustersIndex + (slice * GPUCA_ROW_COUNT + row), 1u); + const ClusterNative c(time, cmprClusters.flagsA[clusterOffset], pad, cmprClusters.sigmaTimeA[clusterOffset], cmprClusters.sigmaPadA[clusterOffset], cmprClusters.qMaxA[clusterOffset], cmprClusters.qTotA[clusterOffset]); + if (currentClusterIndex < decompressor.mMaxNativeClustersPerBuffer) { + decompressor.mTmpNativeClusters[tmpBufferIndex + currentClusterIndex] = c; + } else { + decompressor.raiseError(GPUErrors::ERROR_DECOMPRESSION_ATTACHED_CLUSTER_OVERFLOW, slice * 1000 + row, currentClusterIndex, decompressor.mMaxNativeClustersPerBuffer); + CAMath::AtomicExch(decompressor.mNativeClustersIndex + (slice * GPUCA_ROW_COUNT + row), decompressor.mMaxNativeClustersPerBuffer); + } + return c; } - return c; -} -template -GPUhdi() static void decompressTrack(const CompressedClusters& cmprClusters, const GPUParam& param, const uint32_t maxTime, const uint32_t trackIndex, uint32_t& clusterOffset, Args&... args) -{ - float zOffset = 0; - uint32_t slice = cmprClusters.sliceA[trackIndex]; - uint32_t row = cmprClusters.rowA[trackIndex]; - GPUTPCCompressionTrackModel track; - uint32_t clusterIndex; - for (clusterIndex = 0; clusterIndex < cmprClusters.nTrackClusters[trackIndex]; clusterIndex++) { - uint32_t pad = 0, time = 0; - if (clusterIndex != 0) { - uint8_t tmpSlice = cmprClusters.sliceLegDiffA[clusterOffset - trackIndex - 1]; - bool changeLeg = (tmpSlice >= GPUCA_NSLICES); - if (changeLeg) { - tmpSlice -= GPUCA_NSLICES; - } - if (cmprClusters.nComppressionModes & GPUSettings::CompressionDifferences) { - slice += tmpSlice; - if (slice >= GPUCA_NSLICES) { - slice -= GPUCA_NSLICES; + template + GPUdi() static void decompressTrack(const CompressedClusters& cmprClusters, const GPUParam& param, const uint32_t maxTime, const uint32_t& trackIndex, uint32_t& clusterOffset, Args&... args) + { + float zOffset = 0; + uint32_t slice = cmprClusters.sliceA[trackIndex]; + uint32_t row = cmprClusters.rowA[trackIndex]; + GPUTPCCompressionTrackModel track; + uint32_t clusterIndex; + for (clusterIndex = 0; clusterIndex < cmprClusters.nTrackClusters[trackIndex]; clusterIndex++) { + uint32_t pad = 0, time = 0; + if (clusterIndex != 0) { + uint8_t tmpSlice = cmprClusters.sliceLegDiffA[clusterOffset - trackIndex - 1]; + bool changeLeg = (tmpSlice >= GPUCA_NSLICES); + if (changeLeg) { + tmpSlice -= GPUCA_NSLICES; + } + if (cmprClusters.nComppressionModes & GPUSettings::CompressionDifferences) { + slice += tmpSlice; + if (slice >= GPUCA_NSLICES) { + slice -= GPUCA_NSLICES; + } + row += cmprClusters.rowDiffA[clusterOffset - trackIndex - 1]; + if (row >= GPUCA_ROW_COUNT) { + row -= GPUCA_ROW_COUNT; + } + } else { + slice = tmpSlice; + row = cmprClusters.rowDiffA[clusterOffset - trackIndex - 1]; + } + if (changeLeg && track.Mirror()) { + break; + } + if (track.Propagate(param.tpcGeometry.Row2X(row), param.SliceParam[slice].Alpha)) { + break; + } + uint32_t timeTmp = cmprClusters.timeResA[clusterOffset - trackIndex - 1]; + if (timeTmp & 800000) { + timeTmp |= 0xFF000000; + } + time = timeTmp + ClusterNative::packTime(CAMath::Max(0.f, param.tpcGeometry.LinearZ2Time(slice, track.Z() + zOffset))); + float tmpPad = CAMath::Max(0.f, CAMath::Min((float)param.tpcGeometry.NPads(GPUCA_ROW_COUNT - 1), param.tpcGeometry.LinearY2Pad(slice, row, track.Y()))); + pad = cmprClusters.padResA[clusterOffset - trackIndex - 1] + ClusterNative::packPad(tmpPad); + time = time & 0xFFFFFF; + pad = (uint16_t)pad; + if (pad >= param.tpcGeometry.NPads(row) * ClusterNative::scalePadPacked) { + if (pad >= 0xFFFF - 11968) { // Constant 11968 = (2^15 - MAX_PADS(138) * scalePadPacked(64)) / 2 + pad = 0; + } else { + pad = param.tpcGeometry.NPads(row) * ClusterNative::scalePadPacked - 1; + } } - row += cmprClusters.rowDiffA[clusterOffset - trackIndex - 1]; - if (row >= GPUCA_ROW_COUNT) { - row -= GPUCA_ROW_COUNT; + if (param.continuousMaxTimeBin > 0 && time >= maxTime) { + if (time >= 0xFFFFFF - 544768) { // Constant 544768 = (2^23 - LHCMAXBUNCHES(3564) * MAXORBITS(256) * scaleTimePacked(64) / BCPERTIMEBIN(8)) / 2) + time = 0; + } else { + time = maxTime; + } } } else { - slice = tmpSlice; - row = cmprClusters.rowDiffA[clusterOffset - trackIndex - 1]; + time = cmprClusters.timeA[trackIndex]; + pad = cmprClusters.padA[trackIndex]; } - if (changeLeg && track.Mirror()) { - break; + const auto cluster = decompressTrackStore(cmprClusters, clusterOffset, slice, row, pad, time, args...); + float y = param.tpcGeometry.LinearPad2Y(slice, row, cluster.getPad()); + float z = param.tpcGeometry.LinearTime2Z(slice, cluster.getTime()); + if (clusterIndex == 0) { + zOffset = z; + track.Init(param.tpcGeometry.Row2X(row), y, z - zOffset, param.SliceParam[slice].Alpha, cmprClusters.qPtA[trackIndex], param); } - if (track.Propagate(param.tpcGeometry.Row2X(row), param.SliceParam[slice].Alpha)) { + if (clusterIndex + 1 < cmprClusters.nTrackClusters[trackIndex] && track.Filter(y, z - zOffset, row)) { break; } - uint32_t timeTmp = cmprClusters.timeResA[clusterOffset - trackIndex - 1]; - if (timeTmp & 800000) { - timeTmp |= 0xFF000000; - } - time = timeTmp + ClusterNative::packTime(CAMath::Max(0.f, param.tpcGeometry.LinearZ2Time(slice, track.Z() + zOffset))); - float tmpPad = CAMath::Max(0.f, CAMath::Min((float)param.tpcGeometry.NPads(GPUCA_ROW_COUNT - 1), param.tpcGeometry.LinearY2Pad(slice, row, track.Y()))); - pad = cmprClusters.padResA[clusterOffset - trackIndex - 1] + ClusterNative::packPad(tmpPad); - time = time & 0xFFFFFF; - pad = (uint16_t)pad; - if (pad >= param.tpcGeometry.NPads(row) * ClusterNative::scalePadPacked) { - if (pad >= 0xFFFF - 11968) { // Constant 11968 = (2^15 - MAX_PADS(138) * scalePadPacked(64)) / 2 - pad = 0; - } else { - pad = param.tpcGeometry.NPads(row) * ClusterNative::scalePadPacked - 1; - } - } - if (param.continuousMaxTimeBin > 0 && time >= maxTime) { - if (time >= 0xFFFFFF - 544768) { // Constant 544768 = (2^23 - LHCMAXBUNCHES(3564) * MAXORBITS(256) * scaleTimePacked(64) / BCPERTIMEBIN(8)) / 2) - time = 0; - } else { - time = maxTime; - } - } - } else { - time = cmprClusters.timeA[trackIndex]; - pad = cmprClusters.padA[trackIndex]; + clusterOffset++; } - const auto cluster = decompressTrackStore(cmprClusters, clusterOffset, slice, row, pad, time, args...); - float y = param.tpcGeometry.LinearPad2Y(slice, row, cluster.getPad()); - float z = param.tpcGeometry.LinearTime2Z(slice, cluster.getTime()); - if (clusterIndex == 0) { - zOffset = z; - track.Init(param.tpcGeometry.Row2X(row), y, z - zOffset, param.SliceParam[slice].Alpha, cmprClusters.qPtA[trackIndex], param); - } - if (clusterIndex + 1 < cmprClusters.nTrackClusters[trackIndex] && track.Filter(y, z - zOffset, row)) { - break; - } - clusterOffset++; + clusterOffset += cmprClusters.nTrackClusters[trackIndex] - clusterIndex; } - clusterOffset += cmprClusters.nTrackClusters[trackIndex] - clusterIndex; -} -GPUhdi() static const auto& decompressHitsStore(const CompressedClusters& cmprClusters, uint32_t k, uint32_t time, uint16_t pad, ClusterNative*& clusterBuffer) -{ - return ((*(clusterBuffer++) = ClusterNative(time, cmprClusters.flagsU[k], pad, cmprClusters.sigmaTimeU[k], cmprClusters.sigmaPadU[k], cmprClusters.qMaxU[k], cmprClusters.qTotU[k]))); -} + GPUdi() static const auto& decompressHitsStore(const CompressedClusters& cmprClusters, uint32_t k, uint32_t time, uint16_t pad, ClusterNative*& clusterBuffer) + { + return ((*(clusterBuffer++) = ClusterNative(time, cmprClusters.flagsU[k], pad, cmprClusters.sigmaTimeU[k], cmprClusters.sigmaPadU[k], cmprClusters.qMaxU[k], cmprClusters.qTotU[k]))); + } -GPUhdi() static auto decompressHitsStore(const CompressedClusters& cmprClusters, uint32_t k, uint32_t time, uint16_t pad, std::function func) -{ - const auto cluster = ClusterNative(time, cmprClusters.flagsU[k], pad, cmprClusters.sigmaTimeU[k], cmprClusters.sigmaPadU[k], cmprClusters.qMaxU[k], cmprClusters.qTotU[k]); - func(cluster, k); - return cluster; -} +#ifndef GPUCA_GPUCODE + GPUhi() static auto decompressHitsStore(const CompressedClusters& cmprClusters, uint32_t k, uint32_t time, uint16_t pad, std::function func) + { + const auto cluster = ClusterNative(time, cmprClusters.flagsU[k], pad, cmprClusters.sigmaTimeU[k], cmprClusters.sigmaPadU[k], cmprClusters.qMaxU[k], cmprClusters.qTotU[k]); + func(cluster, k); + return cluster; + } +#endif -template -GPUdi() static void decompressHits(const CompressedClusters& cmprClusters, const uint32_t start, const uint32_t end, Args&... args) -{ - uint32_t time = 0; - uint16_t pad = 0; - for (uint32_t k = start; k < end; k++) { - if (cmprClusters.nComppressionModes & GPUSettings::CompressionDifferences) { - uint32_t timeTmp = cmprClusters.timeDiffU[k]; - if (timeTmp & 800000) { - timeTmp |= 0xFF000000; + template + GPUdi() static void decompressHits(const CompressedClusters& cmprClusters, const uint32_t start, const uint32_t end, Args&... args) + { + uint32_t time = 0; + uint16_t pad = 0; + for (uint32_t k = start; k < end; k++) { + if (cmprClusters.nComppressionModes & GPUSettings::CompressionDifferences) { + uint32_t timeTmp = cmprClusters.timeDiffU[k]; + if (timeTmp & 800000) { + timeTmp |= 0xFF000000; + } + time += timeTmp; + pad += cmprClusters.padDiffU[k]; + } else { + time = cmprClusters.timeDiffU[k]; + pad = cmprClusters.padDiffU[k]; } - time += timeTmp; - pad += cmprClusters.padDiffU[k]; - } else { - time = cmprClusters.timeDiffU[k]; - pad = cmprClusters.padDiffU[k]; + decompressHitsStore(cmprClusters, k, time, pad, args...); } - decompressHitsStore(cmprClusters, k, time, pad, args...); } -} - }; -} +} // namespace GPUCA_NAMESPACE::gpu #endif \ No newline at end of file diff --git a/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.h b/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.h index fc96f5fc72e28..d8e404b8a2ab7 100644 --- a/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.h +++ b/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.h @@ -35,11 +35,6 @@ class TPCClusterDecompressor static constexpr uint32_t NSLICES = GPUCA_NSLICES; static int32_t decompress(const o2::tpc::CompressedClustersFlat* clustersCompressed, o2::tpc::ClusterNativeAccess& clustersNative, std::function allocator, const GPUParam& param, bool deterministicRec); static int32_t decompress(const o2::tpc::CompressedClusters* clustersCompressed, o2::tpc::ClusterNativeAccess& clustersNative, std::function allocator, const GPUParam& param, bool deterministicRec); - - template - static void decompressTrack(const o2::tpc::CompressedClusters* clustersCompressed, const GPUParam& param, const uint32_t maxTime, const uint32_t i, uint32_t& offset, Args&... args); - template - static void decompressHits(const o2::tpc::CompressedClusters* clustersCompressed, const uint32_t start, const uint32_t end, Args&... args); }; } // namespace GPUCA_NAMESPACE::gpu diff --git a/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.inc b/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.inc deleted file mode 100644 index 2ea75b21bf22e..0000000000000 --- a/GPU/GPUTracking/DataCompression/TPCClusterDecompressor.inc +++ /dev/null @@ -1,164 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// \file TPCClusterDecompressor.inc -/// \author David Rohr - -#include "TPCClusterDecompressor.h" -#include "GPUO2DataTypes.h" -#include "GPUParam.h" -#include "GPUTPCCompressionTrackModel.h" -#include -#include -#include -#include - -using namespace GPUCA_NAMESPACE::gpu; -using namespace o2::tpc; - -static inline auto decompressTrackStore(const o2::tpc::CompressedClusters* clustersCompressed, const uint32_t offset, uint32_t slice, uint32_t row, uint32_t pad, uint32_t time, std::function func) -{ - const auto cluster = ClusterNative(time, clustersCompressed->flagsA[offset], pad, clustersCompressed->sigmaTimeA[offset], clustersCompressed->sigmaPadA[offset], clustersCompressed->qMaxA[offset], clustersCompressed->qTotA[offset]); - func(cluster, offset); - return cluster; -} - -static inline const auto& decompressTrackStore(const o2::tpc::CompressedClusters* clustersCompressed, const uint32_t offset, uint32_t slice, uint32_t row, uint32_t pad, uint32_t time, std::vector& clusterVector) -{ - clusterVector.emplace_back(time, clustersCompressed->flagsA[offset], pad, clustersCompressed->sigmaTimeA[offset], clustersCompressed->sigmaPadA[offset], clustersCompressed->qMaxA[offset], clustersCompressed->qTotA[offset]); - return clusterVector.back(); -} - -static inline auto decompressTrackStore(const o2::tpc::CompressedClusters* clustersCompressed, const uint32_t offset, uint32_t slice, uint32_t row, uint32_t pad, uint32_t time, std::vector (&clusters)[GPUCA_NSLICES][GPUCA_ROW_COUNT], std::atomic_flag (&locks)[GPUCA_NSLICES][GPUCA_ROW_COUNT]) -{ - std::vector& clusterVector = clusters[slice][row]; - auto& lock = locks[slice][row]; - while (lock.test_and_set(std::memory_order_acquire)) { - } - // Note the return type is ClusterNative, not auto&, since a different thread might append another cluster, and the vector expansion can change the cluster pointer, so the cluster reference might be invalid - // TODO: A new version that might use a plain array + counter to fill the clusters should change this and the function return type to auto& - ClusterNative retVal = decompressTrackStore(clustersCompressed, offset, slice, row, pad, time, clusterVector); - lock.clear(std::memory_order_release); - return retVal; -} - -template -inline void TPCClusterDecompressor::decompressTrack(const CompressedClusters* clustersCompressed, const GPUParam& param, const uint32_t maxTime, const uint32_t i, uint32_t& offset, Args&... args) -{ - float zOffset = 0; - uint32_t slice = clustersCompressed->sliceA[i]; - uint32_t row = clustersCompressed->rowA[i]; - GPUTPCCompressionTrackModel track; - uint32_t j; - for (j = 0; j < clustersCompressed->nTrackClusters[i]; j++) { - uint32_t pad = 0, time = 0; - if (j) { - uint8_t tmpSlice = clustersCompressed->sliceLegDiffA[offset - i - 1]; - bool changeLeg = (tmpSlice >= NSLICES); - if (changeLeg) { - tmpSlice -= NSLICES; - } - if (clustersCompressed->nComppressionModes & GPUSettings::CompressionDifferences) { - slice += tmpSlice; - if (slice >= NSLICES) { - slice -= NSLICES; - } - row += clustersCompressed->rowDiffA[offset - i - 1]; - if (row >= GPUCA_ROW_COUNT) { - row -= GPUCA_ROW_COUNT; - } - } else { - slice = tmpSlice; - row = clustersCompressed->rowDiffA[offset - i - 1]; - } - if (changeLeg && track.Mirror()) { - break; - } - if (track.Propagate(param.tpcGeometry.Row2X(row), param.SliceParam[slice].Alpha)) { - break; - } - uint32_t timeTmp = clustersCompressed->timeResA[offset - i - 1]; - if (timeTmp & 800000) { - timeTmp |= 0xFF000000; - } - time = timeTmp + ClusterNative::packTime(CAMath::Max(0.f, param.tpcGeometry.LinearZ2Time(slice, track.Z() + zOffset))); - float tmpPad = CAMath::Max(0.f, CAMath::Min((float)param.tpcGeometry.NPads(GPUCA_ROW_COUNT - 1), param.tpcGeometry.LinearY2Pad(slice, row, track.Y()))); - pad = clustersCompressed->padResA[offset - i - 1] + ClusterNative::packPad(tmpPad); - time = time & 0xFFFFFF; - pad = (uint16_t)pad; - if (pad >= param.tpcGeometry.NPads(row) * ClusterNative::scalePadPacked) { - if (pad >= 0xFFFF - 11968) { // Constant 11968 = (2^15 - MAX_PADS(138) * scalePadPacked(64)) / 2 - pad = 0; - } else { - pad = param.tpcGeometry.NPads(row) * ClusterNative::scalePadPacked - 1; - } - } - if (param.continuousMaxTimeBin > 0 && time >= maxTime) { - if (time >= 0xFFFFFF - 544768) { // Constant 544768 = (2^23 - LHCMAXBUNCHES(3564) * MAXORBITS(256) * scaleTimePacked(64) / BCPERTIMEBIN(8)) / 2) - time = 0; - } else { - time = maxTime; - } - } - } else { - time = clustersCompressed->timeA[i]; - pad = clustersCompressed->padA[i]; - } - const auto& cluster = decompressTrackStore(clustersCompressed, offset, slice, row, pad, time, args...); - float y = param.tpcGeometry.LinearPad2Y(slice, row, cluster.getPad()); - float z = param.tpcGeometry.LinearTime2Z(slice, cluster.getTime()); - if (j == 0) { - zOffset = z; - track.Init(param.tpcGeometry.Row2X(row), y, z - zOffset, param.SliceParam[slice].Alpha, clustersCompressed->qPtA[i], param); - } - if (j + 1 < clustersCompressed->nTrackClusters[i] && track.Filter(y, z - zOffset, row)) { - break; - } - offset++; - } - offset += clustersCompressed->nTrackClusters[i] - j; -} - -static inline const auto& decompressHitsStore(const CompressedClusters* clustersCompressed, uint32_t k, uint32_t time, uint16_t pad, ClusterNative*& cl) -{ - return ((*(cl++) = ClusterNative(time, clustersCompressed->flagsU[k], pad, clustersCompressed->sigmaTimeU[k], clustersCompressed->sigmaPadU[k], clustersCompressed->qMaxU[k], clustersCompressed->qTotU[k]))); -} - -static inline auto decompressHitsStore(const CompressedClusters* clustersCompressed, uint32_t k, uint32_t time, uint16_t pad, std::function func) -{ - const auto cluster = ClusterNative(time, clustersCompressed->flagsU[k], pad, clustersCompressed->sigmaTimeU[k], clustersCompressed->sigmaPadU[k], clustersCompressed->qMaxU[k], clustersCompressed->qTotU[k]); - func(cluster, k); - return cluster; -} - -template -inline void TPCClusterDecompressor::decompressHits(const CompressedClusters* clustersCompressed, const uint32_t start, const uint32_t end, Args&... args) -{ - uint32_t time = 0; - uint16_t pad = 0; - for (uint32_t k = start; k < end; k++) { - /*if (cl >= clustersNative.clustersLinear + nTotalClusters) { - throw std::runtime_error("Bad TPC CTF data, decoded more clusters than announced"); - }*/ - if (clustersCompressed->nComppressionModes & GPUSettings::CompressionDifferences) { - uint32_t timeTmp = clustersCompressed->timeDiffU[k]; - if (timeTmp & 800000) { - timeTmp |= 0xFF000000; - } - time += timeTmp; - pad += clustersCompressed->padDiffU[k]; - } else { - time = clustersCompressed->timeDiffU[k]; - pad = clustersCompressed->padDiffU[k]; - } - decompressHitsStore(clustersCompressed, k, time, pad, args...); - } -} From 68d83608c15f2e90a80cc6f8d2aebbdee4bb37f7 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sat, 16 Nov 2024 13:16:36 +0100 Subject: [PATCH 0062/2180] GPU Clusterizer: Fix should not release an unused event --- GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx index af7cc03369afc..dca656e738ef8 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx @@ -865,8 +865,12 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) } if (fragment.index == 0) { - runKernel({GetGridAutoStep(lane, RecoStep::TPCClusterFinding), krnlRunRangeNone, {nullptr, transferRunning[lane] == 1 ? &mEvents->stream[lane] : nullptr}}, clustererShadow.mPclusterInRow, GPUCA_ROW_COUNT * sizeof(*clustererShadow.mPclusterInRow)); - transferRunning[lane] = 2; + deviceEvent* waitEvent = nullptr; + if (transferRunning[lane] == 1) { + waitEvent = &mEvents->stream[lane]; + transferRunning[lane] = 2; + } + runKernel({GetGridAutoStep(lane, RecoStep::TPCClusterFinding), krnlRunRangeNone, {nullptr, waitEvent}}, clustererShadow.mPclusterInRow, GPUCA_ROW_COUNT * sizeof(*clustererShadow.mPclusterInRow)); } if (clusterer.mPmemory->counters.nClusters == 0) { From 5b55339f1ed573bdbf60bf43219d335d2ded054a Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sat, 16 Nov 2024 13:17:13 +0100 Subject: [PATCH 0063/2180] GPU: Fix RecordMarker must operate on reference, since OpenCL will change the event pointer --- GPU/GPUTracking/Base/GPUReconstructionCPU.h | 2 +- GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.cu | 2 +- GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.h | 2 +- .../Base/opencl-common/GPUReconstructionOCL.cxx | 7 ++++++- GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.h | 2 +- GPU/GPUTracking/Global/GPUChain.h | 2 +- GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx | 2 +- GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx | 4 ++-- GPU/GPUTracking/Global/GPUChainTrackingMerger.cxx | 8 ++++---- GPU/GPUTracking/Global/GPUChainTrackingSliceTracker.cxx | 2 +- 10 files changed, 19 insertions(+), 14 deletions(-) diff --git a/GPU/GPUTracking/Base/GPUReconstructionCPU.h b/GPU/GPUTracking/Base/GPUReconstructionCPU.h index ac254221c250c..7eaf3e4a5e40d 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionCPU.h +++ b/GPU/GPUTracking/Base/GPUReconstructionCPU.h @@ -114,7 +114,7 @@ class GPUReconstructionCPU : public GPUReconstructionKernels(), mInternals->Streams[stream])); } +void GPUReconstructionCUDA::RecordMarker(deviceEvent* ev, int32_t stream) { GPUFailedMsg(cudaEventRecord(ev->get(), mInternals->Streams[stream])); } std::unique_ptr GPUReconstructionCUDA::GetThreadContext() { diff --git a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.h b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.h index b9db625a83f1d..070177fb344f1 100644 --- a/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.h +++ b/GPU/GPUTracking/Base/cuda/GPUReconstructionCUDA.h @@ -84,7 +84,7 @@ class GPUReconstructionCUDA : public GPUReconstructionKernels* trackerTraits, std::unique_ptr* vertexerTraits, std::unique_ptr* timeFrame) override; diff --git a/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.cxx b/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.cxx index de32f03340c03..cad56e77c79d5 100644 --- a/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.cxx +++ b/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.cxx @@ -359,6 +359,11 @@ int32_t GPUReconstructionOCL::InitDevice_Runtime() mInternals = master->mInternals; } + for (uint32_t i = 0; i < mEvents.size(); i++) { + cl_event* events = (cl_event*)mEvents[i].data(); + new (events) cl_event[mEvents[i].size()]; + } + return (0); } @@ -432,7 +437,7 @@ size_t GPUReconstructionOCL::WriteToConstantMemory(size_t offset, const void* sr void GPUReconstructionOCL::ReleaseEvent(deviceEvent ev) { GPUFailedMsg(clReleaseEvent(ev.get())); } -void GPUReconstructionOCL::RecordMarker(deviceEvent ev, int32_t stream) { GPUFailedMsg(clEnqueueMarkerWithWaitList(mInternals->command_queue[stream], 0, nullptr, ev.getEventList())); } +void GPUReconstructionOCL::RecordMarker(deviceEvent* ev, int32_t stream) { GPUFailedMsg(clEnqueueMarkerWithWaitList(mInternals->command_queue[stream], 0, nullptr, ev->getEventList())); } int32_t GPUReconstructionOCL::DoStuckProtection(int32_t stream, deviceEvent event) { diff --git a/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.h b/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.h index 02ba0469dee2a..6abe1045b550a 100644 --- a/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.h +++ b/GPU/GPUTracking/Base/opencl-common/GPUReconstructionOCL.h @@ -52,7 +52,7 @@ class GPUReconstructionOCL : public GPUReconstructionDeviceBase size_t WriteToConstantMemory(size_t offset, const void* src, size_t size, int32_t stream = -1, deviceEvent* ev = nullptr) override; size_t GPUMemCpy(void* dst, const void* src, size_t size, int32_t stream, int32_t toGPU, deviceEvent* ev = nullptr, deviceEvent* evList = nullptr, int32_t nEvents = 1) override; void ReleaseEvent(deviceEvent ev) override; - void RecordMarker(deviceEvent ev, int32_t stream) override; + void RecordMarker(deviceEvent* ev, int32_t stream) override; virtual int32_t GetOCLPrograms() = 0; virtual bool CheckPlatform(uint32_t i) = 0; diff --git a/GPU/GPUTracking/Global/GPUChain.h b/GPU/GPUTracking/Global/GPUChain.h index 7a36355bf843d..9c67a05eec443 100644 --- a/GPU/GPUTracking/Global/GPUChain.h +++ b/GPU/GPUTracking/Global/GPUChain.h @@ -101,7 +101,7 @@ class GPUChain } } inline bool IsEventDone(deviceEvent* evList, int32_t nEvents = 1) { return mRec->IsEventDone(evList, nEvents); } - inline void RecordMarker(deviceEvent ev, int32_t stream) { mRec->RecordMarker(ev, stream); } + inline void RecordMarker(deviceEvent* ev, int32_t stream) { mRec->RecordMarker(ev, stream); } virtual inline std::unique_ptr GetThreadContext() { return mRec->GetThreadContext(); } inline void SynchronizeGPU() { mRec->SynchronizeGPU(); } inline void ReleaseEvent(deviceEvent ev, bool doGPU = true) diff --git a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx index dca656e738ef8..ae240181eba65 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx @@ -934,7 +934,7 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) if (transferRunning[lane]) { ReleaseEvent(mEvents->stream[lane], doGPU); } - RecordMarker(mEvents->stream[lane], mRec->NStreams() - 1); + RecordMarker(&mEvents->stream[lane], mRec->NStreams() - 1); transferRunning[lane] = 1; } diff --git a/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx b/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx index c0679c090c20c..98109447de034 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx @@ -37,7 +37,7 @@ int32_t GPUChainTracking::RunTPCCompression() GPUTPCCompression& CompressorShadow = doGPU ? processorsShadow()->tpcCompressor : Compressor; const auto& threadContext = GetThreadContext(); if (mPipelineFinalizationCtx && GetProcessingSettings().doublePipelineClusterizer) { - RecordMarker(mEvents->single, 0); + RecordMarker(&mEvents->single, 0); } if (GetProcessingSettings().tpcCompressionGatherMode == 3) { @@ -124,7 +124,7 @@ int32_t GPUChainTracking::RunTPCCompression() return 1; } if (GetProcessingSettings().tpcCompressionGatherMode == 3) { - RecordMarker(mEvents->stream[outputStream], outputStream); + RecordMarker(&mEvents->stream[outputStream], outputStream); char* deviceFlatPts = (char*)Compressor.mOutput->qTotU; if (GetProcessingSettings().doublePipeline) { const size_t blockSize = CAMath::nextMultipleOf<1024>(copySize / 30); diff --git a/GPU/GPUTracking/Global/GPUChainTrackingMerger.cxx b/GPU/GPUTracking/Global/GPUChainTrackingMerger.cxx index 67a9904a4222f..aba8617ee244d 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingMerger.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingMerger.cxx @@ -33,7 +33,7 @@ void GPUChainTracking::RunTPCTrackingMerger_MergeBorderTracks(int8_t withinSlice uint32_t n = withinSlice == -1 ? NSLICES / 2 : NSLICES; if (GetProcessingSettings().alternateBorderSort && (!mRec->IsGPU() || doGPUall)) { TransferMemoryResourceLinkToHost(RecoStep::TPCMerging, Merger.MemoryResMemory(), 0, &mEvents->init); - RecordMarker(mEvents->single, 0); + RecordMarker(&mEvents->single, 0); for (uint32_t i = 0; i < n; i++) { int32_t stream = i % mRec->NStreams(); runKernel({GetGridAuto(stream, deviceType), krnlRunRangeNone, {nullptr, stream && i < (uint32_t)mRec->NStreams() ? &mEvents->single : nullptr}}, i, withinSlice, mergeMode); @@ -55,7 +55,7 @@ void GPUChainTracking::RunTPCTrackingMerger_MergeBorderTracks(int8_t withinSlice if (i == n - 1) { // Synchronize all execution on stream 0 with the last kernel ne = std::min(n, mRec->NStreams()); for (int32_t j = 1; j < ne; j++) { - RecordMarker(mEvents->slice[j], j); + RecordMarker(&mEvents->slice[j], j); } e = &mEvents->slice[1]; ne--; @@ -251,7 +251,7 @@ int32_t GPUChainTracking::RunTPCTrackingMerger(bool synchronizeOutput) DoDebugAndDump(RecoStep::TPCMerging, 2048, doGPUall, Merger, &GPUTPCGMMerger::DumpFinal, *mDebugFile); if (doGPUall) { - RecordMarker(mEvents->single, 0); + RecordMarker(&mEvents->single, 0); auto* waitEvent = &mEvents->single; if (GetProcessingSettings().keepDisplayMemory || GetProcessingSettings().createO2Output <= 1 || mFractionalQAEnabled) { if (!(GetProcessingSettings().keepDisplayMemory || GetProcessingSettings().createO2Output <= 1)) { @@ -317,7 +317,7 @@ int32_t GPUChainTracking::RunTPCTrackingMerger(bool synchronizeOutput) TransferMemoryResourcesToHost(RecoStep::TPCMerging, &Merger, -1, true); runKernel(GetGridAuto(0, GPUReconstruction::krnlDeviceType::CPU)); } else if (doGPUall) { - RecordMarker(mEvents->single, 0); + RecordMarker(&mEvents->single, 0); TransferMemoryResourceLinkToHost(RecoStep::TPCMerging, Merger.MemoryResOutputO2(), outputStream, nullptr, &mEvents->single); TransferMemoryResourceLinkToHost(RecoStep::TPCMerging, Merger.MemoryResOutputO2Clus(), outputStream); ReleaseEvent(mEvents->single); diff --git a/GPU/GPUTracking/Global/GPUChainTrackingSliceTracker.cxx b/GPU/GPUTracking/Global/GPUChainTrackingSliceTracker.cxx index da4629928789c..c34c01f1e6593 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingSliceTracker.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingSliceTracker.cxx @@ -305,7 +305,7 @@ int32_t GPUChainTracking::RunTPCTrackingSlices_internal() SynchronizeGPU(); } else { for (int32_t i = 0; i < mRec->NStreams(); i++) { - RecordMarker(mEvents->stream[i], i); + RecordMarker(&mEvents->stream[i], i); } runKernel({GetGridAuto(0), krnlRunRangeNone, {&mEvents->single, mEvents->stream, mRec->NStreams()}}); for (int32_t i = 0; i < mRec->NStreams(); i++) { From dce7d4a665d1a06113d7d9c1639704854ec7923a Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sat, 16 Nov 2024 13:40:57 +0100 Subject: [PATCH 0064/2180] GPU OpenCL: Add .oclCompileFromSources option to force OpenCL compilation from sources --- GPU/GPUTracking/Base/opencl2/GPUReconstructionOCL2.cxx | 6 +++--- GPU/GPUTracking/Definitions/GPUSettingsList.h | 3 ++- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/GPU/GPUTracking/Base/opencl2/GPUReconstructionOCL2.cxx b/GPU/GPUTracking/Base/opencl2/GPUReconstructionOCL2.cxx index a118e6d589712..435e69e91f5fe 100644 --- a/GPU/GPUTracking/Base/opencl2/GPUReconstructionOCL2.cxx +++ b/GPU/GPUTracking/Base/opencl2/GPUReconstructionOCL2.cxx @@ -64,14 +64,14 @@ int32_t GPUReconstructionOCL2Backend::GetOCLPrograms() const char* ocl_flags = GPUCA_M_STR(OCL_FLAGS); #ifdef OPENCL2_ENABLED_SPIRV // clang-format off - if (ver >= 2.2f) { - GPUInfo("Reading OpenCL program from SPIR-V IL (Platform version %f)", ver); + if (ver >= 2.2f && !GetProcessingSettings().oclCompileFromSources) { + GPUInfo("Reading OpenCL program from SPIR-V IL (Platform version %4.2f)", ver); mInternals->program = clCreateProgramWithIL(mInternals->context, _binary_GPUReconstructionOCL2Code_spirv_start, _binary_GPUReconstructionOCL2Code_spirv_len, &ocl_error); ocl_flags = ""; } else #endif // clang-format on { - GPUInfo("Compiling OpenCL program from sources (Platform version %f, %s)", ver); + GPUInfo("Compiling OpenCL program from sources (Platform version %4.2f)", ver); size_t program_sizes[1] = {_binary_GPUReconstructionOCL2Code_src_len}; char* programs_sources[1] = {_binary_GPUReconstructionOCL2Code_src_start}; mInternals->program = clCreateProgramWithSource(mInternals->context, (cl_uint)1, (const char**)&programs_sources, program_sizes, &ocl_error); diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index c4e0dadb87659..d5494d04930f5 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -218,7 +218,7 @@ AddHelp("help", 'h') EndConfig() BeginSubConfig(GPUSettingsProcessing, proc, configStandalone, "PROC", 0, "Processing settings", proc) -AddOption(platformNum, int32_t, -1, "", 0, "Platform to use, in case the backend provides multiple platforms (-1 = auto-select)") +AddOption(platformNum, int32_t, -1, "", 0, "Platform to use, in case the backend provides multiple platforms (OpenCL only, -1 = auto-select)") 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") @@ -291,6 +291,7 @@ AddOption(tpcApplyDebugClusterFilter, bool, false, "", 0, "Apply custom cluster AddOption(RTCcacheFolder, std::string, "./rtccache/", "", 0, "Folder in which the cache file is stored") AddOption(RTCprependCommand, std::string, "", "", 0, "Prepend RTC compilation commands by this string") AddOption(RTCoverrideArchitecture, std::string, "", "", 0, "Override arhcitecture part of RTC compilation command line") +AddOption(oclCompileFromSources, bool, false, "", 0, "Compile OpenCL binary from included source code instead of using included spirv code") AddOption(printSettings, bool, false, "", 0, "Print all settings when initializing") AddVariable(eventDisplay, GPUCA_NAMESPACE::gpu::GPUDisplayFrontendInterface*, nullptr) AddSubConfig(GPUSettingsProcessingRTC, rtc) From 9a1148718de8207fbbb3c0a31d86bd88d1670289 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sat, 16 Nov 2024 13:41:23 +0100 Subject: [PATCH 0065/2180] GPU OpenCL: Improve compiler command line arguments --- GPU/Common/GPUCommonMath.h | 2 +- GPU/GPUTracking/Base/opencl2/CMakeLists.txt | 8 ++++++-- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/GPU/Common/GPUCommonMath.h b/GPU/Common/GPUCommonMath.h index bc842d00c6568..0e5db743d0c57 100644 --- a/GPU/Common/GPUCommonMath.h +++ b/GPU/Common/GPUCommonMath.h @@ -399,7 +399,7 @@ GPUdi() T GPUCommonMath::MaxWithRef(T x, T y, T z, T w, S refX, S refY, S refZ, GPUdi() float GPUCommonMath::InvSqrt(float _x) { -#ifdef GPUCA_NO_FAST_MATH +#if defined(GPUCA_NO_FAST_MATH) || defined(__OPENCL__) return 1.f / Sqrt(_x); #elif defined(__CUDACC__) || defined(__HIPCC__) return __frsqrt_rn(_x); diff --git a/GPU/GPUTracking/Base/opencl2/CMakeLists.txt b/GPU/GPUTracking/Base/opencl2/CMakeLists.txt index 73062ad82f728..0e6b9b8d0123d 100644 --- a/GPU/GPUTracking/Base/opencl2/CMakeLists.txt +++ b/GPU/GPUTracking/Base/opencl2/CMakeLists.txt @@ -23,9 +23,11 @@ endif() set(CL_SRC ${GPUDIR}/Base/opencl-common/GPUReconstructionOCL.cl) set(CL_BIN ${CMAKE_CURRENT_BINARY_DIR}/GPUReconstructionOCL2Code) -set(OCL_FLAGS -ferror-limit=1000 -Dcl_clang_storage_class_specifiers -Wno-invalid-constexpr -Wno-unused-command-line-argument -cl-std=CLC++2021) +set(OCL_FLAGS -Dcl_clang_storage_class_specifiers -cl-std=CLC++2021) if(NOT DEFINED GPUCA_NO_FAST_MATH OR NOT ${GPUCA_NO_FAST_MATH}) - set(OCL_FLAGS ${OCL_FLAGS} -Xclang -fdenormal-fp-math-f32=ieee -cl-mad-enable -cl-no-signed-zeros) + set(OCL_FLAGS ${OCL_FLAGS} -cl-denorms-are-zero -cl-mad-enable -cl-no-signed-zeros -cl-fast-relaxed-math) +else() +set(OCL_FLAGS ${OCL_FLAGS} -cl-fp32-correctly-rounded-divide-sqrt) endif() set(OCL_DEFINECL "-D$,$-D>" "-I$,EXCLUDE,^/usr/include/?>,$-I>" @@ -47,6 +49,7 @@ if(OPENCL2_ENABLED_SPIRV) # BUILD OpenCL2 intermediate code for SPIR-V target -O0 --target=spirv64 -fno-integrated-objemitter + -ferror-limit=1000 -Wno-invalid-constexpr -Wno-unused-command-line-argument ${OCL_FLAGS} ${OCL_DEFINECL} -o ${CL_BIN}.spirv -c ${CL_SRC} @@ -64,6 +67,7 @@ if(OPENCL2_ENABLED) # BUILD OpenCL2 source code for runtime compilation target add_custom_command( OUTPUT ${CL_BIN}.src COMMAND ${LLVM_CLANG} + -Wno-unused-command-line-argument ${OCL_FLAGS} ${OCL_DEFINECL} -cl-no-stdinc From 0c01d1b7ab0bf8eb4bf7a8a36480847ada46c763 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sat, 16 Nov 2024 17:58:25 +0100 Subject: [PATCH 0066/2180] GPU TPC: Fix segfault when TPC occupancy map is not requested --- GPU/GPUTracking/Global/GPUChainTrackingSliceTracker.cxx | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/GPU/GPUTracking/Global/GPUChainTrackingSliceTracker.cxx b/GPU/GPUTracking/Global/GPUChainTrackingSliceTracker.cxx index c34c01f1e6593..8db15fb1aef7e 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingSliceTracker.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingSliceTracker.cxx @@ -164,9 +164,11 @@ int32_t GPUChainTracking::RunTPCTrackingSlices_internal() TransferMemoryResourceLinkToGPU(RecoStep::TPCSliceTracking, mInputsHost->mResourceOccupancyMap, streamOccMap, &mEvents->init); } } - uint32_t& occupancyTotal = *mInputsHost->mTPCClusterOccupancyMap; - occupancyTotal = CAMath::Float2UIntRn(mRec->MemoryScalers()->nTPCHits / (mIOPtrs.settingsTF && mIOPtrs.settingsTF->hasNHBFPerTF ? mIOPtrs.settingsTF->nHBFPerTF : 128)); - mRec->UpdateParamOccupancyMap(param().rec.tpc.occupancyMapTimeBins ? mInputsHost->mTPCClusterOccupancyMap + 2 : nullptr, param().rec.tpc.occupancyMapTimeBins ? mInputsShadow->mTPCClusterOccupancyMap + 2 : nullptr, occupancyTotal, streamOccMap); + if (param().rec.tpc.occupancyMapTimeBins || param().rec.tpc.sysClusErrorC12Norm) { + uint32_t& occupancyTotal = *mInputsHost->mTPCClusterOccupancyMap; + occupancyTotal = CAMath::Float2UIntRn(mRec->MemoryScalers()->nTPCHits / (mIOPtrs.settingsTF && mIOPtrs.settingsTF->hasNHBFPerTF ? mIOPtrs.settingsTF->nHBFPerTF : 128)); + mRec->UpdateParamOccupancyMap(param().rec.tpc.occupancyMapTimeBins ? mInputsHost->mTPCClusterOccupancyMap + 2 : nullptr, param().rec.tpc.occupancyMapTimeBins ? mInputsShadow->mTPCClusterOccupancyMap + 2 : nullptr, occupancyTotal, streamOccMap); + } int32_t streamMap[NSLICES]; From 901995584e0a1cff9a80d853b1e7b65f399d5fa2 Mon Sep 17 00:00:00 2001 From: shahoian Date: Mon, 18 Nov 2024 02:33:42 +0100 Subject: [PATCH 0067/2180] extend opt. max-tf-per-file to raw-tf reader, move it to device --- Detectors/CTF/workflow/src/CTFReaderSpec.cxx | 7 ++++++ .../CTF/workflow/src/ctf-reader-workflow.cxx | 7 ------ Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx | 23 +++++++++++++++---- Detectors/Raw/TFReaderDD/src/TFReaderSpec.h | 1 + .../Raw/TFReaderDD/src/tf-reader-workflow.cxx | 8 ------- 5 files changed, 26 insertions(+), 20 deletions(-) diff --git a/Detectors/CTF/workflow/src/CTFReaderSpec.cxx b/Detectors/CTF/workflow/src/CTFReaderSpec.cxx index 9b16e65c3a2b7..70bb589e8836a 100644 --- a/Detectors/CTF/workflow/src/CTFReaderSpec.cxx +++ b/Detectors/CTF/workflow/src/CTFReaderSpec.cxx @@ -148,6 +148,10 @@ void CTFReaderSpec::init(InitContext& ic) mUseLocalTFCounter = ic.options().get("local-tf-counter"); mImposeRunStartMS = ic.options().get("impose-run-start-timstamp"); mInput.checkTFLimitBeforeReading = ic.options().get("limit-tf-before-reading"); + mInput.maxTFs = ic.options().get("max-tf"); + mInput.maxTFs = mInput.maxTFs > 0 ? mInput.maxTFs : 0x7fffffff; + mInput.maxTFsPerFile = ic.options().get("max-tf-per-file"); + mInput.maxTFsPerFile = mInput.maxTFsPerFile > 0 ? mInput.maxTFsPerFile : 0x7fffffff; mRunning = true; mFileFetcher = std::make_unique(mInput.inpdata, mInput.tffileRegex, mInput.remoteRegex, mInput.copyCmd); mFileFetcher->setMaxFilesInQueue(mInput.maxFileCache); @@ -474,6 +478,9 @@ DataProcessorSpec getCTFReaderSpec(const CTFReaderInp& inp) options.emplace_back(ConfigParamSpec{"local-tf-counter", VariantType::Bool, false, {"reassign header.tfCounter from local TF counter"}}); options.emplace_back(ConfigParamSpec{"fetch-failure-threshold", VariantType::Float, 0.f, {"Fail if too many failures( >0: fraction, <0: abs number, 0: no threshold)"}}); options.emplace_back(ConfigParamSpec{"limit-tf-before-reading", VariantType::Bool, false, {"Check TF limiting before reading new TF, otherwhise before injecting it"}}); + options.emplace_back(ConfigParamSpec{"max-tf", VariantType::Int, -1, {"max CTFs to process (<= 0 : infinite)"}}); + options.emplace_back(ConfigParamSpec{"max-tf-per-file", VariantType::Int, -1, {"max TFs to process per ctf file (<= 0 : infinite)"}}); + if (!inp.metricChannel.empty()) { options.emplace_back(ConfigParamSpec{"channel-config", VariantType::String, inp.metricChannel, {"Out-of-band channel config for TF throttling"}}); } diff --git a/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx b/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx index a12c9c10f9dd8..90d259f4e3a5c 100644 --- a/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx +++ b/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx @@ -54,8 +54,6 @@ void customize(std::vector& workflowOptions) options.push_back(ConfigParamSpec{"ctf-input", VariantType::String, "none", {"comma-separated list CTF input files"}}); options.push_back(ConfigParamSpec{"onlyDet", VariantType::String, std::string{DetID::ALL}, {"comma-separated list of detectors to accept. Overrides skipDet"}}); options.push_back(ConfigParamSpec{"skipDet", VariantType::String, std::string{DetID::NONE}, {"comma-separate list of detectors to skip"}}); - options.push_back(ConfigParamSpec{"max-tf", VariantType::Int, -1, {"max CTFs to process (<= 0 : infinite)"}}); - options.push_back(ConfigParamSpec{"max-tf-per-file", VariantType::Int, -1, {"max TFs to process per ctf file (<= 0 : infinite)"}}); options.push_back(ConfigParamSpec{"loop", VariantType::Int, 0, {"loop N times (infinite for N<0)"}}); options.push_back(ConfigParamSpec{"delay", VariantType::Float, 0.f, {"delay in seconds between consecutive TFs sending"}}); options.push_back(ConfigParamSpec{"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 @@ -117,11 +115,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) if (ctfInput.delay_us < 0) { ctfInput.delay_us = 0; } - int n = configcontext.options().get("max-tf"); - ctfInput.maxTFs = n > 0 ? n : 0x7fffffff; - - n = configcontext.options().get("max-tf-per-file"); - ctfInput.maxTFsPerFile = n > 0 ? n : 0x7fffffff; ctfInput.maxFileCache = std::max(1, configcontext.options().get("max-cached-files")); diff --git a/Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx b/Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx index 594d26b5682c6..58a2a775537d4 100644 --- a/Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx +++ b/Detectors/Raw/TFReaderDD/src/TFReaderSpec.cxx @@ -99,6 +99,12 @@ TFReaderSpec::TFReaderSpec(const TFReaderInp& rinp) : mInput(rinp) void TFReaderSpec::init(o2f::InitContext& ic) { mInput.tfIDs = o2::RangeTokenizer::tokenize(ic.options().get("select-tf-ids")); + mInput.maxTFs = ic.options().get("max-tf"); + mInput.maxTFs = mInput.maxTFs > 0 ? mInput.maxTFs : 0x7fffffff; + mInput.maxTFsPerFile = ic.options().get("max-tf-per-file"); + mInput.maxTFsPerFile = mInput.maxTFsPerFile > 0 ? mInput.maxTFsPerFile : 0x7fffffff; + mInput.maxTFCache = std::max(1, ic.options().get("max-cached-tf")); + mInput.maxFileCache = std::max(1, ic.options().get("max-cached-files")); mFileFetcher = std::make_unique(mInput.inpdata, mInput.tffileRegex, mInput.remoteRegex, mInput.copyCmd); mFileFetcher->setMaxFilesInQueue(mInput.maxFileCache); mFileFetcher->setMaxLoops(mInput.maxLoops); @@ -417,15 +423,17 @@ void TFReaderSpec::TFBuilder() } mTFBuilderCounter++; } - if (!acceptTF) { - continue; - } if (mRunning && tf) { - mWaitSendingLast = true; - mTFQueue.push(std::move(tf)); + if (acceptTF) { + mWaitSendingLast = true; + mTFQueue.push(std::move(tf)); + } } else { break; } + if (mInput.maxTFsPerFile > 0 && mInput.maxTFsPerFile >= locID) { // go to next file + break; + } } // remove already processed file from the queue, unless they are needed for further looping if (mFileFetcher) { @@ -527,6 +535,11 @@ o2f::DataProcessorSpec o2::rawdd::getTFReaderSpec(o2::rawdd::TFReaderInp& rinp) } spec.options.emplace_back(o2f::ConfigParamSpec{"select-tf-ids", o2f::VariantType::String, "", {"comma-separated list TF IDs to inject (from cumulative counter of TFs seen)"}}); spec.options.emplace_back(o2f::ConfigParamSpec{"fetch-failure-threshold", o2f::VariantType::Float, 0.f, {"Fatil if too many failures( >0: fraction, <0: abs number, 0: no threshold)"}}); + spec.options.emplace_back(o2f::ConfigParamSpec{"max-tf", o2f::VariantType::Int, -1, {"max TF ID to process (<= 0 : infinite)"}}); + spec.options.emplace_back(o2f::ConfigParamSpec{"max-tf-per-file", o2f::VariantType::Int, -1, {"max TFs to process per raw-tf file (<= 0 : infinite)"}}); + spec.options.emplace_back(o2f::ConfigParamSpec{"max-cached-tf", o2f::VariantType::Int, 3, {"max TFs to cache in memory"}}); + spec.options.emplace_back(o2f::ConfigParamSpec{"max-cached-files", o2f::VariantType::Int, 3, {"max TF files queued (copied for remote source)"}}); + spec.algorithm = o2f::adaptFromTask(rinp); return spec; diff --git a/Detectors/Raw/TFReaderDD/src/TFReaderSpec.h b/Detectors/Raw/TFReaderDD/src/TFReaderSpec.h index b4bb07fad24be..e3a5b5c920010 100644 --- a/Detectors/Raw/TFReaderDD/src/TFReaderSpec.h +++ b/Detectors/Raw/TFReaderDD/src/TFReaderSpec.h @@ -43,6 +43,7 @@ struct TFReaderInp { int64_t delay_us = 0; int maxLoops = 0; int maxTFs = -1; + int maxTFsPerFile = -1; bool sendDummyForMissing = true; bool sup0xccdb = false; std::vector hdVec; diff --git a/Detectors/Raw/TFReaderDD/src/tf-reader-workflow.cxx b/Detectors/Raw/TFReaderDD/src/tf-reader-workflow.cxx index c468d1660fcc7..7d8ee09fe474f 100644 --- a/Detectors/Raw/TFReaderDD/src/tf-reader-workflow.cxx +++ b/Detectors/Raw/TFReaderDD/src/tf-reader-workflow.cxx @@ -28,20 +28,16 @@ void customize(std::vector& workflowOptions) options.push_back(ConfigParamSpec{"onlyDet", VariantType::String, "all", {"list of dectors"}}); options.push_back(ConfigParamSpec{"raw-only-det", VariantType::String, "none", {"do not open non-raw channel for these detectors"}}); options.push_back(ConfigParamSpec{"non-raw-only-det", VariantType::String, "none", {"do not open raw channel for these detectors"}}); - options.push_back(ConfigParamSpec{"max-tf", VariantType::Int, -1, {"max TF ID to process (<= 0 : infinite)"}}); 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{"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{"max-cached-tf", VariantType::Int, 3, {"max TFs to cache in memory"}}); - options.push_back(ConfigParamSpec{"max-cached-files", VariantType::Int, 3, {"max TF files queued (copied for remote source)"}}); 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)"}}); options.push_back(ConfigParamSpec{"raw-channel-config", VariantType::String, "", {"optional raw FMQ channel for non-DPL output"}}); options.push_back(ConfigParamSpec{"send-diststf-0xccdb", VariantType::Bool, false, {"send explicit FLP/DISTSUBTIMEFRAME/0xccdb output"}}); options.push_back(ConfigParamSpec{"disable-dummy-output", VariantType::Bool, false, {"Disable sending empty output if corresponding data is not found in the data"}}); options.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {"semicolon separated key=value strings"}}); - options.push_back(ConfigParamSpec{"timeframes-shm-limit", VariantType::String, "0", {"Minimum amount of SHM required in order to publish data"}}); options.push_back(ConfigParamSpec{"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"}}); @@ -59,8 +55,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) o2::rawdd::TFReaderInp rinp; rinp.inpdata = configcontext.options().get("input-data"); rinp.maxLoops = configcontext.options().get("loop"); - int n = configcontext.options().get("max-tf"); - rinp.maxTFs = n > 0 ? n : 0x7fffffff; auto detlistSelect = configcontext.options().get("onlyDet"); if (detlistSelect == "all") { // Exclude FOCAL from default detlist (must be selected on request) @@ -74,8 +68,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) rinp.rawChannelConfig = configcontext.options().get("raw-channel-config"); rinp.delay_us = uint64_t(1e6 * configcontext.options().get("delay")); // delay in microseconds rinp.verbosity = configcontext.options().get("tf-reader-verbosity"); - rinp.maxTFCache = std::max(1, configcontext.options().get("max-cached-tf")); - rinp.maxFileCache = std::max(1, configcontext.options().get("max-cached-files")); rinp.copyCmd = configcontext.options().get("copy-cmd"); rinp.tffileRegex = configcontext.options().get("tf-file-regex"); rinp.remoteRegex = configcontext.options().get("remote-regex"); From 348b1941d5873138f9f845eecf8f611b7f9d8ff5 Mon Sep 17 00:00:00 2001 From: shahoian Date: Sun, 17 Nov 2024 19:42:38 +0100 Subject: [PATCH 0068/2180] Optional ordering of objects in CCDBPopulator If an option --ordering-latency with positive value T (in ms) is provided than every incoming object will be buffered and uploaded only if no object with the same CCDB path and earlier start of validity was received in preceding T ms. All remaining cached objects are uploaded at EOR (or stop() method call). --- CCDB/include/CCDB/CcdbObjectInfo.h | 23 ++ Detectors/Calibration/README.md | 6 +- .../Calibration/workflow/CCDBPopulatorSpec.h | 300 +++++++++++------- 3 files changed, 220 insertions(+), 109 deletions(-) diff --git a/CCDB/include/CCDB/CcdbObjectInfo.h b/CCDB/include/CCDB/CcdbObjectInfo.h index 6db4cffd63b85..117ca1123104f 100644 --- a/CCDB/include/CCDB/CcdbObjectInfo.h +++ b/CCDB/include/CCDB/CcdbObjectInfo.h @@ -93,6 +93,16 @@ class CcdbObjectInfo [[nodiscard]] long getEndValidityTimestamp() const { return mEnd; } void setEndValidityTimestamp(long end) { mEnd = end; } + bool operator<(const CcdbObjectInfo& other) const + { + return mStart < other.mStart; + } + + bool operator>(const CcdbObjectInfo& other) const + { + return mStart > other.mStart; + } + private: std::string mObjType{}; // object type (e.g. class) std::string mFileName{}; // file name in the CCDB @@ -107,4 +117,17 @@ class CcdbObjectInfo } // namespace o2::ccdb +namespace std +{ +// defining std::hash for InteractionRecord to be used with std containers +template <> +struct hash { + public: + size_t operator()(const o2::ccdb::CcdbObjectInfo& info) const + { + return info.getStartValidityTimestamp(); + } +}; +} // namespace std + #endif // O2_CCDB_CCDBOBJECTINFO_H_ diff --git a/Detectors/Calibration/README.md b/Detectors/Calibration/README.md index 606493ea0cc32..be519be405c07 100644 --- a/Detectors/Calibration/README.md +++ b/Detectors/Calibration/README.md @@ -161,7 +161,11 @@ o2-calibration-ccdb-populator-workflow --sspec-min 0 --sspec-max 1 -b then the `ObjA` will be uploaded only to the default server (`http://alice-ccdb.cern.ch`), `ObjB` will be uploaded to both default and `local` server and `ObjC` will be uploaded to the `local` server only. -By default the ccdb-populator-workflow will not produce `fatal` on failed upload. To require it an option `--fatal-on-failure` can be used. +By default the `ccdb-populator-workflow` will not produce `fatal` on failed upload. To require it an option `--fatal-on-failure` can be used. + +By default the `ccdb-populator-workflow` uploads objects as it gets them. In case there is a danger that objects of the same URL will arrive in the order not sorted in SOV +(which may lead to screaning of the object with later SOV by other object if earlier ROF) one can use an option `--ordering-latency ` of the `ccdb-populator-workflow`. +Then every incoming object will be buffered and uploaded only if no object with the same CCDB path and earlier start of validity was received in preceding N milliseconds. All remaining cached objects are uploaded at EOR (or stop() method call). need to enforce this on the importParticles level of individual generators + for (auto& p : mParticles) { + auto st = o2::mcgenstatus::MCGenStatusEncoding(p.GetStatusCode(), p.GetStatusCode()).fullEncoding; + p.SetStatusCode(st); + p.SetBit(ParticleStatus::kToBeDone, true); + } + + return true; +} + +template +std::string GeneratorHybrid::jsonValueToString(const T& value) +{ + rapidjson::StringBuffer buffer; + rapidjson::Writer writer(buffer); + value.Accept(writer); + return buffer.GetString(); +} + +Bool_t GeneratorHybrid::parseJSON(const std::string& path) +{ + // Parse JSON file to build map + std::ifstream fileStream(path, std::ios::in); + if (!fileStream.is_open()) { + LOG(error) << "Cannot open " << path; + 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 -> " << rapidjson::GetParseError_En(doc.GetParseError()); + return false; + } + + // Put the generator names in mInputGens + if (doc.HasMember("generators")) { + const auto& gens = doc["generators"]; + for (const auto& gen : gens.GetArray()) { + // push in mInputGens the "name" of the generator + std::string name = gen["name"].GetString(); + mInputGens.push_back(name); + if (gen.HasMember("config")) { + if (name == "boxgen") { + const auto& boxconf = gen["config"]; + auto boxConfig = TBufferJSON::FromJSON(jsonValueToString(boxconf).c_str()); + mBoxGenConfigs.push_back(std::move(boxConfig)); + mConfigs.push_back("boxgen_" + std::to_string(mBoxGenConfigs.size() - 1)); + continue; + } else if (name == "pythia8") { + const auto& pythia8conf = gen["config"]; + auto pythia8Config = TBufferJSON::FromJSON(jsonValueToString(pythia8conf).c_str()); + mPythia8GenConfigs.push_back(std::move(pythia8Config)); + mConfigs.push_back("pythia8_" + std::to_string(mPythia8GenConfigs.size() - 1)); + continue; + } 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)); + continue; + } else if (name == "external") { + const auto& extconf = gen["config"]; + auto extConfig = TBufferJSON::FromJSON(jsonValueToString(extconf).c_str()); + mExternalGenConfigs.push_back(std::move(extConfig)); + mConfigs.push_back("external_" + std::to_string(mExternalGenConfigs.size() - 1)); + continue; + } else if (name == "hepmc") { + const auto& genconf = gen["config"]; + const auto& cmdconf = genconf["configcmd"]; + const auto& hepmcconf = genconf["confighepmc"]; + auto cmdConfig = TBufferJSON::FromJSON(jsonValueToString(cmdconf).c_str()); + auto hepmcConfig = TBufferJSON::FromJSON(jsonValueToString(hepmcconf).c_str()); + mFileOrCmdGenConfigs.push_back(std::move(cmdConfig)); + mHepMCGenConfigs.push_back(std::move(hepmcConfig)); + mConfigs.push_back("hepmc_" + std::to_string(mFileOrCmdGenConfigs.size() - 1)); + continue; + } else { + mConfigs.push_back(""); + } + } else { + if (name == "boxgen" || name == "pythia8" || name == "extkinO2" || name == "external" || name == "hepmc") { + LOG(fatal) << "No configuration provided for generator " << name; + return false; + } else { + mConfigs.push_back(""); + } + } + } + } + + // Get fractions and put them in mFractions + if (doc.HasMember("fractions")) { + const auto& fractions = doc["fractions"]; + for (const auto& frac : fractions.GetArray()) { + mFractions.push_back(frac.GetInt()); + } + } else { + // Set fractions to unity for all generators in case they are not provided + const auto& gens = doc["generators"]; + for (const auto& gen : gens.GetArray()) { + mFractions.push_back(1); + } + } + return true; +} + +} // namespace eventgen +} // namespace o2 + +ClassImp(o2::eventgen::GeneratorHybrid); \ No newline at end of file diff --git a/Generators/src/GeneratorHybridParam.cxx b/Generators/src/GeneratorHybridParam.cxx new file mode 100644 index 0000000000000..e15fbb8ee4ba4 --- /dev/null +++ b/Generators/src/GeneratorHybridParam.cxx @@ -0,0 +1,15 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \author M. Giacalone - October 2024 + +#include "Generators/GeneratorHybridParam.h" +O2ParamImpl(o2::eventgen::GeneratorHybridParam); \ No newline at end of file diff --git a/Generators/src/GeneratorPythia8.cxx b/Generators/src/GeneratorPythia8.cxx index a2740a37af19f..a27980a77c992 100644 --- a/Generators/src/GeneratorPythia8.cxx +++ b/Generators/src/GeneratorPythia8.cxx @@ -63,6 +63,27 @@ GeneratorPythia8::GeneratorPythia8() : Generator("ALICEo2", "ALICEo2 Pythia8 Gen /*****************************************************************/ +GeneratorPythia8::GeneratorPythia8(Pythia8GenConfig const& pars) : Generator("ALICEo2", "ALICEo2 Pythia8 Generator") +{ + /** constructor **/ + + mInterface = reinterpret_cast(&mPythia); + mInterfaceName = "pythia8"; + + LOG(info) << "Instance \'Pythia8\' generator with following parameters"; + LOG(info) << "config: " << pars.config; + LOG(info) << "hooksFileName: " << pars.hooksFileName; + LOG(info) << "hooksFuncName: " << pars.hooksFuncName; + + mGenConfig = std::make_unique(pars); + + setConfig(pars.config); + setHooksFileName(pars.hooksFileName); + setHooksFuncName(pars.hooksFuncName); +} + +/*****************************************************************/ + GeneratorPythia8::GeneratorPythia8(const Char_t* name, const Char_t* title) : Generator(name, title) { /** constructor **/ @@ -557,7 +578,8 @@ void GeneratorPythia8::pruneEvent(Pythia8::Event& event, Select select) } } } - if (GeneratorPythia8Param::Instance().verbose) { + int verbose = mGenConfig->verbose; + if (verbose) { LOG(info) << "Pythia event was pruned from " << event.size() << " to " << pruned.size() << " particles"; } @@ -570,7 +592,7 @@ void GeneratorPythia8::initUserFilterCallback() { mUserFilterFcn = [](Pythia8::Particle const&) -> bool { return true; }; - auto& filter = GeneratorPythia8Param::Instance().particleFilter; + std::string filter = mGenConfig->particleFilter; if (filter.size() > 0) { LOG(info) << "Initializing the callback for user-based particle pruning " << filter; auto expandedFileName = o2::utils::expandShellVarsInFileName(filter); @@ -599,7 +621,8 @@ Bool_t // event record in the AOD. std::function partonSelect = [](const Pythia8::Particle&) { return true; }; - if (not GeneratorPythia8Param::Instance().includePartonEvent) { + bool includeParton = mGenConfig->includePartonEvent; + if (not includeParton) { // Select pythia particles partonSelect = [](const Pythia8::Particle& particle) { diff --git a/Generators/src/GeneratorsLinkDef.h b/Generators/src/GeneratorsLinkDef.h index 222004e0957cb..18428a808a86b 100644 --- a/Generators/src/GeneratorsLinkDef.h +++ b/Generators/src/GeneratorsLinkDef.h @@ -29,10 +29,12 @@ #pragma link C++ class o2::eventgen::Generator + ; #pragma link C++ class o2::eventgen::GeneratorTGenerator + ; #pragma link C++ class o2::eventgen::GeneratorExternalParam + ; +#pragma link C++ class o2::eventgen::ExternalGenConfig + ; #pragma link C++ class o2::eventgen::GeneratorGeantinos + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::eventgen::GeneratorExternalParam> + ; #ifdef GENERATORS_WITH_HEPMC3 #pragma link C++ class o2::eventgen::GeneratorHepMC + ; +#pragma link C++ class o2::eventgen::HepMCGenConfig + ; #pragma link C++ class o2::eventgen::GeneratorHepMCParam + ; #endif #ifdef GENERATORS_WITH_PYTHIA6 @@ -44,6 +46,7 @@ #pragma link C++ class o2::eventgen::GeneratorPythia8 + ; #pragma link C++ class o2::eventgen::DecayerPythia8 + ; #pragma link C++ class o2::eventgen::GeneratorPythia8Param + ; +#pragma link C++ class o2::eventgen::Pythia8GenConfig + ; #pragma link C++ class o2::eventgen::DecayerPythia8Param + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::eventgen::GeneratorPythia8Param> + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::eventgen::DecayerPythia8Param> + ; @@ -51,7 +54,10 @@ #endif #pragma link C++ class o2::eventgen::GeneratorFromFile + ; #pragma link C++ class o2::eventgen::GeneratorFromO2Kine + ; +#pragma link C++ class o2::eventgen::GeneratorHybrid + ; +#pragma link C++ class o2::eventgen::GeneratorHybridParam + ; #pragma link C++ class o2::eventgen::GeneratorFromO2KineParam + ; +#pragma link C++ class o2::eventgen::O2KineGenConfig + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::eventgen::GeneratorFromO2KineParam> + ; #pragma link C++ class o2::eventgen::PrimaryGenerator + ; #pragma link C++ class o2::eventgen::PrimaryGeneratorParam + ; @@ -62,6 +68,7 @@ #pragma link C++ class o2::eventgen::TriggerParticleParam + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::eventgen::TriggerParticleParam> + ; #pragma link C++ class o2::eventgen::BoxGunParam + ; +#pragma link C++ class o2::eventgen::BoxGenConfig + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::eventgen::BoxGunParam> + ; #pragma link C++ class o2::eventgen::QEDGenParam + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::eventgen::QEDGenParam> + ; @@ -71,6 +78,7 @@ #pragma link C++ class o2::eventgen::GeneratorTParticleParam + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::eventgen::GeneratorTParticleParam> + ; #pragma link C++ class o2::eventgen::GeneratorFileOrCmdParam + ; +#pragma link C++ class o2::eventgen::FileOrCmdGenConfig + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::eventgen::GeneratorFileOrCmdParam> + ; #pragma link C++ class o2::eventgen::BoxGenerator + ; diff --git a/run/CMakeLists.txt b/run/CMakeLists.txt index fccb3f0085642..f21ecafb0528a 100644 --- a/run/CMakeLists.txt +++ b/run/CMakeLists.txt @@ -331,3 +331,7 @@ set_tests_properties(o2sim_G4_checklogs endif() install(FILES o2-sim-client.py PERMISSIONS GROUP_READ GROUP_EXECUTE OWNER_EXECUTE OWNER_WRITE OWNER_READ WORLD_EXECUTE WORLD_READ DESTINATION ${CMAKE_INSTALL_BINDIR}) +install(DIRECTORY SimExamples/ + DESTINATION examples + PATTERN * + PERMISSIONS GROUP_READ GROUP_EXECUTE OWNER_EXECUTE OWNER_WRITE OWNER_READ WORLD_EXECUTE WORLD_READ) \ No newline at end of file diff --git a/run/SimExamples/Hybrid/README.md b/run/SimExamples/Hybrid/README.md new file mode 100644 index 0000000000000..3c3cba37748bf --- /dev/null +++ b/run/SimExamples/Hybrid/README.md @@ -0,0 +1,18 @@ + + +The usage of the Hybrid generator with the o2-sim is presented in this short manual. +All the other generators are implemented as sub-generators and they can be called thanks to a +JSON file, fed to o2-sim via the GeneratorHybrid.configFile parameter. The O2sim package needs to be loaded in order to use this example. + +The example can be run automatically using the runo2sim.sh script, which contains most of the +available generators in O2. The JSON template can be generated using the ${O2DPG_ROOT}/MC/bin/o2_hybrid_gen.py script. To use this example the user can simply copy the entire Hybrid example folder and execute the script after giving it execution permissions (`chmod +x runo2sim.sh`). + +# Files description + +- **runo2sim.sh** → allows to use the hybrid generator example +- **hybridconfig.json** → example JSON file for the hybrid generator configuration +- **example.optns** → options file to be used in EPOS4 implemented as subgenerator in this example (the .optns must be available in the current working directory) +- **evtpool.root** → cached events to be used with the extkinO2 generator +- **epos4.hepmc** → EPOS4 events stored as hepmc file \ No newline at end of file diff --git a/run/SimExamples/Hybrid/example.optns b/run/SimExamples/Hybrid/example.optns new file mode 100644 index 0000000000000..99f865c2a05b3 --- /dev/null +++ b/run/SimExamples/Hybrid/example.optns @@ -0,0 +1,32 @@ +!-------------------------------------------------------------------- +! proton-proton collision no hydro no hadronic cascade +!-------------------------------------------------------------------- + +!--------------------------------------- +! Define run +!--------------------------------------- + +application hadron !hadron-hadron, hadron-nucleus, or nucleus-nucleus +set laproj 1 !projectile atomic number +set maproj 1 !projectile mass number +set latarg 1 !target atomic number +set matarg 1 !target mass number +set ecms 13600 !sqrt(s)_pp +set istmax 25 !max status considered for storage + +ftime on !string formation time non-zero +!suppressed decays: +nodecays + 110 20 2130 -2130 2230 -2230 1130 -1130 1330 -1330 2330 -2330 3331 -3331 +end + +set ninicon 1 !number of initial conditions used for hydro evolution +core off !core/corona not activated +hydro off !hydro not activated +eos off !eos not activated +hacas off !hadronic cascade not activated +set nfreeze 1 !number of freeze out events per hydro event +set modsho 1 !printout every modsho events +set centrality 0 !0=min bias +set ihepmc 2 !HepMC output enabled on stdout +set nfull 100000 diff --git a/run/SimExamples/Hybrid/hybridconfig.json b/run/SimExamples/Hybrid/hybridconfig.json new file mode 100644 index 0000000000000..ec36930c569fe --- /dev/null +++ b/run/SimExamples/Hybrid/hybridconfig.json @@ -0,0 +1,104 @@ +{ + "generators": [ + { + "name": "pythia8", + "config": { + "config": "$O2_ROOT/share/Generators/egconfig/pythia8_inel.cfg", + "hooksFileName": "", + "hooksFuncName": "", + "includePartonEvent": false, + "particleFilter": "", + "verbose": 0 + } + }, + { + "name": "boxgen", + "config": { + "pdg": 13, + "number": 1, + "eta": [ + -4, + -2.5 + ], + "prange": [ + 0.1, + 5 + ], + "phirange": [ + 0, + 360 + ] + } + }, + { + "name": "boxgen", + "config": { + "pdg": 22, + "number": 1, + "eta": [ + -4, + -2.5 + ], + "prange": [ + 0.1, + 5 + ], + "phirange": [ + 0, + 360 + ] + } + }, + { + "name": "external", + "config": { + "fileName": "${O2DPG_ROOT}/MC/config/PWGDQ/external/generator/GeneratorParamPromptJpsiToElectronEvtGen_pp13TeV.C", + "funcName": "GeneratorParamPromptJpsiToElectronEvtGen_pp13TeV()" + } + }, + { + "name": "extkinO2", + "config": { + "skipNonTrackable": true, + "continueMode": false, + "roundRobin": false, + "randomize": false, + "rngseed": 0, + "randomphi": false, + "fileName": "${PWD}/evtpool.root" + } + }, + { + "name": "hepmc", + "config": { + "configcmd": { + "fileNames": "", + "cmd": "" + }, + "confighepmc": { + "version": 2, + "eventsToSkip": 0, + "fileName": "${PWD}/slight.hepmc", + "prune": false + } + } + }, + { + "name": "pythia8pp" + }, + { + "name": "pythia8hf", + "config": "" + } + ], + "fractions": [ + 1, + 1, + 1, + 1, + 1, + 1, + 1, + 1 + ] +} \ No newline at end of file diff --git a/run/SimExamples/Hybrid/runo2sim.sh b/run/SimExamples/Hybrid/runo2sim.sh new file mode 100644 index 0000000000000..83a7f327abdaa --- /dev/null +++ b/run/SimExamples/Hybrid/runo2sim.sh @@ -0,0 +1,72 @@ +#!/usr/bin/env bash +# +# Hybrid generator simulation example: +# the simulation is configured using a JSON file (hybridconfig.json in this folder), whose +# template can be generated using the script ${O2DPG_ROOT}/MC/bin/o2_hybrid_gen.py +set -x +if [ ! "${O2DPG_ROOT}" ]; then + echo "This needs O2DPG loaded; alienv enter ..." + exit 1 +fi + +[ ! "${O2_ROOT}" ] && echo "Error: This needs O2 loaded" && exit 2 + +NEV=-1 +more="" +JOBS=2 + +usage() +{ + cat </dev/stderr + exit 3 + ;; + esac + shift +done + +# Set number of events in optns file +if [ ! $NEV -eq -1 ]; then + echo "Setting number of events to $NEV" +else + echo "Number of events not set, defaulting to 10..." + NEV=10 +fi + +# Generation of 1000 events using STARlight in a slight.hepmc file +${O2_ROOT}/examples/HepMC_STARlight/run-starlight.sh + +# Generation of event pool with pythia8 (10000 events) in a evtpool.root file +${O2DPG_ROOT}/MC/run/examples/event_pool.sh --make + +# Starting simulation with Hybrid generator +${O2_ROOT}/bin/o2-sim --noGeant -j $JOBS --field ccdb --vertexMode kCCDB --run 300000 --configKeyValues "MFTBase.buildAlignment=true;GeneratorHybrid.configFile=$PWD/hybridconfig.json;GeneratorHybrid.randomize=false;${more}" -g hybrid -o genevents --timestamp 1546300800000 --seed 836302859 -n $NEV \ No newline at end of file diff --git a/run/SimExamples/README.md b/run/SimExamples/README.md index 5615848202fca..139c24693b3ed 100644 --- a/run/SimExamples/README.md +++ b/run/SimExamples/README.md @@ -14,6 +14,8 @@ * \subpage refrunSimExamplesAliRoot_AMPT * \subpage refrunSimExamplesHepMC * \subpage refrunSimExamplesHepMC_STARlight +* \subpage refrunSimExamplesHepMC_EPOS4 +* \subpage refrunSimExamplesHybrid * \subpage refrunSimExamplesJet_Embedding_Pythia8 * \subpage refrunSimExamplesMcTracksToAOD * \subpage refrunSimExamplesMcTracksToAOD From 15dcbf65aec1e5473c1b3de3ed839bb40e148873 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Wed, 20 Nov 2024 13:49:34 +0100 Subject: [PATCH 0079/2180] Fix a few --compression options (#13717) --- Framework/AODMerger/src/aodMerger.cxx | 2 +- Framework/AODMerger/src/aodStrainer.cxx | 2 +- Framework/AODMerger/src/aodThinner.cxx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Framework/AODMerger/src/aodMerger.cxx b/Framework/AODMerger/src/aodMerger.cxx index aa27d4f617b3b..f474cb0d37e43 100644 --- a/Framework/AODMerger/src/aodMerger.cxx +++ b/Framework/AODMerger/src/aodMerger.cxx @@ -48,7 +48,7 @@ int main(int argc, char* argv[]) {"max-size", required_argument, nullptr, 2}, {"skip-non-existing-files", no_argument, nullptr, 3}, {"skip-parent-files-list", no_argument, nullptr, 4}, - {"compression", no_argument, nullptr, 5}, + {"compression", required_argument, nullptr, 5}, {"verbosity", required_argument, nullptr, 'v'}, {"help", no_argument, nullptr, 'h'}, {nullptr, 0, nullptr, 0}}; diff --git a/Framework/AODMerger/src/aodStrainer.cxx b/Framework/AODMerger/src/aodStrainer.cxx index 0ecb8d0d81659..fc54aa9c533cf 100644 --- a/Framework/AODMerger/src/aodStrainer.cxx +++ b/Framework/AODMerger/src/aodStrainer.cxx @@ -71,7 +71,7 @@ int main(int argc, char* argv[]) } else if (c == 4) { downsampling = atof(optarg); } else if (c == 5) { - compression = atof(optarg); + compression = atoi(optarg); } else if (c == 'h') { printf("AO2D strainer tool. Options: \n"); printf(" --input <%s> Contains path to files to be merged. Default: %s\n", inputAO2D.c_str(), inputAO2D.c_str()); diff --git a/Framework/AODMerger/src/aodThinner.cxx b/Framework/AODMerger/src/aodThinner.cxx index f9fb31f9ad08d..5da17032c80d4 100644 --- a/Framework/AODMerger/src/aodThinner.cxx +++ b/Framework/AODMerger/src/aodThinner.cxx @@ -51,7 +51,7 @@ int main(int argc, char* argv[]) {"input", required_argument, nullptr, 'i'}, {"output", required_argument, nullptr, 'o'}, {"overwrite", no_argument, nullptr, 'O'}, - {"compression", no_argument, nullptr, 'c'}, + {"compression", required_argument, nullptr, 'c'}, {"help", no_argument, nullptr, 'h'}, {nullptr, 0, nullptr, 0}}; From d3d07ddc000977f4e53ee70aa15f413b41cc47bb Mon Sep 17 00:00:00 2001 From: shahoian Date: Tue, 19 Nov 2024 21:16:10 +0100 Subject: [PATCH 0080/2180] Protect its/mft decoder agains decreasing row in the same column Discard cable data if this happens. A new error ChipPixelData::DecreasingRow is added. --- .../ITSMFTReconstruction/AlpideCoder.h | 51 +++++++++++-------- .../ITSMFTReconstruction/DecodingStat.h | 3 ++ 2 files changed, 34 insertions(+), 20 deletions(-) diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/AlpideCoder.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/AlpideCoder.h index 321ed52258ff2..b14a6b3f6b62f 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/AlpideCoder.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/AlpideCoder.h @@ -273,33 +273,44 @@ class AlpideCoder uint16_t row = pixID >> 1; // abs id of left column in double column uint16_t colD = (region * NDColInReg + dColID) << 1; // TODO consider <<4 instead of *NDColInReg? - bool rightC = (row & 0x1) ? !(pixID & 0x1) : (pixID & 0x1); // true for right column / lalse for left + bool rightC = (row & 0x1) ? !(pixID & 0x1) : (pixID & 0x1); // true for right column / false for left - if (row == rowPrev && colD == colDPrev) { - // this is a special test to exclude repeated data of the same pixel fired + if (colD == colDPrev) { + bool skip = false; + if (row == rowPrev) { // this is a special test to exclude repeated data of the same pixel fired + skip = true; #ifdef ALPIDE_DECODING_STAT - chipData.setError(ChipStat::RepeatingPixel); - chipData.addErrorInfo((uint64_t(colD + rightC) << 16) | uint64_t(row)); + chipData.setError(ChipStat::RepeatingPixel); + chipData.addErrorInfo((uint64_t(colD + rightC) << 16) | uint64_t(row)); #endif - if ((dataS & (~MaskDColID)) == DATALONG) { // skip pattern w/o decoding - uint8_t hitsPattern = 0; - if (!buffer.next(hitsPattern)) { + } else if (rowPrev < 0xffff && row < rowPrev) { #ifdef ALPIDE_DECODING_STAT - chipData.setError(ChipStat::TruncatedLondData); + chipData.setError(ChipStat::DecreasingRow); + chipData.addErrorInfo((uint64_t(colD + rightC) << 16) | uint64_t(row)); #endif - return unexpectedEOF("CHIP_DATA_LONG:Pattern"); // abandon cable data - } - if (hitsPattern & (~MaskHitMap)) { + return unexpectedEOF("DECREASING_ROW"); // abandon cable data + } + if (skip) { + if ((dataS & (~MaskDColID)) == DATALONG) { // skip pattern w/o decoding + uint8_t hitsPattern = 0; + if (!buffer.next(hitsPattern)) { #ifdef ALPIDE_DECODING_STAT - chipData.setError(ChipStat::WrongDataLongPattern); + chipData.setError(ChipStat::TruncatedLondData); #endif - return unexpectedEOF("CHIP_DATA_LONG:Pattern"); // abandon cable data + return unexpectedEOF("CHIP_DATA_LONG:Pattern"); // abandon cable data + } + if (hitsPattern & (~MaskHitMap)) { +#ifdef ALPIDE_DECODING_STAT + chipData.setError(ChipStat::WrongDataLongPattern); +#endif + return unexpectedEOF("CHIP_DATA_LONG:Pattern"); // abandon cable data + } + LOGP(debug, "hitsPattern: {:#b} expect {:#b}", int(hitsPattern), int(expectInp)); } - LOGP(debug, "hitsPattern: {:#b} expect {:#b}", int(hitsPattern), int(expectInp)); + expectInp = ExpectChipTrailer | ExpectData | ExpectRegion; + continue; // end of DATA(SHORT or LONG) processing } - expectInp = ExpectChipTrailer | ExpectData | ExpectRegion; - continue; // end of DATA(SHORT or LONG) processing - } else if (colD != colDPrev) { + } else { // if we start new double column, transfer the hits accumulated in the right column buffer of prev. double column if (colD < colDPrev && colDPrev != 0xffff) { #ifdef ALPIDE_DECODING_STAT @@ -321,7 +332,7 @@ class AlpideCoder // are first collected in the temporary buffer // real columnt id is col = colD + 1; if (rightC) { - rightColHits[nRightCHits++] = row; // col = colD+1 + rightColHits[nRightCHits++] = row; } else { addHit(chipData, row, colD); // col = colD, left column hits are added directly to the container } @@ -355,7 +366,7 @@ class AlpideCoder if (rightC) { // same as above rightColHits[nRightCHits++] = rowE; } else { - addHit(chipData, rowE, colD + rightC); // left column hits are added directly to the container + addHit(chipData, rowE, colD); // left column hits are added directly to the container } } } diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/DecodingStat.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/DecodingStat.h index 705e814fb4904..9a57228ddce1e 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/DecodingStat.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/DecodingStat.h @@ -68,6 +68,7 @@ struct ChipStat { FlushedIncomplete, // ALPIDE MEB was flushed by the busy handling StrobeExtended, // ALPIDE received a second trigger while the strobe was still open WrongAlpideChipID, // Impossible for given cable ALPIDE ChipOnModule ID + DecreasingRow, // Decreasing row in the same column NErrorsDefined }; @@ -106,6 +107,7 @@ struct ChipStat { "FlushedIncomplete", // ALPIDE MEB was flushed by the busy handling "StrobeExtended", // ALPIDE received a second trigger while the strobe was still open "Wrong Alpide ChipID", // Impossible for given cable ALPIDE ChipOnModule ID + "Decreasing row", // Decreasing row in the same column }; static constexpr std::array ErrActions = { @@ -143,6 +145,7 @@ struct ChipStat { ErrActPropagate | ErrActDump, // ALPIDE MEB was flushed by the busy handling ErrActPropagate | ErrActDump, // ALPIDE received a second trigger while the strobe was still open ErrActPropagate | ErrActDump, // Impossible for given cable ALPIDE ChipOnModule ID + ErrActPropagate | ErrActDump, // Decreasing row in the same column }; uint16_t feeID = -1; size_t nHits = 0; From 07a2041a974e7ba3de44e99d6a50af2aaf3379a8 Mon Sep 17 00:00:00 2001 From: Maximiliano Puccio Date: Wed, 20 Nov 2024 14:05:08 +0100 Subject: [PATCH 0081/2180] Add the TPC pid clusters to the analysis data model (#13617) --- .../AODProducerWorkflowSpec.h | 1 + Detectors/AOD/src/AODProducerWorkflowSpec.cxx | 3 ++ .../include/Framework/AnalysisDataModel.h | 34 +++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h index a85c7f74fa354..94f4526fe30a1 100644 --- a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h +++ b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h @@ -374,6 +374,7 @@ class AODProducerWorkflowDPL : public Task uint8_t itsClusterMap = 0; uint8_t tpcNClsFindable = 0; int8_t tpcNClsFindableMinusFound = 0; + int8_t tpcNClsFindableMinusPID = 0; int8_t tpcNClsFindableMinusCrossedRows = 0; uint8_t tpcNClsShared = 0; uint8_t trdPattern = 0; diff --git a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx index 8bcd590bb0e5c..6c3a418612478 100644 --- a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx +++ b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx @@ -334,6 +334,7 @@ void AODProducerWorkflowDPL::addToTracksExtraTable(TracksExtraCursorType& tracks extraInfoHolder.itsClusterSizes, extraInfoHolder.tpcNClsFindable, extraInfoHolder.tpcNClsFindableMinusFound, + // extraInfoHolder.tpcNClsFindableMinusPID, extraInfoHolder.tpcNClsFindableMinusCrossedRows, extraInfoHolder.tpcNClsShared, extraInfoHolder.trdPattern, @@ -2495,6 +2496,8 @@ AODProducerWorkflowDPL::TrackExtraInfo AODProducerWorkflowDPL::processBarrelTrac extraInfoHolder.tpcNClsFindableMinusFound = tpcOrig.getNClusters() - tpcClData.found; extraInfoHolder.tpcNClsFindableMinusCrossedRows = tpcOrig.getNClusters() - tpcClData.crossed; extraInfoHolder.tpcNClsShared = tpcClData.shared; + uint32_t clsUsedForPID = tpcOrig.getdEdx().NHitsIROC + tpcOrig.getdEdx().NHitsOROC1 + tpcOrig.getdEdx().NHitsOROC2 + tpcOrig.getdEdx().NHitsOROC3; + extraInfoHolder.tpcNClsFindableMinusPID = tpcOrig.getNClusters() - clsUsedForPID; if (src == GIndex::TPC) { // standalone TPC track should set its time from their timebins range if (needBCSlice) { double t = (tpcOrig.getTime0() + 0.5 * (tpcOrig.getDeltaTFwd() - tpcOrig.getDeltaTBwd())) * mTPCBinNS; // central value diff --git a/Framework/Core/include/Framework/AnalysisDataModel.h b/Framework/Core/include/Framework/AnalysisDataModel.h index b19552a14672d..c90e46bf6da06 100644 --- a/Framework/Core/include/Framework/AnalysisDataModel.h +++ b/Framework/Core/include/Framework/AnalysisDataModel.h @@ -243,6 +243,7 @@ DECLARE_SOA_COLUMN(ITSClusterSizes, itsClusterSizes, uint32_t); DECLARE_SOA_COLUMN(ITSClusterMap, itsClusterMap, uint8_t); //! Old cluster ITS cluster map, kept for version 0 compatibility DECLARE_SOA_COLUMN(TPCNClsFindable, tpcNClsFindable, uint8_t); //! Findable TPC clusters for this track geometry DECLARE_SOA_COLUMN(TPCNClsFindableMinusFound, tpcNClsFindableMinusFound, int8_t); //! TPC Clusters: Findable - Found +DECLARE_SOA_COLUMN(TPCNClsFindableMinusPID, tpcNClsFindableMinusPID, int8_t); //! TPC Clusters: Findable - Found clusters used for PID DECLARE_SOA_COLUMN(TPCNClsFindableMinusCrossedRows, tpcNClsFindableMinusCrossedRows, int8_t); //! TPC Clusters: Findable - crossed rows DECLARE_SOA_COLUMN(TPCNClsShared, tpcNClsShared, uint8_t); //! Number of shared TPC clusters DECLARE_SOA_COLUMN(TRDPattern, trdPattern, uint8_t); //! Contributor to the track on TRD layer in bits 0-5, starting from the innermost, bit 6 indicates a potentially split tracklet, bit 7 if the track crossed a padrow @@ -410,6 +411,8 @@ DECLARE_SOA_DYNAMIC_COLUMN(PIDForTracking, pidForTracking, //! PID hypothesis us [](uint32_t flags) -> uint32_t { return flags >> 28; }); DECLARE_SOA_DYNAMIC_COLUMN(TPCNClsFound, tpcNClsFound, //! Number of found TPC clusters [](uint8_t tpcNClsFindable, int8_t tpcNClsFindableMinusFound) -> int16_t { return (int16_t)tpcNClsFindable - tpcNClsFindableMinusFound; }); +DECLARE_SOA_DYNAMIC_COLUMN(TPCNClsPID, tpcNClsPID, //! Number of found TPC clusters used for PID + [](uint8_t tpcNClsFindable, int8_t tpcNClsFindableMinusPID) -> int16_t { return (int16_t)tpcNClsFindable - tpcNClsFindableMinusPID; }); DECLARE_SOA_DYNAMIC_COLUMN(TPCNClsCrossedRows, tpcNClsCrossedRows, //! Number of crossed TPC Rows [](uint8_t tpcNClsFindable, int8_t TPCNClsFindableMinusCrossedRows) -> int16_t { return (int16_t)tpcNClsFindable - TPCNClsFindableMinusCrossedRows; }); DECLARE_SOA_DYNAMIC_COLUMN(ITSNCls, itsNCls, //! Number of ITS clusters @@ -600,10 +603,41 @@ DECLARE_SOA_TABLE_FULL_VERSIONED(StoredTracksExtra_001, "TracksExtra", "AOD", "T track::TPCFractionSharedCls, 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 + track::TPCInnerParam, track::Flags, track::ITSClusterSizes, + track::TPCNClsFindable, track::TPCNClsFindableMinusFound, track::TPCNClsFindableMinusPID, track::TPCNClsFindableMinusCrossedRows, + track::TPCNClsShared, track::v001::extensions::TPCDeltaTFwd, track::v001::extensions::TPCDeltaTBwd, + track::TRDPattern, track::ITSChi2NCl, track::TPCChi2NCl, track::TRDChi2, track::TOFChi2, + track::TPCSignal, track::TRDSignal, track::Length, track::TOFExpMom, + track::PIDForTracking, + track::IsPVContributor, + track::HasITS, track::HasTPC, + track::HasTRD, track::HasTOF, + track::TPCNClsFound, + track::TPCNClsCrossedRows, + track::v001::ITSClusterMap, track::v001::ITSNCls, track::v001::ITSNClsInnerBarrel, + track::v001::ITSClsSizeInLayer, + track::v001::IsITSAfterburner, + track::TOFExpTimeEl, + track::TOFExpTimeMu, + track::TOFExpTimePi, + track::TOFExpTimeKa, + track::TOFExpTimePr, + track::TOFExpTimeDe, + track::TOFExpTimeTr, + track::TOFExpTimeHe, + track::TOFExpTimeAl, + track::TPCCrossedRowsOverFindableCls, + track::TPCFoundOverFindableCls, + track::TPCFractionSharedCls, + track::TrackEtaEMCAL, track::TrackPhiEMCAL, track::TrackTime, track::TrackTimeRes); + DECLARE_SOA_EXTENDED_TABLE(TracksExtra_000, StoredTracksExtra_000, "TRACKEXTRA", //! Additional track information (clusters, PID, etc.) track::DetectorMap); DECLARE_SOA_EXTENDED_TABLE(TracksExtra_001, StoredTracksExtra_001, "TRACKEXTRA", //! Additional track information (clusters, PID, etc.) track::v001::DetectorMap); +DECLARE_SOA_EXTENDED_TABLE(TracksExtra_002, StoredTracksExtra_002, "TRACKEXTRA", //! Additional track information (clusters, PID, etc.) + track::v001::DetectorMap); DECLARE_SOA_TABLE(Run2TrackExtras, "AOD", "RUN2TRACKEXTRA", track::ITSSignal); From c3ffb66878b29d24082a75dacbd75d65b287604c Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Thu, 21 Nov 2024 09:47:13 +0100 Subject: [PATCH 0082/2180] DPL: write support for TTree using arrow::Dataset API (#13718) --- .../include/Framework/RootArrowFilesystem.h | 32 ++ Framework/Core/src/RootArrowFilesystem.cxx | 302 +++++++++++++++++- Framework/Core/src/TableTreeHelpers.cxx | 2 +- Framework/Core/test/test_Root2ArrowTable.cxx | 108 +++++++ 4 files changed, 441 insertions(+), 3 deletions(-) diff --git a/Framework/Core/include/Framework/RootArrowFilesystem.h b/Framework/Core/include/Framework/RootArrowFilesystem.h index df00ce4fa8a76..7c8385ccd2b9d 100644 --- a/Framework/Core/include/Framework/RootArrowFilesystem.h +++ b/Framework/Core/include/Framework/RootArrowFilesystem.h @@ -87,6 +87,11 @@ class TTreeFileSystem : public VirtualRootFileSystemBase { return std::dynamic_pointer_cast(shared_from_this()); }; + + arrow::Result> OpenOutputStream( + const std::string& path, + const std::shared_ptr& metadata) override; + virtual TTree* GetTree(arrow::dataset::FileSource source) = 0; }; @@ -128,6 +133,10 @@ class TFileFileSystem : public VirtualRootFileSystemBase std::shared_ptr GetSubFilesystem(arrow::dataset::FileSource source) override; + arrow::Result> OpenOutputStream( + const std::string& path, + const std::shared_ptr& metadata) override; + // We can go back to the TFile in case this is needed. TDirectoryFile* GetFile() { @@ -218,6 +227,29 @@ class TTreeFileFormat : public arrow::dataset::FileFormat const std::shared_ptr& fragment) const override; }; +// An arrow outputstream which allows to write to a ttree +class TTreeOutputStream : public arrow::io::OutputStream +{ + public: + TTreeOutputStream(TTree* t); + + arrow::Status Close() override; + + arrow::Result Tell() const override; + + arrow::Status Write(const void* data, int64_t nbytes) override; + + bool closed() const override; + + TTree* GetTree() + { + return mTree; + } + + private: + TTree* mTree; +}; + } // namespace o2::framework #endif // O2_FRAMEWORK_ROOT_ARROW_FILESYSTEM_H_ diff --git a/Framework/Core/src/RootArrowFilesystem.cxx b/Framework/Core/src/RootArrowFilesystem.cxx index 46489141c3173..7581ee57e5b9f 100644 --- a/Framework/Core/src/RootArrowFilesystem.cxx +++ b/Framework/Core/src/RootArrowFilesystem.cxx @@ -11,6 +11,7 @@ #include "Framework/RootArrowFilesystem.h" #include "Framework/Endian.h" #include "Framework/RuntimeError.h" +#include "Framework/Signpost.h" #include #include #include @@ -24,6 +25,13 @@ #include #include #include +#include +#include +#include +#include + + +O2_DECLARE_DYNAMIC_LOG(root_arrow_fs); namespace { @@ -76,6 +84,7 @@ auto arrowTypeFromROOT(EDataType type, int size) } namespace o2::framework { +using arrow::Status; TFileFileSystem::TFileFileSystem(TDirectoryFile* f, size_t readahead) : VirtualRootFileSystemBase(), @@ -116,6 +125,15 @@ arrow::Result TFileFileSystem::GetFileInfo(const std::strin return result; } +arrow::Result> TFileFileSystem::OpenOutputStream( + const std::string& path, + const std::shared_ptr& metadata) +{ + auto* t = new TTree(path.c_str(), "should put a name here"); + auto stream = std::make_shared(t); + return stream; +} + arrow::Result VirtualRootFileSystemBase::GetFileInfo(std::string const&) { arrow::fs::FileInfo result; @@ -267,9 +285,279 @@ arrow::Result> TTreeFileFormat::Ma return std::dynamic_pointer_cast(fragment); } +// An arrow outputstream which allows to write to a ttree +TTreeOutputStream::TTreeOutputStream(TTree* t) + : mTree(t) +{ +} + +arrow::Status TTreeOutputStream::Close() +{ + mTree->GetCurrentFile()->Close(); + return arrow::Status::OK(); +} + +arrow::Result TTreeOutputStream::Tell() const +{ + return arrow::Result(arrow::Status::NotImplemented("Cannot move")); +} + +arrow::Status TTreeOutputStream::Write(const void* data, int64_t nbytes) +{ + return arrow::Status::NotImplemented("Cannot write raw bytes to a TTree"); +} + +bool TTreeOutputStream::closed() const +{ + return mTree->GetCurrentFile()->IsOpen() == false; +} + +char const* rootSuffixFromArrow(arrow::Type::type id) +{ + switch (id) { + case arrow::Type::BOOL: + return "/O"; + case arrow::Type::UINT8: + return "/b"; + case arrow::Type::UINT16: + return "/s"; + case arrow::Type::UINT32: + return "/i"; + case arrow::Type::UINT64: + return "/l"; + case arrow::Type::INT8: + return "/B"; + case arrow::Type::INT16: + return "/S"; + case arrow::Type::INT32: + return "/I"; + case arrow::Type::INT64: + return "/L"; + case arrow::Type::FLOAT: + return "/F"; + case arrow::Type::DOUBLE: + return "/D"; + default: + throw runtime_error("Unsupported arrow column type"); + } +} + +class TTreeFileWriter : public arrow::dataset::FileWriter +{ + std::vector branches; + std::vector sizesBranches; + std::vector> valueArrays; + std::vector> sizeArrays; + std::vector> valueTypes; + + std::vector valuesIdealBasketSize; + std::vector sizeIdealBasketSize; + + std::vector typeSizes; + std::vector listSizes; + bool firstBasket = true; + + // This is to create a batsket size according to the first batch. + void finaliseBasketSize(std::shared_ptr firstBatch) + { + O2_SIGNPOST_ID_FROM_POINTER(sid, root_arrow_fs, this); + O2_SIGNPOST_START(root_arrow_fs, sid, "finaliseBasketSize", "First batch with %lli rows received and %zu columns", + firstBatch->num_rows(), firstBatch->columns().size()); + for (size_t i = 0; i < branches.size(); i++) { + auto* branch = branches[i]; + auto* sizeBranch = sizesBranches[i]; + + int valueSize = valueTypes[i]->byte_width(); + if (listSizes[i] == 1) { + O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, sid, "finaliseBasketSize", "Branch %s exists and uses %d bytes per entry for %lli entries.", + branch->GetName(), valueSize, firstBatch->num_rows()); + assert(sizeBranch == nullptr); + branch->SetBasketSize(1024 + firstBatch->num_rows() * valueSize); + } else if (listSizes[i] == -1) { + O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, sid, "finaliseBasketSize", "Branch %s exists and uses %d bytes per entry.", + branch->GetName(), valueSize); + // This should probably lookup the + auto column = firstBatch->GetColumnByName(branch->GetName()); + auto list = std::static_pointer_cast(column); + O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, sid, "finaliseBasketSize", "Branch %s needed. Associated size branch %s and there are %lli entries of size %d in that list.", + branch->GetName(), sizeBranch->GetName(), list->length(), valueSize); + branch->SetBasketSize(1024 + firstBatch->num_rows() * valueSize * list->length()); + sizeBranch->SetBasketSize(1024 + firstBatch->num_rows() * 4); + } else { + O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, sid, "finaliseBasketSize", "Branch %s needed. There are %lli entries per array of size %d in that list.", + branch->GetName(), listSizes[i], valueSize); + assert(sizeBranch == nullptr); + branch->SetBasketSize(1024 + firstBatch->num_rows() * valueSize * listSizes[i]); + } + + auto field = firstBatch->schema()->field(i); + if (field->name().starts_with("fIndexArray")) { + // One int per array to keep track of the size + int idealBasketSize = 4 * firstBatch->num_rows() + 1024 + field->type()->byte_width() * firstBatch->num_rows(); // minimal additional size needed, otherwise we get 2 baskets + int basketSize = std::max(32000, idealBasketSize); // keep a minimum value + sizeBranch->SetBasketSize(basketSize); + branch->SetBasketSize(basketSize); + } + } + O2_SIGNPOST_END(root_arrow_fs, sid, "finaliseBasketSize", "Done"); + } + + public: + // Create the TTree based on the physical_schema, not the one in the batch. + // The write method will have to reconcile the two schemas. + TTreeFileWriter(std::shared_ptr schema, std::shared_ptr options, + std::shared_ptr destination, + arrow::fs::FileLocator destination_locator) + : FileWriter(schema, options, destination, destination_locator) + { + // Batches have the same number of entries for each column. + auto treeStream = std::dynamic_pointer_cast(destination_); + TTree* tree = treeStream->GetTree(); + + for (auto i = 0u; i < schema->fields().size(); ++i) { + auto& field = schema->field(i); + listSizes.push_back(1); + + int valuesIdealBasketSize = 0; + // Construct all the needed branches. + switch (field->type()->id()) { + case arrow::Type::FIXED_SIZE_LIST: { + listSizes.back() = std::static_pointer_cast(field->type())->list_size(); + valuesIdealBasketSize = 1024 + valueTypes.back()->byte_width() * listSizes.back(); + valueTypes.push_back(field->type()->field(0)->type()); + sizesBranches.push_back(nullptr); + std::string leafList = fmt::format("{}[{}]{}", field->name(), listSizes.back(), rootSuffixFromArrow(valueTypes.back()->id())); + branches.push_back(tree->Branch(field->name().c_str(), (char*)nullptr, leafList.c_str())); + } break; + case arrow::Type::LIST: { + valueTypes.push_back(field->type()->field(0)->type()); + listSizes.back() = 0; // VLA, we need to calculate it on the fly; + std::string leafList = fmt::format("{}[{}_size]{}", field->name(), field->name(), rootSuffixFromArrow(valueTypes.back()->id())); + std::string sizeLeafList = field->name() + "_size/I"; + sizesBranches.push_back(tree->Branch((field->name() + "_size").c_str(), (char*)nullptr, sizeLeafList.c_str())); + branches.push_back(tree->Branch(field->name().c_str(), (char*)nullptr, leafList.c_str())); + // Notice that this could be replaced by a better guess of the + // average size of the list elements, but this is not trivial. + } break; + default: { + valueTypes.push_back(field->type()); + std::string leafList = field->name() + rootSuffixFromArrow(valueTypes.back()->id()); + sizesBranches.push_back(nullptr); + branches.push_back(tree->Branch(field->name().c_str(), (char*)nullptr, leafList.c_str())); + } break; + } + } + // We create the branches from the schema + } + + arrow::Status Write(const std::shared_ptr& batch) override + { + if (firstBasket) { + firstBasket = false; + finaliseBasketSize(batch); + } + + // Support writing empty tables + if (batch->columns().empty() || batch->num_rows() == 0) { + return arrow::Status::OK(); + } + + // Batches have the same number of entries for each column. + auto treeStream = std::dynamic_pointer_cast(destination_); + TTree* tree = treeStream->GetTree(); + + // Caches for the vectors of bools. + std::vector> caches; + + for (auto i = 0u; i < batch->columns().size(); ++i) { + auto column = batch->column(i); + auto& field = batch->schema()->field(i); + + valueArrays.push_back(nullptr); + + switch (field->type()->id()) { + case arrow::Type::FIXED_SIZE_LIST: { + auto list = std::static_pointer_cast(column); + valueArrays.back() = list->values(); + } break; + case arrow::Type::LIST: { + auto list = std::static_pointer_cast(column); + valueArrays.back() = list; + } break; + default: + valueArrays.back() = column; + } + } + + int64_t pos = 0; + while (pos < batch->num_rows()) { + for (size_t bi = 0; bi < branches.size(); ++bi) { + auto* branch = branches[bi]; + auto* sizeBranch = sizesBranches[bi]; + auto array = batch->column(bi); + auto& field = batch->schema()->field(bi); + auto& listSize = listSizes[bi]; + auto valueType = valueTypes[bi]; + auto valueArray = valueArrays[bi]; + + if (field->type()->id() == arrow::Type::BOOL) { + auto boolArray = std::static_pointer_cast(array); + int64_t length = boolArray->length(); + arrow::UInt8Builder builder; + auto ok = builder.Reserve(length); + + for (int64_t i = 0; i < length; ++i) { + if (boolArray->IsValid(i)) { + // Expand each boolean value (true/false) to uint8 (1/0) + uint8_t value = boolArray->Value(i) ? 1 : 0; + auto ok = builder.Append(value); + } else { + // Append null for invalid entries + auto ok = builder.AppendNull(); + } + } + + ok = builder.Finish(&caches[bi]); + branch->SetAddress((void*)(caches[bi]->values()->data())); + continue; + } + switch (field->type()->id()) { + case arrow::Type::LIST: { + auto list = std::static_pointer_cast(array); + listSize = list->value_length(pos); + uint8_t const* buffer = std::static_pointer_cast(valueArray)->values()->data() + array->offset() + list->value_offset(pos) * valueType->byte_width(); + branch->SetAddress((void*)buffer); + sizeBranch->SetAddress(&listSize); + }; + break; + case arrow::Type::FIXED_SIZE_LIST: + default: { + uint8_t const* buffer = std::static_pointer_cast(valueArray)->values()->data() + array->offset() + pos * listSize * valueType->byte_width(); + branch->SetAddress((void*)buffer); + }; + } + } + tree->Fill(); + ++pos; + } + return arrow::Status::OK(); + } + + arrow::Future<> FinishInternal() override + { + auto treeStream = std::dynamic_pointer_cast(destination_); + TTree* tree = treeStream->GetTree(); + tree->Write("", TObject::kOverwrite); + tree->SetDirectory(nullptr); + + return {}; + }; +}; + arrow::Result> TTreeFileFormat::MakeWriter(std::shared_ptr destination, std::shared_ptr schema, std::shared_ptr options, arrow::fs::FileLocator destination_locator) const { - throw std::runtime_error("Unsupported operation"); + auto writer = std::make_shared(schema, options, destination, destination_locator); + return std::dynamic_pointer_cast(writer); } std::shared_ptr TTreeFileFormat::DefaultWriteOptions() @@ -401,8 +689,10 @@ arrow::Result TTreeFileFormat::ScanBatchesAsync( int64_t listSize = 1; if (auto fixedSizeList = std::dynamic_pointer_cast(physicalField->type())) { listSize = fixedSizeList->list_size(); + typeSize = fixedSizeList->field(0)->type()->byte_width(); } else if (auto vlaListType = std::dynamic_pointer_cast(physicalField->type())) { listSize = -1; + typeSize = fixedSizeList->field(0)->type()->byte_width(); } if (listSize == -1) { mSizeBranch = branch->GetTree()->GetBranch((std::string{branch->GetName()} + "_size").c_str()); @@ -474,6 +764,15 @@ arrow::Result TTreeFileFormat::ScanBatchesAsync( return generator; } + +arrow::Result> TTreeFileSystem::OpenOutputStream( + const std::string& path, + const std::shared_ptr& metadata) +{ + auto stream = std::make_shared(GetTree({path, shared_from_this()})); + return stream; +} + TBufferFileFS::TBufferFileFS(TBufferFile* f) : VirtualRootFileSystemBase(), mBuffer(f), @@ -512,5 +811,4 @@ std::shared_ptr TBufferFileFS::GetSubFilesystem(arrow } return mFilesystem; } - } // namespace o2::framework diff --git a/Framework/Core/src/TableTreeHelpers.cxx b/Framework/Core/src/TableTreeHelpers.cxx index c20febaac517d..d0fdd0ced5779 100644 --- a/Framework/Core/src/TableTreeHelpers.cxx +++ b/Framework/Core/src/TableTreeHelpers.cxx @@ -512,7 +512,7 @@ void TreeToTable::addAllColumns(TTree* tree, std::vector&& names) if (strncmp(reader->branch()->GetName(), "fIndexArray", strlen("fIndexArray")) == 0) { std::string sizeBranchName = reader->branch()->GetName(); sizeBranchName += "_size"; - TBranch* sizeBranch = (TBranch*)tree->GetBranch(sizeBranchName.c_str()); + auto* sizeBranch = (TBranch*)tree->GetBranch(sizeBranchName.c_str()); if (sizeBranch) { tree->AddBranchToCache(sizeBranch); } diff --git a/Framework/Core/test/test_Root2ArrowTable.cxx b/Framework/Core/test/test_Root2ArrowTable.cxx index 599f1062c63a0..03f0977a4c0c4 100644 --- a/Framework/Core/test/test_Root2ArrowTable.cxx +++ b/Framework/Core/test/test_Root2ArrowTable.cxx @@ -358,4 +358,112 @@ TEST_CASE("RootTree2Dataset") REQUIRE(result.ok()); REQUIRE((*result)->columns().size() == 7); REQUIRE((*result)->num_rows() == 100); + + { + auto int_array = std::static_pointer_cast((*result)->GetColumnByName("ev")); + for (int64_t j = 0; j < int_array->length(); j++) { + REQUIRE(int_array->Value(j) == j + 1); + } + } + + { + auto list_array = std::static_pointer_cast((*result)->GetColumnByName("xyz")); + + // Iterate over the FixedSizeListArray + for (int64_t i = 0; i < list_array->length(); i++) { + auto value_slice = list_array->value_slice(i); + auto float_array = std::static_pointer_cast(value_slice); + + REQUIRE(float_array->Value(0) == 1); + REQUIRE(float_array->Value(1) == 2); + REQUIRE(float_array->Value(2) == i + 1); + } + } + + { + auto list_array = std::static_pointer_cast((*result)->GetColumnByName("ij")); + + // Iterate over the FixedSizeListArray + for (int64_t i = 0; i < list_array->length(); i++) { + auto value_slice = list_array->value_slice(i); + auto int_array = std::static_pointer_cast(value_slice); + REQUIRE(int_array->Value(0) == i); + REQUIRE(int_array->Value(1) == i + 1); + } + } + + auto* output = new TMemFile("foo", "RECREATE"); + auto outFs = std::make_shared(output, 0); + arrow::fs::FileLocator locator{outFs, "/DF_3"}; + + auto destination = outFs->OpenOutputStream(locator.path, {}); + REQUIRE(destination.ok()); + + auto writer = format->MakeWriter(*destination, schema, {}, locator); + auto success = writer->get()->Write(*result); + auto rootDestination = std::dynamic_pointer_cast(*destination); + + REQUIRE(success.ok()); + // Let's read it back... + arrow::dataset::FileSource source2("/DF_3", outFs); + auto newTreeFS = outFs->GetSubFilesystem(source2); + + REQUIRE(format->IsSupported(source) == true); + + auto schemaOptWritten = format->Inspect(source); + REQUIRE(schemaOptWritten.ok()); + auto schemaWritten = *schemaOptWritten; + REQUIRE(schemaWritten->num_fields() == 7); + REQUIRE(schemaWritten->field(0)->type()->id() == arrow::float32()->id()); + REQUIRE(schemaWritten->field(1)->type()->id() == arrow::float32()->id()); + REQUIRE(schemaWritten->field(2)->type()->id() == arrow::float32()->id()); + REQUIRE(schemaWritten->field(3)->type()->id() == arrow::float64()->id()); + REQUIRE(schemaWritten->field(4)->type()->id() == arrow::int32()->id()); + REQUIRE(schemaWritten->field(5)->type()->id() == arrow::fixed_size_list(arrow::float32(), 3)->id()); + REQUIRE(schemaWritten->field(6)->type()->id() == arrow::fixed_size_list(arrow::int32(), 2)->id()); + + auto fragmentWritten = format->MakeFragment(source, {}, schema); + REQUIRE(fragmentWritten.ok()); + auto optionsWritten = std::make_shared(); + options->dataset_schema = schemaWritten; + auto scannerWritten = format->ScanBatchesAsync(optionsWritten, *fragment); + REQUIRE(scannerWritten.ok()); + auto batchesWritten = (*scanner)(); + auto resultWritten = batches.result(); + REQUIRE(resultWritten.ok()); + REQUIRE((*resultWritten)->columns().size() == 7); + REQUIRE((*resultWritten)->num_rows() == 100); + + { + auto int_array = std::static_pointer_cast((*resultWritten)->GetColumnByName("ev")); + for (int64_t j = 0; j < int_array->length(); j++) { + REQUIRE(int_array->Value(j) == j + 1); + } + } + + { + auto list_array = std::static_pointer_cast((*result)->GetColumnByName("xyz")); + + // Iterate over the FixedSizeListArray + for (int64_t i = 0; i < list_array->length(); i++) { + auto value_slice = list_array->value_slice(i); + auto float_array = std::static_pointer_cast(value_slice); + + REQUIRE(float_array->Value(0) == 1); + REQUIRE(float_array->Value(1) == 2); + REQUIRE(float_array->Value(2) == i + 1); + } + } + + { + auto list_array = std::static_pointer_cast((*result)->GetColumnByName("ij")); + + // Iterate over the FixedSizeListArray + for (int64_t i = 0; i < list_array->length(); i++) { + auto value_slice = list_array->value_slice(i); + auto int_array = std::static_pointer_cast(value_slice); + REQUIRE(int_array->Value(0) == i); + REQUIRE(int_array->Value(1) == i + 1); + } + } } From 929bb79415f510ec2faaebc83df0df459d62507d Mon Sep 17 00:00:00 2001 From: swenzel Date: Thu, 21 Nov 2024 16:24:50 +0100 Subject: [PATCH 0083/2180] Fix GeneratorPythia8 construction Fixes smaller issues introduced with the hybrid generator refactoring: - Make sure that internal configuration object is never null; In fact, it does not need to be a pointer. - Make sure the internal configuration struct is initialized from the GeneratorPythia8Param configurable when using the default constructor. This commit fixes an issue/segfault when running the following command o2-sim-dpl-eventgen --generator external --nEvents 200 --aggregate-timeframe 10000 --configFile ${O2DPG_ROOT}/MC/config/ALICE3/ini/pythia8_pp_136tev.ini -b (which defaults constructs a Pythia8 generator) --- .../include/Generators/GeneratorPythia8.h | 4 +- Generators/src/GeneratorPythia8.cxx | 37 +++++++++++-------- 2 files changed, 23 insertions(+), 18 deletions(-) diff --git a/Generators/include/Generators/GeneratorPythia8.h b/Generators/include/Generators/GeneratorPythia8.h index b5abbb2600545..926003c55259b 100644 --- a/Generators/include/Generators/GeneratorPythia8.h +++ b/Generators/include/Generators/GeneratorPythia8.h @@ -90,7 +90,7 @@ class GeneratorPythia8 : public Generator /** default constructor **/ GeneratorPythia8(); /** constructor **/ - GeneratorPythia8(Pythia8GenConfig const& pars); + GeneratorPythia8(Pythia8GenConfig const&); /** constructor **/ GeneratorPythia8(const Char_t* name, const Char_t* title = "ALICEo2 Pythia8 Generator"); /** destructor **/ @@ -285,7 +285,7 @@ class GeneratorPythia8 : public Generator long mInitialRNGSeed = -1; // initial seed for Pythia random number state; // will be transported to Pythia in the Init function through the Pythia::readString("Random:seed") mechanism. // Value of -1 means unitialized; 0 will be time-dependent and values >1 <= MAX_SEED concrete reproducible seeding - std::unique_ptr mGenConfig; // configuration object + Pythia8GenConfig mGenConfig; // configuration object constexpr static long MAX_SEED = 900000000; diff --git a/Generators/src/GeneratorPythia8.cxx b/Generators/src/GeneratorPythia8.cxx index a27980a77c992..8c9b4fcffdff2 100644 --- a/Generators/src/GeneratorPythia8.cxx +++ b/Generators/src/GeneratorPythia8.cxx @@ -53,17 +53,23 @@ GeneratorPythia8::GeneratorPythia8() : Generator("ALICEo2", "ALICEo2 Pythia8 Gen mInterfaceName = "pythia8"; auto& param = GeneratorPythia8Param::Instance(); - LOG(info) << "Instance \'Pythia8\' generator with following parameters"; + LOG(info) << "Default Instance \'Pythia8\' generator with following parameters"; LOG(info) << param; - setConfig(param.config); - setHooksFileName(param.hooksFileName); - setHooksFuncName(param.hooksFuncName); + // convert the outside singleton config to the internally used one + o2::eventgen::Pythia8GenConfig config{param.config, + param.hooksFileName, param.hooksFuncName, param.includePartonEvent, param.particleFilter, param.verbose}; + mGenConfig = config; + + setConfig(config.config); + setHooksFileName(config.hooksFileName); + setHooksFuncName(config.hooksFuncName); + // TODO: use constructor delegation to other interface } /*****************************************************************/ -GeneratorPythia8::GeneratorPythia8(Pythia8GenConfig const& pars) : Generator("ALICEo2", "ALICEo2 Pythia8 Generator") +GeneratorPythia8::GeneratorPythia8(Pythia8GenConfig const& config) : Generator("ALICEo2", "ALICEo2 Pythia8 Generator") { /** constructor **/ @@ -71,15 +77,15 @@ GeneratorPythia8::GeneratorPythia8(Pythia8GenConfig const& pars) : Generator("AL mInterfaceName = "pythia8"; LOG(info) << "Instance \'Pythia8\' generator with following parameters"; - LOG(info) << "config: " << pars.config; - LOG(info) << "hooksFileName: " << pars.hooksFileName; - LOG(info) << "hooksFuncName: " << pars.hooksFuncName; + LOG(info) << "config: " << config.config; + LOG(info) << "hooksFileName: " << config.hooksFileName; + LOG(info) << "hooksFuncName: " << config.hooksFuncName; - mGenConfig = std::make_unique(pars); + mGenConfig = config; - setConfig(pars.config); - setHooksFileName(pars.hooksFileName); - setHooksFuncName(pars.hooksFuncName); + setConfig(mGenConfig.config); + setHooksFileName(mGenConfig.hooksFileName); + setHooksFuncName(mGenConfig.hooksFuncName); } /*****************************************************************/ @@ -578,8 +584,7 @@ void GeneratorPythia8::pruneEvent(Pythia8::Event& event, Select select) } } } - int verbose = mGenConfig->verbose; - if (verbose) { + if (mGenConfig.verbose) { LOG(info) << "Pythia event was pruned from " << event.size() << " to " << pruned.size() << " particles"; } @@ -592,7 +597,7 @@ void GeneratorPythia8::initUserFilterCallback() { mUserFilterFcn = [](Pythia8::Particle const&) -> bool { return true; }; - std::string filter = mGenConfig->particleFilter; + std::string filter = mGenConfig.particleFilter; if (filter.size() > 0) { LOG(info) << "Initializing the callback for user-based particle pruning " << filter; auto expandedFileName = o2::utils::expandShellVarsInFileName(filter); @@ -621,7 +626,7 @@ Bool_t // event record in the AOD. std::function partonSelect = [](const Pythia8::Particle&) { return true; }; - bool includeParton = mGenConfig->includePartonEvent; + bool includeParton = mGenConfig.includePartonEvent; if (not includeParton) { // Select pythia particles From 7f54d8fcd98d3bf73b4d325477b8701c4fe6b99e Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Fri, 22 Nov 2024 07:44:05 +0100 Subject: [PATCH 0084/2180] Fix for failing dataflow build (#13721) --- Generators/CMakeLists.txt | 11 +++++++---- Generators/src/GeneratorFactory.cxx | 8 ++++++-- Generators/src/GeneratorsLinkDef.h | 6 ++++-- 3 files changed, 17 insertions(+), 8 deletions(-) diff --git a/Generators/CMakeLists.txt b/Generators/CMakeLists.txt index 3b32d076aec1a..d60d185817c84 100644 --- a/Generators/CMakeLists.txt +++ b/Generators/CMakeLists.txt @@ -24,8 +24,6 @@ o2_add_library(Generators src/GeneratorTGenerator.cxx src/GeneratorExternalParam.cxx src/GeneratorFromFile.cxx - src/GeneratorHybrid.cxx - src/GeneratorHybridParam.cxx src/GeneratorFromO2KineParam.cxx src/GeneratorFileOrCmd.cxx src/GeneratorFileOrCmdParam.cxx @@ -50,6 +48,8 @@ o2_add_library(Generators $<$:src/GeneratorHepMC.cxx> $<$:src/GeneratorHepMCParam.cxx> $<$:src/AODToHepMC.cxx> + $<$,$>:src/GeneratorHybrid.cxx> + $<$,$>:src/GeneratorHybridParam.cxx> PUBLIC_LINK_LIBRARIES FairRoot::Base O2::SimConfig O2::CommonUtils O2::DetectorsBase O2::ZDCBase O2::SimulationDataFormat ${pythiaTarget} ${hepmcTarget} FairRoot::Gen @@ -70,8 +70,6 @@ set(headers include/Generators/GeneratorTGenerator.h include/Generators/GeneratorExternalParam.h include/Generators/GeneratorFromFile.h - include/Generators/GeneratorHybrid.h - include/Generators/GeneratorHybridParam.h include/Generators/GeneratorFromO2KineParam.h include/Generators/GeneratorFileOrCmd.h include/Generators/GeneratorFileOrCmdParam.h @@ -104,6 +102,11 @@ if(HepMC3_FOUND) list(APPEND headers include/Generators/GeneratorHepMCParam.h) endif() +if(pythia_FOUND AND HepMC3_FOUND) + list(APPEND headers include/Generators/GeneratorHybrid.h) + list(APPEND headers include/Generators/GeneratorHybridParam.h) +endif() + o2_target_root_dictionary(Generators HEADERS ${headers}) o2_add_test_root_macro(share/external/extgen.C diff --git a/Generators/src/GeneratorFactory.cxx b/Generators/src/GeneratorFactory.cxx index 92be3773b54ee..8233024a4c2d7 100644 --- a/Generators/src/GeneratorFactory.cxx +++ b/Generators/src/GeneratorFactory.cxx @@ -18,7 +18,6 @@ #include #include #include -#include #include #include #ifdef GENERATORS_WITH_PYTHIA8 @@ -27,12 +26,15 @@ #endif #include #include -#include #include "Generators/GeneratorFromO2KineParam.h" #ifdef GENERATORS_WITH_HEPMC3 #include #include #endif +#if defined(GENERATORS_WITH_PYTHIA8) && defined(GENERATORS_WITH_HEPMC3) +#include +#include +#endif #include #include #include @@ -260,6 +262,7 @@ void GeneratorFactory::setPrimaryGenerator(o2::conf::SimConfig const& conf, Fair primGen->AddGenerator(boxGen); } } +#if defined(GENERATORS_WITH_PYTHIA8) && defined(GENERATORS_WITH_HEPMC3) } else if (genconfig.compare("hybrid") == 0) { // hybrid using multiple generators LOG(info) << "Init hybrid generator"; auto& hybridparam = GeneratorHybridParam::Instance(); @@ -276,6 +279,7 @@ void GeneratorFactory::setPrimaryGenerator(o2::conf::SimConfig const& conf, Fair } auto hybrid = new o2::eventgen::GeneratorHybrid(config); primGen->AddGenerator(hybrid); +#endif } else { LOG(fatal) << "Invalid generator"; } diff --git a/Generators/src/GeneratorsLinkDef.h b/Generators/src/GeneratorsLinkDef.h index 18428a808a86b..41e14b02f18b9 100644 --- a/Generators/src/GeneratorsLinkDef.h +++ b/Generators/src/GeneratorsLinkDef.h @@ -52,10 +52,12 @@ #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::eventgen::DecayerPythia8Param> + ; #pragma link C++ class o2::eventgen::GeneratorFactory + ; #endif -#pragma link C++ class o2::eventgen::GeneratorFromFile + ; -#pragma link C++ class o2::eventgen::GeneratorFromO2Kine + ; +#if defined(GENERATORS_WITH_PYTHIA8) && defined(GENERATORS_WITH_HEPMC3) #pragma link C++ class o2::eventgen::GeneratorHybrid + ; #pragma link C++ class o2::eventgen::GeneratorHybridParam + ; +#endif +#pragma link C++ class o2::eventgen::GeneratorFromFile + ; +#pragma link C++ class o2::eventgen::GeneratorFromO2Kine + ; #pragma link C++ class o2::eventgen::GeneratorFromO2KineParam + ; #pragma link C++ class o2::eventgen::O2KineGenConfig + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::eventgen::GeneratorFromO2KineParam> + ; From 00c02574bd1dc0f7e46bf4d74ab673b9dea9ac2d Mon Sep 17 00:00:00 2001 From: Christian Sonnabend Date: Fri, 22 Nov 2024 11:00:20 +0100 Subject: [PATCH 0085/2180] ORT library in the O2 framework (#13709) --- Common/CMakeLists.txt | 1 + Common/ML/CMakeLists.txt | 15 + Common/ML/include/ML/3rdparty/GPUORTFloat16.h | 867 ++++++++++++++++++ Common/ML/include/ML/ort_interface.h | 92 ++ Common/ML/src/ort_interface.cxx | 280 ++++++ 5 files changed, 1255 insertions(+) create mode 100644 Common/ML/CMakeLists.txt create mode 100644 Common/ML/include/ML/3rdparty/GPUORTFloat16.h create mode 100644 Common/ML/include/ML/ort_interface.h create mode 100644 Common/ML/src/ort_interface.cxx diff --git a/Common/CMakeLists.txt b/Common/CMakeLists.txt index f435e269575aa..0b92758e45f43 100644 --- a/Common/CMakeLists.txt +++ b/Common/CMakeLists.txt @@ -16,5 +16,6 @@ add_subdirectory(Types) add_subdirectory(Utils) add_subdirectory(SimConfig) add_subdirectory(DCAFitter) +add_subdirectory(ML) o2_data_file(COPY maps DESTINATION Common) diff --git a/Common/ML/CMakeLists.txt b/Common/ML/CMakeLists.txt new file mode 100644 index 0000000000000..74287e774efa1 --- /dev/null +++ b/Common/ML/CMakeLists.txt @@ -0,0 +1,15 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +o2_add_library(ML + SOURCES src/ort_interface.cxx + TARGETVARNAME targetName + PRIVATE_LINK_LIBRARIES O2::Framework ONNXRuntime::ONNXRuntime) diff --git a/Common/ML/include/ML/3rdparty/GPUORTFloat16.h b/Common/ML/include/ML/3rdparty/GPUORTFloat16.h new file mode 100644 index 0000000000000..db65328409d3c --- /dev/null +++ b/Common/ML/include/ML/3rdparty/GPUORTFloat16.h @@ -0,0 +1,867 @@ +// Copyright (c) Microsoft Corporation. All rights reserved. +// Licensed under the MIT License. + +// This code was created from: +// - https://github.com/microsoft/onnxruntime/blob/main/include/onnxruntime/core/session/onnxruntime_float16.h +// - https://github.com/microsoft/onnxruntime/blob/main/include/onnxruntime/core/session/onnxruntime_cxx_api.h + +#include +#include +#include +#include + +namespace o2 +{ + +namespace OrtDataType +{ + +namespace detail +{ + +enum class endian { +#if defined(_WIN32) + little = 0, + big = 1, + native = little, +#elif defined(__GNUC__) || defined(__clang__) + little = __ORDER_LITTLE_ENDIAN__, + big = __ORDER_BIG_ENDIAN__, + native = __BYTE_ORDER__, +#else +#error OrtDataType::detail::endian is not implemented in this environment. +#endif +}; + +static_assert( + endian::native == endian::little || endian::native == endian::big, + "Only little-endian or big-endian native byte orders are supported."); + +} // namespace detail + +/// +/// Shared implementation between public and internal classes. CRTP pattern. +/// +template +struct Float16Impl { + protected: + /// + /// Converts from float to uint16_t float16 representation + /// + /// + /// + constexpr static uint16_t ToUint16Impl(float v) noexcept; + + /// + /// Converts float16 to float + /// + /// float representation of float16 value + float ToFloatImpl() const noexcept; + + /// + /// Creates an instance that represents absolute value. + /// + /// Absolute value + uint16_t AbsImpl() const noexcept + { + return static_cast(val & ~kSignMask); + } + + /// + /// Creates a new instance with the sign flipped. + /// + /// Flipped sign instance + uint16_t NegateImpl() const noexcept + { + return IsNaN() ? val : static_cast(val ^ kSignMask); + } + + public: + // uint16_t special values + static constexpr uint16_t kSignMask = 0x8000U; + static constexpr uint16_t kBiasedExponentMask = 0x7C00U; + static constexpr uint16_t kPositiveInfinityBits = 0x7C00U; + static constexpr uint16_t kNegativeInfinityBits = 0xFC00U; + static constexpr uint16_t kPositiveQNaNBits = 0x7E00U; + static constexpr uint16_t kNegativeQNaNBits = 0xFE00U; + static constexpr uint16_t kEpsilonBits = 0x4170U; + static constexpr uint16_t kMinValueBits = 0xFBFFU; // Minimum normal number + static constexpr uint16_t kMaxValueBits = 0x7BFFU; // Largest normal number + static constexpr uint16_t kOneBits = 0x3C00U; + static constexpr uint16_t kMinusOneBits = 0xBC00U; + + uint16_t val{0}; + + Float16Impl() = default; + + /// + /// Checks if the value is negative + /// + /// true if negative + bool IsNegative() const noexcept + { + return static_cast(val) < 0; + } + + /// + /// Tests if the value is NaN + /// + /// true if NaN + bool IsNaN() const noexcept + { + return AbsImpl() > kPositiveInfinityBits; + } + + /// + /// Tests if the value is finite + /// + /// true if finite + bool IsFinite() const noexcept + { + return AbsImpl() < kPositiveInfinityBits; + } + + /// + /// Tests if the value represents positive infinity. + /// + /// true if positive infinity + bool IsPositiveInfinity() const noexcept + { + return val == kPositiveInfinityBits; + } + + /// + /// Tests if the value represents negative infinity + /// + /// true if negative infinity + bool IsNegativeInfinity() const noexcept + { + return val == kNegativeInfinityBits; + } + + /// + /// Tests if the value is either positive or negative infinity. + /// + /// True if absolute value is infinity + bool IsInfinity() const noexcept + { + return AbsImpl() == kPositiveInfinityBits; + } + + /// + /// Tests if the value is NaN or zero. Useful for comparisons. + /// + /// True if NaN or zero. + bool IsNaNOrZero() const noexcept + { + auto abs = AbsImpl(); + return (abs == 0 || abs > kPositiveInfinityBits); + } + + /// + /// Tests if the value is normal (not zero, subnormal, infinite, or NaN). + /// + /// True if so + bool IsNormal() const noexcept + { + auto abs = AbsImpl(); + return (abs < kPositiveInfinityBits) // is finite + && (abs != 0) // is not zero + && ((abs & kBiasedExponentMask) != 0); // is not subnormal (has a non-zero exponent) + } + + /// + /// Tests if the value is subnormal (denormal). + /// + /// True if so + bool IsSubnormal() const noexcept + { + auto abs = AbsImpl(); + return (abs < kPositiveInfinityBits) // is finite + && (abs != 0) // is not zero + && ((abs & kBiasedExponentMask) == 0); // is subnormal (has a zero exponent) + } + + /// + /// Creates an instance that represents absolute value. + /// + /// Absolute value + Derived Abs() const noexcept { return Derived::FromBits(AbsImpl()); } + + /// + /// Creates a new instance with the sign flipped. + /// + /// Flipped sign instance + Derived Negate() const noexcept { return Derived::FromBits(NegateImpl()); } + + /// + /// IEEE defines that positive and negative zero are equal, this gives us a quick equality check + /// for two values by or'ing the private bits together and stripping the sign. They are both zero, + /// and therefore equivalent, if the resulting value is still zero. + /// + /// first value + /// second value + /// True if both arguments represent zero + static bool AreZero(const Float16Impl& lhs, const Float16Impl& rhs) noexcept + { + return static_cast((lhs.val | rhs.val) & ~kSignMask) == 0; + } + + bool operator==(const Float16Impl& rhs) const noexcept + { + if (IsNaN() || rhs.IsNaN()) { + // IEEE defines that NaN is not equal to anything, including itself. + return false; + } + return val == rhs.val; + } + + bool operator!=(const Float16Impl& rhs) const noexcept { return !(*this == rhs); } + + bool operator<(const Float16Impl& rhs) const noexcept + { + if (IsNaN() || rhs.IsNaN()) { + // IEEE defines that NaN is unordered with respect to everything, including itself. + return false; + } + + const bool left_is_negative = IsNegative(); + if (left_is_negative != rhs.IsNegative()) { + // When the signs of left and right differ, we know that left is less than right if it is + // the negative value. The exception to this is if both values are zero, in which case IEEE + // says they should be equal, even if the signs differ. + return left_is_negative && !AreZero(*this, rhs); + } + return (val != rhs.val) && ((val < rhs.val) ^ left_is_negative); + } +}; + +// The following Float16_t conversions are based on the code from +// Eigen library. + +// The conversion routines are Copyright (c) Fabian Giesen, 2016. +// The original license follows: +// +// Copyright (c) Fabian Giesen, 2016 +// All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted. +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +namespace detail +{ +union float32_bits { + unsigned int u; + float f; +}; +}; // namespace detail + +template +inline constexpr uint16_t Float16Impl::ToUint16Impl(float v) noexcept +{ + detail::float32_bits f{}; + f.f = v; + + constexpr detail::float32_bits f32infty = {255 << 23}; + constexpr detail::float32_bits f16max = {(127 + 16) << 23}; + constexpr detail::float32_bits denorm_magic = {((127 - 15) + (23 - 10) + 1) << 23}; + constexpr unsigned int sign_mask = 0x80000000u; + uint16_t val = static_cast(0x0u); + + unsigned int sign = f.u & sign_mask; + f.u ^= sign; + + // NOTE all the integer compares in this function can be safely + // compiled into signed compares since all operands are below + // 0x80000000. Important if you want fast straight SSE2 code + // (since there's no unsigned PCMPGTD). + + if (f.u >= f16max.u) { // result is Inf or NaN (all exponent bits set) + val = (f.u > f32infty.u) ? 0x7e00 : 0x7c00; // NaN->qNaN and Inf->Inf + } else { // (De)normalized number or zero + if (f.u < (113 << 23)) { // resulting FP16 is subnormal or zero + // use a magic value to align our 10 mantissa bits at the bottom of + // the float. as long as FP addition is round-to-nearest-even this + // just works. + f.f += denorm_magic.f; + + // and one integer subtract of the bias later, we have our final float! + val = static_cast(f.u - denorm_magic.u); + } else { + unsigned int mant_odd = (f.u >> 13) & 1; // resulting mantissa is odd + + // update exponent, rounding bias part 1 + // Equivalent to `f.u += ((unsigned int)(15 - 127) << 23) + 0xfff`, but + // without arithmetic overflow. + f.u += 0xc8000fffU; + // rounding bias part 2 + f.u += mant_odd; + // take the bits! + val = static_cast(f.u >> 13); + } + } + + val |= static_cast(sign >> 16); + return val; +} + +template +inline float Float16Impl::ToFloatImpl() const noexcept +{ + constexpr detail::float32_bits magic = {113 << 23}; + constexpr unsigned int shifted_exp = 0x7c00 << 13; // exponent mask after shift + detail::float32_bits o{}; + + o.u = (val & 0x7fff) << 13; // exponent/mantissa bits + unsigned int exp = shifted_exp & o.u; // just the exponent + o.u += (127 - 15) << 23; // exponent adjust + + // handle exponent special cases + if (exp == shifted_exp) { // Inf/NaN? + o.u += (128 - 16) << 23; // extra exp adjust + } else if (exp == 0) { // Zero/Denormal? + o.u += 1 << 23; // extra exp adjust + o.f -= magic.f; // re-normalize + } + + // Attempt to workaround the Internal Compiler Error on ARM64 + // for bitwise | operator, including std::bitset +#if (defined _MSC_VER) && (defined _M_ARM || defined _M_ARM64 || defined _M_ARM64EC) + if (IsNegative()) { + return -o.f; + } +#else + // original code: + o.u |= (val & 0x8000U) << 16U; // sign bit +#endif + return o.f; +} + +/// Shared implementation between public and internal classes. CRTP pattern. +template +struct BFloat16Impl { + protected: + /// + /// Converts from float to uint16_t float16 representation + /// + /// + /// + static uint16_t ToUint16Impl(float v) noexcept; + + /// + /// Converts bfloat16 to float + /// + /// float representation of bfloat16 value + float ToFloatImpl() const noexcept; + + /// + /// Creates an instance that represents absolute value. + /// + /// Absolute value + uint16_t AbsImpl() const noexcept + { + return static_cast(val & ~kSignMask); + } + + /// + /// Creates a new instance with the sign flipped. + /// + /// Flipped sign instance + uint16_t NegateImpl() const noexcept + { + return IsNaN() ? val : static_cast(val ^ kSignMask); + } + + public: + // uint16_t special values + static constexpr uint16_t kSignMask = 0x8000U; + static constexpr uint16_t kBiasedExponentMask = 0x7F80U; + static constexpr uint16_t kPositiveInfinityBits = 0x7F80U; + static constexpr uint16_t kNegativeInfinityBits = 0xFF80U; + static constexpr uint16_t kPositiveQNaNBits = 0x7FC1U; + static constexpr uint16_t kNegativeQNaNBits = 0xFFC1U; + static constexpr uint16_t kSignaling_NaNBits = 0x7F80U; + static constexpr uint16_t kEpsilonBits = 0x0080U; + static constexpr uint16_t kMinValueBits = 0xFF7FU; + static constexpr uint16_t kMaxValueBits = 0x7F7FU; + static constexpr uint16_t kRoundToNearest = 0x7FFFU; + static constexpr uint16_t kOneBits = 0x3F80U; + static constexpr uint16_t kMinusOneBits = 0xBF80U; + + uint16_t val{0}; + + BFloat16Impl() = default; + + /// + /// Checks if the value is negative + /// + /// true if negative + bool IsNegative() const noexcept + { + return static_cast(val) < 0; + } + + /// + /// Tests if the value is NaN + /// + /// true if NaN + bool IsNaN() const noexcept + { + return AbsImpl() > kPositiveInfinityBits; + } + + /// + /// Tests if the value is finite + /// + /// true if finite + bool IsFinite() const noexcept + { + return AbsImpl() < kPositiveInfinityBits; + } + + /// + /// Tests if the value represents positive infinity. + /// + /// true if positive infinity + bool IsPositiveInfinity() const noexcept + { + return val == kPositiveInfinityBits; + } + + /// + /// Tests if the value represents negative infinity + /// + /// true if negative infinity + bool IsNegativeInfinity() const noexcept + { + return val == kNegativeInfinityBits; + } + + /// + /// Tests if the value is either positive or negative infinity. + /// + /// True if absolute value is infinity + bool IsInfinity() const noexcept + { + return AbsImpl() == kPositiveInfinityBits; + } + + /// + /// Tests if the value is NaN or zero. Useful for comparisons. + /// + /// True if NaN or zero. + bool IsNaNOrZero() const noexcept + { + auto abs = AbsImpl(); + return (abs == 0 || abs > kPositiveInfinityBits); + } + + /// + /// Tests if the value is normal (not zero, subnormal, infinite, or NaN). + /// + /// True if so + bool IsNormal() const noexcept + { + auto abs = AbsImpl(); + return (abs < kPositiveInfinityBits) // is finite + && (abs != 0) // is not zero + && ((abs & kBiasedExponentMask) != 0); // is not subnormal (has a non-zero exponent) + } + + /// + /// Tests if the value is subnormal (denormal). + /// + /// True if so + bool IsSubnormal() const noexcept + { + auto abs = AbsImpl(); + return (abs < kPositiveInfinityBits) // is finite + && (abs != 0) // is not zero + && ((abs & kBiasedExponentMask) == 0); // is subnormal (has a zero exponent) + } + + /// + /// Creates an instance that represents absolute value. + /// + /// Absolute value + Derived Abs() const noexcept { return Derived::FromBits(AbsImpl()); } + + /// + /// Creates a new instance with the sign flipped. + /// + /// Flipped sign instance + Derived Negate() const noexcept { return Derived::FromBits(NegateImpl()); } + + /// + /// IEEE defines that positive and negative zero are equal, this gives us a quick equality check + /// for two values by or'ing the private bits together and stripping the sign. They are both zero, + /// and therefore equivalent, if the resulting value is still zero. + /// + /// first value + /// second value + /// True if both arguments represent zero + static bool AreZero(const BFloat16Impl& lhs, const BFloat16Impl& rhs) noexcept + { + // IEEE defines that positive and negative zero are equal, this gives us a quick equality check + // for two values by or'ing the private bits together and stripping the sign. They are both zero, + // and therefore equivalent, if the resulting value is still zero. + return static_cast((lhs.val | rhs.val) & ~kSignMask) == 0; + } +}; + +template +inline uint16_t BFloat16Impl::ToUint16Impl(float v) noexcept +{ + uint16_t result; + if (std::isnan(v)) { + result = kPositiveQNaNBits; + } else { + auto get_msb_half = [](float fl) { + uint16_t result; +#ifdef __cpp_if_constexpr + if constexpr (detail::endian::native == detail::endian::little) +#else + if (detail::endian::native == detail::endian::little) +#endif + { + std::memcpy(&result, reinterpret_cast(&fl) + sizeof(uint16_t), sizeof(uint16_t)); + } else { + std::memcpy(&result, &fl, sizeof(uint16_t)); + } + return result; + }; + + uint16_t upper_bits = get_msb_half(v); + union { + uint32_t U32; + float F32; + }; + F32 = v; + U32 += (upper_bits & 1) + kRoundToNearest; + result = get_msb_half(F32); + } + return result; +} + +template +inline float BFloat16Impl::ToFloatImpl() const noexcept +{ + if (IsNaN()) { + return std::numeric_limits::quiet_NaN(); + } + float result; + char* const first = reinterpret_cast(&result); + char* const second = first + sizeof(uint16_t); +#ifdef __cpp_if_constexpr + if constexpr (detail::endian::native == detail::endian::little) +#else + if (detail::endian::native == detail::endian::little) +#endif + { + std::memset(first, 0, sizeof(uint16_t)); + std::memcpy(second, &val, sizeof(uint16_t)); + } else { + std::memcpy(first, &val, sizeof(uint16_t)); + std::memset(second, 0, sizeof(uint16_t)); + } + return result; +} + +/** \brief IEEE 754 half-precision floating point data type + * + * \details This struct is used for converting float to float16 and back + * so the user could feed inputs and fetch outputs using these type. + * + * The size of the structure should align with uint16_t and one can freely cast + * uint16_t buffers to/from Ort::Float16_t to feed and retrieve data. + * + * \code{.unparsed} + * // This example demonstrates converion from float to float16 + * constexpr float values[] = {1.f, 2.f, 3.f, 4.f, 5.f}; + * std::vector fp16_values; + * fp16_values.reserve(std::size(values)); + * std::transform(std::begin(values), std::end(values), std::back_inserter(fp16_values), + * [](float value) { return Ort::Float16_t(value); }); + * + * \endcode + */ +struct Float16_t : OrtDataType::Float16Impl { + private: + /// + /// Constructor from a 16-bit representation of a float16 value + /// No conversion is done here. + /// + /// 16-bit representation + constexpr explicit Float16_t(uint16_t v) noexcept { val = v; } + + public: + using Base = OrtDataType::Float16Impl; + + /// + /// Default constructor + /// + Float16_t() = default; + + /// + /// Explicit conversion to uint16_t representation of float16. + /// + /// uint16_t bit representation of float16 + /// new instance of Float16_t + constexpr static Float16_t FromBits(uint16_t v) noexcept { return Float16_t(v); } + + /// + /// __ctor from float. Float is converted into float16 16-bit representation. + /// + /// float value + explicit Float16_t(float v) noexcept { val = Base::ToUint16Impl(v); } + + /// + /// Converts float16 to float + /// + /// float representation of float16 value + float ToFloat() const noexcept { return Base::ToFloatImpl(); } + + /// + /// Checks if the value is negative + /// + /// true if negative + using Base::IsNegative; + + /// + /// Tests if the value is NaN + /// + /// true if NaN + using Base::IsNaN; + + /// + /// Tests if the value is finite + /// + /// true if finite + using Base::IsFinite; + + /// + /// Tests if the value represents positive infinity. + /// + /// true if positive infinity + using Base::IsPositiveInfinity; + + /// + /// Tests if the value represents negative infinity + /// + /// true if negative infinity + using Base::IsNegativeInfinity; + + /// + /// Tests if the value is either positive or negative infinity. + /// + /// True if absolute value is infinity + using Base::IsInfinity; + + /// + /// Tests if the value is NaN or zero. Useful for comparisons. + /// + /// True if NaN or zero. + using Base::IsNaNOrZero; + + /// + /// Tests if the value is normal (not zero, subnormal, infinite, or NaN). + /// + /// True if so + using Base::IsNormal; + + /// + /// Tests if the value is subnormal (denormal). + /// + /// True if so + using Base::IsSubnormal; + + /// + /// Creates an instance that represents absolute value. + /// + /// Absolute value + using Base::Abs; + + /// + /// Creates a new instance with the sign flipped. + /// + /// Flipped sign instance + using Base::Negate; + + /// + /// IEEE defines that positive and negative zero are equal, this gives us a quick equality check + /// for two values by or'ing the private bits together and stripping the sign. They are both zero, + /// and therefore equivalent, if the resulting value is still zero. + /// + /// first value + /// second value + /// True if both arguments represent zero + using Base::AreZero; + + /// + /// User defined conversion operator. Converts Float16_t to float. + /// + explicit operator float() const noexcept { return ToFloat(); } + + using Base::operator==; + using Base::operator!=; + using Base::operator<; +}; + +static_assert(sizeof(Float16_t) == sizeof(uint16_t), "Sizes must match"); + +/** \brief bfloat16 (Brain Floating Point) data type + * + * \details This struct is used for converting float to bfloat16 and back + * so the user could feed inputs and fetch outputs using these type. + * + * The size of the structure should align with uint16_t and one can freely cast + * uint16_t buffers to/from Ort::BFloat16_t to feed and retrieve data. + * + * \code{.unparsed} + * // This example demonstrates converion from float to float16 + * constexpr float values[] = {1.f, 2.f, 3.f, 4.f, 5.f}; + * std::vector bfp16_values; + * bfp16_values.reserve(std::size(values)); + * std::transform(std::begin(values), std::end(values), std::back_inserter(bfp16_values), + * [](float value) { return Ort::BFloat16_t(value); }); + * + * \endcode + */ +struct BFloat16_t : OrtDataType::BFloat16Impl { + private: + /// + /// Constructor from a uint16_t representation of bfloat16 + /// used in FromBits() to escape overload resolution issue with + /// constructor from float. + /// No conversion is done. + /// + /// 16-bit bfloat16 value + constexpr explicit BFloat16_t(uint16_t v) noexcept { val = v; } + + public: + using Base = OrtDataType::BFloat16Impl; + + BFloat16_t() = default; + + /// + /// Explicit conversion to uint16_t representation of bfloat16. + /// + /// uint16_t bit representation of bfloat16 + /// new instance of BFloat16_t + static constexpr BFloat16_t FromBits(uint16_t v) noexcept { return BFloat16_t(v); } + + /// + /// __ctor from float. Float is converted into bfloat16 16-bit representation. + /// + /// float value + explicit BFloat16_t(float v) noexcept { val = Base::ToUint16Impl(v); } + + /// + /// Converts bfloat16 to float + /// + /// float representation of bfloat16 value + float ToFloat() const noexcept { return Base::ToFloatImpl(); } + + /// + /// Checks if the value is negative + /// + /// true if negative + using Base::IsNegative; + + /// + /// Tests if the value is NaN + /// + /// true if NaN + using Base::IsNaN; + + /// + /// Tests if the value is finite + /// + /// true if finite + using Base::IsFinite; + + /// + /// Tests if the value represents positive infinity. + /// + /// true if positive infinity + using Base::IsPositiveInfinity; + + /// + /// Tests if the value represents negative infinity + /// + /// true if negative infinity + using Base::IsNegativeInfinity; + + /// + /// Tests if the value is either positive or negative infinity. + /// + /// True if absolute value is infinity + using Base::IsInfinity; + + /// + /// Tests if the value is NaN or zero. Useful for comparisons. + /// + /// True if NaN or zero. + using Base::IsNaNOrZero; + + /// + /// Tests if the value is normal (not zero, subnormal, infinite, or NaN). + /// + /// True if so + using Base::IsNormal; + + /// + /// Tests if the value is subnormal (denormal). + /// + /// True if so + using Base::IsSubnormal; + + /// + /// Creates an instance that represents absolute value. + /// + /// Absolute value + using Base::Abs; + + /// + /// Creates a new instance with the sign flipped. + /// + /// Flipped sign instance + using Base::Negate; + + /// + /// IEEE defines that positive and negative zero are equal, this gives us a quick equality check + /// for two values by or'ing the private bits together and stripping the sign. They are both zero, + /// and therefore equivalent, if the resulting value is still zero. + /// + /// first value + /// second value + /// True if both arguments represent zero + using Base::AreZero; + + /// + /// User defined conversion operator. Converts BFloat16_t to float. + /// + explicit operator float() const noexcept { return ToFloat(); } + + // We do not have an inherited impl for the below operators + // as the internal class implements them a little differently + bool operator==(const BFloat16_t& rhs) const noexcept; + bool operator!=(const BFloat16_t& rhs) const noexcept { return !(*this == rhs); } + bool operator<(const BFloat16_t& rhs) const noexcept; +}; + +static_assert(sizeof(BFloat16_t) == sizeof(uint16_t), "Sizes must match"); + +} // namespace OrtDataType + +} // namespace o2 \ No newline at end of file diff --git a/Common/ML/include/ML/ort_interface.h b/Common/ML/include/ML/ort_interface.h new file mode 100644 index 0000000000000..e2049b8508cb4 --- /dev/null +++ b/Common/ML/include/ML/ort_interface.h @@ -0,0 +1,92 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ort_interface.h +/// \author Christian Sonnabend +/// \brief A header library for loading ONNX models and inferencing them on CPU and GPU + +#ifndef O2_ML_ONNX_INTERFACE_H +#define O2_ML_ONNX_INTERFACE_H + +// C++ and system includes +#include +#include +#include +#include +#include + +// O2 includes +#include "Framework/Logger.h" + +namespace o2 +{ + +namespace ml +{ + +class OrtModel +{ + + public: + // Constructor + OrtModel() = default; + OrtModel(std::unordered_map optionsMap) { reset(optionsMap); } + void init(std::unordered_map optionsMap) { reset(optionsMap); } + void reset(std::unordered_map); + + virtual ~OrtModel() = default; + + // Conversion + template + std::vector v2v(std::vector&, bool = true); + + // Inferencing + template // class I is the input data type, e.g. float, class O is the output data type, e.g. OrtDataType::Float16_t from O2/Common/ML/include/ML/GPUORTFloat16.h + std::vector inference(std::vector&); + + template // class I is the input data type, e.g. float, class O is the output data type, e.g. O2::gpu::OrtDataType::Float16_t from O2/GPU/GPUTracking/ML/convert_float16.h + std::vector inference(std::vector>&); + + // template // class I is the input data type, e.g. float, class T the throughput data type and class O is the output data type + // std::vector inference(std::vector&); + + // Reset session + void resetSession(); + + std::vector> getNumInputNodes() const { return mInputShapes; } + std::vector> getNumOutputNodes() const { return mOutputShapes; } + std::vector getInputNames() const { return mInputNames; } + std::vector getOutputNames() const { return mOutputNames; } + + void setActiveThreads(int threads) { intraOpNumThreads = threads; } + + private: + // ORT variables -> need to be hidden as Pimpl + struct OrtVariables; + OrtVariables* pImplOrt; + + // Input & Output specifications of the loaded network + std::vector inputNamesChar, outputNamesChar; + std::vector mInputNames, mOutputNames; + std::vector> mInputShapes, mOutputShapes; + + // Environment settings + std::string modelPath, device = "cpu", dtype = "float"; // device options should be cpu, rocm, migraphx, cuda + int intraOpNumThreads = 0, deviceId = 0, enableProfiling = 0, loggingLevel = 0, allocateDeviceMemory = 0, enableOptimizations = 0; + + std::string printShape(const std::vector&); +}; + +} // namespace ml + +} // namespace o2 + +#endif // O2_ML_ORT_INTERFACE_H diff --git a/Common/ML/src/ort_interface.cxx b/Common/ML/src/ort_interface.cxx new file mode 100644 index 0000000000000..27ac8eee16b7b --- /dev/null +++ b/Common/ML/src/ort_interface.cxx @@ -0,0 +1,280 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file ort_interface.cxx +/// \author Christian Sonnabend +/// \brief A header library for loading ONNX models and inferencing them on CPU and GPU + +#include "ML/ort_interface.h" +#include "ML/3rdparty/GPUORTFloat16.h" + +// ONNX includes +#include + +namespace o2 +{ + +namespace ml +{ + +struct OrtModel::OrtVariables { // The actual implementation is hidden in the .cxx file + // ORT runtime objects + Ort::RunOptions runOptions; + std::shared_ptr env = nullptr; + std::shared_ptr session = nullptr; ///< ONNX session + Ort::SessionOptions sessionOptions; + Ort::AllocatorWithDefaultOptions allocator; + Ort::MemoryInfo memoryInfo = Ort::MemoryInfo("Cpu", OrtAllocatorType::OrtDeviceAllocator, 0, OrtMemType::OrtMemTypeDefault); +}; + +void OrtModel::reset(std::unordered_map optionsMap) +{ + + pImplOrt = new OrtVariables(); + + // Load from options map + if (!optionsMap.contains("model-path")) { + LOG(fatal) << "(ORT) Model path cannot be empty!"; + } + modelPath = optionsMap["model-path"]; + device = (optionsMap.contains("device") ? optionsMap["device"] : "CPU"); + dtype = (optionsMap.contains("dtype") ? optionsMap["dtype"] : "float"); + deviceId = (optionsMap.contains("device-id") ? std::stoi(optionsMap["device-id"]) : 0); + allocateDeviceMemory = (optionsMap.contains("allocate-device-memory") ? std::stoi(optionsMap["allocate-device-memory"]) : 0); + intraOpNumThreads = (optionsMap.contains("intra-op-num-threads") ? std::stoi(optionsMap["intra-op-num-threads"]) : 0); + loggingLevel = (optionsMap.contains("logging-level") ? std::stoi(optionsMap["logging-level"]) : 0); + enableProfiling = (optionsMap.contains("enable-profiling") ? std::stoi(optionsMap["enable-profiling"]) : 0); + enableOptimizations = (optionsMap.contains("enable-optimizations") ? std::stoi(optionsMap["enable-optimizations"]) : 0); + + std::string dev_mem_str = "Hip"; +#ifdef ORT_ROCM_BUILD + if (device == "ROCM") { + Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_ROCM(pImplOrt->sessionOptions, deviceId)); + LOG(info) << "(ORT) ROCM execution provider set"; + } +#endif +#ifdef ORT_MIGRAPHX_BUILD + if (device == "MIGRAPHX") { + Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_MIGraphX(pImplOrt->sessionOptions, deviceId)); + LOG(info) << "(ORT) MIGraphX execution provider set"; + } +#endif +#ifdef ORT_CUDA_BUILD + if (device == "CUDA") { + Ort::ThrowOnError(OrtSessionOptionsAppendExecutionProvider_CUDA(pImplOrt->sessionOptions, deviceId)); + LOG(info) << "(ORT) CUDA execution provider set"; + dev_mem_str = "Cuda"; + } +#endif + + if (allocateDeviceMemory) { + pImplOrt->memoryInfo = Ort::MemoryInfo(dev_mem_str.c_str(), OrtAllocatorType::OrtDeviceAllocator, deviceId, OrtMemType::OrtMemTypeDefault); + LOG(info) << "(ORT) Memory info set to on-device memory"; + } + + if (device == "CPU") { + (pImplOrt->sessionOptions).SetIntraOpNumThreads(intraOpNumThreads); + if (intraOpNumThreads > 1) { + (pImplOrt->sessionOptions).SetExecutionMode(ExecutionMode::ORT_PARALLEL); + } else if (intraOpNumThreads == 1) { + (pImplOrt->sessionOptions).SetExecutionMode(ExecutionMode::ORT_SEQUENTIAL); + } + LOG(info) << "(ORT) CPU execution provider set with " << intraOpNumThreads << " threads"; + } + + (pImplOrt->sessionOptions).DisableMemPattern(); + (pImplOrt->sessionOptions).DisableCpuMemArena(); + + if (enableProfiling) { + if (optionsMap.contains("profiling-output-path")) { + (pImplOrt->sessionOptions).EnableProfiling((optionsMap["profiling-output-path"] + "/ORT_LOG_").c_str()); + } else { + LOG(warning) << "(ORT) If profiling is enabled, optionsMap[\"profiling-output-path\"] should be set. Disabling profiling for now."; + (pImplOrt->sessionOptions).DisableProfiling(); + } + } else { + (pImplOrt->sessionOptions).DisableProfiling(); + } + (pImplOrt->sessionOptions).SetGraphOptimizationLevel(GraphOptimizationLevel(enableOptimizations)); + (pImplOrt->sessionOptions).SetLogSeverityLevel(OrtLoggingLevel(loggingLevel)); + + pImplOrt->env = std::make_shared(OrtLoggingLevel(loggingLevel), (optionsMap["onnx-environment-name"].empty() ? "onnx_model_inference" : optionsMap["onnx-environment-name"].c_str())); + pImplOrt->session = std::make_shared(*(pImplOrt->env), modelPath.c_str(), pImplOrt->sessionOptions); + + for (size_t i = 0; i < (pImplOrt->session)->GetInputCount(); ++i) { + mInputNames.push_back((pImplOrt->session)->GetInputNameAllocated(i, pImplOrt->allocator).get()); + } + for (size_t i = 0; i < (pImplOrt->session)->GetInputCount(); ++i) { + mInputShapes.emplace_back((pImplOrt->session)->GetInputTypeInfo(i).GetTensorTypeAndShapeInfo().GetShape()); + } + for (size_t i = 0; i < (pImplOrt->session)->GetOutputCount(); ++i) { + mOutputNames.push_back((pImplOrt->session)->GetOutputNameAllocated(i, pImplOrt->allocator).get()); + } + for (size_t i = 0; i < (pImplOrt->session)->GetOutputCount(); ++i) { + mOutputShapes.emplace_back((pImplOrt->session)->GetOutputTypeInfo(i).GetTensorTypeAndShapeInfo().GetShape()); + } + + inputNamesChar.resize(mInputNames.size(), nullptr); + std::transform(std::begin(mInputNames), std::end(mInputNames), std::begin(inputNamesChar), + [&](const std::string& str) { return str.c_str(); }); + outputNamesChar.resize(mOutputNames.size(), nullptr); + std::transform(std::begin(mOutputNames), std::end(mOutputNames), std::begin(outputNamesChar), + [&](const std::string& str) { return str.c_str(); }); + + // Print names + if (loggingLevel > 1) { + LOG(info) << "Input Nodes:"; + for (size_t i = 0; i < mInputNames.size(); i++) { + LOG(info) << "\t" << mInputNames[i] << " : " << printShape(mInputShapes[i]); + } + + LOG(info) << "Output Nodes:"; + for (size_t i = 0; i < mOutputNames.size(); i++) { + LOG(info) << "\t" << mOutputNames[i] << " : " << printShape(mOutputShapes[i]); + } + } +} + +void OrtModel::resetSession() +{ + pImplOrt->session = std::make_shared(*(pImplOrt->env), modelPath.c_str(), pImplOrt->sessionOptions); +} + +template +std::vector OrtModel::v2v(std::vector& input, bool clearInput) +{ + if constexpr (std::is_same_v) { + return input; + } else { + std::vector output(input.size()); + std::transform(std::begin(input), std::end(input), std::begin(output), [](I f) { return O(f); }); + if (clearInput) { + input.clear(); + } + return output; + } +} + +template // class I is the input data type, e.g. float, class O is the output data type, e.g. O2::gpu::OrtDataType::Float16_t from O2/GPU/GPUTracking/ML/convert_float16.h +std::vector OrtModel::inference(std::vector& input) +{ + std::vector inputShape{(int64_t)(input.size() / mInputShapes[0][1]), (int64_t)mInputShapes[0][1]}; + std::vector inputTensor; + inputTensor.emplace_back(Ort::Value::CreateTensor(pImplOrt->memoryInfo, reinterpret_cast(input.data()), input.size(), inputShape.data(), inputShape.size())); + // input.clear(); + auto outputTensors = (pImplOrt->session)->Run(pImplOrt->runOptions, inputNamesChar.data(), inputTensor.data(), inputTensor.size(), outputNamesChar.data(), outputNamesChar.size()); + O* outputValues = reinterpret_cast(outputTensors[0].template GetTensorMutableData()); + std::vector outputValuesVec{outputValues, outputValues + inputShape[0] * mOutputShapes[0][1]}; + outputTensors.clear(); + return outputValuesVec; +} + +template // class I is the input data type, e.g. float, class O is the output data type, e.g. O2::gpu::OrtDataType::Float16_t from O2/GPU/GPUTracking/ML/convert_float16.h +std::vector OrtModel::inference(std::vector>& input) +{ + std::vector inputTensor; + for (auto i : input) { + std::vector inputShape{(int64_t)(i.size() / mInputShapes[0][1]), (int64_t)mInputShapes[0][1]}; + inputTensor.emplace_back(Ort::Value::CreateTensor(pImplOrt->memoryInfo, reinterpret_cast(i.data()), i.size(), inputShape.data(), inputShape.size())); + } + // input.clear(); + auto outputTensors = (pImplOrt->session)->Run(pImplOrt->runOptions, inputNamesChar.data(), inputTensor.data(), inputTensor.size(), outputNamesChar.data(), outputNamesChar.size()); + O* outputValues = reinterpret_cast(outputTensors[0].template GetTensorMutableData()); + std::vector outputValuesVec{outputValues, outputValues + inputTensor.size() / mInputShapes[0][1] * mOutputShapes[0][1]}; + outputTensors.clear(); + return outputValuesVec; +} + +std::string OrtModel::printShape(const std::vector& v) +{ + std::stringstream ss(""); + for (size_t i = 0; i < v.size() - 1; i++) { + ss << v[i] << "x"; + } + ss << v[v.size() - 1]; + return ss.str(); +} + +template <> +std::vector OrtModel::inference(std::vector& input) +{ + std::vector inputShape{(int64_t)(input.size() / mInputShapes[0][1]), (int64_t)mInputShapes[0][1]}; + std::vector inputTensor; + inputTensor.emplace_back(Ort::Value::CreateTensor(pImplOrt->memoryInfo, input.data(), input.size(), inputShape.data(), inputShape.size())); + // input.clear(); + auto outputTensors = (pImplOrt->session)->Run(pImplOrt->runOptions, inputNamesChar.data(), inputTensor.data(), inputTensor.size(), outputNamesChar.data(), outputNamesChar.size()); + float* outputValues = outputTensors[0].template GetTensorMutableData(); + std::vector outputValuesVec{outputValues, outputValues + inputShape[0] * mOutputShapes[0][1]}; + outputTensors.clear(); + return outputValuesVec; +} + +template <> +std::vector OrtModel::inference(std::vector& input) +{ + std::vector inputShape{(int64_t)(input.size() / mInputShapes[0][1]), (int64_t)mInputShapes[0][1]}; + std::vector inputTensor; + inputTensor.emplace_back(Ort::Value::CreateTensor(pImplOrt->memoryInfo, reinterpret_cast(input.data()), input.size(), inputShape.data(), inputShape.size())); + // input.clear(); + auto outputTensors = (pImplOrt->session)->Run(pImplOrt->runOptions, inputNamesChar.data(), inputTensor.data(), inputTensor.size(), outputNamesChar.data(), outputNamesChar.size()); + float* outputValues = outputTensors[0].template GetTensorMutableData(); + std::vector outputValuesVec{outputValues, outputValues + inputShape[0] * mOutputShapes[0][1]}; + outputTensors.clear(); + return outputValuesVec; +} + +template <> +std::vector OrtModel::inference(std::vector& input) +{ + std::vector inputShape{(int64_t)(input.size() / mInputShapes[0][1]), (int64_t)mInputShapes[0][1]}; + std::vector inputTensor; + inputTensor.emplace_back(Ort::Value::CreateTensor(pImplOrt->memoryInfo, reinterpret_cast(input.data()), input.size(), inputShape.data(), inputShape.size())); + // input.clear(); + auto outputTensors = (pImplOrt->session)->Run(pImplOrt->runOptions, inputNamesChar.data(), inputTensor.data(), inputTensor.size(), outputNamesChar.data(), outputNamesChar.size()); + OrtDataType::Float16_t* outputValues = reinterpret_cast(outputTensors[0].template GetTensorMutableData()); + std::vector outputValuesVec{outputValues, outputValues + inputShape[0] * mOutputShapes[0][1]}; + outputTensors.clear(); + return outputValuesVec; +} + +template <> +std::vector OrtModel::inference(std::vector& input) +{ + std::vector inputShape{(int64_t)(input.size() / mInputShapes[0][1]), (int64_t)mInputShapes[0][1]}; + std::vector inputTensor; + inputTensor.emplace_back(Ort::Value::CreateTensor(pImplOrt->memoryInfo, reinterpret_cast(input.data()), input.size(), inputShape.data(), inputShape.size())); + // input.clear(); + auto outputTensors = (pImplOrt->session)->Run(pImplOrt->runOptions, inputNamesChar.data(), inputTensor.data(), inputTensor.size(), outputNamesChar.data(), outputNamesChar.size()); + OrtDataType::Float16_t* outputValues = reinterpret_cast(outputTensors[0].template GetTensorMutableData()); + std::vector outputValuesVec{outputValues, outputValues + inputShape[0] * mOutputShapes[0][1]}; + outputTensors.clear(); + return outputValuesVec; +} + +template <> +std::vector OrtModel::inference(std::vector>& input) +{ + std::vector inputTensor; + for (auto i : input) { + std::vector inputShape{(int64_t)(i.size() / mInputShapes[0][1]), (int64_t)mInputShapes[0][1]}; + inputTensor.emplace_back(Ort::Value::CreateTensor(pImplOrt->memoryInfo, reinterpret_cast(i.data()), i.size(), inputShape.data(), inputShape.size())); + } + // input.clear(); + auto outputTensors = (pImplOrt->session)->Run(pImplOrt->runOptions, inputNamesChar.data(), inputTensor.data(), inputTensor.size(), outputNamesChar.data(), outputNamesChar.size()); + OrtDataType::Float16_t* outputValues = reinterpret_cast(outputTensors[0].template GetTensorMutableData()); + std::vector outputValuesVec{outputValues, outputValues + inputTensor.size() / mInputShapes[0][1] * mOutputShapes[0][1]}; + outputTensors.clear(); + return outputValuesVec; +} + +} // namespace ml + +} // namespace o2 From 0c734187a7f2f5626e3a5916c62b5ea2385108f9 Mon Sep 17 00:00:00 2001 From: Ernst Hellbar Date: Fri, 15 Nov 2024 12:37:52 +0100 Subject: [PATCH 0086/2180] register available_managed_shm metric only for readout-proxy --- Framework/Core/src/CommonServices.cxx | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/Framework/Core/src/CommonServices.cxx b/Framework/Core/src/CommonServices.cxx index bc750181d54e2..23375b76487b9 100644 --- a/Framework/Core/src/CommonServices.cxx +++ b/Framework/Core/src/CommonServices.cxx @@ -768,8 +768,11 @@ auto sendRelayerMetrics(ServiceRegistryRef registry, DataProcessingStats& stats) using namespace fair::mq::shmem; auto& spec = registry.get(); + auto hasMetric = [&runningWorkflow](const DataProcessingStats::MetricSpec& metric) -> bool { + return metric.metricId == static_cast(ProcessingStatsId::AVAILABLE_MANAGED_SHM_BASE) + (runningWorkflow.shmSegmentId % 512); + }; // FIXME: Ugly, but we do it only every 5 seconds... - if (spec.name == "readout-proxy") { + if (std::find_if(stats.metricSpecs.begin(), stats.metricSpecs.end(), hasMetric) != stats.metricSpecs.end()) { auto device = registry.get().device(); long freeMemory = -1; try { @@ -1105,6 +1108,9 @@ o2::framework::ServiceSpec CommonServices::dataProcessingStats() .sendInitialValue = true}}; for (auto& metric : metrics) { + if (metric.metricId == (int)ProcessingStatsId::AVAILABLE_MANAGED_SHM_BASE + (runningWorkflow.shmSegmentId % 512) && spec.name.compare("readout-proxy") != 0) { + continue; + } stats->registerMetric(metric); } From 32451dc4cc345ccbaf2f6bbcd6594e4cf5b2a911 Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Fri, 22 Nov 2024 15:32:52 +0100 Subject: [PATCH 0087/2180] DPL Analysis: add tf-offset option to mctracks-to-aod converter (#13711) --- run/o2sim_mctracks_to_aod.cxx | 21 ++++++++++++--------- 1 file changed, 12 insertions(+), 9 deletions(-) diff --git a/run/o2sim_mctracks_to_aod.cxx b/run/o2sim_mctracks_to_aod.cxx index 74807fd2a3ce9..f7a85e62a3f9b 100644 --- a/run/o2sim_mctracks_to_aod.cxx +++ b/run/o2sim_mctracks_to_aod.cxx @@ -49,14 +49,13 @@ struct MctracksToAod { "Interaction rate to simulate"}; Configurable filt{"filter-mctracks", false, "Filter tracks"}; + Configurable tfOffset{"tf-offset", 0, "Start TF counter from an offset"}; /** @} */ /** Number of timeframes */ uint64_t mTimeFrame = 0; /** Interaction simulation */ InteractionSampler mSampler; - /** Whether to filter tracks */ - bool mFilter; /** Initialize */ void init(o2::framework::InitContext& /*ic*/) @@ -64,13 +63,14 @@ struct MctracksToAod { mSampler.setInteractionRate(IR); mSampler.setFirstIR({0, 0}); mSampler.init(); - mFilter = filt; + + mTimeFrame = tfOffset; } /** Run the conversion */ void run(o2::framework::ProcessingContext& pc) { - LOG(info) << "=== 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; @@ -78,11 +78,17 @@ struct MctracksToAod { auto nParts = pc.inputs().getNofParts(0); auto nPartsVerify = pc.inputs().getNofParts(1); + + using o2::framework::Lifetime; + using o2::framework::Output; + if (nParts != nPartsVerify) { LOG(warn) << "Mismatch between number of MC headers and " << "number of track vectors: " << nParts << " != " << nPartsVerify << ", shipping the empty timeframe"; + pc.outputs().snapshot(Output{"TFF", "TFFilename", 0}, ""); + pc.outputs().snapshot(Output{"TFN", "TFNumber", 0}, ++mTimeFrame); return; } // TODO: include BC simulation @@ -115,18 +121,15 @@ struct MctracksToAod { tracks, preselect, offset, - mFilter, + (bool)filt, false); LOG(debug) << "Increment BC counter"; bcCounter++; } - using o2::framework::Lifetime; - using o2::framework::Output; - ++mTimeFrame; pc.outputs().snapshot(Output{"TFF", "TFFilename", 0}, ""); - pc.outputs().snapshot(Output{"TFN", "TFNumber", 0}, mTimeFrame); + pc.outputs().snapshot(Output{"TFN", "TFNumber", 0}, ++mTimeFrame); } }; From 20973f4f92cf1fc7a881ebc03a078263b97d31fa Mon Sep 17 00:00:00 2001 From: shahoian Date: Thu, 21 Nov 2024 18:46:30 +0100 Subject: [PATCH 0088/2180] Add TPC sh.clusters info to study output --- .../study/include/GlobalTrackingStudy/TrackInfoExt.h | 3 ++- Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx | 4 ++++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackInfoExt.h b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackInfoExt.h index ea79d5d4a2c92..6fd06e5265946 100644 --- a/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackInfoExt.h +++ b/Detectors/GlobalTrackingWorkflow/study/include/GlobalTrackingStudy/TrackInfoExt.h @@ -41,6 +41,7 @@ struct TrackInfoExt { float q2ptITSTPC = 0.f; float q2ptITSTPCTRD = 0.f; uint16_t nClTPC = 0; + uint16_t nClTPCShared = 0; uint8_t pattITS = 0; uint8_t nClITS = 0; uint8_t rowMinTPC = 0; @@ -54,7 +55,7 @@ struct TrackInfoExt { float getTPCInY0() const { return innerTPCPos0[1]; } float getTPCInZ0() const { return innerTPCPos0[2]; } - ClassDefNV(TrackInfoExt, 3); + ClassDefNV(TrackInfoExt, 4); }; } // namespace dataformats diff --git a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx index 5a67bd344f271..ba453b944a742 100644 --- a/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx +++ b/Detectors/GlobalTrackingWorkflow/study/src/TrackingStudy.cxx @@ -250,6 +250,7 @@ void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) auto fillTPCClInfo = [&recoData, this](const o2::tpc::TrackTPC& trc, o2::dataformats::TrackInfoExt& trExt, float timestampTB = -1e9) { const auto clRefs = recoData.getTPCTracksClusterRefs(); + const auto shMap = recoData.clusterShMapTPC.data(); if (recoData.inputsTPCclusters) { uint8_t clSect = 0, clRow = 0, clRowP = -1; uint32_t clIdx = 0; @@ -259,6 +260,9 @@ void TrackingStudySpec::process(o2::globaltracking::RecoContainer& recoData) trExt.rowCountTPC++; clRowP = clRow; } + if (shMap[clRefs[ic + trc.getClusterRef().getFirstEntry()]]) { + trExt.nClTPCShared++; + } } trc.getClusterReference(clRefs, trc.getNClusterReferences() - 1, clSect, clRow, clIdx); trExt.rowMinTPC = clRow; From ce63ff8c1c90453fa45b8b97eb03c02075c70469 Mon Sep 17 00:00:00 2001 From: shahoian Date: Wed, 20 Nov 2024 15:58:31 +0100 Subject: [PATCH 0089/2180] Object to provide TB to cut due to the Altro sync. procedure --- DataFormats/Detectors/TPC/CMakeLists.txt | 3 +- .../include/DataFormatsTPC/AltroSyncSignal.h | 30 +++++++++++++++++++ .../Detectors/TPC/src/DataFormatsTPCLinkDef.h | 1 + 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 DataFormats/Detectors/TPC/include/DataFormatsTPC/AltroSyncSignal.h diff --git a/DataFormats/Detectors/TPC/CMakeLists.txt b/DataFormats/Detectors/TPC/CMakeLists.txt index b2f9eb9e53e85..b8b93c308e85d 100644 --- a/DataFormats/Detectors/TPC/CMakeLists.txt +++ b/DataFormats/Detectors/TPC/CMakeLists.txt @@ -63,7 +63,8 @@ o2_target_root_dictionary( include/DataFormatsTPC/VDriftCorrFact.h include/DataFormatsTPC/CalibdEdxCorrection.h include/DataFormatsTPC/BetheBlochAleph.h - include/DataFormatsTPC/PIDResponse.h) + include/DataFormatsTPC/PIDResponse.h + include/DataFormatsTPC/AltroSyncSignal.h) o2_add_test( ClusterNative diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/AltroSyncSignal.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/AltroSyncSignal.h new file mode 100644 index 0000000000000..d98dd1c5f6eff --- /dev/null +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/AltroSyncSignal.h @@ -0,0 +1,30 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// \file AltroSyncSignal.h +/// \brief Definition of the timebin from which syncronization starts + +#include "GPUCommonRtypes.h" + +namespace o2::tpc +{ +struct AltroSyncSignal { + int periodTF = 10; // signal repeats every period-th TF + int timebin = 141192.; // every 10 TF, orbit 31, Time bin 384, BC 4 -> 141195, but clusters can be affected before that + + int getTB2Cut(uint32_t tfCounter) const + { + return periodTF > 0 && (tfCounter % periodTF) == 1 && tfCounter > periodTF ? timebin : -1; + } + + ClassDefNV(AltroSyncSignal, 1); +}; +} // namespace o2::tpc diff --git a/DataFormats/Detectors/TPC/src/DataFormatsTPCLinkDef.h b/DataFormats/Detectors/TPC/src/DataFormatsTPCLinkDef.h index 676a4e0144be0..f248a74950a1f 100644 --- a/DataFormats/Detectors/TPC/src/DataFormatsTPCLinkDef.h +++ b/DataFormats/Detectors/TPC/src/DataFormatsTPCLinkDef.h @@ -73,5 +73,6 @@ #pragma link C++ class o2::tpc::TriggerWordDLBZS + ; #pragma link C++ class o2::tpc::TriggerInfoDLBZS + ; #pragma link C++ class std::vector < o2::tpc::TriggerInfoDLBZS> + ; +#pragma link C++ class o2::tpc::AltroSyncSignal + ; #endif From 580d660289f73e517a43023472357c835dceceb1 Mon Sep 17 00:00:00 2001 From: shahoian Date: Sun, 24 Nov 2024 17:19:09 +0100 Subject: [PATCH 0090/2180] fix typo: remove stray . in the int value --- .../Detectors/TPC/include/DataFormatsTPC/AltroSyncSignal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/AltroSyncSignal.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/AltroSyncSignal.h index d98dd1c5f6eff..0717c7bcbb02c 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/AltroSyncSignal.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/AltroSyncSignal.h @@ -18,7 +18,7 @@ namespace o2::tpc { struct AltroSyncSignal { int periodTF = 10; // signal repeats every period-th TF - int timebin = 141192.; // every 10 TF, orbit 31, Time bin 384, BC 4 -> 141195, but clusters can be affected before that + int timebin = 141192; // every 10 TF, orbit 31, Time bin 384, BC 4 -> 141195, but clusters can be affected before that int getTB2Cut(uint32_t tfCounter) const { From 567d25ac1880faac811dcbaa6b30246f28e2d176 Mon Sep 17 00:00:00 2001 From: shahoian Date: Sun, 24 Nov 2024 18:01:08 +0100 Subject: [PATCH 0091/2180] Fix another typo in AltroSyncSignal::timebin default value --- .../Detectors/TPC/include/DataFormatsTPC/AltroSyncSignal.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/AltroSyncSignal.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/AltroSyncSignal.h index 0717c7bcbb02c..6dee49e4ed6c6 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/AltroSyncSignal.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/AltroSyncSignal.h @@ -18,7 +18,7 @@ namespace o2::tpc { struct AltroSyncSignal { int periodTF = 10; // signal repeats every period-th TF - int timebin = 141192; // every 10 TF, orbit 31, Time bin 384, BC 4 -> 141195, but clusters can be affected before that + int timebin = 14192; // every 10 TF, orbit 31, Time bin 384, BC 4 -> 14195, but clusters can be affected before that int getTB2Cut(uint32_t tfCounter) const { From 6cfdeaeaca0f2a1f112e7fd4b2310e3d55652e96 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Sun, 24 Nov 2024 19:21:28 +0100 Subject: [PATCH 0092/2180] DPL: improve arrow::Dataset integration (#13725) - Modularise filesystem to allow easier navigation and support for multiple formats. - Add initial support to multiplex multiple tables on top of the same tree. - Improve support for writing boolean fields. --- .../include/Framework/RootArrowFilesystem.h | 36 ++++- Framework/Core/src/RootArrowFilesystem.cxx | 153 +++++++++++++----- Framework/Core/test/test_Root2ArrowTable.cxx | 146 ++++++++++++----- 3 files changed, 250 insertions(+), 85 deletions(-) diff --git a/Framework/Core/include/Framework/RootArrowFilesystem.h b/Framework/Core/include/Framework/RootArrowFilesystem.h index 7c8385ccd2b9d..48d817bc9ddf2 100644 --- a/Framework/Core/include/Framework/RootArrowFilesystem.h +++ b/Framework/Core/include/Framework/RootArrowFilesystem.h @@ -17,6 +17,8 @@ #include #include +class TFile; +class TBranch; class TTree; class TBufferFile; class TDirectoryFile; @@ -227,11 +229,38 @@ class TTreeFileFormat : public arrow::dataset::FileFormat const std::shared_ptr& fragment) const override; }; -// An arrow outputstream which allows to write to a ttree +// An arrow outputstream which allows to write to a TDirectoryFile. +// This will point to the location of the file itself. You can +// specify the location of the actual object inside it by passing the +// associated path to the Write() API. +class TDirectoryFileOutputStream : public arrow::io::OutputStream +{ + public: + TDirectoryFileOutputStream(TDirectoryFile*); + + arrow::Status Close() override; + + arrow::Result Tell() const override; + + arrow::Status Write(const void* data, int64_t nbytes) override; + + bool closed() const override; + + TDirectoryFile* GetDirectory() + { + return mDirectory; + } + + private: + TDirectoryFile* mDirectory; +}; + +// An arrow outputstream which allows to write to a TTree. Eventually +// with a prefix for the branches. class TTreeOutputStream : public arrow::io::OutputStream { public: - TTreeOutputStream(TTree* t); + TTreeOutputStream(TTree*, std::string branchPrefix); arrow::Status Close() override; @@ -241,6 +270,8 @@ class TTreeOutputStream : public arrow::io::OutputStream bool closed() const override; + TBranch* CreateBranch(char const* branchName, char const* sizeBranch); + TTree* GetTree() { return mTree; @@ -248,6 +279,7 @@ class TTreeOutputStream : public arrow::io::OutputStream private: TTree* mTree; + std::string mBranchPrefix; }; } // namespace o2::framework diff --git a/Framework/Core/src/RootArrowFilesystem.cxx b/Framework/Core/src/RootArrowFilesystem.cxx index 7581ee57e5b9f..7e331814272a6 100644 --- a/Framework/Core/src/RootArrowFilesystem.cxx +++ b/Framework/Core/src/RootArrowFilesystem.cxx @@ -17,7 +17,6 @@ #include #include #include -#include #include #include #include @@ -28,8 +27,11 @@ #include #include #include +#include #include +#include +#include O2_DECLARE_DYNAMIC_LOG(root_arrow_fs); @@ -100,7 +102,6 @@ std::shared_ptr TFileFileSystem::GetSubFilesystem(arr return std::shared_ptr(new SingleTreeFileSystem(tree)); } - auto directory = (TDirectoryFile*)mFile->GetObjectChecked(source.path().c_str(), TClass::GetClass()); if (directory) { return std::shared_ptr(new TFileFileSystem(directory, 50 * 1024 * 1024)); @@ -129,8 +130,15 @@ arrow::Result> TFileFileSystem::OpenOut const std::string& path, const std::shared_ptr& metadata) { - auto* t = new TTree(path.c_str(), "should put a name here"); - auto stream = std::make_shared(t); + if (path == "/") { + return std::make_shared(this->GetFile()); + } + + auto* dir = dynamic_cast(this->GetFile()->Get(path.c_str())); + if (!dir) { + throw runtime_error_f("Unable to open directory %s in file %s", path.c_str(), GetFile()->GetName()); + } + auto stream = std::make_shared(dir); return stream; } @@ -286,13 +294,46 @@ arrow::Result> TTreeFileFormat::Ma } // An arrow outputstream which allows to write to a ttree -TTreeOutputStream::TTreeOutputStream(TTree* t) - : mTree(t) +TDirectoryFileOutputStream::TDirectoryFileOutputStream(TDirectoryFile* f) + : mDirectory(f) +{ +} + +arrow::Status TDirectoryFileOutputStream::Close() +{ + mDirectory->GetFile()->Close(); + return arrow::Status::OK(); +} + +arrow::Result TDirectoryFileOutputStream::Tell() const +{ + return arrow::Result(arrow::Status::NotImplemented("Cannot move")); +} + +arrow::Status TDirectoryFileOutputStream::Write(const void* data, int64_t nbytes) +{ + return arrow::Status::NotImplemented("Cannot write raw bytes to a TTree"); +} + +bool TDirectoryFileOutputStream::closed() const +{ + return mDirectory->GetFile()->IsOpen() == false; +} + +// An arrow outputstream which allows to write to a ttree +// @a branch prefix is to be used to identify a set of branches which all belong to +// the same table. +TTreeOutputStream::TTreeOutputStream(TTree* f, std::string branchPrefix) + : mTree(f), + mBranchPrefix(std::move(branchPrefix)) { } arrow::Status TTreeOutputStream::Close() { + if (mTree->GetCurrentFile() == nullptr) { + return arrow::Status::Invalid("Cannot close a tree not attached to a file"); + } mTree->GetCurrentFile()->Close(); return arrow::Status::OK(); } @@ -309,9 +350,18 @@ arrow::Status TTreeOutputStream::Write(const void* data, int64_t nbytes) bool TTreeOutputStream::closed() const { + // A standalone tree is never closed. + if (mTree->GetCurrentFile() == nullptr) { + return false; + } return mTree->GetCurrentFile()->IsOpen() == false; } +TBranch* TTreeOutputStream::CreateBranch(char const* branchName, char const* sizeBranch) +{ + return mTree->Branch((mBranchPrefix + "/" + branchName).c_str(), (char*)nullptr, (mBranchPrefix + sizeBranch).c_str()); +} + char const* rootSuffixFromArrow(arrow::Type::type id) { switch (id) { @@ -411,8 +461,24 @@ class TTreeFileWriter : public arrow::dataset::FileWriter : FileWriter(schema, options, destination, destination_locator) { // Batches have the same number of entries for each column. + auto directoryStream = std::dynamic_pointer_cast(destination_); auto treeStream = std::dynamic_pointer_cast(destination_); - TTree* tree = treeStream->GetTree(); + + if (directoryStream.get()) { + TDirectoryFile* dir = directoryStream->GetDirectory(); + dir->cd(); + auto* tree = new TTree(destination_locator_.path.c_str(), ""); + treeStream = std::make_shared(tree, ""); + } else if (treeStream.get()) { + // We already have a tree stream, let's derive a new one + // with the destination_locator_.path as prefix for the branches + // This way we can multiplex multiple tables in the same tree. + auto tree = treeStream->GetTree(); + treeStream = std::make_shared(tree, destination_locator_.path); + } else { + // I could simply set a prefix here to merge to an already existing tree. + throw std::runtime_error("Unsupported backend."); + } for (auto i = 0u; i < schema->fields().size(); ++i) { auto& field = schema->field(i); @@ -427,15 +493,15 @@ class TTreeFileWriter : public arrow::dataset::FileWriter valueTypes.push_back(field->type()->field(0)->type()); sizesBranches.push_back(nullptr); std::string leafList = fmt::format("{}[{}]{}", field->name(), listSizes.back(), rootSuffixFromArrow(valueTypes.back()->id())); - branches.push_back(tree->Branch(field->name().c_str(), (char*)nullptr, leafList.c_str())); + branches.push_back(treeStream->CreateBranch(field->name().c_str(), leafList.c_str())); } break; case arrow::Type::LIST: { valueTypes.push_back(field->type()->field(0)->type()); listSizes.back() = 0; // VLA, we need to calculate it on the fly; std::string leafList = fmt::format("{}[{}_size]{}", field->name(), field->name(), rootSuffixFromArrow(valueTypes.back()->id())); std::string sizeLeafList = field->name() + "_size/I"; - sizesBranches.push_back(tree->Branch((field->name() + "_size").c_str(), (char*)nullptr, sizeLeafList.c_str())); - branches.push_back(tree->Branch(field->name().c_str(), (char*)nullptr, leafList.c_str())); + sizesBranches.push_back(treeStream->CreateBranch((field->name() + "_size").c_str(), sizeLeafList.c_str())); + branches.push_back(treeStream->CreateBranch(field->name().c_str(), leafList.c_str())); // Notice that this could be replaced by a better guess of the // average size of the list elements, but this is not trivial. } break; @@ -443,7 +509,7 @@ class TTreeFileWriter : public arrow::dataset::FileWriter valueTypes.push_back(field->type()); std::string leafList = field->name() + rootSuffixFromArrow(valueTypes.back()->id()); sizesBranches.push_back(nullptr); - branches.push_back(tree->Branch(field->name().c_str(), (char*)nullptr, leafList.c_str())); + branches.push_back(treeStream->CreateBranch(field->name().c_str(), leafList.c_str())); } break; } } @@ -463,11 +529,18 @@ class TTreeFileWriter : public arrow::dataset::FileWriter } // Batches have the same number of entries for each column. + auto directoryStream = std::dynamic_pointer_cast(destination_); + TTree* tree = nullptr; + if (directoryStream.get()) { + TDirectoryFile* dir = directoryStream->GetDirectory(); + tree = (TTree*)dir->Get(destination_locator_.path.c_str()); + } auto treeStream = std::dynamic_pointer_cast(destination_); - TTree* tree = treeStream->GetTree(); - // Caches for the vectors of bools. - std::vector> caches; + if (!tree) { + // I could simply set a prefix here to merge to an already existing tree. + throw std::runtime_error("Unsupported backend."); + } for (auto i = 0u; i < batch->columns().size(); ++i) { auto column = batch->column(i); @@ -484,24 +557,11 @@ class TTreeFileWriter : public arrow::dataset::FileWriter auto list = std::static_pointer_cast(column); valueArrays.back() = list; } break; - default: - valueArrays.back() = column; - } - } - - int64_t pos = 0; - while (pos < batch->num_rows()) { - for (size_t bi = 0; bi < branches.size(); ++bi) { - auto* branch = branches[bi]; - auto* sizeBranch = sizesBranches[bi]; - auto array = batch->column(bi); - auto& field = batch->schema()->field(bi); - auto& listSize = listSizes[bi]; - auto valueType = valueTypes[bi]; - auto valueArray = valueArrays[bi]; + case arrow::Type::BOOL: { + // In case of arrays of booleans, we need to go back to their + // char based representation for ROOT to save them. + auto boolArray = std::static_pointer_cast(column); - if (field->type()->id() == arrow::Type::BOOL) { - auto boolArray = std::static_pointer_cast(array); int64_t length = boolArray->length(); arrow::UInt8Builder builder; auto ok = builder.Reserve(length); @@ -516,11 +576,24 @@ class TTreeFileWriter : public arrow::dataset::FileWriter auto ok = builder.AppendNull(); } } + valueArrays.back() = *builder.Finish(); + } break; + default: + valueArrays.back() = column; + } + } + + int64_t pos = 0; + while (pos < batch->num_rows()) { + for (size_t bi = 0; bi < branches.size(); ++bi) { + auto* branch = branches[bi]; + auto* sizeBranch = sizesBranches[bi]; + auto array = batch->column(bi); + auto& field = batch->schema()->field(bi); + auto& listSize = listSizes[bi]; + auto valueType = valueTypes[bi]; + auto valueArray = valueArrays[bi]; - ok = builder.Finish(&caches[bi]); - branch->SetAddress((void*)(caches[bi]->values()->data())); - continue; - } switch (field->type()->id()) { case arrow::Type::LIST: { auto list = std::static_pointer_cast(array); @@ -764,13 +837,16 @@ arrow::Result TTreeFileFormat::ScanBatchesAsync( return generator; } - arrow::Result> TTreeFileSystem::OpenOutputStream( const std::string& path, const std::shared_ptr& metadata) { - auto stream = std::make_shared(GetTree({path, shared_from_this()})); - return stream; + arrow::dataset::FileSource source{path, shared_from_this()}; + auto prefix = metadata->Get("branch_prefix"); + if (prefix.ok()) { + return std::make_shared(GetTree(source), *prefix); + } + return std::make_shared(GetTree(source), ""); } TBufferFileFS::TBufferFileFS(TBufferFile* f) @@ -782,7 +858,6 @@ TBufferFileFS::TBufferFileFS(TBufferFile* f) TTreeFileSystem::~TTreeFileSystem() = default; - arrow::Result TBufferFileFS::GetFileInfo(const std::string& path) { arrow::fs::FileInfo result; diff --git a/Framework/Core/test/test_Root2ArrowTable.cxx b/Framework/Core/test/test_Root2ArrowTable.cxx index 03f0977a4c0c4..a659d488ae24a 100644 --- a/Framework/Core/test/test_Root2ArrowTable.cxx +++ b/Framework/Core/test/test_Root2ArrowTable.cxx @@ -20,13 +20,18 @@ #include #include #include +#include #include #include #include #include #include +#include +#include +#include #include +#include #include #include #include @@ -259,6 +264,82 @@ TEST_CASE("RootTree2Fragment") REQUIRE((*result)->num_rows() == 1000); } +bool validateContents(std::shared_ptr batch) +{ + { + auto int_array = std::static_pointer_cast(batch->GetColumnByName("ev")); + REQUIRE(int_array->length() == 100); + for (int64_t j = 0; j < int_array->length(); j++) { + REQUIRE(int_array->Value(j) == j + 1); + } + } + + { + auto list_array = std::static_pointer_cast(batch->GetColumnByName("xyz")); + + REQUIRE(list_array->length() == 100); + // Iterate over the FixedSizeListArray + for (int64_t i = 0; i < list_array->length(); i++) { + auto value_slice = list_array->value_slice(i); + auto float_array = std::static_pointer_cast(value_slice); + + REQUIRE(float_array->Value(0) == 1); + REQUIRE(float_array->Value(1) == 2); + REQUIRE(float_array->Value(2) == i + 1); + } + } + + { + auto list_array = std::static_pointer_cast(batch->GetColumnByName("ij")); + + REQUIRE(list_array->length() == 100); + // Iterate over the FixedSizeListArray + for (int64_t i = 0; i < list_array->length(); i++) { + auto value_slice = list_array->value_slice(i); + auto int_array = std::static_pointer_cast(value_slice); + REQUIRE(int_array->Value(0) == i); + REQUIRE(int_array->Value(1) == i + 1); + } + } + + { + auto bool_array = std::static_pointer_cast(batch->GetColumnByName("bools")); + + REQUIRE(bool_array->length() == 100); + for (int64_t j = 0; j < bool_array->length(); j++) { + REQUIRE(bool_array->Value(j) == (j % 3 == 0)); + } + } + + { + auto list_array = std::static_pointer_cast(batch->GetColumnByName("manyBools")); + + REQUIRE(list_array->length() == 100); + for (int64_t i = 0; i < list_array->length(); i++) { + auto value_slice = list_array->value_slice(i); + auto bool_array = std::static_pointer_cast(value_slice); + REQUIRE(bool_array->Value(0) == (i % 4 == 0)); + REQUIRE(bool_array->Value(1) == (i % 5 == 0)); + } + } + return true; +} + +bool validateSchema(std::shared_ptr schema) +{ + REQUIRE(schema->num_fields() == 9); + REQUIRE(schema->field(0)->type()->id() == arrow::float32()->id()); + REQUIRE(schema->field(1)->type()->id() == arrow::float32()->id()); + REQUIRE(schema->field(2)->type()->id() == arrow::float32()->id()); + REQUIRE(schema->field(3)->type()->id() == arrow::float64()->id()); + REQUIRE(schema->field(4)->type()->id() == arrow::int32()->id()); + REQUIRE(schema->field(5)->type()->id() == arrow::fixed_size_list(arrow::float32(), 3)->id()); + REQUIRE(schema->field(6)->type()->id() == arrow::fixed_size_list(arrow::int32(), 2)->id()); + REQUIRE(schema->field(7)->type()->id() == arrow::boolean()->id()); + REQUIRE(schema->field(8)->type()->id() == arrow::fixed_size_list(arrow::boolean(), 2)->id()); + return true; +} + TEST_CASE("RootTree2Dataset") { using namespace o2::framework; @@ -307,6 +388,9 @@ TEST_CASE("RootTree2Dataset") Float_t px = 0, py = 1, pz = 2; Double_t random; Int_t ev; + bool oneBool; + bool manyBool[2]; + t->Branch("px", &px, "px/F"); t->Branch("py", &py, "py/F"); t->Branch("pz", &pz, "pz/F"); @@ -314,6 +398,8 @@ TEST_CASE("RootTree2Dataset") t->Branch("ev", &ev, "ev/I"); t->Branch("xyz", xyz, "xyz[3]/F"); t->Branch("ij", ij, "ij[2]/I"); + t->Branch("bools", &oneBool, "bools/O"); + t->Branch("manyBools", &manyBool, "manyBools[2]/O"); // fill the tree for (Int_t i = 0; i < 100; i++) { xyz[0] = 1; @@ -326,6 +412,9 @@ TEST_CASE("RootTree2Dataset") ij[1] = i + 1; random = gRandom->Rndm(); ev = i + 1; + oneBool = (i % 3 == 0); + manyBool[0] = (i % 4 == 0); + manyBool[1] = (i % 5 == 0); t->Fill(); } } @@ -339,7 +428,7 @@ TEST_CASE("RootTree2Dataset") auto schemaOpt = format->Inspect(source); REQUIRE(schemaOpt.ok()); auto schema = *schemaOpt; - REQUIRE(schema->num_fields() == 7); + REQUIRE(schema->num_fields() == 9); REQUIRE(schema->field(0)->type()->id() == arrow::float32()->id()); REQUIRE(schema->field(1)->type()->id() == arrow::float32()->id()); REQUIRE(schema->field(2)->type()->id() == arrow::float32()->id()); @@ -347,6 +436,9 @@ TEST_CASE("RootTree2Dataset") REQUIRE(schema->field(4)->type()->id() == arrow::int32()->id()); REQUIRE(schema->field(5)->type()->id() == arrow::fixed_size_list(arrow::float32(), 3)->id()); REQUIRE(schema->field(6)->type()->id() == arrow::fixed_size_list(arrow::int32(), 2)->id()); + REQUIRE(schema->field(7)->type()->id() == arrow::boolean()->id()); + REQUIRE(schema->field(8)->type()->id() == arrow::fixed_size_list(arrow::boolean(), 2)->id()); + auto fragment = format->MakeFragment(source, {}, schema); REQUIRE(fragment.ok()); auto options = std::make_shared(); @@ -356,7 +448,7 @@ TEST_CASE("RootTree2Dataset") auto batches = (*scanner)(); auto result = batches.result(); REQUIRE(result.ok()); - REQUIRE((*result)->columns().size() == 7); + REQUIRE((*result)->columns().size() == 9); REQUIRE((*result)->num_rows() == 100); { @@ -394,14 +486,16 @@ TEST_CASE("RootTree2Dataset") auto* output = new TMemFile("foo", "RECREATE"); auto outFs = std::make_shared(output, 0); - arrow::fs::FileLocator locator{outFs, "/DF_3"}; - auto destination = outFs->OpenOutputStream(locator.path, {}); + // Open a stream at toplevel + auto destination = outFs->OpenOutputStream("/", {}); REQUIRE(destination.ok()); + // Write to the /DF_3 tree at top level + arrow::fs::FileLocator locator{outFs, "/DF_3"}; auto writer = format->MakeWriter(*destination, schema, {}, locator); auto success = writer->get()->Write(*result); - auto rootDestination = std::dynamic_pointer_cast(*destination); + auto rootDestination = std::dynamic_pointer_cast(*destination); REQUIRE(success.ok()); // Let's read it back... @@ -413,14 +507,7 @@ TEST_CASE("RootTree2Dataset") auto schemaOptWritten = format->Inspect(source); REQUIRE(schemaOptWritten.ok()); auto schemaWritten = *schemaOptWritten; - REQUIRE(schemaWritten->num_fields() == 7); - REQUIRE(schemaWritten->field(0)->type()->id() == arrow::float32()->id()); - REQUIRE(schemaWritten->field(1)->type()->id() == arrow::float32()->id()); - REQUIRE(schemaWritten->field(2)->type()->id() == arrow::float32()->id()); - REQUIRE(schemaWritten->field(3)->type()->id() == arrow::float64()->id()); - REQUIRE(schemaWritten->field(4)->type()->id() == arrow::int32()->id()); - REQUIRE(schemaWritten->field(5)->type()->id() == arrow::fixed_size_list(arrow::float32(), 3)->id()); - REQUIRE(schemaWritten->field(6)->type()->id() == arrow::fixed_size_list(arrow::int32(), 2)->id()); + REQUIRE(validateSchema(schemaWritten)); auto fragmentWritten = format->MakeFragment(source, {}, schema); REQUIRE(fragmentWritten.ok()); @@ -431,39 +518,10 @@ TEST_CASE("RootTree2Dataset") auto batchesWritten = (*scanner)(); auto resultWritten = batches.result(); REQUIRE(resultWritten.ok()); - REQUIRE((*resultWritten)->columns().size() == 7); + REQUIRE((*resultWritten)->columns().size() == 9); REQUIRE((*resultWritten)->num_rows() == 100); + validateContents(*resultWritten); { - auto int_array = std::static_pointer_cast((*resultWritten)->GetColumnByName("ev")); - for (int64_t j = 0; j < int_array->length(); j++) { - REQUIRE(int_array->Value(j) == j + 1); - } - } - - { - auto list_array = std::static_pointer_cast((*result)->GetColumnByName("xyz")); - - // Iterate over the FixedSizeListArray - for (int64_t i = 0; i < list_array->length(); i++) { - auto value_slice = list_array->value_slice(i); - auto float_array = std::static_pointer_cast(value_slice); - - REQUIRE(float_array->Value(0) == 1); - REQUIRE(float_array->Value(1) == 2); - REQUIRE(float_array->Value(2) == i + 1); - } - } - - { - auto list_array = std::static_pointer_cast((*result)->GetColumnByName("ij")); - - // Iterate over the FixedSizeListArray - for (int64_t i = 0; i < list_array->length(); i++) { - auto value_slice = list_array->value_slice(i); - auto int_array = std::static_pointer_cast(value_slice); - REQUIRE(int_array->Value(0) == i); - REQUIRE(int_array->Value(1) == i + 1); - } } } From 4f7d71b64ef2a208e49afa8d4aa6a65bd0ef247a Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sun, 24 Nov 2024 18:08:34 +0100 Subject: [PATCH 0093/2180] GPU: fix bug when filling subthreshold clusters --- GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx b/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx index 0b1c282f3b2f0..d817278404534 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx @@ -223,7 +223,7 @@ GPUd() bool GPUTPCGMTrackParam::Fit(GPUTPCGMMerger* GPUrestrict() merger, int32_ bool dodEdx = param.par.dodEdx && param.dodEdxDownscaled && param.rec.tpc.adddEdxSubThresholdClusters && iWay == nWays - 1 && CAMath::Abs(cluster.row - lastRow) == 2 && cluster.leg == clusters[maxN - 1].leg; dodEdx = AttachClustersPropagate(merger, cluster.slice, lastRow, cluster.row, iTrk, cluster.leg == clusters[maxN - 1].leg, prop, inFlyDirection, GPUCA_MAX_SIN_PHI, dodEdx); if (dodEdx) { - dEdx.fillSubThreshold(lastRow - 1, param); + dEdx.fillSubThreshold(lastRow - wayDirection, param); } } From 5c590eb06f53a038928950fa8cb858280325ca6d Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Mon, 25 Nov 2024 10:44:46 +0100 Subject: [PATCH 0094/2180] ITS: TrackExtensionStudy (#13568) * ITS: Tracker add constexpr Signed-off-by: Felix Schlepper * ITS: TrackExtension fix dangling reference on vector reallocation Signed-off-by: Felix Schlepper * ITS: TrackExtensionStudy add missed/empty cluster patterns Signed-off-by: Felix Schlepper * ITS: TrackExtensionStudy require MC Signed-off-by: Felix Schlepper * ITS: TrackExtensionStudy Efficiencies Signed-off-by: Felix Schlepper * ITS: TrackerTraits getBinsRect mark as const Signed-off-by: Felix Schlepper * ITS: TrackerTraits mark getEmptyBinsRect as consteval Avoid invalid read on first call. Signed-off-by: Felix Schlepper * ITS: TrackerTraits move function for readability Signed-off-by: Felix Schlepper * ITS: TrackExtensionStudy more plots * ITS: TrackerTraits: getBinsRect check layer requested * ITS: TrackExtension allow steering of cuts&directions * ITS: TrackExtension switch to fmt for macos --------- Signed-off-by: Felix Schlepper --- .../ITS/postprocessing/studies/CMakeLists.txt | 4 +- .../include/ITSStudies/TrackExtension.h | 2 +- .../studies/macros/CMakeLists.txt | 16 + .../studies/macros/PostTrackExtension.notest | 629 ++++++++++++++++++ .../studies/src/TrackExtension.cxx | 358 +++++++--- .../standalone-postprocessing-workflow.cxx | 7 +- .../include/ITStracking/Configuration.h | 8 +- .../tracking/include/ITStracking/Tracker.h | 2 +- .../include/ITStracking/TrackerTraits.h | 36 +- .../include/ITStracking/TrackingConfigParam.h | 4 +- .../tracking/include/ITStracking/Vertexer.h | 2 +- Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx | 16 +- .../ITSMFT/ITS/tracking/src/TrackerTraits.cxx | 49 +- 13 files changed, 988 insertions(+), 145 deletions(-) create mode 100644 Detectors/ITSMFT/ITS/postprocessing/studies/macros/CMakeLists.txt create mode 100644 Detectors/ITSMFT/ITS/postprocessing/studies/macros/PostTrackExtension.notest diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/CMakeLists.txt b/Detectors/ITSMFT/ITS/postprocessing/studies/CMakeLists.txt index 361ab4db4fb8e..9794b69631d57 100644 --- a/Detectors/ITSMFT/ITS/postprocessing/studies/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/CMakeLists.txt @@ -30,4 +30,6 @@ o2_target_root_dictionary(ITSPostprocessing HEADERS include/ITSStudies/ITSStudiesConfigParam.h include/ITSStudies/TrackCuts.h include/ITSStudies/TrackMethods.h -LINKDEF src/ITSStudiesLinkDef.h) \ No newline at end of file +LINKDEF src/ITSStudiesLinkDef.h) + +add_subdirectory(macros) diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackExtension.h b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackExtension.h index 2567000746559..fd5b93b0f9509 100644 --- a/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackExtension.h +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/include/ITSStudies/TrackExtension.h @@ -24,7 +24,7 @@ class MCKinematicsReader; namespace its::study { using mask_t = o2::dataformats::GlobalTrackID::mask_t; -o2::framework::DataProcessorSpec getTrackExtensionStudy(mask_t srcTracksMask, mask_t srcClustersMask, bool useMC, std::shared_ptr kineReader); +o2::framework::DataProcessorSpec getTrackExtensionStudy(mask_t srcTracksMask, mask_t srcClustersMask, std::shared_ptr kineReader); } // namespace its::study } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/macros/CMakeLists.txt b/Detectors/ITSMFT/ITS/postprocessing/studies/macros/CMakeLists.txt new file mode 100644 index 0000000000000..2d78e4077ec53 --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/macros/CMakeLists.txt @@ -0,0 +1,16 @@ +# Copyright 2019-2020 CERN and copyright holders of ALICE O2. +# See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +# All rights not expressly granted are reserved. +# +# This software is distributed under the terms of the GNU General Public +# License v3 (GPL Version 3), copied verbatim in the file "COPYING". +# +# In applying this license CERN does not waive the privileges and immunities +# granted to it by virtue of its status as an Intergovernmental Organization +# or submit itself to any jurisdiction. + +# o2_add_test_root_macro( +# PostTrackExtension.C +# PUBLIC_LINK_LIBRARIES ROOT::Hist ROOT::RIO ROOT::Core ROOT::Gpad +# LABELS its-study +# COMPILE_ONLY) diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/macros/PostTrackExtension.notest b/Detectors/ITSMFT/ITS/postprocessing/studies/macros/PostTrackExtension.notest new file mode 100644 index 0000000000000..4a7c9c4159a4b --- /dev/null +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/macros/PostTrackExtension.notest @@ -0,0 +1,629 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#if !defined(__CLING__) || defined(__ROOTCLING__) +#include "TStyle.h" +#include "TFile.h" +#include "TError.h" +#include "TColor.h" +#include "TCanvas.h" +#include "TH2D.h" +#include "TF1.h" +#include "TEfficiency.h" +#include "TMarker.h" +#include "TLegend.h" +#include "TTree.h" +#include "TLatex.h" + +#include +#include +#include +#endif + +static constexpr std::array bitPatternsBefore{15, 30, 31, 60, 62, 63, 120, 124, 126}; +static constexpr std::array bitPatternsAfter{31, 47, 61, 62, 63, 79, 94, 95, 111, 121, 122, 123, 124, 125, 126, 127}; +inline bool bitsCleared(uint8_t b, uint8_t a) { return (a == b) ? true : (b & ~(a & b)) != 0; } +static constexpr std::array patternColors = { + kRed, // Red + kBlue, // Blue + kGreen, // Green + kMagenta, // Magenta + kCyan, // Cyan + kOrange, // Orange + kViolet, // Violet + kYellow, // Yellow + kPink, // Pink + kAzure, // Azure + kSpring, // Spring Green + kTeal, // Teal + kBlack, // Black + kGray, // Gray + kOrange + 7, // Light Orange + kBlue - 9 // Light Blue +}; + +// Marker styles +static constexpr std::array patternMarkers = { + 20, // Full circle + 21, // Full square + 22, // Full triangle up + 23, // Full triangle down + 24, // Open circle + 25, // Open square + 26, // Open triangle up + 27, // Open cross + 28, // Star + 29, // Plus sign + 30, // Open diamond + 31, // Full diamond + 32, // Cross + 33, // Circle with cross + 34, // X sign + 35 // Double open cross +}; + +enum Labels : unsigned int { + eAll = 0, + eGood, + eFake, + eFakeBefore, + eFakeAfter, + eFakeMix, + eTopGood, + eBotGood, + eMixGood, + eTopFake, + eBotFake, + eMixFake, + eN, +}; +static const std::array names{ + "ALL #frac{ext trks}{all trks}", + "GOOD #frac{good ext trks}{all ext trks}", + "FAKE #frac{fake trks}{all ext trks}", + "FAKE BF #frac{fake bf trks}{fake ext trks}", + "FAKE AF #frac{fake af trks}{fake ext trks}", + "FAKE MIX #frac{fake mix trks}{fake ext trks}", + // Good Top/Bot/Mix + "TOP #frac{good top ext trks}{good ext trks}", + "BOT #frac{good bot ext trks}{good ext trks}", + "MIX #frac{good mix ext trks}{good ext trks}", + // Fake Top/Bot/Mix + "TOP #frac{fake top ext trks}{fake ext trks}", + "BOT #frac{fake bot ext trks}{fake ext trks}", + "MIX #frac{fake mix ext trks}{fake ext trks}", +}; +static const std::array colors{kBlack, kGreen, kRed, kCyan, kYellow, kAzure, + // Good Top/Bot/Mix + kBlue, kOrange, kPink, + // Fake Top/Bot/Mix + kBlue, kOrange, kPink}; +static const std::array markers{20, 21, 22, 23, 27, 28, + // Good Top/Bot/Mix + 29, 33, 39, + // Fake Top/Bot/Mix + 29, 33, 39}; +static const char* const texPtX = "#it{p}_{T} (GeV/#it{c})"; +static const char* const texEff = "Efficiency"; +static const char* const texPtRes = "#sigma(#Delta#it{p}_{T}/#it{p}_{T})"; +static const char* const texDCAxyRes = "#sigma(DCA_{#it{xy}}) (#mum)"; +static const char* const texDCAzRes = "#sigma(DCA_{#it{z}}) (#mum)"; +static const char* const fitOpt{"QWMER"}; + +void setStyle(); +TEfficiency* makeEff(TFile*, const char* num, const char* den); + +template +void style(T* t, Labels lab, TLegend* leg = nullptr) +{ + t->SetMarkerStyle(markers[lab]); + t->SetMarkerColor(colors[lab]); + t->SetLineColor(colors[lab]); + if (leg) { + leg->AddEntry(t, names[lab]); + } +} + +template +void stylePattern(T* t, int i, TLegend* leg = nullptr, const char* name = nullptr) +{ + t->SetMarkerStyle(patternMarkers[i]); + t->SetMarkerColor(patternColors[i]); + t->SetLineColor(patternColors[i]); + if (leg) { + leg->AddEntry(t, name); + } +} + +void PostTrackExtension(const char* fileName = "TrackExtensionStudy.root") +{ + setStyle(); + + std::unique_ptr fIn{TFile::Open(fileName, "READ")}; + if (!fIn || fIn->IsZombie()) { + Error("", "Cannot open file %s", fileName); + return; + } + + { // Purity & Fake-Rate + auto c = new TCanvas("cPFR", "", 800, 600); + auto h = c->DrawFrame(0.05, 0.0, 10., 1.05); + h->GetXaxis()->SetTitle(texPtX); + h->GetYaxis()->SetTitle(texEff); + auto leg = new TLegend(0.35, 0.35, 0.7, 0.7); + auto eff = fIn->Get("eExtension"); + style(eff, eAll, leg); + eff->Draw("same"); + auto effPurity = fIn->Get("eExtensionPurity"); + style(effPurity, eGood, leg); + effPurity->Draw("same"); + auto effFake = fIn->Get("eExtensionFake"); + style(effFake, eFake, leg); + effFake->Draw("same"); + leg->Draw(); + gPad->SetLogx(); + gPad->SetGrid(); + c->SaveAs("trkExt_purity_fake.pdf"); + } + + { // FAKE-Rate composition + auto c = new TCanvas("cFR", "", 800, 600); + auto h = c->DrawFrame(0.05, 0.0, 10., 1.05); + h->GetXaxis()->SetTitle(texPtX); + h->GetYaxis()->SetTitle(texEff); + auto leg = new TLegend(0.35, 0.35, 0.7, 0.7); + auto effFake = fIn->Get("eExtensionFake"); + style(effFake, eFake, leg); + effFake->Draw("same"); + auto effFakeBf = fIn->Get("eExtensionFakeBefore"); + style(effFakeBf, eFakeBefore, leg); + effFakeBf->Draw("same"); + auto effFakeAf = fIn->Get("eExtensionFakeAfter"); + style(effFakeAf, eFakeAfter, leg); + effFakeAf->Draw("same"); + auto effFakeMi = fIn->Get("eExtensionFakeMix"); + style(effFakeMi, eFakeMix, leg); + effFakeMi->Draw("same"); + leg->Draw(); + gPad->SetLogx(); + gPad->SetGrid(); + c->SaveAs("trkExt_fake.pdf"); + } + + { // GOOD Top/Bot/Mix Purity composition + auto c = new TCanvas("cGC", "", 800, 600); + auto h = c->DrawFrame(0.05, 0.0, 10., 1.05); + h->GetXaxis()->SetTitle(texPtX); + h->GetYaxis()->SetTitle(texEff); + auto leg = new TLegend(0.35, 0.35, 0.7, 0.7); + auto effTop = makeEff(fIn.get(), "eExtensionTopPurity", "eExtensionPurity"); + style(effTop, eTopGood, leg); + effTop->Draw("same"); + auto effBot = makeEff(fIn.get(), "eExtensionBotPurity", "eExtensionPurity"); + style(effBot, eBotGood, leg); + effBot->Draw("same"); + auto effMix = makeEff(fIn.get(), "eExtensionMixPurity", "eExtensionPurity"); + style(effMix, eMixGood, leg); + effMix->Draw("same"); + leg->Draw(); + gPad->SetLogx(); + gPad->SetGrid(); + c->SaveAs("trkExt_good_comp.pdf"); + } + + { // FAKE Top/Bot/Mix composition + auto c = new TCanvas("cFC", "", 800, 600); + auto h = c->DrawFrame(0.05, 0.0, 10., 1.05); + h->GetXaxis()->SetTitle(texPtX); + h->GetYaxis()->SetTitle(texEff); + auto leg = new TLegend(0.35, 0.35, 0.7, 0.7); + auto effTop = fIn->Get("eExtensionTopFake"); + style(effTop, eTopFake, leg); + effTop->Draw("same"); + auto effBot = fIn->Get("eExtensionBotFake"); + style(effBot, eBotFake, leg); + effBot->Draw("same"); + auto effMix = fIn->Get("eExtensionMixFake"); + style(effMix, eMixFake, leg); + effMix->Draw("same"); + leg->Draw(); + gPad->SetLogx(); + gPad->SetGrid(); + c->SaveAs("trkExt_fake_comp.pdf"); + } + + { // Good Patterns + auto c = new TCanvas("cPatGood", "", 3 * 800, 3 * 600); + c->Divide(3, 3); + for (int i{0}; i < (int)bitPatternsBefore.size(); ++i) { + auto p = c->cd(i + 1); + auto h = p->DrawFrame(0.05, 0.0, 10., 1.05); + h->GetXaxis()->SetTitle(texPtX); + h->GetYaxis()->SetTitle(texEff); + auto leg = new TLegend(0.35, 0.60, 0.7, 0.88); + leg->SetNColumns(4); + leg->SetHeader(std::format("BEFORE={:07b} GOOD Pattern AFTER/BEFORE", bitPatternsBefore[i]).c_str()); + for (int j{0}; j < (int)bitPatternsAfter.size(); ++j) { + if (bitsCleared(bitPatternsBefore[i], bitPatternsAfter[j])) { + continue; + } + auto eff = fIn->Get(std::format("eExtensionPatternGood_{:07b}_{:07b}", bitPatternsBefore[i], bitPatternsAfter[j]).c_str()); + stylePattern(eff, j, leg, std::format("{:07b}", bitPatternsAfter[j]).c_str()); + eff->Draw("same"); + } + leg->Draw(); + p->SetLogx(); + p->SetGrid(); + } + c->SaveAs("trkExt_good_pattern_comp.pdf"); + } + + { // Fake Patterns + auto c = new TCanvas("cPatFake", "", 3 * 800, 3 * 600); + c->Divide(3, 3); + for (int i{0}; i < (int)bitPatternsBefore.size(); ++i) { + auto p = c->cd(i + 1); + auto h = p->DrawFrame(0.05, 0.0, 10., 1.05); + h->GetXaxis()->SetTitle(texPtX); + h->GetYaxis()->SetTitle(texEff); + auto leg = new TLegend(0.35, 0.60, 0.7, 0.88); + leg->SetNColumns(4); + leg->SetHeader(std::format("BEFORE={:07b} FAKE Pattern AFTER/BEFORE", bitPatternsBefore[i]).c_str()); + for (int j{0}; j < (int)bitPatternsAfter.size(); ++j) { + if (bitsCleared(bitPatternsBefore[i], bitPatternsAfter[j])) { + continue; + } + auto eff = fIn->Get(std::format("eExtensionPatternFake_{:07b}_{:07b}", bitPatternsBefore[i], bitPatternsAfter[j]).c_str()); + stylePattern(eff, j, leg, std::format("{:07b}", bitPatternsAfter[j]).c_str()); + eff->Draw("same"); + } + leg->Draw(); + p->SetLogx(); + p->SetGrid(); + } + c->SaveAs("trkExt_fake_pattern_comp.pdf"); + } + + { // DCA + auto fGaus = new TF1("fGaus", "gaus", -200., 200.); + auto dcaXYVsPtNo = fIn->Get("hDCAxyVsPtResNormal"); + auto dcaXYVsPtYes = fIn->Get("hDCAxyVsPtResExtended"); + auto dcazVsPtNo = fIn->Get("hDCAzVsPtResNormal"); + auto dcazVsPtYes = fIn->Get("hDCAzVsPtResExtended"); + auto bins = dcazVsPtNo->GetXaxis()->GetXbins(); + auto dcaXYResNo = new TH1F("hDcaxyResNo", "NORMAL;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", bins->GetSize() - 1, bins->GetArray()); + auto dcaXYResYes = new TH1F("hDcaxyResYes", "EXTENDED;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", bins->GetSize() - 1, bins->GetArray()); + auto dcaZResNo = new TH1F("hDcazResNo", "NORMAL;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", bins->GetSize() - 1, bins->GetArray()); + auto dcaZResYes = new TH1F("hDcazResYes", "EXTENDED;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", bins->GetSize() - 1, bins->GetArray()); + TH1* proj; + for (int iPt{1}; iPt <= bins->GetSize(); ++iPt) { + auto ptMin = dcaXYResNo->GetXaxis()->GetBinLowEdge(iPt); + if (ptMin < 0.1) { + continue; + } + float minFit = (ptMin < 1.) ? -200. : -75.; + float maxFit = (ptMin < 1.) ? 200. : 75.; + + proj = dcaXYVsPtNo->ProjectionY(Form("hProjDCAxy_no_%d", iPt), iPt, iPt); + proj->Fit("fGaus", fitOpt, "", minFit, maxFit); + dcaXYResNo->SetBinContent(iPt, fGaus->GetParameter(2)); + dcaXYResNo->SetBinError(iPt, fGaus->GetParError(2)); + + proj = dcaXYVsPtYes->ProjectionY(Form("hProjDCAxy_yes_%d", iPt), iPt, iPt); + proj->Fit("fGaus", fitOpt, "", minFit, maxFit); + dcaXYResYes->SetBinContent(iPt, fGaus->GetParameter(2)); + dcaXYResYes->SetBinError(iPt, fGaus->GetParError(2)); + + proj = dcazVsPtNo->ProjectionY(Form("hProjDCAz_no_%d", iPt), iPt, iPt); + proj->Fit("fGaus", fitOpt, "", minFit, maxFit); + dcaZResNo->SetBinContent(iPt, fGaus->GetParameter(2)); + dcaZResNo->SetBinError(iPt, fGaus->GetParError(2)); + + proj = dcazVsPtYes->ProjectionY(Form("hProjDCAz_yes_%d", iPt), iPt, iPt); + proj->Fit("fGaus", fitOpt, "", minFit, maxFit); + dcaZResYes->SetBinContent(iPt, fGaus->GetParameter(2)); + dcaZResYes->SetBinError(iPt, fGaus->GetParError(2)); + } + + dcaXYResNo->SetLineColor(kRed); + dcaXYResNo->SetMarkerColor(kRed); + dcaXYResYes->SetLineColor(kBlue); + dcaXYResYes->SetMarkerColor(kBlue); + dcaZResNo->SetLineColor(kRed); + dcaZResNo->SetMarkerColor(kRed); + dcaZResYes->SetLineColor(kBlue); + dcaZResYes->SetMarkerColor(kBlue); + + auto c = new TCanvas("cDCA", "", 2 * 800, 600); + c->Divide(2, 1); + c->cd(1); + auto h = gPad->DrawFrame(0.1, 1, 10., 500); + h->GetXaxis()->SetTitle(texPtX); + h->GetYaxis()->SetTitle(texDCAxyRes); + dcaXYResNo->Draw("SAME"); + dcaXYResYes->Draw("SAME"); + gPad->SetLogx(); + gPad->SetLogy(); + gPad->SetGrid(); + auto leg = new TLegend(0.20, 0.20, 0.40, 0.40); + leg->AddEntry(dcaXYResNo, "Normal"); + leg->AddEntry(dcaXYResYes, "Extended"); + leg->Draw(); + + c->cd(2); + h = gPad->DrawFrame(0.1, 1, 10., 500); + h->GetXaxis()->SetTitle(texPtX); + h->GetYaxis()->SetTitle(texDCAzRes); + dcaZResNo->Draw("SAME"); + dcaZResYes->Draw("SAME"); + gPad->SetLogx(); + gPad->SetLogy(); + gPad->SetGrid(); + + c->SaveAs("trkExt_dca.pdf"); + } + + return; + { // Kinematic variables + auto t = fIn->Get("tree"); + auto c = new TCanvas("cKG", "", 800, 600); + c->Divide(3, 2); + { + auto p = c->cd(1); + p->SetGrid(); + auto h = p->DrawFrame(-.6, 0., .6, 9.); + h->GetXaxis()->SetTitle("#frac{Q^{2}}{p_{T,TRK}}-#frac{Q^{2}}{p_{T,MC}}"); + h->GetYaxis()->SetTitle("n. counts"); + t->Draw("trk.getQ2Pt()-mcTrk.getQ2Pt()>>hPtNo(100,-.6,.6)", "isGood&&!isExtended", "HIST;SAME"); + auto hNo = (TH1F*)p->GetPrimitive("hPtNo"); + hNo->Scale(1.0 / hNo->Integral("width")); + hNo->SetLineColor(kRed); + auto fitNo = new TF1("fitNo", "gaus", -0.04, 0.04); + hNo->Fit(fitNo, "QR"); + fitNo->SetLineColor(kRed); + fitNo->Draw("SAME"); + auto textNo = new TLatex(-0.55, 8.2, Form("#mu = %.3f, #sigma = %.3f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textNo->SetTextColor(kRed); + textNo->SetNDC(false); + textNo->SetTextSize(0.05); + textNo->Draw(); + + t->Draw("trk.getQ2Pt()-mcTrk.getQ2Pt()>>hPtYes(100,-.6,.6)", "isGood&&isExtended", "HIST;SAME"); + auto hYes = (TH1F*)p->GetPrimitive("hPtYes"); + hYes->Scale(1.0 / hYes->Integral("width")); + hYes->SetLineColor(kBlue); + auto fitYes = new TF1("fitYes", "gaus", -0.04, 0.04); + hYes->Fit(fitYes, "QR"); + fitYes->SetLineColor(kBlue); + fitYes->Draw("SAME"); + auto textYes = new TLatex(-0.55, 7, Form("#mu = %.4f, #sigma = %.4f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textYes->SetTextColor(kBlue); + textYes->SetNDC(false); + textYes->SetTextSize(0.05); + textYes->Draw(); + + p->Modified(); + p->Update(); + } + { + auto p = c->cd(2); + p->SetGrid(); + auto h = p->DrawFrame(-3, 0., 3, 2.); + h->GetXaxis()->SetTitle("Y_{TRK}-Y_{MC}"); + h->GetYaxis()->SetTitle("n. counts"); + t->Draw("trk.getY()-mcTrk.getY()>>hYNo(100,-3,3)", "isGood&&!isExtended", "HIST;SAME"); + auto hNo = (TH1F*)p->GetPrimitive("hYNo"); + hNo->Scale(1.0 / hNo->Integral("width")); + hNo->SetLineColor(kRed); + auto fitNo = new TF1("fitNo", "gaus", -0.5, 0.5); + hNo->Fit(fitNo, "QR"); + fitNo->SetLineColor(kRed); + fitNo->Draw("SAME"); + auto textNo = new TLatex(-2, 1.7, Form("#mu = %.3f, #sigma = %.3f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textNo->SetTextColor(kRed); + textNo->SetNDC(false); + textNo->SetTextSize(0.05); + textNo->Draw(); + + t->Draw("trk.getY()-mcTrk.getY()>>hYYes(100,-3,3)", "isGood&&isExtended", "HIST;SAME"); + auto hYes = (TH1F*)p->GetPrimitive("hYYes"); + hYes->Scale(1.0 / hYes->Integral("width")); + hYes->SetLineColor(kBlue); + auto fitYes = new TF1("fitYes", "gaus", -0.5, 0.5); + hYes->Fit(fitYes, "QR"); + fitYes->SetLineColor(kBlue); + fitYes->Draw("SAME"); + auto textYes = new TLatex(-2, 1.5, Form("#mu = %.4f, #sigma = %.4f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textYes->SetTextColor(kBlue); + textYes->SetNDC(false); + textYes->SetTextSize(0.05); + textYes->Draw(); + + p->Modified(); + p->Update(); + } + { + auto p = c->cd(3); + p->SetGrid(); + auto h = p->DrawFrame(-2, 0., 2, 4.2); + h->GetXaxis()->SetTitle("Z_{TRK}-Z_{MC}"); + h->GetYaxis()->SetTitle("n. counts"); + t->Draw("trk.getZ()-mcTrk.getZ()>>hZNo(100,-2,2)", "isGood&&!isExtended", "HIST;SAME"); + auto hNo = (TH1F*)p->GetPrimitive("hZNo"); + hNo->Scale(1.0 / hNo->Integral("width")); + hNo->SetLineColor(kRed); + auto fitNo = new TF1("fitNo", "gaus", -0.2, 0.2); + hNo->Fit(fitNo, "QR"); + fitNo->SetLineColor(kRed); + fitNo->Draw("SAME"); + auto textNo = new TLatex(-1.7, 3.8, Form("#mu = %.3f, #sigma = %.3f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textNo->SetTextColor(kRed); + textNo->SetNDC(false); + textNo->SetTextSize(0.05); + textNo->Draw(); + + t->Draw("trk.getZ()-mcTrk.getZ()>>hZYes(100,-2,2)", "isGood&&isExtended", "HIST;SAME"); + auto hYes = (TH1F*)p->GetPrimitive("hZYes"); + hYes->Scale(1.0 / hYes->Integral("width")); + hYes->SetLineColor(kBlue); + auto fitYes = new TF1("fitYes", "gaus", -0.2, 0.2); + hYes->Fit(fitYes, "QR"); + fitYes->SetLineColor(kBlue); + fitYes->Draw("SAME"); + auto textYes = new TLatex(-1.7, 3.5, Form("#mu = %.4f, #sigma = %.4f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textYes->SetTextColor(kBlue); + textYes->SetNDC(false); + textYes->SetTextSize(0.05); + textYes->Draw(); + + p->Modified(); + p->Update(); + } + { + auto p = c->cd(4); + p->SetGrid(); + auto h = p->DrawFrame(-0.02, 0., 0.02, 370.); + h->GetXaxis()->SetTitle("TGL_{TRK}-TGL_{MC}"); + h->GetYaxis()->SetTitle("n. counts"); + t->Draw("trk.getTgl()-mcTrk.getTgl()>>hTglNo(100,-0.02,0.02)", "isGood&&!isExtended", "HIST;SAME"); + auto hNo = (TH1F*)p->GetPrimitive("hTglNo"); + hNo->Scale(1.0 / hNo->Integral("width")); + hNo->SetLineColor(kRed); + auto fitNo = new TF1("fitNo", "gaus", -0.003, 0.003); + hNo->Fit(fitNo, "QR"); + fitNo->SetLineColor(kRed); + fitNo->Draw("SAME"); + auto textNo = new TLatex(-0.018, 330, Form("#mu = %.3f, #sigma = %.3f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textNo->SetTextColor(kRed); + textNo->SetNDC(false); + textNo->SetTextSize(0.05); + textNo->Draw(); + + t->Draw("trk.getTgl()-mcTrk.getTgl()>>hTglYes(100,-0.02,0.02)", "isGood&&isExtended", "HIST;SAME"); + auto hYes = (TH1F*)p->GetPrimitive("hTglYes"); + hYes->Scale(1.0 / hYes->Integral("width")); + hYes->SetLineColor(kBlue); + auto fitYes = new TF1("fitYes", "gaus", -0.003, 0.003); + hYes->Fit(fitYes, "QR"); + fitYes->SetLineColor(kBlue); + fitYes->Draw("SAME"); + auto textYes = new TLatex(-0.018, 310, Form("#mu = %.6f, #sigma = %.6f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textYes->SetTextColor(kBlue); + textYes->SetNDC(false); + textYes->SetTextSize(0.05); + textYes->Draw(); + + p->Modified(); + p->Update(); + } + { + auto p = c->cd(5); + p->SetGrid(); + auto h = p->DrawFrame(-0.08, 0., 0.08, 80.); + h->GetXaxis()->SetTitle("SNP_{TRK}-SNP_{MC}"); + h->GetYaxis()->SetTitle("n. counts"); + t->Draw("trk.getSnp()-mcTrk.getSnp()>>hSnpNo(100,-0.08,0.08)", "isGood&&!isExtended", "HIST;SAME"); + auto hNo = (TH1F*)p->GetPrimitive("hSnpNo"); + hNo->Scale(1.0 / hNo->Integral("width")); + hNo->SetLineColor(kRed); + auto fitNo = new TF1("fitNo", "gaus", -0.03, 0.03); + hNo->Fit(fitNo, "QR"); + fitNo->SetLineColor(kRed); + fitNo->Draw("SAME"); + auto textNo = new TLatex(-0.07, 72, Form("#mu = %.3f, #sigma = %.3f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textNo->SetTextColor(kRed); + textNo->SetNDC(false); + textNo->SetTextSize(0.05); + textNo->Draw(); + + t->Draw("trk.getSnp()-mcTrk.getSnp()>>hSnpYes(100,-0.08,0.08)", "isGood&&isExtended", "HIST;SAME"); + auto hYes = (TH1F*)p->GetPrimitive("hSnpYes"); + hYes->Scale(1.0 / hYes->Integral("width")); + hYes->SetLineColor(kBlue); + auto fitYes = new TF1("fitYes", "gaus", -0.03, 0.03); + hYes->Fit(fitYes, "QR"); + fitYes->SetLineColor(kBlue); + fitYes->Draw("SAME"); + auto textYes = new TLatex(-0.07, 66, Form("#mu = %.6f, #sigma = %.6f", fitNo->GetParameter(1), fitNo->GetParameter(2))); + textYes->SetTextColor(kBlue); + textYes->SetNDC(false); + textYes->SetTextSize(0.05); + textYes->Draw(); + + p->Modified(); + p->Update(); + } + { + auto p = c->cd(6); + auto legend = new TLegend(0.2, 0.2, 0.8, 0.8); + legend->SetTextSize(0.06); + legend->SetLineWidth(3); + legend->SetHeader("GOOD tracks", "C"); + auto mBlue = new TMarker(); + mBlue->SetMarkerColor(kBlue); + mBlue->SetMarkerSize(4); + legend->AddEntry(mBlue, "extended", "p"); + auto mRed = new TMarker(); + mRed->SetMarkerColor(kRed); + mRed->SetMarkerSize(4); + legend->AddEntry(mRed, "normal", "p"); + legend->SetLineColor(kRed); + legend->Draw(); + } + c->SaveAs("trkExt_kinematics.pdf"); + } +} + +void setStyle() +{ + gStyle->Reset("Plain"); + gStyle->SetOptTitle(0); + gStyle->SetOptStat(0); + gStyle->SetPalette(kRainbow); + gStyle->SetCanvasColor(10); + gStyle->SetCanvasBorderMode(0); + gStyle->SetFrameLineWidth(1); + gStyle->SetFrameFillColor(kWhite); + gStyle->SetPadColor(10); + gStyle->SetPadTickX(1); + gStyle->SetPadTickY(1); + gStyle->SetPadBottomMargin(0.15); + gStyle->SetPadLeftMargin(0.15); + gStyle->SetHistLineWidth(1); + gStyle->SetHistLineColor(kRed); + gStyle->SetFuncWidth(2); + gStyle->SetFuncColor(kGreen); + gStyle->SetLineWidth(2); + gStyle->SetLabelSize(0.045, "xyz"); + gStyle->SetLabelOffset(0.01, "y"); + gStyle->SetLabelOffset(0.01, "x"); + gStyle->SetLabelColor(kBlack, "xyz"); + gStyle->SetTitleSize(0.05, "xyz"); + gStyle->SetTitleOffset(1.25, "y"); + gStyle->SetTitleOffset(1.2, "x"); + gStyle->SetTitleFillColor(kWhite); + gStyle->SetTextSizePixels(26); + gStyle->SetTextFont(42); + gStyle->SetTickLength(0.04, "X"); + gStyle->SetTickLength(0.04, "Y"); + gStyle->SetLegendBorderSize(0); + gStyle->SetLegendFillColor(kWhite); + gStyle->SetFillColor(kWhite); + gStyle->SetLegendFont(42); +} + +TEfficiency* makeEff(TFile* fIn, const char* num, const char* den) +{ + auto h1 = fIn->Get(num)->GetPassedHistogram(); + auto h2 = fIn->Get(den)->GetPassedHistogram(); + auto e = new TEfficiency(*h1, *h2); + return e; +} diff --git a/Detectors/ITSMFT/ITS/postprocessing/studies/src/TrackExtension.cxx b/Detectors/ITSMFT/ITS/postprocessing/studies/src/TrackExtension.cxx index 364a354c700b6..465365ffa3d86 100644 --- a/Detectors/ITSMFT/ITS/postprocessing/studies/src/TrackExtension.cxx +++ b/Detectors/ITSMFT/ITS/postprocessing/studies/src/TrackExtension.cxx @@ -14,17 +14,24 @@ #include "DataFormatsITS/TrackITS.h" #include "DataFormatsITSMFT/CompCluster.h" #include "DetectorsBase/GRPGeomHelper.h" +#include "DetectorsBase/Propagator.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/Task.h" #include "ITSBase/GeometryTGeo.h" #include "ITSStudies/Helpers.h" #include "ITSStudies/TrackExtension.h" +#include "SimulationDataFormat/MCEventHeader.h" #include "SimulationDataFormat/MCTrack.h" #include "Steer/MCKinematicsReader.h" +#include "ReconstructionDataFormats/Vertex.h" +#include "ReconstructionDataFormats/DCA.h" #include #include "TFile.h" +#include "TH1D.h" +#include "TH2D.h" +#include "TEfficiency.h" namespace o2::its::study { @@ -36,7 +43,9 @@ using o2::steer::MCKinematicsReader; class TrackExtensionStudy : public Task { struct ParticleInfo { - int event; + float eventX; + float eventY; + float eventZ; int pdg; float pt; float eta; @@ -60,24 +69,24 @@ class TrackExtensionStudy : public Task public: TrackExtensionStudy(std::shared_ptr dr, mask_t src, - bool useMC, std::shared_ptr kineReader, std::shared_ptr gr) : mDataRequest(dr), mTracksSrc(src), mKineReader(kineReader), mGGCCDBRequest(gr) { - if (useMC) { - LOGP(info, "Read MCKine reader with {} sources", mKineReader->getNSources()); - } + LOGP(info, "Read MCKine reader with {} sources", mKineReader->getNSources()); } ~TrackExtensionStudy() final = default; void init(InitContext& /*ic*/) final; void run(ProcessingContext& /*pc*/) final; void endOfStream(EndOfStreamContext& /*ec*/) final; + void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; void process(); private: static constexpr std::array mBitPatternsBefore{15, 30, 31, 60, 62, 63, 120, 124, 126}; static constexpr std::array mBitPatternsAfter{31, 47, 61, 62, 63, 79, 94, 95, 111, 121, 122, 123, 124, 125, 126, 127}; + const std::bitset<7> mTopMask{"1110000"}; + const std::bitset<7> mBotMask{"0000111"}; void updateTimeDependentParams(ProcessingContext& pc); std::string mOutFileName = "TrackExtensionStudy.root"; @@ -101,12 +110,30 @@ class TrackExtensionStudy : public Task bool mWithTree{false}; std::unique_ptr mHTrackCounts; - std::unique_ptr mHClustersCounts; std::unique_ptr mHLengthAny, mHLengthGood, mHLengthFake; std::unique_ptr mHChi2Any, mHChi2Good, mHChi2Fake; std::unique_ptr mHPtAny, mHPtGood, mHPtFake; std::unique_ptr mHExtensionAny, mHExtensionGood, mHExtensionFake; - std::unique_ptr mHExtensionPatternsAny, mHExtensionPatternsGood, mHExtensionPatternsFake; + std::unique_ptr mHExtensionPatternsAny, mHExtensionPatternsGood, mHExtensionPatternsFake, mHExtensionPatternsGoodMissed, mHExtensionPatternsGoodEmpty; + std::unique_ptr mEExtensionNum, mEExtensionDen, mEExtensionPurityNum, mEExtensionPurityDen, mEExtensionFakeNum, mEExtensionFakeDen; + std::unique_ptr mEExtensionFakeBeforeNum, mEExtensionFakeAfterNum, mEExtensionFakeMixNum; + std::unique_ptr mEExtensionTopNum, mEExtensionTopPurityNum, mEExtensionTopFakeNum; + std::unique_ptr mEExtensionBotNum, mEExtensionBotPurityNum, mEExtensionBotFakeNum; + std::unique_ptr mEExtensionMixNum, mEExtensionMixPurityNum, mEExtensionMixFakeNum; + std::array, mBitPatternsBefore.size()> mEExtensionPatternGoodNum, mEExtensionPatternFakeNum; + std::array, mBitPatternsAfter.size()>, mBitPatternsBefore.size()> mEExtensionPatternIndGoodNum, mEExtensionPatternIndFakeNum; + // DCA + std::unique_ptr mDCAxyVsPtPionsNormal, mDCAxyVsPtPionsExtended; + std::unique_ptr mDCAzVsPtPionsNormal, mDCAzVsPtPionsExtended; + + template + std::unique_ptr createHistogram(C... n, F... b) + { + auto t = std::make_unique(n..., b...); + mHistograms.push_back(static_cast(t.get())); + return std::move(t); + } + std::vector mHistograms; }; void TrackExtensionStudy::init(InitContext& ic) @@ -114,13 +141,13 @@ void TrackExtensionStudy::init(InitContext& ic) o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); mWithTree = ic.options().get("with-tree"); - constexpr size_t effHistBins = 100; + constexpr size_t effHistBins = 40; constexpr float effPtCutLow = 0.01; constexpr float effPtCutHigh = 10.; auto xbins = helpers::makeLogBinning(effHistBins, effPtCutLow, effPtCutHigh); // Track Counting - mHTrackCounts = std::make_unique("hTrackCounts", "Track Stats", 10, 0, 10); + mHTrackCounts = createHistogram("hTrackCounts", "Track Stats", 10, 0, 10); mHTrackCounts->GetXaxis()->SetBinLabel(1, "Total Tracks"); mHTrackCounts->GetXaxis()->SetBinLabel(2, "Normal ANY Tracks"); mHTrackCounts->GetXaxis()->SetBinLabel(3, "Normal GOOD Tracks"); @@ -132,49 +159,87 @@ void TrackExtensionStudy::init(InitContext& ic) mHTrackCounts->GetXaxis()->SetBinLabel(9, "Extended FAKE AFTER Tracks"); mHTrackCounts->GetXaxis()->SetBinLabel(10, "Extended FAKE BEFORE&AFTER Tracks"); - // Cluster Counting - mHClustersCounts = std::make_unique("hClusterCounts", "Cluster Stats", 5, 0, 5); - mHClustersCounts->GetXaxis()->SetBinLabel(1, "Total Clusters"); - mHClustersCounts->GetXaxis()->SetBinLabel(2, "Tracking"); - mHClustersCounts->GetXaxis()->SetBinLabel(3, "Extension"); - mHClustersCounts->GetXaxis()->SetBinLabel(4, "Good Extension"); - mHClustersCounts->GetXaxis()->SetBinLabel(5, "Fake Extension"); - // Length - mHLengthAny = std::make_unique("hLengthAny", "Extended Tracks Length (ANY);NCluster;Entries", 5, 3, 8); - mHLengthGood = std::make_unique("hLengthGood", "Extended Tracks Length (GOOD);NCluster;Entries", 5, 3, 8); - mHLengthFake = std::make_unique("hLengthFake", "Extended Tracks Length (FAKE);NCluster;Entries", 5, 3, 8); + mHLengthAny = createHistogram("hLengthAny", "Extended Tracks Length (ANY);NCluster;Entries", 5, 3, 8); + mHLengthGood = createHistogram("hLengthGood", "Extended Tracks Length (GOOD);NCluster;Entries", 5, 3, 8); + mHLengthFake = createHistogram("hLengthFake", "Extended Tracks Length (FAKE);NCluster;Entries", 5, 3, 8); // Chi2 - mHChi2Any = std::make_unique("hChi2Any", "Extended Tracks Length (ANY);#chi^{2};Entries", 50, 0, 100); - mHChi2Good = std::make_unique("hChi2Good", "Extended Tracks Length (GOOD);#chi^{2};Entries", 50, 0, 100); - mHChi2Fake = std::make_unique("hChi2Fake", "Extended Tracks Length (FAKE);#chi^{2};Entries", 50, 0, 100); + mHChi2Any = createHistogram("hChi2Any", "Extended Tracks Length (ANY);#chi^{2};Entries", 50, 0, 100); + mHChi2Good = createHistogram("hChi2Good", "Extended Tracks Length (GOOD);#chi^{2};Entries", 50, 0, 100); + mHChi2Fake = createHistogram("hChi2Fake", "Extended Tracks Length (FAKE);#chi^{2};Entries", 50, 0, 100); // Pt - mHPtAny = std::make_unique("hPtAny", "Extended Tracks Length (ANY);#it{p}_{T};Entries", xbins.size(), effPtCutLow, effPtCutHigh); - mHPtGood = std::make_unique("hPtGood", "Extended Tracks Length (GOOD);#it{p}_{T};Entries", xbins.size(), effPtCutLow, effPtCutHigh); - mHPtFake = std::make_unique("hPtFake", "Extended Tracks Length (FAKE);#it{p}_{T};Entries", xbins.size(), effPtCutLow, effPtCutHigh); + mHPtAny = createHistogram("hPtAny", "Extended Tracks Length (ANY);#it{p}_{T};Entries", effHistBins, xbins.data()); + mHPtGood = createHistogram("hPtGood", "Extended Tracks Length (GOOD);#it{p}_{T};Entries", effHistBins, xbins.data()); + mHPtFake = createHistogram("hPtFake", "Extended Tracks Length (FAKE);#it{p}_{T};Entries", effHistBins, xbins.data()); // Length - mHExtensionAny = std::make_unique("hExtensionAny", "Extended Tracks Length (ANY);Extended Layer;Entries", 7, 0, 7); - mHExtensionGood = std::make_unique("hExtensionGood", "Extended Tracks Length (GOOD);Extended Layer;Entries", 7, 0, 7); - mHExtensionFake = std::make_unique("hExtensionFake", "Extended Tracks Length (FAKE);Extended Layer;Entries", 7, 0, 7); + mHExtensionAny = createHistogram("hExtensionAny", "Extended Tracks Length (ANY);Extended Layer;Entries", 7, 0, 7); + mHExtensionGood = createHistogram("hExtensionGood", "Extended Tracks Length (GOOD);Extended Layer;Entries", 7, 0, 7); + mHExtensionFake = createHistogram("hExtensionFake", "Extended Tracks Length (FAKE);Extended Layer;Entries", 7, 0, 7); // Patterns - auto makePatternAxisLabels = [&](TH1* h) { + auto makePatternAxisLabels = [&](TH1* h, bool xBefore = true) { for (int i{1}; i <= h->GetXaxis()->GetNbins(); ++i) { - h->GetXaxis()->SetBinLabel(i, fmt::format("{:07b}", mBitPatternsBefore[i - 1]).c_str()); + if (xBefore) { + h->GetXaxis()->SetBinLabel(i, fmt::format("{:07b}", mBitPatternsBefore[i - 1]).c_str()); + } else { + h->GetXaxis()->SetBinLabel(i, fmt::format("{:07b}", mBitPatternsAfter[i - 1]).c_str()); + } } for (int i{1}; i <= h->GetYaxis()->GetNbins(); ++i) { h->GetYaxis()->SetBinLabel(i, fmt::format("{:07b}", mBitPatternsAfter[i - 1]).c_str()); } }; - mHExtensionPatternsAny = std::make_unique("hExtensionPatternsAny", "Extended Tracks Pattern (ANY);Before;After;Entries", mBitPatternsBefore.size(), 0, mBitPatternsBefore.size(), mBitPatternsAfter.size(), 0, mBitPatternsAfter.size()); + mHExtensionPatternsAny = createHistogram("hExtensionPatternsAny", "Extended Tracks Pattern (ANY);Before;After;Entries", mBitPatternsBefore.size(), 0, mBitPatternsBefore.size(), mBitPatternsAfter.size(), 0, mBitPatternsAfter.size()); makePatternAxisLabels(mHExtensionPatternsAny.get()); - mHExtensionPatternsGood = std::make_unique("hExtensionPatternsGood", "Extended Tracks Pattern (GOOD);Before;After;Entries", mBitPatternsBefore.size(), 0, mBitPatternsBefore.size(), mBitPatternsAfter.size(), 0, mBitPatternsAfter.size()); + mHExtensionPatternsGood = createHistogram("hExtensionPatternsGood", "Extended Tracks Pattern (GOOD);Before;After;Entries", mBitPatternsBefore.size(), 0, mBitPatternsBefore.size(), mBitPatternsAfter.size(), 0, mBitPatternsAfter.size()); makePatternAxisLabels(mHExtensionPatternsGood.get()); - mHExtensionPatternsFake = std::make_unique("hExtensionPatternsFake", "Extended Tracks Pattern (FAKE);Before;After;Entries", mBitPatternsBefore.size(), 0, mBitPatternsBefore.size(), mBitPatternsAfter.size(), 0, mBitPatternsAfter.size()); + mHExtensionPatternsFake = createHistogram("hExtensionPatternsFake", "Extended Tracks Pattern (FAKE);Before;After;Entries", mBitPatternsBefore.size(), 0, mBitPatternsBefore.size(), mBitPatternsAfter.size(), 0, mBitPatternsAfter.size()); makePatternAxisLabels(mHExtensionPatternsFake.get()); + mHExtensionPatternsGoodMissed = createHistogram("hExtensionPatternsGoodMissed", "Extended Tracks Pattern (GOOD) Missed Clusters;After;Missed;Entries", mBitPatternsAfter.size(), 0, mBitPatternsAfter.size(), mBitPatternsAfter.size(), 0, mBitPatternsAfter.size()); + makePatternAxisLabels(mHExtensionPatternsGoodMissed.get(), false); + mHExtensionPatternsGoodEmpty = createHistogram("hExtensionPatternsGoodEmpty", "Extended Tracks Pattern (GOOD) Empty Clusters;Before;After;Entries", mBitPatternsAfter.size(), 0, mBitPatternsAfter.size(), mBitPatternsAfter.size(), 0, mBitPatternsAfter.size()); + makePatternAxisLabels(mHExtensionPatternsGoodEmpty.get(), false); + + /// Effiencies + mEExtensionNum = createHistogram("hExtensionNum", "Extension Numerator", effHistBins, xbins.data()); + mEExtensionDen = createHistogram("hExtensionDen", "Extension Dennominator", effHistBins, xbins.data()); + // Purity + mEExtensionPurityNum = createHistogram("hExtensionPurityNum", "Extension Purity Numerator", effHistBins, xbins.data()); + mEExtensionPurityDen = createHistogram("hExtensionPurityDen", "Extension Purity Denominator", effHistBins, xbins.data()); + // Fake + mEExtensionFakeNum = createHistogram("hExtensionFakeNum", "Extension Fake Numerator", effHistBins, xbins.data()); + mEExtensionFakeDen = createHistogram("hExtensionFakeDen", "Extension Fake Denominator", effHistBins, xbins.data()); + mEExtensionFakeBeforeNum = createHistogram("hExtensionFakeBeforeNum", "Extension Fake Before Numerator", effHistBins, xbins.data()); + mEExtensionFakeAfterNum = createHistogram("hExtensionFakeAfterNum", "Extension Fake After Numerator", effHistBins, xbins.data()); + mEExtensionFakeMixNum = createHistogram("hExtensionFakeMixNum", "Extension Fake Mix Numerator", effHistBins, xbins.data()); + // Top + mEExtensionTopNum = createHistogram("hExtensionTopNum", "Extension Top Numerator", effHistBins, xbins.data()); + mEExtensionTopPurityNum = createHistogram("hExtensionTopPurityNum", "Extension Top Purity Numerator", effHistBins, xbins.data()); + mEExtensionTopFakeNum = createHistogram("hExtensionTopFakeNum", "Extension Top Fake Numerator", effHistBins, xbins.data()); + mEExtensionBotNum = createHistogram("hExtensionBotNum", "Extension Bot Numerator", effHistBins, xbins.data()); + mEExtensionBotPurityNum = createHistogram("hExtensionBotPurityNum", "Extension Bot Purity Numerator", effHistBins, xbins.data()); + mEExtensionBotFakeNum = createHistogram("hExtensionBotFakeNum", "Extension Bot Fake Numerator", effHistBins, xbins.data()); + mEExtensionMixNum = createHistogram("hExtensionMixNum", "Extension Mix Numerator", effHistBins, xbins.data()); + mEExtensionMixPurityNum = createHistogram("hExtensionMixPurityNum", "Extension Mix Purity Numerator", effHistBins, xbins.data()); + mEExtensionMixFakeNum = createHistogram("hExtensionMixFakeNum", "Extension Mix Fake Numerator", effHistBins, xbins.data()); + // Patterns + for (int i{0}; i < mBitPatternsBefore.size(); ++i) { + mEExtensionPatternGoodNum[i] = createHistogram(fmt::format("hExtensionPatternGood_{:07b}", mBitPatternsBefore[i]).c_str(), fmt::format("Extended Tracks Pattern (GOOD) {:07b}", mBitPatternsBefore[i]).c_str(), effHistBins, xbins.data()); + mEExtensionPatternFakeNum[i] = createHistogram(fmt::format("hExtensionPatternFake_{:07b}", mBitPatternsBefore[i]).c_str(), fmt::format("Extended Tracks Pattern (FAKE) {:07b}", mBitPatternsBefore[i]).c_str(), effHistBins, xbins.data()); + for (int j{0}; j < mBitPatternsAfter.size(); ++j) { + mEExtensionPatternIndGoodNum[i][j] = createHistogram(fmt::format("hExtensionPatternGood_{:07b}_{:07b}", mBitPatternsBefore[i], mBitPatternsAfter[j]).c_str(), fmt::format("Extended Tracks Pattern (GOOD) {:07b} -> {:07b}", mBitPatternsBefore[i], mBitPatternsAfter[j]).c_str(), effHistBins, xbins.data()); + mEExtensionPatternIndFakeNum[i][j] = createHistogram(fmt::format("hExtensionPatternFake_{:07b}_{:07b}", mBitPatternsBefore[i], mBitPatternsAfter[j]).c_str(), fmt::format("Extended Tracks Pattern (FAKE) {:07b} -> {:07b}", mBitPatternsBefore[i], mBitPatternsAfter[j]).c_str(), effHistBins, xbins.data()); + } + } + + /// DCA + mDCAxyVsPtPionsNormal = createHistogram("hDCAxyVsPtResNormal", "DCA_{#it{xy}} NORMAL Pions;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", effHistBins, xbins.data(), 1000, -500, 500); + mDCAxyVsPtPionsExtended = createHistogram("hDCAxyVsPtResExtended", "DCA_{#it{xy}} EXTENDED Pions;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{xy}}) (#mum)", effHistBins, xbins.data(), 1000, -500, 500); + mDCAzVsPtPionsNormal = createHistogram("hDCAzVsPtResNormal", "DCA_{#it{z}} NORMAL Pions;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", effHistBins, xbins.data(), 1000, -500, 500); + mDCAzVsPtPionsExtended = createHistogram("hDCAzVsPtResExtended", "DCA_{#it{z}} EXTENDED Pions;#it{p}_{T} (GeV/#it{c});#sigma(DCA_{#it{z}}) (#mum)", effHistBins, xbins.data(), 1000, -500, 500); mStream = std::make_unique(mOutFileName.c_str(), "RECREATE"); } @@ -192,8 +257,6 @@ void TrackExtensionStudy::run(ProcessingContext& pc) mClustersMCLCont = recoData.getITSClustersMCLabels(); mInputITSidxs = recoData.getITSTracksClusterRefs(); - mHClustersCounts->SetBinContent(1, mClusters.size()); - LOGP(info, "** Found in {} rofs:\n\t- {} clusters with {} labels\n\t- {} tracks with {} labels", mTracksROFRecords.size(), mClusters.size(), mClustersMCLCont->getIndexedSize(), mTracks.size(), mTracksMCLabels.size()); LOGP(info, "** Found {} sources from kinematic files", mKineReader->getNSources()); @@ -208,10 +271,13 @@ void TrackExtensionStudy::process() for (int iSource{0}; iSource < mKineReader->getNSources(); ++iSource) { mParticleInfo[iSource].resize(mKineReader->getNEvents(iSource)); // events for (int iEvent{0}; iEvent < mKineReader->getNEvents(iSource); ++iEvent) { + const auto& mcEvent = mKineReader->getMCEventHeader(iSource, iEvent); mParticleInfo[iSource][iEvent].resize(mKineReader->getTracks(iSource, iEvent).size()); // tracks for (auto iPart{0}; iPart < mKineReader->getTracks(iEvent).size(); ++iPart) { - auto& part = mKineReader->getTracks(iSource, iEvent)[iPart]; - mParticleInfo[iSource][iEvent][iPart].event = iEvent; + const auto& part = mKineReader->getTracks(iSource, iEvent)[iPart]; + mParticleInfo[iSource][iEvent][iPart].eventX = mcEvent.GetX(); + mParticleInfo[iSource][iEvent][iPart].eventY = mcEvent.GetY(); + mParticleInfo[iSource][iEvent][iPart].eventZ = mcEvent.GetZ(); mParticleInfo[iSource][iEvent][iPart].pdg = part.GetPdgCode(); mParticleInfo[iSource][iEvent][iPart].pt = part.GetPt(); mParticleInfo[iSource][iEvent][iPart].phi = part.GetPhi(); @@ -287,6 +353,8 @@ void TrackExtensionStudy::process() LOGP(info, "\t- Total number of good: {} ({:.2f} %)", good, good * 100. / mTracks.size()); LOGP(info, "\t- Total number of extensions: {} ({:.2f} %)", extended, extended * 100. / mTracks.size()); + o2::dataformats::VertexBase collision; + o2::dataformats::DCA impactParameter; LOGP(info, "** Filling histograms ... "); for (auto iTrack{0}; iTrack < mTracks.size(); ++iTrack) { auto& lab = mTracksMCLabels[iTrack]; @@ -302,6 +370,7 @@ void TrackExtensionStudy::process() continue; } const auto& trk = part.track; + bool isGood = part.isReco && !part.isFake; mHTrackCounts->Fill(0); std::bitset<7> extPattern{0}; @@ -311,44 +380,85 @@ void TrackExtensionStudy::process() } } - if (!extPattern.any()) { - mHTrackCounts->Fill(1); - if (part.isReco || !part.isFake) { - mHTrackCounts->Fill(2); - } else { - mHTrackCounts->Fill(3); + // Tree + while (mWithTree) { + constexpr float refRadius{70.f}; + constexpr float maxSnp{0.9f}; + auto cTrk = trk; + if (!o2::base::Propagator::Instance()->PropagateToXBxByBz(cTrk, refRadius, maxSnp, 2.f, o2::base::Propagator::MatCorrType::USEMatCorrTGeo)) { + break; } - continue; - } - - if (mWithTree) { std::array xyz{(float)part.mcTrack.GetStartVertexCoordinatesX(), (float)part.mcTrack.GetStartVertexCoordinatesY(), (float)part.mcTrack.GetStartVertexCoordinatesZ()}; std::array pxyz{(float)part.mcTrack.GetStartVertexMomentumX(), (float)part.mcTrack.GetStartVertexMomentumY(), (float)part.mcTrack.GetStartVertexMomentumZ()}; auto pdg = O2DatabasePDG::Instance()->GetParticle(part.pdg); if (pdg == nullptr) { - LOGP(fatal, "MC info not available"); + LOGP(error, "MC info not available"); + break; } auto mcTrk = o2::track::TrackPar(xyz, pxyz, TMath::Nint(pdg->Charge() / 3.), true); + if (!mcTrk.rotate(cTrk.getAlpha()) || !o2::base::Propagator::Instance()->PropagateToXBxByBz(mcTrk, refRadius, maxSnp, 2.f, o2::base::Propagator::MatCorrType::USEMatCorrTGeo)) { + break; + } (*mStream) << "tree" - << "trk=" << trk + << "trk=" << cTrk << "mcTrk=" << mcTrk + << "isGood=" << isGood + << "isExtended=" << extPattern.any() << "\n"; + break; + } + + // impact parameter + while (isGood && std::abs(part.pdg) == 211) { + auto trkC = part.track; + collision.setXYZ(part.eventX, part.eventY, part.eventZ); + if (!o2::base::Propagator::Instance()->propagateToDCA(collision, trkC, o2::base::Propagator::Instance()->getNominalBz(), 2.0, o2::base::Propagator::MatCorrType::USEMatCorrTGeo, &impactParameter)) { + break; + } + + auto dcaXY = impactParameter.getY() * 1e4; + auto dcaZ = impactParameter.getZ() * 1e4; + if (!extPattern.any()) { + mDCAxyVsPtPionsNormal->Fill(part.pt, dcaXY); + mDCAzVsPtPionsNormal->Fill(part.pt, dcaZ); + } else { + mDCAxyVsPtPionsExtended->Fill(part.pt, dcaXY); + mDCAzVsPtPionsExtended->Fill(part.pt, dcaZ); + } + break; + } + + mEExtensionDen->Fill(trk.getPt()); + + if (!extPattern.any()) { + mHTrackCounts->Fill(1); + if (part.isReco || !part.isFake) { + mHTrackCounts->Fill(2); + } else { + mHTrackCounts->Fill(3); + } + continue; } mHTrackCounts->Fill(4); mHLengthAny->Fill(trk.getNClusters()); mHChi2Any->Fill(trk.getChi2()); mHPtAny->Fill(trk.getPt()); - if (part.isReco || !part.isFake) { + mEExtensionNum->Fill(trk.getPt()); + mEExtensionPurityDen->Fill(trk.getPt()); + mEExtensionFakeDen->Fill(trk.getPt()); + if (isGood) { mHTrackCounts->Fill(5); mHLengthGood->Fill(trk.getNClusters()); mHChi2Good->Fill(trk.getChi2()); mHPtGood->Fill(trk.getPt()); + mEExtensionPurityNum->Fill(trk.getPt()); } else { mHTrackCounts->Fill(6); mHLengthFake->Fill(trk.getNClusters()); mHChi2Fake->Fill(trk.getChi2()); mHPtFake->Fill(trk.getPt()); + mEExtensionFakeNum->Fill(trk.getPt()); } std::bitset<7> clusPattern{static_cast(trk.getPattern())}; @@ -356,29 +466,28 @@ void TrackExtensionStudy::process() if (extPattern.test(iLayer)) { extPattern.set(iLayer); mHExtensionAny->Fill(iLayer); - if (part.isReco || !part.isFake) { + if (isGood) { mHExtensionGood->Fill(iLayer); } else { mHExtensionFake->Fill(iLayer); } - - if ((part.fakeClusters & (0x1 << iLayer)) == 0) { - mHClustersCounts->Fill(4); - } else { - mHClustersCounts->Fill(5); - } } } - std::bitset<7> oldPattern{clusPattern & ~extPattern}; + std::bitset<7> oldPattern{clusPattern & ~extPattern}, holePattern{clusPattern}; + holePattern.flip(); auto clusN = clusPattern.to_ulong(); auto clusIdx = std::distance(std::begin(mBitPatternsAfter), std::find(std::begin(mBitPatternsAfter), std::end(mBitPatternsAfter), clusN)); auto oldN = oldPattern.to_ulong(); auto oldIdx = std::distance(std::begin(mBitPatternsBefore), std::find(std::begin(mBitPatternsBefore), std::end(mBitPatternsBefore), oldN)); mHExtensionPatternsAny->Fill(oldIdx, clusIdx); - if (part.isReco || !part.isFake) { + if (isGood) { mHExtensionPatternsGood->Fill(oldIdx, clusIdx); + mEExtensionPatternGoodNum[oldIdx]->Fill(trk.getPt()); + mEExtensionPatternIndGoodNum[oldIdx][clusIdx]->Fill(trk.getPt()); } else { mHExtensionPatternsFake->Fill(oldIdx, clusIdx); + mEExtensionPatternFakeNum[oldIdx]->Fill(trk.getPt()); + mEExtensionPatternIndFakeNum[oldIdx][clusIdx]->Fill(trk.getPt()); } // old pattern @@ -392,17 +501,70 @@ void TrackExtensionStudy::process() } } } - if (oldFake && newFake) { mHTrackCounts->Fill(9); + mEExtensionFakeMixNum->Fill(trk.getPt()); } else if (oldFake) { mHTrackCounts->Fill(7); + mEExtensionFakeBeforeNum->Fill(trk.getPt()); } else if (newFake) { mHTrackCounts->Fill(8); + mEExtensionFakeAfterNum->Fill(trk.getPt()); } - mHClustersCounts->SetBinContent(2, mHClustersCounts->GetBinContent(2) + oldPattern.count()); - mHClustersCounts->SetBinContent(3, mHClustersCounts->GetBinContent(3) + extPattern.count()); + // Check if we missed some clusters + if (isGood && holePattern.any()) { + auto missPattern{clusPattern}, emptyPattern{clusPattern}; + for (int iLayer{0}; iLayer < 7; ++iLayer) { + if (!holePattern.test(iLayer)) { + continue; + } + + // Check if there was actually a cluster that we missed + if ((part.clusters & (1 << iLayer)) != 0) { + missPattern.set(iLayer); + } else { + emptyPattern.set(iLayer); + } + } + + if (missPattern != clusPattern) { + auto missN = missPattern.to_ulong(); + auto missIdx = std::distance(std::begin(mBitPatternsAfter), std::find(std::begin(mBitPatternsAfter), std::end(mBitPatternsAfter), missN)); + mHExtensionPatternsGoodMissed->Fill(clusIdx, missIdx); + } + if (emptyPattern != clusPattern) { + auto emptyN = emptyPattern.to_ulong(); + auto emptyIdx = std::distance(std::begin(mBitPatternsAfter), std::find(std::begin(mBitPatternsAfter), std::end(mBitPatternsAfter), emptyN)); + mHExtensionPatternsGoodEmpty->Fill(clusIdx, emptyIdx); + } + } + + // Top/Bot/Mixed Extension + bool isTop = (extPattern & mTopMask).any(); + bool isBot = (extPattern & mBotMask).any(); + if (isTop && isBot) { + mEExtensionMixNum->Fill(trk.getPt()); + if (isGood) { + mEExtensionMixPurityNum->Fill(trk.getPt()); + } else { + mEExtensionMixFakeNum->Fill(trk.getPt()); + } + } else if (isTop) { + mEExtensionTopNum->Fill(trk.getPt()); + if (isGood) { + mEExtensionTopPurityNum->Fill(trk.getPt()); + } else { + mEExtensionTopFakeNum->Fill(trk.getPt()); + } + } else { + mEExtensionBotNum->Fill(trk.getPt()); + if (isGood) { + mEExtensionBotPurityNum->Fill(trk.getPt()); + } else { + mEExtensionBotFakeNum->Fill(trk.getPt()); + } + } } } @@ -421,39 +583,57 @@ void TrackExtensionStudy::endOfStream(EndOfStreamContext& ec) { LOGP(info, "Writing results to {}", mOutFileName); mStream->GetFile()->cd(); + for (const auto h : mHistograms) { + h->Write(); + } - mHTrackCounts->Write(); - mHClustersCounts->Write(); - - mHLengthAny->Write(); - mHLengthGood->Write(); - mHLengthFake->Write(); - - mHChi2Any->Write(); - mHChi2Good->Write(); - mHChi2Fake->Write(); - - mHPtAny->Write(); - mHPtGood->Write(); - mHPtFake->Write(); - - mHExtensionAny->Write(); - mHExtensionGood->Write(); - mHExtensionFake->Write(); - - mHExtensionPatternsAny->Write(); - mHExtensionPatternsGood->Write(); - mHExtensionPatternsFake->Write(); + LOGP(info, "Calculating efficiencies"); + auto makeEff = [](auto num, auto den, const char* name, const char* title) { + auto e = std::make_unique(*num, *den); + e->SetName(name); + e->SetTitle(title); + e->Write(); + }; + makeEff(mEExtensionNum.get(), mEExtensionDen.get(), "eExtension", "Track Extension EXT TRK/ALL"); + makeEff(mEExtensionPurityNum.get(), mEExtensionPurityDen.get(), "eExtensionPurity", "Track Extension Purity GOOD/EXT TRK"); + makeEff(mEExtensionFakeNum.get(), mEExtensionFakeDen.get(), "eExtensionFake", "Track Extension Fake FAKE/EXT TRK"); + makeEff(mEExtensionFakeBeforeNum.get(), mEExtensionFakeNum.get(), "eExtensionFakeBefore", "Track Extension Fake FAKE BEF/FAKE EXT TRK"); + makeEff(mEExtensionFakeAfterNum.get(), mEExtensionFakeNum.get(), "eExtensionFakeAfter", "Track Extension Fake FAKE AFT/FAKE EXT TRK"); + makeEff(mEExtensionFakeMixNum.get(), mEExtensionFakeNum.get(), "eExtensionFakeMix", "Track Extension Fake FAKE MIX/FAKE EXT TRK"); + makeEff(mEExtensionTopNum.get(), mEExtensionDen.get(), "eExtensionTop", "Track Extension Top"); + makeEff(mEExtensionTopPurityNum.get(), mEExtensionPurityDen.get(), "eExtensionTopPurity", "Track Extension Purity GOOD TOP/EXT TRK"); + makeEff(mEExtensionTopFakeNum.get(), mEExtensionFakeNum.get(), "eExtensionTopFake", "Track Extension FAKE TOP/EXT FAKE TRK"); + makeEff(mEExtensionBotNum.get(), mEExtensionDen.get(), "eExtensionBot", "Track Extension Bot"); + makeEff(mEExtensionBotPurityNum.get(), mEExtensionPurityDen.get(), "eExtensionBotPurity", "Track Extension Purity GOOD BOT/EXT TRK"); + makeEff(mEExtensionBotFakeNum.get(), mEExtensionFakeNum.get(), "eExtensionBotFake", "Track Extension FAKE BOT/EXT FAKE TRK"); + makeEff(mEExtensionMixNum.get(), mEExtensionDen.get(), "eExtensionMix", "Track Extension Mix"); + makeEff(mEExtensionMixPurityNum.get(), mEExtensionPurityDen.get(), "eExtensionMixPurity", "Track Extension Purity GOOD MIX/EXT TRK"); + makeEff(mEExtensionMixFakeNum.get(), mEExtensionFakeNum.get(), "eExtensionMixFake", "Track Extension FAKE MIX/EXT FAKE TRK"); + for (int i{0}; i < mBitPatternsBefore.size(); ++i) { + makeEff(mEExtensionPatternGoodNum[i].get(), mEExtensionPurityNum.get(), fmt::format("eExtensionPatternGood_{:07b}", mBitPatternsBefore[i]).c_str(), fmt::format("Extended Tracks Pattern (GOOD) {:07b} GOOD EXT TRK/EXT TRK", mBitPatternsBefore[i]).c_str()); + makeEff(mEExtensionPatternFakeNum[i].get(), mEExtensionFakeNum.get(), fmt::format("eExtensionPatternFake_{:07b}", mBitPatternsBefore[i]).c_str(), fmt::format("Extended Tracks Pattern (FAKE) {:07b} FAKE EXT TRK/EXT TRK", mBitPatternsBefore[i]).c_str()); + for (int j{0}; j < mBitPatternsAfter.size(); ++j) { + makeEff(mEExtensionPatternIndGoodNum[i][j].get(), mEExtensionPatternGoodNum[i].get(), fmt::format("eExtensionPatternGood_{:07b}_{:07b}", mBitPatternsBefore[i], mBitPatternsAfter[j]).c_str(), fmt::format("Extended Tracks Pattern (GOOD) {:07b} -> {:07b} GOOD EXT TRK/EXT TRK", mBitPatternsBefore[i], mBitPatternsAfter[j]).c_str()); + makeEff(mEExtensionPatternIndFakeNum[i][j].get(), mEExtensionPatternFakeNum[i].get(), fmt::format("eExtensionPatternFake_{:07b}_{:07b}", mBitPatternsBefore[i], mBitPatternsAfter[j]).c_str(), fmt::format("Extended Tracks Pattern (FAKE) {:07b} -> {:07b} FAKE EXT TRK/EXT TRK", mBitPatternsBefore[i], mBitPatternsAfter[j]).c_str()); + } + } mStream->Close(); } -DataProcessorSpec getTrackExtensionStudy(mask_t srcTracksMask, mask_t srcClustersMask, bool useMC, std::shared_ptr kineReader) +void TrackExtensionStudy::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } +} + +DataProcessorSpec getTrackExtensionStudy(mask_t srcTracksMask, mask_t srcClustersMask, std::shared_ptr kineReader) { std::vector outputs; auto dataRequest = std::make_shared(); - dataRequest->requestTracks(srcTracksMask, useMC); - dataRequest->requestClusters(srcClustersMask, useMC); + dataRequest->requestTracks(srcTracksMask, true); + dataRequest->requestClusters(srcClustersMask, true); auto ggRequest = std::make_shared(false, // orbitResetTime true, // GRPECS=true @@ -468,7 +648,7 @@ DataProcessorSpec getTrackExtensionStudy(mask_t srcTracksMask, mask_t srcCluster "its-study-track-extension", dataRequest->inputs, outputs, - AlgorithmSpec{adaptFromTask(dataRequest, srcTracksMask, useMC, kineReader, ggRequest)}, + AlgorithmSpec{adaptFromTask(dataRequest, srcTracksMask, kineReader, ggRequest)}, Options{{"with-tree", o2::framework::VariantType::Bool, false, {"Produce in addition a tree"}}}}; } diff --git a/Detectors/ITSMFT/ITS/postprocessing/workflow/standalone-postprocessing-workflow.cxx b/Detectors/ITSMFT/ITS/postprocessing/workflow/standalone-postprocessing-workflow.cxx index 02a75def154fc..30fb39c77f235 100644 --- a/Detectors/ITSMFT/ITS/postprocessing/workflow/standalone-postprocessing-workflow.cxx +++ b/Detectors/ITSMFT/ITS/postprocessing/workflow/standalone-postprocessing-workflow.cxx @@ -113,11 +113,14 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) specs.emplace_back(o2::its::study::getAnomalyStudy(srcCls, useMC)); } if (configcontext.options().get("track-extension-study")) { + if (!useMC) { + LOGP(fatal, "Track Extension Study needs MC!"); + } anyStudy = true; srcTrc = GID::getSourcesMask(configcontext.options().get("track-sources")); srcCls = GID::getSourcesMask("ITS"); - o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, useMC, srcCls, srcTrc); - specs.emplace_back(o2::its::study::getTrackExtensionStudy(srcTrc, srcCls, useMC, mcKinematicsReader)); + o2::globaltracking::InputHelper::addInputSpecs(configcontext, specs, srcCls, srcTrc, srcTrc, true, srcCls, srcTrc); + specs.emplace_back(o2::its::study::getTrackExtensionStudy(srcTrc, srcCls, mcKinematicsReader)); } if (configcontext.options().get("efficiency-study")) { anyStudy = true; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h index 1c4d3360bc7a3..976d01f1d476b 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Configuration.h @@ -94,11 +94,17 @@ struct TrackingParameters { unsigned long MaxMemory = 12000000000UL; float MaxChi2ClusterAttachment = 60.f; float MaxChi2NDF = 30.f; - bool UseTrackFollower = false; bool FindShortTracks = false; bool PerPrimaryVertexProcessing = false; bool SaveTimeBenchmarks = false; bool DoUPCIteration = false; + /// Cluster attachment + bool UseTrackFollower = false; + bool UseTrackFollowerTop = false; + bool UseTrackFollowerBot = false; + bool UseTrackFollowerMix = false; + float TrackFollowerNSigmaCutZ = 1.f; + float TrackFollowerNSigmaCutPhi = 1.f; }; inline int TrackingParameters::CellMinimumLevel() diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h index 70de43d83d8d2..58483e4aa9f6f 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Tracker.h @@ -122,7 +122,7 @@ float Tracker::evaluateTask(void (Tracker::*task)(T...), const char* taskName, s { float diff{0.f}; - if (constants::DoTimeBenchmarks) { + if constexpr (constants::DoTimeBenchmarks) { auto start = std::chrono::high_resolution_clock::now(); (this->*task)(std::forward(args)...); auto end = std::chrono::high_resolution_clock::now(); diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h index 207dd5d3d50f5..46499db92d4d5 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackerTraits.h @@ -78,10 +78,10 @@ class TrackerTraits bool isMatLUT() const; // Others - GPUhd() static constexpr int4 getEmptyBinsRect() { return int4{0, 0, 0, 0}; } - const int4 getBinsRect(const Cluster&, int layer, float z1, float z2, float maxdeltaz, float maxdeltaphi); - const int4 getBinsRect(int layer, float phi, float maxdeltaphi, float z, float maxdeltaz); - const int4 getBinsRect(int layer, float phi, float maxdeltaphi, float z1, float z2, float maxdeltaz); + GPUhd() static consteval int4 getEmptyBinsRect() { return int4{0, 0, 0, 0}; } + const int4 getBinsRect(const Cluster&, int layer, float z1, float z2, float maxdeltaz, float maxdeltaphi) const noexcept; + const int4 getBinsRect(int layer, float phi, float maxdeltaphi, float z, float maxdeltaz) const noexcept; + 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; } @@ -112,6 +112,12 @@ class TrackerTraits bool mIsGPU = false; }; +inline void TrackerTraits::initialiseTimeFrame(const int iteration) +{ + mTimeFrame->initialise(iteration, mTrkParams[iteration], mTrkParams[iteration].NLayers); + setIsGPU(false); +} + inline float TrackerTraits::getBz() const { return mBz; @@ -122,40 +128,32 @@ inline void TrackerTraits::UpdateTrackingParameters(const std::vectorinitialise(iteration, mTrkParams[iteration], mTrkParams[iteration].NLayers); - setIsGPU(false); -} - -inline const int4 TrackerTraits::getBinsRect(const int layerIndex, float phi, float maxdeltaphi, - float z1, float z2, float maxdeltaz) +inline const int4 TrackerTraits::getBinsRect(const int layerIndex, float phi, float maxdeltaphi, float z1, float z2, float maxdeltaz) const noexcept { const float zRangeMin = o2::gpu::GPUCommonMath::Min(z1, z2) - maxdeltaz; const float phiRangeMin = (maxdeltaphi > constants::math::Pi) ? 0.f : phi - maxdeltaphi; const float zRangeMax = o2::gpu::GPUCommonMath::Max(z1, z2) + maxdeltaz; const float phiRangeMax = (maxdeltaphi > constants::math::Pi) ? constants::math::TwoPi : phi + maxdeltaphi; - if (zRangeMax < -mTrkParams[0].LayerZ[layerIndex + 1] || - zRangeMin > mTrkParams[0].LayerZ[layerIndex + 1] || zRangeMin > zRangeMax) { - + if (zRangeMax < -mTrkParams[0].LayerZ[layerIndex] || + zRangeMin > mTrkParams[0].LayerZ[layerIndex] || zRangeMin > zRangeMax) { return getEmptyBinsRect(); } const IndexTableUtils& utils{mTimeFrame->mIndexTableUtils}; - return int4{o2::gpu::GPUCommonMath::Max(0, utils.getZBinIndex(layerIndex + 1, zRangeMin)), + return int4{o2::gpu::GPUCommonMath::Max(0, utils.getZBinIndex(layerIndex, zRangeMin)), utils.getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMin)), - o2::gpu::GPUCommonMath::Min(mTrkParams[0].ZBins - 1, utils.getZBinIndex(layerIndex + 1, zRangeMax)), // /!\ trkParams can potentially change across iterations + o2::gpu::GPUCommonMath::Min(mTrkParams[0].ZBins - 1, utils.getZBinIndex(layerIndex, zRangeMax)), // /!\ trkParams can potentially change across iterations utils.getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMax))}; } } // namespace its diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h index fe5e52bd6277a..68bfdb51170b5 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TrackingConfigParam.h @@ -72,7 +72,9 @@ 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; diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h index 7fb5d1421877e..ac0cf51921176 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/Vertexer.h @@ -176,7 +176,7 @@ float Vertexer::evaluateTask(void (Vertexer::*task)(T...), const char* taskName, { float diff{0.f}; - if (constants::DoTimeBenchmarks) { + if constexpr (constants::DoTimeBenchmarks) { auto start = std::chrono::high_resolution_clock::now(); (this->*task)(std::forward(args)...); auto end = std::chrono::high_resolution_clock::now(); diff --git a/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx b/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx index 9a43402a2e93a..721452bf0361d 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Tracker.cxx @@ -120,7 +120,7 @@ void Tracker::clustersToTracks(std::function logger, std::f total += evaluateTask(&Tracker::findShortPrimaries, "Short primaries finding", logger); std::stringstream sstream; - if (constants::DoTimeBenchmarks) { + if constexpr (constants::DoTimeBenchmarks) { sstream << std::setw(2) << " - " << "Timeframe " << mTimeFrameCounter++ << " processing completed in: " << total << "ms using " << mTraits->getNThreads() << " threads."; } @@ -200,7 +200,7 @@ void Tracker::clustersToTracksHybrid(std::function logger, // total += evaluateTask(&Tracker::findShortPrimaries, "Hybrid short primaries finding", logger); std::stringstream sstream; - if (constants::DoTimeBenchmarks) { + if constexpr (constants::DoTimeBenchmarks) { sstream << std::setw(2) << " - " << "Timeframe " << mTimeFrameCounter++ << " processing completed in: " << total << "ms using " << mTraits->getNThreads() << " threads."; } @@ -502,8 +502,16 @@ void Tracker::getGlobalConfiguration() if (tc.maxMemory) { params.MaxMemory = tc.maxMemory; } - if (tc.useTrackFollower >= 0) { - params.UseTrackFollower = tc.useTrackFollower; + if (tc.useTrackFollower > 0) { + params.UseTrackFollower = true; + // Bit 0: Allow for mixing of top&bot extension --> implies Bits 1&2 set + // Bit 1: Allow for top extension + // Bit 2: Allow for bot extension + params.UseTrackFollowerMix = ((tc.useTrackFollower & (1 << 0)) != 0); + params.UseTrackFollowerTop = ((tc.useTrackFollower & (1 << 1)) != 0); + params.UseTrackFollowerBot = ((tc.useTrackFollower & (1 << 2)) != 0); + params.TrackFollowerNSigmaCutZ = tc.trackFollowerNSigmaZ; + params.TrackFollowerNSigmaCutPhi = tc.trackFollowerNSigmaPhi; } if (tc.cellsPerClusterLimit >= 0) { params.CellsPerClusterLimit = tc.cellsPerClusterLimit; diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx index 4457d4515e0a6..da0abbae9dc1f 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx @@ -111,7 +111,7 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iROFslice, in const float sqInverseDeltaZ0{1.f / (Sq(currentCluster.zCoordinate - primaryVertex.getZ()) + 2.e-8f)}; /// protecting from overflows adding the detector resolution const float sigmaZ{o2::gpu::CAMath::Sqrt(Sq(resolution) * Sq(tanLambda) * ((Sq(inverseR0) + sqInverseDeltaZ0) * Sq(meanDeltaR) + 1.f) + Sq(meanDeltaR * tf->getMSangle(iLayer)))}; - const int4 selectedBinsRect{getBinsRect(currentCluster, iLayer, zAtRmin, zAtRmax, + const int4 selectedBinsRect{getBinsRect(currentCluster, iLayer + 1, zAtRmin, zAtRmax, sigmaZ * mTrkParams[iteration].NSigmaCut, tf->getPhiCut(iLayer))}; if (selectedBinsRect.x == 0 && selectedBinsRect.y == 0 && selectedBinsRect.z == 0 && selectedBinsRect.w == 0) { continue; @@ -679,10 +679,11 @@ void TrackerTraits::extendTracks(const int iteration) for (auto& track : mTimeFrame->getTracks(rof)) { auto backup{track}; bool success{false}; - if (track.getLastClusterLayer() != mTrkParams[iteration].NLayers - 1) { + // 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 (track.getFirstClusterLayer() != 0) { + if ((mTrkParams[iteration].UseTrackFollowerMix || (mTrkParams[iteration].UseTrackFollowerBot && !success)) && track.getFirstClusterLayer() != 0) { success = success || trackFollowing(&track, rof, false, iteration); } if (success) { @@ -830,8 +831,8 @@ bool TrackerTraits::fitTrack(TrackITSExt& track, int start, int end, int step, f } if (mCorrType == o2::base::PropagatorF::MatCorrType::USEMatCorrNONE) { - float radl = 9.36f; // Radiation length of Si [cm] - float rho = 2.33f; // Density of Si [g/cm^3] + constexpr float radl = 9.36f; // Radiation length of Si [cm] + constexpr float rho = 2.33f; // Density of Si [g/cm^3] if (!track.correctForMaterial(mTrkParams[0].LayerxX0[iLayer], mTrkParams[0].LayerxX0[iLayer] * radl * rho, true)) { continue; } @@ -855,36 +856,40 @@ bool TrackerTraits::trackFollowing(TrackITSExt* track, int rof, bool outward, co auto propInstance = o2::base::Propagator::Instance(); const int step = -1 + outward * 2; const int end = outward ? mTrkParams[iteration].NLayers - 1 : 0; - std::vector hypotheses(1, *track); - for (auto& hypo : hypotheses) { - int iLayer = outward ? track->getLastClusterLayer() : track->getFirstClusterLayer(); + std::vector hypotheses(1, *track); // 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; + 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) { - float radl = 9.36f; // Radiation length of Si [cm] - float rho = 2.33f; // Density of Si [g/cm^3] + if (mTrkParams[iteration].CorrType == PropagatorF::MatCorrType::USEMatCorrNONE) { // account for material affects if propagator does not + constexpr float radl = 9.36f; // Radiation length of Si [cm] + constexpr float rho = 2.33f; // Density of Si [g/cm^3] if (!hypoParam.correctForMaterial(mTrkParams[iteration].LayerxX0[iLayer], mTrkParams[iteration].LayerxX0[iLayer] * radl * 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].NSigmaCut * ePhi, z, mTrkParams[iteration].NSigmaCut * eZ)}; - + 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; } @@ -900,9 +905,8 @@ bool TrackerTraits::trackFollowing(TrackITSExt* track, int rof, bool outward, co continue; } - TrackITSExt currentHypo{hypo}, newHypo{hypo}; - bool first{true}; - for (int iPhiCount{0}; iPhiCount < phiBinsNum; iPhiCount++) { + // 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}; @@ -921,7 +925,7 @@ bool TrackerTraits::trackFollowing(TrackITSExt* track, int rof, bool outward, co const TrackingFrameInfo& trackingHit = mTimeFrame->getTrackingFrameInfoOnLayer(iLayer).at(nextCluster.clusterId); - TrackITSExt& tbupdated = first ? hypo : newHypo; + auto tbupdated{hypo}; auto& tbuParams = outward ? tbupdated.getParamOut() : tbupdated.getParamIn(); if (!tbuParams.rotate(trackingHit.alphaTrackingFrame)) { continue; @@ -942,12 +946,7 @@ bool TrackerTraits::trackFollowing(TrackITSExt* track, int rof, bool outward, co } tbupdated.setChi2(tbupdated.getChi2() + predChi2); /// This is wrong for outward propagation as the chi2 refers to inward parameters tbupdated.setExternalClusterIndex(iLayer, nextCluster.clusterId, true); - - if (!first) { - hypotheses.emplace_back(tbupdated); - newHypo = currentHypo; - } - first = false; + hypotheses.emplace_back(tbupdated); } } } From 83049afbb3b006fa19be960363f66b53485a3bec Mon Sep 17 00:00:00 2001 From: shahoian Date: Sun, 24 Nov 2024 18:42:16 +0100 Subject: [PATCH 0095/2180] Add AltroSyncSignal to TPC CDBTypeMap --- Detectors/TPC/base/include/TPCBase/CDBTypes.h | 128 +++++++++--------- 1 file changed, 66 insertions(+), 62 deletions(-) diff --git a/Detectors/TPC/base/include/TPCBase/CDBTypes.h b/Detectors/TPC/base/include/TPCBase/CDBTypes.h index 27cc2e5a79589..75278f2a76902 100644 --- a/Detectors/TPC/base/include/TPCBase/CDBTypes.h +++ b/Detectors/TPC/base/include/TPCBase/CDBTypes.h @@ -24,68 +24,70 @@ namespace o2::tpc /// Calibration and parameter types for CCDB enum class CDBType { - CalPedestal, ///< Pedestal calibration - CalNoise, ///< Noise calibration - CalPedestalNoise, ///< Pedestal and Noise calibration - CalPulser, ///< Pulser calibration - CalCE, ///< Laser CE calibration - CalPadGainFull, ///< Full pad gain calibration - CalPadGainResidual, ///< ResidualpPad gain calibration (e.g. from tracks) - CalLaserTracks, ///< Laser track calibration data - CalVDriftTgl, ///< ITS-TPC difTgl vdrift calibration - CalTimeGain, ///< Gain variation over time - CalTimeGainMC, ///< Gain variation over time for MC - CalGas, ///< DCS gas measurements - CalTemperature, ///< DCS temperature measurements - CalHV, ///< DCS HV measurements - CalTopologyGain, ///< Q cluster topology correction - /// - ConfigFEEPad, ///< FEE pad-by-pad configuration map - ConfigFEE, ///< FEE configuration map for each tag - ConfigRunInfo, ///< FEE run information (run -> tag) - /// - ParDetector, ///< Parameter for Detector - ParElectronics, ///< Parameter for Electronics - ParGas, ///< Parameter for Gas - ParGEM, ///< Parameter for GEM - /// - CalIDC0A, ///< I_0(r,\phi) = _t - CalIDC0C, ///< I_0(r,\phi) = _t - CalIDC1A, ///< I_1(t) = _{r,\phi} - CalIDC1C, ///< I_1(t) = _{r,\phi} - CalIDCDeltaA, ///< \Delta I(r,\phi,t) = I(r,\phi,t) / ( I_0(r,\phi) * I_1(t) ) - CalIDCDeltaC, ///< \Delta I(r,\phi,t) = I(r,\phi,t) / ( I_0(r,\phi) * I_1(t) ) - CalIDCFourierA, ///< Fourier coefficients of CalIDC1 - CalIDCFourierC, ///< Fourier coefficients of CalIDC1 - CalIDCPadStatusMapA, ///< Status map of the pads (dead etc. obatined from CalIDC0) - CalIDCPadStatusMapC, ///< Status map of the pads (dead etc. obatined from CalIDC0) - CalIDCGroupingParA, ///< Parameters which were used for the averaging of the CalIDCDelta - CalIDCGroupingParC, ///< Parameters which were used for the averaging of the CalIDCDelta - /// - CalSAC0, ///< I_0(r,\phi) = _t - CalSAC1, ///< I_1(t) = _{r,\phi} - CalSACDelta, ///< \Delta I(r,\phi,t) = I(r,\phi,t) / ( I_0(r,\phi) * I_1(t) ) - CalSACFourier, ///< Fourier coefficients of CalSAC1 - /// - CalITPC0, ///< 2D average TPC clusters for longer time interval - CalITPC1, ///< 1D integrated TPC clusters - /// - CalCorrMap, ///< Cluster correction map (high IR rate distortions) - CalCorrMapRef, ///< Cluster correction reference map (static distortions) - CalCorrMapMC, ///< Cluster correction map (high IR rate distortions) for MC - CalCorrDerivMapMC, ///< Cluster correction reference map (static distortions) for MC - /// - CalCorrDerivMap, ///< Cluster correction map (derivative map) - /// - CalTimeSeries, ///< integrated DCAs for longer time interval - CalScaler, ///< Scaler from IDCs or combined estimator - CalScalerWeights, ///< Weights for scalers - CalMShape, ///< calibration object for M-shape distortions - /// - CorrMapParam, ///< parameters for CorrectionMapsLoader configuration - /// - DistortionMapMC, ///< full distortions (static + IR dependant) for MC used in the digitizer - DistortionMapDerivMC ///< derivative distortions for MC used in the digitizer for scaling + CalPedestal, ///< Pedestal calibration + CalNoise, ///< Noise calibration + CalPedestalNoise, ///< Pedestal and Noise calibration + CalPulser, ///< Pulser calibration + CalCE, ///< Laser CE calibration + CalPadGainFull, ///< Full pad gain calibration + CalPadGainResidual, ///< ResidualpPad gain calibration (e.g. from tracks) + CalLaserTracks, ///< Laser track calibration data + CalVDriftTgl, ///< ITS-TPC difTgl vdrift calibration + CalTimeGain, ///< Gain variation over time + CalTimeGainMC, ///< Gain variation over time for MC + CalGas, ///< DCS gas measurements + CalTemperature, ///< DCS temperature measurements + CalHV, ///< DCS HV measurements + CalTopologyGain, ///< Q cluster topology correction + /// + ConfigFEEPad, ///< FEE pad-by-pad configuration map + ConfigFEE, ///< FEE configuration map for each tag + ConfigRunInfo, ///< FEE run information (run -> tag) + /// + ParDetector, ///< Parameter for Detector + ParElectronics, ///< Parameter for Electronics + ParGas, ///< Parameter for Gas + ParGEM, ///< Parameter for GEM + /// + CalIDC0A, ///< I_0(r,\phi) = _t + CalIDC0C, ///< I_0(r,\phi) = _t + CalIDC1A, ///< I_1(t) = _{r,\phi} + CalIDC1C, ///< I_1(t) = _{r,\phi} + CalIDCDeltaA, ///< \Delta I(r,\phi,t) = I(r,\phi,t) / ( I_0(r,\phi) * I_1(t) ) + CalIDCDeltaC, ///< \Delta I(r,\phi,t) = I(r,\phi,t) / ( I_0(r,\phi) * I_1(t) ) + CalIDCFourierA, ///< Fourier coefficients of CalIDC1 + CalIDCFourierC, ///< Fourier coefficients of CalIDC1 + CalIDCPadStatusMapA, ///< Status map of the pads (dead etc. obatined from CalIDC0) + CalIDCPadStatusMapC, ///< Status map of the pads (dead etc. obatined from CalIDC0) + CalIDCGroupingParA, ///< Parameters which were used for the averaging of the CalIDCDelta + CalIDCGroupingParC, ///< Parameters which were used for the averaging of the CalIDCDelta + /// + CalSAC0, ///< I_0(r,\phi) = _t + CalSAC1, ///< I_1(t) = _{r,\phi} + CalSACDelta, ///< \Delta I(r,\phi,t) = I(r,\phi,t) / ( I_0(r,\phi) * I_1(t) ) + CalSACFourier, ///< Fourier coefficients of CalSAC1 + /// + CalITPC0, ///< 2D average TPC clusters for longer time interval + CalITPC1, ///< 1D integrated TPC clusters + /// + CalCorrMap, ///< Cluster correction map (high IR rate distortions) + CalCorrMapRef, ///< Cluster correction reference map (static distortions) + CalCorrMapMC, ///< Cluster correction map (high IR rate distortions) for MC + CalCorrDerivMapMC, ///< Cluster correction reference map (static distortions) for MC + /// + CalCorrDerivMap, ///< Cluster correction map (derivative map) + /// + CalTimeSeries, ///< integrated DCAs for longer time interval + CalScaler, ///< Scaler from IDCs or combined estimator + CalScalerWeights, ///< Weights for scalers + CalMShape, ///< calibration object for M-shape distortions + /// + CorrMapParam, ///< parameters for CorrectionMapsLoader configuration + /// + DistortionMapMC, ///< full distortions (static + IR dependant) for MC used in the digitizer + DistortionMapDerivMC, ///< derivative distortions for MC used in the digitizer for scaling + + AltroSyncSignal ///< timing of Altro chip sync. signal }; /// Storage name in CCDB for each calibration and parameter type @@ -153,6 +155,8 @@ const std::unordered_map CDBTypeMap{ // distortion maps {CDBType::DistortionMapMC, "TPC/Calib/DistortionMapMC"}, {CDBType::DistortionMapDerivMC, "TPC/Calib/DistortionMapDerivativeMC"}, + // AltroSyncSignal + {CDBType::AltroSyncSignal, "TPC/Config/AltroSyncSignal"}, }; } // namespace o2::tpc From 756634ddda00e5794414877ed8476bc166e42fe3 Mon Sep 17 00:00:00 2001 From: pillot Date: Thu, 21 Nov 2024 16:16:55 +0100 Subject: [PATCH 0096/2180] change Mathieson K3xy --- Detectors/MUON/MCH/Base/include/MCHBase/ResponseParam.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Detectors/MUON/MCH/Base/include/MCHBase/ResponseParam.h b/Detectors/MUON/MCH/Base/include/MCHBase/ResponseParam.h index a545bb670d59b..6dba967694026 100644 --- a/Detectors/MUON/MCH/Base/include/MCHBase/ResponseParam.h +++ b/Detectors/MUON/MCH/Base/include/MCHBase/ResponseParam.h @@ -30,11 +30,11 @@ struct ResponseParam : public o2::conf::ConfigurableParamHelper { float pitchSt1 = 0.21; ///< anode-cathode pitch (cm) for station 1 float pitchSt2345 = 0.25; ///< anode-cathode pitch (cm) for station 2 to 5 - float mathiesonSqrtKx3St1 = 0.7000; ///< Mathieson parameter sqrt(K3) in x direction for station 1 - float mathiesonSqrtKx3St2345 = 0.7131; ///< Mathieson parameter sqrt(K3) in x direction for station 2 to 5 + float mathiesonSqrtKx3St1 = 0.5477; ///< Mathieson parameter sqrt(K3) in x direction for station 1 + float mathiesonSqrtKx3St2345 = 0.5477; ///< Mathieson parameter sqrt(K3) in x direction for station 2 to 5 - float mathiesonSqrtKy3St1 = 0.7550; ///< Mathieson parameter sqrt(K3) in y direction for station 1 - float mathiesonSqrtKy3St2345 = 0.7642; ///< Mathieson parameter sqrt(K3) in y direction for station 2 to 5 + float mathiesonSqrtKy3St1 = 0.5477; ///< Mathieson parameter sqrt(K3) in y direction for station 1 + float mathiesonSqrtKy3St2345 = 0.5477; ///< Mathieson parameter sqrt(K3) in y direction for station 2 to 5 float chargeSlopeSt1 = 25.; ///< charge slope used in E to charge conversion for station 1 float chargeSlopeSt2345 = 10.; ///< charge slope used in E to charge conversion for station 2 to 5 From c7c9f5464919fe5890a96cfff985ce66ccabbc75 Mon Sep 17 00:00:00 2001 From: shahoian Date: Mon, 25 Nov 2024 17:30:05 +0100 Subject: [PATCH 0097/2180] Fix in the parsing of ccdb-run-dependent options --- .../Core/src/DataDescriptorQueryBuilder.cxx | 27 +++++++------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/Framework/Core/src/DataDescriptorQueryBuilder.cxx b/Framework/Core/src/DataDescriptorQueryBuilder.cxx index 41a14d06f3acc..8b0c239699cc9 100644 --- a/Framework/Core/src/DataDescriptorQueryBuilder.cxx +++ b/Framework/Core/src/DataDescriptorQueryBuilder.cxx @@ -319,12 +319,9 @@ std::vector DataDescriptorQueryBuilder::parse(char const* config) if (*currentKey == "lifetime" && currentValue == "condition") { currentLifetime = Lifetime::Condition; } - if (*currentKey == "ccdb-run-dependent" && (currentValue != "false" && currentValue != "0")) { - attributes.push_back(ConfigParamSpec{*currentKey, VariantType::Bool, true, {}}); - } else if (*currentKey == "ccdb-run-dependent" && (currentValue == "false" || currentValue == "0")) { - attributes.push_back(ConfigParamSpec{*currentKey, VariantType::Bool, false, {}}); - } else if (*currentKey == "ccdb-run-dependent") { - error("ccdb-run-dependent can only be true or false"); + if (*currentKey == "ccdb-run-dependent") { + int val = currentValue == "false" ? 0 : (currentValue == "true" ? 1 : std::stoi(*currentValue)); + attributes.push_back(ConfigParamSpec{*currentKey, VariantType::Int, val, {}}); } else { attributes.push_back(ConfigParamSpec{*currentKey, VariantType::String, *currentValue, {}}); } @@ -333,12 +330,9 @@ std::vector DataDescriptorQueryBuilder::parse(char const* config) if (*currentKey == "lifetime" && currentValue == "condition") { currentLifetime = Lifetime::Condition; } - if (*currentKey == "ccdb-run-dependent" && (currentValue != "false" && currentValue != "0")) { - attributes.push_back(ConfigParamSpec{*currentKey, VariantType::Bool, true, {}}); - } else if (*currentKey == "ccdb-run-dependent" && (currentValue == "false" || currentValue == "0")) { - attributes.push_back(ConfigParamSpec{*currentKey, VariantType::Bool, false, {}}); - } else if (*currentKey == "ccdb-run-dependent") { - error("ccdb-run-dependent can only be true or false"); + if (*currentKey == "ccdb-run-dependent") { + int val = currentValue == "false" ? 0 : (currentValue == "true" ? 1 : std::stoi(*currentValue)); + attributes.push_back(ConfigParamSpec{*currentKey, VariantType::Int, val, {}}); } else { attributes.push_back(ConfigParamSpec{*currentKey, VariantType::String, *currentValue, {}}); } @@ -347,12 +341,9 @@ std::vector DataDescriptorQueryBuilder::parse(char const* config) if (*currentKey == "lifetime" && currentValue == "condition") { currentLifetime = Lifetime::Condition; } - if (*currentKey == "ccdb-run-dependent" && (currentValue != "false" && currentValue != "0")) { - attributes.push_back(ConfigParamSpec{*currentKey, VariantType::Bool, true, {}}); - } else if (*currentKey == "ccdb-run-dependent" && (currentValue == "false" || currentValue == "0")) { - attributes.push_back(ConfigParamSpec{*currentKey, VariantType::Bool, false, {}}); - } else if (*currentKey == "ccdb-run-dependent") { - error("ccdb-run-dependent can only be true or false"); + if (*currentKey == "ccdb-run-dependent") { + int val = currentValue == "false" ? 0 : (currentValue == "true" ? 1 : std::stoi(*currentValue)); + attributes.push_back(ConfigParamSpec{*currentKey, VariantType::Int, val, {}}); } else { attributes.push_back(ConfigParamSpec{*currentKey, VariantType::String, *currentValue, {}}); } From 6321af7e1c7d840b49ab6f041afaaaefe3312516 Mon Sep 17 00:00:00 2001 From: swenzel Date: Mon, 25 Nov 2024 13:35:21 +0100 Subject: [PATCH 0098/2180] Ability to create configurable param from a given external struct This commit provides the new feature of being able to create configurable param instances from a given external C++ struct. For example, a C++ struct `struct A { double x; }` can be used to define a correspondig configurable parameter via ``` class MyConfigParamA : o2::conf::ConfigurableParamPromoter {} ``` This allows to: - have "template" structs to create multiple params of the same structure - separate data from configkey functionality - extract data copies from underlying configurable parameters The application thereof is demonstrated for GeneratorPythia8Param. A unit test is added to check the new functionality. --- .../include/CommonUtils/ConfigurableParam.h | 24 +-- .../CommonUtils/ConfigurableParamHelper.h | 158 +++++++++++++++++- Common/Utils/src/ConfigurableParamHelper.cxx | 30 ++-- Generators/CMakeLists.txt | 7 + .../Generators/GeneratorPythia8Param.h | 19 +-- Generators/src/GeneratorPythia8.cxx | 23 +-- Generators/src/GeneratorPythia8Param.cxx | 2 +- Generators/src/GeneratorsLinkDef.h | 3 +- .../test/test_GeneratorPythia8Param.cxx | 80 +++++++++ 9 files changed, 278 insertions(+), 68 deletions(-) create mode 100644 Generators/test/test_GeneratorPythia8Param.cxx diff --git a/Common/Utils/include/CommonUtils/ConfigurableParam.h b/Common/Utils/include/CommonUtils/ConfigurableParam.h index 717a4c425fc82..f44d9efcaea76 100644 --- a/Common/Utils/include/CommonUtils/ConfigurableParam.h +++ b/Common/Utils/include/CommonUtils/ConfigurableParam.h @@ -321,17 +321,19 @@ class ConfigurableParam } // end namespace o2 // a helper macro for boilerplate code in parameter classes -#define O2ParamDef(classname, key) \ - public: \ - classname(TRootIOCtor*) {} \ - classname(classname const&) = delete; \ - \ - private: \ - static constexpr char const* const sKey = key; \ - static classname sInstance; \ - classname() = default; \ - template \ - friend class o2::conf::ConfigurableParamHelper; +#define O2ParamDef(classname, key) \ + public: \ + classname(TRootIOCtor*) {} \ + classname(classname const&) = delete; \ + \ + private: \ + static constexpr char const* const sKey = key; \ + static classname sInstance; \ + classname() = default; \ + template \ + friend class o2::conf::ConfigurableParamHelper; \ + template \ + friend class o2::conf::ConfigurableParamPromoter; // a helper macro to implement necessary symbols in source #define O2ParamImpl(classname) classname classname::sInstance; diff --git a/Common/Utils/include/CommonUtils/ConfigurableParamHelper.h b/Common/Utils/include/CommonUtils/ConfigurableParamHelper.h index 1dc5d5c4c38f8..7d9cb78bb9968 100644 --- a/Common/Utils/include/CommonUtils/ConfigurableParamHelper.h +++ b/Common/Utils/include/CommonUtils/ConfigurableParamHelper.h @@ -45,18 +45,18 @@ class _ParamHelper { private: static std::vector* getDataMembersImpl(std::string const& mainkey, TClass* cl, void*, - std::map const* provmap); + std::map const* provmap, size_t virtualoffset); static void fillKeyValuesImpl(std::string const& mainkey, TClass* cl, void*, boost::property_tree::ptree*, std::map>*, - EnumRegistry*); + EnumRegistry*, size_t offset); static void printWarning(std::type_info const&); static void assignmentImpl(std::string const& mainkey, TClass* cl, void* to, void* from, - std::map* provmap); + std::map* provmap, size_t offset); static void syncCCDBandRegistry(std::string const& mainkey, TClass* cl, void* to, void* from, - std::map* provmap); + std::map* provmap, size_t offset); static void outputMembersImpl(std::ostream& out, std::string const& mainkey, std::vector const* members, bool showProv, bool useLogger); static void printMembersImpl(std::string const& mainkey, std::vector const* members, bool showProv, bool useLogger); @@ -65,6 +65,9 @@ class _ParamHelper template friend class ConfigurableParamHelper; + + template + friend class ConfigurableParamPromoter; }; // ---------------------------------------------------------------- @@ -140,7 +143,7 @@ class ConfigurableParamHelper : virtual public ConfigurableParam return nullptr; } - return _ParamHelper::getDataMembersImpl(getName(), cl, (void*)this, sValueProvenanceMap); + return _ParamHelper::getDataMembersImpl(getName(), cl, (void*)this, sValueProvenanceMap, 0); } // ---------------------------------------------------------------- @@ -153,7 +156,7 @@ class ConfigurableParamHelper : virtual public ConfigurableParam _ParamHelper::printWarning(typeid(P)); return; } - _ParamHelper::fillKeyValuesImpl(getName(), cl, (void*)this, tree, sKeyToStorageMap, sEnumRegistry); + _ParamHelper::fillKeyValuesImpl(getName(), cl, (void*)this, tree, sKeyToStorageMap, sEnumRegistry, 0); } // ---------------------------------------------------------------- @@ -167,7 +170,7 @@ class ConfigurableParamHelper : virtual public ConfigurableParam file->GetObject(getName().c_str(), readback); if (readback != nullptr) { _ParamHelper::assignmentImpl(getName(), TClass::GetClass(typeid(P)), (void*)this, (void*)readback, - sValueProvenanceMap); + sValueProvenanceMap, 0); delete readback; } setRegisterMode(true); @@ -185,7 +188,146 @@ class ConfigurableParamHelper : virtual public ConfigurableParam // setRegisterMode(false); _ParamHelper::syncCCDBandRegistry(getName(), TClass::GetClass(typeid(P)), (void*)this, (void*)externalobj, - sValueProvenanceMap); + sValueProvenanceMap, 0); + setRegisterMode(true); + } + + // ---------------------------------------------------------------- + + void serializeTo(TFile* file) const final + { + file->WriteObjectAny((void*)this, TClass::GetClass(typeid(P)), getName().c_str()); + } +}; + +// Promotes a simple struct Base to a configurable parameter class +// Aka implements all interfaces for a ConfigurableParam P, which shares or +// takes the fields from a Base struct +template +class ConfigurableParamPromoter : public Base, virtual public ConfigurableParam +{ + public: + using ConfigurableParam::ConfigurableParam; + + static const P& Instance() + { + return P::sInstance; + } + + // extracts a copy of the underlying data struct + Base detach() const + { + static_assert(std::copyable, "Base type must be copyable."); + return static_cast(*this); + } + + // ---------------------------------------------------------------- + std::string getName() const final + { + return P::sKey; + } + + // ---------------------------------------------------------------- + // get the provenace of the member with given key + EParamProvenance getMemberProvenance(const std::string& key) const final + { + return getProvenance(getName() + '.' + key); + } + + // ---------------------------------------------------------------- + + // one of the key methods, using introspection to print itself + void printKeyValues(bool showProv = true, bool useLogger = false) const final + { + if (!isInitialized()) { + initialize(); + } + auto members = getDataMembers(); + _ParamHelper::printMembersImpl(getName(), members, showProv, useLogger); + } + + // + size_t getHash() const final + { + return _ParamHelper::getHashImpl(getName(), getDataMembers()); + } + + // ---------------------------------------------------------------- + + void output(std::ostream& out) const final + { + auto members = getDataMembers(); + _ParamHelper::outputMembersImpl(out, getName(), members, true, false); + } + + // ---------------------------------------------------------------- + + // Grab the list of ConfigurableParam data members + // Returns a nullptr if the TClass of the P template class cannot be created. + std::vector* getDataMembers() const + { + // just a helper line to make sure P::sInstance is looked-up + // and that compiler complains about missing static sInstance of type P + // volatile void* ptr = (void*)&P::sInstance; + // static assert on type of sInstance: + static_assert(std::is_same::value, + "static instance must of same type as class"); + + // obtain the TClass for the Base type and delegate further + auto cl = TClass::GetClass(typeid(Base)); + if (!cl) { + _ParamHelper::printWarning(typeid(Base)); + return nullptr; + } + + // we need to put an offset of 8 bytes since internally this is using data members of the Base class + // which doesn't account for the virtual table of P + return _ParamHelper::getDataMembersImpl(getName(), cl, (void*)this, sValueProvenanceMap, 8); + } + + // ---------------------------------------------------------------- + + // fills the data structures with the initial default values + void putKeyValues(boost::property_tree::ptree* tree) final + { + auto cl = TClass::GetClass(typeid(Base)); + if (!cl) { + _ParamHelper::printWarning(typeid(Base)); + return; + } + _ParamHelper::fillKeyValuesImpl(getName(), cl, (void*)this, tree, sKeyToStorageMap, sEnumRegistry, 8); + } + + // ---------------------------------------------------------------- + + void initFrom(TFile* file) final + { + // switch off auto registering since the readback object is + // only a "temporary" singleton + setRegisterMode(false); + P* readback = nullptr; + file->GetObject(getName().c_str(), readback); + if (readback != nullptr) { + _ParamHelper::assignmentImpl(getName(), TClass::GetClass(typeid(Base)), (void*)this, (void*)readback, + sValueProvenanceMap, 8); + delete readback; + } + setRegisterMode(true); + } + + // ---------------------------------------------------------------- + + void syncCCDBandRegistry(void* externalobj) final + { + // We may be getting an external copy from CCDB which is passed as externalobj. + // The task of this function is to + // a) update the internal registry with fields coming from CCDB + // but only if keys have not been modified via RT == command line / ini file + // b) update the external object with with fields having RT provenance + // + setRegisterMode(false); + _ParamHelper::syncCCDBandRegistry(getName(), TClass::GetClass(typeid(Base)), (void*)this, (void*)externalobj, + sValueProvenanceMap, 8); setRegisterMode(true); } diff --git a/Common/Utils/src/ConfigurableParamHelper.cxx b/Common/Utils/src/ConfigurableParamHelper.cxx index 0fb213b722e26..f217d402bcb45 100644 --- a/Common/Utils/src/ConfigurableParamHelper.cxx +++ b/Common/Utils/src/ConfigurableParamHelper.cxx @@ -182,19 +182,19 @@ std::string asString(TDataMember const& dm, char* pointer) // potentially other cases to be added here LOG(error) << "COULD NOT REPRESENT AS STRING"; - return nullptr; + return std::string(); } // ---------------------------------------------------------------------- std::vector* _ParamHelper::getDataMembersImpl(std::string const& mainkey, TClass* cl, void* obj, - std::map const* provmap) + std::map const* provmap, size_t globaloffset) { std::vector* members = new std::vector; - auto toDataMember = [&members, obj, mainkey, provmap](const TDataMember* dm, int index, int size) { + auto toDataMember = [&members, obj, mainkey, provmap, globaloffset](const TDataMember* dm, int index, int size) { auto TS = getSizeOfUnderlyingType(*dm); - char* pointer = ((char*)obj) + dm->GetOffset() + index * TS; + char* pointer = ((char*)obj) + dm->GetOffset() + index * TS + globaloffset; const std::string name = getName(dm, index, size); auto value = asString(*dm, pointer); @@ -280,14 +280,14 @@ std::type_info const& nameToTypeInfo(const char* tname, TDataType const* dt) void _ParamHelper::fillKeyValuesImpl(std::string const& mainkey, TClass* cl, void* obj, boost::property_tree::ptree* tree, std::map>* keytostoragemap, - EnumRegistry* enumRegistry) + EnumRegistry* enumRegistry, size_t globaloffset) { boost::property_tree::ptree localtree; - auto fillMap = [obj, &mainkey, &localtree, &keytostoragemap, &enumRegistry](const TDataMember* dm, int index, int size) { + auto fillMap = [obj, &mainkey, &localtree, &keytostoragemap, &enumRegistry, globaloffset](const TDataMember* dm, int index, int size) { const auto name = getName(dm, index, size); auto dt = dm->GetDataType(); auto TS = getSizeOfUnderlyingType(*dm); - char* pointer = ((char*)obj) + dm->GetOffset() + index * TS; + char* pointer = ((char*)obj) + dm->GetOffset() + index * TS + globaloffset; localtree.put(name, asString(*dm, pointer)); auto key = mainkey + "." + name; @@ -355,14 +355,14 @@ bool isMemblockDifferent(char const* block1, char const* block2, int sizeinbytes // ---------------------------------------------------------------------- void _ParamHelper::assignmentImpl(std::string const& mainkey, TClass* cl, void* to, void* from, - std::map* provmap) + std::map* provmap, size_t globaloffset) { - auto assignifchanged = [to, from, &mainkey, provmap](const TDataMember* dm, int index, int size) { + auto assignifchanged = [to, from, &mainkey, provmap, globaloffset](const TDataMember* dm, int index, int size) { const auto name = getName(dm, index, size); auto dt = dm->GetDataType(); auto TS = getSizeOfUnderlyingType(*dm); - char* pointerto = ((char*)to) + dm->GetOffset() + index * TS; - char* pointerfrom = ((char*)from) + dm->GetOffset() + index * TS; + char* pointerto = ((char*)to) + dm->GetOffset() + index * TS + globaloffset; + char* pointerfrom = ((char*)from) + dm->GetOffset() + index * TS + globaloffset; // lambda to update the provenance auto updateProv = [&mainkey, name, provmap]() { @@ -402,14 +402,14 @@ void _ParamHelper::assignmentImpl(std::string const& mainkey, TClass* cl, void* // ---------------------------------------------------------------------- void _ParamHelper::syncCCDBandRegistry(const std::string& mainkey, TClass* cl, void* to, void* from, - std::map* provmap) + std::map* provmap, size_t globaloffset) { - auto sync = [to, from, &mainkey, provmap](const TDataMember* dm, int index, int size) { + auto sync = [to, from, &mainkey, provmap, globaloffset](const TDataMember* dm, int index, int size) { const auto name = getName(dm, index, size); auto dt = dm->GetDataType(); auto TS = getSizeOfUnderlyingType(*dm); - char* pointerto = ((char*)to) + dm->GetOffset() + index * TS; - char* pointerfrom = ((char*)from) + dm->GetOffset() + index * TS; + char* pointerto = ((char*)to) + dm->GetOffset() + index * TS + globaloffset; + char* pointerfrom = ((char*)from) + dm->GetOffset() + index * TS + globaloffset; // check current provenance auto key = mainkey + "." + name; diff --git a/Generators/CMakeLists.txt b/Generators/CMakeLists.txt index d60d185817c84..d909b3e604887 100644 --- a/Generators/CMakeLists.txt +++ b/Generators/CMakeLists.txt @@ -129,8 +129,15 @@ if(doBuildSimulation) COMPONENT_NAME Generator LABELS generator PUBLIC_LINK_LIBRARIES O2::Generators) + + o2_add_test(GeneratorPythia8Param NAME test_Generator_test_GeneratorPythia8Param + SOURCES test/test_GeneratorPythia8Param.cxx + COMPONENT_NAME Generator + LABELS generator + PUBLIC_LINK_LIBRARIES O2::Generators) endif() + o2_add_test_root_macro(share/external/tgenerator.C PUBLIC_LINK_LIBRARIES O2::Generators LABELS generators) diff --git a/Generators/include/Generators/GeneratorPythia8Param.h b/Generators/include/Generators/GeneratorPythia8Param.h index 165b1622239f5..612964fca73d9 100644 --- a/Generators/include/Generators/GeneratorPythia8Param.h +++ b/Generators/include/Generators/GeneratorPythia8Param.h @@ -24,28 +24,21 @@ namespace eventgen { /** - ** a parameter class/struct to keep the settings of - ** the Pythia8 event generator and - ** allow the user to modify them + ** a parameter class/struct to configure the settings of + ** the GeneratorPythia8 event generator **/ - -struct GeneratorPythia8Param : public o2::conf::ConfigurableParamHelper { +struct Pythia8GenConfig { std::string config = ""; std::string hooksFileName = ""; std::string hooksFuncName = ""; bool includePartonEvent = false; // whether to keep the event before hadronization std::string particleFilter = ""; // user particle filter int verbose = 0; // verbose control (if > 0 may show more info messages about what is going on) - O2ParamDef(GeneratorPythia8Param, "GeneratorPythia8"); }; -struct Pythia8GenConfig { - std::string config = ""; - std::string hooksFileName = ""; - std::string hooksFuncName = ""; - bool includePartonEvent = false; // whether to keep the event before hadronization - std::string particleFilter = ""; // user particle filter - int verbose = 0; // verbose control (if > 0 may show more info messages about what is going on) +// construct a configurable param singleton out of the Pythia8GenConfig struct +struct GeneratorPythia8Param : public o2::conf::ConfigurableParamPromoter { + O2ParamDef(GeneratorPythia8Param, "GeneratorPythia8"); }; } // end namespace eventgen diff --git a/Generators/src/GeneratorPythia8.cxx b/Generators/src/GeneratorPythia8.cxx index 8c9b4fcffdff2..fef2c4d2e9a1c 100644 --- a/Generators/src/GeneratorPythia8.cxx +++ b/Generators/src/GeneratorPythia8.cxx @@ -45,26 +45,11 @@ namespace eventgen /*****************************************************************/ /*****************************************************************/ -GeneratorPythia8::GeneratorPythia8() : Generator("ALICEo2", "ALICEo2 Pythia8 Generator") +// the default construct uses the GeneratorPythia8Param singleton to extract a config and delegates +// to the proper constructor +GeneratorPythia8::GeneratorPythia8() : GeneratorPythia8(GeneratorPythia8Param::Instance().detach()) { - /** default constructor **/ - - mInterface = reinterpret_cast(&mPythia); - mInterfaceName = "pythia8"; - - auto& param = GeneratorPythia8Param::Instance(); - LOG(info) << "Default Instance \'Pythia8\' generator with following parameters"; - LOG(info) << param; - - // convert the outside singleton config to the internally used one - o2::eventgen::Pythia8GenConfig config{param.config, - param.hooksFileName, param.hooksFuncName, param.includePartonEvent, param.particleFilter, param.verbose}; - mGenConfig = config; - - setConfig(config.config); - setHooksFileName(config.hooksFileName); - setHooksFuncName(config.hooksFuncName); - // TODO: use constructor delegation to other interface + LOG(info) << "GeneratorPythia8 constructed from GeneratorPythia8Param ConfigurableParam"; } /*****************************************************************/ diff --git a/Generators/src/GeneratorPythia8Param.cxx b/Generators/src/GeneratorPythia8Param.cxx index 984680e46ad01..6b477beb16ba9 100644 --- a/Generators/src/GeneratorPythia8Param.cxx +++ b/Generators/src/GeneratorPythia8Param.cxx @@ -12,4 +12,4 @@ /// \author R+Preghenella - January 2020 #include "Generators/GeneratorPythia8Param.h" -O2ParamImpl(o2::eventgen::GeneratorPythia8Param); +O2ParamImpl(o2::eventgen::GeneratorPythia8Param); \ No newline at end of file diff --git a/Generators/src/GeneratorsLinkDef.h b/Generators/src/GeneratorsLinkDef.h index 41e14b02f18b9..fe219c6f5476c 100644 --- a/Generators/src/GeneratorsLinkDef.h +++ b/Generators/src/GeneratorsLinkDef.h @@ -48,8 +48,9 @@ #pragma link C++ class o2::eventgen::GeneratorPythia8Param + ; #pragma link C++ class o2::eventgen::Pythia8GenConfig + ; #pragma link C++ class o2::eventgen::DecayerPythia8Param + ; -#pragma link C++ class o2::conf::ConfigurableParamHelper < o2::eventgen::GeneratorPythia8Param> + ; +#pragma link C++ class o2::conf::ConfigurableParamPromoter < o2::eventgen::GeneratorPythia8Param, o2::eventgen::Pythia8GenConfig> + ; #pragma link C++ class o2::conf::ConfigurableParamHelper < o2::eventgen::DecayerPythia8Param> + ; + #pragma link C++ class o2::eventgen::GeneratorFactory + ; #endif #if defined(GENERATORS_WITH_PYTHIA8) && defined(GENERATORS_WITH_HEPMC3) diff --git a/Generators/test/test_GeneratorPythia8Param.cxx b/Generators/test/test_GeneratorPythia8Param.cxx new file mode 100644 index 0000000000000..c735487ea293c --- /dev/null +++ b/Generators/test/test_GeneratorPythia8Param.cxx @@ -0,0 +1,80 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +#define BOOST_TEST_MODULE Test GeneratorPythia8Param class +#define BOOST_TEST_MAIN +#define BOOST_TEST_DYN_LINK +#include +#include +#include +#include +#include "CCDB/BasicCCDBManager.h" + +// Tests various aspects of the +// ConfigurableParamPromoter class, which is used to promote +// Pythia8GenConfig to a configurable param +BOOST_AUTO_TEST_CASE(pythia8_Pythia8GenConfig) +{ + o2::conf::ConfigurableParam::updateFromString( + "GeneratorPythia8.config=Foo;GeneratorPythia8.includePartonEvent=true"); + + using o2::eventgen::GeneratorPythia8Param; + + BOOST_CHECK_EQUAL(GeneratorPythia8Param::Instance().config, std::string("Foo")); + BOOST_CHECK_EQUAL(GeneratorPythia8Param::Instance().includePartonEvent, true); + + BOOST_CHECK_EQUAL(GeneratorPythia8Param::Instance().includePartonEvent, o2::conf::ConfigurableParam::getValueAs("GeneratorPythia8.includePartonEvent")); + // setValue - getValue + o2::conf::ConfigurableParam::setValue("GeneratorPythia8.config", "Baz"); + BOOST_CHECK_EQUAL(o2::conf::ConfigurableParam::getValueAs("GeneratorPythia8.config"), std::string("Baz")); + BOOST_CHECK_EQUAL(GeneratorPythia8Param::Instance().config, std::string("Baz")); + + // member provenance + BOOST_CHECK_EQUAL(GeneratorPythia8Param::Instance().getMemberProvenance("config"), o2::conf::ConfigurableParam::EParamProvenance::kRT); + BOOST_CHECK_EQUAL(GeneratorPythia8Param::Instance().getMemberProvenance("verbose"), o2::conf::ConfigurableParam::EParamProvenance::kCODE); + + // config detach + auto config_copy = GeneratorPythia8Param::Instance().detach(); + BOOST_CHECK_EQUAL(config_copy.config, std::string("Baz")); + BOOST_CHECK_EQUAL(config_copy.includePartonEvent, true); + + // file IO + TFile tmp_file("GeneratorParamConfig_tmp.root", "RECREATE"); + + GeneratorPythia8Param::Instance().serializeTo(&tmp_file); + // modify the instance to some intermediate fluent value + o2::conf::ConfigurableParam::setValue("GeneratorPythia8.includePartonEvent", "0"); + BOOST_CHECK_EQUAL(config_copy.includePartonEvent, true); + BOOST_CHECK_EQUAL(GeneratorPythia8Param::Instance().includePartonEvent, false); + tmp_file.Close(); + + // read back + TFile tmp_file2("GeneratorParamConfig_tmp.root", "READ"); + const_cast(GeneratorPythia8Param::Instance()).initFrom(&tmp_file2); + BOOST_CHECK_EQUAL(GeneratorPythia8Param::Instance().includePartonEvent, true); + tmp_file2.Close(); + + // CCDB IO + std::string ccdbUrl = "http://ccdb-test.cern.ch:8080"; + bool hostReachable = false; + o2::ccdb::CcdbApi api; + api.init(ccdbUrl); + std::string pathA = "/Generators/UnitTest/Pythia8/GeneratorPythia8Param"; + std::map md; + long start = 1000, stop = 2000; + api.storeAsTFileAny(&GeneratorPythia8Param::Instance(), pathA, md, start, stop); + + // modify the instance to some intermediate fluent value + o2::conf::ConfigurableParam::setValue("GeneratorPythia8.includePartonEvent", "0"); + + auto returnedobj = api.retrieveFromTFileAny(pathA, md, (start + stop) / 2); + GeneratorPythia8Param::Instance().printKeyValues(); +}; From 80827ee98564632ed38bd4ff1dfe990f20dd0885 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Tue, 26 Nov 2024 09:16:12 +0100 Subject: [PATCH 0099/2180] DPL: add test for exception throwing in Variant (#13735) --- Framework/Core/include/Framework/Variant.h | 2 +- Framework/Core/test/test_Variants.cxx | 12 ++++++++++++ 2 files changed, 13 insertions(+), 1 deletion(-) diff --git a/Framework/Core/include/Framework/Variant.h b/Framework/Core/include/Framework/Variant.h index 54a91e90c3638..e69ca05b91d98 100644 --- a/Framework/Core/include/Framework/Variant.h +++ b/Framework/Core/include/Framework/Variant.h @@ -355,7 +355,7 @@ class Variant T get() const { if (mType != variant_trait_v) { - throw runtime_error("Mismatch between types"); + throw runtime_error_f("Variant::get: Mismatch between types %d %d.", mType, variant_trait_v); } return variant_helper::get(&mStore); } diff --git a/Framework/Core/test/test_Variants.cxx b/Framework/Core/test/test_Variants.cxx index a0edf40cf9f0d..a364b228871da 100644 --- a/Framework/Core/test/test_Variants.cxx +++ b/Framework/Core/test/test_Variants.cxx @@ -338,3 +338,15 @@ TEST_CASE("VariantJSONConversionsTest") REQUIRE(vstrings[i] == vvstra.get()[i]); } } + +TEST_CASE("VariantThrowing") +{ + Variant a("true"); + REQUIRE_THROWS_AS(a.get(), o2::framework::RuntimeErrorRef); + try { + a.get(); + } catch (RuntimeErrorRef& ref) { + RuntimeError& error = error_from_ref(ref); + REQUIRE(error.what == std::string("Variant::get: Mismatch between types 4 0.")); + } +} From 73f5254100243ef666a98cea55afaec6f79da221 Mon Sep 17 00:00:00 2001 From: Sergio Date: Mon, 25 Nov 2024 10:41:03 +0100 Subject: [PATCH 0100/2180] Log TrackRefs size before failing assert --- run/checkStack.cxx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/run/checkStack.cxx b/run/checkStack.cxx index 4470ea463fd98..4c2b9d5b50075 100644 --- a/run/checkStack.cxx +++ b/run/checkStack.cxx @@ -143,8 +143,8 @@ int main(int argc, char** argv) if (havereferences) { for (auto& trackID : trackidsinTPC) { auto trackrefs = mcreader.getTrackRefs(eventID, trackID); - assert(trackrefs.size() > 0); LOG(debug) << " Track " << trackID << " has " << trackrefs.size() << " TrackRefs"; + assert(trackrefs.size() > 0); for (auto& ref : trackrefs) { assert(ref.getTrackID() == trackID); } From 63349508cdcd715c60c4755d7303206d314194e8 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Tue, 12 Nov 2024 15:33:18 +0100 Subject: [PATCH 0101/2180] QC: Glo change tpc DCAr axis-range --- Detectors/GLOQC/src/MatchITSTPCQC.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Detectors/GLOQC/src/MatchITSTPCQC.cxx b/Detectors/GLOQC/src/MatchITSTPCQC.cxx index 56462344850d6..f0345175b9a59 100644 --- a/Detectors/GLOQC/src/MatchITSTPCQC.cxx +++ b/Detectors/GLOQC/src/MatchITSTPCQC.cxx @@ -344,10 +344,10 @@ bool MatchITSTPCQC::init() mChi2Refit = new TH1F("mChi2Refit", "Chi2 of refit; chi2", 200, 0, 300); mChi2Refit->SetOption("logy"); mChi2Refit->GetYaxis()->SetTitleOffset(1.4); - mDCAr = new TH1F("mDCAr", "DCA of TPC tracks; DCAr", 200, -100, 100); - mDCArVsPtNum = new TH2F("mDCArVsPtNum", "DCA of TPC tracks Vs Pt Num; #it{p}_{T} [GeV/c]; DCAr", 100, 0, 20., 200, -30, 30); + mDCAr = new TH1F("mDCAr", "DCA of TPC tracks; DCAr", 100, -mDCATPCCutY, mDCATPCCutY); + mDCArVsPtNum = new TH2F("mDCArVsPtNum", "DCA of TPC tracks Vs Pt Num; #it{p}_{T} [GeV/c]; DCAr", 100, 0, 20., 100, -mDCATPCCutY, mDCATPCCutY); mDCArVsPtNum->Sumw2(); - mDCArVsPtDen = new TH2F("mDCArVsPtDen", "DCA of TPC tracks Vs Pt Den; #it{p}_{T} [GeV/c]; DCAr", 100, 0, 20., 200, -30, 30); + mDCArVsPtDen = new TH2F("mDCArVsPtDen", "DCA of TPC tracks Vs Pt Den; #it{p}_{T} [GeV/c]; DCAr", 100, 0, 20., 100, -mDCATPCCutY, mDCATPCCutY); mDCArVsPtDen->Sumw2(); mFractionITSTPCmatchDCArVsPt = new TEfficiency("mFractionITSTPCmatchDCArVsPt", "Fraction of ITSTPC matched tracks wrt TPC vs DCAr; #it{p}_{T} [GeV#it{c}]; DCAr; Eff", 100, 0, 20., 200, -30, 30); From 061a31051f224e2b2ece2bff8abadb8aa89a016b Mon Sep 17 00:00:00 2001 From: David Rohr Date: Tue, 26 Nov 2024 13:25:31 +0100 Subject: [PATCH 0102/2180] GPU: Some cleanup, and fixes when running only part of processing on GPU --- GPU/GPUTracking/Global/GPUChainTracking.cxx | 27 ------------------ .../Global/GPUChainTrackingClusterizer.cxx | 5 ++-- .../Global/GPUChainTrackingSliceTracker.cxx | 27 ++++++++++++++++++ .../SliceTracker/GPUTPCSliceData.cxx | 28 +++++++++---------- .../SliceTracker/GPUTPCTracker.cxx | 4 +-- 5 files changed, 45 insertions(+), 46 deletions(-) diff --git a/GPU/GPUTracking/Global/GPUChainTracking.cxx b/GPU/GPUTracking/Global/GPUChainTracking.cxx index 8c2599604387b..b06d636970da7 100644 --- a/GPU/GPUTracking/Global/GPUChainTracking.cxx +++ b/GPU/GPUTracking/Global/GPUChainTracking.cxx @@ -921,33 +921,6 @@ int32_t GPUChainTracking::FinalizePipelinedProcessing() return RunChainFinalize(); } -int32_t GPUChainTracking::HelperReadEvent(int32_t iSlice, int32_t threadId, GPUReconstructionHelpers::helperParam* par) { return ReadEvent(iSlice, threadId); } - -int32_t GPUChainTracking::HelperOutput(int32_t iSlice, int32_t threadId, GPUReconstructionHelpers::helperParam* par) -{ - if (param().rec.tpc.globalTracking) { - uint32_t tmpSlice = GPUTPCGlobalTracking::GlobalTrackingSliceOrder(iSlice); - uint32_t sliceLeft, sliceRight; - GPUTPCGlobalTracking::GlobalTrackingSliceLeftRight(tmpSlice, sliceLeft, sliceRight); - - while (mSliceSelectorReady < (int32_t)tmpSlice || mSliceSelectorReady < (int32_t)sliceLeft || mSliceSelectorReady < (int32_t)sliceRight) { - if (par->reset) { - return 1; - } - } - GlobalTracking(tmpSlice, 0); - WriteOutput(tmpSlice, 0); - } else { - while (mSliceSelectorReady < iSlice) { - if (par->reset) { - return 1; - } - } - WriteOutput(iSlice, threadId); - } - return 0; -} - int32_t GPUChainTracking::CheckErrorCodes(bool cpuOnly, bool forceShowErrors, std::vector>* fillErrors) { int32_t retVal = 0; diff --git a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx index ae240181eba65..97870d74ca624 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx @@ -457,6 +457,7 @@ std::pair GPUChainTracking::RunTPCClusterizer_transferZS(int int32_t GPUChainTracking::RunTPCClusterizer_prepare(bool restorePointers) { + bool doGPU = mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCClusterFinding; if (restorePointers) { for (uint32_t iSlice = 0; iSlice < NSLICES; iSlice++) { processors()->tpcClusterer[iSlice].mPzsOffsets = mCFContext->ptrSave[iSlice].zsOffsetHost; @@ -512,7 +513,7 @@ int32_t GPUChainTracking::RunTPCClusterizer_prepare(bool restorePointers) uint32_t threshold = 40000000; uint32_t nDigitsScaled = nDigitsBase > threshold ? nDigitsBase : std::min((threshold + nDigitsBase) / 2, 2 * nDigitsBase); processors()->tpcClusterer[iSlice].SetNMaxDigits(processors()->tpcClusterer[iSlice].mPmemory->counters.nDigits, mCFContext->nPagesFragmentMax, nDigitsScaled, mCFContext->nDigitsEndpointMax[iSlice]); - if (mRec->IsGPU()) { + if (doGPU) { processorsShadow()->tpcClusterer[iSlice].SetNMaxDigits(processors()->tpcClusterer[iSlice].mPmemory->counters.nDigits, mCFContext->nPagesFragmentMax, nDigitsScaled, mCFContext->nDigitsEndpointMax[iSlice]); } if (mPipelineNotifyCtx && GetProcessingSettings().doublePipelineClusterizer) { @@ -578,7 +579,7 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) if (RunTPCClusterizer_prepare(mPipelineNotifyCtx && GetProcessingSettings().doublePipelineClusterizer)) { return 1; } - if (GetProcessingSettings().ompAutoNThreads && !mRec->IsGPU()) { + if (GetProcessingSettings().ompAutoNThreads && !doGPU) { mRec->SetNOMPThreads(mRec->MemoryScalers()->nTPCdigits / 20000); } diff --git a/GPU/GPUTracking/Global/GPUChainTrackingSliceTracker.cxx b/GPU/GPUTracking/Global/GPUChainTrackingSliceTracker.cxx index 8db15fb1aef7e..62c93bcb1bfb5 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingSliceTracker.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingSliceTracker.cxx @@ -532,3 +532,30 @@ void GPUChainTracking::WriteOutput(int32_t iSlice, int32_t threadId) GPUInfo("Finished WriteOutput for slice %d on thread %d\n", iSlice, threadId); } } + +int32_t GPUChainTracking::HelperReadEvent(int32_t iSlice, int32_t threadId, GPUReconstructionHelpers::helperParam* par) { return ReadEvent(iSlice, threadId); } + +int32_t GPUChainTracking::HelperOutput(int32_t iSlice, int32_t threadId, GPUReconstructionHelpers::helperParam* par) +{ + if (param().rec.tpc.globalTracking) { + uint32_t tmpSlice = GPUTPCGlobalTracking::GlobalTrackingSliceOrder(iSlice); + uint32_t sliceLeft, sliceRight; + GPUTPCGlobalTracking::GlobalTrackingSliceLeftRight(tmpSlice, sliceLeft, sliceRight); + + while (mSliceSelectorReady < (int32_t)tmpSlice || mSliceSelectorReady < (int32_t)sliceLeft || mSliceSelectorReady < (int32_t)sliceRight) { + if (par->reset) { + return 1; + } + } + GlobalTracking(tmpSlice, 0); + WriteOutput(tmpSlice, 0); + } else { + while (mSliceSelectorReady < iSlice) { + if (par->reset) { + return 1; + } + } + WriteOutput(iSlice, threadId); + } + return 0; +} diff --git a/GPU/GPUTracking/SliceTracker/GPUTPCSliceData.cxx b/GPU/GPUTracking/SliceTracker/GPUTPCSliceData.cxx index 6908bc326a535..6c456a28918ab 100644 --- a/GPU/GPUTracking/SliceTracker/GPUTPCSliceData.cxx +++ b/GPU/GPUTracking/SliceTracker/GPUTPCSliceData.cxx @@ -265,22 +265,20 @@ GPUdii() int32_t GPUTPCSliceData::InitFromClusterData(int32_t nBlocks, int32_t n for (uint32_t i = iThread; i < NumberOfClusters; i += nThreads) { UpdateMinMaxYZ(yMin, yMax, zMin, zMax, YZData[RowOffset + i].x, YZData[RowOffset + i].y); } + } else if (mem->param.par.earlyTpcTransform) { // Early transform case with ClusterNative present + for (uint32_t i = iThread; i < NumberOfClusters; i += nThreads) { + float2 tmp; + tmp.x = mClusterData[RowOffset + i].y; + tmp.y = mClusterData[RowOffset + i].z; + UpdateMinMaxYZ(yMin, yMax, zMin, zMax, tmp.x, tmp.y); + YZData[RowOffset + i] = tmp; + } } else { - if (mem->param.par.earlyTpcTransform) { // Early transform case with ClusterNative present - for (uint32_t i = iThread; i < NumberOfClusters; i += nThreads) { - float2 tmp; - tmp.x = mClusterData[RowOffset + i].y; - tmp.y = mClusterData[RowOffset + i].z; - UpdateMinMaxYZ(yMin, yMax, zMin, zMax, tmp.x, tmp.y); - YZData[RowOffset + i] = tmp; - } - } else { - for (uint32_t i = iThread; i < NumberOfClusters; i += nThreads) { - float x, y, z; - GPUTPCConvertImpl::convert(*mem, iSlice, rowIndex, mem->ioPtrs.clustersNative->clusters[iSlice][rowIndex][i].getPad(), mem->ioPtrs.clustersNative->clusters[iSlice][rowIndex][i].getTime(), x, y, z); - UpdateMinMaxYZ(yMin, yMax, zMin, zMax, y, z); - YZData[RowOffset + i] = CAMath::MakeFloat2(y, z); - } + for (uint32_t i = iThread; i < NumberOfClusters; i += nThreads) { + float x, y, z; + GPUTPCConvertImpl::convert(*mem, iSlice, rowIndex, mem->ioPtrs.clustersNative->clusters[iSlice][rowIndex][i].getPad(), mem->ioPtrs.clustersNative->clusters[iSlice][rowIndex][i].getTime(), x, y, z); + UpdateMinMaxYZ(yMin, yMax, zMin, zMax, y, z); + YZData[RowOffset + i] = CAMath::MakeFloat2(y, z); } } diff --git a/GPU/GPUTracking/SliceTracker/GPUTPCTracker.cxx b/GPU/GPUTracking/SliceTracker/GPUTPCTracker.cxx index 7428a4ccbd0ed..84bdc52ab6f46 100644 --- a/GPU/GPUTracking/SliceTracker/GPUTPCTracker.cxx +++ b/GPU/GPUTracking/SliceTracker/GPUTPCTracker.cxx @@ -81,7 +81,7 @@ void* GPUTPCTracker::SetPointersScratch(void* mem) if (mRec->GetProcessingSettings().memoryAllocationStrategy != GPUMemoryResource::ALLOCATION_INDIVIDUAL) { mem = SetPointersTracklets(mem); } - if (mRec->IsGPU()) { + if (mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCSliceTracking) { computePointerWithAlignment(mem, mTrackletTmpStartHits, GPUCA_ROW_COUNT * mNMaxRowStartHits); computePointerWithAlignment(mem, mRowStartHitCountOffset, GPUCA_ROW_COUNT); } @@ -164,7 +164,7 @@ void GPUTPCTracker::SetMaxData(const GPUTrackingInOutPointers& io) mNMaxTracks = mRec->MemoryScalers()->NTPCSectorTracks(mData.NumberOfHits()); mNMaxTrackHits = mRec->MemoryScalers()->NTPCSectorTrackHits(mData.NumberOfHits(), mRec->GetProcessingSettings().tpcInputWithClusterRejection); #ifdef GPUCA_SORT_STARTHITS_GPU - if (mRec->IsGPU()) { + if (mRec->GetRecoStepsGPU() & GPUDataTypes::RecoStep::TPCSliceTracking) { if (mNMaxStartHits > mNMaxRowStartHits * GPUCA_ROW_COUNT) { mNMaxStartHits = mNMaxRowStartHits * GPUCA_ROW_COUNT; } From 3d3ee4df7e1e3e5b99b9eb623039633e2e0f721b Mon Sep 17 00:00:00 2001 From: Diego Stocco Date: Tue, 19 Nov 2024 10:40:33 +0100 Subject: [PATCH 0103/2180] Improvements on the MID reject list - Correctly build the reject list even for runs where the issue happens at the very last sampled QC object, just before EOR - Search for switched off boards at the end of the period with bad quality instead of the beginning. This should make sure that we do not miss additional boards that stops sending data - Remove additional time margin at the beginning and end of the run - Get run number from QC metadata --- .../MID/Calibration/macros/build_rejectlist.C | 162 +++++++++++------- 1 file changed, 96 insertions(+), 66 deletions(-) diff --git a/Detectors/MUON/MID/Calibration/macros/build_rejectlist.C b/Detectors/MUON/MID/Calibration/macros/build_rejectlist.C index 7a395d2c099da..48391b4460687 100644 --- a/Detectors/MUON/MID/Calibration/macros/build_rejectlist.C +++ b/Detectors/MUON/MID/Calibration/macros/build_rejectlist.C @@ -50,6 +50,17 @@ struct RejectListStruct { std::vector rejectList{}; /// Bad channels }; +/// @brief Useful metadata +struct MDStruct { + long start = 0; /// Start validity + long end = 0; /// End validity + int runNumber = 0; /// Run number + std::string runType; /// Run Type + + bool operator<(const MDStruct& other) const { return start < other.start; } + bool operator==(const MDStruct& other) const { return start == other.start; } +}; + /// @brief Get timestamp in milliseconds /// @param timestamp Input timestamp (in s or ms) /// @return Timestamp in ms @@ -96,23 +107,33 @@ std::string timeRangeToString(long start, long end) /// @param end Query objects created not after /// @param api CDB api /// @param path CDB path -/// @return Vector of start validity of each object sorted in ascending way -std::vector findObjectsTSInPeriod(long start, long end, const o2::ccdb::CcdbApi& api, const char* path) +/// @return Vector of metadata in ascending order +std::vector findObjectsMDInPeriod(long start, long end, const o2::ccdb::CcdbApi& api, const char* path) { - std::vector ts; - auto out = api.list(path, false, "text/plain", getTSMS(end), getTSMS(start)); - std::stringstream ss(out); - std::string token; - while (ss >> token) { - if (token.find("Validity") != std::string::npos) { - ss >> token; - ts.emplace_back(std::atol(token.c_str())); - } + std::vector mds; + auto out = api.list(path, false, "application/json", getTSMS(end), getTSMS(start)); + rapidjson::Document doc; + doc.Parse(out.c_str()); + for (auto& obj : doc["objects"].GetArray()) { + MDStruct md; + md.start = obj["validFrom"].GetInt64(); + md.end = obj["validUntil"].GetInt64(); + md.runNumber = std::atoi(obj["RunNumber"].GetString()); + md.runType = obj["RunType"].GetString(); + mds.emplace_back(md); } - ts.erase(std::unique(ts.begin(), ts.end()), ts.end()); + mds.erase(std::unique(mds.begin(), mds.end()), mds.end()); // Sort timestamps in ascending order - std::sort(ts.begin(), ts.end()); - return ts; + std::sort(mds.begin(), mds.end()); + return mds; +} + +/// @brief Gets the quality trend graph from the quality canvas +/// @param qcQuality MID QC quality canvas +/// @return Quality trend graph +TGraph* getQualityTrend(const TCanvas* qcQuality) +{ + return static_cast(qcQuality->GetListOfPrimitives()->FindObject("Graph")); } /// @brief Find the first and last time when the quality was good or bad @@ -127,7 +148,7 @@ std::pair findTSRange(TCanvas* qcQuality, bool selectBad = t // Medium: 2.5 // Bad: 1.5 // Null: 0.5 - auto* gr = static_cast(qcQuality->GetListOfPrimitives()->FindObject("Graph")); + auto* gr = getQualityTrend(qcQuality); double xp, yp; std::pair range{std::numeric_limits::max(), 0}; for (int ip = 0; ip < gr->GetN(); ++ip) { @@ -144,6 +165,32 @@ std::pair findTSRange(TCanvas* qcQuality, bool selectBad = t return range; } +/// @brief Gets the first and last timestamp in the quality +/// @param qcQuality MID QC quality canvas +/// @return Pair with the first and last timestamp in the quality trend +std::pair getFirstLast(const TCanvas* qcQuality) +{ + auto* gr = getQualityTrend(qcQuality); + double xp1, xp2, yp; + gr->GetPoint(0, xp1, yp); + gr->GetPoint(gr->GetN() - 1, xp2, yp); + return {static_cast(xp1 * 1000), static_cast(xp2 * 1000)}; +} + +/// @brief Update the selected range of timestamp +/// @param selectedTSRange Reference to the selected range to be modified +/// @param qcTSRange Range of the MID quality trend +/// @param runRange Run range +void updateRange(std::pair& selectedTSRange, const std::pair qcTSRange, const std::pair runRange) +{ + if (selectedTSRange.first == qcTSRange.first) { + selectedTSRange.first = runRange.first; + } + if (selectedTSRange.second == qcTSRange.second) { + selectedTSRange.second = runRange.second; + } +} + /// @brief Find bad channels from the occupancy histograms /// @param hits Occupancy histogram /// @param infos Mapping @@ -186,72 +233,61 @@ std::vector getRejectList(std::vector return badChannels; } -/// @brief Gets the run duration with a safety marging -/// @param ccdbApi CCDB api -/// @param marging margin in milliseconds -/// @return Pair with the timestamps of start-margin and end+margin for the run -std::pair getRunDuration(const o2::ccdb::CcdbApi& ccdbApi, int runNumber, int64_t margin = 120000) -{ - auto runRange = o2::ccdb::BasicCCDBManager::getRunDuration(ccdbApi, runNumber); - runRange.first -= margin; // Subtract margin - runRange.second += margin; // Add margin - return runRange; -} - /// @brief Builds the reject list for the selected timestamp -/// @param timestamp Timestamp for query +/// @param md MD structure /// @param qcdbApi QCDB api /// @param ccdbApi CCDB api /// @param outCCDBApi api of the CCDB where the reject list will be uploaded /// @return Reject list -RejectListStruct build_rejectlist(long timestamp, const o2::ccdb::CcdbApi& qcdbApi, const o2::ccdb::CcdbApi& ccdbApi) +RejectListStruct build_rejectlist(const MDStruct& md, const o2::ccdb::CcdbApi& qcdbApi, const o2::ccdb::CcdbApi& ccdbApi) { - std::map metadata; RejectListStruct rl; - auto* qcQuality = qcdbApi.retrieveFromTFileAny(sPathQCQuality, metadata, getTSMS(timestamp)); + if (md.runType != "PHYSICS") { + std::cout << "Run " << md.runNumber << " is of type " << md.runType << ": skip" << std::endl; + return rl; + } + + std::map metadata; + auto* qcQuality = qcdbApi.retrieveFromTFileAny(sPathQCQuality, metadata, getTSMS(md.start)); if (!qcQuality) { - std::cerr << "Cannot find QC quality for " << tsToString(timestamp) << std::endl; + std::cerr << "Cannot find QC quality for " << tsToString(md.start) << std::endl; return rl; } + // Find the first and last timestamp where the quality was bad (if any) auto badTSRange = findTSRange(qcQuality); if (badTSRange.second == 0) { std::cout << "All good" << std::endl; return rl; } + + // Find the first and last timestamp where the quality flag was set + auto qualityTSRange = getFirstLast(qcQuality); // Search for the last timestamp for which the run quality was good auto goodTSRange = findTSRange(qcQuality, false); - // Query the CCDB to see to which run the timestamp corresponds - auto oldestTSInQCQuality = (goodTSRange.first == 0) ? badTSRange.first : goodTSRange.first; - auto grpecs = *ccdbApi.retrieveFromTFileAny("GLO/Config/GRPECS", metadata, getTSMS(oldestTSInQCQuality)); - if (!grpecs.isDetReadOut(o2::detectors::DetID::MID)) { - std::cout << "Error: we are probably reading a parallel run" << std::endl; - grpecs.print(); - return rl; - } - if (grpecs.getRunType() != o2::parameters::GRPECS::PHYSICS) { - std::cout << "This is not a physics run: skip" << std::endl; - grpecs.print(); - return rl; - } - auto runRange = getRunDuration(ccdbApi, grpecs.getRun()); + auto runRange = o2::ccdb::BasicCCDBManager::getRunDuration(ccdbApi, md.runNumber); + updateRange(badTSRange, qualityTSRange, runRange); + updateRange(goodTSRange, qualityTSRange, runRange); // Search for hits histogram in the period where the QC quality was bad - auto tsVector = findObjectsTSInPeriod(badTSRange.first, badTSRange.second, qcdbApi, "qc/MID/MO/QcTaskMIDDigits/Hits"); - if (tsVector.empty()) { + auto mdVector = findObjectsMDInPeriod(badTSRange.first, badTSRange.second, qcdbApi, "qc/MID/MO/QcTaskMIDDigits/Hits"); + if (mdVector.empty()) { std::cerr << "Cannot find hits in period " << tsToString(badTSRange.first) << " - " << tsToString(badTSRange.second) << std::endl; return {}; } - // Focus on the first object found - TH1* occupancy = qcdbApi.retrieveFromTFileAny("qc/MID/MO/QcTaskMIDDigits/Hits", metadata, getTSMS(tsVector.front())); + // Focus on the last object found + // We chose the last instead of the first because it might happen that + // we lose additional boards before the EOR + // If we build the reject list for the first object, we would therefore miss some boards + TH1* occupancy = qcdbApi.retrieveFromTFileAny("qc/MID/MO/QcTaskMIDDigits/Hits", metadata, getTSMS(mdVector.back().start)); o2::mid::GlobalMapper gm; auto infos = gm.buildStripsInfo(); auto badChannels = findBadChannels(occupancy, infos); - auto badChannelsCCDB = *ccdbApi.retrieveFromTFileAny>("MID/Calib/BadChannels", metadata, getTSMS(timestamp)); + auto badChannelsCCDB = *ccdbApi.retrieveFromTFileAny>("MID/Calib/BadChannels", metadata, getTSMS(md.start)); rl.rejectList = getRejectList(badChannels, badChannelsCCDB); if (rl.rejectList.empty()) { - std::cout << "Warning: reject list was empty. It probably means that an entire board is already masked in calibration for run " << grpecs.getRun() << std::endl; + std::cout << "Warning: reject list was empty. It probably means that an entire board is already masked in calibration for run " << md.runNumber << std::endl; return rl; } @@ -260,21 +296,15 @@ RejectListStruct build_rejectlist(long timestamp, const o2::ccdb::CcdbApi& qcdbA for (auto& col : rl.rejectList) { std::cout << col << std::endl; } - std::cout << "Run number: " << grpecs.getRun() << std::endl; - std::cout << "SOR - EOR: " << timeRangeToString(grpecs.getTimeStart(), grpecs.getTimeEnd()) << std::endl; + std::cout << "Run number: " << md.runNumber << std::endl; std::cout << "SOT - EOT: " << timeRangeToString(runRange.first, runRange.second) << std::endl; std::cout << "Good: " << timeRangeToString(goodTSRange.first, goodTSRange.second) << std::endl; std::cout << "Bad: " << timeRangeToString(badTSRange.first, badTSRange.second) << std::endl; + std::cout << "Fraction bad: " << static_cast(badTSRange.second - badTSRange.first) / static_cast(runRange.second - runRange.first) << std::endl; // Set the start of the reject list to the last timestamp in which the occupancy was ok rl.start = goodTSRange.second; - if (goodTSRange.first == 0) { - // If the quality was bad for the full run, set the start of the reject list to the SOR - std::cout << "CAVEAT: no good TS found. Will use SOT instead" << std::endl; - rl.start = runRange.first; - } - // Set the end of the reject list to the end of run - rl.end = runRange.second; + rl.end = badTSRange.second; return rl; } @@ -301,8 +331,8 @@ RejectListStruct load_from_json(const o2::ccdb::CcdbApi& ccdbApi, const char* fi std::cerr << "Problem parsing " << filename << std::endl; return rl; } - auto startRange = getRunDuration(ccdbApi, doc["startRun"].GetInt()); - auto endRange = getRunDuration(ccdbApi, doc["endRun"].GetInt()); + auto startRange = o2::ccdb::BasicCCDBManager::getRunDuration(ccdbApi, doc["startRun"].GetInt()); + auto endRange = o2::ccdb::BasicCCDBManager::getRunDuration(ccdbApi, doc["endRun"].GetInt()); rl.start = startRange.first; rl.end = endRange.second; std::cout << "Manual RL validity: " << timeRangeToString(rl.start, rl.end) << std::endl; @@ -397,9 +427,9 @@ void build_rejectlist(long start, long end, const char* qcdbUrl = "http://ali-qc o2::ccdb::CcdbApi ccdbApi; ccdbApi.init(ccdbUrl); std::vector rls; - auto objectsTS = findObjectsTSInPeriod(start, end, qcdbApi, sPathQCQuality.c_str()); - for (auto ts : objectsTS) { - auto rl = build_rejectlist(ts, qcdbApi, ccdbApi); + auto objectsMD = findObjectsMDInPeriod(start, end, qcdbApi, sPathQCQuality.c_str()); + for (auto md : objectsMD) { + auto rl = build_rejectlist(md, qcdbApi, ccdbApi); if (rl.start != rl.end) { rls.emplace_back(rl); } From 64077ed05db5c336aa4bd5512411bb79c24ba11a Mon Sep 17 00:00:00 2001 From: pillot Date: Mon, 25 Nov 2024 16:24:40 +0100 Subject: [PATCH 0104/2180] send messages at every TF --- .../DevIO/Digits/digits-sampler-workflow.cxx | 70 ++++++++++--------- 1 file changed, 37 insertions(+), 33 deletions(-) diff --git a/Detectors/MUON/MCH/DevIO/Digits/digits-sampler-workflow.cxx b/Detectors/MUON/MCH/DevIO/Digits/digits-sampler-workflow.cxx index 7f3819f110ba3..0184e1c78c0c6 100644 --- a/Detectors/MUON/MCH/DevIO/Digits/digits-sampler-workflow.cxx +++ b/Detectors/MUON/MCH/DevIO/Digits/digits-sampler-workflow.cxx @@ -27,6 +27,7 @@ #include #include #include +#include using namespace o2::framework; @@ -63,61 +64,64 @@ class DigitSamplerTask : public io::DigitIOBaseTask void outputAndClear(DataAllocator& out) { - printSummary(mDigits, mROFs, "-> to output"); + LOGP(info, "Sending {} rofs with {} digits", mROFs.size(), mDigits.size()); out.snapshot(OutputRef{"rofs"}, mROFs); out.snapshot(OutputRef{"digits"}, mDigits); mDigits.clear(); mROFs.clear(); } - bool shouldEnd() const + bool shouldEnd() { bool maxTFreached = mNofProcessedTFs >= mMaxNofTimeFrames; bool maxROFreached = mNofProcessedROFs >= mMaxNofROFs; - return !mReadIsOk || maxTFreached || maxROFreached; + bool lastTF = mInput.peek() == EOF; + return !mReadIsOk || lastTF || maxTFreached || maxROFreached; } void run(ProcessingContext& pc) { if (shouldEnd()) { - // output remaining data if any - if (mROFs.size() > 0) { - --mTFid; - outputAndClear(pc.outputs()); - } - pc.services().get().endOfStream(); - return; + throw std::invalid_argument("process should have ended already"); } std::vector rofs; std::vector digits; - mReadIsOk = mDigitSampler->read(digits, rofs); - if (!mReadIsOk) { - return; - } + while ((mReadIsOk = mDigitSampler->read(digits, rofs))) { + + // process the current input TF if requested + if (shouldProcess()) { + incNofProcessedTFs(); + mNofProcessedROFs += rofs.size(); + // append rofs to mROFs, but shift the indices by the amount of digits + // we have read so far. + auto offset = mDigits.size(); + std::transform(rofs.begin(), rofs.end(), std::back_inserter(mROFs), + [offset](ROFRecord r) { + r.setDataRef(r.getFirstIdx() + offset, r.getNEntries()); + return r; + }); + mDigits.insert(mDigits.end(), digits.begin(), digits.end()); + printSummary(mDigits, mROFs); + printFull(mDigits, mROFs); + } - if (shouldProcess()) { - incNofProcessedTFs(); - mNofProcessedROFs += rofs.size(); - // append rofs to mROFs, but shift the indices by the amount of digits - // we have read so far. - auto offset = mDigits.size(); - std::transform(rofs.begin(), rofs.end(), std::back_inserter(mROFs), - [offset](ROFRecord r) { - r.setDataRef(r.getFirstIdx() + offset, r.getNEntries()); - return r; - }); - mDigits.insert(mDigits.end(), digits.begin(), digits.end()); - printSummary(mDigits, mROFs); - printFull(mDigits, mROFs); - } + // increment the input TF id for the next one + incTFid(); - // output if we've accumulated enough ROFs - if (mROFs.size() >= mMinNumberOfROFsPerTF) { - outputAndClear(pc.outputs()); + // stop here if we've accumulated enough ROFs or TFs + if (mROFs.size() >= mMinNumberOfROFsPerTF || shouldEnd()) { + break; + } } - incTFid(); + // output whatever has been accumulated, even if empty + outputAndClear(pc.outputs()); + + if (shouldEnd()) { + pc.services().get().endOfStream(); + pc.services().get().readyToQuit(QuitRequest::Me); + } } }; From f5e440b943ae6229b5c6b991172940ab1f0e4897 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Tue, 26 Nov 2024 16:31:55 +0100 Subject: [PATCH 0105/2180] GPU TPC: Fix typo in name, and clean up setting of inputGPU struct --- .../DataFormatsTPC/CompressedClusters.h | 2 +- .../DataCompression/GPUTPCDecompression.cxx | 2 +- .../Global/GPUChainTrackingCompression.cxx | 34 ++----------------- 3 files changed, 4 insertions(+), 34 deletions(-) diff --git a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CompressedClusters.h b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CompressedClusters.h index 9f49884035b7e..46da2da2a702e 100644 --- a/DataFormats/Detectors/TPC/include/DataFormatsTPC/CompressedClusters.h +++ b/DataFormats/Detectors/TPC/include/DataFormatsTPC/CompressedClusters.h @@ -29,7 +29,7 @@ struct CompressedClustersCounters { unsigned int nUnattachedClusters = 0; unsigned int nAttachedClustersReduced = 0; unsigned int nSliceRows = 36 * 152; - unsigned char nComppressionModes = 0; + unsigned char nComppressionModes = 0; // Don't fix this name due to ROOT dictionaries! float solenoidBz = -1e6f; int maxTimeBin = -1e6; diff --git a/GPU/GPUTracking/DataCompression/GPUTPCDecompression.cxx b/GPU/GPUTracking/DataCompression/GPUTPCDecompression.cxx index 4039ebb0c100d..0f7acfce86094 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCDecompression.cxx +++ b/GPU/GPUTracking/DataCompression/GPUTPCDecompression.cxx @@ -43,7 +43,7 @@ void GPUTPCDecompression::SetPointersCompressedClusters(void*& mem, T& c, uint32 uint32_t nClAreduced = reducedClA ? nClA - nTr : nClA; - if (!(mRec->GetParam().rec.tpc.compressionTypeMask & GPUSettings::CompressionTrackModel)) { + if (!(c.nComppressionModes & GPUSettings::CompressionTrackModel)) { return; // Track model disabled, do not allocate memory } computePointerWithAlignment(mem, c.qTotA, nClA); diff --git a/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx b/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx index 98109447de034..b11b7d3b11cab 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx @@ -254,40 +254,10 @@ int32_t GPUChainTracking::RunTPCDecompression() int32_t inputStream = 0; int32_t unattachedStream = mRec->NStreams() - 1; - inputGPU.nAttachedClusters = cmprClsHost.nAttachedClusters; - inputGPU.nUnattachedClusters = cmprClsHost.nUnattachedClusters; - inputGPU.nTracks = cmprClsHost.nTracks; - inputGPU.nAttachedClustersReduced = inputGPU.nAttachedClusters - inputGPU.nTracks; - inputGPU.nSliceRows = NSLICES * GPUCA_ROW_COUNT; - inputGPU.nComppressionModes = param().rec.tpc.compressionTypeMask; - inputGPU.solenoidBz = param().bzkG; - inputGPU.maxTimeBin = param().continuousMaxTimeBin; + inputGPU = cmprClsHost; SetupGPUProcessor(&Decompressor, true); WriteToConstantMemory(myStep, (char*)&processors()->tpcDecompressor - (char*)processors(), &DecompressorShadow, sizeof(DecompressorShadow), inputStream); - - inputGPU.nTrackClusters = cmprClsHost.nTrackClusters; - inputGPU.qTotU = cmprClsHost.qTotU; - inputGPU.qMaxU = cmprClsHost.qMaxU; - inputGPU.flagsU = cmprClsHost.flagsU; - inputGPU.padDiffU = cmprClsHost.padDiffU; - inputGPU.timeDiffU = cmprClsHost.timeDiffU; - inputGPU.sigmaPadU = cmprClsHost.sigmaPadU; - inputGPU.sigmaTimeU = cmprClsHost.sigmaTimeU; - inputGPU.nSliceRowClusters = cmprClsHost.nSliceRowClusters; - inputGPU.qTotA = cmprClsHost.qTotA; - inputGPU.qMaxA = cmprClsHost.qMaxA; - inputGPU.flagsA = cmprClsHost.flagsA; - inputGPU.rowDiffA = cmprClsHost.rowDiffA; - inputGPU.sliceLegDiffA = cmprClsHost.sliceLegDiffA; - inputGPU.padResA = cmprClsHost.padResA; - inputGPU.timeResA = cmprClsHost.timeResA; - inputGPU.sigmaPadA = cmprClsHost.sigmaPadA; - inputGPU.sigmaTimeA = cmprClsHost.sigmaTimeA; - inputGPU.qPtA = cmprClsHost.qPtA; - inputGPU.rowA = cmprClsHost.rowA; - inputGPU.sliceA = cmprClsHost.sliceA; - inputGPU.timeA = cmprClsHost.timeA; - inputGPU.padA = cmprClsHost.padA; + inputGPU = cmprClsHost; bool toGPU = true; runKernel({GetGridAutoStep(inputStream, RecoStep::TPCDecompression), krnlRunRangeNone, &mEvents->init}, DecompressorShadow.mNativeClustersIndex, NSLICES * GPUCA_ROW_COUNT * sizeof(DecompressorShadow.mNativeClustersIndex[0])); From 2af1b957f8c0edd472013ec9bc9fbb08ba40a926 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Sun, 24 Nov 2024 18:22:36 +0100 Subject: [PATCH 0106/2180] GPUWorkflow: clean up calib objects a bit --- GPU/Workflow/src/GPUWorkflowSpec.cxx | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/GPU/Workflow/src/GPUWorkflowSpec.cxx b/GPU/Workflow/src/GPUWorkflowSpec.cxx index 94b1c3c2b8a7b..37ae734845667 100644 --- a/GPU/Workflow/src/GPUWorkflowSpec.cxx +++ b/GPU/Workflow/src/GPUWorkflowSpec.cxx @@ -1095,8 +1095,12 @@ Inputs GPURecoWorkflowSpec::inputs() } else if (mSpecConfig.enableDoublePipeline == 1) { inputs.emplace_back("pipelineprepare", gDataOriginGPU, "PIPELINEPREPARE", 0, Lifetime::Timeframe); } + if (mSpecConfig.outputTracks || mSpecConfig.caClusterer) { + // calibration objects for TPC clusterization + inputs.emplace_back("tpcgain", gDataOriginTPC, "PADGAINFULL", 0, Lifetime::Condition, ccdbParamSpec(o2::tpc::CDBTypeMap.at(o2::tpc::CDBType::CalPadGainFull))); + } if (mSpecConfig.outputTracks) { - // loading calibration objects from the CCDB + // calibration objects for TPC tracking const auto mapSources = mSpecConfig.tpcDeadMapSources; if (mapSources != 0) { tpc::SourcesDeadMap sources((mapSources > -1) ? static_cast(mapSources) : tpc::SourcesDeadMap::All); @@ -1107,7 +1111,7 @@ Inputs GPURecoWorkflowSpec::inputs() inputs.emplace_back("tpcruninfo", gDataOriginTPC, "TPCRUNINFO", 0, Lifetime::Condition, ccdbParamSpec(o2::tpc::CDBTypeMap.at(o2::tpc::CDBType::ConfigRunInfo))); } } - inputs.emplace_back("tpcgain", gDataOriginTPC, "PADGAINFULL", 0, Lifetime::Condition, ccdbParamSpec(o2::tpc::CDBTypeMap.at(o2::tpc::CDBType::CalPadGainFull))); + inputs.emplace_back("tpcgainresidual", gDataOriginTPC, "PADGAINRESIDUAL", 0, Lifetime::Condition, ccdbParamSpec(o2::tpc::CDBTypeMap.at(o2::tpc::CDBType::CalPadGainResidual), {}, 1)); // time-dependent if (mSpecConfig.tpcUseMCTimeGain) { inputs.emplace_back("tpctimegain", gDataOriginTPC, "TIMEGAIN", 0, Lifetime::Condition, ccdbParamSpec(o2::tpc::CDBTypeMap.at(o2::tpc::CDBType::CalTimeGainMC), {}, 1)); // time-dependent @@ -1124,11 +1128,6 @@ Inputs GPURecoWorkflowSpec::inputs() if (mSpecConfig.decompressTPC) { inputs.emplace_back(InputSpec{"input", ConcreteDataTypeMatcher{gDataOriginTPC, mSpecConfig.decompressTPCFromROOT ? o2::header::DataDescription("COMPCLUSTERS") : o2::header::DataDescription("COMPCLUSTERSFLAT")}, Lifetime::Timeframe}); } else if (mSpecConfig.caClusterer) { - // if the output type are tracks, then the input spec for the gain map is already defined - if (!mSpecConfig.outputTracks) { - inputs.emplace_back("tpcgain", gDataOriginTPC, "PADGAINFULL", 0, Lifetime::Condition, ccdbParamSpec(o2::tpc::CDBTypeMap.at(o2::tpc::CDBType::CalPadGainFull))); - } - // We accept digits and MC labels also if we run on ZS Raw data, since they are needed for MC label propagation if ((!mSpecConfig.zsOnTheFly || mSpecConfig.processMC) && !mSpecConfig.zsDecoder) { inputs.emplace_back(InputSpec{"input", ConcreteDataTypeMatcher{gDataOriginTPC, "DIGITS"}, Lifetime::Timeframe}); From 2d89de5886811829d04a563f7f6c495546125c79 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Mon, 25 Nov 2024 09:01:34 +0100 Subject: [PATCH 0107/2180] GPU: Add dynamic configuration object to GPUReconstruction --- GPU/GPUTracking/Base/GPUParam.cxx | 5 ++++- GPU/GPUTracking/Base/GPUParam.h | 2 +- GPU/GPUTracking/Base/GPUReconstruction.cxx | 16 ++++++++++++---- GPU/GPUTracking/Base/GPUReconstruction.h | 3 ++- GPU/GPUTracking/Base/GPUReconstructionCPU.cxx | 2 +- GPU/GPUTracking/Definitions/GPUSettingsList.h | 6 ++++++ .../GPUTrackingLinkDef_O2_DataTypes.h | 1 + 7 files changed, 27 insertions(+), 8 deletions(-) diff --git a/GPU/GPUTracking/Base/GPUParam.cxx b/GPU/GPUTracking/Base/GPUParam.cxx index 42d4f61f77116..a74ba87794ed4 100644 --- a/GPU/GPUTracking/Base/GPUParam.cxx +++ b/GPU/GPUTracking/Base/GPUParam.cxx @@ -124,7 +124,7 @@ void GPUParam::SetDefaults(float solenoidBz) par.earlyTpcTransform = false; } -void GPUParam::UpdateSettings(const GPUSettingsGRP* g, const GPUSettingsProcessing* p, const GPURecoStepConfiguration* w) +void GPUParam::UpdateSettings(const GPUSettingsGRP* g, const GPUSettingsProcessing* p, const GPURecoStepConfiguration* w, const GPUSettingsRecDynamic* d) { if (g) { UpdateBzOnly(g->solenoidBzNominalGPU); @@ -145,6 +145,9 @@ void GPUParam::UpdateSettings(const GPUSettingsGRP* g, const GPUSettingsProcessi dodEdxDownscaled = (rand() % 100) < p->tpcDownscaledEdx; } } + if (d) { + rec.dyn = *d; + } } void GPUParam::UpdateBzOnly(float newSolenoidBz) diff --git a/GPU/GPUTracking/Base/GPUParam.h b/GPU/GPUTracking/Base/GPUParam.h index 070ac76f58ffb..fd380c0a39593 100644 --- a/GPU/GPUTracking/Base/GPUParam.h +++ b/GPU/GPUTracking/Base/GPUParam.h @@ -84,7 +84,7 @@ struct GPUParam : public internal::GPUParam_t #ifndef GPUCA_GPUCODE void SetDefaults(float solenoidBz); 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); + void UpdateSettings(const GPUSettingsGRP* g, const GPUSettingsProcessing* p = nullptr, const GPURecoStepConfiguration* w = nullptr, const GPUSettingsRecDynamic* d = nullptr); void UpdateBzOnly(float newSolenoidBz); void LoadClusterErrors(bool Print = 0); void UpdateRun3ClusterErrors(const float* yErrorParam, const float* zErrorParam); diff --git a/GPU/GPUTracking/Base/GPUReconstruction.cxx b/GPU/GPUTracking/Base/GPUReconstruction.cxx index 632bf0f331f31..9abe225c7848e 100644 --- a/GPU/GPUTracking/Base/GPUReconstruction.cxx +++ b/GPU/GPUTracking/Base/GPUReconstruction.cxx @@ -427,7 +427,7 @@ int32_t GPUReconstruction::InitPhaseAfterDevice() (mProcessors[i].proc->*(mProcessors[i].InitializeProcessor))(); } - WriteConstantParams(); // First initialization, if the user doesn't use RunChains + WriteConstantParams(); // Initialize with initial values, can optionally be updated later mInitialized = true; return 0; @@ -1105,7 +1105,12 @@ void GPUReconstruction::DumpSettings(const char* dir) } } -void GPUReconstruction::UpdateSettings(const GPUSettingsGRP* g, const GPUSettingsProcessing* p) +void GPUReconstruction::UpdateDynamicSettings(const GPUSettingsRecDynamic* d) +{ + UpdateSettings(nullptr, nullptr, d); +} + +void GPUReconstruction::UpdateSettings(const GPUSettingsGRP* g, const GPUSettingsProcessing* p, const GPUSettingsRecDynamic* d) { if (g) { mGRPSettings = *g; @@ -1114,8 +1119,11 @@ void GPUReconstruction::UpdateSettings(const GPUSettingsGRP* g, const GPUSetting mProcessingSettings.debugLevel = p->debugLevel; mProcessingSettings.resetTimers = p->resetTimers; } - GPURecoStepConfiguration w = mRecoSteps; - param().UpdateSettings(g, p, &w); + GPURecoStepConfiguration* w = nullptr; + if (mRecoSteps.steps.isSet(GPUDataTypes::RecoStep::TPCdEdx)) { + w = &mRecoSteps; + } + param().UpdateSettings(g, p, w, d); if (mInitialized) { WriteConstantParams(); } diff --git a/GPU/GPUTracking/Base/GPUReconstruction.h b/GPU/GPUTracking/Base/GPUReconstruction.h index 70a066532d938..efad0b41fd571 100644 --- a/GPU/GPUTracking/Base/GPUReconstruction.h +++ b/GPU/GPUTracking/Base/GPUReconstruction.h @@ -200,7 +200,8 @@ class GPUReconstruction void SetSettings(const GPUSettingsGRP* grp, const GPUSettingsRec* rec = nullptr, const GPUSettingsProcessing* proc = nullptr, const GPURecoStepConfiguration* workflow = nullptr); void SetResetTimers(bool reset) { mProcessingSettings.resetTimers = reset; } // May update also after Init() void SetDebugLevelTmp(int32_t level) { mProcessingSettings.debugLevel = level; } // Temporarily, before calling SetSettings() - void UpdateSettings(const GPUSettingsGRP* g, const GPUSettingsProcessing* p = nullptr); + void UpdateSettings(const GPUSettingsGRP* g, const GPUSettingsProcessing* p = nullptr, const GPUSettingsRecDynamic* d = nullptr); + void UpdateDynamicSettings(const GPUSettingsRecDynamic* d); void SetOutputControl(const GPUOutputControl& v) { mOutputControl = v; } void SetOutputControl(void* ptr, size_t size); void SetInputControl(void* ptr, size_t size); diff --git a/GPU/GPUTracking/Base/GPUReconstructionCPU.cxx b/GPU/GPUTracking/Base/GPUReconstructionCPU.cxx index 537c3cf63a628..271bee59db31b 100644 --- a/GPU/GPUTracking/Base/GPUReconstructionCPU.cxx +++ b/GPU/GPUTracking/Base/GPUReconstructionCPU.cxx @@ -228,7 +228,7 @@ int32_t GPUReconstructionCPU::RunChains() mThreadId = GetThread(); } if (mSlaves.size() || mMaster) { - WriteConstantParams(); // Reinitialize + WriteConstantParams(); // Reinitialize // TODO: Get this in sync with GPUChainTracking::DoQueuedUpdates, and consider the doublePipeline } for (uint32_t i = 0; i < mChains.size(); i++) { int32_t retVal = mChains[i]->RunChain(); diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index d5494d04930f5..974ef6a9f0d18 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -181,6 +181,11 @@ AddOptionRTC(pileupBwdNBC, uint8_t, 80, "", 0, "Pre-trigger Pile-up integration AddHelp("help", 'h') EndConfig() +// Dynamic settings, must NOT use AddOptionRTC(...) !!! +BeginSubConfig(GPUSettingsRecDynamic, dyn, configStandalone.rec, "RECDYN", 0, "Reconstruction settings", rec_dyn) +AddHelp("help", 'h') +EndConfig() + BeginSubConfig(GPUSettingsRec, rec, configStandalone, "REC", 0, "Reconstruction settings", rec) AddOptionRTC(maxTrackQPtB5, float, 1.f / GPUCA_MIN_TRACK_PTB5_DEFAULT, "", 0, "required max Q/Pt (==min Pt) of tracks") AddOptionRTC(nonConsecutiveIDs, int8_t, false, "", 0, "Non-consecutive cluster IDs as in HLT, disables features that need access to slice data in TPC merger") @@ -193,6 +198,7 @@ AddOptionRTC(trackingRefitGPUModel, int8_t, 1, "", 0, "Use GPU track model for t AddCustomCPP(void SetMinTrackPtB5(float v) { maxTrackQPtB5 = v > 0.001f ? (1.f / v) : (1.f / 0.001f); }) AddSubConfig(GPUSettingsRecTPC, tpc) AddSubConfig(GPUSettingsRecTRD, trd) +AddSubConfig(GPUSettingsRecDynamic, dyn) AddHelp("help", 'h') EndConfig() diff --git a/GPU/GPUTracking/GPUTrackingLinkDef_O2_DataTypes.h b/GPU/GPUTracking/GPUTrackingLinkDef_O2_DataTypes.h index 16f9b769123f7..6ed4e036c6597 100644 --- a/GPU/GPUTracking/GPUTrackingLinkDef_O2_DataTypes.h +++ b/GPU/GPUTracking/GPUTrackingLinkDef_O2_DataTypes.h @@ -28,6 +28,7 @@ #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 + ; From 07d0819f5a5a467c397fa2f6e0df950076d9869d Mon Sep 17 00:00:00 2001 From: David Rohr Date: Mon, 25 Nov 2024 09:02:29 +0100 Subject: [PATCH 0108/2180] GPU TPC: Add option to apply timebin cut to CTF cluster decoding CPU-only version --- GPU/GPUTracking/Base/GPUParam.cxx | 2 ++ GPU/GPUTracking/Base/GPUParam.h | 1 + GPU/GPUTracking/DataTypes/GPUNewCalibValues.cxx | 3 +++ GPU/GPUTracking/DataTypes/GPUNewCalibValues.h | 2 ++ GPU/GPUTracking/DataTypes/GPUSettings.h | 1 + GPU/GPUTracking/Global/GPUChainTracking.cxx | 3 +++ GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx | 1 + GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx | 7 ++++--- .../Global/GPUChainTrackingDebugAndProfiling.cxx | 3 +++ GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h | 1 + GPU/Workflow/src/GPUWorkflowSpec.cxx | 9 +++++++-- GPU/Workflow/src/GPUWorkflowTPC.cxx | 9 +++++---- 12 files changed, 33 insertions(+), 9 deletions(-) diff --git a/GPU/GPUTracking/Base/GPUParam.cxx b/GPU/GPUTracking/Base/GPUParam.cxx index a74ba87794ed4..661ae830ca6f3 100644 --- a/GPU/GPUTracking/Base/GPUParam.cxx +++ b/GPU/GPUTracking/Base/GPUParam.cxx @@ -120,6 +120,7 @@ void GPUParam::SetDefaults(float solenoidBz) par.toyMCEventsFlag = false; par.continuousTracking = false; continuousMaxTimeBin = 0; + tpcCutTimeBin = 0; par.debugLevel = 0; par.earlyTpcTransform = false; } @@ -132,6 +133,7 @@ void GPUParam::UpdateSettings(const GPUSettingsGRP* g, const GPUSettingsProcessi par.toyMCEventsFlag = g->homemadeEvents; par.continuousTracking = g->grpContinuousMaxTimeBin != 0; continuousMaxTimeBin = g->grpContinuousMaxTimeBin == -1 ? GPUSettings::TPC_MAX_TF_TIME_BIN : g->grpContinuousMaxTimeBin; + tpcCutTimeBin = g->tpcCutTimeBin; } par.earlyTpcTransform = rec.tpc.forceEarlyTransform == -1 ? (!par.continuousTracking) : rec.tpc.forceEarlyTransform; qptB5Scaler = CAMath::Abs(bzkG) > 0.1f ? CAMath::Abs(bzkG) / 5.006680f : 1.f; // Repeat here, since passing in g is optional diff --git a/GPU/GPUTracking/Base/GPUParam.h b/GPU/GPUTracking/Base/GPUParam.h index fd380c0a39593..ce9ac30b7c35b 100644 --- a/GPU/GPUTracking/Base/GPUParam.h +++ b/GPU/GPUTracking/Base/GPUParam.h @@ -59,6 +59,7 @@ struct GPUParam_t { int8_t dodEdxDownscaled; int32_t continuousMaxTimeBin; + int32_t tpcCutTimeBin; GPUTPCGeometry tpcGeometry; // TPC Geometry GPUTPCGMPolynomialField polynomialField; // Polynomial approx. of magnetic field for TPC GM diff --git a/GPU/GPUTracking/DataTypes/GPUNewCalibValues.cxx b/GPU/GPUTracking/DataTypes/GPUNewCalibValues.cxx index f443809d15ef5..e86955d6da500 100644 --- a/GPU/GPUTracking/DataTypes/GPUNewCalibValues.cxx +++ b/GPU/GPUTracking/DataTypes/GPUNewCalibValues.cxx @@ -24,4 +24,7 @@ void GPUNewCalibValues::updateFrom(const GPUNewCalibValues* from) if (from->newContinuousMaxTimeBin) { continuousMaxTimeBin = from->continuousMaxTimeBin; } + if (from->newTPCTimeBinCut) { + tpcTimeBinCut = from->tpcTimeBinCut; + } } diff --git a/GPU/GPUTracking/DataTypes/GPUNewCalibValues.h b/GPU/GPUTracking/DataTypes/GPUNewCalibValues.h index 802306e996553..5d5a31785928c 100644 --- a/GPU/GPUTracking/DataTypes/GPUNewCalibValues.h +++ b/GPU/GPUTracking/DataTypes/GPUNewCalibValues.h @@ -25,8 +25,10 @@ namespace gpu struct GPUNewCalibValues { bool newSolenoidField = false; bool newContinuousMaxTimeBin = false; + bool newTPCTimeBinCut = false; float solenoidField = 0.f; uint32_t continuousMaxTimeBin = 0; + int32_t tpcTimeBinCut = 0; void updateFrom(const GPUNewCalibValues* from); }; diff --git a/GPU/GPUTracking/DataTypes/GPUSettings.h b/GPU/GPUTracking/DataTypes/GPUSettings.h index 69bfb15e3f4b0..b967a7ce42620 100644 --- a/GPU/GPUTracking/DataTypes/GPUSettings.h +++ b/GPU/GPUTracking/DataTypes/GPUSettings.h @@ -60,6 +60,7 @@ struct GPUSettingsGRP { int32_t grpContinuousMaxTimeBin = -2; // 0 for triggered events, -1 for automatic setting, -2 invalid default int32_t needsClusterer = 0; // Set to true if the data requires the clusterizer int32_t doCompClusterDecode = 0; // Set to true if the data contains compressed TPC clusters + int32_t tpcCutTimeBin = 0; // Cut TPC clusters and digits >= this cut }; // Parameters of the current time frame diff --git a/GPU/GPUTracking/Global/GPUChainTracking.cxx b/GPU/GPUTracking/Global/GPUChainTracking.cxx index b06d636970da7..7b8e590242fae 100644 --- a/GPU/GPUTracking/Global/GPUChainTracking.cxx +++ b/GPU/GPUTracking/Global/GPUChainTracking.cxx @@ -641,6 +641,9 @@ int32_t GPUChainTracking::DoQueuedUpdates(int32_t stream, bool updateSlave) if (mNewCalibValues->newContinuousMaxTimeBin) { grp->grpContinuousMaxTimeBin = mNewCalibValues->continuousMaxTimeBin; } + if (mNewCalibValues->newTPCTimeBinCut) { + grp->tpcCutTimeBin = mNewCalibValues->tpcTimeBinCut; + } } } if (GetProcessingSettings().tpcDownscaledEdx != 0) { diff --git a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx index 97870d74ca624..35e5524732b97 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx @@ -18,6 +18,7 @@ #include "GPUO2DataTypes.h" #include "GPUMemorySizeScalers.h" #include "GPUTrackingInputProvider.h" +#include "GPUNewCalibValues.h" #include #ifdef GPUCA_O2_LIB diff --git a/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx b/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx index b11b7d3b11cab..8ca3a83e780fb 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx @@ -219,14 +219,15 @@ int32_t GPUChainTracking::RunTPCDecompression() return ((tmpBuffer = std::make_unique(size))).get(); }; auto& decompressTimer = getTimer("TPCDecompression", 0); - auto allocatorUse = GetProcessingSettings().tpcApplyCFCutsAtDecoding ? std::function{allocatorTmp} : std::function{allocatorFinal}; + bool runFiltering = GetProcessingSettings().tpcApplyCFCutsAtDecoding; + auto allocatorUse = runFiltering ? std::function{allocatorTmp} : std::function{allocatorFinal}; decompressTimer.Start(); if (decomp.decompress(mIOPtrs.tpcCompressedClusters, *mClusterNativeAccess, allocatorUse, param(), GetProcessingSettings().deterministicGPUReconstruction)) { GPUError("Error decompressing clusters"); return 1; } - if (GetProcessingSettings().tpcApplyCFCutsAtDecoding) { - RunTPCClusterFilter(mClusterNativeAccess.get(), allocatorFinal, true); + if (runFiltering) { + RunTPCClusterFilter(mClusterNativeAccess.get(), allocatorFinal, GetProcessingSettings().tpcApplyCFCutsAtDecoding); } decompressTimer.Stop(); mIOPtrs.clustersNative = mClusterNativeAccess.get(); diff --git a/GPU/GPUTracking/Global/GPUChainTrackingDebugAndProfiling.cxx b/GPU/GPUTracking/Global/GPUChainTrackingDebugAndProfiling.cxx index f8a64e9d4faaa..7d4a3420995ad 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingDebugAndProfiling.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingDebugAndProfiling.cxx @@ -315,6 +315,9 @@ void GPUChainTracking::RunTPCClusterFilter(o2::tpc::ClusterNativeAccess* cluster keep = keep && cl.qTot > param().rec.tpc.cfQTotCutoff && cl.qMax > param().rec.tpc.cfQMaxCutoff; keep = keep && (!(cl.getFlags() & o2::tpc::ClusterNative::flagSingle) || ((cl.sigmaPadPacked || cl.qMax > param().rec.tpc.cfQMaxCutoffSinglePad) && (cl.sigmaTimePacked || cl.qMax > param().rec.tpc.cfQMaxCutoffSingleTime))); } + if (param().tpcCutTimeBin > 0) { + keep = keep && cl.getTime() < param().tpcCutTimeBin; + } keep = keep && (!GetProcessingSettings().tpcApplyDebugClusterFilter || clusterFilter.filter(iSector, iRow, cl)); if (iPhase && keep) { outputBuffer[countTotal] = cl; diff --git a/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h b/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h index b218a21306a34..eda3b28c6cff6 100644 --- a/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h +++ b/GPU/Workflow/include/GPUWorkflow/GPUWorkflowSpec.h @@ -233,6 +233,7 @@ class GPURecoWorkflowSpec : public o2::framework::Task bool mITSGeometryCreated = false; bool mTRDGeometryCreated = false; bool mPropagatorInstanceCreated = false; + int32_t mTPCCutAtTimeBin = -1; }; } // end namespace gpu diff --git a/GPU/Workflow/src/GPUWorkflowSpec.cxx b/GPU/Workflow/src/GPUWorkflowSpec.cxx index 37ae734845667..06942eab476c6 100644 --- a/GPU/Workflow/src/GPUWorkflowSpec.cxx +++ b/GPU/Workflow/src/GPUWorkflowSpec.cxx @@ -1013,9 +1013,8 @@ void GPURecoWorkflowSpec::doCalibUpdates(o2::framework::ProcessingContext& pc, c LOG(info) << "Updating solenoid field " << newCalibValues.solenoidField; } if (mAutoContinuousMaxTimeBin) { - mConfig->configGRP.grpContinuousMaxTimeBin = GPUO2InterfaceUtils::getTpcMaxTimeBinFromNHbf(mTFSettings->nHBFPerTF); newCalibValues.newContinuousMaxTimeBin = true; - newCalibValues.continuousMaxTimeBin = mConfig->configGRP.grpContinuousMaxTimeBin; + newCalibValues.continuousMaxTimeBin = mConfig->configGRP.grpContinuousMaxTimeBin = GPUO2InterfaceUtils::getTpcMaxTimeBinFromNHbf(mTFSettings->nHBFPerTF); LOG(info) << "Updating max time bin " << newCalibValues.continuousMaxTimeBin << " (" << mTFSettings->nHBFPerTF << " orbits)"; } @@ -1050,6 +1049,11 @@ void GPURecoWorkflowSpec::doCalibUpdates(o2::framework::ProcessingContext& pc, c if (mSpecConfig.runITSTracking) { needCalibUpdate = fetchCalibsCCDBITS(pc) || needCalibUpdate; } + if (mTPCCutAtTimeBin != mConfig->configGRP.tpcCutTimeBin) { + newCalibValues.newTPCTimeBinCut = true; + newCalibValues.tpcTimeBinCut = mConfig->configGRP.tpcCutTimeBin = mTPCCutAtTimeBin; + needCalibUpdate = true; + } if (needCalibUpdate) { LOG(info) << "Updating GPUReconstruction calibration objects"; mGPUReco->UpdateCalibration(newCalibObjects, newCalibValues); @@ -1098,6 +1102,7 @@ Inputs GPURecoWorkflowSpec::inputs() if (mSpecConfig.outputTracks || mSpecConfig.caClusterer) { // calibration objects for TPC clusterization inputs.emplace_back("tpcgain", gDataOriginTPC, "PADGAINFULL", 0, Lifetime::Condition, ccdbParamSpec(o2::tpc::CDBTypeMap.at(o2::tpc::CDBType::CalPadGainFull))); + inputs.emplace_back("tpcaltrosync", gDataOriginTPC, "ALTROSYNCSIGNAL", 0, Lifetime::Condition, ccdbParamSpec(o2::tpc::CDBTypeMap.at(o2::tpc::CDBType::AltroSyncSignal))); } if (mSpecConfig.outputTracks) { // calibration objects for TPC tracking diff --git a/GPU/Workflow/src/GPUWorkflowTPC.cxx b/GPU/Workflow/src/GPUWorkflowTPC.cxx index 97bf3aed26368..b64c25b63cc54 100644 --- a/GPU/Workflow/src/GPUWorkflowTPC.cxx +++ b/GPU/Workflow/src/GPUWorkflowTPC.cxx @@ -65,10 +65,7 @@ #include "SimulationDataFormat/MCCompLabel.h" #include "Algorithm/Parser.h" #include "DataFormatsGlobalTracking/RecoContainer.h" -#include "DataFormatsTRD/RecoInputContainer.h" -#include "TRDBase/Geometry.h" -#include "TRDBase/GeometryFlat.h" -#include "ITSBase/GeometryTGeo.h" +#include "DataFormatsTPC/AltroSyncSignal.h" #include "CommonUtils/VerbosityConfig.h" #include "CommonUtils/DebugStreamer.h" #include @@ -308,6 +305,10 @@ bool GPURecoWorkflowSpec::fetchCalibsCCDBTPC(ProcessingCon pc.inputs().get*>("tpcgain"); } + if (mSpecConfig.outputTracks || mSpecConfig.caClusterer) { + mTPCCutAtTimeBin = pc.inputs().get("tpcaltrosync")->getTB2Cut(pc.services().get().tfCounter); + } + // these calibrations are only defined for the tracking if (mSpecConfig.outputTracks) { // update the calibration objects in case they changed in the CCDB From 9423b5709f649cfd5fdaac1422981834cafb035f Mon Sep 17 00:00:00 2001 From: David Rohr Date: Mon, 25 Nov 2024 13:21:58 +0100 Subject: [PATCH 0109/2180] GPU TPC: Provide time bin cut also to Clusterizer (not yet used) --- GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx | 2 ++ 1 file changed, 2 insertions(+) diff --git a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx index 35e5524732b97..4bc0ee4e91ff1 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingClusterizer.cxx @@ -574,6 +574,7 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) return ForwardTPCDigits(); } #ifdef GPUCA_TPC_GEOMETRY_O2 + int32_t tpcTimeBinCut = mUpdateNewCalibObjects && mNewCalibValues->newTPCTimeBinCut ? mNewCalibValues->tpcTimeBinCut : param().tpcCutTimeBin; mRec->PushNonPersistentMemory(qStr2Tag("TPCCLUST")); const auto& threadContext = GetThreadContext(); const bool doGPU = GetRecoStepsGPU() & RecoStep::TPCClusterFinding; @@ -766,6 +767,7 @@ int32_t GPUChainTracking::RunTPCClusterizer(bool synchronizeOutput) : 0; uint32_t nBlocks = doGPU ? clusterer.mPmemory->counters.nPagesSubslice : GPUTrackingInOutZS::NENDPOINTS; + (void)tpcTimeBinCut; // TODO: To be used in decoding kernels switch (mCFContext->zsVersion) { default: GPUFatal("Data with invalid TPC ZS mode (%d) received", mCFContext->zsVersion); From 91f1ac35acd5dabfb7c39b3cdbe5e2145053b993 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Tue, 26 Nov 2024 16:31:29 +0100 Subject: [PATCH 0110/2180] GPU TPC: Add some TODO comments --- GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx b/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx index d817278404534..74cc12e9bbd9a 100644 --- a/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx +++ b/GPU/GPUTracking/Merger/GPUTPCGMTrackParam.cxx @@ -370,7 +370,7 @@ GPUd() bool GPUTPCGMTrackParam::Fit(GPUTPCGMMerger* GPUrestrict() merger, int32_ CADEBUG(printf("Reinit linearization\n")); prop.SetTrack(this, prop.GetAlpha()); } - if (param.par.dodEdx && param.dodEdxDownscaled && iWay == nWays - 1 && cluster.leg == clusters[maxN - 1].leg && !(clusterState & GPUTPCGMMergedTrackHit::flagEdge)) { + if (param.par.dodEdx && param.dodEdxDownscaled && iWay == nWays - 1 && cluster.leg == clusters[maxN - 1].leg && !(clusterState & GPUTPCGMMergedTrackHit::flagEdge)) { // TODO: Costimize flag to remove, and option to remove double-clusters float qtot = 0, qmax = 0, pad = 0, relTime = 0; const int32_t clusterCount = (ihit - ihitMergeFirst) * wayDirection + 1; for (int32_t iTmp = ihitMergeFirst; iTmp != ihit + wayDirection; iTmp += wayDirection) { @@ -384,7 +384,7 @@ GPUd() bool GPUTPCGMTrackParam::Fit(GPUTPCGMMerger* GPUrestrict() merger, int32_ relTime += cl.getTime(); } } - qtot /= clusterCount; + qtot /= clusterCount; // TODO: Weighted Average pad /= clusterCount; relTime /= clusterCount; relTime = relTime - CAMath::Round(relTime); @@ -528,7 +528,7 @@ GPUd() int32_t GPUTPCGMTrackParam::MergeDoubleRowClusters(int32_t& ihit, int32_t } } else { CADEBUG(printf("\t\tMerging hit row %d X %f Y %f Z %f (dy %f, dz %f, chiY %f, chiZ %f)\n", clusters[ihit].row, clx, cly, clz, dy, dz, sqrtf(maxDistY), sqrtf(maxDistZ))); - xx += clx * clamp; + xx += clx * clamp; // TODO: Weight in pad/time instead of XYZ yy += cly * clamp; zz += clz * clamp; clusterState |= clusters[ihit].state; From f76f1a70c5664e4f8ed9219a067aaee0a2258378 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 27 Nov 2024 09:11:31 +0100 Subject: [PATCH 0111/2180] GPU TPC: Make TPC Time Bin Cut overrideable from configKeyValue --- GPU/GPUTracking/Definitions/GPUSettingsList.h | 1 + GPU/Workflow/src/GPUWorkflowTPC.cxx | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/GPU/GPUTracking/Definitions/GPUSettingsList.h b/GPU/GPUTracking/Definitions/GPUSettingsList.h index 974ef6a9f0d18..07cd320140909 100644 --- a/GPU/GPUTracking/Definitions/GPUSettingsList.h +++ b/GPU/GPUTracking/Definitions/GPUSettingsList.h @@ -536,6 +536,7 @@ AddOption(solenoidBzNominalGPU, float, -1e6f, "", 0, "Field strength of solenoid AddOption(constBz, bool, false, "", 0, "force constant Bz for tests") AddOption(setMaxTimeBin, int32_t, -2, "", 0, "maximum time bin of continuous data, 0 for triggered events, -1 for automatic continuous mode, -2 for automatic continuous / triggered") AddOption(overrideNHbfPerTF, int32_t, 0, "", 0, "Overrides the number of HBF per TF if != 0") +AddOption(overrideTPCTimeBinCur, int32_t, 0, "", 0, "Overrides TPC time bin cut if > 0") AddOption(deviceType, std::string, "CPU", "", 0, "Device type, CPU | CUDA | HIP | OCL1 | OCL2") AddOption(forceDeviceType, bool, true, "", 0, "force device type, otherwise allows fall-back to CPU") AddOption(synchronousProcessing, bool, false, "", 0, "Apply performance shortcuts for synchronous processing, disable unneeded steps") diff --git a/GPU/Workflow/src/GPUWorkflowTPC.cxx b/GPU/Workflow/src/GPUWorkflowTPC.cxx index b64c25b63cc54..f895587b8b020 100644 --- a/GPU/Workflow/src/GPUWorkflowTPC.cxx +++ b/GPU/Workflow/src/GPUWorkflowTPC.cxx @@ -306,7 +306,7 @@ bool GPURecoWorkflowSpec::fetchCalibsCCDBTPC(ProcessingCon } if (mSpecConfig.outputTracks || mSpecConfig.caClusterer) { - mTPCCutAtTimeBin = pc.inputs().get("tpcaltrosync")->getTB2Cut(pc.services().get().tfCounter); + mTPCCutAtTimeBin = mConfParam->overrideTPCTimeBinCur > 0 ? mConfParam->overrideTPCTimeBinCur : pc.inputs().get("tpcaltrosync")->getTB2Cut(pc.services().get().tfCounter); } // these calibrations are only defined for the tracking From 5d037e637b48b314cd22e6abe958ce93449ab8e6 Mon Sep 17 00:00:00 2001 From: David Rohr Date: Tue, 26 Nov 2024 18:15:30 +0100 Subject: [PATCH 0112/2180] GPU: Should not include std header in GPU device code --- .../include/CommonConstants/MathConstants.h | 3 ++- .../src/TrackParametrizationWithError.cxx | 15 +++++---------- GPU/Common/GPUCommonConstants.h | 2 +- 3 files changed, 8 insertions(+), 12 deletions(-) diff --git a/Common/Constants/include/CommonConstants/MathConstants.h b/Common/Constants/include/CommonConstants/MathConstants.h index 6870b8ddd5712..9ef3b4dba5ae0 100644 --- a/Common/Constants/include/CommonConstants/MathConstants.h +++ b/Common/Constants/include/CommonConstants/MathConstants.h @@ -22,7 +22,8 @@ namespace constants { namespace math { -constexpr float Almost0 = 1.175494351e-38f; +constexpr float Almost0 = 0x1.0p-126f; // smallest non-denormal float +constexpr float Epsilon = 0x0.000002p0f; // smallest float such that 1 != 1 + Epsilon constexpr float Almost1 = 1.f - 1.0e-6f; constexpr float VeryBig = 1.f / Almost0; diff --git a/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx b/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx index 0dd4a4441c0b3..81963adf79938 100644 --- a/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx +++ b/DataFormats/Reconstruction/src/TrackParametrizationWithError.cxx @@ -12,14 +12,9 @@ #include "ReconstructionDataFormats/TrackParametrizationWithError.h" #include "ReconstructionDataFormats/Vertex.h" #include "ReconstructionDataFormats/DCA.h" +#include "CommonConstants/MathConstants.h" #include -#ifndef __OPENCL__ -#include -#else -#include -#endif - #ifndef GPUCA_GPUCODE_DEVICE #include #endif @@ -794,11 +789,11 @@ GPUd() auto TrackParametrizationWithError::getPredictedChi2(const Track // get chi2 wrt other track, which must be defined at the same parameters X,alpha // Supplied non-initialized covToSet matrix is filled by inverse combined matrix for further use - if (gpu::CAMath::Abs(this->getAlpha() - rhs.getAlpha()) > FLT_EPSILON) { + if (gpu::CAMath::Abs(this->getAlpha() - rhs.getAlpha()) > o2::constants::math::Epsilon) { LOG(error) << "The reference Alpha of the tracks differ: " << this->getAlpha() << " : " << rhs.getAlpha(); return 2.f * HugeF; } - if (gpu::CAMath::Abs(this->getX() - rhs.getX()) > FLT_EPSILON) { + if (gpu::CAMath::Abs(this->getX() - rhs.getX()) > o2::constants::math::Epsilon) { LOG(error) << "The reference X of the tracks differ: " << this->getX() << " : " << rhs.getX(); return 2.f * HugeF; } @@ -827,11 +822,11 @@ GPUd() bool TrackParametrizationWithError::update(const TrackParametriz // update track with other track, the inverted combined cov matrix should be supplied // consider skipping this check, since it is usually already done upstream - if (gpu::CAMath::Abs(this->getAlpha() - rhs.getAlpha()) > FLT_EPSILON) { + if (gpu::CAMath::Abs(this->getAlpha() - rhs.getAlpha()) > o2::constants::math::Epsilon) { LOG(error) << "The reference Alpha of the tracks differ: " << this->getAlpha() << " : " << rhs.getAlpha(); return false; } - if (gpu::CAMath::Abs(this->getX() - rhs.getX()) > FLT_EPSILON) { + if (gpu::CAMath::Abs(this->getX() - rhs.getX()) > o2::constants::math::Epsilon) { LOG(error) << "The reference X of the tracks differ: " << this->getX() << " : " << rhs.getX(); return false; } diff --git a/GPU/Common/GPUCommonConstants.h b/GPU/Common/GPUCommonConstants.h index 883f64b7bdd12..f45aa05ed00ca 100644 --- a/GPU/Common/GPUCommonConstants.h +++ b/GPU/Common/GPUCommonConstants.h @@ -20,7 +20,7 @@ #if !defined(__OPENCL1__) namespace GPUCA_NAMESPACE::gpu::gpu_common_constants { -static CONSTEXPR const float kCLight = 0.000299792458f; +static CONSTEXPR const float kCLight = 0.000299792458f; // TODO: Duplicate of MathConstants, fix this when OpenCL1 is removed } #endif From 96e41889866f27476804d9ce6b1d89b0e48ab75b Mon Sep 17 00:00:00 2001 From: David Rohr Date: Wed, 27 Nov 2024 14:08:25 +0100 Subject: [PATCH 0113/2180] GPU TPC: Fix TPC clusterizer qMax cut --- GPU/GPUTracking/TPCClusterFinder/ClusterAccumulator.cxx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/GPU/GPUTracking/TPCClusterFinder/ClusterAccumulator.cxx b/GPU/GPUTracking/TPCClusterFinder/ClusterAccumulator.cxx index 8988126f7a15e..e8176ecb60d78 100644 --- a/GPU/GPUTracking/TPCClusterFinder/ClusterAccumulator.cxx +++ b/GPU/GPUTracking/TPCClusterFinder/ClusterAccumulator.cxx @@ -27,6 +27,10 @@ GPUd() bool ClusterAccumulator::toNative(const ChargePos& pos, Charge q, tpc::Cl if (cn.qTot <= param.rec.tpc.cfQTotCutoff) { return false; } + cn.qMax = q; + if (cn.qMax <= param.rec.tpc.cfQMaxCutoff) { + return false; + } if (mTimeMean < param.rec.tpc.clustersShiftTimebinsClusterizer) { return false; } @@ -48,7 +52,6 @@ GPUd() bool ClusterAccumulator::toNative(const ChargePos& pos, Charge q, tpc::Cl flags |= (wasSplitInPad) ? tpc::ClusterNative::flagSplitPad : 0; flags |= (isSingleCluster) ? tpc::ClusterNative::flagSingle : 0; - cn.qMax = q; cn.setTimeFlags(mTimeMean - param.rec.tpc.clustersShiftTimebinsClusterizer, flags); cn.setPad(mPadMean); cn.setSigmaTime(mTimeSigma); From d8d7b7c2111d5a01bba8761f0ce29bd7a715f95e Mon Sep 17 00:00:00 2001 From: Piotr Konopka Date: Wed, 27 Nov 2024 19:09:53 +0100 Subject: [PATCH 0114/2180] QC-1248 Update QC Flag documentation (#13720) * QC-1248 Update QC Flag documentation Small updates wrt what is ready to use and some clarifications. * rm trailing whitespace --- DataFormats/QualityControl/README.md | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/DataFormats/QualityControl/README.md b/DataFormats/QualityControl/README.md index 486856c983306..33821319b7316 100644 --- a/DataFormats/QualityControl/README.md +++ b/DataFormats/QualityControl/README.md @@ -15,7 +15,7 @@ Data quality is determined through two methods: Both methods utilize the same data format for Flags. During processing (both synchronous and asynchronous), Checks produce Qualities and associate them with Flags. -The Quality Control framework then transmits these Flags to the RCT through a gRPC interface (**not ready yet**, to be done in the scope of QC-978). +The Quality Control framework then transmits these Flags to the RCT through a gRPC interface (more details in QC repository documentation). Detector experts can then review the automatically generated Flags and make any necessary modifications or additions directly in the RCT. ### Quality Control Flag Structure @@ -49,12 +49,13 @@ Each Flag Type has the following attributes: #### Creating and Managing Flag Types * **FlagTypeFactory** ensures a centralized and consistent list of available Flag Types. - New types can only be created through this factory. + New Flags can only be created through this factory. * **[flagTypes.csv](etc/flagTypes.csv)** defines the existing Flag Types, including their ID, name, and "bad quality" determinant, factory method name and a switch to deprecate a flag. The table serves as the source to automatically generate the corresponding methods in FlagTypeFactory. * **Adding new Flag Types:** If a new issue requires a flag not currently defined, propose the addition by contacting the async QC coordinators. They have the authority to add new Flag Types to the RCT. These changes will then be reflected in the [flagTypes.csv](etc/flagTypes.csv) file through a pull request. + Any proposals for new Flag Types should describe the effects on usability of data from analyzer point of view and they should not be detector-specific unless well-argumented. * **Modification of existing Flag Types:** Existing Flag Types should not be modified in terms of their definition. Instead, one may create a new Flag Type and mark the existing one as obsolete in the CSV table. This will add the `[[ deprecated ]]` attribute to the corresponding method. From 74c640eedba0f45814d421920b1aee2f9663c4ea Mon Sep 17 00:00:00 2001 From: Sandro Wenzel Date: Thu, 28 Nov 2024 11:28:39 +0100 Subject: [PATCH 0115/2180] Disable trackref assert This test is currently failing and disrupting the CI in Geant3 check kinematics. Disabling the assert on trackrefs for now. Will investigate the cause offline. Also renaming the variable to avoid shadowing an outer-scope variable. --- run/checkStack.cxx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/run/checkStack.cxx b/run/checkStack.cxx index 4c2b9d5b50075..98f2669c9f97e 100644 --- a/run/checkStack.cxx +++ b/run/checkStack.cxx @@ -142,10 +142,10 @@ int main(int argc, char** argv) bool havereferences = trackrefs->size(); if (havereferences) { for (auto& trackID : trackidsinTPC) { - auto trackrefs = mcreader.getTrackRefs(eventID, trackID); - LOG(debug) << " Track " << trackID << " has " << trackrefs.size() << " TrackRefs"; - assert(trackrefs.size() > 0); - for (auto& ref : trackrefs) { + auto tpc_trackrefs = mcreader.getTrackRefs(eventID, trackID); + LOG(debug) << " Track " << trackID << " has " << tpc_trackrefs.size() << " TrackRefs"; + // assert(tpc_trackrefs.size() > 0); + for (auto& ref : tpc_trackrefs) { assert(ref.getTrackID() == trackID); } } From 66b81d8b6843b997a20b5bd7403e412d1e51f49b Mon Sep 17 00:00:00 2001 From: Roman Lietava Date: Thu, 28 Nov 2024 20:08:39 +0100 Subject: [PATCH 0116/2180] fix: +1 for L1 latency and trigger class check improved (#13682) * fix: +1 for L1 latency * fix: +1 for L1 latency * dev: decoder: checking only trigger class bits which belongs to run * clang * dev: ctp config added also to CTF decoder * clang * fix: getting ctpconfig * clang * fix: removing std:;cout * clang * removing macro's modification to have cleaner PR * dec: two latency vars, rew-decoder accessing ccdb * clang * TriggerParams old variable same name * clang * dev: reading of CCDB offset paraams in rawdatadecoder * clang * dev: config done only if run changed --- .../DataFormatsCTP/TriggerOffsetsParam.h | 3 +- .../include/CTPReconstruction/CTFCoder.h | 12 +++++++- .../CTPReconstruction/RawDataDecoder.h | 6 +++- .../CTP/reconstruction/src/RawDataDecoder.cxx | 25 +++++++++++++---- .../include/CTPWorkflow/EntropyDecoderSpec.h | 1 + .../include/CTPWorkflow/RawDecoderSpec.h | 3 ++ .../CTP/workflow/src/EntropyDecoderSpec.cxx | 19 +++++++++++-- Detectors/CTP/workflow/src/RawDecoderSpec.cxx | 28 ++++++++++++++++--- 8 files changed, 82 insertions(+), 15 deletions(-) diff --git a/DataFormats/Detectors/CTP/include/DataFormatsCTP/TriggerOffsetsParam.h b/DataFormats/Detectors/CTP/include/DataFormatsCTP/TriggerOffsetsParam.h index f931e9eaa8360..063336e5461ce 100644 --- a/DataFormats/Detectors/CTP/include/DataFormatsCTP/TriggerOffsetsParam.h +++ b/DataFormats/Detectors/CTP/include/DataFormatsCTP/TriggerOffsetsParam.h @@ -24,9 +24,10 @@ namespace ctp struct TriggerOffsetsParam : public o2::conf::ConfigurableParamHelper { static constexpr int MaxNDet = 32; // take with margin to account for possible changes / upgrades int64_t LM_L0 = 15; - int64_t L0_L1 = 280; + int64_t L0_L1 = 281; // trigger input latency int64_t globalInputsShift = 0; // Global shift of inps; customOffset[CTP] is global shift of classes int64_t customOffset[MaxNDet] = {}; + int64_t L0_L1_classes = 280; // trigger input latency O2ParamDef(TriggerOffsetsParam, "TriggerOffsetsParam"); // boilerplate stuff + make principal key }; } // namespace ctp diff --git a/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h b/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h index 6ffb3575207e5..9189df5d12685 100644 --- a/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h +++ b/Detectors/CTP/reconstruction/include/CTPReconstruction/CTFCoder.h @@ -25,6 +25,7 @@ #include "DetectorsBase/CTFCoderBase.h" #include "CTPReconstruction/CTFHelper.h" #include "CTPReconstruction/RawDataDecoder.h" +#include "DataFormatsCTP/Configuration.h" class TTree; @@ -53,6 +54,9 @@ class CTFCoder : public o2::ctf::CTFCoderBase void createCoders(const std::vector& bufVec, o2::ctf::CTFCoderBase::OpType op) final; void setDecodeInps(bool decodeinps) { mDecodeInps = decodeinps; } + void setCTPConfig(CTPConfiguration cfg) { mCTPConfig = std::move(cfg); } + bool getDecodeInps() { return mDecodeInps; } + CTPConfiguration& getCTPConfig() { return mCTPConfig; } bool canApplyBCShiftInputs(const o2::InteractionRecord& ir) const { return canApplyBCShift(ir, mBCShiftInputs); } private: @@ -62,6 +66,7 @@ class CTFCoder : public o2::ctf::CTFCoderBase void appendToTree(TTree& tree, CTF& ec); void readFromTree(TTree& tree, int entry, std::vector& data, LumiInfo& lumi); std::vector mDataFilt; + CTPConfiguration mCTPConfig; int mBCShiftInputs = 0; bool mDecodeInps = false; }; @@ -215,8 +220,13 @@ o2::ctf::CTFIOSize CTFCoder::decode(const CTF::base& ec, VTRG& data, LumiInfo& l } } if (mDecodeInps) { + uint64_t trgclassmask = 0xffffffffffffffff; + if (mCTPConfig.getRunNumber() != 0) { + trgclassmask = mCTPConfig.getTriggerClassMask(); + } + // std::cout << "trgclassmask:" << std::hex << trgclassmask << std::dec << std::endl; o2::pmr::vector digits; - o2::ctp::RawDataDecoder::shiftInputs(digitsMap, digits, mFirstTFOrbit); + o2::ctp::RawDataDecoder::shiftInputs(digitsMap, digits, mFirstTFOrbit, trgclassmask); for (auto const& dig : digits) { data.emplace_back(dig); } diff --git a/Detectors/CTP/reconstruction/include/CTPReconstruction/RawDataDecoder.h b/Detectors/CTP/reconstruction/include/CTPReconstruction/RawDataDecoder.h index c50079f9f8717..16a8ec6a6bef1 100644 --- a/Detectors/CTP/reconstruction/include/CTPReconstruction/RawDataDecoder.h +++ b/Detectors/CTP/reconstruction/include/CTPReconstruction/RawDataDecoder.h @@ -22,6 +22,7 @@ #include "Framework/InputRecord.h" #include "DataFormatsCTP/Digits.h" #include "DataFormatsCTP/LumiInfo.h" +#include "DataFormatsCTP/Configuration.h" namespace o2 { @@ -43,14 +44,16 @@ class RawDataDecoder void setVerbose(bool v) { mVerbose = v; } void setMAXErrors(int m) { mErrorMax = m; } int setLumiInp(int lumiinp, std::string inp); + void setCTPConfig(CTPConfiguration cfg) { mCTPConfig = std::move(cfg); }; uint32_t getIRRejected() const { return mIRRejected; } uint32_t getTCRRejected() const { return mTCRRejected; } std::vector& getTFOrbits() { return mTFOrbits; } int getErrorIR() { return mErrorIR; } int getErrorTCR() { return mErrorTCR; } + CTPConfiguration& getCTPConfig() { return mCTPConfig; } int init(); static int shiftNew(const o2::InteractionRecord& irin, uint32_t TFOrbit, std::bitset<48>& inpmask, int64_t shift, int level, std::map& digmap); - static int shiftInputs(std::map& digitsMap, o2::pmr::vector& digits, uint32_t TFOrbit); + static int shiftInputs(std::map& digitsMap, o2::pmr::vector& digits, uint32_t TFOrbit, uint64_t trgclassmask = 0xffffffffffffffff); private: static constexpr uint32_t TF_TRIGGERTYPE_MASK = 0x800; @@ -79,6 +82,7 @@ class RawDataDecoder int mErrorTCR = 0; int mErrorMax = 3; bool mStickyError = false; + CTPConfiguration mCTPConfig; }; } // namespace ctp } // namespace o2 diff --git a/Detectors/CTP/reconstruction/src/RawDataDecoder.cxx b/Detectors/CTP/reconstruction/src/RawDataDecoder.cxx index 4e3d480e463cd..74e5b7481163d 100644 --- a/Detectors/CTP/reconstruction/src/RawDataDecoder.cxx +++ b/Detectors/CTP/reconstruction/src/RawDataDecoder.cxx @@ -89,7 +89,7 @@ int RawDataDecoder::addCTPDigit(uint32_t linkCRU, uint32_t orbit, gbtword80_t& d } } else if (linkCRU == o2::ctp::GBTLinkIDClassRec) { int32_t BCShiftCorrection = -o2::ctp::TriggerOffsetsParam::Instance().customOffset[o2::detectors::DetID::CTP]; - int32_t offset = BCShiftCorrection + o2::ctp::TriggerOffsetsParam::Instance().LM_L0 + o2::ctp::TriggerOffsetsParam::Instance().L0_L1 - 1; + int32_t offset = BCShiftCorrection + o2::ctp::TriggerOffsetsParam::Instance().LM_L0 + o2::ctp::TriggerOffsetsParam::Instance().L0_L1_classes - 1; LOG(debug) << "tcr ir ori:" << ir; if ((ir.orbit <= mTFOrbit) && ((int32_t)ir.bc < offset)) { // LOG(warning) << "Loosing tclass:" << ir; @@ -293,7 +293,12 @@ int RawDataDecoder::decodeRaw(o2::framework::InputRecord& inputs, std::vector& digitsMap, o2::pmr::vector& digits, uint32_t TFOrbit) +int RawDataDecoder::shiftInputs(std::map& digitsMap, o2::pmr::vector& digits, uint32_t TFOrbit, uint64_t trgclassmask) { // int nClasswoInp = 0; // counting classes without input which should never happen int nLM = 0; @@ -527,6 +532,7 @@ int RawDataDecoder::shiftInputs(std::map& digit int nL1 = 0; int nTwI = 0; int nTwoI = 0; + int nTwoIlost = 0; std::map digitsMapShifted; auto L0shift = o2::ctp::TriggerOffsetsParam::Instance().LM_L0; auto L1shift = L0shift + o2::ctp::TriggerOffsetsParam::Instance().L0_L1; @@ -594,11 +600,17 @@ int RawDataDecoder::shiftInputs(std::map& digit if ((d.CTPInputMask & L1MASKInputs).count()) { nL1++; } - if (d.CTPClassMask.count()) { + if ((d.CTPClassMask).to_ulong() & trgclassmask) { if (d.CTPInputMask.count()) { nTwI++; } else { - nTwoI++; + if (d.intRecord.bc == (o2::constants::lhc::LHCMaxBunches - L1shift)) { // input can be lost because latency class-l1input = 1 + nTwoIlost++; + } else { + // LOG(error) << d.intRecord << " " << d.CTPClassMask << " " << d.CTPInputMask; + // std::cout << "ERROR:" << std::hex << d.CTPClassMask << " " << d.CTPInputMask << std::dec << std::endl; + nTwoI++; + } } } digits.push_back(dig.second); @@ -606,6 +618,9 @@ int RawDataDecoder::shiftInputs(std::map& digit if (nTwoI) { // Trigger class wo Input LOG(error) << "LM:" << nLM << " L0:" << nL0 << " L1:" << nL1 << " TwI:" << nTwI << " Trigger classes wo input:" << nTwoI; } + if (nTwoIlost) { + LOG(warn) << " Trigger classes wo input from diff latency 1:" << nTwoIlost; + } return 0; } // diff --git a/Detectors/CTP/workflow/include/CTPWorkflow/EntropyDecoderSpec.h b/Detectors/CTP/workflow/include/CTPWorkflow/EntropyDecoderSpec.h index 4596fe12cb31d..eee7abb08d16c 100644 --- a/Detectors/CTP/workflow/include/CTPWorkflow/EntropyDecoderSpec.h +++ b/Detectors/CTP/workflow/include/CTPWorkflow/EntropyDecoderSpec.h @@ -34,6 +34,7 @@ class EntropyDecoderSpec : public o2::framework::Task void init(o2::framework::InitContext& ic) final; void endOfStream(o2::framework::EndOfStreamContext& ec) final; void finaliseCCDB(o2::framework::ConcreteDataMatcher& matcher, void* obj) final; + void updateTimeDependentParams(framework::ProcessingContext& pc); private: o2::ctp::CTFCoder mCTFCoder; diff --git a/Detectors/CTP/workflow/include/CTPWorkflow/RawDecoderSpec.h b/Detectors/CTP/workflow/include/CTPWorkflow/RawDecoderSpec.h index 607491b5cb48a..a5a1a75a0b594 100644 --- a/Detectors/CTP/workflow/include/CTPWorkflow/RawDecoderSpec.h +++ b/Detectors/CTP/workflow/include/CTPWorkflow/RawDecoderSpec.h @@ -16,6 +16,7 @@ #include #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" +#include "Framework/WorkflowSpec.h" #include "DataFormatsCTP/Digits.h" #include "DataFormatsCTP/LumiInfo.h" #include "CTPReconstruction/RawDataDecoder.h" @@ -50,6 +51,7 @@ class RawDecoderSpec : public framework::Task /// Input RawData: {"ROUT", "RAWDATA", 0, Lifetime::Timeframe} /// Output HW errors: {"CTP", "RAWHWERRORS", 0, Lifetime::Timeframe} -later void run(framework::ProcessingContext& ctx) final; + void updateTimeDependentParams(framework::ProcessingContext& pc); protected: private: @@ -68,6 +70,7 @@ class RawDecoderSpec : public framework::Task uint32_t mNTFToIntegrate = 1; uint32_t mNHBIntegratedT = 0; uint32_t mNHBIntegratedV = 0; + bool mDecodeinputs = 0; std::deque mHistoryT; std::deque mHistoryV; RawDataDecoder mDecoder; diff --git a/Detectors/CTP/workflow/src/EntropyDecoderSpec.cxx b/Detectors/CTP/workflow/src/EntropyDecoderSpec.cxx index 8f3da5f439f80..8c2f5d05aa031 100644 --- a/Detectors/CTP/workflow/src/EntropyDecoderSpec.cxx +++ b/Detectors/CTP/workflow/src/EntropyDecoderSpec.cxx @@ -55,9 +55,8 @@ void EntropyDecoderSpec::run(ProcessingContext& pc) mTimer.Start(false); o2::ctf::CTFIOSize iosize; - mCTFCoder.updateTimeDependentParams(pc, true); + updateTimeDependentParams(pc); auto buff = pc.inputs().get>("ctf_CTP"); - auto& digits = pc.outputs().make>(OutputRef{"digits"}); auto& lumi = pc.outputs().make(OutputRef{"CTPLumi"}); @@ -76,6 +75,20 @@ void EntropyDecoderSpec::endOfStream(EndOfStreamContext& ec) LOGF(info, "CTP Entropy Decoding total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); } +void EntropyDecoderSpec::updateTimeDependentParams(framework::ProcessingContext& pc) +{ + mCTFCoder.updateTimeDependentParams(pc, true); + if (pc.services().get().globalRunNumberChanged) { + const auto ctpcfg = pc.inputs().get("ctpconfig"); + if (mCTFCoder.getDecodeInps()) { + const auto ctpcfg = pc.inputs().get("ctpconfig"); + if (ctpcfg != nullptr) { + mCTFCoder.setCTPConfig(*ctpcfg); + LOG(info) << "ctpconfig for run done:" << mCTFCoder.getCTPConfig().getRunNumber(); + } + } + } +} DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) { @@ -88,7 +101,7 @@ DataProcessorSpec getEntropyDecoderSpec(int verbosity, unsigned int sspec) inputs.emplace_back("ctf_CTP", "CTP", "CTFDATA", sspec, Lifetime::Timeframe); inputs.emplace_back("ctfdict_CTP", "CTP", "CTFDICT", 0, Lifetime::Condition, ccdbParamSpec("CTP/Calib/CTFDictionaryTree")); inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/TriggerOffsets")); - + inputs.emplace_back("ctpconfig", "CTP", "CTPCONFIG", 0, Lifetime::Condition, ccdbParamSpec("CTP/Config/Config", 1)); return DataProcessorSpec{ "ctp-entropy-decoder", inputs, diff --git a/Detectors/CTP/workflow/src/RawDecoderSpec.cxx b/Detectors/CTP/workflow/src/RawDecoderSpec.cxx index 415dbe2a1ffe3..81a927b3caee1 100644 --- a/Detectors/CTP/workflow/src/RawDecoderSpec.cxx +++ b/Detectors/CTP/workflow/src/RawDecoderSpec.cxx @@ -13,20 +13,21 @@ #include #include "Framework/InputRecordWalker.h" #include "Framework/DataRefUtils.h" -#include "Framework/WorkflowSpec.h" #include "Framework/ConfigParamRegistry.h" #include "DetectorsRaw/RDHUtils.h" #include "CTPWorkflow/RawDecoderSpec.h" #include "CommonUtils/VerbosityConfig.h" #include "Framework/InputRecord.h" #include "DataFormatsCTP/TriggerOffsetsParam.h" +#include "Framework/CCDBParamSpec.h" +#include "DataFormatsCTP/Configuration.h" using namespace o2::ctp::reco_workflow; void RawDecoderSpec::init(framework::InitContext& ctx) { - bool decodeinps = ctx.options().get("ctpinputs-decoding"); - mDecoder.setDecodeInps(decodeinps); + mDecodeinputs = ctx.options().get("ctpinputs-decoding"); + mDecoder.setDecodeInps(mDecodeinputs); mNTFToIntegrate = ctx.options().get("ntf-to-average"); mVerbose = ctx.options().get("use-verbose-mode"); int maxerrors = ctx.options().get("print-errors-num"); @@ -42,7 +43,7 @@ void RawDecoderSpec::init(framework::InitContext& ctx) mOutputLumiInfo.inp2 = inp2; mMaxInputSize = ctx.options().get("max-input-size"); mMaxInputSizeFatal = ctx.options().get("max-input-size-fatal"); - LOG(info) << "CTP reco init done. Inputs decoding here:" << decodeinps << " DoLumi:" << mDoLumi << " DoDigits:" << mDoDigits << " NTF:" << mNTFToIntegrate << " Lumi inputs:" << lumiinp1 << ":" << inp1 << " " << lumiinp2 << ":" << inp2 << " Max errors:" << maxerrors << " Max input size:" << mMaxInputSize << " MaxInputSizeFatal:" << mMaxInputSizeFatal; + LOG(info) << "CTP reco init done. Inputs decoding here:" << mDecodeinputs << " DoLumi:" << mDoLumi << " DoDigits:" << mDoDigits << " NTF:" << mNTFToIntegrate << " Lumi inputs:" << lumiinp1 << ":" << inp1 << " " << lumiinp2 << ":" << inp2 << " Max errors:" << maxerrors << " Max input size:" << mMaxInputSize << " MaxInputSizeFatal:" << mMaxInputSizeFatal; // mOutputLumiInfo.printInputs(); } void RawDecoderSpec::endOfStream(framework::EndOfStreamContext& ec) @@ -73,6 +74,7 @@ void RawDecoderSpec::endOfStream(framework::EndOfStreamContext& ec) } void RawDecoderSpec::run(framework::ProcessingContext& ctx) { + updateTimeDependentParams(ctx); mOutputDigits.clear(); std::map digits; using InputSpec = o2::framework::InputSpec; @@ -176,6 +178,7 @@ void RawDecoderSpec::run(framework::ProcessingContext& ctx) mOutputLumiInfo.orbit = lumiPointsHBF1[0].orbit; } mOutputLumiInfo.counts = mCountsT; + mOutputLumiInfo.countsFV0 = mCountsV; mOutputLumiInfo.nHBFCounted = mNHBIntegratedT; mOutputLumiInfo.nHBFCountedFV0 = mNHBIntegratedV; @@ -199,6 +202,8 @@ o2::framework::DataProcessorSpec o2::ctp::reco_workflow::getRawDecoderSpec(bool std::vector outputs; if (digits) { + inputs.emplace_back("ctpconfig", "CTP", "CTPCONFIG", 0, o2::framework::Lifetime::Condition, o2::framework::ccdbParamSpec("CTP/Config/Config", 1)); + inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, o2::framework::Lifetime::Condition, o2::framework::ccdbParamSpec("CTP/Config/TriggerOffsets")); outputs.emplace_back("CTP", "DIGITS", 0, o2::framework::Lifetime::Timeframe); } if (lumi) { @@ -219,3 +224,18 @@ o2::framework::DataProcessorSpec o2::ctp::reco_workflow::getRawDecoderSpec(bool {"max-input-size-fatal", o2::framework::VariantType::Bool, false, {"If true issue fatal error otherwise error on;y"}}, {"ctpinputs-decoding", o2::framework::VariantType::Bool, false, {"Inputs alignment: true - raw decoder - has to be compatible with CTF decoder: allowed options: 10,01,00"}}}}; } +void RawDecoderSpec::updateTimeDependentParams(framework::ProcessingContext& pc) +{ + if (pc.services().get().globalRunNumberChanged) { + pc.inputs().get("trigoffset"); + const auto& trigOffsParam = o2::ctp::TriggerOffsetsParam::Instance(); + LOG(info) << "updateing TroggerOffsetsParam: inputs L0_L1:" << trigOffsParam.L0_L1 << " classes L0_L1:" << trigOffsParam.L0_L1_classes; + if (mDecodeinputs) { + const auto ctpcfg = pc.inputs().get("ctpconfig"); + if (ctpcfg != nullptr) { + mDecoder.setCTPConfig(*ctpcfg); + LOG(info) << "ctpconfig for run done:" << mDecoder.getCTPConfig().getRunNumber(); + } + } + } +} From 78453a74d0d60b1bf9f421067265616ab9fa6f35 Mon Sep 17 00:00:00 2001 From: wiechula <11199190+wiechula@users.noreply.github.com> Date: Thu, 28 Nov 2024 15:53:15 +0100 Subject: [PATCH 0117/2180] TPC MC: Move hit exlusion after Track Ref creation --- Detectors/TPC/simulation/src/Detector.cxx | 36 +++++++++++------------ 1 file changed, 18 insertions(+), 18 deletions(-) diff --git a/Detectors/TPC/simulation/src/Detector.cxx b/Detectors/TPC/simulation/src/Detector.cxx index e261424c41332..36b86d8a6e532 100644 --- a/Detectors/TPC/simulation/src/Detector.cxx +++ b/Detectors/TPC/simulation/src/Detector.cxx @@ -142,6 +142,24 @@ Bool_t Detector::ProcessHits(FairVolume* vol) // TODO: Temporary hack to process only one sector // if (sectorID != 0) return kFALSE; + // ---| momentum and beta gamma |--- + static TLorentzVector momentum; // static to make avoid creation/deletion of this expensive object + fMC->TrackMomentum(momentum); + + const float time = fMC->TrackTime() * 1.0e9; + const int trackID = fMC->GetStack()->GetCurrentTrackNumber(); + const int detID = vol->getMCid(); + o2::data::Stack* stack = (o2::data::Stack*)fMC->GetStack(); + if (fMC->IsTrackEntering() || fMC->IsTrackExiting()) { + stack->addTrackReference(o2::TrackReference(position.X(), position.Y(), position.Z(), momentum.X(), momentum.Y(), + momentum.Z(), fMC->TrackLength(), time, trackID, GetDetId())); + } + if (TMath::Abs(lastReferenceR - fMC->TrackLength()) > kMaxDistRef) { /// we can speedup + stack->addTrackReference(o2::TrackReference(position.X(), position.Y(), position.Z(), momentum.X(), momentum.Y(), + momentum.Z(), fMC->TrackLength(), time, trackID, GetDetId())); + lastReferenceR = fMC->TrackLength(); + } + // ---| remove clusters between the IFC and the FC strips |--- // those should not enter the active readout area // do coarse selection before, to limit number of transformations @@ -164,24 +182,6 @@ Bool_t Detector::ProcessHits(FairVolume* vol) } } - // ---| momentum and beta gamma |--- - static TLorentzVector momentum; // static to make avoid creation/deletion of this expensive object - fMC->TrackMomentum(momentum); - - const float time = fMC->TrackTime() * 1.0e9; - const int trackID = fMC->GetStack()->GetCurrentTrackNumber(); - const int detID = vol->getMCid(); - o2::data::Stack* stack = (o2::data::Stack*)fMC->GetStack(); - if (fMC->IsTrackEntering() || fMC->IsTrackExiting()) { - stack->addTrackReference(o2::TrackReference(position.X(), position.Y(), position.Z(), momentum.X(), momentum.Y(), - momentum.Z(), fMC->TrackLength(), time, trackID, GetDetId())); - } - if (TMath::Abs(lastReferenceR - fMC->TrackLength()) > kMaxDistRef) { /// we can speedup - stack->addTrackReference(o2::TrackReference(position.X(), position.Y(), position.Z(), momentum.X(), momentum.Y(), - momentum.Z(), fMC->TrackLength(), time, trackID, GetDetId())); - lastReferenceR = fMC->TrackLength(); - } - // ===| CONVERT THE ENERGY LOSS TO IONIZATION ELECTRONS |===================== // // The energy loss is implemented directly below and taken GEANT3, From 4b86cfcf491827f6f3daa1984457a8b5609b10f0 Mon Sep 17 00:00:00 2001 From: wiechula <11199190+wiechula@users.noreply.github.com> Date: Thu, 28 Nov 2024 16:04:10 +0100 Subject: [PATCH 0118/2180] Add missing setting of variable --- Detectors/TPC/simulation/src/Detector.cxx | 1 + 1 file changed, 1 insertion(+) diff --git a/Detectors/TPC/simulation/src/Detector.cxx b/Detectors/TPC/simulation/src/Detector.cxx index 36b86d8a6e532..1a7c0fc25802b 100644 --- a/Detectors/TPC/simulation/src/Detector.cxx +++ b/Detectors/TPC/simulation/src/Detector.cxx @@ -153,6 +153,7 @@ Bool_t Detector::ProcessHits(FairVolume* vol) if (fMC->IsTrackEntering() || fMC->IsTrackExiting()) { stack->addTrackReference(o2::TrackReference(position.X(), position.Y(), position.Z(), momentum.X(), momentum.Y(), momentum.Z(), fMC->TrackLength(), time, trackID, GetDetId())); + lastReferenceR = fMC->TrackLength(); } if (TMath::Abs(lastReferenceR - fMC->TrackLength()) > kMaxDistRef) { /// we can speedup stack->addTrackReference(o2::TrackReference(position.X(), position.Y(), position.Z(), momentum.X(), momentum.Y(), From 4f5e4fbe58d201bee5d4948aa003492593e78986 Mon Sep 17 00:00:00 2001 From: jditzelnew <58816213+jditzelnew@users.noreply.github.com> Date: Fri, 29 Nov 2024 12:16:30 +0100 Subject: [PATCH 0119/2180] Updating hypernuclei information in O2 Databases (#13750) Adding correct masses and inserting Xi-bound states Adding Xi-bound states, fixing masses, lifetimes and decay channels. Removing excited states for A=4 hypernuclei (see latest AliRoot AliMC.cxx). --- .../SimulationDataFormat/O2DatabasePDG.h | 54 +++--- Steer/src/O2MCApplication.cxx | 173 +++++++++++++++--- 2 files changed, 165 insertions(+), 62 deletions(-) diff --git a/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h b/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h index 229a1a7a8a535..6b1690946e951 100644 --- a/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h +++ b/DataFormats/simulation/include/SimulationDataFormat/O2DatabasePDG.h @@ -235,62 +235,38 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db) //Hyper nuclei and exotica ionCode = 1010010030; if (!db->GetParticle(ionCode)) { - db->AddParticle("HyperTriton", "HyperTriton", 2.99131, kFALSE, + db->AddParticle("HyperTriton", "HyperTriton", 2.991134, kFALSE, 2.5e-15, 3, "Ion", ionCode); } ionCode = -1010010030; if (!db->GetParticle(ionCode)) { - db->AddParticle("AntiHyperTriton", "AntiHyperTriton", 2.99131, kFALSE, + db->AddParticle("AntiHyperTriton", "AntiHyperTriton", 2.991134, kFALSE, 2.5e-15, 3, "Ion", ionCode); } //hyper hydrogen 4 ground state ionCode = 1010010040; if (!db->GetParticle(ionCode)) { - db->AddParticle("Hyperhydrog4", "Hyperhydrog4", 3.9226, kFALSE, + db->AddParticle("Hyperhydrog4", "Hyperhydrog4", 3.922434, kFALSE, 2.5e-15, 3, "Ion", ionCode); } //anti hyper hydrogen 4 ground state ionCode = -1010010040; if (!db->GetParticle(ionCode)) { - db->AddParticle("AntiHyperhydrog4", "AntiHyperhydrog4", 3.9226, kFALSE, - 2.5e-15, 3, "Ion", ionCode); - } - //hyper hydrogen 4 excited state - ionCode = 1010010041; - if (!db->GetParticle(ionCode)) { - db->AddParticle("Hyperhydrog4*", "Hyperhydrog4*", 3.9237, kFALSE, - 2.5e-15, 3, "Ion", ionCode); - } - //anti hyper hydrogen 4 excited state - ionCode = -1010010041; - if (!db->GetParticle(ionCode)) { - db->AddParticle("AntiHyperhydrog4*", "AntiHyperhydrog4*", 3.9237, kFALSE, + db->AddParticle("AntiHyperhydrog4", "AntiHyperhydrog4", 3.922434, kFALSE, 2.5e-15, 3, "Ion", ionCode); } //hyper helium 4 ground state ionCode = 1010020040; if (!db->GetParticle(ionCode)) { - db->AddParticle("Hyperhelium4", "Hyperhelium4", 3.9217, kFALSE, + db->AddParticle("Hyperhelium4", "Hyperhelium4", 3.921728, kFALSE, 2.5e-15, 6, "Ion", ionCode); } //anti hyper helium 4 ground state ionCode = -1010020040; if (!db->GetParticle(ionCode)) { - db->AddParticle("AntiHyperhelium4", "AntiHyperhelium4", 3.9217, kFALSE, - 2.5e-15, 6, "Ion", ionCode); - } - //hyper helium 4 excited state - ionCode = 1010020041; - if (!db->GetParticle(ionCode)) { - db->AddParticle("Hyperhelium4*", "Hyperhelium4*", 3.9231, kFALSE, - 2.5e-15, 6, "Ion", ionCode); - } - //anti hyper helium 4 excited state - ionCode = -1010020041; - if (!db->GetParticle(ionCode)) { - db->AddParticle("AntiHyperhelium4*", "AntiHyperhelium4*", 3.9231, kFALSE, + db->AddParticle("AntiHyperhelium4", "AntiHyperhelium4", 3.921728, kFALSE, 2.5e-15, 6, "Ion", ionCode); } @@ -309,13 +285,13 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db) ionCode = 1010020050; if (!db->GetParticle(ionCode)) { - db->AddParticle("Hyperhelium5", "Hyperhelium5", 4.841, kFALSE, + db->AddParticle("Hyperhelium5", "Hyperhelium5", 4.839961, kFALSE, 2.5e-15, 6, "Ion", ionCode); } ionCode = -1010020050; if (!db->GetParticle(ionCode)) { - db->AddParticle("AntiHyperhelium5", "AntiHyperhelium5", 4.841, kFALSE, + db->AddParticle("AntiHyperhelium5", "AntiHyperhelium5", 4.839961, kFALSE, 2.5e-15, 6, "Ion", ionCode); } @@ -331,6 +307,20 @@ inline void O2DatabasePDG::addALICEParticles(TDatabasePDG* db) 2.5e-15, 6, "Ion", ionCode); } + // 4-Xi-He + ionCode = 1120020040; + if (!db->GetParticle(ionCode)) { + db->AddParticle("4XiHe", "4XiHe", 4.128, kFALSE, 4.04e-15, 3, "Ion", ionCode); + db->AddAntiParticle("Anti4XiHe", -ionCode); + } + + // 4-Xi-H + ionCode = 1120010040; + if (!db->GetParticle(ionCode)) { + db->AddParticle("4XiH", "4XiH", 4.128, kFALSE, 4.04e-15, 3, "Ion", ionCode); + db->AddAntiParticle("Anti4XiH", -ionCode); + } + // hyper helium 4 sigma ionCode = 1110020040; if (!db->GetParticle(ionCode)) { diff --git a/Steer/src/O2MCApplication.cxx b/Steer/src/O2MCApplication.cxx index 96cc2f2e969db..02d332b0c0641 100644 --- a/Steer/src/O2MCApplication.cxx +++ b/Steer/src/O2MCApplication.cxx @@ -264,29 +264,19 @@ void addSpecialParticles() LOG(info) << "Adding custom particles to VMC"; //Hypertriton - TVirtualMC::GetMC()->DefineParticle(1010010030, "HyperTriton", kPTHadron, 2.99131, 1.0, 2.632e-10, "Ion", 0.0, 0, 1, 0, 0, 0, 0, 0, 3, kFALSE); + TVirtualMC::GetMC()->DefineParticle(1010010030, "HyperTriton", kPTHadron, 2.991134, 1.0, 2.632e-10, "Ion", 0.0, 0, 1, 0, 0, 0, 0, 0, 3, kFALSE); //Anti-Hypertriton - TVirtualMC::GetMC()->DefineParticle(-1010010030, "AntiHyperTriton", kPTHadron, 2.99131, 1.0, 2.632e-10, "Ion", 0.0, 0, 1, 0, 0, 0, 0, 0, 3, kFALSE); + TVirtualMC::GetMC()->DefineParticle(-1010010030, "AntiHyperTriton", kPTHadron, 2.991134, 1.0, 2.632e-10, "Ion", 0.0, 0, 1, 0, 0, 0, 0, 0, 3, kFALSE); //Hyper hydrogen 4 ground state - TVirtualMC::GetMC()->DefineParticle(1010010040, "Hyperhydrog4", kPTHadron, 3.9226, 1.0, 2.632e-10, "Ion", 0.0, 0, 1, 0, 0, 0, 0, 0, 4, kFALSE); + TVirtualMC::GetMC()->DefineParticle(1010010040, "Hyperhydrog4", kPTHadron, 3.922434, 1.0, 2.08e-10, "Ion", 0.0, 0, 1, 0, 0, 0, 0, 0, 4, kFALSE); //Anti-Hyper hydrogen 4 ground state - TVirtualMC::GetMC()->DefineParticle(-1010010040, "AntiHyperhydrog4", kPTHadron, 3.9226, 1.0, 2.632e-10, "Ion", 0.0, 0, 1, 0, 0, 0, 0, 0, 4, kFALSE); - - //Hyper hydrogen 4 excited state - TVirtualMC::GetMC()->DefineParticle(1010010041, "Hyperhydrog4*", kPTHadron, 3.9237, 1.0, 2.632e-10, "Ion", 0.0, 0, 1, 0, 0, 0, 0, 0, 4, kFALSE); - //Anti-Hyper hydrogen 4 excited state - TVirtualMC::GetMC()->DefineParticle(-1010010041, "AntiHyperhydrog4*", kPTHadron, 3.9237, 1.0, 2.632e-10, "Ion", 0.0, 0, 1, 0, 0, 0, 0, 0, 4, kFALSE); + TVirtualMC::GetMC()->DefineParticle(-1010010040, "AntiHyperhydrog4", kPTHadron, 3.922434, 1.0, 2.08e-10, "Ion", 0.0, 0, 1, 0, 0, 0, 0, 0, 4, kFALSE); //Hyper helium 4 ground state - TVirtualMC::GetMC()->DefineParticle(1010020040, "Hyperhelium4", kPTHadron, 3.9217, 2.0, 2.632e-10, "Ion", 0.0, 0, 1, 0, 0, 0, 0, 0, 4, kFALSE); + TVirtualMC::GetMC()->DefineParticle(1010020040, "Hyperhelium4", kPTHadron, 3.921728, 2.0, 2.50e-10, "Ion", 0.0, 0, 1, 0, 0, 0, 0, 0, 4, kFALSE); //Anti-Hyper helium 4 ground state - TVirtualMC::GetMC()->DefineParticle(-1010020040, "AntiHyperhelium4", kPTHadron, 3.9217, 2.0, 2.632e-10, "Ion", 0.0, 0, 1, 0, 0, 0, 0, 0, 4, kFALSE); - - //Hyper helium 4 excited state - TVirtualMC::GetMC()->DefineParticle(1010020041, "Hyperhelium4*", kPTHadron, 3.9231, 2.0, 2.632e-10, "Ion", 0.0, 0, 1, 0, 0, 0, 0, 0, 4, kFALSE); - //Anti-Hyper helium 4 excited state - TVirtualMC::GetMC()->DefineParticle(-1010020041, "AntiHyperhelium4*", kPTHadron, 3.9231, 2.0, 2.632e-10, "Ion", 0.0, 0, 1, 0, 0, 0, 0, 0, 4, kFALSE); + TVirtualMC::GetMC()->DefineParticle(-1010020040, "AntiHyperhelium4", kPTHadron, 3.921728, 2.0, 2.50e-10, "Ion", 0.0, 0, 1, 0, 0, 0, 0, 0, 4, kFALSE); // Lithium 4 ground state TVirtualMC::GetMC()->DefineParticle(1000030040, "Lithium4", kPTHadron, 3.7513, 3.0, 9.1e-23, "Ion", 0.003, 0, 1, 0, 0, 0, 0, 0, 4, kFALSE); @@ -294,15 +284,24 @@ void addSpecialParticles() TVirtualMC::GetMC()->DefineParticle(-1000030040, "AntiLithium4", kPTHadron, 3.7513, 3.0, 9.1e-23, "Ion", 0.003, 0, 1, 0, 0, 0, 0, 0, 4, kFALSE); //Hyper helium 5 - TVirtualMC::GetMC()->DefineParticle(1010020050, "Hyperhelium5", kPTHadron, 4.841, 2.0, 2.632e-10, "Ion", 0.0, 0, 1, 0, 0, 0, 0, 0, 5, kFALSE); + TVirtualMC::GetMC()->DefineParticle(1010020050, "Hyperhelium5", kPTHadron, 4.839961, 2.0, 2.74e-10, "Ion", 0.0, 0, 1, 0, 0, 0, 0, 0, 5, kFALSE); //Anti-Hyper helium 5 - TVirtualMC::GetMC()->DefineParticle(-1010020050, "AntiHyperhelium5", kPTHadron, 4.841, 2.0, 2.632e-10, "Ion", 0.0, 0, 1, 0, 0, 0, 0, 0, 5, kFALSE); + TVirtualMC::GetMC()->DefineParticle(-1010020050, "AntiHyperhelium5", kPTHadron, 4.839961, 2.0, 2.74e-10, "Ion", 0.0, 0, 1, 0, 0, 0, 0, 0, 5, kFALSE); //Double Hyper hydrogen 4 TVirtualMC::GetMC()->DefineParticle(1020010040, "DoubleHyperhydrogen4", kPTHadron, 4.106, 1.0, 2.632e-10, "Ion", 0.0, 0, 1, 0, 0, 0, 0, 0, 4, kFALSE); //Double Anti-Hyper hydrogen 4 TVirtualMC::GetMC()->DefineParticle(-1020010040, "DoubleAntiHyperhydrogen4", kPTHadron, 4.106, 1.0, 2.632e-10, "Ion", 0.0, 0, 1, 0, 0, 0, 0, 0, 4, kFALSE); + // 4Xi(-)H + TVirtualMC::GetMC()->DefineParticle(1120010040, "4XiH", kPTHadron, 4.128, 1.0, 1.639e-10, "Ion", 0.0, 0, 1, 0, 0, 0, 0, 0, 4, kFALSE); + // Anti-4Xi(-)H + TVirtualMC::GetMC()->DefineParticle(-1120010040, "Anti4XiH", kPTHadron, 4.128, 1.0, 1.639e-10, "Ion", 0.0, 0, 1, 0, 0, 0, 0, 0, 4, kFALSE); + // 4Xi(-)He + TVirtualMC::GetMC()->DefineParticle(1120020040, "4XiHe", kPTHadron, 4.128, 1.0, 1.639e-10, "Ion", 0.0, 0, 1, 0, 0, 0, 0, 0, 4, kFALSE); + // Anti-4Xi(-)He + TVirtualMC::GetMC()->DefineParticle(-1120020040, "Anti4XiHe", kPTHadron, 4.128, 1.0, 1.639e-10, "Ion", 0.0, 0, 1, 0, 0, 0, 0, 0, 4, kFALSE); + // Hyper helium 4 sigma TVirtualMC::GetMC()->DefineParticle(1110020040, "Hyperhelium4sigma", kPTHadron, 3.995, 2.0, 8.018e-11, "Ion", 0.0, 0, 1, 0, 0, 0, 0, 0, 4, kFALSE); // Anti-Hyper helium 4 sigma @@ -586,8 +585,6 @@ void addSpecialParticles() mode3[1][2] = -211; // negative pion TVirtualMC::GetMC()->SetDecayMode(1010010040, bratio3, mode3); - //Decay for the excited state (after em transition) - TVirtualMC::GetMC()->SetDecayMode(1010010041, bratio3, mode3); // Define the 2- and 3-body phase space decay for the Hyper Hydrogen 4 Int_t amode3[6][3]; @@ -608,8 +605,6 @@ void addSpecialParticles() amode3[1][2] = 211; // positive pion TVirtualMC::GetMC()->SetDecayMode(-1010010040, abratio3, amode3); - //Decay for the excited state (after em transition) - TVirtualMC::GetMC()->SetDecayMode(-1010010041, abratio3, amode3); // Define the 3-body phase space decay for the Hyper Helium 4 Int_t mode4[6][3]; @@ -621,14 +616,16 @@ void addSpecialParticles() mode4[kz][1] = 0; mode4[kz][2] = 0; } - bratio4[0] = 100.; + bratio4[0] = 50.; mode4[0][0] = 1000020030; // Helium3 mode4[0][1] = -211; // negative pion mode4[0][2] = 2212; // proton + bratio4[1] = 50.; + mode4[1][0] = 1000030040; // lithium-4 + mode4[1][1] = -211; // negative pion + TVirtualMC::GetMC()->SetDecayMode(1010020040, bratio4, mode4); - //Decay for the excited state (after em transition) - TVirtualMC::GetMC()->SetDecayMode(1010020041, bratio4, mode4); // Define the 2-body phase space decay for the Anti-Hyper Helium 4 Int_t amode4[6][3]; @@ -640,14 +637,16 @@ void addSpecialParticles() amode4[kz][1] = 0; amode4[kz][2] = 0; } - abratio4[0] = 100.; + abratio4[0] = 50.; amode4[0][0] = -1000020030; // anti-Helium 3 amode4[0][1] = 211; // positive pion amode4[0][2] = -2212; // anti proton + abratio4[1] = 50.; + amode4[1][0] = -1000030040; // antilithium-4 + amode4[1][1] = 211; // positive pion + TVirtualMC::GetMC()->SetDecayMode(-1010020040, abratio4, amode4); - //Decay for the excited state (after em transition) - TVirtualMC::GetMC()->SetDecayMode(-1010020041, abratio4, amode4); // Define the 2-body phase space decay for the Lithium 4 Int_t model4[6][3]; @@ -733,10 +732,15 @@ void addSpecialParticles() mode42[kz][1] = 0; mode42[kz][2] = 0; } - bratio42[0] = 100.; + bratio42[0] = 50.; mode42[0][0] = 1010020040; // Hyper-Helium4 mode42[0][1] = -211; // negative pion + bratio42[1] = 50.; + mode42[1][0] = 1010010030; // Hypertriton + mode42[1][1] = 2212; // proton + mode42[1][2] = -211; // negative pion + TVirtualMC::GetMC()->SetDecayMode(1020010040, bratio42, mode42); // Define the 2-body phase space decay for the Anti Double Hyper Hydrogen 4 @@ -749,12 +753,121 @@ void addSpecialParticles() amode42[kz][1] = 0; amode42[kz][2] = 0; } - abratio42[0] = 100.; + abratio42[0] = 50.; amode42[0][0] = -1010020040; // anti-Hyper-Helium 4 amode42[0][1] = 211; // positive pion + abratio42[1] = 50.; + amode42[1][0] = -1010010030; // anti-Hypertriton + amode42[1][1] = -2212; // antiproton + amode42[1][2] = 211; // positive pion + TVirtualMC::GetMC()->SetDecayMode(-1020010040, abratio42, amode42); + // Define the decay for the 4Xi(-)He + Int_t mode4XiHe[6][3]; + Float_t bratio4XiHe[6]; + + for (Int_t kz = 0; kz < 6; kz++) { + bratio4XiHe[kz] = 0.; + mode4XiHe[kz][0] = 0; + mode4XiHe[kz][1] = 0; + mode4XiHe[kz][2] = 0; + } + bratio4XiHe[0] = 33.; + mode4XiHe[0][0] = 1010020040; // HyperHelium4 + mode4XiHe[0][1] = -211; // negative pion + + bratio4XiHe[1] = 33.; + mode4XiHe[1][0] = 3122; // lambda + mode4XiHe[1][1] = 1000020030; // helium-3 + mode4XiHe[1][2] = -211; // negative pion + + bratio4XiHe[2] = 33.; + mode4XiHe[2][0] = 1000030040; // lithium-4 + mode4XiHe[2][1] = -211; // negative pion + mode4XiHe[2][2] = -211; // negative pion + + TVirtualMC::GetMC()->SetDecayMode(1120020040, bratio4XiHe, mode4XiHe); + + // Define the decay for the Anti-4Xi(-)He + Int_t amode4XiHe[6][3]; + Float_t abratio4XiHe[6]; + + for (Int_t kz = 0; kz < 6; kz++) { + abratio4XiHe[kz] = 0.; + amode4XiHe[kz][0] = 0; + amode4XiHe[kz][1] = 0; + amode4XiHe[kz][2] = 0; + } + abratio4XiHe[0] = 33.; + amode4XiHe[0][0] = -1010020040; // antiHyperHelium-4 + amode4XiHe[0][1] = 211; // positive pion + + abratio4XiHe[1] = 33.; + amode4XiHe[1][0] = -3122; // antilambda + amode4XiHe[1][1] = -1000020030; // antihelium-3 + amode4XiHe[1][2] = 211; // positive pion + + abratio4XiHe[2] = 33.; + amode4XiHe[2][0] = -1000030040; // antilithium-4 + amode4XiHe[2][1] = 211; // positive pion + amode4XiHe[2][2] = 211; // positive pion + + TVirtualMC::GetMC()->SetDecayMode(-1120020040, abratio4XiHe, amode4XiHe); + + // Define the decay for the 4Xi(-)H + Int_t mode4XiH[6][3]; + Float_t bratio4XiH[6]; + + for (Int_t kz = 0; kz < 6; kz++) { + bratio4XiH[kz] = 0.; + mode4XiH[kz][0] = 0; + mode4XiH[kz][1] = 0; + mode4XiH[kz][2] = 0; + } + bratio4XiH[0] = 33.; + mode4XiH[0][0] = 1010010040; // HyperHydrogen4 + mode4XiH[0][1] = -211; // negative pion + + bratio4XiH[1] = 33.; + mode4XiH[1][0] = 3122; // lambda + mode4XiH[1][1] = 1000010030; // triton + mode4XiH[1][2] = -211; // negative pion + + bratio4XiH[2] = 33.; + mode4XiH[2][0] = 1000020040; // alpha + mode4XiH[2][1] = -211; // negative pion + mode4XiH[2][2] = -211; // negative pion + + TVirtualMC::GetMC()->SetDecayMode(1120010040, bratio4XiH, mode4XiH); + + // Define the decay for the Anti-4Xi(-)H + Int_t amode4XiH[6][3]; + Float_t abratio4XiH[6]; + + for (Int_t kz = 0; kz < 6; kz++) { + abratio4XiH[kz] = 0.; + amode4XiH[kz][0] = 0; + amode4XiH[kz][1] = 0; + amode4XiH[kz][2] = 0; + } + abratio4XiH[0] = 33.; + amode4XiH[0][0] = -1010010040; // antiHyperHydrogen-4 + amode4XiH[0][1] = 211; // positive pion + + abratio4XiH[1] = 33.; + amode4XiH[1][0] = -3122; // antilambda + amode4XiH[1][1] = -1000010030; // antitriton + amode4XiH[1][2] = 211; // positive pion + + abratio4XiH[2] = 33.; + amode4XiH[2][0] = -1000020040; // antialpha + amode4XiH[2][1] = 211; // positive pion + amode4XiH[2][2] = 211; // positive pion + + TVirtualMC::GetMC()->SetDecayMode(-1120010040, abratio4XiH, amode4XiH); + // Define the 2- and 3-body phase space decay for the Hyper Helium 4 sigma Int_t mode4s[6][3]; Float_t bratio4s[6]; From 167b8c0de7f447ee0a462d88d91f4c69f8677f8b Mon Sep 17 00:00:00 2001 From: Marco Giacalone Date: Fri, 29 Nov 2024 17:37:07 +0100 Subject: [PATCH 0120/2180] Re-enabled TPC TrackRef check after fix (#13756) * Re-enabled TPC TrackRef check after fix * Set fixed seed for Geant3/4 tests, previously failing the CI --- run/CMakeLists.txt | 4 ++++ run/checkStack.cxx | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/run/CMakeLists.txt b/run/CMakeLists.txt index f21ecafb0528a..662716901ed0a 100644 --- a/run/CMakeLists.txt +++ b/run/CMakeLists.txt @@ -193,6 +193,8 @@ o2_add_test_command(NAME o2sim_G4 2 --skipModules MFT ZDC + --seed + 15946057944514955802 --configKeyValues "align-geom.mDetectors=none" ENVIRONMENT "${SIMENV}" @@ -255,6 +257,8 @@ o2_add_test_command(NAME o2sim_G3 pythia8pp --chunkSize 10 + --seed + 15946057944514955802 --configKeyValues "align-geom.mDetectors=none" LABELS g3 sim long diff --git a/run/checkStack.cxx b/run/checkStack.cxx index 98f2669c9f97e..36a9da7a62c13 100644 --- a/run/checkStack.cxx +++ b/run/checkStack.cxx @@ -144,7 +144,7 @@ int main(int argc, char** argv) for (auto& trackID : trackidsinTPC) { auto tpc_trackrefs = mcreader.getTrackRefs(eventID, trackID); LOG(debug) << " Track " << trackID << " has " << tpc_trackrefs.size() << " TrackRefs"; - // assert(tpc_trackrefs.size() > 0); + assert(tpc_trackrefs.size() > 0); for (auto& ref : tpc_trackrefs) { assert(ref.getTrackID() == trackID); } From dc760aaed875633b84ce0953ad9cb744892a747d Mon Sep 17 00:00:00 2001 From: Gabriele Cimador <92120560+cima22@users.noreply.github.com> Date: Sun, 1 Dec 2024 15:18:26 +0100 Subject: [PATCH 0121/2180] GPU TPC: Decoding: Add option to apply timebin cut to CTF cluster decoding on GPUs (#13753) * GPU: TPC Decoding: add optional timebin cut to CTF cluster decoding * GPU: TPC Decoding: add missing checks on track model parameters --- .../DataCompression/GPUTPCDecompression.cxx | 21 +++++ .../DataCompression/GPUTPCDecompression.h | 9 ++ .../GPUTPCDecompressionKernels.cxx | 47 +++++++++- .../GPUTPCDecompressionKernels.h | 6 +- .../Definitions/GPUDefGPUParameters.h | 6 ++ .../Global/GPUChainTrackingCompression.cxx | 89 +++++++++++++++---- GPU/GPUTracking/kernels.cmake | 2 + 7 files changed, 163 insertions(+), 17 deletions(-) diff --git a/GPU/GPUTracking/DataCompression/GPUTPCDecompression.cxx b/GPU/GPUTracking/DataCompression/GPUTPCDecompression.cxx index 0f7acfce86094..7c10f0eeef74f 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCDecompression.cxx +++ b/GPU/GPUTracking/DataCompression/GPUTPCDecompression.cxx @@ -84,6 +84,24 @@ void* GPUTPCDecompression::SetPointersTmpNativeBuffersInput(void* mem) return mem; } +void* GPUTPCDecompression::SetPointersTmpClusterNativeAccessForFiltering(void* mem) +{ + computePointerWithAlignment(mem, mNativeClustersBuffer, mNClusterNativeBeforeFiltering); + return mem; +} + +void* GPUTPCDecompression::SetPointersInputClusterNativeAccess(void* mem) +{ + computePointerWithAlignment(mem, mClusterNativeAccess); + return mem; +} + +void* GPUTPCDecompression::SetPointersNClusterPerSectorRow(void* mem) +{ + computePointerWithAlignment(mem, mNClusterPerSectorRow, NSLICES * GPUCA_ROW_COUNT); + return mem; +} + void GPUTPCDecompression::RegisterMemoryAllocation() { AllocateAndInitializeLate(); @@ -91,6 +109,9 @@ void GPUTPCDecompression::RegisterMemoryAllocation() mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersTmpNativeBuffersGPU, GPUMemoryResource::MEMORY_SCRATCH, "TPCDecompressionTmpBuffersGPU"); mResourceTmpIndexes = mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersTmpNativeBuffersOutput, GPUMemoryResource::MEMORY_OUTPUT | GPUMemoryResource::MEMORY_SCRATCH, "TPCDecompressionTmpBuffersOutput"); mResourceTmpClustersOffsets = mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersTmpNativeBuffersInput, GPUMemoryResource::MEMORY_INPUT | GPUMemoryResource::MEMORY_SCRATCH, "TPCDecompressionTmpBuffersInput"); + mResourceTmpBufferBeforeFiltering = mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersTmpClusterNativeAccessForFiltering, GPUMemoryResource::MEMORY_CUSTOM | GPUMemoryResource::MEMORY_SCRATCH, "TPCDecompressionTmpBufferForFiltering"); + mResourceClusterNativeAccess = mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersInputClusterNativeAccess, GPUMemoryResource::MEMORY_INPUT | GPUMemoryResource::MEMORY_CUSTOM | GPUMemoryResource::MEMORY_SCRATCH, "TPCDecompressionTmpClusterAccessForFiltering"); + mResourceNClusterPerSectorRow = mRec->RegisterMemoryAllocation(this, &GPUTPCDecompression::SetPointersNClusterPerSectorRow, GPUMemoryResource::MEMORY_OUTPUT | GPUMemoryResource::MEMORY_CUSTOM | GPUMemoryResource::MEMORY_SCRATCH, "TPCDecompressionTmpClusterCountForFiltering"); } void GPUTPCDecompression::SetMaxData(const GPUTrackingInOutPointers& io) diff --git a/GPU/GPUTracking/DataCompression/GPUTPCDecompression.h b/GPU/GPUTracking/DataCompression/GPUTPCDecompression.h index d9871613d8401..47c64008b176e 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCDecompression.h +++ b/GPU/GPUTracking/DataCompression/GPUTPCDecompression.h @@ -55,6 +55,9 @@ class GPUTPCDecompression : public GPUProcessor void* SetPointersTmpNativeBuffersGPU(void* mem); void* SetPointersTmpNativeBuffersOutput(void* mem); void* SetPointersTmpNativeBuffersInput(void* mem); + void* SetPointersTmpClusterNativeAccessForFiltering(void* mem); + void* SetPointersInputClusterNativeAccess(void* mem); + void* SetPointersNClusterPerSectorRow(void* mem); #endif @@ -63,11 +66,14 @@ class GPUTPCDecompression : public GPUProcessor o2::tpc::CompressedClusters mInputGPU; uint32_t mMaxNativeClustersPerBuffer; + uint32_t mNClusterNativeBeforeFiltering; uint32_t* mNativeClustersIndex; uint32_t* mUnattachedClustersOffsets; uint32_t* mAttachedClustersOffsets; + uint32_t* mNClusterPerSectorRow; o2::tpc::ClusterNative* mTmpNativeClusters; o2::tpc::ClusterNative* mNativeClustersBuffer; + o2::tpc::ClusterNativeAccess* mClusterNativeAccess; template void SetPointersCompressedClusters(void*& mem, T& c, uint32_t nClA, uint32_t nTr, uint32_t nClU, bool reducedClA); @@ -75,6 +81,9 @@ class GPUTPCDecompression : public GPUProcessor int16_t mMemoryResInputGPU = -1; int16_t mResourceTmpIndexes = -1; int16_t mResourceTmpClustersOffsets = -1; + int16_t mResourceTmpBufferBeforeFiltering = -1; + int16_t mResourceClusterNativeAccess = -1; + int16_t mResourceNClusterPerSectorRow = -1; }; } // namespace GPUCA_NAMESPACE::gpu #endif // GPUTPCDECOMPRESSION_H diff --git a/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.cxx b/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.cxx index 2c88ea0079a26..d7f1e2ac88368 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.cxx +++ b/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.cxx @@ -43,7 +43,7 @@ GPUdii() void GPUTPCDecompressionKernels::Thread 0 ? cl.getTime() < param.tpcCutTimeBin : true; +} + +template <> +GPUdii() void GPUTPCDecompressionUtilKernels::Thread(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUsharedref() GPUSharedMemory& smem, processorType& processors) +{ + const GPUParam& GPUrestrict() param = processors.param; + GPUTPCDecompression& GPUrestrict() decompressor = processors.tpcDecompressor; + const ClusterNativeAccess* clusterAccess = decompressor.mClusterNativeAccess; + for (uint32_t i = get_global_id(0); i < GPUCA_NSLICES * GPUCA_ROW_COUNT; i += get_global_size(0)) { + uint32_t slice = i / GPUCA_ROW_COUNT; + uint32_t row = i % GPUCA_ROW_COUNT; + for (uint32_t k = 0; k < clusterAccess->nClusters[slice][row]; k++) { + ClusterNative cl = clusterAccess->clusters[slice][row][k]; + if (isClusterKept(cl, param)) { + decompressor.mNClusterPerSectorRow[i]++; + } + } + } +} + +template <> +GPUdii() void GPUTPCDecompressionUtilKernels::Thread(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUsharedref() GPUSharedMemory& smem, processorType& processors) +{ + const GPUParam& GPUrestrict() param = processors.param; + GPUTPCDecompression& GPUrestrict() decompressor = processors.tpcDecompressor; + ClusterNative* GPUrestrict() clusterBuffer = decompressor.mNativeClustersBuffer; + const ClusterNativeAccess* clusterAccess = decompressor.mClusterNativeAccess; + const ClusterNativeAccess* outputAccess = processors.ioPtrs.clustersNative; + for (uint32_t i = get_global_id(0); i < GPUCA_NSLICES * GPUCA_ROW_COUNT; i += get_global_size(0)) { + uint32_t slice = i / GPUCA_ROW_COUNT; + uint32_t row = i % GPUCA_ROW_COUNT; + uint32_t count = 0; + for (uint32_t k = 0; k < clusterAccess->nClusters[slice][row]; k++) { + const ClusterNative cl = clusterAccess->clusters[slice][row][k]; + if (isClusterKept(cl, param)) { + clusterBuffer[outputAccess->clusterOffset[slice][row] + count] = cl; + count++; + } + } + } +} + template <> GPUdii() void GPUTPCDecompressionUtilKernels::Thread(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUsharedref() GPUSharedMemory& smem, processorType& processors) { diff --git a/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.h b/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.h index 622e1fd984fa7..b45af622ebac8 100644 --- a/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.h +++ b/GPU/GPUTracking/DataCompression/GPUTPCDecompressionKernels.h @@ -59,11 +59,15 @@ class GPUTPCDecompressionUtilKernels : public GPUKernelTemplate { public: enum K : int32_t { - sortPerSectorRow = 0, + countFilteredClusters = 0, + storeFilteredClusters = 1, + sortPerSectorRow = 2, }; template GPUd() static void Thread(int32_t nBlocks, int32_t nThreads, int32_t iBlock, int32_t iThread, GPUsharedref() GPUSharedMemory& smem, processorType& GPUrestrict() processors); + + GPUdi() static bool isClusterKept(const o2::tpc::ClusterNative& cl, const GPUParam& GPUrestrict() param); }; } // namespace GPUCA_NAMESPACE::gpu diff --git a/GPU/GPUTracking/Definitions/GPUDefGPUParameters.h b/GPU/GPUTracking/Definitions/GPUDefGPUParameters.h index 970e1b2926853..3852d37f6facf 100644 --- a/GPU/GPUTracking/Definitions/GPUDefGPUParameters.h +++ b/GPU/GPUTracking/Definitions/GPUDefGPUParameters.h @@ -344,6 +344,12 @@ #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 diff --git a/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx b/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx index 8ca3a83e780fb..01e4d011d08b9 100644 --- a/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx +++ b/GPU/GPUTracking/Global/GPUChainTrackingCompression.cxx @@ -246,6 +246,7 @@ int32_t GPUChainTracking::RunTPCDecompression() mRec->PushNonPersistentMemory(qStr2Tag("TPCDCMPR")); RecoStep myStep = RecoStep::TPCDecompression; bool doGPU = GetRecoStepsGPU() & RecoStep::TPCDecompression; + bool runFiltering = param().tpcCutTimeBin > 0; GPUTPCDecompression& Decompressor = processors()->tpcDecompressor; GPUTPCDecompression& DecompressorShadow = doGPU ? processorsShadow()->tpcDecompressor : Decompressor; const auto& threadContext = GetThreadContext(); @@ -253,6 +254,13 @@ int32_t GPUChainTracking::RunTPCDecompression() CompressedClusters& inputGPU = Decompressor.mInputGPU; 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"); + } + 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"); + } + int32_t inputStream = 0; int32_t unattachedStream = mRec->NStreams() - 1; inputGPU = cmprClsHost; @@ -300,12 +308,6 @@ int32_t GPUChainTracking::RunTPCDecompression() GPUMemCpy(myStep, inputGPUShadow.sigmaPadU, cmprClsHost.sigmaPadU, cmprClsHost.nUnattachedClusters * sizeof(cmprClsHost.sigmaPadU[0]), unattachedStream, toGPU); GPUMemCpy(myStep, inputGPUShadow.sigmaTimeU, cmprClsHost.sigmaTimeU, cmprClsHost.nUnattachedClusters * sizeof(cmprClsHost.sigmaTimeU[0]), unattachedStream, toGPU); - mInputsHost->mNClusterNative = mInputsShadow->mNClusterNative = cmprClsHost.nAttachedClusters + cmprClsHost.nUnattachedClusters; - AllocateRegisteredMemory(mInputsHost->mResourceClusterNativeOutput, mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::clustersNative)]); - AllocateRegisteredMemory(mInputsHost->mResourceClusterNativeBuffer); - DecompressorShadow.mNativeClustersBuffer = mInputsShadow->mPclusterNativeBuffer; - Decompressor.mNativeClustersBuffer = mInputsHost->mPclusterNativeOutput; - WriteToConstantMemory(myStep, (char*)&processors()->tpcDecompressor - (char*)processors(), &DecompressorShadow, sizeof(DecompressorShadow), inputStream); TransferMemoryResourceLinkToHost(RecoStep::TPCDecompression, Decompressor.mResourceTmpIndexes, inputStream, nullptr, mEvents->stream, nStreams); SynchronizeStream(inputStream); uint32_t offset = 0; @@ -324,27 +326,83 @@ int32_t GPUChainTracking::RunTPCDecompression() if (decodedAttachedClusters != cmprClsHost.nAttachedClusters) { GPUWarning("%u / %u clusters failed track model decoding (%f %%)", cmprClsHost.nAttachedClusters - decodedAttachedClusters, cmprClsHost.nAttachedClusters, 100.f * (float)(cmprClsHost.nAttachedClusters - decodedAttachedClusters) / (float)cmprClsHost.nAttachedClusters); } - if (doGPU) { - mClusterNativeAccess->clustersLinear = mInputsShadow->mPclusterNativeBuffer; + if (runFiltering) { // If filtering, allocate a temporary buffer and cluster native access in decompressor context + Decompressor.mNClusterNativeBeforeFiltering = DecompressorShadow.mNClusterNativeBeforeFiltering = decodedAttachedClusters + cmprClsHost.nUnattachedClusters; + AllocateRegisteredMemory(Decompressor.mResourceTmpBufferBeforeFiltering); + AllocateRegisteredMemory(Decompressor.mResourceClusterNativeAccess); + mClusterNativeAccess->clustersLinear = DecompressorShadow.mNativeClustersBuffer; + mClusterNativeAccess->setOffsetPtrs(); + *Decompressor.mClusterNativeAccess = *mClusterNativeAccess; + WriteToConstantMemory(myStep, (char*)&processors()->tpcDecompressor - (char*)processors(), &DecompressorShadow, sizeof(DecompressorShadow), inputStream); + TransferMemoryResourceLinkToGPU(RecoStep::TPCDecompression, Decompressor.mResourceClusterNativeAccess, inputStream, &mEvents->single); + } else { // If not filtering, directly allocate the final buffers + mInputsHost->mNClusterNative = mInputsShadow->mNClusterNative = cmprClsHost.nAttachedClusters + cmprClsHost.nUnattachedClusters; + AllocateRegisteredMemory(mInputsHost->mResourceClusterNativeOutput, mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::clustersNative)]); + AllocateRegisteredMemory(mInputsHost->mResourceClusterNativeBuffer); + DecompressorShadow.mNativeClustersBuffer = mInputsShadow->mPclusterNativeBuffer; + Decompressor.mNativeClustersBuffer = mInputsHost->mPclusterNativeOutput; + DecompressorShadow.mClusterNativeAccess = mInputsShadow->mPclusterNativeAccess; + Decompressor.mClusterNativeAccess = mInputsHost->mPclusterNativeAccess; + WriteToConstantMemory(myStep, (char*)&processors()->tpcDecompressor - (char*)processors(), &DecompressorShadow, sizeof(DecompressorShadow), inputStream); + if (doGPU) { + mClusterNativeAccess->clustersLinear = mInputsShadow->mPclusterNativeBuffer; + mClusterNativeAccess->setOffsetPtrs(); + *mInputsHost->mPclusterNativeAccess = *mClusterNativeAccess; + processorsShadow()->ioPtrs.clustersNative = mInputsShadow->mPclusterNativeAccess; + WriteToConstantMemory(RecoStep::TPCDecompression, (char*)&processors()->ioPtrs - (char*)processors(), &processorsShadow()->ioPtrs, sizeof(processorsShadow()->ioPtrs), inputStream); + TransferMemoryResourceLinkToGPU(RecoStep::TPCDecompression, mInputsHost->mResourceClusterNativeAccess, inputStream, &mEvents->single); + } + mIOPtrs.clustersNative = mClusterNativeAccess.get(); + mClusterNativeAccess->clustersLinear = mInputsHost->mPclusterNativeOutput; mClusterNativeAccess->setOffsetPtrs(); *mInputsHost->mPclusterNativeAccess = *mClusterNativeAccess; - processorsShadow()->ioPtrs.clustersNative = mInputsShadow->mPclusterNativeAccess; - WriteToConstantMemory(RecoStep::TPCDecompression, (char*)&processors()->ioPtrs - (char*)processors(), &processorsShadow()->ioPtrs, sizeof(processorsShadow()->ioPtrs), inputStream); - TransferMemoryResourceLinkToGPU(RecoStep::TPCDecompression, mInputsHost->mResourceClusterNativeAccess, inputStream, &mEvents->single); } - mIOPtrs.clustersNative = mClusterNativeAccess.get(); - mClusterNativeAccess->clustersLinear = mInputsHost->mPclusterNativeOutput; - mClusterNativeAccess->setOffsetPtrs(); uint32_t batchSize = doGPU ? 6 : NSLICES; for (uint32_t iSlice = 0; iSlice < NSLICES; iSlice = iSlice + batchSize) { int32_t iStream = (iSlice / batchSize) % mRec->NStreams(); runKernel({GetGridAuto(iStream), krnlRunRangeNone, {nullptr, &mEvents->single}}, iSlice, batchSize); uint32_t copySize = std::accumulate(mClusterNativeAccess->nClustersSector + iSlice, mClusterNativeAccess->nClustersSector + iSlice + batchSize, 0u); - GPUMemCpy(RecoStep::TPCDecompression, mInputsHost->mPclusterNativeOutput + mClusterNativeAccess->clusterOffset[iSlice][0], DecompressorShadow.mNativeClustersBuffer + mClusterNativeAccess->clusterOffset[iSlice][0], sizeof(Decompressor.mNativeClustersBuffer[0]) * copySize, iStream, false); + if (!runFiltering) { + GPUMemCpy(RecoStep::TPCDecompression, mInputsHost->mPclusterNativeOutput + mClusterNativeAccess->clusterOffset[iSlice][0], DecompressorShadow.mNativeClustersBuffer + mClusterNativeAccess->clusterOffset[iSlice][0], sizeof(Decompressor.mNativeClustersBuffer[0]) * copySize, iStream, false); + } } SynchronizeGPU(); + if (runFiltering) { // If filtering is applied, count how many clusters will remain after filtering and allocate final buffers accordingly + AllocateRegisteredMemory(Decompressor.mResourceNClusterPerSectorRow); + WriteToConstantMemory(myStep, (char*)&processors()->tpcDecompressor - (char*)processors(), &DecompressorShadow, sizeof(DecompressorShadow), unattachedStream); + runKernel({GetGridAutoStep(unattachedStream, RecoStep::TPCDecompression), krnlRunRangeNone}, DecompressorShadow.mNClusterPerSectorRow, NSLICES * GPUCA_ROW_COUNT * sizeof(DecompressorShadow.mNClusterPerSectorRow[0])); + runKernel(GetGridAutoStep(unattachedStream, RecoStep::TPCDecompression)); + TransferMemoryResourceLinkToHost(RecoStep::TPCDecompression, Decompressor.mResourceNClusterPerSectorRow, unattachedStream); + SynchronizeStream(unattachedStream); + uint32_t nClustersFinal = std::accumulate(Decompressor.mNClusterPerSectorRow, Decompressor.mNClusterPerSectorRow + inputGPU.nSliceRows, 0u); + mInputsHost->mNClusterNative = mInputsShadow->mNClusterNative = nClustersFinal; + AllocateRegisteredMemory(mInputsHost->mResourceClusterNativeOutput, mSubOutputControls[GPUTrackingOutputs::getIndex(&GPUTrackingOutputs::clustersNative)]); + AllocateRegisteredMemory(mInputsHost->mResourceClusterNativeBuffer); + DecompressorShadow.mNativeClustersBuffer = mInputsShadow->mPclusterNativeBuffer; + Decompressor.mNativeClustersBuffer = mInputsHost->mPclusterNativeOutput; + WriteToConstantMemory(myStep, (char*)&processors()->tpcDecompressor - (char*)processors(), &DecompressorShadow, sizeof(DecompressorShadow), unattachedStream); + for (uint32_t i = 0; i < NSLICES; i++) { + for (uint32_t j = 0; j < GPUCA_ROW_COUNT; j++) { + mClusterNativeAccess->nClusters[i][j] = Decompressor.mNClusterPerSectorRow[i * GPUCA_ROW_COUNT + j]; + } + } + if (doGPU) { + mClusterNativeAccess->clustersLinear = mInputsShadow->mPclusterNativeBuffer; + mClusterNativeAccess->setOffsetPtrs(); + *mInputsHost->mPclusterNativeAccess = *mClusterNativeAccess; + processorsShadow()->ioPtrs.clustersNative = mInputsShadow->mPclusterNativeAccess; + WriteToConstantMemory(RecoStep::TPCDecompression, (char*)&processors()->ioPtrs - (char*)processors(), &processorsShadow()->ioPtrs, sizeof(processorsShadow()->ioPtrs), unattachedStream); + TransferMemoryResourceLinkToGPU(RecoStep::TPCDecompression, mInputsHost->mResourceClusterNativeAccess, unattachedStream); + } + mIOPtrs.clustersNative = mClusterNativeAccess.get(); + mClusterNativeAccess->clustersLinear = mInputsHost->mPclusterNativeOutput; + mClusterNativeAccess->setOffsetPtrs(); + runKernel(GetGridAutoStep(unattachedStream, RecoStep::TPCDecompression)); + GPUMemCpy(RecoStep::TPCDecompression, mInputsHost->mPclusterNativeOutput, DecompressorShadow.mNativeClustersBuffer, sizeof(Decompressor.mNativeClustersBuffer[0]) * nClustersFinal, unattachedStream, false); + SynchronizeStream(unattachedStream); + } if (GetProcessingSettings().deterministicGPUReconstruction || GetProcessingSettings().debugLevel >= 4) { runKernel(GetGridAutoStep(unattachedStream, RecoStep::TPCDecompression)); const ClusterNativeAccess* decoded = mIOPtrs.clustersNative; @@ -357,6 +415,7 @@ int32_t GPUChainTracking::RunTPCDecompression() } } } + SynchronizeStream(unattachedStream); } mRec->PopNonPersistentMemory(RecoStep::TPCDecompression, qStr2Tag("TPCDCMPR")); } diff --git a/GPU/GPUTracking/kernels.cmake b/GPU/GPUTracking/kernels.cmake index b0aed5aba1166..f028c6990f267 100644 --- a/GPU/GPUTracking/kernels.cmake +++ b/GPU/GPUTracking/kernels.cmake @@ -108,6 +108,8 @@ o2_gpu_add_kernel("GPUTPCCompressionGatherKernels, multiBlock" "GPUTPCCom o2_gpu_add_kernel("GPUTPCDecompressionKernels, step0attached" "= TPCDECOMPRESSION" LB simple int32_t trackStart int32_t trackEnd) o2_gpu_add_kernel("GPUTPCDecompressionKernels, step1unattached" "= TPCDECOMPRESSION" LB simple int32_t sliceStart int32_t nSlices) o2_gpu_add_kernel("GPUTPCDecompressionUtilKernels, sortPerSectorRow" "GPUTPCDecompressionKernels" LB simple) +o2_gpu_add_kernel("GPUTPCDecompressionUtilKernels, countFilteredClusters" "GPUTPCDecompressionKernels" LB simple) +o2_gpu_add_kernel("GPUTPCDecompressionUtilKernels, storeFilteredClusters" "GPUTPCDecompressionKernels" LB simple) o2_gpu_add_kernel("GPUTPCCFCheckPadBaseline" "= TPCCLUSTERFINDER" LB single) o2_gpu_add_kernel("GPUTPCCFChargeMapFiller, fillIndexMap" "= TPCCLUSTERFINDER" LB single) o2_gpu_add_kernel("GPUTPCCFChargeMapFiller, fillFromDigits" "= TPCCLUSTERFINDER" LB single) From 73a96c308c3feac2884787b671d4b6f1421bf32b Mon Sep 17 00:00:00 2001 From: shahoian Date: Sun, 1 Dec 2024 15:14:35 +0100 Subject: [PATCH 0122/2180] Fixes to propagate updates of GPU params (D.Rohr) --- GPU/GPUTracking/DataTypes/GPUNewCalibValues.cxx | 5 ++++- GPU/GPUTracking/Global/GPUChainTracking.cxx | 2 +- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/GPU/GPUTracking/DataTypes/GPUNewCalibValues.cxx b/GPU/GPUTracking/DataTypes/GPUNewCalibValues.cxx index e86955d6da500..f4061fa12873c 100644 --- a/GPU/GPUTracking/DataTypes/GPUNewCalibValues.cxx +++ b/GPU/GPUTracking/DataTypes/GPUNewCalibValues.cxx @@ -19,12 +19,15 @@ using namespace GPUCA_NAMESPACE::gpu; void GPUNewCalibValues::updateFrom(const GPUNewCalibValues* from) { if (from->newSolenoidField) { - solenoidField = from->newSolenoidField; + newSolenoidField = true; + solenoidField = from->solenoidField; } if (from->newContinuousMaxTimeBin) { + newContinuousMaxTimeBin = true; continuousMaxTimeBin = from->continuousMaxTimeBin; } if (from->newTPCTimeBinCut) { + newTPCTimeBinCut = true; tpcTimeBinCut = from->tpcTimeBinCut; } } diff --git a/GPU/GPUTracking/Global/GPUChainTracking.cxx b/GPU/GPUTracking/Global/GPUChainTracking.cxx index 7b8e590242fae..ff476716febe8 100644 --- a/GPU/GPUTracking/Global/GPUChainTracking.cxx +++ b/GPU/GPUTracking/Global/GPUChainTracking.cxx @@ -633,7 +633,7 @@ int32_t GPUChainTracking::DoQueuedUpdates(int32_t stream, bool updateSlave) const GPUSettingsProcessing* p = nullptr; std::lock_guard lk(mMutexUpdateCalib); if (mUpdateNewCalibObjects) { - if (mNewCalibValues->newSolenoidField || mNewCalibValues->newContinuousMaxTimeBin) { + if (mNewCalibValues->newSolenoidField || mNewCalibValues->newContinuousMaxTimeBin || mNewCalibValues->newTPCTimeBinCut) { grp = std::make_unique(mRec->GetGRPSettings()); if (mNewCalibValues->newSolenoidField) { grp->solenoidBzNominalGPU = mNewCalibValues->solenoidField; From feea3ade7df2a217615e356e74906569eac9a24c Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Mon, 2 Dec 2024 10:40:48 +0100 Subject: [PATCH 0123/2180] Qatable (#13633) * TreeStream: Allow also for int8_t to be dumped as signed char Signed-off-by: Felix Schlepper * AOD: Extend TrackQA table * AOD: TrackQA leave _000 as default * COMMON: Add helper macros for bitwise enum struct * AOD: Add optional streamer to producer --------- Signed-off-by: Felix Schlepper --- .../include/CommonUtils/EnumBitOperators.h | 66 ++++++ Common/Utils/include/CommonUtils/TreeStream.h | 7 + .../AODProducerWorkflowSpec.h | 52 +++-- Detectors/AOD/src/AODProducerWorkflowSpec.cxx | 188 +++++++++++++++--- .../include/Framework/AnalysisDataModel.h | 57 ++++-- Framework/Core/include/Framework/DataTypes.h | 10 + 6 files changed, 324 insertions(+), 56 deletions(-) create mode 100644 Common/Utils/include/CommonUtils/EnumBitOperators.h diff --git a/Common/Utils/include/CommonUtils/EnumBitOperators.h b/Common/Utils/include/CommonUtils/EnumBitOperators.h new file mode 100644 index 0000000000000..3369a8eacf615 --- /dev/null +++ b/Common/Utils/include/CommonUtils/EnumBitOperators.h @@ -0,0 +1,66 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. +#ifndef O2_FRAMEWORK_ENUM_BIT_OPERATORS_H_ +#define O2_FRAMEWORK_ENUM_BIT_OPERATORS_H_ + +#include + +#define O2_DEFINE_ENUM_BIT_OPERATORS(enum_t) \ + constexpr auto operator|(enum_t lhs, enum_t rhs) \ + { \ + return static_cast( \ + static_cast>(lhs) | \ + static_cast>(rhs)); \ + } \ + \ + constexpr auto operator&(enum_t lhs, enum_t rhs) \ + { \ + return static_cast( \ + static_cast>(lhs) & \ + static_cast>(rhs)); \ + } \ + \ + constexpr auto operator^(enum_t lhs, enum_t rhs) \ + { \ + return static_cast( \ + static_cast>(lhs) ^ \ + static_cast>(rhs)); \ + } \ + \ + constexpr auto operator~(enum_t op) \ + { \ + return static_cast( \ + ~static_cast>(op)); \ + } \ + \ + constexpr auto& operator|=(enum_t& lhs, enum_t rhs) \ + { \ + lhs = lhs | rhs; \ + return lhs; \ + } \ + \ + constexpr auto& operator&=(enum_t& lhs, enum_t rhs) \ + { \ + lhs = lhs & rhs; \ + return lhs; \ + } \ + \ + constexpr enum_t& operator^=(enum_t& lhs, enum_t rhs) \ + { \ + lhs = lhs ^ rhs; \ + return lhs; \ + } + +#define O2_ENUM_TEST_BIT(mask, value) ((mask & value) == value) +#define O2_ENUM_SET_BIT(bit) ((1 << bit)) +#define O2_ENUM_ANY_BIT(enum) ((static_cast>(enum) != 0)) + +#endif diff --git a/Common/Utils/include/CommonUtils/TreeStream.h b/Common/Utils/include/CommonUtils/TreeStream.h index 2c55f48c98d3a..2aa02f6509d2c 100644 --- a/Common/Utils/include/CommonUtils/TreeStream.h +++ b/Common/Utils/include/CommonUtils/TreeStream.h @@ -63,6 +63,7 @@ class TreeStream const char* getName() const { return mTree.GetName(); } void setID(int id) { mID = id; } int getID() const { return mID; } + TreeStream& operator<<(const Bool_t& b) { CheckIn('B', &b); @@ -75,6 +76,12 @@ class TreeStream return *this; } + TreeStream& operator<<(const int8_t& i) + { + CheckIn('B', &i); + return *this; + } + TreeStream& operator<<(const UChar_t& c) { CheckIn('b', &c); diff --git a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h index 94f4526fe30a1..d9481917f9a05 100644 --- a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h +++ b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h @@ -30,7 +30,11 @@ #include "TStopwatch.h" #include "ZDCBase/Constants.h" #include "GlobalTracking/MatchGlobalFwd.h" +#include "CommonUtils/TreeStreamRedirector.h" +#include "CommonUtils/EnumBitOperators.h" +#include +#include #include #include #include @@ -203,7 +207,15 @@ class BunchCrossings std::vector mTimeWindows; // the time window structure covering the complete duration of mBCTimeVector double mWindowSize; // the size of a single time window -}; // end internal class +}; // end internal class + +// Steering bits for additional output during AOD production +enum struct AODProducerStreamerMask : uint8_t { + None = 0, + TrackQA = O2_ENUM_SET_BIT(0), + All = std::numeric_limits>::max(), +}; +O2_DEFINE_ENUM_BIT_OPERATORS(AODProducerStreamerMask) class AODProducerWorkflowDPL : public Task { @@ -241,6 +253,9 @@ class AODProducerWorkflowDPL : public Task std::unordered_set mGIDUsedBySVtx; std::unordered_set mGIDUsedByStr; + AODProducerStreamerMask mStreamerMask; + std::shared_ptr mStreamer; + int mNThreads = 1; bool mUseMC = true; bool mEnableSV = true; // enable secondary vertices @@ -339,6 +354,7 @@ class AODProducerWorkflowDPL : public Task uint32_t mTrackCovOffDiag = 0xFFFF0000; // 7 bits uint32_t mTrackSignal = 0xFFFFFF00; // 15 bits uint32_t mTrackTime = 0xFFFFFFFF; // use full float precision for time + uint32_t mTPCTime0 = 0xFFFFFFE0; // 18 bits, providing 14256./(1<<19) = 0.027 TB precision e.g., ~0.13 mm in z uint32_t mTrackTimeError = 0xFFFFFF00; // 15 bits uint32_t mTrackPosEMCAL = 0xFFFFFF00; // 15 bits uint32_t mTracklets = 0xFFFFFF00; // 15 bits @@ -397,18 +413,28 @@ class AODProducerWorkflowDPL : public Task struct TrackQA { GID trackID; - float tpcTime0; - int16_t tpcdcaR; - int16_t tpcdcaZ; - uint8_t tpcClusterByteMask; - uint8_t tpcdEdxMax0R; - uint8_t tpcdEdxMax1R; - uint8_t tpcdEdxMax2R; - uint8_t tpcdEdxMax3R; - uint8_t tpcdEdxTot0R; - uint8_t tpcdEdxTot1R; - uint8_t tpcdEdxTot2R; - uint8_t tpcdEdxTot3R; + float tpcTime0{}; + int16_t tpcdcaR{}; + int16_t tpcdcaZ{}; + uint8_t tpcClusterByteMask{}; + uint8_t tpcdEdxMax0R{}; + uint8_t tpcdEdxMax1R{}; + uint8_t tpcdEdxMax2R{}; + uint8_t tpcdEdxMax3R{}; + uint8_t tpcdEdxTot0R{}; + uint8_t tpcdEdxTot1R{}; + uint8_t tpcdEdxTot2R{}; + uint8_t tpcdEdxTot3R{}; + int8_t dRefContY{std::numeric_limits::min()}; + int8_t dRefContZ{std::numeric_limits::min()}; + int8_t dRefContSnp{std::numeric_limits::min()}; + int8_t dRefContTgl{std::numeric_limits::min()}; + int8_t dRefContQ2Pt{std::numeric_limits::min()}; + int8_t dRefGloY{std::numeric_limits::min()}; + int8_t dRefGloZ{std::numeric_limits::min()}; + int8_t dRefGloSnp{std::numeric_limits::min()}; + int8_t dRefGloTgl{std::numeric_limits::min()}; + int8_t dRefGloQ2Pt{std::numeric_limits::min()}; }; // helper struct for addToFwdTracksTable() diff --git a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx index 6c3a418612478..8a2443b57c7ff 100644 --- a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx +++ b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx @@ -51,6 +51,7 @@ #include "Framework/DataTypes.h" #include "Framework/TableBuilder.h" #include "Framework/CCDBParamSpec.h" +#include "CommonUtils/TreeStreamRedirector.h" #include "FT0Base/Geometry.h" #include "GlobalTracking/MatchTOF.h" #include "ReconstructionDataFormats/Cascade.h" @@ -85,8 +86,10 @@ #include "MathUtils/Utils.h" #include "Math/SMatrix.h" #include "TString.h" +#include #include #include +#include #include #include #include @@ -355,25 +358,47 @@ void AODProducerWorkflowDPL::addToTracksExtraTable(TracksExtraCursorType& tracks template void AODProducerWorkflowDPL::addToTracksQATable(TracksQACursorType& tracksQACursor, TrackQA& trackQAInfoHolder) { - - // trackQA - tracksQACursor( - - // truncateFloatFraction(trackQAInfoHolder.tpcdcaR, mTrackChi2), - // truncateFloatFraction(trackQAInfoHolder.tpcdcaZ, mTrackChi2), - trackQAInfoHolder.trackID, - trackQAInfoHolder.tpcTime0, - trackQAInfoHolder.tpcdcaR, - trackQAInfoHolder.tpcdcaZ, - trackQAInfoHolder.tpcClusterByteMask, - trackQAInfoHolder.tpcdEdxMax0R, - trackQAInfoHolder.tpcdEdxMax1R, - trackQAInfoHolder.tpcdEdxMax2R, - trackQAInfoHolder.tpcdEdxMax3R, - trackQAInfoHolder.tpcdEdxTot0R, - trackQAInfoHolder.tpcdEdxTot1R, - trackQAInfoHolder.tpcdEdxTot2R, - trackQAInfoHolder.tpcdEdxTot3R); + if constexpr (std::is_same_v) { // TODO remove remove once version changes + tracksQACursor( + trackQAInfoHolder.trackID, + truncateFloatFraction(trackQAInfoHolder.tpcTime0, mTPCTime0), + trackQAInfoHolder.tpcdcaR, + trackQAInfoHolder.tpcdcaZ, + trackQAInfoHolder.tpcClusterByteMask, + trackQAInfoHolder.tpcdEdxMax0R, + trackQAInfoHolder.tpcdEdxMax1R, + trackQAInfoHolder.tpcdEdxMax2R, + trackQAInfoHolder.tpcdEdxMax3R, + trackQAInfoHolder.tpcdEdxTot0R, + 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); + } else { + tracksQACursor( + trackQAInfoHolder.trackID, + trackQAInfoHolder.tpcTime0, + trackQAInfoHolder.tpcdcaR, + trackQAInfoHolder.tpcdcaZ, + trackQAInfoHolder.tpcClusterByteMask, + trackQAInfoHolder.tpcdEdxMax0R, + trackQAInfoHolder.tpcdEdxMax1R, + trackQAInfoHolder.tpcdEdxMax2R, + trackQAInfoHolder.tpcdEdxMax3R, + trackQAInfoHolder.tpcdEdxTot0R, + trackQAInfoHolder.tpcdEdxTot1R, + trackQAInfoHolder.tpcdEdxTot2R, + trackQAInfoHolder.tpcdEdxTot3R); + } } template @@ -1664,6 +1689,14 @@ void AODProducerWorkflowDPL::init(InitContext& ic) mThinTracks = ic.options().get("thin-tracks"); mPropTracks = ic.options().get("propagate-tracks"); mPropMuons = ic.options().get("propagate-muons"); + if (auto s = ic.options().get("with-streamers"); !s.empty()) { + mStreamerMask = static_cast(std::stoul(s, nullptr, 2)); + if (O2_ENUM_ANY_BIT(mStreamerMask)) { + LOGP(info, "Writing streamer data with mask {:0{}b}", static_cast>(mStreamerMask), std::numeric_limits>::digits); + } else { + LOGP(warn, "Specified non-default empty streamer mask!"); + } + } mTrackQCFraction = ic.options().get("trackqc-fraction"); mTrackQCNTrCut = ic.options().get("trackqc-NTrCut"); if (auto seed = ic.options().get("seed"); seed == 0) { @@ -1705,6 +1738,7 @@ void AODProducerWorkflowDPL::init(InitContext& ic) mTrackCovOffDiag = 0xFFFFFFFF; mTrackSignal = 0xFFFFFFFF; mTrackTime = 0xFFFFFFFF; + mTPCTime0 = 0xFFFFFFFF; mTrackTimeError = 0xFFFFFFFF; mTrackPosEMCAL = 0xFFFFFFFF; mTracklets = 0xFFFFFFFF; @@ -1748,6 +1782,10 @@ void AODProducerWorkflowDPL::init(InitContext& ic) mHeavyIonUpdate = when; mTimer.Reset(); + + if (O2_ENUM_ANY_BIT(mStreamerMask)) { + mStreamer = std::make_unique("AO2DStreamer.root", "RECREATE"); + } } void AODProducerWorkflowDPL::run(ProcessingContext& pc) @@ -1816,7 +1854,7 @@ void AODProducerWorkflowDPL::run(ProcessingContext& pc) auto tracksCursor = createTableCursor(pc); auto tracksCovCursor = createTableCursor(pc); auto tracksExtraCursor = createTableCursor(pc); - auto tracksQACursor = createTableCursor(pc); + auto tracksQACursor = createTableCursor(pc); auto ambigTracksCursor = createTableCursor(pc); auto ambigMFTTracksCursor = createTableCursor(pc); auto ambigFwdTracksCursor = createTableCursor(pc); @@ -2534,16 +2572,15 @@ AODProducerWorkflowDPL::TrackQA AODProducerWorkflowDPL::processBarrelTrackQA(int TrackQA trackQAHolder; auto contributorsGID = data.getTPCContributorGID(trackIndex); const auto& trackPar = data.getTrackParam(trackIndex); - // auto src = trackIndex.getSource(); if (contributorsGID.isIndexSet()) { + auto prop = o2::base::Propagator::Instance(); const auto& tpcOrig = data.getTPCTrack(contributorsGID); /// getDCA - should be done with the copy of TPC only track - // LOGP(info, "GloIdx: {} TPCIdx: {}, NTPCTracks: {}", trackIndex.asString(), contributorsGID.asString(), data.getTPCTracks().size()); - o2::track::TrackParametrization tpcTMP = tpcOrig; /// get backup of the track - o2::base::Propagator::MatCorrType mMatType = o2::base::Propagator::MatCorrType::USEMatCorrLUT; /// should be parameterized - o2::dataformats::VertexBase v = mVtx.getMeanVertex(collisionID < 0 ? 0.f : data.getPrimaryVertex(collisionID).getZ()); + o2::track::TrackParametrization tpcTMP = tpcOrig; /// get backup of the track + const o2::base::Propagator::MatCorrType mMatType = o2::base::Propagator::MatCorrType::USEMatCorrLUT; /// should be parameterized + const o2::dataformats::VertexBase v = mVtx.getMeanVertex(collisionID < 0 ? 0.f : data.getPrimaryVertex(collisionID).getZ()); o2::gpu::gpustd::array dcaInfo{-999., -999.}; - if (o2::base::Propagator::Instance()->propagateToDCABxByBz({v.getX(), v.getY(), v.getZ()}, tpcTMP, 2.f, mMatType, &dcaInfo)) { + if (prop->propagateToDCABxByBz({v.getX(), v.getY(), v.getZ()}, tpcTMP, 2.f, mMatType, &dcaInfo)) { trackQAHolder.tpcdcaR = 100. * dcaInfo[0] / sqrt(1. + trackPar.getQ2Pt() * trackPar.getQ2Pt()); trackQAHolder.tpcdcaZ = 100. * dcaInfo[1] / sqrt(1. + trackPar.getQ2Pt() * trackPar.getQ2Pt()); } @@ -2567,7 +2604,7 @@ AODProducerWorkflowDPL::TrackQA AODProducerWorkflowDPL::processBarrelTrackQA(int } trackQAHolder.tpcTime0 = tpcOrig.getTime0(); trackQAHolder.tpcClusterByteMask = byteMask; - float dEdxNorm = (tpcOrig.getdEdx().dEdxTotTPC > 0) ? 100. / tpcOrig.getdEdx().dEdxTotTPC : 0; + const float dEdxNorm = (tpcOrig.getdEdx().dEdxTotTPC > 0) ? 100. / tpcOrig.getdEdx().dEdxTotTPC : 0; trackQAHolder.tpcdEdxMax0R = uint8_t(tpcOrig.getdEdx().dEdxMaxIROC * dEdxNorm); trackQAHolder.tpcdEdxMax1R = uint8_t(tpcOrig.getdEdx().dEdxMaxOROC1 * dEdxNorm); trackQAHolder.tpcdEdxMax2R = uint8_t(tpcOrig.getdEdx().dEdxMaxOROC2 * dEdxNorm); @@ -2577,7 +2614,99 @@ AODProducerWorkflowDPL::TrackQA AODProducerWorkflowDPL::processBarrelTrackQA(int trackQAHolder.tpcdEdxTot1R = uint8_t(tpcOrig.getdEdx().dEdxTotOROC1 * dEdxNorm); trackQAHolder.tpcdEdxTot2R = uint8_t(tpcOrig.getdEdx().dEdxTotOROC2 * dEdxNorm); trackQAHolder.tpcdEdxTot3R = uint8_t(tpcOrig.getdEdx().dEdxTotOROC3 * dEdxNorm); - /// + + if constexpr (std::is_same_v) { // TODO remove remove once version changes + // Add matching information at a reference point (defined by + // o2::aod::track::trackQARefRadius) in the same frame as the global track + // without material corrections and error propagation + if (auto itsContGID = data.getITSContributorGID(trackIndex); itsContGID.isIndexSet() && itsContGID.getSource() != GIndex::ITSAB) { + const auto& itsOrig = data.getITSTrack(itsContGID); + o2::track::TrackPar gloCopy = trackPar; + o2::track::TrackPar itsCopy = itsOrig; + o2::track::TrackPar tpcCopy = tpcOrig; + if (prop->propagateToX(gloCopy, o2::aod::track::trackQARefRadius, prop->getNominalBz(), o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, mMatCorr) && + prop->propagateToAlphaX(tpcCopy, gloCopy.getAlpha(), o2::aod::track::trackQARefRadius, false, o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, 1, mMatCorr) && + prop->propagateToAlphaX(itsCopy, gloCopy.getAlpha(), o2::aod::track::trackQARefRadius, false, o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, 1, mMatCorr)) { + // All tracks are now at the same radius and in the same frame and we can calculate the deltas wrt. to the global track + // The scale is defined by the global track scaling depending on beta0 + const float beta0 = std::sqrt(std::min(50.f / tpcOrig.getdEdx().dEdxMaxTPC, 1.f)); + const float qpt = gloCopy.getQ2Pt(); + const float x = qpt / beta0; + // scaling is defined as sigmaBins/sqrt(p0^2 + (p1 * q/pt / beta)^2) + auto scaleCont = [&x](int i) -> float { + return o2::aod::track::trackQAScaleBins / std::sqrt(o2::aod::track::trackQAScaleContP0[i] * o2::aod::track::trackQAScaleContP0[i] + (o2::aod::track::trackQAScaleContP1[i] * x) * (o2::aod::track::trackQAScaleContP1[i] * x)); + }; + auto scaleGlo = [&x](int i) -> float { + return o2::aod::track::trackQAScaleBins / std::sqrt(o2::aod::track::trackQAScaleGloP0[i] * o2::aod::track::trackQAScaleGloP0[i] + (o2::aod::track::trackQAScaleGloP1[i] * x) * (o2::aod::track::trackQAScaleGloP1[i] * x)); + }; + + // This allows to safely clamp any float to one byte, using the + // minmal/maximum values as under-/overflow borders and rounding to the nearest integer + auto safeInt8Clamp = [](auto value) -> int8_t { + using ValType = decltype(value); + return static_cast(TMath::Nint(std::clamp(value, static_cast(std::numeric_limits::min()), static_cast(std::numeric_limits::max())))); + }; + + // Calculate deltas for contributors + trackQAHolder.dRefContY = safeInt8Clamp((itsCopy.getY() - tpcCopy.getY()) * scaleCont(0)); + trackQAHolder.dRefContZ = safeInt8Clamp((itsCopy.getZ() - tpcCopy.getZ()) * scaleCont(1)); + trackQAHolder.dRefContSnp = safeInt8Clamp((itsCopy.getSnp() - tpcCopy.getSnp()) * scaleCont(2)); + trackQAHolder.dRefContTgl = safeInt8Clamp((itsCopy.getTgl() - tpcCopy.getTgl()) * scaleCont(3)); + trackQAHolder.dRefContQ2Pt = safeInt8Clamp((itsCopy.getQ2Pt() - tpcCopy.getQ2Pt()) * scaleCont(4)); + // Calculate deltas for global track against averaged contributors + trackQAHolder.dRefGloY = safeInt8Clamp(((itsCopy.getY() + tpcCopy.getY()) * 0.5f - gloCopy.getY()) * scaleGlo(0)); + trackQAHolder.dRefGloZ = safeInt8Clamp(((itsCopy.getZ() + tpcCopy.getZ()) * 0.5f - gloCopy.getZ()) * scaleGlo(1)); + trackQAHolder.dRefGloSnp = safeInt8Clamp(((itsCopy.getSnp() + tpcCopy.getSnp()) * 0.5f - gloCopy.getSnp()) * scaleGlo(2)); + trackQAHolder.dRefGloTgl = safeInt8Clamp(((itsCopy.getTgl() + tpcCopy.getTgl()) * 0.5f - gloCopy.getTgl()) * scaleGlo(3)); + trackQAHolder.dRefGloQ2Pt = safeInt8Clamp(((itsCopy.getQ2Pt() + tpcCopy.getQ2Pt()) * 0.5f - gloCopy.getQ2Pt()) * scaleGlo(4)); + + if (O2_ENUM_TEST_BIT(mStreamerMask, AODProducerStreamerMask::TrackQA)) { + (*mStreamer) << "trackQA" + << "trackITSOrig=" << itsOrig + << "trackTPCOrig=" << tpcOrig + << "trackITSTPCOrig=" << trackPar + << "trackITSProp=" << itsCopy + << "trackTPCProp=" << tpcCopy + << "trackITSTPCProp=" << gloCopy + << "refRadius=" << o2::aod::track::trackQARefRadius + << "scaleBins=" << o2::aod::track::trackQAScaleBins + << "scaleCont0=" << scaleCont(0) + << "scaleCont1=" << scaleCont(1) + << "scaleCont2=" << scaleCont(2) + << "scaleCont3=" << scaleCont(3) + << "scaleCont4=" << scaleCont(4) + << "scaleGlo0=" << scaleGlo(0) + << "scaleGlo1=" << scaleGlo(1) + << "scaleGlo2=" << scaleGlo(2) + << "scaleGlo3=" << scaleGlo(3) + << "scaleGlo4=" << scaleGlo(4) + << "trackQAHolder.tpcTime0=" << trackQAHolder.tpcTime0 + << "trackQAHolder.tpcdcaR=" << trackQAHolder.tpcdcaR + << "trackQAHolder.tpcdcaZ=" << trackQAHolder.tpcdcaZ + << "trackQAHolder.tpcdcaClusterByteMask=" << trackQAHolder.tpcClusterByteMask + << "trackQAHolder.tpcdEdxMax0R=" << trackQAHolder.tpcdEdxMax0R + << "trackQAHolder.tpcdEdxMax1R=" << trackQAHolder.tpcdEdxMax1R + << "trackQAHolder.tpcdEdxMax2R=" << trackQAHolder.tpcdEdxMax2R + << "trackQAHolder.tpcdEdxMax3R=" << trackQAHolder.tpcdEdxMax3R + << "trackQAHolder.tpcdEdxTot0R=" << trackQAHolder.tpcdEdxTot0R + << "trackQAHolder.tpcdEdxTot1R=" << trackQAHolder.tpcdEdxTot1R + << "trackQAHolder.tpcdEdxTot2R=" << trackQAHolder.tpcdEdxTot2R + << "trackQAHolder.tpcdEdxTot3R=" << trackQAHolder.tpcdEdxTot3R + << "trackQAHolder.dRefContY=" << trackQAHolder.dRefContY + << "trackQAHolder.dRefContZ=" << trackQAHolder.dRefContZ + << "trackQAHolder.dRefContSnp=" << trackQAHolder.dRefContSnp + << "trackQAHolder.dRefContTgl=" << trackQAHolder.dRefContTgl + << "trackQAHolder.dRefContQ2Pt=" << trackQAHolder.dRefContQ2Pt + << "trackQAHolder.dRefGloY=" << trackQAHolder.dRefGloY + << "trackQAHolder.dRefGloZ=" << trackQAHolder.dRefGloZ + << "trackQAHolder.dRefGloSnp=" << trackQAHolder.dRefGloSnp + << "trackQAHolder.dRefGloTgl=" << trackQAHolder.dRefGloTgl + << "trackQAHolder.dRefGloQ2Pt=" << trackQAHolder.dRefGloQ2Pt + << "\n"; + } + } + } + } } return trackQAHolder; @@ -2944,6 +3073,8 @@ void AODProducerWorkflowDPL::endOfStream(EndOfStreamContext& /*ec*/) { LOGF(info, "aod producer dpl total timing: Cpu: %.3e Real: %.3e s in %d slots", mTimer.CpuTime(), mTimer.RealTime(), mTimer.Counter() - 1); + + mStreamer.reset(); } DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, bool enableStrangenessTracking, bool useMC, bool CTPConfigPerRun) @@ -3076,6 +3207,7 @@ DataProcessorSpec getAODProducerWorkflowSpec(GID::mask_t src, bool enableSV, boo ConfigParamSpec{"thin-tracks", VariantType::Bool, false, {"Produce thinned track tables"}}, 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{"with-streamers", VariantType::String, "", {"Bit-mask to steer writing of intermediate streamer files"}}, ConfigParamSpec{"seed", VariantType::Int, 0, {"Set seed for random generator used for sampling (0 (default) means using a random_device)"}}, }}; } diff --git a/Framework/Core/include/Framework/AnalysisDataModel.h b/Framework/Core/include/Framework/AnalysisDataModel.h index c90e46bf6da06..e277925ed5603 100644 --- a/Framework/Core/include/Framework/AnalysisDataModel.h +++ b/Framework/Core/include/Framework/AnalysisDataModel.h @@ -15,6 +15,7 @@ #include #include +#include #include #include // std::move @@ -667,28 +668,54 @@ using FullTrack = FullTracks::iterator; namespace trackqa { // TRACKQA TABLE COLUMNS -DECLARE_SOA_INDEX_COLUMN(Track, track); //! track to which this QA information belongs -DECLARE_SOA_COLUMN(TPCTime0, tpcTime0, float); //! tpc only time0 (mTime0 in TPC track) -DECLARE_SOA_COLUMN(TPCDCAR, tpcdcaR, int16_t); //! tpc only DCAr -DECLARE_SOA_COLUMN(TPCDCAZ, tpcdcaZ, int16_t); //! tpc only DCAz -DECLARE_SOA_COLUMN(TPCClusterByteMask, tpcClusterByteMask, uint8_t); //! tracklet bitmask - track defining 8 tracklets (152=8*19 rows) bit set if nCluster>thr (default 5) -DECLARE_SOA_COLUMN(TPCdEdxMax0R, tpcdEdxMax0R, uint8_t); //! TPC dEdxQMax -ROC0/dEdx -DECLARE_SOA_COLUMN(TPCdEdxMax1R, tpcdEdxMax1R, uint8_t); //! TPC dEdxQMax -ROC1/dEdx -DECLARE_SOA_COLUMN(TPCdEdxMax2R, tpcdEdxMax2R, uint8_t); //! TPC dEdxQMax -ROC2/dEdx -DECLARE_SOA_COLUMN(TPCdEdxMax3R, tpcdEdxMax3R, uint8_t); //! TPC dEdxQMax -ROC3/dEdx -DECLARE_SOA_COLUMN(TPCdEdxTot0R, tpcdEdxTot0R, uint8_t); //! TPC dEdxQtot -ROC0/dEdx -DECLARE_SOA_COLUMN(TPCdEdxTot1R, tpcdEdxTot1R, uint8_t); //! TPC dEdxQtot -ROC1/dEdx -DECLARE_SOA_COLUMN(TPCdEdxTot2R, tpcdEdxTot2R, uint8_t); //! TPC dEdxQtot -ROC2/dEdx -DECLARE_SOA_COLUMN(TPCdEdxTot3R, tpcdEdxTot3R, uint8_t); //! TPC dEdxQtot -ROC3/dEdx +DECLARE_SOA_INDEX_COLUMN(Track, track); //! track to which this QA information belongs +DECLARE_SOA_COLUMN(TPCTime0, tpcTime0, float); //! tpc only time0 (mTime0 in TPC track) +DECLARE_SOA_COLUMN(TPCDCAR, tpcdcaR, int16_t); //! tpc only DCAr +DECLARE_SOA_COLUMN(TPCDCAZ, tpcdcaZ, int16_t); //! tpc only DCAz +DECLARE_SOA_COLUMN(TPCClusterByteMask, tpcClusterByteMask, uint8_t); //! tracklet bitmask - track defining 8 tracklets (152=8*19 rows) bit set if nCluster>thr (default 5) +DECLARE_SOA_COLUMN(TPCdEdxMax0R, tpcdEdxMax0R, uint8_t); //! TPC dEdxQMax -ROC0/dEdx +DECLARE_SOA_COLUMN(TPCdEdxMax1R, tpcdEdxMax1R, uint8_t); //! TPC dEdxQMax -ROC1/dEdx +DECLARE_SOA_COLUMN(TPCdEdxMax2R, tpcdEdxMax2R, uint8_t); //! TPC dEdxQMax -ROC2/dEdx +DECLARE_SOA_COLUMN(TPCdEdxMax3R, tpcdEdxMax3R, uint8_t); //! TPC dEdxQMax -ROC3/dEdx +DECLARE_SOA_COLUMN(TPCdEdxTot0R, tpcdEdxTot0R, uint8_t); //! TPC dEdxQtot -ROC0/dEdx +DECLARE_SOA_COLUMN(TPCdEdxTot1R, tpcdEdxTot1R, uint8_t); //! TPC dEdxQtot -ROC1/dEdx +DECLARE_SOA_COLUMN(TPCdEdxTot2R, tpcdEdxTot2R, uint8_t); //! TPC dEdxQtot -ROC2/dEdx +DECLARE_SOA_COLUMN(TPCdEdxTot3R, tpcdEdxTot3R, uint8_t); //! TPC dEdxQtot -ROC3/dEdx +DECLARE_SOA_COLUMN(DeltaRefContParamY, deltaRefContParamY, int8_t); //! Normalized delta of contributor tracks at reference point in the same frame Y +DECLARE_SOA_COLUMN(DeltaRefContParamZ, deltaRefITSParamZ, int8_t); //! Normalized delta of contributor tracks at reference point in the same frame Z +DECLARE_SOA_COLUMN(DeltaRefContParamSnp, deltaRefContParamSnp, int8_t); //! Normalized delta of contributor tracks at reference point in the same frame Snp +DECLARE_SOA_COLUMN(DeltaRefContParamTgl, deltaRefContParamTgl, int8_t); //! Normalized delta of contributor tracks at reference point in the same frame Tgl +DECLARE_SOA_COLUMN(DeltaRefContParamQ2Pt, deltaRefContParamQ2Pt, int8_t); //! Normalized delta of contributor tracks at reference point in the same frame Q2Pt +DECLARE_SOA_COLUMN(DeltaRefGloParamY, deltaRefGloParamY, int8_t); //! Normalized delta of global track to average contributors matched tracks at reference point in the same frame Y +DECLARE_SOA_COLUMN(DeltaRefGloParamZ, deltaRefGloParamZ, int8_t); //! Normalized delta of global track to average contributors matched tracks at reference point in the same frame Z +DECLARE_SOA_COLUMN(DeltaRefGloParamSnp, deltaRefGloParamSnp, int8_t); //! Normalized delta of global track to average contributors matched tracks at reference point in the same frame Snp +DECLARE_SOA_COLUMN(DeltaRefGloParamTgl, deltaRefGloParamTgl, int8_t); //! Normalized delta of global track to average contributors matched tracks at reference point in the same frame Tgl +DECLARE_SOA_COLUMN(DeltaRefGloParamQ2Pt, deltaRefGloParamQ2Pt, int8_t); //! Normalized delta of global track to average contributors matched tracks at reference point in the same frame Q2Pt + +DECLARE_SOA_DYNAMIC_COLUMN(IsDummy, isDummy, //! indicates if the propagation of the contrib. tracks was successful and residuals are available + [](int8_t cY, int8_t cZ, int8_t cSnp, int8_t cTgl, int8_t cQ2Pt, int8_t gY, int8_t gZ, int8_t gSnp, int8_t gTgl, int8_t gQ2Pt) -> bool { + constexpr int8_t m = std::numeric_limits::min(); + return (cY == m && cZ == m && cSnp == m && cTgl == m && cQ2Pt == m && gY == m && gZ == m && gSnp == m && gTgl == m && gQ2Pt == m); + }); } // namespace trackqa -DECLARE_SOA_TABLE(TracksQA, "AOD", "TRACKQA", //! trackQA information - sampled QA information currently for the TPC +DECLARE_SOA_TABLE(TracksQA_000, "AOD", "TRACKQA", //! trackQA information - sampled QA information currently for the TPC - version 0 o2::soa::Index<>, trackqa::TrackId, trackqa::TPCTime0, trackqa::TPCDCAR, trackqa::TPCDCAZ, trackqa::TPCClusterByteMask, trackqa::TPCdEdxMax0R, trackqa::TPCdEdxMax1R, trackqa::TPCdEdxMax2R, trackqa::TPCdEdxMax3R, trackqa::TPCdEdxTot0R, trackqa::TPCdEdxTot1R, trackqa::TPCdEdxTot2R, trackqa::TPCdEdxTot3R); // o2::soa::Index<>, trackqa::TrackId, trackqa::TPCDCAR, trackqa::TPCDCAZ, trackqa::TPCClusterByteMask, -using TrackQA = TracksQA::iterator; +DECLARE_SOA_TABLE_VERSIONED(TracksQA_001, "AOD", "TRACKQA", 1, //! trackQA information - version 1 - including contributor residuals of matched tracks at reference radius + o2::soa::Index<>, trackqa::TrackId, trackqa::TPCTime0, trackqa::TPCDCAR, trackqa::TPCDCAZ, trackqa::TPCClusterByteMask, + trackqa::TPCdEdxMax0R, trackqa::TPCdEdxMax1R, trackqa::TPCdEdxMax2R, trackqa::TPCdEdxMax3R, + trackqa::TPCdEdxTot0R, trackqa::TPCdEdxTot1R, trackqa::TPCdEdxTot2R, trackqa::TPCdEdxTot3R, + trackqa::DeltaRefContParamY, trackqa::DeltaRefContParamZ, trackqa::DeltaRefContParamSnp, trackqa::DeltaRefContParamTgl, trackqa::DeltaRefContParamQ2Pt, + trackqa::DeltaRefGloParamY, trackqa::DeltaRefGloParamZ, trackqa::DeltaRefGloParamSnp, trackqa::DeltaRefGloParamTgl, trackqa::DeltaRefGloParamQ2Pt, + trackqa::IsDummy); + +using TracksQAVersion = TracksQA_000; +using TracksQA = TracksQAVersion::iterator; namespace fwdtrack { diff --git a/Framework/Core/include/Framework/DataTypes.h b/Framework/Core/include/Framework/DataTypes.h index 92af1f79e2314..9d829159718d8 100644 --- a/Framework/Core/include/Framework/DataTypes.h +++ b/Framework/Core/include/Framework/DataTypes.h @@ -15,6 +15,7 @@ #include #include +#include namespace o2::aod::bc { @@ -120,6 +121,15 @@ struct TPCTimeErrEncoding { } }; } // namespace extensions + +// Reference radius for extrapolated tracks +constexpr float trackQARefRadius{50.f}; +constexpr float trackQAScaleBins{5.f}; +// Fit parameters for scale dY, dZ, dSnp, dTgl, dQ2Pt +constexpr std::array trackQAScaleContP0{0.257192, 0.0775375, 0.00424283, 0.00107201, 0.0335447}; +constexpr std::array trackQAScaleContP1{0.189371, 0.409071, 0.00694444, 0.00720038, 0.0806902}; +constexpr std::array trackQAScaleGloP0{0.130985, 0.0775375, 0.00194703, 0.000405458, 0.0160007}; +constexpr std::array trackQAScaleGloP1{0.183731, 0.409071, 0.00621802, 0.00624881, 0.0418957}; } // namespace o2::aod::track namespace o2::aod::fwdtrack From 950b8b700fbd47743cd151895eaf7102e2b3dd67 Mon Sep 17 00:00:00 2001 From: Giulio Eulisse <10544+ktf@users.noreply.github.com> Date: Mon, 2 Dec 2024 15:45:43 +0100 Subject: [PATCH 0124/2180] DPL Analysis: improve arrow::Dataset support for TTree (#13759) --- Framework/Core/src/RootArrowFilesystem.cxx | 7 +- Framework/Core/test/test_Root2ArrowTable.cxx | 120 ++++++++----------- 2 files changed, 54 insertions(+), 73 deletions(-) diff --git a/Framework/Core/src/RootArrowFilesystem.cxx b/Framework/Core/src/RootArrowFilesystem.cxx index 7e331814272a6..5f2d21d942d37 100644 --- a/Framework/Core/src/RootArrowFilesystem.cxx +++ b/Framework/Core/src/RootArrowFilesystem.cxx @@ -13,6 +13,7 @@ #include "Framework/RuntimeError.h" #include "Framework/Signpost.h" #include +#include #include #include #include @@ -427,7 +428,7 @@ class TTreeFileWriter : public arrow::dataset::FileWriter O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, sid, "finaliseBasketSize", "Branch %s exists and uses %d bytes per entry.", branch->GetName(), valueSize); // This should probably lookup the - auto column = firstBatch->GetColumnByName(branch->GetName()); + auto column = firstBatch->GetColumnByName(schema_->field(i)->name()); auto list = std::static_pointer_cast(column); O2_SIGNPOST_EVENT_EMIT(root_arrow_fs, sid, "finaliseBasketSize", "Branch %s needed. Associated size branch %s and there are %lli entries of size %d in that list.", branch->GetName(), sizeBranch->GetName(), list->length(), valueSize); @@ -497,8 +498,8 @@ class TTreeFileWriter : public arrow::dataset::FileWriter } break; case arrow::Type::LIST: { valueTypes.push_back(field->type()->field(0)->type()); - listSizes.back() = 0; // VLA, we need to calculate it on the fly; std::string leafList = fmt::format("{}[{}_size]{}", field->name(), field->name(), rootSuffixFromArrow(valueTypes.back()->id())); + listSizes.back() = -1; // VLA, we need to calculate it on the fly; std::string sizeLeafList = field->name() + "_size/I"; sizesBranches.push_back(treeStream->CreateBranch((field->name() + "_size").c_str(), sizeLeafList.c_str())); branches.push_back(treeStream->CreateBranch(field->name().c_str(), leafList.c_str())); @@ -765,7 +766,7 @@ arrow::Result TTreeFileFormat::ScanBatchesAsync( typeSize = fixedSizeList->field(0)->type()->byte_width(); } else if (auto vlaListType = std::dynamic_pointer_cast(physicalField->type())) { listSize = -1; - typeSize = fixedSizeList->field(0)->type()->byte_width(); + typeSize = vlaListType->field(0)->type()->byte_width(); } if (listSize == -1) { mSizeBranch = branch->GetTree()->GetBranch((std::string{branch->GetName()} + "_size").c_str()); diff --git a/Framework/Core/test/test_Root2ArrowTable.cxx b/Framework/Core/test/test_Root2ArrowTable.cxx index a659d488ae24a..2b0ab9154250c 100644 --- a/Framework/Core/test/test_Root2ArrowTable.cxx +++ b/Framework/Core/test/test_Root2ArrowTable.cxx @@ -322,12 +322,26 @@ bool validateContents(std::shared_ptr batch) REQUIRE(bool_array->Value(1) == (i % 5 == 0)); } } + + { + auto list_array = std::static_pointer_cast(batch->GetColumnByName("vla")); + + REQUIRE(list_array->length() == 100); + for (int64_t i = 0; i < list_array->length(); i++) { + auto value_slice = list_array->value_slice(i); + REQUIRE(value_slice->length() == (i % 10)); + auto int_array = std::static_pointer_cast(value_slice); + for (size_t j = 0; j < value_slice->length(); j++) { + REQUIRE(int_array->Value(j) == j); + } + } + } return true; } bool validateSchema(std::shared_ptr schema) { - REQUIRE(schema->num_fields() == 9); + REQUIRE(schema->num_fields() == 10); REQUIRE(schema->field(0)->type()->id() == arrow::float32()->id()); REQUIRE(schema->field(1)->type()->id() == arrow::float32()->id()); REQUIRE(schema->field(2)->type()->id() == arrow::float32()->id()); @@ -337,6 +351,7 @@ bool validateSchema(std::shared_ptr schema) REQUIRE(schema->field(6)->type()->id() == arrow::fixed_size_list(arrow::int32(), 2)->id()); REQUIRE(schema->field(7)->type()->id() == arrow::boolean()->id()); REQUIRE(schema->field(8)->type()->id() == arrow::fixed_size_list(arrow::boolean(), 2)->id()); + REQUIRE(schema->field(9)->type()->id() == arrow::list(arrow::int32())->id()); return true; } @@ -390,6 +405,8 @@ TEST_CASE("RootTree2Dataset") Int_t ev; bool oneBool; bool manyBool[2]; + int vla[10] = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9}; + int vlaSize = 0; t->Branch("px", &px, "px/F"); t->Branch("py", &py, "py/F"); @@ -400,6 +417,8 @@ TEST_CASE("RootTree2Dataset") t->Branch("ij", ij, "ij[2]/I"); t->Branch("bools", &oneBool, "bools/O"); t->Branch("manyBools", &manyBool, "manyBools[2]/O"); + t->Branch("vla_size", &vlaSize, "vla_size/I"); + t->Branch("vla", vla, "vla[vla_size]/I"); // fill the tree for (Int_t i = 0; i < 100; i++) { xyz[0] = 1; @@ -415,9 +434,11 @@ TEST_CASE("RootTree2Dataset") oneBool = (i % 3 == 0); manyBool[0] = (i % 4 == 0); manyBool[1] = (i % 5 == 0); + vlaSize = i % 10; t->Fill(); } } + f->Write(); size_t totalSizeCompressed = 0; size_t totalSizeUncompressed = 0; @@ -428,16 +449,7 @@ TEST_CASE("RootTree2Dataset") auto schemaOpt = format->Inspect(source); REQUIRE(schemaOpt.ok()); auto schema = *schemaOpt; - REQUIRE(schema->num_fields() == 9); - REQUIRE(schema->field(0)->type()->id() == arrow::float32()->id()); - REQUIRE(schema->field(1)->type()->id() == arrow::float32()->id()); - REQUIRE(schema->field(2)->type()->id() == arrow::float32()->id()); - REQUIRE(schema->field(3)->type()->id() == arrow::float64()->id()); - REQUIRE(schema->field(4)->type()->id() == arrow::int32()->id()); - REQUIRE(schema->field(5)->type()->id() == arrow::fixed_size_list(arrow::float32(), 3)->id()); - REQUIRE(schema->field(6)->type()->id() == arrow::fixed_size_list(arrow::int32(), 2)->id()); - REQUIRE(schema->field(7)->type()->id() == arrow::boolean()->id()); - REQUIRE(schema->field(8)->type()->id() == arrow::fixed_size_list(arrow::boolean(), 2)->id()); + validateSchema(schema); auto fragment = format->MakeFragment(source, {}, schema); REQUIRE(fragment.ok()); @@ -448,41 +460,9 @@ TEST_CASE("RootTree2Dataset") auto batches = (*scanner)(); auto result = batches.result(); REQUIRE(result.ok()); - REQUIRE((*result)->columns().size() == 9); + REQUIRE((*result)->columns().size() == 10); REQUIRE((*result)->num_rows() == 100); - - { - auto int_array = std::static_pointer_cast((*result)->GetColumnByName("ev")); - for (int64_t j = 0; j < int_array->length(); j++) { - REQUIRE(int_array->Value(j) == j + 1); - } - } - - { - auto list_array = std::static_pointer_cast((*result)->GetColumnByName("xyz")); - - // Iterate over the FixedSizeListArray - for (int64_t i = 0; i < list_array->length(); i++) { - auto value_slice = list_array->value_slice(i); - auto float_array = std::static_pointer_cast(value_slice); - - REQUIRE(float_array->Value(0) == 1); - REQUIRE(float_array->Value(1) == 2); - REQUIRE(float_array->Value(2) == i + 1); - } - } - - { - auto list_array = std::static_pointer_cast((*result)->GetColumnByName("ij")); - - // Iterate over the FixedSizeListArray - for (int64_t i = 0; i < list_array->length(); i++) { - auto value_slice = list_array->value_slice(i); - auto int_array = std::static_pointer_cast(value_slice); - REQUIRE(int_array->Value(0) == i); - REQUIRE(int_array->Value(1) == i + 1); - } - } + validateContents(*result); auto* output = new TMemFile("foo", "RECREATE"); auto outFs = std::make_shared(output, 0); @@ -497,31 +477,31 @@ TEST_CASE("RootTree2Dataset") auto success = writer->get()->Write(*result); auto rootDestination = std::dynamic_pointer_cast(*destination); - REQUIRE(success.ok()); - // Let's read it back... - arrow::dataset::FileSource source2("/DF_3", outFs); - auto newTreeFS = outFs->GetSubFilesystem(source2); - - REQUIRE(format->IsSupported(source) == true); - - auto schemaOptWritten = format->Inspect(source); - REQUIRE(schemaOptWritten.ok()); - auto schemaWritten = *schemaOptWritten; - REQUIRE(validateSchema(schemaWritten)); - - auto fragmentWritten = format->MakeFragment(source, {}, schema); - REQUIRE(fragmentWritten.ok()); - auto optionsWritten = std::make_shared(); - options->dataset_schema = schemaWritten; - auto scannerWritten = format->ScanBatchesAsync(optionsWritten, *fragment); - REQUIRE(scannerWritten.ok()); - auto batchesWritten = (*scanner)(); - auto resultWritten = batches.result(); - REQUIRE(resultWritten.ok()); - REQUIRE((*resultWritten)->columns().size() == 9); - REQUIRE((*resultWritten)->num_rows() == 100); - validateContents(*resultWritten); - + SECTION("Read tree") { + REQUIRE(success.ok()); + // Let's read it back... + arrow::dataset::FileSource source2("/DF_3", outFs); + auto newTreeFS = outFs->GetSubFilesystem(source2); + + REQUIRE(format->IsSupported(source) == true); + + auto schemaOptWritten = format->Inspect(source); + REQUIRE(schemaOptWritten.ok()); + auto schemaWritten = *schemaOptWritten; + REQUIRE(validateSchema(schemaWritten)); + + auto fragmentWritten = format->MakeFragment(source, {}, schema); + REQUIRE(fragmentWritten.ok()); + auto optionsWritten = std::make_shared(); + options->dataset_schema = schemaWritten; + auto scannerWritten = format->ScanBatchesAsync(optionsWritten, *fragment); + REQUIRE(scannerWritten.ok()); + auto batchesWritten = (*scanner)(); + auto resultWritten = batches.result(); + REQUIRE(resultWritten.ok()); + REQUIRE((*resultWritten)->columns().size() == 10); + REQUIRE((*resultWritten)->num_rows() == 100); + validateContents(*resultWritten); } } From 4bffbfaa980608a077c39528c079c6c2635ad425 Mon Sep 17 00:00:00 2001 From: shahoian Date: Mon, 2 Dec 2024 18:15:20 +0100 Subject: [PATCH 0125/2180] Optionally select CTFs in timestamps or orbits range New option --run-time-span-file allows to push to DPL only those TFs which overlap with the (separators can be any whitespace, comma or semicolon) records provided via text file (assuming that there are some entries for a given run, otherwise the option is ignored). Multiple ranges per run and multiple runs can be mentioned in a single input file. The range limits can be indicated either as a UNIX timestamp in ms or as an orbit number (in the fill the run belongs to). In case an option --invert-irframe-selection is provided, the selections above are inverted: TFs matching some of the provided ranges will be discarded, while the rest will be pushed to the DPL At the end of the processing the ctf-writer will create a local file ctf_read_ntf.txt containing only the number of TFs pushed to the DPL. In case no TF passed the selections above, this file will contain 0. --- .../include/CommonUtils/IRFrameSelector.h | 2 + Common/Utils/src/IRFrameSelector.cxx | 12 ++ Detectors/CTF/README.md | 48 ++++-- .../include/CTFWorkflow/CTFReaderSpec.h | 2 + Detectors/CTF/workflow/src/CTFReaderSpec.cxx | 157 ++++++++++++++++-- .../CTF/workflow/src/ctf-reader-workflow.cxx | 10 ++ 6 files changed, 208 insertions(+), 23 deletions(-) diff --git a/Common/Utils/include/CommonUtils/IRFrameSelector.h b/Common/Utils/include/CommonUtils/IRFrameSelector.h index 6312ae8314c3a..a4365030b6a12 100644 --- a/Common/Utils/include/CommonUtils/IRFrameSelector.h +++ b/Common/Utils/include/CommonUtils/IRFrameSelector.h @@ -46,6 +46,8 @@ class IRFrameSelector auto getIRFrames() const { return mFrames; } bool isSet() const { return mIsSet; } + void setOwnList(const std::vector& lst, bool toBeSorted); + private: gsl::span mFrames{}; // externally provided span of IRFrames, must be sorted in IRFrame.getMin() o2::dataformats::IRFrame mLastIRFrameChecked{}; // last frame which was checked diff --git a/Common/Utils/src/IRFrameSelector.cxx b/Common/Utils/src/IRFrameSelector.cxx index 8122484659f45..abc0ee1ee6ce3 100644 --- a/Common/Utils/src/IRFrameSelector.cxx +++ b/Common/Utils/src/IRFrameSelector.cxx @@ -167,6 +167,16 @@ size_t IRFrameSelector::loadIRFrames(const std::string& fname) return mOwnList.size(); } +void IRFrameSelector::setOwnList(const std::vector& lst, bool toBeSorted) +{ + clear(); + mOwnList.insert(mOwnList.end(), lst.begin(), lst.end()); + if (toBeSorted) { + std::sort(mOwnList.begin(), mOwnList.end(), [](const auto& a, const auto& b) { return a.getMin() < b.getMin(); }); + } + setSelectedIRFrames(mOwnList, 0, 0, 0, false); +} + void IRFrameSelector::print(bool lst) const { LOGP(info, "Last query stopped at entry {} for IRFrame {}:{}", mLastBoundID, @@ -183,6 +193,8 @@ void IRFrameSelector::clear() { mIsSet = false; mOwnList.clear(); + mLastIRFrameChecked.getMin().clear(); // invalidate + mLastBoundID = -1; mFrames = {}; } diff --git a/Detectors/CTF/README.md b/Detectors/CTF/README.md index e1e65060db523..47ce765de289a 100644 --- a/Detectors/CTF/README.md +++ b/Detectors/CTF/README.md @@ -95,6 +95,14 @@ comma-separated list of detectors to read, Overrides skipDet ``` comma-separated list of detectors to skip + +By default an exception will be thrown if detector is requested but missing in the CTF. To enable injection of the empty output in such case one should use option `--allow-missing-detectors`. + +``` +--ctf-data-subspec arg (=0) +``` +allows to alter the `subSpecification` used to send the CTFDATA from the reader to decoders. Non-0 value must be used in case the data extracted by the CTF-reader should be processed and stored in new CTFs (in order to avoid clash of CTFDATA messages of the reader and writer). + ``` --max-tf arg (=-1) ``` @@ -141,6 +149,8 @@ There is a possibility to read remote root files directly, w/o caching them loca 2) provide proper regex to define remote files, e.g. for the example above: `--remote-regex "^root://.+/eos/aliceo2/.+"`. 3) pass an option `--copy-cmd no-copy`. +## Selective TF reading + ``` --select-ctf-ids ``` @@ -148,24 +158,25 @@ This is a `ctf-reader` device local option allowing selective reading of particu Note that the index corresponds not to the entry of the TF in the CTF tree but to the reader own counter incremented throught all input files (e.g. if the 10 CTF files with 20 TFs each are provided for the input and the selection of TFs `0,2,22,66` is provided, the reader will inject to the DPL the TFs at entries 0 and 2 from the 1st CTF file, entry 5 of the second file, entry 6 of the 3d and will finish the job. -For the ITS and MFT entropy decoding one can request either to decompose clusters to digits and send them instead of clusters (via `o2-ctf-reader-workflow` global options `--its-digits` and `--mft-digits` respectively) -or to apply the noise mask to decoded clusters (or decoded digits). If the masking (e.g. via option `--its-entropy-decoder " --mask-noise "`) is requested, user should provide to the entropy decoder the noise mask file (eventually will be loaded from CCDB) and cluster patterns decoding dictionary (if the clusters were encoded with patterns IDs). -For example, ``` -o2-ctf-reader-workflow --ctf-input --onlyDet ITS,MFT --its-entropy-decoder ' --mask-noise' | ... +--ir-frames-files --skip-skimmed-out-tf ``` -will decode ITS and MFT data, decompose on the fly ITS clusters to digits, mask the noisy pixels with the provided masks, recluster remaining ITS digits and send the new clusters out, together with unchanged MFT clusters. +This option (used for skimming) allow to push to DPL only those TFs which overlap with selected BC-ranges provided via input root file (for various formats see `o2::utils::IRFrameSelector::loadIRFrames` method). + ``` -o2-ctf-reader-workflow --ctf-input --onlyDet ITS,MFT --mft-digits --mft-entropy-decoder ' --mask-noise' | ... +--ir-frames-files ``` -will send decompose clusters to digits and send ben out after masking the noise for the MFT, while ITS clusters will be sent as decoded. - -By default an exception will be thrown if detector is requested but missing in the CTF. To enable injection of the empty output in such case one should use option `--allow-missing-detectors`. +This option allows to push to DPL only those TFs which overlap with the ` ` (separators can be any whitespace, comma or semicolon) records provided via text file (assuming that there are some entries for a given run, otherwise the option is ignored). +Multiple ranges per run and multiple runs can be mentioned in a single input file. The range limits can be indicated either as a UNIX timestamp in `ms` or as an orbit number (in the fill the run belongs to). +In case an option ``` ---ctf-data-subspec arg (=0) +--invert-irframe-selection ``` -allows to alter the `subSpecification` used to send the CTFDATA from the reader to decoders. Non-0 value must be used in case the data extracted by the CTF-reader should be processed and stored in new CTFs (in order to avoid clash of CTFDATA messages of the reader and writer). +is provided, the selections above are inverted: TFs matching some of the provided ranges will be discarded, while the rest will be pushed to the DPL + +At the end of the processing the `ctf-writer` will create a local file `ctf_read_ntf.txt` containing only the number of TFs pushed to the DPL. +In case no TF passed the selections above, this file will contain 0. ## Support for externally provided encoding dictionaries @@ -201,3 +212,18 @@ Additionally, one may throttle on the free SHM by providing an option to the rea Note that by default the reader reads into the memory the CTF data and prepares all output messages but injects them only once the rate-limiter allows that. With the option `--limit-tf-before-reading` set also the preparation of the data to inject will be conditioned by the green light from the rate-limiter. + + +## Modifying ITS/MFT CTF output + +For the ITS and MFT entropy decoding one can request either to decompose clusters to digits and send them instead of clusters (via `o2-ctf-reader-workflow` global options `--its-digits` and `--mft-digits` respectively) +or to apply the noise mask to decoded clusters (or decoded digits). If the masking (e.g. via option `--its-entropy-decoder " --mask-noise "`) is requested, user should provide to the entropy decoder the noise mask file (eventually will be loaded from CCDB) and cluster patterns decoding dictionary (if the clusters were encoded with patterns IDs). +For example, +``` +o2-ctf-reader-workflow --ctf-input --onlyDet ITS,MFT --its-entropy-decoder ' --mask-noise' | ... +``` +will decode ITS and MFT data, decompose on the fly ITS clusters to digits, mask the noisy pixels with the provided masks, recluster remaining ITS digits and send the new clusters out, together with unchanged MFT clusters. +``` +o2-ctf-reader-workflow --ctf-input --onlyDet ITS,MFT --mft-digits --mft-entropy-decoder ' --mask-noise' | ... +``` +will send decompose clusters to digits and send ben out after masking the noise for the MFT, while ITS clusters will be sent as decoded. diff --git a/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h b/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h index 997572e0371b2..b202013a6eea1 100644 --- a/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h +++ b/Detectors/CTF/workflow/include/CTFWorkflow/CTFReaderSpec.h @@ -31,8 +31,10 @@ struct CTFReaderInp { std::string remoteRegex{}; std::string metricChannel{}; std::string fileIRFrames{}; + std::string fileRunTimeSpans{}; std::vector ctfIDs{}; bool skipSkimmedOutTF = false; + bool invertIRFramesSelection = false; bool allowMissingDetectors = false; bool checkTFLimitBeforeReading = false; bool sup0xccdb = false; diff --git a/Detectors/CTF/workflow/src/CTFReaderSpec.cxx b/Detectors/CTF/workflow/src/CTFReaderSpec.cxx index 70bb589e8836a..bcf3b5d975b74 100644 --- a/Detectors/CTF/workflow/src/CTFReaderSpec.cxx +++ b/Detectors/CTF/workflow/src/CTFReaderSpec.cxx @@ -45,6 +45,9 @@ #include "DataFormatsZDC/CTF.h" #include "DataFormatsHMP/CTF.h" #include "DataFormatsCTP/CTF.h" +#include "DataFormatsParameters/AggregatedRunInfo.h" +#include "CCDB/BasicCCDBManager.h" +#include "CommonConstants/LHCConstants.h" #include "Algorithm/RangeTokenizer.h" #include #include @@ -81,6 +84,8 @@ class CTFReaderSpec : public o2::framework::Task void run(o2::framework::ProcessingContext& pc) final; private: + void runTimeRangesToIRFrameSelector(const o2::framework::TimingInfo& timingInfo); + void loadRunTimeSpans(const std::string& flname); void openCTFFile(const std::string& flname); bool processTF(ProcessingContext& pc); void checkTreeEntries(); @@ -91,16 +96,20 @@ class CTFReaderSpec : public o2::framework::Task void tryToFixCTFHeader(CTFHeader& ctfHeader) const; CTFReaderInp mInput{}; o2::utils::IRFrameSelector mIRFrameSelector; // optional IR frames selector + std::map>> mRunTimeRanges; std::unique_ptr mFileFetcher; std::unique_ptr mCTFFile; std::unique_ptr mCTFTree; bool mRunning = false; bool mUseLocalTFCounter = false; + int mConvRunTimeRangesToOrbits = -1; // not defined yet int mCTFCounter = 0; + int mCTFCounterAcc = 0; int mNFailedFiles = 0; int mFilesRead = 0; int mTFLength = 128; int mNWaits = 0; + int mRunNumberPrev = -1; long mTotalWaitTime = 0; long mLastSendTime = 0L; long mCurrTreeEntry = 0L; @@ -129,8 +138,8 @@ void CTFReaderSpec::stopReader() return; } LOGP(info, "CTFReader stops processing, {} files read, {} files failed", mFilesRead - mNFailedFiles, mNFailedFiles); - LOGP(info, "CTF reading total timing: Cpu: {:.3f} Real: {:.3f} s for {} TFs in {} loops, spent {:.2} s in {} data waiting states", - mTimer.CpuTime(), mTimer.RealTime(), mCTFCounter, mFileFetcher->getNLoops(), 1e-6 * mTotalWaitTime, mNWaits); + LOGP(info, "CTF reading total timing: Cpu: {:.3f} Real: {:.3f} s for {} TFs ({} accepted) in {} loops, spent {:.2} s in {} data waiting states", + mTimer.CpuTime(), mTimer.RealTime(), mCTFCounter, mCTFCounterAcc, mFileFetcher->getNLoops(), 1e-6 * mTotalWaitTime, mNWaits); mRunning = false; mFileFetcher->stop(); mFileFetcher.reset(); @@ -164,6 +173,111 @@ void CTFReaderSpec::init(InitContext& ic) mTFLength = hbfu.nHBFPerTF; LOGP(info, "IRFrames will be selected from {}, assumed TF length: {} HBF", mInput.fileIRFrames, mTFLength); } + if (!mInput.fileRunTimeSpans.empty()) { + loadRunTimeSpans(mInput.fileRunTimeSpans); + } +} + +void CTFReaderSpec::runTimeRangesToIRFrameSelector(const o2::framework::TimingInfo& timingInfo) +{ + // convert entries in the runTimeRanges to IRFrameSelector, if needed, convert time to orbit + mIRFrameSelector.clear(); + auto ent = mRunTimeRanges.find(timingInfo.runNumber); + if (ent == mRunTimeRanges.end()) { + LOGP(info, "RunTimeRanges selection was provided but run {} has no entries, all TFs will be processed", timingInfo.runNumber); + return; + } + o2::parameters::AggregatedRunInfo rinfo; + auto& ccdb = o2::ccdb::BasicCCDBManager::instance(); + rinfo = o2::parameters::AggregatedRunInfo::buildAggregatedRunInfo(ccdb, timingInfo.runNumber); + if (rinfo.runNumber != timingInfo.runNumber || rinfo.orbitsPerTF < 1) { + LOGP(fatal, "failed to extract AggregatedRunInfo for run {}", timingInfo.runNumber); + } + mTFLength = rinfo.orbitsPerTF; + std::vector frames; + for (const auto& rng : ent->second) { + long orbMin = 0, orbMax = 0; + if (mConvRunTimeRangesToOrbits > 0) { + orbMin = rinfo.orbitSOR + (rng.first - rinfo.sor) / (o2::constants::lhc::LHCOrbitMUS * 0.001); + orbMax = rinfo.orbitSOR + (rng.second - rinfo.sor) / (o2::constants::lhc::LHCOrbitMUS * 0.001); + } else { + orbMin = rng.first; + orbMax = rng.second; + } + if (orbMin < 0) { + orbMin = 0; + } + if (orbMax < 0) { + orbMax = 0; + } + if (timingInfo.runNumber > 523897) { + orbMin = (orbMin / rinfo.orbitsPerTF) * rinfo.orbitsPerTF; + orbMax = (orbMax / rinfo.orbitsPerTF + 1) * rinfo.orbitsPerTF - 1; + } + LOGP(info, "TFs overlapping with orbits {}:{} will be {}", orbMin, orbMax, mInput.invertIRFramesSelection ? "rejected" : "selected"); + frames.emplace_back(InteractionRecord{0, uint32_t(orbMin)}, InteractionRecord{o2::constants::lhc::LHCMaxBunches, uint32_t(orbMax)}); + } + mIRFrameSelector.setOwnList(frames, true); +} + +void CTFReaderSpec::loadRunTimeSpans(const std::string& flname) +{ + std::ifstream inputFile(flname); + if (!inputFile) { + LOGP(fatal, "Failed to open selected run/timespans file {}", mInput.fileRunTimeSpans); + } + std::string line; + size_t cntl = 0, cntr = 0; + while (std::getline(inputFile, line)) { + cntl++; + for (char& ch : line) { // Replace semicolons and tabs with spaces for uniform processing + if (ch == ';' || ch == '\t' || ch == ',') { + ch = ' '; + } + } + o2::utils::Str::trim(line); + if (line.size() < 1 || line[0] == '#') { + continue; + } + auto tokens = o2::utils::Str::tokenize(line, ' '); + auto logError = [&cntl, &line]() { LOGP(error, "Expected format for selection is tripplet , failed on line#{}: {}", cntl, line); }; + if (tokens.size() >= 3) { + int run = 0; + long rmin, rmax; + try { + run = std::stoi(tokens[0]); + rmin = std::stol(tokens[1]); + rmax = std::stol(tokens[2]); + } catch (...) { + logError(); + continue; + } + + constexpr long ISTimeStamp = 1514761200000L; + int convmn = rmin > ISTimeStamp ? 1 : 0, convmx = rmax > ISTimeStamp ? 1 : 0; // values above ISTimeStamp are timestamps (need to be converted to orbits) + if (rmin > rmax) { + LOGP(fatal, "Provided range limits are not in increasing order, entry is {}", line); + } + if (mConvRunTimeRangesToOrbits == -1) { + if (convmn != convmx) { + LOGP(fatal, "Provided range limits should be both consistent either with orbit number or with unix timestamp in ms, entry is {}", line); + } + mConvRunTimeRangesToOrbits = convmn; // need to convert to orbit if time + LOGP(info, "Interpret selected time-spans input as {}", mConvRunTimeRangesToOrbits == 1 ? "timstamps(ms)" : "orbits"); + } else { + if (mConvRunTimeRangesToOrbits != convmn || mConvRunTimeRangesToOrbits != convmx) { + LOGP(fatal, "Provided range limits should are not consistent with previously determined {} input, entry is {}", mConvRunTimeRangesToOrbits == 1 ? "timestamps" : "orbits", line); + } + } + + mRunTimeRanges[run].emplace_back(rmin, rmax); + cntr++; + } else { + logError(); + } + } + LOGP(info, "Read {} time-spans for {} runs from {}", cntr, mRunTimeRanges.size(), mInput.fileRunTimeSpans); + inputFile.close(); } ///_______________________________________ @@ -256,6 +370,17 @@ void CTFReaderSpec::run(ProcessingContext& pc) pc.services().get().endOfStream(); pc.services().get().readyToQuit(QuitRequest::Me); stopReader(); + const std::string dummy{"ctf_read_ntf.txt"}; + if (mCTFCounterAcc == 0) { + LOGP(warn, "No TF passed selection, writing a 0 to file {}", dummy); + } + try { + std::ofstream outfile; + outfile.open(dummy, std::ios::out | std::ios::trunc); + outfile << mCTFCounterAcc << std::endl; + } catch (...) { + LOGP(error, "Failed to write {}", dummy); + } } } @@ -278,7 +403,7 @@ bool CTFReaderSpec::processTF(ProcessingContext& pc) } if (mUseLocalTFCounter) { - ctfHeader.tfCounter = mCTFCounter; + ctfHeader.tfCounter = mCTFCounterAcc; } LOG(info) << ctfHeader; @@ -289,19 +414,26 @@ bool CTFReaderSpec::processTF(ProcessingContext& pc) timingInfo.tfCounter = ctfHeader.tfCounter; timingInfo.runNumber = ctfHeader.run; + if (mRunTimeRanges.size() && timingInfo.runNumber != mRunNumberPrev) { + runTimeRangesToIRFrameSelector(timingInfo); + } + mRunNumberPrev = timingInfo.runNumber; + if (mIRFrameSelector.isSet()) { o2::InteractionRecord ir0(0, timingInfo.firstTForbit); - // we cannot have GRPECS via DPL CCDB fetcher in the CTFReader, so we use mTFLength extracted from the HBFUtils o2::InteractionRecord ir1(o2::constants::lhc::LHCMaxBunches - 1, timingInfo.firstTForbit < 0xffffffff - (mTFLength - 1) ? timingInfo.firstTForbit + (mTFLength - 1) : 0xffffffff); auto irSpan = mIRFrameSelector.getMatchingFrames({ir0, ir1}); - if (irSpan.size() == 0 && mInput.skipSkimmedOutTF) { - LOGP(info, "Skimming did not define any selection for TF [{}] : [{}]", ir0.asString(), ir1.asString()); + bool acc = true; + if (mInput.skipSkimmedOutTF) { + acc = (irSpan.size() > 0) ? !mInput.invertIRFramesSelection : mInput.invertIRFramesSelection; + LOGP(info, "IRFrame selection contains {} frames for TF [{}] : [{}]: {}use this TF (selection inversion mode is {})", + irSpan.size(), ir0.asString(), ir1.asString(), acc ? "" : "do not ", mInput.invertIRFramesSelection ? "ON" : "OFF"); + } + if (!acc) { return false; - } else { - if (mInput.checkTFLimitBeforeReading) { - limiter.check(pc, mInput.tfRateLimit, mInput.minSHM); - } - LOGP(info, "{} IR-Frames are selected for TF [{}] : [{}]", irSpan.size(), ir0.asString(), ir1.asString()); + } + if (mInput.checkTFLimitBeforeReading) { + limiter.check(pc, mInput.tfRateLimit, mInput.minSHM); } auto outVec = pc.outputs().make>(OutputRef{"selIRFrames"}, irSpan.begin(), irSpan.end()); } else { @@ -329,6 +461,7 @@ bool CTFReaderSpec::processTF(ProcessingContext& pc) processDetector(DetID::CPV, ctfHeader, pc); processDetector(DetID::ZDC, ctfHeader, pc); processDetector(DetID::CTP, ctfHeader, pc); + mCTFCounterAcc++; // send sTF acknowledge message if (!mInput.sup0xccdb) { @@ -466,7 +599,7 @@ DataProcessorSpec getCTFReaderSpec(const CTFReaderInp& inp) outputs.emplace_back(OutputLabel{det.getName()}, det.getDataOrigin(), "CTFDATA", inp.subspec, Lifetime::Timeframe); } } - if (!inp.fileIRFrames.empty()) { + if (!inp.fileIRFrames.empty() || !inp.fileRunTimeSpans.empty()) { outputs.emplace_back(OutputLabel{"selIRFrames"}, "CTF", "SELIRFRAMES", 0, Lifetime::Timeframe); } if (!inp.sup0xccdb) { diff --git a/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx b/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx index 90d259f4e3a5c..1f0ef9a3b871b 100644 --- a/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx +++ b/Detectors/CTF/workflow/src/ctf-reader-workflow.cxx @@ -66,7 +66,9 @@ void customize(std::vector& workflowOptions) options.push_back(ConfigParamSpec{"ctf-data-subspec", VariantType::Int, 0, {"subspec to use for decoded CTF messages (use non-0 if CTF writer will be attached downstream)"}}); options.push_back(ConfigParamSpec{"configKeyValues", VariantType::String, "", {"Semicolon separated key=value strings"}}); options.push_back(ConfigParamSpec{"ir-frames-files", VariantType::String, "", {"If non empty, inject selected IRFrames from this file"}}); + options.push_back(ConfigParamSpec{"run-time-span-file", VariantType::String, "", {"If non empty, inject selected IRFrames from this text file (run, min/max orbit or unix time)"}}); options.push_back(ConfigParamSpec{"skip-skimmed-out-tf", VariantType::Bool, false, {"Do not process TFs with empty IR-Frame coverage"}}); + options.push_back(ConfigParamSpec{"invert-irframe-selection", VariantType::Bool, false, {"Select only frames mentioned in ir-frames-file (skip-skimmed-out-tf applied to TF not selected!)"}}); // options.push_back(ConfigParamSpec{"its-digits", VariantType::Bool, false, {"convert ITS clusters to digits"}}); options.push_back(ConfigParamSpec{"mft-digits", VariantType::Bool, false, {"convert MFT clusters to digits"}}); @@ -125,7 +127,9 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) ctfInput.sup0xccdb = !configcontext.options().get("send-diststf-0xccdb"); ctfInput.minSHM = std::stoul(configcontext.options().get("timeframes-shm-limit")); ctfInput.fileIRFrames = configcontext.options().get("ir-frames-files"); + ctfInput.fileRunTimeSpans = configcontext.options().get("run-time-span-file"); ctfInput.skipSkimmedOutTF = configcontext.options().get("skip-skimmed-out-tf"); + ctfInput.invertIRFramesSelection = configcontext.options().get("invert-irframe-selection"); int verbosity = configcontext.options().get("ctf-reader-verbosity"); int rateLimitingIPCID = std::stoi(configcontext.options().get("timeframes-rate-limit-ipcid")); @@ -133,6 +137,12 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) if (rateLimitingIPCID > -1 && !chanFmt.empty()) { ctfInput.metricChannel = fmt::format(fmt::runtime(chanFmt), o2::framework::ChannelSpecHelpers::defaultIPCFolder(), rateLimitingIPCID); } + if (!ctfInput.fileRunTimeSpans.empty()) { + ctfInput.skipSkimmedOutTF = true; + } + if (!ctfInput.fileIRFrames.empty() && !ctfInput.fileRunTimeSpans.empty()) { + LOGP(fatal, "One cannot provide --ir-frames-files and --run-time-span-file options simultaneously"); + } specs.push_back(o2::ctf::getCTFReaderSpec(ctfInput)); From 33b421259e216f9e87c41438ae4c1fcc63691163 Mon Sep 17 00:00:00 2001 From: Matteo Concas Date: Tue, 3 Dec 2024 16:07:27 +0100 Subject: [PATCH 0126/2180] ITS-GPU: Move Tracklet finder on GPU (#13737) * Fix hybrid vertexer printouts * Move multiplicity mask to a vector * Add gpuSpan * Debugging getSpan * Checkpointing * Fix access in tracklet finding * Fix tracklet LUTs issue * Debugging small discrepancies * Fix bad PhiBins pick * Add tracklet counting * Fix indices for used clusters * Add tracklet writing on the buffer * tracklets on gpu * Tracklet finder on GPU --- .../include/ITSReconstruction/FastMultEst.h | 2 +- .../ITS/reconstruction/src/FastMultEst.cxx | 2 +- .../GPU/ITStrackingGPU/TimeFrameGPU.h | 63 +- .../GPU/ITStrackingGPU/TrackingKernels.h | 71 +- .../ITS/tracking/GPU/ITStrackingGPU/Utils.h | 43 ++ .../ITS/tracking/GPU/cuda/TimeFrameGPU.cu | 168 +++- .../tracking/GPU/cuda/TrackerTraitsGPU.cxx | 335 +++----- .../ITS/tracking/GPU/cuda/TrackingKernels.cu | 723 +++++++++++------- .../tracking/include/ITStracking/TimeFrame.h | 25 +- .../ITSMFT/ITS/tracking/src/TrackerTraits.cxx | 11 +- .../ITS/tracking/src/TrackingInterface.cxx | 2 +- .../ITSMFT/ITS/tracking/src/Vertexer.cxx | 8 +- .../ITS/workflow/src/CookedTrackerSpec.cxx | 2 +- 13 files changed, 848 insertions(+), 607 deletions(-) diff --git a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEst.h b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEst.h index 457381862cc42..9e8299e89b404 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEst.h +++ b/Detectors/ITSMFT/ITS/reconstruction/include/ITSReconstruction/FastMultEst.h @@ -45,7 +45,7 @@ struct FastMultEst { static uint32_t getCurrentRandomSeed(); int selectROFs(const gsl::span rofs, const gsl::span clus, - const gsl::span trig, std::vector& sel); + const gsl::span trig, std::vector& sel); void fillNClPerLayer(const gsl::span& clusters); float process(const std::array ncl) diff --git a/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEst.cxx b/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEst.cxx index a55fafdf60409..c547996c6f356 100644 --- a/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEst.cxx +++ b/Detectors/ITSMFT/ITS/reconstruction/src/FastMultEst.cxx @@ -125,7 +125,7 @@ float FastMultEst::processNoiseImposed(const std::array ncl) } int FastMultEst::selectROFs(const gsl::span rofs, const gsl::span clus, - const gsl::span trig, std::vector& sel) + const gsl::span trig, std::vector& sel) { int nrof = rofs.size(), nsel = 0; const auto& multEstConf = FastMultEstConfig::Instance(); // parameters for mult estimation and cuts diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h index ad8724f315ec8..37f392ebbd3a7 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TimeFrameGPU.h @@ -51,9 +51,19 @@ class TimeFrameGPU : public TimeFrame void initialise(const int, const TrackingParameters&, const int, IndexTableUtils* utils = nullptr, const TimeFrameGPUParameters* pars = nullptr); void initDevice(IndexTableUtils*, const TrackingParameters& trkParam, const TimeFrameGPUParameters&, const int, const int); void initDeviceSAFitting(); + void loadIndexTableUtils(const int); void loadTrackingFrameInfoDevice(const int); void loadUnsortedClustersDevice(const int); void loadClustersDevice(const int); + void loadClustersIndexTables(const int iteration); + void createUsedClustersDevice(const int); + void loadUsedClustersDevice(); + void loadROframeClustersDevice(const int); + void loadMultiplicityCutMask(const int); + void loadVertices(const int); + + /// + void createTrackletsLUTDevice(const int); void loadTrackletsDevice(); void loadTrackletsLUTDevice(); void loadCellsDevice(); @@ -62,6 +72,7 @@ class TimeFrameGPU : public TimeFrame void loadTrackSeedsChi2Device(); void loadRoadsDevice(); void loadTrackSeedsDevice(std::vector&); + void createTrackletsBuffers(); void createCellsBuffers(const int); void createCellsDevice(); void createCellsLUTDevice(); @@ -93,7 +104,7 @@ class TimeFrameGPU : public TimeFrame std::vector>& getLabelsInChunks() { return mLabelsInChunks; } int getNAllocatedROFs() const { return mNrof; } // Allocated means maximum nROF for each chunk while populated is the number of loaded ones. StaticTrackingParameters* getDeviceTrackingParameters() { return mTrackingParamsDevice; } - Vertex* getDeviceVertices() { return mVerticesDevice; } + Vertex* getDeviceVertices() { return mPrimaryVerticesDevice; } int* getDeviceROFramesPV() { return mROFramesPVDevice; } unsigned char* getDeviceUsedClusters(const int); const o2::base::Propagator* getChainPropagator(); @@ -107,8 +118,12 @@ class TimeFrameGPU : public TimeFrame const TrackingFrameInfo** getDeviceArrayTrackingFrameInfo() const { return mTrackingFrameInfoDeviceArray; } const Cluster** getDeviceArrayClusters() const { return mClustersDeviceArray; } const Cluster** getDeviceArrayUnsortedClusters() const { return mUnsortedClustersDeviceArray; } - const Tracklet** getDeviceArrayTracklets() const { return mTrackletsDeviceArray; } - const int** getDeviceArrayTrackletsLUT() const { return mTrackletsLUTDeviceArray; } + const int** getDeviceArrayClustersIndexTables() const { return mClustersIndexTablesDeviceArray; } + std::vector getClusterSizes(); + const unsigned char** getDeviceArrayUsedClusters() const { return mUsedClustersDeviceArray; } + const int** getDeviceROframeClusters() const { return mROFrameClustersDeviceArray; } + Tracklet** getDeviceArrayTracklets() { return mTrackletsDeviceArray; } + int** getDeviceArrayTrackletsLUT() const { return mTrackletsLUTDeviceArray; } int** getDeviceArrayCellsLUT() const { return mCellsLUTDeviceArray; } int** getDeviceArrayNeighboursCellLUT() const { return mNeighboursCellLUTDeviceArray; } CellSeed** getDeviceArrayCells() const { return mCellsDeviceArray; } @@ -116,17 +131,19 @@ class TimeFrameGPU : 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; } void setDevicePropagator(const o2::base::PropagatorImpl*) override; // Host-specific getters - gsl::span getHostNTracklets(const int chunkId); - gsl::span getHostNCells(const int chunkId); + gsl::span getNTracklets() { return mNTracklets; } + gsl::span getNCells() { return mNCells; } // Host-available device getters + gsl::span getDeviceTrackletsLUTs() { return mTrackletsLUTDevice; } gsl::span getDeviceCellLUTs() { return mCellsLUTDevice; } + gsl::span getDeviceTracklet() { return mTrackletsDevice; } gsl::span getDeviceCells() { return mCellsDevice; } - gsl::span getNCellsDevice() { return mNCells; } private: void allocMemAsync(void**, size_t, Stream*, bool); // Abstract owned and unowned memory allocations @@ -136,31 +153,37 @@ class TimeFrameGPU : public TimeFrame StaticTrackingParameters mStaticTrackingParams; // Host-available device buffer sizes + std::array mNTracklets; std::array mNCells; // Device pointers StaticTrackingParameters* mTrackingParamsDevice; IndexTableUtils* mIndexTableUtilsDevice; - std::array mROFramesClustersDevice; - std::array mUsedClustersDevice; - Vertex* mVerticesDevice; - int* mROFramesPVDevice; // Hybrid pref + uint8_t* mMultMaskDevice; + Vertex* mPrimaryVerticesDevice; + int* mROFramesPVDevice; std::array mClustersDevice; std::array mUnsortedClustersDevice; + std::array mClustersIndexTablesDevice; + std::array mUsedClustersDevice; + std::array mROFramesClustersDevice; const Cluster** mClustersDeviceArray; const Cluster** mUnsortedClustersDeviceArray; + const int** mClustersIndexTablesDeviceArray; + const unsigned char** mUsedClustersDeviceArray; + const int** mROFrameClustersDeviceArray; std::array mTrackletsDevice; - const Tracklet** mTrackletsDeviceArray; - const int** mTrackletsLUTDeviceArray; - std::array mTrackletsLUTDevice; + Tracklet** mTrackletsDeviceArray; + std::array mTrackletsLUTDevice; std::array mCellsLUTDevice; std::array mNeighboursLUTDevice; int** mCellsLUTDeviceArray; int** mNeighboursCellDeviceArray; int** mNeighboursCellLUTDeviceArray; + int** mTrackletsLUTDeviceArray; std::array mCellsDevice; std::array mNeighboursIndexTablesDevice; CellSeed* mTrackSeedsDevice; @@ -186,10 +209,6 @@ class TimeFrameGPU : public TimeFrame std::vector> mNVerticesInChunks; std::vector> mLabelsInChunks; - // Host memory used only in GPU tracking - std::vector mHostNTracklets; - std::vector mHostNCells; - // Temporary buffer for storing output tracks from GPU tracking std::vector mTrackITSExt; }; @@ -215,6 +234,16 @@ inline int TimeFrameGPU::getNClustersInRofSpan(const int rofIdstart, co { return static_cast(mROFramesClusters[layerId][(rofIdstart + rofSpanSize) < mROFramesClusters.size() ? rofIdstart + rofSpanSize : mROFramesClusters.size() - 1] - mROFramesClusters[layerId][rofIdstart]); } + +template +inline std::vector TimeFrameGPU::getClusterSizes() +{ + std::vector sizes(mUnsortedClusters.size()); + std::transform(mUnsortedClusters.begin(), mUnsortedClusters.end(), sizes.begin(), + [](const auto& v) { return static_cast(v.size()); }); + return sizes; +} + } // namespace gpu } // namespace its } // namespace o2 diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h index 34e6165b9530f..54bdae302e643 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/TrackingKernels.h @@ -50,11 +50,74 @@ GPUg() void fitTrackSeedsKernel( #endif } // namespace gpu +template +void countTrackletsInROFsHandler(const IndexTableUtils* utils, + const uint8_t* multMask, + const int startROF, + const int endROF, + const int maxROF, + const int deltaROF, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const int nVertices, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const int iteration, + const float NSigmaCut, + std::vector& phiCuts, + const float resolutionPV, + std::vector& minR, + std::vector& maxR, + std::vector& resolutions, + std::vector& radii, + std::vector& mulScatAng, + const int nBlocks, + const int nThreads); + +template +void computeTrackletsInROFsHandler(const IndexTableUtils* utils, + const uint8_t* multMask, + const int startROF, + const int endROF, + const int maxROF, + const int deltaROF, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const int nVertices, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + Tracklet** tracklets, + gsl::span spanTracklets, + gsl::span nTracklets, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const int iteration, + const float NSigmaCut, + std::vector& phiCuts, + const float resolutionPV, + std::vector& minR, + std::vector& maxR, + std::vector& resolutions, + std::vector& radii, + std::vector& mulScatAng, + const int nBlocks, + const int nThreads); + void countCellsHandler(const Cluster** sortedClusters, const Cluster** unsortedClusters, const TrackingFrameInfo** tfInfo, - const Tracklet** tracklets, - const int** trackletsLUT, + Tracklet** tracklets, + int** trackletsLUT, const int nTracklets, const int layer, CellSeed* cells, @@ -70,8 +133,8 @@ void countCellsHandler(const Cluster** sortedClusters, void computeCellsHandler(const Cluster** sortedClusters, const Cluster** unsortedClusters, const TrackingFrameInfo** tfInfo, - const Tracklet** tracklets, - const int** trackletsLUT, + Tracklet** tracklets, + int** trackletsLUT, const int nTracklets, const int layer, CellSeed* cells, diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h index 66244bf854b5f..a88e51742e84a 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h +++ b/Detectors/ITSMFT/ITS/tracking/GPU/ITStrackingGPU/Utils.h @@ -31,6 +31,49 @@ struct gpuPair { namespace gpu { +// Poor man implementation of a span-like struct. It is very limited. +template +struct gpuSpan { + using value_type = T; + using ptr = T*; + using ref = T&; + + GPUd() gpuSpan() : _data(nullptr), _size(0) {} + GPUd() gpuSpan(ptr data, unsigned int dim) : _data(data), _size(dim) {} + GPUd() ref operator[](unsigned int idx) const { return _data[idx]; } + GPUd() unsigned int size() const { return _size; } + GPUd() bool empty() const { return _size == 0; } + GPUd() ref front() const { return _data[0]; } + GPUd() ref back() const { return _data[_size - 1]; } + GPUd() ptr begin() const { return _data; } + GPUd() ptr end() const { return _data + _size; } + + protected: + ptr _data; + unsigned int _size; +}; + +template +struct gpuSpan { + using value_type = T; + using ptr = const T*; + using ref = const T&; + + GPUd() gpuSpan() : _data(nullptr), _size(0) {} + GPUd() gpuSpan(ptr data, unsigned int dim) : _data(data), _size(dim) {} + GPUd() gpuSpan(const gpuSpan& other) : _data(other._data), _size(other._size) {} + GPUd() ref operator[](unsigned int idx) const { return _data[idx]; } + GPUd() unsigned int size() const { return _size; } + GPUd() bool empty() const { return _size == 0; } + GPUd() ref front() const { return _data[0]; } + GPUd() ref back() const { return _data[_size - 1]; } + GPUd() ptr begin() const { return _data; } + GPUd() ptr end() const { return _data + _size; } + + protected: + ptr _data; + unsigned int _size; +}; enum class Task { Tracker = 0, diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu index 67144ba2c98ea..4bd15c0203d81 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TimeFrameGPU.cu @@ -92,6 +92,19 @@ void TimeFrameGPU::setDevicePropagator(const o2::base::PropagatorImpl +void TimeFrameGPU::loadIndexTableUtils(const int iteration) +{ + START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "loading indextable utils"); + if (!iteration) { + LOGP(debug, "gpu-allocation: allocating IndexTableUtils buffer, for {} MB.", sizeof(IndexTableUtils) / MB); + allocMemAsync(reinterpret_cast(&mIndexTableUtilsDevice), sizeof(IndexTableUtils), nullptr, getExtAllocator()); + } + LOGP(debug, "gpu-transfer: loading IndexTableUtils object, for {} MB.", sizeof(IndexTableUtils) / MB); + checkGPUError(cudaMemcpyAsync(mIndexTableUtilsDevice, &mIndexTableUtils, sizeof(IndexTableUtils), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); +} + template void TimeFrameGPU::loadUnsortedClustersDevice(const int iteration) { @@ -128,6 +141,65 @@ void TimeFrameGPU::loadClustersDevice(const int iteration) } } +template +void TimeFrameGPU::loadClustersIndexTables(const int iteration) +{ + if (!iteration) { + START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "loading sorted clusters"); + for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { + LOGP(debug, "gpu-transfer: loading clusters indextable for layer {} with {} elements, for {} MB.", iLayer, mIndexTables[iLayer].size(), mIndexTables[iLayer].size() * sizeof(int) / MB); + allocMemAsync(reinterpret_cast(&mClustersIndexTablesDevice[iLayer]), mIndexTables[iLayer].size() * sizeof(int), nullptr, getExtAllocator()); + checkGPUError(cudaMemcpyAsync(mClustersIndexTablesDevice[iLayer], mIndexTables[iLayer].data(), mIndexTables[iLayer].size() * sizeof(int), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + } + allocMemAsync(reinterpret_cast(&mClustersIndexTablesDeviceArray), nLayers * sizeof(int), nullptr, getExtAllocator()); + checkGPUError(cudaMemcpyAsync(mClustersIndexTablesDeviceArray, mClustersIndexTablesDevice.data(), nLayers * sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); + } +} + +template +void TimeFrameGPU::createUsedClustersDevice(const int iteration) +{ + if (!iteration) { + START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "creating used clusters flags"); + for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { + LOGP(debug, "gpu-transfer: creating {} used clusters flags on layer {}, for {} MB.", mUsedClusters[iLayer].size(), iLayer, mUsedClusters[iLayer].size() * sizeof(unsigned char) / MB); + allocMemAsync(reinterpret_cast(&mUsedClustersDevice[iLayer]), mUsedClusters[iLayer].size() * sizeof(unsigned char), nullptr, getExtAllocator()); + checkGPUError(cudaMemsetAsync(mUsedClustersDevice[iLayer], 0, mUsedClusters[iLayer].size() * sizeof(unsigned char), mGpuStreams[0].get())); + } + allocMemAsync(reinterpret_cast(&mUsedClustersDeviceArray), nLayers * sizeof(unsigned char*), nullptr, getExtAllocator()); + checkGPUError(cudaMemcpyAsync(mUsedClustersDeviceArray, mUsedClustersDevice.data(), nLayers * sizeof(unsigned char*), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); + } +} + +template +void TimeFrameGPU::loadUsedClustersDevice() +{ + START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "loading used clusters flags"); + for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { + LOGP(debug, "gpu-transfer: loading {} used clusters flags on layer {}, for {} MB.", mUsedClusters[iLayer].size(), iLayer, mClusters[iLayer].size() * sizeof(unsigned char) / MB); + checkGPUError(cudaMemcpyAsync(mUsedClustersDevice[iLayer], mUsedClusters[iLayer].data(), mUsedClusters[iLayer].size() * sizeof(unsigned char), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + } + STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); +} + +template +void TimeFrameGPU::loadROframeClustersDevice(const int iteration) +{ + if (!iteration) { + START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "loading ROframe clusters"); + for (auto iLayer{0}; iLayer < nLayers; ++iLayer) { + LOGP(debug, "gpu-transfer: loading {} ROframe clusters info on layer {}, for {} MB.", mROFramesClusters[iLayer].size(), iLayer, mROFramesClusters[iLayer].size() * sizeof(int) / MB); + allocMemAsync(reinterpret_cast(&mROFramesClustersDevice[iLayer]), mROFramesClusters[iLayer].size() * sizeof(int), nullptr, getExtAllocator()); + checkGPUError(cudaMemcpyAsync(mROFramesClustersDevice[iLayer], mROFramesClusters[iLayer].data(), mROFramesClusters[iLayer].size() * sizeof(int), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + } + allocMemAsync(reinterpret_cast(&mROFrameClustersDeviceArray), nLayers * sizeof(int*), nullptr, getExtAllocator()); + checkGPUError(cudaMemcpyAsync(mROFrameClustersDeviceArray, mROFramesClustersDevice.data(), nLayers * sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); + } +} + template void TimeFrameGPU::loadTrackingFrameInfoDevice(const int iteration) { @@ -146,19 +218,76 @@ void TimeFrameGPU::loadTrackingFrameInfoDevice(const int iteration) STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); } +template +void TimeFrameGPU::loadMultiplicityCutMask(const int iteration) +{ + if (!iteration) { + START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "loading multiplicity cut mask"); + LOGP(debug, "gpu-transfer: loading multiplicity cut mask with {} elements, for {} MB.", mMultiplicityCutMask.size(), mMultiplicityCutMask.size() * sizeof(bool) / MB); + allocMemAsync(reinterpret_cast(&mMultMaskDevice), mMultiplicityCutMask.size() * sizeof(uint8_t), nullptr, getExtAllocator()); + checkGPUError(cudaMemcpyAsync(mMultMaskDevice, mMultiplicityCutMask.data(), mMultiplicityCutMask.size() * sizeof(uint8_t), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); + } +} + +template +void TimeFrameGPU::loadVertices(const int iteration) +{ + if (!iteration) { + START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "loading seeding vertices"); + LOGP(debug, "gpu-transfer: loading {} ROframes vertices, for {} MB.", mROFramesPV.size(), mROFramesPV.size() * sizeof(int) / MB); + allocMemAsync(reinterpret_cast(&mROFramesPVDevice), mROFramesPV.size() * sizeof(int), nullptr, getExtAllocator()); + checkGPUError(cudaMemcpyAsync(mROFramesPVDevice, mROFramesPV.data(), mROFramesPV.size() * sizeof(int), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + LOGP(debug, "gpu-transfer: loading {} seeding vertices, for {} MB.", mPrimaryVertices.size(), mPrimaryVertices.size() * sizeof(Vertex) / MB); + allocMemAsync(reinterpret_cast(&mPrimaryVerticesDevice), mPrimaryVertices.size() * sizeof(Vertex), nullptr, getExtAllocator()); + checkGPUError(cudaMemcpyAsync(mPrimaryVerticesDevice, mPrimaryVertices.data(), mPrimaryVertices.size() * sizeof(Vertex), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); + } +} + +template +void TimeFrameGPU::createTrackletsLUTDevice(const int iteration) +{ + START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "creating tracklets LUTs"); + for (auto iLayer{0}; iLayer < nLayers - 1; ++iLayer) { + if (!iteration) { + LOGP(debug, "gpu-transfer: creating tracklets LUT for {} elements on layer {}, for {} MB.", mClusters[iLayer].size() + 1, iLayer, (mClusters[iLayer].size() + 1) * sizeof(int) / MB); + allocMemAsync(reinterpret_cast(&mTrackletsLUTDevice[iLayer]), (mClusters[iLayer].size() + 1) * sizeof(int), nullptr, getExtAllocator()); + } + checkGPUError(cudaMemsetAsync(mTrackletsLUTDevice[iLayer], 0, (mClusters[iLayer].size() + 1) * sizeof(int), mGpuStreams[0].get())); + } + if (!iteration) { + allocMemAsync(reinterpret_cast(&mTrackletsLUTDeviceArray), (nLayers - 1) * sizeof(int*), nullptr, getExtAllocator()); + checkGPUError(cudaMemcpyAsync(mTrackletsLUTDeviceArray, mTrackletsLUTDevice.data(), mTrackletsLUTDevice.size() * sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + } + STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); +} + +template +void TimeFrameGPU::createTrackletsBuffers() +{ + START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "creating cells buffers"); + for (auto iLayer{0}; iLayer < nLayers - 1; ++iLayer) { + mNTracklets[iLayer] = 0; + checkGPUError(cudaMemcpyAsync(&mNTracklets[iLayer], mTrackletsLUTDevice[iLayer] + mClusters[iLayer].size(), sizeof(int), cudaMemcpyDeviceToHost)); + LOGP(debug, "gpu-transfer: creating tracklets buffer for {} elements on layer {}, for {} MB.", mNTracklets[iLayer], iLayer, mNTracklets[iLayer] * sizeof(Tracklet) / MB); + allocMemAsync(reinterpret_cast(&mTrackletsDevice[iLayer]), mNTracklets[iLayer] * sizeof(Tracklet), nullptr, getExtAllocator()); + } + allocMemAsync(reinterpret_cast(&mTrackletsDeviceArray), (nLayers - 1) * sizeof(Tracklet*), nullptr, getExtAllocator()); + checkGPUError(cudaHostRegister(mTrackletsDevice.data(), (nLayers - 1) * sizeof(Tracklet*), cudaHostRegisterPortable)); + checkGPUError(cudaMemcpyAsync(mTrackletsDeviceArray, mTrackletsDevice.data(), (nLayers - 1) * sizeof(Tracklet*), cudaMemcpyHostToDevice, mGpuStreams[0].get())); + STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); +} + template void TimeFrameGPU::loadTrackletsDevice() { START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "loading tracklets"); for (auto iLayer{0}; iLayer < nLayers - 1; ++iLayer) { LOGP(debug, "gpu-transfer: loading {} tracklets on layer {}, for {} MB.", mTracklets[iLayer].size(), iLayer, mTracklets[iLayer].size() * sizeof(Tracklet) / MB); - allocMemAsync(reinterpret_cast(&mTrackletsDevice[iLayer]), mTracklets[iLayer].size() * sizeof(Tracklet), nullptr, getExtAllocator()); checkGPUError(cudaHostRegister(mTracklets[iLayer].data(), mTracklets[iLayer].size() * sizeof(Tracklet), cudaHostRegisterPortable)); checkGPUError(cudaMemcpyAsync(mTrackletsDevice[iLayer], mTracklets[iLayer].data(), mTracklets[iLayer].size() * sizeof(Tracklet), cudaMemcpyHostToDevice, mGpuStreams[0].get())); } - allocMemAsync(reinterpret_cast(&mTrackletsDeviceArray), (nLayers - 1) * sizeof(Tracklet*), nullptr, getExtAllocator()); - checkGPUError(cudaHostRegister(mTrackletsDevice.data(), (nLayers - 1) * sizeof(Tracklet*), cudaHostRegisterPortable)); - checkGPUError(cudaMemcpyAsync(mTrackletsDeviceArray, mTrackletsDevice.data(), (nLayers - 1) * sizeof(Tracklet*), cudaMemcpyHostToDevice, mGpuStreams[0].get())); STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); } @@ -167,14 +296,12 @@ void TimeFrameGPU::loadTrackletsLUTDevice() { START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "loading tracklets"); for (auto iLayer{0}; iLayer < nLayers - 2; ++iLayer) { - LOGP(debug, "gpu-transfer: loading tracklets LUT for {} elements on layer {}, for {} MB", mTrackletsLookupTable[iLayer].size(), iLayer, mTrackletsLookupTable[iLayer].size() * sizeof(int) / MB); - allocMemAsync(reinterpret_cast(&mTrackletsLUTDevice[iLayer]), mTrackletsLookupTable[iLayer].size() * sizeof(int), nullptr, getExtAllocator()); + LOGP(debug, "gpu-transfer: loading tracklets LUT for {} elements on layer {}, for {} MB", mTrackletsLookupTable[iLayer].size(), iLayer + 1, mTrackletsLookupTable[iLayer].size() * sizeof(int) / MB); checkGPUError(cudaHostRegister(mTrackletsLookupTable[iLayer].data(), mTrackletsLookupTable[iLayer].size() * sizeof(int), cudaHostRegisterPortable)); - checkGPUError(cudaMemcpyAsync(mTrackletsLUTDevice[iLayer], mTrackletsLookupTable[iLayer].data(), mTrackletsLookupTable[iLayer].size() * sizeof(int), cudaMemcpyHostToDevice)); + checkGPUError(cudaMemcpyAsync(mTrackletsLUTDevice[iLayer + 1], mTrackletsLookupTable[iLayer].data(), mTrackletsLookupTable[iLayer].size() * sizeof(int), cudaMemcpyHostToDevice)); } - allocMemAsync(reinterpret_cast(&mTrackletsLUTDeviceArray), (nLayers - 2) * sizeof(int*), nullptr, getExtAllocator()); - checkGPUError(cudaHostRegister(mTrackletsLUTDevice.data(), (nLayers - 2) * sizeof(int*), cudaHostRegisterPortable)); - checkGPUError(cudaMemcpyAsync(mTrackletsLUTDeviceArray, mTrackletsLUTDevice.data(), (nLayers - 2) * sizeof(int*), cudaMemcpyHostToDevice)); + checkGPUError(cudaHostRegister(mTrackletsLUTDevice.data(), (nLayers - 1) * sizeof(int*), cudaHostRegisterPortable)); + checkGPUError(cudaMemcpyAsync(mTrackletsLUTDeviceArray, mTrackletsLUTDevice.data(), (nLayers - 1) * sizeof(int*), cudaMemcpyHostToDevice)); STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); } @@ -214,9 +341,9 @@ void TimeFrameGPU::createCellsLUTDevice() { START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "creating cells LUTs"); for (auto iLayer{0}; iLayer < nLayers - 2; ++iLayer) { - LOGP(debug, "gpu-transfer: creating cell LUT for {} elements on layer {}, for {} MB.", mTracklets[iLayer].size() + 1, iLayer, (mTracklets[iLayer].size() + 1) * sizeof(int) / MB); - allocMemAsync(reinterpret_cast(&mCellsLUTDevice[iLayer]), (mTracklets[iLayer].size() + 1) * sizeof(int), nullptr, getExtAllocator()); - checkGPUError(cudaMemsetAsync(mCellsLUTDevice[iLayer], 0, (mTracklets[iLayer].size() + 1) * sizeof(int), mGpuStreams[0].get())); + LOGP(debug, "gpu-transfer: creating cell LUT for {} elements on layer {}, for {} MB.", mNTracklets[iLayer] + 1, iLayer, (mNTracklets[iLayer] + 1) * sizeof(int) / MB); + allocMemAsync(reinterpret_cast(&mCellsLUTDevice[iLayer]), (mNTracklets[iLayer] + 1) * sizeof(int), nullptr, getExtAllocator()); + checkGPUError(cudaMemsetAsync(mCellsLUTDevice[iLayer], 0, (mNTracklets[iLayer] + 1) * sizeof(int), mGpuStreams[0].get())); } allocMemAsync(reinterpret_cast(&mCellsLUTDeviceArray), (nLayers - 2) * sizeof(int*), nullptr, getExtAllocator()); checkGPUError(cudaMemcpyAsync(mCellsLUTDeviceArray, mCellsLUTDevice.data(), mCellsLUTDevice.size() * sizeof(int*), cudaMemcpyHostToDevice, mGpuStreams[0].get())); @@ -228,7 +355,7 @@ void TimeFrameGPU::createCellsBuffers(const int layer) { START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "creating cells buffers"); mNCells[layer] = 0; - checkGPUError(cudaMemcpyAsync(&mNCells[layer], mCellsLUTDevice[layer] + mTracklets[layer].size(), sizeof(int), cudaMemcpyDeviceToHost)); + checkGPUError(cudaMemcpyAsync(&mNCells[layer], mCellsLUTDevice[layer] + mNTracklets[layer], sizeof(int), cudaMemcpyDeviceToHost)); LOGP(debug, "gpu-transfer: creating cell buffer for {} elements on layer {}, for {} MB.", mNCells[layer], layer, mNCells[layer] * sizeof(CellSeed) / MB); allocMemAsync(reinterpret_cast(&mCellsDevice[layer]), mNCells[layer] * sizeof(CellSeed), nullptr, getExtAllocator()); @@ -319,9 +446,9 @@ void TimeFrameGPU::downloadCellsLUTDevice() { START_GPU_STREAM_TIMER(mGpuStreams[0].get(), "downloading cell luts"); for (auto iLayer{0}; iLayer < nLayers - 3; ++iLayer) { - LOGP(debug, "gpu-transfer: downloading cells lut on layer {} for {} elements", iLayer, (mTracklets[iLayer + 1].size() + 1)); - mCellsLookupTable[iLayer].resize(mTracklets[iLayer + 1].size() + 1); - checkGPUError(cudaMemcpyAsync(mCellsLookupTable[iLayer].data(), mCellsLUTDevice[iLayer + 1], (mTracklets[iLayer + 1].size() + 1) * sizeof(int), cudaMemcpyDeviceToHost, mGpuStreams[0].get())); + LOGP(debug, "gpu-transfer: downloading cells lut on layer {} for {} elements", iLayer, (mNTracklets[iLayer + 1] + 1)); + mCellsLookupTable[iLayer].resize(mNTracklets[iLayer + 1] + 1); + checkGPUError(cudaMemcpyAsync(mCellsLookupTable[iLayer].data(), mCellsLUTDevice[iLayer + 1], (mNTracklets[iLayer + 1] + 1) * sizeof(int), cudaMemcpyDeviceToHost, mGpuStreams[0].get())); } STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); } @@ -362,13 +489,6 @@ void TimeFrameGPU::unregisterRest() LOGP(debug, "unregistering rest of the host memory..."); checkGPUError(cudaHostUnregister(mCellsDevice.data())); checkGPUError(cudaHostUnregister(mTrackletsDevice.data())); - checkGPUError(cudaHostUnregister(mTrackletsLUTDevice.data())); - for (auto iLayer{0}; iLayer < nLayers - 1; ++iLayer) { - if (iLayer < nLayers - 2) { - checkGPUError(cudaHostUnregister(mTrackletsLookupTable[iLayer].data())); - } - checkGPUError(cudaHostUnregister(mTracklets[iLayer].data())); - } STOP_GPU_STREAM_TIMER(mGpuStreams[0].get()); } diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx index 3c6a307fc4ff6..ae86507e46325 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackerTraitsGPU.cxx @@ -31,241 +31,18 @@ void TrackerTraitsGPU::initialiseTimeFrame(const int iteration) mTimeFrameGPU->initialise(iteration, mTrkParams[iteration], nLayers); mTimeFrameGPU->loadClustersDevice(iteration); mTimeFrameGPU->loadUnsortedClustersDevice(iteration); + mTimeFrameGPU->loadClustersIndexTables(iteration); mTimeFrameGPU->loadTrackingFrameInfoDevice(iteration); + mTimeFrameGPU->loadMultiplicityCutMask(iteration); + mTimeFrameGPU->loadVertices(iteration); + mTimeFrameGPU->loadROframeClustersDevice(iteration); + mTimeFrameGPU->createUsedClustersDevice(iteration); + mTimeFrameGPU->loadIndexTableUtils(iteration); } template void TrackerTraitsGPU::computeLayerTracklets(const int iteration, int, int) { - // if (!mTimeFrameGPU->getClusters().size()) { - // return; - // } - // const Vertex diamondVert({mTrkParams[iteration].Diamond[0], mTrkParams[iteration].Diamond[1], mTrkParams[iteration].Diamond[2]}, {25.e-6f, 0.f, 0.f, 25.e-6f, 0.f, 36.f}, 1, 1.f); - // gsl::span diamondSpan(&diamondVert, 1); - // std::vector threads(mTimeFrameGPU->getNChunks()); - - // for (int chunkId{0}; chunkId < mTimeFrameGPU->getNChunks(); ++chunkId) { - // int maxTracklets{static_cast(mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->clustersPerROfCapacity) * - // static_cast(mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->maxTrackletsPerCluster)}; - // int maxRofPerChunk{mTimeFrameGPU->mNrof / (int)mTimeFrameGPU->getNChunks()}; - // // Define workload - // auto doTrackReconstruction = [&, chunkId, maxRofPerChunk, iteration]() -> void { - // auto offset = chunkId * maxRofPerChunk; - // auto maxROF = offset + maxRofPerChunk; - // while (offset < maxROF) { - // auto rofs = mTimeFrameGPU->loadChunkData(chunkId, offset, maxROF); - // //////////////////// - // /// Tracklet finding - - // for (int iLayer{0}; iLayer < nLayers - 1; ++iLayer) { - // auto nclus = mTimeFrameGPU->getTotalClustersPerROFrange(offset, rofs, iLayer); - // const float meanDeltaR{mTrkParams[iteration].LayerRadii[iLayer + 1] - mTrkParams[iteration].LayerRadii[iLayer]}; - // gpu::computeLayerTrackletsKernelMultipleRof<<getStream(chunkId).get()>>>( - // iLayer, // const int layerIndex, - // iteration, // const int iteration, - // offset, // const unsigned int startRofId, - // rofs, // const unsigned int rofSize, - // 0, // const unsigned int deltaRof, - // mTimeFrameGPU->getChunk(chunkId).getDeviceClusters(iLayer), // const Cluster* clustersCurrentLayer, - // mTimeFrameGPU->getChunk(chunkId).getDeviceClusters(iLayer + 1), // const Cluster* clustersNextLayer, - // mTimeFrameGPU->getDeviceROframesClusters(iLayer), // const int* roFrameClustersCurrentLayer, // Number of clusters on layer 0 per ROF - // mTimeFrameGPU->getDeviceROframesClusters(iLayer + 1), // const int* roFrameClustersNextLayer, // Number of clusters on layer 1 per ROF - // mTimeFrameGPU->getChunk(chunkId).getDeviceIndexTables(iLayer + 1), // const int* indexTableNextLayer, - // mTimeFrameGPU->getDeviceUsedClusters(iLayer), // const int* usedClustersCurrentLayer, - // mTimeFrameGPU->getDeviceUsedClusters(iLayer + 1), // const int* usedClustersNextLayer, - // mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer), // Tracklet* tracklets, // output data - // mTimeFrameGPU->getDeviceVertices(), // const Vertex* vertices, - // mTimeFrameGPU->getDeviceROframesPV(), // const int* pvROFrame, - // mTimeFrameGPU->getPhiCut(iLayer), // const float phiCut, - // mTimeFrameGPU->getMinR(iLayer + 1), // const float minR, - // mTimeFrameGPU->getMaxR(iLayer + 1), // const float maxR, - // meanDeltaR, // const float meanDeltaR, - // mTimeFrameGPU->getPositionResolution(iLayer), // const float positionResolution, - // mTimeFrameGPU->getMSangle(iLayer), // const float mSAngle, - // mTimeFrameGPU->getDeviceTrackingParameters(), // const StaticTrackingParameters* trkPars, - // mTimeFrameGPU->getDeviceIndexTableUtils(), // const IndexTableUtils* utils - // mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->clustersPerROfCapacity, // const int clustersPerROfCapacity, - // mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->maxTrackletsPerCluster); // const int maxTrackletsPerCluster - - // // Remove empty tracklets due to striding. - // auto nulltracklet = o2::its::Tracklet{}; - // auto thrustTrackletsBegin = thrust::device_ptr(mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer)); - // auto thrustTrackletsEnd = thrust::device_ptr(mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer) + (int)rofs * maxTracklets); - // auto thrustTrackletsAfterEraseEnd = thrust::remove(THRUST_NAMESPACE::par.on(mTimeFrameGPU->getStream(chunkId).get()), - // thrustTrackletsBegin, - // thrustTrackletsEnd, - // nulltracklet); - // // Sort tracklets by first cluster index. - // thrust::sort(THRUST_NAMESPACE::par.on(mTimeFrameGPU->getStream(chunkId).get()), - // thrustTrackletsBegin, - // thrustTrackletsAfterEraseEnd, - // gpu::trackletSortIndexFunctor()); - - // // Remove duplicates. - // auto thrustTrackletsAfterUniqueEnd = thrust::unique(THRUST_NAMESPACE::par.on(mTimeFrameGPU->getStream(chunkId).get()), thrustTrackletsBegin, thrustTrackletsAfterEraseEnd); - - // discardResult(cudaStreamSynchronize(mTimeFrameGPU->getStream(chunkId).get())); - // mTimeFrameGPU->getHostNTracklets(chunkId)[iLayer] = thrustTrackletsAfterUniqueEnd - thrustTrackletsBegin; - // // Compute tracklet lookup table. - // gpu::compileTrackletsLookupTableKernel<<getStream(chunkId).get()>>>(mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer), - // mTimeFrameGPU->getChunk(chunkId).getDeviceTrackletsLookupTables(iLayer), - // mTimeFrameGPU->getHostNTracklets(chunkId)[iLayer]); - // discardResult(cub::DeviceScan::ExclusiveSum(mTimeFrameGPU->getChunk(chunkId).getDeviceCUBTmpBuffer(), // d_temp_storage - // mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->tmpCUBBufferSize, // temp_storage_bytes - // mTimeFrameGPU->getChunk(chunkId).getDeviceTrackletsLookupTables(iLayer), // d_in - // mTimeFrameGPU->getChunk(chunkId).getDeviceTrackletsLookupTables(iLayer), // d_out - // nclus, // num_items - // mTimeFrameGPU->getStream(chunkId).get())); - - // // Create tracklets labels, at the moment on the host - // if (mTimeFrameGPU->hasMCinformation()) { - // std::vector tracklets(mTimeFrameGPU->getHostNTracklets(chunkId)[iLayer]); - // checkGPUError(cudaHostRegister(tracklets.data(), tracklets.size() * sizeof(o2::its::Tracklet), cudaHostRegisterDefault)); - // checkGPUError(cudaMemcpyAsync(tracklets.data(), mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer), tracklets.size() * sizeof(o2::its::Tracklet), cudaMemcpyDeviceToHost, mTimeFrameGPU->getStream(chunkId).get())); - // for (auto& trk : tracklets) { - // MCCompLabel label; - // int currentId{mTimeFrameGPU->mClusters[iLayer][trk.firstClusterIndex].clusterId}; // This is not yet offsetted to the index of the first cluster of the chunk - // int nextId{mTimeFrameGPU->mClusters[iLayer + 1][trk.secondClusterIndex].clusterId}; // This is not yet offsetted to the index of the first cluster of the chunk - // for (auto& lab1 : mTimeFrameGPU->getClusterLabels(iLayer, currentId)) { - // for (auto& lab2 : mTimeFrameGPU->getClusterLabels(iLayer + 1, nextId)) { - // if (lab1 == lab2 && lab1.isValid()) { - // label = lab1; - // break; - // } - // } - // if (label.isValid()) { - // break; - // } - // } - // // TODO: implment label merging. - // // mTimeFrameGPU->getTrackletsLabel(iLayer).emplace_back(label); - // } - // checkGPUError(cudaHostUnregister(tracklets.data())); - // } - // } - - // //////////////// - // /// Cell finding - // for (int iLayer{0}; iLayer < nLayers - 2; ++iLayer) { - // // Compute layer cells. - // gpu::computeLayerCellsKernel<<<10, 1024, 0, mTimeFrameGPU->getStream(chunkId).get()>>>( - // mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer), - // mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer + 1), - // mTimeFrameGPU->getChunk(chunkId).getDeviceTrackletsLookupTables(iLayer + 1), - // mTimeFrameGPU->getHostNTracklets(chunkId)[iLayer], - // nullptr, - // mTimeFrameGPU->getChunk(chunkId).getDeviceCellsLookupTables(iLayer), - // mTimeFrameGPU->getDeviceTrackingParameters()); - - // // Compute number of found Cells - // checkGPUError(cub::DeviceReduce::Sum(mTimeFrameGPU->getChunk(chunkId).getDeviceCUBTmpBuffer(), // d_temp_storage - // mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->tmpCUBBufferSize, // temp_storage_bytes - // mTimeFrameGPU->getChunk(chunkId).getDeviceCellsLookupTables(iLayer), // d_in - // mTimeFrameGPU->getChunk(chunkId).getDeviceNFoundCells() + iLayer, // d_out - // mTimeFrameGPU->getHostNTracklets(chunkId)[iLayer], // num_items - // mTimeFrameGPU->getStream(chunkId).get())); - // // Compute LUT - // discardResult(cub::DeviceScan::ExclusiveSum(mTimeFrameGPU->getChunk(chunkId).getDeviceCUBTmpBuffer(), // d_temp_storage - // mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->tmpCUBBufferSize, // temp_storage_bytes - // mTimeFrameGPU->getChunk(chunkId).getDeviceCellsLookupTables(iLayer), // d_in - // mTimeFrameGPU->getChunk(chunkId).getDeviceCellsLookupTables(iLayer), // d_out - // mTimeFrameGPU->getHostNTracklets(chunkId)[iLayer], // num_items - // mTimeFrameGPU->getStream(chunkId).get())); - - // gpu::computeLayerCellsKernel<<<10, 1024, 0, mTimeFrameGPU->getStream(chunkId).get()>>>( - // mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer), - // mTimeFrameGPU->getChunk(chunkId).getDeviceTracklets(iLayer + 1), - // mTimeFrameGPU->getChunk(chunkId).getDeviceTrackletsLookupTables(iLayer + 1), - // mTimeFrameGPU->getHostNTracklets(chunkId)[iLayer], - // mTimeFrameGPU->getChunk(chunkId).getDeviceCells(iLayer), - // mTimeFrameGPU->getChunk(chunkId).getDeviceCellsLookupTables(iLayer), - // mTimeFrameGPU->getDeviceTrackingParameters()); - // } - // checkGPUError(cudaMemcpyAsync(mTimeFrameGPU->getHostNCells(chunkId).data(), - // mTimeFrameGPU->getChunk(chunkId).getDeviceNFoundCells(), - // (nLayers - 2) * sizeof(int), - // cudaMemcpyDeviceToHost, - // mTimeFrameGPU->getStream(chunkId).get())); - - // // Create cells labels - // // TODO: make it work after fixing the tracklets labels - // if (mTimeFrameGPU->hasMCinformation()) { - // for (int iLayer{0}; iLayer < nLayers - 2; ++iLayer) { - // std::vector cells(mTimeFrameGPU->getHostNCells(chunkId)[iLayer]); - // // Async with not registered memory? - // checkGPUError(cudaMemcpyAsync(cells.data(), mTimeFrameGPU->getChunk(chunkId).getDeviceCells(iLayer), mTimeFrameGPU->getHostNCells(chunkId)[iLayer] * sizeof(o2::its::Cell), cudaMemcpyDeviceToHost)); - // for (auto& cell : cells) { - // MCCompLabel currentLab{mTimeFrameGPU->getTrackletsLabel(iLayer)[cell.getFirstTrackletIndex()]}; - // MCCompLabel nextLab{mTimeFrameGPU->getTrackletsLabel(iLayer + 1)[cell.getSecondTrackletIndex()]}; - // mTimeFrameGPU->getCellsLabel(iLayer).emplace_back(currentLab == nextLab ? currentLab : MCCompLabel()); - // } - // } - // } - - // ///////////////////// - // /// Neighbour finding - // for (int iLayer{0}; iLayer < nLayers - 3; ++iLayer) { - // gpu::computeLayerCellNeighboursKernel<<<10, 1024, 0, mTimeFrameGPU->getStream(chunkId).get()>>>( - // mTimeFrameGPU->getChunk(chunkId).getDeviceCells(iLayer), - // mTimeFrameGPU->getChunk(chunkId).getDeviceCells(iLayer + 1), - // iLayer, - // mTimeFrameGPU->getChunk(chunkId).getDeviceCellsLookupTables(iLayer + 1), - // mTimeFrameGPU->getChunk(chunkId).getDeviceCellNeigboursLookupTables(iLayer), - // nullptr, - // mTimeFrameGPU->getChunk(chunkId).getDeviceNFoundCells(), - // mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->maxNeighboursSize); - - // // Compute Cell Neighbours LUT - // checkGPUError(cub::DeviceScan::ExclusiveSum(mTimeFrameGPU->getChunk(chunkId).getDeviceCUBTmpBuffer(), // d_temp_storage - // mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->tmpCUBBufferSize, // temp_storage_bytes - // mTimeFrameGPU->getChunk(chunkId).getDeviceCellNeigboursLookupTables(iLayer), // d_in - // mTimeFrameGPU->getChunk(chunkId).getDeviceCellNeigboursLookupTables(iLayer), // d_out - // mTimeFrameGPU->getHostNCells(chunkId)[iLayer + 1], // num_items - // mTimeFrameGPU->getStream(chunkId).get())); - - // gpu::computeLayerCellNeighboursKernel<<<10, 1024, 0, mTimeFrameGPU->getStream(chunkId).get()>>>( - // mTimeFrameGPU->getChunk(chunkId).getDeviceCells(iLayer), - // mTimeFrameGPU->getChunk(chunkId).getDeviceCells(iLayer + 1), - // iLayer, - // mTimeFrameGPU->getChunk(chunkId).getDeviceCellsLookupTables(iLayer + 1), - // mTimeFrameGPU->getChunk(chunkId).getDeviceCellNeigboursLookupTables(iLayer), - // mTimeFrameGPU->getChunk(chunkId).getDeviceCellNeighbours(iLayer), - // mTimeFrameGPU->getChunk(chunkId).getDeviceNFoundCells(), - // mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->maxNeighboursSize); - - // // if (!chunkId) { - // // gpu::printBufferLayerOnThread<<<1, 1, 0, mTimeFrameGPU->getStream(chunkId).get()>>>(iLayer, - // // mTimeFrameGPU->getChunk(chunkId).getDeviceCellNeighbours(iLayer), - // // mTimeFrameGPU->getChunk(chunkId).getTimeFrameGPUParameters()->maxNeighboursSize * rofs); - // // } - // } - // // Download cells into vectors - - // for (int iLevel{nLayers - 2}; iLevel >= mTrkParams[iteration].CellMinimumLevel(); --iLevel) { - // const int minimumLevel{iLevel - 1}; - // for (int iLayer{nLayers - 3}; iLayer >= minimumLevel; --iLayer) { - // // gpu::computeLayerRoadsKernel<<<1, 1, 0, mTimeFrameGPU->getStream(chunkId).get()>>>(iLevel, // const int level, - // // iLayer, // const int layerIndex, - // // mTimeFrameGPU->getChunk(chunkId).getDeviceArrayCells(), // const CellSeed** cells, - // // mTimeFrameGPU->getChunk(chunkId).getDeviceNFoundCells(), // const int* nCells, - // // mTimeFrameGPU->getChunk(chunkId).getDeviceArrayNeighboursCell(), // const int** neighbours, - // // mTimeFrameGPU->getChunk(chunkId).getDeviceArrayNeighboursCellLUT(), // const int** neighboursLUT, - // // mTimeFrameGPU->getChunk(chunkId).getDeviceRoads(), // Road* roads, - // // mTimeFrameGPU->getChunk(chunkId).getDeviceRoadsLookupTables(iLayer)); // int* roadsLookupTable - // } - // } - - // // End of tracking for this chunk - // offset += rofs; - // } - // }; - // threads[chunkId] = std::thread(doTrackReconstruction); - // } - // for (auto& thread : threads) { - // thread.join(); - // } - - // mTimeFrameGPU->wipe(nLayers); } template @@ -299,7 +76,7 @@ int TrackerTraitsGPU::getTFNumberOfClusters() const template int TrackerTraitsGPU::getTFNumberOfTracklets() const { - return mTimeFrameGPU->getNumberOfTracklets(); + return std::accumulate(mTimeFrameGPU->getNTracklets().begin(), mTimeFrameGPU->getNTracklets().end(), 0); } template @@ -313,31 +90,94 @@ int TrackerTraitsGPU::getTFNumberOfCells() const template void TrackerTraitsGPU::computeTrackletsHybrid(const int iteration, int iROFslice, int iVertex) { - TrackerTraits::computeLayerTracklets(iteration, iROFslice, iVertex); + auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); + // TrackerTraits::computeLayerTracklets(iteration, iROFslice, iVertex); + mTimeFrameGPU->createTrackletsLUTDevice(iteration); + + const Vertex diamondVert({mTrkParams[iteration].Diamond[0], mTrkParams[iteration].Diamond[1], mTrkParams[iteration].Diamond[2]}, {25.e-6f, 0.f, 0.f, 25.e-6f, 0.f, 36.f}, 1, 1.f); + gsl::span diamondSpan(&diamondVert, 1); + int startROF{mTrkParams[iteration].nROFsPerIterations > 0 ? iROFslice * mTrkParams[iteration].nROFsPerIterations : 0}; + int endROF{mTrkParams[iteration].nROFsPerIterations > 0 ? (iROFslice + 1) * mTrkParams[iteration].nROFsPerIterations + mTrkParams[iteration].DeltaROF : mTimeFrameGPU->getNrof()}; + + countTrackletsInROFsHandler(mTimeFrameGPU->getDeviceIndexTableUtils(), + mTimeFrameGPU->getDeviceMultCutMask(), + startROF, + endROF, + mTimeFrameGPU->getNrof(), + mTrkParams[iteration].DeltaROF, + iVertex, + mTimeFrameGPU->getDeviceVertices(), + mTimeFrameGPU->getDeviceROFramesPV(), + mTimeFrameGPU->getPrimaryVerticesNum(), + mTimeFrameGPU->getDeviceArrayClusters(), + mTimeFrameGPU->getClusterSizes(), + mTimeFrameGPU->getDeviceROframeClusters(), + mTimeFrameGPU->getDeviceArrayUsedClusters(), + mTimeFrameGPU->getDeviceArrayClustersIndexTables(), + mTimeFrameGPU->getDeviceArrayTrackletsLUT(), + mTimeFrameGPU->getDeviceTrackletsLUTs(), // Required for the exclusive sums + iteration, + mTrkParams[iteration].NSigmaCut, + mTimeFrameGPU->getPhiCuts(), + mTrkParams[iteration].PVres, + mTimeFrameGPU->getMinRs(), + mTimeFrameGPU->getMaxRs(), + mTimeFrameGPU->getPositionResolutions(), + mTrkParams[iteration].LayerRadii, + mTimeFrameGPU->getMSangles(), + conf.nBlocks, + conf.nThreads); + mTimeFrameGPU->createTrackletsBuffers(); + computeTrackletsInROFsHandler(mTimeFrameGPU->getDeviceIndexTableUtils(), + mTimeFrameGPU->getDeviceMultCutMask(), + startROF, + endROF, + mTimeFrameGPU->getNrof(), + mTrkParams[iteration].DeltaROF, + iVertex, + mTimeFrameGPU->getDeviceVertices(), + mTimeFrameGPU->getDeviceROFramesPV(), + mTimeFrameGPU->getPrimaryVerticesNum(), + mTimeFrameGPU->getDeviceArrayClusters(), + mTimeFrameGPU->getClusterSizes(), + mTimeFrameGPU->getDeviceROframeClusters(), + mTimeFrameGPU->getDeviceArrayUsedClusters(), + mTimeFrameGPU->getDeviceArrayClustersIndexTables(), + mTimeFrameGPU->getDeviceArrayTracklets(), + mTimeFrameGPU->getDeviceTracklet(), + mTimeFrameGPU->getNTracklets(), + mTimeFrameGPU->getDeviceArrayTrackletsLUT(), + mTimeFrameGPU->getDeviceTrackletsLUTs(), + iteration, + mTrkParams[iteration].NSigmaCut, + mTimeFrameGPU->getPhiCuts(), + mTrkParams[iteration].PVres, + mTimeFrameGPU->getMinRs(), + mTimeFrameGPU->getMaxRs(), + mTimeFrameGPU->getPositionResolutions(), + mTrkParams[iteration].LayerRadii, + mTimeFrameGPU->getMSangles(), + conf.nBlocks, + conf.nThreads); } template void TrackerTraitsGPU::computeCellsHybrid(const int iteration) { - mTimeFrameGPU->loadTrackletsDevice(); - mTimeFrameGPU->loadTrackletsLUTDevice(); mTimeFrameGPU->createCellsLUTDevice(); auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); - // #pragma omp parallel for num_threads(nLayers) for (int iLayer = 0; iLayer < mTrkParams[iteration].CellsPerRoad(); ++iLayer) { - if (mTimeFrameGPU->getTracklets()[iLayer + 1].empty() || - mTimeFrameGPU->getTracklets()[iLayer].empty()) { + if (!mTimeFrameGPU->getNTracklets()[iLayer + 1] || !mTimeFrameGPU->getNTracklets()[iLayer]) { continue; } - - const int currentLayerTrackletsNum{static_cast(mTimeFrameGPU->getTracklets()[iLayer].size())}; + const int currentLayerTrackletsNum{static_cast(mTimeFrameGPU->getNTracklets()[iLayer])}; countCellsHandler(mTimeFrameGPU->getDeviceArrayClusters(), mTimeFrameGPU->getDeviceArrayUnsortedClusters(), mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), mTimeFrameGPU->getDeviceArrayTracklets(), mTimeFrameGPU->getDeviceArrayTrackletsLUT(), - mTimeFrameGPU->getTracklets()[iLayer].size(), + mTimeFrameGPU->getNTracklets()[iLayer], iLayer, nullptr, mTimeFrameGPU->getDeviceArrayCellsLUT(), @@ -354,7 +194,7 @@ void TrackerTraitsGPU::computeCellsHybrid(const int iteration) mTimeFrameGPU->getDeviceArrayTrackingFrameInfo(), mTimeFrameGPU->getDeviceArrayTracklets(), mTimeFrameGPU->getDeviceArrayTrackletsLUT(), - mTimeFrameGPU->getTracklets()[iLayer].size(), + mTimeFrameGPU->getNTracklets()[iLayer], iLayer, mTimeFrameGPU->getDeviceCells()[iLayer], mTimeFrameGPU->getDeviceArrayCellsLUT(), @@ -378,7 +218,7 @@ void TrackerTraitsGPU::findCellsNeighboursHybrid(const int iteration) auto& conf = o2::its::ITSGpuTrackingParamConfig::Instance(); std::vector>> cellsNeighboursLayer(mTrkParams[iteration].CellsPerRoad() - 1); for (int iLayer{0}; iLayer < mTrkParams[iteration].CellsPerRoad() - 1; ++iLayer) { - const int nextLayerCellsNum{static_cast(mTimeFrameGPU->getNCellsDevice()[iLayer + 1])}; + const int nextLayerCellsNum{static_cast(mTimeFrameGPU->getNCells()[iLayer + 1])}; mTimeFrameGPU->getCellsNeighboursLUT()[iLayer].clear(); mTimeFrameGPU->getCellsNeighboursLUT()[iLayer].resize(nextLayerCellsNum, 0); @@ -441,7 +281,7 @@ void TrackerTraitsGPU::findRoads(const int iteration) std::vector lastCellId, updatedCellId; std::vector lastCellSeed, updatedCellSeed; - processNeighbours(startLayer, startLevel, mTimeFrame->getCells()[startLayer], lastCellId, updatedCellSeed, updatedCellId); + processNeighbours(startLayer, startLevel, mTimeFrameGPU->getCells()[startLayer], lastCellId, updatedCellSeed, updatedCellId); int level = startLevel; for (int iLayer{startLayer - 1}; iLayer > 0 && level > 2; --iLayer) { @@ -495,8 +335,8 @@ void TrackerTraitsGPU::findRoads(const int iteration) if (track.getClusterIndex(iLayer) == UnusedIndex) { continue; } - nShared += int(mTimeFrame->isClusterUsed(iLayer, track.getClusterIndex(iLayer))); - isFirstShared |= !iLayer && mTimeFrame->isClusterUsed(iLayer, track.getClusterIndex(iLayer)); + nShared += int(mTimeFrameGPU->isClusterUsed(iLayer, track.getClusterIndex(iLayer))); + isFirstShared |= !iLayer && mTimeFrameGPU->isClusterUsed(iLayer, track.getClusterIndex(iLayer)); } if (nShared > mTrkParams[0].ClusterSharing) { @@ -508,8 +348,8 @@ void TrackerTraitsGPU::findRoads(const int iteration) if (track.getClusterIndex(iLayer) == UnusedIndex) { continue; } - mTimeFrame->markUsedCluster(iLayer, track.getClusterIndex(iLayer)); - int currentROF = mTimeFrame->getClusterROF(iLayer, track.getClusterIndex(iLayer)); + mTimeFrameGPU->markUsedCluster(iLayer, track.getClusterIndex(iLayer)); + int currentROF = mTimeFrameGPU->getClusterROF(iLayer, track.getClusterIndex(iLayer)); for (int iR{0}; iR < 3; ++iR) { if (rofs[iR] == INT_MAX) { rofs[iR] = currentROF; @@ -525,9 +365,10 @@ void TrackerTraitsGPU::findRoads(const int iteration) if (rofs[1] != INT_MAX) { track.setNextROFbit(); } - mTimeFrame->getTracks(std::min(rofs[0], rofs[1])).emplace_back(track); + mTimeFrameGPU->getTracks(std::min(rofs[0], rofs[1])).emplace_back(track); } } + mTimeFrameGPU->loadUsedClustersDevice(); if (iteration == mTrkParams.size() - 1) { mTimeFrameGPU->unregisterHostMemory(0); } diff --git a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu index 73dcf3bcb4894..229827611c077 100644 --- a/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu +++ b/Detectors/ITSMFT/ITS/tracking/GPU/cuda/TrackingKernels.cu @@ -32,6 +32,7 @@ #include "ITStracking/IndexTableUtils.h" #include "ITStracking/MathUtils.h" #include "DataFormatsITS/TrackITS.h" +#include "ReconstructionDataFormats/Vertex.h" #include "ITStrackingGPU/TrackerTraitsGPU.h" #include "ITStrackingGPU/TrackingKernels.h" @@ -70,12 +71,39 @@ inline void gpuAssert(cudaError_t code, const char* file, int line, bool abort = } namespace o2::its - { using namespace constants::its2; +using Vertex = o2::dataformats::Vertex>; + +GPUd() float Sq(float v) +{ + return v * v; +} namespace gpu { + +GPUd() const int4 getBinsRect(const Cluster& currentCluster, const int layerIndex, + const o2::its::IndexTableUtils& utils, + const float z1, const float z2, float maxdeltaz, float maxdeltaphi) +{ + const float zRangeMin = o2::gpu::CAMath::Min(z1, z2) - maxdeltaz; + const float phiRangeMin = (maxdeltaphi > constants::math::Pi) ? 0.f : currentCluster.phi - maxdeltaphi; + const float zRangeMax = o2::gpu::CAMath::Max(z1, z2) + maxdeltaz; + const float phiRangeMax = (maxdeltaphi > constants::math::Pi) ? constants::math::TwoPi : currentCluster.phi + maxdeltaphi; + + if (zRangeMax < -LayersZCoordinate()[layerIndex + 1] || + zRangeMin > LayersZCoordinate()[layerIndex + 1] || zRangeMin > zRangeMax) { + + return getEmptyBinsRect(); + } + + return int4{o2::gpu::CAMath::Max(0, utils.getZBinIndex(layerIndex + 1, zRangeMin)), + utils.getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMin)), + o2::gpu::CAMath::Min(ZBins - 1, utils.getZBinIndex(layerIndex + 1, zRangeMax)), + utils.getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMax))}; +} + GPUd() bool fitTrack(TrackITSExt& track, int start, int end, @@ -127,7 +155,7 @@ GPUd() bool fitTrack(TrackITSExt& track, } nCl++; } - return o2::gpu::GPUCommonMath::Abs(track.getQ2Pt()) < maxQoverPt && track.getChi2() < chi2ndfcut * (nCl * 2 - 5); + return o2::gpu::CAMath::Abs(track.getQ2Pt()) < maxQoverPt && track.getChi2() < chi2ndfcut * (nCl * 2 - 5); } GPUd() o2::track::TrackParCov buildTrackSeed(const Cluster& cluster1, @@ -146,7 +174,7 @@ GPUd() o2::track::TrackParCov buildTrackSeed(const Cluster& cluster1, const float y3 = tf3.positionTrackingFrame[0]; const float z3 = tf3.positionTrackingFrame[1]; - const bool zeroField{o2::gpu::GPUCommonMath::Abs(bz) < o2::constants::math::Almost0}; + 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)); @@ -164,6 +192,17 @@ GPUd() o2::track::TrackParCov buildTrackSeed(const Cluster& cluster1, 0.f, 0.f, 0.f, 0.f, sg2q2pt}); } +// auto sort_tracklets = [] GPUhdni()(const Tracklet& a, const Tracklet& b) { return a.firstClusterIndex < b.firstClusterIndex || (a.firstClusterIndex == b.firstClusterIndex && a.secondClusterIndex < b.secondClusterIndex); }; +// auto equal_tracklets = [] GPUhdni()(const Tracklet& a, const Tracklet& b) { return a.firstClusterIndex == b.firstClusterIndex && a.secondClusterIndex == b.secondClusterIndex; }; + +struct sort_tracklets { + GPUhd() bool operator()(const Tracklet& a, const Tracklet& b) { return a.firstClusterIndex < b.firstClusterIndex || (a.firstClusterIndex == b.firstClusterIndex && a.secondClusterIndex < b.secondClusterIndex); } +}; + +struct equal_tracklets { + GPUhd() bool operator()(const Tracklet& a, const Tracklet& b) { return a.firstClusterIndex == b.firstClusterIndex && a.secondClusterIndex == b.secondClusterIndex; } +}; + template struct pair_to_first : public thrust::unary_function, T1> { GPUhd() int operator()(const gpuPair& a) const @@ -196,6 +235,33 @@ struct is_valid_pair { } }; +GPUd() gpuSpan getPrimaryVertices(const int rof, + const int* roframesPV, + const int nROF, + const uint8_t* mask, + const Vertex* vertices) +{ + const int start_pv_id = roframesPV[rof]; + const int stop_rof = rof >= nROF - 1 ? nROF : rof + 1; + size_t delta = mask[rof] ? roframesPV[stop_rof] - start_pv_id : 0; // return empty span if ROF is excluded + return gpuSpan(&vertices[start_pv_id], delta); +}; + +GPUd() gpuSpan getClustersOnLayer(const int rof, + const int totROFs, + const int layer, + const int** roframesClus, + const Cluster** clusters) +{ + if (rof < 0 || rof >= totROFs) { + return gpuSpan(); + } + const int start_clus_id{roframesClus[layer][rof]}; + const int stop_rof = rof >= totROFs - 1 ? totROFs : rof + 1; + const unsigned int delta = roframesClus[layer][stop_rof] - start_clus_id; + return gpuSpan(&(clusters[layer][start_clus_id]), delta); +} + template GPUg() void fitTrackSeedsKernel( CellSeed* trackSeeds, @@ -314,8 +380,8 @@ GPUg() void computeLayerCellsKernel( const Cluster** sortedClusters, const Cluster** unsortedClusters, const TrackingFrameInfo** tfInfo, - const Tracklet** tracklets, - const int** trackletsLUT, + Tracklet** tracklets, + int** trackletsLUT, const int nTrackletsCurrent, const int layer, CellSeed* cells, @@ -331,8 +397,8 @@ GPUg() void computeLayerCellsKernel( for (int iCurrentTrackletIndex = blockIdx.x * blockDim.x + threadIdx.x; iCurrentTrackletIndex < nTrackletsCurrent; iCurrentTrackletIndex += blockDim.x * gridDim.x) { const Tracklet& currentTracklet = tracklets[layer][iCurrentTrackletIndex]; const int nextLayerClusterIndex{currentTracklet.secondClusterIndex}; - const int nextLayerFirstTrackletIndex{trackletsLUT[layer][nextLayerClusterIndex]}; - const int nextLayerLastTrackletIndex{trackletsLUT[layer][nextLayerClusterIndex + 1]}; + const int nextLayerFirstTrackletIndex{trackletsLUT[layer + 1][nextLayerClusterIndex]}; + const int nextLayerLastTrackletIndex{trackletsLUT[layer + 1][nextLayerClusterIndex + 1]}; if (nextLayerFirstTrackletIndex == nextLayerLastTrackletIndex) { continue; } @@ -342,7 +408,7 @@ GPUg() void computeLayerCellsKernel( break; } const Tracklet& nextTracklet = tracklets[layer + 1][iNextTrackletIndex]; - const float deltaTanLambda{o2::gpu::GPUCommonMath::Abs(currentTracklet.tanLambda - nextTracklet.tanLambda)}; + const float deltaTanLambda{o2::gpu::CAMath::Abs(currentTracklet.tanLambda - nextTracklet.tanLambda)}; if (deltaTanLambda / cellDeltaTanLambdaSigma < nSigmaCut) { const int clusId[3]{ @@ -394,35 +460,124 @@ GPUg() void computeLayerCellsKernel( } } -///////////////////////////////////////// -// Debug Kernels -///////////////////////////////////////// -GPUd() const int4 getBinsRect(const Cluster& currentCluster, const int layerIndex, - const o2::its::IndexTableUtils& utils, - const float z1, const float z2, float maxdeltaz, float maxdeltaphi) +template +GPUg() void computeLayerTrackletsMultiROFKernel( + const IndexTableUtils* utils, + const uint8_t* multMask, + const int layerIndex, + const int startROF, + const int endROF, + const int totalROFs, + const int deltaROF, + const Vertex* vertices, + const int* rofPV, + const int nVertices, + const int vertexId, + const Cluster** clusters, // Input data rof0 + const int** ROFClusters, // Number of clusters on layers per ROF + const unsigned char** usedClusters, // Used clusters + const int** indexTables, // Input data rof0-delta getNphiBins()}; + const int zBins{utils->getNzBins()}; + for (unsigned int iROF{blockIdx.x}; iROF < endROF - startROF; iROF += gridDim.x) { + const short rof0 = iROF + startROF; + auto primaryVertices = getPrimaryVertices(rof0, rofPV, totalROFs, multMask, vertices); + const auto startVtx{vertexId >= 0 ? vertexId : 0}; + const auto endVtx{vertexId >= 0 ? o2::gpu::CAMath::Min(vertexId + 1, static_cast(primaryVertices.size())) : static_cast(primaryVertices.size())}; + const short minROF = o2::gpu::CAMath::Max(startROF, static_cast(rof0 - deltaROF)); + const short maxROF = o2::gpu::CAMath::Min(endROF - 1, static_cast(rof0 + deltaROF)); + auto clustersCurrentLayer = getClustersOnLayer(rof0, totalROFs, layerIndex, ROFClusters, clusters); + if (clustersCurrentLayer.empty()) { + continue; + } - if (zRangeMax < -LayersZCoordinate()[layerIndex + 1] || - zRangeMin > LayersZCoordinate()[layerIndex + 1] || zRangeMin > zRangeMax) { + for (int currentClusterIndex = threadIdx.x; currentClusterIndex < clustersCurrentLayer.size(); currentClusterIndex += blockDim.x) { + unsigned int storedTracklets{0}; + auto currentCluster{clustersCurrentLayer[currentClusterIndex]}; + const int currentSortedIndex{ROFClusters[layerIndex][rof0] + currentClusterIndex}; + if (usedClusters[layerIndex][currentCluster.clusterId]) { + continue; + } - return getEmptyBinsRect(); - } + const float inverseR0{1.f / currentCluster.radius}; + for (int iV{startVtx}; iV < endVtx; ++iV) { + auto& primaryVertex{primaryVertices[iV]}; + if (primaryVertex.isFlagSet(2) && iteration != 3) { + continue; + } + const float resolution = o2::gpu::CAMath::Sqrt(Sq(resolutionPV) / primaryVertex.getNContributors() + Sq(positionResolution)); + const float tanLambda{(currentCluster.zCoordinate - primaryVertex.getZ()) * inverseR0}; + const float zAtRmin{tanLambda * (minR - currentCluster.radius) + currentCluster.zCoordinate}; + const float zAtRmax{tanLambda * (maxR - currentCluster.radius) + currentCluster.zCoordinate}; + const float sqInverseDeltaZ0{1.f / (Sq(currentCluster.zCoordinate - primaryVertex.getZ()) + 2.e-8f)}; /// protecting from overflows adding the detector resolution + const float sigmaZ{o2::gpu::CAMath::Sqrt(Sq(resolution) * Sq(tanLambda) * ((Sq(inverseR0) + sqInverseDeltaZ0) * Sq(meanDeltaR) + 1.f) + Sq(meanDeltaR * MSAngle))}; + const int4 selectedBinsRect{getBinsRect(currentCluster, layerIndex, *utils, zAtRmin, zAtRmax, sigmaZ * NSigmaCut, phiCut)}; + if (selectedBinsRect.x == 0 && selectedBinsRect.y == 0 && selectedBinsRect.z == 0 && selectedBinsRect.w == 0) { + continue; + } + int phiBinsNum{selectedBinsRect.w - selectedBinsRect.y + 1}; - return int4{o2::gpu::GPUCommonMath::Max(0, utils.getZBinIndex(layerIndex + 1, zRangeMin)), - utils.getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMin)), - o2::gpu::GPUCommonMath::Min(ZBins - 1, utils.getZBinIndex(layerIndex + 1, zRangeMax)), - utils.getPhiBinIndex(math_utils::getNormalizedPhi(phiRangeMax))}; -} + if (phiBinsNum < 0) { + phiBinsNum += phiBins; + } -GPUhd() float Sq(float q) -{ - return q * q; + const int tableSize{phiBins * zBins + 1}; + for (short rof1{minROF}; rof1 <= maxROF; ++rof1) { + auto clustersNextLayer = getClustersOnLayer(rof1, totalROFs, layerIndex + 1, ROFClusters, clusters); + if (clustersNextLayer.empty()) { + continue; + } + for (int iPhiCount{0}; iPhiCount < phiBinsNum; iPhiCount++) { + int iPhiBin = (selectedBinsRect.y + iPhiCount) % phiBins; + const int firstBinIndex{utils->getBinIndex(selectedBinsRect.x, iPhiBin)}; + const int maxBinIndex{firstBinIndex + selectedBinsRect.z - selectedBinsRect.x + 1}; + const int firstRowClusterIndex = indexTables[layerIndex + 1][(rof1 - startROF) * tableSize + firstBinIndex]; + const int maxRowClusterIndex = indexTables[layerIndex + 1][(rof1 - startROF) * tableSize + maxBinIndex]; + for (int nextClusterIndex{firstRowClusterIndex}; nextClusterIndex < maxRowClusterIndex; ++nextClusterIndex) { + if (nextClusterIndex >= clustersNextLayer.size()) { + break; + } + const Cluster& nextCluster{clustersNextLayer[nextClusterIndex]}; + if (usedClusters[layerIndex + 1][nextCluster.clusterId]) { + continue; + } + const float deltaPhi{o2::gpu::CAMath::Abs(currentCluster.phi - nextCluster.phi)}; + const float deltaZ{o2::gpu::CAMath::Abs(tanLambda * (nextCluster.radius - currentCluster.radius) + currentCluster.zCoordinate - nextCluster.zCoordinate)}; + const int nextSortedIndex{ROFClusters[layerIndex + 1][rof1] + nextClusterIndex}; + if (deltaZ / sigmaZ < NSigmaCut && (deltaPhi < phiCut || o2::gpu::CAMath::Abs(deltaPhi - constants::math::TwoPi) < phiCut)) { + if constexpr (initRun) { + trackletsLUT[layerIndex][currentSortedIndex]++; // we need l0 as well for usual exclusive sums. + } else { + const float phi{o2::gpu::CAMath::ATan2(currentCluster.yCoordinate - nextCluster.yCoordinate, currentCluster.xCoordinate - nextCluster.xCoordinate)}; + const float tanL{(currentCluster.zCoordinate - nextCluster.zCoordinate) / (currentCluster.radius - nextCluster.radius)}; + new (tracklets[layerIndex] + trackletsLUT[layerIndex][currentSortedIndex] + storedTracklets) Tracklet{currentSortedIndex, nextSortedIndex, tanL, phi, rof0, rof1}; + } + ++storedTracklets; + } + } + } + } + } + } + } } +///////////////////////////////////////// +// Debug Kernels +///////////////////////////////////////// + template GPUd() void pPointer(T* ptr) { @@ -437,7 +592,6 @@ GPUg() void printPointersKernel(std::tuple args) std::apply(print_all, args); } -// Functors to sort tracklets template struct trackletSortEmptyFunctor : public thrust::binary_function { GPUhd() bool operator()(const T& lhs, const T& rhs) const @@ -454,7 +608,6 @@ struct trackletSortIndexFunctor : public thrust::binary_function { } }; -// Print layer buffer GPUg() void printBufferLayerOnThread(const int layer, const int* v, unsigned int size, const int len = 150, const unsigned int tId = 0) { if (blockIdx.x * blockDim.x + threadIdx.x == tId) { @@ -494,52 +647,12 @@ GPUg() void printBufferPointersLayerOnThread(const int layer, void** v, unsigned } } -// Dump vertices GPUg() void printVertices(const Vertex* v, unsigned int size, const unsigned int tId = 0) { if (blockIdx.x * blockDim.x + threadIdx.x == tId) { - printf("vertices: "); + printf("vertices: \n"); for (int i{0}; i < size; ++i) { - printf("x=%f y=%f z=%f\n", v[i].getX(), v[i].getY(), v[i].getZ()); - } - } -} - -// Dump tracklets -GPUg() void printTracklets(const Tracklet* t, - const int offset, - const int startRof, - const int nrof, - const int* roFrameClustersCurrentLayer, // Number of clusters on layer 0 per ROF - const int* roFrameClustersNextLayer, // Number of clusters on layer 1 per ROF - const int maxClustersPerRof = 5e2, - const int maxTrackletsPerCluster = 50, - const unsigned int tId = 0) -{ - if (threadIdx.x == tId) { - auto offsetCurrent{roFrameClustersCurrentLayer[offset]}; - auto offsetNext{roFrameClustersNextLayer[offset]}; - auto offsetChunk{(startRof - offset) * maxClustersPerRof * maxTrackletsPerCluster}; - for (int i{offsetChunk}; i < offsetChunk + nrof * maxClustersPerRof * maxTrackletsPerCluster; ++i) { - if (t[i].firstClusterIndex != -1) { - t[i].dump(offsetCurrent, offsetNext); - } - } - } -} - -GPUg() void printTrackletsNotStrided(const Tracklet* t, - const int offset, - const int* roFrameClustersCurrentLayer, // Number of clusters on layer 0 per ROF - const int* roFrameClustersNextLayer, // Number of clusters on layer 1 per ROF - const int ntracklets, - const unsigned int tId = 0) -{ - if (threadIdx.x == tId) { - auto offsetCurrent{roFrameClustersCurrentLayer[offset]}; - auto offsetNext{roFrameClustersNextLayer[offset]}; - for (int i{0}; i < ntracklets; ++i) { - t[i].dump(offsetCurrent, offsetNext); + printf("\tx=%f y=%f z=%f\n", v[i].getX(), v[i].getY(), v[i].getZ()); } } } @@ -556,102 +669,25 @@ GPUg() void printNeighbours(const gpuPair* neighbours, } } -// Compute the tracklets for a given layer -template -GPUg() void computeLayerTrackletsKernelSingleRof( - const short rof0, - const short maxRofs, - const int layerIndex, - const Cluster* clustersCurrentLayer, // input data rof0 - const Cluster* clustersNextLayer, // input data rof0-delta * trkPars, - const IndexTableUtils* utils, - const unsigned int maxTrackletsPerCluster = 50) +GPUg() void printTrackletsLUTPerROF(const int layerId, + const int** ROFClusters, + int** luts, + const int tId = 0) { - for (int currentClusterIndex = blockIdx.x * blockDim.x + threadIdx.x; currentClusterIndex < currentLayerClustersSize; currentClusterIndex += blockDim.x * gridDim.x) { - unsigned int storedTracklets{0}; - const Cluster& currentCluster{clustersCurrentLayer[currentClusterIndex]}; - const int currentSortedIndex{roFrameClusters[rof0] + currentClusterIndex}; - if (usedClustersLayer[currentSortedIndex]) { - continue; - } - short minRof = (rof0 >= trkPars->DeltaROF) ? rof0 - trkPars->DeltaROF : 0; - short maxRof = (rof0 == static_cast(maxRofs - trkPars->DeltaROF)) ? rof0 : rof0 + trkPars->DeltaROF; - const float inverseR0{1.f / currentCluster.radius}; - for (int iPrimaryVertex{0}; iPrimaryVertex < nVertices; iPrimaryVertex++) { - const auto& primaryVertex{vertices[iPrimaryVertex]}; - if (primaryVertex.getX() == 0.f && primaryVertex.getY() == 0.f && primaryVertex.getZ() == 0.f) { - continue; - } - const float resolution{o2::gpu::GPUCommonMath::Sqrt(Sq(trkPars->PVres) / primaryVertex.getNContributors() + Sq(positionResolution))}; - const float tanLambda{(currentCluster.zCoordinate - primaryVertex.getZ()) * inverseR0}; - const float zAtRmin{tanLambda * (minR - currentCluster.radius) + currentCluster.zCoordinate}; - const float zAtRmax{tanLambda * (maxR - currentCluster.radius) + currentCluster.zCoordinate}; - const float sqInverseDeltaZ0{1.f / (Sq(currentCluster.zCoordinate - primaryVertex.getZ()) + 2.e-8f)}; /// protecting from overflows adding the detector resolution - const float sigmaZ{o2::gpu::CAMath::Sqrt(Sq(resolution) * Sq(tanLambda) * ((Sq(inverseR0) + sqInverseDeltaZ0) * Sq(meanDeltaR) + 1.f) + Sq(meanDeltaR * mSAngle))}; - - const int4 selectedBinsRect{getBinsRect(currentCluster, layerIndex, *utils, zAtRmin, zAtRmax, sigmaZ * trkPars->NSigmaCut, phiCut)}; - if (selectedBinsRect.x == 0 && selectedBinsRect.y == 0 && selectedBinsRect.z == 0 && selectedBinsRect.w == 0) { + if (blockIdx.x * blockDim.x + threadIdx.x == tId) { + for (auto rofId{0}; rofId < 2304; ++rofId) { + int nClus = ROFClusters[layerId][rofId + 1] - ROFClusters[layerId][rofId]; + if (!nClus) { continue; } - int phiBinsNum{selectedBinsRect.w - selectedBinsRect.y + 1}; - if (phiBinsNum < 0) { - phiBinsNum += trkPars->PhiBins; - } - constexpr int tableSize{256 * 128 + 1}; // hardcoded for the time being + printf("rof: %d (%d) ==> ", rofId, nClus); - for (short rof1{minRof}; rof1 <= maxRof; ++rof1) { - if (!(roFrameClustersNext[rof1 + 1] - roFrameClustersNext[rof1])) { // number of clusters on next layer > 0 - continue; - } - for (int iPhiCount{0}; iPhiCount < phiBinsNum; iPhiCount++) { - int iPhiBin = (selectedBinsRect.y + iPhiCount) % trkPars->PhiBins; - const int firstBinIndex{utils->getBinIndex(selectedBinsRect.x, iPhiBin)}; - const int maxBinIndex{firstBinIndex + selectedBinsRect.z - selectedBinsRect.x + 1}; - const int firstRowClusterIndex = indexTable[rof1 * tableSize + firstBinIndex]; - const int maxRowClusterIndex = indexTable[rof1 * tableSize + maxBinIndex]; - for (int iNextCluster{firstRowClusterIndex}; iNextCluster < maxRowClusterIndex; ++iNextCluster) { - if (iNextCluster >= (roFrameClustersNext[rof1 + 1] - roFrameClustersNext[rof1])) { - break; - } - const Cluster& nextCluster{getPtrFromRuler(rof1, clustersNextLayer, roFrameClustersNext)[iNextCluster]}; - if (usedClustersNextLayer[nextCluster.clusterId]) { - continue; - } - const float deltaPhi{o2::gpu::GPUCommonMath::Abs(currentCluster.phi - nextCluster.phi)}; - const float deltaZ{o2::gpu::GPUCommonMath::Abs(tanLambda * (nextCluster.radius - currentCluster.radius) + currentCluster.zCoordinate - nextCluster.zCoordinate)}; - - if (deltaZ / sigmaZ < trkPars->NSigmaCut && (deltaPhi < phiCut || o2::gpu::GPUCommonMath::Abs(deltaPhi - constants::math::TwoPi) < phiCut)) { - trackletsLookUpTable[currentSortedIndex]++; // Race-condition safe - const float phi{o2::gpu::GPUCommonMath::ATan2(currentCluster.yCoordinate - nextCluster.yCoordinate, currentCluster.xCoordinate - nextCluster.xCoordinate)}; - const float tanL{(currentCluster.zCoordinate - nextCluster.zCoordinate) / (currentCluster.radius - nextCluster.radius)}; - const unsigned int stride{currentClusterIndex * maxTrackletsPerCluster}; - new (tracklets + stride + storedTracklets) Tracklet{currentSortedIndex, roFrameClustersNext[rof1] + iNextCluster, tanL, phi, rof0, rof1}; - ++storedTracklets; - } - } - } + for (int iC{0}; iC < nClus; ++iC) { + int nT = luts[layerId][ROFClusters[layerId][rofId] + iC]; + printf("%d\t", nT); } + printf("\n"); } - // if (storedTracklets > maxTrackletsPerCluster) { - // printf("its-gpu-tracklet finder: found more tracklets per clusters (%d) than maximum set (%d), check the configuration!\n", maxTrackletsPerCluster, storedTracklets); - // } } } @@ -661,124 +697,7 @@ GPUg() void compileTrackletsLookupTableKernel(const Tracklet* tracklets, const int nTracklets) { for (int currentTrackletIndex = blockIdx.x * blockDim.x + threadIdx.x; currentTrackletIndex < nTracklets; currentTrackletIndex += blockDim.x * gridDim.x) { - auto& tracklet{tracklets[currentTrackletIndex]}; - if (tracklet.firstClusterIndex >= 0) { - atomicAdd(trackletsLookUpTable + tracklet.firstClusterIndex, 1); - } - } -} - -template -GPUg() void computeLayerTrackletsKernelMultipleRof( - const int layerIndex, - const int iteration, - const unsigned int startRofId, - const unsigned int rofSize, - const int maxRofs, - const Cluster* clustersCurrentLayer, // input data rof0 - const Cluster* clustersNextLayer, // input data rof0-delta * trkPars, - const IndexTableUtils* utils, - const unsigned int maxClustersPerRof = 5e2, - const unsigned int maxTrackletsPerCluster = 50) -{ - const int phiBins{utils->getNphiBins()}; - const int zBins{utils->getNzBins()}; - for (unsigned int iRof{blockIdx.x}; iRof < rofSize; iRof += gridDim.x) { - auto rof0 = iRof + startRofId; - auto nClustersCurrentLayerRof = o2::gpu::GPUCommonMath::Min(roFrameClustersCurrentLayer[rof0 + 1] - roFrameClustersCurrentLayer[rof0], (int)maxClustersPerRof); - // if (nClustersCurrentLayerRof > maxClustersPerRof) { - // printf("its-gpu-tracklet finder: on layer %d found more clusters per ROF (%d) than maximum set (%d), check the configuration!\n", layerIndex, nClustersCurrentLayerRof, maxClustersPerRof); - // } - auto* clustersCurrentLayerRof = clustersCurrentLayer + (roFrameClustersCurrentLayer[rof0] - roFrameClustersCurrentLayer[startRofId]); - auto nVerticesRof0 = nVertices[rof0 + 1] - nVertices[rof0]; - auto trackletsRof0 = tracklets + maxTrackletsPerCluster * maxClustersPerRof * iRof; - for (int currentClusterIndex = threadIdx.x; currentClusterIndex < nClustersCurrentLayerRof; currentClusterIndex += blockDim.x) { - unsigned int storedTracklets{0}; - const Cluster& currentCluster{clustersCurrentLayerRof[currentClusterIndex]}; - const int currentSortedIndex{roFrameClustersCurrentLayer[rof0] + currentClusterIndex}; - const int currentSortedIndexChunk{currentSortedIndex - roFrameClustersCurrentLayer[startRofId]}; - if (usedClustersLayer[currentSortedIndex]) { - continue; - } - - int minRof = (rof0 >= trkPars->DeltaROF) ? rof0 - trkPars->DeltaROF : 0; - int maxRof = (rof0 == maxRofs - trkPars->DeltaROF) ? rof0 : rof0 + trkPars->DeltaROF; // works with delta = {0, 1} - const float inverseR0{1.f / currentCluster.radius}; - - for (int iPrimaryVertex{0}; iPrimaryVertex < nVerticesRof0; iPrimaryVertex++) { - const auto& primaryVertex{vertices[nVertices[rof0] + iPrimaryVertex]}; - const float resolution{o2::gpu::GPUCommonMath::Sqrt(Sq(trkPars->PVres) / primaryVertex.getNContributors() + Sq(positionResolution))}; - const float tanLambda{(currentCluster.zCoordinate - primaryVertex.getZ()) * inverseR0}; - const float zAtRmin{tanLambda * (minR - currentCluster.radius) + currentCluster.zCoordinate}; - const float zAtRmax{tanLambda * (maxR - currentCluster.radius) + currentCluster.zCoordinate}; - const float sqInverseDeltaZ0{1.f / (Sq(currentCluster.zCoordinate - primaryVertex.getZ()) + 2.e-8f)}; /// protecting from overflows adding the detector resolution - const float sigmaZ{o2::gpu::CAMath::Sqrt(Sq(resolution) * Sq(tanLambda) * ((Sq(inverseR0) + sqInverseDeltaZ0) * Sq(meanDeltaR) + 1.f) + Sq(meanDeltaR * mSAngle))}; - - const int4 selectedBinsRect{getBinsRect(currentCluster, layerIndex, *utils, zAtRmin, zAtRmax, sigmaZ * trkPars->NSigmaCut, phiCut)}; - - if (selectedBinsRect.x == 0 && selectedBinsRect.y == 0 && selectedBinsRect.z == 0 && selectedBinsRect.w == 0) { - continue; - } - int phiBinsNum{selectedBinsRect.w - selectedBinsRect.y + 1}; - if (phiBinsNum < 0) { - phiBinsNum += trkPars->PhiBins; - } - const int tableSize{phiBins * zBins + 1}; - for (int rof1{minRof}; rof1 <= maxRof; ++rof1) { - auto nClustersNext{roFrameClustersNextLayer[rof1 + 1] - roFrameClustersNextLayer[rof1]}; - if (!nClustersNext) { // number of clusters on next layer > 0 - continue; - } - for (int iPhiCount{0}; iPhiCount < phiBinsNum; iPhiCount++) { - int iPhiBin = (selectedBinsRect.y + iPhiCount) % trkPars->PhiBins; - const int firstBinIndex{utils->getBinIndex(selectedBinsRect.x, iPhiBin)}; - const int maxBinIndex{firstBinIndex + selectedBinsRect.z - selectedBinsRect.x + 1}; - const int firstRowClusterIndex = indexTablesNext[(rof1 - startRofId) * tableSize + firstBinIndex]; - const int maxRowClusterIndex = indexTablesNext[(rof1 - startRofId) * tableSize + maxBinIndex]; - for (int iNextCluster{firstRowClusterIndex}; iNextCluster < maxRowClusterIndex; ++iNextCluster) { - if (iNextCluster >= nClustersNext) { - break; - } - auto nextClusterIndex{roFrameClustersNextLayer[rof1] - roFrameClustersNextLayer[startRofId] + iNextCluster}; - const Cluster& nextCluster{clustersNextLayer[nextClusterIndex]}; - if (usedClustersNextLayer[nextCluster.clusterId]) { - continue; - } - const float deltaPhi{o2::gpu::GPUCommonMath::Abs(currentCluster.phi - nextCluster.phi)}; - const float deltaZ{o2::gpu::GPUCommonMath::Abs(tanLambda * (nextCluster.radius - currentCluster.radius) + currentCluster.zCoordinate - nextCluster.zCoordinate)}; - - if ((deltaZ / sigmaZ < trkPars->NSigmaCut && (deltaPhi < phiCut || o2::gpu::GPUCommonMath::Abs(deltaPhi - constants::math::TwoPi) < phiCut))) { - const float phi{o2::gpu::GPUCommonMath::ATan2(currentCluster.yCoordinate - nextCluster.yCoordinate, currentCluster.xCoordinate - nextCluster.xCoordinate)}; - const float tanL{(currentCluster.zCoordinate - nextCluster.zCoordinate) / (currentCluster.radius - nextCluster.radius)}; - const unsigned int stride{currentClusterIndex * maxTrackletsPerCluster}; - if (storedTracklets < maxTrackletsPerCluster) { - new (trackletsRof0 + stride + storedTracklets) Tracklet{currentSortedIndexChunk, nextClusterIndex, tanL, phi, static_cast(rof0), static_cast(rof1)}; - } - // else { - // printf("its-gpu-tracklet-finder: on rof %d layer: %d: found more tracklets (%d) than maximum allowed per cluster. This is lossy!\n", rof0, layerIndex, storedTracklets); - // } - ++storedTracklets; - } - } - } - } - } - } + atomicAdd(&trackletsLookUpTable[tracklets[currentTrackletIndex].firstClusterIndex], 1); } } @@ -803,12 +722,176 @@ GPUg() void removeDuplicateTrackletsEntriesLUTKernel( } // namespace gpu +template +void countTrackletsInROFsHandler(const IndexTableUtils* utils, + const uint8_t* multMask, + const int startROF, + const int endROF, + const int maxROF, + const int deltaROF, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const int nVertices, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const int iteration, + const float NSigmaCut, + std::vector& phiCuts, + const float resolutionPV, + std::vector& minRs, + std::vector& maxRs, + std::vector& resolutions, + std::vector& radii, + std::vector& mulScatAng, + const int nBlocks, + const int nThreads) +{ + for (int iLayer = 0; iLayer < nLayers - 1; ++iLayer) { + gpu::computeLayerTrackletsMultiROFKernel<<>>( + utils, + multMask, + iLayer, + startROF, + endROF, + maxROF, + deltaROF, + vertices, + rofPV, + nVertices, + vertexId, + clusters, + ROFClusters, + usedClusters, + clustersIndexTables, + nullptr, + trackletsLUTs, + iteration, + NSigmaCut, + phiCuts[iLayer], + resolutionPV, + minRs[iLayer + 1], + maxRs[iLayer + 1], + resolutions[iLayer], + radii[iLayer + 1] - radii[iLayer], + mulScatAng[iLayer]); + void* d_temp_storage = nullptr; + size_t temp_storage_bytes = 0; + gpuCheckError(cub::DeviceScan::ExclusiveSum(d_temp_storage, // d_temp_storage + temp_storage_bytes, // temp_storage_bytes + trackletsLUTsHost[iLayer], // d_in + trackletsLUTsHost[iLayer], // d_out + nClusters[iLayer] + 1, // num_items + 0)); // NOLINT: this is the offset of the sum, not a pointer + discardResult(cudaMalloc(&d_temp_storage, temp_storage_bytes)); + gpuCheckError(cub::DeviceScan::ExclusiveSum(d_temp_storage, // d_temp_storage + temp_storage_bytes, // temp_storage_bytes + trackletsLUTsHost[iLayer], // d_in + trackletsLUTsHost[iLayer], // d_out + nClusters[iLayer] + 1, // num_items + 0)); // NOLINT: this is the offset of the sum, not a pointer + gpuCheckError(cudaFree(d_temp_storage)); + } +} + +template +void computeTrackletsInROFsHandler(const IndexTableUtils* utils, + const uint8_t* multMask, + const int startROF, + const int endROF, + const int maxROF, + const int deltaROF, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const int nVertices, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + Tracklet** tracklets, + gsl::span spanTracklets, + gsl::span nTracklets, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const int iteration, + const float NSigmaCut, + std::vector& phiCuts, + const float resolutionPV, + std::vector& minRs, + std::vector& maxRs, + std::vector& resolutions, + std::vector& radii, + std::vector& mulScatAng, + const int nBlocks, + const int nThreads) +{ + for (int iLayer = 0; iLayer < nLayers - 1; ++iLayer) { + gpu::computeLayerTrackletsMultiROFKernel<<>>(utils, + multMask, + iLayer, + startROF, + endROF, + maxROF, + deltaROF, + vertices, + rofPV, + nVertices, + vertexId, + clusters, + ROFClusters, + usedClusters, + clustersIndexTables, + tracklets, + trackletsLUTs, + iteration, + NSigmaCut, + phiCuts[iLayer], + resolutionPV, + minRs[iLayer + 1], + maxRs[iLayer + 1], + resolutions[iLayer], + radii[iLayer + 1] - radii[iLayer], + mulScatAng[iLayer]); + thrust::device_ptr tracklets_ptr(spanTracklets[iLayer]); + thrust::sort(thrust::device, tracklets_ptr, tracklets_ptr + nTracklets[iLayer], gpu::sort_tracklets()); + auto unique_end = thrust::unique(thrust::device, tracklets_ptr, tracklets_ptr + nTracklets[iLayer], gpu::equal_tracklets()); + nTracklets[iLayer] = unique_end - tracklets_ptr; + if (iLayer > 0) { + gpuCheckError(cudaMemset(trackletsLUTsHost[iLayer], 0, nClusters[iLayer] * sizeof(int))); + gpu::compileTrackletsLookupTableKernel<<>>(spanTracklets[iLayer], trackletsLUTsHost[iLayer], nTracklets[iLayer]); + void* d_temp_storage = nullptr; + size_t temp_storage_bytes = 0; + gpuCheckError(cub::DeviceScan::ExclusiveSum(d_temp_storage, // d_temp_storage + temp_storage_bytes, // temp_storage_bytes + trackletsLUTsHost[iLayer], // d_in + trackletsLUTsHost[iLayer], // d_out + nClusters[iLayer] + 1, // num_items + 0)); // NOLINT: this is the offset of the sum, not a pointer + discardResult(cudaMalloc(&d_temp_storage, temp_storage_bytes)); + gpuCheckError(cub::DeviceScan::ExclusiveSum(d_temp_storage, // d_temp_storage + temp_storage_bytes, // temp_storage_bytes + trackletsLUTsHost[iLayer], // d_in + trackletsLUTsHost[iLayer], // d_out + nClusters[iLayer] + 1, // num_items + 0)); // NOLINT: this is the offset of the sum, not a pointer + gpuCheckError(cudaFree(d_temp_storage)); + } + } +} + void countCellsHandler( const Cluster** sortedClusters, const Cluster** unsortedClusters, const TrackingFrameInfo** tfInfo, - const Tracklet** tracklets, - const int** trackletsLUT, + Tracklet** tracklets, + int** trackletsLUT, const int nTracklets, const int layer, CellSeed* cells, @@ -850,7 +933,6 @@ void countCellsHandler( cellsLUTsHost, // d_out nTracklets + 1, // num_items 0)); // NOLINT: this is the offset of the sum, not a pointer - // gpu::printBufferLayerOnThread<<<1, 1>>>(layer, cellsLUTsHost, nTracklets + 1); gpuCheckError(cudaFree(d_temp_storage)); } @@ -858,8 +940,8 @@ void computeCellsHandler( const Cluster** sortedClusters, const Cluster** unsortedClusters, const TrackingFrameInfo** tfInfo, - const Tracklet** tracklets, - const int** trackletsLUT, + Tracklet** tracklets, + int** trackletsLUT, const int nTracklets, const int layer, CellSeed* cells, @@ -963,8 +1045,8 @@ void computeCellNeighboursHandler(CellSeed** cellsLayersDevice, const int nThreads) { - gpu::computeLayerCellNeighboursKernel<<>>( + gpu::computeLayerCellNeighboursKernel<<>>( cellsLayersDevice, neighboursLUT, neighboursIndexTable, @@ -1032,4 +1114,65 @@ void trackSeedHandler(CellSeed* trackSeeds, gpuCheckError(cudaPeekAtLastError()); gpuCheckError(cudaDeviceSynchronize()); } -} // namespace o2::its + +template void countTrackletsInROFsHandler<7>(const IndexTableUtils* utils, + const uint8_t* multMask, + const int startROF, + const int endROF, + const int maxROF, + const int deltaROF, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const int nVertices, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const int iteration, + const float NSigmaCut, + std::vector& phiCuts, + const float resolutionPV, + std::vector& minRs, + std::vector& maxRs, + std::vector& resolutions, + std::vector& radii, + std::vector& mulScatAng, + const int nBlocks, + const int nThreads); + +template void computeTrackletsInROFsHandler<7>(const IndexTableUtils* utils, + const uint8_t* multMask, + const int startROF, + const int endROF, + const int maxROF, + const int deltaROF, + const int vertexId, + const Vertex* vertices, + const int* rofPV, + const int nVertices, + const Cluster** clusters, + std::vector nClusters, + const int** ROFClusters, + const unsigned char** usedClusters, + const int** clustersIndexTables, + Tracklet** tracklets, + gsl::span spanTracklets, + gsl::span nTracklets, + int** trackletsLUTs, + gsl::span trackletsLUTsHost, + const int iteration, + const float NSigmaCut, + std::vector& phiCuts, + const float resolutionPV, + std::vector& minRs, + std::vector& maxRs, + std::vector& resolutions, + std::vector& radii, + std::vector& mulScatAng, + const int nBlocks, + const int nThreads); +} // namespace o2::its \ No newline at end of file diff --git a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h index 906eb0fa5c21e..fa4f33782d16a 100644 --- a/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h +++ b/Detectors/ITSMFT/ITS/tracking/include/ITStracking/TimeFrame.h @@ -106,12 +106,16 @@ class TimeFrame float getBeamX() const; float getBeamY() const; - + std::vector& getMinRs() { return mMinR; } + std::vector& getMaxRs() { return mMaxR; } float getMinR(int layer) const { return mMinR[layer]; } float getMaxR(int layer) const { return mMaxR[layer]; } float getMSangle(int layer) const { return mMSangles[layer]; } + std::vector& getMSangles() { return mMSangles; } float getPhiCut(int layer) const { return mPhiCuts[layer]; } + std::vector& getPhiCuts() { return mPhiCuts; } float getPositionResolution(int layer) const { return mPositionResolution[layer]; } + std::vector& getPositionResolutions() { return mPositionResolution; } gsl::span getClustersOnLayer(int rofId, int layerId); gsl::span getClustersOnLayer(int rofId, int layerId) const; @@ -209,8 +213,8 @@ class TimeFrame const unsigned long long& getRoadLabel(int i) const; bool isRoadFake(int i) const; - void setMultiplicityCutMask(const std::vector& cutMask) { mMultiplicityCutMask = cutMask; } - void setROFMask(const std::vector& rofMask) { mROFMask = rofMask; } + 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); } @@ -289,6 +293,7 @@ class TimeFrame std::vector> mTracks; std::vector> mCellsNeighbours; std::vector> mCellsLookupTable; + std::vector mMultiplicityCutMask; const o2::base::PropagatorImpl* mPropagatorDevice = nullptr; // Needed only for GPU protected: @@ -311,8 +316,8 @@ class TimeFrame std::vector mPhiCuts; std::vector mPositionResolution; std::vector mClusterSize; - std::vector mMultiplicityCutMask; - std::vector mROFMask; + + std::vector mROFMask; std::vector> mPValphaX; /// PV x and alpha for track propagation std::vector> mTrackletLabels; std::vector> mCellLabels; @@ -439,33 +444,33 @@ inline gsl::span TimeFrame::getClustersPerROFrange(int rofMin, in return gsl::span(); } int startIdx{mROFramesClusters[layerId][rofMin]}; // First cluster of rofMin - int endIdx{mROFramesClusters[layerId][std::min(rofMin + range, mNrof)]}; + int endIdx{mROFramesClusters[layerId][o2::gpu::CAMath::Min(rofMin + range, mNrof)]}; return {&mClusters[layerId][startIdx], static_cast::size_type>(endIdx - startIdx)}; } inline gsl::span TimeFrame::getROFramesClustersPerROFrange(int rofMin, int range, int layerId) const { - int chkdRange{std::min(range, mNrof - rofMin)}; + int chkdRange{o2::gpu::CAMath::Min(range, mNrof - rofMin)}; return {&mROFramesClusters[layerId][rofMin], static_cast::size_type>(chkdRange)}; } inline gsl::span TimeFrame::getNClustersROFrange(int rofMin, int range, int layerId) const { - int chkdRange{std::min(range, mNrof - rofMin)}; + int chkdRange{o2::gpu::CAMath::Min(range, mNrof - rofMin)}; return {&mNClustersPerROF[layerId][rofMin], static_cast::size_type>(chkdRange)}; } inline int TimeFrame::getTotalClustersPerROFrange(int rofMin, int range, int layerId) const { int startIdx{rofMin}; // First cluster of rofMin - int endIdx{std::min(rofMin + range, mNrof)}; + int endIdx{o2::gpu::CAMath::Min(rofMin + range, mNrof)}; return mROFramesClusters[layerId][endIdx] - mROFramesClusters[layerId][startIdx]; } inline gsl::span TimeFrame::getIndexTablePerROFrange(int rofMin, int range, int layerId) const { const int iTableSize{mIndexTableUtils.getNphiBins() * mIndexTableUtils.getNzBins() + 1}; - int chkdRange{std::min(range, mNrof - rofMin)}; + int chkdRange{o2::gpu::CAMath::Min(range, mNrof - rofMin)}; return {&mIndexTables[layerId][rofMin * iTableSize], static_cast::size_type>(chkdRange * iTableSize)}; } diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx index da0abbae9dc1f..409b20ea23235 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackerTraits.cxx @@ -75,9 +75,9 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iROFslice, in for (int rof0{startROF}; rof0 < endROF; ++rof0) { gsl::span primaryVertices = mTrkParams[iteration].UseDiamond ? diamondSpan : tf->getPrimaryVertices(rof0); const int startVtx{iVertex >= 0 ? iVertex : 0}; - const int endVtx{iVertex >= 0 ? std::min(iVertex + 1, static_cast(primaryVertices.size())) : static_cast(primaryVertices.size())}; - int minRof = std::max(startROF, rof0 - mTrkParams[iteration].DeltaROF); - int maxRof = std::min(endROF - 1, rof0 + mTrkParams[iteration].DeltaROF); + const int endVtx{iVertex >= 0 ? o2::gpu::CAMath::Min(iVertex + 1, static_cast(primaryVertices.size())) : static_cast(primaryVertices.size())}; + int minRof = o2::gpu::CAMath::Max(startROF, rof0 - mTrkParams[iteration].DeltaROF); + int maxRof = o2::gpu::CAMath::Min(endROF - 1, rof0 + mTrkParams[iteration].DeltaROF); #pragma omp parallel for num_threads(mNThreads) for (int iLayer = 0; iLayer < mTrkParams[iteration].TrackletsPerRoad(); ++iLayer) { gsl::span layer0 = tf->getClustersOnLayer(rof0, iLayer); @@ -128,7 +128,6 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iROFslice, in if (layer1.empty()) { continue; } - for (int iPhiCount{0}; iPhiCount < phiBinsNum; iPhiCount++) { int iPhiBin = (selectedBinsRect.y + iPhiCount) % mTrkParams[iteration].PhiBins; const int firstBinIndex{tf->mIndexTableUtils.getBinIndex(selectedBinsRect.x, iPhiBin)}; @@ -145,9 +144,7 @@ void TrackerTraits::computeLayerTracklets(const int iteration, int iROFslice, in } const int firstRowClusterIndex = tf->getIndexTable(rof1, iLayer + 1)[firstBinIndex]; const int maxRowClusterIndex = tf->getIndexTable(rof1, iLayer + 1)[maxBinIndex]; - for (int iNextCluster{firstRowClusterIndex}; iNextCluster < maxRowClusterIndex; ++iNextCluster) { - if (iNextCluster >= (int)layer1.size()) { break; } @@ -668,7 +665,7 @@ void TrackerTraits::findRoads(const int iteration) if (rofs[1] != INT_MAX) { track.setNextROFbit(); } - mTimeFrame->getTracks(std::min(rofs[0], rofs[1])).emplace_back(track); + mTimeFrame->getTracks(o2::gpu::CAMath::Min(rofs[0], rofs[1])).emplace_back(track); } } } diff --git a/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx b/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx index f00d87164d7d6..5b8a9bb1cb0f2 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/TrackingInterface.cxx @@ -174,7 +174,7 @@ void ITSTrackingInterface::run(framework::ProcessingContext& pc) auto errorLogger = [&](std::string s) { LOG(error) << s; }; FastMultEst multEst; // mult estimator - std::vector processingMask, processUPCMask; + 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); diff --git a/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx b/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx index 4eaddc8385b8a..e87e2289b49e7 100644 --- a/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx +++ b/Detectors/ITSMFT/ITS/tracking/src/Vertexer.cxx @@ -90,7 +90,7 @@ float Vertexer::clustersToVerticesHybrid(std::function logg auto timeVertexingIteration = evaluateTask( &Vertexer::findVerticesHybrid, "Hybrid Vertexer vertex finding", [](std::string) {}, iteration); - printEpilog(logger, true, nTracklets01, nTracklets12, mTimeFrame->getNLinesTotal(), mTimeFrame->getTotVertIteration().size(), timeInit, timeTracklet, timeSelection, timeVertexing); + printEpilog(logger, true, nTracklets01, nTracklets12, mTimeFrame->getNLinesTotal(), mTimeFrame->getTotVertIteration()[iteration], timeInitIteration, timeTrackletIteration, timeSelectionIteration, timeVertexingIteration); timeInit += timeInitIteration; timeTracklet += timeTrackletIteration; timeSelection += timeSelectionIteration; @@ -142,9 +142,9 @@ void Vertexer::printEpilog(std::function logger, const float initT, const float trackletT, const float selecT, const float vertexT) { float total = initT + trackletT + selecT + vertexT; - logger(fmt::format(" - {}Vertexer: found {} | {} tracklets in: {} ms", isHybrid ? "Hybrid" : "", trackletN01, trackletN12, trackletT)); - logger(fmt::format(" - {}Vertexer: selected {} tracklets in: {} ms", isHybrid ? "Hybrid" : "", selectedN, selecT)); - logger(fmt::format(" - {}Vertexer: found {} vertices in: {} ms", isHybrid ? "Hybrid" : "", vertexN, vertexT)); + logger(fmt::format(" - {}Vertexer: found {} | {} tracklets in: {} ms", isHybrid ? "Hybrid " : "", trackletN01, trackletN12, trackletT)); + logger(fmt::format(" - {}Vertexer: selected {} tracklets in: {} ms", isHybrid ? "Hybrid " : "", selectedN, selecT)); + logger(fmt::format(" - {}Vertexer: found {} vertices in: {} ms", isHybrid ? "Hybrid " : "", vertexN, vertexT)); // logger(fmt::format(" - Timeframe {} vertexing completed in: {} ms, using {} thread(s).", mTimeFrameCounter++, total, mTraits->getNThreads())); } diff --git a/Detectors/ITSMFT/ITS/workflow/src/CookedTrackerSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/CookedTrackerSpec.cxx index 01e649f982896..4a0470adcf07a 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/CookedTrackerSpec.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/CookedTrackerSpec.cxx @@ -132,7 +132,7 @@ void CookedTrackerDPL::run(ProcessingContext& pc) const auto& multEstConf = FastMultEstConfig::Instance(); // parameters for mult estimation and cuts FastMultEst multEst; // mult estimator - std::vector processingMask; + std::vector processingMask; int cutVertexMult{0}, cutRandomMult = int(rofsinput.size()) - multEst.selectROFs(rofsinput, compClusters, physTriggers, processingMask); // auto processingMask_ephemeral = processingMask; From 8d75c84b1cd4a56055dc06476a8b258e4bc221b3 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Wed, 4 Dec 2024 07:34:47 +0100 Subject: [PATCH 0127/2180] AOD: switch TracksQA from 000 to 001 (#13758) --- Detectors/AOD/src/AODProducerWorkflowSpec.cxx | 241 ++++++++---------- .../include/Framework/AnalysisDataModel.h | 2 +- 2 files changed, 112 insertions(+), 131 deletions(-) diff --git a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx index 8a2443b57c7ff..8ee456634c1e1 100644 --- a/Detectors/AOD/src/AODProducerWorkflowSpec.cxx +++ b/Detectors/AOD/src/AODProducerWorkflowSpec.cxx @@ -358,47 +358,30 @@ void AODProducerWorkflowDPL::addToTracksExtraTable(TracksExtraCursorType& tracks template void AODProducerWorkflowDPL::addToTracksQATable(TracksQACursorType& tracksQACursor, TrackQA& trackQAInfoHolder) { - if constexpr (std::is_same_v) { // TODO remove remove once version changes - tracksQACursor( - trackQAInfoHolder.trackID, - truncateFloatFraction(trackQAInfoHolder.tpcTime0, mTPCTime0), - trackQAInfoHolder.tpcdcaR, - trackQAInfoHolder.tpcdcaZ, - trackQAInfoHolder.tpcClusterByteMask, - trackQAInfoHolder.tpcdEdxMax0R, - trackQAInfoHolder.tpcdEdxMax1R, - trackQAInfoHolder.tpcdEdxMax2R, - trackQAInfoHolder.tpcdEdxMax3R, - trackQAInfoHolder.tpcdEdxTot0R, - 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); - } else { - tracksQACursor( - trackQAInfoHolder.trackID, - trackQAInfoHolder.tpcTime0, - trackQAInfoHolder.tpcdcaR, - trackQAInfoHolder.tpcdcaZ, - trackQAInfoHolder.tpcClusterByteMask, - trackQAInfoHolder.tpcdEdxMax0R, - trackQAInfoHolder.tpcdEdxMax1R, - trackQAInfoHolder.tpcdEdxMax2R, - trackQAInfoHolder.tpcdEdxMax3R, - trackQAInfoHolder.tpcdEdxTot0R, - trackQAInfoHolder.tpcdEdxTot1R, - trackQAInfoHolder.tpcdEdxTot2R, - trackQAInfoHolder.tpcdEdxTot3R); - } + tracksQACursor( + trackQAInfoHolder.trackID, + truncateFloatFraction(trackQAInfoHolder.tpcTime0, mTPCTime0), + trackQAInfoHolder.tpcdcaR, + trackQAInfoHolder.tpcdcaZ, + trackQAInfoHolder.tpcClusterByteMask, + trackQAInfoHolder.tpcdEdxMax0R, + trackQAInfoHolder.tpcdEdxMax1R, + trackQAInfoHolder.tpcdEdxMax2R, + trackQAInfoHolder.tpcdEdxMax3R, + trackQAInfoHolder.tpcdEdxTot0R, + 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); } template @@ -2615,95 +2598,93 @@ AODProducerWorkflowDPL::TrackQA AODProducerWorkflowDPL::processBarrelTrackQA(int trackQAHolder.tpcdEdxTot2R = uint8_t(tpcOrig.getdEdx().dEdxTotOROC2 * dEdxNorm); trackQAHolder.tpcdEdxTot3R = uint8_t(tpcOrig.getdEdx().dEdxTotOROC3 * dEdxNorm); - if constexpr (std::is_same_v) { // TODO remove remove once version changes - // Add matching information at a reference point (defined by - // o2::aod::track::trackQARefRadius) in the same frame as the global track - // without material corrections and error propagation - if (auto itsContGID = data.getITSContributorGID(trackIndex); itsContGID.isIndexSet() && itsContGID.getSource() != GIndex::ITSAB) { - const auto& itsOrig = data.getITSTrack(itsContGID); - o2::track::TrackPar gloCopy = trackPar; - o2::track::TrackPar itsCopy = itsOrig; - o2::track::TrackPar tpcCopy = tpcOrig; - if (prop->propagateToX(gloCopy, o2::aod::track::trackQARefRadius, prop->getNominalBz(), o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, mMatCorr) && - prop->propagateToAlphaX(tpcCopy, gloCopy.getAlpha(), o2::aod::track::trackQARefRadius, false, o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, 1, mMatCorr) && - prop->propagateToAlphaX(itsCopy, gloCopy.getAlpha(), o2::aod::track::trackQARefRadius, false, o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, 1, mMatCorr)) { - // All tracks are now at the same radius and in the same frame and we can calculate the deltas wrt. to the global track - // The scale is defined by the global track scaling depending on beta0 - const float beta0 = std::sqrt(std::min(50.f / tpcOrig.getdEdx().dEdxMaxTPC, 1.f)); - const float qpt = gloCopy.getQ2Pt(); - const float x = qpt / beta0; - // scaling is defined as sigmaBins/sqrt(p0^2 + (p1 * q/pt / beta)^2) - auto scaleCont = [&x](int i) -> float { - return o2::aod::track::trackQAScaleBins / std::sqrt(o2::aod::track::trackQAScaleContP0[i] * o2::aod::track::trackQAScaleContP0[i] + (o2::aod::track::trackQAScaleContP1[i] * x) * (o2::aod::track::trackQAScaleContP1[i] * x)); - }; - auto scaleGlo = [&x](int i) -> float { - return o2::aod::track::trackQAScaleBins / std::sqrt(o2::aod::track::trackQAScaleGloP0[i] * o2::aod::track::trackQAScaleGloP0[i] + (o2::aod::track::trackQAScaleGloP1[i] * x) * (o2::aod::track::trackQAScaleGloP1[i] * x)); - }; - - // This allows to safely clamp any float to one byte, using the - // minmal/maximum values as under-/overflow borders and rounding to the nearest integer - auto safeInt8Clamp = [](auto value) -> int8_t { - using ValType = decltype(value); - return static_cast(TMath::Nint(std::clamp(value, static_cast(std::numeric_limits::min()), static_cast(std::numeric_limits::max())))); - }; - - // Calculate deltas for contributors - trackQAHolder.dRefContY = safeInt8Clamp((itsCopy.getY() - tpcCopy.getY()) * scaleCont(0)); - trackQAHolder.dRefContZ = safeInt8Clamp((itsCopy.getZ() - tpcCopy.getZ()) * scaleCont(1)); - trackQAHolder.dRefContSnp = safeInt8Clamp((itsCopy.getSnp() - tpcCopy.getSnp()) * scaleCont(2)); - trackQAHolder.dRefContTgl = safeInt8Clamp((itsCopy.getTgl() - tpcCopy.getTgl()) * scaleCont(3)); - trackQAHolder.dRefContQ2Pt = safeInt8Clamp((itsCopy.getQ2Pt() - tpcCopy.getQ2Pt()) * scaleCont(4)); - // Calculate deltas for global track against averaged contributors - trackQAHolder.dRefGloY = safeInt8Clamp(((itsCopy.getY() + tpcCopy.getY()) * 0.5f - gloCopy.getY()) * scaleGlo(0)); - trackQAHolder.dRefGloZ = safeInt8Clamp(((itsCopy.getZ() + tpcCopy.getZ()) * 0.5f - gloCopy.getZ()) * scaleGlo(1)); - trackQAHolder.dRefGloSnp = safeInt8Clamp(((itsCopy.getSnp() + tpcCopy.getSnp()) * 0.5f - gloCopy.getSnp()) * scaleGlo(2)); - trackQAHolder.dRefGloTgl = safeInt8Clamp(((itsCopy.getTgl() + tpcCopy.getTgl()) * 0.5f - gloCopy.getTgl()) * scaleGlo(3)); - trackQAHolder.dRefGloQ2Pt = safeInt8Clamp(((itsCopy.getQ2Pt() + tpcCopy.getQ2Pt()) * 0.5f - gloCopy.getQ2Pt()) * scaleGlo(4)); - - if (O2_ENUM_TEST_BIT(mStreamerMask, AODProducerStreamerMask::TrackQA)) { - (*mStreamer) << "trackQA" - << "trackITSOrig=" << itsOrig - << "trackTPCOrig=" << tpcOrig - << "trackITSTPCOrig=" << trackPar - << "trackITSProp=" << itsCopy - << "trackTPCProp=" << tpcCopy - << "trackITSTPCProp=" << gloCopy - << "refRadius=" << o2::aod::track::trackQARefRadius - << "scaleBins=" << o2::aod::track::trackQAScaleBins - << "scaleCont0=" << scaleCont(0) - << "scaleCont1=" << scaleCont(1) - << "scaleCont2=" << scaleCont(2) - << "scaleCont3=" << scaleCont(3) - << "scaleCont4=" << scaleCont(4) - << "scaleGlo0=" << scaleGlo(0) - << "scaleGlo1=" << scaleGlo(1) - << "scaleGlo2=" << scaleGlo(2) - << "scaleGlo3=" << scaleGlo(3) - << "scaleGlo4=" << scaleGlo(4) - << "trackQAHolder.tpcTime0=" << trackQAHolder.tpcTime0 - << "trackQAHolder.tpcdcaR=" << trackQAHolder.tpcdcaR - << "trackQAHolder.tpcdcaZ=" << trackQAHolder.tpcdcaZ - << "trackQAHolder.tpcdcaClusterByteMask=" << trackQAHolder.tpcClusterByteMask - << "trackQAHolder.tpcdEdxMax0R=" << trackQAHolder.tpcdEdxMax0R - << "trackQAHolder.tpcdEdxMax1R=" << trackQAHolder.tpcdEdxMax1R - << "trackQAHolder.tpcdEdxMax2R=" << trackQAHolder.tpcdEdxMax2R - << "trackQAHolder.tpcdEdxMax3R=" << trackQAHolder.tpcdEdxMax3R - << "trackQAHolder.tpcdEdxTot0R=" << trackQAHolder.tpcdEdxTot0R - << "trackQAHolder.tpcdEdxTot1R=" << trackQAHolder.tpcdEdxTot1R - << "trackQAHolder.tpcdEdxTot2R=" << trackQAHolder.tpcdEdxTot2R - << "trackQAHolder.tpcdEdxTot3R=" << trackQAHolder.tpcdEdxTot3R - << "trackQAHolder.dRefContY=" << trackQAHolder.dRefContY - << "trackQAHolder.dRefContZ=" << trackQAHolder.dRefContZ - << "trackQAHolder.dRefContSnp=" << trackQAHolder.dRefContSnp - << "trackQAHolder.dRefContTgl=" << trackQAHolder.dRefContTgl - << "trackQAHolder.dRefContQ2Pt=" << trackQAHolder.dRefContQ2Pt - << "trackQAHolder.dRefGloY=" << trackQAHolder.dRefGloY - << "trackQAHolder.dRefGloZ=" << trackQAHolder.dRefGloZ - << "trackQAHolder.dRefGloSnp=" << trackQAHolder.dRefGloSnp - << "trackQAHolder.dRefGloTgl=" << trackQAHolder.dRefGloTgl - << "trackQAHolder.dRefGloQ2Pt=" << trackQAHolder.dRefGloQ2Pt - << "\n"; - } + // Add matching information at a reference point (defined by + // o2::aod::track::trackQARefRadius) in the same frame as the global track + // without material corrections and error propagation + if (auto itsContGID = data.getITSContributorGID(trackIndex); itsContGID.isIndexSet() && itsContGID.getSource() != GIndex::ITSAB) { + const auto& itsOrig = data.getITSTrack(itsContGID); + o2::track::TrackPar gloCopy = trackPar; + o2::track::TrackPar itsCopy = itsOrig; + o2::track::TrackPar tpcCopy = tpcOrig; + if (prop->propagateToX(gloCopy, o2::aod::track::trackQARefRadius, prop->getNominalBz(), o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, mMatCorr) && + prop->propagateToAlphaX(tpcCopy, gloCopy.getAlpha(), o2::aod::track::trackQARefRadius, false, o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, 1, mMatCorr) && + prop->propagateToAlphaX(itsCopy, gloCopy.getAlpha(), o2::aod::track::trackQARefRadius, false, o2::base::Propagator::MAX_SIN_PHI, o2::base::Propagator::MAX_STEP, 1, mMatCorr)) { + // All tracks are now at the same radius and in the same frame and we can calculate the deltas wrt. to the global track + // The scale is defined by the global track scaling depending on beta0 + const float beta0 = std::sqrt(std::min(50.f / tpcOrig.getdEdx().dEdxMaxTPC, 1.f)); + const float qpt = gloCopy.getQ2Pt(); + const float x = qpt / beta0; + // scaling is defined as sigmaBins/sqrt(p0^2 + (p1 * q/pt / beta)^2) + auto scaleCont = [&x](int i) -> float { + return o2::aod::track::trackQAScaleBins / std::sqrt(o2::aod::track::trackQAScaleContP0[i] * o2::aod::track::trackQAScaleContP0[i] + (o2::aod::track::trackQAScaleContP1[i] * x) * (o2::aod::track::trackQAScaleContP1[i] * x)); + }; + auto scaleGlo = [&x](int i) -> float { + return o2::aod::track::trackQAScaleBins / std::sqrt(o2::aod::track::trackQAScaleGloP0[i] * o2::aod::track::trackQAScaleGloP0[i] + (o2::aod::track::trackQAScaleGloP1[i] * x) * (o2::aod::track::trackQAScaleGloP1[i] * x)); + }; + + // This allows to safely clamp any float to one byte, using the + // minmal/maximum values as under-/overflow borders and rounding to the nearest integer + auto safeInt8Clamp = [](auto value) -> int8_t { + using ValType = decltype(value); + return static_cast(TMath::Nint(std::clamp(value, static_cast(std::numeric_limits::min()), static_cast(std::numeric_limits::max())))); + }; + + // Calculate deltas for contributors + trackQAHolder.dRefContY = safeInt8Clamp((itsCopy.getY() - tpcCopy.getY()) * scaleCont(0)); + trackQAHolder.dRefContZ = safeInt8Clamp((itsCopy.getZ() - tpcCopy.getZ()) * scaleCont(1)); + trackQAHolder.dRefContSnp = safeInt8Clamp((itsCopy.getSnp() - tpcCopy.getSnp()) * scaleCont(2)); + trackQAHolder.dRefContTgl = safeInt8Clamp((itsCopy.getTgl() - tpcCopy.getTgl()) * scaleCont(3)); + trackQAHolder.dRefContQ2Pt = safeInt8Clamp((itsCopy.getQ2Pt() - tpcCopy.getQ2Pt()) * scaleCont(4)); + // Calculate deltas for global track against averaged contributors + trackQAHolder.dRefGloY = safeInt8Clamp(((itsCopy.getY() + tpcCopy.getY()) * 0.5f - gloCopy.getY()) * scaleGlo(0)); + trackQAHolder.dRefGloZ = safeInt8Clamp(((itsCopy.getZ() + tpcCopy.getZ()) * 0.5f - gloCopy.getZ()) * scaleGlo(1)); + trackQAHolder.dRefGloSnp = safeInt8Clamp(((itsCopy.getSnp() + tpcCopy.getSnp()) * 0.5f - gloCopy.getSnp()) * scaleGlo(2)); + trackQAHolder.dRefGloTgl = safeInt8Clamp(((itsCopy.getTgl() + tpcCopy.getTgl()) * 0.5f - gloCopy.getTgl()) * scaleGlo(3)); + trackQAHolder.dRefGloQ2Pt = safeInt8Clamp(((itsCopy.getQ2Pt() + tpcCopy.getQ2Pt()) * 0.5f - gloCopy.getQ2Pt()) * scaleGlo(4)); + + if (O2_ENUM_TEST_BIT(mStreamerMask, AODProducerStreamerMask::TrackQA)) { + (*mStreamer) << "trackQA" + << "trackITSOrig=" << itsOrig + << "trackTPCOrig=" << tpcOrig + << "trackITSTPCOrig=" << trackPar + << "trackITSProp=" << itsCopy + << "trackTPCProp=" << tpcCopy + << "trackITSTPCProp=" << gloCopy + << "refRadius=" << o2::aod::track::trackQARefRadius + << "scaleBins=" << o2::aod::track::trackQAScaleBins + << "scaleCont0=" << scaleCont(0) + << "scaleCont1=" << scaleCont(1) + << "scaleCont2=" << scaleCont(2) + << "scaleCont3=" << scaleCont(3) + << "scaleCont4=" << scaleCont(4) + << "scaleGlo0=" << scaleGlo(0) + << "scaleGlo1=" << scaleGlo(1) + << "scaleGlo2=" << scaleGlo(2) + << "scaleGlo3=" << scaleGlo(3) + << "scaleGlo4=" << scaleGlo(4) + << "trackQAHolder.tpcTime0=" << trackQAHolder.tpcTime0 + << "trackQAHolder.tpcdcaR=" << trackQAHolder.tpcdcaR + << "trackQAHolder.tpcdcaZ=" << trackQAHolder.tpcdcaZ + << "trackQAHolder.tpcdcaClusterByteMask=" << trackQAHolder.tpcClusterByteMask + << "trackQAHolder.tpcdEdxMax0R=" << trackQAHolder.tpcdEdxMax0R + << "trackQAHolder.tpcdEdxMax1R=" << trackQAHolder.tpcdEdxMax1R + << "trackQAHolder.tpcdEdxMax2R=" << trackQAHolder.tpcdEdxMax2R + << "trackQAHolder.tpcdEdxMax3R=" << trackQAHolder.tpcdEdxMax3R + << "trackQAHolder.tpcdEdxTot0R=" << trackQAHolder.tpcdEdxTot0R + << "trackQAHolder.tpcdEdxTot1R=" << trackQAHolder.tpcdEdxTot1R + << "trackQAHolder.tpcdEdxTot2R=" << trackQAHolder.tpcdEdxTot2R + << "trackQAHolder.tpcdEdxTot3R=" << trackQAHolder.tpcdEdxTot3R + << "trackQAHolder.dRefContY=" << trackQAHolder.dRefContY + << "trackQAHolder.dRefContZ=" << trackQAHolder.dRefContZ + << "trackQAHolder.dRefContSnp=" << trackQAHolder.dRefContSnp + << "trackQAHolder.dRefContTgl=" << trackQAHolder.dRefContTgl + << "trackQAHolder.dRefContQ2Pt=" << trackQAHolder.dRefContQ2Pt + << "trackQAHolder.dRefGloY=" << trackQAHolder.dRefGloY + << "trackQAHolder.dRefGloZ=" << trackQAHolder.dRefGloZ + << "trackQAHolder.dRefGloSnp=" << trackQAHolder.dRefGloSnp + << "trackQAHolder.dRefGloTgl=" << trackQAHolder.dRefGloTgl + << "trackQAHolder.dRefGloQ2Pt=" << trackQAHolder.dRefGloQ2Pt + << "\n"; } } } diff --git a/Framework/Core/include/Framework/AnalysisDataModel.h b/Framework/Core/include/Framework/AnalysisDataModel.h index e277925ed5603..a50e99fd95968 100644 --- a/Framework/Core/include/Framework/AnalysisDataModel.h +++ b/Framework/Core/include/Framework/AnalysisDataModel.h @@ -714,7 +714,7 @@ DECLARE_SOA_TABLE_VERSIONED(TracksQA_001, "AOD", "TRACKQA", 1, //! trackQA infor trackqa::IsDummy); -using TracksQAVersion = TracksQA_000; +using TracksQAVersion = TracksQA_001; using TracksQA = TracksQAVersion::iterator; namespace fwdtrack From 7eaa964a5b1a07203d45e7b699c845ca61ff9131 Mon Sep 17 00:00:00 2001 From: Anton Alkin Date: Wed, 4 Dec 2024 08:32:40 +0100 Subject: [PATCH 0128/2180] DPL Analysis: Table definition rewrite (#13664) --- .../AODProducerWorkflow/AODProducerHelpers.h | 4 +- .../AODProducerWorkflowSpec.h | 2 - Detectors/AOD/src/StandaloneAODProducer.cxx | 6 +- Framework/Core/CMakeLists.txt | 1 + Framework/Core/include/Framework/ASoA.h | 1851 +++++++++-------- .../Core/include/Framework/ASoAHelpers.h | 2 +- .../include/Framework/AnalysisDataModel.h | 79 +- .../Core/include/Framework/AnalysisHelpers.h | 334 +-- .../Core/include/Framework/AnalysisManagers.h | 75 +- .../Core/include/Framework/AnalysisTask.h | 107 +- .../Core/include/Framework/BinningPolicy.h | 8 +- .../Core/include/Framework/Expressions.h | 47 +- .../Core/include/Framework/GroupSlicer.h | 6 +- .../include/Framework/IndexBuilderHelpers.h | 12 +- .../Core/include/Framework/StringHelpers.h | 5 + .../Core/include/Framework/TableBuilder.h | 113 +- Framework/Core/src/AODReaderHelpers.cxx | 94 +- Framework/Core/src/ASoA.cxx | 15 +- Framework/Core/src/AnalysisDataModel.cxx | 14 +- Framework/Core/src/IndexBuilderHelpers.cxx | 4 +- Framework/Core/src/TableBuilder.cxx | 2 +- Framework/Core/src/verifyAODFile.cxx | 2 +- Framework/Core/test/benchmark_ASoA.cxx | 14 +- Framework/Core/test/benchmark_ASoAHelpers.cxx | 20 +- .../Core/test/benchmark_TableBuilder.cxx | 22 +- Framework/Core/test/test_ASoA.cxx | 135 +- Framework/Core/test/test_ASoAHelpers.cxx | 62 +- .../Core/test/test_AnalysisDataModel.cxx | 12 +- Framework/Core/test/test_AnalysisTask.cxx | 96 +- Framework/Core/test/test_Expressions.cxx | 2 +- Framework/Core/test/test_GroupSlicer.cxx | 22 +- .../Core/test/test_HistogramRegistry.cxx | 2 +- Framework/Core/test/test_IndexBuilder.cxx | 43 +- Framework/Core/test/test_Root2ArrowTable.cxx | 1 - Framework/Core/test/test_StaticFor.cxx | 8 +- Framework/Core/test/test_StringHelpers.cxx | 4 +- Framework/Core/test/test_TableBuilder.cxx | 12 +- Framework/Core/test/test_TableSpawner.cxx | 74 + Framework/Core/test/test_TreeToTable.cxx | 1 - Framework/Core/test/test_TypeTraits.cxx | 10 +- Framework/Foundation/include/Framework/Pack.h | 3 +- 41 files changed, 1917 insertions(+), 1409 deletions(-) create mode 100644 Framework/Core/test/test_TableSpawner.cxx diff --git a/Detectors/AOD/include/AODProducerWorkflow/AODProducerHelpers.h b/Detectors/AOD/include/AODProducerWorkflow/AODProducerHelpers.h index 9ef05096b2fd2..5351504443269 100644 --- a/Detectors/AOD/include/AODProducerWorkflow/AODProducerHelpers.h +++ b/Detectors/AOD/include/AODProducerWorkflow/AODProducerHelpers.h @@ -18,8 +18,6 @@ #include #include #include -#include -#include #include namespace o2::aodhelpers @@ -55,7 +53,7 @@ auto createTableCursor(framework::ProcessingContext& pc) framework::Produces c; c.resetCursor(pc.outputs() .make(framework::OutputForTable::ref())); - c.setLabel(o2::aod::MetadataTrait::metadata::tableLabel()); + c.setLabel(aod::label()); return c; } } // namespace o2::aodhelpers diff --git a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h index d9481917f9a05..eaaf2d9eaedd9 100644 --- a/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h +++ b/Detectors/AOD/include/AODProducerWorkflow/AODProducerWorkflowSpec.h @@ -21,7 +21,6 @@ #include "DataFormatsTRD/TrackTRD.h" #include "DetectorsBase/GRPGeomHelper.h" #include "DetectorsBase/Propagator.h" -#include "Framework/AnalysisHelpers.h" #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" #include "ReconstructionDataFormats/GlobalTrackID.h" @@ -36,7 +35,6 @@ #include #include #include -#include #include #include using namespace o2::framework; diff --git a/Detectors/AOD/src/StandaloneAODProducer.cxx b/Detectors/AOD/src/StandaloneAODProducer.cxx index a9f5a09141d8c..7ac59a556ac08 100644 --- a/Detectors/AOD/src/StandaloneAODProducer.cxx +++ b/Detectors/AOD/src/StandaloneAODProducer.cxx @@ -94,7 +94,7 @@ void fillMCollisionTable(o2::steer::MCKinematicsReader const& mcreader) TFile outfile("aod.root", "UPDATE"); { - TableToTree t2t(mccoltable, &outfile, aod::MetadataTrait::metadata::tableLabel()); + TableToTree t2t(mccoltable, &outfile, aod::description_str(aod::signature()).data()); t2t.addAllBranches(); t2t.process(); } @@ -200,12 +200,12 @@ void fillCollisionAndTrackTable() f.Close(); TFile outfile("aod.root", "RECREATE"); { - TableToTree t2t(colltable, &outfile, aod::MetadataTrait::metadata::tableLabel()); + TableToTree t2t(colltable, &outfile, aod::description_str(aod::signature()).data()); t2t.addAllBranches(); t2t.process(); } { - TableToTree t2t(tracktable, &outfile, "Tracks" /* aod::MetadataTrait::metadata::tableLabel() */); + TableToTree t2t(tracktable, &outfile, aod::description_str(aod::signature()).data()); t2t.addAllBranches(); t2t.process(); } diff --git a/Framework/Core/CMakeLists.txt b/Framework/Core/CMakeLists.txt index 02367afdcc556..5cdd1241ecfb0 100644 --- a/Framework/Core/CMakeLists.txt +++ b/Framework/Core/CMakeLists.txt @@ -236,6 +236,7 @@ add_executable(o2-test-framework-core test/test_Services.cxx test/test_StringHelpers.cxx test/test_StaticFor.cxx + test/test_TableSpawner.cxx test/test_TMessageSerializer.cxx test/test_TableBuilder.cxx test/test_TimeParallelPipelining.cxx diff --git a/Framework/Core/include/Framework/ASoA.h b/Framework/Core/include/Framework/ASoA.h index 34d18476e483d..25e64daefeba7 100644 --- a/Framework/Core/include/Framework/ASoA.h +++ b/Framework/Core/include/Framework/ASoA.h @@ -15,6 +15,7 @@ #include "Framework/Pack.h" #include "Framework/FunctionalHelpers.h" #include "Headers/DataHeader.h" +#include "Headers/DataHeaderHelpers.h" #include "Framework/CompilerBuiltins.h" #include "Framework/Traits.h" #include "Framework/Expressions.h" @@ -26,152 +27,362 @@ #include #include #include +#include #include #include #include #include -#define DECLARE_SOA_METADATA() \ - template \ - struct MetadataTrait { \ - using metadata = std::void_t; \ - }; - -#define DECLARE_SOA_ITERATOR_METADATA() \ - template \ - requires(o2::soa::is_iterator) \ - struct MetadataTrait { \ - using metadata = typename MetadataTrait::metadata; \ - }; - namespace o2::framework { using ListVector = std::vector>; std::string cutString(std::string&& str); +std::string strToUpper(std::string&& str); +} // namespace o2::framework -struct OriginEnc { - static constexpr size_t size = 4; - uint32_t value; - consteval OriginEnc(uint32_t v) : value{v} - { - } - consteval OriginEnc(std::string_view in) : value{0} +namespace o2::soa +{ +void accessingInvalidIndexFor(const char* getter); +void dereferenceWithWrongType(); +void missingFilterDeclaration(int hash, int ai); +void notBoundTable(const char* tableName); +} // namespace o2::soa + +namespace o2::soa +{ +/// Generic identifier for a table type +struct TableRef { + consteval TableRef() + : label_hash{0}, + desc_hash{0}, + origin_hash{0}, + version{0} { - for (auto i = 0U; i < (size < in.size() ? size : in.size()); ++i) { - value |= ((uint8_t)in[i]) << (8 * i); - } } - operator const char*() const + consteval TableRef(uint32_t _label, uint32_t _desc, uint32_t _origin, uint32_t _version) + : label_hash{_label}, + desc_hash{_desc}, + origin_hash{_origin}, + version{_version} { - return (const char*)(&value); } + uint32_t label_hash; + uint32_t desc_hash; + uint32_t origin_hash; + uint32_t version; - consteval operator o2::header::DataOrigin() + constexpr bool operator==(TableRef const& other) const noexcept { - return o2::header::DataOrigin{value}; + return (this->label_hash == other.label_hash) && + (this->desc_hash == other.desc_hash) && + (this->origin_hash == other.origin_hash) && + (this->version == other.version); } - consteval OriginEnc(OriginEnc const& other) = default; - consteval OriginEnc(OriginEnc&& other) noexcept = default; - consteval OriginEnc& operator=(OriginEnc const& other) = default; - consteval OriginEnc& operator=(OriginEnc&& other) noexcept = default; -}; -} // namespace o2::framework - -template <> -struct fmt::formatter { - char presentation = 's'; - constexpr auto parse(format_parse_context& ctx) + constexpr bool descriptionCompatible(TableRef const& other) const noexcept { - auto it = ctx.begin(), end = ctx.end(); - if (it != end && (*it == 's')) { - presentation = *it++; - } - - // Check if reached the end of the range: - if (it != end && *it != '}') { - throw format_error("invalid pick format"); - } - - // Return an iterator past the end of the parsed range: - return it; + return this->desc_hash == other.desc_hash; } - template - auto format(o2::framework::OriginEnc const& origin, FormatContext& ctx) + constexpr bool descriptionCompatible(uint32_t _desc_hash) const noexcept { - return fmt::format_to(ctx.out(), "{}", (std::string_view)origin); + return this->desc_hash == _desc_hash; } + + constexpr TableRef(TableRef const&) = default; + constexpr TableRef& operator=(TableRef const&) = default; + constexpr TableRef(TableRef&&) = default; + constexpr TableRef& operator=(TableRef&&) = default; }; -namespace o2::aod +/// Helpers to manipulate TableRef arrays +template ar1, std::array ar2> +consteval auto merge() { -DECLARE_SOA_METADATA(); + constexpr const int duplicates = std::ranges::count_if(ar2.begin(), ar2.end(), [&](TableRef const& a) { return std::any_of(ar1.begin(), ar1.end(), [&](TableRef const& e) { return e == a; }); }); + std::array out; + + auto pos = std::copy(ar1.begin(), ar1.end(), out.begin()); + std::copy_if(ar2.begin(), ar2.end(), pos, [&](TableRef const& a) { return std::none_of(ar1.begin(), ar1.end(), [&](TableRef const& e) { return e == a; }); }); + return out; } -namespace o2::soa +template ar1, std::array ar2, typename L> +consteval auto merge_if(L l) { -/// special case for the template with origin -template class Ref> -struct is_specialization_origin : std::false_type { -}; + constexpr const int to_remove = std::ranges::count_if(ar1.begin(), ar1.end(), [&](TableRef const& a) { return !l(a); }); + constexpr const int duplicates = std::ranges::count_if(ar2.begin(), ar2.end(), [&](TableRef const& a) { return std::any_of(ar1.begin(), ar1.end(), [&](TableRef const& e) { return e == a; }) || !l(a); }); + std::array out; -template