Belle II Software development
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
34using namespace std;
35
36namespace Belle2 {
42 using namespace TOP;
43
44 //-----------------------------------------------------------------
46 //-----------------------------------------------------------------
47
48 REG_MODULE(TOPTimeBaseCalibrator);
49
50 //-----------------------------------------------------------------
51 // Implementation
52 //-----------------------------------------------------------------
53
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
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
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.
@ c_NumBoardstacks
number of boardstacks per module
@ c_NumChannels
number of channels per module
@ c_TimeAxisSize
number of samples to calibrate
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.
static TOPGeometryPar * Instance()
Static method to obtain the pointer to its instance.
const FrontEndMapper & getFrontEndMapper() const
Returns front-end mapper (mapping of SCROD's to positions within TOP modules)
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.
STL namespace.
Structure to hold some of the calpulse data.
Structure to hold calpulse raw times expressed in samples since sample 0 of window 0.