MUSIC unknown
Analysis for the MUSIC active-target ionization chamber
Loading...
Searching...
No Matches
TagEfficiency.cpp
Go to the documentation of this file.
1#include "TagEfficiency.hpp"
2#include "Constants.hpp"
3#include "InitUtils.hpp"
4#include "Normalization.hpp"
5#include "Paths.hpp"
6#include "PlottingUtils.hpp"
7#include "StripSumScatter.hpp"
8#include <TCanvas.h>
9#include <TFile.h>
10#include <TGraph.h>
11#include <TGraphErrors.h>
12#include <TH2F.h>
13#include <TLegend.h>
14#include <TNamed.h>
15#include <TParameter.h>
16#include <TRandom3.h>
17#include <TSystem.h>
18#include <TTree.h>
19#include <cmath>
20#include <iostream>
21
22// ---------------------------------------------------------------------------
23// Store
24// ---------------------------------------------------------------------------
25namespace TagEfficiencyStore {
26
27TString Path() {
28 return Paths::ResultsDir() + "/root_files/tag_efficiency.root";
29}
30
31static const char *kFields[5] = {"n_counted", "eff", "eff_err", "migrate",
32 "tag_eff"};
33
34static TString Key(const TString &channel, const char *field, Int_t reac) {
35 return Form("%s_%s_r%d", channel.Data(), field, reac);
36}
37
38void Write(const TString &channel,
39 const std::vector<TagEfficiencyRecord> &records,
40 const TString &method) {
41 TFile f(Path(), "UPDATE");
42 if (f.IsZombie()) {
43 std::cerr << "tag-efficiency: cannot write " << Path() << std::endl;
44 return;
45 }
46 f.cd();
47 TNamed("method", method.Data()).Write("method", TObject::kOverwrite);
48 // Drop the channel's previous records so a strip that lost its cut does
49 // not keep a stale one.
50 for (TIter it(f.GetListOfKeys()); TObject *k = it();)
51 if (TString(k->GetName()).BeginsWith(channel + "_"))
52 f.Delete(Form("%s;*", k->GetName()));
53 for (Int_t i = 0; i < Int_t(records.size()); i++) {
54 const TagEfficiencyRecord &r = records[i];
55 const Double_t v[5] = {r.n_counted, r.eff, r.eff_err, r.migrate, r.tag_eff};
56 for (Int_t k = 0; k < 5; k++) {
57 const TString key = Key(channel, kFields[k], r.reac);
58 TParameter<Double_t>(key, v[k]).Write(key, TObject::kOverwrite);
59 }
60 }
61 f.Close();
62}
63
64Bool_t Load(const TString &channel, Int_t reac, TagEfficiencyRecord &record) {
65 if (gSystem->AccessPathName(Path()))
66 return kFALSE;
67 TFile f(Path(), "READ");
68 if (f.IsZombie())
69 return kFALSE;
70 Double_t v[5];
71 for (Int_t k = 0; k < 5; k++) {
72 TParameter<Double_t> *p = dynamic_cast<TParameter<Double_t> *>(
73 f.Get(Key(channel, kFields[k], reac)));
74 if (!p)
75 return kFALSE;
76 v[k] = p->GetVal();
77 }
78 record.reac = reac;
79 record.n_counted = v[0];
80 record.eff = v[1];
81 record.eff_err = v[2];
82 record.migrate = v[3];
83 record.tag_eff = v[4];
84 return kTRUE;
85}
86
87TString Method() {
88 if (gSystem->AccessPathName(Path()))
89 return "";
90 TFile f(Path(), "READ");
91 TNamed *m = f.IsZombie() ? nullptr : dynamic_cast<TNamed *>(f.Get("method"));
92 return m ? TString(m->GetTitle()) : TString("");
93}
94
95} // namespace TagEfficiencyStore
96
97// ---------------------------------------------------------------------------
98// Bootstrap method
99// ---------------------------------------------------------------------------
100namespace {
101
102const Long64_t kBootstrapTrials = 20000;
103const Double_t kMinEventsInCut = 20.0;
104
105} // namespace
106
107Bool_t TagEfficiency::LoadSigmas(TFile &cache) {
108 Double_t jump[18] = {0}, strip[18] = {0};
109 Int_t found = 0;
110 for (Int_t s = 0; s < 18; s++) {
111 if (TParameter<Double_t> *p = static_cast<TParameter<Double_t> *>(
112 cache.Get(Form("jump_sigma_s%d", s)))) {
113 jump[s] = p->GetVal();
114 found++;
115 }
116 if (TParameter<Double_t> *p = static_cast<TParameter<Double_t> *>(
117 cache.Get(Form("strip_sigma_s%d", s))))
118 strip[s] = p->GetVal();
119 }
122 return found > 0;
123}
124
125// Strips with a hand-drawn cut for this channel, one past XS_STRIP_MAX for
126// the migration out of the last strip. The fitted ellipses are not used: until
127// they reproduce the drawn cuts they also sweep in the beam tail under the
128// island, which outnumbers the reaction a hundred to one at the low strips and
129// drags the mean onto the ridge.
130void TagEfficiency::LoadDrawnCuts(const TString &region) {
131 const CrossSectionConfig &X = Constants::cfg.CROSS_SECTION_CONFIG;
132 strips_.clear();
133 for (Int_t reac = X.XS_STRIP_MIN; reac <= X.XS_STRIP_MAX + 1; reac++) {
134 Strip st;
135 st.cut = RegionCutStore::LoadDrawn(region, reac);
136 if (st.cut)
137 strips_[reac] = st;
138 }
139}
140
141// Mean trace of the events inside each strip's drawn cut, one pass over the
142// reservoir.
143void TagEfficiency::MeanTraces(TTree *tt) {
144 const Int_t kReacMin =
146 Float_t total[18];
147 UInt_t mask = 0;
148 tt->SetBranchStatus("*", 0);
149 tt->SetBranchStatus("total", 1);
150 tt->SetBranchStatus("reac_mask", 1);
151 tt->SetBranchAddress("total", total);
152 tt->SetBranchAddress("reac_mask", &mask);
153 for (std::map<Int_t, Strip>::iterator it = strips_.begin();
154 it != strips_.end(); ++it) {
155 it->second.mean.assign(18, 0.0);
156 it->second.count = 0.0;
157 }
158 const Long64_t n = tt->GetEntries();
159 for (Long64_t i = 0; i < n; i++) {
160 tt->GetEntry(i);
161 if (mask == 0)
162 continue;
163 Double_t td[18];
164 for (Int_t s = 0; s < 18; s++)
165 td[s] = total[s];
166 for (std::map<Int_t, Strip>::iterator it = strips_.begin();
167 it != strips_.end(); ++it) {
168 const Int_t reac = it->first;
169 if (!(mask & (1u << (reac - kReacMin))))
170 continue;
171 Double_t x, y;
172 StripSumScatter::PlaneXY(td, reac, x, y);
173 if (!it->second.cut->IsInside(x, y))
174 continue;
175 for (Int_t s = 0; s < 18; s++)
176 it->second.mean[s] += td[s];
177 it->second.count += 1.0;
178 }
179 }
180 for (std::map<Int_t, Strip>::iterator it = strips_.begin();
181 it != strips_.end(); ++it)
182 if (it->second.count > 0.0)
183 for (Int_t s = 0; s < 18; s++)
184 it->second.mean[s] /= it->second.count;
185 tt->SetBranchStatus("*", 1);
186}
187
188// The mean trace resampled with the measured beam widths, each resample
189// through the tag and the cut at reac, and through the next strip's for the
190// migration.
191TagEfficiency::Outcome TagEfficiency::Bootstrap(Int_t reac, TH2F *plane) {
192 const Strip &st = strips_[reac];
193 TCutG *cut_next = strips_.count(reac + 1) ? strips_[reac + 1].cut : nullptr;
194 TRandom3 rng(12345 + reac);
195 Outcome o;
196 for (Long64_t t = 0; t < kBootstrapTrials; t++) {
197 Double_t total[18];
198 for (Int_t s = 0; s < 18; s++)
199 total[s] = st.mean[s] + rng.Gaus(0.0, StripSumScatter::StripSigma(s));
200 EnergyView ev;
201 for (Int_t s = 0; s < 18; s++)
202 ev.total[s] = total[s];
203 o.n++;
204 Double_t x, y;
205 StripSumScatter::PlaneXY(total, reac, x, y);
206 if (plane)
207 plane->Fill(x, y);
208 if (StripSumScatter::PassesReaction(ev, reac)) {
209 o.tagged++;
210 if (st.cut->IsInside(x, y))
211 o.inside++;
212 }
213 if (cut_next && StripSumScatter::PassesReaction(ev, reac + 1)) {
214 Double_t xn, yn;
215 StripSumScatter::PlaneXY(total, reac + 1, xn, yn);
216 if (cut_next->IsInside(xn, yn))
217 o.next_inside++;
218 }
219 }
220 return o;
221}
222
223void TagEfficiency::DrawMeanTrace(const TString &subdir, Int_t reac,
224 const Strip &st) {
225 TH2F *frame =
226 new TH2F(Form("h_eff_frame_%s_%d", subdir.Data(), reac),
227 ";Strip;#DeltaE [a.u.]", 18, -0.5, 17.5, 100, 0.6, 1.6);
228 frame->SetStats(0);
229 TCanvas *c = PlottingUtils::GetConfiguredCanvas(kFALSE);
230 frame->Draw();
231 TGraphErrors *band = new TGraphErrors(18);
232 TGraph *line = new TGraph(18);
233 for (Int_t s = 0; s < 18; s++) {
234 band->SetPoint(s, s, st.mean[s]);
235 band->SetPointError(s, 0.0, StripSumScatter::StripSigma(s));
236 line->SetPoint(s, s, st.mean[s]);
237 }
238 band->SetFillColorAlpha(kRed + 1, 0.25);
239 band->SetLineColor(kRed + 1);
240 band->Draw("3 SAME");
241 line->SetLineColor(kRed + 1);
242 line->SetLineWidth(2);
243 line->Draw("L SAME");
244 TLegend *leg = PlottingUtils::AddLegend(0.40, 0.875, 0.72, 0.86);
245 leg->AddEntry(
246 line, Form("Mean trace, %.0f events in the drawn cut", st.count), "l");
247 leg->AddEntry(band, "#pm1#sigma beam width per strip", "f");
248 leg->Draw();
249 PlottingUtils::AddText(Form("reaction strip %d", reac), 0.875, 0.68);
250 PlottingUtils::SaveFigure(c, Form("mean_trace_reac%d", reac), subdir,
251 PlotSaveOptions::kLINEAR);
252 delete c;
253 delete frame;
254}
255
256void TagEfficiency::DrawPlane(const TString &subdir, Int_t reac, TH2F *plane,
257 TCutG *cut) {
258 TCanvas *c = PlottingUtils::GetConfiguredCanvas(kFALSE);
259 c->SetLogz(kTRUE);
260 PlottingUtils::ConfigureAndDraw2DHistogram(
261 plane, c, Form("reac %d: bootstrap on the drawn cut", reac));
262 cut->SetLineColor(kBlack);
263 cut->SetLineWidth(2);
264 cut->SetFillStyle(0);
265 cut->Draw("L SAME");
266 PlottingUtils::SaveFigure(c, Form("bootstrap_plane_reac%d", reac), subdir,
267 PlotSaveOptions::kLINEAR);
268 delete c;
269}
270
271Bool_t TagEfficiency::RunChannel(const CrossSectionChannel &ch,
272 TTree *reservoir) {
273 const StripSumScatterConfig &C = Constants::cfg.STRIP_SUM_SCATTER_CONFIG;
274 const CrossSectionConfig &X = Constants::cfg.CROSS_SECTION_CONFIG;
275 const TString region = "region_" + ch.name;
276 const TString subdir = "tag_efficiency/" + ch.name;
277 LoadDrawnCuts(region);
278 if (strips_.empty()) {
279 std::cerr << "tag-efficiency: channel " << ch.name
280 << ": no hand-drawn cuts (" << region
281 << "); draw them in strip-sum-scatter first" << std::endl;
282 return kFALSE;
283 }
284 std::cout << "tag-efficiency: channel " << ch.name << ": mean traces inside "
285 << region << " from " << reservoir->GetEntries()
286 << " reservoir events, jump gate " << C.REAC_JUMP_NSIGMA
287 << " sigma; " << kBootstrapTrials << " bootstrap trials per strip"
288 << std::endl;
289 MeanTraces(reservoir);
290
291 std::vector<TagEfficiencyRecord> records;
292 std::cout << Form("%5s %10s %9s %9s %9s", "strip", "in cut", "tag eff", "eff",
293 "migrate")
294 << std::endl;
295 for (Int_t reac = X.XS_STRIP_MIN; reac <= X.XS_STRIP_MAX; reac++) {
296 if (strips_.find(reac) == strips_.end() ||
297 strips_[reac].count < kMinEventsInCut) {
298 std::cout << Form("%5d %10.0f no drawn cut or too few events", reac,
299 strips_.count(reac) ? strips_[reac].count : 0.0)
300 << std::endl;
301 continue;
302 }
303 const Strip &st = strips_[reac];
304 TH2F *plane = new TH2F(Form("h_boot_plane_%s_%d", ch.name.Data(), reac),
305 ";norm. #DeltaE strips 1#rightarrow16 [a.u.];norm. "
306 "#DeltaE post window [a.u.]",
307 300, C.X_DISPLAY_MIN, C.X_DISPLAY_MAX, 300,
309 const Outcome o = Bootstrap(reac, plane);
310
311 TagEfficiencyRecord r;
312 r.reac = reac;
313 r.n_counted = st.count;
314 r.eff = Double_t(o.inside) / Double_t(o.n);
315 // Binomial on the trials; the sample's own error rides on n_counted.
316 r.eff_err = std::sqrt(r.eff * (1.0 - r.eff) / Double_t(o.n));
317 r.migrate = Double_t(o.next_inside) / Double_t(o.n);
318 r.tag_eff = Double_t(o.tagged) / Double_t(o.n);
319 records.push_back(r);
320 std::cout << Form("%5d %10.0f %9.3f %9.3f %9.3f", reac, r.n_counted,
321 r.tag_eff, r.eff, r.migrate)
322 << std::endl;
323
324 DrawMeanTrace(subdir, reac, st);
325 DrawPlane(subdir, reac, plane, st.cut);
326 delete plane;
327 }
329 return !records.empty();
330}
331
333 const CrossSectionConfig &X = Constants::cfg.CROSS_SECTION_CONFIG;
334 if (X.CHANNELS.empty()) {
335 std::cerr << "tag-efficiency: this dataset declares no CROSS_SECTION_CONFIG"
336 ".CHANNELS"
337 << std::endl;
338 return kFALSE;
339 }
340 TString cache_path =
341 IO::GetRootFilesBaseDir() + "/" + StripSumScatter::CacheName();
342 TFile cache(cache_path, "READ");
343 if (cache.IsZombie()) {
344 std::cerr << "tag-efficiency: no scatter cache at " << cache_path
345 << "; run strip-sum-scatter first" << std::endl;
346 return kFALSE;
347 }
348 if (!LoadSigmas(cache)) {
349 std::cerr << "tag-efficiency: cache carries no per-strip noise sigmas; "
350 "rebuild it"
351 << std::endl;
352 return kFALSE;
353 }
354 TTree *reservoir = static_cast<TTree *>(cache.Get("traces"));
355 if (!reservoir) {
356 std::cerr << "tag-efficiency: no reservoir in the cache" << std::endl;
357 return kFALSE;
358 }
359 Int_t done = 0;
360 for (Int_t c = 0; c < Int_t(X.CHANNELS.size()); c++)
361 if (RunChannel(X.CHANNELS[c], reservoir))
362 done++;
363 std::cout << "tag-efficiency: " << done << " of " << X.CHANNELS.size()
364 << " channels written to " << TagEfficiencyStore::Path()
365 << std::endl;
366 return done > 0;
367}
The dataset configuration, and how it is layered.
The reaction search: strip-sum scatters, beam gating and tagging.
The tag-efficiency correction, and its contract with the cross section.
StripSumScatterConfig STRIP_SUM_SCATTER_CONFIG
CrossSectionConfig CROSS_SECTION_CONFIG
static TString ResultsDir()
Absolute path to the directory receiving generated output.
Definition Paths.cpp:24
static TString CacheName()
Filename of the scatter cache for this configuration.
static void SetStripSigma(const Double_t *sigma)
Install the per-strip sigmas.
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 StripSigma(Int_t strip)
Sigma of a strip's own deposit.
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.
static const char * MethodLabel()
The label stamped into the store for this method.
Bool_t Run()
Measure every configured channel and write the store and figures.
const DatasetConfig & cfg
The active dataset's configuration, flat block.
TCutG * LoadDrawn(const char *name, Int_t reac)
Load only a hand-drawn cut, never a fitted one.
Persistence for the efficiency records.
Bool_t Load(const TString &channel, Int_t reac, TagEfficiencyRecord &record)
Read one record.
TString Method()
The method label stamped into the store.
TString Path()
Path to the store.
void Write(const TString &channel, const std::vector< TagEfficiencyRecord > &records, const TString &method)
Replace one channel's records, keeping every other channel's.
One reaction channel the cross section is extracted for.
TString name
Names the region cut (region_<name>), the tag-efficiency records and the output figure.
What the cross section needs about the experiment rather than the analysis.
Int_t XS_STRIP_MIN
Reaction strips to report a cross section for.
std::vector< CrossSectionChannel > CHANNELS
The reaction channels measured on this dataset.
Double_t total[18]
Summed energy per strip, after strip_factor.
Double_t X_DISPLAY_MIN
Display-only windows for the strip-sum scatters (a.u.): the histograms are built over the fixed Scatt...
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...
Definition Constants.hpp:80
One channel and strip's efficiency, as the cross section consumes it.
Int_t reac
Reaction strip index; -1 for an unset record.
Double_t migrate
Fraction of true reactions at this strip counted at strip reac + 1 instead, which the cross section u...
Double_t n_counted
The numerator this efficiency belongs to: the events counted at this strip by the same selection the ...
Double_t eff
Fraction of true reactions at this strip that end up in n_counted.
Double_t tag_eff
Of eff, the part the tag alone passes; the remainder is the region cut.
Double_t eff_err
Uncertainty on eff, or 0 when the method provides none.