Logo ROOT  
Reference Guide
 
Loading...
Searching...
No Matches
xRooNode.cxx
Go to the documentation of this file.
1/*
2 * Project: xRooFit
3 * Author:
4 * Will Buttinger, RAL 2022
5 *
6 * Copyright (c) 2022, CERN
7 *
8 * Redistribution and use in source and binary forms,
9 * with or without modification, are permitted according to the terms
10 * listed in LICENSE (http://roofit.sourceforge.net/license.txt)
11 */
12
13/** \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#if ROOT_VERSION_CODE > ROOT_VERSION(6, 37, 00)
185#include "RooMultiReal.h"
186#include "RooMultiPdf.h"
187#endif
188
189#include "RooPoisson.h"
190#include "RooGaussian.h"
191#include "RooFormulaVar.h"
192#include "RooGenericPdf.h"
193#include "TVectorD.h"
194#include "TStopwatch.h"
195#include "TTimeStamp.h"
196
197#include <csignal>
198
199#include "TCanvas.h"
200#include "THStack.h"
201
202#include "TLegend.h"
203#include "TLegendEntry.h"
204#include "TGraphErrors.h"
205#include "TMultiGraph.h"
206#include "TFrame.h"
207#include "RooProjectedPdf.h"
208#include "TMemFile.h"
209#include "TGaxis.h"
210#include "TPie.h"
211// #include <thread>
212// #include <future>
213
214#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
215#include "RooNaNPacker.h"
216#endif
217
219
220xRooNode::InteractiveObject *xRooNode::gIntObj = nullptr;
221std::map<std::string, std::tuple<std::function<double(double, double, double)>, bool>> xRooNode::auxFunctions;
222void xRooNode::SetAuxFunction(const char *title, const std::function<double(double, double, double)> &func,
223 bool symmetrize)
224{
225 auxFunctions[title] = std::make_tuple(func, symmetrize);
226}
227
228template <typename T>
229const T &_or_func(const T &a, const T &b)
230{
231 if (a)
232 return a;
233 return b;
234}
235
236////////////////////////////////////////////////////////////////////////////////
237/// Create new object of type classname, with given name and title, and own-wrap it
238/// i.e. the xRooNode will delete the object when the node (and any that reference it) is destroyed
239///
240/// \param classname : the type of the object to create
241/// \param name : the name to give the object
242/// \param title : the title to give the object
243
244xRooNode::xRooNode(const char *classname, const char *name, const char *title)
245 : xRooNode(name, std::shared_ptr<TObject>(TClass::GetClass(classname)
246 ? reinterpret_cast<TObject *>(TClass::GetClass(classname)->New())
247 : nullptr,
248 [](TObject *o) {
249 if (auto w = dynamic_cast<RooWorkspace *>(o); w) {
250#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
251 w->_embeddedDataList.Delete();
252#endif
253 xRooNode(*w, std::make_shared<xRooNode>()).sterilize();
254 }
255 if (o)
256 delete o;
257 }))
258{
259 if (auto a = get<TNamed>(); a)
260 a->SetName(name);
261 SetTitle(title);
262}
263
264xRooNode::xRooNode(const char *name, const std::shared_ptr<TObject> &comp, const std::shared_ptr<xRooNode> &parent)
265 : TNamed(name, ""), fComp(comp), fParent(parent)
266{
267
268 if (!fComp && !fParent && name && strlen(name) > 0) {
271 delete[] _path;
273 // if file is json can try to read
274 if (pathName.EndsWith(".json")) {
275#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
276 fComp = std::make_shared<RooWorkspace>("workspace", name);
279 RooMsgService::instance().setGlobalKillBelow(RooFit::WARNING);
280 if (!tool.importJSON(pathName.Data())) {
281 Error("xRooNode", "Error reading json workspace %s", name);
282 fComp.reset();
283 }
284 RooMsgService::instance().setGlobalKillBelow(msglevel);
285#else
286 Error("xRooNode", "json format workspaces available only in ROOT 6.26 onwards");
287#endif
288 } else {
289
290 // using acquire in the constructor seems to cause a mem leak according to valgrind ... possibly because
291 // (*this) gets called on it before the node is fully constructed
292 auto _file = std::make_shared<TFile>(
293 pathName); // acquire<TFile>(name); // acquire file to ensure stays open while we have the workspace
294 // actually it appears we don't need to keep the file open once we've loaded the workspace, but should be
295 // no harm doing so
296 // otherwise the workspace doesn't saveas
297 auto keys = _file->GetListOfKeys();
298 if (keys) {
299 for (auto &&k : *keys) {
300 auto cl = TClass::GetClass((static_cast<TKey *>(k))->GetClassName());
301 if (cl == RooWorkspace::Class() || cl->InheritsFrom("RooWorkspace")) {
302 fComp.reset(_file->Get<RooWorkspace>(k->GetName()), [](TObject *ws) {
303 // memory leak in workspace, some RooLinkedLists aren't cleared, fixed in ROOT 6.28
304 if (ws) {
305#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
306 dynamic_cast<RooWorkspace *>(ws)->_embeddedDataList.Delete();
307#endif
308 xRooNode(*ws, std::make_shared<xRooNode>()).sterilize();
309 delete ws;
310 }
311 });
312 if (fComp) {
313 TNamed::SetNameTitle(fComp->GetName(), fComp->GetTitle());
314 fParent = std::make_shared<xRooNode>(
315 _file); // keep file alive - seems necessary to save workspace again in some cases
316 break;
317 }
318 }
319 }
320 }
321 }
322 } else if (pathName.EndsWith(".root") || pathName.EndsWith(".json")) {
323 throw std::runtime_error(TString::Format("%s does not exist", name));
324 }
325 }
326
327 if (auto _ws = get<RooWorkspace>(); _ws && (!parent || parent->get<TFile>())) {
329 .getStream(RooFit::INFO)
330 .removeTopic(
331#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 37, 00)
333#else
335#endif
336 ); // stop info message every time
337
338 // check if any of the open files have version numbers greater than our major version
339 // may not read correctly
340 for (auto f : *gROOT->GetListOfFiles()) {
341 if ((dynamic_cast<TFile *>(f)->GetVersion() / 100) > (gROOT->GetVersionInt() / 100)) {
342 Warning("xRooNode", "There is file open with version %d > current version %d ... results may be wrong",
343 dynamic_cast<TFile *>(f)->GetVersion(), gROOT->GetVersionInt());
344 }
345 }
346
347 // load list of colors if there is one
348 if (auto colors = dynamic_cast<TSeqCollection *>(_ws->obj(gROOT->GetListOfColors()->GetName()))) {
350 // gROOT->GetListOfColors()->Clear(); - was getting warnings about colors already defined when overwriting
351 // existing list
352 for (auto col : *colors) {
353 if (!gROOT->GetListOfColors()->FindObject(col->GetName())) {
354 gROOT->GetListOfColors()->Add(gROOT->GetListOfColors()->IsOwner() ? col->Clone() : col);
355 }
356
357 // gROOT->GetListOfColors()->Add(gROOT->GetListOfColors()->IsOwner() ? col->Clone() : col); // in 6.36 root,
358 // colors list became owning
359 }
360 }
361
362 // use the datasets if any to 'mark' observables
363 int checkCount = 0;
364 for (auto &d : _ws->allData()) {
365 for (auto &a : *d->get()) {
366 if (auto v = _ws->var(a->GetName()); v) {
367 v->setAttribute("obs");
368 } else if (auto c = _ws->cat(a->GetName()); c) {
369 c->setAttribute("obs");
370 }
371 }
372 // count how many ds are checked ... if none are checked will check the first
373 checkCount += d->TestBit(1 << 20);
374 }
375
376 if (checkCount == 0 && !_ws->allData().empty())
377 _ws->allData().back()->SetBit(1 << 20, true);
378
379 if (auto _set = dynamic_cast<RooArgSet *>(GETWSSNAPSHOTS(_ws).find("NominalParamValues")); _set) {
380 for (auto s : *_set) {
381 if (auto v = dynamic_cast<RooRealVar *>(s); v) {
382 _ws->var(s->GetName())->setStringAttribute("nominal", TString::Format("%f", v->getVal()));
383 }
384 }
385 }
386
387 // also flag global observables ... relies on ModelConfig existences
389 for (auto &[k, v] : GETWSSETS(_ws)) {
390 if (k == "globalObservables" || TString(k).EndsWith("_GlobalObservables")) {
391 for (auto &s : v) {
392 _allGlobs.add(*s);
393 s->setAttribute("obs");
394 s->setAttribute("global");
395 }
396 } else if (TString(k).EndsWith("_Observables")) {
397 const_cast<RooArgSet &>(v).setAttribAll("obs");
398 } else if (TString(k).EndsWith("_POI")) {
399 for (auto &s : v) {
400 s->setAttribute("poi");
401 auto _v = dynamic_cast<RooRealVar *>(s);
402 if (!_v)
403 continue;
404 // if (!_v->hasRange("physical")) {
405 // _v->setRange("physical", 0, std::numeric_limits<double>::infinity());
406 // // ensure range of poi is also straddling 0
407 // if (_v->getMin() >= 0)
408 // _v->setMin(-1e-5);
409 // }
410 }
411 } else if (TString(k).EndsWith("_NuisParams")) {
412 const_cast<RooArgSet &>(v).setAttribAll("np");
413 }
414 }
415 if (!_allGlobs.empty() && GETWSSETS(_ws).count("globalObservables") == 0) {
416 _ws->defineSet("globalObservables", _allGlobs);
417 }
418
419 // now check if any pars don't have errors defined (not same as error=0) ... if so, use the first pdf (if there is
420 // one) to try setting values from
421 if (!_ws->allPdfs().empty()) {
422 std::set<RooRealVar *> noErrorPars;
423 std::string parNames;
424 for (auto &p : np()) { // infer errors on all floating non-poi parameters
425 auto v = p->get<RooRealVar>();
426 if (!v)
427 continue;
428 if (!v->hasError()) {
429 noErrorPars.insert(v);
430 if (!parNames.empty())
431 parNames += ",";
432 parNames += v->GetName();
433 }
434 }
435 if (!noErrorPars.empty()) {
436 Warning("xRooNode",
437 "Inferring initial errors of %d parameters (%s%s) (give all nuisance parameters an error to avoid "
438 "this msg)",
439 int(noErrorPars.size()), (*noErrorPars.begin())->GetName(), (noErrorPars.size() > 1) ? ",..." : "");
440 // get the first top-level pdf
441 browse();
442 for (auto &a : *this) {
443 if (noErrorPars.empty()) {
444 break;
445 }
446 if (a->fFolder == "!pdfs") {
447 try {
448 auto fr = a->floats().reduced(parNames).fitResult("prefit");
449 if (auto _fr = fr.get<RooFitResult>(); _fr) {
450 std::set<RooRealVar *> foundPars;
451 for (auto &v : noErrorPars) {
452 if (auto arg = dynamic_cast<RooRealVar *>(_fr->floatParsFinal().find(v->GetName()));
453 arg && arg->hasError()) {
454 v->setError(arg->getError());
455 foundPars.insert(v);
456 }
457 }
458 for (auto &v : foundPars) {
459 noErrorPars.erase(v);
460 }
461 }
462 } catch (...) {
463 }
464 }
465 }
466 }
467 }
468 }
469
470 if (strlen(GetTitle()) == 0) {
471 if (fComp) {
472 TNamed::SetTitle(fComp->GetTitle());
473 } else {
474 TNamed::SetTitle(GetName());
475 }
476 }
477}
478
479xRooNode::xRooNode(const TObject &comp, const std::shared_ptr<xRooNode> &parent)
480 : xRooNode(/*[](const TObject& c) {
481c.InheritsFrom("RooAbsArg");
482if (s) {
483return (s->getStringAttribute("alias")) ? s->getStringAttribute("alias") : c.GetName();
484}
485return c.GetName();
486}(comp)*/
487 (comp.InheritsFrom("RooAbsArg") && dynamic_cast<const RooAbsArg *>(&comp)->getStringAttribute("alias"))
488 ? dynamic_cast<const RooAbsArg *>(&comp)->getStringAttribute("alias")
489 : comp.GetName(),
490 std::shared_ptr<TObject>(const_cast<TObject *>(&comp), [](TObject *) {}), parent)
491{
492}
493
494xRooNode::xRooNode(const std::shared_ptr<TObject> &comp, const std::shared_ptr<xRooNode> &parent)
495 : xRooNode(
496 [&]() {
497 if (auto a = std::dynamic_pointer_cast<RooAbsArg>(comp); a && a->getStringAttribute("alias"))
498 return a->getStringAttribute("alias");
499 if (comp)
500 return comp->GetName();
501 return "";
502 }(),
503 comp, parent)
504{
505}
506
508
509void xRooNode::_SetAttribute_(const char *name, const char *value)
510{
511 TString v(value);
512 v.ToUpper();
513 bool isBool = (v == "TRUE" || v == "FALSE");
514 if (auto a = get<RooAbsArg>(); a) {
515 if (value == nullptr || v == "NULLPTR") {
516 if (a->getAttribute(name))
517 a->setAttribute(name, false);
518 else if (a->getStringAttribute(name))
519 a->setStringAttribute(name, nullptr);
520 } else {
521 if (isBool)
522 a->setAttribute(name, (v == "TRUE"));
523 else
524 a->setStringAttribute(name, value);
525 }
526 } else {
527 RooArgList l = argList();
528 for (auto a2 : l) {
529 xRooNode(*a2)._SetAttribute_(name, value);
530 }
531 }
532 // should update this node's state in any browsers ...
533 for (auto a : *gROOT->GetListOfBrowsers()) {
534 TBrowser *b = dynamic_cast<TBrowser *>(a);
535 if (b && GetTreeItem(b)) {
536 if (auto bi = dynamic_cast<TRootBrowser *>(b->GetBrowserImp())) {
537 if (auto fb = dynamic_cast<TGFileBrowser *>(bi->GetActBrowser())) {
538 fb->DoubleClicked(GetTreeItem(b), 0);
539 }
540 }
541 }
542 }
543}
544
545void xRooNode::Checked(TObject *obj, bool val)
546{
547 if (obj != this)
548 return;
549
550 // cycle through states:
551 // unhidden and selected: tick, no uline
552 // hidden and unselected: notick, uline
553 // unhidden and unselected: tick, uline
554 if (auto o = get<RooAbsReal>(); o) {
555 if (o->isSelectedComp() && !val) {
556 // deselecting and hiding
557 o->selectComp(val);
558 o->setAttribute("hidden");
559 } else if (!o->isSelectedComp() && !val) {
560 // selecting
561 o->selectComp(!val);
562 } else if (val) {
563 // unhiding but keeping unselected
564 o->setAttribute("hidden", false);
565 }
566 auto item = GetTreeItem(nullptr);
567 item->CheckItem(!o->getAttribute("hidden"));
568 if (o->isSelectedComp()) {
569 item->ClearColor();
570 } else {
571 item->SetColor(kGray);
572 }
573 return;
574 }
575
576 if (auto o = get(); o) {
577 // if (o->TestBit(1<<20)==val) return; // do nothing
578 o->SetBit(1 << 20, val); // TODO: check is 20th bit ok to play with?
579 if (auto fr = get<RooFitResult>(); fr) {
580 if (auto _ws = ws(); _ws) {
581 if (val) {
582 // ensure fit result is in genericObjects list or snapshots ... if not, add a copy ...
583 if (fr->numStatusHistory() && !_ws->genobj(fr->GetName())) {
584 _ws->import(*fr);
585 if (auto wfr = dynamic_cast<RooFitResult *>(_ws->genobj(fr->GetName()))) {
586 fr = wfr;
587 }
588 }
589 RooArgSet _allVars = _ws->allVars();
590 _allVars = fr->floatParsFinal();
591 _allVars = fr->constPars();
592 for (auto &i : fr->floatParsInit()) {
593 auto v = dynamic_cast<RooRealVar *>(_allVars.find(i->GetName()));
594 if (v)
595 v->setStringAttribute("initVal", TString::Format("%f", dynamic_cast<RooRealVar *>(i)->getVal()));
596 }
597 // uncheck all other fit results
598 for (auto oo : _ws->allGenericObjects()) {
599 if (auto ffr = dynamic_cast<RooFitResult *>(oo); ffr && ffr != fr) {
600 ffr->ResetBit(1 << 20);
601 }
602 }
603 } else
604 _ws->allVars() = fr->floatParsInit();
605 }
606
607 TBrowser *b = nullptr;
608 for (auto a : *gROOT->GetListOfBrowsers()) {
609 b = dynamic_cast<TBrowser *>(a);
610 if (b && GetTreeItem(b)) {
611 break;
612 }
613 }
614 if (b) {
615 auto p = GetTreeItem(b);
616
617 if (p) {
618 // update check marks on siblings
619 if (auto first = p->GetParent()->GetFirstChild()) {
620 do {
621 if (first->HasCheckBox()) {
622 auto _obj = static_cast<xRooNode *>(first->GetUserData());
623 first->CheckItem(_obj->get() && _obj->get()->TestBit(1 << 20));
624 }
625 } while ((first = first->GetNextSibling()));
626 }
627 }
628
629 // also since const status of pars could have changed, refresh all 'poi' and 'np' open nodes
630 if (auto bi = dynamic_cast<TRootBrowser *>(b->GetBrowserImp())) {
631 if (auto fb = dynamic_cast<TGFileBrowser *>(bi->GetActBrowser())) {
632 while (p) {
633 if (TString(p->GetText()).BeginsWith("RooWorkspace::")) {
634 std::function<void(TGListTreeItem *)> rfunc;
635
636 rfunc = [&](TGListTreeItem *i) {
637 if (auto first = i->GetFirstChild()) {
638 do {
639 if (first->IsOpen() &&
640 (TString(first->GetText()) == "poi" || TString(first->GetText()) == "np")) {
641 fb->DoubleClicked(first, 0);
642 } else
643 rfunc(first);
644 } while ((first = first->GetNextSibling()));
645 }
646 };
647 rfunc(p);
648 break;
649 } else {
650 p = p->GetParent();
651 }
652 }
653 }
654 }
655 }
656 }
657 }
658}
659
661{
662 static bool blockBrowse = false;
663 if (blockBrowse)
664 return;
665 if (b == nullptr) {
666 auto b2 = dynamic_cast<TBrowser *>(gROOT->GetListOfBrowsers()->Last());
667 if (!b2 || !b2->GetBrowserImp()) { // no browser imp if browser was closed
668 blockBrowse = true;
669 gEnv->SetValue("X11.UseXft", "no"); // for faster x11
670 gEnv->SetValue("X11.Sync", "no");
671 gEnv->SetValue("X11.FindBestVisual", "no");
672 gEnv->SetValue("Browser.Name", "TRootBrowser"); // forces classic root browser (in 6.26 onwards)
673 gEnv->SetValue("Canvas.Name", "TRootCanvas");
674 b2 = new TBrowser("nodeBrowser", this, "RooFit Browser");
675 blockBrowse = false;
676 } else if (strcmp(b2->GetName(), "nodeBrowser") == 0) {
677 blockBrowse = true;
678 b2->BrowseObject(this);
679 blockBrowse = false;
680 } else {
681 auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b2->GetBrowserImp())));
682 if (_b)
683 _b->AddFSDirectory("Workspaces", nullptr, "SetRootDir");
684 /*auto l = Node2::Class()->GetMenuList();
685 auto o = new CustomClassMenuItem(TClassMenuItem::kPopupUserFunction,Node2::Class(),
686 "blah blah blah","BlahBlah",0,"Option_t*",-1,true);
687 //o->SetCall(o,"BlahBlah","Option_t*",-1);
688 l->AddFirst(o);*/
689 // b->BrowseObject(this);
690 _b->GotoDir(nullptr);
691 _b->Add(this, GetName());
692 // b->Add(this);
693 }
694 return;
695 }
696
697 if (auto item = GetTreeItem(b); item) {
698 if (!item->IsOpen() && IsFolder())
699 return; // no need to rebrowse if closing
700 // update check marks on any child items
701 if (auto first = item->GetFirstChild()) {
702 do {
703 if (first->HasCheckBox()) {
704 auto _obj = static_cast<xRooNode *>(first->GetUserData());
705 first->CheckItem(_obj->get() &&
706 (_obj->get()->TestBit(1 << 20) ||
707 (_obj->get<RooAbsArg>() && !_obj->get<RooAbsArg>()->getAttribute("hidden"))));
708 }
709 } while ((first = first->GetNextSibling()));
710 }
711 }
712
713 browse();
714
715 // for top-level pdfs default to having the .vars browsable too
716 if (get<RooAbsPdf>() && fFolder == "!pdfs" && !_IsShowVars_()) {
717 fBrowsables.push_back(std::make_shared<xRooNode>(vars()));
718 }
719
720 if (auto _fr = get<RooFitResult>(); _fr && fBrowsables.empty()) {
721 // have some common drawing options
722 fBrowsables.push_back(std::make_shared<xRooNode>(".Draw(\"pull\")", nullptr, *this));
723 fBrowsables.push_back(std::make_shared<xRooNode>(".Draw(\"corr10colztext\")", nullptr, *this));
724 if (std::unique_ptr<RooAbsCollection>(_fr->floatParsFinal().selectByAttrib("poi", true))->size() == 1) {
725 fBrowsables.push_back(std::make_shared<xRooNode>(".Draw(\"impact\")", nullptr, *this));
726 }
727 }
728
729 if (empty() && fBrowsables.empty()) {
730 try {
731 if (auto s = get<TStyle>()) {
732 s->SetFillAttributes();
733 if (auto ed = dynamic_cast<TGedEditor *>(TVirtualPadEditor::GetPadEditor())) {
734 ed->SetModel(gPad, s, kButton1Down, true);
735 }
736 } else if (TString(GetName()).BeginsWith(".Draw(\"") && fParent) {
737 fParent->Draw(TString(TString(GetName())(7, strlen(GetName()) - 9)) + b->GetDrawOption());
738 } else {
739 Draw(b->GetDrawOption());
740 }
741 } catch (const std::exception &e) {
742 new TGMsgBox(
743 gClient->GetRoot(),
744 (gROOT->GetListOfBrowsers()->At(0))
745 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
746 : gClient->GetRoot(),
747 "Exception", e.what(),
748 kMBIconExclamation); // deletes self on dismiss?
749 }
750 }
751
752 bool hasFolders = false;
753 if (strlen(GetName()) > 0 && GetName()[0] != '!') { // folders don't have folders
754 for (auto &c : *this) {
755 if (!c->fFolder.empty()) {
756 hasFolders = true;
757 break;
758 }
759 }
760 }
761 // auto _ws = get<RooWorkspace>();
762 if (/*_ws*/ hasFolders) {
763 // organize in folders
764 auto _folders = find(".folders");
765 if (!_folders) {
766 _folders = emplace_back(std::make_shared<xRooNode>(".folders", nullptr, *this));
767 }
768 // ensure entry in folders for every folder type ...
769 for (auto &v : *this) {
770 if (!v->fFolder.empty() && !_folders->find(v->fFolder, false)) {
771 _folders->emplace_back(std::make_shared<xRooNode>(v->fFolder.c_str(), nullptr, *this));
772 }
773 }
774 // now just add all the folders
775 for (auto &v : *_folders) {
776 TString _name = v->GetName();
777 if (_name.BeginsWith('!'))
778 _name = _name(1, _name.Length()); // strip ! from display
779 b->Add(v.get(), _name);
780 }
781 }
782
783 for (auto &v : *this) {
784 if (hasFolders && !v->fFolder.empty())
785 continue; // in the folders
786 if (strcmp(v->GetName(), ".folders") == 0)
787 continue; // never 'browse' the folders property
788 auto _fr = v->get<RooFitResult>();
789 int _checked = (v->get<RooAbsData>() || _fr) ? v->get()->TestBit(1 << 20) : -1;
790 if (_fr && ((_fr->status() == 0 && _fr->numStatusHistory() == 0) || (_fr->floatParsFinal().empty()))) {
791 // this is a "PARTIAL" fit result ... don't allow it to be selected
792 _checked = -1;
793 }
794 if (v->get<RooAbsPdf>() && get<RooSimultaneous>())
795 _checked = !v->get<RooAbsArg>()->getAttribute("hidden");
796 TString _name = v->GetName();
797 if (v->get() && _name.BeginsWith(TString(v->get()->ClassName()) + "::")) {
798 _name = _name(strlen(v->get()->ClassName()) + 2, _name.Length());
799 }
800 if (_name.BeginsWith(".")) {
801 // property node -- display the name of the contained object
802 if (v->get()) {
803 _name = TString::Format("%s: %s::%s", _name.Data(), v->get()->ClassName(),
804 (v->get<RooAbsArg>() && v->get<RooAbsArg>()->getStringAttribute("alias"))
805 ? v->get<RooAbsArg>()->getStringAttribute("alias")
806 : v->get()->GetName());
807 }
808 } else if (v->get() && !v->get<TFile>() && !TString(v->GetName()).BeginsWith('/'))
809 _name = TString::Format("%s::%s", v->get()->ClassName(), _name.Data());
810 if (auto _type = v->GetNodeType(); strlen(_type)) {
811 // decided not to show const values until figure out how to update if value changes
812 /*if (TString(_type)=="Const") _name += TString::Format(" [%s=%g]",_type,v->get<RooConstVar>()->getVal());
813 else*/
814 _name += TString::Format(" [%s]", _type);
815 }
816 if (auto fv = v->get<RooFormulaVar>()) {
817 TString formu = TString::Format(" [%s]", fv->expression());
818 for (size_t i = 0; i < fv->dependents().size(); i++) {
819 formu.ReplaceAll(TString::Format("x[%zu]", i), fv->dependents()[i].GetName());
820 }
821 _name += formu;
822 } else if (auto gv = v->get<RooGenericPdf>()) {
823 TString formu = TString::Format(" [%s]", gv->expression());
824 for (size_t i = 0; i < gv->dependents().size(); i++) {
825 formu.ReplaceAll(TString::Format("x[%zu]", i), gv->dependents()[i].GetName());
826 }
827 _name += formu;
828 }
829 // tool tip defaults to displaying name and title, so temporarily set name to obj name if has one
830 // and set title to the object type
831 TString nameSave(v->TNamed::GetName());
832 TString titleSave(v->TNamed::GetTitle());
833 if (auto o = v->get(); o)
834 v->TNamed::SetNameTitle(o->GetName(), o->ClassName());
835 b->Add(v.get(), _name, _checked);
836 if (auto o = v->get(); o)
837 v->TNamed::SetNameTitle(nameSave, titleSave);
838 if (_checked != -1) {
839 dynamic_cast<TQObject *>(b->GetBrowserImp())
840 ->Connect("Checked(TObject *, bool)", ClassName(), v.get(), "Checked(TObject *, bool)");
841 }
842 if (_fr) {
843 if (_fr->status() || _fr->covQual() != 3) { // snapshots or bad fits
844 v->GetTreeItem(b)->SetColor((_fr->numStatusHistory() && !_fr->floatParsFinal().empty()) ? kRed : kBlue);
845 } else if (_fr->numStatusHistory() == 0) { // partial fit result ..
846 v->GetTreeItem(b)->SetColor(kGray);
847 }
848 }
849 if ((v->fFolder == "!np" || v->fFolder == "!poi")) {
850 if (v->get<RooAbsArg>()->getAttribute("Constant")) {
851 v->GetTreeItem(b)->SetColor(kGray);
852 } else
853 v->GetTreeItem(b)->ClearColor();
854 }
855 if (auto _htr = v->get<RooStats::HypoTestResult>(); _htr) {
856 // check for fit statuses
857 if (auto fits = _htr->GetFitInfo()) {
858 for (int i = 0; i < fits->numEntries(); i++) {
859 // if any fit (other than a genFit) is bad, flag point as bad
860 if (fits->get(i)->getCatIndex("type") != 5 && fits->get(i)->getRealValue("status") != 0) {
861 v->GetTreeItem(b)->SetColor(kRed);
862 break;
863 }
864 }
865 } else {
866 v->GetTreeItem(b)->SetColor(kBlue); // unknown fit status
867 }
868 }
869
870 // v.fBrowsers.insert(b);
871 }
872
873 // for pdfs, check for datasets too and add to list
874 /*if (get<RooAbsPdf>()) {
875 auto dsets = datasets();
876 if (!dsets.empty()) {
877 // check if already have .datasets() in browsables
878 bool found(false);
879 for(auto& p : fBrowsables) {
880 if (TString(p->GetName())==".datasets()") {found=true;
881 // add
882 break;
883 }
884 }
885 if (!found) {
886 fBrowsables.push_back(std::make_shared<xRooNode>(dsets));
887 }
888 }
889 }*/
890 // browse the browsables too
891 for (auto &v : fBrowsables) {
892 TString _name = v->GetName();
893 if (_name == ".memory")
894 continue; // hide the memory from browsing, if put in browsables
895 TString nameSave(v->TNamed::GetName());
896 TString titleSave(v->TNamed::GetTitle());
897 if (auto o = v->get(); o)
898 v->TNamed::SetNameTitle(o->GetName(), o->ClassName());
899 b->Add(v.get(), _name, -1);
900 if (auto o = v->get(); o)
901 v->TNamed::SetNameTitle(nameSave, titleSave);
902 }
903
904 b->SetSelected(this);
905}
906
908{
909 if (!set) {
910 // can't remove as causes a crash, need to remove from the browser first
911 /*for(auto itr = fBrowsables.begin(); itr != fBrowsables.end(); ++itr) {
912 if (strcmp((*itr)->GetName(),".vars")==0) {
913 fBrowsables.erase(itr);
914 }
915 }*/
916 } else {
917 auto v = std::make_shared<xRooNode>(vars());
918 fBrowsables.push_back(v);
919 if (auto l = GetListTree(nullptr)) {
920 l->AddItem(GetTreeItem(nullptr), v->GetName(), v.get());
921 }
922 }
923}
924
926{
927 for (auto &b : fBrowsables) {
928 if (strcmp(b->GetName(), ".vars") == 0)
929 return true;
930 }
931 return false;
932}
933
935{
936 if (strlen(GetName()) > 0 && GetName()[0] == '!')
937 return true;
938 if (strlen(GetName()) > 0 && GetName()[0] == '.' && !(TString(GetName()).BeginsWith(".Draw(\"")))
939 return true;
940 if (empty())
941 const_cast<xRooNode *>(this)->browse();
942 return !empty();
943}
944
945class Axis2 : public TAxis {
946
947public:
948 using TAxis::TAxis;
949 double GetBinWidth(Int_t bin) const override
950 {
951 if (auto v = var(); v)
952 return v->getBinWidth(bin - 1, GetName());
953 return 1;
954 }
955 double GetBinLowEdge(Int_t bin) const override
956 {
957 if (auto v = rvar(); v) {
958 return (bin == v->getBinning(GetName()).numBins() + 1) ? v->getBinning(GetName()).binHigh(bin - 2)
959 : v->getBinning(GetName()).binLow(bin - 1);
960 }
961 return bin - 1;
962 }
963 double GetBinUpEdge(Int_t bin) const override
964 {
965 if (auto v = rvar(); v)
966 return (bin == 0) ? v->getBinning(GetName()).binLow(bin) : v->getBinning(GetName()).binHigh(bin - 1);
967 return bin;
968 }
969
970 const char *GetTitle() const override
971 {
972 return (binning() && strlen(binning()->GetTitle())) ? binning()->GetTitle() : GetParent()->GetTitle();
973 }
974 void SetTitle(const char *title) override
975 {
976 if (binning()) {
977 const_cast<RooAbsBinning *>(binning())->SetTitle(title);
978 } else {
979 dynamic_cast<TNamed *>(GetParent())->SetTitle(title);
980 }
981 }
982
983 void Set(Int_t nbins, const double *xbins) override
984 {
985 if (auto v = dynamic_cast<RooRealVar *>(rvar()))
986 v->setBinning(RooBinning(nbins, xbins), GetName());
987 TAxis::Set(nbins, xbins);
988 }
989 void Set(Int_t nbins, const float *xbins) override
990 {
991 std::vector<double> bins(nbins + 1);
992 for (int i = 0; i <= nbins; i++)
993 bins.at(i) = xbins[i];
994 return Set(nbins, &bins[0]);
995 }
996 void Set(Int_t nbins, double xmin, double xmax) override
997 {
998 if (auto v = dynamic_cast<RooRealVar *>(rvar()))
999 v->setBinning(RooUniformBinning(xmin, xmax, nbins), GetName());
1000 TAxis::Set(nbins, xmin, xmax);
1001 }
1002
1003 const RooAbsBinning *binning() const { return var()->getBinningPtr(GetName()); }
1004
1005 Int_t FindFixBin(const char *label) const override { return TAxis::FindFixBin(label); }
1006 Int_t FindFixBin(double x) const override { return (binning()) ? (binning()->binNumber(x) + 1) : x; }
1007
1008private:
1009 RooAbsLValue *var() const { return dynamic_cast<RooAbsLValue *>(GetParent()); }
1010 RooAbsRealLValue *rvar() const { return dynamic_cast<RooAbsRealLValue *>(GetParent()); }
1011};
1012
1013std::shared_ptr<TObject> xRooNode::getObject(const std::string &name, const std::string &type) const
1014{
1015 // if (fParent) return fParent->getObject(name);
1016
1017 if (auto _owned = find(".memory"); _owned) {
1018 for (auto &o : *_owned) {
1019 if (name == o->GetName()) {
1020 if (type.empty() || o->get()->InheritsFrom(type.c_str()))
1021 return o->fComp;
1022 }
1023 }
1024 }
1025
1026 // see if have a provider
1027 auto _provider = fProvider;
1028 auto _parent = fParent;
1029 while (!_provider && _parent) {
1030 _provider = _parent->fProvider;
1031 _parent = _parent->fParent;
1032 }
1033 if (_provider)
1034 return _provider->getObject(name, type);
1035
1036 if (ws()) {
1037 std::shared_ptr<TObject> out;
1038 if (auto arg = ws()->arg(name.c_str()); arg) {
1039 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
1040 if (!type.empty() && arg->InheritsFrom(type.c_str()))
1041 return _tmp;
1042 if (!out)
1043 out = _tmp;
1044 }
1045 if (auto arg = ws()->data(name.c_str()); arg) {
1046 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
1047 if (!type.empty() && arg->InheritsFrom(type.c_str()))
1048 return _tmp;
1049 if (!out)
1050 out = _tmp;
1051 }
1052 if (auto arg = ws()->genobj(name.c_str()); arg) {
1053 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
1054 if (!type.empty() && arg->InheritsFrom(type.c_str()))
1055 return _tmp;
1056 if (!out)
1057 out = _tmp;
1058 }
1059 if (auto arg = ws()->embeddedData(name.c_str()); arg) {
1060 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
1061 if (!type.empty() && arg->InheritsFrom(type.c_str()))
1062 return _tmp;
1063 if (!out)
1064 out = _tmp;
1065 }
1066 if (auto arg = GETWSSNAPSHOTS(ws()).find(name.c_str()); arg) {
1067 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
1068 if (!type.empty() && arg->InheritsFrom(type.c_str()))
1069 return _tmp;
1070 if (!out)
1071 out = _tmp;
1072 }
1073 return out;
1074 }
1075 if (auto arg = get<RooAbsArg>()) {
1076 // can try all nodes
1077 RooArgSet nodes;
1078 arg->treeNodeServerList(&nodes);
1079 if (auto server = nodes.find(name.c_str())) {
1080 return std::shared_ptr<TObject>(server, [](TObject *) {});
1081 }
1082 }
1083 return nullptr;
1084}
1085
1087{
1088 if (fXAxis) {
1089 // check if num bins needs update or not
1090 if (auto cat = dynamic_cast<RooAbsCategory *>(fXAxis->GetParent());
1091 cat && cat->numTypes() != fXAxis->GetNbins()) {
1092 fXAxis.reset();
1093 } else {
1094 return fXAxis.get();
1095 }
1096 }
1097 RooAbsLValue *x = nullptr;
1098 if (auto a = get<RooAbsArg>(); a && a->isFundamental())
1099 x = dynamic_cast<RooAbsLValue *>(a); // self-axis
1100
1101 auto _parentX = (!x && fParent && !fParent->get<RooSimultaneous>()) ? fParent->GetXaxis() : nullptr;
1102
1103 auto o = get<RooAbsReal>();
1104 if (!o)
1105 return _parentX;
1106
1107 if (auto xName = o->getStringAttribute("xvar"); xName) {
1108 x = dynamic_cast<RooAbsLValue *>(getObject(xName).get());
1109 }
1110
1111 // if xvar has become set equal to an arg and this is a pdf, we will allow a do-over
1112 if (!x) {
1113 // need to choose from dependent fundamentals, in following order:
1114 // parentX (if not a glob), robs, globs, vars, args
1115
1116 if (_parentX && !dynamic_cast<RooAbsArg *>(_parentX->GetParent())->getAttribute("global") &&
1117 (o->dependsOn(*dynamic_cast<RooAbsArg *>(_parentX->GetParent())) || vars().empty())) {
1118 x = dynamic_cast<RooAbsLValue *>(_parentX->GetParent());
1119 } else if (auto _obs = obs(); !_obs.empty()) {
1120 for (auto &v : _obs) {
1121 if (!v->get<RooAbsArg>()->getAttribute("global")) {
1122 x = v->get<RooAbsLValue>();
1123 if (x)
1124 break;
1125 } else if (!x) {
1126 x = v->get<RooAbsLValue>();
1127 }
1128 }
1129 } else if (auto _pars = pars(); !_pars.empty()) {
1130 for (auto &v : _pars) {
1131 if (!v->get<RooAbsArg>()->getAttribute("Constant")) {
1132 x = v->get<RooAbsLValue>();
1133 if (x)
1134 break;
1135 } else if (!x) {
1136 x = v->get<RooAbsLValue>();
1137 }
1138 }
1139 }
1140
1141 if (!x) {
1142 return nullptr;
1143 }
1144 }
1145
1146 /* no longer 'remembering' xvars when call GetXaxis(), as was causing incorrect obs to go onto node
1147 * when e.g. adding histogram with bin errors to a channel - the statFactor and constraint terms were
1148 * created first, causing the channel's xaxis to become equal to a globs at some point because of a GetXaxis call
1149 * Alternatively we could clear the xvar attribute of all client nodes whenever we 'modify' something
1150 * Should do that if processing large workspaces becomes slow
1151 if (o != dynamic_cast<TObject *>(x)) {
1152 o->setStringAttribute("xvar", dynamic_cast<TObject *>(x)->GetName());
1153 } */
1154
1155 // decide binning to use
1156 TString binningName = o->getStringAttribute("binning");
1157 auto _bnames = x->getBinningNames();
1158 bool hasBinning = false;
1159 for (auto &b : _bnames) {
1160 if (b == binningName) {
1161 hasBinning = true;
1162 break;
1163 }
1164 }
1165 if (!hasBinning) {
1166 // doesn't have binning, so clear binning attribute
1167 // this can happen after Combine of models because binning don't get combined yet (should fix this)
1168 Warning("GetXaxis", "Binning %s not defined on %s - clearing", binningName.Data(),
1169 dynamic_cast<TObject *>(x)->GetName());
1170 o->setStringAttribute("binning", nullptr);
1171 binningName = "";
1172 }
1173
1174 if (binningName == "" && o != dynamic_cast<TObject *>(x)) {
1175 // has var has a binning matching this nodes name then use that
1176 auto __bnames = x->getBinningNames();
1177 for (auto &b : __bnames) {
1178 if (b == GetName())
1179 binningName = GetName();
1180 if (b == o->GetName()) {
1181 binningName = o->GetName();
1182 break;
1183 } // best match
1184 }
1185 if (binningName == "") {
1186 // if we are binned in this var then will define that as a binning
1187 if (/*o->isBinnedDistribution(*dynamic_cast<RooAbsArg *>(x))*/
1188 auto bins = _or_func(
1189 /*o->plotSamplingHint(*dynamic_cast<RooAbsRealLValue
1190 *>(x),-std::numeric_limits<double>::infinity(),std::numeric_limits<double>::infinity())*/
1191 (std::list<double> *)(nullptr),
1192 (dynamic_cast<RooAbsRealLValue *>(x))
1193 ? o->binBoundaries(*dynamic_cast<RooAbsRealLValue *>(x), -std::numeric_limits<double>::infinity(),
1194 std::numeric_limits<double>::infinity())
1195 : nullptr);
1196 bins) {
1197 std::vector<double> _bins;
1198 for (auto &b : *bins) {
1199 if (_bins.empty() || std::abs(_bins.back() - b) > 1e-5 * _bins.back())
1200 _bins.push_back(b);
1201 }
1202 fXAxis = std::make_shared<Axis2>(_bins.size() - 1, &_bins[0]);
1203 // add this binning to the var to avoid recalling ...
1204 if (auto _v = dynamic_cast<RooRealVar *>(x); _v) {
1205 _v->setBinning(RooBinning(_bins.size() - 1, &_bins[0], o->GetName()), o->GetName());
1206 _v->getBinning(o->GetName())
1207 .SetTitle(""); // indicates to use the current var title when building histograms etc
1208 //_v->getBinning(o->GetName()).SetTitle(strlen(dynamic_cast<TObject*>(x)->GetTitle()) ?
1209 // dynamic_cast<TObject*>(x)->GetTitle() : dynamic_cast<TObject*>(x)->GetName());
1210 }
1211 binningName = o->GetName();
1212 delete bins;
1213 } else if (_parentX) {
1214 // use parent axis binning if defined, otherwise we will default
1215 binningName = _parentX->GetName();
1216 }
1217 }
1218 }
1219
1220 if (!fXAxis) {
1221 if (auto r = dynamic_cast<RooAbsRealLValue *>(x); r) {
1222 if (r->getBinning(binningName).isUniform()) {
1223 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), r->getMin(binningName), r->getMax(binningName));
1224 } else {
1225 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), r->getBinning(binningName).array());
1226 }
1227 } else if (auto cat = dynamic_cast<RooCategory *>(x)) {
1228 std::vector<double> bins = {};
1229 for (int i = 0; i <= x->numBins(binningName); i++)
1230 bins.push_back(i);
1231 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), &bins[0]);
1232 // TODO have to load current state of bin labels if was a category (sadly not a virtual method)
1233 int i = 1;
1234 std::map<int, std::string> cats; // fill into a map to preserve index ordering
1235 for (auto &c : *cat) {
1236 if (cat->isStateInRange(binningName, c.first.c_str())) {
1237 cats[c.second] = c.first;
1238 }
1239 }
1240 for (auto &[_, label] : cats) {
1241 fXAxis->SetBinLabel(i++, label.c_str());
1242 }
1243 }
1244 }
1245
1246 fXAxis->SetName(binningName);
1247 fXAxis->SetParent(dynamic_cast<TObject *>(x));
1248 return fXAxis.get();
1249}
1250
1251const char *xRooNode::GetIconName() const
1252{
1253 if (auto o = get(); o) {
1254 if (o->InheritsFrom("RooWorkspace"))
1255 return "TFile";
1256 if (o->InheritsFrom("RooAbsData"))
1257 return "TProfile";
1258 if (o->InheritsFrom("RooSimultaneous"))
1259 return "TH3D";
1260
1261 if (o->InheritsFrom("RooProdPdf"))
1262 return "a.C"; // or nullptr for folder
1263 if (o->InheritsFrom("RooRealSumPdf") || o->InheritsFrom("RooAddPdf"))
1264 return "TH2D";
1265 // if(o->InheritsFrom("RooProduct")) return "TH1D";
1266 if (o->InheritsFrom("RooFitResult")) {
1267 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitRooFitResult", true)) {
1268 gClient->GetMimeTypeList()->AddType("xRooFitRooFitResult", "xRooFitRooFitResult", "package.xpm",
1269 "package.xpm", "->Browse()");
1270 }
1271 return "xRooFitRooFitResult";
1272 }
1273 if (o->InheritsFrom("RooRealVar") || o->InheritsFrom("RooCategory")) {
1274 if (get<RooAbsArg>()->getAttribute("obs")) {
1275 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitObs", true)) {
1276 gClient->GetMimeTypeList()->AddType("xRooFitObs", "xRooFitObs", "x_pic.xpm", "x_pic.xpm", "->Browse()");
1277 }
1278 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitGlobs", true)) {
1279 gClient->GetMimeTypeList()->AddType("xRooFitGlobs", "xRooFitGlobs", "z_pic.xpm", "z_pic.xpm",
1280 "->Browse()");
1281 }
1282 return (get<RooAbsArg>()->getAttribute("global") ? "xRooFitGlobs" : "xRooFitObs");
1283 }
1284 return "TLeaf";
1285 }
1286 if (o->InheritsFrom("TStyle")) {
1287 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitTStyle", true)) {
1288 gClient->GetMimeTypeList()->AddType("xRooFitTStyle", "xRooFitTStyle", "bld_colorselect.xpm",
1289 "bld_colorselect.xpm", "->Browse()");
1290 }
1291 return "xRooFitTStyle";
1292 }
1293 if (o->InheritsFrom("RooConstVar")) {
1294 /*if (!gClient->GetMimeTypeList()->GetIcon("xRooFitRooConstVar",true)) {
1295 gClient->GetMimeTypeList()->AddType("xRooFitRooConstVar", "xRooFitRooConstVar", "stop_t.xpm", "stop_t.xpm",
1296 "->Browse()");
1297 }
1298 return "xRooFitRooConstVar";*/
1299 return "TMethodBrowsable-leaf";
1300 }
1301 if (o->InheritsFrom("RooStats::HypoTestInverterResult")) {
1302 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitScanStyle", true)) {
1303 gClient->GetMimeTypeList()->AddType("xRooFitScanStyle", "xRooFitScanStyle", "f2_s.xpm", "f2_s.xpm",
1304 "->Browse()");
1305 }
1306 return "xRooFitScanStyle";
1307 }
1308 if (o->InheritsFrom("RooStats::HypoTestResult")) {
1309 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitTestStyle", true)) {
1310 gClient->GetMimeTypeList()->AddType("xRooFitTestStyle", "xRooFitTestStyle", "diamond.xpm", "diamond.xpm",
1311 "->Browse()");
1312 }
1313 return "xRooFitTestStyle";
1314 }
1315 if (o->InheritsFrom("RooStats::HistFactory::FlexibleInterpVar"))
1316 return "TBranchElement-folder";
1317 if (o->InheritsFrom("RooAbsPdf")) {
1318 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitPDFStyle", true)) {
1319 gClient->GetMimeTypeList()->AddType("xRooFitPDFStyle", "xRooFitPDFStyle", "pdf.xpm", "pdf.xpm",
1320 "->Browse()");
1321 }
1322 return "xRooFitPDFStyle";
1323 }
1324 if (o->InheritsFrom("RooStats::ModelConfig")) {
1325 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitMCStyle", true)) {
1326 gClient->GetMimeTypeList()->AddType("xRooFitMCStyle", "xRooFitMCStyle", "app_t.xpm", "app_t.xpm",
1327 "->Browse()");
1328 }
1329 return "xRooFitMCStyle";
1330 }
1331 if (auto a = dynamic_cast<RooAbsReal *>(o); a) {
1332 if (auto _ax = GetXaxis();
1333 _ax && (a->isBinnedDistribution(*dynamic_cast<RooAbsArg *>(_ax->GetParent())) ||
1334 (dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
1335 std::unique_ptr<std::list<double>>(a->binBoundaries(
1336 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
1337 std::numeric_limits<double>::infinity()))))) {
1338 return "TH1D";
1339 }
1340 return "TF1";
1341 }
1342 return o->ClassName();
1343 }
1344 if (!IsFolder()) {
1345 return "Unknown";
1346 }
1347 return nullptr;
1348}
1349
1350const char *xRooNode::GetNodeType() const
1351{
1352 if (auto o = get(); o && fParent && (fParent->get<RooProduct>() || fParent->get<RooRealSumPdf>())) {
1353 if (o->InheritsFrom("RooStats::HistFactory::FlexibleInterpVar"))
1354 return "Overall";
1355 if (o->InheritsFrom("PiecewiseInterpolation")) {
1356 // check if children are all RooHistFunc ... if so, it's a HistoFactor, otherwise it's a Varied
1357 bool isHisto = true;
1358 for (auto c : const_cast<xRooNode *>(this)->browse()) {
1359 if (!c->get<RooHistFunc>()) {
1360 isHisto = false;
1361 break;
1362 }
1363 }
1364 if (isHisto) {
1365 return (dynamic_cast<RooAbsArg *>(o)->getAttribute("density")) ? "HistoDensity" : "Histo";
1366 } else {
1367 return (dynamic_cast<RooAbsArg *>(o)->getAttribute("density")) ? "VariedDensity" : "Varied";
1368 }
1369 }
1370 if (o->InheritsFrom("RooHistFunc"))
1371 return (dynamic_cast<RooAbsArg *>(o)->getAttribute("density")) ? "SimpleDensity" : "Simple";
1372 if (o->InheritsFrom("RooBinWidthFunction"))
1373 return "Density";
1374 if (o->InheritsFrom("ParamHistFunc"))
1375 return "Shape";
1376 if (o->InheritsFrom("RooRealVar"))
1377 return "Norm";
1378 if (o->InheritsFrom("RooConstVar"))
1379 return "Const";
1380 }
1381 return "";
1382}
1383
1385{
1386 xRooNode out(".coords", nullptr, *this);
1387 // go up through parents looking for slice obs
1388 auto _p = std::shared_ptr<xRooNode>(const_cast<xRooNode *>(this), [](xRooNode *) {});
1389 while (_p) {
1390 TString pName(_p->GetName());
1391 // following is commented out while still considering, but idea is to include category in coords
1392 /*if (auto s = _p->get<RooSimultaneous>(); s && s->indexCat().InheritsFrom("RooCategory") &&
1393 !out.find(s->indexCat().GetName())) { auto cat = const_cast<RooCategory*>(dynamic_cast<const
1394 RooCategory*>(&s->indexCat()));
1395 // check if we have a pdf for every category ... if not then add to cut
1396 cat->clearRange("coordRange",true);
1397 bool hasMissing = false;
1398 std::string includedStates;
1399 for (auto state : *cat) {
1400 if (!s->getPdf(state.first.c_str())) {
1401 hasMissing = true;
1402 } else {
1403 if (!includedStates.empty()) {
1404 includedStates += ",";
1405 }
1406 includedStates += state.first;
1407 }
1408 }
1409 if (hasMissing) {
1410 if(includedStates.find(",") != std::string::npos) {
1411 cat->addToRange("coordRange",includedStates.c_str());
1412 } else {
1413 cat->setLabel(includedStates);
1414 }
1415 out.emplace_back(std::make_shared<xRooNode>(cat->GetName(),_p->getObject<RooAbsArg>(cat->GetName()),_p));
1416 }
1417 } else*/
1418 if (auto pos = pName.Index('='); pos != -1) {
1419 if (pos > 0 && pName(pos - 1) == '<') {
1420 // should be a range on a real lvalue, of form low<=name<high
1421 double low = TString(pName(0, pos - 1)).Atof();
1422 pName = pName(pos + 1, pName.Length());
1423 double high = TString(pName(pName.Index('<') + 1, pName.Length())).Atof();
1424 pName = pName(0, pName.Index('<'));
1425 if (auto _obs = _p->getObject<RooAbsRealLValue>(pName.Data()); _obs) {
1426 if (setVals) {
1427 _obs->setVal((high + low) / 2.);
1428 static_cast<RooRealVar *>(_obs.get())->setRange("coordRange", low, high);
1429 _obs->setStringAttribute(
1430 "coordRange", "coordRange"); // will need if we allow multi disconnected regions, need comma list
1431 }
1432 out.emplace_back(std::make_shared<xRooNode>(_obs->GetName(), _obs, _p));
1433 } else {
1434 throw std::runtime_error(TString::Format("Unknown observable: %s", pName.Data()));
1435 }
1436
1437 } else if (auto _obs = _p->getObject<RooAbsArg>(pName(0, pos)); _obs) {
1438 if (setVals) {
1439 if (auto _cat = dynamic_cast<RooAbsCategoryLValue *>(_obs.get()); _cat) {
1440 _cat->setLabel(pName(pos + 1, pName.Length()));
1441 } else if (auto _var = dynamic_cast<RooAbsRealLValue *>(_obs.get()); _var) {
1442 _var->setVal(TString(pName(pos + 1, pName.Length())).Atof());
1443 }
1444 }
1445 out.emplace_back(std::make_shared<xRooNode>(_obs->GetName(), _obs, _p));
1446 } else {
1447 throw std::runtime_error("Unknown observable, could not find");
1448 }
1449 }
1450 _p = _p->fParent;
1451 }
1452 return out;
1453}
1454
1455void xRooNode::_Add_(const char *name, const char *opt)
1456{
1457 try {
1458 Add(name, opt);
1459 } catch (const std::exception &e) {
1460 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
1461 kMBIconExclamation); // deletes self on dismiss?
1462 }
1463}
1464void xRooNode::_Vary_(const char *what)
1465{
1466 try {
1467 Vary(what);
1468 } catch (const std::exception &e) {
1469 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
1470 kMBIconExclamation); // deletes self on dismiss?
1471 }
1472}
1473
1475{
1476
1477 if (strcmp(GetName(), ".poi") == 0) {
1478 // demote a parameter from being a poi
1479 auto toRemove =
1480 (child.get<RooAbsArg>() || !find(child.GetName())) ? child : xRooNode(find(child.GetName())->fComp);
1481 if (toRemove) {
1482 if (!toRemove.get<RooAbsArg>()->getAttribute("poi")) {
1483 throw std::runtime_error(TString::Format("%s is not a poi", toRemove.GetName()));
1484 }
1485 toRemove.get<RooAbsArg>()->setAttribute("poi", false);
1486 return toRemove;
1487 }
1488 } else if (strcmp(GetName(), ".factors") == 0 || strcmp(GetName(), ".constraints") == 0 ||
1489 strcmp(GetName(), ".components") == 0) {
1490 auto toRemove =
1491 (child.get<RooAbsArg>() || !find(child.GetName())) ? child : xRooNode(find(child.GetName())->fComp);
1492 if (auto p = fParent->get<RooProdPdf>(); p) {
1493 auto pdf = toRemove.get<RooAbsArg>();
1494 if (!pdf)
1495 pdf = p->pdfList().find(child.GetName());
1496 if (!pdf)
1497 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1498 auto i = p->pdfList().index(*pdf);
1499 if (i >= 0) {
1500#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
1501 const_cast<RooArgList &>(p->pdfList()).remove(*pdf);
1502#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
1503 p->_pdfNSetList.erase(p->_pdfNSetList.begin() + i);
1504#else
1505 auto nset = p->_pdfNSetList.At(i);
1506 p->_pdfNSetList.Remove(nset);
1507 delete nset; // I don't think the RooLinkedList owned it so must delete ourself
1508#endif
1509 if (p->_extendedIndex == i)
1510 p->_extendedIndex = -1;
1511 else if (p->_extendedIndex > i)
1512 p->_extendedIndex--;
1513#else
1514 p->removePdfs(RooArgSet(*pdf));
1515#endif
1516 sterilize();
1517 return xRooNode(*pdf);
1518 } else {
1519 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1520 }
1521 } else if (auto p2 = fParent->get<RooProduct>(); p2) {
1522 auto arg = toRemove.get<RooAbsArg>();
1523 if (!arg)
1524 arg = p2->components().find(child.GetName());
1525 if (!arg)
1526 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1527 // remove server ... doesn't seem to trigger removal from proxy
1528#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
1529 p2->_compRSet.remove(*arg);
1530#else
1531 const_cast<RooArgList &>(p2->realComponents()).remove(*arg);
1532#endif
1533 p2->removeServer(*arg, true);
1534 sterilize();
1535 return xRooNode(*arg);
1536 } else if (fParent->get<RooSimultaneous>()) {
1537 // remove from all channels
1538 bool removed = false;
1539 for (auto &c : fParent->bins()) {
1540 try {
1541 c->constraints().Remove(toRemove);
1542 removed = true;
1543 } catch (std::runtime_error &) { /* wasn't a constraint in channel */
1544 }
1545 }
1546 sterilize();
1547 if (!removed)
1548 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1549 return toRemove;
1550 } else if (auto p4 = fParent->get<RooRealSumPdf>(); p4) {
1551 auto arg = toRemove.get<RooAbsArg>();
1552 if (!arg)
1553 arg = p4->funcList().find(child.GetName());
1554 if (!arg)
1555 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1556 // remove, including coef removal ....
1557 auto idx = p4->funcList().index(arg);
1558
1559 if (idx != -1) {
1560
1561 const_cast<RooArgList &>(p4->funcList()).remove(*arg);
1562 p4->removeServer(*arg, true);
1563 // have to be careful removing coef because if shared will end up removing them all!!
1564 std::vector<RooAbsArg *> _coefs;
1565 for (size_t ii = 0; ii < const_cast<RooArgList &>(p4->coefList()).size(); ii++) {
1566 if (ii != size_t(idx))
1567 _coefs.push_back(const_cast<RooArgList &>(p4->coefList()).at(ii));
1568 }
1569 const_cast<RooArgList &>(p4->coefList()).removeAll();
1570 for (auto &a : _coefs)
1571 const_cast<RooArgList &>(p4->coefList()).add(*a);
1572
1573 sterilize();
1574 } else {
1575 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1576 }
1577 return xRooNode(*arg);
1578 } else if (auto p5 = fParent->get<RooAddPdf>(); p5) {
1579 auto arg = toRemove.get<RooAbsArg>();
1580 if (!arg)
1581 arg = p5->pdfList().find(child.GetName());
1582 if (!arg)
1583 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1584 // remove, including coef removal ....
1585 auto idx = p5->pdfList().index(arg);
1586
1587 if (idx != -1) {
1588
1589 const_cast<RooArgList &>(p5->pdfList()).remove(*arg);
1590 p5->removeServer(*arg, true);
1591 // have to be careful removing coef because if shared will end up removing them all!!
1592 std::vector<RooAbsArg *> _coefs;
1593 for (size_t ii = 0; ii < const_cast<RooArgList &>(p5->coefList()).size(); ii++) {
1594 if (ii != size_t(idx))
1595 _coefs.push_back(const_cast<RooArgList &>(p5->coefList()).at(ii));
1596 }
1597 const_cast<RooArgList &>(p5->coefList()).removeAll();
1598 for (auto &a : _coefs)
1599 const_cast<RooArgList &>(p5->coefList()).add(*a);
1600
1601 sterilize();
1602 } else {
1603 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1604 }
1605 return xRooNode(*arg);
1606 } else if (auto p6 = fParent->get<RooAddition>(); p6) {
1607 auto arg = toRemove.get<RooAbsArg>();
1608 if (!arg)
1609 arg = p6->list().find(child.GetName());
1610 if (!arg)
1611 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1612 // remove server ... doesn't seem to trigger removal from proxy
1613 const_cast<RooArgList &>(p6->list()).remove(*arg);
1614 p6->removeServer(*arg, true);
1615 sterilize();
1616 return xRooNode(*arg);
1617 }
1618 }
1619
1620 if (auto w = get<RooWorkspace>(); w) {
1621 xRooNode out(child.GetName());
1622 auto arg = w->components().find(child.GetName());
1623 if (!arg)
1624 arg = operator[](child.GetName())->get<RooAbsArg>();
1625 if (!arg) {
1626 throw std::runtime_error(TString::Format("Cannot find %s in workspace %s", child.GetName(), GetName()));
1627 }
1628 // check has no clients ... if so, cannot delete
1629 if (arg->hasClients()) {
1630 throw std::runtime_error(
1631 TString::Format("Cannot remove %s from workspace %s, because it has dependencies - first remove from those",
1632 child.GetName(), GetName()));
1633 }
1634 const_cast<RooArgSet &>(w->components()).remove(*arg); // deletes arg
1635 Info("Remove", "Deleted %s from workspace %s", out.GetName(), GetName());
1636 return out;
1637 } else if (get<RooProduct>() || get<RooProdPdf>()) {
1638 return factors().Remove(child);
1639 } else if (get<RooRealSumPdf>() || get<RooAddPdf>() || get<RooAddition>()) {
1640 return components().Remove(child);
1641 }
1642
1643 throw std::runtime_error("Removal not implemented for object type " +
1644 std::string(get() ? get()->ClassName() : "null"));
1645}
1646
1648{
1649
1650 class AutoUpdater {
1651 public:
1652 AutoUpdater(xRooNode &_n) : n(_n) {}
1653 ~AutoUpdater() { n.browse(); }
1654 xRooNode &n;
1655 };
1656 AutoUpdater xxx(*this);
1657
1658 TString sOpt(opt);
1659 bool considerType(sOpt == "+");
1660
1661 if (strlen(GetName()) > 0 && GetName()[0] == '!' && fParent) {
1662 // folder .. pass onto parent and add folder to child folder list
1663 const_cast<xRooNode &>(child).fFolder += GetName();
1664 return fParent->Add(child, opt);
1665 }
1666 // this is how to get the first real parent ... may be useful at some point?
1667 /*auto realParent = fParent;
1668 while(!realParent->get()) {
1669 realParent = realParent->fParent;
1670 if (!realParent) throw std::runtime_error("No parentage");
1671 }*/
1672
1673 // adding to a collection node will incorporate the child into the parent of the collection
1674 // in the appropriate way
1675 if (strcmp(GetName(), ".factors") == 0) {
1676 // multiply the parent
1677 return fParent->Multiply(child, opt);
1678 } else if (strcmp(GetName(), ".components") == 0) {
1679 // add to the parent
1680 return fParent->Add(child, opt);
1681 } else if (strcmp(GetName(), ".variations") == 0) {
1682 // vary the parent
1683 return fParent->Vary(child);
1684 } else if (strcmp(GetName(), ".constraints") == 0) {
1685 // constrain the parent
1686 return fParent->Constrain(child);
1687 } else if (strcmp(GetName(), ".bins") == 0 && fParent->get<RooSimultaneous>()) {
1688 // adding a channel (should adding a 'bin' be an 'Extend' operation?)
1689 return fParent->Vary(child);
1690 } else if ((strcmp(GetName(), ".globs") == 0)) {
1691 if (child.get<RooAbsArg>() || (!child.fComp && getObject<RooAbsArg>(child.GetName()))) {
1692 auto out = (child.get<RooAbsArg>()) ? child.get<RooAbsArg>() : getObject<RooAbsArg>(child.GetName()).get();
1693 out->setAttribute("obs");
1694 out->setAttribute("global");
1695 return xRooNode(*out, *this);
1696 }
1697 throw std::runtime_error("Failed to add global observable");
1698 } else if ((strcmp(GetName(), ".poi") == 0)) {
1699 if (child.get<RooAbsLValue>() || (!child.fComp && getObject<RooAbsLValue>(child.GetName()))) {
1700 auto out = (child.get<RooAbsArg>()) ? child.get<RooAbsArg>() : getObject<RooAbsArg>(child.GetName()).get();
1701 out->setAttribute("poi");
1702 return xRooNode(*out, *this);
1703 } else if (!child.get() && fParent->get<RooWorkspace>()) {
1704 // may be creating poi at same time as adding, try add to parent
1705 auto res = fParent->Add(child);
1706 if (res.get<RooAbsLValue>())
1707 return Add(res);
1708 }
1709 throw std::runtime_error("Failed to add parameter of interest");
1710 } else if ((strcmp(GetName(), ".pars") == 0 || strcmp(GetName(), ".vars") == 0) && fParent->get<RooWorkspace>()) {
1711 // adding a parameter, interpret as factory string unless no "[" then create RooRealVar
1712 TString fac(child.GetName());
1713 if (!fac.Contains("[") && !fac.Contains("("))
1714 fac += "[1]";
1715 return xRooNode(*fParent->get<RooWorkspace>()->factory(fac), fParent);
1716 } else if (strcmp(GetName(), ".datasets()") == 0) {
1717
1718 if (auto _data = child.get<RooAbsData>(); _data) {
1719 if (find(_data->GetName())) {
1720 throw std::runtime_error(TString::Format("Cannot add dataset %s, already exists for %s. If intending to "
1721 "combine datasets, please add directly to dataset",
1722 child->GetName(), GetName()));
1723 }
1724 return fParent->Add(child); // add the dataset to the parent
1725 }
1726
1727 // create a dataset - only allowed for pdfs or workspaces
1728 if (auto _ws = ws(); _ws && fParent) {
1729 sOpt.ToLower();
1730 if (!fParent->get<RooAbsPdf>() && (!fParent->get<RooWorkspace>() || sOpt == "asimov")) {
1731 throw std::runtime_error(
1732 "Datasets can only be created for pdfs or workspaces (except if generated dataset, then must be pdf)");
1733 }
1734
1735 if (sOpt == "asimov" || sOpt == "toy") {
1736 // generate expected dataset - note that globs will be frozen at this time
1737 auto _fr = fParent->fitResult();
1738 if (strlen(_fr->GetName()) == 0) { // ensure fit result has a name so that name is saved inside dataset
1739 _fr.get<RooFitResult>()->SetName(TUUID().AsString());
1740 }
1741 auto ds = fParent->generate(_fr, sOpt == "asimov");
1742 if (strlen(child.GetName())) {
1743 ds.SetName(child.GetName());
1744 ds.get<TNamed>()->SetName(child.GetName());
1745 }
1746 if (auto _ds = ds.get<RooAbsData>()) {
1747 _ws->import(*_ds);
1748 }
1749 if (_fr.get<RooFitResult>()->numStatusHistory() == 0) {
1750 if (!GETWSSNAPSHOTS(_ws).find(_fr->GetName())) {
1751 const_cast<RooLinkedList &>(GETWSSNAPSHOTS(_ws)).Add(_fr->Clone());
1752 }
1753 } else if (!_ws->obj(_fr->GetName())) {
1754 _ws->import((*_fr.get<RooFitResult>()));
1755 } // save fr to workspace, for later retrieval
1756 return xRooNode(*_ws->data(ds.GetName()), fParent);
1757 }
1758
1759 auto parentObs = fParent->obs(); // may own globs so keep alive
1760 auto _obs = parentObs.argList();
1761 // put globs in a snapshot
1762 std::unique_ptr<RooAbsCollection> _globs(_obs.selectByAttrib("global", true));
1763 // RooArgSet _tmp; _tmp.add(*_globs);_ws->saveSnapshot(child.GetName(),_tmp);
1764 _obs.remove(*_globs);
1765
1766 // include any coords
1767 _obs.add(coords(false).argList(), true);
1768 // include axis var too, provided it's an observable
1769 if (auto ax = GetXaxis(); ax && dynamic_cast<RooAbsArg *>(ax->GetParent())->getAttribute("obs")) {
1770 _obs.add(*dynamic_cast<RooAbsArg *>(ax->GetParent()));
1771 }
1772 // check if ws already has a dataset with this name, if it does we may need to extend columns
1773 if (auto _d = _ws->data(child.GetName()); _d) {
1774 // add any missing obs
1775 RooArgSet l(_obs);
1776 l.remove(*_d->get(), true, true);
1777 if (!l.empty()) {
1778 auto _dd = dynamic_cast<RooDataSet *>(_d);
1779 if (!_dd)
1780 throw std::runtime_error("Cannot extend dataset with new columns");
1781 for (auto &x : l) {
1782 _dd->addColumn(*x);
1783 }
1784 }
1785 } else {
1786 RooRealVar w("weightVar", "weightVar", 1);
1787 _obs.add(w);
1788 RooDataSet d(child.GetName(), child.GetTitle(), _obs, RooFit::WeightVar("weightVar"));
1789 _ws->import(d);
1790 // seems have to set bits after importing, not before
1791 if (auto __d = _ws->data(child.GetName()))
1792 __d->SetBit(1 << 20, _ws->allData().size() == 1); // sets as selected if is only ds
1793 }
1794 /*if(!_ws->data(child.GetName())) {
1795 RooRealVar w("weightVar", "weightVar", 1);
1796 RooArgSet _obs; _obs.add(w);
1797 RooDataSet d(child.GetName(), child.GetTitle(), _obs, "weightVar");
1798 _ws->import(d);
1799 }*/
1800 auto out = std::shared_ptr<TObject>(_ws->data(child.GetName()), [](TObject *) {});
1801
1802 if (out) {
1803 xRooNode o(out, fParent);
1804 if (child.get<TH1>())
1805 o = *child.get();
1806 return o;
1807 }
1808 }
1809 throw std::runtime_error("Cannot create dataset");
1810 }
1811
1812 if (!get()) {
1813 if (!fParent)
1814 throw std::runtime_error("Cannot add to null object with no parentage");
1815
1816 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
1817 try {
1818 fComp = fParent->Add(*this, "+").fComp;
1819 } catch (...) {
1820 resize(size() - 1);
1821 std::rethrow_exception(std::current_exception());
1822 }
1823 resize(size() - 1); // remove the temporarily added node
1824
1825 if (!fComp) {
1826 throw std::runtime_error("No object");
1827 }
1828 }
1829
1830 if (auto p = get<RooAbsData>(); p) {
1831 if (auto bb = getBrowsable(".sourceds"))
1832 bb->Add(child, opt);
1833 if (auto _data = child.get<RooDataSet>()) {
1834 auto ds = dynamic_cast<RooDataSet *>(p);
1835 if (!ds) {
1836 throw std::runtime_error("Can only add datasets to a dataset");
1837 }
1838
1839 // append any missing globs, and check any existing globs have matching values
1841 auto _globs = globs();
1842 for (auto &glob : child.globs()) {
1843 if (auto g = _globs.find(glob->GetName()); !g) {
1844 globsToAdd.addClone(*glob->get<RooAbsArg>());
1845 } else if (g->GetContent() != glob->GetContent()) {
1846 Warning("Add", "Global observable %s=%g in dataset %s mismatches %s value %g ... ignoring latter",
1847 g->GetName(), g->GetContent(), GetName(), child.GetName(), glob->GetContent());
1848 }
1849 }
1850 // add any existing globs to list then set the list
1851 if (auto _dglobs = p->getGlobalObservables()) {
1852 globsToAdd.addClone(*_dglobs);
1853 } else {
1854 for (auto g : _globs)
1855 globsToAdd.addClone(*g->get<RooAbsArg>());
1856 }
1857 p->setGlobalObservables(globsToAdd);
1858
1859 // append any missing observables to our dataset, then append the dataset
1860
1861 std::set<std::pair<RooAbsCategory *, RooCategory *>> cats;
1862
1863 for (auto col : *_data->get()) {
1864 if (!p->get()->contains(*col)) {
1865 ds->addColumn(*col);
1866 } else if (auto c = dynamic_cast<RooAbsCategory *>(col)) {
1867 // check if any of the states of c have different index to c2
1868 auto c2 = dynamic_cast<RooCategory *>(p->get()->find(*col));
1869 if (!c2) {
1870 throw std::runtime_error(
1871 TString::Format("unexpected type for regular observable: %s", col->GetName()));
1872 }
1873 bool iMatches = true;
1874 for (const auto &nameIdx : *c) {
1875 if (!c2->hasLabel(nameIdx.first) && !c2->hasIndex(nameIdx.second)) {
1876 // can define the state
1877 c2->defineType(nameIdx.first, nameIdx.second);
1878 } else if (c2->lookupIndex(nameIdx.first) != nameIdx.second) {
1879 iMatches = false;
1880 break; // state exists, but with different index!
1881 }
1882 }
1883 if (!iMatches)
1884 cats.insert({c, c2});
1885 }
1886 }
1887 if (cats.empty()) {
1888 ds->append(*_data);
1889 } else {
1890 // cannot use append, because if categoricals use same idx for different states, will not do correct thing
1891 for (int i = 0; i < _data->numEntries(); i++) {
1892 auto row = _data->get(i);
1893 auto w = _data->weight();
1894 ds->get()->assign(*row);
1895 for (auto [c, c2] : cats) {
1896 c2->setLabel(row->getCatLabel(c->GetName()));
1897 }
1898 ds->add(*ds->get(), w);
1899 }
1900 }
1901
1902 ds->SetTitle(TString(ds->GetTitle()) + " + " + _data->GetTitle());
1903 SetTitle(TString(GetTitle()) + " + " + child.GetTitle());
1904 return *this;
1905 }
1906 auto _h = child.get<TH1>();
1907 if (!_h) {
1908 throw std::runtime_error("Can only add histogram or dataset to data");
1909 }
1910 auto _pdf = parentPdf();
1911 if (!_pdf)
1912 throw std::runtime_error("Could not find pdf");
1913 auto _ax = _pdf->GetXaxis();
1914 if (!_ax) {
1915 throw std::runtime_error("Cannot determine binning to add data");
1916 }
1917
1918 RooArgSet obs;
1919 obs.add(*dynamic_cast<RooAbsArg *>(_ax->GetParent()));
1920 obs.add(coords().argList()); // will also move obs to coords
1921
1922 // add any missing obs
1923 RooArgSet l(obs);
1924 l.remove(*p->get(), true, true);
1925 if (!l.empty()) {
1926 auto _d = dynamic_cast<RooDataSet *>(p);
1927 if (!_d)
1928 throw std::runtime_error("Cannot extend dataset with new columns");
1929 for (auto &x : l) {
1930 _d->addColumn(*x);
1931 }
1932 }
1933
1934 // before adding, ensure range is good to cover
1935 for (auto &o : obs) {
1936 if (auto v = dynamic_cast<RooRealVar *>(o); v) {
1937 if (auto dv = dynamic_cast<RooRealVar *>(p->get()->find(v->GetName())); dv) {
1938 if (v->getMin() < dv->getMin())
1939 dv->setMin(v->getMin());
1940 if (v->getMax() > dv->getMax())
1941 dv->setMax(v->getMax());
1942 }
1943 } else if (auto c = dynamic_cast<RooCategory *>(o); c) {
1944 if (auto dc = dynamic_cast<RooCategory *>(p->get()->find(c->GetName())); dc) {
1945 for (const auto &nameIdx : *c) {
1946 if (!dc->hasLabel(nameIdx.first)) {
1947 dc->defineType(nameIdx.first, nameIdx.second);
1948 }
1949 }
1950 }
1951 }
1952 }
1953
1954 for (int i = 1; i <= _h->GetNbinsX(); i++) {
1955 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(_ax->GetParent())) {
1956 if (!_h->GetXaxis()->GetBinLabel(i)) {
1957 throw std::runtime_error(
1958 TString::Format("Categorical observable %s requires bin labels", _ax->GetParent()->GetName()));
1959 } else if (!cat->hasLabel(_h->GetXaxis()->GetBinLabel(i))) {
1960 throw std::runtime_error(TString::Format("Categorical observable %s does not have label %s",
1961 _ax->GetParent()->GetName(), _h->GetXaxis()->GetBinLabel(i)));
1962 } else {
1963 cat->setLabel(_h->GetXaxis()->GetBinLabel(i));
1964 }
1965 } else {
1966 dynamic_cast<RooAbsRealLValue *>(_ax->GetParent())->setVal(_h->GetBinCenter(i));
1967 }
1968 p->add(obs, _h->GetBinContent(i));
1969 }
1970
1971 return *this;
1972 }
1973
1974 if (auto p = get<RooAddPdf>(); p) {
1975 if ((child.get<RooAbsPdf>() || (!child.fComp && getObject<RooAbsPdf>(child.GetName())))) {
1976 auto out = (child.fComp) ? acquire(child.fComp) : getObject<RooAbsArg>(child.GetName());
1977 // don't add a coef if in 'all-extended' mode and this pdf is extendable
1978 auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out);
1979 if (!_pdf) {
1980 throw std::runtime_error("Something went wrong with pdf acquisition");
1981 }
1982
1983 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
1984 _pdf->dependsOn(*static_cast<RooAbsArg *>(_ax->GetParent()))) {
1985 auto _p = _pdf;
1986
1987 if (auto _boundaries = std::unique_ptr<std::list<double>>(_p->binBoundaries(
1988 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
1989 std::numeric_limits<double>::infinity()));
1990 !_boundaries && _ax->GetNbins() > 0) {
1991#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
1992 Warning("Add", "Adding unbinned pdf %s to binned %s - will wrap with RooBinSamplingPdf(...)",
1993 _p->GetName(), GetName());
1994 _p = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _p->GetName()), _p->GetTitle(),
1995 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *_p);
1996 _p->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
1997 if (!_p->getStringAttribute("alias"))
1998 _p->setStringAttribute("alias", out->GetName());
1999#else
2000 throw std::runtime_error(
2001 "unsupported addition of unbinned pdf to binned model - please upgrade to at least ROOT 6.24");
2002#endif
2003 _pdf = _p;
2004 }
2005 }
2006
2007 if (!(_pdf->canBeExtended() && p->coefList().empty())) {
2008 // if extended, use an extended binding as the coef
2009 // otherwise e.g. if adding a RooRealSumPdf the stacked histograms will be above the
2010 // actual pdf histogram because the pdf histogram is just normalized down
2011 if (_pdf->canBeExtended()) {
2012 // FIXME: ExtendedBinding needs the obs list passing to it ... should be fixed in RooFit
2013 std::cout << " warning " << _pdf->GetName() << " wont be correctly normalized" << std::endl;
2014 // until then, this will return "1" and so the pdf's histograms wont be normalized properly in relation
2015 // to stacks of its comps
2016 const_cast<RooArgList &>(p->coefList())
2017 .add(*acquireNew<RooExtendedBinding>(TString::Format("%s_extBind", _pdf->GetName()),
2018 TString::Format("Expected Events of %s", _pdf->GetTitle()),
2019 *_pdf));
2020 } else {
2021
2022 // need to create a coefficient for each existing pdf first, like above
2023 for (auto i = p->coefList().size(); i < p->pdfList().size(); i++) {
2024 const_cast<RooArgList &>(p->coefList())
2026 TString::Format("%s_extBind", p->pdfList().at(i)->GetName()),
2027 TString::Format("Expected Events of %s", p->pdfList().at(i)->GetTitle()),
2028 *static_cast<RooAbsPdf *>(p->pdfList().at(i))));
2029 }
2030
2031 const_cast<RooArgList &>(p->coefList()).add(*acquire2<RooAbsArg, RooRealVar>("1", "1", 1));
2032 }
2033 // ensure not in no-coef mode any more
2034 *reinterpret_cast<bool *>(reinterpret_cast<unsigned char *>(p) +
2035 p->Class()->GetDataMemberOffset("_allExtendable")) = false;
2036 *reinterpret_cast<bool *>(reinterpret_cast<unsigned char *>(p) +
2037 p->Class()->GetDataMemberOffset("_haveLastCoef")) = true;
2038 }
2039 const_cast<RooArgList &>(p->pdfList()).add(*_pdf);
2040 sterilize();
2041 return xRooNode(*_pdf, *this);
2042 } else if ((child.get<TH1>() || child.get<RooAbsReal>() ||
2043 (!child.get() && getObject<RooAbsReal>(child.GetName()))) &&
2044 !child.get<RooAbsPdf>()) {
2045 RooRealSumPdf *_pdf = nullptr;
2046 bool tooMany(false);
2047 for (auto &pp : factors()) {
2048 if (auto _p = pp->get<RooRealSumPdf>(); _p) {
2049 if (_pdf) {
2050 _pdf = nullptr;
2051 tooMany = true;
2052 break;
2053 } // more than one!
2054 _pdf = _p;
2055 }
2056 }
2057 if (_pdf) {
2058 return xRooNode(*_pdf, *this).Add(child);
2059 } else if (!tooMany) {
2060 // create a RooRealSumPdf to hold the child
2061 auto _sumpdf = Add(*acquireNew<RooRealSumPdf>(TString::Format("%s_samples", p->GetName()),
2062 TString::Format("%s samples", GetTitle()), RooArgList(),
2063 RooArgList(), true));
2064 _sumpdf.get<RooAbsArg>()->setStringAttribute("alias", "samples");
2065 return _sumpdf.Add(child);
2066 }
2067 }
2068 }
2069
2070 if (auto p = get<RooRealSumPdf>(); p) {
2071 std::shared_ptr<TObject> out;
2072 auto cc = child.fComp;
2073 bool isConverted = (cc != child.convertForAcquisition(*this, sOpt));
2074 if (child.get<RooAbsReal>()) {
2075 out = acquire(child.fComp);
2076 if (std::dynamic_pointer_cast<TH1>(cc) && !TString(cc->GetOption()).Contains("nostyle")) {
2077 xRooNode(out, *this).styles(cc.get()); // transfer style if adding a histogram
2078 }
2079 }
2080 if (!child.fComp && getObject<RooAbsReal>(child.GetName())) {
2081 Info("Add", "Adding existing function %s to %s", child.GetName(), p->GetName());
2082 out = getObject<RooAbsReal>(child.GetName());
2083 }
2084
2085 if (!out && !child.fComp) {
2086 TDirectory::TContext ctx{nullptr}; // No self-registration of histograms to directories
2087 std::shared_ptr<RooAbsArg> _func;
2088 // a null node .. so create either a new RooProduct or RooHistFunc if has observables (or no deps but has
2089 // x-axis)
2090 auto _obs = robs();
2091 if (!_obs.empty() || GetXaxis()) {
2092 if (_obs.empty()) {
2093 // using X axis to construct hist
2094 auto _ax = dynamic_cast<Axis2 *>(GetXaxis());
2095 auto h =
2096 std::make_unique<TH1D>(child.GetName(), child.GetTitle(), _ax->GetNbins(), _ax->binning()->array());
2097 h->GetXaxis()->SetName(TString::Format("%s;%s", _ax->GetParent()->GetName(), _ax->GetName()));
2098 // technically convertForAcquisition has already acquired so no need to re-acquire but should be harmless
2099 _func = std::dynamic_pointer_cast<RooAbsArg>(acquire(xRooNode(*h).convertForAcquisition(*this)));
2100 } else if (_obs.size() == 1) {
2101 // use the single obs to make a TH1D
2102 auto _x = _obs.at(0)->get<RooAbsLValue>();
2103 auto _bnames = _x->getBinningNames();
2104 TString binningName = p->getStringAttribute("binning");
2105 for (auto &b : _bnames) {
2106 if (b == p->GetName()) {
2107 binningName = p->GetName();
2108 break;
2109 }
2110 }
2111 auto h = std::make_unique<TH1D>(child.GetName(), child.GetTitle(), _x->numBins(binningName),
2112 _x->getBinningPtr(binningName)->array());
2113 h->GetXaxis()->SetName(
2114 TString::Format("%s;%s", dynamic_cast<TObject *>(_x)->GetName(), binningName.Data()));
2115 // technically convertForAcquisition has already acquired so no need to re-acquire but should be harmless
2116 _func = std::dynamic_pointer_cast<RooAbsArg>(acquire(xRooNode(*h).convertForAcquisition(*this)));
2117 Info("Add", "Created SimpleDensity factor %s (xaxis=%s) for %s", _func->GetName(), _obs.at(0)->GetName(),
2118 p->GetName());
2119 } else {
2120 throw std::runtime_error("Unsupported creation of new component in SumPdf for this many obs");
2121 }
2122 } else {
2123 _func = acquireNew<RooProduct>(TString::Format("%s_%s", p->GetName(), child.GetName()), child.GetTitle(),
2124 RooArgList());
2125 }
2126 _func->setStringAttribute("alias", child.GetName());
2127 out = _func;
2128 }
2129
2130 if (auto _f = std::dynamic_pointer_cast<RooHistFunc>(
2131 (child.get<RooProduct>()) ? child.factors()[child.GetName()]->fComp : out);
2132 _f) {
2133 // adding a histfunc directly to a sumpdf, should be a density
2134 _f->setAttribute("density");
2135 if (_f->getAttribute("autodensity")) {
2136 // need to divide by bin widths first
2137 for (int i = 0; i < _f->dataHist().numEntries(); i++) {
2138 auto bin_pars = _f->dataHist().get(i);
2139 _f->dataHist().set(*bin_pars, _f->dataHist().weight() / _f->dataHist().binVolume(*bin_pars));
2140 }
2141 _f->setAttribute("autodensity", false);
2142 _f->setValueDirty();
2143 }
2144
2145 // promote the axis vars to observables
2146 // can't use original child as might refer to unacquired deps
2147 for (auto &x : xRooNode("tmp", _f).vars()) {
2148 x->get<RooAbsArg>()->setAttribute("obs");
2149 }
2150 if (isConverted) {
2151 Info("Add", "Created %s factor RooHistFunc::%s for %s",
2152 _f->getAttribute("density") ? "SimpleDensity" : "Simple", _f->GetName(), p->GetName());
2153 }
2154 }
2155
2156 if (auto _p = std::dynamic_pointer_cast<RooAbsPdf>(out); _p) {
2157 // adding a pdf to a RooRealSumPdf will replace it with a RooAddPdf and put the RooRealSumPdf inside that
2158 // if pdf is extended will use in the "no coefficients" state, where the expectedEvents are taking from
2159 // the pdf integrals
2160 TString newName(p->GetName());
2161 newName.ReplaceAll("_samples", "");
2162 newName += "_components";
2163 Warning("Add", "converting samples to components");
2164
2165 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
2166 _p->dependsOn(*static_cast<RooAbsArg *>(_ax->GetParent()))) {
2167
2168 if (auto _boundaries = std::unique_ptr<std::list<double>>(_p->binBoundaries(
2169 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
2170 std::numeric_limits<double>::infinity()));
2171 !_boundaries && _ax->GetNbins() > 0) {
2172#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
2173 Warning("Add", "Adding unbinned pdf %s to binned %s - will wrap with RooBinSamplingPdf(...)",
2174 _p->GetName(), GetName());
2175 _p = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _p->GetName()), _p->GetTitle(),
2176 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *_p);
2177 _p->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
2178 if (!_p->getStringAttribute("alias"))
2179 _p->setStringAttribute("alias", out->GetName());
2180#else
2181 throw std::runtime_error(
2182 "unsupported addition of unbinned pdf to binned model - please upgrade to at least ROOT 6.24");
2183#endif
2184 }
2185 }
2186
2187 // require to be extended to be in coefficient-free mode ...
2188 // otherwise would lose the integral of the sumPdf (can't think of way to have a coef be the integral)
2189 if (!_p->canBeExtended()) {
2190 _p = acquireNew<RooExtendPdf>(TString::Format("%s_extended", _p->GetName()), _p->GetTitle(), *_p,
2191 *acquire2<RooAbsReal, RooRealVar>("1", "1", 1));
2192 }
2193
2194 return *(Replace(*acquireNew<RooAddPdf>(newName, _p->GetTitle(), RooArgList(*p, *_p)))
2195 .browse()[1]); // returns second node.
2196 }
2197
2198 if (auto _f = std::dynamic_pointer_cast<RooAbsReal>(out); _f) {
2199
2200 // todo: if adding a pdf, should actually replace RooRealSumPdf with a RooAddPdf and put
2201 // the sumPdf and *this* pdf inside that pdf
2202 // only exception is the binSamplingPdf below to integrate unbinned functions across bins
2203
2204 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
2205 _f->dependsOn(*static_cast<RooAbsArg *>(_ax->GetParent()))) {
2206
2207 if (auto _boundaries = std::unique_ptr<std::list<double>>(_f->binBoundaries(
2208 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
2209 std::numeric_limits<double>::infinity()));
2210 !_boundaries && _ax->GetNbins() > 0) {
2211#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
2212 Warning(
2213 "Add",
2214 "Adding unbinned function %s to binned %s - will wrap with RooRealSumPdf(RooBinSamplingPdf(...))",
2215 _f->GetName(), GetName());
2216 auto sumPdf = acquireNew<RooRealSumPdf>(TString::Format("%s_pdfWrapper", _f->GetName()), _f->GetTitle(),
2217 *_f, *acquire2<RooAbsArg, RooRealVar>("1", "1", 1), true);
2218 sumPdf->setStringAttribute("alias", _f->getStringAttribute("alias"));
2219 if (!sumPdf->getStringAttribute("alias"))
2220 sumPdf->setStringAttribute("alias", out->GetName());
2221 _f = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _f->GetName()), _f->GetTitle(),
2222 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *sumPdf);
2223 _f->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
2224 if (!_f->getStringAttribute("alias"))
2225 _f->setStringAttribute("alias", out->GetName());
2226#else
2227 throw std::runtime_error(
2228 "unsupported addition of unbinned function to binned model - please upgrade to at least ROOT 6.24");
2229#endif
2230 }
2231 }
2232
2233 const_cast<RooArgList &>(p->coefList()).add(*acquire2<RooAbsArg, RooRealVar>("1", "1", 1));
2234 const_cast<RooArgList &>(p->funcList()).add(*_f);
2235 // inherit binning if we dont have one yet
2236 if (!p->getStringAttribute("binning"))
2237 p->setStringAttribute("binning", _f->getStringAttribute("binning"));
2238
2239 xRooNode _out(_f, *this);
2240 if (auto gf = p->getStringAttribute("global_factors"); gf) {
2241 TStringToken pattern(gf, ";");
2242 while (pattern.NextToken()) {
2243 auto fac = getObject<RooAbsReal>(pattern.Data());
2244 if (!fac) {
2245 throw std::runtime_error(TString::Format("Could not find global factor %s", pattern.Data()));
2246 }
2247 _out.Multiply(fac);
2248 }
2249 }
2250 sterilize();
2251 // clear children for reload and update shared axis
2252 clear();
2253 fXAxis.reset();
2254 p->setStringAttribute("xvar", nullptr);
2255 browse();
2256 return _out;
2257 }
2258 } else if (auto p2 = get<RooProdPdf>(); p2) {
2259 // can "add" to a RooProdPdf provided trying to add a RooAbsReal not a RooAbsPdf and have a zero or 1
2260 // RooRealSumPdf child.convertForAcquisition(*this); - don't convert here because want generated objects named
2261 // after roorealsumpdf
2262 // would like exception is if child is a factory string! - TODO
2263 if (child.get<RooAbsPdf>() || (!child.get() && getObject<RooAbsPdf>(child.GetName()))) {
2264 // can add if 0 or 1 RooAddPdf ....
2265 RooAddPdf *_pdf = nullptr;
2266 bool tooMany(false);
2267 for (auto &pp : factors()) {
2268 if (auto _p = pp->get<RooAddPdf>(); _p) {
2269 if (_pdf) {
2270 _pdf = nullptr;
2271 tooMany = true;
2272 break;
2273 } // more than one!
2274 _pdf = _p;
2275 }
2276 }
2277 if (_pdf) {
2278 return xRooNode(*_pdf, *this).Add(child);
2279 } else if (!tooMany) {
2280 auto out = this->operator[]("components")->Add(child);
2281 return out;
2282 }
2283 } else if ((child.get<TH1>() || child.get<RooAbsReal>() ||
2284 (!child.get() && getObject<RooAbsReal>(child.GetName()))) &&
2285 !child.get<RooAbsPdf>()) {
2286 RooRealSumPdf *_pdf = nullptr;
2287 RooAddPdf *_backup = nullptr;
2288 bool tooMany(false);
2289 for (auto &pp : factors()) {
2290 if (auto _p = pp->get<RooRealSumPdf>(); _p) {
2291 if (_pdf) {
2292 _pdf = nullptr;
2293 tooMany = true;
2294 break;
2295 } // more than one!
2296 _pdf = _p;
2297 } else if (auto _p2 = pp->get<RooAddPdf>(); _p2) {
2298 _backup = _p2;
2299 for (auto &_pdfa : pp->components()) {
2300 if (auto _p3 = _pdfa->get<RooRealSumPdf>(); _p3) {
2301 if (_pdf) {
2302 _pdf = nullptr;
2303 tooMany = true;
2304 break;
2305 } // more than one!
2306 _pdf = _p3;
2307 }
2308 }
2309 }
2310 }
2311 if (_pdf) {
2312 return xRooNode(*_pdf, *this).Add(child);
2313 } else if (_backup) {
2314 // added *INSIDE* the addPdf -- will create a RooRealSumPdf to hold it
2315 return xRooNode(*_backup, *this).Add(child);
2316 } else if (!tooMany) {
2317 auto out = this->operator[]("samples")->Add(child);
2318 // clear our x-axis to re-evaluate
2319 fXAxis.reset();
2320 p2->setStringAttribute("xvar", nullptr);
2321 return out;
2322 }
2323 }
2324 } else if (auto s = get<RooSimultaneous>(); s) {
2325
2326 // adding to a simultaneous means adding a bin
2327 return bins().Add(child);
2328
2329 // if the child is a RooAbsPdf can just add it as a new channel using name of pdf as the channel name
2330 // if child is a histogram, will create a RooProdPdf
2331
2332 } else if (auto w = get<RooWorkspace>(); w) {
2333 child.convertForAcquisition(
2334 *this, child.get() ? "" : "func" /* if child is a string, allow it to be passed to factory */);
2335 if (child.get()) {
2336 if (auto _d = child.get<RooAbsData>()) {
2337 // don't use acquire method to import, because that adds datasets as Embeddded
2338 if (!w->import(*_d)) {
2339 // should upgrade vars with any obs from the dataset
2340 if (_d->get()) {
2341 std::unique_ptr<RooAbsCollection>(w->allVars().selectCommon(*_d->get()))->setAttribAll("obs");
2342 }
2343 if (_d->getGlobalObservables()) {
2344 std::unique_ptr<RooAbsCollection> globs(w->allVars().selectCommon(*_d->getGlobalObservables()));
2345 globs->setAttribAll("obs");
2346 globs->setAttribAll("global");
2347 }
2348 return xRooNode(child.GetName(), *w->data(child.GetName()), *this);
2349 } else {
2350 throw std::runtime_error(
2351 TString::Format("Could not import dataset %s into workspace %s", child.GetName(), w->GetName())
2352 .Data());
2353 }
2354 } else {
2355 auto out = acquire(child.fComp);
2356 if (out)
2357 return xRooNode(child.GetName(), out, *this);
2358 }
2359 }
2360
2361 if (!child.empty() || child.fFolder == "!pdfs") {
2362 // create a RooSimultaneous using the children as the channels
2363 // children either have "=" in name if specifying channel cat name or otherwise assume
2364 std::string catName = "channelCat";
2365 if (!child.empty()) {
2366 if (TString ss = child.at(0)->GetName(); ss.Contains("=")) {
2367 catName = ss(0, ss.Index('='));
2368 }
2369 }
2370 auto _cat = acquire<RooCategory>(catName.c_str(), catName.c_str());
2371 _cat->setAttribute("obs");
2372 auto out = acquireNew<RooSimultaneous>(child.GetName(), child.GetTitle(), *_cat);
2373 Info("Add", "Created pdf RooSimultaneous::%s in workspace %s", out->GetName(), w->GetName());
2374 return xRooNode(out, *this);
2375 }
2376 } else if (auto coll = get<RooAbsCollection>(); coll && child.get<RooAbsArg>()) {
2377 if (coll->isOwning()) {
2378 coll->addOwned(*static_cast<RooAbsArg *>(child.get<RooAbsArg>()->Clone()));
2379 } else if (child.ws() != ws()) {
2380 coll->add(*static_cast<RooAbsArg *>(acquire(child.fComp).get()));
2381 } else {
2382 coll->add(*child.get<RooAbsArg>());
2383 }
2384 return xRooNode(child.GetName(), *coll->find(child.GetName()), *this);
2385 }
2386
2387 if (sOpt == "pdf") {
2388 // can only add a pdf to a workspace
2389 if (get<RooWorkspace>()) {
2390 const_cast<xRooNode &>(child).fFolder = "!pdfs";
2391 return Add(child);
2392 }
2393 } else if (sOpt == "channel") {
2394 // can add to a model or to a workspace (creates a RooProdPdf either way)
2395 if (get<RooSimultaneous>()) {
2396 return Vary(child);
2397 } else if (get<RooWorkspace>()) {
2398 std::shared_ptr<TObject> out;
2399 child.convertForAcquisition(*this);
2400 if (child.get<RooAbsPdf>()) {
2401 out = acquire(child.fComp);
2402 } else if (!child.fComp) {
2403 out = acquireNew<RooProdPdf>(child.GetName(),
2404 (strlen(child.GetTitle())) ? child.GetTitle() : child.GetName(), RooArgList());
2405 Info("Add", "Created channel RooProdPdf::%s in workspace %s", out->GetName(), get()->GetName());
2406 }
2407 return xRooNode(out, *this);
2408 }
2409 } else if (sOpt == "sample" || sOpt == "func") {
2410 if (get<RooProdPdf>()) {
2411 auto _mainChild = mainChild();
2412 if (_mainChild.get<RooRealSumPdf>()) {
2413 return _mainChild.Add(child, sOpt == "func" ? "func" : "");
2414 } else {
2415 return (*this)["samples"]->Add(child, sOpt == "func" ? "func" : "");
2416 }
2417 }
2418 } else if (sOpt == "dataset") {
2419 if (get<RooWorkspace>()) {
2420 // const_cast<xRooNode&>(child).fFolder = "!datasets";return Add(child);
2421 return (*this).datasets().Add(child);
2422 }
2423 }
2424
2425 if (considerType) {
2426
2427 // interpret 'adding' here as dependent on the object type ...
2428 if (get<RooSimultaneous>()) {
2429 return bins().Add(child);
2430 } else if (TString(child.GetName()).Contains('=')) {
2431 return variations().Add(child);
2432 } else if (get<RooProduct>() || get<RooProdPdf>()) {
2433 return factors().Add(child);
2434 }
2435 }
2436
2437 // Nov 2022 - removed ability to add placeholders ... could bring back if rediscover need for them
2438 // if (!child.get() && child.empty() && strlen(child.GetName())) {
2439 // // can add a 'placeholder' node, note it will be deleted at the next browse
2440 // xRooNode out(child.GetName(),nullptr,*this);
2441 // out.SetTitle(child.GetTitle());
2442 // emplace_back(std::make_shared<xRooNode>(out));
2443 // // update the parent in the out node so that it's copy of the parent knows it has itself in it
2444 // // actually maybe not want this :-/
2445 // //out.fParent = std::make_shared<Node2>(*this);
2446 // for(auto o : *gROOT->GetListOfBrowsers()) {
2447 // if(auto b = dynamic_cast<TBrowser*>(o); b && b->GetBrowserImp()){
2448 // if(auto _b = dynamic_cast<TGFileBrowser*>(
2449 // dynamic_cast<TRootBrowser*>(b->GetBrowserImp())->fActBrowser ); _b) {
2450 // auto _root = _b->fRootDir;
2451 // if (!_root) _root = _b->fListTree->GetFirstItem();
2452 // if (auto item = _b->fListTree->FindItemByObj(_root,this); item) {
2453 // _b->fListTree->AddItem(item,back()->GetName(),back().get());
2454 // }
2455 // }
2456 // }
2457 // }
2458 // return out;
2459 // }
2460
2461 throw std::runtime_error(TString::Format("Cannot add %s to %s", child.GetName(), GetName()));
2462}
2463
2464std::string xRooNode::GetPath() const
2465{
2466 if (!fParent)
2467 return GetName();
2468 return fParent->GetPath() + "/" + GetName();
2469}
2470
2472{
2473 // std::cout << "deleting " << GetPath() << std::endl;
2474}
2475
2477{
2478 if (auto a = get<RooAbsArg>()) {
2479 a->setAttribute("hidden", set);
2480 // if(auto item = GetTreeItem(nullptr); item) {
2481 // if(set) item->SetColor(kRed);
2482 // else item->ClearColor();
2483 // }
2484 }
2485}
2487{
2488 auto a = get<RooAbsArg>();
2489 if (a)
2490 return a->getAttribute("hidden");
2491 return false;
2492}
2493
2495{
2496
2497 if (get() == rhs.get()) {
2498 // nothing to do because objects are identical
2499 return *this;
2500 }
2501
2502 if (auto lhsa = get<RooAbsArg>(), rhsa = rhs.get<RooAbsArg>(); lhsa && rhsa && lhsa->isIdentical(*rhsa)) {
2503 return *this;
2504 }
2505
2506 if (get<RooWorkspace>() && rhs.get<RooWorkspace>()) {
2507
2508 // report which top-level pdfs will be combined
2509 std::set<std::string> pdfs;
2510 for (auto &c : rhs.components()) {
2511 if ((*this)["pdfs"]->find(c->GetName())) {
2512 pdfs.insert(c->GetName());
2513 }
2514 }
2515 if (pdfs.empty()) {
2516 Warning("Combine", "No pdfs will be combined. Please check and/or rename pdfs to match");
2517 } else {
2518 std::stringstream s;
2519 for (auto &p : pdfs)
2520 s << p << ",";
2521 Info("Combine", "pdfs that will be combined: %s", s.str().c_str());
2522 }
2523
2524 std::set<std::string> _np;
2525 auto mynp = np();
2526 for (auto &c : rhs.np()) {
2527 if (mynp.find(c->GetName())) {
2528 _np.insert(c->GetName());
2529 }
2530 }
2531 if (_np.empty()) {
2532 Warning("Combine", "No correlated np");
2533 } else {
2534 std::stringstream s;
2535 for (auto &p : _np)
2536 s << p << ",";
2537 Info("Combine", "np that will be shared (correlated): %s", s.str().c_str());
2538 }
2539 std::set<std::string> _poi;
2540 auto mypoi = poi();
2541 for (auto &c : rhs.poi()) {
2542 if (mypoi.find(c->GetName())) {
2543 _poi.insert(c->GetName());
2544 }
2545 }
2546 if (_poi.empty()) {
2547 Warning("Combine", "No correlated poi");
2548 } else {
2549 std::stringstream s;
2550 for (auto &p : _poi)
2551 s << p << ",";
2552 Info("Combine", "poi that will be shared (correlated): %s", s.str().c_str());
2553 }
2554
2555 // TODO: Check for derived components that have matching names and aren't top-level pdfs
2556 //
2557 // const auto comps = get<RooWorkspace>()->components();
2558 //
2559 // std::set<std::string> leafs;
2560 //
2561 // for(auto& c : rhs.get<RooWorkspace>()->components()) {
2562 // if(comps.find(c->GetName())) {
2563 //
2564 // }
2565 // }
2566 }
2567
2568 // combine components, factors, and variations ... when there is a name clash will combine on that object
2569 for (auto &c : rhs.components()) {
2570 if (get<RooWorkspace>() &&
2571 (c->fFolder == "!scratch" || c->fFolder == "!sets" || c->fFolder == "!snapshots" || c->fFolder == "!models"))
2572 continue;
2573 if (auto _c = components().find(c->GetName()); _c) {
2574 if (!silent) {
2575 Info("Combine", "Combining %s into %s", c->GetPath().c_str(), _c->GetPath().c_str());
2576 }
2577 _c->Combine(*c, true);
2578 } else {
2579 try {
2580 if (!silent) {
2581 Info("Combine", "Adding %s into %s", c->GetPath().c_str(), GetPath().c_str());
2582 }
2583 Add(*c);
2584 } catch (std::exception &e) {
2585 Warning("Combine", "Could not combine %s into %s", c->GetPath().c_str(), GetPath().c_str());
2586 }
2587 }
2588 }
2589
2590 if (!get<RooWorkspace>()) { // don't combine factors of a workspace
2591 for (auto &f : rhs.factors()) {
2592 if (auto _f = factors().find(f->GetName()); _f) {
2593 if (!silent) {
2594 Info("Combine", "Combining %s into %s", f->GetPath().c_str(), _f->GetPath().c_str());
2595 }
2596 _f->Combine(*f, true);
2597 } else {
2598 if (!silent) {
2599 Info("Combine", "Multiplying %s into %s", f->GetPath().c_str(), GetPath().c_str());
2600 }
2601 Multiply(*f);
2602 }
2603 }
2604 } else {
2605 // todo: go back through components, doing sets, snapshots, models, ....
2606 // do after import of pdfs etc so that can acquire the copies from the workspace
2607 for (auto &c : rhs.components()) {
2608 if (c->fFolder == "!sets") {
2609 if (components().find(c->GetName())) {
2610 Info("Combine", "Extending set %s", c->GetName());
2611 get<RooWorkspace>()->extendSet(c->GetName(), c->get<RooAbsCollection>()->contentsString().c_str());
2612 } else {
2613 Info("Combine", "Defining set %s", c->GetName());
2614 get<RooWorkspace>()->defineSet(c->GetName(), c->get<RooAbsCollection>()->contentsString().c_str());
2615 }
2616 }
2617 }
2618
2619 // also transfer datasets
2620 for (auto &ds : rhs.datasets()) {
2621 if (auto _ds = datasets().find(ds->GetName()); _ds) {
2622 if (!silent) {
2623 Info("Combine", "Combining %s into %s", ds->GetPath().c_str(), _ds->GetPath().c_str());
2624 }
2625 _ds->Add(*ds);
2626 } else {
2627 if (!silent) {
2628 Info("Combine", "Adding %s into %s", ds->GetPath().c_str(), GetPath().c_str());
2629 }
2630 datasets().Add(*ds);
2631 }
2632 }
2633 }
2634
2635 for (auto &v : rhs.variations()) {
2636 if (auto _v = variations().find(v->GetName()); _v) {
2637 if (!silent) {
2638 Info("Combine", "Combining variation %s into %s", v->GetPath().c_str(), _v->GetPath().c_str());
2639 }
2640 _v->Combine(*v, true);
2641 } else {
2642 if (!silent) {
2643 Info("Combine", "Varying %s into %s", v->GetPath().c_str(), GetPath().c_str());
2644 }
2645 Vary(*v);
2646 }
2647 }
2648
2649 if (get<RooSimultaneous>()) {
2650 // combine bins (channels) ... special case, never done silently
2651 for (auto &b : rhs.bins()) {
2652 if (auto _b = bins().find(b->GetName()); _b) {
2653 Info("Combine", "Combining %s into %s", b->GetPath().c_str(), _b->GetPath().c_str());
2654 _b->Combine(*b, true);
2655 } else {
2656 Info("Combine", "Extending with %s into %s", b->GetPath().c_str(), GetPath().c_str());
2657 Vary(*b); // extending channels currently done through Vary method
2658 }
2659 }
2660 }
2661
2662 // todo: Should also transfer over binnings of observables
2663
2664 return *this;
2665}
2666
2667xRooNode xRooNode::shallowCopy(const std::string &name, std::shared_ptr<xRooNode> parent)
2668{
2669 xRooNode out(name.c_str(), nullptr,
2670 parent /*? parent : fParent -- was passing fParent for getObject benefit before fProvider concept*/);
2671 // if(!parent) out.fAcquirer = true;
2672 if (!parent)
2673 out.fProvider = fParent;
2674
2675 auto o = get();
2676 if (!o) {
2677 return out;
2678 }
2679
2680 if (auto s = get<RooSimultaneous>(); s) {
2681 auto chans = bins();
2682 if (!chans.empty()) {
2683 // create a new RooSimultaneous with shallow copies of each channel
2684
2685 std::shared_ptr<RooSimultaneous> pdf = out.acquire<RooSimultaneous>(
2686 name.c_str(), o->GetTitle(), const_cast<RooAbsCategoryLValue &>(s->indexCat()));
2687
2688 for (auto &c : chans) {
2689 TString cName(c->GetName());
2690 cName = cName(cName.Index('=') + 1, cName.Length());
2691 // by passing out as the parent, will ensure out acquires everything created
2692 auto c_copy =
2693 c->shallowCopy(name + "_" + c->get()->GetName(), std::shared_ptr<xRooNode>(&out, [](xRooNode *) {}));
2694 pdf->addPdf(*dynamic_cast<RooAbsPdf *>(c_copy.get()), cName);
2695 }
2696 out.fComp = pdf;
2697 return out;
2698 }
2699 } else if (auto p = dynamic_cast<RooProdPdf *>(o); p) {
2700 // main pdf will be copied too
2701 std::shared_ptr<RooProdPdf> pdf =
2702 std::dynamic_pointer_cast<RooProdPdf>(out.acquire(std::shared_ptr<TObject>(p->Clone(/*name.c_str()*/)), false,
2703 true)); // use clone to copy all attributes etc too
2704 auto main = mainChild();
2705 if (main) {
2706 auto newMain =
2707 std::dynamic_pointer_cast<RooAbsArg>(out.acquire(std::shared_ptr<TObject>(main->Clone()), false, true));
2708 std::cout << newMain << " " << newMain->GetName() << std::endl;
2709 // pdf->replaceServer(*pdf->pdfList().find(main->GetName()), *newMain, true, true);
2710 // const_cast<RooArgList&>(pdf->pdfList()).replace(*pdf->pdfList().find(main->GetName()), *newMain);
2711 pdf->redirectServers(RooArgList(*newMain));
2712 }
2713 out.fComp = pdf;
2714 out.sterilize();
2715 return out;
2716 }
2717
2718 return out;
2719}
2720
2722{
2723 static std::unique_ptr<cout_redirect> capture;
2724 std::string captureStr;
2725 bool doCapture = false;
2726 if (!capture && gROOT->FromPopUp()) { // FromPopUp means user executed from the context menu
2727 capture = std::make_unique<cout_redirect>(captureStr);
2728 doCapture = true;
2729 }
2730
2731 TString sOpt(opt);
2732 int depth = 0;
2733 if (sOpt.Contains("depth=")) {
2734 depth = TString(sOpt(sOpt.Index("depth=") + 6, sOpt.Length())).Atoi();
2735 sOpt.ReplaceAll(TString::Format("depth=%d", depth), "");
2736 }
2737 int indent = 0;
2738 if (sOpt.Contains("indent=")) {
2739 indent = TString(sOpt(sOpt.Index("indent=") + 7, sOpt.Length())).Atoi();
2740 sOpt.ReplaceAll(TString::Format("indent=%d", indent), "");
2741 }
2742 bool _more = sOpt.Contains("m");
2743 if (_more)
2744 sOpt.Replace(sOpt.Index("m"), 1, "");
2745 if (sOpt != "")
2746 _more = true;
2747
2748 if (indent == 0) { // only print self if not indenting (will already be printed above if tree traverse)
2749 std::cout << GetPath();
2750 if (get() && get() != this) {
2751 std::cout << ": ";
2752 if (_more || (get<RooAbsArg>() && get<RooAbsArg>()->isFundamental()) || get<RooConstVar>() ||
2754 auto _deps = coords(false).argList(); // want to revert coords after print
2755 auto _snap = std::unique_ptr<RooAbsCollection>(_deps.snapshot());
2756 coords(); // move to coords before printing (in case this matters)
2757 get()->Print(sOpt);
2758 if (auto _fr = get<RooFitResult>(); _fr && dynamic_cast<RooStringVar *>(_fr->constPars().find(".log"))) {
2759 std::cout << "Minimization Logs:" << std::endl;
2760 std::cout << dynamic_cast<RooStringVar *>(_fr->constPars().find(".log"))->getVal() << std::endl;
2761 }
2762 _deps.assignValueOnly(*_snap);
2763 // std::cout << std::endl;
2764 } else {
2765 TString _suffix = "";
2766 if (auto _type = GetNodeType(); strlen(_type)) {
2767 // decided not to show const values until figure out how to update if value changes
2768 /*if (TString(_type)=="Const") _name += TString::Format("
2769 [%s=%g]",_type,v->get<RooConstVar>()->getVal()); else*/
2770 _suffix += TString::Format(" [%s]", _type);
2771 }
2772 if (auto fv = get<RooFormulaVar>()) {
2773 TString formu = TString::Format(" [%s]", fv->expression());
2774 for (size_t i = 0; i < fv->dependents().size(); i++) {
2775 formu.ReplaceAll(TString::Format("x[%zu]", i), fv->dependents()[i].GetName());
2776 }
2777 _suffix += formu;
2778 } else if (auto gv = get<RooGenericPdf>()) {
2779 TString formu = TString::Format(" [%s]", gv->expression());
2780 for (size_t i = 0; i < gv->dependents().size(); i++) {
2781 formu.ReplaceAll(TString::Format("x[%zu]", i), gv->dependents()[i].GetName());
2782 }
2783 _suffix += formu;
2784 }
2785 std::cout << get()->ClassName() << "::" << get()->GetName() << _suffix.Data() << std::endl;
2786 }
2787
2788 } else if (!get()) {
2789 std::cout << std::endl;
2790 }
2791 }
2792 const_cast<xRooNode *>(this)->browse();
2793 std::vector<std::string> folderNames;
2794 for (auto &k : *this) {
2795 if (std::find(folderNames.begin(), folderNames.end(), k->fFolder) == folderNames.end()) {
2796 folderNames.push_back(k->fFolder);
2797 }
2798 }
2799 for (auto &f : folderNames) {
2800 int i = 0;
2801 int iindent = indent;
2802 if (!f.empty()) {
2803 for (int j = 0; j < indent; j++)
2804 std::cout << " ";
2805 std::cout << f << std::endl;
2806 iindent += 1;
2807 }
2808 for (auto &k : *this) {
2809 if (k->fFolder != f) {
2810 i++;
2811 continue;
2812 }
2813 for (int j = 0; j < iindent; j++)
2814 std::cout << " ";
2815 std::cout << i++ << ") " << k->GetName() << " : ";
2816 if (k->get()) {
2817 if (_more || (k->get<RooAbsArg>() && k->get<RooAbsArg>()->isFundamental()) || k->get<RooConstVar>() ||
2818 k->get<RooAbsData>() /*|| k->get<RooProduct>()*/) {
2819 auto _deps = k->coords(false).argList();
2820 auto _snap = std::unique_ptr<RooAbsCollection>(_deps.snapshot());
2821 k->coords(); // move to coords before printing (in case this matters)
2822 k->get()->Print(sOpt); // assumes finishes with an endl
2823 _deps.assignValueOnly(*_snap);
2824 } else {
2825 TString _suffix = "";
2826 if (auto _type = k->GetNodeType(); strlen(_type)) {
2827 // decided not to show const values until figure out how to update if value changes
2828 /*if (TString(_type)=="Const") _name += TString::Format("
2829 [%s=%g]",_type,v->get<RooConstVar>()->getVal()); else*/
2830 _suffix += TString::Format(" [%s]", _type);
2831 }
2832 if (auto fv = k->get<RooFormulaVar>()) {
2833 TString formu = TString::Format(" [%s]", fv->expression());
2834 for (size_t j = 0; j < fv->dependents().size(); j++) {
2835 formu.ReplaceAll(TString::Format("x[%zu]", j), fv->dependents()[j].GetName());
2836 }
2837 _suffix += formu;
2838 } else if (auto gv = k->get<RooGenericPdf>()) {
2839 TString formu = TString::Format(" [%s]", gv->expression());
2840 for (size_t j = 0; j < gv->dependents().size(); j++) {
2841 formu.ReplaceAll(TString::Format("x[%zu]", j), gv->dependents()[j].GetName());
2842 }
2843 _suffix += formu;
2844 }
2845 std::cout << k->get()->ClassName() << "::" << k->get()->GetName() << _suffix.Data() << std::endl;
2846 }
2847 if (depth != 0) {
2848 k->Print(sOpt + TString::Format("depth=%dindent=%d", depth - 1, iindent + 1));
2849 }
2850 } else
2851 std::cout << " NULL " << std::endl;
2852 }
2853 }
2854 if (doCapture) {
2855 capture.reset(); // no captureStr has the string to display
2856 // inject line breaks to avoid msgbox being too wide
2857 size_t lastBreak = 0;
2858 std::string captureStrWithBreaks;
2859 for (size_t i = 0; i < captureStr.size(); i++) {
2861 if (captureStr[i] == '\n') {
2862 lastBreak = i;
2863 }
2864 if (i - lastBreak > 150) {
2865 captureStrWithBreaks += '\n';
2866 lastBreak = i;
2867 }
2868 }
2869 const TGWindow *w =
2870 (gROOT->GetListOfBrowsers()->At(0))
2871 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
2872 : gClient->GetRoot();
2873 new TGMsgBox(gClient->GetRoot(), w, GetName(),
2874 captureStrWithBreaks.c_str()); //,nullptr,kMBDismiss,nullptr,kVerticalFrame,kTextLeft|kTextCenterY);
2875 }
2876}
2877
2879{
2880 if (!child.get()) {
2881
2882 if (auto v = get<RooRealVar>(); v) {
2883
2884 TString constrType = child.GetName();
2885 double mean = std::numeric_limits<double>::quiet_NaN();
2886 double sigma = mean;
2887 if (constrType.BeginsWith("gaussian(")) {
2888 // extract the mean and stddev parameters
2889 // if only one given, it is the stddev
2890 if (constrType.Contains(",")) {
2891 mean = TString(constrType(9, constrType.Index(',') - 9)).Atof();
2892 sigma = TString(constrType(constrType.Index(',') + 1, constrType.Index(')') - constrType.Index(',') + 1))
2893 .Atof();
2894 } else {
2895 mean = std::numeric_limits<double>::quiet_NaN(); // will use the var current value below to set mean
2896 sigma = TString(constrType(9, constrType.Index(')') - 9)).Atof();
2897 }
2898 constrType = "normal";
2899 } else if (constrType == "normal") {
2900 mean = 0;
2901 sigma = 1;
2902 } else if (constrType == "gaussian") {
2903 // extract parameters from the variable
2904 // use current value and error on v as constraint
2905 if (!v->hasError())
2906 throw std::runtime_error("No error on parameter for gaussian constraint");
2907 sigma = v->getError();
2908 mean = v->getVal();
2909 constrType = "normal";
2910 } else if (constrType == "poisson") {
2911 if (!v->hasError())
2912 throw std::runtime_error("No error on parameter for poisson constraint");
2913 mean = 1;
2914 sigma = pow(v->getVal() / v->getError(), 2);
2915 }
2916
2917 if (constrType == "poisson") {
2918 // use current value and error on v as constraint
2919 double tau_val = sigma;
2920 auto globs = acquire<RooRealVar>(Form("globs_%s", v->GetName()), Form("globs_%s", v->GetName()),
2921 v->getVal() * tau_val, (v->getVal() - 5 * v->getError()) * tau_val,
2922 (v->getVal() + 5 * v->getError()) * tau_val);
2923 globs->setConstant();
2924 globs->setAttribute("obs");
2925 globs->setAttribute("global");
2926 globs->setStringAttribute("nominal", TString::Format("%f", tau_val));
2927 auto tau = acquireNew<RooConstVar>(TString::Format("tau_%s", v->GetName()), "", tau_val);
2929 Form("pois_%s", v->GetName()), TString::Format("Poisson Constraint of %s", v->GetTitle()), *globs,
2930 *acquireNew<RooProduct>(TString::Format("mean_%s", v->GetName()),
2931 TString::Format("Poisson Constraint of %s", globs->GetTitle()),
2932 RooArgList(*v, *tau)),
2933 true /* no rounding */);
2934
2935 auto out = Constrain(xRooNode(Form("pois_%s", GetName()), constr));
2936 if (!v->hasError())
2937 v->setError(mean / sqrt(tau_val)); // if v doesnt have an uncert, will put one on it now
2938 Info("Constrain", "Added poisson constraint pdf RooPoisson::%s (tau=%g) for %s", out->GetName(), tau_val,
2939 GetName());
2940 return out;
2941 } else if (constrType == "normal") {
2942
2943 auto globs = acquire<RooRealVar>(Form("globs_%s", v->GetName()), Form("globs_%s", v->GetName()), mean,
2944 mean - 10 * sigma, mean + 10 * sigma);
2945 globs->setAttribute("obs");
2946 globs->setAttribute("global");
2947 globs->setConstant();
2948
2949 globs->setStringAttribute("nominal", TString::Format("%f", mean));
2951 Form("gaus_%s", v->GetName()), TString::Format("Gaussian Constraint of %s", v->GetTitle()), *globs, *v,
2952 *acquireNew<RooConstVar>(TString::Format("sigma_%s", v->GetName()), "", sigma));
2953 auto out = Constrain(xRooNode(Form("gaus_%s", GetName()), constr));
2954 if (!v->hasError())
2955 v->setError(sigma); // if v doesnt have an uncert, will put one on it now
2956 Info("Constrain", "Added gaussian constraint pdf RooGaussian::%s (mean=%g,sigma=%g) for %s", out->GetName(),
2957 mean, sigma, GetName());
2958 return out;
2959 }
2960 }
2961 } else if (auto p = child.get<RooAbsPdf>(); p) {
2962
2963 auto _me = get<RooAbsArg>();
2964 if (!_me) {
2965 throw std::runtime_error("Cannot constrain non arg");
2966 }
2967
2968 if (!p->dependsOn(*_me)) {
2969 throw std::runtime_error("Constraint does not depend on constrainee");
2970 }
2971
2972 // find a parent that can swallow this pdf ... either a RooProdPdf or a RooWorkspace
2973 auto x = fParent;
2974 while (x && !x->get<RooProdPdf>() && !x->get<RooSimultaneous>() && !x->get<RooWorkspace>()) {
2975 x = x->fParent;
2976 }
2977 if (!x) {
2978 throw std::runtime_error("Nowhere to put constraint");
2979 }
2980 // get datasets of the swallower, and add glob to any globs lists
2981 auto childGlobs = child.globs();
2982 if (!childGlobs.empty()) {
2983 for (auto d : x->datasets()) {
2984 if (auto globs = d->get<RooAbsData>()->getGlobalObservables()) {
2986 newGlobs.add(*childGlobs.get<RooArgList>());
2987 d->get<RooAbsData>()->setGlobalObservables(newGlobs);
2988 }
2989 }
2990 // also add to the workspaces globalObservables lists
2991 if (x->ws()) {
2992 for (auto &[k, v] : GETWSSETS(x->ws())) {
2993 if (k == "globalObservables" || TString(k).EndsWith("_GlobalObservables")) {
2994 const_cast<RooArgSet &>(v).add(*childGlobs.get<RooArgList>());
2995 }
2996 }
2997 }
2998 }
2999 if (auto s = x->get<RooSimultaneous>(); s) {
3000 // put into every channel that features parameter
3001 x->browse();
3002 for (auto &c : *x) {
3003 if (auto a = c->get<RooAbsArg>(); a->dependsOn(*_me))
3004 c->Multiply(child);
3005 }
3006 return child;
3007 } else if (x->get<RooProdPdf>()) {
3008 return x->Multiply(child);
3009 } else {
3010 return x->Add(child, "+");
3011 }
3012 }
3013
3014 throw std::runtime_error(TString::Format("Cannot constrain %s", GetName()));
3015}
3016
3018{
3019
3020 class AutoUpdater {
3021 public:
3022 AutoUpdater(xRooNode &_n) : n(_n) {}
3023 ~AutoUpdater() { n.browse(); }
3024 xRooNode &n;
3025 };
3026 AutoUpdater xxx(*this);
3027
3028 if (fBinNumber != -1) {
3029 // scaling a bin ...
3030 if (child.get<RooAbsReal>()) { // if not child then let fall through to create a child and call self again below
3031 // doing a bin-multiplication .. the parent should have a ParamHistFunc called binFactors
3032 // if it doesn't then create one
3033 auto o = std::dynamic_pointer_cast<RooAbsReal>(acquire(child.fComp));
3034
3035 // get binFactor unless parent is a ParamHistFunc already ...
3036
3037 auto binFactors = (fParent->get<ParamHistFunc>()) ? fParent : fParent->factors().find("binFactors");
3038
3039 // it can happen in a loop over bins() that another node has moved fParent inside a product
3040 // so check for fParent having a client with the ORIGNAME:<name> attribute
3041 if (!binFactors && fParent->get<RooAbsArg>()) {
3042 for (auto c : fParent->get<RooAbsArg>()->clients()) {
3043 if (c->IsA() == RooProduct::Class() &&
3044 c->getAttribute(TString::Format("ORIGNAME:%s", fParent->get()->GetName()))) {
3045 // try getting binFactors out of this
3046 binFactors = xRooNode(*c).factors().find("binFactors");
3047 break;
3048 }
3049 }
3050 }
3051
3052 if (!binFactors) {
3053 fParent
3054 ->Multiply(TString::Format("%s_binFactors",
3055 (fParent->mainChild().get())
3056 ? fParent->mainChild()->GetName()
3057 : (fParent->get() ? fParent->get()->GetName() : fParent->GetName()))
3058 .Data(),
3059 "blankshape")
3060 .SetName("binFactors"); // creates ParamHistFunc with all pars = 1 (shared const)
3061 binFactors = fParent->factors().find("binFactors");
3062 if (!binFactors) {
3063 throw std::runtime_error(
3064 TString::Format("Could not create binFactors in parent %s", fParent->GetName()));
3065 }
3066 // auto phf = binFactors->get<ParamHistFunc>();
3067
3068 // create RooProducts for all the bins ... so that added factors don't affect selves
3069 int i = 1;
3070 for (auto &b : binFactors->bins()) {
3071 auto p = acquireNew<RooProduct>(TString::Format("%s_bin%d", binFactors->get()->GetName(), i),
3072 TString::Format("binFactors of bin %d", i), RooArgList());
3073 p->setStringAttribute("alias", TString::Format("%s=%g", binFactors->GetXaxis()->GetParent()->GetName(),
3074 binFactors->GetXaxis()->GetBinCenter(i)));
3075 b->Multiply(*p);
3076 i++;
3077 }
3078 }
3079 // then scale the relevant bin ... if the relevant bin is a "1" then just drop in our factor (inside a
3080 // RooProduct though, to avoid it getting modified by subsequent multiplies)
3081 auto _bin = binFactors->bins().at(fBinNumber - 1);
3082 if (auto phf = binFactors->get<ParamHistFunc>(); phf && _bin) {
3083#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3084 RooArgList &pSet = phf->_paramSet;
3085#else
3086 RooArgList &pSet = const_cast<RooArgList &>(phf->paramList());
3087#endif
3088 if (strcmp(_bin->GetName(), "1") == 0) {
3090 for (std::size_t i = 0; i < pSet.size(); i++) {
3091 if (int(i) != fBinNumber - 1) {
3092 all.add(*pSet.at(i));
3093 } else {
3094 all.add(*o);
3095 }
3096 }
3097 pSet.removeAll();
3098 pSet.add(all);
3099 } else {
3100 _bin->fBinNumber = -1; // to avoid infinite loop
3101 return _bin->Multiply(child, opt);
3102 }
3103 // } else {else if(_bin->get<RooProduct>()) {
3104 // // multiply the element which will just add it as a factor in the rooproduct
3105 // return _bin->Multiply(child,opt);
3106 // } else {
3107 // // not a rooproduct in this bin yet ... so need to replace with a rooproduct and
3108 // multiply that
3109 // // this avoids the undesired behaviour of shared binFactors getting all impacted by
3110 // mulitplies RooArgList all; auto new_p =
3111 // acquireNew<RooProduct>(TString::Format("%s_bin%d",binFactors->get()->GetName(),fBinNumber),TString::Format("binFactors
3112 // of bin %d",fBinNumber),RooArgList(*_bin->get<RooAbsArg>()));
3113 // new_p->setStringAttribute("alias","")
3114 // for (int i = 0; i < phf->_paramSet.size(); i++) {
3115 // if (i != fBinNumber - 1) all.add(*phf->_paramSet.at(i));
3116 // else all.add(*new_p);
3117 // }
3118 // phf->_paramSet.removeAll();
3119 // phf->_paramSet.add(all);
3120 // // now multiply that bin having converted it to RooProduct
3121 // return binFactors->bins().at(fBinNumber - 1)->Multiply(child,opt);
3122 // }
3123 }
3124 return xRooNode(*o, binFactors);
3125 }
3126 } else if (!get() && fParent) {
3127 // try to 'create' object based on parentage
3128 // add child as a temporary child to help with decision making
3129 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
3130 try {
3131 fComp = fParent->Add(*this, "+").fComp;
3132 } catch (...) {
3133 resize(size() - 1);
3134 std::rethrow_exception(std::current_exception());
3135 }
3136 resize(size() - 1); // remove the temporarily added node
3137 }
3138
3139 if (!child.get()) {
3140 TString sOpt(opt);
3141 sOpt.ToLower();
3142 if (auto o = getObject<RooAbsReal>(child.GetName())) {
3143 auto out = Multiply(xRooNode(o, child.fParent));
3144 // have to protect bin case where get() is null (could change but then must change logic above too)
3145 if (get()) {
3146 Info("Multiply", "Scaled %s by existing factor %s::%s",
3147 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), o->ClassName(), o->GetName());
3148 }
3149 return out;
3150 } else if (sOpt == "const") {
3151 auto out = Multiply(RooConstVar(child.GetName(), child.GetTitle(), 1));
3152 if (get()) {
3153 Info("Multiply", "Scaled %s by new const factor %s",
3154 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
3155 }
3156 return out;
3157 } else if (sOpt == "norm") {
3158 if (TString(child.GetName()).Contains("[") && ws()) {
3159 // assume factory method wanted
3160 auto arg = ws()->factory(child.GetName());
3161 if (arg) {
3162 auto out = Multiply(*arg);
3163 if (get()) {
3164 Info("Multiply", "Scaled %s by new norm factor %s",
3165 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
3166 }
3167 return out;
3168 }
3169 throw std::runtime_error(TString::Format("Failed to create new normFactor %s", child.GetName()));
3170 }
3171 auto out = Multiply(RooRealVar(child.GetName(), child.GetTitle(), 1, -1e-5, 100));
3172 if (get()) {
3173 Info("Multiply", "Scaled %s by new norm factor %s",
3174 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
3175 }
3176 return out;
3177 } else if (sOpt == "shape" || sOpt == "simple" || sOpt == "blankshape") {
3178 // needs axis defined
3179 if (auto ax = GetXaxis(); ax) {
3180 auto h = std::shared_ptr<TH1>(BuildHistogram(dynamic_cast<RooAbsLValue *>(ax->GetParent()), true));
3181 h->Reset();
3182 for (int i = 1; i <= h->GetNbinsX(); i++) {
3183 h->SetBinContent(i, 1);
3184 }
3185 h->SetMinimum(0);
3186 h->SetMaximum(100);
3187 h->SetName(TString::Format(";%s", child.GetName())); // ; char indicates don't "rename" this thing
3188 h->SetTitle(child.GetTitle());
3189 if (sOpt.Contains("shape"))
3190 h->SetOption(sOpt);
3191 auto out = Multiply(*h);
3192 if (get()) {
3193 Info("Multiply", "Scaled %s by new %s factor %s",
3194 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), sOpt.Data(), out->GetName());
3195 }
3196 return out;
3197 }
3198 } else if (sOpt == "overall") {
3200 child.GetName(), child.GetTitle(), RooArgList(), 1, std::vector<double>(), std::vector<double>()));
3201 if (get() /* can happen this is null if on a bin node with no shapeFactors*/) {
3202 Info("Multiply", "Scaled %s by new overall factor %s",
3203 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
3204 }
3205 return out;
3206 } else if (sOpt == "func" && ws()) {
3207 // need to get way to get dependencies .. can't pass all as causes circular dependencies issues.
3208 if (auto arg = ws()->factory(TString("expr::") + child.GetName())) {
3209 auto out = Multiply(*arg);
3210 if (get() /* can happen this is null if on a bin node with no shapeFactors*/) {
3211 Info("Multiply", "Scaled %s by new func factor %s",
3212 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
3213 }
3214 return out;
3215 }
3216 }
3217 }
3218 if (auto h = child.get<TH1>(); h && strlen(h->GetOption()) == 0 && strlen(opt) > 0) {
3219 // put the option in the hist
3220 h->SetOption(opt);
3221 }
3222 if (auto w = get<RooWorkspace>(); w) {
3223 // just acquire
3224 std::shared_ptr<TObject> out;
3225 child.convertForAcquisition(*this);
3226 if (child.get<RooAbsReal>())
3227 out = acquire(child.fComp);
3228 return out;
3229 }
3230
3231 if (strcmp(GetName(), ".coef") == 0) { // covers both .coef and .coefs
3232 // need to add this into the relevant coef ... if its not a RooProduct, replace it with one first
3233 if (auto p = fParent->fParent->get<RooAddPdf>()) {
3234 // may be in no-coef mode ... in which case must create coefs (use "ExtendedBindings" but note that these need
3235 // obs list passing to them
3236 if (p->coefList().empty() && !p->pdfList().empty()) {
3237 for (auto _pdf : p->pdfList()) {
3238 const_cast<RooArgList &>(p->coefList())
3239 .add(*acquireNew<RooExtendedBinding>(TString::Format("%s_extBind", _pdf->GetName()),
3240 TString::Format("Expected Events of %s", _pdf->GetTitle()),
3241 *static_cast<RooAbsPdf *>(_pdf)));
3242 }
3243 Info("Multiply", "Created RooExtendedBinding coefficients for all pdfs of %s so that can multiply coef",
3244 p->GetName());
3245 *reinterpret_cast<bool *>(reinterpret_cast<unsigned char *>(p) +
3246 p->Class()->GetDataMemberOffset("_allExtendable")) = false;
3247 *reinterpret_cast<bool *>(reinterpret_cast<unsigned char *>(p) +
3248 p->Class()->GetDataMemberOffset("_haveLastCoef")) = true;
3249 }
3250 for (size_t i = 0; i < p->pdfList().size(); i++) {
3251 if (p->pdfList().at(i) == fParent->get<RooAbsArg>()) {
3252 auto coefs = p->coefList().at(i);
3253 if (!coefs->InheritsFrom("RooProduct")) {
3255 if (!(strcmp(coefs->GetName(), "1") == 0 || strcmp(coefs->GetName(), "ONE") == 0))
3256 oldCoef.add(*coefs);
3257 auto newCoefs = fParent->acquireNew<RooProduct>(
3258 TString::Format("coefs_%s", fParent->GetName()),
3259 TString::Format("coefficients for %s", fParent->GetName()), oldCoef);
3261 for (size_t j = 0; j < p->coefList().size(); j++) {
3262 if (i == j) {
3263 oldCoefs.add(*newCoefs);
3264 } else {
3265 oldCoefs.add(*p->coefList().at(j));
3266 }
3267 }
3268 const_cast<RooArgList &>(p->coefList()).removeAll();
3269 const_cast<RooArgList &>(p->coefList()).add(oldCoefs);
3270 coefs = newCoefs.get();
3271 }
3272 return xRooNode(*coefs, fParent).Multiply(child);
3273 }
3274 }
3275 } else if (auto p2 = fParent->fParent->get<RooRealSumPdf>()) {
3276 // find our function in the funcList, and then update the coefs of it
3277
3278 for (size_t i = 0; i < p2->funcList().size(); i++) {
3279 if (p2->funcList().at(i) == fParent->get<RooAbsArg>()) {
3280 auto coefs = p2->coefList().at(i);
3281 if (!coefs->InheritsFrom("RooProduct")) {
3283 if (!(strcmp(coefs->GetName(), "1") == 0 || strcmp(coefs->GetName(), "ONE") == 0))
3284 oldCoef.add(*coefs);
3285 auto newCoefs = fParent->acquireNew<RooProduct>(
3286 TString::Format("coefs_%s", fParent->GetName()),
3287 TString::Format("coefficients for %s", fParent->GetName()), oldCoef);
3289 for (size_t j = 0; j < p2->coefList().size(); j++) {
3290 if (i == j) {
3291 oldCoefs.add(*newCoefs);
3292 } else {
3293 oldCoefs.add(*p2->coefList().at(j));
3294 }
3295 }
3296 const_cast<RooArgList &>(p2->coefList()).removeAll();
3297 const_cast<RooArgList &>(p2->coefList()).add(oldCoefs);
3298 coefs = newCoefs.get();
3299 }
3300 return xRooNode(*coefs, fParent).Multiply(child);
3301 }
3302 }
3303 }
3304 throw std::runtime_error("this coefs case is not supported");
3305 }
3306
3307 if (auto p = get<RooProduct>(); p) {
3308 std::shared_ptr<TObject> out;
3309 auto cc = child.fComp;
3310 bool isConverted = (child.convertForAcquisition(*this) != cc);
3311 if (child.get<RooAbsReal>())
3312 out = acquire(child.fComp);
3313
3314 // child may be a histfunc or a rooproduct of a histfunc and a paramhist if has stat errors
3315 if (auto _f = std::dynamic_pointer_cast<RooHistFunc>(
3316 (child.get<RooProduct>()) ? child.factors()[child.GetName()]->fComp : out);
3317 _f && _f->getAttribute("autodensity")) {
3318 // should we flag this as a density? yes if there's no other term marked as the density
3319 bool hasDensity = false;
3320 for (auto &f : factors()) {
3321 if (f->get<RooAbsArg>()->getAttribute("density")) {
3322 hasDensity = true;
3323 break;
3324 }
3325 }
3326 _f->setAttribute("density", !hasDensity && fParent && fParent->get<RooRealSumPdf>());
3327 if (_f->getAttribute("density")) {
3328
3329 // need to divide by bin widths first
3330 for (int i = 0; i < _f->dataHist().numEntries(); i++) {
3331 auto bin_pars = _f->dataHist().get(i);
3332 _f->dataHist().set(*bin_pars, _f->dataHist().weight() / _f->dataHist().binVolume(*bin_pars));
3333 }
3334 _f->setValueDirty();
3335
3336 // promote the axis vars to observables
3337 for (auto &x : xRooNode("tmp", _f).vars()) {
3338 x->get<RooAbsArg>()->setAttribute("obs");
3339 }
3340 }
3341 _f->setAttribute("autodensity", false);
3342 }
3343
3344 if (isConverted && child.get<RooHistFunc>()) {
3345 Info("Multiply", "Created %s factor %s in %s",
3346 child.get<RooAbsArg>()->getAttribute("density") ? "SimpleDensity" : "Simple", child->GetName(),
3347 p->GetName());
3348 } else if (isConverted && child.get<ParamHistFunc>()) {
3349 Info("Multiply", "Created Shape factor %s in %s", child->GetName(), p->GetName());
3350 }
3351
3352 if (auto _f = std::dynamic_pointer_cast<RooAbsReal>(out); _f) {
3353#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3354 p->_compRSet.add(*_f);
3355#else
3356 const_cast<RooArgList &>(p->realComponents()).add(*_f);
3357#endif
3358 p->setValueDirty();
3359
3360 browse();
3361 xRooNode _out(_f, *this);
3362 for (auto &_par : _out.pars()) {
3363 if (auto s = _par->get<RooAbsArg>()->getStringAttribute("boundConstraint"); s) {
3364 bool found = false;
3365 for (auto &_constr : _par->constraints()) {
3366 if (strcmp(s, _constr->get()->GetName()) == 0) {
3367 // constraint is already included
3368 found = true;
3369 break;
3370 }
3371 }
3372 if (!found) {
3373 Info("Multiply", "Pulling in %s boundConstraint: %s", _par->GetName(), s);
3374 auto _pdf = getObject<RooAbsPdf>(s);
3375 if (!_pdf) {
3376 throw std::runtime_error("Couldn't find boundConstraint");
3377 }
3378 _par->Constrain(_pdf);
3379 }
3380 }
3381 }
3382 sterilize();
3383 return _out;
3384 }
3385 } else if (auto p2 = get<RooProdPdf>(); p2) {
3386
3387 std::shared_ptr<TObject> out;
3388 child.convertForAcquisition(*this);
3389 if (child.get<RooAbsPdf>()) {
3390 out = acquire(child.fComp);
3391 } else if (child.get<RooAbsReal>() && mainChild().get<RooRealSumPdf>()) {
3392 // cannot multiply a RooProdPdf by a non pdf
3393 throw std::runtime_error(TString::Format("Cannot multiply %s by non-pdf %s", GetName(), child.GetName()));
3394 // return mainChild().Add(child); - nov 2022 - used to do this but now replaced with exception above
3395 } else if (!child.get() || child.get<RooAbsReal>()) {
3396 // need to create or hide inside a sumpdf or rooadpdf
3397 std::shared_ptr<RooAbsPdf> _pdf;
3398 if (!child.get() && strcmp(child.GetName(), "components") == 0) {
3400 Form("%s_%s", p2->GetName(), child.GetName()),
3401 (strlen(child.GetTitle()) && strcmp(child.GetTitle(), child.GetName())) ? child.GetTitle()
3402 : p2->GetTitle(),
3403 RooArgList() /*, RooArgList() forces coef-mode if we specify this list */);
3404 _pdf = _sumpdf;
3405 } else {
3407 Form("%s_%s", p2->GetName(), child.GetName()),
3408 (strlen(child.GetTitle()) && strcmp(child.GetTitle(), child.GetName())) ? child.GetTitle()
3409 : p2->GetTitle(),
3410 RooArgList(), RooArgList(), true);
3411 _sumpdf->setFloor(true);
3412 _pdf = _sumpdf;
3413 }
3414 _pdf->setStringAttribute("alias", child.GetName());
3415 // transfer axis attributes if present (TODO: should GetXaxis look beyond the immediate parent?)
3416 _pdf->setStringAttribute("xvar", p2->getStringAttribute("xvar"));
3417 _pdf->setStringAttribute("binning", p2->getStringAttribute("binning"));
3418 out = _pdf;
3419 Info("Multiply", "Created %s::%s in channel %s", _pdf->ClassName(), _pdf->GetName(), p2->GetName());
3420 if (child.get<RooAbsReal>())
3421 xRooNode(*out, *this).Add(child);
3422 }
3423
3424 if (auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out); _pdf) {
3425#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3426 const_cast<RooArgList &>(p2->pdfList()).add(*_pdf);
3427#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
3428 p2->_pdfNSetList.emplace_back(std::make_unique<RooArgSet>("nset"));
3429#else
3430 p->_pdfNSetList.Add(new RooArgSet("nset"));
3431#endif
3432 if (!p2->canBeExtended() && _pdf->canBeExtended()) {
3433 p2->_extendedIndex = p2->_pdfList.size() - 1;
3434 }
3435#else
3436 p2->addPdfs(RooArgSet(*_pdf));
3437#endif
3438 sterilize();
3439 browse();
3440 return xRooNode(_pdf, *this);
3441 }
3442 } else if (auto p3 = get<RooRealSumPdf>(); p3) {
3443 // multiplying all current and future components
3444 std::shared_ptr<TObject> out;
3445 child.convertForAcquisition(*this);
3446 if (child.get<RooAbsReal>()) {
3447 out = acquire(child.fComp);
3448 for (auto &c : components()) {
3449 c->Multiply(out);
3450 }
3451 TString s = p3->getStringAttribute("global_factors");
3452 if (s != "")
3453 s += ";";
3454 s += out->GetName();
3455 p3->setStringAttribute("global_factors", s);
3456 Info(
3457 "Multiply",
3458 "Flagged %s as a global factor in channel %s (is applied to all current and future samples in the channel)",
3459 out->GetName(), p3->GetName());
3460 return xRooNode(out, *this);
3461 }
3462
3463 } else if (auto p4 = get<RooAbsPdf>(); p4 && !(fParent && fParent->get<RooRealSumPdf>())) {
3464 // multiply the coefs (if this isn't part of a RooAddPdf or RooRealSumPdf then we will eventually throw exception
3465 return coefs().Multiply(child);
3466 } else if (auto p5 = get<RooAbsReal>(); p5 && (!get<RooAbsPdf>() || (fParent && fParent->get<RooRealSumPdf>()))) {
3467 // replace this obj with a RooProduct to allow for multiplication
3468
3469 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
3470 std::set<RooAbsArg *> cl;
3471 for (auto &arg : p5->clients()) {
3472 cl.insert(arg);
3473 }
3474
3475 // if multiple clients, see if only one client is in parentage route
3476 // if so, then assume thats the only client we should replace in
3477 if (cl.size() > 1) {
3478 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
3479 cl.clear();
3480 cl.insert(fParent->get<RooAbsArg>());
3481 } else {
3482 Warning("Multiply", "Scaling %s that has multiple clients", p5->GetName());
3483 }
3484 }
3485
3486 auto new_p = acquireNew<RooProduct>(TString::Format("prod_%s", p5->GetName()), p5->GetTitle(), RooArgList(*p5));
3487 // copy attributes over
3488 for (auto &a : p5->attributes())
3489 new_p->setAttribute(a.c_str());
3490 for (auto &a : p5->stringAttributes())
3491 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
3492 if (!new_p->getStringAttribute("alias"))
3493 new_p->setStringAttribute("alias", p5->GetName());
3494 auto old_p = p5;
3495 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
3496 for (auto arg : cl) {
3497 arg->redirectServers(RooArgSet(*new_p), false, true);
3498 }
3499
3500 fComp = new_p;
3501 return Multiply(child);
3502 }
3503
3504 // before giving up here, assume user wanted a norm factor type if child is just a name
3505 if (!child.get() && strlen(opt) == 0)
3506 return Multiply(child, "norm");
3507
3508 throw std::runtime_error(
3509 TString::Format("Cannot multiply %s by %s%s", GetPath().c_str(), child.GetName(),
3510 (!child.get() && strlen(opt) == 0) ? " (forgot to specify factor type?)" : ""));
3511}
3512
3514{
3515
3516 auto p5 = get<RooAbsArg>();
3517 if (!p5) {
3518 throw std::runtime_error("Only replacement of RooAbsArg is supported");
3519 }
3520 node.convertForAcquisition(*this, "func");
3521
3522 auto new_p = node.get<RooAbsArg>();
3523 if (!new_p) {
3524 throw std::runtime_error(TString::Format("Cannot replace with %s", node.GetName()));
3525 }
3526 auto out = acquire(node.fComp);
3527 new_p = std::dynamic_pointer_cast<RooAbsArg>(out).get();
3528
3529 std::set<RooAbsArg *> cl;
3530 for (auto &arg : p5->clients()) {
3531 if (arg == new_p)
3532 continue; // do not replace in self ... although redirectServers will prevent that anyway
3533 cl.insert(arg);
3534 }
3535
3536 // if multiple clients, see if only one client is in parentage route
3537 // if so, then assume thats the only client we should replace in
3538 if (cl.size() > 1) {
3539 if (fParent && fParent->get<RooAbsArg>() && cl.count(fParent->get<RooAbsArg>()) > 0) {
3540 cl.clear();
3541 cl.insert(fParent->get<RooAbsArg>());
3542 } else {
3543 std::stringstream clientList;
3544 for (auto c : cl)
3545 clientList << c->GetName() << ",";
3546 Warning("Replace", "Replacing %s in all clients: %s", p5->GetName(), clientList.str().c_str());
3547 }
3548 }
3549
3550 new_p->setAttribute(Form("ORIGNAME:%s", p5->GetName())); // used in redirectServers to say what this replaces
3551 for (auto arg : cl) {
3552 // if RooFormulaVar need to ensure the internal formula has been "constructed" otherwise will try to construct
3553 // it from the original expression that may have old parameter in it.
3554 if (auto p = dynamic_cast<RooFormulaVar *>(arg))
3555 p->ok(); // triggers creation of RooFormula
3556 arg->redirectServers(RooArgSet(*new_p), false, true);
3557 }
3558 return node;
3559}
3560
3562{
3563
3564 class AutoUpdater {
3565 public:
3566 AutoUpdater(xRooNode &_n) : n(_n) {}
3567 ~AutoUpdater() { n.browse(); }
3568 xRooNode &n;
3569 };
3570 AutoUpdater xxx(*this);
3571
3572 if (!get() && fParent) {
3573 // try to 'create' object based on parentage
3574 // add child as a temporary child to help with decision making
3575 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
3576 try {
3577 fComp = fParent->Add(*this, "+").fComp;
3578 } catch (...) {
3579 resize(size() - 1);
3580 std::rethrow_exception(std::current_exception());
3581 }
3582 resize(size() - 1); // remove the temporarily added node
3583 }
3584
3585 if (auto p = mainChild(); p) {
3586 // variations applied to the main child if has one
3587 return p.Vary(child);
3588 }
3589
3590 if (auto s = get<RooSimultaneous>(); s && s->indexCat().IsA() == RooCategory::Class()) {
3591 // name is used as cat label
3592 std::string label = child.GetName();
3593 if (auto pos = label.find('='); pos != std::string::npos)
3594 label = label.substr(pos + 1);
3595 if (!s->indexCat().hasLabel(label)) {
3596 // auto idx = static_cast<const RooCategory &>(s->indexCat()).nextAvailableStateIndex(); - can't access,
3597 // protected method ... will have to just assume indices stay in sync
3598 // ensure added to category in any of our datasets too
3599 for (auto _ds : datasets()) {
3600 if (auto bb = _ds->getBrowsable(".sourceds")) {
3601 _ds = bb;
3602 } // shouldn't happen
3603 auto dsCat = _ds->robs()[s->indexCat().GetName()]->get<RooCategory>();
3604 if (!dsCat) {
3605 throw std::runtime_error(TString::Format("Failed to find %s regular observable in %s dataset",
3606 s->indexCat().GetName(), _ds->GetName()));
3607 }
3608 dsCat->defineType(label.c_str());
3609 }
3610 // adding to the index cat after, so that we don't need to generate subdatasets in the call to datasets() above
3611 // (missing cat will trigger cut)
3612 static_cast<RooCategory &>(const_cast<RooAbsCategoryLValue &>(s->indexCat())).defineType(label.c_str());
3613 }
3614 std::shared_ptr<TObject> out;
3615 child.convertForAcquisition(*this);
3616 if (child.get<RooAbsPdf>()) {
3617 out = acquire(child.fComp); // may create a channel from a histogram
3618 } else if (!child.fComp) {
3619 out = acquireNew<RooProdPdf>(TString::Format("%s_%s", s->GetName(), label.c_str()),
3620 (strlen(child.GetTitle())) ? child.GetTitle() : label.c_str(), RooArgList());
3621 Info("Vary", "Created channel RooProdPdf::%s in model %s", out->GetName(), s->GetName());
3622 }
3623
3624 if (auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out); _pdf) {
3625 // before adding the channel, we need to see if we are about to add any globs, and if necessary we must update
3626 // the dataset globs
3627 std::set<RooAbsData *> dsToUpdate;
3628 for (auto _ds : datasets()) {
3629 if (auto bb = _ds->getBrowsable(".sourceds")) {
3630 _ds = bb;
3631 } // shouldn't happen
3632 if (_ds->get<RooAbsData>()->getGlobalObservables()) {
3633 dsToUpdate.insert(_ds->get<RooAbsData>());
3634 }
3635 }
3636 if (!dsToUpdate.empty()) {
3638 _pdf->leafNodeServerList(&leafs);
3639 std::unique_ptr<RooAbsCollection> globals(leafs.selectByAttrib("global", true));
3640 for (auto _ds : dsToUpdate) {
3641 std::string alist;
3643 globs.addClone(*_ds->getGlobalObservables());
3644 for (auto &aa : *globals) {
3645 if (!globs.contains(*aa)) {
3646 globs.addClone(*aa);
3647 alist += std::string(aa->GetName()) + ",";
3648 }
3649 }
3650 if (!alist.empty()) {
3651 Warning("Vary", "Adding %s to global observables of %s", alist.c_str(), _ds->GetName());
3652 _ds->setGlobalObservables(globs);
3653 }
3654 }
3655 }
3656
3657 s->addPdf(*_pdf, label.c_str());
3658 sterilize();
3659 // clear children for reload and update shared axis
3660 clear();
3661 fXAxis.reset();
3662 browse();
3663 return xRooNode(TString::Format("%s=%s", s->indexCat().GetName(), label.data()), _pdf, *this);
3664 }
3665
3666 } else if (auto p = get<RooStats::HistFactory::FlexibleInterpVar>(); p) {
3667
3668 // child needs to be a constvar ...
3669 child.convertForAcquisition(*this);
3670 auto _c = child.get<RooConstVar>();
3671 if (!_c && child.get()) {
3672 throw std::runtime_error("Only pure consts can be set as variations of a flexible interpvar");
3673 }
3674#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3675 double value = (_c ? _c->getVal() : p->_nominal);
3676 double nomVal = p->_nominal;
3677#else
3678 double value = (_c ? _c->getVal() : p->nominal());
3679 double nomVal = p->nominal();
3680#endif
3681
3682 TString cName(child.GetName());
3683 if (cName == "nominal") {
3684 p->setNominal(value);
3685 return *(this->variations().at(cName.Data()));
3686 }
3687 if (cName.CountChar('=') != 1) {
3688 throw std::runtime_error("unsupported variation form");
3689 }
3690 std::string parName = cName(0, cName.Index('='));
3691 double parVal = TString(cName(cName.Index('=') + 1, cName.Length())).Atof();
3692 if (parVal != 1 && parVal != -1) {
3693 throw std::runtime_error("unsupported variation magnitude");
3694 }
3695 bool high = parVal > 0;
3696
3697 if (parName.empty()) {
3698 p->setNominal(value);
3699 } else {
3700 auto v = fParent->getObject<RooRealVar>(parName);
3701 if (!v)
3702 v = fParent->acquire<RooRealVar>(parName.c_str(), parName.c_str(), -5, 5);
3703 if (!v->hasError())
3704 v->setError(1);
3705
3706 if (!p->findServer(*v)) {
3707#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3708 p->_paramList.add(*v);
3709 p->_low.push_back(0);
3710 p->_high.push_back(0);
3711 p->_interpCode.push_back(4);
3712#else
3713 const_cast<RooListProxy &>(p->variables()).add(*v);
3714 const_cast<std::vector<double> &>(p->low()).push_back(0);
3715 const_cast<std::vector<double> &>(p->high()).push_back(0);
3716 const_cast<std::vector<int> &>(p->interpolationCodes()).push_back(4);
3717#endif
3718 v->setAttribute(Form("SYMMETRIC%s_%s", high ? "+" : "-", GetName())); // flag for symmetrized
3719 }
3720
3721 if (high) {
3722 p->setHigh(*v, value);
3723 if (v->getAttribute(Form("SYMMETRIC+_%s", GetName()))) {
3724 p->setLow(*v, 2 * nomVal - value);
3725 }
3726 v->setAttribute(Form("SYMMETRIC-_%s", GetName()), false);
3727 } else {
3728 p->setLow(*v, value);
3729 if (v->getAttribute(Form("SYMMETRIC-_%s", GetName()))) {
3730 p->setHigh(*v, 2 * nomVal - value);
3731 }
3732 v->setAttribute(Form("SYMMETRIC+_%s", GetName()), false);
3733 }
3734
3735 /*if (!unconstrained && fParent->pars()[v->GetName()].constraints().empty()) {
3736 fParent->pars()[v->GetName()].constraints().add("normal");
3737 }*/
3738 }
3739 return *(this->variations().at(cName.Data()));
3740 } else if (auto p2 = get<PiecewiseInterpolation>(); p2) {
3741 TString cName(child.GetName());
3742 if (cName.CountChar('=') != 1) {
3743 throw std::runtime_error("unsupported variation form");
3744 }
3745 TString parName = cName(0, cName.Index('='));
3746 double parVal = TString(cName(cName.Index('=') + 1, cName.Length())).Atof();
3747 if (parVal != 1 && parVal != -1) {
3748 throw std::runtime_error("unsupported variation magnitude");
3749 }
3750#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3751 RooHistFunc *f = dynamic_cast<RooHistFunc *>(p2->_nominal.absArg());
3752 if (!f) {
3753 throw std::runtime_error(
3754 TString::Format("Interpolating %s instead of RooHistFunc", p2->_nominal.absArg()->ClassName()));
3755 }
3756#else
3757 RooHistFunc *f = dynamic_cast<RooHistFunc *>(const_cast<RooAbsReal *>(p2->nominalHist()));
3758 if (!f) {
3759 throw std::runtime_error(
3760 TString::Format("Interpolating %s instead of RooHistFunc", p2->nominalHist()->ClassName()));
3761 }
3762#endif
3763 RooHistFunc *nomf = f;
3764 RooHistFunc *otherf = nullptr;
3765 size_t i = 0;
3766 for (auto par : p2->paramList()) {
3767 if (parName == par->GetName()) {
3768 f = dynamic_cast<RooHistFunc *>((parVal > 0 ? p2->highList() : p2->lowList()).at(i));
3769 otherf = dynamic_cast<RooHistFunc *>((parVal > 0 ? p2->lowList() : p2->highList()).at(i));
3770 break;
3771 }
3772 i++;
3773 }
3774 if (i == p2->paramList().size() && !child.get<RooAbsReal>()) {
3775
3776 // need to add the parameter
3777 auto v = acquire<RooRealVar>(parName, parName, -5, 5);
3778 if (!v->hasError())
3779 v->setError(1);
3780
3781 std::shared_ptr<RooHistFunc> up(
3782 static_cast<RooHistFunc *>(f->Clone(Form("%s_%s_up", f->GetName(), parName.Data()))));
3783 std::shared_ptr<RooHistFunc> down(
3784 static_cast<RooHistFunc *>(f->Clone(Form("%s_%s_down", f->GetName(), parName.Data()))));
3785 // RooHistFunc doesn't clone it's data hist ... do it ourself (will be cloned again if imported into a ws)
3786#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3787 std::unique_ptr<RooDataHist> h1(
3788 static_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", up->GetName()))));
3789 std::unique_ptr<RooDataHist> h2(
3790 static_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", down->GetName()))));
3791 up->_dataHist = dynamic_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", up->GetName())));
3792 down->_dataHist = dynamic_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", down->GetName())));
3793#else
3794 up->cloneAndOwnDataHist(TString::Format("hist_%s", up->GetName()));
3795 down->cloneAndOwnDataHist(TString::Format("hist_%s", down->GetName()));
3796#endif
3797 auto ups = std::dynamic_pointer_cast<RooHistFunc>(acquire(up, false, true));
3798 auto downs = std::dynamic_pointer_cast<RooHistFunc>(acquire(down, false, true));
3799#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3800 p2->_highSet.add(*ups.get());
3801 p2->_lowSet.add(*downs.get());
3802 p2->_interpCode.push_back(4);
3803 p2->_paramSet.add(*v);
3804#else
3805 const_cast<RooArgList &>(p2->highList()).add(*ups);
3806 const_cast<RooArgList &>(p2->lowList()).add(*downs);
3807 const_cast<std::vector<int> &>(p2->interpolationCodes()).push_back(4);
3808 const_cast<RooArgList &>(p2->paramList()).add(*v);
3809#endif
3810 p2->setValueDirty();
3811 f = ((parVal > 0) ? ups : downs).get();
3812 otherf = ((parVal > 0) ? downs : ups).get();
3813 // start off with everything being symmetric
3814 f->setStringAttribute("symmetrizes", otherf->GetName());
3815 f->setStringAttribute("symmetrize_nominal", nomf->GetName());
3816 otherf->setStringAttribute("symmetrized_by", f->GetName());
3817
3818 // constrain par if required
3819 /*if (!unconstrained && fParent->pars()[v->GetName()].constraints().empty()) {
3820 fParent->pars()[v->GetName()].constraints().add("normal");
3821 }*/
3822 }
3823
3824 // child.convertForAcquisition(*this);
3825 if (f) {
3826 if (child.get())
3827 xRooNode("tmp", *f, *this) = *child.get();
3828 f->setValueDirty();
3829 xRooNode out(*f, *this);
3830 out.sterilize();
3831 return out;
3832 }
3833
3834#if ROOT_VERSION_CODE > ROOT_VERSION(6, 37, 00)
3835 } else if (auto pmr = get<RooMultiReal>(); pmr) {
3836 TString cName(child.GetName());
3837 if (cName.CountChar('=') != 1) {
3838 throw std::runtime_error("unsupported variation form");
3839 }
3840 TString parName = cName(0, cName.Index('='));
3841 TString parVal = TString(cName(cName.Index('=') + 1, cName.Length()));
3842
3843 // add parVal to categorical if not already there
3844 if (!pmr->indexCategory()->hasLabel(parVal.Data())) {
3845 dynamic_cast<RooCategory &>(*(pmr->indexCategory())).defineType(parVal);
3846 }
3847 auto idx = pmr->indexCategory()->lookupIndex(parVal.Data());
3848 if (idx < 0) {
3849 throw std::runtime_error("Invalid index");
3850 }
3851 // add child to list ... use copy of "nominal" (0th) if child is empty
3852 child.convertForAcquisition(*this);
3853 auto _c = child.get<RooAbsReal>();
3854 if (!_c) {
3855 if (pmr->getModelList().empty()) {
3856 throw std::runtime_error("No real function given for variation, and no nominal function to clone");
3857 }
3858 _c = std::dynamic_pointer_cast<RooAbsReal>(
3859 acquire(std::shared_ptr<TObject>(pmr->getModelList().at(0)->Clone(
3860 TString::Format("%s_%s", get()->GetName(), child.GetName()))),
3861 false, true))
3862 .get();
3863 _c->setStringAttribute("alias", child.GetName());
3864 }
3865 const_cast<RooListProxy &>(pmr->getModelList()).add(*_c);
3866 return xRooNode(*_c, *this);
3867
3868#endif
3869
3870 } else if (auto p3 = get<RooConstVar>(); p3) {
3871
3872 // never vary the universal consts ... its too dangerous
3873 if (p3->getAttribute("RooRealConstant_Factory_Object")) {
3874 throw std::runtime_error("Cannot vary pure constants");
3875 }
3876
3877 // inject a FlexibleInterpVar ...
3878
3879 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
3880 std::set<RooAbsArg *> cl;
3881 for (auto &arg : p3->clients()) {
3882 cl.insert(arg);
3883 }
3884 // if multiple clients, see if only one client is in parentage route
3885 // if so, then assume thats the only client we should replace in
3886 if (cl.size() > 1) {
3887 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
3888 cl.clear();
3889 cl.insert(fParent->get<RooAbsArg>());
3890 } else {
3891 Warning("Vary", "Varying %s that has multiple clients", p3->GetName());
3892 }
3893 }
3894 p3->setStringAttribute("origName", p3->GetName());
3895 TString n = p3->GetName();
3896 p3->SetName(Form("%s_nominal", p3->GetName())); // if problems should perhaps not rename here
3897
3899 std::vector<double>(), std::vector<double>());
3900
3901 // copy attributes over
3902 for (auto &a : p3->attributes())
3903 new_p->setAttribute(a.c_str());
3904 for (auto &a : p3->stringAttributes())
3905 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
3906 // if (!new_p->getStringAttribute("alias")) new_p->setStringAttribute("alias",p->GetName());
3907 auto old_p = p3;
3908 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
3909 for (auto arg : cl) {
3910 arg->redirectServers(RooArgSet(*new_p), false, true);
3911 }
3912
3913 fComp = new_p;
3914 return Vary(child);
3915
3916 } else if (auto p4 = get<RooAbsReal>(); p4) {
3917 // inject an interpolation node
3918
3919 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
3920 std::set<RooAbsArg *> cl;
3921 for (auto &arg : p4->clients()) {
3922 cl.insert(arg);
3923 }
3924 // if multiple clients, see if only one client is in parentage route
3925 // if so, then assume thats the only client we should replace in
3926 if (cl.size() > 1) {
3927 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
3928 cl.clear();
3929 cl.insert(fParent->get<RooAbsArg>());
3930 } else {
3931 Warning("Vary", "Varying %s that has multiple clients", p4->GetName());
3932 }
3933 }
3934 p4->setStringAttribute("origName", p4->GetName());
3935 TString n = p4->GetName();
3936 p4->SetName(Form("%s_nominal", p4->GetName())); // if problems should perhaps not rename here
3937
3938#if ROOT_VERSION_CODE > ROOT_VERSION(6, 37, 00)
3939 std::shared_ptr<RooAbsArg> new_p;
3940 // if alphanumeric variation name is not a 1 or -1, inject a RooMultiReal not a PiecewiseInterpolation ...
3941 TString cName(child.GetName());
3942 if (cName.CountChar('=') == 1) {
3943 TString parName = cName(0, cName.Index('='));
3944 double parVal = TString(cName(cName.Index('=') + 1, cName.Length())).Atof();
3945 if (parVal != 1 && parVal != -1) {
3946 // get the idxCat ...
3948 if (!idxCat) {
3949 throw std::runtime_error("Failed to acquire categorical index for RooMultiReal");
3950 }
3951 // add an index if none already
3952 if (idxCat->numTypes() == 0) {
3953 idxCat->defineType("nominal");
3954 }
3955 Info("Vary", "Creating a RooMultiReal with category %s", idxCat->GetName());
3957 n, p4->GetTitle(), *idxCat,
3958 RooArgList()); // can't pass nominal model as that will force defining types on category
3959 const_cast<RooListProxy &>(newFunc->getModelList()).add(*p4);
3960 new_p = newFunc;
3961 }
3962 }
3963 if (!new_p) {
3965 }
3966
3967#else
3969#endif
3970
3971 // copy attributes over
3972 for (auto &a : p4->attributes())
3973 new_p->setAttribute(a.c_str());
3974 for (auto &a : p4->stringAttributes())
3975 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
3976 // if (!new_p->getStringAttribute("alias")) new_p->setStringAttribute("alias",p->GetName());
3977 auto old_p = p4;
3978 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
3979 for (auto arg : cl) {
3980 arg->redirectServers(RooArgSet(*new_p), false, true);
3981 }
3982
3983 fComp = new_p;
3984 return Vary(child);
3985 }
3986
3987 Print();
3988 throw std::runtime_error(TString::Format("Cannot vary %s with %s", GetName(), child.GetName()));
3989}
3990
3992{
3994}
3995
3996bool xRooNode::SetContent(double value, const char *par, double val)
3997{
3998 return SetContents(RooConstVar(GetName(), GetTitle(), value), par, val);
3999}
4000
4003 {
4004 if (x && b)
4005 x->setBinning(*b);
4006 if (b)
4007 delete b;
4008 }
4009 RooRealVar *x = nullptr;
4010 RooAbsBinning *b = nullptr;
4011};
4012
4014{
4015
4016 if (!get()) {
4017 fComp = std::shared_ptr<TObject>(const_cast<TObject *>(&o), [](TObject *) {});
4018 if (fParent && !fParent->find(GetName())) {
4019 // either a temporary or a placeholder so need to try genuinely adding
4020 fComp = fParent->Add(*this, "+").fComp;
4021 if (auto a = get<RooAbsArg>(); a && strcmp(a->GetName(), GetName()) && !a->getStringAttribute("alias")) {
4022 a->setStringAttribute("alias", GetName());
4023 }
4024 if (!fComp)
4025 throw std::runtime_error("Cannot determine type");
4026 return *this;
4027 }
4028 }
4029
4030 if (auto h = dynamic_cast<const TH1 *>(&o); h) {
4031 /*auto f = get<RooHistFunc>();
4032 if (!f) {
4033 // if it's a RooProduct locate child with the same name
4034 if (get<RooProduct>()) {
4035 f = factors()[GetName()]->get<RooHistFunc>();
4036 }
4037
4038
4039
4040 }*/
4041 bool _isData = get<RooAbsData>();
4043 if (_isData) {
4044 // need to ensure x-axis matches this h
4045 auto ax = GetXaxis();
4046 if (!ax)
4047 throw std::runtime_error("no xaxis");
4048 auto _v = dynamic_cast<RooRealVar *>(ax->GetParent());
4049 if (_v) {
4050 _b.x = _v;
4051 _b.b = dynamic_cast<RooAbsBinning *>(_v->getBinningPtr(nullptr)->Clone());
4052 if (h->GetXaxis()->IsVariableBinSize()) {
4053 _v->setBinning(RooBinning(h->GetNbinsX(), h->GetXaxis()->GetXbins()->GetArray()));
4054 } else {
4055 _v->setBinning(RooUniformBinning(h->GetXaxis()->GetXmin(), h->GetXaxis()->GetXmax(), h->GetNbinsX()));
4056 }
4057 }
4058 }
4059
4060 if (true) {
4061 for (int bin = 1; bin <= h->GetNbinsX(); bin++) {
4062 SetBinContent(bin, h->GetBinContent(bin));
4063 /*double value = h->GetBinContent(bin);
4064 auto bin_pars = f->dataHist().get(bin - 1);
4065 if (f->getAttribute("density")) {
4066 value /= f->dataHist().binVolume(*bin_pars);
4067 }
4068 f->dataHist().set(*bin_pars, value);*/
4069 if (!_isData && h->GetSumw2N() && !SetBinError(bin, h->GetBinError(bin)))
4070 throw std::runtime_error("Failed setting stat error");
4071 }
4072 return *this;
4073 }
4074 } else if (auto _c = dynamic_cast<const RooConstVar *>(&o); _c) {
4075
4076 if (auto a = get<RooAbsArg>();
4077 (a && a->isFundamental()) || get<RooConstVar>() || get<RooStats::HistFactory::FlexibleInterpVar>()) {
4078 SetBinContent(1, _c->getVal());
4079 return *this;
4080 } else if (get<RooAbsData>()) { // try to do assignment to a dataset (usually setting a bin content)
4081 SetBinContent(0, _c->getVal());
4082 return *this;
4083 }
4084 }
4085
4086 throw std::runtime_error("Assignment failed");
4087
4088 /*
4089
4090 if (fParent && !fParent->mk()) {
4091 throw std::runtime_error("mk failure");
4092 }
4093
4094 if (fComp) return *this;
4095
4096 if (o.InheritsFrom("RooAbsArg")) {
4097 fComp = acquire(std::shared_ptr<TObject>(const_cast<TObject*>(&o),[](TObject* o){}));
4098 std::dynamic_pointer_cast<RooAbsArg>(fComp)->setStringAttribute("alias",GetName());
4099 }
4100
4101 if (fComp && fParent) {
4102 fParent->incorporate(fComp);
4103 }
4104
4105
4106 return *this;
4107 */
4108}
4109
4110void xRooNode::_fit_(const char *constParValues, const char *options)
4111{
4112 try {
4113 // re-float all poi and np before fitting
4114 np().get<RooArgList>()->setAttribAll("Constant", false);
4115 poi().get<RooArgList>()->setAttribAll("Constant", false);
4116 auto _pars = pars();
4117 // std::unique_ptr<RooAbsCollection> snap(_pars.argList().snapshot());
4118 TStringToken pattern(constParValues, ",");
4119 std::map<RooAbsRealLValue *, double> valsToSet;
4120 while (pattern.NextToken()) {
4121 auto idx = pattern.Index('=');
4122 TString pat = (idx == -1) ? TString(pattern) : TString(pattern(0, idx));
4123 double val =
4124 (idx == -1) ? std::numeric_limits<double>::quiet_NaN() : TString(pattern(idx + 1, pattern.Length())).Atof();
4125 bool foundArg = false;
4126 for (auto p : _pars.argList()) {
4127 if (TString(p->GetName()).Contains(TRegexp(pat, true))) {
4128 foundArg = true;
4129 p->setAttribute("Constant", true);
4130 if (!std::isnan(val)) {
4131 valsToSet[dynamic_cast<RooAbsRealLValue *>(p)] = val;
4132 // dynamic_cast<RooAbsRealLValue *>(p)->setVal(val); // don't set yet, to allow for asimov dataset
4133 // creation based on current values
4134 }
4135 }
4136 }
4137 if (!foundArg) {
4138 throw std::runtime_error(std::string("Unrecognised parameter: ") + pat.Data());
4139 }
4140 }
4141
4142 // parse options
4143 TStringToken pattern2(options, ",");
4145 while (pattern2.NextToken()) {
4146 auto idx = pattern2.Index('=');
4147 TString pat = (idx == -1) ? TString(pattern2) : TString(pattern2(0, idx));
4148 TString val = TString(pattern2(idx + 1, pattern2.Length()));
4149 if (auto o = defaultOpts->FindObject(pat)) {
4150 defaultOpts->Remove(o);
4151 delete o;
4152 }
4153 defaultOpts->Add(new RooCmdArg(pat, val.IsDec() ? val.Atoi() : 0, 0, val.IsFloat() ? val.Atof() : 0., 0.,
4154 val.IsAlpha() ? val : nullptr));
4155 }
4156
4157 // use the first selected dataset
4158 auto _dsets = datasets();
4159 TString dsetName = "";
4160 for (auto &d : _dsets) {
4161 if (d->get()->TestBit(1 << 20)) {
4162 dsetName = d->get()->GetName();
4163 break;
4164 }
4165 }
4166 auto _nll = nll(dsetName.Data(), *defaultOpts);
4167 // can now set the values
4168 for (auto [p, v] : valsToSet) {
4169 p->setVal(v);
4170 }
4171 _nll.fitConfigOptions()->SetValue("LogSize", 65536);
4172 _nll.fitConfig()->MinimizerOptions().SetPrintLevel(0);
4173 auto fr = _nll.minimize();
4174 //_pars.argList() = *snap; // restore values - irrelevant as SetFitResult will restore values
4175 if (!fr.get())
4176 throw std::runtime_error("Fit Failed");
4177 SetFitResult(fr.get());
4179 for (unsigned int i = 0; i < fr->numStatusHistory(); i++) {
4180 statusCodes += TString::Format("\n%s = %d", fr->statusLabelHistory(i), fr->statusCodeHistory(i));
4181 }
4182 const TGWindow *w =
4183 (gROOT->GetListOfBrowsers()->At(0))
4184 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4185 : gClient->GetRoot();
4186 TString gofResult = "";
4187 if (_nll.fOpts->find("GoF")) {
4188 gofResult = TString::Format("GoF p-value = %g\n", fr->constPars().getRealValue(".pgof"));
4189 }
4190 if (fr->status() != 0) {
4191 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished with Bad Status Code",
4192 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n%s-------------%s",
4193 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), gofResult.Data(),
4194 statusCodes.Data()),
4196 } else if (fr->covQual() != 3 && _nll.fitConfig()->ParabErrors()) {
4197 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished with Bad Covariance Quality",
4198 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n%s-------------%s",
4199 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), gofResult.Data(),
4200 statusCodes.Data()),
4202 } else {
4203 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished Successfully",
4204 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n%s-------------%s",
4205 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), gofResult.Data(),
4206 statusCodes.Data()));
4207 }
4208 TBrowser *b = nullptr;
4209 for (auto a : *gROOT->GetListOfBrowsers()) {
4210 b = dynamic_cast<TBrowser *>(a);
4211 if (b && GetTreeItem(b)) {
4212 break;
4213 }
4214 }
4215 if (b) {
4216 auto p = GetTreeItem(b);
4217 while (p) {
4218 if (TString(p->GetText()).BeginsWith("RooWorkspace::")) {
4219 // found the workspace ... refresh this node, and if there's a fits node, refresh that
4220 if (auto bi = dynamic_cast<TRootBrowser *>(b->GetBrowserImp())) {
4221 if (auto fb = dynamic_cast<TGFileBrowser *>(bi->GetActBrowser())) {
4222 fb->DoubleClicked(p, 0);
4223 if (auto first = p->GetFirstChild()) {
4224 do {
4225 if (first->IsOpen() && TString(first->GetText()) == "fits") {
4226 fb->DoubleClicked(first, 0);
4227 }
4228 } while ((first = first->GetNextSibling()));
4229 }
4230 }
4231 }
4232 break;
4233 } else {
4234 p = p->GetParent();
4235 }
4236 }
4237 }
4238 } catch (const std::exception &e) {
4239 new TGMsgBox(
4240 gClient->GetRoot(),
4241 (gROOT->GetListOfBrowsers()->At(0))
4242 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4243 : gClient->GetRoot(),
4244 "Exception", e.what(), kMBIconExclamation, kMBOk); // deletes self on dismiss?
4245 }
4246}
4247
4249{
4250 try {
4251 datasets().Add(datasetName, expected ? "asimov" : "toy");
4252 // refresh datasets folder of workspace
4253 TBrowser *b = nullptr;
4254 for (auto a : *gROOT->GetListOfBrowsers()) {
4255 b = dynamic_cast<TBrowser *>(a);
4256 if (b && GetTreeItem(b)) {
4257 break;
4258 }
4259 }
4260 if (b) {
4261 auto p = GetTreeItem(b);
4262 while (p) {
4263 if (TString(p->GetText()).BeginsWith("RooWorkspace::")) {
4264 // found the workspace ... refresh this node, and if there's a datasets node, refresh that
4265 if (auto bi = dynamic_cast<TRootBrowser *>(b->GetBrowserImp())) {
4266 if (auto fb = dynamic_cast<TGFileBrowser *>(bi->GetActBrowser())) {
4267 fb->DoubleClicked(p, 0);
4268 if (auto first = p->GetFirstChild()) {
4269 do {
4270 if (first->IsOpen() && TString(first->GetText()) == "datasets") {
4271 fb->DoubleClicked(first, 0);
4272 }
4273 } while ((first = first->GetNextSibling()));
4274 }
4275 }
4276 }
4277 break;
4278 } else {
4279 p = p->GetParent();
4280 }
4281 }
4282 }
4283 } catch (const std::exception &e) {
4284 new TGMsgBox(
4285 gClient->GetRoot(),
4286 (gROOT->GetListOfBrowsers()->At(0))
4287 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4288 : gClient->GetRoot(),
4289 "Exception", e.what(),
4290 kMBIconExclamation); // deletes self on dismiss?
4291 }
4292}
4293
4294void xRooNode::_scan_(const char *what, double nToys, const char *xvar, int nBinsX, double lowX,
4295 double highX /*, const char*, int, double, double*/, const char *constParValues,
4296 const char *options)
4297{
4298 try {
4301
4302 // use the first selected dataset
4303 auto _dsets = datasets();
4304 TString dsetName = "";
4305 for (auto &d : _dsets) {
4306 if (d->get()->TestBit(1 << 20)) {
4307 dsetName = d->get()->GetName();
4308 break;
4309 }
4310 }
4311 auto _pars = pars();
4312 std::unique_ptr<RooAbsCollection> snap(_pars.argList().snapshot());
4313 TStringToken pattern(constParValues, ",");
4314 while (pattern.NextToken()) {
4315 auto idx = pattern.Index('=');
4316 TString pat = (idx == -1) ? TString(pattern) : TString(pattern(0, idx));
4317 double val =
4318 (idx == -1) ? std::numeric_limits<double>::quiet_NaN() : TString(pattern(idx + 1, pattern.Length())).Atof();
4319 bool foundArg = false;
4320 for (auto par : _pars.argList()) {
4321 if (TString(par->GetName()).Contains(TRegexp(pat, true))) {
4322 foundArg = true;
4323 par->setAttribute("Constant", true);
4324 if (!std::isnan(val)) {
4325 dynamic_cast<RooAbsRealLValue *>(par)->setVal(val);
4326 }
4327 }
4328 }
4329 if (!foundArg) {
4330 throw std::runtime_error(std::string("Unrecognised parameter: ") + pat.Data());
4331 }
4332 }
4333
4334 // parse options
4335 TStringToken pattern2(options, ",");
4337 while (pattern2.NextToken()) {
4338 auto idx = pattern2.Index('=');
4339 TString pat = (idx == -1) ? TString(pattern2) : TString(pattern2(0, idx));
4340 TString val = TString(pattern2(idx + 1, pattern2.Length()));
4341 if (auto o = defaultOpts->FindObject(pat)) {
4342 defaultOpts->Remove(o);
4343 delete o;
4344 }
4345 defaultOpts->Add(new RooCmdArg(pat, val.IsDec() ? val.Atoi() : 0, 0, val.IsFloat() ? val.Atof() : 0., 0.,
4346 val.IsAlpha() ? val : nullptr));
4347 }
4348
4349 auto hs = nll(dsetName.Data(), *defaultOpts).hypoSpace(sXvar);
4350 hs.SetName(TUUID().AsString());
4351 if (nToys) {
4352 sWhat += " toys";
4353 if (nToys > 0) {
4354 sWhat += TString::Format("=%g", nToys);
4355 }
4356 }
4357 hs.SetTitle(sWhat + " scan" + ((dsetName != "") ? TString::Format(" [data=%s]", dsetName.Data()) : ""));
4358 int scanStatus = hs.scan(sWhat + " visualize", nBinsX, lowX, highX);
4359 if (scanStatus != 0) {
4360 new TGMsgBox(
4361 gClient->GetRoot(),
4362 (gROOT->GetListOfBrowsers()->At(0))
4363 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4364 : gClient->GetRoot(),
4365 "Scan Finished with Bad Status Code",
4366 TString::Format("%s\nData = %s\nScan Status Code = %d", hs.GetName(), dsetName.Data(), scanStatus),
4368 }
4369 if (ws()) {
4370 if (auto res = hs.result())
4371 ws()->import(*res);
4372 }
4373
4374 _pars.argList() = *snap; // restore pars
4375
4376 TBrowser *b = nullptr;
4377 for (auto a : *gROOT->GetListOfBrowsers()) {
4378 b = dynamic_cast<TBrowser *>(a);
4379 if (b && GetTreeItem(b)) {
4380 break;
4381 }
4382 }
4383 if (b) {
4384 auto p = GetTreeItem(b);
4385 while (p) {
4386 if (TString(p->GetText()).BeginsWith("RooWorkspace::")) {
4387 // found the workspace ... refresh this node, and if there's a scans node, refresh that
4388 if (auto bi = dynamic_cast<TRootBrowser *>(b->GetBrowserImp())) {
4389 if (auto fb = dynamic_cast<TGFileBrowser *>(bi->GetActBrowser())) {
4390 fb->DoubleClicked(p, 0);
4391 if (auto first = p->GetFirstChild()) {
4392 do {
4393 if (first->IsOpen() && TString(first->GetText()) == "scans") {
4394 fb->DoubleClicked(first, 0);
4395 }
4396 } while ((first = first->GetNextSibling()));
4397 }
4398 }
4399 }
4400 break;
4401 } else {
4402 p = p->GetParent();
4403 }
4404 }
4405 }
4406
4407 } catch (const std::exception &e) {
4408 new TGMsgBox(
4409 gClient->GetRoot(),
4410 (gROOT->GetListOfBrowsers()->At(0))
4411 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4412 : gClient->GetRoot(),
4413 "Exception", e.what(), kMBIconExclamation);
4414 }
4415}
4416
4417void xRooNode::_SetBinContent_(int bin, double value, const char *par, double parVal)
4418{
4419 try {
4420 SetBinContent(bin, value, strlen(par) > 0 ? par : nullptr, parVal);
4421 } catch (const std::exception &e) {
4422 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
4423 kMBIconExclamation); // deletes self on dismiss?
4424 }
4425}
4426
4428{
4429 try {
4430#if ROOT_VERSION_CODE > ROOT_VERSION(6, 29, 00)
4431 // if this is a collection of values, populate a TF1 and display as a dialog
4432 if (!get() && TString(GetName()).BeginsWith("!")) {
4433 browse();
4434 RooArgList args;
4435 for (auto a : *this) {
4436 if (auto arg = a->get<RooRealVar>())
4437 args.add(*arg);
4438 }
4439 TF1 f(GetName(), 0.0, 1.0, std::min(int(args.size()), 10));
4440 int i = 0;
4441 int j = 0;
4442 for (auto c : args) {
4443 j++;
4444 if (j < value) {
4445 continue;
4446 }
4447 auto v = dynamic_cast<RooRealVar *>(c);
4448 f.SetParName(i, c->GetName());
4449 if (v) {
4450 f.SetParLimits(i, v->getMin(), v->getMax());
4451 if (v->isConstant())
4452 f.FixParameter(i, v->getVal());
4453 else {
4454 f.SetParameter(i, v->getVal());
4455 f.SetParError(i, v->getError());
4456 }
4457 }
4458 i++;
4459 if (i == 10) {
4460 break; // max 10 pars shown
4461 }
4462 }
4463 int ret = 0;
4465 gClient->GetDefaultRoot(),
4466 (gROOT->GetListOfBrowsers()->At(0))
4467 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4468 : gClient->GetDefaultRoot(),
4469 &f, nullptr, &ret);
4470 if (ret) {
4471 // user has changed parameter values etc, propagate back to parameters
4472 for (i = 0; i < f.GetNpar(); i++) {
4473 auto c = args.find(f.GetParName(i));
4474 auto v = dynamic_cast<RooRealVar *>(c);
4475 if (v) {
4476 v->setVal(f.GetParameter(i));
4477 double low, high;
4478 f.GetParLimits(i, low, high);
4479 if (low == high) {
4480 v->setConstant(low); // if low==high==0 then is not marked constant
4481 } else {
4482 v->setRange(low, high);
4483 }
4484 }
4485 }
4486 }
4487 return;
4488 }
4489#endif
4490
4491 if (!SetContent(value))
4492 throw std::runtime_error("Failed to SetContent");
4493 } catch (const std::exception &e) {
4494 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
4495 kMBIconExclamation); // deletes self on dismiss?
4496 }
4497}
4498
4499bool xRooNode::SetBinContent(int bin, double value, const char *par, double parVal)
4500{
4501
4502 // create if needed
4503 if (!get()) {
4504 if (fParent && !find(GetName())) {
4505 // if have a binning we create a histogram to match it
4506 if (auto ax = GetXaxis(); ax) {
4507 std::shared_ptr<TH1D> h;
4508 auto _b = dynamic_cast<Axis2 *>(ax)->binning();
4509 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
4510 if (_b->isUniform()) {
4511 h.reset(new TH1D(GetName(), GetTitle(), _b->numBins(), _b->lowBound(), _b->highBound()));
4512 } else {
4513 h.reset(new TH1D(GetName(), GetTitle(), _b->numBins(), _b->array()));
4514 }
4515 h->SetOption("nostyle"); // don't transfer style when added
4516 h->GetXaxis()->SetName(TString::Format("%s;%s", ax->GetParent()->GetName(), ax->GetName()));
4517 fComp = h;
4518 }
4519 fComp = fParent->Add(*this, "sample").fComp;
4520 }
4521 }
4522
4523 // if it's a RooProduct locate child with the same name
4524 if (get<RooProduct>()) {
4525 return factors()[GetName()]->SetBinContent(bin, value, par, parVal);
4526 }
4527
4528 if (get<RooAbsData>()) {
4529 if (auto _data = get<RooDataSet>(); _data) {
4530 auto _ax = (bin) ? GetXaxis() : nullptr;
4531 if (!_ax && bin) {
4532 throw std::runtime_error("Cannot determine binning to fill data");
4533 }
4534 if (_ax && _ax->GetNbins() < bin) {
4535 throw std::out_of_range(TString::Format("%s range %s only has %d bins", _ax->GetParent()->GetName(),
4536 _ax->GetName(), _ax->GetNbins()));
4537 }
4538 RooArgSet obs;
4539
4540 TString cut = "";
4541
4542 for (auto _c : coords()) { // coords() moves vars to their respective coordinates too
4543 if (auto _cat = _c->get<RooAbsCategoryLValue>(); _cat) {
4544 if (cut != "")
4545 cut += " && ";
4546 cut += TString::Format("%s==%d", _cat->GetName(), _cat->getCurrentIndex());
4547 obs.add(*_cat); // note: if we ever changed coords to return clones, would need to keep coords alive
4548 } else if (auto _rv = _c->get<RooAbsRealLValue>(); _rv) {
4549 // todo: check coordRange is a single range rather than multirange
4550 if (cut != "")
4551 cut += " && ";
4552 cut +=
4553 TString::Format("%s>=%f&&%s<%f", _rv->GetName(), _rv->getMin(_rv->getStringAttribute("coordRange")),
4554 _rv->GetName(), _rv->getMax(_rv->getStringAttribute("coordRange")));
4555 obs.add(*_rv); // note: if we ever changed coords to return clones, would need to keep coords alive
4556 } else {
4557 throw std::runtime_error("SetBinContent of data: Unsupported coordinate type");
4558 }
4559 }
4560
4561 RooFormulaVar cutFormula("cut1", cut, obs); // doing this to avoid complaints about unused vars
4562 RooFormulaVar icutFormula("icut1", TString::Format("!(%s)", cut.Data()), obs);
4563
4564 TString cut2;
4565 if (_ax) {
4566 cut2 = TString::Format("%s >= %f && %s < %f", _ax->GetParent()->GetName(), _ax->GetBinLowEdge(bin),
4567 _ax->GetParent()->GetName(), _ax->GetBinUpEdge(bin));
4568 obs.add(*dynamic_cast<RooAbsArg *>(_ax->GetParent()));
4569 } else {
4570 cut2 = "1==1";
4571 }
4572 RooFormulaVar cutFormula2("cut2", cut + " && " + cut2, obs);
4573 RooFormulaVar icutFormula2("icut2", TString::Format("!(%s && %s)", cut.Data(), cut2.Data()), obs);
4574
4575 // // go up through parents looking for slice obs
4576 // auto _p = fParent;
4577 // while(_p) {
4578 // TString pName(_p->GetName());
4579 // if (auto pos = pName.Index('='); pos != -1) {
4580 // if(auto _obs = _p->getObject<RooAbsLValue>(pName(0,pos)); _obs) {
4581 // if(auto _cat = dynamic_cast<RooAbsCategoryLValue*>(_obs.get()); _cat) {
4582 // _cat->setLabel(pName(pos+1,pName.Length()));
4583 // cut += TString::Format("%s%s==%d", (cut=="")?"":" && ",_cat->GetName(),
4584 // _cat->getCurrentIndex());
4585 // } else if(auto _var = dynamic_cast<RooAbsRealLValue*>(_obs.get()); _var) {
4586 // _var->setVal(TString(pName(pos+1,pName.Length())).Atof());
4587 // // TODO: Cut for this!!
4588 // }
4589 // obs.add(*dynamic_cast<RooAbsArg*>(_obs.get()));
4590 // } else {
4591 // throw std::runtime_error("Unknown observable, could not find");
4592 // }
4593 // }
4594 // _p = _p->fParent;
4595 // }
4596
4597 // add observables to dataset if necessary
4598 RooArgSet l(obs);
4599 l.remove(*_data->get(), true, true);
4600 if (!l.empty()) {
4601 // addColumns method is buggy: https://github.com/root-project/root/issues/8787
4602 // incredibly though, addColumn works??
4603 for (auto &x : l) {
4604 _data->addColumn(*x);
4605 }
4606 // instead create a copy dataset and merge it into current
4607 // cant use merge because it drops weightVar
4608 /*RooDataSet tmp("tmp","tmp",l);
4609 for(int i=0;i<_data->numEntries();i++) tmp.add(l);
4610 _data->merge(&tmp);*/
4611 // delete _data->addColumns(l);
4612 }
4613 // before adding, ensure range is good to cover
4614 for (auto &o : obs) {
4615 if (auto v = dynamic_cast<RooRealVar *>(o); v) {
4616 if (auto dv = dynamic_cast<RooRealVar *>(_data->get()->find(v->GetName())); dv) {
4617 if (v->getMin() < dv->getMin())
4618 dv->setMin(v->getMin());
4619 if (v->getMax() > dv->getMax())
4620 dv->setMax(v->getMax());
4621 }
4622 } else if (auto c = dynamic_cast<RooCategory *>(o); c) {
4623 if (auto dc = dynamic_cast<RooCategory *>(_data->get()->find(c->GetName())); dc) {
4624 if (!dc->hasLabel(c->getCurrentLabel())) {
4625 dc->defineType(c->getCurrentLabel(), c->getCurrentIndex());
4626 }
4627 }
4628 }
4629 }
4630
4631 // using SetBinContent means dataset must take on a binned form at these coordinates
4632 // if number of entries doesnt match number of bins then will 'bin' the data
4633 if (bin) {
4634 if (auto _nentries = std::unique_ptr<RooAbsData>(_data->reduce(cutFormula))->numEntries();
4635 _nentries != _ax->GetNbins()) {
4636 auto _contents = GetBinContents(1, _ax->GetNbins());
4637
4638 if (_nentries > 0) {
4639 Info("SetBinContent", "Binning %s in channel: %s", GetName(), cut.Data());
4640 auto _reduced = std::unique_ptr<RooAbsData>(_data->reduce(icutFormula));
4641 _data->reset();
4642 for (int j = 0; j < _reduced->numEntries(); j++) {
4643 auto _obs = _reduced->get(j);
4644 _data->add(*_obs, _reduced->weight());
4645 }
4646 }
4647 for (int i = 1; i <= _ax->GetNbins(); i++) {
4648 // can skip over the bin we will be setting to save a reduce step below
4649 if (i == bin)
4650 continue;
4651 dynamic_cast<RooAbsLValue *>(_ax->GetParent())->setBin(i - 1, _ax->GetName());
4652 _data->add(obs, _contents.at(i - 1));
4653 }
4654 }
4655 }
4656 // remove existing entries
4657 if (std::unique_ptr<RooAbsData>(_data->reduce(cutFormula2))->numEntries() > 0) {
4658 auto _reduced = std::unique_ptr<RooAbsData>(_data->reduce(icutFormula2));
4659 _data->reset();
4660 for (int j = 0; j < _reduced->numEntries(); j++) {
4661 auto _obs = _reduced->get(j);
4662 _data->add(*_obs, _reduced->weight());
4663 }
4664 }
4665 if (_ax)
4666 dynamic_cast<RooAbsLValue *>(_ax->GetParent())->setBin(bin - 1, _ax->GetName());
4667 _data->add(obs, value);
4668 if (auto bb = getBrowsable(".sourceds"))
4669 return bb->SetBinContent(bin, value, par, parVal); // apply to source ds if we have one
4670 return true;
4671
4672 } else if (get<RooDataHist>()) {
4673 throw std::runtime_error("RooDataHist not supported yet");
4674 }
4675 }
4676
4677 if (auto _varies = variations(); !_varies.empty() || (par && strlen(par))) {
4678 if (!par || strlen(par) == 0) {
4679 return _varies["nominal"]->SetBinContent(bin, value, par, parVal);
4680 } else if (auto it = _varies.find(Form("%s=%g", par, parVal)); it) {
4681 return it->SetBinContent(bin, value);
4682 } else {
4683 // need to create the variation : note - if no variations existed up to now this will inject a new node
4684 // so we should redirect ourself to the new node
4685 // TODO: Do we need to redirect parents?
4686 TString s = Form("%s=%g", par, parVal);
4687 return Vary(s.Data()).SetBinContent(bin, value);
4688 }
4689 }
4690
4691 auto o = get();
4692 if (auto p = dynamic_cast<RooRealVar *>(o); p) {
4693 if (!par || strlen(par) == 0) {
4694 if (p->getMax() < value)
4695 p->setMax(value);
4696 if (p->getMin() > value)
4697 p->setMin(value);
4698 p->setVal(value);
4699 sterilize();
4700 return true;
4701 }
4702
4703 } else if (auto c = dynamic_cast<RooConstVar *>(o); c) {
4704
4705 // if parent is a FlexibleInterpVar, change the value in that .
4706 if (strcmp(c->GetName(), Form("%g", c->getVal())) == 0) {
4707 c->SetNameTitle(Form("%g", value), Form("%g", value));
4708 }
4709#if ROOT_VERSION_CODE < ROOT_VERSION(6, 24, 00)
4710 c->_value = value; // in future ROOT versions there is a changeVal method!
4711#else
4712 c->changeVal(value);
4713#endif
4714
4716 fParent->Vary(*this);
4717 }
4718
4719 sterilize();
4720 return true;
4721 } else if (auto f = dynamic_cast<RooHistFunc *>(o); f) {
4722 auto bin_pars = f->dataHist().get(bin - 1);
4723 if (f->getAttribute("density")) {
4724 value /= f->dataHist().binVolume(*bin_pars);
4725 }
4726 f->dataHist().set(*bin_pars, value);
4727 f->setValueDirty();
4728
4729 if (auto otherfName = f->getStringAttribute("symmetrized_by"); otherfName) {
4730 // broken symmetry, so update flags ...
4731 f->setStringAttribute("symmetrized_by", nullptr);
4732 if (auto x = getObject<RooAbsArg>(otherfName); x) {
4733 x->setStringAttribute("symmetrizes", nullptr);
4734 x->setStringAttribute("symmetrize_nominal", nullptr);
4735 }
4736 } else if (auto otherfName2 = f->getStringAttribute("symmetrizes"); otherfName2) {
4737 auto nomf = getObject<RooHistFunc>(f->getStringAttribute("symmetrize_nominal"));
4739 if (nomf && otherf) {
4740 otherf->dataHist().set(*bin_pars, 2 * nomf->dataHist().weight(bin - 1) - value);
4741 otherf->setValueDirty();
4742 }
4743 }
4744 sterilize();
4745 return true;
4746 } else if (auto f2 = dynamic_cast<RooStats::HistFactory::FlexibleInterpVar *>(o); f2) {
4747 // changing nominal value
4748 f2->setNominal(value);
4749 }
4750 throw std::runtime_error(TString::Format("unable to set bin content of %s", GetPath().c_str()));
4751}
4752
4753bool xRooNode::SetBinData(int bin, double value, const xRooNode &data)
4754{
4755 if (data.get<RooAbsData>()) {
4756 // attach as a child before calling datasets(), so that is included in the list
4757 push_back(std::make_shared<xRooNode>(data));
4758 }
4759 auto node = datasets()[data.GetName()];
4760 if (data.get<RooAbsData>()) {
4761 // remove the child we attached
4762 resize(size() - 1);
4763 }
4764 return node->SetBinContent(bin, value);
4765}
4766
4767bool xRooNode::SetData(const TObject &obj, const xRooNode &data)
4768{
4769 if (data.get<RooAbsData>()) {
4770 // attach as a child before calling datasets(), so that is included in the list
4771 push_back(std::make_shared<xRooNode>(data));
4772 }
4773 auto node = datasets()[data.GetName()];
4774 if (data.get<RooAbsData>()) {
4775 // remove the child we attached
4776 resize(size() - 1);
4777 }
4778 return node->SetContents(obj);
4779}
4780
4781bool xRooNode::SetBinError(int bin, double value)
4782{
4783
4784 // if it's a RooProduct locate child with the same name
4785 if (get<RooProduct>()) {
4786 return factors()[GetName()]->SetBinError(bin, value);
4787 }
4788
4789 if (auto _varies = variations(); !_varies.empty()) {
4790 return _varies["nominal"]->SetBinError(bin, value);
4791 }
4792
4793 auto o = get();
4794
4795 if (auto f = dynamic_cast<RooHistFunc *>(o); f) {
4796
4797 // if (f->getAttribute("density")) { value /= f->dataHist().binVolume(*bin_pars); } - commented out because DON'T
4798 // convert .. sumw and sumw2 attributes will be stored not as densities
4799
4800 // NOTE: Can only do this because factors() makes parents of its children it's own parent (it isn't the parent)
4801 // If ever make factors etc part of the parentage then this would need tweaking to get to the true parent
4802 // find first parent that is a RooProduct, that is where the statFactor would live
4803 // stop as soon as we reach pdf object
4804 auto _prodParent = fParent;
4805 while (_prodParent && !_prodParent->get<RooProduct>() && !_prodParent->get<RooAbsPdf>()) {
4806 if (_prodParent->get<PiecewiseInterpolation>() && strcmp(GetName(), "nominal")) {
4807 _prodParent.reset();
4808 break; // only the 'nominal' variation can look for a statFactor outside the variation container
4809 }
4810 _prodParent = _prodParent->fParent;
4811 }
4812 auto _f_stat =
4813 (_prodParent && !_prodParent->get<RooAbsPdf>()) ? _prodParent->factors().find("statFactor") : nullptr;
4814 auto f_stat = (_f_stat) ? _f_stat->get<ParamHistFunc>() : nullptr;
4815 if (_f_stat && _f_stat->get() && !f_stat) {
4816 throw std::runtime_error("stat factor must be a paramhistfunc");
4817 }
4818
4819 // stat uncertainty lives in the "statFactor" factor, each sample has its own one,
4820 // but they can share parameters
4821 if (!f_stat) {
4822 if (value == 0)
4823 return true;
4825 for (auto &p : xRooNode("tmp", *f, std::shared_ptr<xRooNode>(nullptr)).vars()) {
4826 if (parNames != "")
4827 parNames += ",";
4828 parNames += p->get()->GetName();
4829 }
4830 auto h = std::unique_ptr<TH1>(f->dataHist().createHistogram(parNames
4831#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 27, 00)
4832 ,
4834#endif
4835 ));
4836 h->Reset();
4837 h->SetName("statFactor");
4838 h->SetTitle(TString::Format("StatFactor of %s", f->GetTitle()));
4839 h->SetOption("blankshape");
4840
4841 // multiply parent if is nominal
4842 auto toMultiply = this;
4843 if (strcmp(GetName(), "nominal") == 0 && fParent && fParent->get<PiecewiseInterpolation>())
4844 toMultiply = fParent.get();
4845
4846 f_stat = dynamic_cast<ParamHistFunc *>(toMultiply->Multiply(*h).get());
4847 if (!f_stat) {
4848 throw std::runtime_error("Failed creating stat shapeFactor");
4849 }
4850 }
4851
4852 auto phf = f_stat;
4853
4854 TString prefix = f->getStringAttribute("statPrefix");
4855 if (value && prefix == "") {
4856 // find the first parent that can hold components (RooAddPdf, RooRealSumPdf, RooAddition, RooWorkspace) ... use
4857 // that name for the stat factor
4858 auto _p = fParent;
4859 while (_p && !(_p->get()->InheritsFrom("RooRealSumPdf") || _p->get()->InheritsFrom("RooAddPdf") ||
4860 _p->get()->InheritsFrom("RooWorkspace") || _p->get()->InheritsFrom("RooAddition"))) {
4861 _p = _p->fParent;
4862 }
4863 prefix = TString::Format("stat_%s", (_p && _p->get<RooAbsReal>()) ? _p->get()->GetName() : f->GetName());
4864 }
4865 auto newVar = (value == 0) ? getObject<RooRealVar>("1")
4866 : acquire<RooRealVar>(Form("%s_bin%d", prefix.Data(), bin),
4867 Form("#gamma^{%s}_{%d}", prefix.Data(), bin), 1);
4868#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
4869 RooArgList &pSet = phf->_paramSet;
4870#else
4871 RooArgList &pSet = const_cast<RooArgList &>(phf->paramList());
4872#endif
4873 auto var = dynamic_cast<RooRealVar *>(&pSet[bin - 1]);
4874
4875 if (newVar.get() != var) {
4876 // need to swap out var for newVar
4877 // replace ith element in list with new func, or inject into RooProduct
4879 for (std::size_t i = 0; i < pSet.size(); i++) {
4880 if (int(i) != bin - 1) {
4881 all.add(*pSet.at(i));
4882 } else {
4883 all.add(*newVar);
4884 }
4885 }
4886 pSet.removeAll();
4887 pSet.add(all);
4888 }
4889
4890 xRooNode v((value == 0) ? *var : *newVar, *this);
4891 auto rrv = dynamic_cast<RooRealVar *>(v.get());
4892 if (strcmp(rrv->GetName(), "1") != 0) {
4893 TString origName = (f->getStringAttribute("origName")) ? f->getStringAttribute("origName") : GetName();
4894 rrv->setStringAttribute(Form("sumw2_%s", origName.Data()), TString::Format("%f", pow(value, 2)));
4895 auto bin_pars = f->dataHist().get(bin - 1);
4896 auto _binContent = f->dataHist().weight();
4897 if (f->getAttribute("density")) {
4898 _binContent *= f->dataHist().binVolume(*bin_pars);
4899 }
4900 rrv->setStringAttribute(Form("sumw_%s", origName.Data()), TString::Format("%f", _binContent));
4901 double sumw2 = 0;
4902 double sumw = 0;
4903 for (auto &[s, sv] : rrv->stringAttributes()) {
4904 if (s.find("sumw_") == 0) {
4905 sumw += TString(sv).Atof();
4906 } else if (s.find("sumw2_") == 0) {
4907 sumw2 += TString(sv).Atof();
4908 }
4909 }
4910 if (sumw2 && sumw2 != std::numeric_limits<double>::infinity()) {
4911 double tau = pow(sumw, 2) / sumw2;
4912 rrv->setError((tau < 1e-15) ? 1e15 : (/*rrv->getVal()*/ 1. / sqrt(tau))); // not sure why was rrv->getVal()?
4913 rrv->setConstant(false);
4914 // parameter must be constrained
4915 auto _constr = v.constraints();
4916 // std::cout << " setting constraint " << v.GetName() << " nomin=" << tau << std::endl;
4917 if (_constr.empty()) {
4918 rrv->setStringAttribute("boundConstraint", _constr.Add("poisson").get()->GetName());
4919 } else {
4920 auto _glob = _constr.at(0)->obs().at(0)->get<RooRealVar>();
4921 // TODO: Update any globs snapshots that are designed to match the nominal
4922 _glob->setStringAttribute("nominal", TString::Format("%f", tau));
4923 double _min = tau * (1. - 5. * sqrt(1. / tau));
4924 double _max = tau * (1. + 5. * sqrt(1. / tau));
4925 _glob->setRange(_min, _max);
4926 _glob->setVal(tau);
4927 _constr.at(0)->pp().at(0)->SetBinContent(0, tau);
4928 rrv->setStringAttribute("boundConstraint", _constr.at(0)->get()->GetName());
4929 }
4930 rrv->setRange(std::max((1. - 5. * sqrt(1. / tau)), 1e-15), 1. + 5. * sqrt(1. / tau));
4931 } else {
4932 // remove constraint
4933 if (auto _constr = v.constraints(); !_constr.empty()) {
4934 v.constraints().Remove(*_constr.at(0));
4935 }
4936 // set const if sumw2 is 0 (i.e. no error)
4937 rrv->setVal(1);
4938 rrv->setError(0);
4939 rrv->setConstant(sumw2 == 0);
4940 }
4941 }
4942
4943 return true;
4944 }
4945
4946 throw std::runtime_error(TString::Format("%s SetBinError failed", GetName()));
4947}
4948
4949std::shared_ptr<xRooNode> xRooNode::at(const std::string &name, bool browseResult) const
4950{
4951 auto res = find(name, browseResult);
4952 if (res == nullptr)
4953 throw std::out_of_range(name + " does not exist");
4954 return res;
4955}
4956
4957////////////////////////////////////////////////////////////////////////////////
4958/// The RooWorkspace this node belong to, if any
4959
4961{
4962 if (auto _w = get<RooWorkspace>(); _w)
4963 return _w;
4964 if (auto a = get<RooAbsArg>(); a && GETWS(a)) {
4965 return GETWS(a);
4966 }
4967 if (fParent)
4968 return fParent->ws();
4969 return nullptr;
4970}
4971
4973{
4974
4975 xRooNode out(".constraints", nullptr, *this);
4976
4977 std::function<RooAbsPdf *(const xRooNode &n, RooAbsArg &par, std::set<RooAbsPdf *> ignore)> getConstraint;
4978 getConstraint = [&](const xRooNode &n, RooAbsArg &par, std::set<RooAbsPdf *> ignore) {
4979 if (auto _pdf = n.get<RooAbsPdf>()) {
4980 if (ignore.count(_pdf))
4981 return (RooAbsPdf *)nullptr;
4982 ignore.insert(_pdf);
4983 }
4984 auto o = n.get<RooProdPdf>();
4985 if (!o) {
4986 if (n.get<RooSimultaneous>()) {
4987 // check all channels for a constraint if is simultaneous
4988 for (auto &c : n.bins()) {
4989 if (auto oo = getConstraint(*c, par, ignore); oo) {
4990 return oo;
4991 }
4992 }
4993 return (RooAbsPdf *)nullptr;
4994 } else if (n.get<RooAbsPdf>() && n.fParent && n.fParent->get<RooWorkspace>()) {
4995 // reached top-level pdf, which wasn't a simultaneous, so stop here
4996 return (RooAbsPdf *)nullptr;
4997 } else if (auto _ws = n.get<RooWorkspace>(); _ws) {
4998 // reached a workspace, check for any pdf depending on parameter that isnt the ignore
4999 for (auto p : _ws->allPdfs()) {
5000 if (ignore.count(static_cast<RooAbsPdf *>(p)))
5001 continue;
5002 if (p->dependsOn(par)) {
5003 out.emplace_back(std::make_shared<xRooNode>(par.GetName(), *p, *this));
5004 }
5005 }
5006 }
5007 if (!n.fParent)
5008 return (RooAbsPdf *)nullptr;
5009 return getConstraint(*n.fParent, par, ignore);
5010 }
5011 for (auto p : o->pdfList()) {
5012 if (ignore.count(static_cast<RooAbsPdf *>(p)))
5013 continue;
5014 if (p->dependsOn(par)) {
5015 out.emplace_back(std::make_shared<xRooNode>(par.GetName(), *p, *this));
5016 }
5017 }
5018 return (RooAbsPdf *)nullptr;
5019 };
5020
5021 for (auto &p : vars()) {
5022 auto v = dynamic_cast<RooAbsReal *>(p->get());
5023 if (!v)
5024 continue;
5025 if (v->getAttribute("Constant") && v != get<RooAbsReal>())
5026 continue; // skip constants unless we are getting the constraints of a parameter itself
5027 if (v->getAttribute("obs"))
5028 continue; // skip observables ... constraints constrain pars not obs
5029 getConstraint(*this, *v, {get<RooAbsPdf>()});
5030 /*if (auto c = ; c) {
5031 out.emplace_back(std::make_shared<Node2>(p->GetName(), *c, *this));
5032 }*/
5033 }
5034
5035 // finish by removing any constraint that contains another constraint for the same par
5036 // and consolidate common pars
5037 auto it = out.std::vector<std::shared_ptr<xRooNode>>::begin();
5038 while (it != out.std::vector<std::shared_ptr<xRooNode>>::end()) {
5039 bool removeIt = false;
5040 for (auto &c : out) {
5041 if (c.get() == it->get())
5042 continue;
5043 if ((*it)->get<RooAbsArg>()->dependsOn(*c->get<RooAbsArg>())) {
5044 removeIt = true;
5045 std::set<std::string> parNames;
5046 std::string _cName = c->GetName();
5047 do {
5048 parNames.insert(_cName.substr(0, _cName.find(';')));
5049 _cName = _cName.substr(_cName.find(';') + 1);
5050 } while (_cName.find(';') != std::string::npos);
5051 parNames.insert(_cName);
5052 _cName = it->get()->GetName();
5053 do {
5054 parNames.insert(_cName.substr(0, _cName.find(';')));
5055 _cName = _cName.substr(_cName.find(';') + 1);
5056 } while (_cName.find(';') != std::string::npos);
5057 parNames.insert(_cName);
5058 _cName = "";
5059 for (auto &x : parNames) {
5060 if (!_cName.empty())
5061 _cName += ";";
5062 _cName += x;
5063 }
5064 c->TNamed::SetName(_cName.c_str());
5065 break;
5066 }
5067 }
5068 if (removeIt) {
5069 it = out.erase(it);
5070 } else {
5071 ++it;
5072 }
5073 }
5074
5075 // if getting constraints of a fundamental then use the constraint names instead of the par name (because would be
5076 // all same otherwise)
5077 if (get<RooAbsArg>() && get<RooAbsArg>()->isFundamental()) {
5078 for (auto &o : out) {
5079 o->TNamed::SetName(o->get()->GetName());
5080 }
5081 }
5082
5083 return out;
5084}
5085
5086std::shared_ptr<TObject> xRooNode::convertForAcquisition(xRooNode &acquirer, const char *opt) const
5087{
5088
5089 TString sOpt(opt);
5090 sOpt.ToLower();
5092 if (sOpt == "func")
5093 sName = TString("factory:") + sName;
5094
5095 // if arg is a histogram, will acquire it as a RooHistFunc unless no conversion
5096 // todo: could flag not to convert
5097 if (auto h = get<TH1>(); h) {
5098 TString sOpt2(h->GetOption());
5099 std::map<std::string, std::string> stringAttrs;
5100 while (sOpt2.Contains("=")) {
5101 auto pos = sOpt2.Index("=");
5102 auto start = sOpt2.Index(";") + 1;
5103 if (start > pos)
5104 start = 0;
5105 auto end = sOpt2.Index(";", pos);
5106 if (end == -1)
5107 end = sOpt2.Length();
5108 stringAttrs[sOpt2(start, pos - start)] = sOpt2(pos + 1, end - pos - 1);
5109 sOpt2 = TString(sOpt2(0, start)) + TString(sOpt2(end + 1, sOpt2.Length()));
5110 }
5113 if (origName.BeginsWith(';'))
5114 origName = origName(1, origName.Length());
5115 if (newObjName.BeginsWith(';')) {
5116 newObjName =
5117 newObjName(1, newObjName.Length()); // special case if starts with ';' then don't create a fancy name
5118 } else if (acquirer.get() && !acquirer.get<RooWorkspace>()) {
5120 "%s_%s", (acquirer.mainChild().get()) ? acquirer.mainChild()->GetName() : acquirer->GetName(),
5121 newObjName.Data());
5122 }
5123 // can convert to a RooHistFunc, or RooParamHist if option contains 'shape'
5124 TString varName = h->GetXaxis()->GetName();
5125 std::string binningName = newObjName.Data();
5126 if (auto pos = varName.Index(';'); pos != -1) {
5127 binningName = varName(pos + 1, varName.Length());
5128 varName = varName(0, pos);
5129 }
5130
5131 if (varName == "xaxis" &&
5132 !acquirer.get<RooSimultaneous>()) { // default case, try to take axis var and binning from the acquirer
5133 if (auto ax = acquirer.GetXaxis(); ax) {
5134 varName = ax->GetParent()->GetName();
5135 // TODO: check the binning is consistent before using - at least will check nBins below
5136 binningName = ax->GetName();
5137 } else if (acquirer.obs().size() == 1)
5138 varName = acquirer.obs().at(0)->get()->GetName(); // TODO what if no obs but Xaxis var is defined?
5139 }
5140 auto x = acquirer.acquire<RooRealVar>(varName, h->GetXaxis()->GetTitle(), h->GetXaxis()->GetXmin(),
5141 h->GetXaxis()->GetXmax());
5142 if (x->getMin() > h->GetXaxis()->GetXmin())
5143 x->setMin(h->GetXaxis()->GetXmin());
5144 if (x->getMax() < h->GetXaxis()->GetXmax())
5145 x->setMax(h->GetXaxis()->GetXmax());
5146 if (!x->hasBinning(binningName.c_str())) {
5147 if (h->GetXaxis()->IsVariableBinSize()) {
5148 x->setBinning(RooBinning(h->GetNbinsX(), h->GetXaxis()->GetXbins()->GetArray()), binningName.c_str());
5149 } else {
5150 x->setBinning(
5151 RooUniformBinning(h->GetXaxis()->GetXmin(), h->GetXaxis()->GetXmax(), h->GetXaxis()->GetNbins()),
5152 binningName.c_str());
5153 }
5154 x->getBinning(binningName.c_str()).SetTitle(h->GetXaxis()->GetTitle());
5155 if (x->getBinningNames().size() == 2) {
5156 // this was the first binning, so copy it over to be the default binning too
5157 x->setBinning(x->getBinning(binningName.c_str()));
5158 }
5159 } else {
5160 // TODO check binning is compatible with histogram
5161 if (x->getBinning(binningName.c_str()).numBins() != h->GetNbinsX()) {
5162 throw std::runtime_error(
5163 TString::Format("binning mismatch for binning %s of %s", binningName.c_str(), x->GetName()));
5164 }
5165 }
5166
5167 std::shared_ptr<RooAbsArg> _f;
5168
5169 // if acquirer is a RooSimultaneous, will use histogram to define a channel
5170 if (acquirer.get<RooSimultaneous>()) {
5171 _f = acquirer.acquireNew<RooProdPdf>(newObjName, (strlen(h->GetTitle())) ? h->GetTitle() : h->GetName(),
5172 RooArgList());
5173 for (auto &[k, v] : stringAttrs) {
5174 _f->setStringAttribute(k.c_str(), v.c_str());
5175 }
5176 x->setAttribute("obs", true);
5177 } else if (sOpt2.Contains("shape")) {
5178 RooArgList list;
5179 for (int i = 0; i < x->getBinning(binningName.c_str()).numBins(); i++) {
5180 std::shared_ptr<RooAbsArg> arg;
5181 if (sOpt2.Contains("blankshape")) {
5182 arg = acquirer.acquire2<RooAbsArg, RooRealVar>("1", "1", 1);
5183 } else {
5184 if (!h) {
5185 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1), "", 1);
5186 } else if (h->GetMinimumStored() != -1111 || h->GetMaximumStored() != -1111) {
5187 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1),
5188 TString::Format("%s_{%d}", h->GetTitle(), i + 1),
5189 h->GetBinContent(i + 1), h->GetMinimumStored(),
5190 h->GetMaximumStored());
5191 } else {
5192 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1),
5193 TString::Format("%s_{%d}", h->GetTitle(), i + 1),
5194 h->GetBinContent(i + 1));
5195 }
5196 }
5197 list.add(*arg);
5198 }
5199 // paramhistfunc requires the binnings to be loaded as default at construction time
5200 // so load binning temporarily
5201 auto tmp = dynamic_cast<RooAbsBinning *>(x->getBinningPtr(nullptr)->Clone());
5202 x->setBinning(x->getBinning(binningName.c_str()));
5203 _f = acquirer.acquireNew<ParamHistFunc>(newObjName, h->GetTitle(), *x, list);
5204#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
5205 dynamic_cast<ParamHistFunc *>(_f.get())->_paramSet.setName("paramSet"); // so can see when print
5206#else
5207 const_cast<RooArgList &>(dynamic_cast<ParamHistFunc *>(_f.get())->paramList())
5208 .setName("paramSet"); // so can see when print
5209#endif
5210 x->setBinning(*tmp); // restore binning
5211 delete tmp;
5212 for (auto &[k, v] : stringAttrs) {
5213 _f->setStringAttribute(k.c_str(), v.c_str());
5214 }
5215 } else {
5216 auto dh = acquirer.acquireNew<RooDataHist>(Form("hist_%s", newObjName.Data()), h->GetTitle(), *x,
5217 binningName.c_str() /* binning name*/);
5218 if (!dh) {
5219 throw std::runtime_error("Couldn't make data hist");
5220 }
5221 auto f = acquirer.acquireNew<RooHistFunc>(newObjName, h->GetTitle(), *x, *dh,
5222 0 /*interpolation order between bins*/);
5223 f->forceNumInt();
5224 f->setAttribute("autodensity"); // where it gets inserted will determine if this is a density or not
5225 _f = f;
5226
5227 for (auto &[k, v] : stringAttrs) {
5228 _f->setStringAttribute(k.c_str(), v.c_str());
5229 }
5230
5231 // need to do these settings here because used in the assignment step
5232 _f->setStringAttribute("xvar", x->GetName());
5233 _f->setStringAttribute("binning", binningName.c_str());
5234 if (strcmp(_f->GetName(), origName.Data()) && !_f->getStringAttribute("alias"))
5235 _f->setStringAttribute("alias", origName);
5236
5237 // copy values over using the assignment operator - may convert to a RooProduct if there are stat uncerts
5238 xRooNode tmp(h->GetName(), _f, acquirer);
5239 tmp = *h;
5240 _f = std::dynamic_pointer_cast<RooAbsArg>(tmp.fComp); // in case got upgrade to a RooProduct
5241 }
5242
5243 _f->setStringAttribute("xvar", x->GetName());
5244 _f->setStringAttribute("binning", binningName.c_str());
5245 // style(h); // will transfer styling to object if necessary - not doing because this method used with plane hists
5246 // frequently
5247 if (strcmp(_f->GetName(), origName.Data()) && !_f->getStringAttribute("alias"))
5248 _f->setStringAttribute("alias", origName);
5249
5250 fComp = _f;
5251 return _f;
5252 } else if (!get() && sName.BeginsWith("factory:") && acquirer.ws()) {
5253 TString s(sName);
5254 s = TString(s(8, s.Length()));
5255 fComp.reset(acquirer.ws()->factory(s), [](TObject *) {});
5256 if (fComp) {
5257 const_cast<xRooNode *>(this)->TNamed::SetName(fComp->GetName());
5258 }
5259 return fComp;
5260 }
5261
5262 return fComp;
5263}
5264
5265std::shared_ptr<TStyle> xRooNode::style(TObject *initObject, bool autoCreate) const
5266{
5267 return std::dynamic_pointer_cast<TStyle>(styles(initObject, autoCreate).fComp);
5268}
5269
5271{
5272 TString t = GetTitle();
5273
5274 auto arg = get<RooAbsArg>();
5275 if (!initObject && !arg && !gROOT->GetStyle(t)) {
5276 return nullptr;
5277 }
5278
5279 std::unique_ptr<TObject> argInitObject;
5280
5281 if (initObject) {
5282 t = (strlen(initObject->GetTitle())) ? initObject->GetTitle() : initObject->GetName();
5283 } else if (arg) {
5284 if (arg->getStringAttribute("style")) {
5285 t = arg->getStringAttribute("style");
5286 } else if (autoCreate) {
5287 // args will default to histo's object styling, whatever that currently may be
5288 argInitObject = std::make_unique<TH1D>(GetName(), GetTitle(), 1, 0, 1);
5289 initObject = argInitObject.get();
5290 } else {
5291 return nullptr;
5292 }
5293 }
5294
5295 std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case getObject has decided to
5296 // return the owning ptr (for some reason)
5297 if (!gROOT->GetStyle(t)) {
5298 if ((style = getObject<TStyle>(t.Data()))) {
5299 // loaded style (from workspace?) so put in list and use that
5300 gROOT->GetListOfStyles()->Add(style.get());
5301 } else {
5302 if (!autoCreate)
5303 return nullptr;
5304 // create new style - gets put in style list automatically so don't have to delete
5305 // acquire them so saved to workspaces for auto reload ...
5306 style = const_cast<xRooNode &>(*this).acquireNew<TStyle>(t.Data(),
5307 TString::Format("Style for %s component", t.Data()));
5308 if (auto x = dynamic_cast<TAttLine *>(initObject))
5309 ((TAttLine &)*style) = *x;
5310 if (auto x = dynamic_cast<TAttFill *>(initObject))
5311 ((TAttFill &)*style) = *x;
5312 if (auto x = dynamic_cast<TAttMarker *>(initObject))
5313 ((TAttMarker &)*style) = *x;
5314 gROOT->GetListOfStyles()->Add(style.get());
5315 }
5316 } else {
5317 style = std::shared_ptr<TStyle>(gROOT->GetStyle(t), [](TStyle *) {});
5318 }
5319
5320 if (arg && !arg->getStringAttribute("style")) {
5321 arg->setStringAttribute("style", style->GetName());
5322 }
5323
5324 return xRooNode(style, *this);
5325}
5326
5327std::shared_ptr<TObject> xRooNode::acquire(const std::shared_ptr<TObject> &arg, bool checkFactory, bool mustBeNew)
5328{
5329 if (!arg)
5330 return nullptr;
5331 if (!fAcquirer && !get<RooWorkspace>() && fParent)
5332 return fParent->acquire(arg, checkFactory, mustBeNew);
5333
5334 // if has a workspace and our object is the workspace or is in the workspace then add this object to workspace
5335 auto _ws = (fAcquirer) ? nullptr : ws();
5336 if (_ws && (get() == _ws || _ws->arg(GetName()) || (arg && strcmp(arg->GetName(), GetName()) == 0))) {
5338 RooMsgService::instance().setGlobalKillBelow(RooFit::WARNING);
5339 if (auto a = dynamic_cast<RooAbsArg *>(arg.get()); a) {
5340 auto out_arg = _ws->arg(a->GetName());
5341 TString aName = arg->GetName();
5342 int ii = 1;
5343 while (out_arg && mustBeNew) {
5344 a->SetName(TString::Format("%s_%d", aName.Data(), ii++));
5345 out_arg = _ws->arg(a->GetName());
5346 }
5347 if (aName != a->GetName())
5348 Warning("acquire", "Renaming to %s", a->GetName());
5349 if (!out_arg) {
5350 bool done = false;
5351 if (checkFactory) {
5352 if (auto res = _ws->factory(arg->GetName()); res) {
5353 a = res;
5354 done = true;
5355 }
5356 }
5357 if (!done && _ws->import(*a, RooFit::RecycleConflictNodes())) {
5358 if (GETWS(a) != _ws) {
5359 Info("acquire", "A copy of %s has been added to workspace %s", a->GetName(), _ws->GetName());
5360 }
5361 RooMsgService::instance().setGlobalKillBelow(msglevel);
5362 return nullptr;
5363 }
5364 // sanitizeWS(); // clears the caches that might exist up to now, as well interfere with getParameters calls
5365 std::set<std::string> setNames;
5366 for (auto &aa : GETWSSETS(_ws)) {
5367 if (TString(aa.first.c_str()).BeginsWith("CACHE_")) {
5368 setNames.insert(aa.first);
5369 }
5370 }
5371 for (auto &aa : setNames)
5372 ws()->removeSet(aa.c_str());
5373 out_arg = _ws->arg(a->GetName());
5374 if (GETWS(out_arg) != _ws) { // seems that when objects imported their ws isn't set
5375 out_arg->setWorkspace(*_ws);
5376 }
5377 // if any of the leaf nodes of the imported object have "global" label on them, ensure propagate to
5378 // "globalObservables" list if ws has one
5379 if (auto globs = const_cast<RooArgSet *>(ws()->set("globalObservables")); globs) {
5381 out_arg->leafNodeServerList(&leafs);
5382 std::unique_ptr<RooAbsCollection> globals(leafs.selectByAttrib("global", true));
5383 for (auto &aa : *globals) {
5384 if (!globs->contains(*aa)) {
5385 globs->add(*aa);
5386 }
5387 }
5388 }
5389 }
5390 RooMsgService::instance().setGlobalKillBelow(msglevel);
5391 return std::shared_ptr<TObject>(out_arg, [](TObject *) {});
5392 } else if (auto a2 = dynamic_cast<RooAbsData *>(arg.get()); a2) {
5393 if (_ws->import(*a2, RooFit::Embedded())) {
5394 RooMsgService::instance().setGlobalKillBelow(msglevel);
5395 return nullptr;
5396 }
5397 RooMsgService::instance().setGlobalKillBelow(msglevel);
5398 return std::shared_ptr<TObject>(_ws->embeddedData(arg->GetName()), [](TObject *) {});
5399 } else if (arg->InheritsFrom("TNamed")) { // can add any TNamed to a workspace
5400 TObject *out_arg = nullptr;
5401 if (auto fr = dynamic_cast<RooFitResult *>(&*arg); fr && fr->numStatusHistory() == 0) {
5402 // fit results without a status history are treated as snapshots
5403 out_arg = fr->Clone();
5404 const_cast<RooLinkedList &>(GETWSSNAPSHOTS(_ws)).Add(out_arg);
5405 } else {
5406 // ensure will have a unique name for import if must be new
5407 TNamed *aNamed = dynamic_cast<TNamed *>(arg.get());
5408 TString aName = arg->GetName();
5409 out_arg = _ws->genobj(arg->GetName());
5410 int ii = 1;
5411 while (aNamed && out_arg && mustBeNew) {
5412 aNamed->SetName(TString::Format("%s;%d", aName.Data(), ii++));
5413 out_arg = _ws->genobj(aNamed->GetName());
5414 }
5415 if (!out_arg) {
5416 if (aName != arg->GetName()) {
5417 Warning("acquire", "Renaming to %s", arg->GetName());
5418 }
5419 if (_ws->import(*arg, false /*replace existing*/)) {
5420 RooMsgService::instance().setGlobalKillBelow(msglevel);
5421 return nullptr;
5422 }
5423 out_arg = _ws->genobj(arg->GetName());
5424 }
5425 }
5426 RooMsgService::instance().setGlobalKillBelow(msglevel);
5427
5428 return std::shared_ptr<TObject>(out_arg, [](TObject *) {});
5429 }
5430 RooMsgService::instance().setGlobalKillBelow(msglevel);
5431 // Warning("acquire","Not implemented acquisition of object %s",arg->GetName());
5432 // return nullptr;
5433 }
5434 if (!mustBeNew && fProvider) {
5435 auto out = fProvider->getObject(arg->GetName(), arg->ClassName());
5436 if (out)
5437 return out;
5438 }
5439 auto _owned = find(".memory");
5440 if (!_owned) {
5441 _owned = emplace_back(std::make_shared<xRooNode>(".memory", nullptr, *this));
5442 }
5443 // look for exact name, dont use 'find' because doesnt work if trying to find "1" and it doesn't exist, will get back
5444 // idx 1 instead
5445 if (!mustBeNew) {
5446 for (auto &r : *_owned) {
5447 if (strcmp(r->GetName(), arg->GetName()) == 0 && strcmp(r->get()->ClassName(), arg->ClassName()) == 0) {
5448 return r->fComp;
5449 }
5450 }
5451 }
5452 if (!fProvider)
5453 std::cout << GetName() << " taking over " << arg->ClassName() << "::" << arg->GetName() << std::endl;
5454 /*emplace_back(std::make_shared<Node2>(".memory",nullptr,*this))*/
5455 return _owned->emplace_back(std::make_shared<xRooNode>(arg->GetName(), arg, *this))->fComp;
5456 // return arg;
5457}
5458
5459bool xRooNode::SetXaxis(const char *name, const char *title, int nbins, double low, double high)
5460{
5461 RooUniformBinning b(low, high, nbins, name);
5462 b.SetTitle(title);
5463 return SetXaxis(b);
5464}
5465
5466bool xRooNode::SetXaxis(const char *name, const char *title, int nbins, const double *bins)
5467{
5468 RooBinning b(nbins, bins, name);
5469 b.SetTitle(title);
5470 return SetXaxis(b);
5471}
5472
5474{
5475
5476 auto name = binning.GetName();
5477 double high = binning.highBound();
5478 double low = binning.lowBound();
5479 // int nbins = binning.numBins();
5480 auto title = binning.GetTitle();
5481
5482 // if have any dependents and name isn't one of them then stop
5483 auto _deps = vars();
5484 /*if(!_deps.empty() && !_deps.find(name)) {
5485 throw std::runtime_error(TString::Format("%s Does not depend on %s",GetName(),name));
5486 }*/
5487
5488 // object will need to exist
5489 if (!get()) {
5490 if (fParent && !find(GetName())) {
5491 fComp = fParent->Add(*this, "+").fComp;
5492 }
5493 }
5494
5495 auto a = get<RooAbsArg>();
5496 if (!a)
5497 throw std::runtime_error("Cannot SetXaxis of non-arg");
5498
5499 auto _x = acquire<RooRealVar>(name, title, low, high);
5500 _x->setBinning(binning, a->GetName());
5501 _x->getBinning(a->GetName()).SetTitle(title);
5502 if (_x->getBinningNames().size() == 2) {
5503 // this was the first binning, so copy it over to be the default binning too
5504 _x->setBinning(_x->getBinning(a->GetName()));
5505 } else {
5506 // ensure the default binning is wide enough to cover this range
5507 // the alternative to this would be to ensure setNormRange of all pdfs
5508 // are set to correct range (then default can be narrower than some of the named binnings)
5509 if (_x->getMax() < high)
5510 _x->setMax(high);
5511 if (_x->getMin() > low)
5512 _x->setMin(low);
5513 }
5514
5515 if (!_deps.find(name) && get<RooAbsPdf>()) {
5516 // creating a variable for a pdf we will assume it should be an observable
5517 _x->setAttribute("obs");
5518 }
5519
5520 a->setStringAttribute("xvar", _x->GetName());
5521 a->setStringAttribute("binning", a->GetName());
5522 fXAxis.reset(); // remove any existing xaxis
5523
5524 return true;
5525}
5526
5528{
5529 if (!ax)
5530 return false;
5531 if (ax->IsVariableBinSize()) {
5532 return SetXaxis(ax->GetName(), ax->GetTitle(), ax->GetNbins(), ax->GetXbins()->GetArray());
5533 } else {
5534 return SetXaxis(ax->GetName(), ax->GetTitle(), ax->GetNbins(), ax->GetXmin(), ax->GetXmax());
5535 }
5536}
5537
5538bool xRooNode::contains(const std::string &name) const
5539{
5540 return find(name, false) != nullptr;
5541}
5542
5543std::shared_ptr<xRooNode> xRooNode::find(const std::string &name, bool browseResult) const
5544{
5545 std::string partname = (name.find('/') != std::string::npos) ? name.substr(0, name.find('/')) : name;
5546 auto _s = (!get() && fParent) ? fParent->get<RooSimultaneous>()
5547 : get<RooSimultaneous>(); // makes work if doing simPdf.bins()["blah"]
5548 std::string extra = (_s) ? _s->indexCat().GetName() : "";
5549 for (auto &child : *this) {
5550 if (auto _obj = child->get(); name == child->GetName() || partname == child->GetName() ||
5551 (_obj && name == _obj->GetName()) || (_obj && partname == _obj->GetName()) ||
5552 (!extra.empty() && ((extra + "=" + name) == child->GetName() ||
5553 (extra + "=" + partname) == child->GetName()))) {
5554 if (browseResult)
5555 child->browse(); // needed so can go at()->at()->at()...
5556 if (partname != name && name != child->GetName()) {
5557 return child->at(name.substr(partname.length() + 1));
5558 }
5559 return child;
5560 }
5561 if (partname.find('.') != 0) { // do not allow mainChild browsing if trying to find a "." child ... as is done in
5562 // getObject for ".memory"
5563 if (auto x = mainChild(); x && strcmp(child->GetName(), x.GetName()) == 0) {
5564 // can browse directly into main children as if their children were our children
5565 for (auto &child2 : x.browse()) {
5566 if (auto _obj = child2->get(); name == child2->GetName() || partname == child2->GetName() ||
5567 (_obj && name == _obj->GetName()) ||
5568 (_obj && partname == _obj->GetName())) {
5569 if (browseResult)
5570 child2->browse(); // needed for onward read (or is it? there's a browse above too??)
5571 if (partname != name && name != child2->GetName()) {
5572 return child2->at(name.substr(partname.length() + 1));
5573 }
5574 return child2;
5575 }
5576 }
5577 }
5578 }
5579 }
5580 // before giving up see if partName is numeric and indexes within the range
5581 if (TString s(partname); s.IsDec() && size_t(s.Atoi()) < size()) {
5582 auto child2 = at(s.Atoi());
5583 if (partname != name) {
5584 return child2->at(name.substr(partname.length() + 1));
5585 }
5586 return child2;
5587 }
5588 // allow calling of find on a RooWorkspace to access getObject objects ...
5589 if (get<RooWorkspace>() && name != ".memory") {
5590 if (auto obj = getObject(name)) {
5591 auto out = std::make_shared<xRooNode>(obj, *this);
5592 if (browseResult)
5593 out->browse();
5594 return out;
5595 }
5596 }
5597 return nullptr;
5598}
5599
5600std::shared_ptr<xRooNode> xRooNode::operator[](const std::string &name)
5601{
5602 std::string partname = (name.find('/') != std::string::npos) ? name.substr(0, name.find('/')) : name;
5603 browse();
5604 auto _s = (!get() && fParent) ? fParent->get<RooSimultaneous>()
5605 : get<RooSimultaneous>(); // makes work if doing simPdf.bins()["blah"]
5606 std::string extra = (_s) ? _s->indexCat().GetName() : "";
5607 std::shared_ptr<xRooNode> folderNode;
5608 for (auto &child : *this) {
5609 if (name == child->GetName() || partname == child->GetName() ||
5610 (!extra.empty() &&
5611 ((extra + "=" + name) == child->GetName() || (extra + "=" + partname) == child->GetName()))) {
5612 child->browse(); // needed for onward read (or is it? there's a browse above too??)
5613 if (partname != name && name != child->GetName()) {
5614 return child->operator[](name.substr(partname.length() + 1));
5615 }
5616 return child;
5617 }
5618 if (auto x = mainChild(); strcmp(child->GetName(), x.GetName()) == 0) {
5619 // can browse directly into main children as if their children were our children
5620 for (auto &child2 : x.browse()) {
5621 if (name == child2->GetName() || partname == child2->GetName()) {
5622 child2->browse(); // needed for onward read (or is it? there's a browse above too??)
5623 if (partname != name && name != child2->GetName()) {
5624 return child2->operator[](name.substr(partname.length() + 1));
5625 }
5626 return child2;
5627 }
5628 }
5629 }
5630 if (child->fFolder == (std::string("!") + partname)) {
5631 if (!folderNode)
5632 folderNode = std::make_shared<xRooNode>(child->fFolder.c_str(), nullptr, *this);
5633 folderNode->push_back(child);
5634 }
5635 }
5636 if (folderNode) {
5637 if (partname != name) {
5638 return folderNode->operator[](name.substr(partname.length() + 1));
5639 }
5640 return folderNode;
5641 }
5642 // before giving up see if partName is numeric and indexes within the range
5643 if (TString s(partname); s.IsDec() && size_t(s.Atoi()) < size()) {
5644 auto child2 = at(s.Atoi());
5645 if (partname != name) {
5646 return child2->operator[](name.substr(partname.length() + 1));
5647 }
5648 return child2;
5649 }
5650 auto out = std::make_shared<xRooNode>(partname.c_str(), nullptr, *this); // not adding as child yeeet
5651 // special case, if creating a node in the workspace with a specific name, it's a folder node ...
5652 if (get<RooWorkspace>() && partname == "pdfs") {
5653 out->SetName("!pdfs");
5654 }
5655 if (partname != name) {
5656 return out->operator[](name.substr(partname.length() + 1));
5657 }
5658 return out;
5659}
5660
5662{
5663 if (!b) {
5664 for (auto o : *gROOT->GetListOfBrowsers()) {
5665 b = dynamic_cast<TBrowser *>(o);
5666 if (!b || !b->GetBrowserImp())
5667 continue;
5668 if (auto out = GetTreeItem(b); out)
5669 return out;
5670 }
5671 return nullptr;
5672 }
5673 if (!b->GetBrowserImp())
5674 return nullptr;
5675 if (auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp()))); _b) {
5676 auto _root = GETROOTDIR(_b);
5677 ;
5678 if (!_root)
5679 _root = GETLISTTREE(_b)->GetFirstItem();
5681 return GETLISTTREE(_b)->FindItemByObj(_root, const_cast<xRooNode *>(this));
5682 }
5683 return nullptr;
5684}
5685
5687{
5688 if (!b) {
5689 for (auto o : *gROOT->GetListOfBrowsers()) {
5690 b = dynamic_cast<TBrowser *>(o);
5691 if (!b || !b->GetBrowserImp())
5692 continue;
5693 if (auto out = GetListTree(b); out)
5694 return out;
5695 }
5696 return nullptr;
5697 }
5698 if (b->GetBrowserImp()) {
5699 if (auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp())));
5700 _b) {
5701 auto _root = GETROOTDIR(_b);
5702 if (!_root)
5703 _root = GETLISTTREE(_b)->GetFirstItem();
5704 if (auto item = GETLISTTREE(_b)->FindItemByObj(_root, const_cast<xRooNode *>(this)); item) {
5705 return GETLISTTREE(_b);
5706 }
5707 }
5708 }
5709 return nullptr;
5710}
5711
5712void xRooNode::SetName(const char *name)
5713{
5715 if (auto a = get<RooAbsArg>(); a)
5716 a->setStringAttribute("alias", name);
5717 for (auto o : *gROOT->GetListOfBrowsers()) {
5718 if (auto b = dynamic_cast<TBrowser *>(o); b) {
5719 if (auto item = GetTreeItem(b); item) {
5720 item->SetText(name);
5721 }
5722 }
5723 }
5724}
5725
5726void xRooNode::SetTitle(const char *title)
5727{
5728 if (auto o = (get<TNamed>()); o) {
5729 if (auto c = mainChild(); c.get()) {
5730 c.SetTitle(title);
5731 }
5732 o->SetTitle(title);
5733 }
5734 TNamed::SetTitle(title);
5735}
5736
5738{
5739 if (get<RooArgList>() || (!get() && !(strlen(GetName()) > 0 && (GetName()[0] == '!')) && !fBrowseOperation))
5740 return *this; // nothing to browse - 'collection' nodes should already be populated except for folders
5741 // alternative could have been to mandate that the 'components' of a collection node are the children it has.
5742
5743 auto findByObj = [&](const std::shared_ptr<xRooNode> &n) {
5744 std::vector<std::shared_ptr<xRooNode>> &nn = *this;
5745 for (auto &c : nn) {
5746 if (c->get() == n->get() && strcmp(n->GetName(), c->GetName()) == 0)
5747 return c;
5748 }
5749 return std::shared_ptr<xRooNode>(nullptr);
5750 };
5751
5752 auto appendChildren = [&](const xRooNode &n) {
5753 size_t out = 0;
5754 const std::vector<std::shared_ptr<xRooNode>> &nn(n);
5755 for (auto &c : nn) {
5756 if (auto existing = findByObj(c); existing) {
5757 existing->fTimes++;
5758 existing->fFolder = c->fFolder; // transfer folder assignment
5759 } else {
5760 emplace_back(c);
5761 }
5762 if (!TString(c->GetName()).BeginsWith(".coef"))
5763 out++; // don't count .coef as a child, as technically part of parent
5764 }
5765 return out;
5766 };
5767
5768 const std::vector<std::shared_ptr<xRooNode>> &nn2(*this);
5769 for (auto &c : nn2) {
5770 if (strlen(c->GetName()) > 0 && (c->GetName()[0] == '.')) {
5771 c->fTimes = 1;
5772 continue;
5773 } // never auto-cleanup property children
5774 if (strcmp(c->GetName(), "!.pars") == 0) {
5775 c->fTimes = 1;
5776 continue;
5777 } // special collection, also not cleaned up
5778 if (c->get<RooWorkspace>() || c->get<TFile>()) {
5779 c->fTimes = 1;
5780 continue;
5781 } // workspaces and files not cleaned up: TODO have a nocleanup flag instead
5782 c->fTimes = 0;
5783 }
5784
5785 size_t addedChildren = 0;
5786 if (fBrowseOperation) {
5788 } else {
5789 if (get<RooWorkspace>()) {
5791 }
5792
5793 // if (get<RooAbsPdf>() && ((fParent && fParent->get<RooWorkspace>()) || !fParent)) {
5794 // // top-level pdfs will also list the ".vars" property for -- should make this updateable
5795 // //if (auto x = find("!.vars"); !x) { // this is slower because it triggers a browse of !.vars
5796 // if(!contains("!.vars")) {
5797 // emplace_back(std::make_shared<Node2>("!.vars",nullptr,*this));
5798 // } /*else {
5799 // x->fTimes++;
5800 // }*/
5801 // }
5802
5803 // go through components factors and variations, adding all as children if required
5805 if (!get<RooWorkspace>())
5807 // include coefs if any
5808 auto _coefs = coefs();
5809 if (_coefs.get() && strcmp(_coefs->GetName(), "1") != 0 && strcmp(_coefs->GetName(), "ONE") != 0) {
5810 if (_coefs.size() == 1 && _coefs.get<RooAddition>()) {
5811 if (strcmp(_coefs.at(0)->GetName(), "1") != 0 &&
5812 strcmp(_coefs.at(0)->GetName(), "ONE") != 0) { // don't add the "1"
5813 auto coef = std::make_shared<xRooNode>(".coef", *_coefs.at(0)->get(), *this);
5814 if (auto existing = findByObj(coef); existing) {
5815 existing->fTimes++;
5816 existing->fFolder = _coefs.at(0)->fFolder; // transfer folder assignment
5817 } else {
5818 emplace_back(coef);
5819 }
5820 }
5821 } else {
5822 if (auto existing = find(_coefs.GetName()); existing) {
5823 existing->fTimes++;
5824 existing->fFolder = _coefs.fFolder; // transfer folder assignment
5825 } else {
5826 emplace_back(std::make_shared<xRooNode>(_coefs));
5827 }
5828 }
5829 }
5833 if (get<RooAbsData>())
5835 }
5836 // if has no children and is a RooAbsArg, add all the proxies
5837 if (auto arg = get<RooAbsArg>(); arg && addedChildren == 0) {
5838 for (int i = 0; i < arg->numProxies(); i++) {
5839 auto _proxy = arg->getProxy(i);
5840 if (auto a = dynamic_cast<RooArgProxy *>(_proxy)) {
5841 auto c = std::make_shared<xRooNode>(TString::Format(".%s", _proxy->name()), *(a->absArg()), *this);
5842 if (auto existing = findByObj(c); existing) {
5843 existing->fTimes++;
5844 existing->fFolder = c->fFolder; // transfer folder assignment
5845 } else {
5846 // mark any existing children with the same name for cleanup - this happens e.g. if did a Replace on one
5847 // of these nodes note that the child nodes will still become reordered (the old node will be deleted,
5848 // new node will appear at end)
5849 for (auto &child : *this) {
5850 if (strcmp(child->GetName(), c->GetName()) == 0) {
5851 child->fTimes = 0;
5852 }
5853 }
5854 emplace_back(c);
5855 }
5856 } else if (auto s = dynamic_cast<RooAbsCollection *>(_proxy)) {
5857 for (auto a2 : *s) {
5858 auto c = std::make_shared<xRooNode>(*a2, *this);
5859 if (arg->numProxies() != 1) {
5860 c->fFolder = std::string("!.") +
5861 _proxy->name(); // don't put in a folder if there's just 1 proxy (the collection)
5862 }
5863 if (auto existing = findByObj(c); existing) {
5864 existing->fTimes++;
5865 existing->fFolder = c->fFolder; // transfer folder assignment
5866 } else {
5867 emplace_back(c);
5868 }
5869 }
5870 }
5871 }
5872 /*for(auto& s : arg->servers()) {
5873 auto c = std::make_shared<xRooNode>(*s,*this);
5874 if (auto existing = findByObj(c); existing) {
5875 existing->fTimes++;
5876 existing->fFolder = c->fFolder; // transfer folder assignment
5877 } else {
5878 emplace_back(c);
5879 }
5880 }*/
5881 } else if (auto ir = get<RooStats::HypoTestInverterResult>()) {
5882 // check if we already have a hypoSpace in our memory
5883 bool hasHS = false;
5884 for (auto &c : fBrowsables) {
5885 if (strcmp(c->GetName(), ".memory") == 0 && c->get<xRooHypoSpace>()) {
5886 hasHS = true;
5887 break;
5888 }
5889 }
5890 if (!hasHS) {
5891 // add the HS
5892 auto hs =
5893 fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", std::make_shared<xRooHypoSpace>(ir), *this));
5894 // add the hypoPoints first so they appear first
5895 auto _axes = hs->get<xRooHypoSpace>()->axes();
5896
5897 int i = 0;
5898 for (auto &hp : *hs->get<xRooHypoSpace>()) {
5900 for (auto a : _axes) {
5901 if (a != _axes.first())
5902 coordString += ",";
5903 coordString +=
5904 TString::Format("%s=%g", a->GetName(), hp.coords->getRealValue(a->GetName(), ir->GetXValue(i)));
5905 }
5906 auto hpn = emplace_back(std::make_shared<xRooNode>(coordString, hp.hypoTestResult, hs));
5907 hpn->fTimes++;
5908 hpn->fBrowsables.emplace_back(std::make_shared<xRooNode>(
5909 ".memory", std::shared_ptr<xRooNLLVar::xRooHypoPoint>(&hp, [](xRooNLLVar::xRooHypoPoint *) {}), hpn));
5910 i++;
5911 }
5912 } else {
5913 // ensure all hypoTestResults are flagged as keep-alive
5914 std::vector<std::shared_ptr<xRooNode>> &nn = *this;
5915 for (auto &c : nn) {
5916 if (c->get<RooStats::HypoTestResult>())
5917 c->fTimes++;
5918 }
5919 }
5920 // xRooNode tests;
5921 // for(int i=0;i<ir->ArraySize();i++) {
5922 // tests.push_back(std::make_shared<xRooNode>(TString::Format("%g",ir->GetXValue(i)),*ir->GetResult(i),*this));
5923 // }
5924 // appendChildren(tests);
5925 } else if (get<RooStats::HypoTestResult>()) {
5926
5927 // create the xRooHypoPoint if necessary
5928 xRooNLLVar::xRooHypoPoint *hp = nullptr;
5929 for (auto &c : fBrowsables) {
5930 if (strcmp(c->GetName(), ".memory") == 0 && c->get<xRooNLLVar::xRooHypoPoint>()) {
5931 hp = c->get<xRooNLLVar::xRooHypoPoint>();
5932 c->fTimes++; // keep it alive
5933 break;
5934 }
5935 }
5936 if (!hp) {
5937 auto shp =
5938 std::make_shared<xRooNLLVar::xRooHypoPoint>(std::dynamic_pointer_cast<RooStats::HypoTestResult>(fComp));
5939 fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", shp, *this));
5940 hp = shp.get();
5941 }
5942
5943 xRooNode fits;
5944
5945 if (auto fit = hp->ufit()) {
5946 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("ufit");
5947 }
5948 if (auto fit = hp->cfit_null()) {
5949 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("cfit_null");
5950 }
5951 if (auto fit = hp->cfit_alt()) {
5952 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("cfit_alt");
5953 }
5954 if (auto fit = hp->gfit()) {
5955 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("gfit");
5956 }
5957 if (auto asi = hp->asimov()) {
5958 auto asiP = fits.emplace_back(std::make_shared<xRooNode>(
5959 asi->hypoTestResult ? asi->hypoTestResult : std::make_shared<RooStats::HypoTestResult>(asi->result()),
5960 *this));
5961 asiP->TNamed::SetName("asimov");
5962 asiP->fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", asi, asiP));
5963 }
5965 }
5966
5967 // clear anything that has fTimes = 0 still
5968 auto it = std::vector<std::shared_ptr<xRooNode>>::begin();
5969 while (it != std::vector<std::shared_ptr<xRooNode>>::end()) {
5970 if (it->get()->fTimes == 0) {
5971 for (auto o : *gROOT->GetListOfBrowsers()) {
5972 auto b = dynamic_cast<TBrowser *>(o);
5973 if (b && b->GetBrowserImp()) { // browserImp is null if browser was closed
5974 // std::cout << GetPath() << " Removing " << it->get()->GetPath() << std::endl;
5975
5976 if (auto _b =
5977 dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp())));
5978 _b) {
5979 auto _root = GETROOTDIR(_b);
5980 if (!_root)
5981 _root = GETLISTTREE(_b)->GetFirstItem();
5982 if (auto item = GETLISTTREE(_b)->FindItemByObj(_root, this); item) {
5983 GETLISTTREE(_b)->OpenItem(item);
5984 }
5985 }
5986
5987 b->RecursiveRemove(
5988 it->get()); // problem: if obj is living in a collapsed node it wont actually get deleted
5989 /*auto _b = dynamic_cast<TGFileBrowser*>( dynamic_cast<TRootBrowser*>(b->GetBrowserImp())->fActBrowser );
5990 if (_b) {
5991 std::cout << _b->fRootDir->GetText() << std::endl;
5992 if (auto item = _b->fListTree->FindItemByObj(_b->fRootDir,it->get()); item) {
5993 std::cout << "Found obj: " << item << " " << item->GetText() << std::endl;
5994 _b->fListTree->RecursiveDeleteItem(_b->fRootDir,it->get());
5995 }
5996
5997 //b->RecursiveRemove(it->get());
5998 if (auto item = _b->fListTree->FindItemByObj(_b->fRootDir,it->get()); item) {
5999 std::cout << "Still Found obj: " << item << std::endl;
6000 }
6001 _b->fListTree->ClearViewPort();
6002
6003 }*/
6004 }
6005 }
6006 /*it->get()->ResetBit(TObject::kNotDeleted); ++it;*/ it = erase(it);
6007 } else {
6008 ++it;
6009 }
6010 }
6011
6012 return *this;
6013}
6014
6015////////////////////////////////////////////////////////////////////////////////
6016/// List of observables (global and regular) of this node.
6017
6019{
6020 xRooNode out(".obs", std::make_shared<RooArgList>(), *this);
6021 out.get<RooArgList>()->setName((GetPath() + ".obs").c_str());
6022 for (auto o : vars()) {
6023 if (o->get<RooAbsArg>()->getAttribute("obs")) {
6024 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6025 out.emplace_back(o);
6026 }
6027 }
6028 return out;
6029}
6030
6031////////////////////////////////////////////////////////////////////////////////
6032/// List of global observables of this node.
6033
6035{
6036 xRooNode out(".globs", std::make_shared<RooArgList>(), *this);
6037 out.get<RooArgList>()->setName((GetPath() + ".globs").c_str());
6038 for (auto o : obs()) {
6039 if (o->get<RooAbsArg>()->getAttribute("global")) {
6040 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6041 out.emplace_back(o);
6042 }
6043 }
6044 return out;
6045}
6046
6047////////////////////////////////////////////////////////////////////////////////
6048/// List of regular observables of this node.
6049
6051{
6052 xRooNode out(".robs", std::make_shared<RooArgList>(), *this);
6053 out.get<RooArgList>()->setName((GetPath() + ".robs").c_str());
6054 for (auto o : obs()) {
6055 if (!o->get<RooAbsArg>()->getAttribute("global")) {
6056 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6057 out.emplace_back(o);
6058 }
6059 }
6060 return out;
6061}
6062
6063////////////////////////////////////////////////////////////////////////////////
6064/// List of parameters (non-observables) of this node.
6065
6067{
6068 if (strcmp(GetName(), ".bins") == 0 && fParent) {
6069 // return pars of the parent - this method is used by covariances() if e.g. do node.bins().covariances()
6070 return fParent->pars();
6071 }
6072 xRooNode out(".pars", std::make_shared<RooArgList>(), *this);
6073 out.get<RooArgList>()->setName((GetPath() + ".pars").c_str());
6074 for (auto o : vars()) {
6075 if (!o->get<RooAbsArg>()->getAttribute("obs")) {
6076 out.get<RooArgList>()->add(*(o->get<RooAbsArg>()));
6077 out.emplace_back(o);
6078 }
6079 }
6080 return out;
6081}
6082
6083////////////////////////////////////////////////////////////////////////////////
6084/// List of parameters that are currently constant
6085
6087{
6088 xRooNode out(".consts", std::make_shared<RooArgList>(), *this);
6089 out.get<RooArgList>()->setName((GetPath() + ".consts").c_str());
6090 for (auto o : pars()) {
6091 if (o->get<RooAbsArg>()->getAttribute("Constant") || o->get<RooConstVar>()) {
6092 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6093 out.emplace_back(o);
6094 }
6095 }
6096 return out;
6097}
6098
6099////////////////////////////////////////////////////////////////////////////////
6100/// List of parameters that are currently non-constant
6101/// These parameters do not have the "Constant" attribute
6102
6104{
6105 xRooNode out(".floats", std::make_shared<RooArgList>(), *this);
6106 out.get<RooArgList>()->setName((GetPath() + ".floats").c_str());
6107 for (auto o : pars()) {
6108 if (!o->get<RooAbsArg>()->getAttribute("Constant") && !o->get<RooConstVar>()) {
6109 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6110 out.emplace_back(o);
6111 }
6112 }
6113 return out;
6114}
6115
6116////////////////////////////////////////////////////////////////////////////////
6117/// List of parameters of interest: parameters marked as "of interest"
6118/// These parameters have the "poi" attribute
6119
6121{
6122 xRooNode out(".poi", std::make_shared<RooArgList>(), *this);
6123 out.get<RooArgList>()->setName((GetPath() + ".poi").c_str());
6124 for (auto o : pars()) {
6125 if (o->get<RooAbsArg>()->getAttribute("poi")) {
6126 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6127 out.emplace_back(o);
6128 }
6129 }
6130 return out;
6131}
6132
6133////////////////////////////////////////////////////////////////////////////////
6134/// List of nuisance parameters: non-constant parameters that are not marked of interest,
6135/// as well as any parameters that have been marked by the "np" attribute
6136
6138{
6139 xRooNode out(".np", std::make_shared<RooArgList>(), *this);
6140 out.get<RooArgList>()->setName((GetPath() + ".np").c_str());
6141 for (auto o : pars()) {
6142 if (o->get<RooAbsArg>()->getAttribute("np") ||
6143 (!o->get<RooAbsArg>()->getAttribute("Constant") && !o->get<RooAbsArg>()->getAttribute("poi") &&
6144 !o->get<RooConstVar>())) {
6145 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6146 out.emplace_back(o);
6147 }
6148 }
6149 return out;
6150}
6151
6152////////////////////////////////////////////////////////////////////////////////
6153/// List of prespecified parameters: non-floatable parameters
6154
6156{
6157 xRooNode out(".pp", std::make_shared<RooArgList>(), *this);
6158 out.get<RooArgList>()->setName((GetPath() + ".pp").c_str());
6159 for (auto o : pars()) {
6160 if (!o->get<RooAbsArg>()->getAttribute("np") && !o->get<RooAbsArg>()->getAttribute("poi") &&
6161 (o->get<RooAbsArg>()->getAttribute("Constant") || o->get<RooConstVar>())) {
6162 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6163 out.emplace_back(o);
6164 }
6165 }
6166 return out;
6167}
6168
6169////////////////////////////////////////////////////////////////////////////////
6170/// List of variables (observables and parameters) of this node
6171
6173{
6174 xRooNode out(".vars", std::make_shared<RooArgList>(), *this);
6175 out.get<RooArgList>()->setName((GetPath() + ".vars").c_str());
6176 if (auto coll = get<RooAbsCollection>(); coll) {
6177 for (auto &x : *this) {
6178 for (auto &y : x->vars()) {
6179 out.push_back(y);
6180 }
6181 }
6182 return out;
6183 }
6184 if (auto p = get<RooAbsArg>(); p) {
6185 // also need to get all constPars so use leafNodeServerList .. will include self if is fundamental, which is what
6186 // we want
6187 // ensure all globs appear after robs, as we rely on this ordering for picking "x" var in "reduced" method
6190 p->leafNodeServerList(&allLeaves);
6191 for (auto &c : allLeaves) {
6192 if (c->isFundamental() || (dynamic_cast<RooConstVar *>(c) && !TString(c->GetName()).IsFloat())) {
6193 if (!c->getAttribute("global")) {
6194 out.get<RooArgList>()->add(*c);
6195 out.emplace_back(std::make_shared<xRooNode>(*c, *this));
6196 }
6197 if (c->getAttribute("global")) {
6198 _globs.emplace_back(std::make_shared<xRooNode>(*c, *this));
6199 _globs.back()->fFolder = "!globs";
6200 } else if (c->getAttribute("obs")) {
6201 out.back()->fFolder = "!robs";
6202 } else if (c->getAttribute("poi")) {
6203 out.back()->fFolder = "!poi";
6204 } else if (c->getAttribute("np") ||
6205 (!c->getAttribute("Constant") && !c->getAttribute("poi") && c->IsA() != RooConstVar::Class())) {
6206 out.back()->fFolder = "!np";
6207 } else if (!c->getAttribute("Constant") && c->IsA() != RooConstVar::Class()) {
6208 out.back()->fFolder = "!floats";
6209 } else {
6210 out.back()->fFolder = "!pp";
6211 }
6212 }
6213 }
6214 for (auto g : _globs) {
6215 out.get<RooArgList>()->add(*g->get<RooAbsArg>());
6216 out.emplace_back(g);
6217 }
6218 } else if (auto p2 = get<RooAbsData>(); p2) {
6219 for (auto a : *p2->get()) {
6220 a->setAttribute("obs");
6221 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6222 out.get<RooArgList>()->add(*a);
6223 }
6224 if (auto _dglobs = p2->getGlobalObservables()) {
6225 for (auto &a : *_dglobs) {
6226 a->setAttribute("obs");
6227 a->setAttribute("global");
6228 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6229 out.get<RooArgList>()->add(*a);
6230 }
6231 } else if (auto _globs = find(".globs"); _globs && _globs->get<RooAbsCollection>()) {
6232 for (auto &a : *_globs->get<RooAbsCollection>()) {
6233 a->setAttribute("obs");
6234 a->setAttribute("global");
6235 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6236 out.get<RooArgList>()->add(*a);
6237 }
6238 } else if (auto _ws = ws(); _ws) {
6239 if (auto _globs2 = dynamic_cast<RooArgSet *>(GETWSSNAPSHOTS(_ws).find(p2->GetName())); _globs2) {
6240 for (auto a : *_globs2) {
6241 a->setAttribute("obs");
6242 a->setAttribute("global");
6243 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6244 out.get<RooArgList>()->add(*a);
6245 }
6246 } else if (auto _gl = GETWSSETS(_ws).find("globalObservables"); _gl != GETWSSETS(_ws).end()) {
6247 for (auto &_g : _gl->second) {
6248 auto _clone = std::shared_ptr<RooAbsArg>(dynamic_cast<RooAbsArg *>(_g->Clone(_g->GetName())));
6249 if (auto v = std::dynamic_pointer_cast<RooAbsRealLValue>(_clone); v && _g->getStringAttribute("nominal"))
6250 v->setVal(TString(_g->getStringAttribute("nominal")).Atof());
6251 out.emplace_back(std::make_shared<xRooNode>(_clone, *this));
6252 out.get<RooArgList>()->add(*_clone);
6253 }
6254 } else if (fParent) {
6255 // note: this is slow in large workspaces ... too many obs to look through?
6256 std::unique_ptr<RooAbsCollection> _globs3(fParent->obs().get<RooArgList>()->selectByAttrib("global", true));
6257 // std::unique_ptr<RooAbsCollection> _globs(_ws->allVars().selectByAttrib("global",true)); - tried this to
6258 // be quicker but it wasn't
6259 for (auto &_g : *_globs3) {
6260 auto _clone = std::shared_ptr<RooAbsArg>(dynamic_cast<RooAbsArg *>(_g->Clone(_g->GetName())));
6261 if (auto v = std::dynamic_pointer_cast<RooAbsRealLValue>(_clone); v && _g->getStringAttribute("nominal"))
6262 v->setVal(TString(_g->getStringAttribute("nominal")).Atof());
6263 out.emplace_back(std::make_shared<xRooNode>(_clone, *this));
6264 out.get<RooArgList>()->add(*_clone);
6265 }
6266 }
6267 }
6268 } else if (auto w = get<RooWorkspace>(); w) {
6269 for (auto a : w->allVars()) {
6270 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6271 out.get<RooArgList>()->add(*a);
6272 }
6273 // add all cats as well
6274 for (auto a : w->allCats()) {
6275 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6276 out.get<RooArgList>()->add(*a);
6277 }
6278 }
6279 return out;
6280}
6281
6283{
6284 xRooNode out(".components", nullptr, *this);
6285
6286 if (auto p = get<RooAddPdf>(); p) {
6287 // only add each pdf once (the coefs will be accumulated in coefs() method) ...
6288 std::set<RooAbsArg *> donePdfs;
6289 for (auto &o : p->pdfList()) {
6290 if (donePdfs.count(o))
6291 continue;
6292 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6293 donePdfs.insert(o);
6294 }
6295 } else if (auto p2 = get<RooRealSumPdf>(); p2) {
6296 // check for common prefixes and suffixes, will use to define aliases to shorten names
6297 // if have more than 1 function
6298 // TString commonPrefix=""; TString commonSuffix="";
6299 // if (p->funcList().size() > 1) {
6300 // bool checked=false;
6301 // for(auto& o : p->funcList()) {
6302 // if (!checked) {
6303 // commonPrefix = o->GetName(); commonSuffix = o->GetName(); checked=true;
6304 // } else {
6305 //
6306 // }
6307 // }
6308 // }
6309 std::set<RooAbsArg *> doneFuncs;
6310 for (auto &o : p2->funcList()) {
6311 if (doneFuncs.count(o))
6312 continue;
6313 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6314 doneFuncs.insert(o);
6315 }
6316 } else if (auto p3 = get<RooAddition>(); p3) {
6317 for (auto &o : p3->list()) {
6318 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6319 }
6320 } else if (auto p4 = get<RooAbsCollection>(); p4) {
6321 for (auto &a : *p4) {
6322 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6323 }
6324 } else if (auto p5 = get<RooWorkspace>(); p5) {
6325 for (auto &o : p5->components()) {
6326 // only top-level nodes (only clients are integrals or things that aren't part of the workspace)
6327 // if (o->hasClients()) continue;
6328 bool hasClients = false;
6329 for (auto &c : o->clients()) {
6330 if (!c->InheritsFrom("RooRealIntegral") && p5 == GETWS(c)) {
6331 hasClients = true;
6332 break;
6333 }
6334 }
6335 if (hasClients)
6336 continue;
6337 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6338 if (o->InheritsFrom("RooAbsPdf")) {
6339 out.back()->fFolder = "!pdfs";
6340 } else {
6341 out.back()->fFolder = "!scratch";
6342 }
6343 }
6344 for (auto &o : p5->allGenericObjects()) {
6345 if (auto fr = dynamic_cast<RooFitResult *>(o); fr) {
6346 TString s(fr->GetTitle());
6347 if (s.Contains(';'))
6348 s = s(0, s.Index(';'));
6349 if (auto _pdf = out.find(s.Data()); _pdf) {
6350 // std::cout << " type = " << _pdf->get()->ClassName() << std::endl;
6351 out.emplace_back(std::make_shared<xRooNode>(fr->GetName(), *fr, _pdf));
6352 // for a while, this node's parent pointed to something of type Node2!!
6353 // how to fix??? - I fxied it with a new constructor to avoid the shared_ptr<Node2> calling the const
6354 // Node2& constructor via getting wrapped in a Node2(shared_ptr<TObject>) call
6355 // out.back()->fParent = _pdf;
6356 // std::cout << " type2 = " << out.back()->fParent->get()->ClassName() << std::endl;
6357 } else {
6358 out.emplace_back(std::make_shared<xRooNode>(fr->GetName(), *fr, *this));
6359 }
6360 out.back()->fFolder = "!fits";
6361 } else {
6362 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6363 if (strcmp(out.back()->get()->ClassName(), "TStyle") == 0) {
6364 out.back()->fFolder = "!styles";
6365 } else if (strcmp(out.back()->get()->ClassName(), "RooStats::HypoTestInverterResult") == 0) {
6366 out.back()->fFolder = "!scans";
6367 } else if (strcmp(out.back()->get()->ClassName(), "RooStats::ModelConfig") == 0) {
6368 out.back()->fFolder = "!models";
6369 } else {
6370 out.back()->fFolder = "!objects";
6371 }
6372 }
6373 }
6374 for (auto &[k, v] : GETWSSETS(p5)) {
6375 // skip 'CACHE' sets because they are auto-removed when sanitizing workspaces, which will invalidate these
6376 // children
6377 if (k.find("CACHE_") == 0)
6378 continue;
6379 out.emplace_back(std::make_shared<xRooNode>(k.c_str(), v, *this));
6380 out.back()->fFolder = "!sets";
6381 }
6382
6384 std::unique_ptr<TIterator> iter(snaps.MakeIterator());
6385 TObject *snap;
6386 while ((snap = iter->Next())) {
6387 out.emplace_back(std::make_shared<xRooNode>(*snap, *this));
6388 out.back()->fFolder = "!snapshots";
6389 }
6390 } else if (auto mc = get<RooStats::ModelConfig>()) {
6391 // add the pdf as a child, and the external constraints set if its there
6392 if (mc->GetPdf()) {
6393 out.emplace_back(std::make_shared<xRooNode>(".pdf", *mc->GetPdf(), *this));
6394 }
6395 if (mc->GetExternalConstraints()) {
6396 out.emplace_back(std::make_shared<xRooNode>(".extCons", *mc->GetExternalConstraints(), *this));
6397 }
6398 } else if (strlen(GetName()) > 0 && GetName()[0] == '!' && fParent) {
6399 // special case of dynamic property
6400 if (TString(GetName()) == "!.pars") {
6401 for (auto &c : fParent->pars()) {
6402 out.emplace_back(c);
6403 }
6404 } else {
6405 // the components of a folder are the children of the parent (after browsing) that live in this folder
6406 fParent->browse();
6407 for (auto &c : *fParent) {
6408 if (c->fFolder == GetName()) {
6409 out.emplace_back(c);
6410 }
6411 }
6412 }
6413 }
6414
6415 return out;
6416}
6417
6418////////////////////////////////////////////////////////////////////////////////
6419/// bins of a channel or sample, or channels of a multi-channel pdf
6420
6422{
6423 xRooNode out(".bins", nullptr, *this);
6424
6425 if (auto p = get<RooSimultaneous>(); p) {
6426 std::map<int, std::shared_ptr<xRooNode>> cats; // fill into a map to preserve index ordering
6427 for (auto &c : p->indexCat()) { // is alphabetical in labels
6428 auto pp = p->getPdf(c.first.c_str());
6429 if (!pp)
6430 continue;
6431 cats[c.second] =
6432 std::make_shared<xRooNode>(TString::Format("%s=%s", p->indexCat().GetName(), c.first.c_str()), *pp, *this);
6433 }
6434 for (auto &[_, n] : cats)
6435 out.emplace_back(n);
6436 } else if (auto phf = get<ParamHistFunc>(); phf) {
6437 int i = 1;
6438#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
6439 auto &pSet = phf->_paramSet;
6440#else
6441 auto &pSet = phf->paramList();
6442#endif
6443 for (auto par : pSet) {
6444 out.emplace_back(std::make_shared<xRooNode>(*par, *this));
6445 out.back()->fBinNumber = i;
6446 i++;
6447 }
6448 } else if (auto ax = GetXaxis(); ax) {
6449 for (int i = 1; i <= ax->GetNbins(); i++) {
6450 // create a RooProduct of all bin-specific factors of all shapeFactors
6451 std::vector<RooAbsArg *> _factors;
6452 for (auto f : factors()) {
6453 if (f->get<ParamHistFunc>()) {
6454 if (f->bins()[i - 1]->get<RooProduct>()) {
6455 for (auto &ss : f->bins()[i - 1]->factors())
6456 _factors.push_back(ss->get<RooAbsArg>());
6457 } else {
6458 _factors.push_back(f->bins()[i - 1]->get<RooAbsArg>());
6459 }
6460 }
6461 }
6462 out.emplace_back(std::make_shared<xRooNode>(
6463 TString::Format("%g<=%s<%g", ax->GetBinLowEdge(i), ax->GetParent()->GetName(), ax->GetBinLowEdge(i + 1)),
6464 _factors.empty() ? nullptr
6465 : std::make_shared<RooProduct>(TString::Format("%s.binFactors.bin%d", GetName(), i),
6466 "binFactors", RooArgList()),
6467 *this));
6468 for (auto f : _factors) {
6469#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
6470 out.back()->get<RooProduct>()->_compRSet.add(*f);
6471#else
6472 const_cast<RooArgList &>(out.back()->get<RooProduct>()->realComponents()).add(*f);
6473#endif
6474 }
6475 out.back()->fBinNumber = i;
6476 }
6477 }
6478
6479 return out;
6480}
6481
6483{
6485
6486 if (recurse && fParent) {
6487 // get our coefs and multiply it by the parents coefs ...
6488 auto ourCoefs = xRooNode::coefs(false);
6489 auto parentCoefs = fParent->coefs(true);
6490 if (!parentCoefs.get<RooAbsReal>()) {
6491 // no coefs to include, just return our coefs
6492 return ourCoefs;
6493 }
6494 if (!ourCoefs.get<RooAbsReal>()) {
6495 // just return the parent's coefs
6496 return parentCoefs;
6497 }
6498 // if got here, must combine parentCoefs and outCoefs into a RooProduct
6499 xRooNode out(".recursiveCoefs",
6500 std::make_shared<RooProduct>(".recursiveCoefs",
6501 TString::Format("Recursive Coefficients of %s", GetName()),
6502 *ourCoefs.get<RooAbsReal>(), *parentCoefs.get<RooAbsReal>()),
6503 *this);
6504 // keep alive the two coef nodes by adding to out's memory
6505 auto mem = out.emplace_back(std::make_shared<xRooNode>(".memory", nullptr, *this));
6506 mem->emplace_back(std::make_shared<xRooNode>(ourCoefs));
6507 mem->emplace_back(std::make_shared<xRooNode>(parentCoefs));
6508 return out;
6509 }
6510
6511 bool isResidual = false;
6512
6513 // if parent is a sumpdf or addpdf then include the coefs
6514 // if func appears multiple times then coefs must be combined into a RooAddition temporary
6515 if (fParent) {
6516 // handle case where filters are applied .. need to pass through these
6517 // do this by iterating while fComp is null
6518 auto parent = fParent;
6519 if (!parent->fComp) {
6520 while (!parent->fComp && parent->fParent) {
6521 parent = parent->fParent;
6522 }
6523 // parent should now be node above the filters ... need parent of that
6524 parent = parent->fParent;
6525 if (!parent)
6526 parent = fParent; // revert t original parent in case something went wrong
6527 }
6528 if (auto p = parent->get<RooRealSumPdf>(); p) {
6529 std::size_t i = 0;
6530 for (auto &o : p->funcList()) {
6531 if (o == get()) {
6532 if (i >= p->coefList().size()) {
6533 isResidual = true;
6534 coefs.add(p->coefList());
6535 } else {
6536 coefs.add(*p->coefList().at(i));
6537 }
6538 }
6539 i++;
6540 }
6541 } else if (auto p2 = parent->get<RooAddPdf>(); p2) {
6542 std::size_t i = 0;
6543 if (p2->coefList().empty()) {
6544 // this can happen if all pdfs are extended then the coef is effectively the
6545 // expected number of events
6546 // TODO: test behaviour of xRooNode under this scenario (are histograms correct?)
6547 } else {
6548 for (auto &o : p2->pdfList()) {
6549 if (o == get()) {
6550 if (i >= p2->coefList().size()) {
6551 isResidual = true;
6552 coefs.add(p2->coefList());
6553 } else {
6554 coefs.add(*p2->coefList().at(i));
6555 }
6556 }
6557 i++;
6558 }
6559 }
6560 }
6561 }
6562 if (isResidual) {
6563 // return a node representing 1.-sumOfCoefs
6564 // involves creating sumOfCoefs unless there is only 1 coef, then just use that
6565 auto coefSum = coefs.empty()
6566 ? nullptr
6567 : (coefs.size() == 1 ? std::shared_ptr<RooAbsArg>(coefs.at(0), [](RooAbsArg *) {})
6568 : std::make_shared<RooAddition>((isResidual) ? ".sumOfCoefs" : ".coefs",
6569 "Coefficients of", coefs));
6570 xRooNode out(".coef", coefSum ? std::dynamic_pointer_cast<RooAbsArg>(std::make_shared<RooFormulaVar>(
6571 ".coef", "1-sum(otherCoefs)", "1. - @0", *coefSum))
6572 : nullptr /* should we return a "1." instead? */);
6573 if (coefSum && coefs.size() != 1) {
6574 out.emplace_back(std::make_shared<xRooNode>(".memory", nullptr, *this))
6575 ->emplace_back(
6576 std::make_shared<xRooNode>(".sumOfCoefs", coefSum, out)); // added to keep the sum alive! with the node
6577 }
6578 if (!coefs.empty()) {
6579 out.browse();
6580 }
6581 return out;
6582 } else if (coefs.size() == 1) {
6583 xRooNode out(".coef", std::shared_ptr<RooAbsArg>(coefs.at(0), [](RooAbsArg *) {}), *this);
6584 if (!coefs.empty()) {
6585 out.browse();
6586 }
6587 return out;
6588 } else {
6589 auto coefSum =
6590 coefs.empty()
6591 ? nullptr
6592 : std::make_shared<RooAddition>(".coefs", TString::Format("Coefficients of %s", GetName()), coefs);
6593 xRooNode out(".coefs", coefSum, *this);
6594 if (!coefs.empty())
6595 out.browse();
6596
6597 return out;
6598 }
6599}
6600
6602{
6603 xRooNode out(".factors", nullptr, *this);
6604
6605 if (auto p = get<RooProdPdf>(); p) {
6606 auto _main = mainChild();
6607 if (auto a = _main.get<RooRealSumPdf>(); a && !a->getStringAttribute("alias")) {
6608 a->setStringAttribute("alias", "samples");
6609 } else if (auto a2 = _main.get<RooAddPdf>(); a2 && !a2->getStringAttribute("alias")) {
6610 a2->setStringAttribute("alias", "components");
6611 }
6612 int _npdfs = p->pdfList().size();
6613 for (auto &o : p->pdfList()) {
6614 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6615 if (_npdfs > 5 && o != _main.get() && out.back()->robs().size() == 0) // constraints have no robs in them
6616 out.back()->fFolder = "!constraints";
6617 }
6618 } else if (auto p2 = get<RooProduct>(); p2) {
6619 for (auto &o : p2->components()) {
6620 if (o->InheritsFrom("RooProduct")) {
6621 // get factors of this term
6622 auto x = xRooNode("tmp", *o, *this).factors();
6623 for (auto &n : x) {
6624 out.emplace_back(std::make_shared<xRooNode>(n->GetName(), n->fComp, *this));
6625 }
6626 } else {
6627 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6628 }
6629 }
6630 } else if (auto w = get<RooWorkspace>(); w) {
6631 // if workspace, return all functions (not pdfs) that have a RooProduct as one of their clients
6632 // or not clients
6633 // exclude obs and globs
6634 auto oo = obs(); // need to keep alive as may contain owning globs
6635 auto &_obs = *(oo.get<RooArgList>());
6636 for (auto a : w->allFunctions()) {
6637 if (_obs.contains(*a))
6638 continue;
6639 bool show(true);
6640 for (auto c : a->clients()) {
6641 show = false;
6642 if (c->InheritsFrom("RooProduct")) {
6643 show = true;
6644 break;
6645 }
6646 }
6647 if (show)
6648 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6649 }
6650 }
6651
6652 /*
6653 // if parent is a sumpdf or addpdf then include the coefs
6654 // if func appears multiple times then coefs must be combined into a RooAddition temporary
6655 if (fParent) {
6656 RooArgList coefs;
6657 if(auto p = fParent->get<RooRealSumPdf>();p) {
6658 int i=0;
6659 for(auto& o : p->funcList()) {
6660 if (o == get()) {
6661 coefs.add( *p->coefList().at(i) );
6662 }
6663 i++;
6664 }
6665 } else if(auto p = fParent->get<RooAddPdf>(); p) {
6666 int i=0;
6667 for(auto& o : p->pdfList()) {
6668 if (o == get()) {
6669 coefs.add( *p->coefList().at(i) );
6670 }
6671 i++;
6672 }
6673 }
6674 if (!coefs.empty()) {
6675 if (coefs.size() == 1) {
6676 if (strcmp(coefs.at(0)->GetName(),"1")) { // don't add the "1"
6677 out.emplace_back(std::make_shared<Node2>(".coef", *coefs.at(0), *this));
6678 }
6679 } else {
6680 out.emplace_back(std::make_shared<Node2>(".coefs",
6681 std::make_shared<RooAddition>(".coefs", "Coefficients of",
6682 coefs), *this));
6683 }
6684 }
6685 }
6686 */
6687 return out;
6688}
6689
6691{
6692 xRooNode out(".variations", nullptr, *this);
6693
6694 // if (auto p = get<RooSimultaneous>(); p) {
6695 // for (auto &c : p->indexCat()) {
6696 // auto pp = p->getPdf(c.first.c_str());
6697 // if (!pp)
6698 // continue;
6699 // out.emplace_back(
6700 // std::make_shared<xRooNode>(TString::Format("%s=%s", p->indexCat().GetName(), c.first.c_str()), *pp,
6701 // *this));
6702 // }
6703 // } else
6704 if (auto p2 = get<PiecewiseInterpolation>(); p2) {
6705#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
6706 out.emplace_back(std::make_shared<xRooNode>("nominal", p2->_nominal.arg(), *this));
6707#else
6708 out.emplace_back(std::make_shared<xRooNode>("nominal", *(p2->nominalHist()), *this));
6709#endif
6710 for (size_t i = 0; i < p2->paramList().size(); i++) {
6711 // TODO: should we only return one if we find they are symmetrized?
6712 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p2->paramList().at(i)->GetName()),
6713 *p2->highList().at(i), *this));
6714 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p2->paramList().at(i)->GetName()),
6715 *p2->lowList().at(i), *this));
6716 }
6718#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
6719 out.emplace_back(std::make_shared<xRooNode>("nominal", RooFit::RooConst(p3->_nominal), *this));
6720 for (size_t i = 0; i < p3->_paramList.size(); i++) {
6721 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p3->_paramList.at(i)->GetName()),
6722 RooFit::RooConst(p3->_high.at(i)), *this));
6723 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p3->_paramList.at(i)->GetName()),
6724 RooFit::RooConst(p3->_low.at(i)), *this));
6725 }
6726#else
6727 out.emplace_back(std::make_shared<xRooNode>("nominal", RooFit::RooConst(p3->nominal()), *this));
6728 for (size_t i = 0; i < p3->variables().size(); i++) {
6729 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p3->variables().at(i)->GetName()),
6730 RooFit::RooConst(p3->high().at(i)), *this));
6731 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p3->variables().at(i)->GetName()),
6732 RooFit::RooConst(p3->low().at(i)), *this));
6733 }
6734#endif
6735
6736 } else if (auto p4 = get<ParamHistFunc>(); p4) {
6737 // I *think* I put this here so that can browse into a ParamHistFunc
6738 // int i = 0;
6739 // for (auto par : p4->_paramSet) {
6740 // TString _name = par->GetName();
6741 // // if(auto _v = dynamic_cast<RooRealVar*>(p->_dataSet.get(i)->first()); _v) {
6742 // // _name = TString::Format("%s=%g",_v->GetName(),_v->getVal());
6743 // // }
6744 // // out.emplace_back(std::make_shared<xRooNode>(_name,*par,*this)); -- -removed cos now have bin()
6745 // method i++;
6746 // }
6747 }
6748 return out;
6749}
6750
6752{
6753 RooArgList out;
6754 out.setName(GetName());
6755 for (auto &k : *this) {
6756 if (auto o = k->get<RooAbsArg>(); o)
6757 out.add(*o);
6758 }
6759 return out;
6760}
6761
6763{
6764 xRooNode out(".datasets()", nullptr, *this);
6765 // removed the browse operation since no longer showing '.datasets()' in browser
6766 // and otherwise this means dataset reduction operation will be called every time we 'browse()' the datasets node
6767 // out.fBrowseOperation = [](xRooNode *f) { return f->fParent->datasets(); };
6768
6769 if (auto _ws = get<RooWorkspace>(); _ws) {
6770 for (auto &d : _ws->allData()) {
6771 out.emplace_back(std::make_shared<xRooNode>(*d, *this));
6772 out.back()->fFolder = "!datasets";
6773 }
6774 } else if (get<RooAbsPdf>() ||
6775 (!get() && fParent &&
6776 fParent->get<RooAbsPdf>())) { // second condition handles 'bins' nodes of pdf, which have null ptr
6777 // only add datasets that have observables that cover all our observables
6778 auto oo = obs(); // must keep alive in case is owning the globs
6779 RooArgSet _obs(*oo.get<RooArgList>());
6780 //_obs.add(coords(true).argList(), true); // include coord observables too, and current xaxis if there's one -
6781 // added in loop below
6782
6783 TString cut;
6785 for (auto _c : coords()) { // coords() moves vars to their respective coordinates too
6786 if (auto _cat = _c->get<RooAbsCategoryLValue>(); _cat) {
6787 if (cut != "")
6788 cut += " && ";
6789 cut += TString::Format("%s==%d", _cat->GetName(), _cat->getCurrentIndex());
6790 _obs.add(*_cat,
6791 true); // note: if we ever changed coords to return clones, would need to keep coords alive
6792 cutobs.add(*_cat);
6793 } else if (auto _rv = _c->get<RooAbsRealLValue>(); _rv) {
6794 // todo: check coordRange is a single range rather than multirange
6795 if (cut != "")
6796 cut += " && ";
6797 cut += TString::Format("%s>=%f&&%s<%f", _rv->GetName(), _rv->getMin(_rv->getStringAttribute("coordRange")),
6798 _rv->GetName(), _rv->getMax(_rv->getStringAttribute("coordRange")));
6799 _obs.add(*_rv,
6800 true); // note: if we ever changed coords to return clones, would need to keep coords alive
6801 cutobs.add(*_rv);
6802 } else {
6803 throw std::runtime_error("datasets(): Unsupported coordinate type");
6804 }
6805 }
6806 if (auto s = get<RooSimultaneous>()) {
6807 // check if we have a pdf for every category ... if not then add to cut
6808 bool hasMissing = false;
6809 TString extraCut = "";
6810 for (auto cat : s->indexCat()) {
6811 if (!s->getPdf(cat.first.c_str())) {
6812 hasMissing = true;
6813 } else {
6814 if (extraCut != "")
6815 extraCut += " || ";
6816 extraCut += TString::Format("%s==%d", s->indexCat().GetName(), cat.second);
6817 }
6818 }
6819 if (hasMissing) {
6820 if (cut != "")
6821 cut += " && ";
6822 cut += "(" + extraCut + ")";
6823 cutobs.add(s->indexCat());
6824 }
6825 }
6826
6827 if (auto ax = GetXaxis(); ax && dynamic_cast<RooAbsArg *>(ax->GetParent())->getAttribute("obs")) {
6828 auto a = dynamic_cast<RooAbsArg *>(ax->GetParent());
6829 _obs.add(*a, true);
6830 }
6831 xRooNode _datasets; // will be any child datasets, along with datasets of the workspace
6832 for (auto &child : *this) {
6833 if (child->get<RooAbsData>())
6834 _datasets.push_back(child);
6835 }
6836 if (auto __ws = ws(); __ws) {
6837 xRooNode _wsNode(*__ws, *this);
6838 for (auto &d : _wsNode.datasets()) {
6839 _datasets.push_back(d);
6840 }
6841 }
6842
6843 for (auto &d : _datasets) {
6844 if (std::unique_ptr<RooAbsCollection>(d->obs().argList().selectCommon(_obs))->size() == _obs.size()) {
6845 // all obs present .. include
6846
6847 if (cut != "") {
6848 RooFormulaVar cutFormula("cut1", cut, cutobs); // doing this to avoid complaints about unused vars
6849 // TODO: Could consider using a 'filter' node (see filter() method) applied to the dataset instead
6850 // of creating and using a reduced dataset here
6851 out.emplace_back(std::make_shared<xRooNode>(
6852 std::shared_ptr<RooAbsData>(d->get<RooAbsData>()->reduce(
6853 *std::unique_ptr<RooAbsCollection>(d->robs().get<RooArgList>()->selectCommon(_obs)), cutFormula)),
6854 *this));
6855 // put a subset of the globs in the returned dataset too
6856 out.back()->get<RooAbsData>()->setGlobalObservables(*std::unique_ptr<RooAbsCollection>(
6857 d->globs().get<RooArgList>()->selectCommon(*globs().get<RooArgList>())));
6858 if (d->get()->TestBit(1 << 20))
6859 out.back()->get()->SetBit(1 << 20);
6860 // need to attach the original dataset so that things like SetBinContent can interact with it
6861 out.back()->fBrowsables.emplace_back(std::make_shared<xRooNode>(".sourceds", d->fComp, *this));
6862 } else {
6863 out.emplace_back(std::make_shared<xRooNode>(d->fComp, *this));
6864 }
6865 }
6866 }
6867 /*else if(auto p = get<RooFitResult>(); p) {
6868 // look for datasets in workspace that match the fit result name after hashing
6869 for(auto& _d : xRooNode(*_ws,*this).datasets()) {
6870 auto _hash = RooAbsTree::nameToHash(_d->get()->GetName());
6871 if (TString::Format("%d;%d",_hash.first,_hash.second) == p->GetTitle()) {
6872 out.emplace_back(std::make_shared<xRooNode>(_d->fComp, *this));
6873 }
6874 }
6875 }*/
6876 } else if (auto mc = get<RooStats::ModelConfig>()) {
6877 return xRooNode(*mc->GetPdf(), fParent).datasets();
6878 }
6879
6880 return out;
6881}
6882
6883std::shared_ptr<xRooNode> xRooNode::getBrowsable(const char *name) const
6884{
6885 for (auto b : fBrowsables) {
6886 if (b && strcmp(b->GetName(), name) == 0)
6887 return b;
6888 }
6889 return nullptr;
6890}
6891
6893{
6894
6895 if (auto fr = get<RooFitResult>(); fr) {
6896 return nullptr;
6897 }
6898
6899 if (auto theData = get<RooDataSet>(); theData) {
6900
6901 TH1 *theHist = nullptr;
6902
6903 if (fromPad) {
6904 // find first histogram in pad
6905 for (auto o : *fromPad->GetListOfPrimitives()) {
6906 theHist = dynamic_cast<TH1 *>(o);
6907 if (theHist) {
6908 theHist = static_cast<TH1 *>(theHist->Clone());
6909 theHist->Reset();
6910 break;
6911 } // clone because theHist gets deleted below
6912 }
6913 }
6914
6915 if (!theHist) {
6916 auto _parentPdf = parentPdf();
6917 if (!_parentPdf) {
6918 // can still build graph if v is an obs ... will use v binning
6919 auto vo = dynamic_cast<TObject *>(v);
6920 if (v && obs().find(vo->GetName())) {
6921 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(v)) {
6922 theHist = new TH1D(
6923 TString::Format("%s_%s", GetName(), vo->GetName()),
6924 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
6925 cat->numTypes(), 0, cat->numTypes());
6926 int i = 1;
6927 std::map<int, std::string> cats; // fill into a map to preserve index ordering
6928 for (auto &c : *cat) {
6929 cats[c.second] = c.first;
6930 }
6931 for (auto &[_, label] : cats) {
6932 theHist->GetXaxis()->SetBinLabel(i++, label.c_str());
6933 }
6934 } else {
6935 auto _binning = v->getBinningPtr(nullptr);
6936 if (_binning->isUniform()) {
6937 theHist = new TH1D(
6938 TString::Format("%s_%s", GetName(), vo->GetName()),
6939 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
6940 v->numBins(), _binning->lowBound(), _binning->highBound());
6941 } else {
6942 theHist = new TH1D(
6943 TString::Format("%s_%s", GetName(), vo->GetName()),
6944 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
6945 v->numBins(), _binning->array());
6946 }
6947 }
6948 } else {
6949 throw std::runtime_error("Cannot draw dataset without parent PDF");
6950 }
6951 } else {
6952 theHist = _parentPdf->BuildHistogram(v, true);
6953 }
6954 }
6955 if (!theHist)
6956 return nullptr;
6957 // this hist will get filled with w*x to track weighted x position per bin
6958 TH1 *xPos = static_cast<TH1 *>(theHist->Clone("xPos"));
6959 xPos->Reset();
6960 TH1 *xPos2 = static_cast<TH1 *>(theHist->Clone("xPos2"));
6961 xPos2->Reset();
6962 auto nHist = std::unique_ptr<TH1>(static_cast<TH1 *>(theHist->Clone("nEntries")));
6963 nHist->Reset();
6964
6965 auto dataGraph = new TGraphAsymmErrors;
6966 dataGraph->SetEditable(false);
6967 dataGraph->SetName(GetName());
6968 dataGraph->SetTitle(strlen(theData->GetTitle()) ? theData->GetTitle() : theData->GetName());
6969 // next line triggers creation of the histogram inside the graph, in root 6.22 that isn't protected from being
6970 // added to gDirectory
6971 dataGraph->SetTitle(TString::Format("%s;%s;Events", dataGraph->GetTitle(), theHist->GetXaxis()->GetTitle()));
6972 *static_cast<TAttMarker *>(dataGraph) = *static_cast<TAttMarker *>(theHist);
6973 *static_cast<TAttLine *>(dataGraph) = *static_cast<TAttLine *>(theHist);
6974 dataGraph->SetMarkerStyle(20);
6975 dataGraph->SetLineColor(kBlack);
6976 dataGraph->SetMarkerSize(gStyle->GetMarkerSize());
6977
6978 auto _obs = obs();
6979
6980 // auto x = theData->get()->find((v) ? dynamic_cast<TObject*>(v)->GetName() : theHist->GetXaxis()->GetName());
6981 // const RooAbsReal* xvar = (x) ? dynamic_cast<RooAbsReal*>(x) : nullptr;
6982 // const RooAbsCategory* xcat = (x && !xvar) ? dynamic_cast<RooAbsCategory*>(x) : nullptr;
6983 auto x = _obs.find((v) ? dynamic_cast<TObject *>(v)->GetName()
6984 : (theHist->GetXaxis()->IsAlphanumeric() ? theHist->GetXaxis()->GetTimeFormatOnly()
6985 : theHist->GetXaxis()->GetName()));
6986 if (x && x->get<RooAbsArg>()->getAttribute("global")) {
6987 // is global observable ...
6988 dataGraph->SetPoint(0, x->get<RooAbsReal>()->getVal(), 1e-15);
6989 dataGraph->SetTitle(TString::Format("%s = %f", dataGraph->GetTitle(), dataGraph->GetPointX(0)));
6990 delete xPos;
6991 delete xPos2;
6992 delete theHist;
6993 return dataGraph;
6994 }
6995
6996 const RooAbsReal *xvar = (x) ? x->get<RooAbsReal>() : nullptr;
6997 const RooAbsCategory *xcat = (x && !xvar) ? x->get<RooAbsCategory>() : nullptr;
6998
6999 auto _coords = coords();
7000
7001 TString pName((fromPad) ? fromPad->GetName() : "");
7002 auto _pos = pName.Index('=');
7003
7004 int nevent = theData->numEntries();
7005 for (int i = 0; i < nevent; i++) {
7006 theData->get(i);
7007 bool _skip = false;
7008 for (auto _c : _coords) {
7009 if (auto cat = _c->get<RooAbsCategoryLValue>(); cat) {
7010 if (cat->getIndex() != theData->get()->getCatIndex(cat->GetName())) {
7011 _skip = true;
7012 break;
7013 }
7014 } else if (auto rv = _c->get<RooAbsRealLValue>(); rv) {
7015 // must be in range
7016 if (!rv->inRange(theData->get()->getRealValue(rv->GetName()), rv->getStringAttribute("coordRange"))) {
7017 _skip = true;
7018 break;
7019 }
7020 }
7021 }
7022 if (_pos != -1) {
7023 if (auto cat = dynamic_cast<RooAbsCategory *>(theData->get()->find(TString(pName(0, _pos))));
7024 cat && cat->getLabel() != pName(_pos + 1, pName.Length())) {
7025 _skip = true;
7026 }
7027 }
7028 if (_skip)
7029 continue;
7030
7031 if (xvar) {
7032 xPos->Fill(xvar->getVal(), xvar->getVal() * theData->weight());
7033 xPos2->Fill(xvar->getVal(), pow(xvar->getVal(), 2) * theData->weight());
7034 }
7035
7036 if (xcat) {
7037 theHist->Fill(xcat->getLabel(), theData->weight());
7038 nHist->Fill(xcat->getLabel(), 1);
7039 } else {
7040 theHist->Fill((x) ? xvar->getVal() : 0.5, theData->weight());
7041 nHist->Fill((x) ? xvar->getVal() : 0.5, 1);
7042 }
7043 }
7044
7045 xPos->Divide(theHist);
7046 xPos2->Divide(theHist);
7047
7048 // update the x positions to the means for each bin and use poisson asymmetric errors for data ..
7049 for (int i = 0; i < theHist->GetNbinsX(); i++) {
7050 if (includeZeros || nHist->GetBinContent(i + 1)) {
7051 double val = theHist->GetBinContent(i + 1);
7052
7053 dataGraph->SetPoint(dataGraph->GetN(),
7054 (xvar && val) ? xPos->GetBinContent(i + 1) : theHist->GetBinCenter(i + 1), val);
7055
7056 // x-error will be the (weighted) standard deviation of the x values ...
7057 double xErr = xPos2->GetBinContent(i + 1) - pow(xPos->GetBinContent(i + 1), 2);
7058 xErr = (xErr <= 0) ? 0. : sqrt(xErr); // protects against floating point rounding effects
7059
7060 if (xErr || val) {
7061 dataGraph->SetPointError(dataGraph->GetN() - 1, xErr, xErr,
7062 val - 0.5 * TMath::ChisquareQuantile(TMath::Prob(1, 1) / 2., 2. * (val)),
7063 0.5 * TMath::ChisquareQuantile(1. - TMath::Prob(1, 1) / 2., 2. * (val + 1)) -
7064 val);
7065 }
7066 }
7067 }
7068
7069 // transfer limits from theHist to dataGraph hist
7070 dataGraph->GetHistogram()->GetXaxis()->SetLimits(theHist->GetXaxis()->GetXmin(), theHist->GetXaxis()->GetXmax());
7071 // and bin labels, if any
7072 if (xcat) {
7073 dataGraph->GetHistogram()->GetXaxis()->Set(theHist->GetNbinsX(), 0, theHist->GetNbinsX());
7074 for (int i = 1; i <= theHist->GetNbinsX(); i++)
7075 dataGraph->GetHistogram()->GetXaxis()->SetBinLabel(i, theHist->GetXaxis()->GetBinLabel(i));
7076 }
7077
7078 delete xPos;
7079 delete xPos2;
7080 delete theHist;
7081
7082 // std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case getObject
7083 // has decided to return the owning ptr (for some reason) std::string _title =
7084 // strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(); if (!gROOT->GetStyle(_title.c_str()))
7085 // {
7086 // if ( (style = getObject<TStyle>(_title)) ) {
7087 // // loaded style (from workspace?) so put in list and use that
7088 // gROOT->GetListOfStyles()->Add(style.get());
7089 // } else {
7090 // // create new style - gets put in style list automatically so don't have to delete
7091 // // acquire them so saved to workspaces for auto reload ...
7092 // style = const_cast<xRooNode&>(*this).acquireNew<TStyle>(_title.c_str(),
7093 // TString::Format("Style for %s component", _title.c_str()));
7094 // (TAttLine &) (*style) = *dynamic_cast<TAttLine *>(dataGraph);
7095 // (TAttFill &) (*style) = *dynamic_cast<TAttFill *>(dataGraph);
7096 // (TAttMarker &) (*style) = *dynamic_cast<TAttMarker *>(dataGraph);
7097 // gROOT->GetListOfStyles()->Add(style.get());
7098 // }
7099 // }
7100 auto _styleNode = styles(dataGraph);
7101 if (auto _style = _styleNode.get<TStyle>()) {
7102 *dynamic_cast<TAttLine *>(dataGraph) = *_style;
7103 *dynamic_cast<TAttFill *>(dataGraph) = *_style;
7104 *dynamic_cast<TAttMarker *>(dataGraph) = *_style;
7105 }
7106 return dataGraph;
7107 }
7108
7109 throw std::runtime_error("Cannot build graph");
7110}
7111
7113{
7114 if (fr) {
7115 if (auto _w = ws(); _w) {
7116 auto res = acquire(std::shared_ptr<RooFitResult>(const_cast<RooFitResult *>(fr), [](RooFitResult *) {}));
7117 for (auto o : _w->allGenericObjects()) {
7118 if (auto _fr = dynamic_cast<RooFitResult *>(o); _fr) {
7119 _fr->ResetBit(1 << 20);
7120 }
7121 }
7122 res->SetBit(1 << 20);
7123 // assign values
7124 auto allVars = _w->allVars();
7125 allVars = fr->floatParsFinal();
7126 allVars = fr->constPars();
7127 } else {
7128 // need to add to memory as a specific name
7129 throw std::runtime_error("Not supported yet"); // complication is how to replace an existing fitResult in
7130 // .memory auto _clone = std::make_shared<RooFitResult>(*fr);
7131 //_clone->SetName("fitResult");
7132 }
7133 } else {
7135 }
7136}
7137
7139{
7140 if (auto _fr = fr.get<const RooFitResult>()) {
7142 } else
7143 throw std::runtime_error("Not a RooFitResult");
7144}
7145
7146xRooNode xRooNode::fitResult(const char *opt) const
7147{
7148
7149 if (get<RooFitResult>())
7150 return *this;
7151 if (get<RooAbsData>()) {
7152 if (auto _fr = find(".fitResult"); _fr)
7153 return _fr;
7154#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
7155 // check if weightVar of RooAbsData has fitResult attribute on it, will be the generation fit result
7156 if (get<RooDataSet>() && get<RooDataSet>()->weightVar() &&
7157 get<RooDataSet>()->weightVar()->getStringAttribute("fitResult")) {
7158 return xRooNode(getObject<const RooFitResult>(get<RooDataSet>()->weightVar()->getStringAttribute("fitResult")),
7159 *this);
7160 }
7161#endif
7162 return xRooNode();
7163 }
7164
7165 TString sOpt(opt);
7166 if (sOpt == "prefit") {
7167 // build a fitResult using nominal values and infer errors from constraints
7168 // that aren't the 'main' constraints
7169 // Warning("fitResult","Building prefitResult by examining pdf. Consider setting an explicit prefitResult
7170 // (SetFitResult(fr)) where fr name is prefitResult");
7171
7172 // ensure coefs are included if there are any
7173 auto _coefs = coefs();
7174 if (_coefs.get()) {
7175 return xRooNode(RooProduct("tmp", "tmp", RooArgList(*get<RooAbsArg>(), *_coefs.get<RooAbsReal>())))
7176 .fitResult(opt);
7177 }
7178
7179 std::unique_ptr<RooArgList> _pars(dynamic_cast<RooArgList *>(pars().argList().selectByAttrib("Constant", false)));
7180 auto fr = std::make_shared<RooFitResult>("prefitResult", "Prefit");
7181 fr->setFinalParList(*_pars);
7182 for (auto &p : fr->floatParsFinal()) {
7183 auto _v = dynamic_cast<RooRealVar *>(p);
7184 if (!_v)
7185 continue;
7186 if (auto s = _v->getStringAttribute("nominal"); s)
7187 _v->setVal(TString(s).Atof());
7188 auto _constr = xRooNode(fParent->getObject<RooRealVar>(p->GetName()), *this).constraints();
7189 std::shared_ptr<xRooNode> pConstr;
7190 for (auto &c : _constr) {
7191 if (c->get<RooPoisson>() || c->get<RooGaussian>()) {
7192 // require parameter to be a direct server of the constraint pdf to count
7193 bool isServer = true;
7194 if (c->get<RooGaussian>()) {
7195 isServer = false;
7196 for (auto s : c->get<RooAbsArg>()->servers()) {
7197 if (strcmp(s->GetName(), p->GetName()) == 0) {
7198 isServer = true;
7199 break;
7200 }
7201 }
7202 }
7203 if (isServer) {
7204 pConstr = c;
7205 break;
7206 }
7207 }
7208 }
7209 if (pConstr) {
7210 // there will be 3 deps, one will be this par, the other two are the mean and error (or error^2 in case of
7211 // poisson use the one that's a ConstVar as the error to break a tie ...
7212 double prefitVal = 0;
7213 double prefitError = 0;
7214 for (auto &_d : pConstr->vars()) {
7215 if (strcmp(p->GetName(), _d->get()->GetName()) == 0)
7216 continue;
7217 if (auto _c = _d->get<RooConstVar>(); _c && _c->getVal() != 0) {
7218 if (prefitError)
7219 prefitVal = prefitError; // loading val into error already, so move it over
7220 prefitError = _c->getVal();
7221 } else if (prefitError == 0) {
7222 prefitError = _d->get<RooAbsReal>()->getVal();
7223 } else {
7224 prefitVal = _d->get<RooAbsReal>()->getVal();
7225 }
7226 }
7227
7228 if (pConstr->get<RooGaussian>() && pConstr->browse().find(".sigma")) {
7229 prefitError = pConstr->find(".sigma")->get<RooAbsReal>()->getVal();
7230 }
7231 // std::cout << p->GetName() << " extracted " << prefitVal << " " << prefitError << " from ";
7232 // pConstr->deps().Print();
7233 if (pConstr->get<RooPoisson>()) {
7234 // prefitVal will be the global observable value, need to divide that by tau
7236 // prefiterror will be tau ... need 1/sqrt(tau) for error
7237 prefitError = 1. / sqrt(prefitError);
7238 }
7239 if (!_v->getStringAttribute("nominal"))
7240 _v->setVal(prefitVal);
7241 _v->setError(prefitError);
7242 } else {
7243 // unconstrained, remove error
7244 _v->removeError();
7245 }
7246 }
7247 auto _args = consts().argList();
7248 _args.add(pp().argList());
7249 // global obs are added to constPars list too
7250 auto _globs = globs(); // keep alive as may own glob
7251 _args.add(_globs.argList());
7252 fr->setConstParList(_args);
7253 std::unique_ptr<RooArgList> _snap(dynamic_cast<RooArgList *>(_pars->snapshot()));
7254 for (auto &p : *_snap) {
7255 if (auto atr = p->getStringAttribute("initVal"); atr && dynamic_cast<RooRealVar *>(p))
7256 dynamic_cast<RooRealVar *>(p)->setVal(TString(atr).Atof());
7257 }
7258 fr->setInitParList(*_snap);
7259 return xRooNode(fr, *this);
7260 }
7261
7262 // return first checked fit result present in the workspace
7263 if (auto _w = ws(); _w) {
7264 auto checkFr = [&](TObject *o) {
7265 if (auto _fr = dynamic_cast<RooFitResult *>(o); _fr && _fr->TestBit(1 << 20)) {
7266 // check all pars match final/const values ... if mismatch need to create a new RooFitResult
7270 for (auto p : pars()) {
7271 if (p->get<RooAbsArg>()->getAttribute("Constant") || p->get<RooConstVar>()) {
7272 // par must not be in the float list or have different value to what is in constPars (if it is there)
7273 if (_fr->floatParsFinal().find(p->GetName()) ||
7274 (p->get<RooAbsReal>() &&
7275 std::abs(_fr->constPars().getRealValue(p->GetName(), std::numeric_limits<double>::quiet_NaN()) -
7276 p->get<RooAbsReal>()->getVal()) > 1e-15) ||
7277 (p->get<RooAbsCategory>() &&
7278 p->get<RooAbsCategory>()->getCurrentIndex() !=
7279 _fr->constPars().getCatIndex(p->GetName(), std::numeric_limits<int>().max()))) {
7280 newConsts.add(*p->get<RooAbsArg>());
7281 }
7282 } else {
7283 // floating par must be present in the floatPars list with the same value
7284 if (!_fr->floatParsFinal().find(p->GetName())) {
7285 newFloats.add(*p->get<RooAbsArg>());
7286 } else if ((p->get<RooAbsReal>() &&
7287 std::abs(_fr->floatParsFinal().getRealValue(p->GetName(),
7288 std::numeric_limits<double>::quiet_NaN()) -
7289 p->get<RooAbsReal>()->getVal()) > 1e-15) ||
7290 (p->get<RooAbsCategory>() &&
7291 p->get<RooAbsCategory>()->getCurrentIndex() !=
7292 _fr->floatParsFinal().getCatIndex(p->GetName(), std::numeric_limits<int>().max()))) {
7293 // value of existing float changed
7294 oldFloats.add(*p->get<RooAbsArg>());
7295 }
7296 }
7297 }
7298 if (!oldFloats.empty() || !newFloats.empty() || !newConsts.empty()) {
7299 // create new fit result using covariance from the fit result
7300 // remove any new consts from the list before extracting covariance matrix
7301 RooArgList existingFloats(_fr->floatParsFinal());
7302 existingFloats.remove(newConsts, true, true /* match name*/);
7303 auto cov = _fr->reducedCovarianceMatrix(existingFloats);
7304 if (!newFloats.empty()) {
7305 // extend the covariance matrix and add variances using current parameter errors
7306 size_t oldSize = existingFloats.size();
7307 cov.ResizeTo(oldSize + newFloats.size(), oldSize + newFloats.size());
7308 for (size_t i = 0; i < newFloats.size(); i++) {
7309 existingFloats.add(*newFloats.at(i));
7310 auto v = dynamic_cast<RooRealVar *>(newFloats.at(i));
7311 if (v)
7312 cov(oldSize + i, oldSize + i) = std::pow(v->getError(), 2);
7313 }
7314 }
7315 RooArgList existingConsts(_fr->constPars());
7316 existingConsts.remove(newFloats, true, true);
7318
7319 // do we need to add our remaining const pars to the const par list? or the globs?
7320 // for speed we wont bother
7321 // note that generating datasets needs the globs in the const pars list so the robs can be determined
7322 // at the moment this check is done in the generate() method (along with check for missing pars)
7323
7324 auto fr = std::make_shared<RooFitResult>(TString::Format("%s-dirty", _fr->GetName()));
7325 fr->setFinalParList(existingFloats);
7326 fr->setConstParList(existingConsts);
7327 fr->setCovarianceMatrix(cov);
7328 fr->setInitParList(_fr->floatParsInit()); // will only be the pars that were actually float for the fit
7329
7330 return xRooNode(fr, *this);
7331 } else {
7332 // all matching, can return the fit result as-is
7333 return xRooNode(*_fr, std::make_shared<xRooNode>(*_w, std::make_shared<xRooNode>()));
7334 }
7335 }
7336 return xRooNode();
7337 };
7338 for (auto o : _w->allGenericObjects()) {
7339 auto out = checkFr(o);
7340 if (out)
7341 return out;
7342 }
7343 for (auto o : GETWSSNAPSHOTS(_w)) {
7344 auto out = checkFr(o);
7345 if (out)
7346 return out;
7347 }
7348 } else {
7349 // objects not in workspaces are allowed to have a fitResult set in their memory
7350 // use getObject to get it
7351 if (auto fr = getObject<RooFitResult>(".fitResult"); fr) {
7352 return xRooNode(fr, *this);
7353 }
7354 }
7355
7356 // ensure coefs are included if there are any
7357 auto _coefs = coefs();
7358 if (_coefs.get()) {
7359 return xRooNode(RooProduct("tmp", "tmp", RooArgList(*get<RooAbsArg>(), *_coefs.get<RooAbsReal>())))
7360 .fitResult(opt);
7361 }
7362
7363 std::unique_ptr<RooArgList> _pars(dynamic_cast<RooArgList *>(pars().argList().selectByAttrib("Constant", false)));
7364 auto fr = std::make_shared<RooFitResult>(TUUID().AsString());
7365 fr->SetTitle(TString::Format("%s uncorrelated parameter snapshot", GetName()));
7366 fr->setFinalParList(*_pars);
7367 fr->setStatus(-1);
7368
7369 TMatrixDSym cov(fr->floatParsFinal().size());
7370 TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr.get(), _VM));
7371 if (prevCov) {
7372 for (int i = 0; i < prevCov->GetNcols(); i++) {
7373 for (int j = 0; j < prevCov->GetNrows(); j++) {
7374 cov(i, j) = (*prevCov)(i, j);
7375 }
7376 }
7377 }
7378 int i = 0;
7379 for (auto &p : fr->floatParsFinal()) {
7380 if (!prevCov || i >= prevCov->GetNcols()) {
7381 if (auto v = dynamic_cast<RooRealVar *>(p)) {
7382 cov(i, i) = pow(v->getError(), 2);
7383 } else {
7384 cov(i, i) = 0;
7385 }
7386 }
7387 i++;
7388 }
7389 int covQualBackup = fr->covQual();
7390 fr->setCovarianceMatrix(cov);
7391 fr->setCovQual(covQualBackup);
7392
7393 auto _args = consts().argList();
7394 _args.add(pp().argList());
7395 // global obs are added to constPars list too
7396 auto _globs = globs(); // keep alive as may own glob
7397 _args.add(_globs.argList());
7398 fr->setConstParList(_args);
7399 std::unique_ptr<RooArgList> _snap(dynamic_cast<RooArgList *>(_pars->snapshot()));
7400 for (auto &p : *_snap) {
7401 if (auto atr = p->getStringAttribute("initVal"); atr && dynamic_cast<RooRealVar *>(p))
7402 dynamic_cast<RooRealVar *>(p)->setVal(TString(atr).Atof());
7403 }
7404 fr->setInitParList(*_snap);
7405
7406 // return *const_cast<Node2*>(this)->emplace_back(std::make_shared<Node2>(".fitResult",fr,*this));
7407 return xRooNode(fr, *this);
7408}
7409
7410// xRooNode xRooNode::fitTo_(const char* datasetName) const {
7411// try {
7412// return fitTo(datasetName);
7413// } catch(const std::exception& e) {
7414// new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),kMBIconExclamation); // deletes
7415// self on dismiss? return xRooNode();
7416// }
7417// }
7418//
7419// xRooNode xRooNode::fitTo(const char* datasetName) const {
7420// return fitTo(*datasets().at(datasetName));
7421// }
7422
7423void xRooNode::SetRange(const char *range, double low, double high)
7424{
7425 if (!std::isnan(low) && !std::isnan(high) && get<RooRealVar>()) {
7426 if (range && strlen(range)) {
7427 get<RooRealVar>()->setRange(range, low, high);
7428 } else {
7429 get<RooRealVar>()->setRange(low, high);
7430 }
7431 return;
7432 }
7433 if (auto o = get<RooAbsArg>(); o)
7434 o->setStringAttribute("range", range);
7435 // todo: clear the range attribute on all servers
7436 // could make this controlled by a flag but probably easiest to enforce so you must set range
7437 // in children after if you wanted to override
7438}
7439const char *xRooNode::GetRange() const
7440{
7441 std::string &out = fRange;
7442 if (auto o = get<RooAbsArg>(); o && o->getStringAttribute("range"))
7443 out = o->getStringAttribute("range");
7444 auto _parent = fParent;
7445 while (out.empty() && _parent) {
7446 if (auto o = _parent->get<RooAbsArg>(); o && o->getStringAttribute("range"))
7447 out = o->getStringAttribute("range");
7448 _parent = _parent->fParent;
7449 }
7450 return out.c_str();
7451}
7452
7453xRooNLLVar xRooNode::nll(const char *_data, std::initializer_list<RooCmdArg> nllOpts) const
7454{
7455 return nll(xRooNode(_data), nllOpts);
7456}
7457
7459{
7460 return nll(_data, *xRooFit::createNLLOptions());
7461}
7462
7463xRooNLLVar xRooNode::nll(const xRooNode &_data, std::initializer_list<RooCmdArg> nllOpts) const
7464{
7465 auto defaultOpts = xRooFit::createNLLOptions(); // smart pointer will cleanup the list
7466 // add user-specified options to list ... if already existing in default list, override and warn
7468 for (auto opt : *defaultOpts) {
7469 l.Add(opt);
7470 }
7471 for (auto &i : nllOpts) {
7472 if (auto o = l.FindObject(i.GetName())) {
7473 Info("nll", "Overriding NLL Option: %s", o->GetName());
7474 l.Remove(o);
7475 }
7476 l.Add(const_cast<RooCmdArg *>(&i));
7477 }
7478
7479 return nll(_data, l);
7480}
7481
7483{
7484 if (auto mc = get<RooStats::ModelConfig>()) {
7485 return xRooNode(*mc->GetPdf(), fParent).generate(fr, expected, seed);
7486 }
7487
7488 if (!get<RooAbsPdf>()) {
7489 // before giving up, if this is a workspace we can proceed if we only have one model
7490 if (get<RooWorkspace>()) {
7491 std::shared_ptr<xRooNode> mainModel;
7492 for (auto &c : const_cast<xRooNode *>(this)->browse()) {
7493 if (c->get<RooAbsPdf>()) {
7494 if (!mainModel) {
7495 mainModel = c;
7496 } else {
7497 throw std::runtime_error(TString::Format("Workspace has multiple models, you must specify which to "
7498 "generate with (found at least %s and %s)",
7499 mainModel->GetName(), c->GetName()));
7500 }
7501 }
7502 }
7503 if (mainModel)
7504 return mainModel->generate(fr, expected, seed);
7505 }
7506 throw std::runtime_error(TString::Format("%s is not a pdf", GetName()));
7507 }
7508
7509 // when generating, will only include channels that are selected
7510 // any unselected but not hidden channel will have data from the only selected dataset added to it
7511 if (get<RooSimultaneous>()) {
7512 std::string selected;
7513 std::string fromds; // list of channels to take from selected ds
7514 bool hasDeselected = false;
7515 for (auto c : bins()) {
7516 TString cName(c->GetName());
7517 cName = cName(cName.Index('=') + 1, cName.Length());
7518 if (!c->get<RooAbsReal>()->isSelectedComp()) {
7519 hasDeselected = true;
7520 if (!c->get<RooAbsArg>()->getAttribute("hidden")) {
7521 if (!fromds.empty())
7522 fromds += ",";
7523 fromds += cName.Data();
7524 }
7525 } else {
7526 if (!selected.empty())
7527 selected += ",";
7528 selected += cName.Data();
7529 }
7530 }
7531 if (hasDeselected) {
7532 std::string dsetName = "";
7533 if (!fromds.empty()) {
7534 // use the first selected dataset as protodata
7535 auto _dsets = datasets();
7536 for (auto &d : _dsets) {
7537 if (d->get()->TestBit(1 << 20)) {
7538 dsetName = d->get()->GetName();
7539 break;
7540 }
7541 }
7542 if (dsetName.empty()) {
7543 throw std::runtime_error(
7544 "Need at least one dataset selected (SetChecked) to use for deselected regions");
7545 }
7546 }
7547 auto result = reduced(selected).generate(fr, expected, seed);
7548 if (!fromds.empty()) {
7549 auto ds = reduced(fromds).datasets()[dsetName];
7550 result.Add(*ds);
7551 result.SetName(TString(result.GetName()) + "_and_" + dsetName.c_str());
7552 }
7553 return result;
7554 }
7555 }
7556
7557 auto _fr = fr.get<RooFitResult>();
7558 xRooNode fr2;
7559 if (!_fr) {
7560 fr2 = fitResult();
7561 _fr = fr2.get<RooFitResult>();
7562 }
7563
7564 // must ensure fr has all the globs in its constPars list ... any missing must be added
7565 // otherwise generateFrom method wont determine globs properly
7566 // same for any missing pars
7567 auto _globs = globs();
7568 bool missingGlobs(false);
7569 for (auto glob : _globs) {
7570 if (!_fr->constPars().find(*glob->get<RooAbsArg>())) {
7571 missingGlobs = true;
7572 break;
7573 }
7574 }
7575
7576 std::unique_ptr<RooFitResult> newFr;
7577 if (missingGlobs) {
7578 newFr = std::make_unique<RooFitResult>(*_fr);
7579 for (auto glob : _globs) {
7580 if (!newFr->constPars().find(*glob->get<RooAbsArg>())) {
7581 const_cast<RooArgList &>(newFr->constPars()).addClone(*glob->get<RooAbsArg>());
7582 }
7583 }
7584 _fr = newFr.get();
7585 }
7586
7587 // check for missing fundamental pars (consts are not fundamentals)
7588 auto _pars = pars();
7589 bool missingPars(false);
7590 for (auto par : _pars) {
7591 if (!par->get<RooAbsArg>()->isFundamental())
7592 continue;
7593 if (!_fr->constPars().find(*par->get<RooAbsArg>()) && !_fr->floatParsFinal().find(*par->get<RooAbsArg>())) {
7594 missingPars = true;
7595 break;
7596 }
7597 }
7598
7599 if (missingPars) {
7600 newFr = std::make_unique<RooFitResult>(*_fr);
7601 for (auto par : _pars) {
7602 if (!par->get<RooAbsArg>()->isFundamental())
7603 continue;
7604 if (!newFr->constPars().find(*par->get<RooAbsArg>()) &&
7605 !newFr->floatParsFinal().find(*par->get<RooAbsArg>())) {
7606 const_cast<RooArgList &>(newFr->constPars()).addClone(*par->get<RooAbsArg>());
7607 }
7608 }
7609 _fr = newFr.get();
7610 }
7611
7612 return xRooNode(xRooFit::generateFrom(*get<RooAbsPdf>(), *_fr, expected, seed).first, *this);
7613
7614 // should add coords to the dataset too?
7615 // e.g. in the case of generating a dataset for a single channel, include the channelCat
7616 // this will allow datasets to then be combined.
7617 // could just say users must use 'reduced' on the simPdf, even if reducing to a single channel
7618}
7619
7620xRooNLLVar xRooNode::nll(const xRooNode &_data, const RooLinkedList &opts) const
7621{
7622 if (auto mc = get<RooStats::ModelConfig>()) {
7623 if (mc->GetExternalConstraints()) {
7625 for (auto o : opts) {
7626 optsWithConstraints.Add(o->Clone(nullptr));
7627 }
7628 optsWithConstraints.Add(RooFit::ExternalConstraints(*mc->GetExternalConstraints()).Clone(nullptr));
7629 return xRooNode(*mc->GetPdf(), fParent).nll(_data, optsWithConstraints);
7630 } else {
7631 return xRooNode(*mc->GetPdf(), fParent).nll(_data, opts);
7632 }
7633 }
7634
7635 if (!get<RooAbsPdf>()) {
7636 // before giving up, if this is a workspace we can proceed if we only have one model or pdf
7637 if (get<RooWorkspace>()) {
7638 std::shared_ptr<xRooNode> mainPdf, mainModel, otherPdf;
7639 for (auto &c : const_cast<xRooNode *>(this)->browse()) {
7640 if (c->get<RooAbsPdf>()) {
7641 if (!mainPdf) {
7642 mainPdf = c;
7643 } else {
7644 otherPdf = c;
7645 }
7646 } else if (c->get<RooStats::ModelConfig>()) {
7647 if (!mainModel) {
7648 mainModel = c;
7649 } else {
7650 throw std::runtime_error(TString::Format("Workspace has multiple models, you must specify which to "
7651 "build nll with (found at least %s and %s)",
7652 mainModel->GetName(), c->GetName()));
7653 }
7654 }
7655 }
7656 if (mainModel)
7657 return mainModel->nll(_data, opts);
7658 if (mainPdf) {
7659 if (otherPdf) {
7660 throw std::runtime_error(TString::Format("Workspace has multiple pdfs, you must specify which to "
7661 "build nll with (found at least %s and %s)",
7662 mainPdf->GetName(), otherPdf->GetName()));
7663 }
7664 return mainPdf->nll(_data, opts);
7665 }
7666 }
7667 throw std::runtime_error(TString::Format("%s is not a pdf", GetName()));
7668 }
7669
7670 // if simultaneous and any channels deselected then reduce and return
7671 if (get<RooSimultaneous>()) {
7672 std::string selected;
7673 bool hasDeselected = false;
7674 for (auto c : bins()) {
7675 if (!c->get<RooAbsReal>()->isSelectedComp()) {
7676 hasDeselected = true;
7677 } else {
7678 TString cName(c->GetName());
7679 cName = cName(cName.Index('=') + 1, cName.Length());
7680 if (!selected.empty())
7681 selected += ",";
7682 selected += cName.Data();
7683 }
7684 }
7685 if (hasDeselected)
7686 return reduced(selected).nll(_data, opts);
7687 }
7688
7689 if (!_data.get<RooAbsData>()) {
7690 // use node name to find dataset and recall
7691 auto _d = strlen(_data.GetName()) ? datasets().find(_data.GetName()) : nullptr;
7692 if (strlen(_data.GetName()) == 0) {
7693 // create the EXPECTED (asimov) dataset with the observables
7695 _d = std::make_shared<xRooNode>(asi.first, *this);
7696 if (asi.second) {
7697 _d->emplace_back(
7698 std::make_shared<xRooNode>(".globs", std::const_pointer_cast<RooAbsCollection>(asi.second), *_d));
7699 }
7700 } else if (!_d) {
7701 throw std::runtime_error(TString::Format("Cannot find dataset %s", _data.GetName()));
7702 }
7703 return nll(*_d, opts);
7704 } else if (!_data.fParent || _data.fParent->fComp != fComp) {
7705 // dataset is not parented by this node ... meaning it may need to be reduced,
7706 // do this via the datasets() method by attaching and detaching the dataset
7707 xRooNode me(*this); // since we are in a const method, need to create a copy node.
7708 me.push_back(std::make_shared<xRooNode>(_data));
7709 return nll(*me.datasets().at(_data.GetName()), opts);
7710 }
7711
7712 auto _globs = _data.globs(); // keep alive because may own the globs
7713
7714 auto _opts = std::shared_ptr<RooLinkedList>(new RooLinkedList, [](RooLinkedList *l) {
7715 if (l)
7716 l->Delete();
7717 delete l;
7718 });
7719 RooArgSet _globsSet(_globs.argList());
7721 if (GetRange() && strlen(GetRange()))
7722 _opts->Add(RooFit::Range(GetRange()).Clone());
7723
7724 // copy over opts ... need to clone each so can safely delete when _opts destroyed
7725 for (int i = 0; i < opts.GetSize(); i++) {
7726 if (strlen(opts.At(i)->GetName()) == 0)
7727 continue; // skipping "none" cmds
7728 if (strcmp(opts.At(i)->GetName(), "GlobalObservables") == 0) {
7729 // maybe warn here?
7730 } else {
7731 _opts->Add(opts.At(i)->Clone(nullptr)); // nullptr needed because accessing Clone via TObject base class puts
7732 // "" instead, so doesnt copy names
7733 }
7734 }
7735
7736 // use shared_ptr method so NLLVar will take ownership of datasets etc if created above
7737 // snapshots the globs out of the nllOpts (see specific constructor of xRooNLLVar)
7738 auto out = xRooFit::createNLL(std::dynamic_pointer_cast<RooAbsPdf>(fComp),
7739 std::dynamic_pointer_cast<RooAbsData>(_data.fComp), *_opts);
7740 return out;
7741}
7742
7743// xRooNode xRooNode::fitTo(const xRooNode& _data) const {
7744//
7745//
7746// auto _pdf = get<RooAbsPdf>();
7747// if (!_pdf) throw std::runtime_error("Not a pdf");
7748//
7749// auto _globs = _data.globs(); // keep alive because may own the globs
7750// RooArgSet globsSet(_globs.argList());
7751//
7752// std::shared_ptr<RooSimultaneous> newPdf;
7753// if(auto s = get<RooSimultaneous>(); s) {
7754// auto rangeName = GetRange();
7755// if (rangeName) {
7756// // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
7757// std::vector<TString> chanPatterns;
7758// TStringToken pattern(rangeName, ",");
7759// while (pattern.NextToken()) {
7760// chanPatterns.emplace_back(pattern);
7761// }
7762// auto& _cat = const_cast<RooAbsCategoryLValue&>(s->indexCat());
7763// newPdf = std::make_shared<RooSimultaneous>(TString::Format("%s_reduced",GetName()),"Reduced model",_cat);
7764// for(auto& c : variations()) {
7765// TString cName(c->GetName());
7766// cName = cName(cName.Index('=')+1,cName.Length());
7767// _cat.setLabel(cName);
7768// bool matchAny=false;
7769// for(auto& p : chanPatterns) {
7770// if (cName.Contains(TRegexp(p,true))) { matchAny=true; break; }
7771// if (_cat.hasRange(p) && _cat.inRange(p)) { matchAny=true; break; }
7772// }
7773// if(matchAny) {
7774// newPdf->addPdf( *c->get<RooAbsPdf>(), cName );
7775// }
7776// }
7777// RooFitResultTree t(newPdf->GetName(),"",*newPdf);
7778// auto _fr = std::const_pointer_cast<RooFitResult>(t.fitTo(_data.get<RooAbsData>(), &globsSet));
7779// xRooNode parent(_data.GetName(),nullptr,*this);
7780// xRooNode out(_fr->GetName(),/*acquire(_fr)*/ _fr,parent);
7781// // do full propagation by 'checking' the fr ...
7782// out.Checked(&out,true);
7783// return out;
7784// }
7785// }
7786//
7787//
7788//
7789// std::string treeName = TString::Format("fits_%s",GetName()).Data();
7790//
7791// auto _frt = getObject<TTree>(treeName); // get existing frt
7792//
7793// std::shared_ptr<RooFitResultTree> t;
7794// if (_frt) {
7795// t = std::make_shared<RooFitResultTree>(_frt.get());
7796// } else {
7797// t = std::make_shared<RooFitResultTree>(treeName.c_str(),"",*_pdf);
7798// }
7799// //t->SetProgress(true);
7800// auto _fr = std::const_pointer_cast<RooFitResult>(t->fitTo(_data.get<RooAbsData>(), &globsSet));
7801//
7802//
7803//
7804// /*
7805// obs().argList() = s; // sets global observables to their values
7806// auto _fr =
7807// std::shared_ptr<RooFitResult>(_pdf->fitTo(*_data->get<RooAbsData>(),RooFit::GlobalObservables(s),RooFit::Offset(true),RooFit::Save()));
7808// _fr->SetName(TUUID().AsString());
7809// // restore parameters before returning
7810// *std::unique_ptr<RooArgSet>(_pdf->getDependents(_fr->floatParsFinal())) = _fr->floatParsInit();
7811// */
7812//
7813// //_fr->SetTitle(TString::Format("%s;%s",GetName(),datasetName));
7814// if (!_frt) {
7815// t =
7816// std::make_shared<RooFitResultTree>(std::dynamic_pointer_cast<TTree>(const_cast<xRooNode*>(this)->acquire(t->fTree)).get());
7817// }
7818// xRooNode parent(_data.GetName(),nullptr,xRooNode(t,*this));
7819// xRooNode out(_fr->GetName(),/*acquire(_fr)*/ _fr,parent);
7820// // do full propagation by 'checking' the fr ...
7821// out.Checked(&out,true);
7822// return out;
7823// }
7824
7825std::shared_ptr<xRooNode> xRooNode::parentPdf() const
7826{
7827 // find first parent that is a pdf
7828 auto out = fParent;
7829 while (out && !out->get<RooAbsPdf>()) {
7830 out = out->fParent;
7831 }
7832 return out;
7833}
7834
7835xRooNode xRooNode::reduced(const std::string &_range, bool invert) const
7836{
7837 auto rangeName = (_range.empty()) ? GetRange() : _range;
7838 if (!rangeName.empty()) {
7839 std::vector<TString> patterns;
7840 TStringToken pattern(rangeName, ",");
7841 while (pattern.NextToken()) {
7842 patterns.emplace_back(pattern);
7843 }
7844 if (auto s = get<RooSimultaneous>(); s) {
7845 // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
7846 auto &_cat = const_cast<RooAbsCategoryLValue &>(s->indexCat());
7847 auto newPdf =
7848 std::make_shared<RooSimultaneous>(TString::Format("%s_reduced", GetName()), "Reduced model", _cat);
7849 for (auto &c : bins()) {
7850 TString cName(c->GetName());
7851 cName = cName(cName.Index('=') + 1, cName.Length());
7852 _cat.setLabel(cName);
7853 bool matchAny = false;
7854 for (auto &p : patterns) {
7855 if (cName.Contains(TRegexp(p, true))) {
7856 matchAny = true;
7857 break;
7858 }
7859 if (_cat.hasRange(p) && _cat.inRange(p)) {
7860 matchAny = true;
7861 break;
7862 }
7863 }
7864 if ((matchAny && !invert) || (!matchAny && invert)) {
7865 newPdf->addPdf(*c->get<RooAbsPdf>(), cName);
7866 }
7867 }
7868 return xRooNode(newPdf, fParent);
7869 } else if (get() && !get<RooAbsCollection>() && !components().empty()) {
7870 // create a new obj and remove non-matching components
7871 xRooNode out(std::shared_ptr<TObject>(get()->Clone(TString::Format("%s_reduced", get()->GetName()))), fParent);
7872 // go through components and remove any that don't match pattern
7873 std::vector<TObject *> funcs; // to be removed
7874 for (auto &c : out.components()) {
7875 bool matchAny = false;
7876 for (auto &p : patterns) {
7877 if (TString(c->GetName()).Contains(TRegexp(p, true))) {
7878 matchAny = true;
7879 break;
7880 }
7881 }
7882 if (!((matchAny && !invert) || (!matchAny && invert)))
7883 funcs.push_back(c->get());
7884 }
7885 for (auto &c : funcs)
7886 out.Remove(*c);
7887 if (!funcs.empty()) {
7888 if (auto _pdf = out.get<RooRealSumPdf>(); _pdf) {
7889 _pdf->setFloor(false); // remove floor if removed some functions, which allows evaluation of negative
7890 // valued components
7891 }
7892 }
7893 out.browse();
7894 return out;
7895 } else if (auto fr = get<RooFitResult>()) {
7896 // reduce the fit result by moving unselected float pars into the constPars list and dropping their covariances
7897 xRooNode out(std::shared_ptr<TObject>(fr->Clone(TString::Format("%s_reduced", fr->GetName()))), fParent);
7898 fr = out.get<RooFitResult>();
7899 RooArgList _pars = fr->floatParsFinal();
7901 for (auto c : _pars) {
7902 bool matchAny = false;
7903 for (auto &p : patterns) {
7904 if (TString(c->GetName()).Contains(TRegexp(p, true))) {
7905 matchAny = true;
7906 break;
7907 }
7908 }
7909 if (!((matchAny && !invert) || (!matchAny && invert))) {
7910 _remPars.add(*c);
7911 }
7912 }
7913 _pars.remove(_remPars, true);
7914
7915 auto _tmp = fr->reducedCovarianceMatrix(_pars);
7916 int covQualBackup = fr->covQual();
7917 fr->setCovarianceMatrix(_tmp);
7918 fr->setCovQual(covQualBackup);
7919 const_cast<RooArgList &>(fr->floatParsFinal())
7920 .remove(_remPars, true); // is this a memory leak ... should delete the remPars?
7921 return out;
7922
7923 } else if (!get() || get<RooAbsCollection>()) {
7924 // filter the children .... handle special case of filtering ".vars" with "x" option too
7925 xRooNode out(std::make_shared<RooArgList>(), fParent);
7926 out.SetName(TString(GetName()) + "_reduced");
7927 size_t nobs = 0;
7928 bool notAllArgs = false;
7929 bool isVars = (strcmp(GetName(), ".vars") == 0);
7930 for (auto c : *this) {
7931 nobs += (c->fFolder == "!robs" || c->fFolder == "!globs");
7932 bool matchAny = false;
7933 for (auto &p : patterns) {
7934 if (TString(c->GetName()).Contains(TRegexp(p, true)) ||
7935 (isVars && p == "x" && (c->fFolder == "!robs" || c->fFolder == "!globs") && nobs == 1)) {
7936 matchAny = true;
7937 break;
7938 }
7939 }
7940 if ((matchAny && !invert) || (!matchAny && invert)) {
7941 out.push_back(c);
7942 if (auto a = c->get<RooAbsArg>()) {
7943 out.get<RooArgList>()->add(*a);
7944 } else {
7945 notAllArgs = true;
7946 }
7947 }
7948 }
7949 if (notAllArgs) {
7950 out.fComp.reset();
7951 }
7952 return out;
7953 }
7954 }
7955
7956 return get<RooArgList>() ? xRooNode(std::make_shared<RooArgList>(), fParent) : *this;
7957}
7958
7959// xRooNode xRooNode::generate(bool expected) const {
7960//
7961// auto fr = fitResult();
7962// auto _fr = fr.get<RooFitResult>();
7963//
7964// auto _pdf = (get<RooAbsPdf>()) ? std::shared_ptr<const xRooNode>(this, [](const xRooNode*){}) : parentPdf();
7965// if (!_pdf) {
7966// throw std::runtime_error("Could not find pdf");
7967// }
7968//
7969// std::shared_ptr<RooDataTree> t;
7970//
7971// std::shared_ptr<RooSimultaneous> newPdf;
7972// if(auto s = _pdf->get<RooSimultaneous>(); s) {
7973// auto rangeName = GetRange();
7974// if (rangeName) {
7975// // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
7976// std::vector<TString> chanPatterns;
7977// TStringToken pattern(rangeName, ",");
7978// while (pattern.NextToken()) {
7979// chanPatterns.emplace_back(pattern);
7980// }
7981// auto& _cat = const_cast<RooAbsCategoryLValue&>(s->indexCat());
7982// newPdf = std::make_shared<RooSimultaneous>(TString::Format("%s_reduced",GetName()),"Reduced model",_cat);
7983// for(auto& c : _pdf->variations()) {
7984// TString cName(c->GetName());
7985// cName = cName(cName.Index('=')+1,cName.Length());
7986// _cat.setLabel(cName);
7987// bool matchAny=false;
7988// for(auto& p : chanPatterns) {
7989// if (cName.Contains(TRegexp(p,true))) { matchAny=true; break; }
7990// if (_cat.hasRange(p) && _cat.inRange(p)) { matchAny=true; break; }
7991// }
7992// if(matchAny) {
7993// newPdf->addPdf( *c->get<RooAbsPdf>(), cName );
7994// }
7995// }
7996// t = std::make_shared<RooDataTree>(newPdf->GetName(),"",*newPdf);
7997// RooArgSet s1(_pdf->obs().argList());
7998// RooArgSet s2(_pdf->globs().argList());s1.remove(s2);
7999// t->SetObservables(&s1,&s2);
8000// auto _data = t->generate(_fr,expected);
8001//
8002// xRooNode parent(_fr ? _fr->GetName() : "unknown",nullptr,xRooNode(t,*this));
8003// xRooNode out(_data.first->GetName(),/*acquire(_fr)*/ _data.first,parent);
8004// out.emplace_back(std::make_shared<xRooNode>(".globs",std::const_pointer_cast<RooArgSet>(_data.second),out));
8005// return out;
8006// }
8007// }
8008//
8009//
8010// std::string treeName = TString::Format("gen_%s",_pdf->GetName()).Data();
8011//
8012// auto _frt = getObject<TTree>(treeName); // get existing frt
8013//
8014//
8015// if (_frt) {
8016// t = std::make_shared<RooDataTree>(_frt.get());
8017// } else {
8018// t = std::make_shared<RooDataTree>(treeName.c_str(),"",*_pdf->get<RooAbsPdf>());
8019// RooArgSet s1(_pdf->obs().argList());
8020// RooArgSet s2(_pdf->globs().argList());s1.remove(s2);
8021// t->SetObservables(&s1,&s2);
8022// }
8023// auto _data = t->generate(_fr,expected);
8024// if (!_frt) {
8025// t =
8026// std::make_shared<RooDataTree>(std::dynamic_pointer_cast<TTree>(const_cast<xRooNode*>(this)->acquire(t->fTree)).get());
8027// }
8028// xRooNode parent(_fr ? _fr->GetName() : "unknown",nullptr,xRooNode(t,*this));
8029// xRooNode out(_data.first->GetName(),/*acquire(_fr)*/ _data.first,parent);
8030// out.emplace_back(std::make_shared<xRooNode>(".globs",std::const_pointer_cast<RooArgSet>(_data.second),out));
8031// return out;
8032// }
8033
8035public:
8037 double expectedEvents(const RooArgSet *nset) const override
8038 {
8039 return static_cast<RooAbsPdf *>(intpdf.absArg())->expectedEvents(nset);
8040 }
8041 ExtendMode extendMode() const override { return static_cast<RooAbsPdf *>(intpdf.absArg())->extendMode(); }
8042 TObject *clone(const char *newname) const override { return new xRooProjectedPdf(*this, newname); }
8043
8044protected:
8045 double evaluate() const override
8046 {
8047 int code;
8048 return getProjection(&intobs, _normSet, (_normRange.Length() > 0 ? _normRange.Data() : nullptr), code)->getVal();
8049 }
8050};
8051
8052double new_getPropagatedError(const RooAbsReal &f, const RooFitResult &fr, const RooArgSet &nset = {},
8053 RooArgList **pars = nullptr, bool asymHi = false, bool asymLo = false)
8054{
8055 // Calling getParameters() might be costly, but necessary to get the right
8056 // parameters in the RooAbsReal. The RooFitResult only stores snapshots.
8057
8058 // handle simple case that function is a RooRealVar
8059 if (auto rrv = dynamic_cast<const RooRealVar *>(&f); rrv) {
8060 if (auto frrrv = dynamic_cast<RooRealVar *>(fr.floatParsFinal().find(*rrv)); frrrv) {
8061 rrv = frrrv; // use value from fit result
8062 }
8063 if (asymHi) {
8064 return rrv->getErrorHi();
8065 } else if (asymLo) {
8066 return rrv->getErrorLo();
8067 } else {
8068 return rrv->getError();
8069 }
8070 }
8071
8072 RooArgList *_pars = (pars) ? *pars : nullptr;
8073
8074 if (!_pars) {
8075
8077 f.getParameters(&nset, allParamsInAbsReal);
8078
8079 _pars = new RooArgList;
8080 for (auto *rrvFitRes : static_range_cast<RooRealVar *>(fr.floatParsFinal())) {
8081
8082 auto rrvInAbsReal = static_cast<RooRealVar const *>(allParamsInAbsReal.find(*rrvFitRes));
8083
8084 // Strip out parameters with zero error
8085 if (rrvFitRes->getError() <= std::abs(rrvFitRes->getVal()) * std::numeric_limits<double>::epsilon())
8086 continue;
8087
8088 // Ignore parameters in the fit result that this RooAbsReal doesn't depend on
8089 if (!rrvInAbsReal)
8090 continue;
8091
8092 // Checking for float equality is a bad. We check if the values are
8093 // negligibly far away from each other, relative to the uncertainty.
8094 if (std::abs(rrvInAbsReal->getVal() - rrvFitRes->getVal()) > 0.01 * rrvFitRes->getError()) {
8095 std::stringstream errMsg;
8096 errMsg << "RooAbsReal::getPropagatedError(): the parameters of the RooAbsReal don't have"
8097 << " the same values as in the fit result! The logic of getPropagatedError is broken in this case."
8098 << " \n " << rrvInAbsReal->GetName() << " : " << rrvInAbsReal->getVal() << " vs "
8099 << rrvFitRes->getVal();
8100
8101 throw std::runtime_error(errMsg.str());
8102 }
8103
8104 _pars->add(*rrvInAbsReal);
8105 }
8106 }
8107
8108 // Make std::vector of variations
8109 TVectorD F(_pars->size());
8110
8111 // Create std::vector of plus,minus variations for each parameter
8112 TMatrixDSym V(_pars->size() == fr.floatParsFinal().size() ? fr.covarianceMatrix()
8113 : fr.reducedCovarianceMatrix(*_pars));
8114
8115 // TODO: if _pars includes pars not in fr, need to extend matrix with uncorrelated errors of those pars
8116
8117 double nomVal = f.getVal(nset);
8118
8119 for (std::size_t ivar = 0; ivar < _pars->size(); ivar++) {
8120
8121 auto &rrv = static_cast<RooRealVar &>((*_pars)[ivar]);
8122 auto *frrrv = static_cast<RooRealVar *>(fr.floatParsFinal().find(rrv));
8123
8124 double cenVal = rrv.getVal();
8125 double plusVar, minusVar, errVal;
8126
8127 if (asymHi || asymLo) {
8128 errVal = frrrv->getErrorHi();
8129 rrv.setVal(cenVal + errVal);
8130 plusVar = f.getVal(nset);
8131 errVal = frrrv->getErrorLo();
8132 rrv.setVal(cenVal + errVal);
8133 minusVar = f.getVal(nset);
8134 if (asymHi) {
8135 // pick the one that moved result 'up' most
8136 plusVar = std::max(plusVar, minusVar);
8137 minusVar = 2 * nomVal - plusVar; // symmetrizes
8138 } else {
8139 // pick the one that moved result 'down' most
8140 minusVar = std::min(plusVar, minusVar);
8141 plusVar = 2 * nomVal - minusVar; // symmetrizes
8142 }
8143 } else {
8144 errVal = sqrt(V(ivar, ivar));
8145 // Make Plus variation
8146 rrv.setVal(cenVal + errVal);
8147 plusVar = f.getVal(nset);
8148 // Make Minus variation
8149 rrv.setVal(cenVal - errVal);
8150 minusVar = f.getVal(nset);
8151 }
8152 F[ivar] = (plusVar - minusVar) * 0.5;
8153 rrv.setVal(cenVal);
8154 }
8155
8156 // Re-evaluate this RooAbsReal with the central parameters just to be
8157 // extra-safe that a call to `getPropagatedError()` doesn't change any state.
8158 // It should not be necessary because thanks to the dirty flag propagation
8159 // the RooAbsReal is re-evaluated anyway the next time getVal() is called.
8160 // Still there are imaginable corner cases where it would not be triggered,
8161 // for example if the user changes the RooFit operation more after the error
8162 // propagation.
8163 f.getVal(nset);
8164
8165 TMatrixDSym C(_pars->size());
8166 std::vector<double> errVec(_pars->size());
8167 for (std::size_t i = 0; i < _pars->size(); i++) {
8168 errVec[i] = std::sqrt(V(i, i));
8169 for (std::size_t j = i; j < _pars->size(); j++) {
8170 C(i, j) = V(i, j) / std::sqrt(V(i, i) * V(j, j));
8171 C(j, i) = C(i, j);
8172 }
8173 }
8174
8175 // Calculate error in linear approximation from variations and correlation coefficient
8176 double sum = F * (C * F);
8177
8178 if (!pars) {
8179 delete _pars;
8180 } else {
8181 *pars = _pars;
8182 }
8183
8184 return sqrt(sum);
8185}
8186
8187class PdfWrapper : public RooAbsPdf {
8188public:
8189 // need expPdf option while RooProjectedPdf doesn't support keeping things extended
8190 PdfWrapper(RooAbsReal &f, RooAbsReal *coef, bool expEvMode = false, RooAbsPdf *expPdf = nullptr)
8191 : RooAbsPdf(Form("exp_%s", f.GetName())),
8192 fFunc("func", "func", this, f),
8193 fCoef("coef", "coef", this),
8194 fExpPdf("expPdf", "expPdf", this)
8195 {
8196 // don't treat pdf as extended if it has a coefficient and is RooAddPdf: RooAddPdf doesn't extend them unless no
8197 // coefs for any (and all are extendable)
8198 if (coef) {
8199 fCoef.setArg(*coef);
8200 }
8201 if (expPdf && expPdf->canBeExtended() && !(coef && dynamic_cast<RooAddPdf *>(expPdf))) {
8202 fExpPdf.setArg(*expPdf);
8203 } else if (auto _p = dynamic_cast<RooAbsPdf *>(&f);
8204 _p && _p->canBeExtended() && !(coef && dynamic_cast<RooAddPdf *>(_p))) {
8205 fExpPdf.setArg(f); // using self for expectation
8206 }
8207 fExpectedEventsMode = expEvMode;
8208 }
8209 ~PdfWrapper() override{};
8210 PdfWrapper(const PdfWrapper &other, const char *name = nullptr)
8211 : RooAbsPdf(other, name),
8212 fFunc("func", this, other.fFunc),
8213 fCoef("coef", this, other.fCoef),
8214 fExpPdf("expPdf", this, other.fExpPdf),
8215 fExpectedEventsMode(other.fExpectedEventsMode)
8216 {
8217 }
8218 TObject *clone(const char *newname) const override { return new PdfWrapper(*this, newname); }
8219 bool isBinnedDistribution(const RooArgSet &obs) const override { return fFunc->isBinnedDistribution(obs); }
8220 std::list<double> *binBoundaries(RooAbsRealLValue &obs, double xlo, double xhi) const override
8221 {
8222 return fFunc->binBoundaries(obs, xlo, xhi);
8223 }
8224
8225 double evaluate() const override
8226 {
8227 return (fExpectedEventsMode ? 1. : fFunc) *
8228 ((fExpPdf.absArg()) ? static_cast<RooAbsPdf *>(fExpPdf.absArg())->expectedEvents(_normSet) : 1.) *
8229 (fCoef.absArg() ? fCoef : 1.);
8230 }
8231
8232 bool selfNormalized() const override
8233 {
8234 return true;
8235 } // so that doesn't try to do an integral because we are passing integration onto fFunc in evaluate
8236
8237 // faster than full evaluation because doesnt make the integral dependent on the full expression
8239 {
8240#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 28, 00)
8241 double oo = getPropagatedError(fr, nset_in); // method was improved in 6.28 so use this instead
8242 if (std::isnan(oo)) {
8243 // may be consequence of zero uncerts
8244 // Calling getParameters() might be costly, but necessary to get the right
8245 // parameters in the RooAbsReal. The RooFitResult only stores snapshots.
8247 getParameters(&nset_in, allParamsInAbsReal);
8248
8249 RooArgList paramList;
8251
8252 auto rrvInAbsReal = static_cast<RooRealVar const *>(allParamsInAbsReal.find(*rrvFitRes));
8253
8254 // If this RooAbsReal is a RooRealVar in the fit result, we don't need to
8255 // propagate anything and can just return the error in the fit result
8256 if (rrvFitRes->namePtr() == namePtr())
8257 return rrvFitRes->getError();
8258
8259 // Strip out parameters with zero error
8260 if (!rrvFitRes->hasError() ||
8261 rrvFitRes->getError() <= std::abs(rrvFitRes->getVal()) * std::numeric_limits<double>::epsilon())
8262 continue;
8263
8264 // Ignore parameters in the fit result that this RooAbsReal doesn't depend on
8265 if (!rrvInAbsReal)
8266 continue;
8267
8268 // Checking for float equality is a bad. We check if the values are
8269 // negligibly far away from each other, relative to the uncertainty.
8270 if (std::abs(rrvInAbsReal->getVal() - rrvFitRes->getVal()) > 0.01 * rrvFitRes->getError()) {
8271 std::stringstream errMsg;
8272 errMsg
8273 << "RooAbsReal::getPropagatedError(): the parameters of the RooAbsReal don't have"
8274 << " the same values as in the fit result! The logic of getPropagatedError is broken in this case.";
8275
8276 throw std::runtime_error(errMsg.str());
8277 }
8278
8279 paramList.add(*rrvInAbsReal);
8280 }
8281 if (paramList.empty())
8282 return 0.;
8283
8284 std::vector<double> plusVar;
8285 std::vector<double> minusVar;
8286 plusVar.reserve(paramList.size());
8287 minusVar.reserve(paramList.size());
8288
8289 // Create std::vector of plus,minus variations for each parameter
8290 TMatrixDSym V(paramList.size() == fr.floatParsFinal().size() ? fr.covarianceMatrix()
8291 : fr.reducedCovarianceMatrix(paramList));
8292
8293 for (std::size_t ivar = 0; ivar < paramList.size(); ivar++) {
8294
8295 auto &rrv = static_cast<RooRealVar &>(paramList[ivar]);
8296
8297 double cenVal = rrv.getVal();
8298 double errVal = sqrt(V(ivar, ivar));
8299
8300 // this next thing happens if the par has errors but the covariance matrix is empty
8301 // this only happens if the fit was dodgy, so perhaps best to not even try to recover from this
8302 // screwup ... hence I've commented out this fixup here and will let the errors be nan
8303 // if(errVal==0) {
8304 // Warning("getPropagatedError","Missing variance for %s",rrv.GetName());
8305 // errVal = rrv.getError();
8306 // V(ivar,ivar) = errVal*errVal;
8307 // }
8308
8309 // Make Plus variation
8310 rrv.setVal(cenVal + errVal);
8311 plusVar.push_back(getVal(nset_in));
8312
8313 // Make Minus variation
8314 rrv.setVal(cenVal - errVal);
8315 minusVar.push_back(getVal(nset_in));
8316#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
8317 // can try to recover nans ... this stopped being possible in 6.27 onwards because NaNPacker made private
8318 if (std::isnan(plusVar.back()) && RooNaNPacker::isNaNWithPayload(plusVar.back())) {
8319 plusVar.back() = -RooNaNPacker::unpackNaN(plusVar.back());
8320 }
8321 if (std::isnan(minusVar.back()) && RooNaNPacker::isNaNWithPayload(minusVar.back())) {
8322 minusVar.back() = -RooNaNPacker::unpackNaN(minusVar.back());
8323 }
8324#endif
8325 // std::cout << plusVar.back() << " and " << minusVar.back() << std::endl;
8326
8327 rrv.setVal(cenVal);
8328 }
8329
8330 // Re-evaluate this RooAbsReal with the central parameters just to be
8331 // extra-safe that a call to `getPropagatedError()` doesn't change any state.
8332 // It should not be necessary because thanks to the dirty flag propagation
8333 // the RooAbsReal is re-evaluated anyway the next time getVal() is called.
8334 // Still there are imaginable corner cases where it would not be triggered,
8335 // for example if the user changes the RooFit operation more after the error
8336 // propagation.
8337 getVal(nset_in);
8338
8339 TMatrixDSym C(paramList.size());
8340 std::vector<double> errVec(paramList.size());
8341 for (std::size_t i = 0; i < paramList.size(); i++) {
8342 errVec[i] = std::sqrt(V(i, i));
8343 for (std::size_t j = i; j < paramList.size(); j++) {
8344 C(i, j) = V(i, j) / std::sqrt(V(i, i) * V(j, j));
8345 C(j, i) = C(i, j);
8346 }
8347 }
8348
8349 // Make std::vector of variations
8350 TVectorD F(plusVar.size());
8351 for (unsigned int j = 0; j < plusVar.size(); j++) {
8352 F[j] = (plusVar[j] - minusVar[j]) / 2;
8353 }
8354
8355 // Calculate error in linear approximation from variations and correlation coefficient
8356 double sum = F * (C * F);
8357
8358 return sqrt(sum);
8359 }
8360 return oo;
8361#else
8362
8363 // Strip out parameters with zero error
8366 if (frv->getError() > 1e-20) {
8367 fpf_stripped.add(*frv);
8368 }
8369 }
8370
8371 // Clone self for internal use
8372 RooAbsReal *cloneFunc = const_cast<PdfWrapper *>(this); // (RooAbsReal *)fFunc.absArg()->cloneTree();
8373 // RooAbsPdf *clonePdf = dynamic_cast<RooAbsPdf *>(cloneFunc);
8374 RooArgSet *errorParams = cloneFunc->getObservables(fpf_stripped);
8375
8376 RooArgSet *nset =
8377 nset_in.size() == 0 ? cloneFunc->getParameters(*errorParams) : cloneFunc->getObservables(nset_in);
8378
8379 // Make list of parameter instances of cloneFunc in order of error matrix
8380 RooArgList paramList;
8381 const RooArgList &fpf = fpf_stripped;
8382 std::vector<int> fpf_idx;
8383 for (Int_t i = 0; i < fpf.size(); i++) {
8384 RooAbsArg *par = errorParams->find(fpf[i].GetName());
8385 if (par) {
8386 paramList.add(*par);
8387 fpf_idx.push_back(i);
8388 }
8389 }
8390
8391 std::vector<double> plusVar, minusVar;
8392
8393 // Create vector of plus,minus variations for each parameter
8394 TMatrixDSym V(paramList.size() == fr.floatParsFinal().size() ? fr.covarianceMatrix()
8395 : fr.reducedCovarianceMatrix(paramList));
8396
8397 for (Int_t ivar = 0; ivar < paramList.size(); ivar++) {
8398
8400
8401 double cenVal = rrv.getVal();
8402 double errVal = sqrt(V(ivar, ivar));
8403
8404 // Make Plus variation
8405 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal + errVal);
8406 // plusVar.push_back((fExpectedEventsMode ? 1. : cloneFunc->getVal(nset)) *
8407 // (clonePdf ? clonePdf->expectedEvents(nset) : 1.));
8408 plusVar.push_back(cloneFunc->getVal(nset));
8409
8410 // Make Minus variation
8411 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal - errVal);
8412 // minusVar.push_back((fExpectedEventsMode ? 1. : cloneFunc->getVal(nset)) *
8413 // (clonePdf ? clonePdf->expectedEvents(nset) : 1.));
8414 minusVar.push_back(cloneFunc->getVal(nset));
8415
8416 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal);
8417 }
8418
8419 getVal(nset); // reset state
8420
8421 TMatrixDSym C(paramList.size());
8422 std::vector<double> errVec(paramList.size());
8423 for (int i = 0; i < paramList.size(); i++) {
8424 errVec[i] = sqrt(V(i, i));
8425 for (int j = i; j < paramList.size(); j++) {
8426 C(i, j) = V(i, j) / sqrt(V(i, i) * V(j, j));
8427 C(j, i) = C(i, j);
8428 }
8429 }
8430
8431 // Make vector of variations
8432 TVectorD F(plusVar.size());
8433 for (unsigned int j = 0; j < plusVar.size(); j++) {
8434 F[j] = (plusVar[j] - minusVar[j]) / 2;
8435 }
8436
8437 // Calculate error in linear approximation from variations and correlation coefficient
8438 double sum = F * (C * F);
8439
8440 // delete cloneFunc;
8441 delete errorParams;
8442 delete nset;
8443
8444 return sqrt(sum);
8445#endif
8446 }
8447
8448private:
8452 bool fExpectedEventsMode = false;
8453};
8454
8455const xRooNode *runningNode = nullptr;
8457
8459{
8460 std::cout << "Got signal " << signum << std::endl;
8461 if (signum == SIGINT) {
8462 std::cout << "Keyboard interrupt while building histogram" << std::endl;
8463 // TODO: create a global mutex for this
8464 runningNode->fInterrupted = true;
8465 } else {
8467 }
8468}
8469
8471{
8472 auto _doSterilize = [](RooAbsArg *obj) {
8473 if (!obj)
8474 return;
8475 for (int i = 0; i < obj->numCaches(); i++) {
8476 if (auto cache = dynamic_cast<RooObjCacheManager *>(obj->getCache(i))) {
8477 cache->reset();
8478 }
8479 }
8480 if (RooAbsPdf *p = dynamic_cast<RooAbsPdf *>(obj); p) {
8481 p->setNormRange(p->normRange());
8482 }
8483#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
8484 if (RooAbsReal *p = dynamic_cast<RooAbsReal *>(obj); p) {
8485 // need to forget about any normSet that was passed to getVal(...)
8486 // doesn't seem necessary in 6.28
8487
8488 p->setProxyNormSet(nullptr);
8489 p->_lastNSet = nullptr;
8490 }
8491#endif
8492 obj->setValueDirty();
8493 };
8494 if (auto w = get<RooWorkspace>(); w) {
8495 // sterilizing all nodes
8496 for (auto &c : w->components()) {
8497 _doSterilize(c);
8498 }
8499 return;
8500 }
8501 // recursive through all clients and sterlize their normalization caches
8502 std::function<void(RooAbsArg *)> func;
8503 func = [&](RooAbsArg *a) {
8504 if (!a) {
8505 return;
8506 }
8507 _doSterilize(a); // sterilize first so that cache elements don't appear in the client list
8508 // safety net in case sterilizing one client deletes another one of our clients
8509 // monitor for change in clients list size
8510 // found this was only case in 6.26 (valgrind shows invalid read), in 6.28 these went away
8511 // might be in 6.28 the client list iterator became able to handle in-loop edits but didn't see
8512 // in test case that client count changed so just resterilizing if that's the case.
8513 size_t nClients;
8514 do {
8515 nClients = a->clients().size();
8516 for (auto obj : a->clients()) {
8517 func(dynamic_cast<RooAbsArg *>(obj));
8518 if (a->clients().size() != nClients) {
8519 break; // means sterilizing a client changed our clients, so don't trust the client iterator at this
8520 // point
8521 }
8522 }
8523 } while (a->clients().size() != nClients);
8524 };
8525 func(get<RooAbsArg>());
8526}
8527
8528// observables not in the axisVars are automatically projected over
8529xRooNode xRooNode::histo(const xRooNode &vars, const xRooNode &fr, bool content, bool errors, bool stack, bool errorsHi,
8530 bool errorsLo, int nErrorToys) const
8531{
8532
8533 if (!vars.fComp && strlen(vars.GetName())) {
8535 }
8536
8537 xRooNode out(TString::Format("%s.histo", GetName()), nullptr, *this);
8538
8539 RooAbsLValue *v = nullptr;
8540 if (vars.empty()) {
8541 // does an integral
8542 out.fComp = std::shared_ptr<TH1>(
8543 BuildHistogram(nullptr, !content, errors, -1, -1, fr, errorsHi, errorsLo, nErrorToys, nullptr, !stack, false));
8544 } else if (vars.size() == 1) {
8545 v = vars.at(0)->get<RooAbsLValue>();
8546 out.fComp = std::shared_ptr<TH1>(
8547 BuildHistogram(v, !content, errors, 1, 0, fr, errorsHi, errorsLo, nErrorToys, nullptr, !stack, true));
8548 } else {
8549 throw std::runtime_error("multi-dim histo not yet supported");
8550 }
8551
8552 return out;
8553}
8554
8556{
8557 return xRooNode(fComp, xRooNode(range.GetName(), nullptr, *this));
8558}
8559
8561 bool errorsHi, bool errorsLo, int nErrorToys, TH1 *templateHist, bool nostack,
8562 bool setInterp) const
8563{
8564 auto rar = get<RooAbsReal>();
8565 if (!rar) {
8566 if (get<TH1>()) {
8567 return dynamic_cast<TH1 *>(get()->Clone());
8568 }
8569 return nullptr;
8570 }
8571
8572 TObject *vv = rar;
8573 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
8574
8575 TH1 *h = nullptr;
8576 if (!v) {
8577 if (binStart != -1 || binEnd != -1) { // allow v to stay nullptr if doing integral (binStart=binEnd=-1)
8578 if (auto _ax = GetXaxis())
8579 v = dynamic_cast<RooAbsLValue *>(_ax->GetParent());
8580 } else {
8581 // don't need to integrate if doing a self-histogram
8582 v = dynamic_cast<RooRealVar *>(rar);
8583 }
8584 if (v) {
8585 vv = dynamic_cast<TObject *>(v);
8586 } else {
8587 // make a single-bin histogram of just this value
8588 h = new TH1D(rar->GetName(), rar->GetTitle(), 1, 0, 1);
8589 h->GetXaxis()->SetBinLabel(1, rar->GetName());
8590 h->GetXaxis()->SetTimeFormat(rar->GetName());
8591 }
8592 }
8593
8594 auto x = dynamic_cast<RooRealVar *>(v);
8595 bool setTitle = false;
8596 if (templateHist) {
8597 // using template hist for the binning
8598 h = static_cast<TH1 *>(templateHist->Clone(rar->GetName()));
8599 if (h->GetListOfFunctions())
8601 h->SetDirectory(0);
8602 h->SetTitle(rar->GetTitle());
8603 h->Reset();
8604 } else if (x) {
8605 if (x == rar) {
8606 // self histogram ...
8607 h = new TH1D(rar->GetName(), rar->GetTitle(), 1, 0, 1);
8608 h->Sumw2();
8609 h->GetXaxis()->SetBinLabel(1, rar->GetName());
8610 h->SetBinContent(1, rar->getVal());
8611 if (x->getError()) {
8612 h->SetBinError(1, x->getError());
8613 h->SetFillStyle(3005);
8614 h->SetFillColor(h->GetLineColor());
8615 }
8616 h->SetMaximum(x->hasMax() ? x->getMax()
8617 : (h->GetBinContent(1) + std::max(std::abs(h->GetBinContent(1) * 0.1), 50.)));
8618 h->SetMinimum(x->hasMin() ? x->getMin()
8619 : (h->GetBinContent(1) - std::max(std::abs(h->GetBinContent(1) * 0.1), 50.)));
8620 h->GetXaxis()->SetName(dynamic_cast<TObject *>(v)->GetName());
8621 h->SetOption("e2");
8622 h->SetMarkerSize(0);
8623 h->SetMarkerStyle(0);
8624
8625 return h;
8626 }
8627 auto _ax = GetXaxis();
8628 TString binningName = (_ax && _ax->GetParent() == x) ? _ax->GetName() : rar->getStringAttribute("binning");
8629 if (binningName == "")
8630 binningName = rar->GetName();
8631 if (x->hasBinning(binningName)) {
8632 if (x->getBinning(binningName).isUniform()) {
8633 h = new TH1D(rar->GetName(), rar->GetTitle(), x->numBins(binningName) <= 0 ? 100 : x->numBins(binningName),
8634 x->getMin(binningName), x->getMax(binningName));
8635 } else {
8636 h = new TH1D(rar->GetName(), rar->GetTitle(), x->numBins(binningName), x->getBinning(binningName).array());
8637 }
8638 h->GetXaxis()->SetTitle(x->getBinning(binningName).GetTitle());
8639 setTitle = true;
8640 } else if (auto _boundaries =
8641 _or_func(/*rar->plotSamplingHint(*x,x->getMin(),x->getMax())*/ (std::list<double> *)(nullptr),
8642 rar->binBoundaries(*x, -std::numeric_limits<double>::infinity(),
8643 std::numeric_limits<double>::infinity()));
8644 _boundaries) {
8645 std::vector<double> _bins;
8646 for (auto &b : *_boundaries) {
8647 if (_bins.empty() || std::abs(_bins.back() - b) > 1e-5 * _bins.back())
8648 _bins.push_back(b);
8649 } // found sometimes get virtual duplicates in the binning
8650 h = new TH1D(rar->GetName(), rar->GetTitle(), _bins.size() - 1, &_bins[0]);
8651 delete _boundaries;
8652 } else if (!x->hasMax() || !x->hasMin()) {
8653 // use current value of x to estimate range with
8654 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(), x->getVal() * 0.2, x->getVal() * 5);
8655 } else {
8656 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(), x->getBinning().array());
8657 }
8658 h->Sumw2();
8659 } else if (!h) {
8660 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(rar->GetName()), 0, v->numBins(rar->GetName()));
8661 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(v)) {
8662 int i = 1;
8663 std::map<int, std::string> cats; // fill into a map to preserve index ordering
8664 for (auto &c : *cat) {
8665 cats[c.second] = c.first;
8666 }
8667 for (auto &[_, label] : cats) {
8668 h->GetXaxis()->SetBinLabel(i++, label.c_str());
8669 }
8670 }
8671 h->Sumw2();
8672 }
8673 if (auto o = dynamic_cast<TObject *>(v); o && !setTitle) {
8674 h->GetXaxis()->SetTitle(o->GetTitle());
8675 }
8676
8677 if (v) {
8678 if (h->GetXaxis()->IsAlphanumeric()) {
8679 // store the variable name in the TimeFormat property as well, b.c. alphanumeric requires axis name to be
8680 // "xaxis"
8681 h->GetXaxis()->SetTimeFormat(dynamic_cast<TObject *>(v)->GetName());
8682 } else {
8683 h->GetXaxis()->SetName(dynamic_cast<TObject *>(v)->GetName()); // WARNING: messes up display of bin labels
8684 }
8685 }
8686
8687 if (auto s = styles(nullptr, false); s) {
8688 auto _style = s.get<TStyle>();
8689 static_cast<TAttLine &>(*h) = *_style;
8690 static_cast<TAttFill &>(*h) = *_style;
8691 static_cast<TAttMarker &>(*h) = *_style;
8692 }
8693 if (strlen(h->GetXaxis()->GetTitle()) == 0)
8694 h->GetXaxis()->SetTitle(vv->GetTitle());
8695 auto p = dynamic_cast<RooAbsPdf *>(rar);
8696
8697 // possible speed improvement:
8698 // if(auto spdf = dynamic_cast<RooRealSumPdf*>(p); spdf && spdf->canBeExtended()) {
8699 // p = nullptr; // faster to evaluate sumpdf as a function not a pdf
8700 // }
8701
8702 if (empty && !errors) {
8703 return h;
8704 }
8705
8706 // if (!empty) {
8707
8708 auto _coefs = coefs();
8709
8710 RooFitResult *fr = nullptr;
8711 if (errors) {
8712 // must ensure the fit result we obtain includes pars from coefficients if present
8713 if (_fr.get<RooFitResult>()) {
8714 fr = static_cast<RooFitResult *>(_fr.get<RooFitResult>()->Clone());
8715 } else {
8716 auto frn =
8717 (!_coefs.get() ? *this : xRooNode(RooProduct("tmp", "tmp", RooArgList(*rar, *_coefs.get<RooAbsReal>()))))
8718 .fitResult();
8719 if (strlen(_fr.GetName()))
8720 frn = frn.reduced(_fr.GetName());
8721 fr = dynamic_cast<RooFitResult *>(frn->Clone());
8722 }
8723 if (!GETDMP(fr, _finalPars)) {
8725 }
8726
8727 /// Oct2022: No longer doing this because want to allow fitResult to be used to get partial error
8728 // // need to add any floating parameters not included somewhere already in the fit result ...
8729 // now done in the fitResult() method, where a fit result from the workspace can be automatically extended
8730
8731 TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr, _VM));
8732
8733 if (!prevCov || size_t(fr->covarianceMatrix().GetNcols()) < fr->floatParsFinal().size()) {
8734 TMatrixDSym cov(fr->floatParsFinal().size());
8735 if (prevCov) {
8736 for (int i = 0; i < prevCov->GetNcols(); i++) {
8737 for (int j = 0; j < prevCov->GetNrows(); j++) {
8738 cov(i, j) = (*prevCov)(i, j);
8739 }
8740 }
8741 }
8742 int i = 0;
8743 for (auto &p2 : fr->floatParsFinal()) {
8744 if (!prevCov || i >= prevCov->GetNcols()) {
8745 cov(i, i) = pow(dynamic_cast<RooRealVar *>(p2)->getError(), 2);
8746 }
8747 i++;
8748 }
8749 int covQualBackup = fr->covQual();
8752 }
8753
8754 if (v) {
8755 // need to remove v from result as we are plotting as function of v
8756 if (auto _p = fr->floatParsFinal().find(dynamic_cast<TObject *>(v)->GetName()); _p) {
8758 _pars.remove(*_p, true);
8760 int covQualBackup = fr->covQual();
8763 const_cast<RooArgList &>(fr->floatParsFinal())
8764 .remove(*_p, true); // NOTE: I think this might be a memory leak, should delete _p after removal
8765 }
8766 }
8767 // finally check at least one float has errors defined (might not be cause if in prefit state)
8768 bool hasErrors = false;
8769 for (auto pp : fr->floatParsFinal()) {
8770 if (dynamic_cast<RooRealVar *>(pp)->hasError()) {
8771 hasErrors = true;
8772 break;
8773 }
8774 }
8775 if (!hasErrors) {
8776 errors = false;
8777 delete fr;
8778 }
8779 }
8780
8782 if (v)
8783 normSet.add(*dynamic_cast<RooAbsArg *>(v));
8784
8785 if (binEnd == 0)
8786 binEnd = h->GetNbinsX();
8787
8788 bool needBinWidth = false;
8789 // may have MULTIPLE coefficients for the same pdf!
8790
8791 if (x && (p || _coefs.get() || rar->getAttribute("density"))) {
8792 // pdfs of samples embedded in a sumpdf (aka have a coef) will convert their density value to a content
8793 needBinWidth = true;
8794 }
8795
8796 if (auto spdf = dynamic_cast<RooRealSumPdf *>(p);
8797 spdf && spdf->canBeExtended() && !spdf->getFloor() && !_coefs.get()) {
8798 p = nullptr; // if pdf has no floor, will evaluate it as a function to allow it to be negative - evaluation should
8799 // also be faster (no integral)
8800 // exception is if RooRealSumPdf is embedded in a RooAddPdf (detected by presence of coefs) ... then it must be
8801 // evaluated as a pdf technically should check parent is a RooAddPdf, because if was inside a RooRealSumPdf then
8802 // would be evaluated as a function!
8803 }
8804
8805 // check if we need to do any projecting of other observables
8806 RooAbsReal *oldrar = nullptr;
8807 auto _obs = obs();
8808
8809 for (auto o : _obs) {
8810 if (auto rr = o->get<RooRealVar>(); rr && rr->hasRange("coordRange")) {
8811 rr->removeMin("coordRange");
8812 rr->removeMax("coordRange");
8813 rr->setStringAttribute("coordRange", nullptr); // removes the attribute
8814 }
8815 }
8816 // probably should also remove any range on the x-axis variable too, if there is one
8817 if (auto rr = dynamic_cast<RooRealVar *>(v); rr && rr->hasRange("coordRange")) {
8818 rr->removeMin("coordRange");
8819 rr->removeMax("coordRange");
8820 rr->setStringAttribute("coordRange", nullptr); // removes the attribute
8821 }
8822 coords(); // loads current coordinates and populates coordRange, if any
8823
8824 if (auto a = dynamic_cast<RooAbsArg *>(v))
8825 _obs.get<RooArgList>()->remove(*a);
8826 if (!_obs.get<RooArgList>()->empty()) {
8827 oldrar = rar;
8828 normSet.add(*_obs.get<RooArgList>());
8829 // check if any obs are restricted range
8830 bool hasRange = false;
8831 for (auto o : normSet) {
8832 if (auto rr = dynamic_cast<RooRealVar *>(o);
8833 rr && (rr->getStringAttribute("coordRange")) && strlen(rr->getStringAttribute("coordRange"))) {
8834 hasRange = true;
8835 break;
8836 }
8837 }
8838 if (p) {
8839 // need to handle special case of RooSimultaneous ... each pdf needs individually projecting over just its
8840 // dependent obs
8841 if (auto s = dynamic_cast<RooSimultaneous *>(p)) {
8842 auto newrar = new RooSimultaneous("projSim", "projSim", const_cast<RooAbsCategoryLValue &>(s->indexCat()));
8843 for (auto pdf : bins()) {
8844 // auto _pdf =
8845 // pdf->get<RooAbsPdf>()->createProjection(*pdf->get<RooAbsPdf>()->getObservables(*_obs.get<RooArgList>()));
8846 auto _pdf =
8847 new xRooProjectedPdf(TString::Format("%s_projection", pdf->GetName()), "", *pdf->get<RooAbsPdf>(),
8848 *pdf->get<RooAbsPdf>()->getObservables(*_obs.get<RooArgList>()));
8849 if (hasRange) {
8850 dynamic_cast<RooAbsPdf *>(_pdf)->setNormRange("coordRange");
8851 }
8852 newrar->addPdf(*_pdf, pdf->coords()[s->indexCat().GetName()]->get<RooCategory>()->getLabel());
8853 }
8854 rar = newrar;
8855 } else {
8856 rar = p->createProjection(
8857 *_obs.get<RooArgList>()); // TODO should use xRooProjectedPdf here too, because not fixed range and
8858 // extend behaviour of RooProjectedPdf in ROOT yet
8859 if (hasRange) {
8860 dynamic_cast<RooAbsPdf *>(rar)->setNormRange("coordRange");
8861 }
8862 }
8863 if (hasRange)
8864 p->setNormRange("coordRange"); // should get cleared when we sterilize
8865 } else {
8866 if (hasRange) {
8867 // commented out passing of normset so that getVal of non-pdf is always a 'raw' value (needed for raw eval
8868 // of RooRealSumPdf)
8869 rar = std::unique_ptr<RooAbsReal>{rar->createIntegral(
8870 *_obs.get<RooArgList>(),
8871 /*RooFit::NormSet(normSet),*/ RooFit::Range("coordRange"))}
8872 .release();
8873 } else {
8874 rar =
8875 std::unique_ptr<RooAbsReal>{rar->createIntegral(*_obs.get<RooArgList>() /*, RooFit::NormSet(normSet)*/)}
8876 .release();
8877 }
8878 }
8879 }
8880
8881 bool scaleExpected = (p && p->canBeExtended() && !_coefs.get());
8882 // Note about above: if pdf has coefficients then its embedded in a RooAddPdf that has coefs defined ...
8883 // in this case we should *not* scale by expected, since the coefs become the scaling instead
8884 // we should also not build a stack for this (may be a RooRealSumPdf inside a RooAddPdf, but the
8885 // samples of the RooRealSumPdf wont be correctly scaled to line up with overall RooRealSumPdf
8886 // which will be normalized to its coefficient
8887 if (!nostack && p && p->canBeExtended() && _coefs.get()) {
8888 nostack = true;
8889 // if wanted to still hve a stack, would need to scale the stack subcomponents by
8890 // coefs-value / p_integral(raw) ... since raw p-integral will be what stack integrates to
8891 }
8892
8893 std::unique_ptr<RooArgSet> snap(normSet.snapshot());
8895 std::vector<double> lapTimes;
8896 bool warned = false;
8897 if (binStart == -1 && binEnd == -1) {
8898 binEnd = 1;
8899 }
8900 auto cat = (!x) ? dynamic_cast<RooAbsCategoryLValue *>(v) : nullptr;
8901 RooArgList *errorPars = nullptr;
8902 std::unique_ptr<RooAbsCollection> errorParsSnap;
8903
8904 if (!v) {
8905 setInterp = false;
8906 }
8907
8908 if (setInterp) {
8909 RooAbsArg *vvv = dynamic_cast<RooAbsArg *>(v);
8910 // determining if histogram should have interpolation drawing options set on it
8911 // need to strip namespace to discount the "HistFactory" namespace classes from all being treated as binned
8912 TString clNameNoNamespace = rar->ClassName();
8914 setInterp = (clNameNoNamespace.Contains("Hist") || vvv->isCategory() || rar->isBinnedDistribution(*vvv) ||
8915 h->GetNbinsX() == 1 || rar->getAttribute("BinnedLikelihood") ||
8916 (dynamic_cast<RooAbsRealLValue *>(vvv) &&
8917 std::unique_ptr<std::list<double>>(rar->binBoundaries(*dynamic_cast<RooAbsRealLValue *>(vvv),
8918 -std::numeric_limits<double>::infinity(),
8919 std::numeric_limits<double>::infinity()))))
8920 ? false
8921 : true;
8922 if (auto d = dynamic_cast<RooHistFunc *>(rar); d && !d->isBinnedDistribution(*vvv) && h->GetNbinsX() != 1) {
8923 setInterp = true; // hist func is interpolated, so draw it as such
8924 }
8925 if (setInterp && !components().empty()) {
8926 // check if all components of dOpt are "Hist" type (CMS model support)
8927 // if so then don't interp;
8928 bool allHist = true;
8929 for (auto &s : components()) {
8930 TString _clName = s->get()->ClassName();
8931 _clName = _clName(_clName.Last(':') + 1, _clName.Length());
8932 if (!(s->get() && _clName.Contains("Hist"))) {
8933 allHist = false;
8934 break;
8935 }
8936 }
8937 if (allHist)
8938 setInterp = false;
8939 }
8940 if (setInterp) {
8941 h->SetOption("l"); // does linear interpolation between points
8942 }
8943 }
8944
8945 if (errors) {
8946 // may be computing potentially asymmetric errors
8947 // the main histogram will be the error band, and the nominal histogram will be added as a function
8948 // so that it is drawn over the top of the error band
8949 // note that this means GetBinContent on returned histogram will return midpoint of the up and down error
8950 auto l = static_cast<TH1 *>(h->Clone("nominal"));
8951 l->SetDirectory(0);
8952 l->SetFillStyle(0);
8953 h->GetListOfFunctions()->Add(l, (setInterp) ? "lsame" : "histsame");
8954 h->SetOption(setInterp ? "e3" : "e2"); // default draw option E2 or E3 so error band shown .. could have used
8955 // 'EX0' to draw "classic style"
8956 // could take this from the 'band' style object if we create one in future?
8957 h->SetMarkerSize(0);
8958 h->SetFillStyle(3005);
8959 h->SetFillColor(h->GetLineColor());
8960 }
8961
8962 if (nErrorToys > 0) {
8963 errors = false; // wont evaluate error on each toy, will estimate for std.dev or normiles of toys
8964 // need list of errorPars
8965 auto allPars =
8966 (!_coefs.get() ? *this : xRooNode(RooProduct("tmp", "tmp", RooArgList(*rar, *_coefs.get<RooAbsReal>()))))
8967 .pars();
8968 errorPars = new RooArgList; // will be in same order as appear in fr.
8969 for (auto a : fr->floatParsFinal()) {
8970 if (auto par = allPars.get<RooArgList>()->find(*a)) {
8971 errorPars->add(*par);
8972 }
8973 }
8974 errorParsSnap.reset(errorPars->snapshot());
8975 auto l = static_cast<TH1 *>(h->Clone("toys"));
8976 l->Reset(); // removes any functions
8977 l->SetDirectory(0);
8978 h->GetListOfFunctions()->Add(
8979 l, "histsame"); // ensures just this empty hist will be drawn, and not each individual toy
8980
8981 if (errorsLo || errorsHi)
8982 empty = false; // must not be empty b.c. calculation of error relies on knowing nominal (see after loop)
8983 }
8984
8985 for (int toy = 0; toy < (nErrorToys + 1); toy++) {
8986
8987 TH1 *main_h = h;
8988 if (toy > 0) {
8989 h = static_cast<TH1 *>(main_h->Clone(TString::Format("toy_%d", toy)));
8990 h->SetDirectory(0);
8991 h->Reset();
8992 static_cast<TH1 *>(main_h->GetListOfFunctions()->FindObject("toys"))->GetListOfFunctions()->Add(h);
8993 // randomize the parameter values according to the fr's covariance matrix
8994 errorPars->assignValueOnly(fr->randomizePars());
8995 // if any par has 0 error, randomizePars can end up assigning a nan, so replace
8996 // all zero errors with value
8997 for (auto pp : fr->floatParsFinal()) {
8998 auto _vv = dynamic_cast<RooRealVar *>(pp);
8999 if (!_vv)
9000 continue;
9001 if (_vv->getError() == 0)
9002 errorPars->setRealValue(pp->GetName(), _vv->getVal());
9003 }
9004 }
9005
9006 for (int i = std::max(1, binStart); i <= std::min(h->GetNbinsX(), binEnd); i++) {
9007 timeIt.Start(true);
9008 if (x) {
9009 x->setVal(h->GetBinCenter(i));
9010 } else if (cat) {
9011 cat->setLabel(h->GetXaxis()->GetBinLabel(i)); // because order might not match "binning" order
9012 } else if (v) {
9013 v->setBin(i - 1);
9014 }
9015 if (x && !x->inRange("coordRange"))
9016 continue;
9017
9018 double r = 0;
9019 if (!empty || toy > 0) {
9020 r = /*(p && p->selfNormalized())*/ rar->getVal(p ? &normSet : nullptr);
9021#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
9022 if (std::isnan(r) && RooNaNPacker::isNaNWithPayload(r)) {
9024 }
9025#endif
9026 if (r && _coefs.get()) {
9027 r *= _coefs.get<RooAbsReal>()->getVal(normSet);
9028 }
9029 if (needBinWidth) {
9030 r *= h->GetBinWidth(i);
9031 }
9032 if (scaleExpected) {
9033 // std::cout << r << " exp = " << p->expectedEvents(normSet) << " for normRange " << (p->normRange() ?
9034 // p->normRange() : "null") << std::endl; p->Print();rar->Print();
9035 r *= (p->expectedEvents(normSet));
9036 } // do in here in case dependency on var
9037 }
9038 h->SetBinContent(i, r);
9039
9040 if (errors) {
9041 static_cast<TH1 *>(h->FindObject("nominal"))->SetBinContent(i, r); // transfer nominal to nominal hist
9042 double res;
9043 bool doAsym = (errorsHi && errorsLo);
9044 if (doAsym) {
9045 errorsHi = false;
9046 }
9047 if (p) {
9048 // std::cout << "computing error of :" << h->GetBinCenter(i) << std::endl;
9049 // //fr->floatParsFinal().Print(); fr->covarianceMatrix().Print();
9050 // res = PdfWrapper((oldrar) ? *rar : *p, _coefs.get<RooAbsReal>(), !v, oldrar ? p : nullptr)
9051 // .getSimplePropagatedError(*fr, normSet);
9053 PdfWrapper((oldrar) ? *rar : *p, _coefs.get<RooAbsReal>(), !v, oldrar ? p : nullptr), *fr, normSet,
9055#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
9056 // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
9057 p->_normSet = nullptr;
9058#endif
9059 } else {
9060 // res = RooProduct("errorEval", "errorEval",
9061 // RooArgList(*rar, !_coefs.get() ? RooFit::RooConst(1) :
9062 // *_coefs.get<RooAbsReal>()))
9063 // .getPropagatedError(
9064 // *fr /*, normSet*/); // should be no need to pass a normSet to a non-pdf (but
9065 // not verified this)
9067 RooProduct("errorEval", "errorEval",
9068 RooArgList(*rar, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>())),
9069 *fr, {}, &errorPars, errorsHi,
9070 errorsLo); // should be no need to pass a normSet to a non-pdf (but not verified this)
9071 // especially important not to pass in the case we are evaluated RooRealSumPdf as a function! otherwise
9072 // error will be wrong
9073 }
9074 if (needBinWidth) {
9075 res *= h->GetBinWidth(i);
9076 }
9077 h->SetBinError(i, res);
9078 if (doAsym) {
9079 // compute Hi error
9080 errorsHi = true;
9081 errorsLo = false;
9082 if (p) {
9084 PdfWrapper((oldrar) ? *rar : *p, _coefs.get<RooAbsReal>(), !v, oldrar ? p : nullptr), *fr, normSet,
9086 } else {
9088 RooProduct("errorEval", "errorEval",
9089 RooArgList(*rar, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>())),
9090 *fr, {}, &errorPars, errorsHi, errorsLo);
9091 }
9092 if (needBinWidth) {
9093 res *= h->GetBinWidth(i);
9094 }
9095 errorsLo = true;
9096 // lowVal = content - error, highVal = content + res
9097 // => band/2 = (res+error)/2 and band-mid = (2*content+res-error)/2
9098 h->SetBinContent(i, h->GetBinContent(i) + (res - h->GetBinError(i)) * 0.5);
9099 h->SetBinError(i, (res + h->GetBinError(i)) * 0.5);
9100 }
9101 }
9102 timeIt.Stop();
9103 lapTimes.push_back(timeIt.RealTime());
9104 double time_estimate =
9105 (lapTimes.size() > 1)
9106 ? (h->GetNbinsX() * (std::accumulate(lapTimes.begin() + 1, lapTimes.end(), 0.) / (lapTimes.size() - 1)))
9107 : 0.;
9108 if (!warned && (lapTimes.at(0) > 10 || (lapTimes.size() > 2 && time_estimate > 60.))) {
9109 TTimeStamp t2;
9110 t2.Add(time_estimate);
9111 Warning("BuildHistogram", "Building this histogram will take until %s", t2.AsString());
9112 if (errors) {
9113 // install interrupt handler
9114 runningNode = this;
9116 }
9117 warned = true;
9118 }
9119 if (fInterrupted) {
9120 if (errors) {
9121 Warning("BuildHistogram", "Skipping errors for remaining bins");
9122 errors = false;
9123 }
9124 fInterrupted = false;
9125 }
9126 }
9127 if (toy > 0) {
9128 h = main_h;
9129 }
9130 }
9131 if (gOldHandlerr) {
9133 gOldHandlerr = nullptr;
9134 }
9135 normSet = *snap;
9136
9137 if (errorPars) {
9138 if (errorParsSnap)
9140 delete errorPars;
9141 }
9142 if (nErrorToys) {
9143 // compute main histogram error bar from toys
9144 // if not doing asymmetric, then will display std.dev
9145 // otherwise will copy main to nominal and make main error bar s.t. it shows +/-1sigma vals
9146 if (errorsLo && errorsHi) {
9147 auto nomHist = static_cast<TH1 *>(h->FindObject("nominal"));
9148 nomHist->Add(h);
9149 }
9150 for (int i = 1; i <= h->GetNbinsX(); i++) {
9151 std::vector<double> vals;
9152 vals.reserve(nErrorToys);
9153 for (int j = 1; j < (nErrorToys + 1); j++) {
9154 vals.push_back(
9155 static_cast<TH1 *>(h->FindObject("toys")->FindObject(TString::Format("toy_%d", j)))->GetBinContent(i));
9156 }
9157 double upVal, downVal;
9158 if (errorsLo || errorsHi) {
9159 std::sort(vals.begin(), vals.end());
9160 upVal = vals.at(std::round(vals.size() * ROOT::Math::gaussian_cdf(1)));
9161 downVal = vals.at(std::round(vals.size() * ROOT::Math::gaussian_cdf(-1)));
9162 if (!errorsLo)
9163 downVal = 2. * h->GetBinContent(i) - upVal;
9164 if (!errorsHi)
9165 upVal = 2. * h->GetBinContent(i) - downVal;
9166 } else {
9167 double err = TMath::StdDev(vals.begin(), vals.end());
9168 upVal = h->GetBinContent(i) + err;
9169 downVal = h->GetBinContent(i) - err;
9170 }
9171 h->SetBinContent(i, (upVal + downVal) * 0.5);
9172 h->SetBinError(i, (upVal - downVal) * 0.5);
9173 }
9174 }
9175
9176 if (oldrar) {
9177 std::vector<RooAbsArg *> extra;
9178 if (auto s = dynamic_cast<RooSimultaneous *>(rar)) {
9179 // need to delete all the subpdfs we created too
9180 for (auto _pdf : s->servers()) {
9181 if (dynamic_cast<RooAbsPdf *>(_pdf))
9182 extra.push_back(_pdf);
9183 }
9184 }
9185 extra.push_back(rar);
9186 rar = oldrar;
9187 xRooNode(*rar).sterilize(); // need to clear the cache of the created integral - do this before deleting things!
9188 for (auto a : extra)
9189 delete a;
9190 } else {
9191 sterilize(); // needed to forget about the normSet that was passed to getVal()
9192 }
9193
9194 if (!p && !rar->getAttribute("density") && !needBinWidth) {
9195 h->GetYaxis()->SetTitle(rar->getStringAttribute("units"));
9196 } else if ((p && p->canBeExtended()) || (!p && needBinWidth)) {
9197 h->GetYaxis()->SetTitle("Events");
9198 } else {
9199 h->GetYaxis()->SetTitle("Probability Mass");
9200 }
9201 h->GetYaxis()->SetMaxDigits(3);
9202
9203 if (errors) {
9204 delete fr;
9205 }
9206
9207 // build a stack unless not requested
9208 if (!nostack) {
9209 // need to draw copy of hist so shown over the stack
9210 auto hCopy = static_cast<TH1 *>(h->Clone("copy"));
9211 hCopy->Reset();
9212 hCopy->Add(h); // use Reset and Add to clear the function list (dont clear directly as may double-delete if same
9213 // object added twice)
9214 hCopy->SetStats(false);
9215 h->GetListOfFunctions()->Add(hCopy, TString(h->GetOption()) + "same");
9216 h->GetListOfFunctions()->Add(hCopy, "axissame"); // prevents stack covering axis
9217 TString dOpt = (setInterp) ? "LF2" : ""; // should become lf2 if interpolation of histogram is appropriate
9218
9219 const xRooNode *rarNode = this;
9220 RooAbsReal *sf = nullptr;
9221 if (get()->InheritsFrom("RooExtendPdf")) {
9222 const_cast<xRooNode *>(this)->browse();
9223 rarNode = find(".pdf").get();
9224 // rar = rarNode->get<RooAbsReal>();
9225 sf = find(".n")->get<RooAbsReal>();
9226 }
9227
9228 THStack *stack = new THStack("stack", TString::Format("%s;%s", rar->GetTitle(), h->GetXaxis()->GetTitle()));
9229 int count = 0;
9230 std::map<std::string, int> colorByTitle; // TODO: should fill from any existing legend
9231 std::set<std::string> allTitles;
9232 bool titleMatchName = true;
9233 std::map<std::string, TH1 *> histGroups;
9234 std::vector<TH1 *> hhs;
9235 std::set<TH1 *> histsWithBadTitles; // these histograms will have their titles autoFormatted
9236
9237 // support for CMS model case where has single component containing many coeffs
9238 // will build stack by setting each coeff equal to 0 in turn, rebuilding the histogram
9239 // the difference from the "full" histogram will be the component
9241 if (!rarNode->components().empty()) {
9242 auto comps = rarNode->components()[0];
9243 for (auto &c : *comps) {
9244 if (c->fFolder == "!.coeffs")
9245 cms_coefs.add(*c->get<RooAbsArg>());
9246 }
9247 }
9248 if (!cms_coefs.empty()) {
9249 RooRealVar zero("zero", "", 0);
9250 std::shared_ptr<TH1> prevHist(static_cast<TH1 *>(h->Clone()));
9251 prevHist->Reset();
9252 prevHist->Add(h);
9253 for (auto c : cms_coefs) {
9254 // seems I have to remake the function each time, as haven't figured out what cache needs clearing?
9255 std::unique_ptr<RooAbsReal> f(
9256 dynamic_cast<RooAbsReal *>(rarNode->components()[0]->get()->Clone("tmpCopy")));
9257 zero.setAttribute(Form("ORIGNAME:%s", c->GetName())); // used in redirectServers to say what this replaces
9258 f->redirectServers(RooArgSet(zero), false, true); // each time will replace one additional coef
9259 // zero.setAttribute(Form("ORIGNAME:%s",c->GetName()),false); (commented out so that on next iteration
9260 // will still replace all prev)
9261 auto hh = xRooNode(*f, *this).BuildHistogram(v);
9262 hh->SetName(c->GetName());
9263 if (sf)
9264 hh->Scale(sf->getVal());
9265 if (strlen(hh->GetTitle()) == 0) {
9266 hh->SetTitle(c->GetName()); // ensure all hists has titles
9267 histsWithBadTitles.insert(hh);
9268 } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
9269 histsWithBadTitles.insert(hh);
9270 }
9271 titleMatchName &= (TString(c->GetName()) == hh->GetTitle() ||
9272 TString(hh->GetTitle()).BeginsWith(TString(c->GetName()) + "_"));
9273 std::shared_ptr<TH1> nextHist(static_cast<TH1 *>(hh->Clone()));
9274 hh->Add(prevHist.get(), -1.);
9275 hh->Scale(-1.);
9276 hhs.push_back(hh);
9278 }
9279 } else if (get<RooSimultaneous>()) {
9280 // need to create a histogram for each sample across all the channels - will rely on functionality below to
9281 // merge them based on titles
9282
9283 for (auto &chan : bins()) {
9284 TString chanName(chan->GetName());
9285 chanName = chanName(chanName.Index("=") + 1, chanName.Length());
9286 auto samps = chan->mainChild();
9287 if (!samps)
9288 samps = *chan;
9289 for (auto &samp : samps.components()) {
9290 auto hh = static_cast<TH1 *>(h->Clone(samp->GetName()));
9291 hh->Reset();
9292 hh->SetTitle(samp->GetTitle());
9293 if (strlen(hh->GetTitle()) == 0) {
9294 hh->SetTitle(samp->GetName());
9295 histsWithBadTitles.insert(hh);
9296 } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
9297 histsWithBadTitles.insert(hh);
9298 }
9299 hh->SetTitle(TString(hh->GetTitle())
9300 .ReplaceAll(TString(chan->get()->GetName()) + "_",
9301 "")); // remove occurance of channelname_ in title (usually prefix)
9302 titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
9303 TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
9304 hh->SetBinContent(hh->GetXaxis()->FindFixBin(chanName), samp->GetContent());
9305 hhs.push_back(hh);
9306 }
9307 }
9308 } else {
9309 for (auto &samp : rarNode->components()) {
9310 auto hh = samp->BuildHistogram(
9311 v, empty, false /* no errors for stack*/, binStart, binEnd, _fr, false, false, 0, h, true,
9312 setInterp); // passing h to ensure binning is the same for all subcomponent hists
9313 hh->SetName(samp->GetName());
9314 if (sf)
9315 hh->Scale(sf->getVal());
9316 hhs.push_back(hh);
9317 if (strlen(hh->GetTitle()) == 0) {
9318 hh->SetTitle(samp->GetName()); // ensure all hists has titles
9319 histsWithBadTitles.insert(hh);
9320 } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
9321 histsWithBadTitles.insert(hh);
9322 }
9323 titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
9324 TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
9325 }
9326 }
9327
9328 if (!hhs.empty()) {
9329 for (auto &hh : hhs) {
9330 allTitles.insert(hh->GetTitle());
9331 }
9332
9333 // get common prefix to strip off only if all titles match names and
9334 // any title is longer than 10 chars
9335 size_t e = std::min(allTitles.begin()->size(), allTitles.rbegin()->size());
9336 size_t ii = 0;
9337 bool goodPrefix = false;
9338 std::string commonSuffix;
9339 if (titleMatchName && hhs.size() > 1) {
9340 while (ii < e - 1 && allTitles.begin()->at(ii) == allTitles.rbegin()->at(ii)) {
9341 ii++;
9342 if (allTitles.begin()->at(ii) == '_' || allTitles.begin()->at(ii) == ' ')
9343 goodPrefix = true;
9344 }
9345
9346 // find common suffix if there is one .. must start with a "_"
9347 bool stop = false;
9348 while (!stop && commonSuffix.size() < size_t(e - 1)) {
9349 commonSuffix = allTitles.begin()->substr(allTitles.begin()->length() - commonSuffix.length() - 1);
9350 for (auto &tt : allTitles) {
9351 if (!TString(tt).EndsWith(commonSuffix.c_str())) {
9352 commonSuffix = commonSuffix.substr(1);
9353 stop = true;
9354 break;
9355 }
9356 }
9357 }
9358 if (commonSuffix.find('_') == std::string::npos) {
9359 commonSuffix = "";
9360 } else {
9361 commonSuffix = commonSuffix.substr(commonSuffix.find('_'));
9362 }
9363 }
9364 if (!goodPrefix)
9365 ii = 0;
9366
9367 // also find how many characters are needed to distinguish all entries (that dont have the same name)
9368 // then carry on up to first space or underscore
9369 size_t jj = 0;
9370 std::map<std::string, std::string> reducedTitles;
9371 while (reducedTitles.size() != allTitles.size()) {
9372 jj++;
9373 std::map<std::string, int> titlesMap;
9374 for (auto &s : allTitles) {
9375 if (reducedTitles.count(s))
9376 continue;
9377 titlesMap[s.substr(0, jj)]++;
9378 }
9379 for (auto &s : allTitles) {
9380 if (titlesMap[s.substr(0, jj)] == 1 && (jj >= s.length() || s.at(jj) == ' ' || s.at(jj) == '_')) {
9381 reducedTitles[s] = s.substr(0, jj);
9382 }
9383 }
9384 }
9385
9386 // strip common prefix and suffix before adding
9387 for (auto ritr = hhs.rbegin(); ritr != hhs.rend(); ++ritr) { // go in reverse order
9388 if (!histsWithBadTitles.count((*ritr))) {
9389 continue;
9390 }
9391 auto _title = (hhs.size() > 5) ? reducedTitles[(*ritr)->GetTitle()] : (*ritr)->GetTitle();
9392 _title = _title.substr(ii < _title.size() ? ii : 0);
9393 if (!commonSuffix.empty() && TString(_title).EndsWith(commonSuffix.c_str()))
9394 _title = _title.substr(0, _title.length() - commonSuffix.length());
9395 (*ritr)->SetTitle(_title.c_str());
9396 }
9397 }
9398
9399 for (auto &hh : hhs) {
9400 // automatically group hists that all have the same title
9401 if (histGroups.find(hh->GetTitle()) == histGroups.end()) {
9402 histGroups[hh->GetTitle()] = hh;
9403 } else {
9404 // add it into this group
9405 histGroups[hh->GetTitle()]->Add(hh);
9406 delete hh;
9407 hh = nullptr;
9408 continue;
9409 }
9410 auto hhMin = (hh->GetMinimum() == 0) ? hh->GetMinimum(1e-9) : hh->GetMinimum();
9411 if (!stack->GetHists() && h->GetMinimum() > hhMin) {
9412 auto newMin = hhMin - (h->GetMaximum() - hhMin) * gStyle->GetHistTopMargin();
9413 if (hhMin >= 0 && newMin < 0)
9414 newMin = hhMin * 0.99;
9415 // adjustYRange(newMin, h->GetMaximum());
9416 }
9417
9418 /*if(stack->GetHists() && stack->GetHists()->GetEntries()>0) {
9419 // to remove rounding effects on bin boundaries, see if binnings compatible
9420 auto _h1 = dynamic_cast<TH1*>(stack->GetHists()->At(0));
9421 if(_h1->GetNbinsX()==hh->GetNbinsX()) TODO ... finish dealing with silly rounding effects
9422 }*/
9423 TString thisOpt = TString(hh->GetOption()) == "l" ? "LF2" : ""; // need LF2 to get smooth line with fill
9424 // uncomment next line to blend continuous with discrete components .. get some unpleasant "poke through"
9425 // effects though
9426 // if(auto s = samp->get<RooAbsReal>(); s) thisOpt = s->isBinnedDistribution(*dynamic_cast<RooAbsArg*>(v)) ?
9427 // "" : "LF2";
9428 stack->Add(hh, thisOpt);
9429 }
9430 // stack->SetBit(kCanDelete); // should delete its sub histograms
9431 h->GetListOfFunctions()->AddFirst(stack, "noclear same");
9432 // stack->Draw("noclear same");
9433 // h->Draw(
9434 // dOpt + sOpt +
9435 // "same"); // overlay again .. if stack would cover original hist (negative components) we still see
9436 // integral
9437 // h->Draw("axissame"); // redraws axis
9438
9439 TList *ll = stack->GetHists();
9440 if (ll && ll->GetEntries()) {
9441
9442 // finally, ensure all hists are styled
9443 for (auto ho : *ll) {
9444 TH1 *hh = dynamic_cast<TH1 *>(ho);
9445 if (!hh)
9446 continue;
9447 bool createdStyle = (xRooNode(*hh, *this).styles(nullptr, false).get<TStyle>() == nullptr);
9448
9449 if (createdStyle) {
9450 // give hist a color, that isn't the same as any other hists color
9451 hh->SetFillStyle(1001); // solid fill style
9452 bool used = false;
9453 do {
9454 hh->SetFillColor(gEnv->GetValue("XRooFit.MinFillColor", kP10Blue /* was previously 2*/) + (count++));
9455 if (!gROOT->GetColor(hh->GetFillColor())) {
9456 // color doesn't exist, default it to transparent?
9457 hh->SetFillColor(0);
9458 }
9459 // check not already used this color
9460 used = false;
9461 for (auto ho2 : *ll) {
9462 TH1 *hh2 = dynamic_cast<TH1 *>(ho2);
9463 if (!hh2)
9464 continue;
9465 auto _styleNode = xRooNode(*hh2, *this).styles(hh2, false);
9466 auto _style = _styleNode.get<TStyle>();
9467 if (hh != hh2 && _style && _style->GetFillColor() == hh->GetFillColor()) {
9468 used = true;
9469 break;
9470 }
9471 }
9472 } while (used);
9473 }
9474
9475 auto _styleNode = xRooNode(*hh, *this).styles(hh);
9476 if (auto _style = _styleNode.get<TStyle>()) {
9477 *dynamic_cast<TAttLine *>(hh) = *_style;
9478 *dynamic_cast<TAttFill *>(hh) = *_style;
9479 *dynamic_cast<TAttMarker *>(hh) = *_style;
9480 }
9481 // for stacks, fill color of white should be color 10 unless fill style is 0
9482 if (hh->GetFillColor() == kWhite && hh->GetFillStyle() != 0) {
9483 // kWhite means 'transparent' in ROOT ... should really use a FillStyle of 0 for that
9484 // so assume user wanted actual white, which is color 10
9485 hh->SetFillColor(10);
9486 }
9487 }
9488 }
9489 }
9490
9491 return h;
9492}
9493
9494double xRooNode::GetBinData(int bin, const xRooNode &data)
9495{
9496 if (data.get<RooAbsData>()) {
9497 // attach as a child before calling datasets(), so that is included in the list
9498 push_back(std::make_shared<xRooNode>(data));
9499 }
9500 auto node = datasets().find(data.GetName());
9501 if (data.get<RooAbsData>()) {
9502 // remove the child we attached
9503 resize(size() - 1);
9504 }
9505 if (!node)
9506 return std::numeric_limits<double>::quiet_NaN();
9507 return node->GetBinContent(bin);
9508}
9509
9510std::vector<double> xRooNode::GetBinContents(int binStart, int binEnd) const
9511{
9512 if (fBinNumber != -1) {
9513 if (binStart != binEnd || !fParent) {
9514 throw std::runtime_error(TString::Format("%s is a bin - only has one value", GetName()));
9515 }
9516 return fParent->GetBinContents(fBinNumber, fBinNumber);
9517 }
9518 std::vector<double> out;
9519 if (get<RooAbsData>()) {
9520 auto g = BuildGraph(
9521 nullptr,
9522 (binStart != -1 ||
9523 binEnd != -1) /*include points for zeros unless we are asking for a single point with start=end=-1*/);
9524 if (!g) {
9525 return out;
9526 }
9527 if (binStart == binEnd && binStart == -1) {
9528 // integral over all bins if getting bin content -1
9529 double integral(0);
9530 for (int i = 0; i < g->GetN(); i++)
9531 integral += g->GetPointY(i);
9532 out.push_back(integral);
9533 delete g;
9534 return out;
9535 }
9536 for (int i = binStart - 1; i < g->GetN() && (binEnd == 0 || i < binEnd); i++) {
9537 out.push_back(g->GetPointY(i));
9538 }
9539 delete g;
9540 return out;
9541 }
9542
9543 bool doIntegral = false;
9544 if (binStart == binEnd && binStart == -1) {
9545 binStart = -1;
9546 binEnd = -1;
9547 doIntegral = true;
9548 } // return integral if request bin -1
9549 auto h = BuildHistogram(nullptr, false, false, binStart, binEnd);
9550 if (!h) {
9551 throw std::runtime_error(TString::Format("%s has no content", GetName()));
9552 }
9553 if (binEnd == 0) {
9554 binEnd = h->GetNbinsX();
9555 }
9556 if (doIntegral) {
9557 double tot = 0;
9558 for (int i = 1; i <= h->GetNbinsX(); i++) {
9559 tot += h->GetBinContent(i);
9560 }
9561 out.push_back(tot);
9562 } else {
9563 for (int i = binStart; i <= binEnd; i++) {
9564 out.push_back(h->GetBinContent(i));
9565 }
9566 }
9567 delete h;
9568 return out;
9569}
9570
9572{
9573 if (auto a = get<RooAbsArg>(); a) {
9574 // go through servers looking for 'main' thing
9575 for (auto &l : a->servers()) {
9576 if (l->getAttribute("MAIN_MEASUREMENT") || l->InheritsFrom("RooRealSumPdf") || l->InheritsFrom("RooAddPdf")) {
9577 return xRooNode(*l, *this);
9578 }
9579 }
9580 // the main child of a RooProduct is one that has the same name (/alias) as the product (except if is a bin
9581 // factor)
9582 if (a->IsA() == RooProduct::Class() && fBinNumber == -1) {
9583 for (auto &l : factors()) {
9584 if (strcmp(l->GetName(), GetName()) == 0) {
9585 return *l;
9586 }
9587 }
9588 }
9589 }
9590 return xRooNode();
9591}
9592
9594{
9595 if (auto o = get(); o) {
9596 o->Inspect();
9597 } else {
9599 }
9600}
9601
9602bool TopRightPlaceBox(TPad *p, TObject *o, double w, double h, double &xl, double &yb)
9603{
9604#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
9605 // reinitialize collide grid because the filling depends on fUxmin and fUxmax (and ymin ymax too)
9606 // and these aren't filled on the first time we do the placement (they init to 0 and 1), but will be filled
9607 // subsequently
9608 for (int i = 0; i < p->fCGnx; i++) {
9609 for (int j = 0; j < p->fCGny; j++) {
9610 p->fCollideGrid[i + j * p->fCGnx] = true;
9611 }
9612 }
9613 p->FillCollideGrid(o);
9614 Int_t iw = (int)(p->fCGnx * w);
9615 Int_t ih = (int)(p->fCGny * h);
9616
9617 Int_t nxmax = p->fCGnx - iw - 1 - p->fCGnx * p->GetRightMargin();
9618 Int_t nymax = p->fCGny - ih - 1 - p->fCGny * p->GetTopMargin();
9619
9620 for (Int_t j = nymax; j >= 0; j--) {
9621 for (Int_t i = nxmax; i >= 0; i--) {
9622 if (p->Collide(i, j, iw, ih)) {
9623 continue;
9624 } else {
9625 xl = (double)(i) / (double)(p->fCGnx);
9626 yb = (double)(j) / (double)(p->fCGny);
9627 return true;
9628 }
9629 }
9630 }
9631 return false;
9632#else
9633 return p->PlaceBox(o, w, h, xl, yb, "trw");
9634#endif
9635}
9636
9637TPaveText *getPave(const char *name = "labels", bool create = true, bool doPaint = false)
9638{
9639 if (auto p = dynamic_cast<TPaveText *>(gPad->GetPrimitive(name)); p) {
9640 if (doPaint)
9641 gPad->PaintModified(); //-- slows down x11 so trying to avoid
9642 return p;
9643 }
9644 if (!create) {
9645 return nullptr;
9646 }
9647 auto l = new TPaveText(gPad->GetLeftMargin() + 0.02, 1. - gPad->GetTopMargin() - 0.08, 0.6,
9648 1. - gPad->GetTopMargin() - 0.08);
9649 l->SetBorderSize(0);
9650 if (l->GetTextSize() == 0)
9651 l->SetTextSize(gStyle->GetTitleYSize());
9652
9654 // l->SetMargin(0);
9655 l->SetFillStyle(0);
9656 l->SetName(name);
9657 l->Draw();
9658 l->ConvertNDCtoPad();
9659 return l;
9660}
9661
9662TLegend *getLegend(bool create = true, bool doPaint = false)
9663{
9664 if (auto p = dynamic_cast<TLegend *>(gPad->GetPrimitive("legend")); p) {
9665 double x;
9666 double y;
9667 double w = p->GetX2NDC() - p->GetX1NDC();
9668 double h = p->GetY2NDC() - p->GetY1NDC();
9669 if (doPaint)
9670 gPad->PaintModified(); //-- slows down x11 so trying to avoid
9671 if (TopRightPlaceBox(dynamic_cast<TPad *>(gPad), p, w, h, x, y)) {
9672 // squash inside the frame ..
9673 // std::cout << gPad->GetName() << ":" << x << " , " << y << " , " << w << " , " << h << std::endl;
9674 x = std::max(x, (gPad->GetLeftMargin() + 0.02));
9675 y = std::max(y, (gPad->GetBottomMargin() + 0.02));
9676 x = std::min(x, (1. - gPad->GetRightMargin() - 0.02) - w);
9677 y = std::min(y, (1. - gPad->GetTopMargin() - 0.02) - h);
9678 h = std::min(h, (1. - gPad->GetTopMargin() - 0.02) - y);
9679 w = std::min(w, (1. - gPad->GetRightMargin() - 0.02) - x);
9680 // std::cout << gPad->GetName() << ":" << x << " , " << y << " , " << h << " , " << w << std::endl;
9681 p->SetX1NDC(x);
9682 p->SetY1NDC(y);
9683 p->SetX2NDC(x + w);
9684 p->SetY2NDC(y + h);
9685 gPad->Modified();
9686 }
9687 return p;
9688 }
9689 // look for a parent pad called 'legend' and create it there if existing
9690 auto p = gPad;
9691 while ((p != p->GetMother()) && (p = p->GetMother())) {
9692 if (auto q = dynamic_cast<TVirtualPad *>(p->GetPrimitive("legend")); q) {
9693 q->Modified();
9694 p = q;
9695 break;
9696 }
9697 }
9698 auto tmpPad = gPad;
9699 TLegend *l = nullptr;
9700 if (p && strcmp(p->GetName(), "legend") == 0) {
9701 if (l = dynamic_cast<TLegend *>(p->GetPrimitive("legend")); l || !create)
9702 return l;
9703 p->cd();
9704 l = new TLegend(gPad->GetLeftMargin(), 1. - gPad->GetTopMargin(), 1. - gPad->GetRightMargin(),
9705 gPad->GetBottomMargin());
9706 l->SetBorderSize(1); // ensure has a border
9707 } else {
9708 if (!create)
9709 return nullptr;
9710 l = new TLegend(0.6, 1. - gPad->GetTopMargin() - 0.08, 0.75, 1. - gPad->GetTopMargin() - 0.08);
9711 l->SetBorderSize(0);
9712 // legend text will be required to match y-axis
9713 if (l->GetTextSize() == 0) {
9714 l->SetTextSize(gStyle->GetTitleYSize());
9715 l->SetTextFont(gStyle->GetTitleFont("Y"));
9716 }
9717 }
9719 // l->SetMargin(0);
9720 l->SetFillStyle(0);
9721 l->SetName("legend");
9722 l->Draw();
9723 l->ConvertNDCtoPad();
9724 tmpPad->cd();
9725 return l;
9726}
9727
9728std::string formatLegendString(const std::string &s)
9729{
9730 auto i = s.find("\n");
9731 if (i == std::string::npos) {
9732 return s;
9733 }
9734 return std::string("#splitline{") + s.substr(0, i) + "}{" + formatLegendString(s.substr(i + 1)) + "}";
9735}
9736
9737void addLegendEntry(TObject *o, const char *title, const char *opt)
9738{
9739 auto l = getLegend();
9740 if (!l)
9741 return;
9742 // check for entry already existing with same title
9743 for (auto a : *l->GetListOfPrimitives()) {
9744 if (formatLegendString(title) == dynamic_cast<TLegendEntry *>(a)->GetLabel())
9745 return;
9746 }
9747 if (l->GetListOfPrimitives()->GetEntries() > 20)
9748 return; // todo: create an 'other' entry?
9749
9750 auto e = l->AddEntry(o, formatLegendString(title).c_str(), opt);
9751 // move to top of the legend (we add things in at the top)
9752 l->GetListOfPrimitives()->RemoveLast();
9753 l->GetListOfPrimitives()->AddFirst(e);
9754 if (auto nObj = l->GetListOfPrimitives()->GetEntries(); nObj > 0) {
9755 // each entry takes up 0.05 ... maximum of N*(N+4) (where N is # cols) before next column
9756 int nn = l->GetNColumns();
9757 nn *= (nn + 4);
9758 if (nObj > 1 && (nObj % nn) == 1) {
9759 l->SetNColumns(l->GetNColumns() + 1);
9760 if (l->GetBorderSize() == 0) {
9761 l->SetX1NDC(l->GetX2NDC() - 0.15 * l->GetNColumns());
9762 }
9763 }
9764 if (l->GetBorderSize() == 0) {
9765 l->SetY1NDC(l->GetY2NDC() - 0.05 * gPad->GetHNDC() * std::ceil((double(nObj) / l->GetNColumns())));
9766 }
9767 }
9768
9769 getLegend(); // to mark modified
9770}
9771
9772// this exists to avoid calling update excessively because it slows down x11 ... but still
9773// need to call update twice if have a legend drawn in order to relocate it.
9775public:
9776 PadRefresher(TVirtualPad *p) : fPad(p) { nExisting++; }
9778 {
9779 if (fPad) {
9780 getLegend(false, true);
9781 fPad->GetCanvas()->Paint();
9782 fPad->GetCanvas()->Update();
9783#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 30, 00)
9784 fPad->GetCanvas()->ResetUpdated(); // stops previous canvas being replaced in a jupyter notebook
9785#endif
9786 fPad->cd();
9787 }
9788 nExisting--;
9789 }
9790 TVirtualPad *fPad = nullptr;
9791 static int nExisting;
9792};
9793
9795
9797{
9798 // in order to catch exceptions to prevent crash of GUI, do this:
9799 if (gROOT->FromPopUp()) {
9800 gROOT->SetFromPopUp(false);
9801 try {
9802 Draw(opt);
9803 } catch (const std::exception &e) {
9804 new TGMsgBox(
9805 gClient->GetRoot(),
9806 (gROOT->GetListOfBrowsers()->At(0))
9807 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
9808 : gClient->GetRoot(),
9809 "Exception", e.what(),
9810 kMBIconExclamation); // deletes self on dismiss?
9811 }
9812 gROOT->SetFromPopUp(true);
9813 return;
9814 }
9815
9816 TString sOpt2(opt);
9817 sOpt2.ToLower();
9818 if (!get() && !IsFolder() && !sOpt2.Contains("x="))
9819 return;
9820
9821 if (auto mc = get<RooStats::ModelConfig>()) {
9822 xRooNode(*mc->GetPdf(), fParent).Draw(opt); // draw the pdf of the config
9823 } else if (auto ir = get<RooStats::HypoTestInverterResult>()) {
9824 xRooHypoSpace(ir).Draw(opt);
9826 return;
9827 } else if (get<RooStats::HypoTestResult>()) {
9828 if (gPad)
9829 gPad->Clear();
9830 xRooNLLVar::xRooHypoPoint(std::dynamic_pointer_cast<RooStats::HypoTestResult>(fComp)).Draw(opt);
9831 {
9832 PadRefresher p(gPad); // refreshes the pad
9833 }
9835 return;
9836 }
9837
9838 if (sOpt2 == "pcls" && get<RooRealVar>() && fParent && fParent->get<RooAbsPdf>()) {
9839 // use the first selected dataset
9840 auto _dsets = fParent->datasets();
9841 // bool _drawn=false;
9842 TString dsetName = "";
9843 for (auto &d : _dsets) {
9844 if (d->get()->TestBit(1 << 20)) {
9845 dsetName = d->get()->GetName();
9846 break;
9847 }
9848 }
9849 auto hs = fParent->nll(dsetName.Data()).hypoSpace(get<RooRealVar>()->GetName());
9850 hs.limits("cls visualize");
9851 hs.SetName(TUUID().AsString());
9852 if (ws()) {
9853 ws()->import(*hs.result());
9854 }
9855 return;
9856 }
9857
9858 if (auxFunctions.empty()) {
9859 // add the defaults: Ratio and Signif
9861 "Ratio",
9862 [](double a, double b, double) {
9863 if (a == 0)
9864 return 0.;
9865 if (b == 0 && a == 0)
9866 return 1.;
9867 return a / b;
9868 },
9869 true);
9871 "Signif",
9872 [](double n, double b, double sigma) {
9873 double t0 = 0;
9874 if (sigma <= 0.) {
9875 // use simplified expression ...
9876 t0 = 2. * (((n == 0) ? 0 : n * log(n / b)) - (n - b));
9877 } else {
9878 double sigma2 = sigma * sigma;
9879 double b_hathat = 0.5 * (b - sigma2 + sqrt(pow(b - sigma2, 2) + 4 * n * sigma2));
9880 // double s_hat = n - m;
9881 // double b_hat = m;
9882 t0 = 2. * (((n == 0) ? 0 : n * log(n / b_hathat)) + b_hathat - n + pow(b - b_hathat, 2) / (2. * sigma2));
9883 }
9884 if (t0 < 0)
9885 return 0.; // can happen from numerical precision
9886 return (n >= b) ? sqrt(t0) : -sqrt(t0);
9887 },
9888 false);
9889 }
9890
9891 TString sOpt(opt);
9892
9893 RooAbsLValue *v = nullptr;
9894 std::vector<double> xPoints;
9895 if (sOpt2.Contains("x=")) {
9896 // specifying a particular var to scan over ...
9897 int _idx = sOpt2.Index("x=");
9898 int _eidx = sOpt2.Index(';', _idx);
9899 TString varPart = sOpt(_idx + 2, (_eidx < 0 ? sOpt2.Length() : _eidx) - (_idx + 2));
9901 // if varName is of form str(num,num,num) then can infer scan points
9902 if (auto _idx2 = varPart.Index("("); _idx2 > 0) {
9903 varName = varPart(0, _idx2);
9904 TStringToken pattern(TString(varPart(_idx2 + 1, varPart.Length() - _idx2 - 2)), ",");
9905 double min(0);
9906 double max(0);
9907 int nBins = 0;
9908 int ii = 0;
9909 while (pattern.NextToken()) {
9910 TString s = pattern;
9911 if (ii == 0) {
9912 nBins = s.Atoi();
9913 } else if (ii == 1) {
9914 min = s.Atof();
9915 } else if (ii == 2) {
9916 max = s.Atof();
9917 }
9918 ii++;
9919 }
9920 if (nBins > 100)
9921 nBins = 100; // limit scanning to 100 points
9922 if (nBins > 1) {
9923 for (double x = min; x <= max; x += (max - min) / (nBins - 1)) {
9924 xPoints.push_back(x);
9925 }
9926 } else if (nBins == 1)
9927 xPoints.push_back((min + max) / 2.);
9928 }
9929 v = getObject<RooAbsLValue>(varName.Data()).get();
9930 if (!v) {
9931 throw std::runtime_error(TString::Format("Could not find variable %s", varName.Data()));
9932 }
9933 if (xPoints.empty() && !obs().find(varName.Data()) &&
9934 dynamic_cast<RooAbsRealLValue *>(v)) { // will draw obs as regular (e.g. hist)
9935 double tmp = static_cast<RooAbsRealLValue *>(v)->getVal();
9936 for (int i = 0; i < v->numBins(GetName()); i++) {
9937 v->setBin(i, GetName());
9938 xPoints.push_back(static_cast<RooAbsRealLValue *>(v)->getVal());
9939 }
9940 static_cast<RooAbsRealLValue *>(v)->setVal(tmp);
9941 }
9942 sOpt2 = TString(sOpt2(0, _idx)) + sOpt2(_idx + 2 + varPart.Length() + 1, sOpt2.Length());
9943 sOpt = TString(sOpt(0, _idx)) + sOpt(_idx + 2 + varPart.Length() + 1, sOpt.Length());
9944 }
9945 TString forceNames = "";
9946 if (sOpt2.Contains("force")) {
9947 // force plots show how much NLL changes wrt to a change of variables
9948 if (get<RooRealVar>() && fParent && fParent->get<RooAbsPdf>()) {
9949 // assume want force of this parameter from the parent pdf
9950 TString ff = sOpt(sOpt2.Index("force"), sOpt2.Index("force") + 5);
9951 sOpt.ReplaceAll(ff, TString::Format("force%s", get()->GetName()));
9952 fParent->Draw(sOpt);
9953 return;
9954 } else if (get<RooAbsPdf>()) {
9955 // extract the parameter(s) to calculate force for
9956 forceNames = sOpt(sOpt2.Index("force") + 5, sOpt2.Length());
9957 sOpt = sOpt(0, sOpt2.Index("force"));
9958 sOpt2 = sOpt2(0, sOpt2.Index("force"));
9959 } else {
9960 Error("Draw", "Can only compute forces with PDFs");
9961 return; // don't throw because will cause browser to exit if done from there
9962 }
9963 }
9964 bool hasOverlay = sOpt2.Contains("overlay");
9965 TString overlayName = "";
9966 if (hasOverlay) {
9967 // whatever follows overlay is the variation name
9968 overlayName = sOpt(sOpt2.Index("overlay") + 7, sOpt2.Length());
9969 sOpt = sOpt(0, sOpt2.Index("overlay"));
9970 sOpt2 = sOpt2(0, sOpt2.Index("overlay"));
9971 }
9972 if (sOpt2.Contains("ratio") && !sOpt2.Contains("auxratio"))
9973 sOpt += "auxRatio";
9974 if (sOpt2.Contains("significance") && !sOpt2.Contains("auxsignif"))
9975 sOpt += "auxSignif";
9976
9977 std::string auxPlotTitle;
9978 for (auto &[k, _] : auxFunctions) {
9979 if (sOpt.Contains(TString::Format("aux%s", k.c_str()))) {
9980 auxPlotTitle = k;
9981 }
9982 sOpt.ReplaceAll(TString::Format("aux%s", k.c_str()), "");
9983 }
9984
9985 sOpt.ToLower();
9986 sOpt.ReplaceAll("ratio", "");
9987 sOpt.ReplaceAll("significance", ""); // remove old option if still given
9988 bool nostack = sOpt.Contains("nostack");
9989 sOpt.ReplaceAll("nostack", "");
9990 bool hasSame = sOpt.Contains("same");
9991 sOpt.ReplaceAll("same", "");
9992 bool hasGoff = sOpt.Contains("goff");
9993 sOpt.ReplaceAll("goff", "");
9994 bool hasFR = sOpt.Contains("pull") && !get<RooFitResult>();
9995 sOpt.ReplaceAll("pull", "");
9996 bool hasText = sOpt.Contains("text");
9997 bool hasTexte = sOpt.Contains("texte");
9998 bool hasErrorOpt = sOpt.Contains("e");
9999 sOpt.ReplaceAll("e", "");
10000 if (hasTexte) {
10001 sOpt.ReplaceAll("txt", "texte");
10002 } else if (hasText) {
10003 sOpt.ReplaceAll("txt", "text");
10004 }
10005 if (auxPlotTitle == "Signif")
10006 hasErrorOpt = true; // must calculate error to calculate significance
10007 if (hasOverlay)
10008 hasSame = true; // when overlaying must be putting on same
10009
10010 TVirtualPad *pad = gPad;
10011
10012 TH1 *hAxis = nullptr;
10013
10014 auto clearPad = []() {
10015 gPad->Clear();
10016 if (gPad->GetNumber() == 0) {
10017 gPad->SetBottomMargin(gStyle->GetPadBottomMargin());
10018 gPad->SetTopMargin(gStyle->GetPadTopMargin());
10019 gPad->SetLeftMargin(gStyle->GetPadLeftMargin());
10020 gPad->SetRightMargin(gStyle->GetPadRightMargin());
10021 }
10022 // if (gPad == gPad->GetCanvas()) {
10023 // gPad->GetCanvas()->SetCanvasSize( gPad->GetCanvas()->GetWindowWidth() - 4,
10024 // gPad->GetCanvas()->GetWindowHeight() - 28 );
10025 // }
10026 };
10027
10028 if (!hasSame || !pad) {
10029 if (!pad) {
10031 pad = gPad;
10032 }
10033
10034 } else {
10035 // get the histogram representing the axes
10036 hAxis = dynamic_cast<TH1 *>(pad->GetPrimitive("axis"));
10037 if (!hAxis) {
10038 for (auto o : *pad->GetListOfPrimitives()) {
10039 if (hAxis = dynamic_cast<TH1 *>(o); hAxis)
10040 break;
10041 }
10042 }
10043 if (hAxis && !v) {
10044 v = getObject<RooAbsLValue>(hAxis->GetXaxis()->IsAlphanumeric() ? hAxis->GetXaxis()->GetTimeFormatOnly()
10045 : hAxis->GetXaxis()->GetName())
10046 .get();
10047 }
10048 }
10049
10050 if (!hasSame) {
10051 if (gPad != gPad->GetCanvas()) {
10052 gPad->SetName(GetName()); // only rename the pad if its not the parent canvas
10053 }
10054 gPad->SetTitle(GetTitle());
10055 }
10056
10058
10059 auto adjustYRange = [&](double min, double max, TH1 *hh = nullptr, bool symmetrize = false) {
10060 if (!hh)
10061 hh = hAxis;
10062 // give max and min a buffer ...
10063 max += gStyle->GetHistTopMargin() * (max - min);
10064 if (min > 0)
10065 min = std::max(min * 0.9, min - gStyle->GetHistTopMargin() * (max - min));
10066 if (hh) {
10067 double ymin = hh->GetMinimum();
10068 double ymax = hh->GetMaximum();
10069 if (hh->GetMaximumStored() == -1111)
10070 ymax += gStyle->GetHistTopMargin() * (ymax - ymin);
10071 if (hh->GetMinimumStored() == -1111) {
10072 if (gStyle->GetHistMinimumZero() && ymax >= 0) {
10073 ymin = 0;
10074 } else if (ymin < 0) {
10075 ymin -= gStyle->GetHistTopMargin() * (ymax - ymin);
10076 } else {
10077 ymin = std::max(ymin * 0.9, ymin - gStyle->GetHistTopMargin() * (ymax - ymin));
10078 }
10079 // see TGLPlotPainter to complete the mimic, but we leave off here truncating @ 0 if ymax>0
10080 }
10081 // make ymax at least 3x bigger than biggest error if has error
10082 if (hh->GetSumw2()) {
10083 double smallestErrDown3 = -std::numeric_limits<double>::infinity();
10084 double smallestErrUp3 = std::numeric_limits<double>::infinity();
10085 for (int i = 1; i <= hh->GetNbinsX(); i++) {
10086 smallestErrDown3 = std::max(smallestErrDown3, hh->GetBinContent(i) - 3 * hh->GetBinError(i));
10087 smallestErrUp3 = std::min(smallestErrUp3, hh->GetBinContent(i) + 3 * hh->GetBinError(i));
10088 }
10089 max = std::max(max, smallestErrUp3);
10090 min = std::min(min, smallestErrDown3);
10091 }
10092 bool change = false;
10093 if (min < ymin) {
10094 ymin = min;
10095 change = true;
10096 }
10097 if (max > ymax) {
10098 ymax = max;
10099 change = true;
10100 }
10101 if (change) {
10102 // note: unfortunately when user 'unzooms' y axis it resets stored minimum to -1111, so lose range
10103 if (symmetrize) {
10104 double down = hh->GetBinContent(1) - ymin;
10105 double up = ymax - hh->GetBinContent(1);
10106 if (down > up) {
10107 ymax = hh->GetBinContent(1) + down;
10108 } else {
10109 ymin = hh->GetBinContent(1) - up;
10110 }
10111 }
10112 if (hh == hAxis && pad && !pad->GetLogy() && ymin > 0 && (log10(ymax) - log10(max)) >= 3) {
10113 // auto-log the pad
10114 pad->SetLogy();
10115 }
10116 if (hh == hAxis && pad && ymin == 0 && pad->GetLogy()) {
10117 ymin = 1e-2;
10118 }
10119 if (ymin == 0 && ymax > 10)
10120 ymin = 0.1; // adjust min so if user activates log scale it isn't bad
10121 hh->SetMinimum(ymin);
10122 hh->SetMaximum(ymax);
10123 hh->GetYaxis()->Set(1, ymin, ymax);
10124 hh->SetAxisRange(ymin, ymax, "Y");
10125 }
10126 }
10127 };
10128
10129 auto graphMinMax = [](TGraphAsymmErrors *gr) {
10130 double ymax = -std::numeric_limits<double>::infinity();
10131 double ymin = std::numeric_limits<double>::infinity();
10132 for (int i = 0; i < gr->GetN(); i++) {
10133 ymax = std::max(ymax, gr->GetPointY(i) + gr->GetErrorYhigh(i));
10134 ymin = std::min(ymin, gr->GetPointY(i) - gr->GetErrorYlow(i));
10135 }
10136 return std::make_pair(ymin, ymax);
10137 };
10138
10139 if (!xPoints.empty()) {
10140 // create a graph using GetContent
10142 out->SetName(GetName());
10143 out->SetTitle(GetTitle());
10144 out->SetFillColor(out->GetLineColor());
10145 out->SetMarkerStyle(0);
10146 out->SetFillStyle(hasErrorOpt ? 3005 : 0);
10147 double tmp = static_cast<RooAbsRealLValue *>(v)->getVal();
10148 for (auto &x : xPoints) {
10149 static_cast<RooAbsRealLValue *>(v)->setVal(x);
10150 out->AddPoint(x, GetContent());
10151 if (hasErrorOpt) {
10152 out->SetPointEYlow(out->GetN() - 1, GetError());
10153 out->SetPointEYhigh(out->GetN() - 1, out->GetErrorYlow(out->GetN() - 1)); // symmetric error for now
10154 }
10155 }
10156 static_cast<RooAbsRealLValue *>(v)->setVal(tmp);
10157 out->GetHistogram()->GetXaxis()->SetTitle(static_cast<RooAbsRealLValue *>(v)->GetTitle());
10158 out->SetBit(kCanDelete);
10159 out->Draw(TString(hasSame ? "L" : "AL") + (hasErrorOpt ? "3" : ""));
10160 return;
10161 }
10162
10163 if (hasFR) {
10164 // drawing the fitresult as a pull plot on a subpad, and rest of the draw elsewhere
10165 clearPad();
10166 pad->Divide(1, 2, 1e-9, 1e-9); //,0,0);
10167 pad->GetPad(1)->SetPad(0, 0.2, 1, 1);
10168 pad->GetPad(2)->SetPad(0, 0, 1, 0.2);
10169 TString optNoFR(opt);
10170 optNoFR.ReplaceAll("pull", "");
10171 pad->cd(1);
10172 Draw(optNoFR);
10173 pad->cd(2);
10174 auto _fr = fitResult();
10175 _fr.Draw();
10176 // switch into subpad
10177 gPad->cd(1);
10178 gPad->SetFillColor(kGray);
10179 gPad->GetFrame()->SetFillColor(kWhite);
10180 gPad->GetFrame()->SetFillStyle(1001);
10181 gPad->SetTopMargin(0);
10182 gPad->SetBottomMargin(0);
10183 gPad->SetName("pull");
10184 // split the pull graph into individual points -- for benefit of GUI status bar
10185 auto pullGraph = dynamic_cast<TGraphAsymmErrors *>(gPad->GetPrimitive("pulls"));
10186 if (!pullGraph) {
10187 Error("Draw", "Couldn't find pull graph");
10188 return;
10189 }
10190 pullGraph->SetName("nominal");
10191 TMultiGraph *mg = new TMultiGraph;
10192 mg->SetName("editables");
10193
10194 auto scaleHist = static_cast<TH1 *>(pullGraph->FindObject("scales"));
10195 if (!scaleHist)
10196 throw std::runtime_error("Could not find scales in fit result");
10197
10198 for (auto i = 0; i < pullGraph->GetN(); i++) {
10199 auto g = new TGraphAsymmErrors;
10200 g->SetName(scaleHist->GetXaxis()->GetBinLabel(i + 1));
10201 auto _p = dynamic_cast<RooRealVar *>(_fr.get<RooFitResult>()->floatParsFinal().find(g->GetName()));
10202 if (!_p) {
10203 Warning("Draw", "Found a non-var in the floatParsFinal list: %s - this shouldn't happen", g->GetName());
10204 continue;
10205 }
10206 g->SetTitle(TString::Format(
10207 "%s=%g +/- %s [%g,%g]", strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName(), _p->getVal(),
10208 _p->hasAsymError() ? TString::Format("(%g,%g)", _p->getAsymErrorHi(), _p->getAsymErrorLo()).Data()
10209 : TString::Format("%g", _p->getError()).Data(),
10210 scaleHist->GetBinContent(i + 1), scaleHist->GetBinError(i + 1)));
10211 g->SetPoint(0, pullGraph->GetPointX(i), pullGraph->GetPointY(i));
10212 g->SetPointEYhigh(0, pullGraph->GetErrorYhigh(i));
10213 g->SetPointEYlow(0, pullGraph->GetErrorYlow(i));
10214 g->SetEditable(true);
10215 g->SetHighlight(true);
10216 g->SetMarkerStyle(20);
10217 g->SetMarkerSize(0.5);
10218 mg->Add(g);
10219 }
10220 // gPad->GetListOfPrimitives()->Remove(pullGraph); delete pullGraph;
10221 mg->Draw("z0p");
10222 mg->SetBit(kCanDelete);
10223 auto _thisClone = new xRooNode("node", fComp, fParent);
10224 _thisClone->SetBit(kCanDelete);
10225 _thisClone->AppendPad();
10226
10227 // ensure statusbar visible for interactive plot
10228 // turned this off for now ... as not needed if doing through browser, status bar already there
10229 // if (gPad->GetCanvas() && !gPad->GetCanvas()->TestBit(TCanvas::kShowEventStatus)) {
10230 // gPad->GetCanvas()->ToggleEventStatus();
10231 // }
10232 gPad->AddExec("interactivePull", TString::Format("%s::Interactive_Pull()", ClassName()));
10233
10234 pad->cd();
10235 return;
10236 }
10237
10238 if (auto _simPdf = get<RooSimultaneous>();
10239 _simPdf && !(v && strcmp(_simPdf->indexCat().GetName(), dynamic_cast<TObject *>(v)->GetName()) == 0)) {
10240 auto _channels = bins();
10241 int _size = 0;
10242 for (auto &_v : _channels) {
10243 if (!_v->IsHidden())
10244 _size++;
10245 }
10246 if (!hasSame) {
10247 if (_size > 4) {
10248 // add a pad for the common legends
10249 _size++;
10250 }
10251 clearPad();
10252 pad->SetBorderSize(0);
10253 // if (pad->GetCanvas() == pad) {
10254 // if(_size>4) {
10255 // int n = _size;
10256 // Int_t w = 1, h = 1;
10257 // if (pad->GetCanvas()->GetWindowWidth() > pad->GetCanvas()->GetWindowHeight()) {
10258 // w = TMath::Ceil(TMath::Sqrt(n));
10259 // h = TMath::Floor(TMath::Sqrt(n));
10260 // if (w*h < n) w++;
10261 // } else {
10262 // h = TMath::Ceil(TMath::Sqrt(n));
10263 // w = TMath::Floor(TMath::Sqrt(n));
10264 // if (w*h < n) h++;
10265 // }
10266 // // adjust the window size to display only 4 in the window, with scroll bars
10267 // pad->GetCanvas()->SetCanvasSize( w*((pad->GetCanvas()->GetWindowWidth()-4)/2.) -16
10268 // ,h*((pad->GetCanvas()->GetWindowHeight()-28)/2.) - 16 );
10269 // } else {
10270 // //pad->GetCanvas()->Set(
10271 // w*(pad->GetCanvas()->GetWindowWidth()/2.),h*(pad->GetCanvas()->GetWindowHeight()/2.)) )
10272 // }
10273 // }
10274 int ncols = _simPdf->getStringAttribute("ncols") ? TString(_simPdf->getStringAttribute("ncols")).Atoi() : 0;
10275 if (ncols > 0) {
10276 dynamic_cast<TPad *>(pad)->Divide(ncols, std::ceil(double(_size) / ncols), 1e-9, 1e-9);
10277 } else {
10278 dynamic_cast<TPad *>(pad)->DivideSquare(_size, 1e-9, 1e-9);
10279 }
10280 if (_size > 5) {
10281 auto _pad = pad->GetPad(_size); // will use as the legend pad
10282 _pad->SetName("legend");
10283 // stretch the pad all the way to the left
10284 _pad->SetPad(_pad->GetXlowNDC(), _pad->GetYlowNDC(), 1.0, _pad->GetYlowNDC() + _pad->GetHNDC());
10285 // and make all the remaining pads transparent
10286 int x = _size;
10287 while (pad->GetPad(x + 1)) {
10288 pad->GetPad(x + 1)->SetFillStyle(0);
10289 x++;
10290 }
10291 }
10292 }
10293 int i = 0;
10294 auto &chanVar = const_cast<RooAbsCategoryLValue &>(_simPdf->indexCat());
10295 // auto _idx = chanVar.getIndex();
10296 auto _range = GetRange();
10297 std::vector<TString> chanPatterns;
10298 if (_range && strlen(_range)) {
10299 TStringToken pattern(_range, ",");
10300 while (pattern.NextToken()) {
10301 chanPatterns.emplace_back(pattern);
10302 }
10303 }
10304 for (auto &_v : _channels) {
10305 if (_v->IsHidden())
10306 continue;
10307 TString s(_v->GetName());
10308 pad->cd(++i);
10309 gPad->SetName(s);
10310 TString cName = s(s.Index('=') + 1, s.Length());
10311 chanVar.setLabel(cName);
10312 bool inRange = chanPatterns.empty();
10313 for (auto &p : chanPatterns) {
10314 if (chanVar.inRange(p)) {
10315 inRange = true;
10316 break;
10317 }
10318 }
10319 if (!inRange || !_v->get<RooAbsReal>()->isSelectedComp())
10320 gPad->SetFillColor(kGray);
10321 if (!hasSame && _size > 1 && (gStyle->GetTitleFont("Y") % 10) == 3)
10322 gPad->SetLeftMargin(std::min(gPad->GetLeftMargin() * (1. / gPad->GetWNDC()), 0.3));
10323 _v->Draw(opt);
10325 }
10326 pad->cd(0);
10327 gPad->Modified();
10328 // gPad->Update();
10329 return;
10330 }
10331
10332 if (!get() || get<RooArgList>()) {
10333 // is a group draw all the submembers
10334 browse();
10335 int _size = 0;
10336 // int _size = _channels.size(); // size(); if (find("!.vars")) _size--;
10337 for (auto &_v : *this) {
10338 if (_v->IsHidden())
10339 continue;
10340 if (strcmp(GetName(), ".vars") == 0) {
10341 // auto hide obs and "1" and const var
10342 if (_v->get<RooAbsArg>()->getAttribute("obs"))
10343 continue;
10344 if (strcmp(_v->get()->GetName(), "1") == 0 || strcmp(_v->get()->GetName(), "ONE") == 0 ||
10345 TString(_v->get()->GetName()).BeginsWith("binWidth_"))
10346 continue;
10347 if (_v->get()->InheritsFrom("RooConstVar"))
10348 continue;
10349 }
10350 TString s(_v->GetName());
10351 if (s.BeginsWith(".") || s.BeginsWith("!"))
10352 continue;
10353 _size++;
10354 }
10355 if (!hasSame) {
10356 clearPad();
10357 pad->SetBorderSize(0);
10358 dynamic_cast<TPad *>(pad)->DivideSquare(_size, 1e-9, 1e-9);
10359 }
10360 int i = 0;
10361 for (auto &_v : *this) {
10362 if (_v->IsHidden())
10363 continue;
10364 if (strcmp(GetName(), ".vars") == 0) {
10365 // auto hide obs and "1" and const var
10366 if (_v->get<RooAbsArg>()->getAttribute("obs"))
10367 continue;
10368 if (strcmp(_v->get()->GetName(), "1") == 0 || strcmp(_v->get()->GetName(), "ONE") == 0 ||
10369 TString(_v->get()->GetName()).BeginsWith("binWidth_"))
10370 continue;
10371 if (_v->get()->InheritsFrom("RooConstVar"))
10372 continue;
10373 }
10374 TString s(_v->GetName());
10375 if (s.BeginsWith(".") || s.BeginsWith("!"))
10376 continue;
10377 pad->cd(++i);
10378 gPad->SetName(s);
10379 if (!hasSame && _size > 1 && (gStyle->GetTitleFont("Y") % 10) == 3)
10380 gPad->SetLeftMargin(std::min(gPad->GetLeftMargin() * (1. / gPad->GetWNDC()), 0.3));
10381 _v->Draw(opt);
10382 // pad->Modified();//pad->Update();
10384 }
10385 pad->cd(0);
10386 gPad->Modified();
10387 // gPad->Update();
10388 return;
10389 }
10390
10391 if (get()->InheritsFrom("RooProdPdf")) {
10392 // draw the main pdf, if there is one...
10393 auto _mainChild = mainChild();
10394 if (_mainChild) {
10395 _mainChild.Draw(opt);
10396 gPad->SetName(GetName());
10397 return;
10398 }
10399 }
10400
10401 if (auto fr = get<RooFitResult>(); fr) {
10402 if (sOpt.Contains("corr")) {
10403 // do correlation matrix
10404 // if a number follows 'corr', reduce the correlation matrix to show only the most extreme correlations
10405 int numCorrs = TString(sOpt(sOpt.Index("corr") + 4, sOpt.Length())).Atoi();
10406 if (numCorrs == 0)
10407 numCorrs = fr->correlationMatrix().GetNcols();
10408
10409 TH2 *hist = nullptr;
10410 if (numCorrs < fr->correlationMatrix().GetNcols()) {
10411 // need to reduce
10412 std::set<std::pair<double, size_t>> maxCorrs;
10413 for (int i = 0; i < fr->correlationMatrix().GetNcols(); i++) {
10414 double maxCorr = 0;
10415 for (int j = 0; j < fr->correlationMatrix().GetNcols(); j++) {
10416 if (j == i)
10417 continue;
10418 maxCorr = std::max(std::abs(fr->correlationMatrix()(i, j)), maxCorr);
10419 }
10420 maxCorrs.insert({maxCorr, i});
10421 }
10422 std::vector<size_t> topN;
10423 int c = 0;
10424 for (auto itr = maxCorrs.rbegin(); itr != maxCorrs.rend(); ++itr) {
10425 topN.push_back(itr->second);
10426 c++;
10427 if (c == numCorrs)
10428 break;
10429 }
10430 hist = new TH2D(fr->GetName(), TString::Format("%s - Top %d correlations", fr->GetTitle(), numCorrs),
10432 for (size_t i = 0; i < topN.size(); i++) {
10433 hist->GetXaxis()->SetBinLabel(i + 1, fr->floatParsFinal().at(topN.at(i))->GetTitle());
10434 hist->GetYaxis()->SetBinLabel(numCorrs - i, fr->floatParsFinal().at(topN.at(i))->GetTitle());
10435 for (size_t j = 0; j < topN.size(); j++) {
10436 hist->Fill(i + 0.5, numCorrs - j - 0.5, fr->correlationMatrix()(topN.at(i), topN.at(j)));
10437 }
10438 }
10439 hist->SetMinimum(-1);
10440 hist->SetMaximum(1);
10441
10442 } else {
10443 hist = fr->correlationHist(fr->GetName());
10444 hist->SetTitle(fr->GetTitle());
10445 }
10446
10447 hist->SetBit(kCanDelete);
10448 hist->Scale(100);
10449 hist->SetStats(false);
10450 hist->SetDirectory(nullptr);
10452 gStyle->SetPaintTextFormat(".1f");
10453 hist->GetXaxis()->SetTickSize(0);
10454 hist->GetYaxis()->SetTickSize(0);
10455 hist->SetMinimum(-100);
10456 hist->Draw(sOpt);
10458 gPad->SetGrid(1, 1);
10459 gPad->SetLogy(0);
10460 gPad->SetLogx(0);
10461 return;
10462 }
10463
10464 if (sOpt.Contains("brakdown")) { // e will have been removed above
10465
10466 // breakdown is quadrature difference between total error and conditional error
10467 // group by 'group' attribute
10468
10469 std::string poiName;
10470 if (sOpt.Contains("brakdown:")) {
10471 TString sOpt3(opt);
10472 poiName = sOpt3(sOpt3.Index("breakdown:") + 10, sOpt3.Length());
10473 } else {
10474 std::unique_ptr<RooAbsCollection> _poi(fr->floatParsFinal().selectByAttrib("poi", true));
10475 if (_poi->empty()) {
10476 throw std::runtime_error("No floating poi in the fit");
10477 } else if (_poi->size() != 1) {
10478 throw std::runtime_error("Multiple poi in the fit");
10479 }
10480 poiName = _poi->first()->GetName();
10481 }
10482 RooRealVar *poi = dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(poiName.c_str()));
10483 if (!poi) {
10484 throw std::runtime_error(TString::Format("Cannot find parameter %s", poiName.c_str()));
10485 }
10486 std::set<std::string> groups;
10487 for (auto p : fr->floatParsFinal()) {
10488 if (p == poi) {
10489 continue;
10490 } else if (p->getStringAttribute("group")) {
10491 groups.insert(p->getStringAttribute("group"));
10492 } else {
10493 groups.insert(p->GetTitle());
10494 }
10495 }
10496
10497 auto roundedVal = xRooFit::matchPrecision(std::pair(poi->getVal(), poi->getError()));
10498
10499 TPie *pie = new TPie(TString::Format("breakdown:%s", poi->GetName()),
10500 TString::Format("%s: %g #pm %g", poi->GetTitle(), roundedVal.first, roundedVal.second),
10501 groups.size() + 1);
10502
10503 // for display of errors will go to one extra dp ...
10504 roundedVal.second *= .1;
10505
10506 // do breakdown by removing parameters in blocks according to groups and seeing impact on variance
10507 // this will give the correct sum but will be order-dependent if there are correlations between
10508 // groups. therefore we will stick with group-by-group
10509 // RooArgList pars(fr->floatParsFinal()); // pars to not condition on
10510 // double variance = pow(dynamic_cast<RooRealVar*>(poi)->getError(),2);
10511 int i = 0;
10512 for (auto group : groups) {
10513 RooArgList pars(fr->floatParsFinal()); // pars to not condition on
10514 double variance = pow(dynamic_cast<RooRealVar *>(poi)->getError(), 2);
10515 for (auto p : fr->floatParsFinal()) {
10516 if (p == poi) {
10517 continue;
10518 } else if ((p->getStringAttribute("group") && group == p->getStringAttribute("group")) ||
10519 (!p->getStringAttribute("group") && group == p->GetTitle())) {
10520 // conditioning on this parameter ... remove from pars list
10521 pars.remove(*p);
10522 }
10523 }
10524 int idx = pars.index(poiName.c_str());
10525 double reducedVar = fr->conditionalCovarianceMatrix(pars)(idx, idx);
10526 if (reducedVar > variance) {
10527 Warning("Draw", "breakdown group %s variance bigger than preceding?", group.c_str());
10528 pie->SetEntryVal(i, 0);
10529 pie->SetEntryLabel(i, TString::Format("%s: NaN", group.c_str()));
10530 } else {
10531 pie->SetEntryVal(i, variance - reducedVar);
10533 std::pair(sqrt(variance - reducedVar), roundedVal.second)); // r.first will be the rounded error
10534 if (r.first > 0) {
10535 pie->SetEntryLabel(i, TString::Format("%s: %g", group.c_str(), r.first));
10536 } else {
10537 pie->SetEntryLabel(i, group.c_str()); // suppress labels for negligible errors.
10538 }
10539 }
10540 pie->SetEntryFillColor(i, TColor::GetColorPalette(TColor::GetNumberOfColors() * i / pie->GetEntries()));
10541 // variance = reducedVar;
10542 i++;
10543 }
10544 // remaining variance is statistical=
10545 double variance = fr->conditionalCovarianceMatrix(*poi)(0, 0);
10546 auto r =
10547 xRooFit::matchPrecision(std::pair(sqrt(variance), roundedVal.second)); // r.first will be the rounded error
10548 pie->SetEntryVal(i, variance);
10549 pie->SetEntryLabel(i, TString::Format("stat: %g", r.first));
10550 pie->SetEntryFillColor(i, TColor::GetColorPalette(TColor::GetNumberOfColors() * i / pie->GetEntries()));
10551 pie->SetBit(kCanDelete);
10552 pie->SetRadius(0.17);
10553 pie->SetTextSize(gStyle->GetTitleYSize());
10554 pie->Draw("NOL");
10555 return;
10556 }
10557
10558 // plot pull or impact
10560 out->SetName(TString::Format("%s_pull", fr->GetName()));
10561 out->SetTitle("Fit Result Pulls");
10562 std::vector<TString> graphLabels;
10564 ugraph->SetName(TString::Format("%s_pull_unconstrained", fr->GetName()));
10565 ugraph->SetTitle("Fit Result Pulls");
10566 std::vector<TString> ugraphLabels;
10567 std::map<std::string, double> scale;
10568 std::map<std::string, double> offset;
10569 for (auto &p : fr->floatParsFinal()) {
10570 auto _v = dynamic_cast<RooRealVar *>(p);
10571 if (!_v)
10572 continue;
10573
10574 if (std::isnan(_v->getErrorHi()) || std::isnan(_v->getErrorLo())) {
10575 Warning("Draw", "%s error is invalid", _v->GetName());
10576 }
10577
10578 // need to get constraint mean and error parameters ....
10579 // look for normal gaussian and poisson cases
10580 double prefitError = 0;
10581 double prefitVal = 0;
10582 double customScale = 0;
10583 if (auto ip =
10584 dynamic_cast<RooRealVar *>(fr->floatParsInit().find(p->GetName()))) { // handles if no prefit available
10585 prefitError = ip->getError();
10586 prefitVal = ip->getVal();
10587 };
10588
10589 std::shared_ptr<xRooNode> pConstr;
10590 if (fParent && fParent->getObject<RooRealVar>(p->GetName())) {
10591 auto _vv = fParent->getObject<RooRealVar>(p->GetName());
10592 if (_vv->hasRange("pullScale")) {
10593 customScale = (_vv->getMax("pullScale") - _vv->getMin("pullScale")) / 2.;
10594 }
10595 auto _constr = xRooNode(_vv, *this).constraints();
10596 for (auto &c : _constr) {
10597 if (c->get<RooPoisson>() || c->get<RooGaussian>()) {
10598 // require parameter to be a direct server of the constraint pdf to count if its a gaussian
10599 bool isServer = true;
10600 if (c->get<RooGaussian>()) {
10601 isServer = false;
10602 for (auto s : c->get<RooAbsArg>()->servers()) {
10603 if (strcmp(s->GetName(), p->GetName()) == 0) {
10604 isServer = true;
10605 break;
10606 }
10607 }
10608 }
10609 if (isServer) {
10610 pConstr = c;
10611 break;
10612 }
10613 }
10614 }
10615 }
10616 if (pConstr) {
10617
10618 // there will be 3 deps, one will be this par, the other two are the mean and error (or error^2 in case of
10619 // poisson
10620
10621 // std::cout << p->GetName() << " extracted " << prefitVal << " " << prefitError << " from ";
10622 // pConstr->deps().Print();
10623 pConstr->browse();
10624 if (pConstr->get<RooPoisson>() && pConstr->find(".x")) {
10625 std::string xName = pConstr->find(".x")->get()->GetName();
10626 prefitVal = pConstr->find(".x")->get<RooAbsReal>()->getVal();
10627 for (auto &_d : pConstr->vars()) {
10628 if (strcmp(p->GetName(), _d->get()->GetName()) == 0)
10629 continue;
10630 if (xName == _d->get()->GetName())
10631 continue;
10632 if (_d->get<RooAbsReal>()->getVal())
10633 prefitError = _d->get<RooAbsReal>()->getVal();
10634 }
10635 if (fr->constPars().find(pConstr->find(".x")->get()->GetName())) {
10636 // globs was saved to fr, use that instead of current value
10637 prefitVal = fr->constPars().getRealValue(pConstr->find(".x")->get()->GetName());
10638 }
10639 // prefitVal will be the global observable value, need to divide that by tau
10641 // prefiterror will be tau ... need 1/sqrt(tau) for error
10642 prefitError = 1. / sqrt(prefitError);
10643 } else if (auto _g = pConstr->get<RooGaussian>(); _g) {
10644 prefitError =
10645 (pConstr->find(".sigma")) ? pConstr->find(".sigma")->get<RooAbsReal>()->getVal() : prefitError;
10646 prefitVal =
10647 (pConstr->find(".x")) ? pConstr->find(".x")->get<RooAbsReal>()->getVal() : 0; // usually the globs
10648 if (pConstr->find(".x") && fr->constPars().find(pConstr->find(".x")->get()->GetName())) {
10649 // globs was saved to fr, use that instead of current value
10650 prefitVal = fr->constPars().getRealValue(pConstr->find(".x")->get()->GetName());
10651 }
10652 if (pConstr->find(".x") &&
10653 strcmp(p->GetName(), pConstr->find(".x")->get<RooAbsReal>()->GetName()) == 0) {
10654 // hybrid construction case,
10655 prefitVal = pConstr->find(".mean")->get<RooAbsReal>()->getVal();
10656 if (fr->constPars().find(pConstr->find(".mean")->get()->GetName())) {
10657 // globs was saved to fr, use that instead of current value
10658 prefitVal = fr->constPars().getRealValue(pConstr->find(".mean")->get()->GetName());
10659 }
10660 }
10661 }
10662
10663 if (customScale)
10665 if (prefitError == 0) {
10666 Warning("Draw", "failed to determine prefit error of %s, using post-fit error", p->GetName());
10667 prefitError = _v->getError();
10668 }
10669 out->SetPoint(out->GetN(), out->GetN(), (_v->getVal() - prefitVal) / prefitError);
10670 out->SetPointError(out->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
10671 (_v->getErrorHi()) / prefitError);
10672 graphLabels.push_back(p->GetName());
10673 scale[p->GetName()] = prefitError;
10674 offset[p->GetName()] = prefitVal;
10675 } else if (!fParent) {
10676 // no parent to determine constraints from ... prefitError=0 will be the unconstrained ones
10677 if (customScale)
10679 if (prefitError == 0) {
10680 // uses range of var
10681 prefitError = (std::max({_v->getMax() - _v->getVal(), _v->getVal() - _v->getMin(), 4.}) / 4);
10682 ugraph->SetPoint(ugraph->GetN(), ugraph->GetN(), (_v->getVal() - prefitVal) / prefitError);
10683 ugraph->SetPointError(ugraph->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
10684 (_v->getErrorHi()) / prefitError);
10685 ugraphLabels.push_back(p->GetName());
10686 } else {
10687 out->SetPoint(out->GetN(), out->GetN(), (_v->getVal() - prefitVal) / prefitError);
10688 out->SetPointError(out->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
10689 (_v->getErrorHi()) / prefitError);
10690 graphLabels.push_back(p->GetName());
10691 }
10692 scale[p->GetName()] = prefitError;
10693 offset[p->GetName()] = prefitVal;
10694
10695 } else {
10696 // unconstrained (or at least couldn't determine constraint) ... use par range if no prefit error
10697 if (customScale)
10699 if (prefitError == 0) {
10700 prefitError = (std::max({_v->getMax() - _v->getVal(), _v->getVal() - _v->getMin(), 4.}) / 4);
10701 }
10702 ugraph->SetPoint(ugraph->GetN(), ugraph->GetN(), (_v->getVal() - prefitVal) / prefitError);
10703 ugraph->SetPointError(ugraph->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
10704 (_v->getErrorHi()) / prefitError);
10705 ugraphLabels.push_back(p->GetName());
10706 scale[p->GetName()] = prefitError;
10707 offset[p->GetName()] = prefitVal;
10708 }
10709 }
10710 auto graph = out;
10711
10712 // append ugraph points to end of graph
10713 for (int i = 0; i < ugraph->GetN(); i++)
10714 ugraph->SetPointX(i, i + graph->GetN());
10715 int nUnconstrained = ugraph->GetN();
10716 TList tmpList;
10717 tmpList.SetName("tmpList");
10718 tmpList.Add(ugraph);
10719 graph->Merge(&tmpList);
10720 tmpList.RemoveAll();
10721 delete ugraph;
10722 for (auto &l : ugraphLabels) {
10723 graphLabels.push_back(l);
10724 }
10725
10726 graph->SetBit(kCanDelete);
10727 graph->SetMarkerStyle(20);
10728 graph->SetMarkerSize(0.5);
10729
10730 graph->SetMaximum(4);
10731 graph->SetMinimum(-4);
10732
10733 bool doHorizontal =
10734 (!sOpt.Contains("impact") && sOpt.Contains("v")) || (sOpt.Contains("impact") && !sOpt.Contains("himpact"));
10735
10736 std::vector<std::pair<double, std::string>> covariances;
10737 /*double poiError = 0;*/ std::string poiName;
10738 double maxImpact = 0;
10739 if (sOpt.Contains("impact")) {
10740 if (sOpt.Contains("impact:")) {
10741 TString sOpt3(opt);
10742 poiName = sOpt3(sOpt3.Index("impact:") + 7, sOpt3.Length());
10743 } else {
10744 std::unique_ptr<RooAbsCollection> _poi(fr->floatParsFinal().selectByAttrib("poi", true));
10745 if (_poi->empty()) {
10746 throw std::runtime_error("No floating poi in the fit");
10747 } else if (_poi->size() != 1) {
10748 throw std::runtime_error("Multiple poi in the fit");
10749 }
10750 poiName = _poi->first()->GetName();
10751 }
10752 RooAbsArg *poi = fr->floatParsFinal().find(poiName.c_str());
10753 if (!poi) {
10754 throw std::runtime_error(TString::Format("Cannot find parameter %s", poiName.c_str()));
10755 }
10756 size_t poiIdx = fr->floatParsFinal().index(*poi);
10757 // put parameters in order of impact on the poi
10758
10759 // impact is regression coefficient * npError
10760 // relevant regression coefficient is cov / (npVariance)
10761 // i.e. DeltaX/sigmaX = [cov(X,Y)/(sigmaXsigmaY)]DeltaY/sigmaY
10762 // ... DeltaX = [cov(X,Y)/(sigmaY^2)]DeltaY
10763 // if DeltaY is just sigmaY then DeltaX = cov(X,Y)/sigmaY
10764
10765 for (auto &label : graphLabels) {
10766 covariances.emplace_back(fr->covarianceMatrix()(poiIdx, fr->floatParsFinal().index(label)) /
10767 dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(label))->getError(),
10768 label);
10769 }
10770 std::sort(covariances.begin(), covariances.end(),
10771 [&](std::pair<double, std::string> i, std::pair<double, std::string> j) {
10772 return doHorizontal ? (std::abs(i.first) < std::abs(j.first))
10773 : (std::abs(i.first) > std::abs(j.first));
10774 });
10775
10777 std::vector<TString> sortedLabels;
10778 maxImpact = (doHorizontal) ? covariances.back().first
10779 : covariances.front().first; // note: max impact is likely to be self variance
10780 for (auto &c : covariances) {
10781 if (c.second == poi->GetName()) {
10782 // poiError = sqrt(c.first);
10783 continue; // skip self
10784 }
10785 c.first *= 4. / (maxImpact * 1.2);
10786 sortedLabels.push_back(c.second);
10787 size_t i = 0;
10788 for (; i < graphLabels.size(); i++) {
10789 if (graphLabels[i] == c.second) {
10790 break;
10791 }
10792 }
10793 sortedGraph.AddPoint(sortedGraph.GetN(), graph->GetPointY(i));
10794 sortedGraph.SetPointError(sortedGraph.GetN() - 1, 0, 0, graph->GetErrorYlow(i), graph->GetErrorYhigh(i));
10795 }
10796 graph->Set(0);
10798 tmpList2.SetName("tmpList");
10799 tmpList2.Add(&sortedGraph);
10800 graph->Merge(&tmpList2);
10801 tmpList2.RemoveAll();
10803 graph->SetTitle("Fit Result Impact");
10804 }
10805
10806 // create a framing histogram
10807 TH2D *hist;
10808 if (doHorizontal) {
10809 hist = new TH2D(GetName(), fr->GetTitle(), 100, -4, 4, std::max(graph->GetN(), 1), -0.5,
10810 std::max(graph->GetN(), 1) - 0.5);
10811 int i = 1;
10812 for (auto &l : graphLabels) {
10813 hist->GetYaxis()->SetBinLabel(i++, l);
10814 }
10815 if (!graphLabels.empty())
10816 hist->GetYaxis()->LabelsOption("v");
10817 hist->GetXaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
10818 } else {
10819 hist = new TH2D(GetName(), fr->GetTitle(), std::max(graph->GetN(), 1), -0.5, std::max(graph->GetN(), 1) - 0.5,
10820 100, -4, 4);
10821 int i = 1;
10822 for (auto &l : graphLabels) {
10823 hist->GetXaxis()->SetBinLabel(i++, l);
10824 }
10825 if (!graphLabels.empty())
10826 hist->GetXaxis()->LabelsOption("v");
10827 hist->GetYaxis()->SetNdivisions(8, 0, 0);
10828 hist->GetYaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
10829 }
10830 hist->SetStats(false);
10831 hist->SetDirectory(nullptr);
10832 hist->SetBit(kCanDelete);
10833 auto histCopy = dynamic_cast<TH1 *>(hist->Clone(".axis"));
10834 histCopy->SetDirectory(nullptr);
10835 histCopy->SetBit(kCanDelete);
10836 auto _axis = (doHorizontal ? histCopy->GetYaxis() : histCopy->GetXaxis());
10837
10838 /*
10839 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
10840 auto hist = new TH1F(TString::Format(".%s_pullFrame", GetName()), fr->GetTitle(), std::max(graph->GetN(),
10841 1), -0.5, std::max(graph->GetN(), 1) - 0.5); hist->SetStats(false);
10842 hist->SetBit(kCanDelete);
10843 */
10844 // auto hist = graph->GetHistogram();
10845 graph->GetHistogram()->GetXaxis()->Set(std::max(graph->GetN(), 1), -0.5, std::max(graph->GetN(), 1) - 0.5);
10846 for (int ii = 1; ii <= _axis->GetNbins(); ii++) {
10847 graph->GetHistogram()->GetXaxis()->SetBinLabel(ii, _axis->GetBinLabel(ii));
10848 }
10849 // int i = 1;
10850 // for (auto &l : graphLabels) {
10851 // hist->GetXaxis()->SetBinLabel(i++, l);
10852 // }
10853 // hist->SetMaximum(4);
10854 // hist->SetMinimum(-4);
10855 // if (graph->GetN())
10856 // hist->GetXaxis()->LabelsOption("v");
10857 // hist->GetYaxis()->SetNdivisions(8, 0, 0);
10858 // hist->GetYaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
10859 clearPad();
10860 // create a new pad because adjust the margins ...
10861 auto oldPad = gPad;
10862 gPad->Divide(1, 1, 1e-9, 1e-9);
10863 gPad->cd(1);
10864
10865 if (doHorizontal) {
10866 gPad->SetLeftMargin(0.4);
10867 } else {
10868 gPad->SetBottomMargin(0.4);
10869 }
10870
10871 auto pNamesHist = dynamic_cast<TH1F *>(graph->GetHistogram()->Clone("scales")); // used by interactive "pull" plot
10872 pNamesHist->Sumw2();
10873 pNamesHist->SetDirectory(nullptr);
10874
10875 for (int ii = 1; ii <= graph->GetN(); ii++) { // use graph->GetN() to protect against the 0 pars case
10876 auto _p = fr->floatParsFinal().find(_axis->GetBinLabel(ii));
10877 pNamesHist->SetBinContent(ii, offset[_p->GetName()]);
10878 pNamesHist->SetBinError(ii, scale[_p->GetName()]);
10879 _axis->SetBinLabel(ii, strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName());
10880 }
10881
10882 // hist->Draw(); -- now just draw the graph
10883
10884 if (!sOpt.Contains("impact")) {
10885 for (int ii = 2; ii >= 1; ii--) {
10886 auto pullBox = new TGraphErrors;
10887 pullBox->SetName(TString::Format("%dsigmaBand", ii));
10888 pullBox->SetBit(kCanDelete);
10889 pullBox->SetPoint(0, (doHorizontal) ? -ii : -0.5, (doHorizontal) ? -0.5 : 0);
10890 pullBox->SetPoint(1, (doHorizontal) ? ii : (_axis->GetNbins() - 0.5 - nUnconstrained),
10891 (doHorizontal) ? -0.5 : 0);
10892 pullBox->SetPointError(0, 0, (doHorizontal) ? (_axis->GetNbins() - nUnconstrained) : ii);
10893 pullBox->SetPointError(1, 0, (doHorizontal) ? (_axis->GetNbins() - nUnconstrained) : ii);
10894 pullBox->SetFillColor((ii == 2) ? kYellow : kGreen);
10895 hist->GetListOfFunctions()->Add(pullBox, "3"); // pullBox->Draw("3");
10896 }
10897 auto pullLine = new TGraph;
10898 pullLine->SetName("0sigmaLine");
10899 pullLine->SetBit(kCanDelete);
10900 pullLine->SetPoint(0, -0.5, 0);
10901 pullLine->SetPoint(1, _axis->GetNbins() - 0.5, 0);
10902 pullLine->SetLineStyle(2);
10903 pullLine->SetEditable(false);
10904 hist->GetListOfFunctions()->Add(pullLine, "l"); // pullLine->Draw("l");
10905
10906 // also draw vertical line separating constrained from unconstrained, if necessary
10907 if (nUnconstrained > 0) {
10908 pullLine = new TGraph;
10909 pullLine->SetName("dividerLine");
10910 pullLine->SetBit(kCanDelete);
10911 pullLine->SetPoint(0, graph->GetN() - 0.5 - nUnconstrained, -100);
10912 pullLine->SetPoint(1, graph->GetN() - 0.5 - nUnconstrained, 100);
10913 pullLine->SetLineStyle(2);
10914 pullLine->SetEditable(false);
10915 hist->GetListOfFunctions()->Add(pullLine, "l"); // pullLine->Draw("l");
10916 }
10917
10918 // and draw a pave with fr status info
10919 TPaveText *pave =
10920 new TPaveText(gPad->GetLeftMargin(), 1. - gPad->GetTopMargin(), 1. - gPad->GetRightMargin(), 0.98, "NDCNB");
10921 pave->SetFillStyle(0);
10922 pave->SetBorderSize(0);
10923 pave->SetMargin(0.);
10924 pave->SetName("status");
10925 pave->SetTextAlign(31);
10926 pave->AddText(TString::Format("minNLL: %g edm: %g", fr->minNll(), fr->edm()))
10927 ->SetTextColor((fr->status() == 3) ? kRed : kBlack);
10928 std::string covQualTxt;
10929 switch (fr->covQual()) {
10930 case -1: covQualTxt = "Unknown"; break;
10931 case 0: covQualTxt = "Not calculated"; break;
10932 case 1: covQualTxt = "Approximate"; break;
10933 case 2: covQualTxt = "Forced Positive-Definite"; break;
10934 case 3: covQualTxt = "Accurate"; break;
10935 }
10936 pave->AddText(TString::Format("Cov. Quality: %d (%s)", fr->covQual(), covQualTxt.c_str()))
10937 ->SetTextColor((fr->covQual() == 3) ? kBlack : kRed);
10938
10939 std::string statusCodes;
10940 for (unsigned int i = 0; i < fr->numStatusHistory(); i++) {
10941 statusCodes += TString::Format(" %s = %d", fr->statusLabelHistory(i), fr->statusCodeHistory(i));
10942 }
10943 pave->AddText(statusCodes.c_str())->SetTextColor(fr->status() == 0 ? kBlack : kRed);
10944
10945 hist->GetListOfFunctions()->Add(pave);
10946
10947 } else {
10948 gPad->SetTicks(0, 0); // ensure mirrored ticks aren't drawn in this pad
10949
10950 if (doHorizontal) {
10951 // ensure canvas height big enough
10952 if (int(gPad->GetCanvas()->GetWh()) < pNamesHist->GetNbinsX() * 15) {
10953 gPad->GetCanvas()->SetCanvasSize(gPad->GetCanvas()->GetWw(), pNamesHist->GetNbinsX() * 15);
10954 }
10955 }
10956
10957 double factor = 475. / gPad->GetCanvas()->GetWh(); // Wh is the full canvas height, not window height
10958 gPad->SetTopMargin(gStyle->GetPadTopMargin() * factor); // fixed margin height
10959 gPad->SetBottomMargin(gStyle->GetPadBottomMargin() * factor); // fixed margin height
10960
10961 TGaxis *axis =
10962 new TGaxis(_axis->GetXmin(), -4, _axis->GetXmin(), 4, -1.2 * maxImpact, 1.2 * maxImpact, 510, "-S");
10963
10964 if (doHorizontal) {
10965 // _axis->SetLabelSize(
10966 // (_axis->GetLabelFont() % 10 > 2)
10967 // ? (20 / factor)
10968 // : ((gPad->AbsPixeltoY(0) - gPad->AbsPixeltoY(20 / factor)) / (gPad->GetY2() -
10969 // gPad->GetY1())));
10970 // histCopy->GetXaxis()->SetTickLength(histCopy->GetXaxis()->GetTickLength() * factor);
10971 // hist->GetXaxis()->SetTickLength(hist->GetXaxis()->GetTickLength() * factor);
10972 // histCopy->GetYaxis()->SetTickLength(histCopy->GetYaxis()->GetTickLength() * factor);
10973 // hist->GetYaxis()->SetTickLength(hist->GetYaxis()->GetTickLength() * factor);
10974 // histCopy->GetXaxis()->SetTitleOffset(histCopy->GetXaxis()->GetTitleOffset() * factor);
10975 // histCopy->GetXaxis()->SetLabelOffset(histCopy->GetXaxis()->GetLabelOffset() * factor);
10976 // hist->GetXaxis()->SetTitleOffset(hist->GetXaxis()->GetTitleOffset() * factor);
10977 // hist->GetXaxis()->SetLabelOffset(hist->GetXaxis()->GetLabelOffset() * factor);
10978 // histCopy->GetXaxis()->SetTitleOffset(histCopy->GetXaxis()->GetTitleOffset() * factor);
10979 // histCopy->GetXaxis()->SetLabelOffset(histCopy->GetXaxis()->GetLabelOffset() * factor);
10980 }
10981 // copy attributes from TAxis to TGaxis
10982 axis->ImportAxisAttributes((doHorizontal) ? histCopy->GetXaxis() : histCopy->GetYaxis());
10983 axis->SetTitle(TString::Format("#Delta %s", fr->floatParsFinal().find(poiName.c_str())->GetTitle()));
10984
10985 // create impact bar charts
10986 for (int tt = 0; tt < 2; tt++) {
10987 auto impact = static_cast<TH1 *>(
10988 graph->GetHistogram()->Clone(TString::Format("%s_impact+", tt == 0 ? "prefit" : "postfit")));
10989 impact->SetDirectory(nullptr);
10990 impact->GetYaxis()->SetTitle(TString::Format("#Delta%s/#sigma", poiName.c_str()));
10991 impact->SetBarWidth(0.9);
10992 impact->SetBarOffset(0.05);
10993 impact->SetLineColor(kBlack);
10994 impact->SetFillColor(kAzure - 4);
10995 impact->SetFillStyle(tt == 0 ? 3013 : 1001);
10996 auto impact2 =
10997 static_cast<TH1 *>(impact->Clone(TString::Format("%s_impact-", tt == 0 ? "prefit" : "postfit")));
10998 impact2->SetDirectory(nullptr);
10999 impact2->SetFillColor(kCyan);
11000 for (int ii = 1; ii <= pNamesHist->GetNbinsX(); ii++) {
11001 for (auto &c : covariances) {
11002 if (c.second != pNamesHist->GetXaxis()->GetBinLabel(ii))
11003 continue;
11004 auto vv = dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(c.second.c_str()));
11005 auto vv_init = dynamic_cast<RooRealVar *>(fr->floatParsInit().find(c.second.c_str()));
11006 impact->SetBinContent(ii, ((tt == 0 && !vv_init->hasError()) || !vv->hasError())
11007 ? 0.
11008 : c.first * vv->getError() / vv->getErrorHi() *
11009 (tt == 0 ? (vv_init->getErrorHi() / vv->getErrorHi()) : 1.));
11010 impact2->SetBinContent(ii, ((tt == 0 && !vv_init->hasError()) || !vv->hasError())
11011 ? 0.
11012 : c.first * vv->getError() / vv->getErrorLo() *
11013 (tt == 0 ? (vv_init->getErrorLo() / vv->getErrorLo()) : 1.));
11014 }
11015 }
11016 hist->GetListOfFunctions()->Add(impact, (doHorizontal) ? "hbarsamemin0" : "bsamey+");
11017 hist->GetListOfFunctions()->Add(impact2, (doHorizontal) ? "hbarsamemin0" : "bsamey+");
11018 }
11019 // add three lines
11020 for (int ii = -1; ii <= 1; ii++) {
11021 auto pullLine = new TGraph;
11022 pullLine->SetName(TString::Format("%dsigmaLine", ii));
11023 pullLine->SetBit(kCanDelete);
11024 pullLine->SetPoint(0, -0.5, ii);
11025 pullLine->SetPoint(1, hist->GetNbinsY() - 0.5, ii);
11026 pullLine->SetLineStyle(2);
11027 pullLine->SetEditable(false);
11028 hist->GetListOfFunctions()->Add(pullLine, "l");
11029 }
11030 hist->GetListOfFunctions()->Add(axis); // draw axis last
11031 TLegend *leg1 =
11032 new TLegend(0.02, doHorizontal ? (1. - 0.22 * factor) : 0.02, 0.27, (doHorizontal ? 1. : 0.24));
11033 leg1->SetFillStyle(0);
11034 leg1->SetBorderSize(0);
11035 leg1->SetMargin(0.25);
11036 leg1->SetNColumns(2);
11037
11038 leg1->SetTextSize(_axis->GetLabelSize());
11039 leg1->SetTextFont(_axis->GetLabelFont());
11040 leg1->AddEntry((TObject *)nullptr, "Hessian Pre-fit", "");
11041 leg1->AddEntry((TObject *)nullptr, "Impact:", "");
11042 leg1->AddEntry(hist->FindObject("prefit_impact+"), "#theta = #hat{#theta}+#Delta#theta", "f");
11043 leg1->AddEntry(hist->FindObject("prefit_impact-"), "#theta = #hat{#theta}-#Delta#theta", "f");
11044 leg1->AddEntry((TObject *)nullptr, "Hessian Post-fit", "");
11045 leg1->AddEntry((TObject *)nullptr, "Impact:", "");
11046 leg1->AddEntry(hist->FindObject("postfit_impact+"), "#theta = #hat{#theta}+#Delta#theta", "f");
11047 leg1->AddEntry(hist->FindObject("postfit_impact-"), "#theta = #hat{#theta}-#Delta#theta", "f");
11048
11049 hist->GetListOfFunctions()->Add(leg1);
11050 if (gStyle->GetOptTitle()) {
11051 histCopy->SetBit(TH1::kNoTitle);
11052 TPaveText *title =
11053 new TPaveText(gPad->GetLeftMargin(), 1. - gPad->AbsPixeltoY(14), 1. - gPad->GetRightMargin(), 1., "NDC");
11054 title->ConvertNDCtoPad();
11055 title->SetY1NDC(1. - gPad->GetTopMargin() * 0.6);
11056 title->SetY2NDC(1);
11057 title->SetTextSize(
11058 (title->GetTextFont() % 10 > 2)
11059 ? (14 / factor)
11060 : ((gPad->AbsPixeltoY(0) - gPad->AbsPixeltoY(10 / factor)) / (gPad->GetY2() - gPad->GetY1())));
11061 title->SetFillStyle(0);
11062 title->SetBorderSize(0);
11063 title->AddText(histCopy->GetTitle());
11064 hist->GetListOfFunctions()->Add(title);
11065 }
11066 }
11067
11068 graph->SetEditable(false);
11069 pNamesHist->SetLineWidth(0);
11070 pNamesHist->SetMarkerSize(0);
11071 pNamesHist->SetMarkerStyle(0);
11072 graph->GetListOfFunctions()->Add(pNamesHist, "same"); // graph->SetHistogram(pNamesHist);
11073 if (doHorizontal) {
11074
11075 // flip the graph and contained graphs
11076 for (int p = 0; p < graph->GetN(); p++) {
11077 graph->SetPoint(p, graph->GetPointY(p), graph->GetPointX(p));
11078 graph->SetPointError(p, graph->GetErrorYlow(p), graph->GetErrorYhigh(p), graph->GetErrorXlow(p),
11079 graph->GetErrorXhigh(p));
11080 }
11081 for (auto f : *hist->GetListOfFunctions()) {
11082 if (f->InheritsFrom("TH1")) {
11083 // f->Draw("hbarsamemin0");
11084 } /*else if (auto g2 = dynamic_cast<TGraphErrors *>(f)) {
11085 for (int p = 0; p < g2->GetN(); p++) {
11086 g2->SetPoint(p, g2->GetPointY(p), g2->GetPointX(p));
11087 g2->SetPointError(p, g2->GetErrorY(p), _axis->GetNbins());
11088 }
11089 //g2->Draw("3");
11090 } */
11091 else if (auto g = dynamic_cast<TGraph *>(f)) {
11092 for (int p = 0; p < g->GetN(); p++) {
11093 g->SetPoint(p, g->GetPointY(p), g->GetPointX(p));
11094 }
11095 // g->Draw("l");
11096 } else if (auto l = dynamic_cast<TLine *>(f)) {
11097 l->SetX1(l->GetY1());
11098 l->SetX2(l->GetY2());
11099 l->SetY1(_axis->GetXmax());
11100 l->SetY2(_axis->GetXmax());
11101 // l->Draw();
11102 }
11103 }
11104 }
11105
11106 if (!sOpt.Contains("impact")) {
11107 // add labels to graph for unconstrained parameters
11108 for (size_t i = 0; i < ugraphLabels.size(); i++) {
11109 int bin = pNamesHist->GetNbinsX() - ugraphLabels.size() + i + 1;
11110 auto p = dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(pNamesHist->GetXaxis()->GetBinLabel(bin)));
11111 if (!p)
11112 continue;
11113 auto x = graph->GetPointX(graph->GetN() - ugraphLabels.size() + i);
11114 auto y = graph->GetPointY(graph->GetN() - ugraphLabels.size() + i) +
11115 graph->GetErrorYhigh(graph->GetN() - ugraphLabels.size() + i);
11116 auto l = xRooFit::matchPrecision({p->getVal(), p->getError()});
11117 auto t = new TLatex(x, y, TString::Format("%g #pm %g", l.first, l.second));
11118 t->SetBit(kCanDelete);
11119 t->SetTextSize(0.025);
11120 t->SetTextAngle(90);
11121 graph->GetListOfFunctions()->Add(t);
11122 }
11123 }
11124
11125 graph->SetName("pulls");
11126 hist->GetListOfFunctions()->Add(graph, "z0p");
11127 // hist->GetListOfFunctions()->Add(histCopy->Clone(".axis"),(sOpt.Contains("impact") &&
11128 // !doHorizontal)?"axissamey+":"axissame"); // doesn't display right when zoom the axis
11129 if (!hasSame) {
11130 histCopy->Draw((sOpt.Contains("impact") && !doHorizontal)
11131 ? "axisy+"
11132 : "axis"); // draws the axis, called ".axis" for easy access
11133 }
11134 hist->Draw("same");
11135 //
11136 // if(sOpt.Contains("impact")) {
11137 // // make main object the histogram
11138 // auto h = (TH1*)graph->GetHistogram()->Clone("impact");
11139 // graph->GetListOfFunctions()->RemoveAll();
11140 // for(int ii=1;ii<=h->GetNbinsX();ii++) h->SetBinContent(ii,-4);
11141 // h->GetListOfFunctions()->Add(graph,"z0p");
11142 // h->Draw("hbar");
11143 // } else {
11144 // graph->Draw(sOpt.Contains("impact") ? "az0py+" : "az0p");
11145 // }
11146 auto hh = dynamic_cast<TH1 *>(histCopy->Clone(".axiscopy"));
11147 hh->SetDirectory(nullptr);
11148 hh->SetBit(kCanDelete);
11149 hh->Draw(
11150 (sOpt.Contains("impact") && !doHorizontal)
11151 ? "axissamey+"
11152 : "axissame"); // overlay axis again -- important is last so can remove if don't pad->Update before reclear
11153 gPad->Modified();
11154 oldPad->cd();
11155 // gPad->Update();
11156 return;
11157 }
11158
11159 if (get()->InheritsFrom("RooAbsData")) {
11160 auto s = parentPdf();
11161 if (s && s->get<RooSimultaneous>()) {
11162 // drawing dataset associated to a simultaneous means must find subpads with variation names
11163 // may not have subpads if drawning a "Yield" plot ...
11164 bool doneDraw = false;
11165 for (auto c : s->bins()) {
11166 auto _pad = dynamic_cast<TPad *>(gPad->GetPrimitive(c->GetName()));
11167 if (!_pad)
11168 continue; // channel was hidden?
11169 // attach as a child before calling datasets(), so that if this dataset is external to workspace it is
11170 // included still attaching the dataset ensures dataset reduction for the channel is applied
11171 c->push_back(std::make_shared<xRooNode>(*this));
11172 auto ds = c->datasets().find(GetName());
11173 c->resize(c->size() - 1); // remove the child we attached
11174 if (!ds) {
11175 std::cout << " no ds " << GetName() << " - this should never happen!" << std::endl;
11176 continue;
11177 }
11178 auto tmp = gPad;
11179 _pad->cd();
11180 ds->Draw(opt);
11181 doneDraw = true;
11182 tmp->cd();
11183 }
11184 if (doneDraw) {
11185 gPad->Modified();
11186 return;
11187 }
11188 }
11189
11190 if (!s && hasSame) {
11191 // draw onto all subpads with = in the name
11192 // if has no such subpads, draw onto this pad
11193 bool doneDraw = false;
11194 for (auto o : *gPad->GetListOfPrimitives()) {
11195 if (auto p = dynamic_cast<TPad *>(o); p && TString(p->GetName()).Contains('=')) {
11196 auto _tmp = gPad;
11197 p->cd();
11198 Draw(opt);
11199 _tmp->cd();
11200 doneDraw = true;
11201 }
11202 }
11203 if (doneDraw) {
11204 gPad->Modified();
11205 return;
11206 }
11207 }
11208
11209 auto dataGraph = BuildGraph(v, false, (!s && hasSame) ? gPad : nullptr);
11210 if (!dataGraph)
11211 return;
11212
11213 dataGraph->SetBit(kCanDelete); // will be be deleted when pad is cleared
11214 dataGraph->SetMarkerSize(dataGraph->GetMarkerSize() * gPad->GetWNDC()); // scale marker sizes to pad size
11215
11216 if (s && !s->get<RooAbsPdf>()->canBeExtended()) {
11217 // normalize dataGraph to 1
11218 double tot = 0;
11219 for (int i = 0; i < dataGraph->GetN(); i++)
11220 tot += dataGraph->GetPointY(i);
11221 dataGraph->Scale(1. / tot);
11222 }
11223
11224 if (!hasSame) {
11225 clearPad();
11226 dataGraph->Draw("Az0p");
11227 addLegendEntry(dataGraph, strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(), "pEX0");
11228 gPad->Modified();
11229 // gPad->Update();
11230 return;
11231 }
11232
11233 bool noPoint = false;
11234 if (v && dynamic_cast<RooAbsArg *>(v)->getAttribute("global") && dataGraph->GetN() == 1) {
11235 // global observable ... if graph has only 1 data point line it up on the histogram value
11236 for (auto o : *gPad->GetListOfPrimitives()) {
11237 if (auto h = dynamic_cast<TH1 *>(o);
11238 h && strcmp(h->GetXaxis()->GetName(), dynamic_cast<TObject *>(v)->GetName()) == 0) {
11239 dataGraph->SetPointY(0, h->Interpolate(dataGraph->GetPointX(0)));
11240 noPoint = true;
11241 break;
11242 }
11243 }
11244 }
11245
11246 if (auto _pad = dynamic_cast<TPad *>(gPad->FindObject("auxPad")); _pad) {
11247 if (auto h = dynamic_cast<TH1 *>(_pad->GetPrimitive("auxHist")); h) {
11248 TString histName = h->GetTitle(); // split it by | char
11249 TString histType = histName(histName.Index('|') + 1, histName.Length());
11250 histName = histName(0, histName.Index('|'));
11251 if (auto mainHist = dynamic_cast<TH1 *>(gPad->GetPrimitive(histName));
11252 mainHist && auxFunctions.find(h->GetYaxis()->GetTitle()) != auxFunctions.end()) {
11253 // decide what to do based on title of auxHist (previously used name of y-axis but that changed axis
11254 // behaviour) use title instead
11255 auto ratioGraph = dynamic_cast<TGraphAsymmErrors *>(dataGraph->Clone(dataGraph->GetName()));
11256 ratioGraph->SetBit(kCanDelete);
11257 for (int i = 0; i < ratioGraph->GetN(); i++) {
11258 double val = ratioGraph->GetPointY(i);
11259 int binNum = mainHist->FindFixBin(ratioGraph->GetPointX(i));
11260 double nom = mainHist->GetBinContent(binNum);
11261 double nomerr = mainHist->GetBinError(binNum);
11262 double yval =
11263 std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(ratioGraph->GetPointY(i), nom, nomerr);
11264 double yup = std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(val + ratioGraph->GetErrorYhigh(i),
11265 nom, nomerr) -
11266 yval;
11267 double ydown = yval - std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(
11268 val - ratioGraph->GetErrorYlow(i), nom, nomerr);
11269 if (!std::isnan(yval)) {
11270 ratioGraph->SetPointY(i, yval);
11271 if (!std::isnan(yup))
11272 ratioGraph->SetPointEYhigh(i, yup);
11273 if (!std::isnan(ydown))
11274 ratioGraph->SetPointEYlow(i, ydown);
11275 }
11276 }
11277 // remove the zero points
11278 int i = 0;
11279 while (i < ratioGraph->GetN()) {
11280 if (ratioGraph->GetPointY(i) == 0 && ratioGraph->GetErrorYhigh(i) == 0 &&
11281 ratioGraph->GetErrorYlow(i) == 0) {
11282 ratioGraph->RemovePoint(i);
11283 } else {
11284 i++;
11285 }
11286 }
11287 auto _tmpPad = gPad;
11288 _pad->cd();
11289 ratioGraph->Draw("z0psame");
11291 adjustYRange(minMax.first, minMax.second, h, std::get<1>(auxFunctions[h->GetYaxis()->GetTitle()]));
11292 _tmpPad->cd();
11293 }
11294 }
11295 }
11296
11297 dataGraph->Draw("z0p same");
11298 addLegendEntry((noPoint) ? nullptr : dataGraph, strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(),
11299 noPoint ? "" : "pEX0");
11300
11301 auto minMax = graphMinMax(dynamic_cast<TGraphAsymmErrors *>(dataGraph));
11302 adjustYRange(minMax.first, minMax.second);
11303
11304 gPad->Modified();
11305 // gPad->Update();
11306 return;
11307 }
11308
11309 // auto _ax = GetXaxis();
11310 // auto v = (_ax) ? dynamic_cast<RooRealVar*>(/*possibleObs.first()*/_ax->GetParent()) : nullptr;
11311 // if (!v) { v = get<RooRealVar>(); } // self-axis
11312 // if (!v) return;
11313
11314 if (auto lv = get<RooAbsLValue>(); lv && fParent && fParent->get<RooAbsData>()) {
11315 // drawing an observable from a dataset ... build graph, and exit
11316 auto gr = fParent->BuildGraph(lv, true);
11318 gr->Draw(hasSame ? "P" : "AP");
11319 return;
11320 }
11321
11322 if (forceNames != "") {
11323 // drawing a force plot ... build nll and fill a histogram with force terms
11324 auto _dsets = datasets();
11325 bool _drawn = false;
11326 auto _coords = coords();
11327 auto _fr = fitResult();
11328 auto initPar = dynamic_cast<RooRealVar *>(_fr.get<RooFitResult>()->floatParsInit().find(forceNames));
11329 if (!initPar)
11330 return;
11331 std::vector<double> valuesToDo = {initPar->getVal()};
11332 if (initPar->hasError() || initPar->hasAsymError()) {
11333 valuesToDo.push_back(initPar->getVal() + initPar->getErrorLo());
11334 valuesToDo.push_back(initPar->getVal() + initPar->getErrorHi());
11335 }
11336 int ii = 0;
11337 for (auto valueToDo : valuesToDo) {
11338 ii++;
11339 for (auto &d : _dsets) {
11340 if (!d->get()->TestBit(1 << 20))
11341 continue;
11342 auto emptyHist = BuildHistogram(v, true);
11343 emptyHist->SetBit(kCanDelete);
11344 auto _obs = d->obs();
11345 auto x = _obs.find((v) ? dynamic_cast<TObject *>(v)->GetName() : emptyHist->GetXaxis()->GetName());
11346 auto _nll = nll(d);
11347 auto theData = d->get<RooAbsData>();
11348 int nevent = theData->numEntries();
11349 for (int i = 0; i < nevent; i++) {
11350 theData->get(i);
11351 bool _skip = false;
11352 for (const auto &_c : _coords) {
11353 if (auto cat = _c->get<RooAbsCategoryLValue>(); cat) {
11354 if (cat->getIndex() != theData->get()->getCatIndex(cat->GetName())) {
11355 _skip = true;
11356 break;
11357 }
11358 }
11359 }
11360 if (_skip)
11361 continue;
11362
11363 if (x) {
11364 auto val = _nll.pars()->getRealValue(initPar->GetName());
11365 if (ii > 1)
11366 _nll.pars()->setRealValue(initPar->GetName(), valueToDo);
11367 auto nllVal = _nll.getEntryVal(i);
11368 _nll.pars()->setRealValue(initPar->GetName(), initPar->getVal());
11369 auto nllVal2 = _nll.getEntryVal(i);
11370 _nll.pars()->setRealValue(initPar->GetName(), val);
11371 emptyHist->Fill(x->get<RooAbsReal>()->getVal(), (nllVal2 - nllVal));
11372 }
11373 }
11374 // include the extendedTerm, distributed evenly over the bins
11375 // probably should be somehow dependent on data density though (i.e. bins with more data get more of it?)
11376 auto val = _nll.pars()->getRealValue(initPar->GetName());
11377 if (ii > 1)
11378 _nll.pars()->setRealValue(initPar->GetName(), valueToDo);
11379 auto _extTerm = _nll.extendedTermVal();
11380 _nll.pars()->setRealValue(initPar->GetName(), initPar->getVal());
11381 auto _extTerm2 = _nll.extendedTermVal();
11382 _nll.pars()->setRealValue(initPar->GetName(), val);
11383 for (int i = 1; i <= emptyHist->GetNbinsX(); i++) {
11384 emptyHist->SetBinContent(i,
11385 emptyHist->GetBinContent(i) + (_extTerm2 - _extTerm) / emptyHist->GetNbinsX());
11386 emptyHist->SetBinError(i, 0);
11387 }
11388 emptyHist->GetYaxis()->SetTitle("log (L(#theta)/L(#theta_{0}))");
11389 emptyHist->SetTitle(TString::Format("#theta = %g", (ii > 1) ? valueToDo : val));
11390 if (ii == 1)
11391 emptyHist->SetLineColor(kBlack);
11392 if (ii == 2) {
11393 emptyHist->SetLineColor(kRed);
11394 } else if (ii == 3) {
11395 emptyHist->SetLineColor(kBlue);
11396 }
11397 emptyHist->Draw(_drawn ? "same" : "");
11398 _drawn = true;
11399 }
11400 }
11401 return;
11402 }
11403
11404 auto rar = get<RooAbsReal>();
11405 const xRooNode *rarNode = this;
11406 if (!rar) {
11407 // draw a deleteable clone of the object we wrap (since we might own the object)
11408 get()->DrawClone(opt);
11409 return;
11410 }
11411 // RooAbsReal *sf = nullptr;
11412 if (get()->InheritsFrom("RooExtendPdf")) {
11413 browse();
11414 rarNode = find(".pdf").get();
11415 // rar = rarNode->get<RooAbsReal>();
11416 // sf = find(".n")->get<RooAbsReal>();
11417 }
11418
11419 if (!nostack && !hasOverlay &&
11420 (rarNode->get()->InheritsFrom("RooRealSumPdf") || rarNode->get()->InheritsFrom("RooAddPdf") ||
11421 (v && rarNode->get()->InheritsFrom("RooSimultaneous") &&
11422 strcmp(dynamic_cast<TObject *>(v)->GetName(), rarNode->get<RooSimultaneous>()->indexCat().GetName()) == 0))) {
11423 nostack = false;
11424 } else {
11425 // in all other cases, we do not build a stack
11426 nostack = true;
11427 }
11428
11429 auto h = BuildHistogram(v, false, hasErrorOpt, 1, 0, "", false, false, 0, nullptr, nostack, true /*setInterp*/);
11430 if (!h) {
11431 if (get()) {
11432 // draw a deleteable clone of the object we wrap (since we might own the object)
11433 get()->DrawClone(opt);
11434 }
11435 return;
11436 }
11437 h->SetBit(kCanDelete);
11438
11439 if (!v)
11440 v = getObject<RooAbsLValue>(h->GetXaxis()->IsAlphanumeric() ? h->GetXaxis()->GetTimeFormatOnly()
11441 : h->GetXaxis()->GetName())
11442 .get();
11443 RooAbsArg *vv = (v) ? dynamic_cast<RooAbsArg *>(v) : rar;
11444 if (h->GetXaxis()->IsAlphanumeric()) {
11445 // do this to get bin labels
11446 h->GetXaxis()->SetName("xaxis"); // WARNING -- this messes up anywhere we GetXaxis()->GetName()
11447 }
11448
11449 // get style now, before we mess with histogram title
11450 // auto _styleNode = styles(h);
11451
11452 if (rar->InheritsFrom("RooAbsPdf") && !(rar->InheritsFrom("RooRealSumPdf") || rar->InheritsFrom("RooAddPdf") ||
11453 rar->InheritsFrom("RooSimultaneous"))) {
11454 // append parameter values to title if has such
11455 RooArgSet s;
11456 rar->leafNodeServerList(&s);
11457 if (v)
11458 s.remove(*dynamic_cast<RooAbsArg *>(v));
11459 if (!s.empty()) {
11460 TString ss = h->GetTitle();
11461 ss += " [";
11462 bool first = true;
11463 for (auto _p : s) {
11464 auto _v = dynamic_cast<RooRealVar *>(_p);
11465 if (!_v)
11466 continue;
11467 if (!first)
11468 ss += ",";
11469 first = false;
11470 ss += TString::Format("%s=%g", strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName(), _v->getVal());
11471 if (_v->hasError()) {
11472 ss += TString::Format("#pm %g", _v->getError());
11473 }
11474 }
11475 ss += "]";
11476 h->SetTitle(ss);
11477 }
11478 }
11479
11480 if (!hasSame) {
11481 if (obs().find(vv->GetName())) {
11482 gPad->SetGrid(0, 0);
11483 } else {
11484 gPad->SetGrid(1, 1);
11485 }
11486 }
11487 TString dOpt = h->GetOption();
11488 if (dOpt == "l")
11489 h->SetFillStyle(0);
11490 // // need to strip namespace to discount the "HistFactory" namespace classes from all being treated as binned
11491 // TString clNameNoNamespace = rar->ClassName();
11492 // clNameNoNamespace = clNameNoNamespace(clNameNoNamespace.Last(':') + 1, clNameNoNamespace.Length());
11493 // TString dOpt = (clNameNoNamespace.Contains("Hist") || vv->isCategory() || rar->isBinnedDistribution(*vv) ||
11494 // h->GetNbinsX() == 1 || rar->getAttribute("BinnedLikelihood") ||
11495 // (dynamic_cast<RooAbsRealLValue *>(vv) &&
11496 // std::unique_ptr<std::list<double>>(rar->binBoundaries(*dynamic_cast<RooAbsRealLValue *>(vv),
11497 // -std::numeric_limits<double>::infinity(),
11498 // std::numeric_limits<double>::infinity()))))
11499 // ? ""
11500 // : "LF2";
11501 // if (auto d = dynamic_cast<RooHistFunc *>(rar); d && !d->isBinnedDistribution(*vv) && h->GetNbinsX() != 1) {
11502 // dOpt = "LF2"; // hist func is interpolated, so draw it as such
11503 // }
11504 // if (dOpt == "LF2" && !components().empty()) {
11505 // // check if all components of dOpt are "Hist" type (CMS model support)
11506 // // if so then dOpt="";
11507 // bool allHist = true;
11508 // for (auto &s : components()) {
11509 // TString _clName = s->get()->ClassName();
11510 // _clName = _clName(_clName.Last(':') + 1, _clName.Length());
11511 // if (!(s->get() && _clName.Contains("Hist"))) {
11512 // allHist = false;
11513 // break;
11514 // }
11515 // }
11516 // if (allHist)
11517 // dOpt = "";
11518 // }
11519 //
11520 // if(dOpt=="LF2") {
11521 // // ensure any sub hists have lf2 option
11522 // TObjLink *lnk = h->GetListOfFunctions()->FirstLink();
11523 // while (lnk) {
11524 // if(auto hh = dynamic_cast<TH1*>(lnk->GetObject())) {
11525 // if(TString(hh->GetName())=="band" && TString(lnk->GetOption())=="e2same") {
11526 // lnk->SetOption("LF2 e3same");
11527 // } else if(TString(hh->GetName())=="nominal") {
11528 // lnk->SetOption("L same");
11529 // }
11530 // }
11531 // lnk = lnk->Next();
11532 // }
11533 // }
11534
11535 if (rar == vv && rar->IsA() == RooRealVar::Class()) {
11536 dOpt += "TEXT";
11537 // add a TExec to the histogram so that when edited it will propagate to var
11538 gROOT->SetEditHistograms(true);
11539 } else {
11540 gROOT->SetEditHistograms(false);
11541 }
11542
11543 if (hasSame) {
11544 dOpt += " same";
11545 } else {
11546 hAxis = h;
11547 }
11548
11549 if (dOpt.Contains("TEXT") || sOpt.Contains("text")) {
11550 // adjust marker size so text is good
11551 h->SetMarkerSize(gStyle->GetLabelSize("Z") / (0.02 * gPad->GetHNDC()));
11552 }
11553
11554 bool hasError(false);
11555 for (int i = 0; i < h->GetSumw2N(); i++) {
11556 if (h->GetSumw2()->At(i)) {
11557 hasError = true;
11558 break;
11559 }
11560 }
11561
11562 /** This doesn't seem necessary in at least 6.26 any more - pads seem adjusted on their own
11563 if (!hasSame && h->GetYaxis()->GetTitleFont()%10 == 2) {
11564 h->GetYaxis()->SetTitleOffset( gPad->GetLeftMargin() / gStyle->GetPadLeftMargin() );
11565 } */
11566 // don't this instead - dont want to leave as zero (auto) in case show aux plot
11567 if (!hasSame && h->GetYaxis()->GetTitleFont() % 10 == 2) {
11568 h->GetYaxis()->SetTitleOffset(1.);
11569 }
11570
11571 TH1 *errHist = nullptr;
11572
11573 if (!hasSame)
11574 clearPad();
11575
11576 if (rar == vv && rar->IsA() == RooRealVar::Class()) {
11577 // add a TExec to the histogram so that when edited it will propagate to var
11578 // h->GetListOfFunctions()->Add(h->Clone("self"),"TEXTHIST");
11579 dOpt = "TEXT";
11580 auto node = new xRooNode(*this);
11581 auto _hist = (errHist) ? errHist : h;
11582 auto hCopy = (errHist) ? nullptr : dynamic_cast<TH1 *>(h->Clone());
11583 if (hCopy) {
11584 hCopy->Reset();
11585 hCopy->Add(_hist);
11586 hCopy->SetDirectory(nullptr);
11587 }
11588 _hist->GetListOfFunctions()->Add(node);
11589 _hist->GetListOfFunctions()->Add(new TExec(
11590 ".update",
11592 "gROOT->SetEditHistograms(true);auto h = dynamic_cast<TH1*>(gPad->GetPrimitive(\"%s\")); if(h) { double "
11593 "range= h->GetMaximum()-h->GetMinimum(); if(auto n "
11594 "= dynamic_cast<xRooNode*>(h->GetListOfFunctions()->FindObject(\"%s\")); n && "
11595 "n->TestBit(TObject::kNotDeleted) && n->get<RooRealVar>()->getVal() != h->GetBinContent(1)) {"
11596 "h->SetBinContent(1, "
11597 "TString::Format(\"%%.2g\",int(h->GetBinContent(1)/(range*0.01))*range*0.01).Atof());n->SetContent( "
11598 "h->GetBinContent(1) ); for(auto pp : *h->GetListOfFunctions()) if(auto hh = "
11599 "dynamic_cast<TH1*>(pp))hh->SetBinContent(1,h->GetBinContent(1));} if(h->GetBinContent(1)==0.) "
11600 "h->SetBinContent(1,range*0.005); gPad->Modified();gPad->Update(); }",
11601 _hist->GetName(), node->GetName())));
11602 if (errHist) {
11603 errHist->GetListOfFunctions()->Add(h, "TEXT HIST same");
11604 errHist->SetFillColor(h->GetLineColor());
11605 } else {
11606 hCopy->SetBit(kCanDelete);
11607 hCopy->SetFillStyle(0);
11608 _hist->GetListOfFunctions()->Add(hCopy, "TEXT HIST same");
11609 //_hist->SetBinError(1, 0);
11610 }
11611 _hist->SetStats(false);
11612 // if (_hist->GetBinContent(1)==0.) _hist->SetBinContent(1,(_hist->GetMaximum()-_hist->GetMinimum())*0.005);
11613 _hist->Draw(); //_hist->Draw(((hasError) ? "e2" : ""));
11614 gPad->Modified();
11615 return;
11616 }
11617
11618 bool overlayExisted = false;
11619 if (hasOverlay) {
11620 h->SetName(TString::Format("%s%s", h->GetName(), overlayName.Data()));
11621 if (auto existing = dynamic_cast<TH1 *>(gPad->GetPrimitive(h->GetName())); existing) {
11622 existing->Reset();
11623 existing->Add(h);
11624 delete h;
11625 h = existing;
11626 overlayExisted = true;
11627 } else {
11628 TString oldStyle = (rar && rar->getStringAttribute("style")) ? rar->getStringAttribute("style") : "";
11629 h->SetTitle(overlayName);
11630 // for overlays will take style from current gStyle before overriding with personal style
11631 // this ensures initial style will be whatever gStyle is, rather than whatever ours is
11632 static_cast<TAttLine &>(*h) = *gStyle;
11633 static_cast<TAttFill &>(*h) = *gStyle;
11634 static_cast<TAttMarker &>(*h) = *gStyle;
11635 h->SetFillStyle(0); // explicit default for overlays will be transparent fill
11636
11637 // std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case
11638 // getObject has decided to return the owning ptr (for some reason) if
11639 // (!gROOT->GetStyle(h->GetTitle())) {
11640 // if ( (style = getObject<TStyle>(h->GetTitle())) ) {
11641 // // loaded style (from workspace?) so put in list and use that
11642 // gROOT->GetListOfStyles()->Add(style.get());
11643 // } else {
11644 // // create new style - gets put in style list automatically so don't have to delete
11645 // // acquire them so saved to workspaces for auto reload ...
11646 // style = acquireNew<TStyle>(h->GetTitle(),
11647 // TString::Format("Style for %s component", h->GetTitle()));
11648 // (TAttLine &) (*style) = *dynamic_cast<TAttLine *>(h);
11649 // (TAttFill &) (*style) = *dynamic_cast<TAttFill *>(h);
11650 // (TAttMarker &) (*style) = *dynamic_cast<TAttMarker *>(h);
11651 // gROOT->GetListOfStyles()->Add(style.get());
11652 // }
11653 // }
11654 // (TAttLine&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
11655 // (TAttFill&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
11656 // (TAttMarker&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
11657 auto _styleNode = styles(h);
11658 rar->setStringAttribute("style", oldStyle == "" ? nullptr : oldStyle.Data()); // restores old style
11659 if (auto _style = _styleNode.get<TStyle>()) {
11660 (TAttLine &)(*h) = *_style;
11661 (TAttFill &)(*h) = *_style;
11662 (TAttMarker &)(*h) = *_style;
11663 }
11664 h->Draw(dOpt == "LF2" ? "e3" : dOpt);
11665 if (errHist) {
11666 errHist->SetTitle(overlayName);
11667 (TAttLine &)(*errHist) = *h;
11668 errHist->SetFillColor(h->GetLineColor());
11669 }
11670 }
11671 } else {
11672 // if (auto _style = _styleNode.get<TStyle>()) {
11673 // (TAttLine &)(*h) = *_style;
11674 // (TAttFill &)(*h) = *_style;
11675 // (TAttMarker &)(*h) = *_style;
11676 // if (errHist) {
11677 // (TAttLine &)(*errHist) = *h;
11678 // errHist->SetFillColor(h->GetLineColor());
11679 // }
11680 // }
11681 h->Draw(dOpt);
11682 }
11683
11684 if (!hasOverlay && (rarNode->get()->InheritsFrom("RooRealSumPdf") || rarNode->get()->InheritsFrom("RooAddPdf") ||
11685 (rarNode->get()->InheritsFrom("RooSimultaneous") &&
11686 strcmp(vv->GetName(), rarNode->get<RooSimultaneous>()->indexCat().GetName()) == 0))) {
11687 if (auto stack = dynamic_cast<THStack *>(h->FindObject("stack"))) {
11688 // access the stack and set draw options, adjust ranges etc
11689 TObjLink *lnk = stack->GetHists()->FirstLink();
11690 while (lnk) {
11691 TH1 *hh = static_cast<TH1 *>(lnk->GetObject());
11692 // lnk->SetOption(dOpt); - not needed
11693 auto hhMin = (hh->GetMinimum() == 0) ? hh->GetMinimum(1e-9) : hh->GetMinimum();
11694 if (lnk == stack->GetHists()->FirstLink() && h->GetMinimum() > hhMin) {
11695 auto newMin = hhMin - (h->GetMaximum() - hhMin) * gStyle->GetHistTopMargin();
11696 if (hhMin >= 0 && newMin < 0)
11697 newMin = hhMin * 0.99;
11698 adjustYRange(newMin, h->GetMaximum());
11699 }
11700 addLegendEntry(hh, hh->GetTitle(), "f");
11701 lnk = lnk->Next();
11702 }
11703 }
11704
11705 // // build a stack unless not requested
11706 // if (!nostack) {
11707 // THStack *stack = new THStack(TString::Format("%s_stack", rar->GetName()),
11708 // TString::Format("%s;%s", rar->GetTitle(), h->GetXaxis()->GetTitle()));
11709 // int count = 2;
11710 // std::map<std::string, int> colorByTitle; // TODO: should fill from any existing legend
11711 // std::set<std::string> allTitles;
11712 // bool titleMatchName = true;
11713 // std::map<std::string, TH1 *> histGroups;
11714 // std::vector<TH1 *> hhs;
11715 // std::set<TH1 *> histsWithBadTitles; // these histograms will have their titles autoFormatted
11716 //
11717 // // support for CMS model case where has single component containing many coeffs
11718 // // will build stack by setting each coeff equal to 0 in turn, rebuilding the histogram
11719 // // the difference from the "full" histogram will be the component
11720 // RooArgList cms_coefs;
11721 // if (!rarNode->components().empty()) {
11722 // auto comps = rarNode->components()[0];
11723 // for (auto &c : *comps) {
11724 // if (c->fFolder == "!.coeffs")
11725 // cms_coefs.add(*c->get<RooAbsArg>());
11726 // }
11727 // }
11728 // if (!cms_coefs.empty()) {
11729 // RooRealVar zero("zero", "", 0);
11730 // std::shared_ptr<TH1> prevHist(static_cast<TH1 *>(h->Clone()));
11731 // for (auto c : cms_coefs) {
11732 // // seems I have to remake the function each time, as haven't figured out what cache needs
11733 // clearing? std::unique_ptr<RooAbsReal> f(
11734 // dynamic_cast<RooAbsReal *>(rarNode->components()[0]->get()->Clone("tmpCopy")));
11735 // zero.setAttribute(
11736 // Form("ORIGNAME:%s", c->GetName())); // used in redirectServers to say what this
11737 // replaces
11738 // f->redirectServers(RooArgSet(zero), false, true); // each time will replace one additional coef
11739 // // zero.setAttribute(Form("ORIGNAME:%s",c->GetName()),false); (commented out so that on next
11740 // iteration
11741 // // will still replace all prev)
11742 // auto hh = xRooNode(*f, *this).BuildHistogram(v);
11743 // hh->SetName(c->GetName());
11744 // if (sf)
11745 // hh->Scale(sf->getVal());
11746 // if (strlen(hh->GetTitle()) == 0) {
11747 // hh->SetTitle(c->GetName()); // ensure all hists has titles
11748 // histsWithBadTitles.insert(hh);
11749 // } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
11750 // histsWithBadTitles.insert(hh);
11751 // }
11752 // titleMatchName &= (TString(c->GetName()) == hh->GetTitle() ||
11753 // TString(hh->GetTitle()).BeginsWith(TString(c->GetName()) + "_"));
11754 // std::shared_ptr<TH1> nextHist(static_cast<TH1 *>(hh->Clone()));
11755 // hh->Add(prevHist.get(), -1.);
11756 // hh->Scale(-1.);
11757 // hhs.push_back(hh);
11758 // prevHist = nextHist;
11759 // }
11760 // } else if (get<RooSimultaneous>()) {
11761 // // need to create a histogram for each sample across all the channels - will rely on functionality
11762 // below to
11763 // // merge them based on titles
11764 //
11765 // for (auto &chan : bins()) {
11766 // TString chanName(chan->GetName());
11767 // chanName = chanName(chanName.Index("=") + 1, chanName.Length());
11768 // auto samps = chan->mainChild();
11769 // if (!samps)
11770 // samps = *chan;
11771 // for (auto &samp : samps.components()) {
11772 // auto hh = static_cast<TH1 *>(h->Clone(samp->GetName()));
11773 // hh->Reset();
11774 // hh->SetTitle(samp->GetTitle());
11775 // if (strlen(hh->GetTitle()) == 0) {
11776 // hh->SetTitle(samp->GetName());
11777 // histsWithBadTitles.insert(hh);
11778 // } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
11779 // histsWithBadTitles.insert(hh);
11780 // }
11781 // hh->SetTitle(TString(hh->GetTitle())
11782 // .ReplaceAll(TString(chan->get()->GetName()) + "_",
11783 // "")); // remove occurance of channelname_ in title (usually
11784 // prefix)
11785 // titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
11786 // TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
11787 // hh->SetBinContent(hh->GetXaxis()->FindFixBin(chanName), samp->GetContent());
11788 // hhs.push_back(hh);
11789 // }
11790 // }
11791 // } else {
11792 // for (auto &samp : rarNode->components()) {
11793 // auto hh = samp->BuildHistogram(v,false,false,1,0,"",false,false,0,h); // passing h to ensure
11794 // binning is the same for all subcomponent hists if (sf)
11795 // hh->Scale(sf->getVal());
11796 // hhs.push_back(hh);
11797 // if (strlen(hh->GetTitle()) == 0) {
11798 // hh->SetTitle(samp->GetName()); // ensure all hists has titles
11799 // histsWithBadTitles.insert(hh);
11800 // } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
11801 // histsWithBadTitles.insert(hh);
11802 // }
11803 // titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
11804 // TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
11805 // }
11806 // }
11807 //
11808 // if (!hhs.empty()) {
11809 // for (auto &hh : hhs) {
11810 // allTitles.insert(hh->GetTitle());
11811 // }
11812 //
11813 // // get common prefix to strip off only if all titles match names and
11814 // // any title is longer than 10 chars
11815 // size_t e = std::min(allTitles.begin()->size(), allTitles.rbegin()->size());
11816 // size_t ii = 0;
11817 // bool goodPrefix = false;
11818 // std::string commonSuffix;
11819 // if (titleMatchName && hhs.size() > 1) {
11820 // while (ii < e - 1 && allTitles.begin()->at(ii) == allTitles.rbegin()->at(ii)) {
11821 // ii++;
11822 // if (allTitles.begin()->at(ii) == '_' || allTitles.begin()->at(ii) == ' ')
11823 // goodPrefix = true;
11824 // }
11825 //
11826 // // find common suffix if there is one .. must start with a "_"
11827 // bool stop = false;
11828 // while (!stop && commonSuffix.size() < size_t(e - 1)) {
11829 // commonSuffix = allTitles.begin()->substr(allTitles.begin()->length() - commonSuffix.length() -
11830 // 1); for (auto &t : allTitles) {
11831 // if (!TString(t).EndsWith(commonSuffix.c_str())) {
11832 // commonSuffix = commonSuffix.substr(1);
11833 // stop = true;
11834 // break;
11835 // }
11836 // }
11837 // }
11838 // if (commonSuffix.find('_') == std::string::npos) {
11839 // commonSuffix = "";
11840 // } else {
11841 // commonSuffix = commonSuffix.substr(commonSuffix.find('_'));
11842 // }
11843 // }
11844 // if (!goodPrefix)
11845 // ii = 0;
11846 //
11847 // // also find how many characters are needed to distinguish all entries (that dont have the same
11848 // name)
11849 // // then carry on up to first space or underscore
11850 // size_t jj = 0;
11851 // std::map<std::string, std::string> reducedTitles;
11852 // while (reducedTitles.size() != allTitles.size()) {
11853 // jj++;
11854 // std::map<std::string, int> titlesMap;
11855 // for (auto &s : allTitles) {
11856 // if (reducedTitles.count(s))
11857 // continue;
11858 // titlesMap[s.substr(0, jj)]++;
11859 // }
11860 // for (auto &s : allTitles) {
11861 // if (titlesMap[s.substr(0, jj)] == 1 && (jj >= s.length() || s.at(jj) == ' ' || s.at(jj) ==
11862 // '_')) {
11863 // reducedTitles[s] = s.substr(0, jj);
11864 // }
11865 // }
11866 // }
11867 //
11868 // // strip common prefix and suffix before adding
11869 // for (auto ritr = hhs.rbegin(); ritr != hhs.rend(); ++ritr) { // go in reverse order
11870 // if (!histsWithBadTitles.count((*ritr))) {
11871 // continue;
11872 // }
11873 // auto _title = (hhs.size() > 5) ? reducedTitles[(*ritr)->GetTitle()] : (*ritr)->GetTitle();
11874 // _title = _title.substr(ii < _title.size() ? ii : 0);
11875 // if (!commonSuffix.empty() && TString(_title).EndsWith(commonSuffix.c_str()))
11876 // _title = _title.substr(0, _title.length() - commonSuffix.length());
11877 // (*ritr)->SetTitle(_title.c_str());
11878 // }
11879 // }
11880 //
11881 // for (auto &hh : hhs) {
11882 // // automatically group hists that all have the same title
11883 // if (histGroups.find(hh->GetTitle()) == histGroups.end()) {
11884 // histGroups[hh->GetTitle()] = hh;
11885 // } else {
11886 // // add it into this group
11887 // histGroups[hh->GetTitle()]->Add(hh);
11888 // delete hh;
11889 // hh = nullptr;
11890 // continue;
11891 // }
11892 // auto hhMin = (hh->GetMinimum() == 0) ? hh->GetMinimum(1e-9) : hh->GetMinimum();
11893 // if (!stack->GetHists() && h->GetMinimum() > hhMin) {
11894 // auto newMin = hhMin - (h->GetMaximum() - hhMin) * gStyle->GetHistTopMargin();
11895 // if (hhMin >= 0 && newMin < 0)
11896 // newMin = hhMin * 0.99;
11897 // adjustYRange(newMin, h->GetMaximum());
11898 // }
11899 //
11900 // /*if(stack->GetHists() && stack->GetHists()->GetEntries()>0) {
11901 // // to remove rounding effects on bin boundaries, see if binnings compatible
11902 // auto _h1 = dynamic_cast<TH1*>(stack->GetHists()->At(0));
11903 // if(_h1->GetNbinsX()==hh->GetNbinsX()) TODO ... finish dealing with silly rounding effects
11904 // }*/
11905 // TString thisOpt = dOpt;
11906 // // uncomment next line to blend continuous with discrete components .. get some unpleasant "poke
11907 // through"
11908 // // effects though
11909 // // if(auto s = samp->get<RooAbsReal>(); s) thisOpt =
11910 // s->isBinnedDistribution(*dynamic_cast<RooAbsArg*>(v)) ?
11911 // // "" : "LF2";
11912 // stack->Add(hh, thisOpt);
11913 // }
11914 // stack->SetBit(kCanDelete); // should delete its sub histograms
11915 // stack->Draw("noclear same");
11916 // h->Draw(
11917 // dOpt + sOpt +
11918 // "same"); // overlay again .. if stack would cover original hist (negative components) we still see
11919 // integral
11920 // h->Draw("axissame"); // redraws axis
11921 //
11922 // TList *ll = stack->GetHists();
11923 // if (ll && ll->GetEntries()) {
11924 //
11925 // // finally, ensure all hists are styled
11926 // for (auto ho : *ll) {
11927 // TH1 *hh = dynamic_cast<TH1 *>(ho);
11928 // if (!hh)
11929 // continue;
11930 // bool createdStyle = (xRooNode(*hh, *this).styles(nullptr, false).get<TStyle>() == nullptr);
11931 //
11932 // if (createdStyle) {
11933 // // give hist a color, that isn't the same as any other hists color
11934 // hh->SetFillStyle(1001); // solid fill style
11935 // bool used = false;
11936 // do {
11937 // hh->SetFillColor((count++));
11938 // // check not already used this color
11939 // used = false;
11940 // for (auto ho2 : *ll) {
11941 // TH1 *hh2 = dynamic_cast<TH1 *>(ho2);
11942 // if (!hh2)
11943 // continue;
11944 // auto _styleNode = xRooNode(*hh2, *this).styles(hh2, false);
11945 // auto _style = _styleNode.get<TStyle>();
11946 // if (hh != hh2 && _style && _style->GetFillColor() == hh->GetFillColor()) {
11947 // used = true;
11948 // break;
11949 // }
11950 // }
11951 // } while (used);
11952 // }
11953 //
11954 // auto _styleNode = xRooNode(*hh, *this).styles(hh);
11955 // if (auto _style = _styleNode.get<TStyle>()) {
11956 // *dynamic_cast<TAttLine *>(hh) = *_style;
11957 // *dynamic_cast<TAttFill *>(hh) = *_style;
11958 // *dynamic_cast<TAttMarker *>(hh) = *_style;
11959 // }
11960 // // for stacks, fill color of white should be color 10 unless fill style is 0
11961 // if (hh->GetFillColor() == kWhite && hh->GetFillStyle() != 0) {
11962 // // kWhite means 'transparent' in ROOT ... should really use a FillStyle of 0 for that
11963 // // so assume user wanted actual white, which is color 10
11964 // hh->SetFillColor(10);
11965 // }
11966 // addLegendEntry(hh, hh->GetTitle(), "f");
11967 // }
11968 // }
11969 // }
11970 } else if (!overlayExisted) {
11971
11972 if (errHist) {
11973 addLegendEntry(errHist, strlen(errHist->GetTitle()) ? errHist->GetTitle() : GetName(), "fl");
11974 } else {
11975 addLegendEntry(h, strlen(h->GetTitle()) ? h->GetTitle() : GetName(), (hasError) ? "fl" : "l");
11976 }
11977 }
11978
11979 if (errHist) {
11980 dOpt.ReplaceAll("TEXT", "");
11981 errHist->Draw(dOpt + (dOpt.Contains("LF2") ? "e3same" : "e2same"));
11982 double ymax = -std::numeric_limits<double>::infinity();
11983 double ymin = std::numeric_limits<double>::infinity();
11984 for (int i = 1; i <= errHist->GetNbinsX(); i++) {
11985 ymax = std::max(ymax, errHist->GetBinContent(i) + errHist->GetBinError(i));
11986 ymin = std::min(ymin, errHist->GetBinContent(i) - errHist->GetBinError(i));
11987 }
11989 } else {
11990 adjustYRange(h->GetMinimum() * 0.9, h->GetMaximum() * 1.1);
11991 }
11992
11993 if ((!auxPlotTitle.empty()) && !hasSame) {
11994 // create a pad for the ratio ... shift the bottom margin of this pad to make space for it
11995 double padFrac = 0.3;
11996 auto _tmpPad = gPad;
11997 gPad->SetBottomMargin(padFrac);
11998 auto ratioPad = new TPad("auxPad", "aux plot", 0, 0, 1, padFrac);
11999 ratioPad->SetFillColor(_tmpPad->GetFillColor());
12000 ratioPad->SetNumber(1);
12001 ratioPad->SetBottomMargin(ratioPad->GetBottomMargin() / padFrac);
12002 ratioPad->SetTopMargin(0.04);
12003 ratioPad->SetLeftMargin(gPad->GetLeftMargin());
12004 ratioPad->SetRightMargin(gPad->GetRightMargin());
12005 ratioPad->cd();
12006 TH1 *ratioHist = dynamic_cast<TH1 *>((errHist) ? errHist->Clone("auxHist") : h->Clone("auxHist"));
12007 ratioHist->Reset();
12008 ratioHist->Add(h); // removes function list
12009 ratioHist->SetDirectory(nullptr);
12010 ratioHist->SetTitle((errHist) ? errHist->GetName()
12011 : h->GetName()); // abuse the title string to hold the name of the main hist
12012
12013 ratioHist->GetYaxis()->SetNdivisions(5, 0, 0);
12014 ratioHist->GetYaxis()->SetTitle(auxPlotTitle.c_str());
12015 ratioHist->SetTitle(
12016 TString::Format("%s|%s", ratioHist->GetTitle(),
12017 auxPlotTitle.c_str())); // used when plotting data (above) to decide what to calculate
12018 ratioHist->SetMaximum();
12019 ratioHist->SetMinimum(); // resets min and max
12020 ratioPad->SetGridy();
12021
12022 for (int i = 1; i <= ratioHist->GetNbinsX(); i++) {
12023 double val = ratioHist->GetBinContent(i);
12024 double err = ratioHist->GetBinError(i);
12025 ratioHist->SetBinContent(i, std::get<0>(auxFunctions[auxPlotTitle])(val, val, err));
12026 ratioHist->SetBinError(i, std::get<0>(auxFunctions[auxPlotTitle])(val + err, val, err) -
12027 ratioHist->GetBinContent(i));
12028 }
12029
12030 double rHeight = (1. - padFrac) / padFrac; //(_tmpPad->GetWNDC())/(gPad->GetHNDC());
12031 if (ratioHist->GetYaxis()->GetTitleFont() % 10 == 2) {
12032 ratioHist->GetYaxis()->SetTitleSize(ratioHist->GetYaxis()->GetTitleSize() * rHeight);
12033 ratioHist->GetYaxis()->SetLabelSize(ratioHist->GetYaxis()->GetLabelSize() * rHeight);
12034 ratioHist->GetXaxis()->SetTitleSize(ratioHist->GetXaxis()->GetTitleSize() * rHeight);
12035 ratioHist->GetXaxis()->SetLabelSize(ratioHist->GetXaxis()->GetLabelSize() * rHeight);
12036 ratioHist->GetYaxis()->SetTitleOffset(ratioHist->GetYaxis()->GetTitleOffset() / rHeight);
12037 } else {
12038#if ROOT_VERSION_CODE < ROOT_VERSION(6, 26, 00)
12039 ratioHist->GetYaxis()->SetTitleOffset(ratioHist->GetYaxis()->GetTitleOffset() / rHeight);
12040#endif
12041 }
12042 ratioHist->GetXaxis()->SetTickLength(ratioHist->GetXaxis()->GetTickLength() * rHeight);
12043 ratioHist->SetStats(false);
12044 ratioHist->SetBit(TH1::kNoTitle);
12045 ratioHist->SetBit(kCanDelete);
12046 if (errHist) {
12047 auto _h = dynamic_cast<TH1 *>(ratioHist->Clone("auxHist_clone"));
12048 _h->SetDirectory(nullptr);
12049 _h->SetFillColor(0);
12050 ratioHist->GetListOfFunctions()->Add(_h, "histsame");
12051 //_h->Draw("histsame");
12052 }
12053 ratioHist->GetListOfFunctions()->Add(new TExec(
12054 ".updateAxis",
12055 TString::Format("auto h1 = (TH1*)%p; auto h2 = (TH1*)%p; if(h2->GetXaxis()->GetFirst() != "
12056 "h1->GetXaxis()->GetFirst() || h1->GetXaxis()->GetLast()!=h2->GetXaxis()->GetLast()) "
12057 "{h2->GetXaxis()->SetRange(h1->GetXaxis()->GetFirst(),h1->GetXaxis()->GetLast());if(gPad) "
12058 "{gPad->GetCanvas()->Paint();gPad->GetCanvas()->Update();}}",
12059 (void *)ratioHist, (void *)(h))));
12060 ratioHist->Draw((errHist ? "e2" : ""));
12061
12062 _tmpPad->cd();
12063 ratioPad->Draw();
12064 } else if (auto ratioPad = dynamic_cast<TPad *>(gPad->GetPrimitive("auxPad")); hasSame && ratioPad) {
12065 // need to draw histogram in the ratio pad ...
12066 // if doing overlay need to update histogram
12067
12068 if (auto hr = dynamic_cast<TH1 *>(ratioPad->GetPrimitive("auxHist"));
12069 hr && auxFunctions.find(hr->GetYaxis()->GetTitle()) != auxFunctions.end()) {
12070 TString histName = hr->GetTitle(); // split it by | char
12071 TString histType = histName(histName.Index('|') + 1, histName.Length());
12072 histName = histName(0, histName.Index('|'));
12073
12074 if (auto hnom = dynamic_cast<TH1 *>(gPad->GetPrimitive(histName)); hnom) {
12075 h = dynamic_cast<TH1 *>(h->Clone(h->GetName()));
12076 h->SetDirectory(nullptr);
12077 h->SetBit(kCanDelete);
12078 for (int i = 1; i <= hnom->GetNbinsX(); i++) {
12079 double val = h->GetBinContent(i);
12080 double err = h->GetBinError(i);
12081 h->SetBinContent(i, std::get<0>(auxFunctions[hr->GetYaxis()->GetTitle()])(
12082 h->GetBinContent(i), hnom->GetBinContent(i), hnom->GetBinError(i)));
12083 h->SetBinError(i, std::get<0>(auxFunctions[hr->GetYaxis()->GetTitle()])(
12084 val + err, hnom->GetBinContent(i), hnom->GetBinError(i)) -
12085 h->GetBinContent(i));
12086 }
12087 auto _tmpPad = gPad;
12088 ratioPad->cd();
12089 if (hasOverlay) {
12090 if (auto existing = dynamic_cast<TH1 *>(ratioPad->GetPrimitive(h->GetName())); existing) {
12091 existing->Reset();
12092 existing->Add(h);
12093 delete h;
12094 h = existing;
12095 overlayExisted = true;
12096 } else {
12097 h->Draw(dOpt);
12098 }
12099 } else {
12100 h->Draw(dOpt);
12101 }
12102 double ymax = -std::numeric_limits<double>::infinity();
12103 double ymin = std::numeric_limits<double>::infinity();
12104 for (int i = 1; i <= h->GetNbinsX(); i++) {
12105 ymax = std::max(ymax, h->GetBinContent(i) + h->GetBinError(i));
12106 ymin = std::min(ymin, h->GetBinContent(i) - h->GetBinError(i));
12107 }
12108 adjustYRange(ymin, ymax, hr, std::get<1>(auxFunctions[hr->GetYaxis()->GetTitle()]));
12109 // adjustYRange(h->GetMinimum() * (h->GetMinimum()<0 ? 1.1 : 0.9), h->GetMaximum() * (h->GetMinimum()<0 ?
12110 // 0.9 : 1.1), hr, std::get<1>(auxFunctions[hr->GetYaxis()->GetTitle()]));
12111 gPad->Modified();
12112 _tmpPad->cd();
12113 }
12114 }
12115 }
12116
12117 // see if it's in a simultaneous so need to select a cat
12118 /*auto _parent = fParent;
12119 auto _me = rar;
12120 while(_parent) {
12121 if (auto s = _parent->get<RooSimultaneous>(); s) {
12122 for (auto c : s->indexCat()) {
12123 if (auto p = s->getPdf(c.first.c_str());_me==p) {
12124 gPad->SetName(c.first.c_str());
12125 break;
12126 }
12127 }
12128 break;
12129 }
12130 _me = _parent->get<RooAbsReal>();
12131 _parent = _parent->fParent;
12132 }*/
12133
12134 // now draw selected datasets on top if this was a pdf
12135 if (auto _pdf = get<RooAbsPdf>();
12136 !hasSame && _pdf /*&& (_pdf->canBeExtended() || robs().empty())*/ && coefs(true).empty()) {
12137 auto _dsets = datasets();
12138 // bool _drawn=false;
12139 for (auto &d : _dsets) {
12140 if (d->get()->TestBit(1 << 20)) {
12141 d->Draw("same");
12142 //_drawn=true;
12143 }
12144 }
12145 // if (!_drawn && !_dsets.empty()) _dsets[0]->Draw("same"); // always draw if has a dataset
12146 }
12147
12148 gPad->Modified();
12149 // gPad->Update();
12150 getLegend();
12151 gPad->Modified();
12152 // gPad->Update();
12153}
12154
12155void xRooNode::SaveAs(const char *filename, Option_t *option) const
12156{
12158 sOpt.ToLower();
12161 if (sFilename.Contains(".root:")) {
12162 objName = TString(sFilename(sFilename.Index(".root:") + 6, sFilename.Length()));
12163 sFilename = sFilename(0, sFilename.Index(".root:") + 5);
12164 }
12165
12166 if (auto pdf = get<RooAbsPdf>(); pdf) {
12167 // if saving a pdf, will put it inside a workspace, with its datasets, and a modelconfig
12168 // then save the workspace
12169 RooWorkspace w(objName, TString::Format("Workspace of %s", GetTitle()));
12170 xRooNode ws(w);
12171 auto addedPdf = ws.Add(*this);
12172 for (auto ds : datasets()) {
12173 ws.Add(*ds);
12174 }
12175 RooStats::ModelConfig mc("ModelConfig", GetTitle(), &w);
12176 mc.SetPdf(addedPdf->GetName());
12177 mc.SetObservables(*addedPdf.robs().get<RooArgList>());
12178 mc.SetGlobalObservables(*addedPdf.globs().get<RooArgList>());
12179 mc.SetNuisanceParameters(*addedPdf.np().get<RooArgList>());
12180 mc.SetParametersOfInterest(*addedPdf.poi().get<RooArgList>());
12181 ws.Add(mc);
12182
12183 // save the workspace
12185
12186 } else if (auto w = get<RooWorkspace>(); w) {
12187 // ensure the current color set is saved in the workspace
12188 w->import(*gROOT->GetListOfColors(), true);
12189
12190 if (sFilename.EndsWith(".json")) {
12191#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
12192 // stream with json tool
12194 if (tool.exportJSON(sFilename.Data())) {
12195 Info("SaveAs", "%s saved to %s", w->GetName(), sFilename.Data());
12196 } else {
12197 Error("SaveAs", "Unable to save to %s", sFilename.Data());
12198 }
12199#else
12200 Error("SaveAs", "json format workspaces only in ROOT 6.26 onwards");
12201#endif
12202 return;
12203 }
12204
12205#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
12206 // before saving, clear the eocache of all owned nodes
12207 // because causes memory leak when read back in (workspace streamer immediately overwrites the caches)
12208 // fixed in: https://github.com/root-project/root/pull/12024
12209 for (auto &c : w->components()) {
12210 c->_eocache = nullptr;
12211 }
12212#endif
12213 // const_cast<Node2*>(this)->sterilize(); - tried this to reduce mem leak on readback but no improve
12214 if (!w->writeToFile(sFilename, sOpt != "update")) {
12215 Info("SaveAs", "%s saved to %s", w->GetName(), sFilename.Data());
12216 // save any fitDatabase that is loaded in memory too
12217 // TODO: We should do this as well for SaveAs on a scan object
12218 std::function<void(TDirectory *, TDirectory *)> CopyDir;
12219
12221 auto dir = dest->GetDirectory(source->GetName());
12222 if (!dir) {
12223 dir = dest->mkdir(source->GetName());
12224 }
12225 for (auto k : *source->GetListOfKeys()) {
12226 auto key = dynamic_cast<TKey *>(k);
12227 const char *classname = key->GetClassName();
12228 TClass *cl = gROOT->GetClass(classname);
12229 // std::cout << "processing " << key->GetName() << " " << classname << std::endl;
12230 if (!cl) {
12231 continue;
12232 } else if (cl->InheritsFrom(TDirectory::Class())) {
12233 CopyDir(source->GetDirectory(key->GetName()), dir);
12234 } else {
12235 // don't write object if it already exists
12236 if (dir->FindKey(key->GetName()))
12237 continue;
12238 // support FitConfigs ....
12239 if (strcmp(classname, "ROOT::Fit::FitConfig") == 0) {
12240 auto fc = key->ReadObject<ROOT::Fit::FitConfig>();
12241 dir->WriteObject(fc, key->GetName());
12242 delete fc;
12243 } else {
12244 TObject *obj = key->ReadObj();
12245 if (obj) {
12246 dir->WriteTObject(obj, key->GetName());
12247 delete obj;
12248 }
12249 }
12250 }
12251 }
12252 };
12253 if (gROOT->GetListOfFiles()) {
12254 for (auto key : *gROOT->GetListOfFiles()) {
12255 if (auto fitDb = dynamic_cast<TMemFile *>(key);
12256 fitDb /*&& TString(key->GetName()).BeginsWith("fitDatabase_")*/) {
12257 CopyDir(fitDb, std::make_unique<TFile>(sFilename, "UPDATE").get());
12258 Info("SaveAs", "Saved %s to %s", fitDb->GetName(), sFilename.Data());
12259 }
12260 }
12261 }
12262 } else {
12263 Error("SaveAs", "Unable to save to %s", sFilename.Data());
12264 }
12265#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
12266 // restore the cache to every node
12267 for (auto &c : w->components()) {
12268 c->setExpensiveObjectCache(w->expensiveObjectCache());
12269 }
12270#endif
12271 }
12272}
12273
12274double xRooNode::GetBinError(int bin, const xRooNode &fr, int nToys, bool errorsHi, bool errorsLo) const
12275{
12276 auto res = GetBinErrors(bin, bin, fr, nToys, errorsHi, errorsLo);
12277 if (res.empty())
12278 return std::numeric_limits<double>::quiet_NaN();
12279 return res.at(0);
12280}
12281
12282std::vector<double> xRooNode::contents() const
12283{
12284 std::vector<double> out;
12285 out.reserve(size());
12286 for (auto child : *this) {
12287 out.push_back(child->GetContent());
12288 }
12289 return out;
12290}
12291
12293{
12294
12295 auto _fr = fr.get<RooFitResult>();
12296
12297 if (!_fr) {
12298 return covariances(fitResult());
12299 }
12300
12301 auto rho = _fr->correlationMatrix();
12302
12303 TMatrixDSym out(size());
12304
12305 auto _pars = pars();
12306
12307 // 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]
12308 // - nu[theta_n_down]) consistent with propagatedError formula
12309
12310 for (int m = 0; m < rho.GetNrows(); m++) {
12311 auto p_m = dynamic_cast<RooRealVar *>(_fr->floatParsFinal().at(m));
12312 if (!p_m)
12313 continue; // skip categoricals
12314 auto _p = dynamic_cast<RooAbsRealLValue *>(_pars.get<RooArgList>()->find(p_m->GetName()));
12315 if (!_p)
12316 continue;
12317 auto tmp = _p->getVal();
12318 _p->setVal(p_m->getVal() + p_m->getErrorHi());
12319 auto nu_m = contents();
12320 _p->setVal(p_m->getVal() + p_m->getErrorLo());
12321 auto nu_m2 = contents();
12322 _p->setVal(tmp);
12323 for (int n = 0; n < rho.GetNrows(); n++) {
12324 auto p_n = dynamic_cast<RooRealVar *>(_fr->floatParsFinal().at(n));
12325 if (!p_n)
12326 continue; // skip categoricals
12327 auto _p2 = dynamic_cast<RooAbsRealLValue *>(_pars.get<RooArgList>()->find(p_n->GetName()));
12328 if (!_p2)
12329 continue;
12330 auto tmp2 = _p2->getVal();
12331 _p2->setVal(p_n->getVal() + p_n->getErrorHi());
12332 auto nu_n = (p_n == p_m) ? nu_m : contents();
12333 _p2->setVal(p_n->getVal() + p_n->getErrorLo());
12334 auto nu_n2 = (p_n == p_m) ? nu_m2 : contents();
12335 _p2->setVal(tmp2);
12336 for (int i = 0; i < out.GetNrows(); i++) {
12337 for (int j = 0; j < out.GetNrows(); j++) {
12338 out(i, j) += 0.25 * (nu_m[i] - nu_m2[i]) * rho(m, n) * (nu_n[j] - nu_n2[j]);
12339 }
12340 }
12341 }
12342 }
12343 return out;
12344}
12345
12346std::pair<double, double>
12347xRooNode::IntegralAndError(const xRooNode &fr, const char *rangeName, int nToys, bool errorsHi, bool errorsLo) const
12348{
12349 double out = 1.;
12350 double err = std::numeric_limits<double>::quiet_NaN();
12351
12352 std::unique_ptr<RooAbsCollection> _snap;
12354 if (auto _fr = fr.get<RooFitResult>()) {
12355 _pars.add(pars().argList());
12356 _snap.reset(_pars.snapshot());
12357 _pars = _fr->floatParsFinal();
12358 _pars = _fr->constPars();
12359 }
12360
12361 auto _obs = obs();
12362 RooArgSet sobs(*_obs.get<RooArgList>()); // need to make explicit RooArgSet for ROOT 6.36 onwards
12363 auto _coefs = coefs(); // need here to keep alive owned RooProduct
12364 if (auto c = _coefs.get<RooAbsReal>(); c) {
12365 out = c->getVal(sobs); // assumes independent of observables!
12366 }
12367
12368 if (auto p = dynamic_cast<RooAbsPdf *>(get()); p) {
12369 // prefer to use expectedEvents for integrals of RooAbsPdf e.g. for RooProdPdf wont include constraint terms
12370 if (rangeName)
12371 p->setNormRange(rangeName);
12372#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 27, 00)
12374#endif
12375 out *= p->expectedEvents(*_obs.get<RooArgList>());
12376#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
12377 // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
12378 p->_normSet = nullptr;
12379#endif
12380 err = GetBinError(-1, fr, nToys, errorsHi, errorsLo);
12381 if (rangeName)
12382 p->setNormRange(nullptr);
12383 } else if (auto p2 = dynamic_cast<RooAbsReal *>(get()); p2) {
12384 // only integrate over observables we actually depend on
12385 auto f = std::shared_ptr<RooAbsReal>(
12386 p2->createIntegral(*std::unique_ptr<RooArgSet>(p2->getObservables(*_obs.get<RooArgList>())),
12387 rangeName)); // did use x here before using obs
12388 RooProduct pr("int_x_coef", "int_x_coef",
12389 RooArgList(*f, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>()));
12390 out *= f->getVal();
12391 err = xRooNode(pr, *this).GetBinError(-1, fr, nToys, errorsHi, errorsLo);
12392 sterilize(); // needed so that we can forget properly about the integral we just created (and are deleting)
12393 } else if (get<RooAbsData>()) {
12394 out = 0;
12395 auto vals = GetBinContents(1, 0); // returns all bins
12396 auto ax = (rangeName) ? GetXaxis() : nullptr;
12397 auto rv = (ax) ? dynamic_cast<RooRealVar *>(ax->GetParent()) : nullptr;
12398 auto cv = (ax && !rv) ? dynamic_cast<RooCategory *>(ax->GetParent()) : nullptr;
12399 int i = 0;
12400 for (auto &v : vals) {
12401 i++;
12402 if (rangeName) {
12403 if (rv && !rv->inRange(ax->GetBinCenter(i), rangeName))
12404 continue;
12405 if (cv && !cv->isStateInRange(rangeName, ax->GetBinLabel(i)))
12406 continue;
12407 }
12408 out += v;
12409 }
12410 err = 0; // should this be sqrt(sum(v^2)) or something similar
12411 } else {
12412 out = std::numeric_limits<double>::quiet_NaN();
12413 }
12414 if (_snap) {
12415 _pars.RooAbsCollection::operator=(*_snap);
12416 }
12417 return std::make_pair(out, err);
12418}
12419
12420std::vector<double>
12421xRooNode::GetBinErrors(int binStart, int binEnd, const xRooNode &_fr, int nToys, bool errorHi, bool errorLo) const
12422{
12423 // note: so far this method is inconsistent with the BuildHistogram in ways:
12424 // no projection over other variables
12425 // July2023: made RooRealSumPdf evaluate as a function if doesn't have a floor
12426 // but this method will still evaluate it as a pdf (uses PdfWrapper)
12427 // but can get away with it while added NaN recovery to getSimplePropagatedError to pickup raw values
12428
12429 if (fBinNumber != -1) {
12430 if (binStart != binEnd || !fParent) {
12431 throw std::runtime_error(TString::Format("%s is a bin - only has one value", GetName()));
12432 }
12433 return fParent->GetBinErrors(fBinNumber, fBinNumber, _fr, nToys, errorHi, errorLo);
12434 }
12435
12436 std::vector<double> out;
12437
12438 auto _hist = BuildHistogram(nullptr, true, true, binStart, binEnd, _fr, errorHi, errorLo, nToys);
12439 if (!_hist)
12440 return out;
12441 if (binEnd == 0) {
12442 binEnd = _hist->GetNbinsX();
12443 } else if (binEnd == binStart && binEnd == -1) {
12444 binStart = 1;
12445 binEnd = 1; // done an integral, so histogram has only 1 bin
12446 }
12447 for (int bin = binStart; bin <= binEnd; bin++) {
12448 out.push_back(((errorLo && !errorHi) ? (-1.) : 1.) *
12449 _hist->GetBinError(bin)); // using same convention as RooFit that Lo errors are negative
12450 }
12451 delete _hist;
12452 return out;
12453
12454 // auto o = dynamic_cast<RooAbsReal *>(get());
12455 // if (!o)
12456 // return out;
12457 //
12458 // std::shared_ptr<RooFitResult> fr = std::dynamic_pointer_cast<RooFitResult>(_fr.fComp);
12459 // //= dynamic_cast<RooFitResult*>( _fr.get<RooFitResult>() ? _fr->Clone() : fitResult()->Clone());
12460 //
12461 // auto _coefs = coefs();
12462 //
12463 // if (!fr) {
12464 // // need to ensure coefs, if any, are included in fit result retrieval so all pars are loaded
12465 // auto frn = (!_coefs.get() ? *this : xRooNode(RooProduct("tmp", "tmp", RooArgList(*o,
12466 // *_coefs.get<RooAbsReal>()))))
12467 // .fitResult();
12468 // if (strlen(_fr.GetName()))
12469 // frn = frn.reduced(_fr.GetName());
12470 //
12471 // // use name to reduce the fit result, if one given
12472 // fr = std::dynamic_pointer_cast<RooFitResult>(frn.fComp);
12473 // }
12474 //
12475 // if (!GETDMP(fr.get(), _finalPars)) {
12476 // fr->setFinalParList(RooArgList());
12477 // }
12478 //
12479 // /// Oct2022: No longer doing this because want to allow fitResult to be used to get partial error
12480 // // // need to add any floating parameters not included somewhere already in the fit result ...
12481 // // RooArgList l;
12482 // // for(auto& p : pars()) {
12483 // // auto v = p->get<RooRealVar>();
12484 // // if (!v) continue;
12485 // // if (v->isConstant()) continue;
12486 // // if (fr->floatParsFinal().find(v->GetName())) continue;
12487 // // if (fr->_constPars && fr->_constPars->find(v->GetName())) continue;
12488 // // l.add(*v);
12489 // // }
12490 // //
12491 // // if (!l.empty()) {
12492 // // RooArgList l2; l2.addClone(fr->floatParsFinal());
12493 // // l2.addClone(l);
12494 // // fr->setFinalParList(l2);
12495 // // }
12496 //
12497 // TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr.get(), _VM));
12498 //
12499 // if (!prevCov || size_t(prevCov->GetNcols()) < fr->floatParsFinal().size()) {
12500 // TMatrixDSym cov(fr->floatParsFinal().size());
12501 // if (prevCov) {
12502 // for (int i = 0; i < prevCov->GetNcols(); i++) {
12503 // for (int j = 0; j < prevCov->GetNrows(); j++) {
12504 // cov(i, j) = (*prevCov)(i, j);
12505 // }
12506 // }
12507 // }
12508 // int i = 0;
12509 // for (auto &p : fr->floatParsFinal()) {
12510 // if (!prevCov || i >= prevCov->GetNcols()) {
12511 // cov(i, i) = pow(dynamic_cast<RooRealVar *>(p)->getError(), 2);
12512 // }
12513 // i++;
12514 // }
12515 // int covQualBackup = fr->covQual();
12516 // fr->setCovarianceMatrix(cov);
12517 // fr->setCovQual(covQualBackup);
12518 // }
12519 //
12520 // bool doBinWidth = false;
12521 // auto ax = (binStart == -1 && binEnd == -1) ? nullptr : GetXaxis();
12522 //
12523 // auto _obs = obs(); // may own an obs so keep alive here
12524 // RooArgList normSet = _obs.argList();
12525 // // to give consistency with BuildHistogram method, should be only the axis var if defined
12526 // if (ax) {
12527 // normSet.clear();
12528 // normSet.add(*dynamic_cast<RooAbsArg *>(ax->GetParent()));
12529 // }
12530 //
12531 // if (auto p = dynamic_cast<RooAbsPdf *>(o); ax && (p || _coefs.get() || o->getAttribute("density"))) {
12532 // // pdfs of samples embedded in a sumpdf (aka have a coef) will convert their density value to a content
12533 // doBinWidth = true;
12534 // }
12535 // if (binEnd == 0) {
12536 // if (ax) {
12537 // binEnd = ax->GetNbins();
12538 // } else {
12539 // binEnd = binStart;
12540 // }
12541 // }
12542 // for (int bin = binStart; bin <= binEnd; bin++) {
12543 // if (ax)
12544 // dynamic_cast<RooAbsLValue *>(ax->GetParent())->setBin(bin - 1, ax->GetName());
12545 // // if (!SetBin(bin)) { return out; }
12546 //
12547 // double res;
12548 // if (auto p = dynamic_cast<RooAbsPdf *>(o); p) {
12549 // // fr->covarianceMatrix().Print();
12550 // res = PdfWrapper(*p, _coefs.get<RooAbsReal>(), !ax).getSimplePropagatedError(*fr, normSet);
12551 // #if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
12552 // // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
12553 // p->_normSet = nullptr;
12554 // #endif
12555 // } else {
12556 // // res = o->getPropagatedError(*fr, normSet);
12557 // // // TODO: What if coef has error? - probably need a FuncWrapper class
12558 // // if (auto c = _coefs.get<RooAbsReal>(); c) {
12559 // // res *= c->getVal(normSet);
12560 // // }
12561 // res = RooProduct("errorEval", "errorEval",
12562 // RooArgList(*o, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>()))
12563 // .getPropagatedError(*fr, normSet);
12564 // }
12565 // if (doBinWidth) {
12566 // res *= ax->GetBinWidth(bin);
12567 // }
12568 // out.push_back(res);
12569 // }
12570 //
12571 // return out;
12572}
12573
12574std::string cling::printValue(const xRooNode *v)
12575{
12576 if (!v)
12577 return "nullptr\n";
12578 if (!v->empty()) {
12579 std::string out;
12580 size_t left = v->size();
12581 for (auto n : *v) {
12582 left--;
12583 if (!out.empty())
12584 out += ",";
12585 else
12586 out += "{";
12587 out += n->GetName();
12588 if (out.length() > 100 && left > 0) {
12589 out += TString::Format(",... and %lu more", left);
12590 break;
12591 }
12592 }
12593 out += "}\n";
12594 out = std::string(Form("<%s> %s", v->get() ? v->get()->ClassName() : "nullptr", v->GetName())) + out;
12595 return out;
12596 }
12597 std::string out;
12598 if (!(*v)) {
12599 return "<nullptr>";
12600 } else {
12601 return Form("<%s> %s", v->get() ? v->get()->ClassName() : "nullptr", v->GetName());
12602 }
12603
12604 return out;
12605}
12606
@ 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
Option string (const char)
Definition RtypesCore.h:80
@ kGray
Definition Rtypes.h:66
@ kRed
Definition Rtypes.h:67
@ kBlack
Definition Rtypes.h:66
@ kGreen
Definition Rtypes.h:67
@ kWhite
Definition Rtypes.h:66
@ kCyan
Definition Rtypes.h:67
@ kBlue
Definition Rtypes.h:67
@ kAzure
Definition Rtypes.h:68
@ kYellow
Definition Rtypes.h:67
@ kP10Blue
Definition Rtypes.h:72
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:252
#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:372
#define gROOT
Definition TROOT.h:414
char * Form(const char *fmt,...)
Formats a string in a circular formatting buffer.
Definition TString.cxx:2495
R__EXTERN TStyle * gStyle
Definition TStyle.h:442
R__EXTERN TSystem * gSystem
Definition TSystem.h:582
#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:955
double GetBinUpEdge(Int_t bin) const override
Return up edge of bin.
Definition xRooNode.cxx:963
Int_t FindFixBin(double x) const override
Find bin number corresponding to abscissa x
void Set(Int_t nbins, const double *xbins) override
Initialize axis with variable bins.
Definition xRooNode.cxx:983
RooAbsRealLValue * rvar() const
void SetTitle(const char *title) override
Set the title of the TNamed.
Definition xRooNode.cxx:974
void Set(Int_t nbins, double xmin, double xmax) override
Initialize axis with fix bins.
Definition xRooNode.cxx:996
Int_t FindFixBin(const char *label) const override
Find bin number with label.
RooAbsLValue * var() const
const RooAbsBinning * binning() const
void Set(Int_t nbins, const float *xbins) override
Initialize axis with variable bins.
Definition xRooNode.cxx:989
const char * GetTitle() const override
Returns title of object.
Definition xRooNode.cxx:970
double GetBinWidth(Int_t bin) const override
Return bin width.
Definition xRooNode.cxx:949
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:2013
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, int tsType=0)
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:531
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 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
std::string fRange
! only here so can have char* GetRange return so can return nullptr for no range set !...
Definition xRooNode.h:515
void _fit_(const char *constParValues="", const char *options="GoF")
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:545
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
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.
double GetError(const xRooNode &fr="", int nToys=0, bool errorsHi=false, bool errorsLo=false) const
Definition xRooNode.h:419
std::shared_ptr< xRooNode > fProvider
! like a parent but only for use by getObject
Definition xRooNode.h:524
std::shared_ptr< TStyle > style(TObject *initObject=nullptr, bool autoCreate=true) const
xRooNode fitResult(const char *opt="") const
std::shared_ptr< TAxis > fXAxis
! appears that if was fXaxis then dialog box for SetXaxis will take as current value
Definition xRooNode.h:519
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:934
double GetBinData(int bin, const xRooNode &data="obsData")
bool SetBinError(int bin, double value)
bool SetContents(const TObject &obj)
Definition xRooNode.h:349
std::shared_ptr< TObject > fComp
!
Definition xRooNode.h:506
std::pair< double, double > IntegralAndError(const xRooNode &fr="", const char *rangeName=nullptr, int nToys=0, bool errorsHi=false, bool errorsLo=false) const
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
xRooNode np() const
List of nuisance parameters: non-constant parameters that are not marked of interest,...
xRooNode Combine(const xRooNode &rhs, bool silent=false)
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:391
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
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:509
xRooNode robs() const
List of regular observables of this node.
TClass * IsA() const override
Definition xRooNode.h:537
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:532
void _SetAttribute_(const char *name, const char *value=nullptr)
Definition xRooNode.cxx:509
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:660
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:222
std::shared_ptr< TObject > convertForAcquisition(xRooNode &acquirer, const char *opt="") const
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:49
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:76
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.
TObject * Clone(const char *newname=nullptr) const override
Make a clone of an object using the Streamer facility.
Definition RooAbsArg.h:88
virtual bool isFundamental() const
Is this object a fundamental type that can be added to a dataset? Fundamental-type subclasses overrid...
Definition RooAbsArg.h:175
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...
std::string contentsString() const
Return comma separated list of contained object names as STL string.
RooAbsArg * find(const char *name) const
Find object with given name in list.
Abstract base class for binned and unbinned datasets.
Definition RooAbsData.h:56
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:274
Abstract base class for objects that are lvalues, i.e.
Abstract interface for all probability density functions.
Definition RooAbsPdf.h:32
bool canBeExtended() const
If true, PDF can provide extended likelihood term.
Definition RooAbsPdf.h:214
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:326
Abstract base class for objects that represent a real value and implements functionality common to al...
Definition RooAbsReal.h:63
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:107
virtual void forceNumInt(bool flag=true)
Definition RooAbsReal.h:175
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:32
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:61
static TClass * Class()
bool hasError(bool allowZero=true) const
Definition RooRealVar.h:60
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.
< A class that holds configuration information for a model using a workspace as a store
Definition ModelConfig.h:34
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:214
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 Size_t GetMarkerSize() const
Return the marker size.
Definition TAttMarker.h:34
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:667
virtual void SetBinLabel(Int_t bin, const char *label)
Set label for bin.
Definition TAxis.cxx:891
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:422
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:1514
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:4901
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:2973
static Int_t GetColorPalette(Int_t i)
Static function returning the color number i in current palette.
Definition TColor.cxx:1509
static void InitializeColors()
Initialize colors used by the TCanvas based graphics (via TColor objects).
Definition TColor.cxx:1172
static Int_t GetNumberOfColors()
Static function returning number of colors in the color palette.
Definition TColor.cxx:1529
TDirectory::TContext keeps track and restore the current directory.
Definition TDirectory.h:89
Describe directory structure in memory.
Definition TDirectory.h:45
static TClass * Class()
virtual Int_t GetValue(const char *name, Int_t dflt) const
Returns the integer value for a resource.
Definition TEnv.cxx:503
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:744
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:182
A ROOT file is an on-disk file, usually with extension .root, that stores objects in a file-system-li...
Definition TFile.h:130
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:197
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:2927
virtual void ImportAxisAttributes(TAxis *axis)
Internal method to import TAxis attributes to this TGaxis.
Definition TGaxis.cxx:954
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:131
void SetName(const char *name="") override
Set graph name.
Definition TGraph.cxx:2426
void Draw(Option_t *chopt="") override
Draw this graph with its current attributes.
Definition TGraph.cxx:859
virtual Double_t GetPointY(Int_t i) const
Get y value for point i.
Definition TGraph.cxx:1585
1-D histogram with a double per channel (see TH1 documentation)
Definition TH1.h:926
1-D histogram with a float per channel (see TH1 documentation)
Definition TH1.h:878
TH1 is the base class of all histogram classes in ROOT.
Definition TH1.h:109
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:9053
void SetTitle(const char *title) override
Change/set the title.
Definition TH1.cxx:6815
virtual Int_t GetNbinsY() const
Definition TH1.h:542
@ kNoTitle
Don't draw the histogram title.
Definition TH1.h:408
TAxis * GetXaxis()
Definition TH1.h:571
TObject * FindObject(const char *name) const override
Search object named name in the list of functions.
Definition TH1.cxx:3891
virtual void SetMaximum(Double_t maximum=-1111)
Definition TH1.h:652
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:819
TAxis * GetYaxis()
Definition TH1.h:572
void Draw(Option_t *option="") override
Draw this histogram with options.
Definition TH1.cxx:3076
virtual void SetMinimum(Double_t minimum=-1111)
Definition TH1.h:653
TList * GetListOfFunctions() const
Definition TH1.h:488
virtual void Scale(Double_t c1=1, Option_t *option="")
Multiply this histogram by a constant c1.
Definition TH1.cxx:6698
TObject * Clone(const char *newname="") const override
Make a complete copy of the underlying object.
Definition TH1.cxx:2764
virtual void SetStats(Bool_t stats=kTRUE)
Set statistics option on/off.
Definition TH1.cxx:9106
2-D histogram with a double per channel (see TH1 documentation)
Definition TH2.h:400
Service class for 2-D histogram classes.
Definition TH2.h:30
Int_t Fill(Double_t) override
Invalid Fill method.
Definition TH2.cxx:363
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:364
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:20
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:70
virtual void SetX2(Double_t x2)
Definition TLine.h:68
Double_t GetY1() const
Definition TLine.h:52
virtual void SetX1(Double_t x1)
Definition TLine.h:67
virtual void SetY1(Double_t y1)
Definition TLine.h:69
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:532
void Add(TObject *obj) override
Definition TList.h:81
A TMemFile is like a normal TFile except that it reads and writes only from memory.
Definition TMemFile.h:27
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:73
virtual void SetTitle(const char *title="")
Set the title of the TNamed.
Definition TNamed.cxx:173
const char * GetName() const override
Returns name of object.
Definition TNamed.h:49
const char * GetTitle() const override
Returns title of object.
Definition TNamed.h:50
virtual void SetName(const char *name)
Set the name of the TNamed.
Definition TNamed.cxx:149
virtual void SetNameTitle(const char *name, const char *title)
Set all the TNamed parameters (name and title).
Definition TNamed.cxx:163
Mother of all ROOT objects.
Definition TObject.h:42
virtual void Inspect() const
Dump contents of this object in a graphics canvas.
Definition TObject.cxx:565
virtual const char * GetName() const
Returns name of object.
Definition TObject.cxx:458
virtual TObject * Clone(const char *newname="") const
Make a clone of an object using the Streamer facility.
Definition TObject.cxx:242
virtual const char * ClassName() const
Returns name of class to which the object belongs.
Definition TObject.cxx:226
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
Definition TObject.cxx:1075
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:319
virtual TObject * FindObject(const char *name) const
Must be redefined in derived classes.
Definition TObject.cxx:422
virtual void SaveAs(const char *filename="", Option_t *option="") const
Save this object in the file specified by filename.
Definition TObject.cxx:706
virtual void Delete(Option_t *option="")
Delete this object.
Definition TObject.cxx:267
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition TObject.cxx:882
virtual Bool_t InheritsFrom(const char *classname) const
Returns kTRUE if object inherits from class "classname".
Definition TObject.cxx:544
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:1089
virtual const char * GetTitle() const
Returns title of object.
Definition TObject.cxx:502
virtual void Draw(Option_t *option="")
Default Draw method for all objects.
Definition TObject.cxx:293
virtual void Print(Option_t *option="") const
This method must be overridden when a class wants to print itself.
Definition TObject.cxx:656
@ kCanDelete
if object in a list can be deleted
Definition TObject.h:70
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition TObject.cxx:1063
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:86
virtual void ConvertNDCtoPad()
Convert pave coordinates from NDC to Pad coordinates.
Definition TPave.cxx:138
virtual void SetBorderSize(Int_t bordersize=4)
Sets the border size of the TPave box and shadow.
Definition TPave.h:79
virtual void SetY2NDC(Double_t y2)
Definition TPave.h:87
Define 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:138
Ssiz_t Length() const
Definition TString.h:425
Bool_t IsDec() const
Returns true if all characters in string are decimal digits (0-9).
Definition TString.cxx:1946
Int_t Atoi() const
Return integer value of string.
Definition TString.cxx:1994
Bool_t EndsWith(const char *pat, ECaseCompare cmp=kExact) const
Return true if string ends with the specified string.
Definition TString.cxx:2250
Double_t Atof() const
Return floating-point value contained in string.
Definition TString.cxx:2060
Bool_t IsFloat() const
Returns kTRUE if string contains a floating point or integer number.
Definition TString.cxx:1864
const char * Data() const
Definition TString.h:384
Bool_t IsAlpha() const
Returns true if all characters in string are alphabetic.
Definition TString.cxx:1804
Bool_t BeginsWith(const char *s, ECaseCompare cmp=kExact) const
Definition TString.h:632
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:2384
Bool_t Contains(const char *pat, ECaseCompare cmp=kExact) const
Definition TString.h:641
Ssiz_t Index(const char *pat, Ssiz_t i=0, ECaseCompare cmp=kExact) const
Definition TString.h:660
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:1146
Float_t GetPadRightMargin() const
Definition TStyle.h:216
Style_t GetTitleFont(Option_t *axis="X") const
Return title font.
Definition TStyle.cxx:1217
Bool_t GetHistMinimumZero() const
Definition TStyle.h:239
Float_t GetPadLeftMargin() const
Definition TStyle.h:215
Float_t GetTitleYSize() const
Definition TStyle.h:281
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:1285
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:1307
virtual Bool_t ProcessEvents()
Process pending events (GUI, timers, sockets).
Definition TSystem.cxx:414
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:570
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
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
return c2
Definition legend2.C:14
#define F(x, y, z)
double gaussian_cdf(double x, double sigma=1, double x0=0)
Alternative name for same function.
CoordSystem::Scalar get(DisplacementVector2D< CoordSystem, Tag > const &p)
bool EndsWith(std::string_view string, std::string_view suffix)
TClass * GetClass(T *)
Definition TClass.h:679
The namespace RooFit contains mostly switches that change the behaviour of functions of PDFs (or othe...
Definition CodegenImpl.h:71
static constexpr auto NumIntegration
Alias of MsgLevel::NumericIntegration for backwards compatibility.
MsgLevel
Verbosity level for RooMsgService::StreamConfig in RooMsgService.
@ NumericIntegration
constexpr Double_t C()
Velocity of light in .
Definition TMath.h:117
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:2196
Double_t StdDev(Long64_t n, const T *a, const Double_t *w=nullptr)
Definition TMath.h:534
#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:2338
#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:229
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)