Project
Loading...
Searching...
No Matches
RawToCellConverterSpec.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#include <string>
12#include <fairlogger/Logger.h>
23#include "CCDB/CcdbApi.h"
24#include "PHOSBase/Mapping.h"
32
33using namespace o2::phos::reco_workflow;
34
36{
37 LOG(debug) << "Initialize converter ";
38
39 auto path = ctx.options().get<std::string>("mappingpath");
41
42 auto fitmethod = ctx.options().get<std::string>("fitmethod");
43 if (fitmethod == "default") {
44 LOG(info) << "Using default raw fitter";
45 mRawFitter = std::unique_ptr<o2::phos::CaloRawFitter>(new o2::phos::CaloRawFitter);
46 }
47 if (fitmethod == "semigaus") {
48 LOG(info) << "Using SemiGauss raw fitter";
49 mRawFitter = std::unique_ptr<o2::phos::CaloRawFitter>(new o2::phos::CaloRawFitterGS);
50 }
51
52 mFillChi2 = (ctx.options().get<std::string>("fillchi2").compare("on") == 0);
53 if (mFillChi2) {
54 LOG(info) << "Fit quality output will be filled";
55 }
56
57 mDecoder = std::make_unique<AltroDecoder>();
58
59 mPedestalRun = (ctx.options().get<std::string>("pedestal").compare("on") == 0);
60 if (mPedestalRun) {
61 mRawFitter->setPedestal();
62 mDecoder->setPedestalRun(); // sets also keeping both HG and LG channels
63 LOG(info) << "Pedestal run will be processed";
64 }
65
66 mCombineGHLG = (ctx.options().get<std::string>("keepHGLG").compare("on") != 0);
67 if (!mCombineGHLG) {
68 mDecoder->setCombineHGLG(false);
69 LOG(info) << "Both HighGain and LowGain will be kept";
70 }
71 int presamples = ctx.options().get<int>("presamples");
72 mDecoder->setPresamples(presamples);
73 LOG(info) << "Using " << presamples << " pre-samples";
74
75 mKeepTrigNoise = (ctx.options().get<std::string>("keeptrig").compare("on") == 0);
76 if (mKeepTrigNoise) {
77 mDecoder->setKeepTruNoise(mKeepTrigNoise);
78 LOG(info) << "Both trigger digits and summary tables will be kept";
79 }
80}
81
83{
84 // Cache cells from bunch crossings as the component reads timeframes from many links consecutively
85 std::vector<o2::InteractionRecord> irList;
86 std::vector<std::array<short, 56>> cellTRURanges; // start/end points for cells in mTmpCells[ddl] arrays
87 for (int iddl = 14; iddl--;) {
88 mTmpCells[iddl].clear();
89 mTmpTRU[iddl].clear();
90 }
91 mOutputHWErrors.clear();
92 if (mFillChi2) {
93 mOutputFitChi.clear();
94 }
95
96 // if we see requested data type input with 0xDEADBEEF subspec and 0 payload this means that the "delayed message"
97 // mechanism created it in absence of real data from upstream. Processor should send empty output to not block the workflow
98 static size_t contDeadBeef = 0; // number of times 0xDEADBEEF was seen continuously
99 std::vector<o2::framework::InputSpec> dummy{o2::framework::InputSpec{"dummy", o2::framework::ConcreteDataMatcher{"PHS", o2::header::gDataDescriptionRawData, 0xDEADBEEF}}};
100 for (const auto& ref : framework::InputRecordWalker(ctx.inputs(), dummy)) {
101 const auto dh = o2::framework::DataRefUtils::getHeader<o2::header::DataHeader*>(ref);
103 if (payloadSize == 0) { // send empty output
105 if (++contDeadBeef <= maxWarn) {
106 LOGP(warning, "Found input [{}/{}/{:#x}] TF#{} 1st_orbit:{} Payload {} : assuming no payload for all links in this TF{}",
107 dh->dataOrigin.str, dh->dataDescription.str, dh->subSpecification, dh->tfCounter, dh->firstTForbit, payloadSize,
108 contDeadBeef == maxWarn ? fmt::format(". {} such inputs in row received, stopping reporting", contDeadBeef) : "");
109 }
110 mOutputCells.clear();
111 ctx.outputs().snapshot(o2::framework::Output{"PHS", "CELLS", mflpId}, mOutputCells);
112 mOutputTriggerRecords.clear();
113 ctx.outputs().snapshot(o2::framework::Output{"PHS", "CELLTRIGREC", mflpId}, mOutputTriggerRecords);
114 mOutputHWErrors.clear();
115 ctx.outputs().snapshot(o2::framework::Output{"PHS", "RAWHWERRORS", 0}, mOutputHWErrors);
116 if (mFillChi2) {
117 mOutputFitChi.clear();
118 ctx.outputs().snapshot(o2::framework::Output{"PHS", "CELLFITQA", 0}, mOutputFitChi);
119 }
120 return; // empty TF, nothing to process
121 }
122 }
123 contDeadBeef = 0; // if good data, reset the counter
124
125 if (mInitSimParams) { // trigger reading sim/rec parameters from CCDB, singleton initiated in Fetcher
126 ctx.inputs().get<o2::phos::PHOSSimParams*>("recoparams");
127 mInitSimParams = false;
128 }
129
130 std::vector<o2::framework::InputSpec> inputFilter{o2::framework::InputSpec{"filter", o2::framework::ConcreteDataTypeMatcher{"PHS", "RAWDATA"}, o2::framework::Lifetime::Timeframe}};
131 for (const auto& rawData : framework::InputRecordWalker(ctx.inputs(), inputFilter)) {
132
133 const gsl::span<const char>& rawmemory = o2::framework::DataRefUtils::as<const char>(rawData);
134 if (rawmemory.size() == 0) {
135 continue;
136 }
137
138 o2::phos::RawReaderMemory rawreader(rawmemory);
139
140 // loop over all the DMA pages
141 while (rawreader.hasNext()) {
142 try {
143 rawreader.next();
145 // LOG(error) << "Raw decoding error " << (int)e;
146 // add error list
147 mOutputHWErrors.emplace_back(14, (int)e, 1); // Put general errors to non-existing DDL14
148 // if problem in header, abandon this page
152 break;
153 }
154 // if problem in payload, try to continue
155 continue;
156 }
157 auto& header = rawreader.getRawHeader();
158 auto triggerBC = o2::raw::RDHUtils::getTriggerBC(header);
159 auto triggerOrbit = o2::raw::RDHUtils::getTriggerOrbit(header);
160 auto ddl = o2::raw::RDHUtils::getFEEID(header);
161
162 if (ddl >= o2::phos::Mapping::NDDL) { // only 0..13 correct DDLs
163 LOG(error) << "DDL=" << ddl;
164 mOutputHWErrors.emplace_back(14, 16, char(ddl)); // Add non-existing DDL as DDL 15
165 continue; // skip STU ddl
166 }
167
168 o2::InteractionRecord currentIR(triggerBC, triggerOrbit);
169 // Correct for L0-LM trigger lattency
170 const auto tfOrbitFirst = ctx.services().get<o2::framework::TimingInfo>().firstTForbit;
171 const auto& ctpOffsets = o2::ctp::TriggerOffsetsParam::Instance();
172 if (currentIR.differenceInBC({0, tfOrbitFirst}) >= ctpOffsets.LM_L0) {
173 currentIR -= ctpOffsets.LM_L0; // guaranteed to stay in the TF containing the collision
174 } else { // discard the data associated with this IR as they came from previous TF
175 continue;
176 }
177
178 auto irIter = irList.rbegin();
179 auto rangeIter = cellTRURanges.rbegin();
180 while (irIter != irList.rend() && *irIter != currentIR) {
181 irIter++;
182 rangeIter++;
183 }
184 if (irIter != irList.rend()) { // found
185 (*rangeIter)[2 * ddl] = mTmpCells[ddl].size(); // start of the cell list
186 (*rangeIter)[28 + 2 * ddl] = mTmpTRU[ddl].size(); // start of the tru list
187 } else { // create new entry
188 irList.push_back(currentIR);
189 cellTRURanges.emplace_back();
190 cellTRURanges.back().fill(0);
191 cellTRURanges.back()[2 * ddl] = mTmpCells[ddl].size();
192 cellTRURanges.back()[28 + 2 * ddl] = mTmpTRU[ddl].size();
193 rangeIter = cellTRURanges.rbegin();
194 }
195 std::vector<Cell>& currentCellContainer = mTmpCells[ddl];
196 std::vector<Cell>& currentTRUContainer = mTmpTRU[ddl];
197
198 // use the altro decoder to decode the raw data, and extract the RCU trailer
199 mDecoder->decode(rawreader, mRawFitter.get(), currentCellContainer, currentTRUContainer);
200 const std::vector<o2::phos::RawReaderError>& errs = mDecoder->hwerrors();
201 for (auto a : errs) {
202 mOutputHWErrors.emplace_back(a);
203 }
204 if (mFillChi2) {
205 const std::vector<short>& chi2list = mDecoder->chi2list();
206 for (auto a : chi2list) {
207 mOutputFitChi.emplace_back(a);
208 }
209 }
210
211 // Sort cells according to cell ID
212 (*rangeIter)[2 * ddl + 1] = currentCellContainer.size();
213 auto itBegin = currentCellContainer.begin() + (*rangeIter)[2 * ddl];
214 std::sort(itBegin, currentCellContainer.end(), [](o2::phos::Cell& lhs, o2::phos::Cell& rhs) { return lhs.getAbsId() < rhs.getAbsId(); });
215 auto itTrBegin = currentTRUContainer.begin() + (*rangeIter)[28 + 2 * ddl];
216 (*rangeIter)[28 + 2 * ddl + 1] = currentTRUContainer.size();
217 std::sort(itTrBegin, currentTRUContainer.end(), [](o2::phos::Cell& lhs, o2::phos::Cell& rhs) { return lhs.getTRUId() < rhs.getTRUId(); });
218 } // RawReader::hasNext
219 }
220
221 // Loop over BCs, sort cells with increasing cell ID and write to output containers
222 mOutputCells.clear();
223 if (mLastSize > 0) {
224 mOutputCells.reserve(mLastSize);
225 }
226 mOutputTriggerRecords.clear();
227 auto rangeIter = cellTRURanges.begin();
228 for (auto irIter = irList.begin(); irIter != irList.end(); ++irIter, ++rangeIter) {
229 // find all DDLs for current BC
230 // sort separately then concatenate
231 int prevCellSize = mOutputCells.size();
232 for (int iddl = 0; iddl < 14; iddl++) {
233 auto cbegin = mTmpCells[iddl].begin() + (*rangeIter)[2 * iddl];
234 auto cend = mTmpCells[iddl].begin() + (*rangeIter)[2 * iddl + 1];
235
236 if (mCombineGHLG && !mPedestalRun) { // combine for normal data, do not combine e.g. for LED run and pedestal
237 // Combine HG and LG sells
238 // Should be next to each other after sorting
239 auto it1 = cbegin;
240 auto it2 = cbegin;
241 it2++;
242 while (it1 != cend) {
243 if (it2 != cend) {
244 if ((*it1).getAbsId() == (*it2).getAbsId()) { // HG and LG channels, if both, copy only HG as more precise
245 if ((*it1).getType() == o2::phos::HIGH_GAIN) {
246 if ((*it1).getEnergy() < 1023) { // to avoid saturation in Cell creation
247 mOutputCells.push_back(*it1);
248 } else {
249 mOutputCells.push_back(*it2);
250 }
251 } else { // it2 is HighGain
252 if ((*it2).getEnergy() < 1023) { // to avoid saturation in Cell creation
253 mOutputCells.push_back(*it2);
254 } else {
255 mOutputCells.push_back(*it1);
256 }
257 }
258 ++it1; // yes increase twice
259 if (it1 == cend) {
260 break;
261 }
262 ++it2;
263 } else { // no double cells, copy this one
264 mOutputCells.push_back(*it1);
265 }
266 ++it2;
267 } else { // just copy last one
268 mOutputCells.push_back(*it1);
269 }
270 ++it1;
271 }
272 } else {
273 mOutputCells.insert(mOutputCells.end(), cbegin, cend);
274 }
275 } // all readout cells
276 for (int iddl = 0; iddl < 14; iddl++) {
277 auto trbegin = mTmpTRU[iddl].begin() + (*rangeIter)[28 + 2 * iddl];
278 auto trend = mTmpTRU[iddl].begin() + (*rangeIter)[28 + 2 * iddl + 1];
279 // Move trigger cells
280 for (auto tri = trbegin; tri != trend; tri++) {
281 if (tri->getEnergy() > 0 || mKeepTrigNoise) {
282 mOutputCells.emplace_back(tri->getTRUId(), tri->getEnergy(), tri->getTime(), tri->getType());
283 }
284 }
285 }
286
287 mOutputTriggerRecords.emplace_back(*irIter, prevCellSize, mOutputCells.size() - prevCellSize);
288 }
289
290 mLastSize = 1.1 * mOutputCells.size();
291
292 LOG(debug) << "[PHOSRawToCellConverter - run] Writing " << mOutputCells.size() << " cells ...";
293 ctx.outputs().snapshot(o2::framework::Output{"PHS", "CELLS", mflpId}, mOutputCells);
294 ctx.outputs().snapshot(o2::framework::Output{"PHS", "CELLTRIGREC", mflpId}, mOutputTriggerRecords);
295 ctx.outputs().snapshot(o2::framework::Output{"PHS", "RAWHWERRORS", 0}, mOutputHWErrors);
296 if (mFillChi2) {
297 ctx.outputs().snapshot(o2::framework::Output{"PHS", "CELLFITQA", 0}, mOutputFitChi);
298 }
299}
300
302{
303 std::vector<o2::framework::InputSpec> inputs;
304 inputs.emplace_back("RAWDATA", o2::framework::ConcreteDataTypeMatcher{"PHS", "RAWDATA"}, o2::framework::Lifetime::Timeframe);
305 // receive at least 1 guaranteed input (which will allow to acknowledge the TF)
306 inputs.emplace_back("STFDist", "FLP", "DISTSUBTIMEFRAME", 0, o2::framework::Lifetime::Timeframe);
307 inputs.emplace_back("recoparams", o2::header::gDataOriginPHS, "PHS_RecoParams", 0, o2::framework::Lifetime::Condition, o2::framework::ccdbParamSpec("PHS/Config/RecoParams"));
308
309 std::vector<o2::framework::OutputSpec> outputs;
310 outputs.emplace_back("PHS", "CELLS", flpId, o2::framework::Lifetime::Timeframe);
311 outputs.emplace_back("PHS", "CELLTRIGREC", flpId, o2::framework::Lifetime::Timeframe);
312 outputs.emplace_back("PHS", "RAWHWERRORS", 0, o2::framework::Lifetime::Timeframe);
313 outputs.emplace_back("PHS", "CELLFITQA", 0, o2::framework::Lifetime::QA);
314
315 return o2::framework::DataProcessorSpec{"PHOSRawToCellConverterSpec",
316 inputs, // o2::framework::select("A:PHS/RAWDATA"),
317 outputs,
318 o2::framework::adaptFromTask<o2::phos::reco_workflow::RawToCellConverterSpec>(flpId),
320 {"presamples", o2::framework::VariantType::Int, 2, {"presamples time offset"}},
321 {"fitmethod", o2::framework::VariantType::String, "default", {"Fit method (default or semigaus)"}},
322 {"mappingpath", o2::framework::VariantType::String, "", {"Path to mapping files"}},
323 {"fillchi2", o2::framework::VariantType::String, "off", {"Fill sample qualities on/off"}},
324 {"keepHGLG", o2::framework::VariantType::String, "off", {"keep HighGain and Low Gain signals on/off"}},
325 {"pedestal", o2::framework::VariantType::String, "off", {"Analyze as pedestal run on/off"}},
326 {"keeptrig", o2::framework::VariantType::String, "off", {"Keep all trig. tiles for noise scan on/off"}}}};
327}
A helper class to iteratate over all parts of all input routes.
std::ostringstream debug
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 short NDDL
Total number of DDLs.
Definition Mapping.h:45
static Mapping * Instance()
Definition Mapping.cxx:29
ErrorType_t
Codes for different error types.
@ HEADER_DECODING
Header cannot be decoded (format incorrect)
@ PAGE_NOTFOUND
Page was not found (page index outside range)
@ HEADER_INVALID
Header in memory not belonging to requested superpage.
Reader for raw data produced by the Readout application in in-memory format.
const o2::header::RDHAny & getRawHeader() const
access to the raw header of the current page
void next()
Read next payload from the stream.
bool hasNext() const
check if more pages are available in the raw file
void run(framework::ProcessingContext &ctx) final
Run conversion of raw data to cells.
void init(framework::InitContext &ctx) final
Initializing the RawToCellConverterSpec.
GLsizei const GLchar *const * path
Definition glcorearb.h:3591
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
constexpr o2::header::DataOrigin gDataOriginPHS
Definition DataHeader.h:574
constexpr o2::header::DataDescription gDataDescriptionRawData
Definition DataHeader.h:597
std::vector< ConfigParamSpec > ccdbParamSpec(std::string const &path, int runDependent, std::vector< CCDBMetadata > metadata={}, int qrate=0)
std::vector< ConfigParamSpec > Options
framework::DataProcessorSpec getRawToCellConverterSpec(unsigned int flpId)
Creating DataProcessorSpec for the PHOS Cell Converter Spec.
@ HIGH_GAIN
High gain channel.
Definition Cell.h:53
int64_t differenceInBC(const InteractionRecord &other) const
static o2::header::DataHeader::PayloadSizeType getPayloadSize(const DataRef &ref)
void compare(std::string_view s1, std::string_view s2)
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"