4#include "InitUtils.hpp"
6#include "PlottingUtils.hpp"
12#include <TFeldmanCousins.h>
15#include <TGraphAsymmErrors.h>
16#include <TGraphErrors.h>
27#include <TParameter.h>
39const Double_t kBoltzmann = 1.380649e-23;
40const Double_t kPaPerTorr = 133.322368;
43const Double_t kStripLengthCm = 1.578;
44const Double_t kGasTemperatureK = 293.0;
46const Double_t kCm2PerMb = 1.0e-27;
51 return tag.Length() > 0 ?
"_" + tag : TString(
"");
56Bool_t CurveAt(
const TGraph *g, Double_t e, Double_t &v) {
57 for (Int_t p = 1; p < g->GetN(); p++) {
58 const Double_t x0 = g->GetX()[p - 1], x1 = g->GetX()[p];
59 if (!(x0 < e && e < x1))
61 const Double_t y0 = g->GetY()[p - 1], y1 = g->GetY()[p];
62 const Double_t t = (e - x0) / (x1 - x0);
63 v = (y0 > 0.0 && y1 > 0.0)
64 ? std::exp(std::log(y0) + t * (std::log(y1) - std::log(y0)))
72const Int_t kChannelMarker[4] = {20, 21, 22, 23};
73const Int_t kChannelColor[4] = {kBlack, kGreen + 2, kMagenta + 2, kOrange + 7};
76const Int_t kModelColor[8] = {kAzure + 1, kRed + 1, kGreen + 2, kOrange + 7,
77 kMagenta + 1, kCyan + 2, kGray + 2, kViolet - 3};
79const Int_t kComponentColor[4] = {kBlack, kBlue + 1, kGreen + 2, kMagenta + 1};
83Double_t CrossSection::Point::ErrLo()
const {
84 return std::sqrt(stat_lo * stat_lo + sys * sys);
87Double_t CrossSection::Point::ErrHi()
const {
88 return std::sqrt(stat_hi * stat_hi + sys * sys);
91void CrossSection::PoissonInterval(Double_t count, Double_t &below,
93 const Int_t n = Int_t(std::lround(TMath::Max(count, 0.0)));
94 if (n >=
Constants::cfg.CROSS_SECTION_CONFIG.FELDMAN_COUSINS_MAX_COUNT) {
95 below = above = std::sqrt(count);
100 TFeldmanCousins fc(0.6827);
101 fc.SetMuMax(n + 10.0 * std::sqrt(n + 1.0) + 10.0);
103 const Double_t hi = fc.CalculateUpperLimit(n, 0.0);
104 const Double_t lo = fc.GetLowerLimit();
113 Int_t a_beam, Int_t &z, Int_t &a) {
114 Int_t dz = 0, da = 0, mult = 0;
115 for (Int_t i = 0; i < exit.Length(); i++) {
116 const char c = exit[i];
117 if (c >=
'0' && c <=
'9') {
118 mult = mult * 10 + (c -
'0');
121 Int_t pz = 0, pa = 0;
154 const Int_t m = mult > 0 ? mult : 1;
159 if (mult > 0 || exit.Length() == 0)
163 return z > 0 && a > z;
167 if (ch.
label.Length() > 0)
171 Bool_t all_neutron = kTRUE;
172 for (Int_t k = 0; k < Int_t(ch.
talys_exits.size()); k++) {
174 e.ReplaceAll(
"n",
"");
175 for (Int_t i = 0; i < e.Length(); i++)
176 if (e[i] <
'0' || e[i] >
'9')
177 all_neutron = kFALSE;
182 return "(#alpha, xn)";
184 for (Int_t k = 0; k < Int_t(ch.
talys_exits.size()); k++)
186 return "(#alpha, " + joined +
")";
195 return "(#alpha, " + joined +
")";
198Long64_t CrossSection::ReadCount(TFile &f,
const char *name, Bool_t &ok) {
199 TParameter<Long64_t> *p =
static_cast<TParameter<Long64_t> *
>(f.Get(name));
209Double_t CrossSection::Interpolated(TGraph *g, Double_t e) {
210 if (!g || g->GetN() < 2)
212 for (Int_t p = 1; p < g->GetN(); p++) {
213 const Double_t x0 = g->GetX()[p - 1], x1 = g->GetX()[p];
214 if (!(x0 <= e && e <= x1))
216 const Double_t y0 = g->GetY()[p - 1], y1 = g->GetY()[p];
217 const Double_t t = (e - x0) / (x1 - x0);
218 return (y0 > 0.0 && y1 > 0.0)
219 ? std::exp(std::log(y0) + t * (std::log(y1) - std::log(y0)))
220 : y0 + t * (y1 - y0);
226TGraph *CrossSection::Clipped(TGraph *g, Double_t e_lo, Double_t e_hi) {
229 std::vector<Double_t> x, y;
231 if (CurveAt(g, e_lo, v)) {
235 for (Int_t p = 0; p < g->GetN(); p++)
236 if (g->GetX()[p] >= e_lo && g->GetX()[p] <= e_hi) {
237 x.push_back(g->GetX()[p]);
238 y.push_back(g->GetY()[p]);
240 if (CurveAt(g, e_hi, v)) {
244 return x.empty() ? nullptr :
new TGraph(Int_t(x.size()), &x[0], &y[0]);
261Double_t CrossSection::EffectiveEnergy(TGraph *axn, Double_t e_out,
263 const Double_t mid = 0.5 * (e_out + e_in);
264 if (!axn || axn->GetN() < 2)
266 const Double_t lo = TMath::Min(e_out, e_in), hi = TMath::Max(e_out, e_in);
267 if (lo < axn->GetX()[0] || hi > axn->GetX()[axn->GetN() - 1])
269 const Double_t s1 = axn->Eval(hi), s2 = axn->Eval(lo);
270 if (s1 < 0.0 || s2 < 0.0 || s1 + s2 <= 0.0)
272 const Double_t frac =
273 (s1 + s2) / (2.0 * (TMath::Sqrt(0.5 * (s1 * s1 + s2 * s2)) + s2));
274 return lo + frac * (hi - lo);
279TCutG *CrossSection::ScaledCut(TCutG *cut, Double_t scale) {
280 Double_t cx = 0.0, cy = 0.0;
281 for (Int_t p = 0; p < cut->GetN(); p++) {
282 cx += cut->GetX()[p];
283 cy += cut->GetY()[p];
285 cx /= Double_t(cut->GetN());
286 cy /= Double_t(cut->GetN());
287 TCutG *scaled =
new TCutG(*cut);
288 for (Int_t p = 0; p < scaled->GetN(); p++)
289 scaled->SetPoint(p, cx + scale * (cut->GetX()[p] - cx),
290 cy + scale * (cut->GetY()[p] - cy));
299Double_t CrossSection::CountInCut(TH2F *scatter, TCutG *cut, Double_t scale) {
300 TCutG *scaled = ScaledCut(cut, scale);
301 TAxis *ax = scatter->GetXaxis();
302 TAxis *ay = scatter->GetYaxis();
304 for (Int_t i = 1; i <= scatter->GetNbinsX(); i++)
305 for (Int_t j = 1; j <= scatter->GetNbinsY(); j++)
306 if (scaled->IsInside(ax->GetBinCenter(i), ay->GetBinCenter(j)))
307 n += scatter->GetBinContent(i, j);
313Double_t CrossSection::Enclosed(Double_t nsigma) {
314 return 1.0 - std::exp(-0.5 * nsigma * nsigma);
317Double_t CrossSection::GasPressureSys(Double_t sigma)
const {
326Double_t CrossSection::CutVariation(Int_t reac, Double_t per_count,
327 TString &detail)
const {
329 Bool_t at_ok = kTRUE;
330 const Long64_t n_nom = ReadCount(*cache_, Form(
"n_tagged_r%d", reac), at_ok);
332 const Long64_t d_nom = ReadCount(*cache_, Form(
"n_normed_r%d", reac), d_ok);
333 TNamed *vn =
static_cast<TNamed *
>(cache_->Get(
"cut_variants"));
334 if (!at_ok || !d_ok || d_nom <= 0 || !vn)
336 const Double_t s_nom = Double_t(n_nom) / Double_t(d_nom) * per_count;
337 std::map<TString, Double_t> worst;
338 std::map<TString, TString> sides;
339 TString names = vn->GetTitle(), tok;
341 while (names.Tokenize(tok, from,
",")) {
346 ReadCount(*cache_, Form(
"n_tagged_r%d_%s", reac, tok.Data()), ok);
351 Bool_t dv_ok = kTRUE;
353 ReadCount(*cache_, Form(
"n_normed_r%d_%s", reac, tok.Data()), dv_ok);
354 const Double_t denom = dv_ok && d_v > 0 ? Double_t(d_v) : Double_t(d_nom);
355 const TString base = tok(0, tok.Length() - 1);
356 const Double_t ds = Double_t(n_v) / denom * per_count - s_nom;
357 const Double_t d = std::fabs(ds);
360 sides[base] += Form(
"%s%s %+.1f", sides[base].IsNull() ?
"" :
", ",
361 tok.EndsWith(
"+") ?
"up" :
"down", ds);
364 for (std::map<TString, Double_t>::const_iterator it = worst.begin();
365 it != worst.end(); ++it) {
366 var2 += it->second * it->second;
367 detail += Form(
" %s[%s]", it->first.Data(), sides[it->first].Data());
369 return std::sqrt(var2);
372Bool_t CrossSection::LoadCache() {
375 cache_ =
new TFile(path,
"READ");
376 if (cache_->IsZombie()) {
377 std::cerr <<
"cross-section: no scatter cache at " << path
378 <<
"; run strip-sum-scatter first" << std::endl;
382 n_seen_ = ReadCount(*cache_,
"n_seen", have);
383 n_beam_ = ReadCount(*cache_,
"n_normed", have);
384 if (!have || n_beam_ <= 0) {
385 std::cerr <<
"cross-section: the cache carries no normalization counts, so "
386 "it predates them; re-run strip-sum-scatter to refill"
393 areal_ = n_gas_ * kStripLengthCm;
401Bool_t CrossSection::LoadBeam() {
403 std::cerr <<
"cross-section: cannot read the simulated beam at "
408 Double_t e = e_strip0_;
409 for (Int_t s = 0; s <= 16; s++) {
410 e_mid_[s] = (e - 0.5 * dE_[s]) * cm_frac_;
421void CrossSection::LoadTalys() {
424 if (gSystem->AccessPathName(path))
426 TFile f(path,
"READ");
429 for (Int_t m = 0;; m++) {
430 TDirectory *d = f.GetDirectory(Form(
"m%d", m));
433 std::map<std::pair<Int_t, Int_t>, TGraph *> graphs;
434 for (TIter it(d->GetListOfKeys()); TObject *k = it();) {
436 if (sscanf(k->GetName(),
"rp%3d%3d", &z, &a) != 2)
438 TGraph *g =
dynamic_cast<TGraph *
>(d->Get(k->GetName()));
440 graphs[std::make_pair(z, a)] =
static_cast<TGraph *
>(g->Clone());
442 TNamed *label =
dynamic_cast<TNamed *
>(d->Get(
"label"));
443 talys_labels_.push_back(m < Int_t(X.
TALYS_MODELS.size())
445 : label ? TString(label->GetTitle())
446 : TString(Form(
"TALYS model %d", m)));
447 talys_raw_.push_back(graphs);
451std::vector<CrossSection::TalysCurve>
452CrossSection::ExitCurves(
const CrossSectionChannel &ch,
453 const std::vector<TString> &exits,
454 const char *what)
const {
456 std::vector<TalysCurve> curves;
457 if (talys_raw_.empty() || exits.empty())
459 for (Int_t m = 0; m < Int_t(talys_raw_.size()); m++) {
460 std::map<Double_t, Double_t> sum;
461 TString used, missing;
462 for (Int_t k = 0; k < Int_t(exits.size()); k++) {
465 std::map<std::pair<Int_t, Int_t>, TGraph *>::const_iterator it =
466 talys_raw_[m].find(std::make_pair(z, a));
467 if (it == talys_raw_[m].end()) {
468 missing += Form(
" %s(Z=%d,A=%d)", exits[k].Data(), z, a);
471 for (Int_t p = 0; p < it->second->GetN(); p++)
472 sum[it->second->GetX()[p]] += it->second->GetY()[p];
473 used += Form(
" %s(Z=%d,A=%d)", exits[k].Data(), z, a);
475 std::vector<Double_t> tx, ty;
476 for (std::map<Double_t, Double_t>::const_iterator it = sum.begin();
477 it != sum.end(); ++it)
478 if (it->second > 0.0) {
479 tx.push_back(it->first);
480 ty.push_back(it->second);
482 std::cout <<
"cross-section: " << ch.
name <<
": " << talys_labels_[m]
483 <<
": summed " << what << (used.Length() ? used :
" nothing")
484 << (missing.Length() ?
"; not in the file:" + missing :
"")
489 c.label = talys_labels_[m];
490 c.axn =
new TGraph(Int_t(tx.size()), &tx[0], &ty[0]);
496Double_t CrossSection::StripAverage(TGraph *g, Double_t e_out, Double_t e_in,
498 const Double_t lo = TMath::Min(e_out, e_in), hi = TMath::Max(e_out, e_in);
499 covered = g && g->GetN() >= 2 && lo >= g->GetX()[0] &&
500 hi <= g->GetX()[g->GetN() - 1];
501 const Int_t n_sub = 200;
503 for (Int_t k = 0; k <= n_sub; k++) {
504 const Double_t e = lo + (hi - lo) * Double_t(k) / Double_t(n_sub);
505 sum += ((k == 0 || k == n_sub) ? 0.5 : 1.0) * Interpolated(g, e);
507 return sum / Double_t(n_sub);
511CrossSection::SubtractedFraction(
const std::vector<TalysCurve> &talys,
512 const std::vector<TalysCurve> &talys_sub,
513 Double_t e_out, Double_t e_in,
516 if (talys_sub.empty() || talys.empty())
518 Double_t sum = 0.0, lo = 1.0e9, hi = -1.0e9;
519 Int_t used = 0, skipped = 0;
521 for (Int_t ms = 0; ms < Int_t(talys_sub.size()); ms++) {
522 const TalysCurve *chan =
nullptr;
523 for (Int_t m = 0; m < Int_t(talys.size()); m++)
524 if (talys[m].label == talys_sub[ms].label) {
532 Bool_t cov_chan = kFALSE, cov_drop = kFALSE;
533 const Double_t keep = StripAverage(chan->axn, e_out, e_in, cov_chan);
534 const Double_t drop =
535 StripAverage(talys_sub[ms].axn, e_out, e_in, cov_drop);
536 if (!cov_chan || !cov_drop) {
540 if (!(keep + drop > 0.0))
542 const Double_t f = drop / (keep + drop);
544 lo = TMath::Min(lo, f);
545 hi = TMath::Max(hi, f);
550 std::cout << Form(
" no model both has the exits to subtract and "
551 "covers [%.2f, %.2f] MeV: nothing subtracted",
552 TMath::Min(e_out, e_in), TMath::Max(e_out, e_in))
556 spread = used > 1 ? 0.5 * (hi - lo) : 0.0;
557 return sum / Double_t(used);
560Bool_t CrossSection::Strip(
const CrossSectionChannel &ch,
561 const std::vector<TalysCurve> &talys,
562 const std::vector<TalysCurve> &talys_sub, Int_t reac,
565 const TString region =
"region_" + ch.
name;
566 TH2F *h =
static_cast<TH2F *
>(cache_->Get(Form(
"scatter_r%d", reac)));
569 std::cout << Form(
" %2d no %s", reac,
570 h ?
"region cut" :
"scatter in the cache")
578 Bool_t at_ok = kTRUE;
579 const Long64_t n_at = ReadCount(*cache_, Form(
"n_normed_r%d", reac), at_ok);
580 pt.n_denom = Double_t(at_ok && n_at > 0 ? n_at : n_beam_);
584 Double_t e_lab = e_strip0_;
585 for (Int_t s = 0; s < reac; s++)
587 pt.e_in = e_lab * cm_frac_;
588 pt.e_out = (e_lab - dE_[reac]) * cm_frac_;
590 pt.e_eff = use_eff ? EffectiveEnergy(talys.empty() ?
nullptr : talys[0].axn,
592 : 0.5 * (pt.e_in + pt.e_out);
593 pt.e_eff_lo = pt.e_eff_hi = pt.e_eff;
594 for (Int_t m = 1; use_eff && m < Int_t(talys.size()); m++) {
595 const Double_t e = EffectiveEnergy(talys[m].axn, pt.e_out, pt.e_in);
596 pt.e_eff_lo = TMath::Min(pt.e_eff_lo, e);
597 pt.e_eff_hi = TMath::Max(pt.e_eff_hi, e);
599 if (use_eff && talys.size() > 1)
600 std::cout << Form(
" E_cm,eff %.3f (midpoint %.3f); across the %zu "
601 "model shapes %.3f..%.3f -> energy systematic "
603 pt.e_eff, 0.5 * (pt.e_in + pt.e_out), talys.size(),
604 pt.e_eff_lo, pt.e_eff_hi, pt.e_eff_hi - pt.e_eff,
605 pt.e_eff - pt.e_eff_lo)
608 const Double_t norm = pt.n_denom * areal_ * kCm2PerMb;
609 const Bool_t all_tagged =
613 const Double_t n_raw = CountInCut(h, cut, 1.0);
616 SubtractedFraction(talys, talys_sub, pt.e_out, pt.e_in, pt.sub_frac_err);
617 const Double_t n_kept = n_raw * (1.0 - pt.sub_frac);
618 if (pt.sub_frac > 0.0)
619 std::cout << Form(
" %s: TALYS puts %.2f%% +- %.2f%% of what is "
620 "tagged here in it, so %.0f of %.0f events are removed",
622 100.0 * pt.sub_frac_err, n_raw - n_kept, n_raw)
628 pt.sigma = pt.n_reac / norm;
630 Double_t below = 0.0, above = 0.0;
631 PoissonInterval(n_raw, below, above);
632 pt.stat_lo = below * (1.0 - pt.sub_frac) / norm;
633 pt.stat_hi = above * (1.0 - pt.sub_frac) / norm;
635 const Double_t sys_cuts =
636 CutVariation(reac, (1.0 - pt.sub_frac) / (areal_ * kCm2PerMb), detail);
637 const Double_t sys_gas = GasPressureSys(pt.sigma);
639 const Double_t sys_sub = pt.sub_frac_err * n_raw / norm;
641 std::sqrt(sys_cuts * sys_cuts + sys_gas * sys_gas + sys_sub * sys_sub);
642 if (pt.sub_frac > 0.0)
643 std::cout << Form(
" subtraction systematic %.2f mb (%.1f%% of "
644 "the point), against %.2f mb cut variation and %.2f "
647 pt.sigma > 0.0 ? 100.0 * sys_sub / pt.sigma : 0.0,
650 std::cout << Form(
" %2d [%5.2f, %5.2f] %6.2f %6.0f %9.0f "
651 "%7.1f +%.1f -%.1f (+%.1f -%.1f stat%s, %.1f cut "
652 "variation, %.2f gas pressure%s; all tagged events "
654 reac, pt.e_in, pt.e_out, pt.e_eff, pt.n_reac, pt.n_denom,
655 pt.sigma, pt.ErrHi(), pt.ErrLo(), pt.stat_hi, pt.stat_lo,
661 pt.sub_frac > 0.0 ? Form(
", %.2f subtraction", sys_sub)
663 detail.IsNull() ?
"; no variation counts in the cache"
664 :
"; per condition, change of sigma "
665 "with its threshold up (+) and "
673 const Double_t keep = 1.0 - pt.sub_frac;
676 const Bool_t have_fit = n_assigned >= 0.0;
677 const Double_t est_fit =
678 have_fit ? n_assigned * keep / Enclosed(3.0) : est_geo;
680 pt.sigma = pt.n_reac / norm;
684 const Double_t counted = have_fit ? n_assigned : n_raw;
685 const Double_t scale = counted > 0.0 ? pt.sigma / counted : 0.0;
686 Double_t below = 0.0, above = 0.0;
687 PoissonInterval(counted, below, above);
688 pt.stat_lo = below * scale;
689 pt.stat_hi = above * scale;
694 const Double_t est_core =
696 pt.sys = std::fabs(est_core - est_fit) / norm;
700 Double_t lo = pt.sigma, hi = pt.sigma;
701 const Double_t kScale[2] = {0.75, 1.25};
702 for (Int_t k = 0; k < 2; k++) {
703 const Double_t s = keep * CountInCut(h, cut, kScale[k]) /
705 lo = TMath::Min(lo, s);
706 hi = TMath::Max(hi, s);
708 pt.sys = 0.5 * (hi - lo);
711 const Double_t sys_sub =
712 keep > 0.0 ? pt.sub_frac_err / keep * pt.sigma : 0.0;
713 pt.sys = std::sqrt(pt.sys * pt.sys +
714 GasPressureSys(pt.sigma) * GasPressureSys(pt.sigma) +
717 std::cout << Form(
" %2d [%5.2f, %5.2f] %6.2f %6.0f %9.0f "
718 "%7.1f +%.1f -%.1f (+%.1f -%.1f stat, %.1f region; "
719 "in-region %.0f, attributed %s)",
720 reac, pt.e_in, pt.e_out, pt.e_eff, pt.n_reac, pt.n_denom,
721 pt.sigma, pt.ErrHi(), pt.ErrLo(), pt.stat_hi, pt.stat_lo,
722 pt.sys, est_geo, have_fit ? Form(
"%.0f", est_fit) :
"none")
733void CrossSection::CompareReference(
const ChannelResult &r)
const {
734 const std::vector<std::vector<Double_t>> &ref = r.ch->reference_xs;
737 const Int_t n_ref = Int_t(ref.size());
739 Double_t best_rms = 1.0e9;
740 for (Int_t off = 0; off + n_ref - 1 <= 16; off++) {
742 for (Int_t k = 0; k < n_ref; k++) {
743 const Double_t d = e_mid_[off + k] - ref[k][0];
746 const Double_t rms = std::sqrt(s / Double_t(n_ref));
747 if (rms < best_rms) {
752 std::cout << std::endl;
753 std::cout << Form(
" %s vs %s: first row is strip %d from the energies, rms "
755 r.ch->name.Data(), r.ch->reference_label.Data(), best_off,
758 std::cout <<
" strip E_cm,eff published E this work [mb] "
759 "published [mb] ratio"
761 for (Int_t i = 0; i < Int_t(r.points.size()); i++) {
762 const Point &pt = r.points[i];
763 const Int_t row = pt.reac - best_off;
764 if (row < 0 || row >= n_ref) {
765 std::cout << Form(
" %2d %6.2f -- %7.1f "
766 " -- (upstream of the published range)",
767 pt.reac, pt.e_eff, pt.sigma)
771 const Double_t v = ref[row][3];
772 std::cout << Form(
" %2d %6.2f %6.2f %7.1f +%-5.1f -%-5.1f "
773 "%7.1f (%.1f) %5.2f",
774 pt.reac, pt.e_eff, ref[row][0], pt.sigma, pt.ErrHi(),
775 pt.ErrLo(), v, ref[row][4], v > 0.0 ? pt.sigma / v : 0.0)
784void CrossSection::Draw(
const std::vector<const ChannelResult *> &rs,
785 const TString &name)
const {
786 Double_t fx_lo = 1.0e9, fx_hi = -1.0e9, fy_lo = 1.0e9, fy_hi = -1.0e9;
788 TGraphAsymmErrors *g;
791 std::vector<Series> measured, published;
792 std::vector<std::pair<TGraph *, TString>> curves;
793 for (Int_t c = 0; c < Int_t(rs.size()); c++) {
794 const ChannelResult &r = *rs[c];
795 std::vector<Double_t> vx, vy, vexl, vexh, veyl, veyh;
796 for (Int_t i = 0; i < Int_t(r.points.size()); i++) {
797 const Point &pt = r.points[i];
800 vx.push_back(pt.e_eff);
801 vy.push_back(pt.sigma);
802 vexl.push_back(std::hypot(pt.e_eff - TMath::Min(pt.e_in, pt.e_out),
803 pt.e_eff - pt.e_eff_lo));
804 vexh.push_back(std::hypot(TMath::Max(pt.e_in, pt.e_out) - pt.e_eff,
805 pt.e_eff_hi - pt.e_eff));
806 veyl.push_back(pt.ErrLo());
807 veyh.push_back(pt.ErrHi());
808 fx_lo = TMath::Min(fx_lo, vx.back() - vexl.back());
809 fx_hi = TMath::Max(fx_hi, vx.back() + vexh.back());
810 fy_lo = TMath::Min(fy_lo, vy.back());
811 fy_hi = TMath::Max(fy_hi, vy.back());
816 s.g =
new TGraphAsymmErrors(Int_t(vx.size()), &vx[0], &vy[0], &vexl[0],
817 &vexh[0], &veyl[0], &veyh[0]);
818 s.g->SetMarkerStyle(kChannelMarker[c % 4]);
819 s.g->SetMarkerColor(kChannelColor[c % 4]);
820 s.g->SetLineColor(kChannelColor[c % 4]);
821 s.label = rs.size() > 1 ?
"Present Work " +
Label(*r.ch) :
"Present Work";
822 measured.push_back(s);
823 const std::vector<std::vector<Double_t>> &ref = r.ch->reference_xs;
825 std::vector<Double_t> rx, ry, rexl, rexh, rey;
826 for (Int_t k = 0; k < Int_t(ref.size()); k++) {
827 rx.push_back(ref[k][0]);
828 rexh.push_back(ref[k][1]);
829 rexl.push_back(ref[k][2]);
830 ry.push_back(ref[k][3]);
831 rey.push_back(ref[k][4]);
832 fx_lo = TMath::Min(fx_lo, ref[k][0]);
833 fx_hi = TMath::Max(fx_hi, ref[k][0]);
834 fy_lo = TMath::Min(fy_lo, ref[k][3]);
835 fy_hi = TMath::Max(fy_hi, ref[k][3]);
838 p.g =
new TGraphAsymmErrors(Int_t(rx.size()), &rx[0], &ry[0], &rexl[0],
839 &rexh[0], &rey[0], &rey[0]);
840 p.g->SetMarkerStyle(24 + (c % 4));
841 p.g->SetMarkerColor(kRed + 1);
842 p.g->SetLineColor(kRed + 1);
843 p.label = rs.size() > 1 ? r.ch->reference_label +
" " +
Label(*r.ch)
844 : r.ch->reference_label;
845 published.push_back(p);
849 for (Int_t m = 0; m < Int_t(r.talys.size()); m++) {
850 if (rs.size() > 1 && m > 0)
852 curves.push_back(std::make_pair(
853 r.talys[m].axn, rs.size() > 1 ? r.talys[m].label +
" " +
Label(*r.ch)
854 : r.talys[m].label));
857 if (measured.empty())
859 TCanvas *c = PlottingUtils::GetConfiguredCanvas(kTRUE);
861 c->DrawFrame(fx_lo - 0.4, 0.5 * fy_lo, fx_hi + 0.4, 3.0 * fy_hi);
863 rs.size() == 1 ?
Label(*rs[0]->ch).Data() :
"",
869 std::vector<std::pair<TGraph *, TString>> drawn;
870 for (Int_t k = 0; k < Int_t(curves.size()); k++) {
871 TGraph *g = Clipped(curves[k].first, fx_lo - 0.4, fx_hi + 0.4);
876 const Int_t ch = rs.size() > 1 ? k : 0;
877 g->SetLineColor(rs.size() > 1 ? kChannelColor[ch % 4] : kModelColor[k % 8]);
881 drawn.push_back(std::make_pair(g, curves[k].second));
883 for (Int_t k = 0; k < Int_t(published.size()); k++)
884 published[k].g->Draw(
"P SAME");
885 for (Int_t k = 0; k < Int_t(measured.size()); k++)
886 measured[k].g->Draw(
"P SAME");
889 const Int_t n_entries =
890 Int_t(measured.size() + published.size() + drawn.size());
892 PlottingUtils::AddLegend(0.42, 0.89, 0.16, 0.18 + 0.048 * n_entries);
893 for (Int_t k = 0; k < Int_t(measured.size()); k++)
894 leg->AddEntry(measured[k].g, measured[k].label,
"pe");
895 for (Int_t k = 0; k < Int_t(published.size()); k++)
896 leg->AddEntry(published[k].g, published[k].label,
"pe");
897 for (Int_t k = 0; k < Int_t(drawn.size()); k++)
898 leg->AddEntry(drawn[k].first, drawn[k].second,
"l");
902 PlottingUtils::SaveFigure(c, name,
"cross_section", PlotSaveOptions::kLOG);
906void CrossSection::StampPreliminary(
const TPad *pad) {
911 TLatex *stamp =
new TLatex();
913 stamp->SetTextAlign(13);
914 stamp->SetTextSize(0.05);
915 stamp->SetTextFont(62);
916 stamp->SetTextColor(kRed + 1);
917 stamp->DrawLatex(pad->GetLeftMargin() + 0.02,
918 1.0 - pad->GetTopMargin() - 0.02,
"PRELIMINARY");
921Bool_t CrossSection::CollectFitComponents(
const ChannelResult &r, Int_t m,
922 FitComponents &comp)
const {
924 const CrossSectionChannel &ch = *r.ch;
925 for (Int_t k = 0; k < Int_t(ch.
talys_exits.size()); k++) {
928 std::map<std::pair<Int_t, Int_t>, TGraph *>::const_iterator it =
929 talys_raw_[m].find(std::make_pair(z, a));
930 if (it == talys_raw_[m].end())
932 comp.graph.push_back(it->second);
933 comp.label.push_back(
"(#alpha, " + ch.
talys_exits[k] +
")");
936 comp.n_keep = comp.Size();
941 std::map<std::pair<Int_t, Int_t>, TGraph *>::const_iterator it =
942 talys_raw_[m].find(std::make_pair(z, a));
943 if (it == talys_raw_[m].end())
945 comp.graph.push_back(it->second);
946 comp.label.push_back(
"(#alpha, " + ch.
subtract_exits[k] +
") subtracted");
949 if (comp.n_keep == 0) {
950 std::cerr <<
"cross-section: channel " << ch.
name <<
": "
951 << talys_labels_[m] <<
" has none of the channel's exits"
958void CrossSection::FitExitScales(
959 const ChannelResult &r,
const FitComponents &comp, ScaleFit &fs,
960 const std::vector<Double_t> *point_scale)
const {
961 const Int_t nc = comp.Size();
962 const Int_t np = Int_t(r.points.size());
966 for (Int_t k = 0; k < nc; k++) {
968 for (Int_t i = 0; i < np && !any; i++)
969 any = Interpolated(comp.graph[k], r.points[i].e_eff) > 0.0;
971 fs.free_idx.push_back(k);
973 fs.scale.assign(nc, 1.0);
974 fs.scale_err.assign(nc, 0.0);
975 fs.scale_raw.assign(nc, 1.0);
976 fs.scale_raw_err.assign(nc, 0.0);
977 Int_t nf = Int_t(fs.free_idx.size());
978 fs.cov.ResizeTo(nf, nf);
979 Bool_t first_pass = kTRUE;
980 while (nf > 0 && np >= nf) {
981 TMatrixD mat(nf, nf);
983 for (Int_t i = 0; i < np; i++) {
984 const Point &pt = r.points[i];
985 const Double_t k = point_scale ? (*point_scale)[i] : 1.0;
986 const Double_t err = k * 0.5 * (pt.ErrLo() + pt.ErrHi());
989 const Double_t w = 1.0 / (err * err);
990 for (Int_t a = 0; a < nf; a++) {
991 const Double_t ca = Interpolated(comp.graph[fs.free_idx[a]], pt.e_eff);
992 rhs[a] += w * ca * k * pt.sigma;
993 for (Int_t b = 0; b < nf; b++)
995 w * ca * Interpolated(comp.graph[fs.free_idx[b]], pt.e_eff);
998 fs.cov.ResizeTo(nf, nf);
1001 const TVectorD sol = fs.cov * rhs;
1004 for (Int_t a = 0; a < nf; a++) {
1005 fs.scale_raw[fs.free_idx[a]] = sol[a];
1006 fs.scale_raw_err[fs.free_idx[a]] =
1007 std::sqrt(TMath::Max(0.0, fs.cov[a][a]));
1009 first_pass = kFALSE;
1012 for (Int_t a = 0; a < nf; a++)
1013 if (sol[a] < 0.0 && (worst < 0 || sol[a] < sol[worst]))
1016 fs.pinned.push_back(fs.free_idx[worst]);
1017 fs.scale[fs.free_idx[worst]] = 0.0;
1018 fs.free_idx.erase(fs.free_idx.begin() + worst);
1022 for (Int_t a = 0; a < nf; a++) {
1023 fs.scale[fs.free_idx[a]] = sol[a];
1024 fs.scale_err[fs.free_idx[a]] = std::sqrt(TMath::Max(0.0, fs.cov[a][a]));
1029 for (Int_t i = 0; i < np; i++) {
1030 const Point &pt = r.points[i];
1031 const Double_t k = point_scale ? (*point_scale)[i] : 1.0;
1032 const Double_t err = k * 0.5 * (pt.ErrLo() + pt.ErrHi());
1034 for (Int_t c = 0; c < nc; c++)
1035 f += fs.scale[c] * Interpolated(comp.graph[c], pt.e_eff);
1037 fs.chi2 += std::pow((k * pt.sigma - f) / err, 2);
1041void CrossSection::PrintScaleFit(
const CrossSectionChannel &ch, Int_t m,
1042 const FitComponents &comp,
const ScaleFit &fs,
1044 const Int_t nc = comp.Size();
1045 std::cout << std::endl
1046 <<
" " << ch.
name <<
" " <<
Label(ch) <<
": " << talys_labels_[m]
1047 <<
" scaled to this work's points" << std::endl;
1048 for (Int_t k = 0; k < nc; k++) {
1049 const Bool_t is_pinned =
1050 std::find(fs.pinned.begin(), fs.pinned.end(), k) != fs.pinned.end();
1051 std::cout << Form(
" %-26s scale %.4f +- %.4f%s", comp.label[k].Data(),
1052 fs.scale[k], fs.scale_err[k],
1053 is_pinned ?
" (went negative: pinned at 0)"
1054 : fs.scale_err[k] > 0.0
1056 :
" (zero at every point: fixed)")
1059 for (Int_t a = 0; a < fs.nf; a++)
1060 for (Int_t b = a + 1; b < fs.nf; b++)
1061 std::cout << Form(
" correlation %s-%s %.3f",
1062 comp.label[fs.free_idx[a]].Data(),
1063 comp.label[fs.free_idx[b]].Data(),
1064 fs.cov[a][b] / std::sqrt(fs.cov[a][a] * fs.cov[b][b]))
1066 std::cout << Form(
" chi2 %.2f for %d points, %d free scale(s)", fs.chi2,
1072void CrossSection::PrintSubtractionCheck(
const ChannelResult &r,
1073 const FitComponents &comp,
1074 const ScaleFit &fs)
const {
1075 if (comp.n_keep >= comp.Size())
1077 const Int_t np = Int_t(r.points.size());
1079 std::vector<Double_t> undo(np, 1.0);
1080 Bool_t have_undo = kTRUE;
1081 for (Int_t i = 0; i < np; i++) {
1082 const Double_t keep = 1.0 - r.points[i].sub_frac;
1083 if (!(keep > 0.0)) {
1087 undo[i] = 1.0 / keep;
1091 FitExitScales(r, comp, before, &undo);
1093 std::cout <<
" subtraction check: the subtracted exits' scales with the "
1094 "points as measured and as corrected"
1096 for (Int_t k = comp.n_keep; k < comp.Size(); k++) {
1097 const Double_t after = fs.scale_raw[k], after_e = fs.scale_raw_err[k];
1099 std::cout << Form(
" %-26s after %.3f +- %.3f", comp.label[k].Data(),
1105 const Double_t raw = before.scale_raw[k];
1106 const Double_t expected = comp.n_keep > 0 ? -before.scale_raw[0] : -1.0;
1107 std::cout << Form(
" %-26s before %.3f, after %.3f (+- %.3f): moved "
1108 "%+.3f, expected %+.3f",
1109 comp.label[k].Data(), raw, after, after_e, after - raw,
1115 for (Int_t a = 0; a < fs.nf; a++)
1116 for (Int_t b = a + 1; b < fs.nf; b++)
1117 if (fs.cov[a][a] > 0.0 && fs.cov[b][b] > 0.0) {
1119 fs.cov[a][b] / std::sqrt(fs.cov[a][a] * fs.cov[b][b]);
1120 if (std::fabs(c) > std::fabs(rho))
1123 const Double_t chi2_ndf =
1124 np > fs.nf ? fs.chi2 / Double_t(np - fs.nf) : fs.chi2;
1125 const Bool_t degenerate = std::fabs(rho) > 0.9;
1126 const Bool_t poor_fit = chi2_ndf > 5.0;
1127 if (degenerate || poor_fit)
1128 std::cout << Form(
" the scale is not a usable check here: %s%s%s. A "
1129 "component this close to the channel's own absorbs the "
1130 "shape mismatch rather than its own yield, so its value "
1131 "says nothing about how much %s is left. The "
1132 "subtraction rests on the TALYS branching, and its "
1133 "uncertainty is the model spread already in the "
1136 ? Form(
"the two scales are %.2f correlated", rho)
1138 degenerate && poor_fit ?
" and " :
"",
1139 poor_fit ? Form(
"chi2/ndf is %.1f, so the model does not "
1140 "describe the shape",
1146 std::cout << Form(
" chi2/ndf %.1f and correlation %.2f, so the move "
1152Bool_t CrossSection::BuildScaledCurves(
const ChannelResult &r,
1153 const FitComponents &comp,
1154 const ScaleFit &fs, TGraph *&sum,
1156 TGraphErrors *&band)
const {
1157 const Int_t nc = comp.Size();
1160 std::vector<Double_t> gx, gsum, gfit, gband;
1161 for (Int_t p = 0; p < comp.graph[0]->GetN(); p++) {
1162 const Double_t e = comp.graph[0]->GetX()[p];
1163 Double_t s = 0.0, f = 0.0, var = 0.0;
1164 std::vector<Double_t> c(nc);
1165 for (Int_t k = 0; k < nc; k++) {
1166 c[k] = Interpolated(comp.graph[k], e);
1167 if (k < comp.n_keep)
1169 f += fs.scale[k] * c[k];
1171 for (Int_t a = 0; a < fs.nf; a++)
1172 for (Int_t b = 0; b < fs.nf; b++)
1173 var += c[fs.free_idx[a]] * c[fs.free_idx[b]] * fs.cov[a][b];
1179 gband.push_back(3.0 * std::sqrt(TMath::Max(0.0, var)));
1183 sum =
new TGraph(Int_t(gx.size()), &gx[0], &gsum[0]);
1184 fit =
new TGraph(Int_t(gx.size()), &gx[0], &gfit[0]);
1185 std::vector<Double_t> zero(gx.size(), 0.0);
1187 new TGraphErrors(Int_t(gx.size()), &gx[0], &gfit[0], &zero[0], &gband[0]);
1191void CrossSection::BuildMeasuredGraphs(
const ChannelResult &r, TGraph *fit,
1192 TGraphAsymmErrors *&measured,
1193 TGraphAsymmErrors *&deviation,
1194 FitFrame &frame)
const {
1195 const Int_t np = Int_t(r.points.size());
1198 std::vector<Double_t> vx, vy, vexl, vexh, veyl, veyh, dy, deyl, deyh;
1199 for (Int_t i = 0; i < np; i++) {
1200 const Point &pt = r.points[i];
1201 vx.push_back(pt.e_eff);
1202 vy.push_back(pt.sigma);
1203 vexl.push_back(std::hypot(pt.e_eff - TMath::Min(pt.e_in, pt.e_out),
1204 pt.e_eff - pt.e_eff_lo));
1205 vexh.push_back(std::hypot(TMath::Max(pt.e_in, pt.e_out) - pt.e_eff,
1206 pt.e_eff_hi - pt.e_eff));
1207 veyl.push_back(pt.ErrLo());
1208 veyh.push_back(pt.ErrHi());
1209 frame.fx_lo = TMath::Min(frame.fx_lo, vx.back() - vexl.back());
1210 frame.fx_hi = TMath::Max(frame.fx_hi, vx.back() + vexh.back());
1211 frame.fy_lo = TMath::Min(frame.fy_lo, vy.back());
1212 frame.fy_hi = TMath::Max(frame.fy_hi, vy.back());
1213 const Double_t f = Interpolated(fit, pt.e_eff);
1214 const Double_t err = pt.sigma > f ? pt.ErrLo() : pt.ErrHi();
1215 const Double_t pull = err > 0.0 ? (pt.sigma - f) / err : 0.0;
1217 deyl.push_back(1.0);
1218 deyh.push_back(1.0);
1219 frame.pull_max = TMath::Max(frame.pull_max, std::abs(pull) + 1.0);
1220 std::cout << Form(
" %2d %6.2f %8.2f %8.2f "
1222 pt.reac, pt.e_eff, pt.sigma, f, pull)
1225 measured =
new TGraphAsymmErrors(np, &vx[0], &vy[0], &vexl[0], &vexh[0],
1226 &veyl[0], &veyh[0]);
1227 measured->SetMarkerStyle(kChannelMarker[0]);
1228 measured->SetMarkerColor(kChannelColor[0]);
1229 measured->SetLineColor(kChannelColor[0]);
1230 deviation =
new TGraphAsymmErrors(np, &vx[0], &dy[0], &vexl[0], &vexh[0],
1231 &deyl[0], &deyh[0]);
1232 deviation->SetMarkerStyle(kChannelMarker[0]);
1233 deviation->SetMarkerColor(kChannelColor[0]);
1234 deviation->SetLineColor(kChannelColor[0]);
1238CrossSection::BuildPublishedGraph(
const CrossSectionChannel &ch,
1239 FitFrame &frame)
const {
1240 TGraphAsymmErrors *published =
nullptr;
1241 const std::vector<std::vector<Double_t>> &ref = ch.
reference_xs;
1243 std::vector<Double_t> rx, ry, rexl, rexh, rey;
1244 for (Int_t k = 0; k < Int_t(ref.size()); k++) {
1245 rx.push_back(ref[k][0]);
1246 rexh.push_back(ref[k][1]);
1247 rexl.push_back(ref[k][2]);
1248 ry.push_back(ref[k][3]);
1249 rey.push_back(ref[k][4]);
1250 frame.fx_lo = TMath::Min(frame.fx_lo, ref[k][0]);
1251 frame.fx_hi = TMath::Max(frame.fx_hi, ref[k][0]);
1252 frame.fy_lo = TMath::Min(frame.fy_lo, ref[k][3]);
1253 frame.fy_hi = TMath::Max(frame.fy_hi, ref[k][3]);
1255 published =
new TGraphAsymmErrors(Int_t(rx.size()), &rx[0], &ry[0],
1256 &rexl[0], &rexh[0], &rey[0], &rey[0]);
1257 published->SetMarkerStyle(24);
1258 published->SetMarkerColor(kRed + 1);
1259 published->SetLineColor(kRed + 1);
1264void CrossSection::FitLegendWidthPx(
const ScaleFit &fs,
1265 const CrossSectionChannel &ch,
1266 const FitComponents &comp,
1267 const TString &model_label,
1268 Bool_t has_published, TString &scale_text,
1269 Double_t &legend_width_px)
const {
1270 scale_text =
"#times";
1271 for (Int_t k = 0; k < comp.Size(); k++)
1273 Form(
"%s %.2f_{%s}", k ?
" &" :
"", fs.scale[k], comp.tag[k].Data());
1274 std::vector<TString> legend_labels;
1275 legend_labels.push_back(
"Present Work");
1278 legend_labels.push_back(
Label(ch) +
" scaled");
1279 legend_labels.push_back(scale_text);
1280 legend_labels.push_back(
Label(ch));
1281 if (comp.Size() > 1)
1282 for (Int_t k = 0; k < comp.Size(); k++)
1283 legend_labels.push_back(comp.label[k]);
1284 legend_width_px = PlottingUtils::LegendWidthPx(legend_labels, model_label);
1287void CrossSection::DrawFitTopPanel(
1288 const CrossSectionChannel &ch,
const FitFrame &frame,
1289 const TString &scale_text, Double_t legend_width_px, TGraph *sum,
1290 TGraph *fit, TGraphErrors *band,
const FitComponents &comp,
1291 TGraphAsymmErrors *published, TGraphAsymmErrors *measured,
1292 FitPanels &pads)
const {
1293 const Double_t x_lo = frame.fx_lo - 0.4, x_hi = frame.fx_hi + 0.4;
1294 const Int_t nc = comp.Size();
1295 const Int_t panel_height_px = 205;
1296 const Int_t plot_right_margin_px =
1297 TMath::Nint(1200.0 * gStyle->GetPadRightMargin());
1298 const Double_t legend_gap_px = 40.0;
1299 const Int_t extra_width_px =
1300 TMath::Max(0, TMath::Nint(legend_width_px + 2.0 * legend_gap_px) -
1301 plot_right_margin_px);
1302 TCanvas *c = PlottingUtils::GetConfiguredCanvasWithSideLegend(
1303 pads.plot, pads.side, extra_width_px, 800 + panel_height_px, kTRUE);
1304 TPad *top =
nullptr;
1305 TPad *bottom =
nullptr;
1306 PlottingUtils::SplitPadForPanel(pads.plot, panel_height_px, top, bottom);
1309 pads.bottom = bottom;
1310 pads.plot_right_margin_px = plot_right_margin_px;
1311 pads.extra_width_px = extra_width_px;
1315 pads.top->DrawFrame(x_lo, 0.5 * frame.fy_lo, x_hi, 3.0 * frame.fy_hi);
1316 frame_hist->SetTitle(
";;#sigma [mb]");
1317 frame_hist->GetXaxis()->SetLabelSize(0);
1318 frame_hist->GetXaxis()->SetTitleSize(0);
1321 std::vector<Double_t> bx, by, bz, be;
1322 for (Int_t p = 0; p < band->GetN(); p++)
1323 if (band->GetX()[p] >= x_lo && band->GetX()[p] <= x_hi) {
1324 bx.push_back(band->GetX()[p]);
1325 by.push_back(band->GetY()[p]);
1327 be.push_back(band->GetEY()[p]);
1330 TGraphErrors *band_c =
1331 new TGraphErrors(Int_t(bx.size()), &bx[0], &by[0], &bz[0], &be[0]);
1332 band_c->SetFillColorAlpha(kRed + 1, 0.25);
1333 band_c->SetLineWidth(0);
1334 band_c->Draw(
"3 SAME");
1337 if (TGraph *g = Clipped(fit, x_lo, x_hi)) {
1338 PlottingUtils::ConfigureGraph(g, kRed + 1);
1340 pads.entries.push_back(std::make_pair(g,
Label(ch) +
" scaled"));
1341 pads.entries.push_back(std::make_pair((TObject *)
nullptr, scale_text));
1343 if (TGraph *g = Clipped(sum, x_lo, x_hi)) {
1344 PlottingUtils::ConfigureGraph(g, kBlack);
1346 pads.entries.push_back(std::make_pair(g,
Label(ch)));
1349 for (Int_t k = 0; k < nc; k++) {
1350 TGraph *g = Clipped(comp.graph[k], x_lo, x_hi);
1353 PlottingUtils::ConfigureGraph(g, kComponentColor[k % 4]);
1355 const Bool_t subtracted = k >= comp.n_keep;
1357 g->SetLineColor(kGray + 1);
1358 g->SetLineStyle(subtracted ? 3 : 2);
1360 pads.entries.push_back(std::make_pair(g, comp.label[k]));
1363 published->Draw(
"P SAME");
1364 measured->Draw(
"P SAME");
1365 StampPreliminary(pads.top);
1368void CrossSection::DrawPullPanel(
const FitFrame &frame,
1369 TGraphAsymmErrors *deviation,
1370 FitPanels &pads)
const {
1372 const Double_t x_lo = frame.fx_lo - 0.4, x_hi = frame.fx_hi + 0.4;
1374 const Double_t d_lim = TMath::Max(5.0, 2.0 * std::ceil(0.6 * frame.pull_max));
1375 TH1F *dframe = pads.bottom->DrawFrame(x_lo, -d_lim, x_hi, d_lim);
1376 dframe->SetTitle(Form(
";%s [MeV];#delta/#sigma",
1378 dframe->GetYaxis()->SetNdivisions(-202);
1379 dframe->GetYaxis()->CenterTitle(kTRUE);
1380 TLine *zero_line =
new TLine(x_lo, 0.0, x_hi, 0.0);
1381 zero_line->SetLineStyle(2);
1382 zero_line->SetLineColor(kBlack);
1383 zero_line->SetLineWidth(PlottingUtils::GetLineWidth());
1384 zero_line->Draw(
"SAME");
1385 TLine *plus3_line =
new TLine(x_lo, 3.0, x_hi, 3.0);
1386 plus3_line->SetLineStyle(3);
1387 plus3_line->SetLineColor(kGray + 2);
1388 plus3_line->SetLineWidth(PlottingUtils::GetLineWidth());
1389 plus3_line->Draw(
"SAME");
1390 TLine *minus3_line =
new TLine(x_lo, -3.0, x_hi, -3.0);
1391 minus3_line->SetLineStyle(3);
1392 minus3_line->SetLineColor(kGray + 2);
1393 minus3_line->SetLineWidth(PlottingUtils::GetLineWidth());
1394 minus3_line->Draw(
"SAME");
1395 deviation->Draw(
"P SAME");
1404void CrossSection::DrawFit(
const ChannelResult &r,
const TString &name)
const {
1405 const CrossSectionChannel &ch = *r.ch;
1407 if (m < 0 || m >= Int_t(talys_raw_.size())) {
1408 std::cerr <<
"cross-section: channel " << ch.
name <<
": fit_model " << m
1409 <<
" but " << talys_raw_.size() <<
" TALYS model(s) in the file"
1414 if (!CollectFitComponents(r, m, comp))
1416 const Int_t np = Int_t(r.points.size());
1419 FitExitScales(r, comp, fs);
1420 PrintScaleFit(ch, m, comp, fs, np);
1421 PrintSubtractionCheck(r, comp, fs);
1422 std::cout <<
" strip E_cm,eff this work [mb] scaled model [mb] "
1426 TGraph *sum =
nullptr;
1427 TGraph *fit =
nullptr;
1428 TGraphErrors *band =
nullptr;
1429 if (!BuildScaledCurves(r, comp, fs, sum, fit, band))
1433 TGraphAsymmErrors *measured =
nullptr;
1434 TGraphAsymmErrors *deviation =
nullptr;
1435 BuildMeasuredGraphs(r, fit, measured, deviation, frame);
1438 TGraphAsymmErrors *published = BuildPublishedGraph(ch, frame);
1441 Double_t legend_width_px = 0.0;
1442 FitLegendWidthPx(fs, ch, comp, talys_labels_[m], published !=
nullptr,
1443 scale_text, legend_width_px);
1446 DrawFitTopPanel(ch, frame, scale_text, legend_width_px, sum, fit, band, comp,
1447 published, measured, pads);
1448 DrawPullPanel(frame, deviation, pads);
1451 const Int_t n_entries = 2 + (published ? 1 : 0) + Int_t(pads.entries.size());
1452 const Double_t side_width_px =
1453 pads.plot_right_margin_px + pads.extra_width_px;
1454 const Double_t side_gap_px = 0.5 * (side_width_px - legend_width_px);
1455 TLegend *leg = PlottingUtils::AddLegend(
1456 side_gap_px / side_width_px,
1457 (side_gap_px + legend_width_px) / side_width_px, 0.5 - 0.035 * n_entries,
1458 0.5 + 0.035 * n_entries);
1459 leg->SetHeader(talys_labels_[m]);
1460 leg->AddEntry(measured,
"Present Work",
"pe");
1463 for (Int_t k = 0; k < Int_t(pads.entries.size()); k++)
1464 leg->AddEntry(pads.entries[k].first, pads.entries[k].second,
1465 pads.entries[k].first ?
"l" :
"");
1467 PlottingUtils::ScaleFigure(pads.plot);
1468 PlottingUtils::DrawTitle(
1470 PlottingUtils::FigureScale(pads.plot));
1471 PlottingUtils::SaveFigure(pads.c, name,
"cross_section",
1472 PlotSaveOptions::kLOG);
1476Bool_t CrossSection::RunChannel(
const CrossSectionChannel &ch,
1477 ChannelResult &out) {
1480 for (Int_t k = 0; k < Int_t(ch.
talys_exits.size()); k++) {
1483 std::cerr <<
"cross-section: channel " << ch.
name <<
": exit \""
1485 <<
"\" does not parse (light particles n p d t h a g with an "
1486 "optional multiplicity digit)"
1494 std::cerr <<
"cross-section: channel " << ch.
name
1496 <<
"\" does not parse (light particles n p d t h a g with an "
1497 "optional multiplicity digit)"
1502 if (!talys_raw_.empty() && ch.
talys_exits.empty()) {
1503 std::cerr <<
"cross-section: channel " << ch.
name
1504 <<
": TALYS models are declared but the channel names no exits"
1510 std::cerr <<
"cross-section: channel " << ch.
name
1511 <<
": subtract_exits needs the TALYS curves it subtracts; run "
1516 out.talys = ExitCurves(ch, ch.
talys_exits,
"the channel:");
1517 out.talys_sub = ExitCurves(ch, ch.
subtract_exits,
"to subtract:");
1519 std::cerr <<
"cross-section: channel " << ch.
name
1520 <<
": no model has any of the exits to subtract" << std::endl;
1523 std::cout << std::endl
1524 <<
" " << ch.
name <<
" " <<
Label(ch) <<
", region_" << ch.
name
1525 << (out.talys.empty() ?
" (no TALYS curve: midpoint energies)" :
"")
1527 if (!out.talys_sub.empty())
1529 <<
" is not separable from it here, so TALYS's predicted share "
1530 "of each strip's count is removed; the models' spread on "
1531 "that share is a systematic"
1533 std::cout <<
" strip E_cm range [MeV] "
1535 <<
" N_reac N_beam sigma [mb]" << std::endl;
1538 if (Strip(ch, out.talys, out.talys_sub, reac, pt))
1539 out.points.push_back(pt);
1541 if (out.points.empty()) {
1542 std::cerr <<
"cross-section: channel " << ch.
name
1543 <<
": no strip produced a cross section" << std::endl;
1546 CompareReference(out);
1547 std::vector<const ChannelResult *> one(1, &out);
1548 Draw(one,
"cross_section_" + ch.
name + TagSuffix());
1550 DrawFit(out,
"cross_section_" + ch.
name +
"_fit" + TagSuffix());
1558 std::cerr <<
"cross-section: this dataset has no CROSS_SECTION_CONFIG "
1559 "(gas pressure and beam are both required)"
1564 std::cerr <<
"cross-section: this dataset declares no "
1565 "CROSS_SECTION_CONFIG.CHANNELS"
1569 if (!LoadCache() || !LoadBeam())
1572 std::cout <<
"cross-section: " << n_beam_
1573 <<
" beam events past every pre-tag "
1574 <<
"cut, of " << n_seen_ <<
" seen" << std::endl;
1575 std::cout << Form(
" gas %.0f Torr at %.0f K -> %.4e atoms/cm^3; strip "
1576 "%.3f cm -> %.4e atoms/cm^2",
1578 kStripLengthCm, areal_)
1581 std::cout << Form(
" every tagged event counted; the systematic is the cut "
1582 "variation with the gas pressure (+- %.1f Torr)",
1586 std::cout << Form(
" regions at %.1f sigma enclose %.1f%% of the fitted "
1587 "component and counts are corrected for that only",
1591 if (talys_raw_.empty())
1592 std::cout <<
"cross-section: no TALYS graphs in root_files (run talys-xs); "
1593 "midpoint energies, no overlay"
1596 std::vector<ChannelResult> results(X.
CHANNELS.size());
1597 std::vector<const ChannelResult *> done;
1598 for (Int_t c = 0; c < Int_t(X.
CHANNELS.size()); c++)
1599 if (RunChannel(X.
CHANNELS[c], results[c]))
1600 done.push_back(&results[c]);
1603 if (done.size() > 1)
1604 Draw(done,
"cross_section" + TagSuffix());
Double_t TargetGasAtomsPerMolecule(TargetGas gas)
Atoms of the reacting species per molecule of fill gas.
Absolute cross section per reaction strip, for every declared channel.
The reaction search: strip-sum scatters, beam gating and tagging.
static TString Label(const CrossSectionChannel &ch)
Display label for a channel.
static TString SubtractedLabel(const CrossSectionChannel &ch)
Label the channel's subtracted exits, or return an empty string.
Bool_t Run()
Run every channel and write the tables and figures.
static Bool_t ExitResidue(const TString &exit, Int_t z_beam, Int_t a_beam, Int_t &z, Int_t &a)
Residual nucleus left by a named exit channel.
StripSumScatterConfig STRIP_SUM_SCATTER_CONFIG
CrossSectionConfig CROSS_SECTION_CONFIG
static TString DatasetName()
The dataset's isotope name, e.g.
static TString ResultsDir()
Absolute path to the directory receiving generated output.
static TString CacheName()
Filename of the scatter cache for this configuration.
Bool_t Profile(const TString &path, Double_t *dE, Double_t &e_strip0)
Read the per-strip energy loss profile from a beam simulation.
Double_t CmFraction()
Centre-of-mass energy fraction for this dataset's reaction.
TString SimPath()
Path to the dataset's beam simulation file.
const TString & ActiveFileTag()
Output-name prefix of the active epoch.
const DatasetConfig & cfg
The active dataset's configuration, flat block.
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.
TALYS calculation with name for plot and parameters to actually run TALYS (see CrossSectionConfig::TA...
Int_t fit_model
Index into TALYS_MODELS of the model for the second figure (cross_section_<name>_fit): that model's s...
std::vector< std::vector< Double_t > > reference_xs
Published values to compare against, if any, one row per point: the effective centre-of-mass energy [...
std::vector< TString > talys_exits
Which of the residual channels TALYS wrote make up this channel's curve, as exit channels by name: wh...
TString label
The reaction as it appears in the plot title after the dataset name, e.g.
std::vector< TString > subtract_exits
Exits that cannot be separated from this channel; their TALYS branching fraction is removed from the ...
TString name
Names the region cut (region_<name>) and the output figure.
Experimental details needed to properly calculate the cross section.
Int_t FELDMAN_COUSINS_MAX_COUNT
Statistical error on a strip's count.
Int_t XS_STRIP_MIN
Reaction strips for which to calculate a cross-section.
Double_t GAS_PRESSURE_TORR_ERR
Its uncertainty [Torr]; a relative systematic on every point (the cross section scales as 1 / pressur...
Double_t GAS_PRESSURE_TORR
At the pressure the gas was actually at rather than the nominal one.
Int_t BEAM_A
Beam mass number, for the lab-to-centre-of-mass conversion; its Z and element symbol name it to a rea...
Bool_t EFFECTIVE_ENERGY
Report each strip at its effective centre-of-mass energy (the energy below which half the strip's yie...
std::vector< TalysModel > TALYS_MODELS
Hauser-Feshbach predictions from TALYS.
std::vector< CrossSectionChannel > CHANNELS
The reaction channels measured on this dataset.
AnRegionMode AN_REGION_MODE
Double_t AN_REGION_NSIGMA
compute-regions: the (a,n) region is the reaction component's AN_REGION_NSIGMA Mahalanobis ellipse fr...