Project
Loading...
Searching...
No Matches
TPCDistributeIDCSpec.h
Go to the documentation of this file.
1// Copyright 2019-2020 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
16
17#ifndef O2_TPCDISTRIBUTEIDCIDCSPEC_H
18#define O2_TPCDISTRIBUTEIDCIDCSPEC_H
19
20#include <vector>
21#include "Framework/Task.h"
23#include "Framework/Logger.h"
26#include "Headers/DataHeader.h"
29#include "TPCBase/CRU.h"
34
35using namespace o2::framework;
37using namespace o2::tpc;
38
39namespace o2::tpc
40{
41
43{
44 public:
45 TPCDistributeIDCSpec(const std::vector<uint32_t>& crus, const unsigned int timeframes, const int nTFsBuffer, const unsigned int outlanes, const int firstTF, std::shared_ptr<o2::base::GRPGeomRequest> req)
46 : mCRUs{crus}, mTimeFrames{timeframes}, mNTFsBuffer{nTFsBuffer}, mOutLanes{outlanes}, mProcessedCRU{{std::vector<unsigned int>(timeframes), std::vector<unsigned int>(timeframes)}}, mTFStart{{firstTF, firstTF + timeframes}}, mTFEnd{{firstTF + timeframes - 1, mTFStart[1] + timeframes - 1}}, mCCDBRequest(req), mSendCCDBOutputOrbitReset(outlanes), mSendCCDBOutputGRPECS(outlanes)
47 {
48 // pre calculate data description for output
49 mDataDescrOut.reserve(mOutLanes);
50 for (unsigned int i = 0; i < mOutLanes; ++i) {
51 mDataDescrOut.emplace_back(getDataDescriptionIDC(i));
52 }
53
54 // sort vector for binary_search
55 std::sort(mCRUs.begin(), mCRUs.end());
56
57 for (auto& processedCRUbuffer : mProcessedCRUs) {
58 processedCRUbuffer.resize(mTimeFrames);
59 for (auto& crusMap : processedCRUbuffer) {
60 crusMap.reserve(mCRUs.size());
61 for (const auto cruID : mCRUs) {
62 crusMap.emplace(cruID, false);
63 }
64 }
65 }
66
67 const auto sides = IDCFactorization::getSides(mCRUs);
68 for (auto side : sides) {
69 const std::string name = (side == Side::A) ? "idcsgroupa" : "idcsgroupc";
71 }
72 };
73
75 {
77 mNFactorTFs = ic.options().get<int>("nFactorTFs");
78 mNTFsDataDrop = ic.options().get<int>("drop-data-after-nTFs");
79 mCheckEveryNData = ic.options().get<int>("check-data-every-n");
80 if (mCheckEveryNData == 0) {
81 mCheckEveryNData = mTimeFrames / 2;
82 if (mCheckEveryNData == 0) {
83 mCheckEveryNData = 1;
84 }
85 mNTFsDataDrop = mCheckEveryNData;
86 }
87 }
88
89 void finaliseCCDB(ConcreteDataMatcher& matcher, void* obj) final
90 {
92 if (matcher == ConcreteDataMatcher("CTP", "ORBITRESET", 0)) {
93 LOGP(info, "Updating ORBITRESET");
94 std::fill(mSendCCDBOutputOrbitReset.begin(), mSendCCDBOutputOrbitReset.end(), true);
95 } else if (matcher == ConcreteDataMatcher("GLO", "GRPECS", 0)) {
96 // check if received object is valid
97 if (o2::base::GRPGeomHelper::instance().getGRPECS()->getRun() != 0) {
98 LOGP(info, "Updating GRPECS");
99 std::fill(mSendCCDBOutputGRPECS.begin(), mSendCCDBOutputGRPECS.end(), true);
100 } else {
101 LOGP(info, "Detected default GRPECS object");
102 }
103 }
104 }
105
107 {
108 // send orbit reset and orbits per TF only once
109 if (mCCDBRequest->askTime) {
110 const bool grpecsValid = pc.inputs().isValid("grpecs");
111 const bool orbitResetValid = pc.inputs().isValid("orbitReset");
112 if (grpecsValid) {
113 pc.inputs().get<o2::parameters::GRPECSObject*>("grpecs");
114 }
115 if (orbitResetValid) {
116 pc.inputs().get<std::vector<Long64_t>*>("orbitReset");
117 }
118 if (pc.inputs().countValidInputs() == (grpecsValid + orbitResetValid)) {
119 return;
120 }
121 }
122
123 const auto tf = processing_helpers::getCurrentTF(pc);
124
125 // automatically detect firstTF in case firstTF was not specified
126 if (mTFStart.front() <= -1) {
127 const auto firstTF = tf;
128 const long offsetTF = std::abs(mTFStart.front() + 1);
129 const auto nTotTFs = getNRealTFs();
130 mTFStart = {firstTF + offsetTF, firstTF + offsetTF + nTotTFs};
131 mTFEnd = {mTFStart[1] - 1, mTFStart[1] - 1 + nTotTFs};
132 LOGP(info, "Setting {} as first TF", mTFStart[0]);
133 LOGP(info, "Using offset of {} TFs for setting the first TF", offsetTF);
134 }
135
136 // check which buffer to use for current incoming data
137 const bool currentBuffer = (tf > mTFEnd[mBuffer]) ? !mBuffer : mBuffer;
138 if (mTFStart[currentBuffer] > tf) {
139 LOGP(warning, "Current TF {} is older than start of currentBuffer {}. Skipping this TF", tf, mTFStart[currentBuffer]);
140 return;
141 }
142
143 const unsigned int currentOutLane = getOutLane(tf);
144 const unsigned int relTF = (tf - mTFStart[currentBuffer]) / mNTFsBuffer;
145 LOGP(debug, "current TF: {} relative TF: {} current buffer: {} current output lane: {} mTFStart: {}", tf, relTF, currentBuffer, currentOutLane, mTFStart[currentBuffer]);
146
147 if (relTF >= mProcessedCRU[currentBuffer].size()) {
148 LOGP(warning, "Skipping tf {} for lane {}: relative tf {} is larger than size of buffer [{}, {}]: {}", tf, currentOutLane, relTF, mTFStart[currentBuffer], mTFEnd[currentBuffer], mProcessedCRU[currentBuffer].size());
149
150 // check number of processed CRUs for previous TFs. If CRUs are missing for them, they are probably lost/not received
151 mProcessedTotalData = mCheckEveryNData;
152 checkIntervalsForMissingData(pc, currentBuffer, relTF, currentOutLane, tf);
153 return;
154 }
155
156 if (mProcessedCRU[currentBuffer][relTF] == mCRUs.size()) {
157 LOGP(warning, "All CRUs for current TF {} (relTF {}, lane {}) already received. Skipping this TF", tf, relTF, currentOutLane);
158 return;
159 }
160
161 // send start info only once
162 if (mSendOutputStartInfo[currentBuffer]) {
163 mSendOutputStartInfo[currentBuffer] = false;
164 pc.outputs().snapshot(Output{gDataOriginTPC, getDataDescriptionIDCFirstTF(), header::DataHeader::SubSpecificationType{currentOutLane}}, mTFStart[currentBuffer]);
165 }
166
167 if (mSendCCDBOutputOrbitReset[currentOutLane] && mSendCCDBOutputGRPECS[currentOutLane]) {
168 mSendCCDBOutputOrbitReset[currentOutLane] = false;
169 mSendCCDBOutputGRPECS[currentOutLane] = false;
170 pc.outputs().snapshot(Output{gDataOriginTPC, getDataDescriptionIDCOrbitReset(), header::DataHeader::SubSpecificationType{currentOutLane}}, dataformats::Pair<long, int>{o2::base::GRPGeomHelper::instance().getOrbitResetTimeMS(), o2::base::GRPGeomHelper::instance().getNHBFPerTF()});
171 }
172
173 auto inputs = o2::framework::InputRecordWalker(pc.inputs(), mFilter);
174 for (auto it = inputs.begin(); it != inputs.end(); ++it) {
175 auto const* tpcCRUHeader = o2::framework::DataRefUtils::getHeader<o2::header::DataHeader*>(*it);
176 const unsigned int cru = tpcCRUHeader->subSpecification >> 7;
177
178 // check if cru is specified in input cru list
179 if (!std::binary_search(mCRUs.begin(), mCRUs.end(), cru)) {
180 LOGP(debug, "Received data from CRU: {} which was not specified as input. Skipping", cru);
181 continue;
182 }
183
184 if (mProcessedCRUs[currentBuffer][relTF][cru]) {
185 LOGP(warning, "CRU {} for current TF {} (relTF {}, lane {}) already processed. Skipping ...", cru, tf, relTF, currentOutLane);
186 continue;
187 } else {
188 // count total number of processed CRUs for given TF
189 ++mProcessedCRU[currentBuffer][relTF];
190
191 // to keep track of processed CRUs
192 mProcessedCRUs[currentBuffer][relTF][cru] = true;
193 }
194
195 // forward payload by shallow copy
196 if (auto* payloadMsg = it.getPayload()) {
197 pc.outputs().forwardPayload(Output{gDataOriginTPC, mDataDescrOut[currentOutLane], header::DataHeader::SubSpecificationType{cru}}, *payloadMsg);
198 } else [[unlikely]] {
199 // this should never happen
200 LOGP(warning, "No IDCGROUP payload for CRU {} (TF {}, relTF {}); sending empty IDCAGG{} payload", cru, tf, relTF, currentOutLane);
201 sendEmptyIDCOutput(pc, currentOutLane, cru);
202 }
203 }
204
205 LOGP(info, "number of received CRUs for current TF: {} Needed a total number of processed CRUs of: {} Current TF: {}", mProcessedCRU[currentBuffer][relTF], mCRUs.size(), tf);
206
207 // check for missing data if specified
208 if (mNTFsDataDrop > 0) {
209 checkIntervalsForMissingData(pc, currentBuffer, relTF, currentOutLane, tf);
210 }
211
212 if (mProcessedCRU[currentBuffer][relTF] == mCRUs.size()) {
213 ++mProcessedTFs[currentBuffer];
214 }
215
216 if (mProcessedTFs[currentBuffer] == mTimeFrames) {
217 finishInterval(pc, currentOutLane, currentBuffer, tf);
218 }
219 }
220
221 void endOfStream(o2::framework::EndOfStreamContext& ec) final { ec.services().get<ControlService>().readyToQuit(QuitRequest::Me); }
222
224 static header::DataDescription getDataDescriptionIDC(const unsigned int lane)
225 {
226 const std::string name = fmt::format("IDCAGG{}", lane).data();
228 description.runtimeInit(name.substr(0, 16).c_str());
229 return description;
230 }
231
234
235 private:
236 std::vector<uint32_t> mCRUs{};
237 const unsigned int mTimeFrames{};
238 const int mNTFsBuffer{1};
239 const unsigned int mOutLanes{};
240 std::array<unsigned int, 2> mProcessedTFs{{0, 0}};
241 std::array<std::vector<unsigned int>, 2> mProcessedCRU{};
242 std::array<std::vector<std::unordered_map<unsigned int, bool>>, 2> mProcessedCRUs{};
243 std::array<long, 2> mTFStart{};
244 std::array<long, 2> mTFEnd{};
245 std::array<bool, 2> mSendOutputStartInfo{true, true};
246 std::shared_ptr<o2::base::GRPGeomRequest> mCCDBRequest;
247 std::vector<bool> mSendCCDBOutputOrbitReset{};
248 std::vector<bool> mSendCCDBOutputGRPECS{};
249 unsigned int mCurrentOutLane{0};
250 bool mBuffer{false};
251 int mNFactorTFs{0};
252 int mNTFsDataDrop{0};
253 std::array<int, 2> mStartNTFsDataDrop{0};
254 long mProcessedTotalData{0};
255 int mCheckEveryNData{1};
256 std::vector<InputSpec> mFilter{};
257 std::vector<header::DataDescription> mDataDescrOut{};
258
259 void sendEmptyIDCOutput(o2::framework::ProcessingContext& pc, const unsigned int currentOutLane, const unsigned int cru)
260 {
262 }
263
265 unsigned int getOutLane(const uint32_t tf) const { return (tf > mTFEnd[mBuffer]) ? (mCurrentOutLane + 1) % mOutLanes : mCurrentOutLane; }
266
268 unsigned int getNRealTFs() const { return mNTFsBuffer * mTimeFrames; }
269
270 void clearBuffer(const bool currentBuffer)
271 {
272 // resetting received CRUs
273 for (auto& crusMap : mProcessedCRUs[currentBuffer]) {
274 for (auto& it : crusMap) {
275 it.second = false;
276 }
277 }
278
279 mProcessedTFs[currentBuffer] = 0; // reset processed TFs for next aggregation interval
280 std::fill(mProcessedCRU[currentBuffer].begin(), mProcessedCRU[currentBuffer].end(), 0);
281
282 // set integration range for next integration interval
283 mTFStart[mBuffer] = mTFEnd[!mBuffer] + 1;
284 mTFEnd[mBuffer] = mTFStart[mBuffer] + getNRealTFs() - 1;
285
286 // switch buffer
287 mBuffer = !mBuffer;
288
289 // set output lane
290 mCurrentOutLane = ++mCurrentOutLane % mOutLanes;
291 }
292
293 void checkIntervalsForMissingData(o2::framework::ProcessingContext& pc, const bool currentBuffer, const long relTF, const unsigned int currentOutLane, const uint32_t tf)
294 {
295 if (!(mProcessedTotalData++ % mCheckEveryNData)) {
296 LOGP(detail, "Checking for dropped packages...");
297
298 // if last buffer has smaller time range check the whole last buffer
299 if ((mTFStart[currentBuffer] > mTFStart[!currentBuffer]) && (relTF > mNTFsDataDrop)) {
300 LOGP(warning, "Checking last buffer from relTF {} to {}", mStartNTFsDataDrop[!currentBuffer], mProcessedCRU[!currentBuffer].size());
301 const unsigned int lastLane = (currentOutLane == 0) ? (mOutLanes - 1) : (currentOutLane - 1);
302 checkMissingData(pc, !currentBuffer, mStartNTFsDataDrop[!currentBuffer], mProcessedCRU[!currentBuffer].size(), lastLane);
303 LOGP(warning, "All empty TFs of last buffer [{}, {}] filled with dummy and sent, triggered by data from TF {} (relTF {}). Clearing buffer", mTFStart[!currentBuffer], mTFEnd[!currentBuffer], tf, relTF);
304 finishInterval(pc, lastLane, !currentBuffer, tf);
305 }
306
307 const int tfEndCheck = std::clamp(static_cast<int>(relTF) - mNTFsDataDrop, 0, static_cast<int>(mProcessedCRU[currentBuffer].size()));
308 LOGP(detail, "Checking current buffer from relTF {} to {}", mStartNTFsDataDrop[currentBuffer], tfEndCheck);
309 checkMissingData(pc, currentBuffer, mStartNTFsDataDrop[currentBuffer], tfEndCheck, currentOutLane);
310 mStartNTFsDataDrop[currentBuffer] = tfEndCheck;
311 }
312 }
313
314 void checkMissingData(o2::framework::ProcessingContext& pc, const bool currentBuffer, const int startTF, const int endTF, const unsigned int outLane)
315 {
316 for (int iTF = startTF; iTF < endTF; ++iTF) {
317 if (mProcessedCRU[currentBuffer][iTF] != mCRUs.size()) {
318 LOGP(warning, "CRUs for lane {} rel. TF: {} curr TF {} are missing! Processed {} CRUs out of {}", outLane, iTF, mTFStart[currentBuffer] + static_cast<long>(iTF) * mNTFsBuffer + mNTFsBuffer - 1, mProcessedCRU[currentBuffer][iTF], mCRUs.size());
319 ++mProcessedTFs[currentBuffer];
320 mProcessedCRU[currentBuffer][iTF] = mCRUs.size();
321
322 // find missing CRUs
323 for (auto& it : mProcessedCRUs[currentBuffer][iTF]) {
324 if (!it.second) {
325 it.second = true;
326 sendEmptyIDCOutput(pc, outLane, it.first);
327 }
328 }
329 }
330 }
331 }
332
333 void finishInterval(o2::framework::ProcessingContext& pc, const unsigned int currentOutLane, const bool buffer, const uint32_t tf)
334 {
335 if (mNFactorTFs > 0) {
336 mNFactorTFs = 0;
337 // ToDo: Find better fix
338 for (unsigned int ilane = 0; ilane < mOutLanes; ++ilane) {
339 auto& deviceProxy = pc.services().get<FairMQDeviceProxy>();
340 auto& state = deviceProxy.getOutputChannelState({static_cast<int>(ilane)});
341 size_t oldest = std::numeric_limits<size_t>::max() - 1; // just set to really large value
342 state.oldestForChannel = {oldest};
343 }
344 }
345
346 LOGP(info, "All TFs for buffer [{}, {}] (lane {}) received at data from TF {}. Clearing buffer", mTFStart[buffer], mTFEnd[buffer], currentOutLane, tf);
347 clearBuffer(buffer);
348 mStartNTFsDataDrop[buffer] = 0;
349 mSendOutputStartInfo[buffer] = true;
350 }
351};
352
353DataProcessorSpec getTPCDistributeIDCSpec(const int ilane, const std::vector<uint32_t>& crus, const unsigned int timeframes, const unsigned int outlanes, const int firstTF, const bool sendPrecisetimeStamp = false, const int nTFsBuffer = 1)
354{
355 std::vector<InputSpec> inputSpecs;
356 const auto sides = IDCFactorization::getSides(crus);
357 for (auto side : sides) {
358 const std::string name = (side == Side::A) ? "idcsgroupa" : "idcsgroupc";
359 inputSpecs.emplace_back(InputSpec{name.data(), ConcreteDataTypeMatcher{gDataOriginTPC, TPCFLPIDCDevice::getDataDescriptionIDCGroup(side)}, Lifetime::Sporadic});
360 }
361
362 std::vector<OutputSpec> outputSpecs;
363 outputSpecs.reserve(outlanes);
364 for (unsigned int lane = 0; lane < outlanes; ++lane) {
365 outputSpecs.emplace_back(ConcreteDataTypeMatcher{gDataOriginTPC, TPCDistributeIDCSpec::getDataDescriptionIDC(lane)}, Lifetime::Sporadic);
366 outputSpecs.emplace_back(ConcreteDataMatcher{gDataOriginTPC, TPCDistributeIDCSpec::getDataDescriptionIDCFirstTF(), header::DataHeader::SubSpecificationType{lane}}, Lifetime::Sporadic);
367 }
368
369 bool fetchCCDB = false;
370 if (sendPrecisetimeStamp && (ilane == 0)) {
371 fetchCCDB = true;
372 for (unsigned int lane = 0; lane < outlanes; ++lane) {
373 outputSpecs.emplace_back(ConcreteDataMatcher{gDataOriginTPC, TPCDistributeIDCSpec::getDataDescriptionIDCOrbitReset(), header::DataHeader::SubSpecificationType{lane}}, Lifetime::Sporadic);
374 }
375 }
376
377 auto ccdbRequest = std::make_shared<o2::base::GRPGeomRequest>(fetchCCDB, // orbitResetTime
378 fetchCCDB, // GRPECS=true
379 false, // GRPLHCIF
380 false, // GRPMagField
381 false, // askMatLUT
383 inputSpecs);
384
385 const std::string type = "idc";
386 const auto id = fmt::format("tpc-distribute-{}-{:02}", type, ilane);
388 id.data(),
389 inputSpecs,
390 outputSpecs,
391 AlgorithmSpec{adaptFromTask<TPCDistributeIDCSpec>(crus, timeframes, nTFsBuffer, outlanes, firstTF, ccdbRequest)},
392 Options{{"drop-data-after-nTFs", VariantType::Int, 0, {"Number of TFs after which to drop the data."}},
393 {"check-data-every-n", VariantType::Int, 0, {"Number of run function called after which to check for missing data (-1 for no checking, 0 for default checking)."}},
394 {"nFactorTFs", VariantType::Int, 1000, {"Number of TFs to skip for sending oldest TF."}}}}; // end DataProcessorSpec
395 spec.rank = ilane;
396 return spec;
397}
398
399} // namespace o2::tpc
400
401#endif
header::DataDescription description
benchmark::State & state
std::ostringstream debug
int32_t i
Helper for geometry and GRP related CCDB requests.
A helper class to iteratate over all parts of all input routes.
uint32_t side
Definition RawData.h:0
TPC device for processing on FLPs.
auto getOrbitResetTimeMS() const
bool finaliseCCDB(o2::framework::ConcreteDataMatcher &matcher, void *obj)
static GRPGeomHelper & instance()
void setRequest(std::shared_ptr< GRPGeomRequest > req)
CacheId adoptContainer(const Output &, ContainerT &, CacheStrategy, o2::header::SerializationMethod)
A helper class to iteratate over all parts of all input routes.
DataAllocator & outputs()
The data allocator is used to allocate memory for the output data.
ServiceRegistryRef services()
The services registry associated with this processing context.
const std::vector< Side > & getSides() const
static constexpr header::DataDescription getDataDescriptionIDCFirstTF()
void run(o2::framework::ProcessingContext &pc) final
void init(o2::framework::InitContext &ic) final
void finaliseCCDB(ConcreteDataMatcher &matcher, void *obj) final
void endOfStream(o2::framework::EndOfStreamContext &ec) final
This is invoked whenever we have an EndOfStream event.
static constexpr header::DataDescription getDataDescriptionIDCOrbitReset()
TPCDistributeIDCSpec(const std::vector< uint32_t > &crus, const unsigned int timeframes, const int nTFsBuffer, const unsigned int outlanes, const int firstTF, std::shared_ptr< o2::base::GRPGeomRequest > req)
static header::DataDescription getDataDescriptionIDC(const unsigned int lane)
return data description for aggregated IDCs for given lane
static constexpr header::DataDescription getDataDescriptionIDCGroup(const Side side)
GLuint buffer
Definition glcorearb.h:655
GLsizeiptr size
Definition glcorearb.h:659
GLuint GLuint end
Definition glcorearb.h:469
GLuint const GLchar * name
Definition glcorearb.h:781
GLint GLint GLsizei GLint GLenum GLenum type
Definition glcorearb.h:275
constexpr o2::header::DataOrigin gDataOriginTPC
Definition DataHeader.h:576
Defining ITS Vertex explicitly as messageable.
Definition Cartesian.h:288
std::vector< ConfigParamSpec > Options
std::vector< T, fair::mq::pmr::polymorphic_allocator< T > > vector
uint32_t getCurrentTF(o2::framework::ProcessingContext &pc)
Global TPC definitions and constants.
Definition SimTraits.h:172
Enum< T >::Iterator begin(Enum< T >)
Definition Defs.h:156
@ A
Definition Defs.h:35
DataProcessorSpec getTPCDistributeIDCSpec(const int ilane, const std::vector< uint32_t > &crus, const unsigned int timeframes, const unsigned int outlanes, const int firstTF, const bool sendPrecisetimeStamp=false, const int nTFsBuffer=1)
std::unique_ptr< GPUReconstructionTimeframe > tf
uint32_t SubSpecificationType
Definition DataHeader.h:622
void runtimeInit(const char *string, short length=-1)
Definition DataHeader.h:261