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 interaction 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);
121 m_goodHits = TH2F(
"goodHits",
"pulse height vs. pulse width of all good hits",
122 100, 0, 10, 100, 0, 2000);
123 m_goodHits.SetXTitle(
"pulse width (FWHM) [ns]");
124 m_goodHits.SetYTitle(
"pulse height [ADC counts]");
126 "pulse height vs. pulse width of the first calibration pulse",
127 100, 0, 10, 100, 0, 2000);
131 "pulse height vs. pulse width of the second calibration pulse",
132 100, 0, 10, 100, 0, 2000);
146 for (
const auto& digit :
m_digits) {
147 if (digit.getModuleID() !=
m_moduleID)
continue;
148 if (digit.getHitQuality() != TOPDigit::c_Junk) {
149 m_goodHits.Fill(digit.getPulseWidth(), digit.getPulseHeight());
151 if (digit.getHitQuality() != TOPDigit::c_CalPulse)
continue;
152 double rawTime = digit.getRawTime();
153 double errScaleFactor = 1;
155 const auto* rawDigit = digit.getRelated<
TOPRawDigit>();
157 B2ERROR(
"No relation to TOPRawDigit - can't determine falling edge time error");
162 rawTime += rawDigit->
getFWHM();
163 errScaleFactor = rawDigit->getCFDFallingTimeError(1.0) / rawDigit->getCFDLeadingTimeError(1.0);
165 double t = rawTime + digit.getFirstWindow() *
c_WindowSize;
167 B2ERROR(
"Got negative sample number - digit ignored");
170 double et = digit.getTimeError() / sampleWidth * errScaleFactor;
172 B2ERROR(
"Time error is not given - digit ignored");
175 unsigned channel = digit.getChannel();
177 hits[channel].push_back(
Hit(t, et, digit.getPulseHeight(), digit.getPulseWidth()));
181 for (
unsigned channel = 0; channel <
c_NumChannels; channel++) {
182 const auto& channelHits = hits[channel];
183 if (channelHits.size() == 2) {
184 double t0 = channelHits[0].time;
185 double t1 = channelHits[1].time;
186 auto diff = fabs(t0 - t1);
189 double sig0 = channelHits[0].timeErr;
190 double sig1 = channelHits[1].timeErr;
191 double sigma =
sqrt(sig0 * sig0 + sig1 * sig1);
194 m_calPulseFirst.Fill(channelHits[0].pulseWidth, channelHits[0].pulseHeight);
195 m_calPulseSecond.Fill(channelHits[1].pulseWidth, channelHits[1].pulseHeight);
197 m_calPulseSecond.Fill(channelHits[0].pulseWidth, channelHits[0].pulseHeight);
198 m_calPulseFirst.Fill(channelHits[1].pulseWidth, channelHits[1].pulseHeight);
200 }
else if (channelHits.size() > 2) {
201 B2WARNING(
"More than two cal pulses per channel found - ignored");
217 const auto* feMap = feMapper.getMap(
m_moduleID, bs);
219 B2ERROR(
"No front-end map available for slot " <<
m_moduleID <<
", BS" << bs);
222 unsigned scrodID = feMap->getScrodID();
232 fileName +=
"_" + to_string(bs) +
"-scrod" +
233 to_string(scrodID) +
".root";
234 TFile* fout = TFile::Open(fileName.c_str(),
"recreate");
236 B2ERROR(
"Can't open the output file " << fileName);
246 B2INFO(
"Fitting time base corrections for SCROD " << scrodID
247 <<
", output file: " << fileName);
251 TH1F Hchan(
"channels",
"channels with cal pulse",
252 c_NumScrodChannels, 0, c_NumScrodChannels);
253 Hchan.SetXTitle(
"channel number");
254 Hchan.SetYTitle(
"double cal pulse counts");
255 TH1F Hsuccess(
"success",
"successfully fitted channels",
256 c_NumScrodChannels, 0, c_NumScrodChannels);
257 Hsuccess.SetXTitle(
"channel number");
258 TH1F Hchi2(
"chi2",
"normalized chi2",
259 c_NumScrodChannels, 0, c_NumScrodChannels);
260 Hchi2.SetXTitle(
"channel number");
261 Hchi2.SetYTitle(
"chi2/ndf");
262 TH1F Hndf(
"ndf",
"degrees of freedom",
263 c_NumScrodChannels, 0, c_NumScrodChannels);
264 Hndf.SetXTitle(
"channel number");
265 Hndf.SetYTitle(
"ndf");
266 TH1F HDeltaT(
"DeltaT",
"Fitted double pulse delay time",
267 c_NumScrodChannels, 0, c_NumScrodChannels);
268 HDeltaT.SetXTitle(
"channel number");
269 HDeltaT.SetYTitle(
"#Delta T [ns]");
273 for (
unsigned chan = 0; chan < c_NumScrodChannels; chan++) {
274 unsigned channel = chan + bs * c_NumScrodChannels;
276 Hchan.SetBinContent(chan + 1, ntuple.size());
279 if (ok) Hsuccess.Fill(chan);
281 B2INFO(
"... channel " << chan <<
" statistics too low ("
282 << ntuple.size() <<
" double cal pulses)");
300 unsigned scrodID,
unsigned chan,
301 TH1F& Hchi2, TH1F& Hndf,
307 string name =
"timeDiff_ch" + to_string(chan);
308 string forWhat =
"scrod " + to_string(scrodID) +
" channel " + to_string(chan);
309 string title =
"Cal pulse time difference vs. sample for " + forWhat;
312 prof.SetXTitle(
"sample number");
313 prof.SetYTitle(
"time difference [samples]");
315 name =
"sampleOccup_ch" + to_string(chan);
316 title =
"Occupancy for " + forWhat;
318 hist.SetXTitle(
"sample number");
319 hist.SetYTitle(
"entries per sample");
324 for (
int iter = 0; iter < 5; iter++) {
329 for (
auto& twoTimes : ntuple) {
331 double diff = twoTimes.t2 - twoTimes.t1;
332 double Ddiff = diff - profMeans[sample];
333 if (fabs(Ddiff) < 3 * sigma) {
334 prof.Fill(sample, diff);
339 twoTimes.good =
true;
341 twoTimes.good =
false;
345 profMeans[i] = prof.GetBinContent(i + 1);
347 if (n == 0)
return false;
350 sigma =
sqrt(x2 - x * x);
357 double meanTimeDifference = 0;
358 for (
const auto& x : profMeans) meanTimeDifference += x;
359 meanTimeDifference /= profMeans.size();
366 B2INFO(
"... channel " << chan <<
": "
367 << ntuple.size() <<
" double cal pulses)");
371 Hchi2, Hndf, HDeltaT);
373 return iterativeTBC(ntuple, scrodID, chan, meanTimeDifference,
374 Hchi2, Hndf, HDeltaT);
376 B2ERROR(
"Singuler value decomposition not implemented yet");
379 B2ERROR(
"Unknown method " <<
m_method);
387 unsigned scrodID,
unsigned chan,
388 double meanTimeDifference,
389 TH1F& Hchi2, TH1F& Hndf,
398 for (
const auto& twoTimes : ntuple) {
399 if (!twoTimes.good)
continue;
402 int i1 = int(twoTimes.t1);
404 int i2 = int(twoTimes.t2);
409 double relSigma = twoTimes.sigma / meanTimeDifference;
410 double sig2 = relSigma * relSigma;
412 for (
int jj = i1; jj < i2 + 1; jj++) {
414 for (
int kk = i1; kk < i2 + 1; kk++) {
416 A(j, k) += m[j] * m[k] / sig2;
424 string forWhat =
"scrod " + to_string(scrodID) +
" channel " + to_string(chan);
426 saveAsHistogram(A,
"matA_ch" + to_string(chan),
"Matrix for " + forWhat);
427 saveAsHistogram(b,
"vecB_ch" + to_string(chan),
"Right side for " + forWhat);
435 B2INFO(
"... channel " << chan <<
" failed");
442 x[k] += A(k, j) * b[j];
451 for (
const auto& twoTimes : ntuple) {
452 if (!twoTimes.good)
continue;
455 int i1 = int(twoTimes.t1);
457 int i2 = int(twoTimes.t2);
463 double relSigma = twoTimes.sigma / meanTimeDifference;
464 double sig2 = relSigma * relSigma;
465 chi2 += s * s / sig2;
468 Hchi2.SetBinContent(chan + 1, chi2 / ndf);
469 Hndf.SetBinContent(chan + 1, ndf);
474 for (
auto xi : x) sum += xi;
480 for (
auto& xi : x) xi *= DeltaT;
481 HDeltaT.SetBinContent(chan + 1, DeltaT);
486 vector<double> sampleTimes;
487 sampleTimes.push_back(0);
488 for (
auto xi : x) sampleTimes.push_back(xi + sampleTimes.back());
493 saveAsHistogram(x, err,
"dt_ch" + to_string(chan),
"Sample time bins for " + forWhat,
494 "sample number",
"#Delta t [ns]");
496 "Time base corrections for " + forWhat,
"sample number",
"t [ns]");
500 string name =
"timeDiffcal_ch" + to_string(chan);
501 string title =
"Calibrated cal pulse time difference vs. sample for " + forWhat;
503 100, DeltaT - 0.5, DeltaT + 0.5);
504 Hcor.SetXTitle(
"sample number");
505 Hcor.SetYTitle(
"time difference [ns]");
506 Hcor.SetStats(kTRUE);
509 timeBase.
setTimeAxis(sampleTimes, sampleTimes.back() / 2);
511 for (
const auto& twoTimes : ntuple) {
512 if (!twoTimes.good)
continue;
513 double dt = timeBase.
getDeltaTime(0, twoTimes.t2, twoTimes.t1);
515 Hcor.Fill(sample, dt);
519 B2INFO(
"... channel " << chan <<
" OK (chi^2/ndf = " << chi2 / ndf
520 <<
", ndf = " << ndf <<
")");
527 unsigned scrodID,
unsigned chan,
528 double meanTimeDifference,
529 TH1F& Hchi2, TH1F& Hndf,
536 B2INFO(
"TimeBaseCalibration starts for channel#" << chan);
538 double pre_chi2 = 10000000.0;
539 unsigned num_small_dev = 0;
544 double this_chi2 =
Chisq(ntuple, xval);
545 if (this_chi2 < 0)
continue;
546 double deltaChi2 = pre_chi2 - this_chi2;
548 if (fabs(deltaChi2) <
m_deltamin) num_small_dev++;
550 pre_chi2 = this_chi2;
555 double chi2 =
Chisq(ntuple, xval);
556 Hchi2.SetBinContent(chan + 1, chi2);
557 Hndf.SetBinContent(chan + 1,
m_good);
562 for (
auto xi : xval) sum += xi;
569 HDeltaT.SetBinContent(chan + 1, DeltaT);
571 std::vector<double> timeInterval;
572 for (
int i = 0; i <
c_TimeAxisSize; i++)timeInterval.push_back(xval[i + 1] - xval[i]);
575 std::vector<double> sampleTimes;
576 for (
auto xi : xval) sampleTimes.push_back(xi);
579 std::string forWhat =
"scrod " + to_string(scrodID) +
" channel " + to_string(chan);
580 saveAsHistogram(timeInterval,
"dt_ch" + to_string(chan),
"Sample time bins for " + forWhat,
581 "sample number",
"#Delta t [ns]");
583 "Time base corrections for " + forWhat,
"sample number",
"t [ns]");
586 std::string name =
"timeDiffcal_ch" + to_string(chan);
587 std::string title =
"Calibrated cal pulse time difference vs. sample for " + forWhat;
589 100, DeltaT - 0.5, DeltaT + 0.5);
590 Hcor.SetXTitle(
"sample number");
591 Hcor.SetYTitle(
"time difference [ns]");
592 Hcor.SetStats(kTRUE);
595 timeBase.
setTimeAxis(sampleTimes, sampleTimes.back() / 2);
597 for (
const auto& twoTimes : ntuple) {
598 if (!twoTimes.good)
continue;
599 double dt = timeBase.
getDeltaTime(0, twoTimes.t2, twoTimes.t1);
601 Hcor.Fill(sample, dt);
605 B2INFO(
"... channel " << chan <<
" OK (chi^2/ndf = " << chi2
606 <<
", ndf = " <<
m_good <<
")");
614 double wdth = xval[i + 1] - xval[i];
617 xval[i + 1] = xval[i + 1] + 0.5 * fabs(wdth) + 0.5 *
m_min_binwidth;
621 xval[i + 1] = xval[i + 1] + 0.5 * fabs(wdth) + 0.5 *
m_max_binwidth;
626 for (
int i = 0; i <
c_TimeAxisSize; i++) xval[i] = xval[i] - xval[0];
631 double chi2_0 =
Chisq(ntuple, xxval);
632 if (chi2_0 < 0) B2ERROR(
"iTBC chisq_0<0! xval has problem.");
635 TH1D hdrsamp_try(
"hdrsamp_try",
"dchi2/dx distribution", 100, -0.01, 0.01);
639 double chi2_ch =
Chisq(ntuple, xxval);
640 if (chi2_ch < 0)
continue;
641 dr_chi2[smp] = (chi2_ch - chi2_0) /
m_dev_step;
642 hdrsamp_try.Fill(dr_chi2[smp]);
643 xxval[smp] = xval[smp];
647 double vx_it_step = dr_chi2[smp] *
m_xstep;
648 xval[smp] = xval[smp] - vx_it_step;
664 for (
const auto& twoTimes : ntuple) {
665 if (!twoTimes.good)
continue;
667 int i1 = int(twoTimes.t1);
668 double fr = twoTimes.t1 - i1;
669 double samp0 = i1 % 256;
670 double ctdc1 = xxval[samp0] + fr * (xxval[samp0 + 1] - xxval[samp0]);
671 int i2 = int(twoTimes.t2);
672 fr = twoTimes.t2 - i2;
673 double samp1 = i2 % 256;
674 double ctdc2 = xxval[samp1] + fr * (xxval[samp1 + 1] - xxval[samp1]);
676 if (samp1 > samp0) cdt = ctdc2 - ctdc1;
679 if (cdt < m_dt_max && cdt >
m_dt_min) {
689 double mean = sum1 /
m_good;
698 const std::string& name,
699 const std::string& title,
700 const std::string& xTitle,
701 const std::string& yTitle)
703 if (vec.empty())
return;
705 TH1F h(name.c_str(), title.c_str(), vec.size(), 0, vec.size());
706 h.SetXTitle(xTitle.c_str());
707 h.SetYTitle(yTitle.c_str());
708 if (name.find(
"Fit") != string::npos) h.SetLineColor(2);
710 for (
unsigned i = 0; i < vec.size(); i++) h.SetBinContent(i + 1, vec[i]);
717 const vector<double>& err,
720 const string& xTitle,
721 const string& yTitle)
723 if (vec.empty())
return;
725 TH1F h(name.c_str(), title.c_str(), vec.size(), 0, vec.size());
726 h.SetXTitle(xTitle.c_str());
727 h.SetYTitle(yTitle.c_str());
729 for (
unsigned i = 0; i < vec.size(); i++) h.SetBinContent(i + 1, vec[i]);
730 for (
unsigned i = 0; i < err.size(); i++) h.SetBinError(i + 1, err[i]);
740 int n = M.GetNrows();
741 TH2F h(name.c_str(), title.c_str(), n, 0, n, n, 0, n);
742 h.SetXTitle(
"columns");
745 for (
int j = 0; j < n; j++) {
746 for (
int k = 0; k < n; k++) {
747 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 ...
double getFWHM() const
Returns signal full width half maximum.
Calibration constants of a single 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
minimum chisq change in an iteration.
double m_xstep
unit for an interaction 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
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 iterations 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]
@ 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
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 terminate() override
Termination action.
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 calculation.
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.
static void saveAsHistogram(const std::vector< double > &vec, const std::string &name, const std::string &title, const std::string &xTitle="", const std::string &yTitle="")
Save vector to histogram and write it out.
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.