From 14781d4ca3003ef18eafe4e90d4211289131ac41 Mon Sep 17 00:00:00 2001 From: Felix Schlepper Date: Thu, 29 Jan 2026 15:01:45 +0100 Subject: [PATCH] ITS staggered clusterization Signed-off-by: Felix Schlepper --- .../src/StrangenessTrackingSpec.cxx | 2 - Detectors/ITSMFT/ITS/workflow/CMakeLists.txt | 2 - .../include/ITSWorkflow/ClustererSpec.h | 65 ---- .../ITS/workflow/src/ClusterWriterSpec.cxx | 72 ---- .../workflow/src/ClusterWriterWorkflow.cxx | 4 +- .../ITSMFT/ITS/workflow/src/ClustererSpec.cxx | 218 ------------ .../ITSMFT/ITS/workflow/src/RecoWorkflow.cxx | 8 +- Detectors/ITSMFT/MFT/workflow/CMakeLists.txt | 2 - .../include/MFTWorkflow/ClusterWriterSpec.h | 31 -- .../MFT/workflow/src/ClusterWriterSpec.cxx | 72 ---- .../ITSMFT/MFT/workflow/src/ClustererSpec.cxx | 212 ------------ .../ITSMFT/MFT/workflow/src/RecoWorkflow.cxx | 8 +- .../src/mft-cluster-writer-workflow.cxx | 4 +- .../include/ITSMFTReconstruction/Clusterer.h | 14 +- .../ITSMFTReconstruction/ClustererParam.h | 32 +- .../ITSMFTReconstruction/DigitPixelReader.h | 3 +- .../common/reconstruction/src/Clusterer.cxx | 36 +- .../reconstruction/src/DigitPixelReader.cxx | 11 + .../ITSMFT/common/workflow/CMakeLists.txt | 2 + .../ITSMFTWorkflow/ClusterReaderSpec.h | 52 ++- .../ITSMFTWorkflow}/ClusterWriterSpec.h | 16 +- .../include/ITSMFTWorkflow}/ClustererSpec.h | 28 +- .../common/workflow/src/ClusterReaderSpec.cxx | 166 +++++---- .../common/workflow/src/ClusterWriterSpec.cxx | 107 ++++++ .../common/workflow/src/ClustererSpec.cxx | 325 ++++++++++++++++++ .../ITS3/workflow/src/RecoWorkflow.cxx | 4 +- 26 files changed, 658 insertions(+), 838 deletions(-) delete mode 100644 Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClustererSpec.h delete mode 100644 Detectors/ITSMFT/ITS/workflow/src/ClusterWriterSpec.cxx delete mode 100644 Detectors/ITSMFT/ITS/workflow/src/ClustererSpec.cxx delete mode 100644 Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/ClusterWriterSpec.h delete mode 100644 Detectors/ITSMFT/MFT/workflow/src/ClusterWriterSpec.cxx delete mode 100644 Detectors/ITSMFT/MFT/workflow/src/ClustererSpec.cxx rename Detectors/ITSMFT/{ITS/workflow/include/ITSWorkflow => common/workflow/include/ITSMFTWorkflow}/ClusterWriterSpec.h (73%) rename Detectors/ITSMFT/{MFT/workflow/include/MFTWorkflow => common/workflow/include/ITSMFTWorkflow}/ClustererSpec.h (64%) create mode 100644 Detectors/ITSMFT/common/workflow/src/ClusterWriterSpec.cxx create mode 100644 Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx diff --git a/Detectors/GlobalTrackingWorkflow/src/StrangenessTrackingSpec.cxx b/Detectors/GlobalTrackingWorkflow/src/StrangenessTrackingSpec.cxx index 849964aeaf871..e313940b0a91e 100644 --- a/Detectors/GlobalTrackingWorkflow/src/StrangenessTrackingSpec.cxx +++ b/Detectors/GlobalTrackingWorkflow/src/StrangenessTrackingSpec.cxx @@ -17,10 +17,8 @@ #include "DataFormatsGlobalTracking/RecoContainer.h" #include "StrangenessTracking/StrangenessTrackingConfigParam.h" #include "GlobalTrackingWorkflow/StrangenessTrackingSpec.h" -#include "ITSWorkflow/ClusterWriterSpec.h" #include "ITSWorkflow/TrackerSpec.h" #include "ITSWorkflow/TrackReaderSpec.h" -#include "ITSMFTWorkflow/ClusterReaderSpec.h" #include "Framework/CCDBParamSpec.h" #include "DataFormatsParameters/GRPObject.h" diff --git a/Detectors/ITSMFT/ITS/workflow/CMakeLists.txt b/Detectors/ITSMFT/ITS/workflow/CMakeLists.txt index f0d50e59493d4..10e16e49d92b5 100644 --- a/Detectors/ITSMFT/ITS/workflow/CMakeLists.txt +++ b/Detectors/ITSMFT/ITS/workflow/CMakeLists.txt @@ -13,8 +13,6 @@ o2_add_library(ITSWorkflow TARGETVARNAME targetName SOURCES src/RecoWorkflow.cxx src/ClusterWriterWorkflow.cxx - src/ClustererSpec.cxx - src/ClusterWriterSpec.cxx src/TrackerSpec.cxx src/TrackWriterSpec.cxx src/TrackReaderSpec.cxx diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClustererSpec.h b/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClustererSpec.h deleted file mode 100644 index c5038c87fa467..0000000000000 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClustererSpec.h +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file ClustererSpec.h - -#ifndef O2_ITS_CLUSTERERDPL -#define O2_ITS_CLUSTERERDPL - -#include -#include "DetectorsBase/GRPGeomHelper.h" -#include "ITSMFTReconstruction/Clusterer.h" -#include "Framework/DataProcessorSpec.h" -#include "Framework/Task.h" - -using namespace o2::framework; - -namespace o2 -{ - -namespace itsmft -{ -class Clusterer; -} - -namespace its -{ - -class ClustererDPL : public Task -{ - public: - ClustererDPL(std::shared_ptr gr, bool useMC) : mGGCCDBRequest(gr), mUseMC(useMC) {} - ~ClustererDPL() override = default; - void init(InitContext& ic) final; - void run(ProcessingContext& pc) final; - void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final; - void endOfStream(o2::framework::EndOfStreamContext& ec) final; - - private: - void updateTimeDependentParams(ProcessingContext& pc); - - int mState = 0; - bool mUseMC = true; - bool mUseClusterDictionary = true; - int mNThreads = 1; - std::unique_ptr mFile = nullptr; - std::unique_ptr mClusterer = nullptr; - std::shared_ptr mGGCCDBRequest; -}; - -/// create a processor spec -/// run ITS cluster finder -framework::DataProcessorSpec getClustererSpec(bool useMC); - -} // namespace its -} // namespace o2 - -#endif /* O2_ITS_CLUSTERERDPL */ diff --git a/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterSpec.cxx deleted file mode 100644 index 4dffbaf88893c..0000000000000 --- a/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterSpec.cxx +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file ClusterWriterSpec.cxx - -#include - -#include "ITSWorkflow/ClusterWriterSpec.h" -#include "DPLUtils/MakeRootTreeWriterSpec.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "DataFormatsITSMFT/ROFRecord.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace its -{ - -template -using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; -using CompClusType = std::vector; -using PatternsType = std::vector; -using ROFrameRType = std::vector; -using LabelsType = o2::dataformats::MCTruthContainer; -using ROFRecLblT = std::vector; -using namespace o2::header; - -DataProcessorSpec getClusterWriterSpec(bool useMC) -{ - // Spectators for logging - // this is only to restore the original behavior - auto compClustersSize = std::make_shared(0); - auto compClustersSizeGetter = [compClustersSize](CompClusType const& compClusters) { - *compClustersSize = compClusters.size(); - }; - auto logger = [compClustersSize](std::vector const& rofs) { - LOG(info) << "ITSClusterWriter pulled " << *compClustersSize << " clusters, in " << rofs.size() << " RO frames"; - }; - return MakeRootTreeWriterSpec("its-cluster-writer", - "o2clus_its.root", - MakeRootTreeWriterSpec::TreeAttributes{"o2sim", "Tree with ITS clusters"}, - BranchDefinition{InputSpec{"compclus", "ITS", "COMPCLUSTERS", 0}, - "ITSClusterComp", - compClustersSizeGetter}, - BranchDefinition{InputSpec{"patterns", "ITS", "PATTERNS", 0}, - "ITSClusterPatt"}, - BranchDefinition{InputSpec{"ROframes", "ITS", "CLUSTERSROF", 0}, - "ITSClustersROF", - logger}, - BranchDefinition{InputSpec{"labels", "ITS", "CLUSTERSMCTR", 0}, - "ITSClusterMCTruth", - (useMC ? 1 : 0), // one branch if mc labels enabled - ""}, - BranchDefinition{InputSpec{"MC2ROframes", "ITS", "CLUSTERSMC2ROF", 0}, - "ITSClustersMC2ROF", - (useMC ? 1 : 0), // one branch if mc labels enabled - ""})(); -} - -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterWorkflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterWorkflow.cxx index ca5db7acd63e1..aba468b3e9460 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterWorkflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/ClusterWriterWorkflow.cxx @@ -12,7 +12,7 @@ /// @file ClusterWriterWorkflow.cxx #include "ITSWorkflow/ClusterWriterWorkflow.h" -#include "ITSWorkflow/ClusterWriterSpec.h" +#include "ITSMFTWorkflow/ClusterWriterSpec.h" namespace o2 { @@ -26,7 +26,7 @@ framework::WorkflowSpec getWorkflow(bool useMC) { framework::WorkflowSpec specs; - specs.emplace_back(o2::its::getClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getITSClusterWriterSpec(useMC)); return specs; } diff --git a/Detectors/ITSMFT/ITS/workflow/src/ClustererSpec.cxx b/Detectors/ITSMFT/ITS/workflow/src/ClustererSpec.cxx deleted file mode 100644 index d58e4f5d915c1..0000000000000 --- a/Detectors/ITSMFT/ITS/workflow/src/ClustererSpec.cxx +++ /dev/null @@ -1,218 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file ClustererSpec.cxx - -#include - -#include "Framework/ControlService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/CCDBParamSpec.h" -#include "ITSWorkflow/ClustererSpec.h" -#include "DataFormatsITSMFT/Digit.h" -#include "ITSMFTReconstruction/ChipMappingITS.h" -#include "ITSMFTReconstruction/ClustererParam.h" -#include "DataFormatsITSMFT/TopologyDictionary.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/ConstMCTruthContainer.h" -#include "DataFormatsITSMFT/ROFRecord.h" -#include "DataFormatsParameters/GRPObject.h" -#include "ITSMFTReconstruction/DigitPixelReader.h" -#include "ITSMFTBase/DPLAlpideParam.h" -#include "CommonConstants/LHCConstants.h" -#include "DetectorsCommonDataFormats/DetectorNameConf.h" - -using namespace o2::framework; -using namespace o2::itsmft; - -namespace o2 -{ -namespace its -{ - -void ClustererDPL::init(InitContext& ic) -{ - mClusterer = std::make_unique(); - mClusterer->setNChips(o2::itsmft::ChipMappingITS::getNChips()); - mUseClusterDictionary = !ic.options().get("ignore-cluster-dictionary"); - o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); - mNThreads = std::max(1, ic.options().get("nthreads")); - LOGP(info, "Initialising ITSClusterer with {} threads", mNThreads); - mState = 1; -} - -void ClustererDPL::run(ProcessingContext& pc) -{ - updateTimeDependentParams(pc); - auto digits = pc.inputs().get>("digits"); - auto rofs = pc.inputs().get>("ROframes"); - - gsl::span mc2rofs; - gsl::span labelbuffer; - if (mUseMC) { - labelbuffer = pc.inputs().get>("labels"); - mc2rofs = pc.inputs().get>("MC2ROframes"); - } - o2::dataformats::ConstMCTruthContainerView labels(labelbuffer); - - LOG(info) << "ITSClusterer pulled " << digits.size() << " digits, in " - << rofs.size() << " RO frames"; - LOG(info) << "ITSClusterer pulled " << labels.getNElements() << " labels "; - - o2::itsmft::DigitPixelReader reader; - reader.setSquashingDepth(mClusterer->getMaxROFDepthToSquash()); - reader.setSquashingDist(mClusterer->getMaxRowColDiffToMask()); // Sharing same parameter/logic with masking - reader.setMaxBCSeparationToSquash(mClusterer->getMaxBCSeparationToSquash()); - reader.setDigits(digits); - reader.setROFRecords(rofs); - if (mUseMC) { - reader.setMC2ROFRecords(mc2rofs); - reader.setDigitsMCTruth(labels.getIndexedSize() > 0 ? &labels : nullptr); - } - reader.init(); - auto orig = o2::header::gDataOriginITS; - std::vector clusCompVec; - std::vector clusROFVec; - std::vector clusPattVec; - - std::unique_ptr> clusterLabels; - if (mUseMC) { - clusterLabels = std::make_unique>(); - } - mClusterer->process(mNThreads, reader, &clusCompVec, &clusPattVec, &clusROFVec, clusterLabels.get()); - pc.outputs().snapshot(Output{orig, "COMPCLUSTERS", 0}, clusCompVec); - pc.outputs().snapshot(Output{orig, "CLUSTERSROF", 0}, clusROFVec); - pc.outputs().snapshot(Output{orig, "PATTERNS", 0}, clusPattVec); - - if (mUseMC) { - pc.outputs().snapshot(Output{orig, "CLUSTERSMCTR", 0}, *clusterLabels.get()); // at the moment requires snapshot - std::vector clusterMC2ROframes(mc2rofs.size()); - for (int i = mc2rofs.size(); i--;) { - clusterMC2ROframes[i] = mc2rofs[i]; // Simply, replicate it from digits ? - } - pc.outputs().snapshot(Output{orig, "CLUSTERSMC2ROF", 0}, clusterMC2ROframes); - } - - // TODO: in principle, after masking "overflow" pixels the MC2ROFRecord maxROF supposed to change, nominally to minROF - // -> consider recalculationg maxROF - LOG(info) << "ITSClusterer pushed " << clusCompVec.size() << " clusters, in " << clusROFVec.size() << " RO frames"; -} - -///_______________________________________ -void ClustererDPL::updateTimeDependentParams(ProcessingContext& pc) -{ - static bool initOnceDone = false; - o2::base::GRPGeomHelper::instance().checkUpdates(pc); - if (!initOnceDone) { // this params need to be queried only once - initOnceDone = true; - pc.inputs().get("cldict"); // just to trigger the finaliseCCDB - pc.inputs().get*>("alppar"); - pc.inputs().get*>("cluspar"); - mClusterer->setContinuousReadOut(o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(o2::detectors::DetID::ITS)); - // settings for the fired pixel overflow masking - const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); - const auto& clParams = o2::itsmft::ClustererParam::Instance(); - mClusterer->setDropHugeClusters(clParams.dropHugeClusters); - if (clParams.maxBCDiffToMaskBias > 0 && clParams.maxBCDiffToSquashBias > 0) { - LOGP(fatal, "maxBCDiffToMaskBias = {} and maxBCDiffToSquashBias = {} cannot be set at the same time. Either set masking or squashing with a BCDiff > 0", clParams.maxBCDiffToMaskBias, clParams.maxBCDiffToSquashBias); - } - auto nbc = clParams.maxBCDiffToMaskBias; - nbc += mClusterer->isContinuousReadOut() ? alpParams.roFrameLengthInBC : (alpParams.roFrameLengthTrig / o2::constants::lhc::LHCBunchSpacingNS); - mClusterer->setMaxBCSeparationToMask(nbc); - mClusterer->setMaxRowColDiffToMask(clParams.maxRowColDiffToMask); - // Squasher - int rofBC = mClusterer->isContinuousReadOut() ? alpParams.roFrameLengthInBC : (alpParams.roFrameLengthTrig / o2::constants::lhc::LHCBunchSpacingNS); // ROF length in BC - mClusterer->setMaxBCSeparationToSquash(rofBC + clParams.maxBCDiffToSquashBias); - int nROFsToSquash = 0; // squashing disabled if no reset due to maxSOTMUS>0. - if (clParams.maxSOTMUS > 0 && rofBC > 0) { - nROFsToSquash = 2 + int(clParams.maxSOTMUS / (rofBC * o2::constants::lhc::LHCBunchSpacingMUS)); // use squashing - } - mClusterer->setMaxROFDepthToSquash(clParams.maxBCDiffToSquashBias > 0 ? nROFsToSquash : 0); - mClusterer->print(); - } - // we may have other params which need to be queried regularly -} - -///_______________________________________ -void ClustererDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) -{ - if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { - return; - } - if (matcher == ConcreteDataMatcher("ITS", "CLUSDICT", 0)) { - LOG(info) << "cluster dictionary updated" << (!mUseClusterDictionary ? " but its using is disabled" : ""); - if (mUseClusterDictionary) { - mClusterer->setDictionary((const o2::itsmft::TopologyDictionary*)obj); - } - return; - } - // Note: strictly speaking, for Configurable params we don't need finaliseCCDB check, the singletons are updated at the CCDB fetcher level - if (matcher == ConcreteDataMatcher("ITS", "ALPIDEPARAM", 0)) { - LOG(info) << "Alpide param updated"; - const auto& par = o2::itsmft::DPLAlpideParam::Instance(); - par.printKeyValues(); - return; - } - if (matcher == ConcreteDataMatcher("ITS", "CLUSPARAM", 0)) { - LOG(info) << "Cluster param updated"; - const auto& par = o2::itsmft::ClustererParam::Instance(); - par.printKeyValues(); - return; - } -} - -DataProcessorSpec getClustererSpec(bool useMC) -{ - std::vector inputs; - inputs.emplace_back("digits", "ITS", "DIGITS", 0, Lifetime::Timeframe); - inputs.emplace_back("ROframes", "ITS", "DIGITSROF", 0, Lifetime::Timeframe); - inputs.emplace_back("cldict", "ITS", "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec("ITS/Calib/ClusterDictionary")); - inputs.emplace_back("cluspar", "ITS", "CLUSPARAM", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/ClustererParam")); - inputs.emplace_back("alppar", "ITS", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("ITS/Config/AlpideParam")); - auto ggRequest = std::make_shared(false, // orbitResetTime - true, // GRPECS=true - false, // GRPLHCIF - false, // GRPMagField - false, // askMatLUT - o2::base::GRPGeomRequest::None, // geometry - inputs, - true); - std::vector outputs; - outputs.emplace_back("ITS", "COMPCLUSTERS", 0, Lifetime::Timeframe); - outputs.emplace_back("ITS", "PATTERNS", 0, Lifetime::Timeframe); - outputs.emplace_back("ITS", "CLUSTERSROF", 0, Lifetime::Timeframe); - - if (useMC) { - inputs.emplace_back("labels", "ITS", "DIGITSMCTR", 0, Lifetime::Timeframe); - inputs.emplace_back("MC2ROframes", "ITS", "DIGITSMC2ROF", 0, Lifetime::Timeframe); - outputs.emplace_back("ITS", "CLUSTERSMCTR", 0, Lifetime::Timeframe); - outputs.emplace_back("ITS", "CLUSTERSMC2ROF", 0, Lifetime::Timeframe); - } - - return DataProcessorSpec{ - "its-clusterer", - inputs, - outputs, - AlgorithmSpec{adaptFromTask(ggRequest, useMC)}, - Options{ - {"ignore-cluster-dictionary", VariantType::Bool, false, {"do not use cluster dictionary, always store explicit patterns"}}, - {"nthreads", VariantType::Int, 1, {"Number of clustering threads"}}}}; -} - -///_______________________________________ -void ClustererDPL::endOfStream(o2::framework::EndOfStreamContext& ec) -{ - mClusterer->print(); -} - -} // namespace its -} // namespace o2 diff --git a/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx b/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx index 60e28556716f2..9f8cb6c83ef99 100644 --- a/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx +++ b/Detectors/ITSMFT/ITS/workflow/src/RecoWorkflow.cxx @@ -12,8 +12,8 @@ /// @file RecoWorkflow.cxx #include "ITSWorkflow/RecoWorkflow.h" -#include "ITSWorkflow/ClustererSpec.h" -#include "ITSWorkflow/ClusterWriterSpec.h" +#include "ITSMFTWorkflow/ClustererSpec.h" +#include "ITSMFTWorkflow/ClusterWriterSpec.h" #include "ITSWorkflow/TrackerSpec.h" #include "ITSWorkflow/TrackWriterSpec.h" #include "ITStracking/TrackingConfigParam.h" @@ -43,10 +43,10 @@ framework::WorkflowSpec getWorkflow(bool useMC, specs.emplace_back(o2::itsmft::getITSDigitReaderSpec(useMC, false, true, "itsdigits.root")); } if (!upstreamClusters) { - specs.emplace_back(o2::its::getClustererSpec(useMC)); + specs.emplace_back(o2::itsmft::getITSClustererSpec(useMC)); } if (!disableRootOutput) { - specs.emplace_back(o2::its::getClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getITSClusterWriterSpec(useMC)); } if ((trmode != TrackingMode::Off) && (TrackerParamConfig::Instance().trackingMode != TrackingMode::Off)) { if (useGPUWF) { diff --git a/Detectors/ITSMFT/MFT/workflow/CMakeLists.txt b/Detectors/ITSMFT/MFT/workflow/CMakeLists.txt index acb3d0b3e835f..b83699498a6b8 100644 --- a/Detectors/ITSMFT/MFT/workflow/CMakeLists.txt +++ b/Detectors/ITSMFT/MFT/workflow/CMakeLists.txt @@ -12,8 +12,6 @@ o2_add_library(MFTWorkflow TARGETVARNAME targetName SOURCES src/RecoWorkflow.cxx - src/ClustererSpec.cxx - src/ClusterWriterSpec.cxx src/TrackerSpec.cxx src/TrackReaderSpec.cxx src/TrackWriterSpec.cxx diff --git a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/ClusterWriterSpec.h b/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/ClusterWriterSpec.h deleted file mode 100644 index 51dc5a6481eb5..0000000000000 --- a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/ClusterWriterSpec.h +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file ClusterWriterSpec.h - -#ifndef O2_MFT_CLUSTERWRITER_H_ -#define O2_MFT_CLUSTERWRITER_H_ - -#include "Framework/DataProcessorSpec.h" - -namespace o2 -{ -namespace mft -{ - -/// create a processor spec -/// write MFT clusters a root file -framework::DataProcessorSpec getClusterWriterSpec(bool useMC); - -} // namespace mft -} // namespace o2 - -#endif /* O2_MFT_CLUSTERWRITER_H */ diff --git a/Detectors/ITSMFT/MFT/workflow/src/ClusterWriterSpec.cxx b/Detectors/ITSMFT/MFT/workflow/src/ClusterWriterSpec.cxx deleted file mode 100644 index c8061310e34f6..0000000000000 --- a/Detectors/ITSMFT/MFT/workflow/src/ClusterWriterSpec.cxx +++ /dev/null @@ -1,72 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file ClusterWriterSpec.cxx - -#include - -#include "MFTWorkflow/ClusterWriterSpec.h" -#include "DPLUtils/MakeRootTreeWriterSpec.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/MCTruthContainer.h" -#include "DataFormatsITSMFT/ROFRecord.h" - -using namespace o2::framework; - -namespace o2 -{ -namespace mft -{ - -template -using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; -using CompClusType = std::vector; -using PatternsType = std::vector; -using ROFrameRType = std::vector; -using LabelsType = o2::dataformats::MCTruthContainer; -using ROFRecLblT = std::vector; -using namespace o2::header; - -DataProcessorSpec getClusterWriterSpec(bool useMC) -{ - // Spectators for logging - // this is only to restore the original behavior - auto compClustersSize = std::make_shared(0); - auto compClustersSizeGetter = [compClustersSize](CompClusType const& compClusters) { - *compClustersSize = compClusters.size(); - }; - auto logger = [compClustersSize](std::vector const& rofs) { - LOG(info) << "MFTClusterWriter pulled " << *compClustersSize << " clusters, in " << rofs.size() << " RO frames"; - }; - return MakeRootTreeWriterSpec("mft-cluster-writer", - "mftclusters.root", - MakeRootTreeWriterSpec::TreeAttributes{"o2sim", "Tree with MFT clusters"}, - BranchDefinition{InputSpec{"compclus", "MFT", "COMPCLUSTERS", 0}, - "MFTClusterComp", - compClustersSizeGetter}, - BranchDefinition{InputSpec{"patterns", "MFT", "PATTERNS", 0}, - "MFTClusterPatt"}, - BranchDefinition{InputSpec{"ROframes", "MFT", "CLUSTERSROF", 0}, - "MFTClustersROF", - logger}, - BranchDefinition{InputSpec{"labels", "MFT", "CLUSTERSMCTR", 0}, - "MFTClusterMCTruth", - (useMC ? 1 : 0), // one branch if mc labels enabled - ""}, - BranchDefinition{InputSpec{"MC2ROframes", "MFT", "CLUSTERSMC2ROF", 0}, - "MFTClustersMC2ROF", - (useMC ? 1 : 0), // one branch if mc labels enabled - ""})(); -} - -} // namespace mft -} // namespace o2 diff --git a/Detectors/ITSMFT/MFT/workflow/src/ClustererSpec.cxx b/Detectors/ITSMFT/MFT/workflow/src/ClustererSpec.cxx deleted file mode 100644 index 766d7c1a0729e..0000000000000 --- a/Detectors/ITSMFT/MFT/workflow/src/ClustererSpec.cxx +++ /dev/null @@ -1,212 +0,0 @@ -// Copyright 2019-2020 CERN and copyright holders of ALICE O2. -// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. -// All rights not expressly granted are reserved. -// -// This software is distributed under the terms of the GNU General Public -// License v3 (GPL Version 3), copied verbatim in the file "COPYING". -// -// In applying this license CERN does not waive the privileges and immunities -// granted to it by virtue of its status as an Intergovernmental Organization -// or submit itself to any jurisdiction. - -/// @file ClustererSpec.cxx - -#include - -#include "Framework/ControlService.h" -#include "Framework/ConfigParamRegistry.h" -#include "Framework/CCDBParamSpec.h" -#include "MFTWorkflow/ClustererSpec.h" -#include "DataFormatsITSMFT/Digit.h" -#include "ITSMFTReconstruction/ChipMappingMFT.h" -#include "DataFormatsITSMFT/CompCluster.h" -#include "DataFormatsITSMFT/TopologyDictionary.h" -#include "SimulationDataFormat/MCCompLabel.h" -#include "SimulationDataFormat/ConstMCTruthContainer.h" -#include "DataFormatsITSMFT/ROFRecord.h" -#include "DataFormatsParameters/GRPObject.h" -#include "ITSMFTReconstruction/DigitPixelReader.h" -#include "DetectorsBase/GeometryManager.h" -#include "MFTBase/GeometryTGeo.h" -#include "ITSMFTBase/DPLAlpideParam.h" -#include "CommonConstants/LHCConstants.h" -#include "DetectorsCommonDataFormats/DetectorNameConf.h" -#include "ITSMFTReconstruction/ClustererParam.h" - -using namespace o2::framework; -using namespace o2::itsmft; - -namespace o2 -{ -namespace mft -{ - -void ClustererDPL::init(InitContext& ic) -{ - mClusterer = std::make_unique(); - mClusterer->setNChips(o2::itsmft::ChipMappingMFT::getNChips()); - mUseClusterDictionary = !ic.options().get("ignore-cluster-dictionary"); - o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); - mNThreads = std::max(1, ic.options().get("nthreads")); - mState = 1; -} - -void ClustererDPL::run(ProcessingContext& pc) -{ - updateTimeDependentParams(pc); - auto digits = pc.inputs().get>("digits"); - auto rofs = pc.inputs().get>("ROframes"); - - gsl::span mc2rofs; - gsl::span labelbuffer; - if (mUseMC) { - labelbuffer = pc.inputs().get>("labels"); - mc2rofs = pc.inputs().get>("MC2ROframes"); - } - const o2::dataformats::ConstMCTruthContainerView labels(labelbuffer); - - LOG(debug) << "MFTClusterer pulled " << digits.size() << " digits, in " - << rofs.size() << " RO frames"; - - o2::itsmft::DigitPixelReader reader; - reader.setSquashingDepth(mClusterer->getMaxROFDepthToSquash()); - reader.setSquashingDist(mClusterer->getMaxRowColDiffToMask()); // Sharing same parameter/logic with masking - reader.setMaxBCSeparationToSquash(mClusterer->getMaxBCSeparationToSquash()); - reader.setDigits(digits); - reader.setROFRecords(rofs); - if (mUseMC) { - reader.setMC2ROFRecords(mc2rofs); - reader.setDigitsMCTruth(labels.getIndexedSize() > 0 ? &labels : nullptr); - } - reader.init(); - auto orig = o2::header::gDataOriginMFT; - std::vector clusCompVec; - std::vector clusROFVec; - std::vector clusPattVec; - - std::unique_ptr> clusterLabels; - if (mUseMC) { - clusterLabels = std::make_unique>(); - } - mClusterer->process(mNThreads, reader, &clusCompVec, &clusPattVec, &clusROFVec, clusterLabels.get()); - pc.outputs().snapshot(Output{orig, "COMPCLUSTERS", 0}, clusCompVec); - pc.outputs().snapshot(Output{orig, "CLUSTERSROF", 0}, clusROFVec); - pc.outputs().snapshot(Output{orig, "PATTERNS", 0}, clusPattVec); - - if (mUseMC) { - pc.outputs().snapshot(Output{orig, "CLUSTERSMCTR", 0}, *clusterLabels.get()); // at the moment requires snapshot - std::vector clusterMC2ROframes(mc2rofs.size()); - for (int i = mc2rofs.size(); i--;) { - clusterMC2ROframes[i] = mc2rofs[i]; // Simply, replicate it from digits ? - } - pc.outputs().snapshot(Output{orig, "CLUSTERSMC2ROF", 0}, clusterMC2ROframes); - } - - // TODO: in principle, after masking "overflow" pixels the MC2ROFRecord maxROF supposed to change, nominally to minROF - // -> consider recalculationg maxROF - LOG(debug) << "MFTClusterer pushed " << clusCompVec.size() << " compressed clusters, in " << clusROFVec.size() << " RO frames"; -} - -///_______________________________________ -void ClustererDPL::updateTimeDependentParams(ProcessingContext& pc) -{ - o2::base::GRPGeomHelper::instance().checkUpdates(pc); - static bool initOnceDone = false; - if (!initOnceDone) { // this params need to be queried only once - initOnceDone = true; - pc.inputs().get("cldict"); // just to trigger the finaliseCCDB - pc.inputs().get*>("alppar"); - pc.inputs().get*>("cluspar"); - mClusterer->setContinuousReadOut(o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(o2::detectors::DetID::MFT)); - // settings for the fired pixel overflow masking - const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); - const auto& clParams = o2::itsmft::ClustererParam::Instance(); - if (clParams.maxBCDiffToMaskBias > 0 && clParams.maxBCDiffToSquashBias > 0) { - LOGP(fatal, "maxBCDiffToMaskBias = {} and maxBCDiffToSquashBias = {} cannot be set at the same time. Either set masking or squashing with a BCDiff > 0", clParams.maxBCDiffToMaskBias, clParams.maxBCDiffToSquashBias); - } - mClusterer->setDropHugeClusters(clParams.dropHugeClusters); - auto nbc = clParams.maxBCDiffToMaskBias; - nbc += mClusterer->isContinuousReadOut() ? alpParams.roFrameLengthInBC : (alpParams.roFrameLengthTrig / o2::constants::lhc::LHCBunchSpacingNS); - mClusterer->setMaxBCSeparationToMask(nbc); - mClusterer->setMaxRowColDiffToMask(clParams.maxRowColDiffToMask); - // Squasher - int rofBC = mClusterer->isContinuousReadOut() ? alpParams.roFrameLengthInBC : (alpParams.roFrameLengthTrig / o2::constants::lhc::LHCBunchSpacingNS); // ROF length in BC - mClusterer->setMaxBCSeparationToSquash(rofBC + clParams.maxBCDiffToSquashBias); - int nROFsToSquash = 0; // squashing disabled if no reset due to maxSOTMUS>0. - if (clParams.maxSOTMUS > 0 && rofBC > 0) { - nROFsToSquash = 2 + int(clParams.maxSOTMUS / (rofBC * o2::constants::lhc::LHCBunchSpacingMUS)); // use squashing - } - mClusterer->setMaxROFDepthToSquash(nROFsToSquash); - mClusterer->print(); - } - // we may have other params which need to be queried regularly -} - -///_______________________________________ -void ClustererDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) -{ - if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { - return; - } - if (matcher == ConcreteDataMatcher("MFT", "CLUSDICT", 0)) { - LOG(info) << "cluster dictionary updated" << (!mUseClusterDictionary ? " but its using is disabled" : ""); - if (mUseClusterDictionary) { - mClusterer->setDictionary((const TopologyDictionary*)obj); - } - return; - } - // Note: strictly speaking, for Configurable params we don't need finaliseCCDB check, the singletons are updated at the CCDB fetcher level - if (matcher == ConcreteDataMatcher("MFT", "ALPIDEPARAM", 0)) { - LOG(info) << "Alpide param updated"; - const auto& par = o2::itsmft::DPLAlpideParam::Instance(); - par.printKeyValues(); - return; - } - if (matcher == ConcreteDataMatcher("MFT", "CLUSPARAM", 0)) { - LOG(info) << "Cluster param updated"; - const auto& par = o2::itsmft::ClustererParam::Instance(); - par.printKeyValues(); - return; - } -} - -DataProcessorSpec getClustererSpec(bool useMC) -{ - std::vector inputs; - inputs.emplace_back("digits", "MFT", "DIGITS", 0, Lifetime::Timeframe); - inputs.emplace_back("ROframes", "MFT", "DIGITSROF", 0, Lifetime::Timeframe); - inputs.emplace_back("cldict", "MFT", "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec("MFT/Calib/ClusterDictionary")); - inputs.emplace_back("cluspar", "MFT", "CLUSPARAM", 0, Lifetime::Condition, ccdbParamSpec("MFT/Config/ClustererParam")); - inputs.emplace_back("alppar", "MFT", "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec("MFT/Config/AlpideParam")); - auto ggRequest = std::make_shared(false, // orbitResetTime - true, // GRPECS=true - false, // GRPLHCIF - false, // GRPMagField - false, // askMatLUT - o2::base::GRPGeomRequest::None, // geometry - inputs, - true); - std::vector outputs; - outputs.emplace_back("MFT", "COMPCLUSTERS", 0, Lifetime::Timeframe); - outputs.emplace_back("MFT", "PATTERNS", 0, Lifetime::Timeframe); - outputs.emplace_back("MFT", "CLUSTERSROF", 0, Lifetime::Timeframe); - - if (useMC) { - inputs.emplace_back("labels", "MFT", "DIGITSMCTR", 0, Lifetime::Timeframe); - inputs.emplace_back("MC2ROframes", "MFT", "DIGITSMC2ROF", 0, Lifetime::Timeframe); - outputs.emplace_back("MFT", "CLUSTERSMCTR", 0, Lifetime::Timeframe); - outputs.emplace_back("MFT", "CLUSTERSMC2ROF", 0, Lifetime::Timeframe); - } - - return DataProcessorSpec{ - "mft-clusterer", - inputs, - outputs, - AlgorithmSpec{adaptFromTask(ggRequest, useMC)}, - Options{ - {"ignore-cluster-dictionary", VariantType::Bool, false, {"do not use cluster dictionary, always store explicit patterns"}}, - {"nthreads", VariantType::Int, 1, {"Number of clustering threads"}}}}; -} - -} // namespace mft -} // namespace o2 diff --git a/Detectors/ITSMFT/MFT/workflow/src/RecoWorkflow.cxx b/Detectors/ITSMFT/MFT/workflow/src/RecoWorkflow.cxx index 615c9c1b275d4..5d85c0ef81670 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/RecoWorkflow.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/RecoWorkflow.cxx @@ -12,9 +12,9 @@ /// @file RecoWorkflow.cxx #include +#include "ITSMFTWorkflow/ClustererSpec.h" +#include "ITSMFTWorkflow/ClusterWriterSpec.h" #include "MFTWorkflow/RecoWorkflow.h" -#include "MFTWorkflow/ClustererSpec.h" -#include "MFTWorkflow/ClusterWriterSpec.h" #include "MFTWorkflow/TrackerSpec.h" #include "MFTWorkflow/TrackWriterSpec.h" #include "ITSMFTWorkflow/DigitReaderSpec.h" @@ -52,10 +52,10 @@ framework::WorkflowSpec getWorkflow( } } if (!upstreamClusters) { - specs.emplace_back(o2::mft::getClustererSpec(useMC)); + specs.emplace_back(o2::itsmft::getMFTClustererSpec(useMC)); } if (!disableRootOutput) { - specs.emplace_back(o2::mft::getClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getMFTClusterWriterSpec(useMC)); } if (runTracking) { diff --git a/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-writer-workflow.cxx b/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-writer-workflow.cxx index f42b2e0c92a4a..b656970693808 100644 --- a/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-writer-workflow.cxx +++ b/Detectors/ITSMFT/MFT/workflow/src/mft-cluster-writer-workflow.cxx @@ -9,7 +9,7 @@ // granted to it by virtue of its status as an Intergovernmental Organization // or submit itself to any jurisdiction. -#include "MFTWorkflow/ClusterWriterSpec.h" +#include "ITSMFTWorkflow/ClusterWriterSpec.h" #include "Framework/ConfigParamSpec.h" #include "Framework/CompletionPolicyHelpers.h" @@ -34,6 +34,6 @@ WorkflowSpec defineDataProcessing(ConfigContext const& configcontext) { auto useMC = !configcontext.options().get("disable-mc"); WorkflowSpec specs; - specs.emplace_back(o2::mft::getClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getMFTClusterWriterSpec(useMC)); return specs; } diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h index c66468905d0aa..0bdbb701a9356 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/Clusterer.h @@ -215,13 +215,15 @@ class Clusterer int getMaxRowColDiffToMask() const { return mMaxRowColDiffToMask; } void setMaxRowColDiffToMask(int v) { mMaxRowColDiffToMask = v; } - int getMaxROFDepthToSquash() const { return mSquashingDepth; } + int getMaxROFDepthToSquash(int layer = -1) const { return (layer < 0) ? mSquashingDepth : mSquashingLayerDepth[layer]; } void setMaxROFDepthToSquash(int v) { mSquashingDepth = v; } + void addMaxROFDepthToSquash(int v) { mSquashingLayerDepth.push_back(v); } - int getMaxBCSeparationToSquash() const { return mMaxBCSeparationToSquash; } + int getMaxBCSeparationToSquash(int layer = -1) const { return (layer < 0) ? mMaxBCSeparationToSquash : mMaxBCSeparationToSquashLayer[layer]; } void setMaxBCSeparationToSquash(int n) { mMaxBCSeparationToSquash = n; } + void addMaxBCSeparationToSquash(int n) { mMaxBCSeparationToSquashLayer.push_back(n); } - void print() const; + void print(bool showTiming = true) const; void clear(); void reset(); @@ -245,7 +247,7 @@ class Clusterer bool mContinuousReadout = true; ///< flag continuous readout bool mDropHugeClusters = false; ///< don't include clusters that would be split in more than one - ///< mask continuosly fired pixels in frames separated by less than this amount of BCs (fired from hit in prev. ROF) + ///< mask continuously fired pixels in frames separated by less than this amount of BCs (fired from hit in prev. ROF) int mMaxBCSeparationToMask = 6000. / o2::constants::lhc::LHCBunchSpacingNS + 10; int mMaxRowColDiffToMask = 0; ///< provide their difference in col/row is <= than this int mNHugeClus = 0; ///< number of encountered huge clusters @@ -253,6 +255,8 @@ class Clusterer ///< Squashing options int mSquashingDepth = 0; ///< squashing is applied to next N rofs int mMaxBCSeparationToSquash = 6000. / o2::constants::lhc::LHCBunchSpacingNS + 10; + std::vector mSquashingLayerDepth; + std::vector mMaxBCSeparationToSquashLayer; std::vector> mThreads; // buffers for threads std::vector mChips; // currently processed ROF's chips data @@ -288,7 +292,7 @@ void Clusterer::streamCluster(const std::vector& pixbuf, const std::a uint16_t row = bbox.rowMin, col = bbox.colMin; if (pattID == CompCluster::InvalidPatternID || pattIdConverter.isGroup(pattID)) { if (pattID != CompCluster::InvalidPatternID) { - // For groupped topologies, the reference pixel is the COG pixel + // For grouped topologies, the reference pixel is the COG pixel float xCOG = 0., zCOG = 0.; ClusterPattern::getCOG(rowSpanW, colSpanW, patt.data(), xCOG, zCOG); row += round(xCOG); diff --git a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ClustererParam.h b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ClustererParam.h index a71e5f3095b06..fef9a75765353 100644 --- a/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ClustererParam.h +++ b/Detectors/ITSMFT/common/reconstruction/include/ITSMFTReconstruction/ClustererParam.h @@ -29,16 +29,36 @@ template struct ClustererParam : public o2::conf::ConfigurableParamHelper> { static_assert(N == o2::detectors::DetID::ITS || N == o2::detectors::DetID::MFT, "only DetID::ITS or DetID:: MFT are allowed"); + static constexpr int getNLayers() + { + return N == o2::detectors::DetID::ITS ? 7 : 10; + } + static constexpr std::string_view getParamName() { return N == o2::detectors::DetID::ITS ? ParamName[0] : ParamName[1]; } - int maxRowColDiffToMask = DEFRowColDiffToMask(); ///< pixel may be masked as overflow if such a neighbour in prev frame was fired - int maxBCDiffToMaskBias = 10; ///< mask if 2 ROFs differ by <= StrobeLength + Bias BCs, use value <0 to disable masking - int maxBCDiffToSquashBias = -10; ///< squash if 2 ROFs differ by <= StrobeLength + Bias BCs, use value <0 to disable squashing - float maxSOTMUS = 8.; ///< max expected signal over threshold in \mus - bool dropHugeClusters = false; ///< option to drop huge clusters (mitigate beam background) + int maxRowColDiffToMask = DEFRowColDiffToMask(); ///< pixel may be masked as overflow if such a neighbour in prev frame was fired + int maxBCDiffToMaskBias = 10; ///< mask if 2 ROFs differ by <= StrobeLength + Bias BCs, use value <0 to disable masking + int maxBCDiffToSquashBias = -10; ///< squash if 2 ROFs differ by <= StrobeLength + Bias BCs, use value <0 to disable squashing + float maxSOTMUS = 8.; ///< max expected signal over threshold in \mus + bool dropHugeClusters = false; ///< option to drop huge clusters (mitigate beam background) + int maxBCDiffToSquashBiasLayer[getNLayers()] = {}; ///< squash mask per layer + int getMaxBCDiffToSquashBias(int layer) const noexcept + { + bool stag{false}; + for (int i{0}; i < getNLayers(); ++i) { + if (maxBCDiffToSquashBiasLayer[i] != 0) { + stag = true; + break; + } + } + if (stag) { + return maxBCDiffToSquashBiasLayer[layer]; + } + return maxBCDiffToSquashBias; + } O2ParamDef(ClustererParam, getParamName().data()); @@ -46,7 +66,7 @@ struct ClustererParam : public o2::conf::ConfigurableParamHelper(nFired, nThreads); #ifndef WITH_OPENMP nThreads = 1; #endif @@ -173,7 +168,7 @@ void Clusterer::ClustererThread::process(uint16_t chip, uint16_t nChips, CompClu const ConstMCTruth* labelsDigPtr, MCTruth* labelsClPtr, const ROFRecord& rofPtr) { if (stats.empty() || stats.back().firstChip + stats.back().nChips != chip) { // there is a jump, register new block - stats.emplace_back(ThreadStat{chip, 0, uint32_t(compClusPtr->size()), patternsPtr ? uint32_t(patternsPtr->size()) : 0, 0, 0}); + stats.emplace_back(ThreadStat{.firstChip = chip, .nChips = 0, .firstClus = uint32_t(compClusPtr->size()), .firstPatt = patternsPtr ? uint32_t(patternsPtr->size()) : 0, .nClus = 0, .nPatt = 0}); } for (int ic = 0; ic < nChips; ic++) { auto* curChipData = parent->mFiredChipsPtr[chip + ic]; @@ -476,22 +471,31 @@ void Clusterer::clear() } //__________________________________________________ -void Clusterer::print() const +void Clusterer::print(bool showsTiming) const { // print settings - LOGP(info, "Clusterizer squashes overflow pixels separated by {} BC and <= {} in row/col seeking down to {} neighbour ROFs", mMaxBCSeparationToSquash, mMaxRowColDiffToMask, mSquashingDepth); + if (mSquashingLayerDepth.empty()) { + LOGP(info, "Clusterizer squashes overflow pixels separated by {} BC and <= {} in row/col seeking down to {} neighbour ROFs", mMaxBCSeparationToSquash, mMaxRowColDiffToMask, mSquashingDepth); + } else { + LOGP(info, "Clusterizer squashes overflow pixels <= {} in row/col", mMaxRowColDiffToMask); + for (size_t i{0}; i < mSquashingLayerDepth.size(); ++i) { + LOGP(info, "\tlay:{} separated by {} BC seeking down to {} neighbour ROFs", i, mMaxBCSeparationToSquashLayer[i], mSquashingLayerDepth[i]); + } + } LOGP(info, "Clusterizer masks overflow pixels separated by < {} BC and <= {} in row/col", mMaxBCSeparationToMask, mMaxRowColDiffToMask); LOGP(info, "Clusterizer does {} drop huge clusters", mDropHugeClusters ? "" : "not"); + if (showsTiming) { #ifdef _PERFORM_TIMING_ - auto& tmr = const_cast(mTimer); // ugly but this is what root does internally - auto& tmrm = const_cast(mTimerMerge); - LOG(info) << "Inclusive clusterization timing (w/o disk IO): Cpu: " << tmr.CpuTime() - << " Real: " << tmr.RealTime() << " s in " << tmr.Counter() << " slots"; - LOG(info) << "Threads output merging timing : Cpu: " << tmrm.CpuTime() - << " Real: " << tmrm.RealTime() << " s in " << tmrm.Counter() << " slots"; + auto& tmr = const_cast(mTimer); // ugly but this is what root does internally + auto& tmrm = const_cast(mTimerMerge); + LOG(info) << "Inclusive clusterization timing (w/o disk IO): Cpu: " << tmr.CpuTime() + << " Real: " << tmr.RealTime() << " s in " << tmr.Counter() << " slots"; + LOG(info) << "Threads output merging timing : Cpu: " << tmrm.CpuTime() + << " Real: " << tmrm.RealTime() << " s in " << tmrm.Counter() << " slots"; #endif + } } //__________________________________________________ diff --git a/Detectors/ITSMFT/common/reconstruction/src/DigitPixelReader.cxx b/Detectors/ITSMFT/common/reconstruction/src/DigitPixelReader.cxx index b8d88a6fc4223..5c1dbde074649 100644 --- a/Detectors/ITSMFT/common/reconstruction/src/DigitPixelReader.cxx +++ b/Detectors/ITSMFT/common/reconstruction/src/DigitPixelReader.cxx @@ -330,3 +330,14 @@ void DigitPixelReader::clear() mROFRecVec = gsl::span(); mMC2ROFRecVec = gsl::span(); } + +//______________________________________________________________________________ +void DigitPixelReader::reset() +{ + clear(); + mSquashedDigitsMask.clear(); + mBookmarkNextROFs.clear(); + mIdDig = 0; + mIdROF = 0; + mIdROFLast = 0; +} diff --git a/Detectors/ITSMFT/common/workflow/CMakeLists.txt b/Detectors/ITSMFT/common/workflow/CMakeLists.txt index 63cd8d6c0bcee..ead08c4422260 100644 --- a/Detectors/ITSMFT/common/workflow/CMakeLists.txt +++ b/Detectors/ITSMFT/common/workflow/CMakeLists.txt @@ -11,6 +11,8 @@ o2_add_library(ITSMFTWorkflow SOURCES src/ClusterReaderSpec.cxx + src/ClusterWriterSpec.cxx + src/ClustererSpec.cxx src/DigitWriterSpec.cxx src/DigitReaderSpec.cxx src/STFDecoderSpec.cxx diff --git a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterReaderSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterReaderSpec.h index 99318df1cd9d9..82e3890de7475 100644 --- a/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterReaderSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterReaderSpec.h @@ -23,46 +23,51 @@ #include "DataFormatsITSMFT/CompCluster.h" #include "SimulationDataFormat/MCCompLabel.h" #include "SimulationDataFormat/MCTruthContainer.h" +#include "ITSMFTBase/DPLAlpideParam.h" #include "DataFormatsITSMFT/ROFRecord.h" #include "DetectorsCommonDataFormats/DetID.h" using namespace o2::framework; -namespace o2 -{ -namespace itsmft +namespace o2::itsmft { +template class ClusterReader : public Task { public: + static constexpr o2::detectors::DetID ID{N == o2::detectors::DetID::ITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT}; + static constexpr o2::header::DataOrigin Origin{(N == o2::detectors::DetID::ITS) ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; + static constexpr int NLayers{o2::itsmft::DPLAlpideParam::supportsStaggering() ? o2::itsmft::DPLAlpideParam::getNLayers() : 1}; + ClusterReader() = delete; - ClusterReader(o2::detectors::DetID id, bool useMC, bool usePatterns = true, bool triggers = true); + ClusterReader(bool useMC, bool usePatterns = true, bool triggers = true); ~ClusterReader() override = default; void init(InitContext& ic) final; void run(ProcessingContext& pc) final; protected: void connectTree(const std::string& filename); + template + void setBranchAddress(const std::string& base, Ptr& addr, int layer); + std::string getBranchName(const std::string& base, int index) const; - std::vector mClusROFRec, *mClusROFRecPtr = &mClusROFRec; - std::vector mClusterCompArray, *mClusterCompArrayPtr = &mClusterCompArray; - std::vector mPatternsArray, *mPatternsArrayPtr = &mPatternsArray; - o2::dataformats::MCTruthContainer mClusterMCTruth, *mClusterMCTruthPtr = &mClusterMCTruth; - std::vector mClusMC2ROFs, *mClusMC2ROFsPtr = &mClusMC2ROFs; - - o2::header::DataOrigin mOrigin = o2::header::gDataOriginInvalid; + std::array*, NLayers> mClusROFRec; + std::array*, NLayers> mClusterCompArray; + std::array*, NLayers> mPatternsArray; + std::array*, NLayers> mClusterMCTruth; + std::array*, NLayers> mClusMC2ROFs; std::unique_ptr mFile; std::unique_ptr mTree; - bool mUseMC = true; // use MC truth + bool mUseMC = true; // use MC truth bool mUsePatterns = true; // send patterns bool mTriggerOut = true; // send dummy triggers vector - std::string mDetName = ""; - std::string mDetNameLC = ""; - std::string mFileName = ""; + std::string mDetName; + std::string mDetNameLC; + std::string mFileName; std::string mClusTreeName = "o2sim"; std::string mClusROFBranchName = "ClustersROF"; std::string mClusterPattBranchName = "ClusterPatt"; @@ -71,24 +76,18 @@ class ClusterReader : public Task std::string mClustMC2ROFBranchName = "ClustersMC2ROF"; }; -class ITSClusterReader : public ClusterReader +class ITSClusterReader : public ClusterReader { public: ITSClusterReader(bool useMC = true, bool usePatterns = true, bool triggerOut = true) - : ClusterReader(o2::detectors::DetID::ITS, useMC, usePatterns, triggerOut) - { - mOrigin = o2::header::gDataOriginITS; - } + : ClusterReader(useMC, usePatterns, triggerOut) {} }; -class MFTClusterReader : public ClusterReader +class MFTClusterReader : public ClusterReader { public: MFTClusterReader(bool useMC = true, bool usePatterns = true, bool triggerOut = true) - : ClusterReader(o2::detectors::DetID::MFT, useMC, usePatterns, triggerOut) - { - mOrigin = o2::header::gDataOriginMFT; - } + : ClusterReader(useMC, usePatterns, triggerOut) {} }; /// create a processor spec @@ -96,7 +95,6 @@ class MFTClusterReader : public ClusterReader framework::DataProcessorSpec getITSClusterReaderSpec(bool useMC = true, bool usePatterns = true, bool useTriggers = true); framework::DataProcessorSpec getMFTClusterReaderSpec(bool useMC = true, bool usePatterns = true, bool useTriggers = true); -} // namespace itsmft -} // namespace o2 +} // namespace o2::itsmft #endif /* O2_ITSMFT_CLUSTERREADER */ diff --git a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClusterWriterSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterWriterSpec.h similarity index 73% rename from Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClusterWriterSpec.h rename to Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterWriterSpec.h index 42b96786af27a..5ae371e7e09c4 100644 --- a/Detectors/ITSMFT/ITS/workflow/include/ITSWorkflow/ClusterWriterSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClusterWriterSpec.h @@ -11,21 +11,19 @@ /// @file ClusterWriterSpec.h -#ifndef O2_ITS_CLUSTERWRITER -#define O2_ITS_CLUSTERWRITER +#ifndef O2_ITSMFT_CLUSTERWRITER +#define O2_ITSMFT_CLUSTERWRITER #include "Framework/DataProcessorSpec.h" -namespace o2 -{ -namespace its +namespace o2::itsmft { -/// create a processor spec -/// write ITS clusters to ROOT file +template framework::DataProcessorSpec getClusterWriterSpec(bool useMC); +framework::DataProcessorSpec getITSClusterWriterSpec(bool useMC); +framework::DataProcessorSpec getMFTClusterWriterSpec(bool useMC); -} // namespace its -} // namespace o2 +} // namespace o2::itsmft #endif /* O2_ITS_CLUSTERWRITER */ diff --git a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/ClustererSpec.h b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClustererSpec.h similarity index 64% rename from Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/ClustererSpec.h rename to Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClustererSpec.h index f0a763597ff74..b6ebc282c2a27 100644 --- a/Detectors/ITSMFT/MFT/workflow/include/MFTWorkflow/ClustererSpec.h +++ b/Detectors/ITSMFT/common/workflow/include/ITSMFTWorkflow/ClustererSpec.h @@ -11,24 +11,27 @@ /// @file ClustererSpec.h -#ifndef O2_MFT_CLUSTERERDPL_H_ -#define O2_MFT_CLUSTERERDPL_H_ +#ifndef O2_ITSMFT_CLUSTERERDPL_H_ +#define O2_ITSMFT_CLUSTERERDPL_H_ -#include #include "DetectorsBase/GRPGeomHelper.h" #include "Framework/DataProcessorSpec.h" #include "Framework/Task.h" #include "ITSMFTReconstruction/Clusterer.h" +#include "ITSMFTBase/DPLAlpideParam.h" using namespace o2::framework; -namespace o2 -{ -namespace mft +namespace o2::itsmft { +template class ClustererDPL : public Task { + static constexpr o2::detectors::DetID ID{N == o2::detectors::DetID::ITS ? o2::detectors::DetID::ITS : o2::detectors::DetID::MFT}; + static constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; + static constexpr int NLayers{o2::itsmft::DPLAlpideParam::supportsStaggering() ? o2::itsmft::DPLAlpideParam::getNLayers() : 1}; + public: ClustererDPL(std::shared_ptr gr, bool useMC) : mGGCCDBRequest(gr), mUseMC(useMC) {} ~ClustererDPL() override = default; @@ -39,20 +42,19 @@ class ClustererDPL : public Task private: void updateTimeDependentParams(ProcessingContext& pc); - int mState = 0; + std::string mDetName; bool mUseMC = true; bool mUseClusterDictionary = true; int mNThreads = 1; - std::unique_ptr mFile = nullptr; std::unique_ptr mClusterer = nullptr; std::shared_ptr mGGCCDBRequest; + int mLayers{NLayers}; + std::vector mFilter; }; -/// create a processor spec -/// run MFT cluster finder -framework::DataProcessorSpec getClustererSpec(bool useMC); +framework::DataProcessorSpec getITSClustererSpec(bool useMC); +framework::DataProcessorSpec getMFTClustererSpec(bool useMC); -} // namespace mft -} // namespace o2 +} // namespace o2::itsmft #endif /* O2_MFT_CLUSTERERDPL */ diff --git a/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx b/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx index ea906056c7898..bc6418a077810 100644 --- a/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx +++ b/Detectors/ITSMFT/common/workflow/src/ClusterReaderSpec.cxx @@ -12,15 +12,16 @@ /// @file ClusterReaderSpec.cxx #include +#include -#include "TTree.h" +#include #include "Framework/ControlService.h" #include "Framework/ConfigParamRegistry.h" #include "Framework/Logger.h" #include "ITSMFTWorkflow/ClusterReaderSpec.h" +#include "ITSMFTBase/DPLAlpideParam.h" #include "DataFormatsITSMFT/PhysTrigger.h" -#include #include "CommonUtils/NameConf.h" using namespace o2::framework; @@ -31,45 +32,48 @@ namespace o2 namespace itsmft { -ClusterReader::ClusterReader(o2::detectors::DetID id, bool useMC, bool usePatterns, bool triggerOut) +template +ClusterReader::ClusterReader(bool useMC, bool usePatterns, bool triggerOut) : mUseMC(useMC), mUsePatterns(usePatterns), mTriggerOut(triggerOut), mDetName(Origin.as()), mDetNameLC(mDetName) { - assert(id == o2::detectors::DetID::ITS || id == o2::detectors::DetID::MFT); - mDetNameLC = mDetName = id.getName(); - mUseMC = useMC; - mUsePatterns = usePatterns; - mTriggerOut = triggerOut; std::transform(mDetNameLC.begin(), mDetNameLC.end(), mDetNameLC.begin(), ::tolower); + + mClusROFRec.fill(nullptr); + mClusterCompArray.fill(nullptr); + mPatternsArray.fill(nullptr); + mClusterMCTruth.fill(nullptr); + mClusMC2ROFs.fill(nullptr); } -void ClusterReader::init(InitContext& ic) +template +void ClusterReader::init(InitContext& ic) { mFileName = o2::utils::Str::concat_string(o2::utils::Str::rectifyDirectory(ic.options().get("input-dir")), ic.options().get((mDetNameLC + "-cluster-infile").c_str())); connectTree(mFileName); } -void ClusterReader::run(ProcessingContext& pc) +template +void ClusterReader::run(ProcessingContext& pc) { auto ent = mTree->GetReadEntry() + 1; assert(ent < mTree->GetEntries()); // this should not happen mTree->GetEntry(ent); - LOG(info) << mDetName << "ClusterReader pushes " << mClusROFRec.size() << " ROFRecords," - << mClusterCompArray.size() << " compact clusters at entry " << ent; - - // This is a very ugly way of providing DataDescription, which anyway does not need to contain detector name. - // To be fixed once the names-definition class is ready - pc.outputs().snapshot(Output{mOrigin, "CLUSTERSROF", 0}, mClusROFRec); - pc.outputs().snapshot(Output{mOrigin, "COMPCLUSTERS", 0}, mClusterCompArray); - if (mUsePatterns) { - pc.outputs().snapshot(Output{mOrigin, "PATTERNS", 0}, mPatternsArray); - } - if (mUseMC) { - pc.outputs().snapshot(Output{mOrigin, "CLUSTERSMCTR", 0}, mClusterMCTruth); - pc.outputs().snapshot(Output{mOrigin, "CLUSTERSMC2ROF", 0}, mClusMC2ROFs); + + for (uint32_t iLayer = 0; iLayer < NLayers; ++iLayer) { + LOG(info) << mDetName << "ClusterReader:" << iLayer << " pushes " << mClusROFRec[iLayer]->size() << " ROFRecords, " << mClusterCompArray[iLayer]->size() << " compact clusters at entry " << ent; + pc.outputs().snapshot(Output{Origin, "CLUSTERSROF", iLayer}, *mClusROFRec[iLayer]); + pc.outputs().snapshot(Output{Origin, "COMPCLUSTERS", iLayer}, *mClusterCompArray[iLayer]); + if (mUsePatterns) { + pc.outputs().snapshot(Output{Origin, "PATTERNS", iLayer}, *mPatternsArray[iLayer]); + } + if (mUseMC) { + pc.outputs().snapshot(Output{Origin, "CLUSTERSMCTR", iLayer}, *mClusterMCTruth[iLayer]); + pc.outputs().snapshot(Output{Origin, "CLUSTERSMC2ROF", iLayer}, *mClusMC2ROFs[iLayer]); + } } if (mTriggerOut) { std::vector dummyTrig; - pc.outputs().snapshot(Output{mOrigin, "PHYSTRIG", 0}, dummyTrig); + pc.outputs().snapshot(Output{Origin, "PHYSTRIG", 0}, dummyTrig); } if (mTree->GetReadEntry() + 1 >= mTree->GetEntries()) { pc.services().get().endOfStream(); @@ -77,7 +81,8 @@ void ClusterReader::run(ProcessingContext& pc) } } -void ClusterReader::connectTree(const std::string& filename) +template +void ClusterReader::connectTree(const std::string& filename) { mTree.reset(nullptr); // in case it was already loaded mFile.reset(TFile::Open(filename.c_str())); @@ -85,70 +90,89 @@ void ClusterReader::connectTree(const std::string& filename) mTree.reset((TTree*)mFile->Get(mClusTreeName.c_str())); assert(mTree); - mTree->SetBranchAddress((mDetName + mClusROFBranchName).c_str(), &mClusROFRecPtr); - mTree->SetBranchAddress((mDetName + mClusterCompBranchName).c_str(), &mClusterCompArrayPtr); - if (mUsePatterns) { - mTree->SetBranchAddress((mDetName + mClusterPattBranchName).c_str(), &mPatternsArrayPtr); - } - if (mUseMC) { - if (mTree->GetBranch((mDetName + mClustMCTruthBranchName).c_str()) && - mTree->GetBranch((mDetName + mClustMC2ROFBranchName).c_str())) { - mTree->SetBranchAddress((mDetName + mClustMCTruthBranchName).c_str(), &mClusterMCTruthPtr); - mTree->SetBranchAddress((mDetName + mClustMC2ROFBranchName).c_str(), &mClusMC2ROFsPtr); - } else { - LOG(info) << "MC-truth is missing"; - mUseMC = false; + for (uint32_t iLayer = 0; iLayer < NLayers; ++iLayer) { + setBranchAddress(mClusROFBranchName, mClusROFRec[iLayer], iLayer); + setBranchAddress(mClusterCompBranchName, mClusterCompArray[iLayer], iLayer); + if (mUsePatterns) { + setBranchAddress(mClusterPattBranchName, mPatternsArray[iLayer], iLayer); + } + if (mUseMC) { + if (mTree->GetBranch(getBranchName(mClustMCTruthBranchName, iLayer).c_str()) && + mTree->GetBranch(getBranchName(mClustMC2ROFBranchName, iLayer).c_str())) { + setBranchAddress(mClustMCTruthBranchName, mClusterMCTruth[iLayer], iLayer); + setBranchAddress(mClustMC2ROFBranchName, mClusMC2ROFs[iLayer], iLayer); + } else { + LOG(info) << "MC-truth is missing"; + mUseMC = false; + } } } LOG(info) << "Loaded tree from " << filename << " with " << mTree->GetEntries() << " entries"; } -DataProcessorSpec getITSClusterReaderSpec(bool useMC, bool usePatterns, bool triggerOut) +template +std::string ClusterReader::getBranchName(const std::string& base, int index) const +{ + if constexpr (o2::itsmft::DPLAlpideParam::supportsStaggering()) { + return mDetName + base + "_" + std::to_string(index); + } + return mDetName + base; +} + +template +template +void ClusterReader::setBranchAddress(const std::string& base, Ptr& addr, int layer) { - std::vector outputSpec; - outputSpec.emplace_back("ITS", "CLUSTERSROF", 0, Lifetime::Timeframe); - outputSpec.emplace_back("ITS", "COMPCLUSTERS", 0, Lifetime::Timeframe); - if (usePatterns) { - outputSpec.emplace_back("ITS", "PATTERNS", 0, Lifetime::Timeframe); + const auto name = getBranchName(base, layer); + if (Int_t ret = mTree->SetBranchAddress(name.c_str(), &addr); ret != 0) { + LOGP(fatal, "failed to set branch address for {} ret={}", name, ret); } - if (useMC) { - outputSpec.emplace_back("ITS", "CLUSTERSMCTR", 0, Lifetime::Timeframe); - outputSpec.emplace_back("ITS", "CLUSTERSMC2ROF", 0, Lifetime::Timeframe); +} + +namespace +{ +template +std::vector makeOutChannels(o2::header::DataOrigin detOrig, bool mctruth, bool usePatterns, bool triggerOut) +{ + std::vector outputs; + for (uint32_t iLayer = 0; iLayer < ((o2::itsmft::DPLAlpideParam::supportsStaggering()) ? o2::itsmft::DPLAlpideParam::getNLayers() : 1); ++iLayer) { + outputs.emplace_back(detOrig, "CLUSTERSROF", iLayer, Lifetime::Timeframe); + outputs.emplace_back(detOrig, "COMPCLUSTERS", iLayer, Lifetime::Timeframe); + if (usePatterns) { + outputs.emplace_back(detOrig, "PATTERNS", iLayer, Lifetime::Timeframe); + } + if (mctruth) { + outputs.emplace_back(detOrig, "CLUSTERSMCTR", iLayer, Lifetime::Timeframe); + outputs.emplace_back(detOrig, "CLUSTERSMC2ROF", iLayer, Lifetime::Timeframe); + } } if (triggerOut) { - outputSpec.emplace_back("ITS", "PHYSTRIG", 0, Lifetime::Timeframe); + outputs.emplace_back(detOrig, "PHYSTRIG", 0, Lifetime::Timeframe); } + return outputs; +} +} // namespace + +DataProcessorSpec getITSClusterReaderSpec(bool useMC, bool usePatterns, bool triggerOut) +{ return DataProcessorSpec{ - "its-cluster-reader", - Inputs{}, - outputSpec, - AlgorithmSpec{adaptFromTask(useMC, usePatterns, triggerOut)}, - Options{ + .name = "its-cluster-reader", + .inputs = Inputs{}, + .outputs = makeOutChannels("ITS", useMC, usePatterns, triggerOut), + .algorithm = AlgorithmSpec{adaptFromTask(useMC, usePatterns, triggerOut)}, + .options = Options{ {"its-cluster-infile", VariantType::String, "o2clus_its.root", {"Name of the input cluster file"}}, {"input-dir", VariantType::String, "none", {"Input directory"}}}}; } DataProcessorSpec getMFTClusterReaderSpec(bool useMC, bool usePatterns, bool triggerOut) { - std::vector outputSpec; - outputSpec.emplace_back("MFT", "CLUSTERSROF", 0, Lifetime::Timeframe); - outputSpec.emplace_back("MFT", "COMPCLUSTERS", 0, Lifetime::Timeframe); - if (usePatterns) { - outputSpec.emplace_back("MFT", "PATTERNS", 0, Lifetime::Timeframe); - } - if (useMC) { - outputSpec.emplace_back("MFT", "CLUSTERSMCTR", 0, Lifetime::Timeframe); - outputSpec.emplace_back("MFT", "CLUSTERSMC2ROF", 0, Lifetime::Timeframe); - } - if (triggerOut) { - outputSpec.emplace_back("MFT", "PHYSTRIG", 0, Lifetime::Timeframe); - } return DataProcessorSpec{ - "mft-cluster-reader", - Inputs{}, - outputSpec, - AlgorithmSpec{adaptFromTask(useMC, usePatterns, triggerOut)}, - Options{ + .name = "mft-cluster-reader", + .inputs = Inputs{}, + .outputs = makeOutChannels("MFT", useMC, usePatterns, triggerOut), + .algorithm = AlgorithmSpec{adaptFromTask(useMC, usePatterns, triggerOut)}, + .options = Options{ {"mft-cluster-infile", VariantType::String, "mftclusters.root", {"Name of the input cluster file"}}, {"input-dir", VariantType::String, "none", {"Input directory"}}}}; } diff --git a/Detectors/ITSMFT/common/workflow/src/ClusterWriterSpec.cxx b/Detectors/ITSMFT/common/workflow/src/ClusterWriterSpec.cxx new file mode 100644 index 0000000000000..c1900c346133b --- /dev/null +++ b/Detectors/ITSMFT/common/workflow/src/ClusterWriterSpec.cxx @@ -0,0 +1,107 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file ClusterWriterSpec.cxx + +#include +#include +#include +#include +#include + +#include "Framework/ConcreteDataMatcher.h" +#include "ITSMFTBase/DPLAlpideParam.h" +#include "ITSMFTWorkflow/ClusterWriterSpec.h" +#include "DPLUtils/MakeRootTreeWriterSpec.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/MCTruthContainer.h" + +using namespace o2::framework; + +namespace o2::itsmft +{ + +template +using BranchDefinition = MakeRootTreeWriterSpec::BranchDefinition; +using CompClusType = std::vector; +using PatternsType = std::vector; +using ROFrameRType = std::vector; +using LabelsType = o2::dataformats::MCTruthContainer; +using ROFRecLblT = std::vector; +using namespace o2::header; + +template +DataProcessorSpec getClusterWriterSpec(bool useMC) +{ + static constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; + constexpr int NLayers = (DPLAlpideParam::supportsStaggering()) ? DPLAlpideParam::getNLayers() : 1; + const auto detName = Origin.as(); + // Spectators for logging + auto compClusterSizes = std::make_shared>(); + auto compClustersSizeGetter = [compClusterSizes](CompClusType const& compClusters, DataRef const& ref) { + auto const* dh = DataRefUtils::getHeader(ref); + (*compClusterSizes)[dh->subSpecification] = compClusters.size(); + }; + auto logger = [detName, compClusterSizes](std::vector const& rofs, DataRef const& ref) { + auto const* dh = DataRefUtils::getHeader(ref); + const auto i = dh->subSpecification; + LOG(info) << detName << "ClusterWriter:" << i << " pulled " << (*compClusterSizes)[i] << " clusters, in " << rofs.size() << " RO frames"; + }; + auto getIndex = [](DataRef const& ref) -> size_t { + auto const* dh = DataRefUtils::getHeader(ref); + return static_cast(dh->subSpecification); + }; + auto getName = [](std::string base, size_t index) -> std::string { + if constexpr (DPLAlpideParam::supportsStaggering()) { + return base += "_" + std::to_string(index); + } + return base; + }; + auto detNameLC = detName; + std::transform(detNameLC.begin(), detNameLC.end(), detNameLC.begin(), [](unsigned char c) { return std::tolower(c); }); + return MakeRootTreeWriterSpec(std::format("{}-cluster-writer", detNameLC).c_str(), + (o2::detectors::DetID::ITS == N) ? "o2clus_its.root" : "mftclusters.root", + MakeRootTreeWriterSpec::TreeAttributes{.name = "o2sim", .title = std::format("Tree with {} clusters", detName)}, + BranchDefinition{InputSpec{"compclus", ConcreteDataTypeMatcher{Origin, "COMPCLUSTERS"}}, + (detName + "ClusterComp").c_str(), "compact-cluster-branch", + NLayers, + compClustersSizeGetter, + getIndex, + getName}, + BranchDefinition{InputSpec{"patterns", ConcreteDataTypeMatcher{Origin, "PATTERNS"}}, + (detName + "ClusterPatt").c_str(), "cluster-pattern-branch", + NLayers, + getIndex, + getName}, + BranchDefinition{InputSpec{"ROframes", ConcreteDataTypeMatcher{Origin, "CLUSTERSROF"}}, + (detName + "ClustersROF").c_str(), "cluster-rof-branch", + NLayers, + logger, + getIndex, + getName}, + BranchDefinition{InputSpec{"labels", ConcreteDataTypeMatcher{Origin, "CLUSTERSMCTR"}}, + (detName + "ClusterMCTruth").c_str(), "cluster-label-branch", + (useMC ? NLayers : 0), + getIndex, + getName}, + BranchDefinition{InputSpec{"MC2ROframes", ConcreteDataTypeMatcher{Origin, "CLUSTERSMC2ROF"}}, + (detName + "ClustersMC2ROF").c_str(), "cluster-mc2rof-branch", + (useMC ? NLayers : 0), + getIndex, + getName})(); +} + +framework::DataProcessorSpec getITSClusterWriterSpec(bool useMC) { return getClusterWriterSpec(useMC); } +framework::DataProcessorSpec getMFTClusterWriterSpec(bool useMC) { return getClusterWriterSpec(useMC); } + +} // namespace o2::itsmft diff --git a/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx b/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx new file mode 100644 index 0000000000000..0b6bb44ee78c8 --- /dev/null +++ b/Detectors/ITSMFT/common/workflow/src/ClustererSpec.cxx @@ -0,0 +1,325 @@ +// Copyright 2019-2020 CERN and copyright holders of ALICE O2. +// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders. +// All rights not expressly granted are reserved. +// +// This software is distributed under the terms of the GNU General Public +// License v3 (GPL Version 3), copied verbatim in the file "COPYING". +// +// In applying this license CERN does not waive the privileges and immunities +// granted to it by virtue of its status as an Intergovernmental Organization +// or submit itself to any jurisdiction. + +/// @file ClustererSpec.cxx + +#include + +#include "ITSMFTWorkflow/ClustererSpec.h" +#include "Framework/ControlService.h" +#include "Framework/ConfigParamRegistry.h" +#include "Framework/CCDBParamSpec.h" +#include "DataFormatsITSMFT/Digit.h" +#include "Framework/InputRecordWalker.h" +#include "ITSMFTReconstruction/ChipMappingMFT.h" +#include "ITSMFTReconstruction/ChipMappingITS.h" +#include "DataFormatsITSMFT/CompCluster.h" +#include "DataFormatsITSMFT/TopologyDictionary.h" +#include "SimulationDataFormat/MCCompLabel.h" +#include "SimulationDataFormat/ConstMCTruthContainer.h" +#include "DataFormatsITSMFT/ROFRecord.h" +#include "DataFormatsParameters/GRPObject.h" +#include "ITSMFTReconstruction/DigitPixelReader.h" +#include "DetectorsBase/GeometryManager.h" +#include "ITSMFTBase/DPLAlpideParam.h" +#include "CommonConstants/LHCConstants.h" +#include "DetectorsCommonDataFormats/DetectorNameConf.h" +#include "ITSMFTReconstruction/ClustererParam.h" + +namespace o2::itsmft +{ + +template +void ClustererDPL::init(InitContext& ic) +{ + mClusterer = std::make_unique(); + mClusterer->setNChips((N == o2::detectors::DetID::ITS) ? o2::itsmft::ChipMappingITS::getNChips() : o2::itsmft::ChipMappingMFT::getNChips()); + mUseClusterDictionary = !ic.options().get("ignore-cluster-dictionary"); + o2::base::GRPGeomHelper::instance().setRequest(mGGCCDBRequest); + mNThreads = std::max(1, ic.options().get("nthreads")); + mDetName = Origin.as(); + + // prepare data filter + for (int iLayer = 0; iLayer < NLayers; ++iLayer) { + mFilter.emplace_back("digits", Origin, "DIGITS", iLayer, Lifetime::Timeframe); + mFilter.emplace_back("ROframe", Origin, "DIGITSROF", iLayer, Lifetime::Timeframe); + if (mUseMC) { + mFilter.emplace_back("labels", Origin, "DIGITSMCTR", iLayer, Lifetime::Timeframe); + mFilter.emplace_back("MC2ROframes", Origin, "DIGITSMC2ROF", iLayer, Lifetime::Timeframe); + } + } +} + +template +void ClustererDPL::run(ProcessingContext& pc) +{ + updateTimeDependentParams(pc); + + // filter input and compose + std::array, NLayers> digits; + std::array, NLayers> rofs; + std::array, NLayers> labelsbuffer; + std::array, NLayers> mc2rofs; + for (const DataRef& ref : InputRecordWalker{pc.inputs(), mFilter}) { + auto const* dh = DataRefUtils::getHeader(ref); + if (DataRefUtils::match(ref, {"digits", ConcreteDataTypeMatcher{Origin, "DIGITS"}})) { + digits[dh->subSpecification] = pc.inputs().get>(ref); + } + if (DataRefUtils::match(ref, {"ROframe", ConcreteDataTypeMatcher{Origin, "DIGITSROF"}})) { + rofs[dh->subSpecification] = pc.inputs().get>(ref); + } + if (DataRefUtils::match(ref, {"labels", ConcreteDataTypeMatcher{Origin, "DIGITSMCTR"}})) { + labelsbuffer[dh->subSpecification] = pc.inputs().get>(ref); + } + if (DataRefUtils::match(ref, {"MC2ROframes", ConcreteDataTypeMatcher{Origin, "DIGITSMC2ROF"}})) { + mc2rofs[dh->subSpecification] = pc.inputs().get>(ref); + } + } + + // query the first orbit in this TF + const auto firstTForbit = pc.services().get().firstTForbit; + const o2::InteractionRecord firstIR(0, firstTForbit); + const auto& par = DPLAlpideParam::Instance(); + + // process received inputs + uint64_t nClusters{0}; + TStopwatch sw; + o2::itsmft::DigitPixelReader reader; + for (uint32_t iLayer{0}; iLayer < NLayers; ++iLayer) { + int layer = (DPLAlpideParam::supportsStaggering()) ? iLayer : -1; + sw.Start(); + LOG(info) << mDetName << "Clusterer:" << layer << " pulled " << digits[iLayer].size() << " digits, in " << rofs[iLayer].size() << " RO frames"; + + mClusterer->setMaxROFDepthToSquash(mClusterer->getMaxROFDepthToSquash(layer)); + o2::dataformats::ConstMCTruthContainerView labels(labelsbuffer[iLayer]); + reader.setSquashingDepth(mClusterer->getMaxROFDepthToSquash(layer)); + reader.setSquashingDist(mClusterer->getMaxRowColDiffToMask()); // Sharing same parameter/logic with masking + reader.setMaxBCSeparationToSquash(mClusterer->getMaxBCSeparationToSquash(layer)); + reader.setDigits(digits[iLayer]); + reader.setROFRecords(rofs[iLayer]); + if (mUseMC) { + reader.setMC2ROFRecords(mc2rofs[iLayer]); + LOG(info) << mDetName << "Clusterer:" << layer << " pulled " << labels.getNElements() << " labels "; + reader.setDigitsMCTruth(labels.getIndexedSize() > 0 ? &labels : nullptr); + } + reader.init(); + std::vector clusCompVec; + std::vector clusROFVec; + std::vector clusPattVec; + + std::unique_ptr> clusterLabels; + if (mUseMC) { + clusterLabels = std::make_unique>(); + } + mClusterer->process(mNThreads, reader, &clusCompVec, &clusPattVec, &clusROFVec, clusterLabels.get()); + + // ensure that the rof output is continuous + size_t nROFs = clusROFVec.size(); + const int nROFsPerOrbit = o2::constants::lhc::LHCMaxBunches / par.getROFLengthInBC(iLayer); + const int nROFsTF = nROFsPerOrbit * o2::base::GRPGeomHelper::getNHBFPerTF(); + if (nROFsTF != clusROFVec.size()) { + // it can happen that in the digitization rofs without contributing hits are skipped + // however downstream consumers of the clusters cannot know apriori the time structure + // the cluster rofs do not account for the bias so it will start always at BC=0 + // if we receive more cluster rofs then there supposed to be, do not throw away this data + // the clusterer should be blind to this! + const size_t nROFsLayer = std::max((size_t)nROFsTF, clusROFVec.size()); + std::vector expClusRofVec(nROFsLayer); + for (int iROF{0}; iROF < nROFsLayer; ++iROF) { + auto& rof = expClusRofVec[iROF]; + int orb = iROF * par.getROFLengthInBC(iLayer) / o2::constants::lhc::LHCMaxBunches + firstTForbit; + int bc = iROF * par.getROFLengthInBC(iLayer) % o2::constants::lhc::LHCMaxBunches; + o2::InteractionRecord ir(bc, orb); + rof.setBCData(ir); + rof.setROFrame(iROF); + rof.setNEntries(0); + rof.setFirstEntry(-1); + } + uint32_t prevEntry{0}; + for (const auto& rof : clusROFVec) { + const auto& ir = rof.getBCData(); + const auto irToFirst = ir - firstIR; + const int irROF = irToFirst.toLong() / par.getROFLengthInBC(iLayer); + auto& expROF = expClusRofVec[irROF]; + expROF.setFirstEntry(rof.getFirstEntry()); + expROF.setNEntries(rof.getNEntries()); + if (expROF.getBCData() != rof.getBCData()) { + LOGP(fatal, "detected mismatch between expected ROF:{} and received ROF:{}", expROF.asString(), rof.asString()); + } + } + int prevFirst{0}; + for (auto& rof : expClusRofVec) { + if (rof.getFirstEntry() < 0) { + rof.setFirstEntry(prevFirst); + } + prevFirst = rof.getFirstEntry(); + } + nROFs = expClusRofVec.size(); + pc.outputs().snapshot(Output{Origin, "CLUSTERSROF", iLayer}, expClusRofVec); + } else { + pc.outputs().snapshot(Output{Origin, "CLUSTERSROF", iLayer}, clusROFVec); + } + pc.outputs().snapshot(Output{Origin, "COMPCLUSTERS", iLayer}, clusCompVec); + pc.outputs().snapshot(Output{Origin, "PATTERNS", iLayer}, clusPattVec); + + nClusters += clusCompVec.size(); + + if (mUseMC) { + pc.outputs().snapshot(Output{Origin, "CLUSTERSMCTR", iLayer}, *clusterLabels); // at the moment requires snapshot + std::vector clusterMC2ROframes(mc2rofs[iLayer].size()); + for (int i = mc2rofs[iLayer].size(); i--;) { + clusterMC2ROframes[i] = mc2rofs[iLayer][i]; // Simply, replicate it from digits ? + } + pc.outputs().snapshot(Output{Origin, "CLUSTERSMC2ROF", iLayer}, clusterMC2ROframes); + } + reader.reset(); + + // TODO: in principle, after masking "overflow" pixels the MC2ROFRecord maxROF supposed to change, nominally to minROF + // -> consider recalculationg maxROF + sw.Stop(); + LOG(info) << mDetName << "Clusterer:" << layer << " pushed " << clusCompVec.size() << " clusters, in " << nROFs << " RO frames in " << sw.RealTime() << " s"; + } + + LOG(info) << mDetName << "Clusterer produced " << nClusters << " clusters"; +} + +///_______________________________________ +template +void ClustererDPL::updateTimeDependentParams(ProcessingContext& pc) +{ + o2::base::GRPGeomHelper::instance().checkUpdates(pc); + static bool initOnceDone = false; + if (!initOnceDone) { // this params need to be queried only once + initOnceDone = true; + pc.inputs().get("cldict"); // just to trigger the finaliseCCDB + pc.inputs().get*>("alppar"); + pc.inputs().get*>("cluspar"); + mClusterer->setContinuousReadOut(o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(N)); + // settings for the fired pixel overflow masking + const auto& alpParams = o2::itsmft::DPLAlpideParam::Instance(); + const auto& clParams = o2::itsmft::ClustererParam::Instance(); + if (clParams.maxBCDiffToMaskBias > 0 && clParams.maxBCDiffToSquashBias > 0) { + LOGP(fatal, "maxBCDiffToMaskBias = {} and maxBCDiffToSquashBias = {} cannot be set at the same time. Either set masking or squashing with a BCDiff > 0", clParams.maxBCDiffToMaskBias, clParams.maxBCDiffToSquashBias); + } + mClusterer->setDropHugeClusters(clParams.dropHugeClusters); + auto nbc = clParams.maxBCDiffToMaskBias; + nbc += mClusterer->isContinuousReadOut() ? alpParams.roFrameLengthInBC : (alpParams.roFrameLengthTrig / o2::constants::lhc::LHCBunchSpacingNS); + mClusterer->setMaxBCSeparationToMask(nbc); + mClusterer->setMaxRowColDiffToMask(clParams.maxRowColDiffToMask); + // Squasher + int rofBC = mClusterer->isContinuousReadOut() ? alpParams.roFrameLengthInBC : (alpParams.roFrameLengthTrig / o2::constants::lhc::LHCBunchSpacingNS); // ROF length in BC + mClusterer->setMaxBCSeparationToSquash(rofBC + clParams.maxBCDiffToSquashBias); + int nROFsToSquash = 0; // squashing disabled if no reset due to maxSOTMUS>0. + if (clParams.maxSOTMUS > 0 && rofBC > 0) { + nROFsToSquash = 2 + int(clParams.maxSOTMUS / (rofBC * o2::constants::lhc::LHCBunchSpacingMUS)); // use squashing + } + mClusterer->setMaxROFDepthToSquash(nROFsToSquash); + if constexpr (DPLAlpideParam::supportsStaggering()) { + if (mClusterer->isContinuousReadOut()) { + for (int iLayer{0}; iLayer < NLayers; ++iLayer) { + mClusterer->addMaxBCSeparationToSquash(alpParams.getROFLengthInBC(iLayer) + clParams.getMaxBCDiffToSquashBias(iLayer)); + mClusterer->addMaxROFDepthToSquash((clParams.getMaxBCDiffToSquashBias(iLayer) > 0) ? 2 + int(clParams.maxSOTMUS / (alpParams.getROFLengthInBC(iLayer) * o2::constants::lhc::LHCBunchSpacingMUS)) : 0); + } + } + } + mClusterer->print(false); + } + // we may have other params which need to be queried regularly +} + +///_______________________________________ +template +void ClustererDPL::finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) +{ + if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) { + return; + } + if (matcher == ConcreteDataMatcher(Origin, "CLUSDICT", 0)) { + LOG(info) << "cluster dictionary updated" << (!mUseClusterDictionary ? " but its using is disabled" : ""); + if (mUseClusterDictionary) { + mClusterer->setDictionary((const TopologyDictionary*)obj); + } + return; + } + // Note: strictly speaking, for Configurable params we don't need finaliseCCDB check, the singletons are updated at the CCDB fetcher level + if (matcher == ConcreteDataMatcher(Origin, "ALPIDEPARAM", 0)) { + LOG(info) << "Alpide param updated"; + const auto& par = o2::itsmft::DPLAlpideParam::Instance(); + par.printKeyValues(); + return; + } + if (matcher == ConcreteDataMatcher(Origin, "CLUSPARAM", 0)) { + LOG(info) << "Cluster param updated"; + const auto& par = o2::itsmft::ClustererParam::Instance(); + par.printKeyValues(); + return; + } +} + +namespace +{ +template +DataProcessorSpec getClustererSpec(bool useMC) +{ + constexpr o2::header::DataOrigin Origin{N == o2::detectors::DetID::ITS ? o2::header::gDataOriginITS : o2::header::gDataOriginMFT}; + std::vector inputs; + constexpr uint32_t nLayers = (DPLAlpideParam::supportsStaggering()) ? DPLAlpideParam::getNLayers() : 1; + for (uint32_t iLayer = 0; iLayer < nLayers; ++iLayer) { + inputs.emplace_back("digits", Origin, "DIGITS", iLayer, Lifetime::Timeframe); + inputs.emplace_back("ROframes", Origin, "DIGITSROF", iLayer, Lifetime::Timeframe); + if (useMC) { + inputs.emplace_back("labels", Origin, "DIGITSMCTR", iLayer, Lifetime::Timeframe); + inputs.emplace_back("MC2ROframes", Origin, "DIGITSMC2ROF", iLayer, Lifetime::Timeframe); + } + } + inputs.emplace_back("cldict", Origin, "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec(Origin.as() + "/Calib/ClusterDictionary")); + inputs.emplace_back("cluspar", Origin, "CLUSPARAM", 0, Lifetime::Condition, ccdbParamSpec(Origin.as() + "/Config/ClustererParam")); + inputs.emplace_back("alppar", Origin, "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec(Origin.as() + "/Config/AlpideParam")); + auto ggRequest = std::make_shared(false, // orbitResetTime + true, // GRPECS=true + false, // GRPLHCIF + false, // GRPMagField + false, // askMatLUT + o2::base::GRPGeomRequest::None, // geometry + inputs, + true); + std::vector outputs; + for (uint32_t iLayer = 0; iLayer < nLayers; ++iLayer) { + outputs.emplace_back(Origin, "COMPCLUSTERS", iLayer, Lifetime::Timeframe); + outputs.emplace_back(Origin, "PATTERNS", iLayer, Lifetime::Timeframe); + outputs.emplace_back(Origin, "CLUSTERSROF", iLayer, Lifetime::Timeframe); + if (useMC) { + outputs.emplace_back(Origin, "CLUSTERSMCTR", iLayer, Lifetime::Timeframe); + outputs.emplace_back(Origin, "CLUSTERSMC2ROF", iLayer, Lifetime::Timeframe); + } + } + return DataProcessorSpec{ + .name = (N == o2::detectors::DetID::ITS) ? "its-clusterer" : "mft-clusterer", + .inputs = inputs, + .outputs = outputs, + .algorithm = AlgorithmSpec{adaptFromTask>(ggRequest, useMC)}, + .options = Options{ + {"ignore-cluster-dictionary", VariantType::Bool, false, {"do not use cluster dictionary, always store explicit patterns"}}, + {"nthreads", VariantType::Int, 1, {"Number of clustering threads"}}}}; +} +} // namespace + +framework::DataProcessorSpec getITSClustererSpec(bool useMC) +{ + return getClustererSpec(useMC); +} + +framework::DataProcessorSpec getMFTClustererSpec(bool useMC) +{ + return getClustererSpec(useMC); +} + +} // namespace o2::itsmft diff --git a/Detectors/Upgrades/ITS3/workflow/src/RecoWorkflow.cxx b/Detectors/Upgrades/ITS3/workflow/src/RecoWorkflow.cxx index 004c3f6097167..60fe4fabfe481 100644 --- a/Detectors/Upgrades/ITS3/workflow/src/RecoWorkflow.cxx +++ b/Detectors/Upgrades/ITS3/workflow/src/RecoWorkflow.cxx @@ -12,7 +12,7 @@ #include "ITS3Workflow/RecoWorkflow.h" #include "ITS3Workflow/ClustererSpec.h" #include "ITS3Workflow/TrackerSpec.h" -#include "ITSWorkflow/ClusterWriterSpec.h" +#include "ITSMFTWorkflow/ClusterWriterSpec.h" #include "ITSWorkflow/TrackWriterSpec.h" #include "ITS3Workflow/DigitReaderSpec.h" #include "GPUWorkflow/GPUWorkflowSpec.h" @@ -40,7 +40,7 @@ framework::WorkflowSpec getWorkflow(bool useMC, its::TrackingMode::Type trmode, } if (!disableRootOutput) { - specs.emplace_back(o2::its::getClusterWriterSpec(useMC)); + specs.emplace_back(o2::itsmft::getITSClusterWriterSpec(useMC)); } if (trmode != its::TrackingMode::Off) {