Logo ROOT  
Reference Guide
 
All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros Modules Pages
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/** \class ROOT::Experimental::XRooFit::xRooNode
14\ingroup xroofit
15
16The xRooNode class is designed to wrap over a TObject and provide functionality to aid with interacting with that
17object, particularly in the case where the object is a RooFit class instance. It is a smart pointer to the object, so
18you have access to all the methods of the object too.
19
20xRooNode is designed to work in both python and C++, but examples below are given in python because that is imagined
21 be the most common way to use the xRooFit API.
22
23-# [Exploring workspaces](\ref exploring-workspaces)
24
25\anchor exploring-workspaces
26## Exploring workspaces
27
28An existing workspace file (either a ROOT file containing a RooWorkspace, or a json HS3 file) can be opened using
29 xRooNode like this:
30
31\code{.py}
32from ROOT.Experimental import XRooFit
33w = XRooFit.xRooNode("workspace.root") # or can use workspace.json for HS3
34\endcode
35
36 You can explore the content of the workspace somewhat like you would a file system: each node contains sub-nodes,
37 which you can interact with to explore ever deeper. The most relevant methods for navigating the workspace and
38exploring the content are:
39
40
41
42
43 */
44
45#include "RVersion.h"
46
47#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
48
49#define protected public
50#include "TRootBrowser.h"
52#define private public
53#include "RooAbsArg.h"
54#include "RooWorkspace.h"
57#include "RooProdPdf.h"
58#include "TGFileBrowser.h"
59#include "RooFitResult.h"
60#include "TPad.h"
61#undef private
62#include "RooAddPdf.h"
63#include "RooRealSumPdf.h"
64#include "RooProduct.h"
65#include "RooHistFunc.h"
66#include "RooConstVar.h"
67#include "RooSimultaneous.h"
68#undef protected
69
70#define GETWS(a) a->_myws
71#define GETWSSETS(w) w->_namedSets
72#define GETWSSNAPSHOTS(w) w->_snapshots
73#define GETACTBROWSER(b) b->fActBrowser
74#define GETROOTDIR(b) b->fRootDir
75#define GETLISTTREE(b) b->fListTree
76#define GETDMP(o, m) o->m
77
78#else
79
80#include "RooAbsArg.h"
81#include "RooWorkspace.h"
82#include "RooFitResult.h"
83#include "RooConstVar.h"
84#include "RooHistFunc.h"
85#include "RooRealSumPdf.h"
86#include "RooSimultaneous.h"
87#include "RooAddPdf.h"
88#include "RooProduct.h"
89#include "TPad.h"
93#include "RooProdPdf.h"
94#include "TRootBrowser.h"
95#include "TGFileBrowser.h"
96#include "TF1.h"
98
100{
101 return a->workspace();
102}
104{
105 return w->sets();
106}
108{
109 return w->getSnapshots();
110}
112{
113 return b->GetActBrowser();
114}
116{
117 return b->GetRootDir();
118}
120{
121 return b->GetListTree();
122}
123#define GETDMP(o, m) \
124 *reinterpret_cast<void **>(reinterpret_cast<unsigned char *>(o) + o->Class()->GetDataMemberOffset(#m))
125
126#endif
127
128#include "RooAddition.h"
129
130#include "RooCategory.h"
131#include "RooRealVar.h"
132#include "RooStringVar.h"
133#include "RooBinning.h"
134#include "RooUniformBinning.h"
135
136#include "RooAbsData.h"
137#include "RooDataHist.h"
138#include "RooDataSet.h"
139
140#include "xRooFit/xRooNode.h"
141#include "xRooFit/xRooFit.h"
142
143#include "TH1.h"
144#include "TBrowser.h"
145#include "TROOT.h"
146#include "TQObject.h"
147#include "TAxis.h"
148#include "TGraphAsymmErrors.h"
149#include "TMath.h"
150#include "TPRegexp.h"
151#include "TRegexp.h"
152#include "TExec.h"
153#include "TPaveText.h"
154#include "TLatex.h"
155
156#include "TGListTree.h"
157#include "TGMsgBox.h"
158#include "TGedEditor.h"
159#include "TGMimeTypes.h"
160#include "TH2.h"
161#include "RooExtendPdf.h"
162#include "RooExtendedBinding.h"
163
165
166#include "coutCapture.h"
167
168// #include "RooFitTrees/RooFitResultTree.h"
169// #include "RooFitTrees/RooDataTree.h"
170#include "TFile.h"
171#include "TSystem.h"
172#include "TKey.h"
173#include "TEnv.h"
174#include "TStyle.h"
175
176#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
178#endif
179
180#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
181#include "RooBinSamplingPdf.h"
182#endif
183
184#include "RooPoisson.h"
185#include "RooGaussian.h"
186#include "RooFormulaVar.h"
187#include "RooGenericPdf.h"
188#include "TVectorD.h"
189#include "TStopwatch.h"
190#include "TTimeStamp.h"
191
192#include <csignal>
193
194#include "TCanvas.h"
195#include "THStack.h"
196
197#include "TLegend.h"
198#include "TLegendEntry.h"
199#include "TGraphErrors.h"
200#include "TMultiGraph.h"
201#include "TFrame.h"
202#include "RooProjectedPdf.h"
203#include "TMemFile.h"
204#include "TGaxis.h"
205#include "TPie.h"
206// #include <thread>
207// #include <future>
208
209#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
210#include "RooNaNPacker.h"
211#endif
212
214
215xRooNode::InteractiveObject *xRooNode::gIntObj = nullptr;
216std::map<std::string, std::tuple<std::function<double(double, double, double)>, bool>> xRooNode::auxFunctions;
217void xRooNode::SetAuxFunction(const char *title, const std::function<double(double, double, double)> &func,
218 bool symmetrize)
219{
220 auxFunctions[title] = std::make_tuple(func, symmetrize);
221}
222
223template <typename T>
224const T &_or_func(const T &a, const T &b)
225{
226 if (a)
227 return a;
228 return b;
229}
230
231////////////////////////////////////////////////////////////////////////////////
232/// Create new object of type classname, with given name and title, and own-wrap it
233/// i.e. the xRooNode will delete the object when the node (and any that reference it) is destroyed
234///
235/// \param classname : the type of the object to create
236/// \param name : the name to give the object
237/// \param title : the title to give the object
238
239xRooNode::xRooNode(const char *classname, const char *name, const char *title)
240 : xRooNode(name, std::shared_ptr<TObject>(TClass::GetClass(classname)
241 ? reinterpret_cast<TObject *>(TClass::GetClass(classname)->New())
242 : nullptr,
243 [](TObject *o) {
244 if (auto w = dynamic_cast<RooWorkspace *>(o); w) {
245#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
246 w->_embeddedDataList.Delete();
247#endif
248 xRooNode(*w, std::make_shared<xRooNode>()).sterilize();
249 }
250 if (o)
251 delete o;
252 }))
253{
254 if (auto a = get<TNamed>(); a)
255 a->SetName(name);
256 SetTitle(title);
257}
258
259xRooNode::xRooNode(const char *name, const std::shared_ptr<TObject> &comp, const std::shared_ptr<xRooNode> &parent)
260 : TNamed(name, ""), fComp(comp), fParent(parent)
261{
262
263 if (!fComp && !fParent && name && strlen(name) > 0) {
266 delete[] _path;
268 // if file is json can try to read
269 if (pathName.EndsWith(".json")) {
270#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
271 fComp = std::make_shared<RooWorkspace>("workspace", name);
274 RooMsgService::instance().setGlobalKillBelow(RooFit::WARNING);
275 if (!tool.importJSON(pathName.Data())) {
276 Error("xRooNode", "Error reading json workspace %s", name);
277 fComp.reset();
278 }
279 RooMsgService::instance().setGlobalKillBelow(msglevel);
280#else
281 Error("xRooNode", "json format workspaces available only in ROOT 6.26 onwards");
282#endif
283 } else {
284
285 // using acquire in the constructor seems to cause a mem leak according to valgrind ... possibly because
286 // (*this) gets called on it before the node is fully constructed
287 auto _file = std::make_shared<TFile>(
288 pathName); // acquire<TFile>(name); // acquire file to ensure stays open while we have the workspace
289 // actually it appears we don't need to keep the file open once we've loaded the workspace, but should be
290 // no harm doing so
291 // otherwise the workspace doesn't saveas
292 auto keys = _file->GetListOfKeys();
293 if (keys) {
294 for (auto &&k : *keys) {
295 auto cl = TClass::GetClass((static_cast<TKey *>(k))->GetClassName());
296 if (cl == RooWorkspace::Class() || cl->InheritsFrom("RooWorkspace")) {
297 fComp.reset(_file->Get<RooWorkspace>(k->GetName()), [](TObject *ws) {
298 // memory leak in workspace, some RooLinkedLists aren't cleared, fixed in ROOT 6.28
299 if (ws) {
300#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
301 dynamic_cast<RooWorkspace *>(ws)->_embeddedDataList.Delete();
302#endif
303 xRooNode(*ws, std::make_shared<xRooNode>()).sterilize();
304 delete ws;
305 }
306 });
307 if (fComp) {
308 TNamed::SetNameTitle(fComp->GetName(), fComp->GetTitle());
309 fParent = std::make_shared<xRooNode>(
310 _file); // keep file alive - seems necessary to save workspace again in some cases
311 break;
312 }
313 }
314 }
315 }
316 }
317 } else if (pathName.EndsWith(".root") || pathName.EndsWith(".json")) {
318 throw std::runtime_error(TString::Format("%s does not exist", name));
319 }
320 }
321
322 if (auto _ws = get<RooWorkspace>(); _ws && (!parent || parent->get<TFile>())) {
324 .getStream(RooFit::INFO)
325 .removeTopic(RooFit::NumIntegration); // stop info message every time
326
327 // check if any of the open files have version numbers greater than our major version
328 // may not read correctly
329 for (auto f : *gROOT->GetListOfFiles()) {
330 if ((dynamic_cast<TFile *>(f)->GetVersion() / 100) > (gROOT->GetVersionInt() / 100)) {
331 Warning("xRooNode", "There is file open with version %d > current version %d ... results may be wrong",
332 dynamic_cast<TFile *>(f)->GetVersion(), gROOT->GetVersionInt());
333 }
334 }
335
336 // load list of colors if there is one
337 if (auto colors = dynamic_cast<TSeqCollection *>(_ws->obj(gROOT->GetListOfColors()->GetName()))) {
338 gROOT->GetListOfColors()->Clear();
339 for (auto col : *colors) {
340 gROOT->GetListOfColors()->Add(col);
341 }
342 }
343
344 // use the datasets if any to 'mark' observables
345 int checkCount = 0;
346 for (auto &d : _ws->allData()) {
347 for (auto &a : *d->get()) {
348 if (auto v = _ws->var(a->GetName()); v) {
349 v->setAttribute("obs");
350 } else if (auto c = _ws->cat(a->GetName()); c) {
351 c->setAttribute("obs");
352 }
353 }
354 // count how many ds are checked ... if none are checked will check the first
355 checkCount += d->TestBit(1 << 20);
356 }
357
358 if (checkCount == 0 && !_ws->allData().empty())
359 _ws->allData().back()->SetBit(1 << 20, true);
360
361 if (auto _set = dynamic_cast<RooArgSet *>(GETWSSNAPSHOTS(_ws).find("NominalParamValues")); _set) {
362 for (auto s : *_set) {
363 if (auto v = dynamic_cast<RooRealVar *>(s); v) {
364 _ws->var(s->GetName())->setStringAttribute("nominal", TString::Format("%f", v->getVal()));
365 }
366 }
367 }
368
369 // also flag global observables ... relies on ModelConfig existences
371 for (auto &[k, v] : GETWSSETS(_ws)) {
372 if (k == "globalObservables" || TString(k).EndsWith("_GlobalObservables")) {
373 for (auto &s : v) {
374 _allGlobs.add(*s);
375 s->setAttribute("obs");
376 s->setAttribute("global");
377 }
378 } else if (TString(k).EndsWith("_Observables")) {
379 const_cast<RooArgSet &>(v).setAttribAll("obs");
380 } else if (TString(k).EndsWith("_POI")) {
381 for (auto &s : v) {
382 s->setAttribute("poi");
383 auto _v = dynamic_cast<RooRealVar *>(s);
384 if (!_v)
385 continue;
386 // if (!_v->hasRange("physical")) {
387 // _v->setRange("physical", 0, std::numeric_limits<double>::infinity());
388 // // ensure range of poi is also straddling 0
389 // if (_v->getMin() >= 0)
390 // _v->setMin(-1e-5);
391 // }
392 }
393 } else if (TString(k).EndsWith("_NuisParams")) {
394 const_cast<RooArgSet &>(v).setAttribAll("np");
395 }
396 }
397 if (!_allGlobs.empty() && GETWSSETS(_ws).count("globalObservables") == 0) {
398 _ws->defineSet("globalObservables", _allGlobs);
399 }
400
401 // now check if any pars don't have errors defined (not same as error=0) ... if so, use the first pdf (if there is
402 // one) to try setting values from
403 if (!_ws->allPdfs().empty()) {
404 std::set<RooRealVar *> noErrorPars;
405 std::string parNames;
406 for (auto &p : np()) { // infer errors on all floating non-poi parameters
407 auto v = p->get<RooRealVar>();
408 if (!v)
409 continue;
410 if (!v->hasError()) {
411 noErrorPars.insert(v);
412 if (!parNames.empty())
413 parNames += ",";
414 parNames += v->GetName();
415 }
416 }
417 if (!noErrorPars.empty()) {
418 Warning("xRooNode",
419 "Inferring initial errors of %d parameters (%s%s) (give all nuisance parameters an error to avoid "
420 "this msg)",
421 int(noErrorPars.size()), (*noErrorPars.begin())->GetName(), (noErrorPars.size() > 1) ? ",..." : "");
422 // get the first top-level pdf
423 browse();
424 for (auto &a : *this) {
425 if (noErrorPars.empty()) {
426 break;
427 }
428 if (a->fFolder == "!pdfs") {
429 try {
430 auto fr = a->floats().reduced(parNames).fitResult("prefit");
431 if (auto _fr = fr.get<RooFitResult>(); _fr) {
432 std::set<RooRealVar *> foundPars;
433 for (auto &v : noErrorPars) {
434 if (auto arg = dynamic_cast<RooRealVar *>(_fr->floatParsFinal().find(v->GetName()));
435 arg && arg->hasError()) {
436 v->setError(arg->getError());
437 foundPars.insert(v);
438 }
439 }
440 for (auto &v : foundPars) {
441 noErrorPars.erase(v);
442 }
443 }
444 } catch (...) {
445 }
446 }
447 }
448 }
449 }
450 }
451
452 if (strlen(GetTitle()) == 0) {
453 if (fComp) {
454 TNamed::SetTitle(fComp->GetTitle());
455 } else {
456 TNamed::SetTitle(GetName());
457 }
458 }
459}
460
461xRooNode::xRooNode(const TObject &comp, const std::shared_ptr<xRooNode> &parent)
462 : xRooNode(/*[](const TObject& c) {
463c.InheritsFrom("RooAbsArg");
464if (s) {
465return (s->getStringAttribute("alias")) ? s->getStringAttribute("alias") : c.GetName();
466}
467return c.GetName();
468}(comp)*/
469 (comp.InheritsFrom("RooAbsArg") && dynamic_cast<const RooAbsArg *>(&comp)->getStringAttribute("alias"))
470 ? dynamic_cast<const RooAbsArg *>(&comp)->getStringAttribute("alias")
471 : comp.GetName(),
472 std::shared_ptr<TObject>(const_cast<TObject *>(&comp), [](TObject *) {}), parent)
473{
474}
475
476xRooNode::xRooNode(const std::shared_ptr<TObject> &comp, const std::shared_ptr<xRooNode> &parent)
477 : xRooNode(
478 [&]() {
479 if (auto a = std::dynamic_pointer_cast<RooAbsArg>(comp); a && a->getStringAttribute("alias"))
480 return a->getStringAttribute("alias");
481 if (comp)
482 return comp->GetName();
483 return "";
484 }(),
485 comp, parent)
486{
487}
488
490
491void xRooNode::Checked(TObject *obj, bool val)
492{
493 if (obj != this)
494 return;
495
496 // cycle through states:
497 // unhidden and selected: tick, no uline
498 // hidden and unselected: notick, uline
499 // unhidden and unselected: tick, uline
500 if (auto o = get<RooAbsReal>(); o) {
501 if (o->isSelectedComp() && !val) {
502 // deselecting and hiding
503 o->selectComp(val);
504 o->setAttribute("hidden");
505 } else if (!o->isSelectedComp() && !val) {
506 // selecting
507 o->selectComp(!val);
508 } else if (val) {
509 // unhiding but keeping unselected
510 o->setAttribute("hidden", false);
511 }
512 auto item = GetTreeItem(nullptr);
513 item->CheckItem(!o->getAttribute("hidden"));
514 if (o->isSelectedComp()) {
515 item->ClearColor();
516 } else {
517 item->SetColor(kGray);
518 }
519 return;
520 }
521
522 if (auto o = get(); o) {
523 // if (o->TestBit(1<<20)==val) return; // do nothing
524 o->SetBit(1 << 20, val); // TODO: check is 20th bit ok to play with?
525 if (auto fr = get<RooFitResult>(); fr) {
526 if (auto _ws = ws(); _ws) {
527 if (val) {
528 // ensure fit result is in genericObjects list or snapshots ... if not, add a copy ...
529 if (fr->numStatusHistory() && !_ws->genobj(fr->GetName())) {
530 _ws->import(*fr);
531 if (auto wfr = dynamic_cast<RooFitResult *>(_ws->genobj(fr->GetName()))) {
532 fr = wfr;
533 }
534 }
535 RooArgSet _allVars = _ws->allVars();
536 _allVars = fr->floatParsFinal();
537 _allVars = fr->constPars();
538 for (auto &i : fr->floatParsInit()) {
539 auto v = dynamic_cast<RooRealVar *>(_allVars.find(i->GetName()));
540 if (v)
541 v->setStringAttribute("initVal", TString::Format("%f", dynamic_cast<RooRealVar *>(i)->getVal()));
542 }
543 // uncheck all other fit results
544 for (auto oo : _ws->allGenericObjects()) {
545 if (auto ffr = dynamic_cast<RooFitResult *>(oo); ffr && ffr != fr) {
546 ffr->ResetBit(1 << 20);
547 }
548 }
549 } else
550 _ws->allVars() = fr->floatParsInit();
551 }
552 if (auto item = GetTreeItem(nullptr); item) {
553 // update check marks on siblings
554 if (auto first = item->GetParent()->GetFirstChild()) {
555 do {
556 if (first->HasCheckBox()) {
557 auto _obj = static_cast<xRooNode *>(first->GetUserData());
558 first->CheckItem(_obj->get() && _obj->get()->TestBit(1 << 20));
559 }
560 } while ((first = first->GetNextSibling()));
561 }
562 }
563 }
564 }
565}
566
568{
569 static bool blockBrowse = false;
570 if (blockBrowse)
571 return;
572 if (b == nullptr) {
573 auto b2 = dynamic_cast<TBrowser *>(gROOT->GetListOfBrowsers()->Last());
574 if (!b2 || !b2->GetBrowserImp()) { // no browser imp if browser was closed
575 blockBrowse = true;
576 gEnv->SetValue("X11.UseXft", "no"); // for faster x11
577 gEnv->SetValue("X11.Sync", "no");
578 gEnv->SetValue("X11.FindBestVisual", "no");
579 gEnv->SetValue("Browser.Name", "TRootBrowser"); // forces classic root browser (in 6.26 onwards)
580 gEnv->SetValue("Canvas.Name", "TRootCanvas");
581 b2 = new TBrowser("nodeBrowser", this, "RooFit Browser");
582 blockBrowse = false;
583 } else if (strcmp(b2->GetName(), "nodeBrowser") == 0) {
584 blockBrowse = true;
585 b2->BrowseObject(this);
586 blockBrowse = false;
587 } else {
588 auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b2->GetBrowserImp())));
589 if (_b)
590 _b->AddFSDirectory("Workspaces", nullptr, "SetRootDir");
591 /*auto l = Node2::Class()->GetMenuList();
592 auto o = new CustomClassMenuItem(TClassMenuItem::kPopupUserFunction,Node2::Class(),
593 "blah blah blah","BlahBlah",0,"Option_t*",-1,true);
594 //o->SetCall(o,"BlahBlah","Option_t*",-1);
595 l->AddFirst(o);*/
596 // b->BrowseObject(this);
597 _b->GotoDir(nullptr);
598 _b->Add(this, GetName());
599 // b->Add(this);
600 }
601 return;
602 }
603
604 if (auto item = GetTreeItem(b); item) {
605 if (!item->IsOpen() && IsFolder())
606 return; // no need to rebrowse if closing
607 // update check marks on any child items
608 if (auto first = item->GetFirstChild()) {
609 do {
610 if (first->HasCheckBox()) {
611 auto _obj = static_cast<xRooNode *>(first->GetUserData());
612 first->CheckItem(_obj->get() &&
613 (_obj->get()->TestBit(1 << 20) ||
614 (_obj->get<RooAbsArg>() && !_obj->get<RooAbsArg>()->getAttribute("hidden"))));
615 }
616 } while ((first = first->GetNextSibling()));
617 }
618 }
619
620 browse();
621
622 // for top-level pdfs default to having the .vars browsable too
623 if (get<RooAbsPdf>() && fFolder == "!pdfs" && !_IsShowVars_()) {
624 fBrowsables.push_back(std::make_shared<xRooNode>(vars()));
625 }
626
627 if (auto _fr = get<RooFitResult>(); _fr && fBrowsables.empty()) {
628 // have some common drawing options
629 fBrowsables.push_back(std::make_shared<xRooNode>(".Draw(\"pull\")", nullptr, *this));
630 fBrowsables.push_back(std::make_shared<xRooNode>(".Draw(\"corr10colztext\")", nullptr, *this));
631 if (std::unique_ptr<RooAbsCollection>(_fr->floatParsFinal().selectByAttrib("poi", true))->size() == 1) {
632 fBrowsables.push_back(std::make_shared<xRooNode>(".Draw(\"impact\")", nullptr, *this));
633 }
634 }
635
636 if (empty() && fBrowsables.empty()) {
637 try {
638 if (auto s = get<TStyle>()) {
639 s->SetFillAttributes();
640 if (auto ed = dynamic_cast<TGedEditor *>(TVirtualPadEditor::GetPadEditor())) {
641 ed->SetModel(gPad, s, kButton1Down, true);
642 }
643 } else if (TString(GetName()).BeginsWith(".Draw(\"") && fParent) {
644 fParent->Draw(TString(TString(GetName())(7, strlen(GetName()) - 9)) + b->GetDrawOption());
645 } else {
646 Draw(b->GetDrawOption());
647 }
648 } catch (const std::exception &e) {
649 new TGMsgBox(
650 gClient->GetRoot(),
651 (gROOT->GetListOfBrowsers()->At(0))
652 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
653 : gClient->GetRoot(),
654 "Exception", e.what(),
655 kMBIconExclamation); // deletes self on dismiss?
656 }
657 }
658
659 bool hasFolders = false;
660 if (strlen(GetName()) > 0 && GetName()[0] != '!') { // folders don't have folders
661 for (auto &c : *this) {
662 if (!c->fFolder.empty()) {
663 hasFolders = true;
664 break;
665 }
666 }
667 }
668 // auto _ws = get<RooWorkspace>();
669 if (/*_ws*/ hasFolders) {
670 // organize in folders
671 auto _folders = find(".folders");
672 if (!_folders) {
673 _folders = emplace_back(std::make_shared<xRooNode>(".folders", nullptr, *this));
674 }
675 // ensure entry in folders for every folder type ...
676 for (auto &v : *this) {
677 if (!v->fFolder.empty() && !_folders->find(v->fFolder, false)) {
678 _folders->emplace_back(std::make_shared<xRooNode>(v->fFolder.c_str(), nullptr, *this));
679 }
680 }
681 // now just add all the folders
682 for (auto &v : *_folders) {
683 TString _name = v->GetName();
684 if (_name.BeginsWith('!'))
685 _name = _name(1, _name.Length()); // strip ! from display
686 b->Add(v.get(), _name);
687 }
688 }
689
690 for (auto &v : *this) {
691 if (hasFolders && !v->fFolder.empty())
692 continue; // in the folders
693 if (strcmp(v->GetName(), ".folders") == 0)
694 continue; // never 'browse' the folders property
695 auto _fr = v->get<RooFitResult>();
696 int _checked = (v->get<RooAbsData>() || _fr) ? v->get()->TestBit(1 << 20) : -1;
697 if (_fr && ((_fr->status() == 0 && _fr->numStatusHistory() == 0) || (_fr->floatParsFinal().empty()))) {
698 // this is a "PARTIAL" fit result ... don't allow it to be selected
699 _checked = -1;
700 }
701 if (v->get<RooAbsPdf>() && get<RooSimultaneous>())
702 _checked = !v->get<RooAbsArg>()->getAttribute("hidden");
703 TString _name = v->GetName();
704 if (v->get() && _name.BeginsWith(TString(v->get()->ClassName()) + "::")) {
705 _name = _name(strlen(v->get()->ClassName()) + 2, _name.Length());
706 }
707 if (_name.BeginsWith(".")) {
708 // property node -- display the name of the contained object
709 if (v->get()) {
710 _name = TString::Format("%s: %s::%s", _name.Data(), v->get()->ClassName(),
711 (v->get<RooAbsArg>() && v->get<RooAbsArg>()->getStringAttribute("alias"))
712 ? v->get<RooAbsArg>()->getStringAttribute("alias")
713 : v->get()->GetName());
714 }
715 } else if (v->get() && !v->get<TFile>() && !TString(v->GetName()).BeginsWith('/'))
716 _name = TString::Format("%s::%s", v->get()->ClassName(), _name.Data());
717 if (auto _type = v->GetNodeType(); strlen(_type)) {
718 // decided not to show const values until figure out how to update if value changes
719 /*if (TString(_type)=="Const") _name += TString::Format(" [%s=%g]",_type,v->get<RooConstVar>()->getVal());
720 else*/
721 _name += TString::Format(" [%s]", _type);
722 }
723 if (auto fv = v->get<RooFormulaVar>()) {
724 TString formu = TString::Format(" [%s]", fv->expression());
725 for (size_t i = 0; i < fv->dependents().size(); i++) {
726 formu.ReplaceAll(TString::Format("x[%zu]", i), fv->dependents()[i].GetName());
727 }
728 _name += formu;
729 } else if (auto gv = v->get<RooGenericPdf>()) {
730 TString formu = TString::Format(" [%s]", gv->expression());
731 for (size_t i = 0; i < gv->dependents().size(); i++) {
732 formu.ReplaceAll(TString::Format("x[%zu]", i), gv->dependents()[i].GetName());
733 }
734 _name += formu;
735 }
736 // tool tip defaults to displaying name and title, so temporarily set name to obj name if has one
737 // and set title to the object type
738 TString nameSave(v->TNamed::GetName());
739 TString titleSave(v->TNamed::GetTitle());
740 if (auto o = v->get(); o)
741 v->TNamed::SetNameTitle(o->GetName(), o->ClassName());
742 b->Add(v.get(), _name, _checked);
743 if (auto o = v->get(); o)
744 v->TNamed::SetNameTitle(nameSave, titleSave);
745 if (_checked != -1) {
746 dynamic_cast<TQObject *>(b->GetBrowserImp())
747 ->Connect("Checked(TObject *, bool)", ClassName(), v.get(), "Checked(TObject *, bool)");
748 }
749 if (_fr) {
750 if (_fr->status() || _fr->covQual() != 3) { // snapshots or bad fits
751 v->GetTreeItem(b)->SetColor((_fr->numStatusHistory() && !_fr->floatParsFinal().empty()) ? kRed : kBlue);
752 } else if (_fr->numStatusHistory() == 0) { // partial fit result ..
753 v->GetTreeItem(b)->SetColor(kGray);
754 }
755 }
756 if ((v->fFolder == "!np" || v->fFolder == "!poi")) {
757 if (v->get<RooAbsArg>()->getAttribute("Constant")) {
758 v->GetTreeItem(b)->SetColor(kGray);
759 } else
760 v->GetTreeItem(b)->ClearColor();
761 }
762 if (auto _htr = v->get<RooStats::HypoTestResult>(); _htr) {
763 // check for fit statuses
764 if (auto fits = _htr->GetFitInfo()) {
765 for (int i = 0; i < fits->numEntries(); i++) {
766 // if any fit (other than a genFit) is bad, flag point as bad
767 if (fits->get(i)->getCatIndex("type") != 5 && fits->get(i)->getRealValue("status") != 0) {
768 v->GetTreeItem(b)->SetColor(kRed);
769 break;
770 }
771 }
772 } else {
773 v->GetTreeItem(b)->SetColor(kBlue); // unknown fit status
774 }
775 }
776
777 // v.fBrowsers.insert(b);
778 }
779
780 // for pdfs, check for datasets too and add to list
781 /*if (get<RooAbsPdf>()) {
782 auto dsets = datasets();
783 if (!dsets.empty()) {
784 // check if already have .datasets() in browsables
785 bool found(false);
786 for(auto& p : fBrowsables) {
787 if (TString(p->GetName())==".datasets()") {found=true;
788 // add
789 break;
790 }
791 }
792 if (!found) {
793 fBrowsables.push_back(std::make_shared<xRooNode>(dsets));
794 }
795 }
796 }*/
797 // browse the browsables too
798 for (auto &v : fBrowsables) {
799 TString _name = v->GetName();
800 if (_name == ".memory")
801 continue; // hide the memory from browsing, if put in browsables
802 TString nameSave(v->TNamed::GetName());
803 TString titleSave(v->TNamed::GetTitle());
804 if (auto o = v->get(); o)
805 v->TNamed::SetNameTitle(o->GetName(), o->ClassName());
806 b->Add(v.get(), _name, -1);
807 if (auto o = v->get(); o)
808 v->TNamed::SetNameTitle(nameSave, titleSave);
809 }
810
811 b->SetSelected(this);
812}
813
815{
816 if (!set) {
817 // can't remove as causes a crash, need to remove from the browser first
818 /*for(auto itr = fBrowsables.begin(); itr != fBrowsables.end(); ++itr) {
819 if (strcmp((*itr)->GetName(),".vars")==0) {
820 fBrowsables.erase(itr);
821 }
822 }*/
823 } else {
824 auto v = std::make_shared<xRooNode>(vars());
825 fBrowsables.push_back(v);
826 if (auto l = GetListTree(nullptr)) {
827 l->AddItem(GetTreeItem(nullptr), v->GetName(), v.get());
828 }
829 }
830}
831
833{
834 for (auto &b : fBrowsables) {
835 if (strcmp(b->GetName(), ".vars") == 0)
836 return true;
837 }
838 return false;
839}
840
842{
843 if (strlen(GetName()) > 0 && GetName()[0] == '!')
844 return true;
845 if (strlen(GetName()) > 0 && GetName()[0] == '.' && !(TString(GetName()).BeginsWith(".Draw(\"")))
846 return true;
847 if (empty())
848 const_cast<xRooNode *>(this)->browse();
849 return !empty();
850}
851
852class Axis2 : public TAxis {
853
854public:
855 using TAxis::TAxis;
856 double GetBinWidth(Int_t bin) const override
857 {
858 if (auto v = var(); v)
859 return v->getBinWidth(bin - 1, GetName());
860 return 1;
861 }
862 double GetBinLowEdge(Int_t bin) const override
863 {
864 if (auto v = rvar(); v) {
865 return (bin == v->getBinning(GetName()).numBins() + 1) ? v->getBinning(GetName()).binHigh(bin - 2)
866 : v->getBinning(GetName()).binLow(bin - 1);
867 }
868 return bin - 1;
869 }
870 double GetBinUpEdge(Int_t bin) const override
871 {
872 if (auto v = rvar(); v)
873 return (bin == 0) ? v->getBinning(GetName()).binLow(bin) : v->getBinning(GetName()).binHigh(bin - 1);
874 return bin;
875 }
876
877 const char *GetTitle() const override
878 {
879 return (binning() && strlen(binning()->GetTitle())) ? binning()->GetTitle() : GetParent()->GetTitle();
880 }
881 void SetTitle(const char *title) override
882 {
883 if (binning()) {
884 const_cast<RooAbsBinning *>(binning())->SetTitle(title);
885 } else {
886 dynamic_cast<TNamed *>(GetParent())->SetTitle(title);
887 }
888 }
889
890 void Set(Int_t nbins, const double *xbins) override
891 {
892 if (auto v = dynamic_cast<RooRealVar *>(rvar()))
893 v->setBinning(RooBinning(nbins, xbins), GetName());
894 TAxis::Set(nbins, xbins);
895 }
896 void Set(Int_t nbins, const float *xbins) override
897 {
898 std::vector<double> bins(nbins + 1);
899 for (int i = 0; i <= nbins; i++)
900 bins.at(i) = xbins[i];
901 return Set(nbins, &bins[0]);
902 }
903 void Set(Int_t nbins, double xmin, double xmax) override
904 {
905 if (auto v = dynamic_cast<RooRealVar *>(rvar()))
906 v->setBinning(RooUniformBinning(xmin, xmax, nbins), GetName());
907 TAxis::Set(nbins, xmin, xmax);
908 }
909
910 const RooAbsBinning *binning() const { return var()->getBinningPtr(GetName()); }
911
912 Int_t FindFixBin(const char *label) const override { return TAxis::FindFixBin(label); }
913 Int_t FindFixBin(double x) const override { return (binning()) ? (binning()->binNumber(x) + 1) : x; }
914
915private:
916 RooAbsLValue *var() const { return dynamic_cast<RooAbsLValue *>(GetParent()); }
917 RooAbsRealLValue *rvar() const { return dynamic_cast<RooAbsRealLValue *>(GetParent()); }
918};
919
920std::shared_ptr<TObject> xRooNode::getObject(const std::string &name, const std::string &type) const
921{
922 // if (fParent) return fParent->getObject(name);
923
924 if (auto _owned = find(".memory"); _owned) {
925 for (auto &o : *_owned) {
926 if (name == o->GetName()) {
927 if (type.empty() || o->get()->InheritsFrom(type.c_str()))
928 return o->fComp;
929 }
930 }
931 }
932
933 // see if have a provider
934 auto _provider = fProvider;
935 auto _parent = fParent;
936 while (!_provider && _parent) {
937 _provider = _parent->fProvider;
938 _parent = _parent->fParent;
939 }
940 if (_provider)
941 return _provider->getObject(name, type);
942
943 if (ws()) {
944 std::shared_ptr<TObject> out;
945 if (auto arg = ws()->arg(name.c_str()); arg) {
946 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
947 if (!type.empty() && arg->InheritsFrom(type.c_str()))
948 return _tmp;
949 if (!out)
950 out = _tmp;
951 }
952 if (auto arg = ws()->data(name.c_str()); arg) {
953 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
954 if (!type.empty() && arg->InheritsFrom(type.c_str()))
955 return _tmp;
956 if (!out)
957 out = _tmp;
958 }
959 if (auto arg = ws()->genobj(name.c_str()); arg) {
960 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
961 if (!type.empty() && arg->InheritsFrom(type.c_str()))
962 return _tmp;
963 if (!out)
964 out = _tmp;
965 }
966 if (auto arg = ws()->embeddedData(name.c_str()); arg) {
967 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
968 if (!type.empty() && arg->InheritsFrom(type.c_str()))
969 return _tmp;
970 if (!out)
971 out = _tmp;
972 }
973 if (auto arg = GETWSSNAPSHOTS(ws()).find(name.c_str()); arg) {
974 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
975 if (!type.empty() && arg->InheritsFrom(type.c_str()))
976 return _tmp;
977 if (!out)
978 out = _tmp;
979 }
980 return out;
981 }
982 if (auto arg = get<RooAbsArg>()) {
983 // can try all nodes
984 RooArgSet nodes;
985 arg->treeNodeServerList(&nodes);
986 if (auto server = nodes.find(name.c_str())) {
987 return std::shared_ptr<TObject>(server, [](TObject *) {});
988 }
989 }
990 return nullptr;
991}
992
994{
995 if (fXAxis) {
996 // check if num bins needs update or not
997 if (auto cat = dynamic_cast<RooAbsCategory *>(fXAxis->GetParent());
998 cat && cat->numTypes() != fXAxis->GetNbins()) {
999 fXAxis.reset();
1000 } else {
1001 return fXAxis.get();
1002 }
1003 }
1004 RooAbsLValue *x = nullptr;
1005 if (auto a = get<RooAbsArg>(); a && a->isFundamental())
1006 x = dynamic_cast<RooAbsLValue *>(a); // self-axis
1007
1008 auto _parentX = (!x && fParent && !fParent->get<RooSimultaneous>()) ? fParent->GetXaxis() : nullptr;
1009
1010 auto o = get<RooAbsReal>();
1011 if (!o)
1012 return _parentX;
1013
1014 if (auto xName = o->getStringAttribute("xvar"); xName) {
1015 x = dynamic_cast<RooAbsLValue *>(getObject(xName).get());
1016 }
1017
1018 // if xvar has become set equal to an arg and this is a pdf, we will allow a do-over
1019 if (!x) {
1020 // need to choose from dependent fundamentals, in following order:
1021 // parentX (if not a glob), robs, globs, vars, args
1022
1023 if (_parentX && !dynamic_cast<RooAbsArg *>(_parentX->GetParent())->getAttribute("global") &&
1024 (o->dependsOn(*dynamic_cast<RooAbsArg *>(_parentX->GetParent())) || vars().empty())) {
1025 x = dynamic_cast<RooAbsLValue *>(_parentX->GetParent());
1026 } else if (auto _obs = obs(); !_obs.empty()) {
1027 for (auto &v : _obs) {
1028 if (!v->get<RooAbsArg>()->getAttribute("global")) {
1029 x = v->get<RooAbsLValue>();
1030 if (x)
1031 break;
1032 } else if (!x) {
1033 x = v->get<RooAbsLValue>();
1034 }
1035 }
1036 } else if (auto _pars = pars(); !_pars.empty()) {
1037 for (auto &v : _pars) {
1038 if (!v->get<RooAbsArg>()->getAttribute("Constant")) {
1039 x = v->get<RooAbsLValue>();
1040 if (x)
1041 break;
1042 } else if (!x) {
1043 x = v->get<RooAbsLValue>();
1044 }
1045 }
1046 }
1047
1048 if (!x) {
1049 return nullptr;
1050 }
1051 }
1052
1053 if (o != dynamic_cast<TObject *>(x)) {
1054 o->setStringAttribute("xvar", dynamic_cast<TObject *>(x)->GetName());
1055 }
1056
1057 // decide binning to use
1058 TString binningName = o->getStringAttribute("binning");
1059 auto _bnames = x->getBinningNames();
1060 bool hasBinning = false;
1061 for (auto &b : _bnames) {
1062 if (b == binningName) {
1063 hasBinning = true;
1064 break;
1065 }
1066 }
1067 if (!hasBinning) {
1068 // doesn't have binning, so clear binning attribute
1069 // this can happen after Combine of models because binning don't get combined yet (should fix this)
1070 Warning("GetXaxis", "Binning %s not defined on %s - clearing", binningName.Data(),
1071 dynamic_cast<TObject *>(x)->GetName());
1072 o->setStringAttribute("binning", nullptr);
1073 binningName = "";
1074 }
1075
1076 if (binningName == "" && o != dynamic_cast<TObject *>(x)) {
1077 // has var has a binning matching this nodes name then use that
1078 auto __bnames = x->getBinningNames();
1079 for (auto &b : __bnames) {
1080 if (b == GetName())
1081 binningName = GetName();
1082 if (b == o->GetName()) {
1083 binningName = o->GetName();
1084 break;
1085 } // best match
1086 }
1087 if (binningName == "") {
1088 // if we are binned in this var then will define that as a binning
1089 if (/*o->isBinnedDistribution(*dynamic_cast<RooAbsArg *>(x))*/
1090 auto bins = _or_func(
1091 /*o->plotSamplingHint(*dynamic_cast<RooAbsRealLValue
1092 *>(x),-std::numeric_limits<double>::infinity(),std::numeric_limits<double>::infinity())*/
1093 (std::list<double> *)(nullptr),
1094 o->binBoundaries(*dynamic_cast<RooAbsRealLValue *>(x), -std::numeric_limits<double>::infinity(),
1095 std::numeric_limits<double>::infinity()));
1096 bins) {
1097 std::vector<double> _bins;
1098 for (auto &b : *bins) {
1099 if (_bins.empty() || std::abs(_bins.back() - b) > 1e-5 * _bins.back())
1100 _bins.push_back(b);
1101 }
1102 fXAxis = std::make_shared<Axis2>(_bins.size() - 1, &_bins[0]);
1103 // add this binning to the var to avoid recalling ...
1104 if (auto _v = dynamic_cast<RooRealVar *>(x); _v) {
1105 _v->setBinning(RooBinning(_bins.size() - 1, &_bins[0], o->GetName()), o->GetName());
1106 _v->getBinning(o->GetName())
1107 .SetTitle(""); // indicates to use the current var title when building histograms etc
1108 //_v->getBinning(o->GetName()).SetTitle(strlen(dynamic_cast<TObject*>(x)->GetTitle()) ?
1109 // dynamic_cast<TObject*>(x)->GetTitle() : dynamic_cast<TObject*>(x)->GetName());
1110 }
1111 binningName = o->GetName();
1112 delete bins;
1113 } else if (_parentX) {
1114 // use parent axis binning if defined, otherwise we will default
1115 binningName = _parentX->GetName();
1116 }
1117 }
1118 }
1119
1120 if (!fXAxis) {
1121 if (auto r = dynamic_cast<RooAbsRealLValue *>(x); r) {
1122 if (r->getBinning(binningName).isUniform()) {
1123 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), r->getMin(binningName), r->getMax(binningName));
1124 } else {
1125 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), r->getBinning(binningName).array());
1126 }
1127 } else if (auto cat = dynamic_cast<RooCategory *>(x)) {
1128 std::vector<double> bins = {};
1129 for (int i = 0; i <= x->numBins(binningName); i++)
1130 bins.push_back(i);
1131 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), &bins[0]);
1132 // TODO have to load current state of bin labels if was a category (sadly not a virtual method)
1133 int i = 1;
1134 std::map<int, std::string> cats; // fill into a map to preserve index ordering
1135 for (auto &c : *cat) {
1136 if (cat->isStateInRange(binningName, c.first.c_str())) {
1137 cats[c.second] = c.first;
1138 }
1139 }
1140 for (auto &[_, label] : cats) {
1141 fXAxis->SetBinLabel(i++, label.c_str());
1142 }
1143 }
1144 }
1145
1146 fXAxis->SetName(binningName);
1147 fXAxis->SetParent(dynamic_cast<TObject *>(x));
1148 return fXAxis.get();
1149}
1150
1151const char *xRooNode::GetIconName() const
1152{
1153 if (auto o = get(); o) {
1154 if (o->InheritsFrom("RooWorkspace"))
1155 return "TFile";
1156 if (o->InheritsFrom("RooAbsData"))
1157 return "TProfile";
1158 if (o->InheritsFrom("RooSimultaneous"))
1159 return "TH3D";
1160
1161 if (o->InheritsFrom("RooProdPdf"))
1162 return "a.C"; // or nullptr for folder
1163 if (o->InheritsFrom("RooRealSumPdf") || o->InheritsFrom("RooAddPdf"))
1164 return "TH2D";
1165 // if(o->InheritsFrom("RooProduct")) return "TH1D";
1166 if (o->InheritsFrom("RooFitResult")) {
1167 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitRooFitResult", true)) {
1168 gClient->GetMimeTypeList()->AddType("xRooFitRooFitResult", "xRooFitRooFitResult", "package.xpm",
1169 "package.xpm", "->Browse()");
1170 }
1171 return "xRooFitRooFitResult";
1172 }
1173 if (o->InheritsFrom("RooRealVar") || o->InheritsFrom("RooCategory")) {
1174 if (get<RooAbsArg>()->getAttribute("obs")) {
1175 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitObs", true)) {
1176 gClient->GetMimeTypeList()->AddType("xRooFitObs", "xRooFitObs", "x_pic.xpm", "x_pic.xpm", "->Browse()");
1177 }
1178 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitGlobs", true)) {
1179 gClient->GetMimeTypeList()->AddType("xRooFitGlobs", "xRooFitGlobs", "z_pic.xpm", "z_pic.xpm",
1180 "->Browse()");
1181 }
1182 return (get<RooAbsArg>()->getAttribute("global") ? "xRooFitGlobs" : "xRooFitObs");
1183 }
1184 return "TLeaf";
1185 }
1186 if (o->InheritsFrom("TStyle")) {
1187 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitTStyle", true)) {
1188 gClient->GetMimeTypeList()->AddType("xRooFitTStyle", "xRooFitTStyle", "bld_colorselect.xpm",
1189 "bld_colorselect.xpm", "->Browse()");
1190 }
1191 return "xRooFitTStyle";
1192 }
1193 if (o->InheritsFrom("RooConstVar")) {
1194 /*if (!gClient->GetMimeTypeList()->GetIcon("xRooFitRooConstVar",true)) {
1195 gClient->GetMimeTypeList()->AddType("xRooFitRooConstVar", "xRooFitRooConstVar", "stop_t.xpm", "stop_t.xpm",
1196 "->Browse()");
1197 }
1198 return "xRooFitRooConstVar";*/
1199 return "TMethodBrowsable-leaf";
1200 }
1201 if (o->InheritsFrom("RooStats::HypoTestInverterResult")) {
1202 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitScanStyle", true)) {
1203 gClient->GetMimeTypeList()->AddType("xRooFitScanStyle", "xRooFitScanStyle", "f2_s.xpm", "f2_s.xpm",
1204 "->Browse()");
1205 }
1206 return "xRooFitScanStyle";
1207 }
1208 if (o->InheritsFrom("RooStats::HypoTestResult")) {
1209 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitTestStyle", true)) {
1210 gClient->GetMimeTypeList()->AddType("xRooFitTestStyle", "xRooFitTestStyle", "diamond.xpm", "diamond.xpm",
1211 "->Browse()");
1212 }
1213 return "xRooFitTestStyle";
1214 }
1215 if (o->InheritsFrom("RooStats::HistFactory::FlexibleInterpVar"))
1216 return "TBranchElement-folder";
1217 if (o->InheritsFrom("RooAbsPdf")) {
1218 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitPDFStyle", true)) {
1219 gClient->GetMimeTypeList()->AddType("xRooFitPDFStyle", "xRooFitPDFStyle", "pdf.xpm", "pdf.xpm",
1220 "->Browse()");
1221 }
1222 return "xRooFitPDFStyle";
1223 }
1224 if (o->InheritsFrom("RooStats::ModelConfig")) {
1225 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitMCStyle", true)) {
1226 gClient->GetMimeTypeList()->AddType("xRooFitMCStyle", "xRooFitMCStyle", "app_t.xpm", "app_t.xpm",
1227 "->Browse()");
1228 }
1229 return "xRooFitMCStyle";
1230 }
1231 if (auto a = dynamic_cast<RooAbsReal *>(o); a) {
1232 if (auto _ax = GetXaxis();
1233 _ax && (a->isBinnedDistribution(*dynamic_cast<RooAbsArg *>(_ax->GetParent())) ||
1234 (dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
1235 std::unique_ptr<std::list<double>>(a->binBoundaries(
1236 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
1237 std::numeric_limits<double>::infinity()))))) {
1238 return "TH1D";
1239 }
1240 return "TF1";
1241 }
1242 return o->ClassName();
1243 }
1244 if (!IsFolder()) {
1245 return "Unknown";
1246 }
1247 return nullptr;
1248}
1249
1250const char *xRooNode::GetNodeType() const
1251{
1252 if (auto o = get(); o && fParent && (fParent->get<RooProduct>() || fParent->get<RooRealSumPdf>())) {
1253 if (o->InheritsFrom("RooStats::HistFactory::FlexibleInterpVar"))
1254 return "Overall";
1255 if (o->InheritsFrom("PiecewiseInterpolation")) {
1256 // check if children are all RooHistFunc ... if so, it's a HistoFactor, otherwise it's a Varied
1257 bool isHisto=true;
1258 for(auto c : const_cast<xRooNode*>(this)->browse()) {
1259 if(!c->get<RooHistFunc>()) {
1260 isHisto=false; break;
1261 }
1262 }
1263 if(isHisto) {
1264 return (dynamic_cast<RooAbsArg *>(o)->getAttribute("density")) ? "HistoDensity" : "Histo";
1265 } else {
1266 return (dynamic_cast<RooAbsArg *>(o)->getAttribute("density")) ? "VariedDensity" : "Varied";
1267 }
1268 }
1269 if (o->InheritsFrom("RooHistFunc"))
1270 return (dynamic_cast<RooAbsArg *>(o)->getAttribute("density")) ? "SimpleDensity" : "Simple";
1271 if (o->InheritsFrom("RooBinWidthFunction"))
1272 return "Density";
1273 if (o->InheritsFrom("ParamHistFunc"))
1274 return "Shape";
1275 if (o->InheritsFrom("RooRealVar"))
1276 return "Norm";
1277 if (o->InheritsFrom("RooConstVar"))
1278 return "Const";
1279 }
1280 return "";
1281}
1282
1284{
1285 xRooNode out(".coords", nullptr, *this);
1286 // go up through parents looking for slice obs
1287 auto _p = std::shared_ptr<xRooNode>(const_cast<xRooNode *>(this), [](xRooNode *) {});
1288 while (_p) {
1289 TString pName(_p->GetName());
1290 // following is commented out while still considering, but idea is to include category in coords
1291 /*if (auto s = _p->get<RooSimultaneous>(); s && s->indexCat().InheritsFrom("RooCategory") &&
1292 !out.find(s->indexCat().GetName())) { auto cat = const_cast<RooCategory*>(dynamic_cast<const
1293 RooCategory*>(&s->indexCat()));
1294 // check if we have a pdf for every category ... if not then add to cut
1295 cat->clearRange("coordRange",true);
1296 bool hasMissing = false;
1297 std::string includedStates;
1298 for (auto state : *cat) {
1299 if (!s->getPdf(state.first.c_str())) {
1300 hasMissing = true;
1301 } else {
1302 if (!includedStates.empty()) {
1303 includedStates += ",";
1304 }
1305 includedStates += state.first;
1306 }
1307 }
1308 if (hasMissing) {
1309 if(includedStates.find(",") != std::string::npos) {
1310 cat->addToRange("coordRange",includedStates.c_str());
1311 } else {
1312 cat->setLabel(includedStates);
1313 }
1314 out.emplace_back(std::make_shared<xRooNode>(cat->GetName(),_p->getObject<RooAbsArg>(cat->GetName()),_p));
1315 }
1316 } else*/
1317 if (auto pos = pName.Index('='); pos != -1) {
1318 if (pos > 0 && pName(pos - 1) == '<') {
1319 // should be a range on a real lvalue, of form low<=name<high
1320 double low = TString(pName(0, pos - 1)).Atof();
1321 pName = pName(pos + 1, pName.Length());
1322 double high = TString(pName(pName.Index('<') + 1, pName.Length())).Atof();
1323 pName = pName(0, pName.Index('<'));
1324 if (auto _obs = _p->getObject<RooAbsRealLValue>(pName.Data()); _obs) {
1325 if (setVals) {
1326 _obs->setVal((high + low) / 2.);
1327 static_cast<RooRealVar *>(_obs.get())->setRange("coordRange", low, high);
1328 _obs->setStringAttribute(
1329 "coordRange", "coordRange"); // will need if we allow multi disconnected regions, need comma list
1330 }
1331 out.emplace_back(std::make_shared<xRooNode>(_obs->GetName(), _obs, _p));
1332 } else {
1333 throw std::runtime_error(TString::Format("Unknown observable: %s", pName.Data()));
1334 }
1335
1336 } else if (auto _obs = _p->getObject<RooAbsArg>(pName(0, pos)); _obs) {
1337 if (setVals) {
1338 if (auto _cat = dynamic_cast<RooAbsCategoryLValue *>(_obs.get()); _cat) {
1339 _cat->setLabel(pName(pos + 1, pName.Length()));
1340 } else if (auto _var = dynamic_cast<RooAbsRealLValue *>(_obs.get()); _var) {
1341 _var->setVal(TString(pName(pos + 1, pName.Length())).Atof());
1342 }
1343 }
1344 out.emplace_back(std::make_shared<xRooNode>(_obs->GetName(), _obs, _p));
1345 } else {
1346 throw std::runtime_error("Unknown observable, could not find");
1347 }
1348 }
1349 _p = _p->fParent;
1350 }
1351 return out;
1352}
1353
1354void xRooNode::_Add_(const char *name, const char *opt)
1355{
1356 try {
1357 Add(name, opt);
1358 } catch (const std::exception &e) {
1359 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
1360 kMBIconExclamation); // deletes self on dismiss?
1361 }
1362}
1363void xRooNode::_Vary_(const char *what)
1364{
1365 try {
1366 Vary(what);
1367 } catch (const std::exception &e) {
1368 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
1369 kMBIconExclamation); // deletes self on dismiss?
1370 }
1371}
1372
1374{
1375
1376 if (strcmp(GetName(), ".poi") == 0) {
1377 // demote a parameter from being a poi
1378 auto toRemove =
1379 (child.get<RooAbsArg>() || !find(child.GetName())) ? child : xRooNode(find(child.GetName())->fComp);
1380 if (toRemove) {
1381 if (!toRemove.get<RooAbsArg>()->getAttribute("poi")) {
1382 throw std::runtime_error(TString::Format("%s is not a poi", toRemove.GetName()));
1383 }
1384 toRemove.get<RooAbsArg>()->setAttribute("poi", false);
1385 return toRemove;
1386 }
1387 } else if (strcmp(GetName(), ".factors") == 0 || strcmp(GetName(), ".constraints") == 0 ||
1388 strcmp(GetName(), ".components") == 0) {
1389 auto toRemove =
1390 (child.get<RooAbsArg>() || !find(child.GetName())) ? child : xRooNode(find(child.GetName())->fComp);
1391 if (auto p = fParent->get<RooProdPdf>(); p) {
1392 auto pdf = toRemove.get<RooAbsArg>();
1393 if (!pdf)
1394 pdf = p->pdfList().find(child.GetName());
1395 if (!pdf)
1396 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1397 auto i = p->pdfList().index(*pdf);
1398 if (i >= 0) {
1399#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
1400 const_cast<RooArgList &>(p->pdfList()).remove(*pdf);
1401#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
1402 p->_pdfNSetList.erase(p->_pdfNSetList.begin() + i);
1403#else
1404 auto nset = p->_pdfNSetList.At(i);
1405 p->_pdfNSetList.Remove(nset);
1406 delete nset; // I don't think the RooLinkedList owned it so must delete ourself
1407#endif
1408 if (p->_extendedIndex == i)
1409 p->_extendedIndex = -1;
1410 else if (p->_extendedIndex > i)
1411 p->_extendedIndex--;
1412#else
1413 p->removePdfs(RooArgSet(*pdf));
1414#endif
1415 sterilize();
1416 return xRooNode(*pdf);
1417 } else {
1418 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1419 }
1420 } else if (auto p2 = fParent->get<RooProduct>(); p2) {
1421 auto arg = toRemove.get<RooAbsArg>();
1422 if (!arg)
1423 arg = p2->components().find(child.GetName());
1424 if (!arg)
1425 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1426 // remove server ... doesn't seem to trigger removal from proxy
1427#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
1428 p2->_compRSet.remove(*arg);
1429#else
1430 const_cast<RooArgList &>(p2->realComponents()).remove(*arg);
1431#endif
1432 p2->removeServer(*arg, true);
1433 sterilize();
1434 return xRooNode(*arg);
1435 } else if (fParent->get<RooSimultaneous>()) {
1436 // remove from all channels
1437 bool removed = false;
1438 for (auto &c : fParent->bins()) {
1439 try {
1440 c->constraints().Remove(toRemove);
1441 removed = true;
1442 } catch (std::runtime_error &) { /* wasn't a constraint in channel */
1443 }
1444 }
1445 sterilize();
1446 if (!removed)
1447 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1448 return toRemove;
1449 } else if (auto p4 = fParent->get<RooRealSumPdf>(); p4) {
1450 auto arg = toRemove.get<RooAbsArg>();
1451 if (!arg)
1452 arg = p4->funcList().find(child.GetName());
1453 if (!arg)
1454 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1455 // remove, including coef removal ....
1456 auto idx = p4->funcList().index(arg);
1457
1458 if (idx != -1) {
1459
1460 const_cast<RooArgList &>(p4->funcList()).remove(*arg);
1461 p4->removeServer(*arg, true);
1462 // have to be careful removing coef because if shared will end up removing them all!!
1463 std::vector<RooAbsArg *> _coefs;
1464 for (size_t ii = 0; ii < const_cast<RooArgList &>(p4->coefList()).size(); ii++) {
1465 if (ii != size_t(idx))
1466 _coefs.push_back(const_cast<RooArgList &>(p4->coefList()).at(ii));
1467 }
1468 const_cast<RooArgList &>(p4->coefList()).removeAll();
1469 for (auto &a : _coefs)
1470 const_cast<RooArgList &>(p4->coefList()).add(*a);
1471
1472 sterilize();
1473 } else {
1474 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1475 }
1476 return xRooNode(*arg);
1477 } else if (auto p5 = fParent->get<RooAddPdf>(); p5) {
1478 auto arg = toRemove.get<RooAbsArg>();
1479 if (!arg)
1480 arg = p5->pdfList().find(child.GetName());
1481 if (!arg)
1482 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1483 // remove, including coef removal ....
1484 auto idx = p5->pdfList().index(arg);
1485
1486 if (idx != -1) {
1487
1488 const_cast<RooArgList &>(p5->pdfList()).remove(*arg);
1489 p5->removeServer(*arg, true);
1490 // have to be careful removing coef because if shared will end up removing them all!!
1491 std::vector<RooAbsArg *> _coefs;
1492 for (size_t ii = 0; ii < const_cast<RooArgList &>(p5->coefList()).size(); ii++) {
1493 if (ii != size_t(idx))
1494 _coefs.push_back(const_cast<RooArgList &>(p5->coefList()).at(ii));
1495 }
1496 const_cast<RooArgList &>(p5->coefList()).removeAll();
1497 for (auto &a : _coefs)
1498 const_cast<RooArgList &>(p5->coefList()).add(*a);
1499
1500 sterilize();
1501 } else {
1502 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1503 }
1504 return xRooNode(*arg);
1505 } else if (auto p6 = fParent->get<RooAddition>(); p6) {
1506 auto arg = toRemove.get<RooAbsArg>();
1507 if (!arg)
1508 arg = p6->list().find(child.GetName());
1509 if (!arg)
1510 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1511 // remove server ... doesn't seem to trigger removal from proxy
1512 const_cast<RooArgList &>(p6->list()).remove(*arg);
1513 p6->removeServer(*arg, true);
1514 sterilize();
1515 return xRooNode(*arg);
1516 }
1517 }
1518
1519 if (auto w = get<RooWorkspace>(); w) {
1520 xRooNode out(child.GetName());
1521 auto arg = w->components().find(child.GetName());
1522 if (!arg)
1523 arg = operator[](child.GetName())->get<RooAbsArg>();
1524 if (!arg) {
1525 throw std::runtime_error(TString::Format("Cannot find %s in workspace %s", child.GetName(), GetName()));
1526 }
1527 // check has no clients ... if so, cannot delete
1528 if (arg->hasClients()) {
1529 throw std::runtime_error(
1530 TString::Format("Cannot remove %s from workspace %s, because it has dependencies - first remove from those",
1531 child.GetName(), GetName()));
1532 }
1533 const_cast<RooArgSet &>(w->components()).remove(*arg); // deletes arg
1534 Info("Remove", "Deleted %s from workspace %s", out.GetName(), GetName());
1535 return out;
1536 } else if (get<RooProduct>() || get<RooProdPdf>()) {
1537 return factors().Remove(child);
1538 } else if (get<RooRealSumPdf>() || get<RooAddPdf>() || get<RooAddition>()) {
1539 return components().Remove(child);
1540 }
1541
1542 throw std::runtime_error("Removal not implemented for object type " +
1543 std::string(get() ? get()->ClassName() : "null"));
1544}
1545
1547{
1548
1549 class AutoUpdater {
1550 public:
1551 AutoUpdater(xRooNode &_n) : n(_n) {}
1552 ~AutoUpdater() { n.browse(); }
1553 xRooNode &n;
1554 };
1555 AutoUpdater xxx(*this);
1556
1557 TString sOpt(opt);
1558 bool considerType(sOpt == "+");
1559
1560 if (strlen(GetName()) > 0 && GetName()[0] == '!' && fParent) {
1561 // folder .. pass onto parent and add folder to child folder list
1562 const_cast<xRooNode &>(child).fFolder += GetName();
1563 return fParent->Add(child, opt);
1564 }
1565 // this is how to get the first real parent ... may be useful at some point?
1566 /*auto realParent = fParent;
1567 while(!realParent->get()) {
1568 realParent = realParent->fParent;
1569 if (!realParent) throw std::runtime_error("No parentage");
1570 }*/
1571
1572 // adding to a collection node will incorporate the child into the parent of the collection
1573 // in the appropriate way
1574 if (strcmp(GetName(), ".factors") == 0) {
1575 // multiply the parent
1576 return fParent->Multiply(child, opt);
1577 } else if (strcmp(GetName(), ".components") == 0) {
1578 // add to the parent
1579 return fParent->Add(child, opt);
1580 } else if (strcmp(GetName(), ".variations") == 0) {
1581 // vary the parent
1582 return fParent->Vary(child);
1583 } else if (strcmp(GetName(), ".constraints") == 0) {
1584 // constrain the parent
1585 return fParent->Constrain(child);
1586 } else if (strcmp(GetName(), ".bins") == 0 && fParent->get<RooSimultaneous>()) {
1587 // adding a channel (should adding a 'bin' be an 'Extend' operation?)
1588 return fParent->Vary(child);
1589 } else if ((strcmp(GetName(), ".globs") == 0)) {
1590 if (child.get<RooAbsArg>() || (!child.fComp && getObject<RooAbsArg>(child.GetName()))) {
1591 auto out = (child.get<RooAbsArg>()) ? child.get<RooAbsArg>() : getObject<RooAbsArg>(child.GetName()).get();
1592 out->setAttribute("obs");
1593 out->setAttribute("global");
1594 return xRooNode(*out, *this);
1595 }
1596 throw std::runtime_error("Failed to add global observable");
1597 } else if ((strcmp(GetName(), ".poi") == 0)) {
1598 if (child.get<RooAbsLValue>() || (!child.fComp && getObject<RooAbsLValue>(child.GetName()))) {
1599 auto out = (child.get<RooAbsArg>()) ? child.get<RooAbsArg>() : getObject<RooAbsArg>(child.GetName()).get();
1600 out->setAttribute("poi");
1601 return xRooNode(*out, *this);
1602 } else if (!child.get() && fParent->get<RooWorkspace>()) {
1603 // may be creating poi at same time as adding, try add to parent
1604 auto res = fParent->Add(child);
1605 if (res.get<RooAbsLValue>())
1606 return Add(res);
1607 }
1608 throw std::runtime_error("Failed to add parameter of interest");
1609 } else if ((strcmp(GetName(), ".pars") == 0 || strcmp(GetName(), ".vars") == 0) && fParent->get<RooWorkspace>()) {
1610 // adding a parameter, interpret as factory string unless no "[" then create RooRealVar
1611 TString fac(child.GetName());
1612 if (!fac.Contains("["))
1613 fac += "[1]";
1614 return xRooNode(*fParent->get<RooWorkspace>()->factory(fac), fParent);
1615 } else if (strcmp(GetName(), ".datasets()") == 0) {
1616 // create a dataset - only allowed for pdfs or workspaces
1617 if (auto _ws = ws(); _ws && fParent) {
1618 sOpt.ToLower();
1619 if (!fParent->get<RooAbsPdf>() && (!fParent->get<RooWorkspace>() || sOpt == "asimov")) {
1620 throw std::runtime_error(
1621 "Datasets can only be created for pdfs or workspaces (except if generated dataset, then must be pdf)");
1622 }
1623
1624 if (sOpt == "asimov" || sOpt == "toy") {
1625 // generate expected dataset - note that globs will be frozen at this time
1626 auto _fr = fParent->fitResult();
1627 if (strlen(_fr->GetName()) == 0) { // ensure fit result has a name so that name is saved inside dataset
1628 _fr.get<RooFitResult>()->SetName(TUUID().AsString());
1629 }
1630 auto ds = fParent->generate(_fr, sOpt == "asimov");
1631 if (strlen(child.GetName())) {
1632 ds.SetName(child.GetName());
1633 ds.get<TNamed>()->SetName(child.GetName());
1634 }
1635 if (auto _ds = ds.get<RooAbsData>()) {
1636 _ws->import(*_ds);
1637 }
1638 if (_fr.get<RooFitResult>()->numStatusHistory() == 0) {
1639 if (!GETWSSNAPSHOTS(_ws).find(_fr->GetName())) {
1640 const_cast<RooLinkedList &>(GETWSSNAPSHOTS(_ws)).Add(_fr->Clone());
1641 }
1642 } else if (!_ws->obj(_fr->GetName())) {
1643 _ws->import((*_fr.get<RooFitResult>()));
1644 } // save fr to workspace, for later retrieval
1645 return xRooNode(*_ws->data(ds.GetName()), fParent);
1646 }
1647
1648 auto parentObs = fParent->obs(); // may own globs so keep alive
1649 auto _obs = parentObs.argList();
1650 // put globs in a snapshot
1651 std::unique_ptr<RooAbsCollection> _globs(_obs.selectByAttrib("global", true));
1652 // RooArgSet _tmp; _tmp.add(*_globs);_ws->saveSnapshot(child.GetName(),_tmp);
1653 _obs.remove(*_globs);
1654
1655 // include any coords
1656 _obs.add(coords(false).argList(), true);
1657 // include axis var too, provided it's an observable
1658 if (auto ax = GetXaxis(); ax && dynamic_cast<RooAbsArg *>(ax->GetParent())->getAttribute("obs")) {
1659 _obs.add(*dynamic_cast<RooAbsArg *>(ax->GetParent()));
1660 }
1661 // check if ws already has a dataset with this name, if it does we may need to extend columns
1662 if (auto _d = _ws->data(child.GetName()); _d) {
1663 // add any missing obs
1664 RooArgSet l(_obs);
1665 l.remove(*_d->get(), true, true);
1666 if (!l.empty()) {
1667 auto _dd = dynamic_cast<RooDataSet *>(_d);
1668 if (!_dd)
1669 throw std::runtime_error("Cannot extend dataset with new columns");
1670 for (auto &x : l) {
1671 _dd->addColumn(*x);
1672 }
1673 }
1674 } else {
1675 RooRealVar w("weightVar", "weightVar", 1);
1676 _obs.add(w);
1677 RooDataSet d(child.GetName(), child.GetTitle(), _obs, RooFit::WeightVar("weightVar"));
1678 _ws->import(d);
1679 // seems have to set bits after importing, not before
1680 if (auto __d = _ws->data(child.GetName()))
1681 __d->SetBit(1 << 20, _ws->allData().size() == 1); // sets as selected if is only ds
1682 }
1683 /*if(!_ws->data(child.GetName())) {
1684 RooRealVar w("weightVar", "weightVar", 1);
1685 RooArgSet _obs; _obs.add(w);
1686 RooDataSet d(child.GetName(), child.GetTitle(), _obs, "weightVar");
1687 _ws->import(d);
1688 }*/
1689 auto out = std::shared_ptr<TObject>(_ws->data(child.GetName()), [](TObject *) {});
1690
1691 if (out) {
1692 xRooNode o(out, fParent);
1693 if (child.get<TH1>())
1694 o = *child.get();
1695 return o;
1696 }
1697 }
1698 throw std::runtime_error("Cannot create dataset");
1699 }
1700
1701 if (!get()) {
1702 if (!fParent)
1703 throw std::runtime_error("Cannot add to null object with no parentage");
1704
1705 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
1706 try {
1707 fComp = fParent->Add(*this, "+").fComp;
1708 } catch (...) {
1709 resize(size() - 1);
1710 std::rethrow_exception(std::current_exception());
1711 }
1712 resize(size() - 1); // remove the temporarily added node
1713
1714 if (!fComp) {
1715 throw std::runtime_error("No object");
1716 }
1717 }
1718
1719 if (auto p = get<RooAbsData>(); p) {
1720 if (auto bb = getBrowsable(".sourceds"))
1721 bb->Add(child, opt);
1722 if (auto _data = child.get<RooDataSet>()) {
1723 auto ds = dynamic_cast<RooDataSet *>(p);
1724 if (!ds) {
1725 throw std::runtime_error("Can only add datasets to a dataset");
1726 }
1727
1728 // append any missing globs, and check any existing globs have matching values
1730 auto _globs = globs();
1731 for (auto &glob : child.globs()) {
1732 if (auto g = _globs.find(glob->GetName()); !g) {
1733 globsToAdd.addClone(*glob->get<RooAbsArg>());
1734 } else if (g->GetContent() != glob->GetContent()) {
1735 Warning("Add", "Global observable %s=%g in dataset %s mismatches %s value %g ... ignoring latter",
1736 g->GetName(), g->GetContent(), GetName(), child.GetName(), glob->GetContent());
1737 }
1738 }
1739 // add any existing globs to list then set the list
1740 if (auto _dglobs = p->getGlobalObservables()) {
1741 globsToAdd.addClone(*_dglobs);
1742 } else {
1743 for (auto g : _globs)
1744 globsToAdd.addClone(*g->get<RooAbsArg>());
1745 }
1746 p->setGlobalObservables(globsToAdd);
1747
1748 // append any missing observables to our dataset, then append the dataset
1749
1750 for (auto col : *_data->get()) {
1751 if (!p->get()->contains(*col)) {
1752 ds->addColumn(*col);
1753 }
1754 }
1755 ds->append(*_data);
1756 ds->SetTitle(TString(ds->GetTitle()) + " + " + _data->GetTitle());
1757 SetTitle(TString(GetTitle()) + " + " + child.GetTitle());
1758 return *this;
1759 }
1760 auto _h = child.get<TH1>();
1761 if (!_h) {
1762 throw std::runtime_error("Can only add histogram or dataset to data");
1763 }
1764 auto _pdf = parentPdf();
1765 if (!_pdf)
1766 throw std::runtime_error("Could not find pdf");
1767 auto _ax = _pdf->GetXaxis();
1768 if (!_ax) {
1769 throw std::runtime_error("Cannot determine binning to add data");
1770 }
1771
1772 RooArgSet obs;
1773 obs.add(*dynamic_cast<RooAbsArg *>(_ax->GetParent()));
1774 obs.add(coords().argList()); // will also move obs to coords
1775
1776 // add any missing obs
1777 RooArgSet l(obs);
1778 l.remove(*p->get(), true, true);
1779 if (!l.empty()) {
1780 auto _d = dynamic_cast<RooDataSet *>(p);
1781 if (!_d)
1782 throw std::runtime_error("Cannot extend dataset with new columns");
1783 for (auto &x : l) {
1784 _d->addColumn(*x);
1785 }
1786 }
1787
1788 // before adding, ensure range is good to cover
1789 for (auto &o : obs) {
1790 if (auto v = dynamic_cast<RooRealVar *>(o); v) {
1791 if (auto dv = dynamic_cast<RooRealVar *>(p->get()->find(v->GetName())); dv) {
1792 if (v->getMin() < dv->getMin())
1793 dv->setMin(v->getMin());
1794 if (v->getMax() > dv->getMax())
1795 dv->setMax(v->getMax());
1796 }
1797 } else if (auto c = dynamic_cast<RooCategory *>(o); c) {
1798 if (auto dc = dynamic_cast<RooCategory *>(p->get()->find(c->GetName())); dc) {
1799 if (!dc->hasLabel(c->getCurrentLabel())) {
1800 dc->defineType(c->getCurrentLabel(), c->getCurrentIndex());
1801 }
1802 }
1803 }
1804 }
1805
1806 for (int i = 1; i <= _h->GetNbinsX(); i++) {
1807 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(_ax->GetParent())) {
1808 if (!_h->GetXaxis()->GetBinLabel(i)) {
1809 throw std::runtime_error(
1810 TString::Format("Categorical observable %s requires bin labels", _ax->GetParent()->GetName()));
1811 } else if (!cat->hasLabel(_h->GetXaxis()->GetBinLabel(i))) {
1812 throw std::runtime_error(TString::Format("Categorical observable %s does not have label %s",
1813 _ax->GetParent()->GetName(), _h->GetXaxis()->GetBinLabel(i)));
1814 } else {
1815 cat->setLabel(_h->GetXaxis()->GetBinLabel(i));
1816 }
1817 } else {
1818 dynamic_cast<RooAbsRealLValue *>(_ax->GetParent())->setVal(_h->GetBinCenter(i));
1819 }
1820 p->add(obs, _h->GetBinContent(i));
1821 }
1822
1823 return *this;
1824 }
1825
1826 if (auto p = get<RooAddPdf>(); p) {
1827 if ((child.get<RooAbsPdf>() || (!child.fComp && getObject<RooAbsPdf>(child.GetName())))) {
1828 auto out = (child.fComp) ? acquire(child.fComp) : getObject<RooAbsArg>(child.GetName());
1829 // don't add a coef if in 'all-extended' mode and this pdf is extendable
1830 auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out);
1831 if (!_pdf) {
1832 throw std::runtime_error("Something went wrong with pdf acquisition");
1833 }
1834
1835 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
1836 _pdf->dependsOn(*static_cast<RooAbsArg *>(_ax->GetParent()))) {
1837 auto _p = _pdf;
1838
1839 if (auto _boundaries = std::unique_ptr<std::list<double>>(_p->binBoundaries(
1840 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
1841 std::numeric_limits<double>::infinity()));
1842 !_boundaries && _ax->GetNbins() > 0) {
1843#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
1844 Warning("Add", "Adding unbinned pdf %s to binned %s - will wrap with RooBinSamplingPdf(...)",
1845 _p->GetName(), GetName());
1846 _p = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _p->GetName()), _p->GetTitle(),
1847 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *_p);
1848 _p->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
1849 if (!_p->getStringAttribute("alias"))
1850 _p->setStringAttribute("alias", out->GetName());
1851#else
1852 throw std::runtime_error(
1853 "unsupported addition of unbinned pdf to binned model - please upgrade to at least ROOT 6.24");
1854#endif
1855 _pdf = _p;
1856 }
1857 }
1858
1859 if (!(_pdf->canBeExtended() && p->coefList().empty())) {
1860 // if extended, use an extended binding as the coef
1861 // otherwise e.g. if adding a RooRealSumPdf the stacked histograms will be above the
1862 // actual pdf histogram because the pdf histogram is just normalized down
1863 if (_pdf->canBeExtended()) {
1864 // FIXME: ExtendedBinding needs the obs list passing to it ... should be fixed in RooFit
1865 // until then, this will return "1" and so the pdf's histograms wont be normalized properly in relation
1866 // to stacks of its comps
1867 const_cast<RooArgList &>(p->coefList())
1868 .add(*acquireNew<RooExtendedBinding>(TString::Format("%s_extBind", _pdf->GetName()),
1869 TString::Format("Expected Events of %s", _pdf->GetTitle()),
1870 *_pdf));
1871 } else {
1872
1873 // need to create a coefficient for each existing pdf first, like above
1874 for (auto i = p->coefList().size(); i < p->pdfList().size(); i++) {
1875 const_cast<RooArgList &>(p->coefList())
1877 TString::Format("%s_extBind", p->pdfList().at(i)->GetName()),
1878 TString::Format("Expected Events of %s", p->pdfList().at(i)->GetTitle()),
1879 *static_cast<RooAbsPdf *>(p->pdfList().at(i))));
1880 }
1881
1882 const_cast<RooArgList &>(p->coefList()).add(*acquire2<RooAbsArg, RooRealVar>("1", "1", 1));
1883 }
1884 // ensure not in no-coef mode any more
1885 *reinterpret_cast<bool *>(reinterpret_cast<unsigned char *>(p) +
1886 p->Class()->GetDataMemberOffset("_allExtendable")) = false;
1887 *reinterpret_cast<bool *>(reinterpret_cast<unsigned char *>(p) +
1888 p->Class()->GetDataMemberOffset("_haveLastCoef")) = true;
1889 }
1890 const_cast<RooArgList &>(p->pdfList()).add(*_pdf);
1891 sterilize();
1892 return xRooNode(*_pdf, *this);
1893 } else if ((child.get<TH1>() || child.get<RooAbsReal>() ||
1894 (!child.get() && getObject<RooAbsReal>(child.GetName()))) &&
1895 !child.get<RooAbsPdf>()) {
1896 RooRealSumPdf *_pdf = nullptr;
1897 bool tooMany(false);
1898 for (auto &pp : factors()) {
1899 if (auto _p = pp->get<RooRealSumPdf>(); _p) {
1900 if (_pdf) {
1901 _pdf = nullptr;
1902 tooMany = true;
1903 break;
1904 } // more than one!
1905 _pdf = _p;
1906 }
1907 }
1908 if (_pdf) {
1909 return xRooNode(*_pdf, *this).Add(child);
1910 } else if (!tooMany) {
1911 // create a RooRealSumPdf to hold the child
1912 auto _sumpdf = Add(*acquireNew<RooRealSumPdf>(TString::Format("%s_samples", p->GetName()),
1913 TString::Format("%s samples", GetTitle()), RooArgList(),
1914 RooArgList(), true));
1915 _sumpdf.get<RooAbsArg>()->setStringAttribute("alias", "samples");
1916 return _sumpdf.Add(child);
1917 }
1918 }
1919 }
1920
1921 if (auto p = get<RooRealSumPdf>(); p) {
1922 std::shared_ptr<TObject> out;
1923 auto cc = child.fComp;
1924 bool isConverted = (cc != child.convertForAcquisition(*this, sOpt));
1925 if (child.get<RooAbsReal>()) {
1926 out = acquire(child.fComp);
1927 if (std::dynamic_pointer_cast<TH1>(cc) && !TString(cc->GetOption()).Contains("nostyle")) {
1928 xRooNode(out, *this).styles(cc.get()); // transfer style if adding a histogram
1929 }
1930 }
1931 if (!child.fComp && getObject<RooAbsReal>(child.GetName())) {
1932 Info("Add", "Adding existing function %s to %s", child.GetName(), p->GetName());
1933 out = getObject<RooAbsReal>(child.GetName());
1934 }
1935
1936 if (!out && !child.fComp) {
1937 std::shared_ptr<RooAbsArg> _func;
1938 // a null node .. so create either a new RooProduct or RooHistFunc if has observables (or no deps but has
1939 // x-axis)
1940 auto _obs = robs();
1941 if (!_obs.empty() || GetXaxis()) {
1942 if (_obs.empty()) {
1943 // using X axis to construct hist
1944 auto _ax = dynamic_cast<Axis2 *>(GetXaxis());
1945 auto t = TH1::AddDirectoryStatus();
1946 TH1::AddDirectory(false);
1947 auto h =
1948 std::make_unique<TH1D>(child.GetName(), child.GetTitle(), _ax->GetNbins(), _ax->binning()->array());
1950 h->GetXaxis()->SetName(TString::Format("%s;%s", _ax->GetParent()->GetName(), _ax->GetName()));
1951 // technically convertForAcquisition has already acquired so no need to re-acquire but should be harmless
1952 _func = std::dynamic_pointer_cast<RooAbsArg>(acquire(xRooNode(*h).convertForAcquisition(*this)));
1953 } else if (_obs.size() == 1) {
1954 // use the single obs to make a TH1D
1955 auto _x = _obs.at(0)->get<RooAbsLValue>();
1956 auto _bnames = _x->getBinningNames();
1957 TString binningName = p->getStringAttribute("binning");
1958 for (auto &b : _bnames) {
1959 if (b == p->GetName()) {
1960 binningName = p->GetName();
1961 break;
1962 }
1963 }
1964 auto t = TH1::AddDirectoryStatus();
1965 TH1::AddDirectory(false);
1966 auto h = std::make_unique<TH1D>(child.GetName(), child.GetTitle(), _x->numBins(binningName),
1967 _x->getBinningPtr(binningName)->array());
1969 h->GetXaxis()->SetName(
1970 TString::Format("%s;%s", dynamic_cast<TObject *>(_x)->GetName(), binningName.Data()));
1971 // technically convertForAcquisition has already acquired so no need to re-acquire but should be harmless
1972 _func = std::dynamic_pointer_cast<RooAbsArg>(acquire(xRooNode(*h).convertForAcquisition(*this)));
1973 Info("Add", "Created SimpleDensity factor %s (xaxis=%s) for %s", _func->GetName(), _obs.at(0)->GetName(),
1974 p->GetName());
1975 } else {
1976 throw std::runtime_error("Unsupported creation of new component in SumPdf for this many obs");
1977 }
1978 } else {
1979 _func = acquireNew<RooProduct>(TString::Format("%s_%s", p->GetName(), child.GetName()), child.GetTitle(),
1980 RooArgList());
1981 }
1982 _func->setStringAttribute("alias", child.GetName());
1983 out = _func;
1984 }
1985
1986 if (auto _f = std::dynamic_pointer_cast<RooHistFunc>(
1987 (child.get<RooProduct>()) ? child.factors()[child.GetName()]->fComp : out);
1988 _f) {
1989 // adding a histfunc directly to a sumpdf, should be a density
1990 _f->setAttribute("density");
1991 if (_f->getAttribute("autodensity")) {
1992 // need to divide by bin widths first
1993 for (int i = 0; i < _f->dataHist().numEntries(); i++) {
1994 auto bin_pars = _f->dataHist().get(i);
1995 _f->dataHist().set(*bin_pars, _f->dataHist().weight() / _f->dataHist().binVolume(*bin_pars));
1996 }
1997 _f->setAttribute("autodensity", false);
1998 _f->setValueDirty();
1999 }
2000
2001 // promote the axis vars to observables
2002 // can't use original child as might refer to unacquired deps
2003 for (auto &x : xRooNode("tmp", _f).vars()) {
2004 x->get<RooAbsArg>()->setAttribute("obs");
2005 }
2006 if (isConverted) {
2007 Info("Add", "Created %s factor RooHistFunc::%s for %s",
2008 _f->getAttribute("density") ? "SimpleDensity" : "Simple", _f->GetName(), p->GetName());
2009 }
2010 }
2011
2012 if (auto _p = std::dynamic_pointer_cast<RooAbsPdf>(out); _p) {
2013 // adding a pdf to a RooRealSumPdf will replace it with a RooAddPdf and put the RooRealSumPdf inside that
2014 // if pdf is extended will use in the "no coefficients" state, where the expectedEvents are taking from
2015 // the pdf integrals
2016 TString newName(_p->GetName());
2017 newName.ReplaceAll("_samples", "");
2018 newName += "_components";
2019 Warning("Add", "converting samples to components");
2020
2021 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
2022 _p->dependsOn(*static_cast<RooAbsArg *>(_ax->GetParent()))) {
2023
2024 if (auto _boundaries = std::unique_ptr<std::list<double>>(_p->binBoundaries(
2025 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
2026 std::numeric_limits<double>::infinity()));
2027 !_boundaries && _ax->GetNbins() > 0) {
2028#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
2029 Warning("Add", "Adding unbinned pdf %s to binned %s - will wrap with RooBinSamplingPdf(...)",
2030 _p->GetName(), GetName());
2031 _p = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _p->GetName()), _p->GetTitle(),
2032 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *_p);
2033 _p->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
2034 if (!_p->getStringAttribute("alias"))
2035 _p->setStringAttribute("alias", out->GetName());
2036#else
2037 throw std::runtime_error(
2038 "unsupported addition of unbinned pdf to binned model - please upgrade to at least ROOT 6.24");
2039#endif
2040 }
2041 }
2042
2043 // require to be extended to be in coefficient-free mode ...
2044 // otherwise would lose the integral of the sumPdf (can't think of way to have a coef be the integral)
2045 if (!_p->canBeExtended()) {
2046 _p = acquireNew<RooExtendPdf>(TString::Format("%s_extended", _p->GetName()), _p->GetTitle(), *_p,
2047 *acquire2<RooAbsReal, RooRealVar>("1", "1", 1));
2048 }
2049
2050 return *(Replace(*acquireNew<RooAddPdf>(newName, _p->GetTitle(), RooArgList(*p, *_p)))
2051 .browse()[1]); // returns second node.
2052 }
2053
2054 if (auto _f = std::dynamic_pointer_cast<RooAbsReal>(out); _f) {
2055
2056 // todo: if adding a pdf, should actually replace RooRealSumPdf with a RooAddPdf and put
2057 // the sumPdf and *this* pdf inside that pdf
2058 // only exception is the binSamplingPdf below to integrate unbinned functions across bins
2059
2060 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
2061 _f->dependsOn(*static_cast<RooAbsArg *>(_ax->GetParent()))) {
2062
2063 if (auto _boundaries = std::unique_ptr<std::list<double>>(_f->binBoundaries(
2064 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
2065 std::numeric_limits<double>::infinity()));
2066 !_boundaries && _ax->GetNbins() > 0) {
2067#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
2068 Warning(
2069 "Add",
2070 "Adding unbinned function %s to binned %s - will wrap with RooRealSumPdf(RooBinSamplingPdf(...))",
2071 _f->GetName(), GetName());
2072 auto sumPdf = acquireNew<RooRealSumPdf>(TString::Format("%s_pdfWrapper", _f->GetName()), _f->GetTitle(),
2073 *_f, *acquire2<RooAbsArg, RooRealVar>("1", "1", 1), true);
2074 sumPdf->setStringAttribute("alias", _f->getStringAttribute("alias"));
2075 if (!sumPdf->getStringAttribute("alias"))
2076 sumPdf->setStringAttribute("alias", out->GetName());
2077 _f = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _f->GetName()), _f->GetTitle(),
2078 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *sumPdf);
2079 _f->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
2080 if (!_f->getStringAttribute("alias"))
2081 _f->setStringAttribute("alias", out->GetName());
2082#else
2083 throw std::runtime_error(
2084 "unsupported addition of unbinned function to binned model - please upgrade to at least ROOT 6.24");
2085#endif
2086 }
2087 }
2088
2089 const_cast<RooArgList &>(p->coefList()).add(*acquire2<RooAbsArg, RooRealVar>("1", "1", 1));
2090 const_cast<RooArgList &>(p->funcList()).add(*_f);
2091 // inherit binning if we dont have one yet
2092 if (!p->getStringAttribute("binning"))
2093 p->setStringAttribute("binning", _f->getStringAttribute("binning"));
2094
2095 xRooNode _out(_f, *this);
2096 if (auto gf = p->getStringAttribute("global_factors"); gf) {
2097 TStringToken pattern(gf, ";");
2098 while (pattern.NextToken()) {
2099 auto fac = getObject<RooAbsReal>(pattern.Data());
2100 if (!fac) {
2101 throw std::runtime_error(TString::Format("Could not find global factor %s", pattern.Data()));
2102 }
2103 _out.Multiply(fac);
2104 }
2105 }
2106 sterilize();
2107 // clear children for reload and update shared axis
2108 clear();
2109 fXAxis.reset();
2110 p->setStringAttribute("xvar", nullptr);
2111 browse();
2112 return _out;
2113 }
2114 } else if (auto p2 = get<RooProdPdf>(); p2) {
2115 // can "add" to a RooProdPdf provided trying to add a RooAbsReal not a RooAbsPdf and have a zero or 1
2116 // RooRealSumPdf child.convertForAcquisition(*this); - don't convert here because want generated objects named
2117 // after roorealsumpdf
2118 if (child.get<RooAbsPdf>() || (!child.get() && getObject<RooAbsPdf>(child.GetName()))) {
2119 // can add if 0 or 1 RooAddPdf ....
2120 RooAddPdf *_pdf = nullptr;
2121 bool tooMany(false);
2122 for (auto &pp : factors()) {
2123 if (auto _p = pp->get<RooAddPdf>(); _p) {
2124 if (_pdf) {
2125 _pdf = nullptr;
2126 tooMany = true;
2127 break;
2128 } // more than one!
2129 _pdf = _p;
2130 }
2131 }
2132 if (_pdf) {
2133 return xRooNode(*_pdf, *this).Add(child);
2134 } else if (!tooMany) {
2135 auto out = this->operator[]("components")->Add(child);
2136 return out;
2137 }
2138 } else if ((child.get<TH1>() || child.get<RooAbsReal>() ||
2139 (!child.get() && getObject<RooAbsReal>(child.GetName()))) &&
2140 !child.get<RooAbsPdf>()) {
2141 RooRealSumPdf *_pdf = nullptr;
2142 RooAddPdf *_backup = nullptr;
2143 bool tooMany(false);
2144 for (auto &pp : factors()) {
2145 if (auto _p = pp->get<RooRealSumPdf>(); _p) {
2146 if (_pdf) {
2147 _pdf = nullptr;
2148 tooMany = true;
2149 break;
2150 } // more than one!
2151 _pdf = _p;
2152 } else if (auto _p2 = pp->get<RooAddPdf>(); _p2) {
2153 _backup = _p2;
2154 for (auto &_pdfa : pp->components()) {
2155 if (auto _p3 = _pdfa->get<RooRealSumPdf>(); _p3) {
2156 if (_pdf) {
2157 _pdf = nullptr;
2158 tooMany = true;
2159 break;
2160 } // more than one!
2161 _pdf = _p3;
2162 }
2163 }
2164 }
2165 }
2166 if (_pdf) {
2167 return xRooNode(*_pdf, *this).Add(child);
2168 } else if (_backup) {
2169 // added *INSIDE* the addPdf -- will create a RooRealSumPdf to hold it
2170 return xRooNode(*_backup, *this).Add(child);
2171 } else if (!tooMany) {
2172 auto out = this->operator[]("samples")->Add(child);
2173 // clear our x-axis to re-evaluate
2174 fXAxis.reset();
2175 p2->setStringAttribute("xvar", nullptr);
2176 return out;
2177 }
2178 }
2179 } else if (auto s = get<RooSimultaneous>(); s) {
2180
2181 // adding to a simultaneous means adding a bin
2182 return bins().Add(child);
2183
2184 // if the child is a RooAbsPdf can just add it as a new channel using name of pdf as the channel name
2185 // if child is a histogram, will create a RooProdPdf
2186
2187 } else if (auto w = get<RooWorkspace>(); w) {
2188 child.convertForAcquisition(
2189 *this, child.get() ? "" : "func" /* if child is a string, allow it to be passed to factory */);
2190 if (child.get()) {
2191 if (auto _d = child.get<RooAbsData>()) {
2192 // don't use acquire method to import, because that adds datasets as Embeddded
2193 if (!w->import(*_d)) {
2194 // should upgrade vars with any obs from the dataset
2195 if (_d->get()) {
2196 std::unique_ptr<RooAbsCollection>(w->allVars().selectCommon(*_d->get()))->setAttribAll("obs");
2197 }
2198 if (_d->getGlobalObservables()) {
2199 std::unique_ptr<RooAbsCollection> globs(w->allVars().selectCommon(*_d->getGlobalObservables()));
2200 globs->setAttribAll("obs");
2201 globs->setAttribAll("global");
2202 }
2203 return xRooNode(child.GetName(), *w->data(child.GetName()), *this);
2204 } else {
2205 throw std::runtime_error(
2206 TString::Format("Could not import dataset %s into workspace %s", child.GetName(), w->GetName())
2207 .Data());
2208 }
2209 } else {
2210 auto out = acquire(child.fComp);
2211 if (out)
2212 return xRooNode(child.GetName(), out, *this);
2213 }
2214 }
2215
2216 if (!child.empty() || child.fFolder == "!pdfs") {
2217 // create a RooSimultaneous using the children as the channels
2218 // children either have "=" in name if specifying channel cat name or otherwise assume
2219 std::string catName = "channelCat";
2220 if (!child.empty()) {
2221 if (TString ss = child.at(0)->GetName(); ss.Contains("=")) {
2222 catName = ss(0, ss.Index('='));
2223 }
2224 }
2225 auto _cat = acquire<RooCategory>(catName.c_str(), catName.c_str());
2226 _cat->setAttribute("obs");
2227 auto out = acquireNew<RooSimultaneous>(child.GetName(), child.GetTitle(), *_cat);
2228 Info("Add", "Created pdf RooSimultaneous::%s in workspace %s", out->GetName(), w->GetName());
2229 return xRooNode(out, *this);
2230 }
2231 }
2232
2233 if (sOpt == "pdf") {
2234 // can only add a pdf to a workspace
2235 if (get<RooWorkspace>()) {
2236 const_cast<xRooNode &>(child).fFolder = "!pdfs";
2237 return Add(child);
2238 }
2239 } else if (sOpt == "channel") {
2240 // can add to a model or to a workspace (creates a RooProdPdf either way)
2241 if (get<RooSimultaneous>()) {
2242 return Vary(child);
2243 } else if (get<RooWorkspace>()) {
2244 std::shared_ptr<TObject> out;
2245 child.convertForAcquisition(*this);
2246 if (child.get<RooAbsPdf>()) {
2247 out = acquire(child.fComp);
2248 } else if (!child.fComp) {
2249 out = acquireNew<RooProdPdf>(child.GetName(),
2250 (strlen(child.GetTitle())) ? child.GetTitle() : child.GetName(), RooArgList());
2251 Info("Add", "Created channel RooProdPdf::%s in workspace %s", out->GetName(), get()->GetName());
2252 }
2253 return xRooNode(out, *this);
2254 }
2255 } else if (sOpt == "sample" || sOpt == "func") {
2256 if (get<RooProdPdf>()) {
2257 auto _mainChild = mainChild();
2258 if (_mainChild.get<RooRealSumPdf>()) {
2259 return _mainChild.Add(child, sOpt == "func" ? "func" : "");
2260 } else {
2261 return (*this)["samples"]->Add(child, sOpt == "func" ? "func" : "");
2262 }
2263 }
2264 } else if (sOpt == "dataset") {
2265 if (get<RooWorkspace>()) {
2266 // const_cast<xRooNode&>(child).fFolder = "!datasets";return Add(child);
2267 return (*this).datasets().Add(child);
2268 }
2269 }
2270
2271 if (considerType) {
2272
2273 // interpret 'adding' here as dependent on the object type ...
2274 if (get<RooSimultaneous>()) {
2275 return bins().Add(child);
2276 } else if (TString(child.GetName()).Contains('=')) {
2277 return variations().Add(child);
2278 } else if (get<RooProduct>() || get<RooProdPdf>()) {
2279 return factors().Add(child);
2280 }
2281 }
2282
2283 // Nov 2022 - removed ability to add placeholders ... could bring back if rediscover need for them
2284 // if (!child.get() && child.empty() && strlen(child.GetName())) {
2285 // // can add a 'placeholder' node, note it will be deleted at the next browse
2286 // xRooNode out(child.GetName(),nullptr,*this);
2287 // out.SetTitle(child.GetTitle());
2288 // emplace_back(std::make_shared<xRooNode>(out));
2289 // // update the parent in the out node so that it's copy of the parent knows it has itself in it
2290 // // actually maybe not want this :-/
2291 // //out.fParent = std::make_shared<Node2>(*this);
2292 // for(auto o : *gROOT->GetListOfBrowsers()) {
2293 // if(auto b = dynamic_cast<TBrowser*>(o); b && b->GetBrowserImp()){
2294 // if(auto _b = dynamic_cast<TGFileBrowser*>(
2295 // dynamic_cast<TRootBrowser*>(b->GetBrowserImp())->fActBrowser ); _b) {
2296 // auto _root = _b->fRootDir;
2297 // if (!_root) _root = _b->fListTree->GetFirstItem();
2298 // if (auto item = _b->fListTree->FindItemByObj(_root,this); item) {
2299 // _b->fListTree->AddItem(item,back()->GetName(),back().get());
2300 // }
2301 // }
2302 // }
2303 // }
2304 // return out;
2305 // }
2306
2307 throw std::runtime_error(TString::Format("Cannot add %s to %s", child.GetName(), GetName()));
2308}
2309
2310std::string xRooNode::GetPath() const
2311{
2312 if (!fParent)
2313 return GetName();
2314 return fParent->GetPath() + "/" + GetName();
2315}
2316
2318{
2319 // std::cout << "deleting " << GetPath() << std::endl;
2320}
2321
2323{
2324 if (auto a = get<RooAbsArg>()) {
2325 a->setAttribute("hidden", set);
2326 // if(auto item = GetTreeItem(nullptr); item) {
2327 // if(set) item->SetColor(kRed);
2328 // else item->ClearColor();
2329 // }
2330 }
2331}
2333{
2334 auto a = get<RooAbsArg>();
2335 if (a)
2336 return a->getAttribute("hidden");
2337 return false;
2338}
2339
2341{
2342
2343 if (get() == rhs.get()) {
2344 // nothing to do because objects are identical
2345 return *this;
2346 }
2347
2348 // Info("Combine","Combining %s into %s",rhs.GetPath().c_str(),GetPath().c_str());
2349
2350 // combine components, factors, and variations ... when there is a name clash will combine on that object
2351 for (auto &c : rhs.components()) {
2352 if (auto _c = components().find(c->GetName()); _c) {
2353 _c->Combine(*c);
2354 } else {
2355 Add(*c);
2356 }
2357 }
2358
2359 for (auto &f : rhs.factors()) {
2360 if (auto _f = factors().find(f->GetName()); _f) {
2361 _f->Combine(*f);
2362 } else {
2363 Multiply(*f);
2364 }
2365 }
2366
2367 for (auto &v : rhs.variations()) {
2368 if (auto _v = variations().find(v->GetName()); _v) {
2369 _v->Combine(*v);
2370 } else {
2371 Vary(*v);
2372 }
2373 }
2374
2375 // todo: Should also transfer over binnings of observables
2376
2377 return *this;
2378}
2379
2380xRooNode xRooNode::shallowCopy(const std::string &name, std::shared_ptr<xRooNode> parent)
2381{
2382 xRooNode out(name.c_str(), nullptr,
2383 parent /*? parent : fParent -- was passing fParent for getObject benefit before fProvider concept*/);
2384 // if(!parent) out.fAcquirer = true;
2385 if (!parent)
2386 out.fProvider = fParent;
2387
2388 auto o = get();
2389 if (!o) {
2390 return out;
2391 }
2392
2393 if (auto s = get<RooSimultaneous>(); s) {
2394 auto chans = bins();
2395 if (!chans.empty()) {
2396 // create a new RooSimultaneous with shallow copies of each channel
2397
2398 std::shared_ptr<RooSimultaneous> pdf = out.acquire<RooSimultaneous>(
2399 name.c_str(), o->GetTitle(), const_cast<RooAbsCategoryLValue &>(s->indexCat()));
2400
2401 for (auto &c : chans) {
2402 TString cName(c->GetName());
2403 cName = cName(cName.Index('=') + 1, cName.Length());
2404 // by passing out as the parent, will ensure out acquires everything created
2405 auto c_copy =
2406 c->shallowCopy(name + "_" + c->get()->GetName(), std::shared_ptr<xRooNode>(&out, [](xRooNode *) {}));
2407 pdf->addPdf(*dynamic_cast<RooAbsPdf *>(c_copy.get()), cName);
2408 }
2409 out.fComp = pdf;
2410 return out;
2411 }
2412 } else if (auto p = dynamic_cast<RooProdPdf *>(o); p) {
2413 // main pdf will be copied too
2414 std::shared_ptr<RooProdPdf> pdf =
2415 std::dynamic_pointer_cast<RooProdPdf>(out.acquire(std::shared_ptr<TObject>(p->Clone(/*name.c_str()*/)), false,
2416 true)); // use clone to copy all attributes etc too
2417 auto main = mainChild();
2418 if (main) {
2419 auto newMain =
2420 std::dynamic_pointer_cast<RooAbsArg>(out.acquire(std::shared_ptr<TObject>(main->Clone()), false, true));
2421 std::cout << newMain << " " << newMain->GetName() << std::endl;
2422 // pdf->replaceServer(*pdf->pdfList().find(main->GetName()), *newMain, true, true);
2423 // const_cast<RooArgList&>(pdf->pdfList()).replace(*pdf->pdfList().find(main->GetName()), *newMain);
2424 pdf->redirectServers(RooArgList(*newMain));
2425 }
2426 out.fComp = pdf;
2427 out.sterilize();
2428 return out;
2429 }
2430
2431 return out;
2432}
2433
2435{
2436 static std::unique_ptr<cout_redirect> capture;
2437 std::string captureStr;
2438 bool doCapture = false;
2439 if (!capture && gROOT->FromPopUp()) { // FromPopUp means user executed from the context menu
2440 capture = std::make_unique<cout_redirect>(captureStr);
2441 doCapture = true;
2442 }
2443
2444 TString sOpt(opt);
2445 int depth = 0;
2446 if (sOpt.Contains("depth=")) {
2447 depth = TString(sOpt(sOpt.Index("depth=") + 6, sOpt.Length())).Atoi();
2448 sOpt.ReplaceAll(TString::Format("depth=%d", depth), "");
2449 }
2450 int indent = 0;
2451 if (sOpt.Contains("indent=")) {
2452 indent = TString(sOpt(sOpt.Index("indent=") + 7, sOpt.Length())).Atoi();
2453 sOpt.ReplaceAll(TString::Format("indent=%d", indent), "");
2454 }
2455 bool _more = sOpt.Contains("m");
2456 if (_more)
2457 sOpt.Replace(sOpt.Index("m"), 1, "");
2458 if (sOpt != "")
2459 _more = true;
2460
2461 if (indent == 0) { // only print self if not indenting (will already be printed above if tree traverse)
2462 std::cout << GetPath();
2463 if (get() && get() != this) {
2464 std::cout << ": ";
2465 if (_more || (get<RooAbsArg>() && get<RooAbsArg>()->isFundamental()) || get<RooConstVar>() ||
2467 auto _deps = coords(false).argList(); // want to revert coords after print
2468 auto _snap = std::unique_ptr<RooAbsCollection>(_deps.snapshot());
2469 coords(); // move to coords before printing (in case this matters)
2470 get()->Print(sOpt);
2471 if (auto _fr = get<RooFitResult>(); _fr && dynamic_cast<RooStringVar *>(_fr->constPars().find(".log"))) {
2472 std::cout << "Minimization Logs:" << std::endl;
2473 std::cout << dynamic_cast<RooStringVar *>(_fr->constPars().find(".log"))->getVal() << std::endl;
2474 }
2475 _deps.assignValueOnly(*_snap);
2476 // std::cout << std::endl;
2477 } else {
2478 TString _suffix = "";
2479 if (auto _type = GetNodeType(); strlen(_type)) {
2480 // decided not to show const values until figure out how to update if value changes
2481 /*if (TString(_type)=="Const") _name += TString::Format("
2482 [%s=%g]",_type,v->get<RooConstVar>()->getVal()); else*/
2483 _suffix += TString::Format(" [%s]", _type);
2484 }
2485 if (auto fv = get<RooFormulaVar>()) {
2486 TString formu = TString::Format(" [%s]", fv->expression());
2487 for (size_t i = 0; i < fv->dependents().size(); i++) {
2488 formu.ReplaceAll(TString::Format("x[%zu]", i), fv->dependents()[i].GetName());
2489 }
2490 _suffix += formu;
2491 } else if (auto gv = get<RooGenericPdf>()) {
2492 TString formu = TString::Format(" [%s]", gv->expression());
2493 for (size_t i = 0; i < gv->dependents().size(); i++) {
2494 formu.ReplaceAll(TString::Format("x[%zu]", i), gv->dependents()[i].GetName());
2495 }
2496 _suffix += formu;
2497 }
2498 std::cout << get()->ClassName() << "::" << get()->GetName() << _suffix.Data() << std::endl;
2499 }
2500
2501 } else if (!get()) {
2502 std::cout << std::endl;
2503 }
2504 }
2505 const_cast<xRooNode *>(this)->browse();
2506 std::vector<std::string> folderNames;
2507 for (auto &k : *this) {
2508 if (std::find(folderNames.begin(), folderNames.end(), k->fFolder) == folderNames.end()) {
2509 folderNames.push_back(k->fFolder);
2510 }
2511 }
2512 for (auto &f : folderNames) {
2513 int i = 0;
2514 int iindent = indent;
2515 if (!f.empty()) {
2516 for (int j = 0; j < indent; j++)
2517 std::cout << " ";
2518 std::cout << f << std::endl;
2519 iindent += 1;
2520 }
2521 for (auto &k : *this) {
2522 if (k->fFolder != f) {
2523 i++;
2524 continue;
2525 }
2526 for (int j = 0; j < iindent; j++)
2527 std::cout << " ";
2528 std::cout << i++ << ") " << k->GetName() << " : ";
2529 if (k->get()) {
2530 if (_more || (k->get<RooAbsArg>() && k->get<RooAbsArg>()->isFundamental()) || k->get<RooConstVar>() ||
2531 k->get<RooAbsData>() /*|| k->get<RooProduct>()*/) {
2532 auto _deps = k->coords(false).argList();
2533 auto _snap = std::unique_ptr<RooAbsCollection>(_deps.snapshot());
2534 k->coords(); // move to coords before printing (in case this matters)
2535 k->get()->Print(sOpt); // assumes finishes with an endl
2536 _deps.assignValueOnly(*_snap);
2537 } else {
2538 TString _suffix = "";
2539 if (auto _type = k->GetNodeType(); strlen(_type)) {
2540 // decided not to show const values until figure out how to update if value changes
2541 /*if (TString(_type)=="Const") _name += TString::Format("
2542 [%s=%g]",_type,v->get<RooConstVar>()->getVal()); else*/
2543 _suffix += TString::Format(" [%s]", _type);
2544 }
2545 if (auto fv = k->get<RooFormulaVar>()) {
2546 TString formu = TString::Format(" [%s]", fv->expression());
2547 for (size_t j = 0; j < fv->dependents().size(); j++) {
2548 formu.ReplaceAll(TString::Format("x[%zu]", j), fv->dependents()[j].GetName());
2549 }
2550 _suffix += formu;
2551 } else if (auto gv = k->get<RooGenericPdf>()) {
2552 TString formu = TString::Format(" [%s]", gv->expression());
2553 for (size_t j = 0; j < gv->dependents().size(); j++) {
2554 formu.ReplaceAll(TString::Format("x[%zu]", j), gv->dependents()[j].GetName());
2555 }
2556 _suffix += formu;
2557 }
2558 std::cout << k->get()->ClassName() << "::" << k->get()->GetName() << _suffix.Data() << std::endl;
2559 }
2560 if (depth != 0) {
2561 k->Print(sOpt + TString::Format("depth=%dindent=%d", depth - 1, iindent + 1));
2562 }
2563 } else
2564 std::cout << " NULL " << std::endl;
2565 }
2566 }
2567 if (doCapture) {
2568 capture.reset(); // no captureStr has the string to display
2569 // inject line breaks to avoid msgbox being too wide
2570 size_t lastBreak = 0;
2571 std::string captureStrWithBreaks;
2572 for (size_t i = 0; i < captureStr.size(); i++) {
2574 if (captureStr[i] == '\n') {
2575 lastBreak = i;
2576 }
2577 if (i - lastBreak > 150) {
2578 captureStrWithBreaks += '\n';
2579 lastBreak = i;
2580 }
2581 }
2582 const TGWindow *w =
2583 (gROOT->GetListOfBrowsers()->At(0))
2584 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
2585 : gClient->GetRoot();
2586 new TGMsgBox(gClient->GetRoot(), w, GetName(),
2587 captureStrWithBreaks.c_str()); //,nullptr,kMBDismiss,nullptr,kVerticalFrame,kTextLeft|kTextCenterY);
2588 }
2589}
2590
2592{
2593 if (!child.get()) {
2594
2595 if (auto v = get<RooRealVar>(); v) {
2596
2597 TString constrType = child.GetName();
2598 double mean = std::numeric_limits<double>::quiet_NaN();
2599 double sigma = mean;
2600 if (constrType.BeginsWith("gaussian(")) {
2601 // extract the mean and stddev parameters
2602 // if only one given, it is the stddev
2603 if (constrType.Contains(",")) {
2604 mean = TString(constrType(9, constrType.Index(',') - 9)).Atof();
2605 sigma = TString(constrType(constrType.Index(',') + 1, constrType.Index(')') - constrType.Index(',') + 1))
2606 .Atof();
2607 } else {
2608 mean = std::numeric_limits<double>::quiet_NaN(); // will use the var current value below to set mean
2609 sigma = TString(constrType(9, constrType.Index(')') - 9)).Atof();
2610 }
2611 constrType = "normal";
2612 } else if (constrType == "normal") {
2613 mean = 0;
2614 sigma = 1;
2615 } else if (constrType == "gaussian") {
2616 // extract parameters from the variable
2617 // use current value and error on v as constraint
2618 if (!v->hasError())
2619 throw std::runtime_error("No error on parameter for gaussian constraint");
2620 sigma = v->getError();
2621 mean = v->getVal();
2622 constrType = "normal";
2623 } else if (constrType == "poisson") {
2624 if (!v->hasError())
2625 throw std::runtime_error("No error on parameter for poisson constraint");
2626 mean = 1;
2627 sigma = pow(v->getVal() / v->getError(), 2);
2628 }
2629
2630 if (constrType == "poisson") {
2631 // use current value and error on v as constraint
2632 double tau_val = sigma;
2633 auto globs = acquire<RooRealVar>(Form("globs_%s", v->GetName()), Form("globs_%s", v->GetName()),
2634 v->getVal() * tau_val, (v->getVal() - 5 * v->getError()) * tau_val,
2635 (v->getVal() + 5 * v->getError()) * tau_val);
2636 globs->setConstant();
2637 globs->setAttribute("obs");
2638 globs->setAttribute("global");
2639 globs->setStringAttribute("nominal", TString::Format("%f", tau_val));
2640 auto tau = acquireNew<RooConstVar>(TString::Format("tau_%s", v->GetName()), "", tau_val);
2642 Form("pois_%s", v->GetName()), TString::Format("Poisson Constraint of %s", v->GetTitle()), *globs,
2643 *acquireNew<RooProduct>(TString::Format("mean_%s", v->GetName()),
2644 TString::Format("Poisson Constraint of %s", globs->GetTitle()),
2645 RooArgList(*v, *tau)),
2646 true /* no rounding */);
2647
2648 auto out = Constrain(xRooNode(Form("pois_%s", GetName()), constr));
2649 if (!v->hasError())
2650 v->setError(mean / sqrt(tau_val)); // if v doesnt have an uncert, will put one on it now
2651 Info("Constrain", "Added poisson constraint pdf RooPoisson::%s (tau=%g) for %s", out->GetName(), tau_val,
2652 GetName());
2653 return out;
2654 } else if (constrType == "normal") {
2655
2656 auto globs = acquire<RooRealVar>(Form("globs_%s", v->GetName()), Form("globs_%s", v->GetName()), mean,
2657 mean - 10 * sigma, mean + 10 * sigma);
2658 globs->setAttribute("obs");
2659 globs->setAttribute("global");
2660 globs->setConstant();
2661
2662 globs->setStringAttribute("nominal", TString::Format("%f", mean));
2664 Form("gaus_%s", v->GetName()), TString::Format("Gaussian Constraint of %s", v->GetTitle()), *globs, *v,
2665 *acquireNew<RooConstVar>(TString::Format("sigma_%s", v->GetName()), "", sigma));
2666 auto out = Constrain(xRooNode(Form("gaus_%s", GetName()), constr));
2667 if (!v->hasError())
2668 v->setError(sigma); // if v doesnt have an uncert, will put one on it now
2669 Info("Constrain", "Added gaussian constraint pdf RooGaussian::%s (mean=%g,sigma=%g) for %s", out->GetName(),
2670 mean, sigma, GetName());
2671 return out;
2672 }
2673 }
2674 } else if (auto p = child.get<RooAbsPdf>(); p) {
2675
2676 auto _me = get<RooAbsArg>();
2677 if (!_me) {
2678 throw std::runtime_error("Cannot constrain non arg");
2679 }
2680
2681 if (!p->dependsOn(*_me)) {
2682 throw std::runtime_error("Constraint does not depend on constrainee");
2683 }
2684
2685 // find a parent that can swallow this pdf ... either a RooProdPdf or a RooWorkspace
2686 auto x = fParent;
2687 while (x && !x->get<RooProdPdf>() && !x->get<RooSimultaneous>() && !x->get<RooWorkspace>()) {
2688 x = x->fParent;
2689 }
2690 if (!x) {
2691 throw std::runtime_error("Nowhere to put constraint");
2692 }
2693 // get datasets of the swallower, and add glob to any globs lists
2694 auto childGlobs = child.globs();
2695 if (!childGlobs.empty()) {
2696 for (auto d : x->datasets()) {
2697 if (auto globs = d->get<RooAbsData>()->getGlobalObservables()) {
2699 newGlobs.add(*childGlobs.get<RooArgList>());
2700 d->get<RooAbsData>()->setGlobalObservables(newGlobs);
2701 }
2702 }
2703 // also add to the workspaces globalObservables lists
2704 if (x->ws()) {
2705 for (auto &[k, v] : GETWSSETS(x->ws())) {
2706 if (k == "globalObservables" || TString(k).EndsWith("_GlobalObservables")) {
2707 const_cast<RooArgSet &>(v).add(*childGlobs.get<RooArgList>());
2708 }
2709 }
2710 }
2711 }
2712 if (auto s = x->get<RooSimultaneous>(); s) {
2713 // put into every channel that features parameter
2714 x->browse();
2715 for (auto &c : *x) {
2716 if (auto a = c->get<RooAbsArg>(); a->dependsOn(*_me))
2717 c->Multiply(child);
2718 }
2719 return child;
2720 } else if (x->get<RooProdPdf>()) {
2721 return x->Multiply(child);
2722 } else {
2723 return x->Add(child, "+");
2724 }
2725 }
2726
2727 throw std::runtime_error(TString::Format("Cannot constrain %s", GetName()));
2728}
2729
2731{
2732
2733 class AutoUpdater {
2734 public:
2735 AutoUpdater(xRooNode &_n) : n(_n) {}
2736 ~AutoUpdater() { n.browse(); }
2737 xRooNode &n;
2738 };
2739 AutoUpdater xxx(*this);
2740
2741 if (fBinNumber != -1) {
2742 // scaling a bin ...
2743 if (child.get<RooAbsReal>()) { // if not child then let fall through to create a child and call self again below
2744 // doing a bin-multiplication .. the parent should have a ParamHistFunc called binFactors
2745 // if it doesn't then create one
2746 auto o = std::dynamic_pointer_cast<RooAbsReal>(acquire(child.fComp));
2747
2748 // get binFactor unless parent is a ParamHistFunc already ...
2749
2750 auto binFactors = (fParent->get<ParamHistFunc>()) ? fParent : fParent->factors().find("binFactors");
2751
2752 // it can happen in a loop over bins() that another node has moved fParent inside a product
2753 // so check for fParent having a client with the ORIGNAME:<name> attribute
2754 if (!binFactors && fParent->get<RooAbsArg>()) {
2755 for (auto c : fParent->get<RooAbsArg>()->clients()) {
2756 if (c->IsA() == RooProduct::Class() &&
2757 c->getAttribute(TString::Format("ORIGNAME:%s", fParent->get()->GetName()))) {
2758 // try getting binFactors out of this
2759 binFactors = xRooNode(*c).factors().find("binFactors");
2760 break;
2761 }
2762 }
2763 }
2764
2765 if (!binFactors) {
2766 fParent
2767 ->Multiply(TString::Format("%s_binFactors",
2768 (fParent->mainChild().get())
2769 ? fParent->mainChild()->GetName()
2770 : (fParent->get() ? fParent->get()->GetName() : fParent->GetName()))
2771 .Data(),
2772 "blankshape")
2773 .SetName("binFactors"); // creates ParamHistFunc with all pars = 1 (shared const)
2774 binFactors = fParent->factors().find("binFactors");
2775 if (!binFactors) {
2776 throw std::runtime_error(
2777 TString::Format("Could not create binFactors in parent %s", fParent->GetName()));
2778 }
2779 // auto phf = binFactors->get<ParamHistFunc>();
2780
2781 // create RooProducts for all the bins ... so that added factors don't affect selves
2782 int i = 1;
2783 for (auto &b : binFactors->bins()) {
2784 auto p = acquireNew<RooProduct>(TString::Format("%s_bin%d", binFactors->get()->GetName(), i),
2785 TString::Format("binFactors of bin %d", i), RooArgList());
2786 p->setStringAttribute("alias", TString::Format("%s=%g", binFactors->GetXaxis()->GetParent()->GetName(),
2787 binFactors->GetXaxis()->GetBinCenter(i)));
2788 b->Multiply(*p);
2789 i++;
2790 }
2791 }
2792 // then scale the relevant bin ... if the relevant bin is a "1" then just drop in our factor (inside a
2793 // RooProduct though, to avoid it getting modified by subsequent multiplies)
2794 auto _bin = binFactors->bins().at(fBinNumber - 1);
2795 if (auto phf = binFactors->get<ParamHistFunc>(); phf && _bin) {
2796#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
2797 RooArgList &pSet = phf->_paramSet;
2798#else
2799 RooArgList &pSet = const_cast<RooArgList &>(phf->paramList());
2800#endif
2801 if (strcmp(_bin->GetName(), "1") == 0) {
2803 for (std::size_t i = 0; i < pSet.size(); i++) {
2804 if (int(i) != fBinNumber - 1) {
2805 all.add(*pSet.at(i));
2806 } else {
2807 all.add(*o);
2808 }
2809 }
2810 pSet.removeAll();
2811 pSet.add(all);
2812 } else {
2813 _bin->fBinNumber = -1; // to avoid infinite loop
2814 return _bin->Multiply(child, opt);
2815 }
2816 // } else {else if(_bin->get<RooProduct>()) {
2817 // // multiply the element which will just add it as a factor in the rooproduct
2818 // return _bin->Multiply(child,opt);
2819 // } else {
2820 // // not a rooproduct in this bin yet ... so need to replace with a rooproduct and
2821 // multiply that
2822 // // this avoids the undesired behaviour of shared binFactors getting all impacted by
2823 // mulitplies RooArgList all; auto new_p =
2824 // acquireNew<RooProduct>(TString::Format("%s_bin%d",binFactors->get()->GetName(),fBinNumber),TString::Format("binFactors
2825 // of bin %d",fBinNumber),RooArgList(*_bin->get<RooAbsArg>()));
2826 // new_p->setStringAttribute("alias","")
2827 // for (int i = 0; i < phf->_paramSet.size(); i++) {
2828 // if (i != fBinNumber - 1) all.add(*phf->_paramSet.at(i));
2829 // else all.add(*new_p);
2830 // }
2831 // phf->_paramSet.removeAll();
2832 // phf->_paramSet.add(all);
2833 // // now multiply that bin having converted it to RooProduct
2834 // return binFactors->bins().at(fBinNumber - 1)->Multiply(child,opt);
2835 // }
2836 }
2837 return xRooNode(*o, binFactors);
2838 }
2839 } else if (!get() && fParent) {
2840 // try to 'create' object based on parentage
2841 // add child as a temporary child to help with decision making
2842 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
2843 try {
2844 fComp = fParent->Add(*this, "+").fComp;
2845 } catch (...) {
2846 resize(size() - 1);
2847 std::rethrow_exception(std::current_exception());
2848 }
2849 resize(size() - 1); // remove the temporarily added node
2850 }
2851
2852 if (!child.get()) {
2853 TString sOpt(opt);
2854 sOpt.ToLower();
2855 if (auto o = getObject<RooAbsReal>(child.GetName())) {
2856 auto out = Multiply(xRooNode(o, child.fParent));
2857 // have to protect bin case where get() is null (could change but then must change logic above too)
2858 if (get()) {
2859 Info("Multiply", "Scaled %s by existing factor %s::%s",
2860 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), o->ClassName(), o->GetName());
2861 }
2862 return out;
2863 } else if(sOpt=="const") {
2864 auto out = Multiply(RooConstVar(child.GetName(), child.GetTitle(), 1));
2865 if (get()) {
2866 Info("Multiply", "Scaled %s by new const factor %s",
2867 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
2868 }
2869 return out;
2870 } else if (sOpt == "norm") {
2871 if (TString(child.GetName()).Contains("[") && ws()) {
2872 // assume factory method wanted
2873 auto arg = ws()->factory(child.GetName());
2874 if (arg) {
2875 auto out = Multiply(*arg);
2876 if (get()) {
2877 Info("Multiply", "Scaled %s by new norm factor %s",
2878 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
2879 }
2880 return out;
2881 }
2882 throw std::runtime_error(TString::Format("Failed to create new normFactor %s", child.GetName()));
2883 }
2884 auto out = Multiply(RooRealVar(child.GetName(), child.GetTitle(), 1, -1e-5, 100));
2885 if (get()) {
2886 Info("Multiply", "Scaled %s by new norm factor %s",
2887 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
2888 }
2889 return out;
2890 } else if (sOpt == "shape" || sOpt == "simple" || sOpt == "blankshape") {
2891 // needs axis defined
2892 if (auto ax = GetXaxis(); ax) {
2893 auto h = std::shared_ptr<TH1>(BuildHistogram(dynamic_cast<RooAbsLValue *>(ax->GetParent()), true));
2894 h->Reset();
2895 for (int i = 1; i <= h->GetNbinsX(); i++) {
2896 h->SetBinContent(i, 1);
2897 }
2898 h->SetMinimum(0);
2899 h->SetMaximum(100);
2900 h->SetName(TString::Format(";%s", child.GetName())); // ; char indicates don't "rename" this thing
2901 h->SetTitle(child.GetTitle());
2902 if (sOpt.Contains("shape"))
2903 h->SetOption(sOpt);
2904 auto out = Multiply(*h);
2905 if (get()) {
2906 Info("Multiply", "Scaled %s by new %s factor %s",
2907 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), sOpt.Data(), out->GetName());
2908 }
2909 return out;
2910 }
2911 } else if (sOpt == "overall") {
2913 child.GetName(), child.GetTitle(), RooArgList(), 1, std::vector<double>(), std::vector<double>()));
2914 if (get() /* can happen this is null if on a bin node with no shapeFactors*/) {
2915 Info("Multiply", "Scaled %s by new overall factor %s",
2916 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
2917 }
2918 return out;
2919 } else if (sOpt == "func" && ws()) {
2920 // need to get way to get dependencies .. can't pass all as causes circular dependencies issues.
2921 if (auto arg = ws()->factory(TString("expr::") + child.GetName())) {
2922 auto out = Multiply(*arg);
2923 if (get() /* can happen this is null if on a bin node with no shapeFactors*/) {
2924 Info("Multiply", "Scaled %s by new func factor %s",
2925 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
2926 }
2927 return out;
2928 }
2929 }
2930 }
2931 if (auto h = child.get<TH1>(); h && strlen(h->GetOption()) == 0 && strlen(opt) > 0) {
2932 // put the option in the hist
2933 h->SetOption(opt);
2934 }
2935 if (auto w = get<RooWorkspace>(); w) {
2936 // just acquire
2937 std::shared_ptr<TObject> out;
2938 child.convertForAcquisition(*this);
2939 if (child.get<RooAbsReal>())
2940 out = acquire(child.fComp);
2941 return out;
2942 }
2943
2944 if (strcmp(GetName(), ".coef") == 0) { // covers both .coef and .coefs
2945 // need to add this into the relevant coef ... if its not a RooProduct, replace it with one first
2946 if (auto p = fParent->fParent->get<RooAddPdf>()) {
2947 // may be in no-coef mode ... in which case must create coefs (use "ExtendedBindings" but note that these need
2948 // obs list passing to them
2949 if (p->coefList().empty() && !p->pdfList().empty()) {
2950 for (auto _pdf : p->pdfList()) {
2951 const_cast<RooArgList &>(p->coefList())
2952 .add(*acquireNew<RooExtendedBinding>(TString::Format("%s_extBind", _pdf->GetName()),
2953 TString::Format("Expected Events of %s", _pdf->GetTitle()),
2954 *static_cast<RooAbsPdf *>(_pdf)));
2955 }
2956 Info("Multiply", "Created RooExtendedBinding coefficients for all pdfs of %s so that can multiply coef",
2957 p->GetName());
2958 *reinterpret_cast<bool *>(reinterpret_cast<unsigned char *>(p) +
2959 p->Class()->GetDataMemberOffset("_allExtendable")) = false;
2960 *reinterpret_cast<bool *>(reinterpret_cast<unsigned char *>(p) +
2961 p->Class()->GetDataMemberOffset("_haveLastCoef")) = true;
2962 }
2963 for (size_t i = 0; i < p->pdfList().size(); i++) {
2964 if (p->pdfList().at(i) == fParent->get<RooAbsArg>()) {
2965 auto coefs = p->coefList().at(i);
2966 if (!coefs->InheritsFrom("RooProduct")) {
2968 if (!(strcmp(coefs->GetName(), "1") == 0 || strcmp(coefs->GetName(), "ONE") == 0))
2969 oldCoef.add(*coefs);
2970 auto newCoefs = fParent->acquireNew<RooProduct>(
2971 TString::Format("coefs_%s", fParent->GetName()),
2972 TString::Format("coefficients for %s", fParent->GetName()), oldCoef);
2974 for (size_t j = 0; j < p->coefList().size(); j++) {
2975 if (i == j) {
2976 oldCoefs.add(*newCoefs);
2977 } else {
2978 oldCoefs.add(*p->coefList().at(j));
2979 }
2980 }
2981 const_cast<RooArgList &>(p->coefList()).removeAll();
2982 const_cast<RooArgList &>(p->coefList()).add(oldCoefs);
2983 coefs = newCoefs.get();
2984 }
2985 return xRooNode(*coefs, fParent).Multiply(child);
2986 }
2987 }
2988 } else if(auto p2 = fParent->fParent->get<RooRealSumPdf>()) {
2989 // find our function in the funcList, and then update the coefs of it
2990
2991 for (size_t i = 0; i < p2->funcList().size(); i++) {
2992 if (p2->funcList().at(i) == fParent->get<RooAbsArg>()) {
2993 auto coefs = p2->coefList().at(i);
2994 if (!coefs->InheritsFrom("RooProduct")) {
2996 if (!(strcmp(coefs->GetName(), "1") == 0 || strcmp(coefs->GetName(), "ONE") == 0))
2997 oldCoef.add(*coefs);
2998 auto newCoefs = fParent->acquireNew<RooProduct>(
2999 TString::Format("coefs_%s", fParent->GetName()),
3000 TString::Format("coefficients for %s", fParent->GetName()), oldCoef);
3002 for (size_t j = 0; j < p2->coefList().size(); j++) {
3003 if (i == j) {
3004 oldCoefs.add(*newCoefs);
3005 } else {
3006 oldCoefs.add(*p2->coefList().at(j));
3007 }
3008 }
3009 const_cast<RooArgList &>(p2->coefList()).removeAll();
3010 const_cast<RooArgList &>(p2->coefList()).add(oldCoefs);
3011 coefs = newCoefs.get();
3012 }
3013 return xRooNode(*coefs, fParent).Multiply(child);
3014 }
3015 }
3016 }
3017 throw std::runtime_error("this coefs case is not supported");
3018 }
3019
3020 if (auto p = get<RooProduct>(); p) {
3021 std::shared_ptr<TObject> out;
3022 auto cc = child.fComp;
3023 bool isConverted = (child.convertForAcquisition(*this) != cc);
3024 if (child.get<RooAbsReal>())
3025 out = acquire(child.fComp);
3026
3027 // child may be a histfunc or a rooproduct of a histfunc and a paramhist if has stat errors
3028 if (auto _f = std::dynamic_pointer_cast<RooHistFunc>(
3029 (child.get<RooProduct>()) ? child.factors()[child.GetName()]->fComp : out);
3030 _f && _f->getAttribute("autodensity")) {
3031 // should we flag this as a density? yes if there's no other term marked as the density
3032 bool hasDensity = false;
3033 for (auto &f : factors()) {
3034 if (f->get<RooAbsArg>()->getAttribute("density")) {
3035 hasDensity = true;
3036 break;
3037 }
3038 }
3039 _f->setAttribute("density", !hasDensity && fParent && fParent->get<RooRealSumPdf>());
3040 if (_f->getAttribute("density")) {
3041
3042 // need to divide by bin widths first
3043 for (int i = 0; i < _f->dataHist().numEntries(); i++) {
3044 auto bin_pars = _f->dataHist().get(i);
3045 _f->dataHist().set(*bin_pars, _f->dataHist().weight() / _f->dataHist().binVolume(*bin_pars));
3046 }
3047 _f->setValueDirty();
3048
3049 // promote the axis vars to observables
3050 for (auto &x : xRooNode("tmp", _f).vars()) {
3051 x->get<RooAbsArg>()->setAttribute("obs");
3052 }
3053 }
3054 _f->setAttribute("autodensity", false);
3055 }
3056
3057 if (isConverted && child.get<RooHistFunc>()) {
3058 Info("Multiply", "Created %s factor %s in %s",
3059 child.get<RooAbsArg>()->getAttribute("density") ? "SimpleDensity" : "Simple", child->GetName(),
3060 p->GetName());
3061 } else if (isConverted && child.get<ParamHistFunc>()) {
3062 Info("Multiply", "Created Shape factor %s in %s", child->GetName(), p->GetName());
3063 }
3064
3065 if (auto _f = std::dynamic_pointer_cast<RooAbsReal>(out); _f) {
3066#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3067 p->_compRSet.add(*_f);
3068#else
3069 const_cast<RooArgList &>(p->realComponents()).add(*_f);
3070#endif
3071 p->setValueDirty();
3072
3073 browse();
3074 xRooNode _out(_f, *this);
3075 for (auto &_par : _out.pars()) {
3076 if (auto s = _par->get<RooAbsArg>()->getStringAttribute("boundConstraint"); s) {
3077 bool found = false;
3078 for (auto &_constr : _par->constraints()) {
3079 if (strcmp(s, _constr->get()->GetName()) == 0) {
3080 // constraint is already included
3081 found = true;
3082 break;
3083 }
3084 }
3085 if (!found) {
3086 Info("Multiply", "Pulling in %s boundConstraint: %s", _par->GetName(), s);
3087 auto _pdf = getObject<RooAbsPdf>(s);
3088 if (!_pdf) {
3089 throw std::runtime_error("Couldn't find boundConstraint");
3090 }
3091 _par->Constrain(_pdf);
3092 }
3093 }
3094 }
3095 sterilize();
3096 return _out;
3097 }
3098 } else if (auto p2 = get<RooProdPdf>(); p2) {
3099
3100 std::shared_ptr<TObject> out;
3101 child.convertForAcquisition(*this);
3102 if (child.get<RooAbsPdf>()) {
3103 out = acquire(child.fComp);
3104 } else if (child.get<RooAbsReal>() && mainChild().get<RooRealSumPdf>()) {
3105 // cannot multiply a RooProdPdf by a non pdf
3106 throw std::runtime_error(TString::Format("Cannot multiply %s by non-pdf %s", GetName(), child.GetName()));
3107 // return mainChild().Add(child); - nov 2022 - used to do this but now replaced with exception above
3108 } else if (!child.get() || child.get<RooAbsReal>()) {
3109 // need to create or hide inside a sumpdf or rooadpdf
3110 std::shared_ptr<RooAbsPdf> _pdf;
3111 if (!child.get() && strcmp(child.GetName(), "components") == 0) {
3113 Form("%s_%s", p2->GetName(), child.GetName()),
3114 (strlen(child.GetTitle()) && strcmp(child.GetTitle(), child.GetName())) ? child.GetTitle()
3115 : p2->GetTitle(),
3116 RooArgList() /*, RooArgList() forces coef-mode if we specify this list */);
3117 _pdf = _sumpdf;
3118 } else {
3120 Form("%s_%s", p2->GetName(), child.GetName()),
3121 (strlen(child.GetTitle()) && strcmp(child.GetTitle(), child.GetName())) ? child.GetTitle()
3122 : p2->GetTitle(),
3123 RooArgList(), RooArgList(), true);
3124 _sumpdf->setFloor(true);
3125 _pdf = _sumpdf;
3126 }
3127 _pdf->setStringAttribute("alias", child.GetName());
3128 // transfer axis attributes if present (TODO: should GetXaxis look beyond the immediate parent?)
3129 _pdf->setStringAttribute("xvar", p2->getStringAttribute("xvar"));
3130 _pdf->setStringAttribute("binning", p2->getStringAttribute("binning"));
3131 out = _pdf;
3132 Info("Multiply", "Created %s::%s in channel %s", _pdf->ClassName(), _pdf->GetName(), p2->GetName());
3133 if (child.get<RooAbsReal>())
3134 xRooNode(*out, *this).Add(child);
3135 }
3136
3137 if (auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out); _pdf) {
3138#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3139 const_cast<RooArgList &>(p2->pdfList()).add(*_pdf);
3140#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
3141 p2->_pdfNSetList.emplace_back(std::make_unique<RooArgSet>("nset"));
3142#else
3143 p->_pdfNSetList.Add(new RooArgSet("nset"));
3144#endif
3145 if (!p2->canBeExtended() && _pdf->canBeExtended()) {
3146 p2->_extendedIndex = p2->_pdfList.size() - 1;
3147 }
3148#else
3149 p2->addPdfs(RooArgSet(*_pdf));
3150#endif
3151 sterilize();
3152 browse();
3153 return xRooNode(_pdf, *this);
3154 }
3155 } else if (auto p3 = get<RooRealSumPdf>(); p3) {
3156 // multiplying all current and future components
3157 std::shared_ptr<TObject> out;
3158 child.convertForAcquisition(*this);
3159 if (child.get<RooAbsReal>()) {
3160 out = acquire(child.fComp);
3161 for (auto &c : components()) {
3162 c->Multiply(out);
3163 }
3164 TString s = p3->getStringAttribute("global_factors");
3165 if (s != "")
3166 s += ";";
3167 s += out->GetName();
3168 p3->setStringAttribute("global_factors", s);
3169 Info(
3170 "Multiply",
3171 "Flagged %s as a global factor in channel %s (is applied to all current and future samples in the channel)",
3172 out->GetName(), p3->GetName());
3173 return xRooNode(out, *this);
3174 }
3175
3176 } else if (auto p4 = get<RooAbsPdf>(); p4 && !(fParent && fParent->get<RooRealSumPdf>())) {
3177 // multiply the coefs (if this isn't part of a RooAddPdf or RooRealSumPdf then we will eventually throw exception
3178 return coefs().Multiply(child);
3179 } else if (auto p5 = get<RooAbsReal>(); p5 && (!get<RooAbsPdf>() || (fParent && fParent->get<RooRealSumPdf>()))) {
3180 // replace this obj with a RooProduct to allow for multiplication
3181
3182 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
3183 std::set<RooAbsArg *> cl;
3184 for (auto &arg : p5->clients()) {
3185 cl.insert(arg);
3186 }
3187
3188 // if multiple clients, see if only one client is in parentage route
3189 // if so, then assume thats the only client we should replace in
3190 if (cl.size() > 1) {
3191 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
3192 cl.clear();
3193 cl.insert(fParent->get<RooAbsArg>());
3194 } else {
3195 Warning("Multiply", "Scaling %s that has multiple clients", p5->GetName());
3196 }
3197 }
3198
3199 auto new_p = acquireNew<RooProduct>(TString::Format("prod_%s", p5->GetName()), p5->GetTitle(), RooArgList(*p5));
3200 // copy attributes over
3201 for (auto &a : p5->attributes())
3202 new_p->setAttribute(a.c_str());
3203 for (auto &a : p5->stringAttributes())
3204 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
3205 if (!new_p->getStringAttribute("alias"))
3206 new_p->setStringAttribute("alias", p5->GetName());
3207 auto old_p = p5;
3208 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
3209 for (auto arg : cl) {
3210 arg->redirectServers(RooArgSet(*new_p), false, true);
3211 }
3212
3213 fComp = new_p;
3214 return Multiply(child);
3215 }
3216
3217 // before giving up here, assume user wanted a norm factor type if child is just a name
3218 if (!child.get() && strlen(opt) == 0)
3219 return Multiply(child, "norm");
3220
3221 throw std::runtime_error(
3222 TString::Format("Cannot multiply %s by %s%s", GetPath().c_str(), child.GetName(),
3223 (!child.get() && strlen(opt) == 0) ? " (forgot to specify factor type?)" : ""));
3224}
3225
3227{
3228
3229 auto p5 = get<RooAbsArg>();
3230 if (!p5) {
3231 throw std::runtime_error("Only replacement of RooAbsArg is supported");
3232 }
3233 node.convertForAcquisition(*this, "func");
3234
3235 auto new_p = node.get<RooAbsArg>();
3236 if (!new_p) {
3237 throw std::runtime_error(TString::Format("Cannot replace with %s", node.GetName()));
3238 }
3239 auto out = acquire(node.fComp);
3240 new_p = std::dynamic_pointer_cast<RooAbsArg>(out).get();
3241
3242 std::set<RooAbsArg *> cl;
3243 for (auto &arg : p5->clients()) {
3244 if (arg == new_p)
3245 continue; // do not replace in self ... although redirectServers will prevent that anyway
3246 cl.insert(arg);
3247 }
3248
3249 // if multiple clients, see if only one client is in parentage route
3250 // if so, then assume thats the only client we should replace in
3251 if (cl.size() > 1) {
3252 if (fParent && fParent->get<RooAbsArg>() && cl.count(fParent->get<RooAbsArg>()) > 0) {
3253 cl.clear();
3254 cl.insert(fParent->get<RooAbsArg>());
3255 } else {
3256 std::stringstream clientList;
3257 for (auto c : cl)
3258 clientList << c->GetName() << ",";
3259 Warning("Replace", "Replacing %s in all clients: %s", p5->GetName(), clientList.str().c_str());
3260 }
3261 }
3262
3263 new_p->setAttribute(Form("ORIGNAME:%s", p5->GetName())); // used in redirectServers to say what this replaces
3264 for (auto arg : cl) {
3265 // if RooFormulaVar need to ensure the internal formula has been "constructed" otherwise will try to construct
3266 // it from the original expression that may have old parameter in it.
3267 if (auto p = dynamic_cast<RooFormulaVar *>(arg))
3268 p->ok(); // triggers creation of RooFormula
3269 arg->redirectServers(RooArgSet(*new_p), false, true);
3270 }
3271 return node;
3272}
3273
3275{
3276
3277 class AutoUpdater {
3278 public:
3279 AutoUpdater(xRooNode &_n) : n(_n) {}
3280 ~AutoUpdater() { n.browse(); }
3281 xRooNode &n;
3282 };
3283 AutoUpdater xxx(*this);
3284
3285 if (!get() && fParent) {
3286 // try to 'create' object based on parentage
3287 // add child as a temporary child to help with decision making
3288 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
3289 try {
3290 fComp = fParent->Add(*this, "+").fComp;
3291 } catch (...) {
3292 resize(size() - 1);
3293 std::rethrow_exception(std::current_exception());
3294 }
3295 resize(size() - 1); // remove the temporarily added node
3296 }
3297
3298 if (auto p = mainChild(); p) {
3299 // variations applied to the main child if has one
3300 return p.Vary(child);
3301 }
3302
3303 if (auto s = get<RooSimultaneous>(); s && s->indexCat().IsA() == RooCategory::Class()) {
3304 // name is used as cat label
3305 std::string label = child.GetName();
3306 if (auto pos = label.find('='); pos != std::string::npos)
3307 label = label.substr(pos + 1);
3308 if (!s->indexCat().hasLabel(label)) {
3309 static_cast<RooCategory &>(const_cast<RooAbsCategoryLValue &>(s->indexCat())).defineType(label.c_str());
3310 }
3311 std::shared_ptr<TObject> out;
3312 child.convertForAcquisition(*this);
3313 if (child.get<RooAbsPdf>()) {
3314 out = acquire(child.fComp); // may create a channel from a histogram
3315 } else if (!child.fComp) {
3316 out = acquireNew<RooProdPdf>(TString::Format("%s_%s", s->GetName(), label.c_str()),
3317 (strlen(child.GetTitle())) ? child.GetTitle() : label.c_str(), RooArgList());
3318 Info("Vary", "Created channel RooProdPdf::%s in model %s", out->GetName(), s->GetName());
3319 }
3320
3321 if (auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out); _pdf) {
3322 s->addPdf(*_pdf, label.c_str());
3323 sterilize();
3324 // clear children for reload and update shared axis
3325 clear();
3326 fXAxis.reset();
3327 browse();
3328 return xRooNode(TString::Format("%s=%s", s->indexCat().GetName(), label.data()), _pdf, *this);
3329 }
3330
3331 } else if (auto p = get<RooStats::HistFactory::FlexibleInterpVar>(); p) {
3332
3333 // child needs to be a constvar ...
3334 child.convertForAcquisition(*this);
3335 auto _c = child.get<RooConstVar>();
3336 if (!_c && child.get()) {
3337 throw std::runtime_error("Only pure consts can be set as variations of a flexible interpvar");
3338 }
3339#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3340 double value = (_c ? _c->getVal() : p->_nominal);
3341 double nomVal = p->_nominal;
3342#else
3343 double value = (_c ? _c->getVal() : p->nominal());
3344 double nomVal = p->nominal();
3345#endif
3346
3347 TString cName(child.GetName());
3348 if (cName == "nominal") {
3349 p->setNominal(value);
3350 return *(this->variations().at(cName.Data()));
3351 }
3352 if (cName.CountChar('=') != 1) {
3353 throw std::runtime_error("unsupported variation form");
3354 }
3355 std::string parName = cName(0, cName.Index('='));
3356 double parVal = TString(cName(cName.Index('=') + 1, cName.Length())).Atof();
3357 if (parVal != 1 && parVal != -1) {
3358 throw std::runtime_error("unsupported variation magnitude");
3359 }
3360 bool high = parVal > 0;
3361
3362 if (parName.empty()) {
3363 p->setNominal(value);
3364 } else {
3365 auto v = fParent->getObject<RooRealVar>(parName);
3366 if (!v)
3367 v = fParent->acquire<RooRealVar>(parName.c_str(), parName.c_str(), -5, 5);
3368 if (!v->hasError())
3369 v->setError(1);
3370
3371 if (!p->findServer(*v)) {
3372#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3373 p->_paramList.add(*v);
3374 p->_low.push_back(0);
3375 p->_high.push_back(0);
3376 p->_interpCode.push_back(4);
3377#else
3378 const_cast<RooListProxy &>(p->variables()).add(*v);
3379 const_cast<std::vector<double> &>(p->low()).push_back(0);
3380 const_cast<std::vector<double> &>(p->high()).push_back(0);
3381 const_cast<std::vector<int> &>(p->interpolationCodes()).push_back(4);
3382#endif
3383 v->setAttribute(Form("SYMMETRIC%s_%s", high ? "+" : "-", GetName())); // flag for symmetrized
3384 }
3385
3386 if (high) {
3387 p->setHigh(*v, value);
3388 if (v->getAttribute(Form("SYMMETRIC+_%s", GetName()))) {
3389 p->setLow(*v, 2 * nomVal - value);
3390 }
3391 v->setAttribute(Form("SYMMETRIC-_%s", GetName()), false);
3392 } else {
3393 p->setLow(*v, value);
3394 if (v->getAttribute(Form("SYMMETRIC-_%s", GetName()))) {
3395 p->setHigh(*v, 2 * nomVal - value);
3396 }
3397 v->setAttribute(Form("SYMMETRIC+_%s", GetName()), false);
3398 }
3399
3400 /*if (!unconstrained && fParent->pars()[v->GetName()].constraints().empty()) {
3401 fParent->pars()[v->GetName()].constraints().add("normal");
3402 }*/
3403 }
3404 return *(this->variations().at(cName.Data()));
3405 } else if (auto p2 = get<PiecewiseInterpolation>(); p2) {
3406 TString cName(child.GetName());
3407 if (cName.CountChar('=') != 1) {
3408 throw std::runtime_error("unsupported variation form");
3409 }
3410 TString parName = cName(0, cName.Index('='));
3411 double parVal = TString(cName(cName.Index('=') + 1, cName.Length())).Atof();
3412 if (parVal != 1 && parVal != -1) {
3413 throw std::runtime_error("unsupported variation magnitude");
3414 }
3415#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3416 RooHistFunc *f = dynamic_cast<RooHistFunc *>(p2->_nominal.absArg());
3417 if (!f) {
3418 throw std::runtime_error(
3419 TString::Format("Interpolating %s instead of RooHistFunc", p2->_nominal.absArg()->ClassName()));
3420 }
3421#else
3422 RooHistFunc *f = dynamic_cast<RooHistFunc *>(const_cast<RooAbsReal *>(p2->nominalHist()));
3423 if (!f) {
3424 throw std::runtime_error(
3425 TString::Format("Interpolating %s instead of RooHistFunc", p2->nominalHist()->ClassName()));
3426 }
3427#endif
3428 RooHistFunc *nomf = f;
3429 RooHistFunc *otherf = nullptr;
3430 size_t i = 0;
3431 for (auto par : p2->paramList()) {
3432 if (parName == par->GetName()) {
3433 f = dynamic_cast<RooHistFunc *>((parVal > 0 ? p2->highList() : p2->lowList()).at(i));
3434 otherf = dynamic_cast<RooHistFunc *>((parVal > 0 ? p2->lowList() : p2->highList()).at(i));
3435 break;
3436 }
3437 i++;
3438 }
3439 if (i == p2->paramList().size() && !child.get<RooAbsReal>()) {
3440
3441 // need to add the parameter
3442 auto v = acquire<RooRealVar>(parName, parName, -5, 5);
3443 if (!v->hasError())
3444 v->setError(1);
3445
3446 std::shared_ptr<RooHistFunc> up(
3447 static_cast<RooHistFunc *>(f->Clone(Form("%s_%s_up", f->GetName(), parName.Data()))));
3448 std::shared_ptr<RooHistFunc> down(
3449 static_cast<RooHistFunc *>(f->Clone(Form("%s_%s_down", f->GetName(), parName.Data()))));
3450 // RooHistFunc doesn't clone it's data hist ... do it ourself (will be cloned again if imported into a ws)
3451#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3452 std::unique_ptr<RooDataHist> h1(
3453 static_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", up->GetName()))));
3454 std::unique_ptr<RooDataHist> h2(
3455 static_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", down->GetName()))));
3456 up->_dataHist = dynamic_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", up->GetName())));
3457 down->_dataHist = dynamic_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", down->GetName())));
3458#else
3459 up->cloneAndOwnDataHist(TString::Format("hist_%s", up->GetName()));
3460 down->cloneAndOwnDataHist(TString::Format("hist_%s", down->GetName()));
3461#endif
3462 auto ups = std::dynamic_pointer_cast<RooHistFunc>(acquire(up, false, true));
3463 auto downs = std::dynamic_pointer_cast<RooHistFunc>(acquire(down, false, true));
3464#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3465 p2->_highSet.add(*ups.get());
3466 p2->_lowSet.add(*downs.get());
3467 p2->_interpCode.push_back(4);
3468 p2->_paramSet.add(*v);
3469#else
3470 const_cast<RooArgList &>(p2->highList()).add(*ups);
3471 const_cast<RooArgList &>(p2->lowList()).add(*downs);
3472 const_cast<std::vector<int> &>(p2->interpolationCodes()).push_back(4);
3473 const_cast<RooArgList &>(p2->paramList()).add(*v);
3474#endif
3475 p2->setValueDirty();
3476 f = ((parVal > 0) ? ups : downs).get();
3477 otherf = ((parVal > 0) ? downs : ups).get();
3478 // start off with everything being symmetric
3479 f->setStringAttribute("symmetrizes", otherf->GetName());
3480 f->setStringAttribute("symmetrize_nominal", nomf->GetName());
3481 otherf->setStringAttribute("symmetrized_by", f->GetName());
3482
3483 // constrain par if required
3484 /*if (!unconstrained && fParent->pars()[v->GetName()].constraints().empty()) {
3485 fParent->pars()[v->GetName()].constraints().add("normal");
3486 }*/
3487 }
3488
3489 // child.convertForAcquisition(*this);
3490 if (f) {
3491 if (child.get())
3492 xRooNode("tmp", *f, *this) = *child.get();
3493 f->setValueDirty();
3494 xRooNode out(*f, *this);
3495 out.sterilize();
3496 return out;
3497 }
3498
3499 } else if (auto p3 = get<RooConstVar>(); p3) {
3500
3501 // never vary the universal consts ... its too dangerous
3502 if (p3->getAttribute("RooRealConstant_Factory_Object")) {
3503 throw std::runtime_error("Cannot vary pure constants");
3504 }
3505
3506 // inject a FlexibleInterpVar ...
3507
3508 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
3509 std::set<RooAbsArg *> cl;
3510 for (auto &arg : p3->clients()) {
3511 cl.insert(arg);
3512 }
3513 // if multiple clients, see if only one client is in parentage route
3514 // if so, then assume thats the only client we should replace in
3515 if (cl.size() > 1) {
3516 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
3517 cl.clear();
3518 cl.insert(fParent->get<RooAbsArg>());
3519 } else {
3520 Warning("Vary", "Varying %s that has multiple clients", p3->GetName());
3521 }
3522 }
3523 p3->setStringAttribute("origName", p3->GetName());
3524 TString n = p3->GetName();
3525 p3->SetName(Form("%s_nominal", p3->GetName())); // if problems should perhaps not rename here
3526
3528 std::vector<double>(), std::vector<double>());
3529
3530 // copy attributes over
3531 for (auto &a : p3->attributes())
3532 new_p->setAttribute(a.c_str());
3533 for (auto &a : p3->stringAttributes())
3534 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
3535 // if (!new_p->getStringAttribute("alias")) new_p->setStringAttribute("alias",p->GetName());
3536 auto old_p = p3;
3537 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
3538 for (auto arg : cl) {
3539 arg->redirectServers(RooArgSet(*new_p), false, true);
3540 }
3541
3542 fComp = new_p;
3543 return Vary(child);
3544
3545 } else if (auto p4 = get<RooAbsReal>(); p4) {
3546 // inject an interpolation node
3547
3548 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
3549 std::set<RooAbsArg *> cl;
3550 for (auto &arg : p4->clients()) {
3551 cl.insert(arg);
3552 }
3553 // if multiple clients, see if only one client is in parentage route
3554 // if so, then assume thats the only client we should replace in
3555 if (cl.size() > 1) {
3556 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
3557 cl.clear();
3558 cl.insert(fParent->get<RooAbsArg>());
3559 } else {
3560 Warning("Vary", "Varying %s that has multiple clients", p4->GetName());
3561 }
3562 }
3563 p4->setStringAttribute("origName", p4->GetName());
3564 TString n = p4->GetName();
3565 p4->SetName(Form("%s_nominal", p4->GetName())); // if problems should perhaps not rename here
3566
3568
3569 // copy attributes over
3570 for (auto &a : p4->attributes())
3571 new_p->setAttribute(a.c_str());
3572 for (auto &a : p4->stringAttributes())
3573 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
3574 // if (!new_p->getStringAttribute("alias")) new_p->setStringAttribute("alias",p->GetName());
3575 auto old_p = p4;
3576 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
3577 for (auto arg : cl) {
3578 arg->redirectServers(RooArgSet(*new_p), false, true);
3579 }
3580
3581 fComp = new_p;
3582 return Vary(child);
3583 }
3584
3585 Print();
3586 throw std::runtime_error(TString::Format("Cannot vary %s with %s", GetName(), child.GetName()));
3587}
3588
3590{
3592}
3593
3594bool xRooNode::SetContent(double value, const char *par, double val)
3595{
3596 return SetContents(RooConstVar(GetName(), GetTitle(), value), par, val);
3597}
3598
3601 {
3602 if (x && b)
3603 x->setBinning(*b);
3604 if (b)
3605 delete b;
3606 }
3607 RooRealVar *x = nullptr;
3608 RooAbsBinning *b = nullptr;
3609};
3610
3612{
3613
3614 if (!get()) {
3615 fComp = std::shared_ptr<TObject>(const_cast<TObject *>(&o), [](TObject *) {});
3616 if (fParent && !fParent->find(GetName())) {
3617 // either a temporary or a placeholder so need to try genuinely adding
3618 fComp = fParent->Add(*this, "+").fComp;
3619 if (auto a = get<RooAbsArg>(); a && strcmp(a->GetName(), GetName()) && !a->getStringAttribute("alias")) {
3620 a->setStringAttribute("alias", GetName());
3621 }
3622 if (!fComp)
3623 throw std::runtime_error("Cannot determine type");
3624 return *this;
3625 }
3626 }
3627
3628 if (auto h = dynamic_cast<const TH1 *>(&o); h) {
3629 /*auto f = get<RooHistFunc>();
3630 if (!f) {
3631 // if it's a RooProduct locate child with the same name
3632 if (get<RooProduct>()) {
3633 f = factors()[GetName()]->get<RooHistFunc>();
3634 }
3635
3636
3637
3638 }*/
3639 bool _isData = get<RooAbsData>();
3641 if (_isData) {
3642 // need to ensure x-axis matches this h
3643 auto ax = GetXaxis();
3644 if (!ax)
3645 throw std::runtime_error("no xaxis");
3646 auto _v = dynamic_cast<RooRealVar *>(ax->GetParent());
3647 if (_v) {
3648 _b.x = _v;
3649 _b.b = dynamic_cast<RooAbsBinning *>(_v->getBinningPtr(nullptr)->Clone());
3650 if (h->GetXaxis()->IsVariableBinSize()) {
3651 _v->setBinning(RooBinning(h->GetNbinsX(), h->GetXaxis()->GetXbins()->GetArray()));
3652 } else {
3653 _v->setBinning(RooUniformBinning(h->GetXaxis()->GetXmin(), h->GetXaxis()->GetXmax(), h->GetNbinsX()));
3654 }
3655 }
3656 }
3657
3658 if (true) {
3659 for (int bin = 1; bin <= h->GetNbinsX(); bin++) {
3660 SetBinContent(bin, h->GetBinContent(bin));
3661 /*double value = h->GetBinContent(bin);
3662 auto bin_pars = f->dataHist().get(bin - 1);
3663 if (f->getAttribute("density")) {
3664 value /= f->dataHist().binVolume(*bin_pars);
3665 }
3666 f->dataHist().set(*bin_pars, value);*/
3667 if (!_isData && h->GetSumw2N() && !SetBinError(bin, h->GetBinError(bin)))
3668 throw std::runtime_error("Failed setting stat error");
3669 }
3670 return *this;
3671 }
3672 } else if (auto _c = dynamic_cast<const RooConstVar *>(&o); _c) {
3673
3674 if (auto a = get<RooAbsArg>();
3675 (a && a->isFundamental()) || get<RooConstVar>() || get<RooStats::HistFactory::FlexibleInterpVar>()) {
3676 SetBinContent(1, _c->getVal());
3677 return *this;
3678 } else if (get<RooAbsData>()) { // try to do assignment to a dataset (usually setting a bin content)
3679 SetBinContent(0, _c->getVal());
3680 return *this;
3681 }
3682 }
3683
3684 throw std::runtime_error("Assignment failed");
3685
3686 /*
3687
3688 if (fParent && !fParent->mk()) {
3689 throw std::runtime_error("mk failure");
3690 }
3691
3692 if (fComp) return *this;
3693
3694 if (o.InheritsFrom("RooAbsArg")) {
3695 fComp = acquire(std::shared_ptr<TObject>(const_cast<TObject*>(&o),[](TObject* o){}));
3696 std::dynamic_pointer_cast<RooAbsArg>(fComp)->setStringAttribute("alias",GetName());
3697 }
3698
3699 if (fComp && fParent) {
3700 fParent->incorporate(fComp);
3701 }
3702
3703
3704 return *this;
3705 */
3706}
3707
3708void xRooNode::_fit_(const char *constParValues, const char* options)
3709{
3710 try {
3711 auto _pars = pars();
3712 // std::unique_ptr<RooAbsCollection> snap(_pars.argList().snapshot());
3713 TStringToken pattern(constParValues, ",");
3714 std::map<RooAbsRealLValue *, double> valsToSet;
3715 while (pattern.NextToken()) {
3716 auto idx = pattern.Index('=');
3717 TString pat = (idx == -1) ? TString(pattern) : TString(pattern(0, idx));
3718 double val =
3719 (idx == -1) ? std::numeric_limits<double>::quiet_NaN() : TString(pattern(idx + 1, pattern.Length())).Atof();
3720 bool foundArg = false;
3721 for (auto p : _pars.argList()) {
3722 if (TString(p->GetName()).Contains(TRegexp(pat, true))) {
3723 foundArg = true;
3724 p->setAttribute("Constant", true);
3725 if (!std::isnan(val)) {
3726 valsToSet[dynamic_cast<RooAbsRealLValue *>(p)] = val;
3727 // dynamic_cast<RooAbsRealLValue *>(p)->setVal(val); // don't set yet, to allow for asimov dataset
3728 // creation based on current values
3729 }
3730 }
3731 }
3732 if(!foundArg) {
3733 throw std::runtime_error(std::string("Unrecognised parameter: ") + pat.Data());
3734 }
3735 }
3736
3737 // parse options
3738 TStringToken pattern2(options, ",");
3740 while (pattern2.NextToken()) {
3741 auto idx = pattern2.Index('=');
3742 TString pat = (idx == -1) ? TString(pattern2) : TString(pattern2(0, idx));
3743 TString val = TString(pattern2(idx + 1, pattern2.Length()));
3744 if(auto o = defaultOpts->FindObject(pat)) {
3745 defaultOpts->Remove(o);
3746 delete o;
3747 }
3748 defaultOpts->Add( new RooCmdArg(pat,val.IsDec() ? val.Atoi() : 0,0,val.IsFloat() ? val.Atof() : 0.,0.,val.IsAlpha()?val:nullptr) );
3749 }
3750
3751 // use the first selected dataset
3752 auto _dsets = datasets();
3753 TString dsetName = "";
3754 for (auto &d : _dsets) {
3755 if (d->get()->TestBit(1 << 20)) {
3756 dsetName = d->get()->GetName();
3757 break;
3758 }
3759 }
3760 auto _nll = nll(dsetName.Data(),*defaultOpts);
3761 // can now set the values
3762 for (auto [p, v] : valsToSet) {
3763 p->setVal(v);
3764 }
3765 _nll.fitConfigOptions()->SetValue("LogSize", 65536);
3766 _nll.fitConfig()->MinimizerOptions().SetPrintLevel(0);
3767 auto fr = _nll.minimize();
3768 //_pars.argList() = *snap; // restore values - irrelevant as SetFitResult will restore values
3769 if (!fr.get())
3770 throw std::runtime_error("Fit Failed");
3771 SetFitResult(fr.get());
3773 for (unsigned int i = 0; i < fr->numStatusHistory(); i++) {
3774 statusCodes += TString::Format("\n%s = %d", fr->statusLabelHistory(i), fr->statusCodeHistory(i));
3775 }
3776 const TGWindow *w =
3777 (gROOT->GetListOfBrowsers()->At(0))
3778 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
3779 : gClient->GetRoot();
3780 if (fr->status() != 0) {
3781 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished with Bad Status Code",
3782 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n-------------%s",
3783 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), statusCodes.Data()),
3785 } else if (fr->covQual() != 3 && _nll.fitConfig()->ParabErrors()) {
3786 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished with Bad Covariance Quality",
3787 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n-------------%s",
3788 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), statusCodes.Data()),
3790 } else {
3791 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished Successfully",
3792 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n-------------%s",
3793 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), statusCodes.Data()));
3794 }
3795 } catch (const std::exception &e) {
3796 new TGMsgBox(
3797 gClient->GetRoot(),
3798 (gROOT->GetListOfBrowsers()->At(0))
3799 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
3800 : gClient->GetRoot(),
3801 "Exception", e.what(), kMBIconExclamation, kMBOk); // deletes self on dismiss?
3802 }
3803}
3804
3806{
3807 try {
3808 datasets().Add(datasetName, expected ? "asimov" : "toy");
3809 } catch (const std::exception &e) {
3810 new TGMsgBox(
3811 gClient->GetRoot(),
3812 (gROOT->GetListOfBrowsers()->At(0))
3813 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
3814 : gClient->GetRoot(),
3815 "Exception", e.what(),
3816 kMBIconExclamation); // deletes self on dismiss?
3817 }
3818}
3819
3820void xRooNode::_scan_(const char *what, double nToys, const char *xvar, int nBinsX, double lowX,
3821 double highX /*, const char*, int, double, double*/, const char *constParValues, const char* options)
3822{
3823 try {
3826
3827 // use the first selected dataset
3828 auto _dsets = datasets();
3829 TString dsetName = "";
3830 for (auto &d : _dsets) {
3831 if (d->get()->TestBit(1 << 20)) {
3832 dsetName = d->get()->GetName();
3833 break;
3834 }
3835 }
3836 auto _pars = pars();
3837 std::unique_ptr<RooAbsCollection> snap(_pars.argList().snapshot());
3838 TStringToken pattern(constParValues, ",");
3839 while (pattern.NextToken()) {
3840 auto idx = pattern.Index('=');
3841 TString pat = (idx == -1) ? TString(pattern) : TString(pattern(0, idx));
3842 double val =
3843 (idx == -1) ? std::numeric_limits<double>::quiet_NaN() : TString(pattern(idx + 1, pattern.Length())).Atof();
3844 bool foundArg = false;
3845 for (auto par : _pars.argList()) {
3846 if (TString(par->GetName()).Contains(TRegexp(pat, true))) {
3847 foundArg=true;
3848 par->setAttribute("Constant", true);
3849 if (!std::isnan(val)) {
3850 dynamic_cast<RooAbsRealLValue *>(par)->setVal(val);
3851 }
3852 }
3853 }
3854 if(!foundArg) {
3855 throw std::runtime_error(std::string("Unrecognised parameter: ") + pat.Data());
3856 }
3857
3858 }
3859
3860 // parse options
3861 TStringToken pattern2(options, ",");
3863 while (pattern2.NextToken()) {
3864 auto idx = pattern2.Index('=');
3865 TString pat = (idx == -1) ? TString(pattern2) : TString(pattern2(0, idx));
3866 TString val = TString(pattern2(idx + 1, pattern2.Length()));
3867 if(auto o = defaultOpts->FindObject(pat)) {
3868 defaultOpts->Remove(o);
3869 delete o;
3870 }
3871 defaultOpts->Add( new RooCmdArg(pat,val.IsDec() ? val.Atoi() : 0,0,val.IsFloat() ? val.Atof() : 0.,0.,val.IsAlpha()?val:nullptr) );
3872 }
3873
3874 auto hs = nll(dsetName.Data(),*defaultOpts).hypoSpace(sXvar);
3875 hs.SetName(TUUID().AsString());
3876 if (nToys) {
3877 sWhat += " toys";
3878 if (nToys > 0) {
3879 sWhat += TString::Format("=%g", nToys);
3880 }
3881 }
3882 hs.SetTitle(sWhat + " scan" + ((dsetName != "") ? TString::Format(" [data=%s]", dsetName.Data()) : ""));
3883 int scanStatus = hs.scan(sWhat + " visualize", nBinsX, lowX, highX);
3884 if (scanStatus != 0) {
3885 new TGMsgBox(
3886 gClient->GetRoot(),
3887 (gROOT->GetListOfBrowsers()->At(0))
3888 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
3889 : gClient->GetRoot(),
3890 "Scan Finished with Bad Status Code",
3891 TString::Format("%s\nData = %s\nScan Status Code = %d", hs.GetName(), dsetName.Data(), scanStatus),
3893 }
3894 if (ws()) {
3895 if (auto res = hs.result())
3896 ws()->import(*res);
3897 }
3898
3899 _pars.argList() = *snap; // restore pars
3900
3901 } catch (const std::exception &e) {
3902 new TGMsgBox(
3903 gClient->GetRoot(),
3904 (gROOT->GetListOfBrowsers()->At(0))
3905 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
3906 : gClient->GetRoot(),
3907 "Exception", e.what(), kMBIconExclamation);
3908 }
3909}
3910
3911void xRooNode::_SetBinContent_(int bin, double value, const char *par, double parVal)
3912{
3913 try {
3914 SetBinContent(bin, value, strlen(par) > 0 ? par : nullptr, parVal);
3915 } catch (const std::exception &e) {
3916 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
3917 kMBIconExclamation); // deletes self on dismiss?
3918 }
3919}
3920
3922{
3923 try {
3924#if ROOT_VERSION_CODE > ROOT_VERSION(6, 29, 00)
3925 // if this is a collection of values, populate a TF1 and display as a dialog
3926 if (!get() && TString(GetName()).BeginsWith("!")) {
3927 browse();
3928 RooArgList args;
3929 for (auto a : *this) {
3930 if (auto arg = a->get<RooRealVar>())
3931 args.add(*arg);
3932 }
3933 TF1 f(GetName(), 0.0, 1.0, std::min(int(args.size()), 10));
3934 int i = 0;
3935 int j = 0;
3936 for (auto c : args) {
3937 j++;
3938 if (j < value) {
3939 continue;
3940 }
3941 auto v = dynamic_cast<RooRealVar *>(c);
3942 f.SetParName(i, c->GetName());
3943 if (v) {
3944 f.SetParLimits(i, v->getMin(), v->getMax());
3945 if (v->isConstant())
3946 f.FixParameter(i, v->getVal());
3947 else {
3948 f.SetParameter(i, v->getVal());
3949 f.SetParError(i, v->getError());
3950 }
3951 }
3952 i++;
3953 if (i == 10) {
3954 break; // max 10 pars shown
3955 }
3956 }
3957 int ret = 0;
3959 gClient->GetDefaultRoot(),
3960 (gROOT->GetListOfBrowsers()->At(0))
3961 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
3962 : gClient->GetDefaultRoot(),
3963 &f, nullptr, &ret);
3964 if (ret) {
3965 // user has changed parameter values etc, propagate back to parameters
3966 for (i = 0; i < f.GetNpar(); i++) {
3967 auto c = args.find(f.GetParName(i));
3968 auto v = dynamic_cast<RooRealVar *>(c);
3969 if (v) {
3970 v->setVal(f.GetParameter(i));
3971 double low, high;
3972 f.GetParLimits(i, low, high);
3973 if (low == high) {
3974 v->setConstant(low); // if low==high==0 then is not marked constant
3975 } else {
3976 v->setRange(low, high);
3977 }
3978 }
3979 }
3980 }
3981 return;
3982 }
3983#endif
3984
3985 if (!SetContent(value))
3986 throw std::runtime_error("Failed to SetContent");
3987 } catch (const std::exception &e) {
3988 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
3989 kMBIconExclamation); // deletes self on dismiss?
3990 }
3991}
3992
3993bool xRooNode::SetBinContent(int bin, double value, const char *par, double parVal)
3994{
3995
3996 // create if needed
3997 if (!get()) {
3998 if (fParent && !find(GetName())) {
3999 // if have a binning we create a histogram to match it
4000 if (auto ax = GetXaxis(); ax) {
4001 std::shared_ptr<TH1D> h;
4002 auto _b = dynamic_cast<Axis2 *>(ax)->binning();
4003 auto t = TH1::AddDirectoryStatus();
4004 TH1::AddDirectory(false);
4005 if (_b->isUniform()) {
4006 h.reset(new TH1D(GetName(), GetTitle(), _b->numBins(), _b->lowBound(), _b->highBound()));
4007 } else {
4008 h.reset(new TH1D(GetName(), GetTitle(), _b->numBins(), _b->array()));
4009 }
4010 h->SetOption("nostyle"); // don't transfer style when added
4011 h->SetDirectory(nullptr);
4013 h->GetXaxis()->SetName(TString::Format("%s;%s", ax->GetParent()->GetName(), ax->GetName()));
4014 fComp = h;
4015 }
4016 fComp = fParent->Add(*this, "sample").fComp;
4017 }
4018 }
4019
4020 // if it's a RooProduct locate child with the same name
4021 if (get<RooProduct>()) {
4022 return factors()[GetName()]->SetBinContent(bin, value, par, parVal);
4023 }
4024
4025 if (get<RooAbsData>()) {
4026 if (auto _data = get<RooDataSet>(); _data) {
4027 auto _ax = (bin) ? GetXaxis() : nullptr;
4028 if (!_ax && bin) {
4029 throw std::runtime_error("Cannot determine binning to fill data");
4030 }
4031 if (_ax && _ax->GetNbins() < bin) {
4032 throw std::out_of_range(TString::Format("%s range %s only has %d bins", _ax->GetParent()->GetName(),
4033 _ax->GetName(), _ax->GetNbins()));
4034 }
4035 RooArgSet obs;
4036
4037 TString cut = "";
4038
4039 for (auto _c : coords()) { // coords() moves vars to their respective coordinates too
4040 if (auto _cat = _c->get<RooAbsCategoryLValue>(); _cat) {
4041 if (cut != "")
4042 cut += " && ";
4043 cut += TString::Format("%s==%d", _cat->GetName(), _cat->getCurrentIndex());
4044 obs.add(*_cat); // note: if we ever changed coords to return clones, would need to keep coords alive
4045 } else if (auto _rv = _c->get<RooAbsRealLValue>(); _rv) {
4046 // todo: check coordRange is a single range rather than multirange
4047 if (cut != "")
4048 cut += " && ";
4049 cut +=
4050 TString::Format("%s>=%f&&%s<%f", _rv->GetName(), _rv->getMin(_rv->getStringAttribute("coordRange")),
4051 _rv->GetName(), _rv->getMax(_rv->getStringAttribute("coordRange")));
4052 obs.add(*_rv); // note: if we ever changed coords to return clones, would need to keep coords alive
4053 } else {
4054 throw std::runtime_error("SetBinContent of data: Unsupported coordinate type");
4055 }
4056 }
4057
4058 RooFormulaVar cutFormula("cut1", cut, obs); // doing this to avoid complaints about unused vars
4059 RooFormulaVar icutFormula("icut1", TString::Format("!(%s)", cut.Data()), obs);
4060
4061 TString cut2;
4062 if (_ax) {
4063 cut2 = TString::Format("%s >= %f && %s < %f", _ax->GetParent()->GetName(), _ax->GetBinLowEdge(bin),
4064 _ax->GetParent()->GetName(), _ax->GetBinUpEdge(bin));
4065 obs.add(*dynamic_cast<RooAbsArg *>(_ax->GetParent()));
4066 } else {
4067 cut2 = "1==1";
4068 }
4069 RooFormulaVar cutFormula2("cut2", cut + " && " + cut2, obs);
4070 RooFormulaVar icutFormula2("icut2", TString::Format("!(%s && %s)", cut.Data(), cut2.Data()), obs);
4071
4072 // // go up through parents looking for slice obs
4073 // auto _p = fParent;
4074 // while(_p) {
4075 // TString pName(_p->GetName());
4076 // if (auto pos = pName.Index('='); pos != -1) {
4077 // if(auto _obs = _p->getObject<RooAbsLValue>(pName(0,pos)); _obs) {
4078 // if(auto _cat = dynamic_cast<RooAbsCategoryLValue*>(_obs.get()); _cat) {
4079 // _cat->setLabel(pName(pos+1,pName.Length()));
4080 // cut += TString::Format("%s%s==%d", (cut=="")?"":" && ",_cat->GetName(),
4081 // _cat->getCurrentIndex());
4082 // } else if(auto _var = dynamic_cast<RooAbsRealLValue*>(_obs.get()); _var) {
4083 // _var->setVal(TString(pName(pos+1,pName.Length())).Atof());
4084 // // TODO: Cut for this!!
4085 // }
4086 // obs.add(*dynamic_cast<RooAbsArg*>(_obs.get()));
4087 // } else {
4088 // throw std::runtime_error("Unknown observable, could not find");
4089 // }
4090 // }
4091 // _p = _p->fParent;
4092 // }
4093
4094 // add observables to dataset if necessary
4095 RooArgSet l(obs);
4096 l.remove(*_data->get(), true, true);
4097 if (!l.empty()) {
4098 // addColumns method is buggy: https://github.com/root-project/root/issues/8787
4099 // incredibly though, addColumn works??
4100 for (auto &x : l) {
4101 _data->addColumn(*x);
4102 }
4103 // instead create a copy dataset and merge it into current
4104 // cant use merge because it drops weightVar
4105 /*RooDataSet tmp("tmp","tmp",l);
4106 for(int i=0;i<_data->numEntries();i++) tmp.add(l);
4107 _data->merge(&tmp);*/
4108 // delete _data->addColumns(l);
4109 }
4110 // before adding, ensure range is good to cover
4111 for (auto &o : obs) {
4112 if (auto v = dynamic_cast<RooRealVar *>(o); v) {
4113 if (auto dv = dynamic_cast<RooRealVar *>(_data->get()->find(v->GetName())); dv) {
4114 if (v->getMin() < dv->getMin())
4115 dv->setMin(v->getMin());
4116 if (v->getMax() > dv->getMax())
4117 dv->setMax(v->getMax());
4118 }
4119 } else if (auto c = dynamic_cast<RooCategory *>(o); c) {
4120 if (auto dc = dynamic_cast<RooCategory *>(_data->get()->find(c->GetName())); dc) {
4121 if (!dc->hasLabel(c->getCurrentLabel())) {
4122 dc->defineType(c->getCurrentLabel(), c->getCurrentIndex());
4123 }
4124 }
4125 }
4126 }
4127
4128 // using SetBinContent means dataset must take on a binned form at these coordinates
4129 // if number of entries doesnt match number of bins then will 'bin' the data
4130 if (bin) {
4131 if (auto _nentries = std::unique_ptr<RooAbsData>(_data->reduce(cutFormula))->numEntries();
4132 _nentries != _ax->GetNbins()) {
4133 auto _contents = GetBinContents(1, _ax->GetNbins());
4134
4135 if (_nentries > 0) {
4136 Info("SetBinContent", "Binning %s in channel: %s", GetName(), cut.Data());
4137 auto _reduced = std::unique_ptr<RooAbsData>(_data->reduce(icutFormula));
4138 _data->reset();
4139 for (int j = 0; j < _reduced->numEntries(); j++) {
4140 auto _obs = _reduced->get(j);
4141 _data->add(*_obs, _reduced->weight());
4142 }
4143 }
4144 for (int i = 1; i <= _ax->GetNbins(); i++) {
4145 // can skip over the bin we will be setting to save a reduce step below
4146 if (i == bin)
4147 continue;
4148 dynamic_cast<RooAbsLValue *>(_ax->GetParent())->setBin(i - 1, _ax->GetName());
4149 _data->add(obs, _contents.at(i - 1));
4150 }
4151 }
4152 }
4153 // remove existing entries
4154 if (std::unique_ptr<RooAbsData>(_data->reduce(cutFormula2))->numEntries() > 0) {
4155 auto _reduced = std::unique_ptr<RooAbsData>(_data->reduce(icutFormula2));
4156 _data->reset();
4157 for (int j = 0; j < _reduced->numEntries(); j++) {
4158 auto _obs = _reduced->get(j);
4159 _data->add(*_obs, _reduced->weight());
4160 }
4161 }
4162 if (_ax)
4163 dynamic_cast<RooAbsLValue *>(_ax->GetParent())->setBin(bin - 1, _ax->GetName());
4164 _data->add(obs, value);
4165 if (auto bb = getBrowsable(".sourceds"))
4166 return bb->SetBinContent(bin, value, par, parVal); // apply to source ds if we have one
4167 return true;
4168
4169 } else if (get<RooDataHist>()) {
4170 throw std::runtime_error("RooDataHist not supported yet");
4171 }
4172 }
4173
4174 if (auto _varies = variations(); !_varies.empty() || (par && strlen(par))) {
4175 if (!par || strlen(par) == 0) {
4176 return _varies["nominal"]->SetBinContent(bin, value, par, parVal);
4177 } else if (auto it = _varies.find(Form("%s=%g", par, parVal)); it) {
4178 return it->SetBinContent(bin, value);
4179 } else {
4180 // need to create the variation : note - if no variations existed up to now this will inject a new node
4181 // so we should redirect ourself to the new node
4182 // TODO: Do we need to redirect parents?
4183 TString s = Form("%s=%g", par, parVal);
4184 return Vary(s.Data()).SetBinContent(bin, value);
4185 }
4186 }
4187
4188 auto o = get();
4189 if (auto p = dynamic_cast<RooRealVar *>(o); p) {
4190 if (!par || strlen(par) == 0) {
4191 if (p->getMax() < value)
4192 p->setMax(value);
4193 if (p->getMin() > value)
4194 p->setMin(value);
4195 p->setVal(value);
4196 sterilize();
4197 return true;
4198 }
4199
4200 } else if (auto c = dynamic_cast<RooConstVar *>(o); c) {
4201
4202 // if parent is a FlexibleInterpVar, change the value in that .
4203 if (strcmp(c->GetName(), Form("%g", c->getVal())) == 0) {
4204 c->SetNameTitle(Form("%g", value), Form("%g", value));
4205 }
4206#if ROOT_VERSION_CODE < ROOT_VERSION(6, 24, 00)
4207 c->_value = value; // in future ROOT versions there is a changeVal method!
4208#else
4209 c->changeVal(value);
4210#endif
4211
4213 fParent->Vary(*this);
4214 }
4215
4216 sterilize();
4217 return true;
4218 } else if (auto f = dynamic_cast<RooHistFunc *>(o); f) {
4219 auto bin_pars = f->dataHist().get(bin - 1);
4220 if (f->getAttribute("density")) {
4221 value /= f->dataHist().binVolume(*bin_pars);
4222 }
4223 f->dataHist().set(*bin_pars, value);
4224 f->setValueDirty();
4225
4226 if (auto otherfName = f->getStringAttribute("symmetrized_by"); otherfName) {
4227 // broken symmetry, so update flags ...
4228 f->setStringAttribute("symmetrized_by", nullptr);
4229 if (auto x = getObject<RooAbsArg>(otherfName); x) {
4230 x->setStringAttribute("symmetrizes", nullptr);
4231 x->setStringAttribute("symmetrize_nominal", nullptr);
4232 }
4233 } else if (auto otherfName2 = f->getStringAttribute("symmetrizes"); otherfName2) {
4234 auto nomf = getObject<RooHistFunc>(f->getStringAttribute("symmetrize_nominal"));
4236 if (nomf && otherf) {
4237 otherf->dataHist().set(*bin_pars, 2 * nomf->dataHist().weight(bin - 1) - value);
4238 otherf->setValueDirty();
4239 }
4240 }
4241 sterilize();
4242 return true;
4243 } else if (auto f2 = dynamic_cast<RooStats::HistFactory::FlexibleInterpVar *>(o); f2) {
4244 // changing nominal value
4245 f2->setNominal(value);
4246 }
4247 throw std::runtime_error(TString::Format("unable to set bin content of %s", GetPath().c_str()));
4248}
4249
4250bool xRooNode::SetBinData(int bin, double value, const xRooNode &data)
4251{
4252 if (data.get<RooAbsData>()) {
4253 // attach as a child before calling datasets(), so that is included in the list
4254 push_back(std::make_shared<xRooNode>(data));
4255 }
4256 auto node = datasets()[data.GetName()];
4257 if (data.get<RooAbsData>()) {
4258 // remove the child we attached
4259 resize(size() - 1);
4260 }
4261 return node->SetBinContent(bin, value);
4262}
4263
4264bool xRooNode::SetData(const TObject &obj, const xRooNode &data)
4265{
4266 if (data.get<RooAbsData>()) {
4267 // attach as a child before calling datasets(), so that is included in the list
4268 push_back(std::make_shared<xRooNode>(data));
4269 }
4270 auto node = datasets()[data.GetName()];
4271 if (data.get<RooAbsData>()) {
4272 // remove the child we attached
4273 resize(size() - 1);
4274 }
4275 return node->SetContents(obj);
4276}
4277
4278bool xRooNode::SetBinError(int bin, double value)
4279{
4280
4281 // if it's a RooProduct locate child with the same name
4282 if (get<RooProduct>()) {
4283 return factors()[GetName()]->SetBinError(bin, value);
4284 }
4285
4286 if (auto _varies = variations(); !_varies.empty()) {
4287 return _varies["nominal"]->SetBinError(bin, value);
4288 }
4289
4290 auto o = get();
4291
4292 if (auto f = dynamic_cast<RooHistFunc *>(o); f) {
4293
4294 // if (f->getAttribute("density")) { value /= f->dataHist().binVolume(*bin_pars); } - commented out because DON'T
4295 // convert .. sumw and sumw2 attributes will be stored not as densities
4296
4297 // NOTE: Can only do this because factors() makes parents of its children it's own parent (it isn't the parent)
4298 // If ever make factors etc part of the parentage then this would need tweaking to get to the true parent
4299 // find first parent that is a RooProduct, that is where the statFactor would live
4300 // stop as soon as we reach pdf object
4301 auto _prodParent = fParent;
4302 while (_prodParent && !_prodParent->get<RooProduct>() && !_prodParent->get<RooAbsPdf>()) {
4303 if (_prodParent->get<PiecewiseInterpolation>() && strcmp(GetName(), "nominal")) {
4304 _prodParent.reset();
4305 break; // only the 'nominal' variation can look for a statFactor outside the variation container
4306 }
4307 _prodParent = _prodParent->fParent;
4308 }
4309 auto _f_stat =
4310 (_prodParent && !_prodParent->get<RooAbsPdf>()) ? _prodParent->factors().find("statFactor") : nullptr;
4311 auto f_stat = (_f_stat) ? _f_stat->get<ParamHistFunc>() : nullptr;
4312 if (_f_stat && _f_stat->get() && !f_stat) {
4313 throw std::runtime_error("stat factor must be a paramhistfunc");
4314 }
4315
4316 // stat uncertainty lives in the "statFactor" factor, each sample has its own one,
4317 // but they can share parameters
4318 if (!f_stat) {
4319 if (value == 0)
4320 return true;
4322 for (auto &p : xRooNode("tmp", *f, std::shared_ptr<xRooNode>(nullptr)).vars()) {
4323 if (parNames != "")
4324 parNames += ",";
4325 parNames += p->get()->GetName();
4326 }
4327 auto h = std::unique_ptr<TH1>(f->dataHist().createHistogram(parNames
4328#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 27, 00)
4329 ,
4331#endif
4332 ));
4333 h->Reset();
4334 h->SetName("statFactor");
4335 h->SetTitle(TString::Format("StatFactor of %s", f->GetTitle()));
4336 h->SetOption("blankshape");
4337
4338 // multiply parent if is nominal
4339 auto toMultiply = this;
4340 if (strcmp(GetName(), "nominal") == 0 && fParent && fParent->get<PiecewiseInterpolation>())
4341 toMultiply = fParent.get();
4342
4343 f_stat = dynamic_cast<ParamHistFunc *>(toMultiply->Multiply(*h).get());
4344 if (!f_stat) {
4345 throw std::runtime_error("Failed creating stat shapeFactor");
4346 }
4347 }
4348
4349 auto phf = f_stat;
4350
4351 TString prefix = f->getStringAttribute("statPrefix");
4352 if (value && prefix == "") {
4353 // find the first parent that can hold components (RooAddPdf, RooRealSumPdf, RooAddition, RooWorkspace) ... use
4354 // that name for the stat factor
4355 auto _p = fParent;
4356 while (_p && !(_p->get()->InheritsFrom("RooRealSumPdf") || _p->get()->InheritsFrom("RooAddPdf") ||
4357 _p->get()->InheritsFrom("RooWorkspace") || _p->get()->InheritsFrom("RooAddition"))) {
4358 _p = _p->fParent;
4359 }
4360 prefix = TString::Format("stat_%s", (_p && _p->get<RooAbsReal>()) ? _p->get()->GetName() : f->GetName());
4361 }
4362 auto newVar = (value == 0) ? getObject<RooRealVar>("1")
4363 : acquire<RooRealVar>(Form("%s_bin%d", prefix.Data(), bin),
4364 Form("#gamma^{%s}_{%d}", prefix.Data(), bin), 1);
4365#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
4366 RooArgList &pSet = phf->_paramSet;
4367#else
4368 RooArgList &pSet = const_cast<RooArgList &>(phf->paramList());
4369#endif
4370 auto var = dynamic_cast<RooRealVar *>(&pSet[bin - 1]);
4371
4372 if (newVar.get() != var) {
4373 // need to swap out var for newVar
4374 // replace ith element in list with new func, or inject into RooProduct
4376 for (std::size_t i = 0; i < pSet.size(); i++) {
4377 if (int(i) != bin - 1) {
4378 all.add(*pSet.at(i));
4379 } else {
4380 all.add(*newVar);
4381 }
4382 }
4383 pSet.removeAll();
4384 pSet.add(all);
4385 }
4386
4387 xRooNode v((value == 0) ? *var : *newVar, *this);
4388 auto rrv = dynamic_cast<RooRealVar *>(v.get());
4389 if (strcmp(rrv->GetName(), "1") != 0) {
4390 TString origName = (f->getStringAttribute("origName")) ? f->getStringAttribute("origName") : GetName();
4391 rrv->setStringAttribute(Form("sumw2_%s", origName.Data()), TString::Format("%f", pow(value, 2)));
4392 auto bin_pars = f->dataHist().get(bin - 1);
4393 auto _binContent = f->dataHist().weight();
4394 if (f->getAttribute("density")) {
4395 _binContent *= f->dataHist().binVolume(*bin_pars);
4396 }
4397 rrv->setStringAttribute(Form("sumw_%s", origName.Data()), TString::Format("%f", _binContent));
4398 double sumw2 = 0;
4399 double sumw = 0;
4400 for (auto &[s, sv] : rrv->stringAttributes()) {
4401 if (s.find("sumw_") == 0) {
4402 sumw += TString(sv).Atof();
4403 } else if (s.find("sumw2_") == 0) {
4404 sumw2 += TString(sv).Atof();
4405 }
4406 }
4407 if (sumw2 && sumw2 != std::numeric_limits<double>::infinity()) {
4408 double tau = pow(sumw, 2) / sumw2;
4409 rrv->setError((tau < 1e-15) ? 1e15 : (/*rrv->getVal()*/ 1. / sqrt(tau))); // not sure why was rrv->getVal()?
4410 rrv->setConstant(false);
4411 // parameter must be constrained
4412 auto _constr = v.constraints();
4413 // std::cout << " setting constraint " << v.GetName() << " nomin=" << tau << std::endl;
4414 if (_constr.empty()) {
4415 rrv->setStringAttribute("boundConstraint", _constr.Add("poisson").get()->GetName());
4416 } else {
4417 auto _glob = _constr.at(0)->obs().at(0)->get<RooRealVar>();
4418 // TODO: Update any globs snapshots that are designed to match the nominal
4419 _glob->setStringAttribute("nominal", TString::Format("%f", tau));
4420 double _min = tau * (1. - 5. * sqrt(1. / tau));
4421 double _max = tau * (1. + 5. * sqrt(1. / tau));
4422 _glob->setRange(_min, _max);
4423 _glob->setVal(tau);
4424 _constr.at(0)->pp().at(0)->SetBinContent(0, tau);
4425 rrv->setStringAttribute("boundConstraint", _constr.at(0)->get()->GetName());
4426 }
4427 rrv->setRange(std::max((1. - 5. * sqrt(1. / tau)), 1e-15), 1. + 5. * sqrt(1. / tau));
4428 } else {
4429 // remove constraint
4430 if (auto _constr = v.constraints(); !_constr.empty()) {
4431 v.constraints().Remove(*_constr.at(0));
4432 }
4433 // set const if sumw2 is 0 (i.e. no error)
4434 rrv->setVal(1);
4435 rrv->setError(0);
4436 rrv->setConstant(sumw2 == 0);
4437 }
4438 }
4439
4440 return true;
4441 }
4442
4443 throw std::runtime_error(TString::Format("%s SetBinError failed", GetName()));
4444}
4445
4446std::shared_ptr<xRooNode> xRooNode::at(const std::string &name, bool browseResult) const
4447{
4448 auto res = find(name, browseResult);
4449 if (res == nullptr)
4450 throw std::out_of_range(name + " does not exist");
4451 return res;
4452}
4453
4454////////////////////////////////////////////////////////////////////////////////
4455/// The RooWorkspace this node belong to, if any
4456
4458{
4459 if (auto _w = get<RooWorkspace>(); _w)
4460 return _w;
4461 if (auto a = get<RooAbsArg>(); a && GETWS(a)) {
4462 return GETWS(a);
4463 }
4464 if (fParent)
4465 return fParent->ws();
4466 return nullptr;
4467}
4468
4470{
4471
4472 xRooNode out(".constraints", nullptr, *this);
4473
4474 std::function<RooAbsPdf *(const xRooNode &n, RooAbsArg &par, std::set<RooAbsPdf *> ignore)> getConstraint;
4475 getConstraint = [&](const xRooNode &n, RooAbsArg &par, std::set<RooAbsPdf *> ignore) {
4476 if (auto _pdf = n.get<RooAbsPdf>()) {
4477 if (ignore.count(_pdf))
4478 return (RooAbsPdf *)nullptr;
4479 ignore.insert(_pdf);
4480 }
4481 auto o = n.get<RooProdPdf>();
4482 if (!o) {
4483 if (n.get<RooSimultaneous>()) {
4484 // check all channels for a constraint if is simultaneous
4485 for (auto &c : n.bins()) {
4486 if (auto oo = getConstraint(*c, par, ignore); oo) {
4487 return oo;
4488 }
4489 }
4490 return (RooAbsPdf *)nullptr;
4491 } else if (n.get<RooAbsPdf>() && n.fParent && n.fParent->get<RooWorkspace>()) {
4492 // reached top-level pdf, which wasn't a simultaneous, so stop here
4493 return (RooAbsPdf *)nullptr;
4494 } else if (auto _ws = n.get<RooWorkspace>(); _ws) {
4495 // reached a workspace, check for any pdf depending on parameter that isnt the ignore
4496 for (auto p : _ws->allPdfs()) {
4497 if (ignore.count(static_cast<RooAbsPdf *>(p)))
4498 continue;
4499 if (p->dependsOn(par)) {
4500 out.emplace_back(std::make_shared<xRooNode>(par.GetName(), *p, *this));
4501 }
4502 }
4503 }
4504 if (!n.fParent)
4505 return (RooAbsPdf *)nullptr;
4506 return getConstraint(*n.fParent, par, ignore);
4507 }
4508 for (auto p : o->pdfList()) {
4509 if (ignore.count(static_cast<RooAbsPdf *>(p)))
4510 continue;
4511 if (p->dependsOn(par)) {
4512 out.emplace_back(std::make_shared<xRooNode>(par.GetName(), *p, *this));
4513 }
4514 }
4515 return (RooAbsPdf *)nullptr;
4516 };
4517
4518 for (auto &p : vars()) {
4519 auto v = dynamic_cast<RooAbsReal *>(p->get());
4520 if (!v)
4521 continue;
4522 if (v->getAttribute("Constant") && v != get<RooAbsReal>())
4523 continue; // skip constants unless we are getting the constraints of a parameter itself
4524 if (v->getAttribute("obs"))
4525 continue; // skip observables ... constraints constrain pars not obs
4526 getConstraint(*this, *v, {get<RooAbsPdf>()});
4527 /*if (auto c = ; c) {
4528 out.emplace_back(std::make_shared<Node2>(p->GetName(), *c, *this));
4529 }*/
4530 }
4531
4532 // finish by removing any constraint that contains another constraint for the same par
4533 // and consolidate common pars
4534 auto it = out.std::vector<std::shared_ptr<xRooNode>>::begin();
4535 while (it != out.std::vector<std::shared_ptr<xRooNode>>::end()) {
4536 bool removeIt = false;
4537 for (auto &c : out) {
4538 if (c.get() == it->get())
4539 continue;
4540 if ((*it)->get<RooAbsArg>()->dependsOn(*c->get<RooAbsArg>())) {
4541 removeIt = true;
4542 std::set<std::string> parNames;
4543 std::string _cName = c->GetName();
4544 do {
4545 parNames.insert(_cName.substr(0, _cName.find(';')));
4546 _cName = _cName.substr(_cName.find(';') + 1);
4547 } while (_cName.find(';') != std::string::npos);
4548 parNames.insert(_cName);
4549 _cName = it->get()->GetName();
4550 do {
4551 parNames.insert(_cName.substr(0, _cName.find(';')));
4552 _cName = _cName.substr(_cName.find(';') + 1);
4553 } while (_cName.find(';') != std::string::npos);
4554 parNames.insert(_cName);
4555 _cName = "";
4556 for (auto &x : parNames) {
4557 if (!_cName.empty())
4558 _cName += ";";
4559 _cName += x;
4560 }
4561 c->TNamed::SetName(_cName.c_str());
4562 break;
4563 }
4564 }
4565 if (removeIt) {
4566 it = out.erase(it);
4567 } else {
4568 ++it;
4569 }
4570 }
4571
4572 // if getting constraints of a fundamental then use the constraint names instead of the par name (because would be
4573 // all same otherwise)
4574 if (get<RooAbsArg>() && get<RooAbsArg>()->isFundamental()) {
4575 for (auto &o : out) {
4576 o->TNamed::SetName(o->get()->GetName());
4577 }
4578 }
4579
4580 return out;
4581}
4582
4583std::shared_ptr<TObject> xRooNode::convertForAcquisition(xRooNode &acquirer, const char *opt) const
4584{
4585
4586 TString sOpt(opt);
4587 sOpt.ToLower();
4589 if (sOpt == "func")
4590 sName = TString("factory:") + sName;
4591
4592 // if arg is a histogram, will acquire it as a RooHistFunc unless no conversion
4593 // todo: could flag not to convert
4594 if (auto h = get<TH1>(); h) {
4595 TString sOpt2(h->GetOption());
4596 std::map<std::string, std::string> stringAttrs;
4597 while (sOpt2.Contains("=")) {
4598 auto pos = sOpt2.Index("=");
4599 auto start = sOpt2.Index(";") + 1;
4600 if (start > pos)
4601 start = 0;
4602 auto end = sOpt2.Index(";", pos);
4603 if (end == -1)
4604 end = sOpt2.Length();
4605 stringAttrs[sOpt2(start, pos - start)] = sOpt2(pos + 1, end - pos - 1);
4606 sOpt2 = TString(sOpt2(0, start)) + TString(sOpt2(end + 1, sOpt2.Length()));
4607 }
4610 if (origName.BeginsWith(';'))
4611 origName = origName(1, origName.Length());
4612 if (newObjName.BeginsWith(';')) {
4613 newObjName =
4614 newObjName(1, newObjName.Length()); // special case if starts with ';' then don't create a fancy name
4615 } else if (acquirer.get() && !acquirer.get<RooWorkspace>()) {
4617 "%s_%s", (acquirer.mainChild().get()) ? acquirer.mainChild()->GetName() : acquirer->GetName(),
4618 newObjName.Data());
4619 }
4620 // can convert to a RooHistFunc, or RooParamHist if option contains 'shape'
4621 TString varName = h->GetXaxis()->GetName();
4622 std::string binningName = newObjName.Data();
4623 if (auto pos = varName.Index(';'); pos != -1) {
4624 binningName = varName(pos + 1, varName.Length());
4625 varName = varName(0, pos);
4626 }
4627
4628 if (varName == "xaxis" &&
4629 !acquirer.get<RooSimultaneous>()) { // default case, try to take axis var and binning from the acquirer
4630 if (auto ax = acquirer.GetXaxis(); ax) {
4631 varName = ax->GetParent()->GetName();
4632 // TODO: check the binning is consistent before using - at least will check nBins below
4633 binningName = ax->GetName();
4634 } else if (acquirer.obs().size() == 1)
4635 varName = acquirer.obs().at(0)->get()->GetName(); // TODO what if no obs but Xaxis var is defined?
4636 }
4637 auto x = acquirer.acquire<RooRealVar>(varName, h->GetXaxis()->GetTitle(), h->GetXaxis()->GetXmin(),
4638 h->GetXaxis()->GetXmax());
4639 if (x->getMin() > h->GetXaxis()->GetXmin())
4640 x->setMin(h->GetXaxis()->GetXmin());
4641 if (x->getMax() < h->GetXaxis()->GetXmax())
4642 x->setMax(h->GetXaxis()->GetXmax());
4643 if (!x->hasBinning(binningName.c_str())) {
4644 if (h->GetXaxis()->IsVariableBinSize()) {
4645 x->setBinning(RooBinning(h->GetNbinsX(), h->GetXaxis()->GetXbins()->GetArray()), binningName.c_str());
4646 } else {
4647 x->setBinning(
4648 RooUniformBinning(h->GetXaxis()->GetXmin(), h->GetXaxis()->GetXmax(), h->GetXaxis()->GetNbins()),
4649 binningName.c_str());
4650 }
4651 x->getBinning(binningName.c_str()).SetTitle(h->GetXaxis()->GetTitle());
4652 if (x->getBinningNames().size() == 2) {
4653 // this was the first binning, so copy it over to be the default binning too
4654 x->setBinning(x->getBinning(binningName.c_str()));
4655 }
4656 } else {
4657 // TODO check binning is compatible with histogram
4658 if (x->getBinning(binningName.c_str()).numBins() != h->GetNbinsX()) {
4659 throw std::runtime_error(
4660 TString::Format("binning mismatch for binning %s of %s", binningName.c_str(), x->GetName()));
4661 }
4662 }
4663
4664 std::shared_ptr<RooAbsArg> _f;
4665
4666 // if acquirer is a RooSimultaneous, will use histogram to define a channel
4667 if (acquirer.get<RooSimultaneous>()) {
4668 _f = acquirer.acquireNew<RooProdPdf>(newObjName, (strlen(h->GetTitle())) ? h->GetTitle() : h->GetName(),
4669 RooArgList());
4670 for (auto &[k, v] : stringAttrs) {
4671 _f->setStringAttribute(k.c_str(), v.c_str());
4672 }
4673 x->setAttribute("obs", true);
4674 } else if (sOpt2.Contains("shape")) {
4675 RooArgList list;
4676 for (int i = 0; i < x->getBinning(binningName.c_str()).numBins(); i++) {
4677 std::shared_ptr<RooAbsArg> arg;
4678 if (sOpt2.Contains("blankshape")) {
4679 arg = acquirer.acquire2<RooAbsArg, RooRealVar>("1", "1", 1);
4680 } else {
4681 if (!h) {
4682 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1), "", 1);
4683 } else if (h->GetMinimumStored() != -1111 || h->GetMaximumStored() != -1111) {
4684 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1), TString::Format("%s_{%d}",h->GetTitle(),i+1),
4685 h->GetBinContent(i + 1), h->GetMinimumStored(),
4686 h->GetMaximumStored());
4687 } else {
4688 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1), TString::Format("%s_{%d}",h->GetTitle(),i+1),
4689 h->GetBinContent(i + 1));
4690 }
4691 }
4692 list.add(*arg);
4693 }
4694 // paramhistfunc requires the binnings to be loaded as default at construction time
4695 // so load binning temporarily
4696 auto tmp = dynamic_cast<RooAbsBinning *>(x->getBinningPtr(nullptr)->Clone());
4697 x->setBinning(x->getBinning(binningName.c_str()));
4698 _f = acquirer.acquireNew<ParamHistFunc>(newObjName, h->GetTitle(), *x, list);
4699#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
4700 dynamic_cast<ParamHistFunc *>(_f.get())->_paramSet.setName("paramSet"); // so can see when print
4701#else
4702 const_cast<RooArgList &>(dynamic_cast<ParamHistFunc *>(_f.get())->paramList())
4703 .setName("paramSet"); // so can see when print
4704#endif
4705 x->setBinning(*tmp); // restore binning
4706 delete tmp;
4707 for (auto &[k, v] : stringAttrs) {
4708 _f->setStringAttribute(k.c_str(), v.c_str());
4709 }
4710 } else {
4711 auto dh = acquirer.acquireNew<RooDataHist>(Form("hist_%s", newObjName.Data()), h->GetTitle(), *x,
4712 binningName.c_str() /* binning name*/);
4713 if (!dh) {
4714 throw std::runtime_error("Couldn't make data hist");
4715 }
4716 auto f = acquirer.acquireNew<RooHistFunc>(newObjName, h->GetTitle(), *x, *dh,
4717 0 /*interpolation order between bins*/);
4718 f->forceNumInt();
4719 f->setAttribute("autodensity"); // where it gets inserted will determine if this is a density or not
4720 _f = f;
4721
4722 for (auto &[k, v] : stringAttrs) {
4723 _f->setStringAttribute(k.c_str(), v.c_str());
4724 }
4725
4726 // need to do these settings here because used in the assignment step
4727 _f->setStringAttribute("xvar", x->GetName());
4728 _f->setStringAttribute("binning", binningName.c_str());
4729 if (strcmp(_f->GetName(), origName.Data()) && !_f->getStringAttribute("alias"))
4730 _f->setStringAttribute("alias", origName);
4731
4732 // copy values over using the assignment operator - may convert to a RooProduct if there are stat uncerts
4733 xRooNode tmp(h->GetName(), _f, acquirer);
4734 tmp = *h;
4735 _f = std::dynamic_pointer_cast<RooAbsArg>(tmp.fComp); // in case got upgrade to a RooProduct
4736 }
4737
4738 _f->setStringAttribute("xvar", x->GetName());
4739 _f->setStringAttribute("binning", binningName.c_str());
4740 // style(h); // will transfer styling to object if necessary - not doing because this method used with plane hists
4741 // frequently
4742 if (strcmp(_f->GetName(), origName.Data()) && !_f->getStringAttribute("alias"))
4743 _f->setStringAttribute("alias", origName);
4744
4745 fComp = _f;
4746 return _f;
4747 } else if (!get() && sName.BeginsWith("factory:") && acquirer.ws()) {
4748 TString s(sName);
4749 s = TString(s(8, s.Length()));
4750 fComp.reset(acquirer.ws()->factory(s), [](TObject *) {});
4751 if (fComp) {
4752 const_cast<xRooNode *>(this)->TNamed::SetName(fComp->GetName());
4753 }
4754 return fComp;
4755 }
4756
4757 return fComp;
4758}
4759
4760std::shared_ptr<TStyle> xRooNode::style(TObject *initObject, bool autoCreate) const
4761{
4762 return std::dynamic_pointer_cast<TStyle>(styles(initObject, autoCreate).fComp);
4763}
4764
4766{
4767 TString t = GetTitle();
4768
4769 auto arg = get<RooAbsArg>();
4770 if (!initObject && !arg && !gROOT->GetStyle(t)) {
4771 return nullptr;
4772 }
4773
4774 std::unique_ptr<TObject> argInitObject;
4775
4776 if (initObject) {
4777 t = (strlen(initObject->GetTitle())) ? initObject->GetTitle() : initObject->GetName();
4778 } else if (arg) {
4779 if (arg->getStringAttribute("style")) {
4780 t = arg->getStringAttribute("style");
4781 } else if (autoCreate) {
4782 // args will default to histo's object styling, whatever that currently may be
4783 argInitObject = std::make_unique<TH1D>(GetName(), GetTitle(), 1, 0, 1);
4784 initObject = argInitObject.get();
4785 } else {
4786 return nullptr;
4787 }
4788 }
4789
4790 std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case getObject has decided to
4791 // return the owning ptr (for some reason)
4792 if (!gROOT->GetStyle(t)) {
4793 if ((style = getObject<TStyle>(t.Data()))) {
4794 // loaded style (from workspace?) so put in list and use that
4795 gROOT->GetListOfStyles()->Add(style.get());
4796 } else {
4797 if (!autoCreate)
4798 return nullptr;
4799 // create new style - gets put in style list automatically so don't have to delete
4800 // acquire them so saved to workspaces for auto reload ...
4801 style = const_cast<xRooNode &>(*this).acquireNew<TStyle>(t.Data(),
4802 TString::Format("Style for %s component", t.Data()));
4803 if (auto x = dynamic_cast<TAttLine *>(initObject))
4804 ((TAttLine &)*style) = *x;
4805 if (auto x = dynamic_cast<TAttFill *>(initObject))
4806 ((TAttFill &)*style) = *x;
4807 if (auto x = dynamic_cast<TAttMarker *>(initObject))
4808 ((TAttMarker &)*style) = *x;
4809 gROOT->GetListOfStyles()->Add(style.get());
4810 }
4811 } else {
4812 style = std::shared_ptr<TStyle>(gROOT->GetStyle(t), [](TStyle *) {});
4813 }
4814
4815 if (arg && !arg->getStringAttribute("style")) {
4816 arg->setStringAttribute("style", style->GetName());
4817 }
4818
4819 return xRooNode(style, *this);
4820}
4821
4822std::shared_ptr<TObject> xRooNode::acquire(const std::shared_ptr<TObject> &arg, bool checkFactory, bool mustBeNew)
4823{
4824 if (!arg)
4825 return nullptr;
4826 if (!fAcquirer && !get<RooWorkspace>() && fParent)
4827 return fParent->acquire(arg, checkFactory, mustBeNew);
4828
4829 // if has a workspace and our object is the workspace or is in the workspace then add this object to workspace
4830 auto _ws = (fAcquirer) ? nullptr : ws();
4831 if (_ws && (get() == _ws || _ws->arg(GetName()) || (arg && strcmp(arg->GetName(), GetName()) == 0))) {
4833 RooMsgService::instance().setGlobalKillBelow(RooFit::WARNING);
4834 if (auto a = dynamic_cast<RooAbsArg *>(arg.get()); a) {
4835 auto out_arg = _ws->arg(a->GetName());
4836 TString aName = arg->GetName();
4837 int ii = 1;
4838 while (out_arg && mustBeNew) {
4839 a->SetName(TString::Format("%s_%d", aName.Data(), ii++));
4840 out_arg = _ws->arg(a->GetName());
4841 }
4842 if (aName != a->GetName())
4843 Warning("acquire", "Renaming to %s", a->GetName());
4844 if (!out_arg) {
4845 bool done = false;
4846 if (checkFactory) {
4847 if (auto res = _ws->factory(arg->GetName()); res) {
4848 a = res;
4849 done = true;
4850 }
4851 }
4852 if (!done && _ws->import(*a, RooFit::RecycleConflictNodes())) {
4853 if (GETWS(a) != _ws) {
4854 Info("acquire", "A copy of %s has been added to workspace %s", a->GetName(), _ws->GetName());
4855 }
4856 RooMsgService::instance().setGlobalKillBelow(msglevel);
4857 return nullptr;
4858 }
4859 // sanitizeWS(); // clears the caches that might exist up to now, as well interfere with getParameters calls
4860 std::set<std::string> setNames;
4861 for (auto &aa : GETWSSETS(_ws)) {
4862 if (TString(aa.first.c_str()).BeginsWith("CACHE_")) {
4863 setNames.insert(aa.first);
4864 }
4865 }
4866 for (auto &aa : setNames)
4867 ws()->removeSet(aa.c_str());
4868 out_arg = _ws->arg(a->GetName());
4869 if (GETWS(out_arg) != _ws) { // seems that when objects imported their ws isn't set
4870 out_arg->setWorkspace(*_ws);
4871 }
4872 }
4873 RooMsgService::instance().setGlobalKillBelow(msglevel);
4874 return std::shared_ptr<TObject>(out_arg, [](TObject *) {});
4875 } else if (auto a2 = dynamic_cast<RooAbsData *>(arg.get()); a2) {
4876 if (_ws->import(*a2, RooFit::Embedded())) {
4877 RooMsgService::instance().setGlobalKillBelow(msglevel);
4878 return nullptr;
4879 }
4880 RooMsgService::instance().setGlobalKillBelow(msglevel);
4881 return std::shared_ptr<TObject>(_ws->embeddedData(arg->GetName()), [](TObject *) {});
4882 } else if (arg->InheritsFrom("RooFitResult") || arg->InheritsFrom("TTree") || arg->IsA() == TStyle::Class() ||
4883 arg->InheritsFrom("RooStats::HypoTestInverterResult") ||
4884 arg->InheritsFrom("RooStats::HypoTestResult")) {
4885 TObject *out_arg = nullptr;
4886 if(auto fr = dynamic_cast<RooFitResult*>(&*arg); fr && fr->numStatusHistory()==0) {
4887 // fit results without a status history are treated as snapshots
4888 out_arg = fr->Clone();
4889 const_cast<RooLinkedList &>(GETWSSNAPSHOTS(_ws)).Add(out_arg);
4890 } else {
4891 // ensure will have a unique name for import if must be new
4892 TNamed *aNamed = dynamic_cast<TNamed *>(arg.get());
4893 TString aName = arg->GetName();
4894 out_arg = _ws->genobj(arg->GetName());
4895 int ii = 1;
4896 while (aNamed && out_arg && mustBeNew) {
4897 aNamed->SetName(TString::Format("%s;%d", aName.Data(), ii++));
4898 out_arg = _ws->genobj(aNamed->GetName());
4899 }
4900 if (!out_arg) {
4901 if (aName != arg->GetName()) {
4902 Warning("acquire", "Renaming to %s", arg->GetName());
4903 }
4904 if (_ws->import(*arg, false /*replace existing*/)) {
4905 RooMsgService::instance().setGlobalKillBelow(msglevel);
4906 return nullptr;
4907 }
4908 out_arg = _ws->genobj(arg->GetName());
4909 }
4910 }
4911 RooMsgService::instance().setGlobalKillBelow(msglevel);
4912 /* this doesnt work because caller has its own version of fParent, not the one in the browser
4913 for(auto o : *gROOT->GetListOfBrowsers()) {
4914 if(auto b = dynamic_cast<TBrowser*>(o); b){
4915 if(auto _b = dynamic_cast<TGFileBrowser*>( dynamic_cast<TRootBrowser*>(b->GetBrowserImp())->fActBrowser
4916 ); _b) { if (auto item = _b->fListTree->FindItemByObj(_b->fRootDir,this); item) { auto _tmp = _b->fListLevel;
4917 _b->fListLevel = item;
4918 bool _tmp2 = item->IsOpen();
4919 item->SetOpen(false);
4920 this->Browse(b);
4921 item->SetOpen(_tmp2);
4922 _b->fListLevel = _tmp;
4923 }
4924 }
4925 }
4926 }*/
4927 return std::shared_ptr<TObject>(out_arg, [](TObject *) {});
4928 }
4929 RooMsgService::instance().setGlobalKillBelow(msglevel);
4930 // Warning("acquire","Not implemented acquisition of object %s",arg->GetName());
4931 // return nullptr;
4932 }
4933 if (!mustBeNew && fProvider) {
4934 auto out = fProvider->getObject(arg->GetName(), arg->ClassName());
4935 if (out)
4936 return out;
4937 }
4938 auto _owned = find(".memory");
4939 if (!_owned) {
4940 _owned = emplace_back(std::make_shared<xRooNode>(".memory", nullptr, *this));
4941 }
4942 // look for exact name, dont use 'find' because doesnt work if trying to find "1" and it doesn't exist, will get back
4943 // idx 1 instead
4944 if (!mustBeNew) {
4945 for (auto &r : *_owned) {
4946 if (strcmp(r->GetName(), arg->GetName()) == 0 && strcmp(r->get()->ClassName(), arg->ClassName()) == 0) {
4947 return r->fComp;
4948 }
4949 }
4950 }
4951 if (!fProvider)
4952 std::cout << GetName() << " taking over " << arg->ClassName() << "::" << arg->GetName() << std::endl;
4953 /*emplace_back(std::make_shared<Node2>(".memory",nullptr,*this))*/
4954 return _owned->emplace_back(std::make_shared<xRooNode>(arg->GetName(), arg, *this))->fComp;
4955 // return arg;
4956}
4957
4958bool xRooNode::SetXaxis(const char *name, const char *title, int nbins, double low, double high)
4959{
4960 RooUniformBinning b(low, high, nbins, name);
4961 b.SetTitle(title);
4962 return SetXaxis(b);
4963}
4964
4965bool xRooNode::SetXaxis(const char *name, const char *title, int nbins, const double *bins)
4966{
4967 RooBinning b(nbins, bins, name);
4968 b.SetTitle(title);
4969 return SetXaxis(b);
4970}
4971
4973{
4974
4975 auto name = binning.GetName();
4976 double high = binning.highBound();
4977 double low = binning.lowBound();
4978 // int nbins = binning.numBins();
4979 auto title = binning.GetTitle();
4980
4981 // if have any dependents and name isn't one of them then stop
4982 auto _deps = vars();
4983 /*if(!_deps.empty() && !_deps.find(name)) {
4984 throw std::runtime_error(TString::Format("%s Does not depend on %s",GetName(),name));
4985 }*/
4986
4987 // object will need to exist
4988 if (!get()) {
4989 if (fParent && !find(GetName())) {
4990 fComp = fParent->Add(*this, "+").fComp;
4991 }
4992 }
4993
4994 auto a = get<RooAbsArg>();
4995 if (!a)
4996 throw std::runtime_error("Cannot SetXaxis of non-arg");
4997
4998 auto _x = acquire<RooRealVar>(name, title, low, high);
4999 _x->setBinning(binning, a->GetName());
5000 _x->getBinning(a->GetName()).SetTitle(title);
5001 if (_x->getBinningNames().size() == 2) {
5002 // this was the first binning, so copy it over to be the default binning too
5003 _x->setBinning(_x->getBinning(a->GetName()));
5004 } else {
5005 // ensure the default binning is wide enough to cover this range
5006 // the alternative to this would be to ensure setNormRange of all pdfs
5007 // are set to correct range (then default can be narrower than some of the named binnings)
5008 if (_x->getMax() < high)
5009 _x->setMax(high);
5010 if (_x->getMin() > low)
5011 _x->setMin(low);
5012 }
5013
5014 if (!_deps.find(name) && get<RooAbsPdf>()) {
5015 // creating a variable for a pdf we will assume it should be an observable
5016 _x->setAttribute("obs");
5017 }
5018
5019 a->setStringAttribute("xvar", _x->GetName());
5020 a->setStringAttribute("binning", a->GetName());
5021 fXAxis.reset(); // remove any existing xaxis
5022
5023 return true;
5024}
5025
5027{
5028 if (!ax)
5029 return false;
5030 if (ax->IsVariableBinSize()) {
5031 return SetXaxis(ax->GetName(), ax->GetTitle(), ax->GetNbins(), ax->GetXbins()->GetArray());
5032 } else {
5033 return SetXaxis(ax->GetName(), ax->GetTitle(), ax->GetNbins(), ax->GetXmin(), ax->GetXmax());
5034 }
5035}
5036
5037bool xRooNode::contains(const std::string &name) const
5038{
5039 return find(name, false) != nullptr;
5040}
5041
5042std::shared_ptr<xRooNode> xRooNode::find(const std::string &name, bool browseResult) const
5043{
5044 std::string partname = (name.find('/') != std::string::npos) ? name.substr(0, name.find('/')) : name;
5045 auto _s = (!get() && fParent) ? fParent->get<RooSimultaneous>()
5046 : get<RooSimultaneous>(); // makes work if doing simPdf.bins()["blah"]
5047 std::string extra = (_s) ? _s->indexCat().GetName() : "";
5048 for (auto &child : *this) {
5049 if (auto _obj = child->get(); name == child->GetName() || partname == child->GetName() ||
5050 (_obj && name == _obj->GetName()) || (_obj && partname == _obj->GetName()) ||
5051 (!extra.empty() && ((extra + "=" + name) == child->GetName() ||
5052 (extra + "=" + partname) == child->GetName()))) {
5053 if (browseResult)
5054 child->browse(); // needed so can go at()->at()->at()...
5055 if (partname != name && name != child->GetName()) {
5056 return child->at(name.substr(partname.length() + 1));
5057 }
5058 return child;
5059 }
5060 if (partname.find('.') != 0) { // do not allow mainChild browsing if trying to find a "." child ... as is done in
5061 // getObject for ".memory"
5062 if (auto x = mainChild(); x && strcmp(child->GetName(), x.GetName()) == 0) {
5063 // can browse directly into main children as if their children were our children
5064 for (auto &child2 : x.browse()) {
5065 if (auto _obj = child2->get(); name == child2->GetName() || partname == child2->GetName() ||
5066 (_obj && name == _obj->GetName()) ||
5067 (_obj && partname == _obj->GetName())) {
5068 if (browseResult)
5069 child2->browse(); // needed for onward read (or is it? there's a browse above too??)
5070 if (partname != name && name != child2->GetName()) {
5071 return child2->at(name.substr(partname.length() + 1));
5072 }
5073 return child2;
5074 }
5075 }
5076 }
5077 }
5078 }
5079 // before giving up see if partName is numeric and indexes within the range
5080 if (TString s(partname); s.IsDec() && size_t(s.Atoi()) < size()) {
5081 auto child2 = at(s.Atoi());
5082 if (partname != name) {
5083 return child2->at(name.substr(partname.length() + 1));
5084 }
5085 return child2;
5086 }
5087 // allow calling of find on a RooWorkspace to access getObject objects ...
5088 if (get<RooWorkspace>() && name != ".memory") {
5089 if (auto obj = getObject(name)) {
5090 auto out = std::make_shared<xRooNode>(obj, *this);
5091 if (browseResult)
5092 out->browse();
5093 return out;
5094 }
5095 }
5096 return nullptr;
5097}
5098
5099std::shared_ptr<xRooNode> xRooNode::operator[](const std::string &name)
5100{
5101 std::string partname = (name.find('/') != std::string::npos) ? name.substr(0, name.find('/')) : name;
5102 browse();
5103 auto _s = (!get() && fParent) ? fParent->get<RooSimultaneous>()
5104 : get<RooSimultaneous>(); // makes work if doing simPdf.bins()["blah"]
5105 std::string extra = (_s) ? _s->indexCat().GetName() : "";
5106 std::shared_ptr<xRooNode> folderNode;
5107 for (auto &child : *this) {
5108 if (name == child->GetName() || partname == child->GetName() ||
5109 (!extra.empty() &&
5110 ((extra + "=" + name) == child->GetName() || (extra + "=" + partname) == child->GetName()))) {
5111 child->browse(); // needed for onward read (or is it? there's a browse above too??)
5112 if (partname != name && name != child->GetName()) {
5113 return child->operator[](name.substr(partname.length() + 1));
5114 }
5115 return child;
5116 }
5117 if (auto x = mainChild(); strcmp(child->GetName(), x.GetName()) == 0) {
5118 // can browse directly into main children as if their children were our children
5119 for (auto &child2 : x.browse()) {
5120 if (name == child2->GetName() || partname == child2->GetName()) {
5121 child2->browse(); // needed for onward read (or is it? there's a browse above too??)
5122 if (partname != name && name != child2->GetName()) {
5123 return child2->operator[](name.substr(partname.length() + 1));
5124 }
5125 return child2;
5126 }
5127 }
5128 }
5129 if (child->fFolder == (std::string("!") + partname)) {
5130 if (!folderNode)
5131 folderNode = std::make_shared<xRooNode>(child->fFolder.c_str(), nullptr, *this);
5132 folderNode->push_back(child);
5133 }
5134 }
5135 if (folderNode) {
5136 if (partname != name) {
5137 return folderNode->operator[](name.substr(partname.length() + 1));
5138 }
5139 return folderNode;
5140 }
5141 // before giving up see if partName is numeric and indexes within the range
5142 if (TString s(partname); s.IsDec() && size_t(s.Atoi()) < size()) {
5143 auto child2 = at(s.Atoi());
5144 if (partname != name) {
5145 return child2->operator[](name.substr(partname.length() + 1));
5146 }
5147 return child2;
5148 }
5149 auto out = std::make_shared<xRooNode>(partname.c_str(), nullptr, *this); // not adding as child yeeet
5150 // special case, if creating a node in the workspace with a specific name, it's a folder node ...
5151 if (get<RooWorkspace>() && partname == "pdfs") {
5152 out->SetName("!pdfs");
5153 }
5154 if (partname != name) {
5155 return out->operator[](name.substr(partname.length() + 1));
5156 }
5157 return out;
5158}
5159
5161{
5162 if (!b) {
5163 for (auto o : *gROOT->GetListOfBrowsers()) {
5164 b = dynamic_cast<TBrowser *>(o);
5165 if (!b || !b->GetBrowserImp())
5166 continue;
5167 if (auto out = GetTreeItem(b); out)
5168 return out;
5169 }
5170 return nullptr;
5171 }
5172 if (!b->GetBrowserImp())
5173 return nullptr;
5174 if (auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp()))); _b) {
5175 auto _root = GETROOTDIR(_b);
5176 ;
5177 if (!_root)
5178 _root = GETLISTTREE(_b)->GetFirstItem();
5180 return GETLISTTREE(_b)->FindItemByObj(_root, const_cast<xRooNode *>(this));
5181 }
5182 return nullptr;
5183}
5184
5186{
5187 if (!b) {
5188 for (auto o : *gROOT->GetListOfBrowsers()) {
5189 b = dynamic_cast<TBrowser *>(o);
5190 if (!b || !b->GetBrowserImp())
5191 continue;
5192 if (auto out = GetListTree(b); out)
5193 return out;
5194 }
5195 return nullptr;
5196 }
5197 if (b->GetBrowserImp()) {
5198 if (auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp())));
5199 _b) {
5200 auto _root = GETROOTDIR(_b);
5201 if (!_root)
5202 _root = GETLISTTREE(_b)->GetFirstItem();
5203 if (auto item = GETLISTTREE(_b)->FindItemByObj(_root, const_cast<xRooNode *>(this)); item) {
5204 return GETLISTTREE(_b);
5205 }
5206 }
5207 }
5208 return nullptr;
5209}
5210
5211void xRooNode::SetName(const char *name)
5212{
5214 if (auto a = get<RooAbsArg>(); a)
5215 a->setStringAttribute("alias", name);
5216 for (auto o : *gROOT->GetListOfBrowsers()) {
5217 if (auto b = dynamic_cast<TBrowser *>(o); b) {
5218 if (auto item = GetTreeItem(b); item) {
5219 item->SetText(name);
5220 }
5221 }
5222 }
5223}
5224
5225void xRooNode::SetTitle(const char *title)
5226{
5227 if (auto o = (get<TNamed>()); o) {
5228 if (auto c = mainChild(); c.get()) {
5229 c.SetTitle(title);
5230 }
5231 o->SetTitle(title);
5232 }
5233 TNamed::SetTitle(title);
5234}
5235
5237{
5238 if (get<RooArgList>() || (!get() && !(strlen(GetName()) > 0 && (GetName()[0] == '!')) && !fBrowseOperation))
5239 return *this; // nothing to browse - 'collection' nodes should already be populated except for folders
5240 // alternative could have been to mandate that the 'components' of a collection node are the children it has.
5241
5242 auto findByObj = [&](const std::shared_ptr<xRooNode> &n) {
5243 std::vector<std::shared_ptr<xRooNode>> &nn = *this;
5244 for (auto &c : nn) {
5245 if (c->get() == n->get() && strcmp(n->GetName(), c->GetName()) == 0)
5246 return c;
5247 }
5248 return std::shared_ptr<xRooNode>(nullptr);
5249 };
5250
5251 auto appendChildren = [&](const xRooNode &n) {
5252 size_t out = 0;
5253 const std::vector<std::shared_ptr<xRooNode>> &nn(n);
5254 for (auto &c : nn) {
5255 if (auto existing = findByObj(c); existing) {
5256 existing->fTimes++;
5257 existing->fFolder = c->fFolder; // transfer folder assignment
5258 } else {
5259 emplace_back(c);
5260 }
5261 if (!TString(c->GetName()).BeginsWith(".coef"))
5262 out++; // don't count .coef as a child, as technically part of parent
5263 }
5264 return out;
5265 };
5266
5267 const std::vector<std::shared_ptr<xRooNode>> &nn2(*this);
5268 for (auto &c : nn2) {
5269 if (strlen(c->GetName()) > 0 && (c->GetName()[0] == '.')) {
5270 c->fTimes = 1;
5271 continue;
5272 } // never auto-cleanup property children
5273 if (strcmp(c->GetName(), "!.pars") == 0) {
5274 c->fTimes = 1;
5275 continue;
5276 } // special collection, also not cleaned up
5277 if (c->get<RooWorkspace>() || c->get<TFile>()) {
5278 c->fTimes = 1;
5279 continue;
5280 } // workspaces and files not cleaned up: TODO have a nocleanup flag instead
5281 c->fTimes = 0;
5282 }
5283
5284 size_t addedChildren = 0;
5285 if (fBrowseOperation) {
5287 } else {
5288 if (get<RooWorkspace>()) {
5290 }
5291
5292 // if (get<RooAbsPdf>() && ((fParent && fParent->get<RooWorkspace>()) || !fParent)) {
5293 // // top-level pdfs will also list the ".vars" property for -- should make this updateable
5294 // //if (auto x = find("!.vars"); !x) { // this is slower because it triggers a browse of !.vars
5295 // if(!contains("!.vars")) {
5296 // emplace_back(std::make_shared<Node2>("!.vars",nullptr,*this));
5297 // } /*else {
5298 // x->fTimes++;
5299 // }*/
5300 // }
5301
5302 // go through components factors and variations, adding all as children if required
5304 if (!get<RooWorkspace>())
5306 // include coefs if any
5307 auto _coefs = coefs();
5308 if (_coefs.get() && strcmp(_coefs->GetName(), "1") != 0 && strcmp(_coefs->GetName(), "ONE") != 0) {
5309 if (_coefs.size() == 1 && _coefs.get<RooAddition>()) {
5310 if (strcmp(_coefs.at(0)->GetName(), "1") != 0 &&
5311 strcmp(_coefs.at(0)->GetName(), "ONE") != 0) { // don't add the "1"
5312 auto coef = std::make_shared<xRooNode>(".coef", *_coefs.at(0)->get(), *this);
5313 if (auto existing = findByObj(coef); existing) {
5314 existing->fTimes++;
5315 existing->fFolder = _coefs.at(0)->fFolder; // transfer folder assignment
5316 } else {
5317 emplace_back(coef);
5318 }
5319 }
5320 } else {
5321 if (auto existing = find(_coefs.GetName()); existing) {
5322 existing->fTimes++;
5323 existing->fFolder = _coefs.fFolder; // transfer folder assignment
5324 } else {
5325 emplace_back(std::make_shared<xRooNode>(_coefs));
5326 }
5327 }
5328 }
5332 if (get<RooAbsData>())
5334 }
5335 // if has no children and is a RooAbsArg, add all the proxies
5336 if (auto arg = get<RooAbsArg>(); arg && addedChildren == 0) {
5337 for (int i = 0; i < arg->numProxies(); i++) {
5338 auto _proxy = arg->getProxy(i);
5339 if (auto a = dynamic_cast<RooArgProxy *>(_proxy)) {
5340 auto c = std::make_shared<xRooNode>(TString::Format(".%s", _proxy->name()), *(a->absArg()), *this);
5341 if (auto existing = findByObj(c); existing) {
5342 existing->fTimes++;
5343 existing->fFolder = c->fFolder; // transfer folder assignment
5344 } else {
5345 // mark any existing children with the same name for cleanup - this happens e.g. if did a Replace on one
5346 // of these nodes note that the child nodes will still become reordered (the old node will be deleted,
5347 // new node will appear at end)
5348 for (auto &child : *this) {
5349 if (strcmp(child->GetName(), c->GetName()) == 0) {
5350 child->fTimes = 0;
5351 }
5352 }
5353 emplace_back(c);
5354 }
5355 } else if (auto s = dynamic_cast<RooAbsCollection *>(_proxy)) {
5356 for (auto a2 : *s) {
5357 auto c = std::make_shared<xRooNode>(*a2, *this);
5358 if (arg->numProxies() != 1) {
5359 c->fFolder = std::string("!.") +
5360 _proxy->name(); // don't put in a folder if there's just 1 proxy (the collection)
5361 }
5362 if (auto existing = findByObj(c); existing) {
5363 existing->fTimes++;
5364 existing->fFolder = c->fFolder; // transfer folder assignment
5365 } else {
5366 emplace_back(c);
5367 }
5368 }
5369 }
5370 }
5371 /*for(auto& s : arg->servers()) {
5372 auto c = std::make_shared<xRooNode>(*s,*this);
5373 if (auto existing = findByObj(c); existing) {
5374 existing->fTimes++;
5375 existing->fFolder = c->fFolder; // transfer folder assignment
5376 } else {
5377 emplace_back(c);
5378 }
5379 }*/
5380 } else if (auto ir = get<RooStats::HypoTestInverterResult>()) {
5381 // check if we already have a hypoSpace in our memory
5382 bool hasHS = false;
5383 for (auto &c : fBrowsables) {
5384 if (strcmp(c->GetName(), ".memory") == 0 && c->get<xRooHypoSpace>()) {
5385 hasHS = true;
5386 break;
5387 }
5388 }
5389 if (!hasHS) {
5390 // add the HS
5391 auto hs =
5392 fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", std::make_shared<xRooHypoSpace>(ir), *this));
5393 // add the hypoPoints first so they appear first
5394 auto _axes = hs->get<xRooHypoSpace>()->axes();
5395
5396 int i = 0;
5397 for (auto &hp : *hs->get<xRooHypoSpace>()) {
5399 for (auto a : _axes) {
5400 if (a != _axes.first())
5401 coordString += ",";
5402 coordString +=
5403 TString::Format("%s=%g", a->GetName(), hp.coords->getRealValue(a->GetName(), ir->GetXValue(i)));
5404 }
5405 auto hpn = emplace_back(std::make_shared<xRooNode>(coordString, hp.hypoTestResult, hs));
5406 hpn->fTimes++;
5407 hpn->fBrowsables.emplace_back(std::make_shared<xRooNode>(
5408 ".memory", std::shared_ptr<xRooNLLVar::xRooHypoPoint>(&hp, [](xRooNLLVar::xRooHypoPoint *) {}), hpn));
5409 i++;
5410 }
5411 } else {
5412 // ensure all hypoTestResults are flagged as keep-alive
5413 std::vector<std::shared_ptr<xRooNode>> &nn = *this;
5414 for (auto &c : nn) {
5415 if (c->get<RooStats::HypoTestResult>())
5416 c->fTimes++;
5417 }
5418 }
5419 // xRooNode tests;
5420 // for(int i=0;i<ir->ArraySize();i++) {
5421 // tests.push_back(std::make_shared<xRooNode>(TString::Format("%g",ir->GetXValue(i)),*ir->GetResult(i),*this));
5422 // }
5423 // appendChildren(tests);
5424 } else if (get<RooStats::HypoTestResult>()) {
5425
5426 // create the xRooHypoPoint if necessary
5427 xRooNLLVar::xRooHypoPoint *hp = nullptr;
5428 for (auto &c : fBrowsables) {
5429 if (strcmp(c->GetName(), ".memory") == 0 && c->get<xRooNLLVar::xRooHypoPoint>()) {
5430 hp = c->get<xRooNLLVar::xRooHypoPoint>();
5431 c->fTimes++; // keep it alive
5432 break;
5433 }
5434 }
5435 if (!hp) {
5436 auto shp =
5437 std::make_shared<xRooNLLVar::xRooHypoPoint>(std::dynamic_pointer_cast<RooStats::HypoTestResult>(fComp));
5438 fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", shp, *this));
5439 hp = shp.get();
5440 }
5441
5442 xRooNode fits;
5443
5444 if (auto fit = hp->ufit()) {
5445 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("ufit");
5446 }
5447 if (auto fit = hp->cfit_null()) {
5448 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("cfit_null");
5449 }
5450 if (auto fit = hp->cfit_alt()) {
5451 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("cfit_alt");
5452 }
5453 if (auto fit = hp->gfit()) {
5454 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("gfit");
5455 }
5456 if (auto asi = hp->asimov()) {
5457 auto asiP = fits.emplace_back(std::make_shared<xRooNode>(
5458 asi->hypoTestResult ? asi->hypoTestResult : std::make_shared<RooStats::HypoTestResult>(asi->result()),
5459 *this));
5460 asiP->TNamed::SetName("asimov");
5461 asiP->fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", asi, asiP));
5462 }
5464 }
5465
5466 // clear anything that has fTimes = 0 still
5467 auto it = std::vector<std::shared_ptr<xRooNode>>::begin();
5468 while (it != std::vector<std::shared_ptr<xRooNode>>::end()) {
5469 if (it->get()->fTimes == 0) {
5470 for (auto o : *gROOT->GetListOfBrowsers()) {
5471 auto b = dynamic_cast<TBrowser *>(o);
5472 if (b && b->GetBrowserImp()) { // browserImp is null if browser was closed
5473 // std::cout << GetPath() << " Removing " << it->get()->GetPath() << std::endl;
5474
5475 if (auto _b =
5476 dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp())));
5477 _b) {
5478 auto _root = GETROOTDIR(_b);
5479 if (!_root)
5480 _root = GETLISTTREE(_b)->GetFirstItem();
5481 if (auto item = GETLISTTREE(_b)->FindItemByObj(_root, this); item) {
5482 GETLISTTREE(_b)->OpenItem(item);
5483 }
5484 }
5485
5486 b->RecursiveRemove(
5487 it->get()); // problem: if obj is living in a collapsed node it wont actually get deleted
5488 /*auto _b = dynamic_cast<TGFileBrowser*>( dynamic_cast<TRootBrowser*>(b->GetBrowserImp())->fActBrowser );
5489 if (_b) {
5490 std::cout << _b->fRootDir->GetText() << std::endl;
5491 if (auto item = _b->fListTree->FindItemByObj(_b->fRootDir,it->get()); item) {
5492 std::cout << "Found obj: " << item << " " << item->GetText() << std::endl;
5493 _b->fListTree->RecursiveDeleteItem(_b->fRootDir,it->get());
5494 }
5495
5496 //b->RecursiveRemove(it->get());
5497 if (auto item = _b->fListTree->FindItemByObj(_b->fRootDir,it->get()); item) {
5498 std::cout << "Still Found obj: " << item << std::endl;
5499 }
5500 _b->fListTree->ClearViewPort();
5501
5502 }*/
5503 }
5504 }
5505 /*it->get()->ResetBit(TObject::kNotDeleted); ++it;*/ it = erase(it);
5506 } else {
5507 ++it;
5508 }
5509 }
5510
5511 return *this;
5512}
5513
5514////////////////////////////////////////////////////////////////////////////////
5515/// List of observables (global and regular) of this node.
5516
5518{
5519 xRooNode out(".obs", std::make_shared<RooArgList>(), *this);
5520 out.get<RooArgList>()->setName((GetPath() + ".obs").c_str());
5521 for (auto o : vars()) {
5522 if (o->get<RooAbsArg>()->getAttribute("obs")) {
5523 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5524 out.emplace_back(o);
5525 }
5526 }
5527 return out;
5528}
5529
5530////////////////////////////////////////////////////////////////////////////////
5531/// List of global observables of this node.
5532
5534{
5535 xRooNode out(".globs", std::make_shared<RooArgList>(), *this);
5536 out.get<RooArgList>()->setName((GetPath() + ".globs").c_str());
5537 for (auto o : obs()) {
5538 if (o->get<RooAbsArg>()->getAttribute("global")) {
5539 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5540 out.emplace_back(o);
5541 }
5542 }
5543 return out;
5544}
5545
5546////////////////////////////////////////////////////////////////////////////////
5547/// List of regular observables of this node.
5548
5550{
5551 xRooNode out(".robs", std::make_shared<RooArgList>(), *this);
5552 out.get<RooArgList>()->setName((GetPath() + ".robs").c_str());
5553 for (auto o : obs()) {
5554 if (!o->get<RooAbsArg>()->getAttribute("global")) {
5555 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5556 out.emplace_back(o);
5557 }
5558 }
5559 return out;
5560}
5561
5562////////////////////////////////////////////////////////////////////////////////
5563/// List of parameters (non-observables) of this node.
5564
5566{
5567 if (strcmp(GetName(), ".bins") == 0 && fParent) {
5568 // return pars of the parent - this method is used by covariances() if e.g. do node.bins().covariances()
5569 return fParent->pars();
5570 }
5571 xRooNode out(".pars", std::make_shared<RooArgList>(), *this);
5572 out.get<RooArgList>()->setName((GetPath() + ".pars").c_str());
5573 for (auto o : vars()) {
5574 if (!o->get<RooAbsArg>()->getAttribute("obs")) {
5575 out.get<RooArgList>()->add(*(o->get<RooAbsArg>()));
5576 out.emplace_back(o);
5577 }
5578 }
5579 return out;
5580}
5581
5582////////////////////////////////////////////////////////////////////////////////
5583/// List of parameters that are currently constant
5584
5586{
5587 xRooNode out(".consts", std::make_shared<RooArgList>(), *this);
5588 out.get<RooArgList>()->setName((GetPath() + ".consts").c_str());
5589 for (auto o : pars()) {
5590 if (o->get<RooAbsArg>()->getAttribute("Constant") || o->get<RooConstVar>()) {
5591 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5592 out.emplace_back(o);
5593 }
5594 }
5595 return out;
5596}
5597
5598////////////////////////////////////////////////////////////////////////////////
5599/// List of parameters that are currently non-constant
5600/// These parameters do not have the "Constant" attribute
5601
5603{
5604 xRooNode out(".floats", std::make_shared<RooArgList>(), *this);
5605 out.get<RooArgList>()->setName((GetPath() + ".floats").c_str());
5606 for (auto o : pars()) {
5607 if (!o->get<RooAbsArg>()->getAttribute("Constant") && !o->get<RooConstVar>()) {
5608 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5609 out.emplace_back(o);
5610 }
5611 }
5612 return out;
5613}
5614
5615////////////////////////////////////////////////////////////////////////////////
5616/// List of parameters of interest: parameters marked as "of interest"
5617/// These parameters have the "poi" attribute
5618
5620{
5621 xRooNode out(".poi", std::make_shared<RooArgList>(), *this);
5622 out.get<RooArgList>()->setName((GetPath() + ".poi").c_str());
5623 for (auto o : pars()) {
5624 if (o->get<RooAbsArg>()->getAttribute("poi")) {
5625 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5626 out.emplace_back(o);
5627 }
5628 }
5629 return out;
5630}
5631
5632////////////////////////////////////////////////////////////////////////////////
5633/// List of nuisance parameters: non-constant parameters that are not marked of interest,
5634/// as well as any parameters that have been marked by the "np" attribute
5635
5637{
5638 xRooNode out(".np", std::make_shared<RooArgList>(), *this);
5639 out.get<RooArgList>()->setName((GetPath() + ".np").c_str());
5640 for (auto o : pars()) {
5641 if (o->get<RooAbsArg>()->getAttribute("np") ||
5642 (!o->get<RooAbsArg>()->getAttribute("Constant") && !o->get<RooAbsArg>()->getAttribute("poi") &&
5643 !o->get<RooConstVar>())) {
5644 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5645 out.emplace_back(o);
5646 }
5647 }
5648 return out;
5649}
5650
5651////////////////////////////////////////////////////////////////////////////////
5652/// List of prespecified parameters: non-floatable parameters
5653
5655{
5656 xRooNode out(".pp", std::make_shared<RooArgList>(), *this);
5657 out.get<RooArgList>()->setName((GetPath() + ".pp").c_str());
5658 for (auto o : pars()) {
5659 if (!o->get<RooAbsArg>()->getAttribute("np") && !o->get<RooAbsArg>()->getAttribute("poi") &&
5660 (o->get<RooAbsArg>()->getAttribute("Constant") || o->get<RooConstVar>())) {
5661 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
5662 out.emplace_back(o);
5663 }
5664 }
5665 return out;
5666}
5667
5668////////////////////////////////////////////////////////////////////////////////
5669/// List of variables (observables and parameters) of this node
5670
5672{
5673 xRooNode out(".vars", std::make_shared<RooArgList>(), *this);
5674 out.get<RooArgList>()->setName((GetPath() + ".vars").c_str());
5675 if (auto coll = get<RooAbsCollection>(); coll) {
5676 for (auto &x : *this) {
5677 for (auto &y : x->vars()) {
5678 out.push_back(y);
5679 }
5680 }
5681 return out;
5682 }
5683 if (auto p = get<RooAbsArg>(); p) {
5684 // also need to get all constPars so use leafNodeServerList .. will include self if is fundamental, which is what
5685 // we want
5686 // ensure all globs appear after robs, as we rely on this ordering for picking "x" var in "reduced" method
5689 p->leafNodeServerList(&allLeaves);
5690 for (auto &c : allLeaves) {
5691 if (c->isFundamental() || (dynamic_cast<RooConstVar *>(c) && !TString(c->GetName()).IsFloat())) {
5692 if (!c->getAttribute("global")) {
5693 out.get<RooArgList>()->add(*c);
5694 out.emplace_back(std::make_shared<xRooNode>(*c, *this));
5695 }
5696 if (c->getAttribute("global")) {
5697 _globs.emplace_back(std::make_shared<xRooNode>(*c, *this));
5698 _globs.back()->fFolder = "!globs";
5699 } else if (c->getAttribute("obs")) {
5700 out.back()->fFolder = "!robs";
5701 } else if (c->getAttribute("poi")) {
5702 out.back()->fFolder = "!poi";
5703 } else if (c->getAttribute("np") ||
5704 (!c->getAttribute("Constant") && !c->getAttribute("poi") && c->IsA() != RooConstVar::Class())) {
5705 out.back()->fFolder = "!np";
5706 } else if (!c->getAttribute("Constant") && c->IsA() != RooConstVar::Class()) {
5707 out.back()->fFolder = "!floats";
5708 } else {
5709 out.back()->fFolder = "!pp";
5710 }
5711 }
5712 }
5713 for (auto g : _globs) {
5714 out.get<RooArgList>()->add(*g->get<RooAbsArg>());
5715 out.emplace_back(g);
5716 }
5717 } else if (auto p2 = get<RooAbsData>(); p2) {
5718 for (auto a : *p2->get()) {
5719 a->setAttribute("obs");
5720 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5721 out.get<RooArgList>()->add(*a);
5722 }
5723 if (auto _dglobs = p2->getGlobalObservables()) {
5724 for (auto &a : *_dglobs) {
5725 a->setAttribute("obs");
5726 a->setAttribute("global");
5727 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5728 out.get<RooArgList>()->add(*a);
5729 }
5730 } else if (auto _globs = find(".globs"); _globs && _globs->get<RooAbsCollection>()) {
5731 for (auto &a : *_globs->get<RooAbsCollection>()) {
5732 a->setAttribute("obs");
5733 a->setAttribute("global");
5734 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5735 out.get<RooArgList>()->add(*a);
5736 }
5737 } else if (auto _ws = ws(); _ws) {
5738 if (auto _globs2 = dynamic_cast<RooArgSet *>(GETWSSNAPSHOTS(_ws).find(p2->GetName())); _globs2) {
5739 for (auto a : *_globs2) {
5740 a->setAttribute("obs");
5741 a->setAttribute("global");
5742 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5743 out.get<RooArgList>()->add(*a);
5744 }
5745 } else if (auto _gl = GETWSSETS(_ws).find("globalObservables"); _gl != GETWSSETS(_ws).end()) {
5746 for (auto &_g : _gl->second) {
5747 auto _clone = std::shared_ptr<RooAbsArg>(dynamic_cast<RooAbsArg *>(_g->Clone(_g->GetName())));
5748 if (auto v = std::dynamic_pointer_cast<RooAbsRealLValue>(_clone); v && _g->getStringAttribute("nominal"))
5749 v->setVal(TString(_g->getStringAttribute("nominal")).Atof());
5750 out.emplace_back(std::make_shared<xRooNode>(_clone, *this));
5751 out.get<RooArgList>()->add(*_clone);
5752 }
5753 } else if (fParent) {
5754 // note: this is slow in large workspaces ... too many obs to look through?
5755 std::unique_ptr<RooAbsCollection> _globs3(fParent->obs().get<RooArgList>()->selectByAttrib("global", true));
5756 // std::unique_ptr<RooAbsCollection> _globs(_ws->allVars().selectByAttrib("global",true)); - tried this to
5757 // be quicker but it wasn't
5758 for (auto &_g : *_globs3) {
5759 auto _clone = std::shared_ptr<RooAbsArg>(dynamic_cast<RooAbsArg *>(_g->Clone(_g->GetName())));
5760 if (auto v = std::dynamic_pointer_cast<RooAbsRealLValue>(_clone); v && _g->getStringAttribute("nominal"))
5761 v->setVal(TString(_g->getStringAttribute("nominal")).Atof());
5762 out.emplace_back(std::make_shared<xRooNode>(_clone, *this));
5763 out.get<RooArgList>()->add(*_clone);
5764 }
5765 }
5766 }
5767 } else if (auto w = get<RooWorkspace>(); w) {
5768 for (auto a : w->allVars()) {
5769 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5770 out.get<RooArgList>()->add(*a);
5771 }
5772 // add all cats as well
5773 for (auto a : w->allCats()) {
5774 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5775 out.get<RooArgList>()->add(*a);
5776 }
5777 }
5778 return out;
5779}
5780
5782{
5783 xRooNode out(".components", nullptr, *this);
5784
5785 if (auto p = get<RooAddPdf>(); p) {
5786 // only add each pdf once (the coefs will be accumulated in coefs() method) ...
5787 std::set<RooAbsArg *> donePdfs;
5788 for (auto &o : p->pdfList()) {
5789 if (donePdfs.count(o))
5790 continue;
5791 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5792 donePdfs.insert(o);
5793 }
5794 } else if (auto p2 = get<RooRealSumPdf>(); p2) {
5795 // check for common prefixes and suffixes, will use to define aliases to shorten names
5796 // if have more than 1 function
5797 // TString commonPrefix=""; TString commonSuffix="";
5798 // if (p->funcList().size() > 1) {
5799 // bool checked=false;
5800 // for(auto& o : p->funcList()) {
5801 // if (!checked) {
5802 // commonPrefix = o->GetName(); commonSuffix = o->GetName(); checked=true;
5803 // } else {
5804 //
5805 // }
5806 // }
5807 // }
5808 std::set<RooAbsArg *> doneFuncs;
5809 for (auto &o : p2->funcList()) {
5810 if (doneFuncs.count(o))
5811 continue;
5812 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5813 doneFuncs.insert(o);
5814 }
5815 } else if (auto p3 = get<RooAddition>(); p3) {
5816 for (auto &o : p3->list()) {
5817 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5818 }
5819 } else if (auto p4 = get<RooAbsCollection>(); p4) {
5820 for (auto &a : *p4) {
5821 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
5822 }
5823 } else if (auto p5 = get<RooWorkspace>(); p5) {
5824 for (auto &o : p5->components()) {
5825 // only top-level nodes (only clients are integrals or things that aren't part of the workspace)
5826 // if (o->hasClients()) continue;
5827 bool hasClients = false;
5828 for (auto &c : o->clients()) {
5829 if (!c->InheritsFrom("RooRealIntegral") && p5 == GETWS(c)) {
5830 hasClients = true;
5831 break;
5832 }
5833 }
5834 if (hasClients)
5835 continue;
5836 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5837 if (o->InheritsFrom("RooAbsPdf")) {
5838 out.back()->fFolder = "!pdfs";
5839 } else {
5840 out.back()->fFolder = "!scratch";
5841 }
5842 }
5843 for (auto &o : p5->allGenericObjects()) {
5844 if (auto fr = dynamic_cast<RooFitResult *>(o); fr) {
5845 TString s(fr->GetTitle());
5846 if (s.Contains(';'))
5847 s = s(0, s.Index(';'));
5848 if (auto _pdf = out.find(s.Data()); _pdf) {
5849 // std::cout << " type = " << _pdf->get()->ClassName() << std::endl;
5850 out.emplace_back(std::make_shared<xRooNode>(fr->GetName(), *fr, _pdf));
5851 // for a while, this node's parent pointed to something of type Node2!!
5852 // how to fix??? - I fxied it with a new constructor to avoid the shared_ptr<Node2> calling the const
5853 // Node2& constructor via getting wrapped in a Node2(shared_ptr<TObject>) call
5854 // out.back()->fParent = _pdf;
5855 // std::cout << " type2 = " << out.back()->fParent->get()->ClassName() << std::endl;
5856 } else {
5857 out.emplace_back(std::make_shared<xRooNode>(fr->GetName(), *fr, *this));
5858 }
5859 out.back()->fFolder = "!fits";
5860 } else {
5861 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
5862 if (strcmp(out.back()->get()->ClassName(), "TStyle") == 0) {
5863 out.back()->fFolder = "!styles";
5864 } else if (strcmp(out.back()->get()->ClassName(), "RooStats::HypoTestInverterResult") == 0) {
5865 out.back()->fFolder = "!scans";
5866 } else if (strcmp(out.back()->get()->ClassName(), "RooStats::ModelConfig") == 0) {
5867 out.back()->fFolder = "!models";
5868 } else {
5869 out.back()->fFolder = "!objects";
5870 }
5871 }
5872 }
5873 for (auto &[k, v] : GETWSSETS(p5)) {
5874 // skip 'CACHE' sets because they are auto-removed when sanitizing workspaces, which will invalidate these
5875 // children
5876 if (k.find("CACHE_") == 0)
5877 continue;
5878 out.emplace_back(std::make_shared<xRooNode>(k.c_str(), v, *this));
5879 out.back()->fFolder = "!sets";
5880 }
5881
5883 std::unique_ptr<TIterator> iter(snaps.MakeIterator());
5884 TObject *snap;
5885 while ((snap = iter->Next())) {
5886 out.emplace_back(std::make_shared<xRooNode>(*snap, *this));
5887 out.back()->fFolder = "!snapshots";
5888 }
5889 } else if (auto mc = get<RooStats::ModelConfig>()) {
5890 // add the pdf as a child, and the external constraints set if its there
5891 if (mc->GetPdf()) {
5892 out.emplace_back(std::make_shared<xRooNode>(".pdf", *mc->GetPdf(), *this));
5893 }
5894 if (mc->GetExternalConstraints()) {
5895 out.emplace_back(std::make_shared<xRooNode>(".extCons", *mc->GetExternalConstraints(), *this));
5896 }
5897 } else if (strlen(GetName()) > 0 && GetName()[0] == '!' && fParent) {
5898 // special case of dynamic property
5899 if (TString(GetName()) == "!.pars") {
5900 for (auto &c : fParent->pars()) {
5901 out.emplace_back(c);
5902 }
5903 } else {
5904 // the components of a folder are the children of the parent (after browsing) that live in this folder
5905 fParent->browse();
5906 for (auto &c : *fParent) {
5907 if (c->fFolder == GetName()) {
5908 out.emplace_back(c);
5909 }
5910 }
5911 }
5912 }
5913
5914 return out;
5915}
5916
5917////////////////////////////////////////////////////////////////////////////////
5918/// bins of a channel or sample, or channels of a multi-channel pdf
5919
5921{
5922 xRooNode out(".bins", nullptr, *this);
5923
5924 if (auto p = get<RooSimultaneous>(); p) {
5925 std::map<int, std::shared_ptr<xRooNode>> cats; // fill into a map to preserve index ordering
5926 for (auto &c : p->indexCat()) { // is alphabetical in labels
5927 auto pp = p->getPdf(c.first.c_str());
5928 if (!pp)
5929 continue;
5930 cats[c.second] =
5931 std::make_shared<xRooNode>(TString::Format("%s=%s", p->indexCat().GetName(), c.first.c_str()), *pp, *this);
5932 }
5933 for (auto &[_, n] : cats)
5934 out.emplace_back(n);
5935 } else if (auto phf = get<ParamHistFunc>(); phf) {
5936 int i = 1;
5937#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
5938 auto &pSet = phf->_paramSet;
5939#else
5940 auto &pSet = phf->paramList();
5941#endif
5942 for (auto par : pSet) {
5943 out.emplace_back(std::make_shared<xRooNode>(*par, *this));
5944 out.back()->fBinNumber = i;
5945 i++;
5946 }
5947 } else if (auto ax = GetXaxis(); ax) {
5948 for (int i = 1; i <= ax->GetNbins(); i++) {
5949 // create a RooProduct of all bin-specific factors of all shapeFactors
5950 std::vector<RooAbsArg *> _factors;
5951 for (auto f : factors()) {
5952 if (f->get<ParamHistFunc>()) {
5953 if (f->bins()[i - 1]->get<RooProduct>()) {
5954 for (auto &ss : f->bins()[i - 1]->factors())
5955 _factors.push_back(ss->get<RooAbsArg>());
5956 } else {
5957 _factors.push_back(f->bins()[i - 1]->get<RooAbsArg>());
5958 }
5959 }
5960 }
5961 out.emplace_back(std::make_shared<xRooNode>(
5962 TString::Format("%g<=%s<%g", ax->GetBinLowEdge(i), ax->GetParent()->GetName(), ax->GetBinLowEdge(i + 1)),
5963 _factors.empty() ? nullptr
5964 : std::make_shared<RooProduct>(TString::Format("%s.binFactors.bin%d", GetName(), i),
5965 "binFactors", RooArgList()),
5966 *this));
5967 for (auto f : _factors) {
5968#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
5969 out.back()->get<RooProduct>()->_compRSet.add(*f);
5970#else
5971 const_cast<RooArgList &>(out.back()->get<RooProduct>()->realComponents()).add(*f);
5972#endif
5973 }
5974 out.back()->fBinNumber = i;
5975 }
5976 }
5977
5978 return out;
5979}
5980
5982{
5984
5985 if (recurse && fParent) {
5986 // get our coefs and multiply it by the parents coefs ...
5987 auto ourCoefs = xRooNode::coefs(false);
5988 auto parentCoefs = fParent->coefs(true);
5989 if (!parentCoefs.get<RooAbsReal>()) {
5990 // no coefs to include, just return our coefs
5991 return ourCoefs;
5992 }
5993 if (!ourCoefs.get<RooAbsReal>()) {
5994 // just return the parent's coefs
5995 return parentCoefs;
5996 }
5997 // if got here, must combine parentCoefs and outCoefs into a RooProduct
5998 xRooNode out(".recursiveCoefs",
5999 std::make_shared<RooProduct>(".recursiveCoefs",
6000 TString::Format("Recursive Coefficients of %s", GetName()),
6001 *ourCoefs.get<RooAbsReal>(), *parentCoefs.get<RooAbsReal>()),
6002 *this);
6003 // keep alive the two coef nodes by adding to out's memory
6004 auto mem = out.emplace_back(std::make_shared<xRooNode>(".memory", nullptr, *this));
6005 mem->emplace_back(std::make_shared<xRooNode>(ourCoefs));
6006 mem->emplace_back(std::make_shared<xRooNode>(parentCoefs));
6007 return out;
6008 }
6009
6010 bool isResidual = false;
6011
6012 // if parent is a sumpdf or addpdf then include the coefs
6013 // if func appears multiple times then coefs must be combined into a RooAddition temporary
6014 if (fParent) {
6015 // handle case where filters are applied .. need to pass through these
6016 // do this by iterating while fComp is null
6017 auto parent = fParent;
6018 if (!parent->fComp) {
6019 while (!parent->fComp && parent->fParent) {
6020 parent = parent->fParent;
6021 }
6022 // parent should now be node above the filters ... need parent of that
6023 parent = parent->fParent;
6024 if (!parent)
6025 parent = fParent; // revert t original parent in case something went wrong
6026 }
6027 if (auto p = parent->get<RooRealSumPdf>(); p) {
6028 std::size_t i = 0;
6029 for (auto &o : p->funcList()) {
6030 if (o == get()) {
6031 if (i >= p->coefList().size()) {
6032 isResidual = true;
6033 coefs.add(p->coefList());
6034 } else {
6035 coefs.add(*p->coefList().at(i));
6036 }
6037 }
6038 i++;
6039 }
6040 } else if (auto p2 = parent->get<RooAddPdf>(); p2) {
6041 std::size_t i = 0;
6042 if (p2->coefList().empty()) {
6043 // this can happen if all pdfs are extended then the coef is effectively the
6044 // expected number of events
6045 // TODO: test behaviour of xRooNode under this scenario (are histograms correct?)
6046 } else {
6047 for (auto &o : p2->pdfList()) {
6048 if (o == get()) {
6049 if (i >= p2->coefList().size()) {
6050 isResidual = true;
6051 coefs.add(p2->coefList());
6052 } else {
6053 coefs.add(*p2->coefList().at(i));
6054 }
6055 }
6056 i++;
6057 }
6058 }
6059 }
6060 }
6061 if (isResidual) {
6062 // return a node representing 1.-sumOfCoefs
6063 // involves creating sumOfCoefs unless there is only 1 coef, then just use that
6064 auto coefSum = coefs.empty()
6065 ? nullptr
6066 : (coefs.size() == 1 ? std::shared_ptr<RooAbsArg>(coefs.at(0), [](RooAbsArg *) {})
6067 : std::make_shared<RooAddition>((isResidual) ? ".sumOfCoefs" : ".coefs",
6068 "Coefficients of", coefs));
6069 xRooNode out(".coef", coefSum ? std::dynamic_pointer_cast<RooAbsArg>(std::make_shared<RooFormulaVar>(
6070 ".coef", "1-sum(otherCoefs)", "1. - @0", *coefSum))
6071 : nullptr /* should we return a "1." instead? */);
6072 if (coefSum && coefs.size() != 1) {
6073 out.emplace_back(std::make_shared<xRooNode>(".memory", nullptr, *this))
6074 ->emplace_back(
6075 std::make_shared<xRooNode>(".sumOfCoefs", coefSum, out)); // added to keep the sum alive! with the node
6076 }
6077 if (!coefs.empty()) {
6078 out.browse();
6079 }
6080 return out;
6081 } else if (coefs.size() == 1) {
6082 xRooNode out(".coef", std::shared_ptr<RooAbsArg>(coefs.at(0), [](RooAbsArg *) {}), *this);
6083 if (!coefs.empty()) {
6084 out.browse();
6085 }
6086 return out;
6087 } else {
6088 auto coefSum =
6089 coefs.empty()
6090 ? nullptr
6091 : std::make_shared<RooAddition>(".coefs", TString::Format("Coefficients of %s", GetName()), coefs);
6092 xRooNode out(".coefs", coefSum, *this);
6093 if (!coefs.empty())
6094 out.browse();
6095
6096 return out;
6097 }
6098}
6099
6101{
6102 xRooNode out(".factors", nullptr, *this);
6103
6104 if (auto p = get<RooProdPdf>(); p) {
6105 auto _main = mainChild();
6106 if (auto a = _main.get<RooRealSumPdf>(); a && !a->getStringAttribute("alias")) {
6107 a->setStringAttribute("alias", "samples");
6108 } else if (auto a2 = _main.get<RooAddPdf>(); a2 && !a2->getStringAttribute("alias")) {
6109 a2->setStringAttribute("alias", "components");
6110 }
6111 int _npdfs = p->pdfList().size();
6112 for (auto &o : p->pdfList()) {
6113 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6114 if (_npdfs > 5 && o != _main.get())
6115 out.back()->fFolder = "!constraints";
6116 }
6117 } else if (auto p2 = get<RooProduct>(); p2) {
6118 for (auto &o : p2->components()) {
6119 if (o->InheritsFrom("RooProduct")) {
6120 // get factors of this term
6121 auto x = xRooNode("tmp", *o, *this).factors();
6122 for (auto &n : x) {
6123 out.emplace_back(std::make_shared<xRooNode>(n->GetName(), n->fComp, *this));
6124 }
6125 } else {
6126 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6127 }
6128 }
6129 } else if (auto w = get<RooWorkspace>(); w) {
6130 // if workspace, return all functions (not pdfs) that have a RooProduct as one of their clients
6131 // or not clients
6132 // exclude obs and globs
6133 auto oo = obs(); // need to keep alive as may contain owning globs
6134 auto &_obs = *(oo.get<RooArgList>());
6135 for (auto a : w->allFunctions()) {
6136 if (_obs.contains(*a))
6137 continue;
6138 bool show(true);
6139 for (auto c : a->clients()) {
6140 show = false;
6141 if (c->InheritsFrom("RooProduct")) {
6142 show = true;
6143 break;
6144 }
6145 }
6146 if (show)
6147 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6148 }
6149 }
6150
6151 /*
6152 // if parent is a sumpdf or addpdf then include the coefs
6153 // if func appears multiple times then coefs must be combined into a RooAddition temporary
6154 if (fParent) {
6155 RooArgList coefs;
6156 if(auto p = fParent->get<RooRealSumPdf>();p) {
6157 int i=0;
6158 for(auto& o : p->funcList()) {
6159 if (o == get()) {
6160 coefs.add( *p->coefList().at(i) );
6161 }
6162 i++;
6163 }
6164 } else if(auto p = fParent->get<RooAddPdf>(); p) {
6165 int i=0;
6166 for(auto& o : p->pdfList()) {
6167 if (o == get()) {
6168 coefs.add( *p->coefList().at(i) );
6169 }
6170 i++;
6171 }
6172 }
6173 if (!coefs.empty()) {
6174 if (coefs.size() == 1) {
6175 if (strcmp(coefs.at(0)->GetName(),"1")) { // don't add the "1"
6176 out.emplace_back(std::make_shared<Node2>(".coef", *coefs.at(0), *this));
6177 }
6178 } else {
6179 out.emplace_back(std::make_shared<Node2>(".coefs",
6180 std::make_shared<RooAddition>(".coefs", "Coefficients of",
6181 coefs), *this));
6182 }
6183 }
6184 }
6185 */
6186 return out;
6187}
6188
6190{
6191 xRooNode out(".variations", nullptr, *this);
6192
6193 // if (auto p = get<RooSimultaneous>(); p) {
6194 // for (auto &c : p->indexCat()) {
6195 // auto pp = p->getPdf(c.first.c_str());
6196 // if (!pp)
6197 // continue;
6198 // out.emplace_back(
6199 // std::make_shared<xRooNode>(TString::Format("%s=%s", p->indexCat().GetName(), c.first.c_str()), *pp,
6200 // *this));
6201 // }
6202 // } else
6203 if (auto p2 = get<PiecewiseInterpolation>(); p2) {
6204#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
6205 out.emplace_back(std::make_shared<xRooNode>("nominal", p2->_nominal.arg(), *this));
6206#else
6207 out.emplace_back(std::make_shared<xRooNode>("nominal", *(p2->nominalHist()), *this));
6208#endif
6209 for (size_t i = 0; i < p2->paramList().size(); i++) {
6210 // TODO: should we only return one if we find they are symmetrized?
6211 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p2->paramList().at(i)->GetName()),
6212 *p2->highList().at(i), *this));
6213 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p2->paramList().at(i)->GetName()),
6214 *p2->lowList().at(i), *this));
6215 }
6217#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
6218 out.emplace_back(std::make_shared<xRooNode>("nominal", RooFit::RooConst(p3->_nominal), *this));
6219 for (size_t i = 0; i < p3->_paramList.size(); i++) {
6220 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p3->_paramList.at(i)->GetName()),
6221 RooFit::RooConst(p3->_high.at(i)), *this));
6222 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p3->_paramList.at(i)->GetName()),
6223 RooFit::RooConst(p3->_low.at(i)), *this));
6224 }
6225#else
6226 out.emplace_back(std::make_shared<xRooNode>("nominal", RooFit::RooConst(p3->nominal()), *this));
6227 for (size_t i = 0; i < p3->variables().size(); i++) {
6228 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p3->variables().at(i)->GetName()),
6229 RooFit::RooConst(p3->high().at(i)), *this));
6230 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p3->variables().at(i)->GetName()),
6231 RooFit::RooConst(p3->low().at(i)), *this));
6232 }
6233#endif
6234
6235 } else if (auto p4 = get<ParamHistFunc>(); p4) {
6236 // I *think* I put this here so that can browse into a ParamHistFunc
6237 // int i = 0;
6238 // for (auto par : p4->_paramSet) {
6239 // TString _name = par->GetName();
6240 // // if(auto _v = dynamic_cast<RooRealVar*>(p->_dataSet.get(i)->first()); _v) {
6241 // // _name = TString::Format("%s=%g",_v->GetName(),_v->getVal());
6242 // // }
6243 // // out.emplace_back(std::make_shared<xRooNode>(_name,*par,*this)); -- -removed cos now have bin()
6244 // method i++;
6245 // }
6246 }
6247 return out;
6248}
6249
6251{
6252 RooArgList out;
6253 out.setName(GetName());
6254 for (auto &k : *this) {
6255 if (auto o = k->get<RooAbsArg>(); o)
6256 out.add(*o);
6257 }
6258 return out;
6259}
6260
6262{
6263 xRooNode out(".datasets()", nullptr, *this);
6264 // removed the browse operation since no longer showing '.datasets()' in browser
6265 // and otherwise this means dataset reduction operation will be called every time we 'browse()' the datasets node
6266 // out.fBrowseOperation = [](xRooNode *f) { return f->fParent->datasets(); };
6267
6268 if (auto _ws = get<RooWorkspace>(); _ws) {
6269 for (auto &d : _ws->allData()) {
6270 out.emplace_back(std::make_shared<xRooNode>(*d, *this));
6271 out.back()->fFolder = "!datasets";
6272 }
6273 } else if (get<RooAbsPdf>() ||
6274 (!get() && fParent &&
6275 fParent->get<RooAbsPdf>())) { // second condition handles 'bins' nodes of pdf, which have null ptr
6276 // only add datasets that have observables that cover all our observables
6277 auto oo = obs(); // must keep alive in case is owning the globs
6278 RooArgSet _obs(*oo.get<RooArgList>());
6279 //_obs.add(coords(true).argList(), true); // include coord observables too, and current xaxis if there's one -
6280 // added in loop below
6281
6282 TString cut;
6284 for (auto _c : coords()) { // coords() moves vars to their respective coordinates too
6285 if (auto _cat = _c->get<RooAbsCategoryLValue>(); _cat) {
6286 if (cut != "")
6287 cut += " && ";
6288 cut += TString::Format("%s==%d", _cat->GetName(), _cat->getCurrentIndex());
6289 _obs.add(*_cat,
6290 true); // note: if we ever changed coords to return clones, would need to keep coords alive
6291 cutobs.add(*_cat);
6292 } else if (auto _rv = _c->get<RooAbsRealLValue>(); _rv) {
6293 // todo: check coordRange is a single range rather than multirange
6294 if (cut != "")
6295 cut += " && ";
6296 cut += TString::Format("%s>=%f&&%s<%f", _rv->GetName(), _rv->getMin(_rv->getStringAttribute("coordRange")),
6297 _rv->GetName(), _rv->getMax(_rv->getStringAttribute("coordRange")));
6298 _obs.add(*_rv,
6299 true); // note: if we ever changed coords to return clones, would need to keep coords alive
6300 cutobs.add(*_rv);
6301 } else {
6302 throw std::runtime_error("datasets(): Unsupported coordinate type");
6303 }
6304 }
6305 if (auto s = get<RooSimultaneous>()) {
6306 // check if we have a pdf for every category ... if not then add to cut
6307 bool hasMissing = false;
6308 TString extraCut = "";
6309 for (auto cat : s->indexCat()) {
6310 if (!s->getPdf(cat.first.c_str())) {
6311 hasMissing = true;
6312 } else {
6313 if (extraCut != "")
6314 extraCut += " || ";
6315 extraCut += TString::Format("%s==%d", s->indexCat().GetName(), cat.second);
6316 }
6317 }
6318 if (hasMissing) {
6319 if (cut != "")
6320 cut += " && ";
6321 cut += "(" + extraCut + ")";
6322 cutobs.add(s->indexCat());
6323 }
6324 }
6325
6326 if (auto ax = GetXaxis(); ax && dynamic_cast<RooAbsArg *>(ax->GetParent())->getAttribute("obs")) {
6327 auto a = dynamic_cast<RooAbsArg *>(ax->GetParent());
6328 _obs.add(*a, true);
6329 }
6330 xRooNode _datasets; // will be any child datasets, along with datasets of the workspace
6331 for (auto &child : *this) {
6332 if (child->get<RooAbsData>())
6333 _datasets.push_back(child);
6334 }
6335 if (auto __ws = ws(); __ws) {
6336 xRooNode _wsNode(*__ws, *this);
6337 for (auto &d : _wsNode.datasets()) {
6338 _datasets.push_back(d);
6339 }
6340 }
6341
6342 for (auto &d : _datasets) {
6343 if (std::unique_ptr<RooAbsCollection>(d->obs().argList().selectCommon(_obs))->size() == _obs.size()) {
6344 // all obs present .. include
6345
6346 if (cut != "") {
6347 RooFormulaVar cutFormula("cut1", cut, cutobs); // doing this to avoid complaints about unused vars
6348 // TODO: Could consider using a 'filter' node (see filter() method) applied to the dataset instead
6349 // of creating and using a reduced dataset here
6350 out.emplace_back(std::make_shared<xRooNode>(
6351 std::shared_ptr<RooAbsData>(d->get<RooAbsData>()->reduce(
6352 *std::unique_ptr<RooAbsCollection>(d->robs().get<RooArgList>()->selectCommon(_obs)), cutFormula)),
6353 *this));
6354 // put a subset of the globs in the returned dataset too
6355 out.back()->get<RooAbsData>()->setGlobalObservables(*std::unique_ptr<RooAbsCollection>(
6356 d->globs().get<RooArgList>()->selectCommon(*globs().get<RooArgList>())));
6357 if (d->get()->TestBit(1 << 20))
6358 out.back()->get()->SetBit(1 << 20);
6359 // need to attach the original dataset so that things like SetBinContent can interact with it
6360 out.back()->fBrowsables.emplace_back(std::make_shared<xRooNode>(".sourceds", d->fComp, *this));
6361 } else {
6362 out.emplace_back(std::make_shared<xRooNode>(d->fComp, *this));
6363 }
6364 }
6365 }
6366 /*else if(auto p = get<RooFitResult>(); p) {
6367 // look for datasets in workspace that match the fit result name after hashing
6368 for(auto& _d : xRooNode(*_ws,*this).datasets()) {
6369 auto _hash = RooAbsTree::nameToHash(_d->get()->GetName());
6370 if (TString::Format("%d;%d",_hash.first,_hash.second) == p->GetTitle()) {
6371 out.emplace_back(std::make_shared<xRooNode>(_d->fComp, *this));
6372 }
6373 }
6374 }*/
6375 } else if (auto mc = get<RooStats::ModelConfig>()) {
6376 return xRooNode(*mc->GetPdf(), fParent).datasets();
6377 }
6378
6379 return out;
6380}
6381
6382std::shared_ptr<xRooNode> xRooNode::getBrowsable(const char *name) const
6383{
6384 for (auto b : fBrowsables) {
6385 if (b && strcmp(b->GetName(), name) == 0)
6386 return b;
6387 }
6388 return nullptr;
6389}
6390
6392{
6393
6394 if (auto fr = get<RooFitResult>(); fr) {
6395 return nullptr;
6396 }
6397
6398 if (auto theData = get<RooDataSet>(); theData) {
6399
6400 TH1 *theHist = nullptr;
6401
6402 if (fromPad) {
6403 // find first histogram in pad
6404 for (auto o : *fromPad->GetListOfPrimitives()) {
6405 theHist = dynamic_cast<TH1 *>(o);
6406 if (theHist) {
6407 theHist = static_cast<TH1 *>(theHist->Clone());
6408 theHist->Reset();
6409 break;
6410 } // clone because theHist gets deleted below
6411 }
6412 }
6413
6414 if (!theHist) {
6415 auto _parentPdf = parentPdf();
6416 if (!_parentPdf) {
6417 // can still build graph if v is an obs ... will use v binning
6418 auto vo = dynamic_cast<TObject *>(v);
6419 if (v && obs().find(vo->GetName())) {
6420 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(v)) {
6421 theHist = new TH1D(
6422 TString::Format("%s_%s", GetName(), vo->GetName()),
6423 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
6424 cat->numTypes(), 0, cat->numTypes());
6425 int i = 1;
6426 std::map<int, std::string> cats; // fill into a map to preserve index ordering
6427 for (auto &c : *cat) {
6428 cats[c.second] = c.first;
6429 }
6430 for (auto &[_, label] : cats) {
6431 theHist->GetXaxis()->SetBinLabel(i++, label.c_str());
6432 }
6433 } else {
6434 auto _binning = v->getBinningPtr(nullptr);
6435 if (_binning->isUniform()) {
6436 theHist = new TH1D(
6437 TString::Format("%s_%s", GetName(), vo->GetName()),
6438 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
6439 v->numBins(), _binning->lowBound(), _binning->highBound());
6440 } else {
6441 theHist = new TH1D(
6442 TString::Format("%s_%s", GetName(), vo->GetName()),
6443 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
6444 v->numBins(), _binning->array());
6445 }
6446 }
6447 } else {
6448 throw std::runtime_error("Cannot draw dataset without parent PDF");
6449 }
6450 } else {
6451 theHist = _parentPdf->BuildHistogram(v, true);
6452 }
6453 }
6454 if (!theHist)
6455 return nullptr;
6456 // this hist will get filled with w*x to track weighted x position per bin
6457 TH1 *xPos = static_cast<TH1 *>(theHist->Clone("xPos"));
6458 xPos->Reset();
6459 TH1 *xPos2 = static_cast<TH1 *>(theHist->Clone("xPos2"));
6460 xPos2->Reset();
6461 auto nHist = std::unique_ptr<TH1>(static_cast<TH1 *>(theHist->Clone("nEntries")));
6462 nHist->Reset();
6463
6464 auto dataGraph = new TGraphAsymmErrors;
6465 dataGraph->SetEditable(false);
6466 dataGraph->SetName(GetName());
6467 dataGraph->SetTitle(strlen(theData->GetTitle()) ? theData->GetTitle() : theData->GetName());
6468 // next line triggers creation of the histogram inside the graph, in root 6.22 that isn't protected from being
6469 // added to gDirectory
6470 dataGraph->SetTitle(TString::Format("%s;%s;Events", dataGraph->GetTitle(), theHist->GetXaxis()->GetTitle()));
6471 *static_cast<TAttMarker *>(dataGraph) = *static_cast<TAttMarker *>(theHist);
6472 *static_cast<TAttLine *>(dataGraph) = *static_cast<TAttLine *>(theHist);
6473 dataGraph->SetMarkerStyle(20);
6474 dataGraph->SetLineColor(kBlack);
6475
6476 auto _obs = obs();
6477
6478 // auto x = theData->get()->find((v) ? dynamic_cast<TObject*>(v)->GetName() : theHist->GetXaxis()->GetName());
6479 // const RooAbsReal* xvar = (x) ? dynamic_cast<RooAbsReal*>(x) : nullptr;
6480 // const RooAbsCategory* xcat = (x && !xvar) ? dynamic_cast<RooAbsCategory*>(x) : nullptr;
6481 auto x = _obs.find((v) ? dynamic_cast<TObject *>(v)->GetName()
6482 : (theHist->GetXaxis()->IsAlphanumeric() ? theHist->GetXaxis()->GetTimeFormatOnly()
6483 : theHist->GetXaxis()->GetName()));
6484 if (x && x->get<RooAbsArg>()->getAttribute("global")) {
6485 // is global observable ...
6486 dataGraph->SetPoint(0, x->get<RooAbsReal>()->getVal(), 1e-15);
6487 dataGraph->SetTitle(TString::Format("%s = %f", dataGraph->GetTitle(), dataGraph->GetPointX(0)));
6488 delete xPos;
6489 delete xPos2;
6490 delete theHist;
6491 return dataGraph;
6492 }
6493
6494 const RooAbsReal *xvar = (x) ? x->get<RooAbsReal>() : nullptr;
6495 const RooAbsCategory *xcat = (x && !xvar) ? x->get<RooAbsCategory>() : nullptr;
6496
6497 auto _coords = coords();
6498
6499 TString pName((fromPad) ? fromPad->GetName() : "");
6500 auto _pos = pName.Index('=');
6501
6502 int nevent = theData->numEntries();
6503 for (int i = 0; i < nevent; i++) {
6504 theData->get(i);
6505 bool _skip = false;
6506 for (auto _c : _coords) {
6507 if (auto cat = _c->get<RooAbsCategoryLValue>(); cat) {
6508 if (cat->getIndex() != theData->get()->getCatIndex(cat->GetName())) {
6509 _skip = true;
6510 break;
6511 }
6512 } else if (auto rv = _c->get<RooAbsRealLValue>(); rv) {
6513 // must be in range
6514 if (!rv->inRange(theData->get()->getRealValue(rv->GetName()), rv->getStringAttribute("coordRange"))) {
6515 _skip = true;
6516 break;
6517 }
6518 }
6519 }
6520 if (_pos != -1) {
6521 if (auto cat = dynamic_cast<RooAbsCategory *>(theData->get()->find(TString(pName(0, _pos))));
6522 cat && cat->getLabel() != pName(_pos + 1, pName.Length())) {
6523 _skip = true;
6524 }
6525 }
6526 if (_skip)
6527 continue;
6528
6529 if (xvar) {
6530 xPos->Fill(xvar->getVal(), xvar->getVal() * theData->weight());
6531 xPos2->Fill(xvar->getVal(), pow(xvar->getVal(), 2) * theData->weight());
6532 }
6533
6534 if (xcat) {
6535 theHist->Fill(xcat->getLabel(), theData->weight());
6536 nHist->Fill(xcat->getLabel(), 1);
6537 } else {
6538 theHist->Fill((x) ? xvar->getVal() : 0.5, theData->weight());
6539 nHist->Fill((x) ? xvar->getVal() : 0.5, 1);
6540 }
6541 }
6542
6543 xPos->Divide(theHist);
6544 xPos2->Divide(theHist);
6545
6546 // update the x positions to the means for each bin and use poisson asymmetric errors for data ..
6547 for (int i = 0; i < theHist->GetNbinsX(); i++) {
6548 if (includeZeros || nHist->GetBinContent(i + 1)) {
6549 double val = theHist->GetBinContent(i + 1);
6550
6551 dataGraph->SetPoint(dataGraph->GetN(),
6552 (xvar && val) ? xPos->GetBinContent(i + 1) : theHist->GetBinCenter(i + 1), val);
6553
6554 // x-error will be the (weighted) standard deviation of the x values ...
6555 double xErr = xPos2->GetBinContent(i + 1) - pow(xPos->GetBinContent(i + 1), 2);
6556 xErr = (xErr <= 0) ? 0. : sqrt(xErr); // protects against floating point rounding effects
6557
6558 if (xErr || val) {
6559 dataGraph->SetPointError(dataGraph->GetN() - 1, xErr, xErr,
6560 val - 0.5 * TMath::ChisquareQuantile(TMath::Prob(1, 1) / 2., 2. * (val)),
6561 0.5 * TMath::ChisquareQuantile(1. - TMath::Prob(1, 1) / 2., 2. * (val + 1)) -
6562 val);
6563 }
6564 }
6565 }
6566
6567 // transfer limits from theHist to dataGraph hist
6568 dataGraph->GetHistogram()->GetXaxis()->SetLimits(theHist->GetXaxis()->GetXmin(), theHist->GetXaxis()->GetXmax());
6569 // and bin labels, if any
6570 if (xcat) {
6571 dataGraph->GetHistogram()->GetXaxis()->Set(theHist->GetNbinsX(), 0, theHist->GetNbinsX());
6572 for (int i = 1; i <= theHist->GetNbinsX(); i++)
6573 dataGraph->GetHistogram()->GetXaxis()->SetBinLabel(i, theHist->GetXaxis()->GetBinLabel(i));
6574 }
6575
6576 delete xPos;
6577 delete xPos2;
6578 delete theHist;
6579
6580 // std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case getObject
6581 // has decided to return the owning ptr (for some reason) std::string _title =
6582 // strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(); if (!gROOT->GetStyle(_title.c_str()))
6583 // {
6584 // if ( (style = getObject<TStyle>(_title)) ) {
6585 // // loaded style (from workspace?) so put in list and use that
6586 // gROOT->GetListOfStyles()->Add(style.get());
6587 // } else {
6588 // // create new style - gets put in style list automatically so don't have to delete
6589 // // acquire them so saved to workspaces for auto reload ...
6590 // style = const_cast<xRooNode&>(*this).acquireNew<TStyle>(_title.c_str(),
6591 // TString::Format("Style for %s component", _title.c_str()));
6592 // (TAttLine &) (*style) = *dynamic_cast<TAttLine *>(dataGraph);
6593 // (TAttFill &) (*style) = *dynamic_cast<TAttFill *>(dataGraph);
6594 // (TAttMarker &) (*style) = *dynamic_cast<TAttMarker *>(dataGraph);
6595 // gROOT->GetListOfStyles()->Add(style.get());
6596 // }
6597 // }
6598 auto _styleNode = styles(dataGraph);
6599 if (auto _style = _styleNode.get<TStyle>()) {
6600 *dynamic_cast<TAttLine *>(dataGraph) = *_style;
6601 *dynamic_cast<TAttFill *>(dataGraph) = *_style;
6602 *dynamic_cast<TAttMarker *>(dataGraph) = *_style;
6603 }
6604 return dataGraph;
6605 }
6606
6607 throw std::runtime_error("Cannot build graph");
6608}
6609
6611{
6612 if (fr) {
6613 if (auto _w = ws(); _w) {
6614 auto res = acquire(std::shared_ptr<RooFitResult>(const_cast<RooFitResult *>(fr), [](RooFitResult *) {}));
6615 for (auto o : _w->allGenericObjects()) {
6616 if (auto _fr = dynamic_cast<RooFitResult *>(o); _fr) {
6617 _fr->ResetBit(1 << 20);
6618 }
6619 }
6620 res->SetBit(1 << 20);
6621 // assign values
6622 auto allVars = _w->allVars();
6623 allVars = fr->floatParsFinal();
6624 allVars = fr->constPars();
6625 } else {
6626 // need to add to memory as a specific name
6627 throw std::runtime_error("Not supported yet"); // complication is how to replace an existing fitResult in
6628 // .memory auto _clone = std::make_shared<RooFitResult>(*fr);
6629 //_clone->SetName("fitResult");
6630 }
6631 } else {
6633 }
6634}
6635
6637{
6638 if (auto _fr = fr.get<const RooFitResult>()) {
6640 } else
6641 throw std::runtime_error("Not a RooFitResult");
6642}
6643
6644xRooNode xRooNode::fitResult(const char *opt) const
6645{
6646
6647 if (get<RooFitResult>())
6648 return *this;
6649 if (get<RooAbsData>()) {
6650 if (auto _fr = find(".fitResult"); _fr)
6651 return _fr;
6652#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
6653 // check if weightVar of RooAbsData has fitResult attribute on it, will be the generation fit result
6654 if (get<RooDataSet>() && get<RooDataSet>()->weightVar() &&
6655 get<RooDataSet>()->weightVar()->getStringAttribute("fitResult")) {
6656 return xRooNode(getObject<const RooFitResult>(get<RooDataSet>()->weightVar()->getStringAttribute("fitResult")),
6657 *this);
6658 }
6659#endif
6660 return xRooNode();
6661 }
6662
6663 TString sOpt(opt);
6664 if (sOpt == "prefit") {
6665 // build a fitResult using nominal values and infer errors from constraints
6666 // that aren't the 'main' constraints
6667 // Warning("fitResult","Building prefitResult by examining pdf. Consider setting an explicit prefitResult
6668 // (SetFitResult(fr)) where fr name is prefitResult");
6669
6670 // ensure coefs are included if there are any
6671 auto _coefs = coefs();
6672 if (_coefs.get()) {
6673 return xRooNode(RooProduct("tmp", "tmp", RooArgList(*get<RooAbsArg>(), *_coefs.get<RooAbsReal>())))
6674 .fitResult(opt);
6675 }
6676
6677 std::unique_ptr<RooArgList> _pars(dynamic_cast<RooArgList *>(pars().argList().selectByAttrib("Constant", false)));
6678 auto fr = std::make_shared<RooFitResult>("prefitResult", "Prefit");
6679 fr->setFinalParList(*_pars);
6680 for (auto &p : fr->floatParsFinal()) {
6681 auto _v = dynamic_cast<RooRealVar *>(p);
6682 if (!_v)
6683 continue;
6684 if (auto s = _v->getStringAttribute("nominal"); s)
6685 _v->setVal(TString(s).Atof());
6686 auto _constr = xRooNode(fParent->getObject<RooRealVar>(p->GetName()), *this).constraints();
6687 std::shared_ptr<xRooNode> pConstr;
6688 for (auto &c : _constr) {
6689 if (c->get<RooPoisson>() || c->get<RooGaussian>()) {
6690 // require parameter to be a direct server of the constraint pdf to count
6691 bool isServer = true;
6692 if (c->get<RooGaussian>()) {
6693 isServer = false;
6694 for (auto s : c->get<RooAbsArg>()->servers()) {
6695 if (strcmp(s->GetName(), p->GetName()) == 0) {
6696 isServer = true;
6697 break;
6698 }
6699 }
6700 }
6701 if (isServer) {
6702 pConstr = c;
6703 break;
6704 }
6705 }
6706 }
6707 if (pConstr) {
6708 // there will be 3 deps, one will be this par, the other two are the mean and error (or error^2 in case of
6709 // poisson use the one that's a ConstVar as the error to break a tie ...
6710 double prefitVal = 0;
6711 double prefitError = 0;
6712 for (auto &_d : pConstr->vars()) {
6713 if (strcmp(p->GetName(), _d->get()->GetName()) == 0)
6714 continue;
6715 if (auto _c = _d->get<RooConstVar>(); _c && _c->getVal() != 0) {
6716 if (prefitError)
6717 prefitVal = prefitError; // loading val into error already, so move it over
6718 prefitError = _c->getVal();
6719 } else if (prefitError == 0) {
6720 prefitError = _d->get<RooAbsReal>()->getVal();
6721 } else {
6722 prefitVal = _d->get<RooAbsReal>()->getVal();
6723 }
6724 }
6725
6726 if (pConstr->get<RooGaussian>() && pConstr->browse().find(".sigma")) {
6727 prefitError = pConstr->find(".sigma")->get<RooAbsReal>()->getVal();
6728 }
6729 // std::cout << p->GetName() << " extracted " << prefitVal << " " << prefitError << " from ";
6730 // pConstr->deps().Print();
6731 if (pConstr->get<RooPoisson>()) {
6732 // prefitVal will be the global observable value, need to divide that by tau
6734 // prefiterror will be tau ... need 1/sqrt(tau) for error
6735 prefitError = 1. / sqrt(prefitError);
6736 }
6737 if (!_v->getStringAttribute("nominal"))
6738 _v->setVal(prefitVal);
6739 _v->setError(prefitError);
6740 } else {
6741 // unconstrained, remove error
6742 _v->removeError();
6743 }
6744 }
6745 auto _args = consts().argList();
6746 _args.add(pp().argList());
6747 // global obs are added to constPars list too
6748 auto _globs = globs(); // keep alive as may own glob
6749 _args.add(_globs.argList());
6750 fr->setConstParList(_args);
6751 std::unique_ptr<RooArgList> _snap(dynamic_cast<RooArgList *>(_pars->snapshot()));
6752 for (auto &p : *_snap) {
6753 if (auto atr = p->getStringAttribute("initVal"); atr && dynamic_cast<RooRealVar *>(p))
6754 dynamic_cast<RooRealVar *>(p)->setVal(TString(atr).Atof());
6755 }
6756 fr->setInitParList(*_snap);
6757 return xRooNode(fr, *this);
6758 }
6759
6760 // return first checked fit result present in the workspace
6761 if (auto _w = ws(); _w) {
6762 auto checkFr = [&](TObject *o) {
6763 if (auto _fr = dynamic_cast<RooFitResult *>(o); _fr && _fr->TestBit(1 << 20)) {
6764 // check all pars match final/const values ... if mismatch need to create a new RooFitResult
6767 for (auto p : pars()) {
6768 if (p->get<RooAbsArg>()->getAttribute("Constant") || p->get<RooConstVar>()) {
6769 // par must not be in the float list or have different value to what is in constPars (if it is there)
6770 if(_fr->floatParsFinal().find(p->GetName()) ||
6771 (p->get<RooAbsReal>() && std::abs(_fr->constPars().getRealValue(p->GetName(), std::numeric_limits<double>::quiet_NaN()) -
6772 p->get<RooAbsReal>()->getVal()) > 1e-15) ||
6773 (p->get<RooAbsCategory>() && p->get<RooAbsCategory>()->getCurrentIndex() !=
6774 _fr->constPars().getCatIndex(p->GetName(), std::numeric_limits<int>().max()))) {
6775 newConsts.add(*p->get<RooAbsArg>());
6776 }
6777 } else {
6778 // floating par must be present in the floatPars list with the same value
6779 if(!_fr->floatParsFinal().find(p->GetName())) {
6780 newFloats.add(*p->get<RooAbsArg>());
6781 } else if((p->get<RooAbsReal>() && std::abs(_fr->floatParsFinal().getRealValue(p->GetName(), std::numeric_limits<double>::quiet_NaN()) -
6782 p->get<RooAbsReal>()->getVal()) > 1e-15) ||
6783 (p->get<RooAbsCategory>() && p->get<RooAbsCategory>()->getCurrentIndex() !=
6784 _fr->floatParsFinal().getCatIndex(p->GetName(), std::numeric_limits<int>().max()))) {
6785 // value of existing float changed
6786 oldFloats.add(*p->get<RooAbsArg>());
6787 }
6788 }
6789 }
6790 if (!oldFloats.empty() || !newFloats.empty() || !newConsts.empty()) {
6791 // create new fit result using covariance from the fit result
6792 // remove any new consts from the list before extracting covariance matrix
6793 RooArgList existingFloats(_fr->floatParsFinal());
6794 existingFloats.remove(newConsts,true,true/* match name*/);
6795 auto cov = _fr->reducedCovarianceMatrix(existingFloats);
6796 if(!newFloats.empty()) {
6797 // extend the covariance matrix and add variances using current parameter errors
6798 size_t oldSize = existingFloats.size();
6799 cov.ResizeTo(oldSize+newFloats.size(),oldSize+newFloats.size());
6800 for(size_t i=0;i<newFloats.size();i++) {
6801 existingFloats.add(*newFloats.at(i));
6802 auto v = dynamic_cast<RooRealVar*>(newFloats.at(i));
6803 if(v)
6804 cov( oldSize + i, oldSize + i ) = std::pow(v->getError(),2);
6805 }
6806 }
6807 RooArgList existingConsts(_fr->constPars());
6808 existingConsts.remove(newFloats,true,true);
6810
6811 // do we need to add our remaining const pars to the const par list? or the globs?
6812 // for speed we wont bother
6813 // note that generating datasets needs the globs in the const pars list so the robs can be determined
6814 // at the moment this check is done in the generate() method (along with check for missing pars)
6815
6816 auto fr = std::make_shared<RooFitResult>(TString::Format("%s-dirty", _fr->GetName()));
6817 fr->setFinalParList(existingFloats);
6818 fr->setConstParList(existingConsts);
6819 fr->setCovarianceMatrix(cov);
6820 fr->setInitParList(_fr->floatParsInit()); // will only be the pars that were actually float for the fit
6821
6822 return xRooNode(fr, *this);
6823 } else {
6824 // all matching, can return the fit result as-is
6825 return xRooNode(*_fr, std::make_shared<xRooNode>(*_w, std::make_shared<xRooNode>()));
6826 }
6827
6828 }
6829 return xRooNode();
6830 };
6831 for (auto o : _w->allGenericObjects()) {
6832 auto out = checkFr(o);
6833 if (out)
6834 return out;
6835 }
6836 for (auto o : GETWSSNAPSHOTS(_w)) {
6837 auto out = checkFr(o);
6838 if (out)
6839 return out;
6840 }
6841 } else {
6842 // objects not in workspaces are allowed to have a fitResult set in their memory
6843 // use getObject to get it
6844 if (auto fr = getObject<RooFitResult>(".fitResult"); fr) {
6845 return xRooNode(fr, *this);
6846 }
6847 }
6848
6849 // ensure coefs are included if there are any
6850 auto _coefs = coefs();
6851 if (_coefs.get()) {
6852 return xRooNode(RooProduct("tmp", "tmp", RooArgList(*get<RooAbsArg>(), *_coefs.get<RooAbsReal>())))
6853 .fitResult(opt);
6854 }
6855
6856 std::unique_ptr<RooArgList> _pars(dynamic_cast<RooArgList *>(pars().argList().selectByAttrib("Constant", false)));
6857 auto fr = std::make_shared<RooFitResult>(TUUID().AsString());
6858 fr->SetTitle(TString::Format("%s uncorrelated parameter snapshot", GetName()));
6859 fr->setFinalParList(*_pars);
6860 fr->setStatus(-1);
6861
6862 TMatrixDSym cov(fr->floatParsFinal().size());
6863 TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr.get(), _VM));
6864 if (prevCov) {
6865 for (int i = 0; i < prevCov->GetNcols(); i++) {
6866 for (int j = 0; j < prevCov->GetNrows(); j++) {
6867 cov(i, j) = (*prevCov)(i, j);
6868 }
6869 }
6870 }
6871 int i = 0;
6872 for (auto &p : fr->floatParsFinal()) {
6873 if (!prevCov || i >= prevCov->GetNcols()) {
6874 if (auto v = dynamic_cast<RooRealVar *>(p)) {
6875 cov(i, i) = pow(v->getError(), 2);
6876 } else {
6877 cov(i, i) = 0;
6878 }
6879 }
6880 i++;
6881 }
6882 int covQualBackup = fr->covQual();
6883 fr->setCovarianceMatrix(cov);
6884 fr->setCovQual(covQualBackup);
6885
6886 auto _args = consts().argList();
6887 _args.add(pp().argList());
6888 // global obs are added to constPars list too
6889 auto _globs = globs(); // keep alive as may own glob
6890 _args.add(_globs.argList());
6891 fr->setConstParList(_args);
6892 std::unique_ptr<RooArgList> _snap(dynamic_cast<RooArgList *>(_pars->snapshot()));
6893 for (auto &p : *_snap) {
6894 if (auto atr = p->getStringAttribute("initVal"); atr && dynamic_cast<RooRealVar *>(p))
6895 dynamic_cast<RooRealVar *>(p)->setVal(TString(atr).Atof());
6896 }
6897 fr->setInitParList(*_snap);
6898
6899 // return *const_cast<Node2*>(this)->emplace_back(std::make_shared<Node2>(".fitResult",fr,*this));
6900 return xRooNode(fr, *this);
6901}
6902
6903// xRooNode xRooNode::fitTo_(const char* datasetName) const {
6904// try {
6905// return fitTo(datasetName);
6906// } catch(const std::exception& e) {
6907// new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),kMBIconExclamation); // deletes
6908// self on dismiss? return xRooNode();
6909// }
6910// }
6911//
6912// xRooNode xRooNode::fitTo(const char* datasetName) const {
6913// return fitTo(*datasets().at(datasetName));
6914// }
6915
6916void xRooNode::SetRange(const char *range, double low, double high)
6917{
6918 if (!std::isnan(low) && !std::isnan(high) && get<RooRealVar>()) {
6919 if (range && strlen(range)) {
6920 get<RooRealVar>()->setRange(range, low, high);
6921 } else {
6922 get<RooRealVar>()->setRange(low, high);
6923 }
6924 return;
6925 }
6926 if (auto o = get<RooAbsArg>(); o)
6927 o->setStringAttribute("range", range);
6928 // todo: clear the range attribute on all servers
6929 // could make this controlled by a flag but probably easiest to enforce so you must set range
6930 // in children after if you wanted to override
6931}
6932const char *xRooNode::GetRange() const
6933{
6934 std::string &out = fRange;
6935 if (auto o = get<RooAbsArg>(); o && o->getStringAttribute("range"))
6936 out = o->getStringAttribute("range");
6937 auto _parent = fParent;
6938 while (out.empty() && _parent) {
6939 if (auto o = _parent->get<RooAbsArg>(); o && o->getStringAttribute("range"))
6940 out = o->getStringAttribute("range");
6941 _parent = _parent->fParent;
6942 }
6943 return out.c_str();
6944}
6945
6946xRooNLLVar xRooNode::nll(const char *_data, std::initializer_list<RooCmdArg> nllOpts) const
6947{
6948 return nll(xRooNode(_data), nllOpts);
6949}
6950
6952{
6953 return nll(_data, *xRooFit::createNLLOptions());
6954}
6955
6956xRooNLLVar xRooNode::nll(const xRooNode &_data, std::initializer_list<RooCmdArg> nllOpts) const
6957{
6958 auto defaultOpts = xRooFit::createNLLOptions(); // smart pointer will cleanup the list
6959 // add user-specified options to list ... if already existing in default list, override and warn
6961 for (auto opt : *defaultOpts) {
6962 l.Add(opt);
6963 }
6964 for (auto &i : nllOpts) {
6965 if (auto o = l.FindObject(i.GetName())) {
6966 Info("nll", "Overriding NLL Option: %s", o->GetName());
6967 l.Remove(o);
6968 }
6969 l.Add(const_cast<RooCmdArg *>(&i));
6970 }
6971
6972 return nll(_data, l);
6973}
6974
6976{
6977 if (auto mc = get<RooStats::ModelConfig>()) {
6978 return xRooNode(*mc->GetPdf(), fParent).generate(fr, expected, seed);
6979 }
6980
6981 if (!get<RooAbsPdf>()) {
6982 // before giving up, if this is a workspace we can proceed if we only have one model
6983 if (get<RooWorkspace>()) {
6984 std::shared_ptr<xRooNode> mainModel;
6985 for (auto &c : const_cast<xRooNode *>(this)->browse()) {
6986 if (c->get<RooAbsPdf>()) {
6987 if (!mainModel) {
6988 mainModel = c;
6989 } else {
6990 throw std::runtime_error(TString::Format("Workspace has multiple models, you must specify which to "
6991 "generate with (found at least %s and %s)",
6992 mainModel->GetName(), c->GetName()));
6993 }
6994 }
6995 }
6996 if (mainModel)
6997 return mainModel->generate(fr, expected, seed);
6998 }
6999 throw std::runtime_error(TString::Format("%s is not a pdf", GetName()));
7000 }
7001
7002 // when generating, will only include channels that are selected
7003 // any unselected but not hidden channel will have data from the only selected dataset added to it
7004 if (get<RooSimultaneous>()) {
7005 std::string selected;
7006 std::string fromds; // list of channels to take from selected ds
7007 bool hasDeselected = false;
7008 for (auto c : bins()) {
7009 TString cName(c->GetName());
7010 cName = cName(cName.Index('=') + 1, cName.Length());
7011 if (!c->get<RooAbsReal>()->isSelectedComp()) {
7012 hasDeselected = true;
7013 if (!c->get<RooAbsArg>()->getAttribute("hidden")) {
7014 if (!fromds.empty())
7015 fromds += ",";
7016 fromds += cName.Data();
7017 }
7018 } else {
7019 if (!selected.empty())
7020 selected += ",";
7021 selected += cName.Data();
7022 }
7023 }
7024 if (hasDeselected) {
7025 std::string dsetName = "";
7026 if (!fromds.empty()) {
7027 // use the first selected dataset as protodata
7028 auto _dsets = datasets();
7029 for (auto &d : _dsets) {
7030 if (d->get()->TestBit(1 << 20)) {
7031 dsetName = d->get()->GetName();
7032 break;
7033 }
7034 }
7035 if (dsetName.empty()) {
7036 throw std::runtime_error(
7037 "Need at least one dataset selected (SetChecked) to use for deselected regions");
7038 }
7039 }
7040 auto result = reduced(selected).generate(fr, expected, seed);
7041 if (!fromds.empty()) {
7042 auto ds = reduced(fromds).datasets()[dsetName];
7043 result.Add(*ds);
7044 result.SetName(TString(result.GetName()) + "_and_" + dsetName.c_str());
7045 }
7046 return result;
7047 }
7048 }
7049
7050 auto _fr = fr.get<RooFitResult>();
7051 xRooNode fr2;
7052 if(!_fr) {
7053 fr2 = fitResult();
7054 _fr = fr2.get<RooFitResult>();
7055 }
7056
7057 // must ensure fr has all the globs in its constPars list ... any missing must be added
7058 // otherwise generateFrom method wont determine globs properly
7059 // same for any missing pars
7060 auto _globs = globs();
7061 bool missingGlobs(false);
7062 for(auto glob : _globs) {
7063 if(!_fr->constPars().find(*glob->get<RooAbsArg>())) {
7064 missingGlobs = true; break;
7065 }
7066 }
7067
7068 std::unique_ptr<RooFitResult> newFr;
7069 if (missingGlobs) {
7070 newFr = std::make_unique<RooFitResult>(*_fr);
7071 for(auto glob : _globs) {
7072 if(!newFr->constPars().find(*glob->get<RooAbsArg>())) {
7073 const_cast<RooArgList&>(newFr->constPars()).addClone(*glob->get<RooAbsArg>());
7074 }
7075 }
7076 _fr = newFr.get();
7077 }
7078
7079 // check for missing fundamental pars (consts are not fundamentals)
7080 auto _pars = pars();
7081 bool missingPars(false);
7082 for(auto par : _pars) {
7083 if(!par->get<RooAbsArg>()->isFundamental()) continue;
7084 if(!_fr->constPars().find(*par->get<RooAbsArg>()) && !_fr->floatParsFinal().find(*par->get<RooAbsArg>())) {
7085 missingPars = true; break;
7086 }
7087 }
7088
7089 if(missingPars) {
7090 newFr = std::make_unique<RooFitResult>(*_fr);
7091 for(auto par : _pars) {
7092 if(!par->get<RooAbsArg>()->isFundamental()) continue;
7093 if(!newFr->constPars().find(*par->get<RooAbsArg>()) && !newFr->floatParsFinal().find(*par->get<RooAbsArg>())) {
7094 const_cast<RooArgList&>(newFr->constPars()).addClone(*par->get<RooAbsArg>());
7095 }
7096 }
7097 _fr = newFr.get();
7098 }
7099
7100
7101
7102 return xRooNode(
7104 *this);
7105
7106 // should add coords to the dataset too?
7107 // e.g. in the case of generating a dataset for a single channel, include the channelCat
7108 // this will allow datasets to then be combined.
7109 // could just say users must use 'reduced' on the simPdf, even if reducing to a single channel
7110
7111}
7112
7113xRooNLLVar xRooNode::nll(const xRooNode &_data, const RooLinkedList &opts) const
7114{
7115 if (auto mc = get<RooStats::ModelConfig>()) {
7116 if (mc->GetExternalConstraints()) {
7118 for (auto o : opts) {
7119 optsWithConstraints.Add(o->Clone(nullptr));
7120 }
7121 optsWithConstraints.Add(RooFit::ExternalConstraints(*mc->GetExternalConstraints()).Clone(nullptr));
7122 return xRooNode(*mc->GetPdf(), fParent).nll(_data, optsWithConstraints);
7123 } else {
7124 return xRooNode(*mc->GetPdf(), fParent).nll(_data, opts);
7125 }
7126 }
7127
7128 if (!get<RooAbsPdf>()) {
7129 // before giving up, if this is a workspace we can proceed if we only have one model or pdf
7130 if (get<RooWorkspace>()) {
7131 std::shared_ptr<xRooNode> mainPdf, mainModel, otherPdf;
7132 for (auto &c : const_cast<xRooNode *>(this)->browse()) {
7133 if (c->get<RooAbsPdf>()) {
7134 if (!mainPdf) {
7135 mainPdf = c;
7136 } else {
7137 otherPdf = c;
7138 }
7139 } else if (c->get<RooStats::ModelConfig>()) {
7140 if (!mainModel) {
7141 mainModel = c;
7142 } else {
7143 throw std::runtime_error(TString::Format("Workspace has multiple models, you must specify which to "
7144 "build nll with (found at least %s and %s)",
7145 mainModel->GetName(), c->GetName()));
7146 }
7147 }
7148 }
7149 if (mainModel)
7150 return mainModel->nll(_data, opts);
7151 if (mainPdf) {
7152 if (otherPdf) {
7153 throw std::runtime_error(TString::Format("Workspace has multiple pdfs, you must specify which to "
7154 "build nll with (found at least %s and %s)",
7155 mainPdf->GetName(), otherPdf->GetName()));
7156 }
7157 return mainPdf->nll(_data, opts);
7158 }
7159 }
7160 throw std::runtime_error(TString::Format("%s is not a pdf", GetName()));
7161 }
7162
7163 // if simultaneous and any channels deselected then reduce and return
7164 if (get<RooSimultaneous>()) {
7165 std::string selected;
7166 bool hasDeselected = false;
7167 for (auto c : bins()) {
7168 if (!c->get<RooAbsReal>()->isSelectedComp()) {
7169 hasDeselected = true;
7170 } else {
7171 TString cName(c->GetName());
7172 cName = cName(cName.Index('=') + 1, cName.Length());
7173 if (!selected.empty())
7174 selected += ",";
7175 selected += cName.Data();
7176 }
7177 }
7178 if (hasDeselected)
7179 return reduced(selected).nll(_data, opts);
7180 }
7181
7182 if (!_data.get<RooAbsData>()) {
7183 // use node name to find dataset and recall
7184 auto _d = strlen(_data.GetName()) ? datasets().find(_data.GetName()) : nullptr;
7185 if (strlen(_data.GetName()) == 0) {
7186 // create the EXPECTED (asimov) dataset with the observables
7188 _d = std::make_shared<xRooNode>(asi.first, *this);
7189 if (asi.second) {
7190 _d->emplace_back(
7191 std::make_shared<xRooNode>(".globs", std::const_pointer_cast<RooAbsCollection>(asi.second), *_d));
7192 }
7193 } else if (!_d) {
7194 throw std::runtime_error(TString::Format("Cannot find dataset %s", _data.GetName()));
7195 }
7196 return nll(*_d, opts);
7197 } else if (!_data.fParent || _data.fParent->fComp != fComp) {
7198 // dataset is not parented by this node ... meaning it may need to be reduced,
7199 // do this via the datasets() method by attaching and detaching the dataset
7200 xRooNode me(*this); // since we are in a const method, need to create a copy node.
7201 me.push_back(std::make_shared<xRooNode>(_data));
7202 return nll(*me.datasets().at(_data.GetName()), opts);
7203 }
7204
7205 auto _globs = _data.globs(); // keep alive because may own the globs
7206
7207 auto _opts = std::shared_ptr<RooLinkedList>(new RooLinkedList, [](RooLinkedList *l) {
7208 if (l)
7209 l->Delete();
7210 delete l;
7211 });
7212 RooArgSet _globsSet(_globs.argList());
7214 if (GetRange() && strlen(GetRange()))
7215 _opts->Add(RooFit::Range(GetRange()).Clone());
7216
7217 // copy over opts ... need to clone each so can safely delete when _opts destroyed
7218 for (int i = 0; i < opts.GetSize(); i++) {
7219 if (strlen(opts.At(i)->GetName()) == 0)
7220 continue; // skipping "none" cmds
7221 if (strcmp(opts.At(i)->GetName(), "GlobalObservables") == 0) {
7222 // maybe warn here?
7223 } else {
7224 _opts->Add(opts.At(i)->Clone(nullptr)); // nullptr needed because accessing Clone via TObject base class puts
7225 // "" instead, so doesnt copy names
7226 }
7227 }
7228
7229 // use shared_ptr method so NLLVar will take ownership of datasets etc if created above
7230 // snapshots the globs out of the nllOpts (see specific constructor of xRooNLLVar)
7232 std::dynamic_pointer_cast<RooAbsData>(_data.fComp), *_opts);
7234}
7235
7236// xRooNode xRooNode::fitTo(const xRooNode& _data) const {
7237//
7238//
7239// auto _pdf = get<RooAbsPdf>();
7240// if (!_pdf) throw std::runtime_error("Not a pdf");
7241//
7242// auto _globs = _data.globs(); // keep alive because may own the globs
7243// RooArgSet globsSet(_globs.argList());
7244//
7245// std::shared_ptr<RooSimultaneous> newPdf;
7246// if(auto s = get<RooSimultaneous>(); s) {
7247// auto rangeName = GetRange();
7248// if (rangeName) {
7249// // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
7250// std::vector<TString> chanPatterns;
7251// TStringToken pattern(rangeName, ",");
7252// while (pattern.NextToken()) {
7253// chanPatterns.emplace_back(pattern);
7254// }
7255// auto& _cat = const_cast<RooAbsCategoryLValue&>(s->indexCat());
7256// newPdf = std::make_shared<RooSimultaneous>(TString::Format("%s_reduced",GetName()),"Reduced model",_cat);
7257// for(auto& c : variations()) {
7258// TString cName(c->GetName());
7259// cName = cName(cName.Index('=')+1,cName.Length());
7260// _cat.setLabel(cName);
7261// bool matchAny=false;
7262// for(auto& p : chanPatterns) {
7263// if (cName.Contains(TRegexp(p,true))) { matchAny=true; break; }
7264// if (_cat.hasRange(p) && _cat.inRange(p)) { matchAny=true; break; }
7265// }
7266// if(matchAny) {
7267// newPdf->addPdf( *c->get<RooAbsPdf>(), cName );
7268// }
7269// }
7270// RooFitResultTree t(newPdf->GetName(),"",*newPdf);
7271// auto _fr = std::const_pointer_cast<RooFitResult>(t.fitTo(_data.get<RooAbsData>(), &globsSet));
7272// xRooNode parent(_data.GetName(),nullptr,*this);
7273// xRooNode out(_fr->GetName(),/*acquire(_fr)*/ _fr,parent);
7274// // do full propagation by 'checking' the fr ...
7275// out.Checked(&out,true);
7276// return out;
7277// }
7278// }
7279//
7280//
7281//
7282// std::string treeName = TString::Format("fits_%s",GetName()).Data();
7283//
7284// auto _frt = getObject<TTree>(treeName); // get existing frt
7285//
7286// std::shared_ptr<RooFitResultTree> t;
7287// if (_frt) {
7288// t = std::make_shared<RooFitResultTree>(_frt.get());
7289// } else {
7290// t = std::make_shared<RooFitResultTree>(treeName.c_str(),"",*_pdf);
7291// }
7292// //t->SetProgress(true);
7293// auto _fr = std::const_pointer_cast<RooFitResult>(t->fitTo(_data.get<RooAbsData>(), &globsSet));
7294//
7295//
7296//
7297// /*
7298// obs().argList() = s; // sets global observables to their values
7299// auto _fr =
7300// std::shared_ptr<RooFitResult>(_pdf->fitTo(*_data->get<RooAbsData>(),RooFit::GlobalObservables(s),RooFit::Offset(true),RooFit::Save()));
7301// _fr->SetName(TUUID().AsString());
7302// // restore parameters before returning
7303// *std::unique_ptr<RooArgSet>(_pdf->getDependents(_fr->floatParsFinal())) = _fr->floatParsInit();
7304// */
7305//
7306// //_fr->SetTitle(TString::Format("%s;%s",GetName(),datasetName));
7307// if (!_frt) {
7308// t =
7309// std::make_shared<RooFitResultTree>(std::dynamic_pointer_cast<TTree>(const_cast<xRooNode*>(this)->acquire(t->fTree)).get());
7310// }
7311// xRooNode parent(_data.GetName(),nullptr,xRooNode(t,*this));
7312// xRooNode out(_fr->GetName(),/*acquire(_fr)*/ _fr,parent);
7313// // do full propagation by 'checking' the fr ...
7314// out.Checked(&out,true);
7315// return out;
7316// }
7317
7318std::shared_ptr<xRooNode> xRooNode::parentPdf() const
7319{
7320 // find first parent that is a pdf
7321 auto out = fParent;
7322 while (out && !out->get<RooAbsPdf>()) {
7323 out = out->fParent;
7324 }
7325 return out;
7326}
7327
7328xRooNode xRooNode::reduced(const std::string &_range, bool invert) const
7329{
7330 auto rangeName = (_range.empty()) ? GetRange() : _range;
7331 if (!rangeName.empty()) {
7332 std::vector<TString> patterns;
7333 TStringToken pattern(rangeName, ",");
7334 while (pattern.NextToken()) {
7335 patterns.emplace_back(pattern);
7336 }
7337 if (auto s = get<RooSimultaneous>(); s) {
7338 // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
7339 auto &_cat = const_cast<RooAbsCategoryLValue &>(s->indexCat());
7340 auto newPdf =
7341 std::make_shared<RooSimultaneous>(TString::Format("%s_reduced", GetName()), "Reduced model", _cat);
7342 for (auto &c : bins()) {
7343 TString cName(c->GetName());
7344 cName = cName(cName.Index('=') + 1, cName.Length());
7345 _cat.setLabel(cName);
7346 bool matchAny = false;
7347 for (auto &p : patterns) {
7348 if (cName.Contains(TRegexp(p, true))) {
7349 matchAny = true;
7350 break;
7351 }
7352 if (_cat.hasRange(p) && _cat.inRange(p)) {
7353 matchAny = true;
7354 break;
7355 }
7356 }
7357 if ((matchAny && !invert) || (!matchAny && invert)) {
7358 newPdf->addPdf(*c->get<RooAbsPdf>(), cName);
7359 }
7360 }
7361 return xRooNode(newPdf, fParent);
7362 } else if (get() && !get<RooAbsCollection>() && !components().empty()) {
7363 // create a new obj and remove non-matching components
7364 xRooNode out(std::shared_ptr<TObject>(get()->Clone(TString::Format("%s_reduced", get()->GetName()))), fParent);
7365 // go through components and remove any that don't match pattern
7366 std::vector<TObject *> funcs; // to be removed
7367 for (auto &c : out.components()) {
7368 bool matchAny = false;
7369 for (auto &p : patterns) {
7370 if (TString(c->GetName()).Contains(TRegexp(p, true))) {
7371 matchAny = true;
7372 break;
7373 }
7374 }
7375 if (!((matchAny && !invert) || (!matchAny && invert)))
7376 funcs.push_back(c->get());
7377 }
7378 for (auto &c : funcs)
7379 out.Remove(*c);
7380 if (!funcs.empty()) {
7381 if (auto _pdf = out.get<RooRealSumPdf>(); _pdf) {
7382 _pdf->setFloor(false); // remove floor if removed some functions, which allows evaluation of negative
7383 // valued components
7384 }
7385 }
7386 out.browse();
7387 return out;
7388 } else if (auto fr = get<RooFitResult>()) {
7389 // reduce the fit result by moving unselected float pars into the constPars list and dropping their covariances
7390 xRooNode out(std::shared_ptr<TObject>(fr->Clone(TString::Format("%s_reduced", fr->GetName()))), fParent);
7391 fr = out.get<RooFitResult>();
7392 RooArgList _pars = fr->floatParsFinal();
7394 for (auto c : _pars) {
7395 bool matchAny = false;
7396 for (auto &p : patterns) {
7397 if (TString(c->GetName()).Contains(TRegexp(p, true))) {
7398 matchAny = true;
7399 break;
7400 }
7401 }
7402 if (!((matchAny && !invert) || (!matchAny && invert))) {
7403 _remPars.add(*c);
7404 }
7405 }
7406 _pars.remove(_remPars, true);
7407
7408 auto _tmp = fr->reducedCovarianceMatrix(_pars);
7409 int covQualBackup = fr->covQual();
7410 fr->setCovarianceMatrix(_tmp);
7411 fr->setCovQual(covQualBackup);
7412 const_cast<RooArgList &>(fr->floatParsFinal())
7413 .remove(_remPars, true); // is this a memory leak ... should delete the remPars?
7414 return out;
7415
7416 } else if (!get() || get<RooAbsCollection>()) {
7417 // filter the children .... handle special case of filtering ".vars" with "x" option too
7418 xRooNode out(std::make_shared<RooArgList>(), fParent);
7419 out.SetName(TString(GetName()) + "_reduced");
7420 size_t nobs = 0;
7421 bool notAllArgs = false;
7422 bool isVars = (strcmp(GetName(), ".vars") == 0);
7423 for (auto c : *this) {
7424 nobs += (c->fFolder == "!robs" || c->fFolder == "!globs");
7425 bool matchAny = false;
7426 for (auto &p : patterns) {
7427 if (TString(c->GetName()).Contains(TRegexp(p, true)) ||
7428 (isVars && p == "x" && (c->fFolder == "!robs" || c->fFolder == "!globs") && nobs == 1)) {
7429 matchAny = true;
7430 break;
7431 }
7432 }
7433 if ((matchAny && !invert) || (!matchAny && invert)) {
7434 out.push_back(c);
7435 if (auto a = c->get<RooAbsArg>()) {
7436 out.get<RooArgList>()->add(*a);
7437 } else {
7438 notAllArgs = true;
7439 }
7440 }
7441 }
7442 if (notAllArgs) {
7443 out.fComp.reset();
7444 }
7445 return out;
7446 }
7447 }
7448
7449 return get<RooArgList>() ? xRooNode(std::make_shared<RooArgList>(), fParent) : *this;
7450}
7451
7452// xRooNode xRooNode::generate(bool expected) const {
7453//
7454// auto fr = fitResult();
7455// auto _fr = fr.get<RooFitResult>();
7456//
7457// auto _pdf = (get<RooAbsPdf>()) ? std::shared_ptr<const xRooNode>(this, [](const xRooNode*){}) : parentPdf();
7458// if (!_pdf) {
7459// throw std::runtime_error("Could not find pdf");
7460// }
7461//
7462// std::shared_ptr<RooDataTree> t;
7463//
7464// std::shared_ptr<RooSimultaneous> newPdf;
7465// if(auto s = _pdf->get<RooSimultaneous>(); s) {
7466// auto rangeName = GetRange();
7467// if (rangeName) {
7468// // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
7469// std::vector<TString> chanPatterns;
7470// TStringToken pattern(rangeName, ",");
7471// while (pattern.NextToken()) {
7472// chanPatterns.emplace_back(pattern);
7473// }
7474// auto& _cat = const_cast<RooAbsCategoryLValue&>(s->indexCat());
7475// newPdf = std::make_shared<RooSimultaneous>(TString::Format("%s_reduced",GetName()),"Reduced model",_cat);
7476// for(auto& c : _pdf->variations()) {
7477// TString cName(c->GetName());
7478// cName = cName(cName.Index('=')+1,cName.Length());
7479// _cat.setLabel(cName);
7480// bool matchAny=false;
7481// for(auto& p : chanPatterns) {
7482// if (cName.Contains(TRegexp(p,true))) { matchAny=true; break; }
7483// if (_cat.hasRange(p) && _cat.inRange(p)) { matchAny=true; break; }
7484// }
7485// if(matchAny) {
7486// newPdf->addPdf( *c->get<RooAbsPdf>(), cName );
7487// }
7488// }
7489// t = std::make_shared<RooDataTree>(newPdf->GetName(),"",*newPdf);
7490// RooArgSet s1(_pdf->obs().argList());
7491// RooArgSet s2(_pdf->globs().argList());s1.remove(s2);
7492// t->SetObservables(&s1,&s2);
7493// auto _data = t->generate(_fr,expected);
7494//
7495// xRooNode parent(_fr ? _fr->GetName() : "unknown",nullptr,xRooNode(t,*this));
7496// xRooNode out(_data.first->GetName(),/*acquire(_fr)*/ _data.first,parent);
7497// out.emplace_back(std::make_shared<xRooNode>(".globs",std::const_pointer_cast<RooArgSet>(_data.second),out));
7498// return out;
7499// }
7500// }
7501//
7502//
7503// std::string treeName = TString::Format("gen_%s",_pdf->GetName()).Data();
7504//
7505// auto _frt = getObject<TTree>(treeName); // get existing frt
7506//
7507//
7508// if (_frt) {
7509// t = std::make_shared<RooDataTree>(_frt.get());
7510// } else {
7511// t = std::make_shared<RooDataTree>(treeName.c_str(),"",*_pdf->get<RooAbsPdf>());
7512// RooArgSet s1(_pdf->obs().argList());
7513// RooArgSet s2(_pdf->globs().argList());s1.remove(s2);
7514// t->SetObservables(&s1,&s2);
7515// }
7516// auto _data = t->generate(_fr,expected);
7517// if (!_frt) {
7518// t =
7519// std::make_shared<RooDataTree>(std::dynamic_pointer_cast<TTree>(const_cast<xRooNode*>(this)->acquire(t->fTree)).get());
7520// }
7521// xRooNode parent(_fr ? _fr->GetName() : "unknown",nullptr,xRooNode(t,*this));
7522// xRooNode out(_data.first->GetName(),/*acquire(_fr)*/ _data.first,parent);
7523// out.emplace_back(std::make_shared<xRooNode>(".globs",std::const_pointer_cast<RooArgSet>(_data.second),out));
7524// return out;
7525// }
7526
7528public:
7530 double expectedEvents(const RooArgSet *nset) const override
7531 {
7532 return static_cast<RooAbsPdf *>(intpdf.absArg())->expectedEvents(nset);
7533 }
7534 ExtendMode extendMode() const override { return static_cast<RooAbsPdf *>(intpdf.absArg())->extendMode(); }
7535 TObject *clone(const char *newname) const override { return new xRooProjectedPdf(*this, newname); }
7536
7537protected:
7538 double evaluate() const override
7539 {
7540 int code;
7541 return getProjection(&intobs, _normSet, (_normRange.Length() > 0 ? _normRange.Data() : nullptr), code)->getVal();
7542 }
7543};
7544
7545double new_getPropagatedError(const RooAbsReal &f, const RooFitResult &fr, const RooArgSet &nset = {},
7546 RooArgList **pars = nullptr, bool asymHi = false, bool asymLo = false)
7547{
7548 // Calling getParameters() might be costly, but necessary to get the right
7549 // parameters in the RooAbsReal. The RooFitResult only stores snapshots.
7550
7551 // handle simple case that function is a RooRealVar
7552 if (auto rrv = dynamic_cast<const RooRealVar *>(&f); rrv) {
7553 if (auto frrrv = dynamic_cast<RooRealVar *>(fr.floatParsFinal().find(*rrv)); frrrv) {
7554 rrv = frrrv; // use value from fit result
7555 }
7556 if (asymHi) {
7557 return rrv->getErrorHi();
7558 } else if (asymLo) {
7559 return rrv->getErrorLo();
7560 } else {
7561 return rrv->getError();
7562 }
7563 }
7564
7565 RooArgList *_pars = (pars) ? *pars : nullptr;
7566
7567 if (!_pars) {
7568
7570 f.getParameters(&nset, allParamsInAbsReal);
7571
7572 _pars = new RooArgList;
7573 for (auto *rrvFitRes : static_range_cast<RooRealVar *>(fr.floatParsFinal())) {
7574
7575 auto rrvInAbsReal = static_cast<RooRealVar const *>(allParamsInAbsReal.find(*rrvFitRes));
7576
7577 // Strip out parameters with zero error
7578 if (rrvFitRes->getError() <= std::abs(rrvFitRes->getVal()) * std::numeric_limits<double>::epsilon())
7579 continue;
7580
7581 // Ignore parameters in the fit result that this RooAbsReal doesn't depend on
7582 if (!rrvInAbsReal)
7583 continue;
7584
7585 // Checking for float equality is a bad. We check if the values are
7586 // negligibly far away from each other, relative to the uncertainty.
7587 if (std::abs(rrvInAbsReal->getVal() - rrvFitRes->getVal()) > 0.01 * rrvFitRes->getError()) {
7588 std::stringstream errMsg;
7589 errMsg << "RooAbsReal::getPropagatedError(): the parameters of the RooAbsReal don't have"
7590 << " the same values as in the fit result! The logic of getPropagatedError is broken in this case."
7591 << " \n " << rrvInAbsReal->GetName() << " : " << rrvInAbsReal->getVal() << " vs " << rrvFitRes->getVal();
7592
7593 throw std::runtime_error(errMsg.str());
7594 }
7595
7596 _pars->add(*rrvInAbsReal);
7597 }
7598 }
7599
7600 // Make std::vector of variations
7601 TVectorD F(_pars->size());
7602
7603 // Create std::vector of plus,minus variations for each parameter
7604 TMatrixDSym V(_pars->size() == fr.floatParsFinal().size() ? fr.covarianceMatrix()
7605 : fr.reducedCovarianceMatrix(*_pars));
7606
7607 // TODO: if _pars includes pars not in fr, need to extend matrix with uncorrelated errors of those pars
7608
7609 double nomVal = f.getVal(nset);
7610
7611 for (std::size_t ivar = 0; ivar < _pars->size(); ivar++) {
7612
7613 auto &rrv = static_cast<RooRealVar &>((*_pars)[ivar]);
7614 auto *frrrv = static_cast<RooRealVar *>(fr.floatParsFinal().find(rrv));
7615
7616 double cenVal = rrv.getVal();
7617 double plusVar, minusVar, errVal;
7618
7619 if (asymHi || asymLo) {
7620 errVal = frrrv->getErrorHi();
7621 rrv.setVal(cenVal + errVal);
7622 plusVar = f.getVal(nset);
7623 errVal = frrrv->getErrorLo();
7624 rrv.setVal(cenVal + errVal);
7625 minusVar = f.getVal(nset);
7626 if (asymHi) {
7627 // pick the one that moved result 'up' most
7628 plusVar = std::max(plusVar, minusVar);
7629 minusVar = 2 * nomVal - plusVar; // symmetrizes
7630 } else {
7631 // pick the one that moved result 'down' most
7632 minusVar = std::min(plusVar, minusVar);
7633 plusVar = 2 * nomVal - minusVar; // symmetrizes
7634 }
7635 } else {
7636 errVal = sqrt(V(ivar, ivar));
7637 // Make Plus variation
7638 rrv.setVal(cenVal + errVal);
7639 plusVar = f.getVal(nset);
7640 // Make Minus variation
7641 rrv.setVal(cenVal - errVal);
7642 minusVar = f.getVal(nset);
7643 }
7644 F[ivar] = (plusVar - minusVar) * 0.5;
7645 rrv.setVal(cenVal);
7646 }
7647
7648 // Re-evaluate this RooAbsReal with the central parameters just to be
7649 // extra-safe that a call to `getPropagatedError()` doesn't change any state.
7650 // It should not be necessary because thanks to the dirty flag propagation
7651 // the RooAbsReal is re-evaluated anyway the next time getVal() is called.
7652 // Still there are imaginable corner cases where it would not be triggered,
7653 // for example if the user changes the RooFit operation more after the error
7654 // propagation.
7655 f.getVal(nset);
7656
7657 TMatrixDSym C(_pars->size());
7658 std::vector<double> errVec(_pars->size());
7659 for (std::size_t i = 0; i < _pars->size(); i++) {
7660 errVec[i] = std::sqrt(V(i, i));
7661 for (std::size_t j = i; j < _pars->size(); j++) {
7662 C(i, j) = V(i, j) / std::sqrt(V(i, i) * V(j, j));
7663 C(j, i) = C(i, j);
7664 }
7665 }
7666
7667 // Calculate error in linear approximation from variations and correlation coefficient
7668 double sum = F * (C * F);
7669
7670 if (!pars) {
7671 delete _pars;
7672 } else {
7673 *pars = _pars;
7674 }
7675
7676 return sqrt(sum);
7677}
7678
7679class PdfWrapper : public RooAbsPdf {
7680public:
7681 // need expPdf option while RooProjectedPdf doesn't support keeping things extended
7682 PdfWrapper(RooAbsReal &f, RooAbsReal *coef, bool expEvMode = false, RooAbsPdf *expPdf = nullptr)
7683 : RooAbsPdf(Form("exp_%s", f.GetName())),
7684 fFunc("func", "func", this, f),
7685 fCoef("coef", "coef", this),
7686 fExpPdf("expPdf", "expPdf", this)
7687 {
7688 // don't treat pdf as extended if it has a coefficient and is RooAddPdf: RooAddPdf doesn't extend them unless no
7689 // coefs for any (and all are extendable)
7690 if (coef) {
7691 fCoef.setArg(*coef);
7692 }
7693 if (expPdf && expPdf->canBeExtended() && !(coef && dynamic_cast<RooAddPdf *>(expPdf))) {
7694 fExpPdf.setArg(*expPdf);
7695 } else if (auto _p = dynamic_cast<RooAbsPdf *>(&f);
7696 _p && _p->canBeExtended() && !(coef && dynamic_cast<RooAddPdf *>(_p))) {
7697 fExpPdf.setArg(f); // using self for expectation
7698 }
7699 fExpectedEventsMode = expEvMode;
7700 }
7701 ~PdfWrapper() override{};
7702 PdfWrapper(const PdfWrapper &other, const char *name = nullptr)
7703 : RooAbsPdf(other, name),
7704 fFunc("func", this, other.fFunc),
7705 fCoef("coef", this, other.fCoef),
7706 fExpPdf("expPdf", this, other.fExpPdf),
7707 fExpectedEventsMode(other.fExpectedEventsMode)
7708 {
7709 }
7710 TObject *clone(const char *newname) const override { return new PdfWrapper(*this, newname); }
7711 bool isBinnedDistribution(const RooArgSet &obs) const override { return fFunc->isBinnedDistribution(obs); }
7712 std::list<double> *binBoundaries(RooAbsRealLValue &obs, double xlo, double xhi) const override
7713 {
7714 return fFunc->binBoundaries(obs, xlo, xhi);
7715 }
7716
7717 double evaluate() const override
7718 {
7719 return (fExpectedEventsMode ? 1. : fFunc) *
7720 ((fExpPdf.absArg()) ? static_cast<RooAbsPdf *>(fExpPdf.absArg())->expectedEvents(_normSet) : 1.) *
7721 (fCoef.absArg() ? fCoef : 1.);
7722 }
7723
7724 bool selfNormalized() const override
7725 {
7726 return true;
7727 } // so that doesn't try to do an integral because we are passing integration onto fFunc in evaluate
7728
7729 // faster than full evaluation because doesnt make the integral dependent on the full expression
7731 {
7732#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 28, 00)
7733 double oo = getPropagatedError(fr, nset_in); // method was improved in 6.28 so use this instead
7734 if (std::isnan(oo)) {
7735 // may be consequence of zero uncerts
7736 // Calling getParameters() might be costly, but necessary to get the right
7737 // parameters in the RooAbsReal. The RooFitResult only stores snapshots.
7739 getParameters(&nset_in, allParamsInAbsReal);
7740
7741 RooArgList paramList;
7743
7744 auto rrvInAbsReal = static_cast<RooRealVar const *>(allParamsInAbsReal.find(*rrvFitRes));
7745
7746 // If this RooAbsReal is a RooRealVar in the fit result, we don't need to
7747 // propagate anything and can just return the error in the fit result
7748 if (rrvFitRes->namePtr() == namePtr())
7749 return rrvFitRes->getError();
7750
7751 // Strip out parameters with zero error
7752 if (!rrvFitRes->hasError() ||
7753 rrvFitRes->getError() <= std::abs(rrvFitRes->getVal()) * std::numeric_limits<double>::epsilon())
7754 continue;
7755
7756 // Ignore parameters in the fit result that this RooAbsReal doesn't depend on
7757 if (!rrvInAbsReal)
7758 continue;
7759
7760 // Checking for float equality is a bad. We check if the values are
7761 // negligibly far away from each other, relative to the uncertainty.
7762 if (std::abs(rrvInAbsReal->getVal() - rrvFitRes->getVal()) > 0.01 * rrvFitRes->getError()) {
7763 std::stringstream errMsg;
7764 errMsg
7765 << "RooAbsReal::getPropagatedError(): the parameters of the RooAbsReal don't have"
7766 << " the same values as in the fit result! The logic of getPropagatedError is broken in this case.";
7767
7768 throw std::runtime_error(errMsg.str());
7769 }
7770
7771 paramList.add(*rrvInAbsReal);
7772 }
7773 if (paramList.empty())
7774 return 0.;
7775
7776 std::vector<double> plusVar;
7777 std::vector<double> minusVar;
7778 plusVar.reserve(paramList.size());
7779 minusVar.reserve(paramList.size());
7780
7781 // Create std::vector of plus,minus variations for each parameter
7782 TMatrixDSym V(paramList.size() == fr.floatParsFinal().size() ? fr.covarianceMatrix()
7783 : fr.reducedCovarianceMatrix(paramList));
7784
7785 for (std::size_t ivar = 0; ivar < paramList.size(); ivar++) {
7786
7787 auto &rrv = static_cast<RooRealVar &>(paramList[ivar]);
7788
7789 double cenVal = rrv.getVal();
7790 double errVal = sqrt(V(ivar, ivar));
7791
7792 // this next thing happens if the par has errors but the covariance matrix is empty
7793 // this only happens if the fit was dodgy, so perhaps best to not even try to recover from this
7794 // screwup ... hence I've commented out this fixup here and will let the errors be nan
7795 // if(errVal==0) {
7796 // Warning("getPropagatedError","Missing variance for %s",rrv.GetName());
7797 // errVal = rrv.getError();
7798 // V(ivar,ivar) = errVal*errVal;
7799 // }
7800
7801 // Make Plus variation
7802 rrv.setVal(cenVal + errVal);
7803 plusVar.push_back(getVal(nset_in));
7804
7805 // Make Minus variation
7806 rrv.setVal(cenVal - errVal);
7807 minusVar.push_back(getVal(nset_in));
7808#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
7809 // can try to recover nans ... this stopped being possible in 6.27 onwards because NaNPacker made private
7810 if (std::isnan(plusVar.back()) && RooNaNPacker::isNaNWithPayload(plusVar.back())) {
7811 plusVar.back() = -RooNaNPacker::unpackNaN(plusVar.back());
7812 }
7813 if (std::isnan(minusVar.back()) && RooNaNPacker::isNaNWithPayload(minusVar.back())) {
7814 minusVar.back() = -RooNaNPacker::unpackNaN(minusVar.back());
7815 }
7816#endif
7817 // std::cout << plusVar.back() << " and " << minusVar.back() << std::endl;
7818
7819 rrv.setVal(cenVal);
7820 }
7821
7822 // Re-evaluate this RooAbsReal with the central parameters just to be
7823 // extra-safe that a call to `getPropagatedError()` doesn't change any state.
7824 // It should not be necessary because thanks to the dirty flag propagation
7825 // the RooAbsReal is re-evaluated anyway the next time getVal() is called.
7826 // Still there are imaginable corner cases where it would not be triggered,
7827 // for example if the user changes the RooFit operation more after the error
7828 // propagation.
7829 getVal(nset_in);
7830
7831 TMatrixDSym C(paramList.size());
7832 std::vector<double> errVec(paramList.size());
7833 for (std::size_t i = 0; i < paramList.size(); i++) {
7834 errVec[i] = std::sqrt(V(i, i));
7835 for (std::size_t j = i; j < paramList.size(); j++) {
7836 C(i, j) = V(i, j) / std::sqrt(V(i, i) * V(j, j));
7837 C(j, i) = C(i, j);
7838 }
7839 }
7840
7841 // Make std::vector of variations
7842 TVectorD F(plusVar.size());
7843 for (unsigned int j = 0; j < plusVar.size(); j++) {
7844 F[j] = (plusVar[j] - minusVar[j]) / 2;
7845 }
7846
7847 // Calculate error in linear approximation from variations and correlation coefficient
7848 double sum = F * (C * F);
7849
7850 return sqrt(sum);
7851 }
7852 return oo;
7853#else
7854
7855 // Strip out parameters with zero error
7858 if (frv->getError() > 1e-20) {
7859 fpf_stripped.add(*frv);
7860 }
7861 }
7862
7863 // Clone self for internal use
7864 RooAbsReal *cloneFunc = const_cast<PdfWrapper *>(this); // (RooAbsReal *)fFunc.absArg()->cloneTree();
7865 // RooAbsPdf *clonePdf = dynamic_cast<RooAbsPdf *>(cloneFunc);
7866 RooArgSet *errorParams = cloneFunc->getObservables(fpf_stripped);
7867
7868 RooArgSet *nset =
7869 nset_in.size() == 0 ? cloneFunc->getParameters(*errorParams) : cloneFunc->getObservables(nset_in);
7870
7871 // Make list of parameter instances of cloneFunc in order of error matrix
7872 RooArgList paramList;
7873 const RooArgList &fpf = fpf_stripped;
7874 std::vector<int> fpf_idx;
7875 for (Int_t i = 0; i < fpf.size(); i++) {
7876 RooAbsArg *par = errorParams->find(fpf[i].GetName());
7877 if (par) {
7878 paramList.add(*par);
7879 fpf_idx.push_back(i);
7880 }
7881 }
7882
7883 std::vector<double> plusVar, minusVar;
7884
7885 // Create vector of plus,minus variations for each parameter
7886 TMatrixDSym V(paramList.size() == fr.floatParsFinal().size() ? fr.covarianceMatrix()
7887 : fr.reducedCovarianceMatrix(paramList));
7888
7889 for (Int_t ivar = 0; ivar < paramList.size(); ivar++) {
7890
7892
7893 double cenVal = rrv.getVal();
7894 double errVal = sqrt(V(ivar, ivar));
7895
7896 // Make Plus variation
7897 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal + errVal);
7898 // plusVar.push_back((fExpectedEventsMode ? 1. : cloneFunc->getVal(nset)) *
7899 // (clonePdf ? clonePdf->expectedEvents(nset) : 1.));
7900 plusVar.push_back(cloneFunc->getVal(nset));
7901
7902 // Make Minus variation
7903 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal - errVal);
7904 // minusVar.push_back((fExpectedEventsMode ? 1. : cloneFunc->getVal(nset)) *
7905 // (clonePdf ? clonePdf->expectedEvents(nset) : 1.));
7906 minusVar.push_back(cloneFunc->getVal(nset));
7907
7908 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal);
7909 }
7910
7911 getVal(nset); // reset state
7912
7913 TMatrixDSym C(paramList.size());
7914 std::vector<double> errVec(paramList.size());
7915 for (int i = 0; i < paramList.size(); i++) {
7916 errVec[i] = sqrt(V(i, i));
7917 for (int j = i; j < paramList.size(); j++) {
7918 C(i, j) = V(i, j) / sqrt(V(i, i) * V(j, j));
7919 C(j, i) = C(i, j);
7920 }
7921 }
7922
7923 // Make vector of variations
7924 TVectorD F(plusVar.size());
7925 for (unsigned int j = 0; j < plusVar.size(); j++) {
7926 F[j] = (plusVar[j] - minusVar[j]) / 2;
7927 }
7928
7929 // Calculate error in linear approximation from variations and correlation coefficient
7930 double sum = F * (C * F);
7931
7932 // delete cloneFunc;
7933 delete errorParams;
7934 delete nset;
7935
7936 return sqrt(sum);
7937#endif
7938 }
7939
7940private:
7944 bool fExpectedEventsMode = false;
7945};
7946
7947const xRooNode *runningNode = nullptr;
7949
7951{
7952 std::cout << "Got signal " << signum << std::endl;
7953 if (signum == SIGINT) {
7954 std::cout << "Keyboard interrupt while building histogram" << std::endl;
7955 // TODO: create a global mutex for this
7956 runningNode->fInterrupted = true;
7957 } else {
7959 }
7960}
7961
7963{
7964 auto _doSterilize = [](RooAbsArg *obj) {
7965 if (!obj)
7966 return;
7967 for (int i = 0; i < obj->numCaches(); i++) {
7968 if (auto cache = dynamic_cast<RooObjCacheManager *>(obj->getCache(i))) {
7969 cache->reset();
7970 }
7971 }
7972 if (RooAbsPdf *p = dynamic_cast<RooAbsPdf *>(obj); p) {
7973 p->setNormRange(p->normRange());
7974 }
7975#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
7976 if (RooAbsReal *p = dynamic_cast<RooAbsReal *>(obj); p) {
7977 // need to forget about any normSet that was passed to getVal(...)
7978 // doesn't seem necessary in 6.28
7979
7980 p->setProxyNormSet(nullptr);
7981 p->_lastNSet = nullptr;
7982 }
7983#endif
7984 obj->setValueDirty();
7985 };
7986 if (auto w = get<RooWorkspace>(); w) {
7987 // sterilizing all nodes
7988 for (auto &c : w->components()) {
7989 _doSterilize(c);
7990 }
7991 return;
7992 }
7993 // recursive through all clients and sterlize their normalization caches
7994 std::function<void(RooAbsArg *)> func;
7995 func = [&](RooAbsArg *a) {
7996 if (!a) {
7997 return;
7998 }
7999 _doSterilize(a); // sterilize first so that cache elements don't appear in the client list
8000 // safety net in case sterilizing one client deletes another one of our clients
8001 // monitor for change in clients list size
8002 // found this was only case in 6.26 (valgrind shows invalid read), in 6.28 these went away
8003 // might be in 6.28 the client list iterator became able to handle in-loop edits but didn't see
8004 // in test case that client count changed so just resterilizing if that's the case.
8005 size_t nClients;
8006 do {
8007 nClients = a->clients().size();
8008 for (auto obj : a->clients()) {
8009 func(dynamic_cast<RooAbsArg *>(obj));
8010 if (a->clients().size() != nClients) {
8011 break; // means sterilizing a client changed our clients, so don't trust the client iterator at this
8012 // point
8013 }
8014 }
8015 } while (a->clients().size() != nClients);
8016 };
8017 func(get<RooAbsArg>());
8018}
8019
8020// observables not in the axisVars are automatically projected over
8021xRooNode xRooNode::histo(const xRooNode &vars, const xRooNode &fr, bool content, bool errors, bool stack, bool errorsHi,
8022 bool errorsLo, int nErrorToys) const
8023{
8024
8025 if (!vars.fComp && strlen(vars.GetName())) {
8027 }
8028
8029 xRooNode out(TString::Format("%s.histo", GetName()), nullptr, *this);
8030
8031 RooAbsLValue *v = nullptr;
8032 if (vars.empty()) {
8033 // does an integral
8034 out.fComp = std::shared_ptr<TH1>(
8035 BuildHistogram(nullptr, !content, errors, -1, -1, fr, errorsHi, errorsLo, nErrorToys, nullptr, !stack, false));
8036 } else if (vars.size() == 1) {
8037 v = vars.at(0)->get<RooAbsLValue>();
8038 out.fComp = std::shared_ptr<TH1>(
8039 BuildHistogram(v, !content, errors, 1, 0, fr, errorsHi, errorsLo, nErrorToys, nullptr, !stack, true));
8040 } else {
8041 throw std::runtime_error("multi-dim histo not yet supported");
8042 }
8043
8044 return out;
8045}
8046
8048{
8049 return xRooNode(fComp, xRooNode(range.GetName(), nullptr, *this));
8050}
8051
8053 bool errorsHi, bool errorsLo, int nErrorToys, TH1 *templateHist, bool nostack,
8054 bool setInterp) const
8055{
8056 auto rar = get<RooAbsReal>();
8057 if (!rar) {
8058 if(get<TH1>()) { return dynamic_cast<TH1*>(get()->Clone()); }
8059 return nullptr;
8060 }
8061
8062
8063 TObject *vv = rar;
8064
8065 auto t = TH1::AddDirectoryStatus();
8066 TH1::AddDirectory(false);
8067 TH1 *h = nullptr;
8068 if (!v) {
8069 if (binStart != -1 || binEnd != -1) { // allow v to stay nullptr if doing integral (binStart=binEnd=-1)
8070 if (auto _ax = GetXaxis())
8071 v = dynamic_cast<RooAbsLValue *>(_ax->GetParent());
8072 } else {
8073 // don't need to integrate if doing a self-histogram
8074 v = dynamic_cast<RooRealVar *>(rar);
8075 }
8076 if (v) {
8077 vv = dynamic_cast<TObject *>(v);
8078 } else {
8079 // make a single-bin histogram of just this value
8080 h = new TH1D(rar->GetName(), rar->GetTitle(), 1, 0, 1);
8081 h->GetXaxis()->SetBinLabel(1, rar->GetName());
8082 h->GetXaxis()->SetTimeFormat(rar->GetName());
8083 }
8084 }
8085
8086 auto x = dynamic_cast<RooRealVar *>(v);
8087 bool setTitle = false;
8088 if (templateHist) {
8089 // using template hist for the binning
8090 h = static_cast<TH1 *>(templateHist->Clone(rar->GetName()));
8091 if (h->GetListOfFunctions())
8093 h->SetDirectory(0);
8094 h->SetTitle(rar->GetTitle());
8095 h->Reset();
8096 } else if (x) {
8097 if (x == rar) {
8098 // self histogram ...
8099 h = new TH1D(rar->GetName(), rar->GetTitle(), 1, 0, 1);
8100 h->Sumw2();
8101 h->GetXaxis()->SetBinLabel(1, rar->GetName());
8102 h->SetBinContent(1, rar->getVal());
8103 if (x->getError()) {
8104 h->SetBinError(1, x->getError());
8105 h->SetFillStyle(3005);
8106 h->SetFillColor(h->GetLineColor());
8107 }
8108 h->SetMaximum(x->hasMax() ? x->getMax()
8109 : (h->GetBinContent(1) + std::max(std::abs(h->GetBinContent(1) * 0.1), 50.)));
8110 h->SetMinimum(x->hasMin() ? x->getMin()
8111 : (h->GetBinContent(1) - std::max(std::abs(h->GetBinContent(1) * 0.1), 50.)));
8112 h->GetXaxis()->SetName(dynamic_cast<TObject *>(v)->GetName());
8113 h->SetOption("e2");
8114 h->SetMarkerSize(0);
8115 h->SetMarkerStyle(0);
8116
8117 return h;
8118 }
8119 auto _ax = GetXaxis();
8120 TString binningName = (_ax && _ax->GetParent() == x) ? _ax->GetName() : rar->getStringAttribute("binning");
8121 if (binningName == "")
8122 binningName = rar->GetName();
8123 if (x->hasBinning(binningName)) {
8124 if (x->getBinning(binningName).isUniform()) {
8125 h = new TH1D(rar->GetName(), rar->GetTitle(), x->numBins(binningName) <= 0 ? 100 : x->numBins(binningName),
8126 x->getMin(binningName), x->getMax(binningName));
8127 } else {
8128 h = new TH1D(rar->GetName(), rar->GetTitle(), x->numBins(binningName), x->getBinning(binningName).array());
8129 }
8130 h->GetXaxis()->SetTitle(x->getBinning(binningName).GetTitle());
8131 setTitle = true;
8132 } else if (auto _boundaries =
8133 _or_func(/*rar->plotSamplingHint(*x,x->getMin(),x->getMax())*/ (std::list<double> *)(nullptr),
8134 rar->binBoundaries(*x, -std::numeric_limits<double>::infinity(),
8135 std::numeric_limits<double>::infinity()));
8136 _boundaries) {
8137 std::vector<double> _bins;
8138 for (auto &b : *_boundaries) {
8139 if (_bins.empty() || std::abs(_bins.back() - b) > 1e-5 * _bins.back())
8140 _bins.push_back(b);
8141 } // found sometimes get virtual duplicates in the binning
8142 h = new TH1D(rar->GetName(), rar->GetTitle(), _bins.size() - 1, &_bins[0]);
8143 delete _boundaries;
8144 } else if (!x->hasMax() || !x->hasMin()) {
8145 // use current value of x to estimate range with
8146 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(), x->getVal() * 0.2, x->getVal() * 5);
8147 } else {
8148 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(), x->getBinning().array());
8149 }
8150 h->Sumw2();
8151 } else if (!h) {
8152 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(rar->GetName()), 0, v->numBins(rar->GetName()));
8153 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(v)) {
8154 int i = 1;
8155 std::map<int, std::string> cats; // fill into a map to preserve index ordering
8156 for (auto &c : *cat) {
8157 cats[c.second] = c.first;
8158 }
8159 for (auto &[_, label] : cats) {
8160 h->GetXaxis()->SetBinLabel(i++, label.c_str());
8161 }
8162 }
8163 h->Sumw2();
8164 }
8165 if (auto o = dynamic_cast<TObject *>(v); o && !setTitle) {
8166 h->GetXaxis()->SetTitle(o->GetTitle());
8167 }
8169 if (v) {
8170 if (h->GetXaxis()->IsAlphanumeric()) {
8171 // store the variable name in the TimeFormat property as well, b.c. alphanumeric requires axis name to be
8172 // "xaxis"
8173 h->GetXaxis()->SetTimeFormat(dynamic_cast<TObject *>(v)->GetName());
8174 } else {
8175 h->GetXaxis()->SetName(dynamic_cast<TObject *>(v)->GetName()); // WARNING: messes up display of bin labels
8176 }
8177 }
8178
8179 if (auto s = styles(nullptr, false); s) {
8180 auto _style = s.get<TStyle>();
8181 static_cast<TAttLine &>(*h) = *_style;
8182 static_cast<TAttFill &>(*h) = *_style;
8183 static_cast<TAttMarker &>(*h) = *_style;
8184 }
8185 if (strlen(h->GetXaxis()->GetTitle()) == 0)
8186 h->GetXaxis()->SetTitle(vv->GetTitle());
8187 auto p = dynamic_cast<RooAbsPdf *>(rar);
8188
8189 // possible speed improvement:
8190 // if(auto spdf = dynamic_cast<RooRealSumPdf*>(p); spdf && spdf->canBeExtended()) {
8191 // p = nullptr; // faster to evaluate sumpdf as a function not a pdf
8192 // }
8193
8194 if (empty && !errors) {
8195 return h;
8196 }
8197
8198 // if (!empty) {
8199
8200 auto _coefs = coefs();
8201
8202 RooFitResult *fr = nullptr;
8203 if (errors) {
8204 // must ensure the fit result we obtain includes pars from coefficients if present
8205 if (_fr.get<RooFitResult>()) {
8206 fr = static_cast<RooFitResult *>(_fr.get<RooFitResult>()->Clone());
8207 } else {
8208 auto frn =
8209 (!_coefs.get() ? *this : xRooNode(RooProduct("tmp", "tmp", RooArgList(*rar, *_coefs.get<RooAbsReal>()))))
8210 .fitResult();
8211 if (strlen(_fr.GetName()))
8212 frn = frn.reduced(_fr.GetName());
8213 fr = dynamic_cast<RooFitResult *>(frn->Clone());
8214 }
8215 if (!GETDMP(fr, _finalPars)) {
8217 }
8218
8219 /// Oct2022: No longer doing this because want to allow fitResult to be used to get partial error
8220 // // need to add any floating parameters not included somewhere already in the fit result ...
8221 // now done in the fitResult() method, where a fit result from the workspace can be automatically extended
8222
8223 TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr, _VM));
8224
8225 if (!prevCov || size_t(fr->covarianceMatrix().GetNcols()) < fr->floatParsFinal().size()) {
8226 TMatrixDSym cov(fr->floatParsFinal().size());
8227 if (prevCov) {
8228 for (int i = 0; i < prevCov->GetNcols(); i++) {
8229 for (int j = 0; j < prevCov->GetNrows(); j++) {
8230 cov(i, j) = (*prevCov)(i, j);
8231 }
8232 }
8233 }
8234 int i = 0;
8235 for (auto &p2 : fr->floatParsFinal()) {
8236 if (!prevCov || i >= prevCov->GetNcols()) {
8237 cov(i, i) = pow(dynamic_cast<RooRealVar *>(p2)->getError(), 2);
8238 }
8239 i++;
8240 }
8241 int covQualBackup = fr->covQual();
8244 }
8245
8246 if (v) {
8247 // need to remove v from result as we are plotting as function of v
8248 if (auto _p = fr->floatParsFinal().find(dynamic_cast<TObject *>(v)->GetName()); _p) {
8250 _pars.remove(*_p, true);
8252 int covQualBackup = fr->covQual();
8255 const_cast<RooArgList &>(fr->floatParsFinal())
8256 .remove(*_p, true); // NOTE: I think this might be a memory leak, should delete _p after removal
8257 }
8258 }
8259 // finally check at least one float has errors defined (might not be cause if in prefit state)
8260 bool hasErrors = false;
8261 for (auto pp : fr->floatParsFinal()) {
8262 if (dynamic_cast<RooRealVar *>(pp)->hasError()) {
8263 hasErrors = true;
8264 break;
8265 }
8266 }
8267 if (!hasErrors) {
8268 errors = false;
8269 delete fr;
8270 }
8271 }
8272
8274 if (v)
8275 normSet.add(*dynamic_cast<RooAbsArg *>(v));
8276
8277 if (binEnd == 0)
8278 binEnd = h->GetNbinsX();
8279
8280 bool needBinWidth = false;
8281 // may have MULTIPLE coefficients for the same pdf!
8282
8283 if (x && (p || _coefs.get() || rar->getAttribute("density"))) {
8284 // pdfs of samples embedded in a sumpdf (aka have a coef) will convert their density value to a content
8285 needBinWidth = true;
8286 }
8287
8288 if (auto spdf = dynamic_cast<RooRealSumPdf *>(p);
8289 spdf && spdf->canBeExtended() && !spdf->getFloor() && !_coefs.get()) {
8290 p = nullptr; // if pdf has no floor, will evaluate it as a function to allow it to be negative - evaluation should
8291 // also be faster (no integral)
8292 // exception is if RooRealSumPdf is embedded in a RooAddPdf (detected by presence of coefs) ... then it must be
8293 // evaluated as a pdf technically should check parent is a RooAddPdf, because if was inside a RooRealSumPdf then
8294 // would be evaluated as a function!
8295 }
8296
8297 // check if we need to do any projecting of other observables
8298 RooAbsReal *oldrar = nullptr;
8299 auto _obs = obs();
8300
8301 for (auto o : _obs) {
8302 if (auto rr = o->get<RooRealVar>(); rr && rr->hasRange("coordRange")) {
8303 rr->removeRange("coordRange"); // doesn't actually remove, just sets to -inf->+inf
8304 rr->setStringAttribute("coordRange", nullptr); // removes the attribute
8305 }
8306 }
8307 // probably should also remove any range on the x-axis variable too, if there is one
8308 if (auto rr = dynamic_cast<RooRealVar *>(v); rr && rr->hasRange("coordRange")) {
8309 rr->removeRange("coordRange"); // doesn't actually remove, just sets to -inf->+inf
8310 rr->setStringAttribute("coordRange", nullptr); // removes the attribute
8311 }
8312 coords(); // loads current coordinates and populates coordRange, if any
8313
8314 if (auto a = dynamic_cast<RooAbsArg *>(v))
8315 _obs.get<RooArgList>()->remove(*a);
8316 if (!_obs.get<RooArgList>()->empty()) {
8317 oldrar = rar;
8318 normSet.add(*_obs.get<RooArgList>());
8319 // check if any obs are restricted range
8320 bool hasRange = false;
8321 for (auto o : normSet) {
8322 if (auto rr = dynamic_cast<RooRealVar *>(o);
8323 rr && (rr->getStringAttribute("coordRange")) && strlen(rr->getStringAttribute("coordRange"))) {
8324 hasRange = true;
8325 break;
8326 }
8327 }
8328 if (p) {
8329 // need to handle special case of RooSimultaneous ... each pdf needs individually projecting over just its
8330 // dependent obs
8331 if (auto s = dynamic_cast<RooSimultaneous *>(p)) {
8332 auto newrar = new RooSimultaneous("projSim", "projSim", const_cast<RooAbsCategoryLValue &>(s->indexCat()));
8333 for (auto pdf : bins()) {
8334 // auto _pdf =
8335 // pdf->get<RooAbsPdf>()->createProjection(*pdf->get<RooAbsPdf>()->getObservables(*_obs.get<RooArgList>()));
8336 auto _pdf =
8337 new xRooProjectedPdf(TString::Format("%s_projection", pdf->GetName()), "", *pdf->get<RooAbsPdf>(),
8338 *pdf->get<RooAbsPdf>()->getObservables(*_obs.get<RooArgList>()));
8339 if (hasRange) {
8340 dynamic_cast<RooAbsPdf *>(_pdf)->setNormRange("coordRange");
8341 }
8342 newrar->addPdf(*_pdf, pdf->coords()[s->indexCat().GetName()]->get<RooCategory>()->getLabel());
8343 }
8344 rar = newrar;
8345 } else {
8346 rar = p->createProjection(
8347 *_obs.get<RooArgList>()); // TODO should use xRooProjectedPdf here too, because not fixed range and
8348 // extend behaviour of RooProjectedPdf in ROOT yet
8349 if (hasRange) {
8350 dynamic_cast<RooAbsPdf *>(rar)->setNormRange("coordRange");
8351 }
8352 }
8353 if (hasRange)
8354 p->setNormRange("coordRange"); // should get cleared when we sterilize
8355 } else {
8356 if (hasRange) {
8357 // commented out passing of normset so that getVal of non-pdf is always a 'raw' value (needed for raw eval
8358 // of RooRealSumPdf)
8359 rar = std::unique_ptr<RooAbsReal>{rar->createIntegral(
8360 *_obs.get<RooArgList>(),
8361 /*RooFit::NormSet(normSet),*/ RooFit::Range("coordRange"))}
8362 .release();
8363 } else {
8364 rar =
8365 std::unique_ptr<RooAbsReal>{rar->createIntegral(*_obs.get<RooArgList>() /*, RooFit::NormSet(normSet)*/)}
8366 .release();
8367 }
8368 }
8369 }
8370
8371 bool scaleExpected = (p && p->canBeExtended() && !_coefs.get());
8372 // Note about above: if pdf has coefficients then its embedded in a RooAddPdf that has coefs defined ...
8373 // in this case we should *not* scale by expected, since the coefs become the scaling instead
8374 // we should also not build a stack for this (may be a RooRealSumPdf inside a RooAddPdf, but the
8375 // samples of the RooRealSumPdf wont be correctly scaled to line up with overall RooRealSumPdf
8376 // which will be normalized to its coefficient
8377 if (!nostack && p && p->canBeExtended() && _coefs.get()) {
8378 nostack = true;
8379 // if wanted to still hve a stack, would need to scale the stack subcomponents by
8380 // coefs-value / p_integral(raw) ... since raw p-integral will be what stack integrates to
8381 }
8382
8383 std::unique_ptr<RooArgSet> snap(normSet.snapshot());
8385 std::vector<double> lapTimes;
8386 bool warned = false;
8387 if (binStart == -1 && binEnd == -1) {
8388 binEnd = 1;
8389 }
8390 auto cat = (!x) ? dynamic_cast<RooAbsCategoryLValue *>(v) : nullptr;
8391 RooArgList *errorPars = nullptr;
8392 std::unique_ptr<RooAbsCollection> errorParsSnap;
8393
8394 if (!v) {
8395 setInterp = false;
8396 }
8397
8398 if (setInterp) {
8399 RooAbsArg *vvv = dynamic_cast<RooAbsArg *>(v);
8400 // determining if histogram should have interpolation drawing options set on it
8401 // need to strip namespace to discount the "HistFactory" namespace classes from all being treated as binned
8402 TString clNameNoNamespace = rar->ClassName();
8404 setInterp = (clNameNoNamespace.Contains("Hist") || vvv->isCategory() || rar->isBinnedDistribution(*vvv) ||
8405 h->GetNbinsX() == 1 || rar->getAttribute("BinnedLikelihood") ||
8406 (dynamic_cast<RooAbsRealLValue *>(vvv) &&
8407 std::unique_ptr<std::list<double>>(rar->binBoundaries(*dynamic_cast<RooAbsRealLValue *>(vvv),
8408 -std::numeric_limits<double>::infinity(),
8409 std::numeric_limits<double>::infinity()))))
8410 ? false
8411 : true;
8412 if (auto d = dynamic_cast<RooHistFunc *>(rar); d && !d->isBinnedDistribution(*vvv) && h->GetNbinsX() != 1) {
8413 setInterp = true; // hist func is interpolated, so draw it as such
8414 }
8415 if (setInterp && !components().empty()) {
8416 // check if all components of dOpt are "Hist" type (CMS model support)
8417 // if so then don't interp;
8418 bool allHist = true;
8419 for (auto &s : components()) {
8420 TString _clName = s->get()->ClassName();
8421 _clName = _clName(_clName.Last(':') + 1, _clName.Length());
8422 if (!(s->get() && _clName.Contains("Hist"))) {
8423 allHist = false;
8424 break;
8425 }
8426 }
8427 if (allHist)
8428 setInterp = false;
8429 }
8430 if (setInterp) {
8431 h->SetOption("l"); // does linear interpolation between points
8432 }
8433 }
8434
8435 if (errors) {
8436 // may be computing potentially asymmetric errors
8437 // the main histogram will be the error band, and the nominal histogram will be added as a function
8438 // so that it is drawn over the top of the error band
8439 // note that this means GetBinContent on returned histogram will return midpoint of the up and down error
8440 auto l = static_cast<TH1 *>(h->Clone("nominal"));
8441 l->SetDirectory(0);
8442 l->SetFillStyle(0);
8443 h->GetListOfFunctions()->Add(l, (setInterp) ? "lsame" : "histsame");
8444 h->SetOption(setInterp ? "e3" : "e2"); // default draw option E2 or E3 so error band shown .. could have used
8445 // 'EX0' to draw "classic style"
8446 // could take this from the 'band' style object if we create one in future?
8447 h->SetMarkerSize(0);
8448 h->SetFillStyle(3005);
8449 h->SetFillColor(h->GetLineColor());
8450 }
8451
8452 if (nErrorToys > 0) {
8453 errors = false; // wont evaluate error on each toy, will estimate for std.dev or normiles of toys
8454 // need list of errorPars
8455 auto allPars =
8456 (!_coefs.get() ? *this : xRooNode(RooProduct("tmp", "tmp", RooArgList(*rar, *_coefs.get<RooAbsReal>()))))
8457 .pars();
8458 errorPars = new RooArgList; // will be in same order as appear in fr.
8459 for (auto a : fr->floatParsFinal()) {
8460 if (auto par = allPars.get<RooArgList>()->find(*a)) {
8461 errorPars->add(*par);
8462 }
8463 }
8464 errorParsSnap.reset(errorPars->snapshot());
8465 auto l = static_cast<TH1 *>(h->Clone("toys"));
8466 l->Reset(); // removes any functions
8467 l->SetDirectory(0);
8468 h->GetListOfFunctions()->Add(
8469 l, "histsame"); // ensures just this empty hist will be drawn, and not each individual toy
8470
8471 if (errorsLo || errorsHi)
8472 empty = false; // must not be empty b.c. calculation of error relies on knowing nominal (see after loop)
8473 }
8474
8475 for (int toy = 0; toy < (nErrorToys + 1); toy++) {
8476
8477 TH1 *main_h = h;
8478 if (toy > 0) {
8479 h = static_cast<TH1 *>(main_h->Clone(TString::Format("toy_%d", toy)));
8480 h->SetDirectory(0);
8481 h->Reset();
8482 static_cast<TH1 *>(main_h->GetListOfFunctions()->FindObject("toys"))->GetListOfFunctions()->Add(h);
8483 // randomize the parameter values according to the fr's covariance matrix
8484 errorPars->assignValueOnly(fr->randomizePars());
8485 }
8486
8487 for (int i = std::max(1, binStart); i <= std::min(h->GetNbinsX(), binEnd); i++) {
8488 timeIt.Start(true);
8489 if (x) {
8490 x->setVal(h->GetBinCenter(i));
8491 } else if (cat) {
8492 cat->setLabel(h->GetXaxis()->GetBinLabel(i)); // because order might not match "binning" order
8493 } else if (v) {
8494 v->setBin(i - 1);
8495 }
8496 if (x && !x->inRange("coordRange"))
8497 continue;
8498
8499 double r = 0;
8500 if (!empty || toy > 0) {
8501 r = /*(p && p->selfNormalized())*/ rar->getVal(p ? &normSet : nullptr);
8502#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
8503 if (std::isnan(r) && RooNaNPacker::isNaNWithPayload(r)) {
8505 }
8506#endif
8507 if (r && _coefs.get()) {
8508 r *= _coefs.get<RooAbsReal>()->getVal(normSet);
8509 }
8510 if (needBinWidth) {
8511 r *= h->GetBinWidth(i);
8512 }
8513 if (scaleExpected) {
8514 // std::cout << r << " exp = " << p->expectedEvents(normSet) << " for normRange " << (p->normRange() ?
8515 // p->normRange() : "null") << std::endl; p->Print();rar->Print();
8516 r *= (p->expectedEvents(normSet));
8517 } // do in here in case dependency on var
8518 }
8519 h->SetBinContent(i, r);
8520
8521 if (errors) {
8522 static_cast<TH1 *>(h->FindObject("nominal"))->SetBinContent(i, r); // transfer nominal to nominal hist
8523 double res;
8524 bool doAsym = (errorsHi && errorsLo);
8525 if (doAsym) {
8526 errorsHi = false;
8527 }
8528 if (p) {
8529 // std::cout << "computing error of :" << h->GetBinCenter(i) << std::endl;
8530 // //fr->floatParsFinal().Print(); fr->covarianceMatrix().Print();
8531 // res = PdfWrapper((oldrar) ? *rar : *p, _coefs.get<RooAbsReal>(), !v, oldrar ? p : nullptr)
8532 // .getSimplePropagatedError(*fr, normSet);
8534 PdfWrapper((oldrar) ? *rar : *p, _coefs.get<RooAbsReal>(), !v, oldrar ? p : nullptr), *fr, normSet,
8536#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
8537 // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
8538 p->_normSet = nullptr;
8539#endif
8540 } else {
8541 // res = RooProduct("errorEval", "errorEval",
8542 // RooArgList(*rar, !_coefs.get() ? RooFit::RooConst(1) :
8543 // *_coefs.get<RooAbsReal>()))
8544 // .getPropagatedError(
8545 // *fr /*, normSet*/); // should be no need to pass a normSet to a non-pdf (but
8546 // not verified this)
8548 RooProduct("errorEval", "errorEval",
8549 RooArgList(*rar, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>())),
8550 *fr, {}, &errorPars, errorsHi,
8551 errorsLo); // should be no need to pass a normSet to a non-pdf (but not verified this)
8552 // especially important not to pass in the case we are evaluated RooRealSumPdf as a function! otherwise
8553 // error will be wrong
8554 }
8555 if (needBinWidth) {
8556 res *= h->GetBinWidth(i);
8557 }
8558 h->SetBinError(i, res);
8559 if (doAsym) {
8560 // compute Hi error
8561 errorsHi = true;
8562 errorsLo = false;
8563 if (p) {
8565 PdfWrapper((oldrar) ? *rar : *p, _coefs.get<RooAbsReal>(), !v, oldrar ? p : nullptr), *fr, normSet,
8567 } else {
8569 RooProduct("errorEval", "errorEval",
8570 RooArgList(*rar, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>())),
8571 *fr, {}, &errorPars, errorsHi, errorsLo);
8572 }
8573 if (needBinWidth) {
8574 res *= h->GetBinWidth(i);
8575 }
8576 errorsLo = true;
8577 // lowVal = content - error, highVal = content + res
8578 // => band/2 = (res+error)/2 and band-mid = (2*content+res-error)/2
8579 h->SetBinContent(i, h->GetBinContent(i) + (res - h->GetBinError(i)) * 0.5);
8580 h->SetBinError(i, (res + h->GetBinError(i)) * 0.5);
8581 }
8582 }
8583 timeIt.Stop();
8584 lapTimes.push_back(timeIt.RealTime());
8585 double time_estimate =
8586 (lapTimes.size() > 1)
8587 ? (h->GetNbinsX() * (std::accumulate(lapTimes.begin() + 1, lapTimes.end(), 0.) / (lapTimes.size() - 1)))
8588 : 0.;
8589 if (!warned && (lapTimes.at(0) > 10 || (lapTimes.size() > 2 && time_estimate > 60.))) {
8590 TTimeStamp t2;
8591 t2.Add(time_estimate);
8592 Warning("BuildHistogram", "Building this histogram will take until %s", t2.AsString());
8593 if (errors) {
8594 // install interrupt handler
8595 runningNode = this;
8597 }
8598 warned = true;
8599 }
8600 if (fInterrupted) {
8601 if (errors) {
8602 Warning("BuildHistogram", "Skipping errors for remaining bins");
8603 errors = false;
8604 }
8605 fInterrupted = false;
8606 }
8607 }
8608 if (toy > 0) {
8609 h = main_h;
8610 }
8611 }
8612 if (gOldHandlerr) {
8614 gOldHandlerr = nullptr;
8615 }
8616 normSet = *snap;
8617
8618 if (errorPars) {
8619 if (errorParsSnap)
8621 delete errorPars;
8622 }
8623 if (nErrorToys) {
8624 // compute main histogram error bar from toys
8625 // if not doing asymmetric, then will display std.dev
8626 // otherwise will copy main to nominal and make main error bar s.t. it shows +/-1sigma vals
8627 if (errorsLo && errorsHi) {
8628 auto nomHist = static_cast<TH1 *>(h->FindObject("nominal"));
8629 nomHist->Add(h);
8630 }
8631 for (int i = 1; i <= h->GetNbinsX(); i++) {
8632 std::vector<double> vals;
8633 vals.reserve(nErrorToys);
8634 for (int j = 1; j < (nErrorToys + 1); j++) {
8635 vals.push_back(
8636 static_cast<TH1 *>(h->FindObject("toys")->FindObject(TString::Format("toy_%d", j)))->GetBinContent(i));
8637 }
8638 double upVal, downVal;
8639 if (errorsLo || errorsHi) {
8640 std::sort(vals.begin(), vals.end());
8641 upVal = vals.at(std::round(vals.size() * ROOT::Math::gaussian_cdf(1)));
8642 downVal = vals.at(std::round(vals.size() * ROOT::Math::gaussian_cdf(-1)));
8643 if (!errorsLo)
8644 downVal = 2. * h->GetBinContent(i) - upVal;
8645 if (!errorsHi)
8646 upVal = 2. * h->GetBinContent(i) - downVal;
8647 } else {
8648 double err = TMath::StdDev(vals.begin(), vals.end());
8649 upVal = h->GetBinContent(i) + err;
8650 downVal = h->GetBinContent(i) - err;
8651 }
8652 h->SetBinContent(i, (upVal + downVal) * 0.5);
8653 h->SetBinError(i, (upVal - downVal) * 0.5);
8654 }
8655 }
8656
8657 if (oldrar) {
8658 std::vector<RooAbsArg *> extra;
8659 if (auto s = dynamic_cast<RooSimultaneous *>(rar)) {
8660 // need to delete all the subpdfs we created too
8661 for (auto _pdf : s->servers()) {
8662 if (dynamic_cast<RooAbsPdf *>(_pdf))
8663 extra.push_back(_pdf);
8664 }
8665 }
8666 extra.push_back(rar);
8667 rar = oldrar;
8668 xRooNode(*rar).sterilize(); // need to clear the cache of the created integral - do this before deleting things!
8669 for (auto a : extra)
8670 delete a;
8671 } else {
8672 sterilize(); // needed to forget about the normSet that was passed to getVal()
8673 }
8674
8675 if (!p && !rar->getAttribute("density") && !needBinWidth) {
8676 h->GetYaxis()->SetTitle(rar->getStringAttribute("units"));
8677 } else if ((p && p->canBeExtended()) || (!p && needBinWidth)) {
8678 h->GetYaxis()->SetTitle("Events");
8679 } else {
8680 h->GetYaxis()->SetTitle("Probability Mass");
8681 }
8682 h->GetYaxis()->SetMaxDigits(3);
8683
8684 if (errors) {
8685 delete fr;
8686 }
8687
8688 // build a stack unless not requested
8689 if (!nostack) {
8690 // need to draw copy of hist so shown over the stack
8691 auto hCopy = static_cast<TH1 *>(h->Clone("copy"));
8692 hCopy->Reset();
8693 hCopy->Add(h); // use Reset and Add to clear the function list (dont clear directly as may double-delete if same
8694 // object added twice)
8695 hCopy->SetStats(false);
8696 h->GetListOfFunctions()->Add(hCopy, TString(h->GetOption()) + "same");
8697 h->GetListOfFunctions()->Add(hCopy, "axissame"); // prevents stack covering axis
8698 TString dOpt = (setInterp) ? "LF2" : ""; // should become lf2 if interpolation of histogram is appropriate
8699
8700 const xRooNode *rarNode = this;
8701 RooAbsReal *sf = nullptr;
8702 if (get()->InheritsFrom("RooExtendPdf")) {
8703 const_cast<xRooNode *>(this)->browse();
8704 rarNode = find(".pdf").get();
8705 // rar = rarNode->get<RooAbsReal>();
8706 sf = find(".n")->get<RooAbsReal>();
8707 }
8708
8709 THStack *stack = new THStack("stack", TString::Format("%s;%s", rar->GetTitle(), h->GetXaxis()->GetTitle()));
8710 int count = 2;
8711 std::map<std::string, int> colorByTitle; // TODO: should fill from any existing legend
8712 std::set<std::string> allTitles;
8713 bool titleMatchName = true;
8714 std::map<std::string, TH1 *> histGroups;
8715 std::vector<TH1 *> hhs;
8716 std::set<TH1 *> histsWithBadTitles; // these histograms will have their titles autoFormatted
8717
8718 // support for CMS model case where has single component containing many coeffs
8719 // will build stack by setting each coeff equal to 0 in turn, rebuilding the histogram
8720 // the difference from the "full" histogram will be the component
8722 if (!rarNode->components().empty()) {
8723 auto comps = rarNode->components()[0];
8724 for (auto &c : *comps) {
8725 if (c->fFolder == "!.coeffs")
8726 cms_coefs.add(*c->get<RooAbsArg>());
8727 }
8728 }
8729 if (!cms_coefs.empty()) {
8730 RooRealVar zero("zero", "", 0);
8731 std::shared_ptr<TH1> prevHist(static_cast<TH1 *>(h->Clone()));
8732 prevHist->Reset();
8733 prevHist->Add(h);
8734 for (auto c : cms_coefs) {
8735 // seems I have to remake the function each time, as haven't figured out what cache needs clearing?
8736 std::unique_ptr<RooAbsReal> f(
8737 dynamic_cast<RooAbsReal *>(rarNode->components()[0]->get()->Clone("tmpCopy")));
8738 zero.setAttribute(Form("ORIGNAME:%s", c->GetName())); // used in redirectServers to say what this replaces
8739 f->redirectServers(RooArgSet(zero), false, true); // each time will replace one additional coef
8740 // zero.setAttribute(Form("ORIGNAME:%s",c->GetName()),false); (commented out so that on next iteration
8741 // will still replace all prev)
8742 auto hh = xRooNode(*f, *this).BuildHistogram(v);
8743 hh->SetName(c->GetName());
8744 if (sf)
8745 hh->Scale(sf->getVal());
8746 if (strlen(hh->GetTitle()) == 0) {
8747 hh->SetTitle(c->GetName()); // ensure all hists has titles
8748 histsWithBadTitles.insert(hh);
8749 } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
8750 histsWithBadTitles.insert(hh);
8751 }
8752 titleMatchName &= (TString(c->GetName()) == hh->GetTitle() ||
8753 TString(hh->GetTitle()).BeginsWith(TString(c->GetName()) + "_"));
8754 std::shared_ptr<TH1> nextHist(static_cast<TH1 *>(hh->Clone()));
8755 hh->Add(prevHist.get(), -1.);
8756 hh->Scale(-1.);
8757 hhs.push_back(hh);
8759 }
8760 } else if (get<RooSimultaneous>()) {
8761 // need to create a histogram for each sample across all the channels - will rely on functionality below to
8762 // merge them based on titles
8763
8764 for (auto &chan : bins()) {
8765 TString chanName(chan->GetName());
8766 chanName = chanName(chanName.Index("=") + 1, chanName.Length());
8767 auto samps = chan->mainChild();
8768 if (!samps)
8769 samps = *chan;
8770 for (auto &samp : samps.components()) {
8771 auto hh = static_cast<TH1 *>(h->Clone(samp->GetName()));
8772 hh->Reset();
8773 hh->SetTitle(samp->GetTitle());
8774 if (strlen(hh->GetTitle()) == 0) {
8775 hh->SetTitle(samp->GetName());
8776 histsWithBadTitles.insert(hh);
8777 } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
8778 histsWithBadTitles.insert(hh);
8779 }
8780 hh->SetTitle(TString(hh->GetTitle())
8781 .ReplaceAll(TString(chan->get()->GetName()) + "_",
8782 "")); // remove occurance of channelname_ in title (usually prefix)
8783 titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
8784 TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
8785 hh->SetBinContent(hh->GetXaxis()->FindFixBin(chanName), samp->GetContent());
8786 hhs.push_back(hh);
8787 }
8788 }
8789 } else {
8790 for (auto &samp : rarNode->components()) {
8791 auto hh = samp->BuildHistogram(
8792 v, empty, false /* no errors for stack*/, binStart, binEnd, _fr, false, false, 0, h, true,
8793 setInterp); // passing h to ensure binning is the same for all subcomponent hists
8794 hh->SetName(samp->GetName());
8795 if (sf)
8796 hh->Scale(sf->getVal());
8797 hhs.push_back(hh);
8798 if (strlen(hh->GetTitle()) == 0) {
8799 hh->SetTitle(samp->GetName()); // ensure all hists has titles
8800 histsWithBadTitles.insert(hh);
8801 } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
8802 histsWithBadTitles.insert(hh);
8803 }
8804 titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
8805 TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
8806 }
8807 }
8808
8809 if (!hhs.empty()) {
8810 for (auto &hh : hhs) {
8811 allTitles.insert(hh->GetTitle());
8812 }
8813
8814 // get common prefix to strip off only if all titles match names and
8815 // any title is longer than 10 chars
8816 size_t e = std::min(allTitles.begin()->size(), allTitles.rbegin()->size());
8817 size_t ii = 0;
8818 bool goodPrefix = false;
8819 std::string commonSuffix;
8820 if (titleMatchName && hhs.size() > 1) {
8821 while (ii < e - 1 && allTitles.begin()->at(ii) == allTitles.rbegin()->at(ii)) {
8822 ii++;
8823 if (allTitles.begin()->at(ii) == '_' || allTitles.begin()->at(ii) == ' ')
8824 goodPrefix = true;
8825 }
8826
8827 // find common suffix if there is one .. must start with a "_"
8828 bool stop = false;
8829 while (!stop && commonSuffix.size() < size_t(e - 1)) {
8830 commonSuffix = allTitles.begin()->substr(allTitles.begin()->length() - commonSuffix.length() - 1);
8831 for (auto &tt : allTitles) {
8832 if (!TString(tt).EndsWith(commonSuffix.c_str())) {
8833 commonSuffix = commonSuffix.substr(1);
8834 stop = true;
8835 break;
8836 }
8837 }
8838 }
8839 if (commonSuffix.find('_') == std::string::npos) {
8840 commonSuffix = "";
8841 } else {
8842 commonSuffix = commonSuffix.substr(commonSuffix.find('_'));
8843 }
8844 }
8845 if (!goodPrefix)
8846 ii = 0;
8847
8848 // also find how many characters are needed to distinguish all entries (that dont have the same name)
8849 // then carry on up to first space or underscore
8850 size_t jj = 0;
8851 std::map<std::string, std::string> reducedTitles;
8852 while (reducedTitles.size() != allTitles.size()) {
8853 jj++;
8854 std::map<std::string, int> titlesMap;
8855 for (auto &s : allTitles) {
8856 if (reducedTitles.count(s))
8857 continue;
8858 titlesMap[s.substr(0, jj)]++;
8859 }
8860 for (auto &s : allTitles) {
8861 if (titlesMap[s.substr(0, jj)] == 1 && (jj >= s.length() || s.at(jj) == ' ' || s.at(jj) == '_')) {
8862 reducedTitles[s] = s.substr(0, jj);
8863 }
8864 }
8865 }
8866
8867 // strip common prefix and suffix before adding
8868 for (auto ritr = hhs.rbegin(); ritr != hhs.rend(); ++ritr) { // go in reverse order
8869 if (!histsWithBadTitles.count((*ritr))) {
8870 continue;
8871 }
8872 auto _title = (hhs.size() > 5) ? reducedTitles[(*ritr)->GetTitle()] : (*ritr)->GetTitle();
8873 _title = _title.substr(ii < _title.size() ? ii : 0);
8874 if (!commonSuffix.empty() && TString(_title).EndsWith(commonSuffix.c_str()))
8875 _title = _title.substr(0, _title.length() - commonSuffix.length());
8876 (*ritr)->SetTitle(_title.c_str());
8877 }
8878 }
8879
8880 for (auto &hh : hhs) {
8881 // automatically group hists that all have the same title
8882 if (histGroups.find(hh->GetTitle()) == histGroups.end()) {
8883 histGroups[hh->GetTitle()] = hh;
8884 } else {
8885 // add it into this group
8886 histGroups[hh->GetTitle()]->Add(hh);
8887 delete hh;
8888 hh = nullptr;
8889 continue;
8890 }
8891 auto hhMin = (hh->GetMinimum() == 0) ? hh->GetMinimum(1e-9) : hh->GetMinimum();
8892 if (!stack->GetHists() && h->GetMinimum() > hhMin) {
8893 auto newMin = hhMin - (h->GetMaximum() - hhMin) * gStyle->GetHistTopMargin();
8894 if (hhMin >= 0 && newMin < 0)
8895 newMin = hhMin * 0.99;
8896 // adjustYRange(newMin, h->GetMaximum());
8897 }
8898
8899 /*if(stack->GetHists() && stack->GetHists()->GetEntries()>0) {
8900 // to remove rounding effects on bin boundaries, see if binnings compatible
8901 auto _h1 = dynamic_cast<TH1*>(stack->GetHists()->At(0));
8902 if(_h1->GetNbinsX()==hh->GetNbinsX()) TODO ... finish dealing with silly rounding effects
8903 }*/
8904 TString thisOpt = TString(hh->GetOption()) == "l" ? "LF2" : ""; // need LF2 to get smooth line with fill
8905 // uncomment next line to blend continuous with discrete components .. get some unpleasant "poke through"
8906 // effects though
8907 // if(auto s = samp->get<RooAbsReal>(); s) thisOpt = s->isBinnedDistribution(*dynamic_cast<RooAbsArg*>(v)) ?
8908 // "" : "LF2";
8909 stack->Add(hh, thisOpt);
8910 }
8911 // stack->SetBit(kCanDelete); // should delete its sub histograms
8912 h->GetListOfFunctions()->AddFirst(stack, "noclear same");
8913 // stack->Draw("noclear same");
8914 // h->Draw(
8915 // dOpt + sOpt +
8916 // "same"); // overlay again .. if stack would cover original hist (negative components) we still see
8917 // integral
8918 // h->Draw("axissame"); // redraws axis
8919
8920 TList *ll = stack->GetHists();
8921 if (ll && ll->GetEntries()) {
8922
8923 // finally, ensure all hists are styled
8924 for (auto ho : *ll) {
8925 TH1 *hh = dynamic_cast<TH1 *>(ho);
8926 if (!hh)
8927 continue;
8928 bool createdStyle = (xRooNode(*hh, *this).styles(nullptr, false).get<TStyle>() == nullptr);
8929
8930 if (createdStyle) {
8931 // give hist a color, that isn't the same as any other hists color
8932 hh->SetFillStyle(1001); // solid fill style
8933 bool used = false;
8934 do {
8935 hh->SetFillColor((count++));
8936 // check not already used this color
8937 used = false;
8938 for (auto ho2 : *ll) {
8939 TH1 *hh2 = dynamic_cast<TH1 *>(ho2);
8940 if (!hh2)
8941 continue;
8942 auto _styleNode = xRooNode(*hh2, *this).styles(hh2, false);
8943 auto _style = _styleNode.get<TStyle>();
8944 if (hh != hh2 && _style && _style->GetFillColor() == hh->GetFillColor()) {
8945 used = true;
8946 break;
8947 }
8948 }
8949 } while (used);
8950 }
8951
8952 auto _styleNode = xRooNode(*hh, *this).styles(hh);
8953 if (auto _style = _styleNode.get<TStyle>()) {
8954 *dynamic_cast<TAttLine *>(hh) = *_style;
8955 *dynamic_cast<TAttFill *>(hh) = *_style;
8956 *dynamic_cast<TAttMarker *>(hh) = *_style;
8957 }
8958 // for stacks, fill color of white should be color 10 unless fill style is 0
8959 if (hh->GetFillColor() == kWhite && hh->GetFillStyle() != 0) {
8960 // kWhite means 'transparent' in ROOT ... should really use a FillStyle of 0 for that
8961 // so assume user wanted actual white, which is color 10
8962 hh->SetFillColor(10);
8963 }
8964 }
8965 }
8966 }
8967
8968 return h;
8969}
8970
8971double xRooNode::GetBinData(int bin, const xRooNode &data)
8972{
8973 if (data.get<RooAbsData>()) {
8974 // attach as a child before calling datasets(), so that is included in the list
8975 push_back(std::make_shared<xRooNode>(data));
8976 }
8977 auto node = datasets().find(data.GetName());
8978 if (data.get<RooAbsData>()) {
8979 // remove the child we attached
8980 resize(size() - 1);
8981 }
8982 if (!node)
8983 return std::numeric_limits<double>::quiet_NaN();
8984 return node->GetBinContent(bin);
8985}
8986
8987std::vector<double> xRooNode::GetBinContents(int binStart, int binEnd) const
8988{
8989 if (fBinNumber != -1) {
8990 if (binStart != binEnd || !fParent) {
8991 throw std::runtime_error(TString::Format("%s is a bin - only has one value", GetName()));
8992 }
8993 return fParent->GetBinContents(fBinNumber, fBinNumber);
8994 }
8995 std::vector<double> out;
8996 if (get<RooAbsData>()) {
8997 auto g = BuildGraph(
8998 nullptr,
8999 (binStart != -1 ||
9000 binEnd != -1) /*include points for zeros unless we are asking for a single point with start=end=-1*/);
9001 if (!g) {
9002 return out;
9003 }
9004 if (binStart == binEnd && binStart == -1) {
9005 // integral over all bins if getting bin content -1
9006 double integral(0);
9007 for (int i = 0; i < g->GetN(); i++)
9008 integral += g->GetPointY(i);
9009 out.push_back(integral);
9010 delete g;
9011 return out;
9012 }
9013 for (int i = binStart - 1; i < g->GetN() && (binEnd == 0 || i < binEnd); i++) {
9014 out.push_back(g->GetPointY(i));
9015 }
9016 delete g;
9017 return out;
9018 }
9019
9020 bool doIntegral = false;
9021 if (binStart == binEnd && binStart == -1) {
9022 binStart = -1;
9023 binEnd = -1;
9024 doIntegral = true;
9025 } // return integral if request bin -1
9026 auto h = BuildHistogram(nullptr, false, false, binStart, binEnd);
9027 if (!h) {
9028 throw std::runtime_error(TString::Format("%s has no content", GetName()));
9029 }
9030 if (binEnd == 0) {
9031 binEnd = h->GetNbinsX();
9032 }
9033 if (doIntegral) {
9034 double tot = 0;
9035 for (int i = 1; i <= h->GetNbinsX(); i++) {
9036 tot += h->GetBinContent(i);
9037 }
9038 out.push_back(tot);
9039 } else {
9040 for (int i = binStart; i <= binEnd; i++) {
9041 out.push_back(h->GetBinContent(i));
9042 }
9043 }
9044 delete h;
9045 return out;
9046}
9047
9049{
9050 if (auto a = get<RooAbsArg>(); a) {
9051 // go through servers looking for 'main' thing
9052 for (auto &l : a->servers()) {
9053 if (l->getAttribute("MAIN_MEASUREMENT") || l->InheritsFrom("RooRealSumPdf") || l->InheritsFrom("RooAddPdf")) {
9054 return xRooNode(*l, *this);
9055 }
9056 }
9057 // the main child of a RooProduct is one that has the same name (/alias) as the product (except if is a bin
9058 // factor)
9059 if (a->IsA() == RooProduct::Class() && fBinNumber == -1) {
9060 for (auto &l : factors()) {
9061 if (strcmp(l->GetName(), GetName()) == 0) {
9062 return *l;
9063 }
9064 }
9065 }
9066 }
9067 return xRooNode();
9068}
9069
9071{
9072 if (auto o = get(); o) {
9073 o->Inspect();
9074 } else {
9076 }
9077}
9078
9079bool TopRightPlaceBox(TPad *p, TObject *o, double w, double h, double &xl, double &yb)
9080{
9081#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
9082 // reinitialize collide grid because the filling depends on fUxmin and fUxmax (and ymin ymax too)
9083 // and these aren't filled on the first time we do the placement (they init to 0 and 1), but will be filled
9084 // subsequently
9085 for (int i = 0; i < p->fCGnx; i++) {
9086 for (int j = 0; j < p->fCGny; j++) {
9087 p->fCollideGrid[i + j * p->fCGnx] = true;
9088 }
9089 }
9090 p->FillCollideGrid(o);
9091 Int_t iw = (int)(p->fCGnx * w);
9092 Int_t ih = (int)(p->fCGny * h);
9093
9094 Int_t nxmax = p->fCGnx - iw - 1 - p->fCGnx * p->GetRightMargin();
9095 Int_t nymax = p->fCGny - ih - 1 - p->fCGny * p->GetTopMargin();
9096
9097 for (Int_t j = nymax; j >= 0; j--) {
9098 for (Int_t i = nxmax; i >= 0; i--) {
9099 if (p->Collide(i, j, iw, ih)) {
9100 continue;
9101 } else {
9102 xl = (double)(i) / (double)(p->fCGnx);
9103 yb = (double)(j) / (double)(p->fCGny);
9104 return true;
9105 }
9106 }
9107 }
9108 return false;
9109#else
9110 return p->PlaceBox(o, w, h, xl, yb, "trw");
9111#endif
9112}
9113
9114TPaveText *getPave(const char *name = "labels", bool create = true, bool doPaint = false)
9115{
9116 if (auto p = dynamic_cast<TPaveText *>(gPad->GetPrimitive(name)); p) {
9117 if (doPaint)
9118 gPad->PaintModified(); //-- slows down x11 so trying to avoid
9119 return p;
9120 }
9121 if (!create) {
9122 return nullptr;
9123 }
9124 auto l = new TPaveText(gPad->GetLeftMargin() + 0.02, 1. - gPad->GetTopMargin() - 0.08, 0.6,
9125 1. - gPad->GetTopMargin() - 0.08);
9126 l->SetBorderSize(0);
9127 if (l->GetTextSize() == 0)
9128 l->SetTextSize(gStyle->GetTitleYSize());
9129
9131 // l->SetMargin(0);
9132 l->SetFillStyle(0);
9133 l->SetName(name);
9134 l->Draw();
9135 l->ConvertNDCtoPad();
9136 return l;
9137}
9138
9139TLegend *getLegend(bool create = true, bool doPaint = false)
9140{
9141 if (auto p = dynamic_cast<TLegend *>(gPad->GetPrimitive("legend")); p) {
9142 double x;
9143 double y;
9144 double w = p->GetX2NDC() - p->GetX1NDC();
9145 double h = p->GetY2NDC() - p->GetY1NDC();
9146 if (doPaint)
9147 gPad->PaintModified(); //-- slows down x11 so trying to avoid
9148 if (TopRightPlaceBox(dynamic_cast<TPad *>(gPad), p, w, h, x, y)) {
9149 // squash inside the frame ..
9150 // std::cout << gPad->GetName() << ":" << x << " , " << y << " , " << w << " , " << h << std::endl;
9151 x = std::max(x, (gPad->GetLeftMargin() + 0.02));
9152 y = std::max(y, (gPad->GetBottomMargin() + 0.02));
9153 x = std::min(x, (1. - gPad->GetRightMargin() - 0.02) - w);
9154 y = std::min(y, (1. - gPad->GetTopMargin() - 0.02) - h);
9155 h = std::min(h, (1. - gPad->GetTopMargin() - 0.02) - y);
9156 w = std::min(w, (1. - gPad->GetRightMargin() - 0.02) - x);
9157 // std::cout << gPad->GetName() << ":" << x << " , " << y << " , " << h << " , " << w << std::endl;
9158 p->SetX1NDC(x);
9159 p->SetY1NDC(y);
9160 p->SetX2NDC(x + w);
9161 p->SetY2NDC(y + h);
9162 gPad->Modified();
9163 }
9164 return p;
9165 }
9166 // look for a parent pad called 'legend' and create it there if existing
9167 auto p = gPad;
9168 while ((p != p->GetMother()) && (p = p->GetMother())) {
9169 if (auto q = dynamic_cast<TVirtualPad *>(p->GetPrimitive("legend")); q) {
9170 q->Modified();
9171 p = q;
9172 break;
9173 }
9174 }
9175 auto tmpPad = gPad;
9176 TLegend *l = nullptr;
9177 if (p && strcmp(p->GetName(), "legend") == 0) {
9178 if (l = dynamic_cast<TLegend *>(p->GetPrimitive("legend")); l || !create)
9179 return l;
9180 p->cd();
9181 l = new TLegend(gPad->GetLeftMargin(), 1. - gPad->GetTopMargin(), 1. - gPad->GetRightMargin(),
9182 gPad->GetBottomMargin());
9183 l->SetBorderSize(1); // ensure has a border
9184 } else {
9185 if (!create)
9186 return nullptr;
9187 l = new TLegend(0.6, 1. - gPad->GetTopMargin() - 0.08, 0.75, 1. - gPad->GetTopMargin() - 0.08);
9188 l->SetBorderSize(0);
9189 // legend text will be required to match y-axis
9190 if (l->GetTextSize() == 0) {
9191 l->SetTextSize(gStyle->GetTitleYSize());
9192 l->SetTextFont(gStyle->GetTitleFont("Y"));
9193 }
9194 }
9196 // l->SetMargin(0);
9197 l->SetFillStyle(0);
9198 l->SetName("legend");
9199 l->Draw();
9200 l->ConvertNDCtoPad();
9201 tmpPad->cd();
9202 return l;
9203}
9204
9205std::string formatLegendString(const std::string &s)
9206{
9207 auto i = s.find("\n");
9208 if (i == std::string::npos) {
9209 return s;
9210 }
9211 return std::string("#splitline{") + s.substr(0, i) + "}{" + formatLegendString(s.substr(i + 1)) + "}";
9212}
9213
9214void addLegendEntry(TObject *o, const char *title, const char *opt)
9215{
9216 auto l = getLegend();
9217 if (!l)
9218 return;
9219 // check for entry already existing with same title
9220 for (auto a : *l->GetListOfPrimitives()) {
9221 if (formatLegendString(title) == dynamic_cast<TLegendEntry *>(a)->GetLabel())
9222 return;
9223 }
9224 if (l->GetListOfPrimitives()->GetEntries() > 20)
9225 return; // todo: create an 'other' entry?
9226
9227 auto e = l->AddEntry(o, formatLegendString(title).c_str(), opt);
9228 // move to top of the legend (we add things in at the top)
9229 l->GetListOfPrimitives()->RemoveLast();
9230 l->GetListOfPrimitives()->AddFirst(e);
9231 if (auto nObj = l->GetListOfPrimitives()->GetEntries(); nObj > 0) {
9232 // each entry takes up 0.05 ... maximum of N*(N+4) (where N is # cols) before next column
9233 int nn = l->GetNColumns();
9234 nn *= (nn + 4);
9235 if (nObj > 1 && (nObj % nn) == 1) {
9236 l->SetNColumns(l->GetNColumns() + 1);
9237 if (l->GetBorderSize() == 0) {
9238 l->SetX1NDC(l->GetX2NDC() - 0.15 * l->GetNColumns());
9239 }
9240 }
9241 if (l->GetBorderSize() == 0) {
9242 l->SetY1NDC(l->GetY2NDC() - 0.05 * gPad->GetHNDC() * std::ceil((double(nObj) / l->GetNColumns())));
9243 }
9244 }
9245
9246 getLegend(); // to mark modified
9247}
9248
9249// this exists to avoid calling update excessively because it slows down x11 ... but still
9250// need to call update twice if have a legend drawn in order to relocate it.
9252public:
9253 PadRefresher(TVirtualPad *p) : fPad(p) { nExisting++; }
9255 {
9256 if (fPad) {
9257 getLegend(false, true);
9258 fPad->GetCanvas()->Paint();
9259 fPad->GetCanvas()->Update();
9260#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 30, 00)
9261 fPad->GetCanvas()->ResetUpdated(); // stops previous canvas being replaced in a jupyter notebook
9262#endif
9263 fPad->cd();
9264 }
9265 nExisting--;
9266 }
9267 TVirtualPad *fPad = nullptr;
9268 static int nExisting;
9269};
9270
9272
9274{
9275 // in order to catch exceptions to prevent crash of GUI, do this:
9276 if (gROOT->FromPopUp()) {
9277 gROOT->SetFromPopUp(false);
9278 try {
9279 Draw(opt);
9280 } catch (const std::exception &e) {
9281 new TGMsgBox(
9282 gClient->GetRoot(),
9283 (gROOT->GetListOfBrowsers()->At(0))
9284 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
9285 : gClient->GetRoot(),
9286 "Exception", e.what(),
9287 kMBIconExclamation); // deletes self on dismiss?
9288 }
9289 gROOT->SetFromPopUp(true);
9290 return;
9291 }
9292
9293 TString sOpt2(opt);
9294 sOpt2.ToLower();
9295 if (!get() && !IsFolder() && !sOpt2.Contains("x="))
9296 return;
9297
9298 if (auto mc = get<RooStats::ModelConfig>()) {
9299 xRooNode(*mc->GetPdf(), fParent).Draw(opt); // draw the pdf of the config
9300 } else if (auto ir = get<RooStats::HypoTestInverterResult>()) {
9301 xRooHypoSpace(ir).Draw(opt);
9303 return;
9304 } else if (get<RooStats::HypoTestResult>()) {
9305 if (gPad)
9306 gPad->Clear();
9307 xRooNLLVar::xRooHypoPoint(std::dynamic_pointer_cast<RooStats::HypoTestResult>(fComp)).Draw(opt);
9308 {
9309 PadRefresher p(gPad); // refreshes the pad
9310 }
9312 return;
9313 }
9314
9315 if (sOpt2 == "pcls" && get<RooRealVar>() && fParent && fParent->get<RooAbsPdf>()) {
9316 // use the first selected dataset
9317 auto _dsets = fParent->datasets();
9318 // bool _drawn=false;
9319 TString dsetName = "";
9320 for (auto &d : _dsets) {
9321 if (d->get()->TestBit(1 << 20)) {
9322 dsetName = d->get()->GetName();
9323 break;
9324 }
9325 }
9326 auto hs = fParent->nll(dsetName.Data()).hypoSpace(get<RooRealVar>()->GetName());
9327 hs.limits("cls visualize");
9328 hs.SetName(TUUID().AsString());
9329 if (ws()) {
9330 ws()->import(*hs.result());
9331 }
9332 return;
9333 }
9334
9335 if (auxFunctions.empty()) {
9336 // add the defaults: Ratio and Signif
9338 "Ratio",
9339 [](double a, double b, double) {
9340 if (a == 0)
9341 return 0.;
9342 if (b == 0 && a == 0)
9343 return 1.;
9344 return a / b;
9345 },
9346 true);
9348 "Signif",
9349 [](double n, double b, double sigma) {
9350 double t0 = 0;
9351 if (sigma <= 0.) {
9352 // use simplified expression ...
9353 t0 = 2. * (((n == 0) ? 0 : n * log(n / b)) - (n - b));
9354 } else {
9355 double sigma2 = sigma * sigma;
9356 double b_hathat = 0.5 * (b - sigma2 + sqrt(pow(b - sigma2, 2) + 4 * n * sigma2));
9357 // double s_hat = n - m;
9358 // double b_hat = m;
9359 t0 = 2. * (((n == 0) ? 0 : n * log(n / b_hathat)) + b_hathat - n + pow(b - b_hathat, 2) / (2. * sigma2));
9360 }
9361 if (t0 < 0)
9362 return 0.; // can happen from numerical precision
9363 return (n >= b) ? sqrt(t0) : -sqrt(t0);
9364 },
9365 false);
9366 }
9367
9368 TString sOpt(opt);
9369
9370 RooAbsLValue *v = nullptr;
9371 std::vector<double> xPoints;
9372 if (sOpt2.Contains("x=")) {
9373 // specifying a particular var to scan over ...
9374 int _idx = sOpt2.Index("x=");
9375 int _eidx = sOpt2.Index(';', _idx);
9376 TString varPart = sOpt(_idx + 2, (_eidx < 0 ? sOpt2.Length() : _eidx) - (_idx + 2));
9378 // if varName is of form str(num,num,num) then can infer scan points
9379 if (auto _idx2 = varPart.Index("("); _idx2 > 0) {
9380 varName = varPart(0, _idx2);
9381 TStringToken pattern(TString(varPart(_idx2 + 1, varPart.Length() - _idx2 - 2)), ",");
9382 double min(0);
9383 double max(0);
9384 int nBins = 0;
9385 int ii = 0;
9386 while (pattern.NextToken()) {
9387 TString s = pattern;
9388 if (ii == 0) {
9389 nBins = s.Atoi();
9390 } else if (ii == 1) {
9391 min = s.Atof();
9392 } else if (ii == 2) {
9393 max = s.Atof();
9394 }
9395 ii++;
9396 }
9397 if (nBins > 100)
9398 nBins = 100; // limit scanning to 100 points
9399 if (nBins > 1) {
9400 for (double x = min; x <= max; x += (max - min) / (nBins - 1)) {
9401 xPoints.push_back(x);
9402 }
9403 } else if (nBins == 1)
9404 xPoints.push_back((min + max) / 2.);
9405 }
9406 v = getObject<RooAbsLValue>(varName.Data()).get();
9407 if (!v) {
9408 throw std::runtime_error(TString::Format("Could not find variable %s", varName.Data()));
9409 }
9410 if (xPoints.empty() && !obs().find(varName.Data()) &&
9411 dynamic_cast<RooAbsRealLValue *>(v)) { // will draw obs as regular (e.g. hist)
9412 double tmp = static_cast<RooAbsRealLValue *>(v)->getVal();
9413 for (int i = 0; i < v->numBins(GetName()); i++) {
9414 v->setBin(i, GetName());
9415 xPoints.push_back(static_cast<RooAbsRealLValue *>(v)->getVal());
9416 }
9417 static_cast<RooAbsRealLValue *>(v)->setVal(tmp);
9418 }
9419 sOpt2 = TString(sOpt2(0, _idx)) + sOpt2(_idx + 2 + varPart.Length() + 1, sOpt2.Length());
9420 sOpt = TString(sOpt(0, _idx)) + sOpt(_idx + 2 + varPart.Length() + 1, sOpt.Length());
9421 }
9422 TString forceNames = "";
9423 if (sOpt2.Contains("force")) {
9424 // force plots show how much NLL changes wrt to a change of variables
9425 if (get<RooRealVar>() && fParent && fParent->get<RooAbsPdf>()) {
9426 // assume want force of this parameter from the parent pdf
9427 TString ff = sOpt(sOpt2.Index("force"), sOpt2.Index("force") + 5);
9428 sOpt.ReplaceAll(ff, TString::Format("force%s", get()->GetName()));
9429 fParent->Draw(sOpt);
9430 return;
9431 } else if (get<RooAbsPdf>()) {
9432 // extract the parameter(s) to calculate force for
9433 forceNames = sOpt(sOpt2.Index("force") + 5, sOpt2.Length());
9434 sOpt = sOpt(0, sOpt2.Index("force"));
9435 sOpt2 = sOpt2(0, sOpt2.Index("force"));
9436 } else {
9437 Error("Draw", "Can only compute forces with PDFs");
9438 return; // don't throw because will cause browser to exit if done from there
9439 }
9440 }
9441 bool hasOverlay = sOpt2.Contains("overlay");
9442 TString overlayName = "";
9443 if (hasOverlay) {
9444 // whatever follows overlay is the variation name
9445 overlayName = sOpt(sOpt2.Index("overlay") + 7, sOpt2.Length());
9446 sOpt = sOpt(0, sOpt2.Index("overlay"));
9447 sOpt2 = sOpt2(0, sOpt2.Index("overlay"));
9448 }
9449 if (sOpt2.Contains("ratio") && !sOpt2.Contains("auxratio"))
9450 sOpt += "auxRatio";
9451 if (sOpt2.Contains("significance") && !sOpt2.Contains("auxsignif"))
9452 sOpt += "auxSignif";
9453
9454 std::string auxPlotTitle;
9455 for (auto &[k, _] : auxFunctions) {
9456 if (sOpt.Contains(TString::Format("aux%s", k.c_str()))) {
9457 auxPlotTitle = k;
9458 }
9459 sOpt.ReplaceAll(TString::Format("aux%s", k.c_str()), "");
9460 }
9461
9462 sOpt.ToLower();
9463 sOpt.ReplaceAll("ratio", "");
9464 sOpt.ReplaceAll("significance", ""); // remove old option if still given
9465 bool nostack = sOpt.Contains("nostack");
9466 sOpt.ReplaceAll("nostack", "");
9467 bool hasSame = sOpt.Contains("same");
9468 sOpt.ReplaceAll("same", "");
9469 bool hasGoff = sOpt.Contains("goff");
9470 sOpt.ReplaceAll("goff", "");
9471 bool hasFR = sOpt.Contains("pull") && !get<RooFitResult>();
9472 sOpt.ReplaceAll("pull", "");
9473 bool hasText = sOpt.Contains("text");
9474 bool hasTexte = sOpt.Contains("texte");
9475 bool hasErrorOpt = sOpt.Contains("e");
9476 sOpt.ReplaceAll("e", "");
9477 if (hasTexte) {
9478 sOpt.ReplaceAll("txt", "texte");
9479 } else if (hasText) {
9480 sOpt.ReplaceAll("txt", "text");
9481 }
9482 if (auxPlotTitle == "Signif")
9483 hasErrorOpt = true; // must calculate error to calculate significance
9484 if (hasOverlay)
9485 hasSame = true; // when overlaying must be putting on same
9486
9487 TVirtualPad *pad = gPad;
9488
9489 TH1 *hAxis = nullptr;
9490
9491 auto clearPad = []() {
9492 gPad->Clear();
9493 if (gPad->GetNumber() == 0) {
9494 gPad->SetBottomMargin(gStyle->GetPadBottomMargin());
9495 gPad->SetTopMargin(gStyle->GetPadTopMargin());
9496 gPad->SetLeftMargin(gStyle->GetPadLeftMargin());
9497 gPad->SetRightMargin(gStyle->GetPadRightMargin());
9498 }
9499 // if (gPad == gPad->GetCanvas()) {
9500 // gPad->GetCanvas()->SetCanvasSize( gPad->GetCanvas()->GetWindowWidth() - 4,
9501 // gPad->GetCanvas()->GetWindowHeight() - 28 );
9502 // }
9503 };
9504
9505 if (!hasSame || !pad) {
9506 if (!pad) {
9508 pad = gPad;
9509 }
9510
9511 } else {
9512 // get the histogram representing the axes
9513 hAxis = dynamic_cast<TH1 *>(pad->GetPrimitive("axis"));
9514 if (!hAxis) {
9515 for (auto o : *pad->GetListOfPrimitives()) {
9516 if (hAxis = dynamic_cast<TH1 *>(o); hAxis)
9517 break;
9518 }
9519 }
9520 if (hAxis && !v) {
9521 v = getObject<RooAbsLValue>(hAxis->GetXaxis()->IsAlphanumeric() ? hAxis->GetXaxis()->GetTimeFormatOnly()
9522 : hAxis->GetXaxis()->GetName())
9523 .get();
9524 }
9525 }
9526
9527 if (!hasSame) {
9528 if (gPad != gPad->GetCanvas()) {
9529 gPad->SetName(GetName()); // only rename the pad if its not the parent canvas
9530 }
9531 gPad->SetTitle(GetTitle());
9532 }
9533
9535
9536 auto adjustYRange = [&](double min, double max, TH1 *hh = nullptr, bool symmetrize = false) {
9537 if (!hh)
9538 hh = hAxis;
9539 // give max and min a buffer ...
9540 max += gStyle->GetHistTopMargin() * (max - min);
9541 if (min > 0)
9542 min = std::max(min * 0.9, min - gStyle->GetHistTopMargin() * (max - min));
9543 if (hh) {
9544 double ymin = hh->GetMinimum();
9545 double ymax = hh->GetMaximum();
9546 if (hh->GetMaximumStored() == -1111)
9547 ymax += gStyle->GetHistTopMargin() * (ymax - ymin);
9548 if (hh->GetMinimumStored() == -1111) {
9549 if (gStyle->GetHistMinimumZero() && ymax >= 0) {
9550 ymin = 0;
9551 } else if (ymin < 0) {
9552 ymin -= gStyle->GetHistTopMargin() * (ymax - ymin);
9553 } else {
9554 ymin = std::max(ymin * 0.9, ymin - gStyle->GetHistTopMargin() * (ymax - ymin));
9555 }
9556 // see TGLPlotPainter to complete the mimic, but we leave off here truncating @ 0 if ymax>0
9557 }
9558 // make ymax at least 3x bigger than biggest error if has error
9559 if (hh->GetSumw2()) {
9560 double smallestErrDown3 = -std::numeric_limits<double>::infinity();
9561 double smallestErrUp3 = std::numeric_limits<double>::infinity();
9562 for (int i = 1; i <= hh->GetNbinsX(); i++) {
9563 smallestErrDown3 = std::max(smallestErrDown3, hh->GetBinContent(i) - 3 * hh->GetBinError(i));
9564 smallestErrUp3 = std::min(smallestErrUp3, hh->GetBinContent(i) + 3 * hh->GetBinError(i));
9565 }
9566 max = std::max(max, smallestErrUp3);
9567 min = std::min(min, smallestErrDown3);
9568 }
9569 bool change = false;
9570 if (min < ymin) {
9571 ymin = min;
9572 change = true;
9573 }
9574 if (max > ymax) {
9575 ymax = max;
9576 change = true;
9577 }
9578 if (change) {
9579 // note: unfortunately when user 'unzooms' y axis it resets stored minimum to -1111, so lose range
9580 if (symmetrize) {
9581 double down = hh->GetBinContent(1) - ymin;
9582 double up = ymax - hh->GetBinContent(1);
9583 if (down > up) {
9584 ymax = hh->GetBinContent(1) + down;
9585 } else {
9586 ymin = hh->GetBinContent(1) - up;
9587 }
9588 }
9589 if (hh == hAxis && pad && !pad->GetLogy() && ymin > 0 && (log10(ymax) - log10(max)) >= 3) {
9590 // auto-log the pad
9591 pad->SetLogy();
9592 }
9593 if (hh == hAxis && pad && ymin == 0 && pad->GetLogy()) {
9594 ymin = 1e-2;
9595 }
9596 if (ymin == 0 && ymax > 10)
9597 ymin = 0.1; // adjust min so if user activates log scale it isn't bad
9598 hh->SetMinimum(ymin);
9599 hh->SetMaximum(ymax);
9600 hh->GetYaxis()->Set(1, ymin, ymax);
9601 hh->SetAxisRange(ymin, ymax, "Y");
9602 }
9603 }
9604 };
9605
9606 auto graphMinMax = [](TGraphAsymmErrors *gr) {
9607 double ymax = -std::numeric_limits<double>::infinity();
9608 double ymin = std::numeric_limits<double>::infinity();
9609 for (int i = 0; i < gr->GetN(); i++) {
9610 ymax = std::max(ymax, gr->GetPointY(i) + gr->GetErrorYhigh(i));
9611 ymin = std::min(ymin, gr->GetPointY(i) - gr->GetErrorYlow(i));
9612 }
9613 return std::make_pair(ymin, ymax);
9614 };
9615
9616 if (!xPoints.empty()) {
9617 // create a graph using GetContent
9619 out->SetName(GetName());
9620 out->SetTitle(GetTitle());
9621 out->SetFillColor(out->GetLineColor());
9622 out->SetMarkerStyle(0);
9623 out->SetFillStyle(hasErrorOpt ? 3005 : 0);
9624 double tmp = static_cast<RooAbsRealLValue *>(v)->getVal();
9625 for (auto &x : xPoints) {
9626 static_cast<RooAbsRealLValue *>(v)->setVal(x);
9627 out->AddPoint(x, GetContent());
9628 if (hasErrorOpt) {
9629 out->SetPointEYlow(out->GetN() - 1, GetError());
9630 out->SetPointEYhigh(out->GetN() - 1, out->GetErrorYlow(out->GetN() - 1)); // symmetric error for now
9631 }
9632 }
9633 static_cast<RooAbsRealLValue *>(v)->setVal(tmp);
9634 out->GetHistogram()->GetXaxis()->SetTitle(static_cast<RooAbsRealLValue *>(v)->GetTitle());
9635 out->SetBit(kCanDelete);
9636 out->Draw(TString(hasSame ? "L" : "AL") + (hasErrorOpt ? "3" : ""));
9637 return;
9638 }
9639
9640 if (hasFR) {
9641 // drawing the fitresult as a pull plot on a subpad, and rest of the draw elsewhere
9642 clearPad();
9643 pad->Divide(1, 2, 1e-9, 1e-9); //,0,0);
9644 pad->GetPad(1)->SetPad(0, 0.2, 1, 1);
9645 pad->GetPad(2)->SetPad(0, 0, 1, 0.2);
9646 TString optNoFR(opt);
9647 optNoFR.ReplaceAll("pull", "");
9648 pad->cd(1);
9649 Draw(optNoFR);
9650 pad->cd(2);
9651 auto _fr = fitResult();
9652 _fr.Draw();
9653 // switch into subpad
9654 gPad->cd(1);
9655 gPad->SetFillColor(kGray);
9656 gPad->GetFrame()->SetFillColor(kWhite);
9657 gPad->GetFrame()->SetFillStyle(1001);
9658 gPad->SetTopMargin(0);
9659 gPad->SetBottomMargin(0);
9660 gPad->SetName("pull");
9661 // split the pull graph into individual points -- for benefit of GUI status bar
9662 auto pullGraph = dynamic_cast<TGraphAsymmErrors *>(gPad->GetPrimitive("pulls"));
9663 if (!pullGraph) {
9664 Error("Draw", "Couldn't find pull graph");
9665 return;
9666 }
9667 pullGraph->SetName("nominal");
9668 TMultiGraph *mg = new TMultiGraph;
9669 mg->SetName("editables");
9670
9671 auto scaleHist = static_cast<TH1 *>(pullGraph->FindObject("scales"));
9672 if (!scaleHist)
9673 throw std::runtime_error("Could not find scales in fit result");
9674
9675 for (auto i = 0; i < pullGraph->GetN(); i++) {
9676 auto g = new TGraphAsymmErrors;
9677 g->SetName(scaleHist->GetXaxis()->GetBinLabel(i + 1));
9678 auto _p = dynamic_cast<RooRealVar *>(_fr.get<RooFitResult>()->floatParsFinal().find(g->GetName()));
9679 if (!_p) {
9680 Warning("Draw", "Found a non-var in the floatParsFinal list: %s - this shouldn't happen", g->GetName());
9681 continue;
9682 }
9683 g->SetTitle(TString::Format(
9684 "%s=%g +/- %s [%g,%g]", strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName(), _p->getVal(),
9685 _p->hasAsymError() ? TString::Format("(%g,%g)", _p->getAsymErrorHi(), _p->getAsymErrorLo()).Data()
9686 : TString::Format("%g", _p->getError()).Data(),
9687 scaleHist->GetBinContent(i + 1), scaleHist->GetBinError(i + 1)));
9688 g->SetPoint(0, pullGraph->GetPointX(i), pullGraph->GetPointY(i));
9689 g->SetPointEYhigh(0, pullGraph->GetErrorYhigh(i));
9690 g->SetPointEYlow(0, pullGraph->GetErrorYlow(i));
9691 g->SetEditable(true);
9692 g->SetHighlight(true);
9693 g->SetMarkerStyle(20);
9694 g->SetMarkerSize(0.5);
9695 mg->Add(g);
9696 }
9697 // gPad->GetListOfPrimitives()->Remove(pullGraph); delete pullGraph;
9698 mg->Draw("z0p");
9699 mg->SetBit(kCanDelete);
9700 auto _thisClone = new xRooNode("node", fComp, fParent);
9701 _thisClone->SetBit(kCanDelete);
9702 _thisClone->AppendPad();
9703
9704 // ensure statusbar visible for interactive plot
9705 // turned this off for now ... as not needed if doing through browser, status bar already there
9706 // if (gPad->GetCanvas() && !gPad->GetCanvas()->TestBit(TCanvas::kShowEventStatus)) {
9707 // gPad->GetCanvas()->ToggleEventStatus();
9708 // }
9709 gPad->AddExec("interactivePull", TString::Format("%s::Interactive_Pull()", ClassName()));
9710
9711 pad->cd();
9712 return;
9713 }
9714
9715 if (auto _simPdf = get<RooSimultaneous>();
9716 _simPdf && !(v && strcmp(_simPdf->indexCat().GetName(), dynamic_cast<TObject *>(v)->GetName()) == 0)) {
9717 auto _channels = bins();
9718 int _size = 0;
9719 for (auto &_v : _channels) {
9720 if (!_v->IsHidden())
9721 _size++;
9722 }
9723 if (!hasSame) {
9724 if (_size > 4) {
9725 // add a pad for the common legends
9726 _size++;
9727 }
9728 clearPad();
9729 pad->SetBorderSize(0);
9730 // if (pad->GetCanvas() == pad) {
9731 // if(_size>4) {
9732 // int n = _size;
9733 // Int_t w = 1, h = 1;
9734 // if (pad->GetCanvas()->GetWindowWidth() > pad->GetCanvas()->GetWindowHeight()) {
9735 // w = TMath::Ceil(TMath::Sqrt(n));
9736 // h = TMath::Floor(TMath::Sqrt(n));
9737 // if (w*h < n) w++;
9738 // } else {
9739 // h = TMath::Ceil(TMath::Sqrt(n));
9740 // w = TMath::Floor(TMath::Sqrt(n));
9741 // if (w*h < n) h++;
9742 // }
9743 // // adjust the window size to display only 4 in the window, with scroll bars
9744 // pad->GetCanvas()->SetCanvasSize( w*((pad->GetCanvas()->GetWindowWidth()-4)/2.) -16
9745 // ,h*((pad->GetCanvas()->GetWindowHeight()-28)/2.) - 16 );
9746 // } else {
9747 // //pad->GetCanvas()->Set(
9748 // w*(pad->GetCanvas()->GetWindowWidth()/2.),h*(pad->GetCanvas()->GetWindowHeight()/2.)) )
9749 // }
9750 // }
9751 dynamic_cast<TPad *>(pad)->DivideSquare(_size, 1e-9, 1e-9);
9752 if (_size > 5) {
9753 auto _pad = pad->GetPad(_size); // will use as the legend pad
9754 _pad->SetName("legend");
9755 // stretch the pad all the way to the left
9756 _pad->SetPad(_pad->GetXlowNDC(), _pad->GetYlowNDC(), 1.0, _pad->GetYlowNDC() + _pad->GetHNDC());
9757 // and make all the remaining pads transparent
9758 int x = _size;
9759 while (pad->GetPad(x + 1)) {
9760 pad->GetPad(x + 1)->SetFillStyle(0);
9761 x++;
9762 }
9763 }
9764 }
9765 int i = 0;
9766 auto &chanVar = const_cast<RooAbsCategoryLValue &>(_simPdf->indexCat());
9767 // auto _idx = chanVar.getIndex();
9768 auto _range = GetRange();
9769 std::vector<TString> chanPatterns;
9770 if (_range && strlen(_range)) {
9771 TStringToken pattern(_range, ",");
9772 while (pattern.NextToken()) {
9773 chanPatterns.emplace_back(pattern);
9774 }
9775 }
9776 for (auto &_v : _channels) {
9777 if (_v->IsHidden())
9778 continue;
9779 TString s(_v->GetName());
9780 pad->cd(++i);
9781 gPad->SetName(s);
9782 TString cName = s(s.Index('=') + 1, s.Length());
9783 chanVar.setLabel(cName);
9784 bool inRange = chanPatterns.empty();
9785 for (auto &p : chanPatterns) {
9786 if (chanVar.inRange(p)) {
9787 inRange = true;
9788 break;
9789 }
9790 }
9791 if (!inRange || !_v->get<RooAbsReal>()->isSelectedComp())
9792 gPad->SetFillColor(kGray);
9793 if (!hasSame && _size > 1 && (gStyle->GetTitleFont("Y") % 10) == 3)
9794 gPad->SetLeftMargin(std::min(gPad->GetLeftMargin() * (1. / gPad->GetWNDC()), 0.3));
9795 _v->Draw(opt);
9797 }
9798 pad->cd(0);
9799 gPad->Modified();
9800 // gPad->Update();
9801 return;
9802 }
9803
9804 if (!get() || get<RooArgList>()) {
9805 // is a group draw all the submembers
9806 browse();
9807 int _size = 0;
9808 // int _size = _channels.size(); // size(); if (find("!.vars")) _size--;
9809 for (auto &_v : *this) {
9810 if (_v->IsHidden())
9811 continue;
9812 if (strcmp(GetName(), ".vars") == 0) {
9813 // auto hide obs and "1" and const var
9814 if (_v->get<RooAbsArg>()->getAttribute("obs"))
9815 continue;
9816 if (strcmp(_v->get()->GetName(), "1") == 0 || strcmp(_v->get()->GetName(), "ONE") == 0 ||
9817 TString(_v->get()->GetName()).BeginsWith("binWidth_"))
9818 continue;
9819 if (_v->get()->InheritsFrom("RooConstVar"))
9820 continue;
9821 }
9822 TString s(_v->GetName());
9823 if (s.BeginsWith(".") || s.BeginsWith("!"))
9824 continue;
9825 _size++;
9826 }
9827 if (!hasSame) {
9828 clearPad();
9829 pad->SetBorderSize(0);
9830 dynamic_cast<TPad *>(pad)->DivideSquare(_size, 1e-9, 1e-9);
9831 }
9832 int i = 0;
9833 for (auto &_v : *this) {
9834 if (_v->IsHidden())
9835 continue;
9836 if (strcmp(GetName(), ".vars") == 0) {
9837 // auto hide obs and "1" and const var
9838 if (_v->get<RooAbsArg>()->getAttribute("obs"))
9839 continue;
9840 if (strcmp(_v->get()->GetName(), "1") == 0 || strcmp(_v->get()->GetName(), "ONE") == 0 ||
9841 TString(_v->get()->GetName()).BeginsWith("binWidth_"))
9842 continue;
9843 if (_v->get()->InheritsFrom("RooConstVar"))
9844 continue;
9845 }
9846 TString s(_v->GetName());
9847 if (s.BeginsWith(".") || s.BeginsWith("!"))
9848 continue;
9849 pad->cd(++i);
9850 gPad->SetName(s);
9851 if (!hasSame && _size > 1 && (gStyle->GetTitleFont("Y") % 10) == 3)
9852 gPad->SetLeftMargin(std::min(gPad->GetLeftMargin() * (1. / gPad->GetWNDC()), 0.3));
9853 _v->Draw(opt);
9854 // pad->Modified();//pad->Update();
9856 }
9857 pad->cd(0);
9858 gPad->Modified();
9859 // gPad->Update();
9860 return;
9861 }
9862
9863 if (get()->InheritsFrom("RooProdPdf")) {
9864 // draw the main pdf, if there is one...
9865 auto _mainChild = mainChild();
9866 if (_mainChild) {
9867 _mainChild.Draw(opt);
9868 gPad->SetName(GetName());
9869 return;
9870 }
9871 }
9872
9873 if (auto fr = get<RooFitResult>(); fr) {
9874 if (sOpt.Contains("corr")) {
9875 // do correlation matrix
9876 // if a number follows 'corr', reduce the correlation matrix to show only the most extreme correlations
9877 int numCorrs = TString(sOpt(sOpt.Index("corr") + 4, sOpt.Length())).Atoi();
9878 if(numCorrs==0) numCorrs = fr->correlationMatrix().GetNcols();
9879
9880 TH2* hist = nullptr;
9881 if(numCorrs < fr->correlationMatrix().GetNcols()) {
9882 // need to reduce
9883 std::set<std::pair<double,size_t>> maxCorrs;
9884 for(int i=0;i<fr->correlationMatrix().GetNcols();i++) {
9885 double maxCorr = 0;
9886 for(int j=0;j<fr->correlationMatrix().GetNcols();j++) {
9887 if(j==i) continue;
9888 maxCorr = std::max(std::abs(fr->correlationMatrix()(i,j)),maxCorr);
9889 }
9890 maxCorrs.insert({maxCorr,i});
9891 }
9892 std::vector<size_t> topN;
9893 int c=0;
9894 for(auto itr = maxCorrs.rbegin();itr != maxCorrs.rend(); ++itr) {
9895 topN.push_back(itr->second);
9896 c++;
9897 if (c == numCorrs)
9898 break;
9899 }
9900 hist = new TH2D(fr->GetName(),TString::Format("%s - Top %d correlations",fr->GetTitle(),numCorrs),numCorrs,0,numCorrs,numCorrs,0,numCorrs);
9901 for(size_t i = 0;i<topN.size();i++) {
9902 hist->GetXaxis()->SetBinLabel(i+1,fr->floatParsFinal().at(topN.at(i))->GetTitle());
9903 hist->GetYaxis()->SetBinLabel(numCorrs-i,fr->floatParsFinal().at(topN.at(i))->GetTitle());
9904 for(size_t j = 0;j<topN.size();j++) {
9905 hist->Fill(i+0.5,numCorrs-j-0.5,fr->correlationMatrix()(topN.at(i),topN.at(j)));
9906 }
9907 }
9908 hist->SetMinimum(-1); hist->SetMaximum(1);
9909
9910 } else {
9911 hist = fr->correlationHist(fr->GetName());
9912 hist->SetTitle(fr->GetTitle());
9913 }
9914
9915 hist->SetBit(kCanDelete);
9916 hist->Scale(100);
9917 hist->SetStats(false);
9918 hist->SetDirectory(nullptr);
9920 gStyle->SetPaintTextFormat(".1f");
9921 hist->GetXaxis()->SetTickSize(0);
9922 hist->GetYaxis()->SetTickSize(0);
9923 hist->SetMinimum(-100);
9924 hist->Draw(sOpt);
9926 gPad->SetGrid(1, 1);
9927 gPad->SetLogy(0);
9928 gPad->SetLogx(0);
9929 return;
9930 }
9931
9932 if (sOpt.Contains("brakdown")) { // e will have been removed above
9933
9934 // breakdown is quadrature difference between total error and conditional error
9935 // group by 'group' attribute
9936
9937 std::string poiName;
9938 if (sOpt.Contains("brakdown:")) {
9939 TString sOpt3(opt);
9940 poiName = sOpt3(sOpt3.Index("breakdown:") + 10, sOpt3.Length());
9941 } else {
9942 std::unique_ptr<RooAbsCollection> _poi(fr->floatParsFinal().selectByAttrib("poi", true));
9943 if (_poi->empty()) {
9944 throw std::runtime_error("No floating poi in the fit");
9945 } else if (_poi->size() != 1) {
9946 throw std::runtime_error("Multiple poi in the fit");
9947 }
9948 poiName = _poi->first()->GetName();
9949 }
9950 RooRealVar *poi = dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(poiName.c_str()));
9951 if (!poi) {
9952 throw std::runtime_error(TString::Format("Cannot find parameter %s", poiName.c_str()));
9953 }
9954 std::set<std::string> groups;
9955 for (auto p : fr->floatParsFinal()) {
9956 if (p == poi) {
9957 continue;
9958 } else if (p->getStringAttribute("group")) {
9959 groups.insert(p->getStringAttribute("group"));
9960 } else {
9961 groups.insert(p->GetTitle());
9962 }
9963 }
9964
9965 auto roundedVal = xRooFit::matchPrecision(std::pair(poi->getVal(), poi->getError()));
9966
9967 TPie *pie = new TPie(TString::Format("breakdown:%s", poi->GetName()),
9968 TString::Format("%s: %g #pm %g", poi->GetTitle(), roundedVal.first, roundedVal.second),
9969 groups.size() + 1);
9970
9971 // for display of errors will go to one extra dp ...
9972 roundedVal.second *= .1;
9973
9974 // do breakdown by removing parameters in blocks according to groups and seeing impact on variance
9975 // this will give the correct sum but will be order-dependent if there are correlations between
9976 // groups. therefore we will stick with group-by-group
9977 // RooArgList pars(fr->floatParsFinal()); // pars to not condition on
9978 // double variance = pow(dynamic_cast<RooRealVar*>(poi)->getError(),2);
9979 int i = 0;
9980 for (auto group : groups) {
9981 RooArgList pars(fr->floatParsFinal()); // pars to not condition on
9982 double variance = pow(dynamic_cast<RooRealVar *>(poi)->getError(), 2);
9983 for (auto p : fr->floatParsFinal()) {
9984 if (p == poi) {
9985 continue;
9986 } else if ((p->getStringAttribute("group") && group == p->getStringAttribute("group")) ||
9987 (!p->getStringAttribute("group") && group == p->GetTitle())) {
9988 // conditioning on this parameter ... remove from pars list
9989 pars.remove(*p);
9990 }
9991 }
9992 int idx = pars.index(poiName.c_str());
9993 double reducedVar = fr->conditionalCovarianceMatrix(pars)(idx, idx);
9994 if (reducedVar > variance) {
9995 Warning("Draw", "breakdown group %s variance bigger than preceding?", group.c_str());
9996 pie->SetEntryVal(i, 0);
9997 pie->SetEntryLabel(i, TString::Format("%s: NaN", group.c_str()));
9998 } else {
9999 pie->SetEntryVal(i, variance - reducedVar);
10001 std::pair(sqrt(variance - reducedVar), roundedVal.second)); // r.first will be the rounded error
10002 if (r.first > 0) {
10003 pie->SetEntryLabel(i, TString::Format("%s: %g", group.c_str(), r.first));
10004 } else {
10005 pie->SetEntryLabel(i, group.c_str()); // suppress labels for negligible errors.
10006 }
10007 }
10008 pie->SetEntryFillColor(i, TColor::GetColorPalette(TColor::GetNumberOfColors() * i / pie->GetEntries()));
10009 // variance = reducedVar;
10010 i++;
10011 }
10012 // remaining variance is statistical=
10013 double variance = fr->conditionalCovarianceMatrix(*poi)(0, 0);
10014 auto r =
10015 xRooFit::matchPrecision(std::pair(sqrt(variance), roundedVal.second)); // r.first will be the rounded error
10016 pie->SetEntryVal(i, variance);
10017 pie->SetEntryLabel(i, TString::Format("stat: %g", r.first));
10018 pie->SetEntryFillColor(i, TColor::GetColorPalette(TColor::GetNumberOfColors() * i / pie->GetEntries()));
10019 pie->SetBit(kCanDelete);
10020 pie->SetRadius(0.17);
10021 pie->SetTextSize(gStyle->GetTitleYSize());
10022 pie->Draw("NOL");
10023 return;
10024 }
10025
10026 // plot pull or impact
10028 out->SetName(TString::Format("%s_pull", fr->GetName()));
10029 out->SetTitle("Fit Result Pulls");
10030 std::vector<TString> graphLabels;
10032 ugraph->SetName(TString::Format("%s_pull_unconstrained", fr->GetName()));
10033 ugraph->SetTitle("Fit Result Pulls");
10034 std::vector<TString> ugraphLabels;
10035 std::map<std::string, double> scale;
10036 std::map<std::string, double> offset;
10037 for (auto &p : fr->floatParsFinal()) {
10038 auto _v = dynamic_cast<RooRealVar *>(p);
10039 if (!_v)
10040 continue;
10041
10042 if (std::isnan(_v->getErrorHi()) || std::isnan(_v->getErrorLo())) {
10043 Warning("Draw", "%s error is invalid", _v->GetName());
10044 }
10045
10046 // need to get constraint mean and error parameters ....
10047 // look for normal gaussian and poisson cases
10048 double prefitError = 0;
10049 double prefitVal = 0;
10050 double customScale = 0;
10051 if (auto ip =
10052 dynamic_cast<RooRealVar *>(fr->floatParsInit().find(p->GetName()))) { // handles if no prefit available
10053 prefitError = ip->getError();
10054 prefitVal = ip->getVal();
10055 };
10056
10057 std::shared_ptr<xRooNode> pConstr;
10058 if (fParent && fParent->getObject<RooRealVar>(p->GetName())) {
10059 auto _vv = fParent->getObject<RooRealVar>(p->GetName());
10060 if (_vv->hasRange("pullScale")) {
10061 customScale = (_vv->getMax("pullScale") - _vv->getMin("pullScale")) / 2.;
10062 }
10063 auto _constr = xRooNode(_vv, *this).constraints();
10064 for (auto &c : _constr) {
10065 if (c->get<RooPoisson>() || c->get<RooGaussian>()) {
10066 // require parameter to be a direct server of the constraint pdf to count if its a gaussian
10067 bool isServer = true;
10068 if (c->get<RooGaussian>()) {
10069 isServer = false;
10070 for (auto s : c->get<RooAbsArg>()->servers()) {
10071 if (strcmp(s->GetName(), p->GetName()) == 0) {
10072 isServer = true;
10073 break;
10074 }
10075 }
10076 }
10077 if (isServer) {
10078 pConstr = c;
10079 break;
10080 }
10081 }
10082 }
10083 }
10084 if (pConstr) {
10085
10086 // there will be 3 deps, one will be this par, the other two are the mean and error (or error^2 in case of
10087 // poisson
10088
10089 // std::cout << p->GetName() << " extracted " << prefitVal << " " << prefitError << " from ";
10090 // pConstr->deps().Print();
10091 pConstr->browse();
10092 if (pConstr->get<RooPoisson>() && pConstr->find(".x")) {
10093 std::string xName = pConstr->find(".x")->get()->GetName();
10094 prefitVal = pConstr->find(".x")->get<RooAbsReal>()->getVal();
10095 for (auto &_d : pConstr->vars()) {
10096 if (strcmp(p->GetName(), _d->get()->GetName()) == 0)
10097 continue;
10098 if (xName == _d->get()->GetName())
10099 continue;
10100 if (_d->get<RooAbsReal>()->getVal())
10101 prefitError = _d->get<RooAbsReal>()->getVal();
10102 }
10103 // prefitVal will be the global observable value, need to divide that by tau
10105 // prefiterror will be tau ... need 1/sqrt(tau) for error
10106 prefitError = 1. / sqrt(prefitError);
10107 } else if (auto _g = pConstr->get<RooGaussian>(); _g) {
10108 prefitError =
10109 (pConstr->find(".sigma")) ? pConstr->find(".sigma")->get<RooAbsReal>()->getVal() : prefitError;
10110 prefitVal =
10111 (pConstr->find(".x")) ? pConstr->find(".x")->get<RooAbsReal>()->getVal() : 0; // usually the globs
10112 if (pConstr->find(".x") &&
10113 strcmp(p->GetName(), pConstr->find(".x")->get<RooAbsReal>()->GetName()) == 0) {
10114 // hybrid construction case,
10115 prefitVal = pConstr->find(".mean")->get<RooAbsReal>()->getVal();
10116 }
10117 }
10118
10119 if (customScale)
10121 if (prefitError == 0) {
10122 Warning("Draw", "failed to determine prefit error of %s, using post-fit error", p->GetName());
10123 prefitError = _v->getError();
10124 }
10125 out->SetPoint(out->GetN(), out->GetN(), (_v->getVal() - prefitVal) / prefitError);
10126 out->SetPointError(out->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
10127 (_v->getErrorHi()) / prefitError);
10128 graphLabels.push_back(p->GetName());
10129 scale[p->GetName()] = prefitError;
10130 offset[p->GetName()] = prefitVal;
10131 } else if (!fParent) {
10132 // no parent to determine constraints from ... prefitError=0 will be the unconstrained ones
10133 if (customScale)
10135 if (prefitError == 0) {
10136 // uses range of var
10137 prefitError = (std::max({_v->getMax() - _v->getVal(), _v->getVal() - _v->getMin(), 4.}) / 4);
10138 ugraph->SetPoint(ugraph->GetN(), ugraph->GetN(), (_v->getVal() - prefitVal) / prefitError);
10139 ugraph->SetPointError(ugraph->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
10140 (_v->getErrorHi()) / prefitError);
10141 ugraphLabels.push_back(p->GetName());
10142 } else {
10143 out->SetPoint(out->GetN(), out->GetN(), (_v->getVal() - prefitVal) / prefitError);
10144 out->SetPointError(out->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
10145 (_v->getErrorHi()) / prefitError);
10146 graphLabels.push_back(p->GetName());
10147 }
10148 scale[p->GetName()] = prefitError;
10149 offset[p->GetName()] = prefitVal;
10150
10151 } else {
10152 // unconstrained (or at least couldn't determine constraint) ... use par range if no prefit error
10153 if (customScale)
10155 if (prefitError == 0) {
10156 prefitError = (std::max({_v->getMax() - _v->getVal(), _v->getVal() - _v->getMin(), 4.}) / 4);
10157 }
10158 ugraph->SetPoint(ugraph->GetN(), ugraph->GetN(), (_v->getVal() - prefitVal) / prefitError);
10159 ugraph->SetPointError(ugraph->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
10160 (_v->getErrorHi()) / prefitError);
10161 ugraphLabels.push_back(p->GetName());
10162 scale[p->GetName()] = prefitError;
10163 offset[p->GetName()] = prefitVal;
10164 }
10165 }
10166 auto graph = out;
10167
10168 // append ugraph points to end of graph
10169 for (int i = 0; i < ugraph->GetN(); i++)
10170 ugraph->SetPointX(i, i + graph->GetN());
10171 int nUnconstrained = ugraph->GetN();
10172 TList tmpList;
10173 tmpList.SetName("tmpList");
10174 tmpList.Add(ugraph);
10175 graph->Merge(&tmpList);
10176 tmpList.RemoveAll();
10177 delete ugraph;
10178 for (auto &l : ugraphLabels) {
10179 graphLabels.push_back(l);
10180 }
10181
10182 graph->SetBit(kCanDelete);
10183 graph->SetMarkerStyle(20);
10184 graph->SetMarkerSize(0.5);
10185
10186 graph->SetMaximum(4);
10187 graph->SetMinimum(-4);
10188
10189 bool doHorizontal =
10190 (!sOpt.Contains("impact") && sOpt.Contains("v")) || (sOpt.Contains("impact") && !sOpt.Contains("himpact"));
10191
10192 std::vector<std::pair<double, std::string>> covariances;
10193 /*double poiError = 0;*/ std::string poiName;
10194 double maxImpact = 0;
10195 if (sOpt.Contains("impact")) {
10196 if (sOpt.Contains("impact:")) {
10197 TString sOpt3(opt);
10198 poiName = sOpt3(sOpt3.Index("impact:") + 7, sOpt3.Length());
10199 } else {
10200 std::unique_ptr<RooAbsCollection> _poi(fr->floatParsFinal().selectByAttrib("poi", true));
10201 if (_poi->empty()) {
10202 throw std::runtime_error("No floating poi in the fit");
10203 } else if (_poi->size() != 1) {
10204 throw std::runtime_error("Multiple poi in the fit");
10205 }
10206 poiName = _poi->first()->GetName();
10207 }
10208 RooAbsArg *poi = fr->floatParsFinal().find(poiName.c_str());
10209 if (!poi) {
10210 throw std::runtime_error(TString::Format("Cannot find parameter %s", poiName.c_str()));
10211 }
10212 size_t poiIdx = fr->floatParsFinal().index(*poi);
10213 // put parameters in order of impact on the poi
10214
10215 // impact is regression coefficient * npError
10216 // relevant regression coefficient is cov / (npVariance)
10217 // i.e. DeltaX/sigmaX = [cov(X,Y)/(sigmaXsigmaY)]DeltaY/sigmaY
10218 // ... DeltaX = [cov(X,Y)/(sigmaY^2)]DeltaY
10219 // if DeltaY is just sigmaY then DeltaX = cov(X,Y)/sigmaY
10220
10221 for (auto &label : graphLabels) {
10222 covariances.emplace_back(fr->covarianceMatrix()(poiIdx, fr->floatParsFinal().index(label)) /
10223 dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(label))->getError(),
10224 label);
10225 }
10226 std::sort(covariances.begin(), covariances.end(),
10227 [&](std::pair<double, std::string> i, std::pair<double, std::string> j) {
10228 return doHorizontal ? (std::abs(i.first) < std::abs(j.first))
10229 : (std::abs(i.first) > std::abs(j.first));
10230 });
10231
10233 std::vector<TString> sortedLabels;
10234 maxImpact = (doHorizontal) ? covariances.back().first
10235 : covariances.front().first; // note: max impact is likely to be self variance
10236 for (auto &c : covariances) {
10237 if (c.second == poi->GetName()) {
10238 // poiError = sqrt(c.first);
10239 continue; // skip self
10240 }
10241 c.first *= 4. / (maxImpact * 1.2);
10242 sortedLabels.push_back(c.second);
10243 size_t i = 0;
10244 for (; i < graphLabels.size(); i++) {
10245 if (graphLabels[i] == c.second) {
10246 break;
10247 }
10248 }
10249 sortedGraph.AddPoint(sortedGraph.GetN(), graph->GetPointY(i));
10250 sortedGraph.SetPointError(sortedGraph.GetN() - 1, 0, 0, graph->GetErrorYlow(i), graph->GetErrorYhigh(i));
10251 }
10252 graph->Set(0);
10254 tmpList2.SetName("tmpList");
10255 tmpList2.Add(&sortedGraph);
10256 graph->Merge(&tmpList2);
10257 tmpList2.RemoveAll();
10259 graph->SetTitle("Fit Result Impact");
10260 }
10261
10262 // create a framing histogram
10263 TH2D *hist;
10264 if (doHorizontal) {
10265 hist = new TH2D(GetName(), fr->GetTitle(), 100, -4, 4, std::max(graph->GetN(), 1), -0.5,
10266 std::max(graph->GetN(), 1) - 0.5);
10267 int i = 1;
10268 for (auto &l : graphLabels) {
10269 hist->GetYaxis()->SetBinLabel(i++, l);
10270 }
10271 if (!graphLabels.empty())
10272 hist->GetYaxis()->LabelsOption("v");
10273 hist->GetXaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
10274 } else {
10275 hist = new TH2D(GetName(), fr->GetTitle(), std::max(graph->GetN(), 1), -0.5, std::max(graph->GetN(), 1) - 0.5,
10276 100, -4, 4);
10277 int i = 1;
10278 for (auto &l : graphLabels) {
10279 hist->GetXaxis()->SetBinLabel(i++, l);
10280 }
10281 if (!graphLabels.empty())
10282 hist->GetXaxis()->LabelsOption("v");
10283 hist->GetYaxis()->SetNdivisions(8, 0, 0);
10284 hist->GetYaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
10285 }
10286 hist->SetStats(false);
10287 hist->SetDirectory(nullptr);
10288 hist->SetBit(kCanDelete);
10289 auto histCopy = dynamic_cast<TH1 *>(hist->Clone(".axis"));
10290 histCopy->SetDirectory(nullptr);
10291 histCopy->SetBit(kCanDelete);
10292 auto _axis = (doHorizontal ? histCopy->GetYaxis() : histCopy->GetXaxis());
10293
10294 /*
10295 auto t = TH1::AddDirectoryStatus();
10296 TH1::AddDirectory(false);
10297 auto hist = new TH1F(TString::Format(".%s_pullFrame", GetName()), fr->GetTitle(), std::max(graph->GetN(),
10298 1), -0.5, std::max(graph->GetN(), 1) - 0.5); hist->SetStats(false); TH1::AddDirectory(t);
10299 hist->SetBit(kCanDelete);
10300 */
10301 // auto hist = graph->GetHistogram();
10302 graph->GetHistogram()->GetXaxis()->Set(std::max(graph->GetN(), 1), -0.5, std::max(graph->GetN(), 1) - 0.5);
10303 for (int ii = 1; ii <= _axis->GetNbins(); ii++) {
10304 graph->GetHistogram()->GetXaxis()->SetBinLabel(ii, _axis->GetBinLabel(ii));
10305 }
10306 // int i = 1;
10307 // for (auto &l : graphLabels) {
10308 // hist->GetXaxis()->SetBinLabel(i++, l);
10309 // }
10310 // hist->SetMaximum(4);
10311 // hist->SetMinimum(-4);
10312 // if (graph->GetN())
10313 // hist->GetXaxis()->LabelsOption("v");
10314 // hist->GetYaxis()->SetNdivisions(8, 0, 0);
10315 // hist->GetYaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
10316 clearPad();
10317 // create a new pad because adjust the margins ...
10318 auto oldPad = gPad;
10319 gPad->Divide(1, 1, 1e-9, 1e-9);
10320 gPad->cd(1);
10321
10322 if (doHorizontal) {
10323 gPad->SetLeftMargin(0.4);
10324 } else {
10325 gPad->SetBottomMargin(0.4);
10326 }
10327
10328 auto pNamesHist = dynamic_cast<TH1F *>(graph->GetHistogram()->Clone("scales")); // used by interactive "pull" plot
10329 pNamesHist->Sumw2();
10330 pNamesHist->SetDirectory(nullptr);
10331
10332 for (int ii = 1; ii <= graph->GetN(); ii++) { // use graph->GetN() to protect against the 0 pars case
10333 auto _p = fr->floatParsFinal().find(_axis->GetBinLabel(ii));
10334 pNamesHist->SetBinContent(ii, offset[_p->GetName()]);
10335 pNamesHist->SetBinError(ii, scale[_p->GetName()]);
10336 _axis->SetBinLabel(ii, strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName());
10337 }
10338
10339 // hist->Draw(); -- now just draw the graph
10340
10341 if (!sOpt.Contains("impact")) {
10342 for (int ii = 2; ii >= 1; ii--) {
10343 auto pullBox = new TGraphErrors;
10344 pullBox->SetName(TString::Format("%dsigmaBand", ii));
10345 pullBox->SetBit(kCanDelete);
10346 pullBox->SetPoint(0, (doHorizontal) ? -ii : -0.5, (doHorizontal) ? -0.5 : 0);
10347 pullBox->SetPoint(1, (doHorizontal) ? ii : (_axis->GetNbins() - 0.5 - nUnconstrained),
10348 (doHorizontal) ? -0.5 : 0);
10349 pullBox->SetPointError(0, 0, (doHorizontal) ? (_axis->GetNbins() - nUnconstrained) : ii);
10350 pullBox->SetPointError(1, 0, (doHorizontal) ? (_axis->GetNbins() - nUnconstrained) : ii);
10351 pullBox->SetFillColor((ii == 2) ? kYellow : kGreen);
10352 hist->GetListOfFunctions()->Add(pullBox, "3"); // pullBox->Draw("3");
10353 }
10354 auto pullLine = new TGraph;
10355 pullLine->SetName("0sigmaLine");
10356 pullLine->SetBit(kCanDelete);
10357 pullLine->SetPoint(0, -0.5, 0);
10358 pullLine->SetPoint(1, _axis->GetNbins() - 0.5, 0);
10359 pullLine->SetLineStyle(2);
10360 pullLine->SetEditable(false);
10361 hist->GetListOfFunctions()->Add(pullLine, "l"); // pullLine->Draw("l");
10362
10363 // also draw vertical line separating constrained from unconstrained, if necessary
10364 if (nUnconstrained > 0) {
10365 pullLine = new TGraph;
10366 pullLine->SetName("dividerLine");
10367 pullLine->SetBit(kCanDelete);
10368 pullLine->SetPoint(0, graph->GetN() - 0.5 - nUnconstrained, -100);
10369 pullLine->SetPoint(1, graph->GetN() - 0.5 - nUnconstrained, 100);
10370 pullLine->SetLineStyle(2);
10371 pullLine->SetEditable(false);
10372 hist->GetListOfFunctions()->Add(pullLine, "l"); // pullLine->Draw("l");
10373 }
10374
10375 // and draw a pave with fr status info
10376 TPaveText *pave =
10377 new TPaveText(gPad->GetLeftMargin(), 1. - gPad->GetTopMargin(), 1. - gPad->GetRightMargin(), 0.98, "NDCNB");
10378 pave->SetFillStyle(0);
10379 pave->SetBorderSize(0);
10380 pave->SetMargin(0.);
10381 pave->SetName("status");
10382 pave->SetTextAlign(31);
10383 pave->AddText(TString::Format("minNLL: %g edm: %g", fr->minNll(), fr->edm()))->SetTextColor((fr->status() == 3) ? kRed : kBlack);
10384 std::string covQualTxt;
10385 switch (fr->covQual()) {
10386 case -1: covQualTxt = "Unknown"; break;
10387 case 0: covQualTxt = "Not calculated"; break;
10388 case 1: covQualTxt = "Approximate"; break;
10389 case 2: covQualTxt = "Forced Positive-Definite"; break;
10390 case 3: covQualTxt = "Accurate"; break;
10391 }
10392 pave->AddText(TString::Format("Cov. Quality: %d (%s)", fr->covQual(), covQualTxt.c_str()))
10393 ->SetTextColor((fr->covQual() == 3) ? kBlack : kRed);
10394
10395 std::string statusCodes;
10396 for (unsigned int i = 0; i < fr->numStatusHistory(); i++) {
10397 statusCodes += TString::Format(" %s = %d", fr->statusLabelHistory(i), fr->statusCodeHistory(i));
10398 }
10399 pave->AddText(statusCodes.c_str())->SetTextColor(fr->status() == 0 ? kBlack : kRed);
10400
10401 hist->GetListOfFunctions()->Add(pave);
10402
10403 } else {
10404 gPad->SetTicks(0, 0); // ensure mirrored ticks aren't drawn in this pad
10405
10406 if (doHorizontal) {
10407 // ensure canvas height big enough
10408 if (int(gPad->GetCanvas()->GetWh()) < pNamesHist->GetNbinsX() * 15) {
10409 gPad->GetCanvas()->SetCanvasSize(gPad->GetCanvas()->GetWw(), pNamesHist->GetNbinsX() * 15);
10410 }
10411 }
10412
10413 double factor = 475. / gPad->GetCanvas()->GetWh(); // Wh is the full canvas height, not window height
10414 gPad->SetTopMargin(gStyle->GetPadTopMargin() * factor); // fixed margin height
10415 gPad->SetBottomMargin(gStyle->GetPadBottomMargin() * factor); // fixed margin height
10416
10417 TGaxis *axis =
10418 new TGaxis(_axis->GetXmin(), -4, _axis->GetXmin(), 4, -1.2 * maxImpact, 1.2 * maxImpact, 510, "-S");
10419
10420 if (doHorizontal) {
10421 // _axis->SetLabelSize(
10422 // (_axis->GetLabelFont() % 10 > 2)
10423 // ? (20 / factor)
10424 // : ((gPad->AbsPixeltoY(0) - gPad->AbsPixeltoY(20 / factor)) / (gPad->GetY2() -
10425 // gPad->GetY1())));
10426 // histCopy->GetXaxis()->SetTickLength(histCopy->GetXaxis()->GetTickLength() * factor);
10427 // hist->GetXaxis()->SetTickLength(hist->GetXaxis()->GetTickLength() * factor);
10428 // histCopy->GetYaxis()->SetTickLength(histCopy->GetYaxis()->GetTickLength() * factor);
10429 // hist->GetYaxis()->SetTickLength(hist->GetYaxis()->GetTickLength() * factor);
10430 // histCopy->GetXaxis()->SetTitleOffset(histCopy->GetXaxis()->GetTitleOffset() * factor);
10431 // histCopy->GetXaxis()->SetLabelOffset(histCopy->GetXaxis()->GetLabelOffset() * factor);
10432 // hist->GetXaxis()->SetTitleOffset(hist->GetXaxis()->GetTitleOffset() * factor);
10433 // hist->GetXaxis()->SetLabelOffset(hist->GetXaxis()->GetLabelOffset() * factor);
10434 // histCopy->GetXaxis()->SetTitleOffset(histCopy->GetXaxis()->GetTitleOffset() * factor);
10435 // histCopy->GetXaxis()->SetLabelOffset(histCopy->GetXaxis()->GetLabelOffset() * factor);
10436 }
10437 // copy attributes from TAxis to TGaxis
10438 axis->ImportAxisAttributes((doHorizontal) ? histCopy->GetXaxis() : histCopy->GetYaxis());
10439 axis->SetTitle(TString::Format("#Delta %s", fr->floatParsFinal().find(poiName.c_str())->GetTitle()));
10440
10441 // create impact bar charts
10442 for (int tt = 0; tt < 2; tt++) {
10443 auto impact = static_cast<TH1 *>(
10444 graph->GetHistogram()->Clone(TString::Format("%s_impact+", tt == 0 ? "prefit" : "postfit")));
10445 impact->SetDirectory(nullptr);
10446 impact->GetYaxis()->SetTitle(TString::Format("#Delta%s/#sigma", poiName.c_str()));
10447 impact->SetBarWidth(0.9);
10448 impact->SetBarOffset(0.05);
10449 impact->SetLineColor(kBlack);
10450 impact->SetFillColor(kAzure - 4);
10451 impact->SetFillStyle(tt == 0 ? 3013 : 1001);
10452 auto impact2 =
10453 static_cast<TH1 *>(impact->Clone(TString::Format("%s_impact-", tt == 0 ? "prefit" : "postfit")));
10454 impact2->SetDirectory(nullptr);
10455 impact2->SetFillColor(kCyan);
10456 for (int ii = 1; ii <= pNamesHist->GetNbinsX(); ii++) {
10457 for (auto &c : covariances) {
10458 if (c.second != pNamesHist->GetXaxis()->GetBinLabel(ii))
10459 continue;
10460 auto vv = dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(c.second.c_str()));
10461 auto vv_init = dynamic_cast<RooRealVar *>(fr->floatParsInit().find(c.second.c_str()));
10462 impact->SetBinContent(ii, ((tt == 0 && !vv_init->hasError()) || !vv->hasError())
10463 ? 0.
10464 : c.first * vv->getError() / vv->getErrorHi() *
10465 (tt == 0 ? (vv_init->getErrorHi() / vv->getErrorHi()) : 1.));
10466 impact2->SetBinContent(ii, ((tt == 0 && !vv_init->hasError()) || !vv->hasError())
10467 ? 0.
10468 : c.first * vv->getError() / vv->getErrorLo() *
10469 (tt == 0 ? (vv_init->getErrorLo() / vv->getErrorLo()) : 1.));
10470 }
10471 }
10472 hist->GetListOfFunctions()->Add(impact, (doHorizontal) ? "hbarsamemin0" : "bsamey+");
10473 hist->GetListOfFunctions()->Add(impact2, (doHorizontal) ? "hbarsamemin0" : "bsamey+");
10474 }
10475 // add three lines
10476 for (int ii = -1; ii <= 1; ii++) {
10477 auto pullLine = new TGraph;
10478 pullLine->SetName(TString::Format("%dsigmaLine", ii));
10479 pullLine->SetBit(kCanDelete);
10480 pullLine->SetPoint(0, -0.5, ii);
10481 pullLine->SetPoint(1, hist->GetNbinsY() - 0.5, ii);
10482 pullLine->SetLineStyle(2);
10483 pullLine->SetEditable(false);
10484 hist->GetListOfFunctions()->Add(pullLine, "l");
10485 }
10486 hist->GetListOfFunctions()->Add(axis); // draw axis last
10487 TLegend *leg1 =
10488 new TLegend(0.02, doHorizontal ? (1. - 0.22 * factor) : 0.02, 0.27, (doHorizontal ? 1. : 0.24));
10489 leg1->SetFillStyle(0);
10490 leg1->SetBorderSize(0);
10491 leg1->SetMargin(0.25);
10492 leg1->SetNColumns(2);
10493
10494 leg1->SetTextSize(_axis->GetLabelSize());
10495 leg1->SetTextFont(_axis->GetLabelFont());
10496 leg1->AddEntry((TObject *)nullptr, "Hessian Pre-fit", "");
10497 leg1->AddEntry((TObject *)nullptr, "Impact:", "");
10498 leg1->AddEntry(hist->FindObject("prefit_impact+"), "#theta = #hat{#theta}+#Delta#theta", "f");
10499 leg1->AddEntry(hist->FindObject("prefit_impact-"), "#theta = #hat{#theta}-#Delta#theta", "f");
10500 leg1->AddEntry((TObject *)nullptr, "Hessian Post-fit", "");
10501 leg1->AddEntry((TObject *)nullptr, "Impact:", "");
10502 leg1->AddEntry(hist->FindObject("postfit_impact+"), "#theta = #hat{#theta}+#Delta#theta", "f");
10503 leg1->AddEntry(hist->FindObject("postfit_impact-"), "#theta = #hat{#theta}-#Delta#theta", "f");
10504
10505 hist->GetListOfFunctions()->Add(leg1);
10506 if (gStyle->GetOptTitle()) {
10507 histCopy->SetBit(TH1::kNoTitle);
10508 TPaveText *title =
10509 new TPaveText(gPad->GetLeftMargin(), 1. - gPad->AbsPixeltoY(14), 1. - gPad->GetRightMargin(), 1., "NDC");
10510 title->ConvertNDCtoPad();
10511 title->SetY1NDC(1. - gPad->GetTopMargin() * 0.6);
10512 title->SetY2NDC(1);
10513 title->SetTextSize(
10514 (title->GetTextFont() % 10 > 2)
10515 ? (14 / factor)
10516 : ((gPad->AbsPixeltoY(0) - gPad->AbsPixeltoY(10 / factor)) / (gPad->GetY2() - gPad->GetY1())));
10517 title->SetFillStyle(0);
10518 title->SetBorderSize(0);
10519 title->AddText(histCopy->GetTitle());
10520 hist->GetListOfFunctions()->Add(title);
10521 }
10522 }
10523
10524 graph->SetEditable(false);
10525 pNamesHist->SetLineWidth(0);
10526 pNamesHist->SetMarkerSize(0);
10527 pNamesHist->SetMarkerStyle(0);
10528 graph->GetListOfFunctions()->Add(pNamesHist, "same"); // graph->SetHistogram(pNamesHist);
10529 if (doHorizontal) {
10530
10531 // flip the graph and contained graphs
10532 for (int p = 0; p < graph->GetN(); p++) {
10533 graph->SetPoint(p, graph->GetPointY(p), graph->GetPointX(p));
10534 graph->SetPointError(p, graph->GetErrorYlow(p), graph->GetErrorYhigh(p), graph->GetErrorXlow(p),
10535 graph->GetErrorXhigh(p));
10536 }
10537 for (auto f : *hist->GetListOfFunctions()) {
10538 if (f->InheritsFrom("TH1")) {
10539 // f->Draw("hbarsamemin0");
10540 } /*else if (auto g2 = dynamic_cast<TGraphErrors *>(f)) {
10541 for (int p = 0; p < g2->GetN(); p++) {
10542 g2->SetPoint(p, g2->GetPointY(p), g2->GetPointX(p));
10543 g2->SetPointError(p, g2->GetErrorY(p), _axis->GetNbins());
10544 }
10545 //g2->Draw("3");
10546 } */
10547 else if (auto g = dynamic_cast<TGraph *>(f)) {
10548 for (int p = 0; p < g->GetN(); p++) {
10549 g->SetPoint(p, g->GetPointY(p), g->GetPointX(p));
10550 }
10551 // g->Draw("l");
10552 } else if (auto l = dynamic_cast<TLine *>(f)) {
10553 l->SetX1(l->GetY1());
10554 l->SetX2(l->GetY2());
10555 l->SetY1(_axis->GetXmax());
10556 l->SetY2(_axis->GetXmax());
10557 // l->Draw();
10558 }
10559 }
10560 }
10561
10562 if(!sOpt.Contains("impact")) {
10563 // add labels to graph for unconstrained parameters
10564 for(size_t i=0;i<ugraphLabels.size();i++) {
10565 int bin = pNamesHist->GetNbinsX()-ugraphLabels.size()+i+1;
10566 auto p = dynamic_cast<RooRealVar*>(fr->floatParsFinal().find(pNamesHist->GetXaxis()->GetBinLabel(bin)));
10567 if(!p) continue;
10568 auto x = graph->GetPointX(graph->GetN()-ugraphLabels.size()+i);
10569 auto y = graph->GetPointY(graph->GetN()-ugraphLabels.size()+i)+graph->GetErrorYhigh(graph->GetN()-ugraphLabels.size()+i);
10570 auto l = xRooFit::matchPrecision({p->getVal(),p->getError()});
10571 auto t = new TLatex(x,y,TString::Format("%g #pm %g",l.first,l.second));
10572 t->SetBit(kCanDelete);
10573 t->SetTextSize(0.025);
10574 t->SetTextAngle(90);
10575 graph->GetListOfFunctions()->Add(t);
10576 }
10577 }
10578
10579 graph->SetName("pulls");
10580 hist->GetListOfFunctions()->Add(graph, "z0p");
10581 // hist->GetListOfFunctions()->Add(histCopy->Clone(".axis"),(sOpt.Contains("impact") &&
10582 // !doHorizontal)?"axissamey+":"axissame"); // doesn't display right when zoom the axis
10583 if (!hasSame) {
10584 histCopy->Draw((sOpt.Contains("impact") && !doHorizontal)
10585 ? "axisy+"
10586 : "axis"); // draws the axis, called ".axis" for easy access
10587 }
10588 hist->Draw("same");
10589 //
10590 // if(sOpt.Contains("impact")) {
10591 // // make main object the histogram
10592 // auto h = (TH1*)graph->GetHistogram()->Clone("impact");
10593 // graph->GetListOfFunctions()->RemoveAll();
10594 // for(int ii=1;ii<=h->GetNbinsX();ii++) h->SetBinContent(ii,-4);
10595 // h->GetListOfFunctions()->Add(graph,"z0p");
10596 // h->Draw("hbar");
10597 // } else {
10598 // graph->Draw(sOpt.Contains("impact") ? "az0py+" : "az0p");
10599 // }
10600 auto hh = dynamic_cast<TH1 *>(histCopy->Clone(".axiscopy"));
10601 hh->SetDirectory(nullptr);
10602 hh->SetBit(kCanDelete);
10603 hh->Draw(
10604 (sOpt.Contains("impact") && !doHorizontal)
10605 ? "axissamey+"
10606 : "axissame"); // overlay axis again -- important is last so can remove if don't pad->Update before reclear
10607 gPad->Modified();
10608 oldPad->cd();
10609 // gPad->Update();
10610 return;
10611 }
10612
10613 if (get()->InheritsFrom("RooAbsData")) {
10614 auto s = parentPdf();
10615 if (s && s->get<RooSimultaneous>()) {
10616 // drawing dataset associated to a simultaneous means must find subpads with variation names
10617 // may not have subpads if drawning a "Yield" plot ...
10618 bool doneDraw = false;
10619 for (auto c : s->bins()) {
10620 auto _pad = dynamic_cast<TPad *>(gPad->GetPrimitive(c->GetName()));
10621 if (!_pad)
10622 continue; // channel was hidden?
10623 // attach as a child before calling datasets(), so that if this dataset is external to workspace it is
10624 // included still attaching the dataset ensures dataset reduction for the channel is applied
10625 c->push_back(std::make_shared<xRooNode>(*this));
10626 auto ds = c->datasets().find(GetName());
10627 c->resize(c->size() - 1); // remove the child we attached
10628 if (!ds) {
10629 std::cout << " no ds " << GetName() << " - this should never happen!" << std::endl;
10630 continue;
10631 }
10632 auto tmp = gPad;
10633 _pad->cd();
10634 ds->Draw(opt);
10635 doneDraw = true;
10636 tmp->cd();
10637 }
10638 if (doneDraw) {
10639 gPad->Modified();
10640 return;
10641 }
10642 }
10643
10644 if (!s && hasSame) {
10645 // draw onto all subpads with = in the name
10646 // if has no such subpads, draw onto this pad
10647 bool doneDraw = false;
10648 for (auto o : *gPad->GetListOfPrimitives()) {
10649 if (auto p = dynamic_cast<TPad *>(o); p && TString(p->GetName()).Contains('=')) {
10650 auto _tmp = gPad;
10651 p->cd();
10652 Draw(opt);
10653 _tmp->cd();
10654 doneDraw = true;
10655 }
10656 }
10657 if (doneDraw) {
10658 gPad->Modified();
10659 return;
10660 }
10661 }
10662
10663 auto dataGraph = BuildGraph(v, false, (!s && hasSame) ? gPad : nullptr);
10664 if (!dataGraph)
10665 return;
10666
10667 dataGraph->SetBit(kCanDelete); // will be be deleted when pad is cleared
10668 dataGraph->SetMarkerSize(dataGraph->GetMarkerSize() * gPad->GetWNDC()); // scale marker sizes to pad size
10669
10670 if (s && !s->get<RooAbsPdf>()->canBeExtended()) {
10671 // normalize dataGraph to 1
10672 double tot = 0;
10673 for (int i = 0; i < dataGraph->GetN(); i++)
10674 tot += dataGraph->GetPointY(i);
10675 dataGraph->Scale(1. / tot);
10676 }
10677
10678 if (!hasSame) {
10679 clearPad();
10680 dataGraph->Draw("Az0p");
10681 addLegendEntry(dataGraph, strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(), "pEX0");
10682 gPad->Modified();
10683 // gPad->Update();
10684 return;
10685 }
10686
10687 bool noPoint = false;
10688 if (v && dynamic_cast<RooAbsArg *>(v)->getAttribute("global") && dataGraph->GetN() == 1) {
10689 // global observable ... if graph has only 1 data point line it up on the histogram value
10690 for (auto o : *gPad->GetListOfPrimitives()) {
10691 if (auto h = dynamic_cast<TH1 *>(o);
10692 h && strcmp(h->GetXaxis()->GetName(), dynamic_cast<TObject *>(v)->GetName()) == 0) {
10693 dataGraph->SetPointY(0, h->Interpolate(dataGraph->GetPointX(0)));
10694 noPoint = true;
10695 break;
10696 }
10697 }
10698 }
10699
10700 if (auto _pad = dynamic_cast<TPad *>(gPad->FindObject("auxPad")); _pad) {
10701 if (auto h = dynamic_cast<TH1 *>(_pad->GetPrimitive("auxHist")); h) {
10702 TString histName = h->GetTitle(); // split it by | char
10703 TString histType = histName(histName.Index('|') + 1, histName.Length());
10704 histName = histName(0, histName.Index('|'));
10705 if (auto mainHist = dynamic_cast<TH1 *>(gPad->GetPrimitive(histName));
10706 mainHist && auxFunctions.find(h->GetYaxis()->GetTitle()) != auxFunctions.end()) {
10707 // decide what to do based on title of auxHist (previously used name of y-axis but that changed axis
10708 // behaviour) use title instead
10709 auto ratioGraph = dynamic_cast<TGraphAsymmErrors *>(dataGraph->Clone(dataGraph->GetName()));
10710 ratioGraph->SetBit(kCanDelete);
10711 for (int i = 0; i < ratioGraph->GetN(); i++) {
10712 double val = ratioGraph->GetPointY(i);
10713 int binNum = mainHist->FindFixBin(ratioGraph->GetPointX(i));
10714 double nom = mainHist->GetBinContent(binNum);
10715 double nomerr = mainHist->GetBinError(binNum);
10716 double yval =
10717 std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(ratioGraph->GetPointY(i), nom, nomerr);
10718 double yup = std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(val + ratioGraph->GetErrorYhigh(i),
10719 nom, nomerr) -
10720 yval;
10721 double ydown = yval - std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(
10722 val - ratioGraph->GetErrorYlow(i), nom, nomerr);
10723 if (!std::isnan(yval)) {
10724 ratioGraph->SetPointY(i, yval);
10725 if (!std::isnan(yup))
10726 ratioGraph->SetPointEYhigh(i, yup);
10727 if (!std::isnan(ydown))
10728 ratioGraph->SetPointEYlow(i, ydown);
10729 }
10730 }
10731 // remove the zero points
10732 int i = 0;
10733 while (i < ratioGraph->GetN()) {
10734 if (ratioGraph->GetPointY(i) == 0 && ratioGraph->GetErrorYhigh(i) == 0 &&
10735 ratioGraph->GetErrorYlow(i) == 0) {
10736 ratioGraph->RemovePoint(i);
10737 } else {
10738 i++;
10739 }
10740 }
10741 auto _tmpPad = gPad;
10742 _pad->cd();
10743 ratioGraph->Draw("z0psame");
10745 adjustYRange(minMax.first, minMax.second, h, std::get<1>(auxFunctions[h->GetYaxis()->GetTitle()]));
10746 _tmpPad->cd();
10747 }
10748 }
10749 }
10750
10751 dataGraph->Draw("z0p same");
10752 addLegendEntry((noPoint) ? nullptr : dataGraph, strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(),
10753 noPoint ? "" : "pEX0");
10754
10755 auto minMax = graphMinMax(dynamic_cast<TGraphAsymmErrors *>(dataGraph));
10756 adjustYRange(minMax.first, minMax.second);
10757
10758 gPad->Modified();
10759 // gPad->Update();
10760 return;
10761 }
10762
10763 // auto _ax = GetXaxis();
10764 // auto v = (_ax) ? dynamic_cast<RooRealVar*>(/*possibleObs.first()*/_ax->GetParent()) : nullptr;
10765 // if (!v) { v = get<RooRealVar>(); } // self-axis
10766 // if (!v) return;
10767
10768 if (auto lv = get<RooAbsLValue>(); lv && fParent && fParent->get<RooAbsData>()) {
10769 // drawing an observable from a dataset ... build graph, and exit
10770 auto gr = fParent->BuildGraph(lv, true);
10772 gr->Draw(hasSame ? "P" : "AP");
10773 return;
10774 }
10775
10776 if (forceNames != "") {
10777 // drawing a force plot ... build nll and fill a histogram with force terms
10778 auto _dsets = datasets();
10779 bool _drawn = false;
10780 auto _coords = coords();
10781 auto _fr = fitResult();
10782 auto initPar = dynamic_cast<RooRealVar *>(_fr.get<RooFitResult>()->floatParsInit().find(forceNames));
10783 if (!initPar)
10784 return;
10785 std::vector<double> valuesToDo = {initPar->getVal()};
10786 if (initPar->hasError() || initPar->hasAsymError()) {
10787 valuesToDo.push_back(initPar->getVal() + initPar->getErrorLo());
10788 valuesToDo.push_back(initPar->getVal() + initPar->getErrorHi());
10789 }
10790 int ii = 0;
10791 for (auto valueToDo : valuesToDo) {
10792 ii++;
10793 for (auto &d : _dsets) {
10794 if (!d->get()->TestBit(1 << 20))
10795 continue;
10796 auto emptyHist = BuildHistogram(v, true);
10797 emptyHist->SetBit(kCanDelete);
10798 auto _obs = d->obs();
10799 auto x = _obs.find((v) ? dynamic_cast<TObject *>(v)->GetName() : emptyHist->GetXaxis()->GetName());
10800 auto _nll = nll(d);
10801 auto theData = d->get<RooAbsData>();
10802 int nevent = theData->numEntries();
10803 for (int i = 0; i < nevent; i++) {
10804 theData->get(i);
10805 bool _skip = false;
10806 for (const auto &_c : _coords) {
10807 if (auto cat = _c->get<RooAbsCategoryLValue>(); cat) {
10808 if (cat->getIndex() != theData->get()->getCatIndex(cat->GetName())) {
10809 _skip = true;
10810 break;
10811 }
10812 }
10813 }
10814 if (_skip)
10815 continue;
10816
10817 if (x) {
10818 auto val = _nll.pars()->getRealValue(initPar->GetName());
10819 if (ii > 1)
10820 _nll.pars()->setRealValue(initPar->GetName(), valueToDo);
10821 auto nllVal = _nll.getEntryVal(i);
10822 _nll.pars()->setRealValue(initPar->GetName(), initPar->getVal());
10823 auto nllVal2 = _nll.getEntryVal(i);
10824 _nll.pars()->setRealValue(initPar->GetName(), val);
10825 emptyHist->Fill(x->get<RooAbsReal>()->getVal(), (nllVal2 - nllVal));
10826 }
10827 }
10828 // include the extendedTerm, distributed evenly over the bins
10829 // probably should be somehow dependent on data density though (i.e. bins with more data get more of it?)
10830 auto val = _nll.pars()->getRealValue(initPar->GetName());
10831 if (ii > 1)
10832 _nll.pars()->setRealValue(initPar->GetName(), valueToDo);
10833 auto _extTerm = _nll.extendedTermVal();
10834 _nll.pars()->setRealValue(initPar->GetName(), initPar->getVal());
10835 auto _extTerm2 = _nll.extendedTermVal();
10836 _nll.pars()->setRealValue(initPar->GetName(), val);
10837 for (int i = 1; i <= emptyHist->GetNbinsX(); i++) {
10838 emptyHist->SetBinContent(i,
10839 emptyHist->GetBinContent(i) + (_extTerm2 - _extTerm) / emptyHist->GetNbinsX());
10840 emptyHist->SetBinError(i, 0);
10841 }
10842 emptyHist->GetYaxis()->SetTitle("log (L(#theta)/L(#theta_{0}))");
10843 emptyHist->SetTitle(TString::Format("#theta = %g", (ii > 1) ? valueToDo : val));
10844 if (ii == 1)
10845 emptyHist->SetLineColor(kBlack);
10846 if (ii == 2) {
10847 emptyHist->SetLineColor(kRed);
10848 } else if (ii == 3) {
10849 emptyHist->SetLineColor(kBlue);
10850 }
10851 emptyHist->Draw(_drawn ? "same" : "");
10852 _drawn = true;
10853 }
10854 }
10855 return;
10856 }
10857
10858 auto rar = get<RooAbsReal>();
10859 const xRooNode *rarNode = this;
10860 if (!rar) {
10861 // draw a deleteable clone of the object we wrap (since we might own the object)
10862 get()->DrawClone(opt);
10863 return;
10864 }
10865 // RooAbsReal *sf = nullptr;
10866 if (get()->InheritsFrom("RooExtendPdf")) {
10867 browse();
10868 rarNode = find(".pdf").get();
10869 // rar = rarNode->get<RooAbsReal>();
10870 // sf = find(".n")->get<RooAbsReal>();
10871 }
10872
10873 if (!nostack && !hasOverlay &&
10874 (rarNode->get()->InheritsFrom("RooRealSumPdf") || rarNode->get()->InheritsFrom("RooAddPdf") ||
10875 (v && rarNode->get()->InheritsFrom("RooSimultaneous") &&
10876 strcmp(dynamic_cast<TObject *>(v)->GetName(), rarNode->get<RooSimultaneous>()->indexCat().GetName()) == 0))) {
10877 nostack = false;
10878 } else {
10879 // in all other cases, we do not build a stack
10880 nostack = true;
10881 }
10882
10883 auto h = BuildHistogram(v, false, hasErrorOpt, 1, 0, "", false, false, 0, nullptr, nostack, true /*setInterp*/);
10884 if (!h) {
10885 if (get()) {
10886 // draw a deleteable clone of the object we wrap (since we might own the object)
10887 get()->DrawClone(opt);
10888 }
10889 return;
10890 }
10891 h->SetBit(kCanDelete);
10892
10893 if (!v)
10894 v = getObject<RooAbsLValue>(h->GetXaxis()->IsAlphanumeric() ? h->GetXaxis()->GetTimeFormatOnly()
10895 : h->GetXaxis()->GetName())
10896 .get();
10897 RooAbsArg *vv = (v) ? dynamic_cast<RooAbsArg *>(v) : rar;
10898 if (h->GetXaxis()->IsAlphanumeric()) {
10899 // do this to get bin labels
10900 h->GetXaxis()->SetName("xaxis"); // WARNING -- this messes up anywhere we GetXaxis()->GetName()
10901 }
10902
10903 // get style now, before we mess with histogram title
10904 // auto _styleNode = styles(h);
10905
10906 if (rar->InheritsFrom("RooAbsPdf") && !(rar->InheritsFrom("RooRealSumPdf") || rar->InheritsFrom("RooAddPdf") ||
10907 rar->InheritsFrom("RooSimultaneous"))) {
10908 // append parameter values to title if has such
10909 RooArgSet s;
10910 rar->leafNodeServerList(&s);
10911 if (v)
10912 s.remove(*dynamic_cast<RooAbsArg *>(v));
10913 if (!s.empty()) {
10914 TString ss = h->GetTitle();
10915 ss += " [";
10916 bool first = true;
10917 for (auto _p : s) {
10918 auto _v = dynamic_cast<RooRealVar *>(_p);
10919 if (!_v)
10920 continue;
10921 if (!first)
10922 ss += ",";
10923 first = false;
10924 ss += TString::Format("%s=%g", strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName(), _v->getVal());
10925 if (_v->hasError()) {
10926 ss += TString::Format("#pm %g", _v->getError());
10927 }
10928 }
10929 ss += "]";
10930 h->SetTitle(ss);
10931 }
10932 }
10933
10934 if (!hasSame) {
10935 if (obs().find(vv->GetName())) {
10936 gPad->SetGrid(0, 0);
10937 } else {
10938 gPad->SetGrid(1, 1);
10939 }
10940 }
10941 TString dOpt = h->GetOption();
10942 if (dOpt == "l")
10943 h->SetFillStyle(0);
10944 // // need to strip namespace to discount the "HistFactory" namespace classes from all being treated as binned
10945 // TString clNameNoNamespace = rar->ClassName();
10946 // clNameNoNamespace = clNameNoNamespace(clNameNoNamespace.Last(':') + 1, clNameNoNamespace.Length());
10947 // TString dOpt = (clNameNoNamespace.Contains("Hist") || vv->isCategory() || rar->isBinnedDistribution(*vv) ||
10948 // h->GetNbinsX() == 1 || rar->getAttribute("BinnedLikelihood") ||
10949 // (dynamic_cast<RooAbsRealLValue *>(vv) &&
10950 // std::unique_ptr<std::list<double>>(rar->binBoundaries(*dynamic_cast<RooAbsRealLValue *>(vv),
10951 // -std::numeric_limits<double>::infinity(),
10952 // std::numeric_limits<double>::infinity()))))
10953 // ? ""
10954 // : "LF2";
10955 // if (auto d = dynamic_cast<RooHistFunc *>(rar); d && !d->isBinnedDistribution(*vv) && h->GetNbinsX() != 1) {
10956 // dOpt = "LF2"; // hist func is interpolated, so draw it as such
10957 // }
10958 // if (dOpt == "LF2" && !components().empty()) {
10959 // // check if all components of dOpt are "Hist" type (CMS model support)
10960 // // if so then dOpt="";
10961 // bool allHist = true;
10962 // for (auto &s : components()) {
10963 // TString _clName = s->get()->ClassName();
10964 // _clName = _clName(_clName.Last(':') + 1, _clName.Length());
10965 // if (!(s->get() && _clName.Contains("Hist"))) {
10966 // allHist = false;
10967 // break;
10968 // }
10969 // }
10970 // if (allHist)
10971 // dOpt = "";
10972 // }
10973 //
10974 // if(dOpt=="LF2") {
10975 // // ensure any sub hists have lf2 option
10976 // TObjLink *lnk = h->GetListOfFunctions()->FirstLink();
10977 // while (lnk) {
10978 // if(auto hh = dynamic_cast<TH1*>(lnk->GetObject())) {
10979 // if(TString(hh->GetName())=="band" && TString(lnk->GetOption())=="e2same") {
10980 // lnk->SetOption("LF2 e3same");
10981 // } else if(TString(hh->GetName())=="nominal") {
10982 // lnk->SetOption("L same");
10983 // }
10984 // }
10985 // lnk = lnk->Next();
10986 // }
10987 // }
10988
10989 if (rar == vv && rar->IsA() == RooRealVar::Class()) {
10990 dOpt += "TEXT";
10991 // add a TExec to the histogram so that when edited it will propagate to var
10992 gROOT->SetEditHistograms(true);
10993 } else {
10994 gROOT->SetEditHistograms(false);
10995 }
10996
10997 if (hasSame) {
10998 dOpt += " same";
10999 } else {
11000 hAxis = h;
11001 }
11002
11003 if (dOpt.Contains("TEXT") || sOpt.Contains("text")) {
11004 // adjust marker size so text is good
11005 h->SetMarkerSize(gStyle->GetLabelSize("Z") / (0.02 * gPad->GetHNDC()));
11006 }
11007
11008 bool hasError(false);
11009 for (int i = 0; i < h->GetSumw2N(); i++) {
11010 if (h->GetSumw2()->At(i)) {
11011 hasError = true;
11012 break;
11013 }
11014 }
11015
11016 /** This doesn't seem necessary in at least 6.26 any more - pads seem adjusted on their own
11017 if (!hasSame && h->GetYaxis()->GetTitleFont()%10 == 2) {
11018 h->GetYaxis()->SetTitleOffset( gPad->GetLeftMargin() / gStyle->GetPadLeftMargin() );
11019 } */
11020 // don't this instead - dont want to leave as zero (auto) in case show aux plot
11021 if (!hasSame && h->GetYaxis()->GetTitleFont() % 10 == 2) {
11022 h->GetYaxis()->SetTitleOffset(1.);
11023 }
11024
11025 TH1 *errHist = nullptr;
11026
11027 if (!hasSame)
11028 clearPad();
11029
11030 if (rar == vv && rar->IsA() == RooRealVar::Class()) {
11031 // add a TExec to the histogram so that when edited it will propagate to var
11032 // h->GetListOfFunctions()->Add(h->Clone("self"),"TEXTHIST");
11033 dOpt = "TEXT";
11034 auto node = new xRooNode(*this);
11035 auto _hist = (errHist) ? errHist : h;
11036 auto hCopy = (errHist) ? nullptr : dynamic_cast<TH1 *>(h->Clone());
11037 if (hCopy) {
11038 hCopy->Reset();
11039 hCopy->Add(_hist);
11040 hCopy->SetDirectory(nullptr);
11041 }
11042 _hist->GetListOfFunctions()->Add(node);
11043 _hist->GetListOfFunctions()->Add(new TExec(
11044 ".update",
11046 "gROOT->SetEditHistograms(true);auto h = dynamic_cast<TH1*>(gPad->GetPrimitive(\"%s\")); if(h) { double "
11047 "range= h->GetMaximum()-h->GetMinimum(); if(auto n "
11048 "= dynamic_cast<xRooNode*>(h->GetListOfFunctions()->FindObject(\"%s\")); n && "
11049 "n->TestBit(TObject::kNotDeleted) && n->get<RooRealVar>()->getVal() != h->GetBinContent(1)) {"
11050 "h->SetBinContent(1, "
11051 "TString::Format(\"%%.2g\",int(h->GetBinContent(1)/(range*0.01))*range*0.01).Atof());n->SetContent( "
11052 "h->GetBinContent(1) ); for(auto pp : *h->GetListOfFunctions()) if(auto hh = "
11053 "dynamic_cast<TH1*>(pp))hh->SetBinContent(1,h->GetBinContent(1));} if(h->GetBinContent(1)==0.) "
11054 "h->SetBinContent(1,range*0.005); gPad->Modified();gPad->Update(); }",
11055 _hist->GetName(), node->GetName())));
11056 if (errHist) {
11057 errHist->GetListOfFunctions()->Add(h, "TEXT HIST same");
11058 errHist->SetFillColor(h->GetLineColor());
11059 } else {
11060 hCopy->SetBit(kCanDelete);
11061 hCopy->SetFillStyle(0);
11062 _hist->GetListOfFunctions()->Add(hCopy, "TEXT HIST same");
11063 //_hist->SetBinError(1, 0);
11064 }
11065 _hist->SetStats(false);
11066 // if (_hist->GetBinContent(1)==0.) _hist->SetBinContent(1,(_hist->GetMaximum()-_hist->GetMinimum())*0.005);
11067 _hist->Draw(); //_hist->Draw(((hasError) ? "e2" : ""));
11068 gPad->Modified();
11069 return;
11070 }
11071
11072 bool overlayExisted = false;
11073 if (hasOverlay) {
11074 h->SetName(TString::Format("%s%s", h->GetName(), overlayName.Data()));
11075 if (auto existing = dynamic_cast<TH1 *>(gPad->GetPrimitive(h->GetName())); existing) {
11076 existing->Reset();
11077 existing->Add(h);
11078 delete h;
11079 h = existing;
11080 overlayExisted = true;
11081 } else {
11082 TString oldStyle = (rar && rar->getStringAttribute("style")) ? rar->getStringAttribute("style") : "";
11083 h->SetTitle(overlayName);
11084 // for overlays will take style from current gStyle before overriding with personal style
11085 // this ensures initial style will be whatever gStyle is, rather than whatever ours is
11086 static_cast<TAttLine &>(*h) = *gStyle;
11087 static_cast<TAttFill &>(*h) = *gStyle;
11088 static_cast<TAttMarker &>(*h) = *gStyle;
11089 h->SetFillStyle(0); // explicit default for overlays will be transparent fill
11090
11091 // std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case
11092 // getObject has decided to return the owning ptr (for some reason) if
11093 // (!gROOT->GetStyle(h->GetTitle())) {
11094 // if ( (style = getObject<TStyle>(h->GetTitle())) ) {
11095 // // loaded style (from workspace?) so put in list and use that
11096 // gROOT->GetListOfStyles()->Add(style.get());
11097 // } else {
11098 // // create new style - gets put in style list automatically so don't have to delete
11099 // // acquire them so saved to workspaces for auto reload ...
11100 // style = acquireNew<TStyle>(h->GetTitle(),
11101 // TString::Format("Style for %s component", h->GetTitle()));
11102 // (TAttLine &) (*style) = *dynamic_cast<TAttLine *>(h);
11103 // (TAttFill &) (*style) = *dynamic_cast<TAttFill *>(h);
11104 // (TAttMarker &) (*style) = *dynamic_cast<TAttMarker *>(h);
11105 // gROOT->GetListOfStyles()->Add(style.get());
11106 // }
11107 // }
11108 // (TAttLine&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
11109 // (TAttFill&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
11110 // (TAttMarker&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
11111 auto _styleNode = styles(h);
11112 rar->setStringAttribute("style", oldStyle == "" ? nullptr : oldStyle.Data()); // restores old style
11113 if (auto _style = _styleNode.get<TStyle>()) {
11114 (TAttLine &)(*h) = *_style;
11115 (TAttFill &)(*h) = *_style;
11116 (TAttMarker &)(*h) = *_style;
11117 }
11118 h->Draw(dOpt == "LF2" ? "e3" : dOpt);
11119 if (errHist) {
11120 errHist->SetTitle(overlayName);
11121 (TAttLine &)(*errHist) = *h;
11122 errHist->SetFillColor(h->GetLineColor());
11123 }
11124 }
11125 } else {
11126 // if (auto _style = _styleNode.get<TStyle>()) {
11127 // (TAttLine &)(*h) = *_style;
11128 // (TAttFill &)(*h) = *_style;
11129 // (TAttMarker &)(*h) = *_style;
11130 // if (errHist) {
11131 // (TAttLine &)(*errHist) = *h;
11132 // errHist->SetFillColor(h->GetLineColor());
11133 // }
11134 // }
11135 h->Draw(dOpt);
11136 }
11137
11138 if (!hasOverlay && (rarNode->get()->InheritsFrom("RooRealSumPdf") || rarNode->get()->InheritsFrom("RooAddPdf") ||
11139 (rarNode->get()->InheritsFrom("RooSimultaneous") &&
11140 strcmp(vv->GetName(), rarNode->get<RooSimultaneous>()->indexCat().GetName()) == 0))) {
11141 if (auto stack = dynamic_cast<THStack *>(h->FindObject("stack"))) {
11142 // access the stack and set draw options, adjust ranges etc
11143 TObjLink *lnk = stack->GetHists()->FirstLink();
11144 while (lnk) {
11145 TH1 *hh = static_cast<TH1 *>(lnk->GetObject());
11146 // lnk->SetOption(dOpt); - not needed
11147 auto hhMin = (hh->GetMinimum() == 0) ? hh->GetMinimum(1e-9) : hh->GetMinimum();
11148 if (lnk == stack->GetHists()->FirstLink() && h->GetMinimum() > hhMin) {
11149 auto newMin = hhMin - (h->GetMaximum() - hhMin) * gStyle->GetHistTopMargin();
11150 if (hhMin >= 0 && newMin < 0)
11151 newMin = hhMin * 0.99;
11152 adjustYRange(newMin, h->GetMaximum());
11153 }
11154 addLegendEntry(hh, hh->GetTitle(), "f");
11155 lnk = lnk->Next();
11156 }
11157 }
11158
11159 // // build a stack unless not requested
11160 // if (!nostack) {
11161 // THStack *stack = new THStack(TString::Format("%s_stack", rar->GetName()),
11162 // TString::Format("%s;%s", rar->GetTitle(), h->GetXaxis()->GetTitle()));
11163 // int count = 2;
11164 // std::map<std::string, int> colorByTitle; // TODO: should fill from any existing legend
11165 // std::set<std::string> allTitles;
11166 // bool titleMatchName = true;
11167 // std::map<std::string, TH1 *> histGroups;
11168 // std::vector<TH1 *> hhs;
11169 // std::set<TH1 *> histsWithBadTitles; // these histograms will have their titles autoFormatted
11170 //
11171 // // support for CMS model case where has single component containing many coeffs
11172 // // will build stack by setting each coeff equal to 0 in turn, rebuilding the histogram
11173 // // the difference from the "full" histogram will be the component
11174 // RooArgList cms_coefs;
11175 // if (!rarNode->components().empty()) {
11176 // auto comps = rarNode->components()[0];
11177 // for (auto &c : *comps) {
11178 // if (c->fFolder == "!.coeffs")
11179 // cms_coefs.add(*c->get<RooAbsArg>());
11180 // }
11181 // }
11182 // if (!cms_coefs.empty()) {
11183 // RooRealVar zero("zero", "", 0);
11184 // std::shared_ptr<TH1> prevHist(static_cast<TH1 *>(h->Clone()));
11185 // for (auto c : cms_coefs) {
11186 // // seems I have to remake the function each time, as haven't figured out what cache needs
11187 // clearing? std::unique_ptr<RooAbsReal> f(
11188 // dynamic_cast<RooAbsReal *>(rarNode->components()[0]->get()->Clone("tmpCopy")));
11189 // zero.setAttribute(
11190 // Form("ORIGNAME:%s", c->GetName())); // used in redirectServers to say what this
11191 // replaces
11192 // f->redirectServers(RooArgSet(zero), false, true); // each time will replace one additional coef
11193 // // zero.setAttribute(Form("ORIGNAME:%s",c->GetName()),false); (commented out so that on next
11194 // iteration
11195 // // will still replace all prev)
11196 // auto hh = xRooNode(*f, *this).BuildHistogram(v);
11197 // hh->SetName(c->GetName());
11198 // if (sf)
11199 // hh->Scale(sf->getVal());
11200 // if (strlen(hh->GetTitle()) == 0) {
11201 // hh->SetTitle(c->GetName()); // ensure all hists has titles
11202 // histsWithBadTitles.insert(hh);
11203 // } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
11204 // histsWithBadTitles.insert(hh);
11205 // }
11206 // titleMatchName &= (TString(c->GetName()) == hh->GetTitle() ||
11207 // TString(hh->GetTitle()).BeginsWith(TString(c->GetName()) + "_"));
11208 // std::shared_ptr<TH1> nextHist(static_cast<TH1 *>(hh->Clone()));
11209 // hh->Add(prevHist.get(), -1.);
11210 // hh->Scale(-1.);
11211 // hhs.push_back(hh);
11212 // prevHist = nextHist;
11213 // }
11214 // } else if (get<RooSimultaneous>()) {
11215 // // need to create a histogram for each sample across all the channels - will rely on functionality
11216 // below to
11217 // // merge them based on titles
11218 //
11219 // for (auto &chan : bins()) {
11220 // TString chanName(chan->GetName());
11221 // chanName = chanName(chanName.Index("=") + 1, chanName.Length());
11222 // auto samps = chan->mainChild();
11223 // if (!samps)
11224 // samps = *chan;
11225 // for (auto &samp : samps.components()) {
11226 // auto hh = static_cast<TH1 *>(h->Clone(samp->GetName()));
11227 // hh->Reset();
11228 // hh->SetTitle(samp->GetTitle());
11229 // if (strlen(hh->GetTitle()) == 0) {
11230 // hh->SetTitle(samp->GetName());
11231 // histsWithBadTitles.insert(hh);
11232 // } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
11233 // histsWithBadTitles.insert(hh);
11234 // }
11235 // hh->SetTitle(TString(hh->GetTitle())
11236 // .ReplaceAll(TString(chan->get()->GetName()) + "_",
11237 // "")); // remove occurance of channelname_ in title (usually
11238 // prefix)
11239 // titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
11240 // TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
11241 // hh->SetBinContent(hh->GetXaxis()->FindFixBin(chanName), samp->GetContent());
11242 // hhs.push_back(hh);
11243 // }
11244 // }
11245 // } else {
11246 // for (auto &samp : rarNode->components()) {
11247 // auto hh = samp->BuildHistogram(v,false,false,1,0,"",false,false,0,h); // passing h to ensure
11248 // binning is the same for all subcomponent hists if (sf)
11249 // hh->Scale(sf->getVal());
11250 // hhs.push_back(hh);
11251 // if (strlen(hh->GetTitle()) == 0) {
11252 // hh->SetTitle(samp->GetName()); // ensure all hists has titles
11253 // histsWithBadTitles.insert(hh);
11254 // } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
11255 // histsWithBadTitles.insert(hh);
11256 // }
11257 // titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
11258 // TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
11259 // }
11260 // }
11261 //
11262 // if (!hhs.empty()) {
11263 // for (auto &hh : hhs) {
11264 // allTitles.insert(hh->GetTitle());
11265 // }
11266 //
11267 // // get common prefix to strip off only if all titles match names and
11268 // // any title is longer than 10 chars
11269 // size_t e = std::min(allTitles.begin()->size(), allTitles.rbegin()->size());
11270 // size_t ii = 0;
11271 // bool goodPrefix = false;
11272 // std::string commonSuffix;
11273 // if (titleMatchName && hhs.size() > 1) {
11274 // while (ii < e - 1 && allTitles.begin()->at(ii) == allTitles.rbegin()->at(ii)) {
11275 // ii++;
11276 // if (allTitles.begin()->at(ii) == '_' || allTitles.begin()->at(ii) == ' ')
11277 // goodPrefix = true;
11278 // }
11279 //
11280 // // find common suffix if there is one .. must start with a "_"
11281 // bool stop = false;
11282 // while (!stop && commonSuffix.size() < size_t(e - 1)) {
11283 // commonSuffix = allTitles.begin()->substr(allTitles.begin()->length() - commonSuffix.length() -
11284 // 1); for (auto &t : allTitles) {
11285 // if (!TString(t).EndsWith(commonSuffix.c_str())) {
11286 // commonSuffix = commonSuffix.substr(1);
11287 // stop = true;
11288 // break;
11289 // }
11290 // }
11291 // }
11292 // if (commonSuffix.find('_') == std::string::npos) {
11293 // commonSuffix = "";
11294 // } else {
11295 // commonSuffix = commonSuffix.substr(commonSuffix.find('_'));
11296 // }
11297 // }
11298 // if (!goodPrefix)
11299 // ii = 0;
11300 //
11301 // // also find how many characters are needed to distinguish all entries (that dont have the same
11302 // name)
11303 // // then carry on up to first space or underscore
11304 // size_t jj = 0;
11305 // std::map<std::string, std::string> reducedTitles;
11306 // while (reducedTitles.size() != allTitles.size()) {
11307 // jj++;
11308 // std::map<std::string, int> titlesMap;
11309 // for (auto &s : allTitles) {
11310 // if (reducedTitles.count(s))
11311 // continue;
11312 // titlesMap[s.substr(0, jj)]++;
11313 // }
11314 // for (auto &s : allTitles) {
11315 // if (titlesMap[s.substr(0, jj)] == 1 && (jj >= s.length() || s.at(jj) == ' ' || s.at(jj) ==
11316 // '_')) {
11317 // reducedTitles[s] = s.substr(0, jj);
11318 // }
11319 // }
11320 // }
11321 //
11322 // // strip common prefix and suffix before adding
11323 // for (auto ritr = hhs.rbegin(); ritr != hhs.rend(); ++ritr) { // go in reverse order
11324 // if (!histsWithBadTitles.count((*ritr))) {
11325 // continue;
11326 // }
11327 // auto _title = (hhs.size() > 5) ? reducedTitles[(*ritr)->GetTitle()] : (*ritr)->GetTitle();
11328 // _title = _title.substr(ii < _title.size() ? ii : 0);
11329 // if (!commonSuffix.empty() && TString(_title).EndsWith(commonSuffix.c_str()))
11330 // _title = _title.substr(0, _title.length() - commonSuffix.length());
11331 // (*ritr)->SetTitle(_title.c_str());
11332 // }
11333 // }
11334 //
11335 // for (auto &hh : hhs) {
11336 // // automatically group hists that all have the same title
11337 // if (histGroups.find(hh->GetTitle()) == histGroups.end()) {
11338 // histGroups[hh->GetTitle()] = hh;
11339 // } else {
11340 // // add it into this group
11341 // histGroups[hh->GetTitle()]->Add(hh);
11342 // delete hh;
11343 // hh = nullptr;
11344 // continue;
11345 // }
11346 // auto hhMin = (hh->GetMinimum() == 0) ? hh->GetMinimum(1e-9) : hh->GetMinimum();
11347 // if (!stack->GetHists() && h->GetMinimum() > hhMin) {
11348 // auto newMin = hhMin - (h->GetMaximum() - hhMin) * gStyle->GetHistTopMargin();
11349 // if (hhMin >= 0 && newMin < 0)
11350 // newMin = hhMin * 0.99;
11351 // adjustYRange(newMin, h->GetMaximum());
11352 // }
11353 //
11354 // /*if(stack->GetHists() && stack->GetHists()->GetEntries()>0) {
11355 // // to remove rounding effects on bin boundaries, see if binnings compatible
11356 // auto _h1 = dynamic_cast<TH1*>(stack->GetHists()->At(0));
11357 // if(_h1->GetNbinsX()==hh->GetNbinsX()) TODO ... finish dealing with silly rounding effects
11358 // }*/
11359 // TString thisOpt = dOpt;
11360 // // uncomment next line to blend continuous with discrete components .. get some unpleasant "poke
11361 // through"
11362 // // effects though
11363 // // if(auto s = samp->get<RooAbsReal>(); s) thisOpt =
11364 // s->isBinnedDistribution(*dynamic_cast<RooAbsArg*>(v)) ?
11365 // // "" : "LF2";
11366 // stack->Add(hh, thisOpt);
11367 // }
11368 // stack->SetBit(kCanDelete); // should delete its sub histograms
11369 // stack->Draw("noclear same");
11370 // h->Draw(
11371 // dOpt + sOpt +
11372 // "same"); // overlay again .. if stack would cover original hist (negative components) we still see
11373 // integral
11374 // h->Draw("axissame"); // redraws axis
11375 //
11376 // TList *ll = stack->GetHists();
11377 // if (ll && ll->GetEntries()) {
11378 //
11379 // // finally, ensure all hists are styled
11380 // for (auto ho : *ll) {
11381 // TH1 *hh = dynamic_cast<TH1 *>(ho);
11382 // if (!hh)
11383 // continue;
11384 // bool createdStyle = (xRooNode(*hh, *this).styles(nullptr, false).get<TStyle>() == nullptr);
11385 //
11386 // if (createdStyle) {
11387 // // give hist a color, that isn't the same as any other hists color
11388 // hh->SetFillStyle(1001); // solid fill style
11389 // bool used = false;
11390 // do {
11391 // hh->SetFillColor((count++));
11392 // // check not already used this color
11393 // used = false;
11394 // for (auto ho2 : *ll) {
11395 // TH1 *hh2 = dynamic_cast<TH1 *>(ho2);
11396 // if (!hh2)
11397 // continue;
11398 // auto _styleNode = xRooNode(*hh2, *this).styles(hh2, false);
11399 // auto _style = _styleNode.get<TStyle>();
11400 // if (hh != hh2 && _style && _style->GetFillColor() == hh->GetFillColor()) {
11401 // used = true;
11402 // break;
11403 // }
11404 // }
11405 // } while (used);
11406 // }
11407 //
11408 // auto _styleNode = xRooNode(*hh, *this).styles(hh);
11409 // if (auto _style = _styleNode.get<TStyle>()) {
11410 // *dynamic_cast<TAttLine *>(hh) = *_style;
11411 // *dynamic_cast<TAttFill *>(hh) = *_style;
11412 // *dynamic_cast<TAttMarker *>(hh) = *_style;
11413 // }
11414 // // for stacks, fill color of white should be color 10 unless fill style is 0
11415 // if (hh->GetFillColor() == kWhite && hh->GetFillStyle() != 0) {
11416 // // kWhite means 'transparent' in ROOT ... should really use a FillStyle of 0 for that
11417 // // so assume user wanted actual white, which is color 10
11418 // hh->SetFillColor(10);
11419 // }
11420 // addLegendEntry(hh, hh->GetTitle(), "f");
11421 // }
11422 // }
11423 // }
11424 } else if (!overlayExisted) {
11425
11426 if (errHist) {
11427 addLegendEntry(errHist, strlen(errHist->GetTitle()) ? errHist->GetTitle() : GetName(), "fl");
11428 } else {
11429 addLegendEntry(h, strlen(h->GetTitle()) ? h->GetTitle() : GetName(), (hasError) ? "fl" : "l");
11430 }
11431 }
11432
11433 if (errHist) {
11434 dOpt.ReplaceAll("TEXT", "");
11435 errHist->Draw(dOpt + (dOpt.Contains("LF2") ? "e3same" : "e2same"));
11436 double ymax = -std::numeric_limits<double>::infinity();
11437 double ymin = std::numeric_limits<double>::infinity();
11438 for (int i = 1; i <= errHist->GetNbinsX(); i++) {
11439 ymax = std::max(ymax, errHist->GetBinContent(i) + errHist->GetBinError(i));
11440 ymin = std::min(ymin, errHist->GetBinContent(i) - errHist->GetBinError(i));
11441 }
11443 } else {
11444 adjustYRange(h->GetMinimum() * 0.9, h->GetMaximum() * 1.1);
11445 }
11446
11447 if ((!auxPlotTitle.empty()) && !hasSame) {
11448 // create a pad for the ratio ... shift the bottom margin of this pad to make space for it
11449 double padFrac = 0.3;
11450 auto _tmpPad = gPad;
11451 gPad->SetBottomMargin(padFrac);
11452 auto ratioPad = new TPad("auxPad", "aux plot", 0, 0, 1, padFrac);
11453 ratioPad->SetFillColor(_tmpPad->GetFillColor());
11454 ratioPad->SetNumber(1);
11455 ratioPad->SetBottomMargin(ratioPad->GetBottomMargin() / padFrac);
11456 ratioPad->SetTopMargin(0.04);
11457 ratioPad->SetLeftMargin(gPad->GetLeftMargin());
11458 ratioPad->SetRightMargin(gPad->GetRightMargin());
11459 ratioPad->cd();
11460 TH1 *ratioHist = dynamic_cast<TH1 *>((errHist) ? errHist->Clone("auxHist") : h->Clone("auxHist"));
11461 ratioHist->Reset();
11462 ratioHist->Add(h); // removes function list
11463 ratioHist->SetDirectory(nullptr);
11464 ratioHist->SetTitle((errHist) ? errHist->GetName()
11465 : h->GetName()); // abuse the title string to hold the name of the main hist
11466
11467 ratioHist->GetYaxis()->SetNdivisions(5, 0, 0);
11468 ratioHist->GetYaxis()->SetTitle(auxPlotTitle.c_str());
11469 ratioHist->SetTitle(
11470 TString::Format("%s|%s", ratioHist->GetTitle(),
11471 auxPlotTitle.c_str())); // used when plotting data (above) to decide what to calculate
11472 ratioHist->SetMaximum();
11473 ratioHist->SetMinimum(); // resets min and max
11474 ratioPad->SetGridy();
11475
11476 for (int i = 1; i <= ratioHist->GetNbinsX(); i++) {
11477 double val = ratioHist->GetBinContent(i);
11478 double err = ratioHist->GetBinError(i);
11479 ratioHist->SetBinContent(i, std::get<0>(auxFunctions[auxPlotTitle])(val, val, err));
11480 ratioHist->SetBinError(i, std::get<0>(auxFunctions[auxPlotTitle])(val + err, val, err) -
11481 ratioHist->GetBinContent(i));
11482 }
11483
11484 double rHeight = (1.-padFrac) / padFrac; //(_tmpPad->GetWNDC())/(gPad->GetHNDC());
11485 if (ratioHist->GetYaxis()->GetTitleFont() % 10 == 2) {
11486 ratioHist->GetYaxis()->SetTitleSize(ratioHist->GetYaxis()->GetTitleSize() * rHeight);
11487 ratioHist->GetYaxis()->SetLabelSize(ratioHist->GetYaxis()->GetLabelSize() * rHeight);
11488 ratioHist->GetXaxis()->SetTitleSize(ratioHist->GetXaxis()->GetTitleSize() * rHeight);
11489 ratioHist->GetXaxis()->SetLabelSize(ratioHist->GetXaxis()->GetLabelSize() * rHeight);
11490 ratioHist->GetYaxis()->SetTitleOffset(ratioHist->GetYaxis()->GetTitleOffset() / rHeight);
11491 } else {
11492#if ROOT_VERSION_CODE < ROOT_VERSION(6, 26, 00)
11493 ratioHist->GetYaxis()->SetTitleOffset(ratioHist->GetYaxis()->GetTitleOffset() / rHeight);
11494#endif
11495 }
11496 ratioHist->GetXaxis()->SetTickLength(ratioHist->GetXaxis()->GetTickLength() * rHeight);
11497 ratioHist->SetStats(false);
11498 ratioHist->SetBit(TH1::kNoTitle);
11499 ratioHist->SetBit(kCanDelete);
11500 if (errHist) {
11501 auto _h = dynamic_cast<TH1 *>(ratioHist->Clone("auxHist_clone"));
11502 _h->SetDirectory(nullptr);
11503 _h->SetFillColor(0);
11504 ratioHist->GetListOfFunctions()->Add(_h, "histsame");
11505 //_h->Draw("histsame");
11506 }
11507 ratioHist->GetListOfFunctions()->Add(new TExec(
11508 ".updateAxis",
11509 TString::Format("auto h1 = (TH1*)%p; auto h2 = (TH1*)%p; if(h2->GetXaxis()->GetFirst() != "
11510 "h1->GetXaxis()->GetFirst() || h1->GetXaxis()->GetLast()!=h2->GetXaxis()->GetLast()) "
11511 "{h2->GetXaxis()->SetRange(h1->GetXaxis()->GetFirst(),h1->GetXaxis()->GetLast());if(gPad) "
11512 "{gPad->GetCanvas()->Paint();gPad->GetCanvas()->Update();}}",
11513 (void *)ratioHist, (void *)(h))));
11514 ratioHist->Draw((errHist ? "e2" : ""));
11515
11516 _tmpPad->cd();
11517 ratioPad->Draw();
11518 } else if (auto ratioPad = dynamic_cast<TPad *>(gPad->GetPrimitive("auxPad")); hasSame && ratioPad) {
11519 // need to draw histogram in the ratio pad ...
11520 // if doing overlay need to update histogram
11521
11522 if (auto hr = dynamic_cast<TH1 *>(ratioPad->GetPrimitive("auxHist"));
11523 hr && auxFunctions.find(hr->GetYaxis()->GetTitle()) != auxFunctions.end()) {
11524 TString histName = hr->GetTitle(); // split it by | char
11525 TString histType = histName(histName.Index('|') + 1, histName.Length());
11526 histName = histName(0, histName.Index('|'));
11527
11528 if (auto hnom = dynamic_cast<TH1 *>(gPad->GetPrimitive(histName)); hnom) {
11529 h = dynamic_cast<TH1 *>(h->Clone(h->GetName()));
11530 h->SetDirectory(nullptr);
11531 h->SetBit(kCanDelete);
11532 for (int i = 1; i <= hnom->GetNbinsX(); i++) {
11533 double val = h->GetBinContent(i);
11534 double err = h->GetBinError(i);
11535 h->SetBinContent(i, std::get<0>(auxFunctions[hr->GetYaxis()->GetTitle()])(
11536 h->GetBinContent(i), hnom->GetBinContent(i), hnom->GetBinError(i)));
11537 h->SetBinError(i, std::get<0>(auxFunctions[hr->GetYaxis()->GetTitle()])(
11538 val + err, hnom->GetBinContent(i), hnom->GetBinError(i)) -
11539 h->GetBinContent(i));
11540 }
11541 auto _tmpPad = gPad;
11542 ratioPad->cd();
11543 if (hasOverlay) {
11544 if (auto existing = dynamic_cast<TH1 *>(ratioPad->GetPrimitive(h->GetName())); existing) {
11545 existing->Reset();
11546 existing->Add(h);
11547 delete h;
11548 h = existing;
11549 overlayExisted = true;
11550 } else {
11551 h->Draw(dOpt);
11552 }
11553 } else {
11554 h->Draw(dOpt);
11555 }
11556 double ymax = -std::numeric_limits<double>::infinity();
11557 double ymin = std::numeric_limits<double>::infinity();
11558 for (int i = 1; i <= h->GetNbinsX(); i++) {
11559 ymax = std::max(ymax, h->GetBinContent(i) + h->GetBinError(i));
11560 ymin = std::min(ymin, h->GetBinContent(i) - h->GetBinError(i));
11561 }
11562 adjustYRange(ymin, ymax, hr, std::get<1>(auxFunctions[hr->GetYaxis()->GetTitle()]));
11563 // adjustYRange(h->GetMinimum() * (h->GetMinimum()<0 ? 1.1 : 0.9), h->GetMaximum() * (h->GetMinimum()<0 ?
11564 // 0.9 : 1.1), hr, std::get<1>(auxFunctions[hr->GetYaxis()->GetTitle()]));
11565 gPad->Modified();
11566 _tmpPad->cd();
11567 }
11568 }
11569 }
11570
11571 // see if it's in a simultaneous so need to select a cat
11572 /*auto _parent = fParent;
11573 auto _me = rar;
11574 while(_parent) {
11575 if (auto s = _parent->get<RooSimultaneous>(); s) {
11576 for (auto c : s->indexCat()) {
11577 if (auto p = s->getPdf(c.first.c_str());_me==p) {
11578 gPad->SetName(c.first.c_str());
11579 break;
11580 }
11581 }
11582 break;
11583 }
11584 _me = _parent->get<RooAbsReal>();
11585 _parent = _parent->fParent;
11586 }*/
11587
11588 // now draw selected datasets on top if this was a pdf
11589 if (auto _pdf = get<RooAbsPdf>();
11590 !hasSame && _pdf /*&& (_pdf->canBeExtended() || robs().empty())*/ && coefs(true).empty()) {
11591 auto _dsets = datasets();
11592 // bool _drawn=false;
11593 for (auto &d : _dsets) {
11594 if (d->get()->TestBit(1 << 20)) {
11595 d->Draw("same");
11596 //_drawn=true;
11597 }
11598 }
11599 // if (!_drawn && !_dsets.empty()) _dsets[0]->Draw("same"); // always draw if has a dataset
11600 }
11601
11602 gPad->Modified();
11603 // gPad->Update();
11604 getLegend();
11605 gPad->Modified();
11606 // gPad->Update();
11607}
11608
11609void xRooNode::SaveAs(const char *filename, Option_t *option) const
11610{
11612 sOpt.ToLower();
11613 if (auto w = get<RooWorkspace>(); w) {
11614 // ensure the current color set is saved in the workspace
11615 w->import(*gROOT->GetListOfColors(), true);
11616
11617 if (TString(filename).EndsWith(".json")) {
11618#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
11619 // stream with json tool
11621 if (tool.exportJSON(filename)) {
11622 Info("SaveAs", "%s saved to %s", w->GetName(), filename);
11623 } else {
11624 Error("SaveAs", "Unable to save to %s", filename);
11625 }
11626#else
11627 Error("SaveAs", "json format workspaces only in ROOT 6.26 onwards");
11628#endif
11629 return;
11630 }
11631
11632#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
11633 // before saving, clear the eocache of all owned nodes
11634 // because causes memory leak when read back in (workspace streamer immediately overwrites the caches)
11635 // fixed in: https://github.com/root-project/root/pull/12024
11636 for (auto &c : w->components()) {
11637 c->_eocache = nullptr;
11638 }
11639#endif
11640 // const_cast<Node2*>(this)->sterilize(); - tried this to reduce mem leak on readback but no improve
11641 if (!w->writeToFile(filename, sOpt != "update")) {
11642 Info("SaveAs", "%s saved to %s", w->GetName(), filename);
11643 // save any fitDatabase that is loaded in memory too
11644 // TODO: We should do this as well for SaveAs on a scan object
11645 if (auto fitDb = dynamic_cast<TFile *>(gROOT->GetListOfFiles()->FindObject("fitDatabase"))) {
11646
11647 std::function<void(TDirectory *, TDirectory *)> CopyDir;
11648
11650 auto dir = dest->GetDirectory(source->GetName());
11651 if (!dir) {
11652 dir = dest->mkdir(source->GetName());
11653 }
11654 for (auto k : *source->GetListOfKeys()) {
11655 auto key = dynamic_cast<TKey *>(k);
11656 const char *classname = key->GetClassName();
11657 TClass *cl = gROOT->GetClass(classname);
11658 // std::cout << "processing " << key->GetName() << " " << classname << std::endl;
11659 if (!cl) {
11660 continue;
11661 } else if (cl->InheritsFrom(TDirectory::Class())) {
11662 CopyDir(source->GetDirectory(key->GetName()), dir);
11663 } else {
11664 // don't write object if it already exists
11665 if (dir->FindKey(key->GetName()))
11666 continue;
11667 // support FitConfigs ....
11668 if (strcmp(classname, "ROOT::Fit::FitConfig") == 0) {
11669 auto fc = key->ReadObject<ROOT::Fit::FitConfig>();
11670 dir->WriteObject(fc, key->GetName());
11671 delete fc;
11672 } else {
11673 TObject *obj = key->ReadObj();
11674 if (obj) {
11675 dir->WriteTObject(obj, key->GetName());
11676 delete obj;
11677 }
11678 }
11679 }
11680 }
11681 };
11682 CopyDir(fitDb, std::make_unique<TFile>(filename, "UPDATE").get());
11683 Info("SaveAs", "Saved fitDatabase to %s", filename);
11684 }
11685
11686 } else {
11687 Error("SaveAs", "Unable to save to %s", filename);
11688 }
11689#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
11690 // restore the cache to every node
11691 for (auto &c : w->components()) {
11692 c->setExpensiveObjectCache(w->expensiveObjectCache());
11693 }
11694#endif
11695 }
11696}
11697
11698double xRooNode::GetBinError(int bin, const xRooNode &fr, int nToys, bool errorsHi, bool errorsLo) const
11699{
11700 auto res = GetBinErrors(bin, bin, fr, nToys, errorsHi, errorsLo);
11701 if (res.empty())
11702 return std::numeric_limits<double>::quiet_NaN();
11703 return res.at(0);
11704}
11705
11706std::vector<double> xRooNode::contents() const
11707{
11708 std::vector<double> out;
11709 out.reserve(size());
11710 for (auto child : *this) {
11711 out.push_back(child->GetContent());
11712 }
11713 return out;
11714}
11715
11717{
11718
11719 auto _fr = fr.get<RooFitResult>();
11720
11721 if (!_fr) {
11722 return covariances(fitResult());
11723 }
11724
11725 auto rho = _fr->correlationMatrix();
11726
11727 TMatrixDSym out(size());
11728
11729 auto _pars = pars();
11730
11731 // formula for covariance is: C_ij = sum_m[sum_n[ (1/2)(nu[theta_m_up] - nu[theta_m_down])rho_mn(1/2)(nu[theta_n_up]
11732 // - nu[theta_n_down]) consistent with propagatedError formula
11733
11734 for (int m = 0; m < rho.GetNrows(); m++) {
11735 auto p_m = dynamic_cast<RooRealVar *>(_fr->floatParsFinal().at(m));
11736 if (!p_m)
11737 continue; // skip categoricals
11738 auto _p = dynamic_cast<RooAbsRealLValue *>(_pars.get<RooArgList>()->find(p_m->GetName()));
11739 if (!_p)
11740 continue;
11741 auto tmp = _p->getVal();
11742 _p->setVal(p_m->getVal() + p_m->getErrorHi());
11743 auto nu_m = contents();
11744 _p->setVal(p_m->getVal() + p_m->getErrorLo());
11745 auto nu_m2 = contents();
11746 _p->setVal(tmp);
11747 for (int n = 0; n < rho.GetNrows(); n++) {
11748 auto p_n = dynamic_cast<RooRealVar *>(_fr->floatParsFinal().at(n));
11749 if (!p_n)
11750 continue; // skip categoricals
11751 auto _p2 = dynamic_cast<RooAbsRealLValue *>(_pars.get<RooArgList>()->find(p_n->GetName()));
11752 if (!_p2)
11753 continue;
11754 auto tmp2 = _p2->getVal();
11755 _p2->setVal(p_n->getVal() + p_n->getErrorHi());
11756 auto nu_n = (p_n == p_m) ? nu_m : contents();
11757 _p2->setVal(p_n->getVal() + p_n->getErrorLo());
11758 auto nu_n2 = (p_n == p_m) ? nu_m2 : contents();
11759 _p2->setVal(tmp2);
11760 for (int i = 0; i < out.GetNrows(); i++) {
11761 for (int j = 0; j < out.GetNrows(); j++) {
11762 out(i, j) += 0.25 * (nu_m[i] - nu_m2[i]) * rho(m, n) * (nu_n[j] - nu_n2[j]);
11763 }
11764 }
11765 }
11766 }
11767 return out;
11768}
11769
11770std::pair<double, double> xRooNode::IntegralAndError(const xRooNode &fr, const char *rangeName) const
11771{
11772 double out = 1.;
11773 double err = std::numeric_limits<double>::quiet_NaN();
11774
11775 std::unique_ptr<RooAbsCollection> _snap;
11777 if (auto _fr = fr.get<RooFitResult>()) {
11778 _pars.add(pars().argList());
11779 _snap.reset(_pars.snapshot());
11780 _pars = _fr->floatParsFinal();
11781 _pars = _fr->constPars();
11782 }
11783
11784 auto _obs = obs();
11785 RooArgSet sobs(*_obs.get<RooArgList>()); // need to make explicit RooArgSet for ROOT 6.36 onwards
11786 auto _coefs = coefs(); // need here to keep alive owned RooProduct
11787 if (auto c = _coefs.get<RooAbsReal>(); c) {
11788 out = c->getVal(sobs); // assumes independent of observables!
11789 }
11790
11791 if (auto p = dynamic_cast<RooAbsPdf *>(get()); p) {
11792 // prefer to use expectedEvents for integrals of RooAbsPdf e.g. for RooProdPdf wont include constraint terms
11793 if (rangeName)
11794 p->setNormRange(rangeName);
11795#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 27, 00)
11797#endif
11798 out *= p->expectedEvents(*_obs.get<RooArgList>());
11799#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
11800 // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
11801 p->_normSet = nullptr;
11802#endif
11803 err = GetBinError(-1, fr);
11804 if (rangeName)
11805 p->setNormRange(nullptr);
11806 } else if (auto p2 = dynamic_cast<RooAbsReal *>(get()); p2) {
11807 // only integrate over observables we actually depend on
11808 auto f = std::shared_ptr<RooAbsReal>(
11809 p2->createIntegral(*std::unique_ptr<RooArgSet>(p2->getObservables(*_obs.get<RooArgList>())),
11810 rangeName)); // did use x here before using obs
11811 RooProduct pr("int_x_coef", "int_x_coef",
11812 RooArgList(*f, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>()));
11813 out *= f->getVal();
11814 err = xRooNode(pr, *this).GetBinError(-1, fr);
11815 sterilize(); // needed so that we can forget properly about the integral we just created (and are deleting)
11816 } else if (get<RooAbsData>()) {
11817 out = 0;
11818 auto vals = GetBinContents(1, 0); // returns all bins
11819 auto ax = (rangeName) ? GetXaxis() : nullptr;
11820 auto rv = (ax) ? dynamic_cast<RooRealVar *>(ax->GetParent()) : nullptr;
11821 auto cv = (ax && !rv) ? dynamic_cast<RooCategory *>(ax->GetParent()) : nullptr;
11822 int i = 0;
11823 for (auto &v : vals) {
11824 i++;
11825 if (rangeName) {
11826 if (rv && !rv->inRange(ax->GetBinCenter(i), rangeName))
11827 continue;
11828 if (cv && !cv->isStateInRange(rangeName, ax->GetBinLabel(i)))
11829 continue;
11830 }
11831 out += v;
11832 }
11833 err = 0; // should this be sqrt(sum(v^2)) or something similar
11834 } else {
11835 out = std::numeric_limits<double>::quiet_NaN();
11836 }
11837 if (_snap) {
11838 _pars.RooAbsCollection::operator=(*_snap);
11839 }
11840 return std::make_pair(out, err);
11841}
11842
11843std::vector<double>
11844xRooNode::GetBinErrors(int binStart, int binEnd, const xRooNode &_fr, int nToys, bool errorHi, bool errorLo) const
11845{
11846 // note: so far this method is inconsistent with the BuildHistogram in ways:
11847 // no projection over other variables
11848 // July2023: made RooRealSumPdf evaluate as a function if doesn't have a floor
11849 // but this method will still evaluate it as a pdf (uses PdfWrapper)
11850 // but can get away with it while added NaN recovery to getSimplePropagatedError to pickup raw values
11851
11852 if (fBinNumber != -1) {
11853 if (binStart != binEnd || !fParent) {
11854 throw std::runtime_error(TString::Format("%s is a bin - only has one value", GetName()));
11855 }
11856 return fParent->GetBinErrors(fBinNumber, fBinNumber, _fr);
11857 }
11858
11859 std::vector<double> out;
11860
11861 auto _hist = BuildHistogram(nullptr, true, true, binStart, binEnd, _fr, errorHi, errorLo, nToys);
11862 if (!_hist)
11863 return out;
11864 if (binEnd == 0) {
11865 binEnd = _hist->GetNbinsX();
11866 } else if (binEnd == binStart && binEnd == -1) {
11867 binStart = 1;
11868 binEnd = 1; // done an integral, so histogram has only 1 bin
11869 }
11870 for (int bin = binStart; bin <= binEnd; bin++) {
11871 out.push_back(((errorLo && !errorHi) ? (-1.) : 1.) *
11872 _hist->GetBinError(bin)); // using same convention as RooFit that Lo errors are negative
11873 }
11874 delete _hist;
11875 return out;
11876
11877 // auto o = dynamic_cast<RooAbsReal *>(get());
11878 // if (!o)
11879 // return out;
11880 //
11881 // std::shared_ptr<RooFitResult> fr = std::dynamic_pointer_cast<RooFitResult>(_fr.fComp);
11882 // //= dynamic_cast<RooFitResult*>( _fr.get<RooFitResult>() ? _fr->Clone() : fitResult()->Clone());
11883 //
11884 // auto _coefs = coefs();
11885 //
11886 // if (!fr) {
11887 // // need to ensure coefs, if any, are included in fit result retrieval so all pars are loaded
11888 // auto frn = (!_coefs.get() ? *this : xRooNode(RooProduct("tmp", "tmp", RooArgList(*o,
11889 // *_coefs.get<RooAbsReal>()))))
11890 // .fitResult();
11891 // if (strlen(_fr.GetName()))
11892 // frn = frn.reduced(_fr.GetName());
11893 //
11894 // // use name to reduce the fit result, if one given
11895 // fr = std::dynamic_pointer_cast<RooFitResult>(frn.fComp);
11896 // }
11897 //
11898 // if (!GETDMP(fr.get(), _finalPars)) {
11899 // fr->setFinalParList(RooArgList());
11900 // }
11901 //
11902 // /// Oct2022: No longer doing this because want to allow fitResult to be used to get partial error
11903 // // // need to add any floating parameters not included somewhere already in the fit result ...
11904 // // RooArgList l;
11905 // // for(auto& p : pars()) {
11906 // // auto v = p->get<RooRealVar>();
11907 // // if (!v) continue;
11908 // // if (v->isConstant()) continue;
11909 // // if (fr->floatParsFinal().find(v->GetName())) continue;
11910 // // if (fr->_constPars && fr->_constPars->find(v->GetName())) continue;
11911 // // l.add(*v);
11912 // // }
11913 // //
11914 // // if (!l.empty()) {
11915 // // RooArgList l2; l2.addClone(fr->floatParsFinal());
11916 // // l2.addClone(l);
11917 // // fr->setFinalParList(l2);
11918 // // }
11919 //
11920 // TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr.get(), _VM));
11921 //
11922 // if (!prevCov || size_t(prevCov->GetNcols()) < fr->floatParsFinal().size()) {
11923 // TMatrixDSym cov(fr->floatParsFinal().size());
11924 // if (prevCov) {
11925 // for (int i = 0; i < prevCov->GetNcols(); i++) {
11926 // for (int j = 0; j < prevCov->GetNrows(); j++) {
11927 // cov(i, j) = (*prevCov)(i, j);
11928 // }
11929 // }
11930 // }
11931 // int i = 0;
11932 // for (auto &p : fr->floatParsFinal()) {
11933 // if (!prevCov || i >= prevCov->GetNcols()) {
11934 // cov(i, i) = pow(dynamic_cast<RooRealVar *>(p)->getError(), 2);
11935 // }
11936 // i++;
11937 // }
11938 // int covQualBackup = fr->covQual();
11939 // fr->setCovarianceMatrix(cov);
11940 // fr->setCovQual(covQualBackup);
11941 // }
11942 //
11943 // bool doBinWidth = false;
11944 // auto ax = (binStart == -1 && binEnd == -1) ? nullptr : GetXaxis();
11945 //
11946 // auto _obs = obs(); // may own an obs so keep alive here
11947 // RooArgList normSet = _obs.argList();
11948 // // to give consistency with BuildHistogram method, should be only the axis var if defined
11949 // if (ax) {
11950 // normSet.clear();
11951 // normSet.add(*dynamic_cast<RooAbsArg *>(ax->GetParent()));
11952 // }
11953 //
11954 // if (auto p = dynamic_cast<RooAbsPdf *>(o); ax && (p || _coefs.get() || o->getAttribute("density"))) {
11955 // // pdfs of samples embedded in a sumpdf (aka have a coef) will convert their density value to a content
11956 // doBinWidth = true;
11957 // }
11958 // if (binEnd == 0) {
11959 // if (ax) {
11960 // binEnd = ax->GetNbins();
11961 // } else {
11962 // binEnd = binStart;
11963 // }
11964 // }
11965 // for (int bin = binStart; bin <= binEnd; bin++) {
11966 // if (ax)
11967 // dynamic_cast<RooAbsLValue *>(ax->GetParent())->setBin(bin - 1, ax->GetName());
11968 // // if (!SetBin(bin)) { return out; }
11969 //
11970 // double res;
11971 // if (auto p = dynamic_cast<RooAbsPdf *>(o); p) {
11972 // // fr->covarianceMatrix().Print();
11973 // res = PdfWrapper(*p, _coefs.get<RooAbsReal>(), !ax).getSimplePropagatedError(*fr, normSet);
11974 // #if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
11975 // // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
11976 // p->_normSet = nullptr;
11977 // #endif
11978 // } else {
11979 // // res = o->getPropagatedError(*fr, normSet);
11980 // // // TODO: What if coef has error? - probably need a FuncWrapper class
11981 // // if (auto c = _coefs.get<RooAbsReal>(); c) {
11982 // // res *= c->getVal(normSet);
11983 // // }
11984 // res = RooProduct("errorEval", "errorEval",
11985 // RooArgList(*o, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>()))
11986 // .getPropagatedError(*fr, normSet);
11987 // }
11988 // if (doBinWidth) {
11989 // res *= ax->GetBinWidth(bin);
11990 // }
11991 // out.push_back(res);
11992 // }
11993 //
11994 // return out;
11995}
11996
11997std::string cling::printValue(const xRooNode *v)
11998{
11999 if (!v)
12000 return "nullptr\n";
12001 if (!v->empty()) {
12002 std::string out;
12003 size_t left = v->size();
12004 for (auto n : *v) {
12005 left--;
12006 if (!out.empty())
12007 out += ",";
12008 else
12009 out += "{";
12010 out += n->GetName();
12011 if (out.length() > 100 && left > 0) {
12012 out += TString::Format(",... and %lu more", left);
12013 break;
12014 }
12015 }
12016 out += "}\n";
12017 out = std::string(Form("<%s> %s", v->get() ? v->get()->ClassName() : "nullptr", v->GetName())) + out;
12018 return out;
12019 }
12020 std::string out;
12021 if (!(*v)) {
12022 return "<nullptr>";
12023 } else {
12024 return Form("<%s> %s", v->get() ? v->get()->ClassName() : "nullptr", v->GetName());
12025 }
12026
12028}
12029
@ 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.hxx:23
#define ROOT_VERSION_CODE
Definition RVersion.hxx:24
ROOT::RRangeCast< T, false, Range_t > static_range_cast(Range_t &&coll)
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
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
@ kCyan
Definition Rtypes.h:66
@ kBlue
Definition Rtypes.h:66
@ kAzure
Definition Rtypes.h:67
@ kYellow
Definition Rtypes.h:66
static void indent(ostringstream &buf, int indent_level)
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
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:229
#define gClient
Definition TGClient.h:157
@ kMBOk
Definition TGMsgBox.h:33
@ 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 dest
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 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 np
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 result
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:367
#define gROOT
Definition TROOT.h:406
char * Form(const char *fmt,...)
Formats a string in a circular formatting buffer.
Definition TString.cxx:2489
R__EXTERN TStyle * gStyle
Definition TStyle.h:442
R__EXTERN TSystem * gSystem
Definition TSystem.h:572
#define gPad
Color * colors
Definition X3DBuffer.c:21
#define _(A, B)
Definition cfortran.h:108
double GetBinLowEdge(Int_t bin) const override
Return low edge of bin.
Definition xRooNode.cxx:862
double GetBinUpEdge(Int_t bin) const override
Return up edge of bin.
Definition xRooNode.cxx:870
Int_t FindFixBin(double x) const override
Find bin number corresponding to abscissa x.
Definition xRooNode.cxx:913
void Set(Int_t nbins, const double *xbins) override
Initialize axis with variable bins.
Definition xRooNode.cxx:890
RooAbsRealLValue * rvar() const
Definition xRooNode.cxx:917
void SetTitle(const char *title) override
Set the title of the TNamed.
Definition xRooNode.cxx:881
void Set(Int_t nbins, double xmin, double xmax) override
Initialize axis with fix bins.
Definition xRooNode.cxx:903
Int_t FindFixBin(const char *label) const override
Find bin number with label.
Definition xRooNode.cxx:912
RooAbsLValue * var() const
Definition xRooNode.cxx:916
const RooAbsBinning * binning() const
Definition xRooNode.cxx:910
void Set(Int_t nbins, const float *xbins) override
Initialize axis with variable bins.
Definition xRooNode.cxx:896
const char * GetTitle() const override
Returns title of object.
Definition xRooNode.cxx:877
double GetBinWidth(Int_t bin) const override
Return bin width.
Definition xRooNode.cxx:856
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 isBinnedDistribution(const RooArgSet &obs) const override
Tests if the distribution is binned. Unless overridden by derived classes, this always returns false.
bool selfNormalized() const override
Shows if a PDF is self-normalized, which means that no attempt is made to add a normalization term.
~PdfWrapper() override
double getSimplePropagatedError(const RooFitResult &fr, const RooArgSet &nset_in) const
RooRealProxy fCoef
double evaluate() const override
Evaluate this PDF / function / constant. Needs to be overridden by all derived classes.
TObject * clone(const char *newname) const override
std::list< double > * binBoundaries(RooAbsRealLValue &obs, double xlo, double xhi) const override
Retrieve bin boundaries if this distribution is binned in obs.
PdfWrapper(const PdfWrapper &other, const char *name=nullptr)
RooRealProxy fExpPdf
PdfWrapper(RooAbsReal &f, RooAbsReal *coef, bool expEvMode=false, RooAbsPdf *expPdf=nullptr)
RooRealProxy fFunc
The PiecewiseInterpolation is a class that can morph distributions into each other,...
static std::shared_ptr< RooLinkedList > createNLLOptions()
Definition xRooFit.cxx:460
static std::pair< std::shared_ptr< RooAbsData >, std::shared_ptr< const RooAbsCollection > > generateFrom(RooAbsPdf &pdf, const RooFitResult &fr, bool expected=false, int seed=0)
Definition xRooFit.cxx:150
static xRooNLLVar createNLL(const std::shared_ptr< RooAbsPdf > pdf, const std::shared_ptr< RooAbsData > data, const RooLinkedList &nllOpts)
Definition xRooFit.cxx:101
static std::pair< double, double > matchPrecision(const std::pair< double, double > &in)
Definition xRooFit.cxx:2010
void Draw(Option_t *opt="") override
Default Draw method for all objects.
void Draw(Option_t *opt="") override
Default Draw method for all objects.
This xRooNLLVar object has several special methods, e.g.
Definition xRooNLLVar.h:59
xRooHypoSpace hypoSpace(const char *parName, int nPoints, double low, double high, double alt_value=std::numeric_limits< double >::quiet_NaN(), const xRooFit::Asymptotics::PLLType &pllType=xRooFit::Asymptotics::Unknown)
The xRooNode class is designed to wrap over a TObject and provide functionality to aid with interacti...
Definition xRooNode.h:52
void _Add_(const char *name, const char *opt)
TH1 * BuildHistogram(RooAbsLValue *v=nullptr, bool empty=false, bool errors=false, int binStart=1, int binEnd=0, const xRooNode &fr="", bool errorsHi=false, bool errorsLo=false, int nErrorToys=0, TH1 *templateHist=nullptr, bool nostack=true, bool setInterp=false) const
xRooNode filter(const xRooNode &range) const
std::vector< std::shared_ptr< xRooNode > > fBrowsables
Definition xRooNode.h:525
xRooNLLVar nll(const xRooNode &_data, std::initializer_list< RooCmdArg > nllOpts) const
xRooNode poi() const
List of parameters of interest: parameters marked as "of interest" These parameters have the "poi" at...
void SetName(const char *name) override
Set the name of the TNamed.
TGListTreeItem * GetTreeItem(TBrowser *b) const
void SetTitle(const char *title) override
Set the title of the TNamed.
xRooNode Multiply(const xRooNode &child, Option_t *opt="")
xRooNode Replace(const xRooNode &node)
bool SetData(const TObject &obj, const xRooNode &data="obsData")
xRooNode Remove(const xRooNode &child)
xRooNode globs() const
List of global observables of this node.
xRooNode Combine(const xRooNode &rhs)
xRooNode Add(const xRooNode &child, Option_t *opt="")
xRooNode(const char *type, const char *name, const char *title="")
const char * GetIconName() const override
Returns mime type name of object.
void Draw(Option_t *opt="") override
Default Draw method for all objects.
TGraph * BuildGraph(RooAbsLValue *v=nullptr, bool includeZeros=false, TVirtualPad *fromPad=nullptr) const
const std::shared_ptr< xRooNode > & at(size_t idx, bool browseResult=true) const
Definition xRooNode.h:147
bool SetBinContent(int bin, double value, const char *par=nullptr, double parVal=1)
std::shared_ptr< xRooNode > operator[](size_t idx)
Definition xRooNode.h:164
void Checked(TObject *obj, bool val)
Definition xRooNode.cxx:491
void Print(Option_t *opt="") const override
Print TNamed name and title.
std::shared_ptr< T > acquireNew(Args &&...args)
Definition xRooNode.h:274
void SaveAs(const char *filename="", Option_t *option="") const override
Save this object in the file specified by filename.
xRooNode bins() const
bins of a channel or sample, or channels of a multi-channel pdf
std::shared_ptr< xRooNode > find(const std::string &name, bool browseResult=true) const
TGListTree * GetListTree(TBrowser *b) const
xRooNode styles(TObject *initObject=nullptr, bool autoCreate=true) const
bool fInterrupted
appears that if was fXaxis then dialog box for SetXaxis will take as current value
Definition xRooNode.h:515
xRooNode generate(const xRooNode &fr="", bool expected=false, int seed=0)
void Inspect() const override
Dump contents of this object in a graphics canvas.
std::shared_ptr< xRooNode > getBrowsable(const char *name) const
bool SetXaxis(const RooAbsBinning &binning)
double GetBinError(int bin, const xRooNode &fr="", int nToys=0, bool errorsHi=false, bool errorsLo=false) const
bool SetBinData(int bin, double value, const xRooNode &data="obsData")
std::vector< double > GetBinContents(int binStart=1, int binEnd=0) const
xRooNode pp() const
List of prespecified parameters: non-floatable parameters.
std::shared_ptr< xRooNode > fProvider
Definition xRooNode.h:518
std::shared_ptr< TStyle > style(TObject *initObject=nullptr, bool autoCreate=true) const
xRooNode fitResult(const char *opt="") const
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:513
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:58
xRooNode pars() const
List of parameters (non-observables) of this node.
void SetFitResult(const RooFitResult *fr=nullptr)
xRooNode coefs(bool recurse=false) const
bool IsFolder() const override
Returns kTRUE in case object contains browsable objects (like containers or lists of other objects).
Definition xRooNode.cxx:841
double GetBinData(int bin, const xRooNode &data="obsData")
bool SetBinError(int bin, double value)
void _fit_(const char *constParValues="", const char *options="")
bool SetContents(const TObject &obj)
Definition xRooNode.h:347
std::shared_ptr< TObject > fComp
Definition xRooNode.h:500
std::vector< double > contents() const
RooWorkspace * ws() const
The RooWorkspace this node belong to, if any.
xRooNode shallowCopy(const std::string &name, std::shared_ptr< xRooNode > parent=nullptr)
void _generate_(const char *name="", bool expected=false)
std::shared_ptr< TObject > getObject(const std::string &name, const std::string &type="") const
Definition xRooNode.cxx:920
xRooNode np() const
List of nuisance parameters: non-constant parameters that are not marked of interest,...
xRooNode floats() const
List of parameters that are currently non-constant These parameters do not have the "Constant" attrib...
bool contains(const std::string &name) const
double GetBinContent(int bin) const
Definition xRooNode.h:389
xRooNode reduced(const std::string &range="", bool invert=false) const
std::vector< double > GetBinErrors(int binStart=1, int binEnd=0, const xRooNode &fr="", int nToys=0, bool errorsHi=false, bool errorsLo=false) const
auto begin() const -> xRooNodeIterator
Definition xRooNode.h:200
std::shared_ptr< xRooNode > parentPdf() const
like a parent but only for use by getObject
void _scan_(const char *what="plr", double nToys=0, const char *xvar="", int nPointsX=0, double lowX=0, double highX=0, const char *constParValues="", const char *options="")
std::shared_ptr< xRooNode > fParent
Definition xRooNode.h:503
std::pair< double, double > IntegralAndError(const xRooNode &fr="", const char *rangeName=nullptr) const
xRooNode robs() const
List of regular observables of this node.
TClass * IsA() const override
Definition xRooNode.h:531
xRooNode & operator=(const TObject &o)
auto end() const -> xRooNodeIterator
Definition xRooNode.h:201
xRooNode consts() const
List of parameters that are currently constant.
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)
std::function< xRooNode(xRooNode *) fBrowseOperation)
Definition xRooNode.h:526
xRooNode histo(const xRooNode &vars="x", const xRooNode &fr="", bool content=true, bool errors=true, bool stack=true, bool errorsHi=false, bool errorsLo=false, int nErrorToys=0) const
void Browse(TBrowser *b=nullptr) override
Browse object. May be overridden for another default action.
Definition xRooNode.cxx:567
xRooNode obs() const
List of observables (global and regular) of this node.
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:217
std::shared_ptr< TObject > convertForAcquisition(xRooNode &acquirer, const char *opt="") const
double GetError(const xRooNode &fr="") const
Definition xRooNode.h:416
xRooNode vars() const
List of variables (observables and parameters) of this node.
TMatrixDSym covariances(const xRooNode &fr="") const
Class describing the configuration of the fit, options and parameter settings using the ROOT::Fit::Pa...
Definition FitConfig.h:47
const_iterator begin() const
const_iterator end() const
Common abstract base class for objects that represent a value and a "shape" in RooFit.
Definition RooAbsArg.h:77
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 > 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 bool isFundamental() const
Is this object a fundamental type that can be added to a dataset? Fundamental-type subclasses overrid...
Definition RooAbsArg.h:192
Abstract base class for RooRealVar binning definitions.
virtual double highBound() const =0
virtual double lowBound() const =0
Abstract base class for objects that represent a discrete value that can be set from the outside,...
A space to attach TBranches.
const char * getLabel() const
Retrieve current label. Use getCurrentLabel() for more clarity.
Abstract container object that can hold multiple RooAbsArg objects.
RooAbsCollection * selectByAttrib(const char *name, bool value) const
Create a subset of the current collection, consisting only of those elements with the specified attri...
virtual bool remove(const RooAbsArg &var, bool silent=false, bool matchByNameOnly=false)
Remove the specified argument from our list.
virtual bool add(const RooAbsArg &var, bool silent=false)
Add the specified argument to list.
Storage_t::size_type size() const
bool selectCommon(const RooAbsCollection &refColl, RooAbsCollection &outColl) const
Create a subset of the current collection, consisting only of those elements that are contained as we...
RooAbsArg * find(const char *name) const
Find object with given name in list.
Abstract base class for binned and unbinned datasets.
Definition RooAbsData.h:57
RooFit::OwningPtr< RooAbsData > reduce(const RooCmdArg &arg1, const RooCmdArg &arg2={}, const RooCmdArg &arg3={}, const RooCmdArg &arg4={}, const RooCmdArg &arg5={}, const RooCmdArg &arg6={}, const RooCmdArg &arg7={}, const RooCmdArg &arg8={}) const
Create a reduced copy of this dataset.
RooArgSet const * getGlobalObservables() const
Returns snapshot of global observables stored in this data.
Definition RooAbsData.h:290
Abstract base class for objects that are lvalues, i.e.
Abstract interface for all probability density functions.
Definition RooAbsPdf.h:40
bool canBeExtended() const
If true, PDF can provide extended likelihood term.
Definition RooAbsPdf.h:218
Abstract base class for objects that represent a real value that may appear on the left hand side of ...
void setBin(Int_t ibin, const char *rangeName=nullptr) override
Set value to center of bin 'ibin' of binning 'rangeName' (or of default binning if no range is specif...
Context to temporarily change the error logging mode as long as the context is alive.
Definition RooAbsReal.h:322
Abstract base class for objects that represent a real value and implements functionality common to al...
Definition RooAbsReal.h:59
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:103
virtual void forceNumInt(bool flag=true)
Definition RooAbsReal.h:171
Efficient implementation of a sum of PDFs of the form.
Definition RooAddPdf.h:33
Calculates the sum of a set of RooAbsReal terms, or when constructed with two sets,...
Definition RooAddition.h:27
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
Abstract interface for RooAbsArg proxy classes.
Definition RooArgProxy.h:24
RooArgSet is a container object that can hold multiple RooAbsArg objects.
Definition RooArgSet.h:24
Implements a RooAbsBinning in terms of an array of boundary values, posing no constraints on the choi...
Definition RooBinning.h:27
Object to represent discrete states.
Definition RooCategory.h:28
bool defineType(const std::string &label)
Define a state with given name.
static TClass * Class()
Named container for two doubles, two integers two object points and three string pointers that can be...
Definition RooCmdArg.h:26
static const RooCmdArg & none()
Return reference to null argument.
Definition RooCmdArg.cxx:51
TObject * Clone(const char *newName=nullptr) const override
Make a clone of an object using the Streamer facility.
Definition RooCmdArg.h:58
Represents a constant real-valued object.
Definition RooConstVar.h:23
static TClass * Class()
Container class to hold N-dimensional binned data.
Definition RooDataHist.h:40
Container class to hold unbinned data.
Definition RooDataSet.h:34
RooFitResult is a container class to hold the input and output of a PDF fit to a dataset.
TMatrixDSym conditionalCovarianceMatrix(const RooArgList &params) const
Return a reduced covariance matrix, which is calculated as.
void setCovQual(Int_t val)
const TMatrixDSym & covarianceMatrix() const
Return covariance matrix.
Int_t statusCodeHistory(UInt_t icycle) const
const RooArgList & floatParsInit() const
Return list of floating parameters before fit.
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...
const RooArgList & randomizePars() const
Generate random perturbations of the final parameters using the covariance matrix.
void setCovarianceMatrix(TMatrixDSym &V)
Store externally provided correlation matrix in this RooFitResult ;.
double edm() const
Return estimated distance to minimum.
const RooArgList & constPars() const
Return list of constant parameters.
const char * statusLabelHistory(UInt_t icycle) const
Int_t covQual() const
Return MINUIT quality code of covariance matrix.
TH2 * correlationHist(const char *name="correlation_matrix") const
Return TH2D of correlation 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.
UInt_t numStatusHistory() const
const TMatrixDSym & correlationMatrix() const
Return correlation matrix ;.
double minNll() const
Return minimized -log(L) value.
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
Implementation of a probability density function that takes a RooArgList of servers and a C++ express...
A real-valued function sampled from a multidimensional histogram.
Definition RooHistFunc.h:31
bool isBinnedDistribution(const RooArgSet &) const override
Tests if the distribution is binned. Unless overridden by derived classes, this always returns false.
Definition RooHistFunc.h:95
When using RooFit, statistical models can be conveniently handled and stored as a RooWorkspace.
Collection class for internal use, storing a collection of RooAbsArg pointers in a doubly linked list...
static RooMsgService & instance()
Return reference to singleton instance.
Implementation of a RooCacheManager<RooAbsCacheElement> that specializes in the storage of cache elem...
Poisson pdf.
Definition RooPoisson.h:19
Efficient implementation of a product of PDFs of the form.
Definition RooProdPdf.h:36
Represents the product of a given set of RooAbsReal objects.
Definition RooProduct.h:29
static TClass * Class()
A RooAbsPdf implementation that represent a projection of a given input p.d.f and the object returned...
RooProjectedPdf()
Default constructor.
Implements a PDF constructed from a sum of functions:
Variable that can be changed from the outside.
Definition RooRealVar.h:37
void setVal(double value) override
Set value of variable to 'value'.
void setError(double value)
Definition RooRealVar.h:60
static TClass * Class()
bool hasError(bool allowZero=true) const
Definition RooRealVar.h:59
Facilitates simultaneous fitting of multiple PDFs to subsets of a given dataset.
const RooAbsCategoryLValue & indexCat() const
HypoTestResult is a base class for results from hypothesis tests.
ModelConfig is a simple class that holds configuration information specifying how a model should be u...
Definition ModelConfig.h:35
A RooAbsArg implementing string values.
Implementation of RooAbsBinning that provides a uniform binning in 'n' bins between the range end poi...
Persistable container for RooFit projects.
bool removeSet(const char *name)
Remove a named set from the workspace.
static TClass * Class()
RooFactoryWSTool & factory()
Return instance to factory tool.
bool import(const RooAbsArg &arg, const RooCmdArg &arg1={}, const RooCmdArg &arg2={}, const RooCmdArg &arg3={}, const RooCmdArg &arg4={}, const RooCmdArg &arg5={}, const RooCmdArg &arg6={}, const RooCmdArg &arg7={}, const RooCmdArg &arg8={}, const RooCmdArg &arg9={})
Import a RooAbsArg object, e.g.
virtual void SetTickSize(Float_t size=0.03)
Definition TAttAxis.h:60
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:20
virtual void SetFillStyle(Style_t fstyle)
Set the fill area style.
Definition TAttFill.h:40
Line Attributes class.
Definition TAttLine.h:20
Marker Attributes class.
Definition TAttMarker.h:20
virtual Font_t GetTextFont() const
Return the text font.
Definition TAttText.h:37
virtual void SetTextSize(Float_t tsize=1)
Set the text size.
Definition TAttText.h:49
Class to manage histogram axis.
Definition TAxis.h:32
virtual void LabelsOption(Option_t *option="h")
Set option(s) to draw axis with labels option can be:
Definition TAxis.cxx:658
virtual void SetBinLabel(Int_t bin, const char *label)
Set label for bin.
Definition TAxis.cxx:882
TAxis()
Default constructor.
Definition TAxis.cxx:50
virtual void Set(Int_t nbins, Double_t xmin, Double_t xmax)
Initialize axis with fix bins.
Definition TAxis.cxx:790
virtual Int_t FindFixBin(Double_t x) const
Find bin number corresponding to abscissa x.
Definition TAxis.cxx:414
Using a TBrowser one can browse all ROOT objects.
Definition TBrowser.h:37
TBrowserImp * GetBrowserImp() const
Definition TBrowser.h:94
static TCanvas * MakeDefCanvas()
Static function to build a default canvas.
Definition TCanvas.cxx:1516
TClass instances represent classes, structs and namespaces in the ROOT type system.
Definition TClass.h:84
Bool_t InheritsFrom(const char *cl) const override
Return kTRUE if this class inherits from a class with name "classname".
Definition TClass.cxx:4971
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:3069
virtual Int_t GetEntries() const
static Int_t GetColorPalette(Int_t i)
Static function returning the color number i in current palette.
Definition TColor.cxx:1508
static Int_t GetNumberOfColors()
Static function returning number of colors in the color palette.
Definition TColor.cxx:1528
Describe directory structure in memory.
Definition TDirectory.h:45
static TClass * Class()
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
1-Dim function class
Definition TF1.h:233
A ROOT file is an on-disk file, usually with extension .root, that stores objects in a file-system-li...
Definition TFile.h:131
Create a dialog for fit function parameter settings.
System file browser, used as TRootBrowser plug-in.
A list tree is a widget that can contain a number of items arranged in a tree structure.
Definition TGListTree.h:195
ROOT GUI Window base class.
Definition TGWindow.h:23
The axis painter class.
Definition TGaxis.h:26
virtual void SetTitle(const char *title="")
Change the title of the axis.
Definition TGaxis.cxx:2942
virtual void ImportAxisAttributes(TAxis *axis)
Internal method to import TAxis attributes to this TGaxis.
Definition TGaxis.cxx:955
TGraph with asymmetric error bars.
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
Int_t GetN() const
Definition TGraph.h:132
void SetName(const char *name="") override
Set graph name.
Definition TGraph.cxx:2386
void Draw(Option_t *chopt="") override
Draw this graph with its current attributes.
Definition TGraph.cxx:833
virtual Double_t GetPointY(Int_t i) const
Get y value for point i.
Definition TGraph.cxx:1557
1-D histogram with a double per channel (see TH1 documentation)
Definition TH1.h:693
1-D histogram with a float per channel (see TH1 documentation)
Definition TH1.h:645
TH1 is the base class of all histogram classes in ROOT.
Definition TH1.h:59
virtual void SetDirectory(TDirectory *dir)
By default, when a histogram is created, it is added to the list of histogram objects in the current ...
Definition TH1.cxx:8939
void SetTitle(const char *title) override
Change/set the title.
Definition TH1.cxx:6716
virtual Int_t GetNbinsY() const
Definition TH1.h:314
static void AddDirectory(Bool_t add=kTRUE)
Sets the flag controlling the automatic add of histograms in memory.
Definition TH1.cxx:1263
@ kNoTitle
Don't draw the histogram title.
Definition TH1.h:180
TAxis * GetXaxis()
Definition TH1.h:340
TObject * FindObject(const char *name) const override
Search object named name in the list of functions.
Definition TH1.cxx:3834
virtual void SetMaximum(Double_t maximum=-1111)
Definition TH1.h:420
virtual Bool_t Add(TF1 *h1, Double_t c1=1, Option_t *option="")
Performs the operation: this = this + c1*f1 if errors are defined (see TH1::Sumw2),...
Definition TH1.cxx:813
TAxis * GetYaxis()
Definition TH1.h:341
void Draw(Option_t *option="") override
Draw this histogram with options.
Definition TH1.cxx:3037
virtual void SetMinimum(Double_t minimum=-1111)
Definition TH1.h:421
TList * GetListOfFunctions() const
Definition TH1.h:260
virtual void Scale(Double_t c1=1, Option_t *option="")
Multiply this histogram by a constant c1.
Definition TH1.cxx:6602
TObject * Clone(const char *newname="") const override
Make a complete copy of the underlying object.
Definition TH1.cxx:2723
static Bool_t AddDirectoryStatus()
Static function: cannot be inlined on Windows/NT.
Definition TH1.cxx:741
virtual void SetStats(Bool_t stats=kTRUE)
Set statistics option on/off.
Definition TH1.cxx:8992
2-D histogram with a double per channel (see TH1 documentation)
Definition TH2.h:356
Service class for 2-D histogram classes.
Definition TH2.h:30
Int_t Fill(Double_t) override
Invalid Fill method.
Definition TH2.cxx:357
The Histogram stack class.
Definition THStack.h:40
TList * GetHists() const
Definition THStack.h:72
virtual void Add(TH1 *h, Option_t *option="")
Add a new histogram to the list.
Definition THStack.cxx:365
Book space in a file, create I/O buffers, to fill them, (un)compress them.
Definition TKey.h:28
To draw Mathematical Formula.
Definition TLatex.h:18
Storage class for one entry of a TLegend.
This class displays a legend box (TPaveText) containing several legend entries.
Definition TLegend.h:23
Use the TLine constructor to create a simple line.
Definition TLine.h:22
virtual void SetY2(Double_t y2)
Definition TLine.h:68
virtual void SetX2(Double_t x2)
Definition TLine.h:66
Double_t GetY1() const
Definition TLine.h:52
virtual void SetX1(Double_t x1)
Definition TLine.h:65
virtual void SetY1(Double_t y1)
Definition TLine.h:67
Double_t GetY2() const
Definition TLine.h:53
A doubly linked list.
Definition TList.h:38
void Clear(Option_t *option="") override
Remove all objects from the list.
Definition TList.cxx:400
void Add(TObject *obj) override
Definition TList.h:81
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:563
virtual const char * GetName() const
Returns name of object.
Definition TObject.cxx:456
virtual TObject * Clone(const char *newname="") const
Make a clone of an object using the Streamer facility.
Definition TObject.cxx:241
virtual const char * ClassName() const
Returns name of class to which the object belongs.
Definition TObject.cxx:225
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
Definition TObject.cxx:991
virtual TObject * DrawClone(Option_t *option="") const
Draw a clone of this object in the current selected pad with: gROOT->SetSelectedPad(c1).
Definition TObject.cxx:317
virtual TObject * FindObject(const char *name) const
Must be redefined in derived classes.
Definition TObject.cxx:420
virtual void Delete(Option_t *option="")
Delete this object.
Definition TObject.cxx:266
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition TObject.cxx:798
virtual Bool_t InheritsFrom(const char *classname) const
Returns kTRUE if object inherits from class "classname".
Definition TObject.cxx:542
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:1005
virtual const char * GetTitle() const
Returns title of object.
Definition TObject.cxx:500
virtual void Draw(Option_t *option="")
Default Draw method for all objects.
Definition TObject.cxx:292
virtual void Print(Option_t *option="") const
This method must be overridden when a class wants to print itself.
Definition TObject.cxx:654
@ kCanDelete
if object in a list can be deleted
Definition TObject.h:62
The most important graphics class in the ROOT system.
Definition TPad.h:28
A Pave (see TPave) with text, lines or/and boxes inside.
Definition TPaveText.h:21
virtual TText * AddText(Double_t x1, Double_t y1, const char *label)
Add a new Text line to this pavetext at given coordinates.
virtual void SetY1NDC(Double_t y1)
Definition TPave.h:84
virtual void ConvertNDCtoPad()
Convert pave coordinates from NDC to Pad coordinates.
Definition TPave.cxx:139
virtual void SetBorderSize(Int_t bordersize=4)
Sets the border size of the TPave box and shadow.
Definition TPave.h:77
virtual void SetY2NDC(Double_t y2)
Definition TPave.h:85
Draw a Pie Chart,.
Definition TPie.h:23
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.
Sequenceable collection abstract base class.
Stopwatch class.
Definition TStopwatch.h:28
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.
Basic string class.
Definition TString.h:139
Ssiz_t Length() const
Definition TString.h:417
Bool_t IsDec() const
Returns true if all characters in string are decimal digits (0-9).
Definition TString.cxx:1940
Int_t Atoi() const
Return integer value of string.
Definition TString.cxx:1988
Bool_t EndsWith(const char *pat, ECaseCompare cmp=kExact) const
Return true if string ends with the specified string.
Definition TString.cxx:2244
Double_t Atof() const
Return floating-point value contained in string.
Definition TString.cxx:2054
Bool_t IsFloat() const
Returns kTRUE if string contains a floating point or integer number.
Definition TString.cxx:1858
const char * Data() const
Definition TString.h:376
Bool_t IsAlpha() const
Returns true if all characters in string are alphabetic.
Definition TString.cxx:1798
Bool_t BeginsWith(const char *s, ECaseCompare cmp=kExact) const
Definition TString.h:623
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:2378
Bool_t Contains(const char *pat, ECaseCompare cmp=kExact) const
Definition TString.h:632
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:390
Int_t GetOptTitle() const
Definition TStyle.h:248
Float_t GetLabelSize(Option_t *axis="X") const
Return label size.
Definition TStyle.cxx:1147
Float_t GetPadRightMargin() const
Definition TStyle.h:216
Style_t GetTitleFont(Option_t *axis="X") const
Return title font.
Definition TStyle.cxx:1218
Bool_t GetHistMinimumZero() const
Definition TStyle.h:239
Float_t GetPadLeftMargin() const
Definition TStyle.h:215
Float_t GetTitleYSize() const
Definition TStyle.h:281
static TClass * Class()
Double_t GetHistTopMargin() const
Definition TStyle.h:240
Float_t GetPadBottomMargin() const
Definition TStyle.h:213
const char * GetPaintTextFormat() const
Definition TStyle.h:252
Float_t GetPadTopMargin() const
Definition TStyle.h:214
virtual Bool_t ExpandPathName(TString &path)
Expand a pathname getting rid of special shell characters like ~.
Definition TSystem.cxx:1274
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:1296
virtual Bool_t ProcessEvents()
Process pending events (GUI, timers, sockets).
Definition TSystem.cxx:416
The TTimeStamp encapsulates seconds and ns since EPOCH.
Definition TTimeStamp.h:45
This class defines a UUID (Universally Unique IDentifier), also known as GUIDs (Globally Unique IDent...
Definition TUUID.h:42
const char * AsString() const
Return UUID as string. Copy string immediately since it will be reused.
Definition TUUID.cxx:571
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
double evaluate() const override
Evaluate projected p.d.f.
TObject * clone(const char *newname) const override
double expectedEvents(const RooArgSet *nset) const override
Return expected number of events to be used in calculation of extended likelihood.
ExtendMode extendMode() const override
Returns ability of PDF to provide extended likelihood terms.
pt SetTextColor(4)
RooCmdArg RecycleConflictNodes(bool flag=true)
RooConstVar & RooConst(double val)
RooCmdArg Embedded(bool flag=true)
RooCmdArg WeightVar(const char *name="weight", bool reinterpretAsWeight=false)
RooCmdArg GlobalObservables(Args_t &&... argsOrArgSet)
RooCmdArg ExternalConstraints(const RooArgSet &constraintPdfs)
RooCmdArg Range(const char *rangeName, bool adjustNorm=true)
const Double_t sigma
std::ostream & Info()
Definition hadd.cxx:171
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)
double gaussian_cdf(double x, double sigma=1, double x0=0)
Alternative name for same function.
bool EndsWith(const std::string &theString, const std::string &theSubstring)
TClass * GetClass(T *)
Definition TClass.h:672
The namespace RooFit contains mostly switches that change the behaviour of functions of PDFs (or othe...
Definition CodegenImpl.h:64
MsgLevel
Verbosity level for RooMsgService::StreamConfig in RooMsgService.
@ NumIntegration
constexpr Double_t C()
Velocity of light in .
Definition TMath.h:114
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:2193
Double_t StdDev(Long64_t n, const T *a, const Double_t *w=nullptr)
Definition TMath.h:531
#define BEGIN_XROOFIT_NAMESPACE
Definition Config.h:24
#define END_XROOFIT_NAMESPACE
Definition Config.h:25
static const char * what
Definition stlLoader.cc:5
bool isNaNWithPayload() const
Test if this struct has a float packed into its mantissa.
static float unpackNaN(double val)
If val is NaN and a this NaN has been tagged as containing a payload, unpack the float from the manti...
th1 Draw()
TLine lv
Definition textalign.C:5
TMarker m
Definition textangle.C:8
TLine l
Definition textangle.C:4
auto * tt
Definition textangle.C:16
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 GETACTBROWSER(TRootBrowser *b)
Definition xRooNode.cxx:111
auto & GETWSSNAPSHOTS(RooWorkspace *w)
Definition xRooNode.cxx:107
bool TopRightPlaceBox(TPad *p, TObject *o, double w, double h, double &xl, double &yb)
#define GETDMP(o, m)
Definition xRooNode.cxx:123
TPaveText * getPave(const char *name="labels", bool create=true, bool doPaint=false)
std::string formatLegendString(const std::string &s)
auto GETROOTDIR(TGFileBrowser *b)
Definition xRooNode.cxx:115
const xRooNode * runningNode
auto GETLISTTREE(TGFileBrowser *b)
Definition xRooNode.cxx:119
const T & _or_func(const T &a, const T &b)
Definition xRooNode.cxx:224
double new_getPropagatedError(const RooAbsReal &f, const RooFitResult &fr, const RooArgSet &nset={}, RooArgList **pars=nullptr, bool asymHi=false, bool asymLo=false)
TLegend * getLegend(bool create=true, bool doPaint=false)