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>
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);
98 addParam(
"saveMatrix",
m_saveMatrix,
"if true, save also matrix and its inverse in a root file",
false);
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]");
130 "pulse height vs. pulse width of the first calibration pulse",
131 100, 0, 10, 100, 0, 2000);
135 "pulse height vs. pulse width of the second calibration pulse",
136 100, 0, 10, 100, 0, 2000);
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());
159 if (digit.getHitQuality() != TOPDigit::c_CalPulse)
continue;
160 double rawTime = digit.getRawTime();
161 double errScaleFactor = 1;
163 const auto* rawDigit = digit.getRelated<
TOPRawDigit>();
165 B2ERROR(
"No relation to TOPRawDigit - can't determine falling edge time error");
170 rawTime += rawDigit->getFWHM();
171 errScaleFactor = rawDigit->getCFDFallingTimeError(1.0) / rawDigit->getCFDLeadingTimeError(1.0);
173 double t = rawTime + digit.getFirstWindow() *
c_WindowSize;
175 B2ERROR(
"Got negative sample number - digit ignored");
178 double et = digit.getTimeError() / sampleWidth * errScaleFactor;
180 B2ERROR(
"Time error is not given - digit ignored");
183 unsigned channel = digit.getChannel();
185 hits[channel].push_back(
Hit(t, et, digit.getPulseHeight(), digit.getPulseWidth()));
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);
197 double sig0 = channelHits[0].timeErr;
198 double sig1 = channelHits[1].timeErr;
199 double sigma =
sqrt(sig0 * sig0 + sig1 * sig1);
202 m_calPulseFirst.Fill(channelHits[0].pulseWidth, channelHits[0].pulseHeight);
203 m_calPulseSecond.Fill(channelHits[1].pulseWidth, channelHits[1].pulseHeight);
205 m_calPulseSecond.Fill(channelHits[0].pulseWidth, channelHits[0].pulseHeight);
206 m_calPulseFirst.Fill(channelHits[1].pulseWidth, channelHits[1].pulseHeight);
208 }
else if (channelHits.size() > 2) {
209 B2WARNING(
"More than two cal pulses per channel found - ignored");
231 B2ERROR(
"No front-end map available for slot " <<
m_moduleID <<
", BS" << bs);
234 unsigned scrodID = feMap->getScrodID();
244 fileName +=
"_" + to_string(bs) +
"-scrod" +
245 to_string(scrodID) +
".root";
246 TFile* fout = TFile::Open(fileName.c_str(),
"recreate");
248 B2ERROR(
"Can't open the output file " << fileName);
258 B2INFO(
"Fitting time base corrections for SCROD " << scrodID
259 <<
", output file: " << fileName);
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]");
285 for (
unsigned chan = 0; chan < c_NumScrodChannels; chan++) {
286 unsigned channel = chan + bs * c_NumScrodChannels;
288 Hchan.SetBinContent(chan + 1, ntuple.size());
291 if (ok) Hsuccess.Fill(chan);
293 B2INFO(
"... channel " << chan <<
" statistics too low ("
294 << ntuple.size() <<
" double cal pulses)");
312 unsigned scrodID,
unsigned chan,
313 TH1F& Hchi2, TH1F& Hndf,
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;
324 prof.SetXTitle(
"sample number");
325 prof.SetYTitle(
"time difference [samples]");
327 name =
"sampleOccup_ch" + to_string(chan);
328 title =
"Occupancy for " + forWhat;
330 hist.SetXTitle(
"sample number");
331 hist.SetYTitle(
"entries per sample");
336 for (
int iter = 0; iter < 5; iter++) {
341 for (
auto& twoTimes : ntuple) {
343 double diff = twoTimes.t2 - twoTimes.t1;
344 double Ddiff = diff - profMeans[sample];
345 if (fabs(Ddiff) < 3 * sigma) {
346 prof.Fill(sample, diff);
351 twoTimes.good =
true;
353 twoTimes.good =
false;
357 profMeans[i] = prof.GetBinContent(i + 1);
359 if (n == 0)
return false;
362 sigma =
sqrt(x2 - x * x);
369 double meanTimeDifference = 0;
370 for (
auto& x : profMeans) meanTimeDifference += x;
371 meanTimeDifference /= profMeans.size();
378 B2INFO(
"... channel " << chan <<
": "
379 << ntuple.size() <<
" double cal pulses)");
383 Hchi2, Hndf, HDeltaT);
385 return iterativeTBC(ntuple, scrodID, chan, meanTimeDifference,
386 Hchi2, Hndf, HDeltaT);
388 B2ERROR(
"Singuler value decomposition not implemented yet");
391 B2ERROR(
"Unknown method " <<
m_method);
399 unsigned scrodID,
unsigned chan,
400 double meanTimeDifference,
401 TH1F& Hchi2, TH1F& Hndf,
410 for (
const auto& twoTimes : ntuple) {
411 if (!twoTimes.good)
continue;
414 int i1 = int(twoTimes.t1);
416 int i2 = int(twoTimes.t2);
421 double relSigma = twoTimes.sigma / meanTimeDifference;
422 double sig2 = relSigma * relSigma;
424 for (
int jj = i1; jj < i2 + 1; jj++) {
426 for (
int kk = i1; kk < i2 + 1; kk++) {
428 A(j, k) += m[j] * m[k] / sig2;
436 string forWhat =
"scrod " + to_string(scrodID) +
" channel " + to_string(chan);
438 saveAsHistogram(A,
"matA_ch" + to_string(chan),
"Matrix for " + forWhat);
439 saveAsHistogram(b,
"vecB_ch" + to_string(chan),
"Right side for " + forWhat);
447 B2INFO(
"... channel " << chan <<
" failed");
454 x[k] += A(k, j) * b[j];
463 for (
const auto& twoTimes : ntuple) {
464 if (!twoTimes.good)
continue;
467 int i1 = int(twoTimes.t1);
469 int i2 = int(twoTimes.t2);
475 double relSigma = twoTimes.sigma / meanTimeDifference;
476 double sig2 = relSigma * relSigma;
477 chi2 += s * s / sig2;
480 Hchi2.SetBinContent(chan + 1, chi2 / ndf);
481 Hndf.SetBinContent(chan + 1, ndf);
486 for (
auto xi : x) sum += xi;
492 for (
auto& xi : x) xi *= DeltaT;
493 HDeltaT.SetBinContent(chan + 1, DeltaT);
498 vector<double> sampleTimes;
499 sampleTimes.push_back(0);
500 for (
auto xi : x) sampleTimes.push_back(xi + sampleTimes.back());
505 saveAsHistogram(x, err,
"dt_ch" + to_string(chan),
"Sample time bins for " + forWhat,
506 "sample number",
"#Delta t [ns]");
508 "Time base corrections for " + forWhat,
"sample number",
"t [ns]");
512 string name =
"timeDiffcal_ch" + to_string(chan);
513 string title =
"Calibrated cal pulse time difference vs. sample for " + forWhat;
515 100, DeltaT - 0.5, DeltaT + 0.5);
516 Hcor.SetXTitle(
"sample number");
517 Hcor.SetYTitle(
"time difference [ns]");
518 Hcor.SetStats(kTRUE);
521 timeBase.
setTimeAxis(sampleTimes, sampleTimes.back() / 2);
523 for (
const auto& twoTimes : ntuple) {
524 if (!twoTimes.good)
continue;
525 double dt = timeBase.
getDeltaTime(0, twoTimes.t2, twoTimes.t1);
527 Hcor.Fill(sample, dt);
531 B2INFO(
"... channel " << chan <<
" OK (chi^2/ndf = " << chi2 / ndf
532 <<
", ndf = " << ndf <<
")");
539 unsigned scrodID,
unsigned chan,
540 double meanTimeDifference,
541 TH1F& Hchi2, TH1F& Hndf,
548 B2INFO(
"TimeBaseCalibration starts for channel#" << chan);
550 double pre_chi2 = 10000000.0;
551 unsigned num_small_dev = 0;
556 double this_chi2 =
Chisq(ntuple, xval);
557 if (this_chi2 < 0)
continue;
558 double deltaChi2 = pre_chi2 - this_chi2;
560 if (fabs(deltaChi2) <
m_deltamin) num_small_dev++;
562 pre_chi2 = this_chi2;
567 double chi2 =
Chisq(ntuple, xval);
568 Hchi2.SetBinContent(chan + 1, chi2);
569 Hndf.SetBinContent(chan + 1,
m_good);
574 for (
auto xi : xval) sum += xi;
581 HDeltaT.SetBinContent(chan + 1, DeltaT);
583 std::vector<double> timeInterval;
584 for (
int i = 0; i <
c_TimeAxisSize; i++)timeInterval.push_back(xval[i + 1] - xval[i]);
587 std::vector<double> sampleTimes;
588 for (
auto xi : xval) sampleTimes.push_back(xi);
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]");
595 "Time base corrections for " + forWhat,
"sample number",
"t [ns]");
598 std::string name =
"timeDiffcal_ch" + to_string(chan);
599 std::string title =
"Calibrated cal pulse time difference vs. sample for " + forWhat;
601 100, DeltaT - 0.5, DeltaT + 0.5);
602 Hcor.SetXTitle(
"sample number");
603 Hcor.SetYTitle(
"time difference [ns]");
604 Hcor.SetStats(kTRUE);
607 timeBase.
setTimeAxis(sampleTimes, sampleTimes.back() / 2);
609 for (
const auto& twoTimes : ntuple) {
610 if (!twoTimes.good)
continue;
611 double dt = timeBase.
getDeltaTime(0, twoTimes.t2, twoTimes.t1);
613 Hcor.Fill(sample, dt);
617 B2INFO(
"... channel " << chan <<
" OK (chi^2/ndf = " << chi2
618 <<
", ndf = " <<
m_good <<
")");
626 double wdth = xval[i + 1] - xval[i];
629 xval[i + 1] = xval[i + 1] + 0.5 * fabs(wdth) + 0.5 *
m_min_binwidth;
633 xval[i + 1] = xval[i + 1] + 0.5 * fabs(wdth) + 0.5 *
m_max_binwidth;
638 for (
int i = 0; i <
c_TimeAxisSize; i++) xval[i] = xval[i] - xval[0];
643 double chi2_0 =
Chisq(ntuple, xxval);
644 if (chi2_0 < 0) B2ERROR(
"iTBC chisq_0<0! xval has problem.");
647 TH1D hdrsamp_try(
"hdrsamp_try",
"dchi2/dx distribution", 100, -0.01, 0.01);
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];
659 double vx_it_step = dr_chi2[smp] *
m_xstep;
660 xval[smp] = xval[smp] - vx_it_step;
676 for (
const auto& twoTimes : ntuple) {
677 if (!twoTimes.good)
continue;
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]);
690 if (samp1 > samp0) cdt = ctdc2 - ctdc1;
693 if (cdt < m_dt_max && cdt >
m_dt_min) {
703 double mean = sum1 /
m_good;
712 const std::string& name,
713 const std::string& title,
714 const std::string& xTitle,
715 const std::string& yTitle)
const
717 if (vec.empty())
return;
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);
724 for (
unsigned i = 0; i < vec.size(); i++) h.SetBinContent(i + 1, vec[i]);
731 const vector<double>& err,
734 const string& xTitle,
735 const string& yTitle)
const
737 if (vec.empty())
return;
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());
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]);
752 const string& title)
const
754 int n = M.GetNrows();
755 TH2F h(name.c_str(), title.c_str(), n, 0, n, n, 0, n);
756 h.SetXTitle(
"columns");
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));
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
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.
unsigned m_method
method to use
@ 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
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 ¶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.
TOPTimeBaseCalibratorModule()
Constructor.
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.