MUSIC unknown
Analysis for the MUSIC active-target ionization chamber
Loading...
Searching...
No Matches
RegionCuts.cpp
Go to the documentation of this file.
1#include "RegionCuts.hpp"
2#include "Constants.hpp"
3#include "Paths.hpp"
4#include "PlottingUtils.hpp"
5#include <TAxis.h>
6#include <TCanvas.h>
7#include <TFile.h>
8#include <TH1D.h>
9#include <TMath.h>
10#include <TParameter.h>
11#include <TSystem.h>
12#include <cmath>
13#include <iostream>
14#include <vector>
15
16// ---------------------------------------------------------------------------
17// Store
18
19namespace RegionCutStore {
20
21static TString Key(const char *name, Int_t reac) {
22 return Form("%s_reac%d", name, reac);
23}
24
25// A tagged epoch keeps its own cuts beside the untagged eras'.
26TString Dir() {
27 const TString &tag = Constants::ActiveFileTag();
28 return Paths::ResultsDir() + "/root_files/region_cuts" +
29 (tag.Length() > 0 ? "_" + tag : TString(""));
30}
31
32TString Path(const char *name, Int_t reac) {
33 return Dir() + "/" + Key(name, reac) + ".root";
34}
35
36static void WriteOne(const char *name, Int_t reac, TCutG *cut,
37 Double_t n_assigned) {
38 if (!cut)
39 return;
40 TString path = Path(name, reac);
41 TFile f(path, "RECREATE");
42 if (f.IsZombie()) {
43 std::cerr << " [region] cannot open " << path << " to save cut"
44 << std::endl;
45 return;
46 }
47 f.cd();
48 cut->Write(name);
49 if (n_assigned >= 0.0)
50 TParameter<Double_t>("n_assigned", n_assigned).Write();
51 f.Close();
52 std::cout << " [region] saved " << name << " reac " << reac << " -> " << path
53 << std::endl;
54}
55
56void Save(Int_t reac, TCutG *cut_an, TCutG *cut_aa, Double_t n_an_assigned) {
57 gSystem->mkdir(Dir(), kTRUE);
58 WriteOne("region_an", reac, cut_an, n_an_assigned);
59 WriteOne("region_aa", reac, cut_aa, -1.0);
60}
61
62static const char *kFitKeys[14] = {
63 "fit_beam_amp", "fit_beam_mx", "fit_beam_sx", "fit_beam_my", "fit_beam_sy",
64 "fit_beam_rho", "fit_reac_amp", "fit_reac_mx", "fit_reac_sx", "fit_reac_my",
65 "fit_reac_sy", "fit_reac_rho", "fit_n_beam", "fit_n_reac"};
66
67static void FitValues(const RegionFit &fit, Double_t *v) {
68 const Gauss2D *g[2] = {&fit.beam, &fit.reac};
69 for (Int_t k = 0; k < 2; k++) {
70 v[6 * k + 0] = g[k]->amp;
71 v[6 * k + 1] = g[k]->mx;
72 v[6 * k + 2] = g[k]->sx;
73 v[6 * k + 3] = g[k]->my;
74 v[6 * k + 4] = g[k]->sy;
75 v[6 * k + 5] = g[k]->rho;
76 }
77 v[12] = fit.n_beam;
78 v[13] = fit.n_reac;
79}
80
81void SaveFit(Int_t reac, const RegionFit &fit) {
82 TString path = Path("region_an", reac);
83 TFile f(path, "UPDATE");
84 if (f.IsZombie()) {
85 std::cerr << " [region] cannot open " << path << " to save the fit"
86 << std::endl;
87 return;
88 }
89 Double_t v[14];
90 FitValues(fit, v);
91 f.cd();
92 for (Int_t k = 0; k < 14; k++)
93 TParameter<Double_t>(kFitKeys[k], v[k])
94 .Write(kFitKeys[k], TObject::kOverwrite);
95 f.Close();
96}
97
98Double_t LoadAssigned(const char *name, Int_t reac) {
99 TString path = Path(name, reac);
100 if (gSystem->AccessPathName(path))
101 return -1.0;
102 TFile f(path, "READ");
103 if (f.IsZombie())
104 return -1.0;
105 TParameter<Double_t> *p =
106 dynamic_cast<TParameter<Double_t> *>(f.Get("n_assigned"));
107 const Double_t n = p ? p->GetVal() : -1.0;
108 f.Close();
109 return n;
110}
111
112static TCutG *ReadFrom(const TString &path, const char *key, const char *name) {
113 if (gSystem->AccessPathName(path))
114 return nullptr;
115 TFile f(path, "READ");
116 if (f.IsZombie())
117 return nullptr;
118 TCutG *stored = dynamic_cast<TCutG *>(f.Get(key));
119 if (!stored) {
120 f.Close();
121 return nullptr;
122 }
123 // The file owns the object; hand back a copy that outlives the close.
124 TCutG *cut = static_cast<TCutG *>(stored->Clone(name));
125 f.Close();
126 return cut;
127}
128
129TCutG *Load(const char *name, Int_t reac) {
130 return ReadFrom(Path(name, reac), name, name);
131}
132
133} // namespace RegionCutStore
134
135// ---------------------------------------------------------------------------
136// Finder
137
138namespace {
139
140// ROOT's "bigaus": peak height `amp` in counts per bin, no normalisation.
141Double_t Bigaus(const Gauss2D &g, Double_t x, Double_t y) {
142 const Double_t dx = (x - g.mx) / g.sx, dy = (y - g.my) / g.sy;
143 const Double_t r2 = 1.0 - g.rho * g.rho;
144 return g.amp *
145 std::exp(-0.5 / r2 * (dx * dx + dy * dy - 2.0 * g.rho * dx * dy));
146}
147
148// Squared Mahalanobis distance of (x, y) from a component.
149Double_t Mahal2(const Gauss2D &g, Double_t x, Double_t y) {
150 const Double_t dx = (x - g.mx) / g.sx, dy = (y - g.my) / g.sy;
151 return (dx * dx + dy * dy - 2.0 * g.rho * dx * dy) / (1.0 - g.rho * g.rho);
152}
153
154// log of the bivariate normalisation, so densities of two components with
155// different widths compare fairly.
156Double_t LogNorm(const Gauss2D &g) {
157 return std::log(2.0 * TMath::Pi() * g.sx * g.sy *
158 std::sqrt(1.0 - g.rho * g.rho));
159}
160
165Gauss2D MomentsToGauss(Double_t sw, Double_t sx, Double_t sy, Double_t sxx,
166 Double_t syy, Double_t sxy, Double_t bwx, Double_t bwy) {
167 Gauss2D g;
168 g.mx = sx / sw;
169 g.my = sy / sw;
170 const Double_t vx = sxx / sw - g.mx * g.mx, vy = syy / sw - g.my * g.my;
171 const Double_t cv = sxy / sw - g.mx * g.my;
172 g.sx = std::sqrt(TMath::Max(vx, bwx * bwx));
173 g.sy = std::sqrt(TMath::Max(vy, bwy * bwy));
174 g.rho = TMath::Max(-0.95, TMath::Min(0.95, cv / (g.sx * g.sy)));
175 g.amp = sw * bwx * bwy /
176 (2.0 * TMath::Pi() * g.sx * g.sy * std::sqrt(1.0 - g.rho * g.rho));
177 return g;
178}
179
180// FWHM-based width of a projection around its maximum (over its set range).
181Double_t WidthAtMax(TH1D *h, Double_t &at) {
182 Int_t b = h->GetMaximumBin();
183 Double_t c = h->GetBinContent(b);
184 at = h->GetBinCenter(b);
185 Int_t lo = b, hi = b;
186 while (lo > 1 && h->GetBinContent(lo) > 0.5 * c)
187 lo--;
188 while (hi < h->GetNbinsX() && h->GetBinContent(hi) > 0.5 * c)
189 hi++;
190 return TMath::Max(h->GetBinWidth(1),
191 (h->GetBinCenter(hi) - h->GetBinCenter(lo)) / 2.355);
192}
193
194// Height above the ridge (AboveRidge, declared in the header): island lives
195// at u ~ 4, the ridge at u ~ 0 — a separation a plain y projection washes out.
197
198// The fitted mixture evaluated per bin over the window, in a copy of the
199// scatter, so it can be projected and contoured exactly like the data.
200TH2F *ModelHist(TH2F *scatter, const RegionFit &fit, Int_t reac) {
201 TH2F *m = static_cast<TH2F *>(scatter->Clone(Form("model_reac%d", reac)));
202 m->SetDirectory(nullptr);
203 m->Reset();
204 TAxis *ax = m->GetXaxis(), *ay = m->GetYaxis();
205 for (Int_t i = ax->FindBin(fit.x_lo); i <= ax->FindBin(fit.x_hi); i++)
206 for (Int_t j = ay->FindBin(fit.y_lo); j <= ay->FindBin(fit.y_hi); j++) {
207 Double_t x = ax->GetBinCenter(i), y = ay->GetBinCenter(j);
208 m->SetBinContent(i, j, Bigaus(fit.beam, x, y) + Bigaus(fit.reac, x, y));
209 }
210 return m;
211}
212
218Bool_t BeamFromCore(TH2F *scatter, Int_t reac, Int_t bx0, Int_t bx1, Int_t by0,
219 Int_t by1, Gauss2D &beam, TString &why) {
220 TAxis *ax = scatter->GetXaxis(), *ay = scatter->GetYaxis();
221 const Double_t bwx = ax->GetBinWidth(1), bwy = ay->GetBinWidth(1);
222 TH1D *px = scatter->ProjectionX(Form("rcf_px_%d", reac), by0, by1);
223 TH1D *py = scatter->ProjectionY(Form("rcf_py_%d", reac), bx0, bx1);
224 px->SetDirectory(nullptr);
225 py->SetDirectory(nullptr);
226 px->GetXaxis()->SetRange(bx0, bx1);
227 py->GetXaxis()->SetRange(by0, by1);
228 Double_t mx0 = 0.0, my0 = 0.0;
229 const Double_t sx0 = WidthAtMax(px, mx0), sy0 = WidthAtMax(py, my0);
230 delete px;
231 delete py;
232 Double_t sw = 0, sx = 0, sy = 0, sxx = 0, syy = 0, sxy = 0;
233 for (Int_t i = ax->FindBin(mx0 - 2 * sx0); i <= ax->FindBin(mx0 + 2 * sx0);
234 i++)
235 for (Int_t j = ay->FindBin(my0 - 2 * sy0); j <= ay->FindBin(my0 + 2 * sy0);
236 j++) {
237 Double_t w = scatter->GetBinContent(i, j);
238 Double_t x = ax->GetBinCenter(i), y = ay->GetBinCenter(j);
239 sw += w;
240 sx += w * x;
241 sy += w * y;
242 sxx += w * x * x;
243 syy += w * y * y;
244 sxy += w * x * y;
245 }
246 if (!(sw > 0)) {
247 why = "empty beam core";
248 return kFALSE;
249 }
250 beam = MomentsToGauss(sw, sx, sy, sxx, syy, sxy, bwx, bwy);
251 return kTRUE;
252}
253
254} // namespace
255
256namespace RegionCutFinder {
257
258RegionFit FitMixture(TH2F *scatter, Int_t reac, Double_t x_lo, Double_t x_hi,
259 Double_t y_lo, Double_t y_hi) {
260 RegionFit fit;
261 fit.x_lo = x_lo;
262 fit.x_hi = x_hi;
263 fit.y_lo = y_lo;
264 fit.y_hi = y_hi;
265 TAxis *ax = scatter->GetXaxis(), *ay = scatter->GetYaxis();
266 const Int_t bx0 = ax->FindBin(x_lo), bx1 = ax->FindBin(x_hi);
267 const Int_t by0 = ay->FindBin(y_lo), by1 = ay->FindBin(y_hi);
268 const Double_t bwx = ax->GetBinWidth(1), bwy = ay->GetBinWidth(1);
269
270 Gauss2D beam;
271 if (!BeamFromCore(scatter, reac, bx0, bx1, by0, by1, beam, fit.why))
272 return fit;
273
274 // Seed: the island, u ~ 4 above the ridge and beyond the beam in x, is the
275 // x maximum beyond +3 sigma_x in 3..15 sigma above the ridge at its centroid.
276 TH1D hx(Form("rcf_hx_%d", reac), "", 80, x_lo, x_hi);
277 hx.SetDirectory(nullptr);
278 for (Int_t i = bx0; i <= bx1; i++)
279 for (Int_t j = by0; j <= by1; j++) {
280 const Double_t w = scatter->GetBinContent(i, j);
281 if (!(w > 0))
282 continue;
283 const Double_t x = ax->GetBinCenter(i), y = ay->GetBinCenter(j);
284 const Double_t u = AboveRidge(beam, x, y);
285 if (u > 3.0 && u < 15.0 && x > beam.mx + 3.0 * beam.sx)
286 hx.Fill(x, w);
287 }
288 const Int_t bpk = hx.GetMaximumBin();
289 const Double_t xpk = hx.GetBinCenter(bpk);
290 if (hx.GetBinContent(bpk) < 5.0) {
291 fit.why = "nothing above the ridge beyond +3 sigma_x of the beam";
292 return fit;
293 }
294 Double_t s[6] = {0, 0, 0, 0, 0, 0};
295 for (Int_t i = bx0; i <= bx1; i++)
296 for (Int_t j = by0; j <= by1; j++) {
297 const Double_t w = scatter->GetBinContent(i, j);
298 if (!(w > 0))
299 continue;
300 const Double_t x = ax->GetBinCenter(i), y = ay->GetBinCenter(j);
301 const Double_t u = AboveRidge(beam, x, y);
302 if (u > 3.0 && u < 15.0 && std::fabs(x - xpk) < 2.0 * beam.sx) {
303 s[0] += w;
304 s[1] += w * x;
305 s[2] += w * y;
306 s[3] += w * x * x;
307 s[4] += w * y * y;
308 s[5] += w * x * y;
309 }
310 }
311 if (s[0] < 20.0) {
312 fit.why = Form("only %.0f events in the seed neighbourhood", s[0]);
313 return fit;
314 }
315 Gauss2D reac_g = beam; // shape starts as the beam's
316 reac_g.mx = s[1] / s[0];
317 reac_g.my = s[2] / s[0];
318
319 // EM: a bin joins the reaction only if above the ridge, within 3 sigma of it,
320 // and log-density beats the beam's; the prior keeps 4-sigma tail bins with it
321 Double_t n_beam = 0.0, n_reac = 0.0;
322 Double_t total = 0.0;
323 for (Int_t i = bx0; i <= bx1; i++)
324 for (Int_t j = by0; j <= by1; j++)
325 total += scatter->GetBinContent(i, j);
326 Double_t prior_reac = 1.0e-3; // until the first assignment counts it
327 for (Int_t iter = 0; iter < 20; iter++) {
328 Double_t mb[6] = {0, 0, 0, 0, 0, 0}, mr[6] = {0, 0, 0, 0, 0, 0};
329 const Double_t lp_b = std::log(1.0 - prior_reac) - LogNorm(beam);
330 const Double_t lp_r = std::log(prior_reac) - LogNorm(reac_g);
331 for (Int_t i = bx0; i <= bx1; i++)
332 for (Int_t j = by0; j <= by1; j++) {
333 const Double_t w = scatter->GetBinContent(i, j);
334 if (!(w > 0))
335 continue;
336 const Double_t x = ax->GetBinCenter(i), y = ay->GetBinCenter(j);
337 const Double_t d2r = Mahal2(reac_g, x, y);
338 // Admission at 3 sigma above the ridge: at 2 sigma the component takes
339 // the beam's tail sheet; ~3 sigma_x off the ridge, doubles the count.
340 const Bool_t to_reac =
341 AboveRidge(beam, x, y) > 3.0 && d2r < 9.0 &&
342 (lp_r - 0.5 * d2r) > (lp_b - 0.5 * Mahal2(beam, x, y));
343 Double_t *m = to_reac ? mr : mb;
344 m[0] += w;
345 m[1] += w * x;
346 m[2] += w * y;
347 m[3] += w * x * x;
348 m[4] += w * y * y;
349 m[5] += w * x * y;
350 }
351 if (mr[0] < 20.0) {
352 fit.why = Form("reaction component starved (%.0f events) at iteration %d",
353 mr[0], iter);
354 return fit;
355 }
356 Gauss2D nb =
357 MomentsToGauss(mb[0], mb[1], mb[2], mb[3], mb[4], mb[5], bwx, bwy);
358 Gauss2D nr =
359 MomentsToGauss(mr[0], mr[1], mr[2], mr[3], mr[4], mr[5], bwx, bwy);
360 // Island = beam blob displaced by the reaction: in sim 4-6% wider in x,
361 // 8-10% in y, so the cap is 1.1x; looser takes tail ~3 sigma_x off ridge.
362 nr.sx = TMath::Min(nr.sx, 1.1 * nb.sx);
363 nr.sy = TMath::Min(nr.sy, 1.1 * nb.sy);
364 // Island x-y correlation comes from the same beam-energy spread, so it
365 // cannot exceed the beam's; more elongation along ridge traces beam tail.
366 if (std::fabs(nr.rho) > std::fabs(nb.rho))
367 nr.rho = nr.rho < 0 ? -std::fabs(nb.rho) : std::fabs(nb.rho);
368 const Bool_t moved = std::fabs(nr.mx - reac_g.mx) > 0.1 * bwx ||
369 std::fabs(nr.my - reac_g.my) > 0.1 * bwy;
370 beam = nb;
371 reac_g = nr;
372 n_beam = mb[0];
373 n_reac = mr[0];
374 prior_reac = TMath::Max(1.0e-6, n_reac / total);
375 if (!moved)
376 break;
377 }
378 fit.beam = beam;
379 fit.reac = reac_g;
380
381 if (fit.reac.my < fit.beam.my + 2.0 * fit.beam.sy) {
382 fit.why = "reaction component fell back onto the ridge";
383 return fit;
384 }
385 if (n_reac < 20.0) {
386 fit.why = Form("reaction component holds only %.0f events", n_reac);
387 return fit;
388 }
389 fit.n_beam = n_beam;
390 fit.n_reac = n_reac;
391 fit.ok = kTRUE;
392 return fit;
393}
394
395Double_t AboveRidge(const Gauss2D &b, Double_t x, Double_t y) {
396 return ((y - b.my) - b.rho * (b.sy / b.sx) * (x - b.mx)) /
397 (b.sy * std::sqrt(1.0 - b.rho * b.rho));
398}
399
400TCutG *EllipseCut(const char *name, const Gauss2D &g, Double_t nsigma,
401 Int_t npts) {
402 // Mahalanobis contour at nsigma, via the Cholesky factor of the covariance.
403 TCutG *c = new TCutG(name, npts + 1);
404 const Double_t q = std::sqrt(1.0 - g.rho * g.rho);
405 for (Int_t i = 0; i <= npts; i++) {
406 Double_t t = 2.0 * TMath::Pi() * i / npts;
407 Double_t u = std::cos(t), v = std::sin(t);
408 c->SetPoint(i, g.mx + nsigma * g.sx * u,
409 g.my + nsigma * g.sy * (g.rho * u + q * v));
410 }
411 c->SetLineColor(kBlack);
412 c->SetLineWidth(2);
413 return c;
414}
415
416Double_t CountInside(TH2F *scatter, TCutG *cut, const RegionFit &fit) {
417 if (!cut)
418 return 0.0;
419 TAxis *ax = scatter->GetXaxis(), *ay = scatter->GetYaxis();
420 Double_t n = 0.0;
421 for (Int_t i = ax->FindBin(fit.x_lo); i <= ax->FindBin(fit.x_hi); i++)
422 for (Int_t j = ay->FindBin(fit.y_lo); j <= ay->FindBin(fit.y_hi); j++)
423 if (cut->IsInside(ax->GetBinCenter(i), ay->GetBinCenter(j)))
424 n += scatter->GetBinContent(i, j);
425 return n;
426}
427
428void SaveFigures(TH2F *scatter, Int_t reac, const RegionFit &fit, TCutG *an,
429 TCutG *aa, const TString &subdir) {
430 scatter->GetXaxis()->SetRangeUser(fit.x_lo, fit.x_hi);
431 scatter->GetYaxis()->SetRangeUser(fit.y_lo, fit.y_hi);
432 const Int_t cReac = kRed + 1, cBeam = kAzure + 1;
433
434 // 1. Regions on the scatter, each fitted component's 1/2/3 sigma contours
435 // dashed behind, so fit and cut are both on the page.
436 {
437 TCanvas *c = PlottingUtils::GetConfiguredCanvas(kFALSE);
438 c->SetLogz(kTRUE);
439 PlottingUtils::ConfigureAndDraw2DHistogram(
440 scatter, c,
441 Form("reac %d: fitted components (dashed 1,2,3#sigma) and regions "
442 "(filled)",
443 reac));
444 std::vector<TCutG *> tmp;
445 for (Int_t k = 1; k <= 3; k++) {
446 TCutG *eb = EllipseCut(Form("cb%d_%d", k, reac), fit.beam, k);
447 eb->SetLineColor(cBeam);
448 eb->SetLineStyle(2);
449 eb->SetLineWidth(1);
450 eb->Draw("L SAME");
451 tmp.push_back(eb);
452 TCutG *er = EllipseCut(Form("cr%d_%d", k, reac), fit.reac, k);
453 er->SetLineColor(cReac);
454 er->SetLineStyle(2);
455 er->SetLineWidth(1);
456 er->Draw("L SAME");
457 tmp.push_back(er);
458 }
459 if (aa) {
460 aa->SetFillColorAlpha(cBeam, 0.15);
461 aa->SetLineColor(cBeam);
462 aa->Draw("F SAME");
463 aa->Draw("L SAME");
464 }
465 if (an) {
466 an->SetFillColorAlpha(cReac, 0.25);
467 an->SetLineColor(cReac);
468 an->Draw("F SAME");
469 an->Draw("L SAME");
470 }
471 // 2D: z is already log from the utility; kLOG means a log y here.
472 PlottingUtils::SaveFigure(c, Form("regions_reac%d", reac), subdir,
473 PlotSaveOptions::kLINEAR);
474 delete c;
475 for (Int_t k = 0; k < Int_t(tmp.size()); k++)
476 delete tmp[k];
477 }
478
479 // 2. The fitted density itself as contours over the data.
480 TH2F *model = ModelHist(scatter, fit, reac);
481 {
482 TCanvas *c = PlottingUtils::GetConfiguredCanvas(kFALSE);
483 c->SetLogz(kTRUE);
484 PlottingUtils::ConfigureAndDraw2DHistogram(
485 scatter, c,
486 Form("reac %d: data with the fitted mixture's density", reac));
487 model->SetContour(10);
488 model->SetLineColor(cReac);
489 model->SetLineWidth(1);
490 model->Draw("CONT3 SAME");
491 PlottingUtils::SaveFigure(c, Form("regions_reac%d_model", reac), subdir,
492 PlotSaveOptions::kLINEAR);
493 delete c;
494 }
495
496 // 3./4. Each projection with the model's projection overlaid.
497 TAxis *ax = scatter->GetXaxis(), *ay = scatter->GetYaxis();
498 const Int_t bx0 = ax->FindBin(fit.x_lo), bx1 = ax->FindBin(fit.x_hi);
499 const Int_t by0 = ay->FindBin(fit.y_lo), by1 = ay->FindBin(fit.y_hi);
500 for (Int_t which = 0; which < 2; which++) {
501 TH1D *d = which == 0 ? scatter->ProjectionX(Form("dpx_%d", reac), by0, by1)
502 : scatter->ProjectionY(Form("dpy_%d", reac), bx0, bx1);
503 TH1D *m = which == 0 ? model->ProjectionX(Form("mpx_%d", reac), by0, by1)
504 : model->ProjectionY(Form("mpy_%d", reac), bx0, bx1);
505 d->SetDirectory(nullptr);
506 m->SetDirectory(nullptr);
507 d->GetXaxis()->SetRange(which == 0 ? bx0 : by0, which == 0 ? bx1 : by1);
508 TCanvas *c = PlottingUtils::GetConfiguredCanvas(kTRUE);
509 PlottingUtils::ConfigureAndDrawHistogram(
510 d, kBlack,
511 Form("reac %d: %s projection, data (black) and fitted mixture (red)",
512 reac, which == 0 ? "x" : "y"));
513 m->SetLineColor(cReac);
514 m->SetLineWidth(2);
515 m->Draw("HIST SAME");
516 PlottingUtils::SaveFigure(
517 c, Form("regions_reac%d_proj%s", reac, which == 0 ? "x" : "y"), subdir,
518 PlotSaveOptions::kLOG);
519 delete c;
520 delete d;
521 delete m;
522 }
523 delete model;
524}
525
526} // namespace RegionCutFinder
Dataset configuration.
static TString ResultsDir()
Absolute path to the directory receiving generated output.
Definition Paths.cpp:24
const TString & ActiveFileTag()
Output-name prefix of the active epoch.
Finding regions by fitting a two-component mixture to the scatter.
RegionFit FitMixture(TH2F *scatter, Int_t reac, Double_t x_lo, Double_t x_hi, Double_t y_lo, Double_t y_hi)
Fit the two-component mixture to one strip's scatter.
TCutG * EllipseCut(const char *name, const Gauss2D &g, Double_t nsigma, Int_t npts=64)
Closed polygon of a component's Mahalanobis contour.
Double_t AboveRidge(const Gauss2D &beam, Double_t x, Double_t y)
How far a point sits above the beam ridge, in conditional sigma.
void SaveFigures(TH2F *scatter, Int_t reac, const RegionFit &fit, TCutG *an, TCutG *aa, const TString &subdir)
Save the diagnostic figures for a strip's regions.
Double_t CountInside(TH2F *scatter, TCutG *cut, const RegionFit &fit)
Count scatter events inside a cut.
On-disk home of the per-strip (a,n) and (a,a') region cuts.
TString Path(const char *name, Int_t reac)
Path to one cut's file.
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.
void SaveFit(Int_t reac, const RegionFit &fit)
Store the fitted components beside the (a,n) cut.
TString Dir()
Directory holding the region cut files.
TCutG * Load(const char *name, Int_t reac)
Load a cut from its per-cut file.
Double_t LoadAssigned(const char *name, Int_t reac)
The attributed count stored with a cut.
One bivariate Gaussian component of a strip's scatter.
Double_t mx
Mean in x.
Double_t sx
Width in x.
Double_t amp
Amplitude.
Double_t rho
x-y correlation, in [-1, 1].
Double_t sy
Width in y.
Double_t my
Mean in y.
Result of fitting the two-component mixture to one strip's scatter.
Double_t y_hi
Fit window.
Double_t n_beam
Events the mixture attributes to each component.
Double_t x_lo
Double_t n_reac
TString why
Why the fit failed; set only when ok is false.
Bool_t ok
Whether the fit succeeded. Check this first.
Gauss2D reac
The reaction population above the ridge.
Gauss2D beam
The beam-like population, pinned from its own core.
Double_t y_lo
Double_t x_hi