Project
Loading...
Searching...
No Matches
ClustererSpec.cxx
Go to the documentation of this file.
1// Copyright 2019-2026 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
13
14#include <vector>
15#include <format>
16
37
38namespace o2::itsmft
39{
40
41template <int N>
42ClustererDPL<N>::ClustererDPL(std::shared_ptr<o2::base::GRPGeomRequest> gr, bool useMC, bool doStag) : mGGCCDBRequest(gr), mUseMC(useMC), mDoStaggering(doStag)
43{
44 if (mDoStaggering) {
46 }
47}
48
49template <int N>
51{
52 mClusterer = std::make_unique<o2::itsmft::Clusterer>();
54 mUseClusterDictionary = !ic.options().get<bool>("ignore-cluster-dictionary");
56 mNThreads = std::max(1, ic.options().get<int>("nthreads"));
57 mDetName = Origin.as<std::string>();
58
59 // prepare data filter
60 for (int iLayer = 0; iLayer < mLayers; ++iLayer) {
61 mFilter.emplace_back("digits", Origin, "DIGITS", iLayer, Lifetime::Timeframe);
62 mFilter.emplace_back("ROframe", Origin, "DIGITSROF", iLayer, Lifetime::Timeframe);
63 if (mUseMC) {
64 mFilter.emplace_back("labels", Origin, "DIGITSMCTR", iLayer, Lifetime::Timeframe);
65 }
66 }
67}
68
69template <int N>
71{
72 updateTimeDependentParams(pc);
73
74 // filter input and compose
75 std::vector<gsl::span<const o2::itsmft::Digit>> digits(mLayers);
76 std::vector<gsl::span<const o2::itsmft::ROFRecord>> rofs(mLayers);
77 std::vector<gsl::span<const char>> labelsbuffer(mLayers);
78 for (const DataRef& ref : InputRecordWalker{pc.inputs(), mFilter}) {
79 auto const* dh = DataRefUtils::getHeader<o2::header::DataHeader*>(ref);
80 if (DataRefUtils::match(ref, {"digits", ConcreteDataTypeMatcher{Origin, "DIGITS"}})) {
81 digits[dh->subSpecification] = pc.inputs().get<gsl::span<o2::itsmft::Digit>>(ref);
82 }
83 if (DataRefUtils::match(ref, {"ROframe", ConcreteDataTypeMatcher{Origin, "DIGITSROF"}})) {
84 rofs[dh->subSpecification] = pc.inputs().get<gsl::span<o2::itsmft::ROFRecord>>(ref);
85 }
86 if (DataRefUtils::match(ref, {"labels", ConcreteDataTypeMatcher{Origin, "DIGITSMCTR"}})) {
87 labelsbuffer[dh->subSpecification] = pc.inputs().get<gsl::span<char>>(ref);
88 }
89 }
90
91 // query the first orbit in this TF
92 const auto firstTForbit = pc.services().get<o2::framework::TimingInfo>().firstTForbit;
93 const o2::InteractionRecord firstIR(0, firstTForbit);
94 const auto& par = DPLAlpideParam<N>::Instance();
95
96 // process received inputs
97 uint64_t nClusters{0};
98 TStopwatch sw;
100 for (uint32_t iLayer{0}; iLayer < mLayers; ++iLayer) {
101 int layer = (mDoStaggering) ? iLayer : -1;
102 sw.Start();
103 LOG(info) << mDetName << "Clusterer" << ((mDoStaggering) ? std::format(" on layer {}", layer) : "") << " pulled " << digits[iLayer].size() << " digits, in " << rofs[iLayer].size() << " RO frames";
104
105 mClusterer->setMaxROFDepthToSquash(mClusterer->getMaxROFDepthToSquash(layer));
107 reader.setSquashingDepth(mClusterer->getMaxROFDepthToSquash(layer));
108 reader.setSquashingDist(mClusterer->getMaxRowColDiffToMask()); // Sharing same parameter/logic with masking
109 reader.setMaxBCSeparationToSquash(mClusterer->getMaxBCSeparationToSquash(layer));
110 reader.setDigits(digits[iLayer]);
111 reader.setROFRecords(rofs[iLayer]);
112 if (mUseMC) {
113 LOG(info) << mDetName << "Clusterer" << ((mDoStaggering) ? std::format(" on layer {}", layer) : "") << " pulled " << labels.getNElements() << " labels ";
114 reader.setDigitsMCTruth(labels.getIndexedSize() > 0 ? &labels : nullptr);
115 }
116 reader.init();
117 std::vector<o2::itsmft::CompClusterExt> clusCompVec;
118 std::vector<o2::itsmft::ROFRecord> clusROFVec;
119 std::vector<unsigned char> clusPattVec;
120
121 std::unique_ptr<o2::dataformats::MCTruthContainer<o2::MCCompLabel>> clusterLabels;
122 if (mUseMC) {
123 clusterLabels = std::make_unique<o2::dataformats::MCTruthContainer<o2::MCCompLabel>>();
124 }
125 mClusterer->process(mNThreads, reader, &clusCompVec, &clusPattVec, &clusROFVec, clusterLabels.get());
126
127 // ensure that the rof output is continuous
128 size_t nROFs = clusROFVec.size();
129 const int nROFsPerOrbit = o2::constants::lhc::LHCMaxBunches / par.getROFLengthInBC(iLayer);
130 const int nROFsTF = nROFsPerOrbit * o2::base::GRPGeomHelper::getNHBFPerTF();
131 // It can happen that in the digitization rofs without contributing hits are skipped or there are stray ROFs
132 // We will preserve the clusters as they are but the stray ROFs will be removed (leaving their clusters unaddressed).
133 std::vector<o2::itsmft::ROFRecord> expClusRofVec(nROFsTF);
134 for (int iROF{0}; iROF < nROFsTF; ++iROF) {
135 auto& rof = expClusRofVec[iROF];
136 int orb = iROF * par.getROFLengthInBC(iLayer) / o2::constants::lhc::LHCMaxBunches + firstTForbit;
137 int bc = iROF * par.getROFLengthInBC(iLayer) % o2::constants::lhc::LHCMaxBunches + par.getROFDelayInBC(iLayer);
139 rof.setBCData(ir);
140 rof.setROFrame(iROF);
141 rof.setNEntries(0);
142 rof.setFirstEntry(-1);
143 }
144 uint32_t prevEntry{0};
145 for (const auto& rof : clusROFVec) {
146 const auto& ir = rof.getBCData();
147 if (ir < firstIR) {
148 LOGP(warn, "Discard ROF {} preceding TF 1st orbit {}{}", ir.asString(), firstTForbit, ((mDoStaggering) ? std::format(" on layer {}", layer) : ""));
149 continue;
150 }
151 auto irToFirst = ir - firstIR;
152 if (irToFirst.toLong() - par.getROFDelayInBC(iLayer) < 0) {
153 LOGP(warn, "Discard ROF {} preceding TF 1st orbit {} due to imposed ROF delay{}", ir.asString(), firstTForbit, ((mDoStaggering) ? std::format(" on layer {}", iLayer) : ""));
154 continue;
155 }
156 irToFirst -= par.getROFDelayInBC(iLayer);
157 const long irROF = irToFirst.toLong() / par.getROFLengthInBC(iLayer);
158 if (irROF >= nROFsTF) {
159 LOGP(warn, "Discard ROF {} exceeding TF orbit range{}", ir.asString(), ((mDoStaggering) ? std::format(" on layer {}", layer) : ""));
160 continue;
161 }
162 auto& expROF = expClusRofVec[irROF];
163 if (expROF.getNEntries() == 0) {
164 expROF.setFirstEntry(rof.getFirstEntry());
165 expROF.setNEntries(rof.getNEntries());
166 } else {
167 if (expROF.getNEntries() < rof.getNEntries()) {
168 LOGP(warn, "Repeating {} with {} clusters, prefer to already processed instance with {} clusters{}", rof.asString(), rof.getNEntries(), expROF.getNEntries(), ((mDoStaggering) ? std::format(" on layer {}", layer) : ""));
169 expROF.setFirstEntry(rof.getFirstEntry());
170 expROF.setNEntries(rof.getNEntries());
171 } else {
172 LOGP(warn, "Repeating {} with {} clusters, discard preferring already processed instance with {} clusters{}", rof.asString(), rof.getNEntries(), expROF.getNEntries(), ((mDoStaggering) ? std::format(" on layer {}", layer) : ""));
173 }
174 }
175 }
176 int prevLast{0};
177 for (auto& rof : expClusRofVec) {
178 if (rof.getFirstEntry() < 0) {
179 rof.setFirstEntry(prevLast);
180 }
181 prevLast = rof.getFirstEntry() + rof.getNEntries();
182 }
183 nROFs = expClusRofVec.size();
184 pc.outputs().snapshot(Output{Origin, "CLUSTERSROF", iLayer}, expClusRofVec);
185
186 pc.outputs().snapshot(Output{Origin, "COMPCLUSTERS", iLayer}, clusCompVec);
187 pc.outputs().snapshot(Output{Origin, "PATTERNS", iLayer}, clusPattVec);
188
189 nClusters += clusCompVec.size();
190
191 if (mUseMC) {
192 pc.outputs().snapshot(Output{Origin, "CLUSTERSMCTR", iLayer}, *clusterLabels); // at the moment requires snapshot
193 // write dummy MC2ROF vector to keep writer/readers backward compatible
194 static std::vector<o2::itsmft::MC2ROFRecord> dummyMC2ROF;
195 pc.outputs().snapshot(Output{Origin, "CLUSTERSMC2ROF", iLayer}, dummyMC2ROF);
196 }
197 reader.reset();
198
199 sw.Stop();
200 LOG(info) << mDetName << "Clusterer" << ((mDoStaggering) ? std::format(": {}", iLayer) : "") << " pushed " << clusCompVec.size() << " clusters, in " << nROFs << " RO frames in " << sw.RealTime() << " s";
201 }
202
203 LOG(info) << mDetName << "Clusterer produced " << nClusters << " clusters";
204}
205
207template <int N>
209{
211 static bool initOnceDone = false;
212 if (!initOnceDone) { // this params need to be queried only once
213 initOnceDone = true;
214 pc.inputs().get<TopologyDictionary*>("cldict"); // just to trigger the finaliseCCDB
216 pc.inputs().get<o2::itsmft::ClustererParam<N>*>("cluspar");
217 mClusterer->setContinuousReadOut(o2::base::GRPGeomHelper::instance().getGRPECS()->isDetContinuousReadOut(N));
218 // settings for the fired pixel overflow masking
219 const auto& alpParams = o2::itsmft::DPLAlpideParam<N>::Instance();
220 const auto& clParams = o2::itsmft::ClustererParam<N>::Instance();
221 if (clParams.maxBCDiffToMaskBias > 0 && clParams.maxBCDiffToSquashBias > 0) {
222 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);
223 }
224 mClusterer->setDropHugeClusters(clParams.dropHugeClusters);
225 auto nbc = clParams.maxBCDiffToMaskBias;
226 nbc += mClusterer->isContinuousReadOut() ? alpParams.roFrameLengthInBC : (alpParams.roFrameLengthTrig / o2::constants::lhc::LHCBunchSpacingNS);
227 mClusterer->setMaxBCSeparationToMask(nbc);
228 mClusterer->setMaxRowColDiffToMask(clParams.maxRowColDiffToMask);
229 // Squasher
230 int rofBC = mClusterer->isContinuousReadOut() ? alpParams.roFrameLengthInBC : (alpParams.roFrameLengthTrig / o2::constants::lhc::LHCBunchSpacingNS); // ROF length in BC
231 mClusterer->setMaxBCSeparationToSquash(rofBC + clParams.maxBCDiffToSquashBias);
232 int nROFsToSquash = 0; // squashing disabled if no reset due to maxSOTMUS>0.
233 if (clParams.maxSOTMUS > 0 && rofBC > 0) {
234 nROFsToSquash = 2 + int(clParams.maxSOTMUS / (rofBC * o2::constants::lhc::LHCBunchSpacingMUS)); // use squashing
235 }
236 mClusterer->setMaxROFDepthToSquash(nROFsToSquash);
237 if (mDoStaggering) {
238 if (mClusterer->isContinuousReadOut()) {
239 for (int iLayer{0}; iLayer < mLayers; ++iLayer) {
240 mClusterer->addMaxBCSeparationToSquash(alpParams.getROFLengthInBC(iLayer) + clParams.getMaxBCDiffToSquashBias(iLayer));
241 mClusterer->addMaxROFDepthToSquash((clParams.getMaxBCDiffToSquashBias(iLayer) > 0) ? 2 + int(clParams.maxSOTMUS / (alpParams.getROFLengthInBC(iLayer) * o2::constants::lhc::LHCBunchSpacingMUS)) : 0);
242 }
243 }
244 }
245 mClusterer->print(false);
246 }
247 // we may have other params which need to be queried regularly
248}
249
251template <int N>
253{
254 if (o2::base::GRPGeomHelper::instance().finaliseCCDB(matcher, obj)) {
255 return;
256 }
257 if (matcher == ConcreteDataMatcher(Origin, "CLUSDICT", 0)) {
258 LOG(info) << "cluster dictionary updated" << (!mUseClusterDictionary ? " but its using is disabled" : "");
259 if (mUseClusterDictionary) {
260 mClusterer->setDictionary((const TopologyDictionary*)obj);
261 }
262 return;
263 }
264 // Note: strictly speaking, for Configurable params we don't need finaliseCCDB check, the singletons are updated at the CCDB fetcher level
265 if (matcher == ConcreteDataMatcher(Origin, "ALPIDEPARAM", 0)) {
266 LOG(info) << "Alpide param updated";
267 const auto& par = o2::itsmft::DPLAlpideParam<N>::Instance();
268 par.printKeyValues();
269 return;
270 }
271 if (matcher == ConcreteDataMatcher(Origin, "CLUSPARAM", 0)) {
272 LOG(info) << "Cluster param updated";
273 const auto& par = o2::itsmft::ClustererParam<N>::Instance();
274 par.printKeyValues();
275 return;
276 }
277}
278
279namespace
280{
281template <int N>
282DataProcessorSpec getClustererSpec(bool useMC, bool doStag)
283{
285 std::vector<InputSpec> inputs;
286 uint32_t nLayers = doStag ? DPLAlpideParam<N>::getNLayers() : 1;
287 for (uint32_t iLayer = 0; iLayer < nLayers; ++iLayer) {
288 inputs.emplace_back("digits", Origin, "DIGITS", iLayer, Lifetime::Timeframe);
289 inputs.emplace_back("ROframes", Origin, "DIGITSROF", iLayer, Lifetime::Timeframe);
290 if (useMC) {
291 inputs.emplace_back("labels", Origin, "DIGITSMCTR", iLayer, Lifetime::Timeframe);
292 }
293 }
294 inputs.emplace_back("cldict", Origin, "CLUSDICT", 0, Lifetime::Condition, ccdbParamSpec(Origin.as<std::string>() + "/Calib/ClusterDictionary"));
295 inputs.emplace_back("cluspar", Origin, "CLUSPARAM", 0, Lifetime::Condition, ccdbParamSpec(Origin.as<std::string>() + "/Config/ClustererParam"));
296 inputs.emplace_back("alppar", Origin, "ALPIDEPARAM", 0, Lifetime::Condition, ccdbParamSpec(Origin.as<std::string>() + "/Config/AlpideParam"));
297 auto ggRequest = std::make_shared<o2::base::GRPGeomRequest>(false, // orbitResetTime
298 true, // GRPECS=true
299 false, // GRPLHCIF
300 false, // GRPMagField
301 false, // askMatLUT
303 inputs,
304 true);
305 std::vector<OutputSpec> outputs;
306 for (uint32_t iLayer = 0; iLayer < nLayers; ++iLayer) {
307 outputs.emplace_back(Origin, "COMPCLUSTERS", iLayer, Lifetime::Timeframe);
308 outputs.emplace_back(Origin, "PATTERNS", iLayer, Lifetime::Timeframe);
309 outputs.emplace_back(Origin, "CLUSTERSROF", iLayer, Lifetime::Timeframe);
310 if (useMC) {
311 outputs.emplace_back(Origin, "CLUSTERSMCTR", iLayer, Lifetime::Timeframe);
312 outputs.emplace_back(Origin, "CLUSTERSMC2ROF", iLayer, Lifetime::Timeframe);
313 }
314 }
315 return DataProcessorSpec{
316 .name = (N == o2::detectors::DetID::ITS) ? "its-clusterer" : "mft-clusterer",
317 .inputs = inputs,
318 .outputs = outputs,
319 .algorithm = AlgorithmSpec{adaptFromTask<ClustererDPL<N>>(ggRequest, useMC, doStag)},
320 .options = Options{
321 {"ignore-cluster-dictionary", VariantType::Bool, false, {"do not use cluster dictionary, always store explicit patterns"}},
322 {"nthreads", VariantType::Int, 1, {"Number of clustering threads"}}}};
323}
324} // namespace
325
327{
328 return getClustererSpec<o2::detectors::DetID::ITS>(useMC, doStag);
329}
330
332{
333 return getClustererSpec<o2::detectors::DetID::MFT>(useMC, doStag);
334}
335
336} // namespace o2::itsmft
std::vector< std::string > labels
Definition of the ITS/MFT clusterer settings.
Definition of the ITSMFT compact cluster.
A const (ready only) version of MCTruthContainer.
Definition of the ITSMFT digit.
Definition of the ClusterTopology class.
Definition of the Names Generator class.
Definition of the GeometryManager class.
Definition of the Alpide pixel reader for MC digits processing.
uint64_t bc
Definition RawEventData.h:5
Header of the General Run Parameters object.
Definition of the ITSMFT ROFrame (trigger) record.
A helper class to iteratate over all parts of all input routes.
Header to collect LHC related constants.
int nClusters
void checkUpdates(o2::framework::ProcessingContext &pc)
static GRPGeomHelper & instance()
void setRequest(std::shared_ptr< GRPGeomRequest > req)
static constexpr ID ITS
Definition DetID.h:63
void snapshot(const Output &spec, T const &object)
ConfigParamRegistry const & options()
Definition InitContext.h:33
A helper class to iteratate over all parts of all input routes.
decltype(auto) get(R binding, int part=0) const
DataAllocator & outputs()
The data allocator is used to allocate memory for the output data.
InputRecord & inputs()
The inputs associated with this processing context.
ServiceRegistryRef services()
The services registry associated with this processing context.
static constexpr int getNChips()
number of chips per barrel
static constexpr Int_t getNChips()
void init(InitContext &ic) final
ClustererDPL(std::shared_ptr< o2::base::GRPGeomRequest > gr, bool useMC, bool doStag)
void finaliseCCDB(ConcreteDataMatcher &matcher, void *obj) final
void run(ProcessingContext &pc) final
void setDigitsMCTruth(const o2::dataformats::ConstMCTruthContainerView< o2::MCCompLabel > *m)
void setDigits(const gsl::span< const o2::itsmft::Digit > a)
void setSquashingDist(const int16_t v)
void setROFRecords(const gsl::span< const o2::itsmft::ROFRecord > a)
void setSquashingDepth(const int16_t v)
GLsizeiptr size
Definition glcorearb.h:659
GLenum GLuint GLint GLint layer
Definition glcorearb.h:1310
GLint ref
Definition glcorearb.h:291
constexpr o2::header::DataOrigin gDataOriginMFT
Definition DataHeader.h:572
constexpr o2::header::DataOrigin gDataOriginITS
Definition DataHeader.h:570
Origins::iterator Origin
constexpr double LHCBunchSpacingMUS
constexpr int LHCMaxBunches
constexpr double LHCBunchSpacingNS
AlgorithmSpec adaptFromTask(Args &&... args)
Definition Task.h:59
std::vector< ConfigParamSpec > ccdbParamSpec(std::string const &path, int runDependent, std::vector< CCDBMetadata > metadata={}, int qrate=0)
framework::DataProcessorSpec getMFTClustererSpec(bool useMC, bool doStag)
framework::DataProcessorSpec getITSClustererSpec(bool useMC, bool doStag)
std::string asString() const
static bool match(DataRef const &ref, const char *binding)
static constexpr int getNLayers()
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
o2::InteractionRecord ir(0, 0)
TStopwatch sw
std::vector< Digit > digits