Project
Loading...
Searching...
No Matches
GainCalibrator.cxx
Go to the documentation of this file.
1// Copyright 2019-2020 CERN and copyright holders of ALICE O2.
2// See https://alice-o2.web.cern.ch/copyright for details of the copyright holders.
3// All rights not expressly granted are reserved.
4//
5// This software is distributed under the terms of the GNU General Public
6// License v3 (GPL Version 3), copied verbatim in the file "COPYING".
7//
8// In applying this license CERN does not waive the privileges and immunities
9// granted to it by virtue of its status as an Intergovernmental Organization
10// or submit itself to any jurisdiction.
11
12#include "Framework/Logger.h"
16#include "CPVBase/Geometry.h"
18#include "CCDB/CcdbApi.h"
20#include <TF1.h>
21#include <TFitResult.h>
22#include "MathUtils/fit.h"
23
24namespace o2
25{
26namespace cpv
27{
29//_____________________________________________________________________________
30// AmplitudeSpectrum
31//_____________________________________________________________________________
33 mSumA(0.),
34 mSumA2(0.)
35{
36 mBinContent.fill(0);
37}
38//_____________________________________________________________________________
40{
41 mNEntries = 0;
42 mSumA = 0.;
43 mSumA2 = 0.;
44 mBinContent.fill(0);
45}
46//_____________________________________________________________________________
48{
49 mSumA += rhs.mSumA;
50 mSumA2 += rhs.mSumA2;
51 mNEntries += rhs.mNEntries;
52 for (int i = 0; i < nBins; i++) {
53 mBinContent[i] += rhs.mBinContent[i];
54 }
55 return *this;
56}
57//_____________________________________________________________________________
58void AmplitudeSpectrum::fill(float amplitude)
59{
60 if ((lRange <= amplitude) && (amplitude < rRange)) {
61 int bin = ((amplitude - lRange) / (rRange - lRange)) * nBins;
62 mBinContent[bin]++;
63 mNEntries++;
64 mSumA += amplitude;
65 mSumA2 += amplitude * amplitude;
66 }
67}
68//_____________________________________________________________________________
70{
71 int rebin = nBins / h->GetNbinsX();
72 if (h != nullptr) {
73 for (int iBin = 0; iBin < h->GetNbinsX(); iBin++) {
74 float binC = 0.;
75 for (uint16_t i = iBin * rebin; i < (iBin + 1) * rebin; i++) {
76 binC += mBinContent[i];
77 }
78 h->SetBinContent(iBin + 1, binC);
79 }
80 }
81}
82int AmplitudeSpectrum::nEventsInRange(float lR, float rR)
83{
84 if (lR < lRange) {
85 lR = lRange;
86 }
87 if (rR > rRange) {
88 rR = rRange;
89 }
90 int first = ((lR - lRange) / (rRange - lRange)) * nBins;
91 int last = ((rR - lRange) / (rRange - lRange)) * nBins;
92 int nEvents = 0;
93 for (int i = first; i < last; i++) {
94 nEvents += mBinContent[i];
95 }
96 return nEvents;
97}
98//_____________________________________________________________________________
99// GainCalibData
100//_____________________________________________________________________________
101void GainCalibData::fill(const gsl::span<const Digit> digits)
102{
103 for (auto& dig : digits) {
104 mAmplitudeSpectra[dig.getAbsId()].fill(dig.getAmplitude());
105 }
106}
107//_____________________________________________________________________________
109{
110 for (int i = 0; i < Geometry::kNCHANNELS; i++) {
112 }
113 LOG(info) << "Merged GainCalibData with previous one.";
114 print();
115}
116//_____________________________________________________________________________
118{
119 int nNonEmptySpectra = 0;
120 uint64_t nTotalEntries = 0;
121 for (int i = 0; i < Geometry::kNCHANNELS; i++) {
122 if (mAmplitudeSpectra[i].getNEntries()) {
123 nNonEmptySpectra++;
124 nTotalEntries += mAmplitudeSpectra[i].getNEntries();
125 }
126 }
127 LOG(info) << "GainCalibData::print() : "
128 << "I have " << nNonEmptySpectra
129 << " non-empty amplitude spectra with " << nTotalEntries
130 << " entries total ranged in (" << AmplitudeSpectrum::lRange << "; " << AmplitudeSpectrum::rRange << ").";
131}
132//_____________________________________________________________________________
133// GainCalibrator
134//_____________________________________________________________________________
136{
137 LOG(info) << "GainCalibrator::GainCalibrator() : "
138 << "Gain calibrator created!";
139}
140//_____________________________________________________________________________
142{
143 auto& cpvParams = o2::cpv::CPVCalibParams::Instance();
144 mMinEvents = cpvParams.gainMinEvents;
145 mMinNChannelsToCalibrate = cpvParams.gainMinNChannelsToCalibrate;
146 mDesiredLandauMPV = cpvParams.gainDesiredLandauMPV;
147 mToleratedChi2PerNDF = cpvParams.gainToleratedChi2PerNDF;
148 mMinAllowedCoeff = cpvParams.gainMinAllowedCoeff;
149 mMaxAllowedCoeff = cpvParams.gainMaxAllowedCoeff;
150 mFitRangeL = cpvParams.gainFitRangeL;
151 mFitRangeR = cpvParams.gainFitRangeR;
152 mUpdateTFInterval = cpvParams.gainCheckForCalibrationInterval;
153
154 // adjust fit ranges to descrete binned values
155 if (mFitRangeL < AmplitudeSpectrum::lRange) {
156 mFitRangeL = AmplitudeSpectrum::lRange;
157 }
158 if (mFitRangeR > AmplitudeSpectrum::rRange) {
159 mFitRangeR = AmplitudeSpectrum::rRange;
160 }
162 mFitRangeL = AmplitudeSpectrum::lRange + std::floor((mFitRangeL - AmplitudeSpectrum::lRange) / binWidth) * binWidth;
163 mFitRangeR = AmplitudeSpectrum::lRange + std::floor((mFitRangeR - AmplitudeSpectrum::lRange) / binWidth) * binWidth;
164
165 LOG(info) << "GainCalibrator::configParameters() : Parameters used: ";
166 LOG(info) << "mMinEvents = " << mMinEvents;
167 LOG(info) << "mDesiredLandauMPV = " << mDesiredLandauMPV;
168 LOG(info) << "mToleratedChi2PerNDF = " << mToleratedChi2PerNDF;
169 LOG(info) << "mMinAllowedCoeff = " << mMinAllowedCoeff;
170 LOG(info) << "mMaxAllowedCoeff = " << mMaxAllowedCoeff;
171 LOG(info) << "mFitRangeL = " << mFitRangeL;
172 LOG(info) << "mFitRangeR = " << mFitRangeR;
173 LOG(info) << "mUpdateTFInterval = " << mUpdateTFInterval;
174}
175//_____________________________________________________________________________
177{
178 LOG(info) << "GainCalibrator::initOutput() : output vectors cleared";
179 mCcdbInfoGainsVec.clear();
180 mGainsVec.clear();
181 mCcdbInfoGainCalibDataVec.clear();
182 mGainCalibDataVec.clear();
183}
184//_____________________________________________________________________________
186{
187 GainCalibData* calibData = slot.getContainer();
188 LOG(info) << "GainCalibrator::finalizeSlot() : finalizing slot "
189 << slot.getTFStart() << " <= TF <= " << slot.getTFEnd();
190 slot.getContainer()->print();
191 CalibParams* newGains = new CalibParams(1);
192
193 // estimate new gains by fitting amplitude spectra
194 int nChannelsCalibrated = 0, nChannelsTooSmallCoeff = 0, nChannelsTooLargeCoeff = 0;
195 TF1* fLandau = new TF1("fLandau", "landau", mFitRangeL, mFitRangeR);
196 fLandau->SetParLimits(0, 0., 1.E6);
197 fLandau->SetParLimits(1, mDesiredLandauMPV / mMaxAllowedCoeff, mDesiredLandauMPV / mMinAllowedCoeff);
198 fLandau->SetParLimits(2, 0., 1.E3);
200 h.Rebin(std::ceil(mMaxAllowedCoeff)); // rebin histogram in order to avoid descrete structures in it
201 // double binWidth = (AmplitudeSpectrum::rRange - AmplitudeSpectrum::lRange) / AmplitudeSpectrum::nBins;
202 // size_t nBinsToFit = (mFitRangeR - mFitRangeL) / binWidth;
203 // uint32_t xMin = (mFitRangeL - AmplitudeSpectrum::lRange) / binWidth;
204 // uint32_t xMax = (mFitRangeR - AmplitudeSpectrum::lRange) / binWidth;
205 int nCalibratedChannels = 0;
206 int badChi2Channels = 0;
207 for (unsigned short i = 0; i < Geometry::kNCHANNELS; i++) {
208 newGains->setGain(i, mPreviousGains.get()->getGain(i)); // copy previous gains first
209 // print some info
210 if ((i % 500) == 0) {
211 LOG(info) << "GainCalibrator::finalizeSlot() : checking channel " << i;
212 }
213 if (slot.getContainer()->mAmplitudeSpectra[i].getNEntries() > mMinEvents) { // we are ready to fit
214 if (slot.getContainer()->mAmplitudeSpectra[i].nEventsInRange(mFitRangeL, mFitRangeR) < mMinEvents) { // actually not enough events in fit range
215 continue;
216 }
217 h.Reset();
218 slot.getContainer()->mAmplitudeSpectra[i].dumpToHisto(&h);
219 // set some starting values
220 double mean = slot.getContainer()->mAmplitudeSpectra[i].getMean();
221 double rms = slot.getContainer()->mAmplitudeSpectra[i].getRMS();
222 double startingValue = slot.getContainer()->mAmplitudeSpectra[i].getBinContent()[(int)mean];
223 fLandau->SetParameters(startingValue, mean, rms);
224 auto fitResult = h.Fit(fLandau, "SQL0N", "", mFitRangeL, mFitRangeR);
225 // auto fitResult = o2::math_utils::fit<uint32_t>(nBinsToFit, &(slot.getContainer()->mAmplitudeSpectra[i].getBinContent()[xMin]), xMin, xMax, *fLandau);
226 if (fitResult->Chi2() / fitResult->Ndf() > mToleratedChi2PerNDF) {
227 badChi2Channels++;
228 // in case of bad fit -> do something sofisticated. but what?
229 // continue;
230 if (badChi2Channels < 20) {
231 LOG(info) << "GainCalibrator::finalizeSlot() : bad chi2/ndf in fit of spectrum in channel " << i;
232 fitResult->Print("V");
233 } else if (badChi2Channels == 20) {
234 LOG(info) << "GainCalibrator::finalizeSlot() : bad chi2/ndf in fit of spectrum in channel " << i;
235 fitResult->Print("V");
236 LOG(info) << "GainCalibrator::finalizeSlot() : bad chi2/ndf is reported 20 times. Muting ";
237 }
238 }
239 // calib coeffs are defined as mDesiredLandauMPV/actualMPV*previousCoeff
240 float coeff = mDesiredLandauMPV / fLandau->GetParameter(1) * mPreviousGains.get()->getGain(i);
241 slot.getContainer()->mAmplitudeSpectra[i].reset();
242 if (mMinAllowedCoeff <= coeff && coeff <= mMaxAllowedCoeff) {
243 newGains->setGain(i, coeff);
244 nChannelsCalibrated++;
245 } else if (mMinAllowedCoeff >= coeff) {
246 newGains->setGain(i, mMinAllowedCoeff);
247 } else {
248 newGains->setGain(i, mMaxAllowedCoeff);
249 }
250 }
251 }
252 delete fLandau;
253 LOG(info) << "GainCalibrator::finalizeSlot() : succesfully calibrated " << nChannelsCalibrated << "channels";
254 LOG(info) << "GainCalibrator::finalizeSlot() : bad chi2/ndf is occured " << badChi2Channels << "times";
255 // prepare new ccdb entries for sending
256 mGainsVec.push_back(*newGains);
257 // metadata for o2::cpv::CalibParams
258 std::map<std::string, std::string> metaData;
259 auto className = o2::utils::MemFileHelper::getClassName(*newGains);
260 auto fileName = o2::ccdb::CcdbApi::generateFileName(className);
261 auto timeStamp = o2::ccdb::getCurrentTimestamp();
262 mCcdbInfoGainsVec.emplace_back("CPV/Calib/Gains", className, fileName, metaData, timeStamp, timeStamp + 31536000000); // one year validity time (in milliseconds!)
263
264 mPreviousGainCalibData.reset(new GainCalibData(*slot.getContainer())); // Slot is going to be closed and removed after finalisation -> save calib data for next Slot
265 mPreviousGains.reset(newGains); // save new obtained gains as previous ones
266}
267//_____________________________________________________________________________
269{
270 auto& cont = getSlots();
271 if (cont.empty()) {
272 LOG(warning) << "GainCalibrator::prepareForEnding() : have no TimeSlots to end. Sorry about that.";
273 return;
274 }
275 // we assume only one single slot
276 auto& slot = cont.back();
277 LOG(info) << "GainCalibrator::prepareForEnding() : sending GainCalibData from slot("
278 << slot.getTFStart() << " <= TF <= " << slot.getTFEnd() << ") to CCDB";
279 slot.getContainer()->print();
280 // metadata for o2::cpv::GainCalibData
281 std::map<std::string, std::string> metaData;
282 mGainCalibDataVec.push_back(*(slot.getContainer()));
283 auto className = o2::utils::MemFileHelper::getClassName(*(slot.getContainer()));
284 auto fileName = o2::ccdb::CcdbApi::generateFileName(className);
285 auto timeStamp = o2::ccdb::getCurrentTimestamp();
286 mCcdbInfoGainCalibDataVec.emplace_back("CPV/PhysicsRun/GainCalibData", className, fileName, metaData, timeStamp, timeStamp + 604800000); // 1 week validity time (in milliseconds!)
287}
288//_____________________________________________________________________________
290{
291 LOG(info) << "GainCalibrator::emplaceNewSlot() : emplacing new Slot from tstart = " << tstart << " to " << tend;
292 auto& cont = getSlots();
293 auto& slot = front ? cont.emplace_front(tstart, tend) : cont.emplace_back(tstart, tend);
294 slot.setContainer(std::make_unique<GainCalibData>());
295 slot.getContainer()->merge(mPreviousGainCalibData.get());
296 return slot;
297}
298//_____________________________________________________________________________
300{
301 if (mCurrentTFInfo.tfCounter % mUpdateTFInterval != 0) {
302 return false; // check once per N TFs if enough statistics
303 }
304 int nChannelsToCalibrate = 0;
305 for (int i = 0; i < Geometry::kNCHANNELS; i++) {
306 if (slot.getContainer()->mAmplitudeSpectra[i].getNEntries() > mMinEvents) {
307 nChannelsToCalibrate++;
308 }
309 }
310 if (nChannelsToCalibrate >= mMinNChannelsToCalibrate) {
311 LOG(info) << "GainCalibrator::hasEnoughtData() : slot "
312 << slot.getTFStart() << " <= TF <= " << slot.getTFEnd() << " is ready for calibration ("
313 << nChannelsToCalibrate << " are going to be calibrated).";
314 return true;
315 } else {
316 LOG(info) << "GainCalibrator::hasEnoughtData() : slot "
317 << slot.getTFStart() << " <= TF <= " << slot.getTFEnd() << " is not ready for calibration ("
318 << nChannelsToCalibrate << " channels to be calibrated wich is not enough).";
319 }
320 return false;
321}
322//_____________________________________________________________________________
323} // end namespace cpv
324} // end namespace o2
Utils and constants for calibration and related workflows.
int32_t i
Class for time synchronization of RawReader instances.
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 float lRange
static constexpr float rRange
int nEventsInRange(float lR, float rR)
void fill(float amplitude)
AmplitudeSpectrum & operator+=(const AmplitudeSpectrum &rhs)
static constexpr uint16_t nBins
void setGain(unsigned short cellID, float c)
Set High Gain energy calibration coefficient.
Definition CalibParams.h:54
std::array< AmplitudeSpectrum, Geometry::kNCHANNELS > mAmplitudeSpectra
void fill(const gsl::span< const Digit > data)
void merge(const GainCalibData *prev)
void finalizeSlot(GainTimeSlot &slot) final
bool hasEnoughData(const GainTimeSlot &slot) const final
GainTimeSlot & emplaceNewSlot(bool front, TFType tstart, TFType tend) final
static constexpr short kNCHANNELS
Definition Geometry.h:30
long getCurrentTimestamp()
returns the timestamp in long corresponding to "now"
o2::calibration::TimeSlot< GainCalibData > GainTimeSlot
a couple of static helper functions to create timestamp values for CCDB queries or override obsolete ...
static std::string getClassName(const T &obj)
get the class name of the object
const int nEvents
Definition test_Fifo.cxx:27
LOG(info)<< "Compressed in "<< sw.CpuTime()<< " s"
std::vector< Digit > digits