3#include "PlottingUtils.hpp"
21static TString Key(
const char *name, Int_t reac) {
22 return Form(
"%s_reac%d", name, reac);
27TString
Path(
const char *name, Int_t reac) {
28 return Dir() +
"/" + Key(name, reac) +
".root";
31static TString LegacyPath() {
35static void WriteOne(
const char *name, Int_t reac, TCutG *cut,
36 Double_t n_assigned) {
39 TString path =
Path(name, reac);
40 TFile f(path,
"RECREATE");
42 std::cerr <<
" [region] cannot open " << path <<
" to save cut"
48 if (n_assigned >= 0.0)
49 TParameter<Double_t>(
"n_assigned", n_assigned).Write();
51 std::cout <<
" [region] saved " << name <<
" reac " << reac <<
" -> " << path
55void Save(Int_t reac, TCutG *cut_an, TCutG *cut_aa, Double_t n_an_assigned) {
56 gSystem->mkdir(
Dir(), kTRUE);
57 WriteOne(
"region_an", reac, cut_an, n_an_assigned);
58 WriteOne(
"region_aa", reac, cut_aa, -1.0);
61static const char *kFitKeys[14] = {
62 "fit_beam_amp",
"fit_beam_mx",
"fit_beam_sx",
"fit_beam_my",
"fit_beam_sy",
63 "fit_beam_rho",
"fit_reac_amp",
"fit_reac_mx",
"fit_reac_sx",
"fit_reac_my",
64 "fit_reac_sy",
"fit_reac_rho",
"fit_n_beam",
"fit_n_reac"};
66static void FitValues(
const RegionFit &fit, Double_t *v) {
67 const Gauss2D *g[2] = {&fit.
beam, &fit.
reac};
68 for (Int_t k = 0; k < 2; k++) {
69 v[6 * k + 0] = g[k]->
amp;
70 v[6 * k + 1] = g[k]->
mx;
71 v[6 * k + 2] = g[k]->
sx;
72 v[6 * k + 3] = g[k]->
my;
73 v[6 * k + 4] = g[k]->
sy;
74 v[6 * k + 5] = g[k]->
rho;
81 TString path =
Path(
"region_an", reac);
82 TFile f(path,
"UPDATE");
84 std::cerr <<
" [region] cannot open " << path <<
" to save the fit"
91 for (Int_t k = 0; k < 14; k++)
92 TParameter<Double_t>(kFitKeys[k], v[k])
93 .Write(kFitKeys[k], TObject::kOverwrite);
98 TString path =
Path(
"region_an", reac);
99 if (gSystem->AccessPathName(path))
101 TFile f(path,
"READ");
105 for (Int_t k = 0; k < 14; k++) {
106 TParameter<Double_t> *p =
107 dynamic_cast<TParameter<Double_t> *
>(f.Get(kFitKeys[k]));
113 for (Int_t k = 0; k < 2; k++) {
114 g[k]->
amp = v[6 * k + 0];
115 g[k]->
mx = v[6 * k + 1];
116 g[k]->
sx = v[6 * k + 2];
117 g[k]->
my = v[6 * k + 3];
118 g[k]->
sy = v[6 * k + 4];
119 g[k]->
rho = v[6 * k + 5];
128 TString path =
Path(name, reac);
129 if (gSystem->AccessPathName(path))
131 TFile f(path,
"READ");
134 TParameter<Double_t> *p =
135 dynamic_cast<TParameter<Double_t> *
>(f.Get(
"n_assigned"));
136 const Double_t n = p ? p->GetVal() : -1.0;
141static TCutG *ReadFrom(
const TString &path,
const char *key,
const char *name) {
142 if (gSystem->AccessPathName(path))
144 TFile f(path,
"READ");
147 TCutG *stored =
dynamic_cast<TCutG *
>(f.Get(key));
153 TCutG *cut =
static_cast<TCutG *
>(stored->Clone(name));
158TCutG *
Load(
const char *name, Int_t reac) {
159 TCutG *cut = ReadFrom(
Path(name, reac), name, name);
162 return ReadFrom(LegacyPath(), Key(name, reac), name);
169 TString path =
Path(name, reac);
170 if (!gSystem->AccessPathName(path)) {
171 TFile f(path,
"READ");
172 const Bool_t fitted = !f.IsZombie() && f.Get(
"n_assigned") !=
nullptr;
175 if (TCutG *cut = ReadFrom(path, name, name))
178 return ReadFrom(LegacyPath(), Key(name, reac), name);
190Double_t Bigaus(
const Gauss2D &g, Double_t x, Double_t y) {
191 const Double_t dx = (x - g.
mx) / g.
sx, dy = (y - g.
my) / g.
sy;
192 const Double_t r2 = 1.0 - g.
rho * g.
rho;
194 std::exp(-0.5 / r2 * (dx * dx + dy * dy - 2.0 * g.
rho * dx * dy));
198Double_t Mahal2(
const Gauss2D &g, Double_t x, Double_t y) {
199 const Double_t dx = (x - g.
mx) / g.
sx, dy = (y - g.
my) / g.
sy;
200 return (dx * dx + dy * dy - 2.0 * g.
rho * dx * dy) / (1.0 - g.
rho * g.
rho);
205Double_t LogNorm(
const Gauss2D &g) {
206 return std::log(2.0 * TMath::Pi() * g.
sx * g.
sy *
207 std::sqrt(1.0 - g.
rho * g.
rho));
214Gauss2D MomentsToGauss(Double_t sw, Double_t sx, Double_t sy, Double_t sxx,
215 Double_t syy, Double_t sxy, Double_t bwx, Double_t bwy) {
219 const Double_t vx = sxx / sw - g.
mx * g.
mx, vy = syy / sw - g.
my * g.
my;
220 const Double_t cv = sxy / sw - g.
mx * g.
my;
221 g.
sx = std::sqrt(TMath::Max(vx, bwx * bwx));
222 g.
sy = std::sqrt(TMath::Max(vy, bwy * bwy));
223 g.
rho = TMath::Max(-0.95, TMath::Min(0.95, cv / (g.
sx * g.
sy)));
224 g.
amp = sw * bwx * bwy /
225 (2.0 * TMath::Pi() * g.
sx * g.
sy * std::sqrt(1.0 - g.
rho * g.
rho));
230Double_t WidthAtMax(TH1D *h, Double_t &at) {
231 Int_t b = h->GetMaximumBin();
232 Double_t c = h->GetBinContent(b);
233 at = h->GetBinCenter(b);
234 Int_t lo = b, hi = b;
235 while (lo > 1 && h->GetBinContent(lo) > 0.5 * c)
237 while (hi < h->GetNbinsX() && h->GetBinContent(hi) > 0.5 * c)
239 return TMath::Max(h->GetBinWidth(1),
240 (h->GetBinCenter(hi) - h->GetBinCenter(lo)) / 2.355);
251TH2F *ModelHist(TH2F *scatter,
const RegionFit &fit, Int_t reac) {
252 TH2F *m =
static_cast<TH2F *
>(scatter->Clone(Form(
"model_reac%d", reac)));
253 m->SetDirectory(
nullptr);
255 TAxis *ax = m->GetXaxis(), *ay = m->GetYaxis();
256 for (Int_t i = ax->FindBin(fit.
x_lo); i <= ax->FindBin(fit.
x_hi); i++)
257 for (Int_t j = ay->FindBin(fit.
y_lo); j <= ay->FindBin(fit.
y_hi); j++) {
258 Double_t x = ax->GetBinCenter(i), y = ay->GetBinCenter(j);
259 m->SetBinContent(i, j,
260 Bigaus(fit.
beam, x, y) +
271Bool_t BeamFromCore(TH2F *scatter, Int_t reac, Int_t bx0, Int_t bx1, Int_t by0,
272 Int_t by1,
Gauss2D &beam, TString &why) {
273 TAxis *ax = scatter->GetXaxis(), *ay = scatter->GetYaxis();
274 const Double_t bwx = ax->GetBinWidth(1), bwy = ay->GetBinWidth(1);
275 TH1D *px = scatter->ProjectionX(Form(
"rcf_px_%d", reac), by0, by1);
276 TH1D *py = scatter->ProjectionY(Form(
"rcf_py_%d", reac), bx0, bx1);
277 px->SetDirectory(
nullptr);
278 py->SetDirectory(
nullptr);
279 px->GetXaxis()->SetRange(bx0, bx1);
280 py->GetXaxis()->SetRange(by0, by1);
281 Double_t mx0 = 0.0, my0 = 0.0;
282 const Double_t sx0 = WidthAtMax(px, mx0), sy0 = WidthAtMax(py, my0);
285 Double_t sw = 0, sx = 0, sy = 0, sxx = 0, syy = 0, sxy = 0;
286 for (Int_t i = ax->FindBin(mx0 - 2 * sx0); i <= ax->FindBin(mx0 + 2 * sx0);
288 for (Int_t j = ay->FindBin(my0 - 2 * sy0); j <= ay->FindBin(my0 + 2 * sy0);
290 Double_t w = scatter->GetBinContent(i, j);
291 Double_t x = ax->GetBinCenter(i), y = ay->GetBinCenter(j);
300 why =
"empty beam core";
303 beam = MomentsToGauss(sw, sx, sy, sxx, syy, sxy, bwx, bwy);
312 Double_t y_lo, Double_t y_hi) {
318 TAxis *ax = scatter->GetXaxis(), *ay = scatter->GetYaxis();
319 const Int_t bx0 = ax->FindBin(x_lo), bx1 = ax->FindBin(x_hi);
320 const Int_t by0 = ay->FindBin(y_lo), by1 = ay->FindBin(y_hi);
321 const Double_t bwx = ax->GetBinWidth(1), bwy = ay->GetBinWidth(1);
324 if (!BeamFromCore(scatter, reac, bx0, bx1, by0, by1, beam, fit.
why))
334 TH1D hx(Form(
"rcf_hx_%d", reac),
"", 80, x_lo, x_hi);
335 hx.SetDirectory(
nullptr);
336 for (Int_t i = bx0; i <= bx1; i++)
337 for (Int_t j = by0; j <= by1; j++) {
338 const Double_t w = scatter->GetBinContent(i, j);
341 const Double_t x = ax->GetBinCenter(i), y = ay->GetBinCenter(j);
343 if (u > 3.0 && u < 15.0 && x > beam.
mx + 3.0 * beam.
sx)
346 const Int_t bpk = hx.GetMaximumBin();
347 const Double_t xpk = hx.GetBinCenter(bpk);
348 if (hx.GetBinContent(bpk) < 5.0) {
349 fit.
why =
"nothing above the ridge beyond +3 sigma_x of the beam";
352 Double_t s[6] = {0, 0, 0, 0, 0, 0};
353 for (Int_t i = bx0; i <= bx1; i++)
354 for (Int_t j = by0; j <= by1; j++) {
355 const Double_t w = scatter->GetBinContent(i, j);
358 const Double_t x = ax->GetBinCenter(i), y = ay->GetBinCenter(j);
360 if (u > 3.0 && u < 15.0 && std::fabs(x - xpk) < 2.0 * beam.
sx) {
370 fit.
why = Form(
"only %.0f events in the seed neighbourhood", s[0]);
374 reac_g.
mx = s[1] / s[0];
375 reac_g.
my = s[2] / s[0];
385 Double_t n_beam = 0.0, n_reac = 0.0;
386 Double_t total = 0.0;
387 for (Int_t i = bx0; i <= bx1; i++)
388 for (Int_t j = by0; j <= by1; j++)
389 total += scatter->GetBinContent(i, j);
390 Double_t prior_reac = 1.0e-3;
391 for (Int_t iter = 0; iter < 20; iter++) {
392 Double_t mb[6] = {0, 0, 0, 0, 0, 0}, mr[6] = {0, 0, 0, 0, 0, 0};
393 const Double_t lp_b = std::log(1.0 - prior_reac) - LogNorm(beam);
394 const Double_t lp_r = std::log(prior_reac) - LogNorm(reac_g);
395 for (Int_t i = bx0; i <= bx1; i++)
396 for (Int_t j = by0; j <= by1; j++) {
397 const Double_t w = scatter->GetBinContent(i, j);
400 const Double_t x = ax->GetBinCenter(i), y = ay->GetBinCenter(j);
401 const Double_t d2r = Mahal2(reac_g, x, y);
407 const Bool_t to_reac =
409 (lp_r - 0.5 * d2r) > (lp_b - 0.5 * Mahal2(beam, x, y));
410 Double_t *m = to_reac ? mr : mb;
419 fit.
why = Form(
"reaction component starved (%.0f events) at iteration %d",
424 MomentsToGauss(mb[0], mb[1], mb[2], mb[3], mb[4], mb[5], bwx, bwy);
426 MomentsToGauss(mr[0], mr[1], mr[2], mr[3], mr[4], mr[5], bwx, bwy);
434 nr.
sx = TMath::Min(nr.
sx, 1.1 * nb.
sx);
435 nr.
sy = TMath::Min(nr.
sy, 1.1 * nb.
sy);
439 if (std::fabs(nr.
rho) > std::fabs(nb.
rho))
440 nr.
rho = nr.
rho < 0 ? -std::fabs(nb.
rho) : std::fabs(nb.
rho);
441 const Bool_t moved = std::fabs(nr.
mx - reac_g.
mx) > 0.1 * bwx ||
442 std::fabs(nr.
my - reac_g.
my) > 0.1 * bwy;
447 prior_reac = TMath::Max(1.0e-6, n_reac / total);
455 fit.
why =
"reaction component fell back onto the ridge";
459 fit.
why = Form(
"reaction component holds only %.0f events", n_reac);
469 return ((y - b.
my) - b.
rho * (b.
sy / b.
sx) * (x - b.
mx)) /
470 (b.
sy * std::sqrt(1.0 - b.
rho * b.
rho));
474 Double_t y_lo, Double_t y_hi) {
481 TAxis *ax = scatter->GetXaxis(), *ay = scatter->GetYaxis();
482 if (!BeamFromCore(scatter, reac, ax->FindBin(x_lo), ax->FindBin(x_hi),
483 ay->FindBin(y_lo), ay->FindBin(y_hi), fit.
beam, fit.
why))
492 Double_t nsig_hi, Double_t x_lo, Double_t x_hi,
493 Double_t y_lo, Double_t y_hi) {
495 auto edge = [&](Double_t x, Double_t n) {
496 const Double_t y = beam.
my +
497 beam.
rho * (beam.
sy / beam.
sx) * (x - beam.
mx) +
498 n * beam.
sy * std::sqrt(1.0 - beam.
rho * beam.
rho);
499 return TMath::Max(y_lo, TMath::Min(y_hi, y));
501 TCutG *c =
new TCutG(name, 5);
502 c->SetPoint(0, x_lo, edge(x_lo, nsig_lo));
503 c->SetPoint(1, x_hi, edge(x_hi, nsig_lo));
504 c->SetPoint(2, x_hi, edge(x_hi, nsig_hi));
505 c->SetPoint(3, x_lo, edge(x_lo, nsig_hi));
506 c->SetPoint(4, x_lo, edge(x_lo, nsig_lo));
507 c->SetLineColor(kBlack);
515 TCutG *c =
new TCutG(name, npts + 1);
516 const Double_t q = std::sqrt(1.0 - g.
rho * g.
rho);
517 for (Int_t i = 0; i <= npts; i++) {
518 Double_t t = 2.0 * TMath::Pi() * i / npts;
519 Double_t u = std::cos(t), v = std::sin(t);
520 c->SetPoint(i, g.
mx + nsigma * g.
sx * u,
521 g.
my + nsigma * g.
sy * (g.
rho * u + q * v));
523 c->SetLineColor(kBlack);
531 TAxis *ax = scatter->GetXaxis(), *ay = scatter->GetYaxis();
533 for (Int_t i = ax->FindBin(fit.
x_lo); i <= ax->FindBin(fit.
x_hi); i++)
534 for (Int_t j = ay->FindBin(fit.
y_lo); j <= ay->FindBin(fit.
y_hi); j++)
535 if (cut->IsInside(ax->GetBinCenter(i), ay->GetBinCenter(j)))
536 n += scatter->GetBinContent(i, j);
541 TCutG *aa,
const TString &subdir) {
542 scatter->GetXaxis()->SetRangeUser(fit.
x_lo, fit.
x_hi);
543 scatter->GetYaxis()->SetRangeUser(fit.
y_lo, fit.
y_hi);
544 const Int_t cReac = kRed + 1, cBeam = kAzure + 1;
550 TCanvas *c = PlottingUtils::GetConfiguredCanvas(kFALSE);
552 PlottingUtils::ConfigureAndDraw2DHistogram(
554 Form(
"reac %d: fitted components (dashed 1,2,3#sigma) and regions "
557 std::vector<TCutG *> tmp;
558 for (Int_t k = 1; k <= 3; k++) {
560 eb->SetLineColor(cBeam);
568 er->SetLineColor(cReac);
575 aa->SetFillColorAlpha(cBeam, 0.15);
576 aa->SetLineColor(cBeam);
581 an->SetFillColorAlpha(cReac, 0.25);
582 an->SetLineColor(cReac);
587 PlottingUtils::SaveFigure(c, Form(
"regions_reac%d", reac), subdir,
588 PlotSaveOptions::kLINEAR);
590 for (Int_t k = 0; k < Int_t(tmp.size()); k++)
595 TH2F *model = ModelHist(scatter, fit, reac);
597 TCanvas *c = PlottingUtils::GetConfiguredCanvas(kFALSE);
599 PlottingUtils::ConfigureAndDraw2DHistogram(
601 Form(
"reac %d: data with the fitted mixture's density", reac));
602 model->SetContour(10);
603 model->SetLineColor(cReac);
604 model->SetLineWidth(1);
605 model->Draw(
"CONT3 SAME");
606 PlottingUtils::SaveFigure(c, Form(
"regions_reac%d_model", reac), subdir,
607 PlotSaveOptions::kLINEAR);
612 TAxis *ax = scatter->GetXaxis(), *ay = scatter->GetYaxis();
613 const Int_t bx0 = ax->FindBin(fit.
x_lo), bx1 = ax->FindBin(fit.
x_hi);
614 const Int_t by0 = ay->FindBin(fit.
y_lo), by1 = ay->FindBin(fit.
y_hi);
615 for (Int_t which = 0; which < 2; which++) {
616 TH1D *d = which == 0 ? scatter->ProjectionX(Form(
"dpx_%d", reac), by0, by1)
617 : scatter->ProjectionY(Form(
"dpy_%d", reac), bx0, bx1);
618 TH1D *m = which == 0 ? model->ProjectionX(Form(
"mpx_%d", reac), by0, by1)
619 : model->ProjectionY(Form(
"mpy_%d", reac), bx0, bx1);
620 d->SetDirectory(
nullptr);
621 m->SetDirectory(
nullptr);
622 d->GetXaxis()->SetRange(which == 0 ? bx0 : by0, which == 0 ? bx1 : by1);
623 TCanvas *c = PlottingUtils::GetConfiguredCanvas(kTRUE);
624 PlottingUtils::ConfigureAndDrawHistogram(
626 Form(
"reac %d: %s projection, data (black) and fitted mixture (red)",
627 reac, which == 0 ?
"x" :
"y"));
628 m->SetLineColor(cReac);
630 m->Draw(
"HIST SAME");
631 PlottingUtils::SaveFigure(
632 c, Form(
"regions_reac%d_proj%s", reac, which == 0 ?
"x" :
"y"), subdir,
633 PlotSaveOptions::kLOG);
static TString ResultsDir()
Absolute path to the directory receiving generated output.
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.
TCutG * RidgeBandCut(const char *name, const Gauss2D &beam, Double_t nsig_lo, Double_t nsig_hi, Double_t x_lo, Double_t x_hi, Double_t y_lo, Double_t y_hi)
Closed polygon of a band above the beam ridge.
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.
RegionFit FitBeam(TH2F *scatter, Int_t reac, Double_t x_lo, Double_t x_hi, Double_t y_lo, Double_t y_hi)
Fit the beam component alone, with no reaction component.
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.
TCutG * LoadDrawn(const char *name, Int_t reac)
Load only a hand-drawn cut, never a fitted one.
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.
Bool_t LoadFit(Int_t reac, RegionFit &fit)
Read back a stored fit.
TString Dir()
Directory holding the region cut files.
TCutG * Load(const char *name, Int_t reac)
Load a cut, from either storage generation.
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 rho
x-y correlation, in [-1, 1].
Result of fitting the two-component mixture to one strip's scatter.
Double_t n_beam
Events the mixture attributes to each component.
Bool_t has_reac
kFALSE for a beam-only fit, as used by the ridge-band region mode.
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.