StickyCodeMetrics.cxx
Go to the documentation of this file.
1 // StickyCodeMetrics.cxx
2 
3 #include "StickyCodeMetrics.h"
4 #include <map>
5 #include <iostream>
6 #include <sstream>
7 #include <vector>
8 
9 #include "TH1F.h"
10 #include "TF1.h"
11 #include "TList.h"
12 //#include "TFitResult.h"
13 
14 using std::map;
15 using std::string;
16 using std::cout;
17 using std::endl;
18 using std::ostringstream;
19 using std::vector;
20 
21 namespace {
22 using Index = unsigned int;
23 }
24 
25 //**********************************************************************
26 
28 StickyCodeMetrics(Name hnam, Name httl, Index nbin, Index lowbin,
29  float sigmaMin, float sigmaMax)
30 : m_hnam(hnam), m_httl(httl), m_nbin(nbin), m_lowbin(lowbin),
31  m_sigmaMin(sigmaMin), m_sigmaMax(sigmaMax) { }
32 
33 //**********************************************************************
34 
36  m_counts = counts;
37  return evaluateMetrics();
38 }
39 
40 //**********************************************************************
41 
43  m_counts.clear();
44  for ( AdcCount adc : adcs ) {
45  if ( m_counts.find(adc) == m_counts.end() ) m_counts[adc] = 0;
46  ++m_counts[adc];
47  }
48  return evaluateMetrics();
49 }
50 
51 //**********************************************************************
52 
53 int StickyCodeMetrics::evaluate(const TH1* pha) {
54  const string myname = "StickyCodeMetrics::evaluate: ";
55  Index nbin = pha->GetNbinsX();
56  Index iadc0 = pha->GetBinLowEdge(1);
57  Index iadcLast = pha->GetBinLowEdge(nbin);
58  Index nhadc = iadcLast - iadc0 + 1;
59  m_counts.clear();
60  if ( nhadc != nbin ) {
61  cout << myname << "Histogram " << pha->GetName() << " has inconsistent binning." << endl;
62  } else {
63  for ( Index ibin=0; ibin<nbin; ++ibin ) {
64  double count = pha->GetBinContent(ibin+1);
65  if ( count != 0.0 ) m_counts[iadc0+ibin] = count;
66  }
67  }
68  return evaluateMetrics();
69 }
70 
71 //**********************************************************************
72 
74  m_counts.clear();
76 }
77 
78 //**********************************************************************
79 
81  const string myname = "StickyCodeMetrics::evaluateMetrics: ";
82  const int dbg = 0; // Nonzero give debug logging.
83  if ( dbg ) cout << myname << "Debugging level: " << dbg << endl;
84  const BinCounter& counts = m_counts;
85  m_nsample = 0;
86  Index badadc = 999999;
87  Index maxadc = badadc;
88  Index maxCount = 0;
89  Index maxCount2 = 0;
90  Index nmod0 = 0;
91  Index nmod1 = 0;
92  Index nmod63 = 0;
93  double adcSum = 0.0;
94  double countSum = 0.0;
95  for ( BinCounter::value_type ibco : counts ) {
96  Index iadc = ibco.first;
97  double count = ibco.second;
98  m_nsample += count;
99  if ( count > maxCount ) {
100  maxCount = count;
101  maxadc = iadc;
102  }
103  AdcCount adcmod = iadc%64;
104  if ( adcmod == 0 ) nmod0 += count;
105  if ( adcmod == 1 ) nmod1 += count;
106  if ( adcmod == 63 ) nmod63 += count;
107  countSum += count;
108  adcSum += count*iadc;
109  }
110  double countSum2 = 0;
111  double adcSum2 = 0;
112  Index maxadc2 = badadc;
113  for ( BinCounter::value_type ibco : counts ) {
114  Index iadc = ibco.first;
115  double count = ibco.second;
116  if ( iadc != maxadc ) {
117  countSum2 += count;
118  adcSum2 += count*iadc;
119  if ( count > maxCount2 ) {
120  maxCount2 = count;
121  maxadc2 = iadc;
122  }
123  }
124  }
125  m_maxAdc = maxadc;
126  m_maxAdc2 = maxadc2;
127  m_meanAdc = countSum>0 ? adcSum/countSum : -1.0;
128  m_meanAdc2 = countSum2>0 ? adcSum2/countSum2 : -1.0;
129  m_maxFraction = maxCount/countSum;
130  m_zeroFraction = nmod0/countSum;
131  m_oneFraction = nmod1/countSum;
132  m_highFraction = nmod63/countSum;
133  // Create the histogram if configured.
134  if ( counts.size() == 0 ) return 0;
135  if ( m_hnam.size() == 0 ) return 0;
136  if ( m_nbin == 0 ) return 1;
137  if ( m_lowbin == 0 ) return 2;
138  Index nbinHist = m_nbin; // # bins in the histogram
139  // Create vectors with counts and rebinned counts.
140  // binCounts[ibin] holds the count for iadc = binOffset + ibin
141  // rebinCounts[ireb] holds the count sum for nrebin bins starting at
142  // iadc = binOffset + ireb*rebin
143  // Not use of fabs in case someone passes negative values.
144  Index idadc1 = counts.begin()->first;
145  Index idadc2 = counts.rbegin()->first;
146  Index nrebin = m_lowbin;
147  vector<double> binCounts;
148  // Pad the count vector with half the histo range so we can center the
149  // data in the histogram.
150  Index halfHist = nbinHist/2;
151  Index iadc1 = idadc1 < halfHist ? 0 : idadc1 - halfHist;
152  iadc1 = nrebin*(iadc1/nrebin);
153  Index binOffset = iadc1;
154  Index iadc2= idadc2 + halfHist;
155  if ( iadc2 > 4096 && idadc2 < 4096 ) iadc2 = 4096;
156  countSum = 0.0;
157  for ( Index iadc=iadc1; iadc<=iadc2; ++iadc ) {
158  BinCounter::const_iterator icnt = counts.find(iadc);
159  double count = icnt != counts.end() ? fabs(icnt->second) : 0;
160  countSum += count;
161  binCounts.push_back(count);
162  }
163  Index nbin = binCounts.size();
164  Index nreb = (nbin+nrebin-1)/nrebin;
165  vector<double> rebinCounts(nreb, 0.0);
166  vector<double> rebinAdcCounts(nreb, 0.0);
167  for ( Index ibin=0; ibin<nbin; ++ibin ) {
168  double count = binCounts[ibin];
169  Index iadc = binOffset + ibin;
170  if ( count > 0 ) {
171  Index ireb = ibin/nrebin;
172  rebinCounts[ireb] += count;
173  rebinAdcCounts[ireb] += iadc*count;
174  }
175  }
176  if ( dbg >= 2 ) {
177  cout << myname << " Data ADC range: [" << idadc1 << ", " << idadc2 << "]" << endl;
178  cout << myname << " Bin offset: " << binOffset << endl;
179  cout << myname << " Data bin count: " << nbin << endl;
180  cout << myname << " Data rebin count: " << rebinCounts.size() << endl;
181  cout << myname << " Hist bin count: " << nbinHist << endl;
182  }
183  // Initialize the histogram under and overflows.
184  double countUnder = 0.0;
185  double countOver = 0.0;
186  // Evaluate the hist ADC range [ihadc1, ihadc2)
187  // First check if all data fit in histogram.
188  Index ihadc1 = binOffset;
189  while ( ihadc1 + nrebin <= idadc1 ) ihadc1 += nrebin;
190  Index ihadc2 = ihadc1 + nbinHist;
191  // Data fits in histogram range.
192  if ( ihadc2 > idadc2 ) {
193  Index nbinLeft = idadc1 - ihadc1;
194  Index nbinRight = ihadc2 - idadc2 - 1;
195  if ( dbg >= 3 ) cout << myname << "All data are in histogram range." << endl;
196  if ( dbg >= 4 ) cout << myname << "nl, nr, hleft: " << nbinLeft << ", "
197  << nbinRight << ", " << ihadc1 << endl;
198  while ( nbinRight > nbinLeft + nrebin ) {
199  ihadc1 -= nrebin;
200  ihadc2 -= nrebin;
201  nbinRight -= nrebin;
202  nbinLeft += nrebin;
203  if ( dbg >= 4 ) cout << myname << "nl, nr, hleft: " << nbinLeft << ", "
204  << nbinRight << ", " << ihadc1 << endl;
205  if ( ihadc1 < nrebin ) break;
206  }
207  // Data extends beyond histogram range.
208  } else {
209  ihadc1 = binOffset;
210  ihadc2 = ihadc1 + nbinHist;
211  if ( dbg >= 3 ) cout << myname << "Some data are outside histogram range." << endl;
212  // Preferred ADC count for the center of the histogram.
213  double adcHistCenter = maxAdc();
214  if ( fabs(maxAdc2() - maxAdc()) > 0.1*nbinHist ) {
215  adcHistCenter = 0.5*(maxAdc2() + maxAdc());
216  }
217  if ( dbg >= 3 ) {
218  cout << myname << " max ADC: " << maxAdc() << endl;
219  cout << myname << " max ADC2: " << maxAdc() << endl;
220  cout << myname << " mean ADC: " << meanAdc() << endl;
221  cout << myname << " Preferred hist center: " << adcHistCenter << endl;
222  }
223  // Get the total counts in the starting histogram range.
224  double countSumHist = 0.0;
225  double adcSumHist = 0.0;
226  for ( Index iadc=ihadc1; iadc<ihadc2; ++iadc ) {
227  Index ibin = iadc - binOffset;
228  countSumHist += binCounts[ibin];
229  adcSumHist += binCounts[ibin]*iadc;
230  }
231  // Find the value of ihadc1 that gives the maximum area.
232  // If areas are similar, center the distribution.
233  Index ihadc1Selected = ihadc1;
234  double countSumHistSelected = countSumHist;
235  double hmean = countSumHist == 0.0 ? 0.0 : adcSumHist/countSumHist;
236  double hmeanSelected = hmean;
237  if ( dbg >= 4 ) cout << " hleft, hmean, area: " << ihadc1 << ", " << hmean
238  << ", " << countSumHist << endl;
239  while ( ihadc1 <= idadc2 ) {
240  // Add the new area to the right.
241  for ( Index iadc=ihadc2; iadc<ihadc2+nrebin; ++iadc ) {
242  if ( iadc > idadc2 ) break;
243  Index ibin = iadc - binOffset;
244  countSumHist += binCounts[ibin];
245  adcSumHist += iadc*binCounts[ibin];
246  }
247  // Subtract the area to the left.
248  Index ireb = (ihadc1-binOffset)/nrebin;
249  if ( dbg >= 4 ) cout << " ireb/nreb: " << ireb << "/" << nrebin << endl;
250  countSumHist -= rebinCounts[ireb];
251  adcSumHist -= rebinAdcCounts[ireb];
252  hmean = countSumHist == 0.0 ? 0.0 : adcSumHist/countSumHist;
253  // Update the range.
254  ihadc1 += nrebin;
255  ihadc2 += nrebin;
256  if ( dbg >= 4 ) cout << " hleft, hmean, area: " << ihadc1 << ", " << hmean
257  << ", " << countSumHist << endl;
258  // Update the histogram range if this area is bigger or
259  // if it is about the same and is closer to the mean.
260  double diffCount = countSumHist - countSumHistSelected;
261  bool sameCount = diffCount == 0.0;
262  if ( ! sameCount ) {
263  double rat = diffCount/(countSumHist + countSumHistSelected);
264  sameCount = fabs(rat) < 0.001;
265  }
266  if ( ihadc1Selected > ihadc1 ) {
267  cout << myname << "Unexpected value for ihadc1Selected: " << ihadc1Selected << endl;
268  cout << myname << " with ihadc1: " << ihadc1 << endl;
269  abort();
270  }
271  bool haveOverlap = ihadc1Selected + nbinHist > ihadc1;
272  bool updateSel = false;
273  if ( sameCount && haveOverlap ) {
274  if ( dbg >= 4 ) cout << " Same count." << endl;
275  if ( true ) {
276  double diffOld = fabs(ihadc1Selected + nbinHist/2 - hmeanSelected);
277  double diffNew = fabs(ihadc1 + nbinHist/2 - hmean);
278  updateSel = diffNew < diffOld;
279  } else {
280  double diffOld = fabs(ihadc1Selected + nbinHist/2 - adcHistCenter);
281  double diffNew = fabs(ihadc1 + nbinHist/2 - adcHistCenter);
282  updateSel = diffNew < diffOld;
283  }
284  } else {
285  updateSel = diffCount > 0.0;
286  }
287  if ( updateSel ) {
288  countSumHistSelected = countSumHist;
289  hmeanSelected = hmean;
290  ihadc1Selected = ihadc1;
291  if ( dbg >= 4 ) cout << " Updated selected hleft, hmean to " << ihadc1Selected
292  << ", " << hmeanSelected << endl;
293  }
294  }
295  ihadc1 = ihadc1Selected;
296  ihadc2 = ihadc1 + nbinHist;
297  // Get the under and overflows.
298  for ( BinCounter::value_type icnt : counts ) {
299  Index iadc = icnt.first;
300  double count = icnt.second;
301  if ( iadc < ihadc1 ) countUnder += count;
302  if ( iadc >= ihadc2 ) countOver += count;
303  }
304  }
305  // Create histogram.
306  TH1* ph = new TH1F(m_hnam.c_str(), m_httl.c_str(), nbinHist, ihadc1, ihadc2);
307  string::size_type iposx = m_httl.find(";");
308  bool haveXlab = iposx != string::npos;
309  bool haveYlab = haveXlab && m_httl.find(";", iposx+1) != string::npos;
310  if ( ! haveXlab ) ph->GetXaxis()->SetTitle("ADC count");
311  if ( ! haveYlab ) ph->GetYaxis()->SetTitle("# samples");
312  ph->SetDirectory(0);
313  ph->Sumw2(); // Likelihood fit wants histogram to be weighted
314  ph->SetStats(0);
315  ph->SetLineWidth(2);
316  for ( Index iadc=binOffset; iadc<=idadc2; ++iadc ) {
317  BinCounter::const_iterator icnt = counts.find(iadc);
318  Index count = icnt != counts.end() ? fabs(icnt->second) : 0;
319  ph->SetBinContent(iadc+1-ihadc1, count);
320  }
321  ph->SetBinContent(0, countUnder);
322  ph->SetBinContent(nbinHist+1, countOver);
323  m_ph.reset(ph);
324  // Fit the histogram.
325  TF1 fitter("pedgaus", "gaus", ihadc1, ihadc2, TF1::EAddToList::kNo);
326  fitter.SetParameters(0.1*ph->Integral(), ph->GetMean(), 5.0);
327  if ( m_sigmaMax > m_sigmaMin ) {
328  fitter.SetParLimits(2, m_sigmaMin, m_sigmaMax);
329  } else if ( m_sigmaMax == m_sigmaMin ) {
330  fitter.FixParameter(2, m_sigmaMin);
331  } else {
332  fitter.SetParLimits(2, 0.1, 100.0);
333  }
334  TF1* pfinit = dynamic_cast<TF1*>(fitter.Clone("pedgaus0"));
335  pfinit->SetLineColor(3);
336  pfinit->SetLineStyle(2);
337  string fopt = "0";
338  fopt = "WWBQ";
339  fopt = "LWBQ"; // Use likelihood fit to include empty bins
340  // Block Root info message for new Canvas produced in fit.
341  int levelSave = gErrorIgnoreLevel;
342  gErrorIgnoreLevel = 1001;
343  gErrorIgnoreLevel = 2001; // Block warnings in Fit
344  // Block non-default (e.g. art) from handling the Root "error".
345  // We switch to the Root default handler while making the call to Print.
346  ErrorHandlerFunc_t pehSave = nullptr;
347  ErrorHandlerFunc_t pehDefault = DefaultErrorHandler;
348  if ( GetErrorHandler() != pehDefault ) {
349  pehSave = SetErrorHandler(pehDefault);
350  }
351  //TFitResultPtr pres = ph->Fit(&fitter, fopt.c_str());
352  //m_fitStatus = pres->Status();
353  m_fitStatus = ph->Fit(&fitter, fopt.c_str());
354  if ( pehSave != nullptr ) SetErrorHandler(pehSave);
355  gErrorIgnoreLevel = levelSave;
356  ph->GetListOfFunctions()->AddLast(pfinit, "0");
357  ph->GetListOfFunctions()->Last()->SetBit(TF1::kNotDraw, true);
358  m_fitMean = fitter.GetParameter(1);
359  m_fitSigma = fitter.GetParameter(2);
360  m_fitExcess = (maxCount - fitter.Eval(maxAdc()))/countSum;
361  return 0;
362 }
363 
364 //**********************************************************************
365 
367  DataMap res;
368  res.setInt( prefix + "MaxAdc", maxAdc());
369  res.setInt( prefix + "MaxAdc2", maxAdc2());
370  res.setFloat(prefix + "MeanAdc", meanAdc());
371  res.setFloat(prefix + "MeanAdc2", meanAdc2());
372  res.setFloat(prefix + "MaxFraction", maxFraction());
373  res.setFloat(prefix + "ZeroFraction", zeroFraction());
374  res.setFloat(prefix + "OneFraction", oneFraction());
375  res.setFloat(prefix + "HighFraction", highFraction());
376  res.setFloat(prefix + "ClassicFraction", classicFraction());
377  res.setInt(prefix + "FitStatus", fitStatus());
378  res.setFloat(prefix + "FitMean", fitMean());
379  res.setFloat(prefix + "FitSigma", fitSigma());
380  res.setFloat(prefix + "FitExcess", fitExcess());
381  return res;
382 }
383 
384 //**********************************************************************
385 
386 void StickyCodeMetrics::print(string prefix) const {
387  ostringstream sout;
388  sout.precision(2);
389  sout << prefix << " # samples: " << nsample();
390  sout << "\n";
391  sout << prefix << " Most common ADC code: " << maxAdc();
392  sout << "\n";
393  sout << prefix << " Next most common code: " << maxAdc2();
394  sout << "\n";
395  sout << prefix << " Mean ADC: " << std::fixed << meanAdc();
396  sout << "\n";
397  sout << prefix << " Mean ADC w/o max: " << std::fixed << meanAdc2();
398  sout << "\n";
399  sout.precision(3);
400  sout << prefix << " Frac in max bin: " << maxFraction();
401  sout << "\n";
402  sout << prefix << " Frac LSB=0: " << zeroFraction();
403  sout << "\n";
404  sout << prefix << " Frac LSB=1: " << oneFraction();
405  sout << "\n";
406  sout << prefix << " Frac LSB=64: " << highFraction();
407  sout << "\n";
408  sout << prefix << " Frac LSB=0,64: " << classicFraction();
409  sout << "\n";
410  sout << prefix << " Fit status: " << fitStatus();
411  sout << "\n";
412  sout << prefix << " Fit mean: " << fitMean();
413  sout << "\n";
414  sout << prefix << " Fit sigma: " << fitSigma();
415  sout << "\n";
416  sout << prefix << " Fit excess: " << fitExcess();
417  cout << sout.str() << endl;
418 }
419 
420 //**********************************************************************
std::vector< AdcCount > AdcCountVector
Definition: AdcTypes.h:19
AdcIndex maxAdc2() const
DataMap getMetrics(std::string prefix="scm") const
double meanAdc() const
void setFloat(Name name, float val)
Definition: DataMap.h:133
bool dbg
std::string string
Definition: nybbler.cc:12
struct vector vector
int16_t adc
Definition: CRTFragment.hh:202
intermediate_table::const_iterator const_iterator
unsigned int Index
int fitStatus() const
double fitExcess() const
counts_as<> counts
Number of ADC counts, represented by signed short int.
Definition: electronics.h:116
size_t size
Definition: lodepng.cpp:55
void setInt(Name name, int val)
Definition: DataMap.h:131
double fitMean() const
double oneFraction() const
double classicFraction() const
Index nsample() const
AdcIndex maxAdc() const
std::map< Index, double > BinCounter
double highFraction() const
double maxFraction() const
double fitSigma() const
double zeroFraction() const
short AdcCount
Definition: AdcTypes.h:18
double meanAdc2() const
void print(std::string prefix="") const
StickyCodeMetrics()=default
int evaluate(const BinCounter &counts)
QTextStream & endl(QTextStream &s)