9inline Double_t
StripTotalAdc(
const UShort_t *l,
const UShort_t *r, UShort_t s0,
10 UShort_t s17, Int_t s) {
15 return Double_t(l[s - 1]) + Double_t(r[s - 1]);
21inline Int_t
GatePartner(Int_t s) {
return s <= 1 ? 0 : s - 1; }
35 std::vector<ChannelCal> chans;
45 for (Int_t s = 1; s <= 16; s++) {
46 c.
name = Form(
"L%d", s);
50 c.
name = Form(
"R%d", s);
68Char_t
LongSide(Int_t strip) {
return (strip % 2 == 0) ?
'R' :
'L'; }
81 const TString &run_label,
82 const TString &plot_subdir,
83 Bool_t save_plot = kTRUE) {
87 TFile *sf = IO::OpenForReading(sub);
88 if (!sf || sf->IsZombie()) {
93 TTree *tree =
static_cast<TTree *
>(sf->Get(
"events"));
101 UShort_t leftdE_adc[16], rightdE_adc[16], strip0_adc = 0, strip17_adc = 0;
102 tree->SetBranchAddress(
"LeftdE", leftdE_adc);
103 tree->SetBranchAddress(
"RightdE", rightdE_adc);
104 tree->SetBranchAddress(
"Strip0dE", &strip0_adc);
105 tree->SetBranchAddress(
"Strip17dE", &strip17_adc);
111 const Int_t kBeamGateNBins = 1024;
113 new TH2F(Form(
"h2_gate_s%d_s%d_%s", sx, sy, run_label.Data()),
114 Form(
";Strip%d #DeltaE [ADC];Strip%d #DeltaE [ADC]", sx, sy),
117 h->SetDirectory(
nullptr);
118 Long64_t n = tree->GetEntries();
120 const Long64_t kMaxPoints = 2000000;
121 std::vector<std::pair<Float_t, Float_t>> pts;
122 pts.reserve(std::min(n, kMaxPoints));
123 for (Long64_t j = 0; j < n; j++) {
126 StripTotalAdc(leftdE_adc, rightdE_adc, strip0_adc, strip17_adc, sx);
128 StripTotalAdc(leftdE_adc, rightdE_adc, strip0_adc, strip17_adc, sy);
129 if (tx > 0.0 && ty > 0.0) {
131 if (Long64_t(pts.size()) < kMaxPoints)
132 pts.push_back(std::make_pair(Float_t(tx), Float_t(ty)));
138 if (h->GetEntries() < 100) {
140 <<
": too few events for the strip " << sy
141 <<
" beam gate (strips " << sx <<
" vs " << sy <<
")"
150 const Int_t kSeedHalfBins = 40;
154 <<
": no bins above beam seed threshold"
161 const Int_t kMomentRefineIters = 4;
162 const Double_t kMomentRefineNSigma = 2.5;
163 const Double_t bw_x = h->GetXaxis()->GetBinWidth(1);
164 const Double_t bw_y = h->GetYaxis()->GetBinWidth(1);
166 h->GetMaximumBin(bx, by, bz);
167 const Double_t peak_val = h->GetBinContent(bx, by);
168 for (Int_t iter = 0; iter < kMomentRefineIters; iter++) {
169 Int_t wlo_bx = std::max(
170 1, h->GetXaxis()->FindBin(m.
mu_x - kMomentRefineNSigma * m.
sigma_x));
171 Int_t whi_bx = std::min(
173 h->GetXaxis()->FindBin(m.
mu_x + kMomentRefineNSigma * m.
sigma_x));
174 Int_t wlo_by = std::max(
175 1, h->GetYaxis()->FindBin(m.
mu_y - kMomentRefineNSigma * m.
sigma_y));
176 Int_t whi_by = std::min(
178 h->GetYaxis()->FindBin(m.
mu_y + kMomentRefineNSigma * m.
sigma_y));
180 h, wlo_bx, whi_bx, wlo_by, whi_by, 0.30 * peak_val, bw_x, bw_y);
190 <<
" vs " << sy <<
"): "
192 :
"CLIPPED MOMENTS (fit failed)")
193 <<
" mu=(" << out.
mu_x <<
"," << out.
mu_y <<
") sigma=("
195 <<
") rho=" << out.
rho <<
" chi2/ndf=" << spot.
chi2_ndf
199 TCanvas *cv = PlottingUtils::GetConfiguredCanvas(kFALSE);
200 PlottingUtils::ConfigureAndDraw2DHistogram(h, cv);
204 PlottingUtils::SaveFigure(cv, Form(
"beam_gate_s%02d", sy),
205 plot_subdir +
"/beam_gate",
206 PlotSaveOptions::kLINEAR);
215template <
class T>
inline Double_t
Median(std::vector<T> &v) {
218 Int_t n = Int_t(v.size());
219 std::nth_element(v.begin(), v.begin() + n / 2, v.end());
220 Double_t med = Double_t(v[n / 2]);
222 std::nth_element(v.begin(), v.begin() + n / 2 - 1, v.end());
223 med = 0.5 * (med + Double_t(v[n / 2 - 1]));
238 Int_t n = Int_t(v.size());
241 std::vector<Float_t> s(v);
242 std::sort(s.begin(), s.end());
243 Int_t i_lo = Int_t(0.05 * n);
244 Int_t i_hi = Int_t(0.95 * n);
245 Int_t i_q1 = Int_t(0.25 * n);
246 Int_t i_q3 = Int_t(0.75 * n);
249 Double_t p_lo = Double_t(s[i_lo]);
250 Double_t p_hi = Double_t(s[i_hi]);
251 Double_t med = Double_t(s[n / 2]);
252 sigma = (Double_t(s[i_q3]) - Double_t(s[i_q1])) / 1.349;
254 sigma = 0.05 * (med > 0.0 ? med : 1.0);
255 if (!(p_hi > p_lo)) {
259 const Int_t nbins = 100;
260 TH1F h(
"robust_peak_seed_h",
"", nbins, p_lo, p_hi);
261 h.SetDirectory(
nullptr);
262 for (Int_t j = 0; j < n; j++)
263 if (Double_t(s[j]) >= p_lo && Double_t(s[j]) <= p_hi)
264 h.Fill(Double_t(s[j]));
265 mode = h.GetBinCenter(h.GetMaximumBin());
277 std::vector<Float_t>
l;
278 std::vector<Float_t>
r;
293 const std::vector<ChannelCal> &chans,
295 std::vector<std::vector<Float_t>> &samples,
297 Int_t n_chans = Int_t(chans.size());
298 samples.assign(n_chans, std::vector<Float_t>());
300 for (Int_t s = 0; s <= 17; s++)
301 any = any || gate[s].ok;
306 TFile *sf = IO::OpenForReading(sub);
307 if (!sf || sf->IsZombie()) {
312 TTree *tree =
static_cast<TTree *
>(sf->Get(
"events"));
320 UShort_t leftdE_adc[16], rightdE_adc[16], strip0_adc = 0, strip17_adc = 0;
321 Short_t cathode_adc = 0, grid_adc = 0;
322 tree->SetBranchAddress(
"LeftdE", leftdE_adc);
323 tree->SetBranchAddress(
"RightdE", rightdE_adc);
324 tree->SetBranchAddress(
"Strip0dE", &strip0_adc);
325 tree->SetBranchAddress(
"Strip17dE", &strip17_adc);
326 tree->SetBranchAddress(
"Cathode", &cathode_adc);
327 tree->SetBranchAddress(
"Grid", &grid_adc);
329 Long64_t n = tree->GetEntries();
330 for (Long64_t j = 0; j < n; j++) {
335 for (Int_t s = 1; s <= 16; s++) {
336 Int_t lv = Int_t(leftdE_adc[s - 1]);
337 Int_t rv = Int_t(rightdE_adc[s - 1]);
338 if (lv > 0 && rv > 0 && Long64_t(pairs[s].l.size()) <
kPairCap) {
339 pairs[s].
l.push_back(Float_t(lv));
340 pairs[s].
r.push_back(Float_t(rv));
345 Bool_t pass[18] = {kFALSE};
346 for (Int_t s = 1; s <= 17; s++) {
349 const Double_t x =
StripTotalAdc(leftdE_adc, rightdE_adc, strip0_adc,
352 StripTotalAdc(leftdE_adc, rightdE_adc, strip0_adc, strip17_adc, s);
353 if (x <= 0.0 || y <= 0.0)
362 for (Int_t s = 1; s <= 16; s++) {
363 if (!pass[s] || Long64_t(pairs[s].gated_long.size()) >=
kPairCap)
365 Bool_t l_is_long = (
LongSide(s) ==
'L');
367 l_is_long ? Int_t(leftdE_adc[s - 1]) : Int_t(rightdE_adc[s - 1]);
369 l_is_long ? Int_t(rightdE_adc[s - 1]) : Int_t(leftdE_adc[s - 1]);
371 pairs[s].
gated_long.push_back(Float_t(long_v));
372 pairs[s].
gated_short.push_back(Float_t(short_v > 0 ? short_v : 0));
376 for (Int_t i = 0; i < n_chans; i++) {
377 if (Long64_t(samples[i].size()) >=
kSampleCap)
382 const Int_t s = (c.
side ==
'C' || c.
side ==
'G') ? 1 : c.
strip;
383 if (s < 0 || s > 17 || !pass[s])
387 v = Int_t(c.
strip == 0 ? strip0_adc : strip17_adc);
388 else if (c.
side ==
'L')
389 v = Int_t(leftdE_adc[c.
strip - 1]);
390 else if (c.
side ==
'R')
391 v = Int_t(rightdE_adc[c.
strip - 1]);
392 else if (c.
side ==
'C')
393 v = Int_t(cathode_adc);
394 else if (c.
side ==
'G')
397 samples[i].push_back(Float_t(v));
499 std::vector<Double_t>
x,
y,
ey;
520 const std::vector<Double_t> &y, Double_t &slope,
521 Double_t &intercept) {
522 const Int_t n = Int_t(x.size());
525 std::vector<Double_t> slopes;
526 slopes.reserve(n * (n - 1) / 2);
527 for (Int_t i = 0; i < n; i++)
528 for (Int_t j = i + 1; j < n; j++) {
529 Double_t dx = x[j] - x[i];
532 slopes.push_back((y[j] - y[i]) / dx);
537 std::vector<Double_t> resid;
539 for (Int_t i = 0; i < n; i++)
540 resid.push_back(y[i] - slope * x[i]);
541 intercept =
Median(resid);
554 const std::vector<Float_t> &v_long, Double_t c_long,
555 Double_t &slope_out, Double_t &intercept_out,
560 dbg.
n_gated = Long64_t(v_short.size());
561 if (c_long <= 0 || v_short.size() != v_long.size() || v_short.size() < 500) {
562 dbg.
fail = c_long <= 0 ?
"c_long<=0"
563 : v_short.size() != v_long.size() ?
"size mismatch"
564 :
"fewer than 500 gated pairs";
571 std::vector<std::pair<Double_t, Double_t>> in_window;
572 for (Int_t j = 0; j < Int_t(v_short.size()); j++) {
573 Double_t sh = Double_t(v_short[j]), lg = Double_t(v_long[j]);
574 if (sh < lo || sh >= hi)
577 if (lg <= kRidgeBandLo * c_long || lg >=
kRidgeBandHi * c_long)
580 in_window.push_back(std::make_pair(sh, lg));
586 std::vector<std::vector<Double_t>> slice(n_slices);
587 for (Int_t j = 0; j < Int_t(in_window.size()); j++) {
588 Int_t b = Int_t((in_window[j].first - lo) / (hi - lo) * n_slices);
589 if (b >= 0 && b < n_slices)
590 slice[b].push_back(in_window[j].second);
592 std::vector<Double_t> x, y, ey;
601 for (Int_t b = 0; b < n_slices; b++) {
602 if (Long64_t(slice[b].size()) < min_per_slice)
604 std::sort(slice[b].begin(), slice[b].end());
605 const Int_t nb = Int_t(slice[b].size());
608 const Double_t iqr = slice[b][nb * 3 / 4] - slice[b][nb / 4];
609 const Double_t med =
Median(slice[b]);
610 x.push_back(lo + (b + 0.5) * (hi - lo) / n_slices);
612 ey.push_back(1.253 * (iqr / 1.349) / TMath::Sqrt(Double_t(nb)));
628 dbg.
fail =
"fewer than kRidgeMinPts filled slices";
633 Double_t slope = 0.0, inter = 0.0;
635 dbg.
fail =
"degenerate slice set (TheilSenLine)";
639 intercept_out = inter;
646 dbg.
fail =
"slope >= 0";
651 if (inter < 0.80 * c_long || inter > 1.35 * c_long) {
652 dbg.
fail =
"intercept outside [0.80, 1.35]*C_long";
657 return -inter / slope;
666 const TString &plot_subdir) {
667 TString subdir = plot_subdir +
"/ridge";
668 for (Int_t s = 1; s <= 16; s++) {
673 Bool_t l_is_long = (
LongSide(s) ==
'L');
676 TH2F *h =
new TH2F(Form(
"h_ridge_s%d", s),
677 Form(
";Strip %d Short (%c) #DeltaE [ADC];Strip %d Long "
678 "(%c) #DeltaE [ADC]",
679 s, l_is_long ?
'R' :
'L', s, l_is_long ?
'L' :
'R'),
680 200, 0.0, xhi, 200, 0.0, 1.60 * d.
c_long);
681 for (Int_t j = 0; j < Int_t(p.
gated_short.size()); j++)
683 TCanvas *cv = PlottingUtils::GetConfiguredCanvas(kFALSE);
684 PlottingUtils::Configure2DHistogram(h, cv);
686 TGraphErrors *g =
nullptr;
688 g =
new TGraphErrors(Int_t(d.
x.size()), &d.
x[0], &d.
y[0],
nullptr,
690 g->SetMarkerStyle(20);
691 g->SetMarkerSize(0.8);
692 g->SetMarkerColor(kBlack);
693 g->SetLineColor(kBlack);
701 lf->SetLineColor(kViolet + 2);
707 TLine *lw[2] = {
nullptr,
nullptr};
709 const Double_t ymax = 1.60 * d.
c_long;
710 lw[0] =
new TLine(d.
lo, 0.0, d.
lo, ymax);
711 lw[1] =
new TLine(d.
hi, 0.0, d.
hi, ymax);
712 for (Int_t k = 0; k < 2; k++) {
713 lw[k]->SetLineColor(kGray + 2);
714 lw[k]->SetLineStyle(2);
719 PlottingUtils::SaveFigure(cv, Form(
"ridge_s%02d", s), subdir,
720 PlotSaveOptions::kLINEAR);
733Double_t
GmFindPeak(
const std::vector<Float_t> &v, Double_t lo, Double_t hi,
734 Double_t skip_frac) {
735 if (v.empty() || hi <= lo)
737 std::vector<Long64_t> h(
kGmBins, 0);
738 Double_t bw = (hi - lo) /
kGmBins;
739 for (Int_t j = 0; j < Int_t(v.size()); j++) {
740 Double_t x = Double_t(v[j]);
741 if (x < lo || x >= hi)
743 Int_t b = Int_t((x - lo) / bw);
747 Int_t skip = Int_t(
kGmBins * skip_frac);
750 for (Int_t b = skip; b <
kGmBins; b++) {
756 if (bmax < 0 || vmax <= 0)
758 return lo + (bmax + 0.5) * bw;
777static void IndexStripEnds(
const std::vector<ChannelCal> &chans,
778 Int_t idx_l[18], Int_t idx_r[18]) {
779 for (Int_t s = 0; s < 18; s++) {
783 for (Int_t i = 0; i < Int_t(chans.size()); i++) {
784 if (chans[i].strip >= 1 && chans[i].strip <= 16) {
785 if (chans[i].side ==
'L')
786 idx_l[chans[i].strip] = i;
787 else if (chans[i].side ==
'R')
788 idx_r[chans[i].strip] = i;
797 Double_t &peak_short, Double_t &peak_long,
799 const std::vector<Float_t> &v_short = p.
gated_short;
800 const std::vector<Float_t> &v_long = p.
gated_long;
801 Double_t slope = 0.0, inter = 0.0;
802 const Double_t crossing =
805 const Double_t k = -slope;
807 peak_short = peak_long / k;
808 offset = (inter - peak_long) / k;
810 <<
" ridge slope=" << Form(
"%.3f", slope)
811 <<
" intercept=" << Form(
"%.1f", inter)
812 <<
" ADC; long anchor=" << Form(
"%.1f", peak_long)
813 <<
" (modal) short anchor=" << Form(
"%.1f", peak_short)
814 <<
" ADC short offset=" << Form(
"%.1f", offset)
815 <<
" ADC (" << Form(
"%.3f", offset / peak_long)
816 <<
" x C_long)" << std::endl;
819 <<
"(slope=" << Form(
"%.3f", slope)
820 <<
", intercept=" << Form(
"%.1f", inter) <<
")"
821 << (dbg.
fail[0] ? Form(
" [%s]", dbg.
fail) :
"")
822 <<
" gated=" << Form(
"%lld", dbg.n_gated)
823 <<
" short_win=" << Form(
"%lld", dbg.n_short_window)
824 <<
" long_band=" << Form(
"%lld", dbg.n_long_band)
825 <<
" slices=" << dbg.n_slices
826 <<
" min_per_slice=" << dbg.min_per_slice
832static void MeasureRidges(std::vector<ChannelCal> &chans,
834 const Int_t idx_l[18],
const Int_t idx_r[18],
836 for (Int_t s = 1; s <= 16; s++) {
837 if (idx_l[s] < 0 || idx_r[s] < 0)
839 Bool_t l_is_long = (
LongSide(s) ==
'L');
840 ChannelCal &c_long = chans[l_is_long ? idx_l[s] : idx_r[s]];
841 ChannelCal &c_short = chans[l_is_long ? idx_r[s] : idx_l[s]];
845 <<
": long side uncalibrated; skipping L/R gain match" << std::endl;
848 Double_t peak_short = 0.0, peak_long = 0.0, offset = 0.0;
849 FitStripRidge(s, pairs[s], c_long, ridge_dbg[s], peak_short, peak_long,
851 if (peak_short <= 0 || peak_long <= 0)
855 Double_t ratio = peak_short / peak_long;
858 <<
" strip " << s <<
": ridge ratio " << Form(
"%.2f", ratio)
860 <<
"]; rejecting anchor " << Form(
"%.1f", peak_short) <<
" ADC"
880 std::vector<Double_t> all_ratios;
881 for (Int_t par = 0; par < 2; par++) {
883 all_ratios.insert(all_ratios.end(), v.begin(), v.end());
888 if (!all_ratios.empty())
893 <<
" all=" << Form(
"%.3f", m.
global_ratio) << std::endl;
896 for (Int_t par = 0; par < 2; par++) {
910static void ApplyGainMatch(std::vector<ChannelCal> &chans,
911 const Int_t idx_l[18],
const Int_t idx_r[18],
913 for (Int_t s = 1; s <= 16; s++) {
914 if (idx_l[s] < 0 || idx_r[s] < 0)
916 Bool_t l_is_long = (
LongSide(s) ==
'L');
917 ChannelCal &c_long = chans[l_is_long ? idx_l[s] : idx_r[s]];
918 ChannelCal &c_short = chans[l_is_long ? idx_r[s] : idx_l[s]];
928 <<
": no ridge and no ratio fallback; keeping "
936 <<
" strip " << s <<
" short_anchor=ratio fallback "
939 << (m.
median_ratio[s % 2] > 0 ?
"" :
" (global, parity had none)")
940 <<
" short offset=parity median "
951 <<
" ADC short anchor="
953 <<
" ADC short offset="
955 << (m.
ridge_ok[s] ?
"" :
" (fallback)") << std::endl;
961static void CheckSummedBeamPeaks(std::vector<ChannelCal> &chans,
963 const Int_t idx_l[18],
const Int_t idx_r[18],
965 Double_t esum_peak[18] = {0};
966 for (Int_t s = 1; s <= 16; s++) {
970 Bool_t l_is_long = (
LongSide(s) ==
'L');
971 Double_t g_long =
Gain(chans[l_is_long ? idx_l[s] : idx_r[s]]);
972 Double_t g_short =
Gain(chans[l_is_long ? idx_r[s] : idx_l[s]]);
973 std::vector<Float_t> esum;
977 for (Int_t j = 0; j < Int_t(p.
gated_long.size()); j++) {
980 esum.push_back(Float_t(
986 for (Int_t s = 1; s <= 16; s++) {
989 if (esum_peak[s] <= 0) {
991 <<
" strip " << s <<
": no summed beam peak in (" <<
kGmEsumLo
992 <<
", " <<
kGmEsumHi <<
") a.u." << std::endl;
995 Double_t dev = esum_peak[s] - 1.0;
997 <<
" summed beam peak=" << Form(
"%.4f", esum_peak[s])
998 <<
" a.u. (" << Form(
"%+.1f%%", 100.0 * dev) <<
")"
999 << (TMath::Abs(dev) > 0.05 ?
" <-- check ridge fit"
1007 const TString &plot_subdir) {
1009 Int_t idx_l[18], idx_r[18];
1010 IndexStripEnds(chans, idx_l, idx_r);
1013 MeasureRidges(chans, pairs, idx_l, idx_r, ridge_dbg, m);
1018 RidgeFallbackMedians(m);
1019 ApplyGainMatch(chans, idx_l, idx_r, m);
1020 CheckSummedBeamPeaks(chans, pairs, idx_l, idx_r, m);
1028 std::vector<std::vector<Float_t>> &samples,
1030 const TString &plot_subdir) {
1031 Int_t n_chans = Int_t(chans.size());
1033 for (Int_t i = 0; i < n_chans; i++) {
1035 std::vector<Float_t> &v = samples[i];
1042 if (c.
side ==
'C') {
1047 Double_t mode = 0.0, rsigma = 0.0;
1052 <<
" (n=" << c.
n_samples <<
")" << std::endl;
1067 if (TObject *old = dst->Get(
"calibration"))
1069 TTree *cal =
new TTree(
"calibration",
"Per-channel normMUSIC calibration");
1076 Float_t gain_left[16] = {0}, gain_right[16] = {0};
1077 Float_t gain_strip0 = 0.0f, gain_strip17 = 0.0f;
1078 Float_t gain_cathode = 0.0f;
1079 Float_t gain_grid = 0.0f;
1082 Float_t offset_left[16] = {0}, offset_right[16] = {0};
1085 Float_t ridge_ratio[18] = {0};
1086 Float_t ridge_offset[18] = {0};
1087 Float_t long_anchor[18] = {0};
1088 Int_t n_actual = TMath::Min(Int_t(chans.size()),
kMaxChannels);
1089 for (Int_t k = 0; k < n_actual; k++) {
1092 gain[k] = ok[k] ? Float_t(
Gain(c)) : 0.0f;
1093 fit_adc[k] = Float_t(c.
fit_adc);
1103 long_anchor[c.
strip] = Float_t(1.0 / gain[k]);
1106 gain_strip0 = gain[k];
1107 else if (c.
side ==
'S' && c.
strip == 17)
1108 gain_strip17 = gain[k];
1110 gain_left[c.
strip - 1] = gain[k];
1113 gain_right[c.
strip - 1] = gain[k];
1115 }
else if (c.
side ==
'C')
1116 gain_cathode = gain[k];
1117 else if (c.
side ==
'G')
1118 gain_grid = gain[k];
1120 cal->Branch(
"Gain", gain, Form(
"Gain[%d]/F",
kMaxChannels));
1122 cal->Branch(
"FitADC", fit_adc, Form(
"FitADC[%d]/F",
kMaxChannels));
1123 cal->Branch(
"FitN", fit_n, Form(
"FitN[%d]/L",
kMaxChannels));
1124 cal->Branch(
"GainLeft", gain_left,
"GainLeft[16]/F");
1125 cal->Branch(
"GainRight", gain_right,
"GainRight[16]/F");
1126 cal->Branch(
"GainStrip0", &gain_strip0,
"GainStrip0/F");
1127 cal->Branch(
"GainStrip17", &gain_strip17,
"GainStrip17/F");
1128 cal->Branch(
"GainCathode", &gain_cathode,
"GainCathode/F");
1129 cal->Branch(
"GainGrid", &gain_grid,
"GainGrid/F");
1130 cal->Branch(
"OffsetLeft", offset_left,
"OffsetLeft[16]/F");
1131 cal->Branch(
"OffsetRight", offset_right,
"OffsetRight[16]/F");
1132 cal->Branch(
"RidgeRatio", ridge_ratio,
"RidgeRatio[18]/F");
1133 cal->Branch(
"RidgeOffset", ridge_offset,
"RidgeOffset[18]/F");
1134 cal->Branch(
"LongAnchor", long_anchor,
"LongAnchor[18]/F");
1138 Float_t strip_factor[18], strip_offset[18];
1139 for (Int_t s = 0; s < 18; s++) {
1140 strip_factor[s] = 1.0f;
1141 strip_offset[s] = 0.0f;
1143 if (align && align->
ok)
1144 for (Int_t s = 0; s < 18; s++) {
1145 strip_factor[s] = Float_t(align->
factors[s]);
1146 strip_offset[s] = Float_t(align->
offsets[s]);
1148 cal->Branch(
"StripFactor", strip_factor,
"StripFactor[18]/F");
1149 cal->Branch(
"StripOffset", strip_offset,
"StripOffset[18]/F");
1151 cal->Write(
"calibration", TObject::kOverwrite);
1160 const std::vector<ChannelCal> &chans,
1163 TFile *f = IO::OpenForWriting(events_subpath,
"UPDATE");
1164 if (!f || f->IsZombie()) {
1165 std::cerr <<
"Cannot open " << events_subpath <<
" to write calibration"
1181 const TString &file_label) {
1182 const Int_t kNStrips = 18;
1187 const Int_t nbins = TMath::Max(20, Int_t((emax - emin) / 0.025 + 0.5));
1189 for (Int_t s = 0; s < kNStrips; s++) {
1190 h[s] =
new TH1D(Form(
"h_dynrange_%s_S%d", file_label.Data(), s),
1191 ";#DeltaE [a.u.];Counts", nbins, emin, emax);
1192 h[s]->SetDirectory(
nullptr);
1195 TFile *sf = IO::OpenForReading(sub);
1196 if (!sf || sf->IsZombie()) {
1199 for (Int_t s = 0; s < kNStrips; s++)
1203 TTree *tree =
static_cast<TTree *
>(sf->Get(
"events"));
1207 for (Int_t s = 0; s < kNStrips; s++)
1216 for (Int_t s = 0; s < kNStrips; s++)
1222 TH1D *hg =
new TH1D(Form(
"h_dynrange_%s_grid", file_label.Data()),
1223 ";Grid #DeltaE [a.u.];Counts", nbins, emin, emax);
1224 hg->SetDirectory(
nullptr);
1225 Long64_t n = tree->GetEntries();
1226 for (Long64_t j = 0; j < n; j++) {
1229 for (Int_t s = 0; s < kNStrips; s++) {
1230 Double_t v = ev.
Total(s);
1239 std::vector<Int_t> colors = PlottingUtils::GetDefaultColors();
1241 for (Int_t s = 0; s < kNStrips; s++) {
1242 Double_t m = h[s]->GetMaximum();
1246 TCanvas *cv = PlottingUtils::GetConfiguredCanvas(kFALSE);
1247 cv->SetRightMargin(0.20);
1248 Bool_t first = kTRUE;
1249 for (Int_t s = 0; s < kNStrips; s++) {
1250 Int_t color = colors[s % Int_t(colors.size())];
1251 h[s]->SetLineColor(color);
1252 h[s]->SetLineWidth(2);
1253 h[s]->SetMaximum(1.15 * y_top);
1254 h[s]->Draw(first ?
"HIST" :
"HIST SAME");
1257 TLegend *leg = PlottingUtils::AddLegend(0.81, 0.99, 0.10, 0.95);
1258 for (Int_t s = 0; s < kNStrips; s++)
1259 leg->AddEntry(h[s], Form(
"S%d", s),
"l");
1262 PlottingUtils::SaveFigure(cv,
"dynamic_range_check", plot_subdir,
1263 PlotSaveOptions::kLOG);
1266 TCanvas *cg = PlottingUtils::GetConfiguredCanvas(kTRUE);
1267 PlottingUtils::ConfigureAndDrawHistogram(hg, kBlue + 1);
1269 PlottingUtils::SaveFigure(cg,
"grid_spectrum", plot_subdir,
1270 PlotSaveOptions::kLOG);
1273 for (Int_t s = 0; s < kNStrips; s++)
1286 Int_t b_lo = proj->FindBin(lo);
1287 Int_t b_hi = proj->FindBin(hi);
1289 Double_t val_max = 0;
1290 for (Int_t b = b_lo; b <= b_hi; b++) {
1291 Double_t v = proj->GetBinContent(b);
1297 if (b_max < 0 || b_max == b_lo || b_max == b_hi)
1299 Double_t peak = proj->GetBinCenter(b_max);
1300 if (b_max > 1 && b_max < proj->GetNbinsX()) {
1301 const Double_t ym = proj->GetBinContent(b_max - 1);
1302 const Double_t y0 = proj->GetBinContent(b_max);
1303 const Double_t yp = proj->GetBinContent(b_max + 1);
1304 const Double_t denom = ym - 2.0 * y0 + yp;
1306 Double_t shift = 0.5 * (ym - yp) / denom;
1307 shift = TMath::Max(-0.5, TMath::Min(0.5, shift));
1308 peak += shift * proj->GetBinWidth(b_max);
1342 const TString &plot_subdir,
1343 const TString &file_label) {
1344 const Int_t kNStrips = 18;
1345 const Int_t kNHistBins = 2000;
1346 const Double_t kHistMin = 0.0;
1347 const Double_t kHistMax = 10.0;
1348 const Int_t kSmoothTimes = 5;
1349 const Double_t kBeamLo = 0.6, kBeamHi = 1.5;
1350 const Double_t kPileLoX = 1.7, kPileHiX = 2.3;
1352 for (Int_t s = 0; s < kNStrips; s++) {
1360 TFile *sf = IO::OpenForReading(sub);
1361 if (!sf || sf->IsZombie()) {
1367 TTree *tree =
static_cast<TTree *
>(sf->Get(
"events"));
1376 std::cerr <<
" " << file_label
1377 <<
": calibration tree not found -- skipping alignment"
1384 TH2D *h2 =
new TH2D(Form(
"h2_strip_align_%s", file_label.Data()),
1385 ";Strip number;#DeltaE [a.u.]", kNStrips, -0.5,
1386 kNStrips - 0.5, kNHistBins, kHistMin, kHistMax);
1387 h2->SetDirectory(
nullptr);
1389 Long64_t n = tree->GetEntries();
1390 Long64_t n_used = 0;
1391 for (Long64_t j = 0; j < n; j++) {
1394 Bool_t any = kFALSE;
1395 for (Int_t s = 0; s < kNStrips; s++) {
1396 Double_t v = ev.
Total(s);
1399 h2->Fill(Double_t(s), v);
1411 Bool_t beam_ok[kNStrips] = {kFALSE};
1412 Bool_t pile_ok[kNStrips] = {kFALSE};
1413 Int_t valid_strips = 0;
1415 for (Int_t s = 0; s < kNStrips; s++) {
1416 Int_t bin_ix = s + 1;
1417 TH1D *proj = h2->ProjectionY(
1418 Form(
"hproj_align_%s_s%d", file_label.Data(), s), bin_ix, bin_ix);
1419 proj->SetDirectory(
nullptr);
1420 Long64_t n_entries = Long64_t(proj->GetEntries());
1423 <<
" strip " << s <<
": too few entries for alignment (" << n_entries
1424 <<
")" << std::endl;
1428 proj->Smooth(kSmoothTimes);
1433 <<
" strip " << s <<
": no beam peak in [" << kBeamLo <<
", "
1434 << kBeamHi <<
"] a.u.; left unaligned" << std::endl;
1440 Double_t pile =
SmoothedPeakIn(proj, kPileLoX * beam, kPileHiX * beam);
1448 result.
factors[s] = 1.0 / (pile - beam);
1451 result.
factors[s] = 1.0 / beam;
1457 << (pile > 0 ? Form(
"%.4f", pile)
1458 :
"none (factor only)")
1459 <<
" a.u. factor=" << Form(
"%.4f", result.
factors[s])
1460 <<
" offset=" << Form(
"%+.4f", result.
offsets[s])
1461 <<
" (n=" << n_entries <<
")" << std::endl;
1463 result.
ok = (valid_strips >= 4) ? kTRUE : kFALSE;
1468 TCanvas *cv = PlottingUtils::GetConfiguredCanvas(kFALSE);
1469 PlottingUtils::ConfigureAndDraw2DHistogram(h2, cv);
1470 TGraph *g_beam_plot =
new TGraph(kNStrips);
1471 TGraph *g_pile_plot =
new TGraph(kNStrips);
1472 Int_t nb = 0, npk = 0;
1473 for (Int_t s = 0; s < kNStrips; s++) {
1475 g_beam_plot->SetPoint(nb++, Double_t(s), result.
centroids[s]);
1477 g_pile_plot->SetPoint(npk++, Double_t(s), result.
pileups[s]);
1479 g_beam_plot->Set(nb);
1480 g_pile_plot->Set(npk);
1482 g_beam_plot->SetMarkerStyle(20);
1483 g_beam_plot->SetMarkerColor(kOrange);
1484 g_beam_plot->Draw(
"P SAME");
1487 g_pile_plot->SetMarkerStyle(21);
1488 g_pile_plot->SetMarkerColor(kViolet + 2);
1489 g_pile_plot->Draw(
"P SAME");
1492 PlottingUtils::SaveFigure(cv,
"strip_alignment_check", plot_subdir,
1493 PlotSaveOptions::kLINEAR);
1505 const FileSpec &spec,
const std::vector<ChannelCal> &chans_template) {
1507 TString plot_subdir =
"beam_calibration/" + file_label;
1516 for (Int_t s = 1; s <= 17; s++) {
1523 <<
" " << file_label <<
": strip " << s
1524 <<
" beam gate failed; that strip is not calibrated here"
1532 std::cerr <<
" " << file_label <<
": every per-strip beam gate failed"
1537 std::vector<ChannelCal> chans = chans_template;
1538 std::vector<std::vector<Float_t>> samples;
1548 for (Int_t i = 0; i < Int_t(chans.size()); i++) {
1555 else if (c.
side ==
'G')
1557 else if (c.
side ==
'S')
1558 kind =
"unsegmented";
1566 why = Form(
"bad exp anchor (fit_adc=%.1f)", c.
fit_adc);
1568 <<
" -> gain 0; " << why
1569 <<
" (beam n=" << c.
n_samples <<
")" << std::endl;
1572 for (Int_t i = 0; i < Int_t(chans.size()); i++)
1575 <<
" a.u./ADC" << std::endl;
1600 Int_t n_cal = 0, n_uncal = 0, n_ridge = 0, n_aligned = 0;
1601 for (Int_t i = 0; i < Int_t(chans.size()); i++) {
1606 if (chans[i].ridge_ratio > 0.0)
1609 for (Int_t s = 1; s <= 16; s++)
1610 if (align.
ok && align.
factors[s] != 1.0)
1613 const TString line =
1614 Form(
"[calibration] %s: gates %d/17, channels %d calibrated (%d not), "
1615 "ridge measured on %d/16 strips, alignment on %d/16",
1616 file_label.Data(), n_gates, n_cal, n_uncal, n_ridge, n_aligned);
1617 std::cout << line << std::endl;
1638 Int_t run,
const std::vector<FileSpec> &specs) {
1639 std::vector<Double_t> per_strip[18], per_strip_off[18];
1640 for (Int_t k = 0; k < Int_t(specs.size()); k++) {
1642 TFile *cf = IO::OpenForReading(sub);
1643 if (!cf || cf->IsZombie()) {
1648 TTree *t =
static_cast<TTree *
>(cf->Get(
"calibration"));
1649 if (!t || t->GetEntries() < 1 || !t->GetBranch(
"RidgeRatio") ||
1650 !t->GetBranch(
"RidgeOffset")) {
1655 Float_t rr[18] = {0}, ro[18] = {0};
1656 t->SetBranchAddress(
"RidgeRatio", rr);
1657 t->SetBranchAddress(
"RidgeOffset", ro);
1659 for (Int_t s = 1; s <= 16; s++)
1661 per_strip[s].push_back(Double_t(rr[s]));
1662 per_strip_off[s].push_back(Double_t(ro[s]));
1670 Double_t med[18] = {0}, med_off[18] = {0};
1671 std::cout <<
"Run " << run <<
" ridge-ratio aggregation:" << std::endl;
1672 for (Int_t s = 1; s <= 16; s++) {
1673 std::vector<Double_t> &v = per_strip[s];
1674 std::vector<Double_t> &vo = per_strip_off[s];
1676 std::cerr <<
" strip " << s <<
": only " << v.size()
1677 <<
" subfiles measured the ridge; leaving per-subfile gains"
1681 std::sort(v.begin(), v.end());
1682 std::sort(vo.begin(), vo.end());
1683 Int_t m = Int_t(v.size());
1686 Double_t lo = v[m / 4], hi = v[(3 * m) / 4];
1687 Double_t lo_off = vo[m / 4], hi_off = vo[(3 * m) / 4];
1690 std::cout <<
" strip " << s <<
" ratio=" << Form(
"%.4f", med[s])
1691 <<
" IQR " << Form(
"%.4f", lo) <<
"-" << Form(
"%.4f", hi)
1692 <<
" offset=" << Form(
"%.4f", med_off[s]) <<
" x C_long IQR "
1693 << Form(
"%.4f", lo_off) <<
"-" << Form(
"%.4f", hi_off)
1694 <<
" from " << m <<
" subfiles" << std::endl;
1697 Int_t n_rewritten = 0;
1698 for (Int_t k = 0; k < Int_t(specs.size()); k++) {
1700 TFile *cf = IO::OpenForWriting(sub,
"UPDATE");
1701 if (!cf || cf->IsZombie()) {
1706 TTree *t =
static_cast<TTree *
>(cf->Get(
"calibration"));
1707 if (!t || t->GetEntries() < 1 || !t->GetBranch(
"LongAnchor") ||
1708 !t->GetBranch(
"OffsetLeft")) {
1713 Float_t gl[16] = {0}, gr[16] = {0}, ol[16] = {0}, orr[16] = {0},
1714 la[18] = {0}, rr[18] = {0}, ro[18] = {0};
1715 t->SetBranchAddress(
"GainLeft", gl);
1716 t->SetBranchAddress(
"GainRight", gr);
1717 t->SetBranchAddress(
"OffsetLeft", ol);
1718 t->SetBranchAddress(
"OffsetRight", orr);
1719 t->SetBranchAddress(
"LongAnchor", la);
1720 t->SetBranchAddress(
"RidgeRatio", rr);
1721 t->SetBranchAddress(
"RidgeOffset", ro);
1723 Bool_t changed = kFALSE;
1724 for (Int_t s = 1; s <= 16; s++) {
1725 if (med[s] <= 0 || la[s] <= 0)
1729 Double_t anchor = med[s] * Double_t(la[s]);
1732 const Float_t g = Float_t(1.0 / anchor);
1733 const Float_t off = Float_t(med_off[s] * Double_t(la[s]));
1741 rr[s] = Float_t(med[s]);
1742 ro[s] = Float_t(med_off[s]);
1746 TTree *nt = t->CloneTree(0);
1749 nt->Write(
"calibration", TObject::kOverwrite);
1755 std::cout <<
" rewrote short gains and offsets in " << n_rewritten
1756 <<
" subfiles" << std::endl;
1761 InitUtils::SetROOTPreferences(PlotSaveFormat::kPNG,
1764 gROOT->SetBatch(kTRUE);
1766 std::vector<FileSpec> specs;
1767 if (file_label.IsNull()) {
1769 if (specs.empty()) {
1770 std::cerr <<
"No file specs from FileSet::BuildProcessedFileSpecs()"
1777 std::cerr <<
"Could not resolve file label '" << file_label <<
"'"
1786 Int_t n_specs = Int_t(specs.size());
1788 std::set<Int_t> runs;
1789 for (Int_t k = 0; k < n_specs; k++)
1790 runs.insert(specs[k].run);
1793 TMath::Min(Int_t(std::thread::hardware_concurrency()), n_specs);
1794 n_workers = TMath::Min(n_workers,
Constants::cfg.MAX_FUSED_WORKERS);
1797 std::cout <<
"calibrate-beam: " << n_specs <<
" subfiles on " << n_workers
1798 <<
" workers" << std::endl;
1800 std::queue<Int_t> work;
1801 for (Int_t k = 0; k < n_specs; k++)
1803 std::mutex work_mutex;
1805 std::vector<std::thread> workers;
1806 for (Int_t w = 0; w < n_workers; w++) {
1807 workers.emplace_back([&]() {
1811 std::lock_guard<std::mutex> lk(work_mutex);
1823 for (Int_t w = 0; w < Int_t(workers.size()); w++)
void ComputeLRGainMatch(std::vector< ChannelCal > &chans, const StripPairSamples pairs[18], const TString &plot_subdir)
const Long64_t kRidgeMinPerSlice
Double_t StripTotalAdc(const UShort_t *l, const UShort_t *r, UShort_t s0, UShort_t s17, Int_t s)
Double_t SmoothedPeakIn(TH1D *proj, Double_t lo, Double_t hi)
Peak of a smoothed projection in [lo, hi]: the highest bin of the window, refined to sub-bin precisio...
const Int_t kGmBins
L/R gain matching, after the check_LR notebook's recipe (37Cl_an_check_LR.ipynb, "Save 2-pass calibra...
const Double_t kRidgeBandHi
void SaveDynamicRangeOverlay(const FileSpec &spec, const TString &plot_subdir, const TString &file_label)
void RobustPeakSeed(const std::vector< Float_t > &v, Double_t &mode, Double_t &sigma)
Robust peak/width estimate used to seed the beam-peak Gaussian fit, and as the fallback anchor when t...
const Double_t kRidgeRatioLo
BeamFit2D FindBeamGateStrips(const FileSpec &spec, Int_t sx, Int_t sy, const TString &run_label, const TString &plot_subdir, Bool_t save_plot=kTRUE)
The beam gate for one strip: a 2D Gaussian on the (strip sx, strip sy) raw totals.
Bool_t TheilSenLine(const std::vector< Double_t > &x, const std::vector< Double_t > &y, Double_t &slope, Double_t &intercept)
Theil-Sen robust line: slope = median of pairwise slopes, intercept = median residual.
const Double_t kRidgeBandLo
Double_t RidgeShortAnchor(const std::vector< Float_t > &v_short, const std::vector< Float_t > &v_long, Double_t c_long, Double_t &slope_out, Double_t &intercept_out, RidgeFit &dbg)
Robust line through the single-particle charge-sharing ridge, returning the fitted line's short-axis ...
const Long64_t kMinSamples
Double_t GmFindPeak(const std::vector< Float_t > &v, Double_t lo, Double_t hi, Double_t skip_frac)
Histogram-mode peak finder, mirroring the notebook's find_peak(): histogram v over [lo,...
Char_t LongSide(Int_t strip)
const Long64_t kSampleCap
void ReduceToAnchors(std::vector< ChannelCal > &chans, std::vector< std::vector< Float_t > > &samples, const StripPairSamples pairs[18], const TString &plot_subdir)
Cathode uses median + IQR/1.349 (asymmetric tail, no clean peak).
void WriteCalibrationTree(TFile *dst, const std::vector< ChannelCal > &chans, const StripAlignmentResult *align)
Writes a one-row calibration tree into the open file dst (typically a per-subfile ....
Int_t GatePartner(Int_t s)
The strip whose total is paired with strip s to gate it: the one before it, except strips 0 and 1 whi...
void SaveRidgeFitPlots(const StripPairSamples pairs[18], const RidgeFit dbg[18], const TString &plot_subdir)
One plot per strip under <plot_subdir>/ridge, named ridge_s<NN>: the beam-gated long-vs-short plane,...
void CollectAnchorSamplesOneSubfile(const FileSpec &spec, const std::vector< ChannelCal > &chans, const BeamFit2D gate[18], std::vector< std::vector< Float_t > > &samples, StripPairSamples pairs[18])
Every strip carries its own beam gate, on its own total and its neighbour's (GatePartner).
Double_t Median(std::vector< T > &v)
StripAlignmentResult FindStripCentroidAlignment(const FileSpec &spec, const TString &plot_subdir, const TString &file_label)
Per-strip two-point alignment of the strip total.
const Double_t kRidgeShortMaxFrac
const Double_t kRidgeShortMinFrac
Ridge-slope short anchor.
Bool_t IsCalibrated(const ChannelCal &c)
Double_t Gain(const ChannelCal &c)
const Double_t kRidgeRatioHi
const Long64_t kRidgeNoiseFloor
void WriteCalibrationToEvents(const FileSpec &spec, const std::vector< ChannelCal > &chans, const StripAlignmentResult *align)
Writes the per-channel gain table (tree "calibration") into the subfile's own events file.
const Int_t kRidgeMinSlices
const Long64_t kPairCap
(L, R) pair cap for the L/R gain-matching passes.
std::mutex g_plot_mutex
Serialises all plotting and canvas work.
static SpotFit FitSpotFromPoints(TH2F *h, std::vector< std::pair< Float_t, Float_t > > &pts, const Moments2D &seed)
A beam spot from a coarse histogram and the same events unbinned: the sample's sigma-clipped moments ...
static void DrawEllipse(const BeamFit2D &b, Double_t n, Color_t color)
Draw the n-sigma contour of a fitted spot on the current pad.
static Bool_t InEllipseXY(const BeamFit2D &b, Double_t x, Double_t y, Double_t n)
Whether a point lies inside the fitted ellipse at n sigma: the correlation-aware Mahalanobis chi2 bel...
static Moments2D ComputeMoments(TH2F *h, Int_t lo_bx, Int_t hi_bx, Int_t lo_by, Int_t hi_by, Double_t thresh, Double_t bw_x, Double_t bw_y)
Second moments of a histogram region above a threshold.
static Moments2D SeedSpotMoments(TH2F *h, Int_t seed_half_bins)
Second moments of a coarse spot histogram's seed window: the maximum bin, ± seed_half_bins around it,...
static void Run(const TString &file_label="")
Calibrate every configured subfile, then aggregate per run.
static void CalibrateBeamOneSubfile(const FileSpec &spec, const std::vector< ChannelCal > &chans_template)
Calibrate one subfile end to end.
static std::vector< ChannelCal > BuildChannels()
One ChannelCal per readout channel in the active channel map.
static void AggregateRidgeRatiosForRun(Int_t run, const std::vector< FileSpec > &specs)
Replace each subfile's ridge ratio and short-end offset with the run-level medians.
static TString EventsName(const FileSpec &s)
Filename of the built-events ROOT file for a subfile.
static TString FileLabel(const FileSpec &s)
Human-readable label identifying a subfile.
static FileSpec ResolveFileSpec(const TString &file_label)
Recover the FileSpec behind a label from FileLabel().
static std::vector< FileSpec > BuildProcessedFileSpecs()
Every subfile that already has processed output.
static TString DatasetDir()
Absolute path to the active dataset directory, analysis/<iso>.
static TString ResultsDir()
Absolute path to the directory receiving generated output.
Double_t ActiveStripEMaxAdc()
Upper bound of the per-strip energy range, in ADC.
Bool_t SavePlots()
FileInSample(), by the name the plot call sites use.
Bool_t InPlotSample(Int_t k)
Whether the k-th file (0-based, in file order) of an epoch's list is in the plot sample.
std::ostream & Detail()
std::cout for a file in the sample, a stream that discards otherwise: the per-file detail lines (per ...
const DatasetConfig & cfg
The active dataset's configuration, flat block.
void SetPlotsThisFile(Bool_t on)
Mark the file this thread is about to process as in (or out of) the plot sample.
std::ostream & DetailErr()
A fitted 2-D Gaussian beam spot.
Double_t rho
Correlation coefficient, in [-1, 1].
Double_t sigma_y
Widths along each axis.
Result of a beam spot fit: the spot to use, and whether the Gaussian fit (rather than the clipped-mom...
Double_t chi2_ndf
The fit's reduced chi2; -1 when it did not run.
Bool_t fit_used
kTRUE when fit is the fit, not the fallback.
BeamFit2D fit
The spot to use; ok when usable.
One readout channel's beam-peak calibration.
Double_t ridge_offset
Short channels only: offset_adc in units of C_long, from this subfile's own ridge fit,...
Double_t offset_adc
ADC subtracted from this channel before its gain, only when the channel fired (a reading of 0 stays 0...
Char_t side
Readout end: 'L' or 'R' for the split strips 1-16, 'S' for the unsplit end strips 0 and 17,...
TString name
Channel name from the active channel map.
Double_t gain
Left/right gain-match override.
Double_t fit_adc
Modal beam peak, in ADC: the modal bin of the channel's beam-peak histogram (100 bins over the 5th-95...
Int_t strip
Anode strip index this channel reads; -1 for the cathode.
Long64_t n_samples
Events the anchor was taken from.
Double_t ridge_ratio
Short channels only: C_short / C_long = -1 / slope from this subfile's own ridge fit,...
A view over one event's energies, decoding raw ADC into calibrated units.
void Decode()
Decode the currently loaded entry into the value members.
Bool_t Attach(TTree *t)
Bind to an events tree and set up the branch addresses.
Double_t Total(Int_t strip) const
A strip's deposit: the sum of its ends, or the unsegmented value.
Bool_t is_normed
Whether a calibration tree was found.
Double_t grid
Grid energy.
One input file: a run number and the subfile suffix within it.
std::vector< Double_t > offsets_found[2]
Double_t short_anchor_adc[18]
Double_t long_anchor_adc[18]
Double_t median_offset[2]
std::vector< Double_t > ratios_found[2]
Double_t short_offset_adc[18]
Second moments of a 2-D distribution over a bin range.
Double_t sigma_y
Weighted RMS widths.
Double_t mu_y
Weighted centroid.
Double_t weight
Total weight included; zero means the range held nothing above threshold.
Everything SaveRidgeFitPlots needs to redraw a fit: the slice medians it was fitted to,...
std::vector< Double_t > y
std::vector< Double_t > ey
std::vector< Double_t > x
Output of the strip centroid alignment, the post-gain calibration step.
Double_t centroids[18]
Measured beam peak per strip before alignment, in calibrated units.
Double_t pileups[18]
Measured two-particle pile-up peak per strip before alignment, in calibrated units; 0 where none was ...
Double_t factors[18]
Per-strip multiplicative alignment factors, applied to every end.
Double_t offsets[18]
Per-strip additive offsets in calibrated units, applied after the factor to the strip total; EnergyVi...
Bool_t ok
Whether the step produced usable results.
Paired (L, R) raw-ADC samples for one strip, collected UNGATED from events where both ends fire,...
std::vector< Float_t > gated_long
std::vector< Float_t > gated_short