5#include "PlottingUtils.hpp"
8#include <TDecompChol.h>
11#include <TFitResultPtr.h>
36 return "long L (odd strips)";
38 return "long R (even strips)";
40 return "short L (even strips)";
42 return "short R (odd strips)";
44 return "strip 0 (unsegmented)";
46 return "strip 17 (unsegmented)";
48 return "grid (seed channel)";
113 return o ? o->
enabled : kFALSE;
130 for (Int_t b = 0; b <
kNBins; b++) {
143 const Int_t b =
BinOf(dt_us * 1.0e-6);
146 const Double_t ex = TMath::Exp(-dt_us /
tau_us);
160 for (Int_t g = 0; g <
kNGROUPS; g++) {
173 for (Int_t g = 0; g <
kNGROUPS; g++) {
195 const Double_t l = TMath::Log10(dt_s);
196 if (l < kLogLo || l >=
kLogHi)
214 const Int_t n = Int_t(dt_us.size());
221 for (Int_t i = 0; i < n; i++)
222 prof->Fill(dt_us[i], dev[i]);
223 TF1 *f =
new TF1(
"ph_tau_fit_fn",
"[0] + [1] * exp(-x / [2]) + [3] * x",
225 const Double_t head = prof->GetBinContent(prof->FindBin(
kTauFitLoUs + 1.0));
226 const Double_t tail = prof->GetBinContent(prof->FindBin(
kTauFitHiUs - 5.0));
227 f->SetParameters(tail, head - tail, 10.0, 0.0);
228 f->SetParLimits(2, 0.5, 200.0);
229 TFitResultPtr r = prof->Fit(f,
"RQS0");
232 out.
p0 = f->GetParameter(0);
233 out.
p1 = f->GetParameter(1);
234 out.
p1err = f->GetParError(1);
235 out.
p2 = f->GetParameter(2);
236 out.
p2err = f->GetParError(2);
237 out.
p3 = f->GetParameter(3);
238 out.
chi2 = f->GetChisquare();
239 out.
ndf = f->GetNDF();
240 out.
flat = TMath::Abs(out.
p1) < 2.0 * out.
p1err || out.
p2 <= 0.51 ||
245 f->SetBit(TF1::kNotDraw);
250Int_t
AmpBinOf(Double_t e_prev, Double_t mode, Int_t n_amp) {
251 if (n_amp <= 1 || !(mode > 0.0))
254 const Int_t a = Int_t(e_prev / mode + 0.5);
255 return TMath::Min(a, n_amp - 1);
263 const char *times = latex ?
"#times" :
"x";
265 return Form(
"< 0.5%s beam", times);
267 return Form(
"%s %.1f%s beam", latex ?
"#geq" :
">=", a - 0.5, times);
268 return Form(
"~%d%s beam", a, times);
274 const std::map<std::pair<Int_t, Int_t>, TString> &cm =
276 for (std::map<std::pair<Int_t, Int_t>, TString>::const_iterator it =
278 it != cm.end(); ++it) {
279 const TString &name = it->second;
280 const Int_t idx = it->first.first * nch + it->first.second;
281 if (idx < 0 || idx >= Int_t(gm.size()))
283 if (name ==
"Strip0") {
287 if (name ==
"Strip17") {
291 if (name ==
"Grid") {
295 if (name.Length() < 2 || (name[0] !=
'L' && name[0] !=
'R'))
297 TString num = name(1, name.Length() - 1);
300 const Int_t s = num.Atoi();
303 const Bool_t odd = (s % 2) != 0;
304 const Bool_t is_long = (name[0] ==
'L') ? odd : !odd;
316TString NameOfIndex(Int_t idx) {
318 const std::map<std::pair<Int_t, Int_t>, TString> &cm =
320 std::map<std::pair<Int_t, Int_t>, TString>::const_iterator it =
321 cm.find(std::make_pair(idx / nch, idx % nch));
322 return it == cm.end() ? TString(
"") : it->second;
325Int_t IndexOfName(
const TString &want) {
327 const std::map<std::pair<Int_t, Int_t>, TString> &cm =
329 for (std::map<std::pair<Int_t, Int_t>, TString>::const_iterator it =
331 it != cm.end(); ++it)
332 if (it->second == want)
333 return it->first.first * nch + it->first.second;
337inline Int_t HitIndex(
const RawHit &h) {
352 Int_t Bin(Int_t a, Int_t b)
const {
return 1 + a *
kNBins + b; }
353 Int_t Low(Int_t a, Int_t b)
const {
354 return 1 + n_amp *
kNBins + a * n_free + b;
356 Int_t D(Int_t j, Int_t a)
const {
357 return 1 + n_amp *
kNBins + n_amp * n_free + 2 * (j * n_amp + a);
359 Int_t B(Int_t j, Int_t a)
const {
return D(j, a) + 1; }
361 return 1 + n_amp *
kNBins + n_amp * n_free + 2 * n_tau * n_amp;
363 Bool_t HasForm()
const {
return n_tau > 0; }
369 Double_t t_m_us = 0.0, t_f_us = 0.0, t_lo_us = 0.0;
370 std::vector<Double_t> tau_us, em, ef;
373 static const Int_t kTabN = 4096;
374 std::vector<Double_t> tab;
375 Double_t tab_lo = 0.0, tab_step = 0.0;
379 tab.assign(tau_us.size() * kTabN, 0.0);
380 for (
size_t j = 0; j < tau_us.size(); j++)
381 for (Int_t i = 0; i < kTabN; i++)
383 TMath::Exp(-TMath::Power(10.0, tab_lo + i * tab_step) / tau_us[j]);
386 inline Double_t Exp(Int_t j, Double_t l)
const {
387 Double_t u = (l - tab_lo) / tab_step;
389 return tab[j * kTabN];
391 return tab[j * kTabN + kTabN - 1];
392 const Int_t i = Int_t(u);
393 const Double_t f = u - i;
394 return tab[j * kTabN + i] * (1.0 - f) + tab[j * kTabN + i + 1] * f;
403 std::vector<Int_t> taus;
410 std::vector<Double_t> A;
411 std::vector<Double_t> b;
412 Double_t syy = 0.0, sy = 0.0;
414 void Init(Int_t np_) {
416 A.assign(
size_t(np) * np, 0.0);
421 void Add(
const Normal &o) {
422 for (
size_t i = 0; i < A.size(); i++)
424 for (
size_t i = 0; i < b.size(); i++)
430 inline Double_t &At(Int_t a, Int_t c) {
return A[size_t(a) * np + c]; }
431 inline Double_t Get(Int_t a, Int_t c)
const {
432 return a <= c ? A[size_t(a) * np + c] : A[size_t(c) * np + a];
437Double_t Shift(
const std::deque<Past> &d, Double_t t,
const Kernel &K,
440 for (
size_t k = 0; k < d.size(); k++) {
441 const Past &p = d[k];
442 const Double_t dt_s = (t - p.t) * 1.0e-12;
443 const Int_t b =
BinOf(dt_s);
446 const Int_t a =
AmpBinOf(p.e, mode, K.n_amp);
447 s += (K.form ? K.FormAt(a, dt_s * 1.0e6) : K.k[a][b]) * p.e;
453Double_t Predict(
const Kernel &K,
const FeatLayout &lay,
454 const std::vector<Double_t> &x) {
455 Double_t pred = K.intercept;
456 for (Int_t a = 0; a < K.n_amp; a++) {
458 for (Int_t b = 0; b < lay.n_free; b++)
459 pred += K.k_low[a][b] * x[lay.Low(a, b)];
461 pred += -K.c[a] * x[lay.D(K.j_tau, a)] + K.cb[a] * x[lay.B(K.j_tau, a)];
463 for (Int_t b = 0; b <
kNBins; b++)
464 pred += K.k_binned[a][b] * x[lay.Bin(a, b)];
472Bool_t SolveSubset(
const Normal &N,
const std::vector<Int_t> &keep, TVectorD &p,
474 const Double_t kRidge = 1.0e-8;
475 const Int_t nk = Int_t(keep.size());
480 for (Int_t a = 0; a < nk; a++) {
481 bk[a] = N.b[keep[a]];
482 for (Int_t b = 0; b < nk; b++)
483 Ak(a, b) = N.Get(keep[a], keep[b]);
486 for (Int_t a = 0; a < nk; a++)
487 scale[a] = Ak(a, a) > 0.0 ? 1.0 / TMath::Sqrt(Ak(a, a)) : 1.0;
490 for (Int_t a = 0; a < nk; a++) {
491 bs[a] = bk[a] * scale[a];
492 for (Int_t b = 0; b < nk; b++)
493 As(a, b) = Ak(a, b) * scale[a] * scale[b];
496 TDecompChol chol(As);
497 Bool_t solved = chol.Decompose();
500 TVectorD ps = chol.Solve(bs, solved);
505 for (Int_t a = 0; a < nk; a++) {
506 p[a] = ps[a] * scale[a];
507 drop += p[a] * bk[a];
509 return std::isfinite(drop);
515 const std::vector<RawHit> &hits;
516 const std::vector<Int_t> &group_of;
517 const std::vector<Int_t> &fit_ch;
518 const std::vector<Int_t> &long_ch;
519 const std::vector<Double_t> &mode;
520 std::vector<Double_t> &ev_e;
521 const std::vector<Double_t> &mean;
531 const FeatLayout &lay;
532 const FormGrid &grid;
537Bool_t Lookahead(
const ScanState &st,
size_t i0) {
538 for (Int_t k = 0; k < Int_t(st.fit_ch.size()); k++)
539 st.ev_e[st.fit_ch[k]] = 0.0;
540 Bool_t strip0_fired = st.strip0 < 0;
541 const ULong64_t t0 = st.hits[i0].timestamp;
545 const Int_t i = HitIndex(st.hits[i0]);
546 if (i >= 0 && i < st.nidx && st.group_of[i] !=
kNONE)
547 st.ev_e[i] = Double_t(st.hits[i0].energy);
549 for (
size_t j = i0 + 1;
550 j < st.hits.size() && st.hits[j].timestamp - t0 < st.window_ps; j++) {
551 const Int_t i = HitIndex(st.hits[j]);
552 if (i == st.strip0 && st.hits[j].energy > 0)
553 strip0_fired = kTRUE;
554 if (i < 0 || i >= st.nidx || st.group_of[i] ==
kNONE)
556 if (Double_t(st.hits[j].energy) > st.ev_e[i])
557 st.ev_e[i] = Double_t(st.hits[j].energy);
564Bool_t BeamFor(
const ScanState &st, Int_t g) {
565 const Int_t chain =
ChainOf(g);
566 for (Int_t k = 0; k < Int_t(st.long_ch.size()); k++) {
567 const Int_t c = st.long_ch[k];
568 if (!(st.mode[c] > 0.0))
570 const Double_t r = st.ev_e[c] / st.mode[c];
571 const Bool_t own = chain >= 0 &&
ChainOf(st.group_of[c]) == chain;
572 if (own ? (r < st.olo || r > st.ohi) : (r < st.blo || r > st.bhi))
582void Scan(
const ScanState &st, Int_t g,
const std::vector<size_t> &seeds_g,
583 const ScanNeeds &needs, std::vector<Double_t> &x,
584 const std::function<
void(Int_t,
const std::vector<Double_t> &,
585 Double_t, Double_t)> &fn) {
586 const Bool_t any_form =
587 st.lay.HasForm() && (needs.low || !needs.taus.empty());
588 std::vector<std::deque<Past>> past(st.nidx);
589 size_t next_seed = 0;
590 for (
size_t j = 0; j < st.hits.size(); j++) {
591 if (next_seed < seeds_g.size() && seeds_g[next_seed] == j) {
593 const Double_t tg = Double_t(st.hits[j].timestamp);
595 for (Int_t k = 0; k < Int_t(st.fit_ch.size()); k++) {
596 const Int_t c = st.fit_ch[k];
598 if (st.group_of[c] != g || !(st.ev_e[c] > 0.0))
600 std::deque<Past> &d = past[c];
601 while (!d.empty() && d.front().t < tg - st.keep_ps)
603 std::fill(x.begin(), x.end(), 0.0);
605 for (
size_t p = 0; p < d.size(); p++) {
606 const Double_t dt_s = (tg - d[p].t) * 1.0e-12;
610 const Double_t l_s = TMath::Log10(dt_s);
611 if (l_s < kLogLo || l_s >=
kLogHi)
614 const Int_t a =
AmpBinOf(d[p].e, st.mode[c], st.n_amp);
615 const Double_t e = d[p].e;
617 x[st.lay.Bin(a, b)] += e;
620 const Double_t dt_us = dt_s * 1.0e6;
621 if (dt_us < st.grid.t_lo_us) {
622 if (needs.low && b < st.lay.n_free)
623 x[st.lay.Low(a, b)] += e;
626 if (needs.taus.empty())
628 const Double_t l = l_s + 6.0;
629 const Bool_t past_freeze = dt_us > st.grid.t_f_us;
630 for (
size_t q = 0; q < needs.taus.size(); q++) {
631 const Int_t j = needs.taus[q];
632 const Double_t ex = st.grid.Exp(j, l);
633 x[st.lay.D(j, a)] += e * ex * st.grid.em[j];
635 x[st.lay.B(j, a)] += e * (st.grid.ef[j] - ex);
638 const Double_t dt_prev = d.empty() ? -1.0 : (tg - d.back().t) * 1.0e-12;
639 fn(c, x, st.ev_e[c] - st.mean[c], dt_prev);
642 const Int_t i = HitIndex(st.hits[j]);
643 if (i >= 0 && i < st.nidx && st.group_of[i] == g)
645 {Double_t(st.hits[j].timestamp), Double_t(st.hits[j].energy)});
654Double_t R2Of(
const Kernel &K,
const Normal &N,
const FeatLayout &lay) {
657 const Double_t ybar = N.sy / N.n;
658 const Double_t ss_tot = N.syy - N.n * ybar * ybar;
661 TVectorD p(lay.Size());
664 for (Int_t a = 0; a < K.n_amp; a++) {
666 for (Int_t b = 0; b < lay.n_free; b++)
667 p[lay.Low(a, b)] = K.k_low[a][b];
669 p[lay.D(K.j_tau, a)] = -K.c[a];
670 p[lay.B(K.j_tau, a)] = K.cb[a];
673 for (Int_t b = 0; b <
kNBins; b++)
674 p[lay.Bin(a, b)] = K.k_binned[a][b];
679 std::vector<Int_t> nz;
680 for (Int_t i = 0; i < lay.Size(); i++)
683 Double_t ss_res = N.syy;
684 for (
size_t u = 0; u < nz.size(); u++) {
685 ss_res -= 2.0 * p[nz[u]] * N.b[nz[u]];
686 for (
size_t v = 0; v < nz.size(); v++)
687 ss_res += p[nz[u]] * p[nz[v]] * N.Get(nz[u], nz[v]);
689 return 1.0 - ss_res / ss_tot;
692Bool_t FitKernel(
const Normal &N,
const FeatLayout &lay,
const FormGrid &grid,
693 Int_t given_j, Bool_t want_form,
Kernel &K, TString &why) {
697 K.n_free = lay.n_free;
698 K.t_m_us = grid.t_m_us;
699 K.t_f_us = grid.t_f_us;
700 K.t_lo_us = grid.t_lo_us;
704 why = Form(
"only %lld pairs", N.n);
707 const Double_t ybar = N.sy / N.n;
708 const Double_t ss_tot = N.syy - N.n * ybar * ybar;
709 if (!(ss_tot > 0.0)) {
713 const Int_t n_amp = lay.n_amp;
714 K.rms_before = TMath::Sqrt(ss_tot / N.n);
717 Bool_t binned_ok = kFALSE;
718 Double_t intercept_binned = 0.0;
720 std::vector<Int_t> keep(1, 0);
721 for (Int_t a = 0; a < n_amp; a++)
722 for (Int_t b = 0; b <
kNBins; b++)
723 if (N.Get(lay.Bin(a, b), lay.Bin(a, b)) > 0.0)
724 keep.push_back(lay.Bin(a, b));
727 if (SolveSubset(N, keep, p, drop)) {
728 const Double_t ss_res = N.syy - drop;
729 for (Int_t i = 0; i < Int_t(keep.size()); i++) {
731 intercept_binned = p[i];
733 K.k_binned[(keep[i] - 1) /
kNBins][(keep[i] - 1) %
kNBins] = p[i];
735 K.r2_binned = 1.0 - ss_res / ss_tot;
736 K.rms_after_binned = TMath::Sqrt(TMath::Max(0.0, ss_res) / N.n);
737 binned_ok = K.r2_binned > 0.0;
745 Double_t best_res = 0.0, given_res = 0.0;
747 TVectorD best_p, given_p;
748 std::vector<Int_t> best_keep, given_keep;
749 for (Int_t j = 0; j < lay.n_tau; j++) {
752 std::vector<Int_t> keep(1, 0);
753 for (Int_t a = 0; a < n_amp; a++) {
754 for (Int_t b = 0; b < lay.n_free; b++)
755 if (N.Get(lay.Low(a, b), lay.Low(a, b)) > 0.0)
756 keep.push_back(lay.Low(a, b));
757 if (N.Get(lay.D(j, a), lay.D(j, a)) > 0.0)
758 keep.push_back(lay.D(j, a));
759 if (N.Get(lay.B(j, a), lay.B(j, a)) > 0.0)
760 keep.push_back(lay.B(j, a));
764 if (!SolveSubset(N, keep, p, drop))
766 const Double_t ss_res = N.syy - drop;
769 given_p.ResizeTo(p.GetNrows());
774 if (free_j < 0 || ss_res < best_res) {
777 best_p.ResizeTo(p.GetNrows());
783 K.tau_free_us = grid.tau_us[free_j];
784 K.r2_form_free = 1.0 - best_res / ss_tot;
788 if (given_j >= 0 && given_keep.empty())
789 why = Form(
"form at the given tau %.1f us did not solve; profiled tau "
791 grid.tau_us[given_j]);
792 const Bool_t use_given = given_j >= 0 && !given_keep.empty();
793 const Int_t j = use_given ? given_j : free_j;
795 best_res = given_res;
796 best_p.ResizeTo(given_p.GetNrows());
798 best_keep = given_keep;
803 K.tau_us = grid.tau_us[j];
804 K.tau_given = use_given;
806 K.tau_free_us = K.tau_us;
807 K.r2_form_free = 1.0 - best_res / ss_tot;
809 for (Int_t i = 0; i < Int_t(best_keep.size()); i++) {
810 const Int_t f = best_keep[i];
812 K.intercept_form = best_p[i];
815 Bool_t placed = kFALSE;
816 for (Int_t a = 0; a < n_amp && !placed; a++) {
817 for (Int_t b = 0; b < lay.n_free && !placed; b++)
818 if (f == lay.Low(a, b)) {
819 K.k_low[a][b] = best_p[i];
822 if (!placed && f == lay.D(j, a)) {
825 }
else if (!placed && f == lay.B(j, a)) {
831 K.r2_form = 1.0 - best_res / ss_tot;
832 K.rms_after_form = TMath::Sqrt(TMath::Max(0.0, best_res) / N.n);
833 if (!(K.r2_form > 0.0))
840 K.form = want_form && K.form_ok;
842 K.intercept = K.intercept_form;
844 K.rms_after = K.rms_after_form;
845 for (Int_t a = 0; a < n_amp; a++)
846 for (Int_t b = 0; b <
kNBins; b++)
850 K.intercept = intercept_binned;
852 K.rms_after = K.rms_after_binned;
853 for (Int_t a = 0; a < n_amp; a++)
854 for (Int_t b = 0; b <
kNBins; b++)
855 K.k[a][b] = K.k_binned[a][b];
857 if (!K.ok && why.IsNull())
858 why =
"binned fit did not solve";
865void AccumulateClassBlock(Normal &Nc,
const std::vector<Double_t> &xx,
866 Double_t y,
const std::vector<Int_t> &v) {
867 for (
size_t ia = 0; ia < v.size(); ia++) {
868 const Int_t i = v[ia];
869 const Double_t xi = xx[i];
872 for (
size_t ib = ia; ib < v.size(); ib++)
873 Nc.At(i, v[ib]) += xi * xx[v[ib]];
880 std::vector<Normal> &N;
881 const FeatLayout &lay;
883 Int_t bin_lo, bin_hi, low_lo, low_hi;
884 std::vector<Int_t> &nz_bin;
885 std::vector<Int_t> &nz_low;
886 std::vector<std::vector<Int_t>> &nz_tau;
888 void operator()(Int_t c,
const std::vector<Double_t> &xx, Double_t y,
897 for (Int_t i = bin_lo; i < bin_hi; i++)
901 for (Int_t i = low_lo; i < low_hi; i++)
904 for (Int_t j = 0; j < lay.n_tau; j++) {
906 for (Int_t a = 0; a < n_amp; a++) {
907 if (xx[lay.D(j, a)] != 0.0)
908 nz_tau[j].push_back(lay.D(j, a));
909 if (xx[lay.B(j, a)] != 0.0)
910 nz_tau[j].push_back(lay.B(j, a));
913 AccumulateClassBlock(Nc, xx, y, nz_bin);
914 AccumulateClassBlock(Nc, xx, y, nz_low);
915 for (Int_t j = 0; j < lay.n_tau; j++) {
916 const std::vector<Int_t> &vt = nz_tau[j];
919 AccumulateClassBlock(Nc, xx, y, vt);
920 for (
size_t ia = 0; ia < nz_low.size(); ia++)
921 for (
size_t ib = 0; ib < vt.size(); ib++)
922 Nc.At(nz_low[ia], vt[ib]) += xx[nz_low[ia]] * xx[vt[ib]];
930 std::vector<Double_t> &dt_us;
931 std::vector<Double_t> &dev;
933 void operator()(Int_t,
const std::vector<Double_t> &, Double_t y,
936 dt_us.push_back(dt_prev);
946 const FeatLayout &lay;
949 void operator()(Int_t c,
const std::vector<Double_t> &xx, Double_t y,
951 const Kernel &K = res.kernel_ch[c];
954 const Double_t pred = Predict(K, lay, xx);
955 const Double_t after = y - pred;
956 res.dev_vs_pred[g]->Fill(pred, y);
957 res.dev_before[g]->Fill(y);
958 res.dev_after[g]->Fill(after);
960 const Double_t l = TMath::Log10(dt_prev * 1.0e6);
961 res.dtprev_before[g]->Fill(l, y);
962 res.dtprev_after[g]->Fill(l, after);
963 res.dtprev_before_ch[c]->Fill(l, y);
964 res.dtprev_after_ch[c]->Fill(l, after);
971Bool_t
Measure(std::vector<RawHit> &hits,
const std::vector<Int_t> &group_of,
972 Result &res,
const TString &file_label) {
973 res.
n_hits = Long64_t(hits.size());
976 Bool_t sorted = kTRUE;
977 for (
size_t j = 1; j < hits.size() && sorted; j++)
978 sorted = hits[j].timestamp >= hits[j - 1].timestamp;
981 std::stable_sort(hits.begin(), hits.end(),
982 [](
const RawHit &a,
const RawHit &b) {
983 return a.timestamp < b.timestamp;
989 const Int_t strip0 = IndexOfName(
"Strip0");
991 std::cerr <<
" " << file_label
992 <<
": pulse history: no reference channel in the map"
999 const Int_t nidx = Int_t(group_of.size());
1000 const Int_t n_amp = TMath::Max(
1004 std::vector<Int_t> fit_ch, long_ch;
1005 for (Int_t i = 0; i < nidx; i++) {
1006 if (group_of[i] ==
kNONE)
1008 fit_ch.push_back(i);
1010 long_ch.push_back(i);
1012 if (long_ch.empty())
1018 const Int_t nb = 128;
1019 std::vector<std::vector<Long64_t>> spec(nidx, std::vector<Long64_t>(nb, 0));
1020 for (
size_t j = 0; j < hits.size(); j++) {
1021 const RawHit &h = hits[j];
1022 const Int_t i = HitIndex(h);
1023 if (i < 0 || i >= nidx || group_of[i] ==
kNONE)
1025 const Int_t b = Int_t(Double_t(h.energy) / emax * nb);
1026 if (b >= 0 && b < nb)
1029 res.
mode.assign(nidx, 0.0);
1030 for (Int_t k = 0; k < Int_t(fit_ch.size()); k++) {
1031 const Int_t i = fit_ch[k];
1033 for (Int_t b = nb / 16; b < nb; b++)
1034 if (best < 0 || spec[i][b] > spec[i][best])
1036 res.
mode[i] = best >= 0 ? (best + 0.5) * emax / nb : 0.0;
1038 const std::vector<Double_t> &mode = res.
mode;
1044 std::vector<size_t> seeds[
kNGROUPS];
1045 std::vector<Double_t> mean(nidx, 0.0);
1046 std::vector<Double_t> ev_e(nidx, 0.0);
1051 Bool_t want_form = kFALSE;
1052 for (Int_t g = 1; g <
kNGROUPS; g++)
1059 grid.t_f_us = 2.0 * rise + flat;
1060 grid.t_lo_us = grid.t_f_us - grid.t_m_us;
1063 const Double_t tau =
1066 grid.tau_us.push_back(tau);
1067 grid.em.push_back(TMath::Exp(-grid.t_m_us / tau));
1068 grid.ef.push_back(TMath::Exp(-grid.t_f_us / tau));
1074 for (Int_t g = 0; g <
kNGROUPS; g++)
1077 for (Int_t g = 1; g <
kNGROUPS; g++) {
1081 for (Int_t j =
kNTauGrid; j < Int_t(grid.tau_us.size()); j++)
1082 if (TMath::Abs(grid.tau_us[j] - tau) < 1.0e-6)
1084 if (given_j[g] < 0) {
1085 given_j[g] = Int_t(grid.tau_us.size());
1086 grid.tau_us.push_back(tau);
1087 grid.em.push_back(TMath::Exp(-grid.t_m_us / tau));
1088 grid.ef.push_back(TMath::Exp(-grid.t_f_us / tau));
1094 lay.n_tau = Int_t(grid.tau_us.size());
1097 lay.n_free = want_form ? TMath::Max(0,
BinOf(grid.t_lo_us * 1.0e-6) + 1) : 0;
1098 const Int_t np = lay.Size();
1099 const Double_t keep_ps = TMath::Power(10.0,
kLogHi) * 1.0e12;
1100 std::vector<Double_t> x(np, 0.0);
1101 const ScanState st = {hits, group_of, fit_ch, long_ch, mode, ev_e,
1102 mean, nidx, strip0, window_ps, olo, ohi,
1103 blo, bhi, n_amp, keep_ps, lay, grid};
1104 std::vector<Long64_t> nmean(nidx, 0);
1105 for (
size_t j = 0; j < hits.size(); j++) {
1106 const RawHit &h = hits[j];
1107 if (HitIndex(h) != ref)
1109 if (Double_t(h.energy) < ref_lo || Double_t(h.energy) > ref_hi)
1112 if (!Lookahead(st, j))
1114 for (Int_t g = 1; g <
kNGROUPS; g++) {
1115 if (!BeamFor(st, g))
1117 seeds[g].push_back(j);
1118 for (Int_t k = 0; k < Int_t(fit_ch.size()); k++) {
1119 const Int_t c = fit_ch[k];
1120 if (group_of[c] == g && ev_e[c] > 0.0) {
1128 for (Int_t g = 1; g <
kNGROUPS; g++)
1132 std::cerr <<
" " << file_label <<
": pulse history: only "
1137 for (Int_t k = 0; k < Int_t(fit_ch.size()); k++) {
1138 const Int_t c = fit_ch[k];
1139 mean[c] = nmean[c] ? mean[c] / Double_t(nmean[c]) : 0.0;
1144 std::vector<Normal> N(nidx);
1145 for (Int_t k = 0; k < Int_t(fit_ch.size()); k++)
1146 N[fit_ch[k]].Init(np);
1149 ScanNeeds all_needs;
1150 for (Int_t j = 0; j < lay.n_tau; j++)
1151 all_needs.taus.push_back(j);
1152 std::vector<Int_t> nz_bin, nz_low;
1153 std::vector<std::vector<Int_t>> nz_tau(lay.n_tau);
1154 const Int_t bin_lo = 1, bin_hi = 1 + n_amp *
kNBins;
1155 const Int_t low_lo = bin_hi, low_hi = bin_hi + n_amp * lay.n_free;
1156 for (Int_t g = 1; g <
kNGROUPS; g++) {
1160 FitSample fit_sample{N, lay, n_amp, bin_lo, bin_hi,
1161 low_lo, low_hi, nz_bin, nz_low, nz_tau};
1162 Scan(st, g, seeds[g], all_needs, x, fit_sample);
1167 for (Int_t k = 0; k < Int_t(fit_ch.size()); k++)
1168 res.
name_ch[fit_ch[k]] = NameOfIndex(fit_ch[k]);
1170 Bool_t any = kFALSE;
1171 for (Int_t g = 1; g <
kNGROUPS; g++) {
1180 for (Int_t k = 0; k < Int_t(fit_ch.size()); k++)
1181 if (group_of[fit_ch[k]] == g)
1182 Ng.Add(N[fit_ch[k]]);
1185 G.
n_beam = Long64_t(seeds[g].size());
1187 std::cerr <<
" " << file_label <<
": pulse history " <<
GroupName(g)
1188 <<
": " << why << std::endl;
1191 for (Int_t k = 0; k < Int_t(fit_ch.size()); k++) {
1192 const Int_t c = fit_ch[k];
1193 if (group_of[c] != g)
1199 FitKernel(N[c], lay, grid, given_j[g],
GroupWantsForm(g), K, why_c);
1202 why_c = Form(
"only %lld pairs, fewer than %lld", N[c].n,
1204 const Long64_t n_own = N[c].n;
1209 }
else if (!why_c.IsNull()) {
1211 << res.
name_ch[c] <<
": " << why_c << std::endl;
1215 K.
r2_group = G.
ok ? R2Of(G, N[c], lay) : 0.0;
1225 for (Int_t g = 1; g <
kNGROUPS; g++) {
1229 std::vector<Double_t> dt_us, dev;
1234 TauSample tau_sample{dt_us, dev};
1235 Scan(st, g, seeds[g], none, x, tau_sample);
1242 const TString tag = file_label;
1244 for (Int_t g = 1; g <
kNGROUPS; g++) {
1247 new TH2D(Form(
"h_ph_dev_vs_pred_%s_%s", gn, tag.Data()),
1248 ";Predicted Deviation [ADC];Measured Deviation [ADC]", 120,
1249 -600.0, 600.0, 120, -600.0, 600.0);
1251 Form(
"h_ph_dev_before_%s_%s", gn, tag.Data()),
1252 Form(
";%s #minus Mean %s [ADC];Events", gn, gn), 240, -600.0, 600.0);
1254 Form(
"h_ph_dev_after_%s_%s", gn, tag.Data()),
1255 Form(
";%s #minus Mean %s [ADC];Events", gn, gn), 240, -600.0, 600.0);
1257 Form(
"p_ph_dtprev_before_%s_%s", gn, tag.Data()),
1258 Form(
";log_{10}(#Deltat [#mus]);%s #minus Mean %s [ADC]", gn, gn), 48,
1261 Form(
"p_ph_dtprev_after_%s_%s", gn, tag.Data()),
1262 Form(
";log_{10}(#Deltat [#mus]);%s #minus Mean %s [ADC]", gn, gn), 48,
1264 res.
shift[g] =
new TH1D(Form(
"h_ph_shift_%s_%s", gn, tag.Data()),
1265 ";Applied Shift [ADC];Hits", 240, -600.0, 600.0);
1266 TH1 *hists[6] = {
static_cast<TH1 *
>(res.
dev_vs_pred[g]),
1271 static_cast<TH1 *
>(res.
shift[g])};
1272 for (Int_t h = 0; h < 6; h++)
1273 hists[h]->SetDirectory(
nullptr);
1279 for (Int_t k = 0; k < Int_t(fit_ch.size()); k++) {
1280 const Int_t c = fit_ch[k];
1281 const char *cn = res.
name_ch[c].Data();
1283 Form(
"p_ph_dtprev_before_ch_%s_%s", cn, tag.Data()),
1284 Form(
";log_{10}(#Deltat [#mus]);%s #minus Mean %s [ADC]", cn, cn), 48,
1287 Form(
"p_ph_dtprev_after_ch_%s_%s", cn, tag.Data()),
1288 Form(
";log_{10}(#Deltat [#mus]);%s #minus Mean %s [ADC]", cn, cn), 48,
1293 for (Int_t g = 1; g <
kNGROUPS; g++) {
1299 needs.bins = kFALSE;
1301 for (Int_t k = 0; k < Int_t(fit_ch.size()); k++) {
1303 if (group_of[fit_ch[k]] != g || !K.
ok)
1310 if (K.
j_tau >= 0 && std::find(needs.taus.begin(), needs.taus.end(),
1311 K.
j_tau) == needs.taus.end())
1312 needs.taus.push_back(K.
j_tau);
1314 DiagSample diag_sample{res, lay, g};
1315 Scan(st, g, seeds[g], needs, x, diag_sample);
1320void Apply(std::vector<RawHit> &hits,
const std::vector<Int_t> &group_of,
1322 const Int_t nidx = Int_t(group_of.size());
1323 const Double_t keep_ps =
Constants::cfg.PULSE_HISTORY_APPLY_MAX_US * 1.0e6;
1324 std::vector<std::deque<Past>> past(nidx);
1325 Double_t shift_sum[
kNGROUPS] = {0};
1327 for (
size_t j = 0; j < hits.size(); j++) {
1328 RawHit &h = hits[j];
1329 const Int_t i = HitIndex(h);
1330 if (i < 0 || i >= nidx || group_of[i] ==
kNONE)
1332 const Int_t g = group_of[i];
1333 const Double_t t = Double_t(h.timestamp);
1334 const Double_t e_raw = Double_t(h.energy);
1335 std::deque<Past> &d = past[i];
1336 while (!d.empty() && d.front().t < t - keep_ps)
1343 Shift(d, t, *K, i < Int_t(res.
mode.size()) ? res.
mode[i] : 0.0);
1344 Double_t e = e_raw - s;
1350 }
else if (e > 65535.0) {
1353 h.energy = UShort_t(e + 0.5);
1358 res.
shift[g]->Fill(-s);
1361 d.push_back({t, e_raw});
1363 for (Int_t g = 0; g <
kNGROUPS; g++)
1364 res.
mean_shift[g] = shift_n[g] ? shift_sum[g] / shift_n[g] : 0.0;
1371TString FormLines(
const Kernel &K,
const char *indent) {
1372 std::ostringstream s;
1373 s << Form(
"%sform: tau %.1f us (%s) R^2 %.3f vs binned %.3f rms %.1f vs "
1375 indent, K.tau_us, K.tau_given ?
"given" :
"profiled", K.r2_form,
1376 K.r2_binned, K.rms_after_form, K.rms_after_binned,
1377 (!K.form && K.form_ok) ?
" [bins applied]" :
"")
1382 const Double_t ratio = K.tau_us > 0.0 ? K.tau_free_us / K.tau_us : 0.0;
1383 s << Form(
"%s profile prefers tau %.1f us (%.2f x given, grid step "
1384 "1.15) R^2 %.4f there vs %.4f at given%s",
1385 indent, K.tau_free_us, ratio, K.r2_form_free, K.r2_form,
1386 (ratio > 0.0 && ratio < 1.0 / 1.15) || ratio > 1.15
1387 ?
" <-- given tau not preferred"
1391 for (Int_t a = 0; a < K.n_amp; a++)
1392 s << Form(
"%s band %d: c %+.4f (undershoot > 0) c_B %+.5f W_eff %.0f "
1394 indent, a, K.c[a], K.cb[a], K.EffectiveWindowUs(a))
1399const char *StatusOf(
const Kernel &K, Bool_t enabled) {
1404 return K.form ?
"FORM " :
"BINNED ";
1409TString
Report(
const Result &res,
const TString &file_label, Bool_t detail) {
1410 std::ostringstream s;
1411 s << Form(
" pulse history %s: %lld hits%s, %lld reference seeds, %lld "
1412 "beam-like events; %lld hits corrected, %lld clamped to 0",
1413 file_label.Data(), res.
n_hits,
1414 res.
sorted_input ?
"" :
" (SORTED: input was not time ordered)",
1417 for (Int_t g = 1; g <
kNGROUPS; g++) {
1421 if (!detail && !res.
enabled[g])
1423 s << Form(
" %-22s %s beam events %lld n %lld R^2 %.3f rms %.1f -> "
1424 "%.1f ADC mean shift %.1f ADC clamped %lld [group fit]",
1436 s << FormLines(K,
" ");
1437 for (Int_t a = 0; a < K.
n_amp; a++) {
1441 s << Form(
" kernel, previous pulse %s:",
1443 for (Int_t b = 0; b <
kNBins; b++)
1452 " tau fit: flat (A %.1f +- %.1f ADC within 2 sigma of 0)",
1456 s << Form(
" tau fit: %.1f +- %.1f us (A %+.1f +- %.1f ADC,"
1457 " level %+.1f, baseline lift %+.4f ADC/us, chi2/ndf %.1f/%d)",
1463 for (Int_t c = 0; c < Int_t(res.
kernel_ch.size()); c++) {
1467 s << Form(
" %-8s %s n %-9lld R^2 %.3f rms %.1f -> %.1f ADC",
1468 res.
name_ch[c].Data(), StatusOf(C, kTRUE), C.
n, C.
r2,
1471 s << Form(
" [group kernel: %s]", C.
why_group.Data()) << std::endl;
1479 for (Int_t a = 0; a < C.
n_amp; a++)
1480 s << Form(
" %+.3f", C.
c[a]);
1481 s << Form(
" (binned R^2 %.3f, group kernel %.3f)", C.
r2_binned,
1486 const Int_t b_from =
BinOf(4.7e-6);
1487 const Double_t from_us =
1490 Double_t kmax = 0.0;
1491 for (Int_t a = 0; a < C.
n_amp; a++)
1492 for (Int_t b = b_from; b <
kNBins; b++)
1493 if (TMath::Abs(C.
k[a][b]) > TMath::Abs(kmax))
1495 s << Form(
" max|k| beyond %.1f us %+.3f (group kernel R^2 %.3f)",
1508TLegend *RightLegend(Int_t nentries, Bool_t header, Bool_t top,
1509 Double_t x1 = 0.62, Int_t ncols = 1) {
1510 const Int_t nrows = (header ? 1 : 0) + (nentries + ncols - 1) / ncols;
1511 const Double_t h = 0.048 * nrows + 0.02;
1512 TLegend *leg = top ? PlottingUtils::AddLegend(x1, 0.89, 0.87 - h, 0.87)
1513 : PlottingUtils::AddLegend(x1, 0.89, 0.15, 0.15 + h);
1514 leg->SetNColumns(ncols);
1520void DrawKernelFigure(
const Kernel &K,
const TString &subdir,
1521 const char *fname) {
1522 const Int_t colors[
kMAX_AMP_BINS] = {kBlack, kRed + 1, kAzure + 1,
1523 kGreen + 2, kOrange + 7, kMagenta + 1};
1525 TCanvas *c = PlottingUtils::GetConfiguredCanvas(kFALSE);
1526 Double_t ylo = 0.0, yhi = 0.0;
1527 for (Int_t a = 0; a < K.n_amp; a++)
1528 for (Int_t b = 0; b <
kNBins; b++) {
1529 ylo = TMath::Min(ylo, K.k_binned[a][b]);
1530 yhi = TMath::Max(yhi, K.k_binned[a][b]);
1532 TH1F *frame = c->DrawFrame(xlo, ylo - 0.02, xhi, yhi + 0.02);
1533 frame->SetTitle(
";log_{10}(#Deltat [#mus]);"
1534 "Relative Amplitude Shift");
1535 TLegend *leg =
nullptr;
1536 if (K.n_amp > 1 || K.form_ok) {
1539 leg = RightLegend(K.n_amp * (K.form_ok ? 2 : 1), K.form_ok,
1540 !(TMath::Abs(ylo) > yhi), 0.45);
1542 leg->SetHeader(Form(
"#tau = %.0f #mus%s", K.tau_us,
1543 K.tau_given ?
" (given)" :
" (profiled)"));
1545 for (Int_t a = 0; a < K.n_amp; a++) {
1546 TGraph *gr =
new TGraph();
1547 for (Int_t b = 0; b <
kNBins; b++)
1548 gr->SetPoint(b, xlo + (b + 0.5) * (xhi - xlo) /
kNBins, K.k_binned[a][b]);
1549 gr->SetMarkerStyle(20);
1552 gr->SetLineWidth(2);
1553 gr->Draw(K.form_ok ?
"P SAME" :
"PL SAME");
1555 const TString band =
BandLabel(a, K.n_amp, kTRUE);
1556 const char *sep = K.n_amp > 1 ?
", " :
"";
1559 Form(
"%s%s%s%s", band.Data(), sep,
1560 K.n_amp > 1 ?
"bins" :
"Bins",
1561 K.form_ok && !K.form ?
" (applied)" :
""),
1564 const Int_t nf = 200;
1565 TGraph *gf =
new TGraph();
1566 for (Int_t i = 0; i < nf; i++) {
1567 const Double_t l = xlo + (i + 0.5) * (xhi - xlo) / nf;
1568 gf->SetPoint(i, l, K.FormAt(a, TMath::Power(10.0, l)));
1571 gf->SetLineWidth(2);
1572 gf->SetLineStyle(K.form ? 1 : 2);
1575 Form(
"%s%s%s%s", band.Data(), sep,
1576 K.n_amp > 1 ?
"form" :
"Form",
1577 K.form ?
" (applied)" :
""),
1583 TLine *zero =
new TLine(xlo, 0.0, xhi, 0.0);
1584 zero->SetLineStyle(2);
1586 PlottingUtils::SaveFigure(c, fname, subdir, PlotSaveOptions::kLINEAR);
1591Int_t ChannelColor(Int_t k) {
1592 const Int_t colors[16] = {kBlack, kRed + 1, kAzure + 1, kGreen + 2,
1593 kOrange + 7, kMagenta + 1, kCyan + 2, kYellow + 2,
1594 kGray + 2, kRed - 7, kAzure - 3, kGreen - 6,
1595 kOrange - 3, kMagenta - 7, kCyan - 6, kPink + 7};
1596 return colors[k % 16];
1602 Int_t group = 0, board = -1, channel = -1, n_amp = 1;
1603 Char_t name[32] = {0};
1604 Bool_t ok = kFALSE, enabled = kTRUE, from_group = kFALSE, form = kFALSE,
1605 form_ok = kFALSE, form_tau_given = kFALSE;
1606 Double_t form_tau_us = 0.0, form_tau_free_us = 0.0, r2_form_free = 0.0,
1607 r2_form = 0.0, r2_binned = 0.0, r2_group = 0.0;
1611 Double_t tau_us = 0.0, tau_err_us = 0.0, tau_p0 = 0.0, tau_p1 = 0.0,
1613 Bool_t tau_ok = kFALSE, tau_flat = kTRUE;
1614 Double_t intercept = 0.0, r2 = 0.0, rms_before = 0.0, rms_after = 0.0,
1615 mean_shift = 0.0, apply_max_us = 0.0;
1616 Long64_t n = 0, n_beam = 0, n_corrected = 0, n_clamped = 0;
1621void FillPulseRow(PulseRow &row,
const Kernel &K, Int_t g, Int_t idx,
1622 const Result &res, Int_t nch, TTree *t) {
1624 row.board = idx >= 0 ? idx / nch : -1;
1625 row.channel = idx >= 0 ? idx % nch : -1;
1626 strncpy(row.name, idx >= 0 ? res.name_ch[idx].Data() :
GroupTag(g),
1627 sizeof(row.name) - 1);
1628 row.name[
sizeof(row.name) - 1] = 0;
1630 row.enabled = res.enabled[g];
1631 row.from_group = K.from_group;
1633 row.form_ok = K.form_ok;
1634 row.form_tau_us = K.tau_us;
1635 row.form_tau_given = K.tau_given;
1636 row.form_tau_free_us = K.tau_free_us;
1637 row.r2_form_free = K.r2_form_free;
1638 row.r2_form = K.r2_form;
1639 row.r2_binned = K.r2_binned;
1640 row.r2_group = K.r2_group;
1641 row.n_amp = K.n_amp;
1643 row.form_c[a] = K.c[a];
1644 row.form_cb[a] = K.cb[a];
1645 for (Int_t b = 0; b <
kNBins; b++) {
1646 row.k[a *
kNBins + b] = K.k[a][b];
1647 row.k_binned[a *
kNBins + b] = K.k_binned[a][b];
1650 for (Int_t b = 0; b <
kNBins; b++)
1652 row.intercept = K.intercept;
1654 row.rms_before = K.rms_before;
1655 row.rms_after = K.rms_after;
1656 row.mean_shift = res.mean_shift[g];
1658 row.tau_ok = K.tau.ok;
1659 row.tau_flat = K.tau.flat;
1660 row.tau_us = K.tau.ok ? K.tau.p2 : 0.0;
1661 row.tau_err_us = K.tau.ok ? K.tau.p2err : 0.0;
1662 row.tau_p0 = K.tau.p0;
1663 row.tau_p1 = K.tau.p1;
1664 row.tau_p3 = K.tau.p3;
1666 row.n_beam = res.n_beam_events;
1667 row.n_corrected = res.n_corrected;
1668 row.n_clamped = res.n_clamped;
1678 const TString subdir =
"pulse_history/" + file_label;
1679 const TString subdir_ch = subdir +
"/channels";
1682 for (Int_t g = 1; g <
kNGROUPS; g++)
1684 DrawKernelFigure(res.
kernel[g], subdir, Form(
"kernel_%s",
GroupTag(g)));
1686 for (Int_t c = 0; c < Int_t(res.
kernel_ch.size()); c++)
1688 DrawKernelFigure(res.
kernel_ch[c], subdir_ch,
1689 Form(
"kernel_%s", res.
name_ch[c].Data()));
1692 for (Int_t g = 1; g <
kNGROUPS; g++) {
1696 std::vector<Int_t> chans;
1697 for (Int_t c = 0; c < Int_t(res.
kernel_ch.size()); c++)
1702 const Int_t band = G.
n_amp > 1 ? 1 : 0;
1703 Double_t ylo = 0.0, yhi = 0.0;
1704 for (
size_t k = 0; k < chans.size(); k++)
1705 for (Int_t b = 0; b <
kNBins; b++) {
1706 ylo = TMath::Min(ylo, res.
kernel_ch[chans[k]].k[band][b]);
1707 yhi = TMath::Max(yhi, res.
kernel_ch[chans[k]].k[band][b]);
1709 TCanvas *c = PlottingUtils::GetConfiguredCanvas(kFALSE);
1710 TH1F *frame = c->DrawFrame(xlo, ylo - 0.02, xhi, yhi + 0.02);
1711 frame->SetTitle(
";log_{10}(#Deltat [#mus]);Relative Amplitude Shift");
1712 TLegend *leg = RightLegend(Int_t(chans.size()) + 1, G.
n_amp > 1,
1713 !(TMath::Abs(ylo) > yhi), 0.58, 2);
1716 Form(
"Previous pulse %s",
BandLabel(band, G.
n_amp, kTRUE).Data()));
1717 for (
size_t k = 0; k < chans.size(); k++) {
1719 TGraph *gr =
new TGraph();
1720 for (Int_t b = 0; b <
kNBins; b++)
1721 gr->SetPoint(b, xlo + (b + 0.5) * (xhi - xlo) /
kNBins, K.
k[band][b]);
1722 gr->SetLineColor(ChannelColor(Int_t(k)));
1723 gr->SetMarkerColor(ChannelColor(Int_t(k)));
1724 gr->SetMarkerStyle(20);
1725 gr->SetMarkerSize(0.7);
1726 gr->SetLineWidth(2);
1728 gr->Draw(
"PL SAME");
1730 Form(
"%s%s", res.
name_ch[chans[k]].Data(),
1734 TGraph *gg =
new TGraph();
1735 for (Int_t b = 0; b <
kNBins; b++)
1736 gg->SetPoint(b, xlo + (b + 0.5) * (xhi - xlo) /
kNBins, G.
k[band][b]);
1737 gg->SetLineColor(kGray + 1);
1738 gg->SetLineWidth(3);
1739 gg->SetLineStyle(2);
1741 leg->AddEntry(gg,
"Group fit",
"l");
1743 TLine *zero =
new TLine(xlo, 0.0, xhi, 0.0);
1744 zero->SetLineStyle(2);
1746 PlottingUtils::SaveFigure(c, Form(
"kernel_channels_%s",
GroupTag(g)),
1747 subdir, PlotSaveOptions::kLINEAR);
1750 for (Int_t g = 1; g <
kNGROUPS; g++) {
1753 TCanvas *c = PlottingUtils::GetConfiguredCanvas(kFALSE);
1755 PlottingUtils::ConfigureAndDraw2DHistogram(res.
dev_vs_pred[g], c);
1756 TLine *diag =
new TLine(-600.0, -600.0, 600.0, 600.0);
1757 diag->SetLineColor(kRed + 1);
1758 diag->SetLineStyle(2);
1760 PlottingUtils::SaveFigure(c, Form(
"deviation_vs_predicted_%s", gn),
1761 subdir, PlotSaveOptions::kLINEAR);
1765 TCanvas *c = PlottingUtils::GetConfiguredCanvas(kTRUE);
1766 PlottingUtils::ConfigureAndDrawHistogram(res.
dev_before[g], kBlack);
1767 res.
dev_after[g]->SetLineColor(kRed + 1);
1770 TLegend *leg = RightLegend(2, kFALSE, kTRUE, 0.55);
1776 PlottingUtils::SaveFigure(c, Form(
"deviation_before_after_%s", gn),
1777 subdir, PlotSaveOptions::kLOG);
1781 TCanvas *c = PlottingUtils::GetConfiguredCanvas(kFALSE);
1784 PlottingUtils::ConfigureAndDrawHistogram(res.
dtprev_before[g], kBlack);
1789 TLegend *leg = RightLegend(2, kFALSE, kFALSE);
1793 PlottingUtils::SaveFigure(c, Form(
"deviation_vs_dt_previous_%s", gn),
1794 subdir, PlotSaveOptions::kLINEAR);
1799 for (Int_t pass = 0; pass < 2; pass++) {
1800 std::vector<TProfile *> &prof =
1802 std::vector<Int_t> chans;
1803 for (Int_t c = 0; c < Int_t(prof.size()); c++)
1804 if (res.
group_of[c] == g && prof[c] && prof[c]->GetEntries() > 0)
1808 TCanvas *c = PlottingUtils::GetConfiguredCanvas(kFALSE);
1809 TProfile *first = prof[chans[0]];
1810 first->SetMinimum(pass == 0 ? -300.0 : -100.0);
1811 first->SetMaximum(pass == 0 ? 200.0 : 100.0);
1812 first->SetTitle(
";log_{10}(#Deltat [#mus]);Channel #minus Mean [ADC]");
1813 PlottingUtils::ConfigureAndDrawHistogram(first, ChannelColor(0));
1814 TLegend *leg = RightLegend(Int_t(chans.size()), kTRUE, kFALSE, 0.58, 2);
1815 leg->SetHeader(pass == 0 ?
"Before correction" :
"After correction");
1816 leg->AddEntry(first, res.
name_ch[chans[0]].Data(),
"l");
1817 for (
size_t k = 1; k < chans.size(); k++) {
1818 TProfile *p = prof[chans[k]];
1819 p->SetLineColor(ChannelColor(Int_t(k)));
1820 p->SetMarkerColor(ChannelColor(Int_t(k)));
1823 leg->AddEntry(p, res.
name_ch[chans[k]].Data(),
"l");
1826 PlottingUtils::SaveFigure(c,
1827 Form(
"deviation_vs_dt_channels_%s_%s",
1828 pass == 0 ?
"before" :
"after", gn),
1829 subdir, PlotSaveOptions::kLINEAR);
1832 if (res.
shift[g] && res.
shift[g]->GetEntries() > 0) {
1833 TCanvas *c = PlottingUtils::GetConfiguredCanvas(kTRUE);
1834 PlottingUtils::ConfigureAndDrawHistogram(res.
shift[g], kBlack);
1835 PlottingUtils::SaveFigure(c, Form(
"applied_shift_%s", gn), subdir,
1836 PlotSaveOptions::kLOG);
1844 TFile *f = IO::OpenForWriting(events_subpath,
"UPDATE");
1845 if (!f || f->IsZombie()) {
1851 if (TObject *old = f->Get(
"pulse_history"))
1855 TTree *t =
new TTree(
"pulse_history",
1856 "Pulse-history kernel per channel and per group");
1858 t->Branch(
"Group", &row.group,
"Group/I");
1859 t->Branch(
"Board", &row.board,
"Board/I");
1860 t->Branch(
"Channel", &row.channel,
"Channel/I");
1861 t->Branch(
"Name", row.name,
"Name/C");
1862 t->Branch(
"Ok", &row.ok,
"Ok/O");
1865 t->Branch(
"Enabled", &row.enabled,
"Enabled/O");
1868 t->Branch(
"FromGroup", &row.from_group,
"FromGroup/O");
1869 t->Branch(
"NAmpBins", &row.n_amp,
"NAmpBins/I");
1873 t->Branch(
"KernelBinned", row.k_binned,
1875 t->Branch(
"Form", &row.form,
"Form/O");
1876 t->Branch(
"FormOk", &row.form_ok,
"FormOk/O");
1877 t->Branch(
"FormTauUs", &row.form_tau_us,
"FormTauUs/D");
1878 t->Branch(
"FormTauGiven", &row.form_tau_given,
"FormTauGiven/O");
1879 t->Branch(
"FormTauFreeUs", &row.form_tau_free_us,
"FormTauFreeUs/D");
1880 t->Branch(
"R2FormFree", &row.r2_form_free,
"R2FormFree/D");
1881 t->Branch(
"FormC", row.form_c, Form(
"FormC[%d]/D",
kMAX_AMP_BINS));
1882 t->Branch(
"FormCB", row.form_cb, Form(
"FormCB[%d]/D",
kMAX_AMP_BINS));
1883 t->Branch(
"R2Form", &row.r2_form,
"R2Form/D");
1884 t->Branch(
"R2Binned", &row.r2_binned,
"R2Binned/D");
1885 t->Branch(
"R2Group", &row.r2_group,
"R2Group/D");
1886 t->Branch(
"BinCentreUs", row.centre_us, Form(
"BinCentreUs[%d]/D",
kNBins));
1887 t->Branch(
"Intercept", &row.intercept,
"Intercept/D");
1888 t->Branch(
"R2", &row.r2,
"R2/D");
1889 t->Branch(
"RmsBefore", &row.rms_before,
"RmsBefore/D");
1890 t->Branch(
"RmsAfter", &row.rms_after,
"RmsAfter/D");
1891 t->Branch(
"MeanShift", &row.mean_shift,
"MeanShift/D");
1892 t->Branch(
"ApplyMaxUs", &row.apply_max_us,
"ApplyMaxUs/D");
1895 t->Branch(
"TauOk", &row.tau_ok,
"TauOk/O");
1896 t->Branch(
"TauFlat", &row.tau_flat,
"TauFlat/O");
1897 t->Branch(
"TauUs", &row.tau_us,
"TauUs/D");
1898 t->Branch(
"TauErrUs", &row.tau_err_us,
"TauErrUs/D");
1899 t->Branch(
"TauP0", &row.tau_p0,
"TauP0/D");
1900 t->Branch(
"TauAmp", &row.tau_p1,
"TauAmp/D");
1901 t->Branch(
"TauLift", &row.tau_p3,
"TauLift/D");
1902 t->Branch(
"N", &row.n,
"N/L");
1903 t->Branch(
"NBeamEvents", &row.n_beam,
"NBeamEvents/L");
1904 t->Branch(
"NCorrected", &row.n_corrected,
"NCorrected/L");
1905 t->Branch(
"NClamped", &row.n_clamped,
"NClamped/L");
1907 for (Int_t g = 1; g <
kNGROUPS; g++)
1908 FillPulseRow(row, res.
kernel[g], g, -1, res, nch, t);
1909 for (Int_t c = 0; c < Int_t(res.
kernel_ch.size()); c++)
1912 t->Write(
"pulse_history", TObject::kOverwrite);
std::mutex g_plot_mutex
Serialises all plotting and canvas work.
The per-group settings of the pulse-history correction.
Pole-zero pulse-history correction on the raw hit stream.
Double_t PULSE_HISTORY_APPLY_MAX_US
few hundred microseconds, so pulses beyond 100 us still move it.
Double_t ActiveReferenceChannelMaxAdc()
Upper energy gate on the seed channel, in ADC.
const TString & ActiveReferenceChannel()
Channel whose hits seed events.
Double_t ActiveStripEMaxAdc()
Upper bound of the per-strip energy range, in ADC.
Double_t ActiveEventTimeWindowUs()
Coincidence window for event building, in microseconds.
Bool_t SavePlots()
FileInSample(), by the name the plot call sites use.
const std::map< std::pair< Int_t, Int_t >, TString > & ActiveChannelMap()
The channel map in force.
Double_t ActiveReferenceChannelMinAdc()
Lower energy gate on the seed channel, in ADC.
Int_t ActiveNBoards()
Boards in the active epoch's setup.
const DatasetConfig & cfg
The active dataset's configuration, flat block.
Int_t ActiveNChannels()
Channels per board.
std::ostream & DetailErr()
const PulseHistoryGroups & ActivePulseHistoryGroups()
The pulse-history groups in force: the active epoch's set, or the flat PULSE_HISTORY_GROUPS when no e...
Bool_t GroupWantsForm(Int_t g)
Whether the configuration asks for the pole-zero form on this group rather than the binned kernel.
@ kLONG_LEFT
Long end, left chain.
@ kNONE
Not part of any corrected group.
@ kSHORT_RIGHT
Short end, right chain.
@ kNGROUPS
Count of groups; not a group itself.
@ kSTRIP0
Strip 0, unsegmented.
@ kGRID
The Frisch grid: the seed channel, one per event.
@ kSTRIP17
Strip 17, unsegmented.
@ kSHORT_LEFT
Short end, left chain.
@ kLONG_RIGHT
Long end, right chain.
const Double_t kTauFitLoUs
const Double_t kTauGridLoUs
const Double_t kLogHi
Highest dt bin edge: log10 of 316 us in seconds.
Double_t GroupTauUs(Int_t g)
The preamp decay time the configuration gives for this group, in microseconds; 0 when it is to be pro...
Int_t ChainOf(Int_t g)
Which readout chain a group belongs to.
const Int_t kNBins
Bins in the kernel, spanning kLogLo to kLogHi in log10 of dt: twelve per decade, 0....
const Double_t kLogLo
Lowest dt bin edge: log10 of 1 us in seconds.
const Int_t kTauFitMinEntries
Below this many (event, channel) pairs in the window the fit is skipped.
void FitTauDecay(const std::vector< Double_t > &dt_us, const std::vector< Double_t > &dev, Kernel::TauFit &out)
p0 + p1 exp(-dt / p2) + p3 dt through (dt us, deviation ADC) pairs, the form the pole-zero residual p...
const Double_t kTauFitHiUs
Double_t BinCentreUs(Int_t b)
Centre of a kernel bin, in microseconds, for reports.
const Int_t kMAX_AMP_BINS
Maximum amplitude bands a kernel may split the previous pulse into.
TString BandLabel(Int_t a, Int_t n_amp, Bool_t latex)
const char * GroupTag(Int_t g)
Short tag for filenames: L, R, Ls, Rs, S0, S17.
std::vector< Int_t > BuildGroupMap()
Group lookup for every (board, channel) under the active map.
const Long64_t kMinPairsPerChannel
Fewest (event, channel) pairs a channel needs for a kernel of its own; below it the channel is correc...
Int_t BinOf(Double_t dt_s)
Kernel bin for a time since the previous pulse.
void Apply(std::vector< RawHit > &hits, const std::vector< Int_t > &group_of, Result &res)
Apply the measured kernels to the hit stream, in place.
Bool_t Measure(std::vector< RawHit > &hits, const std::vector< Int_t > &group_of, Result &res, const TString &file_label)
Measure the kernels on this subfile's beam-like events.
Bool_t IsLongGroup(Int_t g)
Whether a group is a long end rather than a short end or an unsegmented strip.
void SavePlots(Result &res, const TString &file_label)
Draw and save the diagnostics, then free them.
Bool_t GroupEnabled(Int_t g)
Whether the groups in force (Constants::ActivePulseHistoryGroups()) have this group corrected.
const UInt_t kFlagClamped
Flag set on a hit whose correction was clamped rather than applied in full.
const Double_t kTauGridHiUs
Int_t AmpBinOf(Double_t e_prev, Double_t mode, Int_t n_amp)
Amplitude band of a previous pulse.
void WriteToEventsFile(const TString &events_subpath, const Result &res)
Record the kernels and counters alongside a subfile's events.
const char * GroupName(Int_t g)
Human-readable name of a Group.
TString Report(const Result &res, const TString &file_label, Bool_t detail=kTRUE)
Format the pass as a human-readable report.
Per-group pulse-history setting: whether the group is corrected and with which kernel shape.
PulseHistoryKernel kernel
Double_t tau_us
Known preamp decay time in microseconds, for the form.
Which channel groups the pulse-history correction fits and applies to, one setting per kernel group o...
PulseHistoryGroupOption long_right
Long end of the even strips (R).
PulseHistoryGroupOption strip17
Strip 17, unsegmented.
PulseHistoryGroupOption grid
The Frisch grid, the channel whose hits seed the events.
PulseHistoryGroupOption short_right
Short end of the odd strips (R).
PulseHistoryGroupOption strip0
Strip 0, unsegmented.
PulseHistoryGroupOption long_left
Long end of the odd strips (L).
PulseHistoryGroupOption short_left
Short end of the even strips (L).
Single-exponential summary of the dt profile for this group.
Double_t p0
Level at large dt, ADC.
Bool_t flat
Amplitude within 2 sigma of zero: no decay.
Double_t chi2
Chi2 of the fit over the window.
Double_t p3
Linear lift (the baseline term), ADC/us.
Int_t ndf
Its degrees of freedom.
Bool_t ok
Whether the fit converged.
Double_t p1err
Its error, ADC.
Double_t p2err
Its error, us.
Double_t p1
Tail amplitude at dt = 0, ADC.
Double_t p2
Decay time, us.
One fitted correction: a channel's own, or a group's.
Double_t rms_before
Channel-deviation RMS before correction.
Double_t t_m_us
Read point after a pulse: rise + peaking.
Double_t r2
Coefficient of determination.
Double_t tau_us
The applied form's decay time: given or profiled.
Int_t j_tau
Grid index of the applied tau; internal.
Double_t FormAt(Int_t a, Double_t dt_us) const
The form at one gap for one band, per unit previous height.
Double_t k_low[kMAX_AMP_BINS][kNBins]
Free coefficients, bins < n_free.
Long64_t n_beam
Beam-like events passing this group's selection.
Double_t EffectiveWindowUs(Int_t a) const
Effective restorer window implied by the fitted amplitudes, c tau / c_B, in microseconds; 0 when c_B ...
Double_t c[kMAX_AMP_BINS]
Direct-tail amplitude per band; > 0 undershoot.
Double_t r2_binned
And the binned fit's, for comparison.
Double_t k_binned[kMAX_AMP_BINS][kNBins]
The binned fit's coefficients, always kept so the form can be judged against them.
Double_t cb[kMAX_AMP_BINS]
Baseline-term amplitude per band.
Bool_t ok
Whether this kernel was successfully fitted.
Double_t t_lo_us
From where the form holds: (2 rise + flat) - t_m.
Int_t n_free
Bins below t_lo that stay free under the form.
Bool_t tau_given
Whether tau_us came from the configuration.
Long64_t n
(event, channel) pairs entering the fit.
TauFit tau
Summary of the dt profile, all hits.
Bool_t from_group
Channel kernels only: this is a copy of the group's kernel because the channel's own could not be fit...
Double_t tau_free_us
The profile's own preference, on the grid, and its fit quality: the check on a given tau_us.
Double_t r2_group
Channel kernels only: the R^2 the group's kernel reaches on this channel's pairs, so the gain from a ...
Double_t rms_after
And after, so the gain is visible.
Double_t t_f_us
Baseline freeze after a trigger: 2 rise + flat.
Int_t n_amp
Amplitude bands actually used, at most kMAX_AMP_BINS.
Kernel()
Construct unfitted, with zeroed coefficients.
Double_t k[kMAX_AMP_BINS][kNBins]
The applied kernel at the bin centres, [amplitude band][dt bin]: the binned coefficients themselves,...
Everything one subfile's pulse-history pass produced.
TH1D * dev_after[kNGROUPS]
And after.
std::vector< TProfile * > dtprev_before_ch
Per channel index, deviation against time to previous pulse before and after the channel's own kernel...
~Result()
Frees any diagnostic histograms still held.
Long64_t n_corrected
Hits that received a correction.
std::vector< Double_t > mode
Beam peak per (board, channel) index, in ADC.
TH2D * dev_vs_pred[kNGROUPS]
Channel deviation against predicted shift.
void FreeDiagnostics()
Delete every diagnostic histogram and null the pointers.
Long64_t n_beam_events
Beam-like events the fit drew on.
TProfile * dtprev_after[kNGROUPS]
And after.
Bool_t enabled[kNGROUPS]
Whether the group was configured for correction.
TProfile * dtprev_before[kNGROUPS]
Deviation against time to previous pulse.
Long64_t n_clamped_group[kNGROUPS]
Clamps per group.
Long64_t n_seeds
Hits usable as a previous pulse.
Bool_t sorted_input
Whether the input arrived time-ordered.
std::vector< Int_t > group_of
The group map Measure() was given.
std::vector< TString > name_ch
Channel-map name per index.
Double_t mean_shift[kNGROUPS]
Mean subtracted term per group, ADC.
Long64_t n_clamped
Corrections clamped rather than applied in full.
std::vector< Kernel > kernel_ch
The applied kernels, one per (board, channel) index as in the group map (empty before Measure()): the...
Kernel kernel[kNGROUPS]
The group fits, one per Group: each the fit over the sum of its channels, the fallback for a channel ...
TH1D * shift[kNGROUPS]
Shift applied per hit.
std::vector< TProfile * > dtprev_after_ch
Result()
Construct with zeroed counters and null diagnostics.
Long64_t n_hits
Hits examined.
TH1D * dev_before[kNGROUPS]
Deviation distribution before correction.