Project
Loading...
Searching...
No Matches
TOFChannelCalibrator.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#include "Framework/Logger.h"
14#include <cassert>
15#include <iostream>
16#include <sstream>
17#include <TStopwatch.h>
18#include "Fit/Fitter.h"
19#include "Fit/BinData.h"
20#include "Math/WrappedMultiTF1.h"
21
22#ifdef WITH_OPENMP
23#include <omp.h>
24#endif
25
26namespace o2
27{
28namespace tof
29{
30
34using boost::histogram::indexed;
35using namespace o2::tof;
36//using boost::histogram::algorithm; // not sure why it does not work...
37
38//_____________________________________________
39void TOFChannelData::fill(const gsl::span<const o2::dataformats::CalibInfoTOF> data)
40{
41 // check on multiplicity to avoid noisy events (SAFE MODE)
42 static int ntf = 0;
43 static float sumDt = 0;
44
45 if (mSafeMode) {
46 float sumdt = 0;
47 int ngood = 0;
48 for (int j = 0; j < data.size(); j++) {
49 float dtraw = data[j].getDeltaTimePi();
50 float dt = mCalibTOFapi->getTimeCalibration(data[j].getTOFChIndex(), data[j].getTot());
51 if (dt > -50000 && dt < 50000) {
52 sumdt += dt;
53 ngood++;
54 }
55 }
56 if (ngood) {
57 sumdt /= ngood;
58 sumDt += sumdt;
59 ntf++;
60 }
61
62 if (ntf && (sumdt > 5000 + sumDt / ntf || sumdt < -5000 + sumDt / ntf)) { // skip TF since it is very far from average behaviour (probably noise if detector already partial calibrated)
63 return;
64 }
65 }
66
67 // fill container
68 for (int i = data.size(); i--;) {
69 auto flags = data[i].getFlags();
70 if (flags & o2::dataformats::CalibInfoTOF::kMultiHit) { // skip multi-hit clusters
71 continue;
72 }
73 if (flags & o2::dataformats::CalibInfoTOF::kNoBC) { // skip events far from Int BC
74 continue;
75 }
76
77 auto ch = data[i].getTOFChIndex();
78 int sector = ch / Geo::NPADSXSECTOR;
79 int chInSect = ch % Geo::NPADSXSECTOR;
80 auto dt = data[i].getDeltaTimePi();
81
82 auto tot = data[i].getTot();
83 // TO BE DISCUSSED: could it be that the LHCphase is too old? If we ar ein sync mode, it could be that it is not yet created for the current run, so the one from the previous run (which could be very old) is used. But maybe it does not matter much, since soon enough a calibrated LHC phase should be produced
84 auto corr = mCalibTOFapi->getTimeCalibration(ch, tot); // we take into account LHCphase, offsets and time slewing
85 auto dtcorr = dt - corr;
86
87 int used = o2::tof::Utils::addMaskBC(data[i].getMask(), data[i].getTOFChIndex()); // fill the current BC candidate mask and return the one used
88 dtcorr -= used * o2::tof::Geo::BC_TIME_INPS;
89
90 // add calib info for computation of LHC phase
92
93 // uncomment to enable auto correction of LHC phase
94 // dtcorr -= Utils::mLHCPhase;
95
96 LOG(debug) << "Residual LHCphase = " << Utils::mLHCPhase;
97
98#ifdef DEBUGGING
99 mChannelDist->Fill(ch, dtcorr);
100#endif
101
102 if (!mPerStrip) {
103 mHisto[sector](dtcorr, chInSect); // we pass the calibrated time
104 mEntries[ch] += 1;
105 } else {
106 int istrip = ch / 96;
107 int istripInSector = chInSect / 96;
108 int halffea = (chInSect % 96) / 12;
109 int choffset = (istrip - istripInSector) * 96;
110 //int minch = istripInSector * 96;
111 //int maxch = minch + 96;
112 int minch = istripInSector * 96 + halffea * 12;
113 int maxch = minch + 12;
114 for (int ich = minch; ich < maxch; ich++) {
115 mHisto[sector](dtcorr, ich); // we pass the calibrated time
116 mEntries[ich + choffset] += 1;
117 }
118 }
119 }
120}
121
122//_____________________________________________
123void TOFChannelData::fill(const gsl::span<const o2::tof::CalibInfoCluster> data)
124{
125 // fill container
126 for (int i = data.size(); i--;) {
127 auto ch = data[i].getCH();
128 auto dch = data[i].getDCH(); // this is a char! if you print it, you need to cast it to int
129 auto dt = data[i].getDT();
130 auto tot1 = data[i].getTOT1();
131 auto tot2 = data[i].getTOT2();
132
133 // we order them so that the channel number of the first cluster is smaller than
134 // the one of the second cluster
135 if (dch < 0) {
136 ch += dch;
137 dt = -dt;
138 dch = -dch;
139 float inv = tot1;
140 tot1 = tot2;
141 tot2 = inv;
142 }
143
144 int sector = ch / Geo::NPADSXSECTOR;
145 int absoluteStrip = ch / Geo::NPADS;
146 int stripInSect = absoluteStrip % Geo::NSTRIPXSECTOR;
147 int shift = 0;
148 if (dch == 1) {
149 shift = 0; // 2nd channel is on the right
150 } else if (dch == 48) {
151 shift = 1; // 2nd channel is at the top
152 } else {
153 continue;
154 }
155 int chOnStrip = ch % 96;
156 int comb = 96 * shift + chOnStrip; // index of the current pair of clusters on the strip; there are in total 96 + 48
157
158 auto corr1 = mCalibTOFapi->getTimeCalibration(ch, tot1); // we take into account LHCphase, offsets and time slewing
159 auto corr2 = mCalibTOFapi->getTimeCalibration(ch + dch, tot2); // we take into account LHCphase, offsets and time slewing
160 LOG(debug) << "inserting in channel " << ch << ", " << ch + dch << ": dt = " << dt << ", tot1 = " << tot1 << ", tot2 = " << tot2 << ", corr1 = " << corr1 << ", corr2 = " << corr2 << ", corrected dt = " << dt - corr1 + corr2;
161
162 dt -= corr1 - corr2;
163
164 int combInSect = comb + stripInSect * NCOMBINSTRIP;
165
166 LOG(debug) << "ch = " << ch << ", sector = " << sector << ", absoluteStrip = " << absoluteStrip << ", stripInSect = " << stripInSect << ", shift = " << shift << ", dch = " << (int)dch << ", chOnStrip = " << chOnStrip << ", comb = " << comb;
167
168 mHisto[sector](dt, combInSect); // we pass the difference of the *calibrated* times
169 mEntries[comb + NCOMBINSTRIP * absoluteStrip] += 1;
170
171#ifdef DEBUGGING
172 mChannelDist->Fill(comb + NCOMBINSTRIP * absoluteStrip, dt);
173#endif
174 }
175}
176
177//_____________________________________________
179{
180 // merge data of 2 slots
181 for (int isect = 0; isect < Geo::NSECTORS; isect++) {
182 mHisto[isect] += prev->getHisto(isect);
183 }
184 for (auto iel = 0; iel < mEntries.size(); iel++) {
185 mEntries[iel] += prev->mEntries[iel];
186 }
187}
188
189//_____________________________________________
190bool TOFChannelData::hasEnoughData(int minEntries) const
191{
192 // true if all channels can be fitted --> have enough statistics
193
194 // We consider that we have enough entries if the mean of the number of entries in the channels
195 // with at least one entry is greater than the cut at "minEntries"
196 // Channels/pairs with zero entries are assumed to be off --> we do not consider them
197
198 //printEntries();
199 int nValid = 0;
200 float mean = 0;
201 int smallestElementIndex = -1;
202 int smallestEntries = 1e5;
203 int largestElementIndex = -1;
204 int largestEntries = 0;
205 for (auto i = 0; i < mEntries.size(); ++i) {
206 if (mEntries[i] != 0) { // skipping channels/pairs if they have zero entries (most likely they are simply off)
207 mean += mEntries[i];
208 ++nValid;
209 if (mEntries[i] < smallestEntries) {
210 smallestEntries = mEntries[i];
211 smallestElementIndex = i;
212 }
213 if (mEntries[i] > largestEntries) {
214 largestEntries = mEntries[i];
215 largestElementIndex = i;
216 }
217 }
218 }
219 if (nValid == 0) {
220 LOG(info) << "hasEnough = false: all channels/pairs are empty";
221 return false;
222 }
223
224 LOG(debug) << "mean = " << mean << ", nvalid = " << nValid;
225 mean /= nValid;
226
227 LOG(debug) << "minElement is at position " << smallestElementIndex << " and has " << smallestEntries << " entries";
228 LOG(debug) << "maxElement is at position " << largestElementIndex << " and has " << largestEntries << " entries";
229 float threshold = minEntries + 5 * std::sqrt(minEntries);
230 bool enough = mean < threshold ? false : true;
231 if (enough) {
232 LOG(info) << "hasEnough: " << (int)enough << " ("
233 << nValid << " valid channels found (should be " << mEntries.size() << ") with mean = "
234 << mean << " with cut at = " << threshold << ") ";
235 }
236 return enough;
237}
238
239//_____________________________________________
241{
242 LOG(info) << "Printing histograms:";
243 std::ostringstream os;
244 for (int isect = 0; isect < Geo::NSECTORS; isect++) {
245 LOG(info) << "Sector: " << isect;
246 os << mHisto[isect];
247 auto nentriesInSec = boost::histogram::algorithm::sum(mHisto[isect]);
248 LOG(info) << "Number of entries in histogram: " << boost::histogram::algorithm::sum(mHisto[isect]);
249 int cnt = 0;
250 if (nentriesInSec != 0) {
251 for (auto&& x : indexed(mHisto[isect])) {
252 if (x.get() > 0) {
253 const auto i = x.index(0); // current index along first axis --> t-texp
254 const auto j = x.index(1); // current index along second axis --> channel
255 const auto b0 = x.bin(0); // current bin interval along first axis --> t-texp
256 const auto b1 = x.bin(1); // current bin interval along second axis --> channel
257 LOG(info) << "bin " << cnt << ": channel = " << j << " in [" << b1.lower() << ", " << b1.upper()
258 << "], t-texp in [" << b0.lower() << ", " << b0.upper() << "], has entries = " << x.get();
259 }
260 cnt++;
261 }
262 }
263 LOG(info) << cnt << " bins inspected";
264 }
265}
266
267//_____________________________________________
268void TOFChannelData::print(int isect) const
269{
270 LOG(info) << "*** Printing histogram " << isect;
271 std::ostringstream os;
272 int cnt = 0;
273 os << mHisto[isect];
274 LOG(info) << "Number of entries in histogram: " << boost::histogram::algorithm::sum(mHisto[isect]);
275 for (auto&& x : indexed(mHisto[isect])) { // does not work also when I use indexed(*(mHisto[sector]))
276 cnt++;
277 LOG(debug) << " c " << cnt << " i " << x.index(0) << " j " << x.index(1) << " b0 " << x.bin(0) << " b1 " << x.bin(1) << " val= " << *x << "|" << x.get();
278 if (x.get() > 0) {
279 LOG(info) << "x = " << x.get() << " c " << cnt;
280 }
281 }
282 LOG(info) << cnt << " bins inspected";
283}
284
285//_____________________________________________
287{
288 // to print number of entries per channel
289 for (int i = 0; i < mEntries.size(); ++i) {
290 if (mEntries.size() > tof::Geo::NCHANNELS) {
291 LOG(info) << "pair of channels " << i << " has " << mEntries[i] << " entries";
292 } else {
293 LOG(info) << "channel " << i << " has " << mEntries[i] << " entries";
294 }
295 }
296}
297
298//_____________________________________________
300{
301 // find the bin along the x-axis (with t-texp) where the value "v" is; this does not depend on the channel
302 // (axis 1), nor on the sector, so we use sector0
303
304 if (v == mRange) {
305 v -= 1.e-1;
306 }
307
308 LOG(debug) << "In FindBin, v = : " << v;
309 LOG(debug) << "bin0 limits: lower = " << mHisto[0].axis(0).bin(0).lower() << ", upper = " << mHisto[0].axis(0).bin(0).upper();
310 LOG(debug) << "bin1000 limits: lower = " << mHisto[0].axis(0).bin(mNBins - 1).lower() << ", upper = " << mHisto[0].axis(0).bin(mNBins - 1).upper();
311 LOG(debug) << "v = " << v << " is in bin " << mHisto[0].axis(0).index(v);
312
313 return mHisto[0].axis(0).index(v);
314}
315
316//_____________________________________________
317float TOFChannelData::integral(int chmin, int chmax, float binmin, float binmax) const
318{
319 // calculates the integral in [chmin, chmax] and in [binmin, binmax]
320
321 if (binmin < -mRange || binmax > mRange || chmin < 0 || chmax >= Geo::NSECTORS * mNElsPerSector) {
322 throw std::runtime_error("Check your bins, we cannot calculate the integrals in under/overflows bins");
323 }
324 if (binmax < binmin || chmax < chmin) {
325 throw std::runtime_error("Check your bin limits!");
326 }
327
328 int sector = chmin / mNElsPerSector;
329 if (sector != chmax / mNElsPerSector) {
330 throw std::runtime_error("We cannot integrate over channels that belong to different sectors");
331 }
332
333 int chinsectormin = chmin % mNElsPerSector;
334 int chinsectormax = chmax % mNElsPerSector;
335
336 float res2 = 0;
337 //TStopwatch t3;
338 int ind = -1;
339 int binxmin = findBin(binmin);
340 int binxmax = findBin(binmax);
341 LOG(debug) << "binxmin = " << binxmin << ", binxmax = " << binxmax;
342 //t3.Start();
343 for (unsigned j = chinsectormin; j <= chinsectormax; ++j) {
344 for (unsigned i = binxmin; i <= binxmax; ++i) {
345 const auto& v = mHisto[sector].at(i, j);
346 res2 += v;
347 }
348 }
349 //t3.Stop();
350 LOG(debug) << "Time for integral looping over axis (result = " << res2 << "):";
351 //t3.Print();
352
353 return res2;
354
355}
356
357//_____________________________________________
358float TOFChannelData::integral(int ch, float binmin, float binmax) const
359{
360 // calculates the integral along one fixed channel and in [binmin, binmax]
361
362 return integral(ch, ch, binmin, binmax);
363}
364
365//_____________________________________________
366float TOFChannelData::integral(int chmin, int chmax, int binxmin, int binxmax) const
367{
368 // calculates the integral in [chmin, chmax] and in [binmin, binmax]
369
370 if (binxmin < 0 || binxmax > mNBins || chmin < 0 || chmax >= Geo::NSECTORS * mNElsPerSector) {
371 throw std::runtime_error("Check your bins, we cannot calculate the integrals in under/overflows bins");
372 }
373 if (binxmax < binxmin || chmax < chmin) {
374 throw std::runtime_error("Check your bin limits!");
375 }
376
377 int sector = chmin / mNElsPerSector;
378 if (sector != chmax / mNElsPerSector) {
379 throw std::runtime_error("We cannot integrate over channels that belong to different sectors");
380 }
381
382 int chinsectormin = chmin % mNElsPerSector;
383 int chinsectormax = chmax % mNElsPerSector;
384
385 float res2 = 0;
386 //TStopwatch t3;
387 //t3.Start();
388 for (unsigned j = chinsectormin; j <= chinsectormax; ++j) {
389 for (unsigned i = binxmin; i <= binxmax; ++i) {
390 const auto& v = mHisto[sector].at(i, j);
391 res2 += v;
392 }
393 }
394 //t3.Stop();
395 LOG(debug) << "Time for integral looping over axis (result = " << res2 << "):";
396 //t3.Print();
397 return res2;
398
399}
400
401//_____________________________________________
402float TOFChannelData::integral(int ch, int binxmin, int binxmax) const
403{
404 // calculates the integral along one fixed channel and in [binmin, binmax]
405
406 return integral(ch, ch, binxmin, binxmax);
407}
408
409//_____________________________________________
410float TOFChannelData::integral(int ch) const
411{
412 // calculates the integral along one fixed channel and in the full x-range
413
414 return mEntries.at(ch);
415}
416
417//_____________________________________________
419{
420 // empty the container and redefine the range
421
423 std::fill(mEntries.begin(), mEntries.end(), 0);
424 mV2Bin = mNBins / (2 * mRange);
425 for (int isect = 0; isect < 18; isect++) {
426 mHisto[isect] = boost::histogram::make_histogram(boost::histogram::axis::regular<>(mNBins, -mRange, mRange, "t-texp"),
427 boost::histogram::axis::integer<>(0, mNElsPerSector, "channel index in sector" + std::to_string(isect)));
428 }
429 return;
430}
431
432//-------------------------------------------------------------------
433// TOF Channel Calibrator
434//-------------------------------------------------------------------
435
436template <typename T>
438{
439 // Extract results for the single slot
440
442 LOG(info) << "Finalize slot for calibration with cosmics " << slot.getTFStart() << " <= TF <= " << slot.getTFEnd();
443
444 // for the CCDB entry
445 std::map<std::string, std::string> md;
446 TimeSlewing& ts = mCalibTOFapi->getSlewParamObj(); // we take the current CCDB object, since we want to simply update the offset
447 // ts.bind();
448
449 int nbins = c->getNbins();
450 float range = c->getRange();
451 std::vector<int> entriesPerChannel = c->getEntriesPerChannel();
452
453#ifdef WITH_OPENMP
454 if (mNThreads < 1) {
455 mNThreads = std::min(omp_get_max_threads(), NMAXTHREADS);
456 }
457 LOG(debug) << "Number of threads that will be used = " << mNThreads;
458#pragma omp parallel for schedule(dynamic) num_threads(mNThreads)
459#else
460 mNThreads = 1;
461#endif
462 for (int sector = 0; sector < Geo::NSECTORS; sector++) {
463 TMatrixD mat(3, 3);
464 int ithread = 0;
465#ifdef WITH_OPENMP
466 ithread = omp_get_thread_num();
467#endif
468
469 LOG(info) << "Processing sector " << sector << " with thread " << ithread;
470 double xp[NCOMBINSTRIP], exp[NCOMBINSTRIP], deltat[NCOMBINSTRIP], edeltat[NCOMBINSTRIP];
471
472 std::array<double, 3> fitValues;
473 std::vector<float> histoValues;
474
475 auto& histo = c->getHisto(sector);
476
477 int offsetPairInSector = sector * Geo::NSTRIPXSECTOR * NCOMBINSTRIP;
478 int offsetsector = sector * Geo::NSTRIPXSECTOR * Geo::NPADS;
479 for (int istrip = 0; istrip < Geo::NSTRIPXSECTOR; istrip++) {
480 LOG(info) << "Processing strip " << istrip;
481 double fracUnderPeak[Geo::NPADS] = {0.};
482 bool isChON[96] = {false};
483 int offsetstrip = istrip * Geo::NPADS + offsetsector;
484 int goodpoints = 0;
485 int allpoints = 0;
486
487 TLinearFitter localFitter(1, mStripOffsetFunction.c_str());
488
489 int offsetPairInStrip = istrip * NCOMBINSTRIP;
490
491 localFitter.StoreData(kFALSE);
492 localFitter.ClearPoints();
493 for (int ipair = 0; ipair < NCOMBINSTRIP; ipair++) {
494 int chinsector = ipair + offsetPairInStrip;
495 int ich = chinsector + offsetPairInSector;
496 auto entriesInPair = entriesPerChannel.at(ich);
497 xp[allpoints] = ipair + 0.5; // pair index
498
499 if (entriesInPair == 0) {
500 localFitter.AddPoint(&(xp[allpoints]), 0.0, 1.0);
501 allpoints++;
502 continue; // skip always since a channel with 0 entries is normal, it will be flagged as problematic
503 }
504 if (entriesInPair < mMinEntries) {
505 LOG(debug) << "pair " << ich << " will not be calibrated since it has only " << entriesInPair << " entries (min = " << mMinEntries << ")";
506 localFitter.AddPoint(&(xp[allpoints]), 0.0, 1.0);
507 allpoints++;
508 continue;
509 }
510 fitValues.fill(-99999999);
511 histoValues.clear();
512
513 // make the slice of the 2D histogram so that we have the 1D of the current channel
514 for (unsigned i = 0; i < nbins; ++i) {
515 const auto& v = histo.at(i, chinsector);
516 LOG(debug) << "channel = " << ich << ", in sector = " << sector << " (where it is channel = " << chinsector << ") bin = " << i << " value = " << v;
517 histoValues.push_back(v);
518 }
519
520 double fitres = entriesInPair - 1;
521 fitres = fitGaus(nbins, histoValues.data(), -range, range, fitValues, nullptr, 2., true);
522 if (fitres >= 0) {
523 LOG(debug) << "Pair " << ich << " :: Fit result " << fitres << " Mean = " << fitValues[1] << " Sigma = " << fitValues[2];
524 } else {
525 LOG(debug) << "Pair " << ich << " :: Fit failed with result = " << fitres;
526 localFitter.AddPoint(&(xp[allpoints]), 0.0, 1.0);
527 allpoints++;
528 continue;
529 }
530
531 if (fitValues[2] < 0) {
532 fitValues[2] = -fitValues[2];
533 }
534
535 float intmin = fitValues[1] - 5 * fitValues[2]; // mean - 5*sigma
536 float intmax = fitValues[1] + 5 * fitValues[2]; // mean + 5*sigma
537
538 if (intmin < -mRange) {
539 intmin = -mRange;
540 }
541 if (intmax < -mRange) {
542 intmax = -mRange;
543 }
544 if (intmin > mRange) {
545 intmin = mRange;
546 }
547 if (intmax > mRange) {
548 intmax = mRange;
549 }
550
551 xp[allpoints] = ipair + 0.5; // pair index
552 exp[allpoints] = 0.0; // error on pair index (dummy since it is on the pair index)
553 deltat[allpoints] = fitValues[1]; // delta between offsets from channels in pair (from the fit) - in ps
554 float integral = c->integral(ich, intmin, intmax);
555 edeltat[allpoints] = 20 + fitValues[2] / sqrt(integral); // TODO: for now put by default to 20 ps since it was seen to be reasonable; but it should come from the fit: who gives us the error from the fit ??????
556 localFitter.AddPoint(&(xp[allpoints]), deltat[allpoints], edeltat[allpoints]);
557 goodpoints++;
558 allpoints++;
559 int ch1 = ipair % 96;
560 int ch2 = ipair / 96 ? ch1 + 48 : ch1 + 1;
561 isChON[ch1] = true;
562 isChON[ch2] = true;
563
564 float fractionUnderPeak = entriesInPair > 0 ? integral / entriesInPair : 0;
565 // we keep as fractionUnderPeak of the channel the largest one that is found in the 3 possible pairs with that channel (for both channels ch1 and ch2 in the pair)
566 if (fracUnderPeak[ch1] < fractionUnderPeak) {
567 fracUnderPeak[ch1] = fractionUnderPeak;
568 }
569 if (fracUnderPeak[ch2] < fractionUnderPeak) {
570 fracUnderPeak[ch2] = fractionUnderPeak;
571 }
572
573#ifdef DEBUGGING
574// mFitCal->Fill(ipair + offsetPairInStrip + offsetPairInSector, fitValues[1]);
575#endif
576
577 } // end loop pairs
578
579 // fit strip offset
580 if (goodpoints == 0) {
581 continue;
582 }
583 LOG(debug) << "We found " << goodpoints << " good points for strip " << istrip << " in sector " << sector << " --> we can fit the TGraph";
584
585 bool isFirst = true;
586 int nparams = 0;
587 LOG(debug) << "N parameters before fixing = " << localFitter.GetNumberFreeParameters();
588
589 // we fix to zero the parameters that have no entry, plus the first one that we find, which we will use as reference for the other offsets
590 for (int i = 0; i < 96; ++i) {
591 if (isChON[i]) {
592 if (isFirst) {
593 localFitter.FixParameter(i, 0.);
594 isFirst = false;
595 } else {
596 nparams++;
597 }
598 } else {
599 localFitter.FixParameter(i, 0.);
600 }
601 }
602
603 LOG(debug) << "Strip = " << istrip << " fitted by thread = " << ithread << ", goodpoints = " << goodpoints << ", number of free parameters = "
604 << localFitter.GetNumberFreeParameters() << ", NDF = " << goodpoints - localFitter.GetNumberFreeParameters();
605
606 if (goodpoints <= localFitter.GetNumberFreeParameters()) {
607 LOG(debug) << "Skipped";
608 continue;
609 }
610
611 LOG(debug) << "N real params = " << nparams << ", fitter has " << localFitter.GetNumberFreeParameters() << " free parameters, " << localFitter.GetNumberTotalParameters() << " total parameters, " << localFitter.GetNpoints() << " points";
612 LOG(info) << "Sector = " << sector << ", strip = " << istrip << " fitted by thread = " << ithread << ": ready to fit";
613 localFitter.Eval();
614
615 LOG(info) << "Sector = " << sector << ", strip = " << istrip << " fitted by thread = " << ithread << " with Chi/NDF " << localFitter.GetChisquare() << "/" << goodpoints - localFitter.GetNumberFreeParameters();
616 LOG(debug) << "Strip = " << istrip << " fitted by thread = " << ithread << " with Chi/NDF " << localFitter.GetChisquare() << "/" << goodpoints - localFitter.GetNumberFreeParameters();
617
618 // if(localFitter.GetChisquare() > (goodpoints - localFitter.GetNumberFreeParameters())*10){
619 // continue;
620 // }
621
622 //update calibrations
623 for (int ichLocal = 0; ichLocal < Geo::NPADS; ichLocal++) {
624 int ich = ichLocal + offsetstrip;
625 ts.updateOffsetInfo(ich, localFitter.GetParameter(ichLocal));
626#ifdef DEBUGGING
627 mFitCal->Fill(ich, localFitter.GetParameter(ichLocal));
628#endif
629 ts.setFractionUnderPeak(ich / Geo::NPADSXSECTOR, ich % Geo::NPADSXSECTOR, fracUnderPeak[ichLocal]);
630 ts.setSigmaPeak(ich / Geo::NPADSXSECTOR, ich % Geo::NPADSXSECTOR, std::abs(std::sqrt(localFitter.GetCovarianceMatrixElement(ichLocal, ichLocal))));
631 }
632
633 } // end loop strips
634 } // end loop sectors
635
637 auto flName = o2::ccdb::CcdbApi::generateFileName(clName);
638 auto startValidity = slot.getStaticStartTimeMS() - o2::ccdb::CcdbObjectInfo::SECOND * 10; // adding a marging, in case some TFs were not processed
639 auto endValidity = o2::ccdb::CcdbObjectInfo::MONTH * 2;
640 ts.setStartValidity(startValidity);
641 ts.setEndValidity(endValidity);
642 mInfoVector.emplace_back("TOF/Calib/ChannelCalib", clName, flName, md, startValidity, endValidity);
643 mTimeSlewingVector.emplace_back(ts);
644
645#ifdef DEBUGGING
646 TFile fout("debug_tof_cal.root", "RECREATE");
647 mFitCal->Write();
648 mChannelDist->Write();
649 fout.Close();
650#endif
651}
652
653//_____________________________________________
654
655template <typename T>
657{
658 // Extract results for the single slot
660 LOG(info) << "Finalize slot " << slot.getTFStart() << " <= TF <= " << slot.getTFEnd();
661 int nbins = c->getNbins();
662 float range = c->getRange();
663 std::vector<int> entriesPerChannel = c->getEntriesPerChannel();
664
665 // for the CCDB entry
666 std::map<std::string, std::string> md;
667 TimeSlewing ts = mCalibTOFapi->getSlewParamObj(); // we take the current CCDB object, since we want to simply update the offset
668 ts.bind();
669
670#ifdef WITH_OPENMP
671 if (mNThreads < 1) {
672 mNThreads = std::min(omp_get_max_threads(), NMAXTHREADS);
673 }
674 LOG(debug) << "Number of threads that will be used = " << mNThreads;
675#pragma omp parallel for schedule(dynamic) num_threads(mNThreads)
676#else
677 mNThreads = 1;
678#endif
679 for (int sector = 0; sector < Geo::NSECTORS; sector++) {
680 TMatrixD mat(3, 3);
681 int ithread = 0;
682#ifdef WITH_OPENMP
683 ithread = omp_get_thread_num();
684#endif
685 LOG(info) << "Processing sector " << sector << " with thread " << ithread;
686 auto& histo = c->getHisto(sector);
687
688 std::array<double, 3> fitValues;
689 std::vector<float> histoValues;
690 for (int chinsector = 0; chinsector < Geo::NPADSXSECTOR; chinsector++) {
691 // make the slice of the 2D histogram so that we have the 1D of the current channel
692 int ich = chinsector + sector * Geo::NPADSXSECTOR;
693 auto entriesInChannel = entriesPerChannel.at(ich);
694 if (entriesInChannel == 0) {
695 ts.setChannelOffset(ich, 0.0);
696 continue; // skip always since a channel with 0 entries is normal, it will be flagged as problematic
697 }
698
699 if (entriesInChannel < mMinEntries) {
700 LOG(debug) << "channel " << ich << " will not be calibrated since it has only " << entriesInChannel << " entries (min = " << mMinEntries << ")";
701 ts.setChannelOffset(ich, 0.0);
702 continue;
703 }
704
705 LOG(debug) << "channel " << ich << " will be calibrated since it has " << entriesInChannel << " entries (min = " << mMinEntries << ")";
706 fitValues.fill(-99999999);
707 histoValues.clear();
708 // more efficient way
709 int imax = nbins / 2;
710 double maxval = 0;
711 double binwidth = 2 * range / nbins;
712 int binrange = int(1500 / binwidth) + 1;
713 float minRange = -range;
714 float maxRange = range;
715 int nbinsUsed = 0;
716 for (unsigned j = chinsector; j <= chinsector; ++j) {
717 for (unsigned i = 0; i < nbins; ++i) { // find peak
718 const auto& v = histo.at(i, j);
719 if (v > maxval) {
720 maxval = v;
721 imax = i;
722 }
723 }
724
725 float renorm = 1.; // to avoid fit problem when stats is too large (bad chi2)
726 if (maxval > 10) {
727 renorm = 10. / maxval;
728 }
729
730 for (unsigned i = 0; i < nbins; ++i) {
731 const auto& v = histo.at(i, j);
732 LOG(debug) << "channel = " << ich << ", in sector = " << sector << " (where it is channel = " << chinsector << ") bin = " << i << " value = " << v;
733 if (i >= imax - binrange && i < imax + binrange) {
734 histoValues.push_back(v * renorm);
735 nbinsUsed++;
736 } // not count for entries far from the peak (fit optimization)
737 }
738 }
739
740 minRange = (imax - nbins / 2 - binrange) * binwidth;
741 maxRange = (imax - nbins / 2 + binrange) * binwidth;
742
743 double fitres = fitGaus(nbinsUsed, histoValues.data(), minRange, maxRange, fitValues, nullptr, 2., false);
744 LOG(info) << "channel = " << ich << " fitted by thread = " << ithread;
745 if (fitres > -10) {
746 LOG(info) << "Channel " << ich << " :: Fit result " << fitres << " Mean = " << fitValues[1] << " Sigma = " << fitValues[2];
747 } else {
748#ifdef DEBUGGING
749 FILE* f = fopen(Form("%d.cal", ich), "w");
750 for (int i = 0; i < histoValues.size(); i++) {
751 fprintf(f, "%d %f %f\n", i, minRange + binwidth * i, histoValues[i]);
752 }
753 fclose(f);
754#endif
755 LOG(info) << "Channel " << ich << " :: Fit failed with result = " << fitres;
757 ts.setSigmaPeak(ich / Geo::NPADSXSECTOR, ich % Geo::NPADSXSECTOR, 99999);
758 ts.setChannelOffset(ich, 0.0);
759 continue;
760 }
761
762 if (fitValues[2] < 0) {
763 fitValues[2] = -fitValues[2];
764 }
765
766 float fractionUnderPeak;
767 float intmin = fitValues[1] - 5 * fitValues[2]; // mean - 5*sigma
768 float intmax = fitValues[1] + 5 * fitValues[2]; // mean + 5*sigma
769
770 if (intmin < -mRange) {
771 intmin = -mRange;
772 }
773 if (intmax < -mRange) {
774 intmax = -mRange;
775 }
776 if (intmin > mRange) {
777 intmin = mRange;
778 }
779 if (intmax > mRange) {
780 intmax = mRange;
781 }
782
783 fractionUnderPeak = entriesInChannel > 0 ? c->integral(ich, intmin, intmax) / entriesInChannel : 0;
784 // now we need to store the results in the TimeSlewingObject
785 ts.setFractionUnderPeak(ich / Geo::NPADSXSECTOR, ich % Geo::NPADSXSECTOR, fractionUnderPeak);
786 ts.setSigmaPeak(ich / Geo::NPADSXSECTOR, ich % Geo::NPADSXSECTOR, std::abs(fitValues[2]));
787
788 int tobeused = o2::tof::Utils::getMaxUsedChannel(ich);
789 fitValues[1] += tobeused * o2::tof::Geo::BC_TIME_INPS; // adjust by adding the right BC
790
791 if (std::abs(fitValues[1]) > mRange) {
793 ts.setSigmaPeak(ich / Geo::NPADSXSECTOR, ich % Geo::NPADSXSECTOR, 99999);
794 }
795
796 bool isProb = ts.getFractionUnderPeak(ich) < 0.5 || ts.getSigmaPeak(ich) > 1000;
797
798 if (!isProb) {
799 ts.updateOffsetInfo(ich, fitValues[1]);
800 } else {
801 ts.setChannelOffset(ich, 0.0);
802 }
803
804#ifdef DEBUGGING
805 mFitCal->Fill(ich, fitValues[1]);
806#endif
807 LOG(debug) << "udpdate channel " << ich << " with " << fitValues[1] << " offset in ps";
808 } // end loop channels in sector
809 } // end loop over sectors
811 auto flName = o2::ccdb::CcdbApi::generateFileName(clName);
812 auto startValidity = slot.getStaticStartTimeMS() - o2::ccdb::CcdbObjectInfo::SECOND * 10; // adding a marging, in case some TFs were not processed
813 auto endValidity = startValidity + o2::ccdb::CcdbObjectInfo::MONTH * 2;
814 ts.setStartValidity(startValidity);
815 ts.setEndValidity(endValidity);
816 mInfoVector.emplace_back("TOF/Calib/ChannelCalib", clName, flName, md, startValidity, endValidity);
817 mTimeSlewingVector.emplace_back(ts);
818
819#ifdef DEBUGGING
820 TFile fout("debug_tof_cal.root", "RECREATE");
821 mFitCal->Write();
822 mChannelDist->Write();
823 fout.Close();
824#endif
825
827}
828
829//_____________________________________________
830
833
834} // end namespace tof
835} // end namespace o2
uint64_t exp(uint64_t base, uint8_t exp) noexcept
int32_t i
const GPUTPCGMMerger::trackCluster & b1
uint32_t j
Definition RawData.h:0
uint32_t c
Definition RawData.h:2
std::ostringstream debug
std::tuple< ransState_t, stream_IT > renorm(ransState_t state, stream_IT outputIter, count_t frequency, size_t symbolTablePrecision)
long getStaticStartTimeMS() const
Definition TimeSlot.h:49
TFType getTFEnd() const
Definition TimeSlot.h:47
const Container * getContainer() const
Definition TimeSlot.h:53
TFType getTFStart() const
Definition TimeSlot.h:46
static std::string generateFileName(const std::string &inp)
Definition CcdbApi.cxx:798
static constexpr long MONTH
static constexpr long SECOND
float getFractionUnderPeak(int sector, int channel) const
void setSigmaPeak(int sector, int channel, float value)
float getSigmaPeak(int sector, int channel) const
void setFractionUnderPeak(int sector, int channel, float value)
bool updateOffsetInfo(int channel, float residualOffset)
float getTimeCalibration(int ich, float tot) const
static constexpr Int_t NSECTORS
Definition Geo.h:120
static constexpr Double_t BC_TIME_INPS
Definition Geo.h:103
static constexpr Int_t NSTRIPXSECTOR
Definition Geo.h:116
static constexpr Int_t NPADS
Definition Geo.h:110
static constexpr Int_t NPADSXSECTOR
Definition Geo.h:118
static constexpr int NCHANNELS
Definition Geo.h:124
void fill(const gsl::span< const o2::dataformats::CalibInfoTOF > data)
static constexpr int NCOMBINSTRIP
boostHisto & getHisto(int isect)
float integral(int chmin, int chmax, float binmin, float binmax) const
void merge(const TOFChannelData *prev)
bool hasEnoughData(int minEntries) const
static void addCalibTrack(float time)
Definition Utils.cxx:72
static int addMaskBC(int mask, int channel)
Definition Utils.cxx:270
static float mLHCPhase
Definition Utils.h:62
static int getMaxUsedChannel(int channel)
Definition Utils.cxx:317
static void printFillScheme()
Definition Utils.cxx:109
GLint GLenum GLint x
Definition glcorearb.h:403
const GLdouble * v
Definition glcorearb.h:832
GLdouble f
Definition glcorearb.h:310
GLenum GLint * range
Definition glcorearb.h:1899
GLboolean * data
Definition glcorearb.h:298
GLbitfield flags
Definition glcorearb.h:1570
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
std::string to_string(gsl::span< T, Size > span)
Definition common.h:52
static std::string getClassName(const T &obj)
get the class name of the object
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"