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 std::shared_ptr<RooAbsArg> _func;
2087 // a null node .. so create either a new RooProduct or RooHistFunc if has observables (or no deps but has
2088 // x-axis)
2089 auto _obs = robs();
2090 if (!_obs.empty() || GetXaxis()) {
2091 if (_obs.empty()) {
2092 // using X axis to construct hist
2093 auto _ax = dynamic_cast<Axis2 *>(GetXaxis());
2094 auto t = TH1::AddDirectoryStatus();
2095 TH1::AddDirectory(false);
2096 auto h =
2097 std::make_unique<TH1D>(child.GetName(), child.GetTitle(), _ax->GetNbins(), _ax->binning()->array());
2099 h->GetXaxis()->SetName(TString::Format("%s;%s", _ax->GetParent()->GetName(), _ax->GetName()));
2100 // technically convertForAcquisition has already acquired so no need to re-acquire but should be harmless
2101 _func = std::dynamic_pointer_cast<RooAbsArg>(acquire(xRooNode(*h).convertForAcquisition(*this)));
2102 } else if (_obs.size() == 1) {
2103 // use the single obs to make a TH1D
2104 auto _x = _obs.at(0)->get<RooAbsLValue>();
2105 auto _bnames = _x->getBinningNames();
2106 TString binningName = p->getStringAttribute("binning");
2107 for (auto &b : _bnames) {
2108 if (b == p->GetName()) {
2109 binningName = p->GetName();
2110 break;
2111 }
2112 }
2113 auto t = TH1::AddDirectoryStatus();
2114 TH1::AddDirectory(false);
2115 auto h = std::make_unique<TH1D>(child.GetName(), child.GetTitle(), _x->numBins(binningName),
2116 _x->getBinningPtr(binningName)->array());
2118 h->GetXaxis()->SetName(
2119 TString::Format("%s;%s", dynamic_cast<TObject *>(_x)->GetName(), binningName.Data()));
2120 // technically convertForAcquisition has already acquired so no need to re-acquire but should be harmless
2121 _func = std::dynamic_pointer_cast<RooAbsArg>(acquire(xRooNode(*h).convertForAcquisition(*this)));
2122 Info("Add", "Created SimpleDensity factor %s (xaxis=%s) for %s", _func->GetName(), _obs.at(0)->GetName(),
2123 p->GetName());
2124 } else {
2125 throw std::runtime_error("Unsupported creation of new component in SumPdf for this many obs");
2126 }
2127 } else {
2128 _func = acquireNew<RooProduct>(TString::Format("%s_%s", p->GetName(), child.GetName()), child.GetTitle(),
2129 RooArgList());
2130 }
2131 _func->setStringAttribute("alias", child.GetName());
2132 out = _func;
2133 }
2134
2135 if (auto _f = std::dynamic_pointer_cast<RooHistFunc>(
2136 (child.get<RooProduct>()) ? child.factors()[child.GetName()]->fComp : out);
2137 _f) {
2138 // adding a histfunc directly to a sumpdf, should be a density
2139 _f->setAttribute("density");
2140 if (_f->getAttribute("autodensity")) {
2141 // need to divide by bin widths first
2142 for (int i = 0; i < _f->dataHist().numEntries(); i++) {
2143 auto bin_pars = _f->dataHist().get(i);
2144 _f->dataHist().set(*bin_pars, _f->dataHist().weight() / _f->dataHist().binVolume(*bin_pars));
2145 }
2146 _f->setAttribute("autodensity", false);
2147 _f->setValueDirty();
2148 }
2149
2150 // promote the axis vars to observables
2151 // can't use original child as might refer to unacquired deps
2152 for (auto &x : xRooNode("tmp", _f).vars()) {
2153 x->get<RooAbsArg>()->setAttribute("obs");
2154 }
2155 if (isConverted) {
2156 Info("Add", "Created %s factor RooHistFunc::%s for %s",
2157 _f->getAttribute("density") ? "SimpleDensity" : "Simple", _f->GetName(), p->GetName());
2158 }
2159 }
2160
2161 if (auto _p = std::dynamic_pointer_cast<RooAbsPdf>(out); _p) {
2162 // adding a pdf to a RooRealSumPdf will replace it with a RooAddPdf and put the RooRealSumPdf inside that
2163 // if pdf is extended will use in the "no coefficients" state, where the expectedEvents are taking from
2164 // the pdf integrals
2165 TString newName(p->GetName());
2166 newName.ReplaceAll("_samples", "");
2167 newName += "_components";
2168 Warning("Add", "converting samples to components");
2169
2170 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
2171 _p->dependsOn(*static_cast<RooAbsArg *>(_ax->GetParent()))) {
2172
2173 if (auto _boundaries = std::unique_ptr<std::list<double>>(_p->binBoundaries(
2174 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
2175 std::numeric_limits<double>::infinity()));
2176 !_boundaries && _ax->GetNbins() > 0) {
2177#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
2178 Warning("Add", "Adding unbinned pdf %s to binned %s - will wrap with RooBinSamplingPdf(...)",
2179 _p->GetName(), GetName());
2180 _p = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _p->GetName()), _p->GetTitle(),
2181 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *_p);
2182 _p->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
2183 if (!_p->getStringAttribute("alias"))
2184 _p->setStringAttribute("alias", out->GetName());
2185#else
2186 throw std::runtime_error(
2187 "unsupported addition of unbinned pdf to binned model - please upgrade to at least ROOT 6.24");
2188#endif
2189 }
2190 }
2191
2192 // require to be extended to be in coefficient-free mode ...
2193 // otherwise would lose the integral of the sumPdf (can't think of way to have a coef be the integral)
2194 if (!_p->canBeExtended()) {
2195 _p = acquireNew<RooExtendPdf>(TString::Format("%s_extended", _p->GetName()), _p->GetTitle(), *_p,
2196 *acquire2<RooAbsReal, RooRealVar>("1", "1", 1));
2197 }
2198
2199 return *(Replace(*acquireNew<RooAddPdf>(newName, _p->GetTitle(), RooArgList(*p, *_p)))
2200 .browse()[1]); // returns second node.
2201 }
2202
2203 if (auto _f = std::dynamic_pointer_cast<RooAbsReal>(out); _f) {
2204
2205 // todo: if adding a pdf, should actually replace RooRealSumPdf with a RooAddPdf and put
2206 // the sumPdf and *this* pdf inside that pdf
2207 // only exception is the binSamplingPdf below to integrate unbinned functions across bins
2208
2209 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
2210 _f->dependsOn(*static_cast<RooAbsArg *>(_ax->GetParent()))) {
2211
2212 if (auto _boundaries = std::unique_ptr<std::list<double>>(_f->binBoundaries(
2213 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
2214 std::numeric_limits<double>::infinity()));
2215 !_boundaries && _ax->GetNbins() > 0) {
2216#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
2217 Warning(
2218 "Add",
2219 "Adding unbinned function %s to binned %s - will wrap with RooRealSumPdf(RooBinSamplingPdf(...))",
2220 _f->GetName(), GetName());
2221 auto sumPdf = acquireNew<RooRealSumPdf>(TString::Format("%s_pdfWrapper", _f->GetName()), _f->GetTitle(),
2222 *_f, *acquire2<RooAbsArg, RooRealVar>("1", "1", 1), true);
2223 sumPdf->setStringAttribute("alias", _f->getStringAttribute("alias"));
2224 if (!sumPdf->getStringAttribute("alias"))
2225 sumPdf->setStringAttribute("alias", out->GetName());
2226 _f = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _f->GetName()), _f->GetTitle(),
2227 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *sumPdf);
2228 _f->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
2229 if (!_f->getStringAttribute("alias"))
2230 _f->setStringAttribute("alias", out->GetName());
2231#else
2232 throw std::runtime_error(
2233 "unsupported addition of unbinned function to binned model - please upgrade to at least ROOT 6.24");
2234#endif
2235 }
2236 }
2237
2238 const_cast<RooArgList &>(p->coefList()).add(*acquire2<RooAbsArg, RooRealVar>("1", "1", 1));
2239 const_cast<RooArgList &>(p->funcList()).add(*_f);
2240 // inherit binning if we dont have one yet
2241 if (!p->getStringAttribute("binning"))
2242 p->setStringAttribute("binning", _f->getStringAttribute("binning"));
2243
2244 xRooNode _out(_f, *this);
2245 if (auto gf = p->getStringAttribute("global_factors"); gf) {
2246 TStringToken pattern(gf, ";");
2247 while (pattern.NextToken()) {
2248 auto fac = getObject<RooAbsReal>(pattern.Data());
2249 if (!fac) {
2250 throw std::runtime_error(TString::Format("Could not find global factor %s", pattern.Data()));
2251 }
2252 _out.Multiply(fac);
2253 }
2254 }
2255 sterilize();
2256 // clear children for reload and update shared axis
2257 clear();
2258 fXAxis.reset();
2259 p->setStringAttribute("xvar", nullptr);
2260 browse();
2261 return _out;
2262 }
2263 } else if (auto p2 = get<RooProdPdf>(); p2) {
2264 // can "add" to a RooProdPdf provided trying to add a RooAbsReal not a RooAbsPdf and have a zero or 1
2265 // RooRealSumPdf child.convertForAcquisition(*this); - don't convert here because want generated objects named
2266 // after roorealsumpdf
2267 // would like exception is if child is a factory string! - TODO
2268 if (child.get<RooAbsPdf>() || (!child.get() && getObject<RooAbsPdf>(child.GetName()))) {
2269 // can add if 0 or 1 RooAddPdf ....
2270 RooAddPdf *_pdf = nullptr;
2271 bool tooMany(false);
2272 for (auto &pp : factors()) {
2273 if (auto _p = pp->get<RooAddPdf>(); _p) {
2274 if (_pdf) {
2275 _pdf = nullptr;
2276 tooMany = true;
2277 break;
2278 } // more than one!
2279 _pdf = _p;
2280 }
2281 }
2282 if (_pdf) {
2283 return xRooNode(*_pdf, *this).Add(child);
2284 } else if (!tooMany) {
2285 auto out = this->operator[]("components")->Add(child);
2286 return out;
2287 }
2288 } else if ((child.get<TH1>() || child.get<RooAbsReal>() ||
2289 (!child.get() && getObject<RooAbsReal>(child.GetName()))) &&
2290 !child.get<RooAbsPdf>()) {
2291 RooRealSumPdf *_pdf = nullptr;
2292 RooAddPdf *_backup = nullptr;
2293 bool tooMany(false);
2294 for (auto &pp : factors()) {
2295 if (auto _p = pp->get<RooRealSumPdf>(); _p) {
2296 if (_pdf) {
2297 _pdf = nullptr;
2298 tooMany = true;
2299 break;
2300 } // more than one!
2301 _pdf = _p;
2302 } else if (auto _p2 = pp->get<RooAddPdf>(); _p2) {
2303 _backup = _p2;
2304 for (auto &_pdfa : pp->components()) {
2305 if (auto _p3 = _pdfa->get<RooRealSumPdf>(); _p3) {
2306 if (_pdf) {
2307 _pdf = nullptr;
2308 tooMany = true;
2309 break;
2310 } // more than one!
2311 _pdf = _p3;
2312 }
2313 }
2314 }
2315 }
2316 if (_pdf) {
2317 return xRooNode(*_pdf, *this).Add(child);
2318 } else if (_backup) {
2319 // added *INSIDE* the addPdf -- will create a RooRealSumPdf to hold it
2320 return xRooNode(*_backup, *this).Add(child);
2321 } else if (!tooMany) {
2322 auto out = this->operator[]("samples")->Add(child);
2323 // clear our x-axis to re-evaluate
2324 fXAxis.reset();
2325 p2->setStringAttribute("xvar", nullptr);
2326 return out;
2327 }
2328 }
2329 } else if (auto s = get<RooSimultaneous>(); s) {
2330
2331 // adding to a simultaneous means adding a bin
2332 return bins().Add(child);
2333
2334 // if the child is a RooAbsPdf can just add it as a new channel using name of pdf as the channel name
2335 // if child is a histogram, will create a RooProdPdf
2336
2337 } else if (auto w = get<RooWorkspace>(); w) {
2338 child.convertForAcquisition(
2339 *this, child.get() ? "" : "func" /* if child is a string, allow it to be passed to factory */);
2340 if (child.get()) {
2341 if (auto _d = child.get<RooAbsData>()) {
2342 // don't use acquire method to import, because that adds datasets as Embeddded
2343 if (!w->import(*_d)) {
2344 // should upgrade vars with any obs from the dataset
2345 if (_d->get()) {
2346 std::unique_ptr<RooAbsCollection>(w->allVars().selectCommon(*_d->get()))->setAttribAll("obs");
2347 }
2348 if (_d->getGlobalObservables()) {
2349 std::unique_ptr<RooAbsCollection> globs(w->allVars().selectCommon(*_d->getGlobalObservables()));
2350 globs->setAttribAll("obs");
2351 globs->setAttribAll("global");
2352 }
2353 return xRooNode(child.GetName(), *w->data(child.GetName()), *this);
2354 } else {
2355 throw std::runtime_error(
2356 TString::Format("Could not import dataset %s into workspace %s", child.GetName(), w->GetName())
2357 .Data());
2358 }
2359 } else {
2360 auto out = acquire(child.fComp);
2361 if (out)
2362 return xRooNode(child.GetName(), out, *this);
2363 }
2364 }
2365
2366 if (!child.empty() || child.fFolder == "!pdfs") {
2367 // create a RooSimultaneous using the children as the channels
2368 // children either have "=" in name if specifying channel cat name or otherwise assume
2369 std::string catName = "channelCat";
2370 if (!child.empty()) {
2371 if (TString ss = child.at(0)->GetName(); ss.Contains("=")) {
2372 catName = ss(0, ss.Index('='));
2373 }
2374 }
2375 auto _cat = acquire<RooCategory>(catName.c_str(), catName.c_str());
2376 _cat->setAttribute("obs");
2377 auto out = acquireNew<RooSimultaneous>(child.GetName(), child.GetTitle(), *_cat);
2378 Info("Add", "Created pdf RooSimultaneous::%s in workspace %s", out->GetName(), w->GetName());
2379 return xRooNode(out, *this);
2380 }
2381 } else if (auto coll = get<RooAbsCollection>(); coll && child.get<RooAbsArg>()) {
2382 if (coll->isOwning()) {
2383 coll->addOwned(*static_cast<RooAbsArg *>(child.get<RooAbsArg>()->Clone()));
2384 } else if (child.ws() != ws()) {
2385 coll->add(*static_cast<RooAbsArg *>(acquire(child.fComp).get()));
2386 } else {
2387 coll->add(*child.get<RooAbsArg>());
2388 }
2389 return xRooNode(child.GetName(), *coll->find(child.GetName()), *this);
2390 }
2391
2392 if (sOpt == "pdf") {
2393 // can only add a pdf to a workspace
2394 if (get<RooWorkspace>()) {
2395 const_cast<xRooNode &>(child).fFolder = "!pdfs";
2396 return Add(child);
2397 }
2398 } else if (sOpt == "channel") {
2399 // can add to a model or to a workspace (creates a RooProdPdf either way)
2400 if (get<RooSimultaneous>()) {
2401 return Vary(child);
2402 } else if (get<RooWorkspace>()) {
2403 std::shared_ptr<TObject> out;
2404 child.convertForAcquisition(*this);
2405 if (child.get<RooAbsPdf>()) {
2406 out = acquire(child.fComp);
2407 } else if (!child.fComp) {
2408 out = acquireNew<RooProdPdf>(child.GetName(),
2409 (strlen(child.GetTitle())) ? child.GetTitle() : child.GetName(), RooArgList());
2410 Info("Add", "Created channel RooProdPdf::%s in workspace %s", out->GetName(), get()->GetName());
2411 }
2412 return xRooNode(out, *this);
2413 }
2414 } else if (sOpt == "sample" || sOpt == "func") {
2415 if (get<RooProdPdf>()) {
2416 auto _mainChild = mainChild();
2417 if (_mainChild.get<RooRealSumPdf>()) {
2418 return _mainChild.Add(child, sOpt == "func" ? "func" : "");
2419 } else {
2420 return (*this)["samples"]->Add(child, sOpt == "func" ? "func" : "");
2421 }
2422 }
2423 } else if (sOpt == "dataset") {
2424 if (get<RooWorkspace>()) {
2425 // const_cast<xRooNode&>(child).fFolder = "!datasets";return Add(child);
2426 return (*this).datasets().Add(child);
2427 }
2428 }
2429
2430 if (considerType) {
2431
2432 // interpret 'adding' here as dependent on the object type ...
2433 if (get<RooSimultaneous>()) {
2434 return bins().Add(child);
2435 } else if (TString(child.GetName()).Contains('=')) {
2436 return variations().Add(child);
2437 } else if (get<RooProduct>() || get<RooProdPdf>()) {
2438 return factors().Add(child);
2439 }
2440 }
2441
2442 // Nov 2022 - removed ability to add placeholders ... could bring back if rediscover need for them
2443 // if (!child.get() && child.empty() && strlen(child.GetName())) {
2444 // // can add a 'placeholder' node, note it will be deleted at the next browse
2445 // xRooNode out(child.GetName(),nullptr,*this);
2446 // out.SetTitle(child.GetTitle());
2447 // emplace_back(std::make_shared<xRooNode>(out));
2448 // // update the parent in the out node so that it's copy of the parent knows it has itself in it
2449 // // actually maybe not want this :-/
2450 // //out.fParent = std::make_shared<Node2>(*this);
2451 // for(auto o : *gROOT->GetListOfBrowsers()) {
2452 // if(auto b = dynamic_cast<TBrowser*>(o); b && b->GetBrowserImp()){
2453 // if(auto _b = dynamic_cast<TGFileBrowser*>(
2454 // dynamic_cast<TRootBrowser*>(b->GetBrowserImp())->fActBrowser ); _b) {
2455 // auto _root = _b->fRootDir;
2456 // if (!_root) _root = _b->fListTree->GetFirstItem();
2457 // if (auto item = _b->fListTree->FindItemByObj(_root,this); item) {
2458 // _b->fListTree->AddItem(item,back()->GetName(),back().get());
2459 // }
2460 // }
2461 // }
2462 // }
2463 // return out;
2464 // }
2465
2466 throw std::runtime_error(TString::Format("Cannot add %s to %s", child.GetName(), GetName()));
2467}
2468
2469std::string xRooNode::GetPath() const
2470{
2471 if (!fParent)
2472 return GetName();
2473 return fParent->GetPath() + "/" + GetName();
2474}
2475
2477{
2478 // std::cout << "deleting " << GetPath() << std::endl;
2479}
2480
2482{
2483 if (auto a = get<RooAbsArg>()) {
2484 a->setAttribute("hidden", set);
2485 // if(auto item = GetTreeItem(nullptr); item) {
2486 // if(set) item->SetColor(kRed);
2487 // else item->ClearColor();
2488 // }
2489 }
2490}
2492{
2493 auto a = get<RooAbsArg>();
2494 if (a)
2495 return a->getAttribute("hidden");
2496 return false;
2497}
2498
2500{
2501
2502 if (get() == rhs.get()) {
2503 // nothing to do because objects are identical
2504 return *this;
2505 }
2506
2507 if (auto lhsa = get<RooAbsArg>(), rhsa = rhs.get<RooAbsArg>(); lhsa && rhsa && lhsa->isIdentical(*rhsa)) {
2508 return *this;
2509 }
2510
2511 if (get<RooWorkspace>() && rhs.get<RooWorkspace>()) {
2512
2513 // report which top-level pdfs will be combined
2514 std::set<std::string> pdfs;
2515 for (auto &c : rhs.components()) {
2516 if ((*this)["pdfs"]->find(c->GetName())) {
2517 pdfs.insert(c->GetName());
2518 }
2519 }
2520 if (pdfs.empty()) {
2521 Warning("Combine", "No pdfs will be combined. Please check and/or rename pdfs to match");
2522 } else {
2523 std::stringstream s;
2524 for (auto &p : pdfs)
2525 s << p << ",";
2526 Info("Combine", "pdfs that will be combined: %s", s.str().c_str());
2527 }
2528
2529 std::set<std::string> _np;
2530 auto mynp = np();
2531 for (auto &c : rhs.np()) {
2532 if (mynp.find(c->GetName())) {
2533 _np.insert(c->GetName());
2534 }
2535 }
2536 if (_np.empty()) {
2537 Warning("Combine", "No correlated np");
2538 } else {
2539 std::stringstream s;
2540 for (auto &p : _np)
2541 s << p << ",";
2542 Info("Combine", "np that will be shared (correlated): %s", s.str().c_str());
2543 }
2544 std::set<std::string> _poi;
2545 auto mypoi = poi();
2546 for (auto &c : rhs.poi()) {
2547 if (mypoi.find(c->GetName())) {
2548 _poi.insert(c->GetName());
2549 }
2550 }
2551 if (_poi.empty()) {
2552 Warning("Combine", "No correlated poi");
2553 } else {
2554 std::stringstream s;
2555 for (auto &p : _poi)
2556 s << p << ",";
2557 Info("Combine", "poi that will be shared (correlated): %s", s.str().c_str());
2558 }
2559
2560 // TODO: Check for derived components that have matching names and aren't top-level pdfs
2561 //
2562 // const auto comps = get<RooWorkspace>()->components();
2563 //
2564 // std::set<std::string> leafs;
2565 //
2566 // for(auto& c : rhs.get<RooWorkspace>()->components()) {
2567 // if(comps.find(c->GetName())) {
2568 //
2569 // }
2570 // }
2571 }
2572
2573 // combine components, factors, and variations ... when there is a name clash will combine on that object
2574 for (auto &c : rhs.components()) {
2575 if (get<RooWorkspace>() &&
2576 (c->fFolder == "!scratch" || c->fFolder == "!sets" || c->fFolder == "!snapshots" || c->fFolder == "!models"))
2577 continue;
2578 if (auto _c = components().find(c->GetName()); _c) {
2579 if (!silent) {
2580 Info("Combine", "Combining %s into %s", c->GetPath().c_str(), _c->GetPath().c_str());
2581 }
2582 _c->Combine(*c, true);
2583 } else {
2584 try {
2585 if (!silent) {
2586 Info("Combine", "Adding %s into %s", c->GetPath().c_str(), GetPath().c_str());
2587 }
2588 Add(*c);
2589 } catch (std::exception &e) {
2590 Warning("Combine", "Could not combine %s into %s", c->GetPath().c_str(), GetPath().c_str());
2591 }
2592 }
2593 }
2594
2595 if (!get<RooWorkspace>()) { // don't combine factors of a workspace
2596 for (auto &f : rhs.factors()) {
2597 if (auto _f = factors().find(f->GetName()); _f) {
2598 if (!silent) {
2599 Info("Combine", "Combining %s into %s", f->GetPath().c_str(), _f->GetPath().c_str());
2600 }
2601 _f->Combine(*f, true);
2602 } else {
2603 if (!silent) {
2604 Info("Combine", "Multiplying %s into %s", f->GetPath().c_str(), GetPath().c_str());
2605 }
2606 Multiply(*f);
2607 }
2608 }
2609 } else {
2610 // todo: go back through components, doing sets, snapshots, models, ....
2611 // do after import of pdfs etc so that can acquire the copies from the workspace
2612 for (auto &c : rhs.components()) {
2613 if (c->fFolder == "!sets") {
2614 if (components().find(c->GetName())) {
2615 Info("Combine", "Extending set %s", c->GetName());
2616 get<RooWorkspace>()->extendSet(c->GetName(), c->get<RooAbsCollection>()->contentsString().c_str());
2617 } else {
2618 Info("Combine", "Defining set %s", c->GetName());
2619 get<RooWorkspace>()->defineSet(c->GetName(), c->get<RooAbsCollection>()->contentsString().c_str());
2620 }
2621 }
2622 }
2623
2624 // also transfer datasets
2625 for (auto &ds : rhs.datasets()) {
2626 if (auto _ds = datasets().find(ds->GetName()); _ds) {
2627 if (!silent) {
2628 Info("Combine", "Combining %s into %s", ds->GetPath().c_str(), _ds->GetPath().c_str());
2629 }
2630 _ds->Add(*ds);
2631 } else {
2632 if (!silent) {
2633 Info("Combine", "Adding %s into %s", ds->GetPath().c_str(), GetPath().c_str());
2634 }
2635 datasets().Add(*ds);
2636 }
2637 }
2638 }
2639
2640 for (auto &v : rhs.variations()) {
2641 if (auto _v = variations().find(v->GetName()); _v) {
2642 if (!silent) {
2643 Info("Combine", "Combining variation %s into %s", v->GetPath().c_str(), _v->GetPath().c_str());
2644 }
2645 _v->Combine(*v, true);
2646 } else {
2647 if (!silent) {
2648 Info("Combine", "Varying %s into %s", v->GetPath().c_str(), GetPath().c_str());
2649 }
2650 Vary(*v);
2651 }
2652 }
2653
2654 if (get<RooSimultaneous>()) {
2655 // combine bins (channels) ... special case, never done silently
2656 for (auto &b : rhs.bins()) {
2657 if (auto _b = bins().find(b->GetName()); _b) {
2658 Info("Combine", "Combining %s into %s", b->GetPath().c_str(), _b->GetPath().c_str());
2659 _b->Combine(*b, true);
2660 } else {
2661 Info("Combine", "Extending with %s into %s", b->GetPath().c_str(), GetPath().c_str());
2662 Vary(*b); // extending channels currently done through Vary method
2663 }
2664 }
2665 }
2666
2667 // todo: Should also transfer over binnings of observables
2668
2669 return *this;
2670}
2671
2672xRooNode xRooNode::shallowCopy(const std::string &name, std::shared_ptr<xRooNode> parent)
2673{
2674 xRooNode out(name.c_str(), nullptr,
2675 parent /*? parent : fParent -- was passing fParent for getObject benefit before fProvider concept*/);
2676 // if(!parent) out.fAcquirer = true;
2677 if (!parent)
2678 out.fProvider = fParent;
2679
2680 auto o = get();
2681 if (!o) {
2682 return out;
2683 }
2684
2685 if (auto s = get<RooSimultaneous>(); s) {
2686 auto chans = bins();
2687 if (!chans.empty()) {
2688 // create a new RooSimultaneous with shallow copies of each channel
2689
2690 std::shared_ptr<RooSimultaneous> pdf = out.acquire<RooSimultaneous>(
2691 name.c_str(), o->GetTitle(), const_cast<RooAbsCategoryLValue &>(s->indexCat()));
2692
2693 for (auto &c : chans) {
2694 TString cName(c->GetName());
2695 cName = cName(cName.Index('=') + 1, cName.Length());
2696 // by passing out as the parent, will ensure out acquires everything created
2697 auto c_copy =
2698 c->shallowCopy(name + "_" + c->get()->GetName(), std::shared_ptr<xRooNode>(&out, [](xRooNode *) {}));
2699 pdf->addPdf(*dynamic_cast<RooAbsPdf *>(c_copy.get()), cName);
2700 }
2701 out.fComp = pdf;
2702 return out;
2703 }
2704 } else if (auto p = dynamic_cast<RooProdPdf *>(o); p) {
2705 // main pdf will be copied too
2706 std::shared_ptr<RooProdPdf> pdf =
2707 std::dynamic_pointer_cast<RooProdPdf>(out.acquire(std::shared_ptr<TObject>(p->Clone(/*name.c_str()*/)), false,
2708 true)); // use clone to copy all attributes etc too
2709 auto main = mainChild();
2710 if (main) {
2711 auto newMain =
2712 std::dynamic_pointer_cast<RooAbsArg>(out.acquire(std::shared_ptr<TObject>(main->Clone()), false, true));
2713 std::cout << newMain << " " << newMain->GetName() << std::endl;
2714 // pdf->replaceServer(*pdf->pdfList().find(main->GetName()), *newMain, true, true);
2715 // const_cast<RooArgList&>(pdf->pdfList()).replace(*pdf->pdfList().find(main->GetName()), *newMain);
2716 pdf->redirectServers(RooArgList(*newMain));
2717 }
2718 out.fComp = pdf;
2719 out.sterilize();
2720 return out;
2721 }
2722
2723 return out;
2724}
2725
2727{
2728 static std::unique_ptr<cout_redirect> capture;
2729 std::string captureStr;
2730 bool doCapture = false;
2731 if (!capture && gROOT->FromPopUp()) { // FromPopUp means user executed from the context menu
2732 capture = std::make_unique<cout_redirect>(captureStr);
2733 doCapture = true;
2734 }
2735
2736 TString sOpt(opt);
2737 int depth = 0;
2738 if (sOpt.Contains("depth=")) {
2739 depth = TString(sOpt(sOpt.Index("depth=") + 6, sOpt.Length())).Atoi();
2740 sOpt.ReplaceAll(TString::Format("depth=%d", depth), "");
2741 }
2742 int indent = 0;
2743 if (sOpt.Contains("indent=")) {
2744 indent = TString(sOpt(sOpt.Index("indent=") + 7, sOpt.Length())).Atoi();
2745 sOpt.ReplaceAll(TString::Format("indent=%d", indent), "");
2746 }
2747 bool _more = sOpt.Contains("m");
2748 if (_more)
2749 sOpt.Replace(sOpt.Index("m"), 1, "");
2750 if (sOpt != "")
2751 _more = true;
2752
2753 if (indent == 0) { // only print self if not indenting (will already be printed above if tree traverse)
2754 std::cout << GetPath();
2755 if (get() && get() != this) {
2756 std::cout << ": ";
2757 if (_more || (get<RooAbsArg>() && get<RooAbsArg>()->isFundamental()) || get<RooConstVar>() ||
2759 auto _deps = coords(false).argList(); // want to revert coords after print
2760 auto _snap = std::unique_ptr<RooAbsCollection>(_deps.snapshot());
2761 coords(); // move to coords before printing (in case this matters)
2762 get()->Print(sOpt);
2763 if (auto _fr = get<RooFitResult>(); _fr && dynamic_cast<RooStringVar *>(_fr->constPars().find(".log"))) {
2764 std::cout << "Minimization Logs:" << std::endl;
2765 std::cout << dynamic_cast<RooStringVar *>(_fr->constPars().find(".log"))->getVal() << std::endl;
2766 }
2767 _deps.assignValueOnly(*_snap);
2768 // std::cout << std::endl;
2769 } else {
2770 TString _suffix = "";
2771 if (auto _type = GetNodeType(); strlen(_type)) {
2772 // decided not to show const values until figure out how to update if value changes
2773 /*if (TString(_type)=="Const") _name += TString::Format("
2774 [%s=%g]",_type,v->get<RooConstVar>()->getVal()); else*/
2775 _suffix += TString::Format(" [%s]", _type);
2776 }
2777 if (auto fv = get<RooFormulaVar>()) {
2778 TString formu = TString::Format(" [%s]", fv->expression());
2779 for (size_t i = 0; i < fv->dependents().size(); i++) {
2780 formu.ReplaceAll(TString::Format("x[%zu]", i), fv->dependents()[i].GetName());
2781 }
2782 _suffix += formu;
2783 } else if (auto gv = get<RooGenericPdf>()) {
2784 TString formu = TString::Format(" [%s]", gv->expression());
2785 for (size_t i = 0; i < gv->dependents().size(); i++) {
2786 formu.ReplaceAll(TString::Format("x[%zu]", i), gv->dependents()[i].GetName());
2787 }
2788 _suffix += formu;
2789 }
2790 std::cout << get()->ClassName() << "::" << get()->GetName() << _suffix.Data() << std::endl;
2791 }
2792
2793 } else if (!get()) {
2794 std::cout << std::endl;
2795 }
2796 }
2797 const_cast<xRooNode *>(this)->browse();
2798 std::vector<std::string> folderNames;
2799 for (auto &k : *this) {
2800 if (std::find(folderNames.begin(), folderNames.end(), k->fFolder) == folderNames.end()) {
2801 folderNames.push_back(k->fFolder);
2802 }
2803 }
2804 for (auto &f : folderNames) {
2805 int i = 0;
2806 int iindent = indent;
2807 if (!f.empty()) {
2808 for (int j = 0; j < indent; j++)
2809 std::cout << " ";
2810 std::cout << f << std::endl;
2811 iindent += 1;
2812 }
2813 for (auto &k : *this) {
2814 if (k->fFolder != f) {
2815 i++;
2816 continue;
2817 }
2818 for (int j = 0; j < iindent; j++)
2819 std::cout << " ";
2820 std::cout << i++ << ") " << k->GetName() << " : ";
2821 if (k->get()) {
2822 if (_more || (k->get<RooAbsArg>() && k->get<RooAbsArg>()->isFundamental()) || k->get<RooConstVar>() ||
2823 k->get<RooAbsData>() /*|| k->get<RooProduct>()*/) {
2824 auto _deps = k->coords(false).argList();
2825 auto _snap = std::unique_ptr<RooAbsCollection>(_deps.snapshot());
2826 k->coords(); // move to coords before printing (in case this matters)
2827 k->get()->Print(sOpt); // assumes finishes with an endl
2828 _deps.assignValueOnly(*_snap);
2829 } else {
2830 TString _suffix = "";
2831 if (auto _type = k->GetNodeType(); strlen(_type)) {
2832 // decided not to show const values until figure out how to update if value changes
2833 /*if (TString(_type)=="Const") _name += TString::Format("
2834 [%s=%g]",_type,v->get<RooConstVar>()->getVal()); else*/
2835 _suffix += TString::Format(" [%s]", _type);
2836 }
2837 if (auto fv = k->get<RooFormulaVar>()) {
2838 TString formu = TString::Format(" [%s]", fv->expression());
2839 for (size_t j = 0; j < fv->dependents().size(); j++) {
2840 formu.ReplaceAll(TString::Format("x[%zu]", j), fv->dependents()[j].GetName());
2841 }
2842 _suffix += formu;
2843 } else if (auto gv = k->get<RooGenericPdf>()) {
2844 TString formu = TString::Format(" [%s]", gv->expression());
2845 for (size_t j = 0; j < gv->dependents().size(); j++) {
2846 formu.ReplaceAll(TString::Format("x[%zu]", j), gv->dependents()[j].GetName());
2847 }
2848 _suffix += formu;
2849 }
2850 std::cout << k->get()->ClassName() << "::" << k->get()->GetName() << _suffix.Data() << std::endl;
2851 }
2852 if (depth != 0) {
2853 k->Print(sOpt + TString::Format("depth=%dindent=%d", depth - 1, iindent + 1));
2854 }
2855 } else
2856 std::cout << " NULL " << std::endl;
2857 }
2858 }
2859 if (doCapture) {
2860 capture.reset(); // no captureStr has the string to display
2861 // inject line breaks to avoid msgbox being too wide
2862 size_t lastBreak = 0;
2863 std::string captureStrWithBreaks;
2864 for (size_t i = 0; i < captureStr.size(); i++) {
2866 if (captureStr[i] == '\n') {
2867 lastBreak = i;
2868 }
2869 if (i - lastBreak > 150) {
2870 captureStrWithBreaks += '\n';
2871 lastBreak = i;
2872 }
2873 }
2874 const TGWindow *w =
2875 (gROOT->GetListOfBrowsers()->At(0))
2876 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
2877 : gClient->GetRoot();
2878 new TGMsgBox(gClient->GetRoot(), w, GetName(),
2879 captureStrWithBreaks.c_str()); //,nullptr,kMBDismiss,nullptr,kVerticalFrame,kTextLeft|kTextCenterY);
2880 }
2881}
2882
2884{
2885 if (!child.get()) {
2886
2887 if (auto v = get<RooRealVar>(); v) {
2888
2889 TString constrType = child.GetName();
2890 double mean = std::numeric_limits<double>::quiet_NaN();
2891 double sigma = mean;
2892 if (constrType.BeginsWith("gaussian(")) {
2893 // extract the mean and stddev parameters
2894 // if only one given, it is the stddev
2895 if (constrType.Contains(",")) {
2896 mean = TString(constrType(9, constrType.Index(',') - 9)).Atof();
2897 sigma = TString(constrType(constrType.Index(',') + 1, constrType.Index(')') - constrType.Index(',') + 1))
2898 .Atof();
2899 } else {
2900 mean = std::numeric_limits<double>::quiet_NaN(); // will use the var current value below to set mean
2901 sigma = TString(constrType(9, constrType.Index(')') - 9)).Atof();
2902 }
2903 constrType = "normal";
2904 } else if (constrType == "normal") {
2905 mean = 0;
2906 sigma = 1;
2907 } else if (constrType == "gaussian") {
2908 // extract parameters from the variable
2909 // use current value and error on v as constraint
2910 if (!v->hasError())
2911 throw std::runtime_error("No error on parameter for gaussian constraint");
2912 sigma = v->getError();
2913 mean = v->getVal();
2914 constrType = "normal";
2915 } else if (constrType == "poisson") {
2916 if (!v->hasError())
2917 throw std::runtime_error("No error on parameter for poisson constraint");
2918 mean = 1;
2919 sigma = pow(v->getVal() / v->getError(), 2);
2920 }
2921
2922 if (constrType == "poisson") {
2923 // use current value and error on v as constraint
2924 double tau_val = sigma;
2925 auto globs = acquire<RooRealVar>(Form("globs_%s", v->GetName()), Form("globs_%s", v->GetName()),
2926 v->getVal() * tau_val, (v->getVal() - 5 * v->getError()) * tau_val,
2927 (v->getVal() + 5 * v->getError()) * tau_val);
2928 globs->setConstant();
2929 globs->setAttribute("obs");
2930 globs->setAttribute("global");
2931 globs->setStringAttribute("nominal", TString::Format("%f", tau_val));
2932 auto tau = acquireNew<RooConstVar>(TString::Format("tau_%s", v->GetName()), "", tau_val);
2934 Form("pois_%s", v->GetName()), TString::Format("Poisson Constraint of %s", v->GetTitle()), *globs,
2935 *acquireNew<RooProduct>(TString::Format("mean_%s", v->GetName()),
2936 TString::Format("Poisson Constraint of %s", globs->GetTitle()),
2937 RooArgList(*v, *tau)),
2938 true /* no rounding */);
2939
2940 auto out = Constrain(xRooNode(Form("pois_%s", GetName()), constr));
2941 if (!v->hasError())
2942 v->setError(mean / sqrt(tau_val)); // if v doesnt have an uncert, will put one on it now
2943 Info("Constrain", "Added poisson constraint pdf RooPoisson::%s (tau=%g) for %s", out->GetName(), tau_val,
2944 GetName());
2945 return out;
2946 } else if (constrType == "normal") {
2947
2948 auto globs = acquire<RooRealVar>(Form("globs_%s", v->GetName()), Form("globs_%s", v->GetName()), mean,
2949 mean - 10 * sigma, mean + 10 * sigma);
2950 globs->setAttribute("obs");
2951 globs->setAttribute("global");
2952 globs->setConstant();
2953
2954 globs->setStringAttribute("nominal", TString::Format("%f", mean));
2956 Form("gaus_%s", v->GetName()), TString::Format("Gaussian Constraint of %s", v->GetTitle()), *globs, *v,
2957 *acquireNew<RooConstVar>(TString::Format("sigma_%s", v->GetName()), "", sigma));
2958 auto out = Constrain(xRooNode(Form("gaus_%s", GetName()), constr));
2959 if (!v->hasError())
2960 v->setError(sigma); // if v doesnt have an uncert, will put one on it now
2961 Info("Constrain", "Added gaussian constraint pdf RooGaussian::%s (mean=%g,sigma=%g) for %s", out->GetName(),
2962 mean, sigma, GetName());
2963 return out;
2964 }
2965 }
2966 } else if (auto p = child.get<RooAbsPdf>(); p) {
2967
2968 auto _me = get<RooAbsArg>();
2969 if (!_me) {
2970 throw std::runtime_error("Cannot constrain non arg");
2971 }
2972
2973 if (!p->dependsOn(*_me)) {
2974 throw std::runtime_error("Constraint does not depend on constrainee");
2975 }
2976
2977 // find a parent that can swallow this pdf ... either a RooProdPdf or a RooWorkspace
2978 auto x = fParent;
2979 while (x && !x->get<RooProdPdf>() && !x->get<RooSimultaneous>() && !x->get<RooWorkspace>()) {
2980 x = x->fParent;
2981 }
2982 if (!x) {
2983 throw std::runtime_error("Nowhere to put constraint");
2984 }
2985 // get datasets of the swallower, and add glob to any globs lists
2986 auto childGlobs = child.globs();
2987 if (!childGlobs.empty()) {
2988 for (auto d : x->datasets()) {
2989 if (auto globs = d->get<RooAbsData>()->getGlobalObservables()) {
2991 newGlobs.add(*childGlobs.get<RooArgList>());
2992 d->get<RooAbsData>()->setGlobalObservables(newGlobs);
2993 }
2994 }
2995 // also add to the workspaces globalObservables lists
2996 if (x->ws()) {
2997 for (auto &[k, v] : GETWSSETS(x->ws())) {
2998 if (k == "globalObservables" || TString(k).EndsWith("_GlobalObservables")) {
2999 const_cast<RooArgSet &>(v).add(*childGlobs.get<RooArgList>());
3000 }
3001 }
3002 }
3003 }
3004 if (auto s = x->get<RooSimultaneous>(); s) {
3005 // put into every channel that features parameter
3006 x->browse();
3007 for (auto &c : *x) {
3008 if (auto a = c->get<RooAbsArg>(); a->dependsOn(*_me))
3009 c->Multiply(child);
3010 }
3011 return child;
3012 } else if (x->get<RooProdPdf>()) {
3013 return x->Multiply(child);
3014 } else {
3015 return x->Add(child, "+");
3016 }
3017 }
3018
3019 throw std::runtime_error(TString::Format("Cannot constrain %s", GetName()));
3020}
3021
3023{
3024
3025 class AutoUpdater {
3026 public:
3027 AutoUpdater(xRooNode &_n) : n(_n) {}
3028 ~AutoUpdater() { n.browse(); }
3029 xRooNode &n;
3030 };
3031 AutoUpdater xxx(*this);
3032
3033 if (fBinNumber != -1) {
3034 // scaling a bin ...
3035 if (child.get<RooAbsReal>()) { // if not child then let fall through to create a child and call self again below
3036 // doing a bin-multiplication .. the parent should have a ParamHistFunc called binFactors
3037 // if it doesn't then create one
3038 auto o = std::dynamic_pointer_cast<RooAbsReal>(acquire(child.fComp));
3039
3040 // get binFactor unless parent is a ParamHistFunc already ...
3041
3042 auto binFactors = (fParent->get<ParamHistFunc>()) ? fParent : fParent->factors().find("binFactors");
3043
3044 // it can happen in a loop over bins() that another node has moved fParent inside a product
3045 // so check for fParent having a client with the ORIGNAME:<name> attribute
3046 if (!binFactors && fParent->get<RooAbsArg>()) {
3047 for (auto c : fParent->get<RooAbsArg>()->clients()) {
3048 if (c->IsA() == RooProduct::Class() &&
3049 c->getAttribute(TString::Format("ORIGNAME:%s", fParent->get()->GetName()))) {
3050 // try getting binFactors out of this
3051 binFactors = xRooNode(*c).factors().find("binFactors");
3052 break;
3053 }
3054 }
3055 }
3056
3057 if (!binFactors) {
3058 fParent
3059 ->Multiply(TString::Format("%s_binFactors",
3060 (fParent->mainChild().get())
3061 ? fParent->mainChild()->GetName()
3062 : (fParent->get() ? fParent->get()->GetName() : fParent->GetName()))
3063 .Data(),
3064 "blankshape")
3065 .SetName("binFactors"); // creates ParamHistFunc with all pars = 1 (shared const)
3066 binFactors = fParent->factors().find("binFactors");
3067 if (!binFactors) {
3068 throw std::runtime_error(
3069 TString::Format("Could not create binFactors in parent %s", fParent->GetName()));
3070 }
3071 // auto phf = binFactors->get<ParamHistFunc>();
3072
3073 // create RooProducts for all the bins ... so that added factors don't affect selves
3074 int i = 1;
3075 for (auto &b : binFactors->bins()) {
3076 auto p = acquireNew<RooProduct>(TString::Format("%s_bin%d", binFactors->get()->GetName(), i),
3077 TString::Format("binFactors of bin %d", i), RooArgList());
3078 p->setStringAttribute("alias", TString::Format("%s=%g", binFactors->GetXaxis()->GetParent()->GetName(),
3079 binFactors->GetXaxis()->GetBinCenter(i)));
3080 b->Multiply(*p);
3081 i++;
3082 }
3083 }
3084 // then scale the relevant bin ... if the relevant bin is a "1" then just drop in our factor (inside a
3085 // RooProduct though, to avoid it getting modified by subsequent multiplies)
3086 auto _bin = binFactors->bins().at(fBinNumber - 1);
3087 if (auto phf = binFactors->get<ParamHistFunc>(); phf && _bin) {
3088#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3089 RooArgList &pSet = phf->_paramSet;
3090#else
3091 RooArgList &pSet = const_cast<RooArgList &>(phf->paramList());
3092#endif
3093 if (strcmp(_bin->GetName(), "1") == 0) {
3095 for (std::size_t i = 0; i < pSet.size(); i++) {
3096 if (int(i) != fBinNumber - 1) {
3097 all.add(*pSet.at(i));
3098 } else {
3099 all.add(*o);
3100 }
3101 }
3102 pSet.removeAll();
3103 pSet.add(all);
3104 } else {
3105 _bin->fBinNumber = -1; // to avoid infinite loop
3106 return _bin->Multiply(child, opt);
3107 }
3108 // } else {else if(_bin->get<RooProduct>()) {
3109 // // multiply the element which will just add it as a factor in the rooproduct
3110 // return _bin->Multiply(child,opt);
3111 // } else {
3112 // // not a rooproduct in this bin yet ... so need to replace with a rooproduct and
3113 // multiply that
3114 // // this avoids the undesired behaviour of shared binFactors getting all impacted by
3115 // mulitplies RooArgList all; auto new_p =
3116 // acquireNew<RooProduct>(TString::Format("%s_bin%d",binFactors->get()->GetName(),fBinNumber),TString::Format("binFactors
3117 // of bin %d",fBinNumber),RooArgList(*_bin->get<RooAbsArg>()));
3118 // new_p->setStringAttribute("alias","")
3119 // for (int i = 0; i < phf->_paramSet.size(); i++) {
3120 // if (i != fBinNumber - 1) all.add(*phf->_paramSet.at(i));
3121 // else all.add(*new_p);
3122 // }
3123 // phf->_paramSet.removeAll();
3124 // phf->_paramSet.add(all);
3125 // // now multiply that bin having converted it to RooProduct
3126 // return binFactors->bins().at(fBinNumber - 1)->Multiply(child,opt);
3127 // }
3128 }
3129 return xRooNode(*o, binFactors);
3130 }
3131 } else if (!get() && fParent) {
3132 // try to 'create' object based on parentage
3133 // add child as a temporary child to help with decision making
3134 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
3135 try {
3136 fComp = fParent->Add(*this, "+").fComp;
3137 } catch (...) {
3138 resize(size() - 1);
3139 std::rethrow_exception(std::current_exception());
3140 }
3141 resize(size() - 1); // remove the temporarily added node
3142 }
3143
3144 if (!child.get()) {
3145 TString sOpt(opt);
3146 sOpt.ToLower();
3147 if (auto o = getObject<RooAbsReal>(child.GetName())) {
3148 auto out = Multiply(xRooNode(o, child.fParent));
3149 // have to protect bin case where get() is null (could change but then must change logic above too)
3150 if (get()) {
3151 Info("Multiply", "Scaled %s by existing factor %s::%s",
3152 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), o->ClassName(), o->GetName());
3153 }
3154 return out;
3155 } else if (sOpt == "const") {
3156 auto out = Multiply(RooConstVar(child.GetName(), child.GetTitle(), 1));
3157 if (get()) {
3158 Info("Multiply", "Scaled %s by new const factor %s",
3159 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
3160 }
3161 return out;
3162 } else if (sOpt == "norm") {
3163 if (TString(child.GetName()).Contains("[") && ws()) {
3164 // assume factory method wanted
3165 auto arg = ws()->factory(child.GetName());
3166 if (arg) {
3167 auto out = Multiply(*arg);
3168 if (get()) {
3169 Info("Multiply", "Scaled %s by new norm factor %s",
3170 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
3171 }
3172 return out;
3173 }
3174 throw std::runtime_error(TString::Format("Failed to create new normFactor %s", child.GetName()));
3175 }
3176 auto out = Multiply(RooRealVar(child.GetName(), child.GetTitle(), 1, -1e-5, 100));
3177 if (get()) {
3178 Info("Multiply", "Scaled %s by new norm factor %s",
3179 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
3180 }
3181 return out;
3182 } else if (sOpt == "shape" || sOpt == "simple" || sOpt == "blankshape") {
3183 // needs axis defined
3184 if (auto ax = GetXaxis(); ax) {
3185 auto h = std::shared_ptr<TH1>(BuildHistogram(dynamic_cast<RooAbsLValue *>(ax->GetParent()), true));
3186 h->Reset();
3187 for (int i = 1; i <= h->GetNbinsX(); i++) {
3188 h->SetBinContent(i, 1);
3189 }
3190 h->SetMinimum(0);
3191 h->SetMaximum(100);
3192 h->SetName(TString::Format(";%s", child.GetName())); // ; char indicates don't "rename" this thing
3193 h->SetTitle(child.GetTitle());
3194 if (sOpt.Contains("shape"))
3195 h->SetOption(sOpt);
3196 auto out = Multiply(*h);
3197 if (get()) {
3198 Info("Multiply", "Scaled %s by new %s factor %s",
3199 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), sOpt.Data(), out->GetName());
3200 }
3201 return out;
3202 }
3203 } else if (sOpt == "overall") {
3205 child.GetName(), child.GetTitle(), RooArgList(), 1, std::vector<double>(), std::vector<double>()));
3206 if (get() /* can happen this is null if on a bin node with no shapeFactors*/) {
3207 Info("Multiply", "Scaled %s by new overall factor %s",
3208 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
3209 }
3210 return out;
3211 } else if (sOpt == "func" && ws()) {
3212 // need to get way to get dependencies .. can't pass all as causes circular dependencies issues.
3213 if (auto arg = ws()->factory(TString("expr::") + child.GetName())) {
3214 auto out = Multiply(*arg);
3215 if (get() /* can happen this is null if on a bin node with no shapeFactors*/) {
3216 Info("Multiply", "Scaled %s by new func factor %s",
3217 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
3218 }
3219 return out;
3220 }
3221 }
3222 }
3223 if (auto h = child.get<TH1>(); h && strlen(h->GetOption()) == 0 && strlen(opt) > 0) {
3224 // put the option in the hist
3225 h->SetOption(opt);
3226 }
3227 if (auto w = get<RooWorkspace>(); w) {
3228 // just acquire
3229 std::shared_ptr<TObject> out;
3230 child.convertForAcquisition(*this);
3231 if (child.get<RooAbsReal>())
3232 out = acquire(child.fComp);
3233 return out;
3234 }
3235
3236 if (strcmp(GetName(), ".coef") == 0) { // covers both .coef and .coefs
3237 // need to add this into the relevant coef ... if its not a RooProduct, replace it with one first
3238 if (auto p = fParent->fParent->get<RooAddPdf>()) {
3239 // may be in no-coef mode ... in which case must create coefs (use "ExtendedBindings" but note that these need
3240 // obs list passing to them
3241 if (p->coefList().empty() && !p->pdfList().empty()) {
3242 for (auto _pdf : p->pdfList()) {
3243 const_cast<RooArgList &>(p->coefList())
3244 .add(*acquireNew<RooExtendedBinding>(TString::Format("%s_extBind", _pdf->GetName()),
3245 TString::Format("Expected Events of %s", _pdf->GetTitle()),
3246 *static_cast<RooAbsPdf *>(_pdf)));
3247 }
3248 Info("Multiply", "Created RooExtendedBinding coefficients for all pdfs of %s so that can multiply coef",
3249 p->GetName());
3250 *reinterpret_cast<bool *>(reinterpret_cast<unsigned char *>(p) +
3251 p->Class()->GetDataMemberOffset("_allExtendable")) = false;
3252 *reinterpret_cast<bool *>(reinterpret_cast<unsigned char *>(p) +
3253 p->Class()->GetDataMemberOffset("_haveLastCoef")) = true;
3254 }
3255 for (size_t i = 0; i < p->pdfList().size(); i++) {
3256 if (p->pdfList().at(i) == fParent->get<RooAbsArg>()) {
3257 auto coefs = p->coefList().at(i);
3258 if (!coefs->InheritsFrom("RooProduct")) {
3260 if (!(strcmp(coefs->GetName(), "1") == 0 || strcmp(coefs->GetName(), "ONE") == 0))
3261 oldCoef.add(*coefs);
3262 auto newCoefs = fParent->acquireNew<RooProduct>(
3263 TString::Format("coefs_%s", fParent->GetName()),
3264 TString::Format("coefficients for %s", fParent->GetName()), oldCoef);
3266 for (size_t j = 0; j < p->coefList().size(); j++) {
3267 if (i == j) {
3268 oldCoefs.add(*newCoefs);
3269 } else {
3270 oldCoefs.add(*p->coefList().at(j));
3271 }
3272 }
3273 const_cast<RooArgList &>(p->coefList()).removeAll();
3274 const_cast<RooArgList &>(p->coefList()).add(oldCoefs);
3275 coefs = newCoefs.get();
3276 }
3277 return xRooNode(*coefs, fParent).Multiply(child);
3278 }
3279 }
3280 } else if (auto p2 = fParent->fParent->get<RooRealSumPdf>()) {
3281 // find our function in the funcList, and then update the coefs of it
3282
3283 for (size_t i = 0; i < p2->funcList().size(); i++) {
3284 if (p2->funcList().at(i) == fParent->get<RooAbsArg>()) {
3285 auto coefs = p2->coefList().at(i);
3286 if (!coefs->InheritsFrom("RooProduct")) {
3288 if (!(strcmp(coefs->GetName(), "1") == 0 || strcmp(coefs->GetName(), "ONE") == 0))
3289 oldCoef.add(*coefs);
3290 auto newCoefs = fParent->acquireNew<RooProduct>(
3291 TString::Format("coefs_%s", fParent->GetName()),
3292 TString::Format("coefficients for %s", fParent->GetName()), oldCoef);
3294 for (size_t j = 0; j < p2->coefList().size(); j++) {
3295 if (i == j) {
3296 oldCoefs.add(*newCoefs);
3297 } else {
3298 oldCoefs.add(*p2->coefList().at(j));
3299 }
3300 }
3301 const_cast<RooArgList &>(p2->coefList()).removeAll();
3302 const_cast<RooArgList &>(p2->coefList()).add(oldCoefs);
3303 coefs = newCoefs.get();
3304 }
3305 return xRooNode(*coefs, fParent).Multiply(child);
3306 }
3307 }
3308 }
3309 throw std::runtime_error("this coefs case is not supported");
3310 }
3311
3312 if (auto p = get<RooProduct>(); p) {
3313 std::shared_ptr<TObject> out;
3314 auto cc = child.fComp;
3315 bool isConverted = (child.convertForAcquisition(*this) != cc);
3316 if (child.get<RooAbsReal>())
3317 out = acquire(child.fComp);
3318
3319 // child may be a histfunc or a rooproduct of a histfunc and a paramhist if has stat errors
3320 if (auto _f = std::dynamic_pointer_cast<RooHistFunc>(
3321 (child.get<RooProduct>()) ? child.factors()[child.GetName()]->fComp : out);
3322 _f && _f->getAttribute("autodensity")) {
3323 // should we flag this as a density? yes if there's no other term marked as the density
3324 bool hasDensity = false;
3325 for (auto &f : factors()) {
3326 if (f->get<RooAbsArg>()->getAttribute("density")) {
3327 hasDensity = true;
3328 break;
3329 }
3330 }
3331 _f->setAttribute("density", !hasDensity && fParent && fParent->get<RooRealSumPdf>());
3332 if (_f->getAttribute("density")) {
3333
3334 // need to divide by bin widths first
3335 for (int i = 0; i < _f->dataHist().numEntries(); i++) {
3336 auto bin_pars = _f->dataHist().get(i);
3337 _f->dataHist().set(*bin_pars, _f->dataHist().weight() / _f->dataHist().binVolume(*bin_pars));
3338 }
3339 _f->setValueDirty();
3340
3341 // promote the axis vars to observables
3342 for (auto &x : xRooNode("tmp", _f).vars()) {
3343 x->get<RooAbsArg>()->setAttribute("obs");
3344 }
3345 }
3346 _f->setAttribute("autodensity", false);
3347 }
3348
3349 if (isConverted && child.get<RooHistFunc>()) {
3350 Info("Multiply", "Created %s factor %s in %s",
3351 child.get<RooAbsArg>()->getAttribute("density") ? "SimpleDensity" : "Simple", child->GetName(),
3352 p->GetName());
3353 } else if (isConverted && child.get<ParamHistFunc>()) {
3354 Info("Multiply", "Created Shape factor %s in %s", child->GetName(), p->GetName());
3355 }
3356
3357 if (auto _f = std::dynamic_pointer_cast<RooAbsReal>(out); _f) {
3358#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3359 p->_compRSet.add(*_f);
3360#else
3361 const_cast<RooArgList &>(p->realComponents()).add(*_f);
3362#endif
3363 p->setValueDirty();
3364
3365 browse();
3366 xRooNode _out(_f, *this);
3367 for (auto &_par : _out.pars()) {
3368 if (auto s = _par->get<RooAbsArg>()->getStringAttribute("boundConstraint"); s) {
3369 bool found = false;
3370 for (auto &_constr : _par->constraints()) {
3371 if (strcmp(s, _constr->get()->GetName()) == 0) {
3372 // constraint is already included
3373 found = true;
3374 break;
3375 }
3376 }
3377 if (!found) {
3378 Info("Multiply", "Pulling in %s boundConstraint: %s", _par->GetName(), s);
3379 auto _pdf = getObject<RooAbsPdf>(s);
3380 if (!_pdf) {
3381 throw std::runtime_error("Couldn't find boundConstraint");
3382 }
3383 _par->Constrain(_pdf);
3384 }
3385 }
3386 }
3387 sterilize();
3388 return _out;
3389 }
3390 } else if (auto p2 = get<RooProdPdf>(); p2) {
3391
3392 std::shared_ptr<TObject> out;
3393 child.convertForAcquisition(*this);
3394 if (child.get<RooAbsPdf>()) {
3395 out = acquire(child.fComp);
3396 } else if (child.get<RooAbsReal>() && mainChild().get<RooRealSumPdf>()) {
3397 // cannot multiply a RooProdPdf by a non pdf
3398 throw std::runtime_error(TString::Format("Cannot multiply %s by non-pdf %s", GetName(), child.GetName()));
3399 // return mainChild().Add(child); - nov 2022 - used to do this but now replaced with exception above
3400 } else if (!child.get() || child.get<RooAbsReal>()) {
3401 // need to create or hide inside a sumpdf or rooadpdf
3402 std::shared_ptr<RooAbsPdf> _pdf;
3403 if (!child.get() && strcmp(child.GetName(), "components") == 0) {
3405 Form("%s_%s", p2->GetName(), child.GetName()),
3406 (strlen(child.GetTitle()) && strcmp(child.GetTitle(), child.GetName())) ? child.GetTitle()
3407 : p2->GetTitle(),
3408 RooArgList() /*, RooArgList() forces coef-mode if we specify this list */);
3409 _pdf = _sumpdf;
3410 } else {
3412 Form("%s_%s", p2->GetName(), child.GetName()),
3413 (strlen(child.GetTitle()) && strcmp(child.GetTitle(), child.GetName())) ? child.GetTitle()
3414 : p2->GetTitle(),
3415 RooArgList(), RooArgList(), true);
3416 _sumpdf->setFloor(true);
3417 _pdf = _sumpdf;
3418 }
3419 _pdf->setStringAttribute("alias", child.GetName());
3420 // transfer axis attributes if present (TODO: should GetXaxis look beyond the immediate parent?)
3421 _pdf->setStringAttribute("xvar", p2->getStringAttribute("xvar"));
3422 _pdf->setStringAttribute("binning", p2->getStringAttribute("binning"));
3423 out = _pdf;
3424 Info("Multiply", "Created %s::%s in channel %s", _pdf->ClassName(), _pdf->GetName(), p2->GetName());
3425 if (child.get<RooAbsReal>())
3426 xRooNode(*out, *this).Add(child);
3427 }
3428
3429 if (auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out); _pdf) {
3430#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3431 const_cast<RooArgList &>(p2->pdfList()).add(*_pdf);
3432#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
3433 p2->_pdfNSetList.emplace_back(std::make_unique<RooArgSet>("nset"));
3434#else
3435 p->_pdfNSetList.Add(new RooArgSet("nset"));
3436#endif
3437 if (!p2->canBeExtended() && _pdf->canBeExtended()) {
3438 p2->_extendedIndex = p2->_pdfList.size() - 1;
3439 }
3440#else
3441 p2->addPdfs(RooArgSet(*_pdf));
3442#endif
3443 sterilize();
3444 browse();
3445 return xRooNode(_pdf, *this);
3446 }
3447 } else if (auto p3 = get<RooRealSumPdf>(); p3) {
3448 // multiplying all current and future components
3449 std::shared_ptr<TObject> out;
3450 child.convertForAcquisition(*this);
3451 if (child.get<RooAbsReal>()) {
3452 out = acquire(child.fComp);
3453 for (auto &c : components()) {
3454 c->Multiply(out);
3455 }
3456 TString s = p3->getStringAttribute("global_factors");
3457 if (s != "")
3458 s += ";";
3459 s += out->GetName();
3460 p3->setStringAttribute("global_factors", s);
3461 Info(
3462 "Multiply",
3463 "Flagged %s as a global factor in channel %s (is applied to all current and future samples in the channel)",
3464 out->GetName(), p3->GetName());
3465 return xRooNode(out, *this);
3466 }
3467
3468 } else if (auto p4 = get<RooAbsPdf>(); p4 && !(fParent && fParent->get<RooRealSumPdf>())) {
3469 // multiply the coefs (if this isn't part of a RooAddPdf or RooRealSumPdf then we will eventually throw exception
3470 return coefs().Multiply(child);
3471 } else if (auto p5 = get<RooAbsReal>(); p5 && (!get<RooAbsPdf>() || (fParent && fParent->get<RooRealSumPdf>()))) {
3472 // replace this obj with a RooProduct to allow for multiplication
3473
3474 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
3475 std::set<RooAbsArg *> cl;
3476 for (auto &arg : p5->clients()) {
3477 cl.insert(arg);
3478 }
3479
3480 // if multiple clients, see if only one client is in parentage route
3481 // if so, then assume thats the only client we should replace in
3482 if (cl.size() > 1) {
3483 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
3484 cl.clear();
3485 cl.insert(fParent->get<RooAbsArg>());
3486 } else {
3487 Warning("Multiply", "Scaling %s that has multiple clients", p5->GetName());
3488 }
3489 }
3490
3491 auto new_p = acquireNew<RooProduct>(TString::Format("prod_%s", p5->GetName()), p5->GetTitle(), RooArgList(*p5));
3492 // copy attributes over
3493 for (auto &a : p5->attributes())
3494 new_p->setAttribute(a.c_str());
3495 for (auto &a : p5->stringAttributes())
3496 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
3497 if (!new_p->getStringAttribute("alias"))
3498 new_p->setStringAttribute("alias", p5->GetName());
3499 auto old_p = p5;
3500 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
3501 for (auto arg : cl) {
3502 arg->redirectServers(RooArgSet(*new_p), false, true);
3503 }
3504
3505 fComp = new_p;
3506 return Multiply(child);
3507 }
3508
3509 // before giving up here, assume user wanted a norm factor type if child is just a name
3510 if (!child.get() && strlen(opt) == 0)
3511 return Multiply(child, "norm");
3512
3513 throw std::runtime_error(
3514 TString::Format("Cannot multiply %s by %s%s", GetPath().c_str(), child.GetName(),
3515 (!child.get() && strlen(opt) == 0) ? " (forgot to specify factor type?)" : ""));
3516}
3517
3519{
3520
3521 auto p5 = get<RooAbsArg>();
3522 if (!p5) {
3523 throw std::runtime_error("Only replacement of RooAbsArg is supported");
3524 }
3525 node.convertForAcquisition(*this, "func");
3526
3527 auto new_p = node.get<RooAbsArg>();
3528 if (!new_p) {
3529 throw std::runtime_error(TString::Format("Cannot replace with %s", node.GetName()));
3530 }
3531 auto out = acquire(node.fComp);
3532 new_p = std::dynamic_pointer_cast<RooAbsArg>(out).get();
3533
3534 std::set<RooAbsArg *> cl;
3535 for (auto &arg : p5->clients()) {
3536 if (arg == new_p)
3537 continue; // do not replace in self ... although redirectServers will prevent that anyway
3538 cl.insert(arg);
3539 }
3540
3541 // if multiple clients, see if only one client is in parentage route
3542 // if so, then assume thats the only client we should replace in
3543 if (cl.size() > 1) {
3544 if (fParent && fParent->get<RooAbsArg>() && cl.count(fParent->get<RooAbsArg>()) > 0) {
3545 cl.clear();
3546 cl.insert(fParent->get<RooAbsArg>());
3547 } else {
3548 std::stringstream clientList;
3549 for (auto c : cl)
3550 clientList << c->GetName() << ",";
3551 Warning("Replace", "Replacing %s in all clients: %s", p5->GetName(), clientList.str().c_str());
3552 }
3553 }
3554
3555 new_p->setAttribute(Form("ORIGNAME:%s", p5->GetName())); // used in redirectServers to say what this replaces
3556 for (auto arg : cl) {
3557 // if RooFormulaVar need to ensure the internal formula has been "constructed" otherwise will try to construct
3558 // it from the original expression that may have old parameter in it.
3559 if (auto p = dynamic_cast<RooFormulaVar *>(arg))
3560 p->ok(); // triggers creation of RooFormula
3561 arg->redirectServers(RooArgSet(*new_p), false, true);
3562 }
3563 return node;
3564}
3565
3567{
3568
3569 class AutoUpdater {
3570 public:
3571 AutoUpdater(xRooNode &_n) : n(_n) {}
3572 ~AutoUpdater() { n.browse(); }
3573 xRooNode &n;
3574 };
3575 AutoUpdater xxx(*this);
3576
3577 if (!get() && fParent) {
3578 // try to 'create' object based on parentage
3579 // add child as a temporary child to help with decision making
3580 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
3581 try {
3582 fComp = fParent->Add(*this, "+").fComp;
3583 } catch (...) {
3584 resize(size() - 1);
3585 std::rethrow_exception(std::current_exception());
3586 }
3587 resize(size() - 1); // remove the temporarily added node
3588 }
3589
3590 if (auto p = mainChild(); p) {
3591 // variations applied to the main child if has one
3592 return p.Vary(child);
3593 }
3594
3595 if (auto s = get<RooSimultaneous>(); s && s->indexCat().IsA() == RooCategory::Class()) {
3596 // name is used as cat label
3597 std::string label = child.GetName();
3598 if (auto pos = label.find('='); pos != std::string::npos)
3599 label = label.substr(pos + 1);
3600 if (!s->indexCat().hasLabel(label)) {
3601 // auto idx = static_cast<const RooCategory &>(s->indexCat()).nextAvailableStateIndex(); - can't access,
3602 // protected method ... will have to just assume indices stay in sync
3603 // ensure added to category in any of our datasets too
3604 for (auto _ds : datasets()) {
3605 if (auto bb = _ds->getBrowsable(".sourceds")) {
3606 _ds = bb;
3607 } // shouldn't happen
3608 auto dsCat = _ds->robs()[s->indexCat().GetName()]->get<RooCategory>();
3609 if (!dsCat) {
3610 throw std::runtime_error(TString::Format("Failed to find %s regular observable in %s dataset",
3611 s->indexCat().GetName(), _ds->GetName()));
3612 }
3613 dsCat->defineType(label.c_str());
3614 }
3615 // adding to the index cat after, so that we don't need to generate subdatasets in the call to datasets() above
3616 // (missing cat will trigger cut)
3617 static_cast<RooCategory &>(const_cast<RooAbsCategoryLValue &>(s->indexCat())).defineType(label.c_str());
3618 }
3619 std::shared_ptr<TObject> out;
3620 child.convertForAcquisition(*this);
3621 if (child.get<RooAbsPdf>()) {
3622 out = acquire(child.fComp); // may create a channel from a histogram
3623 } else if (!child.fComp) {
3624 out = acquireNew<RooProdPdf>(TString::Format("%s_%s", s->GetName(), label.c_str()),
3625 (strlen(child.GetTitle())) ? child.GetTitle() : label.c_str(), RooArgList());
3626 Info("Vary", "Created channel RooProdPdf::%s in model %s", out->GetName(), s->GetName());
3627 }
3628
3629 if (auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out); _pdf) {
3630 // before adding the channel, we need to see if we are about to add any globs, and if necessary we must update
3631 // the dataset globs
3632 std::set<RooAbsData *> dsToUpdate;
3633 for (auto _ds : datasets()) {
3634 if (auto bb = _ds->getBrowsable(".sourceds")) {
3635 _ds = bb;
3636 } // shouldn't happen
3637 if (_ds->get<RooAbsData>()->getGlobalObservables()) {
3638 dsToUpdate.insert(_ds->get<RooAbsData>());
3639 }
3640 }
3641 if (!dsToUpdate.empty()) {
3643 _pdf->leafNodeServerList(&leafs);
3644 std::unique_ptr<RooAbsCollection> globals(leafs.selectByAttrib("global", true));
3645 for (auto _ds : dsToUpdate) {
3646 std::string alist;
3648 globs.addClone(*_ds->getGlobalObservables());
3649 for (auto &aa : *globals) {
3650 if (!globs.contains(*aa)) {
3651 globs.addClone(*aa);
3652 alist += std::string(aa->GetName()) + ",";
3653 }
3654 }
3655 if (!alist.empty()) {
3656 Warning("Vary", "Adding %s to global observables of %s", alist.c_str(), _ds->GetName());
3657 _ds->setGlobalObservables(globs);
3658 }
3659 }
3660 }
3661
3662 s->addPdf(*_pdf, label.c_str());
3663 sterilize();
3664 // clear children for reload and update shared axis
3665 clear();
3666 fXAxis.reset();
3667 browse();
3668 return xRooNode(TString::Format("%s=%s", s->indexCat().GetName(), label.data()), _pdf, *this);
3669 }
3670
3671 } else if (auto p = get<RooStats::HistFactory::FlexibleInterpVar>(); p) {
3672
3673 // child needs to be a constvar ...
3674 child.convertForAcquisition(*this);
3675 auto _c = child.get<RooConstVar>();
3676 if (!_c && child.get()) {
3677 throw std::runtime_error("Only pure consts can be set as variations of a flexible interpvar");
3678 }
3679#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3680 double value = (_c ? _c->getVal() : p->_nominal);
3681 double nomVal = p->_nominal;
3682#else
3683 double value = (_c ? _c->getVal() : p->nominal());
3684 double nomVal = p->nominal();
3685#endif
3686
3687 TString cName(child.GetName());
3688 if (cName == "nominal") {
3689 p->setNominal(value);
3690 return *(this->variations().at(cName.Data()));
3691 }
3692 if (cName.CountChar('=') != 1) {
3693 throw std::runtime_error("unsupported variation form");
3694 }
3695 std::string parName = cName(0, cName.Index('='));
3696 double parVal = TString(cName(cName.Index('=') + 1, cName.Length())).Atof();
3697 if (parVal != 1 && parVal != -1) {
3698 throw std::runtime_error("unsupported variation magnitude");
3699 }
3700 bool high = parVal > 0;
3701
3702 if (parName.empty()) {
3703 p->setNominal(value);
3704 } else {
3705 auto v = fParent->getObject<RooRealVar>(parName);
3706 if (!v)
3707 v = fParent->acquire<RooRealVar>(parName.c_str(), parName.c_str(), -5, 5);
3708 if (!v->hasError())
3709 v->setError(1);
3710
3711 if (!p->findServer(*v)) {
3712#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3713 p->_paramList.add(*v);
3714 p->_low.push_back(0);
3715 p->_high.push_back(0);
3716 p->_interpCode.push_back(4);
3717#else
3718 const_cast<RooListProxy &>(p->variables()).add(*v);
3719 const_cast<std::vector<double> &>(p->low()).push_back(0);
3720 const_cast<std::vector<double> &>(p->high()).push_back(0);
3721 const_cast<std::vector<int> &>(p->interpolationCodes()).push_back(4);
3722#endif
3723 v->setAttribute(Form("SYMMETRIC%s_%s", high ? "+" : "-", GetName())); // flag for symmetrized
3724 }
3725
3726 if (high) {
3727 p->setHigh(*v, value);
3728 if (v->getAttribute(Form("SYMMETRIC+_%s", GetName()))) {
3729 p->setLow(*v, 2 * nomVal - value);
3730 }
3731 v->setAttribute(Form("SYMMETRIC-_%s", GetName()), false);
3732 } else {
3733 p->setLow(*v, value);
3734 if (v->getAttribute(Form("SYMMETRIC-_%s", GetName()))) {
3735 p->setHigh(*v, 2 * nomVal - value);
3736 }
3737 v->setAttribute(Form("SYMMETRIC+_%s", GetName()), false);
3738 }
3739
3740 /*if (!unconstrained && fParent->pars()[v->GetName()].constraints().empty()) {
3741 fParent->pars()[v->GetName()].constraints().add("normal");
3742 }*/
3743 }
3744 return *(this->variations().at(cName.Data()));
3745 } else if (auto p2 = get<PiecewiseInterpolation>(); p2) {
3746 TString cName(child.GetName());
3747 if (cName.CountChar('=') != 1) {
3748 throw std::runtime_error("unsupported variation form");
3749 }
3750 TString parName = cName(0, cName.Index('='));
3751 double parVal = TString(cName(cName.Index('=') + 1, cName.Length())).Atof();
3752 if (parVal != 1 && parVal != -1) {
3753 throw std::runtime_error("unsupported variation magnitude");
3754 }
3755#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3756 RooHistFunc *f = dynamic_cast<RooHistFunc *>(p2->_nominal.absArg());
3757 if (!f) {
3758 throw std::runtime_error(
3759 TString::Format("Interpolating %s instead of RooHistFunc", p2->_nominal.absArg()->ClassName()));
3760 }
3761#else
3762 RooHistFunc *f = dynamic_cast<RooHistFunc *>(const_cast<RooAbsReal *>(p2->nominalHist()));
3763 if (!f) {
3764 throw std::runtime_error(
3765 TString::Format("Interpolating %s instead of RooHistFunc", p2->nominalHist()->ClassName()));
3766 }
3767#endif
3768 RooHistFunc *nomf = f;
3769 RooHistFunc *otherf = nullptr;
3770 size_t i = 0;
3771 for (auto par : p2->paramList()) {
3772 if (parName == par->GetName()) {
3773 f = dynamic_cast<RooHistFunc *>((parVal > 0 ? p2->highList() : p2->lowList()).at(i));
3774 otherf = dynamic_cast<RooHistFunc *>((parVal > 0 ? p2->lowList() : p2->highList()).at(i));
3775 break;
3776 }
3777 i++;
3778 }
3779 if (i == p2->paramList().size() && !child.get<RooAbsReal>()) {
3780
3781 // need to add the parameter
3782 auto v = acquire<RooRealVar>(parName, parName, -5, 5);
3783 if (!v->hasError())
3784 v->setError(1);
3785
3786 std::shared_ptr<RooHistFunc> up(
3787 static_cast<RooHistFunc *>(f->Clone(Form("%s_%s_up", f->GetName(), parName.Data()))));
3788 std::shared_ptr<RooHistFunc> down(
3789 static_cast<RooHistFunc *>(f->Clone(Form("%s_%s_down", f->GetName(), parName.Data()))));
3790 // RooHistFunc doesn't clone it's data hist ... do it ourself (will be cloned again if imported into a ws)
3791#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3792 std::unique_ptr<RooDataHist> h1(
3793 static_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", up->GetName()))));
3794 std::unique_ptr<RooDataHist> h2(
3795 static_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", down->GetName()))));
3796 up->_dataHist = dynamic_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", up->GetName())));
3797 down->_dataHist = dynamic_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", down->GetName())));
3798#else
3799 up->cloneAndOwnDataHist(TString::Format("hist_%s", up->GetName()));
3800 down->cloneAndOwnDataHist(TString::Format("hist_%s", down->GetName()));
3801#endif
3802 auto ups = std::dynamic_pointer_cast<RooHistFunc>(acquire(up, false, true));
3803 auto downs = std::dynamic_pointer_cast<RooHistFunc>(acquire(down, false, true));
3804#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3805 p2->_highSet.add(*ups.get());
3806 p2->_lowSet.add(*downs.get());
3807 p2->_interpCode.push_back(4);
3808 p2->_paramSet.add(*v);
3809#else
3810 const_cast<RooArgList &>(p2->highList()).add(*ups);
3811 const_cast<RooArgList &>(p2->lowList()).add(*downs);
3812 const_cast<std::vector<int> &>(p2->interpolationCodes()).push_back(4);
3813 const_cast<RooArgList &>(p2->paramList()).add(*v);
3814#endif
3815 p2->setValueDirty();
3816 f = ((parVal > 0) ? ups : downs).get();
3817 otherf = ((parVal > 0) ? downs : ups).get();
3818 // start off with everything being symmetric
3819 f->setStringAttribute("symmetrizes", otherf->GetName());
3820 f->setStringAttribute("symmetrize_nominal", nomf->GetName());
3821 otherf->setStringAttribute("symmetrized_by", f->GetName());
3822
3823 // constrain par if required
3824 /*if (!unconstrained && fParent->pars()[v->GetName()].constraints().empty()) {
3825 fParent->pars()[v->GetName()].constraints().add("normal");
3826 }*/
3827 }
3828
3829 // child.convertForAcquisition(*this);
3830 if (f) {
3831 if (child.get())
3832 xRooNode("tmp", *f, *this) = *child.get();
3833 f->setValueDirty();
3834 xRooNode out(*f, *this);
3835 out.sterilize();
3836 return out;
3837 }
3838
3839#if ROOT_VERSION_CODE > ROOT_VERSION(6, 37, 00)
3840 } else if (auto pmr = get<RooMultiReal>(); pmr) {
3841 TString cName(child.GetName());
3842 if (cName.CountChar('=') != 1) {
3843 throw std::runtime_error("unsupported variation form");
3844 }
3845 TString parName = cName(0, cName.Index('='));
3846 TString parVal = TString(cName(cName.Index('=') + 1, cName.Length()));
3847
3848 // add parVal to categorical if not already there
3849 if (!pmr->indexCategory()->hasLabel(parVal.Data())) {
3850 dynamic_cast<RooCategory &>(*(pmr->indexCategory())).defineType(parVal);
3851 }
3852 auto idx = pmr->indexCategory()->lookupIndex(parVal.Data());
3853 if (idx < 0) {
3854 throw std::runtime_error("Invalid index");
3855 }
3856 // add child to list ... use copy of "nominal" (0th) if child is empty
3857 child.convertForAcquisition(*this);
3858 auto _c = child.get<RooAbsReal>();
3859 if (!_c) {
3860 if (pmr->getModelList().empty()) {
3861 throw std::runtime_error("No real function given for variation, and no nominal function to clone");
3862 }
3863 _c = std::dynamic_pointer_cast<RooAbsReal>(
3864 acquire(std::shared_ptr<TObject>(pmr->getModelList().at(0)->Clone(
3865 TString::Format("%s_%s", get()->GetName(), child.GetName()))),
3866 false, true))
3867 .get();
3868 _c->setStringAttribute("alias", child.GetName());
3869 }
3870 const_cast<RooListProxy &>(pmr->getModelList()).add(*_c);
3871 return xRooNode(*_c, *this);
3872
3873#endif
3874
3875 } else if (auto p3 = get<RooConstVar>(); p3) {
3876
3877 // never vary the universal consts ... its too dangerous
3878 if (p3->getAttribute("RooRealConstant_Factory_Object")) {
3879 throw std::runtime_error("Cannot vary pure constants");
3880 }
3881
3882 // inject a FlexibleInterpVar ...
3883
3884 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
3885 std::set<RooAbsArg *> cl;
3886 for (auto &arg : p3->clients()) {
3887 cl.insert(arg);
3888 }
3889 // if multiple clients, see if only one client is in parentage route
3890 // if so, then assume thats the only client we should replace in
3891 if (cl.size() > 1) {
3892 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
3893 cl.clear();
3894 cl.insert(fParent->get<RooAbsArg>());
3895 } else {
3896 Warning("Vary", "Varying %s that has multiple clients", p3->GetName());
3897 }
3898 }
3899 p3->setStringAttribute("origName", p3->GetName());
3900 TString n = p3->GetName();
3901 p3->SetName(Form("%s_nominal", p3->GetName())); // if problems should perhaps not rename here
3902
3904 std::vector<double>(), std::vector<double>());
3905
3906 // copy attributes over
3907 for (auto &a : p3->attributes())
3908 new_p->setAttribute(a.c_str());
3909 for (auto &a : p3->stringAttributes())
3910 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
3911 // if (!new_p->getStringAttribute("alias")) new_p->setStringAttribute("alias",p->GetName());
3912 auto old_p = p3;
3913 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
3914 for (auto arg : cl) {
3915 arg->redirectServers(RooArgSet(*new_p), false, true);
3916 }
3917
3918 fComp = new_p;
3919 return Vary(child);
3920
3921 } else if (auto p4 = get<RooAbsReal>(); p4) {
3922 // inject an interpolation node
3923
3924 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
3925 std::set<RooAbsArg *> cl;
3926 for (auto &arg : p4->clients()) {
3927 cl.insert(arg);
3928 }
3929 // if multiple clients, see if only one client is in parentage route
3930 // if so, then assume thats the only client we should replace in
3931 if (cl.size() > 1) {
3932 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
3933 cl.clear();
3934 cl.insert(fParent->get<RooAbsArg>());
3935 } else {
3936 Warning("Vary", "Varying %s that has multiple clients", p4->GetName());
3937 }
3938 }
3939 p4->setStringAttribute("origName", p4->GetName());
3940 TString n = p4->GetName();
3941 p4->SetName(Form("%s_nominal", p4->GetName())); // if problems should perhaps not rename here
3942
3943#if ROOT_VERSION_CODE > ROOT_VERSION(6, 37, 00)
3944 std::shared_ptr<RooAbsArg> new_p;
3945 // if alphanumeric variation name is not a 1 or -1, inject a RooMultiReal not a PiecewiseInterpolation ...
3946 TString cName(child.GetName());
3947 if (cName.CountChar('=') == 1) {
3948 TString parName = cName(0, cName.Index('='));
3949 double parVal = TString(cName(cName.Index('=') + 1, cName.Length())).Atof();
3950 if (parVal != 1 && parVal != -1) {
3951 // get the idxCat ...
3953 if (!idxCat) {
3954 throw std::runtime_error("Failed to acquire categorical index for RooMultiReal");
3955 }
3956 // add an index if none already
3957 if (idxCat->numTypes() == 0) {
3958 idxCat->defineType("nominal");
3959 }
3960 Info("Vary", "Creating a RooMultiReal with category %s", idxCat->GetName());
3962 n, p4->GetTitle(), *idxCat,
3963 RooArgList()); // can't pass nominal model as that will force defining types on category
3964 const_cast<RooListProxy &>(newFunc->getModelList()).add(*p4);
3965 new_p = newFunc;
3966 }
3967 }
3968 if (!new_p) {
3970 }
3971
3972#else
3974#endif
3975
3976 // copy attributes over
3977 for (auto &a : p4->attributes())
3978 new_p->setAttribute(a.c_str());
3979 for (auto &a : p4->stringAttributes())
3980 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
3981 // if (!new_p->getStringAttribute("alias")) new_p->setStringAttribute("alias",p->GetName());
3982 auto old_p = p4;
3983 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
3984 for (auto arg : cl) {
3985 arg->redirectServers(RooArgSet(*new_p), false, true);
3986 }
3987
3988 fComp = new_p;
3989 return Vary(child);
3990 }
3991
3992 Print();
3993 throw std::runtime_error(TString::Format("Cannot vary %s with %s", GetName(), child.GetName()));
3994}
3995
3997{
3999}
4000
4001bool xRooNode::SetContent(double value, const char *par, double val)
4002{
4003 return SetContents(RooConstVar(GetName(), GetTitle(), value), par, val);
4004}
4005
4008 {
4009 if (x && b)
4010 x->setBinning(*b);
4011 if (b)
4012 delete b;
4013 }
4014 RooRealVar *x = nullptr;
4015 RooAbsBinning *b = nullptr;
4016};
4017
4019{
4020
4021 if (!get()) {
4022 fComp = std::shared_ptr<TObject>(const_cast<TObject *>(&o), [](TObject *) {});
4023 if (fParent && !fParent->find(GetName())) {
4024 // either a temporary or a placeholder so need to try genuinely adding
4025 fComp = fParent->Add(*this, "+").fComp;
4026 if (auto a = get<RooAbsArg>(); a && strcmp(a->GetName(), GetName()) && !a->getStringAttribute("alias")) {
4027 a->setStringAttribute("alias", GetName());
4028 }
4029 if (!fComp)
4030 throw std::runtime_error("Cannot determine type");
4031 return *this;
4032 }
4033 }
4034
4035 if (auto h = dynamic_cast<const TH1 *>(&o); h) {
4036 /*auto f = get<RooHistFunc>();
4037 if (!f) {
4038 // if it's a RooProduct locate child with the same name
4039 if (get<RooProduct>()) {
4040 f = factors()[GetName()]->get<RooHistFunc>();
4041 }
4042
4043
4044
4045 }*/
4046 bool _isData = get<RooAbsData>();
4048 if (_isData) {
4049 // need to ensure x-axis matches this h
4050 auto ax = GetXaxis();
4051 if (!ax)
4052 throw std::runtime_error("no xaxis");
4053 auto _v = dynamic_cast<RooRealVar *>(ax->GetParent());
4054 if (_v) {
4055 _b.x = _v;
4056 _b.b = dynamic_cast<RooAbsBinning *>(_v->getBinningPtr(nullptr)->Clone());
4057 if (h->GetXaxis()->IsVariableBinSize()) {
4058 _v->setBinning(RooBinning(h->GetNbinsX(), h->GetXaxis()->GetXbins()->GetArray()));
4059 } else {
4060 _v->setBinning(RooUniformBinning(h->GetXaxis()->GetXmin(), h->GetXaxis()->GetXmax(), h->GetNbinsX()));
4061 }
4062 }
4063 }
4064
4065 if (true) {
4066 for (int bin = 1; bin <= h->GetNbinsX(); bin++) {
4067 SetBinContent(bin, h->GetBinContent(bin));
4068 /*double value = h->GetBinContent(bin);
4069 auto bin_pars = f->dataHist().get(bin - 1);
4070 if (f->getAttribute("density")) {
4071 value /= f->dataHist().binVolume(*bin_pars);
4072 }
4073 f->dataHist().set(*bin_pars, value);*/
4074 if (!_isData && h->GetSumw2N() && !SetBinError(bin, h->GetBinError(bin)))
4075 throw std::runtime_error("Failed setting stat error");
4076 }
4077 return *this;
4078 }
4079 } else if (auto _c = dynamic_cast<const RooConstVar *>(&o); _c) {
4080
4081 if (auto a = get<RooAbsArg>();
4082 (a && a->isFundamental()) || get<RooConstVar>() || get<RooStats::HistFactory::FlexibleInterpVar>()) {
4083 SetBinContent(1, _c->getVal());
4084 return *this;
4085 } else if (get<RooAbsData>()) { // try to do assignment to a dataset (usually setting a bin content)
4086 SetBinContent(0, _c->getVal());
4087 return *this;
4088 }
4089 }
4090
4091 throw std::runtime_error("Assignment failed");
4092
4093 /*
4094
4095 if (fParent && !fParent->mk()) {
4096 throw std::runtime_error("mk failure");
4097 }
4098
4099 if (fComp) return *this;
4100
4101 if (o.InheritsFrom("RooAbsArg")) {
4102 fComp = acquire(std::shared_ptr<TObject>(const_cast<TObject*>(&o),[](TObject* o){}));
4103 std::dynamic_pointer_cast<RooAbsArg>(fComp)->setStringAttribute("alias",GetName());
4104 }
4105
4106 if (fComp && fParent) {
4107 fParent->incorporate(fComp);
4108 }
4109
4110
4111 return *this;
4112 */
4113}
4114
4115void xRooNode::_fit_(const char *constParValues, const char *options)
4116{
4117 try {
4118 // re-float all poi and np before fitting
4119 np().get<RooArgList>()->setAttribAll("Constant", false);
4120 poi().get<RooArgList>()->setAttribAll("Constant", false);
4121 auto _pars = pars();
4122 // std::unique_ptr<RooAbsCollection> snap(_pars.argList().snapshot());
4123 TStringToken pattern(constParValues, ",");
4124 std::map<RooAbsRealLValue *, double> valsToSet;
4125 while (pattern.NextToken()) {
4126 auto idx = pattern.Index('=');
4127 TString pat = (idx == -1) ? TString(pattern) : TString(pattern(0, idx));
4128 double val =
4129 (idx == -1) ? std::numeric_limits<double>::quiet_NaN() : TString(pattern(idx + 1, pattern.Length())).Atof();
4130 bool foundArg = false;
4131 for (auto p : _pars.argList()) {
4132 if (TString(p->GetName()).Contains(TRegexp(pat, true))) {
4133 foundArg = true;
4134 p->setAttribute("Constant", true);
4135 if (!std::isnan(val)) {
4136 valsToSet[dynamic_cast<RooAbsRealLValue *>(p)] = val;
4137 // dynamic_cast<RooAbsRealLValue *>(p)->setVal(val); // don't set yet, to allow for asimov dataset
4138 // creation based on current values
4139 }
4140 }
4141 }
4142 if (!foundArg) {
4143 throw std::runtime_error(std::string("Unrecognised parameter: ") + pat.Data());
4144 }
4145 }
4146
4147 // parse options
4148 TStringToken pattern2(options, ",");
4150 while (pattern2.NextToken()) {
4151 auto idx = pattern2.Index('=');
4152 TString pat = (idx == -1) ? TString(pattern2) : TString(pattern2(0, idx));
4153 TString val = TString(pattern2(idx + 1, pattern2.Length()));
4154 if (auto o = defaultOpts->FindObject(pat)) {
4155 defaultOpts->Remove(o);
4156 delete o;
4157 }
4158 defaultOpts->Add(new RooCmdArg(pat, val.IsDec() ? val.Atoi() : 0, 0, val.IsFloat() ? val.Atof() : 0., 0.,
4159 val.IsAlpha() ? val : nullptr));
4160 }
4161
4162 // use the first selected dataset
4163 auto _dsets = datasets();
4164 TString dsetName = "";
4165 for (auto &d : _dsets) {
4166 if (d->get()->TestBit(1 << 20)) {
4167 dsetName = d->get()->GetName();
4168 break;
4169 }
4170 }
4171 auto _nll = nll(dsetName.Data(), *defaultOpts);
4172 // can now set the values
4173 for (auto [p, v] : valsToSet) {
4174 p->setVal(v);
4175 }
4176 _nll.fitConfigOptions()->SetValue("LogSize", 65536);
4177 _nll.fitConfig()->MinimizerOptions().SetPrintLevel(0);
4178 auto fr = _nll.minimize();
4179 //_pars.argList() = *snap; // restore values - irrelevant as SetFitResult will restore values
4180 if (!fr.get())
4181 throw std::runtime_error("Fit Failed");
4182 SetFitResult(fr.get());
4184 for (unsigned int i = 0; i < fr->numStatusHistory(); i++) {
4185 statusCodes += TString::Format("\n%s = %d", fr->statusLabelHistory(i), fr->statusCodeHistory(i));
4186 }
4187 const TGWindow *w =
4188 (gROOT->GetListOfBrowsers()->At(0))
4189 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4190 : gClient->GetRoot();
4191 TString gofResult = "";
4192 if (_nll.fOpts->find("GoF")) {
4193 gofResult = TString::Format("GoF p-value = %g\n", fr->constPars().getRealValue(".pgof"));
4194 }
4195 if (fr->status() != 0) {
4196 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished with Bad Status Code",
4197 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n%s-------------%s",
4198 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), gofResult.Data(),
4199 statusCodes.Data()),
4201 } else if (fr->covQual() != 3 && _nll.fitConfig()->ParabErrors()) {
4202 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished with Bad Covariance Quality",
4203 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n%s-------------%s",
4204 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), gofResult.Data(),
4205 statusCodes.Data()),
4207 } else {
4208 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished Successfully",
4209 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n%s-------------%s",
4210 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), gofResult.Data(),
4211 statusCodes.Data()));
4212 }
4213 TBrowser *b = nullptr;
4214 for (auto a : *gROOT->GetListOfBrowsers()) {
4215 b = dynamic_cast<TBrowser *>(a);
4216 if (b && GetTreeItem(b)) {
4217 break;
4218 }
4219 }
4220 if (b) {
4221 auto p = GetTreeItem(b);
4222 while (p) {
4223 if (TString(p->GetText()).BeginsWith("RooWorkspace::")) {
4224 // found the workspace ... refresh this node, and if there's a fits node, refresh that
4225 if (auto bi = dynamic_cast<TRootBrowser *>(b->GetBrowserImp())) {
4226 if (auto fb = dynamic_cast<TGFileBrowser *>(bi->GetActBrowser())) {
4227 fb->DoubleClicked(p, 0);
4228 if (auto first = p->GetFirstChild()) {
4229 do {
4230 if (first->IsOpen() && TString(first->GetText()) == "fits") {
4231 fb->DoubleClicked(first, 0);
4232 }
4233 } while ((first = first->GetNextSibling()));
4234 }
4235 }
4236 }
4237 break;
4238 } else {
4239 p = p->GetParent();
4240 }
4241 }
4242 }
4243 } catch (const std::exception &e) {
4244 new TGMsgBox(
4245 gClient->GetRoot(),
4246 (gROOT->GetListOfBrowsers()->At(0))
4247 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4248 : gClient->GetRoot(),
4249 "Exception", e.what(), kMBIconExclamation, kMBOk); // deletes self on dismiss?
4250 }
4251}
4252
4254{
4255 try {
4256 datasets().Add(datasetName, expected ? "asimov" : "toy");
4257 // refresh datasets folder of workspace
4258 TBrowser *b = nullptr;
4259 for (auto a : *gROOT->GetListOfBrowsers()) {
4260 b = dynamic_cast<TBrowser *>(a);
4261 if (b && GetTreeItem(b)) {
4262 break;
4263 }
4264 }
4265 if (b) {
4266 auto p = GetTreeItem(b);
4267 while (p) {
4268 if (TString(p->GetText()).BeginsWith("RooWorkspace::")) {
4269 // found the workspace ... refresh this node, and if there's a datasets node, refresh that
4270 if (auto bi = dynamic_cast<TRootBrowser *>(b->GetBrowserImp())) {
4271 if (auto fb = dynamic_cast<TGFileBrowser *>(bi->GetActBrowser())) {
4272 fb->DoubleClicked(p, 0);
4273 if (auto first = p->GetFirstChild()) {
4274 do {
4275 if (first->IsOpen() && TString(first->GetText()) == "datasets") {
4276 fb->DoubleClicked(first, 0);
4277 }
4278 } while ((first = first->GetNextSibling()));
4279 }
4280 }
4281 }
4282 break;
4283 } else {
4284 p = p->GetParent();
4285 }
4286 }
4287 }
4288 } catch (const std::exception &e) {
4289 new TGMsgBox(
4290 gClient->GetRoot(),
4291 (gROOT->GetListOfBrowsers()->At(0))
4292 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4293 : gClient->GetRoot(),
4294 "Exception", e.what(),
4295 kMBIconExclamation); // deletes self on dismiss?
4296 }
4297}
4298
4299void xRooNode::_scan_(const char *what, double nToys, const char *xvar, int nBinsX, double lowX,
4300 double highX /*, const char*, int, double, double*/, const char *constParValues,
4301 const char *options)
4302{
4303 try {
4306
4307 // use the first selected dataset
4308 auto _dsets = datasets();
4309 TString dsetName = "";
4310 for (auto &d : _dsets) {
4311 if (d->get()->TestBit(1 << 20)) {
4312 dsetName = d->get()->GetName();
4313 break;
4314 }
4315 }
4316 auto _pars = pars();
4317 std::unique_ptr<RooAbsCollection> snap(_pars.argList().snapshot());
4318 TStringToken pattern(constParValues, ",");
4319 while (pattern.NextToken()) {
4320 auto idx = pattern.Index('=');
4321 TString pat = (idx == -1) ? TString(pattern) : TString(pattern(0, idx));
4322 double val =
4323 (idx == -1) ? std::numeric_limits<double>::quiet_NaN() : TString(pattern(idx + 1, pattern.Length())).Atof();
4324 bool foundArg = false;
4325 for (auto par : _pars.argList()) {
4326 if (TString(par->GetName()).Contains(TRegexp(pat, true))) {
4327 foundArg = true;
4328 par->setAttribute("Constant", true);
4329 if (!std::isnan(val)) {
4330 dynamic_cast<RooAbsRealLValue *>(par)->setVal(val);
4331 }
4332 }
4333 }
4334 if (!foundArg) {
4335 throw std::runtime_error(std::string("Unrecognised parameter: ") + pat.Data());
4336 }
4337 }
4338
4339 // parse options
4340 TStringToken pattern2(options, ",");
4342 while (pattern2.NextToken()) {
4343 auto idx = pattern2.Index('=');
4344 TString pat = (idx == -1) ? TString(pattern2) : TString(pattern2(0, idx));
4345 TString val = TString(pattern2(idx + 1, pattern2.Length()));
4346 if (auto o = defaultOpts->FindObject(pat)) {
4347 defaultOpts->Remove(o);
4348 delete o;
4349 }
4350 defaultOpts->Add(new RooCmdArg(pat, val.IsDec() ? val.Atoi() : 0, 0, val.IsFloat() ? val.Atof() : 0., 0.,
4351 val.IsAlpha() ? val : nullptr));
4352 }
4353
4354 auto hs = nll(dsetName.Data(), *defaultOpts).hypoSpace(sXvar);
4355 hs.SetName(TUUID().AsString());
4356 if (nToys) {
4357 sWhat += " toys";
4358 if (nToys > 0) {
4359 sWhat += TString::Format("=%g", nToys);
4360 }
4361 }
4362 hs.SetTitle(sWhat + " scan" + ((dsetName != "") ? TString::Format(" [data=%s]", dsetName.Data()) : ""));
4363 int scanStatus = hs.scan(sWhat + " visualize", nBinsX, lowX, highX);
4364 if (scanStatus != 0) {
4365 new TGMsgBox(
4366 gClient->GetRoot(),
4367 (gROOT->GetListOfBrowsers()->At(0))
4368 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4369 : gClient->GetRoot(),
4370 "Scan Finished with Bad Status Code",
4371 TString::Format("%s\nData = %s\nScan Status Code = %d", hs.GetName(), dsetName.Data(), scanStatus),
4373 }
4374 if (ws()) {
4375 if (auto res = hs.result())
4376 ws()->import(*res);
4377 }
4378
4379 _pars.argList() = *snap; // restore pars
4380
4381 TBrowser *b = nullptr;
4382 for (auto a : *gROOT->GetListOfBrowsers()) {
4383 b = dynamic_cast<TBrowser *>(a);
4384 if (b && GetTreeItem(b)) {
4385 break;
4386 }
4387 }
4388 if (b) {
4389 auto p = GetTreeItem(b);
4390 while (p) {
4391 if (TString(p->GetText()).BeginsWith("RooWorkspace::")) {
4392 // found the workspace ... refresh this node, and if there's a scans node, refresh that
4393 if (auto bi = dynamic_cast<TRootBrowser *>(b->GetBrowserImp())) {
4394 if (auto fb = dynamic_cast<TGFileBrowser *>(bi->GetActBrowser())) {
4395 fb->DoubleClicked(p, 0);
4396 if (auto first = p->GetFirstChild()) {
4397 do {
4398 if (first->IsOpen() && TString(first->GetText()) == "scans") {
4399 fb->DoubleClicked(first, 0);
4400 }
4401 } while ((first = first->GetNextSibling()));
4402 }
4403 }
4404 }
4405 break;
4406 } else {
4407 p = p->GetParent();
4408 }
4409 }
4410 }
4411
4412 } catch (const std::exception &e) {
4413 new TGMsgBox(
4414 gClient->GetRoot(),
4415 (gROOT->GetListOfBrowsers()->At(0))
4416 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4417 : gClient->GetRoot(),
4418 "Exception", e.what(), kMBIconExclamation);
4419 }
4420}
4421
4422void xRooNode::_SetBinContent_(int bin, double value, const char *par, double parVal)
4423{
4424 try {
4425 SetBinContent(bin, value, strlen(par) > 0 ? par : nullptr, parVal);
4426 } catch (const std::exception &e) {
4427 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
4428 kMBIconExclamation); // deletes self on dismiss?
4429 }
4430}
4431
4433{
4434 try {
4435#if ROOT_VERSION_CODE > ROOT_VERSION(6, 29, 00)
4436 // if this is a collection of values, populate a TF1 and display as a dialog
4437 if (!get() && TString(GetName()).BeginsWith("!")) {
4438 browse();
4439 RooArgList args;
4440 for (auto a : *this) {
4441 if (auto arg = a->get<RooRealVar>())
4442 args.add(*arg);
4443 }
4444 TF1 f(GetName(), 0.0, 1.0, std::min(int(args.size()), 10));
4445 int i = 0;
4446 int j = 0;
4447 for (auto c : args) {
4448 j++;
4449 if (j < value) {
4450 continue;
4451 }
4452 auto v = dynamic_cast<RooRealVar *>(c);
4453 f.SetParName(i, c->GetName());
4454 if (v) {
4455 f.SetParLimits(i, v->getMin(), v->getMax());
4456 if (v->isConstant())
4457 f.FixParameter(i, v->getVal());
4458 else {
4459 f.SetParameter(i, v->getVal());
4460 f.SetParError(i, v->getError());
4461 }
4462 }
4463 i++;
4464 if (i == 10) {
4465 break; // max 10 pars shown
4466 }
4467 }
4468 int ret = 0;
4470 gClient->GetDefaultRoot(),
4471 (gROOT->GetListOfBrowsers()->At(0))
4472 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4473 : gClient->GetDefaultRoot(),
4474 &f, nullptr, &ret);
4475 if (ret) {
4476 // user has changed parameter values etc, propagate back to parameters
4477 for (i = 0; i < f.GetNpar(); i++) {
4478 auto c = args.find(f.GetParName(i));
4479 auto v = dynamic_cast<RooRealVar *>(c);
4480 if (v) {
4481 v->setVal(f.GetParameter(i));
4482 double low, high;
4483 f.GetParLimits(i, low, high);
4484 if (low == high) {
4485 v->setConstant(low); // if low==high==0 then is not marked constant
4486 } else {
4487 v->setRange(low, high);
4488 }
4489 }
4490 }
4491 }
4492 return;
4493 }
4494#endif
4495
4496 if (!SetContent(value))
4497 throw std::runtime_error("Failed to SetContent");
4498 } catch (const std::exception &e) {
4499 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
4500 kMBIconExclamation); // deletes self on dismiss?
4501 }
4502}
4503
4504bool xRooNode::SetBinContent(int bin, double value, const char *par, double parVal)
4505{
4506
4507 // create if needed
4508 if (!get()) {
4509 if (fParent && !find(GetName())) {
4510 // if have a binning we create a histogram to match it
4511 if (auto ax = GetXaxis(); ax) {
4512 std::shared_ptr<TH1D> h;
4513 auto _b = dynamic_cast<Axis2 *>(ax)->binning();
4514 auto t = TH1::AddDirectoryStatus();
4515 TH1::AddDirectory(false);
4516 if (_b->isUniform()) {
4517 h.reset(new TH1D(GetName(), GetTitle(), _b->numBins(), _b->lowBound(), _b->highBound()));
4518 } else {
4519 h.reset(new TH1D(GetName(), GetTitle(), _b->numBins(), _b->array()));
4520 }
4521 h->SetOption("nostyle"); // don't transfer style when added
4522 h->SetDirectory(nullptr);
4524 h->GetXaxis()->SetName(TString::Format("%s;%s", ax->GetParent()->GetName(), ax->GetName()));
4525 fComp = h;
4526 }
4527 fComp = fParent->Add(*this, "sample").fComp;
4528 }
4529 }
4530
4531 // if it's a RooProduct locate child with the same name
4532 if (get<RooProduct>()) {
4533 return factors()[GetName()]->SetBinContent(bin, value, par, parVal);
4534 }
4535
4536 if (get<RooAbsData>()) {
4537 if (auto _data = get<RooDataSet>(); _data) {
4538 auto _ax = (bin) ? GetXaxis() : nullptr;
4539 if (!_ax && bin) {
4540 throw std::runtime_error("Cannot determine binning to fill data");
4541 }
4542 if (_ax && _ax->GetNbins() < bin) {
4543 throw std::out_of_range(TString::Format("%s range %s only has %d bins", _ax->GetParent()->GetName(),
4544 _ax->GetName(), _ax->GetNbins()));
4545 }
4546 RooArgSet obs;
4547
4548 TString cut = "";
4549
4550 for (auto _c : coords()) { // coords() moves vars to their respective coordinates too
4551 if (auto _cat = _c->get<RooAbsCategoryLValue>(); _cat) {
4552 if (cut != "")
4553 cut += " && ";
4554 cut += TString::Format("%s==%d", _cat->GetName(), _cat->getCurrentIndex());
4555 obs.add(*_cat); // note: if we ever changed coords to return clones, would need to keep coords alive
4556 } else if (auto _rv = _c->get<RooAbsRealLValue>(); _rv) {
4557 // todo: check coordRange is a single range rather than multirange
4558 if (cut != "")
4559 cut += " && ";
4560 cut +=
4561 TString::Format("%s>=%f&&%s<%f", _rv->GetName(), _rv->getMin(_rv->getStringAttribute("coordRange")),
4562 _rv->GetName(), _rv->getMax(_rv->getStringAttribute("coordRange")));
4563 obs.add(*_rv); // note: if we ever changed coords to return clones, would need to keep coords alive
4564 } else {
4565 throw std::runtime_error("SetBinContent of data: Unsupported coordinate type");
4566 }
4567 }
4568
4569 RooFormulaVar cutFormula("cut1", cut, obs); // doing this to avoid complaints about unused vars
4570 RooFormulaVar icutFormula("icut1", TString::Format("!(%s)", cut.Data()), obs);
4571
4572 TString cut2;
4573 if (_ax) {
4574 cut2 = TString::Format("%s >= %f && %s < %f", _ax->GetParent()->GetName(), _ax->GetBinLowEdge(bin),
4575 _ax->GetParent()->GetName(), _ax->GetBinUpEdge(bin));
4576 obs.add(*dynamic_cast<RooAbsArg *>(_ax->GetParent()));
4577 } else {
4578 cut2 = "1==1";
4579 }
4580 RooFormulaVar cutFormula2("cut2", cut + " && " + cut2, obs);
4581 RooFormulaVar icutFormula2("icut2", TString::Format("!(%s && %s)", cut.Data(), cut2.Data()), obs);
4582
4583 // // go up through parents looking for slice obs
4584 // auto _p = fParent;
4585 // while(_p) {
4586 // TString pName(_p->GetName());
4587 // if (auto pos = pName.Index('='); pos != -1) {
4588 // if(auto _obs = _p->getObject<RooAbsLValue>(pName(0,pos)); _obs) {
4589 // if(auto _cat = dynamic_cast<RooAbsCategoryLValue*>(_obs.get()); _cat) {
4590 // _cat->setLabel(pName(pos+1,pName.Length()));
4591 // cut += TString::Format("%s%s==%d", (cut=="")?"":" && ",_cat->GetName(),
4592 // _cat->getCurrentIndex());
4593 // } else if(auto _var = dynamic_cast<RooAbsRealLValue*>(_obs.get()); _var) {
4594 // _var->setVal(TString(pName(pos+1,pName.Length())).Atof());
4595 // // TODO: Cut for this!!
4596 // }
4597 // obs.add(*dynamic_cast<RooAbsArg*>(_obs.get()));
4598 // } else {
4599 // throw std::runtime_error("Unknown observable, could not find");
4600 // }
4601 // }
4602 // _p = _p->fParent;
4603 // }
4604
4605 // add observables to dataset if necessary
4606 RooArgSet l(obs);
4607 l.remove(*_data->get(), true, true);
4608 if (!l.empty()) {
4609 // addColumns method is buggy: https://github.com/root-project/root/issues/8787
4610 // incredibly though, addColumn works??
4611 for (auto &x : l) {
4612 _data->addColumn(*x);
4613 }
4614 // instead create a copy dataset and merge it into current
4615 // cant use merge because it drops weightVar
4616 /*RooDataSet tmp("tmp","tmp",l);
4617 for(int i=0;i<_data->numEntries();i++) tmp.add(l);
4618 _data->merge(&tmp);*/
4619 // delete _data->addColumns(l);
4620 }
4621 // before adding, ensure range is good to cover
4622 for (auto &o : obs) {
4623 if (auto v = dynamic_cast<RooRealVar *>(o); v) {
4624 if (auto dv = dynamic_cast<RooRealVar *>(_data->get()->find(v->GetName())); dv) {
4625 if (v->getMin() < dv->getMin())
4626 dv->setMin(v->getMin());
4627 if (v->getMax() > dv->getMax())
4628 dv->setMax(v->getMax());
4629 }
4630 } else if (auto c = dynamic_cast<RooCategory *>(o); c) {
4631 if (auto dc = dynamic_cast<RooCategory *>(_data->get()->find(c->GetName())); dc) {
4632 if (!dc->hasLabel(c->getCurrentLabel())) {
4633 dc->defineType(c->getCurrentLabel(), c->getCurrentIndex());
4634 }
4635 }
4636 }
4637 }
4638
4639 // using SetBinContent means dataset must take on a binned form at these coordinates
4640 // if number of entries doesnt match number of bins then will 'bin' the data
4641 if (bin) {
4642 if (auto _nentries = std::unique_ptr<RooAbsData>(_data->reduce(cutFormula))->numEntries();
4643 _nentries != _ax->GetNbins()) {
4644 auto _contents = GetBinContents(1, _ax->GetNbins());
4645
4646 if (_nentries > 0) {
4647 Info("SetBinContent", "Binning %s in channel: %s", GetName(), cut.Data());
4648 auto _reduced = std::unique_ptr<RooAbsData>(_data->reduce(icutFormula));
4649 _data->reset();
4650 for (int j = 0; j < _reduced->numEntries(); j++) {
4651 auto _obs = _reduced->get(j);
4652 _data->add(*_obs, _reduced->weight());
4653 }
4654 }
4655 for (int i = 1; i <= _ax->GetNbins(); i++) {
4656 // can skip over the bin we will be setting to save a reduce step below
4657 if (i == bin)
4658 continue;
4659 dynamic_cast<RooAbsLValue *>(_ax->GetParent())->setBin(i - 1, _ax->GetName());
4660 _data->add(obs, _contents.at(i - 1));
4661 }
4662 }
4663 }
4664 // remove existing entries
4665 if (std::unique_ptr<RooAbsData>(_data->reduce(cutFormula2))->numEntries() > 0) {
4666 auto _reduced = std::unique_ptr<RooAbsData>(_data->reduce(icutFormula2));
4667 _data->reset();
4668 for (int j = 0; j < _reduced->numEntries(); j++) {
4669 auto _obs = _reduced->get(j);
4670 _data->add(*_obs, _reduced->weight());
4671 }
4672 }
4673 if (_ax)
4674 dynamic_cast<RooAbsLValue *>(_ax->GetParent())->setBin(bin - 1, _ax->GetName());
4675 _data->add(obs, value);
4676 if (auto bb = getBrowsable(".sourceds"))
4677 return bb->SetBinContent(bin, value, par, parVal); // apply to source ds if we have one
4678 return true;
4679
4680 } else if (get<RooDataHist>()) {
4681 throw std::runtime_error("RooDataHist not supported yet");
4682 }
4683 }
4684
4685 if (auto _varies = variations(); !_varies.empty() || (par && strlen(par))) {
4686 if (!par || strlen(par) == 0) {
4687 return _varies["nominal"]->SetBinContent(bin, value, par, parVal);
4688 } else if (auto it = _varies.find(Form("%s=%g", par, parVal)); it) {
4689 return it->SetBinContent(bin, value);
4690 } else {
4691 // need to create the variation : note - if no variations existed up to now this will inject a new node
4692 // so we should redirect ourself to the new node
4693 // TODO: Do we need to redirect parents?
4694 TString s = Form("%s=%g", par, parVal);
4695 return Vary(s.Data()).SetBinContent(bin, value);
4696 }
4697 }
4698
4699 auto o = get();
4700 if (auto p = dynamic_cast<RooRealVar *>(o); p) {
4701 if (!par || strlen(par) == 0) {
4702 if (p->getMax() < value)
4703 p->setMax(value);
4704 if (p->getMin() > value)
4705 p->setMin(value);
4706 p->setVal(value);
4707 sterilize();
4708 return true;
4709 }
4710
4711 } else if (auto c = dynamic_cast<RooConstVar *>(o); c) {
4712
4713 // if parent is a FlexibleInterpVar, change the value in that .
4714 if (strcmp(c->GetName(), Form("%g", c->getVal())) == 0) {
4715 c->SetNameTitle(Form("%g", value), Form("%g", value));
4716 }
4717#if ROOT_VERSION_CODE < ROOT_VERSION(6, 24, 00)
4718 c->_value = value; // in future ROOT versions there is a changeVal method!
4719#else
4720 c->changeVal(value);
4721#endif
4722
4724 fParent->Vary(*this);
4725 }
4726
4727 sterilize();
4728 return true;
4729 } else if (auto f = dynamic_cast<RooHistFunc *>(o); f) {
4730 auto bin_pars = f->dataHist().get(bin - 1);
4731 if (f->getAttribute("density")) {
4732 value /= f->dataHist().binVolume(*bin_pars);
4733 }
4734 f->dataHist().set(*bin_pars, value);
4735 f->setValueDirty();
4736
4737 if (auto otherfName = f->getStringAttribute("symmetrized_by"); otherfName) {
4738 // broken symmetry, so update flags ...
4739 f->setStringAttribute("symmetrized_by", nullptr);
4740 if (auto x = getObject<RooAbsArg>(otherfName); x) {
4741 x->setStringAttribute("symmetrizes", nullptr);
4742 x->setStringAttribute("symmetrize_nominal", nullptr);
4743 }
4744 } else if (auto otherfName2 = f->getStringAttribute("symmetrizes"); otherfName2) {
4745 auto nomf = getObject<RooHistFunc>(f->getStringAttribute("symmetrize_nominal"));
4747 if (nomf && otherf) {
4748 otherf->dataHist().set(*bin_pars, 2 * nomf->dataHist().weight(bin - 1) - value);
4749 otherf->setValueDirty();
4750 }
4751 }
4752 sterilize();
4753 return true;
4754 } else if (auto f2 = dynamic_cast<RooStats::HistFactory::FlexibleInterpVar *>(o); f2) {
4755 // changing nominal value
4756 f2->setNominal(value);
4757 }
4758 throw std::runtime_error(TString::Format("unable to set bin content of %s", GetPath().c_str()));
4759}
4760
4761bool xRooNode::SetBinData(int bin, double value, const xRooNode &data)
4762{
4763 if (data.get<RooAbsData>()) {
4764 // attach as a child before calling datasets(), so that is included in the list
4765 push_back(std::make_shared<xRooNode>(data));
4766 }
4767 auto node = datasets()[data.GetName()];
4768 if (data.get<RooAbsData>()) {
4769 // remove the child we attached
4770 resize(size() - 1);
4771 }
4772 return node->SetBinContent(bin, value);
4773}
4774
4775bool xRooNode::SetData(const TObject &obj, const xRooNode &data)
4776{
4777 if (data.get<RooAbsData>()) {
4778 // attach as a child before calling datasets(), so that is included in the list
4779 push_back(std::make_shared<xRooNode>(data));
4780 }
4781 auto node = datasets()[data.GetName()];
4782 if (data.get<RooAbsData>()) {
4783 // remove the child we attached
4784 resize(size() - 1);
4785 }
4786 return node->SetContents(obj);
4787}
4788
4789bool xRooNode::SetBinError(int bin, double value)
4790{
4791
4792 // if it's a RooProduct locate child with the same name
4793 if (get<RooProduct>()) {
4794 return factors()[GetName()]->SetBinError(bin, value);
4795 }
4796
4797 if (auto _varies = variations(); !_varies.empty()) {
4798 return _varies["nominal"]->SetBinError(bin, value);
4799 }
4800
4801 auto o = get();
4802
4803 if (auto f = dynamic_cast<RooHistFunc *>(o); f) {
4804
4805 // if (f->getAttribute("density")) { value /= f->dataHist().binVolume(*bin_pars); } - commented out because DON'T
4806 // convert .. sumw and sumw2 attributes will be stored not as densities
4807
4808 // NOTE: Can only do this because factors() makes parents of its children it's own parent (it isn't the parent)
4809 // If ever make factors etc part of the parentage then this would need tweaking to get to the true parent
4810 // find first parent that is a RooProduct, that is where the statFactor would live
4811 // stop as soon as we reach pdf object
4812 auto _prodParent = fParent;
4813 while (_prodParent && !_prodParent->get<RooProduct>() && !_prodParent->get<RooAbsPdf>()) {
4814 if (_prodParent->get<PiecewiseInterpolation>() && strcmp(GetName(), "nominal")) {
4815 _prodParent.reset();
4816 break; // only the 'nominal' variation can look for a statFactor outside the variation container
4817 }
4818 _prodParent = _prodParent->fParent;
4819 }
4820 auto _f_stat =
4821 (_prodParent && !_prodParent->get<RooAbsPdf>()) ? _prodParent->factors().find("statFactor") : nullptr;
4822 auto f_stat = (_f_stat) ? _f_stat->get<ParamHistFunc>() : nullptr;
4823 if (_f_stat && _f_stat->get() && !f_stat) {
4824 throw std::runtime_error("stat factor must be a paramhistfunc");
4825 }
4826
4827 // stat uncertainty lives in the "statFactor" factor, each sample has its own one,
4828 // but they can share parameters
4829 if (!f_stat) {
4830 if (value == 0)
4831 return true;
4833 for (auto &p : xRooNode("tmp", *f, std::shared_ptr<xRooNode>(nullptr)).vars()) {
4834 if (parNames != "")
4835 parNames += ",";
4836 parNames += p->get()->GetName();
4837 }
4838 auto h = std::unique_ptr<TH1>(f->dataHist().createHistogram(parNames
4839#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 27, 00)
4840 ,
4842#endif
4843 ));
4844 h->Reset();
4845 h->SetName("statFactor");
4846 h->SetTitle(TString::Format("StatFactor of %s", f->GetTitle()));
4847 h->SetOption("blankshape");
4848
4849 // multiply parent if is nominal
4850 auto toMultiply = this;
4851 if (strcmp(GetName(), "nominal") == 0 && fParent && fParent->get<PiecewiseInterpolation>())
4852 toMultiply = fParent.get();
4853
4854 f_stat = dynamic_cast<ParamHistFunc *>(toMultiply->Multiply(*h).get());
4855 if (!f_stat) {
4856 throw std::runtime_error("Failed creating stat shapeFactor");
4857 }
4858 }
4859
4860 auto phf = f_stat;
4861
4862 TString prefix = f->getStringAttribute("statPrefix");
4863 if (value && prefix == "") {
4864 // find the first parent that can hold components (RooAddPdf, RooRealSumPdf, RooAddition, RooWorkspace) ... use
4865 // that name for the stat factor
4866 auto _p = fParent;
4867 while (_p && !(_p->get()->InheritsFrom("RooRealSumPdf") || _p->get()->InheritsFrom("RooAddPdf") ||
4868 _p->get()->InheritsFrom("RooWorkspace") || _p->get()->InheritsFrom("RooAddition"))) {
4869 _p = _p->fParent;
4870 }
4871 prefix = TString::Format("stat_%s", (_p && _p->get<RooAbsReal>()) ? _p->get()->GetName() : f->GetName());
4872 }
4873 auto newVar = (value == 0) ? getObject<RooRealVar>("1")
4874 : acquire<RooRealVar>(Form("%s_bin%d", prefix.Data(), bin),
4875 Form("#gamma^{%s}_{%d}", prefix.Data(), bin), 1);
4876#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
4877 RooArgList &pSet = phf->_paramSet;
4878#else
4879 RooArgList &pSet = const_cast<RooArgList &>(phf->paramList());
4880#endif
4881 auto var = dynamic_cast<RooRealVar *>(&pSet[bin - 1]);
4882
4883 if (newVar.get() != var) {
4884 // need to swap out var for newVar
4885 // replace ith element in list with new func, or inject into RooProduct
4887 for (std::size_t i = 0; i < pSet.size(); i++) {
4888 if (int(i) != bin - 1) {
4889 all.add(*pSet.at(i));
4890 } else {
4891 all.add(*newVar);
4892 }
4893 }
4894 pSet.removeAll();
4895 pSet.add(all);
4896 }
4897
4898 xRooNode v((value == 0) ? *var : *newVar, *this);
4899 auto rrv = dynamic_cast<RooRealVar *>(v.get());
4900 if (strcmp(rrv->GetName(), "1") != 0) {
4901 TString origName = (f->getStringAttribute("origName")) ? f->getStringAttribute("origName") : GetName();
4902 rrv->setStringAttribute(Form("sumw2_%s", origName.Data()), TString::Format("%f", pow(value, 2)));
4903 auto bin_pars = f->dataHist().get(bin - 1);
4904 auto _binContent = f->dataHist().weight();
4905 if (f->getAttribute("density")) {
4906 _binContent *= f->dataHist().binVolume(*bin_pars);
4907 }
4908 rrv->setStringAttribute(Form("sumw_%s", origName.Data()), TString::Format("%f", _binContent));
4909 double sumw2 = 0;
4910 double sumw = 0;
4911 for (auto &[s, sv] : rrv->stringAttributes()) {
4912 if (s.find("sumw_") == 0) {
4913 sumw += TString(sv).Atof();
4914 } else if (s.find("sumw2_") == 0) {
4915 sumw2 += TString(sv).Atof();
4916 }
4917 }
4918 if (sumw2 && sumw2 != std::numeric_limits<double>::infinity()) {
4919 double tau = pow(sumw, 2) / sumw2;
4920 rrv->setError((tau < 1e-15) ? 1e15 : (/*rrv->getVal()*/ 1. / sqrt(tau))); // not sure why was rrv->getVal()?
4921 rrv->setConstant(false);
4922 // parameter must be constrained
4923 auto _constr = v.constraints();
4924 // std::cout << " setting constraint " << v.GetName() << " nomin=" << tau << std::endl;
4925 if (_constr.empty()) {
4926 rrv->setStringAttribute("boundConstraint", _constr.Add("poisson").get()->GetName());
4927 } else {
4928 auto _glob = _constr.at(0)->obs().at(0)->get<RooRealVar>();
4929 // TODO: Update any globs snapshots that are designed to match the nominal
4930 _glob->setStringAttribute("nominal", TString::Format("%f", tau));
4931 double _min = tau * (1. - 5. * sqrt(1. / tau));
4932 double _max = tau * (1. + 5. * sqrt(1. / tau));
4933 _glob->setRange(_min, _max);
4934 _glob->setVal(tau);
4935 _constr.at(0)->pp().at(0)->SetBinContent(0, tau);
4936 rrv->setStringAttribute("boundConstraint", _constr.at(0)->get()->GetName());
4937 }
4938 rrv->setRange(std::max((1. - 5. * sqrt(1. / tau)), 1e-15), 1. + 5. * sqrt(1. / tau));
4939 } else {
4940 // remove constraint
4941 if (auto _constr = v.constraints(); !_constr.empty()) {
4942 v.constraints().Remove(*_constr.at(0));
4943 }
4944 // set const if sumw2 is 0 (i.e. no error)
4945 rrv->setVal(1);
4946 rrv->setError(0);
4947 rrv->setConstant(sumw2 == 0);
4948 }
4949 }
4950
4951 return true;
4952 }
4953
4954 throw std::runtime_error(TString::Format("%s SetBinError failed", GetName()));
4955}
4956
4957std::shared_ptr<xRooNode> xRooNode::at(const std::string &name, bool browseResult) const
4958{
4959 auto res = find(name, browseResult);
4960 if (res == nullptr)
4961 throw std::out_of_range(name + " does not exist");
4962 return res;
4963}
4964
4965////////////////////////////////////////////////////////////////////////////////
4966/// The RooWorkspace this node belong to, if any
4967
4969{
4970 if (auto _w = get<RooWorkspace>(); _w)
4971 return _w;
4972 if (auto a = get<RooAbsArg>(); a && GETWS(a)) {
4973 return GETWS(a);
4974 }
4975 if (fParent)
4976 return fParent->ws();
4977 return nullptr;
4978}
4979
4981{
4982
4983 xRooNode out(".constraints", nullptr, *this);
4984
4985 std::function<RooAbsPdf *(const xRooNode &n, RooAbsArg &par, std::set<RooAbsPdf *> ignore)> getConstraint;
4986 getConstraint = [&](const xRooNode &n, RooAbsArg &par, std::set<RooAbsPdf *> ignore) {
4987 if (auto _pdf = n.get<RooAbsPdf>()) {
4988 if (ignore.count(_pdf))
4989 return (RooAbsPdf *)nullptr;
4990 ignore.insert(_pdf);
4991 }
4992 auto o = n.get<RooProdPdf>();
4993 if (!o) {
4994 if (n.get<RooSimultaneous>()) {
4995 // check all channels for a constraint if is simultaneous
4996 for (auto &c : n.bins()) {
4997 if (auto oo = getConstraint(*c, par, ignore); oo) {
4998 return oo;
4999 }
5000 }
5001 return (RooAbsPdf *)nullptr;
5002 } else if (n.get<RooAbsPdf>() && n.fParent && n.fParent->get<RooWorkspace>()) {
5003 // reached top-level pdf, which wasn't a simultaneous, so stop here
5004 return (RooAbsPdf *)nullptr;
5005 } else if (auto _ws = n.get<RooWorkspace>(); _ws) {
5006 // reached a workspace, check for any pdf depending on parameter that isnt the ignore
5007 for (auto p : _ws->allPdfs()) {
5008 if (ignore.count(static_cast<RooAbsPdf *>(p)))
5009 continue;
5010 if (p->dependsOn(par)) {
5011 out.emplace_back(std::make_shared<xRooNode>(par.GetName(), *p, *this));
5012 }
5013 }
5014 }
5015 if (!n.fParent)
5016 return (RooAbsPdf *)nullptr;
5017 return getConstraint(*n.fParent, par, ignore);
5018 }
5019 for (auto p : o->pdfList()) {
5020 if (ignore.count(static_cast<RooAbsPdf *>(p)))
5021 continue;
5022 if (p->dependsOn(par)) {
5023 out.emplace_back(std::make_shared<xRooNode>(par.GetName(), *p, *this));
5024 }
5025 }
5026 return (RooAbsPdf *)nullptr;
5027 };
5028
5029 for (auto &p : vars()) {
5030 auto v = dynamic_cast<RooAbsReal *>(p->get());
5031 if (!v)
5032 continue;
5033 if (v->getAttribute("Constant") && v != get<RooAbsReal>())
5034 continue; // skip constants unless we are getting the constraints of a parameter itself
5035 if (v->getAttribute("obs"))
5036 continue; // skip observables ... constraints constrain pars not obs
5037 getConstraint(*this, *v, {get<RooAbsPdf>()});
5038 /*if (auto c = ; c) {
5039 out.emplace_back(std::make_shared<Node2>(p->GetName(), *c, *this));
5040 }*/
5041 }
5042
5043 // finish by removing any constraint that contains another constraint for the same par
5044 // and consolidate common pars
5045 auto it = out.std::vector<std::shared_ptr<xRooNode>>::begin();
5046 while (it != out.std::vector<std::shared_ptr<xRooNode>>::end()) {
5047 bool removeIt = false;
5048 for (auto &c : out) {
5049 if (c.get() == it->get())
5050 continue;
5051 if ((*it)->get<RooAbsArg>()->dependsOn(*c->get<RooAbsArg>())) {
5052 removeIt = true;
5053 std::set<std::string> parNames;
5054 std::string _cName = c->GetName();
5055 do {
5056 parNames.insert(_cName.substr(0, _cName.find(';')));
5057 _cName = _cName.substr(_cName.find(';') + 1);
5058 } while (_cName.find(';') != std::string::npos);
5059 parNames.insert(_cName);
5060 _cName = it->get()->GetName();
5061 do {
5062 parNames.insert(_cName.substr(0, _cName.find(';')));
5063 _cName = _cName.substr(_cName.find(';') + 1);
5064 } while (_cName.find(';') != std::string::npos);
5065 parNames.insert(_cName);
5066 _cName = "";
5067 for (auto &x : parNames) {
5068 if (!_cName.empty())
5069 _cName += ";";
5070 _cName += x;
5071 }
5072 c->TNamed::SetName(_cName.c_str());
5073 break;
5074 }
5075 }
5076 if (removeIt) {
5077 it = out.erase(it);
5078 } else {
5079 ++it;
5080 }
5081 }
5082
5083 // if getting constraints of a fundamental then use the constraint names instead of the par name (because would be
5084 // all same otherwise)
5085 if (get<RooAbsArg>() && get<RooAbsArg>()->isFundamental()) {
5086 for (auto &o : out) {
5087 o->TNamed::SetName(o->get()->GetName());
5088 }
5089 }
5090
5091 return out;
5092}
5093
5094std::shared_ptr<TObject> xRooNode::convertForAcquisition(xRooNode &acquirer, const char *opt) const
5095{
5096
5097 TString sOpt(opt);
5098 sOpt.ToLower();
5100 if (sOpt == "func")
5101 sName = TString("factory:") + sName;
5102
5103 // if arg is a histogram, will acquire it as a RooHistFunc unless no conversion
5104 // todo: could flag not to convert
5105 if (auto h = get<TH1>(); h) {
5106 TString sOpt2(h->GetOption());
5107 std::map<std::string, std::string> stringAttrs;
5108 while (sOpt2.Contains("=")) {
5109 auto pos = sOpt2.Index("=");
5110 auto start = sOpt2.Index(";") + 1;
5111 if (start > pos)
5112 start = 0;
5113 auto end = sOpt2.Index(";", pos);
5114 if (end == -1)
5115 end = sOpt2.Length();
5116 stringAttrs[sOpt2(start, pos - start)] = sOpt2(pos + 1, end - pos - 1);
5117 sOpt2 = TString(sOpt2(0, start)) + TString(sOpt2(end + 1, sOpt2.Length()));
5118 }
5121 if (origName.BeginsWith(';'))
5122 origName = origName(1, origName.Length());
5123 if (newObjName.BeginsWith(';')) {
5124 newObjName =
5125 newObjName(1, newObjName.Length()); // special case if starts with ';' then don't create a fancy name
5126 } else if (acquirer.get() && !acquirer.get<RooWorkspace>()) {
5128 "%s_%s", (acquirer.mainChild().get()) ? acquirer.mainChild()->GetName() : acquirer->GetName(),
5129 newObjName.Data());
5130 }
5131 // can convert to a RooHistFunc, or RooParamHist if option contains 'shape'
5132 TString varName = h->GetXaxis()->GetName();
5133 std::string binningName = newObjName.Data();
5134 if (auto pos = varName.Index(';'); pos != -1) {
5135 binningName = varName(pos + 1, varName.Length());
5136 varName = varName(0, pos);
5137 }
5138
5139 if (varName == "xaxis" &&
5140 !acquirer.get<RooSimultaneous>()) { // default case, try to take axis var and binning from the acquirer
5141 if (auto ax = acquirer.GetXaxis(); ax) {
5142 varName = ax->GetParent()->GetName();
5143 // TODO: check the binning is consistent before using - at least will check nBins below
5144 binningName = ax->GetName();
5145 } else if (acquirer.obs().size() == 1)
5146 varName = acquirer.obs().at(0)->get()->GetName(); // TODO what if no obs but Xaxis var is defined?
5147 }
5148 auto x = acquirer.acquire<RooRealVar>(varName, h->GetXaxis()->GetTitle(), h->GetXaxis()->GetXmin(),
5149 h->GetXaxis()->GetXmax());
5150 if (x->getMin() > h->GetXaxis()->GetXmin())
5151 x->setMin(h->GetXaxis()->GetXmin());
5152 if (x->getMax() < h->GetXaxis()->GetXmax())
5153 x->setMax(h->GetXaxis()->GetXmax());
5154 if (!x->hasBinning(binningName.c_str())) {
5155 if (h->GetXaxis()->IsVariableBinSize()) {
5156 x->setBinning(RooBinning(h->GetNbinsX(), h->GetXaxis()->GetXbins()->GetArray()), binningName.c_str());
5157 } else {
5158 x->setBinning(
5159 RooUniformBinning(h->GetXaxis()->GetXmin(), h->GetXaxis()->GetXmax(), h->GetXaxis()->GetNbins()),
5160 binningName.c_str());
5161 }
5162 x->getBinning(binningName.c_str()).SetTitle(h->GetXaxis()->GetTitle());
5163 if (x->getBinningNames().size() == 2) {
5164 // this was the first binning, so copy it over to be the default binning too
5165 x->setBinning(x->getBinning(binningName.c_str()));
5166 }
5167 } else {
5168 // TODO check binning is compatible with histogram
5169 if (x->getBinning(binningName.c_str()).numBins() != h->GetNbinsX()) {
5170 throw std::runtime_error(
5171 TString::Format("binning mismatch for binning %s of %s", binningName.c_str(), x->GetName()));
5172 }
5173 }
5174
5175 std::shared_ptr<RooAbsArg> _f;
5176
5177 // if acquirer is a RooSimultaneous, will use histogram to define a channel
5178 if (acquirer.get<RooSimultaneous>()) {
5179 _f = acquirer.acquireNew<RooProdPdf>(newObjName, (strlen(h->GetTitle())) ? h->GetTitle() : h->GetName(),
5180 RooArgList());
5181 for (auto &[k, v] : stringAttrs) {
5182 _f->setStringAttribute(k.c_str(), v.c_str());
5183 }
5184 x->setAttribute("obs", true);
5185 } else if (sOpt2.Contains("shape")) {
5186 RooArgList list;
5187 for (int i = 0; i < x->getBinning(binningName.c_str()).numBins(); i++) {
5188 std::shared_ptr<RooAbsArg> arg;
5189 if (sOpt2.Contains("blankshape")) {
5190 arg = acquirer.acquire2<RooAbsArg, RooRealVar>("1", "1", 1);
5191 } else {
5192 if (!h) {
5193 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1), "", 1);
5194 } else if (h->GetMinimumStored() != -1111 || h->GetMaximumStored() != -1111) {
5195 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1),
5196 TString::Format("%s_{%d}", h->GetTitle(), i + 1),
5197 h->GetBinContent(i + 1), h->GetMinimumStored(),
5198 h->GetMaximumStored());
5199 } else {
5200 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1),
5201 TString::Format("%s_{%d}", h->GetTitle(), i + 1),
5202 h->GetBinContent(i + 1));
5203 }
5204 }
5205 list.add(*arg);
5206 }
5207 // paramhistfunc requires the binnings to be loaded as default at construction time
5208 // so load binning temporarily
5209 auto tmp = dynamic_cast<RooAbsBinning *>(x->getBinningPtr(nullptr)->Clone());
5210 x->setBinning(x->getBinning(binningName.c_str()));
5211 _f = acquirer.acquireNew<ParamHistFunc>(newObjName, h->GetTitle(), *x, list);
5212#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
5213 dynamic_cast<ParamHistFunc *>(_f.get())->_paramSet.setName("paramSet"); // so can see when print
5214#else
5215 const_cast<RooArgList &>(dynamic_cast<ParamHistFunc *>(_f.get())->paramList())
5216 .setName("paramSet"); // so can see when print
5217#endif
5218 x->setBinning(*tmp); // restore binning
5219 delete tmp;
5220 for (auto &[k, v] : stringAttrs) {
5221 _f->setStringAttribute(k.c_str(), v.c_str());
5222 }
5223 } else {
5224 auto dh = acquirer.acquireNew<RooDataHist>(Form("hist_%s", newObjName.Data()), h->GetTitle(), *x,
5225 binningName.c_str() /* binning name*/);
5226 if (!dh) {
5227 throw std::runtime_error("Couldn't make data hist");
5228 }
5229 auto f = acquirer.acquireNew<RooHistFunc>(newObjName, h->GetTitle(), *x, *dh,
5230 0 /*interpolation order between bins*/);
5231 f->forceNumInt();
5232 f->setAttribute("autodensity"); // where it gets inserted will determine if this is a density or not
5233 _f = f;
5234
5235 for (auto &[k, v] : stringAttrs) {
5236 _f->setStringAttribute(k.c_str(), v.c_str());
5237 }
5238
5239 // need to do these settings here because used in the assignment step
5240 _f->setStringAttribute("xvar", x->GetName());
5241 _f->setStringAttribute("binning", binningName.c_str());
5242 if (strcmp(_f->GetName(), origName.Data()) && !_f->getStringAttribute("alias"))
5243 _f->setStringAttribute("alias", origName);
5244
5245 // copy values over using the assignment operator - may convert to a RooProduct if there are stat uncerts
5246 xRooNode tmp(h->GetName(), _f, acquirer);
5247 tmp = *h;
5248 _f = std::dynamic_pointer_cast<RooAbsArg>(tmp.fComp); // in case got upgrade to a RooProduct
5249 }
5250
5251 _f->setStringAttribute("xvar", x->GetName());
5252 _f->setStringAttribute("binning", binningName.c_str());
5253 // style(h); // will transfer styling to object if necessary - not doing because this method used with plane hists
5254 // frequently
5255 if (strcmp(_f->GetName(), origName.Data()) && !_f->getStringAttribute("alias"))
5256 _f->setStringAttribute("alias", origName);
5257
5258 fComp = _f;
5259 return _f;
5260 } else if (!get() && sName.BeginsWith("factory:") && acquirer.ws()) {
5261 TString s(sName);
5262 s = TString(s(8, s.Length()));
5263 fComp.reset(acquirer.ws()->factory(s), [](TObject *) {});
5264 if (fComp) {
5265 const_cast<xRooNode *>(this)->TNamed::SetName(fComp->GetName());
5266 }
5267 return fComp;
5268 }
5269
5270 return fComp;
5271}
5272
5273std::shared_ptr<TStyle> xRooNode::style(TObject *initObject, bool autoCreate) const
5274{
5275 return std::dynamic_pointer_cast<TStyle>(styles(initObject, autoCreate).fComp);
5276}
5277
5279{
5280 TString t = GetTitle();
5281
5282 auto arg = get<RooAbsArg>();
5283 if (!initObject && !arg && !gROOT->GetStyle(t)) {
5284 return nullptr;
5285 }
5286
5287 std::unique_ptr<TObject> argInitObject;
5288
5289 if (initObject) {
5290 t = (strlen(initObject->GetTitle())) ? initObject->GetTitle() : initObject->GetName();
5291 } else if (arg) {
5292 if (arg->getStringAttribute("style")) {
5293 t = arg->getStringAttribute("style");
5294 } else if (autoCreate) {
5295 // args will default to histo's object styling, whatever that currently may be
5296 argInitObject = std::make_unique<TH1D>(GetName(), GetTitle(), 1, 0, 1);
5297 initObject = argInitObject.get();
5298 } else {
5299 return nullptr;
5300 }
5301 }
5302
5303 std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case getObject has decided to
5304 // return the owning ptr (for some reason)
5305 if (!gROOT->GetStyle(t)) {
5306 if ((style = getObject<TStyle>(t.Data()))) {
5307 // loaded style (from workspace?) so put in list and use that
5308 gROOT->GetListOfStyles()->Add(style.get());
5309 } else {
5310 if (!autoCreate)
5311 return nullptr;
5312 // create new style - gets put in style list automatically so don't have to delete
5313 // acquire them so saved to workspaces for auto reload ...
5314 style = const_cast<xRooNode &>(*this).acquireNew<TStyle>(t.Data(),
5315 TString::Format("Style for %s component", t.Data()));
5316 if (auto x = dynamic_cast<TAttLine *>(initObject))
5317 ((TAttLine &)*style) = *x;
5318 if (auto x = dynamic_cast<TAttFill *>(initObject))
5319 ((TAttFill &)*style) = *x;
5320 if (auto x = dynamic_cast<TAttMarker *>(initObject))
5321 ((TAttMarker &)*style) = *x;
5322 gROOT->GetListOfStyles()->Add(style.get());
5323 }
5324 } else {
5325 style = std::shared_ptr<TStyle>(gROOT->GetStyle(t), [](TStyle *) {});
5326 }
5327
5328 if (arg && !arg->getStringAttribute("style")) {
5329 arg->setStringAttribute("style", style->GetName());
5330 }
5331
5332 return xRooNode(style, *this);
5333}
5334
5335std::shared_ptr<TObject> xRooNode::acquire(const std::shared_ptr<TObject> &arg, bool checkFactory, bool mustBeNew)
5336{
5337 if (!arg)
5338 return nullptr;
5339 if (!fAcquirer && !get<RooWorkspace>() && fParent)
5340 return fParent->acquire(arg, checkFactory, mustBeNew);
5341
5342 // if has a workspace and our object is the workspace or is in the workspace then add this object to workspace
5343 auto _ws = (fAcquirer) ? nullptr : ws();
5344 if (_ws && (get() == _ws || _ws->arg(GetName()) || (arg && strcmp(arg->GetName(), GetName()) == 0))) {
5346 RooMsgService::instance().setGlobalKillBelow(RooFit::WARNING);
5347 if (auto a = dynamic_cast<RooAbsArg *>(arg.get()); a) {
5348 auto out_arg = _ws->arg(a->GetName());
5349 TString aName = arg->GetName();
5350 int ii = 1;
5351 while (out_arg && mustBeNew) {
5352 a->SetName(TString::Format("%s_%d", aName.Data(), ii++));
5353 out_arg = _ws->arg(a->GetName());
5354 }
5355 if (aName != a->GetName())
5356 Warning("acquire", "Renaming to %s", a->GetName());
5357 if (!out_arg) {
5358 bool done = false;
5359 if (checkFactory) {
5360 if (auto res = _ws->factory(arg->GetName()); res) {
5361 a = res;
5362 done = true;
5363 }
5364 }
5365 if (!done && _ws->import(*a, RooFit::RecycleConflictNodes())) {
5366 if (GETWS(a) != _ws) {
5367 Info("acquire", "A copy of %s has been added to workspace %s", a->GetName(), _ws->GetName());
5368 }
5369 RooMsgService::instance().setGlobalKillBelow(msglevel);
5370 return nullptr;
5371 }
5372 // sanitizeWS(); // clears the caches that might exist up to now, as well interfere with getParameters calls
5373 std::set<std::string> setNames;
5374 for (auto &aa : GETWSSETS(_ws)) {
5375 if (TString(aa.first.c_str()).BeginsWith("CACHE_")) {
5376 setNames.insert(aa.first);
5377 }
5378 }
5379 for (auto &aa : setNames)
5380 ws()->removeSet(aa.c_str());
5381 out_arg = _ws->arg(a->GetName());
5382 if (GETWS(out_arg) != _ws) { // seems that when objects imported their ws isn't set
5383 out_arg->setWorkspace(*_ws);
5384 }
5385 // if any of the leaf nodes of the imported object have "global" label on them, ensure propagate to
5386 // "globalObservables" list if ws has one
5387 if (auto globs = const_cast<RooArgSet *>(ws()->set("globalObservables")); globs) {
5389 out_arg->leafNodeServerList(&leafs);
5390 std::unique_ptr<RooAbsCollection> globals(leafs.selectByAttrib("global", true));
5391 for (auto &aa : *globals) {
5392 if (!globs->contains(*aa)) {
5393 globs->add(*aa);
5394 }
5395 }
5396 }
5397 }
5398 RooMsgService::instance().setGlobalKillBelow(msglevel);
5399 return std::shared_ptr<TObject>(out_arg, [](TObject *) {});
5400 } else if (auto a2 = dynamic_cast<RooAbsData *>(arg.get()); a2) {
5401 if (_ws->import(*a2, RooFit::Embedded())) {
5402 RooMsgService::instance().setGlobalKillBelow(msglevel);
5403 return nullptr;
5404 }
5405 RooMsgService::instance().setGlobalKillBelow(msglevel);
5406 return std::shared_ptr<TObject>(_ws->embeddedData(arg->GetName()), [](TObject *) {});
5407 } else if (arg->InheritsFrom("TNamed")) { // can add any TNamed to a workspace
5408 TObject *out_arg = nullptr;
5409 if (auto fr = dynamic_cast<RooFitResult *>(&*arg); fr && fr->numStatusHistory() == 0) {
5410 // fit results without a status history are treated as snapshots
5411 out_arg = fr->Clone();
5412 const_cast<RooLinkedList &>(GETWSSNAPSHOTS(_ws)).Add(out_arg);
5413 } else {
5414 // ensure will have a unique name for import if must be new
5415 TNamed *aNamed = dynamic_cast<TNamed *>(arg.get());
5416 TString aName = arg->GetName();
5417 out_arg = _ws->genobj(arg->GetName());
5418 int ii = 1;
5419 while (aNamed && out_arg && mustBeNew) {
5420 aNamed->SetName(TString::Format("%s;%d", aName.Data(), ii++));
5421 out_arg = _ws->genobj(aNamed->GetName());
5422 }
5423 if (!out_arg) {
5424 if (aName != arg->GetName()) {
5425 Warning("acquire", "Renaming to %s", arg->GetName());
5426 }
5427 if (_ws->import(*arg, false /*replace existing*/)) {
5428 RooMsgService::instance().setGlobalKillBelow(msglevel);
5429 return nullptr;
5430 }
5431 out_arg = _ws->genobj(arg->GetName());
5432 }
5433 }
5434 RooMsgService::instance().setGlobalKillBelow(msglevel);
5435
5436 return std::shared_ptr<TObject>(out_arg, [](TObject *) {});
5437 }
5438 RooMsgService::instance().setGlobalKillBelow(msglevel);
5439 // Warning("acquire","Not implemented acquisition of object %s",arg->GetName());
5440 // return nullptr;
5441 }
5442 if (!mustBeNew && fProvider) {
5443 auto out = fProvider->getObject(arg->GetName(), arg->ClassName());
5444 if (out)
5445 return out;
5446 }
5447 auto _owned = find(".memory");
5448 if (!_owned) {
5449 _owned = emplace_back(std::make_shared<xRooNode>(".memory", nullptr, *this));
5450 }
5451 // look for exact name, dont use 'find' because doesnt work if trying to find "1" and it doesn't exist, will get back
5452 // idx 1 instead
5453 if (!mustBeNew) {
5454 for (auto &r : *_owned) {
5455 if (strcmp(r->GetName(), arg->GetName()) == 0 && strcmp(r->get()->ClassName(), arg->ClassName()) == 0) {
5456 return r->fComp;
5457 }
5458 }
5459 }
5460 if (!fProvider)
5461 std::cout << GetName() << " taking over " << arg->ClassName() << "::" << arg->GetName() << std::endl;
5462 /*emplace_back(std::make_shared<Node2>(".memory",nullptr,*this))*/
5463 return _owned->emplace_back(std::make_shared<xRooNode>(arg->GetName(), arg, *this))->fComp;
5464 // return arg;
5465}
5466
5467bool xRooNode::SetXaxis(const char *name, const char *title, int nbins, double low, double high)
5468{
5469 RooUniformBinning b(low, high, nbins, name);
5470 b.SetTitle(title);
5471 return SetXaxis(b);
5472}
5473
5474bool xRooNode::SetXaxis(const char *name, const char *title, int nbins, const double *bins)
5475{
5476 RooBinning b(nbins, bins, name);
5477 b.SetTitle(title);
5478 return SetXaxis(b);
5479}
5480
5482{
5483
5484 auto name = binning.GetName();
5485 double high = binning.highBound();
5486 double low = binning.lowBound();
5487 // int nbins = binning.numBins();
5488 auto title = binning.GetTitle();
5489
5490 // if have any dependents and name isn't one of them then stop
5491 auto _deps = vars();
5492 /*if(!_deps.empty() && !_deps.find(name)) {
5493 throw std::runtime_error(TString::Format("%s Does not depend on %s",GetName(),name));
5494 }*/
5495
5496 // object will need to exist
5497 if (!get()) {
5498 if (fParent && !find(GetName())) {
5499 fComp = fParent->Add(*this, "+").fComp;
5500 }
5501 }
5502
5503 auto a = get<RooAbsArg>();
5504 if (!a)
5505 throw std::runtime_error("Cannot SetXaxis of non-arg");
5506
5507 auto _x = acquire<RooRealVar>(name, title, low, high);
5508 _x->setBinning(binning, a->GetName());
5509 _x->getBinning(a->GetName()).SetTitle(title);
5510 if (_x->getBinningNames().size() == 2) {
5511 // this was the first binning, so copy it over to be the default binning too
5512 _x->setBinning(_x->getBinning(a->GetName()));
5513 } else {
5514 // ensure the default binning is wide enough to cover this range
5515 // the alternative to this would be to ensure setNormRange of all pdfs
5516 // are set to correct range (then default can be narrower than some of the named binnings)
5517 if (_x->getMax() < high)
5518 _x->setMax(high);
5519 if (_x->getMin() > low)
5520 _x->setMin(low);
5521 }
5522
5523 if (!_deps.find(name) && get<RooAbsPdf>()) {
5524 // creating a variable for a pdf we will assume it should be an observable
5525 _x->setAttribute("obs");
5526 }
5527
5528 a->setStringAttribute("xvar", _x->GetName());
5529 a->setStringAttribute("binning", a->GetName());
5530 fXAxis.reset(); // remove any existing xaxis
5531
5532 return true;
5533}
5534
5536{
5537 if (!ax)
5538 return false;
5539 if (ax->IsVariableBinSize()) {
5540 return SetXaxis(ax->GetName(), ax->GetTitle(), ax->GetNbins(), ax->GetXbins()->GetArray());
5541 } else {
5542 return SetXaxis(ax->GetName(), ax->GetTitle(), ax->GetNbins(), ax->GetXmin(), ax->GetXmax());
5543 }
5544}
5545
5546bool xRooNode::contains(const std::string &name) const
5547{
5548 return find(name, false) != nullptr;
5549}
5550
5551std::shared_ptr<xRooNode> xRooNode::find(const std::string &name, bool browseResult) const
5552{
5553 std::string partname = (name.find('/') != std::string::npos) ? name.substr(0, name.find('/')) : name;
5554 auto _s = (!get() && fParent) ? fParent->get<RooSimultaneous>()
5555 : get<RooSimultaneous>(); // makes work if doing simPdf.bins()["blah"]
5556 std::string extra = (_s) ? _s->indexCat().GetName() : "";
5557 for (auto &child : *this) {
5558 if (auto _obj = child->get(); name == child->GetName() || partname == child->GetName() ||
5559 (_obj && name == _obj->GetName()) || (_obj && partname == _obj->GetName()) ||
5560 (!extra.empty() && ((extra + "=" + name) == child->GetName() ||
5561 (extra + "=" + partname) == child->GetName()))) {
5562 if (browseResult)
5563 child->browse(); // needed so can go at()->at()->at()...
5564 if (partname != name && name != child->GetName()) {
5565 return child->at(name.substr(partname.length() + 1));
5566 }
5567 return child;
5568 }
5569 if (partname.find('.') != 0) { // do not allow mainChild browsing if trying to find a "." child ... as is done in
5570 // getObject for ".memory"
5571 if (auto x = mainChild(); x && strcmp(child->GetName(), x.GetName()) == 0) {
5572 // can browse directly into main children as if their children were our children
5573 for (auto &child2 : x.browse()) {
5574 if (auto _obj = child2->get(); name == child2->GetName() || partname == child2->GetName() ||
5575 (_obj && name == _obj->GetName()) ||
5576 (_obj && partname == _obj->GetName())) {
5577 if (browseResult)
5578 child2->browse(); // needed for onward read (or is it? there's a browse above too??)
5579 if (partname != name && name != child2->GetName()) {
5580 return child2->at(name.substr(partname.length() + 1));
5581 }
5582 return child2;
5583 }
5584 }
5585 }
5586 }
5587 }
5588 // before giving up see if partName is numeric and indexes within the range
5589 if (TString s(partname); s.IsDec() && size_t(s.Atoi()) < size()) {
5590 auto child2 = at(s.Atoi());
5591 if (partname != name) {
5592 return child2->at(name.substr(partname.length() + 1));
5593 }
5594 return child2;
5595 }
5596 // allow calling of find on a RooWorkspace to access getObject objects ...
5597 if (get<RooWorkspace>() && name != ".memory") {
5598 if (auto obj = getObject(name)) {
5599 auto out = std::make_shared<xRooNode>(obj, *this);
5600 if (browseResult)
5601 out->browse();
5602 return out;
5603 }
5604 }
5605 return nullptr;
5606}
5607
5608std::shared_ptr<xRooNode> xRooNode::operator[](const std::string &name)
5609{
5610 std::string partname = (name.find('/') != std::string::npos) ? name.substr(0, name.find('/')) : name;
5611 browse();
5612 auto _s = (!get() && fParent) ? fParent->get<RooSimultaneous>()
5613 : get<RooSimultaneous>(); // makes work if doing simPdf.bins()["blah"]
5614 std::string extra = (_s) ? _s->indexCat().GetName() : "";
5615 std::shared_ptr<xRooNode> folderNode;
5616 for (auto &child : *this) {
5617 if (name == child->GetName() || partname == child->GetName() ||
5618 (!extra.empty() &&
5619 ((extra + "=" + name) == child->GetName() || (extra + "=" + partname) == child->GetName()))) {
5620 child->browse(); // needed for onward read (or is it? there's a browse above too??)
5621 if (partname != name && name != child->GetName()) {
5622 return child->operator[](name.substr(partname.length() + 1));
5623 }
5624 return child;
5625 }
5626 if (auto x = mainChild(); strcmp(child->GetName(), x.GetName()) == 0) {
5627 // can browse directly into main children as if their children were our children
5628 for (auto &child2 : x.browse()) {
5629 if (name == child2->GetName() || partname == child2->GetName()) {
5630 child2->browse(); // needed for onward read (or is it? there's a browse above too??)
5631 if (partname != name && name != child2->GetName()) {
5632 return child2->operator[](name.substr(partname.length() + 1));
5633 }
5634 return child2;
5635 }
5636 }
5637 }
5638 if (child->fFolder == (std::string("!") + partname)) {
5639 if (!folderNode)
5640 folderNode = std::make_shared<xRooNode>(child->fFolder.c_str(), nullptr, *this);
5641 folderNode->push_back(child);
5642 }
5643 }
5644 if (folderNode) {
5645 if (partname != name) {
5646 return folderNode->operator[](name.substr(partname.length() + 1));
5647 }
5648 return folderNode;
5649 }
5650 // before giving up see if partName is numeric and indexes within the range
5651 if (TString s(partname); s.IsDec() && size_t(s.Atoi()) < size()) {
5652 auto child2 = at(s.Atoi());
5653 if (partname != name) {
5654 return child2->operator[](name.substr(partname.length() + 1));
5655 }
5656 return child2;
5657 }
5658 auto out = std::make_shared<xRooNode>(partname.c_str(), nullptr, *this); // not adding as child yeeet
5659 // special case, if creating a node in the workspace with a specific name, it's a folder node ...
5660 if (get<RooWorkspace>() && partname == "pdfs") {
5661 out->SetName("!pdfs");
5662 }
5663 if (partname != name) {
5664 return out->operator[](name.substr(partname.length() + 1));
5665 }
5666 return out;
5667}
5668
5670{
5671 if (!b) {
5672 for (auto o : *gROOT->GetListOfBrowsers()) {
5673 b = dynamic_cast<TBrowser *>(o);
5674 if (!b || !b->GetBrowserImp())
5675 continue;
5676 if (auto out = GetTreeItem(b); out)
5677 return out;
5678 }
5679 return nullptr;
5680 }
5681 if (!b->GetBrowserImp())
5682 return nullptr;
5683 if (auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp()))); _b) {
5684 auto _root = GETROOTDIR(_b);
5685 ;
5686 if (!_root)
5687 _root = GETLISTTREE(_b)->GetFirstItem();
5689 return GETLISTTREE(_b)->FindItemByObj(_root, const_cast<xRooNode *>(this));
5690 }
5691 return nullptr;
5692}
5693
5695{
5696 if (!b) {
5697 for (auto o : *gROOT->GetListOfBrowsers()) {
5698 b = dynamic_cast<TBrowser *>(o);
5699 if (!b || !b->GetBrowserImp())
5700 continue;
5701 if (auto out = GetListTree(b); out)
5702 return out;
5703 }
5704 return nullptr;
5705 }
5706 if (b->GetBrowserImp()) {
5707 if (auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp())));
5708 _b) {
5709 auto _root = GETROOTDIR(_b);
5710 if (!_root)
5711 _root = GETLISTTREE(_b)->GetFirstItem();
5712 if (auto item = GETLISTTREE(_b)->FindItemByObj(_root, const_cast<xRooNode *>(this)); item) {
5713 return GETLISTTREE(_b);
5714 }
5715 }
5716 }
5717 return nullptr;
5718}
5719
5720void xRooNode::SetName(const char *name)
5721{
5723 if (auto a = get<RooAbsArg>(); a)
5724 a->setStringAttribute("alias", name);
5725 for (auto o : *gROOT->GetListOfBrowsers()) {
5726 if (auto b = dynamic_cast<TBrowser *>(o); b) {
5727 if (auto item = GetTreeItem(b); item) {
5728 item->SetText(name);
5729 }
5730 }
5731 }
5732}
5733
5734void xRooNode::SetTitle(const char *title)
5735{
5736 if (auto o = (get<TNamed>()); o) {
5737 if (auto c = mainChild(); c.get()) {
5738 c.SetTitle(title);
5739 }
5740 o->SetTitle(title);
5741 }
5742 TNamed::SetTitle(title);
5743}
5744
5746{
5747 if (get<RooArgList>() || (!get() && !(strlen(GetName()) > 0 && (GetName()[0] == '!')) && !fBrowseOperation))
5748 return *this; // nothing to browse - 'collection' nodes should already be populated except for folders
5749 // alternative could have been to mandate that the 'components' of a collection node are the children it has.
5750
5751 auto findByObj = [&](const std::shared_ptr<xRooNode> &n) {
5752 std::vector<std::shared_ptr<xRooNode>> &nn = *this;
5753 for (auto &c : nn) {
5754 if (c->get() == n->get() && strcmp(n->GetName(), c->GetName()) == 0)
5755 return c;
5756 }
5757 return std::shared_ptr<xRooNode>(nullptr);
5758 };
5759
5760 auto appendChildren = [&](const xRooNode &n) {
5761 size_t out = 0;
5762 const std::vector<std::shared_ptr<xRooNode>> &nn(n);
5763 for (auto &c : nn) {
5764 if (auto existing = findByObj(c); existing) {
5765 existing->fTimes++;
5766 existing->fFolder = c->fFolder; // transfer folder assignment
5767 } else {
5768 emplace_back(c);
5769 }
5770 if (!TString(c->GetName()).BeginsWith(".coef"))
5771 out++; // don't count .coef as a child, as technically part of parent
5772 }
5773 return out;
5774 };
5775
5776 const std::vector<std::shared_ptr<xRooNode>> &nn2(*this);
5777 for (auto &c : nn2) {
5778 if (strlen(c->GetName()) > 0 && (c->GetName()[0] == '.')) {
5779 c->fTimes = 1;
5780 continue;
5781 } // never auto-cleanup property children
5782 if (strcmp(c->GetName(), "!.pars") == 0) {
5783 c->fTimes = 1;
5784 continue;
5785 } // special collection, also not cleaned up
5786 if (c->get<RooWorkspace>() || c->get<TFile>()) {
5787 c->fTimes = 1;
5788 continue;
5789 } // workspaces and files not cleaned up: TODO have a nocleanup flag instead
5790 c->fTimes = 0;
5791 }
5792
5793 size_t addedChildren = 0;
5794 if (fBrowseOperation) {
5796 } else {
5797 if (get<RooWorkspace>()) {
5799 }
5800
5801 // if (get<RooAbsPdf>() && ((fParent && fParent->get<RooWorkspace>()) || !fParent)) {
5802 // // top-level pdfs will also list the ".vars" property for -- should make this updateable
5803 // //if (auto x = find("!.vars"); !x) { // this is slower because it triggers a browse of !.vars
5804 // if(!contains("!.vars")) {
5805 // emplace_back(std::make_shared<Node2>("!.vars",nullptr,*this));
5806 // } /*else {
5807 // x->fTimes++;
5808 // }*/
5809 // }
5810
5811 // go through components factors and variations, adding all as children if required
5813 if (!get<RooWorkspace>())
5815 // include coefs if any
5816 auto _coefs = coefs();
5817 if (_coefs.get() && strcmp(_coefs->GetName(), "1") != 0 && strcmp(_coefs->GetName(), "ONE") != 0) {
5818 if (_coefs.size() == 1 && _coefs.get<RooAddition>()) {
5819 if (strcmp(_coefs.at(0)->GetName(), "1") != 0 &&
5820 strcmp(_coefs.at(0)->GetName(), "ONE") != 0) { // don't add the "1"
5821 auto coef = std::make_shared<xRooNode>(".coef", *_coefs.at(0)->get(), *this);
5822 if (auto existing = findByObj(coef); existing) {
5823 existing->fTimes++;
5824 existing->fFolder = _coefs.at(0)->fFolder; // transfer folder assignment
5825 } else {
5826 emplace_back(coef);
5827 }
5828 }
5829 } else {
5830 if (auto existing = find(_coefs.GetName()); existing) {
5831 existing->fTimes++;
5832 existing->fFolder = _coefs.fFolder; // transfer folder assignment
5833 } else {
5834 emplace_back(std::make_shared<xRooNode>(_coefs));
5835 }
5836 }
5837 }
5841 if (get<RooAbsData>())
5843 }
5844 // if has no children and is a RooAbsArg, add all the proxies
5845 if (auto arg = get<RooAbsArg>(); arg && addedChildren == 0) {
5846 for (int i = 0; i < arg->numProxies(); i++) {
5847 auto _proxy = arg->getProxy(i);
5848 if (auto a = dynamic_cast<RooArgProxy *>(_proxy)) {
5849 auto c = std::make_shared<xRooNode>(TString::Format(".%s", _proxy->name()), *(a->absArg()), *this);
5850 if (auto existing = findByObj(c); existing) {
5851 existing->fTimes++;
5852 existing->fFolder = c->fFolder; // transfer folder assignment
5853 } else {
5854 // mark any existing children with the same name for cleanup - this happens e.g. if did a Replace on one
5855 // of these nodes note that the child nodes will still become reordered (the old node will be deleted,
5856 // new node will appear at end)
5857 for (auto &child : *this) {
5858 if (strcmp(child->GetName(), c->GetName()) == 0) {
5859 child->fTimes = 0;
5860 }
5861 }
5862 emplace_back(c);
5863 }
5864 } else if (auto s = dynamic_cast<RooAbsCollection *>(_proxy)) {
5865 for (auto a2 : *s) {
5866 auto c = std::make_shared<xRooNode>(*a2, *this);
5867 if (arg->numProxies() != 1) {
5868 c->fFolder = std::string("!.") +
5869 _proxy->name(); // don't put in a folder if there's just 1 proxy (the collection)
5870 }
5871 if (auto existing = findByObj(c); existing) {
5872 existing->fTimes++;
5873 existing->fFolder = c->fFolder; // transfer folder assignment
5874 } else {
5875 emplace_back(c);
5876 }
5877 }
5878 }
5879 }
5880 /*for(auto& s : arg->servers()) {
5881 auto c = std::make_shared<xRooNode>(*s,*this);
5882 if (auto existing = findByObj(c); existing) {
5883 existing->fTimes++;
5884 existing->fFolder = c->fFolder; // transfer folder assignment
5885 } else {
5886 emplace_back(c);
5887 }
5888 }*/
5889 } else if (auto ir = get<RooStats::HypoTestInverterResult>()) {
5890 // check if we already have a hypoSpace in our memory
5891 bool hasHS = false;
5892 for (auto &c : fBrowsables) {
5893 if (strcmp(c->GetName(), ".memory") == 0 && c->get<xRooHypoSpace>()) {
5894 hasHS = true;
5895 break;
5896 }
5897 }
5898 if (!hasHS) {
5899 // add the HS
5900 auto hs =
5901 fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", std::make_shared<xRooHypoSpace>(ir), *this));
5902 // add the hypoPoints first so they appear first
5903 auto _axes = hs->get<xRooHypoSpace>()->axes();
5904
5905 int i = 0;
5906 for (auto &hp : *hs->get<xRooHypoSpace>()) {
5908 for (auto a : _axes) {
5909 if (a != _axes.first())
5910 coordString += ",";
5911 coordString +=
5912 TString::Format("%s=%g", a->GetName(), hp.coords->getRealValue(a->GetName(), ir->GetXValue(i)));
5913 }
5914 auto hpn = emplace_back(std::make_shared<xRooNode>(coordString, hp.hypoTestResult, hs));
5915 hpn->fTimes++;
5916 hpn->fBrowsables.emplace_back(std::make_shared<xRooNode>(
5917 ".memory", std::shared_ptr<xRooNLLVar::xRooHypoPoint>(&hp, [](xRooNLLVar::xRooHypoPoint *) {}), hpn));
5918 i++;
5919 }
5920 } else {
5921 // ensure all hypoTestResults are flagged as keep-alive
5922 std::vector<std::shared_ptr<xRooNode>> &nn = *this;
5923 for (auto &c : nn) {
5924 if (c->get<RooStats::HypoTestResult>())
5925 c->fTimes++;
5926 }
5927 }
5928 // xRooNode tests;
5929 // for(int i=0;i<ir->ArraySize();i++) {
5930 // tests.push_back(std::make_shared<xRooNode>(TString::Format("%g",ir->GetXValue(i)),*ir->GetResult(i),*this));
5931 // }
5932 // appendChildren(tests);
5933 } else if (get<RooStats::HypoTestResult>()) {
5934
5935 // create the xRooHypoPoint if necessary
5936 xRooNLLVar::xRooHypoPoint *hp = nullptr;
5937 for (auto &c : fBrowsables) {
5938 if (strcmp(c->GetName(), ".memory") == 0 && c->get<xRooNLLVar::xRooHypoPoint>()) {
5939 hp = c->get<xRooNLLVar::xRooHypoPoint>();
5940 c->fTimes++; // keep it alive
5941 break;
5942 }
5943 }
5944 if (!hp) {
5945 auto shp =
5946 std::make_shared<xRooNLLVar::xRooHypoPoint>(std::dynamic_pointer_cast<RooStats::HypoTestResult>(fComp));
5947 fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", shp, *this));
5948 hp = shp.get();
5949 }
5950
5951 xRooNode fits;
5952
5953 if (auto fit = hp->ufit()) {
5954 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("ufit");
5955 }
5956 if (auto fit = hp->cfit_null()) {
5957 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("cfit_null");
5958 }
5959 if (auto fit = hp->cfit_alt()) {
5960 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("cfit_alt");
5961 }
5962 if (auto fit = hp->gfit()) {
5963 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("gfit");
5964 }
5965 if (auto asi = hp->asimov()) {
5966 auto asiP = fits.emplace_back(std::make_shared<xRooNode>(
5967 asi->hypoTestResult ? asi->hypoTestResult : std::make_shared<RooStats::HypoTestResult>(asi->result()),
5968 *this));
5969 asiP->TNamed::SetName("asimov");
5970 asiP->fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", asi, asiP));
5971 }
5973 }
5974
5975 // clear anything that has fTimes = 0 still
5976 auto it = std::vector<std::shared_ptr<xRooNode>>::begin();
5977 while (it != std::vector<std::shared_ptr<xRooNode>>::end()) {
5978 if (it->get()->fTimes == 0) {
5979 for (auto o : *gROOT->GetListOfBrowsers()) {
5980 auto b = dynamic_cast<TBrowser *>(o);
5981 if (b && b->GetBrowserImp()) { // browserImp is null if browser was closed
5982 // std::cout << GetPath() << " Removing " << it->get()->GetPath() << std::endl;
5983
5984 if (auto _b =
5985 dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp())));
5986 _b) {
5987 auto _root = GETROOTDIR(_b);
5988 if (!_root)
5989 _root = GETLISTTREE(_b)->GetFirstItem();
5990 if (auto item = GETLISTTREE(_b)->FindItemByObj(_root, this); item) {
5991 GETLISTTREE(_b)->OpenItem(item);
5992 }
5993 }
5994
5995 b->RecursiveRemove(
5996 it->get()); // problem: if obj is living in a collapsed node it wont actually get deleted
5997 /*auto _b = dynamic_cast<TGFileBrowser*>( dynamic_cast<TRootBrowser*>(b->GetBrowserImp())->fActBrowser );
5998 if (_b) {
5999 std::cout << _b->fRootDir->GetText() << std::endl;
6000 if (auto item = _b->fListTree->FindItemByObj(_b->fRootDir,it->get()); item) {
6001 std::cout << "Found obj: " << item << " " << item->GetText() << std::endl;
6002 _b->fListTree->RecursiveDeleteItem(_b->fRootDir,it->get());
6003 }
6004
6005 //b->RecursiveRemove(it->get());
6006 if (auto item = _b->fListTree->FindItemByObj(_b->fRootDir,it->get()); item) {
6007 std::cout << "Still Found obj: " << item << std::endl;
6008 }
6009 _b->fListTree->ClearViewPort();
6010
6011 }*/
6012 }
6013 }
6014 /*it->get()->ResetBit(TObject::kNotDeleted); ++it;*/ it = erase(it);
6015 } else {
6016 ++it;
6017 }
6018 }
6019
6020 return *this;
6021}
6022
6023////////////////////////////////////////////////////////////////////////////////
6024/// List of observables (global and regular) of this node.
6025
6027{
6028 xRooNode out(".obs", std::make_shared<RooArgList>(), *this);
6029 out.get<RooArgList>()->setName((GetPath() + ".obs").c_str());
6030 for (auto o : vars()) {
6031 if (o->get<RooAbsArg>()->getAttribute("obs")) {
6032 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6033 out.emplace_back(o);
6034 }
6035 }
6036 return out;
6037}
6038
6039////////////////////////////////////////////////////////////////////////////////
6040/// List of global observables of this node.
6041
6043{
6044 xRooNode out(".globs", std::make_shared<RooArgList>(), *this);
6045 out.get<RooArgList>()->setName((GetPath() + ".globs").c_str());
6046 for (auto o : obs()) {
6047 if (o->get<RooAbsArg>()->getAttribute("global")) {
6048 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6049 out.emplace_back(o);
6050 }
6051 }
6052 return out;
6053}
6054
6055////////////////////////////////////////////////////////////////////////////////
6056/// List of regular observables of this node.
6057
6059{
6060 xRooNode out(".robs", std::make_shared<RooArgList>(), *this);
6061 out.get<RooArgList>()->setName((GetPath() + ".robs").c_str());
6062 for (auto o : obs()) {
6063 if (!o->get<RooAbsArg>()->getAttribute("global")) {
6064 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6065 out.emplace_back(o);
6066 }
6067 }
6068 return out;
6069}
6070
6071////////////////////////////////////////////////////////////////////////////////
6072/// List of parameters (non-observables) of this node.
6073
6075{
6076 if (strcmp(GetName(), ".bins") == 0 && fParent) {
6077 // return pars of the parent - this method is used by covariances() if e.g. do node.bins().covariances()
6078 return fParent->pars();
6079 }
6080 xRooNode out(".pars", std::make_shared<RooArgList>(), *this);
6081 out.get<RooArgList>()->setName((GetPath() + ".pars").c_str());
6082 for (auto o : vars()) {
6083 if (!o->get<RooAbsArg>()->getAttribute("obs")) {
6084 out.get<RooArgList>()->add(*(o->get<RooAbsArg>()));
6085 out.emplace_back(o);
6086 }
6087 }
6088 return out;
6089}
6090
6091////////////////////////////////////////////////////////////////////////////////
6092/// List of parameters that are currently constant
6093
6095{
6096 xRooNode out(".consts", std::make_shared<RooArgList>(), *this);
6097 out.get<RooArgList>()->setName((GetPath() + ".consts").c_str());
6098 for (auto o : pars()) {
6099 if (o->get<RooAbsArg>()->getAttribute("Constant") || o->get<RooConstVar>()) {
6100 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6101 out.emplace_back(o);
6102 }
6103 }
6104 return out;
6105}
6106
6107////////////////////////////////////////////////////////////////////////////////
6108/// List of parameters that are currently non-constant
6109/// These parameters do not have the "Constant" attribute
6110
6112{
6113 xRooNode out(".floats", std::make_shared<RooArgList>(), *this);
6114 out.get<RooArgList>()->setName((GetPath() + ".floats").c_str());
6115 for (auto o : pars()) {
6116 if (!o->get<RooAbsArg>()->getAttribute("Constant") && !o->get<RooConstVar>()) {
6117 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6118 out.emplace_back(o);
6119 }
6120 }
6121 return out;
6122}
6123
6124////////////////////////////////////////////////////////////////////////////////
6125/// List of parameters of interest: parameters marked as "of interest"
6126/// These parameters have the "poi" attribute
6127
6129{
6130 xRooNode out(".poi", std::make_shared<RooArgList>(), *this);
6131 out.get<RooArgList>()->setName((GetPath() + ".poi").c_str());
6132 for (auto o : pars()) {
6133 if (o->get<RooAbsArg>()->getAttribute("poi")) {
6134 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6135 out.emplace_back(o);
6136 }
6137 }
6138 return out;
6139}
6140
6141////////////////////////////////////////////////////////////////////////////////
6142/// List of nuisance parameters: non-constant parameters that are not marked of interest,
6143/// as well as any parameters that have been marked by the "np" attribute
6144
6146{
6147 xRooNode out(".np", std::make_shared<RooArgList>(), *this);
6148 out.get<RooArgList>()->setName((GetPath() + ".np").c_str());
6149 for (auto o : pars()) {
6150 if (o->get<RooAbsArg>()->getAttribute("np") ||
6151 (!o->get<RooAbsArg>()->getAttribute("Constant") && !o->get<RooAbsArg>()->getAttribute("poi") &&
6152 !o->get<RooConstVar>())) {
6153 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6154 out.emplace_back(o);
6155 }
6156 }
6157 return out;
6158}
6159
6160////////////////////////////////////////////////////////////////////////////////
6161/// List of prespecified parameters: non-floatable parameters
6162
6164{
6165 xRooNode out(".pp", std::make_shared<RooArgList>(), *this);
6166 out.get<RooArgList>()->setName((GetPath() + ".pp").c_str());
6167 for (auto o : pars()) {
6168 if (!o->get<RooAbsArg>()->getAttribute("np") && !o->get<RooAbsArg>()->getAttribute("poi") &&
6169 (o->get<RooAbsArg>()->getAttribute("Constant") || o->get<RooConstVar>())) {
6170 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6171 out.emplace_back(o);
6172 }
6173 }
6174 return out;
6175}
6176
6177////////////////////////////////////////////////////////////////////////////////
6178/// List of variables (observables and parameters) of this node
6179
6181{
6182 xRooNode out(".vars", std::make_shared<RooArgList>(), *this);
6183 out.get<RooArgList>()->setName((GetPath() + ".vars").c_str());
6184 if (auto coll = get<RooAbsCollection>(); coll) {
6185 for (auto &x : *this) {
6186 for (auto &y : x->vars()) {
6187 out.push_back(y);
6188 }
6189 }
6190 return out;
6191 }
6192 if (auto p = get<RooAbsArg>(); p) {
6193 // also need to get all constPars so use leafNodeServerList .. will include self if is fundamental, which is what
6194 // we want
6195 // ensure all globs appear after robs, as we rely on this ordering for picking "x" var in "reduced" method
6198 p->leafNodeServerList(&allLeaves);
6199 for (auto &c : allLeaves) {
6200 if (c->isFundamental() || (dynamic_cast<RooConstVar *>(c) && !TString(c->GetName()).IsFloat())) {
6201 if (!c->getAttribute("global")) {
6202 out.get<RooArgList>()->add(*c);
6203 out.emplace_back(std::make_shared<xRooNode>(*c, *this));
6204 }
6205 if (c->getAttribute("global")) {
6206 _globs.emplace_back(std::make_shared<xRooNode>(*c, *this));
6207 _globs.back()->fFolder = "!globs";
6208 } else if (c->getAttribute("obs")) {
6209 out.back()->fFolder = "!robs";
6210 } else if (c->getAttribute("poi")) {
6211 out.back()->fFolder = "!poi";
6212 } else if (c->getAttribute("np") ||
6213 (!c->getAttribute("Constant") && !c->getAttribute("poi") && c->IsA() != RooConstVar::Class())) {
6214 out.back()->fFolder = "!np";
6215 } else if (!c->getAttribute("Constant") && c->IsA() != RooConstVar::Class()) {
6216 out.back()->fFolder = "!floats";
6217 } else {
6218 out.back()->fFolder = "!pp";
6219 }
6220 }
6221 }
6222 for (auto g : _globs) {
6223 out.get<RooArgList>()->add(*g->get<RooAbsArg>());
6224 out.emplace_back(g);
6225 }
6226 } else if (auto p2 = get<RooAbsData>(); p2) {
6227 for (auto a : *p2->get()) {
6228 a->setAttribute("obs");
6229 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6230 out.get<RooArgList>()->add(*a);
6231 }
6232 if (auto _dglobs = p2->getGlobalObservables()) {
6233 for (auto &a : *_dglobs) {
6234 a->setAttribute("obs");
6235 a->setAttribute("global");
6236 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6237 out.get<RooArgList>()->add(*a);
6238 }
6239 } else if (auto _globs = find(".globs"); _globs && _globs->get<RooAbsCollection>()) {
6240 for (auto &a : *_globs->get<RooAbsCollection>()) {
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 _ws = ws(); _ws) {
6247 if (auto _globs2 = dynamic_cast<RooArgSet *>(GETWSSNAPSHOTS(_ws).find(p2->GetName())); _globs2) {
6248 for (auto a : *_globs2) {
6249 a->setAttribute("obs");
6250 a->setAttribute("global");
6251 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6252 out.get<RooArgList>()->add(*a);
6253 }
6254 } else if (auto _gl = GETWSSETS(_ws).find("globalObservables"); _gl != GETWSSETS(_ws).end()) {
6255 for (auto &_g : _gl->second) {
6256 auto _clone = std::shared_ptr<RooAbsArg>(dynamic_cast<RooAbsArg *>(_g->Clone(_g->GetName())));
6257 if (auto v = std::dynamic_pointer_cast<RooAbsRealLValue>(_clone); v && _g->getStringAttribute("nominal"))
6258 v->setVal(TString(_g->getStringAttribute("nominal")).Atof());
6259 out.emplace_back(std::make_shared<xRooNode>(_clone, *this));
6260 out.get<RooArgList>()->add(*_clone);
6261 }
6262 } else if (fParent) {
6263 // note: this is slow in large workspaces ... too many obs to look through?
6264 std::unique_ptr<RooAbsCollection> _globs3(fParent->obs().get<RooArgList>()->selectByAttrib("global", true));
6265 // std::unique_ptr<RooAbsCollection> _globs(_ws->allVars().selectByAttrib("global",true)); - tried this to
6266 // be quicker but it wasn't
6267 for (auto &_g : *_globs3) {
6268 auto _clone = std::shared_ptr<RooAbsArg>(dynamic_cast<RooAbsArg *>(_g->Clone(_g->GetName())));
6269 if (auto v = std::dynamic_pointer_cast<RooAbsRealLValue>(_clone); v && _g->getStringAttribute("nominal"))
6270 v->setVal(TString(_g->getStringAttribute("nominal")).Atof());
6271 out.emplace_back(std::make_shared<xRooNode>(_clone, *this));
6272 out.get<RooArgList>()->add(*_clone);
6273 }
6274 }
6275 }
6276 } else if (auto w = get<RooWorkspace>(); w) {
6277 for (auto a : w->allVars()) {
6278 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6279 out.get<RooArgList>()->add(*a);
6280 }
6281 // add all cats as well
6282 for (auto a : w->allCats()) {
6283 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6284 out.get<RooArgList>()->add(*a);
6285 }
6286 }
6287 return out;
6288}
6289
6291{
6292 xRooNode out(".components", nullptr, *this);
6293
6294 if (auto p = get<RooAddPdf>(); p) {
6295 // only add each pdf once (the coefs will be accumulated in coefs() method) ...
6296 std::set<RooAbsArg *> donePdfs;
6297 for (auto &o : p->pdfList()) {
6298 if (donePdfs.count(o))
6299 continue;
6300 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6301 donePdfs.insert(o);
6302 }
6303 } else if (auto p2 = get<RooRealSumPdf>(); p2) {
6304 // check for common prefixes and suffixes, will use to define aliases to shorten names
6305 // if have more than 1 function
6306 // TString commonPrefix=""; TString commonSuffix="";
6307 // if (p->funcList().size() > 1) {
6308 // bool checked=false;
6309 // for(auto& o : p->funcList()) {
6310 // if (!checked) {
6311 // commonPrefix = o->GetName(); commonSuffix = o->GetName(); checked=true;
6312 // } else {
6313 //
6314 // }
6315 // }
6316 // }
6317 std::set<RooAbsArg *> doneFuncs;
6318 for (auto &o : p2->funcList()) {
6319 if (doneFuncs.count(o))
6320 continue;
6321 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6322 doneFuncs.insert(o);
6323 }
6324 } else if (auto p3 = get<RooAddition>(); p3) {
6325 for (auto &o : p3->list()) {
6326 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6327 }
6328 } else if (auto p4 = get<RooAbsCollection>(); p4) {
6329 for (auto &a : *p4) {
6330 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6331 }
6332 } else if (auto p5 = get<RooWorkspace>(); p5) {
6333 for (auto &o : p5->components()) {
6334 // only top-level nodes (only clients are integrals or things that aren't part of the workspace)
6335 // if (o->hasClients()) continue;
6336 bool hasClients = false;
6337 for (auto &c : o->clients()) {
6338 if (!c->InheritsFrom("RooRealIntegral") && p5 == GETWS(c)) {
6339 hasClients = true;
6340 break;
6341 }
6342 }
6343 if (hasClients)
6344 continue;
6345 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6346 if (o->InheritsFrom("RooAbsPdf")) {
6347 out.back()->fFolder = "!pdfs";
6348 } else {
6349 out.back()->fFolder = "!scratch";
6350 }
6351 }
6352 for (auto &o : p5->allGenericObjects()) {
6353 if (auto fr = dynamic_cast<RooFitResult *>(o); fr) {
6354 TString s(fr->GetTitle());
6355 if (s.Contains(';'))
6356 s = s(0, s.Index(';'));
6357 if (auto _pdf = out.find(s.Data()); _pdf) {
6358 // std::cout << " type = " << _pdf->get()->ClassName() << std::endl;
6359 out.emplace_back(std::make_shared<xRooNode>(fr->GetName(), *fr, _pdf));
6360 // for a while, this node's parent pointed to something of type Node2!!
6361 // how to fix??? - I fxied it with a new constructor to avoid the shared_ptr<Node2> calling the const
6362 // Node2& constructor via getting wrapped in a Node2(shared_ptr<TObject>) call
6363 // out.back()->fParent = _pdf;
6364 // std::cout << " type2 = " << out.back()->fParent->get()->ClassName() << std::endl;
6365 } else {
6366 out.emplace_back(std::make_shared<xRooNode>(fr->GetName(), *fr, *this));
6367 }
6368 out.back()->fFolder = "!fits";
6369 } else {
6370 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6371 if (strcmp(out.back()->get()->ClassName(), "TStyle") == 0) {
6372 out.back()->fFolder = "!styles";
6373 } else if (strcmp(out.back()->get()->ClassName(), "RooStats::HypoTestInverterResult") == 0) {
6374 out.back()->fFolder = "!scans";
6375 } else if (strcmp(out.back()->get()->ClassName(), "RooStats::ModelConfig") == 0) {
6376 out.back()->fFolder = "!models";
6377 } else {
6378 out.back()->fFolder = "!objects";
6379 }
6380 }
6381 }
6382 for (auto &[k, v] : GETWSSETS(p5)) {
6383 // skip 'CACHE' sets because they are auto-removed when sanitizing workspaces, which will invalidate these
6384 // children
6385 if (k.find("CACHE_") == 0)
6386 continue;
6387 out.emplace_back(std::make_shared<xRooNode>(k.c_str(), v, *this));
6388 out.back()->fFolder = "!sets";
6389 }
6390
6392 std::unique_ptr<TIterator> iter(snaps.MakeIterator());
6393 TObject *snap;
6394 while ((snap = iter->Next())) {
6395 out.emplace_back(std::make_shared<xRooNode>(*snap, *this));
6396 out.back()->fFolder = "!snapshots";
6397 }
6398 } else if (auto mc = get<RooStats::ModelConfig>()) {
6399 // add the pdf as a child, and the external constraints set if its there
6400 if (mc->GetPdf()) {
6401 out.emplace_back(std::make_shared<xRooNode>(".pdf", *mc->GetPdf(), *this));
6402 }
6403 if (mc->GetExternalConstraints()) {
6404 out.emplace_back(std::make_shared<xRooNode>(".extCons", *mc->GetExternalConstraints(), *this));
6405 }
6406 } else if (strlen(GetName()) > 0 && GetName()[0] == '!' && fParent) {
6407 // special case of dynamic property
6408 if (TString(GetName()) == "!.pars") {
6409 for (auto &c : fParent->pars()) {
6410 out.emplace_back(c);
6411 }
6412 } else {
6413 // the components of a folder are the children of the parent (after browsing) that live in this folder
6414 fParent->browse();
6415 for (auto &c : *fParent) {
6416 if (c->fFolder == GetName()) {
6417 out.emplace_back(c);
6418 }
6419 }
6420 }
6421 }
6422
6423 return out;
6424}
6425
6426////////////////////////////////////////////////////////////////////////////////
6427/// bins of a channel or sample, or channels of a multi-channel pdf
6428
6430{
6431 xRooNode out(".bins", nullptr, *this);
6432
6433 if (auto p = get<RooSimultaneous>(); p) {
6434 std::map<int, std::shared_ptr<xRooNode>> cats; // fill into a map to preserve index ordering
6435 for (auto &c : p->indexCat()) { // is alphabetical in labels
6436 auto pp = p->getPdf(c.first.c_str());
6437 if (!pp)
6438 continue;
6439 cats[c.second] =
6440 std::make_shared<xRooNode>(TString::Format("%s=%s", p->indexCat().GetName(), c.first.c_str()), *pp, *this);
6441 }
6442 for (auto &[_, n] : cats)
6443 out.emplace_back(n);
6444 } else if (auto phf = get<ParamHistFunc>(); phf) {
6445 int i = 1;
6446#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
6447 auto &pSet = phf->_paramSet;
6448#else
6449 auto &pSet = phf->paramList();
6450#endif
6451 for (auto par : pSet) {
6452 out.emplace_back(std::make_shared<xRooNode>(*par, *this));
6453 out.back()->fBinNumber = i;
6454 i++;
6455 }
6456 } else if (auto ax = GetXaxis(); ax) {
6457 for (int i = 1; i <= ax->GetNbins(); i++) {
6458 // create a RooProduct of all bin-specific factors of all shapeFactors
6459 std::vector<RooAbsArg *> _factors;
6460 for (auto f : factors()) {
6461 if (f->get<ParamHistFunc>()) {
6462 if (f->bins()[i - 1]->get<RooProduct>()) {
6463 for (auto &ss : f->bins()[i - 1]->factors())
6464 _factors.push_back(ss->get<RooAbsArg>());
6465 } else {
6466 _factors.push_back(f->bins()[i - 1]->get<RooAbsArg>());
6467 }
6468 }
6469 }
6470 out.emplace_back(std::make_shared<xRooNode>(
6471 TString::Format("%g<=%s<%g", ax->GetBinLowEdge(i), ax->GetParent()->GetName(), ax->GetBinLowEdge(i + 1)),
6472 _factors.empty() ? nullptr
6473 : std::make_shared<RooProduct>(TString::Format("%s.binFactors.bin%d", GetName(), i),
6474 "binFactors", RooArgList()),
6475 *this));
6476 for (auto f : _factors) {
6477#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
6478 out.back()->get<RooProduct>()->_compRSet.add(*f);
6479#else
6480 const_cast<RooArgList &>(out.back()->get<RooProduct>()->realComponents()).add(*f);
6481#endif
6482 }
6483 out.back()->fBinNumber = i;
6484 }
6485 }
6486
6487 return out;
6488}
6489
6491{
6493
6494 if (recurse && fParent) {
6495 // get our coefs and multiply it by the parents coefs ...
6496 auto ourCoefs = xRooNode::coefs(false);
6497 auto parentCoefs = fParent->coefs(true);
6498 if (!parentCoefs.get<RooAbsReal>()) {
6499 // no coefs to include, just return our coefs
6500 return ourCoefs;
6501 }
6502 if (!ourCoefs.get<RooAbsReal>()) {
6503 // just return the parent's coefs
6504 return parentCoefs;
6505 }
6506 // if got here, must combine parentCoefs and outCoefs into a RooProduct
6507 xRooNode out(".recursiveCoefs",
6508 std::make_shared<RooProduct>(".recursiveCoefs",
6509 TString::Format("Recursive Coefficients of %s", GetName()),
6510 *ourCoefs.get<RooAbsReal>(), *parentCoefs.get<RooAbsReal>()),
6511 *this);
6512 // keep alive the two coef nodes by adding to out's memory
6513 auto mem = out.emplace_back(std::make_shared<xRooNode>(".memory", nullptr, *this));
6514 mem->emplace_back(std::make_shared<xRooNode>(ourCoefs));
6515 mem->emplace_back(std::make_shared<xRooNode>(parentCoefs));
6516 return out;
6517 }
6518
6519 bool isResidual = false;
6520
6521 // if parent is a sumpdf or addpdf then include the coefs
6522 // if func appears multiple times then coefs must be combined into a RooAddition temporary
6523 if (fParent) {
6524 // handle case where filters are applied .. need to pass through these
6525 // do this by iterating while fComp is null
6526 auto parent = fParent;
6527 if (!parent->fComp) {
6528 while (!parent->fComp && parent->fParent) {
6529 parent = parent->fParent;
6530 }
6531 // parent should now be node above the filters ... need parent of that
6532 parent = parent->fParent;
6533 if (!parent)
6534 parent = fParent; // revert t original parent in case something went wrong
6535 }
6536 if (auto p = parent->get<RooRealSumPdf>(); p) {
6537 std::size_t i = 0;
6538 for (auto &o : p->funcList()) {
6539 if (o == get()) {
6540 if (i >= p->coefList().size()) {
6541 isResidual = true;
6542 coefs.add(p->coefList());
6543 } else {
6544 coefs.add(*p->coefList().at(i));
6545 }
6546 }
6547 i++;
6548 }
6549 } else if (auto p2 = parent->get<RooAddPdf>(); p2) {
6550 std::size_t i = 0;
6551 if (p2->coefList().empty()) {
6552 // this can happen if all pdfs are extended then the coef is effectively the
6553 // expected number of events
6554 // TODO: test behaviour of xRooNode under this scenario (are histograms correct?)
6555 } else {
6556 for (auto &o : p2->pdfList()) {
6557 if (o == get()) {
6558 if (i >= p2->coefList().size()) {
6559 isResidual = true;
6560 coefs.add(p2->coefList());
6561 } else {
6562 coefs.add(*p2->coefList().at(i));
6563 }
6564 }
6565 i++;
6566 }
6567 }
6568 }
6569 }
6570 if (isResidual) {
6571 // return a node representing 1.-sumOfCoefs
6572 // involves creating sumOfCoefs unless there is only 1 coef, then just use that
6573 auto coefSum = coefs.empty()
6574 ? nullptr
6575 : (coefs.size() == 1 ? std::shared_ptr<RooAbsArg>(coefs.at(0), [](RooAbsArg *) {})
6576 : std::make_shared<RooAddition>((isResidual) ? ".sumOfCoefs" : ".coefs",
6577 "Coefficients of", coefs));
6578 xRooNode out(".coef", coefSum ? std::dynamic_pointer_cast<RooAbsArg>(std::make_shared<RooFormulaVar>(
6579 ".coef", "1-sum(otherCoefs)", "1. - @0", *coefSum))
6580 : nullptr /* should we return a "1." instead? */);
6581 if (coefSum && coefs.size() != 1) {
6582 out.emplace_back(std::make_shared<xRooNode>(".memory", nullptr, *this))
6583 ->emplace_back(
6584 std::make_shared<xRooNode>(".sumOfCoefs", coefSum, out)); // added to keep the sum alive! with the node
6585 }
6586 if (!coefs.empty()) {
6587 out.browse();
6588 }
6589 return out;
6590 } else if (coefs.size() == 1) {
6591 xRooNode out(".coef", std::shared_ptr<RooAbsArg>(coefs.at(0), [](RooAbsArg *) {}), *this);
6592 if (!coefs.empty()) {
6593 out.browse();
6594 }
6595 return out;
6596 } else {
6597 auto coefSum =
6598 coefs.empty()
6599 ? nullptr
6600 : std::make_shared<RooAddition>(".coefs", TString::Format("Coefficients of %s", GetName()), coefs);
6601 xRooNode out(".coefs", coefSum, *this);
6602 if (!coefs.empty())
6603 out.browse();
6604
6605 return out;
6606 }
6607}
6608
6610{
6611 xRooNode out(".factors", nullptr, *this);
6612
6613 if (auto p = get<RooProdPdf>(); p) {
6614 auto _main = mainChild();
6615 if (auto a = _main.get<RooRealSumPdf>(); a && !a->getStringAttribute("alias")) {
6616 a->setStringAttribute("alias", "samples");
6617 } else if (auto a2 = _main.get<RooAddPdf>(); a2 && !a2->getStringAttribute("alias")) {
6618 a2->setStringAttribute("alias", "components");
6619 }
6620 int _npdfs = p->pdfList().size();
6621 for (auto &o : p->pdfList()) {
6622 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6623 if (_npdfs > 5 && o != _main.get() && out.back()->robs().size() == 0) // constraints have no robs in them
6624 out.back()->fFolder = "!constraints";
6625 }
6626 } else if (auto p2 = get<RooProduct>(); p2) {
6627 for (auto &o : p2->components()) {
6628 if (o->InheritsFrom("RooProduct")) {
6629 // get factors of this term
6630 auto x = xRooNode("tmp", *o, *this).factors();
6631 for (auto &n : x) {
6632 out.emplace_back(std::make_shared<xRooNode>(n->GetName(), n->fComp, *this));
6633 }
6634 } else {
6635 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6636 }
6637 }
6638 } else if (auto w = get<RooWorkspace>(); w) {
6639 // if workspace, return all functions (not pdfs) that have a RooProduct as one of their clients
6640 // or not clients
6641 // exclude obs and globs
6642 auto oo = obs(); // need to keep alive as may contain owning globs
6643 auto &_obs = *(oo.get<RooArgList>());
6644 for (auto a : w->allFunctions()) {
6645 if (_obs.contains(*a))
6646 continue;
6647 bool show(true);
6648 for (auto c : a->clients()) {
6649 show = false;
6650 if (c->InheritsFrom("RooProduct")) {
6651 show = true;
6652 break;
6653 }
6654 }
6655 if (show)
6656 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6657 }
6658 }
6659
6660 /*
6661 // if parent is a sumpdf or addpdf then include the coefs
6662 // if func appears multiple times then coefs must be combined into a RooAddition temporary
6663 if (fParent) {
6664 RooArgList coefs;
6665 if(auto p = fParent->get<RooRealSumPdf>();p) {
6666 int i=0;
6667 for(auto& o : p->funcList()) {
6668 if (o == get()) {
6669 coefs.add( *p->coefList().at(i) );
6670 }
6671 i++;
6672 }
6673 } else if(auto p = fParent->get<RooAddPdf>(); p) {
6674 int i=0;
6675 for(auto& o : p->pdfList()) {
6676 if (o == get()) {
6677 coefs.add( *p->coefList().at(i) );
6678 }
6679 i++;
6680 }
6681 }
6682 if (!coefs.empty()) {
6683 if (coefs.size() == 1) {
6684 if (strcmp(coefs.at(0)->GetName(),"1")) { // don't add the "1"
6685 out.emplace_back(std::make_shared<Node2>(".coef", *coefs.at(0), *this));
6686 }
6687 } else {
6688 out.emplace_back(std::make_shared<Node2>(".coefs",
6689 std::make_shared<RooAddition>(".coefs", "Coefficients of",
6690 coefs), *this));
6691 }
6692 }
6693 }
6694 */
6695 return out;
6696}
6697
6699{
6700 xRooNode out(".variations", nullptr, *this);
6701
6702 // if (auto p = get<RooSimultaneous>(); p) {
6703 // for (auto &c : p->indexCat()) {
6704 // auto pp = p->getPdf(c.first.c_str());
6705 // if (!pp)
6706 // continue;
6707 // out.emplace_back(
6708 // std::make_shared<xRooNode>(TString::Format("%s=%s", p->indexCat().GetName(), c.first.c_str()), *pp,
6709 // *this));
6710 // }
6711 // } else
6712 if (auto p2 = get<PiecewiseInterpolation>(); p2) {
6713#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
6714 out.emplace_back(std::make_shared<xRooNode>("nominal", p2->_nominal.arg(), *this));
6715#else
6716 out.emplace_back(std::make_shared<xRooNode>("nominal", *(p2->nominalHist()), *this));
6717#endif
6718 for (size_t i = 0; i < p2->paramList().size(); i++) {
6719 // TODO: should we only return one if we find they are symmetrized?
6720 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p2->paramList().at(i)->GetName()),
6721 *p2->highList().at(i), *this));
6722 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p2->paramList().at(i)->GetName()),
6723 *p2->lowList().at(i), *this));
6724 }
6726#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
6727 out.emplace_back(std::make_shared<xRooNode>("nominal", RooFit::RooConst(p3->_nominal), *this));
6728 for (size_t i = 0; i < p3->_paramList.size(); i++) {
6729 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p3->_paramList.at(i)->GetName()),
6730 RooFit::RooConst(p3->_high.at(i)), *this));
6731 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p3->_paramList.at(i)->GetName()),
6732 RooFit::RooConst(p3->_low.at(i)), *this));
6733 }
6734#else
6735 out.emplace_back(std::make_shared<xRooNode>("nominal", RooFit::RooConst(p3->nominal()), *this));
6736 for (size_t i = 0; i < p3->variables().size(); i++) {
6737 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p3->variables().at(i)->GetName()),
6738 RooFit::RooConst(p3->high().at(i)), *this));
6739 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p3->variables().at(i)->GetName()),
6740 RooFit::RooConst(p3->low().at(i)), *this));
6741 }
6742#endif
6743
6744 } else if (auto p4 = get<ParamHistFunc>(); p4) {
6745 // I *think* I put this here so that can browse into a ParamHistFunc
6746 // int i = 0;
6747 // for (auto par : p4->_paramSet) {
6748 // TString _name = par->GetName();
6749 // // if(auto _v = dynamic_cast<RooRealVar*>(p->_dataSet.get(i)->first()); _v) {
6750 // // _name = TString::Format("%s=%g",_v->GetName(),_v->getVal());
6751 // // }
6752 // // out.emplace_back(std::make_shared<xRooNode>(_name,*par,*this)); -- -removed cos now have bin()
6753 // method i++;
6754 // }
6755 }
6756 return out;
6757}
6758
6760{
6761 RooArgList out;
6762 out.setName(GetName());
6763 for (auto &k : *this) {
6764 if (auto o = k->get<RooAbsArg>(); o)
6765 out.add(*o);
6766 }
6767 return out;
6768}
6769
6771{
6772 xRooNode out(".datasets()", nullptr, *this);
6773 // removed the browse operation since no longer showing '.datasets()' in browser
6774 // and otherwise this means dataset reduction operation will be called every time we 'browse()' the datasets node
6775 // out.fBrowseOperation = [](xRooNode *f) { return f->fParent->datasets(); };
6776
6777 if (auto _ws = get<RooWorkspace>(); _ws) {
6778 for (auto &d : _ws->allData()) {
6779 out.emplace_back(std::make_shared<xRooNode>(*d, *this));
6780 out.back()->fFolder = "!datasets";
6781 }
6782 } else if (get<RooAbsPdf>() ||
6783 (!get() && fParent &&
6784 fParent->get<RooAbsPdf>())) { // second condition handles 'bins' nodes of pdf, which have null ptr
6785 // only add datasets that have observables that cover all our observables
6786 auto oo = obs(); // must keep alive in case is owning the globs
6787 RooArgSet _obs(*oo.get<RooArgList>());
6788 //_obs.add(coords(true).argList(), true); // include coord observables too, and current xaxis if there's one -
6789 // added in loop below
6790
6791 TString cut;
6793 for (auto _c : coords()) { // coords() moves vars to their respective coordinates too
6794 if (auto _cat = _c->get<RooAbsCategoryLValue>(); _cat) {
6795 if (cut != "")
6796 cut += " && ";
6797 cut += TString::Format("%s==%d", _cat->GetName(), _cat->getCurrentIndex());
6798 _obs.add(*_cat,
6799 true); // note: if we ever changed coords to return clones, would need to keep coords alive
6800 cutobs.add(*_cat);
6801 } else if (auto _rv = _c->get<RooAbsRealLValue>(); _rv) {
6802 // todo: check coordRange is a single range rather than multirange
6803 if (cut != "")
6804 cut += " && ";
6805 cut += TString::Format("%s>=%f&&%s<%f", _rv->GetName(), _rv->getMin(_rv->getStringAttribute("coordRange")),
6806 _rv->GetName(), _rv->getMax(_rv->getStringAttribute("coordRange")));
6807 _obs.add(*_rv,
6808 true); // note: if we ever changed coords to return clones, would need to keep coords alive
6809 cutobs.add(*_rv);
6810 } else {
6811 throw std::runtime_error("datasets(): Unsupported coordinate type");
6812 }
6813 }
6814 if (auto s = get<RooSimultaneous>()) {
6815 // check if we have a pdf for every category ... if not then add to cut
6816 bool hasMissing = false;
6817 TString extraCut = "";
6818 for (auto cat : s->indexCat()) {
6819 if (!s->getPdf(cat.first.c_str())) {
6820 hasMissing = true;
6821 } else {
6822 if (extraCut != "")
6823 extraCut += " || ";
6824 extraCut += TString::Format("%s==%d", s->indexCat().GetName(), cat.second);
6825 }
6826 }
6827 if (hasMissing) {
6828 if (cut != "")
6829 cut += " && ";
6830 cut += "(" + extraCut + ")";
6831 cutobs.add(s->indexCat());
6832 }
6833 }
6834
6835 if (auto ax = GetXaxis(); ax && dynamic_cast<RooAbsArg *>(ax->GetParent())->getAttribute("obs")) {
6836 auto a = dynamic_cast<RooAbsArg *>(ax->GetParent());
6837 _obs.add(*a, true);
6838 }
6839 xRooNode _datasets; // will be any child datasets, along with datasets of the workspace
6840 for (auto &child : *this) {
6841 if (child->get<RooAbsData>())
6842 _datasets.push_back(child);
6843 }
6844 if (auto __ws = ws(); __ws) {
6845 xRooNode _wsNode(*__ws, *this);
6846 for (auto &d : _wsNode.datasets()) {
6847 _datasets.push_back(d);
6848 }
6849 }
6850
6851 for (auto &d : _datasets) {
6852 if (std::unique_ptr<RooAbsCollection>(d->obs().argList().selectCommon(_obs))->size() == _obs.size()) {
6853 // all obs present .. include
6854
6855 if (cut != "") {
6856 RooFormulaVar cutFormula("cut1", cut, cutobs); // doing this to avoid complaints about unused vars
6857 // TODO: Could consider using a 'filter' node (see filter() method) applied to the dataset instead
6858 // of creating and using a reduced dataset here
6859 out.emplace_back(std::make_shared<xRooNode>(
6860 std::shared_ptr<RooAbsData>(d->get<RooAbsData>()->reduce(
6861 *std::unique_ptr<RooAbsCollection>(d->robs().get<RooArgList>()->selectCommon(_obs)), cutFormula)),
6862 *this));
6863 // put a subset of the globs in the returned dataset too
6864 out.back()->get<RooAbsData>()->setGlobalObservables(*std::unique_ptr<RooAbsCollection>(
6865 d->globs().get<RooArgList>()->selectCommon(*globs().get<RooArgList>())));
6866 if (d->get()->TestBit(1 << 20))
6867 out.back()->get()->SetBit(1 << 20);
6868 // need to attach the original dataset so that things like SetBinContent can interact with it
6869 out.back()->fBrowsables.emplace_back(std::make_shared<xRooNode>(".sourceds", d->fComp, *this));
6870 } else {
6871 out.emplace_back(std::make_shared<xRooNode>(d->fComp, *this));
6872 }
6873 }
6874 }
6875 /*else if(auto p = get<RooFitResult>(); p) {
6876 // look for datasets in workspace that match the fit result name after hashing
6877 for(auto& _d : xRooNode(*_ws,*this).datasets()) {
6878 auto _hash = RooAbsTree::nameToHash(_d->get()->GetName());
6879 if (TString::Format("%d;%d",_hash.first,_hash.second) == p->GetTitle()) {
6880 out.emplace_back(std::make_shared<xRooNode>(_d->fComp, *this));
6881 }
6882 }
6883 }*/
6884 } else if (auto mc = get<RooStats::ModelConfig>()) {
6885 return xRooNode(*mc->GetPdf(), fParent).datasets();
6886 }
6887
6888 return out;
6889}
6890
6891std::shared_ptr<xRooNode> xRooNode::getBrowsable(const char *name) const
6892{
6893 for (auto b : fBrowsables) {
6894 if (b && strcmp(b->GetName(), name) == 0)
6895 return b;
6896 }
6897 return nullptr;
6898}
6899
6901{
6902
6903 if (auto fr = get<RooFitResult>(); fr) {
6904 return nullptr;
6905 }
6906
6907 if (auto theData = get<RooDataSet>(); theData) {
6908
6909 TH1 *theHist = nullptr;
6910
6911 if (fromPad) {
6912 // find first histogram in pad
6913 for (auto o : *fromPad->GetListOfPrimitives()) {
6914 theHist = dynamic_cast<TH1 *>(o);
6915 if (theHist) {
6916 theHist = static_cast<TH1 *>(theHist->Clone());
6917 theHist->Reset();
6918 break;
6919 } // clone because theHist gets deleted below
6920 }
6921 }
6922
6923 if (!theHist) {
6924 auto _parentPdf = parentPdf();
6925 if (!_parentPdf) {
6926 // can still build graph if v is an obs ... will use v binning
6927 auto vo = dynamic_cast<TObject *>(v);
6928 if (v && obs().find(vo->GetName())) {
6929 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(v)) {
6930 theHist = new TH1D(
6931 TString::Format("%s_%s", GetName(), vo->GetName()),
6932 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
6933 cat->numTypes(), 0, cat->numTypes());
6934 int i = 1;
6935 std::map<int, std::string> cats; // fill into a map to preserve index ordering
6936 for (auto &c : *cat) {
6937 cats[c.second] = c.first;
6938 }
6939 for (auto &[_, label] : cats) {
6940 theHist->GetXaxis()->SetBinLabel(i++, label.c_str());
6941 }
6942 } else {
6943 auto _binning = v->getBinningPtr(nullptr);
6944 if (_binning->isUniform()) {
6945 theHist = new TH1D(
6946 TString::Format("%s_%s", GetName(), vo->GetName()),
6947 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
6948 v->numBins(), _binning->lowBound(), _binning->highBound());
6949 } else {
6950 theHist = new TH1D(
6951 TString::Format("%s_%s", GetName(), vo->GetName()),
6952 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
6953 v->numBins(), _binning->array());
6954 }
6955 }
6956 } else {
6957 throw std::runtime_error("Cannot draw dataset without parent PDF");
6958 }
6959 } else {
6960 theHist = _parentPdf->BuildHistogram(v, true);
6961 }
6962 }
6963 if (!theHist)
6964 return nullptr;
6965 // this hist will get filled with w*x to track weighted x position per bin
6966 TH1 *xPos = static_cast<TH1 *>(theHist->Clone("xPos"));
6967 xPos->Reset();
6968 TH1 *xPos2 = static_cast<TH1 *>(theHist->Clone("xPos2"));
6969 xPos2->Reset();
6970 auto nHist = std::unique_ptr<TH1>(static_cast<TH1 *>(theHist->Clone("nEntries")));
6971 nHist->Reset();
6972
6973 auto dataGraph = new TGraphAsymmErrors;
6974 dataGraph->SetEditable(false);
6975 dataGraph->SetName(GetName());
6976 dataGraph->SetTitle(strlen(theData->GetTitle()) ? theData->GetTitle() : theData->GetName());
6977 // next line triggers creation of the histogram inside the graph, in root 6.22 that isn't protected from being
6978 // added to gDirectory
6979 dataGraph->SetTitle(TString::Format("%s;%s;Events", dataGraph->GetTitle(), theHist->GetXaxis()->GetTitle()));
6980 *static_cast<TAttMarker *>(dataGraph) = *static_cast<TAttMarker *>(theHist);
6981 *static_cast<TAttLine *>(dataGraph) = *static_cast<TAttLine *>(theHist);
6982 dataGraph->SetMarkerStyle(20);
6983 dataGraph->SetLineColor(kBlack);
6984 dataGraph->SetMarkerSize(gStyle->GetMarkerSize());
6985
6986 auto _obs = obs();
6987
6988 // auto x = theData->get()->find((v) ? dynamic_cast<TObject*>(v)->GetName() : theHist->GetXaxis()->GetName());
6989 // const RooAbsReal* xvar = (x) ? dynamic_cast<RooAbsReal*>(x) : nullptr;
6990 // const RooAbsCategory* xcat = (x && !xvar) ? dynamic_cast<RooAbsCategory*>(x) : nullptr;
6991 auto x = _obs.find((v) ? dynamic_cast<TObject *>(v)->GetName()
6992 : (theHist->GetXaxis()->IsAlphanumeric() ? theHist->GetXaxis()->GetTimeFormatOnly()
6993 : theHist->GetXaxis()->GetName()));
6994 if (x && x->get<RooAbsArg>()->getAttribute("global")) {
6995 // is global observable ...
6996 dataGraph->SetPoint(0, x->get<RooAbsReal>()->getVal(), 1e-15);
6997 dataGraph->SetTitle(TString::Format("%s = %f", dataGraph->GetTitle(), dataGraph->GetPointX(0)));
6998 delete xPos;
6999 delete xPos2;
7000 delete theHist;
7001 return dataGraph;
7002 }
7003
7004 const RooAbsReal *xvar = (x) ? x->get<RooAbsReal>() : nullptr;
7005 const RooAbsCategory *xcat = (x && !xvar) ? x->get<RooAbsCategory>() : nullptr;
7006
7007 auto _coords = coords();
7008
7009 TString pName((fromPad) ? fromPad->GetName() : "");
7010 auto _pos = pName.Index('=');
7011
7012 int nevent = theData->numEntries();
7013 for (int i = 0; i < nevent; i++) {
7014 theData->get(i);
7015 bool _skip = false;
7016 for (auto _c : _coords) {
7017 if (auto cat = _c->get<RooAbsCategoryLValue>(); cat) {
7018 if (cat->getIndex() != theData->get()->getCatIndex(cat->GetName())) {
7019 _skip = true;
7020 break;
7021 }
7022 } else if (auto rv = _c->get<RooAbsRealLValue>(); rv) {
7023 // must be in range
7024 if (!rv->inRange(theData->get()->getRealValue(rv->GetName()), rv->getStringAttribute("coordRange"))) {
7025 _skip = true;
7026 break;
7027 }
7028 }
7029 }
7030 if (_pos != -1) {
7031 if (auto cat = dynamic_cast<RooAbsCategory *>(theData->get()->find(TString(pName(0, _pos))));
7032 cat && cat->getLabel() != pName(_pos + 1, pName.Length())) {
7033 _skip = true;
7034 }
7035 }
7036 if (_skip)
7037 continue;
7038
7039 if (xvar) {
7040 xPos->Fill(xvar->getVal(), xvar->getVal() * theData->weight());
7041 xPos2->Fill(xvar->getVal(), pow(xvar->getVal(), 2) * theData->weight());
7042 }
7043
7044 if (xcat) {
7045 theHist->Fill(xcat->getLabel(), theData->weight());
7046 nHist->Fill(xcat->getLabel(), 1);
7047 } else {
7048 theHist->Fill((x) ? xvar->getVal() : 0.5, theData->weight());
7049 nHist->Fill((x) ? xvar->getVal() : 0.5, 1);
7050 }
7051 }
7052
7053 xPos->Divide(theHist);
7054 xPos2->Divide(theHist);
7055
7056 // update the x positions to the means for each bin and use poisson asymmetric errors for data ..
7057 for (int i = 0; i < theHist->GetNbinsX(); i++) {
7058 if (includeZeros || nHist->GetBinContent(i + 1)) {
7059 double val = theHist->GetBinContent(i + 1);
7060
7061 dataGraph->SetPoint(dataGraph->GetN(),
7062 (xvar && val) ? xPos->GetBinContent(i + 1) : theHist->GetBinCenter(i + 1), val);
7063
7064 // x-error will be the (weighted) standard deviation of the x values ...
7065 double xErr = xPos2->GetBinContent(i + 1) - pow(xPos->GetBinContent(i + 1), 2);
7066 xErr = (xErr <= 0) ? 0. : sqrt(xErr); // protects against floating point rounding effects
7067
7068 if (xErr || val) {
7069 dataGraph->SetPointError(dataGraph->GetN() - 1, xErr, xErr,
7070 val - 0.5 * TMath::ChisquareQuantile(TMath::Prob(1, 1) / 2., 2. * (val)),
7071 0.5 * TMath::ChisquareQuantile(1. - TMath::Prob(1, 1) / 2., 2. * (val + 1)) -
7072 val);
7073 }
7074 }
7075 }
7076
7077 // transfer limits from theHist to dataGraph hist
7078 dataGraph->GetHistogram()->GetXaxis()->SetLimits(theHist->GetXaxis()->GetXmin(), theHist->GetXaxis()->GetXmax());
7079 // and bin labels, if any
7080 if (xcat) {
7081 dataGraph->GetHistogram()->GetXaxis()->Set(theHist->GetNbinsX(), 0, theHist->GetNbinsX());
7082 for (int i = 1; i <= theHist->GetNbinsX(); i++)
7083 dataGraph->GetHistogram()->GetXaxis()->SetBinLabel(i, theHist->GetXaxis()->GetBinLabel(i));
7084 }
7085
7086 delete xPos;
7087 delete xPos2;
7088 delete theHist;
7089
7090 // std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case getObject
7091 // has decided to return the owning ptr (for some reason) std::string _title =
7092 // strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(); if (!gROOT->GetStyle(_title.c_str()))
7093 // {
7094 // if ( (style = getObject<TStyle>(_title)) ) {
7095 // // loaded style (from workspace?) so put in list and use that
7096 // gROOT->GetListOfStyles()->Add(style.get());
7097 // } else {
7098 // // create new style - gets put in style list automatically so don't have to delete
7099 // // acquire them so saved to workspaces for auto reload ...
7100 // style = const_cast<xRooNode&>(*this).acquireNew<TStyle>(_title.c_str(),
7101 // TString::Format("Style for %s component", _title.c_str()));
7102 // (TAttLine &) (*style) = *dynamic_cast<TAttLine *>(dataGraph);
7103 // (TAttFill &) (*style) = *dynamic_cast<TAttFill *>(dataGraph);
7104 // (TAttMarker &) (*style) = *dynamic_cast<TAttMarker *>(dataGraph);
7105 // gROOT->GetListOfStyles()->Add(style.get());
7106 // }
7107 // }
7108 auto _styleNode = styles(dataGraph);
7109 if (auto _style = _styleNode.get<TStyle>()) {
7110 *dynamic_cast<TAttLine *>(dataGraph) = *_style;
7111 *dynamic_cast<TAttFill *>(dataGraph) = *_style;
7112 *dynamic_cast<TAttMarker *>(dataGraph) = *_style;
7113 }
7114 return dataGraph;
7115 }
7116
7117 throw std::runtime_error("Cannot build graph");
7118}
7119
7121{
7122 if (fr) {
7123 if (auto _w = ws(); _w) {
7124 auto res = acquire(std::shared_ptr<RooFitResult>(const_cast<RooFitResult *>(fr), [](RooFitResult *) {}));
7125 for (auto o : _w->allGenericObjects()) {
7126 if (auto _fr = dynamic_cast<RooFitResult *>(o); _fr) {
7127 _fr->ResetBit(1 << 20);
7128 }
7129 }
7130 res->SetBit(1 << 20);
7131 // assign values
7132 auto allVars = _w->allVars();
7133 allVars = fr->floatParsFinal();
7134 allVars = fr->constPars();
7135 } else {
7136 // need to add to memory as a specific name
7137 throw std::runtime_error("Not supported yet"); // complication is how to replace an existing fitResult in
7138 // .memory auto _clone = std::make_shared<RooFitResult>(*fr);
7139 //_clone->SetName("fitResult");
7140 }
7141 } else {
7143 }
7144}
7145
7147{
7148 if (auto _fr = fr.get<const RooFitResult>()) {
7150 } else
7151 throw std::runtime_error("Not a RooFitResult");
7152}
7153
7154xRooNode xRooNode::fitResult(const char *opt) const
7155{
7156
7157 if (get<RooFitResult>())
7158 return *this;
7159 if (get<RooAbsData>()) {
7160 if (auto _fr = find(".fitResult"); _fr)
7161 return _fr;
7162#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
7163 // check if weightVar of RooAbsData has fitResult attribute on it, will be the generation fit result
7164 if (get<RooDataSet>() && get<RooDataSet>()->weightVar() &&
7165 get<RooDataSet>()->weightVar()->getStringAttribute("fitResult")) {
7166 return xRooNode(getObject<const RooFitResult>(get<RooDataSet>()->weightVar()->getStringAttribute("fitResult")),
7167 *this);
7168 }
7169#endif
7170 return xRooNode();
7171 }
7172
7173 TString sOpt(opt);
7174 if (sOpt == "prefit") {
7175 // build a fitResult using nominal values and infer errors from constraints
7176 // that aren't the 'main' constraints
7177 // Warning("fitResult","Building prefitResult by examining pdf. Consider setting an explicit prefitResult
7178 // (SetFitResult(fr)) where fr name is prefitResult");
7179
7180 // ensure coefs are included if there are any
7181 auto _coefs = coefs();
7182 if (_coefs.get()) {
7183 return xRooNode(RooProduct("tmp", "tmp", RooArgList(*get<RooAbsArg>(), *_coefs.get<RooAbsReal>())))
7184 .fitResult(opt);
7185 }
7186
7187 std::unique_ptr<RooArgList> _pars(dynamic_cast<RooArgList *>(pars().argList().selectByAttrib("Constant", false)));
7188 auto fr = std::make_shared<RooFitResult>("prefitResult", "Prefit");
7189 fr->setFinalParList(*_pars);
7190 for (auto &p : fr->floatParsFinal()) {
7191 auto _v = dynamic_cast<RooRealVar *>(p);
7192 if (!_v)
7193 continue;
7194 if (auto s = _v->getStringAttribute("nominal"); s)
7195 _v->setVal(TString(s).Atof());
7196 auto _constr = xRooNode(fParent->getObject<RooRealVar>(p->GetName()), *this).constraints();
7197 std::shared_ptr<xRooNode> pConstr;
7198 for (auto &c : _constr) {
7199 if (c->get<RooPoisson>() || c->get<RooGaussian>()) {
7200 // require parameter to be a direct server of the constraint pdf to count
7201 bool isServer = true;
7202 if (c->get<RooGaussian>()) {
7203 isServer = false;
7204 for (auto s : c->get<RooAbsArg>()->servers()) {
7205 if (strcmp(s->GetName(), p->GetName()) == 0) {
7206 isServer = true;
7207 break;
7208 }
7209 }
7210 }
7211 if (isServer) {
7212 pConstr = c;
7213 break;
7214 }
7215 }
7216 }
7217 if (pConstr) {
7218 // there will be 3 deps, one will be this par, the other two are the mean and error (or error^2 in case of
7219 // poisson use the one that's a ConstVar as the error to break a tie ...
7220 double prefitVal = 0;
7221 double prefitError = 0;
7222 for (auto &_d : pConstr->vars()) {
7223 if (strcmp(p->GetName(), _d->get()->GetName()) == 0)
7224 continue;
7225 if (auto _c = _d->get<RooConstVar>(); _c && _c->getVal() != 0) {
7226 if (prefitError)
7227 prefitVal = prefitError; // loading val into error already, so move it over
7228 prefitError = _c->getVal();
7229 } else if (prefitError == 0) {
7230 prefitError = _d->get<RooAbsReal>()->getVal();
7231 } else {
7232 prefitVal = _d->get<RooAbsReal>()->getVal();
7233 }
7234 }
7235
7236 if (pConstr->get<RooGaussian>() && pConstr->browse().find(".sigma")) {
7237 prefitError = pConstr->find(".sigma")->get<RooAbsReal>()->getVal();
7238 }
7239 // std::cout << p->GetName() << " extracted " << prefitVal << " " << prefitError << " from ";
7240 // pConstr->deps().Print();
7241 if (pConstr->get<RooPoisson>()) {
7242 // prefitVal will be the global observable value, need to divide that by tau
7244 // prefiterror will be tau ... need 1/sqrt(tau) for error
7245 prefitError = 1. / sqrt(prefitError);
7246 }
7247 if (!_v->getStringAttribute("nominal"))
7248 _v->setVal(prefitVal);
7249 _v->setError(prefitError);
7250 } else {
7251 // unconstrained, remove error
7252 _v->removeError();
7253 }
7254 }
7255 auto _args = consts().argList();
7256 _args.add(pp().argList());
7257 // global obs are added to constPars list too
7258 auto _globs = globs(); // keep alive as may own glob
7259 _args.add(_globs.argList());
7260 fr->setConstParList(_args);
7261 std::unique_ptr<RooArgList> _snap(dynamic_cast<RooArgList *>(_pars->snapshot()));
7262 for (auto &p : *_snap) {
7263 if (auto atr = p->getStringAttribute("initVal"); atr && dynamic_cast<RooRealVar *>(p))
7264 dynamic_cast<RooRealVar *>(p)->setVal(TString(atr).Atof());
7265 }
7266 fr->setInitParList(*_snap);
7267 return xRooNode(fr, *this);
7268 }
7269
7270 // return first checked fit result present in the workspace
7271 if (auto _w = ws(); _w) {
7272 auto checkFr = [&](TObject *o) {
7273 if (auto _fr = dynamic_cast<RooFitResult *>(o); _fr && _fr->TestBit(1 << 20)) {
7274 // check all pars match final/const values ... if mismatch need to create a new RooFitResult
7278 for (auto p : pars()) {
7279 if (p->get<RooAbsArg>()->getAttribute("Constant") || p->get<RooConstVar>()) {
7280 // par must not be in the float list or have different value to what is in constPars (if it is there)
7281 if (_fr->floatParsFinal().find(p->GetName()) ||
7282 (p->get<RooAbsReal>() &&
7283 std::abs(_fr->constPars().getRealValue(p->GetName(), std::numeric_limits<double>::quiet_NaN()) -
7284 p->get<RooAbsReal>()->getVal()) > 1e-15) ||
7285 (p->get<RooAbsCategory>() &&
7286 p->get<RooAbsCategory>()->getCurrentIndex() !=
7287 _fr->constPars().getCatIndex(p->GetName(), std::numeric_limits<int>().max()))) {
7288 newConsts.add(*p->get<RooAbsArg>());
7289 }
7290 } else {
7291 // floating par must be present in the floatPars list with the same value
7292 if (!_fr->floatParsFinal().find(p->GetName())) {
7293 newFloats.add(*p->get<RooAbsArg>());
7294 } else if ((p->get<RooAbsReal>() &&
7295 std::abs(_fr->floatParsFinal().getRealValue(p->GetName(),
7296 std::numeric_limits<double>::quiet_NaN()) -
7297 p->get<RooAbsReal>()->getVal()) > 1e-15) ||
7298 (p->get<RooAbsCategory>() &&
7299 p->get<RooAbsCategory>()->getCurrentIndex() !=
7300 _fr->floatParsFinal().getCatIndex(p->GetName(), std::numeric_limits<int>().max()))) {
7301 // value of existing float changed
7302 oldFloats.add(*p->get<RooAbsArg>());
7303 }
7304 }
7305 }
7306 if (!oldFloats.empty() || !newFloats.empty() || !newConsts.empty()) {
7307 // create new fit result using covariance from the fit result
7308 // remove any new consts from the list before extracting covariance matrix
7309 RooArgList existingFloats(_fr->floatParsFinal());
7310 existingFloats.remove(newConsts, true, true /* match name*/);
7311 auto cov = _fr->reducedCovarianceMatrix(existingFloats);
7312 if (!newFloats.empty()) {
7313 // extend the covariance matrix and add variances using current parameter errors
7314 size_t oldSize = existingFloats.size();
7315 cov.ResizeTo(oldSize + newFloats.size(), oldSize + newFloats.size());
7316 for (size_t i = 0; i < newFloats.size(); i++) {
7317 existingFloats.add(*newFloats.at(i));
7318 auto v = dynamic_cast<RooRealVar *>(newFloats.at(i));
7319 if (v)
7320 cov(oldSize + i, oldSize + i) = std::pow(v->getError(), 2);
7321 }
7322 }
7323 RooArgList existingConsts(_fr->constPars());
7324 existingConsts.remove(newFloats, true, true);
7326
7327 // do we need to add our remaining const pars to the const par list? or the globs?
7328 // for speed we wont bother
7329 // note that generating datasets needs the globs in the const pars list so the robs can be determined
7330 // at the moment this check is done in the generate() method (along with check for missing pars)
7331
7332 auto fr = std::make_shared<RooFitResult>(TString::Format("%s-dirty", _fr->GetName()));
7333 fr->setFinalParList(existingFloats);
7334 fr->setConstParList(existingConsts);
7335 fr->setCovarianceMatrix(cov);
7336 fr->setInitParList(_fr->floatParsInit()); // will only be the pars that were actually float for the fit
7337
7338 return xRooNode(fr, *this);
7339 } else {
7340 // all matching, can return the fit result as-is
7341 return xRooNode(*_fr, std::make_shared<xRooNode>(*_w, std::make_shared<xRooNode>()));
7342 }
7343 }
7344 return xRooNode();
7345 };
7346 for (auto o : _w->allGenericObjects()) {
7347 auto out = checkFr(o);
7348 if (out)
7349 return out;
7350 }
7351 for (auto o : GETWSSNAPSHOTS(_w)) {
7352 auto out = checkFr(o);
7353 if (out)
7354 return out;
7355 }
7356 } else {
7357 // objects not in workspaces are allowed to have a fitResult set in their memory
7358 // use getObject to get it
7359 if (auto fr = getObject<RooFitResult>(".fitResult"); fr) {
7360 return xRooNode(fr, *this);
7361 }
7362 }
7363
7364 // ensure coefs are included if there are any
7365 auto _coefs = coefs();
7366 if (_coefs.get()) {
7367 return xRooNode(RooProduct("tmp", "tmp", RooArgList(*get<RooAbsArg>(), *_coefs.get<RooAbsReal>())))
7368 .fitResult(opt);
7369 }
7370
7371 std::unique_ptr<RooArgList> _pars(dynamic_cast<RooArgList *>(pars().argList().selectByAttrib("Constant", false)));
7372 auto fr = std::make_shared<RooFitResult>(TUUID().AsString());
7373 fr->SetTitle(TString::Format("%s uncorrelated parameter snapshot", GetName()));
7374 fr->setFinalParList(*_pars);
7375 fr->setStatus(-1);
7376
7377 TMatrixDSym cov(fr->floatParsFinal().size());
7378 TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr.get(), _VM));
7379 if (prevCov) {
7380 for (int i = 0; i < prevCov->GetNcols(); i++) {
7381 for (int j = 0; j < prevCov->GetNrows(); j++) {
7382 cov(i, j) = (*prevCov)(i, j);
7383 }
7384 }
7385 }
7386 int i = 0;
7387 for (auto &p : fr->floatParsFinal()) {
7388 if (!prevCov || i >= prevCov->GetNcols()) {
7389 if (auto v = dynamic_cast<RooRealVar *>(p)) {
7390 cov(i, i) = pow(v->getError(), 2);
7391 } else {
7392 cov(i, i) = 0;
7393 }
7394 }
7395 i++;
7396 }
7397 int covQualBackup = fr->covQual();
7398 fr->setCovarianceMatrix(cov);
7399 fr->setCovQual(covQualBackup);
7400
7401 auto _args = consts().argList();
7402 _args.add(pp().argList());
7403 // global obs are added to constPars list too
7404 auto _globs = globs(); // keep alive as may own glob
7405 _args.add(_globs.argList());
7406 fr->setConstParList(_args);
7407 std::unique_ptr<RooArgList> _snap(dynamic_cast<RooArgList *>(_pars->snapshot()));
7408 for (auto &p : *_snap) {
7409 if (auto atr = p->getStringAttribute("initVal"); atr && dynamic_cast<RooRealVar *>(p))
7410 dynamic_cast<RooRealVar *>(p)->setVal(TString(atr).Atof());
7411 }
7412 fr->setInitParList(*_snap);
7413
7414 // return *const_cast<Node2*>(this)->emplace_back(std::make_shared<Node2>(".fitResult",fr,*this));
7415 return xRooNode(fr, *this);
7416}
7417
7418// xRooNode xRooNode::fitTo_(const char* datasetName) const {
7419// try {
7420// return fitTo(datasetName);
7421// } catch(const std::exception& e) {
7422// new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),kMBIconExclamation); // deletes
7423// self on dismiss? return xRooNode();
7424// }
7425// }
7426//
7427// xRooNode xRooNode::fitTo(const char* datasetName) const {
7428// return fitTo(*datasets().at(datasetName));
7429// }
7430
7431void xRooNode::SetRange(const char *range, double low, double high)
7432{
7433 if (!std::isnan(low) && !std::isnan(high) && get<RooRealVar>()) {
7434 if (range && strlen(range)) {
7435 get<RooRealVar>()->setRange(range, low, high);
7436 } else {
7437 get<RooRealVar>()->setRange(low, high);
7438 }
7439 return;
7440 }
7441 if (auto o = get<RooAbsArg>(); o)
7442 o->setStringAttribute("range", range);
7443 // todo: clear the range attribute on all servers
7444 // could make this controlled by a flag but probably easiest to enforce so you must set range
7445 // in children after if you wanted to override
7446}
7447const char *xRooNode::GetRange() const
7448{
7449 std::string &out = fRange;
7450 if (auto o = get<RooAbsArg>(); o && o->getStringAttribute("range"))
7451 out = o->getStringAttribute("range");
7452 auto _parent = fParent;
7453 while (out.empty() && _parent) {
7454 if (auto o = _parent->get<RooAbsArg>(); o && o->getStringAttribute("range"))
7455 out = o->getStringAttribute("range");
7456 _parent = _parent->fParent;
7457 }
7458 return out.c_str();
7459}
7460
7461xRooNLLVar xRooNode::nll(const char *_data, std::initializer_list<RooCmdArg> nllOpts) const
7462{
7463 return nll(xRooNode(_data), nllOpts);
7464}
7465
7467{
7468 return nll(_data, *xRooFit::createNLLOptions());
7469}
7470
7471xRooNLLVar xRooNode::nll(const xRooNode &_data, std::initializer_list<RooCmdArg> nllOpts) const
7472{
7473 auto defaultOpts = xRooFit::createNLLOptions(); // smart pointer will cleanup the list
7474 // add user-specified options to list ... if already existing in default list, override and warn
7476 for (auto opt : *defaultOpts) {
7477 l.Add(opt);
7478 }
7479 for (auto &i : nllOpts) {
7480 if (auto o = l.FindObject(i.GetName())) {
7481 Info("nll", "Overriding NLL Option: %s", o->GetName());
7482 l.Remove(o);
7483 }
7484 l.Add(const_cast<RooCmdArg *>(&i));
7485 }
7486
7487 return nll(_data, l);
7488}
7489
7491{
7492 if (auto mc = get<RooStats::ModelConfig>()) {
7493 return xRooNode(*mc->GetPdf(), fParent).generate(fr, expected, seed);
7494 }
7495
7496 if (!get<RooAbsPdf>()) {
7497 // before giving up, if this is a workspace we can proceed if we only have one model
7498 if (get<RooWorkspace>()) {
7499 std::shared_ptr<xRooNode> mainModel;
7500 for (auto &c : const_cast<xRooNode *>(this)->browse()) {
7501 if (c->get<RooAbsPdf>()) {
7502 if (!mainModel) {
7503 mainModel = c;
7504 } else {
7505 throw std::runtime_error(TString::Format("Workspace has multiple models, you must specify which to "
7506 "generate with (found at least %s and %s)",
7507 mainModel->GetName(), c->GetName()));
7508 }
7509 }
7510 }
7511 if (mainModel)
7512 return mainModel->generate(fr, expected, seed);
7513 }
7514 throw std::runtime_error(TString::Format("%s is not a pdf", GetName()));
7515 }
7516
7517 // when generating, will only include channels that are selected
7518 // any unselected but not hidden channel will have data from the only selected dataset added to it
7519 if (get<RooSimultaneous>()) {
7520 std::string selected;
7521 std::string fromds; // list of channels to take from selected ds
7522 bool hasDeselected = false;
7523 for (auto c : bins()) {
7524 TString cName(c->GetName());
7525 cName = cName(cName.Index('=') + 1, cName.Length());
7526 if (!c->get<RooAbsReal>()->isSelectedComp()) {
7527 hasDeselected = true;
7528 if (!c->get<RooAbsArg>()->getAttribute("hidden")) {
7529 if (!fromds.empty())
7530 fromds += ",";
7531 fromds += cName.Data();
7532 }
7533 } else {
7534 if (!selected.empty())
7535 selected += ",";
7536 selected += cName.Data();
7537 }
7538 }
7539 if (hasDeselected) {
7540 std::string dsetName = "";
7541 if (!fromds.empty()) {
7542 // use the first selected dataset as protodata
7543 auto _dsets = datasets();
7544 for (auto &d : _dsets) {
7545 if (d->get()->TestBit(1 << 20)) {
7546 dsetName = d->get()->GetName();
7547 break;
7548 }
7549 }
7550 if (dsetName.empty()) {
7551 throw std::runtime_error(
7552 "Need at least one dataset selected (SetChecked) to use for deselected regions");
7553 }
7554 }
7555 auto result = reduced(selected).generate(fr, expected, seed);
7556 if (!fromds.empty()) {
7557 auto ds = reduced(fromds).datasets()[dsetName];
7558 result.Add(*ds);
7559 result.SetName(TString(result.GetName()) + "_and_" + dsetName.c_str());
7560 }
7561 return result;
7562 }
7563 }
7564
7565 auto _fr = fr.get<RooFitResult>();
7566 xRooNode fr2;
7567 if (!_fr) {
7568 fr2 = fitResult();
7569 _fr = fr2.get<RooFitResult>();
7570 }
7571
7572 // must ensure fr has all the globs in its constPars list ... any missing must be added
7573 // otherwise generateFrom method wont determine globs properly
7574 // same for any missing pars
7575 auto _globs = globs();
7576 bool missingGlobs(false);
7577 for (auto glob : _globs) {
7578 if (!_fr->constPars().find(*glob->get<RooAbsArg>())) {
7579 missingGlobs = true;
7580 break;
7581 }
7582 }
7583
7584 std::unique_ptr<RooFitResult> newFr;
7585 if (missingGlobs) {
7586 newFr = std::make_unique<RooFitResult>(*_fr);
7587 for (auto glob : _globs) {
7588 if (!newFr->constPars().find(*glob->get<RooAbsArg>())) {
7589 const_cast<RooArgList &>(newFr->constPars()).addClone(*glob->get<RooAbsArg>());
7590 }
7591 }
7592 _fr = newFr.get();
7593 }
7594
7595 // check for missing fundamental pars (consts are not fundamentals)
7596 auto _pars = pars();
7597 bool missingPars(false);
7598 for (auto par : _pars) {
7599 if (!par->get<RooAbsArg>()->isFundamental())
7600 continue;
7601 if (!_fr->constPars().find(*par->get<RooAbsArg>()) && !_fr->floatParsFinal().find(*par->get<RooAbsArg>())) {
7602 missingPars = true;
7603 break;
7604 }
7605 }
7606
7607 if (missingPars) {
7608 newFr = std::make_unique<RooFitResult>(*_fr);
7609 for (auto par : _pars) {
7610 if (!par->get<RooAbsArg>()->isFundamental())
7611 continue;
7612 if (!newFr->constPars().find(*par->get<RooAbsArg>()) &&
7613 !newFr->floatParsFinal().find(*par->get<RooAbsArg>())) {
7614 const_cast<RooArgList &>(newFr->constPars()).addClone(*par->get<RooAbsArg>());
7615 }
7616 }
7617 _fr = newFr.get();
7618 }
7619
7620 return xRooNode(xRooFit::generateFrom(*get<RooAbsPdf>(), *_fr, expected, seed).first, *this);
7621
7622 // should add coords to the dataset too?
7623 // e.g. in the case of generating a dataset for a single channel, include the channelCat
7624 // this will allow datasets to then be combined.
7625 // could just say users must use 'reduced' on the simPdf, even if reducing to a single channel
7626}
7627
7628xRooNLLVar xRooNode::nll(const xRooNode &_data, const RooLinkedList &opts) const
7629{
7630 if (auto mc = get<RooStats::ModelConfig>()) {
7631 if (mc->GetExternalConstraints()) {
7633 for (auto o : opts) {
7634 optsWithConstraints.Add(o->Clone(nullptr));
7635 }
7636 optsWithConstraints.Add(RooFit::ExternalConstraints(*mc->GetExternalConstraints()).Clone(nullptr));
7637 return xRooNode(*mc->GetPdf(), fParent).nll(_data, optsWithConstraints);
7638 } else {
7639 return xRooNode(*mc->GetPdf(), fParent).nll(_data, opts);
7640 }
7641 }
7642
7643 if (!get<RooAbsPdf>()) {
7644 // before giving up, if this is a workspace we can proceed if we only have one model or pdf
7645 if (get<RooWorkspace>()) {
7646 std::shared_ptr<xRooNode> mainPdf, mainModel, otherPdf;
7647 for (auto &c : const_cast<xRooNode *>(this)->browse()) {
7648 if (c->get<RooAbsPdf>()) {
7649 if (!mainPdf) {
7650 mainPdf = c;
7651 } else {
7652 otherPdf = c;
7653 }
7654 } else if (c->get<RooStats::ModelConfig>()) {
7655 if (!mainModel) {
7656 mainModel = c;
7657 } else {
7658 throw std::runtime_error(TString::Format("Workspace has multiple models, you must specify which to "
7659 "build nll with (found at least %s and %s)",
7660 mainModel->GetName(), c->GetName()));
7661 }
7662 }
7663 }
7664 if (mainModel)
7665 return mainModel->nll(_data, opts);
7666 if (mainPdf) {
7667 if (otherPdf) {
7668 throw std::runtime_error(TString::Format("Workspace has multiple pdfs, you must specify which to "
7669 "build nll with (found at least %s and %s)",
7670 mainPdf->GetName(), otherPdf->GetName()));
7671 }
7672 return mainPdf->nll(_data, opts);
7673 }
7674 }
7675 throw std::runtime_error(TString::Format("%s is not a pdf", GetName()));
7676 }
7677
7678 // if simultaneous and any channels deselected then reduce and return
7679 if (get<RooSimultaneous>()) {
7680 std::string selected;
7681 bool hasDeselected = false;
7682 for (auto c : bins()) {
7683 if (!c->get<RooAbsReal>()->isSelectedComp()) {
7684 hasDeselected = true;
7685 } else {
7686 TString cName(c->GetName());
7687 cName = cName(cName.Index('=') + 1, cName.Length());
7688 if (!selected.empty())
7689 selected += ",";
7690 selected += cName.Data();
7691 }
7692 }
7693 if (hasDeselected)
7694 return reduced(selected).nll(_data, opts);
7695 }
7696
7697 if (!_data.get<RooAbsData>()) {
7698 // use node name to find dataset and recall
7699 auto _d = strlen(_data.GetName()) ? datasets().find(_data.GetName()) : nullptr;
7700 if (strlen(_data.GetName()) == 0) {
7701 // create the EXPECTED (asimov) dataset with the observables
7703 _d = std::make_shared<xRooNode>(asi.first, *this);
7704 if (asi.second) {
7705 _d->emplace_back(
7706 std::make_shared<xRooNode>(".globs", std::const_pointer_cast<RooAbsCollection>(asi.second), *_d));
7707 }
7708 } else if (!_d) {
7709 throw std::runtime_error(TString::Format("Cannot find dataset %s", _data.GetName()));
7710 }
7711 return nll(*_d, opts);
7712 } else if (!_data.fParent || _data.fParent->fComp != fComp) {
7713 // dataset is not parented by this node ... meaning it may need to be reduced,
7714 // do this via the datasets() method by attaching and detaching the dataset
7715 xRooNode me(*this); // since we are in a const method, need to create a copy node.
7716 me.push_back(std::make_shared<xRooNode>(_data));
7717 return nll(*me.datasets().at(_data.GetName()), opts);
7718 }
7719
7720 auto _globs = _data.globs(); // keep alive because may own the globs
7721
7722 auto _opts = std::shared_ptr<RooLinkedList>(new RooLinkedList, [](RooLinkedList *l) {
7723 if (l)
7724 l->Delete();
7725 delete l;
7726 });
7727 RooArgSet _globsSet(_globs.argList());
7729 if (GetRange() && strlen(GetRange()))
7730 _opts->Add(RooFit::Range(GetRange()).Clone());
7731
7732 // copy over opts ... need to clone each so can safely delete when _opts destroyed
7733 for (int i = 0; i < opts.GetSize(); i++) {
7734 if (strlen(opts.At(i)->GetName()) == 0)
7735 continue; // skipping "none" cmds
7736 if (strcmp(opts.At(i)->GetName(), "GlobalObservables") == 0) {
7737 // maybe warn here?
7738 } else {
7739 _opts->Add(opts.At(i)->Clone(nullptr)); // nullptr needed because accessing Clone via TObject base class puts
7740 // "" instead, so doesnt copy names
7741 }
7742 }
7743
7744 // use shared_ptr method so NLLVar will take ownership of datasets etc if created above
7745 // snapshots the globs out of the nllOpts (see specific constructor of xRooNLLVar)
7746 auto out = xRooFit::createNLL(std::dynamic_pointer_cast<RooAbsPdf>(fComp),
7747 std::dynamic_pointer_cast<RooAbsData>(_data.fComp), *_opts);
7748 return out;
7749}
7750
7751// xRooNode xRooNode::fitTo(const xRooNode& _data) const {
7752//
7753//
7754// auto _pdf = get<RooAbsPdf>();
7755// if (!_pdf) throw std::runtime_error("Not a pdf");
7756//
7757// auto _globs = _data.globs(); // keep alive because may own the globs
7758// RooArgSet globsSet(_globs.argList());
7759//
7760// std::shared_ptr<RooSimultaneous> newPdf;
7761// if(auto s = get<RooSimultaneous>(); s) {
7762// auto rangeName = GetRange();
7763// if (rangeName) {
7764// // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
7765// std::vector<TString> chanPatterns;
7766// TStringToken pattern(rangeName, ",");
7767// while (pattern.NextToken()) {
7768// chanPatterns.emplace_back(pattern);
7769// }
7770// auto& _cat = const_cast<RooAbsCategoryLValue&>(s->indexCat());
7771// newPdf = std::make_shared<RooSimultaneous>(TString::Format("%s_reduced",GetName()),"Reduced model",_cat);
7772// for(auto& c : variations()) {
7773// TString cName(c->GetName());
7774// cName = cName(cName.Index('=')+1,cName.Length());
7775// _cat.setLabel(cName);
7776// bool matchAny=false;
7777// for(auto& p : chanPatterns) {
7778// if (cName.Contains(TRegexp(p,true))) { matchAny=true; break; }
7779// if (_cat.hasRange(p) && _cat.inRange(p)) { matchAny=true; break; }
7780// }
7781// if(matchAny) {
7782// newPdf->addPdf( *c->get<RooAbsPdf>(), cName );
7783// }
7784// }
7785// RooFitResultTree t(newPdf->GetName(),"",*newPdf);
7786// auto _fr = std::const_pointer_cast<RooFitResult>(t.fitTo(_data.get<RooAbsData>(), &globsSet));
7787// xRooNode parent(_data.GetName(),nullptr,*this);
7788// xRooNode out(_fr->GetName(),/*acquire(_fr)*/ _fr,parent);
7789// // do full propagation by 'checking' the fr ...
7790// out.Checked(&out,true);
7791// return out;
7792// }
7793// }
7794//
7795//
7796//
7797// std::string treeName = TString::Format("fits_%s",GetName()).Data();
7798//
7799// auto _frt = getObject<TTree>(treeName); // get existing frt
7800//
7801// std::shared_ptr<RooFitResultTree> t;
7802// if (_frt) {
7803// t = std::make_shared<RooFitResultTree>(_frt.get());
7804// } else {
7805// t = std::make_shared<RooFitResultTree>(treeName.c_str(),"",*_pdf);
7806// }
7807// //t->SetProgress(true);
7808// auto _fr = std::const_pointer_cast<RooFitResult>(t->fitTo(_data.get<RooAbsData>(), &globsSet));
7809//
7810//
7811//
7812// /*
7813// obs().argList() = s; // sets global observables to their values
7814// auto _fr =
7815// std::shared_ptr<RooFitResult>(_pdf->fitTo(*_data->get<RooAbsData>(),RooFit::GlobalObservables(s),RooFit::Offset(true),RooFit::Save()));
7816// _fr->SetName(TUUID().AsString());
7817// // restore parameters before returning
7818// *std::unique_ptr<RooArgSet>(_pdf->getDependents(_fr->floatParsFinal())) = _fr->floatParsInit();
7819// */
7820//
7821// //_fr->SetTitle(TString::Format("%s;%s",GetName(),datasetName));
7822// if (!_frt) {
7823// t =
7824// std::make_shared<RooFitResultTree>(std::dynamic_pointer_cast<TTree>(const_cast<xRooNode*>(this)->acquire(t->fTree)).get());
7825// }
7826// xRooNode parent(_data.GetName(),nullptr,xRooNode(t,*this));
7827// xRooNode out(_fr->GetName(),/*acquire(_fr)*/ _fr,parent);
7828// // do full propagation by 'checking' the fr ...
7829// out.Checked(&out,true);
7830// return out;
7831// }
7832
7833std::shared_ptr<xRooNode> xRooNode::parentPdf() const
7834{
7835 // find first parent that is a pdf
7836 auto out = fParent;
7837 while (out && !out->get<RooAbsPdf>()) {
7838 out = out->fParent;
7839 }
7840 return out;
7841}
7842
7843xRooNode xRooNode::reduced(const std::string &_range, bool invert) const
7844{
7845 auto rangeName = (_range.empty()) ? GetRange() : _range;
7846 if (!rangeName.empty()) {
7847 std::vector<TString> patterns;
7848 TStringToken pattern(rangeName, ",");
7849 while (pattern.NextToken()) {
7850 patterns.emplace_back(pattern);
7851 }
7852 if (auto s = get<RooSimultaneous>(); s) {
7853 // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
7854 auto &_cat = const_cast<RooAbsCategoryLValue &>(s->indexCat());
7855 auto newPdf =
7856 std::make_shared<RooSimultaneous>(TString::Format("%s_reduced", GetName()), "Reduced model", _cat);
7857 for (auto &c : bins()) {
7858 TString cName(c->GetName());
7859 cName = cName(cName.Index('=') + 1, cName.Length());
7860 _cat.setLabel(cName);
7861 bool matchAny = false;
7862 for (auto &p : patterns) {
7863 if (cName.Contains(TRegexp(p, true))) {
7864 matchAny = true;
7865 break;
7866 }
7867 if (_cat.hasRange(p) && _cat.inRange(p)) {
7868 matchAny = true;
7869 break;
7870 }
7871 }
7872 if ((matchAny && !invert) || (!matchAny && invert)) {
7873 newPdf->addPdf(*c->get<RooAbsPdf>(), cName);
7874 }
7875 }
7876 return xRooNode(newPdf, fParent);
7877 } else if (get() && !get<RooAbsCollection>() && !components().empty()) {
7878 // create a new obj and remove non-matching components
7879 xRooNode out(std::shared_ptr<TObject>(get()->Clone(TString::Format("%s_reduced", get()->GetName()))), fParent);
7880 // go through components and remove any that don't match pattern
7881 std::vector<TObject *> funcs; // to be removed
7882 for (auto &c : out.components()) {
7883 bool matchAny = false;
7884 for (auto &p : patterns) {
7885 if (TString(c->GetName()).Contains(TRegexp(p, true))) {
7886 matchAny = true;
7887 break;
7888 }
7889 }
7890 if (!((matchAny && !invert) || (!matchAny && invert)))
7891 funcs.push_back(c->get());
7892 }
7893 for (auto &c : funcs)
7894 out.Remove(*c);
7895 if (!funcs.empty()) {
7896 if (auto _pdf = out.get<RooRealSumPdf>(); _pdf) {
7897 _pdf->setFloor(false); // remove floor if removed some functions, which allows evaluation of negative
7898 // valued components
7899 }
7900 }
7901 out.browse();
7902 return out;
7903 } else if (auto fr = get<RooFitResult>()) {
7904 // reduce the fit result by moving unselected float pars into the constPars list and dropping their covariances
7905 xRooNode out(std::shared_ptr<TObject>(fr->Clone(TString::Format("%s_reduced", fr->GetName()))), fParent);
7906 fr = out.get<RooFitResult>();
7907 RooArgList _pars = fr->floatParsFinal();
7909 for (auto c : _pars) {
7910 bool matchAny = false;
7911 for (auto &p : patterns) {
7912 if (TString(c->GetName()).Contains(TRegexp(p, true))) {
7913 matchAny = true;
7914 break;
7915 }
7916 }
7917 if (!((matchAny && !invert) || (!matchAny && invert))) {
7918 _remPars.add(*c);
7919 }
7920 }
7921 _pars.remove(_remPars, true);
7922
7923 auto _tmp = fr->reducedCovarianceMatrix(_pars);
7924 int covQualBackup = fr->covQual();
7925 fr->setCovarianceMatrix(_tmp);
7926 fr->setCovQual(covQualBackup);
7927 const_cast<RooArgList &>(fr->floatParsFinal())
7928 .remove(_remPars, true); // is this a memory leak ... should delete the remPars?
7929 return out;
7930
7931 } else if (!get() || get<RooAbsCollection>()) {
7932 // filter the children .... handle special case of filtering ".vars" with "x" option too
7933 xRooNode out(std::make_shared<RooArgList>(), fParent);
7934 out.SetName(TString(GetName()) + "_reduced");
7935 size_t nobs = 0;
7936 bool notAllArgs = false;
7937 bool isVars = (strcmp(GetName(), ".vars") == 0);
7938 for (auto c : *this) {
7939 nobs += (c->fFolder == "!robs" || c->fFolder == "!globs");
7940 bool matchAny = false;
7941 for (auto &p : patterns) {
7942 if (TString(c->GetName()).Contains(TRegexp(p, true)) ||
7943 (isVars && p == "x" && (c->fFolder == "!robs" || c->fFolder == "!globs") && nobs == 1)) {
7944 matchAny = true;
7945 break;
7946 }
7947 }
7948 if ((matchAny && !invert) || (!matchAny && invert)) {
7949 out.push_back(c);
7950 if (auto a = c->get<RooAbsArg>()) {
7951 out.get<RooArgList>()->add(*a);
7952 } else {
7953 notAllArgs = true;
7954 }
7955 }
7956 }
7957 if (notAllArgs) {
7958 out.fComp.reset();
7959 }
7960 return out;
7961 }
7962 }
7963
7964 return get<RooArgList>() ? xRooNode(std::make_shared<RooArgList>(), fParent) : *this;
7965}
7966
7967// xRooNode xRooNode::generate(bool expected) const {
7968//
7969// auto fr = fitResult();
7970// auto _fr = fr.get<RooFitResult>();
7971//
7972// auto _pdf = (get<RooAbsPdf>()) ? std::shared_ptr<const xRooNode>(this, [](const xRooNode*){}) : parentPdf();
7973// if (!_pdf) {
7974// throw std::runtime_error("Could not find pdf");
7975// }
7976//
7977// std::shared_ptr<RooDataTree> t;
7978//
7979// std::shared_ptr<RooSimultaneous> newPdf;
7980// if(auto s = _pdf->get<RooSimultaneous>(); s) {
7981// auto rangeName = GetRange();
7982// if (rangeName) {
7983// // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
7984// std::vector<TString> chanPatterns;
7985// TStringToken pattern(rangeName, ",");
7986// while (pattern.NextToken()) {
7987// chanPatterns.emplace_back(pattern);
7988// }
7989// auto& _cat = const_cast<RooAbsCategoryLValue&>(s->indexCat());
7990// newPdf = std::make_shared<RooSimultaneous>(TString::Format("%s_reduced",GetName()),"Reduced model",_cat);
7991// for(auto& c : _pdf->variations()) {
7992// TString cName(c->GetName());
7993// cName = cName(cName.Index('=')+1,cName.Length());
7994// _cat.setLabel(cName);
7995// bool matchAny=false;
7996// for(auto& p : chanPatterns) {
7997// if (cName.Contains(TRegexp(p,true))) { matchAny=true; break; }
7998// if (_cat.hasRange(p) && _cat.inRange(p)) { matchAny=true; break; }
7999// }
8000// if(matchAny) {
8001// newPdf->addPdf( *c->get<RooAbsPdf>(), cName );
8002// }
8003// }
8004// t = std::make_shared<RooDataTree>(newPdf->GetName(),"",*newPdf);
8005// RooArgSet s1(_pdf->obs().argList());
8006// RooArgSet s2(_pdf->globs().argList());s1.remove(s2);
8007// t->SetObservables(&s1,&s2);
8008// auto _data = t->generate(_fr,expected);
8009//
8010// xRooNode parent(_fr ? _fr->GetName() : "unknown",nullptr,xRooNode(t,*this));
8011// xRooNode out(_data.first->GetName(),/*acquire(_fr)*/ _data.first,parent);
8012// out.emplace_back(std::make_shared<xRooNode>(".globs",std::const_pointer_cast<RooArgSet>(_data.second),out));
8013// return out;
8014// }
8015// }
8016//
8017//
8018// std::string treeName = TString::Format("gen_%s",_pdf->GetName()).Data();
8019//
8020// auto _frt = getObject<TTree>(treeName); // get existing frt
8021//
8022//
8023// if (_frt) {
8024// t = std::make_shared<RooDataTree>(_frt.get());
8025// } else {
8026// t = std::make_shared<RooDataTree>(treeName.c_str(),"",*_pdf->get<RooAbsPdf>());
8027// RooArgSet s1(_pdf->obs().argList());
8028// RooArgSet s2(_pdf->globs().argList());s1.remove(s2);
8029// t->SetObservables(&s1,&s2);
8030// }
8031// auto _data = t->generate(_fr,expected);
8032// if (!_frt) {
8033// t =
8034// std::make_shared<RooDataTree>(std::dynamic_pointer_cast<TTree>(const_cast<xRooNode*>(this)->acquire(t->fTree)).get());
8035// }
8036// xRooNode parent(_fr ? _fr->GetName() : "unknown",nullptr,xRooNode(t,*this));
8037// xRooNode out(_data.first->GetName(),/*acquire(_fr)*/ _data.first,parent);
8038// out.emplace_back(std::make_shared<xRooNode>(".globs",std::const_pointer_cast<RooArgSet>(_data.second),out));
8039// return out;
8040// }
8041
8043public:
8045 double expectedEvents(const RooArgSet *nset) const override
8046 {
8047 return static_cast<RooAbsPdf *>(intpdf.absArg())->expectedEvents(nset);
8048 }
8049 ExtendMode extendMode() const override { return static_cast<RooAbsPdf *>(intpdf.absArg())->extendMode(); }
8050 TObject *clone(const char *newname) const override { return new xRooProjectedPdf(*this, newname); }
8051
8052protected:
8053 double evaluate() const override
8054 {
8055 int code;
8056 return getProjection(&intobs, _normSet, (_normRange.Length() > 0 ? _normRange.Data() : nullptr), code)->getVal();
8057 }
8058};
8059
8060double new_getPropagatedError(const RooAbsReal &f, const RooFitResult &fr, const RooArgSet &nset = {},
8061 RooArgList **pars = nullptr, bool asymHi = false, bool asymLo = false)
8062{
8063 // Calling getParameters() might be costly, but necessary to get the right
8064 // parameters in the RooAbsReal. The RooFitResult only stores snapshots.
8065
8066 // handle simple case that function is a RooRealVar
8067 if (auto rrv = dynamic_cast<const RooRealVar *>(&f); rrv) {
8068 if (auto frrrv = dynamic_cast<RooRealVar *>(fr.floatParsFinal().find(*rrv)); frrrv) {
8069 rrv = frrrv; // use value from fit result
8070 }
8071 if (asymHi) {
8072 return rrv->getErrorHi();
8073 } else if (asymLo) {
8074 return rrv->getErrorLo();
8075 } else {
8076 return rrv->getError();
8077 }
8078 }
8079
8080 RooArgList *_pars = (pars) ? *pars : nullptr;
8081
8082 if (!_pars) {
8083
8085 f.getParameters(&nset, allParamsInAbsReal);
8086
8087 _pars = new RooArgList;
8088 for (auto *rrvFitRes : static_range_cast<RooRealVar *>(fr.floatParsFinal())) {
8089
8090 auto rrvInAbsReal = static_cast<RooRealVar const *>(allParamsInAbsReal.find(*rrvFitRes));
8091
8092 // Strip out parameters with zero error
8093 if (rrvFitRes->getError() <= std::abs(rrvFitRes->getVal()) * std::numeric_limits<double>::epsilon())
8094 continue;
8095
8096 // Ignore parameters in the fit result that this RooAbsReal doesn't depend on
8097 if (!rrvInAbsReal)
8098 continue;
8099
8100 // Checking for float equality is a bad. We check if the values are
8101 // negligibly far away from each other, relative to the uncertainty.
8102 if (std::abs(rrvInAbsReal->getVal() - rrvFitRes->getVal()) > 0.01 * rrvFitRes->getError()) {
8103 std::stringstream errMsg;
8104 errMsg << "RooAbsReal::getPropagatedError(): the parameters of the RooAbsReal don't have"
8105 << " the same values as in the fit result! The logic of getPropagatedError is broken in this case."
8106 << " \n " << rrvInAbsReal->GetName() << " : " << rrvInAbsReal->getVal() << " vs "
8107 << rrvFitRes->getVal();
8108
8109 throw std::runtime_error(errMsg.str());
8110 }
8111
8112 _pars->add(*rrvInAbsReal);
8113 }
8114 }
8115
8116 // Make std::vector of variations
8117 TVectorD F(_pars->size());
8118
8119 // Create std::vector of plus,minus variations for each parameter
8120 TMatrixDSym V(_pars->size() == fr.floatParsFinal().size() ? fr.covarianceMatrix()
8121 : fr.reducedCovarianceMatrix(*_pars));
8122
8123 // TODO: if _pars includes pars not in fr, need to extend matrix with uncorrelated errors of those pars
8124
8125 double nomVal = f.getVal(nset);
8126
8127 for (std::size_t ivar = 0; ivar < _pars->size(); ivar++) {
8128
8129 auto &rrv = static_cast<RooRealVar &>((*_pars)[ivar]);
8130 auto *frrrv = static_cast<RooRealVar *>(fr.floatParsFinal().find(rrv));
8131
8132 double cenVal = rrv.getVal();
8133 double plusVar, minusVar, errVal;
8134
8135 if (asymHi || asymLo) {
8136 errVal = frrrv->getErrorHi();
8137 rrv.setVal(cenVal + errVal);
8138 plusVar = f.getVal(nset);
8139 errVal = frrrv->getErrorLo();
8140 rrv.setVal(cenVal + errVal);
8141 minusVar = f.getVal(nset);
8142 if (asymHi) {
8143 // pick the one that moved result 'up' most
8144 plusVar = std::max(plusVar, minusVar);
8145 minusVar = 2 * nomVal - plusVar; // symmetrizes
8146 } else {
8147 // pick the one that moved result 'down' most
8148 minusVar = std::min(plusVar, minusVar);
8149 plusVar = 2 * nomVal - minusVar; // symmetrizes
8150 }
8151 } else {
8152 errVal = sqrt(V(ivar, ivar));
8153 // Make Plus variation
8154 rrv.setVal(cenVal + errVal);
8155 plusVar = f.getVal(nset);
8156 // Make Minus variation
8157 rrv.setVal(cenVal - errVal);
8158 minusVar = f.getVal(nset);
8159 }
8160 F[ivar] = (plusVar - minusVar) * 0.5;
8161 rrv.setVal(cenVal);
8162 }
8163
8164 // Re-evaluate this RooAbsReal with the central parameters just to be
8165 // extra-safe that a call to `getPropagatedError()` doesn't change any state.
8166 // It should not be necessary because thanks to the dirty flag propagation
8167 // the RooAbsReal is re-evaluated anyway the next time getVal() is called.
8168 // Still there are imaginable corner cases where it would not be triggered,
8169 // for example if the user changes the RooFit operation more after the error
8170 // propagation.
8171 f.getVal(nset);
8172
8173 TMatrixDSym C(_pars->size());
8174 std::vector<double> errVec(_pars->size());
8175 for (std::size_t i = 0; i < _pars->size(); i++) {
8176 errVec[i] = std::sqrt(V(i, i));
8177 for (std::size_t j = i; j < _pars->size(); j++) {
8178 C(i, j) = V(i, j) / std::sqrt(V(i, i) * V(j, j));
8179 C(j, i) = C(i, j);
8180 }
8181 }
8182
8183 // Calculate error in linear approximation from variations and correlation coefficient
8184 double sum = F * (C * F);
8185
8186 if (!pars) {
8187 delete _pars;
8188 } else {
8189 *pars = _pars;
8190 }
8191
8192 return sqrt(sum);
8193}
8194
8195class PdfWrapper : public RooAbsPdf {
8196public:
8197 // need expPdf option while RooProjectedPdf doesn't support keeping things extended
8198 PdfWrapper(RooAbsReal &f, RooAbsReal *coef, bool expEvMode = false, RooAbsPdf *expPdf = nullptr)
8199 : RooAbsPdf(Form("exp_%s", f.GetName())),
8200 fFunc("func", "func", this, f),
8201 fCoef("coef", "coef", this),
8202 fExpPdf("expPdf", "expPdf", this)
8203 {
8204 // don't treat pdf as extended if it has a coefficient and is RooAddPdf: RooAddPdf doesn't extend them unless no
8205 // coefs for any (and all are extendable)
8206 if (coef) {
8207 fCoef.setArg(*coef);
8208 }
8209 if (expPdf && expPdf->canBeExtended() && !(coef && dynamic_cast<RooAddPdf *>(expPdf))) {
8210 fExpPdf.setArg(*expPdf);
8211 } else if (auto _p = dynamic_cast<RooAbsPdf *>(&f);
8212 _p && _p->canBeExtended() && !(coef && dynamic_cast<RooAddPdf *>(_p))) {
8213 fExpPdf.setArg(f); // using self for expectation
8214 }
8215 fExpectedEventsMode = expEvMode;
8216 }
8217 ~PdfWrapper() override{};
8218 PdfWrapper(const PdfWrapper &other, const char *name = nullptr)
8219 : RooAbsPdf(other, name),
8220 fFunc("func", this, other.fFunc),
8221 fCoef("coef", this, other.fCoef),
8222 fExpPdf("expPdf", this, other.fExpPdf),
8223 fExpectedEventsMode(other.fExpectedEventsMode)
8224 {
8225 }
8226 TObject *clone(const char *newname) const override { return new PdfWrapper(*this, newname); }
8227 bool isBinnedDistribution(const RooArgSet &obs) const override { return fFunc->isBinnedDistribution(obs); }
8228 std::list<double> *binBoundaries(RooAbsRealLValue &obs, double xlo, double xhi) const override
8229 {
8230 return fFunc->binBoundaries(obs, xlo, xhi);
8231 }
8232
8233 double evaluate() const override
8234 {
8235 return (fExpectedEventsMode ? 1. : fFunc) *
8236 ((fExpPdf.absArg()) ? static_cast<RooAbsPdf *>(fExpPdf.absArg())->expectedEvents(_normSet) : 1.) *
8237 (fCoef.absArg() ? fCoef : 1.);
8238 }
8239
8240 bool selfNormalized() const override
8241 {
8242 return true;
8243 } // so that doesn't try to do an integral because we are passing integration onto fFunc in evaluate
8244
8245 // faster than full evaluation because doesnt make the integral dependent on the full expression
8247 {
8248#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 28, 00)
8249 double oo = getPropagatedError(fr, nset_in); // method was improved in 6.28 so use this instead
8250 if (std::isnan(oo)) {
8251 // may be consequence of zero uncerts
8252 // Calling getParameters() might be costly, but necessary to get the right
8253 // parameters in the RooAbsReal. The RooFitResult only stores snapshots.
8255 getParameters(&nset_in, allParamsInAbsReal);
8256
8257 RooArgList paramList;
8259
8260 auto rrvInAbsReal = static_cast<RooRealVar const *>(allParamsInAbsReal.find(*rrvFitRes));
8261
8262 // If this RooAbsReal is a RooRealVar in the fit result, we don't need to
8263 // propagate anything and can just return the error in the fit result
8264 if (rrvFitRes->namePtr() == namePtr())
8265 return rrvFitRes->getError();
8266
8267 // Strip out parameters with zero error
8268 if (!rrvFitRes->hasError() ||
8269 rrvFitRes->getError() <= std::abs(rrvFitRes->getVal()) * std::numeric_limits<double>::epsilon())
8270 continue;
8271
8272 // Ignore parameters in the fit result that this RooAbsReal doesn't depend on
8273 if (!rrvInAbsReal)
8274 continue;
8275
8276 // Checking for float equality is a bad. We check if the values are
8277 // negligibly far away from each other, relative to the uncertainty.
8278 if (std::abs(rrvInAbsReal->getVal() - rrvFitRes->getVal()) > 0.01 * rrvFitRes->getError()) {
8279 std::stringstream errMsg;
8280 errMsg
8281 << "RooAbsReal::getPropagatedError(): the parameters of the RooAbsReal don't have"
8282 << " the same values as in the fit result! The logic of getPropagatedError is broken in this case.";
8283
8284 throw std::runtime_error(errMsg.str());
8285 }
8286
8287 paramList.add(*rrvInAbsReal);
8288 }
8289 if (paramList.empty())
8290 return 0.;
8291
8292 std::vector<double> plusVar;
8293 std::vector<double> minusVar;
8294 plusVar.reserve(paramList.size());
8295 minusVar.reserve(paramList.size());
8296
8297 // Create std::vector of plus,minus variations for each parameter
8298 TMatrixDSym V(paramList.size() == fr.floatParsFinal().size() ? fr.covarianceMatrix()
8299 : fr.reducedCovarianceMatrix(paramList));
8300
8301 for (std::size_t ivar = 0; ivar < paramList.size(); ivar++) {
8302
8303 auto &rrv = static_cast<RooRealVar &>(paramList[ivar]);
8304
8305 double cenVal = rrv.getVal();
8306 double errVal = sqrt(V(ivar, ivar));
8307
8308 // this next thing happens if the par has errors but the covariance matrix is empty
8309 // this only happens if the fit was dodgy, so perhaps best to not even try to recover from this
8310 // screwup ... hence I've commented out this fixup here and will let the errors be nan
8311 // if(errVal==0) {
8312 // Warning("getPropagatedError","Missing variance for %s",rrv.GetName());
8313 // errVal = rrv.getError();
8314 // V(ivar,ivar) = errVal*errVal;
8315 // }
8316
8317 // Make Plus variation
8318 rrv.setVal(cenVal + errVal);
8319 plusVar.push_back(getVal(nset_in));
8320
8321 // Make Minus variation
8322 rrv.setVal(cenVal - errVal);
8323 minusVar.push_back(getVal(nset_in));
8324#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
8325 // can try to recover nans ... this stopped being possible in 6.27 onwards because NaNPacker made private
8326 if (std::isnan(plusVar.back()) && RooNaNPacker::isNaNWithPayload(plusVar.back())) {
8327 plusVar.back() = -RooNaNPacker::unpackNaN(plusVar.back());
8328 }
8329 if (std::isnan(minusVar.back()) && RooNaNPacker::isNaNWithPayload(minusVar.back())) {
8330 minusVar.back() = -RooNaNPacker::unpackNaN(minusVar.back());
8331 }
8332#endif
8333 // std::cout << plusVar.back() << " and " << minusVar.back() << std::endl;
8334
8335 rrv.setVal(cenVal);
8336 }
8337
8338 // Re-evaluate this RooAbsReal with the central parameters just to be
8339 // extra-safe that a call to `getPropagatedError()` doesn't change any state.
8340 // It should not be necessary because thanks to the dirty flag propagation
8341 // the RooAbsReal is re-evaluated anyway the next time getVal() is called.
8342 // Still there are imaginable corner cases where it would not be triggered,
8343 // for example if the user changes the RooFit operation more after the error
8344 // propagation.
8345 getVal(nset_in);
8346
8347 TMatrixDSym C(paramList.size());
8348 std::vector<double> errVec(paramList.size());
8349 for (std::size_t i = 0; i < paramList.size(); i++) {
8350 errVec[i] = std::sqrt(V(i, i));
8351 for (std::size_t j = i; j < paramList.size(); j++) {
8352 C(i, j) = V(i, j) / std::sqrt(V(i, i) * V(j, j));
8353 C(j, i) = C(i, j);
8354 }
8355 }
8356
8357 // Make std::vector of variations
8358 TVectorD F(plusVar.size());
8359 for (unsigned int j = 0; j < plusVar.size(); j++) {
8360 F[j] = (plusVar[j] - minusVar[j]) / 2;
8361 }
8362
8363 // Calculate error in linear approximation from variations and correlation coefficient
8364 double sum = F * (C * F);
8365
8366 return sqrt(sum);
8367 }
8368 return oo;
8369#else
8370
8371 // Strip out parameters with zero error
8374 if (frv->getError() > 1e-20) {
8375 fpf_stripped.add(*frv);
8376 }
8377 }
8378
8379 // Clone self for internal use
8380 RooAbsReal *cloneFunc = const_cast<PdfWrapper *>(this); // (RooAbsReal *)fFunc.absArg()->cloneTree();
8381 // RooAbsPdf *clonePdf = dynamic_cast<RooAbsPdf *>(cloneFunc);
8382 RooArgSet *errorParams = cloneFunc->getObservables(fpf_stripped);
8383
8384 RooArgSet *nset =
8385 nset_in.size() == 0 ? cloneFunc->getParameters(*errorParams) : cloneFunc->getObservables(nset_in);
8386
8387 // Make list of parameter instances of cloneFunc in order of error matrix
8388 RooArgList paramList;
8389 const RooArgList &fpf = fpf_stripped;
8390 std::vector<int> fpf_idx;
8391 for (Int_t i = 0; i < fpf.size(); i++) {
8392 RooAbsArg *par = errorParams->find(fpf[i].GetName());
8393 if (par) {
8394 paramList.add(*par);
8395 fpf_idx.push_back(i);
8396 }
8397 }
8398
8399 std::vector<double> plusVar, minusVar;
8400
8401 // Create vector of plus,minus variations for each parameter
8402 TMatrixDSym V(paramList.size() == fr.floatParsFinal().size() ? fr.covarianceMatrix()
8403 : fr.reducedCovarianceMatrix(paramList));
8404
8405 for (Int_t ivar = 0; ivar < paramList.size(); ivar++) {
8406
8408
8409 double cenVal = rrv.getVal();
8410 double errVal = sqrt(V(ivar, ivar));
8411
8412 // Make Plus variation
8413 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal + errVal);
8414 // plusVar.push_back((fExpectedEventsMode ? 1. : cloneFunc->getVal(nset)) *
8415 // (clonePdf ? clonePdf->expectedEvents(nset) : 1.));
8416 plusVar.push_back(cloneFunc->getVal(nset));
8417
8418 // Make Minus variation
8419 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal - errVal);
8420 // minusVar.push_back((fExpectedEventsMode ? 1. : cloneFunc->getVal(nset)) *
8421 // (clonePdf ? clonePdf->expectedEvents(nset) : 1.));
8422 minusVar.push_back(cloneFunc->getVal(nset));
8423
8424 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal);
8425 }
8426
8427 getVal(nset); // reset state
8428
8429 TMatrixDSym C(paramList.size());
8430 std::vector<double> errVec(paramList.size());
8431 for (int i = 0; i < paramList.size(); i++) {
8432 errVec[i] = sqrt(V(i, i));
8433 for (int j = i; j < paramList.size(); j++) {
8434 C(i, j) = V(i, j) / sqrt(V(i, i) * V(j, j));
8435 C(j, i) = C(i, j);
8436 }
8437 }
8438
8439 // Make vector of variations
8440 TVectorD F(plusVar.size());
8441 for (unsigned int j = 0; j < plusVar.size(); j++) {
8442 F[j] = (plusVar[j] - minusVar[j]) / 2;
8443 }
8444
8445 // Calculate error in linear approximation from variations and correlation coefficient
8446 double sum = F * (C * F);
8447
8448 // delete cloneFunc;
8449 delete errorParams;
8450 delete nset;
8451
8452 return sqrt(sum);
8453#endif
8454 }
8455
8456private:
8460 bool fExpectedEventsMode = false;
8461};
8462
8463const xRooNode *runningNode = nullptr;
8465
8467{
8468 std::cout << "Got signal " << signum << std::endl;
8469 if (signum == SIGINT) {
8470 std::cout << "Keyboard interrupt while building histogram" << std::endl;
8471 // TODO: create a global mutex for this
8472 runningNode->fInterrupted = true;
8473 } else {
8475 }
8476}
8477
8479{
8480 auto _doSterilize = [](RooAbsArg *obj) {
8481 if (!obj)
8482 return;
8483 for (int i = 0; i < obj->numCaches(); i++) {
8484 if (auto cache = dynamic_cast<RooObjCacheManager *>(obj->getCache(i))) {
8485 cache->reset();
8486 }
8487 }
8488 if (RooAbsPdf *p = dynamic_cast<RooAbsPdf *>(obj); p) {
8489 p->setNormRange(p->normRange());
8490 }
8491#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
8492 if (RooAbsReal *p = dynamic_cast<RooAbsReal *>(obj); p) {
8493 // need to forget about any normSet that was passed to getVal(...)
8494 // doesn't seem necessary in 6.28
8495
8496 p->setProxyNormSet(nullptr);
8497 p->_lastNSet = nullptr;
8498 }
8499#endif
8500 obj->setValueDirty();
8501 };
8502 if (auto w = get<RooWorkspace>(); w) {
8503 // sterilizing all nodes
8504 for (auto &c : w->components()) {
8505 _doSterilize(c);
8506 }
8507 return;
8508 }
8509 // recursive through all clients and sterlize their normalization caches
8510 std::function<void(RooAbsArg *)> func;
8511 func = [&](RooAbsArg *a) {
8512 if (!a) {
8513 return;
8514 }
8515 _doSterilize(a); // sterilize first so that cache elements don't appear in the client list
8516 // safety net in case sterilizing one client deletes another one of our clients
8517 // monitor for change in clients list size
8518 // found this was only case in 6.26 (valgrind shows invalid read), in 6.28 these went away
8519 // might be in 6.28 the client list iterator became able to handle in-loop edits but didn't see
8520 // in test case that client count changed so just resterilizing if that's the case.
8521 size_t nClients;
8522 do {
8523 nClients = a->clients().size();
8524 for (auto obj : a->clients()) {
8525 func(dynamic_cast<RooAbsArg *>(obj));
8526 if (a->clients().size() != nClients) {
8527 break; // means sterilizing a client changed our clients, so don't trust the client iterator at this
8528 // point
8529 }
8530 }
8531 } while (a->clients().size() != nClients);
8532 };
8533 func(get<RooAbsArg>());
8534}
8535
8536// observables not in the axisVars are automatically projected over
8537xRooNode xRooNode::histo(const xRooNode &vars, const xRooNode &fr, bool content, bool errors, bool stack, bool errorsHi,
8538 bool errorsLo, int nErrorToys) const
8539{
8540
8541 if (!vars.fComp && strlen(vars.GetName())) {
8543 }
8544
8545 xRooNode out(TString::Format("%s.histo", GetName()), nullptr, *this);
8546
8547 RooAbsLValue *v = nullptr;
8548 if (vars.empty()) {
8549 // does an integral
8550 out.fComp = std::shared_ptr<TH1>(
8551 BuildHistogram(nullptr, !content, errors, -1, -1, fr, errorsHi, errorsLo, nErrorToys, nullptr, !stack, false));
8552 } else if (vars.size() == 1) {
8553 v = vars.at(0)->get<RooAbsLValue>();
8554 out.fComp = std::shared_ptr<TH1>(
8555 BuildHistogram(v, !content, errors, 1, 0, fr, errorsHi, errorsLo, nErrorToys, nullptr, !stack, true));
8556 } else {
8557 throw std::runtime_error("multi-dim histo not yet supported");
8558 }
8559
8560 return out;
8561}
8562
8564{
8565 return xRooNode(fComp, xRooNode(range.GetName(), nullptr, *this));
8566}
8567
8569 bool errorsHi, bool errorsLo, int nErrorToys, TH1 *templateHist, bool nostack,
8570 bool setInterp) const
8571{
8572 auto rar = get<RooAbsReal>();
8573 if (!rar) {
8574 if (get<TH1>()) {
8575 return dynamic_cast<TH1 *>(get()->Clone());
8576 }
8577 return nullptr;
8578 }
8579
8580 TObject *vv = rar;
8581
8582 auto t = TH1::AddDirectoryStatus();
8583 TH1::AddDirectory(false);
8584 TH1 *h = nullptr;
8585 if (!v) {
8586 if (binStart != -1 || binEnd != -1) { // allow v to stay nullptr if doing integral (binStart=binEnd=-1)
8587 if (auto _ax = GetXaxis())
8588 v = dynamic_cast<RooAbsLValue *>(_ax->GetParent());
8589 } else {
8590 // don't need to integrate if doing a self-histogram
8591 v = dynamic_cast<RooRealVar *>(rar);
8592 }
8593 if (v) {
8594 vv = dynamic_cast<TObject *>(v);
8595 } else {
8596 // make a single-bin histogram of just this value
8597 h = new TH1D(rar->GetName(), rar->GetTitle(), 1, 0, 1);
8598 h->GetXaxis()->SetBinLabel(1, rar->GetName());
8599 h->GetXaxis()->SetTimeFormat(rar->GetName());
8600 }
8601 }
8602
8603 auto x = dynamic_cast<RooRealVar *>(v);
8604 bool setTitle = false;
8605 if (templateHist) {
8606 // using template hist for the binning
8607 h = static_cast<TH1 *>(templateHist->Clone(rar->GetName()));
8608 if (h->GetListOfFunctions())
8610 h->SetDirectory(0);
8611 h->SetTitle(rar->GetTitle());
8612 h->Reset();
8613 } else if (x) {
8614 if (x == rar) {
8615 // self histogram ...
8616 h = new TH1D(rar->GetName(), rar->GetTitle(), 1, 0, 1);
8617 h->Sumw2();
8618 h->GetXaxis()->SetBinLabel(1, rar->GetName());
8619 h->SetBinContent(1, rar->getVal());
8620 if (x->getError()) {
8621 h->SetBinError(1, x->getError());
8622 h->SetFillStyle(3005);
8623 h->SetFillColor(h->GetLineColor());
8624 }
8625 h->SetMaximum(x->hasMax() ? x->getMax()
8626 : (h->GetBinContent(1) + std::max(std::abs(h->GetBinContent(1) * 0.1), 50.)));
8627 h->SetMinimum(x->hasMin() ? x->getMin()
8628 : (h->GetBinContent(1) - std::max(std::abs(h->GetBinContent(1) * 0.1), 50.)));
8629 h->GetXaxis()->SetName(dynamic_cast<TObject *>(v)->GetName());
8630 h->SetOption("e2");
8631 h->SetMarkerSize(0);
8632 h->SetMarkerStyle(0);
8633
8634 return h;
8635 }
8636 auto _ax = GetXaxis();
8637 TString binningName = (_ax && _ax->GetParent() == x) ? _ax->GetName() : rar->getStringAttribute("binning");
8638 if (binningName == "")
8639 binningName = rar->GetName();
8640 if (x->hasBinning(binningName)) {
8641 if (x->getBinning(binningName).isUniform()) {
8642 h = new TH1D(rar->GetName(), rar->GetTitle(), x->numBins(binningName) <= 0 ? 100 : x->numBins(binningName),
8643 x->getMin(binningName), x->getMax(binningName));
8644 } else {
8645 h = new TH1D(rar->GetName(), rar->GetTitle(), x->numBins(binningName), x->getBinning(binningName).array());
8646 }
8647 h->GetXaxis()->SetTitle(x->getBinning(binningName).GetTitle());
8648 setTitle = true;
8649 } else if (auto _boundaries =
8650 _or_func(/*rar->plotSamplingHint(*x,x->getMin(),x->getMax())*/ (std::list<double> *)(nullptr),
8651 rar->binBoundaries(*x, -std::numeric_limits<double>::infinity(),
8652 std::numeric_limits<double>::infinity()));
8653 _boundaries) {
8654 std::vector<double> _bins;
8655 for (auto &b : *_boundaries) {
8656 if (_bins.empty() || std::abs(_bins.back() - b) > 1e-5 * _bins.back())
8657 _bins.push_back(b);
8658 } // found sometimes get virtual duplicates in the binning
8659 h = new TH1D(rar->GetName(), rar->GetTitle(), _bins.size() - 1, &_bins[0]);
8660 delete _boundaries;
8661 } else if (!x->hasMax() || !x->hasMin()) {
8662 // use current value of x to estimate range with
8663 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(), x->getVal() * 0.2, x->getVal() * 5);
8664 } else {
8665 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(), x->getBinning().array());
8666 }
8667 h->Sumw2();
8668 } else if (!h) {
8669 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(rar->GetName()), 0, v->numBins(rar->GetName()));
8670 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(v)) {
8671 int i = 1;
8672 std::map<int, std::string> cats; // fill into a map to preserve index ordering
8673 for (auto &c : *cat) {
8674 cats[c.second] = c.first;
8675 }
8676 for (auto &[_, label] : cats) {
8677 h->GetXaxis()->SetBinLabel(i++, label.c_str());
8678 }
8679 }
8680 h->Sumw2();
8681 }
8682 if (auto o = dynamic_cast<TObject *>(v); o && !setTitle) {
8683 h->GetXaxis()->SetTitle(o->GetTitle());
8684 }
8686 if (v) {
8687 if (h->GetXaxis()->IsAlphanumeric()) {
8688 // store the variable name in the TimeFormat property as well, b.c. alphanumeric requires axis name to be
8689 // "xaxis"
8690 h->GetXaxis()->SetTimeFormat(dynamic_cast<TObject *>(v)->GetName());
8691 } else {
8692 h->GetXaxis()->SetName(dynamic_cast<TObject *>(v)->GetName()); // WARNING: messes up display of bin labels
8693 }
8694 }
8695
8696 if (auto s = styles(nullptr, false); s) {
8697 auto _style = s.get<TStyle>();
8698 static_cast<TAttLine &>(*h) = *_style;
8699 static_cast<TAttFill &>(*h) = *_style;
8700 static_cast<TAttMarker &>(*h) = *_style;
8701 }
8702 if (strlen(h->GetXaxis()->GetTitle()) == 0)
8703 h->GetXaxis()->SetTitle(vv->GetTitle());
8704 auto p = dynamic_cast<RooAbsPdf *>(rar);
8705
8706 // possible speed improvement:
8707 // if(auto spdf = dynamic_cast<RooRealSumPdf*>(p); spdf && spdf->canBeExtended()) {
8708 // p = nullptr; // faster to evaluate sumpdf as a function not a pdf
8709 // }
8710
8711 if (empty && !errors) {
8712 return h;
8713 }
8714
8715 // if (!empty) {
8716
8717 auto _coefs = coefs();
8718
8719 RooFitResult *fr = nullptr;
8720 if (errors) {
8721 // must ensure the fit result we obtain includes pars from coefficients if present
8722 if (_fr.get<RooFitResult>()) {
8723 fr = static_cast<RooFitResult *>(_fr.get<RooFitResult>()->Clone());
8724 } else {
8725 auto frn =
8726 (!_coefs.get() ? *this : xRooNode(RooProduct("tmp", "tmp", RooArgList(*rar, *_coefs.get<RooAbsReal>()))))
8727 .fitResult();
8728 if (strlen(_fr.GetName()))
8729 frn = frn.reduced(_fr.GetName());
8730 fr = dynamic_cast<RooFitResult *>(frn->Clone());
8731 }
8732 if (!GETDMP(fr, _finalPars)) {
8734 }
8735
8736 /// Oct2022: No longer doing this because want to allow fitResult to be used to get partial error
8737 // // need to add any floating parameters not included somewhere already in the fit result ...
8738 // now done in the fitResult() method, where a fit result from the workspace can be automatically extended
8739
8740 TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr, _VM));
8741
8742 if (!prevCov || size_t(fr->covarianceMatrix().GetNcols()) < fr->floatParsFinal().size()) {
8743 TMatrixDSym cov(fr->floatParsFinal().size());
8744 if (prevCov) {
8745 for (int i = 0; i < prevCov->GetNcols(); i++) {
8746 for (int j = 0; j < prevCov->GetNrows(); j++) {
8747 cov(i, j) = (*prevCov)(i, j);
8748 }
8749 }
8750 }
8751 int i = 0;
8752 for (auto &p2 : fr->floatParsFinal()) {
8753 if (!prevCov || i >= prevCov->GetNcols()) {
8754 cov(i, i) = pow(dynamic_cast<RooRealVar *>(p2)->getError(), 2);
8755 }
8756 i++;
8757 }
8758 int covQualBackup = fr->covQual();
8761 }
8762
8763 if (v) {
8764 // need to remove v from result as we are plotting as function of v
8765 if (auto _p = fr->floatParsFinal().find(dynamic_cast<TObject *>(v)->GetName()); _p) {
8767 _pars.remove(*_p, true);
8769 int covQualBackup = fr->covQual();
8772 const_cast<RooArgList &>(fr->floatParsFinal())
8773 .remove(*_p, true); // NOTE: I think this might be a memory leak, should delete _p after removal
8774 }
8775 }
8776 // finally check at least one float has errors defined (might not be cause if in prefit state)
8777 bool hasErrors = false;
8778 for (auto pp : fr->floatParsFinal()) {
8779 if (dynamic_cast<RooRealVar *>(pp)->hasError()) {
8780 hasErrors = true;
8781 break;
8782 }
8783 }
8784 if (!hasErrors) {
8785 errors = false;
8786 delete fr;
8787 }
8788 }
8789
8791 if (v)
8792 normSet.add(*dynamic_cast<RooAbsArg *>(v));
8793
8794 if (binEnd == 0)
8795 binEnd = h->GetNbinsX();
8796
8797 bool needBinWidth = false;
8798 // may have MULTIPLE coefficients for the same pdf!
8799
8800 if (x && (p || _coefs.get() || rar->getAttribute("density"))) {
8801 // pdfs of samples embedded in a sumpdf (aka have a coef) will convert their density value to a content
8802 needBinWidth = true;
8803 }
8804
8805 if (auto spdf = dynamic_cast<RooRealSumPdf *>(p);
8806 spdf && spdf->canBeExtended() && !spdf->getFloor() && !_coefs.get()) {
8807 p = nullptr; // if pdf has no floor, will evaluate it as a function to allow it to be negative - evaluation should
8808 // also be faster (no integral)
8809 // exception is if RooRealSumPdf is embedded in a RooAddPdf (detected by presence of coefs) ... then it must be
8810 // evaluated as a pdf technically should check parent is a RooAddPdf, because if was inside a RooRealSumPdf then
8811 // would be evaluated as a function!
8812 }
8813
8814 // check if we need to do any projecting of other observables
8815 RooAbsReal *oldrar = nullptr;
8816 auto _obs = obs();
8817
8818 for (auto o : _obs) {
8819 if (auto rr = o->get<RooRealVar>(); rr && rr->hasRange("coordRange")) {
8820 rr->removeRange("coordRange"); // doesn't actually remove, just sets to -inf->+inf
8821 rr->setStringAttribute("coordRange", nullptr); // removes the attribute
8822 }
8823 }
8824 // probably should also remove any range on the x-axis variable too, if there is one
8825 if (auto rr = dynamic_cast<RooRealVar *>(v); rr && rr->hasRange("coordRange")) {
8826 rr->removeRange("coordRange"); // doesn't actually remove, just sets to -inf->+inf
8827 rr->setStringAttribute("coordRange", nullptr); // removes the attribute
8828 }
8829 coords(); // loads current coordinates and populates coordRange, if any
8830
8831 if (auto a = dynamic_cast<RooAbsArg *>(v))
8832 _obs.get<RooArgList>()->remove(*a);
8833 if (!_obs.get<RooArgList>()->empty()) {
8834 oldrar = rar;
8835 normSet.add(*_obs.get<RooArgList>());
8836 // check if any obs are restricted range
8837 bool hasRange = false;
8838 for (auto o : normSet) {
8839 if (auto rr = dynamic_cast<RooRealVar *>(o);
8840 rr && (rr->getStringAttribute("coordRange")) && strlen(rr->getStringAttribute("coordRange"))) {
8841 hasRange = true;
8842 break;
8843 }
8844 }
8845 if (p) {
8846 // need to handle special case of RooSimultaneous ... each pdf needs individually projecting over just its
8847 // dependent obs
8848 if (auto s = dynamic_cast<RooSimultaneous *>(p)) {
8849 auto newrar = new RooSimultaneous("projSim", "projSim", const_cast<RooAbsCategoryLValue &>(s->indexCat()));
8850 for (auto pdf : bins()) {
8851 // auto _pdf =
8852 // pdf->get<RooAbsPdf>()->createProjection(*pdf->get<RooAbsPdf>()->getObservables(*_obs.get<RooArgList>()));
8853 auto _pdf =
8854 new xRooProjectedPdf(TString::Format("%s_projection", pdf->GetName()), "", *pdf->get<RooAbsPdf>(),
8855 *pdf->get<RooAbsPdf>()->getObservables(*_obs.get<RooArgList>()));
8856 if (hasRange) {
8857 dynamic_cast<RooAbsPdf *>(_pdf)->setNormRange("coordRange");
8858 }
8859 newrar->addPdf(*_pdf, pdf->coords()[s->indexCat().GetName()]->get<RooCategory>()->getLabel());
8860 }
8861 rar = newrar;
8862 } else {
8863 rar = p->createProjection(
8864 *_obs.get<RooArgList>()); // TODO should use xRooProjectedPdf here too, because not fixed range and
8865 // extend behaviour of RooProjectedPdf in ROOT yet
8866 if (hasRange) {
8867 dynamic_cast<RooAbsPdf *>(rar)->setNormRange("coordRange");
8868 }
8869 }
8870 if (hasRange)
8871 p->setNormRange("coordRange"); // should get cleared when we sterilize
8872 } else {
8873 if (hasRange) {
8874 // commented out passing of normset so that getVal of non-pdf is always a 'raw' value (needed for raw eval
8875 // of RooRealSumPdf)
8876 rar = std::unique_ptr<RooAbsReal>{rar->createIntegral(
8877 *_obs.get<RooArgList>(),
8878 /*RooFit::NormSet(normSet),*/ RooFit::Range("coordRange"))}
8879 .release();
8880 } else {
8881 rar =
8882 std::unique_ptr<RooAbsReal>{rar->createIntegral(*_obs.get<RooArgList>() /*, RooFit::NormSet(normSet)*/)}
8883 .release();
8884 }
8885 }
8886 }
8887
8888 bool scaleExpected = (p && p->canBeExtended() && !_coefs.get());
8889 // Note about above: if pdf has coefficients then its embedded in a RooAddPdf that has coefs defined ...
8890 // in this case we should *not* scale by expected, since the coefs become the scaling instead
8891 // we should also not build a stack for this (may be a RooRealSumPdf inside a RooAddPdf, but the
8892 // samples of the RooRealSumPdf wont be correctly scaled to line up with overall RooRealSumPdf
8893 // which will be normalized to its coefficient
8894 if (!nostack && p && p->canBeExtended() && _coefs.get()) {
8895 nostack = true;
8896 // if wanted to still hve a stack, would need to scale the stack subcomponents by
8897 // coefs-value / p_integral(raw) ... since raw p-integral will be what stack integrates to
8898 }
8899
8900 std::unique_ptr<RooArgSet> snap(normSet.snapshot());
8902 std::vector<double> lapTimes;
8903 bool warned = false;
8904 if (binStart == -1 && binEnd == -1) {
8905 binEnd = 1;
8906 }
8907 auto cat = (!x) ? dynamic_cast<RooAbsCategoryLValue *>(v) : nullptr;
8908 RooArgList *errorPars = nullptr;
8909 std::unique_ptr<RooAbsCollection> errorParsSnap;
8910
8911 if (!v) {
8912 setInterp = false;
8913 }
8914
8915 if (setInterp) {
8916 RooAbsArg *vvv = dynamic_cast<RooAbsArg *>(v);
8917 // determining if histogram should have interpolation drawing options set on it
8918 // need to strip namespace to discount the "HistFactory" namespace classes from all being treated as binned
8919 TString clNameNoNamespace = rar->ClassName();
8921 setInterp = (clNameNoNamespace.Contains("Hist") || vvv->isCategory() || rar->isBinnedDistribution(*vvv) ||
8922 h->GetNbinsX() == 1 || rar->getAttribute("BinnedLikelihood") ||
8923 (dynamic_cast<RooAbsRealLValue *>(vvv) &&
8924 std::unique_ptr<std::list<double>>(rar->binBoundaries(*dynamic_cast<RooAbsRealLValue *>(vvv),
8925 -std::numeric_limits<double>::infinity(),
8926 std::numeric_limits<double>::infinity()))))
8927 ? false
8928 : true;
8929 if (auto d = dynamic_cast<RooHistFunc *>(rar); d && !d->isBinnedDistribution(*vvv) && h->GetNbinsX() != 1) {
8930 setInterp = true; // hist func is interpolated, so draw it as such
8931 }
8932 if (setInterp && !components().empty()) {
8933 // check if all components of dOpt are "Hist" type (CMS model support)
8934 // if so then don't interp;
8935 bool allHist = true;
8936 for (auto &s : components()) {
8937 TString _clName = s->get()->ClassName();
8938 _clName = _clName(_clName.Last(':') + 1, _clName.Length());
8939 if (!(s->get() && _clName.Contains("Hist"))) {
8940 allHist = false;
8941 break;
8942 }
8943 }
8944 if (allHist)
8945 setInterp = false;
8946 }
8947 if (setInterp) {
8948 h->SetOption("l"); // does linear interpolation between points
8949 }
8950 }
8951
8952 if (errors) {
8953 // may be computing potentially asymmetric errors
8954 // the main histogram will be the error band, and the nominal histogram will be added as a function
8955 // so that it is drawn over the top of the error band
8956 // note that this means GetBinContent on returned histogram will return midpoint of the up and down error
8957 auto l = static_cast<TH1 *>(h->Clone("nominal"));
8958 l->SetDirectory(0);
8959 l->SetFillStyle(0);
8960 h->GetListOfFunctions()->Add(l, (setInterp) ? "lsame" : "histsame");
8961 h->SetOption(setInterp ? "e3" : "e2"); // default draw option E2 or E3 so error band shown .. could have used
8962 // 'EX0' to draw "classic style"
8963 // could take this from the 'band' style object if we create one in future?
8964 h->SetMarkerSize(0);
8965 h->SetFillStyle(3005);
8966 h->SetFillColor(h->GetLineColor());
8967 }
8968
8969 if (nErrorToys > 0) {
8970 errors = false; // wont evaluate error on each toy, will estimate for std.dev or normiles of toys
8971 // need list of errorPars
8972 auto allPars =
8973 (!_coefs.get() ? *this : xRooNode(RooProduct("tmp", "tmp", RooArgList(*rar, *_coefs.get<RooAbsReal>()))))
8974 .pars();
8975 errorPars = new RooArgList; // will be in same order as appear in fr.
8976 for (auto a : fr->floatParsFinal()) {
8977 if (auto par = allPars.get<RooArgList>()->find(*a)) {
8978 errorPars->add(*par);
8979 }
8980 }
8981 errorParsSnap.reset(errorPars->snapshot());
8982 auto l = static_cast<TH1 *>(h->Clone("toys"));
8983 l->Reset(); // removes any functions
8984 l->SetDirectory(0);
8985 h->GetListOfFunctions()->Add(
8986 l, "histsame"); // ensures just this empty hist will be drawn, and not each individual toy
8987
8988 if (errorsLo || errorsHi)
8989 empty = false; // must not be empty b.c. calculation of error relies on knowing nominal (see after loop)
8990 }
8991
8992 for (int toy = 0; toy < (nErrorToys + 1); toy++) {
8993
8994 TH1 *main_h = h;
8995 if (toy > 0) {
8996 h = static_cast<TH1 *>(main_h->Clone(TString::Format("toy_%d", toy)));
8997 h->SetDirectory(0);
8998 h->Reset();
8999 static_cast<TH1 *>(main_h->GetListOfFunctions()->FindObject("toys"))->GetListOfFunctions()->Add(h);
9000 // randomize the parameter values according to the fr's covariance matrix
9001 errorPars->assignValueOnly(fr->randomizePars());
9002 // if any par has 0 error, randomizePars can end up assigning a nan, so replace
9003 // all zero errors with value
9004 for (auto pp : fr->floatParsFinal()) {
9005 auto _vv = dynamic_cast<RooRealVar *>(pp);
9006 if (!_vv)
9007 continue;
9008 if (_vv->getError() == 0)
9009 errorPars->setRealValue(pp->GetName(), _vv->getVal());
9010 }
9011 }
9012
9013 for (int i = std::max(1, binStart); i <= std::min(h->GetNbinsX(), binEnd); i++) {
9014 timeIt.Start(true);
9015 if (x) {
9016 x->setVal(h->GetBinCenter(i));
9017 } else if (cat) {
9018 cat->setLabel(h->GetXaxis()->GetBinLabel(i)); // because order might not match "binning" order
9019 } else if (v) {
9020 v->setBin(i - 1);
9021 }
9022 if (x && !x->inRange("coordRange"))
9023 continue;
9024
9025 double r = 0;
9026 if (!empty || toy > 0) {
9027 r = /*(p && p->selfNormalized())*/ rar->getVal(p ? &normSet : nullptr);
9028#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
9029 if (std::isnan(r) && RooNaNPacker::isNaNWithPayload(r)) {
9031 }
9032#endif
9033 if (r && _coefs.get()) {
9034 r *= _coefs.get<RooAbsReal>()->getVal(normSet);
9035 }
9036 if (needBinWidth) {
9037 r *= h->GetBinWidth(i);
9038 }
9039 if (scaleExpected) {
9040 // std::cout << r << " exp = " << p->expectedEvents(normSet) << " for normRange " << (p->normRange() ?
9041 // p->normRange() : "null") << std::endl; p->Print();rar->Print();
9042 r *= (p->expectedEvents(normSet));
9043 } // do in here in case dependency on var
9044 }
9045 h->SetBinContent(i, r);
9046
9047 if (errors) {
9048 static_cast<TH1 *>(h->FindObject("nominal"))->SetBinContent(i, r); // transfer nominal to nominal hist
9049 double res;
9050 bool doAsym = (errorsHi && errorsLo);
9051 if (doAsym) {
9052 errorsHi = false;
9053 }
9054 if (p) {
9055 // std::cout << "computing error of :" << h->GetBinCenter(i) << std::endl;
9056 // //fr->floatParsFinal().Print(); fr->covarianceMatrix().Print();
9057 // res = PdfWrapper((oldrar) ? *rar : *p, _coefs.get<RooAbsReal>(), !v, oldrar ? p : nullptr)
9058 // .getSimplePropagatedError(*fr, normSet);
9060 PdfWrapper((oldrar) ? *rar : *p, _coefs.get<RooAbsReal>(), !v, oldrar ? p : nullptr), *fr, normSet,
9062#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
9063 // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
9064 p->_normSet = nullptr;
9065#endif
9066 } else {
9067 // res = RooProduct("errorEval", "errorEval",
9068 // RooArgList(*rar, !_coefs.get() ? RooFit::RooConst(1) :
9069 // *_coefs.get<RooAbsReal>()))
9070 // .getPropagatedError(
9071 // *fr /*, normSet*/); // should be no need to pass a normSet to a non-pdf (but
9072 // not verified this)
9074 RooProduct("errorEval", "errorEval",
9075 RooArgList(*rar, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>())),
9076 *fr, {}, &errorPars, errorsHi,
9077 errorsLo); // should be no need to pass a normSet to a non-pdf (but not verified this)
9078 // especially important not to pass in the case we are evaluated RooRealSumPdf as a function! otherwise
9079 // error will be wrong
9080 }
9081 if (needBinWidth) {
9082 res *= h->GetBinWidth(i);
9083 }
9084 h->SetBinError(i, res);
9085 if (doAsym) {
9086 // compute Hi error
9087 errorsHi = true;
9088 errorsLo = false;
9089 if (p) {
9091 PdfWrapper((oldrar) ? *rar : *p, _coefs.get<RooAbsReal>(), !v, oldrar ? p : nullptr), *fr, normSet,
9093 } else {
9095 RooProduct("errorEval", "errorEval",
9096 RooArgList(*rar, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>())),
9097 *fr, {}, &errorPars, errorsHi, errorsLo);
9098 }
9099 if (needBinWidth) {
9100 res *= h->GetBinWidth(i);
9101 }
9102 errorsLo = true;
9103 // lowVal = content - error, highVal = content + res
9104 // => band/2 = (res+error)/2 and band-mid = (2*content+res-error)/2
9105 h->SetBinContent(i, h->GetBinContent(i) + (res - h->GetBinError(i)) * 0.5);
9106 h->SetBinError(i, (res + h->GetBinError(i)) * 0.5);
9107 }
9108 }
9109 timeIt.Stop();
9110 lapTimes.push_back(timeIt.RealTime());
9111 double time_estimate =
9112 (lapTimes.size() > 1)
9113 ? (h->GetNbinsX() * (std::accumulate(lapTimes.begin() + 1, lapTimes.end(), 0.) / (lapTimes.size() - 1)))
9114 : 0.;
9115 if (!warned && (lapTimes.at(0) > 10 || (lapTimes.size() > 2 && time_estimate > 60.))) {
9116 TTimeStamp t2;
9117 t2.Add(time_estimate);
9118 Warning("BuildHistogram", "Building this histogram will take until %s", t2.AsString());
9119 if (errors) {
9120 // install interrupt handler
9121 runningNode = this;
9123 }
9124 warned = true;
9125 }
9126 if (fInterrupted) {
9127 if (errors) {
9128 Warning("BuildHistogram", "Skipping errors for remaining bins");
9129 errors = false;
9130 }
9131 fInterrupted = false;
9132 }
9133 }
9134 if (toy > 0) {
9135 h = main_h;
9136 }
9137 }
9138 if (gOldHandlerr) {
9140 gOldHandlerr = nullptr;
9141 }
9142 normSet = *snap;
9143
9144 if (errorPars) {
9145 if (errorParsSnap)
9147 delete errorPars;
9148 }
9149 if (nErrorToys) {
9150 // compute main histogram error bar from toys
9151 // if not doing asymmetric, then will display std.dev
9152 // otherwise will copy main to nominal and make main error bar s.t. it shows +/-1sigma vals
9153 if (errorsLo && errorsHi) {
9154 auto nomHist = static_cast<TH1 *>(h->FindObject("nominal"));
9155 nomHist->Add(h);
9156 }
9157 for (int i = 1; i <= h->GetNbinsX(); i++) {
9158 std::vector<double> vals;
9159 vals.reserve(nErrorToys);
9160 for (int j = 1; j < (nErrorToys + 1); j++) {
9161 vals.push_back(
9162 static_cast<TH1 *>(h->FindObject("toys")->FindObject(TString::Format("toy_%d", j)))->GetBinContent(i));
9163 }
9164 double upVal, downVal;
9165 if (errorsLo || errorsHi) {
9166 std::sort(vals.begin(), vals.end());
9167 upVal = vals.at(std::round(vals.size() * ROOT::Math::gaussian_cdf(1)));
9168 downVal = vals.at(std::round(vals.size() * ROOT::Math::gaussian_cdf(-1)));
9169 if (!errorsLo)
9170 downVal = 2. * h->GetBinContent(i) - upVal;
9171 if (!errorsHi)
9172 upVal = 2. * h->GetBinContent(i) - downVal;
9173 } else {
9174 double err = TMath::StdDev(vals.begin(), vals.end());
9175 upVal = h->GetBinContent(i) + err;
9176 downVal = h->GetBinContent(i) - err;
9177 }
9178 h->SetBinContent(i, (upVal + downVal) * 0.5);
9179 h->SetBinError(i, (upVal - downVal) * 0.5);
9180 }
9181 }
9182
9183 if (oldrar) {
9184 std::vector<RooAbsArg *> extra;
9185 if (auto s = dynamic_cast<RooSimultaneous *>(rar)) {
9186 // need to delete all the subpdfs we created too
9187 for (auto _pdf : s->servers()) {
9188 if (dynamic_cast<RooAbsPdf *>(_pdf))
9189 extra.push_back(_pdf);
9190 }
9191 }
9192 extra.push_back(rar);
9193 rar = oldrar;
9194 xRooNode(*rar).sterilize(); // need to clear the cache of the created integral - do this before deleting things!
9195 for (auto a : extra)
9196 delete a;
9197 } else {
9198 sterilize(); // needed to forget about the normSet that was passed to getVal()
9199 }
9200
9201 if (!p && !rar->getAttribute("density") && !needBinWidth) {
9202 h->GetYaxis()->SetTitle(rar->getStringAttribute("units"));
9203 } else if ((p && p->canBeExtended()) || (!p && needBinWidth)) {
9204 h->GetYaxis()->SetTitle("Events");
9205 } else {
9206 h->GetYaxis()->SetTitle("Probability Mass");
9207 }
9208 h->GetYaxis()->SetMaxDigits(3);
9209
9210 if (errors) {
9211 delete fr;
9212 }
9213
9214 // build a stack unless not requested
9215 if (!nostack) {
9216 // need to draw copy of hist so shown over the stack
9217 auto hCopy = static_cast<TH1 *>(h->Clone("copy"));
9218 hCopy->Reset();
9219 hCopy->Add(h); // use Reset and Add to clear the function list (dont clear directly as may double-delete if same
9220 // object added twice)
9221 hCopy->SetStats(false);
9222 h->GetListOfFunctions()->Add(hCopy, TString(h->GetOption()) + "same");
9223 h->GetListOfFunctions()->Add(hCopy, "axissame"); // prevents stack covering axis
9224 TString dOpt = (setInterp) ? "LF2" : ""; // should become lf2 if interpolation of histogram is appropriate
9225
9226 const xRooNode *rarNode = this;
9227 RooAbsReal *sf = nullptr;
9228 if (get()->InheritsFrom("RooExtendPdf")) {
9229 const_cast<xRooNode *>(this)->browse();
9230 rarNode = find(".pdf").get();
9231 // rar = rarNode->get<RooAbsReal>();
9232 sf = find(".n")->get<RooAbsReal>();
9233 }
9234
9235 THStack *stack = new THStack("stack", TString::Format("%s;%s", rar->GetTitle(), h->GetXaxis()->GetTitle()));
9236 int count = 0;
9237 std::map<std::string, int> colorByTitle; // TODO: should fill from any existing legend
9238 std::set<std::string> allTitles;
9239 bool titleMatchName = true;
9240 std::map<std::string, TH1 *> histGroups;
9241 std::vector<TH1 *> hhs;
9242 std::set<TH1 *> histsWithBadTitles; // these histograms will have their titles autoFormatted
9243
9244 // support for CMS model case where has single component containing many coeffs
9245 // will build stack by setting each coeff equal to 0 in turn, rebuilding the histogram
9246 // the difference from the "full" histogram will be the component
9248 if (!rarNode->components().empty()) {
9249 auto comps = rarNode->components()[0];
9250 for (auto &c : *comps) {
9251 if (c->fFolder == "!.coeffs")
9252 cms_coefs.add(*c->get<RooAbsArg>());
9253 }
9254 }
9255 if (!cms_coefs.empty()) {
9256 RooRealVar zero("zero", "", 0);
9257 std::shared_ptr<TH1> prevHist(static_cast<TH1 *>(h->Clone()));
9258 prevHist->Reset();
9259 prevHist->Add(h);
9260 for (auto c : cms_coefs) {
9261 // seems I have to remake the function each time, as haven't figured out what cache needs clearing?
9262 std::unique_ptr<RooAbsReal> f(
9263 dynamic_cast<RooAbsReal *>(rarNode->components()[0]->get()->Clone("tmpCopy")));
9264 zero.setAttribute(Form("ORIGNAME:%s", c->GetName())); // used in redirectServers to say what this replaces
9265 f->redirectServers(RooArgSet(zero), false, true); // each time will replace one additional coef
9266 // zero.setAttribute(Form("ORIGNAME:%s",c->GetName()),false); (commented out so that on next iteration
9267 // will still replace all prev)
9268 auto hh = xRooNode(*f, *this).BuildHistogram(v);
9269 hh->SetName(c->GetName());
9270 if (sf)
9271 hh->Scale(sf->getVal());
9272 if (strlen(hh->GetTitle()) == 0) {
9273 hh->SetTitle(c->GetName()); // ensure all hists has titles
9274 histsWithBadTitles.insert(hh);
9275 } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
9276 histsWithBadTitles.insert(hh);
9277 }
9278 titleMatchName &= (TString(c->GetName()) == hh->GetTitle() ||
9279 TString(hh->GetTitle()).BeginsWith(TString(c->GetName()) + "_"));
9280 std::shared_ptr<TH1> nextHist(static_cast<TH1 *>(hh->Clone()));
9281 hh->Add(prevHist.get(), -1.);
9282 hh->Scale(-1.);
9283 hhs.push_back(hh);
9285 }
9286 } else if (get<RooSimultaneous>()) {
9287 // need to create a histogram for each sample across all the channels - will rely on functionality below to
9288 // merge them based on titles
9289
9290 for (auto &chan : bins()) {
9291 TString chanName(chan->GetName());
9292 chanName = chanName(chanName.Index("=") + 1, chanName.Length());
9293 auto samps = chan->mainChild();
9294 if (!samps)
9295 samps = *chan;
9296 for (auto &samp : samps.components()) {
9297 auto hh = static_cast<TH1 *>(h->Clone(samp->GetName()));
9298 hh->Reset();
9299 hh->SetTitle(samp->GetTitle());
9300 if (strlen(hh->GetTitle()) == 0) {
9301 hh->SetTitle(samp->GetName());
9302 histsWithBadTitles.insert(hh);
9303 } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
9304 histsWithBadTitles.insert(hh);
9305 }
9306 hh->SetTitle(TString(hh->GetTitle())
9307 .ReplaceAll(TString(chan->get()->GetName()) + "_",
9308 "")); // remove occurance of channelname_ in title (usually prefix)
9309 titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
9310 TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
9311 hh->SetBinContent(hh->GetXaxis()->FindFixBin(chanName), samp->GetContent());
9312 hhs.push_back(hh);
9313 }
9314 }
9315 } else {
9316 for (auto &samp : rarNode->components()) {
9317 auto hh = samp->BuildHistogram(
9318 v, empty, false /* no errors for stack*/, binStart, binEnd, _fr, false, false, 0, h, true,
9319 setInterp); // passing h to ensure binning is the same for all subcomponent hists
9320 hh->SetName(samp->GetName());
9321 if (sf)
9322 hh->Scale(sf->getVal());
9323 hhs.push_back(hh);
9324 if (strlen(hh->GetTitle()) == 0) {
9325 hh->SetTitle(samp->GetName()); // ensure all hists has titles
9326 histsWithBadTitles.insert(hh);
9327 } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
9328 histsWithBadTitles.insert(hh);
9329 }
9330 titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
9331 TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
9332 }
9333 }
9334
9335 if (!hhs.empty()) {
9336 for (auto &hh : hhs) {
9337 allTitles.insert(hh->GetTitle());
9338 }
9339
9340 // get common prefix to strip off only if all titles match names and
9341 // any title is longer than 10 chars
9342 size_t e = std::min(allTitles.begin()->size(), allTitles.rbegin()->size());
9343 size_t ii = 0;
9344 bool goodPrefix = false;
9345 std::string commonSuffix;
9346 if (titleMatchName && hhs.size() > 1) {
9347 while (ii < e - 1 && allTitles.begin()->at(ii) == allTitles.rbegin()->at(ii)) {
9348 ii++;
9349 if (allTitles.begin()->at(ii) == '_' || allTitles.begin()->at(ii) == ' ')
9350 goodPrefix = true;
9351 }
9352
9353 // find common suffix if there is one .. must start with a "_"
9354 bool stop = false;
9355 while (!stop && commonSuffix.size() < size_t(e - 1)) {
9356 commonSuffix = allTitles.begin()->substr(allTitles.begin()->length() - commonSuffix.length() - 1);
9357 for (auto &tt : allTitles) {
9358 if (!TString(tt).EndsWith(commonSuffix.c_str())) {
9359 commonSuffix = commonSuffix.substr(1);
9360 stop = true;
9361 break;
9362 }
9363 }
9364 }
9365 if (commonSuffix.find('_') == std::string::npos) {
9366 commonSuffix = "";
9367 } else {
9368 commonSuffix = commonSuffix.substr(commonSuffix.find('_'));
9369 }
9370 }
9371 if (!goodPrefix)
9372 ii = 0;
9373
9374 // also find how many characters are needed to distinguish all entries (that dont have the same name)
9375 // then carry on up to first space or underscore
9376 size_t jj = 0;
9377 std::map<std::string, std::string> reducedTitles;
9378 while (reducedTitles.size() != allTitles.size()) {
9379 jj++;
9380 std::map<std::string, int> titlesMap;
9381 for (auto &s : allTitles) {
9382 if (reducedTitles.count(s))
9383 continue;
9384 titlesMap[s.substr(0, jj)]++;
9385 }
9386 for (auto &s : allTitles) {
9387 if (titlesMap[s.substr(0, jj)] == 1 && (jj >= s.length() || s.at(jj) == ' ' || s.at(jj) == '_')) {
9388 reducedTitles[s] = s.substr(0, jj);
9389 }
9390 }
9391 }
9392
9393 // strip common prefix and suffix before adding
9394 for (auto ritr = hhs.rbegin(); ritr != hhs.rend(); ++ritr) { // go in reverse order
9395 if (!histsWithBadTitles.count((*ritr))) {
9396 continue;
9397 }
9398 auto _title = (hhs.size() > 5) ? reducedTitles[(*ritr)->GetTitle()] : (*ritr)->GetTitle();
9399 _title = _title.substr(ii < _title.size() ? ii : 0);
9400 if (!commonSuffix.empty() && TString(_title).EndsWith(commonSuffix.c_str()))
9401 _title = _title.substr(0, _title.length() - commonSuffix.length());
9402 (*ritr)->SetTitle(_title.c_str());
9403 }
9404 }
9405
9406 for (auto &hh : hhs) {
9407 // automatically group hists that all have the same title
9408 if (histGroups.find(hh->GetTitle()) == histGroups.end()) {
9409 histGroups[hh->GetTitle()] = hh;
9410 } else {
9411 // add it into this group
9412 histGroups[hh->GetTitle()]->Add(hh);
9413 delete hh;
9414 hh = nullptr;
9415 continue;
9416 }
9417 auto hhMin = (hh->GetMinimum() == 0) ? hh->GetMinimum(1e-9) : hh->GetMinimum();
9418 if (!stack->GetHists() && h->GetMinimum() > hhMin) {
9419 auto newMin = hhMin - (h->GetMaximum() - hhMin) * gStyle->GetHistTopMargin();
9420 if (hhMin >= 0 && newMin < 0)
9421 newMin = hhMin * 0.99;
9422 // adjustYRange(newMin, h->GetMaximum());
9423 }
9424
9425 /*if(stack->GetHists() && stack->GetHists()->GetEntries()>0) {
9426 // to remove rounding effects on bin boundaries, see if binnings compatible
9427 auto _h1 = dynamic_cast<TH1*>(stack->GetHists()->At(0));
9428 if(_h1->GetNbinsX()==hh->GetNbinsX()) TODO ... finish dealing with silly rounding effects
9429 }*/
9430 TString thisOpt = TString(hh->GetOption()) == "l" ? "LF2" : ""; // need LF2 to get smooth line with fill
9431 // uncomment next line to blend continuous with discrete components .. get some unpleasant "poke through"
9432 // effects though
9433 // if(auto s = samp->get<RooAbsReal>(); s) thisOpt = s->isBinnedDistribution(*dynamic_cast<RooAbsArg*>(v)) ?
9434 // "" : "LF2";
9435 stack->Add(hh, thisOpt);
9436 }
9437 // stack->SetBit(kCanDelete); // should delete its sub histograms
9438 h->GetListOfFunctions()->AddFirst(stack, "noclear same");
9439 // stack->Draw("noclear same");
9440 // h->Draw(
9441 // dOpt + sOpt +
9442 // "same"); // overlay again .. if stack would cover original hist (negative components) we still see
9443 // integral
9444 // h->Draw("axissame"); // redraws axis
9445
9446 TList *ll = stack->GetHists();
9447 if (ll && ll->GetEntries()) {
9448
9449 // finally, ensure all hists are styled
9450 for (auto ho : *ll) {
9451 TH1 *hh = dynamic_cast<TH1 *>(ho);
9452 if (!hh)
9453 continue;
9454 bool createdStyle = (xRooNode(*hh, *this).styles(nullptr, false).get<TStyle>() == nullptr);
9455
9456 if (createdStyle) {
9457 // give hist a color, that isn't the same as any other hists color
9458 hh->SetFillStyle(1001); // solid fill style
9459 bool used = false;
9460 do {
9461 hh->SetFillColor(gEnv->GetValue("XRooFit.MinFillColor", kP10Blue /* was previously 2*/) + (count++));
9462 if (!gROOT->GetColor(hh->GetFillColor())) {
9463 // color doesn't exist, default it to transparent?
9464 hh->SetFillColor(0);
9465 }
9466 // check not already used this color
9467 used = false;
9468 for (auto ho2 : *ll) {
9469 TH1 *hh2 = dynamic_cast<TH1 *>(ho2);
9470 if (!hh2)
9471 continue;
9472 auto _styleNode = xRooNode(*hh2, *this).styles(hh2, false);
9473 auto _style = _styleNode.get<TStyle>();
9474 if (hh != hh2 && _style && _style->GetFillColor() == hh->GetFillColor()) {
9475 used = true;
9476 break;
9477 }
9478 }
9479 } while (used);
9480 }
9481
9482 auto _styleNode = xRooNode(*hh, *this).styles(hh);
9483 if (auto _style = _styleNode.get<TStyle>()) {
9484 *dynamic_cast<TAttLine *>(hh) = *_style;
9485 *dynamic_cast<TAttFill *>(hh) = *_style;
9486 *dynamic_cast<TAttMarker *>(hh) = *_style;
9487 }
9488 // for stacks, fill color of white should be color 10 unless fill style is 0
9489 if (hh->GetFillColor() == kWhite && hh->GetFillStyle() != 0) {
9490 // kWhite means 'transparent' in ROOT ... should really use a FillStyle of 0 for that
9491 // so assume user wanted actual white, which is color 10
9492 hh->SetFillColor(10);
9493 }
9494 }
9495 }
9496 }
9497
9498 return h;
9499}
9500
9501double xRooNode::GetBinData(int bin, const xRooNode &data)
9502{
9503 if (data.get<RooAbsData>()) {
9504 // attach as a child before calling datasets(), so that is included in the list
9505 push_back(std::make_shared<xRooNode>(data));
9506 }
9507 auto node = datasets().find(data.GetName());
9508 if (data.get<RooAbsData>()) {
9509 // remove the child we attached
9510 resize(size() - 1);
9511 }
9512 if (!node)
9513 return std::numeric_limits<double>::quiet_NaN();
9514 return node->GetBinContent(bin);
9515}
9516
9517std::vector<double> xRooNode::GetBinContents(int binStart, int binEnd) const
9518{
9519 if (fBinNumber != -1) {
9520 if (binStart != binEnd || !fParent) {
9521 throw std::runtime_error(TString::Format("%s is a bin - only has one value", GetName()));
9522 }
9523 return fParent->GetBinContents(fBinNumber, fBinNumber);
9524 }
9525 std::vector<double> out;
9526 if (get<RooAbsData>()) {
9527 auto g = BuildGraph(
9528 nullptr,
9529 (binStart != -1 ||
9530 binEnd != -1) /*include points for zeros unless we are asking for a single point with start=end=-1*/);
9531 if (!g) {
9532 return out;
9533 }
9534 if (binStart == binEnd && binStart == -1) {
9535 // integral over all bins if getting bin content -1
9536 double integral(0);
9537 for (int i = 0; i < g->GetN(); i++)
9538 integral += g->GetPointY(i);
9539 out.push_back(integral);
9540 delete g;
9541 return out;
9542 }
9543 for (int i = binStart - 1; i < g->GetN() && (binEnd == 0 || i < binEnd); i++) {
9544 out.push_back(g->GetPointY(i));
9545 }
9546 delete g;
9547 return out;
9548 }
9549
9550 bool doIntegral = false;
9551 if (binStart == binEnd && binStart == -1) {
9552 binStart = -1;
9553 binEnd = -1;
9554 doIntegral = true;
9555 } // return integral if request bin -1
9556 auto h = BuildHistogram(nullptr, false, false, binStart, binEnd);
9557 if (!h) {
9558 throw std::runtime_error(TString::Format("%s has no content", GetName()));
9559 }
9560 if (binEnd == 0) {
9561 binEnd = h->GetNbinsX();
9562 }
9563 if (doIntegral) {
9564 double tot = 0;
9565 for (int i = 1; i <= h->GetNbinsX(); i++) {
9566 tot += h->GetBinContent(i);
9567 }
9568 out.push_back(tot);
9569 } else {
9570 for (int i = binStart; i <= binEnd; i++) {
9571 out.push_back(h->GetBinContent(i));
9572 }
9573 }
9574 delete h;
9575 return out;
9576}
9577
9579{
9580 if (auto a = get<RooAbsArg>(); a) {
9581 // go through servers looking for 'main' thing
9582 for (auto &l : a->servers()) {
9583 if (l->getAttribute("MAIN_MEASUREMENT") || l->InheritsFrom("RooRealSumPdf") || l->InheritsFrom("RooAddPdf")) {
9584 return xRooNode(*l, *this);
9585 }
9586 }
9587 // the main child of a RooProduct is one that has the same name (/alias) as the product (except if is a bin
9588 // factor)
9589 if (a->IsA() == RooProduct::Class() && fBinNumber == -1) {
9590 for (auto &l : factors()) {
9591 if (strcmp(l->GetName(), GetName()) == 0) {
9592 return *l;
9593 }
9594 }
9595 }
9596 }
9597 return xRooNode();
9598}
9599
9601{
9602 if (auto o = get(); o) {
9603 o->Inspect();
9604 } else {
9606 }
9607}
9608
9609bool TopRightPlaceBox(TPad *p, TObject *o, double w, double h, double &xl, double &yb)
9610{
9611#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
9612 // reinitialize collide grid because the filling depends on fUxmin and fUxmax (and ymin ymax too)
9613 // and these aren't filled on the first time we do the placement (they init to 0 and 1), but will be filled
9614 // subsequently
9615 for (int i = 0; i < p->fCGnx; i++) {
9616 for (int j = 0; j < p->fCGny; j++) {
9617 p->fCollideGrid[i + j * p->fCGnx] = true;
9618 }
9619 }
9620 p->FillCollideGrid(o);
9621 Int_t iw = (int)(p->fCGnx * w);
9622 Int_t ih = (int)(p->fCGny * h);
9623
9624 Int_t nxmax = p->fCGnx - iw - 1 - p->fCGnx * p->GetRightMargin();
9625 Int_t nymax = p->fCGny - ih - 1 - p->fCGny * p->GetTopMargin();
9626
9627 for (Int_t j = nymax; j >= 0; j--) {
9628 for (Int_t i = nxmax; i >= 0; i--) {
9629 if (p->Collide(i, j, iw, ih)) {
9630 continue;
9631 } else {
9632 xl = (double)(i) / (double)(p->fCGnx);
9633 yb = (double)(j) / (double)(p->fCGny);
9634 return true;
9635 }
9636 }
9637 }
9638 return false;
9639#else
9640 return p->PlaceBox(o, w, h, xl, yb, "trw");
9641#endif
9642}
9643
9644TPaveText *getPave(const char *name = "labels", bool create = true, bool doPaint = false)
9645{
9646 if (auto p = dynamic_cast<TPaveText *>(gPad->GetPrimitive(name)); p) {
9647 if (doPaint)
9648 gPad->PaintModified(); //-- slows down x11 so trying to avoid
9649 return p;
9650 }
9651 if (!create) {
9652 return nullptr;
9653 }
9654 auto l = new TPaveText(gPad->GetLeftMargin() + 0.02, 1. - gPad->GetTopMargin() - 0.08, 0.6,
9655 1. - gPad->GetTopMargin() - 0.08);
9656 l->SetBorderSize(0);
9657 if (l->GetTextSize() == 0)
9658 l->SetTextSize(gStyle->GetTitleYSize());
9659
9661 // l->SetMargin(0);
9662 l->SetFillStyle(0);
9663 l->SetName(name);
9664 l->Draw();
9665 l->ConvertNDCtoPad();
9666 return l;
9667}
9668
9669TLegend *getLegend(bool create = true, bool doPaint = false)
9670{
9671 if (auto p = dynamic_cast<TLegend *>(gPad->GetPrimitive("legend")); p) {
9672 double x;
9673 double y;
9674 double w = p->GetX2NDC() - p->GetX1NDC();
9675 double h = p->GetY2NDC() - p->GetY1NDC();
9676 if (doPaint)
9677 gPad->PaintModified(); //-- slows down x11 so trying to avoid
9678 if (TopRightPlaceBox(dynamic_cast<TPad *>(gPad), p, w, h, x, y)) {
9679 // squash inside the frame ..
9680 // std::cout << gPad->GetName() << ":" << x << " , " << y << " , " << w << " , " << h << std::endl;
9681 x = std::max(x, (gPad->GetLeftMargin() + 0.02));
9682 y = std::max(y, (gPad->GetBottomMargin() + 0.02));
9683 x = std::min(x, (1. - gPad->GetRightMargin() - 0.02) - w);
9684 y = std::min(y, (1. - gPad->GetTopMargin() - 0.02) - h);
9685 h = std::min(h, (1. - gPad->GetTopMargin() - 0.02) - y);
9686 w = std::min(w, (1. - gPad->GetRightMargin() - 0.02) - x);
9687 // std::cout << gPad->GetName() << ":" << x << " , " << y << " , " << h << " , " << w << std::endl;
9688 p->SetX1NDC(x);
9689 p->SetY1NDC(y);
9690 p->SetX2NDC(x + w);
9691 p->SetY2NDC(y + h);
9692 gPad->Modified();
9693 }
9694 return p;
9695 }
9696 // look for a parent pad called 'legend' and create it there if existing
9697 auto p = gPad;
9698 while ((p != p->GetMother()) && (p = p->GetMother())) {
9699 if (auto q = dynamic_cast<TVirtualPad *>(p->GetPrimitive("legend")); q) {
9700 q->Modified();
9701 p = q;
9702 break;
9703 }
9704 }
9705 auto tmpPad = gPad;
9706 TLegend *l = nullptr;
9707 if (p && strcmp(p->GetName(), "legend") == 0) {
9708 if (l = dynamic_cast<TLegend *>(p->GetPrimitive("legend")); l || !create)
9709 return l;
9710 p->cd();
9711 l = new TLegend(gPad->GetLeftMargin(), 1. - gPad->GetTopMargin(), 1. - gPad->GetRightMargin(),
9712 gPad->GetBottomMargin());
9713 l->SetBorderSize(1); // ensure has a border
9714 } else {
9715 if (!create)
9716 return nullptr;
9717 l = new TLegend(0.6, 1. - gPad->GetTopMargin() - 0.08, 0.75, 1. - gPad->GetTopMargin() - 0.08);
9718 l->SetBorderSize(0);
9719 // legend text will be required to match y-axis
9720 if (l->GetTextSize() == 0) {
9721 l->SetTextSize(gStyle->GetTitleYSize());
9722 l->SetTextFont(gStyle->GetTitleFont("Y"));
9723 }
9724 }
9726 // l->SetMargin(0);
9727 l->SetFillStyle(0);
9728 l->SetName("legend");
9729 l->Draw();
9730 l->ConvertNDCtoPad();
9731 tmpPad->cd();
9732 return l;
9733}
9734
9735std::string formatLegendString(const std::string &s)
9736{
9737 auto i = s.find("\n");
9738 if (i == std::string::npos) {
9739 return s;
9740 }
9741 return std::string("#splitline{") + s.substr(0, i) + "}{" + formatLegendString(s.substr(i + 1)) + "}";
9742}
9743
9744void addLegendEntry(TObject *o, const char *title, const char *opt)
9745{
9746 auto l = getLegend();
9747 if (!l)
9748 return;
9749 // check for entry already existing with same title
9750 for (auto a : *l->GetListOfPrimitives()) {
9751 if (formatLegendString(title) == dynamic_cast<TLegendEntry *>(a)->GetLabel())
9752 return;
9753 }
9754 if (l->GetListOfPrimitives()->GetEntries() > 20)
9755 return; // todo: create an 'other' entry?
9756
9757 auto e = l->AddEntry(o, formatLegendString(title).c_str(), opt);
9758 // move to top of the legend (we add things in at the top)
9759 l->GetListOfPrimitives()->RemoveLast();
9760 l->GetListOfPrimitives()->AddFirst(e);
9761 if (auto nObj = l->GetListOfPrimitives()->GetEntries(); nObj > 0) {
9762 // each entry takes up 0.05 ... maximum of N*(N+4) (where N is # cols) before next column
9763 int nn = l->GetNColumns();
9764 nn *= (nn + 4);
9765 if (nObj > 1 && (nObj % nn) == 1) {
9766 l->SetNColumns(l->GetNColumns() + 1);
9767 if (l->GetBorderSize() == 0) {
9768 l->SetX1NDC(l->GetX2NDC() - 0.15 * l->GetNColumns());
9769 }
9770 }
9771 if (l->GetBorderSize() == 0) {
9772 l->SetY1NDC(l->GetY2NDC() - 0.05 * gPad->GetHNDC() * std::ceil((double(nObj) / l->GetNColumns())));
9773 }
9774 }
9775
9776 getLegend(); // to mark modified
9777}
9778
9779// this exists to avoid calling update excessively because it slows down x11 ... but still
9780// need to call update twice if have a legend drawn in order to relocate it.
9782public:
9783 PadRefresher(TVirtualPad *p) : fPad(p) { nExisting++; }
9785 {
9786 if (fPad) {
9787 getLegend(false, true);
9788 fPad->GetCanvas()->Paint();
9789 fPad->GetCanvas()->Update();
9790#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 30, 00)
9791 fPad->GetCanvas()->ResetUpdated(); // stops previous canvas being replaced in a jupyter notebook
9792#endif
9793 fPad->cd();
9794 }
9795 nExisting--;
9796 }
9797 TVirtualPad *fPad = nullptr;
9798 static int nExisting;
9799};
9800
9802
9804{
9805 // in order to catch exceptions to prevent crash of GUI, do this:
9806 if (gROOT->FromPopUp()) {
9807 gROOT->SetFromPopUp(false);
9808 try {
9809 Draw(opt);
9810 } catch (const std::exception &e) {
9811 new TGMsgBox(
9812 gClient->GetRoot(),
9813 (gROOT->GetListOfBrowsers()->At(0))
9814 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
9815 : gClient->GetRoot(),
9816 "Exception", e.what(),
9817 kMBIconExclamation); // deletes self on dismiss?
9818 }
9819 gROOT->SetFromPopUp(true);
9820 return;
9821 }
9822
9823 TString sOpt2(opt);
9824 sOpt2.ToLower();
9825 if (!get() && !IsFolder() && !sOpt2.Contains("x="))
9826 return;
9827
9828 if (auto mc = get<RooStats::ModelConfig>()) {
9829 xRooNode(*mc->GetPdf(), fParent).Draw(opt); // draw the pdf of the config
9830 } else if (auto ir = get<RooStats::HypoTestInverterResult>()) {
9831 xRooHypoSpace(ir).Draw(opt);
9833 return;
9834 } else if (get<RooStats::HypoTestResult>()) {
9835 if (gPad)
9836 gPad->Clear();
9837 xRooNLLVar::xRooHypoPoint(std::dynamic_pointer_cast<RooStats::HypoTestResult>(fComp)).Draw(opt);
9838 {
9839 PadRefresher p(gPad); // refreshes the pad
9840 }
9842 return;
9843 }
9844
9845 if (sOpt2 == "pcls" && get<RooRealVar>() && fParent && fParent->get<RooAbsPdf>()) {
9846 // use the first selected dataset
9847 auto _dsets = fParent->datasets();
9848 // bool _drawn=false;
9849 TString dsetName = "";
9850 for (auto &d : _dsets) {
9851 if (d->get()->TestBit(1 << 20)) {
9852 dsetName = d->get()->GetName();
9853 break;
9854 }
9855 }
9856 auto hs = fParent->nll(dsetName.Data()).hypoSpace(get<RooRealVar>()->GetName());
9857 hs.limits("cls visualize");
9858 hs.SetName(TUUID().AsString());
9859 if (ws()) {
9860 ws()->import(*hs.result());
9861 }
9862 return;
9863 }
9864
9865 if (auxFunctions.empty()) {
9866 // add the defaults: Ratio and Signif
9868 "Ratio",
9869 [](double a, double b, double) {
9870 if (a == 0)
9871 return 0.;
9872 if (b == 0 && a == 0)
9873 return 1.;
9874 return a / b;
9875 },
9876 true);
9878 "Signif",
9879 [](double n, double b, double sigma) {
9880 double t0 = 0;
9881 if (sigma <= 0.) {
9882 // use simplified expression ...
9883 t0 = 2. * (((n == 0) ? 0 : n * log(n / b)) - (n - b));
9884 } else {
9885 double sigma2 = sigma * sigma;
9886 double b_hathat = 0.5 * (b - sigma2 + sqrt(pow(b - sigma2, 2) + 4 * n * sigma2));
9887 // double s_hat = n - m;
9888 // double b_hat = m;
9889 t0 = 2. * (((n == 0) ? 0 : n * log(n / b_hathat)) + b_hathat - n + pow(b - b_hathat, 2) / (2. * sigma2));
9890 }
9891 if (t0 < 0)
9892 return 0.; // can happen from numerical precision
9893 return (n >= b) ? sqrt(t0) : -sqrt(t0);
9894 },
9895 false);
9896 }
9897
9898 TString sOpt(opt);
9899
9900 RooAbsLValue *v = nullptr;
9901 std::vector<double> xPoints;
9902 if (sOpt2.Contains("x=")) {
9903 // specifying a particular var to scan over ...
9904 int _idx = sOpt2.Index("x=");
9905 int _eidx = sOpt2.Index(';', _idx);
9906 TString varPart = sOpt(_idx + 2, (_eidx < 0 ? sOpt2.Length() : _eidx) - (_idx + 2));
9908 // if varName is of form str(num,num,num) then can infer scan points
9909 if (auto _idx2 = varPart.Index("("); _idx2 > 0) {
9910 varName = varPart(0, _idx2);
9911 TStringToken pattern(TString(varPart(_idx2 + 1, varPart.Length() - _idx2 - 2)), ",");
9912 double min(0);
9913 double max(0);
9914 int nBins = 0;
9915 int ii = 0;
9916 while (pattern.NextToken()) {
9917 TString s = pattern;
9918 if (ii == 0) {
9919 nBins = s.Atoi();
9920 } else if (ii == 1) {
9921 min = s.Atof();
9922 } else if (ii == 2) {
9923 max = s.Atof();
9924 }
9925 ii++;
9926 }
9927 if (nBins > 100)
9928 nBins = 100; // limit scanning to 100 points
9929 if (nBins > 1) {
9930 for (double x = min; x <= max; x += (max - min) / (nBins - 1)) {
9931 xPoints.push_back(x);
9932 }
9933 } else if (nBins == 1)
9934 xPoints.push_back((min + max) / 2.);
9935 }
9936 v = getObject<RooAbsLValue>(varName.Data()).get();
9937 if (!v) {
9938 throw std::runtime_error(TString::Format("Could not find variable %s", varName.Data()));
9939 }
9940 if (xPoints.empty() && !obs().find(varName.Data()) &&
9941 dynamic_cast<RooAbsRealLValue *>(v)) { // will draw obs as regular (e.g. hist)
9942 double tmp = static_cast<RooAbsRealLValue *>(v)->getVal();
9943 for (int i = 0; i < v->numBins(GetName()); i++) {
9944 v->setBin(i, GetName());
9945 xPoints.push_back(static_cast<RooAbsRealLValue *>(v)->getVal());
9946 }
9947 static_cast<RooAbsRealLValue *>(v)->setVal(tmp);
9948 }
9949 sOpt2 = TString(sOpt2(0, _idx)) + sOpt2(_idx + 2 + varPart.Length() + 1, sOpt2.Length());
9950 sOpt = TString(sOpt(0, _idx)) + sOpt(_idx + 2 + varPart.Length() + 1, sOpt.Length());
9951 }
9952 TString forceNames = "";
9953 if (sOpt2.Contains("force")) {
9954 // force plots show how much NLL changes wrt to a change of variables
9955 if (get<RooRealVar>() && fParent && fParent->get<RooAbsPdf>()) {
9956 // assume want force of this parameter from the parent pdf
9957 TString ff = sOpt(sOpt2.Index("force"), sOpt2.Index("force") + 5);
9958 sOpt.ReplaceAll(ff, TString::Format("force%s", get()->GetName()));
9959 fParent->Draw(sOpt);
9960 return;
9961 } else if (get<RooAbsPdf>()) {
9962 // extract the parameter(s) to calculate force for
9963 forceNames = sOpt(sOpt2.Index("force") + 5, sOpt2.Length());
9964 sOpt = sOpt(0, sOpt2.Index("force"));
9965 sOpt2 = sOpt2(0, sOpt2.Index("force"));
9966 } else {
9967 Error("Draw", "Can only compute forces with PDFs");
9968 return; // don't throw because will cause browser to exit if done from there
9969 }
9970 }
9971 bool hasOverlay = sOpt2.Contains("overlay");
9972 TString overlayName = "";
9973 if (hasOverlay) {
9974 // whatever follows overlay is the variation name
9975 overlayName = sOpt(sOpt2.Index("overlay") + 7, sOpt2.Length());
9976 sOpt = sOpt(0, sOpt2.Index("overlay"));
9977 sOpt2 = sOpt2(0, sOpt2.Index("overlay"));
9978 }
9979 if (sOpt2.Contains("ratio") && !sOpt2.Contains("auxratio"))
9980 sOpt += "auxRatio";
9981 if (sOpt2.Contains("significance") && !sOpt2.Contains("auxsignif"))
9982 sOpt += "auxSignif";
9983
9984 std::string auxPlotTitle;
9985 for (auto &[k, _] : auxFunctions) {
9986 if (sOpt.Contains(TString::Format("aux%s", k.c_str()))) {
9987 auxPlotTitle = k;
9988 }
9989 sOpt.ReplaceAll(TString::Format("aux%s", k.c_str()), "");
9990 }
9991
9992 sOpt.ToLower();
9993 sOpt.ReplaceAll("ratio", "");
9994 sOpt.ReplaceAll("significance", ""); // remove old option if still given
9995 bool nostack = sOpt.Contains("nostack");
9996 sOpt.ReplaceAll("nostack", "");
9997 bool hasSame = sOpt.Contains("same");
9998 sOpt.ReplaceAll("same", "");
9999 bool hasGoff = sOpt.Contains("goff");
10000 sOpt.ReplaceAll("goff", "");
10001 bool hasFR = sOpt.Contains("pull") && !get<RooFitResult>();
10002 sOpt.ReplaceAll("pull", "");
10003 bool hasText = sOpt.Contains("text");
10004 bool hasTexte = sOpt.Contains("texte");
10005 bool hasErrorOpt = sOpt.Contains("e");
10006 sOpt.ReplaceAll("e", "");
10007 if (hasTexte) {
10008 sOpt.ReplaceAll("txt", "texte");
10009 } else if (hasText) {
10010 sOpt.ReplaceAll("txt", "text");
10011 }
10012 if (auxPlotTitle == "Signif")
10013 hasErrorOpt = true; // must calculate error to calculate significance
10014 if (hasOverlay)
10015 hasSame = true; // when overlaying must be putting on same
10016
10017 TVirtualPad *pad = gPad;
10018
10019 TH1 *hAxis = nullptr;
10020
10021 auto clearPad = []() {
10022 gPad->Clear();
10023 if (gPad->GetNumber() == 0) {
10024 gPad->SetBottomMargin(gStyle->GetPadBottomMargin());
10025 gPad->SetTopMargin(gStyle->GetPadTopMargin());
10026 gPad->SetLeftMargin(gStyle->GetPadLeftMargin());
10027 gPad->SetRightMargin(gStyle->GetPadRightMargin());
10028 }
10029 // if (gPad == gPad->GetCanvas()) {
10030 // gPad->GetCanvas()->SetCanvasSize( gPad->GetCanvas()->GetWindowWidth() - 4,
10031 // gPad->GetCanvas()->GetWindowHeight() - 28 );
10032 // }
10033 };
10034
10035 if (!hasSame || !pad) {
10036 if (!pad) {
10038 pad = gPad;
10039 }
10040
10041 } else {
10042 // get the histogram representing the axes
10043 hAxis = dynamic_cast<TH1 *>(pad->GetPrimitive("axis"));
10044 if (!hAxis) {
10045 for (auto o : *pad->GetListOfPrimitives()) {
10046 if (hAxis = dynamic_cast<TH1 *>(o); hAxis)
10047 break;
10048 }
10049 }
10050 if (hAxis && !v) {
10051 v = getObject<RooAbsLValue>(hAxis->GetXaxis()->IsAlphanumeric() ? hAxis->GetXaxis()->GetTimeFormatOnly()
10052 : hAxis->GetXaxis()->GetName())
10053 .get();
10054 }
10055 }
10056
10057 if (!hasSame) {
10058 if (gPad != gPad->GetCanvas()) {
10059 gPad->SetName(GetName()); // only rename the pad if its not the parent canvas
10060 }
10061 gPad->SetTitle(GetTitle());
10062 }
10063
10065
10066 auto adjustYRange = [&](double min, double max, TH1 *hh = nullptr, bool symmetrize = false) {
10067 if (!hh)
10068 hh = hAxis;
10069 // give max and min a buffer ...
10070 max += gStyle->GetHistTopMargin() * (max - min);
10071 if (min > 0)
10072 min = std::max(min * 0.9, min - gStyle->GetHistTopMargin() * (max - min));
10073 if (hh) {
10074 double ymin = hh->GetMinimum();
10075 double ymax = hh->GetMaximum();
10076 if (hh->GetMaximumStored() == -1111)
10077 ymax += gStyle->GetHistTopMargin() * (ymax - ymin);
10078 if (hh->GetMinimumStored() == -1111) {
10079 if (gStyle->GetHistMinimumZero() && ymax >= 0) {
10080 ymin = 0;
10081 } else if (ymin < 0) {
10082 ymin -= gStyle->GetHistTopMargin() * (ymax - ymin);
10083 } else {
10084 ymin = std::max(ymin * 0.9, ymin - gStyle->GetHistTopMargin() * (ymax - ymin));
10085 }
10086 // see TGLPlotPainter to complete the mimic, but we leave off here truncating @ 0 if ymax>0
10087 }
10088 // make ymax at least 3x bigger than biggest error if has error
10089 if (hh->GetSumw2()) {
10090 double smallestErrDown3 = -std::numeric_limits<double>::infinity();
10091 double smallestErrUp3 = std::numeric_limits<double>::infinity();
10092 for (int i = 1; i <= hh->GetNbinsX(); i++) {
10093 smallestErrDown3 = std::max(smallestErrDown3, hh->GetBinContent(i) - 3 * hh->GetBinError(i));
10094 smallestErrUp3 = std::min(smallestErrUp3, hh->GetBinContent(i) + 3 * hh->GetBinError(i));
10095 }
10096 max = std::max(max, smallestErrUp3);
10097 min = std::min(min, smallestErrDown3);
10098 }
10099 bool change = false;
10100 if (min < ymin) {
10101 ymin = min;
10102 change = true;
10103 }
10104 if (max > ymax) {
10105 ymax = max;
10106 change = true;
10107 }
10108 if (change) {
10109 // note: unfortunately when user 'unzooms' y axis it resets stored minimum to -1111, so lose range
10110 if (symmetrize) {
10111 double down = hh->GetBinContent(1) - ymin;
10112 double up = ymax - hh->GetBinContent(1);
10113 if (down > up) {
10114 ymax = hh->GetBinContent(1) + down;
10115 } else {
10116 ymin = hh->GetBinContent(1) - up;
10117 }
10118 }
10119 if (hh == hAxis && pad && !pad->GetLogy() && ymin > 0 && (log10(ymax) - log10(max)) >= 3) {
10120 // auto-log the pad
10121 pad->SetLogy();
10122 }
10123 if (hh == hAxis && pad && ymin == 0 && pad->GetLogy()) {
10124 ymin = 1e-2;
10125 }
10126 if (ymin == 0 && ymax > 10)
10127 ymin = 0.1; // adjust min so if user activates log scale it isn't bad
10128 hh->SetMinimum(ymin);
10129 hh->SetMaximum(ymax);
10130 hh->GetYaxis()->Set(1, ymin, ymax);
10131 hh->SetAxisRange(ymin, ymax, "Y");
10132 }
10133 }
10134 };
10135
10136 auto graphMinMax = [](TGraphAsymmErrors *gr) {
10137 double ymax = -std::numeric_limits<double>::infinity();
10138 double ymin = std::numeric_limits<double>::infinity();
10139 for (int i = 0; i < gr->GetN(); i++) {
10140 ymax = std::max(ymax, gr->GetPointY(i) + gr->GetErrorYhigh(i));
10141 ymin = std::min(ymin, gr->GetPointY(i) - gr->GetErrorYlow(i));
10142 }
10143 return std::make_pair(ymin, ymax);
10144 };
10145
10146 if (!xPoints.empty()) {
10147 // create a graph using GetContent
10149 out->SetName(GetName());
10150 out->SetTitle(GetTitle());
10151 out->SetFillColor(out->GetLineColor());
10152 out->SetMarkerStyle(0);
10153 out->SetFillStyle(hasErrorOpt ? 3005 : 0);
10154 double tmp = static_cast<RooAbsRealLValue *>(v)->getVal();
10155 for (auto &x : xPoints) {
10156 static_cast<RooAbsRealLValue *>(v)->setVal(x);
10157 out->AddPoint(x, GetContent());
10158 if (hasErrorOpt) {
10159 out->SetPointEYlow(out->GetN() - 1, GetError());
10160 out->SetPointEYhigh(out->GetN() - 1, out->GetErrorYlow(out->GetN() - 1)); // symmetric error for now
10161 }
10162 }
10163 static_cast<RooAbsRealLValue *>(v)->setVal(tmp);
10164 out->GetHistogram()->GetXaxis()->SetTitle(static_cast<RooAbsRealLValue *>(v)->GetTitle());
10165 out->SetBit(kCanDelete);
10166 out->Draw(TString(hasSame ? "L" : "AL") + (hasErrorOpt ? "3" : ""));
10167 return;
10168 }
10169
10170 if (hasFR) {
10171 // drawing the fitresult as a pull plot on a subpad, and rest of the draw elsewhere
10172 clearPad();
10173 pad->Divide(1, 2, 1e-9, 1e-9); //,0,0);
10174 pad->GetPad(1)->SetPad(0, 0.2, 1, 1);
10175 pad->GetPad(2)->SetPad(0, 0, 1, 0.2);
10176 TString optNoFR(opt);
10177 optNoFR.ReplaceAll("pull", "");
10178 pad->cd(1);
10179 Draw(optNoFR);
10180 pad->cd(2);
10181 auto _fr = fitResult();
10182 _fr.Draw();
10183 // switch into subpad
10184 gPad->cd(1);
10185 gPad->SetFillColor(kGray);
10186 gPad->GetFrame()->SetFillColor(kWhite);
10187 gPad->GetFrame()->SetFillStyle(1001);
10188 gPad->SetTopMargin(0);
10189 gPad->SetBottomMargin(0);
10190 gPad->SetName("pull");
10191 // split the pull graph into individual points -- for benefit of GUI status bar
10192 auto pullGraph = dynamic_cast<TGraphAsymmErrors *>(gPad->GetPrimitive("pulls"));
10193 if (!pullGraph) {
10194 Error("Draw", "Couldn't find pull graph");
10195 return;
10196 }
10197 pullGraph->SetName("nominal");
10198 TMultiGraph *mg = new TMultiGraph;
10199 mg->SetName("editables");
10200
10201 auto scaleHist = static_cast<TH1 *>(pullGraph->FindObject("scales"));
10202 if (!scaleHist)
10203 throw std::runtime_error("Could not find scales in fit result");
10204
10205 for (auto i = 0; i < pullGraph->GetN(); i++) {
10206 auto g = new TGraphAsymmErrors;
10207 g->SetName(scaleHist->GetXaxis()->GetBinLabel(i + 1));
10208 auto _p = dynamic_cast<RooRealVar *>(_fr.get<RooFitResult>()->floatParsFinal().find(g->GetName()));
10209 if (!_p) {
10210 Warning("Draw", "Found a non-var in the floatParsFinal list: %s - this shouldn't happen", g->GetName());
10211 continue;
10212 }
10213 g->SetTitle(TString::Format(
10214 "%s=%g +/- %s [%g,%g]", strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName(), _p->getVal(),
10215 _p->hasAsymError() ? TString::Format("(%g,%g)", _p->getAsymErrorHi(), _p->getAsymErrorLo()).Data()
10216 : TString::Format("%g", _p->getError()).Data(),
10217 scaleHist->GetBinContent(i + 1), scaleHist->GetBinError(i + 1)));
10218 g->SetPoint(0, pullGraph->GetPointX(i), pullGraph->GetPointY(i));
10219 g->SetPointEYhigh(0, pullGraph->GetErrorYhigh(i));
10220 g->SetPointEYlow(0, pullGraph->GetErrorYlow(i));
10221 g->SetEditable(true);
10222 g->SetHighlight(true);
10223 g->SetMarkerStyle(20);
10224 g->SetMarkerSize(0.5);
10225 mg->Add(g);
10226 }
10227 // gPad->GetListOfPrimitives()->Remove(pullGraph); delete pullGraph;
10228 mg->Draw("z0p");
10229 mg->SetBit(kCanDelete);
10230 auto _thisClone = new xRooNode("node", fComp, fParent);
10231 _thisClone->SetBit(kCanDelete);
10232 _thisClone->AppendPad();
10233
10234 // ensure statusbar visible for interactive plot
10235 // turned this off for now ... as not needed if doing through browser, status bar already there
10236 // if (gPad->GetCanvas() && !gPad->GetCanvas()->TestBit(TCanvas::kShowEventStatus)) {
10237 // gPad->GetCanvas()->ToggleEventStatus();
10238 // }
10239 gPad->AddExec("interactivePull", TString::Format("%s::Interactive_Pull()", ClassName()));
10240
10241 pad->cd();
10242 return;
10243 }
10244
10245 if (auto _simPdf = get<RooSimultaneous>();
10246 _simPdf && !(v && strcmp(_simPdf->indexCat().GetName(), dynamic_cast<TObject *>(v)->GetName()) == 0)) {
10247 auto _channels = bins();
10248 int _size = 0;
10249 for (auto &_v : _channels) {
10250 if (!_v->IsHidden())
10251 _size++;
10252 }
10253 if (!hasSame) {
10254 if (_size > 4) {
10255 // add a pad for the common legends
10256 _size++;
10257 }
10258 clearPad();
10259 pad->SetBorderSize(0);
10260 // if (pad->GetCanvas() == pad) {
10261 // if(_size>4) {
10262 // int n = _size;
10263 // Int_t w = 1, h = 1;
10264 // if (pad->GetCanvas()->GetWindowWidth() > pad->GetCanvas()->GetWindowHeight()) {
10265 // w = TMath::Ceil(TMath::Sqrt(n));
10266 // h = TMath::Floor(TMath::Sqrt(n));
10267 // if (w*h < n) w++;
10268 // } else {
10269 // h = TMath::Ceil(TMath::Sqrt(n));
10270 // w = TMath::Floor(TMath::Sqrt(n));
10271 // if (w*h < n) h++;
10272 // }
10273 // // adjust the window size to display only 4 in the window, with scroll bars
10274 // pad->GetCanvas()->SetCanvasSize( w*((pad->GetCanvas()->GetWindowWidth()-4)/2.) -16
10275 // ,h*((pad->GetCanvas()->GetWindowHeight()-28)/2.) - 16 );
10276 // } else {
10277 // //pad->GetCanvas()->Set(
10278 // w*(pad->GetCanvas()->GetWindowWidth()/2.),h*(pad->GetCanvas()->GetWindowHeight()/2.)) )
10279 // }
10280 // }
10281 int ncols = _simPdf->getStringAttribute("ncols") ? TString(_simPdf->getStringAttribute("ncols")).Atoi() : 0;
10282 if (ncols > 0) {
10283 dynamic_cast<TPad *>(pad)->Divide(ncols, std::ceil(double(_size) / ncols), 1e-9, 1e-9);
10284 } else {
10285 dynamic_cast<TPad *>(pad)->DivideSquare(_size, 1e-9, 1e-9);
10286 }
10287 if (_size > 5) {
10288 auto _pad = pad->GetPad(_size); // will use as the legend pad
10289 _pad->SetName("legend");
10290 // stretch the pad all the way to the left
10291 _pad->SetPad(_pad->GetXlowNDC(), _pad->GetYlowNDC(), 1.0, _pad->GetYlowNDC() + _pad->GetHNDC());
10292 // and make all the remaining pads transparent
10293 int x = _size;
10294 while (pad->GetPad(x + 1)) {
10295 pad->GetPad(x + 1)->SetFillStyle(0);
10296 x++;
10297 }
10298 }
10299 }
10300 int i = 0;
10301 auto &chanVar = const_cast<RooAbsCategoryLValue &>(_simPdf->indexCat());
10302 // auto _idx = chanVar.getIndex();
10303 auto _range = GetRange();
10304 std::vector<TString> chanPatterns;
10305 if (_range && strlen(_range)) {
10306 TStringToken pattern(_range, ",");
10307 while (pattern.NextToken()) {
10308 chanPatterns.emplace_back(pattern);
10309 }
10310 }
10311 for (auto &_v : _channels) {
10312 if (_v->IsHidden())
10313 continue;
10314 TString s(_v->GetName());
10315 pad->cd(++i);
10316 gPad->SetName(s);
10317 TString cName = s(s.Index('=') + 1, s.Length());
10318 chanVar.setLabel(cName);
10319 bool inRange = chanPatterns.empty();
10320 for (auto &p : chanPatterns) {
10321 if (chanVar.inRange(p)) {
10322 inRange = true;
10323 break;
10324 }
10325 }
10326 if (!inRange || !_v->get<RooAbsReal>()->isSelectedComp())
10327 gPad->SetFillColor(kGray);
10328 if (!hasSame && _size > 1 && (gStyle->GetTitleFont("Y") % 10) == 3)
10329 gPad->SetLeftMargin(std::min(gPad->GetLeftMargin() * (1. / gPad->GetWNDC()), 0.3));
10330 _v->Draw(opt);
10332 }
10333 pad->cd(0);
10334 gPad->Modified();
10335 // gPad->Update();
10336 return;
10337 }
10338
10339 if (!get() || get<RooArgList>()) {
10340 // is a group draw all the submembers
10341 browse();
10342 int _size = 0;
10343 // int _size = _channels.size(); // size(); if (find("!.vars")) _size--;
10344 for (auto &_v : *this) {
10345 if (_v->IsHidden())
10346 continue;
10347 if (strcmp(GetName(), ".vars") == 0) {
10348 // auto hide obs and "1" and const var
10349 if (_v->get<RooAbsArg>()->getAttribute("obs"))
10350 continue;
10351 if (strcmp(_v->get()->GetName(), "1") == 0 || strcmp(_v->get()->GetName(), "ONE") == 0 ||
10352 TString(_v->get()->GetName()).BeginsWith("binWidth_"))
10353 continue;
10354 if (_v->get()->InheritsFrom("RooConstVar"))
10355 continue;
10356 }
10357 TString s(_v->GetName());
10358 if (s.BeginsWith(".") || s.BeginsWith("!"))
10359 continue;
10360 _size++;
10361 }
10362 if (!hasSame) {
10363 clearPad();
10364 pad->SetBorderSize(0);
10365 dynamic_cast<TPad *>(pad)->DivideSquare(_size, 1e-9, 1e-9);
10366 }
10367 int i = 0;
10368 for (auto &_v : *this) {
10369 if (_v->IsHidden())
10370 continue;
10371 if (strcmp(GetName(), ".vars") == 0) {
10372 // auto hide obs and "1" and const var
10373 if (_v->get<RooAbsArg>()->getAttribute("obs"))
10374 continue;
10375 if (strcmp(_v->get()->GetName(), "1") == 0 || strcmp(_v->get()->GetName(), "ONE") == 0 ||
10376 TString(_v->get()->GetName()).BeginsWith("binWidth_"))
10377 continue;
10378 if (_v->get()->InheritsFrom("RooConstVar"))
10379 continue;
10380 }
10381 TString s(_v->GetName());
10382 if (s.BeginsWith(".") || s.BeginsWith("!"))
10383 continue;
10384 pad->cd(++i);
10385 gPad->SetName(s);
10386 if (!hasSame && _size > 1 && (gStyle->GetTitleFont("Y") % 10) == 3)
10387 gPad->SetLeftMargin(std::min(gPad->GetLeftMargin() * (1. / gPad->GetWNDC()), 0.3));
10388 _v->Draw(opt);
10389 // pad->Modified();//pad->Update();
10391 }
10392 pad->cd(0);
10393 gPad->Modified();
10394 // gPad->Update();
10395 return;
10396 }
10397
10398 if (get()->InheritsFrom("RooProdPdf")) {
10399 // draw the main pdf, if there is one...
10400 auto _mainChild = mainChild();
10401 if (_mainChild) {
10402 _mainChild.Draw(opt);
10403 gPad->SetName(GetName());
10404 return;
10405 }
10406 }
10407
10408 if (auto fr = get<RooFitResult>(); fr) {
10409 if (sOpt.Contains("corr")) {
10410 // do correlation matrix
10411 // if a number follows 'corr', reduce the correlation matrix to show only the most extreme correlations
10412 int numCorrs = TString(sOpt(sOpt.Index("corr") + 4, sOpt.Length())).Atoi();
10413 if (numCorrs == 0)
10414 numCorrs = fr->correlationMatrix().GetNcols();
10415
10416 TH2 *hist = nullptr;
10417 if (numCorrs < fr->correlationMatrix().GetNcols()) {
10418 // need to reduce
10419 std::set<std::pair<double, size_t>> maxCorrs;
10420 for (int i = 0; i < fr->correlationMatrix().GetNcols(); i++) {
10421 double maxCorr = 0;
10422 for (int j = 0; j < fr->correlationMatrix().GetNcols(); j++) {
10423 if (j == i)
10424 continue;
10425 maxCorr = std::max(std::abs(fr->correlationMatrix()(i, j)), maxCorr);
10426 }
10427 maxCorrs.insert({maxCorr, i});
10428 }
10429 std::vector<size_t> topN;
10430 int c = 0;
10431 for (auto itr = maxCorrs.rbegin(); itr != maxCorrs.rend(); ++itr) {
10432 topN.push_back(itr->second);
10433 c++;
10434 if (c == numCorrs)
10435 break;
10436 }
10437 hist = new TH2D(fr->GetName(), TString::Format("%s - Top %d correlations", fr->GetTitle(), numCorrs),
10439 for (size_t i = 0; i < topN.size(); i++) {
10440 hist->GetXaxis()->SetBinLabel(i + 1, fr->floatParsFinal().at(topN.at(i))->GetTitle());
10441 hist->GetYaxis()->SetBinLabel(numCorrs - i, fr->floatParsFinal().at(topN.at(i))->GetTitle());
10442 for (size_t j = 0; j < topN.size(); j++) {
10443 hist->Fill(i + 0.5, numCorrs - j - 0.5, fr->correlationMatrix()(topN.at(i), topN.at(j)));
10444 }
10445 }
10446 hist->SetMinimum(-1);
10447 hist->SetMaximum(1);
10448
10449 } else {
10450 hist = fr->correlationHist(fr->GetName());
10451 hist->SetTitle(fr->GetTitle());
10452 }
10453
10454 hist->SetBit(kCanDelete);
10455 hist->Scale(100);
10456 hist->SetStats(false);
10457 hist->SetDirectory(nullptr);
10459 gStyle->SetPaintTextFormat(".1f");
10460 hist->GetXaxis()->SetTickSize(0);
10461 hist->GetYaxis()->SetTickSize(0);
10462 hist->SetMinimum(-100);
10463 hist->Draw(sOpt);
10465 gPad->SetGrid(1, 1);
10466 gPad->SetLogy(0);
10467 gPad->SetLogx(0);
10468 return;
10469 }
10470
10471 if (sOpt.Contains("brakdown")) { // e will have been removed above
10472
10473 // breakdown is quadrature difference between total error and conditional error
10474 // group by 'group' attribute
10475
10476 std::string poiName;
10477 if (sOpt.Contains("brakdown:")) {
10478 TString sOpt3(opt);
10479 poiName = sOpt3(sOpt3.Index("breakdown:") + 10, sOpt3.Length());
10480 } else {
10481 std::unique_ptr<RooAbsCollection> _poi(fr->floatParsFinal().selectByAttrib("poi", true));
10482 if (_poi->empty()) {
10483 throw std::runtime_error("No floating poi in the fit");
10484 } else if (_poi->size() != 1) {
10485 throw std::runtime_error("Multiple poi in the fit");
10486 }
10487 poiName = _poi->first()->GetName();
10488 }
10489 RooRealVar *poi = dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(poiName.c_str()));
10490 if (!poi) {
10491 throw std::runtime_error(TString::Format("Cannot find parameter %s", poiName.c_str()));
10492 }
10493 std::set<std::string> groups;
10494 for (auto p : fr->floatParsFinal()) {
10495 if (p == poi) {
10496 continue;
10497 } else if (p->getStringAttribute("group")) {
10498 groups.insert(p->getStringAttribute("group"));
10499 } else {
10500 groups.insert(p->GetTitle());
10501 }
10502 }
10503
10504 auto roundedVal = xRooFit::matchPrecision(std::pair(poi->getVal(), poi->getError()));
10505
10506 TPie *pie = new TPie(TString::Format("breakdown:%s", poi->GetName()),
10507 TString::Format("%s: %g #pm %g", poi->GetTitle(), roundedVal.first, roundedVal.second),
10508 groups.size() + 1);
10509
10510 // for display of errors will go to one extra dp ...
10511 roundedVal.second *= .1;
10512
10513 // do breakdown by removing parameters in blocks according to groups and seeing impact on variance
10514 // this will give the correct sum but will be order-dependent if there are correlations between
10515 // groups. therefore we will stick with group-by-group
10516 // RooArgList pars(fr->floatParsFinal()); // pars to not condition on
10517 // double variance = pow(dynamic_cast<RooRealVar*>(poi)->getError(),2);
10518 int i = 0;
10519 for (auto group : groups) {
10520 RooArgList pars(fr->floatParsFinal()); // pars to not condition on
10521 double variance = pow(dynamic_cast<RooRealVar *>(poi)->getError(), 2);
10522 for (auto p : fr->floatParsFinal()) {
10523 if (p == poi) {
10524 continue;
10525 } else if ((p->getStringAttribute("group") && group == p->getStringAttribute("group")) ||
10526 (!p->getStringAttribute("group") && group == p->GetTitle())) {
10527 // conditioning on this parameter ... remove from pars list
10528 pars.remove(*p);
10529 }
10530 }
10531 int idx = pars.index(poiName.c_str());
10532 double reducedVar = fr->conditionalCovarianceMatrix(pars)(idx, idx);
10533 if (reducedVar > variance) {
10534 Warning("Draw", "breakdown group %s variance bigger than preceding?", group.c_str());
10535 pie->SetEntryVal(i, 0);
10536 pie->SetEntryLabel(i, TString::Format("%s: NaN", group.c_str()));
10537 } else {
10538 pie->SetEntryVal(i, variance - reducedVar);
10540 std::pair(sqrt(variance - reducedVar), roundedVal.second)); // r.first will be the rounded error
10541 if (r.first > 0) {
10542 pie->SetEntryLabel(i, TString::Format("%s: %g", group.c_str(), r.first));
10543 } else {
10544 pie->SetEntryLabel(i, group.c_str()); // suppress labels for negligible errors.
10545 }
10546 }
10547 pie->SetEntryFillColor(i, TColor::GetColorPalette(TColor::GetNumberOfColors() * i / pie->GetEntries()));
10548 // variance = reducedVar;
10549 i++;
10550 }
10551 // remaining variance is statistical=
10552 double variance = fr->conditionalCovarianceMatrix(*poi)(0, 0);
10553 auto r =
10554 xRooFit::matchPrecision(std::pair(sqrt(variance), roundedVal.second)); // r.first will be the rounded error
10555 pie->SetEntryVal(i, variance);
10556 pie->SetEntryLabel(i, TString::Format("stat: %g", r.first));
10557 pie->SetEntryFillColor(i, TColor::GetColorPalette(TColor::GetNumberOfColors() * i / pie->GetEntries()));
10558 pie->SetBit(kCanDelete);
10559 pie->SetRadius(0.17);
10560 pie->SetTextSize(gStyle->GetTitleYSize());
10561 pie->Draw("NOL");
10562 return;
10563 }
10564
10565 // plot pull or impact
10567 out->SetName(TString::Format("%s_pull", fr->GetName()));
10568 out->SetTitle("Fit Result Pulls");
10569 std::vector<TString> graphLabels;
10571 ugraph->SetName(TString::Format("%s_pull_unconstrained", fr->GetName()));
10572 ugraph->SetTitle("Fit Result Pulls");
10573 std::vector<TString> ugraphLabels;
10574 std::map<std::string, double> scale;
10575 std::map<std::string, double> offset;
10576 for (auto &p : fr->floatParsFinal()) {
10577 auto _v = dynamic_cast<RooRealVar *>(p);
10578 if (!_v)
10579 continue;
10580
10581 if (std::isnan(_v->getErrorHi()) || std::isnan(_v->getErrorLo())) {
10582 Warning("Draw", "%s error is invalid", _v->GetName());
10583 }
10584
10585 // need to get constraint mean and error parameters ....
10586 // look for normal gaussian and poisson cases
10587 double prefitError = 0;
10588 double prefitVal = 0;
10589 double customScale = 0;
10590 if (auto ip =
10591 dynamic_cast<RooRealVar *>(fr->floatParsInit().find(p->GetName()))) { // handles if no prefit available
10592 prefitError = ip->getError();
10593 prefitVal = ip->getVal();
10594 };
10595
10596 std::shared_ptr<xRooNode> pConstr;
10597 if (fParent && fParent->getObject<RooRealVar>(p->GetName())) {
10598 auto _vv = fParent->getObject<RooRealVar>(p->GetName());
10599 if (_vv->hasRange("pullScale")) {
10600 customScale = (_vv->getMax("pullScale") - _vv->getMin("pullScale")) / 2.;
10601 }
10602 auto _constr = xRooNode(_vv, *this).constraints();
10603 for (auto &c : _constr) {
10604 if (c->get<RooPoisson>() || c->get<RooGaussian>()) {
10605 // require parameter to be a direct server of the constraint pdf to count if its a gaussian
10606 bool isServer = true;
10607 if (c->get<RooGaussian>()) {
10608 isServer = false;
10609 for (auto s : c->get<RooAbsArg>()->servers()) {
10610 if (strcmp(s->GetName(), p->GetName()) == 0) {
10611 isServer = true;
10612 break;
10613 }
10614 }
10615 }
10616 if (isServer) {
10617 pConstr = c;
10618 break;
10619 }
10620 }
10621 }
10622 }
10623 if (pConstr) {
10624
10625 // there will be 3 deps, one will be this par, the other two are the mean and error (or error^2 in case of
10626 // poisson
10627
10628 // std::cout << p->GetName() << " extracted " << prefitVal << " " << prefitError << " from ";
10629 // pConstr->deps().Print();
10630 pConstr->browse();
10631 if (pConstr->get<RooPoisson>() && pConstr->find(".x")) {
10632 std::string xName = pConstr->find(".x")->get()->GetName();
10633 prefitVal = pConstr->find(".x")->get<RooAbsReal>()->getVal();
10634 for (auto &_d : pConstr->vars()) {
10635 if (strcmp(p->GetName(), _d->get()->GetName()) == 0)
10636 continue;
10637 if (xName == _d->get()->GetName())
10638 continue;
10639 if (_d->get<RooAbsReal>()->getVal())
10640 prefitError = _d->get<RooAbsReal>()->getVal();
10641 }
10642 if (fr->constPars().find(pConstr->find(".x")->get()->GetName())) {
10643 // globs was saved to fr, use that instead of current value
10644 prefitVal = fr->constPars().getRealValue(pConstr->find(".x")->get()->GetName());
10645 }
10646 // prefitVal will be the global observable value, need to divide that by tau
10648 // prefiterror will be tau ... need 1/sqrt(tau) for error
10649 prefitError = 1. / sqrt(prefitError);
10650 } else if (auto _g = pConstr->get<RooGaussian>(); _g) {
10651 prefitError =
10652 (pConstr->find(".sigma")) ? pConstr->find(".sigma")->get<RooAbsReal>()->getVal() : prefitError;
10653 prefitVal =
10654 (pConstr->find(".x")) ? pConstr->find(".x")->get<RooAbsReal>()->getVal() : 0; // usually the globs
10655 if (pConstr->find(".x") && fr->constPars().find(pConstr->find(".x")->get()->GetName())) {
10656 // globs was saved to fr, use that instead of current value
10657 prefitVal = fr->constPars().getRealValue(pConstr->find(".x")->get()->GetName());
10658 }
10659 if (pConstr->find(".x") &&
10660 strcmp(p->GetName(), pConstr->find(".x")->get<RooAbsReal>()->GetName()) == 0) {
10661 // hybrid construction case,
10662 prefitVal = pConstr->find(".mean")->get<RooAbsReal>()->getVal();
10663 if (fr->constPars().find(pConstr->find(".mean")->get()->GetName())) {
10664 // globs was saved to fr, use that instead of current value
10665 prefitVal = fr->constPars().getRealValue(pConstr->find(".mean")->get()->GetName());
10666 }
10667 }
10668 }
10669
10670 if (customScale)
10672 if (prefitError == 0) {
10673 Warning("Draw", "failed to determine prefit error of %s, using post-fit error", p->GetName());
10674 prefitError = _v->getError();
10675 }
10676 out->SetPoint(out->GetN(), out->GetN(), (_v->getVal() - prefitVal) / prefitError);
10677 out->SetPointError(out->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
10678 (_v->getErrorHi()) / prefitError);
10679 graphLabels.push_back(p->GetName());
10680 scale[p->GetName()] = prefitError;
10681 offset[p->GetName()] = prefitVal;
10682 } else if (!fParent) {
10683 // no parent to determine constraints from ... prefitError=0 will be the unconstrained ones
10684 if (customScale)
10686 if (prefitError == 0) {
10687 // uses range of var
10688 prefitError = (std::max({_v->getMax() - _v->getVal(), _v->getVal() - _v->getMin(), 4.}) / 4);
10689 ugraph->SetPoint(ugraph->GetN(), ugraph->GetN(), (_v->getVal() - prefitVal) / prefitError);
10690 ugraph->SetPointError(ugraph->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
10691 (_v->getErrorHi()) / prefitError);
10692 ugraphLabels.push_back(p->GetName());
10693 } else {
10694 out->SetPoint(out->GetN(), out->GetN(), (_v->getVal() - prefitVal) / prefitError);
10695 out->SetPointError(out->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
10696 (_v->getErrorHi()) / prefitError);
10697 graphLabels.push_back(p->GetName());
10698 }
10699 scale[p->GetName()] = prefitError;
10700 offset[p->GetName()] = prefitVal;
10701
10702 } else {
10703 // unconstrained (or at least couldn't determine constraint) ... use par range if no prefit error
10704 if (customScale)
10706 if (prefitError == 0) {
10707 prefitError = (std::max({_v->getMax() - _v->getVal(), _v->getVal() - _v->getMin(), 4.}) / 4);
10708 }
10709 ugraph->SetPoint(ugraph->GetN(), ugraph->GetN(), (_v->getVal() - prefitVal) / prefitError);
10710 ugraph->SetPointError(ugraph->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
10711 (_v->getErrorHi()) / prefitError);
10712 ugraphLabels.push_back(p->GetName());
10713 scale[p->GetName()] = prefitError;
10714 offset[p->GetName()] = prefitVal;
10715 }
10716 }
10717 auto graph = out;
10718
10719 // append ugraph points to end of graph
10720 for (int i = 0; i < ugraph->GetN(); i++)
10721 ugraph->SetPointX(i, i + graph->GetN());
10722 int nUnconstrained = ugraph->GetN();
10723 TList tmpList;
10724 tmpList.SetName("tmpList");
10725 tmpList.Add(ugraph);
10726 graph->Merge(&tmpList);
10727 tmpList.RemoveAll();
10728 delete ugraph;
10729 for (auto &l : ugraphLabels) {
10730 graphLabels.push_back(l);
10731 }
10732
10733 graph->SetBit(kCanDelete);
10734 graph->SetMarkerStyle(20);
10735 graph->SetMarkerSize(0.5);
10736
10737 graph->SetMaximum(4);
10738 graph->SetMinimum(-4);
10739
10740 bool doHorizontal =
10741 (!sOpt.Contains("impact") && sOpt.Contains("v")) || (sOpt.Contains("impact") && !sOpt.Contains("himpact"));
10742
10743 std::vector<std::pair<double, std::string>> covariances;
10744 /*double poiError = 0;*/ std::string poiName;
10745 double maxImpact = 0;
10746 if (sOpt.Contains("impact")) {
10747 if (sOpt.Contains("impact:")) {
10748 TString sOpt3(opt);
10749 poiName = sOpt3(sOpt3.Index("impact:") + 7, sOpt3.Length());
10750 } else {
10751 std::unique_ptr<RooAbsCollection> _poi(fr->floatParsFinal().selectByAttrib("poi", true));
10752 if (_poi->empty()) {
10753 throw std::runtime_error("No floating poi in the fit");
10754 } else if (_poi->size() != 1) {
10755 throw std::runtime_error("Multiple poi in the fit");
10756 }
10757 poiName = _poi->first()->GetName();
10758 }
10759 RooAbsArg *poi = fr->floatParsFinal().find(poiName.c_str());
10760 if (!poi) {
10761 throw std::runtime_error(TString::Format("Cannot find parameter %s", poiName.c_str()));
10762 }
10763 size_t poiIdx = fr->floatParsFinal().index(*poi);
10764 // put parameters in order of impact on the poi
10765
10766 // impact is regression coefficient * npError
10767 // relevant regression coefficient is cov / (npVariance)
10768 // i.e. DeltaX/sigmaX = [cov(X,Y)/(sigmaXsigmaY)]DeltaY/sigmaY
10769 // ... DeltaX = [cov(X,Y)/(sigmaY^2)]DeltaY
10770 // if DeltaY is just sigmaY then DeltaX = cov(X,Y)/sigmaY
10771
10772 for (auto &label : graphLabels) {
10773 covariances.emplace_back(fr->covarianceMatrix()(poiIdx, fr->floatParsFinal().index(label)) /
10774 dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(label))->getError(),
10775 label);
10776 }
10777 std::sort(covariances.begin(), covariances.end(),
10778 [&](std::pair<double, std::string> i, std::pair<double, std::string> j) {
10779 return doHorizontal ? (std::abs(i.first) < std::abs(j.first))
10780 : (std::abs(i.first) > std::abs(j.first));
10781 });
10782
10784 std::vector<TString> sortedLabels;
10785 maxImpact = (doHorizontal) ? covariances.back().first
10786 : covariances.front().first; // note: max impact is likely to be self variance
10787 for (auto &c : covariances) {
10788 if (c.second == poi->GetName()) {
10789 // poiError = sqrt(c.first);
10790 continue; // skip self
10791 }
10792 c.first *= 4. / (maxImpact * 1.2);
10793 sortedLabels.push_back(c.second);
10794 size_t i = 0;
10795 for (; i < graphLabels.size(); i++) {
10796 if (graphLabels[i] == c.second) {
10797 break;
10798 }
10799 }
10800 sortedGraph.AddPoint(sortedGraph.GetN(), graph->GetPointY(i));
10801 sortedGraph.SetPointError(sortedGraph.GetN() - 1, 0, 0, graph->GetErrorYlow(i), graph->GetErrorYhigh(i));
10802 }
10803 graph->Set(0);
10805 tmpList2.SetName("tmpList");
10806 tmpList2.Add(&sortedGraph);
10807 graph->Merge(&tmpList2);
10808 tmpList2.RemoveAll();
10810 graph->SetTitle("Fit Result Impact");
10811 }
10812
10813 // create a framing histogram
10814 TH2D *hist;
10815 if (doHorizontal) {
10816 hist = new TH2D(GetName(), fr->GetTitle(), 100, -4, 4, std::max(graph->GetN(), 1), -0.5,
10817 std::max(graph->GetN(), 1) - 0.5);
10818 int i = 1;
10819 for (auto &l : graphLabels) {
10820 hist->GetYaxis()->SetBinLabel(i++, l);
10821 }
10822 if (!graphLabels.empty())
10823 hist->GetYaxis()->LabelsOption("v");
10824 hist->GetXaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
10825 } else {
10826 hist = new TH2D(GetName(), fr->GetTitle(), std::max(graph->GetN(), 1), -0.5, std::max(graph->GetN(), 1) - 0.5,
10827 100, -4, 4);
10828 int i = 1;
10829 for (auto &l : graphLabels) {
10830 hist->GetXaxis()->SetBinLabel(i++, l);
10831 }
10832 if (!graphLabels.empty())
10833 hist->GetXaxis()->LabelsOption("v");
10834 hist->GetYaxis()->SetNdivisions(8, 0, 0);
10835 hist->GetYaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
10836 }
10837 hist->SetStats(false);
10838 hist->SetDirectory(nullptr);
10839 hist->SetBit(kCanDelete);
10840 auto histCopy = dynamic_cast<TH1 *>(hist->Clone(".axis"));
10841 histCopy->SetDirectory(nullptr);
10842 histCopy->SetBit(kCanDelete);
10843 auto _axis = (doHorizontal ? histCopy->GetYaxis() : histCopy->GetXaxis());
10844
10845 /*
10846 auto t = TH1::AddDirectoryStatus();
10847 TH1::AddDirectory(false);
10848 auto hist = new TH1F(TString::Format(".%s_pullFrame", GetName()), fr->GetTitle(), std::max(graph->GetN(),
10849 1), -0.5, std::max(graph->GetN(), 1) - 0.5); hist->SetStats(false); TH1::AddDirectory(t);
10850 hist->SetBit(kCanDelete);
10851 */
10852 // auto hist = graph->GetHistogram();
10853 graph->GetHistogram()->GetXaxis()->Set(std::max(graph->GetN(), 1), -0.5, std::max(graph->GetN(), 1) - 0.5);
10854 for (int ii = 1; ii <= _axis->GetNbins(); ii++) {
10855 graph->GetHistogram()->GetXaxis()->SetBinLabel(ii, _axis->GetBinLabel(ii));
10856 }
10857 // int i = 1;
10858 // for (auto &l : graphLabels) {
10859 // hist->GetXaxis()->SetBinLabel(i++, l);
10860 // }
10861 // hist->SetMaximum(4);
10862 // hist->SetMinimum(-4);
10863 // if (graph->GetN())
10864 // hist->GetXaxis()->LabelsOption("v");
10865 // hist->GetYaxis()->SetNdivisions(8, 0, 0);
10866 // hist->GetYaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
10867 clearPad();
10868 // create a new pad because adjust the margins ...
10869 auto oldPad = gPad;
10870 gPad->Divide(1, 1, 1e-9, 1e-9);
10871 gPad->cd(1);
10872
10873 if (doHorizontal) {
10874 gPad->SetLeftMargin(0.4);
10875 } else {
10876 gPad->SetBottomMargin(0.4);
10877 }
10878
10879 auto pNamesHist = dynamic_cast<TH1F *>(graph->GetHistogram()->Clone("scales")); // used by interactive "pull" plot
10880 pNamesHist->Sumw2();
10881 pNamesHist->SetDirectory(nullptr);
10882
10883 for (int ii = 1; ii <= graph->GetN(); ii++) { // use graph->GetN() to protect against the 0 pars case
10884 auto _p = fr->floatParsFinal().find(_axis->GetBinLabel(ii));
10885 pNamesHist->SetBinContent(ii, offset[_p->GetName()]);
10886 pNamesHist->SetBinError(ii, scale[_p->GetName()]);
10887 _axis->SetBinLabel(ii, strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName());
10888 }
10889
10890 // hist->Draw(); -- now just draw the graph
10891
10892 if (!sOpt.Contains("impact")) {
10893 for (int ii = 2; ii >= 1; ii--) {
10894 auto pullBox = new TGraphErrors;
10895 pullBox->SetName(TString::Format("%dsigmaBand", ii));
10896 pullBox->SetBit(kCanDelete);
10897 pullBox->SetPoint(0, (doHorizontal) ? -ii : -0.5, (doHorizontal) ? -0.5 : 0);
10898 pullBox->SetPoint(1, (doHorizontal) ? ii : (_axis->GetNbins() - 0.5 - nUnconstrained),
10899 (doHorizontal) ? -0.5 : 0);
10900 pullBox->SetPointError(0, 0, (doHorizontal) ? (_axis->GetNbins() - nUnconstrained) : ii);
10901 pullBox->SetPointError(1, 0, (doHorizontal) ? (_axis->GetNbins() - nUnconstrained) : ii);
10902 pullBox->SetFillColor((ii == 2) ? kYellow : kGreen);
10903 hist->GetListOfFunctions()->Add(pullBox, "3"); // pullBox->Draw("3");
10904 }
10905 auto pullLine = new TGraph;
10906 pullLine->SetName("0sigmaLine");
10907 pullLine->SetBit(kCanDelete);
10908 pullLine->SetPoint(0, -0.5, 0);
10909 pullLine->SetPoint(1, _axis->GetNbins() - 0.5, 0);
10910 pullLine->SetLineStyle(2);
10911 pullLine->SetEditable(false);
10912 hist->GetListOfFunctions()->Add(pullLine, "l"); // pullLine->Draw("l");
10913
10914 // also draw vertical line separating constrained from unconstrained, if necessary
10915 if (nUnconstrained > 0) {
10916 pullLine = new TGraph;
10917 pullLine->SetName("dividerLine");
10918 pullLine->SetBit(kCanDelete);
10919 pullLine->SetPoint(0, graph->GetN() - 0.5 - nUnconstrained, -100);
10920 pullLine->SetPoint(1, graph->GetN() - 0.5 - nUnconstrained, 100);
10921 pullLine->SetLineStyle(2);
10922 pullLine->SetEditable(false);
10923 hist->GetListOfFunctions()->Add(pullLine, "l"); // pullLine->Draw("l");
10924 }
10925
10926 // and draw a pave with fr status info
10927 TPaveText *pave =
10928 new TPaveText(gPad->GetLeftMargin(), 1. - gPad->GetTopMargin(), 1. - gPad->GetRightMargin(), 0.98, "NDCNB");
10929 pave->SetFillStyle(0);
10930 pave->SetBorderSize(0);
10931 pave->SetMargin(0.);
10932 pave->SetName("status");
10933 pave->SetTextAlign(31);
10934 pave->AddText(TString::Format("minNLL: %g edm: %g", fr->minNll(), fr->edm()))
10935 ->SetTextColor((fr->status() == 3) ? kRed : kBlack);
10936 std::string covQualTxt;
10937 switch (fr->covQual()) {
10938 case -1: covQualTxt = "Unknown"; break;
10939 case 0: covQualTxt = "Not calculated"; break;
10940 case 1: covQualTxt = "Approximate"; break;
10941 case 2: covQualTxt = "Forced Positive-Definite"; break;
10942 case 3: covQualTxt = "Accurate"; break;
10943 }
10944 pave->AddText(TString::Format("Cov. Quality: %d (%s)", fr->covQual(), covQualTxt.c_str()))
10945 ->SetTextColor((fr->covQual() == 3) ? kBlack : kRed);
10946
10947 std::string statusCodes;
10948 for (unsigned int i = 0; i < fr->numStatusHistory(); i++) {
10949 statusCodes += TString::Format(" %s = %d", fr->statusLabelHistory(i), fr->statusCodeHistory(i));
10950 }
10951 pave->AddText(statusCodes.c_str())->SetTextColor(fr->status() == 0 ? kBlack : kRed);
10952
10953 hist->GetListOfFunctions()->Add(pave);
10954
10955 } else {
10956 gPad->SetTicks(0, 0); // ensure mirrored ticks aren't drawn in this pad
10957
10958 if (doHorizontal) {
10959 // ensure canvas height big enough
10960 if (int(gPad->GetCanvas()->GetWh()) < pNamesHist->GetNbinsX() * 15) {
10961 gPad->GetCanvas()->SetCanvasSize(gPad->GetCanvas()->GetWw(), pNamesHist->GetNbinsX() * 15);
10962 }
10963 }
10964
10965 double factor = 475. / gPad->GetCanvas()->GetWh(); // Wh is the full canvas height, not window height
10966 gPad->SetTopMargin(gStyle->GetPadTopMargin() * factor); // fixed margin height
10967 gPad->SetBottomMargin(gStyle->GetPadBottomMargin() * factor); // fixed margin height
10968
10969 TGaxis *axis =
10970 new TGaxis(_axis->GetXmin(), -4, _axis->GetXmin(), 4, -1.2 * maxImpact, 1.2 * maxImpact, 510, "-S");
10971
10972 if (doHorizontal) {
10973 // _axis->SetLabelSize(
10974 // (_axis->GetLabelFont() % 10 > 2)
10975 // ? (20 / factor)
10976 // : ((gPad->AbsPixeltoY(0) - gPad->AbsPixeltoY(20 / factor)) / (gPad->GetY2() -
10977 // gPad->GetY1())));
10978 // histCopy->GetXaxis()->SetTickLength(histCopy->GetXaxis()->GetTickLength() * factor);
10979 // hist->GetXaxis()->SetTickLength(hist->GetXaxis()->GetTickLength() * factor);
10980 // histCopy->GetYaxis()->SetTickLength(histCopy->GetYaxis()->GetTickLength() * factor);
10981 // hist->GetYaxis()->SetTickLength(hist->GetYaxis()->GetTickLength() * factor);
10982 // histCopy->GetXaxis()->SetTitleOffset(histCopy->GetXaxis()->GetTitleOffset() * factor);
10983 // histCopy->GetXaxis()->SetLabelOffset(histCopy->GetXaxis()->GetLabelOffset() * factor);
10984 // hist->GetXaxis()->SetTitleOffset(hist->GetXaxis()->GetTitleOffset() * factor);
10985 // hist->GetXaxis()->SetLabelOffset(hist->GetXaxis()->GetLabelOffset() * factor);
10986 // histCopy->GetXaxis()->SetTitleOffset(histCopy->GetXaxis()->GetTitleOffset() * factor);
10987 // histCopy->GetXaxis()->SetLabelOffset(histCopy->GetXaxis()->GetLabelOffset() * factor);
10988 }
10989 // copy attributes from TAxis to TGaxis
10990 axis->ImportAxisAttributes((doHorizontal) ? histCopy->GetXaxis() : histCopy->GetYaxis());
10991 axis->SetTitle(TString::Format("#Delta %s", fr->floatParsFinal().find(poiName.c_str())->GetTitle()));
10992
10993 // create impact bar charts
10994 for (int tt = 0; tt < 2; tt++) {
10995 auto impact = static_cast<TH1 *>(
10996 graph->GetHistogram()->Clone(TString::Format("%s_impact+", tt == 0 ? "prefit" : "postfit")));
10997 impact->SetDirectory(nullptr);
10998 impact->GetYaxis()->SetTitle(TString::Format("#Delta%s/#sigma", poiName.c_str()));
10999 impact->SetBarWidth(0.9);
11000 impact->SetBarOffset(0.05);
11001 impact->SetLineColor(kBlack);
11002 impact->SetFillColor(kAzure - 4);
11003 impact->SetFillStyle(tt == 0 ? 3013 : 1001);
11004 auto impact2 =
11005 static_cast<TH1 *>(impact->Clone(TString::Format("%s_impact-", tt == 0 ? "prefit" : "postfit")));
11006 impact2->SetDirectory(nullptr);
11007 impact2->SetFillColor(kCyan);
11008 for (int ii = 1; ii <= pNamesHist->GetNbinsX(); ii++) {
11009 for (auto &c : covariances) {
11010 if (c.second != pNamesHist->GetXaxis()->GetBinLabel(ii))
11011 continue;
11012 auto vv = dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(c.second.c_str()));
11013 auto vv_init = dynamic_cast<RooRealVar *>(fr->floatParsInit().find(c.second.c_str()));
11014 impact->SetBinContent(ii, ((tt == 0 && !vv_init->hasError()) || !vv->hasError())
11015 ? 0.
11016 : c.first * vv->getError() / vv->getErrorHi() *
11017 (tt == 0 ? (vv_init->getErrorHi() / vv->getErrorHi()) : 1.));
11018 impact2->SetBinContent(ii, ((tt == 0 && !vv_init->hasError()) || !vv->hasError())
11019 ? 0.
11020 : c.first * vv->getError() / vv->getErrorLo() *
11021 (tt == 0 ? (vv_init->getErrorLo() / vv->getErrorLo()) : 1.));
11022 }
11023 }
11024 hist->GetListOfFunctions()->Add(impact, (doHorizontal) ? "hbarsamemin0" : "bsamey+");
11025 hist->GetListOfFunctions()->Add(impact2, (doHorizontal) ? "hbarsamemin0" : "bsamey+");
11026 }
11027 // add three lines
11028 for (int ii = -1; ii <= 1; ii++) {
11029 auto pullLine = new TGraph;
11030 pullLine->SetName(TString::Format("%dsigmaLine", ii));
11031 pullLine->SetBit(kCanDelete);
11032 pullLine->SetPoint(0, -0.5, ii);
11033 pullLine->SetPoint(1, hist->GetNbinsY() - 0.5, ii);
11034 pullLine->SetLineStyle(2);
11035 pullLine->SetEditable(false);
11036 hist->GetListOfFunctions()->Add(pullLine, "l");
11037 }
11038 hist->GetListOfFunctions()->Add(axis); // draw axis last
11039 TLegend *leg1 =
11040 new TLegend(0.02, doHorizontal ? (1. - 0.22 * factor) : 0.02, 0.27, (doHorizontal ? 1. : 0.24));
11041 leg1->SetFillStyle(0);
11042 leg1->SetBorderSize(0);
11043 leg1->SetMargin(0.25);
11044 leg1->SetNColumns(2);
11045
11046 leg1->SetTextSize(_axis->GetLabelSize());
11047 leg1->SetTextFont(_axis->GetLabelFont());
11048 leg1->AddEntry((TObject *)nullptr, "Hessian Pre-fit", "");
11049 leg1->AddEntry((TObject *)nullptr, "Impact:", "");
11050 leg1->AddEntry(hist->FindObject("prefit_impact+"), "#theta = #hat{#theta}+#Delta#theta", "f");
11051 leg1->AddEntry(hist->FindObject("prefit_impact-"), "#theta = #hat{#theta}-#Delta#theta", "f");
11052 leg1->AddEntry((TObject *)nullptr, "Hessian Post-fit", "");
11053 leg1->AddEntry((TObject *)nullptr, "Impact:", "");
11054 leg1->AddEntry(hist->FindObject("postfit_impact+"), "#theta = #hat{#theta}+#Delta#theta", "f");
11055 leg1->AddEntry(hist->FindObject("postfit_impact-"), "#theta = #hat{#theta}-#Delta#theta", "f");
11056
11057 hist->GetListOfFunctions()->Add(leg1);
11058 if (gStyle->GetOptTitle()) {
11059 histCopy->SetBit(TH1::kNoTitle);
11060 TPaveText *title =
11061 new TPaveText(gPad->GetLeftMargin(), 1. - gPad->AbsPixeltoY(14), 1. - gPad->GetRightMargin(), 1., "NDC");
11062 title->ConvertNDCtoPad();
11063 title->SetY1NDC(1. - gPad->GetTopMargin() * 0.6);
11064 title->SetY2NDC(1);
11065 title->SetTextSize(
11066 (title->GetTextFont() % 10 > 2)
11067 ? (14 / factor)
11068 : ((gPad->AbsPixeltoY(0) - gPad->AbsPixeltoY(10 / factor)) / (gPad->GetY2() - gPad->GetY1())));
11069 title->SetFillStyle(0);
11070 title->SetBorderSize(0);
11071 title->AddText(histCopy->GetTitle());
11072 hist->GetListOfFunctions()->Add(title);
11073 }
11074 }
11075
11076 graph->SetEditable(false);
11077 pNamesHist->SetLineWidth(0);
11078 pNamesHist->SetMarkerSize(0);
11079 pNamesHist->SetMarkerStyle(0);
11080 graph->GetListOfFunctions()->Add(pNamesHist, "same"); // graph->SetHistogram(pNamesHist);
11081 if (doHorizontal) {
11082
11083 // flip the graph and contained graphs
11084 for (int p = 0; p < graph->GetN(); p++) {
11085 graph->SetPoint(p, graph->GetPointY(p), graph->GetPointX(p));
11086 graph->SetPointError(p, graph->GetErrorYlow(p), graph->GetErrorYhigh(p), graph->GetErrorXlow(p),
11087 graph->GetErrorXhigh(p));
11088 }
11089 for (auto f : *hist->GetListOfFunctions()) {
11090 if (f->InheritsFrom("TH1")) {
11091 // f->Draw("hbarsamemin0");
11092 } /*else if (auto g2 = dynamic_cast<TGraphErrors *>(f)) {
11093 for (int p = 0; p < g2->GetN(); p++) {
11094 g2->SetPoint(p, g2->GetPointY(p), g2->GetPointX(p));
11095 g2->SetPointError(p, g2->GetErrorY(p), _axis->GetNbins());
11096 }
11097 //g2->Draw("3");
11098 } */
11099 else if (auto g = dynamic_cast<TGraph *>(f)) {
11100 for (int p = 0; p < g->GetN(); p++) {
11101 g->SetPoint(p, g->GetPointY(p), g->GetPointX(p));
11102 }
11103 // g->Draw("l");
11104 } else if (auto l = dynamic_cast<TLine *>(f)) {
11105 l->SetX1(l->GetY1());
11106 l->SetX2(l->GetY2());
11107 l->SetY1(_axis->GetXmax());
11108 l->SetY2(_axis->GetXmax());
11109 // l->Draw();
11110 }
11111 }
11112 }
11113
11114 if (!sOpt.Contains("impact")) {
11115 // add labels to graph for unconstrained parameters
11116 for (size_t i = 0; i < ugraphLabels.size(); i++) {
11117 int bin = pNamesHist->GetNbinsX() - ugraphLabels.size() + i + 1;
11118 auto p = dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(pNamesHist->GetXaxis()->GetBinLabel(bin)));
11119 if (!p)
11120 continue;
11121 auto x = graph->GetPointX(graph->GetN() - ugraphLabels.size() + i);
11122 auto y = graph->GetPointY(graph->GetN() - ugraphLabels.size() + i) +
11123 graph->GetErrorYhigh(graph->GetN() - ugraphLabels.size() + i);
11124 auto l = xRooFit::matchPrecision({p->getVal(), p->getError()});
11125 auto t = new TLatex(x, y, TString::Format("%g #pm %g", l.first, l.second));
11126 t->SetBit(kCanDelete);
11127 t->SetTextSize(0.025);
11128 t->SetTextAngle(90);
11129 graph->GetListOfFunctions()->Add(t);
11130 }
11131 }
11132
11133 graph->SetName("pulls");
11134 hist->GetListOfFunctions()->Add(graph, "z0p");
11135 // hist->GetListOfFunctions()->Add(histCopy->Clone(".axis"),(sOpt.Contains("impact") &&
11136 // !doHorizontal)?"axissamey+":"axissame"); // doesn't display right when zoom the axis
11137 if (!hasSame) {
11138 histCopy->Draw((sOpt.Contains("impact") && !doHorizontal)
11139 ? "axisy+"
11140 : "axis"); // draws the axis, called ".axis" for easy access
11141 }
11142 hist->Draw("same");
11143 //
11144 // if(sOpt.Contains("impact")) {
11145 // // make main object the histogram
11146 // auto h = (TH1*)graph->GetHistogram()->Clone("impact");
11147 // graph->GetListOfFunctions()->RemoveAll();
11148 // for(int ii=1;ii<=h->GetNbinsX();ii++) h->SetBinContent(ii,-4);
11149 // h->GetListOfFunctions()->Add(graph,"z0p");
11150 // h->Draw("hbar");
11151 // } else {
11152 // graph->Draw(sOpt.Contains("impact") ? "az0py+" : "az0p");
11153 // }
11154 auto hh = dynamic_cast<TH1 *>(histCopy->Clone(".axiscopy"));
11155 hh->SetDirectory(nullptr);
11156 hh->SetBit(kCanDelete);
11157 hh->Draw(
11158 (sOpt.Contains("impact") && !doHorizontal)
11159 ? "axissamey+"
11160 : "axissame"); // overlay axis again -- important is last so can remove if don't pad->Update before reclear
11161 gPad->Modified();
11162 oldPad->cd();
11163 // gPad->Update();
11164 return;
11165 }
11166
11167 if (get()->InheritsFrom("RooAbsData")) {
11168 auto s = parentPdf();
11169 if (s && s->get<RooSimultaneous>()) {
11170 // drawing dataset associated to a simultaneous means must find subpads with variation names
11171 // may not have subpads if drawning a "Yield" plot ...
11172 bool doneDraw = false;
11173 for (auto c : s->bins()) {
11174 auto _pad = dynamic_cast<TPad *>(gPad->GetPrimitive(c->GetName()));
11175 if (!_pad)
11176 continue; // channel was hidden?
11177 // attach as a child before calling datasets(), so that if this dataset is external to workspace it is
11178 // included still attaching the dataset ensures dataset reduction for the channel is applied
11179 c->push_back(std::make_shared<xRooNode>(*this));
11180 auto ds = c->datasets().find(GetName());
11181 c->resize(c->size() - 1); // remove the child we attached
11182 if (!ds) {
11183 std::cout << " no ds " << GetName() << " - this should never happen!" << std::endl;
11184 continue;
11185 }
11186 auto tmp = gPad;
11187 _pad->cd();
11188 ds->Draw(opt);
11189 doneDraw = true;
11190 tmp->cd();
11191 }
11192 if (doneDraw) {
11193 gPad->Modified();
11194 return;
11195 }
11196 }
11197
11198 if (!s && hasSame) {
11199 // draw onto all subpads with = in the name
11200 // if has no such subpads, draw onto this pad
11201 bool doneDraw = false;
11202 for (auto o : *gPad->GetListOfPrimitives()) {
11203 if (auto p = dynamic_cast<TPad *>(o); p && TString(p->GetName()).Contains('=')) {
11204 auto _tmp = gPad;
11205 p->cd();
11206 Draw(opt);
11207 _tmp->cd();
11208 doneDraw = true;
11209 }
11210 }
11211 if (doneDraw) {
11212 gPad->Modified();
11213 return;
11214 }
11215 }
11216
11217 auto dataGraph = BuildGraph(v, false, (!s && hasSame) ? gPad : nullptr);
11218 if (!dataGraph)
11219 return;
11220
11221 dataGraph->SetBit(kCanDelete); // will be be deleted when pad is cleared
11222 dataGraph->SetMarkerSize(dataGraph->GetMarkerSize() * gPad->GetWNDC()); // scale marker sizes to pad size
11223
11224 if (s && !s->get<RooAbsPdf>()->canBeExtended()) {
11225 // normalize dataGraph to 1
11226 double tot = 0;
11227 for (int i = 0; i < dataGraph->GetN(); i++)
11228 tot += dataGraph->GetPointY(i);
11229 dataGraph->Scale(1. / tot);
11230 }
11231
11232 if (!hasSame) {
11233 clearPad();
11234 dataGraph->Draw("Az0p");
11235 addLegendEntry(dataGraph, strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(), "pEX0");
11236 gPad->Modified();
11237 // gPad->Update();
11238 return;
11239 }
11240
11241 bool noPoint = false;
11242 if (v && dynamic_cast<RooAbsArg *>(v)->getAttribute("global") && dataGraph->GetN() == 1) {
11243 // global observable ... if graph has only 1 data point line it up on the histogram value
11244 for (auto o : *gPad->GetListOfPrimitives()) {
11245 if (auto h = dynamic_cast<TH1 *>(o);
11246 h && strcmp(h->GetXaxis()->GetName(), dynamic_cast<TObject *>(v)->GetName()) == 0) {
11247 dataGraph->SetPointY(0, h->Interpolate(dataGraph->GetPointX(0)));
11248 noPoint = true;
11249 break;
11250 }
11251 }
11252 }
11253
11254 if (auto _pad = dynamic_cast<TPad *>(gPad->FindObject("auxPad")); _pad) {
11255 if (auto h = dynamic_cast<TH1 *>(_pad->GetPrimitive("auxHist")); h) {
11256 TString histName = h->GetTitle(); // split it by | char
11257 TString histType = histName(histName.Index('|') + 1, histName.Length());
11258 histName = histName(0, histName.Index('|'));
11259 if (auto mainHist = dynamic_cast<TH1 *>(gPad->GetPrimitive(histName));
11260 mainHist && auxFunctions.find(h->GetYaxis()->GetTitle()) != auxFunctions.end()) {
11261 // decide what to do based on title of auxHist (previously used name of y-axis but that changed axis
11262 // behaviour) use title instead
11263 auto ratioGraph = dynamic_cast<TGraphAsymmErrors *>(dataGraph->Clone(dataGraph->GetName()));
11264 ratioGraph->SetBit(kCanDelete);
11265 for (int i = 0; i < ratioGraph->GetN(); i++) {
11266 double val = ratioGraph->GetPointY(i);
11267 int binNum = mainHist->FindFixBin(ratioGraph->GetPointX(i));
11268 double nom = mainHist->GetBinContent(binNum);
11269 double nomerr = mainHist->GetBinError(binNum);
11270 double yval =
11271 std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(ratioGraph->GetPointY(i), nom, nomerr);
11272 double yup = std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(val + ratioGraph->GetErrorYhigh(i),
11273 nom, nomerr) -
11274 yval;
11275 double ydown = yval - std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(
11276 val - ratioGraph->GetErrorYlow(i), nom, nomerr);
11277 if (!std::isnan(yval)) {
11278 ratioGraph->SetPointY(i, yval);
11279 if (!std::isnan(yup))
11280 ratioGraph->SetPointEYhigh(i, yup);
11281 if (!std::isnan(ydown))
11282 ratioGraph->SetPointEYlow(i, ydown);
11283 }
11284 }
11285 // remove the zero points
11286 int i = 0;
11287 while (i < ratioGraph->GetN()) {
11288 if (ratioGraph->GetPointY(i) == 0 && ratioGraph->GetErrorYhigh(i) == 0 &&
11289 ratioGraph->GetErrorYlow(i) == 0) {
11290 ratioGraph->RemovePoint(i);
11291 } else {
11292 i++;
11293 }
11294 }
11295 auto _tmpPad = gPad;
11296 _pad->cd();
11297 ratioGraph->Draw("z0psame");
11299 adjustYRange(minMax.first, minMax.second, h, std::get<1>(auxFunctions[h->GetYaxis()->GetTitle()]));
11300 _tmpPad->cd();
11301 }
11302 }
11303 }
11304
11305 dataGraph->Draw("z0p same");
11306 addLegendEntry((noPoint) ? nullptr : dataGraph, strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(),
11307 noPoint ? "" : "pEX0");
11308
11309 auto minMax = graphMinMax(dynamic_cast<TGraphAsymmErrors *>(dataGraph));
11310 adjustYRange(minMax.first, minMax.second);
11311
11312 gPad->Modified();
11313 // gPad->Update();
11314 return;
11315 }
11316
11317 // auto _ax = GetXaxis();
11318 // auto v = (_ax) ? dynamic_cast<RooRealVar*>(/*possibleObs.first()*/_ax->GetParent()) : nullptr;
11319 // if (!v) { v = get<RooRealVar>(); } // self-axis
11320 // if (!v) return;
11321
11322 if (auto lv = get<RooAbsLValue>(); lv && fParent && fParent->get<RooAbsData>()) {
11323 // drawing an observable from a dataset ... build graph, and exit
11324 auto gr = fParent->BuildGraph(lv, true);
11326 gr->Draw(hasSame ? "P" : "AP");
11327 return;
11328 }
11329
11330 if (forceNames != "") {
11331 // drawing a force plot ... build nll and fill a histogram with force terms
11332 auto _dsets = datasets();
11333 bool _drawn = false;
11334 auto _coords = coords();
11335 auto _fr = fitResult();
11336 auto initPar = dynamic_cast<RooRealVar *>(_fr.get<RooFitResult>()->floatParsInit().find(forceNames));
11337 if (!initPar)
11338 return;
11339 std::vector<double> valuesToDo = {initPar->getVal()};
11340 if (initPar->hasError() || initPar->hasAsymError()) {
11341 valuesToDo.push_back(initPar->getVal() + initPar->getErrorLo());
11342 valuesToDo.push_back(initPar->getVal() + initPar->getErrorHi());
11343 }
11344 int ii = 0;
11345 for (auto valueToDo : valuesToDo) {
11346 ii++;
11347 for (auto &d : _dsets) {
11348 if (!d->get()->TestBit(1 << 20))
11349 continue;
11350 auto emptyHist = BuildHistogram(v, true);
11351 emptyHist->SetBit(kCanDelete);
11352 auto _obs = d->obs();
11353 auto x = _obs.find((v) ? dynamic_cast<TObject *>(v)->GetName() : emptyHist->GetXaxis()->GetName());
11354 auto _nll = nll(d);
11355 auto theData = d->get<RooAbsData>();
11356 int nevent = theData->numEntries();
11357 for (int i = 0; i < nevent; i++) {
11358 theData->get(i);
11359 bool _skip = false;
11360 for (const auto &_c : _coords) {
11361 if (auto cat = _c->get<RooAbsCategoryLValue>(); cat) {
11362 if (cat->getIndex() != theData->get()->getCatIndex(cat->GetName())) {
11363 _skip = true;
11364 break;
11365 }
11366 }
11367 }
11368 if (_skip)
11369 continue;
11370
11371 if (x) {
11372 auto val = _nll.pars()->getRealValue(initPar->GetName());
11373 if (ii > 1)
11374 _nll.pars()->setRealValue(initPar->GetName(), valueToDo);
11375 auto nllVal = _nll.getEntryVal(i);
11376 _nll.pars()->setRealValue(initPar->GetName(), initPar->getVal());
11377 auto nllVal2 = _nll.getEntryVal(i);
11378 _nll.pars()->setRealValue(initPar->GetName(), val);
11379 emptyHist->Fill(x->get<RooAbsReal>()->getVal(), (nllVal2 - nllVal));
11380 }
11381 }
11382 // include the extendedTerm, distributed evenly over the bins
11383 // probably should be somehow dependent on data density though (i.e. bins with more data get more of it?)
11384 auto val = _nll.pars()->getRealValue(initPar->GetName());
11385 if (ii > 1)
11386 _nll.pars()->setRealValue(initPar->GetName(), valueToDo);
11387 auto _extTerm = _nll.extendedTermVal();
11388 _nll.pars()->setRealValue(initPar->GetName(), initPar->getVal());
11389 auto _extTerm2 = _nll.extendedTermVal();
11390 _nll.pars()->setRealValue(initPar->GetName(), val);
11391 for (int i = 1; i <= emptyHist->GetNbinsX(); i++) {
11392 emptyHist->SetBinContent(i,
11393 emptyHist->GetBinContent(i) + (_extTerm2 - _extTerm) / emptyHist->GetNbinsX());
11394 emptyHist->SetBinError(i, 0);
11395 }
11396 emptyHist->GetYaxis()->SetTitle("log (L(#theta)/L(#theta_{0}))");
11397 emptyHist->SetTitle(TString::Format("#theta = %g", (ii > 1) ? valueToDo : val));
11398 if (ii == 1)
11399 emptyHist->SetLineColor(kBlack);
11400 if (ii == 2) {
11401 emptyHist->SetLineColor(kRed);
11402 } else if (ii == 3) {
11403 emptyHist->SetLineColor(kBlue);
11404 }
11405 emptyHist->Draw(_drawn ? "same" : "");
11406 _drawn = true;
11407 }
11408 }
11409 return;
11410 }
11411
11412 auto rar = get<RooAbsReal>();
11413 const xRooNode *rarNode = this;
11414 if (!rar) {
11415 // draw a deleteable clone of the object we wrap (since we might own the object)
11416 get()->DrawClone(opt);
11417 return;
11418 }
11419 // RooAbsReal *sf = nullptr;
11420 if (get()->InheritsFrom("RooExtendPdf")) {
11421 browse();
11422 rarNode = find(".pdf").get();
11423 // rar = rarNode->get<RooAbsReal>();
11424 // sf = find(".n")->get<RooAbsReal>();
11425 }
11426
11427 if (!nostack && !hasOverlay &&
11428 (rarNode->get()->InheritsFrom("RooRealSumPdf") || rarNode->get()->InheritsFrom("RooAddPdf") ||
11429 (v && rarNode->get()->InheritsFrom("RooSimultaneous") &&
11430 strcmp(dynamic_cast<TObject *>(v)->GetName(), rarNode->get<RooSimultaneous>()->indexCat().GetName()) == 0))) {
11431 nostack = false;
11432 } else {
11433 // in all other cases, we do not build a stack
11434 nostack = true;
11435 }
11436
11437 auto h = BuildHistogram(v, false, hasErrorOpt, 1, 0, "", false, false, 0, nullptr, nostack, true /*setInterp*/);
11438 if (!h) {
11439 if (get()) {
11440 // draw a deleteable clone of the object we wrap (since we might own the object)
11441 get()->DrawClone(opt);
11442 }
11443 return;
11444 }
11445 h->SetBit(kCanDelete);
11446
11447 if (!v)
11448 v = getObject<RooAbsLValue>(h->GetXaxis()->IsAlphanumeric() ? h->GetXaxis()->GetTimeFormatOnly()
11449 : h->GetXaxis()->GetName())
11450 .get();
11451 RooAbsArg *vv = (v) ? dynamic_cast<RooAbsArg *>(v) : rar;
11452 if (h->GetXaxis()->IsAlphanumeric()) {
11453 // do this to get bin labels
11454 h->GetXaxis()->SetName("xaxis"); // WARNING -- this messes up anywhere we GetXaxis()->GetName()
11455 }
11456
11457 // get style now, before we mess with histogram title
11458 // auto _styleNode = styles(h);
11459
11460 if (rar->InheritsFrom("RooAbsPdf") && !(rar->InheritsFrom("RooRealSumPdf") || rar->InheritsFrom("RooAddPdf") ||
11461 rar->InheritsFrom("RooSimultaneous"))) {
11462 // append parameter values to title if has such
11463 RooArgSet s;
11464 rar->leafNodeServerList(&s);
11465 if (v)
11466 s.remove(*dynamic_cast<RooAbsArg *>(v));
11467 if (!s.empty()) {
11468 TString ss = h->GetTitle();
11469 ss += " [";
11470 bool first = true;
11471 for (auto _p : s) {
11472 auto _v = dynamic_cast<RooRealVar *>(_p);
11473 if (!_v)
11474 continue;
11475 if (!first)
11476 ss += ",";
11477 first = false;
11478 ss += TString::Format("%s=%g", strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName(), _v->getVal());
11479 if (_v->hasError()) {
11480 ss += TString::Format("#pm %g", _v->getError());
11481 }
11482 }
11483 ss += "]";
11484 h->SetTitle(ss);
11485 }
11486 }
11487
11488 if (!hasSame) {
11489 if (obs().find(vv->GetName())) {
11490 gPad->SetGrid(0, 0);
11491 } else {
11492 gPad->SetGrid(1, 1);
11493 }
11494 }
11495 TString dOpt = h->GetOption();
11496 if (dOpt == "l")
11497 h->SetFillStyle(0);
11498 // // need to strip namespace to discount the "HistFactory" namespace classes from all being treated as binned
11499 // TString clNameNoNamespace = rar->ClassName();
11500 // clNameNoNamespace = clNameNoNamespace(clNameNoNamespace.Last(':') + 1, clNameNoNamespace.Length());
11501 // TString dOpt = (clNameNoNamespace.Contains("Hist") || vv->isCategory() || rar->isBinnedDistribution(*vv) ||
11502 // h->GetNbinsX() == 1 || rar->getAttribute("BinnedLikelihood") ||
11503 // (dynamic_cast<RooAbsRealLValue *>(vv) &&
11504 // std::unique_ptr<std::list<double>>(rar->binBoundaries(*dynamic_cast<RooAbsRealLValue *>(vv),
11505 // -std::numeric_limits<double>::infinity(),
11506 // std::numeric_limits<double>::infinity()))))
11507 // ? ""
11508 // : "LF2";
11509 // if (auto d = dynamic_cast<RooHistFunc *>(rar); d && !d->isBinnedDistribution(*vv) && h->GetNbinsX() != 1) {
11510 // dOpt = "LF2"; // hist func is interpolated, so draw it as such
11511 // }
11512 // if (dOpt == "LF2" && !components().empty()) {
11513 // // check if all components of dOpt are "Hist" type (CMS model support)
11514 // // if so then dOpt="";
11515 // bool allHist = true;
11516 // for (auto &s : components()) {
11517 // TString _clName = s->get()->ClassName();
11518 // _clName = _clName(_clName.Last(':') + 1, _clName.Length());
11519 // if (!(s->get() && _clName.Contains("Hist"))) {
11520 // allHist = false;
11521 // break;
11522 // }
11523 // }
11524 // if (allHist)
11525 // dOpt = "";
11526 // }
11527 //
11528 // if(dOpt=="LF2") {
11529 // // ensure any sub hists have lf2 option
11530 // TObjLink *lnk = h->GetListOfFunctions()->FirstLink();
11531 // while (lnk) {
11532 // if(auto hh = dynamic_cast<TH1*>(lnk->GetObject())) {
11533 // if(TString(hh->GetName())=="band" && TString(lnk->GetOption())=="e2same") {
11534 // lnk->SetOption("LF2 e3same");
11535 // } else if(TString(hh->GetName())=="nominal") {
11536 // lnk->SetOption("L same");
11537 // }
11538 // }
11539 // lnk = lnk->Next();
11540 // }
11541 // }
11542
11543 if (rar == vv && rar->IsA() == RooRealVar::Class()) {
11544 dOpt += "TEXT";
11545 // add a TExec to the histogram so that when edited it will propagate to var
11546 gROOT->SetEditHistograms(true);
11547 } else {
11548 gROOT->SetEditHistograms(false);
11549 }
11550
11551 if (hasSame) {
11552 dOpt += " same";
11553 } else {
11554 hAxis = h;
11555 }
11556
11557 if (dOpt.Contains("TEXT") || sOpt.Contains("text")) {
11558 // adjust marker size so text is good
11559 h->SetMarkerSize(gStyle->GetLabelSize("Z") / (0.02 * gPad->GetHNDC()));
11560 }
11561
11562 bool hasError(false);
11563 for (int i = 0; i < h->GetSumw2N(); i++) {
11564 if (h->GetSumw2()->At(i)) {
11565 hasError = true;
11566 break;
11567 }
11568 }
11569
11570 /** This doesn't seem necessary in at least 6.26 any more - pads seem adjusted on their own
11571 if (!hasSame && h->GetYaxis()->GetTitleFont()%10 == 2) {
11572 h->GetYaxis()->SetTitleOffset( gPad->GetLeftMargin() / gStyle->GetPadLeftMargin() );
11573 } */
11574 // don't this instead - dont want to leave as zero (auto) in case show aux plot
11575 if (!hasSame && h->GetYaxis()->GetTitleFont() % 10 == 2) {
11576 h->GetYaxis()->SetTitleOffset(1.);
11577 }
11578
11579 TH1 *errHist = nullptr;
11580
11581 if (!hasSame)
11582 clearPad();
11583
11584 if (rar == vv && rar->IsA() == RooRealVar::Class()) {
11585 // add a TExec to the histogram so that when edited it will propagate to var
11586 // h->GetListOfFunctions()->Add(h->Clone("self"),"TEXTHIST");
11587 dOpt = "TEXT";
11588 auto node = new xRooNode(*this);
11589 auto _hist = (errHist) ? errHist : h;
11590 auto hCopy = (errHist) ? nullptr : dynamic_cast<TH1 *>(h->Clone());
11591 if (hCopy) {
11592 hCopy->Reset();
11593 hCopy->Add(_hist);
11594 hCopy->SetDirectory(nullptr);
11595 }
11596 _hist->GetListOfFunctions()->Add(node);
11597 _hist->GetListOfFunctions()->Add(new TExec(
11598 ".update",
11600 "gROOT->SetEditHistograms(true);auto h = dynamic_cast<TH1*>(gPad->GetPrimitive(\"%s\")); if(h) { double "
11601 "range= h->GetMaximum()-h->GetMinimum(); if(auto n "
11602 "= dynamic_cast<xRooNode*>(h->GetListOfFunctions()->FindObject(\"%s\")); n && "
11603 "n->TestBit(TObject::kNotDeleted) && n->get<RooRealVar>()->getVal() != h->GetBinContent(1)) {"
11604 "h->SetBinContent(1, "
11605 "TString::Format(\"%%.2g\",int(h->GetBinContent(1)/(range*0.01))*range*0.01).Atof());n->SetContent( "
11606 "h->GetBinContent(1) ); for(auto pp : *h->GetListOfFunctions()) if(auto hh = "
11607 "dynamic_cast<TH1*>(pp))hh->SetBinContent(1,h->GetBinContent(1));} if(h->GetBinContent(1)==0.) "
11608 "h->SetBinContent(1,range*0.005); gPad->Modified();gPad->Update(); }",
11609 _hist->GetName(), node->GetName())));
11610 if (errHist) {
11611 errHist->GetListOfFunctions()->Add(h, "TEXT HIST same");
11612 errHist->SetFillColor(h->GetLineColor());
11613 } else {
11614 hCopy->SetBit(kCanDelete);
11615 hCopy->SetFillStyle(0);
11616 _hist->GetListOfFunctions()->Add(hCopy, "TEXT HIST same");
11617 //_hist->SetBinError(1, 0);
11618 }
11619 _hist->SetStats(false);
11620 // if (_hist->GetBinContent(1)==0.) _hist->SetBinContent(1,(_hist->GetMaximum()-_hist->GetMinimum())*0.005);
11621 _hist->Draw(); //_hist->Draw(((hasError) ? "e2" : ""));
11622 gPad->Modified();
11623 return;
11624 }
11625
11626 bool overlayExisted = false;
11627 if (hasOverlay) {
11628 h->SetName(TString::Format("%s%s", h->GetName(), overlayName.Data()));
11629 if (auto existing = dynamic_cast<TH1 *>(gPad->GetPrimitive(h->GetName())); existing) {
11630 existing->Reset();
11631 existing->Add(h);
11632 delete h;
11633 h = existing;
11634 overlayExisted = true;
11635 } else {
11636 TString oldStyle = (rar && rar->getStringAttribute("style")) ? rar->getStringAttribute("style") : "";
11637 h->SetTitle(overlayName);
11638 // for overlays will take style from current gStyle before overriding with personal style
11639 // this ensures initial style will be whatever gStyle is, rather than whatever ours is
11640 static_cast<TAttLine &>(*h) = *gStyle;
11641 static_cast<TAttFill &>(*h) = *gStyle;
11642 static_cast<TAttMarker &>(*h) = *gStyle;
11643 h->SetFillStyle(0); // explicit default for overlays will be transparent fill
11644
11645 // std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case
11646 // getObject has decided to return the owning ptr (for some reason) if
11647 // (!gROOT->GetStyle(h->GetTitle())) {
11648 // if ( (style = getObject<TStyle>(h->GetTitle())) ) {
11649 // // loaded style (from workspace?) so put in list and use that
11650 // gROOT->GetListOfStyles()->Add(style.get());
11651 // } else {
11652 // // create new style - gets put in style list automatically so don't have to delete
11653 // // acquire them so saved to workspaces for auto reload ...
11654 // style = acquireNew<TStyle>(h->GetTitle(),
11655 // TString::Format("Style for %s component", h->GetTitle()));
11656 // (TAttLine &) (*style) = *dynamic_cast<TAttLine *>(h);
11657 // (TAttFill &) (*style) = *dynamic_cast<TAttFill *>(h);
11658 // (TAttMarker &) (*style) = *dynamic_cast<TAttMarker *>(h);
11659 // gROOT->GetListOfStyles()->Add(style.get());
11660 // }
11661 // }
11662 // (TAttLine&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
11663 // (TAttFill&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
11664 // (TAttMarker&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
11665 auto _styleNode = styles(h);
11666 rar->setStringAttribute("style", oldStyle == "" ? nullptr : oldStyle.Data()); // restores old style
11667 if (auto _style = _styleNode.get<TStyle>()) {
11668 (TAttLine &)(*h) = *_style;
11669 (TAttFill &)(*h) = *_style;
11670 (TAttMarker &)(*h) = *_style;
11671 }
11672 h->Draw(dOpt == "LF2" ? "e3" : dOpt);
11673 if (errHist) {
11674 errHist->SetTitle(overlayName);
11675 (TAttLine &)(*errHist) = *h;
11676 errHist->SetFillColor(h->GetLineColor());
11677 }
11678 }
11679 } else {
11680 // if (auto _style = _styleNode.get<TStyle>()) {
11681 // (TAttLine &)(*h) = *_style;
11682 // (TAttFill &)(*h) = *_style;
11683 // (TAttMarker &)(*h) = *_style;
11684 // if (errHist) {
11685 // (TAttLine &)(*errHist) = *h;
11686 // errHist->SetFillColor(h->GetLineColor());
11687 // }
11688 // }
11689 h->Draw(dOpt);
11690 }
11691
11692 if (!hasOverlay && (rarNode->get()->InheritsFrom("RooRealSumPdf") || rarNode->get()->InheritsFrom("RooAddPdf") ||
11693 (rarNode->get()->InheritsFrom("RooSimultaneous") &&
11694 strcmp(vv->GetName(), rarNode->get<RooSimultaneous>()->indexCat().GetName()) == 0))) {
11695 if (auto stack = dynamic_cast<THStack *>(h->FindObject("stack"))) {
11696 // access the stack and set draw options, adjust ranges etc
11697 TObjLink *lnk = stack->GetHists()->FirstLink();
11698 while (lnk) {
11699 TH1 *hh = static_cast<TH1 *>(lnk->GetObject());
11700 // lnk->SetOption(dOpt); - not needed
11701 auto hhMin = (hh->GetMinimum() == 0) ? hh->GetMinimum(1e-9) : hh->GetMinimum();
11702 if (lnk == stack->GetHists()->FirstLink() && h->GetMinimum() > hhMin) {
11703 auto newMin = hhMin - (h->GetMaximum() - hhMin) * gStyle->GetHistTopMargin();
11704 if (hhMin >= 0 && newMin < 0)
11705 newMin = hhMin * 0.99;
11706 adjustYRange(newMin, h->GetMaximum());
11707 }
11708 addLegendEntry(hh, hh->GetTitle(), "f");
11709 lnk = lnk->Next();
11710 }
11711 }
11712
11713 // // build a stack unless not requested
11714 // if (!nostack) {
11715 // THStack *stack = new THStack(TString::Format("%s_stack", rar->GetName()),
11716 // TString::Format("%s;%s", rar->GetTitle(), h->GetXaxis()->GetTitle()));
11717 // int count = 2;
11718 // std::map<std::string, int> colorByTitle; // TODO: should fill from any existing legend
11719 // std::set<std::string> allTitles;
11720 // bool titleMatchName = true;
11721 // std::map<std::string, TH1 *> histGroups;
11722 // std::vector<TH1 *> hhs;
11723 // std::set<TH1 *> histsWithBadTitles; // these histograms will have their titles autoFormatted
11724 //
11725 // // support for CMS model case where has single component containing many coeffs
11726 // // will build stack by setting each coeff equal to 0 in turn, rebuilding the histogram
11727 // // the difference from the "full" histogram will be the component
11728 // RooArgList cms_coefs;
11729 // if (!rarNode->components().empty()) {
11730 // auto comps = rarNode->components()[0];
11731 // for (auto &c : *comps) {
11732 // if (c->fFolder == "!.coeffs")
11733 // cms_coefs.add(*c->get<RooAbsArg>());
11734 // }
11735 // }
11736 // if (!cms_coefs.empty()) {
11737 // RooRealVar zero("zero", "", 0);
11738 // std::shared_ptr<TH1> prevHist(static_cast<TH1 *>(h->Clone()));
11739 // for (auto c : cms_coefs) {
11740 // // seems I have to remake the function each time, as haven't figured out what cache needs
11741 // clearing? std::unique_ptr<RooAbsReal> f(
11742 // dynamic_cast<RooAbsReal *>(rarNode->components()[0]->get()->Clone("tmpCopy")));
11743 // zero.setAttribute(
11744 // Form("ORIGNAME:%s", c->GetName())); // used in redirectServers to say what this
11745 // replaces
11746 // f->redirectServers(RooArgSet(zero), false, true); // each time will replace one additional coef
11747 // // zero.setAttribute(Form("ORIGNAME:%s",c->GetName()),false); (commented out so that on next
11748 // iteration
11749 // // will still replace all prev)
11750 // auto hh = xRooNode(*f, *this).BuildHistogram(v);
11751 // hh->SetName(c->GetName());
11752 // if (sf)
11753 // hh->Scale(sf->getVal());
11754 // if (strlen(hh->GetTitle()) == 0) {
11755 // hh->SetTitle(c->GetName()); // ensure all hists has titles
11756 // histsWithBadTitles.insert(hh);
11757 // } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
11758 // histsWithBadTitles.insert(hh);
11759 // }
11760 // titleMatchName &= (TString(c->GetName()) == hh->GetTitle() ||
11761 // TString(hh->GetTitle()).BeginsWith(TString(c->GetName()) + "_"));
11762 // std::shared_ptr<TH1> nextHist(static_cast<TH1 *>(hh->Clone()));
11763 // hh->Add(prevHist.get(), -1.);
11764 // hh->Scale(-1.);
11765 // hhs.push_back(hh);
11766 // prevHist = nextHist;
11767 // }
11768 // } else if (get<RooSimultaneous>()) {
11769 // // need to create a histogram for each sample across all the channels - will rely on functionality
11770 // below to
11771 // // merge them based on titles
11772 //
11773 // for (auto &chan : bins()) {
11774 // TString chanName(chan->GetName());
11775 // chanName = chanName(chanName.Index("=") + 1, chanName.Length());
11776 // auto samps = chan->mainChild();
11777 // if (!samps)
11778 // samps = *chan;
11779 // for (auto &samp : samps.components()) {
11780 // auto hh = static_cast<TH1 *>(h->Clone(samp->GetName()));
11781 // hh->Reset();
11782 // hh->SetTitle(samp->GetTitle());
11783 // if (strlen(hh->GetTitle()) == 0) {
11784 // hh->SetTitle(samp->GetName());
11785 // histsWithBadTitles.insert(hh);
11786 // } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
11787 // histsWithBadTitles.insert(hh);
11788 // }
11789 // hh->SetTitle(TString(hh->GetTitle())
11790 // .ReplaceAll(TString(chan->get()->GetName()) + "_",
11791 // "")); // remove occurance of channelname_ in title (usually
11792 // prefix)
11793 // titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
11794 // TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
11795 // hh->SetBinContent(hh->GetXaxis()->FindFixBin(chanName), samp->GetContent());
11796 // hhs.push_back(hh);
11797 // }
11798 // }
11799 // } else {
11800 // for (auto &samp : rarNode->components()) {
11801 // auto hh = samp->BuildHistogram(v,false,false,1,0,"",false,false,0,h); // passing h to ensure
11802 // binning is the same for all subcomponent hists if (sf)
11803 // hh->Scale(sf->getVal());
11804 // hhs.push_back(hh);
11805 // if (strlen(hh->GetTitle()) == 0) {
11806 // hh->SetTitle(samp->GetName()); // ensure all hists has titles
11807 // histsWithBadTitles.insert(hh);
11808 // } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
11809 // histsWithBadTitles.insert(hh);
11810 // }
11811 // titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
11812 // TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
11813 // }
11814 // }
11815 //
11816 // if (!hhs.empty()) {
11817 // for (auto &hh : hhs) {
11818 // allTitles.insert(hh->GetTitle());
11819 // }
11820 //
11821 // // get common prefix to strip off only if all titles match names and
11822 // // any title is longer than 10 chars
11823 // size_t e = std::min(allTitles.begin()->size(), allTitles.rbegin()->size());
11824 // size_t ii = 0;
11825 // bool goodPrefix = false;
11826 // std::string commonSuffix;
11827 // if (titleMatchName && hhs.size() > 1) {
11828 // while (ii < e - 1 && allTitles.begin()->at(ii) == allTitles.rbegin()->at(ii)) {
11829 // ii++;
11830 // if (allTitles.begin()->at(ii) == '_' || allTitles.begin()->at(ii) == ' ')
11831 // goodPrefix = true;
11832 // }
11833 //
11834 // // find common suffix if there is one .. must start with a "_"
11835 // bool stop = false;
11836 // while (!stop && commonSuffix.size() < size_t(e - 1)) {
11837 // commonSuffix = allTitles.begin()->substr(allTitles.begin()->length() - commonSuffix.length() -
11838 // 1); for (auto &t : allTitles) {
11839 // if (!TString(t).EndsWith(commonSuffix.c_str())) {
11840 // commonSuffix = commonSuffix.substr(1);
11841 // stop = true;
11842 // break;
11843 // }
11844 // }
11845 // }
11846 // if (commonSuffix.find('_') == std::string::npos) {
11847 // commonSuffix = "";
11848 // } else {
11849 // commonSuffix = commonSuffix.substr(commonSuffix.find('_'));
11850 // }
11851 // }
11852 // if (!goodPrefix)
11853 // ii = 0;
11854 //
11855 // // also find how many characters are needed to distinguish all entries (that dont have the same
11856 // name)
11857 // // then carry on up to first space or underscore
11858 // size_t jj = 0;
11859 // std::map<std::string, std::string> reducedTitles;
11860 // while (reducedTitles.size() != allTitles.size()) {
11861 // jj++;
11862 // std::map<std::string, int> titlesMap;
11863 // for (auto &s : allTitles) {
11864 // if (reducedTitles.count(s))
11865 // continue;
11866 // titlesMap[s.substr(0, jj)]++;
11867 // }
11868 // for (auto &s : allTitles) {
11869 // if (titlesMap[s.substr(0, jj)] == 1 && (jj >= s.length() || s.at(jj) == ' ' || s.at(jj) ==
11870 // '_')) {
11871 // reducedTitles[s] = s.substr(0, jj);
11872 // }
11873 // }
11874 // }
11875 //
11876 // // strip common prefix and suffix before adding
11877 // for (auto ritr = hhs.rbegin(); ritr != hhs.rend(); ++ritr) { // go in reverse order
11878 // if (!histsWithBadTitles.count((*ritr))) {
11879 // continue;
11880 // }
11881 // auto _title = (hhs.size() > 5) ? reducedTitles[(*ritr)->GetTitle()] : (*ritr)->GetTitle();
11882 // _title = _title.substr(ii < _title.size() ? ii : 0);
11883 // if (!commonSuffix.empty() && TString(_title).EndsWith(commonSuffix.c_str()))
11884 // _title = _title.substr(0, _title.length() - commonSuffix.length());
11885 // (*ritr)->SetTitle(_title.c_str());
11886 // }
11887 // }
11888 //
11889 // for (auto &hh : hhs) {
11890 // // automatically group hists that all have the same title
11891 // if (histGroups.find(hh->GetTitle()) == histGroups.end()) {
11892 // histGroups[hh->GetTitle()] = hh;
11893 // } else {
11894 // // add it into this group
11895 // histGroups[hh->GetTitle()]->Add(hh);
11896 // delete hh;
11897 // hh = nullptr;
11898 // continue;
11899 // }
11900 // auto hhMin = (hh->GetMinimum() == 0) ? hh->GetMinimum(1e-9) : hh->GetMinimum();
11901 // if (!stack->GetHists() && h->GetMinimum() > hhMin) {
11902 // auto newMin = hhMin - (h->GetMaximum() - hhMin) * gStyle->GetHistTopMargin();
11903 // if (hhMin >= 0 && newMin < 0)
11904 // newMin = hhMin * 0.99;
11905 // adjustYRange(newMin, h->GetMaximum());
11906 // }
11907 //
11908 // /*if(stack->GetHists() && stack->GetHists()->GetEntries()>0) {
11909 // // to remove rounding effects on bin boundaries, see if binnings compatible
11910 // auto _h1 = dynamic_cast<TH1*>(stack->GetHists()->At(0));
11911 // if(_h1->GetNbinsX()==hh->GetNbinsX()) TODO ... finish dealing with silly rounding effects
11912 // }*/
11913 // TString thisOpt = dOpt;
11914 // // uncomment next line to blend continuous with discrete components .. get some unpleasant "poke
11915 // through"
11916 // // effects though
11917 // // if(auto s = samp->get<RooAbsReal>(); s) thisOpt =
11918 // s->isBinnedDistribution(*dynamic_cast<RooAbsArg*>(v)) ?
11919 // // "" : "LF2";
11920 // stack->Add(hh, thisOpt);
11921 // }
11922 // stack->SetBit(kCanDelete); // should delete its sub histograms
11923 // stack->Draw("noclear same");
11924 // h->Draw(
11925 // dOpt + sOpt +
11926 // "same"); // overlay again .. if stack would cover original hist (negative components) we still see
11927 // integral
11928 // h->Draw("axissame"); // redraws axis
11929 //
11930 // TList *ll = stack->GetHists();
11931 // if (ll && ll->GetEntries()) {
11932 //
11933 // // finally, ensure all hists are styled
11934 // for (auto ho : *ll) {
11935 // TH1 *hh = dynamic_cast<TH1 *>(ho);
11936 // if (!hh)
11937 // continue;
11938 // bool createdStyle = (xRooNode(*hh, *this).styles(nullptr, false).get<TStyle>() == nullptr);
11939 //
11940 // if (createdStyle) {
11941 // // give hist a color, that isn't the same as any other hists color
11942 // hh->SetFillStyle(1001); // solid fill style
11943 // bool used = false;
11944 // do {
11945 // hh->SetFillColor((count++));
11946 // // check not already used this color
11947 // used = false;
11948 // for (auto ho2 : *ll) {
11949 // TH1 *hh2 = dynamic_cast<TH1 *>(ho2);
11950 // if (!hh2)
11951 // continue;
11952 // auto _styleNode = xRooNode(*hh2, *this).styles(hh2, false);
11953 // auto _style = _styleNode.get<TStyle>();
11954 // if (hh != hh2 && _style && _style->GetFillColor() == hh->GetFillColor()) {
11955 // used = true;
11956 // break;
11957 // }
11958 // }
11959 // } while (used);
11960 // }
11961 //
11962 // auto _styleNode = xRooNode(*hh, *this).styles(hh);
11963 // if (auto _style = _styleNode.get<TStyle>()) {
11964 // *dynamic_cast<TAttLine *>(hh) = *_style;
11965 // *dynamic_cast<TAttFill *>(hh) = *_style;
11966 // *dynamic_cast<TAttMarker *>(hh) = *_style;
11967 // }
11968 // // for stacks, fill color of white should be color 10 unless fill style is 0
11969 // if (hh->GetFillColor() == kWhite && hh->GetFillStyle() != 0) {
11970 // // kWhite means 'transparent' in ROOT ... should really use a FillStyle of 0 for that
11971 // // so assume user wanted actual white, which is color 10
11972 // hh->SetFillColor(10);
11973 // }
11974 // addLegendEntry(hh, hh->GetTitle(), "f");
11975 // }
11976 // }
11977 // }
11978 } else if (!overlayExisted) {
11979
11980 if (errHist) {
11981 addLegendEntry(errHist, strlen(errHist->GetTitle()) ? errHist->GetTitle() : GetName(), "fl");
11982 } else {
11983 addLegendEntry(h, strlen(h->GetTitle()) ? h->GetTitle() : GetName(), (hasError) ? "fl" : "l");
11984 }
11985 }
11986
11987 if (errHist) {
11988 dOpt.ReplaceAll("TEXT", "");
11989 errHist->Draw(dOpt + (dOpt.Contains("LF2") ? "e3same" : "e2same"));
11990 double ymax = -std::numeric_limits<double>::infinity();
11991 double ymin = std::numeric_limits<double>::infinity();
11992 for (int i = 1; i <= errHist->GetNbinsX(); i++) {
11993 ymax = std::max(ymax, errHist->GetBinContent(i) + errHist->GetBinError(i));
11994 ymin = std::min(ymin, errHist->GetBinContent(i) - errHist->GetBinError(i));
11995 }
11997 } else {
11998 adjustYRange(h->GetMinimum() * 0.9, h->GetMaximum() * 1.1);
11999 }
12000
12001 if ((!auxPlotTitle.empty()) && !hasSame) {
12002 // create a pad for the ratio ... shift the bottom margin of this pad to make space for it
12003 double padFrac = 0.3;
12004 auto _tmpPad = gPad;
12005 gPad->SetBottomMargin(padFrac);
12006 auto ratioPad = new TPad("auxPad", "aux plot", 0, 0, 1, padFrac);
12007 ratioPad->SetFillColor(_tmpPad->GetFillColor());
12008 ratioPad->SetNumber(1);
12009 ratioPad->SetBottomMargin(ratioPad->GetBottomMargin() / padFrac);
12010 ratioPad->SetTopMargin(0.04);
12011 ratioPad->SetLeftMargin(gPad->GetLeftMargin());
12012 ratioPad->SetRightMargin(gPad->GetRightMargin());
12013 ratioPad->cd();
12014 TH1 *ratioHist = dynamic_cast<TH1 *>((errHist) ? errHist->Clone("auxHist") : h->Clone("auxHist"));
12015 ratioHist->Reset();
12016 ratioHist->Add(h); // removes function list
12017 ratioHist->SetDirectory(nullptr);
12018 ratioHist->SetTitle((errHist) ? errHist->GetName()
12019 : h->GetName()); // abuse the title string to hold the name of the main hist
12020
12021 ratioHist->GetYaxis()->SetNdivisions(5, 0, 0);
12022 ratioHist->GetYaxis()->SetTitle(auxPlotTitle.c_str());
12023 ratioHist->SetTitle(
12024 TString::Format("%s|%s", ratioHist->GetTitle(),
12025 auxPlotTitle.c_str())); // used when plotting data (above) to decide what to calculate
12026 ratioHist->SetMaximum();
12027 ratioHist->SetMinimum(); // resets min and max
12028 ratioPad->SetGridy();
12029
12030 for (int i = 1; i <= ratioHist->GetNbinsX(); i++) {
12031 double val = ratioHist->GetBinContent(i);
12032 double err = ratioHist->GetBinError(i);
12033 ratioHist->SetBinContent(i, std::get<0>(auxFunctions[auxPlotTitle])(val, val, err));
12034 ratioHist->SetBinError(i, std::get<0>(auxFunctions[auxPlotTitle])(val + err, val, err) -
12035 ratioHist->GetBinContent(i));
12036 }
12037
12038 double rHeight = (1. - padFrac) / padFrac; //(_tmpPad->GetWNDC())/(gPad->GetHNDC());
12039 if (ratioHist->GetYaxis()->GetTitleFont() % 10 == 2) {
12040 ratioHist->GetYaxis()->SetTitleSize(ratioHist->GetYaxis()->GetTitleSize() * rHeight);
12041 ratioHist->GetYaxis()->SetLabelSize(ratioHist->GetYaxis()->GetLabelSize() * rHeight);
12042 ratioHist->GetXaxis()->SetTitleSize(ratioHist->GetXaxis()->GetTitleSize() * rHeight);
12043 ratioHist->GetXaxis()->SetLabelSize(ratioHist->GetXaxis()->GetLabelSize() * rHeight);
12044 ratioHist->GetYaxis()->SetTitleOffset(ratioHist->GetYaxis()->GetTitleOffset() / rHeight);
12045 } else {
12046#if ROOT_VERSION_CODE < ROOT_VERSION(6, 26, 00)
12047 ratioHist->GetYaxis()->SetTitleOffset(ratioHist->GetYaxis()->GetTitleOffset() / rHeight);
12048#endif
12049 }
12050 ratioHist->GetXaxis()->SetTickLength(ratioHist->GetXaxis()->GetTickLength() * rHeight);
12051 ratioHist->SetStats(false);
12052 ratioHist->SetBit(TH1::kNoTitle);
12053 ratioHist->SetBit(kCanDelete);
12054 if (errHist) {
12055 auto _h = dynamic_cast<TH1 *>(ratioHist->Clone("auxHist_clone"));
12056 _h->SetDirectory(nullptr);
12057 _h->SetFillColor(0);
12058 ratioHist->GetListOfFunctions()->Add(_h, "histsame");
12059 //_h->Draw("histsame");
12060 }
12061 ratioHist->GetListOfFunctions()->Add(new TExec(
12062 ".updateAxis",
12063 TString::Format("auto h1 = (TH1*)%p; auto h2 = (TH1*)%p; if(h2->GetXaxis()->GetFirst() != "
12064 "h1->GetXaxis()->GetFirst() || h1->GetXaxis()->GetLast()!=h2->GetXaxis()->GetLast()) "
12065 "{h2->GetXaxis()->SetRange(h1->GetXaxis()->GetFirst(),h1->GetXaxis()->GetLast());if(gPad) "
12066 "{gPad->GetCanvas()->Paint();gPad->GetCanvas()->Update();}}",
12067 (void *)ratioHist, (void *)(h))));
12068 ratioHist->Draw((errHist ? "e2" : ""));
12069
12070 _tmpPad->cd();
12071 ratioPad->Draw();
12072 } else if (auto ratioPad = dynamic_cast<TPad *>(gPad->GetPrimitive("auxPad")); hasSame && ratioPad) {
12073 // need to draw histogram in the ratio pad ...
12074 // if doing overlay need to update histogram
12075
12076 if (auto hr = dynamic_cast<TH1 *>(ratioPad->GetPrimitive("auxHist"));
12077 hr && auxFunctions.find(hr->GetYaxis()->GetTitle()) != auxFunctions.end()) {
12078 TString histName = hr->GetTitle(); // split it by | char
12079 TString histType = histName(histName.Index('|') + 1, histName.Length());
12080 histName = histName(0, histName.Index('|'));
12081
12082 if (auto hnom = dynamic_cast<TH1 *>(gPad->GetPrimitive(histName)); hnom) {
12083 h = dynamic_cast<TH1 *>(h->Clone(h->GetName()));
12084 h->SetDirectory(nullptr);
12085 h->SetBit(kCanDelete);
12086 for (int i = 1; i <= hnom->GetNbinsX(); i++) {
12087 double val = h->GetBinContent(i);
12088 double err = h->GetBinError(i);
12089 h->SetBinContent(i, std::get<0>(auxFunctions[hr->GetYaxis()->GetTitle()])(
12090 h->GetBinContent(i), hnom->GetBinContent(i), hnom->GetBinError(i)));
12091 h->SetBinError(i, std::get<0>(auxFunctions[hr->GetYaxis()->GetTitle()])(
12092 val + err, hnom->GetBinContent(i), hnom->GetBinError(i)) -
12093 h->GetBinContent(i));
12094 }
12095 auto _tmpPad = gPad;
12096 ratioPad->cd();
12097 if (hasOverlay) {
12098 if (auto existing = dynamic_cast<TH1 *>(ratioPad->GetPrimitive(h->GetName())); existing) {
12099 existing->Reset();
12100 existing->Add(h);
12101 delete h;
12102 h = existing;
12103 overlayExisted = true;
12104 } else {
12105 h->Draw(dOpt);
12106 }
12107 } else {
12108 h->Draw(dOpt);
12109 }
12110 double ymax = -std::numeric_limits<double>::infinity();
12111 double ymin = std::numeric_limits<double>::infinity();
12112 for (int i = 1; i <= h->GetNbinsX(); i++) {
12113 ymax = std::max(ymax, h->GetBinContent(i) + h->GetBinError(i));
12114 ymin = std::min(ymin, h->GetBinContent(i) - h->GetBinError(i));
12115 }
12116 adjustYRange(ymin, ymax, hr, std::get<1>(auxFunctions[hr->GetYaxis()->GetTitle()]));
12117 // adjustYRange(h->GetMinimum() * (h->GetMinimum()<0 ? 1.1 : 0.9), h->GetMaximum() * (h->GetMinimum()<0 ?
12118 // 0.9 : 1.1), hr, std::get<1>(auxFunctions[hr->GetYaxis()->GetTitle()]));
12119 gPad->Modified();
12120 _tmpPad->cd();
12121 }
12122 }
12123 }
12124
12125 // see if it's in a simultaneous so need to select a cat
12126 /*auto _parent = fParent;
12127 auto _me = rar;
12128 while(_parent) {
12129 if (auto s = _parent->get<RooSimultaneous>(); s) {
12130 for (auto c : s->indexCat()) {
12131 if (auto p = s->getPdf(c.first.c_str());_me==p) {
12132 gPad->SetName(c.first.c_str());
12133 break;
12134 }
12135 }
12136 break;
12137 }
12138 _me = _parent->get<RooAbsReal>();
12139 _parent = _parent->fParent;
12140 }*/
12141
12142 // now draw selected datasets on top if this was a pdf
12143 if (auto _pdf = get<RooAbsPdf>();
12144 !hasSame && _pdf /*&& (_pdf->canBeExtended() || robs().empty())*/ && coefs(true).empty()) {
12145 auto _dsets = datasets();
12146 // bool _drawn=false;
12147 for (auto &d : _dsets) {
12148 if (d->get()->TestBit(1 << 20)) {
12149 d->Draw("same");
12150 //_drawn=true;
12151 }
12152 }
12153 // if (!_drawn && !_dsets.empty()) _dsets[0]->Draw("same"); // always draw if has a dataset
12154 }
12155
12156 gPad->Modified();
12157 // gPad->Update();
12158 getLegend();
12159 gPad->Modified();
12160 // gPad->Update();
12161}
12162
12163void xRooNode::SaveAs(const char *filename, Option_t *option) const
12164{
12166 sOpt.ToLower();
12169 if (sFilename.Contains(".root:")) {
12170 objName = TString(sFilename(sFilename.Index(".root:") + 6, sFilename.Length()));
12171 sFilename = sFilename(0, sFilename.Index(".root:") + 5);
12172 }
12173
12174 if (auto pdf = get<RooAbsPdf>(); pdf) {
12175 // if saving a pdf, will put it inside a workspace, with its datasets, and a modelconfig
12176 // then save the workspace
12177 RooWorkspace w(objName, TString::Format("Workspace of %s", GetTitle()));
12178 xRooNode ws(w);
12179 auto addedPdf = ws.Add(*this);
12180 for (auto ds : datasets()) {
12181 ws.Add(*ds);
12182 }
12183 RooStats::ModelConfig mc("ModelConfig", GetTitle(), &w);
12184 mc.SetPdf(addedPdf->GetName());
12185 mc.SetObservables(*addedPdf.robs().get<RooArgList>());
12186 mc.SetGlobalObservables(*addedPdf.globs().get<RooArgList>());
12187 mc.SetNuisanceParameters(*addedPdf.np().get<RooArgList>());
12188 mc.SetParametersOfInterest(*addedPdf.poi().get<RooArgList>());
12189 ws.Add(mc);
12190
12191 // save the workspace
12193
12194 } else if (auto w = get<RooWorkspace>(); w) {
12195 // ensure the current color set is saved in the workspace
12196 w->import(*gROOT->GetListOfColors(), true);
12197
12198 if (sFilename.EndsWith(".json")) {
12199#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
12200 // stream with json tool
12202 if (tool.exportJSON(sFilename.Data())) {
12203 Info("SaveAs", "%s saved to %s", w->GetName(), sFilename.Data());
12204 } else {
12205 Error("SaveAs", "Unable to save to %s", sFilename.Data());
12206 }
12207#else
12208 Error("SaveAs", "json format workspaces only in ROOT 6.26 onwards");
12209#endif
12210 return;
12211 }
12212
12213#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
12214 // before saving, clear the eocache of all owned nodes
12215 // because causes memory leak when read back in (workspace streamer immediately overwrites the caches)
12216 // fixed in: https://github.com/root-project/root/pull/12024
12217 for (auto &c : w->components()) {
12218 c->_eocache = nullptr;
12219 }
12220#endif
12221 // const_cast<Node2*>(this)->sterilize(); - tried this to reduce mem leak on readback but no improve
12222 if (!w->writeToFile(sFilename, sOpt != "update")) {
12223 Info("SaveAs", "%s saved to %s", w->GetName(), sFilename.Data());
12224 // save any fitDatabase that is loaded in memory too
12225 // TODO: We should do this as well for SaveAs on a scan object
12226 std::function<void(TDirectory *, TDirectory *)> CopyDir;
12227
12229 auto dir = dest->GetDirectory(source->GetName());
12230 if (!dir) {
12231 dir = dest->mkdir(source->GetName());
12232 }
12233 for (auto k : *source->GetListOfKeys()) {
12234 auto key = dynamic_cast<TKey *>(k);
12235 const char *classname = key->GetClassName();
12236 TClass *cl = gROOT->GetClass(classname);
12237 // std::cout << "processing " << key->GetName() << " " << classname << std::endl;
12238 if (!cl) {
12239 continue;
12240 } else if (cl->InheritsFrom(TDirectory::Class())) {
12241 CopyDir(source->GetDirectory(key->GetName()), dir);
12242 } else {
12243 // don't write object if it already exists
12244 if (dir->FindKey(key->GetName()))
12245 continue;
12246 // support FitConfigs ....
12247 if (strcmp(classname, "ROOT::Fit::FitConfig") == 0) {
12248 auto fc = key->ReadObject<ROOT::Fit::FitConfig>();
12249 dir->WriteObject(fc, key->GetName());
12250 delete fc;
12251 } else {
12252 TObject *obj = key->ReadObj();
12253 if (obj) {
12254 dir->WriteTObject(obj, key->GetName());
12255 delete obj;
12256 }
12257 }
12258 }
12259 }
12260 };
12261 if (gROOT->GetListOfFiles()) {
12262 for (auto key : *gROOT->GetListOfFiles()) {
12263 if (auto fitDb = dynamic_cast<TMemFile *>(key);
12264 fitDb /*&& TString(key->GetName()).BeginsWith("fitDatabase_")*/) {
12265 CopyDir(fitDb, std::make_unique<TFile>(sFilename, "UPDATE").get());
12266 Info("SaveAs", "Saved %s to %s", fitDb->GetName(), sFilename.Data());
12267 }
12268 }
12269 }
12270 } else {
12271 Error("SaveAs", "Unable to save to %s", sFilename.Data());
12272 }
12273#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
12274 // restore the cache to every node
12275 for (auto &c : w->components()) {
12276 c->setExpensiveObjectCache(w->expensiveObjectCache());
12277 }
12278#endif
12279 }
12280}
12281
12282double xRooNode::GetBinError(int bin, const xRooNode &fr, int nToys, bool errorsHi, bool errorsLo) const
12283{
12284 auto res = GetBinErrors(bin, bin, fr, nToys, errorsHi, errorsLo);
12285 if (res.empty())
12286 return std::numeric_limits<double>::quiet_NaN();
12287 return res.at(0);
12288}
12289
12290std::vector<double> xRooNode::contents() const
12291{
12292 std::vector<double> out;
12293 out.reserve(size());
12294 for (auto child : *this) {
12295 out.push_back(child->GetContent());
12296 }
12297 return out;
12298}
12299
12301{
12302
12303 auto _fr = fr.get<RooFitResult>();
12304
12305 if (!_fr) {
12306 return covariances(fitResult());
12307 }
12308
12309 auto rho = _fr->correlationMatrix();
12310
12311 TMatrixDSym out(size());
12312
12313 auto _pars = pars();
12314
12315 // 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]
12316 // - nu[theta_n_down]) consistent with propagatedError formula
12317
12318 for (int m = 0; m < rho.GetNrows(); m++) {
12319 auto p_m = dynamic_cast<RooRealVar *>(_fr->floatParsFinal().at(m));
12320 if (!p_m)
12321 continue; // skip categoricals
12322 auto _p = dynamic_cast<RooAbsRealLValue *>(_pars.get<RooArgList>()->find(p_m->GetName()));
12323 if (!_p)
12324 continue;
12325 auto tmp = _p->getVal();
12326 _p->setVal(p_m->getVal() + p_m->getErrorHi());
12327 auto nu_m = contents();
12328 _p->setVal(p_m->getVal() + p_m->getErrorLo());
12329 auto nu_m2 = contents();
12330 _p->setVal(tmp);
12331 for (int n = 0; n < rho.GetNrows(); n++) {
12332 auto p_n = dynamic_cast<RooRealVar *>(_fr->floatParsFinal().at(n));
12333 if (!p_n)
12334 continue; // skip categoricals
12335 auto _p2 = dynamic_cast<RooAbsRealLValue *>(_pars.get<RooArgList>()->find(p_n->GetName()));
12336 if (!_p2)
12337 continue;
12338 auto tmp2 = _p2->getVal();
12339 _p2->setVal(p_n->getVal() + p_n->getErrorHi());
12340 auto nu_n = (p_n == p_m) ? nu_m : contents();
12341 _p2->setVal(p_n->getVal() + p_n->getErrorLo());
12342 auto nu_n2 = (p_n == p_m) ? nu_m2 : contents();
12343 _p2->setVal(tmp2);
12344 for (int i = 0; i < out.GetNrows(); i++) {
12345 for (int j = 0; j < out.GetNrows(); j++) {
12346 out(i, j) += 0.25 * (nu_m[i] - nu_m2[i]) * rho(m, n) * (nu_n[j] - nu_n2[j]);
12347 }
12348 }
12349 }
12350 }
12351 return out;
12352}
12353
12354std::pair<double, double>
12355xRooNode::IntegralAndError(const xRooNode &fr, const char *rangeName, int nToys, bool errorsHi, bool errorsLo) const
12356{
12357 double out = 1.;
12358 double err = std::numeric_limits<double>::quiet_NaN();
12359
12360 std::unique_ptr<RooAbsCollection> _snap;
12362 if (auto _fr = fr.get<RooFitResult>()) {
12363 _pars.add(pars().argList());
12364 _snap.reset(_pars.snapshot());
12365 _pars = _fr->floatParsFinal();
12366 _pars = _fr->constPars();
12367 }
12368
12369 auto _obs = obs();
12370 RooArgSet sobs(*_obs.get<RooArgList>()); // need to make explicit RooArgSet for ROOT 6.36 onwards
12371 auto _coefs = coefs(); // need here to keep alive owned RooProduct
12372 if (auto c = _coefs.get<RooAbsReal>(); c) {
12373 out = c->getVal(sobs); // assumes independent of observables!
12374 }
12375
12376 if (auto p = dynamic_cast<RooAbsPdf *>(get()); p) {
12377 // prefer to use expectedEvents for integrals of RooAbsPdf e.g. for RooProdPdf wont include constraint terms
12378 if (rangeName)
12379 p->setNormRange(rangeName);
12380#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 27, 00)
12382#endif
12383 out *= p->expectedEvents(*_obs.get<RooArgList>());
12384#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
12385 // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
12386 p->_normSet = nullptr;
12387#endif
12388 err = GetBinError(-1, fr, nToys, errorsHi, errorsLo);
12389 if (rangeName)
12390 p->setNormRange(nullptr);
12391 } else if (auto p2 = dynamic_cast<RooAbsReal *>(get()); p2) {
12392 // only integrate over observables we actually depend on
12393 auto f = std::shared_ptr<RooAbsReal>(
12394 p2->createIntegral(*std::unique_ptr<RooArgSet>(p2->getObservables(*_obs.get<RooArgList>())),
12395 rangeName)); // did use x here before using obs
12396 RooProduct pr("int_x_coef", "int_x_coef",
12397 RooArgList(*f, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>()));
12398 out *= f->getVal();
12399 err = xRooNode(pr, *this).GetBinError(-1, fr, nToys, errorsHi, errorsLo);
12400 sterilize(); // needed so that we can forget properly about the integral we just created (and are deleting)
12401 } else if (get<RooAbsData>()) {
12402 out = 0;
12403 auto vals = GetBinContents(1, 0); // returns all bins
12404 auto ax = (rangeName) ? GetXaxis() : nullptr;
12405 auto rv = (ax) ? dynamic_cast<RooRealVar *>(ax->GetParent()) : nullptr;
12406 auto cv = (ax && !rv) ? dynamic_cast<RooCategory *>(ax->GetParent()) : nullptr;
12407 int i = 0;
12408 for (auto &v : vals) {
12409 i++;
12410 if (rangeName) {
12411 if (rv && !rv->inRange(ax->GetBinCenter(i), rangeName))
12412 continue;
12413 if (cv && !cv->isStateInRange(rangeName, ax->GetBinLabel(i)))
12414 continue;
12415 }
12416 out += v;
12417 }
12418 err = 0; // should this be sqrt(sum(v^2)) or something similar
12419 } else {
12420 out = std::numeric_limits<double>::quiet_NaN();
12421 }
12422 if (_snap) {
12423 _pars.RooAbsCollection::operator=(*_snap);
12424 }
12425 return std::make_pair(out, err);
12426}
12427
12428std::vector<double>
12429xRooNode::GetBinErrors(int binStart, int binEnd, const xRooNode &_fr, int nToys, bool errorHi, bool errorLo) const
12430{
12431 // note: so far this method is inconsistent with the BuildHistogram in ways:
12432 // no projection over other variables
12433 // July2023: made RooRealSumPdf evaluate as a function if doesn't have a floor
12434 // but this method will still evaluate it as a pdf (uses PdfWrapper)
12435 // but can get away with it while added NaN recovery to getSimplePropagatedError to pickup raw values
12436
12437 if (fBinNumber != -1) {
12438 if (binStart != binEnd || !fParent) {
12439 throw std::runtime_error(TString::Format("%s is a bin - only has one value", GetName()));
12440 }
12441 return fParent->GetBinErrors(fBinNumber, fBinNumber, _fr, nToys, errorHi, errorLo);
12442 }
12443
12444 std::vector<double> out;
12445
12446 auto _hist = BuildHistogram(nullptr, true, true, binStart, binEnd, _fr, errorHi, errorLo, nToys);
12447 if (!_hist)
12448 return out;
12449 if (binEnd == 0) {
12450 binEnd = _hist->GetNbinsX();
12451 } else if (binEnd == binStart && binEnd == -1) {
12452 binStart = 1;
12453 binEnd = 1; // done an integral, so histogram has only 1 bin
12454 }
12455 for (int bin = binStart; bin <= binEnd; bin++) {
12456 out.push_back(((errorLo && !errorHi) ? (-1.) : 1.) *
12457 _hist->GetBinError(bin)); // using same convention as RooFit that Lo errors are negative
12458 }
12459 delete _hist;
12460 return out;
12461
12462 // auto o = dynamic_cast<RooAbsReal *>(get());
12463 // if (!o)
12464 // return out;
12465 //
12466 // std::shared_ptr<RooFitResult> fr = std::dynamic_pointer_cast<RooFitResult>(_fr.fComp);
12467 // //= dynamic_cast<RooFitResult*>( _fr.get<RooFitResult>() ? _fr->Clone() : fitResult()->Clone());
12468 //
12469 // auto _coefs = coefs();
12470 //
12471 // if (!fr) {
12472 // // need to ensure coefs, if any, are included in fit result retrieval so all pars are loaded
12473 // auto frn = (!_coefs.get() ? *this : xRooNode(RooProduct("tmp", "tmp", RooArgList(*o,
12474 // *_coefs.get<RooAbsReal>()))))
12475 // .fitResult();
12476 // if (strlen(_fr.GetName()))
12477 // frn = frn.reduced(_fr.GetName());
12478 //
12479 // // use name to reduce the fit result, if one given
12480 // fr = std::dynamic_pointer_cast<RooFitResult>(frn.fComp);
12481 // }
12482 //
12483 // if (!GETDMP(fr.get(), _finalPars)) {
12484 // fr->setFinalParList(RooArgList());
12485 // }
12486 //
12487 // /// Oct2022: No longer doing this because want to allow fitResult to be used to get partial error
12488 // // // need to add any floating parameters not included somewhere already in the fit result ...
12489 // // RooArgList l;
12490 // // for(auto& p : pars()) {
12491 // // auto v = p->get<RooRealVar>();
12492 // // if (!v) continue;
12493 // // if (v->isConstant()) continue;
12494 // // if (fr->floatParsFinal().find(v->GetName())) continue;
12495 // // if (fr->_constPars && fr->_constPars->find(v->GetName())) continue;
12496 // // l.add(*v);
12497 // // }
12498 // //
12499 // // if (!l.empty()) {
12500 // // RooArgList l2; l2.addClone(fr->floatParsFinal());
12501 // // l2.addClone(l);
12502 // // fr->setFinalParList(l2);
12503 // // }
12504 //
12505 // TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr.get(), _VM));
12506 //
12507 // if (!prevCov || size_t(prevCov->GetNcols()) < fr->floatParsFinal().size()) {
12508 // TMatrixDSym cov(fr->floatParsFinal().size());
12509 // if (prevCov) {
12510 // for (int i = 0; i < prevCov->GetNcols(); i++) {
12511 // for (int j = 0; j < prevCov->GetNrows(); j++) {
12512 // cov(i, j) = (*prevCov)(i, j);
12513 // }
12514 // }
12515 // }
12516 // int i = 0;
12517 // for (auto &p : fr->floatParsFinal()) {
12518 // if (!prevCov || i >= prevCov->GetNcols()) {
12519 // cov(i, i) = pow(dynamic_cast<RooRealVar *>(p)->getError(), 2);
12520 // }
12521 // i++;
12522 // }
12523 // int covQualBackup = fr->covQual();
12524 // fr->setCovarianceMatrix(cov);
12525 // fr->setCovQual(covQualBackup);
12526 // }
12527 //
12528 // bool doBinWidth = false;
12529 // auto ax = (binStart == -1 && binEnd == -1) ? nullptr : GetXaxis();
12530 //
12531 // auto _obs = obs(); // may own an obs so keep alive here
12532 // RooArgList normSet = _obs.argList();
12533 // // to give consistency with BuildHistogram method, should be only the axis var if defined
12534 // if (ax) {
12535 // normSet.clear();
12536 // normSet.add(*dynamic_cast<RooAbsArg *>(ax->GetParent()));
12537 // }
12538 //
12539 // if (auto p = dynamic_cast<RooAbsPdf *>(o); ax && (p || _coefs.get() || o->getAttribute("density"))) {
12540 // // pdfs of samples embedded in a sumpdf (aka have a coef) will convert their density value to a content
12541 // doBinWidth = true;
12542 // }
12543 // if (binEnd == 0) {
12544 // if (ax) {
12545 // binEnd = ax->GetNbins();
12546 // } else {
12547 // binEnd = binStart;
12548 // }
12549 // }
12550 // for (int bin = binStart; bin <= binEnd; bin++) {
12551 // if (ax)
12552 // dynamic_cast<RooAbsLValue *>(ax->GetParent())->setBin(bin - 1, ax->GetName());
12553 // // if (!SetBin(bin)) { return out; }
12554 //
12555 // double res;
12556 // if (auto p = dynamic_cast<RooAbsPdf *>(o); p) {
12557 // // fr->covarianceMatrix().Print();
12558 // res = PdfWrapper(*p, _coefs.get<RooAbsReal>(), !ax).getSimplePropagatedError(*fr, normSet);
12559 // #if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
12560 // // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
12561 // p->_normSet = nullptr;
12562 // #endif
12563 // } else {
12564 // // res = o->getPropagatedError(*fr, normSet);
12565 // // // TODO: What if coef has error? - probably need a FuncWrapper class
12566 // // if (auto c = _coefs.get<RooAbsReal>(); c) {
12567 // // res *= c->getVal(normSet);
12568 // // }
12569 // res = RooProduct("errorEval", "errorEval",
12570 // RooArgList(*o, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>()))
12571 // .getPropagatedError(*fr, normSet);
12572 // }
12573 // if (doBinWidth) {
12574 // res *= ax->GetBinWidth(bin);
12575 // }
12576 // out.push_back(res);
12577 // }
12578 //
12579 // return out;
12580}
12581
12582std::string cling::printValue(const xRooNode *v)
12583{
12584 if (!v)
12585 return "nullptr\n";
12586 if (!v->empty()) {
12587 std::string out;
12588 size_t left = v->size();
12589 for (auto n : *v) {
12590 left--;
12591 if (!out.empty())
12592 out += ",";
12593 else
12594 out += "{";
12595 out += n->GetName();
12596 if (out.length() > 100 && left > 0) {
12597 out += TString::Format(",... and %lu more", left);
12598 break;
12599 }
12600 }
12601 out += "}\n";
12602 out = std::string(Form("<%s> %s", v->get() ? v->get()->ClassName() : "nullptr", v->GetName())) + out;
12603 return out;
12604 }
12605 std::string out;
12606 if (!(*v)) {
12607 return "<nullptr>";
12608 } else {
12609 return Form("<%s> %s", v->get() ? v->get()->ClassName() : "nullptr", v->GetName());
12610 }
12611
12612 return out;
12613}
12614
@ 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:370
#define gROOT
Definition TROOT.h:411
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:572
#define gPad
Color * colors
Definition X3DBuffer.c:21
#define _(A, B)
Definition cfortran.h:108
double GetBinLowEdge(Int_t bin) const override
Return low edge of bin.
Definition xRooNode.cxx: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
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
bool fInterrupted
appears that if was fXaxis then dialog box for SetXaxis will take as current value
Definition xRooNode.h:521
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
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
only here so can have char* GetRange return so can return nullptr for no range set (required for RooC...
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
like a parent but only for use by getObject
void _scan_(const char *what="plr", double nToys=0, const char *xvar="", int nPointsX=0, double lowX=0, double highX=0, const char *constParValues="", const char *options="")
std::shared_ptr< xRooNode > fParent
Definition xRooNode.h: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:57
RooFit::OwningPtr< RooAbsData > reduce(const RooCmdArg &arg1, const RooCmdArg &arg2={}, const RooCmdArg &arg3={}, const RooCmdArg &arg4={}, const RooCmdArg &arg5={}, const RooCmdArg &arg6={}, const RooCmdArg &arg7={}, const RooCmdArg &arg8={}) const
Create a reduced copy of this dataset.
RooArgSet const * getGlobalObservables() const
Returns snapshot of global observables stored in this data.
Definition RooAbsData.h:293
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:666
virtual void SetBinLabel(Int_t bin, const char *label)
Set label for bin.
Definition TAxis.cxx:875
TAxis()
Default constructor.
Definition TAxis.cxx:49
virtual void Set(Int_t nbins, Double_t xmin, Double_t xmax)
Initialize axis with fix bins.
Definition TAxis.cxx:783
virtual Int_t FindFixBin(Double_t x) const
Find bin number corresponding to abscissa x
Definition TAxis.cxx:421
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
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:490
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:735
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:131
Create a dialog for fit function parameter settings.
System file browser, used as TRootBrowser plug-in.
A list tree is a widget that can contain a number of items arranged in a tree structure.
Definition TGListTree.h: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:2918
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:2329
void Draw(Option_t *chopt="") override
Draw this graph with its current attributes.
Definition TGraph.cxx:832
virtual Double_t GetPointY(Int_t i) const
Get y value for point i.
Definition TGraph.cxx:1555
1-D histogram with a double per channel (see TH1 documentation)
Definition TH1.h:927
1-D histogram with a float per channel (see TH1 documentation)
Definition TH1.h:879
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:8978
void SetTitle(const char *title) override
Change/set the title.
Definition TH1.cxx:6766
virtual Int_t GetNbinsY() const
Definition TH1.h:543
static void AddDirectory(Bool_t add=kTRUE)
Sets the flag controlling the automatic add of histograms in memory.
Definition TH1.cxx:1263
@ kNoTitle
Don't draw the histogram title.
Definition TH1.h:409
TAxis * GetXaxis()
Definition TH1.h:572
TObject * FindObject(const char *name) const override
Search object named name in the list of functions.
Definition TH1.cxx:3845
virtual void SetMaximum(Double_t maximum=-1111)
Definition TH1.h:653
virtual Bool_t Add(TF1 *h1, Double_t c1=1, Option_t *option="")
Performs the operation: this = this + c1*f1 if errors are defined (see TH1::Sumw2),...
Definition TH1.cxx:813
TAxis * GetYaxis()
Definition TH1.h:573
void Draw(Option_t *option="") override
Draw this histogram with options.
Definition TH1.cxx:3048
virtual void SetMinimum(Double_t minimum=-1111)
Definition TH1.h:654
TList * GetListOfFunctions() const
Definition TH1.h:489
virtual void Scale(Double_t c1=1, Option_t *option="")
Multiply this histogram by a constant c1.
Definition TH1.cxx:6652
TObject * Clone(const char *newname="") const override
Make a complete copy of the underlying object.
Definition TH1.cxx:2734
static Bool_t AddDirectoryStatus()
Static function: cannot be inlined on Windows/NT.
Definition TH1.cxx:741
virtual void SetStats(Bool_t stats=kTRUE)
Set statistics option on/off.
Definition TH1.cxx:9031
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:356
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:399
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:19
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:41
virtual void Inspect() const
Dump contents of this object in a graphics canvas.
Definition TObject.cxx:564
virtual const char * GetName() const
Returns name of object.
Definition TObject.cxx:457
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:1057
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:318
virtual TObject * FindObject(const char *name) const
Must be redefined in derived classes.
Definition TObject.cxx:421
virtual void SaveAs(const char *filename="", Option_t *option="") const
Save this object in the file specified by filename.
Definition TObject.cxx:705
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:864
virtual Bool_t InheritsFrom(const char *classname) const
Returns kTRUE if object inherits from class "classname".
Definition TObject.cxx:543
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:1071
virtual const char * GetTitle() const
Returns title of object.
Definition TObject.cxx:501
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:655
@ kCanDelete
if object in a list can be deleted
Definition TObject.h:68
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition TObject.cxx:1045
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
Draw a Pie Chart,.
Definition TPie.h:23
This is the ROOT implementation of the Qt object communication mechanism (see also http://www....
Definition TQObject.h:48
Regular expression class.
Definition TRegexp.h:31
This class creates a ROOT object browser, constituted by three main tabs.
Sequenceable collection abstract base class.
Stopwatch class.
Definition TStopwatch.h:28
Provides iteration through tokens of a given string.
Definition TPRegexp.h:143
Bool_t NextToken()
Get the next token, it is stored in this TString.
Basic string class.
Definition TString.h: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:631
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:640
Ssiz_t Index(const char *pat, Ssiz_t i=0, ECaseCompare cmp=kExact) const
Definition TString.h:659
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:67
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:2339
#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)