7#include <RooFitResult.h>
8#include <RooMsgService.h>
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,
17 const TString &info_label)
18 : TGMainFrame(parent, 1400, 900) {
22 display_bin_width_kev_ = display_bin_width_kev;
23 total_pdf_ = total_pdf;
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;
40 needs_redraw_ = kFALSE;
43 for (Int_t pi = 0; pi < num_peaks_; pi++) {
45 params_.push_back(p.
mu);
46 params_.push_back(p.
sigma);
56 params_.push_back(bkg_->bkg_yield);
57 params_.push_back(bkg_->bkg_slope);
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_);
66 for (Int_t i = 0; i < num_params_; i++) {
67 original_params_[i] = params_[i]->getVal();
68 original_fixed_[i] = params_[i]->isConstant();
71 if (params_[i]->hasMin() && params_[i]->hasMax()) {
72 lo = params_[i]->getMin();
73 hi = params_[i]->getMax();
75 GetDefaultBounds(i, lo, hi);
77 original_bounds_low_[i] = lo;
78 original_bounds_high_[i] = hi;
79 current_bounds_low_[i] = lo;
80 current_bounds_high_[i] = hi;
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_);
90 total_graph_ =
nullptr;
94 plus3_line_ =
nullptr;
95 minus3_line_ =
nullptr;
96 chi2_label_ =
nullptr;
98 for (Int_t p = 0; p < 3; p++) {
99 for (Int_t c = 0; c < 4; c++) {
100 comp_graphs_[p][c] =
nullptr;
107 redraw_timer_ =
new TTimer(
this, 50);
108 redraw_timer_->TurnOn();
110 SetWindowName(
"Interactive RooFit Editor");
112 Resize(GetDefaultSize());
120 redraw_timer_->TurnOff();
121 delete redraw_timer_;
132 if (main_pad_ && main_pad_->GetListOfPrimitives()) {
133 main_pad_->GetListOfPrimitives()->Clear(
"nodelete");
135 if (residual_pad_ && residual_pad_->GetListOfPrimitives()) {
136 residual_pad_->GetListOfPrimitives()->Clear(
"nodelete");
138 for (Int_t p = 0; p < 3; p++) {
139 for (Int_t c = 0; c < 4; c++) {
140 delete comp_graphs_[p][c];
151 delete residual_pad_;
155void InteractiveRooFitEditor::BuildGUI() {
156 TGHorizontalFrame *main_frame =
new TGHorizontalFrame(
this, 1400, 850);
158 new TGLayoutHints(kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2));
161 new TRootEmbeddedCanvas(
"RooFitEditorCanvas", main_frame, 850, 800);
162 main_frame->AddFrame(
164 new TGLayoutHints(kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2));
166 TGVerticalFrame *controls =
new TGVerticalFrame(main_frame, 520, 800);
167 main_frame->AddFrame(
168 controls,
new TGLayoutHints(kLHintsExpandY | kLHintsRight, 2, 2, 2, 2));
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));
178 TGGroupFrame *range_grp =
179 new TGGroupFrame(controls,
"Fit Range", kVerticalFrame);
180 controls->AddFrame(range_grp,
new TGLayoutHints(kLHintsExpandX, 3, 3, 3, 3));
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));
190 TGHorizontalFrame *range_entries =
new TGHorizontalFrame(range_grp, 200, 28);
191 range_grp->AddFrame(range_entries,
192 new TGLayoutHints(kLHintsExpandX, 2, 2, 2, 2));
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(
203 new TGLayoutHints(kLHintsExpandX | kLHintsCenterY, 2, 2, 2, 2));
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(
214 new TGLayoutHints(kLHintsExpandX | kLHintsCenterY, 2, 2, 2, 2));
216 TGTab *tabs =
new TGTab(controls, 510, 700);
218 tabs,
new TGLayoutHints(kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2));
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);
226 TGCompositeFrame *bkg_tab = tabs->AddTab(
"Background");
227 BuildBackgroundTab(bkg_tab);
229 TGHorizontalFrame *btn_frame =
new TGHorizontalFrame(controls, 510, 40);
231 btn_frame,
new TGLayoutHints(kLHintsExpandX | kLHintsBottom, 2, 2, 5, 5));
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);
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);
249void InteractiveRooFitEditor::BuildPeakTab(TGCompositeFrame *parent,
251 Int_t offset = peak_idx * 10;
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");
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");
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");
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");
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");
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));
291 Double_t val = params_[param_idx]->getVal();
292 Bool_t fixed = original_fixed_[param_idx];
294 TGLabel *label =
new TGLabel(row, name);
295 row->AddFrame(label,
new TGLayoutHints(kLHintsCenterY, 2, 4, 2, 2));
296 label->Resize(90, 20);
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;
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;
314 TGCheckButton *fix_cb =
new TGCheckButton(row,
"Fix", kFixBase + param_idx);
315 fix_cb->Associate(
this);
317 fix_cb->SetState(kButtonDown);
319 row->AddFrame(fix_cb,
new TGLayoutHints(kLHintsCenterY, 2, 2, 2, 2));
320 fix_checks_[param_idx] = fix_cb;
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;
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;
339 slider->SetEnabled(kFALSE);
340 entry->GetNumberEntry()->SetEnabled(kFALSE);
344Double_t InteractiveRooFitEditor::EvalPdfDensity(RooAbsPdf *pdf, Double_t xv) {
345 Double_t saved = x_->getVal();
348 Double_t v = pdf->getVal(&nset);
353Double_t InteractiveRooFitEditor::ComponentExpected(RooAbsPdf *pdf,
356 Double_t bin_width = hist_->GetBinWidth(1);
357 return yield * EvalPdfDensity(pdf, xv) * bin_width;
360void InteractiveRooFitEditor::InitDrawing() {
361 TCanvas *canvas = embedded_canvas_->GetCanvas();
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);
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();
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);
391 total_graph_ =
new TGraph(kNDrawPts);
392 total_graph_->SetLineColor(kAzure);
393 total_graph_->SetLineWidth(2);
394 total_graph_->Draw(
"L same");
396 bkg_graph_ =
new TGraph(kNDrawPts);
397 bkg_graph_->SetLineColor(kGreen);
398 bkg_graph_->SetLineWidth(2);
399 bkg_graph_->Draw(
"L same");
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");
412 chi2_label_ =
new TLatex();
413 chi2_label_->SetNDC();
414 chi2_label_->SetTextSize(0.045);
415 chi2_label_->SetTextAlign(31);
418 main_pad_->SetLogy(kTRUE);
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_)
428 Double_t data = hist_->GetBinContent(i);
429 Double_t error = hist_->GetBinError(i);
430 if (error > 0 && data > 0)
433 if (n_res_points_ < 1)
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(
"");
444 hist_draw_->GetXaxis()->GetBinLowEdge(hist_draw_->GetXaxis()->GetFirst());
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");
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");
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");
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");
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();
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;
494 total_graph_->SetPoint(i, xv,
495 total_exp * total_pdf_->getVal(&nset) * bin_width);
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;
502 bkg_graph_->SetPoint(i, xv,
503 bkg_yield * bkg_->bkg_pdf->getVal(&nset) * bin_width);
506 for (Int_t pi = 0; pi < num_peaks_; pi++) {
507 RooFitPeakModel &p = (*peaks_)[pi];
514 for (Int_t i = 0; i < kNDrawPts; i++) {
515 Double_t xv = range_low_ + i * x_step;
517 Double_t bkg_v = bkg_yield * bkg_->bkg_pdf->getVal(&nset) * bin_width;
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 +
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);
534void InteractiveRooFitEditor::UpdateResPoints() {
535 Int_t nbins = hist_->GetNbinsX();
536 Double_t bin_width = hist_->GetBinWidth(1);
538 Double_t total_exp = total_pdf_->expectedEvents(&nset);
539 Double_t saved = x_->getVal();
542 for (Int_t i = 1; i <= nbins; i++) {
543 Double_t xv = hist_->GetBinCenter(i);
544 if (xv < range_low_ || xv > range_high_)
546 Double_t data = hist_->GetBinContent(i);
547 Double_t error = hist_->GetBinError(i);
548 if (error > 0 && data > 0) {
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);
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);
566void InteractiveRooFitEditor::UpdateCanvas() {
570 Double_t chi2_sum = 0;
572 Int_t nbins = hist_->GetNbinsX();
573 Double_t bin_width = hist_->GetBinWidth(1);
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_)
581 Double_t data = hist_->GetBinContent(i);
582 Double_t error = hist_->GetBinError(i);
583 if (error > 0 && data > 0) {
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;
593 for (Int_t i = 0; i < num_params_; i++) {
597 Int_t ndf = ndf_bins - n_free;
599 chi2_label_->SetText(0.85, 0.85,
600 Form(
"#chi^{2}/ndf = %.3f", chi2_sum / ndf));
602 chi2_label_->SetText(0.85, 0.85,
"#chi^{2}/ndf = N/A");
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);
610 TCanvas *canvas = embedded_canvas_->GetCanvas();
611 main_pad_->Modified();
612 residual_pad_->Modified();
616 needs_redraw_ = kFALSE;
619void InteractiveRooFitEditor::SyncAllWidgets() {
621 for (Int_t i = 0; i < num_params_; i++) {
627void InteractiveRooFitEditor::SyncWidget(Int_t param_idx) {
628 Double_t val = params_[param_idx]->getVal();
629 Bool_t fixed = params_[param_idx]->isConstant();
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();
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];
642 sliders_[param_idx]->SetPosition(ValToSlider(param_idx, clamped));
643 value_entries_[param_idx]->SetNumber(val);
646 fix_checks_[param_idx]->SetState(kButtonDown);
647 sliders_[param_idx]->SetEnabled(kFALSE);
648 value_entries_[param_idx]->GetNumberEntry()->SetEnabled(kFALSE);
650 fix_checks_[param_idx]->SetState(kButtonUp);
651 sliders_[param_idx]->SetEnabled(kTRUE);
652 value_entries_[param_idx]->GetNumberEntry()->SetEnabled(kTRUE);
655 lo_bound_entries_[param_idx]->SetNumber(current_bounds_low_[param_idx]);
656 hi_bound_entries_[param_idx]->SetNumber(current_bounds_high_[param_idx]);
659void InteractiveRooFitEditor::OnSliderMoved(Int_t param_idx) {
662 if (IsFixed(param_idx))
665 Int_t pos = sliders_[param_idx]->GetPosition();
666 Double_t val = SliderToVal(param_idx, pos);
667 params_[param_idx]->setVal(val);
670 value_entries_[param_idx]->SetNumber(val);
673 needs_redraw_ = kTRUE;
676void InteractiveRooFitEditor::OnEntryChanged(Int_t param_idx) {
679 if (IsFixed(param_idx))
682 Double_t val = value_entries_[param_idx]->GetNumber();
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;
689 if (val > current_bounds_high_[param_idx]) {
690 current_bounds_high_[param_idx] = val;
691 bounds_changed = kTRUE;
694 params_[param_idx]->setRange(current_bounds_low_[param_idx],
695 current_bounds_high_[param_idx]);
696 params_[param_idx]->setVal(val);
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]);
706 needs_redraw_ = kTRUE;
709void InteractiveRooFitEditor::OnBoundsChanged(Int_t param_idx) {
713 Double_t new_lo = lo_bound_entries_[param_idx]->GetNumber();
714 Double_t new_hi = hi_bound_entries_[param_idx]->GetNumber();
716 if (new_lo >= new_hi)
719 current_bounds_low_[param_idx] = new_lo;
720 current_bounds_high_[param_idx] = new_hi;
722 Double_t val = params_[param_idx]->getVal();
728 if (!IsFixed(param_idx)) {
729 params_[param_idx]->setRange(new_lo, new_hi);
731 params_[param_idx]->setVal(val);
734 sliders_[param_idx]->SetPosition(ValToSlider(param_idx, val));
735 value_entries_[param_idx]->SetNumber(val);
738 needs_redraw_ = kTRUE;
741void InteractiveRooFitEditor::OnFixToggled(Int_t param_idx) {
742 Bool_t now_fixed = (fix_checks_[param_idx]->GetState() == kButtonDown);
745 params_[param_idx]->setConstant(kTRUE);
746 sliders_[param_idx]->SetEnabled(kFALSE);
747 value_entries_[param_idx]->GetNumberEntry()->SetEnabled(kFALSE);
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);
756 needs_redraw_ = kTRUE;
759void InteractiveRooFitEditor::OnRangeChanged() {
763 Double_t new_lo = range_slider_->GetMinPosition();
764 Double_t new_hi = range_slider_->GetMaxPosition();
766 if (new_lo >= new_hi)
770 range_high_ = new_hi;
772 x_->setRange(range_low_, range_high_);
777 hist_draw_, *events_, (Float_t)range_low_, (Float_t)range_high_,
778 display_bin_width_kev_);
780 hist_draw_->GetXaxis()->SetRangeUser(0.9 * range_low_, 1.1 * range_high_);
783 range_lo_entry_->SetNumber(range_low_);
784 range_hi_entry_->SetNumber(range_high_);
787 needs_redraw_ = kTRUE;
790void InteractiveRooFitEditor::DoRefit() {
791 for (Int_t i = 0; i < num_params_; i++) {
793 params_[i]->setRange(current_bounds_low_[i], current_bounds_high_[i]);
797 RooFitResult *res = total_pdf_->fitTo(
798 *data_, RooFit::Save(kTRUE), RooFit::Extended(kTRUE),
800 RooFit::PrintLevel(-1), RooFit::PrintEvalErrors(-1), RooFit::Strategy(2),
806 needs_redraw_ = kTRUE;
810void InteractiveRooFitEditor::DoAccept() {
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);
821 params_[i]->setRange(original_bounds_low_[i], original_bounds_high_[i]);
822 params_[i]->setConstant(kFALSE);
825 x_->setRange(original_range_low_, original_range_high_);
827 original_range_high_);
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);
840 params_[i]->setRange(original_bounds_low_[i], original_bounds_high_[i]);
841 params_[i]->setConstant(kFALSE);
845 range_low_ = original_range_low_;
846 range_high_ = original_range_high_;
847 x_->setRange(range_low_, range_high_);
849 hist_draw_->GetXaxis()->SetRangeUser(0.9 * range_low_, 1.1 * range_high_);
851 range_slider_->SetPosition(range_low_, range_high_);
852 range_lo_entry_->SetNumber(range_low_);
853 range_hi_entry_->SetNumber(range_high_);
857 needs_redraw_ = kTRUE;
868 switch (GET_MSG(msg)) {
870 switch (GET_SUBMSG(msg)) {
872 if (parm1 == kBtnRefit)
874 else if (parm1 == kBtnAccept)
876 else if (parm1 == kBtnCancel)
878 else if (parm1 == kBtnReset)
881 case kCM_CHECKBUTTON:
882 if (parm1 >= kFixBase && parm1 < kFixBase + num_params_) {
883 OnFixToggled(parm1 - kFixBase);
889 if (parm1 == kRangeSlider) {
891 }
else if (parm1 >= kSliderBase && parm1 < kSliderBase + num_params_) {
892 OnSliderMoved(parm1 - kSliderBase);
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) {
902 range_slider_->SetPosition(new_lo, new_hi);
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);
921 if (timer == redraw_timer_ && needs_redraw_) {
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];
934 Double_t frac = (val - lo) / (hi - lo);
939 return static_cast<Int_t
>(frac * kSliderRes);
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);
949Bool_t InteractiveRooFitEditor::IsFixed(Int_t param_idx) {
950 return (fix_checks_[param_idx]->GetState() == kButtonDown);
953void InteractiveRooFitEditor::GetDefaultBounds(Int_t param_idx, Double_t &lo,
955 Double_t peak_height = hist_->GetMaximum();
956 Double_t range_width = range_high_ - range_low_;
958 if (param_idx == BkgConstIdx()) {
960 hi = peak_height * range_width * 10.0;
963 if (param_idx == BkgSlopeIdx()) {
969 Int_t local = param_idx % 10;
976 lo = range_width * 0.001;
977 hi = range_width * 0.5;
981 hi = peak_height * range_width * 10.0;
1006Int_t InteractiveRooFitEditor::PeakStyle(Int_t peak_idx) {
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,
1019 const TString &info_label) {
1021 std::cerr <<
"InteractiveRooFitEditor: GUI not available (gClient is null)."
1031 gClient->GetRoot(), hist, events, display_bin_width_kev, total_pdf, x,
1032 data, peaks, bkg, range_low, range_high, info_label);
1034 while (!editor->
IsDone()) {
1035 gSystem->ProcessEvents();
1053 gROOT->GetListOfCanvases()->Remove(ecanvas);
1054 ecanvas->SetBatch(kTRUE);
1057 editor->DontCallClose();
1058 editor->UnmapWindow();
1059 gSystem->ProcessEvents();
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.
RooFormulaVar * high_exp_yield
RooRealVar * ratio_low_exp
RooRealVar * ratio_low_lin
RooRealVar * slope_low_lin
RooRealVar * tau_ratio_low_exp
RooFormulaVar * low_exp_yield
RooRealVar * tau_ratio_high_exp
RooFormulaVar * low_lin_yield
RooRealVar * ratio_high_exp
RooFormulaVar * step_yield