Project
Loading...
Searching...
No Matches
ThresholdCalibratorSpec.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
17
18#ifdef WITH_OPENMP
19#include <omp.h>
20#endif
21
22namespace o2
23{
24namespace its
25{
26
28// Define error function for ROOT fitting
29double erf(double* xx, double* par)
30{
31 return (nInjScaled / 2) * TMath::Erf((xx[0] - par[0]) / (sqrt(2) * par[1])) + (nInjScaled / 2);
32}
33
34// ITHR erf is reversed
35double erf_ithr(double* xx, double* par)
36{
37 return (nInjScaled / 2) * (1 - TMath::Erf((xx[0] - par[0]) / (sqrt(2) * par[1])));
38}
39
41// Default constructor
43 : mChipModSel(inpConf.chipModSel), mChipModBase(inpConf.chipModBase)
44{
45 mSelfName = o2::utils::Str::concat_string(ChipMappingITS::getName(), "ITSThresholdCalibrator");
46}
47
49// Default deconstructor
51{
52 // Clear dynamic memory
53
54 delete[] this->mX;
55 this->mX = nullptr;
56
57 if (this->mFitType == FIT) {
58 delete this->mFitHist;
59 this->mFitHist = nullptr;
60 delete this->mFitFunction;
61 this->mFitFunction = nullptr;
62 }
63}
64
67{
68 LOGF(info, "ITSThresholdCalibrator init...", mSelfName);
69
70 mPercentageCut = ic.options().get<short int>("percentage-cut");
71
72 mColStep = ic.options().get<short int>("s-curve-col-step");
73 if (mColStep >= N_COL) {
74 LOG(warning) << "mColStep = " << mColStep << ": saving s-curves of only 1 pixel (pix 0) per row";
75 }
76
77 std::string fittype = ic.options().get<std::string>("fittype");
78 if (fittype == "derivative") {
79 this->mFitType = DERIVATIVE;
80
81 } else if (fittype == "fit") {
82 this->mFitType = FIT;
83
84 } else if (fittype == "hitcounting") {
85 this->mFitType = HITCOUNTING;
86
87 } else {
88 LOG(error) << "fittype " << fittype
89 << " not recognized, please use 'derivative', 'fit', or 'hitcounting'";
90 throw fittype;
91 }
92
93 // Get metafile directory from input
94 try {
95 this->mMetafileDir = ic.options().get<std::string>("meta-output-dir");
96 } catch (std::exception const& e) {
97 LOG(warning) << "Input parameter meta-output-dir not found"
98 << "\n*** Setting metafile output directory to /dev/null";
99 }
100 if (this->mMetafileDir != "/dev/null") {
101 this->mMetafileDir = o2::utils::Str::rectifyDirectory(this->mMetafileDir);
102 }
103
104 // Get ROOT output directory from input
105 try {
106 this->mOutputDir = ic.options().get<std::string>("output-dir");
107 } catch (std::exception const& e) {
108 LOG(warning) << "Input parameter output-dir not found"
109 << "\n*** Setting ROOT output directory to ./";
110 }
111 this->mOutputDir = o2::utils::Str::rectifyDirectory(this->mOutputDir);
112
113 // Get metadata data type from input
114 try {
115 this->mMetaType = ic.options().get<std::string>("meta-type");
116 } catch (std::exception const& e) {
117 LOG(warning) << "Input parameter meta-type not found"
118 << "\n*** Disabling 'type' in metadata output files";
119 }
120
121 this->mVerboseOutput = ic.options().get<bool>("verbose");
122
123 // Get number of threads
124 this->mNThreads = ic.options().get<int>("nthreads");
125
126 // Check fit type vs nthreads (fit option is not thread safe!)
127 if (mFitType == FIT && mNThreads > 1) {
128 throw std::runtime_error("Multiple threads are requested with fit method which is not thread safe");
129 }
130
131 // Machine hostname
132 this->mHostname = boost::asio::ip::host_name();
133
134 // check flag to tag single noisy pix in digital and analog scans
135 this->mTagSinglePix = ic.options().get<bool>("enable-single-pix-tag");
136
137 // get min and max ithr and vcasn (default if not specified)
138 inMinVcasn = ic.options().get<short int>("min-vcasn");
139 inMaxVcasn = ic.options().get<short int>("max-vcasn");
140 inMinIthr = ic.options().get<short int>("min-ithr");
141 inMaxIthr = ic.options().get<short int>("max-ithr");
142 if (inMinVcasn > inMaxVcasn || inMinIthr > inMaxIthr) {
143 throw std::runtime_error("Min VCASN/ITHR is larger than Max VCASN/ITHR: check the settings, analysis not possible");
144 }
145
146 // Get flag to enable most-probable value calculation
147 isMpv = ic.options().get<bool>("enable-mpv");
148
149 // Parameters to operate in manual mode (when run type is not recognized automatically)
150 isManualMode = ic.options().get<bool>("manual-mode");
151 if (isManualMode) {
152 try {
153 manualMin = ic.options().get<short int>("manual-min");
154 } catch (std::exception const& e) {
155 throw std::runtime_error("Min value of the scan parameter not found, mandatory in manual mode");
156 }
157
158 try {
159 manualMax = ic.options().get<short int>("manual-max");
160 } catch (std::exception const& e) {
161 throw std::runtime_error("Max value of the scan parameter not found, mandatory in manual mode");
162 }
163
164 try {
165 manualScanType = ic.options().get<std::string>("manual-scantype");
166 } catch (std::exception const& e) {
167 throw std::runtime_error("Scan type not found, mandatory in manual mode");
168 }
169
170 try {
171 saveTree = ic.options().get<bool>("save-tree");
172 } catch (std::exception const& e) {
173 throw std::runtime_error("Please specify if you want to save the ROOT trees, mandatory in manual mode");
174 }
175
176 // this is not mandatory since it's 1 by default
177 manualStep = ic.options().get<short int>("manual-step");
178
179 // this is not mandatory since it's 0 by default
180 manualMin2 = ic.options().get<short int>("manual-min2");
181
182 // this is not mandatory since it's 0 by default
183 manualMax2 = ic.options().get<short int>("manual-max2");
184
185 // this is not mandatory since it's 1 by default
186 manualStep2 = ic.options().get<short int>("manual-step2");
187
188 // this is not mandatory since it's 5 by default
189 manualStrobeWindow = ic.options().get<short int>("manual-strobewindow");
190
191 // Flag to scale the number of injections by 3 in case --meb-select is used
192 scaleNinj = ic.options().get<bool>("scale-ninj");
193 }
194
195 // Flag to enable the analysis of CRU_ITS data
196 isCRUITS = ic.options().get<bool>("enable-cru-its");
197
198 // Number of injections
199 nInj = ic.options().get<int>("ninj");
201
202 // flag to set the url ccdb mgr
203 this->mCcdbMgrUrl = ic.options().get<std::string>("ccdb-mgr-url");
204 // FIXME: Temporary solution to retrieve ConfDBmap
205 long int ts = o2::ccdb::getCurrentTimestamp();
206 LOG(info) << "Getting confDB map from ccdb - timestamp: " << ts;
208 mgr.setURL(mCcdbMgrUrl);
209 mgr.setTimestamp(ts);
210 mConfDBmap = mgr.get<std::vector<int>>("ITS/Calib/Confdbmap");
211
212 // Parameters to dump s-curves on disk
213 isDumpS = ic.options().get<bool>("dump-scurves");
214 maxDumpS = ic.options().get<int>("max-dump");
215 chipDumpS = ic.options().get<std::string>("chip-dump"); // comma-separated list of chips
216 chipDumpList = getIntegerVect(chipDumpS);
217 if (isDumpS && mFitType != FIT) {
218 LOG(error) << "S-curve dump enabled but `fittype` is not fit. Please check";
219 }
220 if (isDumpS) {
221 fileDumpS = TFile::Open(Form("s-curves_%d.root", mChipModSel), "RECREATE"); // in case of multiple processes, every process will have it's own file
222 if (maxDumpS < 0) {
223 LOG(info) << "`max-dump` " << maxDumpS << ". Dumping all s-curves";
224 } else {
225 LOG(info) << "`max-dump` " << maxDumpS << ". Dumping " << maxDumpS << " s-curves";
226 }
227 if (!chipDumpList.size()) {
228 LOG(info) << "Dumping s-curves for all chips";
229 } else {
230 LOG(info) << "Dumping s-curves for chips: " << chipDumpS;
231 }
232 }
233
234 // flag to enable the calculation of the slope in 2d pulse shape scans
235 doSlopeCalculation = ic.options().get<bool>("calculate-slope");
236 if (doSlopeCalculation) {
237 try {
238 chargeA = ic.options().get<int>("charge-a");
239 } catch (std::exception const& e) {
240 throw std::runtime_error("You want to do the slop calculation but you did not specify charge-a");
241 }
242
243 try {
244 chargeB = ic.options().get<int>("charge-b");
245 } catch (std::exception const& e) {
246 throw std::runtime_error("You want to do the slop calculation but you did not specify charge-b");
247 }
248 }
249
250 // Variable to select from which multi-event buffer select the hits
251 mMeb = ic.options().get<int>("meb-select");
252 if (mMeb > 2) {
253 LOG(error) << "MEB cannot be greater than 2. Please check your command line.";
254 }
255
256 return;
257}
258
260// Get number of active links for a given RU
261short int ITSThresholdCalibrator::getNumberOfActiveLinks(bool* links)
262{
263 int nL = 0;
264 for (int i = 0; i < 3; i++) {
265 if (links[i]) {
266 nL++;
267 }
268 }
269 return nL;
270}
271
273// Get link ID: 0,1,2 for IB RUs / 0,1 for OB RUs
274short int ITSThresholdCalibrator::getLinkID(short int chipID, short int ruID)
275{
276 if (chipID < 432) {
277 return (chipID - ruID * 9) / 3;
278 } else if (chipID >= 432 && chipID < 6480) {
279 return (chipID - 48 * 9 - (ruID - 48) * 112) / 56;
280 } else {
281 return (chipID - 48 * 9 - 54 * 112 - (ruID - 102) * 196) / 98;
282 }
283}
284
286// Get list of chipID (from 0 to 24119) attached to a RU based on the links which are active
287std::vector<short int> ITSThresholdCalibrator::getChipListFromRu(short int ruID, bool* links)
288{
289 std::vector<short int> cList;
290 int a, b;
291 if (ruID < 48) {
292 a = ruID * 9;
293 b = a + 9 - 1;
294 } else if (ruID >= 48 && ruID < 102) {
295 a = 48 * 9 + (ruID - 48) * 112;
296 b = a + 112 - 1;
297 } else {
298 a = 48 * 9 + 54 * 112 + (ruID - 102) * 196;
299 b = a + 196 - 1;
300 }
301
302 for (int c = a; c <= b; c++) {
303 short int lid = getLinkID(c, ruID);
304 if (links[lid]) {
305 cList.push_back(c);
306 }
307 }
308
309 return cList;
310}
311
313// Get RU ID (from 0 to 191) from a given O2ChipID (from 0 to 24119)
314short int ITSThresholdCalibrator::getRUID(short int chipID)
315{
316 // below there are the inverse of the formulas in getChipListFromRu(...)
317 if (chipID < 432) { // IB
318 return chipID / 9;
319 } else if (chipID >= 432 && chipID < 6480) { // ML
320 return (chipID - 48 * 9 + 112 * 48) / 112;
321 } else { // OL
322 return (chipID - 48 * 9 - 54 * 112 + 102 * 196) / 196;
323 }
324}
325
327// Convert comma-separated list of integers to a vector of int
328std::vector<short int> ITSThresholdCalibrator::getIntegerVect(std::string& s)
329{
330 std::stringstream ss(s);
331 std::vector<short int> result;
332 char ch;
333 short int tmp;
334 while (ss >> tmp) {
335 result.push_back(tmp);
336 ss >> ch;
337 }
338 return result;
339}
340
342// Open a new ROOT file and threshold TTree for that file
343void ITSThresholdCalibrator::initThresholdTree(bool recreate /*=true*/)
344{
345
346 // Create output directory to store output
347 std::string dir = this->mOutputDir + fmt::format("{}_{}/", mDataTakingContext.envId, mDataTakingContext.runNumber);
349 LOG(info) << "Created " << dir << " directory for ROOT trees output";
350
351 std::string filename = dir + mDataTakingContext.runNumber + '_' +
352 std::to_string(this->mFileNumber) + '_' + this->mHostname + "_modSel" + std::to_string(mChipModSel) + ".root.part";
353
354 // Check if file already exists
355 struct stat buffer;
356 if (recreate && stat(filename.c_str(), &buffer) == 0) {
357 LOG(warning) << "File " << filename << " already exists, recreating";
358 }
359
360 // Initialize ROOT output file
361 // to prevent premature external usage, use temporary name
362 const char* option = recreate ? "RECREATE" : "UPDATE";
363 mRootOutfile = new TFile(filename.c_str(), option);
364
365 // Tree containing the s-curves points
366 mScTree = new TTree("s-curve-points", "s-curve-points");
367 mScTree->Branch("chipid", &vChipid, "vChipID[1024]/S");
368 mScTree->Branch("row", &vRow, "vRow[1024]/S");
369
370 // Initialize output TTree branches
371 mThresholdTree = new TTree("ITS_calib_tree", "ITS_calib_tree");
372 mThresholdTree->Branch("chipid", &vChipid, "vChipID[1024]/S");
373 mThresholdTree->Branch("row", &vRow, "vRow[1024]/S");
374 if (mScanType == 'T') {
375 mThresholdTree->Branch("thr", &vThreshold, "vThreshold[1024]/S");
376 mThresholdTree->Branch("noise", &vNoise, "vNoise[1024]/F");
377 mThresholdTree->Branch("spoints", &vPoints, "vPoints[1024]/b");
378 mThresholdTree->Branch("success", &vSuccess, "vSuccess[1024]/O");
379
380 mScTree->Branch("chg", &vCharge, "vCharge[1024]/b");
381 mScTree->Branch("hits", &vHits, "vHits[1024]/b");
382 } else if (mScanType == 'D' || mScanType == 'A') { // this->mScanType == 'D' and this->mScanType == 'A'
383 mThresholdTree->Branch("n_hits", &vThreshold, "vThreshold[1024]/S");
384 } else if (mScanType == 'P') {
385 mThresholdTree->Branch("n_hits", &vThreshold, "vThreshold[1024]/S");
386 mThresholdTree->Branch("strobedel", &vMixData, "vMixData[1024]/S");
387 } else if (mScanType == 'p') {
388 mThresholdTree->Branch("n_hits", &vThreshold, "vThreshold[1024]/S");
389 mThresholdTree->Branch("strobedel", &vMixData, "vMixData[1024]/S");
390 mThresholdTree->Branch("charge", &vCharge, "vCharge[1024]/b");
391 if (doSlopeCalculation) {
392 mSlopeTree = new TTree("line_tree", "line_tree");
393 mSlopeTree->Branch("chipid", &vChipid, "vChipID[1024]/S");
394 mSlopeTree->Branch("row", &vRow, "vRow[1024]/S");
395 mSlopeTree->Branch("slope", &vSlope, "vSlope[1024]/F");
396 mSlopeTree->Branch("intercept", &vIntercept, "vIntercept[1024]/F");
397 }
398 } else if (mScanType == 'R') {
399 mThresholdTree->Branch("n_hits", &vThreshold, "vThreshold[1024]/S");
400 mThresholdTree->Branch("vresetd", &vMixData, "vMixData[1024]/S");
401 } else if (mScanType == 'r') {
402 mThresholdTree->Branch("thr", &vThreshold, "vThreshold[1024]/S");
403 mThresholdTree->Branch("noise", &vNoise, "vNoise[1024]/F");
404 mThresholdTree->Branch("success", &vSuccess, "vSuccess[1024]/O");
405 mThresholdTree->Branch("vresetd", &vMixData, "vMixData[1024]/S");
406 }
407
408 return;
409}
410
412// Returns upper / lower limits for threshold determination.
413// data is the number of trigger counts per charge injected;
414// x is the array of charge injected values;
415// NPoints is the length of both arrays.
416bool ITSThresholdCalibrator::findUpperLower(
417 std::vector<std::vector<unsigned short int>> data, const short int& NPoints,
418 short int& lower, short int& upper, bool flip, int iloop2)
419{
420 // Initialize (or re-initialize) upper and lower
421 upper = -1;
422 lower = -1;
423
424 if (flip) { // ITHR case. lower is at large mX[i], upper is at small mX[i]
425
426 for (int i = 0; i < NPoints; i++) {
427 int comp = mScanType != 'r' ? data[iloop2][i] : data[i][iloop2];
428 if (comp == 0) {
429 upper = i;
430 break;
431 }
432 }
433
434 if (upper == -1) {
435 return false;
436 }
437 for (int i = upper; i > 0; i--) {
438 int comp = mScanType != 'r' ? data[iloop2][i] : data[i][iloop2];
439 if (comp >= nInjScaled) {
440 lower = i;
441 break;
442 }
443 }
444
445 } else { // not flipped
446
447 for (int i = 0; i < NPoints; i++) {
448 int comp = mScanType != 'r' ? data[iloop2][i] : data[i][iloop2];
449 if (comp >= nInjScaled) {
450 upper = i;
451 break;
452 }
453 }
454
455 if (upper == -1) {
456 return false;
457 }
458 for (int i = upper; i > 0; i--) {
459 int comp = mScanType != 'r' ? data[iloop2][i] : data[i][iloop2];
460 if (comp == 0) {
461 lower = i;
462 break;
463 }
464 }
465 }
466
467 // If search was successful, return central x value
468 if ((lower == -1) || (upper < lower)) {
469 return false;
470 }
471 return true;
472}
473
475// Main findThreshold function which calls one of the three methods
476bool ITSThresholdCalibrator::findThreshold(
477 const short int& chipID, std::vector<std::vector<unsigned short int>> data, const float* x, short int& NPoints,
478 float& thresh, float& noise, int& spoints, int iloop2)
479{
480 bool success = false;
481
482 switch (this->mFitType) {
483 case DERIVATIVE: // Derivative method
484 success = this->findThresholdDerivative(data, x, NPoints, thresh, noise, spoints, iloop2);
485 break;
486
487 case FIT: // Fit method
488 success = this->findThresholdFit(chipID, data, x, NPoints, thresh, noise, spoints, iloop2);
489 break;
490
491 case HITCOUNTING: // Hit-counting method
492 success = this->findThresholdHitcounting(data, x, NPoints, thresh, iloop2);
493 // noise = 0;
494 break;
495 }
496
497 return success;
498}
499
501// Use ROOT to find the threshold and noise via S-curve fit
502// data is the number of trigger counts per charge injected;
503// x is the array of charge injected values;
504// NPoints is the length of both arrays.
505// thresh, noise, chi2 pointers are updated with results from the fit
506// spoints: number of points in the S of the S-curve (with n_hits between 0 and 50, excluding first and last point)
507// iloop2 is 0 for thr scan but is equal to vresetd index in 2D vresetd scan
508bool ITSThresholdCalibrator::findThresholdFit(
509 const short int& chipID, std::vector<std::vector<unsigned short int>> data, const float* x, const short int& NPoints,
510 float& thresh, float& noise, int& spoints, int iloop2)
511{
512 // Find lower & upper values of the S-curve region
513 short int lower, upper;
514 bool flip = (this->mScanType == 'I');
515 auto fndVal = std::find(chipDumpList.begin(), chipDumpList.end(), chipID);
516
517 if (!this->findUpperLower(data, NPoints, lower, upper, flip, iloop2) || lower == upper) {
518 if (this->mVerboseOutput) {
519 LOG(warning) << "Start-finding unsuccessful: (lower, upper) = ("
520 << lower << ", " << upper << ")";
521 }
522
523 if (isDumpS && (dumpCounterS[chipID] < maxDumpS || maxDumpS < 0) && (fndVal != chipDumpList.end() || !chipDumpList.size())) { // save bad s-curves
524 for (int i = 0; i < NPoints; i++) {
525 this->mFitHist->SetBinContent(i + 1, mScanType != 'r' ? data[iloop2][i] : data[i][iloop2]);
526 }
527 fileDumpS->cd();
528 mFitHist->Write();
529 }
530 if (isDumpS) {
531 dumpCounterS[chipID]++;
532 }
533
534 return false;
535 }
536 float start = (this->mX[upper] + this->mX[lower]) / 2;
537
538 if (start < 0) {
539 if (this->mVerboseOutput) {
540 LOG(warning) << "Start-finding unsuccessful: Start = " << start;
541 }
542 return false;
543 }
544
545 for (int i = 0; i < NPoints; i++) {
546 this->mFitHist->SetBinContent(i + 1, mScanType != 'r' ? data[iloop2][i] : data[i][iloop2]);
547 }
548
549 // Initialize starting parameters
550 this->mFitFunction->SetParameter(0, start);
551 this->mFitFunction->SetParameter(1, 8);
552
553 this->mFitHist->Fit("mFitFunction", "RQL");
554 if (isDumpS && (dumpCounterS[chipID] < maxDumpS || maxDumpS < 0) && (fndVal != chipDumpList.end() || !chipDumpList.size())) { // save good s-curves
555 fileDumpS->cd();
556 mFitHist->Write();
557 }
558 if (isDumpS) {
559 dumpCounterS[chipID]++;
560 }
561
562 noise = this->mFitFunction->GetParameter(1);
563 thresh = this->mFitFunction->GetParameter(0);
564 float chi2 = this->mFitFunction->GetChisquare() / this->mFitFunction->GetNDF();
565 spoints = upper - lower - 1;
566
567 // Clean up histogram for next time it is used
568 this->mFitHist->Reset();
569
570 return (chi2 < 5);
571}
572
574// Use ROOT to find the threshold and noise via derivative method
575// data is the number of trigger counts per charge injected;
576// x is the array of charge injected values;
577// NPoints is the length of both arrays.
578// spoints: number of points in the S of the S-curve (with n_hits between 0 and 50, excluding first and last point)
579// iloop2 is 0 for thr scan but is equal to vresetd index in 2D vresetd scan
580bool ITSThresholdCalibrator::findThresholdDerivative(std::vector<std::vector<unsigned short int>> data, const float* x, const short int& NPoints,
581 float& thresh, float& noise, int& spoints, int iloop2)
582{
583 // Find lower & upper values of the S-curve region
584 short int lower, upper;
585 bool flip = (this->mScanType == 'I');
586 if (!this->findUpperLower(data, NPoints, lower, upper, flip, iloop2) || lower == upper) {
587 if (this->mVerboseOutput) {
588 LOG(warning) << "Start-finding unsuccessful: (lower, upper) = (" << lower << ", " << upper << ")";
589 }
590 return false;
591 }
592
593 int deriv_size = upper - lower;
594 float deriv[deriv_size];
595 float xfx = 0, fx = 0;
596
597 // Fill array with derivatives
598 for (int i = lower; i < upper; i++) {
599 deriv[i - lower] = std::abs(mScanType != 'r' ? (data[iloop2][i + 1] - data[iloop2][i]) : (data[i + 1][iloop2] - data[i][iloop2])) / (this->mX[i + 1] - mX[i]);
600 xfx += this->mX[i] * deriv[i - lower];
601 fx += deriv[i - lower];
602 }
603
604 if (fx > 0.) {
605 thresh = xfx / fx;
606 }
607 float stddev = 0;
608 for (int i = lower; i < upper; i++) {
609 stddev += std::pow(this->mX[i] - thresh, 2) * deriv[i - lower];
610 }
611
612 stddev /= fx;
613 noise = std::sqrt(stddev);
614 spoints = upper - lower - 1;
615
616 return fx > 0.;
617}
618
620// Use ROOT to find the threshold and noise via derivative method
621// data is the number of trigger counts per charge injected;
622// x is the array of charge injected values;
623// NPoints is the length of both arrays.
624// iloop2 is 0 for thr scan but is equal to vresetd index in 2D vresetd scan
625bool ITSThresholdCalibrator::findThresholdHitcounting(
626 std::vector<std::vector<unsigned short int>> data, const float* x, const short int& NPoints, float& thresh, int iloop2)
627{
628 unsigned short int numberOfHits = 0;
629 bool is50 = false;
630 for (unsigned short int i = 0; i < NPoints; i++) {
631 numberOfHits += (mScanType != 'r') ? data[iloop2][i] : data[i][iloop2];
632 int comp = (mScanType != 'r') ? data[iloop2][i] : data[i][iloop2];
633 if (!is50 && comp == nInjScaled) {
634 is50 = true;
635 }
636 }
637
638 // If not enough counts return a failure
639 if (!is50) {
640 if (this->mVerboseOutput) {
641 LOG(warning) << "Calculation unsuccessful: too few hits. Skipping this pixel";
642 }
643 return false;
644 }
645
646 if (this->mScanType == 'T') {
647 thresh = this->mX[N_RANGE - 1] - numberOfHits / float(nInjScaled);
648 } else if (this->mScanType == 'V') {
649 thresh = (this->mX[N_RANGE - 1] * nInjScaled - numberOfHits) / float(nInjScaled);
650 } else if (this->mScanType == 'I') {
651 thresh = (numberOfHits + nInjScaled * this->mX[0]) / float(nInjScaled);
652 } else {
653 LOG(error) << "Unexpected runtype encountered in findThresholdHitcounting()";
654 return false;
655 }
656
657 return true;
658}
659
661// Run threshold extraction on completed row and update memory
662void ITSThresholdCalibrator::extractThresholdRow(const short int& chipID, const short int& row)
663{
664 if (this->mScanType == 'D' || this->mScanType == 'A') {
665 // Loop over all columns (pixels) in the row
666 for (short int col_i = 0; col_i < this->N_COL; col_i++) {
667 vChipid[col_i] = chipID;
668 vRow[col_i] = row;
669 vThreshold[col_i] = this->mPixelHits[chipID][row][col_i][0][0];
670 if (vThreshold[col_i] > nInj) {
671 this->mNoisyPixID[chipID].push_back(col_i * 1000 + row);
672 } else if (vThreshold[col_i] > 0 && vThreshold[col_i] < nInj) {
673 this->mIneffPixID[chipID].push_back(col_i * 1000 + row);
674 } else if (vThreshold[col_i] == 0) {
675 this->mDeadPixID[chipID].push_back(col_i * 1000 + row);
676 }
677 }
678 } else if (this->mScanType == 'P' || this->mScanType == 'p' || mScanType == 'R') {
679 // Loop over all columns (pixels) in the row
680 for (short int var1_i = 0; var1_i < this->N_RANGE; var1_i++) {
681 for (short int chg_i = 0; chg_i < this->N_RANGE2; chg_i++) {
682 for (short int col_i = 0; col_i < this->N_COL; col_i++) {
683 vChipid[col_i] = chipID;
684 vRow[col_i] = row;
685 vThreshold[col_i] = this->mPixelHits[chipID][row][col_i][chg_i][var1_i];
686 vMixData[col_i] = (var1_i * this->mStep) + mMin;
687 if (mScanType != 'R') {
688 vMixData[col_i]++; // +1 because a delay of n correspond to a real delay of n+1 (from ALPIDE manual)
689 }
690 vCharge[col_i] = (unsigned char)(chg_i * this->mStep2 + mMin2);
691 }
692 this->saveThreshold();
693 }
694 }
695
696 if (doSlopeCalculation) {
697 int delA = -1, delB = -1;
698 for (short int col_i = 0; col_i < this->N_COL; col_i++) {
699 for (short int chg_i = 0; chg_i < 2; chg_i++) {
700 bool isFound = false;
701 int checkchg = !chg_i ? chargeA / mStep2 : chargeB / mStep2;
702 for (short int sdel_i = N_RANGE - 1; sdel_i >= 0; sdel_i--) {
703 if (mPixelHits[chipID][row][col_i][checkchg - 1][sdel_i] == nInj) {
704 if (!chg_i) {
705 delA = mMin + sdel_i * mStep + mStep / 2;
706 isFound = true;
707 } else {
708 delB = mMin + sdel_i * mStep + mStep / 2;
709 isFound = true;
710 }
711 break;
712 }
713 } // end loop on strobe delays
714
715 if (!isFound) { // if not found, take the first point with hits starting from the left (i.e. in principle the closest point to the one with MAX n_hits)
716 for (short int sdel_i = 0; sdel_i < N_RANGE; sdel_i++) {
717 if (mPixelHits[chipID][row][col_i][checkchg - 1][sdel_i] > 0) {
718 if (!chg_i) {
719 delA = mMin + sdel_i * mStep + mStep / 2;
720 } else {
721 delB = mMin + sdel_i * mStep + mStep / 2;
722 }
723 break;
724 }
725 } // end loop on strobe delays
726 } // end if on isFound
727 } // end loop on the two charges
728
729 if (delA > 0 && delB > 0 && delA != delB) {
730 vSlope[col_i] = ((float)(chargeA - chargeB) / (float)(delA - delB));
731 vIntercept[col_i] = (float)chargeA - (float)(vSlope[col_i] * delA);
732 if (vSlope[col_i] < 0) { // protection for non expected slope
733 vSlope[col_i] = 0.;
734 vIntercept[col_i] = 0.;
735 }
736 } else {
737 vSlope[col_i] = 0.;
738 vIntercept[col_i] = 0.;
739 }
740 } // end loop on pix
741
742 mSlopeTree->Fill();
743 }
744
745 } else { // threshold, vcasn, ithr
746
747 short int iRU = getRUID(chipID);
748#ifdef WITH_OPENMP
749 omp_set_num_threads(mNThreads);
750#pragma omp parallel for schedule(dynamic)
751#endif
752 // Loop over all columns (pixels) in the row
753 for (short int col_i = 0; col_i < this->N_COL; col_i++) {
754 // Do the threshold fit
755 float thresh = 0., noise = 0.;
756 bool success = false;
757 int spoints = 0;
758 int scan_i = mScanType == 'r' ? (mLoopVal[iRU][row] - mMin) / mStep : 0;
759 if (isDumpS) { // already protected for multi-thread in the init
760 mFitHist->SetName(Form("scurve_chip%d_row%d_col%d_scani%d", chipID, row, col_i, scan_i));
761 }
762
763 success = this->findThreshold(chipID, mPixelHits[chipID][row][col_i],
764 this->mX, mScanType == 'r' ? N_RANGE2 : N_RANGE, thresh, noise, spoints, scan_i);
765
766 vChipid[col_i] = chipID;
767 vRow[col_i] = row;
768 vThreshold[col_i] = (mScanType == 'T' || mScanType == 'r') ? (short int)(thresh * 10.) : (short int)(thresh);
769 vNoise[col_i] = (float)(noise * 10.); // always factor 10 also for ITHR/VCASN to not have all zeros
770 vSuccess[col_i] = success;
771 vPoints[col_i] = spoints > 0 ? (unsigned char)(spoints) : 0;
772
773 if (mScanType == 'r') {
774 vMixData[col_i] = mLoopVal[iRU][row];
775 }
776 }
777 if (mScanType == 'r') {
778 this->saveThreshold(); // save before moving to the next vresetd
779 }
780
781 // Fill the ScTree tree
782 if (mScanType == 'T') { // TODO: store also for other scans?
783 for (int ichg = mMin; ichg <= mMax; ichg++) {
784 for (short int col_i = 0; col_i < this->N_COL; col_i += mColStep) {
785 vCharge[col_i] = ichg;
786 vHits[col_i] = mPixelHits[chipID][row][col_i][0][ichg - mMin];
787 }
788 mScTree->Fill();
789 }
790 }
791 } // end of the else
792
793 // Saves threshold information to internal memory
794 if (mScanType != 'P' && mScanType != 'p' && mScanType != 'R' && mScanType != 'r') {
795 this->saveThreshold();
796 }
797}
798
800void ITSThresholdCalibrator::saveThreshold()
801{
802 // In the case of a full threshold scan, write to TTree
803 if (this->mScanType == 'T' || this->mScanType == 'D' || this->mScanType == 'A' || this->mScanType == 'P' || this->mScanType == 'p' || this->mScanType == 'R' || this->mScanType == 'r') {
804 this->mThresholdTree->Fill();
805 }
806
807 if (this->mScanType != 'D' && this->mScanType != 'A' && this->mScanType != 'P' && this->mScanType != 'p' && this->mScanType != 'R' && this->mScanType != 'r') {
808 // Save info in a map for later averaging
809 int sumT = 0, sumSqT = 0, sumN = 0, sumSqN = 0;
810 int countSuccess = 0, countUnsuccess = 0;
811 for (int i = 0; i < this->N_COL; i++) {
812 if (vSuccess[i]) {
813 sumT += vThreshold[i];
814 sumN += (int)vNoise[i];
815 sumSqT += (vThreshold[i]) * (vThreshold[i]);
816 sumSqN += ((int)vNoise[i]) * ((int)vNoise[i]);
817 countSuccess++;
818 if (vThreshold[i] >= mMin && vThreshold[i] <= mMax && (mScanType == 'I' || mScanType == 'V')) {
819 mpvCounter[vChipid[0]][vThreshold[i] - mMin]++;
820 }
821 } else {
822 countUnsuccess++;
823 }
824 }
825 short int chipID = vChipid[0];
826 std::array<long int, 6> dataSum{{sumT, sumSqT, sumN, sumSqN, countSuccess, countUnsuccess}};
827 if (!(this->mThresholds.count(chipID))) {
828 this->mThresholds[chipID] = dataSum;
829 } else {
830 std::array<long int, 6> dataAll{{this->mThresholds[chipID][0] + dataSum[0], this->mThresholds[chipID][1] + dataSum[1], this->mThresholds[chipID][2] + dataSum[2], this->mThresholds[chipID][3] + dataSum[3], this->mThresholds[chipID][4] + dataSum[4], this->mThresholds[chipID][5] + dataSum[5]}};
831 this->mThresholds[chipID] = dataAll;
832 }
833 }
834
835 return;
836}
837
839// Perform final operations on output objects. In the case of a full threshold
840// scan, rename ROOT file and create metadata file for writing to EOS
841void ITSThresholdCalibrator::finalizeOutput()
842{
843 // Check that objects actually exist in memory
844 if (!(mScTree) || !(this->mRootOutfile) || !(this->mThresholdTree) || (doSlopeCalculation && !(this->mSlopeTree))) {
845 return;
846 }
847
848 // Ensure that everything has been written to the ROOT file
849 this->mRootOutfile->cd();
850 this->mThresholdTree->Write(nullptr, TObject::kOverwrite);
851 this->mScTree->Write(nullptr, TObject::kOverwrite);
852
853 if (doSlopeCalculation) {
854 this->mSlopeTree->Write(nullptr, TObject::kOverwrite);
855 }
856
857 // Clean up the mThresholdTree, mScTree and ROOT output file
858 delete this->mThresholdTree;
859 this->mThresholdTree = nullptr;
860 delete mScTree;
861 mScTree = nullptr;
862 if (doSlopeCalculation) {
863 delete this->mSlopeTree;
864 this->mSlopeTree = nullptr;
865 }
866
867 this->mRootOutfile->Close();
868 delete this->mRootOutfile;
869 this->mRootOutfile = nullptr;
870
871 // Check that expected output directory exists
872 std::string dir = this->mOutputDir + fmt::format("{}_{}/", mDataTakingContext.envId, mDataTakingContext.runNumber);
873 if (!std::filesystem::exists(dir)) {
874 LOG(error) << "Cannot find expected output directory " << dir;
875 return;
876 }
877
878 // Expected ROOT output filename
879 std::string filename = mDataTakingContext.runNumber + '_' +
880 std::to_string(this->mFileNumber) + '_' + this->mHostname + "_modSel" + std::to_string(mChipModSel);
881 std::string filenameFull = dir + filename;
882 try {
883 std::rename((filenameFull + ".root.part").c_str(),
884 (filenameFull + ".root").c_str());
885 } catch (std::exception const& e) {
886 LOG(error) << "Failed to rename ROOT file " << filenameFull
887 << ".root.part, reason: " << e.what();
888 }
889
890 // Create metadata file
892 mdFile->fillFileData(filenameFull + ".root");
893 mdFile->setDataTakingContext(mDataTakingContext);
894 if (!(this->mMetaType.empty())) {
895 mdFile->type = this->mMetaType;
896 }
897 mdFile->priority = "high";
898 mdFile->lurl = filenameFull + ".root";
899 auto metaFileNameTmp = fmt::format("{}{}.tmp", this->mMetafileDir, filename);
900 auto metaFileName = fmt::format("{}{}.done", this->mMetafileDir, filename);
901 try {
902 std::ofstream metaFileOut(metaFileNameTmp);
903 metaFileOut << mdFile->asString() << '\n';
904 metaFileOut.close();
905 std::filesystem::rename(metaFileNameTmp, metaFileName);
906 } catch (std::exception const& e) {
907 LOG(error) << "Failed to create threshold metadata file "
908 << metaFileName << ", reason: " << e.what();
909 }
910 delete mdFile;
911
912 // Next time a file is created, use a larger number
913 this->mFileNumber++;
914
915 return;
916
917} // finalizeOutput
918
920// Set the run_type for this run
921// Initialize the memory needed for this specific type of run
922void ITSThresholdCalibrator::setRunType(const short int& runtype)
923{
924
925 // Save run type info for future evaluation
926 this->mRunType = runtype;
927
928 if (runtype == THR_SCAN) {
929 // full_threshold-scan -- just extract thresholds for each pixel and write to TTree
930 // 512 rows per chip
931 this->mScanType = 'T';
932 this->initThresholdTree();
933 this->mMin = 0;
934 this->mMax = 50;
935 this->N_RANGE = 51;
936 this->mCheckExactRow = true;
937
938 } else if (runtype == THR_SCAN_SHORT || runtype == THR_SCAN_SHORT_100HZ ||
939 runtype == THR_SCAN_SHORT_200HZ || runtype == THR_SCAN_SHORT_33 || runtype == THR_SCAN_SHORT_2_10HZ || runtype == THR_SCAN_SHORT_150INJ) {
940 // threshold_scan_short -- just extract thresholds for each pixel and write to TTree
941 // 10 rows per chip
942 this->mScanType = 'T';
943 this->initThresholdTree();
944 this->mMin = 0;
945 this->mMax = 50;
946 this->N_RANGE = 51;
947 this->mCheckExactRow = true;
948 if (runtype == THR_SCAN_SHORT_150INJ) {
949 nInj = 150;
950 if (mMeb >= 0) {
951 nInjScaled = nInj / 3;
952 }
953 }
954 } else if (runtype == VCASN150 || runtype == VCASN100 || runtype == VCASN100_100HZ || runtype == VCASN130 || runtype == VCASNBB) {
955 // VCASN tuning for different target thresholds
956 // Store average VCASN for each chip into CCDB
957 // ATTENTION: with back bias (VCASNBB) put max vcasn to 130 (default is 80)
958 // 4 rows per chip
959 this->mScanType = 'V';
960 this->mMin = inMinVcasn; // 30 is the default
961 this->mMax = inMaxVcasn; // 80 is the default
962 this->N_RANGE = mMax - mMin + 1;
963 this->mCheckExactRow = true;
964
965 } else if (runtype == ITHR150 || runtype == ITHR100 || runtype == ITHR100_100HZ || runtype == ITHR130) {
966 // ITHR tuning -- average ITHR per chip
967 // S-curve is backwards from VCASN case, otherwise same
968 // 4 rows per chip
969 this->mScanType = 'I';
970 this->mMin = inMinIthr; // 25 is the default
971 this->mMax = inMaxIthr; // 100 is the default
972 this->N_RANGE = mMax - mMin + 1;
973 this->mCheckExactRow = true;
974
975 } else if (runtype == DIGITAL_SCAN || runtype == DIGITAL_SCAN_100HZ || runtype == DIGITAL_SCAN_NOMASK) {
976 // Digital scan -- only storing one value per chip, no fit needed
977 this->mScanType = 'D';
978 this->initThresholdTree();
979 this->mFitType = NO_FIT;
980 this->mMin = 0;
981 this->mMax = 0;
982 this->N_RANGE = mMax - mMin + 1;
983 this->mCheckExactRow = false;
984
985 } else if (runtype == ANALOGUE_SCAN) {
986 // Analogue scan -- only storing one value per chip, no fit needed
987 this->mScanType = 'A';
988 this->initThresholdTree();
989 this->mFitType = NO_FIT;
990 this->mMin = 0;
991 this->mMax = 0;
992 this->N_RANGE = mMax - mMin + 1;
993 this->mCheckExactRow = false;
994
995 } else if (runtype == PULSELENGTH_SCAN) {
996 // Pulse length scan
997 this->mScanType = 'P';
998 this->initThresholdTree();
999 this->mFitType = NO_FIT;
1000 this->mMin = 0;
1001 this->mMax = 400; // strobe delay goes from 0 to 400 (included) in steps of 4
1002 this->mStep = 4;
1003 this->mStrobeWindow = 5; // it's 4 but it corresponds to 4+1 (as from alpide manual)
1004 this->N_RANGE = (mMax - mMin) / mStep + 1;
1005 this->mCheckExactRow = true;
1006 } else if (runtype == TOT_CALIBRATION || runtype == TOT_CALIBRATION_1_ROW) {
1007 // Pulse length scan 2D (charge vs strobe delay)
1008 this->mScanType = 'p'; // small p, just to distinguish from capital P
1009 this->initThresholdTree();
1010 this->mFitType = NO_FIT;
1011 this->mMin = (runtype == TOT_CALIBRATION) ? 300 : 0;
1012 this->mMax = (runtype == TOT_CALIBRATION) ? 1100 : 2000; // strobe delay goes from 0 to 2000 or 1100 (included) in steps of 10
1013 this->mStep = 10;
1014 this->mStrobeWindow = 2; // it's 1 but it corresponds to 1+1 (as from alpide manual)
1015 this->N_RANGE = (mMax - mMin) / mStep + 1;
1016 this->mMin2 = 30; // charge min
1017 this->mMax2 = 60; // charge max
1018 this->mStep2 = 30; // step for the charge
1019 this->mCalculate2DParams = false; // do not calculate time over threshold, pulse length, etc..
1020 if (runtype == TOT_CALIBRATION_1_ROW) {
1021 this->mMin2 = 0; // charge min
1022 this->mMax2 = 170; // charge max
1023 this->mStep2 = 1; // step for the charge
1024 }
1025 this->N_RANGE2 = (mMax2 - mMin2) / mStep2 + 1;
1026 this->mCheckExactRow = true;
1027 } else if (runtype == VRESETD_150 || runtype == VRESETD_300 || runtype == VRESETD_2D) {
1028 this->mScanType = 'R'; // capital R is for 1D scan
1029 if (runtype == VRESETD_150 || runtype == VRESETD_300) {
1030 this->mFitType = NO_FIT;
1031 }
1032 this->mMin = 100;
1033 this->mMax = 240; // vresetd goes from 100 to 240 in steps of 5
1034 this->mStep = 5;
1035 this->N_RANGE = (mMax - mMin) / mStep + 1;
1036 if (runtype == VRESETD_2D) {
1037 this->mScanType = 'r'; // small r, just to distinguish from capital R
1038 this->mMin2 = 0; // charge min
1039 this->mMax2 = 50; // charge max
1040 this->mStep2 = 1; // step for the charge
1041 this->N_RANGE2 = (mMax2 - mMin2) / mStep2 + 1;
1042 }
1043 this->mCheckExactRow = true;
1044 this->initThresholdTree();
1045 } else {
1046 // No other run type recognized by this workflow
1047 LOG(error) << "Runtype " << runtype << " not recognized by calibration workflow (ignore if you are in manual mode)";
1048 if (isManualMode) {
1049 LOG(info) << "Entering manual mode: be sure to have set all parameters correctly";
1050 this->mScanType = manualScanType[0];
1051 this->mMin = manualMin;
1052 this->mMax = manualMax;
1053 this->mMin2 = manualMin2;
1054 this->mMax2 = manualMax2;
1055 this->mStep = manualStep; // 1 by default
1056 this->mStep2 = manualStep2; // 1 by default
1057 this->mStrobeWindow = manualStrobeWindow; // 5 = 125 ns by default
1058 this->N_RANGE = (mMax - mMin) / mStep + 1;
1059 this->N_RANGE2 = (mMax2 - mMin2) / mStep2 + 1;
1060 if (saveTree) {
1061 this->initThresholdTree();
1062 }
1063 this->mFitType = (mScanType == 'D' || mScanType == 'A' || mScanType == 'P' || mScanType == 'p') ? NO_FIT : mFitType;
1064 this->mCheckExactRow = (mScanType == 'D' || mScanType == 'A') ? false : true;
1065 if (scaleNinj) {
1066 nInjScaled = nInj / 3;
1067 }
1068 } else {
1069 throw runtype;
1070 }
1071 }
1072
1073 this->mX = new float[mScanType == 'r' ? N_RANGE2 : N_RANGE];
1074 for (short int i = ((mScanType == 'r') ? mMin2 : mMin); i <= ((mScanType == 'r') ? mMax2 / mStep2 : mMax / mStep); i++) {
1075 this->mX[i - (mScanType == 'r' ? mMin2 : mMin)] = (float)i + 0.5;
1076 }
1077
1078 // Initialize objects for doing the threshold fits
1079 if (this->mFitType == FIT) {
1080 // Initialize the histogram used for error function fits
1081 // Will initialize the TF1 in setRunType (it is different for different runs)
1082 this->mFitHist = new TH1F(
1083 "mFitHist", "mFitHist", mScanType == 'r' ? N_RANGE2 : N_RANGE, mX[0] - 1., mX[(mScanType == 'r' ? N_RANGE2 : N_RANGE) - 1]);
1084
1085 // Initialize correct fit function for the scan type
1086 this->mFitFunction = (this->mScanType == 'I')
1087 ? new TF1("mFitFunction", erf_ithr, mMin, mMax, 2)
1088 : new TF1("mFitFunction", erf, (mScanType == 'T' || mScanType == 'r') ? 3 : mMin, mScanType == 'r' ? mMax2 : mMax, 2);
1089 this->mFitFunction->SetParName(0, "Threshold");
1090 this->mFitFunction->SetParName(1, "Noise");
1091 }
1092
1093 return;
1094}
1095
1097// Calculate pulse parameters in 1D scan: time over threshold, rise time, ...
1098std::vector<float> ITSThresholdCalibrator::calculatePulseParams(const short int& chipID)
1099{
1100
1101 int rt_mindel = -1, rt_maxdel = -1, tot_mindel = -1, tot_maxdel = -1;
1102 int sumRt = 0, sumSqRt = 0, countRt = 0, sumTot = 0, sumSqTot = 0, countTot = 0;
1103
1104 for (auto itrow = mPixelHits[chipID].begin(); itrow != mPixelHits[chipID].end(); itrow++) { // loop over the chip rows
1105 short int row = itrow->first;
1106 for (short int col_i = 0; col_i < this->N_COL; col_i++) { // loop over the pixels on the row
1107 for (short int sdel_i = 0; sdel_i < this->N_RANGE; sdel_i++) { // loop over the strobe delays
1108 if (mPixelHits[chipID][row][col_i][0][sdel_i] > 0 && mPixelHits[chipID][row][col_i][0][sdel_i] < nInj && rt_mindel < 0) { // from left, the last bin with 0 hits or the first with some hits
1109 rt_mindel = sdel_i > 0 ? ((sdel_i - 1) * mStep) + 1 : (sdel_i * mStep) + 1; // + 1 because if delay = n, we get n+1 in reality (ALPIDE feature)
1110 }
1111 if (mPixelHits[chipID][row][col_i][0][sdel_i] == nInj) {
1112 rt_maxdel = (sdel_i * mStep) + 1;
1113 tot_mindel = (sdel_i * mStep) + 1;
1114 break;
1115 }
1116 }
1117
1118 for (short int sdel_i = N_RANGE - 1; sdel_i >= 0; sdel_i--) { // from right, the first bin with nInj hits
1119 if (mPixelHits[chipID][row][col_i][0][sdel_i] == nInj) {
1120 tot_maxdel = (sdel_i * mStep) + 1;
1121 break;
1122 }
1123 }
1124
1125 if (tot_maxdel > tot_mindel && tot_mindel >= 0 && tot_maxdel >= 0) {
1126 sumTot += tot_maxdel - tot_mindel - (int)(mStrobeWindow / 2);
1127 sumSqTot += (tot_maxdel - tot_mindel - (int)(mStrobeWindow / 2)) * (tot_maxdel - tot_mindel - (int)(mStrobeWindow / 2));
1128 countTot++;
1129 }
1130
1131 if (rt_maxdel > rt_mindel && rt_maxdel > 0) {
1132 if (rt_mindel < 0) {
1133 sumRt += mStep + (int)(mStrobeWindow / 2); // resolution -> in case the rise is "instantaneous"
1134 sumSqRt += (mStep + (int)(mStrobeWindow / 2)) * (mStep + (int)(mStrobeWindow / 2));
1135 } else {
1136 sumRt += rt_maxdel - rt_mindel + (int)(mStrobeWindow / 2);
1137 sumSqRt += (rt_maxdel - rt_mindel + (int)(mStrobeWindow / 2)) * (rt_maxdel - rt_mindel + (int)(mStrobeWindow / 2));
1138 }
1139 countRt++;
1140 }
1141
1142 rt_mindel = -1;
1143 rt_maxdel = -1;
1144 tot_maxdel = -1;
1145 tot_mindel = -1;
1146 } // end loop over col_i
1147 } // end loop over chip rows
1148
1149 std::vector<float> output; // {avgRt, rmsRt, avgTot, rmsTot}
1150 // Avg Rt
1151 output.push_back(!countRt ? 0. : (float)sumRt / (float)countRt);
1152 // Rms Rt
1153 output.push_back(!countRt ? 0. : (std::sqrt((float)sumSqRt / (float)countRt - output[0] * output[0])) * 25.);
1154 output[0] *= 25.;
1155 // Avg ToT
1156 output.push_back(!countTot ? 0. : (float)sumTot / (float)countTot);
1157 // Rms ToT
1158 output.push_back(!countTot ? 0. : (std::sqrt((float)sumSqTot / (float)countTot - output[2] * output[2])) * 25.);
1159 output[2] *= 25.;
1160
1161 return output;
1162}
1163
1165// Calculate pulse parameters in 2D scan
1166std::vector<float> ITSThresholdCalibrator::calculatePulseParams2D(const short int& chipID)
1167{
1168 long int sumTot = 0, sumSqTot = 0, countTot = 0;
1169 long int sumMinThr = 0, sumSqMinThr = 0, countMinThr = 0;
1170 long int sumMinThrDel = 0, sumSqMinThrDel = 0;
1171 long int sumMaxPl = 0, sumSqMaxPl = 0, countMaxPl = 0;
1172 long int sumMaxPlChg = 0, sumSqMaxPlChg = 0;
1173
1174 for (auto itrow = mPixelHits[chipID].begin(); itrow != mPixelHits[chipID].end(); itrow++) { // loop over the chip rows
1175 short int row = itrow->first;
1176 for (short int col_i = 0; col_i < this->N_COL; col_i++) { // loop over the pixels on the row
1177 int minThr = 1e7, minThrDel = 1e7, maxPl = -1, maxPlChg = -1;
1178 int tot_mindel = 1e7;
1179 bool isFound = false;
1180 for (short int chg_i = 0; chg_i < this->N_RANGE2; chg_i++) { // loop over charges
1181 for (short int sdel_i = 0; sdel_i < this->N_RANGE; sdel_i++) { // loop over the strobe delays
1182 if (mPixelHits[chipID][row][col_i][chg_i][sdel_i] == nInj) { // minimum threshold charge and delay
1183 minThr = chg_i * mStep2;
1184 minThrDel = (sdel_i * mStep) + 1; // +1 because n->n+1 (as from alpide manual)
1185 isFound = true;
1186 break;
1187 }
1188 }
1189 if (isFound) {
1190 break;
1191 }
1192 }
1193 isFound = false;
1194 for (short int sdel_i = this->N_RANGE - 1; sdel_i >= 0; sdel_i--) { // loop over the strobe delays
1195 for (short int chg_i = this->N_RANGE2 - 1; chg_i >= 0; chg_i--) { // loop over charges
1196 if (mPixelHits[chipID][row][col_i][chg_i][sdel_i] == nInj) { // max pulse length charge and delay
1197 maxPl = (sdel_i * mStep) + 1;
1198 maxPlChg = chg_i * mStep2;
1199 isFound = true;
1200 break;
1201 }
1202 }
1203 if (isFound) {
1204 break;
1205 }
1206 }
1207 isFound = false;
1208 for (short int sdel_i = 0; sdel_i < this->N_RANGE; sdel_i++) { // loop over the strobe delays
1209 for (short int chg_i = 0; chg_i < this->N_RANGE2; chg_i++) { // loop over charges
1210 if (mPixelHits[chipID][row][col_i][chg_i][sdel_i] == nInj) { // min delay for the ToT calculation
1211 tot_mindel = (sdel_i * mStep) + 1;
1212 isFound = true;
1213 break;
1214 }
1215 }
1216 if (isFound) {
1217 break;
1218 }
1219 }
1220
1221 if (maxPl > tot_mindel && tot_mindel < 1e7 && maxPl >= 0) { // ToT
1222 sumTot += maxPl - tot_mindel - (int)(mStrobeWindow / 2);
1223 sumSqTot += (maxPl - tot_mindel - (int)(mStrobeWindow / 2)) * (maxPl - tot_mindel - (int)(mStrobeWindow / 2));
1224 countTot++;
1225 }
1226
1227 if (minThr < 1e7) { // minimum threshold
1228 sumMinThr += minThr;
1229 sumSqMinThr += minThr * minThr;
1230 sumMinThrDel += minThrDel;
1231 sumSqMinThrDel += minThrDel * minThrDel;
1232 countMinThr++;
1233 }
1234
1235 if (maxPl >= 0) { // pulse length
1236 sumMaxPl += maxPl;
1237 sumSqMaxPl += maxPl * maxPl;
1238 sumMaxPlChg += maxPlChg;
1239 sumSqMaxPlChg += maxPlChg * maxPlChg;
1240 countMaxPl++;
1241 }
1242 } // end loop over col_i
1243 } // end loop over chip rows
1244
1245 // Pulse shape 2D output: avgToT, rmsToT, MTC, rmsMTC, avgMTCD, rmsMTCD, avgMPL, rmsMPL, avgMPLC, rmsMPLC
1246 std::vector<long int> values = {sumTot, sumSqTot, countTot, sumMinThr, sumSqMinThr, countMinThr, sumMinThrDel, sumSqMinThrDel, countMinThr, sumMaxPl, sumSqMaxPl, countMaxPl, sumMaxPlChg, sumSqMaxPlChg, countMaxPl};
1247 std::vector<float> output;
1248
1249 for (int i = 0; i < values.size(); i += 3) {
1250 // Avg
1251 output.push_back(!values[i + 2] ? 0. : (float)values[i] / (float)values[i + 2]);
1252 // Rms
1253 output.push_back(!values[i + 2] ? 0. : std::sqrt((float)values[i + 1] / (float)values[i + 2] - output[output.size() - 1] * output[output.size() - 1]));
1254 if (i == 0 || i == 6 || i == 9) {
1255 output[output.size() - 1] *= 25.;
1256 output[output.size() - 2] *= 25.;
1257 }
1258 }
1259
1260 return output;
1261}
1263// Extract thresholds and update memory
1264void ITSThresholdCalibrator::extractAndUpdate(const short int& chipID, const short int& row)
1265{
1266 // In threshold scan case, reset mThresholdTree before writing to a new file
1267 if ((this->mScanType == 'T' || this->mScanType == 'D' || this->mScanType == 'A' || this->mScanType == 'P' || this->mScanType == 'p' || mScanType == 'R' || mScanType == 'r') && ((this->mRowCounter)++ == N_ROWS_PER_FILE)) {
1268 // Finalize output and create a new TTree and ROOT file
1269 this->finalizeOutput();
1270 this->initThresholdTree();
1271 // Reset data counter for the next output file
1272 this->mRowCounter = 1;
1273 }
1274
1275 // Extract threshold values and save to memory
1276 this->extractThresholdRow(chipID, row);
1277
1278 return;
1279}
1280
1282// Main running function
1283// Get info from previous stf decoder workflow, then loop over readout frames
1284// (ROFs) to count hits and extract thresholds
1286{
1287 if (mRunStopRequested) { // give up when run stop request arrived
1288 return;
1289 }
1290
1291 updateTimeDependentParams(pc);
1292
1293 // Calibration vector
1294 const auto calibs = pc.inputs().get<gsl::span<o2::itsmft::GBTCalibData>>("calib");
1295 const auto digits = pc.inputs().get<gsl::span<o2::itsmft::Digit>>("digits");
1296 const auto ROFs = pc.inputs().get<gsl::span<o2::itsmft::ROFRecord>>("digitsROF");
1297
1298 // Store some lengths for convenient looping
1299 const unsigned int nROF = (unsigned int)ROFs.size();
1300
1301 // Loop over readout frames (usually only 1, sometimes 2)
1302 for (unsigned int iROF = 0; iROF < nROF; iROF++) {
1303
1304 unsigned int rofIndex = ROFs[iROF].getFirstEntry();
1305 unsigned int rofNEntries = ROFs[iROF].getNEntries();
1306
1307 // Find the correct charge, row, cw counter values for this ROF
1308 short int loopval = -1, realcharge = 0;
1309 short int row = -1;
1310 short int cwcnt = -1;
1311 bool isAllZero = true;
1312 short int ruIndex = -1;
1313 for (short int iRU = 0; iRU < this->N_RU; iRU++) {
1314 const auto& calib = calibs[iROF * this->N_RU + iRU];
1315 if (calib.calibUserField != 0) {
1316 mRuSet.insert(iRU);
1317 ruIndex = iRU;
1318 isAllZero = false;
1319
1320 if (loopval >= 0) {
1321 LOG(warning) << "More than one charge detected!";
1322 }
1323
1324 if (this->mRunType == -1) {
1325 mCdwVersion = isCRUITS ? 0 : ((short int)(calib.calibUserField >> 45)) & 0x7;
1326 LOG(info) << "CDW version: " << mCdwVersion;
1327 short int runtype = isCRUITS ? -2 : !mCdwVersion ? ((short int)(calib.calibUserField >> 24)) & 0xff
1328 : ((short int)(calib.calibUserField >> 9)) & 0x7f;
1329 mConfDBv = !mCdwVersion ? ((short int)(calib.calibUserField >> 32)) & 0xffff : ((short int)(calib.calibUserField >> 32)) & 0x1fff; // confDB version
1330 this->setRunType(runtype);
1331 LOG(info) << "Calibrator will ship these run parameters to aggregator:";
1332 LOG(info) << "Run type : " << mRunType;
1333 LOG(info) << "Scan type : " << mScanType;
1334 LOG(info) << "Fit type : " << std::to_string(mFitType);
1335 LOG(info) << "DB version (ignore in TOT_CALIB & VRESET2D): " << mConfDBv;
1336 }
1337 this->mRunTypeUp = isCRUITS ? -1 : !mCdwVersion ? ((short int)(calib.calibUserField >> 24)) & 0xff
1338 : ((short int)(calib.calibUserField >> 9)) & 0x7f;
1339
1340 // count the zeros
1341 if (!mRunTypeUp) {
1342 mRunTypeRU[iRU]++;
1343 mRunTypeRUCopy[iRU]++;
1344 }
1345 // Divide calibration word (24-bit) by 2^16 to get the first 8 bits
1346 if (this->mScanType == 'T') {
1347 // For threshold scan have to subtract from 170 to get charge value
1348 loopval = isCRUITS ? (short int)((calib.calibUserField >> 16) & 0xff) : !mCdwVersion ? (short int)(170 - (calib.calibUserField >> 16) & 0xff)
1349 : (short int)(170 - (calib.calibUserField >> 16) & 0xffff);
1350 } else if (this->mScanType == 'D' || this->mScanType == 'A') { // Digital scan
1351 loopval = 0;
1352 } else { // VCASN / ITHR tuning and Pulse length scan (it's the strobe delay in this case), and vresetd scan
1353 loopval = !mCdwVersion ? (short int)((calib.calibUserField >> 16) & 0xff) : (short int)((calib.calibUserField >> 16) & 0xffff);
1354 }
1355
1356 if (this->mScanType == 'p' || this->mScanType == 'r') {
1357 realcharge = 170 - ((short int)(calib.calibUserField >> 32)) & 0x1fff; // not existing with CDW v0
1358 }
1359
1360 // Last 16 bits should be the row (only uses up to 9 bits)
1361 row = !mCdwVersion ? (short int)(calib.calibUserField & 0xffff) : (short int)(calib.calibUserField & 0x1ff);
1362 // cw counter
1363 cwcnt = (short int)(calib.calibCounter);
1364 // count the last N injections
1365 short int checkVal = (mScanType == 'I') ? mMin : mMax;
1366 if ((mScanType != 'r' && loopval == checkVal) || (mScanType == 'r' && realcharge == mMax2)) {
1367 mCdwCntRU[iRU][row]++;
1368 mLoopVal[iRU][row] = loopval; // keep loop val (relevant for VRESET2D scan only)
1369 }
1370 if (this->mVerboseOutput) {
1371 LOG(info) << "RU: " << iRU << " CDWcounter: " << cwcnt << " row: " << row << " Loopval: " << loopval << " realcharge: " << realcharge << " confDBv: " << mCdwVersion;
1372 LOG(info) << "NDIGITS: " << digits.size();
1373 }
1374
1375 break;
1376 }
1377 }
1378
1379 if (isCRUITS && isAllZero) {
1380 if (mRunType == -1) {
1381 short int runtype = -2;
1382 mConfDBv = 0;
1383 this->setRunType(runtype);
1384 LOG(info) << "Running with CRU_ITS data - Calibrator will ship these run parameters to aggregator:";
1385 LOG(info) << "Run type (non-sense) : " << mRunType;
1386 LOG(info) << "Scan type : " << mScanType;
1387 LOG(info) << "Fit type : " << std::to_string(mFitType);
1388 LOG(info) << "DB version (non-sense): " << mConfDBv;
1389 }
1390 loopval = 0;
1391 realcharge = 0;
1392 row = 0;
1393 cwcnt = 0;
1394 }
1395
1396 if (loopval > this->mMax || loopval < this->mMin || ((mScanType == 'p' || mScanType == 'r') && (realcharge > this->mMax2 || realcharge < this->mMin2))) {
1397 if (this->mVerboseOutput) {
1398 LOG(warning) << "CW issues - loopval value " << loopval << " out of range for min " << this->mMin
1399 << " and max " << this->mMax << " (range: " << N_RANGE << ")";
1400 if (mScanType == 'p' || mScanType == 'r') {
1401 LOG(warning) << " and/or realcharge value " << realcharge << " out of range from min " << this->mMin2
1402 << " and max " << this->mMax2 << " (range: " << N_RANGE2 << ")";
1403 }
1404 }
1405 } else {
1406 std::vector<short int> mChips;
1407 std::map<short int, bool> mChipsForbRows;
1408 // loop to retrieve list of chips and start tagging bad dcols if the hits does not come from this row
1409 for (unsigned int idig = rofIndex; idig < rofIndex + rofNEntries; idig++) { // gets chipid
1410 auto& d = digits[idig];
1411 short int chipID = (short int)d.getChipIndex();
1412 if ((chipID % mChipModBase) != mChipModSel) {
1413 continue;
1414 }
1415 if (d.getRow() != row && mVerboseOutput) {
1416 LOG(info) << "iROF: " << iROF << " ChipID " << chipID << ": current row is " << d.getRow() << " (col = " << d.getColumn() << ") but the one in CW is " << row;
1417 }
1418 if (std::find(mChips.begin(), mChips.end(), chipID) == mChips.end()) {
1419 mChips.push_back(chipID);
1420 }
1421 }
1422 // loop to allocate memory only for allowed rows
1423 for (auto& chipID : mChips) {
1424 // mark active RU links
1425 short int ru = getRUID(chipID);
1426 mActiveLinks[ru][getLinkID(chipID, ru)] = true;
1427 // check rows and allocate memory
1428 if (mScanType != 'r' && mForbiddenRows.count(chipID)) {
1429 for (int iforb = mForbiddenRows[chipID].size() - 1; iforb >= 0; iforb--) {
1430 if (mForbiddenRows[chipID][iforb] == row) {
1431 mChipsForbRows[chipID] = true;
1432 break;
1433 }
1434 }
1435 }
1436 if (mChipsForbRows[chipID]) {
1437 continue;
1438 }
1439 if (!this->mPixelHits.count(chipID)) {
1440 if (mScanType == 'D' || mScanType == 'A') { // for digital and analog scan initialize the full matrix for each chipID
1441 for (int irow = 0; irow < 512; irow++) {
1442 this->mPixelHits[chipID][irow] = std::vector<std::vector<std::vector<unsigned short int>>>(this->N_COL, std::vector<std::vector<unsigned short int>>(N_RANGE2, std::vector<unsigned short int>(N_RANGE, 0)));
1443 }
1444 } else {
1445 this->mPixelHits[chipID][row] = std::vector<std::vector<std::vector<unsigned short int>>>(this->N_COL, std::vector<std::vector<unsigned short int>>(N_RANGE2, std::vector<unsigned short int>(N_RANGE, 0)));
1446 }
1447 } else if (!this->mPixelHits[chipID].count(row)) { // allocate memory for chip = chipID or for a row of this chipID
1448 this->mPixelHits[chipID][row] = std::vector<std::vector<std::vector<unsigned short int>>>(this->N_COL, std::vector<std::vector<unsigned short int>>(N_RANGE2, std::vector<unsigned short int>(N_RANGE, 0)));
1449 }
1450 }
1451
1452 // loop to count hits from digits
1453 short int loopPoint = (loopval - this->mMin) / mStep;
1454 short int chgPoint = (realcharge - this->mMin2) / mStep2;
1455 for (unsigned int idig = rofIndex; idig < rofIndex + rofNEntries; idig++) {
1456 auto& d = digits[idig];
1457 short int chipID = (short int)d.getChipIndex();
1458 short int col = (short int)d.getColumn();
1459
1460 if ((chipID % mChipModBase) != mChipModSel) {
1461 continue;
1462 }
1463
1464 if (!mChipsForbRows[chipID] && (!mCheckExactRow || d.getRow() == row) && (mMeb < 0 || cwcnt % 3 == mMeb)) { // row has NOT to be forbidden and we ignore hits coming from other rows (potential masking issue on chip)
1465 // Increment the number of counts for this pixel
1466 this->mPixelHits[chipID][d.getRow()][col][chgPoint][loopPoint]++;
1467 }
1468 }
1469 } // if (charge)
1470
1472 // Prepare the ChipDone object for QC + extract data if the row is completed
1473 if (ruIndex < 0) {
1474 continue;
1475 }
1476 short int nL = 0;
1477 for (int iL = 0; iL < 3; iL++) {
1478 if (mActiveLinks[ruIndex][iL]) {
1479 nL++; // count active links
1480 }
1481 }
1482 std::vector<short int> chipEnabled = getChipListFromRu(ruIndex, mActiveLinks[ruIndex]); // chip boundaries
1483 // Fill the chipDone info string
1484 if (mRunTypeRUCopy[ruIndex] == nInjScaled * nL) {
1485 for (short int iChip = 0; iChip < chipEnabled.size(); iChip++) {
1486 if ((chipEnabled[iChip] % mChipModBase) != mChipModSel) {
1487 continue;
1488 }
1489 addDatabaseEntry(chipEnabled[iChip], "", std::vector<float>(), true);
1490 }
1491 mRunTypeRUCopy[ruIndex] = 0; // reset here is safer (the other counter is reset in finalize)
1492 }
1493 // Check if scan of a row is finished: only for specific scans!
1494 bool passCondition = (mCdwCntRU[ruIndex][row] >= nInjScaled * nL);
1495 if (mScanType != 'D' && mScanType != 'A' && mScanType != 'P' && mScanType != 'p' && mScanType != 'R' && passCondition) {
1496 // extract data from the row
1497 for (short int iChip = 0; iChip < chipEnabled.size(); iChip++) {
1498 short int chipID = chipEnabled[iChip];
1499 if ((chipID % mChipModBase) != mChipModSel) {
1500 continue;
1501 }
1502 if (!isDumpS || (std::find(chipDumpList.begin(), chipDumpList.end(), chipID) != chipDumpList.end() || !chipDumpList.size())) { // to dump s-curves as histograms
1503 if (mPixelHits.count(chipID)) {
1504 if (mPixelHits[chipID].count(row)) { // make sure the row exists
1505 extractAndUpdate(chipID, row);
1506 if (mScanType != 'r' || (mScanType == 'r' && mLoopVal[ruIndex][row] == mMax)) {
1507 mPixelHits[chipID].erase(row);
1508 }
1509 mForbiddenRows[chipID].push_back(row);
1510 }
1511 }
1512 }
1513 }
1514 mCdwCntRU[ruIndex][row] = 0; // reset
1515 }
1516 } // for (ROFs)
1517
1518 if (!(this->mRunTypeUp)) {
1519 finalize();
1520 LOG(info) << "Shipping all outputs to aggregator (before endOfStream arrival!)";
1521 pc.outputs().snapshot(Output{"ITS", "TSTR", (unsigned int)mChipModSel}, this->mTuning);
1522 pc.outputs().snapshot(Output{"ITS", "PIXTYP", (unsigned int)mChipModSel}, this->mPixStat);
1523 pc.outputs().snapshot(Output{"ITS", "RUNT", (unsigned int)mChipModSel}, this->mRunType);
1524 pc.outputs().snapshot(Output{"ITS", "SCANT", (unsigned int)mChipModSel}, this->mScanType);
1525 pc.outputs().snapshot(Output{"ITS", "FITT", (unsigned int)mChipModSel}, this->mFitType);
1526 pc.outputs().snapshot(Output{"ITS", "CONFDBV", (unsigned int)mChipModSel}, this->mConfDBv);
1527 pc.outputs().snapshot(Output{"ITS", "QCSTR", (unsigned int)mChipModSel}, this->mChipDoneQc);
1528 // reset the DCSconfigObject_t before next ship out
1529 mTuning.clear();
1530 mPixStat.clear();
1531 mChipDoneQc.clear();
1532 } else if (pc.transitionState() == TransitionHandlingState::Requested) {
1533 LOG(info) << "Run stop requested during the scan, sending output to aggregator and then stopping to process new data";
1534 mRunStopRequested = true;
1535 finalize(); // calculating average thresholds based on what's collected up to this moment
1536 pc.outputs().snapshot(Output{"ITS", "TSTR", (unsigned int)mChipModSel}, this->mTuning); // dummy here
1537 pc.outputs().snapshot(Output{"ITS", "PIXTYP", (unsigned int)mChipModSel}, this->mPixStat);
1538 pc.outputs().snapshot(Output{"ITS", "RUNT", (unsigned int)mChipModSel}, this->mRunType);
1539 pc.outputs().snapshot(Output{"ITS", "SCANT", (unsigned int)mChipModSel}, this->mScanType);
1540 pc.outputs().snapshot(Output{"ITS", "FITT", (unsigned int)mChipModSel}, this->mFitType);
1541 pc.outputs().snapshot(Output{"ITS", "CONFDBV", (unsigned int)mChipModSel}, this->mConfDBv);
1542 pc.outputs().snapshot(Output{"ITS", "QCSTR", (unsigned int)mChipModSel}, this->mChipDoneQc);
1543 mChipDoneQc.clear();
1544 mPixStat.clear();
1545 mTuning.clear();
1546 }
1547
1548 return;
1549}
1550
1552// Retrieve conf DB map from production ccdb
1554{
1555 if (matcher == ConcreteDataMatcher("ITS", "CONFDBMAP", 0)) {
1556 LOG(info) << "Conf DB map retrieved from CCDB";
1557 mConfDBmap = (std::vector<int>*)obj;
1558 }
1559}
1560
1562// Calculate the average threshold given a vector of threshold objects
1563void ITSThresholdCalibrator::findAverage(const std::array<long int, 6>& data, float& avgT, float& rmsT, float& avgN, float& rmsN)
1564{
1565 avgT = (!data[4]) ? 0. : (float)data[0] / (float)data[4];
1566 rmsT = (!data[4]) ? 0. : std::sqrt((float)data[1] / (float)data[4] - avgT * avgT);
1567 avgN = (!data[4]) ? 0. : (float)data[2] / (float)data[4];
1568 rmsN = (!data[4]) ? 0. : std::sqrt((float)data[3] / (float)data[4] - avgN * avgN);
1569 return;
1570}
1571
1573void ITSThresholdCalibrator::addDatabaseEntry(
1574 const short int& chipID, const char* name, std::vector<float> data, bool isQC)
1575{
1576
1577 /*--------------------------------------------------------------------
1578 // Format of *data
1579 // - Threshold scan: avgT, rmsT, avgN, rmsN, status
1580 // - ITHR/VCASN scan: avg value, rms value, 0, 0, status (0 are just placeholder since they will not be used)
1581 // - Dig/Ana scans: empty vector
1582 // - Pulse shape 1D: avgRt, rmsRt, avgToT, rmsToT
1583 // - Pulse shape 2D: avgToT, rmsToT, MTC, rmsMTC, avgMTCD, rmsMTCD, avgMPL, rmsMPL, avgMPLC, rmsMPLC
1584 */
1585 // Obtain specific chip information from the chip ID (layer, stave, ...)
1586 int lay, sta, ssta, mod, chipInMod; // layer, stave, sub stave, module, chip
1587 this->mp.expandChipInfoHW(chipID, lay, sta, ssta, mod, chipInMod);
1588
1589 char stave[6];
1590 snprintf(stave, 6, "L%d_%02d", lay, sta);
1591
1592 if (isQC) {
1593 o2::dcs::addConfigItem(this->mChipDoneQc, "O2ChipID", std::to_string(chipID));
1594 return;
1595 }
1596
1597 // Get ConfDB id for the chip chipID
1598 int confDBid = (*mConfDBmap)[chipID];
1599
1600 // Bad pix list and bad dcols for dig and ana scan
1601 if (this->mScanType == 'D' || this->mScanType == 'A') {
1602 short int vPixDcolCounter[512] = {0}; // count #bad_pix per dcol
1603 std::string dcolIDs = "";
1604 std::string pixIDs_Noisy = "";
1605 std::string pixIDs_Dead = "";
1606 std::string pixIDs_Ineff = "";
1607 std::vector<int>& v = PixelType == "Noisy" ? mNoisyPixID[chipID] : PixelType == "Dead" ? mDeadPixID[chipID]
1608 : mIneffPixID[chipID];
1609 // Number of pixel types
1610 int n_pixel = v.size(), nDcols = 0;
1611 std::string ds = "-1"; // dummy string
1612 // find bad dcols and add them one by one
1613 if (PixelType == "Noisy") {
1614 for (int i = 0; i < v.size(); i++) {
1615 short int dcol = ((v[i] - v[i] % 1000) / 1000) / 2;
1616 vPixDcolCounter[dcol]++;
1617 }
1618 for (int i = 0; i < 512; i++) {
1619 if (vPixDcolCounter[i] > N_PIX_DCOL) {
1620 o2::dcs::addConfigItem(this->mTuning, "O2ChipID", std::to_string(chipID));
1621 o2::dcs::addConfigItem(this->mTuning, "ChipDbID", std::to_string(confDBid));
1622 o2::dcs::addConfigItem(this->mTuning, "Dcol", std::to_string(i));
1623 o2::dcs::addConfigItem(this->mTuning, "Row", ds);
1624 o2::dcs::addConfigItem(this->mTuning, "Col", ds);
1625
1626 dcolIDs += std::to_string(i) + '|'; // prepare string for second object for ccdb prod
1627 nDcols++;
1628 }
1629 }
1630 }
1631
1632 if (this->mTagSinglePix) {
1633 if (PixelType == "Noisy") {
1634 for (int i = 0; i < v.size(); i++) {
1635 short int dcol = ((v[i] - v[i] % 1000) / 1000) / 2;
1636 if (vPixDcolCounter[dcol] > N_PIX_DCOL) { // single pixels must not be already in dcolIDs
1637 continue;
1638 }
1639
1640 // Noisy pixel IDs
1641 pixIDs_Noisy += std::to_string(v[i]);
1642 if (i + 1 < v.size()) {
1643 pixIDs_Noisy += '|';
1644 }
1645
1646 o2::dcs::addConfigItem(this->mTuning, "O2ChipID", std::to_string(chipID));
1647 o2::dcs::addConfigItem(this->mTuning, "ChipDbID", std::to_string(confDBid));
1648 o2::dcs::addConfigItem(this->mTuning, "Dcol", ds);
1649 o2::dcs::addConfigItem(this->mTuning, "Row", std::to_string(v[i] % 1000));
1650 o2::dcs::addConfigItem(this->mTuning, "Col", std::to_string(int((v[i] - v[i] % 1000) / 1000)));
1651 }
1652 }
1653
1654 if (PixelType == "Dead") {
1655 for (int i = 0; i < v.size(); i++) {
1656 pixIDs_Dead += std::to_string(v[i]);
1657 if (i + 1 < v.size()) {
1658 pixIDs_Dead += '|';
1659 }
1660 }
1661 }
1662
1663 if (PixelType == "Ineff") {
1664 for (int i = 0; i < v.size(); i++) {
1665 pixIDs_Ineff += std::to_string(v[i]);
1666 if (i + 1 < v.size()) {
1667 pixIDs_Ineff += '|';
1668 }
1669 }
1670 }
1671 }
1672
1673 if (!dcolIDs.empty()) {
1674 dcolIDs.pop_back(); // remove last pipe from the string
1675 } else {
1676 dcolIDs = "-1";
1677 }
1678
1679 if (pixIDs_Noisy.empty()) {
1680 pixIDs_Noisy = "-1";
1681 }
1682
1683 if (pixIDs_Dead.empty()) {
1684 pixIDs_Dead = "-1";
1685 }
1686
1687 if (pixIDs_Ineff.empty()) {
1688 pixIDs_Ineff = "-1";
1689 }
1690
1691 o2::dcs::addConfigItem(this->mPixStat, "O2ChipID", std::to_string(chipID));
1692 if (PixelType == "Dead" || PixelType == "Ineff") {
1693 o2::dcs::addConfigItem(this->mPixStat, "PixelType", PixelType);
1694 o2::dcs::addConfigItem(this->mPixStat, "PixelNos", n_pixel);
1695 o2::dcs::addConfigItem(this->mPixStat, "DcolNos", "-1");
1696 } else {
1697 o2::dcs::addConfigItem(this->mPixStat, "PixelType", PixelType);
1698 o2::dcs::addConfigItem(this->mPixStat, "PixelNos", n_pixel);
1699 o2::dcs::addConfigItem(this->mPixStat, "DcolNos", nDcols);
1700 }
1701 }
1702 if (this->mScanType != 'D' && this->mScanType != 'A' && this->mScanType != 'P' && this->mScanType != 'p') {
1703 o2::dcs::addConfigItem(this->mTuning, "O2ChipID", std::to_string(chipID));
1704 o2::dcs::addConfigItem(this->mTuning, "ChipDbID", std::to_string(confDBid));
1705 o2::dcs::addConfigItem(this->mTuning, name, (strcmp(name, "ITHR") == 0 || strcmp(name, "VCASN") == 0) ? std::to_string((int)data[0]) : std::to_string(data[0]));
1706 o2::dcs::addConfigItem(this->mTuning, "Rms", std::to_string(data[1]));
1707 o2::dcs::addConfigItem(this->mTuning, "Status", std::to_string((int)data[4])); // percentage of unsuccess
1708 }
1709 if (this->mScanType == 'T') {
1710 o2::dcs::addConfigItem(this->mTuning, "Noise", std::to_string(data[2]));
1711 o2::dcs::addConfigItem(this->mTuning, "NoiseRms", std::to_string(data[3]));
1712 }
1713
1714 if (this->mScanType == 'P') {
1715 o2::dcs::addConfigItem(this->mTuning, "O2ChipID", std::to_string(chipID));
1716 o2::dcs::addConfigItem(this->mTuning, "ChipDbID", std::to_string(confDBid));
1717 o2::dcs::addConfigItem(this->mTuning, "Tot", std::to_string(data[2])); // time over threshold
1718 o2::dcs::addConfigItem(this->mTuning, "TotRms", std::to_string(data[3])); // time over threshold rms
1719 o2::dcs::addConfigItem(this->mTuning, "Rt", std::to_string(data[0])); // rise time
1720 o2::dcs::addConfigItem(this->mTuning, "RtRms", std::to_string(data[1])); // rise time rms
1721 }
1722
1723 //- Pulse shape 2D: avgToT, rmsToT, MTC, rmsMTC, avgMTCD, rmsMTCD, avgMPL, rmsMPL, avgMPLC, rmsMPLC
1724 if (this->mScanType == 'p') {
1725 o2::dcs::addConfigItem(this->mTuning, "O2ChipID", std::to_string(chipID));
1726 o2::dcs::addConfigItem(this->mTuning, "ChipDbID", std::to_string(confDBid));
1727 o2::dcs::addConfigItem(this->mTuning, "Tot", std::to_string(data[0])); // time over threshold
1728 o2::dcs::addConfigItem(this->mTuning, "TotRms", std::to_string(data[1])); // time over threshold rms
1729 o2::dcs::addConfigItem(this->mTuning, "MinThrChg", std::to_string(data[2])); // Min threshold charge
1730 o2::dcs::addConfigItem(this->mTuning, "MinThrChgRms", std::to_string(data[3])); // Min threshold charge rms
1731 o2::dcs::addConfigItem(this->mTuning, "MinThrChgDel", std::to_string(data[4])); // Min threshold charge delay
1732 o2::dcs::addConfigItem(this->mTuning, "MinThrChgDelRms", std::to_string(data[5])); // Min threshold charge delay rms
1733 o2::dcs::addConfigItem(this->mTuning, "MaxPulLen", std::to_string(data[6])); // Max pulse length
1734 o2::dcs::addConfigItem(this->mTuning, "MaxPulLenRms", std::to_string(data[7])); // Max pulse length rms
1735 o2::dcs::addConfigItem(this->mTuning, "MaxPulLenChg", std::to_string(data[8])); // Max pulse length charge
1736 o2::dcs::addConfigItem(this->mTuning, "MaxPulLenChgRms", std::to_string(data[9])); // Max pulse length charge rms
1737 }
1738
1739 return;
1740}
1741
1742//___________________________________________________________________
1743void ITSThresholdCalibrator::updateTimeDependentParams(ProcessingContext& pc)
1744{
1745 static bool initOnceDone = false;
1746 if (!initOnceDone) {
1747 initOnceDone = true;
1748 mDataTakingContext = pc.services().get<DataTakingContext>();
1749 }
1750 mTimingInfo = pc.services().get<o2::framework::TimingInfo>();
1751}
1752
1755{
1756 // Add configuration item to output strings for CCDB
1757 const char* name = nullptr;
1758 std::set<int> thisRUs;
1759
1760 if (mScanType == 'V' || mScanType == 'I' || mScanType == 'T') {
1761 // Loop over each chip and calculate avg and rms
1762 name = mScanType == 'V' ? "VCASN" : mScanType == 'I' ? "ITHR"
1763 : "THR";
1764 if (mScanType == 'I') {
1765 // Only ITHR scan: assign default ITHR = 50 if chip has no avg ITHR
1766 for (auto& iRU : mRuSet) {
1767 if (mRunTypeRU[iRU] >= nInjScaled * getNumberOfActiveLinks(mActiveLinks[iRU]) || mRunStopRequested) {
1768 std::vector<short int> chipList = getChipListFromRu(iRU, mActiveLinks[iRU]);
1769 for (size_t i = 0; i < chipList.size(); i++) {
1770 if ((chipList[i] % mChipModBase) != mChipModSel) {
1771 continue;
1772 }
1773 if (!mThresholds.count(chipList[i])) {
1774 if (mVerboseOutput) {
1775 LOG(info) << "Setting ITHR = 50 for chip " << chipList[i];
1776 }
1777 std::vector<float> data = {50, 0, 0, 0, 0};
1778 addDatabaseEntry(chipList[i], name, data, false);
1779 }
1780 }
1781 }
1782 }
1783 }
1784
1785 auto it = this->mThresholds.cbegin();
1786 while (it != this->mThresholds.cend()) {
1787 short int iRU = getRUID(it->first);
1788 if (!isCRUITS && (mRunTypeRU[iRU] < nInjScaled * getNumberOfActiveLinks(mActiveLinks[iRU]) && !mRunStopRequested)) {
1789 ++it;
1790 continue;
1791 }
1792 thisRUs.insert(iRU);
1793 float avgT, rmsT, avgN, rmsN, mpvT, outVal;
1794 this->findAverage(it->second, avgT, rmsT, avgN, rmsN);
1795 outVal = avgT;
1796 if (isMpv) {
1797 mpvT = std::distance(mpvCounter[it->first].begin(), std::max_element(mpvCounter[it->first].begin(), mpvCounter[it->first].end())) + mMin;
1798 outVal = mpvT;
1799 }
1800 if (mVerboseOutput) {
1801 LOG(info) << "Average or mpv " << name << " of chip " << it->first << " = " << outVal << " e-";
1802 }
1803 float status = ((float)it->second[4] / (float)(it->second[4] + it->second[5])) * 100.; // percentage of successful threshold extractions
1804 if (status < mPercentageCut && (mScanType == 'I' || mScanType == 'V')) {
1805 if (mScanType == 'I') { // default ITHR if percentage of success < mPercentageCut
1806 outVal = 50.;
1807 if (mVerboseOutput) {
1808 LOG(info) << "Chip " << it->first << " status is " << status << ". Setting ITHR = 50";
1809 }
1810 } else { // better to not set any VCASN if the percentage of success < mPercentageCut
1811 it = this->mThresholds.erase(it);
1812 if (mVerboseOutput) {
1813 LOG(info) << "Chip " << it->first << " status is " << status << ". Ignoring this chip.";
1814 }
1815 continue;
1816 }
1817 }
1818 std::vector<float> data = {outVal, rmsT, avgN, rmsN, status};
1819 this->addDatabaseEntry(it->first, name, data, false);
1820 it = this->mThresholds.erase(it);
1821 }
1822 } else if (this->mScanType == 'D' || this->mScanType == 'A') {
1823 // Loop over each chip and calculate avg and rms
1824 name = "PixID";
1825 // Extract hits from the full matrix
1826 auto itchip = this->mPixelHits.cbegin();
1827 while (itchip != this->mPixelHits.cend()) { // loop over chips collected
1828 short int iRU = getRUID(itchip->first);
1829 if (!isCRUITS && (mRunTypeRU[iRU] < nInjScaled * getNumberOfActiveLinks(mActiveLinks[iRU]) && !mRunStopRequested)) {
1830 ++itchip;
1831 continue;
1832 }
1833 thisRUs.insert(iRU);
1834 if (mVerboseOutput) {
1835 LOG(info) << "Extracting hits for the full matrix of chip " << itchip->first;
1836 }
1837 for (short int irow = 0; irow < 512; irow++) {
1838 this->extractAndUpdate(itchip->first, irow);
1839 }
1840 if (this->mVerboseOutput) {
1841 LOG(info) << "Chip " << itchip->first << " hits extracted";
1842 }
1843 ++itchip;
1844 }
1845
1846 auto it = this->mNoisyPixID.cbegin();
1847 while (it != this->mNoisyPixID.cend()) {
1848 PixelType = "Noisy";
1849 if (mVerboseOutput) {
1850 LOG(info) << "Extracting noisy pixels in the full matrix of chip " << it->first;
1851 }
1852 this->addDatabaseEntry(it->first, name, std::vector<float>(), false); // all zeros are not used here
1853 if (this->mVerboseOutput) {
1854 LOG(info) << "Chip " << it->first << " done";
1855 }
1856 it = this->mNoisyPixID.erase(it);
1857 }
1858
1859 auto it_d = this->mDeadPixID.cbegin();
1860 while (it_d != this->mDeadPixID.cend()) {
1861 if (mVerboseOutput) {
1862 LOG(info) << "Extracting dead pixels in the full matrix of chip " << it_d->first;
1863 }
1864 PixelType = "Dead";
1865 this->addDatabaseEntry(it_d->first, name, std::vector<float>(), false); // all zeros are not used here
1866 it_d = this->mDeadPixID.erase(it_d);
1867 }
1868
1869 auto it_ineff = this->mIneffPixID.cbegin();
1870 while (it_ineff != this->mIneffPixID.cend()) {
1871 if (mVerboseOutput) {
1872 LOG(info) << "Extracting inefficient pixels in the full matrix of chip " << it_ineff->first;
1873 }
1874 PixelType = "Ineff";
1875 this->addDatabaseEntry(it_ineff->first, name, std::vector<float>(), false);
1876 it_ineff = this->mIneffPixID.erase(it_ineff);
1877 }
1878 } else if (this->mScanType == 'P' || this->mScanType == 'p' || mScanType == 'R') { // pulse length scan 1D and 2D, vresetd scan 1D (2D already extracted in run())
1879 name = "Pulse";
1880 // extract hits for the available row(s)
1881 auto itchip = this->mPixelHits.cbegin();
1882 while (itchip != mPixelHits.cend()) {
1883 int iRU = getRUID(itchip->first);
1884 if (!mRunStopRequested && mRunTypeRU[iRU] < nInjScaled * getNumberOfActiveLinks(mActiveLinks[iRU])) {
1885 ++itchip;
1886 continue;
1887 }
1888 thisRUs.insert(iRU);
1889 if (mVerboseOutput) {
1890 LOG(info) << "Extracting hits from pulse shape scan or vresetd scan, chip " << itchip->first;
1891 }
1892 auto itrow = this->mPixelHits[itchip->first].cbegin();
1893 while (itrow != mPixelHits[itchip->first].cend()) { // in case there are multiple rows, for now it's 1 row
1894 this->extractAndUpdate(itchip->first, itrow->first); // fill the tree
1895 ++itrow;
1896 }
1897 if (mCalculate2DParams && (mScanType == 'P' || mScanType == 'p')) {
1898 this->addDatabaseEntry(itchip->first, name, mScanType == 'P' ? calculatePulseParams(itchip->first) : calculatePulseParams2D(itchip->first), false);
1899 }
1900 if (this->mVerboseOutput) {
1901 LOG(info) << "Chip " << itchip->first << " hits extracted";
1902 }
1903 ++itchip;
1904 }
1905 // reset RU counters so that the chips which are done will not appear again in the DCSConfigObject
1906 }
1907
1908 for (auto& ru : thisRUs) {
1909 mRunTypeRU[ru] = 0; // reset
1910 }
1911
1912 return;
1913}
1914
1916// O2 functionality allowing to do post-processing when the upstream device
1917// tells that there will be no more input data
1919{
1920 if (!isEnded && !mRunStopRequested) {
1921 LOGF(info, "endOfStream report:", mSelfName);
1922 if (isCRUITS) {
1923 finalize();
1924 }
1925 this->finalizeOutput();
1926 isEnded = true;
1927 }
1928 return;
1929}
1930
1932// DDS stop method: simply close the latest tree
1934{
1935 if (!isEnded) {
1936 LOGF(info, "stop() report:", mSelfName);
1937 this->finalizeOutput();
1938 isEnded = true;
1939 }
1940 return;
1941}
1942
1945{
1947 std::vector<InputSpec> inputs;
1948 inputs.emplace_back("digits", detOrig, "DIGITS", 0, Lifetime::Timeframe);
1949 inputs.emplace_back("digitsROF", detOrig, "DIGITSROF", 0, Lifetime::Timeframe);
1950 inputs.emplace_back("calib", detOrig, "GBTCALIB", 0, Lifetime::Timeframe);
1951 // inputs.emplace_back("confdbmap", detOrig, "CONFDBMAP", 0, Lifetime::Condition,
1952 // o2::framework::ccdbParamSpec("ITS/Calib/Confdbmap"));
1953
1954 std::vector<OutputSpec> outputs;
1955 outputs.emplace_back("ITS", "TSTR", inpConf.chipModSel);
1956 outputs.emplace_back("ITS", "PIXTYP", inpConf.chipModSel);
1957 outputs.emplace_back("ITS", "RUNT", inpConf.chipModSel);
1958 outputs.emplace_back("ITS", "SCANT", inpConf.chipModSel);
1959 outputs.emplace_back("ITS", "FITT", inpConf.chipModSel);
1960 outputs.emplace_back("ITS", "CONFDBV", inpConf.chipModSel);
1961 outputs.emplace_back("ITS", "QCSTR", inpConf.chipModSel);
1962
1963 return DataProcessorSpec{
1964 "its-calibrator_" + std::to_string(inpConf.chipModSel),
1965 inputs,
1966 outputs,
1967 AlgorithmSpec{adaptFromTask<ITSThresholdCalibrator>(inpConf)},
1968 Options{{"fittype", VariantType::String, "derivative", {"Fit type to extract thresholds, with options: fit, derivative (default), hitcounting"}},
1969 {"verbose", VariantType::Bool, false, {"Use verbose output mode"}},
1970 {"output-dir", VariantType::String, "./", {"ROOT trees output directory"}},
1971 {"meta-output-dir", VariantType::String, "/dev/null", {"Metadata output directory"}},
1972 {"meta-type", VariantType::String, "", {"metadata type"}},
1973 {"nthreads", VariantType::Int, 1, {"Number of threads, default is 1"}},
1974 {"enable-cw-cnt-check", VariantType::Bool, false, {"Use to enable the check of the calib word counter row by row in addition to the hits"}},
1975 {"enable-single-pix-tag", VariantType::Bool, false, {"Use to enable tagging of single noisy pix in digital and analogue scan"}},
1976 {"ccdb-mgr-url", VariantType::String, "", {"CCDB url to download confDBmap"}},
1977 {"min-vcasn", VariantType::Int, 30, {"Min value of VCASN in vcasn scan, default is 30"}},
1978 {"max-vcasn", VariantType::Int, 70, {"Max value of VCASN in vcasn scan, default is 70"}},
1979 {"min-ithr", VariantType::Int, 25, {"Min value of ITHR in ithr scan, default is 25"}},
1980 {"max-ithr", VariantType::Int, 100, {"Max value of ITHR in ithr scan, default is 100"}},
1981 {"manual-mode", VariantType::Bool, false, {"Flag to activate the manual mode in case run type is not recognized"}},
1982 {"manual-min", VariantType::Int, 0, {"Min value of the variable used for the scan: use only in manual mode"}},
1983 {"manual-max", VariantType::Int, 50, {"Max value of the variable used for the scan: use only in manual mode"}},
1984 {"manual-min2", VariantType::Int, 0, {"Min2 value of the 2nd variable (if any) used for the scan (ex: charge in tot_calib): use only in manual mode"}},
1985 {"manual-max2", VariantType::Int, 50, {"Max2 value of the 2nd variable (if any) used for the scan (ex: charge in tot_calib): use only in manual mode"}},
1986 {"manual-step", VariantType::Int, 1, {"Step value: defines the steps between manual-min and manual-max. Default is 1. Use only in manual mode"}},
1987 {"manual-step2", VariantType::Int, 1, {"Step2 value: defines the steps between manual-min2 and manual-max2. Default is 1. Use only in manual mode"}},
1988 {"manual-scantype", VariantType::String, "T", {"scan type, can be D, T, I, V, P, p: use only in manual mode"}},
1989 {"manual-strobewindow", VariantType::Int, 5, {"strobe duration in clock cycles, default is 5 = 125 ns: use only in manual mode"}},
1990 {"save-tree", VariantType::Bool, false, {"Flag to save ROOT tree on disk: use only in manual mode"}},
1991 {"scale-ninj", VariantType::Bool, false, {"Flag to activate the scale of the number of injects to be used to count hits from specific MEBs: use only in manual mode and in combination with --meb-select"}},
1992 {"enable-mpv", VariantType::Bool, false, {"Flag to enable calculation of most-probable value in vcasn/ithr scans"}},
1993 {"enable-cru-its", VariantType::Bool, false, {"Flag to enable the analysis of raw data on disk produced by CRU_ITS IB commissioning tools"}},
1994 {"ninj", VariantType::Int, 50, {"Number of injections per change, default is 50"}},
1995 {"dump-scurves", VariantType::Bool, false, {"Dump any s-curve to disk in ROOT file. Works only with fit option."}},
1996 {"max-dump", VariantType::Int, -1, {"Maximum number of s-curves to dump in ROOT file per chip. Works with fit option and dump-scurves flag enabled. Default: dump all"}},
1997 {"chip-dump", VariantType::String, "", {"Dump s-curves only for these Chip IDs (0 to 24119). If multiple IDs, write them separated by comma. Default is empty string: dump all"}},
1998 {"calculate-slope", VariantType::Bool, false, {"For Pulse Shape 2D: if enabled it calculate the slope of the charge vs strobe delay trend for each pixel and fill it in the output tree"}},
1999 {"charge-a", VariantType::Int, 0, {"To use with --calculate-slope, it defines the charge (in DAC) for the 1st point used for the slope calculation"}},
2000 {"charge-b", VariantType::Int, 0, {"To use with --calculate-slope, it defines the charge (in DAC) for the 2nd point used for the slope calculation"}},
2001 {"meb-select", VariantType::Int, -1, {"Select from which multi-event buffer consider the hits: 0,1 or 2"}},
2002 {"s-curve-col-step", VariantType::Int, 8, {"save s-curves points to tree every s-curve-col-step pixels on 1 row"}},
2003 {"percentage-cut", VariantType::Int, 25, {"discard chip in ITHR/VCASN scan if the percentage of success is less than this cut"}}}};
2004}
2005} // namespace its
2006} // namespace o2
int32_t i
void output(const std::map< std::string, ChannelStat > &channels)
Definition rawdump.cxx:197
uint32_t col
Definition RawData.h:4
uint32_t c
Definition RawData.h:2
static BasicCCDBManager & instance()
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.
TransitionHandlingState transitionState() const
void run(ProcessingContext &pc) final
void stop() final
This is invoked on stop.
void endOfStream(EndOfStreamContext &ec) final
This is invoked whenever we have an EndOfStream event.
void finaliseCCDB(ConcreteDataMatcher &matcher, void *obj) final
ITSThresholdCalibrator(const ITSCalibInpConf &inpConf)
void expandChipInfoHW(int idSW, int &lay, int &sta, int &ssta, int &mod, int &chipInMod) const
convert global SW chip ID to name in HW conventions
static constexpr std::string_view getName()
GLint GLenum GLint x
Definition glcorearb.h:403
GLint GLsizei count
Definition glcorearb.h:399
GLuint64EXT * result
Definition glcorearb.h:5662
GLuint buffer
Definition glcorearb.h:655
GLsizeiptr size
Definition glcorearb.h:659
const GLdouble * v
Definition glcorearb.h:832
GLuint const GLchar * name
Definition glcorearb.h:781
GLboolean GLboolean GLboolean b
Definition glcorearb.h:1233
GLenum GLsizei GLsizei GLint * values
Definition glcorearb.h:1576
GLboolean * data
Definition glcorearb.h:298
GLuint start
Definition glcorearb.h:469
GLboolean GLboolean GLboolean GLboolean a
Definition glcorearb.h:1233
constexpr o2::header::DataOrigin gDataOriginITS
Definition DataHeader.h:570
long getCurrentTimestamp()
returns the timestamp in long corresponding to "now"
void addConfigItem(DCSconfigObject_t &configVector, std::string key, const T value)
std::vector< ConfigParamSpec > Options
double erf_ithr(double *xx, double *par)
double erf(double *xx, double *par)
o2::framework::DataProcessorSpec getITSThresholdCalibratorSpec(const ITSCalibInpConf &inpConf)
return * this
Enum< T >::Iterator begin(Enum< T >)
Definition Defs.h:173
void createDirectoriesIfAbsent(std::string const &path)
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
Defining DataPointCompositeObject explicitly as copiable.
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
std::string filename()
std::string asString() const
bool fillFileData(const std::string &fname, bool fillmd5=false, const std::string &tmpEnding="")
void setDataTakingContext(const o2::framework::DataTakingContext &dtc)
std::string envId
The environment ID for the deployment.
std::string runNumber
The current run number.
static std::string rectifyDirectory(const std::string_view p)
static std::string concat_string(Ts const &... ts)
o2::mch::DsIndex ds
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
std::vector< Digit > digits
std::vector< int > row