8#include <RooFitResult.h>
9#include <RooGlobalFunc.h>
10#include <RooMsgService.h>
16 const TGWindow *parent, RooSimultaneous *sim_pdf, RooAbsData *combined_data,
17 RooRealVar *x,
const std::vector<SimEditorChannelView> &channel_views,
18 Double_t range_low, Double_t range_high,
const TString &info_label,
20 : TGMainFrame(parent, 1500, 950) {
23 combined_data_ = combined_data;
25 channels_ = channel_views;
26 info_label_text_ = info_label;
27 fit_debug_ = fit_debug;
28 range_low_ = range_low;
29 range_high_ = range_high;
30 original_range_low_ = range_low;
31 original_range_high_ = range_high;
33 hist_x_min_ = channels_[0].hist->GetXaxis()->GetXmin();
34 hist_x_max_ = channels_[0].hist->GetXaxis()->GetXmax();
35 for (
size_t i = 1; i < channels_.size(); i++) {
36 Double_t cmin = channels_[i].hist->GetXaxis()->GetXmin();
37 Double_t cmax = channels_[i].hist->GetXaxis()->GetXmax();
38 if (cmin < hist_x_min_)
40 if (cmax > hist_x_max_)
46 needs_redraw_ = kFALSE;
49 for (
size_t ci = 0; ci < channels_.size(); ci++) {
52 for (Int_t pi = 0; pi < cv.
num_peaks; pi++) {
68 Int_t np = (Int_t)cv.
params.size();
81 for (Int_t i = 0; i < np; i++) {
86 lo = cv.
params[i]->getMin();
87 hi = cv.
params[i]->getMax();
89 GetDefaultBounds(ci, i, lo, hi);
109 for (Int_t p = 0; p < 3; p++) {
110 for (Int_t c = 0; c < 4; c++) {
116 ApplyBackgroundSlopeBounds();
121 redraw_timer_ =
new TTimer(
this, 50);
122 redraw_timer_->TurnOn();
124 SetWindowName(
"Interactive Simultaneous Fit Editor");
126 Resize(GetDefaultSize());
134 redraw_timer_->TurnOff();
135 delete redraw_timer_;
146 for (Int_t ci = 0; ci < (Int_t)channels_.size(); ci++) {
149 cv.
main_pad->GetListOfPrimitives()->Clear(
"nodelete");
152 cv.
residual_pad->GetListOfPrimitives()->Clear(
"nodelete");
154 for (Int_t p = 0; p < 3; p++) {
155 for (Int_t c = 0; c < 4; c++) {
172Int_t InteractiveSimultaneousFitEditor::ChannelIndexFromWidgetId(
173 Int_t base, Int_t parm1, Int_t &local_idx) {
174 Int_t offset = parm1 - base;
175 Int_t ch_idx = offset / kSliderStride;
176 local_idx = offset % kSliderStride;
177 if (ch_idx < 0 || ch_idx >= (Int_t)channels_.size())
179 if (local_idx < 0 || local_idx >= (Int_t)channels_[ch_idx].params.size())
184void InteractiveSimultaneousFitEditor::BuildGUI() {
185 TGHorizontalFrame *main_frame =
new TGHorizontalFrame(
this, 1500, 900);
187 new TGLayoutHints(kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2));
189 TGTab *outer_tabs =
new TGTab(main_frame, 1500, 900);
190 main_frame->AddFrame(
192 new TGLayoutHints(kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2));
194 for (
size_t ci = 0; ci < channels_.size(); ci++) {
195 TGCompositeFrame *channel_tab =
196 outer_tabs->AddTab(channels_[ci].name.Data());
197 BuildChannelTab(channel_tab, (Int_t)ci);
200 TGHorizontalFrame *bottom =
new TGHorizontalFrame(
this, 1500, 80);
202 new TGLayoutHints(kLHintsExpandX | kLHintsBottom, 2, 2, 2, 5));
204 if (info_label_text_.Length() > 0) {
205 TGLabel *info_label =
new TGLabel(bottom, info_label_text_);
206 bottom->AddFrame(info_label,
new TGLayoutHints(kLHintsLeft | kLHintsCenterY,
210 TGGroupFrame *range_grp =
211 new TGGroupFrame(bottom,
"Fit Range (shared)", kHorizontalFrame);
212 bottom->AddFrame(range_grp,
213 new TGLayoutHints(kLHintsLeft | kLHintsCenterY, 4, 4, 0, 0));
216 new TGDoubleHSlider(range_grp, 220, kDoubleScaleNo, kRangeSlider);
217 range_slider_->SetRange(hist_x_min_, hist_x_max_);
218 range_slider_->SetPosition(range_low_, range_high_);
219 range_slider_->Associate(
this);
220 range_grp->AddFrame(range_slider_,
221 new TGLayoutHints(kLHintsCenterY, 2, 2, 2, 2));
223 range_lo_entry_ =
new TGNumberEntry(
224 range_grp, range_low_, 7, kRangeLoEntry, TGNumberFormat::kNESReal,
225 TGNumberFormat::kNEAAnyNumber, TGNumberFormat::kNELNoLimits);
226 range_lo_entry_->GetNumberEntry()->Associate(
this);
227 range_grp->AddFrame(range_lo_entry_,
228 new TGLayoutHints(kLHintsCenterY, 2, 2, 2, 2));
230 range_hi_entry_ =
new TGNumberEntry(
231 range_grp, range_high_, 7, kRangeHiEntry, TGNumberFormat::kNESReal,
232 TGNumberFormat::kNEAAnyNumber, TGNumberFormat::kNELNoLimits);
233 range_hi_entry_->GetNumberEntry()->Associate(
this);
234 range_grp->AddFrame(range_hi_entry_,
235 new TGLayoutHints(kLHintsCenterY, 2, 2, 2, 2));
237 TGTextButton *refit_btn =
new TGTextButton(bottom,
"Refit", kBtnRefit);
238 refit_btn->Associate(
this);
239 TGTextButton *accept_btn =
new TGTextButton(bottom,
"Accept", kBtnAccept);
240 accept_btn->Associate(
this);
241 TGTextButton *cancel_btn =
new TGTextButton(bottom,
"Cancel", kBtnCancel);
242 cancel_btn->Associate(
this);
243 TGTextButton *reset_btn =
new TGTextButton(bottom,
"Reset", kBtnReset);
244 reset_btn->Associate(
this);
246 TGLayoutHints *btn_hints =
247 new TGLayoutHints(kLHintsRight | kLHintsCenterY, 4, 4, 4, 4);
248 bottom->AddFrame(reset_btn, btn_hints);
249 bottom->AddFrame(cancel_btn, btn_hints);
250 bottom->AddFrame(accept_btn, btn_hints);
251 bottom->AddFrame(refit_btn, btn_hints);
254void InteractiveSimultaneousFitEditor::BuildChannelTab(TGCompositeFrame *parent,
256 SimEditorChannelView &cv = channels_[ch_idx];
258 TGHorizontalFrame *row =
new TGHorizontalFrame(parent, 1490, 850);
260 row,
new TGLayoutHints(kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2));
262 TString canvas_name =
"SimEditorCanvas_" + cv.
name;
264 new TRootEmbeddedCanvas(canvas_name.Data(), row, 850, 800);
266 new TGLayoutHints(kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2));
268 TGVerticalFrame *controls =
new TGVerticalFrame(row, 540, 800);
269 row->AddFrame(controls,
270 new TGLayoutHints(kLHintsExpandY | kLHintsRight, 2, 2, 2, 2));
272 TGTab *peak_tabs =
new TGTab(controls, 530, 800);
275 new TGLayoutHints(kLHintsExpandX | kLHintsExpandY, 2, 2, 2, 2));
277 for (Int_t pi = 0; pi < cv.
num_peaks; pi++) {
278 TString tab_name = TString::Format(
"Peak %d", pi + 1);
279 TGCompositeFrame *pf = peak_tabs->AddTab(tab_name);
280 BuildPeakSubTab(pf, ch_idx, pi);
282 TGCompositeFrame *bf = peak_tabs->AddTab(
"Background");
283 BuildBackgroundSubTab(bf, ch_idx);
286void InteractiveSimultaneousFitEditor::BuildPeakSubTab(TGCompositeFrame *parent,
289 Int_t offset = peak_idx * 10;
291 TGGroupFrame *gaus_grp =
new TGGroupFrame(parent,
"Gaussian", kVerticalFrame);
292 parent->AddFrame(gaus_grp,
new TGLayoutHints(kLHintsExpandX, 3, 3, 3, 1));
293 AddParamRow(gaus_grp, ch_idx, offset + 0,
"Mu");
294 AddParamRow(gaus_grp, ch_idx, offset + 1,
"Sigma");
295 AddParamRow(gaus_grp, ch_idx, offset + 2,
"Yield");
297 TGGroupFrame *step_grp =
new TGGroupFrame(parent,
"Step", kVerticalFrame);
298 parent->AddFrame(step_grp,
new TGLayoutHints(kLHintsExpandX, 3, 3, 1, 1));
299 AddParamRow(step_grp, ch_idx, offset + 3,
"Step Ratio");
301 TGGroupFrame *ltail_grp =
302 new TGGroupFrame(parent,
"Low-Side Tails", kVerticalFrame);
303 parent->AddFrame(ltail_grp,
new TGLayoutHints(kLHintsExpandX, 3, 3, 1, 1));
304 AddParamRow(ltail_grp, ch_idx, offset + 4,
"Exp Amp");
305 AddParamRow(ltail_grp, ch_idx, offset + 5,
"Exp Decay/Sigma");
306 AddParamRow(ltail_grp, ch_idx, offset + 6,
"Lin Ratio");
307 AddParamRow(ltail_grp, ch_idx, offset + 7,
"Lin Slope");
309 TGGroupFrame *htail_grp =
310 new TGGroupFrame(parent,
"High-Side Tail", kVerticalFrame);
311 parent->AddFrame(htail_grp,
new TGLayoutHints(kLHintsExpandX, 3, 3, 1, 3));
312 AddParamRow(htail_grp, ch_idx, offset + 8,
"Exp Amp");
313 AddParamRow(htail_grp, ch_idx, offset + 9,
"Exp Decay/Sigma");
316void InteractiveSimultaneousFitEditor::BuildBackgroundSubTab(
317 TGCompositeFrame *parent, Int_t ch_idx) {
318 TGGroupFrame *bkg_grp =
319 new TGGroupFrame(parent,
"Background", kVerticalFrame);
320 parent->AddFrame(bkg_grp,
new TGLayoutHints(kLHintsExpandX, 3, 3, 3, 3));
321 AddParamRow(bkg_grp, ch_idx, BkgConstIdx(ch_idx),
"Yield");
322 AddParamRow(bkg_grp, ch_idx, BkgSlopeIdx(ch_idx),
"Slope");
325void InteractiveSimultaneousFitEditor::AddParamRow(TGCompositeFrame *parent,
329 SimEditorChannelView &cv = channels_[ch_idx];
330 Int_t global_id = ch_idx * kSliderStride + param_idx;
332 TGHorizontalFrame *row =
new TGHorizontalFrame(parent, 510, 28);
333 parent->AddFrame(row,
new TGLayoutHints(kLHintsExpandX, 1, 1, 1, 1));
335 Double_t val = cv.
params[param_idx]->getVal();
338 TGLabel *label =
new TGLabel(row, name);
339 row->AddFrame(label,
new TGLayoutHints(kLHintsCenterY, 2, 4, 2, 2));
340 label->Resize(90, 20);
343 new TGHSlider(row, 140, kSlider1, kSliderBase + global_id);
344 slider->SetRange(0, kSliderRes);
345 slider->SetPosition(ValToSlider(ch_idx, param_idx, val));
346 slider->Associate(
this);
347 row->AddFrame(slider,
348 new TGLayoutHints(kLHintsExpandX | kLHintsCenterY, 2, 2, 2, 2));
349 cv.
sliders[param_idx] = slider;
351 TGNumberEntry *entry =
new TGNumberEntry(
352 row, val, 8, kEntryBase + global_id, TGNumberFormat::kNESReal,
353 TGNumberFormat::kNEAAnyNumber, TGNumberFormat::kNELNoLimits);
354 entry->GetNumberEntry()->Associate(
this);
355 row->AddFrame(entry,
new TGLayoutHints(kLHintsCenterY, 2, 2, 2, 2));
358 TGCheckButton *fix_cb =
new TGCheckButton(row,
"Fix", kFixBase + global_id);
359 fix_cb->Associate(
this);
361 fix_cb->SetState(kButtonDown);
362 row->AddFrame(fix_cb,
new TGLayoutHints(kLHintsCenterY, 2, 2, 2, 2));
365 TGNumberEntry *lo_entry =
new TGNumberEntry(
367 TGNumberFormat::kNESReal, TGNumberFormat::kNEAAnyNumber,
368 TGNumberFormat::kNELNoLimits);
369 lo_entry->GetNumberEntry()->Associate(
this);
370 row->AddFrame(lo_entry,
new TGLayoutHints(kLHintsCenterY, 1, 1, 2, 2));
373 TGNumberEntry *hi_entry =
new TGNumberEntry(
375 TGNumberFormat::kNESReal, TGNumberFormat::kNEAAnyNumber,
376 TGNumberFormat::kNELNoLimits);
377 hi_entry->GetNumberEntry()->Associate(
this);
378 row->AddFrame(hi_entry,
new TGLayoutHints(kLHintsCenterY, 1, 1, 2, 2));
382 slider->SetEnabled(kFALSE);
383 entry->GetNumberEntry()->SetEnabled(kFALSE);
387void InteractiveSimultaneousFitEditor::InitDrawing() {
388 for (
size_t ci = 0; ci < channels_.size(); ci++) {
389 InitChannelDrawing(channels_[ci]);
393void InteractiveSimultaneousFitEditor::InitChannelDrawing(
398 TString main_name =
"simed_main_" + cv.
name;
399 cv.
main_pad =
new TPad(main_name.Data(), main_name.Data(), 0, 0.3, 1, 1.0);
406 TString res_name =
"simed_res_" + cv.
name;
407 cv.
residual_pad =
new TPad(res_name.Data(), res_name.Data(), 0, 0, 1, 0.3);
415 TString draw_name =
"hist_simed_" + cv.
name;
416 cv.
hist_draw =
static_cast<TH1 *
>(cv.
hist->Clone(draw_name.Data()));
418 cv.
hist_draw->GetXaxis()->SetRangeUser(0.9 * range_low_, 1.1 * range_high_);
419 cv.
hist_draw->GetXaxis()->SetLabelSize(0);
420 cv.
hist_draw->GetXaxis()->SetTitleSize(0);
435 Int_t comp_colors[4] = {kBlack, kGray, kRed, kOrange};
436 for (Int_t p = 0; p < cv.
num_peaks; p++) {
437 for (Int_t c = 0; c < 4; c++) {
439 cv.
comp_graphs[p][c]->SetLineColor(comp_colors[c]);
455 Int_t nbh = cv.
hist->GetNbinsX();
457 for (Int_t i = 1; i <= nbh; i++) {
458 Double_t xv = cv.
hist->GetBinCenter(i);
459 if (xv < range_low_ || xv > range_high_)
461 Double_t d = cv.
hist->GetBinContent(i);
462 Double_t e = cv.
hist->GetBinError(i);
476 Double_t ax_min = 0.9 * range_low_;
477 Double_t ax_max = 1.1 * range_high_;
478 cv.
res_graph->GetXaxis()->SetLimits(ax_min, ax_max);
479 cv.
res_graph->GetYaxis()->SetTitle(
"#delta/#sigma");
480 cv.
res_graph->GetXaxis()->SetTitle(cv.
hist->GetXaxis()->GetTitle());
481 cv.
res_graph->GetXaxis()->SetTitleSize(0.13);
482 cv.
res_graph->GetYaxis()->SetTitleSize(0.13);
483 cv.
res_graph->GetXaxis()->SetLabelSize(0.12);
484 cv.
res_graph->GetYaxis()->SetLabelSize(0.12);
485 cv.
res_graph->GetXaxis()->SetTitleOffset(1.0);
486 cv.
res_graph->GetYaxis()->SetTitleOffset(0.3);
487 cv.
res_graph->GetYaxis()->SetNdivisions(505);
488 cv.
res_graph->GetXaxis()->SetNdivisions(510);
489 cv.
res_graph->GetYaxis()->CenterTitle(kTRUE);
490 cv.
res_graph->GetYaxis()->SetRangeUser(-5.5, 5.5);
493 TString zero_name =
"zero_simed_" + cv.
name;
494 cv.
zero_line =
new TF1(zero_name.Data(),
"0", ax_min, ax_max);
500 TString plus3_name =
"plus3_simed_" + cv.
name;
501 cv.
plus3_line =
new TF1(plus3_name.Data(),
"3", ax_min, ax_max);
507 TString minus3_name =
"minus3_simed_" + cv.
name;
508 cv.
minus3_line =
new TF1(minus3_name.Data(),
"-3", ax_min, ax_max);
515void InteractiveSimultaneousFitEditor::UpdateCanvases() {
516 for (
size_t ci = 0; ci < channels_.size(); ci++) {
517 UpdateChannelGraphs(channels_[ci]);
518 UpdateChannelResiduals(channels_[ci]);
519 UpdateChannelChi2(channels_[ci]);
521 TCanvas *canvas = channels_[ci].embedded_canvas->GetCanvas();
522 channels_[ci].main_pad->Modified();
523 channels_[ci].residual_pad->Modified();
527 needs_redraw_ = kFALSE;
530void InteractiveSimultaneousFitEditor::UpdateChannelGraphs(
532 Double_t x_step = (range_high_ - range_low_) / (kNDrawPts - 1);
533 Double_t bin_width = cv.
hist->GetBinWidth(1);
534 Double_t saved = x_->getVal();
537 Double_t total_exp = cv.
pdf->expectedEvents(&nset);
538 for (Int_t i = 0; i < kNDrawPts; i++) {
539 Double_t xv = range_low_ + i * x_step;
542 total_exp * cv.
pdf->getVal(&nset) * bin_width);
546 for (Int_t i = 0; i < kNDrawPts; i++) {
547 Double_t xv = range_low_ + i * x_step;
550 i, xv, bkg_yield * cv.
bkg->
bkg_pdf->getVal(&nset) * bin_width);
553 for (Int_t pi = 0; pi < cv.
num_peaks; pi++) {
554 RooFitPeakModel &p = (*cv.
peaks)[pi];
561 for (Int_t i = 0; i < kNDrawPts; i++) {
562 Double_t xv = range_low_ + i * x_step;
564 Double_t bkg_v = bkg_yield * cv.
bkg->
bkg_pdf->getVal(&nset) * bin_width;
567 i, xv, gy * p.
gauss_pdf->getVal(&nset) * bin_width + bkg_v);
569 i, xv, sy * p.
step_pdf->getVal(&nset) * bin_width + bkg_v);
570 Double_t y_low = lexp_y * p.
low_exp_pdf->getVal(&nset) * bin_width +
572 cv.
comp_graphs[pi][2]->SetPoint(i, xv, y_low + bkg_v);
574 i, xv, hexp_y * p.
high_exp_pdf->getVal(&nset) * bin_width + bkg_v);
581void InteractiveSimultaneousFitEditor::UpdateChannelResiduals(
583 Int_t nbh = cv.
hist->GetNbinsX();
584 Double_t bin_width = cv.
hist->GetBinWidth(1);
586 Double_t total_exp = cv.
pdf->expectedEvents(&nset);
587 Double_t saved = x_->getVal();
590 for (Int_t i = 1; i <= nbh; i++) {
591 Double_t xv = cv.
hist->GetBinCenter(i);
592 if (xv < range_low_ || xv > range_high_)
594 Double_t d = cv.
hist->GetBinContent(i);
595 Double_t e = cv.
hist->GetBinError(i);
596 if (e > 0 && d > 0) {
598 Double_t fit_val = total_exp * cv.
pdf->getVal(&nset) * bin_width;
599 Double_t pull = (d - fit_val) / e;
607 Double_t ax_min = 0.9 * range_low_;
608 Double_t ax_max = 1.1 * range_high_;
609 cv.
res_graph->GetXaxis()->SetLimits(ax_min, ax_max);
613 cv.
res_graph->GetYaxis()->SetRangeUser(-5.5, 5.5);
618void InteractiveSimultaneousFitEditor::UpdateChannelChi2(
620 Double_t chi2_sum = 0;
622 Int_t nbh = cv.
hist->GetNbinsX();
623 Double_t bin_width = cv.
hist->GetBinWidth(1);
625 Double_t total_exp = cv.
pdf->expectedEvents(&nset);
626 Double_t saved = x_->getVal();
627 for (Int_t i = 1; i <= nbh; i++) {
628 Double_t xv = cv.
hist->GetBinCenter(i);
629 if (xv < range_low_ || xv > range_high_)
631 Double_t d = cv.
hist->GetBinContent(i);
632 Double_t e = cv.
hist->GetBinError(i);
633 if (e > 0 && d > 0) {
635 Double_t fit_val = total_exp * cv.
pdf->getVal(&nset) * bin_width;
636 Double_t r = (d - fit_val) / e;
644 for (Int_t i = 0; i < (Int_t)cv.
params.size(); i++) {
645 if (!IsFixed((Int_t)(&cv - channels_.data()), i))
648 Int_t ndf = ndf_bins - n_free;
651 Form(
"#chi^{2}/ndf = %.3f", chi2_sum / ndf));
653 cv.
chi2_label->SetText(0.85, 0.85,
"#chi^{2}/ndf = N/A");
656void InteractiveSimultaneousFitEditor::SyncAllWidgets() {
658 for (
size_t ci = 0; ci < channels_.size(); ci++) {
659 for (Int_t i = 0; i < (Int_t)channels_[ci].params.size(); i++) {
660 SyncChannelWidget((Int_t)ci, i);
666void InteractiveSimultaneousFitEditor::SyncChannelWidget(Int_t ch_idx,
668 SimEditorChannelView &cv = channels_[ch_idx];
669 Double_t val = cv.
params[param_idx]->getVal();
670 Bool_t fixed = cv.
params[param_idx]->isConstant();
672 if (!fixed && cv.
params[param_idx]->hasMin() &&
673 cv.
params[param_idx]->hasMax()) {
678 Double_t clamped = val;
684 cv.
sliders[param_idx]->SetPosition(ValToSlider(ch_idx, param_idx, clamped));
688 cv.
fix_checks[param_idx]->SetState(kButtonDown);
689 cv.
sliders[param_idx]->SetEnabled(kFALSE);
690 cv.
value_entries[param_idx]->GetNumberEntry()->SetEnabled(kFALSE);
692 cv.
fix_checks[param_idx]->SetState(kButtonUp);
693 cv.
sliders[param_idx]->SetEnabled(kTRUE);
694 cv.
value_entries[param_idx]->GetNumberEntry()->SetEnabled(kTRUE);
701void InteractiveSimultaneousFitEditor::OnSliderMoved(Int_t ch_idx,
705 if (IsFixed(ch_idx, param_idx))
708 SimEditorChannelView &cv = channels_[ch_idx];
709 Int_t pos = cv.
sliders[param_idx]->GetPosition();
710 Double_t val = SliderToVal(ch_idx, param_idx, pos);
711 cv.
params[param_idx]->setVal(val);
717 needs_redraw_ = kTRUE;
720void InteractiveSimultaneousFitEditor::OnEntryChanged(Int_t ch_idx,
724 if (IsFixed(ch_idx, param_idx))
727 SimEditorChannelView &cv = channels_[ch_idx];
730 Bool_t bounds_changed = kFALSE;
733 bounds_changed = kTRUE;
737 bounds_changed = kTRUE;
742 cv.
params[param_idx]->setVal(val);
745 cv.
sliders[param_idx]->SetPosition(ValToSlider(ch_idx, param_idx, val));
746 if (bounds_changed) {
753 needs_redraw_ = kTRUE;
756void InteractiveSimultaneousFitEditor::OnBoundsChanged(Int_t ch_idx,
761 SimEditorChannelView &cv = channels_[ch_idx];
764 if (new_lo >= new_hi)
770 Double_t val = cv.
params[param_idx]->getVal();
776 if (!IsFixed(ch_idx, param_idx)) {
777 cv.
params[param_idx]->setRange(new_lo, new_hi);
779 cv.
params[param_idx]->setVal(val);
782 cv.
sliders[param_idx]->SetPosition(ValToSlider(ch_idx, param_idx, val));
786 needs_redraw_ = kTRUE;
789void InteractiveSimultaneousFitEditor::OnFixToggled(Int_t ch_idx,
791 SimEditorChannelView &cv = channels_[ch_idx];
792 Bool_t now_fixed = (cv.
fix_checks[param_idx]->GetState() == kButtonDown);
795 cv.
params[param_idx]->setConstant(kTRUE);
796 cv.
sliders[param_idx]->SetEnabled(kFALSE);
797 cv.
value_entries[param_idx]->GetNumberEntry()->SetEnabled(kFALSE);
801 cv.
params[param_idx]->setConstant(kFALSE);
802 cv.
sliders[param_idx]->SetEnabled(kTRUE);
803 cv.
value_entries[param_idx]->GetNumberEntry()->SetEnabled(kTRUE);
805 needs_redraw_ = kTRUE;
808void InteractiveSimultaneousFitEditor::OnRangeChanged() {
811 Double_t new_lo = range_slider_->GetMinPosition();
812 Double_t new_hi = range_slider_->GetMaxPosition();
813 if (new_lo >= new_hi)
817 range_high_ = new_hi;
819 x_->setRange(range_low_, range_high_);
821 ApplyBackgroundSlopeBounds();
823 for (
size_t ci = 0; ci < channels_.size(); ci++) {
824 SimEditorChannelView &cv = channels_[ci];
830 cv.
hist_draw->GetXaxis()->SetRangeUser(0.9 * range_low_, 1.1 * range_high_);
834 range_lo_entry_->SetNumber(range_low_);
835 range_hi_entry_->SetNumber(range_high_);
838 needs_redraw_ = kTRUE;
841void InteractiveSimultaneousFitEditor::DoRefit() {
842 for (
size_t ci = 0; ci < channels_.size(); ci++) {
843 SimEditorChannelView &cv = channels_[ci];
844 for (Int_t i = 0; i < (Int_t)cv.
params.size(); i++) {
845 if (!IsFixed((Int_t)ci, i)) {
855 RooAbsReal::setEvalErrorLoggingMode(RooAbsReal::PrintErrors);
857 sim_pdf_->createNLL(*combined_data_, RooFit::Extended(kTRUE),
859 Double_t nll_seed = (nll != 0) ? nll->getVal() : 0.0;
860 std::cout <<
"=== AU_ROOFIT_FIT_DEBUG: pre-refit NLL = " << nll_seed
861 << (std::isfinite(nll_seed) ?
"" :
" <== NON-FINITE")
862 <<
" ===" << std::endl;
865 DiagnoseInvalidComponents();
868 Int_t print_level = fit_debug_ ? 1 : -1;
869 Int_t eval_errors = fit_debug_ ? 10 : -1;
870 RooFitResult *res = sim_pdf_->fitTo(
871 *combined_data_, RooFit::Save(kTRUE), RooFit::Extended(kTRUE),
873 RooFit::PrintLevel(print_level), RooFit::PrintEvalErrors(eval_errors),
874 RooFit::Strategy(1), RooFit::Minimizer(
"Minuit2",
"migrad"),
880 needs_redraw_ = kTRUE;
884void InteractiveSimultaneousFitEditor::DoAccept() {
889void InteractiveSimultaneousFitEditor::DoCancel() {
890 for (
size_t ci = 0; ci < channels_.size(); ci++) {
891 SimEditorChannelView &cv = channels_[ci];
892 for (Int_t i = 0; i < (Int_t)cv.
params.size(); i++) {
895 cv.
params[i]->setConstant(kTRUE);
899 cv.
params[i]->setConstant(kFALSE);
903 x_->setRange(original_range_low_, original_range_high_);
905 original_range_high_);
910void InteractiveSimultaneousFitEditor::DoReset() {
911 for (
size_t ci = 0; ci < channels_.size(); ci++) {
912 SimEditorChannelView &cv = channels_[ci];
913 for (Int_t i = 0; i < (Int_t)cv.
params.size(); i++) {
918 cv.
params[i]->setConstant(kTRUE);
922 cv.
params[i]->setConstant(kFALSE);
926 range_low_ = original_range_low_;
927 range_high_ = original_range_high_;
928 x_->setRange(range_low_, range_high_);
930 ApplyBackgroundSlopeBounds();
931 for (
size_t ci = 0; ci < channels_.size(); ci++) {
932 channels_[ci].hist_draw->GetXaxis()->SetRangeUser(0.9 * range_low_,
936 range_slider_->SetPosition(range_low_, range_high_);
937 range_lo_entry_->SetNumber(range_low_);
938 range_hi_entry_->SetNumber(range_high_);
942 needs_redraw_ = kTRUE;
946void InteractiveSimultaneousFitEditor::ApplyBackgroundSlopeBounds() {
953 if (range_high_ <= 0)
955 Double_t slope_lo = -0.9 / range_high_;
956 for (
size_t ci = 0; ci < channels_.size(); ci++) {
957 SimEditorChannelView &cv = channels_[ci];
958 Int_t si = BkgSlopeIdx((Int_t)ci);
959 if (si < 0 || si >= (Int_t)cv.
params.size())
961 RooRealVar *slope = cv.
params[si];
962 if (slope->isConstant())
968 if (slope->getVal() < slope_lo)
969 slope->setVal(slope_lo);
970 slope->setRange(slope_lo, hi);
974void InteractiveSimultaneousFitEditor::DiagnoseInvalidComponents() {
976 for (
size_t ci = 0; ci < channels_.size(); ci++) {
977 SimEditorChannelView &cv = channels_[ci];
978 std::cout <<
" channel '" << cv.
name <<
"' components:" << std::endl;
981 for (
size_t pi = 0; pi < cv.
peaks->size(); pi++) {
982 RooFitPeakModel &p = (*cv.
peaks)[pi];
985 const char *names[5] = {
"gauss",
"step",
"lowExp",
"lowLin",
"highExp"};
986 for (Int_t k = 0; k < 5; k++) {
990 comps[k]->createIntegral(nset, RooFit::NormSet(nset),
992 Double_t nrm = (integ != 0) ? integ->getVal() : 0.0;
995 if (!std::isfinite(nrm) || nrm <= 0.0)
996 std::cout <<
" [BAD NORM] peak" << (pi + 1) <<
"." << names[k]
997 <<
" integral=" << nrm << std::endl;
1002 const Int_t n_samp = 4000;
1003 Double_t step_x = (range_high_ - range_low_) / (Double_t)(n_samp - 1);
1005 for (Int_t i = 0; i < n_samp && reported < 8; i++) {
1006 Double_t xv = range_low_ + i * step_x;
1008 for (
size_t pi = 0; pi < cv.
peaks->size() && reported < 8; pi++) {
1009 RooFitPeakModel &p = (*cv.
peaks)[pi];
1012 const char *names[5] = {
"gauss",
"step",
"lowExp",
"lowLin",
"highExp"};
1013 for (Int_t k = 0; k < 5; k++) {
1016 Double_t r = comps[k]->getVal();
1017 if (!std::isfinite(r)) {
1018 std::cout <<
" [BAD RAW] peak" << (pi + 1) <<
"." << names[k]
1019 <<
" raw=" << r <<
" at x=" << xv << std::endl;
1035 switch (GET_MSG(msg)) {
1037 switch (GET_SUBMSG(msg)) {
1039 if (parm1 == kBtnRefit)
1041 else if (parm1 == kBtnAccept)
1043 else if (parm1 == kBtnCancel)
1045 else if (parm1 == kBtnReset)
1048 case kCM_CHECKBUTTON: {
1050 Int_t ch = ChannelIndexFromWidgetId(kFixBase, parm1, local);
1052 OnFixToggled(ch, local);
1058 if (parm1 == kRangeSlider) {
1062 Int_t ch = ChannelIndexFromWidgetId(kSliderBase, parm1, local);
1064 OnSliderMoved(ch, local);
1068 if (GET_SUBMSG(msg) == kTE_ENTER || GET_SUBMSG(msg) == kTE_TAB) {
1069 if (parm1 == kRangeLoEntry || parm1 == kRangeHiEntry) {
1070 Double_t new_lo = range_lo_entry_->GetNumber();
1071 Double_t new_hi = range_hi_entry_->GetNumber();
1072 if (new_lo < new_hi) {
1074 range_slider_->SetPosition(new_lo, new_hi);
1080 Int_t ch = ChannelIndexFromWidgetId(kEntryBase, parm1, local);
1082 OnEntryChanged(ch, local);
1084 ch = ChannelIndexFromWidgetId(kLoBoundBase, parm1, local);
1086 OnBoundsChanged(ch, local);
1088 ch = ChannelIndexFromWidgetId(kHiBoundBase, parm1, local);
1090 OnBoundsChanged(ch, local);
1101 if (timer == redraw_timer_ && needs_redraw_) {
1109Int_t InteractiveSimultaneousFitEditor::ValToSlider(Int_t ch_idx,
1117 Double_t frac = (val - lo) / (hi - lo);
1122 return static_cast<Int_t
>(frac * kSliderRes);
1125Double_t InteractiveSimultaneousFitEditor::SliderToVal(Int_t ch_idx,
1128 SimEditorChannelView &cv = channels_[ch_idx];
1131 Double_t frac =
static_cast<Double_t
>(pos) / kSliderRes;
1132 return lo + frac * (hi - lo);
1135Bool_t InteractiveSimultaneousFitEditor::IsFixed(Int_t ch_idx,
1137 return (channels_[ch_idx].fix_checks[param_idx]->GetState() == kButtonDown);
1140void InteractiveSimultaneousFitEditor::GetDefaultBounds(Int_t ch_idx,
1144 SimEditorChannelView &cv = channels_[ch_idx];
1145 Double_t peak_height = cv.
hist->GetMaximum();
1146 Double_t range_width = range_high_ - range_low_;
1148 if (param_idx == BkgConstIdx(ch_idx)) {
1150 hi = peak_height * range_width * 10.0;
1153 if (param_idx == BkgSlopeIdx(ch_idx)) {
1159 Int_t local = param_idx % 10;
1166 lo = cv.
hist->GetBinWidth(1);
1167 hi = range_width * 0.1;
1171 hi = peak_height * range_width * 10.0;
1196Int_t InteractiveSimultaneousFitEditor::PeakStyle(Int_t peak_idx) {
1205 RooSimultaneous *sim_pdf, RooAbsData *combined_data, RooRealVar *x,
1206 std::vector<SimEditorChannelView> &channel_views, Double_t range_low,
1207 Double_t range_high,
const TString &info_label, Bool_t fit_debug) {
1209 std::cerr <<
"InteractiveSimultaneousFitEditor: GUI not available"
1220 gClient->GetRoot(), sim_pdf, combined_data, x, channel_views,
1221 range_low, range_high, info_label, fit_debug);
1223 while (!editor->
IsDone()) {
1224 gSystem->ProcessEvents();
1239 std::vector<SimEditorChannelView> *cvs = editor->
GetChannels();
1240 for (
size_t ci = 0; ci < cvs->size(); ci++) {
1241 TCanvas *c = (*cvs)[ci].embedded_canvas->GetCanvas();
1243 gROOT->GetListOfCanvases()->Remove(c);
1248 editor->DontCallClose();
1249 editor->UnmapWindow();
1250 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 LaunchInteractiveSimultaneousFitEditor(RooSimultaneous *sim_pdf, RooAbsData *combined_data, RooRealVar *x, std::vector< SimEditorChannelView > &channel_views, Double_t range_low, Double_t range_high, const TString &info_label, Bool_t fit_debug)
Open the simultaneous editor and pump its event loop.
RooCmdArg BestAvailableBackend()
The best RooFit evaluation backend this build supports.
Interactive editor for a multi-channel simultaneous RooFit fit.
virtual void CloseWindow()
Window-manager close. Treated as a cancel, not an accept.
Bool_t WasAccepted() const
Whether the user accepted rather than cancelled.
std::vector< SimEditorChannelView > * GetChannels()
The per-channel views, carrying the edited parameters.
virtual Bool_t HandleTimer(TTimer *timer)
Redraw tick.
TTimer * GetRedrawTimer()
The coalescing redraw timer, for the driving event loop.
InteractiveSimultaneousFitEditor(const TGWindow *parent, RooSimultaneous *sim_pdf, RooAbsData *combined_data, RooRealVar *x, const std::vector< SimEditorChannelView > &channel_views, Double_t range_low, Double_t range_high, const TString &info_label="", Bool_t fit_debug=kFALSE)
Build the editor around a converged simultaneous model.
virtual ~InteractiveSimultaneousFitEditor()
Destroys the widgets and drawing objects the editor created.
virtual Bool_t ProcessMessage(Long_t msg, Long_t parm1, Long_t parm2)
ROOT GUI message dispatch for every widget in the editor.
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.
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
Everything the simultaneous editor needs to show one channel.
std::vector< Double_t > original_bounds_high
std::vector< TGNumberEntry * > lo_bound_entries
std::vector< TGCheckButton * > fix_checks
Float_t display_bin_width_kev
Display bin width.
const std::vector< Double_t > * events
Event-level values behind hist, so the display can be rebinned live as the fit range changes.
RooFitBackgroundModel * bkg
Background model.
std::vector< Double_t > current_bounds_low
std::vector< Double_t > original_bounds_low
std::vector< Double_t > original_params
std::vector< Double_t > current_bounds_high
std::vector< TGNumberEntry * > hi_bound_entries
TH1 * hist
Display histogram for this channel.
std::vector< TGNumberEntry * > value_entries
std::vector< Bool_t > original_fixed
std::vector< TGHSlider * > sliders
RooAbsPdf * pdf
This channel's summed model.
std::vector< RooRealVar * > params
TGraph * comp_graphs[3][4]
Int_t num_peaks
Peaks in this channel.
TRootEmbeddedCanvas * embedded_canvas
std::vector< RooFitPeakModel > * peaks
Per-peak parameter models.
TString name
Channel name, shown on the tab.