Project
Loading...
Searching...
No Matches
TRDTrapSimulatorSpec.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
13
14#include <chrono>
15#include <optional>
16#include <gsl/span>
17
18#ifdef WITH_OPENMP
19#include <omp.h>
20#endif
21
22#include "TFile.h"
23
28#include "Framework/Lifetime.h"
29#include "fairlogger/Logger.h"
31
38
39using namespace o2::framework;
40
41namespace o2
42{
43namespace trd
44{
45
46using namespace constants;
47
48void TRDDPLTrapSimulatorTask::initTrapConfig(long timeStamp)
49{
51 mTrapConfig = ccdbmgr.getForTimeStamp<o2::trd::TrapConfig>("TRD/TrapConfig/" + mTrapConfigName, timeStamp);
52
53 if (mEnableTrapConfigDump) {
54 mTrapConfig->DumpTrapConfig2File("run3trapconfig_dump");
55 }
56
57 if (mTrapConfig->getConfigName() == "" && mTrapConfig->getConfigVersion() == "") {
58 //some trap configs dont have config name and version set, in those cases, just show the file name used.
59 LOG(info) << "using TRAPconfig: " << mTrapConfigName;
60 } else {
61 LOG(info) << "using TRAPconfig :\"" << mTrapConfig->getConfigName().c_str() << "\".\"" << mTrapConfig->getConfigVersion().c_str() << "\"";
62 }
63}
64
65void TRDDPLTrapSimulatorTask::setOnlineGainTables()
66{
67 //check FGBY from trapconfig.
68 //check the input parameter of trd-onlinegaincorrection.
69 //warn if you have chosen a trapconfig with gaincorrections but chosen not to use them.
70 if (mEnableOnlineGainCorrection) {
71 if (mTrapConfig->getTrapReg(TrapConfig::kFGBY) == 0) {
72 LOG(warn) << "you have asked to do online gain calibrations but the selected trap config does not have FGBY enabled, so modifying trapconfig to conform to your command line request. OnlineGains will be 1, i.e. no effect.";
73 for (int iDet = 0; iDet < MAXCHAMBER; ++iDet) {
74 mTrapConfig->setTrapReg(TrapConfig::kFGBY, 1, iDet);
75 }
76 }
77 mCalib->setOnlineGainTables(mOnlineGainTableName);
78 //TODO add some error checking inhere.
79 // gain factors are per MCM
80 // allocate the registers accordingly
81 for (int ch = 0; ch < NADCMCM; ++ch) {
84 mTrapConfig->setTrapRegAlloc(regFGAN, TrapConfig::kAllocByMCM);
85 mTrapConfig->setTrapRegAlloc(regFGFN, TrapConfig::kAllocByMCM);
86 }
87
88 for (int iDet = 0; iDet < MAXCHAMBER; ++iDet) {
89 const int nRobs = Geometry::getStack(iDet) == 2 ? Geometry::ROBmaxC0() : Geometry::ROBmaxC1();
90 for (int rob = 0; rob < nRobs; ++rob) {
91 for (int mcm = 0; mcm < NMCMROB; ++mcm) {
92 // set ADC reference voltage
93 mTrapConfig->setTrapReg(TrapConfig::kADCDAC, mCalib->getOnlineGainAdcdac(iDet, rob, mcm), iDet, rob, mcm);
94 // set constants channel-wise
95 for (int ch = 0; ch < NADCMCM; ++ch) {
98 mTrapConfig->setTrapReg(regFGAN, mCalib->getOnlineGainFGAN(iDet, rob, mcm, ch), iDet, rob, mcm);
99 mTrapConfig->setTrapReg(regFGFN, mCalib->getOnlineGainFGFN(iDet, rob, mcm, ch), iDet, rob, mcm);
100 }
101 }
102 }
103 }
104 } else if (mTrapConfig->getTrapReg(TrapConfig::kFGBY) == 1) {
105 LOG(warn) << "you have asked to not use online gain calibrations but the selected trap config does have FGBY enabled. Gain calibrations will be 1, i.e. no effect";
106 }
107}
108
109void TRDDPLTrapSimulatorTask::processTRAPchips(int& nTracklets, std::vector<Tracklet64>& trackletsAccum, std::array<TrapSimulator, NMCMHCMAX>& trapSimulators, std::vector<short>& digitCounts, std::vector<int>& digitIndices)
110{
111 // TRAP processing for current half chamber
112 for (int iTrap = 0; iTrap < NMCMHCMAX; ++iTrap) {
113 if (!trapSimulators[iTrap].isDataSet()) {
114 continue;
115 }
116 trapSimulators[iTrap].setBaselines();
117 trapSimulators[iTrap].filter();
118 trapSimulators[iTrap].tracklet();
119 auto trackletsOut = trapSimulators[iTrap].getTrackletArray64();
120 nTracklets += trackletsOut.size();
121 trackletsAccum.insert(trackletsAccum.end(), trackletsOut.begin(), trackletsOut.end());
122 if (mUseMC) {
123 auto digitCountOut = trapSimulators[iTrap].getTrackletDigitCount();
124 digitCounts.insert(digitCounts.end(), digitCountOut.begin(), digitCountOut.end());
125 auto digitIndicesOut = trapSimulators[iTrap].getTrackletDigitIndices();
126 digitIndices.insert(digitIndices.end(), digitIndicesOut.begin(), digitIndicesOut.end());
127 }
128 trapSimulators[iTrap].reset();
129 }
130}
131
133{
134 mTrapConfigName = ic.options().get<std::string>("trd-trapconfig");
135 mEnableOnlineGainCorrection = ic.options().get<bool>("trd-onlinegaincorrection");
136 mOnlineGainTableName = ic.options().get<std::string>("trd-onlinegaintable");
137 mRunNumber = ic.options().get<int>("trd-runnum");
138 mEnableTrapConfigDump = ic.options().get<bool>("trd-dumptrapconfig");
139 mUseFloatingPointForQ = ic.options().get<bool>("float-arithmetic");
140 mChargeScalingFactor = ic.options().get<int>("scale-q");
141 if (mChargeScalingFactor != -1) {
142 if (mChargeScalingFactor < 1) {
143 LOG(error) << "Charge scaling factor < 1 defined. Setting it to 1";
144 // careful, scaling factor is an int! actuallty values smaller than 3 don't yield a valid factor since scaling factor = 2^32 / 3
145 mChargeScalingFactor = 1;
146 } else if (mChargeScalingFactor > 1024) {
147 LOG(error) << "Charge scaling factor > 1024 defined. Setting it to 1024";
148 // no technical reason, but with 10 bit ADC values 1024 is already way to high for scaling...
149 mChargeScalingFactor = 1024;
150 }
151 }
152 if (mUseFloatingPointForQ) {
153 LOG(info) << "Tracklet charges will be calculated using floating point arithmetic";
154 if (mChargeScalingFactor != -1) {
155 LOG(error) << "Charge scaling factor defined, but floating point arithmetic configured. Charge scaling has no effect";
156 }
157 } else {
158 LOG(info) << "Tracklet charges will be calculated using a fixed multiplier";
159 }
160
161#ifdef WITH_OPENMP
162 int askedThreads = TRDSimParams::Instance().digithreads;
163 int maxThreads = omp_get_max_threads();
164 if (askedThreads < 0) {
165 mNumThreads = maxThreads;
166 } else {
167 mNumThreads = std::min(maxThreads, askedThreads);
168 }
169 LOG(info) << "Trap simulation running with " << mNumThreads << " threads ";
170#endif
171}
172
174{
175 // this method steeres the processing of the TRAP simulation
176 LOG(info) << "TRD Trap Simulator Device running over incoming message";
177
178 if (!mInitCcdbObjectsDone) {
179 auto creationTime = pc.services().get<o2::framework::TimingInfo>().creation;
180 auto timeStamp = (mRunNumber < 0) ? creationTime : mRunNumber;
181 mCalib = std::make_unique<Calibrations>();
182 mCalib->getCCDBObjects(timeStamp);
183 initTrapConfig(timeStamp);
184 setOnlineGainTables();
185 mInitCcdbObjectsDone = true;
186 }
187
188 // input
189 auto digits = pc.inputs().get<gsl::span<o2::trd::Digit>>("digitinput"); // block of TRD digits
190 auto triggerRecords = pc.inputs().get<std::vector<o2::trd::TriggerRecord>>("triggerrecords"); // time and number of digits for each collision
191 LOG(debug) << "Received " << digits.size() << " digits from " << triggerRecords.size() << " collisions";
192
193 // load MC information if label processing is enabeld
195 using lblType = std::decay_t<decltype(pc.inputs().get<o2::dataformats::ConstMCTruthContainer<o2::MCCompLabel>>(""))>;
196 std::optional<lblType> lblDigits;
197 if (mUseMC) {
198 lblDigits.emplace(pc.inputs().get<o2::dataformats::ConstMCTruthContainer<o2::MCCompLabel>>("labelinput")); // MC labels associated to the input digits
199 lblDigitsPtr = &lblDigits.value();
200 LOG(debug) << "Labels contain " << lblDigitsPtr->getNElements() << " elements with and indexed size of " << lblDigitsPtr->getIndexedSize();
201 if (lblDigitsPtr->getIndexedSize() != digits.size()) {
202 LOG(warn) << "Digits and Labels coming into TrapSimulator are of differing sizes, labels will be jibberish. " << lblDigitsPtr->getIndexedSize() << "!=" << digits.size();
203 }
204 }
205
206 // output
207 std::vector<Digit> digitsOut; // in case downscaling is applied this vector keeps the digits which should not be removed
208 std::vector<Tracklet64> tracklets; // calculated tracklets
209 o2::dataformats::MCTruthContainer<o2::MCCompLabel> lblTracklets; // MC labels for the tracklets, taken from the digits which make up the tracklet (duplicates are removed)
210
211 auto timeProcessingStart = std::chrono::high_resolution_clock::now(); // measure total processing time
212
213 // sort digits by half chamber ID for each collision and keep track in index vector
214 auto sortStart = std::chrono::high_resolution_clock::now();
215 std::vector<unsigned int> digitIdxArray(digits.size()); // digit indices sorted by half chamber ID for each time frame
216 std::iota(digitIdxArray.begin(), digitIdxArray.end(), 0);
217 for (auto& trig : triggerRecords) {
218 std::stable_sort(std::begin(digitIdxArray) + trig.getFirstDigit(), std::begin(digitIdxArray) + trig.getNumberOfDigits() + trig.getFirstDigit(),
219 [&digits](unsigned int i, unsigned int j) { return digits[i].getHCId() < digits[j].getHCId(); });
220 }
221 auto sortTime = std::chrono::high_resolution_clock::now() - sortStart;
222
223 // prepare data structures for accumulating results per collision
224 std::vector<int> nTracklets(triggerRecords.size());
225 std::vector<std::vector<Tracklet64>> trackletsAccum;
226 trackletsAccum.resize(triggerRecords.size());
227 std::vector<std::vector<short>> digitCountsAccum; // holds the number of digits included in each tracklet (therefore has the same number of elements as trackletsAccum)
228 // digitIndicesAccum holds the global indices of the digits which comprise the tracklets
229 // with the help of digitCountsAccum one can loop through this vector and find the corresponding digit indices for each tracklet
230 std::vector<std::vector<int>> digitIndicesAccum;
231 digitCountsAccum.resize(triggerRecords.size());
232 digitIndicesAccum.resize(triggerRecords.size());
233
234 auto timeParallelStart = std::chrono::high_resolution_clock::now();
235
236#ifdef WITH_OPENMP
237#pragma omp parallel for schedule(dynamic) num_threads(mNumThreads)
238#endif
239 for (size_t iTrig = 0; iTrig < triggerRecords.size(); ++iTrig) {
240 int currHCId = -1;
241 std::array<TrapSimulator, NMCMHCMAX> trapSimulators{}; //the up to 64 trap simulators for a single half chamber
242 for (int iDigit = triggerRecords[iTrig].getFirstDigit(); iDigit < (triggerRecords[iTrig].getFirstDigit() + triggerRecords[iTrig].getNumberOfDigits()); ++iDigit) {
243 const auto& digit = &digits[digitIdxArray[iDigit]];
244 if (currHCId < 0) {
245 currHCId = digit->getHCId();
246 }
247 if (currHCId != digit->getHCId()) {
248 // we switch to a new half chamber, process all TRAPs of the previous half chamber which contain data
249 processTRAPchips(nTracklets[iTrig], trackletsAccum[iTrig], trapSimulators, digitCountsAccum[iTrig], digitIndicesAccum[iTrig]);
250 currHCId = digit->getHCId();
251 }
252 // fill the digit data into the corresponding TRAP chip
253 int trapIdx = (digit->getROB() / 2) * NMCMROB + digit->getMCM();
254 if (!trapSimulators[trapIdx].isDataSet()) {
255 trapSimulators[trapIdx].init(mTrapConfig, digit->getDetector(), digit->getROB(), digit->getMCM());
256 if (mUseFloatingPointForQ) {
257 trapSimulators[trapIdx].setUseFloatingPointForQ();
258 } else if (mChargeScalingFactor != -1) {
259 trapSimulators[trapIdx].setChargeScalingFactor(mChargeScalingFactor);
260 }
261 }
262 if (digit->getChannel() != 22) {
263 // 22 signals invalid digit read by raw reader
264 trapSimulators[trapIdx].setData(digit->getChannel(), digit->getADC(), digitIdxArray[iDigit]);
265 }
266 }
267 // take care of the TRAPs for the last half chamber
268 processTRAPchips(nTracklets[iTrig], trackletsAccum[iTrig], trapSimulators, digitCountsAccum[iTrig], digitIndicesAccum[iTrig]);
269 } // done with parallel processing
270 auto parallelTime = std::chrono::high_resolution_clock::now() - timeParallelStart;
271
272 // accumulate results and add MC labels
273 int nDigitsRejected = 0;
274 for (size_t iTrig = 0; iTrig < triggerRecords.size(); ++iTrig) {
275 if (mUseMC) {
276 int currDigitIndex = 0; // counter for all digits which are associated to tracklets
277 int trkltIdxStart = tracklets.size();
278 for (int iTrklt = 0; iTrklt < nTracklets[iTrig]; ++iTrklt) {
279 int tmp = currDigitIndex;
280 for (int iDigitIndex = tmp; iDigitIndex < tmp + digitCountsAccum[iTrig][iTrklt]; ++iDigitIndex) {
281 if (iDigitIndex == tmp) {
282 // for the first digit composing the tracklet we don't need to check for duplicate labels
283 lblTracklets.addElements(trkltIdxStart + iTrklt, lblDigitsPtr->getLabels(digitIndicesAccum[iTrig][iDigitIndex]));
284 } else {
285 // in case more than one digit composes the tracklet we add only the labels
286 // from the additional digit(s) which are not already contained in the previous
287 // digit(s)
288 auto currentLabels = lblTracklets.getLabels(trkltIdxStart + iTrklt);
289 auto newLabels = lblDigitsPtr->getLabels(digitIndicesAccum[iTrig][iDigitIndex]);
290 for (const auto& newLabel : newLabels) {
291 bool alreadyIn = false;
292 for (const auto& currLabel : currentLabels) {
293 if (currLabel == newLabel) {
294 alreadyIn = true;
295 break;
296 }
297 }
298 if (!alreadyIn) {
299 lblTracklets.addElement(trkltIdxStart + iTrklt, newLabel);
300 }
301 }
302 }
303 ++currDigitIndex;
304 }
305 }
306 }
307 tracklets.insert(tracklets.end(), trackletsAccum[iTrig].begin(), trackletsAccum[iTrig].end());
308 triggerRecords[iTrig].setTrackletRange(tracklets.size() - nTracklets[iTrig], nTracklets[iTrig]);
309 if ((iTrig % mDigitDownscaling) == 0) {
310 // we keep digits for this trigger
311 digitsOut.insert(digitsOut.end(), digits.begin() + triggerRecords[iTrig].getFirstDigit(), digits.begin() + triggerRecords[iTrig].getFirstDigit() + triggerRecords[iTrig].getNumberOfDigits());
312 triggerRecords[iTrig].setDigitRange(digitsOut.size() - triggerRecords[iTrig].getNumberOfDigits(), triggerRecords[iTrig].getNumberOfDigits());
313 } else {
314 // digits are not kept, we just need to update the trigger record
315 nDigitsRejected += triggerRecords[iTrig].getNumberOfDigits();
316 triggerRecords[iTrig].setDigitRange(digitsOut.size(), 0);
317 }
318 }
319
320 auto processingTime = std::chrono::high_resolution_clock::now() - timeProcessingStart;
321
322 LOG(info) << "Trap simulator found " << tracklets.size() << " tracklets from " << digits.size() << " Digits.";
323 LOG(info) << "In total " << nDigitsRejected << " digits were rejected due to digit downscaling factor of " << mDigitDownscaling;
324 if (mUseMC) {
325 LOG(info) << "In total " << lblTracklets.getNElements() << " MC labels are associated to the " << lblTracklets.getIndexedSize() << " tracklets";
326 }
327 LOG(info) << "Total processing time : " << std::chrono::duration_cast<std::chrono::milliseconds>(processingTime).count() << "ms";
328 LOG(info) << "Digit Sorting took: " << std::chrono::duration_cast<std::chrono::milliseconds>(sortTime).count() << "ms";
329 LOG(info) << "Processing time for parallel region: " << std::chrono::duration_cast<std::chrono::milliseconds>(parallelTime).count() << "ms";
330
331 pc.outputs().snapshot(Output{"TRD", "TRACKLETS", 0}, tracklets);
332 pc.outputs().snapshot(Output{"TRD", "TRKTRGRD", 0}, triggerRecords);
333 pc.outputs().snapshot(Output{"TRD", "DIGITS", 0}, digitsOut);
334 if (mUseMC) {
335 pc.outputs().snapshot(Output{"TRD", "TRKLABELS", 0}, lblTracklets);
336 }
337
338 LOG(debug) << "TRD Trap Simulator Device exiting";
339}
340
342{
343 std::vector<InputSpec> inputs;
344 std::vector<OutputSpec> outputs;
345
346 inputs.emplace_back("digitinput", "TRD", "DIGITS", 1);
347 inputs.emplace_back("triggerrecords", "TRD", "TRKTRGRD", 1);
348
349 outputs.emplace_back("TRD", "DIGITS", 0, Lifetime::Timeframe);
350 outputs.emplace_back("TRD", "TRACKLETS", 0, Lifetime::Timeframe);
351 outputs.emplace_back("TRD", "TRKTRGRD", 0, Lifetime::Timeframe);
352
353 if (useMC) {
354 inputs.emplace_back("labelinput", "TRD", "LABELS", 0);
355 outputs.emplace_back("TRD", "TRKLABELS", 0, Lifetime::Timeframe);
356 }
357
358 return DataProcessorSpec{"TRAP",
359 inputs,
360 outputs,
361 AlgorithmSpec{adaptFromTask<TRDDPLTrapSimulatorTask>(useMC, digitDownscaling)},
362 Options{
363 {"scale-q", VariantType::Int, -1, {"Divide tracklet charges by this number. For -1 the value from the TRAP config will be taken instead"}},
364 {"float-arithmetic", VariantType::Bool, false, {"Enable floating point calculation of the tracklet charges instead of using fixed multiplier"}},
365 {"trd-trapconfig", VariantType::String, "cf_pg-fpnp32_zs-s16-deh_tb30_trkl-b5n-fs1e24-ht200-qs0e24s24e23-pidlinear-pt100_ptrg.r5549", {"TRAP config name"}},
366 {"trd-onlinegaincorrection", VariantType::Bool, false, {"Apply online gain calibrations, mostly for back checking to run2 by setting FGBY to 0"}},
367 {"trd-onlinegaintable", VariantType::String, "Krypton_2015-02", {"Online gain table to be use, names found in CCDB, obviously trd-onlinegaincorrection must be set as well."}},
368 {"trd-dumptrapconfig", VariantType::Bool, false, {"Dump the selected trap configuration at loading time, to text file"}},
369 {"trd-runnum", VariantType::Int, -1, {"Run number to use to anchor simulation to (from Run 2 297595 was used)"}}}};
370};
371
372} //end namespace trd
373} //end namespace o2
uint16_t mcm
uint16_t rob
A const (ready only) version of MCTruthContainer.
int32_t i
uint32_t j
Definition RawData.h:0
std::ostringstream debug
static BasicCCDBManager & instance()
A read-only version of MCTruthContainer allowing for storage optimisation.
gsl::span< const TruthElement > getLabels(uint32_t dataindex) const
A container to hold and manage MC truth information/labels.
gsl::span< TruthElement > getLabels(uint32_t dataindex)
void addElements(uint32_t dataindex, gsl::span< CompatibleLabel > elements)
void addElement(uint32_t dataindex, TruthElement const &element, bool noElement=false)
void snapshot(const Output &spec, T const &object)
ConfigParamRegistry const & options()
Definition InitContext.h:33
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.
void run(o2::framework::ProcessingContext &pc) override
void init(o2::framework::InitContext &ic) override
bool setTrapReg(TrapReg_t reg, int value, int det)
int getTrapReg(TrapReg_t reg, int det=-1, int rob=-1, int mcm=-1)
std::string getConfigVersion()
Definition TrapConfig.h:501
bool setTrapRegAlloc(TrapReg_t reg, Alloc_t mode)
Definition TrapConfig.h:494
std::string getConfigName()
Definition TrapConfig.h:502
void DumpTrapConfig2File(std::string filename)
Defining PrimaryVertex explicitly as messageable.
Definition TFIDInfo.h:20
std::vector< ConfigParamSpec > Options
constexpr int NMCMROB
the number of MCMs per ROB
Definition Constants.h:46
constexpr int NADCMCM
the number of ADC channels per MCM
Definition Constants.h:52
constexpr int MAXCHAMBER
the maximum number of installed chambers
Definition Constants.h:30
constexpr int NMCMHCMAX
the maximum number of MCMs for one half chamber (C1 type)
Definition Constants.h:47
o2::framework::DataProcessorSpec getTRDTrapSimulatorSpec(bool useMC, int digitDownscaling)
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
std::vector< Digit > digits
std::vector< Tracklet64 > tracklets