Project
Loading...
Searching...
No Matches
RawDecoderSpec.cxx
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
12#include <string>
13#include <fairlogger/Logger.h>
26#include <filesystem>
27
28using namespace o2::ctp::reco_workflow;
29
31{
32 mCheckConsistency = ctx.options().get<bool>("check-consistency");
33 mDecoder.setCheckConsistency(mCheckConsistency);
34 mDecodeinputs = ctx.options().get<bool>("ctpinputs-decoding");
35 mDecoder.setDecodeInps(mDecodeinputs);
36 mNTFToIntegrate = ctx.options().get<int>("ntf-to-average");
37 LOG(info) << "Window size: " << mNTFToIntegrate << " TFs";
38 mVerbose = ctx.options().get<bool>("use-verbose-mode");
39 int maxerrors = ctx.options().get<int>("print-errors-num");
40 mDecoder.setVerbose(mVerbose);
41 mDecoder.setDoLumi(mDoLumi);
42 mDecoder.setDoDigits(mDoDigits);
43 mDecoder.setMAXErrors(maxerrors);
44 std::string lumiinp1 = ctx.options().get<std::string>("lumi-inp1");
45 std::string lumiinp2 = ctx.options().get<std::string>("lumi-inp2");
46 int inp1 = mDecoder.setLumiInp(1, lumiinp1);
47 int inp2 = mDecoder.setLumiInp(2, lumiinp2);
48 mOutputLumiInfo.inp1 = inp1;
49 mOutputLumiInfo.inp2 = inp2;
50 mMaxInputSize = ctx.options().get<int>("max-input-size");
51 mMaxInputSizeFatal = ctx.options().get<bool>("max-input-size-fatal");
52 LOG(info) << "CTP reco init done. Inputs decoding here:" << mDecodeinputs << " DoLumi:" << mDoLumi << " DoDigits:" << mDoDigits << " NTF:" << mNTFToIntegrate << " Lumi inputs:" << lumiinp1 << ":" << inp1 << " " << lumiinp2 << ":" << inp2 << " Max errors:" << maxerrors << " Max input size:" << mMaxInputSize << " MaxInputSizeFatal:" << mMaxInputSizeFatal << " CheckConsistency:" << mCheckConsistency;
53 mMassiOutDir = ctx.options().get<std::string>("massi-out-dir");
54 LOG(info) << "Massi output dir:" << mMassiOutDir;
55 mCrossSection = ctx.options().get<double>("cross-section");
56 LOG(info) << "Cross section (ub): " << mCrossSection;
57 mReorderDepth = ctx.options().get<int>("tf-reorder-depth");
58 // mOutputLumiInfo.printInputs();
59}
61{
62 auto clsEA = mDecoder.getClassErrorsA();
63 auto clsEB = mDecoder.getClassErrorsB();
64 auto cntCA = mDecoder.getClassCountersA();
65 auto cntCB = mDecoder.getClassCountersB();
66 int totClasses = 0;
67 for (int i = 0; i < o2::ctp::CTP_NCLASSES; i++) {
68 mClsEA[i] += clsEA[i];
69 mClsEB[i] += clsEB[i];
70 mClsA[i] += cntCA[i];
71 mClsB[i] += cntCB[i];
72 totClasses += cntCA[i];
73 }
74 auto& TFOrbits = mDecoder.getTFOrbits();
75 std::sort(TFOrbits.begin(), TFOrbits.end());
76 size_t l = TFOrbits.size();
77 uint32_t o0 = 0;
78 if (l) {
79 o0 = TFOrbits[0];
80 }
81 int nmiss = 0;
82 int nprt = 0;
83 std::cout << "Missing orbits:";
84 for (int i = 1; i < l; i++) {
85 if ((TFOrbits[i] - o0) > 0x20) {
86 if (nprt < 20) {
87 std::cout << " " << o0 << "-" << TFOrbits[i];
88 }
89 nmiss += (TFOrbits[i] - o0) / 0x20;
90 nprt++;
91 }
92 o0 = TFOrbits[i];
93 }
94 std::cout << std::endl;
95 LOG(info) << "Number of non continous TF:" << nmiss << std::endl;
96 LOG(info) << "Lost in shiftInputs:" << mLostDueToShiftInps;
97 LOG(info) << "Lost in addDigit Inputs:" << mIRRejected << " Classes:" << mTCRRejected;
98 if (mErrorIR || mErrorTCR) {
99 LOG(error) << "# of IR errors:" << mErrorIR << " TCR errors:" << mErrorTCR << std::endl;
100 }
101 if (mCheckConsistency) {
102 LOG(info) << "Lost due to the shift Consistency Checker:" << mDecoder.getLostDueToShiftCls();
103 LOG(info) << "Total classes:" << totClasses;
104 auto ctpcfg = mDecoder.getCTPConfig();
105 for (int i = 0; i < o2::ctp::CTP_NCLASSES; i++) {
106 std::string name = ctpcfg.getClassNameFromIndex(i);
107 if (mClsEA[i]) {
108 LOG(error) << " Class without inputs:";
109 }
110 LOG(important) << "CLASS:" << name << ":" << i << " Cls=>Inp:" << mClsA[i] << " Inp=>Cls:" << mClsB[i] << " ErrorsCls=>Inps:" << mClsEA[i] << " MissingInps=>Cls:" << mClsEB[i];
111 }
112 }
113 flushAllPendingTFs();
114 if (mTFsInCurrentWindow > 0) {
115 double timeInterval = orbitTime * mOrbitsInCurrentWindow;
116 double totalLumi1 = 0.0;
117 double totalLumi2 = 0.0;
118 double totalLumiErr1 = 0.0;
119 double totalLumiErr2 = 0.0;
120 size_t filledBCs = mLHCBCs.count();
121 for (size_t bc = 0; bc < mCountsPerBC1.size(); ++bc) {
122 if (mCountsPerBC1[bc] > 0) {
123 double rate1 = mCountsPerBC1[bc] / timeInterval;
124 double lumi1 = rate1 / mCrossSection;
125 double lumiErr1 = std::sqrt(mCountsPerBC1[bc]) / (timeInterval * mCrossSection);
126 auto [mu, correctedRate1] = pileupCorrection(rate1);
127 double correctedLumi1 = correctedRate1 / mCrossSection;
128 writeMassiLinePerBC(bc, mWindowStartTime, lumi1, lumiErr1, correctedLumi1, correctedRate1, mu);
129 }
130 if (mLHCBCs.test(bc)) {
131 totalLumi1 += mCountsPerBC1[bc] / (timeInterval * mCrossSection);
132 totalLumi2 += mCountsPerBC2[bc] / (timeInterval * mCrossSection);
133 totalLumiErr1 += std::sqrt(mCountsPerBC1[bc]) / (timeInterval * mCrossSection);
134 totalLumiErr2 += std::sqrt(mCountsPerBC2[bc]) / (timeInterval * mCrossSection);
135 writeMassiLineLumi(mWindowStartTime, totalLumi1, totalLumiErr1);
136 }
137 }
138 LOG(info) << "Flushed trailing partial window of " << mTFsInCurrentWindow << " TFs at end of stream";
139 }
140 // Calculate and print total luminosity for given fill
141 double totalFillCountsInp1 = 0.0;
142 double totalFillCountsInp2 = 0.0;
143 for (const auto& count : mTotalCountsPerBC1) {
144 totalFillCountsInp1 += count;
145 }
146 for (const auto& count : mTotalCountsPerBC2) {
147 totalFillCountsInp2 += count;
148 }
149 // Estimate the total integrated luminosity for the fill in ub^-1 and the rate in Hz
150 double avgRate1 = totalFillCountsInp1 / mTotalElapsedTime;
151 double fillDurationSec = (mRunInfo.eor - mRunInfo.sor) / 1000.0;
152 double totalIntLumiInp1 = totalFillCountsInp1 / mCrossSection;
153 double estimatedTotalIntLumiInp1 = (avgRate1 / mCrossSection) * fillDurationSec;
154 LOG(info) << "Total Integrated Luminosity Input 1: " << totalIntLumiInp1 << " ub^-1" << " Rate (vis): " << avgRate1 << " Hz, Estimated Total Integrated Lumi: " << estimatedTotalIntLumiInp1 << " ub^-1";
155 // Close files at end of stream
156 for (auto& [bucket, ofs] : mMassiFiles) {
157 ofs.close();
158 }
159}
161{
163 mOutputDigits.clear();
164 std::map<o2::InteractionRecord, CTPDigit> digits;
168 // setUpDummyLink
169 auto& inputs = ctx.inputs();
170 auto dummyOutput = [&ctx, this]() {
171 if (this->mDoDigits) {
172 ctx.outputs().snapshot(o2::framework::Output{"CTP", "DIGITS", 0}, this->mOutputDigits);
173 }
174 if (this->mDoLumi) {
175 ctx.outputs().snapshot(o2::framework::Output{"CTP", "LUMI", 0}, this->mOutputLumiInfo);
176 }
177 };
178 // if we see requested data type input with 0xDEADBEEF subspec and 0 payload this means that the "delayed message"
179 // mechanism created it in absence of real data from upstream. Processor should send empty output to not block the workflow
180 {
181 static size_t contDeadBeef = 0; // number of times 0xDEADBEEF was seen continuously
182 std::vector<InputSpec> dummy{InputSpec{"dummy", o2::framework::ConcreteDataMatcher{"CTP", "RAWDATA", 0xDEADBEEF}}};
183 for (const auto& ref : o2::framework::InputRecordWalker(inputs, dummy)) {
184 const auto dh = o2::framework::DataRefUtils::getHeader<o2::header::DataHeader*>(ref);
186 if (payloadSize == 0) {
188 if (++contDeadBeef <= maxWarn) {
189 LOGP(alarm, "Found input [{}/{}/{:#x}] TF#{} 1st_orbit:{} Payload {} : assuming no payload for all links in this TF{}",
190 dh->dataOrigin.str, dh->dataDescription.str, dh->subSpecification, dh->tfCounter, dh->firstTForbit, payloadSize,
191 contDeadBeef == maxWarn ? fmt::format(". {} such inputs in row received, stopping reporting", contDeadBeef) : "");
192 }
193 dummyOutput();
194 return;
195 }
196 }
197 contDeadBeef = 0; // if good data, reset the counter
198 }
199 //
200 std::vector<LumiInfo> lumiPointsHBF1;
201 std::vector<InputSpec> filter{InputSpec{"filter", ConcreteDataTypeMatcher{"CTP", "RAWDATA"}, Lifetime::Timeframe}};
202 bool fatal_flag = 0;
203 size_t payloadSize = 0;
204 bool gotFirstOrbit = false;
205
206 for (const auto& ref : o2::framework::InputRecordWalker(inputs, filter)) {
207 const auto dh = o2::framework::DataRefUtils::getHeader<o2::header::DataHeader*>(ref);
208 if (!gotFirstOrbit) {
209 mFirstOrbit = dh->firstTForbit;
210 gotFirstOrbit = true;
211 if (mHavePrevTF) {
212 uint32_t expectedOrbit = mPrevTFLastOrbit;
213 if (mFirstOrbit != expectedOrbit) {
214 int64_t diff = static_cast<int64_t>(mFirstOrbit) - static_cast<int64_t>(mPrevTFLastOrbit);
215 if (diff < 0) {
216 LOG(warning) << "TF arrived out of order: previous TF ended at orbit " << mPrevTFLastOrbit << ", this TF starts at " << mFirstOrbit << " (orbit went backwards by " << diff << ")";
217 } else if (diff > 0) {
218 LOG(warning) << "Gap detected: previous TF ended at orbit " << expectedOrbit << ", this TF starts at " << mFirstOrbit << " (missing " << (mFirstOrbit - expectedOrbit) << " orbits)";
219 }
220 }
221 }
222 }
223 mPrevTFLastOrbit = mFirstOrbit + mRunInfo.orbitsPerTF;
224 mHavePrevTF = true;
225 if (mMaxInputSize > 0) {
227 }
228 }
229 LOG(info) << "mFirstOrbit for this TF: " << mFirstOrbit << " gotFirstOrbit: " << gotFirstOrbit;
230 // if (payloadSize > (size_t)mMaxInputSize) {
231 if (mMaxInputSize > 0 && payloadSize > (size_t)mMaxInputSize) {
232 if (mMaxInputSizeFatal) {
233 fatal_flag = 1;
234 LOG(error) << "Input data size bigger than threshold: " << mMaxInputSize << " < " << payloadSize << " decoding TF and exiting.";
235 // LOG(fatal) << "Input data size:" << payloadSize; - fatal issued in decoder
236 } else {
237 LOG(error) << "Input data size:" << payloadSize << " sending dummy output";
238 dummyOutput();
239 return;
240 }
241 }
242
243 int ret = 0;
244 if (fatal_flag) {
245 ret = mDecoder.decodeRawFatal(inputs, filter);
246 } else {
247 ret = mDecoder.decodeRaw(inputs, filter, mOutputDigits, lumiPointsHBF1);
248 }
249 if (ret == 1) {
250 dummyOutput();
251 return;
252 }
253 if (mDoDigits) {
254 LOG(info) << "[CTPRawToDigitConverter - run] Writing " << mOutputDigits.size() << " digits. IR rejected:" << mDecoder.getIRRejected() << " TCR rejected:" << mDecoder.getTCRRejected();
255 ctx.outputs().snapshot(o2::framework::Output{"CTP", "DIGITS", 0}, mOutputDigits);
256 mLostDueToShiftInps += mDecoder.getLostDueToShiftInp();
257 mErrorIR += mDecoder.getErrorIR();
258 mErrorTCR += mDecoder.getErrorTCR();
259 mIRRejected += mDecoder.getIRRejected();
260 mTCRRejected += mDecoder.getTCRRejected();
261 // Luminosity per bunch crossing
262 computeLumiPerBC(mOutputDigits, mFirstOrbit, static_cast<uint32_t>(mRunInfo.orbitsPerTF));
263 }
264 if (mDoLumi) {
265 uint32_t tfCountsT = 0;
266 uint32_t tfCountsV = 0;
267 for (auto const& lp : lumiPointsHBF1) {
268 tfCountsT += lp.counts;
269 tfCountsV += lp.countsFV0;
270 }
271 // LOG(info) << "Lumi rate:" << tfCounts/(128.*88e-6);
272 // FT0
273 mHistoryT.push_back(tfCountsT);
274 mCountsT += tfCountsT;
275 if (mHistoryT.size() <= mNTFToIntegrate) {
276 mNHBIntegratedT += lumiPointsHBF1.size();
277 } else {
278 mCountsT -= mHistoryT.front();
279 mHistoryT.pop_front();
280 }
281 // FV0
282 mHistoryV.push_back(tfCountsV);
283 mCountsV += tfCountsV;
284 if (mHistoryV.size() <= mNTFToIntegrate) {
285 mNHBIntegratedV += lumiPointsHBF1.size();
286 } else {
287 mCountsV -= mHistoryV.front();
288 mHistoryV.pop_front();
289 }
290 //
291 if (mNHBIntegratedT || mNHBIntegratedV) {
292 mOutputLumiInfo.orbit = lumiPointsHBF1[0].orbit;
293 }
294 mOutputLumiInfo.counts = mCountsT;
295
296 mOutputLumiInfo.countsFV0 = mCountsV;
297 mOutputLumiInfo.nHBFCounted = mNHBIntegratedT;
298 mOutputLumiInfo.nHBFCountedFV0 = mNHBIntegratedV;
299 if (mVerbose) {
300 mOutputLumiInfo.printInputs();
301 LOGP(info, "Orbit {}: {}/{} counts inp1/inp2 in {}/{} HBFs -> lumi_inp1 = {:.3e}+-{:.3e} lumi_inp2 = {:.3e}+-{:.3e}", mOutputLumiInfo.orbit, mCountsT, mCountsV, mNHBIntegratedT, mNHBIntegratedV, mOutputLumiInfo.getLumi(), mOutputLumiInfo.getLumiError(), mOutputLumiInfo.getLumiFV0(), mOutputLumiInfo.getLumiFV0Error());
302 }
303 ctx.outputs().snapshot(o2::framework::Output{"CTP", "LUMI", 0}, mOutputLumiInfo);
304 }
305}
306// Function to compute luminosity per BC from the interaction counts from CTP digits
307// std::pair<std::array<double, o2::constants::lhc::LHCMaxBunches>, std::array<double, o2::constants::lhc::LHCMaxBunches>>
308void RawDecoderSpec::computeLumiPerBC(const o2::pmr::vector<CTPDigit>& ctpdigits, uint32_t firstOrbit, uint32_t orbitsPerTF)
309{
310 int inp1 = mOutputLumiInfo.inp1;
311 int inp2 = mOutputLumiInfo.inp2;
312
313 uint64_t inputMask1 = 1ull << (inp1 - 1); // TVX
314 uint64_t inputMask2 = 1ull << (inp2 - 1); // VBA
315
316 std::array<double, o2::constants::lhc::LHCMaxBunches> tfCountsPerBC1{};
317 std::array<double, o2::constants::lhc::LHCMaxBunches> tfCountsPerBC2{};
318
319 for (const auto& digit : ctpdigits) {
320 uint32_t orbit = digit.intRecord.orbit;
321 if (orbit < firstOrbit || orbit >= firstOrbit + orbitsPerTF) {
322 LOG(warning) << "Digit orbit " << orbit << " outside expected TF range [" << firstOrbit << ", " << (firstOrbit + orbitsPerTF) << ") - skipping";
323 continue;
324 }
325 uint64_t mask = digit.CTPInputMask.to_ullong();
326 uint16_t bc = digit.intRecord.bc;
328 if (mask & inputMask1) {
329 tfCountsPerBC1[bc] += 1.0;
330 }
331 if (mask & inputMask2) {
332 tfCountsPerBC2[bc] += 1.0;
333 }
334 }
335 }
336 int64_t unixTimeStart = unixTimeForOrbitStart(firstOrbit);
337 if (mPendingTFs.count(firstOrbit)) {
338 LOG(warning) << "Duplicate firstOrbit " << firstOrbit << " received - overwriting pending entry";
339 }
340 mPendingTFs[firstOrbit] = PendingTF{tfCountsPerBC1, tfCountsPerBC2, unixTimeStart, orbitsPerTF};
341 if (!mPendingTFs.empty()) {
342 uint32_t smallestPending = mPendingTFs.begin()->first;
343 if (firstOrbit < smallestPending) {
344 LOG(warning) << "Late TF: firstOrbit=" << firstOrbit << " arrived after smallest pending=" << smallestPending;
345 }
346 }
347 flushReadyTFs();
348 // integrateLumi(tfCountsPerBC1, tfCountsPerBC2, unixTimeStart, orbitsPerTF);
349}
350// Accumulate luminosity per BC over multiple time frames
351void RawDecoderSpec::integrateLumi(const std::array<double, o2::constants::lhc::LHCMaxBunches>& tfCounts1, const std::array<double, o2::constants::lhc::LHCMaxBunches>& tfCounts2, int64_t unixTimeStart, uint32_t nOrbitsThisTF)
352{
353 if (mTFsInCurrentWindow == 0) {
354 mWindowStartTime = unixTimeStart;
355 }
356
357 for (size_t bc = 0; bc < mCountsPerBC1.size(); ++bc) {
358 mCountsPerBC1[bc] += tfCounts1[bc];
359 mTotalCountsPerBC1[bc] += tfCounts1[bc];
360 }
361 for (size_t bc = 0; bc < mCountsPerBC2.size(); ++bc) {
362 mCountsPerBC2[bc] += tfCounts2[bc];
363 mTotalCountsPerBC2[bc] += tfCounts2[bc];
364 }
365 mTotalElapsedTime += nOrbitsThisTF * orbitTime;
366 mOrbitsInCurrentWindow += nOrbitsThisTF;
367 ++mTFsInCurrentWindow;
368
369 if (mTFsInCurrentWindow < mNTFToIntegrate) {
370 return; // Window not yet filled
371 }
372
373 if (mTFsInCurrentWindow >= mNTFToIntegrate) {
374 double timeInterval = orbitTime * mOrbitsInCurrentWindow; // Total time in seconds for the current window
375 // Count number of filled BCs
376 size_t filledBCs = mLHCBCs.count();
377 // Total lumi over filled BCs for this window
378 double totalLumi1 = 0.0;
379 double totalLumi2 = 0.0;
380 double totalLumiErr1 = 0.0;
381 double totalLumiErr2 = 0.0;
382 for (size_t bc = 0; bc < mCountsPerBC1.size(); ++bc) { // Luminosity per BC
383 if (mCountsPerBC1[bc] > 0 || mCountsPerBC2[bc] > 0) {
384 double rate1 = mCountsPerBC1[bc] / timeInterval;
385 double rate2 = mCountsPerBC2[bc] / timeInterval;
386 double lumi1 = rate1 / mCrossSection;
387 double lumi2 = rate2 / mCrossSection;
388 double lumiErr1 = std::sqrt(mCountsPerBC1[bc]) / (timeInterval * mCrossSection);
389 double lumiErr2 = std::sqrt(mCountsPerBC2[bc]) / (timeInterval * mCrossSection);
390 auto [mu, correctedRate1] = pileupCorrection(rate1);
391 double correctedLumi1 = correctedRate1 / mCrossSection;
392 if (mCountsPerBC1[bc] > 0) {
393 // LOG(info) << "BC: " << bc + 1 << " Rate: " << rate1 << " Corrected Rate: " << correctedRate1 << " mu: " << mu;
394 writeMassiLinePerBC(bc, mWindowStartTime, lumi1, lumiErr1, correctedRate1, correctedLumi1, mu);
395 }
396 }
397
398 // Total luminosity over filled BCs for this window
399 if (mLHCBCs.test(bc)) {
400 totalLumi1 += mCountsPerBC1[bc] / (timeInterval * mCrossSection);
401 totalLumi2 += mCountsPerBC2[bc] / (timeInterval * mCrossSection);
402 totalLumiErr1 += std::sqrt(mCountsPerBC1[bc]) / (timeInterval * mCrossSection);
403 totalLumiErr2 += std::sqrt(mCountsPerBC2[bc]) / (timeInterval * mCrossSection);
404 }
405 }
406 writeMassiLineLumi(mWindowStartTime, totalLumi1, totalLumiErr1);
407 // Reset counters for the next window
408 mCountsPerBC1.fill(0.0);
409 mCountsPerBC2.fill(0.0);
410 mTFsInCurrentWindow = 0;
411 mOrbitsInCurrentWindow = 0;
412 }
413}
414void RawDecoderSpec::writeMassiLinePerBC(int bc, int64_t unixTimeStart, double lumi, double lumiErr, double correctedRate, double correctedLumi, double mu)
415{
416 int rfBucket = (bc * 10) + 1;
417 auto it = mMassiFiles.find(rfBucket);
418 if (it == mMassiFiles.end()) {
419 std::string dirPath = mMassiOutDir + "/" + std::to_string(mMassiYear) + "/lumi/" + mFillNumber;
420
421 std::error_code ec;
422 std::filesystem::create_directories(dirPath, ec);
423 if (ec) {
424 LOG(error) << "Failed to create Massi output directory " << dirPath << ": " << ec.message();
425 return;
426 }
427 std::string filename = dirPath + "/" + mFillNumber + "_lumi_" + std::to_string(rfBucket) + "_ALICE.txt";
428 auto result = mMassiFiles.emplace(rfBucket, std::ofstream(filename, std::ios::app));
429 it = result.first;
430 }
431 std::ofstream& ofs = it->second;
432 ofs << std::fixed << std::setprecision(0) << unixTimeStart << " " << mStableBeams << " ";
433 ofs << (std::abs(lumi) < 1e-3 ? std::scientific : std::fixed) << std::setprecision(7) << lumi << " ";
434 ofs << (std::abs(lumiErr) < 1e-3 ? std::scientific : std::fixed) << std::setprecision(7) << lumiErr << " ";
435 ofs << (std::abs(correctedLumi) < 1e-3 ? std::scientific : std::fixed) << std::setprecision(7) << correctedLumi << " ";
436 ofs << std::fixed << std::setprecision(7) << correctedRate << " " << mu << " " << std::endl;
437 ofs.flush();
438}
439void RawDecoderSpec::writeMassiLineLumi(int64_t unixTimeStart, double lumi, double lumiErr)
440{
441 std::string dirPath = mMassiOutDir + "/" + std::to_string(mMassiYear) + "/lumi/" + mFillNumber;
442 std::error_code ec;
443 std::filesystem::create_directories(dirPath, ec);
444 if (ec) {
445 LOG(error) << "Failed to create Massi output directory " << dirPath << ": " << ec.message();
446 return;
447 }
448 std::string filename = dirPath + "/" + mFillNumber + "_lumi_ALICE.txt";
449 std::ofstream ofs(filename, std::ios::app);
450 ofs << std::fixed << std::setprecision(0) << unixTimeStart << " " << mStableBeams << " ";
451 ofs << (std::abs(lumi) < 1e-3 ? std::scientific : std::fixed) << std::setprecision(7) << lumi << " ";
452 ofs << (std::abs(lumiErr) < 1e-3 ? std::scientific : std::fixed) << std::setprecision(7) << lumiErr << " " << std::endl;
453 ofs.flush();
454}
456{
457 int64_t orbitResetTimeMUS = mRunInfo.orbitReset;
458 return (orbitResetTimeMUS + static_cast<int64_t>(orbit) * o2::constants::lhc::LHCOrbitMUS) * 1e-3; // Return in milliseconds
459}
460int RawDecoderSpec::yearFromUnixTime(int64_t unixTimeStart) const
461{
462 std::time_t time = static_cast<std::time_t>(unixTimeStart);
463 std::tm* tm = std::gmtime(&time);
464 return tm->tm_year + 1900;
465}
467{
468 auto& ccdbMgr = o2::ccdb::BasicCCDBManager::instance();
470 mOrbitsPerTF = mRunInfo.orbitsPerTF;
471 mMassiYear = yearFromUnixTime(mRunInfo.sor / 1000.0);
472 mOrbitResetTimeSec = mRunInfo.orbitReset * 1e-6;
473 mRunStartTime = mRunInfo.sor / 1000;
474 mRunEndTime = mRunInfo.eor / 1000;
475 LOG(info) << "Run start time: " << mRunStartTime << " Run end time: " << mRunEndTime;
476}
477void RawDecoderSpec::flushReadyTFs()
478{
479 while (mPendingTFs.size() > mReorderDepth) {
480 auto it = mPendingTFs.begin();
481 integrateLumi(it->second.countsPerBC1, it->second.countsPerBC2, it->second.unixTimeStart, it->second.nOrbitsThisTF);
482 mPendingTFs.erase(it);
483 }
484}
485void RawDecoderSpec::flushAllPendingTFs()
486{
487 while (!mPendingTFs.empty()) {
488 auto it = mPendingTFs.begin();
489 integrateLumi(it->second.countsPerBC1, it->second.countsPerBC2, it->second.unixTimeStart, it->second.nOrbitsThisTF);
490 mPendingTFs.erase(it);
491 }
492}
493std::pair<double, double> RawDecoderSpec::pileupCorrection(double rate) const
494{
496 if (p >= 1.0) {
497 LOG(warning) << "Pile-up correction: p = " << p << " >= 1";
498 return {0, 0};
499 }
500 double mu = -std::log(1 - p);
501 double correctedRate = mu * o2::constants::lhc::LHCRevFreq;
502 return {mu, correctedRate};
503}
505{
506 if (!digits && !lumi) {
507 throw std::runtime_error("all outputs were disabled");
508 }
509 std::vector<o2::framework::InputSpec> inputs;
510 inputs.emplace_back("TF", o2::framework::ConcreteDataTypeMatcher{"CTP", "RAWDATA"}, o2::framework::Lifetime::Timeframe);
511 if (askDISTSTF) {
512 inputs.emplace_back("stdDist", "FLP", "DISTSUBTIMEFRAME", 0, o2::framework::Lifetime::Timeframe);
513 }
514
515 std::vector<o2::framework::OutputSpec> outputs;
516 inputs.emplace_back("ctpconfig", "CTP", "CTPCONFIG", 0, o2::framework::Lifetime::Condition, o2::framework::ccdbParamSpec("CTP/Config/Config", 1));
517 inputs.emplace_back("grplhcif", "GLO", "GRPLHCIF", 0, o2::framework::Lifetime::Condition, o2::framework::ccdbParamSpec("GLO/Config/GRPLHCIF"));
518 inputs.emplace_back("trigoffset", "CTP", "Trig_Offset", 0, o2::framework::Lifetime::Condition, o2::framework::ccdbParamSpec("CTP/Config/TriggerOffsets"));
519 if (digits) {
520 outputs.emplace_back("CTP", "DIGITS", 0, o2::framework::Lifetime::Timeframe);
521 }
522 if (lumi) {
523 outputs.emplace_back("CTP", "LUMI", 0, o2::framework::Lifetime::Timeframe);
524 }
526 "ctp-raw-decoder-lumi",
527 inputs,
528 outputs,
529 o2::framework::AlgorithmSpec{o2::framework::adaptFromTask<o2::ctp::reco_workflow::RawDecoderSpec>(digits, lumi)},
531 {"ntf-to-average", o2::framework::VariantType::Int, 100, {"Time interval for averaging luminosity in units of TF"}},
532 {"print-errors-num", o2::framework::VariantType::Int, 3, {"Max number of errors to print"}},
533 {"lumi-inp1", o2::framework::VariantType::String, "TVX", {"The first input used for online lumi. Name in capital."}},
534 {"lumi-inp2", o2::framework::VariantType::String, "VBA", {"The second input used for online lumi. Name in capital."}},
535 {"use-verbose-mode", o2::framework::VariantType::Bool, false, {"Verbose logging"}},
536 {"max-input-size", o2::framework::VariantType::Int, 0, {"Do not process input if bigger than max size, 0 - do not check"}},
537 {"max-input-size-fatal", o2::framework::VariantType::Bool, false, {"If true issue fatal error otherwise error only"}},
538 {"check-consistency", o2::framework::VariantType::Bool, false, {"If true checks digits consistency using ctp config"}},
539 {"ctpinputs-decoding", o2::framework::VariantType::Bool, false, {"Inputs alignment: true - raw decoder - has to be compatible with CTF decoder: allowed options: 10,01,00"}},
540 {"cross-section", o2::framework::VariantType::Double, 59500.0, {"Cross-section in ub, default for pp collisions"}},
541 {"tf-reorder-depth", o2::framework::VariantType::Int, 300, {"Number of TFs to buffer to correct out of-order TF delivery"}},
542 {"massi-out-dir", o2::framework::VariantType::String, ".", {"Output directory for Massi files"}}}};
543}
545{
547 pc.inputs().get<o2::ctp::TriggerOffsetsParam*>("trigoffset");
548 const auto& trigOffsParam = o2::ctp::TriggerOffsetsParam::Instance();
549 LOG(info) << "updateing TroggerOffsetsParam: inputs L0_L1:" << trigOffsParam.L0_L1 << " classes L0_L1:" << trigOffsParam.L0_L1_classes;
550 const auto ctpcfg = pc.inputs().get<o2::ctp::CTPConfiguration*>("ctpconfig");
551 if (ctpcfg != nullptr) {
552 mDecoder.setCTPConfig(*ctpcfg);
553 LOG(info) << "ctpconfig for run done:" << mDecoder.getCTPConfig().getRunNumber();
554 }
555 const auto grplhcif = pc.inputs().get<o2::parameters::GRPLHCIFData*>("grplhcif");
556 if (grplhcif != nullptr) {
557 LOG(info) << "GRPLHCIF injection scheme: " << grplhcif->getInjectionScheme();
558 LOG(info) << "Bunch filling with time: " << grplhcif->getBunchFillingTime();
559 LOG(info) << "Fill number time: " << grplhcif->getFillNumberTime();
560 LOG(info) << "Injection scheme time: " << grplhcif->getInjectionSchemeTime();
561
562 // Get filled bunches
563 auto bfilling = grplhcif->getBunchFilling();
564 std::vector<int> bcs = bfilling.getFilledBCs();
565 LOG(info) << "Filled BCs: " << bcs.size();
566 mLHCBCs.reset();
567 for (auto const& bc : bcs) {
568 mLHCBCs.set(bc, 1);
569 }
570 mFillNumber = std::to_string(grplhcif->getFillNumber());
571 }
572 int runNumber = pc.services().get<o2::framework::TimingInfo>().runNumber;
573 fetchRunInfo(runNumber);
574 }
575}
definition of CTPConfiguration and related CTP structures
uint64_t orbit
Definition RawEventData.h:6
uint64_t bc
Definition RawEventData.h:5
int16_t time
Definition RawEventData.h:4
int32_t i
container for the LHC InterFace data
A helper class to iteratate over all parts of all input routes.
Header to collect LHC related constants.
static BasicCCDBManager & instance()
std::string getClassNameFromIndex(int index)
std::array< uint64_t, o2::ctp::CTP_NCLASSES > getClassCountersA()
uint32_t getTCRRejected() const
std::array< uint64_t, o2::ctp::CTP_NCLASSES > getClassErrorsA()
std::array< uint64_t, o2::ctp::CTP_NCLASSES > getClassCountersB()
std::vector< uint32_t > & getTFOrbits()
void setCTPConfig(CTPConfiguration cfg)
int setLumiInp(int lumiinp, std::string inp)
void setDoDigits(bool digi)
int decodeRaw(o2::framework::InputRecord &inputs, std::vector< o2::framework::InputSpec > &filter, o2::pmr::vector< CTPDigit > &digits, std::vector< LumiInfo > &lumiPointsHBF1)
std::array< uint64_t, o2::ctp::CTP_NCLASSES > getClassErrorsB()
CTPConfiguration & getCTPConfig()
void setCheckConsistency(bool check)
void setDecodeInps(bool decodeinps)
void setDoLumi(bool lumi)
int decodeRawFatal(o2::framework::InputRecord &inputs, std::vector< o2::framework::InputSpec > &filter)
uint32_t getIRRejected() const
void endOfStream(o2::framework::EndOfStreamContext &ec) final
This is invoked whenever we have an EndOfStream event.
void updateTimeDependentParams(framework::ProcessingContext &pc)
void computeLumiPerBC(const o2::pmr::vector< CTPDigit > &ctpdigits, uint32_t firstOrbit, uint32_t orbitsPerTF)
Compute per BC luminosity from the interaction counts from CTP digits.
void init(framework::InitContext &ctx) final
Initializing the RawDecoderSpec.
void writeMassiLinePerBC(int bc, int64_t unixTime, double lumi, double lumiErr, double correctedRate, double correctedLumi, double mu)
void writeMassiLineLumi(int64_t unixTime, double lumi, double lumiErr)
void integrateLumi(const std::array< double, o2::constants::lhc::LHCMaxBunches > &tfCounts1, const std::array< double, o2::constants::lhc::LHCMaxBunches > &tfCounts2, int64_t unixTime, uint32_t nOrbitsThisTF)
Integrate luminosity per BC over multiple time frames.
void run(framework::ProcessingContext &ctx) final
Run conversion of raw data to cells.
int yearFromUnixTime(int64_t unixTime) const
int64_t unixTimeForOrbitStart(uint32_t orbit) const
A helper class to iteratate over all parts of all input routes.
decltype(auto) get(R binding, int part=0) const
InputRecord & inputs()
The inputs associated with this processing context.
ServiceRegistryRef services()
The services registry associated with this processing context.
const std::string & getInjectionScheme() const
GLint GLsizei count
Definition glcorearb.h:399
GLuint64EXT * result
Definition glcorearb.h:5662
GLuint GLenum * rate
Definition glcorearb.h:5735
GLuint const GLchar * name
Definition glcorearb.h:781
GLint GLint GLint GLint GLint GLint GLint GLbitfield GLenum filter
Definition glcorearb.h:1308
GLint GLuint mask
Definition glcorearb.h:291
constexpr int LHCMaxBunches
constexpr double LHCOrbitMUS
constexpr double LHCRevFreq
o2::framework::DataProcessorSpec getRawDecoderSpec(bool askSTFDist, bool digits, bool lumi)
Creating DataProcessorSpec for the CTP.
Lifetime
Possible Lifetime of objects being exchanged by the DPL.
Definition Lifetime.h:18
std::vector< ConfigParamSpec > ccdbParamSpec(std::string const &path, int runDependent, std::vector< CCDBMetadata > metadata={}, int qrate=0)
std::vector< ConfigParamSpec > Options
const track::TrackFitContext< NLayers > const TrackFollowContext< NLayers > & ctx
std::vector< T, fair::mq::pmr::polymorphic_allocator< T > > vector
constexpr double mu
Definition Specs.h:32
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
std::string filename()
void printInputs() const
Definition LumiInfo.cxx:20
uint64_t countsFV0
Definition LumiInfo.h:29
uint64_t counts
Definition LumiInfo.h:28
uint32_t nHBFCounted
Definition LumiInfo.h:26
float getLumiFV0Error() const
Definition LumiInfo.h:36
uint32_t nHBFCountedFV0
Definition LumiInfo.h:27
float getLumiError() const
Definition LumiInfo.h:35
float getLumi() const
Definition LumiInfo.h:32
float getLumiFV0() const
Definition LumiInfo.h:33
uint32_t orbit
Definition LumiInfo.h:25
static o2::header::DataHeader::PayloadSizeType getPayloadSize(const DataRef &ref)
static AggregatedRunInfo buildAggregatedRunInfo_DATA(o2::ccdb::CCDBManagerInstance &ccdb, int runnumber)
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
LumiInfo lumi
std::vector< Digit > digits