Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
xRooNode.cxx
Go to the documentation of this file.
1/*
2 * Project: xRooFit
3 * Author:
4 * Will Buttinger, RAL 2022
5 *
6 * Copyright (c) 2022, CERN
7 *
8 * Redistribution and use in source and binary forms,
9 * with or without modification, are permitted according to the terms
10 * listed in LICENSE (http://roofit.sourceforge.net/license.txt)
11 */
12
13#include "RVersion.h"
14
15#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
16
17#define protected public
18#include "TRootBrowser.h"
20#define private public
21#include "RooAbsArg.h"
22#include "RooWorkspace.h"
25#include "RooProdPdf.h"
26#include "TGFileBrowser.h"
27#include "RooFitResult.h"
28#include "TPad.h"
29#undef private
30#include "RooAddPdf.h"
31#include "RooRealSumPdf.h"
32#include "RooProduct.h"
33#include "RooHistFunc.h"
34#include "RooConstVar.h"
35#include "RooSimultaneous.h"
36#undef protected
37
38#define GETWS(a) a->_myws
39#define GETWSSETS(w) w->_namedSets
40#define GETWSSNAPSHOTS(w) w->_snapshots
41#define GETACTBROWSER(b) b->fActBrowser
42#define GETROOTDIR(b) b->fRootDir
43#define GETLISTTREE(b) b->fListTree
44#define GETDMP(o,m) o->m
45
46#else
47
48#include "RooAbsArg.h"
49#include "RooWorkspace.h"
50#include "RooFitResult.h"
51#include "RooConstVar.h"
52#include "RooHistFunc.h"
53#include "RooRealSumPdf.h"
54#include "RooSimultaneous.h"
55#include "RooAddPdf.h"
56#include "RooProduct.h"
57#include "TPad.h"
61#include "RooProdPdf.h"
62#include "TRootBrowser.h"
63#include "TGFileBrowser.h"
64
65RooWorkspace* GETWS(RooAbsArg * a) { return a->workspace(); }
66const auto& GETWSSETS(RooWorkspace * w){ return w->sets(); }
67auto GETWSSNAPSHOTS(RooWorkspace * w){ return w->getSnapshots(); }
68auto GETACTBROWSER(TRootBrowser * b){ return b->GetActBrowser(); }
69auto GETROOTDIR(TGFileBrowser * b){ return b->GetRootDir(); }
70auto GETLISTTREE(TGFileBrowser * b) { return b->GetListTree(); }
71#define GETDMP(o,m) (*(void**)(((unsigned char*)o) + o->Class()->GetDataMemberOffset(#m)))
72
73#endif
74
75#include "RooAddition.h"
76
77#include "RooCategory.h"
78#include "RooRealVar.h"
79#include "RooStringVar.h"
80#include "RooBinning.h"
81#include "RooUniformBinning.h"
82
83#include "RooAbsData.h"
84#include "RooDataHist.h"
85#include "RooDataSet.h"
86
87#include "xRooFit/xRooNode.h"
88#include "xRooFit/xRooFit.h"
89
90#include "TH1.h"
91#include "TBrowser.h"
92#include "TROOT.h"
93#include "TQObject.h"
94#include "TAxis.h"
95#include "TGraphAsymmErrors.h"
96#include "TMath.h"
97#include "TPRegexp.h"
98#include "TRegexp.h"
99#include "TExec.h"
100
101#include "TGListTree.h"
102#include "TGMsgBox.h"
103#include "TGedEditor.h"
104#include "TGMimeTypes.h"
105#include "TH2.h"
106
107//#include "RooFitTrees/RooFitResultTree.h"
108//#include "RooFitTrees/RooDataTree.h"
109#include "TFile.h"
110#include "TSystem.h"
111#include "TKey.h"
112#include "TEnv.h"
113#include "TStyle.h"
114
115#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
117#endif
118
119#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
120#include "RooBinSamplingPdf.h"
121#endif
122
123#include "RooPoisson.h"
124#include "RooGaussian.h"
125#include "RooFormulaVar.h"
126#include "TVectorD.h"
127#include "TStopwatch.h"
128#include "TTimeStamp.h"
129
130#include <csignal>
131
132#include "TCanvas.h"
133#include "THStack.h"
134
135#include "TLegend.h"
136#include "TLegendEntry.h"
137#include "TGraphErrors.h"
138#include "TMultiGraph.h"
139#include "TFrame.h"
140
142
143xRooNode::InteractiveObject *xRooNode::gIntObj = nullptr;
144std::map<std::string, std::tuple<std::function<double(double, double, double)>, bool>> xRooNode::auxFunctions;
145void xRooNode::SetAuxFunction(const char *title, const std::function<double(double, double, double)> &func,
146 bool symmetrize)
147{
148 auxFunctions[title] = std::make_tuple(func, symmetrize);
149}
150
151template <typename T>
152const T &_or_func(const T &a, const T &b)
153{
154 if (a)
155 return a;
156 return b;
157}
158
159xRooNode::xRooNode(const char *classname, const char *name, const char *title)
160 : xRooNode(name,
161 std::shared_ptr<TObject>(
162 TClass::GetClass(classname) ? (TObject *)TClass::GetClass(classname)->New() : nullptr, [](TObject *o) {
163 if (auto w = dynamic_cast<RooWorkspace *>(o); w) {
164#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
165 w->_embeddedDataList.Delete();
166#endif
167 xRooNode(*w, std::make_shared<xRooNode>()).sterilize();
168 }
169 if (o)
170 delete o;
171 }))
172{
173 if (auto a = get<TNamed>(); a)
174 a->SetName(name);
175 SetTitle(title);
176}
177
178xRooNode::xRooNode(const char *name, const std::shared_ptr<TObject> &comp, const std::shared_ptr<xRooNode> &parent)
179 : TNamed(name, ""), fComp(comp), fParent(parent)
180{
181
182 if (!fComp && !fParent) {
184 if (!gSystem->AccessPathName(pathName)) {
185 // if file is json can try to read
186 if (pathName.EndsWith(".json")) {
187#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
188 fComp = std::make_shared<RooWorkspace>("workspace", name);
189 RooJSONFactoryWSTool tool(*get<RooWorkspace>());
192 if (!tool.importJSON(pathName.Data())) {
193 Error("xRooNode", "Error reading json workspace %s", name);
194 fComp.reset();
195 }
197#else
198 Error("xRooNode", "json format workspaces available only in ROOT 6.26 onwards");
199#endif
200 } else {
201
202 // using acquire in the constructor seems to cause a mem leak according to valgrind ... possibly because
203 // (*this) gets called on it before the node is fully constructed
204 auto _file = std::make_shared<TFile>(
205 pathName); // acquire<TFile>(name); // acquire file to ensure stays open while we have the workspace
206 // actually it appears we don't need to keep the file open once we've loaded the workspace, but should be
207 // no harm doing so
208 // otherwise the workspace doesn't saveas
209 auto keys = _file->GetListOfKeys();
210 if (keys) {
211 for (auto &&k : *keys) {
212 auto cl = TClass::GetClass(((TKey *)k)->GetClassName());
213 if (cl == RooWorkspace::Class() || cl->InheritsFrom("RooWorkspace")) {
214 fComp.reset(_file->Get<RooWorkspace>(k->GetName()), [](TObject *ws) {
215 // memory leak in workspace, some RooLinkedLists aren't cleared, fixed in ROOT 6.28
216 if (ws) {
217#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
218 dynamic_cast<RooWorkspace *>(ws)->_embeddedDataList.Delete();
219#endif
220 xRooNode(*ws, std::make_shared<xRooNode>()).sterilize();
221 delete ws;
222 }
223 });
224 if (fComp) {
225 TNamed::SetNameTitle(fComp->GetName(), fComp->GetTitle());
226 fParent = std::make_shared<xRooNode>(
227 _file); // keep file alive - seems necessary to save workspace again in some cases
228 break;
229 }
230 }
231 }
232 }
233 }
234 } else if (pathName.EndsWith(".root") || pathName.EndsWith(".json")) {
235 throw std::runtime_error(TString::Format("%s does not exist", name));
236 }
237 }
238
239 if (auto _ws = get<RooWorkspace>(); _ws && (!parent || parent->get<TFile>())) {
242 .removeTopic(RooFit::NumIntegration); // stop info message every time
243
244 // check if any of the open files have version numbers greater than our major version
245 // may not read correctly
246 for (auto f : *gROOT->GetListOfFiles()) {
247 if ((dynamic_cast<TFile *>(f)->GetVersion() / 100) > (gROOT->GetVersionInt() / 100)) {
248 Warning("xRooNode", "There is file open with version %d > current version %d ... results may be wrong",
249 dynamic_cast<TFile *>(f)->GetVersion(), gROOT->GetVersionInt());
250 }
251 }
252
253 // use the datasets if any to 'mark' observables
254 int checkCount = 0;
255 for (auto &d : _ws->allData()) {
256 for (auto &a : *d->get()) {
257 if (auto v = _ws->var(a->GetName()); v)
258 v->setAttribute("obs");
259 else if (auto c = _ws->cat(a->GetName()); c)
260 c->setAttribute("obs");
261 }
262 // count how many ds are checked ... if none are checked will check the first
263 checkCount += d->TestBit(1 << 20);
264 }
265
266 if (checkCount == 0 && !_ws->allData().empty())
267 _ws->allData().back()->SetBit(1 << 20, true);
268
269 if (auto _set = dynamic_cast<RooArgSet *>(GETWSSNAPSHOTS(_ws).find("NominalParamValues")); _set) {
270 for (auto s : *_set) {
271 if (auto v = dynamic_cast<RooRealVar *>(s); v) {
272 _ws->var(s->GetName())->setStringAttribute("nominal", TString::Format("%f", v->getVal()));
273 }
274 }
275 }
276
277 // also flag global observables ... relies on ModelConfig existences
278 RooArgSet _allGlobs;
279 for (auto &[k, v] : GETWSSETS(_ws)) {
280 if (k == "globalObservables" || TString(k).EndsWith("_GlobalObservables")) {
281 for (auto &s : v) {
282 _allGlobs.add(*s);
283 s->setAttribute("obs");
284 s->setAttribute("global");
285 }
286 }
287 if (TString(k).EndsWith("_POI")) {
288 for (auto &s : v) {
289 s->setAttribute("poi");
290 auto _v = dynamic_cast<RooRealVar *>(s);
291 if (!_v)
292 continue;
293 if (!_v->hasRange("physical")) {
294 _v->setRange("physical", 0, std::numeric_limits<double>::infinity());
295 // ensure range of poi is also straddling 0
296 if (_v->getMin() >= 0)
297 _v->setMin(-1e-5);
298 }
299 }
300 }
301 }
302 if (!_allGlobs.empty() && GETWSSETS(_ws).count("globalObservables") == 0) {
303 _ws->defineSet("globalObservables", _allGlobs);
304 }
305
306 // now check if any pars don't have errors defined (not same as error=0) ... if so, use the first pdf (if there is
307 // one) to try setting values from
308 if (!_ws->allPdfs().empty()) {
309 std::vector<RooRealVar *> noErrorPars;
310 for (auto &p : pars()) {
311 auto v = p->get<RooRealVar>();
312 if (!v)
313 continue;
314 if (!v->hasError())
315 noErrorPars.push_back(v);
316 }
317 if (!noErrorPars.empty()) {
318 // get the first top-level pdf
319 browse();
320 for (auto &a : *this) {
321 if (a->fFolder == "!models") {
322 try {
323 auto fr = a->fitResult("prefit");
324 if (auto _fr = fr.get<RooFitResult>(); _fr) {
325 for (auto &v : noErrorPars) {
326 if (auto arg = dynamic_cast<RooRealVar *>(_fr->floatParsFinal().find(v->GetName()));
327 arg && arg->hasError()) {
328 v->setError(arg->getError());
329 }
330 }
331 }
332 } catch (...) {
333 }
334 }
335 }
336 }
337 }
338 }
339
340 if (strlen(GetTitle()) == 0) {
341 if (fComp)
342 TNamed::SetTitle(fComp->GetTitle());
343 else
344 TNamed::SetTitle(GetName());
345 }
346}
347
348xRooNode::xRooNode(const TObject &comp, const std::shared_ptr<xRooNode> &parent)
349 : xRooNode(/*[](const TObject& c) {
350c.InheritsFrom("RooAbsArg");
351if (s) {
352return (s->getStringAttribute("alias")) ? s->getStringAttribute("alias") : c.GetName();
353}
354return c.GetName();
355}(comp)*/
356 (comp.InheritsFrom("RooAbsArg") && dynamic_cast<const RooAbsArg *>(&comp)->getStringAttribute("alias"))
357 ? dynamic_cast<const RooAbsArg *>(&comp)->getStringAttribute("alias")
358 : comp.GetName(),
359 std::shared_ptr<TObject>(const_cast<TObject *>(&comp), [](TObject *) {}), parent)
360{
361}
362
363xRooNode::xRooNode(const std::shared_ptr<TObject> &comp, const std::shared_ptr<xRooNode> &parent)
364 : xRooNode(
365 [&]() {
366 if (auto a = std::dynamic_pointer_cast<RooAbsArg>(comp); a && a->getStringAttribute("alias"))
367 return a->getStringAttribute("alias");
368 if (comp)
369 return comp->GetName();
370 return "";
371 }(),
372 comp, parent)
373{
374}
375
377
378void xRooNode::Checked(TObject *obj, bool val)
379{
380 if (obj != this)
381 return;
382
383 // cycle through states:
384 // unhidden and selected: tick, no uline
385 // hidden and unselected: notick, uline
386 // unhidden and unselected: tick, uline
387 if (auto o = get<RooAbsReal>(); o) {
388 if (o->isSelectedComp() && !val) {
389 // deselecting and hiding
390 o->selectComp(val);
391 o->setAttribute("hidden");
392 } else if (!o->isSelectedComp() && !val) {
393 // selecting
394 o->selectComp(!val);
395 } else if (val) {
396 // unhiding but keeping unselected
397 o->setAttribute("hidden", false);
398 }
399 auto item = GetTreeItem(nullptr);
400 item->CheckItem(!o->getAttribute("hidden"));
401 if (o->isSelectedComp())
402 item->ClearColor();
403 else
404 item->SetColor(kGray);
405 return;
406 }
407
408 if (auto o = get(); o) {
409 // if (o->TestBit(1<<20)==val) return; // do nothing
410 o->SetBit(1 << 20, val); // TODO: check is 20th bit ok to play with?
411 if (auto fr = get<RooFitResult>(); fr) {
412 if (auto _ws = ws(); _ws) {
413 if (val) {
414 RooArgSet _allVars = _ws->allVars();
415 _allVars = fr->floatParsFinal();
416 _allVars = fr->constPars();
417 for (auto &i : fr->floatParsInit()) {
418 auto v = dynamic_cast<RooRealVar *>(_allVars.find(i->GetName()));
419 if (v)
420 v->setStringAttribute("initVal", TString::Format("%f", dynamic_cast<RooRealVar *>(i)->getVal()));
421 }
422 // uncheck all other fit results
423 for (auto oo : _ws->allGenericObjects()) {
424 if (auto ffr = dynamic_cast<RooFitResult *>(oo); ffr && ffr != fr) {
425 ffr->ResetBit(1 << 20);
426 }
427 }
428 } else
429 _ws->allVars() = fr->floatParsInit();
430 }
431 if (auto item = GetTreeItem(nullptr); item) {
432 // update check marks on siblings
433 if (auto first = item->GetParent()->GetFirstChild()) {
434 do {
435 if (first->HasCheckBox()) {
436 auto _obj = static_cast<xRooNode *>(first->GetUserData());
437 first->CheckItem(_obj->get() && _obj->get()->TestBit(1 << 20));
438 }
439 } while ((first = first->GetNextSibling()));
440 }
441 }
442 }
443 }
444}
445
447{
448 static bool blockBrowse = false;
449 if (blockBrowse)
450 return;
451 if (b == 0) {
452 auto b2 = dynamic_cast<TBrowser *>(gROOT->GetListOfBrowsers()->Last());
453 if (!b2 || !b2->GetBrowserImp()) { // no browser imp if browser was closed
454 blockBrowse = true;
455 gEnv->SetValue("X11.UseXft", "no"); // for faster x11
456 gEnv->SetValue("X11.Sync", "no");
457 gEnv->SetValue("X11.FindBestVisual", "no");
458 gEnv->SetValue("Browser.Name", "TRootBrowser"); // forces classic root browser (in 6.26 onwards)
459 b2 = new TBrowser("nodeBrowser", this, "RooFit Browser");
460 blockBrowse = false;
461 } else if (strcmp(b2->GetName(), "nodeBrowser") == 0) {
462 blockBrowse = true;
463 b2->BrowseObject(this);
464 blockBrowse = false;
465 } else {
466 auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b2->GetBrowserImp())));
467 if (_b)
468 _b->AddFSDirectory("Workspaces", 0, "SetRootDir");
469 /*auto l = Node2::Class()->GetMenuList();
470 auto o = new CustomClassMenuItem(TClassMenuItem::kPopupUserFunction,Node2::Class(),
471 "blah blah blah","BlahBlah",0,"Option_t*",-1,true);
472 //o->SetCall(o,"BlahBlah","Option_t*",-1);
473 l->AddFirst(o);*/
474 // b->BrowseObject(this);
475 _b->GotoDir(0);
476 _b->Add(this, GetName());
477 // b->Add(this);
478 }
479 return;
480 }
481
482 if (auto item = GetTreeItem(b); item) {
483 if (!item->IsOpen() && IsFolder())
484 return; // no need to rebrowse if closing
485 // update check marks on any child items
486 if (auto first = item->GetFirstChild()) {
487 do {
488 if (first->HasCheckBox()) {
489 auto _obj = static_cast<xRooNode *>(first->GetUserData());
490 first->CheckItem(_obj->get() &&
491 (_obj->get()->TestBit(1 << 20) ||
492 (_obj->get<RooAbsArg>() && !_obj->get<RooAbsArg>()->getAttribute("hidden"))));
493 }
494 } while ((first = first->GetNextSibling()));
495 }
496 }
497
498 browse();
499 if (empty()) {
500 try {
501 if (auto s = get<TStyle>()) {
502 s->SetFillAttributes();
503 if (auto ed = dynamic_cast<TGedEditor *>(TVirtualPadEditor::GetPadEditor())) {
504 ed->SetModel(gPad, s, kButton1Down, true);
505 }
506 } else
507 Draw(b->GetDrawOption());
508 } catch (const std::exception &e) {
509 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
510 kMBIconExclamation); // deletes self on dismiss?
511 }
512 }
513
514 bool hasFolders = false;
515 if (strlen(GetName()) > 0 && GetName()[0] != '!') { // folders don't have folders
516 for (auto &c : *this) {
517 if (!c->fFolder.empty()) {
518 hasFolders = true;
519 break;
520 }
521 }
522 }
523 // auto _ws = get<RooWorkspace>();
524 if (/*_ws*/ hasFolders) {
525 // organize in folders
526 auto _folders = find(".folders");
527 if (!_folders) {
528 _folders = emplace_back(std::make_shared<xRooNode>(".folders", nullptr, *this));
529 }
530 // ensure entry in folders for every folder type ...
531 for (auto &v : *this) {
532 if (v->fFolder != "" && !_folders->find(v->fFolder)) {
533 _folders->emplace_back(std::make_shared<xRooNode>(v->fFolder.c_str(), nullptr, *this));
534 }
535 }
536 // now just add all the folders
537 for (auto &v : *_folders) {
538 TString _name = v->GetName();
539 if (_name.BeginsWith('!'))
540 _name = _name(1, _name.Length()); // strip ! from display
541 b->Add(v.get(), _name);
542 }
543 }
544
545 for (auto &v : *this) {
546 if (hasFolders && !v->fFolder.empty())
547 continue; // in the folders
548 if (strcmp(v->GetName(), ".folders") == 0)
549 continue; // never 'browse' the folders property
550 int _checked = (v->get<RooAbsData>() || v->get<RooFitResult>()) ? v->get()->TestBit(1 << 20) : -1;
551 if (v->get<RooAbsPdf>() && get<RooSimultaneous>())
552 _checked = !v->get<RooAbsArg>()->getAttribute("hidden");
553 TString _name = v->GetName();
554 if (v->get() && _name.BeginsWith(TString(v->get()->ClassName()) + "::")) {
555 _name = _name(strlen(v->get()->ClassName()) + 2, _name.Length());
556 }
557 if (_name.BeginsWith(".")) {
558 // property node -- display the name of the contained object
559 if (v->get())
560 _name = TString::Format("%s: %s::%s", _name.Data(), v->get()->ClassName(),
561 (v->get<RooAbsArg>() && v->get<RooAbsArg>()->getStringAttribute("alias"))
562 ? v->get<RooAbsArg>()->getStringAttribute("alias")
563 : v->get()->GetName());
564 } else if (v->get() && !v->get<TFile>() && !TString(v->GetName()).BeginsWith('/'))
565 _name = TString::Format("%s::%s", v->get()->ClassName(), _name.Data());
566 if (auto _type = v->GetNodeType(); strlen(_type)) {
567 // decided not to show const values until figure out how to update if value changes
568 /*if (TString(_type)=="Const") _name += TString::Format(" [%s=%g]",_type,v->get<RooConstVar>()->getVal());
569 else*/
570 _name += TString::Format(" [%s]", _type);
571 }
572 // tool tip defaults to displaying name and title, so temporarily set name to obj name if has one
573 // and set title to the object type
574 TString nameSave(v->TNamed::GetName());
575 TString titleSave(v->TNamed::GetTitle());
576 if (auto o = v->get(); o)
577 v->TNamed::SetNameTitle(o->GetName(), o->ClassName());
578 b->Add(v.get(), _name, _checked);
579 if (auto o = v->get(); o)
580 v->TNamed::SetNameTitle(nameSave, titleSave);
581 if (_checked != -1) {
582 dynamic_cast<TQObject *>(b->GetBrowserImp())
583 ->Connect("Checked(TObject *, Bool_t)", ClassName(), v.get(), "Checked(TObject *, Bool_t)");
584 if (auto _fr = v->get<RooFitResult>(); _fr && _fr->status())
585 v->GetTreeItem(b)->SetColor(kRed);
586 }
587 // v.fBrowsers.insert(b);
588 }
589 // for pdfs, check for datasets too and add to list
590 /*if (get<RooAbsPdf>()) {
591 auto dsets = datasets();
592 if (!dsets.empty()) {
593 // check if already have .datasets() in browsables
594 bool found(false);
595 for(auto& p : fBrowsables) {
596 if (TString(p->GetName())==".datasets()") {found=true;
597 // add
598 break;
599 }
600 }
601 if (!found) {
602 fBrowsables.push_back(std::make_shared<xRooNode>(dsets));
603 }
604 }
605 }*/
606 // browse the browsables too
607 for (auto &v : fBrowsables) {
608 TString _name = v->GetName();
609 TString nameSave(v->TNamed::GetName());
610 TString titleSave(v->TNamed::GetTitle());
611 if (auto o = v->get(); o)
612 v->TNamed::SetNameTitle(o->GetName(), o->ClassName());
613 b->Add(v.get(), _name, -1);
614 if (auto o = v->get(); o)
615 v->TNamed::SetNameTitle(nameSave, titleSave);
616 }
617
618 b->SetSelected(this);
619}
620
622{
623 if (!set) {
624 // can't remove as causes a crash, need to remove from the browser first
625 /*for(auto itr = fBrowsables.begin(); itr != fBrowsables.end(); ++itr) {
626 if (strcmp((*itr)->GetName(),".vars")==0) {
627 fBrowsables.erase(itr);
628 }
629 }*/
630 } else {
631 auto v = std::make_shared<xRooNode>(vars());
632 fBrowsables.push_back(v);
633 if (auto l = GetListTree(nullptr)) {
634 l->AddItem(GetTreeItem(nullptr), v->GetName(), v.get());
635 }
636 }
637}
638
640{
641 for (auto &b : fBrowsables) {
642 if (strcmp(b->GetName(), ".vars") == 0)
643 return true;
644 }
645 return false;
646}
647
649{
650 if (strlen(GetName()) > 0 && GetName()[0] == '!')
651 return true;
652 if (strlen(GetName()) > 0 && GetName()[0] == '.')
653 return true;
654 if (empty())
655 const_cast<xRooNode *>(this)->browse();
656 return !empty();
657}
658
659class Axis2 : public TAxis {
660
661public:
662 using TAxis::TAxis;
663 Double_t GetBinWidth(Int_t bin) const override
664 {
665 if (auto v = var(); v)
666 return v->getBinWidth(bin - 1, GetName());
667 return 1;
668 }
669 Double_t GetBinLowEdge(Int_t bin) const override
670 {
671 if (auto v = rvar(); v)
672 return v->getBinning(GetName()).binLow(bin - 1);
673 return bin - 1;
674 }
675 Double_t GetBinUpEdge(Int_t bin) const override
676 {
677 if (auto v = rvar(); v)
678 return v->getBinning(GetName()).binHigh(bin - 1);
679 return bin;
680 }
681
682 const char *GetTitle() const override
683 {
684 return (binning() && strlen(binning()->GetTitle())) ? binning()->GetTitle() : GetParent()->GetTitle();
685 }
686 void SetTitle(const char *title) override
687 {
688 if (binning())
689 const_cast<RooAbsBinning *>(binning())->SetTitle(title);
690 else
691 dynamic_cast<TNamed *>(GetParent())->SetTitle(title);
692 }
693
694 void Set(Int_t nbins, const Double_t *xbins) override
695 {
696 if (auto v = dynamic_cast<RooRealVar *>(rvar()))
697 v->setBinning(RooBinning(nbins, xbins), GetName());
698 TAxis::Set(nbins, xbins);
699 }
700 void Set(Int_t nbins, const Float_t *xbins) override
701 {
702 std::vector<double> bins(nbins + 1);
703 for (int i = 0; i <= nbins; i++)
704 bins.at(i) = xbins[i];
705 return Set(nbins, &bins[0]);
706 }
707 void Set(Int_t nbins, Double_t xmin, Double_t xmax) override
708 {
709 if (auto v = dynamic_cast<RooRealVar *>(rvar()))
710 v->setBinning(RooUniformBinning(xmin, xmax, nbins), GetName());
711 TAxis::Set(nbins, xmin, xmax);
712 }
713
714 const RooAbsBinning *binning() const { return var()->getBinningPtr(GetName()); }
715
716 Int_t FindFixBin(const char *label) const override { return TAxis::FindFixBin(label); }
717 Int_t FindFixBin(Double_t x) const override { return (binning()) ? (binning()->binNumber(x) + 1) : x; }
718
719private:
720 RooAbsLValue *var() const { return dynamic_cast<RooAbsLValue *>(GetParent()); }
721 RooAbsRealLValue *rvar() const { return dynamic_cast<RooAbsRealLValue *>(GetParent()); }
722};
723
724std::shared_ptr<TObject> xRooNode::getObject(const std::string &name, const std::string &type) const
725{
726 // if (fParent) return fParent->getObject(name);
727
728 if (auto _owned = find(".memory"); _owned) {
729 for (auto &o : *_owned) {
730 if (name == o->GetName()) {
731 if (type.empty() || o->get()->InheritsFrom(type.c_str()))
732 return o->fComp;
733 }
734 }
735 }
736
737 // see if have a provider
738 auto _provider = fProvider;
739 auto _parent = fParent;
740 while (!_provider && _parent) {
741 _provider = _parent->fProvider;
742 _parent = _parent->fParent;
743 }
744 if (_provider)
745 return _provider->getObject(name, type);
746
747 if (ws()) {
748 std::shared_ptr<TObject> out;
749 if (auto arg = ws()->arg(name.c_str()); arg) {
750 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
751 if (!type.empty() && arg->InheritsFrom(type.c_str()))
752 return _tmp;
753 if (!out)
754 out = _tmp;
755 }
756 if (auto arg = ws()->data(name.c_str()); arg) {
757 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
758 if (!type.empty() && arg->InheritsFrom(type.c_str()))
759 return _tmp;
760 if (!out)
761 out = _tmp;
762 }
763 if (auto arg = ws()->genobj(name.c_str()); arg) {
764 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
765 if (!type.empty() && arg->InheritsFrom(type.c_str()))
766 return _tmp;
767 if (!out)
768 out = _tmp;
769 }
770 if (auto arg = ws()->embeddedData(name.c_str()); arg) {
771 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
772 if (!type.empty() && arg->InheritsFrom(type.c_str()))
773 return _tmp;
774 if (!out)
775 out = _tmp;
776 }
777 return out;
778 }
779 return nullptr;
780}
781
783{
784 if (fXAxis) {
785 // check if num bins needs update or not
786 if(auto cat = dynamic_cast<RooAbsCategory*>(fXAxis->GetParent()); cat && cat->numTypes()!=fXAxis->GetNbins()) {
787 fXAxis.reset();
788 } else {
789 return fXAxis.get();
790 }
791 }
792 RooAbsLValue *x = nullptr;
793 if (auto a = get<RooAbsArg>(); a && a->isFundamental())
794 x = dynamic_cast<RooAbsLValue *>(a); // self-axis
795
796 auto _parentX = (!x && fParent && !fParent->get<RooSimultaneous>()) ? fParent->GetXaxis() : nullptr;
797
798 auto o = get<RooAbsReal>();
799 if (!o)
800 return _parentX;
801
802 if (auto xName = o->getStringAttribute("xvar"); xName) {
803 x = dynamic_cast<RooAbsLValue *>(getObject(xName).get());
804 }
805
806 // if xvar has become set equal to an arg and this is a pdf, we will allow a do-over
807 if (!x) {
808 // need to choose from dependent fundamentals, in following order:
809 // parentX, obs, globs, vars, args
810
811 if (_parentX && (o->dependsOn(*dynamic_cast<RooAbsArg *>(_parentX->GetParent())) || vars().size() == 0)) {
812 x = dynamic_cast<RooAbsLValue *>(_parentX->GetParent());
813 } else if (auto _obs = obs(); !_obs.empty()) {
814 for (auto &v : _obs) {
815 if (!v->get<RooAbsArg>()->getAttribute("global")) {
816 x = v->get<RooAbsLValue>();
817 if (x)
818 break;
819 } else if (!x) {
820 x = v->get<RooAbsLValue>();
821 }
822 }
823 } else if (auto _pars = pars(); !_pars.empty()) {
824 for (auto &v : _pars) {
825 if (!v->get<RooAbsArg>()->getAttribute("Constant")) {
826 x = v->get<RooAbsLValue>();
827 if (x)
828 break;
829 } else if (!x) {
830 x = v->get<RooAbsLValue>();
831 }
832 }
833 }
834
835 if (!x) {
836 return nullptr;
837 }
838 }
839
840 if (o != dynamic_cast<TObject *>(x)) {
841 o->setStringAttribute("xvar", dynamic_cast<TObject *>(x)->GetName());
842 }
843
844 // decide binning to use
845 TString binningName = o->getStringAttribute("binning");
846 auto _bnames = x->getBinningNames();
847 bool hasBinning = false;
848 for (auto &b : _bnames) {
849 if (b == binningName) {
850 hasBinning = true;
851 break;
852 }
853 }
854 if (!hasBinning) {
855 // doesn't have binning, so clear binning attribute
856 // this can happen after Combine of models because binning don't get combined yet (should fix this)
857 Warning("GetXaxis", "Binning %s not defined on %s - clearing", binningName.Data(),
858 dynamic_cast<TObject *>(x)->GetName());
859 o->setStringAttribute("binning", nullptr);
860 binningName = "";
861 }
862
863 if (binningName == "" && o != dynamic_cast<TObject *>(x)) {
864 // has var has a binning matching this nodes name then use that
865 auto __bnames = x->getBinningNames();
866 for (auto &b : __bnames) {
867 if (b == GetName())
868 binningName = GetName();
869 if (b == o->GetName()) {
870 binningName = o->GetName();
871 break;
872 } // best match
873 }
874 if (binningName == "") {
875 // if we are binned in this var then will define that as a binning
876 if (/*o->isBinnedDistribution(*dynamic_cast<RooAbsArg *>(x))*/
877 auto bins = _or_func(
878 /*o->plotSamplingHint(*dynamic_cast<RooAbsRealLValue
879 *>(x),-std::numeric_limits<double>::infinity(),std::numeric_limits<double>::infinity())*/
880 (std::list<double> *)(nullptr),
881 o->binBoundaries(*dynamic_cast<RooAbsRealLValue *>(x), -std::numeric_limits<double>::infinity(),
882 std::numeric_limits<double>::infinity()));
883 bins) {
884 std::vector<double> _bins;
885 for (auto &b : *bins) {
886 if (_bins.empty() || std::abs(_bins.back() - b) > 1e-5 * _bins.back())
887 _bins.push_back(b);
888 }
889 fXAxis = std::make_shared<Axis2>(_bins.size() - 1, &_bins[0]);
890 // add this binning to the var to avoid recalling ...
891 if (auto _v = dynamic_cast<RooRealVar *>(x); _v) {
892 _v->setBinning(RooBinning(_bins.size() - 1, &_bins[0], o->GetName()), o->GetName());
893 _v->getBinning(o->GetName())
894 .SetTitle(""); // indicates to use the current var title when building histograms etc
895 //_v->getBinning(o->GetName()).SetTitle(strlen(dynamic_cast<TObject*>(x)->GetTitle()) ?
896 //dynamic_cast<TObject*>(x)->GetTitle() : dynamic_cast<TObject*>(x)->GetName());
897 }
898 binningName = o->GetName();
899 delete bins;
900 } else if (_parentX) {
901 // use parent axis binning if defined, otherwise we will default
902 binningName = _parentX->GetName();
903 }
904 }
905 }
906
907 if (!fXAxis) {
908 if (auto r = dynamic_cast<RooAbsRealLValue *>(x); r) {
909 if (r->getBinning(binningName).isUniform()) {
910 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), r->getMin(binningName), r->getMax(binningName));
911 } else {
912 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), r->getBinning(binningName).array());
913 }
914 } else if(dynamic_cast<RooAbsCategory*>(x)) {
915 std::vector<double> bins = {};
916 for (int i = 0; i <= x->numBins(binningName); i++)
917 bins.push_back(i);
918 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), &bins[0]);
919 // TODO have to load current state of bin labels if was a category (sadly not a virtual method)
920 for (int i = 0; i < x->numBins(binningName); i++) {
921 fXAxis->SetBinLabel(i+1,dynamic_cast<RooAbsCategory *>(x)->lookupName(i).c_str());
922 }
923 }
924 }
925
926 fXAxis->SetName(binningName);
927 fXAxis->SetParent(dynamic_cast<TObject *>(x));
928 return fXAxis.get();
929}
930
931const char *xRooNode::GetIconName() const
932{
933 if (auto o = get(); o) {
934 if (o->InheritsFrom("RooWorkspace"))
935 return "TFile";
936 if (o->InheritsFrom("RooAbsData"))
937 return "TProfile";
938 if (o->InheritsFrom("RooSimultaneous"))
939 return "TH3D";
940
941 if (o->InheritsFrom("RooProdPdf"))
942 return "a.C"; // or nullptr for folder
943 if (o->InheritsFrom("RooRealSumPdf") || o->InheritsFrom("RooAddPdf"))
944 return "TH2D";
945 // if(o->InheritsFrom("RooProduct")) return "TH1D";
946 if (o->InheritsFrom("RooFitResult")) {
947 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitRooFitResult", true)) {
948 gClient->GetMimeTypeList()->AddType("xRooFitRooFitResult", "xRooFitRooFitResult", "package.xpm",
949 "package.xpm", "->Browse()");
950 }
951 return "xRooFitRooFitResult";
952 }
953 if (o->InheritsFrom("RooRealVar") || o->InheritsFrom("RooCategory")) {
954 if (get<RooAbsArg>()->getAttribute("obs")) {
955 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitObs", true)) {
956 gClient->GetMimeTypeList()->AddType("xRooFitObs", "xRooFitObs", "x_pic.xpm", "x_pic.xpm", "->Browse()");
957 }
958 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitGlobs", true)) {
959 gClient->GetMimeTypeList()->AddType("xRooFitGlobs", "xRooFitGlobs", "z_pic.xpm", "z_pic.xpm",
960 "->Browse()");
961 }
962 return (get<RooAbsArg>()->getAttribute("global") ? "xRooFitGlobs" : "xRooFitObs");
963 }
964 return "TLeaf";
965 }
966 if (o->InheritsFrom("TStyle")) {
967 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitTStyle", true)) {
968 gClient->GetMimeTypeList()->AddType("xRooFitTStyle", "xRooFitTStyle", "bld_colorselect.xpm",
969 "bld_colorselect.xpm", "->Browse()");
970 }
971 return "xRooFitTStyle";
972 }
973 if (o->InheritsFrom("RooConstVar")) {
974 /*if (!gClient->GetMimeTypeList()->GetIcon("xRooFitRooConstVar",true)) {
975 gClient->GetMimeTypeList()->AddType("xRooFitRooConstVar", "xRooFitRooConstVar", "stop_t.xpm", "stop_t.xpm",
976 "->Browse()");
977 }
978 return "xRooFitRooConstVar";*/
979 return "TMethodBrowsable-leaf";
980 }
981 if (o->InheritsFrom("RooStats::HistFactory::FlexibleInterpVar"))
982 return "TBranchElement-folder";
983 if (auto a = dynamic_cast<RooAbsReal *>(o); a) {
984 if (auto _ax = GetXaxis();
985 _ax && (a->isBinnedDistribution(*dynamic_cast<RooAbsArg *>(_ax->GetParent())) ||
986 (dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
987 std::unique_ptr<std::list<double>>(a->binBoundaries(
988 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
989 std::numeric_limits<double>::infinity()))))) {
990 return "TH1D";
991 }
992 return "TF1";
993 }
994 return o->ClassName();
995 }
996 if (!IsFolder()) {
997 return "Unknown";
998 }
999 return nullptr;
1000}
1001
1002const char *xRooNode::GetNodeType() const
1003{
1004 if (auto o = get(); o && fParent && (fParent->get<RooProduct>() || fParent->get<RooRealSumPdf>())) {
1005 if (o->InheritsFrom("RooStats::HistFactory::FlexibleInterpVar"))
1006 return "Overall";
1007 if (o->InheritsFrom("PiecewiseInterpolation"))
1008 return (dynamic_cast<RooAbsArg *>(o)->getAttribute("density")) ? "DensityHisto" : "Histo";
1009 if (o->InheritsFrom("RooHistFunc"))
1010 return (dynamic_cast<RooAbsArg *>(o)->getAttribute("density")) ? "ConstDensityHisto" : "ConstHisto";
1011 if (o->InheritsFrom("RooBinWidthFunction"))
1012 return "Density";
1013 if (o->InheritsFrom("ParamHistFunc"))
1014 return "Shape";
1015 if (o->InheritsFrom("RooRealVar"))
1016 return "Norm";
1017 if (o->InheritsFrom("RooConstVar"))
1018 return "Const";
1019 }
1020 return "";
1021}
1022
1023xRooNode xRooNode::coords(bool setVals) const
1024{
1025 xRooNode out(".coords", nullptr, *this);
1026 // go up through parents looking for slice obs
1027 auto _p = std::shared_ptr<xRooNode>(const_cast<xRooNode *>(this), [](xRooNode *) {});
1028 while (_p) {
1029 TString pName(_p->GetName());
1030 if (auto pos = pName.Index('='); pos != -1) {
1031 if (auto _obs = _p->getObject<RooAbsArg>(pName(0, pos)); _obs) {
1032 if (setVals) {
1033 if (auto _cat = dynamic_cast<RooAbsCategoryLValue *>(_obs.get()); _cat) {
1034 _cat->setLabel(pName(pos + 1, pName.Length()));
1035 } else if (auto _var = dynamic_cast<RooAbsRealLValue *>(_obs.get()); _var) {
1036 _var->setVal(TString(pName(pos + 1, pName.Length())).Atof());
1037 }
1038 }
1039 out.emplace_back(std::make_shared<xRooNode>(_obs->GetName(), _obs, _p));
1040 } else {
1041 throw std::runtime_error("Unknown observable, could not find");
1042 }
1043 }
1044 _p = _p->fParent;
1045 }
1046 return out;
1047}
1048
1049void xRooNode::_Add_(const char *name, const char *opt)
1050{
1051 try {
1052 Add(name, opt);
1053 } catch (const std::exception &e) {
1054 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
1055 kMBIconExclamation); // deletes self on dismiss?
1056 }
1057}
1058void xRooNode::_Vary_(const char *what)
1059{
1060 try {
1061 Vary(what);
1062 } catch (const std::exception &e) {
1063 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
1064 kMBIconExclamation); // deletes self on dismiss?
1065 }
1066}
1067
1069{
1070
1071 if (strcmp(GetName(), ".factors") == 0 || strcmp(GetName(), ".constraints") == 0 ||
1072 strcmp(GetName(), ".components") == 0) {
1073 auto toRemove =
1074 (child.get<RooAbsArg>() || !find(child.GetName())) ? child : xRooNode(find(child.GetName())->fComp);
1075 if (auto p = fParent->get<RooProdPdf>(); p) {
1076 auto pdf = toRemove.get<RooAbsArg>();
1077 if (!pdf)
1078 pdf = p->pdfList().find(child.GetName());
1079 if (!pdf)
1080 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1081 auto i = p->pdfList().index(*pdf);
1082 if (i >= 0) {
1083#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
1084 const_cast<RooArgList&>(p->pdfList()).remove(*pdf);
1085#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
1086 p->_pdfNSetList.erase(p->_pdfNSetList.begin() + i);
1087#else
1088 auto nset = p->_pdfNSetList.At(i);
1089 p->_pdfNSetList.Remove(nset);
1090 delete nset; // I don't think the RooLinkedList owned it so must delete ourself
1091#endif
1092 if (p->_extendedIndex == i)
1093 p->_extendedIndex = -1;
1094 else if (p->_extendedIndex > i)
1095 p->_extendedIndex--;
1096#else
1097 p->removePdfs(RooArgSet(*pdf));
1098#endif
1099 sterilize();
1100 return xRooNode(*pdf);
1101 } else {
1102 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1103 }
1104 } else if (auto p2 = fParent->get<RooProduct>(); p2) {
1105 auto arg = toRemove.get<RooAbsArg>();
1106 if (!arg)
1107 arg = p2->components().find(child.GetName());
1108 if (!arg)
1109 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1110 // remove server ... doesn't seem to trigger removal from proxy
1111#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
1112 p2->_compRSet.remove(*arg);
1113#else
1114 const_cast<RooArgList&>(p2->realComponents()).remove(*arg);
1115#endif
1116 p2->removeServer(*arg, true);
1117 sterilize();
1118 return xRooNode(*arg);
1119 } else if (fParent->get<RooSimultaneous>()) {
1120 // remove from all channels
1121 bool removed = false;
1122 for (auto &c : fParent->bins()) {
1123 try {
1124 c->constraints().Remove(toRemove);
1125 removed = true;
1126 } catch (std::runtime_error &) { /* wasn't a constraint in channel */
1127 }
1128 }
1129 sterilize();
1130 if (!removed)
1131 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1132 return toRemove;
1133 } else if (auto p4 = fParent->get<RooRealSumPdf>(); p4) {
1134 auto arg = toRemove.get<RooAbsArg>();
1135 if (!arg)
1136 arg = p4->funcList().find(child.GetName());
1137 if (!arg)
1138 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1139 // remove, including coef removal ....
1140 auto idx = p4->funcList().index(arg);
1141
1142 if (idx != -1) {
1143
1144 const_cast<RooArgList&>(p4->funcList()).remove(*arg);
1145 p4->removeServer(*arg, true);
1146 // have to be careful removing coef because if shared will end up removing them all!!
1147 std::vector<RooAbsArg *> _coefs;
1148 for (size_t ii = 0; ii < const_cast<RooArgList&>(p4->coefList()).size(); ii++) {
1149 if (ii != size_t(idx))
1150 _coefs.push_back(const_cast<RooArgList&>(p4->coefList()).at(ii));
1151 }
1152 const_cast<RooArgList&>(p4->coefList()).removeAll();
1153 for (auto &a : _coefs)
1154 const_cast<RooArgList&>(p4->coefList()).add(*a);
1155
1156 sterilize();
1157 } else {
1158 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1159 }
1160 return xRooNode(*arg);
1161 } // todo: add support for RooAddPdf and RooAddition
1162 }
1163
1164 if (auto w = get<RooWorkspace>(); w) {
1165 xRooNode out(child.GetName());
1166 auto arg = w->components().find(child.GetName());
1167 if (!arg)
1168 arg = operator[](child.GetName())->get<RooAbsArg>();
1169 if (!arg) {
1170 throw std::runtime_error(TString::Format("Cannot find %s in workspace %s", child.GetName(), GetName()));
1171 }
1172 // check has no clients ... if so, cannot delete
1173 if (arg->hasClients()) {
1174 throw std::runtime_error(
1175 TString::Format("Cannot remove %s from workspace %s, because it has dependencies - first remove from those",
1176 child.GetName(), GetName()));
1177 }
1178 const_cast<RooArgSet&>(w->components()).remove(*arg); // deletes arg
1179 Info("Remove", "Deleted %s from workspace %s", out.GetName(), GetName());
1180 return out;
1181 } else if (get<RooProduct>() || get<RooProdPdf>()) {
1182 return factors().Remove(child);
1183 } else if (get<RooRealSumPdf>()) {
1184 return components().Remove(child);
1185 }
1186
1187 throw std::runtime_error("Removal not implemented for this type of object");
1188}
1189
1191{
1192
1193 class AutoUpdater {
1194 public:
1195 AutoUpdater(xRooNode &_n) : n(_n) {}
1196 ~AutoUpdater() { n.browse(); }
1197 xRooNode &n;
1198 };
1199 AutoUpdater xxx(*this);
1200
1201 TString sOpt(opt);
1202 bool considerType(sOpt == "+");
1203
1204 if (strlen(GetName()) > 0 && GetName()[0] == '!' && fParent) {
1205 // folder .. pass onto parent and add folder to child folder list
1206 const_cast<xRooNode &>(child).fFolder += GetName();
1207 return fParent->Add(child, opt);
1208 }
1209 // this is how to get the first real parent ... may be useful at some point?
1210 /*auto realParent = fParent;
1211 while(!realParent->get()) {
1212 realParent = realParent->fParent;
1213 if (!realParent) throw std::runtime_error("No parentage");
1214 }*/
1215
1216 // adding to a collection node will incorporate the child into the parent of the collection
1217 // in the appropriate way
1218 if (strcmp(GetName(), ".factors") == 0) {
1219 // multiply the parent
1220 return fParent->Multiply(child, opt);
1221 } else if (strcmp(GetName(), ".components") == 0) {
1222 // add to the parent
1223 return fParent->Add(child, opt);
1224 } else if (strcmp(GetName(), ".variations") == 0) {
1225 // vary the parent
1226 return fParent->Vary(child);
1227 } else if (strcmp(GetName(), ".constraints") == 0) {
1228 // constrain the parent
1229 return fParent->Constrain(child);
1230 } else if (strcmp(GetName(), ".bins") == 0 && fParent->get<RooSimultaneous>()) {
1231 // adding a channel (should adding a 'bin' be an 'Extend' operation?)
1232 return fParent->Vary(child);
1233 } else if ((strcmp(GetName(), ".pars") == 0 || strcmp(GetName(),".vars")==0) && fParent->get<RooWorkspace>()) {
1234 // adding a parameter, interpret as factory string unless no "[" then create RooRealVar
1235 TString fac(child.GetName());
1236 if (!fac.Contains("["))
1237 fac += "[1]";
1238 return xRooNode(*fParent->get<RooWorkspace>()->factory(fac), fParent);
1239 } else if (strcmp(GetName(), ".datasets()") == 0) {
1240 // create a dataset - only allowed for pdfs or workspaces
1241 if (auto _ws = ws(); _ws && fParent) {
1242 sOpt.ToLower();
1243 if (!fParent->get<RooAbsPdf>() && (!fParent->get<RooWorkspace>() || sOpt == "asimov")) {
1244 throw std::runtime_error(
1245 "Datasets can only be created for pdfs or workspaces (except if generated dataset, then must be pdf)");
1246 }
1247
1248 if (sOpt == "asimov" || sOpt == "toy") {
1249 // generate expected dataset - note that globs will be frozen at this time
1250 auto _fr = std::dynamic_pointer_cast<const RooFitResult>(fParent->fitResult().fComp);
1251 if (strlen(_fr->GetName()) == 0)
1252 std::const_pointer_cast<RooFitResult>(_fr)->SetName(TUUID().AsString());
1253 auto asi = xRooFit::generateFrom(*fParent->get<RooAbsPdf>(), _fr, sOpt == "asimov");
1254 if (strlen(child.GetName()))
1255 asi.first->SetName(child.GetName());
1256 if (asi.first) {
1257 _ws->import(*asi.first);
1258 }
1259 if (!_ws->obj(_fr->GetName())) {
1260 _ws->import(const_cast<RooFitResult &>(*_fr));
1261 } // save fr to workspace, for later retrieval
1262 if (asi.second) {
1263#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
1264 _ws->saveSnapshot(asi.first->GetName(), *asi.second,
1265 true); // TODO: Migrate to using globs inside datasets
1266#else
1267 RooArgSet _tmp;
1268 _tmp.add(*asi.second);
1269 _ws->saveSnapshot(asi.first->GetName(), _tmp, true);
1270#endif
1271 }
1272 return xRooNode(*_ws->data(asi.first->GetName()), fParent);
1273 }
1274
1275 auto _obs = fParent->obs().argList();
1276 // put globs in a snapshot
1277 std::unique_ptr<RooAbsCollection> _globs(_obs.selectByAttrib("global", true));
1278 // RooArgSet _tmp; _tmp.add(*_globs);_ws->saveSnapshot(child.GetName(),_tmp);
1279 _obs.remove(*_globs);
1280
1281 // include any coords
1282 _obs.add(coords(false).argList(), true);
1283 // include axis var too, provided it's an observable
1284 if (auto ax = GetXaxis(); ax && dynamic_cast<RooAbsArg *>(ax->GetParent())->getAttribute("obs")) {
1285 _obs.add(*dynamic_cast<RooAbsArg *>(ax->GetParent()));
1286 }
1287 // check if ws already has a dataset with this name, if it does we may need to extend columns
1288 if (auto _d = _ws->data(child.GetName()); _d) {
1289 // add any missing obs
1290 RooArgSet l(_obs);
1291 l.remove(*_d->get(), true, true);
1292 if (!l.empty()) {
1293 auto _dd = dynamic_cast<RooDataSet *>(_d);
1294 if (!_dd)
1295 throw std::runtime_error("Cannot extend dataset with new columns");
1296 for (auto &x : l) {
1297 _dd->addColumn(*x);
1298 }
1299 }
1300 } else {
1301 RooRealVar w("weightVar", "weightVar", 1);
1302 _obs.add(w);
1303 RooDataSet d(child.GetName(), child.GetTitle(), _obs, "weightVar");
1304 _ws->import(d);
1305 // seems have to set bits after importing, not before
1306 if (auto __d = _ws->data(child.GetName()))
1307 __d->SetBit(1 << 20, _ws->allData().size() == 1); // sets as selected if is only ds
1308 }
1309 /*if(!_ws->data(child.GetName())) {
1310 RooRealVar w("weightVar", "weightVar", 1);
1311 RooArgSet _obs; _obs.add(w);
1312 RooDataSet d(child.GetName(), child.GetTitle(), _obs, "weightVar");
1313 _ws->import(d);
1314 }*/
1315 auto out = std::shared_ptr<TObject>(_ws->data(child.GetName()), [](TObject *) {});
1316
1317 if (out) {
1318 xRooNode o(out, fParent);
1319 if (child.get<TH1>())
1320 o = *child.get();
1321 return o;
1322 }
1323 }
1324 throw std::runtime_error("Cannot create dataset");
1325 }
1326
1327 if (!get()) {
1328 if (!fParent)
1329 throw std::runtime_error("Cannot add to null object with no parentage");
1330
1331 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
1332 try {
1333 fComp = fParent->Add(*this, "+").fComp;
1334 } catch (...) {
1335 resize(size() - 1);
1336 std::rethrow_exception(std::current_exception());
1337 }
1338 resize(size() - 1); // remove the temporarily added node
1339
1340 if (!fComp) {
1341 throw std::runtime_error("No object");
1342 }
1343 }
1344
1345 if (auto p = get<RooAbsData>(); p) {
1346 auto _h = child.get<TH1>();
1347 if (!_h) {
1348 throw std::runtime_error("Can only add histogram to data");
1349 }
1350 auto _pdf = parentPdf();
1351 if (!_pdf)
1352 throw std::runtime_error("Could not find pdf");
1353 auto _ax = _pdf->GetXaxis();
1354 if (!_ax) {
1355 throw std::runtime_error("Cannot determine binning to add data");
1356 }
1357
1358 RooArgSet obs;
1359 obs.add(*dynamic_cast<RooAbsArg *>(_ax->GetParent()));
1360 obs.add(coords().argList()); // will also move obs to coords
1361
1362 // add any missing obs
1363 RooArgSet l(obs);
1364 l.remove(*p->get(), true, true);
1365 if (!l.empty()) {
1366 auto _d = dynamic_cast<RooDataSet *>(p);
1367 if (!_d)
1368 throw std::runtime_error("Cannot extend dataset with new columns");
1369 for (auto &x : l) {
1370 _d->addColumn(*x);
1371 }
1372 }
1373
1374 for (int i = 1; i <= _h->GetNbinsX(); i++) {
1375 dynamic_cast<RooAbsRealLValue *>(_ax->GetParent())->setVal(_h->GetBinCenter(i));
1376 p->add(obs, _h->GetBinContent(i));
1377 }
1378
1379 return *this;
1380 }
1381
1382 if (auto p = get<RooAddPdf>(); p && child.get<RooAbsPdf>()) {
1383 auto out = acquire(child.fComp);
1384 const_cast<RooArgList&>(p->coefList()).add(*acquire<RooRealVar>("1", "1", 1));
1385 const_cast<RooArgList&>(p->pdfList()).add(*std::dynamic_pointer_cast<RooAbsReal>(out));
1386 sterilize();
1387 return xRooNode(out, *this);
1388 }
1389
1390 if (auto p = get<RooRealSumPdf>(); p) {
1391 std::shared_ptr<TObject> out;
1392 auto cc = child.fComp;
1393 bool isConverted = (cc != child.convertForAcquisition(*this, sOpt));
1394 if (child.get<RooAbsReal>())
1395 out = acquire(child.fComp);
1396 if (!child.fComp && getObject<RooAbsReal>(child.GetName())) {
1397 Info("Add", "Adding existing function %s to %s", child.GetName(), p->GetName());
1398 out = getObject<RooAbsReal>(child.GetName());
1399 }
1400
1401 if (!out && !child.fComp) {
1402 std::shared_ptr<RooAbsArg> _func;
1403 // a null node .. so create either a new RooProduct or RooHistFunc if has observables (or no deps but has
1404 // x-axis)
1405 auto _obs = obs();
1406 if (!_obs.empty() || GetXaxis()) {
1407 if (_obs.empty()) {
1408 // using X axis to construct hist
1409 auto _ax = dynamic_cast<Axis2 *>(GetXaxis());
1410 auto t = TH1::AddDirectoryStatus();
1411 TH1::AddDirectory(false);
1412 auto h =
1413 std::make_unique<TH1D>(child.GetName(), child.GetTitle(), _ax->GetNbins(), _ax->binning()->array());
1415 h->GetXaxis()->SetName(TString::Format("%s;%s", _ax->GetParent()->GetName(), _ax->GetName()));
1416 // technically convertForAcquisition has already acquired so no need to re-acquire but should be harmless
1417 _func = std::dynamic_pointer_cast<RooAbsArg>(acquire(xRooNode(*h).convertForAcquisition(*this)));
1418 } else if (_obs.size() == 1) {
1419 // use the single obs to make a TH1D
1420 auto _x = _obs.at(0)->get<RooAbsLValue>();
1421 auto _bnames = _x->getBinningNames();
1422 TString binningName = p->getStringAttribute("binning");
1423 for (auto &b : _bnames) {
1424 if (b == p->GetName()) {
1425 binningName = p->GetName();
1426 break;
1427 }
1428 }
1429 auto t = TH1::AddDirectoryStatus();
1430 TH1::AddDirectory(false);
1431 auto h = std::make_unique<TH1D>(child.GetName(), child.GetTitle(), _x->numBins(binningName),
1432 _x->getBinningPtr(binningName)->array());
1434 h->GetXaxis()->SetName(
1435 TString::Format("%s;%s", dynamic_cast<TObject *>(_x)->GetName(), binningName.Data()));
1436 // technically convertForAcquisition has already acquired so no need to re-acquire but should be harmless
1437 _func = std::dynamic_pointer_cast<RooAbsArg>(acquire(xRooNode(*h).convertForAcquisition(*this)));
1438 Info("Add", "Created densityhisto factor %s for %s", _func->GetName(), p->GetName());
1439 } else {
1440 throw std::runtime_error("Unsupported creation of new component in SumPdf for this many obs");
1441 }
1442 } else {
1443 _func = acquireNew<RooProduct>(TString::Format("%s_%s", p->GetName(), child.GetName()), child.GetTitle(),
1444 RooArgList());
1445 }
1446 _func->setStringAttribute("alias", child.GetName());
1447 out = _func;
1448 }
1449
1450 if (auto _f = std::dynamic_pointer_cast<RooHistFunc>(
1451 (child.get<RooProduct>()) ? child.factors()[child.GetName()]->fComp : out);
1452 _f) {
1453 // adding a histfunc directly to a sumpdf, should be a density
1454 _f->setAttribute("density");
1455 if (_f->getAttribute("autodensity")) {
1456 // need to divide by bin widths first
1457 for (int i = 0; i < _f->dataHist().numEntries(); i++) {
1458 auto bin_pars = _f->dataHist().get(i);
1459 _f->dataHist().set(*bin_pars, _f->dataHist().weight() / _f->dataHist().binVolume(*bin_pars));
1460 }
1461 _f->setAttribute("autodensity", false);
1462 _f->setValueDirty();
1463 }
1464
1465 // promote the axis vars to observables
1466 // can't use original child as might refer to unacquired deps
1467 for (auto &x : xRooNode("tmp", _f).vars()) {
1468 x->get<RooAbsArg>()->setAttribute("obs");
1469 }
1470 if (isConverted)
1471 Info("Add", "Created %s factor RooHistFunc::%s for %s",
1472 _f->getAttribute("density") ? "densityhisto" : "histo", _f->GetName(), p->GetName());
1473 }
1474
1475 if (auto _f = std::dynamic_pointer_cast<RooAbsReal>(out); _f) {
1476
1477 // todo: if adding a pdf, should actually replace RooRealSumPdf with a RooAddPdf and put
1478 // the sumPdf and *this* pdf inside that pdf
1479 // only exception is the binSamplingPdf below to integrate unbinned functions across bins
1480
1481 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent())) {
1482
1483 if (auto _boundaries = std::unique_ptr<std::list<double>>(_f->binBoundaries(
1484 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
1485 std::numeric_limits<double>::infinity()));
1486 !_boundaries && _ax->GetNbins() > 0) {
1487#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
1488 Warning("Add", "Adding unbinned function %s to binned %s - will wrap it in a RooBinSamplingPdf",
1489 _f->GetName(), GetName());
1490 auto sumPdf = acquireNew<RooRealSumPdf>(TString::Format("%s_pdfWrapper", _f->GetName()), _f->GetTitle(),
1491 *_f, *acquire<RooRealVar>("1", "1", 1), true);
1492 sumPdf->setStringAttribute("alias", _f->getStringAttribute("alias"));
1493 if (!sumPdf->getStringAttribute("alias"))
1494 sumPdf->setStringAttribute("alias", out->GetName());
1495 _f = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _f->GetName()), _f->GetTitle(),
1496 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *sumPdf);
1497 _f->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
1498 if (!_f->getStringAttribute("alias"))
1499 _f->setStringAttribute("alias", out->GetName());
1500#else
1501 throw std::runtime_error(
1502 "unsupported addition of unbinned function to binned model - please upgrade to at least ROOT 6.24");
1503#endif
1504 }
1505 }
1506
1507 const_cast<RooArgList&>(p->coefList()).add(*acquire<RooRealVar>("1", "1", 1));
1508 const_cast<RooArgList&>(p->funcList()).add(*_f);
1509 // inherit binning if we dont have one yet
1510 if (!p->getStringAttribute("binning"))
1511 p->setStringAttribute("binning", _f->getStringAttribute("binning"));
1512
1513 xRooNode _out(_f, *this);
1514 if (auto gf = p->getStringAttribute("global_factors"); gf) {
1515 TStringToken pattern(gf, ";");
1516 while (pattern.NextToken()) {
1517 auto fac = getObject<RooAbsReal>(pattern.Data());
1518 if (!fac) {
1519 throw std::runtime_error(TString::Format("Could not find global factor %s", pattern.Data()));
1520 }
1521 _out.Multiply(fac);
1522 }
1523 }
1524 sterilize();
1525 return _out;
1526 }
1527 } else if (auto p2 = get<RooProdPdf>(); p2) {
1528 // can "add" to a RooProdPdf provided trying to add a RooAbsReal not a RooAbsPdf and have a zero or 1
1529 // RooRealSumPdf child.convertForAcquisition(*this); - don't convert here because want generated objects named
1530 // after roorealsumpdf
1531 if ((child.get<TH1>() || child.get<RooAbsReal>() || (!child.get() && getObject<RooAbsReal>(child.GetName()))) &&
1532 !child.get<RooAbsPdf>()) {
1533 RooRealSumPdf *_pdf = nullptr;
1534 bool tooMany(false);
1535 for (auto &pp : factors()) {
1536 if (auto _p = pp->get<RooRealSumPdf>(); _p) {
1537 if (_pdf) {
1538 _pdf = nullptr;
1539 tooMany = true;
1540 break;
1541 } // more than one!
1542 _pdf = _p;
1543 }
1544 }
1545 if (_pdf) {
1546 return xRooNode(*_pdf, *this).Add(child);
1547 } else if (!tooMany) {
1548 auto out = this->operator[]("samples")->Add(child);
1549 return out;
1550 }
1551 } else if (child.get<RooAbsPdf>()) {
1552 // can add if 0 or 1 RooAddPdf ....
1553 RooAddPdf *_pdf = nullptr;
1554 bool tooMany(false);
1555 for (auto &pp : factors()) {
1556 if (auto _p = pp->get<RooAddPdf>(); _p) {
1557 if (_pdf) {
1558 _pdf = nullptr;
1559 tooMany = true;
1560 break;
1561 } // more than one!
1562 _pdf = _p;
1563 }
1564 }
1565 if (_pdf) {
1566 return xRooNode(*_pdf, *this).Add(child);
1567 } else if (!tooMany) {
1568 auto out = this->operator[]("components")->Add(child);
1569 return out;
1570 }
1571 }
1572 } else if (auto s = get<RooSimultaneous>(); s) {
1573
1574 // adding to a simultaneous means adding a bin
1575 return bins().Add(child);
1576
1577 // if the child is a RooAbsPdf can just add it as a new channel using name of pdf as the channel name
1578 // if child is a histogram, will create a RooProdPdf
1579
1580 } else if (auto w = get<RooWorkspace>(); w) {
1581 child.convertForAcquisition(*this);
1582 if (child.get()) {
1583 auto out = acquire(child.fComp);
1584 if (out)
1585 return xRooNode(child.GetName(), out, *this);
1586 }
1587
1588 if (!child.empty() || child.fFolder == "!models") {
1589 // create a RooSimultaneous using the children as the channels
1590 // children either have "=" in name if specifying channel cat name or otherwise assume
1591 std::string catName = "channelCat";
1592 if (!child.empty()) {
1593 if (TString ss = child.at(0)->GetName(); ss.Contains("=")) {
1594 catName = ss(0, ss.Index('='));
1595 }
1596 }
1597 auto _cat = acquire<RooCategory>(catName.c_str(), catName.c_str());
1598 _cat->setAttribute("obs");
1599 auto out = acquireNew<RooSimultaneous>(child.GetName(), child.GetTitle(), *_cat);
1600 Info("Add", "Created model RooSimultaneous::%s in workspace %s", out->GetName(), w->GetName());
1601 return xRooNode(out, *this);
1602 }
1603 }
1604
1605 if (sOpt == "model") {
1606 // can only add a model to a workspace
1607 if (get<RooWorkspace>()) {
1608 const_cast<xRooNode &>(child).fFolder = "!models";
1609 return Add(child);
1610 }
1611 } else if (sOpt == "channel") {
1612 // can add to a model or to a workspace (creates a RooProdPdf either way)
1613 if (get<RooSimultaneous>()) {
1614 return Vary(child);
1615 } else if (get<RooWorkspace>()) {
1616 std::shared_ptr<TObject> out;
1617 child.convertForAcquisition(*this);
1618 if (child.get<RooAbsPdf>())
1619 out = acquire(child.fComp);
1620 else if (!child.fComp) {
1621 out = acquireNew<RooProdPdf>(child.GetName(),
1622 (strlen(child.GetTitle())) ? child.GetTitle() : child.GetName(), RooArgList());
1623 Info("Add", "Created channel RooProdPdf::%s in workspace %s", out->GetName(), get()->GetName());
1624 }
1625 return xRooNode(out, *this);
1626 }
1627 } else if (sOpt == "sample" || sOpt == "func") {
1628 if (get<RooProdPdf>()) {
1629 auto _mainChild = mainChild();
1630 if (_mainChild.get<RooRealSumPdf>()) {
1631 return _mainChild.Add(child, sOpt == "func" ? "func" : "");
1632 } else {
1633 return (*this)["samples"]->Add(child, sOpt == "func" ? "func" : "");
1634 }
1635 }
1636 } else if (sOpt == "dataset") {
1637 if (get<RooWorkspace>()) {
1638 // const_cast<xRooNode&>(child).fFolder = "!datasets";return Add(child);
1639 return (*this).datasets().Add(child);
1640 }
1641 }
1642
1643 if (considerType) {
1644
1645 // interpret 'adding' here as dependent on the object type ...
1646 if (get<RooSimultaneous>()) {
1647 return bins().Add(child);
1648 } else if(TString(child.GetName()).Contains('=')) {
1649 return variations().Add(child);
1650 } else if (get<RooProduct>() || get<RooProdPdf>()) {
1651 return factors().Add(child);
1652 }
1653 }
1654
1655 // Nov 2022 - removed ability to add placeholders ... could bring back if rediscover need for them
1656 // if (!child.get() && child.empty() && strlen(child.GetName())) {
1657 // // can add a 'placeholder' node, note it will be deleted at the next browse
1658 // xRooNode out(child.GetName(),nullptr,*this);
1659 // out.SetTitle(child.GetTitle());
1660 // emplace_back(std::make_shared<xRooNode>(out));
1661 // // update the parent in the out node so that it's copy of the parent knows it has itself in it
1662 // // actually maybe not want this :-/
1663 // //out.fParent = std::make_shared<Node2>(*this);
1664 // for(auto o : *gROOT->GetListOfBrowsers()) {
1665 // if(auto b = dynamic_cast<TBrowser*>(o); b && b->GetBrowserImp()){
1666 // if(auto _b = dynamic_cast<TGFileBrowser*>(
1667 // dynamic_cast<TRootBrowser*>(b->GetBrowserImp())->fActBrowser ); _b) {
1668 // auto _root = _b->fRootDir;
1669 // if (!_root) _root = _b->fListTree->GetFirstItem();
1670 // if (auto item = _b->fListTree->FindItemByObj(_root,this); item) {
1671 // _b->fListTree->AddItem(item,back()->GetName(),back().get());
1672 // }
1673 // }
1674 // }
1675 // }
1676 // return out;
1677 // }
1678
1679 throw std::runtime_error(TString::Format("Cannot add %s to %s", child.GetName(), GetName()));
1680}
1681
1682std::string xRooNode::GetPath() const
1683{
1684 if (!fParent)
1685 return GetName();
1686 return fParent->GetPath() + "/" + GetName();
1687}
1688
1690{
1691 // std::cout << "deleting " << GetPath() << std::endl;
1692}
1693
1695{
1696 if (auto a = get<RooAbsArg>()) {
1697 a->setAttribute("hidden", set);
1698 // if(auto item = GetTreeItem(nullptr); item) {
1699 // if(set) item->SetColor(kRed);
1700 // else item->ClearColor();
1701 // }
1702 }
1703}
1705{
1706 auto a = get<RooAbsArg>();
1707 if (a)
1708 return a->getAttribute("hidden");
1709 return false;
1710}
1711
1713{
1714
1715 if (get() == rhs.get()) {
1716 // nothing to do because objects are identical
1717 return *this;
1718 }
1719
1720 // Info("Combine","Combining %s into %s",rhs.GetPath().c_str(),GetPath().c_str());
1721
1722 // combine components, factors, and variations ... when there is a name clash will combine on that object
1723 for (auto &c : rhs.components()) {
1724 if (auto _c = components().find(c->GetName()); _c) {
1725 _c->Combine(*c);
1726 } else {
1727 Add(*c);
1728 }
1729 }
1730
1731 for (auto &f : rhs.factors()) {
1732 if (auto _f = factors().find(f->GetName()); _f) {
1733 _f->Combine(*f);
1734 } else {
1735 Multiply(*f);
1736 }
1737 }
1738
1739 for (auto &v : rhs.variations()) {
1740 if (auto _v = variations().find(v->GetName()); _v) {
1741 _v->Combine(*v);
1742 } else {
1743 Vary(*v);
1744 }
1745 }
1746
1747 // todo: Should also transfer over binnings of observables
1748
1749 return *this;
1750}
1751
1752xRooNode xRooNode::shallowCopy(const std::string &name, std::shared_ptr<xRooNode> parent)
1753{
1754 xRooNode out(name.c_str(), nullptr,
1755 parent /*? parent : fParent -- was passing fParent for getObject benefit before fProvider concept*/);
1756 // if(!parent) out.fAcquirer = true;
1757 if (!parent)
1758 out.fProvider = fParent;
1759
1760 auto o = get();
1761 if (!o) {
1762 return out;
1763 }
1764
1765 if (auto s = get<RooSimultaneous>(); s) {
1766 auto chans = bins();
1767 if (!chans.empty()) {
1768 // create a new RooSimultaneous with shallow copies of each channel
1769
1770 std::shared_ptr<RooSimultaneous> pdf = out.acquire<RooSimultaneous>(
1771 name.c_str(), o->GetTitle(), const_cast<RooAbsCategoryLValue &>(s->indexCat()));
1772
1773 for (auto &c : chans) {
1774 TString cName(c->GetName());
1775 cName = cName(cName.Index('=') + 1, cName.Length());
1776 // by passing out as the parent, will ensure out acquires everything created
1777 auto c_copy =
1778 c->shallowCopy(name + "_" + c->get()->GetName(), std::shared_ptr<xRooNode>(&out, [](xRooNode *) {}));
1779 pdf->addPdf(*dynamic_cast<RooAbsPdf *>(c_copy.get()), cName);
1780 }
1781 out.fComp = pdf;
1782 return out;
1783 }
1784 } else if (auto p = dynamic_cast<RooProdPdf *>(o); p) {
1785 // main pdf will be copied too
1786 std::shared_ptr<RooProdPdf> pdf = std::dynamic_pointer_cast<RooProdPdf>(
1787 out.acquire(std::shared_ptr<TObject>(p->Clone(name.c_str())))); // use clone to copy all attributes etc too
1788 auto main = mainChild();
1789 if (main) {
1790 auto newMain = std::dynamic_pointer_cast<RooAbsArg>(
1791 out.acquire(std::shared_ptr<TObject>(main->Clone((name + "_pdf").c_str()))));
1792 pdf->replaceServer(*pdf->pdfList().find(main->GetName()), *newMain, true, true);
1793 const_cast<RooArgList&>(pdf->pdfList()).replace(*pdf->pdfList().find(main->GetName()), *newMain);
1794// pdf->_cacheMgr.reset();
1795// pdf->setValueDirty();
1796// pdf->setNormRange(0);
1797 }
1798 out.fComp = pdf;
1799 out.sterilize();
1800 return out;
1801 }
1802
1803 return out;
1804}
1805
1807{
1808 TString sOpt(opt);
1809 int depth = 0;
1810 if (sOpt.Contains("depth=")) {
1811 depth = TString(sOpt(sOpt.Index("depth=") + 6, sOpt.Length())).Atoi();
1812 sOpt.ReplaceAll(TString::Format("depth=%d", depth), "");
1813 }
1814 int indent = 0;
1815 if (sOpt.Contains("indent=")) {
1816 indent = TString(sOpt(sOpt.Index("indent=") + 7, sOpt.Length())).Atoi();
1817 sOpt.ReplaceAll(TString::Format("indent=%d", indent), "");
1818 }
1819 bool _more = sOpt.Contains("m");
1820 if (_more)
1821 sOpt.Replace(sOpt.Index("m"), 1, "");
1822 if (sOpt != "")
1823 _more = true;
1824
1825 if (indent == 0) { // only print self if not indenting (will already be printed above if tree traverse)
1826 std::cout << GetPath();
1827 if (get() && get() != this) {
1828 std::cout << ": ";
1829 if (_more ||
1830 (get<RooAbsArg>() && (get<RooAbsArg>()->isFundamental() || get<RooConstVar>() || get<RooAbsData>())) ||
1831 get<RooProduct>()) {
1832 auto _deps = coords(false).argList(); // want to revert coords after print
1833 auto _snap = std::unique_ptr<RooAbsCollection>(_deps.snapshot());
1834 coords(); // move to coords before printing (in case this matters)
1835 get()->Print(sOpt);
1836 if (auto _fr = get<RooFitResult>(); _fr && dynamic_cast<RooStringVar *>(_fr->constPars().find(".log"))) {
1837 std::cout << "Minimization Logs:" << std::endl;
1838 std::cout << dynamic_cast<RooStringVar *>(_fr->constPars().find(".log"))->getVal() << std::endl;
1839 }
1840 _deps.assignValueOnly(*_snap);
1841 // std::cout << std::endl;
1842 } else
1843 std::cout << get()->ClassName() << "::" << get()->GetName() << std::endl;
1844 } else if (!get()) {
1845 std::cout << std::endl;
1846 }
1847 }
1848 const_cast<xRooNode *>(this)->browse();
1849 std::vector<std::string> folderNames;
1850 for (auto &k : *this) {
1851 if (std::find(folderNames.begin(), folderNames.end(), k->fFolder) == folderNames.end()) {
1852 folderNames.push_back(k->fFolder);
1853 }
1854 }
1855 for (auto &f : folderNames) {
1856 int i = 0;
1857 int iindent = indent;
1858 if (!f.empty()) {
1859 for (int j = 0; j < indent; j++)
1860 std::cout << " ";
1861 std::cout << f << std::endl;
1862 iindent += 1;
1863 }
1864 for (auto &k : *this) {
1865 if (k->fFolder != f) {
1866 i++;
1867 continue;
1868 }
1869 for (int j = 0; j < iindent; j++)
1870 std::cout << " ";
1871 std::cout << i++ << ") " << k->GetName() << " : ";
1872 if (k->get()) {
1873 if (_more || (k->get<RooAbsArg>() && (k->get<RooAbsArg>()->isFundamental() || k->get<RooConstVar>() ||
1874 k->get<RooAbsData>())) /*|| k->get<RooProduct>()*/) {
1875 auto _deps = k->coords(false).argList();
1876 auto _snap = std::unique_ptr<RooAbsCollection>(_deps.snapshot());
1877 k->coords(); // move to coords before printing (in case this matters)
1878 k->get()->Print(sOpt); // assumes finishes with an endl
1879 _deps.assignValueOnly(*_snap);
1880 } else
1881 std::cout << k->get()->ClassName() << "::" << k->get()->GetName() << std::endl;
1882 if (depth != 0) {
1883 k->Print(sOpt + TString::Format("depth=%dindent=%d", depth - 1, iindent + 1));
1884 }
1885 } else
1886 std::cout << " NULL " << std::endl;
1887 }
1888 }
1889}
1890
1892{
1893 if (!child.get()) {
1894
1895 if (auto v = get<RooRealVar>(); v) {
1896
1897 TString constrType = child.GetName();
1898 double mean = std::numeric_limits<double>::quiet_NaN();
1899 double sigma = mean;
1900 if (constrType.BeginsWith("gaussian(")) {
1901 // extract the mean and stddev parameters
1902 // if only one given, it is the stddev
1903 if (constrType.Contains(",")) {
1904 mean = TString(constrType(9, constrType.Index(',') - 9)).Atof();
1905 sigma = TString(constrType(constrType.Index(',') + 1, constrType.Index(')') - constrType.Index(',') + 1))
1906 .Atof();
1907 } else {
1908 mean = std::numeric_limits<double>::quiet_NaN(); // will use the var current value below to set mean
1909 sigma = TString(constrType(9, constrType.Index(')') - 9)).Atof();
1910 }
1911 constrType = "normal";
1912 } else if (constrType == "normal") {
1913 mean = 0;
1914 sigma = 1;
1915 } else if (constrType == "gaussian") {
1916 // extract parameters from the variable
1917 // use current value and error on v as constraint
1918 if (!v->hasError())
1919 throw std::runtime_error("No error on parameter for gaussian constraint");
1920 sigma = v->getError();
1921 mean = v->getVal();
1922 constrType = "normal";
1923 } else if (constrType == "poisson") {
1924 if (!v->hasError())
1925 throw std::runtime_error("No error on parameter for poisson constraint");
1926 mean = 1;
1927 sigma = pow(v->getVal() / v->getError(), 2);
1928 }
1929
1930 if (constrType == "poisson") {
1931 // use current value and error on v as constraint
1932 double tau_val = sigma;
1933 auto globs = acquire<RooRealVar>(Form("globs_%s", v->GetName()), Form("globs_%s", v->GetName()),
1934 v->getVal() * tau_val, (v->getVal() - 5 * v->getError()) * tau_val,
1935 (v->getVal() + 5 * v->getError()) * tau_val);
1936 globs->setConstant();
1937 globs->setAttribute("obs");
1938 globs->setAttribute("global");
1939 globs->setStringAttribute("nominal", TString::Format("%f", tau_val));
1940 auto tau = acquireNew<RooConstVar>(TString::Format("tau_%s", v->GetName()), "", tau_val);
1941 auto constr = acquireNew<RooPoisson>(
1942 Form("pois_%s", v->GetName()), TString::Format("Poisson Constraint of %s", v->GetTitle()), *globs,
1943 *acquireNew<RooProduct>(TString::Format("mean_%s", v->GetName()),
1944 TString::Format("Poisson Constraint of %s", globs->GetTitle()),
1945 RooArgList(*v, *tau)),
1946 true /* no rounding */);
1947
1948 auto out = Constrain(xRooNode(Form("pois_%s", GetName()), constr));
1949 if (!v->hasError())
1950 v->setError(mean / sqrt(tau_val)); // if v doesnt have an uncert, will put one on it now
1951 Info("Constrain", "Added poisson constraint pdf RooPoisson::%s (tau=%g) for %s", out->GetName(), tau_val,
1952 GetName());
1953 return out;
1954 } else if (constrType == "normal") {
1955
1956 auto globs = acquire<RooRealVar>(Form("globs_%s", v->GetName()), Form("globs_%s", v->GetName()), mean,
1957 mean - 10 * sigma, mean + 10 * sigma);
1958 globs->setAttribute("obs");
1959 globs->setAttribute("global");
1960 globs->setConstant();
1961
1962 globs->setStringAttribute("nominal", TString::Format("%f", mean));
1963 auto constr = acquireNew<RooGaussian>(
1964 Form("gaus_%s", v->GetName()), TString::Format("Gaussian Constraint of %s", v->GetTitle()), *globs, *v,
1965 *acquireNew<RooConstVar>(TString::Format("sigma_%s", v->GetName()), "", sigma));
1966 auto out = Constrain(xRooNode(Form("gaus_%s", GetName()), constr));
1967 if (!v->hasError())
1968 v->setError(sigma); // if v doesnt have an uncert, will put one on it now
1969 Info("Constrain", "Added gaussian constraint pdf RooGaussian::%s (mean=%g,sigma=%g) for %s", out->GetName(),
1970 mean, sigma, GetName());
1971 return out;
1972 }
1973 }
1974 } else if (auto p = child.get<RooAbsPdf>(); p) {
1975
1976 auto _me = get<RooAbsArg>();
1977 if (!_me) {
1978 throw std::runtime_error("Cannot constrain non arg");
1979 }
1980
1981 if (!p->dependsOn(*_me)) {
1982 throw std::runtime_error("Constraint does not depend on constrainee");
1983 }
1984
1985 // find a parent that can swallow this pdf ... either a RooProdPdf or a RooWorkspace
1986 auto x = fParent;
1987 while (x && !x->get<RooProdPdf>() && !x->get<RooSimultaneous>() && !x->get<RooWorkspace>()) {
1988 x = x->fParent;
1989 }
1990 if (!x) {
1991 throw std::runtime_error("Nowhere to put constraint");
1992 }
1993
1994 if (auto s = x->get<RooSimultaneous>(); s) {
1995 // put into every channel that features parameter
1996 x->browse();
1997 for (auto &c : *x) {
1998 if (auto a = c->get<RooAbsArg>(); a->dependsOn(*_me))
1999 c->Multiply(child);
2000 }
2001 return child;
2002 } else if (x->get<RooProdPdf>()) {
2003 return x->Multiply(child);
2004 } else {
2005 return x->Add(child, "+");
2006 }
2007 }
2008
2009 throw std::runtime_error(TString::Format("Cannot constrain %s", GetName()));
2010}
2011
2013{
2014
2015 class AutoUpdater {
2016 public:
2017 AutoUpdater(xRooNode &_n) : n(_n) {}
2018 ~AutoUpdater() { n.browse(); }
2019 xRooNode &n;
2020 };
2021 AutoUpdater xxx(*this);
2022
2023 if (fBinNumber != -1) {
2024 // scaling a bin ...
2025 if (child.get<RooAbsReal>()) { // if not child then let fall through to create a child and call self again below
2026 // doing a bin-multiplication .. the parent should have a ParamHistFunc called binFactors
2027 // if it doesn't then create one
2028 auto o = std::dynamic_pointer_cast<RooAbsReal>(acquire(child.fComp));
2029
2030 // get binFactor unless parent is a ParamHistFunc already ...
2031
2032 auto binFactors = (fParent->get<ParamHistFunc>()) ? fParent : fParent->factors().find("binFactors");
2033
2034 // it can happen in a loop over bins() that another node has moved fParent inside a product
2035 // so check for fParent having a client with the ORIGNAME:<name> attribute
2036 if (!binFactors && fParent->get<RooAbsArg>()) {
2037 for (auto c : fParent->get<RooAbsArg>()->clients()) {
2038 if (c->IsA() == RooProduct::Class() &&
2039 c->getAttribute(TString::Format("ORIGNAME:%s", fParent->get()->GetName()))) {
2040 // try getting binFactors out of this
2041 binFactors = xRooNode(*c).factors().find("binFactors");
2042 break;
2043 }
2044 }
2045 }
2046
2047 if (!binFactors) {
2048 fParent
2049 ->Multiply(TString::Format("%s_binFactors",
2050 (fParent->mainChild().get())
2051 ? fParent->mainChild()->GetName()
2052 : (fParent->get() ? fParent->get()->GetName() : fParent->GetName()))
2053 .Data(),
2054 "blankshape")
2055 .SetName("binFactors"); // creates ParamHistFunc with all pars = 1 (shared const)
2056 binFactors = fParent->factors().find("binFactors");
2057 if (!binFactors) {
2058 throw std::runtime_error(
2059 TString::Format("Could not create binFactors in parent %s", fParent->GetName()));
2060 }
2061 // auto phf = binFactors->get<ParamHistFunc>();
2062
2063 // create RooProducts for all the bins ... so that added factors don't affect selves
2064 int i = 1;
2065 for (auto &b : binFactors->bins()) {
2066 auto p = acquireNew<RooProduct>(TString::Format("%s_bin%d", binFactors->get()->GetName(), i),
2067 TString::Format("binFactors of bin %d", i), RooArgList());
2068 p->setStringAttribute("alias", TString::Format("%s=%g", binFactors->GetXaxis()->GetParent()->GetName(),
2069 binFactors->GetXaxis()->GetBinCenter(i)));
2070 b->Multiply(*p);
2071 i++;
2072 }
2073 }
2074 // then scale the relevant bin ... if the relevent bin is a "1" then just drop in our factor (inside a
2075 // RooProduct though, to avoid it getting modified by subsequent multiplies)
2076 auto _bin = binFactors->bins().at(fBinNumber - 1);
2077 if (auto phf = binFactors->get<ParamHistFunc>(); phf && _bin) {
2078#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
2079 RooArgList& pSet = phf->_paramSet;
2080#else
2081 RooArgList& pSet = const_cast<RooArgList&>(phf->paramList());
2082#endif
2083 if (strcmp(_bin->GetName(), "1") == 0) {
2084 RooArgList all;
2085 for (int i = 0; i < pSet.getSize(); i++) {
2086 if (i != fBinNumber - 1)
2087 all.add(*pSet.at(i));
2088 else
2089 all.add(*o);
2090 }
2091 pSet.removeAll();
2092 pSet.add(all);
2093 } else {
2094 _bin->fBinNumber = -1; // to avoid infinite loop
2095 return _bin->Multiply(child, opt);
2096 }
2097 // } else {else if(_bin->get<RooProduct>()) {
2098 // // multiply the element which will just add it as a factor in the rooproduct
2099 // return _bin->Multiply(child,opt);
2100 // } else {
2101 // // not a rooproduct in this bin yet ... so need to replace with a rooproduct and
2102 // multiply that
2103 // // this avoids the undesired behaviour of shared binFactors getting all impacted by
2104 // mulitplies RooArgList all; auto new_p =
2105 // acquireNew<RooProduct>(TString::Format("%s_bin%d",binFactors->get()->GetName(),fBinNumber),TString::Format("binFactors
2106 // of bin %d",fBinNumber),RooArgList(*_bin->get<RooAbsArg>()));
2107 // new_p->setStringAttribute("alias","")
2108 // for (int i = 0; i < phf->_paramSet.getSize(); i++) {
2109 // if (i != fBinNumber - 1) all.add(*phf->_paramSet.at(i));
2110 // else all.add(*new_p);
2111 // }
2112 // phf->_paramSet.removeAll();
2113 // phf->_paramSet.add(all);
2114 // // now multiply that bin having converted it to RooProduct
2115 // return binFactors->bins().at(fBinNumber - 1)->Multiply(child,opt);
2116 // }
2117 }
2118 return xRooNode(*o, binFactors);
2119 }
2120 } else if (!get() && fParent) {
2121 // try to 'create' object based on parentage
2122 // add child as a temporary child to help with decision making
2123 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
2124 try {
2125 fComp = fParent->Add(*this, "+").fComp;
2126 } catch (...) {
2127 resize(size() - 1);
2128 std::rethrow_exception(std::current_exception());
2129 }
2130 resize(size() - 1); // remove the temporarily added node
2131 }
2132
2133 if (!child.get()) {
2134 TString sOpt(opt);
2135 sOpt.ToLower();
2136 if (auto o = getObject<RooAbsReal>(child.GetName())) {
2137 auto out = Multiply(xRooNode(o, child.fParent));
2138 // have to protect bin case where get() is null (could change but then must change logic above too)
2139 if (get())
2140 Info("Multiply", "Scaled %s by existing factor %s::%s",
2141 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), o->ClassName(), o->GetName());
2142 return out;
2143 } else if (sOpt == "norm") {
2144 auto out = Multiply(RooRealVar(child.GetName(), child.GetTitle(), 1, -1e-5, 100));
2145 if (get())
2146 Info("Multiply", "Scaled %s by new norm factor %s",
2147 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
2148 return out;
2149 } else if (sOpt == "shape" || sOpt == "histo" || sOpt == "blankshape") {
2150 // needs axis defined
2151 if (auto ax = GetXaxis(); ax) {
2152 auto h = std::shared_ptr<TH1>(BuildHistogram(dynamic_cast<RooAbsLValue *>(ax->GetParent()), true));
2153 h->Reset();
2154 for (int i = 1; i <= h->GetNbinsX(); i++) {
2155 h->SetBinContent(i, 1);
2156 }
2157 h->SetMinimum(0);
2158 h->SetMaximum(100);
2159 h->SetName(TString::Format(";%s", child.GetName())); // ; char indicates don't "rename" this thing
2160 h->SetTitle(child.GetTitle());
2161 if (sOpt.Contains("shape"))
2162 h->SetOption(sOpt);
2163 auto out = Multiply(*h);
2164 if (get())
2165 Info("Multiply", "Scaled %s by new %s factor %s",
2166 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), sOpt.Data(), out->GetName());
2167 return out;
2168 }
2169 } else if (sOpt == "overall") {
2170 auto out = Multiply(acquireNew<RooStats::HistFactory::FlexibleInterpVar>(
2171 child.GetName(), child.GetTitle(), RooArgList(), 1, std::vector<double>(), std::vector<double>()));
2172 if (get() /* can happen this is null if on a bin node with no shapeFactors*/)
2173 Info("Multiply", "Scaled %s by new overall factor %s",
2174 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
2175 return out;
2176 } else if (sOpt == "func" && ws()) {
2177 // need to get way to get dependencies .. can't pass all as causes circular dependencies issues.
2178 if (auto arg = ws()->factory(TString("expr::") + child.GetName())) {
2179 auto out = Multiply(*arg);
2180 if (get() /* can happen this is null if on a bin node with no shapeFactors*/)
2181 Info("Multiply", "Scaled %s by new func factor %s",
2182 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
2183 return out;
2184 }
2185 }
2186 }
2187 if (auto h = child.get<TH1>(); h && strlen(h->GetOption()) == 0 && strlen(opt) > 0) {
2188 // put the option in the hist
2189 h->SetOption(opt);
2190 }
2191 if (auto w = get<RooWorkspace>(); w) {
2192 // just acquire
2193 std::shared_ptr<TObject> out;
2194 child.convertForAcquisition(*this);
2195 if (child.get<RooAbsReal>())
2196 out = acquire(child.fComp);
2197 return out;
2198 }
2199
2200 if (strcmp(GetName(), ".coefs") == 0) {
2201 // need to add this into the relevant coef ... if its not a RooProduct, replace it with one first
2202 if (auto p = fParent->fParent->get<RooAddPdf>()) {
2203 for (size_t i = 0; i < p->pdfList().size(); i++) {
2204 if (p->pdfList().at(i) == fParent->get<RooAbsArg>()) {
2205 auto coefs = p->coefList().at(i);
2206 if (!coefs->InheritsFrom("RooProduct")) {
2207 RooArgList oldCoef;
2208 if (!(strcmp(coefs->GetName(), "1") == 0 || strcmp(coefs->GetName(), "ONE") == 0))
2209 oldCoef.add(*coefs);
2210 auto newCoefs = fParent->acquireNew<RooProduct>(
2211 TString::Format("coefs_%s", fParent->GetName()),
2212 TString::Format("coefficients for %s", fParent->GetName()), oldCoef);
2213 RooArgList oldCoefs;
2214 for (size_t j = 0; j < p->coefList().size(); j++) {
2215 if (i == j)
2216 oldCoefs.add(*newCoefs);
2217 else
2218 oldCoefs.add(*p->coefList().at(j));
2219 }
2220 const_cast<RooArgList&>(p->coefList()).removeAll();
2221 const_cast<RooArgList&>(p->coefList()).add(oldCoefs);
2222 coefs = newCoefs.get();
2223 }
2224 return xRooNode(*coefs, fParent).Multiply(child);
2225 }
2226 }
2227 }
2228 throw std::runtime_error("this coefs case is not supported");
2229 }
2230
2231 if (auto p = get<RooProduct>(); p) {
2232 std::shared_ptr<TObject> out;
2233 auto cc = child.fComp;
2234 bool isConverted = (child.convertForAcquisition(*this) != cc);
2235 if (child.get<RooAbsReal>())
2236 out = acquire(child.fComp);
2237
2238 // child may be a histfunc or a rooproduct of a histfunc and a paramhist if has stat errors
2239 if (auto _f = std::dynamic_pointer_cast<RooHistFunc>(
2240 (child.get<RooProduct>()) ? child.factors()[child.GetName()]->fComp : out);
2241 _f && _f->getAttribute("autodensity")) {
2242 // should we flag this as a density? yes if there's no other term marked as the density
2243 bool hasDensity = false;
2244 for (auto &f : factors()) {
2245 if (f->get<RooAbsArg>()->getAttribute("density")) {
2246 hasDensity = true;
2247 break;
2248 }
2249 }
2250 _f->setAttribute("density", !hasDensity && fParent && fParent->get<RooRealSumPdf>());
2251 if (_f->getAttribute("density")) {
2252
2253 // need to divide by bin widths first
2254 for (int i = 0; i < _f->dataHist().numEntries(); i++) {
2255 auto bin_pars = _f->dataHist().get(i);
2256 _f->dataHist().set(*bin_pars, _f->dataHist().weight() / _f->dataHist().binVolume(*bin_pars));
2257 }
2258 _f->setValueDirty();
2259
2260 // promote the axis vars to observables
2261 for (auto &x : xRooNode("tmp", _f).vars()) {
2262 x->get<RooAbsArg>()->setAttribute("obs");
2263 }
2264 }
2265 _f->setAttribute("autodensity", false);
2266 }
2267
2268 if (isConverted && child.get<RooHistFunc>()) {
2269 Info("Multiply", "Created %s factor %s in %s",
2270 child.get<RooAbsArg>()->getAttribute("density") ? "densityhisto" : "histo", child->GetName(),
2271 p->GetName());
2272 } else if (isConverted && child.get<ParamHistFunc>()) {
2273 Info("Multiply", "Created shape factor %s in %s", child->GetName(), p->GetName());
2274 }
2275
2276 if (auto _f = std::dynamic_pointer_cast<RooAbsReal>(out); _f) {
2277#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
2278 p->_compRSet.add(*_f);
2279#else
2280 const_cast<RooArgList&>(p->realComponents()).add(*_f);
2281#endif
2282 p->setValueDirty();
2283
2284 browse();
2285 xRooNode _out(_f, *this);
2286 for (auto &_par : _out.pars()) {
2287 if (auto s = _par->get<RooAbsArg>()->getStringAttribute("boundConstraint"); s) {
2288 bool found = false;
2289 for (auto &_constr : _par->constraints()) {
2290 if (strcmp(s, _constr->get()->GetName()) == 0) {
2291 // constraint is already included
2292 found = true;
2293 break;
2294 }
2295 }
2296 if (!found) {
2297 Info("Multiply", "Pulling in %s boundConstraint: %s", _par->GetName(), s);
2298 auto _pdf = getObject<RooAbsPdf>(s);
2299 if (!_pdf) {
2300 throw std::runtime_error("Couldn't find boundConstraint");
2301 }
2302 _par->Constrain(_pdf);
2303 }
2304 }
2305 }
2306 sterilize();
2307 return _out;
2308 }
2309 } else if (auto p2 = get<RooProdPdf>(); p2) {
2310
2311 std::shared_ptr<TObject> out;
2312 child.convertForAcquisition(*this);
2313 if (child.get<RooAbsPdf>())
2314 out = acquire(child.fComp);
2315 else if (child.get<RooAbsReal>() && mainChild().get<RooRealSumPdf>()) {
2316 // cannot multiply a RooProdPdf by a non pdf
2317 throw std::runtime_error(TString::Format("Cannot multiply %s by non-pdf %s", GetName(), child.GetName()));
2318 // return mainChild().Add(child); - nov 2022 - used to do this but now replaced with exception above
2319 } else if (!child.get() || child.get<RooAbsReal>()) {
2320 // need to create or hide inside a sumpdf or rooadpdf
2321 std::shared_ptr<RooAbsPdf> _pdf;
2322 if (!child.get() && strcmp(child.GetName(), "components") == 0) {
2323 auto _sumpdf = acquireNew<RooAddPdf>(Form("%s_%s", p2->GetName(), child.GetName()),
2324 (strlen(child.GetTitle()) && strcmp(child.GetTitle(), child.GetName()))
2325 ? child.GetTitle()
2326 : p2->GetTitle(),
2327 RooArgList(), RooArgList());
2328 _pdf = _sumpdf;
2329 } else {
2330 auto _sumpdf = acquireNew<RooRealSumPdf>(
2331 Form("%s_%s", p2->GetName(), child.GetName()),
2332 (strlen(child.GetTitle()) && strcmp(child.GetTitle(), child.GetName())) ? child.GetTitle()
2333 : p2->GetTitle(),
2334 RooArgList(), RooArgList(), true);
2335 _sumpdf->setFloor(true);
2336 _pdf = _sumpdf;
2337 }
2338 _pdf->setStringAttribute("alias", child.GetName());
2339 // transfer axis attributes if present (TODO: should GetXaxis look beyond the immediate parent?)
2340 _pdf->setStringAttribute("xvar", p2->getStringAttribute("xvar"));
2341 _pdf->setStringAttribute("binning", p2->getStringAttribute("binning"));
2342 out = _pdf;
2343 Info("Multiply", "Created %s::%s in channel %s", _pdf->ClassName(), _pdf->GetName(), p2->GetName());
2344 if (child.get<RooAbsReal>())
2345 xRooNode(*out, *this).Add(child);
2346 }
2347
2348 if (auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out); _pdf) {
2349#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
2350 const_cast<RooArgList&>(p2->pdfList()).add(*_pdf);
2351#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
2352 p2->_pdfNSetList.emplace_back(std::make_unique<RooArgSet>("nset"));
2353#else
2354 p->_pdfNSetList.Add(new RooArgSet("nset"));
2355#endif
2356 if (!p2->canBeExtended() && _pdf->canBeExtended()) {
2357 p2->_extendedIndex = p2->_pdfList.size() - 1;
2358 }
2359#else
2360 p2->addPdfs(RooArgSet(*_pdf));
2361#endif
2362 // TODO: any more cleanup?
2363 sterilize();
2364// p2->_cacheMgr.reset();
2365// p2->setValueDirty();
2366// p2->setNormRange(0);
2367 browse();
2368 return xRooNode(_pdf, *this);
2369 }
2370 } else if (auto p3 = get<RooRealSumPdf>(); p3) {
2371 // multiplying all current and future components
2372 std::shared_ptr<TObject> out;
2373 child.convertForAcquisition(*this);
2374 if (child.get<RooAbsReal>()) {
2375 out = acquire(child.fComp);
2376 for (auto &c : components()) {
2377 c->Multiply(out);
2378 }
2379 TString s = p3->getStringAttribute("global_factors");
2380 if (s != "")
2381 s += ";";
2382 s += out->GetName();
2383 p3->setStringAttribute("global_factors", s);
2384 Info(
2385 "Multiply",
2386 "Flagged %s as a global factor in channel %s (is applied to all current and future samples in the channel)",
2387 out->GetName(), p3->GetName());
2388 return xRooNode(out, *this);
2389 }
2390
2391 } else if (auto p4 = get<RooAbsPdf>(); p4 && !(fParent && fParent->get<RooRealSumPdf>())) {
2392 // multiply the coefs (if this isn't part of a RooAddPdf or RooRealSumPdf then we will eventually throw exception
2393 return coefs().Multiply(child);
2394 } else if (auto p5 = get<RooAbsReal>(); p5 && (!get<RooAbsPdf>() || (fParent && fParent->get<RooRealSumPdf>()))) {
2395 // replace this obj with a RooProduct to allow for multiplication
2396
2397 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
2398 std::set<RooAbsArg *> cl;
2399 for (auto &arg : p5->clients()) {
2400 cl.insert(arg);
2401 }
2402
2403 // if multiple clients, see if only one client is in parentage route
2404 // if so, then assume thats the only client we should replace in
2405 if (cl.size() > 1) {
2406 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
2407 cl.clear();
2408 cl.insert(fParent->get<RooAbsArg>());
2409 } else {
2410 Warning("Multiply", "Scaling %s that has multiple clients", p5->GetName());
2411 }
2412 }
2413
2414 auto new_p = acquireNew<RooProduct>(TString::Format("prod_%s", p5->GetName()), p5->GetTitle(), RooArgList(*p5));
2415 // copy attributes over
2416 for (auto &a : p5->attributes())
2417 new_p->setAttribute(a.c_str());
2418 for (auto &a : p5->stringAttributes())
2419 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
2420 if (!new_p->getStringAttribute("alias"))
2421 new_p->setStringAttribute("alias", p5->GetName());
2422 auto old_p = p5;
2423 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
2424 for (auto arg : cl) {
2425 arg->redirectServers(RooArgSet(*new_p), false, true);
2426 }
2427
2428 fComp = new_p;
2429 return Multiply(child);
2430 }
2431
2432 // before giving up here, assume user wanted a norm factor type if child is just a name
2433 if (!child.get() && strlen(opt) == 0)
2434 return Multiply(child, "norm");
2435
2436 throw std::runtime_error(
2437 TString::Format("Cannot multiply %s by %s%s", GetPath().c_str(), child.GetName(),
2438 (!child.get() && strlen(opt) == 0) ? " (forgot to specify factor type?)" : ""));
2439}
2440
2442{
2443
2444 class AutoUpdater {
2445 public:
2446 AutoUpdater(xRooNode &_n) : n(_n) {}
2447 ~AutoUpdater() { n.browse(); }
2448 xRooNode &n;
2449 };
2450 AutoUpdater xxx(*this);
2451
2452 if (!get() && fParent) {
2453 // try to 'create' object based on parentage
2454 // add child as a temporary child to help with decision making
2455 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
2456 try {
2457 fComp = fParent->Add(*this, "+").fComp;
2458 } catch (...) {
2459 resize(size() - 1);
2460 std::rethrow_exception(std::current_exception());
2461 }
2462 resize(size() - 1); // remove the temporarily added node
2463 }
2464
2465 if (auto p = mainChild(); p) {
2466 // variations applied to the main child if has one
2467 return p.Vary(child);
2468 }
2469
2470 if (auto s = get<RooSimultaneous>(); s && s->indexCat().IsA()==RooCategory::Class()) {
2471 // name is used as cat label
2472 std::string label = child.GetName();
2473 if (auto pos = label.find("="); pos != std::string::npos)
2474 label = label.substr(pos + 1);
2475 if (!s->indexCat().hasLabel(label)) {
2476 static_cast<RooCategory&>(const_cast<RooAbsCategoryLValue &>(s->indexCat())).defineType(label.c_str());
2477 }
2478 std::shared_ptr<TObject> out;
2479 child.convertForAcquisition(*this);
2480 if (child.get<RooAbsPdf>())
2481 out = acquire(child.fComp); // may create a channel from a histogram
2482 else if (!child.fComp) {
2483 out = acquireNew<RooProdPdf>(TString::Format("%s_%s", s->GetName(), label.c_str()),
2484 (strlen(child.GetTitle())) ? child.GetTitle() : label.c_str(), RooArgList());
2485 Info("Vary", "Created channel RooProdPdf::%s in model %s", out->GetName(), s->GetName());
2486 }
2487
2488 if (auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out); _pdf) {
2489 s->addPdf(*_pdf, label.c_str());
2490 sterilize();
2491 // clear children for reload and update shared axis
2492 clear(); fXAxis.reset();
2493 browse();
2494 return xRooNode(TString::Format("%s=%s", s->indexCat().GetName(), label.data()), _pdf, *this);
2495 }
2496
2497 } else if (auto p = get<RooStats::HistFactory::FlexibleInterpVar>(); p) {
2498
2499 // child needs to be a constvar ...
2500 child.convertForAcquisition(*this);
2501 auto _c = child.get<RooConstVar>();
2502 if (!_c && child.get()) {
2503 throw std::runtime_error("Only pure consts can be set as variations of a flexible interpvar");
2504 }
2505#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
2506 double value = (_c ? _c->getVal() : p->_nominal);
2507 double nomVal = p->_nominal;
2508#else
2509 double value = (_c ? _c->getVal() : p->nominal());
2510 double nomVal = p->nominal();
2511#endif
2512
2513 TString cName(child.GetName());
2514 if (cName == "nominal") {
2515 p->setNominal(value);
2516 return *(this->variations().at(cName.Data()));
2517 }
2518 if (cName.CountChar('=') != 1) {
2519 throw std::runtime_error("unsupported variation form");
2520 }
2521 std::string parName = cName(0, cName.Index('='));
2522 double parVal = TString(cName(cName.Index('=') + 1, cName.Length())).Atof();
2523 if (parVal != 1 && parVal != -1) {
2524 throw std::runtime_error("unsupported variation magnitude");
2525 }
2526 bool high = parVal > 0;
2527
2528 if (parName.empty()) {
2529 p->setNominal(value);
2530 } else {
2531 auto v = fParent->getObject<RooRealVar>(parName);
2532 if (!v)
2533 v = fParent->acquire<RooRealVar>(parName.c_str(), parName.c_str(), -5, 5);
2534 if (!v->hasError())
2535 v->setError(1);
2536
2537 if (!p->findServer(*v)) {
2538#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
2539 p->_paramList.add(*v);
2540 p->_low.push_back(0);
2541 p->_high.push_back(0);
2542 p->_interpCode.push_back(4);
2543#else
2544 const_cast<RooListProxy&>(p->variables()).add(*v);
2545 const_cast<std::vector<double>&>(p->low()).push_back(0);
2546 const_cast<std::vector<double>&>(p->high()).push_back(0);
2547 const_cast<std::vector<int>&>(p->interpolationCodes()).push_back(4);
2548#endif
2549 v->setAttribute(Form("SYMMETRIC%s_%s", high ? "+" : "-", GetName())); // flag for symmetrized
2550 }
2551
2552 if (high) {
2553 p->setHigh(*v, value);
2554 if (v->getAttribute(Form("SYMMETRIC+_%s", GetName()))) {
2555 p->setLow(*v, 2 * nomVal - value);
2556 }
2557 v->setAttribute(Form("SYMMETRIC-_%s", GetName()), false);
2558 } else {
2559 p->setLow(*v, value);
2560 if (v->getAttribute(Form("SYMMETRIC-_%s", GetName()))) {
2561 p->setHigh(*v, 2 * nomVal - value);
2562 }
2563 v->setAttribute(Form("SYMMETRIC+_%s", GetName()), false);
2564 }
2565
2566 /*if (!unconstrained && fParent->pars()[v->GetName()].constraints().empty()) {
2567 fParent->pars()[v->GetName()].constraints().add("normal");
2568 }*/
2569 }
2570 return *(this->variations().at(cName.Data()));
2571 } else if (auto p2 = get<PiecewiseInterpolation>(); p2) {
2572 TString cName(child.GetName());
2573 if (cName.CountChar('=') != 1) {
2574 throw std::runtime_error("unsupported variation form");
2575 }
2576 TString parName = cName(0, cName.Index('='));
2577 double parVal = TString(cName(cName.Index('=') + 1, cName.Length())).Atof();
2578 if (parVal != 1 && parVal != -1) {
2579 throw std::runtime_error("unsupported variation magnitude");
2580 }
2581#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
2582 RooHistFunc *f = dynamic_cast<RooHistFunc *>(p2->_nominal.absArg());
2583 if (!f) {
2584 throw std::runtime_error(
2585 TString::Format("Interpolating %s instead of RooHistFunc", p2->_nominal.absArg()->ClassName()));
2586 }
2587#else
2588 RooHistFunc *f = dynamic_cast<RooHistFunc *>(const_cast<RooAbsReal*>(p2->nominalHist()));
2589 if (!f) {
2590 throw std::runtime_error(
2591 TString::Format("Interpolating %s instead of RooHistFunc", p2->nominalHist()->ClassName()));
2592 }
2593#endif
2594 RooHistFunc *nomf = f;
2595 RooHistFunc *otherf = nullptr;
2596 size_t i = 0;
2597 for (auto par : p2->paramList()) {
2598 if (parName == par->GetName()) {
2599 f = dynamic_cast<RooHistFunc *>((parVal > 0 ? p2->highList() : p2->lowList()).at(i));
2600 otherf = dynamic_cast<RooHistFunc *>((parVal > 0 ? p2->lowList() : p2->highList()).at(i));
2601 break;
2602 }
2603 i++;
2604 }
2605 if (i == p2->paramList().size() && !child.get<RooAbsReal>()) {
2606
2607 // need to add the parameter
2608 auto v = acquire<RooRealVar>(parName, parName, -5, 5);
2609 if (!v->hasError())
2610 v->setError(1);
2611
2612 std::shared_ptr<RooHistFunc> up(static_cast<RooHistFunc *>(f->Clone(Form("%s_%s_up", f->GetName(), parName.Data()))));
2613 std::shared_ptr<RooHistFunc> down(static_cast<RooHistFunc *>(f->Clone(Form("%s_%s_down", f->GetName(), parName.Data()))));
2614 // RooHistFunc doesn't clone it's data hist ... do it ourself (will be cloned again if imported into a ws)
2615#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
2616 std::unique_ptr<RooDataHist> h1(static_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", up->GetName()))));
2617 std::unique_ptr<RooDataHist> h2(static_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", down->GetName()))));
2618 up->_dataHist = dynamic_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", up->GetName())));
2619 down->_dataHist = dynamic_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", down->GetName())));
2620#else
2621 up->cloneAndOwnDataHist(TString::Format("hist_%s", up->GetName()));
2622 down->cloneAndOwnDataHist(TString::Format("hist_%s", down->GetName()));
2623#endif
2624 auto ups = std::dynamic_pointer_cast<RooHistFunc>(acquire(up, false, true));
2625 auto downs = std::dynamic_pointer_cast<RooHistFunc>(acquire(down, false, true));
2626#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
2627 p2->_highSet.add(*ups.get());
2628 p2->_lowSet.add(*downs.get());
2629 p2->_interpCode.push_back(4);
2630 p2->_paramSet.add(*v);
2631#else
2632 const_cast<RooArgList&>(p2->highList()).add(*ups.get());
2633 const_cast<RooArgList&>(p2->lowList()).add(*downs.get());
2634 const_cast<std::vector<int>&>(p2->interpolationCodes()).push_back(4);
2635 const_cast<RooArgList&>(p2->paramList()).add(*v);
2636#endif
2637 p2->setValueDirty();
2638 f = ((parVal > 0) ? ups : downs).get();
2639 otherf = ((parVal > 0) ? downs : ups).get();
2640 // start off with everything being symmetric
2641 f->setStringAttribute("symmetrizes", otherf->GetName());
2642 f->setStringAttribute("symmetrize_nominal", nomf->GetName());
2643 otherf->setStringAttribute("symmetrized_by", f->GetName());
2644
2645 // constrain par if required
2646 /*if (!unconstrained && fParent->pars()[v->GetName()].constraints().empty()) {
2647 fParent->pars()[v->GetName()].constraints().add("normal");
2648 }*/
2649 }
2650
2651 // child.convertForAcquisition(*this);
2652 if (f) {
2653 if (child.get())
2654 xRooNode("tmp", *f, *this) = *child.get();
2655 f->setValueDirty();
2656 xRooNode out(*f, *this);
2657 out.sterilize();
2658 return out;
2659 }
2660
2661 } else if (auto p3 = get<RooConstVar>(); p3) {
2662
2663 // never vary the universal consts ... its too dangerous
2664 if (p3->getAttribute("RooRealConstant_Factory_Object")) {
2665 throw std::runtime_error("Cannot vary pure constants");
2666 }
2667
2668 // inject a FlexibleInterpVar ...
2669
2670 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
2671 std::set<RooAbsArg *> cl;
2672 for (auto &arg : p3->clients()) {
2673 cl.insert(arg);
2674 }
2675 // if multiple clients, see if only one client is in parentage route
2676 // if so, then assume thats the only client we should replace in
2677 if (cl.size() > 1) {
2678 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
2679 cl.clear();
2680 cl.insert(fParent->get<RooAbsArg>());
2681 } else {
2682 Warning("Vary", "Varying %s that has multiple clients", p3->GetName());
2683 }
2684 }
2685 p3->setStringAttribute("origName", p3->GetName());
2686 TString n = p3->GetName();
2687 p3->SetName(Form("%s_nominal", p3->GetName())); // if problems should perhaps not rename here
2688
2689 auto new_p = acquireNew<RooStats::HistFactory::FlexibleInterpVar>(n, p3->GetTitle(), RooArgList(), p3->getVal(),
2690 std::vector<double>(), std::vector<double>());
2691
2692 // copy attributes over
2693 for (auto &a : p3->attributes())
2694 new_p->setAttribute(a.c_str());
2695 for (auto &a : p3->stringAttributes())
2696 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
2697 // if (!new_p->getStringAttribute("alias")) new_p->setStringAttribute("alias",p->GetName());
2698 auto old_p = p3;
2699 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
2700 for (auto arg : cl) {
2701 arg->redirectServers(RooArgSet(*new_p), false, true);
2702 }
2703
2704 fComp = new_p;
2705 return Vary(child);
2706
2707 } else if (auto p4 = get<RooAbsReal>(); p4) {
2708 // inject an interpolation node
2709
2710 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
2711 std::set<RooAbsArg *> cl;
2712 for (auto &arg : p4->clients()) {
2713 cl.insert(arg);
2714 }
2715 // if multiple clients, see if only one client is in parentage route
2716 // if so, then assume thats the only client we should replace in
2717 if (cl.size() > 1) {
2718 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
2719 cl.clear();
2720 cl.insert(fParent->get<RooAbsArg>());
2721 } else {
2722 Warning("Vary", "Varying %s that has multiple clients", p4->GetName());
2723 }
2724 }
2725 p4->setStringAttribute("origName", p4->GetName());
2726 TString n = p4->GetName();
2727 p4->SetName(Form("%s_nominal", p4->GetName())); // if problems should perhaps not rename here
2728
2729 auto new_p = acquireNew<PiecewiseInterpolation>(n, p4->GetTitle(), *p4, RooArgList(), RooArgList(), RooArgList());
2730
2731 // copy attributes over
2732 for (auto &a : p4->attributes())
2733 new_p->setAttribute(a.c_str());
2734 for (auto &a : p4->stringAttributes())
2735 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
2736 // if (!new_p->getStringAttribute("alias")) new_p->setStringAttribute("alias",p->GetName());
2737 auto old_p = p4;
2738 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
2739 for (auto arg : cl) {
2740 arg->redirectServers(RooArgSet(*new_p), false, true);
2741 }
2742
2743 fComp = new_p;
2744 return Vary(child);
2745 }
2746
2747 Print();
2748 throw std::runtime_error(TString::Format("Cannot vary %s with %s", GetName(), child.GetName()));
2749}
2750
2752{
2754}
2755
2756bool xRooNode::SetContents(double value, const char *par, double val)
2757{
2758 return SetContents(RooConstVar(GetName(), GetTitle(), value), par, val);
2759}
2760
2763 {
2764 if (x && b)
2765 x->setBinning(*b);
2766 if (b)
2767 delete b;
2768 }
2769 RooRealVar *x = nullptr;
2770 RooAbsBinning *b = nullptr;
2771};
2772
2774{
2775
2776 if (!get()) {
2777 fComp = std::shared_ptr<TObject>(const_cast<TObject *>(&o), [](TObject *) {});
2778 if (fParent && !fParent->find(GetName())) {
2779 // either a temporary or a placeholder so need to try genuinely adding
2780 fComp = fParent->Add(*this, "+").fComp;
2781 if (auto a = get<RooAbsArg>(); a && strcmp(a->GetName(), GetName()) && !a->getStringAttribute("alias")) {
2782 a->setStringAttribute("alias", GetName());
2783 }
2784 if (!fComp)
2785 throw std::runtime_error("Cannot determine type");
2786 return *this;
2787 }
2788 }
2789
2790 if (auto h = dynamic_cast<const TH1 *>(&o); h) {
2791 /*auto f = get<RooHistFunc>();
2792 if (!f) {
2793 // if it's a RooProduct locate child with the same name
2794 if (get<RooProduct>()) {
2795 f = factors()[GetName()]->get<RooHistFunc>();
2796 }
2797
2798
2799
2800 }*/
2801 bool _isData = get<RooAbsData>();
2802 BinningRestorer _b;
2803 if (_isData) {
2804 // need to ensure x-axis matches this h
2805 auto ax = GetXaxis();
2806 if (!ax)
2807 throw std::runtime_error("no xaxis");
2808 auto _v = dynamic_cast<RooRealVar *>(ax->GetParent());
2809 if (_v) {
2810 _b.x = _v;
2811 _b.b = dynamic_cast<RooAbsBinning *>(_v->getBinningPtr(0)->Clone());
2812 if (h->GetXaxis()->IsVariableBinSize()) {
2813 _v->setBinning(RooBinning(h->GetNbinsX(), h->GetXaxis()->GetXbins()->GetArray()));
2814 } else {
2815 _v->setBinning(RooUniformBinning(h->GetXaxis()->GetXmin(), h->GetXaxis()->GetXmax(), h->GetNbinsX()));
2816 }
2817 }
2818 }
2819
2820 if (true) {
2821 for (int bin = 1; bin <= h->GetNbinsX(); bin++) {
2822 SetBinContent(bin, h->GetBinContent(bin));
2823 /*double value = h->GetBinContent(bin);
2824 auto bin_pars = f->dataHist().get(bin - 1);
2825 if (f->getAttribute("density")) {
2826 value /= f->dataHist().binVolume(*bin_pars);
2827 }
2828 f->dataHist().set(*bin_pars, value);*/
2829 if (!_isData && h->GetSumw2N() && !SetBinError(bin, h->GetBinError(bin)))
2830 throw std::runtime_error("Failed setting stat error");
2831 }
2832 return *this;
2833 }
2834 } else if (auto _c = dynamic_cast<const RooConstVar *>(&o); _c) {
2835
2836 if (auto a = get<RooAbsArg>();
2837 (a && a->isFundamental()) || get<RooConstVar>() || get<RooStats::HistFactory::FlexibleInterpVar>()) {
2838 SetBinContent(1, _c->getVal());
2839 return *this;
2840 }
2841 }
2842
2843 throw std::runtime_error("Assignment failed");
2844
2845 /*
2846
2847 if (fParent && !fParent->mk()) {
2848 throw std::runtime_error("mk failure");
2849 }
2850
2851 if (fComp) return *this;
2852
2853 if (o.InheritsFrom("RooAbsArg")) {
2854 fComp = acquire(std::shared_ptr<TObject>(const_cast<TObject*>(&o),[](TObject* o){}));
2855 std::dynamic_pointer_cast<RooAbsArg>(fComp)->setStringAttribute("alias",GetName());
2856 }
2857
2858 if (fComp && fParent) {
2859 fParent->incorporate(fComp);
2860 }
2861
2862
2863 return *this;
2864 */
2865}
2866
2867void xRooNode::_fitTo_(const char *datasetName, const char *constParValues)
2868{
2869 try {
2870 auto _pars = pars();
2871 // std::unique_ptr<RooAbsCollection> snap(_pars.argList().snapshot());
2872 TStringToken pattern(constParValues, ",");
2873 while (pattern.NextToken()) {
2874 auto idx = pattern.Index('=');
2875 TString pat = (idx == -1) ? TString(pattern) : TString(pattern(0, idx));
2876 double val =
2877 (idx == -1) ? std::numeric_limits<double>::quiet_NaN() : TString(pattern(idx + 1, pattern.Length())).Atof();
2878 for (auto p : _pars.argList()) {
2879 if (TString(p->GetName()).Contains(TRegexp(pat, true))) {
2880 p->setAttribute("Constant", true);
2881 if (!std::isnan(val)) { dynamic_cast<RooAbsRealLValue *>(p)->setVal(val); }
2882 }
2883 }
2884 }
2885 auto _nll = nll(datasetName);
2886 _nll.fitConfigOptions()->SetValue("LogSize", 65536);
2887 _nll.fitConfig()->MinimizerOptions().SetPrintLevel(0);
2888 auto fr = _nll.minimize();
2889 //_pars.argList() = *snap; // restore values - irrelevant as SetFitResult will restore values
2890 if (!fr.get())
2891 throw std::runtime_error("Fit Failed");
2892 SetFitResult(fr.get());
2893 if (fr->status() != 0)
2894 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Fit Finished",
2895 TString::Format("Fit Status Code = %d", fr->status()), kMBIconExclamation);
2896 else
2897 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Fit Finished",
2898 TString::Format("Fit Status Code = %d", fr->status()));
2899 } catch (const std::exception &e) {
2900 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
2901 kMBIconExclamation); // deletes self on dismiss?
2902 }
2903}
2904
2905void xRooNode::_generate_(const char *datasetName, bool expected)
2906{
2907 try {
2908 datasets().Add(datasetName, expected ? "asimov" : "toy");
2909 } catch (const std::exception &e) {
2910 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
2911 kMBIconExclamation); // deletes self on dismiss?
2912 }
2913}
2914
2915void xRooNode::_SetBinContent_(int bin, double value, const char *par, double parVal)
2916{
2917 try {
2918 SetBinContent(bin, value, strlen(par) > 0 ? par : nullptr, parVal);
2919 } catch (const std::exception &e) {
2920 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
2921 kMBIconExclamation); // deletes self on dismiss?
2922 }
2923}
2924
2926{
2927 try {
2928 if (!SetContents(value))
2929 throw std::runtime_error("Failed to SetContent");
2930 } catch (const std::exception &e) {
2931 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
2932 kMBIconExclamation); // deletes self on dismiss?
2933 }
2934}
2935
2936bool xRooNode::SetBinContent(int bin, double value, const char *par, double parVal)
2937{
2938
2939 // create if needed
2940 if (!get()) {
2941 if (fParent && !find(GetName())) {
2942 // if have a binning we create a histogram to match it
2943 if (auto ax = GetXaxis(); ax) {
2944 std::shared_ptr<TH1D> h;
2945 auto _b = dynamic_cast<Axis2 *>(ax)->binning();
2946 auto t = TH1::AddDirectoryStatus();
2947 TH1::AddDirectory(false);
2948 if (_b->isUniform()) {
2949 h.reset(new TH1D(GetName(), GetTitle(), _b->numBins(), _b->lowBound(), _b->highBound()));
2950 } else {
2951 h.reset(new TH1D(GetName(), GetTitle(), _b->numBins(), _b->array()));
2952 }
2953 h->SetDirectory(0);
2955 h->GetXaxis()->SetName(TString::Format("%s;%s", ax->GetParent()->GetName(), ax->GetName()));
2956 fComp = h;
2957 }
2958 fComp = fParent->Add(*this, "sample").fComp;
2959 }
2960 }
2961
2962 // if it's a RooProduct locate child with the same name
2963 if (get<RooProduct>()) {
2964 return factors()[GetName()]->SetBinContent(bin, value, par, parVal);
2965 }
2966
2967 if (get<RooAbsData>()) {
2968 if (auto _data = get<RooDataSet>(); _data) {
2969 auto _ax = GetXaxis();
2970 if (!_ax) {
2971 throw std::runtime_error("Cannot determine binning to fill data");
2972 }
2973 if (_ax->GetNbins() < bin)
2974 throw std::out_of_range(TString::Format("%s range %s only has %d bins", _ax->GetParent()->GetName(),
2975 _ax->GetName(), _ax->GetNbins()));
2976 RooArgSet obs;
2977
2978 TString cut = "";
2979
2980 for (auto _c : coords()) { // coords() moves vars to their respective coordinates too
2981 if (auto _cat = _c->get<RooAbsCategoryLValue>(); _cat) {
2982 if (cut != "")
2983 cut += " && ";
2984 cut += TString::Format("%s==%d", _cat->GetName(), _cat->getCurrentIndex());
2985 obs.add(*_cat); // note: if we ever changed coords to return clones, would need to keep coords alive
2986 } else {
2987 throw std::runtime_error("SetBinContent of data: Unsupported coordinate type");
2988 }
2989 }
2990
2991 RooFormulaVar cutFormula("cut1", cut, obs); // doing this to avoid complaints about unused vars
2992 RooFormulaVar icutFormula("icut1", TString::Format("!(%s)", cut.Data()), obs);
2993
2994 TString cut2 = TString::Format("%s >= %f && %s < %f", _ax->GetParent()->GetName(), _ax->GetBinLowEdge(bin),
2995 _ax->GetParent()->GetName(), _ax->GetBinUpEdge(bin));
2996 obs.add(*dynamic_cast<RooAbsArg *>(_ax->GetParent()));
2997
2998 RooFormulaVar cutFormula2("cut2", cut + " && " + cut2, obs);
2999 RooFormulaVar icutFormula2("icut2", TString::Format("!(%s && %s)", cut.Data(), cut2.Data()), obs);
3000
3001 // // go up through parents looking for slice obs
3002 // auto _p = fParent;
3003 // while(_p) {
3004 // TString pName(_p->GetName());
3005 // if (auto pos = pName.Index('='); pos != -1) {
3006 // if(auto _obs = _p->getObject<RooAbsLValue>(pName(0,pos)); _obs) {
3007 // if(auto _cat = dynamic_cast<RooAbsCategoryLValue*>(_obs.get()); _cat) {
3008 // _cat->setLabel(pName(pos+1,pName.Length()));
3009 // cut += TString::Format("%s%s==%d", (cut=="")?"":" && ",_cat->GetName(),
3010 // _cat->getCurrentIndex());
3011 // } else if(auto _var = dynamic_cast<RooAbsRealLValue*>(_obs.get()); _var) {
3012 // _var->setVal(TString(pName(pos+1,pName.Length())).Atof());
3013 // // TODO: Cut for this!!
3014 // }
3015 // obs.add(*dynamic_cast<RooAbsArg*>(_obs.get()));
3016 // } else {
3017 // throw std::runtime_error("Unknown observable, could not find");
3018 // }
3019 // }
3020 // _p = _p->fParent;
3021 // }
3022
3023 // add observables to dataset if necessary
3024 RooArgSet l(obs);
3025 l.remove(*_data->get(), true, true);
3026 if (!l.empty()) {
3027 // addColumns method is buggy: https://github.com/root-project/root/issues/8787
3028 // incredibly though, addColumn works??
3029 for (auto &x : l) {
3030 _data->addColumn(*x);
3031 }
3032 // instead create a copy dataset and merge it into current
3033 // cant use merge because it drops weightVar
3034 /*RooDataSet tmp("tmp","tmp",l);
3035 for(int i=0;i<_data->numEntries();i++) tmp.add(l);
3036 _data->merge(&tmp);*/
3037 // delete _data->addColumns(l);
3038 }
3039 // before adding, ensure range is good to cover
3040 for (auto &o : obs) {
3041 if (auto v = dynamic_cast<RooRealVar *>(o); v) {
3042 if (auto dv = dynamic_cast<RooRealVar *>(_data->get()->find(v->GetName())); dv) {
3043 if (v->getMin() < dv->getMin())
3044 dv->setMin(v->getMin());
3045 if (v->getMax() > dv->getMax())
3046 dv->setMax(v->getMax());
3047 }
3048 } else if (auto c = dynamic_cast<RooCategory *>(o); c) {
3049 if (auto dc = dynamic_cast<RooCategory *>(_data->get()->find(c->GetName())); dc) {
3050 if (!dc->hasLabel(c->getCurrentLabel())) {
3051 dc->defineType(c->getCurrentLabel(), c->getCurrentIndex());
3052 }
3053 }
3054 }
3055 }
3056
3057 // using SetBinContent means dataset must take on a binned form at these coordinates
3058 // if number of entries doesnt match number of bins then will 'bin' the data
3059 if (auto _nentries = std::unique_ptr<RooAbsData>(_data->reduce(cutFormula))->numEntries();
3060 _nentries != _ax->GetNbins()) {
3061 auto _contents = GetBinContents(1, _ax->GetNbins());
3062
3063 if (_nentries > 0) {
3064 Info("SetBinContent", "Binning %s in channel: %s", GetName(), cut.Data());
3065 auto _reduced = std::unique_ptr<RooAbsData>(_data->reduce(icutFormula));
3066 _data->reset();
3067 for (int j = 0; j < _reduced->numEntries(); j++) {
3068 auto _obs = _reduced->get(j);
3069 _data->add(*_obs, _reduced->weight());
3070 }
3071 }
3072 for (int i = 1; i <= _ax->GetNbins(); i++) {
3073 // can skip over the bin we will be setting to save a reduce step below
3074 if (i == bin)
3075 continue;
3076 dynamic_cast<RooAbsLValue *>(_ax->GetParent())->setBin(i - 1, _ax->GetName());
3077 _data->add(obs, _contents.at(i - 1));
3078 }
3079 }
3080
3081 // remove existing entries
3082 if (std::unique_ptr<RooAbsData>(_data->reduce(cutFormula2))->numEntries() > 0) {
3083 auto _reduced = std::unique_ptr<RooAbsData>(_data->reduce(icutFormula2));
3084 _data->reset();
3085 for (int j = 0; j < _reduced->numEntries(); j++) {
3086 auto _obs = _reduced->get(j);
3087 _data->add(*_obs, _reduced->weight());
3088 }
3089 }
3090 dynamic_cast<RooAbsLValue *>(_ax->GetParent())->setBin(bin - 1, _ax->GetName());
3091 _data->add(obs, value);
3092 return true;
3093
3094 } else if (get<RooDataHist>()) {
3095 throw std::runtime_error("RooDataHist not supported yet");
3096 }
3097 }
3098
3099 if (auto _varies = variations(); !_varies.empty() || (par && strlen(par))) {
3100 if (!par || strlen(par) == 0) {
3101 return _varies["nominal"]->SetBinContent(bin, value, par, parVal);
3102 } else if (auto it = _varies.find(Form("%s=%g", par, parVal)); it) {
3103 return it->SetBinContent(bin, value);
3104 } else {
3105 // need to create the variation : note - if no variations existed up to now this will inject a new node
3106 // so we should redirect ourself to the new node
3107 // TODO: Do we need to redirect parents?
3108 TString s = Form("%s=%g", par, parVal);
3109 return Vary(s.Data()).SetBinContent(bin, value);
3110 }
3111 }
3112
3113 auto o = get();
3114 if (auto p = dynamic_cast<RooRealVar *>(o); p) {
3115 if (!par || strlen(par) == 0) {
3116 if (p->getMax() < value)
3117 p->setMax(value);
3118 if (p->getMin() > value)
3119 p->setMin(value);
3120 p->setVal(value);
3121 sterilize();
3122 return true;
3123 }
3124
3125 } else if (auto c = dynamic_cast<RooConstVar *>(o); c) {
3126
3127 // if parent is a FlexibleInterpVar, change the value in that .
3128 if (strcmp(c->GetName(), Form("%g", c->getVal())) == 0) {
3129 c->SetNameTitle(Form("%g", value), Form("%g", value));
3130 }
3131#if ROOT_VERSION_CODE < ROOT_VERSION(6, 24, 00)
3132 c->_value = value; // in future ROOT versions there is a changeVal method!
3133#else
3134 c->changeVal(value);
3135#endif
3136
3138 fParent->Vary(*this);
3139 }
3140
3141 sterilize();
3142 return true;
3143 } else if (auto f = dynamic_cast<RooHistFunc *>(o); f) {
3144 auto bin_pars = f->dataHist().get(bin - 1);
3145 if (f->getAttribute("density")) {
3146 value /= f->dataHist().binVolume(*bin_pars);
3147 }
3148 f->dataHist().set(*bin_pars, value);
3149 f->setValueDirty();
3150
3151 if (auto otherfName = f->getStringAttribute("symmetrized_by"); otherfName) {
3152 // broken symmetry, so update flags ...
3153 f->setStringAttribute("symmetrized_by", nullptr);
3154 if (auto x = getObject<RooAbsArg>(otherfName); x) {
3155 x->setStringAttribute("symmetrizes", nullptr);
3156 x->setStringAttribute("symmetrize_nominal", nullptr);
3157 }
3158 } else if (auto otherfName2 = f->getStringAttribute("symmetrizes"); otherfName2) {
3159 auto nomf = getObject<RooHistFunc>(f->getStringAttribute("symmetrize_nominal"));
3160 auto otherf = getObject<RooHistFunc>(otherfName2);
3161 if (nomf && otherf) {
3162 otherf->dataHist().set(*bin_pars, 2 * nomf->dataHist().weight(bin - 1) - value);
3163 otherf->setValueDirty();
3164 }
3165 }
3166 sterilize();
3167 return true;
3168 } else if (auto f2 = dynamic_cast<RooStats::HistFactory::FlexibleInterpVar *>(o); f2) {
3169 // changing nominal value
3170 f2->setNominal(value);
3171 }
3172 throw std::runtime_error(TString::Format("unable to set bin content of %s", GetPath().c_str()));
3173}
3174
3175bool xRooNode::SetBinData(int bin, double value, const char *dataName)
3176{
3177 return datasets()[dataName]->SetBinContent(bin, value);
3178}
3179
3180bool xRooNode::SetBinError(int bin, double value)
3181{
3182
3183 // if it's a RooProduct locate child with the same name
3184 if (get<RooProduct>()) {
3185 return factors()[GetName()]->SetBinError(bin, value);
3186 }
3187
3188 if (auto _varies = variations(); !_varies.empty()) {
3189 return _varies["nominal"]->SetBinError(bin, value);
3190 }
3191
3192 auto o = get();
3193
3194 if (auto f = dynamic_cast<RooHistFunc *>(o); f) {
3195
3196 // if (f->getAttribute("density")) { value /= f->dataHist().binVolume(*bin_pars); } - commented out because DON'T
3197 // convert .. sumw and sumw2 attributes will be stored not as densities
3198
3199 // NOTE: Can only do this because factors() makes parents of its children it's own parent (it isn't the parent)
3200 // If ever make factors etc part of the parentage then this would need tweaking to get to the true parent
3201 // find first parent that is a RooProduct, that is where the statFactor would live
3202 // stop as soon as we reach pdf object
3203 auto _prodParent = fParent;
3204 while (_prodParent && !_prodParent->get<RooProduct>() && !_prodParent->get<RooAbsPdf>()) {
3205 if (_prodParent->get<PiecewiseInterpolation>() && strcmp(GetName(), "nominal")) {
3206 _prodParent.reset();
3207 break; // only the 'nominal' variation can look for a statFactor outside the variation container
3208 }
3209 _prodParent = _prodParent->fParent;
3210 }
3211 auto _f_stat =
3212 (_prodParent && !_prodParent->get<RooAbsPdf>()) ? _prodParent->factors().find("statFactor") : nullptr;
3213 auto f_stat = (_f_stat) ? _f_stat->get<ParamHistFunc>() : nullptr;
3214 if (_f_stat && _f_stat->get() && !f_stat) {
3215 throw std::runtime_error("stat factor must be a paramhistfunc");
3216 }
3217
3218 // stat uncertainty lives in the "statFactor" factor, each sample has its own one,
3219 // but they can share parameters
3220 if (!f_stat) {
3221 if (value == 0)
3222 return true;
3223 TString parNames;
3224 for (auto &p : xRooNode("tmp", *f, std::shared_ptr<xRooNode>(nullptr)).vars()) {
3225 if (parNames != "")
3226 parNames += ",";
3227 parNames += p->get()->GetName();
3228 }
3229 auto h = std::unique_ptr<TH1>(f->dataHist().createHistogram(parNames
3230#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 27, 00)
3231 ,
3233#endif
3234 ));
3235 h->Reset();
3236 h->SetName("statFactor");
3237 h->SetTitle(TString::Format("StatFactor of %s", f->GetTitle()));
3238 h->SetOption("blankshape");
3239
3240 // multiply parent if is nominal
3241 auto toMultiply = this;
3242 if (strcmp(GetName(), "nominal") == 0 && fParent && fParent->get<PiecewiseInterpolation>())
3243 toMultiply = fParent.get();
3244
3245 f_stat = dynamic_cast<ParamHistFunc *>(toMultiply->Multiply(*h).get());
3246 if (!f_stat) {
3247 throw std::runtime_error("Failed creating stat shapeFactor");
3248 }
3249 }
3250
3251 auto phf = f_stat;
3252
3253 TString prefix = f->getStringAttribute("statPrefix");
3254 if (value && prefix == "") {
3255 // find the first parent that can hold components (RooAddPdf, RooRealSumPdf, RooAddition, RooWorkspace) ... use
3256 // that name for the stat factor
3257 auto _p = fParent;
3258 while (_p && !(_p->get()->InheritsFrom("RooRealSumPdf") || _p->get()->InheritsFrom("RooAddPdf") ||
3259 _p->get()->InheritsFrom("RooWorkspace") || _p->get()->InheritsFrom("RooAddition"))) {
3260 _p = _p->fParent;
3261 }
3262 prefix = TString::Format("stat_%s", (_p && _p->get<RooAbsReal>()) ? _p->get()->GetName() : f->GetName());
3263 }
3264 auto newVar = (value == 0) ? getObject<RooRealVar>("1")
3265 : acquire<RooRealVar>(Form("%s_bin%d", prefix.Data(), bin),
3266 Form("%s_bin%d", prefix.Data(), bin), 1);
3267#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3268 RooArgList& pSet = phf->_paramSet;
3269#else
3270 RooArgList& pSet = const_cast<RooArgList&>(phf->paramList());
3271#endif
3272 auto var = dynamic_cast<RooRealVar *>(&pSet[bin - 1]);
3273
3274 if (newVar.get() != var) {
3275 // need to swap out var for newVar
3276 // replace ith element in list with new func, or inject into RooProduct
3277 RooArgList all;
3278 for (int i = 0; i < pSet.getSize(); i++) {
3279 if (i != bin - 1)
3280 all.add(*pSet.at(i));
3281 else {
3282 all.add(*newVar);
3283 }
3284 }
3285 pSet.removeAll();
3286 pSet.add(all);
3287 }
3288
3289 xRooNode v((value == 0) ? *var : *newVar, *this);
3290 auto rrv = dynamic_cast<RooRealVar *>(v.get());
3291 if (strcmp(rrv->GetName(), "1") != 0) {
3292 TString origName = (f->getStringAttribute("origName")) ? f->getStringAttribute("origName") : GetName();
3293 rrv->setStringAttribute(Form("sumw2_%s", origName.Data()), TString::Format("%f", pow(value, 2)));
3294 auto bin_pars = f->dataHist().get(bin - 1);
3295 auto _binContent = f->dataHist().weight();
3296 if (f->getAttribute("density")) {
3297 _binContent *= f->dataHist().binVolume(*bin_pars);
3298 }
3299 rrv->setStringAttribute(Form("sumw_%s", origName.Data()), TString::Format("%f", _binContent));
3300 double sumw2 = 0;
3301 double sumw = 0;
3302 for (auto &[s, sv] : rrv->stringAttributes()) {
3303 if (s.find("sumw_") == 0) {
3304 sumw += TString(sv).Atof();
3305 } else if (s.find("sumw2_") == 0) {
3306 sumw2 += TString(sv).Atof();
3307 }
3308 }
3309 if (sumw2 && sumw2 != std::numeric_limits<double>::infinity()) {
3310 double tau = pow(sumw, 2) / sumw2;
3311 rrv->setError((tau < 1e-15) ? 1e15 : (/*rrv->getVal()*/ 1. / sqrt(tau))); // not sure why was rrv->getVal()?
3312 rrv->setConstant(false);
3313 // parameter must be constrained
3314 auto _constr = v.constraints();
3315 // std::cout << " setting constraint " << v.GetName() << " nomin=" << tau << std::endl;
3316 if (_constr.empty()) {
3317 rrv->setStringAttribute("boundConstraint", _constr.Add("poisson").get()->GetName());
3318 } else {
3319 auto _glob = _constr.at(0)->obs().at(0)->get<RooRealVar>();
3320 // TODO: Update any globs snapshots that are designed to match the nominal
3321 _glob->setStringAttribute("nominal", TString::Format("%f", tau));
3322 double _min = tau * (1. - 5. * sqrt(1. / tau));
3323 double _max = tau * (1. + 5. * sqrt(1. / tau));
3324 _glob->setRange(_min, _max);
3325 _glob->setVal(tau);
3326 _constr.at(0)->args().at(0)->SetBinContent(0, tau);
3327 rrv->setStringAttribute("boundConstraint", _constr.at(0)->get()->GetName());
3328 }
3329 rrv->setRange(std::max((1. - 5. * sqrt(1. / tau)), 1e-15), 1. + 5. * sqrt(1. / tau));
3330 } else {
3331 // remove constraint
3332 if (auto _constr = v.constraints(); !_constr.empty()) {
3333 v.constraints().Remove(*_constr.at(0));
3334 }
3335 // set const if sumw2 is 0 (i.e. no error)
3336 rrv->setVal(1);
3337 rrv->setError(0);
3338 rrv->setConstant(sumw2 == 0);
3339 }
3340 }
3341
3342 return true;
3343 }
3344
3345 throw std::runtime_error(TString::Format("%s SetBinError failed", GetName()));
3346}
3347
3348std::shared_ptr<xRooNode> xRooNode::find(const std::string &name) const
3349{
3350 try {
3351 return at(name);
3352 } catch (std::out_of_range &) {
3353 return nullptr;
3354 }
3355}
3356
3358{
3359 if (auto _w = get<RooWorkspace>(); _w)
3360 return _w;
3361 if (auto a = get<RooAbsArg>(); a && GETWS(a)) {
3362 return GETWS(a);
3363 }
3364 if (fParent)
3365 return fParent->ws();
3366 return nullptr;
3367}
3368
3370{
3371
3372 xRooNode out(".constraints", nullptr, *this);
3373
3374 std::function<RooAbsPdf *(const xRooNode &n, RooAbsArg &par, RooAbsPdf *ignore)> getConstraint;
3375 getConstraint = [&](const xRooNode &n, RooAbsArg &par, RooAbsPdf *ignore) {
3376 // std::cout << "Getting constraint of "<< n.GetName() << std::endl;
3377 auto o = n.get<RooProdPdf>();
3378 if (!o) {
3379 if (n.get<RooSimultaneous>() || (n.get<RooAbsPdf>() && n.fParent && n.fParent->get<RooWorkspace>())) {
3380 // if at top-level or is a simultaneous, check all channels for a constraint
3381 for (auto &c : n.bins()) {
3382 if (auto oo = getConstraint(*c.get(), par, nullptr); oo) {
3383 return oo;
3384 }
3385 }
3386 return (RooAbsPdf *)nullptr;
3387 } else if (auto _ws = n.get<RooWorkspace>(); _ws) {
3388 // reached a workspace, check for any pdf depending on parameter that isnt the ignore
3389 for (auto p : _ws->allPdfs()) {
3390 if (p == ignore)
3391 continue;
3392 if (p->dependsOn(par)) {
3393 out.emplace_back(std::make_shared<xRooNode>(par.GetName(), *p, *this));
3394 }
3395 }
3396 }
3397 if (!n.fParent)
3398 return (RooAbsPdf *)nullptr;
3399 return getConstraint(*n.fParent.get(), par, n.get<RooAbsPdf>());
3400 }
3401 for (auto p : o->pdfList()) {
3402 if (p == ignore)
3403 continue;
3404 if (p->dependsOn(par)) {
3405 out.emplace_back(std::make_shared<xRooNode>(par.GetName(), *p, *this));
3406 }
3407 }
3408 return (RooAbsPdf *)nullptr;
3409 };
3410
3411 for (auto &p : vars()) {
3412 auto v = dynamic_cast<RooAbsReal *>(p->get());
3413 if (!v)
3414 continue;
3415 if (v->getAttribute("Constant"))
3416 continue; // skip constants ?
3417 if (v->getAttribute("obs"))
3418 continue; // skip observables ... constraints constrain pars not obs
3419 getConstraint(*this, *v, get<RooAbsPdf>());
3420 /*if (auto c = ; c) {
3421 out.emplace_back(std::make_shared<Node2>(p->GetName(), *c, *this));
3422 }*/
3423 }
3424
3425 // finish by removing any constraint that contains another constraint for the same par
3426 // and consolidate common pars
3427 auto it = out.begin();
3428 while (it != out.end()) {
3429 bool removeIt = false;
3430 for (auto &c : out) {
3431 if (c.get() == it->get())
3432 continue;
3433 if ((*it)->get<RooAbsArg>()->dependsOn(*c->get<RooAbsArg>())) {
3434 removeIt = true;
3435 std::set<std::string> parNames;
3436 std::string _cName = c->GetName();
3437 do {
3438 parNames.insert(_cName.substr(0, _cName.find(';')));
3439 _cName = _cName.substr(_cName.find(';') + 1);
3440 } while (_cName.find(';') != std::string::npos);
3441 parNames.insert(_cName);
3442 _cName = it->get()->GetName();
3443 do {
3444 parNames.insert(_cName.substr(0, _cName.find(';')));
3445 _cName = _cName.substr(_cName.find(';') + 1);
3446 } while (_cName.find(';') != std::string::npos);
3447 parNames.insert(_cName);
3448 _cName = "";
3449 for (auto &x : parNames) {
3450 if (!_cName.empty())
3451 _cName += ";";
3452 _cName += x;
3453 }
3454 c->TNamed::SetName(_cName.c_str());
3455 break;
3456 }
3457 }
3458 if (removeIt)
3459 it = out.erase(it);
3460 else
3461 ++it;
3462 }
3463
3464 // if getting constraints of a fundamental then use the constraint names instead of the par name (because would be
3465 // all same otherwise)
3466 if (get<RooAbsArg>() && get<RooAbsArg>()->isFundamental()) {
3467 for (auto &o : out) {
3468 o->TNamed::SetName(o->get()->GetName());
3469 }
3470 }
3471
3472 return out;
3473}
3474
3475std::shared_ptr<TObject> xRooNode::convertForAcquisition(xRooNode &acquirer, const char *opt) const
3476{
3477
3478 TString sOpt(opt);
3479 sOpt.ToLower();
3480 TString sName(GetName());
3481 if (sOpt == "func")
3482 sName = TString("factory:") + sName;
3483
3484 // if arg is a histogram, will acquire it as a RooHistFunc unless no conversion
3485 // todo: could flag not to convert
3486 if (auto h = get<TH1>(); h) {
3487 TString sOpt2(h->GetOption());
3488 std::map<std::string, std::string> stringAttrs;
3489 while (sOpt2.Contains("=")) {
3490 auto pos = sOpt2.Index("=");
3491 auto start = sOpt2.Index(";") + 1;
3492 if (start > pos)
3493 start = 0;
3494 auto end = sOpt2.Index(";", pos);
3495 if (end == -1)
3496 end = sOpt2.Length();
3497 stringAttrs[sOpt2(start, pos - start)] = sOpt2(pos + 1, end - pos - 1);
3498 sOpt2 = TString(sOpt2(0, start)) + TString(sOpt2(end + 1, sOpt2.Length()));
3499 }
3500 TString newObjName = GetName();
3501 TString origName = GetName();
3502 if (origName.BeginsWith(';'))
3503 origName = origName(1, origName.Length());
3504 if (newObjName.BeginsWith(';'))
3505 newObjName =
3506 newObjName(1, newObjName.Length()); // special case if starts with ';' then don't create a fancy name
3507 else if (acquirer.get() && !acquirer.get<RooWorkspace>())
3508 newObjName = TString::Format(
3509 "%s_%s", (acquirer.mainChild().get()) ? acquirer.mainChild()->GetName() : acquirer->GetName(),
3510 newObjName.Data());
3511 // can convert to a RooHistFunc, or RooParamHist if option contains 'shape'
3512 TString varName = h->GetXaxis()->GetName();
3513 std::string binningName = newObjName.Data();
3514 if (auto pos = varName.Index(';'); pos != -1) {
3515 binningName = varName(pos + 1, varName.Length());
3516 varName = varName(0, pos);
3517 }
3518
3519 if (varName == "xaxis" &&
3520 !acquirer.get<RooSimultaneous>()) { // default case, try to take axis var and binning from the acquirer
3521 if (auto ax = acquirer.GetXaxis(); ax) {
3522 varName = ax->GetParent()->GetName();
3523 // TODO: check the binning is consistent before using - at least will check nBins below
3524 binningName = ax->GetName();
3525 } else if (acquirer.obs().size() == 1)
3526 varName = acquirer.obs().at(0)->get()->GetName(); // TODO what if no obs but Xaxis var is defined?
3527 }
3528 auto x = acquirer.acquire<RooRealVar>(varName, h->GetXaxis()->GetTitle(), h->GetXaxis()->GetXmin(),
3529 h->GetXaxis()->GetXmax());
3530 if (x->getMin() > h->GetXaxis()->GetXmin())
3531 x->setMin(h->GetXaxis()->GetXmin());
3532 if (x->getMax() < h->GetXaxis()->GetXmax())
3533 x->setMax(h->GetXaxis()->GetXmax());
3534 if (!x->hasBinning(binningName.c_str())) {
3535 if (h->GetXaxis()->IsVariableBinSize()) {
3536 x->setBinning(RooBinning(h->GetNbinsX(), h->GetXaxis()->GetXbins()->GetArray()), binningName.c_str());
3537 } else {
3538 x->setBinning(
3539 RooUniformBinning(h->GetXaxis()->GetXmin(), h->GetXaxis()->GetXmax(), h->GetXaxis()->GetNbins()),
3540 binningName.c_str());
3541 }
3542 x->getBinning(binningName.c_str()).SetTitle(h->GetXaxis()->GetTitle());
3543 if (x->getBinningNames().size() == 2) {
3544 // this was the first binning, so copy it over to be the default binning too
3545 x->setBinning(x->getBinning(binningName.c_str()));
3546 }
3547 } else {
3548 // TODO check binning is compatible with histogram
3549 if (x->getBinning(binningName.c_str()).numBins() != h->GetNbinsX()) {
3550 throw std::runtime_error(
3551 TString::Format("binning mismatch for binning %s of %s", binningName.c_str(), x->GetName()));
3552 }
3553 }
3554
3555 std::shared_ptr<RooAbsArg> _f;
3556
3557 // if acquirer is a RooSimultaneous, will use histogram to define a channel
3558 if (acquirer.get<RooSimultaneous>()) {
3559 _f = acquirer.acquireNew<RooProdPdf>(newObjName, (strlen(h->GetTitle())) ? h->GetTitle() : h->GetName(),
3560 RooArgList());
3561 for (auto &[k, v] : stringAttrs) {
3562 _f->setStringAttribute(k.c_str(), v.c_str());
3563 }
3564 x->setAttribute("obs", true);
3565 } else if (sOpt2.Contains("shape")) {
3566 RooArgList list;
3567 for (int i = 0; i < x->getBinning(binningName.c_str()).numBins(); i++) {
3568 std::shared_ptr<RooRealVar> arg;
3569 if (sOpt2.Contains("blankshape")) {
3570 arg = acquirer.acquire<RooRealVar>("1", "1", 1);
3571 } else {
3572 if (!h) {
3573 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1), "", 1);
3574 }
3575 if (h->GetMinimumStored() != -1111 || h->GetMaximumStored() != -1111) {
3576 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1), "",
3577 h->GetBinContent(i + 1), h->GetMinimumStored(),
3578 h->GetMaximumStored());
3579 } else {
3580 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1), "",
3581 h->GetBinContent(i + 1));
3582 }
3583 }
3584 list.add(*arg);
3585 }
3586 // paramhistfunc requires the binnings to be loaded as default at construction time
3587 // so load binning temporarily
3588 auto tmp = dynamic_cast<RooAbsBinning *>(x->getBinningPtr(0)->Clone());
3589 x->setBinning(x->getBinning(binningName.c_str()));
3590 _f = acquirer.acquireNew<ParamHistFunc>(newObjName, h->GetTitle(), *x, list);
3591#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3592 dynamic_cast<ParamHistFunc *>(_f.get())->_paramSet.setName("paramSet"); // so can see when print
3593#else
3594 const_cast<RooArgList&>(dynamic_cast<ParamHistFunc *>(_f.get())->paramList()).setName("paramSet"); // so can see when print
3595#endif
3596 x->setBinning(*tmp); // restore binning
3597 delete tmp;
3598 for (auto &[k, v] : stringAttrs) {
3599 _f->setStringAttribute(k.c_str(), v.c_str());
3600 }
3601 } else {
3602 auto dh = acquirer.acquireNew<RooDataHist>(Form("hist_%s", newObjName.Data()), h->GetTitle(), *x,
3603 binningName.c_str() /* binning name*/);
3604 if (!dh) {
3605 throw std::runtime_error("Couldn't make data hist");
3606 }
3607 auto f = acquirer.acquireNew<RooHistFunc>(newObjName, h->GetTitle(), *x, *dh,
3608 0 /*interpolation order between bins*/);
3609 f->forceNumInt();
3610 f->setAttribute("autodensity"); // where it gets inserted will determine if this is a density or not
3611 _f = f;
3612
3613 for (auto &[k, v] : stringAttrs) {
3614 _f->setStringAttribute(k.c_str(), v.c_str());
3615 }
3616
3617 // need to do these settings here because used in the assignment step
3618 _f->setStringAttribute("xvar", x->GetName());
3619 _f->setStringAttribute("binning", binningName.c_str());
3620 if (strcmp(_f->GetName(), origName.Data()) && !_f->getStringAttribute("alias"))
3621 _f->setStringAttribute("alias", origName);
3622
3623 // copy values over using the assignment operator - may convert to a RooProduct if there are stat uncerts
3624 xRooNode tmp(h->GetName(), _f, acquirer);
3625 tmp = *h;
3626 _f = std::dynamic_pointer_cast<RooAbsArg>(tmp.fComp); // in case got upgrade to a RooProduct
3627 }
3628
3629 _f->setStringAttribute("xvar", x->GetName());
3630 _f->setStringAttribute("binning", binningName.c_str());
3631 // style(h); // will transfer styling to object if necessary - not doing because this method used with plane hists
3632 // frequently
3633 if (strcmp(_f->GetName(), origName.Data()) && !_f->getStringAttribute("alias"))
3634 _f->setStringAttribute("alias", origName);
3635
3636 fComp = _f;
3637 return _f;
3638 } else if (!get() && sName.BeginsWith("factory:") && acquirer.ws()) {
3639 TString s(sName);
3640 s = TString(s(8, s.Length()));
3641 fComp.reset(acquirer.ws()->factory(s), [](TObject *) {});
3642 return fComp;
3643 }
3644
3645 return fComp;
3646}
3647
3648std::shared_ptr<TStyle> xRooNode::style(TObject *initObject) const
3649{
3650
3651 auto arg = get<RooAbsArg>();
3652 if (!initObject && !arg) {
3653 return nullptr;
3654 }
3655
3656 TString t = GetTitle();
3657 if (initObject) {
3658 t = (strlen(initObject->GetTitle())) ? initObject->GetTitle() : initObject->GetName();
3659 } else if (arg) {
3660 if (arg->getStringAttribute("style"))
3661 t = arg->getStringAttribute("style");
3662 else
3663 return nullptr;
3664 }
3665
3666 std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case getObject has decided to
3667 // return the owning ptr (for some reason)
3668 if (!gROOT->GetStyle(t)) {
3669 if ((style = getObject<TStyle>(t.Data()))) {
3670 // loaded style (from workspace?) so put in list and use that
3671 gROOT->GetListOfStyles()->Add(style.get());
3672 } else {
3673 // create new style - gets put in style list automatically so don't have to delete
3674 // acquire them so saved to workspaces for auto reload ...
3675 style = const_cast<xRooNode &>(*this).acquireNew<TStyle>(t.Data(),
3676 TString::Format("Style for %s component", t.Data()));
3677 if (auto x = dynamic_cast<TAttLine *>(initObject))
3678 ((TAttLine &)*style) = *x;
3679 if (auto x = dynamic_cast<TAttFill *>(initObject))
3680 ((TAttFill &)*style) = *x;
3681 if (auto x = dynamic_cast<TAttMarker *>(initObject))
3682 ((TAttMarker &)*style) = *x;
3683 gROOT->GetListOfStyles()->Add(style.get());
3684 }
3685 } else {
3686 style = std::shared_ptr<TStyle>(gROOT->GetStyle(t), [](TStyle *) {});
3687 }
3688
3689 if (arg && !arg->getStringAttribute("style")) {
3690 arg->setStringAttribute("style", style->GetName());
3691 }
3692
3693 return style;
3694}
3695
3696std::shared_ptr<TObject> xRooNode::acquire(const std::shared_ptr<TObject> &arg, bool checkFactory, bool mustBeNew)
3697{
3698 if (!arg)
3699 return nullptr;
3700 if (!fAcquirer && !get<RooWorkspace>() && fParent)
3701 return fParent->acquire(arg, checkFactory, mustBeNew);
3702
3703 // if has a workspace and our object is the workspace or is in the workspace then add this object to workspace
3704 auto _ws = (fAcquirer) ? nullptr : ws();
3705 if (_ws && (get() == _ws || _ws->arg(GetName()) || (arg && strcmp(arg->GetName(), GetName()) == 0))) {
3708 if (auto a = dynamic_cast<RooAbsArg *>(arg.get()); a) {
3709 auto out_arg = _ws->arg(a->GetName());
3710 TString aName = arg->GetName();
3711 int ii = 1;
3712 while (out_arg && mustBeNew) {
3713 a->SetName(TString::Format("%s_%d", aName.Data(), ii++));
3714 out_arg = _ws->arg(a->GetName());
3715 }
3716 if (aName != a->GetName())
3717 Warning("acquire", "Renaming to %s", a->GetName());
3718 if (!out_arg) {
3719 bool done = false;
3720 if (checkFactory) {
3721 if (auto res = _ws->factory(arg->GetName()); res) {
3722 a = res;
3723 done = true;
3724 }
3725 }
3726 if (!done && _ws->import(*a, RooFit::RecycleConflictNodes())) {
3727 if (GETWS(a) != _ws) {
3728 Info("acquire", "A copy of %s has been added to workspace %s", a->GetName(), _ws->GetName());
3729 }
3731 return nullptr;
3732 }
3733 // sanitizeWS(); // clears the caches that might exist up to now, as well interfere with getParameters calls
3734 std::set<std::string> setNames;
3735 for (auto &aa : GETWSSETS(_ws)) {
3736 if (TString(aa.first.c_str()).BeginsWith("CACHE_")) {
3737 setNames.insert(aa.first);
3738 }
3739 }
3740 for (auto &aa : setNames)
3741 ws()->removeSet(aa.c_str());
3742
3743 out_arg = _ws->arg(a->GetName());
3744 }
3746 return std::shared_ptr<TObject>(out_arg, [](TObject *) {});
3747 } else if (auto a2 = dynamic_cast<RooAbsData *>(arg.get()); a2) {
3748 if (_ws->import(*a2, RooFit::Embedded())) {
3750 return nullptr;
3751 }
3753 return std::shared_ptr<TObject>(_ws->embeddedData(arg->GetName()), [](TObject *) {});
3754 } else if (arg->InheritsFrom("RooFitResult") || arg->InheritsFrom("TTree") || arg->IsA() == TStyle::Class()) {
3755 if (_ws->import(*arg.get(), true /*replace existing*/)) {
3757 return nullptr;
3758 }
3760 /* this doesnt work because caller has its own version of fParent, not the one in the browser
3761 for(auto o : *gROOT->GetListOfBrowsers()) {
3762 if(auto b = dynamic_cast<TBrowser*>(o); b){
3763 if(auto _b = dynamic_cast<TGFileBrowser*>( dynamic_cast<TRootBrowser*>(b->GetBrowserImp())->fActBrowser
3764 ); _b) { if (auto item = _b->fListTree->FindItemByObj(_b->fRootDir,this); item) { auto _tmp = _b->fListLevel;
3765 _b->fListLevel = item;
3766 bool _tmp2 = item->IsOpen();
3767 item->SetOpen(false);
3768 this->Browse(b);
3769 item->SetOpen(_tmp2);
3770 _b->fListLevel = _tmp;
3771 }
3772 }
3773 }
3774 }*/
3775 return std::shared_ptr<TObject>(_ws->genobj(arg->GetName()), [](TObject *) {});
3776 }
3778 // Warning("acquire","Not implemented acquisition of object %s",arg->GetName());
3779 // return nullptr;
3780 }
3781 if (fProvider) {
3782 auto out = fProvider->getObject(arg->GetName(), arg->ClassName());
3783 if (out)
3784 return out;
3785 }
3786 auto _owned = find(".memory");
3787 if (!_owned) {
3788 _owned = emplace_back(std::make_shared<xRooNode>(".memory", nullptr, *this));
3789 }
3790 // look for exact name, dont use 'find' because doesnt work if trying to find "1" and it doesn't exist, will get back
3791 // idx 1 instead
3792 for (auto &r : *_owned) {
3793 if (strcmp(r->GetName(), arg->GetName()) == 0 && strcmp(r->get()->ClassName(), arg->ClassName()) == 0) {
3794 return r->fComp;
3795 }
3796 }
3797 if (!fProvider)
3798 std::cout << GetName() << " taking over " << arg->ClassName() << "::" << arg->GetName() << std::endl;
3799 /*emplace_back(std::make_shared<Node2>(".memory",nullptr,*this))*/
3800 return _owned->emplace_back(std::make_shared<xRooNode>(arg->GetName(), arg, *this))->fComp;
3801 // return arg;
3802}
3803
3804bool xRooNode::SetXaxis(const char *name, const char *title, int nbins, double low, double high)
3805{
3806 RooUniformBinning b(low, high, nbins, name);
3807 b.SetTitle(title);
3808 return SetXaxis(b);
3809}
3810
3811bool xRooNode::SetXaxis(const char *name, const char *title, int nbins, double *bins)
3812{
3813 RooBinning b(nbins, bins, name);
3814 b.SetTitle(title);
3815 return SetXaxis(b);
3816}
3817
3819{
3820
3821 auto name = binning.GetName();
3822 double high = binning.highBound();
3823 double low = binning.lowBound();
3824 // int nbins = binning.numBins();
3825 auto title = binning.GetTitle();
3826
3827 // if have any dependents and name isn't one of them then stop
3828 auto _deps = vars();
3829 /*if(!_deps.empty() && !_deps.find(name)) {
3830 throw std::runtime_error(TString::Format("%s Does not depend on %s",GetName(),name));
3831 }*/
3832
3833 // object will need to exist
3834 if (!get()) {
3835 if (fParent && !find(GetName())) {
3836 fComp = fParent->Add(*this, "+").fComp;
3837 }
3838 }
3839
3840 auto a = get<RooAbsArg>();
3841 if (!a)
3842 throw std::runtime_error("Cannot SetXaxis of non-arg");
3843
3844 auto _x = acquire<RooRealVar>(name, title, low, high);
3845 _x->setBinning(binning, a->GetName());
3846 _x->getBinning(a->GetName()).SetTitle(title);
3847 if (_x->getBinningNames().size() == 2) {
3848 // this was the first binning, so copy it over to be the default binning too
3849 _x->setBinning(_x->getBinning(a->GetName()));
3850 } else {
3851 // ensure the default binning is wide enough to cover this range
3852 // the alternative to this would be to ensure setNormRange of all pdfs
3853 // are set to correct range (then default can be narrower than some of the named binnings)
3854 if (_x->getMax() < high)
3855 _x->setMax(high);
3856 if (_x->getMin() > low)
3857 _x->setMin(low);
3858 }
3859
3860 if (!_deps.find(name) && get<RooAbsPdf>()) {
3861 // creating a variable for a pdf we will assume it should be an observable
3862 _x->setAttribute("obs");
3863 }
3864
3865 a->setStringAttribute("xvar", _x->GetName());
3866 a->setStringAttribute("binning", a->GetName());
3867 fXAxis.reset(); // remove any existing xaxis
3868
3869 return true;
3870}
3871
3872bool xRooNode::contains(const std::string &name) const
3873{
3874 try {
3875 return at(name, false) != nullptr;
3876 } catch (std::out_of_range &) {
3877 return false;
3878 }
3879}
3880
3881std::shared_ptr<xRooNode> xRooNode::at(const std::string &name, bool browseResult) const
3882{
3883 std::string partname = (name.find('/') != std::string::npos) ? name.substr(0, name.find('/')) : name;
3884 auto _s = (!get() && fParent) ? fParent->get<RooSimultaneous>()
3885 : get<RooSimultaneous>(); // makes work if doing simPdf.bins()["blah"]
3886 std::string extra = (_s) ? _s->indexCat().GetName() : "";
3887 for (auto &child : *this) {
3888 if (auto _obj = child->get(); name == child->GetName() || partname == child->GetName() ||
3889 (_obj && name == _obj->GetName()) || (_obj && partname == _obj->GetName()) ||
3890 (!extra.empty() && ((extra + "=" + name) == child->GetName() ||
3891 (extra + "=" + partname) == child->GetName()))) {
3892 if (browseResult)
3893 child->browse(); // needed so can go at()->at()->at()...
3894 if (partname != name && name != child->GetName()) {
3895 return child->at(name.substr(partname.length() + 1));
3896 }
3897 return child;
3898 }
3899 if (auto x = mainChild(); x && strcmp(child->GetName(), x.GetName()) == 0) {
3900 // can browse directly into main children as if their children were our children
3901 for (auto &child2 : x.browse()) {
3902 if (auto _obj = child2->get(); name == child2->GetName() || partname == child2->GetName() ||
3903 (_obj && name == _obj->GetName()) || (_obj && partname == _obj->GetName())) {
3904 if (browseResult)
3905 child2->browse(); // needed for onward read (or is it? there's a browse above too??)
3906 if (partname != name && name != child2->GetName()) {
3907 return child2->at(name.substr(partname.length() + 1));
3908 }
3909 return child2;
3910 }
3911 }
3912 }
3913 }
3914 // before giving up see if partName is numeric and indexes within the range
3915 if (TString s(partname); s.IsDec() && size_t(s.Atoi()) < size()) {
3916 auto child2 = at(s.Atoi());
3917 if (partname != name) {
3918 return child2->at(name.substr(partname.length() + 1));
3919 }
3920 return child2;
3921 }
3922 throw std::out_of_range(name + " does not exist");
3923}
3924
3925std::shared_ptr<xRooNode> xRooNode::operator[](const std::string &name)
3926{
3927 std::string partname = (name.find('/') != std::string::npos) ? name.substr(0, name.find('/')) : name;
3928 browse();
3929 auto _s = (!get() && fParent) ? fParent->get<RooSimultaneous>()
3930 : get<RooSimultaneous>(); // makes work if doing simPdf.bins()["blah"]
3931 std::string extra = (_s) ? _s->indexCat().GetName() : "";
3932 for (auto &child : *this) {
3933 if (name == child->GetName() || partname == child->GetName() ||
3934 (!extra.empty() &&
3935 ((extra + "=" + name) == child->GetName() || (extra + "=" + partname) == child->GetName()))) {
3936 child->browse(); // needed for onward read (or is it? there's a browse above too??)
3937 if (partname != name && name != child->GetName()) {
3938 return child->operator[](name.substr(partname.length() + 1));
3939 }
3940 return child;
3941 }
3942 if (auto x = mainChild(); strcmp(child->GetName(), x.GetName()) == 0) {
3943 // can browse directly into main children as if their children were our children
3944 for (auto &child2 : x.browse()) {
3945 if (name == child2->GetName() || partname == child2->GetName()) {
3946 child2->browse(); // needed for onward read (or is it? there's a browse above too??)
3947 if (partname != name && name != child2->GetName()) {
3948 return child2->operator[](name.substr(partname.length() + 1));
3949 }
3950 return child2;
3951 }
3952 }
3953 }
3954 }
3955 auto out = std::make_shared<xRooNode>(partname.c_str(), nullptr, *this); // not adding as child yeeet
3956 if (partname != name) {
3957 return out->operator[](name.substr(partname.length() + 1));
3958 }
3959 return out;
3960}
3961
3963{
3964 if (!b) {
3965 for (auto o : *gROOT->GetListOfBrowsers()) {
3966 b = dynamic_cast<TBrowser *>(o);
3967 if (!b || !b->GetBrowserImp())
3968 continue;
3969 if (auto out = GetTreeItem(b); out)
3970 return out;
3971 }
3972 return nullptr;
3973 }
3974 if (!b->GetBrowserImp())
3975 return nullptr;
3976 if (auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp()))); _b) {
3977 auto _root = GETROOTDIR(_b);;
3978 if (!_root)
3979 _root = GETLISTTREE(_b)->GetFirstItem();
3981 return GETLISTTREE(_b)->FindItemByObj(_root, const_cast<xRooNode *>(this));
3982 }
3983 return nullptr;
3984}
3985
3987{
3988 if (!b) {
3989 for (auto o : *gROOT->GetListOfBrowsers()) {
3990 b = dynamic_cast<TBrowser *>(o);
3991 if (!b || !b->GetBrowserImp())
3992 continue;
3993 if (auto out = GetListTree(b); out)
3994 return out;
3995 }
3996 return nullptr;
3997 }
3998 if (b->GetBrowserImp()) {
3999 if (auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp()))); _b) {
4000 auto _root = GETROOTDIR(_b);
4001 if (!_root)
4002 _root = GETLISTTREE(_b)->GetFirstItem();
4003 if (auto item = GETLISTTREE(_b)->FindItemByObj(_root, const_cast<xRooNode *>(this)); item) {
4004 return GETLISTTREE(_b);
4005 }
4006 }
4007 }
4008 return nullptr;
4009}
4010
4011void xRooNode::SetName(const char *name)
4012{
4014 if (auto a = get<RooAbsArg>(); a)
4015 a->setStringAttribute("alias", name);
4016 for (auto o : *gROOT->GetListOfBrowsers()) {
4017 if (auto b = dynamic_cast<TBrowser *>(o); b) {
4018 if (auto item = GetTreeItem(b); item) {
4019 item->SetText(name);
4020 }
4021 }
4022 }
4023}
4024
4026{
4027 if (get<RooArgList>() || (!get() && !(strlen(GetName()) > 0 && (GetName()[0] == '!')) && !fBrowseOperation))
4028 return *this; // nothing to browse - 'collection' nodes should already be populated except for folders
4029 // alternative could have been to mandate that the 'components' of a collection node are the children it has.
4030
4031 auto findByObj = [&](const std::shared_ptr<xRooNode> &n) {
4032 for (auto &c : *this) {
4033 if (c->get() == n->get() && strcmp(n->GetName(), c->GetName()) == 0)
4034 return c;
4035 }
4036 return std::shared_ptr<xRooNode>(nullptr);
4037 };
4038
4039 auto appendChildren = [&](const xRooNode &n) {
4040 size_t out = 0;
4041 for (auto &c : n) {
4042 if (auto existing = findByObj(c); existing) {
4043 existing->fTimes++;
4044 existing->fFolder = c->fFolder; // transfer folder assignment
4045 } else {
4046 emplace_back(c);
4047 }
4048 if (TString(c->GetName()) != ".coef")
4049 out++; // don't count .coef as a child, as technically part of parent
4050 }
4051 return out;
4052 };
4053
4054 for (auto &c : *this) {
4055 if (strlen(c->GetName()) > 0 && (c->GetName()[0] == '.')) {
4056 c->fTimes = 1;
4057 continue;
4058 } // never auto-cleanup property children
4059 if (strcmp(c->GetName(), "!.pars") == 0) {
4060 c->fTimes = 1;
4061 continue;
4062 } // special collection, also not cleaned up
4063 if (c->get<RooWorkspace>() || c->get<TFile>()) {
4064 c->fTimes = 1;
4065 continue;
4066 } // workspaces and files not cleaned up: TODO have a nocleanup flag instead
4067 c->fTimes = 0;
4068 }
4069
4070 size_t addedChildren = 0;
4071 if (fBrowseOperation) {
4072 addedChildren += appendChildren(fBrowseOperation(this));
4073 } else {
4074 if (get<RooWorkspace>()) {
4075 addedChildren += appendChildren(datasets());
4076 }
4077
4078 // if (get<RooAbsPdf>() && ((fParent && fParent->get<RooWorkspace>()) || !fParent)) {
4079 // // top-level pdfs will also list the ".vars" property for -- should make this updateable
4080 // //if (auto x = find("!.vars"); !x) { // this is slower because it triggers a browse of !.vars
4081 // if(!contains("!.vars")) {
4082 // emplace_back(std::make_shared<Node2>("!.vars",nullptr,*this));
4083 // } /*else {
4084 // x->fTimes++;
4085 // }*/
4086 // }
4087
4088 // go through components factors and variations, adding all as children if required
4089 addedChildren += appendChildren(components());
4090 if (!get<RooWorkspace>())
4091 addedChildren += appendChildren(factors());
4092 addedChildren += appendChildren(variations());
4093 if (get<ParamHistFunc>() || get<RooSimultaneous>())
4094 addedChildren += appendChildren(bins());
4095 if (get<RooAbsData>())
4096 addedChildren += appendChildren(obs());
4097 }
4098 // if has no children and is a RooAbsArg, add all the proxies
4099 if (auto arg = get<RooAbsArg>(); arg && addedChildren == 0) {
4100 for (int i = 0; i < arg->numProxies(); i++) {
4101 auto _proxy = arg->getProxy(i);
4102 if (auto a = dynamic_cast<RooArgProxy *>(_proxy)) {
4103 auto c = std::make_shared<xRooNode>(TString::Format(".%s", _proxy->name()), *(a->absArg()), *this);
4104 if (auto existing = findByObj(c); existing) {
4105 existing->fTimes++;
4106 existing->fFolder = c->fFolder; // transfer folder assignment
4107 } else {
4108 emplace_back(c);
4109 }
4110 } else if (auto s = dynamic_cast<RooAbsCollection *>(_proxy)) {
4111 for (auto a2 : *s) {
4112 auto c = std::make_shared<xRooNode>(*a2, *this);
4113 c->fFolder = std::string("!.") + _proxy->name();
4114 if (auto existing = findByObj(c); existing) {
4115 existing->fTimes++;
4116 existing->fFolder = c->fFolder; // transfer folder assignment
4117 } else {
4118 emplace_back(c);
4119 }
4120 }
4121 }
4122 }
4123 /*for(auto& s : arg->servers()) {
4124 auto c = std::make_shared<xRooNode>(*s,*this);
4125 if (auto existing = findByObj(c); existing) {
4126 existing->fTimes++;
4127 existing->fFolder = c->fFolder; // transfer folder assignment
4128 } else {
4129 emplace_back(c);
4130 }
4131 }*/
4132 }
4133
4134 // clear anything that has fTimes = 0 still
4135 auto it = begin();
4136 while (it != end()) {
4137 if (it->get()->fTimes == 0) {
4138 for (auto o : *gROOT->GetListOfBrowsers()) {
4139 auto b = dynamic_cast<TBrowser *>(o);
4140 if (b && b->GetBrowserImp()) { // browserImp is null if browser was closed
4141 // std::cout << GetPath() << " Removing " << it->get()->GetPath() << std::endl;
4142
4143 if (auto _b =
4144 dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp())));
4145 _b) {
4146 auto _root = GETROOTDIR(_b);
4147 if (!_root)
4148 _root = GETLISTTREE(_b)->GetFirstItem();
4149 if (auto item = GETLISTTREE(_b)->FindItemByObj(_root, this); item) {
4150 GETLISTTREE(_b)->OpenItem(item);
4151 }
4152 }
4153
4154 b->RecursiveRemove(
4155 it->get()); // problem: if obj is living in a collapsed node it wont actually get deleted
4156 /*auto _b = dynamic_cast<TGFileBrowser*>( dynamic_cast<TRootBrowser*>(b->GetBrowserImp())->fActBrowser );
4157 if (_b) {
4158 std::cout << _b->fRootDir->GetText() << std::endl;
4159 if (auto item = _b->fListTree->FindItemByObj(_b->fRootDir,it->get()); item) {
4160 std::cout << "Found obj: " << item << " " << item->GetText() << std::endl;
4161 _b->fListTree->RecursiveDeleteItem(_b->fRootDir,it->get());
4162 }
4163
4164 //b->RecursiveRemove(it->get());
4165 if (auto item = _b->fListTree->FindItemByObj(_b->fRootDir,it->get()); item) {
4166 std::cout << "Still Found obj: " << item << std::endl;
4167 }
4168 _b->fListTree->ClearViewPort();
4169
4170 }*/
4171 }
4172 }
4173 /*it->get()->ResetBit(TObject::kNotDeleted); ++it;*/ it = erase(it);
4174 } else {
4175 ++it;
4176 }
4177 }
4178
4179 return *this;
4180}
4181
4183{
4184 xRooNode out(".obs", std::make_shared<RooArgList>(), *this);
4185 out.get<RooArgList>()->setName((GetPath() + ".obs").c_str());
4186 for (auto o : vars()) {
4187 if (o->get<RooAbsArg>()->getAttribute("obs")) {
4188 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
4189 out.emplace_back(o);
4190 }
4191 }
4192 return out;
4193}
4194
4196{
4197 xRooNode out(".globs", std::make_shared<RooArgList>(), *this);
4198 out.get<RooArgList>()->setName((GetPath() + ".globs").c_str());
4199 for (auto o : obs()) {
4200 if (o->get<RooAbsArg>()->getAttribute("global")) {
4201 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
4202 out.emplace_back(o);
4203 }
4204 }
4205 return out;
4206}
4207
4209{
4210 xRooNode out(".robs", std::make_shared<RooArgList>(), *this);
4211 out.get<RooArgList>()->setName((GetPath() + ".robs").c_str());
4212 for (auto o : obs()) {
4213 if (!o->get<RooAbsArg>()->getAttribute("global")) {
4214 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
4215 out.emplace_back(o);
4216 }
4217 }
4218 return out;
4219}
4220
4222{
4223 xRooNode out(".pars", std::make_shared<RooArgList>(), *this);
4224 out.get<RooArgList>()->setName((GetPath() + ".pars").c_str());
4225 for (auto o : vars()) {
4226 if (!o->get<RooAbsArg>()->getAttribute("obs")) {
4227 out.get<RooArgList>()->add(*(o->get<RooAbsArg>()));
4228 out.emplace_back(o);
4229 }
4230 }
4231 return out;
4232}
4233
4235{
4236 xRooNode out(".args", std::make_shared<RooArgList>(), *this);
4237 out.get<RooArgList>()->setName((GetPath() + ".args").c_str());
4238 for (auto o : pars()) {
4239 if (o->get<RooConstVar>() || o->get<RooAbsArg>()->getAttribute("Constant")) {
4240 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
4241 out.emplace_back(o);
4242 }
4243 }
4244 return out;
4245}
4246
4248{
4249 xRooNode out(".floats", std::make_shared<RooArgList>(), *this);
4250 out.get<RooArgList>()->setName((GetPath() + ".floats").c_str());
4251 for (auto o : pars()) {
4252 if (!o->get<RooAbsArg>()->getAttribute("Constant") && !o->get<RooConstVar>()) {
4253 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
4254 out.emplace_back(o);
4255 }
4256 }
4257 return out;
4258}
4259
4261{
4262 xRooNode out(".poi", std::make_shared<RooArgList>(), *this);
4263 out.get<RooArgList>()->setName((GetPath() + ".poi").c_str());
4264 for (auto o : pars()) {
4265 if (o->get<RooAbsArg>()->getAttribute("poi")) {
4266 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
4267 out.emplace_back(o);
4268 }
4269 }
4270 return out;
4271}
4272
4274{
4275 xRooNode out(".np", std::make_shared<RooArgList>(), *this);
4276 out.get<RooArgList>()->setName((GetPath() + ".np").c_str());
4277 for (auto o : pars()) {
4278 if (!o->get<RooAbsArg>()->getAttribute("Constant") && !o->get<RooAbsArg>()->getAttribute("poi") &&
4279 !o->get<RooConstVar>()) {
4280 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
4281 out.emplace_back(o);
4282 }
4283 }
4284 return out;
4285}
4286
4288{
4289 xRooNode out(".vars", std::make_shared<RooArgList>(), *this);
4290 out.get<RooArgList>()->setName((GetPath() + ".vars").c_str());
4291 if (auto p = get<RooAbsArg>(); p) {
4292 // also need to get all constPars so use leafNodeServerList .. will include self if is fundamental, which is what
4293 // we want
4294 RooArgSet allLeafs;
4295 p->leafNodeServerList(&allLeafs);
4296 for (auto &c : allLeafs) {
4297 if (c->isFundamental() || (dynamic_cast<RooConstVar *>(c) && !TString(c->GetName()).IsFloat())) {
4298 out.get<RooArgList>()->add(*c);
4299 out.emplace_back(std::make_shared<xRooNode>(*c, *this));
4300 if (c->getAttribute("global"))
4301 out.back()->fFolder = "!globs";
4302 else if (c->getAttribute("obs"))
4303 out.back()->fFolder = "!obs";
4304 else if (dynamic_cast<RooConstVar *>(c))
4305 out.back()->fFolder = "!consts";
4306 else
4307 out.back()->fFolder = "!pars";
4308 }
4309 }
4310 } else if (auto p2 = get<RooAbsData>(); p2) {
4311 for (auto a : *p2->get()) {
4312 a->setAttribute("obs");
4313 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
4314 out.get<RooArgList>()->add(*a);
4315 }
4316 if (auto _globs = find(".globs"); _globs && _globs->get<RooAbsCollection>()) {
4317 for (auto &a : *_globs->get<RooAbsCollection>()) {
4318 a->setAttribute("obs");
4319 a->setAttribute("global");
4320 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
4321 out.get<RooArgList>()->add(*a);
4322 }
4323 } else if (auto _ws = ws(); _ws) {
4324 if (auto _globs2 = dynamic_cast<RooArgSet *>(GETWSSNAPSHOTS(_ws).find(p2->GetName())); _globs2) {
4325 for (auto a : *_globs2) {
4326 a->setAttribute("obs");
4327 a->setAttribute("global");
4328 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
4329 out.get<RooArgList>()->add(*a);
4330 }
4331 } else if (auto _gl = GETWSSETS(_ws).find("globalObservables"); _gl != GETWSSETS(_ws).end()) {
4332 for (auto &_g : _gl->second) {
4333 auto _clone = std::shared_ptr<RooAbsArg>(dynamic_cast<RooAbsArg *>(_g->Clone(_g->GetName())));
4334 if (auto v = std::dynamic_pointer_cast<RooAbsRealLValue>(_clone); v && _g->getStringAttribute("nominal"))
4335 v->setVal(TString(_g->getStringAttribute("nominal")).Atof());
4336 out.emplace_back(std::make_shared<xRooNode>(_clone, *this));
4337 out.get<RooArgList>()->add(*_clone);
4338 }
4339 } else if (fParent) {
4340 // note: this is slow in large workspaces ... too many obs to look through?
4341 std::unique_ptr<RooAbsCollection> _globs3(fParent->obs().argList().selectByAttrib("global", true));
4342 // std::unique_ptr<RooAbsCollection> _globs(_ws->allVars().selectByAttrib("global",true)); - tried this to
4343 // be quicker but it wasn't
4344 for (auto &_g : *_globs3) {
4345 auto _clone = std::shared_ptr<RooAbsArg>(dynamic_cast<RooAbsArg *>(_g->Clone(_g->GetName())));
4346 if (auto v = std::dynamic_pointer_cast<RooAbsRealLValue>(_clone); v && _g->getStringAttribute("nominal"))
4347 v->setVal(TString(_g->getStringAttribute("nominal")).Atof());
4348 out.emplace_back(std::make_shared<xRooNode>(_clone, *this));
4349 out.get<RooArgList>()->add(*_clone);
4350 }
4351 }
4352 }
4353 } else if (auto w = get<RooWorkspace>(); w) {
4354 for (auto a : w->allVars()) {
4355 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
4356 out.get<RooArgList>()->add(*a);
4357 }
4358 // add all cats as well
4359 for (auto a : w->allCats()) {
4360 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
4361 out.get<RooArgList>()->add(*a);
4362 }
4363 }
4364 return out;
4365}
4366
4368{
4369 xRooNode out(".components", nullptr, *this);
4370
4371 if (auto p = get<RooAddPdf>(); p) {
4372 for (auto &o : p->pdfList()) {
4373 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
4374 }
4375 } else if (auto p2 = get<RooRealSumPdf>(); p2) {
4376 // check for common prefixes and suffixes, will use to define aliases to shorten names
4377 // if have more than 1 function
4378 // TString commonPrefix=""; TString commonSuffix="";
4379 // if (p->funcList().size() > 1) {
4380 // bool checked=false;
4381 // for(auto& o : p->funcList()) {
4382 // if (!checked) {
4383 // commonPrefix = o->GetName(); commonSuffix = o->GetName(); checked=true;
4384 // } else {
4385 //
4386 // }
4387 // }
4388 // }
4389 for (auto &o : p2->funcList()) {
4390 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
4391 }
4392 } else if (auto p3 = get<RooAddition>(); p3) {
4393 for (auto &o : p3->list()) {
4394 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
4395 }
4396 } /*else if(auto p = get<RooFitResultTree>(); p) {
4397 long _nentries = p->GetEntries();
4398
4399 // iterate up through parents until we are out of the tree
4400 int depth = 0;
4401 auto _pdf = fParent;
4402 while(_pdf && _pdf->get()==p) {
4403 _pdf = _pdf->fParent;
4404 depth++;
4405 }
4406
4407 // first layer is organised by dsid ...
4408 if (depth==0) {
4409 long total = 0;
4410 for (auto &_d : _pdf->datasets()) { // parent of a frt should be the pdf
4411 auto _hash = RooAbsTree::nameToHash(_d->get()->GetName());
4412 TString _sel = TString::Format("data_hash.first==%d&&data_hash.second==%d", _hash.first, _hash.second);
4413 auto nFits = p->get()->GetEntries(_sel);
4414 if (nFits > 0) {
4415 total += nFits;
4416 out.emplace_back(std::make_shared<xRooNode>( _d->GetName(), fComp, *this));
4417 }
4418 if (total >= _nentries) break;
4419 }
4420 if (total < _nentries) {
4421 out.emplace_back(std::make_shared<xRooNode>("otherDatasets", fComp, *this));
4422 }
4423 } else if(depth==1) {
4424 // get unconditional fit if we can ...
4425 std::string dName = (strcmp(GetName(),"otherDatasets")==0) ? "*" : GetName();
4426 long total = 0;
4427 if(auto _ufits = p->GetEntrys(p->BuildSelection(dName, std::map<std::string, double>{})); !_ufits.empty()) {
4428 for(auto i : _ufits) {
4429 auto _fr = p->GetFit(i);
4430 TUUID uuid(_fr->GetName());
4431 TString _name = (dName=="*") ? _fr->GetTitle() : "";
4432 if (_name!="") _name += ";";
4433 // before adding a copy of fit, see if fit already exists in this and reuse
4434 _name += uuid.GetTime().AsString();
4435 if (auto _existing = find(_name.Data()); _existing) {
4436 out.emplace_back(std::make_shared<xRooNode>(*_existing));
4437 } else {
4438 out.emplace_back(std::make_shared<xRooNode>(_name, _fr, *this));
4439 out.back()->fFolder = "!unconditional";
4440 }
4441
4442 }
4443 total += _ufits.size();
4444 }
4445 for(auto& _par : *p->GetParameters()) {
4446 if (total >= _nentries) break;
4447 if (_par->getAttribute("Constant")) continue;
4448 //auto _sel = p->BuildSelection(dName,{{_par->GetName(),
4449 {dynamic_cast<RooRealVar*>(_par)->getMin(),dynamic_cast<RooRealVar*>(_par)->getMax()}}}); if (auto n = p->GetEntrys(
4450 p->BuildSelection(dName, {{_par->GetName(), std::numeric_limits<double>::quiet_NaN()}}),
4451 1).size();n > 0) {
4452 //if (p->get()->GetEntries(_sel) > 0) {
4453 out.emplace_back(std::make_shared<xRooNode>(_par->GetName(),fComp,*this));
4454 total += n;
4455 }
4456 }
4457 } else if(depth==2) {
4458 std::string dName = (strcmp(fParent->GetName(),"otherDatasets")==0) ? "*" : fParent->GetName();
4459 //std::cout << p->BuildSelection(dName,{{GetName(),std::numeric_limits<double>::quiet_NaN()}}).GetTitle() <<
4460 std::endl; for(auto& i : p->GetEntrys( p->BuildSelection(dName, {{GetName(),
4461 std::numeric_limits<double>::quiet_NaN()}}), -1)) { auto _fr = p->GetFit(i); TString _name =
4462 TString::Format("%f;%s",dynamic_cast<RooAbsReal*>(_fr->constPars().find(GetName()))->getVal(),TUUID(_fr->GetName()).GetTime().AsString());
4463 if (auto _existing = find(_name.Data()); _existing) {
4464 out.emplace_back(std::make_shared<xRooNode>(*_existing));
4465 } else {
4466 out.emplace_back(std::make_shared<xRooNode>(_name, _fr, *this));
4467 }
4468 }
4469 }
4470
4471 }*/
4472 else if (auto p5 = get<RooWorkspace>(); p5) {
4473 for (auto &o : p5->components()) {
4474 // only top-level nodes (only clients are integrals)
4475 // if (o->hasClients()) continue;
4476 bool hasClients = false;
4477 for (auto &c : o->clients())
4478 if (!c->InheritsFrom("RooRealIntegral")) {
4479 hasClients = true;
4480 break;
4481 }
4482 if (hasClients)
4483 continue;
4484 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
4485 if (o->InheritsFrom("RooAbsPdf"))
4486 out.back()->fFolder = "!models";
4487 else
4488 out.back()->fFolder = "!scratch";
4489 }
4490 for (auto &o : p5->allGenericObjects()) {
4491 if (auto fr = dynamic_cast<RooFitResult *>(o); fr) {
4492 TString s(fr->GetTitle());
4493 if (s.Contains(';'))
4494 s = s(0, s.Index(';'));
4495 if (auto _pdf = out.find(s.Data()); _pdf) {
4496 // std::cout << " type = " << _pdf->get()->ClassName() << std::endl;
4497 out.emplace_back(std::make_shared<xRooNode>(fr->GetName(), *fr, _pdf));
4498 // for a while, this node's parent pointed to something of type Node2!!
4499 // how to fix??? - I fxied it with a new constructo to avoid the shared_ptr<Node2> calling the const
4500 // Node2& constructor via getting wrapped in a Node2(shared_ptr<TObject>) call
4501 // out.back()->fParent = _pdf;
4502 // std::cout << " type2 = " << out.back()->fParent->get()->ClassName() << std::endl;
4503 } else {
4504 out.emplace_back(std::make_shared<xRooNode>(fr->GetName(), *fr, *this));
4505 }
4506 out.back()->fFolder = "!fits";
4507 } /*else if(auto t = dynamic_cast<TTree*>(o); t) {
4508 if (!t->GetUserInfo()) continue;
4509 if (t->GetUserInfo()->FindObject("fitConfig")) {
4510 auto frt = getObject<RooFitResultTree>(t->GetName());
4511 if (!frt) {
4512 frt = const_cast<xRooNode *>(this)->acquire<RooFitResultTree>(t);
4513 }
4514 if (frt) {
4515 if (auto _pdf = frt->GetPdf()) {
4516 out.emplace_back(std::make_shared<xRooNode>(frt, xRooNode(*_pdf, *this)));
4517 out.back()->fFolder = "!fits";
4518 }
4519 }
4520 } else if(t->GetUserInfo()->FindObject("rootVersion")) {
4521 // assume its a RooDataTree until we have better way to identify
4522 auto rdt = getObject<RooDataTree>(t->GetName());
4523 if (!rdt) {
4524 rdt = const_cast<xRooNode *>(this)->acquire<RooDataTree>(t);
4525 }
4526 if (rdt) {
4527 if (auto _pdf = rdt->GetPdf()) {
4528 out.emplace_back(std::make_shared<xRooNode>(rdt, xRooNode(*_pdf, *this)));
4529 out.back()->fFolder = "!datasets";
4530 }
4531 }
4532 }
4533 // TODO: Handle other tree type?
4534 }*/
4535 else {
4536 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
4537 out.back()->fFolder = "!objects";
4538 }
4539 }
4540 for (auto &[k, v] : GETWSSETS(p5)) {
4541 // skip 'CACHE' sets because they are auto-removed when sanitizing workspaces, which will invalidate these
4542 // children
4543 if (k.find("CACHE_") == 0)
4544 continue;
4545 out.emplace_back(std::make_shared<xRooNode>(k.c_str(), v, *this));
4546 out.back()->fFolder = "!sets";
4547 }
4548
4549 RooLinkedList snaps = GETWSSNAPSHOTS(p5);
4550 std::unique_ptr<TIterator> iter(snaps.MakeIterator());
4551 RooArgSet *snap;
4552 while ((snap = (RooArgSet *)iter->Next())) {
4553 out.emplace_back(std::make_shared<xRooNode>(*snap, *this));
4554 out.back()->fFolder = "!snapshots";
4555 }
4556 } else if (strlen(GetName()) > 0 && GetName()[0] == '!' && fParent) {
4557 // special case of dynamic property
4558 if (TString(GetName()) == "!.pars") {
4559 for (auto &c : fParent->pars()) {
4560 out.emplace_back(c);
4561 }
4562 } else {
4563 // the components of a folder are the children of the parent (after browsing) that live in this folder
4564 fParent->browse();
4565 for (auto &c : *fParent) {
4566 if (c->fFolder == GetName()) {
4567 out.emplace_back(c);
4568 }
4569 }
4570 }
4571 }
4572
4573 return out;
4574}
4575
4577{
4578 xRooNode out(".bins", nullptr, *this);
4579
4580 if (auto p = get<RooSimultaneous>(); p) {
4581 for (auto &c : p->indexCat()) {
4582 auto pp = p->getPdf(c.first.c_str());
4583 if (!pp)
4584 continue;
4585 out.emplace_back(
4586 std::make_shared<xRooNode>(TString::Format("%s=%s", p->indexCat().GetName(), c.first.c_str()), *pp, *this));
4587 }
4588 } else if (auto phf = get<ParamHistFunc>(); phf) {
4589 int i = 1;
4590#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
4591 auto& pSet = phf->_paramSet;
4592#else
4593 auto& pSet = phf->paramList();
4594#endif
4595 for (auto par : pSet) {
4596 out.emplace_back(std::make_shared<xRooNode>(*par, *this));
4597 out.back()->fBinNumber = i;
4598 i++;
4599 }
4600 } else if (auto ax = GetXaxis(); ax) {
4601 for (int i = 1; i <= ax->GetNbins(); i++) {
4602 // create a RooProduct of all bin-specific factors of all shapeFactors
4603 std::vector<RooAbsArg *> _factors;
4604 for (auto f : factors()) {
4605 if (f->get<ParamHistFunc>()) {
4606 if (f->bins()[i - 1]->get<RooProduct>())
4607 for (auto &ss : f->bins()[i - 1]->factors())
4608 _factors.push_back(ss->get<RooAbsArg>());
4609 else
4610 _factors.push_back(f->bins()[i - 1]->get<RooAbsArg>());
4611 }
4612 }
4613 out.emplace_back(std::make_shared<xRooNode>(
4614 TString::Format("%s=%g", ax->GetParent()->GetName(), ax->GetBinCenter(i)),
4615 _factors.empty() ? nullptr
4616 : std::make_shared<RooProduct>(TString::Format("%s.binFactors.bin%d", GetName(), i),
4617 "binFactors", RooArgList()),
4618 *this));
4619 for (auto f : _factors) {
4620#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
4621 out.back()->get<RooProduct>()->_compRSet.add(*f);
4622#else
4623 const_cast<RooArgList&>(out.back()->get<RooProduct>()->realComponents()).add(*f);
4624#endif
4625 }
4626 out.back()->fBinNumber = i;
4627 }
4628 }
4629
4630 return out;
4631}
4632
4634{
4636
4637 // if parent is a sumpdf or addpdf then include the coefs
4638 // if func appears multiple times then coefs must be combined into a RooAddition temporary
4639 if (fParent) {
4640 if (auto p = fParent->get<RooRealSumPdf>(); p) {
4641 int i = 0;
4642 for (auto &o : p->funcList()) {
4643 if (o == get()) {
4644 coefs.add(*p->coefList().at(i));
4645 }
4646 i++;
4647 }
4648 } else if (auto p2 = fParent->get<RooAddPdf>(); p2) {
4649 int i = 0;
4650 for (auto &o : p2->pdfList()) {
4651 if (o == get()) {
4652 coefs.add(*p2->coefList().at(i));
4653 }
4654 i++;
4655 }
4656 }
4657 }
4658 xRooNode out(".coefs", coefs.empty() ? nullptr : std::make_shared<RooAddition>(".coefs", "Coefficients of", coefs),
4659 *this);
4660 if (!coefs.empty())
4661 out.browse();
4662
4663 return out;
4664}
4665
4667{
4668 xRooNode out(".factors", nullptr, *this);
4669
4670 if (auto p = get<RooProdPdf>(); p) {
4671 auto _main = mainChild();
4672 if (auto a = _main.get<RooRealSumPdf>(); a && !a->getStringAttribute("alias"))
4673 a->setStringAttribute("alias", "samples");
4674 else if (auto a2 = _main.get<RooAddPdf>(); a2 && !a2->getStringAttribute("alias"))
4675 a2->setStringAttribute("alias", "components");
4676 int _npdfs = p->pdfList().size();
4677 for (auto &o : p->pdfList()) {
4678 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
4679 if (_npdfs > 5 && o != _main.get())
4680 out.back()->fFolder = "!constraints";
4681 }
4682 } else if (auto p2 = get<RooProduct>(); p2) {
4683 for (auto &o : p2->components()) {
4684 if (o->InheritsFrom("RooProduct")) {
4685 // get factors of this term
4686 auto x = xRooNode("tmp", *o, *this).factors();
4687 for (auto &n : x) {
4688 out.emplace_back(std::make_shared<xRooNode>(n->GetName(), n->fComp, *this));
4689 }
4690 } else {
4691 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
4692 }
4693 }
4694 } else if (auto w = get<RooWorkspace>(); w) {
4695 // if workspace, return all functions (not pdfs) that have a RooProduct as one of their clients
4696 // or not clients
4697 // exclude obs and globs
4698 auto _obs = obs().argList();
4699 for (auto a : w->allFunctions()) {
4700 if (_obs.contains(*a))
4701 continue;
4702 bool show(true);
4703 for (auto c : a->clients()) {
4704 show = false;
4705 if (c->InheritsFrom("RooProduct"))
4706 show = true;
4707 }
4708 if (show)
4709 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
4710 }
4711 }
4712
4713 // include coefs if any
4714 auto _coefs = coefs();
4715 if (!_coefs.empty()) {
4716 if (_coefs.size() == 1) {
4717 if (strcmp(_coefs.at(0)->GetName(), "1") != 0 &&
4718 strcmp(_coefs.at(0)->GetName(), "ONE") != 0) { // don't add the "1"
4719 out.emplace_back(std::make_shared<xRooNode>(".coef", *_coefs.at(0)->get(), *this));
4720 }
4721 } else {
4722 out.emplace_back(std::make_shared<xRooNode>(_coefs));
4723 }
4724 }
4725 /*
4726 // if parent is a sumpdf or addpdf then include the coefs
4727 // if func appears multiple times then coefs must be combined into a RooAddition temporary
4728 if (fParent) {
4729 RooArgList coefs;
4730 if(auto p = fParent->get<RooRealSumPdf>();p) {
4731 int i=0;
4732 for(auto& o : p->funcList()) {
4733 if (o == get()) {
4734 coefs.add( *p->coefList().at(i) );
4735 }
4736 i++;
4737 }
4738 } else if(auto p = fParent->get<RooAddPdf>(); p) {
4739 int i=0;
4740 for(auto& o : p->pdfList()) {
4741 if (o == get()) {
4742 coefs.add( *p->coefList().at(i) );
4743 }
4744 i++;
4745 }
4746 }
4747 if (!coefs.empty()) {
4748 if (coefs.size() == 1) {
4749 if (strcmp(coefs.at(0)->GetName(),"1")) { // don't add the "1"
4750 out.emplace_back(std::make_shared<Node2>(".coef", *coefs.at(0), *this));
4751 }
4752 } else {
4753 out.emplace_back(std::make_shared<Node2>(".coefs",
4754 std::make_shared<RooAddition>(".coefs", "Coefficients of",
4755 coefs), *this));
4756 }
4757 }
4758 }
4759 */
4760 return out;
4761}
4762
4764{
4765 xRooNode out(".variations", nullptr, *this);
4766
4767// if (auto p = get<RooSimultaneous>(); p) {
4768// for (auto &c : p->indexCat()) {
4769// auto pp = p->getPdf(c.first.c_str());
4770// if (!pp)
4771// continue;
4772// out.emplace_back(
4773// std::make_shared<xRooNode>(TString::Format("%s=%s", p->indexCat().GetName(), c.first.c_str()), *pp, *this));
4774// }
4775// } else
4776 if (auto p2 = get<PiecewiseInterpolation>(); p2) {
4777#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
4778 out.emplace_back(std::make_shared<xRooNode>("nominal", p2->_nominal.arg(), *this));
4779#else
4780 out.emplace_back(std::make_shared<xRooNode>("nominal", *(p2->nominalHist()), *this));
4781#endif
4782 for (size_t i = 0; i < p2->paramList().size(); i++) {
4783 // TODO: should we only return one if we find they are symmetrized?
4784 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p2->paramList().at(i)->GetName()),
4785 *p2->highList().at(i), *this));
4786 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p2->paramList().at(i)->GetName()),
4787 *p2->lowList().at(i), *this));
4788 }
4789 } else if (auto p3 = get<RooStats::HistFactory::FlexibleInterpVar>(); p3) {
4790#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
4791 out.emplace_back(std::make_shared<xRooNode>("nominal", RooFit::RooConst(p3->_nominal), *this));
4792 for (size_t i = 0; i < p3->_paramList.size(); i++) {
4793 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p3->_paramList.at(i)->GetName()),
4794 RooFit::RooConst(p3->_high.at(i)), *this));
4795 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p3->_paramList.at(i)->GetName()),
4796 RooFit::RooConst(p3->_low.at(i)), *this));
4797 }
4798#else
4799 out.emplace_back(std::make_shared<xRooNode>("nominal", RooFit::RooConst(p3->nominal()), *this));
4800 for (size_t i = 0; i < p3->variables().size(); i++) {
4801 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p3->variables().at(i)->GetName()),
4802 RooFit::RooConst(p3->high().at(i)), *this));
4803 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p3->variables().at(i)->GetName()),
4804 RooFit::RooConst(p3->low().at(i)), *this));
4805 }
4806#endif
4807
4808 } else if (auto p4 = get<ParamHistFunc>(); p4) {
4809 // I *think* I put this here so that can browse into a ParamHistFunc
4810// int i = 0;
4811// for (auto par : p4->_paramSet) {
4812// TString _name = par->GetName();
4813// // if(auto _v = dynamic_cast<RooRealVar*>(p->_dataSet.get(i)->first()); _v) {
4814// // _name = TString::Format("%s=%g",_v->GetName(),_v->getVal());
4815// // }
4816// // out.emplace_back(std::make_shared<xRooNode>(_name,*par,*this)); -- -removed cos now have bin() method
4817// i++;
4818// }
4819 }
4820 return out;
4821}
4822
4824{
4825 RooArgList out;
4826 out.setName(GetName());
4827 for (auto &k : *this) {
4828 if (auto o = k->get<RooAbsArg>(); o)
4829 out.add(*o);
4830 }
4831 return out;
4832}
4833
4835{
4836 xRooNode out(".datasets()", nullptr, *this);
4837 out.fBrowseOperation = [](xRooNode *f) { return f->fParent->datasets(); };
4838
4839 if (auto _ws = get<RooWorkspace>(); _ws) {
4840 for (auto &d : _ws->allData()) {
4841 out.emplace_back(std::make_shared<xRooNode>(*d, *this));
4842 out.back()->fFolder = "!datasets";
4843 }
4844 } else if (auto __ws = ws(); __ws) {
4845 if (get<RooAbsPdf>()) {
4846 // only add datasets that have observables that cover all our observables
4847 RooArgSet _obs(obs().argList());
4848 _obs.add(coords(false).argList(), true); // include coord observables too, and current xaxis if there's one
4849 if (auto ax = GetXaxis(); ax && dynamic_cast<RooAbsArg *>(ax->GetParent())->getAttribute("obs")) {
4850 auto a = dynamic_cast<RooAbsArg *>(ax->GetParent());
4851 _obs.add(*a, true);
4852 }
4853 xRooNode _wsNode(*__ws, *this);
4854 for (auto &d : _wsNode.datasets()) {
4855 if (std::unique_ptr<RooAbsCollection>(d->obs().argList().selectCommon(_obs))->size() == _obs.size()) {
4856 // all obs present .. include
4857 out.emplace_back(std::make_shared<xRooNode>(d->fComp, *this));
4858 }
4859 }
4860 } /*else if(auto p = get<RooFitResult>(); p) {
4861 // look for datasets in workspace that match the fit result name after hashing
4862 for(auto& _d : xRooNode(*_ws,*this).datasets()) {
4863 auto _hash = RooAbsTree::nameToHash(_d->get()->GetName());
4864 if (TString::Format("%d;%d",_hash.first,_hash.second) == p->GetTitle()) {
4865 out.emplace_back(std::make_shared<xRooNode>(_d->fComp, *this));
4866 }
4867 }
4868 }*/
4869 }
4870
4871 return out;
4872}
4873
4874TGraph *xRooNode::BuildGraph(RooAbsLValue *v, bool includeZeros, TVirtualPad *fromPad) const
4875{
4876
4877 if (auto fr = get<RooFitResult>(); fr) {
4878 return nullptr;
4879 }
4880
4881 if (auto theData = get<RooDataSet>(); theData) {
4882
4883 TH1 *theHist = nullptr;
4884
4885 if (fromPad) {
4886 // find first histogram in pad
4887 for (auto o : *fromPad->GetListOfPrimitives()) {
4888 theHist = dynamic_cast<TH1 *>(o);
4889 if (theHist) {
4890 theHist = (TH1 *)theHist->Clone();
4891 theHist->Reset();
4892 break;
4893 } // clone because theHist gets deleted below
4894 }
4895 }
4896
4897 if (!theHist) {
4898 auto _parentPdf = parentPdf();
4899 if (!_parentPdf) {
4900 // can still build graph if v is an obs ... will use v binning
4901 auto vo = dynamic_cast<TObject *>(v);
4902 if (v && obs().find(vo->GetName())) {
4903 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(v)) {
4904 theHist = new TH1D(
4905 TString::Format("%s_%s", GetName(), vo->GetName()),
4906 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
4907 cat->numTypes(), 0, cat->numTypes());
4908 for (int i = 0; i < cat->numTypes(); i++) {
4909 cat->setBin(i);
4910 theHist->GetXaxis()->SetBinLabel(i + 1, cat->getLabel());
4911 }
4912 } else {
4913 theHist = new TH1D(
4914 TString::Format("%s_%s", GetName(), vo->GetName()),
4915 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
4916 v->numBins(), v->getBinningPtr(nullptr)->lowBound(), v->getBinningPtr(nullptr)->highBound());
4917 }
4918 } else {
4919 throw std::runtime_error("Cannot draw dataset without parent PDF");
4920 }
4921 } else {
4922 theHist = _parentPdf->BuildHistogram(v, true);
4923 }
4924 }
4925 if (!theHist)
4926 return nullptr;
4927 // this hist will get filled with w*x to track weighted x position per bin
4928 TH1 *xPos = (TH1 *)theHist->Clone("xPos");
4929 xPos->Reset();
4930 TH1 *xPos2 = (TH1 *)theHist->Clone("xPos2");
4931 xPos2->Reset();
4932 auto nHist = std::unique_ptr<TH1>((TH1 *)theHist->Clone("nEntries"));
4933 nHist->Reset();
4934
4935 auto dataGraph = new TGraphAsymmErrors;
4936 dataGraph->SetEditable(false);
4937 dataGraph->SetName(GetName());
4938 dataGraph->SetTitle(strlen(theData->GetTitle()) ? theData->GetTitle() : theData->GetName());
4939 // next line triggers creation of the histogram inside the graph, in root 6.22 that isn't protected from being
4940 // added to gDirectory
4941 dataGraph->SetTitle(TString::Format("%s;%s;Events", dataGraph->GetTitle(), theHist->GetXaxis()->GetTitle()));
4942 *static_cast<TAttMarker *>(dataGraph) = *static_cast<TAttMarker *>(theHist);
4943 *static_cast<TAttLine *>(dataGraph) = *static_cast<TAttLine *>(theHist);
4944 dataGraph->SetMarkerStyle(20);
4945 dataGraph->SetLineColor(kBlack);
4946
4947 auto _obs = obs();
4948
4949 // auto x = theData->get()->find((v) ? dynamic_cast<TObject*>(v)->GetName() : theHist->GetXaxis()->GetName());
4950 // const RooAbsReal* xvar = (x) ? dynamic_cast<RooAbsReal*>(x) : nullptr;
4951 // const RooAbsCategory* xcat = (x && !xvar) ? dynamic_cast<RooAbsCategory*>(x) : nullptr;
4952 auto x = _obs.find((v) ? dynamic_cast<TObject *>(v)->GetName() : theHist->GetXaxis()->GetName());
4953 if (x && x->get<RooAbsArg>()->getAttribute("global")) {
4954 // is global observable ...
4955 dataGraph->SetPoint(0, x->get<RooAbsReal>()->getVal(), 1e-15);
4956 dataGraph->SetTitle(TString::Format("%s = %f", dataGraph->GetTitle(), dataGraph->GetPointX(0)));
4957 delete xPos;
4958 delete xPos2;
4959 delete theHist;
4960 return dataGraph;
4961 }
4962
4963 const RooAbsReal *xvar = (x) ? x->get<RooAbsReal>() : nullptr;
4964 const RooAbsCategory *xcat = (x && !xvar) ? x->get<RooAbsCategory>() : nullptr;
4965
4966 auto _coords = coords();
4967
4968 TString pName((fromPad) ? fromPad->GetName() : "");
4969 auto _pos = pName.Index('=');
4970
4971 int nevent = theData->numEntries();
4972 for (int i = 0; i < nevent; i++) {
4973 theData->get(i);
4974 bool _skip = false;
4975 for (auto _c : _coords) {
4976 if (auto cat = _c->get<RooAbsCategoryLValue>(); cat) {
4977 if (cat->getIndex() != theData->get()->getCatIndex(cat->GetName())) {
4978 _skip = true;
4979 break;
4980 }
4981 }
4982 }
4983 if (_pos != -1) {
4984 if (auto cat = dynamic_cast<RooAbsCategory *>(theData->get()->find(TString(pName(0, _pos))));
4985 cat && cat->getLabel() != pName(_pos + 1, pName.Length())) {
4986 _skip = true;
4987 }
4988 }
4989 if (_skip)
4990 continue;
4991
4992 if (xvar) {
4993 xPos->Fill(xvar->getVal(), xvar->getVal() * theData->weight());
4994 xPos2->Fill(xvar->getVal(), pow(xvar->getVal(), 2) * theData->weight());
4995 }
4996
4997 if (xcat) {
4998 theHist->Fill(xcat->getLabel(), theData->weight());
4999 nHist->Fill(xcat->getLabel(), 1);
5000 } else {
5001 theHist->Fill((x) ? xvar->getVal() : 0.5, theData->weight());
5002 nHist->Fill((x) ? xvar->getVal() : 0.5, 1);
5003 }
5004 }
5005
5006 xPos->Divide(theHist);
5007 xPos2->Divide(theHist);
5008
5009 // update the x positions to the means for each bin and use poisson asymmetric errors for data ..
5010 for (int i = 0; i < theHist->GetNbinsX(); i++) {
5011 if (includeZeros || nHist->GetBinContent(i + 1)) {
5012 double val = theHist->GetBinContent(i + 1);
5013
5014 dataGraph->SetPoint(dataGraph->GetN(),
5015 (xvar && val) ? xPos->GetBinContent(i + 1) : theHist->GetBinCenter(i + 1), val);
5016
5017 // x-error will be the (weighted) standard deviation of the x values ...
5018 double xErr = sqrt(xPos2->GetBinContent(i + 1) - pow(xPos->GetBinContent(i + 1), 2));
5019
5020 if (xErr || val)
5021 dataGraph->SetPointError(dataGraph->GetN() - 1, xErr, xErr,
5022 val - 0.5 * TMath::ChisquareQuantile(TMath::Prob(1, 1) / 2., 2. * (val)),
5023 0.5 * TMath::ChisquareQuantile(1. - TMath::Prob(1, 1) / 2., 2. * (val + 1)) -
5024 val);
5025 }
5026 }
5027
5028 // transfer limits from theHist to dataGraph hist
5029 dataGraph->GetHistogram()->GetXaxis()->SetLimits(theHist->GetXaxis()->GetXmin(), theHist->GetXaxis()->GetXmax());
5030 // and bin labels, if any
5031 if (xcat) {
5032 dataGraph->GetHistogram()->GetXaxis()->Set(theHist->GetNbinsX(), 0, theHist->GetNbinsX());
5033 for (int i = 1; i <= theHist->GetNbinsX(); i++)
5034 dataGraph->GetHistogram()->GetXaxis()->SetBinLabel(i, theHist->GetXaxis()->GetBinLabel(i));
5035 }
5036
5037 delete xPos;
5038 delete xPos2;
5039 delete theHist;
5040
5041 // std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case getObject
5042 // has decided to return the owning ptr (for some reason) std::string _title =
5043 // strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(); if (!gROOT->GetStyle(_title.c_str()))
5044 // {
5045 // if ( (style = getObject<TStyle>(_title)) ) {
5046 // // loaded style (from workspace?) so put in list and use that
5047 // gROOT->GetListOfStyles()->Add(style.get());
5048 // } else {
5049 // // create new style - gets put in style list automatically so don't have to delete
5050 // // acquire them so saved to workspaces for auto reload ...
5051 // style = const_cast<xRooNode&>(*this).acquireNew<TStyle>(_title.c_str(),
5052 // TString::Format("Style for %s component", _title.c_str()));
5053 // (TAttLine &) (*style) = *dynamic_cast<TAttLine *>(dataGraph);
5054 // (TAttFill &) (*style) = *dynamic_cast<TAttFill *>(dataGraph);
5055 // (TAttMarker &) (*style) = *dynamic_cast<TAttMarker *>(dataGraph);
5056 // gROOT->GetListOfStyles()->Add(style.get());
5057 // }
5058 // }
5059 auto _style = style(dataGraph);
5060 *dynamic_cast<TAttLine *>(dataGraph) = *_style;
5061 *dynamic_cast<TAttFill *>(dataGraph) = *_style;
5062 *dynamic_cast<TAttMarker *>(dataGraph) = *_style;
5063
5064 return dataGraph;
5065 }
5066
5067 throw std::runtime_error("Cannot build graph");
5068}
5069
5071{
5072 if (fr) {
5073 if (auto _w = ws(); _w) {
5074 auto res = acquire(std::shared_ptr<RooFitResult>(const_cast<RooFitResult *>(fr), [](RooFitResult *) {}));
5075 for (auto o : _w->allGenericObjects()) {
5076 if (auto _fr = dynamic_cast<RooFitResult *>(o); _fr) {
5077 _fr->ResetBit(1 << 20);
5078 }
5079 }
5080 res->SetBit(1 << 20);
5081 // assign values
5082 auto allVars = _w->allVars();
5083 allVars = fr->floatParsFinal();
5084 allVars = fr->constPars();
5085 } else {
5086 // need to add to memory as a specific name
5087 throw std::runtime_error(
5088 "Not supported yet"); // complication is how to replace an existing fitResult in .memory
5089 // auto _clone = std::make_shared<RooFitResult>(*fr);
5090 //_clone->SetName("fitResult");
5091 }
5092 } else {
5093 SetFitResult(fitResult("prefit").get<RooFitResult>());
5094 }
5095}
5096
5098{
5099 if (auto _fr = fr.get<const RooFitResult>()) {
5100 SetFitResult(_fr);
5101 } else
5102 throw std::runtime_error("Not a RooFitResult");
5103}
5104
5105xRooNode xRooNode::fitResult(const char *opt) const
5106{
5107
5108 if (get<RooFitResult>())
5109 return *this;
5110 if (get<RooAbsData>()) {
5111 if (auto _fr = find(".fitResult"); _fr)
5112 return _fr;
5113#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
5114 // check if weightVar of RooAbsData has fitResult attribute on it, will be the generation fit result
5115 if (get<RooDataSet>() && get<RooDataSet>()->weightVar() &&
5116 get<RooDataSet>()->weightVar()->getStringAttribute("fitResult")) {
5117 return xRooNode(getObject<const RooFitResult>(get<RooDataSet>()->weightVar()->getStringAttribute("fitResult")),
5118 *this);
5119 }
5120#endif
5121 return xRooNode();
5122 }
5123
5124 TString sOpt(opt);
5125 if (sOpt == "prefit") {
5126 // build a fitResult using nominal values and infer errors from constraints
5127 // that aren't the 'main' constraints
5128 // Warning("fitResult","Building prefitResult by examining pdf. Consider setting an explicit prefitResult
5129 // (SetFitResult(fr)) where fr name is prefitResult");
5130
5131 std::unique_ptr<RooArgList> _pars(dynamic_cast<RooArgList *>(pars().argList().selectByAttrib("Constant", false)));
5132 auto fr = std::make_shared<RooFitResult>("prefitResult", "Prefit");
5133 fr->setFinalParList(*_pars);
5134 for (auto &p : fr->floatParsFinal()) {
5135 auto _v = dynamic_cast<RooRealVar *>(p);
5136 if (!_v)
5137 continue;
5138 if (auto s = _v->getStringAttribute("nominal"); s)
5139 _v->setVal(TString(s).Atof());
5140 auto _constr = xRooNode(fParent->getObject<RooRealVar>(p->GetName()), *this).constraints();
5141 std::shared_ptr<xRooNode> pConstr;
5142 for (auto &c : _constr) {
5143 if (c->get<RooPoisson>() || c->get<RooGaussian>()) {
5144 pConstr = c;
5145 break;
5146 }
5147 }
5148 if (pConstr) {
5149 // there will be 3 deps, one will be this par, the other two are the mean and error (or error^2 in case of
5150 // poisson use the one that's a ConstVar as the error to break a tie ...
5151 double prefitVal = 0, prefitError = 0;
5152 for (auto &_d : pConstr->vars()) {
5153 if (strcmp(p->GetName(), _d->get()->GetName()) == 0)
5154 continue;
5155 if (auto _c = _d->get<RooConstVar>(); _c && _c->getVal() != 0) {
5156 if (prefitError)
5157 prefitVal = prefitError; // loading val into error already, so move it over
5158 prefitError = _c->getVal();
5159 } else if (prefitError == 0)
5160 prefitError = _d->get<RooAbsReal>()->getVal();
5161 else
5162 prefitVal = _d->get<RooAbsReal>()->getVal();
5163 }
5164
5165 if (pConstr->get<RooGaussian>() && pConstr->browse().find(".sigma")) {
5166 prefitError = pConstr->find(".sigma")->get<RooAbsReal>()->getVal();
5167 }
5168 // std::cout << p->GetName() << " extracted " << prefitVal << " " << prefitError << " from ";
5169 // pConstr->deps().Print();
5170 if (pConstr->get<RooPoisson>()) {
5171 // prefitVal will be the global observable value, need to divide that by tau
5172 prefitVal /= prefitError;
5173 // prefiterror will be tau ... need 1/sqrt(tau) for error
5174 prefitError = 1. / sqrt(prefitError);
5175 }
5176 if (!_v->getStringAttribute("nominal"))
5177 _v->setVal(prefitVal);
5178 _v->setError(prefitError);
5179 } else {
5180 // unconstrained, remove error
5181 _v->removeError();
5182 }
5183 }
5184 auto _args = args().argList();
5185 // global obs are added to constPars list too
5186 auto _globs = globs(); // keep alive as may own glob
5187 _args.add(_globs.argList());
5188 fr->setConstParList(_args);
5189 std::unique_ptr<RooArgList> _snap(dynamic_cast<RooArgList *>(_pars->snapshot()));
5190 for (auto &p : *_snap) {
5191 if (auto atr = p->getStringAttribute("initVal"); atr && dynamic_cast<RooRealVar *>(p))
5192 dynamic_cast<RooRealVar *>(p)->setVal(TString(atr).Atof());
5193 }
5194 fr->setInitParList(*_snap);
5195 return xRooNode(fr, *this);
5196 }
5197
5198 // return first checked fit result present in the workspace
5199 if (auto _w = ws(); _w) {
5200 for (auto o : _w->allGenericObjects()) {
5201 if (auto _fr = dynamic_cast<RooFitResult *>(o); _fr && _fr->TestBit(1 << 20)) {
5202 // check all pars match final/const values ... if mismatch need to create a new RooFitResult
5203 bool match = true;
5204 for (auto p : pars()) {
5205 if (!p->get<RooAbsReal>())
5206 continue;
5207 if (p->get<RooAbsArg>()->getAttribute("Constant")) {
5208 if (_fr->floatParsFinal().find(p->GetName()) ||
5209 std::abs(_fr->constPars().getRealValue(p->GetName(), std::numeric_limits<double>::quiet_NaN()) -
5210 p->get<RooAbsReal>()->getVal()) > 1e-15) {
5211 match = false;
5212 break;
5213 }
5214 } else {
5215 if (_fr->constPars().find(p->GetName()) ||
5216 std::abs(
5217 _fr->floatParsFinal().getRealValue(p->GetName(), std::numeric_limits<double>::quiet_NaN()) -
5218 p->get<RooAbsReal>()->getVal()) > 1e-15) {
5219 match = false;
5220 break;
5221 }
5222 }
5223 }
5224 if (!match) {
5225 // create new fit result using covariances from this fit result
5226 std::unique_ptr<RooArgList> _pars(
5227 dynamic_cast<RooArgList *>(pars().argList().selectByAttrib("Constant", false)));
5228 auto fr = std::make_shared<RooFitResult>("");
5229 fr->SetTitle(TString::Format("%s parameter snapshot", GetName()));
5230 fr->setFinalParList(*_pars);
5231 TMatrixTSym<Double_t> *prevCov = static_cast<TMatrixTSym<Double_t>*>(GETDMP(fr.get(),_VM));
5232 if (prevCov) {
5233 auto cov = _fr->reducedCovarianceMatrix(*_pars);
5234 fr->setCovarianceMatrix(cov);
5235 }
5236
5237 auto _args = args().argList();
5238 // global obs are added to constPars list too
5239 auto _globs = globs(); // keep alive as may own glob
5240 _args.add(_globs.argList());
5241 fr->setConstParList(_args);
5242 std::unique_ptr<RooArgList> _snap(dynamic_cast<RooArgList *>(_pars->snapshot()));
5243 for (auto &p : *_snap) {
5244 if (auto atr = p->getStringAttribute("initVal"); atr && dynamic_cast<RooRealVar *>(p))
5245 dynamic_cast<RooRealVar *>(p)->setVal(TString(atr).Atof());
5246 }
5247 fr->setInitParList(*_snap);
5248 return xRooNode(fr, *this);
5249 }
5250 return xRooNode(*_fr, std::make_shared<xRooNode>(*_w, std::make_shared<xRooNode>()));
5251 }
5252 }
5253 } else {
5254 // objects not in workspaces are allowed to have a fitResult set in their memory
5255 // use getObject to get it
5256 if (auto fr = getObject<RooFitResult>(".fitResult"); fr) {
5257 return xRooNode(fr, *this);
5258 }
5259 }
5260
5261 std::unique_ptr<RooArgList> _pars(dynamic_cast<RooArgList *>(pars().argList().selectByAttrib("Constant", false)));
5262 auto fr = std::make_shared<RooFitResult>("");
5263 fr->SetTitle(TString::Format("%s uncorrelated parameter snapshot", GetName()));
5264 fr->setFinalParList(*_pars);
5265
5266 TMatrixDSym cov(fr->floatParsFinal().getSize());
5267 TMatrixTSym<Double_t> *prevCov = static_cast<TMatrixTSym<Double_t>*>(GETDMP(fr.get(),_VM));
5268 if (prevCov) {
5269 for (int i = 0; i < prevCov->GetNcols(); i++) {
5270 for (int j = 0; j < prevCov->GetNrows(); j++) {
5271 cov(i, j) = (*prevCov)(i, j);
5272 }
5273 }
5274 }
5275 int i = 0;
5276 for (auto &p : fr->floatParsFinal()) {
5277 if (!prevCov || i >= prevCov->GetNcols()) {
5278 cov(i, i) = pow(dynamic_cast<RooRealVar *>(p)->getError(), 2);
5279 }
5280 i++;
5281 }
5282 int covQualBackup = fr->covQual();
5283 fr->setCovarianceMatrix(cov);
5284 fr->setCovQual(covQualBackup);
5285
5286 auto _args = args().argList();
5287 // global obs are added to constPars list too
5288 auto _globs = globs(); // keep alive as may own glob
5289 _args.add(_globs.argList());
5290 fr->setConstParList(_args);
5291 std::unique_ptr<RooArgList> _snap(dynamic_cast<RooArgList *>(_pars->snapshot()));
5292 for (auto &p : *_snap) {
5293 if (auto atr = p->getStringAttribute("initVal"); atr && dynamic_cast<RooRealVar *>(p))
5294 dynamic_cast<RooRealVar *>(p)->setVal(TString(atr).Atof());
5295 }
5296 fr->setInitParList(*_snap);
5297
5298 // return *const_cast<Node2*>(this)->emplace_back(std::make_shared<Node2>(".fitResult",fr,*this));
5299 return xRooNode(fr, *this);
5300}
5301
5302// xRooNode xRooNode::fitTo_(const char* datasetName) const {
5303// try {
5304// return fitTo(datasetName);
5305// } catch(const std::exception& e) {
5306// new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),kMBIconExclamation); // deletes
5307// self on dismiss? return xRooNode();
5308// }
5309// }
5310//
5311// xRooNode xRooNode::fitTo(const char* datasetName) const {
5312// return fitTo(*datasets().at(datasetName));
5313// }
5314
5315void xRooNode::SetRange(const char *range, double low, double high)
5316{
5317 if (!std::isnan(low) && !std::isnan(high) && get<RooRealVar>()) {
5318 if (range && strlen(range))
5319 get<RooRealVar>()->setRange(range, low, high);
5320 else
5321 get<RooRealVar>()->setRange(low, high);
5322 return;
5323 }
5324 if (auto o = get<RooAbsArg>(); o)
5325 o->setStringAttribute("range", range);
5326 // todo: clear the range attribute on all servers
5327 // could make this controlled by a flag but probably easiest to enforce so you must set range
5328 // in children after if you wanted to override
5329}
5330const char *xRooNode::GetRange() const
5331{
5332 std::string &out = fRange;
5333 if (auto o = get<RooAbsArg>(); o && o->getStringAttribute("range"))
5334 out = o->getStringAttribute("range");
5335 auto _parent = fParent;
5336 while (out.empty() && _parent) {
5337 if (auto o = _parent->get<RooAbsArg>(); o && o->getStringAttribute("range"))
5338 out = o->getStringAttribute("range");
5339 _parent = _parent->fParent;
5340 }
5341 return out.c_str();
5342}
5343
5345{
5346 return nll(_data, *xRooFit::createNLLOptions());
5347}
5348
5349xRooNLLVar xRooNode::nll(const xRooNode &_data, std::initializer_list<RooCmdArg> nllOpts) const
5350{
5352 for (auto &i : nllOpts)
5353 l.Add(const_cast<RooCmdArg *>(&i));
5354 return nll(_data, l);
5355}
5356
5357xRooNLLVar xRooNode::nll(const xRooNode &_data, const RooLinkedList &opts) const
5358{
5359
5360 if (!get<RooAbsPdf>())
5361 throw std::runtime_error(TString::Format("%s is not a pdf", GetName()));
5362
5363 // if simultaneous and any channels deselected then reduce and return
5364 if (get<RooSimultaneous>()) {
5365 std::string selected;
5366 bool hasDeselected = false;
5367 for (auto c : bins()) {
5368 if (!c->get<RooAbsReal>()->isSelectedComp()) {
5369 hasDeselected = true;
5370 } else {
5371 TString cName(c->GetName());
5372 cName = cName(cName.Index('=') + 1, cName.Length());
5373 if (!selected.empty())
5374 selected += ",";
5375 selected += cName.Data();
5376 }
5377 }
5378 if (hasDeselected)
5379 return reduced(selected).nll(_data, opts);
5380 }
5381
5382 if (!_data.get<RooAbsData>()) {
5383 // use node name to find dataset and recall
5384 auto _d = strlen(_data.GetName()) ? datasets().find(_data.GetName()) : nullptr;
5385 if (strlen(_data.GetName()) == 0) {
5386 // create the EXPECTED (asimov) dataset with the observables
5387 auto asi = xRooFit::generateFrom(*get<RooAbsPdf>(),
5388 std::dynamic_pointer_cast<const RooFitResult>(fitResult().fComp), true);
5389 _d = std::make_shared<xRooNode>(asi.first, *this);
5390 _d->emplace_back(
5391 std::make_shared<xRooNode>(".globs", std::const_pointer_cast<RooAbsCollection>(asi.second), *_d));
5392 } else if (!_d) {
5393 throw std::runtime_error(TString::Format("Cannot find dataset %s", _data.GetName()));
5394 }
5395 return nll(*_d, opts);
5396 }
5397
5398 auto _globs = _data.globs(); // keep alive because may own the globs
5399
5400 auto _opts = std::shared_ptr<RooLinkedList>(new RooLinkedList, [](RooLinkedList *l) {
5401 if (l)
5402 l->Delete();
5403 delete l;
5404 });
5405 RooArgSet _globsSet(_globs.argList());
5406 _opts->Add(RooFit::GlobalObservables(_globsSet).Clone());
5407 if (GetRange() && strlen(GetRange()))
5408 _opts->Add(RooFit::Range(GetRange()).Clone());
5409
5410 // copy over opts ... need to clone each so can safely delete when _opts destroyed
5411 for (int i = 0; i < opts.GetSize(); i++) {
5412 if (strlen(opts.At(i)->GetName()) == 0)
5413 continue; // skipping "none" cmds
5414 if (strcmp(opts.At(i)->GetName(), "GlobalObservables") == 0) {
5415 // maybe warn here?
5416 } else {
5417 _opts->Add(opts.At(i)->Clone(nullptr)); // nullptr needed because accessing Clone via TObject base class puts
5418 // "" instead, so doesnt copy names
5419 }
5420 }
5421
5422 // use shared_ptr method so NLLVar will take ownership of datasets etc if created above
5423 // snapshots the globs out of the nllOpts (see specific constructor of xRooNLLVar)
5424 auto out = xRooFit::createNLL(std::dynamic_pointer_cast<RooAbsPdf>(fComp),
5425 std::dynamic_pointer_cast<RooAbsData>(_data.fComp), *_opts);
5426 return out;
5427}
5428
5429// xRooNode xRooNode::fitTo(const xRooNode& _data) const {
5430//
5431//
5432// auto _pdf = get<RooAbsPdf>();
5433// if (!_pdf) throw std::runtime_error("Not a pdf");
5434//
5435// auto _globs = _data.globs(); // keep alive because may own the globs
5436// RooArgSet globsSet(_globs.argList());
5437//
5438// std::shared_ptr<RooSimultaneous> newPdf;
5439// if(auto s = get<RooSimultaneous>(); s) {
5440// auto rangeName = GetRange();
5441// if (rangeName) {
5442// // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
5443// std::vector<TString> chanPatterns;
5444// TStringToken pattern(rangeName, ",");
5445// while (pattern.NextToken()) {
5446// chanPatterns.emplace_back(pattern);
5447// }
5448// auto& _cat = const_cast<RooAbsCategoryLValue&>(s->indexCat());
5449// newPdf = std::make_shared<RooSimultaneous>(TString::Format("%s_reduced",GetName()),"Reduced model",_cat);
5450// for(auto& c : variations()) {
5451// TString cName(c->GetName());
5452// cName = cName(cName.Index('=')+1,cName.Length());
5453// _cat.setLabel(cName);
5454// bool matchAny=false;
5455// for(auto& p : chanPatterns) {
5456// if (cName.Contains(TRegexp(p,true))) { matchAny=true; break; }
5457// if (_cat.hasRange(p) && _cat.inRange(p)) { matchAny=true; break; }
5458// }
5459// if(matchAny) {
5460// newPdf->addPdf( *c->get<RooAbsPdf>(), cName );
5461// }
5462// }
5463// RooFitResultTree t(newPdf->GetName(),"",*newPdf);
5464// auto _fr = std::const_pointer_cast<RooFitResult>(t.fitTo(_data.get<RooAbsData>(), &globsSet));
5465// xRooNode parent(_data.GetName(),nullptr,*this);
5466// xRooNode out(_fr->GetName(),/*acquire(_fr)*/ _fr,parent);
5467// // do full propagation by 'checking' the fr ...
5468// out.Checked(&out,true);
5469// return out;
5470// }
5471// }
5472//
5473//
5474//
5475// std::string treeName = TString::Format("fits_%s",GetName()).Data();
5476//
5477// auto _frt = getObject<TTree>(treeName); // get existing frt
5478//
5479// std::shared_ptr<RooFitResultTree> t;
5480// if (_frt) {
5481// t = std::make_shared<RooFitResultTree>(_frt.get());
5482// } else {
5483// t = std::make_shared<RooFitResultTree>(treeName.c_str(),"",*_pdf);
5484// }
5485// //t->SetProgress(true);
5486// auto _fr = std::const_pointer_cast<RooFitResult>(t->fitTo(_data.get<RooAbsData>(), &globsSet));
5487//
5488//
5489//
5490// /*
5491// obs().argList() = s; // sets global observables to their values
5492// auto _fr =
5493// std::shared_ptr<RooFitResult>(_pdf->fitTo(*_data->get<RooAbsData>(),RooFit::GlobalObservables(s),RooFit::Offset(true),RooFit::Save()));
5494// _fr->SetName(TUUID().AsString());
5495// // restore parameters before returning
5496// *std::unique_ptr<RooArgSet>(_pdf->getDependents(_fr->floatParsFinal())) = _fr->floatParsInit();
5497// */
5498//
5499// //_fr->SetTitle(TString::Format("%s;%s",GetName(),datasetName));
5500// if (!_frt) {
5501// t =
5502// std::make_shared<RooFitResultTree>(std::dynamic_pointer_cast<TTree>(const_cast<xRooNode*>(this)->acquire(t->fTree)).get());
5503// }
5504// xRooNode parent(_data.GetName(),nullptr,xRooNode(t,*this));
5505// xRooNode out(_fr->GetName(),/*acquire(_fr)*/ _fr,parent);
5506// // do full propagation by 'checking' the fr ...
5507// out.Checked(&out,true);
5508// return out;
5509// }
5510
5511std::shared_ptr<xRooNode> xRooNode::parentPdf() const
5512{
5513 // find first parent that is a pdf
5514 auto out = fParent;
5515 while (out && !out->get<RooAbsPdf>()) {
5516 out = out->fParent;
5517 }
5518 return out;
5519}
5520
5521xRooNode xRooNode::reduced(const std::string &_range) const
5522{
5523 auto rangeName = (_range.empty()) ? GetRange() : _range;
5524 if (!rangeName.empty()) {
5525 std::vector<TString> patterns;
5526 TStringToken pattern(rangeName, ",");
5527 while (pattern.NextToken()) {
5528 patterns.emplace_back(pattern);
5529 }
5530 if (auto s = get<RooSimultaneous>(); s) {
5531 // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
5532 auto &_cat = const_cast<RooAbsCategoryLValue &>(s->indexCat());
5533 auto newPdf =
5534 std::make_shared<RooSimultaneous>(TString::Format("%s_reduced", GetName()), "Reduced model", _cat);
5535 for (auto &c : bins()) {
5536 TString cName(c->GetName());
5537 cName = cName(cName.Index('=') + 1, cName.Length());
5538 _cat.setLabel(cName);
5539 bool matchAny = false;
5540 for (auto &p : patterns) {
5541 if (cName.Contains(TRegexp(p, true))) {
5542 matchAny = true;
5543 break;
5544 }
5545 if (_cat.hasRange(p) && _cat.inRange(p)) {
5546 matchAny = true;
5547 break;
5548 }
5549 }
5550 if (matchAny) {
5551 newPdf->addPdf(*c->get<RooAbsPdf>(), cName);
5552 }
5553 }
5554 return xRooNode(newPdf, fParent);
5555 } else if (!components().empty()) {
5556 // create a new obj and remove non-matching components
5557 xRooNode out(std::shared_ptr<TObject>(get()->Clone(TString::Format("%s_reduced", get()->GetName()))), fParent);
5558 // go through components and remove any that don't match pattern
5559 std::vector<TObject *> funcs; // to be removed
5560 for (auto &c : out.components()) {
5561 bool matchAny = false;
5562 for (auto &p : patterns) {
5563 if (TString(c->GetName()).Contains(TRegexp(p, true))) {
5564 matchAny = true;
5565 break;
5566 }
5567 }
5568 if (!matchAny)
5569 funcs.push_back(c->get());
5570 }
5571 for (auto &c : funcs)
5572 out.Remove(*c);
5573 out.browse();
5574 return out;
5575 } else if (auto fr = get<RooFitResult>()) {
5576 // reduce the fit result by moving unselected float pars into the constPars list and dropping their covariances
5577 xRooNode out(std::shared_ptr<TObject>(fr->Clone(TString::Format("%s_reduced", fr->GetName()))), fParent);
5578 fr = out.get<RooFitResult>();
5579 RooArgList _pars = fr->floatParsFinal();
5580 RooArgList _remPars;
5581 for (auto c : _pars) {
5582 bool matchAny = false;
5583 for (auto &p : patterns) {
5584 if (TString(c->GetName()).Contains(TRegexp(p, true))) {
5585 matchAny = true;
5586 break;
5587 }
5588 }
5589 if (!matchAny) {
5590 _remPars.add(*c);
5591 }
5592 }
5593 _pars.remove(_remPars, true);
5594
5595 auto _tmp = fr->reducedCovarianceMatrix(_pars);
5596 int covQualBackup = fr->covQual();
5597 fr->setCovarianceMatrix(_tmp);
5598 fr->setCovQual(covQualBackup);
5599 const_cast<RooArgList&>(fr->floatParsFinal()).remove(_remPars, true);
5600 return out;
5601
5602 } else if (!get() || get<RooArgList>()) {
5603 // filter the children ....
5604 xRooNode out(get<RooArgList>() ? std::make_shared<RooArgList>() : std::shared_ptr<TObject>(nullptr), fParent);
5605 for (auto c : *this) {
5606 bool matchAny = false;
5607 for (auto &p : patterns) {
5608 if (TString(c->GetName()).Contains(TRegexp(p, true))) {
5609 matchAny = true;
5610 break;
5611 }
5612 }
5613 if (matchAny) {
5614 out.push_back(c);
5615 if (auto l = out.get<RooArgList>()) {
5616 l->add(*c->get<RooAbsArg>());
5617 }
5618 }
5619 }
5620 return out;
5621 }
5622 }
5623
5624 return get<RooArgList>() ? xRooNode(std::make_shared<RooArgList>(), fParent) : *this;
5625}
5626
5627// xRooNode xRooNode::generate(bool expected) const {
5628//
5629// auto fr = fitResult();
5630// auto _fr = fr.get<RooFitResult>();
5631//
5632// auto _pdf = (get<RooAbsPdf>()) ? std::shared_ptr<const xRooNode>(this, [](const xRooNode*){}) : parentPdf();
5633// if (!_pdf) {
5634// throw std::runtime_error("Could not find pdf");
5635// }
5636//
5637// std::shared_ptr<RooDataTree> t;
5638//
5639// std::shared_ptr<RooSimultaneous> newPdf;
5640// if(auto s = _pdf->get<RooSimultaneous>(); s) {
5641// auto rangeName = GetRange();
5642// if (rangeName) {
5643// // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
5644// std::vector<TString> chanPatterns;
5645// TStringToken pattern(rangeName, ",");
5646// while (pattern.NextToken()) {
5647// chanPatterns.emplace_back(pattern);
5648// }
5649// auto& _cat = const_cast<RooAbsCategoryLValue&>(s->indexCat());
5650// newPdf = std::make_shared<RooSimultaneous>(TString::Format("%s_reduced",GetName()),"Reduced model",_cat);
5651// for(auto& c : _pdf->variations()) {
5652// TString cName(c->GetName());
5653// cName = cName(cName.Index('=')+1,cName.Length());
5654// _cat.setLabel(cName);
5655// bool matchAny=false;
5656// for(auto& p : chanPatterns) {
5657// if (cName.Contains(TRegexp(p,true))) { matchAny=true; break; }
5658// if (_cat.hasRange(p) && _cat.inRange(p)) { matchAny=true; break; }
5659// }
5660// if(matchAny) {
5661// newPdf->addPdf( *c->get<RooAbsPdf>(), cName );
5662// }
5663// }
5664// t = std::make_shared<RooDataTree>(newPdf->GetName(),"",*newPdf);
5665// RooArgSet s1(_pdf->obs().argList());
5666// RooArgSet s2(_pdf->globs().argList());s1.remove(s2);
5667// t->SetObservables(&s1,&s2);
5668// auto _data = t->generate(_fr,expected);
5669//
5670// xRooNode parent(_fr ? _fr->GetName() : "unknown",nullptr,xRooNode(t,*this));
5671// xRooNode out(_data.first->GetName(),/*acquire(_fr)*/ _data.first,parent);
5672// out.emplace_back(std::make_shared<xRooNode>(".globs",std::const_pointer_cast<RooArgSet>(_data.second),out));
5673// return out;
5674// }
5675// }
5676//
5677//
5678// std::string treeName = TString::Format("gen_%s",_pdf->GetName()).Data();
5679//
5680// auto _frt = getObject<TTree>(treeName); // get existing frt
5681//
5682//
5683// if (_frt) {
5684// t = std::make_shared<RooDataTree>(_frt.get());
5685// } else {
5686// t = std::make_shared<RooDataTree>(treeName.c_str(),"",*_pdf->get<RooAbsPdf>());
5687// RooArgSet s1(_pdf->obs().argList());
5688// RooArgSet s2(_pdf->globs().argList());s1.remove(s2);
5689// t->SetObservables(&s1,&s2);
5690// }
5691// auto _data = t->generate(_fr,expected);
5692// if (!_frt) {
5693// t =
5694// std::make_shared<RooDataTree>(std::dynamic_pointer_cast<TTree>(const_cast<xRooNode*>(this)->acquire(t->fTree)).get());
5695// }
5696// xRooNode parent(_fr ? _fr->GetName() : "unknown",nullptr,xRooNode(t,*this));
5697// xRooNode out(_data.first->GetName(),/*acquire(_fr)*/ _data.first,parent);
5698// out.emplace_back(std::make_shared<xRooNode>(".globs",std::const_pointer_cast<RooArgSet>(_data.second),out));
5699// return out;
5700// }
5701
5702class PdfWrapper : public RooAbsPdf {
5703public:
5704 PdfWrapper(RooAbsPdf &f, RooAbsReal *coef, bool expEvMode = false)
5705 : RooAbsPdf(Form("exp_%s", f.GetName())), fFunc("func", "func", this, f), fCoef("coef", "coef", this)
5706 {
5707 if (coef)
5708 fCoef.setArg(*coef);
5709 fExpectedEventsMode = expEvMode;
5710 }
5711 virtual ~PdfWrapper(){};
5712 PdfWrapper(const PdfWrapper &other, const char *name = 0)
5713 : RooAbsPdf(other, name), fFunc("func", this, other.fFunc), fCoef("coef", this, other.fCoef),
5714 fExpectedEventsMode(other.fExpectedEventsMode)
5715 {
5716 }
5717 virtual TObject *clone(const char *newname) const override { return new PdfWrapper(*this, newname); }
5718 Bool_t isBinnedDistribution(const RooArgSet &obs) const override { return fFunc->isBinnedDistribution(obs); }
5719 std::list<Double_t> *binBoundaries(RooAbsRealLValue &obs, Double_t xlo, Double_t xhi) const override
5720 {
5721 return fFunc->binBoundaries(obs, xlo, xhi);
5722 }
5723
5724 double evaluate() const override
5725 {
5726 return (fExpectedEventsMode ? 1. : fFunc) *
5727 (dynamic_cast<RooAbsPdf *>(fFunc.absArg())->expectedEvents(_normSet)) * (fCoef.absArg() ? fCoef : 1.);
5728 }
5729
5730 // faster than full evaluation because doesnt make the integral dependent on the full expression
5732 {
5733
5734 // Strip out parameters with zero error
5735 RooArgList fpf_stripped;
5737 RooRealVar *frv;
5738 while ((frv = (RooRealVar *)fi.next())) {
5739 if (frv->getError() > 1e-20) {
5740 fpf_stripped.add(*frv);
5741 }
5742 }
5743
5744 // Clone self for internal use
5745 RooAbsReal *cloneFunc = (RooAbsReal *)fFunc.absArg()->cloneTree();
5746 RooAbsPdf *clonePdf = dynamic_cast<RooAbsPdf *>(cloneFunc);
5747 std::unique_ptr<RooArgSet> errorParams{cloneFunc->getObservables(fpf_stripped)};
5748
5749 std::unique_ptr<RooArgSet> nset =
5750 nset_in.empty() ? std::unique_ptr<RooArgSet>{cloneFunc->getParameters(*errorParams)} : std::unique_ptr<RooArgSet>{cloneFunc->getObservables(nset_in)};
5751
5752 // Make list of parameter instances of cloneFunc in order of error matrix
5753 RooArgList paramList;
5754 const RooArgList &fpf = fpf_stripped;
5755 std::vector<int> fpf_idx;
5756 for (Int_t i = 0; i < fpf.getSize(); i++) {
5757 RooAbsArg *par = errorParams->find(fpf[i].GetName());
5758 if (par) {
5759 paramList.add(*par);
5760 fpf_idx.push_back(i);
5761 }
5762 }
5763
5764 std::vector<Double_t> plusVar, minusVar;
5765
5766 // Create vector of plus,minus variations for each parameter
5767 TMatrixDSym V(paramList.getSize() == fr.floatParsFinal().getSize() ? fr.covarianceMatrix()
5768 : fr.reducedCovarianceMatrix(paramList));
5769
5770 for (Int_t ivar = 0; ivar < paramList.getSize(); ivar++) {
5771
5772 RooRealVar &rrv = (RooRealVar &)fpf[fpf_idx[ivar]];
5773
5774 Double_t cenVal = rrv.getVal();
5775 Double_t errVal = sqrt(V(ivar, ivar));
5776
5777 // Make Plus variation
5778 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal + errVal);
5779 plusVar.push_back((fExpectedEventsMode ? 1. : cloneFunc->getVal(&*nset)) *
5780 (clonePdf ? clonePdf->expectedEvents(&*nset) : 1.));
5781
5782 // Make Minus variation
5783 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal - errVal);
5784 minusVar.push_back((fExpectedEventsMode ? 1. : cloneFunc->getVal(&*nset)) *
5785 (clonePdf ? clonePdf->expectedEvents(&*nset) : 1.));
5786
5787 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal);
5788 }
5789
5790 TMatrixDSym C(paramList.getSize());
5791 std::vector<double> errVec(paramList.getSize());
5792 for (int i = 0; i < paramList.getSize(); i++) {
5793 errVec[i] = sqrt(V(i, i));
5794 for (int j = i; j < paramList.getSize(); j++) {
5795 C(i, j) = V(i, j) / sqrt(V(i, i) * V(j, j));
5796 C(j, i) = C(i, j);
5797 }
5798 }
5799
5800 // Make vector of variations
5801 TVectorD F(plusVar.size());
5802 for (unsigned int j = 0; j < plusVar.size(); j++) {
5803 F[j] = (plusVar[j] - minusVar[j]) / 2;
5804 }
5805
5806 // Calculate error in linear approximation from variations and correlation coefficient
5807 Double_t sum = F * (C * F);
5808
5809 delete cloneFunc;
5810
5811 return sqrt(sum);
5812 }
5813
5814private:
5817 bool fExpectedEventsMode = false;
5818};
5819
5820const xRooNode *runningNode = nullptr;
5822
5824{
5825 std::cout << "Got signal " << signum << std::endl;
5826 if (signum == SIGINT) {
5827 std::cout << "Keyboard interrupt while building histogram" << std::endl;
5828 // TODO: create a global mutex for this
5829 runningNode->fInterrupted = true;
5830 } else {
5831 gOldHandlerr(signum);
5832 }
5833}
5834
5836{
5837 auto _doSterilize = [](RooAbsArg *obj) {
5838
5839 for(int i=0;i<obj->numCaches();i++) {
5840 if(auto cache = dynamic_cast<RooObjCacheManager*>(obj->getCache(i))) {
5841 cache->reset();
5842 }
5843 }
5844 if (RooAbsPdf *p = dynamic_cast<RooAbsPdf *>(obj); p) {
5845 p->setNormRange(nullptr);
5846 }
5847
5848
5849
5850// if (RooAbsPdf *arg = dynamic_cast<RooAbsPdf *>(obj); arg) {
5851// arg->_normMgr.reset();
5852// arg->_normSet = nullptr;
5853// if (RooProdPdf *p = dynamic_cast<RooProdPdf *>(arg); p) {
5854// p->_cacheMgr.reset();
5855// p->setNormRange(0);
5856// } else if (auto p2 = dynamic_cast<RooRealSumPdf *>(arg); p2) {
5857// p2->_normIntMgr.reset();
5858// } else if (auto p3 = dynamic_cast<RooSimultaneous *>(arg); p3) {
5859// p3->_partIntMgr.reset();
5860// }
5861// // std::cout << "cleaned " << arg->GetName() << std::endl;
5862//
5863// } else if (auto p = dynamic_cast<RooProduct *>(obj); p) {
5864// p->_cacheMgr.reset();
5865// } else if (auto p2 = dynamic_cast<PiecewiseInterpolation *>(obj); p2) {
5866// p2->_normIntMgr.reset();
5867// } else if (auto p3 = dynamic_cast<ParamHistFunc *>(obj); p3) {
5868// p3->_normIntMgr.reset();
5869// }
5870 if (obj) {
5871 obj->setValueDirty();
5872 }
5873 };
5874 if (auto w = get<RooWorkspace>(); w) {
5875 // sterilizing all nodes
5876 for (auto &c : w->components()) {
5877 _doSterilize(c);
5878 }
5879 return;
5880 }
5881 // recursive through all clients and sterlize their normalization caches
5882 std::function<void(RooAbsArg *)> func;
5883 func = [&](RooAbsArg *a) {
5884 if (!a)
5885 return;
5886 auto itr = a->clientIterator();
5887 TObject *obj;
5888 while ((obj = itr->Next())) {
5889 _doSterilize(dynamic_cast<RooAbsArg *>(obj));
5890 if (RooAbsArg *arg = dynamic_cast<RooAbsArg *>(obj); arg) {
5891 func(arg);
5892 }
5893 }
5894 delete itr;
5895 };
5896 func(dynamic_cast<RooAbsArg *>(get()));
5897 _doSterilize(dynamic_cast<RooAbsArg *>(get())); // sterilize self
5898}
5899
5900TH1 *xRooNode::BuildHistogram(RooAbsLValue *v, bool empty, bool errors, int binStart, int binEnd) const
5901{
5902 auto rar = get<RooAbsReal>();
5903 if (!rar)
5904 return nullptr;
5905
5906 TObject *vv = rar;
5907
5908 auto t = TH1::AddDirectoryStatus();
5909 TH1::AddDirectory(false);
5910 TH1 *h = nullptr;
5911 if (!v) {
5912 if (auto _ax = GetXaxis())
5913 v = dynamic_cast<RooAbsLValue *>(_ax->GetParent());
5914 if (v)
5915 vv = dynamic_cast<TObject *>(v);
5916 else {
5917 // make a single-bin histogram of just this value
5918 h = new TH1D(rar->GetName(), rar->GetTitle(), 1, 0, 1);
5919 h->GetXaxis()->SetBinLabel(1, rar->GetName());
5920 h->GetXaxis()->SetName(rar->GetName());
5921 }
5922 }
5923
5924 auto x = dynamic_cast<RooRealVar *>(v);
5925 if (x) {
5926 if (x == rar) {
5927 // self histogram ...
5928 h = new TH1D(rar->GetName(), rar->GetTitle(), 1, 0, 1);
5929 h->Sumw2();
5930 h->GetXaxis()->SetBinLabel(1, rar->GetName());
5931 h->SetBinContent(1, rar->getVal());
5932 if (x->hasError())
5933 h->SetBinError(1, x->getError());
5934 h->SetMaximum(x->hasMax() ? x->getMax()
5935 : (h->GetBinContent(1) + std::max(std::abs(h->GetBinContent(1) * 0.1), 50.)));
5936 h->SetMinimum(x->hasMin() ? x->getMin()
5937 : (h->GetBinContent(1) - std::max(std::abs(h->GetBinContent(1) * 0.1), 50.)));
5938 h->GetXaxis()->SetName(dynamic_cast<TObject *>(v)->GetName());
5939 return h;
5940 }
5941 auto _ax = GetXaxis();
5942 TString binningName = (_ax && _ax->GetParent() == x) ? _ax->GetName() : rar->getStringAttribute("binning");
5943 if (binningName == "")
5944 binningName = rar->GetName();
5945 if (x->hasBinning(binningName)) {
5946 if (x->getBinning(binningName).isUniform()) {
5947 h = new TH1D(rar->GetName(), rar->GetTitle(), x->numBins(binningName) <= 0 ? 100 : x->numBins(binningName),
5948 x->getMin(binningName), x->getMax(binningName));
5949 } else {
5950 h = new TH1D(rar->GetName(), rar->GetTitle(), x->numBins(binningName), x->getBinning(binningName).array());
5951 }
5952 h->GetXaxis()->SetTitle(x->getBinning(binningName).GetTitle());
5953 } else if (auto _boundaries =
5954 _or_func(/*rar->plotSamplingHint(*x,x->getMin(),x->getMax())*/ (std::list<double> *)(nullptr),
5955 rar->binBoundaries(*x, -std::numeric_limits<double>::infinity(),
5956 std::numeric_limits<double>::infinity()));
5957 _boundaries) {
5958 std::vector<double> _bins;
5959 for (auto &b : *_boundaries) {
5960 if (_bins.empty() || std::abs(_bins.back() - b) > 1e-5 * _bins.back())
5961 _bins.push_back(b);
5962 } // found sometimes get virtual duplicates in the binning
5963 h = new TH1D(rar->GetName(), rar->GetTitle(), _bins.size() - 1, &_bins[0]);
5964 delete _boundaries;
5965 } else if (!x->hasMax() || !x->hasMin()) {
5966 // use current value of x to estimate range with
5967 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(), x->getVal() * 0.2, x->getVal() * 5);
5968 } else {
5969 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(), x->getBinning().array());
5970 }
5971
5972 } else if (!h) {
5973 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(rar->GetName()), 0, v->numBins(rar->GetName()));
5974 }
5976 h->Sumw2();
5977 if (v)
5978 h->GetXaxis()->SetName(dynamic_cast<TObject *>(v)->GetName()); // WARNING: messes up display of bin labels
5979 if (auto s = style(); s) {
5980 static_cast<TAttLine &>(*h) = *s;
5981 static_cast<TAttFill &>(*h) = *s;
5982 static_cast<TAttMarker &>(*h) = *s;
5983 }
5984 if (strlen(h->GetXaxis()->GetTitle()) == 0)
5985 h->GetXaxis()->SetTitle(vv->GetTitle());
5986 auto p = dynamic_cast<RooAbsPdf *>(rar);
5987
5988 RooFitResult *fr = nullptr;
5989 if (errors) {
5990 fr = dynamic_cast<RooFitResult *>(fitResult().get()->Clone());
5991 if (!GETDMP(fr,_finalPars)) {
5993 }
5994
5995
5996 /// Oct2022: No longer doing this because want to allow fitResult to be used to get partial error
5997 // // need to add any floating parameters not included somewhere already in the fit result ...
5998 // RooArgList l;
5999 // for(auto& p : pars()) {
6000 // auto vv = p->get<RooRealVar>();
6001 // if (!vv) continue;
6002 // if (vv == dynamic_cast<RooRealVar*>(v)) continue;
6003 // if (vv->isConstant()) continue;
6004 // if (fr->floatParsFinal().find(vv->GetName())) continue;
6005 // if (fr->_constPars && fr->_constPars->find(vv->GetName())) continue;
6006 // l.add(*vv);
6007 // }
6008 //
6009 // if (!l.empty()) {
6010 // RooArgList l2; l2.addClone(fr->floatParsFinal());
6011 // l2.addClone(l);
6012 // fr->setFinalParList(l2);
6013 // }
6014
6015 TMatrixTSym<Double_t> *prevCov = static_cast<TMatrixTSym<Double_t>*>(GETDMP(fr,_VM));
6016
6017 if (!prevCov || size_t(fr->covarianceMatrix().GetNcols()) < fr->floatParsFinal().size()) {
6018 TMatrixDSym cov(fr->floatParsFinal().getSize());
6019 if (prevCov) {
6020 for (int i = 0; i < prevCov->GetNcols(); i++) {
6021 for (int j = 0; j < prevCov->GetNrows(); j++) {
6022 cov(i, j) = (*prevCov)(i, j);
6023 }
6024 }
6025 }
6026 int i = 0;
6027 for (auto &p2 : fr->floatParsFinal()) {
6028 if (!prevCov || i >= prevCov->GetNcols()) {
6029 cov(i, i) = pow(dynamic_cast<RooRealVar *>(p2)->getError(), 2);
6030 }
6031 i++;
6032 }
6033 int covQualBackup = fr->covQual();
6034 fr->setCovarianceMatrix(cov);
6035 fr->setCovQual(covQualBackup);
6036 }
6037
6038 // need to remove v from result as we are plotting as function of v
6039 if (auto _p = fr->floatParsFinal().find(dynamic_cast<TObject *>(v)->GetName()); _p) {
6040 RooArgList _pars = fr->floatParsFinal();
6041 _pars.remove(*_p, true);
6042 auto _tmp = fr->reducedCovarianceMatrix(_pars);
6043 int covQualBackup = fr->covQual();
6044 fr->setCovarianceMatrix(_tmp);
6045 fr->setCovQual(covQualBackup);
6046 const_cast<RooArgList&>(fr->floatParsFinal()).remove(*_p, true);
6047 }
6048 }
6049
6050 RooArgSet normSet;
6051 if (v)
6052 normSet.add(*dynamic_cast<RooAbsArg *>(v));
6053
6054 if (!empty) {
6055 if (binEnd == 0)
6056 binEnd = h->GetNbinsX();
6057 bool needBinWidth = false;
6058 // may have MULTIPLE coefficients for the same pdf!
6059 auto _coefs = coefs();
6060 if ((p || !_coefs.empty() || rar->getAttribute("density")) && x) {
6061 // pdfs of samples embedded in a sumpdf (aka have a coef) will convert their density value to a content
6062 needBinWidth = true;
6063 }
6064 std::unique_ptr<RooArgSet> snap(normSet.snapshot());
6065 TStopwatch timeIt;
6066 std::vector<double> lapTimes;
6067 bool warned = false;
6068 for (int i = std::max(1, binStart); i <= std::min(h->GetNbinsX(), binEnd); i++) {
6069 timeIt.Start(true);
6070 if (x)
6071 x->setVal(h->GetBinCenter(i));
6072 else if (v)
6073 v->setBin(i - 1);
6074 double r = /*(p && p->selfNormalized())*/ rar->getVal(normSet);
6075 if (r && !_coefs.empty()) {
6076 r *= _coefs.get<RooAbsReal>()->getVal(normSet);
6077 }
6078 if (needBinWidth) {
6079 r *= h->GetBinWidth(i);
6080 }
6081 if (p && p->canBeExtended()) {
6082 r *= (p->expectedEvents(normSet));
6083 } // do in here in case dependency on var
6084 h->SetBinContent(i, r);
6085
6086 if (errors) {
6087 double res;
6088 if (p) {
6089 // std::cout << "computing error of :" << h->GetBinCenter(i) << std::endl;
6090 // //fr->floatParsFinal().Print(); fr->covarianceMatrix().Print();
6091 res = PdfWrapper(*p, _coefs.get<RooAbsReal>()).getSimplePropagatedError(*fr, normSet);
6092#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
6093 // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
6094 p->_normSet = nullptr;
6095#endif
6096 } else {
6097 res = rar->getPropagatedError(*fr, normSet);
6098 // TODO: What if coef has error? - probably need a FuncWrapper class
6099 if (auto c = _coefs.get<RooAbsReal>(); c) {
6100 res *= c->getVal(normSet);
6101 }
6102 }
6103 if (needBinWidth) {
6104 res *= h->GetBinWidth(i);
6105 }
6106 h->SetBinError(i, res);
6107 }
6108 timeIt.Stop();
6109 lapTimes.push_back(timeIt.RealTime());
6110 double time_estimate =
6111 (lapTimes.size() > 1)
6112 ? (h->GetNbinsX() * (std::accumulate(lapTimes.begin() + 1, lapTimes.end(), 0.) / (lapTimes.size() - 1)))
6113 : 0.;
6114 if (!warned && (lapTimes.at(0) > 10 || (lapTimes.size() > 2 && time_estimate > 60.))) {
6115 TTimeStamp t2;
6116 t2.Add(time_estimate);
6117 Warning("BuildHistogram", "Building this histogram will take until %s", t2.AsString());
6118 if (errors) {
6119 // install interrupt handler
6120 runningNode = this;
6121 gOldHandlerr = signal(SIGINT, buildHistogramInterrupt);
6122 }
6123 warned = true;
6124 }
6125 if (fInterrupted) {
6126 if (errors) {
6127 Warning("BuildHistogram", "Skipping errors for remaining bins");
6128 errors = false;
6129 }
6130 fInterrupted = false;
6131 }
6132 }
6133 if (gOldHandlerr) {
6134 signal(SIGINT, gOldHandlerr);
6135 gOldHandlerr = 0;
6136 }
6137 normSet = *snap;
6138 }
6139
6140 if (!p) {
6141 h->GetYaxis()->SetTitle(rar->getStringAttribute("units"));
6142 } else if (p->canBeExtended()) {
6143 h->GetYaxis()->SetTitle("Events");
6144 } else {
6145 h->GetYaxis()->SetTitle("Probability Mass");
6146 }
6147 h->GetYaxis()->SetMaxDigits(3);
6148
6149 if (errors)
6150 delete fr;
6151
6152 return h;
6153}
6154
6155double xRooNode::GetBinData(int bin, const char *dataName)
6156{
6157 auto node = datasets().find(dataName);
6158 if (!node)
6159 return std::numeric_limits<double>::quiet_NaN();
6160 return node->GetBinContent(bin);
6161}
6162
6163std::vector<double> xRooNode::GetBinContents(int binStart, int binEnd) const
6164{
6165 if (fBinNumber != -1) {
6166 if (binStart != binEnd || !fParent) {
6167 throw std::runtime_error(TString::Format("%s is a bin - only has one value", GetName()));
6168 }
6169 return fParent->GetBinContents(fBinNumber, fBinNumber);
6170 }
6171 std::vector<double> out;
6172 if (get<RooAbsData>()) {
6173 auto g = BuildGraph(nullptr, true /*include points for zeros*/);
6174 if (!g)
6175 return out;
6176 for (int i = binStart - 1; i < g->GetN() && (binEnd == 0 || i < binEnd); i++) {
6177 out.push_back(g->GetPointY(i));
6178 }
6179 delete g;
6180 return out;
6181 }
6182
6183 auto h = BuildHistogram(nullptr, false, false, binStart, binEnd);
6184 if (!h) {
6185 throw std::runtime_error(TString::Format("%s has no content", GetName()));
6186 }
6187 if (binEnd == 0)
6188 binEnd = h->GetNbinsX();
6189 for (int i = binStart; i <= binEnd; i++) {
6190 out.push_back(h->GetBinContent(i));
6191 }
6192 delete h;
6193 return out;
6194}
6195
6197{
6198 if (auto a = get<RooAbsArg>(); a) {
6199 // go through servers looking for 'main' thing
6200 for (auto &l : a->servers()) {
6201 if (l->getAttribute("MAIN_MEASUREMENT") || l->InheritsFrom("RooRealSumPdf") || l->InheritsFrom("RooAddPdf")) {
6202 return xRooNode(*l, *this);
6203 }
6204 }
6205 // the main child of a RooProduct is one that has the same name (/alias) as the product (except if is a bin
6206 // factor)
6207 if (a->IsA() == RooProduct::Class() && fBinNumber == -1) {
6208 for (auto &l : factors()) {
6209 if (strcmp(l->GetName(), GetName()) == 0) {
6210 return *l;
6211 }
6212 }
6213 }
6214 }
6215 return xRooNode();
6216}
6217
6219{
6220 if (auto o = get(); o)
6221 o->Inspect();
6222 else
6224}
6225
6227{
6228#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
6229 // reinitialize collide grid because the filling depends on fUxmin and fUxmax (and ymin ymax too)
6230 // and these aren't filled on the first time we do the placement (they init to 0 and 1), but will be filled subsequently
6231 for (int i = 0; i < p->fCGnx; i++) {
6232 for (int j = 0; j < p->fCGny; j++) {
6233 p->fCollideGrid[i + j * p->fCGnx] = kTRUE;
6234 }
6235 }
6236 p->FillCollideGrid(o);
6237 Int_t iw = (int)(p->fCGnx * w);
6238 Int_t ih = (int)(p->fCGny * h);
6239
6240 Int_t nxmax = p->fCGnx - iw - 1 - p->fCGnx * p->GetRightMargin();
6241 Int_t nymax = p->fCGny - ih - 1 - p->fCGny * p->GetTopMargin();
6242
6243 for (Int_t j = nymax; j >= 0; j--) {
6244 for (Int_t i = nxmax; i >= 0; i--) {
6245 if (p->Collide(i, j, iw, ih)) {
6246 continue;
6247 } else {
6248 xl = (Double_t)(i) / (Double_t)(p->fCGnx);
6249 yb = (Double_t)(j) / (Double_t)(p->fCGny);
6250 return kTRUE;
6251 }
6252 }
6253 }
6254 return kFALSE;
6255#else
6256 return p->PlaceBox(o, w, h, xl, yb, "trw");
6257#endif
6258}
6259
6260TLegend *getLegend(bool create = true, bool doPaint = false)
6261{
6262 if (auto p = dynamic_cast<TLegend *>(gPad->GetPrimitive("legend")); p) {
6263 double x, y;
6264 double w = p->GetX2NDC() - p->GetX1NDC(), h = p->GetY2NDC() - p->GetY1NDC();
6265 if (doPaint)
6266 gPad->PaintModified(); //-- slows down x11 so trying to avoid
6267 if (TopRightPlaceBox(dynamic_cast<TPad *>(gPad), p, w, h, x, y)) {
6268 // squash inside the frame ..
6269 //std::cout << gPad->GetName() << ":" << x << " , " << y << " , " << w << " , " << h << std::endl;
6270 x = std::max(x, (gPad->GetLeftMargin() + 0.02));
6271 y = std::max(y, (gPad->GetBottomMargin() + 0.02));
6272 x = std::min(x, (1. - gPad->GetRightMargin() - 0.02) - w);
6273 y = std::min(y, (1. - gPad->GetTopMargin() - 0.02) - h);
6274 h = std::min(h, (1. - gPad->GetTopMargin() - 0.02) - y);
6275 w = std::min(w, (1. - gPad->GetRightMargin() - 0.02) - x);
6276 // std::cout << gPad->GetName() << ":" << x << " , " << y << " , " << h << " , " << w << std::endl;
6277 p->SetX1NDC(x);
6278 p->SetY1NDC(y);
6279 p->SetX2NDC(x + w);
6280 p->SetY2NDC(y + h);
6281 gPad->Modified();
6282 }
6283 return p;
6284 }
6285 // look for a parent pad called 'legend' and create it there if existing
6286 auto p = gPad;
6287 while ((p != p->GetMother()) && (p = p->GetMother())) {
6288 if (auto q = dynamic_cast<TVirtualPad *>(p->GetPrimitive("legend")); q) {
6289 q->Modified();
6290 p = q;
6291 break;
6292 }
6293 }
6294 auto tmpPad = gPad;
6295 TLegend *l = nullptr;
6296 if (p && strcmp(p->GetName(), "legend") == 0) {
6297 if (l = dynamic_cast<TLegend *>(p->GetPrimitive("legend")); l || !create)
6298 return l;
6299 p->cd();
6300 l = new TLegend(gPad->GetLeftMargin(), 1. - gPad->GetTopMargin(), 1. - gPad->GetRightMargin(),
6301 gPad->GetBottomMargin());
6302 } else {
6303 if (!create)
6304 return nullptr;
6305 l = new TLegend(0.6, 1. - gPad->GetTopMargin() - 0.08, 1. - gPad->GetRightMargin(),
6306 1. - gPad->GetTopMargin() - 0.08);
6307 l->SetBorderSize(0);
6308 if (l->GetTextSize()==0) l->SetTextSize(gStyle->GetTitleYSize());
6309 }
6311 // l->SetMargin(0);
6312 l->SetFillStyle(0);
6313 l->SetName("legend");
6314 l->Draw();
6315 l->ConvertNDCtoPad();
6316 tmpPad->cd();
6317 return l;
6318};
6319
6320void addLegendEntry(TObject *o, const char *title, const char *opt)
6321{
6322 auto l = getLegend();
6323 if (!l)
6324 return;
6325 // check for entry already existing with same title
6326 for (auto a : *l->GetListOfPrimitives()) {
6327 if (!strcmp(dynamic_cast<TLegendEntry *>(a)->GetLabel(), title))
6328 return;
6329 }
6330 if (l->GetListOfPrimitives()->GetEntries() > 20)
6331 return; // todo: create an 'other' entry?
6332
6333 l->AddEntry(o, title, opt);
6334 if (auto nObj = l->GetListOfPrimitives()->GetEntries(); nObj > 0) {
6335 // each entry takes up 0.05 ... maximum of N*(N+4) (where N is # cols) before next column
6336 int nn = l->GetNColumns();
6337 nn *= (nn + 4);
6338 if (nObj > 1 && (nObj % nn) == 1) {
6339 l->SetNColumns(l->GetNColumns() + 1);
6340 } else if (nObj <= nn && nObj >= l->GetNColumns() * l->GetNColumns()) {
6341 l->SetY1NDC(l->GetY2NDC() - 0.05 * gPad->GetHNDC() * nObj);
6342 }
6343 }
6344
6345 getLegend(); // to mark modified
6346}
6347
6348// this exists to avoid calling update excessively because it slows down x11 ... but still
6349// need to call update twice if have a legend drawn in order to relocate it.
6351public:
6352 PadRefresher(TVirtualPad *p) : fPad(p) { nExisting++; }
6354 {
6355 if (fPad) {
6356 getLegend(false, true);
6357 fPad->GetCanvas()->Update();
6358 fPad->cd();
6359 }
6360 nExisting--;
6361 }
6362 TVirtualPad *fPad = nullptr;
6363 static int nExisting;
6364};
6365
6367
6369{
6370 if (!get() && !IsFolder())
6371 return;
6372
6373 if (auxFunctions.empty()) {
6374 // add the defaults: Ratio and Signif
6376 "Ratio",
6377 [](double a, double b, double) {
6378 if (a == 0)
6379 return 0.;
6380 if (b == 0 && a == 0)
6381 return 1.;
6382 return a / b;
6383 },
6384 true);
6386 "Signif",
6387 [](double n, double b, double sigma) {
6388 double t0 = 0;
6389 if (sigma <= 0.) {
6390 // use simplified expression ...
6391 t0 = 2. * (((n == 0) ? 0 : n * log(n / b)) - (n - b));
6392 } else {
6393 double sigma2 = sigma * sigma;
6394 double b_hathat = 0.5 * (b - sigma2 + sqrt(pow(b - sigma2, 2) + 4 * n * sigma2));
6395 // double s_hat = n - m;
6396 // double b_hat = m;
6397 t0 = 2. * (((n == 0) ? 0 : n * log(n / b_hathat)) + b_hathat - n + pow(b - b_hathat, 2) / (2. * sigma2));
6398 }
6399 if (t0 < 0)
6400 return 0.; // can happen from numerical precision
6401 return (n >= b) ? sqrt(t0) : -sqrt(t0);
6402 },
6403 false);
6404 }
6405
6406 TString sOpt(opt);
6407 TString sOpt2(sOpt);
6408 sOpt2.ToLower();
6409 TString forceNames = "";
6410 if (sOpt2.Contains("force")) {
6411 // force plots show how much NLL changes wrt to a change of variables
6412 if (get<RooRealVar>() && fParent && fParent->get<RooAbsPdf>()) {
6413 // assume want force of this parameter from the parent pdf
6414 TString ff = sOpt(sOpt2.Index("force"), sOpt2.Index("force") + 5);
6415 sOpt.ReplaceAll(ff, TString::Format("force%s", get()->GetName()));
6416 fParent->Draw(sOpt);
6417 return;
6418 } else if (get<RooAbsPdf>()) {
6419 // extract the parameter(s) to calculate force for
6420 forceNames = sOpt(sOpt2.Index("force") + 5, sOpt2.Length());
6421 sOpt = sOpt(0, sOpt2.Index("force"));
6422 sOpt2 = sOpt2(0, sOpt2.Index("force"));
6423 } else {
6424 throw std::runtime_error("Can only compute forces with PDFs");
6425 }
6426 }
6427 bool hasOverlay = sOpt2.Contains("overlay");
6428 TString overlayName = "";
6429 if (hasOverlay) {
6430 // whatever follows overlay is the variation name
6431 overlayName = sOpt(sOpt2.Index("overlay") + 7, sOpt2.Length());
6432 sOpt = sOpt(0, sOpt2.Index("overlay"));
6433 sOpt2 = sOpt2(0, sOpt2.Index("overlay"));
6434 }
6435 if (sOpt2.Contains("ratio") && !sOpt2.Contains("auxratio"))
6436 sOpt += "auxRatio";
6437 if (sOpt2.Contains("significance") && !sOpt2.Contains("auxsignif"))
6438 sOpt += "auxSignif";
6439
6440 std::string auxPlotTitle = "";
6441 for (auto &[k, _] : auxFunctions) {
6442 if (sOpt.Contains(TString::Format("aux%s", k.c_str()))) {
6443 auxPlotTitle = k;
6444 }
6445 sOpt.ReplaceAll(TString::Format("aux%s", k.c_str()), "");
6446 }
6447
6448 sOpt.ToLower();
6449 sOpt.ReplaceAll("ratio", "");
6450 sOpt.ReplaceAll("significance", ""); // remove old option if still given
6451 bool hasSame = sOpt.Contains("same");
6452 sOpt.ReplaceAll("same", "");
6453 bool hasGoff = sOpt.Contains("goff");
6454 sOpt.ReplaceAll("goff", "");
6455 bool hasFR = sOpt.Contains("pull");
6456 sOpt.ReplaceAll("pull", "");
6457 bool hasText = sOpt.Contains("text");
6458 bool hasErrorOpt = sOpt.Contains("e");
6459 sOpt.ReplaceAll("e", "");
6460 if (hasText)
6461 sOpt.ReplaceAll("txt", "text");
6462 if (auxPlotTitle == "Signif")
6463 hasErrorOpt = true; // must calculate error to calculate significance
6464 if (hasOverlay)
6465 hasSame = true; // when overlaying must be putting on same
6466
6467 TVirtualPad *pad = gPad;
6468
6469 TH1 *hAxis = nullptr;
6470 RooAbsLValue *v = nullptr;
6471
6472 auto clearPad = []() {
6473 gPad->Clear();
6474 if (gPad->GetNumber() == 0) {
6475 gPad->SetBottomMargin(gStyle->GetPadBottomMargin());
6476 gPad->SetTopMargin(gStyle->GetPadTopMargin());
6477 gPad->SetLeftMargin(gStyle->GetPadLeftMargin());
6478 gPad->SetRightMargin(gStyle->GetPadRightMargin());
6479 }
6480 // if (gPad == gPad->GetCanvas()) {
6481 // gPad->GetCanvas()->SetCanvasSize( gPad->GetCanvas()->GetWindowWidth() - 4,
6482 // gPad->GetCanvas()->GetWindowHeight() - 28 );
6483 // }
6484 };
6485
6486 if (!hasSame || !pad) {
6487 if (!pad) {
6489 pad = gPad;
6490 }
6491
6492 } else {
6493 // get the histogram representing the axes
6494 hAxis = dynamic_cast<TH1 *>(pad->GetPrimitive("axis"));
6495 if (!hAxis) {
6496 for (auto o : *pad->GetListOfPrimitives()) {
6497 if (hAxis = dynamic_cast<TH1 *>(o); hAxis)
6498 break;
6499 }
6500 }
6501 if (hAxis) {
6502 v = getObject<RooAbsLValue>(hAxis->GetXaxis()->GetName()).get();
6503 }
6504 }
6505
6506 if (!hasSame) {
6507 gPad->SetName(GetName());
6508 gPad->SetTitle(GetTitle());
6509 }
6510
6511 PadRefresher padRefresh(((!hasSame || hasOverlay || PadRefresher::nExisting == 0) && !hasGoff) ? gPad : nullptr);
6512
6513 auto adjustYRange = [&](double min, double max, TH1 *hh = nullptr, bool symmetrize = false) {
6514 if (!hh)
6515 hh = hAxis;
6516 // give max and min a buffer ...
6517 max += gStyle->GetHistTopMargin() * (max - min);
6518 if (min > 0)
6519 min = std::max(min * 0.9, min - gStyle->GetHistTopMargin() * (max - min));
6520 if (hh) {
6521 double ymin = hh->GetMinimum();
6522 double ymax = hh->GetMaximum();
6523 if (hh->GetMaximumStored() == -1111)
6524 ymax += gStyle->GetHistTopMargin() * (ymax - ymin);
6525 if (hh->GetMinimumStored() == -1111) {
6526 if (gStyle->GetHistMinimumZero() && ymax >= 0)
6527 ymin = 0;
6528 else if (ymin < 0)
6529 ymin -= gStyle->GetHistTopMargin() * (ymax - ymin);
6530 else
6531 ymin = std::max(ymin * 0.9, ymin - gStyle->GetHistTopMargin() * (ymax - ymin));
6532 // see TGLPlotPainter to complete the mimic, but we leave off here truncating @ 0 if ymax>0
6533 }
6534 // make ymax at least 3x bigger than biggest error if has error
6535 if (hh->GetSumw2()) {
6536 double smallestErrDown3 = -std::numeric_limits<double>::infinity();
6537 double smallestErrUp3 = std::numeric_limits<double>::infinity();
6538 for (int i = 1; i <= hh->GetNbinsX(); i++) {
6539 smallestErrDown3 = std::max(smallestErrDown3, hh->GetBinContent(i) - 3 * hh->GetBinError(i));
6540 smallestErrUp3 = std::min(smallestErrUp3, hh->GetBinContent(i) + 3 * hh->GetBinError(i));
6541 }
6542 max = std::max(max, smallestErrUp3);
6543 min = std::min(min, smallestErrDown3);
6544 }
6545 bool change = false;
6546 if (min < ymin) {
6547 ymin = min;
6548 change = true;
6549 }
6550 if (max > ymax) {
6551 ymax = max;
6552 change = true;
6553 }
6554 if (change) {
6555 // note: unfortunately when user 'unzooms' y axis it resets stored minimum to -1111, so lose range
6556 if (symmetrize) {
6557 double down = hh->GetBinContent(1) - ymin;
6558 double up = ymax - hh->GetBinContent(1);
6559 if (down > up)
6560 ymax = hh->GetBinContent(1) + down;
6561 else
6562 ymin = hh->GetBinContent(1) - up;
6563 }
6564 if (hh == hAxis && pad && !pad->GetLogy() && ymin > 0 && (log10(ymax) - log10(max)) >= 3) {
6565 // auto-log the pad
6566 pad->SetLogy();
6567 }
6568 if (hh == hAxis && pad && ymin == 0 && pad->GetLogy()) {
6569 ymin = 1e-2;
6570 }
6571 hh->SetMinimum(ymin);
6572 hh->SetMaximum(ymax);
6573 hh->GetYaxis()->Set(1, ymin, ymax);
6574 hh->SetAxisRange(ymin, ymax, "Y");
6575 }
6576 }
6577 };
6578
6579 auto graphMinMax = [](TGraphAsymmErrors *gr) {
6580 double ymax = -std::numeric_limits<double>::infinity();
6581 double ymin = std::numeric_limits<double>::infinity();
6582 for (int i = 0; i < gr->GetN(); i++) {
6583 ymax = std::max(ymax, gr->GetPointY(i) + gr->GetErrorYhigh(i));
6584 ymin = std::min(ymin, gr->GetPointY(i) - gr->GetErrorYlow(i));
6585 }
6586 return std::make_pair(ymin, ymax);
6587 };
6588
6589 if (hasFR) {
6590 // drawing the fitresult as a pull plot on a subpad, and rest of the draw elsewhere
6591 clearPad();
6592 pad->Divide(1, 2, 1e-9, 1e-9); //,0,0);
6593 pad->GetPad(1)->SetPad(0, 0.2, 1, 1);
6594 pad->GetPad(2)->SetPad(0, 0, 1, 0.2);
6595 TString optNoFR(opt);
6596 optNoFR.ReplaceAll("pull", "");
6597 pad->cd(1);
6598 Draw(optNoFR);
6599 pad->cd(2);
6600 auto _fr = fitResult();
6601 _fr.Draw();
6602 // switch into subpad
6603 gPad->cd(1);
6604 gPad->SetFillColor(kGray);
6605 gPad->GetFrame()->SetFillColor(kWhite);
6606 gPad->GetFrame()->SetFillStyle(1001);
6607 gPad->SetTopMargin(0);
6608 gPad->SetBottomMargin(0);
6609 gPad->SetName("pull");
6610 // split the pull graph into individual points -- for benefit of GUI status bar
6611 auto pullGraph =
6612 dynamic_cast<TGraphAsymmErrors *>(gPad->GetPrimitive(TString::Format("%s_pull", _fr->GetName())));
6613 if (!pullGraph) {
6614 throw std::runtime_error("Couldn't find pull graph");
6615 }
6616 pullGraph->SetName("nominal");
6617 TMultiGraph *mg = new TMultiGraph;
6618 mg->SetName("editables");
6619
6620 for (auto i = 0; i < pullGraph->GetN(); i++) {
6621 auto g = new TGraphAsymmErrors;
6622 g->SetName(pullGraph->GetHistogram()->GetXaxis()->GetBinLabel(i + 1));
6623 auto _p = dynamic_cast<RooRealVar *>(_fr.get<RooFitResult>()->floatParsFinal().find(g->GetName()));
6624 if (!_p) {
6625 Warning("Draw", "Found a non-var in the floatParsFinal list: %s - this shouldn't happen", g->GetName());
6626 continue;
6627 }
6628 g->SetTitle(TString::Format(
6629 "%s=%g +/- %s [%g,%g]", strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName(), _p->getVal(),
6630 _p->hasAsymError() ? TString::Format("(%g,%g)", _p->getAsymErrorHi(), _p->getAsymErrorLo()).Data()
6631 : TString::Format("%g", _p->getError()).Data(),
6632 pullGraph->GetHistogram()->GetBinContent(i + 1), pullGraph->GetHistogram()->GetBinError(i + 1)));
6633 g->SetPoint(0, pullGraph->GetPointX(i), pullGraph->GetPointY(i));
6634 g->SetPointEYhigh(0, pullGraph->GetErrorYhigh(i));
6635 g->SetPointEYlow(0, pullGraph->GetErrorYlow(i));
6636 g->SetEditable(true);
6637 g->SetHighlight(true);
6638 g->SetMarkerStyle(20);
6639 g->SetMarkerSize(0.5);
6640 mg->Add(g);
6641 }
6642 // gPad->GetListOfPrimitives()->Remove(pullGraph); delete pullGraph;
6643 mg->Draw("z0p");
6644 mg->SetBit(kCanDelete);
6645 auto _thisClone = new xRooNode("node", fComp, fParent);
6646 _thisClone->SetBit(kCanDelete);
6647 _thisClone->AppendPad();
6648
6649 // ensure statusbar visible for interactive plot
6650 if (gPad->GetCanvas() && !gPad->GetCanvas()->TestBit(TCanvas::kShowEventStatus)) {
6651 gPad->GetCanvas()->ToggleEventStatus();
6652 }
6653 gPad->AddExec("interactivePull", TString::Format("%s::Interactive_Pull()", ClassName()));
6654
6655 pad->cd();
6656 return;
6657 }
6658
6659 if (auto _simPdf = get<RooSimultaneous>(); _simPdf) {
6660 int _size = 0;
6661 auto _channels = bins();
6662 for (auto &_v : _channels) {
6663 if (!_v->IsHidden())
6664 _size++;
6665 }
6666 if (!hasSame) {
6667 clearPad();
6668 pad->SetBorderSize(0);
6669 // if (pad->GetCanvas() == pad) {
6670 // if(_size>4) {
6671 // int n = _size;
6672 // Int_t w = 1, h = 1;
6673 // if (pad->GetCanvas()->GetWindowWidth() > pad->GetCanvas()->GetWindowHeight()) {
6674 // w = TMath::Ceil(TMath::Sqrt(n));
6675 // h = TMath::Floor(TMath::Sqrt(n));
6676 // if (w*h < n) w++;
6677 // } else {
6678 // h = TMath::Ceil(TMath::Sqrt(n));
6679 // w = TMath::Floor(TMath::Sqrt(n));
6680 // if (w*h < n) h++;
6681 // }
6682 // // adjust the window size to display only 4 in the window, with scroll bars
6683 // pad->GetCanvas()->SetCanvasSize( w*((pad->GetCanvas()->GetWindowWidth()-4)/2.) -16
6684 // ,h*((pad->GetCanvas()->GetWindowHeight()-28)/2.) - 16 );
6685 // } else {
6686 // //pad->GetCanvas()->Set(
6687 // w*(pad->GetCanvas()->GetWindowWidth()/2.),h*(pad->GetCanvas()->GetWindowHeight()/2.)) )
6688 // }
6689 // }
6690 dynamic_cast<TPad *>(pad)->DivideSquare(_size, 1e-9, 1e-9);
6691 }
6692 int i = 0;
6693 auto &chanVar = const_cast<RooAbsCategoryLValue &>(_simPdf->indexCat());
6694 // auto _idx = chanVar.getIndex();
6695 auto _range = GetRange();
6696 std::vector<TString> chanPatterns;
6697 if (_range && strlen(_range)) {
6698 TStringToken pattern(_range, ",");
6699 while (pattern.NextToken()) {
6700 chanPatterns.emplace_back(pattern);
6701 }
6702 }
6703 for (auto &_v : _channels) {
6704 if (_v->IsHidden())
6705 continue;
6706 TString s(_v->GetName());
6707 pad->cd(++i);
6708 gPad->SetName(s);
6709 TString cName = s(s.Index('=') + 1, s.Length());
6710 chanVar.setLabel(cName);
6711 bool inRange = chanPatterns.empty();
6712 for (auto &p : chanPatterns)
6713 if (chanVar.inRange(p)) {
6714 inRange = true;
6715 break;
6716 }
6717 if (!inRange || !_v->get<RooAbsReal>()->isSelectedComp())
6718 gPad->SetFillColor(kGray);
6719 if (!hasSame && _size > 1 && (gStyle->GetTitleFont("Y") % 10) == 3)
6720 gPad->SetLeftMargin(std::min(gPad->GetLeftMargin() * (1. / gPad->GetWNDC()), 0.3));
6721 _v->Draw(opt);
6723 }
6724 pad->cd(0);
6725 gPad->Modified();
6726 // gPad->Update();
6727 return;
6728 }
6729
6730 if (!get() || get<RooArgList>()) {
6731 // is a group draw all the submembers
6732 browse();
6733 int _size = 0;
6734 // int _size = _channels.size(); // size(); if (find("!.vars")) _size--;
6735 for (auto &_v : *this) {
6736 if (_v->IsHidden())
6737 continue;
6738 if (strcmp(GetName(), ".vars") == 0) {
6739 // auto hide obs and "1" and const var
6740 if (_v->get<RooAbsArg>()->getAttribute("obs"))
6741 continue;
6742 if (strcmp(_v->get()->GetName(), "1") == 0 || strcmp(_v->get()->GetName(), "ONE") == 0 ||
6743 TString(_v->get()->GetName()).BeginsWith("binWidth_"))
6744 continue;
6745 if (_v->get()->InheritsFrom("RooConstVar"))
6746 continue;
6747 }
6748 TString s(_v->GetName());
6749 if (s.BeginsWith(".") || s.BeginsWith("!"))
6750 continue;
6751 _size++;
6752 }
6753 if (!hasSame) {
6754 clearPad();
6755 pad->SetBorderSize(0);
6756 dynamic_cast<TPad *>(pad)->DivideSquare(_size, 1e-9, 1e-9);
6757 }
6758 int i = 0;
6759 for (auto &_v : *this) {
6760 if (_v->IsHidden())
6761 continue;
6762 if (strcmp(GetName(), ".vars") == 0) {
6763 // auto hide obs and "1" and const var
6764 if (_v->get<RooAbsArg>()->getAttribute("obs"))
6765 continue;
6766 if (strcmp(_v->get()->GetName(), "1") == 0 || strcmp(_v->get()->GetName(), "ONE") == 0 ||
6767 TString(_v->get()->GetName()).BeginsWith("binWidth_"))
6768 continue;
6769 if (_v->get()->InheritsFrom("RooConstVar"))
6770 continue;
6771 }
6772 TString s(_v->GetName());
6773 if (s.BeginsWith(".") || s.BeginsWith("!"))
6774 continue;
6775 pad->cd(++i);
6776 gPad->SetName(s);
6777 if (!hasSame && _size > 1 && (gStyle->GetTitleFont("Y") % 10) == 3)
6778 gPad->SetLeftMargin(std::min(gPad->GetLeftMargin() * (1. / gPad->GetWNDC()), 0.3));
6779 _v->Draw(opt);
6780 // pad->Modified();//pad->Update();
6782 }
6783 pad->cd(0);
6784 gPad->Modified();
6785 // gPad->Update();
6786 return;
6787 }
6788
6789 if (get()->InheritsFrom("RooProdPdf")) {
6790 // draw the main pdf ...
6791 mainChild().Draw(opt);
6792 gPad->SetName(GetName());
6793 return;
6794 }
6795
6796 if (auto fr = get<RooFitResult>(); fr) {
6797 if (sOpt.Contains("corr")) {
6798 // do correlation matrix
6799 auto hist = fr->correlationHist(fr->GetName());
6800 hist->SetTitle(fr->GetTitle());
6801 hist->SetBit(kCanDelete);
6802 hist->Scale(100);
6804 gStyle->SetPaintTextFormat(".1f");
6805 hist->GetXaxis()->SetTickSize(0);
6806 hist->GetYaxis()->SetTickSize(0);
6807 hist->Draw(sOpt);
6809 gPad->SetGrid(1,1);
6810 return;
6811 }
6812
6813 // plot pull
6815 out->SetName(TString::Format("%s_pull", fr->GetName()));
6816 out->SetTitle("Fit Result Pulls");
6817 std::vector<TString> graphLabels;
6819 ugraph->SetName(TString::Format("%s_pull_unconstrained", fr->GetName()));
6820 ugraph->SetTitle("Fit Result Pulls");
6821 std::vector<TString> ugraphLabels;
6822 std::map<std::string, double> scale;
6823 std::map<std::string, double> offset;
6824 for (auto &p : fr->floatParsFinal()) {
6825 auto _v = dynamic_cast<RooRealVar *>(p);
6826 if (!_v)
6827 continue;
6828 // need to get constraint mean and error parameters ....
6829 // look for normal gaussian and poisson cases
6830 double prefitError = dynamic_cast<RooRealVar *>(fr->floatParsInit().find(p->GetName()))->getError();
6831 double prefitVal = dynamic_cast<RooRealVar *>(fr->floatParsInit().find(p->GetName()))->getVal();
6832
6833 std::shared_ptr<xRooNode> pConstr;
6834 if (fParent && fParent->getObject<RooRealVar>(p->GetName())) {
6835 auto _constr = xRooNode(fParent->getObject<RooRealVar>(p->GetName()), *this).constraints();
6836 for (auto &c : _constr) {
6837 if (c->get<RooPoisson>() || c->get<RooGaussian>()) {
6838 pConstr = c;
6839 break;
6840 }
6841 }
6842 }
6843 if (pConstr) {
6844
6845 // there will be 3 deps, one will be this par, the other two are the mean and error (or error^2 in case of
6846 // poisson
6847
6848 // std::cout << p->GetName() << " extracted " << prefitVal << " " << prefitError << " from ";
6849 // pConstr->deps().Print();
6850 pConstr->browse();
6851 if (pConstr->get<RooPoisson>() && pConstr->find(".x")) {
6852 std::string xName = pConstr->find(".x")->get()->GetName();
6853 prefitVal = pConstr->find(".x")->get<RooAbsReal>()->getVal();
6854 for (auto &_d : pConstr->vars()) {
6855 if (strcmp(p->GetName(), _d->get()->GetName()) == 0)
6856 continue;
6857 if (xName == _d->get()->GetName())
6858 continue;
6859 prefitError = _d->get<RooAbsReal>()->getVal();
6860 }
6861 // prefitVal will be the global observable value, need to divide that by tau
6862 prefitVal /= prefitError;
6863 // prefiterror will be tau ... need 1/sqrt(tau) for error
6864 prefitError = 1. / sqrt(prefitError);
6865 } else if (auto _g = pConstr->get<RooGaussian>(); _g) {
6866 prefitError = (pConstr->find(".sigma")) ? pConstr->find(".sigma")->get<RooAbsReal>()->getVal() : 0;
6867 prefitVal =
6868 (pConstr->find(".x")) ? pConstr->find(".x")->get<RooAbsReal>()->getVal() : 0; // usually the globs
6869 if (pConstr->find(".x") &&
6870 strcmp(p->GetName(), pConstr->find(".x")->get<RooAbsReal>()->GetName()) == 0) {
6871 // hybrid construction case,
6872 prefitVal = pConstr->find(".mean")->get<RooAbsReal>()->getVal();
6873 }
6874 }
6875
6876 if (prefitError == 0)
6877 prefitError = dynamic_cast<RooRealVar *>(fr->floatParsInit().find(p->GetName()))->getError();
6878 if (prefitError == 0) {
6879 Warning("Draw", "failed to determine prefit error of %s, using post-fit error", p->GetName());
6880 prefitError = _v->getError();
6881 }
6882 out->SetPoint(out->GetN(), out->GetN(), (_v->getVal() - prefitVal) / prefitError);
6883 out->SetPointError(out->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
6884 (_v->getErrorHi()) / prefitError);
6885 graphLabels.push_back(p->GetName());
6886 scale[p->GetName()] = prefitError;
6887 offset[p->GetName()] = prefitVal;
6888 } else if (!fParent) {
6889 // no parent to determine constraints from ... prefitError=0 will be the unconstrained ones
6890 if (prefitError == 0) {
6891 // uses range of var
6892 prefitError = (std::max({_v->getMax() - _v->getVal(), _v->getVal() - _v->getMin(), 4.}) / 4);
6893 ugraph->SetPoint(ugraph->GetN(), ugraph->GetN(), (_v->getVal() - prefitVal) / prefitError);
6894 ugraph->SetPointError(ugraph->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
6895 (_v->getErrorHi()) / prefitError);
6896 ugraphLabels.push_back(p->GetName());
6897 } else {
6898 out->SetPoint(out->GetN(), out->GetN(), (_v->getVal() - prefitVal) / prefitError);
6899 out->SetPointError(out->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
6900 (_v->getErrorHi()) / prefitError);
6901 graphLabels.push_back(p->GetName());
6902 }
6903 scale[p->GetName()] = prefitError;
6904 offset[p->GetName()] = prefitVal;
6905
6906 } else {
6907 // unconstrained (or at least couldn't determine constraint) ... use par range if no prefit error
6908 if (prefitError == 0) {
6909 prefitError = (std::max({_v->getMax() - _v->getVal(), _v->getVal() - _v->getMin(), 4.}) / 4);
6910 }
6911 ugraph->SetPoint(ugraph->GetN(), ugraph->GetN(), (_v->getVal() - prefitVal) / prefitError);
6912 ugraph->SetPointError(ugraph->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
6913 (_v->getErrorHi()) / prefitError);
6914 ugraphLabels.push_back(p->GetName());
6915 scale[p->GetName()] = prefitError;
6916 offset[p->GetName()] = prefitVal;
6917 }
6918 }
6919 auto graph = out;
6920
6921 // append ugraph points to end of graph
6922 for (int i = 0; i < ugraph->GetN(); i++)
6923 ugraph->SetPointX(i, i + graph->GetN());
6924 int nUnconstrained = ugraph->GetN();
6925 TList tmpList;
6926 tmpList.SetName("tmpList");
6927 tmpList.Add(ugraph);
6928 graph->Merge(&tmpList);
6929 tmpList.RemoveAll();
6930 delete ugraph;
6931
6932 graph->SetBit(kCanDelete);
6933 graph->SetMarkerStyle(20);
6934 graph->SetMarkerSize(0.5);
6935
6936 auto t = TH1::AddDirectoryStatus();
6937 TH1::AddDirectory(false);
6938 auto hist = new TH1F(TString::Format(".%s_pullFrame", GetName()), fr->GetTitle(), std::max(graph->GetN(), 1),
6939 -0.5, std::max(graph->GetN(), 1) - 0.5);
6940 hist->SetStats(false);
6942 hist->SetBit(kCanDelete);
6943 int i = 1;
6944 for (auto &l : graphLabels) {
6945 hist->GetXaxis()->SetBinLabel(i++, l);
6946 }
6947 for (auto &l : ugraphLabels) {
6948 hist->GetXaxis()->SetBinLabel(i++, l);
6949 }
6950 hist->SetMaximum(4);
6951 hist->SetMinimum(-4);
6952 if (graph->GetN())
6953 hist->GetXaxis()->LabelsOption("v");
6954 hist->GetYaxis()->SetNdivisions(8, 0, 0);
6955 hist->GetYaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
6956 hAxis = hist;
6957 clearPad();
6958 // create a new pad because adjust the margins ...
6959 auto oldPad = gPad;
6960 gPad->Divide(1, 1, 1e-9, 1e-9);
6961 gPad->cd(1);
6962 gPad->SetBottomMargin(0.4);
6963
6964 auto pNamesHist = dynamic_cast<TH1F *>(hist->Clone("pnames"));
6965 pNamesHist->Sumw2();
6966 pNamesHist->SetDirectory(0);
6967
6968 for (int ii = 1; ii <= graph->GetN(); ii++) { // use graph->GetN() to protect against the 0 pars case
6969 auto _p = fr->floatParsFinal().find(hist->GetXaxis()->GetBinLabel(ii));
6970 pNamesHist->SetBinContent(ii, offset[_p->GetName()]);
6971 pNamesHist->SetBinError(ii, scale[_p->GetName()]);
6972 hist->GetXaxis()->SetBinLabel(ii, strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName());
6973 }
6974
6975 hist->Draw();
6976
6977 for (int ii = 2; ii >= 1; ii--) {
6978 auto pullBox = new TGraphErrors;
6979 pullBox->SetBit(kCanDelete);
6980 pullBox->SetPoint(0, -0.5, 0);
6981 pullBox->SetPoint(1, hist->GetNbinsX() - 0.5 - nUnconstrained, 0);
6982 pullBox->SetPointError(0, 0, ii);
6983 pullBox->SetPointError(1, 0, ii);
6984 pullBox->SetFillColor((ii == 2) ? kYellow : kGreen);
6985 pullBox->Draw("3");
6986 }
6987 auto pullLine = new TGraph;
6988 pullLine->SetBit(kCanDelete);
6989 pullLine->SetPoint(0, -0.5, 0);
6990 pullLine->SetPoint(1, hist->GetNbinsX() - 0.5 - nUnconstrained, 0);
6991 pullLine->SetLineStyle(2);
6992 pullLine->SetEditable(false);
6993 pullLine->Draw("l");
6994 if (nUnconstrained > 0) {
6995 pullLine = new TGraph;
6996 pullLine->SetBit(kCanDelete);
6997 pullLine->SetPoint(0, graph->GetN() - 0.5 - nUnconstrained, -100);
6998 pullLine->SetPoint(1, graph->GetN() - 0.5 - nUnconstrained, 100);
6999 pullLine->SetLineStyle(2);
7000 pullLine->SetEditable(false);
7001 pullLine->Draw("l");
7002 }
7003 auto minMax = graphMinMax(graph);
7004 adjustYRange(minMax.first, minMax.second);
7005
7006 graph->SetEditable(false);
7007 graph->SetHistogram(pNamesHist);
7008 graph->Draw("z0p");
7009 hist->Draw(
7010 "axissame"); // overlay axis again -- important is last so can remove if don't pad->Update before reclear
7011 gPad->Modified();
7012 oldPad->cd();
7013 // gPad->Update();
7014 return;
7015 }
7016
7017 if (get()->InheritsFrom("RooAbsData")) {
7018 auto s = parentPdf();
7019 if (s && s->get<RooSimultaneous>()) {
7020 // drawing dataset associated to a simultaneous means must find subpads with variation names
7021 for (auto c : s->bins()) {
7022 auto _pad = dynamic_cast<TPad *>(gPad->GetPrimitive(c->GetName()));
7023 if (!_pad)
7024 continue; // channel was hidden?
7025 auto ds = c->datasets().find(GetName());
7026 if (!ds)
7027 continue;
7028 auto tmp = gPad;
7029 _pad->cd();
7030 ds->Draw(opt);
7031 tmp->cd();
7032 }
7033 gPad->Modified();
7034 return;
7035 }
7036
7037 if (!s && hasSame) {
7038 // draw onto all subpads with = in the name
7039 // if has no such subpads, draw onto this pad
7040 bool doneDraw = false;
7041 for (auto o : *gPad->GetListOfPrimitives()) {
7042 if (auto p = dynamic_cast<TPad *>(o); p && TString(p->GetName()).Contains('=')) {
7043 auto _tmp = gPad;
7044 p->cd();
7045 Draw(opt);
7046 _tmp->cd();
7047 doneDraw = true;
7048 }
7049 }
7050 if (doneDraw) {
7051 gPad->Modified();
7052 return;
7053 }
7054 }
7055
7056 auto dataGraph = BuildGraph(v, false, (!s && hasSame) ? gPad : nullptr);
7057 if (!dataGraph)
7058 return;
7059
7060 dataGraph->SetBit(kCanDelete); // will be be deleted when pad is cleared
7061
7062 if (!hasSame) {
7063 clearPad();
7064 dataGraph->Draw("Az0p");
7065 addLegendEntry(dataGraph, strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(), "pEX0");
7066 gPad->Modified();
7067 // gPad->Update();
7068 return;
7069 }
7070
7071 bool noPoint = false;
7072 if (v && dynamic_cast<RooAbsArg *>(v)->getAttribute("global") && dataGraph->GetN() == 1) {
7073 // global observable ... if graph has only 1 data point line it up on the histogram value
7074 for (auto o : *gPad->GetListOfPrimitives()) {
7075 if (auto h = dynamic_cast<TH1 *>(o);
7076 h && strcmp(h->GetXaxis()->GetName(), dynamic_cast<TObject *>(v)->GetName()) == 0) {
7077 dataGraph->SetPointY(0, h->Interpolate(dataGraph->GetPointX(0)));
7078 noPoint = true;
7079 break;
7080 }
7081 }
7082 }
7083
7084 if (auto _pad = dynamic_cast<TPad *>(gPad->FindObject("auxPad")); _pad) {
7085 if (auto h = dynamic_cast<TH1 *>(_pad->GetPrimitive("auxHist")); h) {
7086 TString histName = h->GetTitle(); // split it by | char
7087 TString histType = histName(histName.Index('|') + 1, histName.Length());
7088 histName = histName(0, histName.Index('|'));
7089 if (auto mainHist = dynamic_cast<TH1 *>(gPad->GetPrimitive(histName));
7090 mainHist && auxFunctions.find(h->GetYaxis()->GetTitle()) != auxFunctions.end()) {
7091 // decide what to do based on title of auxHist (previously used name of y-axis but that changed axis
7092 // behaviour) use title instead
7093 auto ratioGraph = dynamic_cast<TGraphAsymmErrors *>(dataGraph->Clone(dataGraph->GetName()));
7094 ratioGraph->SetBit(kCanDelete);
7095 for (int i = 0; i < ratioGraph->GetN(); i++) {
7096 double val = ratioGraph->GetPointY(i);
7097 double nom = mainHist->GetBinContent(i + 1);
7098 double nomerr = mainHist->GetBinError(i + 1);
7099 ratioGraph->SetPointY(
7100 i, std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(ratioGraph->GetPointY(i), nom, nomerr));
7101 ratioGraph->SetPointEYhigh(i, std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(
7102 val + ratioGraph->GetErrorYhigh(i), nom, nomerr) -
7103 ratioGraph->GetPointY(i));
7104 ratioGraph->SetPointEYlow(i, ratioGraph->GetPointY(i) -
7105 std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(
7106 val - ratioGraph->GetErrorYlow(i), nom, nomerr));
7107 }
7108 // remove the zero points
7109 int i = 0;
7110 while (i < ratioGraph->GetN()) {
7111 if (ratioGraph->GetPointY(i) == 0 && ratioGraph->GetErrorYhigh(i) == 0 &&
7112 ratioGraph->GetErrorYlow(i) == 0)
7113 ratioGraph->RemovePoint(i);
7114 else
7115 i++;
7116 }
7117 auto _tmpPad = gPad;
7118 _pad->cd();
7119 ratioGraph->Draw("z0psame");
7120 auto minMax = graphMinMax(ratioGraph);
7121 adjustYRange(minMax.first, minMax.second, h, std::get<1>(auxFunctions[h->GetYaxis()->GetTitle()]));
7122 _tmpPad->cd();
7123 }
7124 }
7125 }
7126
7127 dataGraph->Draw("z0p same");
7128 addLegendEntry((noPoint) ? nullptr : dataGraph, strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(),
7129 noPoint ? "" : "pEX0");
7130
7131 auto minMax = graphMinMax(dynamic_cast<TGraphAsymmErrors *>(dataGraph));
7132 adjustYRange(minMax.first, minMax.second);
7133
7134 gPad->Modified();
7135 // gPad->Update();
7136 return;
7137 }
7138
7139 // auto _ax = GetXaxis();
7140 // auto v = (_ax) ? dynamic_cast<RooRealVar*>(/*possibleObs.first()*/_ax->GetParent()) : nullptr;
7141 // if (!v) { v = get<RooRealVar>(); } // self-axis
7142 // if (!v) return;
7143
7144 if (auto lv = get<RooAbsLValue>(); lv && fParent && fParent->get<RooAbsData>()) {
7145 // drawing an observable from a dataset ... build graph, and exit
7146 auto gr = fParent->BuildGraph(lv, true);
7148 gr->Draw(hasSame ? "P" : "AP");
7149 return;
7150 }
7151
7152 if (forceNames != "") {
7153 // drawing a force plot ... build nll and fill a histogram with force terms
7154 auto _dsets = datasets();
7155 bool _drawn = false;
7156 auto _coords = coords();
7157 auto _fr = fitResult();
7158 auto initPar = dynamic_cast<RooRealVar *>(_fr.get<RooFitResult>()->floatParsInit().find(forceNames));
7159 if (!initPar)
7160 return;
7161 for (auto &d : _dsets) {
7162 if (!d->get()->TestBit(1 << 20))
7163 continue;
7164 auto emptyHist = BuildHistogram(v, true);
7165 emptyHist->SetBit(kCanDelete);
7166 auto _obs = d->obs();
7167 auto x = _obs.find((v) ? dynamic_cast<TObject *>(v)->GetName() : emptyHist->GetXaxis()->GetName());
7168 auto _nll = nll(d);
7169 auto theData = d->get<RooAbsData>();
7170 int nevent = theData->numEntries();
7171 for (int i = 0; i < nevent; i++) {
7172 theData->get(i);
7173 bool _skip = false;
7174 for (const auto &_c : _coords) {
7175 if (auto cat = _c->get<RooAbsCategoryLValue>(); cat) {
7176 if (cat->getIndex() != theData->get()->getCatIndex(cat->GetName())) {
7177 _skip = true;
7178 break;
7179 }
7180 }
7181 }
7182 if (_skip)
7183 continue;
7184
7185 if (x) {
7186 auto val = _nll.pars()->getRealValue(initPar->GetName());
7187 auto nllVal = _nll.getEntryVal(i);
7188 _nll.pars()->setRealValue(initPar->GetName(), initPar->getVal());
7189 auto nllVal2 = _nll.getEntryVal(i);
7190 _nll.pars()->setRealValue(initPar->GetName(), val);
7191 emptyHist->Fill(x->get<RooAbsReal>()->getVal(), (nllVal2 - nllVal));
7192 }
7193 }
7194 auto val = _nll.pars()->getRealValue(initPar->GetName());
7195 auto _extTerm = _nll.extendedTerm();
7196 _nll.pars()->setRealValue(initPar->GetName(), initPar->getVal());
7197 auto _extTerm2 = _nll.extendedTerm();
7198 _nll.pars()->setRealValue(initPar->GetName(), val);
7199 for (int i = 1; i <= emptyHist->GetNbinsX(); i++) {
7200 emptyHist->SetBinContent(i, emptyHist->GetBinContent(i) + (_extTerm2 - _extTerm) / emptyHist->GetNbinsX());
7201 emptyHist->SetBinError(i, 0);
7202 }
7203 emptyHist->GetYaxis()->SetTitle("log (L(#theta)/L(#theta_{0}))");
7204 emptyHist->Draw(_drawn ? "same" : "");
7205 _drawn = true;
7206 }
7207 return;
7208 }
7209
7210 auto rar = get<RooAbsReal>();
7211 if (!rar) {
7212 get()->Draw();
7213 return;
7214 }
7215
7216 auto h = BuildHistogram(v, false, hasErrorOpt);
7217 if (!h)
7218 return;
7219 h->SetBit(kCanDelete);
7220 if (!v)
7221 v = getObject<RooAbsLValue>(h->GetXaxis()->GetName()).get();
7222 RooAbsArg *vv = (v) ? dynamic_cast<RooAbsArg *>(v) : rar;
7223 if (h->GetXaxis()->IsAlphanumeric()) {
7224 // do this to get bin labels
7225 h->GetXaxis()->SetName("xaxis"); // WARNING -- this messes up anywhere we GetXaxis()->GetName()
7226 }
7227 if (!hasSame) {
7228 if (obs().find(vv->GetName())) {
7229 gPad->SetGrid(0, 0);
7230 } else {
7231 gPad->SetGrid(1, 1);
7232 }
7233 }
7234 TString dOpt = (TString(rar->ClassName()).Contains("Hist") || rar->isBinnedDistribution(*vv) ||
7235 rar->getAttribute("BinnedLikelihood") ||
7236 (dynamic_cast<RooAbsRealLValue *>(vv) &&
7237 std::unique_ptr<std::list<double>>(rar->binBoundaries(*dynamic_cast<RooAbsRealLValue *>(vv),
7238 -std::numeric_limits<double>::infinity(),
7239 std::numeric_limits<double>::infinity()))))
7240 ? ""
7241 : "LF2";
7242 if (dOpt == "LF2" && !components().empty()) {
7243 // check if all components of dOpt are "Hist" type (CMS model support)
7244 // if so then dOpt="";
7245 bool allHist = true;
7246 for (auto &s : components()) {
7247 if (!(s->get() && TString(s->get()->ClassName()).Contains("Hist"))) {
7248 allHist = false;
7249 break;
7250 }
7251 }
7252 if (allHist)
7253 dOpt = "";
7254 }
7255 if (rar == vv)
7256 dOpt += "TEXT";
7257
7258 if (rar == vv && rar->IsA() == RooRealVar::Class()) {
7259 // add a TExec to the histogram so that when edited it will propagate to var
7260 gROOT->SetEditHistograms(true);
7261 } else {
7262 gROOT->SetEditHistograms(false);
7263 }
7264
7265 if (hasSame)
7266 dOpt += " same";
7267 else
7268 hAxis = h;
7269
7270 if (dOpt.Contains("TEXT") || sOpt.Contains("text")) {
7271 // adjust marker size so text is good
7272 h->SetMarkerSize(gStyle->GetLabelSize("Z") / (0.02 * gPad->GetHNDC()));
7273 }
7274
7275 bool hasError(false);
7276 for (int i = 0; i < h->GetSumw2N(); i++) {
7277 if (h->GetSumw2()->At(i)) {
7278 hasError = true;
7279 break;
7280 }
7281 }
7282
7283 /** This doesn't seem necessary in at least 6.26 any more - pads seem adjusted on their own
7284 if (!hasSame && h->GetYaxis()->GetTitleFont()%10 == 2) {
7285 h->GetYaxis()->SetTitleOffset( gPad->GetLeftMargin() / gStyle->GetPadLeftMargin() );
7286 } */
7287 // don't this instead - dont want to leave as zero (auto) in case show aux plot
7288 if (!hasSame && h->GetYaxis()->GetTitleFont() % 10 == 2) {
7289 h->GetYaxis()->SetTitleOffset(1.);
7290 }
7291
7292 TH1 *errHist = nullptr;
7293 if (hasError) {
7294 h->SetFillStyle(hasError ? 3005 : 0);
7295 h->SetFillColor(h->GetLineColor());
7296 h->SetMarkerStyle(0);
7297 errHist = dynamic_cast<TH1 *>(h->Clone(Form("%s_err", h->GetName())));
7298 h->SetFillStyle(0);
7299 for (int i = 1; i <= h->GetNbinsX(); i++) {
7300 h->SetBinError(i, 0);
7301 }
7302 }
7303
7304 if (!hasSame)
7305 clearPad();
7306
7307 if (rar == vv && rar->IsA() == RooRealVar::Class()) {
7308 // add a TExec to the histogram so that when edited it will propagate to var
7309 // h->GetListOfFunctions()->Add(h->Clone("self"),"TEXTHIST");
7310 dOpt = "TEXT";
7311 auto node = new xRooNode(*this);
7312 auto _hist = (errHist) ? errHist : h;
7313 auto hCopy = (errHist) ? nullptr : h->Clone();
7314 _hist->GetListOfFunctions()->Add(node);
7315 _hist->GetListOfFunctions()->Add(new TExec(
7316 ".update",
7318 "gROOT->SetEditHistograms(true);auto h = dynamic_cast<TH1*>(gPad->GetPrimitive(\"%s\")); if(h) { if(auto n "
7319 "= dynamic_cast<xRooNode*>(h->GetListOfFunctions()->FindObject(\"%s\")); n && "
7320 "n->TestBit(TObject::kNotDeleted) && n->get<RooRealVar>()->getVal() != h->GetBinContent(1)) {double range "
7321 "= h->GetMaximum()-h->GetMinimum(); h->SetBinContent(1, "
7322 "TString::Format(\"%%.2g\",int(h->GetBinContent(1)/(range*0.01))*range*0.01).Atof());n->SetContents( "
7323 "h->GetBinContent(1) ); for(auto pp : *h->GetListOfFunctions()) if(auto hh = "
7324 "dynamic_cast<TH1*>(pp))hh->SetBinContent(1,h->GetBinContent(1));} gPad->Modified();gPad->Update(); }",
7325 _hist->GetName(), node->GetName())));
7326 if (errHist) {
7327 errHist->GetListOfFunctions()->Add(h, "TEXT HIST same");
7328 errHist->SetFillColor(h->GetLineColor());
7329 } else {
7330 hCopy->SetBit(kCanDelete);
7331 _hist->GetListOfFunctions()->Add(hCopy, "TEXT HIST same");
7332 _hist->SetBinError(1, 0);
7333 }
7334 _hist->SetStats(false);
7335 _hist->Draw(((errHist) ? "e2" : ""));
7336 gPad->Modified();
7337 return;
7338 }
7339
7340 bool overlayExisted = false;
7341 if (hasOverlay) {
7342 h->SetName(TString::Format("%s%s", h->GetName(), overlayName.Data()));
7343 if (auto existing = dynamic_cast<TH1 *>(gPad->GetPrimitive(h->GetName())); existing) {
7344 existing->Reset();
7345 existing->Add(h);
7346 delete h;
7347 h = existing;
7348 overlayExisted = true;
7349 } else {
7350 TString oldStyle = (rar && rar->getStringAttribute("style")) ? rar->getStringAttribute("style") : "";
7351 h->SetTitle(overlayName);
7352 // for overlays will take style from current gStyle before overriding with personal style
7353 // this ensures initial style will be whatever gStyle is, rather than whatever ours is
7354 (TAttLine &)(*h) = *gStyle;
7355
7356 // std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case
7357 // getObject has decided to return the owning ptr (for some reason) if
7358 // (!gROOT->GetStyle(h->GetTitle())) {
7359 // if ( (style = getObject<TStyle>(h->GetTitle())) ) {
7360 // // loaded style (from workspace?) so put in list and use that
7361 // gROOT->GetListOfStyles()->Add(style.get());
7362 // } else {
7363 // // create new style - gets put in style list automatically so don't have to delete
7364 // // acquire them so saved to workspaces for auto reload ...
7365 // style = acquireNew<TStyle>(h->GetTitle(),
7366 // TString::Format("Style for %s component", h->GetTitle()));
7367 // (TAttLine &) (*style) = *dynamic_cast<TAttLine *>(h);
7368 // (TAttFill &) (*style) = *dynamic_cast<TAttFill *>(h);
7369 // (TAttMarker &) (*style) = *dynamic_cast<TAttMarker *>(h);
7370 // gROOT->GetListOfStyles()->Add(style.get());
7371 // }
7372 // }
7373 // (TAttLine&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
7374 // (TAttFill&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
7375 // (TAttMarker&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
7376 auto _style = style(h);
7377 rar->setStringAttribute("style",oldStyle=="" ? nullptr : oldStyle.Data()); // restores old style
7378 (TAttLine &)(*h) = *_style;
7379 (TAttFill &)(*h) = *_style;
7380 (TAttMarker &)(*h) = *_style;
7381
7382 h->Draw(dOpt);
7383 if (errHist) {
7384 errHist->SetTitle(overlayName);
7385 (TAttLine &)(*errHist) = *h;
7386 errHist->SetFillColor(h->GetLineColor());
7387 }
7388 }
7389 } else {
7390 h->Draw(dOpt + sOpt);
7391 }
7392
7393 if (!hasOverlay && (rar->InheritsFrom("RooRealSumPdf") || rar->InheritsFrom("RooAddPdf"))) {
7394 // build a stack
7395 THStack *stack = new THStack(TString::Format("%s_stack", rar->GetName()),
7396 TString::Format("%s;%s", rar->GetTitle(), h->GetXaxis()->GetTitle()));
7397 int count = 2;
7398 std::map<std::string, int> colorByTitle; // TODO: should fill from any existing legend
7399 std::set<std::string> allTitles;
7400 bool titleMatchName = true;
7401 std::map<std::string, TH1 *> histGroups;
7402 std::vector<TH1 *> hhs;
7403 if (components().size() == 1) {
7404 // support for CMS model case where has single component containing many coeffs
7405 // will build stack by setting each coeff equal to 0 in turn, rebuilding the histogram
7406 // the difference from the "full" histogram will be the component
7407 auto comps = components()[0];
7409 for (auto &c : *comps) {
7410 if (c->fFolder == "!.coeffs")
7411 coefs.add(*c->get<RooAbsArg>());
7412 }
7413 if (!coefs.empty()) {
7414 RooRealVar zero("zero", "", 0);
7415 std::shared_ptr<TH1> prevHist((TH1 *)h->Clone());
7416 for (auto c : coefs) {
7417 // seems I have to remake the function each time, as haven't figured out what cache needs clearing?
7418 std::unique_ptr<RooAbsReal> f(dynamic_cast<RooAbsReal *>(comps->get()->Clone("tmpCopy")));
7419 zero.setAttribute(
7420 Form("ORIGNAME:%s", c->GetName())); // used in redirectServers to say what this replaces
7421 f->redirectServers(RooArgSet(zero), false, true); // each time will replace one additional coef
7422 // zero.setAttribute(Form("ORIGNAME:%s",c->GetName()),false); (commented out so that on next iteration
7423 // will still replace all prev)
7424 auto hh = xRooNode(*f, *this).BuildHistogram(v);
7425 if (strlen(hh->GetTitle()) == 0)
7426 hh->SetTitle(c->GetName()); // ensure all hists has titles
7427 titleMatchName &= (TString(c->GetName()) == hh->GetTitle() ||
7428 TString(hh->GetTitle()).BeginsWith(TString(c->GetName()) + "_"));
7429 std::shared_ptr<TH1> nextHist((TH1 *)hh->Clone());
7430 hh->Add(prevHist.get(), -1.);
7431 hh->Scale(-1.);
7432 hhs.push_back(hh);
7433 prevHist = nextHist;
7434 }
7435 }
7436 } else {
7437 for (auto &samp : components()) {
7438 auto hh = samp->BuildHistogram(v);
7439 hhs.push_back(hh);
7440 if (strlen(hh->GetTitle()) == 0)
7441 hh->SetTitle(samp->GetName()); // ensure all hists has titles
7442 titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
7443 TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
7444 }
7445 }
7446 for (auto &hh : hhs) {
7447 // automatically group hists that all have the same title
7448 if (histGroups.find(hh->GetTitle()) == histGroups.end()) {
7449 histGroups[hh->GetTitle()] = hh;
7450 } else {
7451 // add it into this group
7452 histGroups[hh->GetTitle()]->Add(hh);
7453 delete hh;
7454 continue;
7455 }
7456 auto hhMin = (hh->GetMinimum() == 0) ? hh->GetMinimum(1e-9) : hh->GetMinimum();
7457 if (!stack->GetHists() && h->GetMinimum() > hhMin) {
7458 auto newMin = hhMin - (h->GetMaximum() - hhMin) * gStyle->GetHistTopMargin();
7459 if (hhMin >= 0 && newMin < 0)
7460 newMin = hhMin * 0.99;
7461 adjustYRange(newMin, h->GetMaximum());
7462 }
7463 if (auto it = colorByTitle.find(hh->GetTitle()); it != colorByTitle.end()) {
7464 hh->SetFillColor(it->second);
7465 } else {
7466 if (hh->GetFillColor() == 0) {
7467 hh->SetFillColor((count++) % 100);
7468 }
7469 colorByTitle[hh->GetTitle()] = hh->GetFillColor();
7470 }
7471 /*if(stack->GetHists() && stack->GetHists()->GetEntries()>0) {
7472 // to remove rounding effects on bin boundaries, see if binnings compatible
7473 auto _h1 = dynamic_cast<TH1*>(stack->GetHists()->At(0));
7474 if(_h1->GetNbinsX()==hh->GetNbinsX()) TODO ... finish dealing with silly rounding effects
7475 }*/
7476 TString thisOpt = dOpt;
7477 // uncomment next line to blend continuous with discrete components .. get some unpleasant "poke through"
7478 // effects though
7479 // if(auto s = samp->get<RooAbsReal>(); s) thisOpt = s->isBinnedDistribution(*dynamic_cast<RooAbsArg*>(v)) ? ""
7480 // : "LF2";
7481 stack->Add(hh, thisOpt);
7482 allTitles.insert(hh->GetTitle());
7483 }
7484 stack->SetBit(kCanDelete); // should delete its sub histograms
7485 stack->Draw("noclear same");
7486 h->Draw("axissame"); // overlay axis again
7487
7488 TList *ll = stack->GetHists();
7489 if (ll && ll->GetEntries()) {
7490
7491 // get common prefix to strip off only if all titles match names and
7492 // any title is longer than 10 chars
7493 size_t e = std::min(allTitles.begin()->size(), allTitles.rbegin()->size());
7494 size_t ii = 0;
7495 bool goodPrefix = false;
7496 std::string commonSuffix;
7497 if (titleMatchName && ll->GetEntries() > 1) {
7498 while (ii < e - 1 && allTitles.begin()->at(ii) == allTitles.rbegin()->at(ii)) {
7499 ii++;
7500 if (allTitles.begin()->at(ii) == '_' || allTitles.begin()->at(ii) == ' ')
7501 goodPrefix = true;
7502 }
7503
7504 // find common suffix if there is one .. must start with a "_"
7505 bool stop = false;
7506 while (!stop && commonSuffix.size() < size_t(e - 1)) {
7507 commonSuffix = allTitles.begin()->substr(allTitles.begin()->length() - commonSuffix.length() - 1);
7508 for (auto &t : allTitles) {
7509 if (!TString(t).EndsWith(commonSuffix.c_str())) {
7510 commonSuffix = commonSuffix.substr(1);
7511 stop = true;
7512 break;
7513 }
7514 }
7515 }
7516 if (commonSuffix.find('_') == std::string::npos)
7517 commonSuffix = "";
7518 else
7519 commonSuffix = commonSuffix.substr(commonSuffix.find('_'));
7520 }
7521 if (!goodPrefix)
7522 ii = 0;
7523
7524 // also find how many characters are needed to distinguish all entries (that dont have the same name)
7525 // then carry on up to first space or underscore
7526 size_t jj = 0;
7527 std::map<std::string, std::string> reducedTitles;
7528 while (reducedTitles.size() != allTitles.size()) {
7529 jj++;
7530 std::map<std::string, int> titlesMap;
7531 for (auto &s : allTitles) {
7532 if (reducedTitles.count(s))
7533 continue;
7534 titlesMap[s.substr(0, jj)]++;
7535 }
7536 for (auto &s : allTitles) {
7537 if (titlesMap[s.substr(0, jj)] == 1 && (jj >= s.length() || s.at(jj) == ' ' || s.at(jj) == '_')) {
7538 reducedTitles[s] = s.substr(0, jj);
7539 }
7540 }
7541 }
7542
7543 // strip common prefix and suffix before adding
7544 for (int i = ll->GetEntries() - 1; i >= 0; i--) { // go in reverse order
7545 auto _title = (ll->GetEntries() > 5) ? reducedTitles[ll->At(i)->GetTitle()] : ll->At(i)->GetTitle();
7546 _title = _title.substr(ii < _title.size() ? ii : 0);
7547 if (!commonSuffix.empty() && TString(_title).EndsWith(commonSuffix.c_str()))
7548 _title = _title.substr(0, _title.length() - commonSuffix.length());
7549
7550 // style hists according to availble styles ... creating if necessary
7551 // keeping this code here because style() method would be for the stack instead of
7552 // for the components
7553 std::shared_ptr<TStyle> _style; // use to keep alive for access from GetStyle below, in case getObject has
7554 // decided to return the owning ptr (for some reason)
7555 if (!gROOT->GetStyle(_title.c_str())) {
7556 if ((_style = getObject<TStyle>(_title))) {
7557 // loaded style (from workspace?) so put in list and use that
7558 gROOT->GetListOfStyles()->Add(_style.get());
7559 } else {
7560 // create new style - gets put in style list automatically so don't have to delete
7561 // acquire them so saved to workspaces for auto reload ...
7562 _style =
7563 acquireNew<TStyle>(_title.c_str(), TString::Format("Style for %s component", _title.c_str()));
7564 (TAttLine &)(*_style) = *dynamic_cast<TAttLine *>(ll->At(i));
7565 (TAttFill &)(*_style) = *dynamic_cast<TAttFill *>(ll->At(i));
7566 (TAttMarker &)(*_style) = *dynamic_cast<TAttMarker *>(ll->At(i));
7567 gROOT->GetListOfStyles()->Add(_style.get());
7568 }
7569 } else {
7570 _style = std::shared_ptr<TStyle>(gROOT->GetStyle(_title.c_str()), [](TStyle *) {});
7571 }
7572 dynamic_cast<TNamed *>(ll->At(i))->SetTitle(_title.c_str());
7573 // auto _style = style(ll->At(i));
7574 *dynamic_cast<TAttLine *>(ll->At(i)) = *_style;
7575 *dynamic_cast<TAttFill *>(ll->At(i)) = *_style;
7576 *dynamic_cast<TAttMarker *>(ll->At(i)) = *_style;
7577
7578 addLegendEntry(ll->At(i), _title.c_str(), "f");
7579 }
7580 }
7581 } else if (!overlayExisted) {
7582
7583 if (errHist) {
7584 addLegendEntry(errHist, strlen(errHist->GetTitle()) ? errHist->GetTitle() : GetName(), "fl");
7585 } else {
7586 if (rar->InheritsFrom("RooAbsPdf") &&
7587 !(rar->InheritsFrom("RooRealSumPdf") || rar->InheritsFrom("RooAddPdf"))) {
7588 // append parameter values to title if has such
7589 RooArgSet s;
7590 rar->leafNodeServerList(&s);
7591 if (v)
7592 s.remove(*dynamic_cast<RooAbsArg *>(v));
7593 if (!s.empty()) {
7594 TString ss = h->GetTitle();
7595 ss += " [";
7596 bool first = true;
7597 for (auto _p : s) {
7598 auto _v = dynamic_cast<RooAbsReal *>(_p);
7599 if (!_v)
7600 continue;
7601 if (!first)
7602 ss += ",";
7603 first = false;
7604 ss += TString::Format("%s=%g", strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName(), _v->getVal());
7605 }
7606 ss += "]";
7607 h->SetTitle(ss);
7608 }
7609 }
7610
7611 addLegendEntry(h, strlen(h->GetTitle()) ? h->GetTitle() : GetName(), "l");
7612 }
7613 }
7614
7615 if (errHist) {
7616 dOpt.ReplaceAll("TEXT", "");
7617 errHist->Draw(dOpt + (dOpt.Contains("LF2") ? "e3same" : "e2same"));
7618 double ymax = -std::numeric_limits<double>::infinity();
7619 double ymin = std::numeric_limits<double>::infinity();
7620 for (int i = 1; i <= errHist->GetNbinsX(); i++) {
7621 ymax = std::max(ymax, errHist->GetBinContent(i) + errHist->GetBinError(i));
7622 ymin = std::min(ymin, errHist->GetBinContent(i) - errHist->GetBinError(i));
7623 }
7624 adjustYRange(ymin, ymax);
7625 } else {
7626 adjustYRange(h->GetMinimum() * 0.9, h->GetMaximum() * 1.1);
7627 }
7628
7629 if ((!auxPlotTitle.empty()) && !hasSame) {
7630 // create a pad for the ratio ... shift the bottom margin of this pad to make space for it
7631 double padFrac = 0.3;
7632 auto _tmpPad = gPad;
7633 gPad->SetBottomMargin(padFrac);
7634 auto ratioPad = new TPad("auxPad", "aux plot", 0, 0, 1, padFrac);
7635 ratioPad->SetFillColor(_tmpPad->GetFillColor());
7636 ratioPad->SetNumber(1);
7637 ratioPad->SetBottomMargin(ratioPad->GetBottomMargin() / padFrac);
7638 ratioPad->SetTopMargin(0.04);
7639 ratioPad->SetLeftMargin(gPad->GetLeftMargin());
7640 ratioPad->SetRightMargin(gPad->GetRightMargin());
7641 ratioPad->cd();
7642 TH1 *ratioHist = dynamic_cast<TH1 *>((errHist) ? errHist->Clone("auxHist") : h->Clone("auxHist"));
7643 ratioHist->SetTitle((errHist) ? errHist->GetName()
7644 : h->GetName()); // abuse the title string to hold the name of the main hist
7645
7646 ratioHist->GetYaxis()->SetNdivisions(5, 0, 0);
7647 ratioHist->GetYaxis()->SetTitle(auxPlotTitle.c_str());
7648 ratioHist->SetTitle(
7649 TString::Format("%s|%s", ratioHist->GetTitle(),
7650 auxPlotTitle.c_str())); // used when plotting data (above) to decide what to calculate
7651 ratioHist->SetMaximum();
7652 ratioHist->SetMinimum(); // resets min and max
7653 ratioPad->SetGridy();
7654
7655 for (int i = 1; i <= ratioHist->GetNbinsX(); i++) {
7656 double val = ratioHist->GetBinContent(i);
7657 double err = ratioHist->GetBinError(i);
7658 ratioHist->SetBinContent(i, std::get<0>(auxFunctions[auxPlotTitle])(val, val, err));
7659 ratioHist->SetBinError(i, std::get<0>(auxFunctions[auxPlotTitle])(val + err, val, err) -
7660 ratioHist->GetBinContent(i));
7661 }
7662
7663 double rHeight = 1. / padFrac; //(_tmpPad->GetWNDC())/(gPad->GetHNDC());
7664 if (ratioHist->GetYaxis()->GetTitleFont() % 10 == 2) {
7665 ratioHist->GetYaxis()->SetTitleSize(ratioHist->GetYaxis()->GetTitleSize() * rHeight);
7666 ratioHist->GetYaxis()->SetLabelSize(ratioHist->GetYaxis()->GetLabelSize() * rHeight);
7667 ratioHist->GetXaxis()->SetTitleSize(ratioHist->GetXaxis()->GetTitleSize() * rHeight);
7668 ratioHist->GetXaxis()->SetLabelSize(ratioHist->GetXaxis()->GetLabelSize() * rHeight);
7669 ratioHist->GetYaxis()->SetTitleOffset(ratioHist->GetYaxis()->GetTitleOffset() / rHeight);
7670 } else {
7671#if ROOT_VERSION_CODE < ROOT_VERSION(6, 26, 00)
7672 ratioHist->GetYaxis()->SetTitleOffset(ratioHist->GetYaxis()->GetTitleOffset() / rHeight);
7673#endif
7674 }
7675 ratioHist->GetXaxis()->SetTickLength(ratioHist->GetXaxis()->GetTickLength() * rHeight);
7676 ratioHist->SetStats(false);
7677 ratioHist->SetBit(TH1::kNoTitle);
7678 ratioHist->SetBit(kCanDelete);
7679 ratioHist->Draw((errHist ? "e2" : ""));
7680 if (errHist) {
7681 auto _h = dynamic_cast<TH1 *>(ratioHist->Clone("auxHist_clone"));
7682 _h->SetFillColor(0);
7683 _h->Draw("histsame");
7684 }
7685 _tmpPad->cd();
7686 ratioPad->Draw();
7687 } else if (auto ratioPad = dynamic_cast<TPad *>(gPad->GetPrimitive("auxPad")); hasSame && ratioPad) {
7688 // need to draw histogram in the ratio pad ...
7689 // if doing overlay need to update histogram
7690
7691 if (auto hr = dynamic_cast<TH1 *>(ratioPad->GetPrimitive("auxHist"));
7692 hr && auxFunctions.find(hr->GetYaxis()->GetTitle()) != auxFunctions.end()) {
7693 TString histName = hr->GetTitle(); // split it by | char
7694 TString histType = histName(histName.Index('|') + 1, histName.Length());
7695 histName = histName(0, histName.Index('|'));
7696
7697 if (auto hnom = dynamic_cast<TH1 *>(gPad->GetPrimitive(histName)); hnom) {
7698 h = dynamic_cast<TH1 *>(h->Clone(h->GetName()));
7700 for (int i = 1; i <= hnom->GetNbinsX(); i++) {
7701 double val = h->GetBinContent(i);
7702 double err = h->GetBinError(i);
7703 h->SetBinContent(i, std::get<0>(auxFunctions[hr->GetYaxis()->GetTitle()])(
7704 h->GetBinContent(i), hnom->GetBinContent(i), hnom->GetBinError(i)));
7705 h->SetBinError(i, std::get<0>(auxFunctions[hr->GetYaxis()->GetTitle()])(
7706 val + err, hnom->GetBinContent(i), hnom->GetBinError(i)) -
7707 h->GetBinContent(i));
7708 }
7709 auto _tmpPad = gPad;
7710 ratioPad->cd();
7711 if (hasOverlay) {
7712 if (auto existing = dynamic_cast<TH1 *>(ratioPad->GetPrimitive(h->GetName())); existing) {
7713 existing->Reset();
7714 existing->Add(h);
7715 delete h;
7716 h = existing;
7717 overlayExisted = true;
7718 } else {
7719 h->Draw(dOpt);
7720 }
7721 } else {
7722 h->Draw(dOpt);
7723 }
7724 double ymax = -std::numeric_limits<double>::infinity();
7725 double ymin = std::numeric_limits<double>::infinity();
7726 for (int i = 1; i <= h->GetNbinsX(); i++) {
7727 ymax = std::max(ymax, h->GetBinContent(i) + h->GetBinError(i));
7728 ymin = std::min(ymin, h->GetBinContent(i) - h->GetBinError(i));
7729 }
7730 adjustYRange(ymin, ymax, hr, std::get<1>(auxFunctions[hr->GetYaxis()->GetTitle()]));
7731 // adjustYRange(h->GetMinimum() * (h->GetMinimum()<0 ? 1.1 : 0.9), h->GetMaximum() * (h->GetMinimum()<0 ?
7732 // 0.9 : 1.1), hr, std::get<1>(auxFunctions[hr->GetYaxis()->GetTitle()]));
7733 gPad->Modified();
7734 _tmpPad->cd();
7735 }
7736 }
7737 }
7738
7739 // see if it's in a simultaneous so need to select a cat
7740 /*auto _parent = fParent;
7741 auto _me = rar;
7742 while(_parent) {
7743 if (auto s = _parent->get<RooSimultaneous>(); s) {
7744 for (auto c : s->indexCat()) {
7745 if (auto p = s->getPdf(c.first.c_str());_me==p) {
7746 gPad->SetName(c.first.c_str());
7747 break;
7748 }
7749 }
7750 break;
7751 }
7752 _me = _parent->get<RooAbsReal>();
7753 _parent = _parent->fParent;
7754 }*/
7755
7756 // now draw selected datasets on top if this was a pdf
7757 if (!hasSame && get<RooAbsPdf>()) {
7758 auto _dsets = datasets();
7759 // bool _drawn=false;
7760 for (auto &d : _dsets) {
7761 if (d->get()->TestBit(1 << 20)) {
7762 d->Draw("same");
7763 //_drawn=true;
7764 }
7765 }
7766 // if (!_drawn && !_dsets.empty()) _dsets[0]->Draw("same"); // always draw if has a dataset
7767 }
7768
7769 gPad->Modified();
7770 // gPad->Update();
7771 getLegend();
7772 gPad->Modified();
7773 // gPad->Update();
7774}
7775
7776void xRooNode::SaveAs(const char *filename, Option_t *option) const
7777{
7778 TString sOpt(option);
7779 sOpt.ToLower();
7780 if (auto w = get<RooWorkspace>(); w) {
7781
7782 if (TString(filename).EndsWith(".json")) {
7783#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
7784 // stream with json tool
7785 RooJSONFactoryWSTool tool(*w);
7786 if (tool.exportJSON(filename)) {
7787 Info("SaveAs", "%s saved to %s", w->GetName(), filename);
7788 } else {
7789 Error("SaveAs", "Unable to save to %s", filename);
7790 }
7791#else
7792 Error("SaveAs", "json format workspaces only in ROOT 6.26 onwards");
7793#endif
7794 return;
7795 }
7796
7797#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
7798 // before saving, clear the eocache of all owned nodes
7799 // because causes memory leak when read back in (workspace streamer immediately overwrites the caches)
7800 // fixed in: https://github.com/root-project/root/pull/12024
7801 for (auto &c : w->components()) {
7802 c->_eocache = nullptr;
7803 }
7804#endif
7805 // const_cast<Node2*>(this)->sterilize(); - tried this to reduce mem leak on readback but no improve
7806 if (!w->writeToFile(filename, sOpt != "update")) {
7807 Info("SaveAs", "%s saved to %s", w->GetName(), filename);
7808 } else {
7809 Error("SaveAs", "Unable to save to %s", filename);
7810 }
7811#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
7812 // restore the cache to every node
7813 for (auto &c : w->components()) {
7814 c->setExpensiveObjectCache(w->expensiveObjectCache());
7815 }
7816#endif
7817 }
7818}
7819
7820double xRooNode::GetBinError(int bin, const xRooNode &fr) const
7821{
7822 auto res = GetBinErrors(bin, bin, fr);
7823 if (res.empty())
7824 return std::numeric_limits<double>::quiet_NaN();
7825 return res.at(0);
7826}
7827
7828std::pair<double, double> xRooNode::IntegralAndError(const xRooNode &fr, const char *rangeName) const
7829{
7830 double out = 1.;
7831 double err = std::numeric_limits<double>::quiet_NaN();
7832
7833 std::unique_ptr<RooAbsCollection> _snap;
7834 RooArgList _pars;
7835 if (auto _fr = fr.get<RooFitResult>()) {
7836 _pars.add(pars().argList());
7837 _snap.reset(_pars.snapshot());
7838 _pars = _fr->floatParsFinal();
7839 _pars = _fr->constPars();
7840 }
7841
7842 auto _obs = obs().argList();
7843 auto _coefs = coefs(); // need here to keep alive owned RooProduct
7844 if (auto c = _coefs.get<RooAbsReal>(); c) {
7845 out = c->getVal(_obs); // assumes independent of observables!
7846 }
7847
7848 if (auto p = dynamic_cast<RooAbsPdf *>(get()); p) {
7849 // prefer to use expectedEvents for integrals of RooAbsPdf e.g. for RooProdPdf wont include constraint terms
7850 if (rangeName)
7851 p->setNormRange(rangeName);
7852 out *= p->expectedEvents(_obs);
7853#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
7854 // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
7855 p->_normSet = nullptr;
7856#endif
7857 err = GetBinError(-1, fr);
7858 if (rangeName)
7859 p->setNormRange(nullptr);
7860 } else if (auto p2 = dynamic_cast<RooAbsReal *>(get()); p2) {
7861 // only integrate over observables we actually depend on
7862 auto f = std::shared_ptr<RooAbsReal>(p2->createIntegral(*std::unique_ptr<RooArgSet>(p2->getObservables(_obs)),
7863 rangeName)); // did use x here before using obs
7864 double tmp =
7865 out; // coef value ... not included in Error of integral we just created (doesn't have coefs() return)
7866 out *= f->getVal();
7867 err = tmp * xRooNode(f, *this).GetBinError(-1, fr);
7868 } else if (get<RooAbsData>()) {
7869 out = 0;
7870 auto vals = GetBinContents(1, 0); // returns all bins
7871 auto ax = (rangeName) ? GetXaxis() : nullptr;
7872 auto rv = (ax) ? dynamic_cast<RooRealVar *>(ax->GetParent()) : nullptr;
7873 auto cv = (ax && !rv) ? dynamic_cast<RooCategory *>(ax->GetParent()) : nullptr;
7874 int i = 0;
7875 for (auto &v : vals) {
7876 i++;
7877 if (rangeName) {
7878 if (rv && !rv->inRange(ax->GetBinCenter(i), rangeName))
7879 continue;
7880 if (cv && !cv->isStateInRange(rangeName, ax->GetBinLabel(i)))
7881 continue;
7882 }
7883 out += v;
7884 }
7885 err = 0; // should this be sqrt(sum(v^2)) or something similar
7886 } else {
7887 out = std::numeric_limits<double>::quiet_NaN();
7888 }
7889 if (_snap) {
7890 _pars.RooAbsCollection::operator=(*_snap);
7891 }
7892 return std::make_pair(out, err);
7893}
7894
7895std::vector<double> xRooNode::GetBinErrors(int binStart, int binEnd, const xRooNode &_fr) const
7896{
7897 std::vector<double> out;
7898
7899 auto o = dynamic_cast<RooAbsReal *>(get());
7900 if (!o)
7901 return out;
7902
7903 std::shared_ptr<RooFitResult> fr = std::dynamic_pointer_cast<RooFitResult>(_fr.fComp);
7904 //= dynamic_cast<RooFitResult*>( _fr.get<RooFitResult>() ? _fr->Clone() : fitResult()->Clone());
7905
7906 if (!fr) {
7907 // use name to reduce the fit result, if one given
7908 fr = std::dynamic_pointer_cast<RooFitResult>(strlen(_fr.GetName()) ? fitResult().reduced(_fr.GetName()).fComp
7909 : fitResult().fComp);
7910 }
7911
7912 if (!GETDMP(fr.get(),_finalPars)) {
7913 fr->setFinalParList(RooArgList());
7914 }
7915
7916 /// Oct2022: No longer doing this because want to allow fitResult to be used to get partial error
7917 // // need to add any floating parameters not included somewhere already in the fit result ...
7918 // RooArgList l;
7919 // for(auto& p : pars()) {
7920 // auto v = p->get<RooRealVar>();
7921 // if (!v) continue;
7922 // if (v->isConstant()) continue;
7923 // if (fr->floatParsFinal().find(v->GetName())) continue;
7924 // if (fr->_constPars && fr->_constPars->find(v->GetName())) continue;
7925 // l.add(*v);
7926 // }
7927 //
7928 // if (!l.empty()) {
7929 // RooArgList l2; l2.addClone(fr->floatParsFinal());
7930 // l2.addClone(l);
7931 // fr->setFinalParList(l2);
7932 // }
7933
7934 TMatrixTSym<Double_t> *prevCov = static_cast<TMatrixTSym<Double_t>*>(GETDMP(fr.get(),_VM));
7935
7936
7937 if (!prevCov || size_t(prevCov->GetNcols()) < fr->floatParsFinal().size()) {
7938 TMatrixDSym cov(fr->floatParsFinal().getSize());
7939 if (prevCov) {
7940 for (int i = 0; i < prevCov->GetNcols(); i++) {
7941 for (int j = 0; j < prevCov->GetNrows(); j++) {
7942 cov(i, j) = (*prevCov)(i, j);
7943 }
7944 }
7945 }
7946 int i = 0;
7947 for (auto &p : fr->floatParsFinal()) {
7948 if (!prevCov || i >= prevCov->GetNcols()) {
7949 cov(i, i) = pow(dynamic_cast<RooRealVar *>(p)->getError(), 2);
7950 }
7951 i++;
7952 }
7953 int covQualBackup = fr->covQual();
7954 fr->setCovarianceMatrix(cov);
7955 fr->setCovQual(covQualBackup);
7956 }
7957
7958 auto _coefs = coefs();
7959
7960 bool doBinWidth = false;
7961 auto ax = (binStart == -1 && binEnd == -1) ? nullptr : GetXaxis();
7962
7963 RooArgList normSet = obs().argList();
7964 // to give consistency with BuildHistogram method, should be only the axis var if defined
7965 if (ax) {
7966 normSet.clear();
7967 normSet.add(*dynamic_cast<RooAbsArg *>(ax->GetParent()));
7968 }
7969
7970 if (auto p = dynamic_cast<RooAbsPdf *>(o); ax && (p || _coefs.get() || o->getAttribute("density"))) {
7971 // pdfs of samples embedded in a sumpdf (aka have a coef) will convert their density value to a content
7972 doBinWidth = true;
7973 }
7974 if (binEnd == 0) {
7975 if (ax)
7976 binEnd = ax->GetNbins();
7977 else
7978 binEnd = binStart;
7979 }
7980 for (int bin = binStart; bin <= binEnd; bin++) {
7981 if (ax)
7982 dynamic_cast<RooAbsLValue *>(ax->GetParent())->setBin(bin - 1, ax->GetName());
7983 // if (!SetBin(bin)) { return out; }
7984
7985 double res;
7986 if (auto p = dynamic_cast<RooAbsPdf *>(o); p) {
7987 // fr->covarianceMatrix().Print();
7988 res = PdfWrapper(*p, _coefs.get<RooAbsReal>(), !ax).getSimplePropagatedError(*fr, normSet);
7989#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
7990 // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
7991 p->_normSet = nullptr;
7992#endif
7993 } else {
7994 res = o->getPropagatedError(*fr, normSet);
7995 // TODO: What if coef has error? - probably need a FuncWrapper class
7996 if (auto c = _coefs.get<RooAbsReal>(); c) {
7997 res *= c->getVal(normSet);
7998 }
7999 }
8000 if (doBinWidth) {
8001 res *= ax->GetBinWidth(bin);
8002 }
8003 out.push_back(res);
8004 }
8005
8006 return out;
8007}
8008
@ kButton1Down
Definition Buttons.h:17
int main()
Definition Prototype.cxx:12
#define d(i)
Definition RSha256.hxx:102
#define b(i)
Definition RSha256.hxx:100
#define f(i)
Definition RSha256.hxx:104
#define c(i)
Definition RSha256.hxx:101
#define g(i)
Definition RSha256.hxx:105
#define a(i)
Definition RSha256.hxx:99
#define h(i)
Definition RSha256.hxx:106
#define e(i)
Definition RSha256.hxx:103
#define ROOT_VERSION(a, b, c)
Definition RVersion.h:20
#define ROOT_VERSION_CODE
Definition RVersion.h:21
RooAbsReal * _func
Definition RooMinuit.h:90
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
bool Bool_t
Definition RtypesCore.h:63
float Float_t
Definition RtypesCore.h:57
constexpr Bool_t kFALSE
Definition RtypesCore.h:101
double Double_t
Definition RtypesCore.h:59
constexpr Bool_t kTRUE
Definition RtypesCore.h:100
const char Option_t
Definition RtypesCore.h:66
@ kGray
Definition Rtypes.h:65
@ kRed
Definition Rtypes.h:66
@ kBlack
Definition Rtypes.h:65
@ kGreen
Definition Rtypes.h:66
@ kWhite
Definition Rtypes.h:65
@ kYellow
Definition Rtypes.h:66
static void indent(ostringstream &buf, int indent_level)
R__EXTERN TEnv * gEnv
Definition TEnv.h:170
void Warning(const char *location, const char *msgfmt,...)
Use this function in warning situations.
Definition TError.cxx:241
#define gClient
Definition TGClient.h:157
@ kMBIconExclamation
Definition TGMsgBox.h:24
winID h TVirtualViewer3D TVirtualGLPainter p
winID h TVirtualViewer3D vv
Option_t Option_t option
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char filename
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t r
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t child
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void funcs
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t type
Option_t Option_t style
char name[80]
Definition TGX11.cxx:110
float xmin
float * q
float ymin
float xmax
float ymax
@ kCanDelete
Definition TObject.h:369
#define gROOT
Definition TROOT.h:405
char * Form(const char *fmt,...)
Formats a string in a circular formatting buffer.
Definition TString.cxx:2467
R__EXTERN TStyle * gStyle
Definition TStyle.h:414
R__EXTERN TSystem * gSystem
Definition TSystem.h:560
#define gPad
#define _(A, B)
Definition cfortran.h:108
void Set(Int_t nbins, const Float_t *xbins) override
Initialize axis with variable bins.
Definition xRooNode.cxx:700
void Set(Int_t nbins, const Double_t *xbins) override
Initialize axis with variable bins.
Definition xRooNode.cxx:694
Int_t FindFixBin(Double_t x) const override
Find bin number corresponding to abscissa x.
Definition xRooNode.cxx:717
Double_t GetBinUpEdge(Int_t bin) const override
Return up edge of bin.
Definition xRooNode.cxx:675
RooAbsRealLValue * rvar() const
Definition xRooNode.cxx:721
void SetTitle(const char *title) override
Set the title of the TNamed.
Definition xRooNode.cxx:686
Double_t GetBinLowEdge(Int_t bin) const override
Return low edge of bin.
Definition xRooNode.cxx:669
void Set(Int_t nbins, Double_t xmin, Double_t xmax) override
Initialize axis with fix bins.
Definition xRooNode.cxx:707
Int_t FindFixBin(const char *label) const override
Find bin number with label.
Definition xRooNode.cxx:716
RooAbsLValue * var() const
Definition xRooNode.cxx:720
const RooAbsBinning * binning() const
Definition xRooNode.cxx:714
const char * GetTitle() const override
Returns title of object.
Definition xRooNode.cxx:682
Double_t GetBinWidth(Int_t bin) const override
Return bin width.
Definition xRooNode.cxx:663
PadRefresher(TVirtualPad *p)
static int nExisting
A class which maps the current values of a RooRealVar (or a set of RooRealVars) to one of a number of...
Bool_t isBinnedDistribution(const RooArgSet &obs) const override
Tests if the distribution is binned. Unless overridden by derived classes, this always returns false.
RooRealProxy fCoef
double evaluate() const override
Evaluate this PDF / function / constant. Needs to be overridden by all derived classes.
PdfWrapper(RooAbsPdf &f, RooAbsReal *coef, bool expEvMode=false)
PdfWrapper(const PdfWrapper &other, const char *name=0)
virtual ~PdfWrapper()
Double_t getSimplePropagatedError(const RooFitResult &fr, const RooArgSet &nset_in) const
virtual TObject * clone(const char *newname) const override
RooRealProxy fFunc
std::list< Double_t > * binBoundaries(RooAbsRealLValue &obs, Double_t xlo, Double_t xhi) const override
Retrieve bin boundaries if this distribution is binned in obs.
The PiecewiseInterpolation is a class that can morph distributions into each other,...
RooAbsArg is the common abstract base class for objects that represent a value and a "shape" in RooFi...
Definition RooAbsArg.h:74
bool dependsOn(const RooAbsCollection &serverList, const RooAbsArg *ignoreArg=nullptr, bool valueOnly=false) const
Test whether we depend on (ie, are served by) any object in the specified collection.
void setStringAttribute(const Text_t *key, const Text_t *value)
Associate string 'value' to this object under key 'key'.
RooFit::OwningPtr< RooArgSet > getParameters(const RooAbsData *data, bool stripDisconnected=true) const
Create a list of leaf nodes in the arg tree starting with ourself as top node that don't match any of...
RooFit::OwningPtr< RooArgSet > getObservables(const RooArgSet &set, bool valueOnly=true) const
Given a set of possible observables, return the observables that this PDF depends on.
void SetName(const char *name) override
Set the name of the TNamed.
const Text_t * getStringAttribute(const Text_t *key) const
Get string attribute mapped under key 'key'.
bool getAttribute(const Text_t *name) const
Check if a named attribute is set. By default, all attributes are unset.
virtual RooAbsArg * cloneTree(const char *newname=nullptr) const
Clone tree expression of objects.
void setAttribute(const Text_t *name, bool value=true)
Set (default) or clear a named boolean attribute of this object.
virtual bool isFundamental() const
Is this object a fundamental type that can be added to a dataset? Fundamental-type subclasses overrid...
Definition RooAbsArg.h:247
RooAbsBinning is the abstract base class for RooRealVar binning definitions.
virtual double highBound() const =0
virtual double lowBound() const =0
RooAbsCategoryLValue is the common abstract base class for objects that represent a discrete value th...
A space to attach TBranches.
const char * getLabel() const
Retrieve current label. Use getCurrentLabel() for more clarity.
RooAbsCollection is an abstract container object that can hold multiple RooAbsArg objects.
virtual void removeAll()
Remove all arguments from our set, deleting them if we own them.
virtual bool remove(const RooAbsArg &var, bool silent=false, bool matchByNameOnly=false)
Remove the specified argument from our list.
RooAbsCollection * snapshot(bool deepCopy=true) const
Take a snap shot of current collection contents.
Int_t getSize() const
Return the number of elements in the collection.
virtual bool add(const RooAbsArg &var, bool silent=false)
Add the specified argument to list.
RooFIter fwdIterator() const
One-time forward iterator.
Storage_t::size_type size() const
void clear()
Clear contents. If the collection is owning, it will also delete the contents.
void setName(const char *name)
RooAbsArg * find(const char *name) const
Find object with given name in list.
RooAbsData is the common abstract base class for binned and unbinned datasets.
Definition RooAbsData.h:59
virtual const RooArgSet * get() const
Definition RooAbsData.h:103
virtual Int_t numEntries() const
Return number of entries in dataset, i.e., count unweighted entries.
Abstract base class for objects that are lvalues, i.e.
virtual std::list< std::string > getBinningNames() const =0
virtual double expectedEvents(const RooArgSet *nset) const
Return expected number of events to be used in calculation of extended likelihood.
RooAbsRealLValue is the common abstract base class for objects that represent a real value that may a...
RooAbsReal is the common abstract base class for objects that represent a real value and implements f...
Definition RooAbsReal.h:62
bool isSelectedComp() const
If true, the current pdf is a selected component (for use in plotting)
double getVal(const RooArgSet *normalisationSet=nullptr) const
Evaluate object.
Definition RooAbsReal.h:91
virtual void forceNumInt(bool flag=true)
Definition RooAbsReal.h:173
RooAddPdf is an efficient implementation of a sum of PDFs of the form.
Definition RooAddPdf.h:34
RooArgList is a container object that can hold multiple RooAbsArg objects.
Definition RooArgList.h:22
RooAbsArg * at(Int_t idx) const
Return object at given index, or nullptr if index is out of range.
Definition RooArgList.h:110
RooArgProxy is the abstract interface for RooAbsArg proxy classes.
Definition RooArgProxy.h:24
RooArgSet is a container object that can hold multiple RooAbsArg objects.
Definition RooArgSet.h:55
RooArgSet * snapshot(bool deepCopy=true) const
Use RooAbsCollection::snapshot(), but return as RooArgSet.
Definition RooArgSet.h:178
Class RooBinning is an implements RooAbsBinning in terms of an array of boundary values,...
Definition RooBinning.h:27
RooCategory is an object to represent discrete states.
Definition RooCategory.h:28
bool defineType(const std::string &label)
Define a state with given name.
static TClass * Class()
RooCmdArg is a named container for two doubles, two integers two object points and three string point...
Definition RooCmdArg.h:26
static const RooCmdArg & none()
Return reference to null argument.
Definition RooCmdArg.cxx:51
RooConstVar represent a constant real-valued object.
Definition RooConstVar.h:26
The RooDataHist is a container class to hold N-dimensional binned data.
Definition RooDataHist.h:39
RooDataSet is a container class to hold unbinned data.
Definition RooDataSet.h:57
A one-time forward iterator working on RooLinkedList or RooAbsCollection.
RooAbsArg * next()
Return next element or nullptr if at end.
RooFitResult is a container class to hold the input and output of a PDF fit to a dataset.
void setCovQual(Int_t val)
const TMatrixDSym & covarianceMatrix() const
Return covariance matrix.
TMatrixDSym reducedCovarianceMatrix(const RooArgList &params) const
Return a reduced covariance matrix (Note that Vred is a simple sub-matrix of V, row/columns are order...
void setCovarianceMatrix(TMatrixDSym &V)
Store externally provided correlation matrix in this RooFitResult ;.
const RooArgList & constPars() const
Return list of constant parameters.
Int_t covQual() const
Return MINUIT quality code of covariance matrix.
const RooArgList & floatParsFinal() const
Return list of floating parameters after fit.
Int_t status() const
Return MINUIT status code.
void setFinalParList(const RooArgList &list)
Fill the list of final values of the floating parameters.
static std::shared_ptr< RooLinkedList > createNLLOptions()
Definition xRooFit.cxx:388
static xRooNLLVar createNLL(const std::shared_ptr< RooAbsPdf > pdf, const std::shared_ptr< RooAbsData > data, const RooLinkedList &nllOpts)
Definition xRooFit.cxx:66
static std::pair< std::shared_ptr< RooAbsData >, std::shared_ptr< const RooAbsCollection > > generateFrom(RooAbsPdf &pdf, const std::shared_ptr< const RooFitResult > &fr, bool expected=false, int seed=0)
Definition xRooFit.cxx:115
void _Add_(const char *name, const char *opt)
void SetTitle(const char *title) override
Set the title of the TNamed.
Definition xRooNode.h:120
xRooNLLVar nll(const xRooNode &_data, std::initializer_list< RooCmdArg > nllOpts) const
void SetName(const char *name) override
Set the name of the TNamed.
auto end() const -> decltype(std::vector< std::shared_ptr< xRooNode > >::end())
Definition xRooNode.h:156
TGListTreeItem * GetTreeItem(TBrowser *b) const
xRooNode Multiply(const xRooNode &child, Option_t *opt="")
xRooNode Remove(const xRooNode &child)
std::shared_ptr< TAxis > fXAxis
only here so can have char* GetRange return so can return nullptr for no range set (required for RooC...
Definition xRooNode.h:366
xRooNode Combine(const xRooNode &rhs)
std::shared_ptr< T > acquireNew(Args &&...args)
Definition xRooNode.h:194
bool SetBinData(int bin, double value, const char *dataName="obsData")
bool fInterrupted
appears that if was fXaxis then dialog box for SetXaxis will take as current value
Definition xRooNode.h:368
xRooNode Add(const xRooNode &child, Option_t *opt="")
const char * GetIconName() const override
Returns mime type name of object.
Definition xRooNode.cxx:931
void Draw(Option_t *opt="") override
Default Draw method for all objects.
double GetBinError(int bin, const xRooNode &fr="") const
std::vector< double > GetBinErrors(int binStart=1, int binEnd=0, const xRooNode &fr="") const
void _ShowVars_(Bool_t set=kTRUE)
Definition xRooNode.cxx:621
TGraph * BuildGraph(RooAbsLValue *v=nullptr, bool includeZeros=false, TVirtualPad *fromPad=nullptr) const
std::shared_ptr< xRooNode > find(const std::string &name) const
const char * GetNodeType() const
bool SetBinContent(int bin, double value, const char *par=nullptr, double parVal=1)
void Checked(TObject *obj, bool val)
Definition xRooNode.cxx:378
void Print(Option_t *opt="") const override
Print TNamed name and title.
void SaveAs(const char *filename="", Option_t *option="") const override
Save this object in the file specified by filename.
const std::shared_ptr< xRooNode > & at(size_t idx, bool browseResult=true) const
Definition xRooNode.h:132
TGListTree * GetListTree(TBrowser *b) const
void Inspect() const override
Dump contents of this object in a graphics canvas.
auto begin() const -> decltype(std::vector< std::shared_ptr< xRooNode > >::begin())
Definition xRooNode.h:152
bool SetXaxis(const RooAbsBinning &binning)
void _fitTo_(const char *datasetName="", const char *constParValues="")
std::shared_ptr< xRooNode > fProvider
Definition xRooNode.h:371
std::vector< double > GetBinContents(int binStart=1, int binEnd=0) const
xRooNode fitResult(const char *opt="") const
std::shared_ptr< TObject > fComp
Definition xRooNode.h:353
xRooNode coords(bool setVals=true) const
xRooNode Vary(const xRooNode &child)
xRooNode Constrain(const xRooNode &child)
static std::map< std::string, std::tuple< std::function< double(double, double, double)>, bool > > auxFunctions
Definition xRooNode.h:57
void SetFitResult(const RooFitResult *fr=nullptr)
bool IsFolder() const override
Returns kTRUE in case object contains browsable objects (like containers or lists of other objects).
Definition xRooNode.cxx:648
bool SetBinError(int bin, double value)
bool SetContents(const TObject &obj)
Definition xRooNode.h:258
void _Vary_(const char *what)
RooWorkspace * ws() const
xRooNode shallowCopy(const std::string &name, std::shared_ptr< xRooNode > parent=nullptr)
void _generate_(const char *name="", bool expected=false)
TH1 * BuildHistogram(RooAbsLValue *v=nullptr, bool empty=false, bool errors=false, int binStart=1, int binEnd=0) const
std::shared_ptr< TObject > getObject(const std::string &name, const std::string &type="") const
Definition xRooNode.cxx:724
std::shared_ptr< TStyle > style(TObject *initObject=nullptr) const
void SetHidden(Bool_t set=kTRUE)
bool contains(const std::string &name) const
std::vector< std::shared_ptr< xRooNode > > fBrowsables
Definition xRooNode.h:377
std::shared_ptr< xRooNode > operator[](size_t idx)
Definition xRooNode.h:148
std::shared_ptr< xRooNode > parentPdf() const
like a parent but only for use by getObject
std::pair< double, double > IntegralAndError(const xRooNode &fr="", const char *rangeName=nullptr) const
xRooNode & operator=(const TObject &o)
void _SetBinContent_(int bin, double value, const char *par="", double parVal=1)
std::shared_ptr< TObject > acquire(const std::shared_ptr< TObject > &arg, bool checkFactory=false, bool mustBeNew=false)
xRooNode(const char *type, const char *name, const char *title="")
xRooNode reduced(const std::string &range="") const
std::function< xRooNode(xRooNode *)> fBrowseOperation
Definition xRooNode.h:378
void Browse(TBrowser *b=nullptr) override
Browse object. May be overridden for another default action.
Definition xRooNode.cxx:446
double GetBinData(int bin, const char *dataName="obsData")
TClass * IsA() const override
Definition xRooNode.h:380
void SetRange(const char *range, double low=std::numeric_limits< double >::quiet_NaN(), double high=std::numeric_limits< double >::quiet_NaN())
static void SetAuxFunction(const char *title, const std::function< double(double, double, double)> &func, bool symmetrize=false)
Definition xRooNode.cxx:145
std::shared_ptr< TObject > convertForAcquisition(xRooNode &acquirer, const char *opt="") const
const char * GetRange() const
std::shared_ptr< xRooNode > fParent
Definition xRooNode.h:356
A RooFormulaVar is a generic implementation of a real-valued object, which takes a RooArgList of serv...
Plain Gaussian p.d.f.
Definition RooGaussian.h:24
RooHistFunc implements a real-valued function sampled from a multidimensional histogram.
Definition RooHistFunc.h:31
When using RooFit, statistical models can be conveniently handled and stored as a RooWorkspace.
bool importJSON(std::string const &filename)
bool exportJSON(std::string const &fileName)
RooLinkedList is an collection class for internal use, storing a collection of RooAbsArg pointers in ...
Int_t GetSize() const
TObject * At(int index) const
Return object stored in sequential position given by index.
TIterator * MakeIterator(bool forward=true) const
Create a TIterator for this list.
static RooMsgService & instance()
Return reference to singleton instance.
StreamConfig & getStream(Int_t id)
void setGlobalKillBelow(RooFit::MsgLevel level)
RooFit::MsgLevel globalKillBelow() const
Class RooObjCacheManager is an implementation of class RooCacheManager<RooAbsCacheElement> and specia...
Poisson pdf.
Definition RooPoisson.h:19
RooProdPdf is an efficient implementation of a product of PDFs of the form.
Definition RooProdPdf.h:33
A RooProduct represents the product of a given set of RooAbsReal objects.
Definition RooProduct.h:29
static TClass * Class()
The class RooRealSumPdf implements a PDF constructed from a sum of functions:
RooRealVar represents a variable that can be changed from the outside.
Definition RooRealVar.h:40
void setVal(double value) override
Set value of variable to 'value'.
void setError(double value)
Definition RooRealVar.h:64
static TClass * Class()
double getError() const
Definition RooRealVar.h:62
void setRange(const char *name, double min, double max)
Set a fit or plotting range.
RooSimultaneous facilitates simultaneous fitting of multiple PDFs to subsets of a given dataset.
RooStringVar is a RooAbsArg implementing string values.
RooUniformBinning is an implementation of RooAbsBinning that provides a uniform binning in 'n' bins b...
The RooWorkspace is a persistable container for RooFit projects.
const std::map< std::string, RooArgSet > & sets() const
bool removeSet(const char *name)
Remove a named set from the workspace.
static TClass * Class()
RooFactoryWSTool & factory()
Return instance to factory tool.
virtual void SetTitleOffset(Float_t offset=1)
Set distance between the axis and the axis title.
Definition TAttAxis.cxx:298
virtual Style_t GetTitleFont() const
Definition TAttAxis.h:47
virtual void SetLabelSize(Float_t size=0.04)
Set size of axis labels.
Definition TAttAxis.cxx:203
virtual void SetTitleSize(Float_t size=0.04)
Set size of axis title.
Definition TAttAxis.cxx:309
virtual Float_t GetTitleSize() const
Definition TAttAxis.h:44
virtual Float_t GetLabelSize() const
Definition TAttAxis.h:41
virtual Float_t GetTickLength() const
Definition TAttAxis.h:45
virtual Float_t GetTitleOffset() const
Definition TAttAxis.h:43
virtual void SetTickLength(Float_t length=0.03)
Set tick mark length.
Definition TAttAxis.cxx:284
virtual void SetNdivisions(Int_t n=510, Bool_t optim=kTRUE)
Set the number of divisions for this axis.
Definition TAttAxis.cxx:233
Fill Area Attributes class.
Definition TAttFill.h:19
virtual void SetFillColor(Color_t fcolor)
Set the fill area color.
Definition TAttFill.h:37
virtual void SetFillStyle(Style_t fstyle)
Set the fill area style.
Definition TAttFill.h:39
Line Attributes class.
Definition TAttLine.h:18
virtual void SetLineColor(Color_t lcolor)
Set the line color.
Definition TAttLine.h:40
Marker Attributes class.
Definition TAttMarker.h:19
virtual void SetMarkerSize(Size_t msize=1)
Set the marker size.
Definition TAttMarker.h:45
Class to manage histogram axis.
Definition TAxis.h:30
virtual void SetBinLabel(Int_t bin, const char *label)
Set label for bin.
Definition TAxis.cxx:851
const char * GetTitle() const override
Returns title of object.
Definition TAxis.h:130
TAxis()
Default constructor.
Definition TAxis.cxx:50
Double_t GetXmax() const
Definition TAxis.h:135
const char * GetBinLabel(Int_t bin) const
Return label for bin.
Definition TAxis.cxx:440
virtual void Set(Int_t nbins, Double_t xmin, Double_t xmax)
Initialize axis with fix bins.
Definition TAxis.cxx:759
virtual Int_t FindFixBin(Double_t x) const
Find bin number corresponding to abscissa x.
Definition TAxis.cxx:419
Double_t GetXmin() const
Definition TAxis.h:134
Using a TBrowser one can browse all ROOT objects.
Definition TBrowser.h:37
static TCanvas * MakeDefCanvas()
Static function to build a default canvas.
Definition TCanvas.cxx:1504
@ kShowEventStatus
Definition TCanvas.h:88
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:81
Bool_t InheritsFrom(const char *cl) const override
Return kTRUE if this class inherits from a class with name "classname".
Definition TClass.cxx:4874
static TClass * GetClass(const char *name, Bool_t load=kTRUE, Bool_t silent=kFALSE)
Static method returning pointer to TClass of the specified class name.
Definition TClass.cxx:2968
void SetName(const char *name)
virtual Int_t GetEntries() const
virtual void RemoveAll(TCollection *col)
Remove all objects in collection col from this collection.
virtual void SetValue(const char *name, const char *value, EEnvLevel level=kEnvChange, const char *type=nullptr)
Set the value of a resource or create a new resource.
Definition TEnv.cxx:736
TExec is a utility class that can be used to execute a C++ command when some event happens in a pad.
Definition TExec.h:26
A ROOT file is a suite of consecutive data records (TKey instances) with a well defined format.
Definition TFile.h:51
System file browser, used as TRootBrowser plug-in.
void AddFSDirectory(const char *entry, const char *path=nullptr, Option_t *opt="")
Add file system directory in the list tree.
A list tree is a widget that can contain a number of items arranged in a tree structure.
Definition TGListTree.h:195
TGraph with asymmetric error bars.
virtual void SetPointError(Double_t exl, Double_t exh, Double_t eyl, Double_t eyh)
Set ex and ey values for point pointed by the mouse.
A TGraphErrors is a TGraph with error bars.
Double_t GetErrorYlow(Int_t bin) const override
It returns the error along Y at point i.
Double_t GetErrorYhigh(Int_t bin) const override
It returns the error along Y at point i.
A TGraph is an object made of two arrays X and Y with npoints each.
Definition TGraph.h:41
virtual void SetPoint(Int_t i, Double_t x, Double_t y)
Set x and y values for point number i.
Definition TGraph.cxx:2325
Int_t GetN() const
Definition TGraph.h:129
void SetName(const char *name="") override
Set graph name.
Definition TGraph.cxx:2364
void Draw(Option_t *chopt="") override
Draw this graph with its current attributes.
Definition TGraph.cxx:808
virtual Double_t GetPointY(Int_t i) const
Get y value for point i.
Definition TGraph.cxx:1539
void SetTitle(const char *title="") override
Change (i.e.
Definition TGraph.cxx:2380
virtual void SetPointX(Int_t i, Double_t x)
Set x value for point i.
Definition TGraph.cxx:2349
virtual void SetEditable(Bool_t editable=kTRUE)
if editable=kFALSE, the graph cannot be modified with the mouse by default a TGraph is editable
Definition TGraph.cxx:2284
1-D histogram with a double per channel (see TH1 documentation)}
Definition TH1.h:620
1-D histogram with a float per channel (see TH1 documentation)}
Definition TH1.h:577
TH1 is the base class of all histogram classes in ROOT.
Definition TH1.h:58
virtual Double_t GetBinCenter(Int_t bin) const
Return bin center for 1D histogram.
Definition TH1.cxx:9007
void SetTitle(const char *title) override
Change (i.e.
Definition TH1.cxx:6700
virtual Double_t GetBinError(Int_t bin) const
Return value of error associated to bin number bin.
Definition TH1.cxx:8929
static void AddDirectory(Bool_t add=kTRUE)
Sets the flag controlling the automatic add of histograms in memory.
Definition TH1.cxx:1267
@ kNoTitle
Don't draw the histogram title.
Definition TH1.h:168
virtual void Reset(Option_t *option="")
Reset this histogram: contents, errors, etc.
Definition TH1.cxx:7091
TAxis * GetXaxis()
Definition TH1.h:322
virtual Int_t GetNbinsX() const
Definition TH1.h:295
virtual void SetMaximum(Double_t maximum=-1111)
Definition TH1.h:400
virtual void SetBinError(Int_t bin, Double_t error)
Set the bin Error Note that this resets the bin eror option to be of Normal Type and for the non-empt...
Definition TH1.cxx:9072
virtual Int_t Fill(Double_t x)
Increment bin with abscissa X by 1.
Definition TH1.cxx:3338
TAxis * GetYaxis()
Definition TH1.h:323
void Draw(Option_t *option="") override
Draw this histogram with options.
Definition TH1.cxx:3060
virtual void SetMinimum(Double_t minimum=-1111)
Definition TH1.h:401
virtual void SetBinContent(Int_t bin, Double_t content)
Set bin content see convention for numbering bins in TH1::GetBin In case the bin number is greater th...
Definition TH1.cxx:9088
TList * GetListOfFunctions() const
Definition TH1.h:242
void SetName(const char *name) override
Change the name of this histogram.
Definition TH1.cxx:8826
virtual Double_t GetBinContent(Int_t bin) const
Return content of bin number bin.
Definition TH1.cxx:5025
TObject * Clone(const char *newname="") const override
Make a complete copy of the underlying object.
Definition TH1.cxx:2727
virtual Bool_t Divide(TF1 *f1, Double_t c1=1)
Performs the operation: this = this/(c1*f1) if errors are defined (see TH1::Sumw2),...
Definition TH1.cxx:2815
virtual void Sumw2(Bool_t flag=kTRUE)
Create structure to store sum of squares of weights.
Definition TH1.cxx:8886
static Bool_t AddDirectoryStatus()
Static function: cannot be inlined on Windows/NT.
Definition TH1.cxx:735
virtual void SetStats(Bool_t stats=kTRUE)
Set statistics option on/off.
Definition TH1.cxx:8856
The Histogram stack class.
Definition THStack.h:38
TList * GetHists() const
Definition THStack.h:70
virtual void Add(TH1 *h, Option_t *option="")
add a new histogram to the list Only 1-d and 2-d histograms currently supported.
Definition THStack.cxx:364
void Draw(Option_t *chopt="") override
Draw this multihist with its current attributes.
Definition THStack.cxx:449
Book space in a file, create I/O buffers, to fill them, (un)compress them.
Definition TKey.h:28
Storage class for one entry of a TLegend.
This class displays a legend box (TPaveText) containing several legend entries.
Definition TLegend.h:23
A doubly linked list.
Definition TList.h:38
void Add(TObject *obj) override
Definition TList.h:81
TObject * At(Int_t idx) const override
Returns the object at position idx. Returns 0 if idx is out of range.
Definition TList.cxx:357
Int_t GetNrows() const
Int_t GetNcols() const
TMatrixTSym.
Definition TMatrixTSym.h:34
A TMultiGraph is a collection of TGraph (or derived) objects.
Definition TMultiGraph.h:34
virtual void Add(TGraph *graph, Option_t *chopt="")
Add a new graph to the list of graphs.
void Draw(Option_t *chopt="") override
Draw this multigraph with its current attributes.
The TNamed class is the base class for all named ROOT classes.
Definition TNamed.h:29
TObject * Clone(const char *newname="") const override
Make a clone of an object using the Streamer facility.
Definition TNamed.cxx:74
virtual void SetTitle(const char *title="")
Set the title of the TNamed.
Definition TNamed.cxx:164
const char * GetName() const override
Returns name of object.
Definition TNamed.h:47
const char * GetTitle() const override
Returns title of object.
Definition TNamed.h:48
virtual void SetName(const char *name)
Set the name of the TNamed.
Definition TNamed.cxx:140
virtual void SetNameTitle(const char *name, const char *title)
Set all the TNamed parameters (name and title).
Definition TNamed.cxx:154
Mother of all ROOT objects.
Definition TObject.h:41
virtual void Inspect() const
Dump contents of this object in a graphics canvas.
Definition TObject.cxx:546
virtual const char * GetName() const
Returns name of object.
Definition TObject.cxx:439
R__ALWAYS_INLINE Bool_t TestBit(UInt_t f) const
Definition TObject.h:201
virtual TObject * Clone(const char *newname="") const
Make a clone of an object using the Streamer facility.
Definition TObject.cxx:223
virtual const char * ClassName() const
Returns name of class to which the object belongs.
Definition TObject.cxx:207
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
Definition TObject.cxx:956
virtual void Delete(Option_t *option="")
Delete this object.
Definition TObject.cxx:248
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition TObject.cxx:774
virtual Bool_t InheritsFrom(const char *classname) const
Returns kTRUE if object inherits from class "classname".
Definition TObject.cxx:525
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:970
virtual const char * GetTitle() const
Returns title of object.
Definition TObject.cxx:483
virtual void Draw(Option_t *option="")
Default Draw method for all objects.
Definition TObject.cxx:274
virtual void Print(Option_t *option="") const
This method must be overridden when a class wants to print itself.
Definition TObject.cxx:630
@ kCanDelete
if object in a list can be deleted
Definition TObject.h:62
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition TObject.cxx:944
The most important graphics class in the ROOT system.
Definition TPad.h:28
TVirtualPad * cd(Int_t subpadnumber=0) override
Set Current pad.
Definition TPad.cxx:597
This is the ROOT implementation of the Qt object communication mechanism (see also http://www....
Definition TQObject.h:48
Regular expression class.
Definition TRegexp.h:31
This class creates a ROOT object browser, constituted by three main tabs.
Stopwatch class.
Definition TStopwatch.h:28
Double_t RealTime()
Stop the stopwatch (if it is running) and return the realtime (in seconds) passed between the start a...
void Start(Bool_t reset=kTRUE)
Start the stopwatch.
void Stop()
Stop the stopwatch.
Provides iteration through tokens of a given string.
Definition TPRegexp.h:143
Bool_t NextToken()
Get the next token, it is stored in this TString.
Definition TPRegexp.cxx:975
Basic string class.
Definition TString.h:139
Ssiz_t Length() const
Definition TString.h:421
Bool_t IsDec() const
Returns true if all characters in string are decimal digits (0-9).
Definition TString.cxx:1918
void ToLower()
Change string to lower-case.
Definition TString.cxx:1170
Int_t Atoi() const
Return integer value of string.
Definition TString.cxx:1966
Bool_t EndsWith(const char *pat, ECaseCompare cmp=kExact) const
Return true if string ends with the specified string.
Definition TString.cxx:2222
Double_t Atof() const
Return floating-point value contained in string.
Definition TString.cxx:2032
TString & Replace(Ssiz_t pos, Ssiz_t n, const char *s)
Definition TString.h:694
const char * Data() const
Definition TString.h:380
TString & ReplaceAll(const TString &s1, const TString &s2)
Definition TString.h:704
Bool_t BeginsWith(const char *s, ECaseCompare cmp=kExact) const
Definition TString.h:627
Int_t CountChar(Int_t c) const
Return number of times character c occurs in the string.
Definition TString.cxx:508
static TString Format(const char *fmt,...)
Static method which formats a string using a printf style format descriptor and return a TString.
Definition TString.cxx:2356
Bool_t Contains(const char *pat, ECaseCompare cmp=kExact) const
Definition TString.h:636
Ssiz_t Index(const char *pat, Ssiz_t i=0, ECaseCompare cmp=kExact) const
Definition TString.h:651
TStyle objects may be created to define special styles.
Definition TStyle.h:29
void SetPaintTextFormat(const char *format="g")
Definition TStyle.h:370
Float_t GetLabelSize(Option_t *axis="X") const
Return label size.
Definition TStyle.cxx:1101
Float_t GetPadRightMargin() const
Definition TStyle.h:206
Style_t GetTitleFont(Option_t *axis="X") const
Return title font.
Definition TStyle.cxx:1165
Bool_t GetHistMinimumZero() const
Definition TStyle.h:229
Float_t GetPadLeftMargin() const
Definition TStyle.h:205
Float_t GetTitleYSize() const
Definition TStyle.h:271
static TClass * Class()
Double_t GetHistTopMargin() const
Definition TStyle.h:230
Float_t GetPadBottomMargin() const
Definition TStyle.h:203
const char * GetPaintTextFormat() const
Definition TStyle.h:242
Float_t GetPadTopMargin() const
Definition TStyle.h:204
virtual Bool_t ExpandPathName(TString &path)
Expand a pathname getting rid of special shell characters like ~.
Definition TSystem.cxx:1277
virtual Bool_t AccessPathName(const char *path, EAccessMode mode=kFileExists)
Returns FALSE if one can access a file using the specified access mode.
Definition TSystem.cxx:1299
virtual Bool_t ProcessEvents()
Process pending events (GUI, timers, sockets).
Definition TSystem.cxx:419
The TTimeStamp encapsulates seconds and ns since EPOCH.
Definition TTimeStamp.h:45
void Add(const TTimeStamp &offset)
Add "offset" as a delta time.
const char * AsString(const Option_t *option="") const
Return the date & time as a string.
This class defines a UUID (Universally Unique IDentifier), also known as GUIDs (Globally Unique IDent...
Definition TUUID.h:42
static TVirtualPadEditor * GetPadEditor(Bool_t load=kTRUE)
Returns the pad editor dialog. Static method.
TVirtualPad is an abstract base class for the Pad and Canvas classes.
Definition TVirtualPad.h:51
virtual void Modified(Bool_t flag=1)=0
virtual TList * GetListOfPrimitives() const =0
virtual void SetPad(const char *name, const char *title, Double_t xlow, Double_t ylow, Double_t xup, Double_t yup, Color_t color=35, Short_t bordersize=5, Short_t bordermode=-1)=0
virtual TVirtualPad * cd(Int_t subpadnumber=0)=0
void Draw(Option_t *option="") override=0
Default Draw method for all objects.
const char * GetName() const override=0
Returns name of object.
virtual TVirtualPad * GetPad(Int_t subpadnumber) const =0
virtual void Divide(Int_t nx=1, Int_t ny=1, Float_t xmargin=0.01, Float_t ymargin=0.01, Int_t color=0)=0
virtual Int_t GetLogy() const =0
virtual void SetLogy(Int_t value=1)=0
virtual TObject * GetPrimitive(const char *name) const =0
virtual void SetBorderSize(Short_t bordersize)=0
virtual void SetName(const char *name)=0
TClass * IsA() const override
RooCmdArg RecycleConflictNodes(bool flag=true)
RooConstVar & RooConst(double val)
RooCmdArg Embedded(bool flag=true)
RooCmdArg GlobalObservables(Args_t &&... argsOrArgSet)
RooCmdArg Range(const char *rangeName, bool adjustNorm=true)
const Double_t sigma
Double_t y[n]
Definition legend1.C:17
Double_t x[n]
Definition legend1.C:17
const Int_t n
Definition legend1.C:16
TGraphErrors * gr
Definition legend1.C:25
TH1F * h1
Definition legend1.C:5
#define F(x, y, z)
bool EndsWith(const std::string &theString, const std::string &theSubstring)
TClass * GetClass(T *)
Definition TClass.h:659
The namespace RooFit contains mostly switches that change the behaviour of functions of PDFs (or othe...
Definition Common.h:18
MsgLevel
Verbosity level for RooMsgService::StreamConfig in RooMsgService.
@ NumIntegration
Double_t Prob(Double_t chi2, Int_t ndf)
Computation of the probability for a certain Chi-squared (chi2) and number of degrees of freedom (ndf...
Definition TMath.cxx:637
Double_t ChisquareQuantile(Double_t p, Double_t ndf)
Evaluate the quantiles of the chi-squared probability distribution function.
Definition TMath.cxx:2189
Definition first.py:1
Definition graph.py:1
#define BEGIN_XROOFIT_NAMESPACE
Definition Config.h:24
#define END_XROOFIT_NAMESPACE
Definition Config.h:25
static const char * what
Definition stlLoader.cc:6
RooAbsBinning * b
RooRealVar * x
void removeTopic(RooFit::MsgTopic oldTopic)
th1 Draw()
TLine lv
Definition textalign.C:5
TLine l
Definition textangle.C:4
static uint64_t sum(uint64_t i)
Definition Factory.cxx:2345
#define GETWS(a)
#define GETWSSETS(w)
void(* gOldHandlerr)(int)
void addLegendEntry(TObject *o, const char *title, const char *opt)
void buildHistogramInterrupt(int signum)
auto GETWSSNAPSHOTS(RooWorkspace *w)
Definition xRooNode.cxx:67
auto GETACTBROWSER(TRootBrowser *b)
Definition xRooNode.cxx:68
#define GETDMP(o, m)
Definition xRooNode.cxx:71
auto GETROOTDIR(TGFileBrowser *b)
Definition xRooNode.cxx:69
const xRooNode * runningNode
auto GETLISTTREE(TGFileBrowser *b)
Definition xRooNode.cxx:70
const T & _or_func(const T &a, const T &b)
Definition xRooNode.cxx:152
Bool_t TopRightPlaceBox(TPad *p, TObject *o, Double_t w, Double_t h, Double_t &xl, Double_t &yb)
TLegend * getLegend(bool create=true, bool doPaint=false)