6 for (Int_t i = 0; i < 64; i++) {
23 std::map<Int_t, TH2F *>::iterator it;
24 for (it = m_scatter.begin(); it != m_scatter.end(); ++it)
29Int_t StripSumScatter::ReacIndex(Int_t reac) {
30 return reac -
Constants::cfg.STRIP_SUM_SCATTER_CONFIG.REACTION_STRIP_MIN;
39 return TMath::Min(reac + TMath::Max(1, it->second), 17);
44 return TMath::Max(hi, reac + 1);
47Double_t StripSumScatter::s_jumpSigma[18] = {0.0};
48Double_t StripSumScatter::s_stripSigma[18] = {0.0};
51 return (strip >= 0 && strip < 18) ? s_jumpSigma[strip] : 0.0;
55 return (strip >= 0 && strip < 18) ? s_stripSigma[strip] : 0.0;
59 for (Int_t s = 0; s < 18; s++)
60 s_jumpSigma[s] = sigma[s];
64 for (Int_t s = 0; s < 18; s++)
65 s_stripSigma[s] = sigma[s];
73Bool_t StripSumScatter::BeamUpstreamOf(
const EnergyView &ev, Int_t reac) {
77 for (Int_t s = 1; s < reac; s++)
84static Double_t ClippedWidth(
const std::vector<Double_t> &values) {
85 const Int_t kClipPasses = 3;
86 const Double_t kClipNSigma = 3.0;
87 Double_t mean = 0.0, width = 0.0;
88 for (Int_t pass = 0; pass < kClipPasses; pass++) {
89 Double_t sum = 0.0, sum2 = 0.0;
91 for (Int_t k = 0; k < Int_t(values.size()); k++) {
92 const Double_t v = values[k];
93 if (pass > 0 && TMath::Abs(v - mean) > kClipNSigma * width)
101 mean = sum / Double_t(kept);
102 width = TMath::Sqrt(TMath::Max(0.0, sum2 / Double_t(kept) - mean * mean));
107Bool_t StripSumScatter::MeasureBeamNoise(TChain *chain, Double_t *jump_sigma,
108 Double_t *strip_sigma) {
109 const Long64_t kMaxEvents = 200000;
110 const Long64_t kMinEvents = 1000;
111 for (Int_t s = 0; s < 18; s++) {
113 strip_sigma[s] = 0.0;
120 EnableEventBranches(chain);
121 const Long64_t n = TMath::Min(chain->GetEntries(), kMaxEvents);
122 std::vector<std::vector<Double_t>> diff(18), deposit(18);
123 for (Long64_t j = 0; j < n; j++) {
126 if (!AllStripsFired(ev) || IsPileup(ev) || IsNoise(ev))
128 if (
Constants::cfg.STRIP_SUM_SCATTER_CONFIG.REJECT_OFFBEAM && IsOffbeam(ev))
130 if (IsParityAsymmetric(ev))
132 deposit[0].push_back(ev.
total[0]);
133 for (Int_t s = 1; s < 18; s++) {
134 diff[s].push_back(ev.
total[s] - ev.
total[s - 1]);
135 deposit[s].push_back(ev.
total[s]);
138 chain->ResetBranchAddresses();
139 if (Long64_t(deposit[0].size()) < kMinEvents)
144 for (Int_t s = 0; s < 18; s++)
145 strip_sigma[s] = ClippedWidth(deposit[s]);
146 for (Int_t s = 1; s < 18; s++)
147 jump_sigma[s] = ClippedWidth(diff[s]);
148 for (Int_t s = 1; s <= 16; s++)
149 if (!(jump_sigma[s] > 0.0) || !(strip_sigma[s] > 0.0))
154void StripSumScatter::EnableEventBranches(TChain *chain) {
155 chain->SetBranchStatus(
"*", 0);
156 chain->SetBranchStatus(
"Left_0_17_dE", 1);
157 chain->SetBranchStatus(
"RightdE", 1);
158 chain->SetBranchStatus(
"Cathode", 1);
160 if (chain->GetBranch(
"SeedTs"))
161 chain->SetBranchStatus(
"SeedTs", 1);
165 if (
Constants::cfg.STRIP_SUM_SCATTER_CONFIG.PLOT_PARITY_REJECTED_GRID &&
166 chain->GetBranch(
"Grid"))
167 chain->SetBranchStatus(
"Grid", 1);
170Bool_t StripSumScatter::AllStripsFired(
const EnergyView &ev) {
175 for (Int_t s = 1; s <= 16; s++)
176 if (!(ev.
total[s] > 0.0))
182 const Double_t kReacJumpMin =
JumpMin(reac);
183 const Double_t kReacJumpMax =
185 const Double_t kSmoothMaxStep =
186 Constants::cfg.STRIP_SUM_SCATTER_CONFIG.REQUIRE_SMOOTHNESS_MAX_STEP;
187 const Int_t kSmoothHiStrip =
188 Constants::cfg.STRIP_SUM_SCATTER_CONFIG.REQUIRE_SMOOTHNESS_END_STRIP;
189 const Double_t kEndStripMax =
192 if (!AllStripsFired(ev))
196 if (!BeamUpstreamOf(ev, reac))
198 Double_t reac_jump = ev.
total[reac] - ev.
total[reac - 1];
199 if (!(reac_jump > kReacJumpMin && reac_jump < kReacJumpMax))
201 if (!(ev.
total[reac] > 1.0 + kReacJumpMin &&
202 ev.
total[reac] < 1.0 + kReacJumpMax))
205 for (Int_t s = reac + 1; s <= kSmoothHiStrip; s++)
206 if (TMath::Abs(ev.
total[s] - ev.
total[s - 1]) > kSmoothMaxStep)
209 return ev.
total[16] < kEndStripMax;
211 Constants::cfg.STRIP_SUM_SCATTER_CONFIG.REQUIRE_STRIP_16_BELOW_BEAM ? 16
213 return ev.
total[end_strip] < kEndStripMax;
216Bool_t StripSumScatter::IsPureBeam(
const EnergyView &ev,
220 if (!AllStripsFired(ev))
225 if (!PassesGate(be.
s1_s2, ev, 1, 2))
228 if (!PassesGate(be.
s0_s1, ev, 0, 1))
232 if (!PassesGate(be.
s15_s16, ev, 15, 16))
235 if (!PassesGate(be.
s16_s17, ev, 16, 17))
241Bool_t StripSumScatter::IsPileup(
const EnergyView &ev) {
243 const Double_t kThresh =
245 const Int_t kMinStrips =
248 for (Int_t s = 1; s <= 16; s++)
249 if (ev.
total[s] >= kThresh && ++n >= kMinStrips)
253 const Double_t kThresh =
255 const Int_t kMinStrips =
258 for (Int_t s = 1; s <= 16; s++)
259 if (ev.
total[s] >= kThresh && ++n >= kMinStrips)
264Bool_t StripSumScatter::IsNoise(
const EnergyView &ev) {
266 const Double_t kThresh =
268 const Int_t kMinStrips =
271 for (Int_t s = 1; s <= 16; s++)
272 if (ev.
total[s] <= kThresh && ++n >= kMinStrips)
276 const Double_t kThresh =
278 const Int_t kMinStrips =
281 for (Int_t s = 1; s <= 16; s++)
282 if (ev.
total[s] <= kThresh && ++n >= kMinStrips)
287Bool_t StripSumScatter::IsOffbeam(
const EnergyView &ev) {
289 const Int_t kMinStrips =
293 const Double_t kBeamRef = 1.0;
295 for (Int_t s = 1; s <= 16; s++)
296 if (TMath::Abs(ev.
total[s] - kBeamRef) >= kDist && ++n >= kMinStrips)
305Bool_t StripSumScatter::IsParityAsymmetric(
const EnergyView &ev) {
309 Double_t odd = 0.0, even = 0.0;
310 for (Int_t s = 1; s <= 16; s++)
311 ((s % 2) ? odd : even) += ev.
total[s];
314 return TMath::Abs(even / odd - 1.0) > kMax;
317Double_t StripSumScatter::SumRange(
const Double_t *total, Int_t lo, Int_t hi) {
319 for (Int_t s = lo; s <= hi; s++)
328 y = SumRange(total,
YLoOf(reac),
YHiOf(reac));
331std::vector<GateSpec> StripSumScatter::ActiveGates() {
332 std::vector<GateSpec> gates;
351 TString name =
"StripSumScatter_cache";
361 Int_t sx, Int_t sy) {
362 Double_t g0 = ev.
total[sx];
363 Double_t g1 = ev.
total[sy];
364 if (!(g0 > 0.0 && g1 > 0.0))
366 const Double_t kGateNSigmaX =
368 const Double_t kGateNSigmaY =
374StripSumScatter::FindBeamGate(TChain *chain, Int_t sx, Int_t sy,
375 const std::vector<GateSpec> &prior_specs,
376 const std::vector<BeamFit2D> &prior_gates,
377 const TString &tag,
const TString &subdir) {
381 const Int_t kSeedHalfBins =
383 const Double_t kSeedFrac = 0.3;
384 const Long64_t kSampleMaxPoints =
390 EnableEventBranches(chain);
392 new TH2F(Form(
"h2_beamgate_s%d_s%d_%s", sx, sy, tag.Data()),
393 Form(
";#DeltaE strip %d [a.u.];#DeltaE strip %d [a.u.]", sx, sy),
394 kGateBins, kGateMin, kGateMax, kGateBins, kGateMin, kGateMax);
395 h->SetDirectory(
nullptr);
396 Long64_t n = chain->GetEntries();
398 for (Long64_t j = 0; j < n; j += stride) {
402 Bool_t prior_ok = kTRUE;
403 for (Int_t gi = 0; gi < Int_t(prior_specs.size()); gi++)
404 if (!PassesGate(prior_gates[gi], ev, prior_specs[gi].sx,
405 prior_specs[gi].sy)) {
411 Double_t x = ev.
total[sx];
412 Double_t y = ev.
total[sy];
413 if (x > 0.0 && y > 0.0)
416 if (h->GetEntries() < 100) {
420 Double_t bw_x = h->GetXaxis()->GetBinWidth(1);
421 Double_t bw_y = h->GetYaxis()->GetBinWidth(1);
422 Int_t bx = 0, by = 0, bz = 0;
423 h->GetMaximumBin(bx, by, bz);
424 Double_t peak_val = h->GetBinContent(bx, by);
425 Int_t lo_bx = std::max(1, bx - kSeedHalfBins);
426 Int_t hi_bx = std::min(h->GetNbinsX(), bx + kSeedHalfBins);
427 Int_t lo_by = std::max(1, by - kSeedHalfBins);
428 Int_t hi_by = std::min(h->GetNbinsY(), by + kSeedHalfBins);
430 kSeedFrac * peak_val, bw_x, bw_y);
443 const Double_t kGateNSigmaX =
445 const Double_t kGateNSigmaY =
449 TCanvas *c = PlottingUtils::GetConfiguredCanvas(kFALSE);
450 PlottingUtils::ConfigureAndDraw2DHistogram(h, c);
455 Double_t sum = sxx + syy;
456 Double_t diff = sxx - syy;
457 Double_t det = TMath::Sqrt(diff * diff + 4.0 * sxy * sxy);
458 Double_t lambda1 = 0.5 * (sum + det);
459 Double_t lambda2 = 0.5 * (sum - det);
460 Double_t theta = 0.5 * TMath::ATan2(2.0 * sxy, diff) * 180.0 / TMath::Pi();
462 new TEllipse(out.
mu_x, out.
mu_y, kGateNSigmaX * TMath::Sqrt(lambda1),
463 kGateNSigmaX * TMath::Sqrt(lambda2), 0, 360, theta);
465 e->SetLineColor(kRed + 1);
468 PlottingUtils::SaveFigure(c, Form(
"beam_gate_s%d_s%d", sx, sy), subdir,
469 PlotSaveOptions::kLINEAR);
476void StripSumScatter::DrawTraceSet(
const std::vector<TGraph *> &traces,
478 for (Int_t i = 0; i < Int_t(traces.size()); i++) {
479 traces[i]->SetLineColor(color);
480 traces[i]->SetLineWidth(1);
481 traces[i]->Draw(
"L SAME");
492void StripSumScatter::DrawAltDecodeRegionTraces(Int_t reac, TCutG *cutAn,
497 const Int_t kTracesPerRegion =
499 UInt_t bit = (1u << ReacIndex(reac));
501 std::vector<const TraceEvt *> ev_beam, ev_aa, ev_an;
502 for (Int_t k = 0; k < Int_t(m_reservoir.size()); k++) {
503 const TraceEvt &e = m_reservoir[k];
505 if (Int_t(ev_beam.size()) < kTracesPerRegion)
506 ev_beam.push_back(&e);
512 for (Int_t s = 0; s < 18; s++)
513 td[s] = Double_t(e.
total[s]);
514 Double_t x = 0.0, y = 0.0;
516 if (cutAn && Int_t(ev_an.size()) < kTracesPerRegion &&
517 cutAn->IsInside(x, y))
519 else if (cutAa && Int_t(ev_aa.size()) < kTracesPerRegion &&
520 cutAa->IsInside(x, y))
525 for (Int_t s = 0; s < 18; s++)
527 for (Int_t s = 1; s <= 16; s++) {
528 std::vector<Double_t> v;
529 for (Int_t k = 0; k < Int_t(ev_beam.size()); k++) {
531 Double_t(ev_beam[k]->long_au[s]) + Double_t(ev_beam[k]->short_au[s]);
537 std::sort(v.begin(), v.end());
538 Double_t med = v[v.size() / 2];
543 std::vector<TGraph *> g_beam, g_aa, g_an;
544 std::vector<const std::vector<const TraceEvt *> *> srcs;
545 srcs.push_back(&ev_beam);
546 srcs.push_back(&ev_aa);
547 srcs.push_back(&ev_an);
548 std::vector<std::vector<TGraph *> *> dsts;
549 dsts.push_back(&g_beam);
550 dsts.push_back(&g_aa);
551 dsts.push_back(&g_an);
552 for (Int_t c = 0; c < 3; c++) {
553 for (Int_t k = 0; k < Int_t(srcs[c]->size()); k++) {
554 const TraceEvt &e = *(*srcs[c])[k];
556 for (Int_t s = 0; s < 18; s++)
558 for (Int_t s = 1; s <= 16; s++)
564 dsts[c]->push_back(TraceFromTotal(alt));
568 const char *tag = long_only_is_current ?
"sum" :
"longonly";
569 DrawRegionTraces(Form(
"region_traces_reac%d_altdecode_%s", reac, tag),
570 "strip_sum_scatter", g_beam, g_aa, g_an, 0.6, 1.6,
572 DrawRegionMeanTraces(
573 Form(
"region_mean_traces_reac%d_altdecode_%s", reac, tag),
574 "strip_sum_scatter", g_beam, g_aa, g_an, 0.6, 1.6,
"#DeltaE [a.u.]");
578 Form(
"/plots/strip_sum_scatter/region_traces_reac%d_altdecode_%s.txt",
580 std::ofstream out(txt.Data());
582 out <<
"# selected under "
583 << (long_only_is_current ?
"long-side-only" :
"L+R sum")
584 <<
" decode; alternate rendering is " << tag << std::endl;
585 out <<
"# per-strip normalisation applied to the summed decode so that the"
586 " summed BEAM median is 1.0 a.u."
588 out <<
"# strip norm then per class: <long_only> <sum> ratio "
591 const char *cls[3] = {
"beam",
"aa",
"an"};
593 for (Int_t c = 0; c < 3; c++)
594 out <<
" " << cls[c] <<
"_long " << cls[c] <<
"_sum " << cls[c]
595 <<
"_ratio " << cls[c] <<
"_shortfrac";
597 for (Int_t s = 1; s <= 16; s++) {
598 out << s <<
" " << Form(
"%.6f", norm[s]);
599 for (Int_t c = 0; c < 3; c++) {
600 Double_t sl = 0, ss = 0;
601 Int_t nn = Int_t(srcs[c]->size());
602 for (Int_t k = 0; k < nn; k++) {
603 sl += Double_t((*srcs[c])[k]->long_au[s]);
604 ss += Double_t((*srcs[c])[k]->short_au[s]);
610 Double_t sum = (sl + ss) * norm[s];
611 out <<
" " << Form(
"%.5f", sl) <<
" " << Form(
"%.5f", sum) <<
" "
612 << Form(
"%.5f", sl > 0 ? sum / sl : 0.0) <<
" "
613 << Form(
"%.5f", (sl + ss) > 0 ? ss / (sl + ss) : 0.0);
618 std::cout <<
"Wrote " << txt << std::endl;
621 for (Int_t c = 0; c < 3; c++)
622 for (Int_t k = 0; k < Int_t(dsts[c]->size()); k++)
623 delete (*dsts[c])[k];
626TGraph *StripSumScatter::TraceFromTotal(
const Float_t *total) {
629 for (Int_t s = 0; s < 18; s++)
630 td[s] = Double_t(total[s]);
634void StripSumScatter::DrawRegionTraces(
const TString &save_name,
635 const TString &subdir,
636 const std::vector<TGraph *> &beam,
637 const std::vector<TGraph *> &aa,
638 const std::vector<TGraph *> &an,
639 Double_t y_min, Double_t y_max,
640 const char *y_title) {
645 new TH2F(
"h_region_trace_frame", Form(
";Strip;%s", y_title),
646 s_hi - s_lo + 1, s_lo - 0.5, s_hi + 0.5, 100, y_min, y_max);
648 TCanvas *c = PlottingUtils::GetConfiguredCanvas(kFALSE);
650 DrawTraceSet(beam, kGray + 2);
651 DrawTraceSet(aa, kAzure + 2);
652 DrawTraceSet(an, kRed + 1);
654 TGraph *p_beam =
new TGraph(1);
655 TGraph *p_aa =
new TGraph(1);
656 TGraph *p_an =
new TGraph(1);
657 TGraph *proxies[3] = {p_beam, p_aa, p_an};
658 Int_t pcol[3] = {kGray + 2, kAzure + 2, kRed + 1};
659 for (Int_t i = 0; i < 3; i++) {
660 proxies[i]->SetPoint(0, -1e9, -1e9);
661 proxies[i]->SetLineColor(pcol[i]);
662 proxies[i]->SetLineWidth(3);
664 TLegend *leg = PlottingUtils::AddLegend(0.725, 0.875, 0.70, 0.86);
665 leg->AddEntry(p_beam,
"Beam",
"l");
666 leg->AddEntry(p_aa,
"(#alpha,#alpha')",
"l");
667 leg->AddEntry(p_an,
"(#alpha,n)",
"l");
670 PlottingUtils::SaveFigure(c, save_name, subdir, PlotSaveOptions::kLINEAR);
674void StripSumScatter::DrawRegionMeanTraces(
const TString &save_name,
675 const TString &subdir,
676 const std::vector<TGraph *> &beam,
677 const std::vector<TGraph *> &aa,
678 const std::vector<TGraph *> &an,
679 Double_t y_min, Double_t y_max,
680 const char *y_title) {
682 TH2F *frame =
new TH2F(
"h_region_mean_frame", Form(
";Strip;%s", y_title), 18,
683 -0.5, 17.5, 100, y_min, y_max);
685 frame->SetDirectory(
nullptr);
686 TCanvas *c = PlottingUtils::GetConfiguredCanvas(kFALSE);
689 const std::vector<TGraph *> *regions[3] = {&beam, &aa, &an};
690 Int_t colors[3] = {kGray + 2, kAzure + 2, kRed + 1};
691 const char *labels[3] = {
"Beam",
"(#alpha,#alpha')",
"(#alpha,n)"};
692 std::vector<TGraphErrors *> means;
693 TLegend *leg = PlottingUtils::AddLegend(0.725, 0.875, 0.70, 0.86);
695 for (Int_t r = 0; r < 3; r++) {
696 const std::vector<TGraph *> &tr = *regions[r];
699 Int_t npts = tr[0]->GetN();
700 std::vector<Double_t> mean(npts, 0.0), m2(npts, 0.0);
701 for (Int_t t = 0; t < Int_t(tr.size()); t++) {
702 Double_t *yv = tr[t]->GetY();
703 for (Int_t p = 0; p < npts; p++) {
705 m2[p] += yv[p] * yv[p];
708 Double_t nt = Double_t(tr.size());
709 TGraphErrors *ge =
new TGraphErrors(npts);
710 Double_t *xv = tr[0]->GetX();
711 for (Int_t p = 0; p < npts; p++) {
713 Double_t var = m2[p] / nt - mean[p] * mean[p];
714 ge->SetPoint(p, xv[p], mean[p]);
715 ge->SetPointError(p, 0.0, var > 0.0 ? TMath::Sqrt(var) : 0.0);
717 ge->SetLineColor(colors[r]);
719 ge->SetFillColorAlpha(colors[r], 0.15);
722 leg->AddEntry(ge, labels[r],
"l");
725 for (Int_t i = 0; i < Int_t(means.size()); i++)
726 means[i]->Draw(
"LX SAME");
729 PlottingUtils::SaveFigure(c, save_name, subdir, PlotSaveOptions::kLINEAR);
730 for (Int_t i = 0; i < Int_t(means.size()); i++)
737void StripSumScatter::TraceYRange(
const std::vector<TGraph *> &beam,
738 const std::vector<TGraph *> &aa,
739 const std::vector<TGraph *> &an,
740 Double_t &y_min, Double_t &y_max) {
741 y_min = std::numeric_limits<Double_t>::max();
742 y_max = -std::numeric_limits<Double_t>::max();
743 const std::vector<TGraph *> *sets[3] = {&beam, &aa, &an};
744 for (Int_t si = 0; si < 3; si++) {
745 const std::vector<TGraph *> &v = *sets[si];
746 for (Int_t i = 0; i < Int_t(v.size()); i++) {
747 Double_t x = 0.0, y = 0.0;
748 for (Int_t k = 0; k < v[i]->GetN(); k++) {
749 v[i]->GetPoint(k, x, y);
750 if (x < 0.5 || x > 16.5)
763 Double_t pad = 0.05 * (y_max - y_min);
770TCutG *StripSumScatter::PromptCut(TCanvas *c,
const char *name,
772 std::cout <<
" >>> draw the " << label
773 <<
" region: left-click vertices, double-click to close"
776 TCutG *cut =
static_cast<TCutG *
>(c->WaitPrimitive(
"CUTG",
"CutG"));
778 std::cerr <<
" no " << label <<
" cut drawn" << std::endl;
782 cut->SetLineColor(kBlack);
783 cut->SetLineWidth(2);
795void StripSumScatter::SaveRegionCuts(Int_t reac, TCutG *cut_an, TCutG *cut_aa) {
799TCutG *StripSumScatter::LoadRegionCut(
const char *name, Int_t reac) {
803void StripSumScatter::SmoothTrace(
const Double_t *in, Double_t *out,
805 Int_t half = width / 2;
806 for (Int_t s = 0; s < 18; s++) {
807 Int_t lo = TMath::Max(0, s - half);
808 Int_t hi = TMath::Min(17, s + half);
810 for (Int_t t = lo; t <= hi; t++)
812 out[s] = sum / Double_t(hi - lo + 1);
821void StripSumScatter::SavitzkyGolay(
const Double_t *in, Double_t *out) {
822 static const Int_t K = 2;
826 static const Double_t sg_coeff[2 * K + 1] = {
834 for (Int_t s = 0; s < 18; s++) {
835 Int_t lo = TMath::Max(0, s - K);
836 Int_t hi = TMath::Min(17, s + K);
841 for (Int_t t = lo; t <= hi; t++) {
842 Int_t offset = t - s + K;
843 val += sg_coeff[offset] * in[t];
844 wsum += sg_coeff[offset];
848 out[s] = (wsum != 0.0) ? val / wsum : in[s];
856Int_t StripSumScatter::FindTrigger(
const Double_t *td,
const Double_t *base,
857 Double_t beam_sigma) {
858 const Int_t s_lo = 2;
859 const Int_t s_hi = 16;
861 const Double_t frac =
864 const Double_t nsigma =
867 Double_t peak_signal = -1.0e30;
869 for (Int_t s = s_lo; s <= s_hi; s++) {
870 Double_t signal = td[s] - base[s];
871 if (signal > peak_signal)
872 peak_signal = signal;
875 if (peak_signal < nsigma)
878 Double_t thresh = frac * peak_signal;
880 for (Int_t s = s_lo; s <= s_hi; s++) {
881 Double_t signal = td[s] - base[s];
883 if (signal >= thresh && signal >= nsigma)
891TGraph *StripSumScatter::SmoothedTraceFromTotal(
const Float_t *total) {
892 Double_t td[18], sgd[18];
893 for (Int_t s = 0; s < 18; s++)
894 td[s] = Double_t(total[s]);
895 SavitzkyGolay(td, sgd);
899void StripSumScatter::ClusterVarHists(Int_t reac, TCutG *cut_aa, TCutG *cut_an,
900 const TString &subdir) {
903 const char *vkey[NV] = {
"energy",
"peak3",
"plateau",
904 "tail",
"reacstrip",
"mult",
905 "trigtaildev",
"reacslope3",
"beamdev"};
906 const char *vtitle[NV] = {
907 "#Sigma_{all strips}(#DeltaE#minus1) [a.u.]",
908 "#Sigma_{trig#pm1}#DeltaE (0 if no trigger) [a.u.]",
909 "Plateau Excess #Sigma_{trig+1..trig+POST}(#DeltaE#minus1) [a.u.]",
910 "#DeltaE(s17) [a.u.]",
912 "Both-side Multiplicity (strips 1-16)",
913 "|#DeltaE#minusbeam| at trigger + at s17 [a.u.]",
914 "#DeltaE(reac+3) #minus #DeltaE(reac#minus3) [a.u.]",
915 "RMS_{8-17}(#DeltaE#minusbeam) [a.u.]"};
916 const char *clabel[NC] = {
"beam",
"(a,a')",
"(a,n)"};
920 const Int_t kClusterSmoothWindow =
924 std::vector<Double_t> vals_raw[NC][NV];
928 std::vector<Double_t> vals_sg[NC][NV];
929 UInt_t bit = (1u << ReacIndex(reac));
936 for (Int_t s = 0; s < 18; s++)
939 for (Int_t k = 0; k < Int_t(m_reservoir.size()); k++)
940 if (m_reservoir[k].beam_flat) {
941 for (Int_t s = 0; s < 18; s++)
942 base[s] += Double_t(m_reservoir[k].total[s]);
945 for (Int_t s = 0; s < 18; s++)
946 base[s] = (nbeam > 0) ? base[s] / Double_t(nbeam) : 1.0;
952 Double_t beam_sumsq = 0.0;
953 Long64_t beam_npt = 0;
954 for (Int_t k = 0; k < Int_t(m_reservoir.size()); k++)
955 if (m_reservoir[k].beam_flat)
956 for (Int_t s = 0; s < 18; s++) {
957 Double_t d = Double_t(m_reservoir[k].total[s]) - base[s];
961 Double_t beam_sigma =
962 (beam_npt > 0) ? TMath::Sqrt(beam_sumsq / beam_npt) : 0.0;
966 Long64_t triggered = 0, no_trigger = 0;
968 for (Int_t k = 0; k < Int_t(m_reservoir.size()); k++) {
969 for (Int_t s = 0; s < 18; s++)
970 td_all[s] = Double_t(m_reservoir[k].total[s]);
971 if (FindTrigger(td_all, base, beam_sigma) >= 0)
976 const Double_t reac_onset_gate =
978 const Double_t cf_frac =
980 std::cout <<
" beam reference: mean+RMS of " << nbeam
981 <<
" pure-beam events (fitted s0,s1 & s16,s17 ellipses); "
982 << Form(
"noise sigma=%.4f", beam_sigma) << std::endl;
983 std::cout << Form(
" reaction onset: gate %g-sigma = %.4f, CF fraction %g; "
984 "triggered %lld of %lld (no trigger: %lld)",
986 reac_onset_gate, cf_frac, Long64_t(triggered),
987 Long64_t(m_reservoir.size()), Long64_t(no_trigger))
990 for (Int_t k = 0; k < Int_t(m_reservoir.size()); k++) {
991 const TraceEvt &e = m_reservoir[k];
993 for (Int_t s = 0; s < 18; s++)
994 td[s] = Double_t(e.
total[s]);
999 Double_t x = 0.0, y = 0.0;
1001 if (cut_aa && cut_aa->IsInside(x, y))
1003 else if (cut_an && cut_an->IsInside(x, y))
1010 Double_t energy = 0.0;
1011 for (Int_t s = 0; s < 18; s++) {
1012 energy += td[s] - 1.0;
1015 Int_t trigger_strip = FindTrigger(td, base, beam_sigma);
1016 Bool_t has_trig = (trigger_strip >= 0);
1020 const Int_t kPlateauPost =
1022 Double_t plateau = 0.0;
1024 for (Int_t d = 1; d <= kPlateauPost; d++) {
1025 Int_t s = trigger_strip + d;
1026 if (s >= 0 && s < 18)
1027 plateau += td[s] - 1.0;
1031 Double_t peak3 = 0.0;
1033 for (Int_t s = trigger_strip - 1; s <= trigger_strip + 1; s++)
1034 if (s >= 0 && s < 18)
1038 Double_t trigtaildev =
1039 has_trig ? TMath::Abs(td[trigger_strip] - base[trigger_strip]) +
1040 TMath::Abs(td[17] - base[17])
1046 has_trig && (trigger_strip - 3 >= 0) && (trigger_strip + 3 <= 17);
1047 Double_t reacslope3 =
1048 ok3 ? td[trigger_strip + 3] - td[trigger_strip - 3] : 0.0;
1057 Double_t beamdev = 0.0;
1059 for (Int_t s = 8; s <= 17; s++) {
1060 Double_t d = td[s] - base[s];
1064 beamdev = TMath::Sqrt(beamdev / Double_t(n_bl));
1065 Double_t v[NV] = {energy,
1069 Double_t(trigger_strip),
1078 Bool_t vok[NV] = {kTRUE, kTRUE, kTRUE, kTRUE, kTRUE,
1079 kTRUE, has_trig, ok3, kTRUE};
1080 for (Int_t iv = 0; iv < NV; iv++)
1082 vals_raw[cls][iv].push_back(v[iv]);
1088 SavitzkyGolay(td, sgd);
1089 Double_t energy_sg = 0.0;
1090 for (Int_t s = 0; s < 18; s++)
1091 energy_sg += sgd[s] - 1.0;
1092 Double_t ex_sg[18], sm_ex_sg[18];
1093 for (Int_t s = 0; s < 18; s++)
1094 ex_sg[s] = sgd[s] - base[s];
1095 SmoothTrace(ex_sg, sm_ex_sg, kClusterSmoothWindow);
1096 Int_t reacstrip_sg = FindTrigger(sgd, base, beam_sigma);
1097 Bool_t has_trig_sg = (reacstrip_sg >= 0);
1101 Double_t plateau_sg = 0.0;
1103 for (Int_t d = 1; d <= kPlateauPost; d++) {
1104 Int_t s = reacstrip_sg + d;
1105 if (s >= 0 && s < 18)
1106 plateau_sg += sgd[s] - 1.0;
1108 Double_t peak3_sg = 0.0;
1110 for (Int_t s = reacstrip_sg - 1; s <= reacstrip_sg + 1; s++)
1111 if (s >= 0 && s < 18)
1113 Double_t trigtaildev_sg =
1114 has_trig_sg ? TMath::Abs(sgd[reacstrip_sg] - base[reacstrip_sg]) +
1115 TMath::Abs(sgd[17] - base[17])
1118 has_trig_sg && (reacstrip_sg - 3 >= 0) && (reacstrip_sg + 3 <= 17);
1119 Double_t reacslope3_sg =
1120 ok3_sg ? sgd[reacstrip_sg + 3] - sgd[reacstrip_sg - 3] : 0.0;
1121 Double_t beamdev_sg = 0.0;
1123 for (Int_t s = 8; s <= 17; s++) {
1124 Double_t d = sgd[s] - base[s];
1125 beamdev_sg += d * d;
1128 beamdev_sg = TMath::Sqrt(beamdev_sg / Double_t(n_bl_sg));
1129 Double_t v_sg[NV] = {energy_sg,
1133 Double_t(reacstrip_sg),
1138 Bool_t vok_sg[NV] = {kTRUE, kTRUE, kTRUE, kTRUE, kTRUE,
1139 kTRUE, has_trig_sg, ok3_sg, kTRUE};
1140 for (Int_t iv = 0; iv < NV; iv++)
1142 vals_sg[cls][iv].push_back(v_sg[iv]);
1145 std::cout <<
"cluster-var hists (reac " << reac
1146 <<
"): beam=" << vals_raw[0][0].size()
1147 <<
" (a,a')=" << vals_raw[1][0].size()
1148 <<
" (a,n)=" << vals_raw[2][0].size() << std::endl;
1150 std::vector<Int_t> colors = PlottingUtils::GetDefaultColors();
1152 const Int_t kNP = 2;
1153 const char *pass_label[kNP] = {
"raw",
"sg"};
1155 for (Int_t ip = 0; ip < kNP; ip++) {
1156 if (ip == 1 &&
Constants::cfg.STRIP_SUM_SCATTER_CONFIG.SKIP_SAVGOL_PLOTS)
1158 std::vector<Double_t>(*vals)[NC][NV] = (ip == 0) ? &vals_raw : &vals_sg;
1159 for (Int_t iv = 0; iv < NV; iv++) {
1160 Double_t lo = 1.0e30, hi = -1.0e30;
1161 Double_t mean[NC] = {0.0, 0.0, 0.0};
1162 for (Int_t ic = 0; ic < NC; ic++) {
1163 for (Int_t m = 0; m < Int_t((*vals)[ic][iv].size()); m++) {
1164 lo = TMath::Min(lo, (*vals)[ic][iv][m]);
1165 hi = TMath::Max(hi, (*vals)[ic][iv][m]);
1166 mean[ic] += (*vals)[ic][iv][m];
1168 if (!(*vals)[ic][iv].empty())
1169 mean[ic] /= Double_t((*vals)[ic][iv].size());
1172 std::cout <<
" [" << pass_label[ip] <<
"] " << vkey[iv]
1173 <<
": mean beam=" << mean[0] <<
" (a,a')=" << mean[1]
1174 <<
" (a,n)=" << mean[2] <<
" [range " << lo <<
".." << hi
1175 <<
"]" << std::endl;
1182 }
else if (iv == 5) {
1191 Double_t pad = 0.05 * (hi - lo);
1196 TCanvas *c = PlottingUtils::GetConfiguredCanvas(kTRUE);
1197 TString axis = Form(
";%s;Counts", vtitle[iv]);
1198 std::vector<TH1F *> hs;
1199 Double_t ymax = 0.0;
1200 for (Int_t ic = 0; ic < NC; ic++) {
1202 Form(
"h_cv_%s_%s_c%d_r%d", vkey[iv], pass_label[ip], ic, reac),
1203 axis, nbins, lo, hi);
1204 h->SetDirectory(
nullptr);
1205 for (Int_t m = 0; m < Int_t((*vals)[ic][iv].size()); m++)
1206 h->Fill((*vals)[ic][iv][m]);
1208 PlottingUtils::ConfigureHistogram(h, colors[ic % Int_t(colors.size())],
1211 ymax = TMath::Max(ymax, h->GetMaximum());
1216 TLegend *leg = PlottingUtils::AddLegend(0.775, 0.875, 0.70, 0.86);
1218 for (Int_t ic = 0; ic < NC; ic++) {
1220 hs[0]->SetMaximum(3.0 * ymax);
1221 hs[0]->SetMinimum(1.0e-1);
1222 hs[0]->Draw(
"HIST");
1224 hs[ic]->Draw(
"HIST SAME");
1226 leg->AddEntry(hs[ic], clabel[ic],
"l");
1230 TString sub_subdir = subdir +
"/clusters_" + pass_label[ip];
1232 PlottingUtils::SaveFigure(
1233 c, Form(
"cluster_var_%s_%s_reac%d", vkey[iv], pass_label[ip], reac),
1234 sub_subdir, PlotSaveOptions::kLOG);
1235 for (Int_t m = 0; m < Int_t(hs.size()); m++)
1242TString StripSumScatter::BuildFingerprint(
const std::vector<Int_t> &run_order,
1243 std::map<Int_t, TChain *> &chains) {
1244 const Int_t kReacMin =
1246 const Int_t kReacMax =
1248 const Double_t kReacJumpNSigma =
1250 const Double_t kReacJumpMax =
1252 const Int_t kSmoothHiStrip =
1254 const Double_t kSmoothMaxStep =
1256 const Double_t kEndStripMax =
1259 const Int_t kGateStripX =
1261 const Int_t kGateStripY =
1263 const Double_t kGateNSigmaX =
1265 const Double_t kGateNSigmaY =
1280 "v19 reac[%d,%d] bmult[%d,%d] jump[%.2fsig,%.3f] smooth=%d,%d "
1281 "step=%.3f s17=%.3f gate[s%d,s%d,%.2f,%.2f,%d,%.3f,%.3f] par=%.3f",
1282 kReacMin, kReacMax,
Constants::cfg.STRIP_SUM_SCATTER_CONFIG.BOTH_MULT_MAX,
1284 kReacJumpNSigma, kReacJumpMax,
1285 Int_t(
Constants::cfg.STRIP_SUM_SCATTER_CONFIG.REQUIRE_SMOOTHNESS),
1286 kSmoothHiStrip, kSmoothMaxStep, kEndStripMax, kGateStripX, kGateStripY,
1287 kGateNSigmaX, kGateNSigmaY, kGateBins, kGateMin, kGateMax,
1292 for (Int_t reac = kReacMin; reac <= kReacMax; reac++)
1293 s += Form(
"%s%.4f", reac == kReacMin ?
"" :
",",
JumpMin(reac));
1298 s += Form(
" up=%d,%.2fsig[",
1300 .REQUIRE_BEAM_UPSTREAM_OF_REAC),
1302 for (Int_t strip = 1; strip < kReacMax; strip++)
1303 s += Form(
"%s%.4f", strip == 1 ?
"" :
",",
1309 std::vector<GateSpec> gates = ActiveGates();
1310 for (Int_t i = 0; i < Int_t(gates.size()); i++)
1311 s += Form(
" g[s%d,s%d]", gates[i].sx, gates[i].sy);
1315 for (Int_t i = 0; i < Int_t(run_order.size()); i++) {
1316 Int_t run = run_order[i];
1317 s += Form(
" r%d:%lld", run, chains[run]->GetEntries());
1323 Form(
"buildx[%.3f,%.3f] buildy[%.3f,%.3f] bins[%d,%d] x[%d,%d]",
1329 for (Int_t reac = kReacMin; reac <= kReacMax; reac++)
1331 Form(
"%s%d-%d", reac == kReacMin ?
"[" :
",",
YLoOf(reac),
YHiOf(reac));
1333 return s +
" | " + plane;
1338static TString TagPart(
const TString &fingerprint) {
1339 const Ssiz_t bar = fingerprint.Index(
" | ");
1340 return bar < 0 ? TString(
"") : TString(fingerprint(0, bar));
1347void StripSumScatter::YBounds(Double_t *y_lo, Double_t *y_hi) {
1348 const Int_t kReacMin =
1350 const Int_t kReacMax =
1352 for (Int_t reac = kReacMin; reac <= kReacMax; reac++) {
1353 Int_t ri = reac - kReacMin;
1354 std::map<Int_t, std::pair<Double_t, Double_t>>::const_iterator it =
1356 if (it !=
Constants::cfg.STRIP_SUM_SCATTER_CONFIG.Y_DISPLAY_RANGE.end()) {
1357 y_lo[ri] = it->second.first;
1358 y_hi[ri] = it->second.second;
1366TString StripSumScatter::PrettyLabel(
const TString &tag) {
1368 base.ReplaceAll(
"_eres",
"");
1370 return "(#alpha,#alpha')";
1372 return "(#alpha,n)";
1386Bool_t StripSumScatter::SimBeamGains(Double_t *gain) {
1387 const Long64_t kSampleMaxPoints =
1390 for (Int_t s = 0; s < 18; s++)
1398 for (Int_t i = 0; i < Int_t(specs.size()); i++) {
1400 std::cout <<
"No eres sim file for " << specs[i].tag <<
", using standard"
1404 base.ReplaceAll(
"_eres",
"");
1405 if (base ==
"beam") {
1410 if (file.Length() == 0)
1413 TFile *f = IO::OpenForReading(file);
1414 if (!f || f->IsZombie()) {
1415 std::cerr <<
"strip-sum-scatter: cannot open sim beam file " << file
1416 <<
"; sim overlay stays in raw sim units." << std::endl;
1421 TTree *t =
static_cast<TTree *
>(f->Get(
"events_MeV"));
1423 std::cerr <<
"strip-sum-scatter: no events_MeV tree in sim beam file "
1424 << file <<
"; sim overlay stays in raw sim units." << std::endl;
1429 Float_t left[18] = {0}, right[18] = {0};
1430 t->SetBranchAddress(
"Left_0_17_dE", left);
1431 t->SetBranchAddress(
"RightdE", right);
1432 Long64_t n = t->GetEntries();
1434 Double_t sum[18] = {0};
1435 Long64_t cnt[18] = {0};
1439 for (Int_t s = 0; s < 18; s++)
1441 for (Long64_t j = 0; j < n; j += stride) {
1444 SimTotal(left, right, unit, total);
1445 for (Int_t s = 0; s < 18; s++)
1446 if (total[s] > 0.0) {
1454 for (Int_t s = 0; s < 18; s++) {
1455 if (cnt[s] > 0 && sum[s] > 0.0) {
1456 gain[s] = 1.0 / (sum[s] / Double_t(cnt[s]));
1462 std::cout <<
"strip-sum-scatter: sim per-strip beam normalization to 1 a.u. ("
1463 << n_set <<
" strips)." << std::endl;
1467void StripSumScatter::SimTotal(
const Float_t *left,
const Float_t *right,
1468 const Double_t *gain, Double_t *total) {
1469 for (Int_t s = 0; s < 18; s++)
1470 total[s] = gain[s] * (Double_t(left[s]) + Double_t(right[s]));
1472 for (Int_t s = 1; s <= 16; s++)
1474 gain[s] * ((s % 2) != 0 ? Double_t(left[s]) : Double_t(right[s]));
1477TGraph *StripSumScatter::SimPopScatter(
const TString &file, Int_t reac,
1478 const Double_t *gain,
1479 Long64_t max_points) {
1483 TFile *f = IO::OpenForReading(file);
1484 if (!f || f->IsZombie()) {
1489 TTree *t =
static_cast<TTree *
>(f->Get(
"events_MeV"));
1491 std::cerr <<
" no events_MeV tree in " << file << std::endl;
1496 Int_t y_lo = reac + 1;
1497 Int_t y_hi = TMath::Min(reac + 6, 17);
1498 Float_t left[18] = {0}, right[18] = {0};
1499 t->SetBranchAddress(
"Left_0_17_dE", left);
1500 t->SetBranchAddress(
"RightdE", right);
1501 Long64_t n = t->GetEntries();
1503 TGraph *g =
new TGraph();
1505 for (Long64_t j = 0; j < n; j += stride) {
1508 SimTotal(left, right, gain, total);
1509 Double_t x = SumRange(total, kXLo, kXHi);
1510 Double_t y = SumRange(total, y_lo, y_hi);
1512 g->SetPoint(k++, x, y);
1524std::vector<TGraph *> StripSumScatter::SimPopTraces(
const TString &file,
1525 const Double_t *gain,
1526 Long64_t max_traces) {
1527 std::vector<TGraph *> traces;
1528 TFile *f = IO::OpenForReading(file);
1529 if (!f || f->IsZombie()) {
1534 TTree *t =
static_cast<TTree *
>(f->Get(
"events_MeV"));
1540 Float_t left[18] = {0}, right[18] = {0};
1541 t->SetBranchAddress(
"Left_0_17_dE", left);
1542 t->SetBranchAddress(
"RightdE", right);
1543 Long64_t n = t->GetEntries();
1545 for (Long64_t j = 0; j < n && Int_t(traces.size()) < max_traces;
1549 SimTotal(left, right, gain, total);
1561void StripSumScatter::SimTraceOverlay() {
1562 const Int_t kReacMin =
1564 const Int_t kReacMax =
1566 const Int_t kTracesPerRegion =
1574 std::map<Int_t, TString> aa_file, an_file;
1575 std::map<Int_t, Bool_t> aa_eres, an_eres;
1576 std::vector<TString> beam_files;
1577 Bool_t beam_eres = kFALSE;
1578 for (Int_t i = 0; i < Int_t(specs.size()); i++) {
1580 base.ReplaceAll(
"_eres",
"");
1584 if (base ==
"beam") {
1585 if (beam_eres && !eres)
1587 if (eres && !beam_eres)
1589 beam_files.push_back(file);
1590 beam_eres = beam_eres || eres;
1591 }
else if (strip >= kReacMin && strip <= kReacMax) {
1592 std::map<Int_t, TString> &files = base ==
"aa" ? aa_file : an_file;
1593 std::map<Int_t, Bool_t> &have_eres = base ==
"aa" ? aa_eres : an_eres;
1594 if (base !=
"aa" && base !=
"an")
1596 if (files.count(strip) && (have_eres[strip] || !eres))
1598 files[strip] = file;
1599 have_eres[strip] = eres;
1604 if (!SimBeamGains(gain))
1605 for (Int_t s = 0; s < 18; s++)
1608 std::vector<TGraph *> beam_traces;
1609 for (Int_t i = 0; i < Int_t(beam_files.size()) &&
1610 Int_t(beam_traces.size()) < kTracesPerRegion;
1612 std::vector<TGraph *> t = SimPopTraces(
1613 beam_files[i], gain, kTracesPerRegion - Int_t(beam_traces.size()));
1614 for (Int_t k = 0; k < Int_t(t.size()); k++)
1615 beam_traces.push_back(t[k]);
1618 for (Int_t r = kReacMin; r <= kReacMax; r++) {
1619 std::vector<TGraph *> aa_traces, an_traces;
1620 if (aa_file.find(r) != aa_file.end())
1621 aa_traces = SimPopTraces(aa_file[r], gain, kTracesPerRegion);
1622 if (an_file.find(r) != an_file.end())
1623 an_traces = SimPopTraces(an_file[r], gain, kTracesPerRegion);
1624 if (aa_traces.empty() && an_traces.empty())
1626 DrawRegionTraces(Form(
"sim_region_traces_reac%d", r),
"sim_scatter",
1627 beam_traces, aa_traces, an_traces, 0.6, 1.6,
1629 for (Int_t i = 0; i < Int_t(aa_traces.size()); i++)
1630 delete aa_traces[i];
1631 for (Int_t i = 0; i < Int_t(an_traces.size()); i++)
1632 delete an_traces[i];
1634 for (Int_t i = 0; i < Int_t(beam_traces.size()); i++)
1635 delete beam_traces[i];
1641TString StripSumScatter::SimFingerprint(
1642 const std::vector<RemixSim::SimFileSpec> &specs) {
1648 const Int_t kReacMin =
1650 const Int_t kReacMax =
1657 TString s = Form(
"v4 reac[%d,%d] x[%d,%d]", kReacMin, kReacMax, kXLo, kXHi);
1658 for (Int_t i = 0; i < Int_t(specs.size()); i++) {
1660 Long_t
id = 0, flags = 0, mtime = 0;
1662 if (gSystem->GetPathInfo(f, &
id, &size, &flags, &mtime) != 0) {
1666 s += Form(
" %s:%lld:%ld", specs[i].tag.Data(), size, mtime);
1674Bool_t StripSumScatter::LoadSimCache(
1675 const TString &fp, std::map<Int_t, std::vector<TGraph *>> &by_strip) {
1676 TString full = IO::GetRootFilesBaseDir() + TString(
"/") +
1677 "StripSumScatter_simcache.root";
1678 if (gSystem->AccessPathName(full))
1680 TFile *f = IO::OpenForReading(
"StripSumScatter_simcache.root");
1681 if (!f || f->IsZombie()) {
1686 TNamed *cfp =
static_cast<TNamed *
>(f->Get(
"sim_fingerprint"));
1687 if (!cfp || fp != cfp->GetTitle()) {
1692 TIter next(f->GetListOfKeys());
1694 while ((key =
static_cast<TKey *
>(next()))) {
1695 TString name = key->GetName();
1696 if (!name.BeginsWith(
"simg_r"))
1698 TString rest = name(6, name.Length() - 6);
1699 Int_t us = rest.Index(
"_p");
1702 Int_t r = TString(rest(0, us)).Atoi();
1703 TGraph *g =
static_cast<TGraph *
>(f->Get(name));
1706 by_strip[r].push_back(
static_cast<TGraph *
>(g->Clone()));
1713void StripSumScatter::WriteSimCache(
1714 const TString &fp,
const std::map<Int_t, std::vector<TGraph *>> &by_strip) {
1715 TFile *out = IO::OpenForWriting(
"StripSumScatter_simcache.root",
"RECREATE");
1716 if (!out || out->IsZombie()) {
1722 TNamed cfp(
"sim_fingerprint", fp.Data());
1724 std::map<Int_t, std::vector<TGraph *>>::const_iterator it;
1725 for (it = by_strip.begin(); it != by_strip.end(); ++it)
1726 for (Int_t i = 0; i < Int_t(it->second.size()); i++)
1727 it->second[i]->Write(Form(
"simg_r%d_p%d", it->first, Int_t(i)));
1738void StripSumScatter::SimOverlay() {
1739 const Int_t kReacMin =
1741 const Int_t kReacMax =
1747 if (specs.empty()) {
1749 <<
"strip-sum-scatter: no sim control files; skipping sim overlay."
1753 TString fp = SimFingerprint(specs);
1755 std::map<Int_t, std::vector<TGraph *>>
1757 Bool_t loaded = LoadSimCache(fp, by_strip);
1763 std::map<std::pair<TString, Int_t>, std::pair<SimPop, Bool_t>> chosen;
1764 for (Int_t i = 0; i < Int_t(specs.size()); i++) {
1766 base.ReplaceAll(
"_eres",
"");
1769 std::pair<TString, Int_t> key(base, strip);
1770 if (chosen.count(key) && (chosen[key].second || !eres))
1774 p.
label = PrettyLabel(specs[i].tag);
1775 chosen[key] = std::make_pair(p, eres);
1777 std::map<Int_t, std::vector<SimPop>> reacted;
1778 std::vector<SimPop> refs;
1779 for (std::map<std::pair<TString, Int_t>,
1780 std::pair<SimPop, Bool_t>>::const_iterator it =
1782 it != chosen.end(); ++it) {
1783 if (!it->second.second)
1784 std::cout <<
"No eres sim file for " << it->first.first
1785 << (it->first.second >= 0 ? Form(
"_s%d", it->first.second)
1787 <<
", using standard" << std::endl;
1788 if (it->first.second < 0)
1789 refs.push_back(it->second.first);
1791 reacted[it->first.second].push_back(it->second.first);
1794 if (!SimBeamGains(gain))
1795 for (Int_t s = 0; s < 18; s++)
1797 const Long64_t kSimMaxPoints = 25000;
1798 for (Int_t r = kReacMin; r <= kReacMax; r++) {
1799 std::vector<SimPop> group = reacted[r];
1800 for (Int_t i = 0; i < Int_t(refs.size()); i++)
1801 group.push_back(refs[i]);
1802 for (Int_t i = 0; i < Int_t(group.size()); i++) {
1803 TGraph *g = SimPopScatter(group[i].file, r, gain, kSimMaxPoints);
1804 if (!g || g->GetN() == 0) {
1809 g->SetTitle(group[i].label);
1810 by_strip[r].push_back(g);
1814 std::map<Int_t, std::vector<TGraph *>>::const_iterator cit;
1815 for (cit = by_strip.begin(); cit != by_strip.end(); ++cit)
1816 n_graphs += Int_t(cit->second.size());
1817 if (n_graphs == 0) {
1818 std::cerr <<
"strip-sum-scatter: no sim data found (regenerate "
1819 "sim_root_files); skipping sim overlay."
1823 WriteSimCache(fp, by_strip);
1824 std::cout <<
"strip-sum-scatter: built + cached sim overlay (" << n_graphs
1825 <<
" population graphs)." << std::endl;
1828 <<
"strip-sum-scatter: loaded cached sim overlay (fingerprint match)."
1832 std::map<Int_t, std::vector<TGraph *>>::iterator it;
1833 for (it = by_strip.begin(); it != by_strip.end(); ++it) {
1834 Int_t r = it->first;
1835 std::map<Int_t, TH2F *>::const_iterator sit = m_scatter.find(r);
1836 if (sit == m_scatter.end())
1839 TH2F *ref = sit->second;
1841 new TH2F(Form(
"sim_frame_r%d", r),
"", 10, ref->GetXaxis()->GetXmin(),
1842 ref->GetXaxis()->GetXmax(), 10, ref->GetYaxis()->GetXmin(),
1843 ref->GetYaxis()->GetXmax());
1845 frame->GetXaxis()->SetTitle(ref->GetXaxis()->GetTitle());
1846 frame->GetYaxis()->SetTitle(ref->GetYaxis()->GetTitle());
1847 TCanvas *c = PlottingUtils::GetConfiguredCanvas(kFALSE);
1848 c->SetLeftMargin(0.18);
1851 TLegend *leg = PlottingUtils::AddLegend(0.725, 0.875, 0.70, 0.86);
1852 for (Int_t i = 0; i < Int_t(it->second.size()); i++) {
1853 TGraph *g = it->second[i];
1856 TString lab = g->GetTitle();
1857 Int_t color = kBlack;
1860 else if (lab ==
"(#alpha,#alpha')")
1862 else if (lab ==
"(#alpha,n)")
1864 g->SetMarkerStyle(20);
1865 g->SetMarkerSize(0.3);
1866 g->SetMarkerColorAlpha(color, 0.35);
1867 g->SetLineColor(color);
1869 leg->AddEntry(g, g->GetTitle(),
"p");
1872 PlottingUtils::SaveFigure(c,
1873 Form(
"sim_normsumE_reac%d_s%d_%d_vs_s%d_%d", r,
1875 "sim_scatter", PlotSaveOptions::kLINEAR);
1881 std::map<Int_t, std::vector<TGraph *>>::iterator dit;
1882 for (dit = by_strip.begin(); dit != by_strip.end(); ++dit)
1883 for (Int_t i = 0; i < Int_t(dit->second.size()); i++)
1884 delete dit->second[i];
1887Bool_t StripSumScatter::TryLoadCache(
const TString &cacheName,
1888 const TString &fingerprint) {
1889 const Int_t kReacMin =
1891 const Int_t kReacMax =
1894 TString cache_full = IO::GetRootFilesBaseDir() + TString(
"/") + cacheName;
1895 if (gSystem->AccessPathName(cache_full)) {
1896 std::cout <<
"strip-sum-scatter: no cache file found; will rebuild."
1901 TFile *cf = IO::OpenForReading(cacheName);
1902 if (!cf || cf->IsZombie()) {
1905 std::cout <<
"strip-sum-scatter: cache file unreadable; rebuilding."
1910 TNamed *fp =
static_cast<TNamed *
>(cf->Get(
"fingerprint"));
1911 const Bool_t exact = fp && fingerprint == fp->GetTitle();
1914 const Bool_t reproject = !exact && fp && !TagPart(fingerprint).IsNull() &&
1915 TagPart(fingerprint) == TagPart(fp->GetTitle());
1916 if (!exact && !reproject) {
1917 std::cout <<
"strip-sum-scatter: cache present but stale; rebuilding."
1919 std::cout <<
" cached: " << (fp ? fp->GetTitle() :
"(none)") << std::endl;
1920 std::cout <<
" wanted: " << fingerprint << std::endl;
1927 for (Int_t reac = kReacMin; reac <= kReacMax && ok && exact; reac++) {
1928 TH2F *h =
static_cast<TH2F *
>(cf->Get(Form(
"scatter_r%d", reac)));
1933 TH2F *hc =
static_cast<TH2F *
>(h->Clone());
1934 hc->SetDirectory(
nullptr);
1935 m_scatter[reac] = hc;
1939 if (TParameter<Long64_t> *p =
1940 dynamic_cast<TParameter<Long64_t> *
>(cf->Get(
"n_seen")))
1941 m_nSeen = p->GetVal();
1942 if (TParameter<Long64_t> *p =
1943 dynamic_cast<TParameter<Long64_t> *
>(cf->Get(
"n_normed")))
1944 m_nNormed = p->GetVal();
1945 for (Int_t reac = kReacMin; reac <= kReacMax; reac++) {
1946 if (TParameter<Long64_t> *p =
dynamic_cast<TParameter<Long64_t> *
>(
1947 cf->Get(Form(
"n_normed_r%d", reac))))
1948 m_normedAt[ReacIndex(reac)] = p->GetVal();
1949 if (TParameter<Long64_t> *p =
dynamic_cast<TParameter<Long64_t> *
>(
1950 cf->Get(Form(
"n_tagged_r%d", reac))))
1951 m_tagged[ReacIndex(reac)] = p->GetVal();
1954 TTree *tt =
static_cast<TTree *
>(cf->Get(
"traces"));
1955 if (reproject && !tt)
1959 tt->SetBranchAddress(
"total", e.
total);
1960 tt->SetBranchAddress(
"total_adc", e.
total_adc);
1961 tt->SetBranchAddress(
"reac_mask", &e.
reac_mask);
1962 tt->SetBranchAddress(
"beam_flat", &e.
beam_flat);
1963 tt->SetBranchAddress(
"both_mult", &e.
both_mult);
1965 if (tt->GetBranch(
"seed_ts"))
1966 tt->SetBranchAddress(
"seed_ts", &e.
seed_ts);
1967 Long64_t nt = tt->GetEntries();
1968 m_reservoir.reserve(nt);
1969 for (Long64_t j = 0; j < nt; j++) {
1971 m_reservoir.push_back(e);
1979 std::cout <<
"strip-sum-scatter: cache partially corrupt; rebuilding."
1981 m_reservoir.clear();
1985 std::cout <<
"strip-sum-scatter: cache tagging matches but the plane "
1986 "changed; re-projecting "
1987 << m_reservoir.size() <<
" reservoir events." << std::endl;
1989 ReprojectFromReservoir();
1990 WriteCache(cacheName, fingerprint);
1992 std::cout <<
"strip-sum-scatter: loaded cached scatters + "
1993 << m_reservoir.size() <<
" reservoir events (fingerprint match)."
1999void StripSumScatter::WriteCache(
const TString &cacheName,
2000 const TString &fingerprint) {
2001 const Int_t kReacMin =
2003 const Int_t kReacMax =
2006 TFile *out = IO::OpenForWriting(cacheName,
"RECREATE");
2007 if (!out || out->IsZombie()) {
2013 TNamed fp(
"fingerprint", fingerprint.Data());
2017 TParameter<Long64_t>(
"n_seen", m_nSeen).Write();
2018 TParameter<Long64_t>(
"n_normed", m_nNormed).Write();
2020 for (Int_t s = 1; s < 18; s++)
2021 TParameter<Double_t>(Form(
"jump_sigma_s%d", s), s_jumpSigma[s]).Write();
2022 for (Int_t s = 0; s < 18; s++)
2023 TParameter<Double_t>(Form(
"strip_sigma_s%d", s), s_stripSigma[s]).Write();
2024 for (Int_t reac = kReacMin; reac <= kReacMax; reac++)
2025 TParameter<Long64_t>(Form(
"n_normed_r%d", reac),
2026 m_normedAt[ReacIndex(reac)])
2028 for (Int_t reac = kReacMin; reac <= kReacMax; reac++)
2029 TParameter<Long64_t>(Form(
"n_tagged_r%d", reac), m_tagged[ReacIndex(reac)])
2031 for (Int_t reac = kReacMin; reac <= kReacMax; reac++)
2032 m_scatter[reac]->
Write(Form(
"scatter_r%d", reac));
2034 TTree *tt =
new TTree(
"traces",
"strip-sum trace reservoir");
2036 tt->Branch(
"total", e.
total,
"total[18]/F");
2037 tt->Branch(
"total_adc", e.
total_adc,
"total_adc[18]/F");
2038 tt->Branch(
"reac_mask", &e.
reac_mask,
"reac_mask/i");
2039 tt->Branch(
"beam_flat", &e.
beam_flat,
"beam_flat/O");
2040 tt->Branch(
"both_mult", &e.
both_mult,
"both_mult/I");
2041 tt->Branch(
"seed_ts", &e.
seed_ts,
"seed_ts/l");
2042 for (Int_t k = 0; k < Int_t(m_reservoir.size()); k++) {
2049 std::cout <<
"strip-sum-scatter: wrote cache " << cacheName << std::endl;
2056StripSumScatter::FitRunGates(Int_t run, TChain *chain,
2057 const std::vector<GateSpec> &activeGates) {
2058 SingleRunFitResult res;
2059 if (!chain || chain->GetEntries() == 0)
2066 std::vector<GateSpec> emptyPrior;
2067 std::vector<BeamFit2D> emptyGates;
2068 const TString tag = Form(
"run%d", run);
2069 const TString subdir = Form(
"strip_sum_scatter/run%d", run);
2070 Int_t ent_sx = 0, ent_sy = 1;
2071 const Char_t *ent_tag =
"s0/s1";
2078 BeamFit2D ent_ell = FindBeamGate(chain, ent_sx, ent_sy, emptyPrior,
2079 emptyGates, tag, subdir);
2086 std::cout <<
" run " << run <<
" beam ellipse " << ent_tag <<
": mu=("
2087 << ent_ell.
mu_x <<
"," << ent_ell.
mu_y <<
")" << std::endl;
2090 std::cerr <<
" run " << run <<
" beam ellipse " << ent_tag
2091 <<
" failed; skipping run" << std::endl;
2097 FindBeamGate(chain, 15, 16, emptyPrior, emptyGates, tag, subdir);
2100 std::cout <<
" run " << run <<
" beam ellipse s15/s16: mu=("
2105 std::cerr <<
" run " << run
2106 <<
" beam ellipse s15/s16 failed; skipping run" << std::endl;
2112 FindBeamGate(chain, 16, 17, emptyPrior, emptyGates, tag, subdir);
2115 std::cout <<
" run " << run <<
" beam ellipse s16/s17: mu=("
2120 std::cerr <<
" run " << run
2121 <<
" beam ellipse s16/s17 failed; skipping run" << std::endl;
2130 std::vector<BeamFit2D> runGates;
2131 std::vector<GateSpec> priorSpecs;
2132 Bool_t allOk = kTRUE;
2133 for (Int_t gi = 0; gi < Int_t(activeGates.size()); gi++) {
2134 BeamFit2D g = FindBeamGate(chain, activeGates[gi].sx, activeGates[gi].sy,
2135 priorSpecs, runGates, Form(
"run%d", run),
2136 Form(
"strip_sum_scatter/run%d", run));
2139 std::cout <<
" run " << run <<
" beam gate s" << activeGates[gi].sx
2140 <<
"/s" << activeGates[gi].sy <<
": mu=(" << g.
mu_x <<
","
2141 << g.
mu_y <<
")" << std::endl;
2144 std::cerr <<
" run " << run <<
" beam gate s" << activeGates[gi].sx
2145 <<
"/s" << activeGates[gi].sy <<
" failed; skipping run"
2149 runGates.push_back(g);
2150 priorSpecs.push_back(activeGates[gi]);
2161 Int_t run, TChain *chain,
const std::vector<GateSpec> &activeGates,
2162 const std::vector<BeamFit2D> &runGates,
const BeamEllipses &runBeam) {
2163 const Int_t kReacMin =
2165 const Int_t kReacMax =
2171 const Int_t kBeamReservoirCap =
2173 const Int_t nReacStrips = kReacMax - kReacMin + 1;
2175 SingleRunFillResult res;
2176 res.
scatters.assign(nReacStrips,
nullptr);
2181 for (Int_t reac = kReacMin; reac <= kReacMax; reac++) {
2183 new TH2F(Form(
"scatter_r%d_run%d", reac, run),
"", kXBins,
2186 h->SetDirectory(
nullptr);
2189 Long64_t totalGated = 0, totalSeen = 0, totalNormed = 0;
2190 res.
tagged.assign(nReacStrips, 0);
2192 Int_t nBeamKept = 0;
2195 EnableEventBranches(chain);
2196 ULong64_t seed_ts_in = 0;
2197 if (chain->GetBranch(
"SeedTs"))
2198 chain->SetBranchAddress(
"SeedTs", &seed_ts_in);
2199 Long64_t n = chain->GetEntries();
2200 Int_t nReac = kReacMax - kReacMin + 1;
2203 std::cout <<
"Run " << run <<
": filling " << nReac
2204 <<
" reaction-strip scatters over " << n <<
" events..."
2208 for (Long64_t j = 0; j < n; j++) {
2213 Bool_t passesAll = kTRUE;
2214 for (Int_t gi = 0; gi < Int_t(activeGates.size()); gi++)
2215 if (!PassesGate(runGates[gi], ev, activeGates[gi].sx,
2216 activeGates[gi].sy)) {
2226 if (
Constants::cfg.STRIP_SUM_SCATTER_CONFIG.REJECT_OFFBEAM && IsOffbeam(ev))
2228 if (IsParityAsymmetric(ev))
2232 if (
Constants::cfg.STRIP_SUM_SCATTER_CONFIG.BOTH_MULT_MAX >= 0) {
2233 const Int_t hi = TMath::Min(
2236 for (Int_t s = 1; s <= hi; s++)
2239 if (nboth >
Constants::cfg.STRIP_SUM_SCATTER_CONFIG.BOTH_MULT_MAX)
2251 if (AllStripsFired(ev)) {
2252 for (Int_t reac = kReacMin; reac <= kReacMax; reac++)
2253 if (BeamUpstreamOf(ev, reac))
2258 for (Int_t reac = kReacMin; reac <= kReacMax; reac++) {
2261 mask |= (1u << ReacIndex(reac));
2262 res.
tagged[ReacIndex(reac)]++;
2263 Double_t x = 0.0, y = 0.0;
2265 res.
scatters[ReacIndex(reac)]->Fill(x, y);
2273 Bool_t beam = (mask == 0) && IsPureBeam(ev, runBeam);
2274 if (mask == 0 && !(beam && nBeamKept < kBeamReservoirCap))
2280 for (Int_t s = 0; s < 18; s++) {
2287 for (Int_t s = 1; s <= 16; s++) {
2292 Bool_t l_is_long = ((s % 2) != 0);
2293 e.
long_au[s] = Float_t(l_is_long ? lv : rv);
2294 e.
short_au[s] = Float_t(l_is_long ? rv : lv);
2300 for (Int_t s = 1; s <= 16; s++)
2302 : Float_t(ev.rightdE_adc[s]);
2309 for (Int_t s = 1; s <= 16; s++)
2320 res.
gated = totalGated;
2321 res.
seen = totalSeen;
2322 res.
normed = totalNormed;
2327static void RunIndexedParallel(Int_t n, Int_t workers,
2328 const std::function<
void(Int_t)> &task) {
2329 std::queue<Int_t> work;
2330 for (Int_t i = 0; i < n; i++)
2332 std::mutex work_mutex;
2333 std::vector<std::thread> pool;
2334 for (Int_t w = 0; w < workers; w++) {
2335 pool.emplace_back([&]() {
2339 std::lock_guard<std::mutex> lk(work_mutex);
2349 for (Int_t w = 0; w < Int_t(pool.size()); w++)
2353void StripSumScatter::AllocateScatters() {
2354 const Int_t kReacMin =
2356 const Int_t kReacMax =
2362 for (Int_t reac = kReacMin; reac <= kReacMax; reac++) {
2364 new TH2F(Form(
"scatter_r%d", reac),
2365 Form(
";norm. #DeltaE strips %d#rightarrow%d [a.u.];norm. "
2366 "#DeltaE strips %d#rightarrow%d [a.u.]",
2370 h->SetDirectory(
nullptr);
2372 m_scatter[reac] = h;
2376void StripSumScatter::ReprojectFromReservoir() {
2377 const Int_t kReacMin =
2379 const Int_t kReacMax =
2381 Long64_t nFilled = 0;
2382 for (Int_t k = 0; k < Int_t(m_reservoir.size()); k++) {
2383 const TraceEvt &e = m_reservoir[k];
2387 for (Int_t s = 0; s < 18; s++)
2388 total[s] = e.
total[s];
2389 for (Int_t reac = kReacMin; reac <= kReacMax; reac++) {
2390 if (!(e.
reac_mask & (1u << ReacIndex(reac))))
2392 Double_t x = 0.0, y = 0.0;
2394 m_scatter[reac]->Fill(x, y);
2398 std::cout <<
"strip-sum-scatter: re-projected " << nFilled
2399 <<
" tagged entries." << std::endl;
2402void StripSumScatter::FillScatters(
const std::vector<Int_t> &runOrder,
2403 std::map<Int_t, TChain *> &chains) {
2404 const Int_t kReacMin =
2406 const Int_t kReacMax =
2413 std::vector<GateSpec> activeGates = ActiveGates();
2416 const Int_t nRuns = Int_t(runOrder.size());
2417 std::vector<TChain *> chainVec(nRuns);
2418 for (Int_t i = 0; i < nRuns; i++)
2419 chainVec[i] = chains[runOrder[i]];
2422 TMath::Min(Int_t(std::thread::hardware_concurrency()), nRuns);
2423 n_workers = TMath::Min(
2424 n_workers,
Constants::cfg.STRIP_SUM_SCATTER_CONFIG.MAX_STRIP_SUM_WORKERS);
2427 std::cout <<
"strip-sum-scatter: " << nRuns <<
" runs on " << n_workers
2428 <<
" workers" << std::endl;
2432 std::vector<SingleRunFitResult> fits(nRuns);
2433 RunIndexedParallel(nRuns, n_workers, [&](Int_t i) {
2434 fits[i] = FitRunGates(runOrder[i], chainVec[i], activeGates);
2447 std::vector<FillTask> tasks;
2449 std::map<Int_t, Int_t> idx_of_run;
2450 for (Int_t i = 0; i < nRuns; i++)
2451 idx_of_run[runOrder[i]] = i;
2453 for (Int_t k = 0; k < Int_t(specs.size()); k++) {
2454 std::map<Int_t, Int_t>::const_iterator it = idx_of_run.find(specs[k].run);
2455 if (it == idx_of_run.end() || !fits[it->second].ok)
2457 TString full = IO::GetRootFilesBaseDir() +
"/" +
2459 if (gSystem->AccessPathName(full))
2462 t.run_idx = it->second;
2467 Int_t nTasks = Int_t(tasks.size());
2468 Int_t fill_workers =
2469 TMath::Min(Int_t(std::thread::hardware_concurrency()), nTasks);
2471 TMath::Min(fill_workers,
2473 if (fill_workers < 1)
2475 std::cout <<
"strip-sum-scatter: filling " << nTasks <<
" files on "
2476 << fill_workers <<
" workers" << std::endl;
2487 std::vector<SingleRunFillResult> fills(nTasks);
2488 std::vector<Bool_t> filled(nTasks, kFALSE);
2489 Int_t next_merge = 0;
2490 std::mutex merge_mutex;
2491 Long64_t totalGated = 0, totalSeen = 0;
2492 const Int_t kBeamReservoirCap =
2494 Int_t nBeamKept = 0;
2495 auto merge = [&](Int_t t) {
2496 for (Int_t reac = kReacMin; reac <= kReacMax; reac++) {
2497 Int_t ri = ReacIndex(reac);
2498 if (ri < Int_t(fills[t].scatters.size()) && fills[t].scatters[ri])
2499 m_scatter[reac]->Add(fills[t].scatters[ri]);
2505 for (Int_t k = 0; k < Int_t(fills[t].reservoir.size()); k++) {
2506 const TraceEvt &e = fills[t].reservoir[k];
2508 if (nBeamKept >= kBeamReservoirCap)
2512 m_reservoir.push_back(e);
2514 totalGated += fills[t].gated;
2515 totalSeen += fills[t].seen;
2516 m_nNormed += fills[t].normed;
2517 for (Int_t k = 0; k < Int_t(fills[t].normed_at.size()); k++)
2518 m_normedAt[k] += fills[t].normed_at[k];
2519 for (Int_t k = 0; k < Int_t(fills[t].tagged.size()); k++)
2520 m_tagged[k] += fills[t].tagged[k];
2521 for (Int_t k = 0; k < Int_t(fills[t].scatters.size()); k++)
2522 delete fills[t].scatters[k];
2523 fills[t].scatters.clear();
2524 std::vector<TraceEvt>().swap(fills[t].reservoir);
2526 RunIndexedParallel(nTasks, fill_workers, [&](Int_t t) {
2527 Int_t i = tasks[t].run_idx;
2529 TChain ch(
"events");
2530 ch.Add(tasks[t].path);
2531 if (ch.GetEntries() > 0)
2532 fills[t] = FillRunScatters(runOrder[i], &ch, activeGates,
2533 fits[i].series_gates, fits[i].pure_beam);
2535 std::lock_guard<std::mutex> lk(merge_mutex);
2537 while (next_merge < nTasks && filled[next_merge]) {
2542 m_nSeen = totalSeen;
2543 std::cout <<
"strip-sum-scatter: " << totalGated <<
" reaction-tagged of "
2544 << totalSeen <<
" events (" << m_nNormed
2545 <<
" past every pre-tag cut); reservoir " << m_reservoir.size()
2549void StripSumScatter::PlotScatters() {
2553 YBounds(m_yLo, m_yHi);
2556 const Int_t kReacMin =
2558 const Int_t kReacMax =
2562 for (Int_t reac = kReacMin; reac <= kReacMax; reac++) {
2563 TCanvas *c = PlottingUtils::GetConfiguredCanvas(kFALSE);
2566 Int_t ri = ReacIndex(reac);
2567 m_scatter[reac]->GetXaxis()->SetRangeUser(
2570 m_scatter[reac]->GetYaxis()->SetRangeUser(m_yLo[ri], m_yHi[ri]);
2571 PlottingUtils::ConfigureAndDraw2DHistogram(m_scatter[reac], c);
2572 m_scatter[reac]->GetYaxis()->SetTitleOffset(1.3);
2573 c->SetLeftMargin(0.18);
2574 PlottingUtils::SaveFigure(c,
2575 Form(
"normsumE_reac%d_s%d_%d_vs_s%d_%d", reac,
2577 "strip_sum_scatter", PlotSaveOptions::kLINEAR);
2588void StripSumScatter::PlotParityRejectedGrid(
2589 const std::vector<Int_t> &run_order, std::map<Int_t, TChain *> &chains) {
2594 std::cout <<
"strip-sum-scatter: PLOT_PARITY_REJECTED_GRID is set but "
2595 "PARITY_ASYM_MAX <= 0; no parity cut to reject events, "
2596 "skipping the grid diagnostic."
2605 TH1F *h_au =
new TH1F(
"grid_parity_rejected_au",
2606 ";Grid #DeltaE [a.u.];Counts", 400, 0.0, 1.0);
2608 new TH1F(
"grid_parity_rejected_adc",
";Grid #DeltaE [ADC];Counts", 400,
2609 0.0, grid_max_adc > 0.0 ? grid_max_adc : 16384.0);
2610 h_au->SetDirectory(
nullptr);
2611 h_adc->SetDirectory(
nullptr);
2613 Long64_t nRejected = 0;
2614 for (Int_t i = 0; i < Int_t(run_order.size()); i++) {
2615 TChain *chain = chains[run_order[i]];
2620 EnableEventBranches(chain);
2621 const Long64_t n = chain->GetEntries();
2622 for (Long64_t j = 0; j < n; j++) {
2625 if (!AllStripsFired(ev) || IsPileup(ev) || IsNoise(ev))
2629 if (IsParityAsymmetric(ev)) {
2630 h_au->Fill(ev.
grid);
2635 chain->ResetBranchAddresses();
2638 std::cout <<
"strip-sum-scatter: parity-rejected grid diagnostic: "
2639 << nRejected <<
" events rejected by the parity cut." << std::endl;
2640 if (nRejected == 0) {
2648 TCanvas *c_au = PlottingUtils::GetConfiguredCanvas(kFALSE);
2649 PlottingUtils::ConfigureAndDrawHistogram(h_au, kBlue + 1);
2650 PlottingUtils::SaveFigure(c_au,
"grid_parity_rejected_au",
2651 "strip_sum_scatter", PlotSaveOptions::kLOG);
2653 TCanvas *c_adc = PlottingUtils::GetConfiguredCanvas(kFALSE);
2654 PlottingUtils::ConfigureAndDrawHistogram(h_adc, kBlue + 1);
2655 PlottingUtils::SaveFigure(c_adc,
"grid_parity_rejected_adc",
2656 "strip_sum_scatter", PlotSaveOptions::kLOG);
2663void StripSumScatter::InteractiveOverlay(Int_t reac) {
2664 const Int_t kReacMin =
2666 const Int_t kReacMax =
2670 const Int_t kTracesPerRegion =
2673 if (reac < kReacMin || reac > kReacMax) {
2674 std::cerr <<
"strip-sum-scatter: candidate reaction strip " << reac
2675 <<
" outside [" << kReacMin <<
"," << kReacMax
2676 <<
"]; skipping interactive overlay." << std::endl;
2682 TCutG *cutAn =
nullptr;
2683 TCutG *cutAa =
nullptr;
2686 if (!
Constants::cfg.STRIP_SUM_SCATTER_CONFIG.REGION_CUT_REDRAW) {
2687 cutAn = LoadRegionCut(
"region_an", reac);
2688 cutAa = LoadRegionCut(
"region_aa", reac);
2690 TCanvas *cutCanvas =
nullptr;
2699 static Int_t app_argc = 1;
2700 static char app_arg0[] =
"strip-sum-scatter";
2701 static char *app_argv[] = {app_arg0};
2703 if (cutAn && cutAa) {
2704 std::cout <<
" [region] loaded saved cuts for reac " << reac << std::endl;
2710 if (!gSystem->Getenv(
"DISPLAY")) {
2711 std::cerr <<
"strip-sum-scatter: no saved region cuts for reac " << reac
2712 <<
" and no DISPLAY to draw them; skipping interactive "
2713 "region-trace overlay (scatters already saved)."
2718 new TApplication(
"strip-sum-scatter", &app_argc, app_argv);
2719 gROOT->SetBatch(kFALSE);
2721 cutCanvas =
new TCanvas(
"c_strip_sum_regions",
2722 "Draw (a,n) then (a,a') regions", 900, 700);
2723 cutCanvas->SetLogz(kTRUE);
2724 m_scatter[reac]->Draw(
"COLZ");
2725 cutCanvas->Update();
2726 cutAn = PromptCut(cutCanvas,
"region_an",
"(a,n)");
2727 cutAa = PromptCut(cutCanvas,
"region_aa",
"(a,a')");
2728 SaveRegionCuts(reac, cutAn, cutAa);
2730 cutCanvas->GetListOfPrimitives()->Remove(cutAn);
2731 cutCanvas->GetListOfPrimitives()->Remove(cutAa);
2732 gROOT->SetEditorMode();
2733 gSystem->ProcessEvents();
2746 gSystem->ProcessEvents();
2751 std::vector<TGraph *> tr_an, tr_aa, tr_beam;
2754 std::vector<TGraph *> tr_an_adc, tr_aa_adc, tr_beam_adc;
2758 const Bool_t kSkipSg =
2760 std::vector<TGraph *> tr_an_sg, tr_aa_sg, tr_beam_sg;
2761 UInt_t bit = (1u << ReacIndex(reac));
2763 for (Int_t k = 0; k < Int_t(m_reservoir.size()); k++) {
2764 if (Int_t(tr_an.size()) >= kTracesPerRegion &&
2765 Int_t(tr_aa.size()) >= kTracesPerRegion &&
2766 Int_t(tr_beam.size()) >= kTracesPerRegion)
2768 const TraceEvt &e = m_reservoir[k];
2769 if (e.
beam_flat && Int_t(tr_beam.size()) < kTracesPerRegion) {
2770 tr_beam.push_back(TraceFromTotal(e.
total));
2771 tr_beam_adc.push_back(TraceFromTotal(e.
total_adc));
2773 tr_beam_sg.push_back(SmoothedTraceFromTotal(e.
total));
2779 for (Int_t s = 0; s < 18; s++)
2780 td[s] = Double_t(e.
total[s]);
2781 Double_t x = 0.0, y = 0.0;
2783 if (cutAn && Int_t(tr_an.size()) < kTracesPerRegion &&
2784 cutAn->IsInside(x, y)) {
2785 tr_an.push_back(TraceFromTotal(e.
total));
2786 tr_an_adc.push_back(TraceFromTotal(e.
total_adc));
2788 tr_an_sg.push_back(SmoothedTraceFromTotal(e.
total));
2789 }
else if (cutAa && Int_t(tr_aa.size()) < kTracesPerRegion &&
2790 cutAa->IsInside(x, y)) {
2791 tr_aa.push_back(TraceFromTotal(e.
total));
2792 tr_aa_adc.push_back(TraceFromTotal(e.
total_adc));
2794 tr_aa_sg.push_back(SmoothedTraceFromTotal(e.
total));
2798 std::cout <<
"Sampled traces: beam=" << tr_beam.size()
2799 <<
" (a,a')=" << tr_aa.size() <<
" (a,n)=" << tr_an.size()
2801 if (
Constants::cfg.STRIP_SUM_SCATTER_CONFIG.ALT_DECODE_REGION_TRACES)
2802 DrawAltDecodeRegionTraces(reac, cutAn, cutAa);
2803 DrawRegionTraces(Form(
"region_traces_reac%d", reac),
"strip_sum_scatter",
2804 tr_beam, tr_aa, tr_an, 0.6, 1.6,
"#DeltaE [a.u.]");
2805 DrawRegionMeanTraces(Form(
"region_mean_traces_reac%d", reac),
2806 "strip_sum_scatter", tr_beam, tr_aa, tr_an, 0.6, 1.6,
2808 Double_t adc_y_lo = 0.0, adc_y_hi = 0.0;
2809 TraceYRange(tr_beam_adc, tr_aa_adc, tr_an_adc, adc_y_lo, adc_y_hi);
2810 DrawRegionTraces(Form(
"region_traces_reac%d_adc", reac),
"strip_sum_scatter",
2811 tr_beam_adc, tr_aa_adc, tr_an_adc, adc_y_lo, adc_y_hi,
2813 DrawRegionMeanTraces(Form(
"region_mean_traces_reac%d_adc", reac),
2814 "strip_sum_scatter", tr_beam_adc, tr_aa_adc, tr_an_adc,
2815 adc_y_lo, adc_y_hi,
"#DeltaE [ADC]");
2817 DrawRegionTraces(Form(
"region_traces_reac%d_sg", reac),
"strip_sum_scatter",
2818 tr_beam_sg, tr_aa_sg, tr_an_sg, 0.6, 1.6,
2820 DrawRegionMeanTraces(Form(
"region_mean_traces_reac%d_sg", reac),
2821 "strip_sum_scatter", tr_beam_sg, tr_aa_sg, tr_an_sg,
2822 0.7, 1.3,
"#DeltaE [a.u.]");
2825 for (Int_t i = 0; i < Int_t(tr_an.size()); i++)
2827 for (Int_t i = 0; i < Int_t(tr_aa.size()); i++)
2829 for (Int_t i = 0; i < Int_t(tr_beam.size()); i++)
2831 for (Int_t i = 0; i < Int_t(tr_an_adc.size()); i++)
2832 delete tr_an_adc[i];
2833 for (Int_t i = 0; i < Int_t(tr_aa_adc.size()); i++)
2834 delete tr_aa_adc[i];
2835 for (Int_t i = 0; i < Int_t(tr_beam_adc.size()); i++)
2836 delete tr_beam_adc[i];
2837 for (Int_t i = 0; i < Int_t(tr_an_sg.size()); i++)
2839 for (Int_t i = 0; i < Int_t(tr_aa_sg.size()); i++)
2841 for (Int_t i = 0; i < Int_t(tr_beam_sg.size()); i++)
2842 delete tr_beam_sg[i];
2850 ROOT::EnableThreadSafety();
2851 InitUtils::SetROOTPreferences(PlotSaveFormat::kPNG,
2854 gROOT->SetBatch(kTRUE);
2856 std::vector<Int_t> run_order;
2858 if (run_order.empty()) {
2859 std::cerr <<
"strip-sum-scatter: no runs found" << std::endl;
2866 Double_t jump_sigma[18], strip_sigma[18];
2867 if (!MeasureBeamNoise(chain_by_run[run_order[0]], jump_sigma, strip_sigma)) {
2868 std::cerr <<
"strip-sum-scatter: cannot measure the beam noise on run "
2869 << run_order[0] << std::endl;
2875 const Int_t kReacMin =
2877 const Int_t kReacMax =
2880 Form(
"strip-sum-scatter: jump noise sigma from run %d:", run_order[0]);
2881 for (Int_t reac = kReacMin; reac <= kReacMax; reac++)
2882 line += Form(
" s%d %.4f", reac, jump_sigma[reac]);
2883 std::cout << line << std::endl;
2885 Form(
"strip-sum-scatter: strip noise sigma from run %d:", run_order[0]);
2886 for (Int_t strip = 1; strip < kReacMax; strip++)
2887 line += Form(
" s%d %.4f", strip, strip_sigma[strip]);
2888 std::cout << line << std::endl;
2889 std::cout << Form(
"strip-sum-scatter: jump gate %.2f sigma -> %.4f at "
2890 "strip %d, %.4f at strip %d",
2897 TString fingerprint = BuildFingerprint(run_order, chain_by_run);
2900 Bool_t loaded = TryLoadCache(cache_name, fingerprint);
2903 FillScatters(run_order, chain_by_run);
2904 WriteCache(cache_name, fingerprint);
2913 PlotParityRejectedGrid(run_order, chain_by_run);
2922 Int_t reac =
Constants::cfg.STRIP_SUM_SCATTER_CONFIG.CANDIDATE_REAC_STRIP;
2923 InteractiveOverlay(reac);
2926 for (Int_t i = 0; i < Int_t(run_order.size()); i++)
2927 delete chain_by_run[run_order[i]];
std::mutex g_plot_mutex
Serialises all plotting and canvas work.
std::mutex g_log_mutex
Serialises multi-line progress logging from worker threads.
The reaction search: strip-sum scatters, beam gating and tagging.
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 Bool_t InEllipseXY(const BeamFit2D &b, Double_t x, Double_t y, Double_t nx, Double_t ny)
Whether a point lies inside the fitted beam ellipse.
StripSumScatterConfig STRIP_SUM_SCATTER_CONFIG
Bool_t IGNORE_SHORT_STRIPS
static TGraph * BuildTraceFromTotals(const Double_t *total)
Build a trace graph from one event's per-strip totals.
static TString EventsName(const FileSpec &s)
Filename of the built-events ROOT file for a subfile.
static std::map< Int_t, TChain * > GroupEventsByRun(std::vector< Int_t > &run_order)
Chain every run's events files, grouped by run.
static std::vector< FileSpec > BuildProcessedFileSpecs()
Every subfile that already has processed output.
static Long64_t SampleStride(Long64_t n_total, Long64_t max_points)
Stride that visits at most max_points of n_total entries.
static TString DatasetDir()
Absolute path to the active dataset directory, analysis/<iso>.
static TString ResultsDir()
Absolute path to the directory receiving generated output.
static Bool_t IsEresTag(const TString &tag)
Whether a tag denotes an energy-resolution simulation.
static TString SimRootPath(const SimFileSpec &s)
Absolute path to a simulation's ROOT file.
static Int_t ReactionStripOf(const TString &tag)
Which strip a simulated reaction occurs on.
static TString TagWithoutStrip(const TString &tag)
The tag with any trailing _s<N> reaction-strip token removed.
static std::vector< SimFileSpec > BuildFileSpecs()
Every simulation this dataset defines.
static TString CacheName()
Filename of the scatter cache for this configuration.
static Int_t YLoOf(Int_t reac)
First strip of the post-trigger window summed onto y.
static void SetStripSigma(const Double_t *sigma)
Install the per-strip sigmas.
static Int_t YHiOf(Int_t reac)
Last strip of the post-trigger window, inclusive.
static Bool_t PassesReaction(const EnergyView &ev, Int_t reac)
Whether an event is tagged as a reaction at a given strip.
static Double_t JumpMin(Int_t reac)
Minimum jump for a tag: REAC_JUMP_NSIGMA * JumpSigma(reac).
static Double_t StripSigma(Int_t strip)
Sigma of a strip's own deposit.
void Run()
Build or load the scatters, tag reactions, and draw everything.
static Double_t JumpSigma(Int_t strip)
Sigma of the strip-to-strip difference total[s] - total[s-1].
StripSumScatter()
Construct with empty scatters and no cache loaded.
~StripSumScatter()
Frees the scatters and the reservoir.
static void PlaneXY(const Double_t *total, Int_t reac, Double_t &x, Double_t &y)
Where an event sits in the scatter plane for a given reaction strip.
static void SetJumpSigma(const Double_t *sigma)
Install the jump sigmas.
Double_t ActiveGridMaxAdc()
Grid full scale, in ADC.
const DatasetConfig & cfg
The active dataset's configuration, flat block.
void Save(Int_t reac, TCutG *cut_an, TCutG *cut_aa, Double_t n_an_assigned=-1.0)
Write a strip's two region cuts.
TCutG * Load(const char *name, Int_t reac)
Load a cut, from either storage generation.
const Double_t kXMin
Lower x bound of the build window.
const Double_t kXMax
Upper x bound; x sums 16 strips.
const Double_t kYMin
Lower y bound.
const Double_t kYMax
Upper y bound; y sums the post-trigger strips only.
void Write(const TString &channel, const std::vector< TagEfficiencyRecord > &records, const TString &method)
Replace one channel's records, keeping every other channel's.
The classification ellipses defining a pure-beam event.
BeamFit2D s15_s16
Alternative exit ellipse.
BeamFit2D s0_s1
Entrance ellipse on strips 0 and 1.
Bool_t ok
Whether the fits succeeded.
Bool_t use_s15_s16
Which exit ellipse is in force.
BeamFit2D s16_s17
Exit ellipse on strips 16 and 17.
BeamFit2D s1_s2
Alternative entrance ellipse, per PURE_BEAM_GATE.
A fitted 2-D Gaussian beam spot.
Double_t rho
Correlation coefficient, in [-1, 1].
Double_t sigma_y
Widths along each axis.
Double_t amp
Peak amplitude.
Bool_t ok
Whether the fit converged.
A view over one event's energies, decoding raw ADC into calibrated units.
void Decode()
Decode the currently loaded entry into the value members.
UShort_t rightdE_adc[18]
Raw right-side strip ADC values.
Float_t gain_left[18]
Per-strip left-side gain.
Bool_t Attach(TTree *t)
Bind to an events tree and set up the branch addresses.
Double_t total[18]
Summed energy per strip, after strip_factor.
Float_t strip_factor[18]
Per-strip multiplicative alignment, pol3_reference / centroid, applied to total after the per-channel...
UShort_t left_0_17_adc[18]
Raw left-side strip ADC values.
Float_t gain_right[18]
Per-strip right-side gain.
Short_t grid_adc
Raw Frisch grid ADC value.
Double_t grid
Grid energy.
A pair of strips whose sums form one classification plane.
Int_t sx
Strip whose sum forms the x axis.
Int_t sy
Strip whose sum forms the y axis.
Double_t rho
Correlation coefficient, in [-1, 1].
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.
TString label
Legend label.
TString file
Simulation ROOT file.
One run's filled scatters, reservoir and normalisation counts.
std::vector< Long64_t > normed_at
Per-strip denominator: beam particles that reached that strip under exactly the conditions a reaction...
Long64_t seen
Events examined.
Long64_t normed
Events surviving every cut applied before reaction tagging: the beam gates, the pileup,...
Long64_t gated
Events passing the beam gates.
std::vector< Long64_t > tagged
Events tagged at each reaction strip, indexed the same way.
std::vector< TH2F * > scatters
Private clones, one per reaction strip.
std::vector< TraceEvt > reservoir
Tagged events from this run.
One run's fitted beam gates.
std::vector< BeamFit2D > series_gates
One gate per active GateSpec.
Bool_t ok
Whether the fits succeeded.
BeamEllipses pure_beam
Entrance and exit ellipses.
Everything governing the reaction search in the strip-sum scatters.
Long64_t SAMPLE_MAX_POINTS
Double_t PARITY_ASYM_MAX
Reject events whose even strips and odd strips disagree by more than this fraction,...
std::map< Int_t, Int_t > POST_WINDOW_STRIPS
Int_t POST_WINDOW_LAST_STRIP
Int_t POST_TRIGGER_SUM_STRIPS
Strips summed onto the scatter y-axis after the trigger strip: y spans reac+1 .
Int_t CLUSTER_SMOOTH_WINDOW
Double_t REAC_JUMP_NSIGMA
Minimum jump at the reaction strip for a tag, in sigma of the measured strip-to-strip beam noise (Str...
Bool_t PLOT_PARITY_REJECTED_GRID
Diagnostic only: when set, run an extra pass over the events (gated by PARITY_ASYM_MAX > 0) that fill...
Double_t REQUIRE_SMOOTHNESS_MAX_STEP
Double_t PILEUP_THRESHOLD
Bool_t REQUIRE_BEAM_UPSTREAM_OF_REAC
Tolerance in sigma of each strip's measured beam spread (StripSumScatter::StripSigma).
Double_t BEAM_UPSTREAM_NSIGMA
Int_t REQUIRE_SMOOTHNESS_END_STRIP
std::map< Int_t, std::pair< Double_t, Double_t > > Y_DISPLAY_RANGE
Per-reaction-strip y-axis display windows, overriding Y_DISPLAY_MIN/MAX for individual strips (displa...
Double_t PILEUP_THRESH_PY
Double_t TRIGGER_CFD_FRAC
Float_t total[18]
Calibrated per-strip totals.
Int_t both_mult
Split strips (1-16) with both ends above threshold.
Float_t total_adc[18]
Raw, un-normalised ADC sum per strip.
Float_t long_au[18]
Long end of each split strip.
ULong64_t seed_ts
Timestamp of the grid hit that seeded the event, from the events tree.
Bool_t beam_flat
Whether the trace looked flat, i.e. beam-like.
Float_t short_au[18]
Short end.
UInt_t reac_mask
Bit per reaction strip this event was tagged at.