10 #include <top/modules/TOPTimeBaseCalibrator/TOPTimeBaseCalibratorModule.h>
13 #include <top/geometry/TOPGeometryPar.h>
16 #include <framework/datastore/StoreArray.h>
19 #include <framework/logging/Logger.h>
22 #include <top/dataobjects/TOPDigit.h>
23 #include <top/dataobjects/TOPRawDigit.h>
24 #include <top/dbobjects/TOPSampleTimes.h>
30 #include <TMatrixDSym.h>
54 TOPTimeBaseCalibratorModule::TOPTimeBaseCalibratorModule() :
Module()
64 "lower bound on time difference [samples].", 0.0);
66 "upper bound on time difference [samples].", 100.0);
68 "name (with path) of the directory for the output root files.",
71 "minimal required hits per channel.", (
unsigned) 1000);
73 "number of iTBC iterations", (
unsigned) 100);
75 "Max number of iTBC iterations for conversion.", (
unsigned) 50);
77 "Minimal of chi2 change in iterations.", 0.2);
79 "minimum Delta T of raw calpulse in iTBC", 20.0);
81 "maximum Delta T of raw calpulse in iTBC", 24.0);
83 "unit for an interation of delta(X_s)", 0.020);
85 "a step size to calculate the value of d(chisq)/dxval", 0.001);
87 "update m_xstep if m_dchi2dxv < m_change_step", 0.015);
89 "a new step for delta(X_s) if m_dchi2dxv < m_change_step", 2.0 *
m_xstep);
91 "(sigma_0(dT))**2 for nomarlization of chisq = sum{dT^2/sigma^2}", 0.0424 * 0.0424);
94 "1 - matrix inversion, 2 - iterative, "
95 "3 - matrix inversion w/ singular value decomposition.", (
unsigned) 1);
97 "if true, use cal pulse falling edge instead of rising edge",
false);
124 m_goodHits = TH2F(
"goodHits",
"pulse height vs. pulse width of all good hits",
125 100, 0, 10, 100, 0, 2000);
126 m_goodHits.SetXTitle(
"pulse width (FWHM) [ns]");
127 m_goodHits.SetYTitle(
"pulse height [ADC counts]");
129 "pulse height vs. pulse width of the first calibration pulse",
130 100, 0, 10, 100, 0, 2000);
134 "pulse height vs. pulse width of the second calibration pulse",
135 100, 0, 10, 100, 0, 2000);
153 for (
const auto& digit :
m_digits) {
154 if (digit.getModuleID() !=
m_moduleID)
continue;
155 if (digit.getHitQuality() != TOPDigit::c_Junk) {
156 m_goodHits.Fill(digit.getPulseWidth(), digit.getPulseHeight());
158 if (digit.getHitQuality() != TOPDigit::c_CalPulse)
continue;
159 double rawTime = digit.getRawTime();
160 double errScaleFactor = 1;
162 const auto* rawDigit = digit.getRelated<
TOPRawDigit>();
164 B2ERROR(
"No relation to TOPRawDigit - can't determine falling edge time error");
169 rawTime += rawDigit->getFWHM();
170 errScaleFactor = rawDigit->getCFDFallingTimeError(1.0) / rawDigit->getCFDLeadingTimeError(1.0);
172 double t = rawTime + digit.getFirstWindow() *
c_WindowSize;
174 B2ERROR(
"Got negative sample number - digit ignored");
177 double et = digit.getTimeError() / sampleWidth * errScaleFactor;
179 B2ERROR(
"Time error is not given - digit ignored");
182 unsigned channel = digit.getChannel();
184 hits[channel].push_back(
Hit(t, et, digit.getPulseHeight(), digit.getPulseWidth()));
188 for (
unsigned channel = 0; channel <
c_NumChannels; channel++) {
189 const auto& channelHits = hits[channel];
190 if (channelHits.size() == 2) {
191 double t0 = channelHits[0].time;
192 double t1 = channelHits[1].time;
193 auto diff = fabs(t0 - t1);
196 double sig0 = channelHits[0].timeErr;
197 double sig1 = channelHits[1].timeErr;
198 double sigma =
sqrt(sig0 * sig0 + sig1 * sig1);
201 m_calPulseFirst.Fill(channelHits[0].pulseWidth, channelHits[0].pulseHeight);
202 m_calPulseSecond.Fill(channelHits[1].pulseWidth, channelHits[1].pulseHeight);
204 m_calPulseSecond.Fill(channelHits[0].pulseWidth, channelHits[0].pulseHeight);
205 m_calPulseFirst.Fill(channelHits[1].pulseWidth, channelHits[1].pulseHeight);
207 }
else if (channelHits.size() > 2) {
208 B2WARNING(
"More than two cal pulses per channel found - ignored");
230 B2ERROR(
"No front-end map available for slot " <<
m_moduleID <<
", BS" << bs);
233 unsigned scrodID = feMap->getScrodID();
243 fileName +=
"_" + to_string(bs) +
"-scrod" +
244 to_string(scrodID) +
".root";
245 TFile* fout = TFile::Open(fileName.c_str(),
"recreate");
247 B2ERROR(
"Can't open the output file " << fileName);
257 B2INFO(
"Fitting time base corrections for SCROD " << scrodID
258 <<
", output file: " << fileName);
262 TH1F Hchan(
"channels",
"channels with cal pulse",
263 c_NumScrodChannels, 0, c_NumScrodChannels);
264 Hchan.SetXTitle(
"channel number");
265 Hchan.SetYTitle(
"double cal pulse counts");
266 TH1F Hsuccess(
"success",
"successfuly fitted channels",
267 c_NumScrodChannels, 0, c_NumScrodChannels);
268 Hsuccess.SetXTitle(
"channel number");
269 TH1F Hchi2(
"chi2",
"normalized chi2",
270 c_NumScrodChannels, 0, c_NumScrodChannels);
271 Hchi2.SetXTitle(
"channel number");
272 Hchi2.SetYTitle(
"chi2/ndf");
273 TH1F Hndf(
"ndf",
"degrees of freedom",
274 c_NumScrodChannels, 0, c_NumScrodChannels);
275 Hndf.SetXTitle(
"channel number");
276 Hndf.SetYTitle(
"ndf");
277 TH1F HDeltaT(
"DeltaT",
"Fitted double pulse delay time",
278 c_NumScrodChannels, 0, c_NumScrodChannels);
279 HDeltaT.SetXTitle(
"channel number");
280 HDeltaT.SetYTitle(
"#Delta T [ns]");
284 for (
unsigned chan = 0; chan < c_NumScrodChannels; chan++) {
285 unsigned channel = chan + bs * c_NumScrodChannels;
287 Hchan.SetBinContent(chan + 1, ntuple.size());
290 if (ok) Hsuccess.Fill(chan);
292 B2INFO(
"... channel " << chan <<
" statistics too low ("
293 << ntuple.size() <<
" double cal pulses)");
311 unsigned scrodID,
unsigned chan,
312 TH1F& Hchi2, TH1F& Hndf,
318 string name =
"timeDiff_ch" + to_string(chan);
319 string forWhat =
"scrod " + to_string(scrodID) +
" channel " + to_string(chan);
320 string title =
"Cal pulse time difference vs. sample for " + forWhat;
323 prof.SetXTitle(
"sample number");
324 prof.SetYTitle(
"time difference [samples]");
326 name =
"sampleOccup_ch" + to_string(chan);
327 title =
"Occupancy for " + forWhat;
329 hist.SetXTitle(
"sample number");
330 hist.SetYTitle(
"entries per sample");
335 for (
int iter = 0; iter < 5; iter++) {
340 for (
auto& twoTimes : ntuple) {
342 double diff = twoTimes.t2 - twoTimes.t1;
343 double Ddiff = diff - profMeans[sample];
344 if (fabs(Ddiff) < 3 * sigma) {
345 prof.Fill(sample, diff);
350 twoTimes.good =
true;
352 twoTimes.good =
false;
356 profMeans[i] = prof.GetBinContent(i + 1);
358 if (n == 0)
return false;
361 sigma =
sqrt(x2 - x * x);
368 double meanTimeDifference = 0;
369 for (
auto& x : profMeans) meanTimeDifference += x;
370 meanTimeDifference /= profMeans.size();
377 B2INFO(
"... channel " << chan <<
": "
378 << ntuple.size() <<
" double cal pulses)");
382 Hchi2, Hndf, HDeltaT);
384 return iterativeTBC(ntuple, scrodID, chan, meanTimeDifference,
385 Hchi2, Hndf, HDeltaT);
387 B2ERROR(
"Singuler value decomposition not implemented yet");
390 B2ERROR(
"Unknown method " <<
m_method);
398 unsigned scrodID,
unsigned chan,
399 double meanTimeDifference,
400 TH1F& Hchi2, TH1F& Hndf,
409 for (
const auto& twoTimes : ntuple) {
410 if (!twoTimes.good)
continue;
413 int i1 = int(twoTimes.t1);
415 int i2 = int(twoTimes.t2);
420 double relSigma = twoTimes.sigma / meanTimeDifference;
421 double sig2 = relSigma * relSigma;
423 for (
int jj = i1; jj < i2 + 1; jj++) {
425 for (
int kk = i1; kk < i2 + 1; kk++) {
427 A(j, k) += m[j] * m[k] / sig2;
435 string forWhat =
"scrod " + to_string(scrodID) +
" channel " + to_string(chan);
436 saveAsHistogram(A,
"matA_ch" + to_string(chan),
"Matrix for " + forWhat);
437 saveAsHistogram(b,
"vecB_ch" + to_string(chan),
"Right side for " + forWhat);
444 B2INFO(
"... channel " << chan <<
" failed");
451 x[k] += A(k, j) * b[j];
460 for (
const auto& twoTimes : ntuple) {
461 if (!twoTimes.good)
continue;
464 int i1 = int(twoTimes.t1);
466 int i2 = int(twoTimes.t2);
472 double relSigma = twoTimes.sigma / meanTimeDifference;
473 double sig2 = relSigma * relSigma;
474 chi2 += s * s / sig2;
477 Hchi2.SetBinContent(chan + 1, chi2 / ndf);
478 Hndf.SetBinContent(chan + 1, ndf);
483 for (
auto xi : x) sum += xi;
489 for (
auto& xi : x) xi *= DeltaT;
490 HDeltaT.SetBinContent(chan + 1, DeltaT);
495 vector<double> sampleTimes;
496 sampleTimes.push_back(0);
497 for (
auto xi : x) sampleTimes.push_back(xi + sampleTimes.back());
501 saveAsHistogram(A,
"invA_ch" + to_string(chan),
"Inverted matrix for " + forWhat);
502 saveAsHistogram(x, err,
"dt_ch" + to_string(chan),
"Sample time bins for " + forWhat,
503 "sample number",
"#Delta t [ns]");
505 "Time base corrections for " + forWhat,
"sample number",
"t [ns]");
509 string name =
"timeDiffcal_ch" + to_string(chan);
510 string title =
"Calibrated cal pulse time difference vs. sample for " + forWhat;
512 100, DeltaT - 0.5, DeltaT + 0.5);
513 Hcor.SetXTitle(
"sample number");
514 Hcor.SetYTitle(
"time difference [ns]");
515 Hcor.SetStats(kTRUE);
518 timeBase.
setTimeAxis(sampleTimes, sampleTimes.back() / 2);
520 for (
const auto& twoTimes : ntuple) {
521 if (!twoTimes.good)
continue;
522 double dt = timeBase.
getDeltaTime(0, twoTimes.t2, twoTimes.t1);
524 Hcor.Fill(sample, dt);
528 B2INFO(
"... channel " << chan <<
" OK (chi^2/ndf = " << chi2 / ndf
529 <<
", ndf = " << ndf <<
")");
536 unsigned scrodID,
unsigned chan,
537 double meanTimeDifference,
538 TH1F& Hchi2, TH1F& Hndf,
545 B2INFO(
"TimeBaseCalibration starts for channel#" << chan);
547 double pre_chi2 = 10000000.0;
548 unsigned num_small_dev = 0;
553 double this_chi2 =
Chisq(ntuple, xval);
554 if (this_chi2 < 0)
continue;
555 double deltaChi2 = pre_chi2 - this_chi2;
557 if (fabs(deltaChi2) <
m_deltamin) num_small_dev++;
559 pre_chi2 = this_chi2;
564 double chi2 =
Chisq(ntuple, xval);
565 Hchi2.SetBinContent(chan + 1, chi2);
566 Hndf.SetBinContent(chan + 1,
m_good);
571 for (
auto xi : xval) sum += xi;
578 HDeltaT.SetBinContent(chan + 1, DeltaT);
580 std::vector<double> timeInterval;
581 for (
int i = 0; i <
c_TimeAxisSize; i++)timeInterval.push_back(xval[i + 1] - xval[i]);
584 std::vector<double> sampleTimes;
585 for (
auto xi : xval) sampleTimes.push_back(xi);
588 std::string forWhat =
"scrod " + to_string(scrodID) +
" channel " + to_string(chan);
589 saveAsHistogram(timeInterval,
"dt_ch" + to_string(chan),
"Sample time bins for " + forWhat,
590 "sample number",
"#Delta t [ns]");
592 "Time base corrections for " + forWhat,
"sample number",
"t [ns]");
595 std::string name =
"timeDiffcal_ch" + to_string(chan);
596 std::string title =
"Calibrated cal pulse time difference vs. sample for " + forWhat;
598 100, DeltaT - 0.5, DeltaT + 0.5);
599 Hcor.SetXTitle(
"sample number");
600 Hcor.SetYTitle(
"time difference [ns]");
601 Hcor.SetStats(kTRUE);
604 timeBase.
setTimeAxis(sampleTimes, sampleTimes.back() / 2);
606 for (
const auto& twoTimes : ntuple) {
607 if (!twoTimes.good)
continue;
608 double dt = timeBase.
getDeltaTime(0, twoTimes.t2, twoTimes.t1);
610 Hcor.Fill(sample, dt);
614 B2INFO(
"... channel " << chan <<
" OK (chi^2/ndf = " << chi2
615 <<
", ndf = " <<
m_good <<
")");
623 double wdth = xval[i + 1] - xval[i];
626 xval[i + 1] = xval[i + 1] + 0.5 * fabs(wdth) + 0.5 *
m_min_binwidth;
630 xval[i + 1] = xval[i + 1] + 0.5 * fabs(wdth) + 0.5 *
m_max_binwidth;
635 for (
int i = 0; i <
c_TimeAxisSize; i++) xval[i] = xval[i] - xval[0];
640 double chi2_0 =
Chisq(ntuple, xxval);
641 if (chi2_0 < 0) B2ERROR(
"iTBC chisq_0<0! xval has problem.");
644 TH1D hdrsamp_try(
"hdrsamp_try",
"dchi2/dx distribution", 100, -0.01, 0.01);
648 double chi2_ch =
Chisq(ntuple, xxval);
649 if (chi2_ch < 0)
continue;
650 dr_chi2[smp] = (chi2_ch - chi2_0) /
m_dev_step;
651 hdrsamp_try.Fill(dr_chi2[smp]);
652 xxval[smp] = xval[smp];
656 double vx_it_step = dr_chi2[smp] *
m_xstep;
657 xval[smp] = xval[smp] - vx_it_step;
673 for (
const auto& twoTimes : ntuple) {
674 if (!twoTimes.good)
continue;
678 int i1 = int(twoTimes.t1);
679 double fr = twoTimes.t1 - i1;
680 double samp0 = i1 % 256;
681 double ctdc1 = xxval[samp0] + fr * (xxval[samp0 + 1] - xxval[samp0]);
682 int i2 = int(twoTimes.t2);
683 fr = twoTimes.t2 - i2;
684 double samp1 = i2 % 256;
685 double ctdc2 = xxval[samp1] + fr * (xxval[samp1 + 1] - xxval[samp1]);
687 if (samp1 > samp0) cdt = ctdc2 - ctdc1;
690 if (cdt < m_dt_max && cdt >
m_dt_min) {
700 double mean = sum1 /
m_good;
709 const std::string& name,
710 const std::string& title,
711 const std::string& xTitle,
712 const std::string& yTitle)
const
714 if (vec.empty())
return;
716 TH1F h(name.c_str(), title.c_str(), vec.size(), 0, vec.size());
717 h.SetXTitle(xTitle.c_str());
718 h.SetYTitle(yTitle.c_str());
719 if (name.find(
"Fit") != string::npos) h.SetLineColor(2);
721 for (
unsigned i = 0; i < vec.size(); i++) h.SetBinContent(i + 1, vec[i]);
728 const vector<double>& err,
731 const string& xTitle,
732 const string& yTitle)
const
734 if (vec.empty())
return;
736 TH1F h(name.c_str(), title.c_str(), vec.size(), 0, vec.size());
737 h.SetXTitle(xTitle.c_str());
738 h.SetYTitle(yTitle.c_str());
740 for (
unsigned i = 0; i < vec.size(); i++) h.SetBinContent(i + 1, vec[i]);
741 for (
unsigned i = 0; i < err.size(); i++) h.SetBinError(i + 1, err[i]);
749 const string& title)
const
751 int n = M.GetNrows();
752 TH2F h(name.c_str(), title.c_str(), n, 0, n, n, 0, n);
753 h.SetXTitle(
"columns");
756 for (
int j = 0; j < n; j++) {
757 for (
int k = 0; k < n; k++) {
758 h.SetBinContent(j + 1, n - k, M(j, k));
void setDescription(const std::string &description)
Sets the description of the module.
const TOPNominalTDC & getNominalTDC() const
Returns nominal time-to-digit conversion parameters.
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 ...
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
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.
unsigned m_method
method to use
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.
@ c_NumBoardstacks
number of boardstacks per module
@ c_NumChannels
number of channels per module
@ c_TimeAxisSize
number of samples to calibrate
@ c_WindowSize
samples per window
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 ¶mVariable, const std::string &description, const T &defaultValue)
Adds a new parameter to the module.
#define REG_MODULE(moduleName)
Register the given module (without 'Module' suffix) with the framework.
double sqrt(double a)
sqrt for double
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()
virtual ~TOPTimeBaseCalibratorModule()
Destructor.
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.