Belle II Software  release-08-01-10
TOPTimeBaseCalibratorModule.cc
1 /**************************************************************************
2  * basf2 (Belle II Analysis Software Framework) *
3  * Author: The Belle II Collaboration *
4  * *
5  * See git log for contributors and copyright holders. *
6  * This file is licensed under LGPL-3.0, see LICENSE.md. *
7  **************************************************************************/
8 
9 // Own header.
10 #include <top/modules/TOPTimeBaseCalibrator/TOPTimeBaseCalibratorModule.h>
11 
12 // TOP headers.
13 #include <top/geometry/TOPGeometryPar.h>
14 
15 // framework - DataStore
16 #include <framework/datastore/StoreArray.h>
17 
18 // framework aux
19 #include <framework/logging/Logger.h>
20 
21 // DataStore classes
22 #include <top/dataobjects/TOPDigit.h>
23 #include <top/dataobjects/TOPRawDigit.h>
24 #include <top/dbobjects/TOPSampleTimes.h>
25 
26 // Root
27 #include "TFile.h"
28 #include "TProfile.h"
29 #include <TSystem.h>
30 #include <TMatrixDSym.h>
31 
32 #include <iostream>
33 
34 using namespace std;
35 
36 namespace Belle2 {
42  using namespace TOP;
43 
44  //-----------------------------------------------------------------
46  //-----------------------------------------------------------------
47 
48  REG_MODULE(TOPTimeBaseCalibrator);
49 
50  //-----------------------------------------------------------------
51  // Implementation
52  //-----------------------------------------------------------------
53 
54  TOPTimeBaseCalibratorModule::TOPTimeBaseCalibratorModule() : Module()
55 
56  {
57  // set module description
58  setDescription("Sample time calibrator");
59  // setPropertyFlags(c_ParallelProcessingCertified | c_InitializeInProcess);
60 
61  // Add parameters
62  addParam("moduleID", m_moduleID, "slot ID to calibrate.");
63  addParam("minTimeDiff", m_minTimeDiff,
64  "lower bound on time difference [samples].", 0.0);
65  addParam("maxTimeDiff", m_maxTimeDiff,
66  "upper bound on time difference [samples].", 100.0);
67  addParam("directoryName", m_directoryName,
68  "name (with path) of the directory for the output root files.",
69  string(""));
70  addParam("minHits", m_minHits,
71  "minimal required hits per channel.", (unsigned) 1000);
72  addParam("numIterations", m_numIterations,
73  "number of iTBC iterations", (unsigned) 100);
74  addParam("numConvIter", m_conv_iter,
75  "Max number of iTBC iterations for conversion.", (unsigned) 50);
76  addParam("minChi2Change", m_dchi2_min,
77  "Minimal of chi2 change in iterations.", 0.2);
78  addParam("dtMin", m_dt_min,
79  "minimum Delta T of raw calpulse in iTBC", 20.0);
80  addParam("dtMax", m_dt_max,
81  "maximum Delta T of raw calpulse in iTBC", 24.0);
82  addParam("xStep", m_xstep,
83  "unit for an interation of delta(X_s)", 0.020);
84  addParam("devStep", m_dev_step,
85  "a step size to calculate the value of d(chisq)/dxval", 0.001);
86  addParam("chgStep", m_change_xstep,
87  "update m_xstep if m_dchi2dxv < m_change_step", 0.015);
88  addParam("newStep", m_new_xstep,
89  "a new step for delta(X_s) if m_dchi2dxv < m_change_step", 2.0 * m_xstep);
90  addParam("sigm2_exp", m_sigm2_exp,
91  "(sigma_0(dT))**2 for nomarlization of chisq = sum{dT^2/sigma^2}", 0.0424 * 0.0424);
92 
93  addParam("method", m_method, "method: 0 - profile histograms only, "
94  "1 - matrix inversion, 2 - iterative, "
95  "3 - matrix inversion w/ singular value decomposition.", (unsigned) 1);
96  addParam("useFallingEdge", m_useFallingEdge,
97  "if true, use cal pulse falling edge instead of rising edge", false);
98  addParam("saveMatrix", m_saveMatrix, "if true, save also matrix and its inverse in a root file", false);
99 
100  }
101 
103  {
104  }
105 
107  {
108 
109  // input
110  m_digits.isRequired();
111 
112  // checks
113  const auto* geo = TOPGeometryPar::Instance()->getGeometry();
114  if (!geo->isModuleIDValid(m_moduleID))
115  B2ERROR("Invalid module ID: " << m_moduleID);
116 
117  // check for existance and mkdir if not
118  if (m_directoryName.empty()) m_directoryName = "./";
119  if (m_directoryName != "./") gSystem->mkdir(m_directoryName.c_str(), kTRUE);
120 
121  // synchronization time corresponding to two ASIC windows
122  m_syncTimeBase = geo->getNominalTDC().getSyncTimeBase();
123 
124  // control histograms
125  m_goodHits = TH2F("goodHits", "pulse height vs. pulse width of all good hits",
126  100, 0, 10, 100, 0, 2000);
127  m_goodHits.SetXTitle("pulse width (FWHM) [ns]");
128  m_goodHits.SetYTitle("pulse height [ADC counts]");
129  m_calPulseFirst = TH2F("calPulseFirst",
130  "pulse height vs. pulse width of the first calibration pulse",
131  100, 0, 10, 100, 0, 2000);
132  m_calPulseFirst.SetXTitle("pulse width (FWHM) [ns]");
133  m_calPulseFirst.SetYTitle("pulse height [ADC counts]");
134  m_calPulseSecond = TH2F("calPulseSecond",
135  "pulse height vs. pulse width of the second calibration pulse",
136  100, 0, 10, 100, 0, 2000);
137  m_calPulseSecond.SetXTitle("pulse width (FWHM) [ns]");
138  m_calPulseSecond.SetYTitle("pulse height [ADC counts]");
139 
140  }
141 
142 
144  {
145  }
146 
148  {
149  const auto* geo = TOPGeometryPar::Instance()->getGeometry();
150  double sampleWidth = geo->getNominalTDC().getSampleWidth();
151 
152  vector<Hit> hits[c_NumChannels];
153 
154  for (const auto& digit : m_digits) {
155  if (digit.getModuleID() != m_moduleID) continue;
156  if (digit.getHitQuality() != TOPDigit::c_Junk) {
157  m_goodHits.Fill(digit.getPulseWidth(), digit.getPulseHeight());
158  }
159  if (digit.getHitQuality() != TOPDigit::c_CalPulse) continue;
160  double rawTime = digit.getRawTime();
161  double errScaleFactor = 1;
162  if (m_useFallingEdge) {
163  const auto* rawDigit = digit.getRelated<TOPRawDigit>();
164  if (!rawDigit) {
165  B2ERROR("No relation to TOPRawDigit - can't determine falling edge time error");
166  continue;
167  }
168  // rawTime may include corrections due to window number discontinuity,
169  // therefore one must add the width and not just use getCFDFallingTime()
170  rawTime += rawDigit->getFWHM();
171  errScaleFactor = rawDigit->getCFDFallingTimeError(1.0) / rawDigit->getCFDLeadingTimeError(1.0);
172  }
173  double t = rawTime + digit.getFirstWindow() * c_WindowSize;
174  if (t < 0) {
175  B2ERROR("Got negative sample number - digit ignored");
176  continue;
177  }
178  double et = digit.getTimeError() / sampleWidth * errScaleFactor;
179  if (et <= 0) {
180  B2ERROR("Time error is not given - digit ignored");
181  continue;
182  }
183  unsigned channel = digit.getChannel();
184  if (channel < c_NumChannels) {
185  hits[channel].push_back(Hit(t, et, digit.getPulseHeight(), digit.getPulseWidth()));
186  }
187  }
188 
189  for (unsigned channel = 0; channel < c_NumChannels; channel++) {
190  const auto& channelHits = hits[channel];
191  if (channelHits.size() == 2) {
192  double t0 = channelHits[0].time;
193  double t1 = channelHits[1].time;
194  auto diff = fabs(t0 - t1); // since not sorted yet
195  if (diff < m_minTimeDiff) continue;
196  if (diff > m_maxTimeDiff) continue;
197  double sig0 = channelHits[0].timeErr;
198  double sig1 = channelHits[1].timeErr;
199  double sigma = sqrt(sig0 * sig0 + sig1 * sig1);
200  m_ntuples[channel].push_back(TwoTimes(t0, t1, sigma));
201  if (t0 < t1) { // check, since not sorted yet
202  m_calPulseFirst.Fill(channelHits[0].pulseWidth, channelHits[0].pulseHeight);
203  m_calPulseSecond.Fill(channelHits[1].pulseWidth, channelHits[1].pulseHeight);
204  } else {
205  m_calPulseSecond.Fill(channelHits[0].pulseWidth, channelHits[0].pulseHeight);
206  m_calPulseFirst.Fill(channelHits[1].pulseWidth, channelHits[1].pulseHeight);
207  }
208  } else if (channelHits.size() > 2) {
209  B2WARNING("More than two cal pulses per channel found - ignored");
210  }
211  }
212 
213  }
214 
215 
217  {
218  }
219 
221  {
222 
223  const auto& feMapper = TOPGeometryPar::Instance()->getFrontEndMapper();
224 
225  for (unsigned bs = 0; bs < c_NumBoardstacks; bs ++) {
226 
227  // determine scrod ID
228 
229  const auto* feMap = feMapper.getMap(m_moduleID, bs);
230  if (!feMap) {
231  B2ERROR("No front-end map available for slot " << m_moduleID << ", BS" << bs);
232  continue;
233  }
234  unsigned scrodID = feMap->getScrodID();
235 
236  // open output root file
237 
238  string fileName = m_directoryName + "/tbcSlot";
239  if (m_moduleID < 10) {
240  fileName += "0" + to_string(m_moduleID);
241  } else {
242  fileName += to_string(m_moduleID);
243  }
244  fileName += "_" + to_string(bs) + "-scrod" +
245  to_string(scrodID) + ".root";
246  TFile* fout = TFile::Open(fileName.c_str(), "recreate");
247  if (!fout) {
248  B2ERROR("Can't open the output file " << fileName);
249  continue;
250  }
251 
252  // write control histograms
253 
254  m_goodHits.Write();
255  m_calPulseFirst.Write();
256  m_calPulseSecond.Write();
257 
258  B2INFO("Fitting time base corrections for SCROD " << scrodID
259  << ", output file: " << fileName);
260 
261  // book histograms
262 
263  TH1F Hchan("channels", "channels with cal pulse",
264  c_NumScrodChannels, 0, c_NumScrodChannels);
265  Hchan.SetXTitle("channel number");
266  Hchan.SetYTitle("double cal pulse counts");
267  TH1F Hsuccess("success", "successfuly fitted channels",
268  c_NumScrodChannels, 0, c_NumScrodChannels);
269  Hsuccess.SetXTitle("channel number");
270  TH1F Hchi2("chi2", "normalized chi2",
271  c_NumScrodChannels, 0, c_NumScrodChannels);
272  Hchi2.SetXTitle("channel number");
273  Hchi2.SetYTitle("chi2/ndf");
274  TH1F Hndf("ndf", "degrees of freedom",
275  c_NumScrodChannels, 0, c_NumScrodChannels);
276  Hndf.SetXTitle("channel number");
277  Hndf.SetYTitle("ndf");
278  TH1F HDeltaT("DeltaT", "Fitted double pulse delay time",
279  c_NumScrodChannels, 0, c_NumScrodChannels);
280  HDeltaT.SetXTitle("channel number");
281  HDeltaT.SetYTitle("#Delta T [ns]");
282 
283  // loop over channels of a single SCROD
284 
285  for (unsigned chan = 0; chan < c_NumScrodChannels; chan++) {
286  unsigned channel = chan + bs * c_NumScrodChannels;
287  auto& ntuple = m_ntuples[channel];
288  Hchan.SetBinContent(chan + 1, ntuple.size());
289  if (ntuple.size() > m_minHits) {
290  bool ok = calibrateChannel(ntuple, scrodID, chan, Hchi2, Hndf, HDeltaT);
291  if (ok) Hsuccess.Fill(chan);
292  } else {
293  B2INFO("... channel " << chan << " statistics too low ("
294  << ntuple.size() << " double cal pulses)");
295  }
296  }
297 
298  // write histograms and close the file
299 
300  Hchan.Write();
301  Hsuccess.Write();
302  Hchi2.Write();
303  Hndf.Write();
304  HDeltaT.Write();
305  fout->Close();
306 
307  }
308  }
309 
310 
311  bool TOPTimeBaseCalibratorModule::calibrateChannel(vector<TwoTimes>& ntuple,
312  unsigned scrodID, unsigned chan,
313  TH1F& Hchi2, TH1F& Hndf,
314  TH1F& HDeltaT)
315  {
316 
317  // determine outlayer removal cuts
318 
319  string name = "timeDiff_ch" + to_string(chan);
320  string forWhat = "scrod " + to_string(scrodID) + " channel " + to_string(chan);
321  string title = "Cal pulse time difference vs. sample for " + forWhat;
322  TProfile prof(name.c_str(), title.c_str(), c_TimeAxisSize, 0, c_TimeAxisSize,
324  prof.SetXTitle("sample number");
325  prof.SetYTitle("time difference [samples]");
326 
327  name = "sampleOccup_ch" + to_string(chan);
328  title = "Occupancy for " + forWhat;
329  TH1F hist(name.c_str(), title.c_str(), c_TimeAxisSize, 0, c_TimeAxisSize);
330  hist.SetXTitle("sample number");
331  hist.SetYTitle("entries per sample");
332 
333  vector<double> profMeans(c_TimeAxisSize, (m_maxTimeDiff + m_minTimeDiff) / 2);
334  double sigma = (m_maxTimeDiff - m_minTimeDiff) / 6;
335 
336  for (int iter = 0; iter < 5; iter++) {
337  prof.Reset();
338  double x = 0;
339  double x2 = 0;
340  int n = 0;
341  for (auto& twoTimes : ntuple) {
342  int sample = int(twoTimes.t1) % c_TimeAxisSize;
343  double diff = twoTimes.t2 - twoTimes.t1;
344  double Ddiff = diff - profMeans[sample];
345  if (fabs(Ddiff) < 3 * sigma) {
346  prof.Fill(sample, diff);
347  hist.Fill(sample);
348  x += Ddiff;
349  x2 += Ddiff * Ddiff;
350  n++;
351  twoTimes.good = true;
352  } else {
353  twoTimes.good = false;
354  }
355  }
356  for (int i = 0; i < c_TimeAxisSize; i++) {
357  profMeans[i] = prof.GetBinContent(i + 1);
358  }
359  if (n == 0) return false;
360  x2 /= n;
361  x /= n;
362  sigma = sqrt(x2 - x * x);
363  }
364  prof.Write();
365  hist.Write();
366 
367  // calculate average time difference
368 
369  double meanTimeDifference = 0;
370  for (auto& x : profMeans) meanTimeDifference += x;
371  meanTimeDifference /= profMeans.size();
372  meanTimeDifference -= int(meanTimeDifference / static_cast<double>(c_TimeAxisSize)) * c_TimeAxisSize;
373 
374  // perform the fit
375 
376  switch (m_method) {
377  case 0:
378  B2INFO("... channel " << chan << ": "
379  << ntuple.size() << " double cal pulses)");
380  return false;
381  case 1:
382  return matrixInversion(ntuple, scrodID, chan, meanTimeDifference,
383  Hchi2, Hndf, HDeltaT);
384  case 2:
385  return iterativeTBC(ntuple, scrodID, chan, meanTimeDifference,
386  Hchi2, Hndf, HDeltaT);
387  case 3:
388  B2ERROR("Singuler value decomposition not implemented yet");
389  return false;
390  default:
391  B2ERROR("Unknown method " << m_method);
392  return false;
393  }
394 
395  }
396 
397 
398  bool TOPTimeBaseCalibratorModule::matrixInversion(const vector<TwoTimes>& ntuple,
399  unsigned scrodID, unsigned chan,
400  double meanTimeDifference,
401  TH1F& Hchi2, TH1F& Hndf,
402  TH1F& HDeltaT)
403  {
404 
405  // Ax = b: construct matrix A and right side vector b
406 
407  TMatrixDSym A(c_TimeAxisSize);
408  vector<double> b(c_TimeAxisSize, 0.0);
409 
410  for (const auto& twoTimes : ntuple) {
411  if (!twoTimes.good) continue;
412 
413  vector<double> m(c_TimeAxisSize, 0.0);
414  int i1 = int(twoTimes.t1);
415  m[i1 % c_TimeAxisSize] = 1.0 - (twoTimes.t1 - i1);
416  int i2 = int(twoTimes.t2);
417  m[i2 % c_TimeAxisSize] = twoTimes.t2 - i2;
418  i2 = i1 + (i2 - i1) % c_TimeAxisSize;
419  for (int k = i1 + 1; k < i2; k++) m[k % c_TimeAxisSize] = 1;
420 
421  double relSigma = twoTimes.sigma / meanTimeDifference;
422  double sig2 = relSigma * relSigma;
423 
424  for (int jj = i1; jj < i2 + 1; jj++) {
425  int j = jj % c_TimeAxisSize;
426  for (int kk = i1; kk < i2 + 1; kk++) {
427  int k = kk % c_TimeAxisSize;
428  A(j, k) += m[j] * m[k] / sig2;
429  }
430  }
431  for (int k = 0; k < c_TimeAxisSize; k++) b[k] += m[k] / sig2;
432  }
433 
434  // save as histograms
435 
436  string forWhat = "scrod " + to_string(scrodID) + " channel " + to_string(chan);
437  if (m_saveMatrix) {
438  saveAsHistogram(A, "matA_ch" + to_string(chan), "Matrix for " + forWhat);
439  saveAsHistogram(b, "vecB_ch" + to_string(chan), "Right side for " + forWhat);
440  }
441 
442  // invert matrix A and solve the equation: x = A^{-1}b
443 
444  double det = 0;
445  A.Invert(&det);
446  if (det == 0) {
447  B2INFO("... channel " << chan << " failed");
448  return false;
449  }
450 
451  vector<double> x(c_TimeAxisSize, 0.0);
452  for (int k = 0; k < c_TimeAxisSize; k++) {
453  for (int j = 0; j < c_TimeAxisSize; j++) {
454  x[k] += A(k, j) * b[j];
455  }
456  }
457 
458  // calculate chi^2
459 
460  double chi2 = 0;
461  int ndf = -c_TimeAxisSize;
462 
463  for (const auto& twoTimes : ntuple) {
464  if (!twoTimes.good) continue;
465 
466  vector<double> m(c_TimeAxisSize, 0.0);
467  int i1 = int(twoTimes.t1);
468  m[i1 % c_TimeAxisSize] = 1.0 - (twoTimes.t1 - i1);
469  int i2 = int(twoTimes.t2);
470  m[i2 % c_TimeAxisSize] = twoTimes.t2 - i2;
471  i2 = i1 + (i2 - i1) % c_TimeAxisSize;
472  for (int k = i1 + 1; k < i2; k++) m[k % c_TimeAxisSize] = 1;
473  double s = -1.0;
474  for (int k = 0; k < c_TimeAxisSize; k++) s += m[k] * x[k];
475  double relSigma = twoTimes.sigma / meanTimeDifference;
476  double sig2 = relSigma * relSigma;
477  chi2 += s * s / sig2;
478  ndf++;
479  }
480  Hchi2.SetBinContent(chan + 1, chi2 / ndf);
481  Hndf.SetBinContent(chan + 1, ndf);
482 
483  // constrain sum of x to 2*syncTimeBase and calculate sample times
484 
485  double sum = 0;
486  for (auto xi : x) sum += xi;
487  if (sum == 0) {
488  B2ERROR("sum == 0");
489  return false;
490  }
491  double DeltaT = 2 * m_syncTimeBase / sum;
492  for (auto& xi : x) xi *= DeltaT;
493  HDeltaT.SetBinContent(chan + 1, DeltaT);
494 
495  vector<double> err;
496  for (int k = 0; k < c_TimeAxisSize; k++) err.push_back(sqrt(A(k, k)) * DeltaT);
497 
498  vector<double> sampleTimes;
499  sampleTimes.push_back(0);
500  for (auto xi : x) sampleTimes.push_back(xi + sampleTimes.back());
501 
502  // save results as histograms
503 
504  if (m_saveMatrix) saveAsHistogram(A, "invA_ch" + to_string(chan), "Inverted matrix for " + forWhat);
505  saveAsHistogram(x, err, "dt_ch" + to_string(chan), "Sample time bins for " + forWhat,
506  "sample number", "#Delta t [ns]");
507  saveAsHistogram(sampleTimes, "sampleTimes_ch" + to_string(chan),
508  "Time base corrections for " + forWhat, "sample number", "t [ns]");
509 
510  // calibrated cal pulse time difference
511 
512  string name = "timeDiffcal_ch" + to_string(chan);
513  string title = "Calibrated cal pulse time difference vs. sample for " + forWhat;
514  TH2F Hcor(name.c_str(), title.c_str(), c_TimeAxisSize, 0, c_TimeAxisSize,
515  100, DeltaT - 0.5, DeltaT + 0.5);
516  Hcor.SetXTitle("sample number");
517  Hcor.SetYTitle("time difference [ns]");
518  Hcor.SetStats(kTRUE);
519 
520  TOPSampleTimes timeBase;
521  timeBase.setTimeAxis(sampleTimes, sampleTimes.back() / 2);
522 
523  for (const auto& twoTimes : ntuple) {
524  if (!twoTimes.good) continue;
525  double dt = timeBase.getDeltaTime(0, twoTimes.t2, twoTimes.t1);
526  int sample = int(twoTimes.t1) % c_TimeAxisSize;
527  Hcor.Fill(sample, dt);
528  }
529  Hcor.Write();
530 
531  B2INFO("... channel " << chan << " OK (chi^2/ndf = " << chi2 / ndf
532  << ", ndf = " << ndf << ")");
533 
534  return true;
535  }
536 
537 
538  bool TOPTimeBaseCalibratorModule::iterativeTBC(const std::vector<TwoTimes>& ntuple,
539  unsigned scrodID, unsigned chan,
540  double meanTimeDifference,
541  TH1F& Hchi2, TH1F& Hndf,
542  TH1F& HDeltaT)
543  {
544  std::vector<double> xval(c_TimeAxisSize + 1, 0.0);
545  double wx = 2 * m_syncTimeBase / static_cast<double>(c_TimeAxisSize);
546  for (int i = 0; i < c_TimeAxisSize + 1; i++) xval[i] = i * wx;
547 
548  B2INFO("TimeBaseCalibration starts for channel#" << chan);
549 
550  double pre_chi2 = 10000000.0;
551  unsigned num_small_dev = 0;
552 
553  for (unsigned j = 0; j < m_numIterations; j++) {
554 
555  Iteration(ntuple, xval);
556  double this_chi2 = Chisq(ntuple, xval);
557  if (this_chi2 < 0)continue;
558  double deltaChi2 = pre_chi2 - this_chi2;
559  if (deltaChi2 < -m_dchi2_min) break;
560  if (fabs(deltaChi2) < m_deltamin) num_small_dev++;
561  if (num_small_dev > m_conv_iter) break;
562  pre_chi2 = this_chi2;
563  }
564 
565  // calculate chi^2
566 
567  double chi2 = Chisq(ntuple, xval);
568  Hchi2.SetBinContent(chan + 1, chi2);
569  Hndf.SetBinContent(chan + 1, m_good);
570 
571  // constrain sum of x to 2*syncTimeBase and calculate sample times, not necessary here
572 
573  double sum = 0;
574  for (auto xi : xval) sum += xi;
575  if (sum == 0) {
576  B2ERROR("sum == 0");
577  return false;
578  }
579 
580  double DeltaT = meanTimeDifference * (2 * m_syncTimeBase / static_cast<double>(c_TimeAxisSize));
581  HDeltaT.SetBinContent(chan + 1, DeltaT);
582 
583  std::vector<double> timeInterval;
584  for (int i = 0; i < c_TimeAxisSize; i++)timeInterval.push_back(xval[i + 1] - xval[i]);
585 
586 
587  std::vector<double> sampleTimes;
588  for (auto xi : xval) sampleTimes.push_back(xi);
589 
590  // save results as histograms
591  std::string forWhat = "scrod " + to_string(scrodID) + " channel " + to_string(chan);
592  saveAsHistogram(timeInterval, "dt_ch" + to_string(chan), "Sample time bins for " + forWhat,
593  "sample number", "#Delta t [ns]");
594  saveAsHistogram(sampleTimes, "sampleTimes_ch" + to_string(chan),
595  "Time base corrections for " + forWhat, "sample number", "t [ns]");
596 
597  // calibrated cal pulse time difference
598  std::string name = "timeDiffcal_ch" + to_string(chan);
599  std::string title = "Calibrated cal pulse time difference vs. sample for " + forWhat;
600  TH2F Hcor(name.c_str(), title.c_str(), c_TimeAxisSize, 0, c_TimeAxisSize,
601  100, DeltaT - 0.5, DeltaT + 0.5);
602  Hcor.SetXTitle("sample number");
603  Hcor.SetYTitle("time difference [ns]");
604  Hcor.SetStats(kTRUE);
605 
606  TOPSampleTimes timeBase;
607  timeBase.setTimeAxis(sampleTimes, sampleTimes.back() / 2);
608 
609  for (const auto& twoTimes : ntuple) {
610  if (!twoTimes.good) continue;
611  double dt = timeBase.getDeltaTime(0, twoTimes.t2, twoTimes.t1);
612  int sample = int(twoTimes.t1) % c_TimeAxisSize;
613  Hcor.Fill(sample, dt);
614  }
615  Hcor.Write();
616 
617  B2INFO("... channel " << chan << " OK (chi^2/ndf = " << chi2
618  << ", ndf = " << m_good << ")");
619 
620  return true;
621  }
622 
623  void TOPTimeBaseCalibratorModule::Iteration(const std::vector<TwoTimes>& ntuple, std::vector<double>& xval)
624  {
625  for (int i = 0; i < c_TimeAxisSize; i++) {
626  double wdth = xval[i + 1] - xval[i];
627  if (wdth < m_min_binwidth) {
628  xval[i] = xval[i] - 0.5 * fabs(wdth) - 0.5 * m_min_binwidth;
629  xval[i + 1] = xval[i + 1] + 0.5 * fabs(wdth) + 0.5 * m_min_binwidth;
630  }
631  if (wdth > m_max_binwidth) {
632  xval[i] = xval[i] - 0.5 * fabs(wdth) - 0.5 * m_max_binwidth;
633  xval[i + 1] = xval[i + 1] + 0.5 * fabs(wdth) + 0.5 * m_max_binwidth;
634  }
635  }
636 
637  if (xval[0] != 0)
638  for (int i = 0; i < c_TimeAxisSize; i++) xval[i] = xval[i] - xval[0];
639 
640  std::vector<double> xxval(c_TimeAxisSize + 1, 0.0);
641  for (int i = 0; i < c_TimeAxisSize + 1; i++) xxval[i] = xval[i];
642 
643  double chi2_0 = Chisq(ntuple, xxval);
644  if (chi2_0 < 0) B2ERROR("iTBC chisq_0<0! xval has problem.");
645 
646  std::vector<double> dr_chi2(c_TimeAxisSize + 1, 0.0);
647  TH1D hdrsamp_try("hdrsamp_try", "dchi2/dx distribution", 100, -0.01, 0.01);
648 
649  for (int smp = 1; smp < c_TimeAxisSize; smp++) {
650  xxval[smp] = xval[smp] + m_dev_step;
651  double chi2_ch = Chisq(ntuple, xxval);
652  if (chi2_ch < 0)continue;
653  dr_chi2[smp] = (chi2_ch - chi2_0) / m_dev_step;
654  hdrsamp_try.Fill(dr_chi2[smp]);
655  xxval[smp] = xval[smp];
656  }
657 
658  for (int smp = 1; smp < c_TimeAxisSize; smp++) {
659  double vx_it_step = dr_chi2[smp] * m_xstep;
660  xval[smp] = xval[smp] - vx_it_step;
661  }
662 
663  //save rms of dchi2/dxval.
664  m_dchi2dxv = hdrsamp_try.GetRMS();
665  //change m_xstep
667  }
668 
669  double TOPTimeBaseCalibratorModule::Chisq(const std::vector<TwoTimes>& ntuple, const std::vector<double>& xxval)
670  {
671  double sum1 = 0.0;
672  double sum2 = 0.0; //sum od dt and dt**2
673 
674  m_good = 0;
675 
676  for (const auto& twoTimes : ntuple) {
677  if (!twoTimes.good) continue;
678 
679  std::vector<double> m(c_TimeAxisSize, 0.0);
680 
681  int i1 = int(twoTimes.t1);
682  double fr = twoTimes.t1 - i1;
683  double samp0 = i1 % 256;
684  double ctdc1 = xxval[samp0] + fr * (xxval[samp0 + 1] - xxval[samp0]);
685  int i2 = int(twoTimes.t2);
686  fr = twoTimes.t2 - i2;
687  double samp1 = i2 % 256;
688  double ctdc2 = xxval[samp1] + fr * (xxval[samp1 + 1] - xxval[samp1]);
689  double cdt = 0.0;
690  if (samp1 > samp0) cdt = ctdc2 - ctdc1;
691  else cdt = ctdc2 - ctdc1 + m_syncTimeBase * 2;
692 
693  if (cdt < m_dt_max && cdt > m_dt_min) {
694  sum1 += cdt;
695  sum2 += cdt * cdt;
696  m_good++;
697  }
698  }
699 
700  double chi2 = -1.0;
701 
702  if (m_good > 10) {
703  double mean = sum1 / m_good;
704  chi2 = (sum2 - m_good * mean * mean) / m_good / m_sigm2_exp;
705  }
706  return chi2;
707  }
708 
709 
710 
711  void TOPTimeBaseCalibratorModule::saveAsHistogram(const std::vector<double>& vec,
712  const std::string& name,
713  const std::string& title,
714  const std::string& xTitle,
715  const std::string& yTitle) const
716  {
717  if (vec.empty()) return;
718 
719  TH1F h(name.c_str(), title.c_str(), vec.size(), 0, vec.size());
720  h.SetXTitle(xTitle.c_str());
721  h.SetYTitle(yTitle.c_str());
722  if (name.find("Fit") != string::npos) h.SetLineColor(2);
723 
724  for (unsigned i = 0; i < vec.size(); i++) h.SetBinContent(i + 1, vec[i]);
725 
726  h.Write();
727  }
728 
729 
730  void TOPTimeBaseCalibratorModule::saveAsHistogram(const vector<double>& vec,
731  const vector<double>& err,
732  const string& name,
733  const string& title,
734  const string& xTitle,
735  const string& yTitle) const
736  {
737  if (vec.empty()) return;
738 
739  TH1F h(name.c_str(), title.c_str(), vec.size(), 0, vec.size());
740  h.SetXTitle(xTitle.c_str());
741  h.SetYTitle(yTitle.c_str());
742 
743  for (unsigned i = 0; i < vec.size(); i++) h.SetBinContent(i + 1, vec[i]);
744  for (unsigned i = 0; i < err.size(); i++) h.SetBinError(i + 1, err[i]);
745 
746  h.Write();
747  }
748 
749 
751  const string& name,
752  const string& title) const
753  {
754  int n = M.GetNrows();
755  TH2F h(name.c_str(), title.c_str(), n, 0, n, n, 0, n);
756  h.SetXTitle("columns");
757  h.SetYTitle("rows");
758 
759  for (int j = 0; j < n; j++) {
760  for (int k = 0; k < n; k++) {
761  h.SetBinContent(j + 1, n - k, M(j, k));
762  }
763  }
764 
765  h.Write();
766  }
767 
768 
769 
771 } // end Belle2 namespace
772 
Base class for Modules.
Definition: Module.h:72
void setDescription(const std::string &description)
Sets the description of the module.
Definition: Module.cc:214
const TOPNominalTDC & getNominalTDC() const
Returns nominal time-to-digit conversion parameters.
Definition: TOPGeometry.h:218
double getSampleWidth() const
Returns time difference between two samples.
Class to store unpacked raw data (hits in feature-extraction format) It provides also calculation of ...
Definition: TOPRawDigit.h:24
Calibration constants of a singe ASIC channel: time axis (sample times)
double getDeltaTime(int window, double sample2, double sample1) const
Returns time difference between sample2 and sample1.
double m_deltamin
minumum chisq change in an iteration.
double m_xstep
unit for an interation of delta(X_s)
TH2F m_calPulseSecond
pulse height versus width of the second calibration pulse
double m_sigm2_exp
(sigma_0(dT))**2 for nomarlization of chisq = sum{dT^2/sigma^2}
std::vector< TwoTimes > m_ntuples[c_NumChannels]
channel wise data
@ c_NumBoardstacks
number of boardstacks per module
@ c_NumChannels
number of channels per module
@ c_TimeAxisSize
number of samples to calibrate
double m_min_binwidth
minimum time interval of one sample
TH2F m_goodHits
pulse height versus width of all good hits
double m_syncTimeBase
synchronization time (two ASIC windows)
double m_new_xstep
m_xstep = m_new_xstep if m_dchi2dxv < m_change_step
double m_dt_min
minimum Delta T of raw calpulse
bool m_saveMatrix
if true, save also matrix and its inverse in a root file
double m_dt_max
maximum Delta T of raw calpulse
double m_dev_step
a step size to calculate the value of d(chisq)/dxval
unsigned m_conv_iter
Number of iteration with chisq changes less than deltamin.
double m_dchi2dxv
rms of 255 dchi2/dxval values
double m_max_binwidth
maximum time interval of one sample
unsigned m_minHits
minimal required hits per channel
double m_minTimeDiff
lower bound for time difference [samples]
double m_change_xstep
update m_xstep if m_dchi2dxv < m_change_step
StoreArray< TOPDigit > m_digits
collection of digits
unsigned m_numIterations
Number of Iterations of iTBC.
std::string m_directoryName
directory name for the output root files
int m_good
good events used for chisq cal.
TH2F m_calPulseFirst
pulse height versus width of the first calibration pulse
double m_dchi2_min
quit if chisq increase in iteratons is larger than this value.
bool m_useFallingEdge
if true, use falling edge instead of rising
double m_maxTimeDiff
upper bound for time difference [samples]
const TOPFrontEndMap * getMap(int moduleID, int bs) const
Return map from TOP module side.
const TOPGeometry * getGeometry() const
Returns pointer to geometry object using basf2 units.
const FrontEndMapper & getFrontEndMapper() const
Returns front-end mapper (mapping of SCROD's to positions within TOP modules)
static TOPGeometryPar * Instance()
Static method to obtain the pointer to its instance.
void addParam(const std::string &name, T &paramVariable, const std::string &description, const T &defaultValue)
Adds a new parameter to the module.
Definition: Module.h:560
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
Definition: Module.h:650
double sqrt(double a)
sqrt for double
Definition: beamHelpers.h:28
bool matrixInversion(const std::vector< TwoTimes > &ntuple, unsigned scrodID, unsigned scrodChannel, double meanTimeDifference, TH1F &Hchi2, TH1F &Hndf, TH1F &HDeltaT)
Method by matrix inversion.
bool calibrateChannel(std::vector< TwoTimes > &ntuple, unsigned scrodID, unsigned scrodChannel, TH1F &Hchi2, TH1F &Hndf, TH1F &HDeltaT)
calibrate single channel
virtual void initialize() override
Initialize the Module.
virtual void event() override
Event processor.
virtual void endRun() override
End-of-run action.
virtual void terminate() override
Termination action.
virtual void beginRun() override
Called when entering a new run.
void Iteration(const std::vector< TwoTimes > &ntuple, std::vector< double > &xval)
Iteration function called by iterativeTBC()
double Chisq(const std::vector< TwoTimes > &ntuple, const std::vector< double > &xxval)
Return the chisqure of one set of TBC constants (xval) in iTBC calculaton.
void saveAsHistogram(const std::vector< double > &vec, const std::string &name, const std::string &title, const std::string &xTitle="", const std::string &yTitle="") const
Save vector to histogram and write it out.
bool iterativeTBC(const std::vector< TwoTimes > &ntuple, unsigned scrodID, unsigned scrodChannel, double meanTimeDifference, TH1F &Hchi2, TH1F &Hndf, TH1F &HDeltaT)
Method by iteration of chi2 minimization.
void setTimeAxis(double syncTimeBase)
Sets equidistant time axis (uncalibrated).
Abstract base class for different kinds of events.
Structure to hold some of the calpulse data.
Structure to hold calpulse raw times expressed in samples since sample 0 of window 0.