Analysis-Utilities 26.9.9
C++/ROOT utilities for nuclear measurement data analysis
Loading...
Searching...
No Matches
InteractiveRooFitEditor.cpp
Go to the documentation of this file.
2
4#include "PlottingUtils.hpp"
5
6#include <RooArgSet.h>
7#include <RooFitResult.h>
8#include <RooMsgService.h>
9#include <TROOT.h>
10#include <iostream>
11
13 const TGWindow *parent, TH1 *hist, const std::vector<Double_t> *events,
14 Float_t display_bin_width_kev, RooAbsPdf *total_pdf, RooRealVar *x,
15 RooAbsData *data, std::vector<RooFitPeakModel> *peaks,
16 RooFitBackgroundModel *bkg, Double_t range_low, Double_t range_high,
17 const TString &info_label)
18 : TGMainFrame(parent, 1400, 900) {
19
20 hist_ = hist;
21 events_ = events;
22 display_bin_width_kev_ = display_bin_width_kev;
23 total_pdf_ = total_pdf;
24 x_ = x;
25 data_ = data;
26 peaks_ = peaks;
27 bkg_ = bkg;
28 info_label_text_ = info_label;
29 range_low_ = range_low;
30 range_high_ = range_high;
31 original_range_low_ = range_low;
32 original_range_high_ = range_high;
33 hist_x_min_ = hist->GetXaxis()->GetXmin();
34 hist_x_max_ = hist->GetXaxis()->GetXmax();
35 num_peaks_ = (Int_t)peaks->size();
36 num_params_ = num_peaks_ * 10 + 2;
37
38 accepted_ = kFALSE;
39 done_ = kFALSE;
40 needs_redraw_ = kFALSE;
41 syncing_ = kFALSE;
42
43 for (Int_t pi = 0; pi < num_peaks_; pi++) {
44 RooFitPeakModel &p = (*peaks_)[pi];
45 params_.push_back(p.mu);
46 params_.push_back(p.sigma);
47 params_.push_back(p.gaus_yield);
48 params_.push_back(p.ratio_step);
49 params_.push_back(p.ratio_low_exp);
50 params_.push_back(p.tau_ratio_low_exp);
51 params_.push_back(p.ratio_low_lin);
52 params_.push_back(p.slope_low_lin);
53 params_.push_back(p.ratio_high_exp);
54 params_.push_back(p.tau_ratio_high_exp);
55 }
56 params_.push_back(bkg_->bkg_yield);
57 params_.push_back(bkg_->bkg_slope);
58
59 original_params_.resize(num_params_);
60 original_bounds_low_.resize(num_params_);
61 original_bounds_high_.resize(num_params_);
62 original_fixed_.resize(num_params_);
63 current_bounds_low_.resize(num_params_);
64 current_bounds_high_.resize(num_params_);
65
66 for (Int_t i = 0; i < num_params_; i++) {
67 original_params_[i] = params_[i]->getVal();
68 original_fixed_[i] = params_[i]->isConstant();
69
70 Double_t lo, hi;
71 if (params_[i]->hasMin() && params_[i]->hasMax()) {
72 lo = params_[i]->getMin();
73 hi = params_[i]->getMax();
74 } else {
75 GetDefaultBounds(i, lo, hi);
76 }
77 original_bounds_low_[i] = lo;
78 original_bounds_high_[i] = hi;
79 current_bounds_low_[i] = lo;
80 current_bounds_high_[i] = hi;
81 }
82
83 sliders_.resize(num_params_);
84 value_entries_.resize(num_params_);
85 fix_checks_.resize(num_params_);
86 lo_bound_entries_.resize(num_params_);
87 hi_bound_entries_.resize(num_params_);
88
89 hist_draw_ = nullptr;
90 total_graph_ = nullptr;
91 bkg_graph_ = nullptr;
92 res_graph_ = nullptr;
93 zero_line_ = nullptr;
94 plus3_line_ = nullptr;
95 minus3_line_ = nullptr;
96 chi2_label_ = nullptr;
97 n_res_points_ = 0;
98 for (Int_t p = 0; p < 3; p++) {
99 for (Int_t c = 0; c < 4; c++) {
100 comp_graphs_[p][c] = nullptr;
101 }
102 }
103
104 BuildGUI();
105 InitDrawing();
106
107 redraw_timer_ = new TTimer(this, 50);
108 redraw_timer_->TurnOn();
109
110 SetWindowName("Interactive RooFit Editor");
111 MapSubwindows();
112 Resize(GetDefaultSize());
113 MapWindow();
114
115 UpdateCanvas();
116}
117
119 if (redraw_timer_) {
120 redraw_timer_->TurnOff();
121 delete redraw_timer_;
122 }
123
124 // Drawing primitives drawn on the embedded canvas — TPad does not
125 // auto-delete user primitives, so they have to go by hand.
126 //
127 // Pre-clear each pad's primitives list with "nodelete" before destroying
128 // the primitives themselves. The launcher removes the embedded canvas
129 // from gROOT->GetListOfCanvases() to suppress X11 errors during teardown,
130 // which also disables RecursiveRemove's cleanup of pad lists; without
131 // this manual Clear, ~TPad later walks stale pointers and segfaults.
132 if (main_pad_ && main_pad_->GetListOfPrimitives()) {
133 main_pad_->GetListOfPrimitives()->Clear("nodelete");
134 }
135 if (residual_pad_ && residual_pad_->GetListOfPrimitives()) {
136 residual_pad_->GetListOfPrimitives()->Clear("nodelete");
137 }
138 for (Int_t p = 0; p < 3; p++) {
139 for (Int_t c = 0; c < 4; c++) {
140 delete comp_graphs_[p][c];
141 }
142 }
143 delete chi2_label_;
144 delete zero_line_;
145 delete plus3_line_;
146 delete minus3_line_;
147 delete res_graph_;
148 delete bkg_graph_;
149 delete total_graph_;
150 delete hist_draw_;
151 delete residual_pad_;
152 delete main_pad_;
153}
154
155void InteractiveRooFitEditor::BuildGUI() {
156 TGHorizontalFrame *main_frame = new TGHorizontalFrame(this, 1400, 850);
157 AddFrame(main_frame,
158 new TGLayoutHints(kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2));
159
160 embedded_canvas_ =
161 new TRootEmbeddedCanvas("RooFitEditorCanvas", main_frame, 850, 800);
162 main_frame->AddFrame(
163 embedded_canvas_,
164 new TGLayoutHints(kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2));
165
166 TGVerticalFrame *controls = new TGVerticalFrame(main_frame, 520, 800);
167 main_frame->AddFrame(
168 controls, new TGLayoutHints(kLHintsExpandY | kLHintsRight, 2, 2, 2, 2));
169
170 if (info_label_text_.Length() > 0) {
171 TGGroupFrame *info_grp = new TGGroupFrame(controls, "Info", kVerticalFrame);
172 controls->AddFrame(info_grp, new TGLayoutHints(kLHintsExpandX, 3, 3, 3, 3));
173 TGLabel *info_label = new TGLabel(info_grp, info_label_text_);
174 info_grp->AddFrame(info_label,
175 new TGLayoutHints(kLHintsCenterX, 4, 4, 4, 4));
176 }
177
178 TGGroupFrame *range_grp =
179 new TGGroupFrame(controls, "Fit Range", kVerticalFrame);
180 controls->AddFrame(range_grp, new TGLayoutHints(kLHintsExpandX, 3, 3, 3, 3));
181
182 range_slider_ =
183 new TGDoubleHSlider(range_grp, 200, kDoubleScaleNo, kRangeSlider);
184 range_slider_->SetRange(hist_x_min_, hist_x_max_);
185 range_slider_->SetPosition(range_low_, range_high_);
186 range_slider_->Associate(this);
187 range_grp->AddFrame(range_slider_,
188 new TGLayoutHints(kLHintsExpandX, 2, 2, 2, 2));
189
190 TGHorizontalFrame *range_entries = new TGHorizontalFrame(range_grp, 200, 28);
191 range_grp->AddFrame(range_entries,
192 new TGLayoutHints(kLHintsExpandX, 2, 2, 2, 2));
193
194 TGLabel *range_lo_label = new TGLabel(range_entries, "Low:");
195 range_entries->AddFrame(range_lo_label,
196 new TGLayoutHints(kLHintsCenterY, 2, 2, 2, 2));
197 range_lo_entry_ = new TGNumberEntry(
198 range_entries, range_low_, 8, kRangeLoEntry, TGNumberFormat::kNESReal,
199 TGNumberFormat::kNEAAnyNumber, TGNumberFormat::kNELNoLimits);
200 range_lo_entry_->GetNumberEntry()->Associate(this);
201 range_entries->AddFrame(
202 range_lo_entry_,
203 new TGLayoutHints(kLHintsExpandX | kLHintsCenterY, 2, 2, 2, 2));
204
205 TGLabel *range_hi_label = new TGLabel(range_entries, "High:");
206 range_entries->AddFrame(range_hi_label,
207 new TGLayoutHints(kLHintsCenterY, 8, 2, 2, 2));
208 range_hi_entry_ = new TGNumberEntry(
209 range_entries, range_high_, 8, kRangeHiEntry, TGNumberFormat::kNESReal,
210 TGNumberFormat::kNEAAnyNumber, TGNumberFormat::kNELNoLimits);
211 range_hi_entry_->GetNumberEntry()->Associate(this);
212 range_entries->AddFrame(
213 range_hi_entry_,
214 new TGLayoutHints(kLHintsExpandX | kLHintsCenterY, 2, 2, 2, 2));
215
216 TGTab *tabs = new TGTab(controls, 510, 700);
217 controls->AddFrame(
218 tabs, new TGLayoutHints(kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2));
219
220 for (Int_t p = 0; p < num_peaks_; p++) {
221 TString tab_name = TString::Format("Peak %d", p + 1);
222 TGCompositeFrame *tab_frame = tabs->AddTab(tab_name);
223 BuildPeakTab(tab_frame, p);
224 }
225
226 TGCompositeFrame *bkg_tab = tabs->AddTab("Background");
227 BuildBackgroundTab(bkg_tab);
228
229 TGHorizontalFrame *btn_frame = new TGHorizontalFrame(controls, 510, 40);
230 controls->AddFrame(
231 btn_frame, new TGLayoutHints(kLHintsExpandX | kLHintsBottom, 2, 2, 5, 5));
232
233 TGTextButton *refit_btn = new TGTextButton(btn_frame, "Refit", kBtnRefit);
234 refit_btn->Associate(this);
235 TGTextButton *accept_btn = new TGTextButton(btn_frame, "Accept", kBtnAccept);
236 accept_btn->Associate(this);
237 TGTextButton *cancel_btn = new TGTextButton(btn_frame, "Cancel", kBtnCancel);
238 cancel_btn->Associate(this);
239 TGTextButton *reset_btn = new TGTextButton(btn_frame, "Reset", kBtnReset);
240 reset_btn->Associate(this);
241
242 TGLayoutHints *btn_hints = new TGLayoutHints(kLHintsExpandX, 4, 4, 2, 2);
243 btn_frame->AddFrame(refit_btn, btn_hints);
244 btn_frame->AddFrame(accept_btn, btn_hints);
245 btn_frame->AddFrame(cancel_btn, btn_hints);
246 btn_frame->AddFrame(reset_btn, btn_hints);
247}
248
249void InteractiveRooFitEditor::BuildPeakTab(TGCompositeFrame *parent,
250 Int_t peak_idx) {
251 Int_t offset = peak_idx * 10;
252
253 TGGroupFrame *gaus_grp = new TGGroupFrame(parent, "Gaussian", kVerticalFrame);
254 parent->AddFrame(gaus_grp, new TGLayoutHints(kLHintsExpandX, 3, 3, 3, 1));
255 AddParamRow(gaus_grp, offset + 0, "Mu");
256 AddParamRow(gaus_grp, offset + 1, "Sigma");
257 AddParamRow(gaus_grp, offset + 2, "Yield");
258
259 TGGroupFrame *step_grp = new TGGroupFrame(parent, "Step", kVerticalFrame);
260 parent->AddFrame(step_grp, new TGLayoutHints(kLHintsExpandX, 3, 3, 1, 1));
261 AddParamRow(step_grp, offset + 3, "Step Ratio");
262
263 TGGroupFrame *ltail_grp =
264 new TGGroupFrame(parent, "Low-Side Tails", kVerticalFrame);
265 parent->AddFrame(ltail_grp, new TGLayoutHints(kLHintsExpandX, 3, 3, 1, 1));
266 AddParamRow(ltail_grp, offset + 4, "Exp Amp");
267 AddParamRow(ltail_grp, offset + 5, "Exp Decay/Sigma");
268 AddParamRow(ltail_grp, offset + 6, "Lin Ratio");
269 AddParamRow(ltail_grp, offset + 7, "Lin Slope");
270
271 TGGroupFrame *htail_grp =
272 new TGGroupFrame(parent, "High-Side Tail", kVerticalFrame);
273 parent->AddFrame(htail_grp, new TGLayoutHints(kLHintsExpandX, 3, 3, 1, 3));
274 AddParamRow(htail_grp, offset + 8, "Exp Amp");
275 AddParamRow(htail_grp, offset + 9, "Exp Decay/Sigma");
276}
277
278void InteractiveRooFitEditor::BuildBackgroundTab(TGCompositeFrame *parent) {
279 TGGroupFrame *bkg_grp =
280 new TGGroupFrame(parent, "Background", kVerticalFrame);
281 parent->AddFrame(bkg_grp, new TGLayoutHints(kLHintsExpandX, 3, 3, 3, 3));
282 AddParamRow(bkg_grp, BkgConstIdx(), "Yield");
283 AddParamRow(bkg_grp, BkgSlopeIdx(), "Slope");
284}
285
286void InteractiveRooFitEditor::AddParamRow(TGCompositeFrame *parent,
287 Int_t param_idx, const char *name) {
288 TGHorizontalFrame *row = new TGHorizontalFrame(parent, 490, 28);
289 parent->AddFrame(row, new TGLayoutHints(kLHintsExpandX, 1, 1, 1, 1));
290
291 Double_t val = params_[param_idx]->getVal();
292 Bool_t fixed = original_fixed_[param_idx];
293
294 TGLabel *label = new TGLabel(row, name);
295 row->AddFrame(label, new TGLayoutHints(kLHintsCenterY, 2, 4, 2, 2));
296 label->Resize(90, 20);
297
298 TGHSlider *slider =
299 new TGHSlider(row, 140, kSlider1, kSliderBase + param_idx);
300 slider->SetRange(0, kSliderRes);
301 slider->SetPosition(ValToSlider(param_idx, val));
302 slider->Associate(this);
303 row->AddFrame(slider,
304 new TGLayoutHints(kLHintsExpandX | kLHintsCenterY, 2, 2, 2, 2));
305 sliders_[param_idx] = slider;
306
307 TGNumberEntry *entry = new TGNumberEntry(
308 row, val, 8, kEntryBase + param_idx, TGNumberFormat::kNESReal,
309 TGNumberFormat::kNEAAnyNumber, TGNumberFormat::kNELNoLimits);
310 entry->GetNumberEntry()->Associate(this);
311 row->AddFrame(entry, new TGLayoutHints(kLHintsCenterY, 2, 2, 2, 2));
312 value_entries_[param_idx] = entry;
313
314 TGCheckButton *fix_cb = new TGCheckButton(row, "Fix", kFixBase + param_idx);
315 fix_cb->Associate(this);
316 if (fixed) {
317 fix_cb->SetState(kButtonDown);
318 }
319 row->AddFrame(fix_cb, new TGLayoutHints(kLHintsCenterY, 2, 2, 2, 2));
320 fix_checks_[param_idx] = fix_cb;
321
322 TGNumberEntry *lo_entry = new TGNumberEntry(
323 row, current_bounds_low_[param_idx], 6, kLoBoundBase + param_idx,
324 TGNumberFormat::kNESReal, TGNumberFormat::kNEAAnyNumber,
325 TGNumberFormat::kNELNoLimits);
326 lo_entry->GetNumberEntry()->Associate(this);
327 row->AddFrame(lo_entry, new TGLayoutHints(kLHintsCenterY, 1, 1, 2, 2));
328 lo_bound_entries_[param_idx] = lo_entry;
329
330 TGNumberEntry *hi_entry = new TGNumberEntry(
331 row, current_bounds_high_[param_idx], 6, kHiBoundBase + param_idx,
332 TGNumberFormat::kNESReal, TGNumberFormat::kNEAAnyNumber,
333 TGNumberFormat::kNELNoLimits);
334 hi_entry->GetNumberEntry()->Associate(this);
335 row->AddFrame(hi_entry, new TGLayoutHints(kLHintsCenterY, 1, 1, 2, 2));
336 hi_bound_entries_[param_idx] = hi_entry;
337
338 if (fixed) {
339 slider->SetEnabled(kFALSE);
340 entry->GetNumberEntry()->SetEnabled(kFALSE);
341 }
342}
343
344Double_t InteractiveRooFitEditor::EvalPdfDensity(RooAbsPdf *pdf, Double_t xv) {
345 Double_t saved = x_->getVal();
346 x_->setVal(xv);
347 RooArgSet nset(*x_);
348 Double_t v = pdf->getVal(&nset);
349 x_->setVal(saved);
350 return v;
351}
352
353Double_t InteractiveRooFitEditor::ComponentExpected(RooAbsPdf *pdf,
354 Double_t yield,
355 Double_t xv) {
356 Double_t bin_width = hist_->GetBinWidth(1);
357 return yield * EvalPdfDensity(pdf, xv) * bin_width;
358}
359
360void InteractiveRooFitEditor::InitDrawing() {
361 TCanvas *canvas = embedded_canvas_->GetCanvas();
362 canvas->cd();
363
364 main_pad_ = new TPad("rfeditor_main", "rfeditor_main", 0, 0.3, 1, 1.0);
365 main_pad_->SetBottomMargin(0.04);
366 main_pad_->SetGridx(1);
367 main_pad_->SetGridy(1);
368 main_pad_->SetTopMargin(0.08);
369 main_pad_->Draw();
370
371 residual_pad_ = new TPad("rfeditor_res", "rfeditor_res", 0, 0, 1, 0.3);
372 residual_pad_->SetTopMargin(0.04);
373 residual_pad_->SetBottomMargin(0.35);
374 residual_pad_->SetGridx(1);
375 residual_pad_->SetGridy(1);
376 residual_pad_->Draw();
377
378 main_pad_->cd();
379
380 hist_draw_ = static_cast<TH1 *>(hist_->Clone("hist_rfeditor_draw"));
381 hist_draw_->SetDirectory(0);
382 Double_t min_disp = 0.9 * range_low_;
383 Double_t max_disp = 1.1 * range_high_;
384 hist_draw_->GetXaxis()->SetRangeUser(min_disp, max_disp);
385 hist_draw_->GetXaxis()->SetLabelSize(0);
386 hist_draw_->GetXaxis()->SetTitleSize(0);
387 hist_draw_->SetLineColor(kViolet);
388 hist_draw_->SetLineWidth(2);
389 hist_draw_->Draw();
390
391 total_graph_ = new TGraph(kNDrawPts);
392 total_graph_->SetLineColor(kAzure);
393 total_graph_->SetLineWidth(2);
394 total_graph_->Draw("L same");
395
396 bkg_graph_ = new TGraph(kNDrawPts);
397 bkg_graph_->SetLineColor(kGreen);
398 bkg_graph_->SetLineWidth(2);
399 bkg_graph_->Draw("L same");
400
401 Int_t comp_colors[4] = {kBlack, kGray, kRed, kOrange};
402 for (Int_t p = 0; p < num_peaks_; p++) {
403 for (Int_t c = 0; c < 4; c++) {
404 comp_graphs_[p][c] = new TGraph(kNDrawPts);
405 comp_graphs_[p][c]->SetLineColor(comp_colors[c]);
406 comp_graphs_[p][c]->SetLineStyle(PeakStyle(p));
407 comp_graphs_[p][c]->SetLineWidth(2);
408 comp_graphs_[p][c]->Draw("L same");
409 }
410 }
411
412 chi2_label_ = new TLatex();
413 chi2_label_->SetNDC();
414 chi2_label_->SetTextSize(0.045);
415 chi2_label_->SetTextAlign(31);
416 chi2_label_->Draw();
417
418 main_pad_->SetLogy(kTRUE);
419
420 residual_pad_->cd();
421
422 n_res_points_ = 0;
423 Int_t nbins = hist_->GetNbinsX();
424 for (Int_t i = 1; i <= nbins; i++) {
425 Double_t xv = hist_->GetBinCenter(i);
426 if (xv < range_low_ || xv > range_high_)
427 continue;
428 Double_t data = hist_->GetBinContent(i);
429 Double_t error = hist_->GetBinError(i);
430 if (error > 0 && data > 0)
431 n_res_points_++;
432 }
433 if (n_res_points_ < 1)
434 n_res_points_ = 1;
435
436 res_graph_ = new TGraph(n_res_points_);
437 res_graph_->SetMarkerStyle(20);
438 res_graph_->SetMarkerSize(0.8);
439 res_graph_->SetMarkerColor(kAzure);
440 res_graph_->SetLineColor(kAzure);
441 res_graph_->SetTitle("");
442
443 Double_t ax_min =
444 hist_draw_->GetXaxis()->GetBinLowEdge(hist_draw_->GetXaxis()->GetFirst());
445 Double_t ax_max =
446 hist_draw_->GetXaxis()->GetBinUpEdge(hist_draw_->GetXaxis()->GetLast());
447 res_graph_->GetXaxis()->SetLimits(ax_min, ax_max);
448 res_graph_->GetYaxis()->SetTitle("#delta/#sigma");
449 res_graph_->GetXaxis()->SetTitle(hist_->GetXaxis()->GetTitle());
450 res_graph_->GetXaxis()->SetTitleSize(0.13);
451 res_graph_->GetYaxis()->SetTitleSize(0.13);
452 res_graph_->GetXaxis()->SetLabelSize(0.12);
453 res_graph_->GetYaxis()->SetLabelSize(0.12);
454 res_graph_->GetXaxis()->SetTitleOffset(1.0);
455 res_graph_->GetYaxis()->SetTitleOffset(0.3);
456 res_graph_->GetYaxis()->SetNdivisions(505);
457 res_graph_->GetXaxis()->SetNdivisions(510);
458 res_graph_->GetYaxis()->CenterTitle(kTRUE);
459 res_graph_->GetYaxis()->SetRangeUser(-5.5, 5.5);
460 res_graph_->Draw("AP");
461
462 zero_line_ = new TF1("zero_rfeditor", "0", ax_min, ax_max);
463 zero_line_->SetLineColor(kBlack);
464 zero_line_->SetLineStyle(2);
465 zero_line_->SetLineWidth(2);
466 zero_line_->Draw("same");
467
468 plus3_line_ = new TF1("plus3_rfeditor", "3", ax_min, ax_max);
469 plus3_line_->SetLineColor(kGray + 2);
470 plus3_line_->SetLineStyle(3);
471 plus3_line_->SetLineWidth(2);
472 plus3_line_->Draw("same");
473
474 minus3_line_ = new TF1("minus3_rfeditor", "-3", ax_min, ax_max);
475 minus3_line_->SetLineColor(kGray + 2);
476 minus3_line_->SetLineStyle(3);
477 minus3_line_->SetLineWidth(2);
478 minus3_line_->Draw("same");
479
480 UpdateAllGraphs();
481 UpdateResPoints();
482}
483
484void InteractiveRooFitEditor::UpdateAllGraphs() {
485 Double_t x_step = (range_high_ - range_low_) / (kNDrawPts - 1);
486 Double_t bin_width = hist_->GetBinWidth(1);
487 Double_t saved = x_->getVal();
488 RooArgSet nset(*x_);
489
490 Double_t total_exp = total_pdf_->expectedEvents(&nset);
491 for (Int_t i = 0; i < kNDrawPts; i++) {
492 Double_t xv = range_low_ + i * x_step;
493 x_->setVal(xv);
494 total_graph_->SetPoint(i, xv,
495 total_exp * total_pdf_->getVal(&nset) * bin_width);
496 }
497
498 Double_t bkg_yield = bkg_->bkg_yield->getVal();
499 for (Int_t i = 0; i < kNDrawPts; i++) {
500 Double_t xv = range_low_ + i * x_step;
501 x_->setVal(xv);
502 bkg_graph_->SetPoint(i, xv,
503 bkg_yield * bkg_->bkg_pdf->getVal(&nset) * bin_width);
504 }
505
506 for (Int_t pi = 0; pi < num_peaks_; pi++) {
507 RooFitPeakModel &p = (*peaks_)[pi];
508 Double_t gy = p.gaus_yield->getVal();
509 Double_t sy = p.step_yield->getVal();
510 Double_t lexp_y = p.low_exp_yield->getVal();
511 Double_t llin_y = p.low_lin_yield->getVal();
512 Double_t hexp_y = p.high_exp_yield->getVal();
513
514 for (Int_t i = 0; i < kNDrawPts; i++) {
515 Double_t xv = range_low_ + i * x_step;
516 x_->setVal(xv);
517 Double_t bkg_v = bkg_yield * bkg_->bkg_pdf->getVal(&nset) * bin_width;
518
519 comp_graphs_[pi][0]->SetPoint(
520 i, xv, gy * p.gauss_pdf->getVal(&nset) * bin_width + bkg_v);
521 comp_graphs_[pi][1]->SetPoint(
522 i, xv, sy * p.step_pdf->getVal(&nset) * bin_width + bkg_v);
523 Double_t y_low = lexp_y * p.low_exp_pdf->getVal(&nset) * bin_width +
524 llin_y * p.low_lin_pdf->getVal(&nset) * bin_width;
525 comp_graphs_[pi][2]->SetPoint(i, xv, y_low + bkg_v);
526 comp_graphs_[pi][3]->SetPoint(
527 i, xv, hexp_y * p.high_exp_pdf->getVal(&nset) * bin_width + bkg_v);
528 }
529 }
530
531 x_->setVal(saved);
532}
533
534void InteractiveRooFitEditor::UpdateResPoints() {
535 Int_t nbins = hist_->GetNbinsX();
536 Double_t bin_width = hist_->GetBinWidth(1);
537 RooArgSet nset(*x_);
538 Double_t total_exp = total_pdf_->expectedEvents(&nset);
539 Double_t saved = x_->getVal();
540
541 Int_t pt = 0;
542 for (Int_t i = 1; i <= nbins; i++) {
543 Double_t xv = hist_->GetBinCenter(i);
544 if (xv < range_low_ || xv > range_high_)
545 continue;
546 Double_t data = hist_->GetBinContent(i);
547 Double_t error = hist_->GetBinError(i);
548 if (error > 0 && data > 0) {
549 x_->setVal(xv);
550 Double_t fit_val = total_exp * total_pdf_->getVal(&nset) * bin_width;
551 Double_t pull = (data - fit_val) / error;
552 res_graph_->SetPoint(pt, xv, pull);
553 pt++;
554 }
555 }
556 res_graph_->Set(pt);
557 n_res_points_ = pt;
558
559 Double_t disp_lo = 0.9 * range_low_;
560 Double_t disp_hi = 1.1 * range_high_;
561 res_graph_->GetXaxis()->SetLimits(disp_lo, disp_hi);
562
563 x_->setVal(saved);
564}
565
566void InteractiveRooFitEditor::UpdateCanvas() {
567 UpdateAllGraphs();
568 UpdateResPoints();
569
570 Double_t chi2_sum = 0;
571 Int_t ndf_bins = 0;
572 Int_t nbins = hist_->GetNbinsX();
573 Double_t bin_width = hist_->GetBinWidth(1);
574 RooArgSet nset(*x_);
575 Double_t total_exp = total_pdf_->expectedEvents(&nset);
576 Double_t saved = x_->getVal();
577 for (Int_t i = 1; i <= nbins; i++) {
578 Double_t xv = hist_->GetBinCenter(i);
579 if (xv < range_low_ || xv > range_high_)
580 continue;
581 Double_t data = hist_->GetBinContent(i);
582 Double_t error = hist_->GetBinError(i);
583 if (error > 0 && data > 0) {
584 x_->setVal(xv);
585 Double_t fit_val = total_exp * total_pdf_->getVal(&nset) * bin_width;
586 Double_t residual = (data - fit_val) / error;
587 chi2_sum += residual * residual;
588 ndf_bins++;
589 }
590 }
591 x_->setVal(saved);
592 Int_t n_free = 0;
593 for (Int_t i = 0; i < num_params_; i++) {
594 if (!IsFixed(i))
595 n_free++;
596 }
597 Int_t ndf = ndf_bins - n_free;
598 if (ndf > 0) {
599 chi2_label_->SetText(0.85, 0.85,
600 Form("#chi^{2}/ndf = %.3f", chi2_sum / ndf));
601 } else {
602 chi2_label_->SetText(0.85, 0.85, "#chi^{2}/ndf = N/A");
603 }
604
605 zero_line_->SetRange(0.9 * range_low_, 1.1 * range_high_);
606 plus3_line_->SetRange(0.9 * range_low_, 1.1 * range_high_);
607 minus3_line_->SetRange(0.9 * range_low_, 1.1 * range_high_);
608 res_graph_->GetYaxis()->SetRangeUser(-5.5, 5.5);
609
610 TCanvas *canvas = embedded_canvas_->GetCanvas();
611 main_pad_->Modified();
612 residual_pad_->Modified();
613 canvas->Modified();
614 canvas->Update();
615
616 needs_redraw_ = kFALSE;
617}
618
619void InteractiveRooFitEditor::SyncAllWidgets() {
620 syncing_ = kTRUE;
621 for (Int_t i = 0; i < num_params_; i++) {
622 SyncWidget(i);
623 }
624 syncing_ = kFALSE;
625}
626
627void InteractiveRooFitEditor::SyncWidget(Int_t param_idx) {
628 Double_t val = params_[param_idx]->getVal();
629 Bool_t fixed = params_[param_idx]->isConstant();
630
631 if (!fixed && params_[param_idx]->hasMin() && params_[param_idx]->hasMax()) {
632 current_bounds_low_[param_idx] = params_[param_idx]->getMin();
633 current_bounds_high_[param_idx] = params_[param_idx]->getMax();
634 }
635
636 Double_t clamped = val;
637 if (clamped < current_bounds_low_[param_idx])
638 clamped = current_bounds_low_[param_idx];
639 if (clamped > current_bounds_high_[param_idx])
640 clamped = current_bounds_high_[param_idx];
641
642 sliders_[param_idx]->SetPosition(ValToSlider(param_idx, clamped));
643 value_entries_[param_idx]->SetNumber(val);
644
645 if (fixed) {
646 fix_checks_[param_idx]->SetState(kButtonDown);
647 sliders_[param_idx]->SetEnabled(kFALSE);
648 value_entries_[param_idx]->GetNumberEntry()->SetEnabled(kFALSE);
649 } else {
650 fix_checks_[param_idx]->SetState(kButtonUp);
651 sliders_[param_idx]->SetEnabled(kTRUE);
652 value_entries_[param_idx]->GetNumberEntry()->SetEnabled(kTRUE);
653 }
654
655 lo_bound_entries_[param_idx]->SetNumber(current_bounds_low_[param_idx]);
656 hi_bound_entries_[param_idx]->SetNumber(current_bounds_high_[param_idx]);
657}
658
659void InteractiveRooFitEditor::OnSliderMoved(Int_t param_idx) {
660 if (syncing_)
661 return;
662 if (IsFixed(param_idx))
663 return;
664
665 Int_t pos = sliders_[param_idx]->GetPosition();
666 Double_t val = SliderToVal(param_idx, pos);
667 params_[param_idx]->setVal(val);
668
669 syncing_ = kTRUE;
670 value_entries_[param_idx]->SetNumber(val);
671 syncing_ = kFALSE;
672
673 needs_redraw_ = kTRUE;
674}
675
676void InteractiveRooFitEditor::OnEntryChanged(Int_t param_idx) {
677 if (syncing_)
678 return;
679 if (IsFixed(param_idx))
680 return;
681
682 Double_t val = value_entries_[param_idx]->GetNumber();
683
684 Bool_t bounds_changed = kFALSE;
685 if (val < current_bounds_low_[param_idx]) {
686 current_bounds_low_[param_idx] = val;
687 bounds_changed = kTRUE;
688 }
689 if (val > current_bounds_high_[param_idx]) {
690 current_bounds_high_[param_idx] = val;
691 bounds_changed = kTRUE;
692 }
693
694 params_[param_idx]->setRange(current_bounds_low_[param_idx],
695 current_bounds_high_[param_idx]);
696 params_[param_idx]->setVal(val);
697
698 syncing_ = kTRUE;
699 sliders_[param_idx]->SetPosition(ValToSlider(param_idx, val));
700 if (bounds_changed) {
701 lo_bound_entries_[param_idx]->SetNumber(current_bounds_low_[param_idx]);
702 hi_bound_entries_[param_idx]->SetNumber(current_bounds_high_[param_idx]);
703 }
704 syncing_ = kFALSE;
705
706 needs_redraw_ = kTRUE;
707}
708
709void InteractiveRooFitEditor::OnBoundsChanged(Int_t param_idx) {
710 if (syncing_)
711 return;
712
713 Double_t new_lo = lo_bound_entries_[param_idx]->GetNumber();
714 Double_t new_hi = hi_bound_entries_[param_idx]->GetNumber();
715
716 if (new_lo >= new_hi)
717 return;
718
719 current_bounds_low_[param_idx] = new_lo;
720 current_bounds_high_[param_idx] = new_hi;
721
722 Double_t val = params_[param_idx]->getVal();
723 if (val < new_lo)
724 val = new_lo;
725 if (val > new_hi)
726 val = new_hi;
727
728 if (!IsFixed(param_idx)) {
729 params_[param_idx]->setRange(new_lo, new_hi);
730 }
731 params_[param_idx]->setVal(val);
732
733 syncing_ = kTRUE;
734 sliders_[param_idx]->SetPosition(ValToSlider(param_idx, val));
735 value_entries_[param_idx]->SetNumber(val);
736 syncing_ = kFALSE;
737
738 needs_redraw_ = kTRUE;
739}
740
741void InteractiveRooFitEditor::OnFixToggled(Int_t param_idx) {
742 Bool_t now_fixed = (fix_checks_[param_idx]->GetState() == kButtonDown);
743
744 if (now_fixed) {
745 params_[param_idx]->setConstant(kTRUE);
746 sliders_[param_idx]->SetEnabled(kFALSE);
747 value_entries_[param_idx]->GetNumberEntry()->SetEnabled(kFALSE);
748 } else {
749 params_[param_idx]->setRange(current_bounds_low_[param_idx],
750 current_bounds_high_[param_idx]);
751 params_[param_idx]->setConstant(kFALSE);
752 sliders_[param_idx]->SetEnabled(kTRUE);
753 value_entries_[param_idx]->GetNumberEntry()->SetEnabled(kTRUE);
754 }
755
756 needs_redraw_ = kTRUE;
757}
758
759void InteractiveRooFitEditor::OnRangeChanged() {
760 if (syncing_)
761 return;
762
763 Double_t new_lo = range_slider_->GetMinPosition();
764 Double_t new_hi = range_slider_->GetMaxPosition();
765
766 if (new_lo >= new_hi)
767 return;
768
769 range_low_ = new_lo;
770 range_high_ = new_hi;
771
772 x_->setRange(range_low_, range_high_);
773 x_->setRange(RooFitUtils::kFitRangeName, range_low_, range_high_);
774
775 if (events_) {
777 hist_draw_, *events_, (Float_t)range_low_, (Float_t)range_high_,
778 display_bin_width_kev_);
779 }
780 hist_draw_->GetXaxis()->SetRangeUser(0.9 * range_low_, 1.1 * range_high_);
781
782 syncing_ = kTRUE;
783 range_lo_entry_->SetNumber(range_low_);
784 range_hi_entry_->SetNumber(range_high_);
785 syncing_ = kFALSE;
786
787 needs_redraw_ = kTRUE;
788}
789
790void InteractiveRooFitEditor::DoRefit() {
791 for (Int_t i = 0; i < num_params_; i++) {
792 if (!IsFixed(i)) {
793 params_[i]->setRange(current_bounds_low_[i], current_bounds_high_[i]);
794 }
795 }
796
797 RooFitResult *res = total_pdf_->fitTo(
798 *data_, RooFit::Save(kTRUE), RooFit::Extended(kTRUE),
799 RooFit::Range(RooFitUtils::kFitRangeName), RooFit::SumW2Error(kFALSE),
800 RooFit::PrintLevel(-1), RooFit::PrintEvalErrors(-1), RooFit::Strategy(2),
801 RooFit::Minimizer("Minuit2", "migrad"), BestAvailableBackend());
802 if (res)
803 delete res;
804
805 SyncAllWidgets();
806 needs_redraw_ = kTRUE;
807 UpdateCanvas();
808}
809
810void InteractiveRooFitEditor::DoAccept() {
811 accepted_ = kTRUE;
812 done_ = kTRUE;
813}
814
815void InteractiveRooFitEditor::DoCancel() {
816 for (Int_t i = 0; i < num_params_; i++) {
817 params_[i]->setVal(original_params_[i]);
818 if (original_fixed_[i]) {
819 params_[i]->setConstant(kTRUE);
820 } else {
821 params_[i]->setRange(original_bounds_low_[i], original_bounds_high_[i]);
822 params_[i]->setConstant(kFALSE);
823 }
824 }
825 x_->setRange(original_range_low_, original_range_high_);
826 x_->setRange(RooFitUtils::kFitRangeName, original_range_low_,
827 original_range_high_);
828 accepted_ = kFALSE;
829 done_ = kTRUE;
830}
831
832void InteractiveRooFitEditor::DoReset() {
833 for (Int_t i = 0; i < num_params_; i++) {
834 params_[i]->setVal(original_params_[i]);
835 current_bounds_low_[i] = original_bounds_low_[i];
836 current_bounds_high_[i] = original_bounds_high_[i];
837 if (original_fixed_[i]) {
838 params_[i]->setConstant(kTRUE);
839 } else {
840 params_[i]->setRange(original_bounds_low_[i], original_bounds_high_[i]);
841 params_[i]->setConstant(kFALSE);
842 }
843 }
844
845 range_low_ = original_range_low_;
846 range_high_ = original_range_high_;
847 x_->setRange(range_low_, range_high_);
848 x_->setRange(RooFitUtils::kFitRangeName, range_low_, range_high_);
849 hist_draw_->GetXaxis()->SetRangeUser(0.9 * range_low_, 1.1 * range_high_);
850 syncing_ = kTRUE;
851 range_slider_->SetPosition(range_low_, range_high_);
852 range_lo_entry_->SetNumber(range_low_);
853 range_hi_entry_->SetNumber(range_high_);
854 syncing_ = kFALSE;
855
856 SyncAllWidgets();
857 needs_redraw_ = kTRUE;
858 UpdateCanvas();
859}
860
861Bool_t InteractiveRooFitEditor::ProcessMessage(Long_t msg, Long_t parm1,
862 Long_t parm2) {
863 if (syncing_)
864 return kTRUE;
865
866 (void)parm2;
867
868 switch (GET_MSG(msg)) {
869 case kC_COMMAND:
870 switch (GET_SUBMSG(msg)) {
871 case kCM_BUTTON:
872 if (parm1 == kBtnRefit)
873 DoRefit();
874 else if (parm1 == kBtnAccept)
875 DoAccept();
876 else if (parm1 == kBtnCancel)
877 DoCancel();
878 else if (parm1 == kBtnReset)
879 DoReset();
880 break;
881 case kCM_CHECKBUTTON:
882 if (parm1 >= kFixBase && parm1 < kFixBase + num_params_) {
883 OnFixToggled(parm1 - kFixBase);
884 }
885 break;
886 }
887 break;
888 case kC_HSLIDER:
889 if (parm1 == kRangeSlider) {
890 OnRangeChanged();
891 } else if (parm1 >= kSliderBase && parm1 < kSliderBase + num_params_) {
892 OnSliderMoved(parm1 - kSliderBase);
893 }
894 break;
895 case kC_TEXTENTRY:
896 if (GET_SUBMSG(msg) == kTE_ENTER || GET_SUBMSG(msg) == kTE_TAB) {
897 if (parm1 == kRangeLoEntry || parm1 == kRangeHiEntry) {
898 Double_t new_lo = range_lo_entry_->GetNumber();
899 Double_t new_hi = range_hi_entry_->GetNumber();
900 if (new_lo < new_hi) {
901 syncing_ = kTRUE;
902 range_slider_->SetPosition(new_lo, new_hi);
903 syncing_ = kFALSE;
904 OnRangeChanged();
905 }
906 } else if (parm1 >= kEntryBase && parm1 < kEntryBase + num_params_) {
907 OnEntryChanged(parm1 - kEntryBase);
908 } else if (parm1 >= kLoBoundBase && parm1 < kLoBoundBase + num_params_) {
909 OnBoundsChanged(parm1 - kLoBoundBase);
910 } else if (parm1 >= kHiBoundBase && parm1 < kHiBoundBase + num_params_) {
911 OnBoundsChanged(parm1 - kHiBoundBase);
912 }
913 }
914 break;
915 }
916
917 return kTRUE;
918}
919
921 if (timer == redraw_timer_ && needs_redraw_) {
922 UpdateCanvas();
923 }
924 return kTRUE;
925}
926
928
929Int_t InteractiveRooFitEditor::ValToSlider(Int_t param_idx, Double_t val) {
930 Double_t lo = current_bounds_low_[param_idx];
931 Double_t hi = current_bounds_high_[param_idx];
932 if (hi <= lo)
933 return 0;
934 Double_t frac = (val - lo) / (hi - lo);
935 if (frac < 0.0)
936 frac = 0.0;
937 if (frac > 1.0)
938 frac = 1.0;
939 return static_cast<Int_t>(frac * kSliderRes);
940}
941
942Double_t InteractiveRooFitEditor::SliderToVal(Int_t param_idx, Int_t pos) {
943 Double_t lo = current_bounds_low_[param_idx];
944 Double_t hi = current_bounds_high_[param_idx];
945 Double_t frac = static_cast<Double_t>(pos) / kSliderRes;
946 return lo + frac * (hi - lo);
947}
948
949Bool_t InteractiveRooFitEditor::IsFixed(Int_t param_idx) {
950 return (fix_checks_[param_idx]->GetState() == kButtonDown);
951}
952
953void InteractiveRooFitEditor::GetDefaultBounds(Int_t param_idx, Double_t &lo,
954 Double_t &hi) {
955 Double_t peak_height = hist_->GetMaximum();
956 Double_t range_width = range_high_ - range_low_;
957
958 if (param_idx == BkgConstIdx()) {
959 lo = 0;
960 hi = peak_height * range_width * 10.0;
961 return;
962 }
963 if (param_idx == BkgSlopeIdx()) {
964 lo = -1000.0;
965 hi = 1000.0;
966 return;
967 }
968
969 Int_t local = param_idx % 10;
970 switch (local) {
971 case 0:
972 lo = range_low_;
973 hi = range_high_;
974 break;
975 case 1:
976 lo = range_width * 0.001;
977 hi = range_width * 0.5;
978 break;
979 case 2:
980 lo = 0;
981 hi = peak_height * range_width * 10.0;
982 break;
983 case 3:
984 case 4:
985 case 6:
986 case 8:
987 lo = 0;
988 hi = 0.5;
989 break;
990 case 5:
991 case 9:
992 lo = 1.0;
993 hi = 100.0;
994 break;
995 case 7:
996 lo = -1.0;
997 hi = 1.0;
998 break;
999 default:
1000 lo = -1000;
1001 hi = 1000;
1002 break;
1003 }
1004}
1005
1006Int_t InteractiveRooFitEditor::PeakStyle(Int_t peak_idx) {
1007 if (peak_idx == 0)
1008 return 1;
1009 if (peak_idx == 1)
1010 return 3;
1011 return 4;
1012}
1013
1015 TH1 *hist, const std::vector<Double_t> *events,
1016 Float_t display_bin_width_kev, RooAbsPdf *total_pdf, RooRealVar *x,
1017 RooAbsData *data, std::vector<RooFitPeakModel> *peaks,
1018 RooFitBackgroundModel *bkg, Double_t range_low, Double_t range_high,
1019 const TString &info_label) {
1020 if (!gClient) {
1021 std::cerr << "InteractiveRooFitEditor: GUI not available (gClient is null)."
1022 << std::endl;
1023 return kFALSE;
1024 }
1025
1026 // Swallow recoverable X protocol errors for the editor's lifetime so a
1027 // transient bad redraw doesn't trip ROOT's crashing default handler.
1029
1031 gClient->GetRoot(), hist, events, display_bin_width_kev, total_pdf, x,
1032 data, peaks, bkg, range_low, range_high, info_label);
1033
1034 while (!editor->IsDone()) {
1035 gSystem->ProcessEvents();
1036 gSystem->Sleep(10);
1037 }
1038
1039 Bool_t result = editor->WasAccepted();
1040
1041 editor->GetRedrawTimer()->TurnOff();
1042
1043 // Prevent TCanvas::Close() from doing X11 operations during teardown.
1044 // Removing the canvas from the canvases list also suppresses X11 events
1045 // for it on the next ProcessEvents (otherwise the next editor's first
1046 // ProcessEvents picks up stale paint events and crashes in DrawString).
1047 // Batch mode disables remaining X11 drawing calls.
1048 // Note: removing the canvas from this list disables RecursiveRemove's
1049 // pad-primitive cleanup, so the editor destructor explicitly clears
1050 // pad primitive lists before deleting drawn objects.
1051 TCanvas *ecanvas = editor->GetEmbeddedCanvas()->GetCanvas();
1052 if (ecanvas) {
1053 gROOT->GetListOfCanvases()->Remove(ecanvas);
1054 ecanvas->SetBatch(kTRUE);
1055 }
1056
1057 editor->DontCallClose();
1058 editor->UnmapWindow();
1059 gSystem->ProcessEvents();
1060 delete editor;
1061
1062 AURestoreXErrorHandler(xerr_save);
1063 return result;
1064}
Stops a recoverable X protocol error from becoming a fatal SIGSEGV.
AUXErrorHandlerSave AUInstallTolerantXErrorHandler()
Install the tolerant X error handler for the duration of an event loop.
void AURestoreXErrorHandler(AUXErrorHandlerSave s)
Put back the handler that was in place before the tolerant one.
Bool_t LaunchInteractiveRooFitEditor(TH1 *hist, const std::vector< Double_t > *events, Float_t display_bin_width_kev, RooAbsPdf *total_pdf, RooRealVar *x, RooAbsData *data, std::vector< RooFitPeakModel > *peaks, RooFitBackgroundModel *bkg, Double_t range_low, Double_t range_high, const TString &info_label)
Open the RooFit editor and pump its event loop until the user is done.
RooCmdArg BestAvailableBackend()
The best RooFit evaluation backend this build supports.
Interactive editor for an unbinned RooFit fit from RooFitUtils.
TRootEmbeddedCanvas * GetEmbeddedCanvas()
The canvas holding the fit and residual pads.
InteractiveRooFitEditor(const TGWindow *parent, TH1 *hist, const std::vector< Double_t > *events, Float_t display_bin_width_kev, RooAbsPdf *total_pdf, RooRealVar *x, RooAbsData *data, std::vector< RooFitPeakModel > *peaks, RooFitBackgroundModel *bkg, Double_t range_low, Double_t range_high, const TString &info_label="")
Build the editor around a converged RooFit model.
virtual Bool_t ProcessMessage(Long_t msg, Long_t parm1, Long_t parm2)
ROOT GUI message dispatch for every widget in the editor.
TTimer * GetRedrawTimer()
The coalescing redraw timer, for the driving event loop.
Bool_t WasAccepted() const
Whether the user accepted rather than cancelled.
virtual void CloseWindow()
Window-manager close. Treated as a cancel, not an accept.
virtual ~InteractiveRooFitEditor()
Destroys the widgets and drawing objects the editor created.
virtual Bool_t HandleTimer(TTimer *timer)
Redraw tick.
Bool_t IsDone() const
Whether the editor has finished and the loop may exit.
static void RefillDisplayHistogram(TH1 *hist, const std::vector< Double_t > &events, Float_t fit_range_low, Float_t fit_range_high, Float_t display_bin_width_kev)
Rebin an existing display histogram in place.
static constexpr const char * kFitRangeName
Name of the RooFit range this class fits over.
What AUInstallTolerantXErrorHandler() needs in order to undo itself.
The RooFit objects making up one channel's background.
Every RooFit object making up one peak.
RooRealVar * sigma
RooFormulaVar * high_exp_yield
RooRealVar * ratio_low_exp
RooAbsPdf * low_exp_pdf
RooRealVar * ratio_low_lin
RooRealVar * slope_low_lin
RooRealVar * tau_ratio_low_exp
RooFormulaVar * low_exp_yield
RooAbsPdf * low_lin_pdf
RooRealVar * ratio_step
RooAbsPdf * gauss_pdf
RooRealVar * mu
RooAbsPdf * step_pdf
RooRealVar * tau_ratio_high_exp
RooAbsPdf * high_exp_pdf
RooFormulaVar * low_lin_yield
RooRealVar * ratio_high_exp
RooFormulaVar * step_yield
RooRealVar * gaus_yield