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 if (gEnv->GetValue("XRooFit.SkipInitParErrorInference", false)) {
441 Warning("xRooNode", "Skipping because XRooFit.SkipInitParErrorInference=true. This is expert-only, you "
442 "should fix your workspaces!");
443 } else {
444 // get the first top-level pdf
445 browse();
446 for (auto &a : *this) {
447 if (noErrorPars.empty()) {
448 break;
449 }
450 if (a->fFolder == "!pdfs") {
451 try {
452 auto fr = a->floats().reduced(parNames).fitResult("prefit");
453 if (auto _fr = fr.get<RooFitResult>(); _fr) {
454 std::set<RooRealVar *> foundPars;
455 for (auto &v : noErrorPars) {
456 if (auto arg = dynamic_cast<RooRealVar *>(_fr->floatParsFinal().find(v->GetName()));
457 arg && arg->hasError()) {
458 v->setError(arg->getError());
459 foundPars.insert(v);
460 }
461 }
462 for (auto &v : foundPars) {
463 noErrorPars.erase(v);
464 }
465 }
466 } catch (...) {
467 }
468 }
469 }
470 }
471 }
472 }
473 }
474
475 if (strlen(GetTitle()) == 0) {
476 if (fComp) {
477 TNamed::SetTitle(fComp->GetTitle());
478 } else {
479 TNamed::SetTitle(GetName());
480 }
481 }
482}
483
484xRooNode::xRooNode(const TObject &comp, const std::shared_ptr<xRooNode> &parent)
485 : xRooNode(/*[](const TObject& c) {
486c.InheritsFrom("RooAbsArg");
487if (s) {
488return (s->getStringAttribute("alias")) ? s->getStringAttribute("alias") : c.GetName();
489}
490return c.GetName();
491}(comp)*/
492 (comp.InheritsFrom("RooAbsArg") && dynamic_cast<const RooAbsArg *>(&comp)->getStringAttribute("alias"))
493 ? dynamic_cast<const RooAbsArg *>(&comp)->getStringAttribute("alias")
494 : comp.GetName(),
495 std::shared_ptr<TObject>(const_cast<TObject *>(&comp), [](TObject *) {}), parent)
496{
497}
498
499xRooNode::xRooNode(const std::shared_ptr<TObject> &comp, const std::shared_ptr<xRooNode> &parent)
500 : xRooNode(
501 [&]() {
502 if (auto a = std::dynamic_pointer_cast<RooAbsArg>(comp); a && a->getStringAttribute("alias"))
503 return a->getStringAttribute("alias");
504 if (comp)
505 return comp->GetName();
506 return "";
507 }(),
508 comp, parent)
509{
510}
511
513
514void xRooNode::_SetAttribute_(const char *name, const char *value)
515{
516 TString v(value);
517 v.ToUpper();
518 bool isBool = (v == "TRUE" || v == "FALSE");
519 if (auto a = get<RooAbsArg>(); a) {
520 if (value == nullptr || v == "NULLPTR") {
521 if (a->getAttribute(name))
522 a->setAttribute(name, false);
523 else if (a->getStringAttribute(name))
524 a->setStringAttribute(name, nullptr);
525 } else {
526 if (isBool)
527 a->setAttribute(name, (v == "TRUE"));
528 else
529 a->setStringAttribute(name, value);
530 }
531 } else {
532 RooArgList l = argList();
533 for (auto a2 : l) {
534 xRooNode(*a2)._SetAttribute_(name, value);
535 }
536 }
537 // should update this node's state in any browsers ...
538 for (auto a : *gROOT->GetListOfBrowsers()) {
539 TBrowser *b = dynamic_cast<TBrowser *>(a);
540 if (b && GetTreeItem(b)) {
541 if (auto bi = dynamic_cast<TRootBrowser *>(b->GetBrowserImp())) {
542 if (auto fb = dynamic_cast<TGFileBrowser *>(bi->GetActBrowser())) {
543 fb->DoubleClicked(GetTreeItem(b), 0);
544 }
545 }
546 }
547 }
548}
549
550void xRooNode::Checked(TObject *obj, bool val)
551{
552 if (obj != this)
553 return;
554
555 // cycle through states:
556 // unhidden and selected: tick, no uline
557 // hidden and unselected: notick, uline
558 // unhidden and unselected: tick, uline
559 if (auto o = get<RooAbsReal>(); o) {
560 if (o->isSelectedComp() && !val) {
561 // deselecting and hiding
562 o->selectComp(val);
563 o->setAttribute("hidden");
564 } else if (!o->isSelectedComp() && !val) {
565 // selecting
566 o->selectComp(!val);
567 } else if (val) {
568 // unhiding but keeping unselected
569 o->setAttribute("hidden", false);
570 }
571 auto item = GetTreeItem(nullptr);
572 item->CheckItem(!o->getAttribute("hidden"));
573 if (o->isSelectedComp()) {
574 item->ClearColor();
575 } else {
576 item->SetColor(kGray);
577 }
578 return;
579 }
580
581 if (auto o = get(); o) {
582 // if (o->TestBit(1<<20)==val) return; // do nothing
583 o->SetBit(1 << 20, val); // TODO: check is 20th bit ok to play with?
584 if (auto fr = get<RooFitResult>(); fr) {
585 if (auto _ws = ws(); _ws) {
586 if (val) {
587 // ensure fit result is in genericObjects list or snapshots ... if not, add a copy ...
588 if (fr->numStatusHistory() && !_ws->genobj(fr->GetName())) {
589 _ws->import(*fr);
590 if (auto wfr = dynamic_cast<RooFitResult *>(_ws->genobj(fr->GetName()))) {
591 fr = wfr;
592 }
593 }
594 RooArgSet _allVars = _ws->allVars();
595 _allVars = fr->floatParsFinal();
596 _allVars = fr->constPars();
597 for (auto &i : fr->floatParsInit()) {
598 auto v = dynamic_cast<RooRealVar *>(_allVars.find(i->GetName()));
599 if (v)
600 v->setStringAttribute("initVal", TString::Format("%f", dynamic_cast<RooRealVar *>(i)->getVal()));
601 }
602 // uncheck all other fit results
603 for (auto oo : _ws->allGenericObjects()) {
604 if (auto ffr = dynamic_cast<RooFitResult *>(oo); ffr && ffr != fr) {
605 ffr->ResetBit(1 << 20);
606 }
607 }
608 } else
609 _ws->allVars() = fr->floatParsInit();
610 }
611
612 TBrowser *b = nullptr;
613 for (auto a : *gROOT->GetListOfBrowsers()) {
614 b = dynamic_cast<TBrowser *>(a);
615 if (b && GetTreeItem(b)) {
616 break;
617 }
618 }
619 if (b) {
620 auto p = GetTreeItem(b);
621
622 if (p) {
623 // update check marks on siblings
624 if (auto first = p->GetParent()->GetFirstChild()) {
625 do {
626 if (first->HasCheckBox()) {
627 auto _obj = static_cast<xRooNode *>(first->GetUserData());
628 first->CheckItem(_obj->get() && _obj->get()->TestBit(1 << 20));
629 }
630 } while ((first = first->GetNextSibling()));
631 }
632 }
633
634 // also since const status of pars could have changed, refresh all 'poi' and 'np' open nodes
635 if (auto bi = dynamic_cast<TRootBrowser *>(b->GetBrowserImp())) {
636 if (auto fb = dynamic_cast<TGFileBrowser *>(bi->GetActBrowser())) {
637 while (p) {
638 if (TString(p->GetText()).BeginsWith("RooWorkspace::")) {
639 std::function<void(TGListTreeItem *)> rfunc;
640
641 rfunc = [&](TGListTreeItem *i) {
642 if (auto first = i->GetFirstChild()) {
643 do {
644 if (first->IsOpen() &&
645 (TString(first->GetText()) == "poi" || TString(first->GetText()) == "np")) {
646 fb->DoubleClicked(first, 0);
647 } else
648 rfunc(first);
649 } while ((first = first->GetNextSibling()));
650 }
651 };
652 rfunc(p);
653 break;
654 } else {
655 p = p->GetParent();
656 }
657 }
658 }
659 }
660 }
661 }
662 }
663}
664
666{
667 static bool blockBrowse = false;
668 if (blockBrowse)
669 return;
670 if (b == nullptr) {
671 auto b2 = dynamic_cast<TBrowser *>(gROOT->GetListOfBrowsers()->Last());
672 if (!b2 || !b2->GetBrowserImp()) { // no browser imp if browser was closed
673 blockBrowse = true;
674 gEnv->SetValue("X11.UseXft", "no"); // for faster x11
675 gEnv->SetValue("X11.Sync", "no");
676 gEnv->SetValue("X11.FindBestVisual", "no");
677 gEnv->SetValue("Browser.Name", "TRootBrowser"); // forces classic root browser (in 6.26 onwards)
678 gEnv->SetValue("Canvas.Name", "TRootCanvas");
679 b2 = new TBrowser("nodeBrowser", this, "RooFit Browser");
680 blockBrowse = false;
681 } else if (strcmp(b2->GetName(), "nodeBrowser") == 0) {
682 blockBrowse = true;
683 b2->BrowseObject(this);
684 blockBrowse = false;
685 } else {
686 auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b2->GetBrowserImp())));
687 if (_b) {
688 _b->AddFSDirectory("Workspaces", nullptr, "SetRootDir");
689 _b->GotoDir(nullptr);
690 _b->Add(this, GetName());
691 }
692 }
693 return;
694 }
695
696 if (auto item = GetTreeItem(b); item) {
697 if (!item->IsOpen() && IsFolder())
698 return; // no need to rebrowse if closing
699 // update check marks on any child items
700 if (auto first = item->GetFirstChild()) {
701 do {
702 if (first->HasCheckBox()) {
703 auto _obj = static_cast<xRooNode *>(first->GetUserData());
704 first->CheckItem(_obj->get() &&
705 (_obj->get()->TestBit(1 << 20) ||
706 (_obj->get<RooAbsArg>() && !_obj->get<RooAbsArg>()->getAttribute("hidden"))));
707 }
708 } while ((first = first->GetNextSibling()));
709 }
710 }
711
712 browse();
713
714 // for top-level pdfs default to having the .vars browsable too
715 if (get<RooAbsPdf>() && fFolder == "!pdfs" && !_IsShowVars_()) {
716 fBrowsables.push_back(std::make_shared<xRooNode>(vars()));
717 }
718
719 if (auto _fr = get<RooFitResult>(); _fr && fBrowsables.empty()) {
720 // have some common drawing options
721 fBrowsables.push_back(std::make_shared<xRooNode>(".Draw(\"pull\")", nullptr, *this));
722 fBrowsables.push_back(std::make_shared<xRooNode>(".Draw(\"corr10colztext\")", nullptr, *this));
723 if (std::unique_ptr<RooAbsCollection>(_fr->floatParsFinal().selectByAttrib("poi", true))->size() == 1) {
724 fBrowsables.push_back(std::make_shared<xRooNode>(".Draw(\"impact\")", nullptr, *this));
725 }
726 }
727
728 if (empty() && fBrowsables.empty()) {
729 try {
730 if (auto s = get<TStyle>()) {
731 s->SetFillAttributes();
732 if (auto ed = dynamic_cast<TGedEditor *>(TVirtualPadEditor::GetPadEditor())) {
733 ed->SetModel(gPad, s, kButton1Down, true);
734 }
735 } else if (TString(GetName()).BeginsWith(".Draw(\"") && fParent) {
736 fParent->Draw(TString(TString(GetName())(7, strlen(GetName()) - 9)) + b->GetDrawOption());
737 } else {
738 Draw(b->GetDrawOption());
739 }
740 } catch (const std::exception &e) {
741 new TGMsgBox(
742 gClient->GetRoot(),
743 (gROOT->GetListOfBrowsers()->At(0))
744 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
745 : gClient->GetRoot(),
746 "Exception", e.what(),
747 kMBIconExclamation); // deletes self on dismiss?
748 }
749 }
750
751 bool hasFolders = false;
752 if (strlen(GetName()) > 0 && GetName()[0] != '!') { // folders don't have folders
753 for (auto &c : *this) {
754 if (!c->fFolder.empty()) {
755 hasFolders = true;
756 break;
757 }
758 }
759 }
760 // auto _ws = get<RooWorkspace>();
761 if (/*_ws*/ hasFolders) {
762 // organize in folders
763 auto _folders = find(".folders");
764 if (!_folders) {
765 _folders = emplace_back(std::make_shared<xRooNode>(".folders", nullptr, *this));
766 }
767 // ensure entry in folders for every folder type ...
768 for (auto &v : *this) {
769 if (!v->fFolder.empty() && !_folders->find(v->fFolder, false)) {
770 _folders->emplace_back(std::make_shared<xRooNode>(v->fFolder.c_str(), nullptr, *this));
771 }
772 }
773 // now just add all the folders
774 for (auto &v : *_folders) {
775 TString _name = v->GetName();
776 if (_name.BeginsWith('!'))
777 _name = _name(1, _name.Length()); // strip ! from display
778 b->Add(v.get(), _name);
779 }
780 }
781
782 for (auto &v : *this) {
783 if (hasFolders && !v->fFolder.empty())
784 continue; // in the folders
785 if (strcmp(v->GetName(), ".folders") == 0)
786 continue; // never 'browse' the folders property
787 auto _fr = v->get<RooFitResult>();
788 int _checked = (v->get<RooAbsData>() || _fr) ? v->get()->TestBit(1 << 20) : -1;
789 if (_fr && ((_fr->status() == 0 && _fr->numStatusHistory() == 0) || (_fr->floatParsFinal().empty()))) {
790 // this is a "PARTIAL" fit result ... don't allow it to be selected
791 _checked = -1;
792 }
793 if (v->get<RooAbsPdf>() && get<RooSimultaneous>())
794 _checked = !v->get<RooAbsArg>()->getAttribute("hidden");
795 TString _name = v->GetName();
796 if (v->get() && _name.BeginsWith(TString(v->get()->ClassName()) + "::")) {
797 _name = _name(strlen(v->get()->ClassName()) + 2, _name.Length());
798 }
799 if (_name.BeginsWith(".")) {
800 // property node -- display the name of the contained object
801 if (v->get()) {
802 _name = TString::Format("%s: %s::%s", _name.Data(), v->get()->ClassName(),
803 (v->get<RooAbsArg>() && v->get<RooAbsArg>()->getStringAttribute("alias"))
804 ? v->get<RooAbsArg>()->getStringAttribute("alias")
805 : v->get()->GetName());
806 }
807 } else if (v->get() && !v->get<TFile>() && !TString(v->GetName()).BeginsWith('/'))
808 _name = TString::Format("%s::%s", v->get()->ClassName(), _name.Data());
809 if (auto _type = v->GetNodeType(); strlen(_type)) {
810 // decided not to show const values until figure out how to update if value changes
811 /*if (TString(_type)=="Const") _name += TString::Format(" [%s=%g]",_type,v->get<RooConstVar>()->getVal());
812 else*/
813 _name += TString::Format(" [%s]", _type);
814 }
815 if (auto fv = v->get<RooFormulaVar>()) {
816 TString formu = TString::Format(" [%s]", fv->expression());
817 for (size_t i = 0; i < fv->dependents().size(); i++) {
818 formu.ReplaceAll(TString::Format("x[%zu]", i), fv->dependents()[i].GetName());
819 }
820 _name += formu;
821 } else if (auto gv = v->get<RooGenericPdf>()) {
822 TString formu = TString::Format(" [%s]", gv->expression());
823 for (size_t i = 0; i < gv->dependents().size(); i++) {
824 formu.ReplaceAll(TString::Format("x[%zu]", i), gv->dependents()[i].GetName());
825 }
826 _name += formu;
827 } else if (auto pi = v->get<PiecewiseInterpolation>()) {
828 // check if all interpCodes are the same.
829 std::set<int> interpCodes;
830 for (auto &c : pi->interpolationCodes())
831 interpCodes.insert(c);
832 if (interpCodes.size() == 1) {
833 _name += TString::Format(" [InterpCode=%d]", *interpCodes.begin());
834 }
835 } else if (auto fiv = v->get<RooStats::HistFactory::FlexibleInterpVar>()) {
836 // check if all interpCodes are the same.
837 std::set<int> interpCodes;
838 for (auto &c : fiv->interpolationCodes())
839 interpCodes.insert(c == 4 ? 5 : c); // in definition of FlexibleInterpVar 4 gets replaced with 5
840 if (interpCodes.size() == 1) {
841 _name += TString::Format(" [InterpCode=%d]", *interpCodes.begin());
842 }
843 }
844 // tool tip defaults to displaying name and title, so temporarily set name to obj name if has one
845 // and set title to the object type
846 TString nameSave(v->TNamed::GetName());
847 TString titleSave(v->TNamed::GetTitle());
848 if (auto o = v->get(); o)
849 v->TNamed::SetNameTitle(o->GetName(), o->ClassName());
850 b->Add(v.get(), _name, _checked);
851 if (auto o = v->get(); o)
852 v->TNamed::SetNameTitle(nameSave, titleSave);
853 if (_checked != -1) {
854 dynamic_cast<TQObject *>(b->GetBrowserImp())
855 ->Connect("Checked(TObject *, bool)", ClassName(), v.get(), "Checked(TObject *, bool)");
856 }
857 if (_fr) {
858 if (_fr->status() || _fr->covQual() != 3) { // snapshots or bad fits
859 v->GetTreeItem(b)->SetColor((_fr->numStatusHistory() && !_fr->floatParsFinal().empty()) ? kRed : kBlue);
860 } else if (_fr->numStatusHistory() == 0) { // partial fit result ..
861 v->GetTreeItem(b)->SetColor(kGray);
862 }
863 }
864 if ((v->fFolder == "!np" || v->fFolder == "!poi")) {
865 if (v->get<RooAbsArg>()->getAttribute("Constant")) {
866 v->GetTreeItem(b)->SetColor(kGray);
867 } else
868 v->GetTreeItem(b)->ClearColor();
869 }
870 if (auto _htr = v->get<RooStats::HypoTestResult>(); _htr) {
871 // check for fit statuses
872 if (auto fits = _htr->GetFitInfo()) {
873 for (int i = 0; i < fits->numEntries(); i++) {
874 // if any fit (other than a genFit) is bad, flag point as bad
875 if (fits->get(i)->getCatIndex("type") != 5 && fits->get(i)->getRealValue("status") != 0) {
876 v->GetTreeItem(b)->SetColor(kRed);
877 break;
878 }
879 }
880 } else {
881 v->GetTreeItem(b)->SetColor(kBlue); // unknown fit status
882 }
883 }
884
885 // v.fBrowsers.insert(b);
886 }
887
888 // for pdfs, check for datasets too and add to list
889 /*if (get<RooAbsPdf>()) {
890 auto dsets = datasets();
891 if (!dsets.empty()) {
892 // check if already have .datasets() in browsables
893 bool found(false);
894 for(auto& p : fBrowsables) {
895 if (TString(p->GetName())==".datasets()") {found=true;
896 // add
897 break;
898 }
899 }
900 if (!found) {
901 fBrowsables.push_back(std::make_shared<xRooNode>(dsets));
902 }
903 }
904 }*/
905 // browse the browsables too
906 for (auto &v : fBrowsables) {
907 TString _name = v->GetName();
908 if (_name == ".memory")
909 continue; // hide the memory from browsing, if put in browsables
910 TString nameSave(v->TNamed::GetName());
911 TString titleSave(v->TNamed::GetTitle());
912 if (auto o = v->get(); o)
913 v->TNamed::SetNameTitle(o->GetName(), o->ClassName());
914 b->Add(v.get(), _name, -1);
915 if (auto o = v->get(); o)
916 v->TNamed::SetNameTitle(nameSave, titleSave);
917 }
918
919 b->SetSelected(this);
920}
921
923{
924 if (!set) {
925 // can't remove as causes a crash, need to remove from the browser first
926 /*for(auto itr = fBrowsables.begin(); itr != fBrowsables.end(); ++itr) {
927 if (strcmp((*itr)->GetName(),".vars")==0) {
928 fBrowsables.erase(itr);
929 }
930 }*/
931 } else {
932 auto v = std::make_shared<xRooNode>(vars());
933 fBrowsables.push_back(v);
934 if (auto l = GetListTree(nullptr)) {
935 l->AddItem(GetTreeItem(nullptr), v->GetName(), v.get());
936 }
937 }
938}
939
941{
942 for (auto &b : fBrowsables) {
943 if (strcmp(b->GetName(), ".vars") == 0)
944 return true;
945 }
946 return false;
947}
948
950{
951 if (strlen(GetName()) > 0 && GetName()[0] == '!')
952 return true;
953 if (strlen(GetName()) > 0 && GetName()[0] == '.' && !(TString(GetName()).BeginsWith(".Draw(\"")))
954 return true;
955 if (empty())
956 const_cast<xRooNode *>(this)->browse();
957 return !empty();
958}
959
960class Axis2 : public TAxis {
961
962public:
963 using TAxis::TAxis;
964 double GetBinWidth(Int_t bin) const override
965 {
966 if (auto v = var(); v)
967 return v->getBinWidth(bin - 1, GetName());
968 return 1;
969 }
970 double GetBinLowEdge(Int_t bin) const override
971 {
972 if (auto v = rvar(); v) {
973 return (bin == v->getBinning(GetName()).numBins() + 1) ? v->getBinning(GetName()).binHigh(bin - 2)
974 : v->getBinning(GetName()).binLow(bin - 1);
975 }
976 return bin - 1;
977 }
978 double GetBinUpEdge(Int_t bin) const override
979 {
980 if (auto v = rvar(); v)
981 return (bin == 0) ? v->getBinning(GetName()).binLow(bin) : v->getBinning(GetName()).binHigh(bin - 1);
982 return bin;
983 }
984
985 const char *GetTitle() const override
986 {
987 return (binning() && strlen(binning()->GetTitle())) ? binning()->GetTitle() : GetParent()->GetTitle();
988 }
989 void SetTitle(const char *title) override
990 {
991 if (binning()) {
992 const_cast<RooAbsBinning *>(binning())->SetTitle(title);
993 } else {
994 dynamic_cast<TNamed *>(GetParent())->SetTitle(title);
995 }
996 }
997
998 void Set(Int_t nbins, const double *xbins) override
999 {
1000 if (auto v = dynamic_cast<RooRealVar *>(rvar()))
1001 v->setBinning(RooBinning(nbins, xbins), GetName());
1002 TAxis::Set(nbins, xbins);
1003 }
1004 void Set(Int_t nbins, const float *xbins) override
1005 {
1006 std::vector<double> bins(nbins + 1);
1007 for (int i = 0; i <= nbins; i++)
1008 bins.at(i) = xbins[i];
1009 return Set(nbins, &bins[0]);
1010 }
1011 void Set(Int_t nbins, double xmin, double xmax) override
1012 {
1013 if (auto v = dynamic_cast<RooRealVar *>(rvar()))
1014 v->setBinning(RooUniformBinning(xmin, xmax, nbins), GetName());
1015 TAxis::Set(nbins, xmin, xmax);
1016 }
1017
1018 const RooAbsBinning *binning() const { return var()->getBinningPtr(GetName()); }
1019
1020 Int_t FindFixBin(const char *label) const override { return TAxis::FindFixBin(label); }
1021 Int_t FindFixBin(double x) const override { return (binning()) ? (binning()->binNumber(x) + 1) : x; }
1022
1023private:
1024 RooAbsLValue *var() const { return dynamic_cast<RooAbsLValue *>(GetParent()); }
1025 RooAbsRealLValue *rvar() const { return dynamic_cast<RooAbsRealLValue *>(GetParent()); }
1026};
1027
1028std::shared_ptr<TObject> xRooNode::getObject(const std::string &name, const std::string &type) const
1029{
1030 // if (fParent) return fParent->getObject(name);
1031
1032 if (auto _owned = find(".memory"); _owned) {
1033 for (auto &o : *_owned) {
1034 if (name == o->GetName()) {
1035 if (type.empty() || o->get()->InheritsFrom(type.c_str()))
1036 return o->fComp;
1037 }
1038 }
1039 }
1040
1041 // see if have a provider
1042 auto _provider = fProvider;
1043 auto _parent = fParent;
1044 while (!_provider && _parent) {
1045 _provider = _parent->fProvider;
1046 _parent = _parent->fParent;
1047 }
1048 if (_provider)
1049 return _provider->getObject(name, type);
1050
1051 if (ws()) {
1052 std::shared_ptr<TObject> out;
1053 if (auto arg = ws()->arg(name.c_str()); arg) {
1054 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
1055 if (!type.empty() && arg->InheritsFrom(type.c_str()))
1056 return _tmp;
1057 if (!out)
1058 out = _tmp;
1059 }
1060 if (auto arg = ws()->data(name.c_str()); arg) {
1061 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
1062 if (!type.empty() && arg->InheritsFrom(type.c_str()))
1063 return _tmp;
1064 if (!out)
1065 out = _tmp;
1066 }
1067 if (auto arg = ws()->genobj(name.c_str()); arg) {
1068 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
1069 if (!type.empty() && arg->InheritsFrom(type.c_str()))
1070 return _tmp;
1071 if (!out)
1072 out = _tmp;
1073 }
1074 if (auto arg = ws()->embeddedData(name.c_str()); arg) {
1075 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
1076 if (!type.empty() && arg->InheritsFrom(type.c_str()))
1077 return _tmp;
1078 if (!out)
1079 out = _tmp;
1080 }
1081 if (auto arg = GETWSSNAPSHOTS(ws()).find(name.c_str()); arg) {
1082 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
1083 if (!type.empty() && arg->InheritsFrom(type.c_str()))
1084 return _tmp;
1085 if (!out)
1086 out = _tmp;
1087 }
1088 return out;
1089 }
1090 if (auto arg = get<RooAbsArg>()) {
1091 // can try all nodes
1092 RooArgSet nodes;
1093 arg->treeNodeServerList(&nodes);
1094 if (auto server = nodes.find(name.c_str())) {
1095 return std::shared_ptr<TObject>(server, [](TObject *) {});
1096 }
1097 }
1098 return nullptr;
1099}
1100
1102{
1103 if (fXAxis) {
1104 // check if num bins needs update or not
1105 if (auto cat = dynamic_cast<RooAbsCategory *>(fXAxis->GetParent());
1106 cat && cat->numTypes() != fXAxis->GetNbins()) {
1107 fXAxis.reset();
1108 } else {
1109 return fXAxis.get();
1110 }
1111 }
1112 RooAbsLValue *x = nullptr;
1113 if (auto a = get<RooAbsArg>(); a && a->isFundamental())
1114 x = dynamic_cast<RooAbsLValue *>(a); // self-axis
1115
1116 auto _parentX = (!x && fParent && !fParent->get<RooSimultaneous>()) ? fParent->GetXaxis() : nullptr;
1117
1118 auto o = get<RooAbsReal>();
1119 if (!o)
1120 return _parentX;
1121
1122 if (auto xName = o->getStringAttribute("xvar"); xName) {
1123 x = dynamic_cast<RooAbsLValue *>(getObject(xName).get());
1124 }
1125
1126 // if xvar has become set equal to an arg and this is a pdf, we will allow a do-over
1127 if (!x) {
1128 // need to choose from dependent fundamentals, in following order:
1129 // parentX (if not a glob), robs, globs, vars, args
1130
1131 if (_parentX && !dynamic_cast<RooAbsArg *>(_parentX->GetParent())->getAttribute("global") &&
1132 (o->dependsOn(*dynamic_cast<RooAbsArg *>(_parentX->GetParent())) || vars().empty())) {
1133 x = dynamic_cast<RooAbsLValue *>(_parentX->GetParent());
1134 } else if (auto _obs = obs(); !_obs.empty()) {
1135 for (auto &v : _obs) {
1136 if (!v->get<RooAbsArg>()->getAttribute("global")) {
1137 x = v->get<RooAbsLValue>();
1138 if (x)
1139 break;
1140 } else if (!x) {
1141 x = v->get<RooAbsLValue>();
1142 }
1143 }
1144 } else if (auto _pars = pars(); !_pars.empty()) {
1145 for (auto &v : _pars) {
1146 if (!v->get<RooAbsArg>()->getAttribute("Constant")) {
1147 x = v->get<RooAbsLValue>();
1148 if (x)
1149 break;
1150 } else if (!x) {
1151 x = v->get<RooAbsLValue>();
1152 }
1153 }
1154 }
1155
1156 if (!x) {
1157 return nullptr;
1158 }
1159 }
1160
1161 /* no longer 'remembering' xvars when call GetXaxis(), as was causing incorrect obs to go onto node
1162 * when e.g. adding histogram with bin errors to a channel - the statFactor and constraint terms were
1163 * created first, causing the channel's xaxis to become equal to a globs at some point because of a GetXaxis call
1164 * Alternatively we could clear the xvar attribute of all client nodes whenever we 'modify' something
1165 * Should do that if processing large workspaces becomes slow
1166 if (o != dynamic_cast<TObject *>(x)) {
1167 o->setStringAttribute("xvar", dynamic_cast<TObject *>(x)->GetName());
1168 } */
1169
1170 // decide binning to use
1171 TString binningName = o->getStringAttribute("binning");
1172 auto _bnames = x->getBinningNames();
1173 bool hasBinning = false;
1174 for (auto &b : _bnames) {
1175 if (b == binningName) {
1176 hasBinning = true;
1177 break;
1178 }
1179 }
1180 if (!hasBinning) {
1181 // doesn't have binning, so clear binning attribute
1182 // this can happen after Combine of models because binning don't get combined yet (should fix this)
1183 Warning("GetXaxis", "Binning %s not defined on %s - clearing", binningName.Data(),
1184 dynamic_cast<TObject *>(x)->GetName());
1185 o->setStringAttribute("binning", nullptr);
1186 binningName = "";
1187 }
1188
1189 if (binningName == "" && o != dynamic_cast<TObject *>(x)) {
1190 // has var has a binning matching this nodes name then use that
1191 auto __bnames = x->getBinningNames();
1192 for (auto &b : __bnames) {
1193 if (b == GetName())
1194 binningName = GetName();
1195 if (b == o->GetName()) {
1196 binningName = o->GetName();
1197 break;
1198 } // best match
1199 }
1200 if (binningName == "") {
1201 // if we are binned in this var then will define that as a binning
1202 if (/*o->isBinnedDistribution(*dynamic_cast<RooAbsArg *>(x))*/
1203 auto bins = _or_func(
1204 /*o->plotSamplingHint(*dynamic_cast<RooAbsRealLValue
1205 *>(x),-std::numeric_limits<double>::infinity(),std::numeric_limits<double>::infinity())*/
1206 (std::list<double> *)(nullptr),
1207 (dynamic_cast<RooAbsRealLValue *>(x))
1208 ? o->binBoundaries(*dynamic_cast<RooAbsRealLValue *>(x), -std::numeric_limits<double>::infinity(),
1209 std::numeric_limits<double>::infinity())
1210 : nullptr);
1211 bins) {
1212 std::vector<double> _bins;
1213 for (auto &b : *bins) {
1214 if (_bins.empty() || std::abs(_bins.back() - b) > 1e-5 * _bins.back())
1215 _bins.push_back(b);
1216 }
1217 fXAxis = std::make_shared<Axis2>(_bins.size() - 1, &_bins[0]);
1218 // add this binning to the var to avoid recalling ...
1219 if (auto _v = dynamic_cast<RooRealVar *>(x); _v) {
1220 _v->setBinning(RooBinning(_bins.size() - 1, &_bins[0], o->GetName()), o->GetName());
1221 _v->getBinning(o->GetName())
1222 .SetTitle(""); // indicates to use the current var title when building histograms etc
1223 //_v->getBinning(o->GetName()).SetTitle(strlen(dynamic_cast<TObject*>(x)->GetTitle()) ?
1224 // dynamic_cast<TObject*>(x)->GetTitle() : dynamic_cast<TObject*>(x)->GetName());
1225 }
1226 binningName = o->GetName();
1227 delete bins;
1228 } else if (_parentX) {
1229 // use parent axis binning if defined, otherwise we will default
1230 binningName = _parentX->GetName();
1231 }
1232 }
1233 }
1234
1235 if (!fXAxis) {
1236 if (auto r = dynamic_cast<RooAbsRealLValue *>(x); r) {
1237 if (r->getBinning(binningName).isUniform()) {
1238 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), r->getMin(binningName), r->getMax(binningName));
1239 } else {
1240 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), r->getBinning(binningName).array());
1241 }
1242 } else if (auto cat = dynamic_cast<RooCategory *>(x)) {
1243 std::vector<double> bins = {};
1244 for (int i = 0; i <= x->numBins(binningName); i++)
1245 bins.push_back(i);
1246 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), &bins[0]);
1247 // TODO have to load current state of bin labels if was a category (sadly not a virtual method)
1248 int i = 1;
1249 std::map<int, std::string> cats; // fill into a map to preserve index ordering
1250 for (auto &c : *cat) {
1251 if (cat->isStateInRange(binningName, c.first.c_str())) {
1252 cats[c.second] = c.first;
1253 }
1254 }
1255 for (auto &[_, label] : cats) {
1256 fXAxis->SetBinLabel(i++, label.c_str());
1257 }
1258 }
1259 }
1260
1261 fXAxis->SetName(binningName);
1262 fXAxis->SetParent(dynamic_cast<TObject *>(x));
1263 return fXAxis.get();
1264}
1265
1266const char *xRooNode::GetIconName() const
1267{
1268 if (auto o = get(); o) {
1269 if (o->InheritsFrom("RooWorkspace"))
1270 return "TFile";
1271 if (o->InheritsFrom("RooAbsData"))
1272 return "TProfile";
1273 if (o->InheritsFrom("RooSimultaneous"))
1274 return "TH3D";
1275
1276 if (o->InheritsFrom("RooProdPdf"))
1277 return "a.C"; // or nullptr for folder
1278 if (o->InheritsFrom("RooRealSumPdf") || o->InheritsFrom("RooAddPdf"))
1279 return "TH2D";
1280 // if(o->InheritsFrom("RooProduct")) return "TH1D";
1281 if (o->InheritsFrom("RooFitResult")) {
1282 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitRooFitResult", true)) {
1283 gClient->GetMimeTypeList()->AddType("xRooFitRooFitResult", "xRooFitRooFitResult", "package.xpm",
1284 "package.xpm", "->Browse()");
1285 }
1286 return "xRooFitRooFitResult";
1287 }
1288 if (o->InheritsFrom("RooRealVar") || o->InheritsFrom("RooCategory")) {
1289 if (get<RooAbsArg>()->getAttribute("obs")) {
1290 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitObs", true)) {
1291 gClient->GetMimeTypeList()->AddType("xRooFitObs", "xRooFitObs", "x_pic.xpm", "x_pic.xpm", "->Browse()");
1292 }
1293 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitGlobs", true)) {
1294 gClient->GetMimeTypeList()->AddType("xRooFitGlobs", "xRooFitGlobs", "z_pic.xpm", "z_pic.xpm",
1295 "->Browse()");
1296 }
1297 return (get<RooAbsArg>()->getAttribute("global") ? "xRooFitGlobs" : "xRooFitObs");
1298 }
1299 return "TLeaf";
1300 }
1301 if (o->InheritsFrom("TStyle")) {
1302 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitTStyle", true)) {
1303 gClient->GetMimeTypeList()->AddType("xRooFitTStyle", "xRooFitTStyle", "bld_colorselect.xpm",
1304 "bld_colorselect.xpm", "->Browse()");
1305 }
1306 return "xRooFitTStyle";
1307 }
1308 if (o->InheritsFrom("RooConstVar")) {
1309 /*if (!gClient->GetMimeTypeList()->GetIcon("xRooFitRooConstVar",true)) {
1310 gClient->GetMimeTypeList()->AddType("xRooFitRooConstVar", "xRooFitRooConstVar", "stop_t.xpm", "stop_t.xpm",
1311 "->Browse()");
1312 }
1313 return "xRooFitRooConstVar";*/
1314 return "TMethodBrowsable-leaf";
1315 }
1316 if (o->InheritsFrom("RooStats::HypoTestInverterResult")) {
1317 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitScanStyle", true)) {
1318 gClient->GetMimeTypeList()->AddType("xRooFitScanStyle", "xRooFitScanStyle", "f2_s.xpm", "f2_s.xpm",
1319 "->Browse()");
1320 }
1321 return "xRooFitScanStyle";
1322 }
1323 if (o->InheritsFrom("RooStats::HypoTestResult")) {
1324 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitTestStyle", true)) {
1325 gClient->GetMimeTypeList()->AddType("xRooFitTestStyle", "xRooFitTestStyle", "diamond.xpm", "diamond.xpm",
1326 "->Browse()");
1327 }
1328 return "xRooFitTestStyle";
1329 }
1330 if (o->InheritsFrom("RooStats::HistFactory::FlexibleInterpVar"))
1331 return "TBranchElement-folder";
1332 if (o->InheritsFrom("RooAbsPdf")) {
1333 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitPDFStyle", true)) {
1334 gClient->GetMimeTypeList()->AddType("xRooFitPDFStyle", "xRooFitPDFStyle", "pdf.xpm", "pdf.xpm",
1335 "->Browse()");
1336 }
1337 return "xRooFitPDFStyle";
1338 }
1339 if (o->InheritsFrom("RooStats::ModelConfig")) {
1340 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitMCStyle", true)) {
1341 gClient->GetMimeTypeList()->AddType("xRooFitMCStyle", "xRooFitMCStyle", "app_t.xpm", "app_t.xpm",
1342 "->Browse()");
1343 }
1344 return "xRooFitMCStyle";
1345 }
1346 if (auto a = dynamic_cast<RooAbsReal *>(o); a) {
1347 if (auto _ax = GetXaxis();
1348 _ax && (a->isBinnedDistribution(*dynamic_cast<RooAbsArg *>(_ax->GetParent())) ||
1349 (dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
1350 std::unique_ptr<std::list<double>>(a->binBoundaries(
1351 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
1352 std::numeric_limits<double>::infinity()))))) {
1353 return "TH1D";
1354 }
1355 return "TF1";
1356 }
1357 return o->ClassName();
1358 }
1359 if (!IsFolder()) {
1360 return "Unknown";
1361 }
1362 return nullptr;
1363}
1364
1365const char *xRooNode::GetNodeType() const
1366{
1367 if (auto rrs = get<RooRealSumPdf>(); rrs) {
1368 // if is BinnedLikelihood show that option
1369 if (rrs->getAttribute("BinnedLikelihood"))
1370 return "BinnedLikelihood";
1371 }
1372 if (auto o = get(); o && fParent && (fParent->get<RooProduct>() || fParent->get<RooRealSumPdf>())) {
1373 if (o->InheritsFrom("RooStats::HistFactory::FlexibleInterpVar"))
1374 return "Overall";
1375 if (o->InheritsFrom("PiecewiseInterpolation")) {
1376 // check if children are all RooHistFunc ... if so, it's a HistoFactor, otherwise it's a Varied
1377 bool isHisto = true;
1378 for (auto c : const_cast<xRooNode *>(this)->browse()) {
1379 if (!c->get<RooHistFunc>()) {
1380 isHisto = false;
1381 break;
1382 }
1383 }
1384 if (isHisto) {
1385 return (dynamic_cast<RooAbsArg *>(o)->getAttribute("density")) ? "HistoDensity" : "Histo";
1386 } else {
1387 return (dynamic_cast<RooAbsArg *>(o)->getAttribute("density")) ? "VariedDensity" : "Varied";
1388 }
1389 }
1390 if (o->InheritsFrom("RooHistFunc"))
1391 return (dynamic_cast<RooAbsArg *>(o)->getAttribute("density")) ? "SimpleDensity" : "Simple";
1392 if (o->InheritsFrom("RooBinWidthFunction"))
1393 return "Density";
1394 if (o->InheritsFrom("ParamHistFunc"))
1395 return "Shape";
1396 if (o->InheritsFrom("RooRealVar"))
1397 return "Norm";
1398 if (o->InheritsFrom("RooConstVar"))
1399 return "Const";
1400 }
1401 return "";
1402}
1403
1405{
1406 xRooNode out(".coords", nullptr, *this);
1407 // go up through parents looking for slice obs
1408 auto _p = std::shared_ptr<xRooNode>(const_cast<xRooNode *>(this), [](xRooNode *) {});
1409 while (_p) {
1410 TString pName(_p->GetName());
1411 // following is commented out while still considering, but idea is to include category in coords
1412 /*if (auto s = _p->get<RooSimultaneous>(); s && s->indexCat().InheritsFrom("RooCategory") &&
1413 !out.find(s->indexCat().GetName())) { auto cat = const_cast<RooCategory*>(dynamic_cast<const
1414 RooCategory*>(&s->indexCat()));
1415 // check if we have a pdf for every category ... if not then add to cut
1416 cat->clearRange("coordRange",true);
1417 bool hasMissing = false;
1418 std::string includedStates;
1419 for (auto state : *cat) {
1420 if (!s->getPdf(state.first.c_str())) {
1421 hasMissing = true;
1422 } else {
1423 if (!includedStates.empty()) {
1424 includedStates += ",";
1425 }
1426 includedStates += state.first;
1427 }
1428 }
1429 if (hasMissing) {
1430 if(includedStates.find(",") != std::string::npos) {
1431 cat->addToRange("coordRange",includedStates.c_str());
1432 } else {
1433 cat->setLabel(includedStates);
1434 }
1435 out.emplace_back(std::make_shared<xRooNode>(cat->GetName(),_p->getObject<RooAbsArg>(cat->GetName()),_p));
1436 }
1437 } else*/
1438 if (auto pos = pName.Index('='); pos != -1) {
1439 if (pos > 0 && pName(pos - 1) == '<') {
1440 // should be a range on a real lvalue, of form low<=name<high
1441 double low = TString(pName(0, pos - 1)).Atof();
1442 pName = pName(pos + 1, pName.Length());
1443 double high = TString(pName(pName.Index('<') + 1, pName.Length())).Atof();
1444 pName = pName(0, pName.Index('<'));
1445 if (auto _obs = _p->getObject<RooAbsRealLValue>(pName.Data()); _obs) {
1446 if (setVals) {
1447 _obs->setVal((high + low) / 2.);
1448 static_cast<RooRealVar *>(_obs.get())->setRange("coordRange", low, high);
1449 _obs->setStringAttribute(
1450 "coordRange", "coordRange"); // will need if we allow multi disconnected regions, need comma list
1451 }
1452 out.emplace_back(std::make_shared<xRooNode>(_obs->GetName(), _obs, _p));
1453 } else {
1454 throw std::runtime_error(TString::Format("Unknown observable: %s", pName.Data()));
1455 }
1456
1457 } else if (auto _obs = _p->getObject<RooAbsArg>(pName(0, pos)); _obs) {
1458 if (setVals) {
1459 if (auto _cat = dynamic_cast<RooAbsCategoryLValue *>(_obs.get()); _cat) {
1460 _cat->setLabel(pName(pos + 1, pName.Length()));
1461 } else if (auto _var = dynamic_cast<RooAbsRealLValue *>(_obs.get()); _var) {
1462 _var->setVal(TString(pName(pos + 1, pName.Length())).Atof());
1463 }
1464 }
1465 out.emplace_back(std::make_shared<xRooNode>(_obs->GetName(), _obs, _p));
1466 } else {
1467 throw std::runtime_error("Unknown observable, could not find");
1468 }
1469 }
1470 _p = _p->fParent;
1471 }
1472 return out;
1473}
1474
1475void xRooNode::_Add_(const char *name, const char *opt)
1476{
1477 try {
1478 Add(name, opt);
1479 } catch (const std::exception &e) {
1480 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
1481 kMBIconExclamation); // deletes self on dismiss?
1482 }
1483}
1484void xRooNode::_Vary_(const char *what)
1485{
1486 try {
1487 Vary(what);
1488 } catch (const std::exception &e) {
1489 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
1490 kMBIconExclamation); // deletes self on dismiss?
1491 }
1492}
1493
1495{
1496
1497 if (strcmp(GetName(), ".poi") == 0) {
1498 // demote a parameter from being a poi
1499 auto toRemove =
1500 (child.get<RooAbsArg>() || !find(child.GetName())) ? child : xRooNode(find(child.GetName())->fComp);
1501 if (toRemove) {
1502 if (!toRemove.get<RooAbsArg>()->getAttribute("poi")) {
1503 throw std::runtime_error(TString::Format("%s is not a poi", toRemove.GetName()));
1504 }
1505 toRemove.get<RooAbsArg>()->setAttribute("poi", false);
1506 return toRemove;
1507 }
1508 } else if (strcmp(GetName(), ".factors") == 0 || strcmp(GetName(), ".constraints") == 0 ||
1509 strcmp(GetName(), ".components") == 0) {
1510 auto toRemove =
1511 (child.get<RooAbsArg>() || !find(child.GetName())) ? child : xRooNode(find(child.GetName())->fComp);
1512 if (auto p = fParent->get<RooProdPdf>(); p) {
1513 auto pdf = toRemove.get<RooAbsArg>();
1514 if (!pdf)
1515 pdf = p->pdfList().find(child.GetName());
1516 if (!pdf)
1517 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1518 auto i = p->pdfList().index(*pdf);
1519 if (i >= 0) {
1520#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
1521 const_cast<RooArgList &>(p->pdfList()).remove(*pdf);
1522#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
1523 p->_pdfNSetList.erase(p->_pdfNSetList.begin() + i);
1524#else
1525 auto nset = p->_pdfNSetList.At(i);
1526 p->_pdfNSetList.Remove(nset);
1527 delete nset; // I don't think the RooLinkedList owned it so must delete ourself
1528#endif
1529 if (p->_extendedIndex == i)
1530 p->_extendedIndex = -1;
1531 else if (p->_extendedIndex > i)
1532 p->_extendedIndex--;
1533#else
1534 p->removePdfs(RooArgSet(*pdf));
1535#endif
1536 sterilize();
1537 return xRooNode(*pdf);
1538 } else {
1539 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1540 }
1541 } else if (auto p2 = fParent->get<RooProduct>(); p2) {
1542 auto arg = toRemove.get<RooAbsArg>();
1543 if (!arg)
1544 arg = p2->components().find(child.GetName());
1545 if (!arg)
1546 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1547 // remove server ... doesn't seem to trigger removal from proxy
1548#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
1549 p2->_compRSet.remove(*arg);
1550#else
1551 const_cast<RooArgList &>(p2->realComponents()).remove(*arg);
1552#endif
1553 p2->removeServer(*arg, true);
1554 sterilize();
1555 return xRooNode(*arg);
1556 } else if (fParent->get<RooSimultaneous>()) {
1557 // remove from all channels
1558 bool removed = false;
1559 for (auto &c : fParent->bins()) {
1560 try {
1561 c->constraints().Remove(toRemove);
1562 removed = true;
1563 } catch (std::runtime_error &) { /* wasn't a constraint in channel */
1564 }
1565 }
1566 sterilize();
1567 if (!removed)
1568 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1569 return toRemove;
1570 } else if (auto p4 = fParent->get<RooRealSumPdf>(); p4) {
1571 auto arg = toRemove.get<RooAbsArg>();
1572 if (!arg)
1573 arg = p4->funcList().find(child.GetName());
1574 if (!arg)
1575 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1576 // remove, including coef removal ....
1577 auto idx = p4->funcList().index(arg);
1578
1579 if (idx != -1) {
1580
1581 const_cast<RooArgList &>(p4->funcList()).remove(*arg);
1582 p4->removeServer(*arg, true);
1583 // have to be careful removing coef because if shared will end up removing them all!!
1584 std::vector<RooAbsArg *> _coefs;
1585 for (size_t ii = 0; ii < const_cast<RooArgList &>(p4->coefList()).size(); ii++) {
1586 if (ii != size_t(idx))
1587 _coefs.push_back(const_cast<RooArgList &>(p4->coefList()).at(ii));
1588 }
1589 const_cast<RooArgList &>(p4->coefList()).removeAll();
1590 for (auto &a : _coefs)
1591 const_cast<RooArgList &>(p4->coefList()).add(*a);
1592
1593 sterilize();
1594 } else {
1595 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1596 }
1597 return xRooNode(*arg);
1598 } else if (auto p5 = fParent->get<RooAddPdf>(); p5) {
1599 auto arg = toRemove.get<RooAbsArg>();
1600 if (!arg)
1601 arg = p5->pdfList().find(child.GetName());
1602 if (!arg)
1603 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1604 // remove, including coef removal ....
1605 auto idx = p5->pdfList().index(arg);
1606
1607 if (idx != -1) {
1608
1609 const_cast<RooArgList &>(p5->pdfList()).remove(*arg);
1610 p5->removeServer(*arg, true);
1611 // have to be careful removing coef because if shared will end up removing them all!!
1612 std::vector<RooAbsArg *> _coefs;
1613 for (size_t ii = 0; ii < const_cast<RooArgList &>(p5->coefList()).size(); ii++) {
1614 if (ii != size_t(idx))
1615 _coefs.push_back(const_cast<RooArgList &>(p5->coefList()).at(ii));
1616 }
1617 const_cast<RooArgList &>(p5->coefList()).removeAll();
1618 for (auto &a : _coefs)
1619 const_cast<RooArgList &>(p5->coefList()).add(*a);
1620
1621 sterilize();
1622 } else {
1623 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1624 }
1625 return xRooNode(*arg);
1626 } else if (auto p6 = fParent->get<RooAddition>(); p6) {
1627 auto arg = toRemove.get<RooAbsArg>();
1628 if (!arg)
1629 arg = p6->list().find(child.GetName());
1630 if (!arg)
1631 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1632 // remove server ... doesn't seem to trigger removal from proxy
1633 const_cast<RooArgList &>(p6->list()).remove(*arg);
1634 p6->removeServer(*arg, true);
1635 sterilize();
1636 return xRooNode(*arg);
1637 }
1638 }
1639
1640 if (auto w = get<RooWorkspace>(); w) {
1641 xRooNode out(child.GetName());
1642 auto arg = w->components().find(child.GetName());
1643 if (!arg)
1644 arg = operator[](child.GetName())->get<RooAbsArg>();
1645 if (!arg) {
1646 throw std::runtime_error(TString::Format("Cannot find %s in workspace %s", child.GetName(), GetName()));
1647 }
1648 // check has no clients ... if so, cannot delete
1649 if (arg->hasClients()) {
1650 throw std::runtime_error(
1651 TString::Format("Cannot remove %s from workspace %s, because it has dependencies - first remove from those",
1652 child.GetName(), GetName()));
1653 }
1654 const_cast<RooArgSet &>(w->components()).remove(*arg); // deletes arg
1655 Info("Remove", "Deleted %s from workspace %s", out.GetName(), GetName());
1656 return out;
1657 } else if (get<RooProduct>() || get<RooProdPdf>()) {
1658 return factors().Remove(child);
1659 } else if (get<RooRealSumPdf>() || get<RooAddPdf>() || get<RooAddition>()) {
1660 return components().Remove(child);
1661 }
1662
1663 throw std::runtime_error("Removal not implemented for object type " +
1664 std::string(get() ? get()->ClassName() : "null"));
1665}
1666
1668{
1669
1670 class AutoUpdater {
1671 public:
1672 AutoUpdater(xRooNode &_n) : n(_n) {}
1673 ~AutoUpdater() { n.browse(); }
1674 xRooNode &n;
1675 };
1676 AutoUpdater xxx(*this);
1677
1678 TString sOpt(opt);
1679 bool considerType(sOpt == "+");
1680
1681 if (strlen(GetName()) > 0 && GetName()[0] == '!' && fParent) {
1682 // folder .. pass onto parent and add folder to child folder list
1683 const_cast<xRooNode &>(child).fFolder += GetName();
1684 return fParent->Add(child, opt);
1685 }
1686 // this is how to get the first real parent ... may be useful at some point?
1687 /*auto realParent = fParent;
1688 while(!realParent->get()) {
1689 realParent = realParent->fParent;
1690 if (!realParent) throw std::runtime_error("No parentage");
1691 }*/
1692
1693 // adding to a collection node will incorporate the child into the parent of the collection
1694 // in the appropriate way
1695 if (strcmp(GetName(), ".factors") == 0) {
1696 // multiply the parent
1697 return fParent->Multiply(child, opt);
1698 } else if (strcmp(GetName(), ".components") == 0) {
1699 // add to the parent
1700 return fParent->Add(child, opt);
1701 } else if (strcmp(GetName(), ".variations") == 0) {
1702 // vary the parent
1703 return fParent->Vary(child);
1704 } else if (strcmp(GetName(), ".constraints") == 0) {
1705 // constrain the parent
1706 return fParent->Constrain(child);
1707 } else if (strcmp(GetName(), ".bins") == 0 && fParent->get<RooSimultaneous>()) {
1708 // adding a channel (should adding a 'bin' be an 'Extend' operation?)
1709 return fParent->Vary(child);
1710 } else if ((strcmp(GetName(), ".globs") == 0)) {
1711 if (child.get<RooAbsArg>() || (!child.fComp && getObject<RooAbsArg>(child.GetName()))) {
1712 auto out = (child.get<RooAbsArg>()) ? child.get<RooAbsArg>() : getObject<RooAbsArg>(child.GetName()).get();
1713 out->setAttribute("obs");
1714 out->setAttribute("global");
1715 return xRooNode(*out, *this);
1716 }
1717 throw std::runtime_error("Failed to add global observable");
1718 } else if ((strcmp(GetName(), ".poi") == 0)) {
1719 if (child.get<RooAbsLValue>() || (!child.fComp && getObject<RooAbsLValue>(child.GetName()))) {
1720 auto out = (child.get<RooAbsArg>()) ? child.get<RooAbsArg>() : getObject<RooAbsArg>(child.GetName()).get();
1721 out->setAttribute("poi");
1722 return xRooNode(*out, *this);
1723 } else if (!child.get() && fParent->get<RooWorkspace>()) {
1724 // may be creating poi at same time as adding, try add to parent
1725 auto res = fParent->Add(child);
1726 if (res.get<RooAbsLValue>())
1727 return Add(res);
1728 }
1729 throw std::runtime_error("Failed to add parameter of interest");
1730 } else if ((strcmp(GetName(), ".pars") == 0 || strcmp(GetName(), ".vars") == 0) && fParent->get<RooWorkspace>()) {
1731 // adding a parameter, interpret as factory string unless no "[" then create RooRealVar
1732 TString fac(child.GetName());
1733 if (!fac.Contains("[") && !fac.Contains("("))
1734 fac += "[1]";
1735 return xRooNode(*fParent->get<RooWorkspace>()->factory(fac), fParent);
1736 } else if (strcmp(GetName(), ".datasets()") == 0) {
1737
1738 if (auto _data = child.get<RooAbsData>(); _data) {
1739 if (find(_data->GetName())) {
1740 throw std::runtime_error(TString::Format("Cannot add dataset %s, already exists for %s. If intending to "
1741 "combine datasets, please add directly to dataset",
1742 child->GetName(), GetName()));
1743 }
1744 return fParent->Add(child); // add the dataset to the parent
1745 }
1746
1747 // create a dataset - only allowed for pdfs or workspaces
1748 if (auto _ws = ws(); _ws && fParent) {
1749 sOpt.ToLower();
1750 if (!fParent->get<RooAbsPdf>() && (!fParent->get<RooWorkspace>() || sOpt == "asimov")) {
1751 throw std::runtime_error(
1752 "Datasets can only be created for pdfs or workspaces (except if generated dataset, then must be pdf)");
1753 }
1754
1755 if (sOpt == "asimov" || sOpt == "toy") {
1756 // generate expected dataset - note that globs will be frozen at this time
1757 auto _fr = fParent->fitResult();
1758 if (strlen(_fr->GetName()) == 0) { // ensure fit result has a name so that name is saved inside dataset
1759 _fr.get<RooFitResult>()->SetName(TUUID().AsString());
1760 }
1761 auto ds = fParent->generate(_fr, sOpt == "asimov");
1762 if (strlen(child.GetName())) {
1763 ds.SetName(child.GetName());
1764 ds.get<TNamed>()->SetName(child.GetName());
1765 }
1766 if (auto _ds = ds.get<RooAbsData>()) {
1767 _ws->import(*_ds);
1768 }
1769 if (_fr.get<RooFitResult>()->numStatusHistory() == 0) {
1770 if (!GETWSSNAPSHOTS(_ws).find(_fr->GetName())) {
1771 const_cast<RooLinkedList &>(GETWSSNAPSHOTS(_ws)).Add(_fr->Clone());
1772 }
1773 } else if (!_ws->obj(_fr->GetName())) {
1774 _ws->import((*_fr.get<RooFitResult>()));
1775 } // save fr to workspace, for later retrieval
1776 return xRooNode(*_ws->data(ds.GetName()), fParent);
1777 }
1778
1779 auto parentObs = fParent->obs(); // may own globs so keep alive
1780 auto _obs = parentObs.argList();
1781 // put globs in a snapshot
1782 std::unique_ptr<RooAbsCollection> _globs(_obs.selectByAttrib("global", true));
1783 // RooArgSet _tmp; _tmp.add(*_globs);_ws->saveSnapshot(child.GetName(),_tmp);
1784 _obs.remove(*_globs);
1785
1786 // include any coords
1787 _obs.add(coords(false).argList(), true);
1788 // include axis var too, provided it's an observable
1789 if (auto ax = GetXaxis(); ax && dynamic_cast<RooAbsArg *>(ax->GetParent())->getAttribute("obs")) {
1790 _obs.add(*dynamic_cast<RooAbsArg *>(ax->GetParent()));
1791 }
1792 // check if ws already has a dataset with this name, if it does we may need to extend columns
1793 if (auto _d = _ws->data(child.GetName()); _d) {
1794 // add any missing obs
1795 RooArgSet l(_obs);
1796 l.remove(*_d->get(), true, true);
1797 if (!l.empty()) {
1798 auto _dd = dynamic_cast<RooDataSet *>(_d);
1799 if (!_dd)
1800 throw std::runtime_error("Cannot extend dataset with new columns");
1801 for (auto &x : l) {
1802 _dd->addColumn(*x);
1803 }
1804 }
1805 } else {
1806 RooRealVar w("weightVar", "weightVar", 1);
1807 _obs.add(w);
1808 RooDataSet d(child.GetName(), child.GetTitle(), _obs, RooFit::WeightVar("weightVar"));
1809 _ws->import(d);
1810 // seems have to set bits after importing, not before
1811 if (auto __d = _ws->data(child.GetName()))
1812 __d->SetBit(1 << 20, _ws->allData().size() == 1); // sets as selected if is only ds
1813 }
1814 /*if(!_ws->data(child.GetName())) {
1815 RooRealVar w("weightVar", "weightVar", 1);
1816 RooArgSet _obs; _obs.add(w);
1817 RooDataSet d(child.GetName(), child.GetTitle(), _obs, "weightVar");
1818 _ws->import(d);
1819 }*/
1820 auto out = std::shared_ptr<TObject>(_ws->data(child.GetName()), [](TObject *) {});
1821
1822 if (out) {
1823 xRooNode o(out, fParent);
1824 if (child.get<TH1>())
1825 o = *child.get();
1826 return o;
1827 }
1828 }
1829 throw std::runtime_error("Cannot create dataset");
1830 }
1831
1832 if (!get()) {
1833 if (!fParent)
1834 throw std::runtime_error("Cannot add to null object with no parentage");
1835
1836 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
1837 try {
1838 fComp = fParent->Add(*this, "+").fComp;
1839 } catch (...) {
1840 resize(size() - 1);
1841 std::rethrow_exception(std::current_exception());
1842 }
1843 resize(size() - 1); // remove the temporarily added node
1844
1845 if (!fComp) {
1846 throw std::runtime_error("No object");
1847 }
1848 }
1849
1850 if (auto p = get<RooAbsData>(); p) {
1851 if (auto bb = getBrowsable(".sourceds"))
1852 bb->Add(child, opt);
1853 if (auto _data = child.get<RooDataSet>()) {
1854 auto ds = dynamic_cast<RooDataSet *>(p);
1855 if (!ds) {
1856 throw std::runtime_error("Can only add datasets to a dataset");
1857 }
1858
1859 // append any missing globs, and check any existing globs have matching values
1861 auto _globs = globs();
1862 for (auto &glob : child.globs()) {
1863 if (auto g = _globs.find(glob->GetName()); !g) {
1864 globsToAdd.addClone(*glob->get<RooAbsArg>());
1865 } else if (g->GetContent() != glob->GetContent()) {
1866 Warning("Add", "Global observable %s=%g in dataset %s mismatches %s value %g ... ignoring latter",
1867 g->GetName(), g->GetContent(), GetName(), child.GetName(), glob->GetContent());
1868 }
1869 }
1870 // add any existing globs to list then set the list
1871 if (auto _dglobs = p->getGlobalObservables()) {
1872 globsToAdd.addClone(*_dglobs);
1873 } else {
1874 for (auto g : _globs)
1875 globsToAdd.addClone(*g->get<RooAbsArg>());
1876 }
1877 p->setGlobalObservables(globsToAdd);
1878
1879 // append any missing observables to our dataset, then append the dataset
1880
1881 std::set<std::pair<RooAbsCategory *, RooCategory *>> cats;
1882
1883 for (auto col : *_data->get()) {
1884 if (!p->get()->contains(*col)) {
1885 ds->addColumn(*col);
1886 } else if (auto c = dynamic_cast<RooAbsCategory *>(col)) {
1887 // check if any of the states of c have different index to c2
1888 auto c2 = dynamic_cast<RooCategory *>(p->get()->find(*col));
1889 if (!c2) {
1890 throw std::runtime_error(
1891 TString::Format("unexpected type for regular observable: %s", col->GetName()));
1892 }
1893 bool iMatches = true;
1894 for (const auto &nameIdx : *c) {
1895 if (!c2->hasLabel(nameIdx.first) && !c2->hasIndex(nameIdx.second)) {
1896 // can define the state
1897 c2->defineType(nameIdx.first, nameIdx.second);
1898 } else if (c2->lookupIndex(nameIdx.first) != nameIdx.second) {
1899 iMatches = false;
1900 break; // state exists, but with different index!
1901 }
1902 }
1903 if (!iMatches)
1904 cats.insert({c, c2});
1905 }
1906 }
1907 if (cats.empty()) {
1908 ds->append(*_data);
1909 } else {
1910 // cannot use append, because if categoricals use same idx for different states, will not do correct thing
1911 for (int i = 0; i < _data->numEntries(); i++) {
1912 auto row = _data->get(i);
1913 auto w = _data->weight();
1914 ds->get()->assign(*row);
1915 for (auto [c, c2] : cats) {
1916 c2->setLabel(row->getCatLabel(c->GetName()));
1917 }
1918 ds->add(*ds->get(), w);
1919 }
1920 }
1921
1922 ds->SetTitle(TString(ds->GetTitle()) + " + " + _data->GetTitle());
1923 SetTitle(TString(GetTitle()) + " + " + child.GetTitle());
1924 return *this;
1925 }
1926 auto _arg = child.get<RooAbsArg>();
1927 if (auto _ds = dynamic_cast<RooDataSet *>(p); _arg && _ds) {
1928 // can add var or function of existing obs to dataset as a column
1929 _ds->addColumn(*_arg);
1930 _arg->setAttribute("obs");
1931 return xRooNode(*_arg, *this);
1932 }
1933 auto _h = child.get<TH1>();
1934 if (!_h) {
1935 throw std::runtime_error("Can only add histogram or var/expr or dataset to data");
1936 }
1937 auto _pdf = parentPdf();
1938 if (!_pdf)
1939 throw std::runtime_error("Could not find pdf");
1940 auto _ax = _pdf->GetXaxis();
1941 if (!_ax) {
1942 throw std::runtime_error("Cannot determine binning to add data");
1943 }
1944
1945 RooArgSet obs;
1946 obs.add(*dynamic_cast<RooAbsArg *>(_ax->GetParent()));
1947 obs.add(coords().argList()); // will also move obs to coords
1948
1949 // add any missing obs
1950 RooArgSet l(obs);
1951 l.remove(*p->get(), true, true);
1952 if (!l.empty()) {
1953 auto _d = dynamic_cast<RooDataSet *>(p);
1954 if (!_d)
1955 throw std::runtime_error("Cannot extend dataset with new columns");
1956 for (auto &x : l) {
1957 _d->addColumn(*x);
1958 }
1959 }
1960
1961 // before adding, ensure range is good to cover
1962 for (auto &o : obs) {
1963 if (auto v = dynamic_cast<RooRealVar *>(o); v) {
1964 if (auto dv = dynamic_cast<RooRealVar *>(p->get()->find(v->GetName())); dv) {
1965 if (v->getMin() < dv->getMin())
1966 dv->setMin(v->getMin());
1967 if (v->getMax() > dv->getMax())
1968 dv->setMax(v->getMax());
1969 }
1970 } else if (auto c = dynamic_cast<RooCategory *>(o); c) {
1971 if (auto dc = dynamic_cast<RooCategory *>(p->get()->find(c->GetName())); dc) {
1972 for (const auto &nameIdx : *c) {
1973 if (!dc->hasLabel(nameIdx.first)) {
1974 dc->defineType(nameIdx.first, nameIdx.second);
1975 }
1976 }
1977 }
1978 }
1979 }
1980
1981 for (int i = 1; i <= _h->GetNbinsX(); i++) {
1982 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(_ax->GetParent())) {
1983 if (!_h->GetXaxis()->GetBinLabel(i)) {
1984 throw std::runtime_error(
1985 TString::Format("Categorical observable %s requires bin labels", _ax->GetParent()->GetName()));
1986 } else if (!cat->hasLabel(_h->GetXaxis()->GetBinLabel(i))) {
1987 throw std::runtime_error(TString::Format("Categorical observable %s does not have label %s",
1988 _ax->GetParent()->GetName(), _h->GetXaxis()->GetBinLabel(i)));
1989 } else {
1990 cat->setLabel(_h->GetXaxis()->GetBinLabel(i));
1991 }
1992 } else {
1993 dynamic_cast<RooAbsRealLValue *>(_ax->GetParent())->setVal(_h->GetBinCenter(i));
1994 }
1995 p->add(obs, _h->GetBinContent(i));
1996 }
1997
1998 return *this;
1999 }
2000
2001 if (auto p = get<RooAddPdf>(); p) {
2002 auto cc = child.fComp;
2003 // bool isConverted = (cc != child.convertForAcquisition(*this, sOpt));
2004 child.convertForAcquisition(*this, sOpt);
2005 if ((child.get<RooAbsPdf>() || (!child.fComp && getObject<RooAbsPdf>(child.GetName())))) {
2006 auto out = (child.fComp) ? acquire(child.fComp) : getObject<RooAbsArg>(child.GetName());
2007 // don't add a coef if in 'all-extended' mode and this pdf is extendable
2008 auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out);
2009 if (!_pdf) {
2010 throw std::runtime_error("Something went wrong with pdf acquisition");
2011 }
2012
2013 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
2014 _pdf->dependsOn(*static_cast<RooAbsArg *>(_ax->GetParent()))) {
2015 auto _p = _pdf;
2016
2017 if (auto _boundaries = std::unique_ptr<std::list<double>>(_p->binBoundaries(
2018 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
2019 std::numeric_limits<double>::infinity()));
2020 !_boundaries && _ax->GetNbins() > 0) {
2021#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
2022 Warning("Add", "Adding unbinned pdf %s to binned %s - will wrap with RooBinSamplingPdf(...)",
2023 _p->GetName(), GetName());
2024 _p = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _p->GetName()), _p->GetTitle(),
2025 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *_p);
2026 _p->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
2027 if (!_p->getStringAttribute("alias"))
2028 _p->setStringAttribute("alias", out->GetName());
2029#else
2030 throw std::runtime_error(
2031 "unsupported addition of unbinned pdf to binned model - please upgrade to at least ROOT 6.24");
2032#endif
2033 _pdf = _p;
2034 }
2035 }
2036
2037 if (!(_pdf->canBeExtended() && p->coefList().empty())) {
2038 // if extended, use an extended binding as the coef
2039 // otherwise e.g. if adding a RooRealSumPdf the stacked histograms will be above the
2040 // actual pdf histogram because the pdf histogram is just normalized down
2041 if (_pdf->canBeExtended()) {
2042 // FIXME: ExtendedBinding needs the obs list passing to it ... should be fixed in RooFit
2043 std::cout << " warning " << _pdf->GetName() << " wont be correctly normalized" << std::endl;
2044 // until then, this will return "1" and so the pdf's histograms wont be normalized properly in relation
2045 // to stacks of its comps
2046 const_cast<RooArgList &>(p->coefList())
2047 .add(*acquireNew<RooExtendedBinding>(TString::Format("%s_extBind", _pdf->GetName()),
2048 TString::Format("Expected Events of %s", _pdf->GetTitle()),
2049 *_pdf));
2050 } else {
2051
2052 // need to create a coefficient for each existing pdf first, like above
2053 for (auto i = p->coefList().size(); i < p->pdfList().size(); i++) {
2054 const_cast<RooArgList &>(p->coefList())
2056 TString::Format("%s_extBind", p->pdfList().at(i)->GetName()),
2057 TString::Format("Expected Events of %s", p->pdfList().at(i)->GetTitle()),
2058 *static_cast<RooAbsPdf *>(p->pdfList().at(i))));
2059 }
2060
2061 const_cast<RooArgList &>(p->coefList()).add(*acquire2<RooAbsArg, RooRealVar>("1", "1", 1));
2062 }
2063 // ensure not in no-coef mode any more
2064 *reinterpret_cast<bool *>(reinterpret_cast<unsigned char *>(p) +
2065 p->Class()->GetDataMemberOffset("_allExtendable")) = false;
2066 *reinterpret_cast<bool *>(reinterpret_cast<unsigned char *>(p) +
2067 p->Class()->GetDataMemberOffset("_haveLastCoef")) = true;
2068 }
2069 const_cast<RooArgList &>(p->pdfList()).add(*_pdf);
2070 sterilize();
2071 return xRooNode(*_pdf, *this);
2072 } else if ((child.get<TH1>() || child.get<RooAbsReal>() ||
2073 (!child.get() && getObject<RooAbsReal>(child.GetName()))) &&
2074 !child.get<RooAbsPdf>()) {
2075 RooRealSumPdf *_pdf = nullptr;
2076 bool tooMany(false);
2077 for (auto &pp : factors()) {
2078 if (auto _p = pp->get<RooRealSumPdf>(); _p) {
2079 if (_pdf) {
2080 _pdf = nullptr;
2081 tooMany = true;
2082 break;
2083 } // more than one!
2084 _pdf = _p;
2085 }
2086 }
2087 if (_pdf) {
2088 return xRooNode(*_pdf, *this).Add(child);
2089 } else if (!tooMany) {
2090 // create a RooRealSumPdf to hold the child
2091 auto _sumpdf = Add(*acquireNew<RooRealSumPdf>(TString::Format("%s_samples", p->GetName()),
2092 TString::Format("%s samples", GetTitle()), RooArgList(),
2093 RooArgList(), true));
2094 _sumpdf.get<RooAbsArg>()->setStringAttribute("alias", "samples");
2095 return _sumpdf.Add(child);
2096 }
2097 }
2098 }
2099
2100 if (auto p = get<RooRealSumPdf>(); p) {
2101 std::shared_ptr<TObject> out;
2102 auto cc = child.fComp;
2103 bool isConverted = (cc != child.convertForAcquisition(*this, sOpt));
2104 if (child.get<RooAbsReal>()) {
2105 out = acquire(child.fComp);
2106 if (std::dynamic_pointer_cast<TH1>(cc) && !TString(cc->GetOption()).Contains("nostyle")) {
2107 xRooNode(out, *this).styles(cc.get()); // transfer style if adding a histogram
2108 }
2109 }
2110 if (!child.fComp && getObject<RooAbsReal>(child.GetName())) {
2111 Info("Add", "Adding existing function %s to %s", child.GetName(), p->GetName());
2112 out = getObject<RooAbsReal>(child.GetName());
2113 }
2114
2115 if (!out && !child.fComp) {
2116 std::shared_ptr<RooAbsArg> _func;
2117 // a null node .. so create either a new RooProduct or RooHistFunc if has observables (or no deps but has
2118 // x-axis)
2119 auto _obs = robs();
2120 if (!_obs.empty() || GetXaxis()) {
2121 if (_obs.empty()) {
2122 // using X axis to construct hist
2123 auto _ax = dynamic_cast<Axis2 *>(GetXaxis());
2124 std::unique_ptr<TH1D> h;
2125 {
2126 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
2127 h = std::make_unique<TH1D>(child.GetName(), child.GetTitle(), _ax->GetNbins(),
2128 _ax->binning()->array());
2129 }
2130 h->GetXaxis()->SetName(TString::Format("%s;%s", _ax->GetParent()->GetName(), _ax->GetName()));
2131 // technically convertForAcquisition has already acquired so no need to re-acquire but should be harmless
2132 _func = std::dynamic_pointer_cast<RooAbsArg>(acquire(xRooNode(*h).convertForAcquisition(*this)));
2133 } else if (_obs.size() == 1) {
2134 // use the single obs to make a TH1D
2135 auto _x = _obs.at(0)->get<RooAbsLValue>();
2136 auto _bnames = _x->getBinningNames();
2137 TString binningName = p->getStringAttribute("binning");
2138 for (auto &b : _bnames) {
2139 if (b == p->GetName()) {
2140 binningName = p->GetName();
2141 break;
2142 }
2143 }
2144 std::unique_ptr<TH1D> h;
2145 {
2146 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
2147 h = std::make_unique<TH1D>(child.GetName(), child.GetTitle(), _x->numBins(binningName),
2148 _x->getBinningPtr(binningName)->array());
2149 }
2150 h->GetXaxis()->SetName(
2151 TString::Format("%s;%s", dynamic_cast<TObject *>(_x)->GetName(), binningName.Data()));
2152 // technically convertForAcquisition has already acquired so no need to re-acquire but should be harmless
2153 _func = std::dynamic_pointer_cast<RooAbsArg>(acquire(xRooNode(*h).convertForAcquisition(*this)));
2154 Info("Add", "Created SimpleDensity factor %s (xaxis=%s) for %s", _func->GetName(), _obs.at(0)->GetName(),
2155 p->GetName());
2156 } else {
2157 throw std::runtime_error("Unsupported creation of new component in SumPdf for this many obs");
2158 }
2159 } else {
2160 _func = acquireNew<RooProduct>(TString::Format("%s_%s", p->GetName(), child.GetName()), child.GetTitle(),
2161 RooArgList());
2162 }
2163 _func->setStringAttribute("alias", child.GetName());
2164 out = _func;
2165 }
2166
2167 if (auto _f = std::dynamic_pointer_cast<RooHistFunc>(
2168 (child.get<RooProduct>()) ? child.factors()[child.GetName()]->fComp : out);
2169 _f) {
2170 // adding a histfunc directly to a sumpdf, should be a density
2171 _f->setAttribute("density");
2172 if (_f->getAttribute("autodensity")) {
2173 // need to divide by bin widths first
2174 for (int i = 0; i < _f->dataHist().numEntries(); i++) {
2175 auto bin_pars = _f->dataHist().get(i);
2176 _f->dataHist().set(*bin_pars, _f->dataHist().weight(i) / _f->dataHist().binVolume(*bin_pars));
2177 }
2178 _f->setAttribute("autodensity", false);
2179 _f->setValueDirty();
2180 }
2181
2182 // promote the axis vars to observables
2183 // can't use original child as might refer to unacquired deps
2184 for (auto &x : xRooNode("tmp", _f).vars()) {
2185 x->get<RooAbsArg>()->setAttribute("obs");
2186 }
2187 if (isConverted) {
2188 Info("Add", "Created %s factor RooHistFunc::%s for %s",
2189 _f->getAttribute("density") ? "SimpleDensity" : "Simple", _f->GetName(), p->GetName());
2190 }
2191 }
2192
2193 if (auto _p = std::dynamic_pointer_cast<RooAbsPdf>(out); _p) {
2194 // adding a pdf to a RooRealSumPdf will replace it with a RooAddPdf and put the RooRealSumPdf inside that
2195 // if pdf is extended will use in the "no coefficients" state, where the expectedEvents are taking from
2196 // the pdf integrals
2197 TString newName(p->GetName());
2198 newName.ReplaceAll("_samples", "");
2199 newName += "_components";
2200 Warning("Add", "converting samples to components");
2201
2202 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
2203 _p->dependsOn(*static_cast<RooAbsArg *>(_ax->GetParent()))) {
2204
2205 if (auto _boundaries = std::unique_ptr<std::list<double>>(_p->binBoundaries(
2206 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
2207 std::numeric_limits<double>::infinity()));
2208 !_boundaries && _ax->GetNbins() > 0) {
2209#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
2210 Warning("Add", "Adding unbinned pdf %s to binned %s - will wrap with RooBinSamplingPdf(...)",
2211 _p->GetName(), GetName());
2212 _p = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _p->GetName()), _p->GetTitle(),
2213 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *_p);
2214 _p->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
2215 if (!_p->getStringAttribute("alias"))
2216 _p->setStringAttribute("alias", out->GetName());
2217#else
2218 throw std::runtime_error(
2219 "unsupported addition of unbinned pdf to binned model - please upgrade to at least ROOT 6.24");
2220#endif
2221 }
2222 }
2223
2224 // require to be extended to be in coefficient-free mode ...
2225 // otherwise would lose the integral of the sumPdf (can't think of way to have a coef be the integral)
2226 if (!_p->canBeExtended()) {
2227 _p = acquireNew<RooExtendPdf>(TString::Format("%s_extended", _p->GetName()), _p->GetTitle(), *_p,
2228 *acquire2<RooAbsReal, RooRealVar>("1", "1", 1));
2229 }
2230
2231 return *(Replace(*acquireNew<RooAddPdf>(newName, _p->GetTitle(), RooArgList(*p, *_p)))
2232 .browse()[1]); // returns second node.
2233 }
2234
2235 if (auto _f = std::dynamic_pointer_cast<RooAbsReal>(out); _f) {
2236
2237 // todo: if adding a pdf, should actually replace RooRealSumPdf with a RooAddPdf and put
2238 // the sumPdf and *this* pdf inside that pdf
2239 // only exception is the binSamplingPdf below to integrate unbinned functions across bins
2240
2241 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
2242 _f->dependsOn(*static_cast<RooAbsArg *>(_ax->GetParent()))) {
2243
2244 if (auto _boundaries = std::unique_ptr<std::list<double>>(_f->binBoundaries(
2245 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
2246 std::numeric_limits<double>::infinity()));
2247 !_boundaries && _ax->GetNbins() > 0) {
2248#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
2249 Warning(
2250 "Add",
2251 "Adding unbinned function %s to binned %s - will wrap with RooRealSumPdf(RooBinSamplingPdf(...))",
2252 _f->GetName(), GetName());
2253 auto sumPdf = acquireNew<RooRealSumPdf>(TString::Format("%s_pdfWrapper", _f->GetName()), _f->GetTitle(),
2254 *_f, *acquire2<RooAbsArg, RooRealVar>("1", "1", 1), true);
2255 sumPdf->setStringAttribute("alias", _f->getStringAttribute("alias"));
2256 if (!sumPdf->getStringAttribute("alias"))
2257 sumPdf->setStringAttribute("alias", out->GetName());
2258 _f = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _f->GetName()), _f->GetTitle(),
2259 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *sumPdf);
2260 _f->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
2261 if (!_f->getStringAttribute("alias"))
2262 _f->setStringAttribute("alias", out->GetName());
2263#else
2264 throw std::runtime_error(
2265 "unsupported addition of unbinned function to binned model - please upgrade to at least ROOT 6.24");
2266#endif
2267 }
2268 }
2269
2270 const_cast<RooArgList &>(p->coefList()).add(*acquire2<RooAbsArg, RooRealVar>("1", "1", 1));
2271 const_cast<RooArgList &>(p->funcList()).add(*_f);
2272 // inherit binning if we dont have one yet
2273 if (!p->getStringAttribute("binning"))
2274 p->setStringAttribute("binning", _f->getStringAttribute("binning"));
2275
2276 xRooNode _out(_f, *this);
2277 if (auto gf = p->getStringAttribute("global_factors"); gf) {
2278 TStringToken pattern(gf, ";");
2279 while (pattern.NextToken()) {
2280 auto fac = getObject<RooAbsReal>(pattern.Data());
2281 if (!fac) {
2282 throw std::runtime_error(TString::Format("Could not find global factor %s", pattern.Data()));
2283 }
2284 _out.Multiply(fac);
2285 }
2286 }
2287 sterilize();
2288 // clear children for reload and update shared axis
2289 clear();
2290 fXAxis.reset();
2291 p->setStringAttribute("xvar", nullptr);
2292 browse();
2293 return _out;
2294 }
2295 } else if (auto p2 = get<RooProdPdf>(); p2) {
2296 // can "add" to a RooProdPdf provided trying to add a RooAbsReal not a RooAbsPdf and have a zero or 1
2297 // RooRealSumPdf child.convertForAcquisition(*this); - don't convert here because want generated objects named
2298 // after roorealsumpdf
2299 // would like exception is if child is a factory string! - TODO
2300 if (child.get<RooAbsPdf>() || (!child.get() && getObject<RooAbsPdf>(child.GetName()))) {
2301 // can add if 0 or 1 RooAddPdf ....
2302 RooAddPdf *_pdf = nullptr;
2303 bool tooMany(false);
2304 for (auto &pp : factors()) {
2305 if (auto _p = pp->get<RooAddPdf>(); _p) {
2306 if (_pdf) {
2307 _pdf = nullptr;
2308 tooMany = true;
2309 break;
2310 } // more than one!
2311 _pdf = _p;
2312 }
2313 }
2314 if (_pdf) {
2315 return xRooNode(*_pdf, *this).Add(child);
2316 } else if (!tooMany) {
2317 auto out = this->operator[]("components")->Add(child);
2318 return out;
2319 }
2320 } else if ((child.get<TH1>() || child.get<RooAbsReal>() ||
2321 (!child.get() && getObject<RooAbsReal>(child.GetName()))) &&
2322 !child.get<RooAbsPdf>()) {
2323 RooRealSumPdf *_pdf = nullptr;
2324 RooAddPdf *_backup = nullptr;
2325 bool tooMany(false);
2326 for (auto &pp : factors()) {
2327 if (auto _p = pp->get<RooRealSumPdf>(); _p) {
2328 if (_pdf) {
2329 _pdf = nullptr;
2330 tooMany = true;
2331 break;
2332 } // more than one!
2333 _pdf = _p;
2334 } else if (auto _p2 = pp->get<RooAddPdf>(); _p2) {
2335 _backup = _p2;
2336 for (auto &_pdfa : pp->components()) {
2337 if (auto _p3 = _pdfa->get<RooRealSumPdf>(); _p3) {
2338 if (_pdf) {
2339 _pdf = nullptr;
2340 tooMany = true;
2341 break;
2342 } // more than one!
2343 _pdf = _p3;
2344 }
2345 }
2346 }
2347 }
2348 if (_pdf) {
2349 return xRooNode(*_pdf, *this).Add(child);
2350 } else if (_backup) {
2351 // added *INSIDE* the addPdf -- will create a RooRealSumPdf to hold it
2352 return xRooNode(*_backup, *this).Add(child);
2353 } else if (!tooMany) {
2354 auto out = this->operator[]("samples")->Add(child);
2355 // clear our x-axis to re-evaluate
2356 fXAxis.reset();
2357 p2->setStringAttribute("xvar", nullptr);
2358 return out;
2359 }
2360 }
2361 } else if (auto s = get<RooSimultaneous>(); s) {
2362
2363 // adding to a simultaneous means adding a bin
2364 return bins().Add(child);
2365
2366 // if the child is a RooAbsPdf can just add it as a new channel using name of pdf as the channel name
2367 // if child is a histogram, will create a RooProdPdf
2368
2369 } else if (auto w = get<RooWorkspace>(); w) {
2370 child.convertForAcquisition(
2371 *this, child.get() ? "" : "func" /* if child is a string, allow it to be passed to factory */);
2372 if (child.get()) {
2373 if (auto _d = child.get<RooAbsData>()) {
2374 // don't use acquire method to import, because that adds datasets as Embeddded
2375 if (!w->import(*_d)) {
2376 // should upgrade vars with any obs from the dataset
2377 if (_d->get()) {
2378 std::unique_ptr<RooAbsCollection>(w->allVars().selectCommon(*_d->get()))->setAttribAll("obs");
2379 }
2380 if (_d->getGlobalObservables()) {
2381 std::unique_ptr<RooAbsCollection> globs(w->allVars().selectCommon(*_d->getGlobalObservables()));
2382 globs->setAttribAll("obs");
2383 globs->setAttribAll("global");
2384 }
2385 return xRooNode(child.GetName(), *w->data(child.GetName()), *this);
2386 } else {
2387 throw std::runtime_error(
2388 TString::Format("Could not import dataset %s into workspace %s", child.GetName(), w->GetName())
2389 .Data());
2390 }
2391 } else {
2392 auto out = acquire(child.fComp);
2393 if (out)
2394 return xRooNode(child.GetName(), out, *this);
2395 }
2396 }
2397
2398 if (!child.empty() || child.fFolder == "!pdfs") {
2399 // create a RooSimultaneous using the children as the channels
2400 // children either have "=" in name if specifying channel cat name or otherwise assume
2401 std::string catName = "channelCat";
2402 if (!child.empty()) {
2403 if (TString ss = child.at(0)->GetName(); ss.Contains("=")) {
2404 catName = ss(0, ss.Index('='));
2405 }
2406 }
2407 auto _cat = acquire<RooCategory>(catName.c_str(), catName.c_str());
2408 _cat->setAttribute("obs");
2409 auto out = acquireNew<RooSimultaneous>(child.GetName(), child.GetTitle(), *_cat);
2410 Info("Add", "Created pdf RooSimultaneous::%s in workspace %s", out->GetName(), w->GetName());
2411 return xRooNode(out, *this);
2412 }
2413 } else if (auto coll = get<RooAbsCollection>(); coll && child.get<RooAbsArg>()) {
2414 if (coll->isOwning()) {
2415 coll->addOwned(*static_cast<RooAbsArg *>(child.get<RooAbsArg>()->Clone()));
2416 } else if (child.ws() != ws()) {
2417 coll->add(*static_cast<RooAbsArg *>(acquire(child.fComp).get()));
2418 } else {
2419 coll->add(*child.get<RooAbsArg>());
2420 }
2421 return xRooNode(child.GetName(), *coll->find(child.GetName()), *this);
2422 }
2423
2424 if (sOpt == "pdf") {
2425 // can only add a pdf to a workspace
2426 if (get<RooWorkspace>()) {
2427 const_cast<xRooNode &>(child).fFolder = "!pdfs";
2428 return Add(child);
2429 }
2430 } else if (sOpt == "channel") {
2431 // can add to a model or to a workspace (creates a RooProdPdf either way)
2432 if (get<RooSimultaneous>()) {
2433 return Vary(child);
2434 } else if (get<RooWorkspace>()) {
2435 std::shared_ptr<TObject> out;
2436 child.convertForAcquisition(*this);
2437 if (child.get<RooAbsPdf>()) {
2438 out = acquire(child.fComp);
2439 } else if (!child.fComp) {
2440 out = acquireNew<RooProdPdf>(child.GetName(),
2441 (strlen(child.GetTitle())) ? child.GetTitle() : child.GetName(), RooArgList());
2442 Info("Add", "Created channel RooProdPdf::%s in workspace %s", out->GetName(), get()->GetName());
2443 }
2444 return xRooNode(out, *this);
2445 }
2446 } else if (sOpt == "sample" || sOpt == "func") {
2447 if (get<RooProdPdf>()) {
2448 auto _mainChild = mainChild();
2449 if (_mainChild.get<RooRealSumPdf>()) {
2450 return _mainChild.Add(child, sOpt == "func" ? "func" : "");
2451 } else {
2452 return (*this)["samples"]->Add(child, sOpt == "func" ? "func" : "");
2453 }
2454 }
2455 } else if (sOpt == "dataset") {
2456 if (get<RooWorkspace>()) {
2457 // const_cast<xRooNode&>(child).fFolder = "!datasets";return Add(child);
2458 return (*this).datasets().Add(child);
2459 }
2460 }
2461
2462 if (considerType) {
2463
2464 // interpret 'adding' here as dependent on the object type ...
2465 if (get<RooSimultaneous>()) {
2466 return bins().Add(child);
2467 } else if (TString(child.GetName()).Contains('=')) {
2468 return variations().Add(child);
2469 } else if (get<RooProduct>() || get<RooProdPdf>()) {
2470 return factors().Add(child);
2471 }
2472 }
2473
2474 // Nov 2022 - removed ability to add placeholders ... could bring back if rediscover need for them
2475 // if (!child.get() && child.empty() && strlen(child.GetName())) {
2476 // // can add a 'placeholder' node, note it will be deleted at the next browse
2477 // xRooNode out(child.GetName(),nullptr,*this);
2478 // out.SetTitle(child.GetTitle());
2479 // emplace_back(std::make_shared<xRooNode>(out));
2480 // // update the parent in the out node so that it's copy of the parent knows it has itself in it
2481 // // actually maybe not want this :-/
2482 // //out.fParent = std::make_shared<Node2>(*this);
2483 // for(auto o : *gROOT->GetListOfBrowsers()) {
2484 // if(auto b = dynamic_cast<TBrowser*>(o); b && b->GetBrowserImp()){
2485 // if(auto _b = dynamic_cast<TGFileBrowser*>(
2486 // dynamic_cast<TRootBrowser*>(b->GetBrowserImp())->fActBrowser ); _b) {
2487 // auto _root = _b->fRootDir;
2488 // if (!_root) _root = _b->fListTree->GetFirstItem();
2489 // if (auto item = _b->fListTree->FindItemByObj(_root,this); item) {
2490 // _b->fListTree->AddItem(item,back()->GetName(),back().get());
2491 // }
2492 // }
2493 // }
2494 // }
2495 // return out;
2496 // }
2497
2498 throw std::runtime_error(TString::Format("Cannot add %s to %s", child.GetName(), GetName()));
2499}
2500
2501std::string xRooNode::GetPath() const
2502{
2503 if (!fParent)
2504 return GetName();
2505 return fParent->GetPath() + "/" + GetName();
2506}
2507
2509{
2510 // std::cout << "deleting " << GetPath() << std::endl;
2511}
2512
2514{
2515 if (auto a = get<RooAbsArg>()) {
2516 a->setAttribute("hidden", set);
2517 // if(auto item = GetTreeItem(nullptr); item) {
2518 // if(set) item->SetColor(kRed);
2519 // else item->ClearColor();
2520 // }
2521 }
2522}
2524{
2525 auto a = get<RooAbsArg>();
2526 if (a)
2527 return a->getAttribute("hidden");
2528 return false;
2529}
2530
2532{
2533
2534 if (get() == rhs.get()) {
2535 // nothing to do because objects are identical
2536 return *this;
2537 }
2538
2539 if (auto lhsa = get<RooAbsArg>(), rhsa = rhs.get<RooAbsArg>(); lhsa && rhsa && lhsa->isIdentical(*rhsa)) {
2540 return *this;
2541 }
2542
2543 if (get<RooWorkspace>() && rhs.get<RooWorkspace>()) {
2544
2545 // report which top-level pdfs will be combined
2546 std::set<std::string> pdfs;
2547 for (auto &c : rhs.components()) {
2548 if ((*this)["pdfs"]->find(c->GetName())) {
2549 pdfs.insert(c->GetName());
2550 }
2551 }
2552 if (pdfs.empty()) {
2553 Warning("Combine", "No pdfs will be combined. Please check and/or rename pdfs to match");
2554 } else {
2555 std::stringstream s;
2556 for (auto &p : pdfs)
2557 s << p << ",";
2558 Info("Combine", "pdfs that will be combined: %s", s.str().c_str());
2559 }
2560
2561 std::set<std::string> _np;
2562 auto mynp = np();
2563 for (auto &c : rhs.np()) {
2564 if (mynp.find(c->GetName())) {
2565 _np.insert(c->GetName());
2566 }
2567 }
2568 if (_np.empty()) {
2569 Warning("Combine", "No correlated np");
2570 } else {
2571 std::stringstream s;
2572 for (auto &p : _np)
2573 s << p << ",";
2574 Info("Combine", "np that will be shared (correlated): %s", s.str().c_str());
2575 }
2576 std::set<std::string> _poi;
2577 auto mypoi = poi();
2578 for (auto &c : rhs.poi()) {
2579 if (mypoi.find(c->GetName())) {
2580 _poi.insert(c->GetName());
2581 }
2582 }
2583 if (_poi.empty()) {
2584 Warning("Combine", "No correlated poi");
2585 } else {
2586 std::stringstream s;
2587 for (auto &p : _poi)
2588 s << p << ",";
2589 Info("Combine", "poi that will be shared (correlated): %s", s.str().c_str());
2590 }
2591
2592 // TODO: Check for derived components that have matching names and aren't top-level pdfs
2593 //
2594 // const auto comps = get<RooWorkspace>()->components();
2595 //
2596 // std::set<std::string> leafs;
2597 //
2598 // for(auto& c : rhs.get<RooWorkspace>()->components()) {
2599 // if(comps.find(c->GetName())) {
2600 //
2601 // }
2602 // }
2603 }
2604
2605 // combine components, factors, and variations ... when there is a name clash will combine on that object
2606 for (auto &c : rhs.components()) {
2607 if (get<RooWorkspace>() &&
2608 (c->fFolder == "!scratch" || c->fFolder == "!sets" || c->fFolder == "!snapshots" || c->fFolder == "!models"))
2609 continue;
2610 if (auto _c = components().find(c->GetName()); _c) {
2611 if (!silent) {
2612 Info("Combine", "Combining %s into %s", c->GetPath().c_str(), _c->GetPath().c_str());
2613 }
2614 _c->Combine(*c, true);
2615 } else {
2616 try {
2617 if (!silent) {
2618 Info("Combine", "Adding %s into %s", c->GetPath().c_str(), GetPath().c_str());
2619 }
2620 Add(*c);
2621 } catch (std::exception &e) {
2622 Warning("Combine", "Could not combine %s into %s", c->GetPath().c_str(), GetPath().c_str());
2623 }
2624 }
2625 }
2626
2627 if (!get<RooWorkspace>()) { // don't combine factors of a workspace
2628 for (auto &f : rhs.factors()) {
2629 if (auto _f = factors().find(f->GetName()); _f) {
2630 if (!silent) {
2631 Info("Combine", "Combining %s into %s", f->GetPath().c_str(), _f->GetPath().c_str());
2632 }
2633 _f->Combine(*f, true);
2634 } else {
2635 if (!silent) {
2636 Info("Combine", "Multiplying %s into %s", f->GetPath().c_str(), GetPath().c_str());
2637 }
2638 Multiply(*f);
2639 }
2640 }
2641 } else {
2642 // todo: go back through components, doing sets, snapshots, models, ....
2643 // do after import of pdfs etc so that can acquire the copies from the workspace
2644 for (auto &c : rhs.components()) {
2645 if (c->fFolder == "!sets") {
2646 if (components().find(c->GetName())) {
2647 Info("Combine", "Extending set %s", c->GetName());
2648 get<RooWorkspace>()->extendSet(c->GetName(), c->get<RooAbsCollection>()->contentsString().c_str());
2649 } else {
2650 Info("Combine", "Defining set %s", c->GetName());
2651 get<RooWorkspace>()->defineSet(c->GetName(), c->get<RooAbsCollection>()->contentsString().c_str());
2652 }
2653 }
2654 }
2655
2656 // also transfer datasets
2657 for (auto &ds : rhs.datasets()) {
2658 if (auto _ds = datasets().find(ds->GetName()); _ds) {
2659 if (!silent) {
2660 Info("Combine", "Combining %s into %s", ds->GetPath().c_str(), _ds->GetPath().c_str());
2661 }
2662 _ds->Add(*ds);
2663 } else {
2664 if (!silent) {
2665 Info("Combine", "Adding %s into %s", ds->GetPath().c_str(), GetPath().c_str());
2666 }
2667 datasets().Add(*ds);
2668 }
2669 }
2670 }
2671
2672 for (auto &v : rhs.variations()) {
2673 if (auto _v = variations().find(v->GetName()); _v) {
2674 if (!silent) {
2675 Info("Combine", "Combining variation %s into %s", v->GetPath().c_str(), _v->GetPath().c_str());
2676 }
2677 _v->Combine(*v, true);
2678 } else {
2679 if (!silent) {
2680 Info("Combine", "Varying %s into %s", v->GetPath().c_str(), GetPath().c_str());
2681 }
2682 Vary(*v);
2683 }
2684 }
2685
2686 if (get<RooSimultaneous>()) {
2687 // combine bins (channels) ... special case, never done silently
2688 for (auto &b : rhs.bins()) {
2689 if (auto _b = bins().find(b->GetName()); _b) {
2690 Info("Combine", "Combining %s into %s", b->GetPath().c_str(), _b->GetPath().c_str());
2691 _b->Combine(*b, true);
2692 } else {
2693 Info("Combine", "Extending with %s into %s", b->GetPath().c_str(), GetPath().c_str());
2694 Vary(*b); // extending channels currently done through Vary method
2695 }
2696 }
2697 }
2698
2699 // todo: Should also transfer over binnings of observables
2700
2701 return *this;
2702}
2703
2704xRooNode xRooNode::shallowCopy(const std::string &name, std::shared_ptr<xRooNode> parent)
2705{
2706 xRooNode out(name.c_str(), nullptr,
2707 parent /*? parent : fParent -- was passing fParent for getObject benefit before fProvider concept*/);
2708 // if(!parent) out.fAcquirer = true;
2709 if (!parent)
2710 out.fProvider = fParent;
2711
2712 auto o = get();
2713 if (!o) {
2714 return out;
2715 }
2716
2717 if (auto s = get<RooSimultaneous>(); s) {
2718 auto chans = bins();
2719 if (!chans.empty()) {
2720 // create a new RooSimultaneous with shallow copies of each channel
2721
2722 std::shared_ptr<RooSimultaneous> pdf = out.acquire<RooSimultaneous>(
2723 name.c_str(), o->GetTitle(), const_cast<RooAbsCategoryLValue &>(s->indexCat()));
2724
2725 for (auto &c : chans) {
2726 TString cName(c->GetName());
2727 cName = cName(cName.Index('=') + 1, cName.Length());
2728 // by passing out as the parent, will ensure out acquires everything created
2729 auto c_copy =
2730 c->shallowCopy(name + "_" + c->get()->GetName(), std::shared_ptr<xRooNode>(&out, [](xRooNode *) {}));
2731 pdf->addPdf(*dynamic_cast<RooAbsPdf *>(c_copy.get()), cName);
2732 }
2733 out.fComp = pdf;
2734 return out;
2735 }
2736 } else if (auto p = dynamic_cast<RooProdPdf *>(o); p) {
2737 // main pdf will be copied too
2738 std::shared_ptr<RooProdPdf> pdf =
2739 std::dynamic_pointer_cast<RooProdPdf>(out.acquire(std::shared_ptr<TObject>(p->Clone(/*name.c_str()*/)), false,
2740 true)); // use clone to copy all attributes etc too
2741 auto main = mainChild();
2742 if (main) {
2743 auto newMain =
2744 std::dynamic_pointer_cast<RooAbsArg>(out.acquire(std::shared_ptr<TObject>(main->Clone()), false, true));
2745 std::cout << newMain << " " << newMain->GetName() << std::endl;
2746 // pdf->replaceServer(*pdf->pdfList().find(main->GetName()), *newMain, true, true);
2747 // const_cast<RooArgList&>(pdf->pdfList()).replace(*pdf->pdfList().find(main->GetName()), *newMain);
2748 pdf->redirectServers(RooArgList(*newMain));
2749 }
2750 out.fComp = pdf;
2751 out.sterilize();
2752 return out;
2753 }
2754
2755 return out;
2756}
2757
2759{
2760 static std::unique_ptr<cout_redirect> capture;
2761 std::string captureStr;
2762 bool doCapture = false;
2763 if (!capture && gROOT->FromPopUp()) { // FromPopUp means user executed from the context menu
2764 capture = std::make_unique<cout_redirect>(captureStr);
2765 doCapture = true;
2766 }
2767
2768 TString sOpt(opt);
2769 int depth = 0;
2770 if (sOpt.Contains("depth=")) {
2771 depth = TString(sOpt(sOpt.Index("depth=") + 6, sOpt.Length())).Atoi();
2772 sOpt.ReplaceAll(TString::Format("depth=%d", depth), "");
2773 }
2774 int indent = 0;
2775 if (sOpt.Contains("indent=")) {
2776 indent = TString(sOpt(sOpt.Index("indent=") + 7, sOpt.Length())).Atoi();
2777 sOpt.ReplaceAll(TString::Format("indent=%d", indent), "");
2778 }
2779 bool _more = sOpt.Contains("m");
2780 if (_more)
2781 sOpt.Replace(sOpt.Index("m"), 1, "");
2782 if (sOpt != "")
2783 _more = true;
2784
2785 if (indent == 0) { // only print self if not indenting (will already be printed above if tree traverse)
2786 std::cout << GetPath();
2787 if (get() && get() != this) {
2788 std::cout << ": ";
2789 if (_more || (get<RooAbsArg>() && get<RooAbsArg>()->isFundamental()) || get<RooConstVar>() ||
2791 auto _deps = coords(false).argList(); // want to revert coords after print
2792 auto _snap = std::unique_ptr<RooAbsCollection>(_deps.snapshot());
2793 coords(); // move to coords before printing (in case this matters)
2794 get()->Print(sOpt);
2795 if (auto _fr = get<RooFitResult>(); _fr && dynamic_cast<RooStringVar *>(_fr->constPars().find(".log"))) {
2796 std::cout << "Minimization Logs:" << std::endl;
2797 std::cout << dynamic_cast<RooStringVar *>(_fr->constPars().find(".log"))->getVal() << std::endl;
2798 }
2799 _deps.assignValueOnly(*_snap);
2800 // std::cout << std::endl;
2801 } else {
2802 TString _suffix = "";
2803 if (auto _type = GetNodeType(); strlen(_type)) {
2804 // decided not to show const values until figure out how to update if value changes
2805 /*if (TString(_type)=="Const") _name += TString::Format("
2806 [%s=%g]",_type,v->get<RooConstVar>()->getVal()); else*/
2807 _suffix += TString::Format(" [%s]", _type);
2808 }
2809 if (auto fv = get<RooFormulaVar>()) {
2810 TString formu = TString::Format(" [%s]", fv->expression());
2811 for (size_t i = 0; i < fv->dependents().size(); i++) {
2812 formu.ReplaceAll(TString::Format("x[%zu]", i), fv->dependents()[i].GetName());
2813 }
2814 _suffix += formu;
2815 } else if (auto gv = get<RooGenericPdf>()) {
2816 TString formu = TString::Format(" [%s]", gv->expression());
2817 for (size_t i = 0; i < gv->dependents().size(); i++) {
2818 formu.ReplaceAll(TString::Format("x[%zu]", i), gv->dependents()[i].GetName());
2819 }
2820 _suffix += formu;
2821 } else if (auto pi = get<PiecewiseInterpolation>()) {
2822 // check if all interpCodes are the same. Will include in the NodeType
2823 std::set<int> interpCodes;
2824 for (auto &c : pi->interpolationCodes())
2825 interpCodes.insert(c);
2826 if (interpCodes.size() == 1) {
2827 _suffix += TString::Format(" [InterpCode=%d]", *interpCodes.begin());
2828 }
2830 // check if all interpCodes are the same.
2831 std::set<int> interpCodes;
2832 for (auto &c : fiv->interpolationCodes())
2833 interpCodes.insert(c == 4 ? 5 : c); // in definition of FlexibleInterpVar 4 gets replaced with 5
2834 if (interpCodes.size() == 1) {
2835 _suffix += TString::Format(" [InterpCode=%d]", *interpCodes.begin());
2836 }
2837 }
2838 std::cout << get()->ClassName() << "::" << get()->GetName() << _suffix.Data() << std::endl;
2839 }
2840
2841 } else if (!get()) {
2842 std::cout << std::endl;
2843 }
2844 }
2845 const_cast<xRooNode *>(this)->browse();
2846 std::vector<std::string> folderNames;
2847 for (auto &k : *this) {
2848 if (std::find(folderNames.begin(), folderNames.end(), k->fFolder) == folderNames.end()) {
2849 folderNames.push_back(k->fFolder);
2850 }
2851 }
2852 for (auto &f : folderNames) {
2853 int i = 0;
2854 int iindent = indent;
2855 if (!f.empty()) {
2856 for (int j = 0; j < indent; j++)
2857 std::cout << " ";
2858 std::cout << f << std::endl;
2859 iindent += 1;
2860 }
2861 for (auto &k : *this) {
2862 if (k->fFolder != f) {
2863 i++;
2864 continue;
2865 }
2866 for (int j = 0; j < iindent; j++)
2867 std::cout << " ";
2868 std::cout << i++ << ") " << k->GetName() << " : ";
2869 if (k->get()) {
2870 if (_more || (k->get<RooAbsArg>() && k->get<RooAbsArg>()->isFundamental()) || k->get<RooConstVar>() ||
2871 k->get<RooAbsData>() /*|| k->get<RooProduct>()*/) {
2872 auto _deps = k->coords(false).argList();
2873 auto _snap = std::unique_ptr<RooAbsCollection>(_deps.snapshot());
2874 k->coords(); // move to coords before printing (in case this matters)
2875 k->get()->Print(sOpt); // assumes finishes with an endl
2876 _deps.assignValueOnly(*_snap);
2877 } else {
2878 TString _suffix = "";
2879 if (auto _type = k->GetNodeType(); strlen(_type)) {
2880 // decided not to show const values until figure out how to update if value changes
2881 /*if (TString(_type)=="Const") _name += TString::Format("
2882 [%s=%g]",_type,v->get<RooConstVar>()->getVal()); else*/
2883 _suffix += TString::Format(" [%s]", _type);
2884 }
2885 if (auto fv = k->get<RooFormulaVar>()) {
2886 TString formu = TString::Format(" [%s]", fv->expression());
2887 for (size_t j = 0; j < fv->dependents().size(); j++) {
2888 formu.ReplaceAll(TString::Format("x[%zu]", j), fv->dependents()[j].GetName());
2889 }
2890 _suffix += formu;
2891 } else if (auto gv = k->get<RooGenericPdf>()) {
2892 TString formu = TString::Format(" [%s]", gv->expression());
2893 for (size_t j = 0; j < gv->dependents().size(); j++) {
2894 formu.ReplaceAll(TString::Format("x[%zu]", j), gv->dependents()[j].GetName());
2895 }
2896 _suffix += formu;
2897 } else if (auto pi = k->get<PiecewiseInterpolation>()) {
2898 // check if all interpCodes are the same. Will include in the NodeType
2899 std::set<int> interpCodes;
2900 for (auto &c : pi->interpolationCodes())
2901 interpCodes.insert(c);
2902 if (interpCodes.size() == 1) {
2903 _suffix += TString::Format(" [InterpCode=%d]", *interpCodes.begin());
2904 }
2905 } else if (auto fiv = k->get<RooStats::HistFactory::FlexibleInterpVar>()) {
2906 // check if all interpCodes are the same.
2907 std::set<int> interpCodes;
2908 for (auto &c : fiv->interpolationCodes())
2909 interpCodes.insert(c == 4 ? 5 : c); // in definition of FlexibleInterpVar 4 gets replaced with 5
2910 if (interpCodes.size() == 1) {
2911 _suffix += TString::Format(" [InterpCode=%d]", *interpCodes.begin());
2912 }
2913 }
2914 std::cout << k->get()->ClassName() << "::" << k->get()->GetName() << _suffix.Data() << std::endl;
2915 }
2916 if (depth != 0) {
2917 k->Print(sOpt + TString::Format("depth=%dindent=%d", depth - 1, iindent + 1));
2918 }
2919 } else
2920 std::cout << " NULL " << std::endl;
2921 }
2922 }
2923 if (doCapture) {
2924 capture.reset(); // no captureStr has the string to display
2925 // inject line breaks to avoid msgbox being too wide
2926 size_t lastBreak = 0;
2927 std::string captureStrWithBreaks;
2928 for (size_t i = 0; i < captureStr.size(); i++) {
2930 if (captureStr[i] == '\n') {
2931 lastBreak = i;
2932 }
2933 if (i - lastBreak > 150) {
2934 captureStrWithBreaks += '\n';
2935 lastBreak = i;
2936 }
2937 }
2938 const TGWindow *w =
2939 (gROOT->GetListOfBrowsers()->At(0))
2940 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
2941 : gClient->GetRoot();
2942 new TGMsgBox(gClient->GetRoot(), w, GetName(),
2943 captureStrWithBreaks.c_str()); //,nullptr,kMBDismiss,nullptr,kVerticalFrame,kTextLeft|kTextCenterY);
2944 }
2945}
2946
2948{
2949 if (!child.get()) {
2950
2951 if (auto v = get<RooRealVar>(); v) {
2952
2953 TString constrType = child.GetName();
2954 double mean = std::numeric_limits<double>::quiet_NaN();
2955 double sigma = mean;
2956 if (constrType.BeginsWith("gaussian(")) {
2957 // extract the mean and stddev parameters
2958 // if only one given, it is the stddev
2959 if (constrType.Contains(",")) {
2960 mean = TString(constrType(9, constrType.Index(',') - 9)).Atof();
2961 sigma = TString(constrType(constrType.Index(',') + 1, constrType.Index(')') - constrType.Index(',') + 1))
2962 .Atof();
2963 } else {
2964 mean = std::numeric_limits<double>::quiet_NaN(); // will use the var current value below to set mean
2965 sigma = TString(constrType(9, constrType.Index(')') - 9)).Atof();
2966 }
2967 constrType = "normal";
2968 } else if (constrType == "normal") {
2969 mean = 0;
2970 sigma = 1;
2971 } else if (constrType == "gaussian") {
2972 // extract parameters from the variable
2973 // use current value and error on v as constraint
2974 if (!v->hasError())
2975 throw std::runtime_error("No error on parameter for gaussian constraint");
2976 sigma = v->getError();
2977 mean = v->getVal();
2978 constrType = "normal";
2979 } else if (constrType == "poisson") {
2980 if (!v->hasError())
2981 throw std::runtime_error("No error on parameter for poisson constraint");
2982 mean = 1;
2983 sigma = pow(v->getVal() / v->getError(), 2);
2984 }
2985
2986 if (constrType == "poisson") {
2987 // use current value and error on v as constraint
2988 double tau_val = sigma;
2989 auto globs = acquire<RooRealVar>(Form("globs_%s", v->GetName()), Form("globs_%s", v->GetName()),
2990 v->getVal() * tau_val, (v->getVal() - 5 * v->getError()) * tau_val,
2991 (v->getVal() + 5 * v->getError()) * tau_val);
2992 globs->setConstant();
2993 globs->setAttribute("obs");
2994 globs->setAttribute("global");
2995 globs->setStringAttribute("nominal", TString::Format("%f", tau_val));
2996 auto tau = acquireNew<RooConstVar>(TString::Format("tau_%s", v->GetName()), "", tau_val);
2998 Form("pois_%s", v->GetName()), TString::Format("Poisson Constraint of %s", v->GetTitle()), *globs,
2999 *acquireNew<RooProduct>(TString::Format("mean_%s", v->GetName()),
3000 TString::Format("Poisson Constraint of %s", globs->GetTitle()),
3001 RooArgList(*v, *tau)),
3002 true /* no rounding */);
3003
3004 auto out = Constrain(xRooNode(Form("pois_%s", GetName()), constr));
3005 if (!v->hasError())
3006 v->setError(mean / sqrt(tau_val)); // if v doesnt have an uncert, will put one on it now
3007 Info("Constrain", "Added poisson constraint pdf RooPoisson::%s (tau=%g) for %s", out->GetName(), tau_val,
3008 GetName());
3009 return out;
3010 } else if (constrType == "normal") {
3011
3012 auto globs = acquire<RooRealVar>(Form("globs_%s", v->GetName()), Form("globs_%s", v->GetName()), mean,
3013 mean - 10 * sigma, mean + 10 * sigma);
3014 globs->setAttribute("obs");
3015 globs->setAttribute("global");
3016 globs->setConstant();
3017
3018 globs->setStringAttribute("nominal", TString::Format("%f", mean));
3020 Form("gaus_%s", v->GetName()), TString::Format("Gaussian Constraint of %s", v->GetTitle()), *globs, *v,
3021 *acquireNew<RooConstVar>(TString::Format("sigma_%s", v->GetName()), "", sigma));
3022 auto out = Constrain(xRooNode(Form("gaus_%s", GetName()), constr));
3023 if (!v->hasError())
3024 v->setError(sigma); // if v doesnt have an uncert, will put one on it now
3025 Info("Constrain", "Added gaussian constraint pdf RooGaussian::%s (mean=%g,sigma=%g) for %s", out->GetName(),
3026 mean, sigma, GetName());
3027 return out;
3028 }
3029 }
3030 } else if (auto p = child.get<RooAbsPdf>(); p) {
3031
3032 auto _me = get<RooAbsArg>();
3033 if (!_me) {
3034 throw std::runtime_error("Cannot constrain non arg");
3035 }
3036
3037 if (!p->dependsOn(*_me)) {
3038 throw std::runtime_error("Constraint does not depend on constrainee");
3039 }
3040
3041 // find a parent that can swallow this pdf ... either a RooProdPdf or a RooWorkspace
3042 auto x = fParent;
3043 while (x && !x->get<RooProdPdf>() && !x->get<RooSimultaneous>() && !x->get<RooWorkspace>()) {
3044 x = x->fParent;
3045 }
3046 if (!x) {
3047 throw std::runtime_error("Nowhere to put constraint");
3048 }
3049 // get datasets of the swallower, and add glob to any globs lists
3050 auto childGlobs = child.globs();
3051 if (!childGlobs.empty()) {
3052 for (auto d : x->datasets()) {
3053 if (auto globs = d->get<RooAbsData>()->getGlobalObservables()) {
3055 newGlobs.add(*childGlobs.get<RooArgList>());
3056 d->get<RooAbsData>()->setGlobalObservables(newGlobs);
3057 }
3058 }
3059 // also add to the workspaces globalObservables lists
3060 if (x->ws()) {
3061 for (auto &[k, v] : GETWSSETS(x->ws())) {
3062 if (k == "globalObservables" || TString(k).EndsWith("_GlobalObservables")) {
3063 const_cast<RooArgSet &>(v).add(*childGlobs.get<RooArgList>());
3064 }
3065 }
3066 }
3067 }
3068 if (auto s = x->get<RooSimultaneous>(); s) {
3069 // put into every channel that features parameter
3070 x->browse();
3071 for (auto &c : *x) {
3072 if (auto a = c->get<RooAbsArg>(); a->dependsOn(*_me))
3073 c->Multiply(child);
3074 }
3075 return child;
3076 } else if (x->get<RooProdPdf>()) {
3077 return x->Multiply(child);
3078 } else {
3079 return x->Add(child, "+");
3080 }
3081 }
3082
3083 throw std::runtime_error(TString::Format("Cannot constrain %s", GetName()));
3084}
3085
3087{
3088
3089 class AutoUpdater {
3090 public:
3091 AutoUpdater(xRooNode &_n) : n(_n) {}
3092 ~AutoUpdater() { n.browse(); }
3093 xRooNode &n;
3094 };
3095 AutoUpdater xxx(*this);
3096
3097 if (fBinNumber != -1) {
3098 // scaling a bin ...
3099 if (child.get<RooAbsReal>()) { // if not child then let fall through to create a child and call self again below
3100 // doing a bin-multiplication .. the parent should have a ParamHistFunc called binFactors
3101 // if it doesn't then create one
3102 auto o = std::dynamic_pointer_cast<RooAbsReal>(acquire(child.fComp));
3103
3104 // get binFactor unless parent is a ParamHistFunc already ...
3105
3106 auto binFactors = (fParent->get<ParamHistFunc>()) ? fParent : fParent->factors().find("binFactors");
3107
3108 // it can happen in a loop over bins() that another node has moved fParent inside a product
3109 // so check for fParent having a client with the ORIGNAME:<name> attribute
3110 if (!binFactors && fParent->get<RooAbsArg>()) {
3111 for (auto c : fParent->get<RooAbsArg>()->clients()) {
3112 if (c->IsA() == RooProduct::Class() &&
3113 c->getAttribute(TString::Format("ORIGNAME:%s", fParent->get()->GetName()))) {
3114 // try getting binFactors out of this
3115 binFactors = xRooNode(*c).factors().find("binFactors");
3116 break;
3117 }
3118 }
3119 }
3120
3121 if (!binFactors) {
3122 fParent
3123 ->Multiply(TString::Format("%s_binFactors",
3124 (fParent->mainChild().get())
3125 ? fParent->mainChild()->GetName()
3126 : (fParent->get() ? fParent->get()->GetName() : fParent->GetName()))
3127 .Data(),
3128 "blankshape")
3129 .SetName("binFactors"); // creates ParamHistFunc with all pars = 1 (shared const)
3130 binFactors = fParent->factors().find("binFactors");
3131 if (!binFactors) {
3132 throw std::runtime_error(
3133 TString::Format("Could not create binFactors in parent %s", fParent->GetName()));
3134 }
3135 // auto phf = binFactors->get<ParamHistFunc>();
3136
3137 // create RooProducts for all the bins ... so that added factors don't affect selves
3138 int i = 1;
3139 for (auto &b : binFactors->bins()) {
3140 auto p = acquireNew<RooProduct>(TString::Format("%s_bin%d", binFactors->get()->GetName(), i),
3141 TString::Format("binFactors of bin %d", i), RooArgList());
3142 p->setStringAttribute("alias", TString::Format("%s=%g", binFactors->GetXaxis()->GetParent()->GetName(),
3143 binFactors->GetXaxis()->GetBinCenter(i)));
3144 b->Multiply(*p);
3145 i++;
3146 }
3147 }
3148 // then scale the relevant bin ... if the relevant bin is a "1" then just drop in our factor (inside a
3149 // RooProduct though, to avoid it getting modified by subsequent multiplies)
3150 auto _bin = binFactors->bins().at(fBinNumber - 1);
3151 if (auto phf = binFactors->get<ParamHistFunc>(); phf && _bin) {
3152#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3153 RooArgList &pSet = phf->_paramSet;
3154#else
3155 RooArgList &pSet = const_cast<RooArgList &>(phf->paramList());
3156#endif
3157 if (strcmp(_bin->GetName(), "1") == 0) {
3159 for (std::size_t i = 0; i < pSet.size(); i++) {
3160 if (int(i) != fBinNumber - 1) {
3161 all.add(*pSet.at(i));
3162 } else {
3163 all.add(*o);
3164 }
3165 }
3166 pSet.removeAll();
3167 pSet.add(all);
3168 } else {
3169 _bin->fBinNumber = -1; // to avoid infinite loop
3170 return _bin->Multiply(child, opt);
3171 }
3172 // } else {else if(_bin->get<RooProduct>()) {
3173 // // multiply the element which will just add it as a factor in the rooproduct
3174 // return _bin->Multiply(child,opt);
3175 // } else {
3176 // // not a rooproduct in this bin yet ... so need to replace with a rooproduct and
3177 // multiply that
3178 // // this avoids the undesired behaviour of shared binFactors getting all impacted by
3179 // mulitplies RooArgList all; auto new_p =
3180 // acquireNew<RooProduct>(TString::Format("%s_bin%d",binFactors->get()->GetName(),fBinNumber),TString::Format("binFactors
3181 // of bin %d",fBinNumber),RooArgList(*_bin->get<RooAbsArg>()));
3182 // new_p->setStringAttribute("alias","")
3183 // for (int i = 0; i < phf->_paramSet.size(); i++) {
3184 // if (i != fBinNumber - 1) all.add(*phf->_paramSet.at(i));
3185 // else all.add(*new_p);
3186 // }
3187 // phf->_paramSet.removeAll();
3188 // phf->_paramSet.add(all);
3189 // // now multiply that bin having converted it to RooProduct
3190 // return binFactors->bins().at(fBinNumber - 1)->Multiply(child,opt);
3191 // }
3192 }
3193 return xRooNode(*o, binFactors);
3194 }
3195 } else if (!get() && fParent) {
3196 // try to 'create' object based on parentage
3197 // add child as a temporary child to help with decision making
3198 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
3199 try {
3200 fComp = fParent->Add(*this, "+").fComp;
3201 } catch (...) {
3202 resize(size() - 1);
3203 std::rethrow_exception(std::current_exception());
3204 }
3205 resize(size() - 1); // remove the temporarily added node
3206 }
3207
3208 if (!child.get()) {
3209 TString sOpt(opt);
3210 sOpt.ToLower();
3211 if (auto o = getObject<RooAbsReal>(child.GetName())) {
3212 auto out = Multiply(xRooNode(o, child.fParent));
3213 // have to protect bin case where get() is null (could change but then must change logic above too)
3214 if (get()) {
3215 Info("Multiply", "Scaled %s by existing factor %s::%s",
3216 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), o->ClassName(), o->GetName());
3217 }
3218 return out;
3219 } else if (sOpt == "const") {
3220 auto out = Multiply(RooConstVar(child.GetName(), child.GetTitle(), 1));
3221 if (get()) {
3222 Info("Multiply", "Scaled %s by new const factor %s",
3223 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
3224 }
3225 return out;
3226 } else if (sOpt == "norm") {
3227 if (TString(child.GetName()).Contains("[") && ws()) {
3228 // assume factory method wanted
3229 auto arg = ws()->factory(child.GetName());
3230 if (arg) {
3231 auto out = Multiply(*arg);
3232 if (get()) {
3233 Info("Multiply", "Scaled %s by new norm factor %s",
3234 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
3235 }
3236 return out;
3237 }
3238 throw std::runtime_error(TString::Format("Failed to create new normFactor %s", child.GetName()));
3239 }
3240 auto out = Multiply(RooRealVar(child.GetName(), child.GetTitle(), 1, -1e-5, 100));
3241 if (get()) {
3242 Info("Multiply", "Scaled %s by new norm factor %s",
3243 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
3244 }
3245 return out;
3246 } else if (sOpt == "shape" || sOpt == "simple" || sOpt == "blankshape") {
3247 // needs axis defined
3248 if (auto ax = GetXaxis(); ax) {
3249 auto h = std::shared_ptr<TH1>(BuildHistogram(dynamic_cast<RooAbsLValue *>(ax->GetParent()), true));
3250 h->Reset();
3251 for (int i = 1; i <= h->GetNbinsX(); i++) {
3252 h->SetBinContent(i, 1);
3253 }
3254 h->SetMinimum(0);
3255 h->SetMaximum(100);
3256 h->SetName(TString::Format(";%s", child.GetName())); // ; char indicates don't "rename" this thing
3257 h->SetTitle(child.GetTitle());
3258 if (sOpt.Contains("shape"))
3259 h->SetOption(sOpt);
3260 auto out = Multiply(*h);
3261 if (get()) {
3262 Info("Multiply", "Scaled %s by new %s factor %s",
3263 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), sOpt.Data(), out->GetName());
3264 }
3265 return out;
3266 }
3267 } else if (sOpt == "overall") {
3269 child.GetName(), child.GetTitle(), RooArgList(), 1, std::vector<double>(), std::vector<double>()));
3270 if (get() /* can happen this is null if on a bin node with no shapeFactors*/) {
3271 Info("Multiply", "Scaled %s by new overall factor %s",
3272 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
3273 }
3274 return out;
3275 } else if (sOpt == "func" && ws()) {
3276 // need to get way to get dependencies .. can't pass all as causes circular dependencies issues.
3277 if (auto arg = ws()->factory(TString("expr::") + child.GetName())) {
3278 auto out = Multiply(*arg);
3279 if (get() /* can happen this is null if on a bin node with no shapeFactors*/) {
3280 Info("Multiply", "Scaled %s by new func factor %s",
3281 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
3282 }
3283 return out;
3284 }
3285 }
3286 }
3287 if (auto h = child.get<TH1>(); h && strlen(h->GetOption()) == 0 && strlen(opt) > 0) {
3288 // put the option in the hist
3289 h->SetOption(opt);
3290 }
3291 if (auto w = get<RooWorkspace>(); w) {
3292 // just acquire
3293 std::shared_ptr<TObject> out;
3294 child.convertForAcquisition(*this);
3295 if (child.get<RooAbsReal>())
3296 out = acquire(child.fComp);
3297 return out;
3298 }
3299
3300 if (strcmp(GetName(), ".coef") == 0) { // covers both .coef and .coefs
3301 // need to add this into the relevant coef ... if its not a RooProduct, replace it with one first
3302 if (auto p = fParent->fParent->get<RooAddPdf>()) {
3303 // may be in no-coef mode ... in which case must create coefs (use "ExtendedBindings" but note that these need
3304 // obs list passing to them
3305 if (p->coefList().empty() && !p->pdfList().empty()) {
3306 for (auto _pdf : p->pdfList()) {
3307 const_cast<RooArgList &>(p->coefList())
3308 .add(*acquireNew<RooExtendedBinding>(TString::Format("%s_extBind", _pdf->GetName()),
3309 TString::Format("Expected Events of %s", _pdf->GetTitle()),
3310 *static_cast<RooAbsPdf *>(_pdf)));
3311 }
3312 Info("Multiply", "Created RooExtendedBinding coefficients for all pdfs of %s so that can multiply coef",
3313 p->GetName());
3314 *reinterpret_cast<bool *>(reinterpret_cast<unsigned char *>(p) +
3315 p->Class()->GetDataMemberOffset("_allExtendable")) = false;
3316 *reinterpret_cast<bool *>(reinterpret_cast<unsigned char *>(p) +
3317 p->Class()->GetDataMemberOffset("_haveLastCoef")) = true;
3318 }
3319 for (size_t i = 0; i < p->pdfList().size(); i++) {
3320 if (p->pdfList().at(i) == fParent->get<RooAbsArg>()) {
3321 auto coefs = p->coefList().at(i);
3322 if (!coefs->InheritsFrom("RooProduct")) {
3324 if (!(strcmp(coefs->GetName(), "1") == 0 || strcmp(coefs->GetName(), "ONE") == 0))
3325 oldCoef.add(*coefs);
3326 auto newCoefs = fParent->acquireNew<RooProduct>(
3327 TString::Format("coefs_%s", fParent->GetName()),
3328 TString::Format("coefficients for %s", fParent->GetName()), oldCoef);
3330 for (size_t j = 0; j < p->coefList().size(); j++) {
3331 if (i == j) {
3332 oldCoefs.add(*newCoefs);
3333 } else {
3334 oldCoefs.add(*p->coefList().at(j));
3335 }
3336 }
3337 const_cast<RooArgList &>(p->coefList()).removeAll();
3338 const_cast<RooArgList &>(p->coefList()).add(oldCoefs);
3339 coefs = newCoefs.get();
3340 }
3341 return xRooNode(*coefs, fParent).Multiply(child);
3342 }
3343 }
3344 } else if (auto p2 = fParent->fParent->get<RooRealSumPdf>()) {
3345 // find our function in the funcList, and then update the coefs of it
3346
3347 for (size_t i = 0; i < p2->funcList().size(); i++) {
3348 if (p2->funcList().at(i) == fParent->get<RooAbsArg>()) {
3349 auto coefs = p2->coefList().at(i);
3350 if (!coefs->InheritsFrom("RooProduct")) {
3352 if (!(strcmp(coefs->GetName(), "1") == 0 || strcmp(coefs->GetName(), "ONE") == 0))
3353 oldCoef.add(*coefs);
3354 auto newCoefs = fParent->acquireNew<RooProduct>(
3355 TString::Format("coefs_%s", fParent->GetName()),
3356 TString::Format("coefficients for %s", fParent->GetName()), oldCoef);
3358 for (size_t j = 0; j < p2->coefList().size(); j++) {
3359 if (i == j) {
3360 oldCoefs.add(*newCoefs);
3361 } else {
3362 oldCoefs.add(*p2->coefList().at(j));
3363 }
3364 }
3365 const_cast<RooArgList &>(p2->coefList()).removeAll();
3366 const_cast<RooArgList &>(p2->coefList()).add(oldCoefs);
3367 coefs = newCoefs.get();
3368 }
3369 return xRooNode(*coefs, fParent).Multiply(child);
3370 }
3371 }
3372 }
3373 throw std::runtime_error("this coefs case is not supported");
3374 }
3375
3376 if (auto p = get<RooProduct>(); p) {
3377 std::shared_ptr<TObject> out;
3378 auto cc = child.fComp;
3379 bool isConverted = (child.convertForAcquisition(*this) != cc);
3380 if (child.get<RooAbsReal>())
3381 out = acquire(child.fComp);
3382
3383 // child may be a histfunc or a rooproduct of a histfunc and a paramhist if has stat errors
3384 if (auto _f = std::dynamic_pointer_cast<RooHistFunc>(
3385 (child.get<RooProduct>()) ? child.factors()[child.GetName()]->fComp : out);
3386 _f && _f->getAttribute("autodensity")) {
3387 // should we flag this as a density? yes if there's no other term marked as the density
3388 bool hasDensity = false;
3389 for (auto &f : factors()) {
3390 if (f->get<RooAbsArg>()->getAttribute("density")) {
3391 hasDensity = true;
3392 break;
3393 }
3394 }
3395 _f->setAttribute("density", !hasDensity && fParent && fParent->get<RooRealSumPdf>());
3396 if (_f->getAttribute("density")) {
3397
3398 // need to divide by bin widths first
3399 for (int i = 0; i < _f->dataHist().numEntries(); i++) {
3400 auto bin_pars = _f->dataHist().get(i);
3401 _f->dataHist().set(*bin_pars, _f->dataHist().weight(i) / _f->dataHist().binVolume(*bin_pars));
3402 }
3403 _f->setValueDirty();
3404
3405 // promote the axis vars to observables
3406 for (auto &x : xRooNode("tmp", _f).vars()) {
3407 x->get<RooAbsArg>()->setAttribute("obs");
3408 }
3409 }
3410 _f->setAttribute("autodensity", false);
3411 }
3412
3413 if (isConverted && child.get<RooHistFunc>()) {
3414 Info("Multiply", "Created %s factor %s in %s",
3415 child.get<RooAbsArg>()->getAttribute("density") ? "SimpleDensity" : "Simple", child->GetName(),
3416 p->GetName());
3417 } else if (isConverted && child.get<ParamHistFunc>()) {
3418 Info("Multiply", "Created Shape factor %s in %s", child->GetName(), p->GetName());
3419 }
3420
3421 if (auto _f = std::dynamic_pointer_cast<RooAbsReal>(out); _f) {
3422#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3423 p->_compRSet.add(*_f);
3424#else
3425 const_cast<RooArgList &>(p->realComponents()).add(*_f);
3426#endif
3427 p->setValueDirty();
3428
3429 browse();
3430 xRooNode _out(_f, *this);
3431 for (auto &_par : _out.pars()) {
3432 if (auto s = _par->get<RooAbsArg>()->getStringAttribute("boundConstraint"); s) {
3433 bool found = false;
3434 for (auto &_constr : _par->constraints()) {
3435 if (strcmp(s, _constr->get()->GetName()) == 0) {
3436 // constraint is already included
3437 found = true;
3438 break;
3439 }
3440 }
3441 if (!found) {
3442 Info("Multiply", "Pulling in %s boundConstraint: %s", _par->GetName(), s);
3443 auto _pdf = getObject<RooAbsPdf>(s);
3444 if (!_pdf) {
3445 throw std::runtime_error("Couldn't find boundConstraint");
3446 }
3447 _par->Constrain(_pdf);
3448 }
3449 }
3450 }
3451 sterilize();
3452 return _out;
3453 }
3454 } else if (auto p2 = get<RooProdPdf>(); p2) {
3455
3456 std::shared_ptr<TObject> out;
3457 child.convertForAcquisition(*this);
3458 if (child.get<RooAbsPdf>()) {
3459 out = acquire(child.fComp);
3460 } else if (child.get<RooAbsReal>() && mainChild().get<RooRealSumPdf>()) {
3461 // cannot multiply a RooProdPdf by a non pdf
3462 throw std::runtime_error(TString::Format("Cannot multiply %s by non-pdf %s", GetName(), child.GetName()));
3463 // return mainChild().Add(child); - nov 2022 - used to do this but now replaced with exception above
3464 } else if (!child.get() || child.get<RooAbsReal>()) {
3465 // need to create or hide inside a sumpdf or rooadpdf
3466 std::shared_ptr<RooAbsPdf> _pdf;
3467 if (!child.get() && strcmp(child.GetName(), "components") == 0) {
3469 Form("%s_%s", p2->GetName(), child.GetName()),
3470 (strlen(child.GetTitle()) && strcmp(child.GetTitle(), child.GetName())) ? child.GetTitle()
3471 : p2->GetTitle(),
3472 RooArgList() /*, RooArgList() forces coef-mode if we specify this list */);
3473 _pdf = _sumpdf;
3474 } else {
3476 Form("%s_%s", p2->GetName(), child.GetName()),
3477 (strlen(child.GetTitle()) && strcmp(child.GetTitle(), child.GetName())) ? child.GetTitle()
3478 : p2->GetTitle(),
3479 RooArgList(), RooArgList(), true);
3480 _sumpdf->setFloor(true);
3481 _pdf = _sumpdf;
3482 }
3483 _pdf->setStringAttribute("alias", child.GetName());
3484 // transfer axis attributes if present (TODO: should GetXaxis look beyond the immediate parent?)
3485 _pdf->setStringAttribute("xvar", p2->getStringAttribute("xvar"));
3486 _pdf->setStringAttribute("binning", p2->getStringAttribute("binning"));
3487 out = _pdf;
3488 Info("Multiply", "Created %s::%s in channel %s", _pdf->ClassName(), _pdf->GetName(), p2->GetName());
3489 if (child.get<RooAbsReal>())
3490 xRooNode(*out, *this).Add(child);
3491 }
3492
3493 if (auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out); _pdf) {
3494#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3495 const_cast<RooArgList &>(p2->pdfList()).add(*_pdf);
3496#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
3497 p2->_pdfNSetList.emplace_back(std::make_unique<RooArgSet>("nset"));
3498#else
3499 p->_pdfNSetList.Add(new RooArgSet("nset"));
3500#endif
3501 if (!p2->canBeExtended() && _pdf->canBeExtended()) {
3502 p2->_extendedIndex = p2->_pdfList.size() - 1;
3503 }
3504#else
3505 p2->addPdfs(RooArgSet(*_pdf));
3506#endif
3507 sterilize();
3508 browse();
3509 return xRooNode(_pdf, *this);
3510 }
3511 } else if (auto p3 = get<RooRealSumPdf>(); p3) {
3512 // multiplying all current and future components
3513 std::shared_ptr<TObject> out;
3514 child.convertForAcquisition(*this);
3515 if (child.get<RooAbsReal>()) {
3516 out = acquire(child.fComp);
3517 for (auto &c : components()) {
3518 c->Multiply(out);
3519 }
3520 TString s = p3->getStringAttribute("global_factors");
3521 if (s != "")
3522 s += ";";
3523 s += out->GetName();
3524 p3->setStringAttribute("global_factors", s);
3525 Info(
3526 "Multiply",
3527 "Flagged %s as a global factor in channel %s (is applied to all current and future samples in the channel)",
3528 out->GetName(), p3->GetName());
3529 return xRooNode(out, *this);
3530 }
3531
3532 } else if (auto p4 = get<RooAbsPdf>(); p4 && !(fParent && fParent->get<RooRealSumPdf>())) {
3533 // multiply the coefs (if this isn't part of a RooAddPdf or RooRealSumPdf then we will eventually throw exception
3534 return coefs().Multiply(child);
3535 } else if (auto p5 = get<RooAbsReal>(); p5 && (!get<RooAbsPdf>() || (fParent && fParent->get<RooRealSumPdf>()))) {
3536 // replace this obj with a RooProduct to allow for multiplication
3537
3538 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
3539 std::set<RooAbsArg *> cl;
3540 for (auto &arg : p5->clients()) {
3541 cl.insert(arg);
3542 }
3543
3544 // if multiple clients, see if only one client is in parentage route
3545 // if so, then assume thats the only client we should replace in
3546 if (cl.size() > 1) {
3547 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
3548 cl.clear();
3549 cl.insert(fParent->get<RooAbsArg>());
3550 } else {
3551 Warning("Multiply", "Scaling %s that has multiple clients", p5->GetName());
3552 }
3553 }
3554
3555 auto new_p = acquireNew<RooProduct>(TString::Format("prod_%s", p5->GetName()), p5->GetTitle(), RooArgList(*p5));
3556 // copy attributes over
3557 for (auto &a : p5->attributes())
3558 new_p->setAttribute(a.c_str());
3559 for (auto &a : p5->stringAttributes())
3560 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
3561 if (!new_p->getStringAttribute("alias"))
3562 new_p->setStringAttribute("alias", p5->GetName());
3563 auto old_p = p5;
3564 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
3565 for (auto arg : cl) {
3566 arg->redirectServers(RooArgSet(*new_p), false, true);
3567 }
3568
3569 fComp = new_p;
3570 return Multiply(child);
3571 }
3572
3573 // before giving up here, assume user wanted a norm factor type if child is just a name
3574 if (!child.get() && strlen(opt) == 0)
3575 return Multiply(child, "norm");
3576
3577 throw std::runtime_error(
3578 TString::Format("Cannot multiply %s by %s%s", GetPath().c_str(), child.GetName(),
3579 (!child.get() && strlen(opt) == 0) ? " (forgot to specify factor type?)" : ""));
3580}
3581
3583{
3584
3585 auto p5 = get<RooAbsArg>();
3586 if (!p5) {
3587 throw std::runtime_error("Only replacement of RooAbsArg is supported");
3588 }
3589 node.convertForAcquisition(*this, "func");
3590
3591 auto new_p = node.get<RooAbsArg>();
3592 if (!new_p) {
3593 throw std::runtime_error(TString::Format("Cannot replace with %s", node.GetName()));
3594 }
3595 auto out = acquire(node.fComp);
3596 new_p = std::dynamic_pointer_cast<RooAbsArg>(out).get();
3597
3598 std::set<RooAbsArg *> cl;
3599 for (auto &arg : p5->clients()) {
3600 if (arg == new_p)
3601 continue; // do not replace in self ... although redirectServers will prevent that anyway
3602 cl.insert(arg);
3603 }
3604
3605 // if multiple clients, see if only one client is in parentage route
3606 // if so, then assume thats the only client we should replace in
3607 if (cl.size() > 1) {
3608 if (fParent && fParent->get<RooAbsArg>() && cl.count(fParent->get<RooAbsArg>()) > 0) {
3609 cl.clear();
3610 cl.insert(fParent->get<RooAbsArg>());
3611 } else {
3612 std::stringstream clientList;
3613 for (auto c : cl)
3614 clientList << c->GetName() << ",";
3615 Warning("Replace", "Replacing %s in all clients: %s", p5->GetName(), clientList.str().c_str());
3616 }
3617 }
3618
3619 new_p->setAttribute(Form("ORIGNAME:%s", p5->GetName())); // used in redirectServers to say what this replaces
3620 for (auto arg : cl) {
3621 // if RooFormulaVar need to ensure the internal formula has been "constructed" otherwise will try to construct
3622 // it from the original expression that may have old parameter in it.
3623 if (auto p = dynamic_cast<RooFormulaVar *>(arg))
3624 p->ok(); // triggers creation of RooFormula
3625 arg->redirectServers(RooArgSet(*new_p), false, true);
3626 }
3627 return node;
3628}
3629
3631{
3632
3633 class AutoUpdater {
3634 public:
3635 AutoUpdater(xRooNode &_n) : n(_n) {}
3636 ~AutoUpdater() { n.browse(); }
3637 xRooNode &n;
3638 };
3639 AutoUpdater xxx(*this);
3640
3641 if (!get() && fParent) {
3642 // try to 'create' object based on parentage
3643 // add child as a temporary child to help with decision making
3644 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
3645 try {
3646 fComp = fParent->Add(*this, "+").fComp;
3647 } catch (...) {
3648 resize(size() - 1);
3649 std::rethrow_exception(std::current_exception());
3650 }
3651 resize(size() - 1); // remove the temporarily added node
3652 }
3653
3654 if (auto p = mainChild(); p) {
3655 // variations applied to the main child if has one
3656 return p.Vary(child);
3657 }
3658
3659 if (auto s = get<RooSimultaneous>(); s && s->indexCat().IsA() == RooCategory::Class()) {
3660 // name is used as cat label
3661 std::string label = child.GetName();
3662 if (auto pos = label.find('='); pos != std::string::npos)
3663 label = label.substr(pos + 1);
3664 if (!s->indexCat().hasLabel(label)) {
3665 // auto idx = static_cast<const RooCategory &>(s->indexCat()).nextAvailableStateIndex(); - can't access,
3666 // protected method ... will have to just assume indices stay in sync
3667 // ensure added to category in any of our datasets too
3668 for (auto _ds : datasets()) {
3669 if (auto bb = _ds->getBrowsable(".sourceds")) {
3670 _ds = bb;
3671 } // shouldn't happen
3672 auto dsCat = _ds->robs()[s->indexCat().GetName()]->get<RooCategory>();
3673 if (!dsCat) {
3674 throw std::runtime_error(TString::Format("Failed to find %s regular observable in %s dataset",
3675 s->indexCat().GetName(), _ds->GetName()));
3676 }
3677 dsCat->defineType(label.c_str());
3678 }
3679 // adding to the index cat after, so that we don't need to generate subdatasets in the call to datasets() above
3680 // (missing cat will trigger cut)
3681 static_cast<RooCategory &>(const_cast<RooAbsCategoryLValue &>(s->indexCat())).defineType(label.c_str());
3682 }
3683 std::shared_ptr<TObject> out;
3684 child.convertForAcquisition(*this);
3685 if (child.get<RooAbsPdf>()) {
3686 out = acquire(child.fComp); // may create a channel from a histogram
3687 } else if (!child.fComp) {
3688 out = acquireNew<RooProdPdf>(TString::Format("%s_%s", s->GetName(), label.c_str()),
3689 (strlen(child.GetTitle())) ? child.GetTitle() : label.c_str(), RooArgList());
3690 Info("Vary", "Created channel RooProdPdf::%s in model %s", out->GetName(), s->GetName());
3691 }
3692
3693 if (auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out); _pdf) {
3694 // before adding the channel, we need to see if we are about to add any globs, and if necessary we must update
3695 // the dataset globs
3696 std::set<RooAbsData *> dsToUpdate;
3697 for (auto _ds : datasets()) {
3698 if (auto bb = _ds->getBrowsable(".sourceds")) {
3699 _ds = bb;
3700 } // shouldn't happen
3701 if (_ds->get<RooAbsData>()->getGlobalObservables()) {
3702 dsToUpdate.insert(_ds->get<RooAbsData>());
3703 }
3704 }
3705 if (!dsToUpdate.empty()) {
3707 _pdf->leafNodeServerList(&leafs);
3708 std::unique_ptr<RooAbsCollection> globals(leafs.selectByAttrib("global", true));
3709 for (auto _ds : dsToUpdate) {
3710 std::string alist;
3712 globs.addClone(*_ds->getGlobalObservables());
3713 for (auto &aa : *globals) {
3714 if (!globs.contains(*aa)) {
3715 globs.addClone(*aa);
3716 alist += std::string(aa->GetName()) + ",";
3717 }
3718 }
3719 if (!alist.empty()) {
3720 Warning("Vary", "Adding %s to global observables of %s", alist.c_str(), _ds->GetName());
3721 _ds->setGlobalObservables(globs);
3722 }
3723 }
3724 }
3725
3726 s->addPdf(*_pdf, label.c_str());
3727 sterilize();
3728 // clear children for reload and update shared axis
3729 clear();
3730 fXAxis.reset();
3731 browse();
3732 return xRooNode(TString::Format("%s=%s", s->indexCat().GetName(), label.data()), _pdf, *this);
3733 }
3734
3735 } else if (auto p = get<RooStats::HistFactory::FlexibleInterpVar>(); p) {
3736
3737 // child needs to be a constvar ...
3738 child.convertForAcquisition(*this);
3739 auto _c = child.get<RooConstVar>();
3740 if (!_c && child.get()) {
3741 throw std::runtime_error("Only pure consts can be set as variations of a flexible interpvar");
3742 }
3743#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3744 double value = (_c ? _c->getVal() : p->_nominal);
3745 double nomVal = p->_nominal;
3746#else
3747 double value = (_c ? _c->getVal() : p->nominal());
3748 double nomVal = p->nominal();
3749#endif
3750
3751 TString cName(child.GetName());
3752 if (cName == "nominal") {
3753 p->setNominal(value);
3754 return *(this->variations().at(cName.Data()));
3755 }
3756 if (cName.CountChar('=') != 1) {
3757 throw std::runtime_error("unsupported variation form");
3758 }
3759 std::string parName = cName(0, cName.Index('='));
3760 double parVal = TString(cName(cName.Index('=') + 1, cName.Length())).Atof();
3761 if (parVal != 1 && parVal != -1) {
3762 throw std::runtime_error("unsupported variation magnitude");
3763 }
3764 bool high = parVal > 0;
3765
3766 if (parName.empty()) {
3767 p->setNominal(value);
3768 } else {
3769 auto v = fParent->getObject<RooRealVar>(parName);
3770 if (!v)
3771 v = fParent->acquire<RooRealVar>(parName.c_str(), parName.c_str(), -5, 5);
3772 if (!v->hasError())
3773 v->setError(1);
3774
3775 if (!p->findServer(*v)) {
3776#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3777 p->_paramList.add(*v);
3778 p->_low.push_back(0);
3779 p->_high.push_back(0);
3780 p->_interpCode.push_back(4);
3781#else
3782 const_cast<RooListProxy &>(p->variables()).add(*v);
3783 const_cast<std::vector<double> &>(p->low()).push_back(0);
3784 const_cast<std::vector<double> &>(p->high()).push_back(0);
3785 const_cast<std::vector<int> &>(p->interpolationCodes()).push_back(4);
3786#endif
3787 v->setAttribute(Form("SYMMETRIC%s_%s", high ? "+" : "-", GetName())); // flag for symmetrized
3788 }
3789
3790 if (high) {
3791 p->setHigh(*v, value);
3792 if (v->getAttribute(Form("SYMMETRIC+_%s", GetName()))) {
3793 p->setLow(*v, 2 * nomVal - value);
3794 }
3795 v->setAttribute(Form("SYMMETRIC-_%s", GetName()), false);
3796 } else {
3797 p->setLow(*v, value);
3798 if (v->getAttribute(Form("SYMMETRIC-_%s", GetName()))) {
3799 p->setHigh(*v, 2 * nomVal - value);
3800 }
3801 v->setAttribute(Form("SYMMETRIC+_%s", GetName()), false);
3802 }
3803
3804 /*if (!unconstrained && fParent->pars()[v->GetName()].constraints().empty()) {
3805 fParent->pars()[v->GetName()].constraints().add("normal");
3806 }*/
3807 }
3808 return *(this->variations().at(cName.Data()));
3809 } else if (auto p2 = get<PiecewiseInterpolation>(); p2) {
3810 TString cName(child.GetName());
3811 if (cName.CountChar('=') != 1) {
3812 throw std::runtime_error("unsupported variation form");
3813 }
3814 TString parName = cName(0, cName.Index('='));
3815 double parVal = TString(cName(cName.Index('=') + 1, cName.Length())).Atof();
3816 if (parVal != 1 && parVal != -1) {
3817 throw std::runtime_error("unsupported variation magnitude");
3818 }
3819#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3820 RooHistFunc *f = dynamic_cast<RooHistFunc *>(p2->_nominal.absArg());
3821 if (!f) {
3822 throw std::runtime_error(
3823 TString::Format("Interpolating %s instead of RooHistFunc", p2->_nominal.absArg()->ClassName()));
3824 }
3825#else
3826 RooHistFunc *f = dynamic_cast<RooHistFunc *>(const_cast<RooAbsReal *>(p2->nominalHist()));
3827 if (!f) {
3828 throw std::runtime_error(
3829 TString::Format("Interpolating %s instead of RooHistFunc", p2->nominalHist()->ClassName()));
3830 }
3831#endif
3832 RooHistFunc *nomf = f;
3833 RooHistFunc *otherf = nullptr;
3834 size_t i = 0;
3835 for (auto par : p2->paramList()) {
3836 if (parName == par->GetName()) {
3837 f = dynamic_cast<RooHistFunc *>((parVal > 0 ? p2->highList() : p2->lowList()).at(i));
3838 otherf = dynamic_cast<RooHistFunc *>((parVal > 0 ? p2->lowList() : p2->highList()).at(i));
3839 break;
3840 }
3841 i++;
3842 }
3843 if (i == p2->paramList().size() && !child.get<RooAbsReal>()) {
3844
3845 // need to add the parameter
3846 auto v = acquire<RooRealVar>(parName, parName, -5, 5);
3847 if (!v->hasError())
3848 v->setError(1);
3849
3850 std::shared_ptr<RooHistFunc> up(
3851 static_cast<RooHistFunc *>(f->Clone(Form("%s_%s_up", f->GetName(), parName.Data()))));
3852 std::shared_ptr<RooHistFunc> down(
3853 static_cast<RooHistFunc *>(f->Clone(Form("%s_%s_down", f->GetName(), parName.Data()))));
3854 // RooHistFunc doesn't clone it's data hist ... do it ourself (will be cloned again if imported into a ws)
3855#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3856 std::unique_ptr<RooDataHist> h1(
3857 static_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", up->GetName()))));
3858 std::unique_ptr<RooDataHist> h2(
3859 static_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", down->GetName()))));
3860 up->_dataHist = dynamic_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", up->GetName())));
3861 down->_dataHist = dynamic_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", down->GetName())));
3862#else
3863 up->cloneAndOwnDataHist(TString::Format("hist_%s", up->GetName()));
3864 down->cloneAndOwnDataHist(TString::Format("hist_%s", down->GetName()));
3865#endif
3866 auto ups = std::dynamic_pointer_cast<RooHistFunc>(acquire(up, false, true));
3867 auto downs = std::dynamic_pointer_cast<RooHistFunc>(acquire(down, false, true));
3868#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3869 p2->_highSet.add(*ups.get());
3870 p2->_lowSet.add(*downs.get());
3871 p2->_interpCode.push_back(4);
3872 p2->_paramSet.add(*v);
3873#else
3874 const_cast<RooArgList &>(p2->highList()).add(*ups);
3875 const_cast<RooArgList &>(p2->lowList()).add(*downs);
3876 const_cast<std::vector<int> &>(p2->interpolationCodes()).push_back(4);
3877 const_cast<RooArgList &>(p2->paramList()).add(*v);
3878#endif
3879 p2->setValueDirty();
3880 f = ((parVal > 0) ? ups : downs).get();
3881 otherf = ((parVal > 0) ? downs : ups).get();
3882 // start off with everything being symmetric
3883 f->setStringAttribute("symmetrizes", otherf->GetName());
3884 f->setStringAttribute("symmetrize_nominal", nomf->GetName());
3885 otherf->setStringAttribute("symmetrized_by", f->GetName());
3886
3887 // constrain par if required
3888 /*if (!unconstrained && fParent->pars()[v->GetName()].constraints().empty()) {
3889 fParent->pars()[v->GetName()].constraints().add("normal");
3890 }*/
3891 }
3892
3893 // child.convertForAcquisition(*this);
3894 if (f) {
3895 if (child.get())
3896 xRooNode("tmp", *f, *this) = *child.get();
3897 f->setValueDirty();
3898 xRooNode out(*f, *this);
3899 out.sterilize();
3900 return out;
3901 }
3902
3903#if ROOT_VERSION_CODE > ROOT_VERSION(6, 37, 00)
3904 } else if (auto pmr = get<RooMultiReal>(); pmr) {
3905 TString cName(child.GetName());
3906 if (cName.CountChar('=') != 1) {
3907 throw std::runtime_error("unsupported variation form");
3908 }
3909 TString parName = cName(0, cName.Index('='));
3910 TString parVal = TString(cName(cName.Index('=') + 1, cName.Length()));
3911
3912 // add parVal to categorical if not already there
3913 if (!pmr->indexCategory()->hasLabel(parVal.Data())) {
3914 dynamic_cast<RooCategory &>(*(pmr->indexCategory())).defineType(parVal);
3915 }
3916 auto idx = pmr->indexCategory()->lookupIndex(parVal.Data());
3917 if (idx < 0) {
3918 throw std::runtime_error("Invalid index");
3919 }
3920 // add child to list ... use copy of "nominal" (0th) if child is empty
3921 child.convertForAcquisition(*this);
3922 auto _c = child.get<RooAbsReal>();
3923 if (!_c) {
3924 if (pmr->getModelList().empty()) {
3925 throw std::runtime_error("No real function given for variation, and no nominal function to clone");
3926 }
3927 _c = std::dynamic_pointer_cast<RooAbsReal>(
3928 acquire(std::shared_ptr<TObject>(pmr->getModelList().at(0)->Clone(
3929 TString::Format("%s_%s", get()->GetName(), child.GetName()))),
3930 false, true))
3931 .get();
3932 _c->setStringAttribute("alias", child.GetName());
3933 }
3934 const_cast<RooListProxy &>(pmr->getModelList()).add(*_c);
3935 return xRooNode(*_c, *this);
3936
3937#endif
3938
3939 } else if (auto p3 = get<RooConstVar>(); p3) {
3940
3941 // never vary the universal consts ... its too dangerous
3942 if (p3->getAttribute("RooRealConstant_Factory_Object")) {
3943 throw std::runtime_error("Cannot vary pure constants");
3944 }
3945
3946 // inject a FlexibleInterpVar ...
3947
3948 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
3949 std::set<RooAbsArg *> cl;
3950 for (auto &arg : p3->clients()) {
3951 cl.insert(arg);
3952 }
3953 // if multiple clients, see if only one client is in parentage route
3954 // if so, then assume thats the only client we should replace in
3955 if (cl.size() > 1) {
3956 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
3957 cl.clear();
3958 cl.insert(fParent->get<RooAbsArg>());
3959 } else {
3960 Warning("Vary", "Varying %s that has multiple clients", p3->GetName());
3961 }
3962 }
3963 p3->setStringAttribute("origName", p3->GetName());
3964 TString n = p3->GetName();
3965 p3->SetName(Form("%s_nominal", p3->GetName())); // if problems should perhaps not rename here
3966
3968 std::vector<double>(), std::vector<double>());
3969
3970 // copy attributes over
3971 for (auto &a : p3->attributes())
3972 new_p->setAttribute(a.c_str());
3973 for (auto &a : p3->stringAttributes())
3974 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
3975 // if (!new_p->getStringAttribute("alias")) new_p->setStringAttribute("alias",p->GetName());
3976 auto old_p = p3;
3977 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
3978 for (auto arg : cl) {
3979 arg->redirectServers(RooArgSet(*new_p), false, true);
3980 }
3981
3982 fComp = new_p;
3983 return Vary(child);
3984
3985 } else if (auto p4 = get<RooAbsReal>(); p4) {
3986 // inject an interpolation node
3987
3988 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
3989 std::set<RooAbsArg *> cl;
3990 for (auto &arg : p4->clients()) {
3991 cl.insert(arg);
3992 }
3993 // if multiple clients, see if only one client is in parentage route
3994 // if so, then assume thats the only client we should replace in
3995 if (cl.size() > 1) {
3996 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
3997 cl.clear();
3998 cl.insert(fParent->get<RooAbsArg>());
3999 } else {
4000 Warning("Vary", "Varying %s that has multiple clients", p4->GetName());
4001 }
4002 }
4003 p4->setStringAttribute("origName", p4->GetName());
4004 TString n = p4->GetName();
4005 p4->SetName(Form("%s_nominal", p4->GetName())); // if problems should perhaps not rename here
4006
4007#if ROOT_VERSION_CODE > ROOT_VERSION(6, 37, 00)
4008 std::shared_ptr<RooAbsArg> new_p;
4009 // if alphanumeric variation name is not a 1 or -1, inject a RooMultiReal not a PiecewiseInterpolation ...
4010 TString cName(child.GetName());
4011 if (cName.CountChar('=') == 1) {
4012 TString parName = cName(0, cName.Index('='));
4013 double parVal = TString(cName(cName.Index('=') + 1, cName.Length())).Atof();
4014 if (parVal != 1 && parVal != -1) {
4015 // get the idxCat ...
4017 if (!idxCat) {
4018 throw std::runtime_error("Failed to acquire categorical index for RooMultiReal");
4019 }
4020 // add an index if none already
4021 if (idxCat->numTypes() == 0) {
4022 idxCat->defineType("nominal");
4023 }
4024 Info("Vary", "Creating a RooMultiReal with category %s", idxCat->GetName());
4026 n, p4->GetTitle(), *idxCat,
4027 RooArgList()); // can't pass nominal model as that will force defining types on category
4028 const_cast<RooListProxy &>(newFunc->getModelList()).add(*p4);
4029 new_p = newFunc;
4030 }
4031 }
4032 if (!new_p) {
4034 }
4035
4036#else
4038#endif
4039
4040 // copy attributes over
4041 for (auto &a : p4->attributes())
4042 new_p->setAttribute(a.c_str());
4043 for (auto &a : p4->stringAttributes())
4044 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
4045 // if (!new_p->getStringAttribute("alias")) new_p->setStringAttribute("alias",p->GetName());
4046 auto old_p = p4;
4047 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
4048 for (auto arg : cl) {
4049 arg->redirectServers(RooArgSet(*new_p), false, true);
4050 }
4051
4052 fComp = new_p;
4053 return Vary(child);
4054 }
4055
4056 Print();
4057 throw std::runtime_error(TString::Format("Cannot vary %s with %s", GetName(), child.GetName()));
4058}
4059
4061{
4063}
4064
4065bool xRooNode::SetContent(double value, const char *par, double val)
4066{
4067 return SetContents(RooConstVar(GetName(), GetTitle(), value), par, val);
4068}
4069
4072 {
4073 if (x && b)
4074 x->setBinning(*b);
4075 if (b)
4076 delete b;
4077 }
4078 RooRealVar *x = nullptr;
4079 RooAbsBinning *b = nullptr;
4080};
4081
4083{
4084
4085 if (!get()) {
4086 fComp = std::shared_ptr<TObject>(const_cast<TObject *>(&o), [](TObject *) {});
4087 if (fParent && !fParent->find(GetName())) {
4088 // either a temporary or a placeholder so need to try genuinely adding
4089 fComp = fParent->Add(*this, "+").fComp;
4090 if (auto a = get<RooAbsArg>(); a && strcmp(a->GetName(), GetName()) && !a->getStringAttribute("alias")) {
4091 a->setStringAttribute("alias", GetName());
4092 }
4093 if (!fComp)
4094 throw std::runtime_error("Cannot determine type");
4095 return *this;
4096 }
4097 }
4098
4099 if (auto h = dynamic_cast<const TH1 *>(&o); h) {
4100 /*auto f = get<RooHistFunc>();
4101 if (!f) {
4102 // if it's a RooProduct locate child with the same name
4103 if (get<RooProduct>()) {
4104 f = factors()[GetName()]->get<RooHistFunc>();
4105 }
4106
4107
4108
4109 }*/
4110 bool _isData = get<RooAbsData>();
4112 if (_isData) {
4113 // need to ensure x-axis matches this h
4114 auto ax = GetXaxis();
4115 if (!ax)
4116 throw std::runtime_error("no xaxis");
4117 auto _v = dynamic_cast<RooRealVar *>(ax->GetParent());
4118 if (_v) {
4119 _b.x = _v;
4120 _b.b = dynamic_cast<RooAbsBinning *>(_v->getBinningPtr(nullptr)->Clone());
4121 if (h->GetXaxis()->IsVariableBinSize()) {
4122 _v->setBinning(RooBinning(h->GetNbinsX(), h->GetXaxis()->GetXbins()->GetArray()));
4123 } else {
4124 _v->setBinning(RooUniformBinning(h->GetXaxis()->GetXmin(), h->GetXaxis()->GetXmax(), h->GetNbinsX()));
4125 }
4126 }
4127 }
4128
4129 if (true) {
4130 for (int bin = 1; bin <= h->GetNbinsX(); bin++) {
4131 SetBinContent(bin, h->GetBinContent(bin));
4132 /*double value = h->GetBinContent(bin);
4133 auto bin_pars = f->dataHist().get(bin - 1);
4134 if (f->getAttribute("density")) {
4135 value /= f->dataHist().binVolume(*bin_pars);
4136 }
4137 f->dataHist().set(*bin_pars, value);*/
4138 if (!_isData && h->GetSumw2N() && !SetBinError(bin, h->GetBinError(bin)))
4139 throw std::runtime_error("Failed setting stat error");
4140 }
4141 return *this;
4142 }
4143 } else if (auto _c = dynamic_cast<const RooConstVar *>(&o); _c) {
4144
4145 if (auto a = get<RooAbsArg>();
4146 (a && a->isFundamental()) || get<RooConstVar>() || get<RooStats::HistFactory::FlexibleInterpVar>()) {
4147 SetBinContent(1, _c->getVal());
4148 return *this;
4149 } else if (get<RooAbsData>()) { // try to do assignment to a dataset (usually setting a bin content)
4150 SetBinContent(0, _c->getVal());
4151 return *this;
4152 }
4153 }
4154
4155 throw std::runtime_error("Assignment failed");
4156
4157 /*
4158
4159 if (fParent && !fParent->mk()) {
4160 throw std::runtime_error("mk failure");
4161 }
4162
4163 if (fComp) return *this;
4164
4165 if (o.InheritsFrom("RooAbsArg")) {
4166 fComp = acquire(std::shared_ptr<TObject>(const_cast<TObject*>(&o),[](TObject* o){}));
4167 std::dynamic_pointer_cast<RooAbsArg>(fComp)->setStringAttribute("alias",GetName());
4168 }
4169
4170 if (fComp && fParent) {
4171 fParent->incorporate(fComp);
4172 }
4173
4174
4175 return *this;
4176 */
4177}
4178
4179void xRooNode::_fit_(const char *constParValues, const char *options)
4180{
4181 try {
4182 // re-float all poi and np before fitting
4183 np().get<RooArgList>()->setAttribAll("Constant", false);
4184 poi().get<RooArgList>()->setAttribAll("Constant", false);
4185 auto _pars = pars();
4186 // std::unique_ptr<RooAbsCollection> snap(_pars.argList().snapshot());
4187 TStringToken pattern(constParValues, ",");
4188 std::map<RooAbsRealLValue *, double> valsToSet;
4189 while (pattern.NextToken()) {
4190 auto idx = pattern.Index('=');
4191 TString pat = (idx == -1) ? TString(pattern) : TString(pattern(0, idx));
4192 double val =
4193 (idx == -1) ? std::numeric_limits<double>::quiet_NaN() : TString(pattern(idx + 1, pattern.Length())).Atof();
4194 bool foundArg = false;
4195 for (auto p : _pars.argList()) {
4196 if (TString(p->GetName()).Contains(TRegexp(pat, true))) {
4197 foundArg = true;
4198 p->setAttribute("Constant", true);
4199 if (!std::isnan(val)) {
4200 valsToSet[dynamic_cast<RooAbsRealLValue *>(p)] = val;
4201 // dynamic_cast<RooAbsRealLValue *>(p)->setVal(val); // don't set yet, to allow for asimov dataset
4202 // creation based on current values
4203 }
4204 }
4205 }
4206 if (!foundArg) {
4207 throw std::runtime_error(std::string("Unrecognised parameter: ") + pat.Data());
4208 }
4209 }
4210
4211 // parse options
4212 TStringToken pattern2(options, ",");
4214 while (pattern2.NextToken()) {
4215 auto idx = pattern2.Index('=');
4216 TString pat = (idx == -1) ? TString(pattern2) : TString(pattern2(0, idx));
4217 TString val = TString(pattern2(idx + 1, pattern2.Length()));
4218 if (auto o = defaultOpts->FindObject(pat)) {
4219 defaultOpts->Remove(o);
4220 delete o;
4221 }
4222 defaultOpts->Add(new RooCmdArg(pat, val.IsDec() ? val.Atoi() : 0, 0, val.IsFloat() ? val.Atof() : 0., 0.,
4223 val.IsAlpha() ? val : nullptr));
4224 }
4225
4226 // use the first selected dataset
4227 auto _dsets = datasets();
4228 TString dsetName = "";
4229 for (auto &d : _dsets) {
4230 if (d->get()->TestBit(1 << 20)) {
4231 dsetName = d->get()->GetName();
4232 break;
4233 }
4234 }
4235 auto _nll = nll(dsetName.Data(), *defaultOpts);
4236 // can now set the values
4237 for (auto [p, v] : valsToSet) {
4238 p->setVal(v);
4239 }
4240 _nll.fitConfigOptions()->SetValue("LogSize", 65536);
4241 _nll.fitConfig()->MinimizerOptions().SetPrintLevel(0);
4242 auto fr = _nll.minimize();
4243 //_pars.argList() = *snap; // restore values - irrelevant as SetFitResult will restore values
4244 if (!fr.get())
4245 throw std::runtime_error("Fit Failed");
4246 SetFitResult(fr.get());
4248 for (unsigned int i = 0; i < fr->numStatusHistory(); i++) {
4249 statusCodes += TString::Format("\n%s = %d", fr->statusLabelHistory(i), fr->statusCodeHistory(i));
4250 }
4251 const TGWindow *w =
4252 (gROOT->GetListOfBrowsers()->At(0))
4253 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4254 : gClient->GetRoot();
4255 TString gofResult = "";
4256 if (_nll.fOpts->find("GoF")) {
4257 gofResult = TString::Format("GoF p-value = %g (mainTerm = %g)\n", fr->constPars().getRealValue(".pgof"),
4258 fr->constPars().getRealValue(".mainterm_pgof"));
4259 }
4260 if (fr->status() != 0) {
4261 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished with Bad Status Code",
4262 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n%s-------------%s",
4263 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), gofResult.Data(),
4264 statusCodes.Data()),
4266 } else if (fr->covQual() != 3 && _nll.fitConfig()->ParabErrors()) {
4267 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished with Bad Covariance Quality",
4268 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n%s-------------%s",
4269 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), gofResult.Data(),
4270 statusCodes.Data()),
4272 } else {
4273 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished Successfully",
4274 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n%s-------------%s",
4275 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), gofResult.Data(),
4276 statusCodes.Data()));
4277 }
4278 TBrowser *b = nullptr;
4279 for (auto a : *gROOT->GetListOfBrowsers()) {
4280 b = dynamic_cast<TBrowser *>(a);
4281 if (b && GetTreeItem(b)) {
4282 break;
4283 }
4284 }
4285 if (b) {
4286 auto p = GetTreeItem(b);
4287 while (p) {
4288 if (TString(p->GetText()).BeginsWith("RooWorkspace::")) {
4289 // found the workspace ... refresh this node, and if there's a fits node, refresh that
4290 if (auto bi = dynamic_cast<TRootBrowser *>(b->GetBrowserImp())) {
4291 if (auto fb = dynamic_cast<TGFileBrowser *>(bi->GetActBrowser())) {
4292 fb->DoubleClicked(p, 0);
4293 if (auto first = p->GetFirstChild()) {
4294 do {
4295 if (first->IsOpen() && TString(first->GetText()) == "fits") {
4296 fb->DoubleClicked(first, 0);
4297 }
4298 } while ((first = first->GetNextSibling()));
4299 }
4300 }
4301 }
4302 break;
4303 } else {
4304 p = p->GetParent();
4305 }
4306 }
4307 }
4308 } catch (const std::exception &e) {
4309 new TGMsgBox(
4310 gClient->GetRoot(),
4311 (gROOT->GetListOfBrowsers()->At(0))
4312 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4313 : gClient->GetRoot(),
4314 "Exception", e.what(), kMBIconExclamation, kMBOk); // deletes self on dismiss?
4315 }
4316}
4317
4318void xRooNode::_generate_(const char *datasetName, bool expected)
4319{
4320 try {
4321 datasets().Add(datasetName, expected ? "asimov" : "toy");
4322 // refresh datasets folder of workspace
4323 TBrowser *b = nullptr;
4324 for (auto a : *gROOT->GetListOfBrowsers()) {
4325 b = dynamic_cast<TBrowser *>(a);
4326 if (b && GetTreeItem(b)) {
4327 break;
4328 }
4329 }
4330 if (b) {
4331 auto p = GetTreeItem(b);
4332 while (p) {
4333 if (TString(p->GetText()).BeginsWith("RooWorkspace::")) {
4334 // found the workspace ... refresh this node, and if there's a datasets node, refresh that
4335 if (auto bi = dynamic_cast<TRootBrowser *>(b->GetBrowserImp())) {
4336 if (auto fb = dynamic_cast<TGFileBrowser *>(bi->GetActBrowser())) {
4337 fb->DoubleClicked(p, 0);
4338 if (auto first = p->GetFirstChild()) {
4339 do {
4340 if (first->IsOpen() && TString(first->GetText()) == "datasets") {
4341 fb->DoubleClicked(first, 0);
4342 }
4343 } while ((first = first->GetNextSibling()));
4344 }
4345 }
4346 }
4347 break;
4348 } else {
4349 p = p->GetParent();
4350 }
4351 }
4352 }
4353 } catch (const std::exception &e) {
4354 new TGMsgBox(
4355 gClient->GetRoot(),
4356 (gROOT->GetListOfBrowsers()->At(0))
4357 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4358 : gClient->GetRoot(),
4359 "Exception", e.what(),
4360 kMBIconExclamation); // deletes self on dismiss?
4361 }
4362}
4363
4364void xRooNode::_scan_(const char *what, double nToys, const char *xvar, int nBinsX, double lowX,
4365 double highX /*, const char*, int, double, double*/, const char *constParValues,
4366 const char *options)
4367{
4368 try {
4371
4372 // use the first selected dataset
4373 auto _dsets = datasets();
4374 TString dsetName = "";
4375 for (auto &d : _dsets) {
4376 if (d->get()->TestBit(1 << 20)) {
4377 dsetName = d->get()->GetName();
4378 break;
4379 }
4380 }
4381 auto _pars = pars();
4382 std::unique_ptr<RooAbsCollection> snap(_pars.argList().snapshot());
4383 TStringToken pattern(constParValues, ",");
4384 while (pattern.NextToken()) {
4385 auto idx = pattern.Index('=');
4386 TString pat = (idx == -1) ? TString(pattern) : TString(pattern(0, idx));
4387 double val =
4388 (idx == -1) ? std::numeric_limits<double>::quiet_NaN() : TString(pattern(idx + 1, pattern.Length())).Atof();
4389 bool foundArg = false;
4390 for (auto par : _pars.argList()) {
4391 if (TString(par->GetName()).Contains(TRegexp(pat, true))) {
4392 foundArg = true;
4393 par->setAttribute("Constant", true);
4394 if (!std::isnan(val)) {
4395 dynamic_cast<RooAbsRealLValue *>(par)->setVal(val);
4396 }
4397 }
4398 }
4399 if (!foundArg) {
4400 throw std::runtime_error(std::string("Unrecognised parameter: ") + pat.Data());
4401 }
4402 }
4403
4404 // parse options
4405 TStringToken pattern2(options, ",");
4407 while (pattern2.NextToken()) {
4408 auto idx = pattern2.Index('=');
4409 TString pat = (idx == -1) ? TString(pattern2) : TString(pattern2(0, idx));
4410 TString val = TString(pattern2(idx + 1, pattern2.Length()));
4411 if (auto o = defaultOpts->FindObject(pat)) {
4412 defaultOpts->Remove(o);
4413 delete o;
4414 }
4415 defaultOpts->Add(new RooCmdArg(pat, val.IsDec() ? val.Atoi() : 0, 0, val.IsFloat() ? val.Atof() : 0., 0.,
4416 val.IsAlpha() ? val : nullptr));
4417 }
4418
4419 auto hs = nll(dsetName.Data(), *defaultOpts).hypoSpace(sXvar);
4420 hs.SetName(TUUID().AsString());
4421 if (nToys) {
4422 sWhat += " toys";
4423 if (nToys > 0) {
4424 sWhat += TString::Format("=%g", nToys);
4425 }
4426 }
4427 hs.SetTitle(sWhat + " scan" + ((dsetName != "") ? TString::Format(" [data=%s]", dsetName.Data()) : ""));
4428 int scanStatus = hs.scan(sWhat + " visualize", nBinsX, lowX, highX);
4429 if (scanStatus != 0) {
4430 new TGMsgBox(
4431 gClient->GetRoot(),
4432 (gROOT->GetListOfBrowsers()->At(0))
4433 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4434 : gClient->GetRoot(),
4435 "Scan Finished with Bad Status Code",
4436 TString::Format("%s\nData = %s\nScan Status Code = %d", hs.GetName(), dsetName.Data(), scanStatus),
4438 }
4439 if (ws()) {
4440 if (auto res = hs.result())
4441 ws()->import(*res);
4442 }
4443
4444 _pars.argList() = *snap; // restore pars
4445
4446 TBrowser *b = nullptr;
4447 for (auto a : *gROOT->GetListOfBrowsers()) {
4448 b = dynamic_cast<TBrowser *>(a);
4449 if (b && GetTreeItem(b)) {
4450 break;
4451 }
4452 }
4453 if (b) {
4454 auto p = GetTreeItem(b);
4455 while (p) {
4456 if (TString(p->GetText()).BeginsWith("RooWorkspace::")) {
4457 // found the workspace ... refresh this node, and if there's a scans node, refresh that
4458 if (auto bi = dynamic_cast<TRootBrowser *>(b->GetBrowserImp())) {
4459 if (auto fb = dynamic_cast<TGFileBrowser *>(bi->GetActBrowser())) {
4460 fb->DoubleClicked(p, 0);
4461 if (auto first = p->GetFirstChild()) {
4462 do {
4463 if (first->IsOpen() && TString(first->GetText()) == "scans") {
4464 fb->DoubleClicked(first, 0);
4465 }
4466 } while ((first = first->GetNextSibling()));
4467 }
4468 }
4469 }
4470 break;
4471 } else {
4472 p = p->GetParent();
4473 }
4474 }
4475 }
4476
4477 } catch (const std::exception &e) {
4478 new TGMsgBox(
4479 gClient->GetRoot(),
4480 (gROOT->GetListOfBrowsers()->At(0))
4481 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4482 : gClient->GetRoot(),
4483 "Exception", e.what(), kMBIconExclamation);
4484 }
4485}
4486
4487void xRooNode::_SetBinContent_(int bin, double value, const char *par, double parVal)
4488{
4489 try {
4490 SetBinContent(bin, value, strlen(par) > 0 ? par : nullptr, parVal);
4491 } catch (const std::exception &e) {
4492 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
4493 kMBIconExclamation); // deletes self on dismiss?
4494 }
4495}
4496
4498{
4499 try {
4500#if ROOT_VERSION_CODE > ROOT_VERSION(6, 29, 00)
4501 // if this is a collection of values, populate a TF1 and display as a dialog
4502 if (!get() && TString(GetName()).BeginsWith("!")) {
4503 browse();
4505 for (auto a : *this) {
4506 if (auto arg = a->get<RooRealVar>())
4507 args.add(*arg);
4508 }
4509 TF1 f(GetName(), 0.0, 1.0, std::min(int(args.size()), 10));
4510 int i = 0;
4511 int j = 0;
4512 for (auto c : args) {
4513 j++;
4514 if (j < value) {
4515 continue;
4516 }
4517 auto v = dynamic_cast<RooRealVar *>(c);
4518 f.SetParName(i, c->GetName());
4519 if (v) {
4520 f.SetParLimits(i, v->getMin(), v->getMax());
4521 if (v->isConstant())
4522 f.FixParameter(i, v->getVal());
4523 else {
4524 f.SetParameter(i, v->getVal());
4525 f.SetParError(i, v->getError());
4526 }
4527 }
4528 i++;
4529 if (i == 10) {
4530 break; // max 10 pars shown
4531 }
4532 }
4533 int ret = 0;
4535 gClient->GetDefaultRoot(),
4536 (gROOT->GetListOfBrowsers()->At(0))
4537 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4538 : gClient->GetDefaultRoot(),
4539 &f, nullptr, &ret);
4540 if (ret) {
4541 // user has changed parameter values etc, propagate back to parameters
4542 for (i = 0; i < f.GetNpar(); i++) {
4543 auto c = args.find(f.GetParName(i));
4544 auto v = dynamic_cast<RooRealVar *>(c);
4545 if (v) {
4546 v->setVal(f.GetParameter(i));
4547 double low, high;
4548 f.GetParLimits(i, low, high);
4549 if (low == high) {
4550 v->setConstant(low); // if low==high==0 then is not marked constant
4551 } else {
4552 v->setRange(low, high);
4553 }
4554 }
4555 }
4556 }
4557 return;
4558 }
4559#endif
4560
4561 if (!SetContent(value))
4562 throw std::runtime_error("Failed to SetContent");
4563 } catch (const std::exception &e) {
4564 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
4565 kMBIconExclamation); // deletes self on dismiss?
4566 }
4567}
4568
4569bool xRooNode::SetBinContent(int bin, double value, const char *par, double parVal)
4570{
4571
4572 // create if needed
4573 if (!get()) {
4574 if (fParent && !find(GetName())) {
4575 // if have a binning we create a histogram to match it
4576 if (auto ax = GetXaxis(); ax) {
4577 std::shared_ptr<TH1D> h;
4578 auto _b = dynamic_cast<Axis2 *>(ax)->binning();
4579 if (_b->isUniform()) {
4580 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
4581 h.reset(new TH1D(GetName(), GetTitle(), _b->numBins(), _b->lowBound(), _b->highBound()));
4582 } else {
4583 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
4584 h.reset(new TH1D(GetName(), GetTitle(), _b->numBins(), _b->array()));
4585 }
4586 h->SetOption("nostyle"); // don't transfer style when added
4587 h->GetXaxis()->SetName(TString::Format("%s;%s", ax->GetParent()->GetName(), ax->GetName()));
4588 fComp = h;
4589 }
4590 fComp = fParent->Add(*this, "sample").fComp;
4591 }
4592 }
4593
4594 // if it's a RooProduct locate child with the same name
4595 if (get<RooProduct>()) {
4596 return factors()[GetName()]->SetBinContent(bin, value, par, parVal);
4597 }
4598
4599 if (get<RooAbsData>()) {
4600 if (auto _data = get<RooDataSet>(); _data) {
4601 auto _ax = (bin) ? GetXaxis() : nullptr;
4602 if (!_ax && bin) {
4603 throw std::runtime_error("Cannot determine binning to fill data");
4604 }
4605 if (_ax && _ax->GetNbins() < bin) {
4606 throw std::out_of_range(TString::Format("%s range %s only has %d bins", _ax->GetParent()->GetName(),
4607 _ax->GetName(), _ax->GetNbins()));
4608 }
4609 RooArgSet obs;
4610
4611 TString cut = "";
4612
4613 for (auto _c : coords()) { // coords() moves vars to their respective coordinates too
4614 if (auto _cat = _c->get<RooAbsCategoryLValue>(); _cat) {
4615 if (cut != "")
4616 cut += " && ";
4617 cut += TString::Format("%s==%d", _cat->GetName(), _cat->getCurrentIndex());
4618 obs.add(*_cat); // note: if we ever changed coords to return clones, would need to keep coords alive
4619 } else if (auto _rv = _c->get<RooAbsRealLValue>(); _rv) {
4620 // todo: check coordRange is a single range rather than multirange
4621 if (cut != "")
4622 cut += " && ";
4623 cut +=
4624 TString::Format("%s>=%f&&%s<%f", _rv->GetName(), _rv->getMin(_rv->getStringAttribute("coordRange")),
4625 _rv->GetName(), _rv->getMax(_rv->getStringAttribute("coordRange")));
4626 obs.add(*_rv); // note: if we ever changed coords to return clones, would need to keep coords alive
4627 } else {
4628 throw std::runtime_error("SetBinContent of data: Unsupported coordinate type");
4629 }
4630 }
4631
4632 RooFormulaVar cutFormula("cut1", cut, obs); // doing this to avoid complaints about unused vars
4633 RooFormulaVar icutFormula("icut1", TString::Format("!(%s)", cut.Data()), obs);
4634
4635 TString cut2;
4636 if (_ax) {
4637 cut2 = TString::Format("%s >= %f && %s < %f", _ax->GetParent()->GetName(), _ax->GetBinLowEdge(bin),
4638 _ax->GetParent()->GetName(), _ax->GetBinUpEdge(bin));
4639 obs.add(*dynamic_cast<RooAbsArg *>(_ax->GetParent()));
4640 } else {
4641 cut2 = "1==1";
4642 }
4643 RooFormulaVar cutFormula2("cut2", cut + " && " + cut2, obs);
4644 RooFormulaVar icutFormula2("icut2", TString::Format("!(%s && %s)", cut.Data(), cut2.Data()), obs);
4645
4646 // // go up through parents looking for slice obs
4647 // auto _p = fParent;
4648 // while(_p) {
4649 // TString pName(_p->GetName());
4650 // if (auto pos = pName.Index('='); pos != -1) {
4651 // if(auto _obs = _p->getObject<RooAbsLValue>(pName(0,pos)); _obs) {
4652 // if(auto _cat = dynamic_cast<RooAbsCategoryLValue*>(_obs.get()); _cat) {
4653 // _cat->setLabel(pName(pos+1,pName.Length()));
4654 // cut += TString::Format("%s%s==%d", (cut=="")?"":" && ",_cat->GetName(),
4655 // _cat->getCurrentIndex());
4656 // } else if(auto _var = dynamic_cast<RooAbsRealLValue*>(_obs.get()); _var) {
4657 // _var->setVal(TString(pName(pos+1,pName.Length())).Atof());
4658 // // TODO: Cut for this!!
4659 // }
4660 // obs.add(*dynamic_cast<RooAbsArg*>(_obs.get()));
4661 // } else {
4662 // throw std::runtime_error("Unknown observable, could not find");
4663 // }
4664 // }
4665 // _p = _p->fParent;
4666 // }
4667
4668 // add observables to dataset if necessary
4669 RooArgSet l(obs);
4670 l.remove(*_data->get(), true, true);
4671 if (!l.empty()) {
4672 // addColumns method is buggy: https://github.com/root-project/root/issues/8787
4673 // incredibly though, addColumn works??
4674 for (auto &x : l) {
4675 _data->addColumn(*x);
4676 }
4677 // instead create a copy dataset and merge it into current
4678 // cant use merge because it drops weightVar
4679 /*RooDataSet tmp("tmp","tmp",l);
4680 for(int i=0;i<_data->numEntries();i++) tmp.add(l);
4681 _data->merge(&tmp);*/
4682 // delete _data->addColumns(l);
4683 }
4684 // before adding, ensure range is good to cover
4685 for (auto &o : obs) {
4686 if (auto v = dynamic_cast<RooRealVar *>(o); v) {
4687 if (auto dv = dynamic_cast<RooRealVar *>(_data->get()->find(v->GetName())); dv) {
4688 if (v->getMin() < dv->getMin())
4689 dv->setMin(v->getMin());
4690 if (v->getMax() > dv->getMax())
4691 dv->setMax(v->getMax());
4692 }
4693 } else if (auto c = dynamic_cast<RooCategory *>(o); c) {
4694 if (auto dc = dynamic_cast<RooCategory *>(_data->get()->find(c->GetName())); dc) {
4695 if (!dc->hasLabel(c->getCurrentLabel())) {
4696 dc->defineType(c->getCurrentLabel(), c->getCurrentIndex());
4697 }
4698 }
4699 }
4700 }
4701
4702 // using SetBinContent means dataset must take on a binned form at these coordinates
4703 // if number of entries doesnt match number of bins then will 'bin' the data
4704 if (bin) {
4705 if (auto _nentries = std::unique_ptr<RooAbsData>(_data->reduce(cutFormula))->numEntries();
4706 _nentries != _ax->GetNbins()) {
4707 auto _contents = GetBinContents(1, _ax->GetNbins());
4708
4709 if (_nentries > 0) {
4710 Info("SetBinContent", "Binning %s in channel: %s", GetName(), cut.Data());
4711 auto _reduced = std::unique_ptr<RooAbsData>(_data->reduce(icutFormula));
4712 _data->reset();
4713 for (int j = 0; j < _reduced->numEntries(); j++) {
4714 auto _obs = _reduced->get(j);
4715 _data->add(*_obs, _reduced->weight());
4716 }
4717 }
4718 for (int i = 1; i <= _ax->GetNbins(); i++) {
4719 // can skip over the bin we will be setting to save a reduce step below
4720 if (i == bin)
4721 continue;
4722 dynamic_cast<RooAbsLValue *>(_ax->GetParent())->setBin(i - 1, _ax->GetName());
4723 _data->add(obs, _contents.at(i - 1));
4724 }
4725 }
4726 }
4727 // remove existing entries
4728 if (std::unique_ptr<RooAbsData>(_data->reduce(cutFormula2))->numEntries() > 0) {
4729 auto _reduced = std::unique_ptr<RooAbsData>(_data->reduce(icutFormula2));
4730 _data->reset();
4731 for (int j = 0; j < _reduced->numEntries(); j++) {
4732 auto _obs = _reduced->get(j);
4733 _data->add(*_obs, _reduced->weight());
4734 }
4735 }
4736 if (_ax)
4737 dynamic_cast<RooAbsLValue *>(_ax->GetParent())->setBin(bin - 1, _ax->GetName());
4738 _data->add(obs, value);
4739 if (auto bb = getBrowsable(".sourceds"))
4740 return bb->SetBinContent(bin, value, par, parVal); // apply to source ds if we have one
4741 return true;
4742
4743 } else if (get<RooDataHist>()) {
4744 throw std::runtime_error("RooDataHist not supported yet");
4745 }
4746 }
4747
4748 if (auto _varies = variations(); !_varies.empty() || (par && strlen(par))) {
4749 if (!par || strlen(par) == 0) {
4750 return _varies["nominal"]->SetBinContent(bin, value, par, parVal);
4751 } else if (auto it = _varies.find(Form("%s=%g", par, parVal)); it) {
4752 return it->SetBinContent(bin, value);
4753 } else {
4754 // need to create the variation : note - if no variations existed up to now this will inject a new node
4755 // so we should redirect ourself to the new node
4756 // TODO: Do we need to redirect parents?
4757 TString s = Form("%s=%g", par, parVal);
4758 return Vary(s.Data()).SetBinContent(bin, value);
4759 }
4760 }
4761
4762 auto o = get();
4763 if (auto p = dynamic_cast<RooRealVar *>(o); p) {
4764 if (!par || strlen(par) == 0) {
4765 if (p->getMax() < value)
4766 p->setMax(value);
4767 if (p->getMin() > value)
4768 p->setMin(value);
4769 p->setVal(value);
4770 sterilize();
4771 return true;
4772 }
4773
4774 } else if (auto c = dynamic_cast<RooConstVar *>(o); c) {
4775
4776 // if parent is a FlexibleInterpVar, change the value in that .
4777 if (strcmp(c->GetName(), Form("%g", c->getVal())) == 0) {
4778 c->SetNameTitle(Form("%g", value), Form("%g", value));
4779 }
4780#if ROOT_VERSION_CODE < ROOT_VERSION(6, 24, 00)
4781 c->_value = value; // in future ROOT versions there is a changeVal method!
4782#else
4783 c->changeVal(value);
4784#endif
4785
4787 fParent->Vary(*this);
4788 }
4789
4790 sterilize();
4791 return true;
4792 } else if (auto f = dynamic_cast<RooHistFunc *>(o); f) {
4793 auto bin_pars = f->dataHist().get(bin - 1);
4794 if (f->getAttribute("density")) {
4795 value /= f->dataHist().binVolume(*bin_pars);
4796 }
4797 f->dataHist().set(*bin_pars, value);
4798 f->setValueDirty();
4799
4800 if (auto otherfName = f->getStringAttribute("symmetrized_by"); otherfName) {
4801 // broken symmetry, so update flags ...
4802 f->setStringAttribute("symmetrized_by", nullptr);
4803 if (auto x = getObject<RooAbsArg>(otherfName); x) {
4804 x->setStringAttribute("symmetrizes", nullptr);
4805 x->setStringAttribute("symmetrize_nominal", nullptr);
4806 }
4807 } else if (auto otherfName2 = f->getStringAttribute("symmetrizes"); otherfName2) {
4808 auto nomf = getObject<RooHistFunc>(f->getStringAttribute("symmetrize_nominal"));
4810 if (nomf && otherf) {
4811 otherf->dataHist().set(*bin_pars, 2 * nomf->dataHist().weight(bin - 1) - value);
4812 otherf->setValueDirty();
4813 }
4814 }
4815 sterilize();
4816 return true;
4817 } else if (auto f2 = dynamic_cast<RooStats::HistFactory::FlexibleInterpVar *>(o); f2) {
4818 // changing nominal value
4819 f2->setNominal(value);
4820 }
4821 throw std::runtime_error(TString::Format("unable to set bin content of %s", GetPath().c_str()));
4822}
4823
4824bool xRooNode::SetBinData(int bin, double value, const xRooNode &data)
4825{
4826 if (data.get<RooAbsData>()) {
4827 // attach as a child before calling datasets(), so that is included in the list
4828 push_back(std::make_shared<xRooNode>(data));
4829 }
4830 auto node = datasets()[data.GetName()];
4831 if (data.get<RooAbsData>()) {
4832 // remove the child we attached
4833 resize(size() - 1);
4834 }
4835 return node->SetBinContent(bin, value);
4836}
4837
4838bool xRooNode::SetData(const TObject &obj, const xRooNode &data)
4839{
4840 if (data.get<RooAbsData>()) {
4841 // attach as a child before calling datasets(), so that is included in the list
4842 push_back(std::make_shared<xRooNode>(data));
4843 }
4844 auto node = datasets()[data.GetName()];
4845 if (data.get<RooAbsData>()) {
4846 // remove the child we attached
4847 resize(size() - 1);
4848 }
4849 return node->SetContents(obj);
4850}
4851
4852bool xRooNode::SetBinError(int bin, double value)
4853{
4854
4855 // if it's a RooProduct locate child with the same name
4856 if (get<RooProduct>()) {
4857 return factors()[GetName()]->SetBinError(bin, value);
4858 }
4859
4860 if (auto _varies = variations(); !_varies.empty()) {
4861 return _varies["nominal"]->SetBinError(bin, value);
4862 }
4863
4864 auto o = get();
4865
4866 if (auto f = dynamic_cast<RooHistFunc *>(o); f) {
4867
4868 // if (f->getAttribute("density")) { value /= f->dataHist().binVolume(*bin_pars); } - commented out because DON'T
4869 // convert .. sumw and sumw2 attributes will be stored not as densities
4870
4871 // NOTE: Can only do this because factors() makes parents of its children it's own parent (it isn't the parent)
4872 // If ever make factors etc part of the parentage then this would need tweaking to get to the true parent
4873 // find first parent that is a RooProduct, that is where the statFactor would live
4874 // stop as soon as we reach pdf object
4875 auto _prodParent = fParent;
4876 while (_prodParent && !_prodParent->get<RooProduct>() && !_prodParent->get<RooAbsPdf>()) {
4877 if (_prodParent->get<PiecewiseInterpolation>() && strcmp(GetName(), "nominal")) {
4878 _prodParent.reset();
4879 break; // only the 'nominal' variation can look for a statFactor outside the variation container
4880 }
4881 _prodParent = _prodParent->fParent;
4882 }
4883 auto _f_stat =
4884 (_prodParent && !_prodParent->get<RooAbsPdf>()) ? _prodParent->factors().find("statFactor") : nullptr;
4885 auto f_stat = (_f_stat) ? _f_stat->get<ParamHistFunc>() : nullptr;
4886 if (_f_stat && _f_stat->get() && !f_stat) {
4887 throw std::runtime_error("stat factor must be a paramhistfunc");
4888 }
4889
4890 // stat uncertainty lives in the "statFactor" factor, each sample has its own one,
4891 // but they can share parameters
4892 if (!f_stat) {
4893 if (value == 0)
4894 return true;
4896 for (auto &p : xRooNode("tmp", *f, std::shared_ptr<xRooNode>(nullptr)).vars()) {
4897 if (parNames != "")
4898 parNames += ",";
4899 parNames += p->get()->GetName();
4900 }
4901 auto h = std::unique_ptr<TH1>(f->dataHist().createHistogram(parNames
4902#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 27, 00)
4903 ,
4905#endif
4906 ));
4907 h->Reset();
4908 h->SetName("statFactor");
4909 h->SetTitle(TString::Format("StatFactor of %s", f->GetTitle()));
4910 h->SetOption("blankshape");
4911
4912 // multiply parent if is nominal
4913 auto toMultiply = this;
4914 if (strcmp(GetName(), "nominal") == 0 && fParent && fParent->get<PiecewiseInterpolation>())
4915 toMultiply = fParent.get();
4916
4917 f_stat = dynamic_cast<ParamHistFunc *>(toMultiply->Multiply(*h).get());
4918 if (!f_stat) {
4919 throw std::runtime_error("Failed creating stat shapeFactor");
4920 }
4921 }
4922
4923 auto phf = f_stat;
4924
4925 TString prefix = f->getStringAttribute("statPrefix");
4926 if (value && prefix == "") {
4927 // find the first parent that can hold components (RooAddPdf, RooRealSumPdf, RooAddition, RooWorkspace) ... use
4928 // that name for the stat factor
4929 auto _p = fParent;
4930 while (_p && !(_p->get()->InheritsFrom("RooRealSumPdf") || _p->get()->InheritsFrom("RooAddPdf") ||
4931 _p->get()->InheritsFrom("RooWorkspace") || _p->get()->InheritsFrom("RooAddition"))) {
4932 _p = _p->fParent;
4933 }
4934 prefix = TString::Format("stat_%s", (_p && _p->get<RooAbsReal>()) ? _p->get()->GetName() : f->GetName());
4935 }
4936 auto newVar = (value == 0) ? getObject<RooRealVar>("1")
4937 : acquire<RooRealVar>(Form("%s_bin%d", prefix.Data(), bin),
4938 Form("#gamma^{%s}_{%d}", prefix.Data(), bin), 1);
4939#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
4940 RooArgList &pSet = phf->_paramSet;
4941#else
4942 RooArgList &pSet = const_cast<RooArgList &>(phf->paramList());
4943#endif
4944 auto var = dynamic_cast<RooRealVar *>(&pSet[bin - 1]);
4945
4946 if (newVar.get() != var) {
4947 // need to swap out var for newVar
4948 // replace ith element in list with new func, or inject into RooProduct
4950 for (std::size_t i = 0; i < pSet.size(); i++) {
4951 if (int(i) != bin - 1) {
4952 all.add(*pSet.at(i));
4953 } else {
4954 all.add(*newVar);
4955 }
4956 }
4957 pSet.removeAll();
4958 pSet.add(all);
4959 }
4960
4961 xRooNode v((value == 0) ? *var : *newVar, *this);
4962 auto rrv = dynamic_cast<RooRealVar *>(v.get());
4963 if (strcmp(rrv->GetName(), "1") != 0) {
4964 TString origName = (f->getStringAttribute("origName")) ? f->getStringAttribute("origName") : GetName();
4965 rrv->setStringAttribute(Form("sumw2_%s", origName.Data()), TString::Format("%f", pow(value, 2)));
4966 auto bin_pars = f->dataHist().get(bin - 1);
4967 auto _binContent = f->dataHist().weight(bin - 1);
4968 if (f->getAttribute("density")) {
4969 _binContent *= f->dataHist().binVolume(*bin_pars);
4970 }
4971 rrv->setStringAttribute(Form("sumw_%s", origName.Data()), TString::Format("%f", _binContent));
4972 double sumw2 = 0;
4973 double sumw = 0;
4974 for (auto &[s, sv] : rrv->stringAttributes()) {
4975 if (s.find("sumw_") == 0) {
4976 sumw += TString(sv).Atof();
4977 } else if (s.find("sumw2_") == 0) {
4978 sumw2 += TString(sv).Atof();
4979 }
4980 }
4981 if (sumw2 && sumw2 != std::numeric_limits<double>::infinity()) {
4982 double tau = pow(sumw, 2) / sumw2;
4983 rrv->setError((tau < 1e-15) ? 1e15 : (/*rrv->getVal()*/ 1. / sqrt(tau))); // not sure why was rrv->getVal()?
4984 rrv->setConstant(false);
4985 // parameter must be constrained
4986 auto _constr = v.constraints();
4987 // std::cout << " setting constraint " << v.GetName() << " nomin=" << tau << std::endl;
4988 if (_constr.empty()) {
4989 rrv->setStringAttribute("boundConstraint", _constr.Add("poisson").get()->GetName());
4990 } else {
4991 auto _glob = _constr.at(0)->obs().at(0)->get<RooRealVar>();
4992 // TODO: Update any globs snapshots that are designed to match the nominal
4993 _glob->setStringAttribute("nominal", TString::Format("%f", tau));
4994 double _min = tau * (1. - 5. * sqrt(1. / tau));
4995 double _max = tau * (1. + 5. * sqrt(1. / tau));
4996 _glob->setRange(_min, _max);
4997 _glob->setVal(tau);
4998 _constr.at(0)->pp().at(0)->SetBinContent(0, tau);
4999 rrv->setStringAttribute("boundConstraint", _constr.at(0)->get()->GetName());
5000 }
5001 rrv->setRange(std::max((1. - 5. * sqrt(1. / tau)), 1e-15), 1. + 5. * sqrt(1. / tau));
5002 } else {
5003 // remove constraint
5004 if (auto _constr = v.constraints(); !_constr.empty()) {
5005 v.constraints().Remove(*_constr.at(0));
5006 }
5007 // set const if sumw2 is 0 (i.e. no error)
5008 rrv->setVal(1);
5009 rrv->setError(0);
5010 rrv->setConstant(sumw2 == 0);
5011 }
5012 }
5013
5014 return true;
5015 }
5016
5017 throw std::runtime_error(TString::Format("%s SetBinError failed", GetName()));
5018}
5019
5020std::shared_ptr<xRooNode> xRooNode::at(const std::string &name, bool browseResult) const
5021{
5022 auto res = find(name, browseResult);
5023 if (res == nullptr)
5024 throw std::out_of_range(name + " does not exist");
5025 return res;
5026}
5027
5028////////////////////////////////////////////////////////////////////////////////
5029/// The RooWorkspace this node belong to, if any
5030
5032{
5033 if (auto _w = get<RooWorkspace>(); _w)
5034 return _w;
5035 if (auto a = get<RooAbsArg>(); a && GETWS(a)) {
5036 return GETWS(a);
5037 }
5038 if (fParent)
5039 return fParent->ws();
5040 return nullptr;
5041}
5042
5044{
5045
5046 xRooNode out(".constraints", nullptr, *this);
5047
5048 std::function<RooAbsPdf *(const xRooNode &n, RooAbsArg &par, std::set<RooAbsPdf *> ignore)> getConstraint;
5049 getConstraint = [&](const xRooNode &n, RooAbsArg &par, std::set<RooAbsPdf *> ignore) {
5050 if (auto _pdf = n.get<RooAbsPdf>()) {
5051 if (ignore.count(_pdf))
5052 return (RooAbsPdf *)nullptr;
5053 ignore.insert(_pdf);
5054 }
5055 auto o = n.get<RooProdPdf>();
5056 if (!o) {
5057 if (n.get<RooSimultaneous>()) {
5058 // check all channels for a constraint if is simultaneous
5059 for (auto &c : n.bins()) {
5060 if (auto oo = getConstraint(*c, par, ignore); oo) {
5061 return oo;
5062 }
5063 }
5064 return (RooAbsPdf *)nullptr;
5065 } else if (n.get<RooAbsPdf>() && n.fParent && n.fParent->get<RooWorkspace>()) {
5066 // reached top-level pdf, which wasn't a simultaneous, so stop here
5067 return (RooAbsPdf *)nullptr;
5068 } else if (auto _ws = n.get<RooWorkspace>(); _ws) {
5069 // reached a workspace, check for any pdf depending on parameter that isnt the ignore
5070 for (auto p : _ws->allPdfs()) {
5071 if (ignore.count(static_cast<RooAbsPdf *>(p)))
5072 continue;
5073 if (p->dependsOn(par)) {
5074 out.emplace_back(std::make_shared<xRooNode>(par.GetName(), *p, *this));
5075 }
5076 }
5077 }
5078 if (!n.fParent)
5079 return (RooAbsPdf *)nullptr;
5080 return getConstraint(*n.fParent, par, ignore);
5081 }
5082 for (auto p : o->pdfList()) {
5083 if (ignore.count(static_cast<RooAbsPdf *>(p)))
5084 continue;
5085 if (p->dependsOn(par)) {
5086 out.emplace_back(std::make_shared<xRooNode>(par.GetName(), *p, *this));
5087 }
5088 }
5089 return (RooAbsPdf *)nullptr;
5090 };
5091
5092 for (auto &p : vars()) {
5093 auto v = dynamic_cast<RooAbsReal *>(p->get());
5094 if (!v)
5095 continue;
5096 if (v->getAttribute("Constant") && v != get<RooAbsReal>())
5097 continue; // skip constants unless we are getting the constraints of a parameter itself
5098 if (v->getAttribute("obs"))
5099 continue; // skip observables ... constraints constrain pars not obs
5100 getConstraint(*this, *v, {get<RooAbsPdf>()});
5101 /*if (auto c = ; c) {
5102 out.emplace_back(std::make_shared<Node2>(p->GetName(), *c, *this));
5103 }*/
5104 }
5105
5106 // finish by removing any constraint that contains another constraint for the same par
5107 // and consolidate common pars
5108 auto it = out.std::vector<std::shared_ptr<xRooNode>>::begin();
5109 while (it != out.std::vector<std::shared_ptr<xRooNode>>::end()) {
5110 bool removeIt = false;
5111 for (auto &c : out) {
5112 if (c.get() == it->get())
5113 continue;
5114 if ((*it)->get<RooAbsArg>()->dependsOn(*c->get<RooAbsArg>())) {
5115 removeIt = true;
5116 std::set<std::string> parNames;
5117 std::string _cName = c->GetName();
5118 do {
5119 parNames.insert(_cName.substr(0, _cName.find(';')));
5120 _cName = _cName.substr(_cName.find(';') + 1);
5121 } while (_cName.find(';') != std::string::npos);
5122 parNames.insert(_cName);
5123 _cName = it->get()->GetName();
5124 do {
5125 parNames.insert(_cName.substr(0, _cName.find(';')));
5126 _cName = _cName.substr(_cName.find(';') + 1);
5127 } while (_cName.find(';') != std::string::npos);
5128 parNames.insert(_cName);
5129 _cName = "";
5130 for (auto &x : parNames) {
5131 if (!_cName.empty())
5132 _cName += ";";
5133 _cName += x;
5134 }
5135 c->TNamed::SetName(_cName.c_str());
5136 break;
5137 }
5138 }
5139 if (removeIt) {
5140 it = out.erase(it);
5141 } else {
5142 ++it;
5143 }
5144 }
5145
5146 // if getting constraints of a fundamental then use the constraint names instead of the par name (because would be
5147 // all same otherwise)
5148 if (get<RooAbsArg>() && get<RooAbsArg>()->isFundamental()) {
5149 for (auto &o : out) {
5150 o->TNamed::SetName(o->get()->GetName());
5151 }
5152 }
5153
5154 return out;
5155}
5156
5157std::shared_ptr<TObject> xRooNode::convertForAcquisition(xRooNode &acquirer, const char *opt) const
5158{
5159
5160 TString sOpt(opt);
5161 sOpt.ToLower();
5163 if (sOpt == "func")
5164 sName = TString("factory:") + sName;
5165
5166 // if arg is a histogram, will acquire it as a RooHistFunc unless no conversion
5167 // todo: could flag not to convert
5168 if (auto h = get<TH1>(); h) {
5169 TString sOpt2(h->GetOption());
5170 std::map<std::string, std::string> stringAttrs;
5171 while (sOpt2.Contains("=")) {
5172 auto pos = sOpt2.Index("=");
5173 auto start = sOpt2.Index(";") + 1;
5174 if (start > pos)
5175 start = 0;
5176 auto end = sOpt2.Index(";", pos);
5177 if (end == -1)
5178 end = sOpt2.Length();
5179 stringAttrs[sOpt2(start, pos - start)] = sOpt2(pos + 1, end - pos - 1);
5180 sOpt2 = TString(sOpt2(0, start)) + TString(sOpt2(end + 1, sOpt2.Length()));
5181 }
5184 if (origName.BeginsWith(';'))
5185 origName = origName(1, origName.Length());
5186 if (newObjName.BeginsWith(';')) {
5187 newObjName =
5188 newObjName(1, newObjName.Length()); // special case if starts with ';' then don't create a fancy name
5189 } else if (acquirer.get() && !acquirer.get<RooWorkspace>()) {
5191 "%s_%s", (acquirer.mainChild().get()) ? acquirer.mainChild()->GetName() : acquirer->GetName(),
5192 newObjName.Data());
5193 }
5194 // can convert to a RooHistFunc, or RooParamHist if option contains 'shape'
5195 TString varName = h->GetXaxis()->GetName();
5196 std::string binningName = newObjName.Data();
5197 if (auto pos = varName.Index(';'); pos != -1) {
5198 binningName = varName(pos + 1, varName.Length());
5199 varName = varName(0, pos);
5200 }
5201
5202 if (varName == "xaxis" &&
5203 !acquirer.get<RooSimultaneous>()) { // default case, try to take axis var and binning from the acquirer
5204 if (auto ax = acquirer.GetXaxis(); ax) {
5205 varName = ax->GetParent()->GetName();
5206 // TODO: check the binning is consistent before using - at least will check nBins below
5207 binningName = ax->GetName();
5208 } else if (acquirer.obs().size() == 1)
5209 varName = acquirer.obs().at(0)->get()->GetName(); // TODO what if no obs but Xaxis var is defined?
5210 }
5211 auto x = acquirer.acquire<RooRealVar>(varName, h->GetXaxis()->GetTitle(), h->GetXaxis()->GetXmin(),
5212 h->GetXaxis()->GetXmax());
5213 if (x->getMin() > h->GetXaxis()->GetXmin())
5214 x->setMin(h->GetXaxis()->GetXmin());
5215 if (x->getMax() < h->GetXaxis()->GetXmax())
5216 x->setMax(h->GetXaxis()->GetXmax());
5217 if (!x->hasBinning(binningName.c_str())) {
5218 if (h->GetXaxis()->IsVariableBinSize()) {
5219 x->setBinning(RooBinning(h->GetNbinsX(), h->GetXaxis()->GetXbins()->GetArray()), binningName.c_str());
5220 } else {
5221 x->setBinning(
5222 RooUniformBinning(h->GetXaxis()->GetXmin(), h->GetXaxis()->GetXmax(), h->GetXaxis()->GetNbins()),
5223 binningName.c_str());
5224 }
5225 x->getBinning(binningName.c_str()).SetTitle(h->GetXaxis()->GetTitle());
5226 if (x->getBinningNames().size() == 2) {
5227 // this was the first binning, so copy it over to be the default binning too
5228 x->setBinning(x->getBinning(binningName.c_str()));
5229 }
5230 } else {
5231 // TODO check binning is compatible with histogram
5232 if (x->getBinning(binningName.c_str()).numBins() != h->GetNbinsX()) {
5233 throw std::runtime_error(
5234 TString::Format("binning mismatch for binning %s of %s", binningName.c_str(), x->GetName()));
5235 }
5236 }
5237
5238 std::shared_ptr<RooAbsArg> _f;
5239
5240 // if acquirer is a RooSimultaneous, will use histogram to define a channel
5241 if (acquirer.get<RooSimultaneous>()) {
5242 _f = acquirer.acquireNew<RooProdPdf>(newObjName, (strlen(h->GetTitle())) ? h->GetTitle() : h->GetName(),
5243 RooArgList());
5244 for (auto &[k, v] : stringAttrs) {
5245 _f->setStringAttribute(k.c_str(), v.c_str());
5246 }
5247 x->setAttribute("obs", true);
5248 } else if (sOpt2.Contains("shape")) {
5249 RooArgList list;
5250 for (int i = 0; i < x->getBinning(binningName.c_str()).numBins(); i++) {
5251 std::shared_ptr<RooAbsArg> arg;
5252 if (sOpt2.Contains("blankshape")) {
5253 arg = acquirer.acquire2<RooAbsArg, RooRealVar>("1", "1", 1);
5254 } else {
5255 if (!h) {
5256 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1), "", 1);
5257 } else if (h->GetMinimumStored() != -1111 || h->GetMaximumStored() != -1111) {
5258 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1),
5259 TString::Format("%s_{%d}", h->GetTitle(), i + 1),
5260 h->GetBinContent(i + 1), h->GetMinimumStored(),
5261 h->GetMaximumStored());
5262 } else {
5263 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1),
5264 TString::Format("%s_{%d}", h->GetTitle(), i + 1),
5265 h->GetBinContent(i + 1));
5266 }
5267 }
5268 list.add(*arg);
5269 }
5270 // paramhistfunc requires the binnings to be loaded as default at construction time
5271 // so load binning temporarily
5272 auto tmp = dynamic_cast<RooAbsBinning *>(x->getBinningPtr(nullptr)->Clone());
5273 x->setBinning(x->getBinning(binningName.c_str()));
5274 _f = acquirer.acquireNew<ParamHistFunc>(newObjName, h->GetTitle(), *x, list);
5275#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
5276 dynamic_cast<ParamHistFunc *>(_f.get())->_paramSet.setName("paramSet"); // so can see when print
5277#else
5278 const_cast<RooArgList &>(dynamic_cast<ParamHistFunc *>(_f.get())->paramList())
5279 .setName("paramSet"); // so can see when print
5280#endif
5281 x->setBinning(*tmp); // restore binning
5282 delete tmp;
5283 for (auto &[k, v] : stringAttrs) {
5284 _f->setStringAttribute(k.c_str(), v.c_str());
5285 }
5286 } else {
5287 auto dh = acquirer.acquireNew<RooDataHist>(Form("hist_%s", newObjName.Data()), h->GetTitle(), *x,
5288 binningName.c_str() /* binning name*/);
5289 if (!dh) {
5290 throw std::runtime_error("Couldn't make data hist");
5291 }
5292 auto f = acquirer.acquireNew<RooHistFunc>(newObjName, h->GetTitle(), *x, *dh,
5293 0 /*interpolation order between bins*/);
5294 f->forceNumInt();
5295 f->setAttribute("autodensity"); // where it gets inserted will determine if this is a density or not
5296 _f = f;
5297
5298 for (auto &[k, v] : stringAttrs) {
5299 _f->setStringAttribute(k.c_str(), v.c_str());
5300 }
5301
5302 // need to do these settings here because used in the assignment step
5303 _f->setStringAttribute("xvar", x->GetName());
5304 _f->setStringAttribute("binning", binningName.c_str());
5305 if (strcmp(_f->GetName(), origName.Data()) && !_f->getStringAttribute("alias"))
5306 _f->setStringAttribute("alias", origName);
5307
5308 // copy values over using the assignment operator - may convert to a RooProduct if there are stat uncerts
5309 xRooNode tmp(h->GetName(), _f, acquirer);
5310 tmp = *h;
5311 _f = std::dynamic_pointer_cast<RooAbsArg>(tmp.fComp); // in case got upgrade to a RooProduct
5312 }
5313
5314 _f->setStringAttribute("xvar", x->GetName());
5315 _f->setStringAttribute("binning", binningName.c_str());
5316 // style(h); // will transfer styling to object if necessary - not doing because this method used with plane hists
5317 // frequently
5318 if (strcmp(_f->GetName(), origName.Data()) && !_f->getStringAttribute("alias"))
5319 _f->setStringAttribute("alias", origName);
5320
5321 fComp = _f;
5322 return _f;
5323 } else if (!get() && (sName.BeginsWith("factory:") || sName.Contains("::")) && acquirer.ws()) {
5324 TString s(sName);
5325 if (sName.BeginsWith("factory:"))
5326 s = TString(s(8, s.Length()));
5327 fComp.reset(acquirer.ws()->factory(s), [](TObject *) {});
5328 if (fComp) {
5329 const_cast<xRooNode *>(this)->TNamed::SetName(fComp->GetName());
5330 }
5331 return fComp;
5332 }
5333
5334 return fComp;
5335}
5336
5337std::shared_ptr<TStyle> xRooNode::style(TObject *initObject, bool autoCreate) const
5338{
5339 return std::dynamic_pointer_cast<TStyle>(styles(initObject, autoCreate).fComp);
5340}
5341
5343{
5344 TString t = GetTitle();
5345
5346 auto arg = get<RooAbsArg>();
5347 if (!initObject && !arg && !gROOT->GetStyle(t)) {
5348 return nullptr;
5349 }
5350
5351 std::unique_ptr<TObject> argInitObject;
5352
5353 if (initObject) {
5354 t = (strlen(initObject->GetTitle())) ? initObject->GetTitle() : initObject->GetName();
5355 } else if (arg) {
5356 if (arg->getStringAttribute("style")) {
5357 t = arg->getStringAttribute("style");
5358 } else if (autoCreate) {
5359 // args will default to histo's object styling, whatever that currently may be
5360 argInitObject = std::make_unique<TH1D>(GetName(), GetTitle(), 1, 0, 1);
5361 initObject = argInitObject.get();
5362 } else {
5363 return nullptr;
5364 }
5365 }
5366
5367 std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case getObject has decided to
5368 // return the owning ptr (for some reason)
5369 if (!gROOT->GetStyle(t)) {
5370 if ((style = getObject<TStyle>(t.Data()))) {
5371 // loaded style (from workspace?) so put in list and use that
5372 gROOT->GetListOfStyles()->Add(style.get());
5373 } else {
5374 if (!autoCreate)
5375 return nullptr;
5376 // create new style - gets put in style list automatically so don't have to delete
5377 // acquire them so saved to workspaces for auto reload ...
5378 style = const_cast<xRooNode &>(*this).acquireNew<TStyle>(t.Data(),
5379 TString::Format("Style for %s component", t.Data()));
5380 if (auto x = dynamic_cast<TAttLine *>(initObject))
5381 ((TAttLine &)*style) = *x;
5382 if (auto x = dynamic_cast<TAttFill *>(initObject))
5383 ((TAttFill &)*style) = *x;
5384 if (auto x = dynamic_cast<TAttMarker *>(initObject))
5385 ((TAttMarker &)*style) = *x;
5386 gROOT->GetListOfStyles()->Add(style.get());
5387 }
5388 } else {
5389 style = std::shared_ptr<TStyle>(gROOT->GetStyle(t), [](TStyle *) {});
5390 }
5391
5392 if (arg && !arg->getStringAttribute("style")) {
5393 arg->setStringAttribute("style", style->GetName());
5394 }
5395
5396 return xRooNode(style, *this);
5397}
5398
5399std::shared_ptr<TObject> xRooNode::acquire(const std::shared_ptr<TObject> &arg, bool checkFactory, bool mustBeNew)
5400{
5401 if (!arg)
5402 return nullptr;
5403 if (!fAcquirer && !get<RooWorkspace>() && fParent)
5404 return fParent->acquire(arg, checkFactory, mustBeNew);
5405
5406 // if has a workspace and our object is the workspace or is in the workspace then add this object to workspace
5407 auto _ws = (fAcquirer) ? nullptr : ws();
5408 if (_ws && (get() == _ws || _ws->arg(GetName()) || (arg && strcmp(arg->GetName(), GetName()) == 0))) {
5410 RooMsgService::instance().setGlobalKillBelow(RooFit::WARNING);
5411 if (auto a = dynamic_cast<RooAbsArg *>(arg.get()); a) {
5412 auto out_arg = _ws->arg(a->GetName());
5413 TString aName = arg->GetName();
5414 int ii = 1;
5415 while (out_arg && mustBeNew) {
5416 a->SetName(TString::Format("%s_%d", aName.Data(), ii++));
5417 out_arg = _ws->arg(a->GetName());
5418 }
5419 if (aName != a->GetName())
5420 Warning("acquire", "Renaming to %s", a->GetName());
5421 if (!out_arg) {
5422 bool done = false;
5423 if (checkFactory) {
5424 if (auto res = _ws->factory(arg->GetName()); res) {
5425 a = res;
5426 done = true;
5427 }
5428 }
5429 if (!done && _ws->import(*a, RooFit::RecycleConflictNodes())) {
5430 if (GETWS(a) != _ws) {
5431 Info("acquire", "A copy of %s has been added to workspace %s", a->GetName(), _ws->GetName());
5432 }
5433 RooMsgService::instance().setGlobalKillBelow(msglevel);
5434 return nullptr;
5435 }
5436 // sanitizeWS(); // clears the caches that might exist up to now, as well interfere with getParameters calls
5437 std::set<std::string> setNames;
5438 for (auto &aa : GETWSSETS(_ws)) {
5439 if (TString(aa.first.c_str()).BeginsWith("CACHE_")) {
5440 setNames.insert(aa.first);
5441 }
5442 }
5443 for (auto &aa : setNames)
5444 ws()->removeSet(aa.c_str());
5445 out_arg = _ws->arg(a->GetName());
5446 if (GETWS(out_arg) != _ws) { // seems that when objects imported their ws isn't set
5447 out_arg->setWorkspace(*_ws);
5448 }
5449 // if any of the leaf nodes of the imported object have "global" label on them, ensure propagate to
5450 // "globalObservables" list if ws has one
5451 if (auto globs = const_cast<RooArgSet *>(ws()->set("globalObservables")); globs) {
5453 out_arg->leafNodeServerList(&leafs);
5454 std::unique_ptr<RooAbsCollection> globals(leafs.selectByAttrib("global", true));
5455 for (auto &aa : *globals) {
5456 if (!globs->contains(*aa)) {
5457 globs->add(*aa);
5458 }
5459 }
5460 }
5461 }
5462 RooMsgService::instance().setGlobalKillBelow(msglevel);
5463 return std::shared_ptr<TObject>(out_arg, [](TObject *) {});
5464 } else if (auto a2 = dynamic_cast<RooAbsData *>(arg.get()); a2) {
5465 if (_ws->import(*a2, RooFit::Embedded())) {
5466 RooMsgService::instance().setGlobalKillBelow(msglevel);
5467 return nullptr;
5468 }
5469 RooMsgService::instance().setGlobalKillBelow(msglevel);
5470 return std::shared_ptr<TObject>(_ws->embeddedData(arg->GetName()), [](TObject *) {});
5471 } else if (arg->InheritsFrom("TNamed")) { // can add any TNamed to a workspace
5472 TObject *out_arg = nullptr;
5473 if (auto fr = dynamic_cast<RooFitResult *>(&*arg); fr && fr->numStatusHistory() == 0) {
5474 // fit results without a status history are treated as snapshots
5475 out_arg = fr->Clone();
5476 const_cast<RooLinkedList &>(GETWSSNAPSHOTS(_ws)).Add(out_arg);
5477 } else {
5478 // ensure will have a unique name for import if must be new
5479 TNamed *aNamed = dynamic_cast<TNamed *>(arg.get());
5480 TString aName = arg->GetName();
5481 out_arg = _ws->genobj(arg->GetName());
5482 int ii = 1;
5483 while (aNamed && out_arg && mustBeNew) {
5484 aNamed->SetName(TString::Format("%s;%d", aName.Data(), ii++));
5485 out_arg = _ws->genobj(aNamed->GetName());
5486 }
5487 if (!out_arg) {
5488 if (aName != arg->GetName()) {
5489 Warning("acquire", "Renaming to %s", arg->GetName());
5490 }
5491 if (_ws->import(*arg, false /*replace existing*/)) {
5492 RooMsgService::instance().setGlobalKillBelow(msglevel);
5493 return nullptr;
5494 }
5495 out_arg = _ws->genobj(arg->GetName());
5496 }
5497 }
5498 RooMsgService::instance().setGlobalKillBelow(msglevel);
5499
5500 return std::shared_ptr<TObject>(out_arg, [](TObject *) {});
5501 }
5502 RooMsgService::instance().setGlobalKillBelow(msglevel);
5503 // Warning("acquire","Not implemented acquisition of object %s",arg->GetName());
5504 // return nullptr;
5505 }
5506 if (!mustBeNew && fProvider) {
5507 auto out = fProvider->getObject(arg->GetName(), arg->ClassName());
5508 if (out)
5509 return out;
5510 }
5511 auto _owned = find(".memory");
5512 if (!_owned) {
5513 _owned = emplace_back(std::make_shared<xRooNode>(".memory", nullptr, *this));
5514 }
5515 // look for exact name, dont use 'find' because doesnt work if trying to find "1" and it doesn't exist, will get back
5516 // idx 1 instead
5517 if (!mustBeNew) {
5518 for (auto &r : *_owned) {
5519 if (strcmp(r->GetName(), arg->GetName()) == 0 && strcmp(r->get()->ClassName(), arg->ClassName()) == 0) {
5520 return r->fComp;
5521 }
5522 }
5523 }
5524 if (!fProvider)
5525 std::cout << GetName() << " taking over " << arg->ClassName() << "::" << arg->GetName() << std::endl;
5526 /*emplace_back(std::make_shared<Node2>(".memory",nullptr,*this))*/
5527 return _owned->emplace_back(std::make_shared<xRooNode>(arg->GetName(), arg, *this))->fComp;
5528 // return arg;
5529}
5530
5531bool xRooNode::SetXaxis(const char *name, const char *title, int nbins, double low, double high)
5532{
5533 RooUniformBinning b(low, high, nbins, name);
5534 b.SetTitle(title);
5535 return SetXaxis(b);
5536}
5537
5538bool xRooNode::SetXaxis(const char *name, const char *title, int nbins, const double *bins)
5539{
5540 RooBinning b(nbins, bins, name);
5541 b.SetTitle(title);
5542 return SetXaxis(b);
5543}
5544
5546{
5547
5548 auto name = binning.GetName();
5549 double high = binning.highBound();
5550 double low = binning.lowBound();
5551 // int nbins = binning.numBins();
5552 auto title = binning.GetTitle();
5553
5554 // if have any dependents and name isn't one of them then stop
5555 auto _deps = vars();
5556 /*if(!_deps.empty() && !_deps.find(name)) {
5557 throw std::runtime_error(TString::Format("%s Does not depend on %s",GetName(),name));
5558 }*/
5559
5560 // object will need to exist
5561 if (!get()) {
5562 if (fParent && !find(GetName())) {
5563 fComp = fParent->Add(*this, "+").fComp;
5564 }
5565 }
5566
5567 auto a = get<RooAbsArg>();
5568 if (!a)
5569 throw std::runtime_error("Cannot SetXaxis of non-arg");
5570
5571 auto _x = acquire<RooRealVar>(name, title, low, high);
5572 _x->setBinning(binning, a->GetName());
5573 _x->getBinning(a->GetName()).SetTitle(title);
5574 if (_x->getBinningNames().size() == 2) {
5575 // this was the first binning, so copy it over to be the default binning too
5576 _x->setBinning(_x->getBinning(a->GetName()));
5577 } else {
5578 // ensure the default binning is wide enough to cover this range
5579 // the alternative to this would be to ensure setNormRange of all pdfs
5580 // are set to correct range (then default can be narrower than some of the named binnings)
5581 if (_x->getMax() < high)
5582 _x->setMax(high);
5583 if (_x->getMin() > low)
5584 _x->setMin(low);
5585 }
5586
5587 if (!_deps.find(name) && get<RooAbsPdf>()) {
5588 // creating a variable for a pdf we will assume it should be an observable
5589 _x->setAttribute("obs");
5590 }
5591
5592 a->setStringAttribute("xvar", _x->GetName());
5593 a->setStringAttribute("binning", a->GetName());
5594 fXAxis.reset(); // remove any existing xaxis
5595
5596 return true;
5597}
5598
5600{
5601 if (!ax)
5602 return false;
5603 if (ax->IsVariableBinSize()) {
5604 return SetXaxis(ax->GetName(), ax->GetTitle(), ax->GetNbins(), ax->GetXbins()->GetArray());
5605 } else {
5606 return SetXaxis(ax->GetName(), ax->GetTitle(), ax->GetNbins(), ax->GetXmin(), ax->GetXmax());
5607 }
5608}
5609
5610bool xRooNode::contains(const std::string &name) const
5611{
5612 return find(name, false) != nullptr;
5613}
5614
5615std::shared_ptr<xRooNode> xRooNode::find(const std::string &name, bool browseResult) const
5616{
5617 std::string partname = (name.find('/') != std::string::npos) ? name.substr(0, name.find('/')) : name;
5618 auto _s = (!get() && fParent) ? fParent->get<RooSimultaneous>()
5619 : get<RooSimultaneous>(); // makes work if doing simPdf.bins()["blah"]
5620 std::string extra = (_s) ? _s->indexCat().GetName() : "";
5621 for (auto &child : *this) {
5622 if (auto _obj = child->get(); name == child->GetName() || partname == child->GetName() ||
5623 (_obj && name == _obj->GetName()) || (_obj && partname == _obj->GetName()) ||
5624 (!extra.empty() && ((extra + "=" + name) == child->GetName() ||
5625 (extra + "=" + partname) == child->GetName()))) {
5626 if (browseResult)
5627 child->browse(); // needed so can go at()->at()->at()...
5628 if (partname != name && name != child->GetName()) {
5629 return child->at(name.substr(partname.length() + 1));
5630 }
5631 return child;
5632 }
5633 if (partname.find('.') != 0) { // do not allow mainChild browsing if trying to find a "." child ... as is done in
5634 // getObject for ".memory"
5635 if (auto x = mainChild(); x && strcmp(child->GetName(), x.GetName()) == 0) {
5636 // can browse directly into main children as if their children were our children
5637 for (auto &child2 : x.browse()) {
5638 if (auto _obj = child2->get(); name == child2->GetName() || partname == child2->GetName() ||
5639 (_obj && name == _obj->GetName()) ||
5640 (_obj && partname == _obj->GetName())) {
5641 if (browseResult)
5642 child2->browse(); // needed for onward read (or is it? there's a browse above too??)
5643 if (partname != name && name != child2->GetName()) {
5644 return child2->at(name.substr(partname.length() + 1));
5645 }
5646 return child2;
5647 }
5648 }
5649 }
5650 }
5651 }
5652 // before giving up see if partName is numeric and indexes within the range
5653 if (TString s(partname); s.IsDec() && size_t(s.Atoi()) < size()) {
5654 auto child2 = at(s.Atoi());
5655 if (partname != name) {
5656 return child2->at(name.substr(partname.length() + 1));
5657 }
5658 return child2;
5659 }
5660 // allow calling of find on a RooWorkspace to access getObject objects ...
5661 if (get<RooWorkspace>() && name != ".memory") {
5662 if (auto obj = getObject(name)) {
5663 auto out = std::make_shared<xRooNode>(obj, *this);
5664 if (browseResult)
5665 out->browse();
5666 return out;
5667 }
5668 }
5669 return nullptr;
5670}
5671
5672std::shared_ptr<xRooNode> xRooNode::operator[](const std::string &name)
5673{
5674 std::string partname = (name.find('/') != std::string::npos) ? name.substr(0, name.find('/')) : name;
5675 browse();
5676 auto _s = (!get() && fParent) ? fParent->get<RooSimultaneous>()
5677 : get<RooSimultaneous>(); // makes work if doing simPdf.bins()["blah"]
5678 std::string extra = (_s) ? _s->indexCat().GetName() : "";
5679 std::shared_ptr<xRooNode> folderNode;
5680 for (auto &child : *this) {
5681 if (name == child->GetName() || partname == child->GetName() ||
5682 (!extra.empty() &&
5683 ((extra + "=" + name) == child->GetName() || (extra + "=" + partname) == child->GetName()))) {
5684 child->browse(); // needed for onward read (or is it? there's a browse above too??)
5685 if (partname != name && name != child->GetName()) {
5686 return child->operator[](name.substr(partname.length() + 1));
5687 }
5688 return child;
5689 }
5690 if (auto x = mainChild(); strcmp(child->GetName(), x.GetName()) == 0) {
5691 // can browse directly into main children as if their children were our children
5692 for (auto &child2 : x.browse()) {
5693 if (name == child2->GetName() || partname == child2->GetName()) {
5694 child2->browse(); // needed for onward read (or is it? there's a browse above too??)
5695 if (partname != name && name != child2->GetName()) {
5696 return child2->operator[](name.substr(partname.length() + 1));
5697 }
5698 return child2;
5699 }
5700 }
5701 }
5702 if (child->fFolder == (std::string("!") + partname)) {
5703 if (!folderNode)
5704 folderNode = std::make_shared<xRooNode>(child->fFolder.c_str(), nullptr, *this);
5705 folderNode->push_back(child);
5706 }
5707 }
5708 if (folderNode) {
5709 if (partname != name) {
5710 return folderNode->operator[](name.substr(partname.length() + 1));
5711 }
5712 return folderNode;
5713 }
5714 // before giving up see if partName is numeric and indexes within the range
5715 if (TString s(partname); s.IsDec() && size_t(s.Atoi()) < size()) {
5716 auto child2 = at(s.Atoi());
5717 if (partname != name) {
5718 return child2->operator[](name.substr(partname.length() + 1));
5719 }
5720 return child2;
5721 }
5722 auto out = std::make_shared<xRooNode>(partname.c_str(), nullptr, *this); // not adding as child yeeet
5723 // special case, if creating a node in the workspace with a specific name, it's a folder node ...
5724 if (get<RooWorkspace>() && partname == "pdfs") {
5725 out->SetName("!pdfs");
5726 }
5727 if (partname != name) {
5728 return out->operator[](name.substr(partname.length() + 1));
5729 }
5730 return out;
5731}
5732
5734{
5735 if (!b) {
5736 for (auto o : *gROOT->GetListOfBrowsers()) {
5737 b = dynamic_cast<TBrowser *>(o);
5738 if (!b || !b->GetBrowserImp())
5739 continue;
5740 if (auto out = GetTreeItem(b); out)
5741 return out;
5742 }
5743 return nullptr;
5744 }
5745 if (!b->GetBrowserImp())
5746 return nullptr;
5747 if (auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp()))); _b) {
5748 auto _root = GETROOTDIR(_b);
5749 ;
5750 if (!_root)
5751 _root = GETLISTTREE(_b)->GetFirstItem();
5753 return GETLISTTREE(_b)->FindItemByObj(_root, const_cast<xRooNode *>(this));
5754 }
5755 return nullptr;
5756}
5757
5759{
5760 if (!b) {
5761 for (auto o : *gROOT->GetListOfBrowsers()) {
5762 b = dynamic_cast<TBrowser *>(o);
5763 if (!b || !b->GetBrowserImp())
5764 continue;
5765 if (auto out = GetListTree(b); out)
5766 return out;
5767 }
5768 return nullptr;
5769 }
5770 if (b->GetBrowserImp()) {
5771 if (auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp())));
5772 _b) {
5773 auto _root = GETROOTDIR(_b);
5774 if (!_root)
5775 _root = GETLISTTREE(_b)->GetFirstItem();
5776 if (auto item = GETLISTTREE(_b)->FindItemByObj(_root, const_cast<xRooNode *>(this)); item) {
5777 return GETLISTTREE(_b);
5778 }
5779 }
5780 }
5781 return nullptr;
5782}
5783
5784void xRooNode::SetName(const char *name)
5785{
5787 if (auto a = get<RooAbsArg>(); a)
5788 a->setStringAttribute("alias", name);
5789 for (auto o : *gROOT->GetListOfBrowsers()) {
5790 if (auto b = dynamic_cast<TBrowser *>(o); b) {
5791 if (auto item = GetTreeItem(b); item) {
5792 item->SetText(name);
5793 }
5794 }
5795 }
5796}
5797
5798void xRooNode::SetTitle(const char *title)
5799{
5800 if (auto o = (get<TNamed>()); o) {
5801 if (auto c = mainChild(); c.get()) {
5802 c.SetTitle(title);
5803 }
5804 o->SetTitle(title);
5805 }
5806 TNamed::SetTitle(title);
5807}
5808
5810{
5811 if (get<RooArgList>() || (!get() && !(strlen(GetName()) > 0 && (GetName()[0] == '!')) && !fBrowseOperation))
5812 return *this; // nothing to browse - 'collection' nodes should already be populated except for folders
5813 // alternative could have been to mandate that the 'components' of a collection node are the children it has.
5814
5815 auto findByObj = [&](const std::shared_ptr<xRooNode> &n) {
5816 std::vector<std::shared_ptr<xRooNode>> &nn = *this;
5817 for (auto &c : nn) {
5818 if (c->get() == n->get() && strcmp(n->GetName(), c->GetName()) == 0)
5819 return c;
5820 }
5821 return std::shared_ptr<xRooNode>(nullptr);
5822 };
5823
5824 auto appendChildren = [&](const xRooNode &n) {
5825 size_t out = 0;
5826 const std::vector<std::shared_ptr<xRooNode>> &nn(n);
5827 for (auto &c : nn) {
5828 if (auto existing = findByObj(c); existing) {
5829 existing->fTimes++;
5830 existing->fFolder = c->fFolder; // transfer folder assignment
5831 } else {
5832 emplace_back(c);
5833 }
5834 if (!TString(c->GetName()).BeginsWith(".coef"))
5835 out++; // don't count .coef as a child, as technically part of parent
5836 }
5837 return out;
5838 };
5839
5840 const std::vector<std::shared_ptr<xRooNode>> &nn2(*this);
5841 for (auto &c : nn2) {
5842 if (strlen(c->GetName()) > 0 && (c->GetName()[0] == '.')) {
5843 c->fTimes = 1;
5844 continue;
5845 } // never auto-cleanup property children
5846 if (strcmp(c->GetName(), "!.pars") == 0) {
5847 c->fTimes = 1;
5848 continue;
5849 } // special collection, also not cleaned up
5850 if (c->get<RooWorkspace>() || c->get<TFile>()) {
5851 c->fTimes = 1;
5852 continue;
5853 } // workspaces and files not cleaned up: TODO have a nocleanup flag instead
5854 c->fTimes = 0;
5855 }
5856
5857 size_t addedChildren = 0;
5858 if (fBrowseOperation) {
5860 } else {
5861 if (get<RooWorkspace>()) {
5863 }
5864
5865 // if (get<RooAbsPdf>() && ((fParent && fParent->get<RooWorkspace>()) || !fParent)) {
5866 // // top-level pdfs will also list the ".vars" property for -- should make this updateable
5867 // //if (auto x = find("!.vars"); !x) { // this is slower because it triggers a browse of !.vars
5868 // if(!contains("!.vars")) {
5869 // emplace_back(std::make_shared<Node2>("!.vars",nullptr,*this));
5870 // } /*else {
5871 // x->fTimes++;
5872 // }*/
5873 // }
5874
5875 // go through components factors and variations, adding all as children if required
5877 if (!get<RooWorkspace>())
5879 // include coefs if any
5880 auto _coefs = coefs();
5881 if (_coefs.get() && strcmp(_coefs->GetName(), "1") != 0 && strcmp(_coefs->GetName(), "ONE") != 0) {
5882 if (_coefs.size() == 1 && _coefs.get<RooAddition>()) {
5883 if (strcmp(_coefs.at(0)->GetName(), "1") != 0 &&
5884 strcmp(_coefs.at(0)->GetName(), "ONE") != 0) { // don't add the "1"
5885 auto coef = std::make_shared<xRooNode>(".coef", *_coefs.at(0)->get(), *this);
5886 if (auto existing = findByObj(coef); existing) {
5887 existing->fTimes++;
5888 existing->fFolder = _coefs.at(0)->fFolder; // transfer folder assignment
5889 } else {
5890 emplace_back(coef);
5891 }
5892 }
5893 } else {
5894 if (auto existing = find(_coefs.GetName()); existing) {
5895 existing->fTimes++;
5896 existing->fFolder = _coefs.fFolder; // transfer folder assignment
5897 } else {
5898 emplace_back(std::make_shared<xRooNode>(_coefs));
5899 }
5900 }
5901 }
5905 if (get<RooAbsData>())
5907 }
5908 // if has no children and is a RooAbsArg, add all the proxies
5909 if (auto arg = get<RooAbsArg>(); arg && addedChildren == 0) {
5910 for (int i = 0; i < arg->numProxies(); i++) {
5911 auto _proxy = arg->getProxy(i);
5912 if (auto a = dynamic_cast<RooArgProxy *>(_proxy)) {
5913 auto c = std::make_shared<xRooNode>(TString::Format(".%s", _proxy->name()), *(a->absArg()), *this);
5914 if (auto existing = findByObj(c); existing) {
5915 existing->fTimes++;
5916 existing->fFolder = c->fFolder; // transfer folder assignment
5917 } else {
5918 // mark any existing children with the same name for cleanup - this happens e.g. if did a Replace on one
5919 // of these nodes note that the child nodes will still become reordered (the old node will be deleted,
5920 // new node will appear at end)
5921 for (auto &child : *this) {
5922 if (strcmp(child->GetName(), c->GetName()) == 0) {
5923 child->fTimes = 0;
5924 }
5925 }
5926 emplace_back(c);
5927 }
5928 } else if (auto s = dynamic_cast<RooAbsCollection *>(_proxy)) {
5929 for (auto a2 : *s) {
5930 auto c = std::make_shared<xRooNode>(*a2, *this);
5931 if (arg->numProxies() != 1) {
5932 c->fFolder = std::string("!.") +
5933 _proxy->name(); // don't put in a folder if there's just 1 proxy (the collection)
5934 }
5935 if (auto existing = findByObj(c); existing) {
5936 existing->fTimes++;
5937 existing->fFolder = c->fFolder; // transfer folder assignment
5938 } else {
5939 emplace_back(c);
5940 }
5941 }
5942 }
5943 }
5944 /*for(auto& s : arg->servers()) {
5945 auto c = std::make_shared<xRooNode>(*s,*this);
5946 if (auto existing = findByObj(c); existing) {
5947 existing->fTimes++;
5948 existing->fFolder = c->fFolder; // transfer folder assignment
5949 } else {
5950 emplace_back(c);
5951 }
5952 }*/
5953 } else if (auto ir = get<RooStats::HypoTestInverterResult>()) {
5954 // check if we already have a hypoSpace in our memory
5955 bool hasHS = false;
5956 for (auto &c : fBrowsables) {
5957 if (strcmp(c->GetName(), ".memory") == 0 && c->get<xRooHypoSpace>()) {
5958 hasHS = true;
5959 break;
5960 }
5961 }
5962 if (!hasHS) {
5963 // add the HS
5964 auto hs =
5965 fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", std::make_shared<xRooHypoSpace>(ir), *this));
5966 // add the hypoPoints first so they appear first
5967 auto _axes = hs->get<xRooHypoSpace>()->axes();
5968
5969 int i = 0;
5970 for (auto &hp : *hs->get<xRooHypoSpace>()) {
5972 for (auto a : _axes) {
5973 if (a != _axes.first())
5974 coordString += ",";
5975 coordString +=
5976 TString::Format("%s=%g", a->GetName(), hp.coords->getRealValue(a->GetName(), ir->GetXValue(i)));
5977 }
5978 auto hpn = emplace_back(std::make_shared<xRooNode>(coordString, hp.hypoTestResult, hs));
5979 hpn->fTimes++;
5980 hpn->fBrowsables.emplace_back(std::make_shared<xRooNode>(
5981 ".memory", std::shared_ptr<xRooNLLVar::xRooHypoPoint>(&hp, [](xRooNLLVar::xRooHypoPoint *) {}), hpn));
5982 i++;
5983 }
5984 } else {
5985 // ensure all hypoTestResults are flagged as keep-alive
5986 std::vector<std::shared_ptr<xRooNode>> &nn = *this;
5987 for (auto &c : nn) {
5988 if (c->get<RooStats::HypoTestResult>())
5989 c->fTimes++;
5990 }
5991 }
5992 // xRooNode tests;
5993 // for(int i=0;i<ir->ArraySize();i++) {
5994 // tests.push_back(std::make_shared<xRooNode>(TString::Format("%g",ir->GetXValue(i)),*ir->GetResult(i),*this));
5995 // }
5996 // appendChildren(tests);
5997 } else if (get<RooStats::HypoTestResult>()) {
5998
5999 // create the xRooHypoPoint if necessary
6000 xRooNLLVar::xRooHypoPoint *hp = nullptr;
6001 for (auto &c : fBrowsables) {
6002 if (strcmp(c->GetName(), ".memory") == 0 && c->get<xRooNLLVar::xRooHypoPoint>()) {
6003 hp = c->get<xRooNLLVar::xRooHypoPoint>();
6004 c->fTimes++; // keep it alive
6005 break;
6006 }
6007 }
6008 if (!hp) {
6009 auto shp =
6010 std::make_shared<xRooNLLVar::xRooHypoPoint>(std::dynamic_pointer_cast<RooStats::HypoTestResult>(fComp));
6011 fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", shp, *this));
6012 hp = shp.get();
6013 }
6014
6015 xRooNode fits;
6016
6017 if (auto fit = hp->ufit()) {
6018 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("ufit");
6019 }
6020 if (auto fit = hp->cfit_null()) {
6021 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("cfit_null");
6022 }
6023 if (auto fit = hp->cfit_alt()) {
6024 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("cfit_alt");
6025 }
6026 if (auto fit = hp->gfit()) {
6027 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("gfit");
6028 }
6029 if (auto asi = hp->asimov()) {
6030 auto asiP = fits.emplace_back(std::make_shared<xRooNode>(
6031 asi->hypoTestResult ? asi->hypoTestResult : std::make_shared<RooStats::HypoTestResult>(asi->result()),
6032 *this));
6033 asiP->TNamed::SetName("asimov");
6034 asiP->fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", asi, asiP));
6035 }
6037 }
6038
6039 // clear anything that has fTimes = 0 still
6040 auto it = std::vector<std::shared_ptr<xRooNode>>::begin();
6041 while (it != std::vector<std::shared_ptr<xRooNode>>::end()) {
6042 if (it->get()->fTimes == 0) {
6043 for (auto o : *gROOT->GetListOfBrowsers()) {
6044 auto b = dynamic_cast<TBrowser *>(o);
6045 if (b && b->GetBrowserImp()) { // browserImp is null if browser was closed
6046 // std::cout << GetPath() << " Removing " << it->get()->GetPath() << std::endl;
6047
6048 if (auto _b =
6049 dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp())));
6050 _b) {
6051 auto _root = GETROOTDIR(_b);
6052 if (!_root)
6053 _root = GETLISTTREE(_b)->GetFirstItem();
6054 if (auto item = GETLISTTREE(_b)->FindItemByObj(_root, this); item) {
6055 GETLISTTREE(_b)->OpenItem(item);
6056 }
6057 }
6058
6059 b->RecursiveRemove(
6060 it->get()); // problem: if obj is living in a collapsed node it wont actually get deleted
6061 /*auto _b = dynamic_cast<TGFileBrowser*>( dynamic_cast<TRootBrowser*>(b->GetBrowserImp())->fActBrowser );
6062 if (_b) {
6063 std::cout << _b->fRootDir->GetText() << std::endl;
6064 if (auto item = _b->fListTree->FindItemByObj(_b->fRootDir,it->get()); item) {
6065 std::cout << "Found obj: " << item << " " << item->GetText() << std::endl;
6066 _b->fListTree->RecursiveDeleteItem(_b->fRootDir,it->get());
6067 }
6068
6069 //b->RecursiveRemove(it->get());
6070 if (auto item = _b->fListTree->FindItemByObj(_b->fRootDir,it->get()); item) {
6071 std::cout << "Still Found obj: " << item << std::endl;
6072 }
6073 _b->fListTree->ClearViewPort();
6074
6075 }*/
6076 }
6077 }
6078 /*it->get()->ResetBit(TObject::kNotDeleted); ++it;*/ it = erase(it);
6079 } else {
6080 ++it;
6081 }
6082 }
6083
6084 return *this;
6085}
6086
6087////////////////////////////////////////////////////////////////////////////////
6088/// List of observables (global and regular) of this node.
6089
6091{
6092 xRooNode out(".obs", std::make_shared<RooArgList>(), *this);
6093 out.get<RooArgList>()->setName((GetPath() + ".obs").c_str());
6094 for (auto o : vars()) {
6095 if (o->get<RooAbsArg>()->getAttribute("obs")) {
6096 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6097 out.emplace_back(o);
6098 }
6099 }
6100 return out;
6101}
6102
6103////////////////////////////////////////////////////////////////////////////////
6104/// List of global observables of this node.
6105
6107{
6108 xRooNode out(".globs", std::make_shared<RooArgList>(), *this);
6109 out.get<RooArgList>()->setName((GetPath() + ".globs").c_str());
6110 for (auto o : obs()) {
6111 if (o->get<RooAbsArg>()->getAttribute("global")) {
6112 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6113 out.emplace_back(o);
6114 }
6115 }
6116 return out;
6117}
6118
6119////////////////////////////////////////////////////////////////////////////////
6120/// List of regular observables of this node.
6121
6123{
6124 xRooNode out(".robs", std::make_shared<RooArgList>(), *this);
6125 out.get<RooArgList>()->setName((GetPath() + ".robs").c_str());
6126 for (auto o : obs()) {
6127 if (!o->get<RooAbsArg>()->getAttribute("global")) {
6128 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6129 out.emplace_back(o);
6130 }
6131 }
6132 return out;
6133}
6134
6135////////////////////////////////////////////////////////////////////////////////
6136/// List of parameters (non-observables) of this node.
6137
6139{
6140 if (strcmp(GetName(), ".bins") == 0 && fParent) {
6141 // return pars of the parent - this method is used by covariances() if e.g. do node.bins().covariances()
6142 return fParent->pars();
6143 }
6144 xRooNode out(".pars", std::make_shared<RooArgList>(), *this);
6145 out.get<RooArgList>()->setName((GetPath() + ".pars").c_str());
6146 for (auto o : vars()) {
6147 if (!o->get<RooAbsArg>()->getAttribute("obs")) {
6148 out.get<RooArgList>()->add(*(o->get<RooAbsArg>()));
6149 out.emplace_back(o);
6150 }
6151 }
6152 return out;
6153}
6154
6155////////////////////////////////////////////////////////////////////////////////
6156/// List of parameters that are currently constant
6157
6159{
6160 xRooNode out(".consts", std::make_shared<RooArgList>(), *this);
6161 out.get<RooArgList>()->setName((GetPath() + ".consts").c_str());
6162 for (auto o : pars()) {
6163 if (o->get<RooAbsArg>()->getAttribute("Constant") || o->get<RooConstVar>()) {
6164 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6165 out.emplace_back(o);
6166 }
6167 }
6168 return out;
6169}
6170
6171////////////////////////////////////////////////////////////////////////////////
6172/// List of parameters that are currently non-constant
6173/// These parameters do not have the "Constant" attribute
6174
6176{
6177 xRooNode out(".floats", std::make_shared<RooArgList>(), *this);
6178 out.get<RooArgList>()->setName((GetPath() + ".floats").c_str());
6179 for (auto o : pars()) {
6180 if (!o->get<RooAbsArg>()->getAttribute("Constant") && !o->get<RooConstVar>()) {
6181 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6182 out.emplace_back(o);
6183 }
6184 }
6185 return out;
6186}
6187
6188////////////////////////////////////////////////////////////////////////////////
6189/// List of parameters of interest: parameters marked as "of interest"
6190/// These parameters have the "poi" attribute
6191
6193{
6194 xRooNode out(".poi", std::make_shared<RooArgList>(), *this);
6195 out.get<RooArgList>()->setName((GetPath() + ".poi").c_str());
6196 for (auto o : pars()) {
6197 if (o->get<RooAbsArg>()->getAttribute("poi")) {
6198 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6199 out.emplace_back(o);
6200 }
6201 }
6202 return out;
6203}
6204
6205////////////////////////////////////////////////////////////////////////////////
6206/// List of nuisance parameters: non-constant parameters that are not marked of interest,
6207/// as well as any parameters that have been marked by the "np" attribute
6208
6210{
6211 xRooNode out(".np", std::make_shared<RooArgList>(), *this);
6212 out.get<RooArgList>()->setName((GetPath() + ".np").c_str());
6213 for (auto o : pars()) {
6214 if (o->get<RooAbsArg>()->getAttribute("np") ||
6215 (!o->get<RooAbsArg>()->getAttribute("Constant") && !o->get<RooAbsArg>()->getAttribute("poi") &&
6216 !o->get<RooConstVar>())) {
6217 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6218 out.emplace_back(o);
6219 }
6220 }
6221 return out;
6222}
6223
6224////////////////////////////////////////////////////////////////////////////////
6225/// List of prespecified parameters: non-floatable parameters
6226
6228{
6229 xRooNode out(".pp", std::make_shared<RooArgList>(), *this);
6230 out.get<RooArgList>()->setName((GetPath() + ".pp").c_str());
6231 for (auto o : pars()) {
6232 if (!o->get<RooAbsArg>()->getAttribute("np") && !o->get<RooAbsArg>()->getAttribute("poi") &&
6233 (o->get<RooAbsArg>()->getAttribute("Constant") || o->get<RooConstVar>())) {
6234 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6235 out.emplace_back(o);
6236 }
6237 }
6238 return out;
6239}
6240
6241////////////////////////////////////////////////////////////////////////////////
6242/// List of variables (observables and parameters) of this node
6243
6245{
6246 xRooNode out(".vars", std::make_shared<RooArgList>(), *this);
6247 out.get<RooArgList>()->setName((GetPath() + ".vars").c_str());
6248 if (auto coll = get<RooAbsCollection>(); coll) {
6249 for (auto &x : *this) {
6250 for (auto &y : x->vars()) {
6251 out.push_back(y);
6252 }
6253 }
6254 return out;
6255 }
6256 if (auto p = get<RooAbsArg>(); p) {
6257 // also need to get all constPars so use leafNodeServerList .. will include self if is fundamental, which is what
6258 // we want
6259 // ensure all globs appear after robs, as we rely on this ordering for picking "x" var in "reduced" method
6262 p->leafNodeServerList(&allLeaves);
6263 for (auto &c : allLeaves) {
6264 if (c->isFundamental() || (dynamic_cast<RooConstVar *>(c) && !TString(c->GetName()).IsFloat())) {
6265 if (!c->getAttribute("global")) {
6266 out.get<RooArgList>()->add(*c);
6267 out.emplace_back(std::make_shared<xRooNode>(*c, *this));
6268 }
6269 if (c->getAttribute("global")) {
6270 _globs.emplace_back(std::make_shared<xRooNode>(*c, *this));
6271 _globs.back()->fFolder = "!globs";
6272 } else if (c->getAttribute("obs")) {
6273 out.back()->fFolder = "!robs";
6274 } else if (c->getAttribute("poi")) {
6275 out.back()->fFolder = "!poi";
6276 } else if (c->getAttribute("np") ||
6277 (!c->getAttribute("Constant") && !c->getAttribute("poi") && c->IsA() != RooConstVar::Class())) {
6278 out.back()->fFolder = "!np";
6279 } else if (!c->getAttribute("Constant") && c->IsA() != RooConstVar::Class()) {
6280 out.back()->fFolder = "!floats";
6281 } else {
6282 out.back()->fFolder = "!pp";
6283 }
6284 }
6285 }
6286 for (auto g : _globs) {
6287 out.get<RooArgList>()->add(*g->get<RooAbsArg>());
6288 out.emplace_back(g);
6289 }
6290 } else if (auto p2 = get<RooAbsData>(); p2) {
6291 for (auto a : *p2->get()) {
6292 a->setAttribute("obs");
6293 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6294 out.get<RooArgList>()->add(*a);
6295 }
6296 if (auto _dglobs = p2->getGlobalObservables()) {
6297 for (auto &a : *_dglobs) {
6298 a->setAttribute("obs");
6299 a->setAttribute("global");
6300 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6301 out.get<RooArgList>()->add(*a);
6302 }
6303 } else if (auto _globs = find(".globs"); _globs && _globs->get<RooAbsCollection>()) {
6304 for (auto &a : *_globs->get<RooAbsCollection>()) {
6305 a->setAttribute("obs");
6306 a->setAttribute("global");
6307 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6308 out.get<RooArgList>()->add(*a);
6309 }
6310 } else if (auto _ws = ws(); _ws) {
6311 if (auto _globs2 = dynamic_cast<RooArgSet *>(GETWSSNAPSHOTS(_ws).find(p2->GetName())); _globs2) {
6312 for (auto a : *_globs2) {
6313 a->setAttribute("obs");
6314 a->setAttribute("global");
6315 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6316 out.get<RooArgList>()->add(*a);
6317 }
6318 } else if (auto _gl = GETWSSETS(_ws).find("globalObservables"); _gl != GETWSSETS(_ws).end()) {
6319 for (auto &_g : _gl->second) {
6320 auto _clone = std::shared_ptr<RooAbsArg>(dynamic_cast<RooAbsArg *>(_g->Clone(_g->GetName())));
6321 if (auto v = std::dynamic_pointer_cast<RooAbsRealLValue>(_clone); v && _g->getStringAttribute("nominal"))
6322 v->setVal(TString(_g->getStringAttribute("nominal")).Atof());
6323 out.emplace_back(std::make_shared<xRooNode>(_clone, *this));
6324 out.get<RooArgList>()->add(*_clone);
6325 }
6326 } else if (fParent) {
6327 // note: this is slow in large workspaces ... too many obs to look through?
6328 std::unique_ptr<RooAbsCollection> _globs3(fParent->obs().get<RooArgList>()->selectByAttrib("global", true));
6329 // std::unique_ptr<RooAbsCollection> _globs(_ws->allVars().selectByAttrib("global",true)); - tried this to
6330 // be quicker but it wasn't
6331 for (auto &_g : *_globs3) {
6332 auto _clone = std::shared_ptr<RooAbsArg>(dynamic_cast<RooAbsArg *>(_g->Clone(_g->GetName())));
6333 if (auto v = std::dynamic_pointer_cast<RooAbsRealLValue>(_clone); v && _g->getStringAttribute("nominal"))
6334 v->setVal(TString(_g->getStringAttribute("nominal")).Atof());
6335 out.emplace_back(std::make_shared<xRooNode>(_clone, *this));
6336 out.get<RooArgList>()->add(*_clone);
6337 }
6338 }
6339 }
6340 } else if (auto w = get<RooWorkspace>(); w) {
6341 for (auto a : w->components()) {
6342 if (a->InheritsFrom(RooRealVar::Class()) || a->InheritsFrom(RooCategory::Class()) ||
6343 a->InheritsFrom(RooConstVar::Class())) {
6344 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6345 }
6346 }
6347 }
6348 return out;
6349}
6350
6352{
6353 xRooNode out(".components", nullptr, *this);
6354
6355 if (auto p = get<RooAddPdf>(); p) {
6356 // only add each pdf once (the coefs will be accumulated in coefs() method) ...
6357 std::set<RooAbsArg *> donePdfs;
6358 for (auto &o : p->pdfList()) {
6359 if (donePdfs.count(o))
6360 continue;
6361 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6362 donePdfs.insert(o);
6363 }
6364 } else if (auto p2 = get<RooRealSumPdf>(); p2) {
6365 // check for common prefixes and suffixes, will use to define aliases to shorten names
6366 // if have more than 1 function
6367 // TString commonPrefix=""; TString commonSuffix="";
6368 // if (p->funcList().size() > 1) {
6369 // bool checked=false;
6370 // for(auto& o : p->funcList()) {
6371 // if (!checked) {
6372 // commonPrefix = o->GetName(); commonSuffix = o->GetName(); checked=true;
6373 // } else {
6374 //
6375 // }
6376 // }
6377 // }
6378 std::set<RooAbsArg *> doneFuncs;
6379 for (auto &o : p2->funcList()) {
6380 if (doneFuncs.count(o))
6381 continue;
6382 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6383 doneFuncs.insert(o);
6384 }
6385 } else if (auto p3 = get<RooAddition>(); p3) {
6386 for (auto &o : p3->list()) {
6387 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6388 }
6389 } else if (auto p4 = get<RooAbsCollection>(); p4) {
6390 for (auto &a : *p4) {
6391 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6392 }
6393 } else if (auto p5 = get<RooWorkspace>(); p5) {
6394 for (auto &o : p5->components()) {
6395 // only top-level nodes (only clients are integrals or things that aren't part of the workspace)
6396 // if (o->hasClients()) continue;
6397 bool hasClients = false;
6398 for (auto &c : o->clients()) {
6399 if (!c->InheritsFrom("RooRealIntegral") && p5 == GETWS(c)) {
6400 hasClients = true;
6401 break;
6402 }
6403 }
6404 if (hasClients)
6405 continue;
6406 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6407 if (o->InheritsFrom("RooAbsPdf")) {
6408 out.back()->fFolder = "!pdfs";
6409 } else {
6410 out.back()->fFolder = "!scratch";
6411 }
6412 }
6413 for (auto &o : p5->allGenericObjects()) {
6414 if (auto fr = dynamic_cast<RooFitResult *>(o); fr) {
6415 TString s(fr->GetTitle());
6416 if (s.Contains(';'))
6417 s = s(0, s.Index(';'));
6418 if (auto _pdf = out.find(s.Data()); _pdf) {
6419 // std::cout << " type = " << _pdf->get()->ClassName() << std::endl;
6420 out.emplace_back(std::make_shared<xRooNode>(fr->GetName(), *fr, _pdf));
6421 // for a while, this node's parent pointed to something of type Node2!!
6422 // how to fix??? - I fxied it with a new constructor to avoid the shared_ptr<Node2> calling the const
6423 // Node2& constructor via getting wrapped in a Node2(shared_ptr<TObject>) call
6424 // out.back()->fParent = _pdf;
6425 // std::cout << " type2 = " << out.back()->fParent->get()->ClassName() << std::endl;
6426 } else {
6427 out.emplace_back(std::make_shared<xRooNode>(fr->GetName(), *fr, *this));
6428 }
6429 out.back()->fFolder = "!fits";
6430 } else {
6431 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6432 if (strcmp(out.back()->get()->ClassName(), "TStyle") == 0) {
6433 out.back()->fFolder = "!styles";
6434 } else if (strcmp(out.back()->get()->ClassName(), "RooStats::HypoTestInverterResult") == 0) {
6435 out.back()->fFolder = "!scans";
6436 } else if (strcmp(out.back()->get()->ClassName(), "RooStats::ModelConfig") == 0) {
6437 out.back()->fFolder = "!models";
6438 } else {
6439 out.back()->fFolder = "!objects";
6440 }
6441 }
6442 }
6443 for (auto &[k, v] : GETWSSETS(p5)) {
6444 // skip 'CACHE' sets because they are auto-removed when sanitizing workspaces, which will invalidate these
6445 // children
6446 if (k.find("CACHE_") == 0)
6447 continue;
6448 out.emplace_back(std::make_shared<xRooNode>(k.c_str(), v, *this));
6449 out.back()->fFolder = "!sets";
6450 }
6451
6453 std::unique_ptr<TIterator> iter(snaps.MakeIterator());
6454 TObject *snap;
6455 while ((snap = iter->Next())) {
6456 out.emplace_back(std::make_shared<xRooNode>(*snap, *this));
6457 out.back()->fFolder = "!snapshots";
6458 }
6459 } else if (auto mc = get<RooStats::ModelConfig>()) {
6460 // add the pdf as a child, and the external constraints set if its there
6461 if (mc->GetPdf()) {
6462 out.emplace_back(std::make_shared<xRooNode>(".pdf", *mc->GetPdf(), *this));
6463 }
6464 if (mc->GetExternalConstraints()) {
6465 out.emplace_back(std::make_shared<xRooNode>(".extCons", *mc->GetExternalConstraints(), *this));
6466 }
6467 } else if (strlen(GetName()) > 0 && GetName()[0] == '!' && fParent) {
6468 // special case of dynamic property
6469 if (TString(GetName()) == "!.pars") {
6470 for (auto &c : fParent->pars()) {
6471 out.emplace_back(c);
6472 }
6473 } else {
6474 // the components of a folder are the children of the parent (after browsing) that live in this folder
6475 fParent->browse();
6476 for (auto &c : *fParent) {
6477 if (c->fFolder == GetName()) {
6478 out.emplace_back(c);
6479 }
6480 }
6481 }
6482 }
6483
6484 return out;
6485}
6486
6487////////////////////////////////////////////////////////////////////////////////
6488/// bins of a channel or sample, or channels of a multi-channel pdf
6489
6491{
6492 xRooNode out(".bins", nullptr, *this);
6493
6494 if (auto p = get<RooSimultaneous>(); p) {
6495 std::map<int, std::shared_ptr<xRooNode>> cats; // fill into a map to preserve index ordering
6496 for (auto &c : p->indexCat()) { // is alphabetical in labels
6497 auto pp = p->getPdf(c.first.c_str());
6498 if (!pp)
6499 continue;
6500 cats[c.second] =
6501 std::make_shared<xRooNode>(TString::Format("%s=%s", p->indexCat().GetName(), c.first.c_str()), *pp, *this);
6502 }
6503 for (auto &[_, n] : cats)
6504 out.emplace_back(n);
6505 } else if (auto phf = get<ParamHistFunc>(); phf) {
6506 int i = 1;
6507#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
6508 auto &pSet = phf->_paramSet;
6509#else
6510 auto &pSet = phf->paramList();
6511#endif
6512 for (auto par : pSet) {
6513 out.emplace_back(std::make_shared<xRooNode>(*par, *this));
6514 out.back()->fBinNumber = i;
6515 i++;
6516 }
6517 } else if (auto ax = GetXaxis(); ax) {
6518 for (int i = 1; i <= ax->GetNbins(); i++) {
6519 // create a RooProduct of all bin-specific factors of all shapeFactors
6520 std::vector<RooAbsArg *> _factors;
6521 for (auto f : factors()) {
6522 if (f->get<ParamHistFunc>()) {
6523 if (f->bins()[i - 1]->get<RooProduct>()) {
6524 for (auto &ss : f->bins()[i - 1]->factors())
6525 _factors.push_back(ss->get<RooAbsArg>());
6526 } else {
6527 _factors.push_back(f->bins()[i - 1]->get<RooAbsArg>());
6528 }
6529 }
6530 }
6531 out.emplace_back(std::make_shared<xRooNode>(
6532 TString::Format("%g<=%s<%g", ax->GetBinLowEdge(i), ax->GetParent()->GetName(), ax->GetBinLowEdge(i + 1)),
6533 _factors.empty() ? nullptr
6534 : std::make_shared<RooProduct>(TString::Format("%s.binFactors.bin%d", GetName(), i),
6535 "binFactors", RooArgList()),
6536 *this));
6537 for (auto f : _factors) {
6538#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
6539 out.back()->get<RooProduct>()->_compRSet.add(*f);
6540#else
6541 const_cast<RooArgList &>(out.back()->get<RooProduct>()->realComponents()).add(*f);
6542#endif
6543 }
6544 out.back()->fBinNumber = i;
6545 }
6546 }
6547
6548 return out;
6549}
6550
6552{
6554
6555 if (recurse && fParent) {
6556 // get our coefs and multiply it by the parents coefs ...
6557 auto ourCoefs = xRooNode::coefs(false);
6558 auto parentCoefs = fParent->coefs(true);
6559 if (!parentCoefs.get<RooAbsReal>()) {
6560 // no coefs to include, just return our coefs
6561 return ourCoefs;
6562 }
6563 if (!ourCoefs.get<RooAbsReal>()) {
6564 // just return the parent's coefs
6565 return parentCoefs;
6566 }
6567 // if got here, must combine parentCoefs and outCoefs into a RooProduct
6568 xRooNode out(".recursiveCoefs",
6569 std::make_shared<RooProduct>(".recursiveCoefs",
6570 TString::Format("Recursive Coefficients of %s", GetName()),
6571 *ourCoefs.get<RooAbsReal>(), *parentCoefs.get<RooAbsReal>()),
6572 *this);
6573 // keep alive the two coef nodes by adding to out's memory
6574 auto mem = out.emplace_back(std::make_shared<xRooNode>(".memory", nullptr, *this));
6575 mem->emplace_back(std::make_shared<xRooNode>(ourCoefs));
6576 mem->emplace_back(std::make_shared<xRooNode>(parentCoefs));
6577 return out;
6578 }
6579
6580 bool isResidual = false;
6581
6582 // if parent is a sumpdf or addpdf then include the coefs
6583 // if func appears multiple times then coefs must be combined into a RooAddition temporary
6584 if (fParent) {
6585 // handle case where filters are applied .. need to pass through these
6586 // do this by iterating while fComp is null
6587 auto parent = fParent;
6588 if (!parent->fComp) {
6589 while (!parent->fComp && parent->fParent) {
6590 parent = parent->fParent;
6591 }
6592 // parent should now be node above the filters ... need parent of that
6593 parent = parent->fParent;
6594 if (!parent)
6595 parent = fParent; // revert t original parent in case something went wrong
6596 }
6597 if (auto p = parent->get<RooRealSumPdf>(); p) {
6598 std::size_t i = 0;
6599 for (auto &o : p->funcList()) {
6600 if (o == get()) {
6601 if (i >= p->coefList().size()) {
6602 isResidual = true;
6603 coefs.add(p->coefList());
6604 } else {
6605 coefs.add(*p->coefList().at(i));
6606 }
6607 }
6608 i++;
6609 }
6610 } else if (auto p2 = parent->get<RooAddPdf>(); p2) {
6611 std::size_t i = 0;
6612 if (p2->coefList().empty()) {
6613 // this can happen if all pdfs are extended then the coef is effectively the
6614 // expected number of events
6615 // TODO: test behaviour of xRooNode under this scenario (are histograms correct?)
6616 } else {
6617 for (auto &o : p2->pdfList()) {
6618 if (o == get()) {
6619 if (i >= p2->coefList().size()) {
6620 isResidual = true;
6621 coefs.add(p2->coefList());
6622 } else {
6623 coefs.add(*p2->coefList().at(i));
6624 }
6625 }
6626 i++;
6627 }
6628 }
6629 }
6630 }
6631 if (isResidual) {
6632 // return a node representing 1.-sumOfCoefs
6633 // involves creating sumOfCoefs unless there is only 1 coef, then just use that
6634 auto coefSum = coefs.empty()
6635 ? nullptr
6636 : (coefs.size() == 1 ? std::shared_ptr<RooAbsArg>(coefs.at(0), [](RooAbsArg *) {})
6637 : std::make_shared<RooAddition>((isResidual) ? ".sumOfCoefs" : ".coefs",
6638 "Coefficients of", coefs));
6639 xRooNode out(".coef", coefSum ? std::dynamic_pointer_cast<RooAbsArg>(std::make_shared<RooFormulaVar>(
6640 ".coef", "1-sum(otherCoefs)", "1. - @0", *coefSum))
6641 : nullptr /* should we return a "1." instead? */);
6642 if (coefSum && coefs.size() != 1) {
6643 out.emplace_back(std::make_shared<xRooNode>(".memory", nullptr, *this))
6644 ->emplace_back(
6645 std::make_shared<xRooNode>(".sumOfCoefs", coefSum, out)); // added to keep the sum alive! with the node
6646 }
6647 if (!coefs.empty()) {
6648 out.browse();
6649 }
6650 return out;
6651 } else if (coefs.size() == 1) {
6652 xRooNode out(".coef", std::shared_ptr<RooAbsArg>(coefs.at(0), [](RooAbsArg *) {}), *this);
6653 if (!coefs.empty()) {
6654 out.browse();
6655 }
6656 return out;
6657 } else {
6658 auto coefSum =
6659 coefs.empty()
6660 ? nullptr
6661 : std::make_shared<RooAddition>(".coefs", TString::Format("Coefficients of %s", GetName()), coefs);
6662 xRooNode out(".coefs", coefSum, *this);
6663 if (!coefs.empty())
6664 out.browse();
6665
6666 return out;
6667 }
6668}
6669
6671{
6672 xRooNode out(".factors", nullptr, *this);
6673
6674 if (auto p = get<RooProdPdf>(); p) {
6675 auto _main = mainChild();
6676 if (auto a = _main.get<RooRealSumPdf>(); a && !a->getStringAttribute("alias")) {
6677 a->setStringAttribute("alias", "samples");
6678 } else if (auto a2 = _main.get<RooAddPdf>(); a2 && !a2->getStringAttribute("alias")) {
6679 a2->setStringAttribute("alias", "components");
6680 }
6681 int _npdfs = p->pdfList().size();
6682 for (auto &o : p->pdfList()) {
6683 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6684 if (_npdfs > 5 && o != _main.get() && out.back()->robs().size() == 0) // constraints have no robs in them
6685 out.back()->fFolder = "!constraints";
6686 }
6687 } else if (auto p2 = get<RooProduct>(); p2) {
6688 for (auto &o : p2->components()) {
6689 if (o->InheritsFrom("RooProduct")) {
6690 // get factors of this term
6691 auto x = xRooNode("tmp", *o, *this).factors();
6692 for (auto &n : x) {
6693 out.emplace_back(std::make_shared<xRooNode>(n->GetName(), n->fComp, *this));
6694 }
6695 } else {
6696 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6697 }
6698 }
6699 } else if (auto w = get<RooWorkspace>(); w) {
6700 // if workspace, return all functions (not pdfs) that have a RooProduct as one of their clients
6701 // or not clients
6702 // exclude obs and globs
6703 auto oo = obs(); // need to keep alive as may contain owning globs
6704 auto &_obs = *(oo.get<RooArgList>());
6705 for (auto a : w->allFunctions()) {
6706 if (_obs.contains(*a))
6707 continue;
6708 bool show(true);
6709 for (auto c : a->clients()) {
6710 show = false;
6711 if (c->InheritsFrom("RooProduct")) {
6712 show = true;
6713 break;
6714 }
6715 }
6716 if (show)
6717 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6718 }
6719 }
6720
6721 /*
6722 // if parent is a sumpdf or addpdf then include the coefs
6723 // if func appears multiple times then coefs must be combined into a RooAddition temporary
6724 if (fParent) {
6725 RooArgList coefs;
6726 if(auto p = fParent->get<RooRealSumPdf>();p) {
6727 int i=0;
6728 for(auto& o : p->funcList()) {
6729 if (o == get()) {
6730 coefs.add( *p->coefList().at(i) );
6731 }
6732 i++;
6733 }
6734 } else if(auto p = fParent->get<RooAddPdf>(); p) {
6735 int i=0;
6736 for(auto& o : p->pdfList()) {
6737 if (o == get()) {
6738 coefs.add( *p->coefList().at(i) );
6739 }
6740 i++;
6741 }
6742 }
6743 if (!coefs.empty()) {
6744 if (coefs.size() == 1) {
6745 if (strcmp(coefs.at(0)->GetName(),"1")) { // don't add the "1"
6746 out.emplace_back(std::make_shared<Node2>(".coef", *coefs.at(0), *this));
6747 }
6748 } else {
6749 out.emplace_back(std::make_shared<Node2>(".coefs",
6750 std::make_shared<RooAddition>(".coefs", "Coefficients of",
6751 coefs), *this));
6752 }
6753 }
6754 }
6755 */
6756 return out;
6757}
6758
6760{
6761 xRooNode out(".variations", nullptr, *this);
6762
6763 // if (auto p = get<RooSimultaneous>(); p) {
6764 // for (auto &c : p->indexCat()) {
6765 // auto pp = p->getPdf(c.first.c_str());
6766 // if (!pp)
6767 // continue;
6768 // out.emplace_back(
6769 // std::make_shared<xRooNode>(TString::Format("%s=%s", p->indexCat().GetName(), c.first.c_str()), *pp,
6770 // *this));
6771 // }
6772 // } else
6773 if (auto p2 = get<PiecewiseInterpolation>(); p2) {
6774#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
6775 out.emplace_back(std::make_shared<xRooNode>("nominal", p2->_nominal.arg(), *this));
6776#else
6777 out.emplace_back(std::make_shared<xRooNode>("nominal", *(p2->nominalHist()), *this));
6778#endif
6779 for (size_t i = 0; i < p2->paramList().size(); i++) {
6780 // TODO: should we only return one if we find they are symmetrized?
6781 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p2->paramList().at(i)->GetName()),
6782 *p2->highList().at(i), *this));
6783 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p2->paramList().at(i)->GetName()),
6784 *p2->lowList().at(i), *this));
6785 }
6787#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
6788 out.emplace_back(std::make_shared<xRooNode>("nominal", RooFit::RooConst(p3->_nominal), *this));
6789 for (size_t i = 0; i < p3->_paramList.size(); i++) {
6790 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p3->_paramList.at(i)->GetName()),
6791 RooFit::RooConst(p3->_high.at(i)), *this));
6792 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p3->_paramList.at(i)->GetName()),
6793 RooFit::RooConst(p3->_low.at(i)), *this));
6794 }
6795#else
6796 out.emplace_back(std::make_shared<xRooNode>("nominal", RooFit::RooConst(p3->nominal()), *this));
6797 for (size_t i = 0; i < p3->variables().size(); i++) {
6798 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p3->variables().at(i)->GetName()),
6799 RooFit::RooConst(p3->high().at(i)), *this));
6800 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p3->variables().at(i)->GetName()),
6801 RooFit::RooConst(p3->low().at(i)), *this));
6802 }
6803#endif
6804
6805 } else if (auto p4 = get<ParamHistFunc>(); p4) {
6806 // I *think* I put this here so that can browse into a ParamHistFunc
6807 // int i = 0;
6808 // for (auto par : p4->_paramSet) {
6809 // TString _name = par->GetName();
6810 // // if(auto _v = dynamic_cast<RooRealVar*>(p->_dataSet.get(i)->first()); _v) {
6811 // // _name = TString::Format("%s=%g",_v->GetName(),_v->getVal());
6812 // // }
6813 // // out.emplace_back(std::make_shared<xRooNode>(_name,*par,*this)); -- -removed cos now have bin()
6814 // method i++;
6815 // }
6816 }
6817 return out;
6818}
6819
6821{
6822 RooArgList out;
6823 out.setName(GetName());
6824 for (auto &k : *this) {
6825 if (auto o = k->get<RooAbsArg>(); o)
6826 out.add(*o);
6827 }
6828 return out;
6829}
6830
6832{
6833 xRooNode out(".datasets()", nullptr, *this);
6834 // removed the browse operation since no longer showing '.datasets()' in browser
6835 // and otherwise this means dataset reduction operation will be called every time we 'browse()' the datasets node
6836 // out.fBrowseOperation = [](xRooNode *f) { return f->fParent->datasets(); };
6837
6838 if (auto _ws = get<RooWorkspace>(); _ws) {
6839 for (auto &d : _ws->allData()) {
6840 out.emplace_back(std::make_shared<xRooNode>(*d, *this));
6841 out.back()->fFolder = "!datasets";
6842 }
6843 } else if (get<RooAbsPdf>() ||
6844 (!get() && fParent &&
6845 fParent->get<RooAbsPdf>())) { // second condition handles 'bins' nodes of pdf, which have null ptr
6846 // only add datasets that have observables that cover all our observables
6847 auto oo = obs(); // must keep alive in case is owning the globs
6848 RooArgSet _obs(*oo.get<RooArgList>());
6849 //_obs.add(coords(true).argList(), true); // include coord observables too, and current xaxis if there's one -
6850 // added in loop below
6851
6852 TString cut;
6854 for (auto _c : coords()) { // coords() moves vars to their respective coordinates too
6855 if (auto _cat = _c->get<RooAbsCategoryLValue>(); _cat) {
6856 if (cut != "")
6857 cut += " && ";
6858 cut += TString::Format("%s==%d", _cat->GetName(), _cat->getCurrentIndex());
6859 _obs.add(*_cat,
6860 true); // note: if we ever changed coords to return clones, would need to keep coords alive
6861 cutobs.add(*_cat);
6862 } else if (auto _rv = _c->get<RooAbsRealLValue>(); _rv) {
6863 // todo: check coordRange is a single range rather than multirange
6864 if (cut != "")
6865 cut += " && ";
6866 cut += TString::Format("%s>=%f&&%s<%f", _rv->GetName(), _rv->getMin(_rv->getStringAttribute("coordRange")),
6867 _rv->GetName(), _rv->getMax(_rv->getStringAttribute("coordRange")));
6868 _obs.add(*_rv,
6869 true); // note: if we ever changed coords to return clones, would need to keep coords alive
6870 cutobs.add(*_rv);
6871 } else {
6872 throw std::runtime_error("datasets(): Unsupported coordinate type");
6873 }
6874 }
6875 if (auto s = get<RooSimultaneous>()) {
6876 // check if we have a pdf for every category ... if not then add to cut
6877 bool hasMissing = false;
6878 TString extraCut = "";
6879 for (auto cat : s->indexCat()) {
6880 if (!s->getPdf(cat.first.c_str())) {
6881 hasMissing = true;
6882 } else {
6883 if (extraCut != "")
6884 extraCut += " || ";
6885 extraCut += TString::Format("%s==%d", s->indexCat().GetName(), cat.second);
6886 }
6887 }
6888 if (hasMissing) {
6889 if (cut != "")
6890 cut += " && ";
6891 cut += "(" + extraCut + ")";
6892 cutobs.add(s->indexCat());
6893 }
6894 }
6895
6896 if (auto ax = GetXaxis(); ax && dynamic_cast<RooAbsArg *>(ax->GetParent())->getAttribute("obs")) {
6897 auto a = dynamic_cast<RooAbsArg *>(ax->GetParent());
6898 _obs.add(*a, true);
6899 }
6900 xRooNode _datasets; // will be any child datasets, along with datasets of the workspace
6901 for (auto &child : *this) {
6902 if (child->get<RooAbsData>())
6903 _datasets.push_back(child);
6904 }
6905 if (auto __ws = ws(); __ws) {
6906 xRooNode _wsNode(*__ws, *this);
6907 for (auto &d : _wsNode.datasets()) {
6908 _datasets.push_back(d);
6909 }
6910 }
6911
6912 for (auto &d : _datasets) {
6913 if (std::unique_ptr<RooAbsCollection>(d->obs().argList().selectCommon(_obs))->size() == _obs.size()) {
6914 // all obs present .. include
6915
6916 if (cut != "") {
6917 RooFormulaVar cutFormula("cut1", cut, cutobs); // doing this to avoid complaints about unused vars
6918 // TODO: Could consider using a 'filter' node (see filter() method) applied to the dataset instead
6919 // of creating and using a reduced dataset here
6920 out.emplace_back(std::make_shared<xRooNode>(
6921 std::shared_ptr<RooAbsData>(d->get<RooAbsData>()->reduce(
6922 *std::unique_ptr<RooAbsCollection>(d->robs().get<RooArgList>()->selectCommon(_obs)), cutFormula)),
6923 *this));
6924 // put a subset of the globs in the returned dataset too
6925 out.back()->get<RooAbsData>()->setGlobalObservables(*std::unique_ptr<RooAbsCollection>(
6926 d->globs().get<RooArgList>()->selectCommon(*globs().get<RooArgList>())));
6927 if (d->get()->TestBit(1 << 20))
6928 out.back()->get()->SetBit(1 << 20);
6929 // need to attach the original dataset so that things like SetBinContent can interact with it
6930 out.back()->fBrowsables.emplace_back(std::make_shared<xRooNode>(".sourceds", d->fComp, *this));
6931 } else {
6932 out.emplace_back(std::make_shared<xRooNode>(d->fComp, *this));
6933 }
6934 }
6935 }
6936 /*else if(auto p = get<RooFitResult>(); p) {
6937 // look for datasets in workspace that match the fit result name after hashing
6938 for(auto& _d : xRooNode(*_ws,*this).datasets()) {
6939 auto _hash = RooAbsTree::nameToHash(_d->get()->GetName());
6940 if (TString::Format("%d;%d",_hash.first,_hash.second) == p->GetTitle()) {
6941 out.emplace_back(std::make_shared<xRooNode>(_d->fComp, *this));
6942 }
6943 }
6944 }*/
6945 } else if (auto mc = get<RooStats::ModelConfig>()) {
6946 return xRooNode(*mc->GetPdf(), fParent).datasets();
6947 }
6948
6949 return out;
6950}
6951
6953{
6954 xRooNode out(".parents", nullptr, *this);
6955 if (auto a = get<RooAbsArg>()) {
6956 for (auto c : a->clients()) {
6957 out.push_back(std::make_shared<xRooNode>(*c, *this));
6958 }
6959 }
6960 return out;
6961}
6962
6964{
6965 if (auto w = get<RooWorkspace>()) {
6966 xRooNode out(".args", w->components(), *this);
6967 out.browse(); // populate
6968 return out;
6969 } else if (auto a = get<RooAbsArg>()) {
6970 xRooNode out(".args", std::make_shared<RooArgList>(), *this);
6971 out.get<RooArgList>()->setName((GetPath() + ".args").c_str());
6972 a->treeNodeServerList(out.get<RooArgList>());
6973 out.browse(); // populate
6974 return out;
6975 }
6976 return nullptr;
6977}
6978
6979std::shared_ptr<xRooNode> xRooNode::getBrowsable(const char *name) const
6980{
6981 for (auto b : fBrowsables) {
6982 if (b && strcmp(b->GetName(), name) == 0)
6983 return b;
6984 }
6985 return nullptr;
6986}
6987
6989{
6990
6991 if (auto fr = get<RooFitResult>(); fr) {
6992 return nullptr;
6993 }
6994
6995 if (auto theData = get<RooDataSet>(); theData) {
6996
6997 TH1 *theHist = nullptr;
6998
6999 if (fromPad) {
7000 // find first histogram in pad
7001 for (auto o : *fromPad->GetListOfPrimitives()) {
7002 theHist = dynamic_cast<TH1 *>(o);
7003 if (theHist) {
7004 theHist = static_cast<TH1 *>(theHist->Clone());
7005 theHist->Reset();
7006 break;
7007 } // clone because theHist gets deleted below
7008 }
7009 }
7010
7011 if (!theHist) {
7012 auto _parentPdf = parentPdf();
7013 if (!_parentPdf) {
7014 // can still build graph if v is an obs ... will use v binning
7015 auto vo = dynamic_cast<TObject *>(v);
7016 if (v && obs().find(vo->GetName())) {
7017 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(v)) {
7018 theHist = new TH1D(
7019 TString::Format("%s_%s", GetName(), vo->GetName()),
7020 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
7021 cat->numTypes(), 0, cat->numTypes());
7022 int i = 1;
7023 std::map<int, std::string> cats; // fill into a map to preserve index ordering
7024 for (auto &c : *cat) {
7025 cats[c.second] = c.first;
7026 }
7027 for (auto &[_, label] : cats) {
7028 theHist->GetXaxis()->SetBinLabel(i++, label.c_str());
7029 }
7030 } else {
7031 auto _binning = v->getBinningPtr(nullptr);
7032 if (_binning->isUniform()) {
7033 theHist = new TH1D(
7034 TString::Format("%s_%s", GetName(), vo->GetName()),
7035 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
7036 v->numBins(), _binning->lowBound(), _binning->highBound());
7037 } else {
7038 theHist = new TH1D(
7039 TString::Format("%s_%s", GetName(), vo->GetName()),
7040 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
7041 v->numBins(), _binning->array());
7042 }
7043 }
7044 } else {
7045 throw std::runtime_error("Cannot draw dataset without parent PDF");
7046 }
7047 } else {
7048 theHist = _parentPdf->BuildHistogram(v, true);
7049 }
7050 }
7051 if (!theHist)
7052 return nullptr;
7053 // this hist will get filled with w*x to track weighted x position per bin
7054 TH1 *xPos = static_cast<TH1 *>(theHist->Clone("xPos"));
7055 xPos->Reset();
7056 TH1 *xPos2 = static_cast<TH1 *>(theHist->Clone("xPos2"));
7057 xPos2->Reset();
7058 auto nHist = std::unique_ptr<TH1>(static_cast<TH1 *>(theHist->Clone("nEntries")));
7059 nHist->Reset();
7060
7061 auto dataGraph = new TGraphAsymmErrors;
7062 dataGraph->SetEditable(false);
7063 dataGraph->SetName(GetName());
7064 dataGraph->SetTitle(strlen(theData->GetTitle()) ? theData->GetTitle() : theData->GetName());
7065 // next line triggers creation of the histogram inside the graph, in root 6.22 that isn't protected from being
7066 // added to gDirectory
7067 dataGraph->SetTitle(TString::Format("%s;%s;Events", dataGraph->GetTitle(), theHist->GetXaxis()->GetTitle()));
7068 *static_cast<TAttMarker *>(dataGraph) = *static_cast<TAttMarker *>(theHist);
7069 *static_cast<TAttLine *>(dataGraph) = *static_cast<TAttLine *>(theHist);
7070
7071 // default style based on if generated or not
7072
7073 if (auto w = theData->weightVar(); w && w->getStringAttribute("fitResult")) {
7074 // is generated
7075 dataGraph->SetLineColor(kGreen + 2);
7076 if (w->getAttribute("expected")) {
7077 // is asimov
7078 dataGraph->SetLineColor(kBlue);
7079 }
7080 } else {
7081 dataGraph->SetLineColor(kBlack);
7082 }
7083 dataGraph->SetMarkerStyle(20);
7084 dataGraph->SetMarkerColor(dataGraph->GetLineColor());
7085 dataGraph->SetMarkerSize(gStyle->GetMarkerSize());
7086
7087 auto _obs = obs();
7088
7089 // auto x = theData->get()->find((v) ? dynamic_cast<TObject*>(v)->GetName() : theHist->GetXaxis()->GetName());
7090 // const RooAbsReal* xvar = (x) ? dynamic_cast<RooAbsReal*>(x) : nullptr;
7091 // const RooAbsCategory* xcat = (x && !xvar) ? dynamic_cast<RooAbsCategory*>(x) : nullptr;
7092 auto x = _obs.find((v) ? dynamic_cast<TObject *>(v)->GetName()
7093 : (theHist->GetXaxis()->IsAlphanumeric() ? theHist->GetXaxis()->GetTimeFormatOnly()
7094 : theHist->GetXaxis()->GetName()));
7095 if (x && x->get<RooAbsArg>()->getAttribute("global")) {
7096 // is global observable ...
7097 dataGraph->SetPoint(0, x->get<RooAbsReal>()->getVal(), 1e-15);
7098 dataGraph->SetTitle(TString::Format("%s = %f", dataGraph->GetTitle(), dataGraph->GetPointX(0)));
7099 delete xPos;
7100 delete xPos2;
7101 delete theHist;
7102 return dataGraph;
7103 }
7104
7105 const RooAbsReal *xvar = (x) ? x->get<RooAbsReal>() : nullptr;
7106 const RooAbsCategory *xcat = (x && !xvar) ? x->get<RooAbsCategory>() : nullptr;
7107
7108 auto _coords = coords();
7109
7110 TString pName((fromPad) ? fromPad->GetName() : "");
7111 auto _pos = pName.Index('=');
7112
7113 int nevent = theData->numEntries();
7114 for (int i = 0; i < nevent; i++) {
7115 theData->get(i);
7116 bool _skip = false;
7117 for (auto _c : _coords) {
7118 if (auto cat = _c->get<RooAbsCategoryLValue>(); cat) {
7119 if (cat->getIndex() != theData->get()->getCatIndex(cat->GetName())) {
7120 _skip = true;
7121 break;
7122 }
7123 } else if (auto rv = _c->get<RooAbsRealLValue>(); rv) {
7124 // must be in range
7125 if (!rv->inRange(theData->get()->getRealValue(rv->GetName()), rv->getStringAttribute("coordRange"))) {
7126 _skip = true;
7127 break;
7128 }
7129 }
7130 }
7131 if (_pos != -1) {
7132 if (auto cat = dynamic_cast<RooAbsCategory *>(theData->get()->find(TString(pName(0, _pos))));
7133 cat && cat->getLabel() != pName(_pos + 1, pName.Length())) {
7134 _skip = true;
7135 }
7136 }
7137 if (_skip)
7138 continue;
7139
7140 if (xvar) {
7141 xPos->Fill(xvar->getVal(), xvar->getVal() * theData->weight());
7142 xPos2->Fill(xvar->getVal(), pow(xvar->getVal(), 2) * theData->weight());
7143 }
7144
7145 if (xcat) {
7146 theHist->Fill(xcat->getLabel(), theData->weight());
7147 nHist->Fill(xcat->getLabel(), 1);
7148 } else {
7149 theHist->Fill((x) ? xvar->getVal() : 0.5, theData->weight());
7150 nHist->Fill((x) ? xvar->getVal() : 0.5, 1);
7151 }
7152 }
7153
7154 xPos->Divide(theHist);
7155 xPos2->Divide(theHist);
7156
7157 // update the x positions to the means for each bin and use poisson asymmetric errors for data ..
7158 for (int i = 0; i < theHist->GetNbinsX(); i++) {
7159 if (includeZeros || nHist->GetBinContent(i + 1)) {
7160 double val = theHist->GetBinContent(i + 1);
7161
7162 dataGraph->SetPoint(dataGraph->GetN(),
7163 (xvar && val) ? xPos->GetBinContent(i + 1) : theHist->GetBinCenter(i + 1), val);
7164
7165 // x-error will be the (weighted) standard deviation of the x values ...
7166 double xErr = xPos2->GetBinContent(i + 1) - pow(xPos->GetBinContent(i + 1), 2);
7167 xErr = (xErr <= 0) ? 0. : sqrt(xErr); // protects against floating point rounding effects
7168
7169 if (xErr || val) {
7170 dataGraph->SetPointError(dataGraph->GetN() - 1, xErr, xErr,
7171 val - 0.5 * TMath::ChisquareQuantile(TMath::Prob(1, 1) / 2., 2. * (val)),
7172 0.5 * TMath::ChisquareQuantile(1. - TMath::Prob(1, 1) / 2., 2. * (val + 1)) -
7173 val);
7174 }
7175 }
7176 }
7177
7178 // transfer limits from theHist to dataGraph hist
7179 dataGraph->GetHistogram()->GetXaxis()->SetLimits(theHist->GetXaxis()->GetXmin(), theHist->GetXaxis()->GetXmax());
7180 // and bin labels, if any
7181 if (xcat) {
7182 dataGraph->GetHistogram()->GetXaxis()->Set(theHist->GetNbinsX(), 0, theHist->GetNbinsX());
7183 for (int i = 1; i <= theHist->GetNbinsX(); i++)
7184 dataGraph->GetHistogram()->GetXaxis()->SetBinLabel(i, theHist->GetXaxis()->GetBinLabel(i));
7185 }
7186
7187 delete xPos;
7188 delete xPos2;
7189 delete theHist;
7190
7191 // std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case getObject
7192 // has decided to return the owning ptr (for some reason) std::string _title =
7193 // strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(); if (!gROOT->GetStyle(_title.c_str()))
7194 // {
7195 // if ( (style = getObject<TStyle>(_title)) ) {
7196 // // loaded style (from workspace?) so put in list and use that
7197 // gROOT->GetListOfStyles()->Add(style.get());
7198 // } else {
7199 // // create new style - gets put in style list automatically so don't have to delete
7200 // // acquire them so saved to workspaces for auto reload ...
7201 // style = const_cast<xRooNode&>(*this).acquireNew<TStyle>(_title.c_str(),
7202 // TString::Format("Style for %s component", _title.c_str()));
7203 // (TAttLine &) (*style) = *dynamic_cast<TAttLine *>(dataGraph);
7204 // (TAttFill &) (*style) = *dynamic_cast<TAttFill *>(dataGraph);
7205 // (TAttMarker &) (*style) = *dynamic_cast<TAttMarker *>(dataGraph);
7206 // gROOT->GetListOfStyles()->Add(style.get());
7207 // }
7208 // }
7209 auto _styleNode = styles(dataGraph);
7210 if (auto _style = _styleNode.get<TStyle>()) {
7211 *dynamic_cast<TAttLine *>(dataGraph) = *_style;
7212 *dynamic_cast<TAttFill *>(dataGraph) = *_style;
7213 *dynamic_cast<TAttMarker *>(dataGraph) = *_style;
7214 }
7215 return dataGraph;
7216 }
7217
7218 throw std::runtime_error("Cannot build graph");
7219}
7220
7222{
7223 if (fr) {
7224 if (auto _w = ws(); _w) {
7225 auto res = acquire(std::shared_ptr<RooFitResult>(const_cast<RooFitResult *>(fr), [](RooFitResult *) {}));
7226 for (auto o : _w->allGenericObjects()) {
7227 if (auto _fr = dynamic_cast<RooFitResult *>(o); _fr) {
7228 _fr->ResetBit(1 << 20);
7229 }
7230 }
7231 res->SetBit(1 << 20);
7232 // assign values
7233 auto allVars = _w->allVars();
7234 allVars = fr->floatParsFinal();
7235 allVars = fr->constPars();
7236 } else {
7237 // need to add to memory as a specific name
7238 throw std::runtime_error("Not supported yet"); // complication is how to replace an existing fitResult in
7239 // .memory auto _clone = std::make_shared<RooFitResult>(*fr);
7240 //_clone->SetName("fitResult");
7241 }
7242 } else {
7244 }
7245}
7246
7248{
7249 if (auto _fr = fr.get<const RooFitResult>()) {
7251 } else
7252 throw std::runtime_error("Not a RooFitResult");
7253}
7254
7255xRooNode xRooNode::fitResult(const char *opt) const
7256{
7257
7258 if (get<RooFitResult>())
7259 return *this;
7260 if (get<RooAbsData>()) {
7261 if (auto _fr = find(".fitResult"); _fr)
7262 return _fr;
7263#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
7264 // check if weightVar of RooAbsData has fitResult attribute on it, will be the generation fit result
7265 if (get<RooDataSet>() && get<RooDataSet>()->weightVar() &&
7266 get<RooDataSet>()->weightVar()->getStringAttribute("fitResult")) {
7267 return xRooNode(getObject<const RooFitResult>(get<RooDataSet>()->weightVar()->getStringAttribute("fitResult")),
7268 *this);
7269 }
7270#endif
7271 return xRooNode();
7272 }
7273
7274 TString sOpt(opt);
7275 if (sOpt == "prefit") {
7276 // build a fitResult using nominal values and infer errors from constraints
7277 // that aren't the 'main' constraints
7278 // Warning("fitResult","Building prefitResult by examining pdf. Consider setting an explicit prefitResult
7279 // (SetFitResult(fr)) where fr name is prefitResult");
7280
7281 // ensure coefs are included if there are any
7282 auto _coefs = coefs();
7283 if (_coefs.get()) {
7284 return xRooNode(RooProduct("tmp", "tmp", RooArgList(*get<RooAbsArg>(), *_coefs.get<RooAbsReal>())))
7285 .fitResult(opt);
7286 }
7287
7288 std::unique_ptr<RooArgList> _pars(dynamic_cast<RooArgList *>(pars().argList().selectByAttrib("Constant", false)));
7289 auto fr = std::make_shared<RooFitResult>("prefitResult", "Prefit");
7290 fr->setFinalParList(*_pars);
7291 for (auto &p : fr->floatParsFinal()) {
7292 auto _v = dynamic_cast<RooRealVar *>(p);
7293 if (!_v)
7294 continue;
7295 if (auto s = _v->getStringAttribute("nominal"); s)
7296 _v->setVal(TString(s).Atof());
7297 auto _constr = xRooNode(fParent->getObject<RooRealVar>(p->GetName()), *this).constraints();
7298 std::shared_ptr<xRooNode> pConstr;
7299 for (auto &c : _constr) {
7300 if (c->get<RooPoisson>() || c->get<RooGaussian>()) {
7301 // require parameter to be a direct server of the constraint pdf to count
7302 bool isServer = true;
7303 if (c->get<RooGaussian>()) {
7304 isServer = false;
7305 for (auto s : c->get<RooAbsArg>()->servers()) {
7306 if (strcmp(s->GetName(), p->GetName()) == 0) {
7307 isServer = true;
7308 break;
7309 }
7310 }
7311 }
7312 if (isServer) {
7313 pConstr = c;
7314 break;
7315 }
7316 }
7317 }
7318 if (pConstr) {
7319 // there will be 3 deps, one will be this par, the other two are the mean and error (or error^2 in case of
7320 // poisson use the one that's a ConstVar as the error to break a tie ...
7321 double prefitVal = 0;
7322 double prefitError = 0;
7323 for (auto &_d : pConstr->vars()) {
7324 if (strcmp(p->GetName(), _d->get()->GetName()) == 0)
7325 continue;
7326 if (auto _c = _d->get<RooConstVar>(); _c && _c->getVal() != 0) {
7327 if (prefitError)
7328 prefitVal = prefitError; // loading val into error already, so move it over
7329 prefitError = _c->getVal();
7330 } else if (prefitError == 0) {
7331 prefitError = _d->get<RooAbsReal>()->getVal();
7332 } else {
7333 prefitVal = _d->get<RooAbsReal>()->getVal();
7334 }
7335 }
7336
7337 if (pConstr->get<RooGaussian>() && pConstr->browse().find(".sigma")) {
7338 prefitError = pConstr->find(".sigma")->get<RooAbsReal>()->getVal();
7339 }
7340 // std::cout << p->GetName() << " extracted " << prefitVal << " " << prefitError << " from ";
7341 // pConstr->deps().Print();
7342 if (pConstr->get<RooPoisson>()) {
7343 // prefitVal will be the global observable value, need to divide that by tau
7345 // prefiterror will be tau ... need 1/sqrt(tau) for error
7346 prefitError = 1. / sqrt(prefitError);
7347 }
7348 if (!_v->getStringAttribute("nominal"))
7349 _v->setVal(prefitVal);
7350 _v->setError(prefitError);
7351 } else {
7352 // unconstrained, remove error
7353 _v->removeError();
7354 }
7355 }
7356 auto _args = consts().argList();
7357 _args.add(pp().argList());
7358 // global obs are added to constPars list too
7359 auto _globs = globs(); // keep alive as may own glob
7360 _args.add(_globs.argList());
7361 fr->setConstParList(_args);
7362 std::unique_ptr<RooArgList> _snap(dynamic_cast<RooArgList *>(_pars->snapshot()));
7363 for (auto &p : *_snap) {
7364 if (auto atr = p->getStringAttribute("initVal"); atr && dynamic_cast<RooRealVar *>(p))
7365 dynamic_cast<RooRealVar *>(p)->setVal(TString(atr).Atof());
7366 }
7367 fr->setInitParList(*_snap);
7368 return xRooNode(fr, *this);
7369 }
7370
7371 // return first checked fit result present in the workspace
7372 if (auto _w = ws(); _w) {
7373 auto checkFr = [&](TObject *o) {
7374 if (auto _fr = dynamic_cast<RooFitResult *>(o); _fr && _fr->TestBit(1 << 20)) {
7375 // check all pars match final/const values ... if mismatch need to create a new RooFitResult
7379 for (auto p : pars()) {
7380 if (p->get<RooAbsArg>()->getAttribute("Constant") || p->get<RooConstVar>()) {
7381 // par must not be in the float list or have different value to what is in constPars (if it is there)
7382 if (_fr->floatParsFinal().find(p->GetName()) ||
7383 (p->get<RooAbsReal>() &&
7384 std::abs(_fr->constPars().getRealValue(p->GetName(), std::numeric_limits<double>::quiet_NaN()) -
7385 p->get<RooAbsReal>()->getVal()) > 1e-15) ||
7386 (p->get<RooAbsCategory>() &&
7387 p->get<RooAbsCategory>()->getCurrentIndex() !=
7388 _fr->constPars().getCatIndex(p->GetName(), std::numeric_limits<int>().max()))) {
7389 newConsts.add(*p->get<RooAbsArg>());
7390 }
7391 } else {
7392 // floating par must be present in the floatPars list with the same value
7393 if (!_fr->floatParsFinal().find(p->GetName())) {
7394 newFloats.add(*p->get<RooAbsArg>());
7395 } else if ((p->get<RooAbsReal>() &&
7396 std::abs(_fr->floatParsFinal().getRealValue(p->GetName(),
7397 std::numeric_limits<double>::quiet_NaN()) -
7398 p->get<RooAbsReal>()->getVal()) > 1e-15) ||
7399 (p->get<RooAbsCategory>() &&
7400 p->get<RooAbsCategory>()->getCurrentIndex() !=
7401 _fr->floatParsFinal().getCatIndex(p->GetName(), std::numeric_limits<int>().max()))) {
7402 // value of existing float changed
7403 oldFloats.add(*p->get<RooAbsArg>());
7404 }
7405 }
7406 }
7407 if (!oldFloats.empty() || !newFloats.empty() || !newConsts.empty()) {
7408 // create new fit result using covariance from the fit result
7409 // remove any new consts from the list before extracting covariance matrix
7410 RooArgList existingFloats(_fr->floatParsFinal());
7411 existingFloats.remove(newConsts, true, true /* match name*/);
7412 auto cov = _fr->reducedCovarianceMatrix(existingFloats);
7413 if (!newFloats.empty()) {
7414 // extend the covariance matrix and add variances using current parameter errors
7415 size_t oldSize = existingFloats.size();
7416 cov.ResizeTo(oldSize + newFloats.size(), oldSize + newFloats.size());
7417 for (size_t i = 0; i < newFloats.size(); i++) {
7418 existingFloats.add(*newFloats.at(i));
7419 auto v = dynamic_cast<RooRealVar *>(newFloats.at(i));
7420 if (v)
7421 cov(oldSize + i, oldSize + i) = std::pow(v->getError(), 2);
7422 }
7423 }
7424 RooArgList existingConsts(_fr->constPars());
7425 existingConsts.remove(newFloats, true, true);
7427
7428 // do we need to add our remaining const pars to the const par list? or the globs?
7429 // for speed we wont bother
7430 // note that generating datasets needs the globs in the const pars list so the robs can be determined
7431 // at the moment this check is done in the generate() method (along with check for missing pars)
7432
7433 auto fr = std::make_shared<RooFitResult>(TString::Format("%s-dirty", _fr->GetName()));
7434 fr->setFinalParList(existingFloats);
7435 fr->setConstParList(existingConsts);
7436 fr->setCovarianceMatrix(cov);
7437 fr->setInitParList(_fr->floatParsInit()); // will only be the pars that were actually float for the fit
7438
7439 return xRooNode(fr, *this);
7440 } else {
7441 // all matching, can return the fit result as-is
7442 return xRooNode(*_fr, std::make_shared<xRooNode>(*_w, std::make_shared<xRooNode>()));
7443 }
7444 }
7445 return xRooNode();
7446 };
7447 for (auto o : _w->allGenericObjects()) {
7448 auto out = checkFr(o);
7449 if (out)
7450 return out;
7451 }
7452 for (auto o : GETWSSNAPSHOTS(_w)) {
7453 auto out = checkFr(o);
7454 if (out)
7455 return out;
7456 }
7457 } else {
7458 // objects not in workspaces are allowed to have a fitResult set in their memory
7459 // use getObject to get it
7460 if (auto fr = getObject<RooFitResult>(".fitResult"); fr) {
7461 return xRooNode(fr, *this);
7462 }
7463 }
7464
7465 // ensure coefs are included if there are any
7466 auto _coefs = coefs();
7467 if (_coefs.get()) {
7468 return xRooNode(RooProduct("tmp", "tmp", RooArgList(*get<RooAbsArg>(), *_coefs.get<RooAbsReal>())))
7469 .fitResult(opt);
7470 }
7471
7472 std::unique_ptr<RooArgList> _pars(dynamic_cast<RooArgList *>(pars().argList().selectByAttrib("Constant", false)));
7473 auto fr = std::make_shared<RooFitResult>(TUUID().AsString());
7474 fr->SetTitle(TString::Format("%s uncorrelated parameter snapshot", GetName()));
7475 fr->setFinalParList(*_pars);
7476 fr->setStatus(-1);
7477
7478 TMatrixDSym cov(fr->floatParsFinal().size());
7479 TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr.get(), _VM));
7480 if (prevCov) {
7481 for (int i = 0; i < prevCov->GetNcols(); i++) {
7482 for (int j = 0; j < prevCov->GetNrows(); j++) {
7483 cov(i, j) = (*prevCov)(i, j);
7484 }
7485 }
7486 }
7487 int i = 0;
7488 for (auto &p : fr->floatParsFinal()) {
7489 if (!prevCov || i >= prevCov->GetNcols()) {
7490 if (auto v = dynamic_cast<RooRealVar *>(p)) {
7491 cov(i, i) = pow(v->getError(), 2);
7492 } else {
7493 cov(i, i) = 0;
7494 }
7495 }
7496 i++;
7497 }
7498 int covQualBackup = fr->covQual();
7499 fr->setCovarianceMatrix(cov);
7500 fr->setCovQual(covQualBackup);
7501
7502 auto _args = consts().argList();
7503 _args.add(pp().argList());
7504 // global obs are added to constPars list too
7505 auto _globs = globs(); // keep alive as may own glob
7506 _args.add(_globs.argList());
7507 fr->setConstParList(_args);
7508 std::unique_ptr<RooArgList> _snap(dynamic_cast<RooArgList *>(_pars->snapshot()));
7509 for (auto &p : *_snap) {
7510 if (auto atr = p->getStringAttribute("initVal"); atr && dynamic_cast<RooRealVar *>(p))
7511 dynamic_cast<RooRealVar *>(p)->setVal(TString(atr).Atof());
7512 }
7513 fr->setInitParList(*_snap);
7514
7515 // return *const_cast<Node2*>(this)->emplace_back(std::make_shared<Node2>(".fitResult",fr,*this));
7516 return xRooNode(fr, *this);
7517}
7518
7519// xRooNode xRooNode::fitTo_(const char* datasetName) const {
7520// try {
7521// return fitTo(datasetName);
7522// } catch(const std::exception& e) {
7523// new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),kMBIconExclamation); // deletes
7524// self on dismiss? return xRooNode();
7525// }
7526// }
7527//
7528// xRooNode xRooNode::fitTo(const char* datasetName) const {
7529// return fitTo(*datasets().at(datasetName));
7530// }
7531
7532void xRooNode::SetRange(const char *range, double low, double high)
7533{
7534 if (!std::isnan(low) && !std::isnan(high) && get<RooRealVar>()) {
7535 if (range && strlen(range)) {
7536 get<RooRealVar>()->setRange(range, low, high);
7537 } else {
7538 get<RooRealVar>()->setRange(low, high);
7539 }
7540 return;
7541 }
7542 if (auto o = get<RooAbsArg>(); o)
7543 o->setStringAttribute("range", range);
7544 // todo: clear the range attribute on all servers
7545 // could make this controlled by a flag but probably easiest to enforce so you must set range
7546 // in children after if you wanted to override
7547}
7548const char *xRooNode::GetRange() const
7549{
7550 std::string &out = fRange;
7551 if (auto o = get<RooAbsArg>(); o && o->getStringAttribute("range"))
7552 out = o->getStringAttribute("range");
7553 auto _parent = fParent;
7554 while (out.empty() && _parent) {
7555 if (auto o = _parent->get<RooAbsArg>(); o && o->getStringAttribute("range"))
7556 out = o->getStringAttribute("range");
7557 _parent = _parent->fParent;
7558 }
7559 return out.c_str();
7560}
7561
7562xRooNLLVar xRooNode::nll(const char *_data, std::initializer_list<RooCmdArg> nllOpts) const
7563{
7564 return nll(xRooNode(_data), nllOpts);
7565}
7566
7568{
7569 return nll(_data, *xRooFit::createNLLOptions());
7570}
7571
7572xRooNLLVar xRooNode::nll(const xRooNode &_data, std::initializer_list<RooCmdArg> nllOpts) const
7573{
7574 auto defaultOpts = xRooFit::createNLLOptions(); // smart pointer will cleanup the list
7575 // add user-specified options to list ... if already existing in default list, override and warn
7577 for (auto opt : *defaultOpts) {
7578 l.Add(opt);
7579 }
7580 for (auto &i : nllOpts) {
7581 if (auto o = l.FindObject(i.GetName())) {
7582 Info("nll", "Overriding NLL Option: %s", o->GetName());
7583 l.Remove(o);
7584 }
7585 l.Add(const_cast<RooCmdArg *>(&i));
7586 }
7587
7588 return nll(_data, l);
7589}
7590
7591xRooNode xRooNode::generate(const xRooNode &fr, bool expected, int seed)
7592{
7593 if (auto mc = get<RooStats::ModelConfig>()) {
7594 return xRooNode(*mc->GetPdf(), fParent).generate(fr, expected, seed);
7595 }
7596
7597 if (!get<RooAbsPdf>()) {
7598 // before giving up, if this is a workspace we can proceed if we only have one model
7599 if (get<RooWorkspace>()) {
7600 std::shared_ptr<xRooNode> mainModel;
7601 for (auto &c : const_cast<xRooNode *>(this)->browse()) {
7602 if (c->get<RooAbsPdf>()) {
7603 if (!mainModel) {
7604 mainModel = c;
7605 } else {
7606 throw std::runtime_error(TString::Format("Workspace has multiple models, you must specify which to "
7607 "generate with (found at least %s and %s)",
7608 mainModel->GetName(), c->GetName()));
7609 }
7610 }
7611 }
7612 if (mainModel)
7613 return mainModel->generate(fr, expected, seed);
7614 }
7615 throw std::runtime_error(TString::Format("%s is not a pdf", GetName()));
7616 }
7617
7618 // when generating, will only include channels that are selected
7619 // any unselected but not hidden channel will have data from the only selected dataset added to it
7620 if (get<RooSimultaneous>()) {
7621 std::string selected;
7622 std::string fromds; // list of channels to take from selected ds
7623 bool hasDeselected = false;
7624 for (auto c : bins()) {
7625 TString cName(c->GetName());
7626 cName = cName(cName.Index('=') + 1, cName.Length());
7627 if (!c->get<RooAbsReal>()->isSelectedComp()) {
7628 hasDeselected = true;
7629 if (!c->get<RooAbsArg>()->getAttribute("hidden")) {
7630 if (!fromds.empty())
7631 fromds += ",";
7632 fromds += cName.Data();
7633 }
7634 } else {
7635 if (!selected.empty())
7636 selected += ",";
7637 selected += cName.Data();
7638 }
7639 }
7640 if (hasDeselected) {
7641 std::string dsetName = "";
7642 if (!fromds.empty()) {
7643 // use the first selected dataset as protodata
7644 auto _dsets = datasets();
7645 for (auto &d : _dsets) {
7646 if (d->get()->TestBit(1 << 20)) {
7647 dsetName = d->get()->GetName();
7648 break;
7649 }
7650 }
7651 if (dsetName.empty()) {
7652 throw std::runtime_error(
7653 "Need at least one dataset selected (SetChecked) to use for deselected regions");
7654 }
7655 }
7656 auto result = reduced(selected).generate(fr, expected, seed);
7657 if (!fromds.empty()) {
7658 auto ds = reduced(fromds).datasets()[dsetName];
7659 result.Add(*ds);
7660 result.SetName(TString(result.GetName()) + "_and_" + dsetName.c_str());
7661 }
7662 return result;
7663 }
7664 }
7665
7666 auto _fr = fr.get<RooFitResult>();
7667 xRooNode fr2;
7668 if (!_fr) {
7669 fr2 = fitResult();
7670 _fr = fr2.get<RooFitResult>();
7671 }
7672
7673 // must ensure fr has all the globs in its constPars list ... any missing must be added
7674 // otherwise generateFrom method wont determine globs properly
7675 // same for any missing pars
7676 auto _globs = globs();
7677 bool missingGlobs(false);
7678 for (auto glob : _globs) {
7679 if (!_fr->constPars().find(*glob->get<RooAbsArg>())) {
7680 missingGlobs = true;
7681 break;
7682 }
7683 }
7684
7685 std::unique_ptr<RooFitResult> newFr;
7686 if (missingGlobs) {
7687 newFr = std::make_unique<RooFitResult>(*_fr);
7688 for (auto glob : _globs) {
7689 if (!newFr->constPars().find(*glob->get<RooAbsArg>())) {
7690 const_cast<RooArgList &>(newFr->constPars()).addClone(*glob->get<RooAbsArg>());
7691 }
7692 }
7693 _fr = newFr.get();
7694 }
7695
7696 // check for missing fundamental pars (consts are not fundamentals)
7697 auto _pars = pars();
7698 bool missingPars(false);
7699 for (auto par : _pars) {
7700 if (!par->get<RooAbsArg>()->isFundamental())
7701 continue;
7702 if (!_fr->constPars().find(*par->get<RooAbsArg>()) && !_fr->floatParsFinal().find(*par->get<RooAbsArg>())) {
7703 missingPars = true;
7704 break;
7705 }
7706 }
7707
7708 if (missingPars) {
7709 newFr = std::make_unique<RooFitResult>(*_fr);
7710 for (auto par : _pars) {
7711 if (!par->get<RooAbsArg>()->isFundamental())
7712 continue;
7713 if (!newFr->constPars().find(*par->get<RooAbsArg>()) &&
7714 !newFr->floatParsFinal().find(*par->get<RooAbsArg>())) {
7715 const_cast<RooArgList &>(newFr->constPars()).addClone(*par->get<RooAbsArg>());
7716 }
7717 }
7718 _fr = newFr.get();
7719 }
7720
7721 return xRooNode(xRooFit::generateFrom(*get<RooAbsPdf>(), *_fr, expected, seed).first, *this);
7722
7723 // should add coords to the dataset too?
7724 // e.g. in the case of generating a dataset for a single channel, include the channelCat
7725 // this will allow datasets to then be combined.
7726 // could just say users must use 'reduced' on the simPdf, even if reducing to a single channel
7727}
7728
7729xRooNLLVar xRooNode::nll(const xRooNode &_data, const RooLinkedList &opts) const
7730{
7731 if (auto mc = get<RooStats::ModelConfig>()) {
7732 if (mc->GetExternalConstraints()) {
7734 for (auto o : opts) {
7735 optsWithConstraints.Add(o->Clone(nullptr));
7736 }
7737 optsWithConstraints.Add(RooFit::ExternalConstraints(*mc->GetExternalConstraints()).Clone(nullptr));
7738 return xRooNode(*mc->GetPdf(), fParent).nll(_data, optsWithConstraints);
7739 } else {
7740 return xRooNode(*mc->GetPdf(), fParent).nll(_data, opts);
7741 }
7742 }
7743
7744 if (!get<RooAbsPdf>()) {
7745 // before giving up, if this is a workspace we can proceed if we only have one model or pdf
7746 if (get<RooWorkspace>()) {
7747 std::shared_ptr<xRooNode> mainPdf, mainModel, otherPdf;
7748 for (auto &c : const_cast<xRooNode *>(this)->browse()) {
7749 if (c->get<RooAbsPdf>()) {
7750 if (!mainPdf) {
7751 mainPdf = c;
7752 } else {
7753 otherPdf = c;
7754 }
7755 } else if (c->get<RooStats::ModelConfig>()) {
7756 if (!mainModel) {
7757 mainModel = c;
7758 } else {
7759 throw std::runtime_error(TString::Format("Workspace has multiple models, you must specify which to "
7760 "build nll with (found at least %s and %s)",
7761 mainModel->GetName(), c->GetName()));
7762 }
7763 }
7764 }
7765 if (mainModel)
7766 return mainModel->nll(_data, opts);
7767 if (mainPdf) {
7768 if (otherPdf) {
7769 throw std::runtime_error(TString::Format("Workspace has multiple pdfs, you must specify which to "
7770 "build nll with (found at least %s and %s)",
7771 mainPdf->GetName(), otherPdf->GetName()));
7772 }
7773 return mainPdf->nll(_data, opts);
7774 }
7775 }
7776 throw std::runtime_error(TString::Format("%s is not a pdf", GetName()));
7777 }
7778
7779 // if simultaneous and any channels deselected then reduce and return
7780 if (get<RooSimultaneous>()) {
7781 std::string selected;
7782 bool hasDeselected = false;
7783 for (auto c : bins()) {
7784 if (!c->get<RooAbsReal>()->isSelectedComp()) {
7785 hasDeselected = true;
7786 } else {
7787 TString cName(c->GetName());
7788 cName = cName(cName.Index('=') + 1, cName.Length());
7789 if (!selected.empty())
7790 selected += ",";
7791 selected += cName.Data();
7792 }
7793 }
7794 if (hasDeselected)
7795 return reduced(selected).nll(_data, opts);
7796 }
7797
7798 if (!_data.get<RooAbsData>()) {
7799 // use node name to find dataset and recall
7800 auto _d = strlen(_data.GetName()) ? datasets().find(_data.GetName()) : nullptr;
7801 if (strlen(_data.GetName()) == 0) {
7802 // create the EXPECTED (asimov) dataset with the observables
7804 _d = std::make_shared<xRooNode>(asi.first, *this);
7805 if (asi.second) {
7806 _d->emplace_back(
7807 std::make_shared<xRooNode>(".globs", std::const_pointer_cast<RooAbsCollection>(asi.second), *_d));
7808 }
7809 } else if (!_d) {
7810 throw std::runtime_error(TString::Format("Cannot find dataset %s", _data.GetName()));
7811 }
7812 return nll(*_d, opts);
7813 } else if (!_data.fParent || _data.fParent->fComp != fComp) {
7814 // dataset is not parented by this node ... meaning it may need to be reduced,
7815 // do this via the datasets() method by attaching and detaching the dataset
7816 xRooNode me(*this); // since we are in a const method, need to create a copy node.
7817 me.push_back(std::make_shared<xRooNode>(_data));
7818 return nll(*me.datasets().at(_data.GetName()), opts);
7819 }
7820
7821 auto _globs = _data.globs(); // keep alive because may own the globs
7822
7823 auto _opts = std::shared_ptr<RooLinkedList>(new RooLinkedList, [](RooLinkedList *l) {
7824 if (l)
7825 l->Delete();
7826 delete l;
7827 });
7828 RooArgSet _globsSet(_globs.argList());
7830 if (GetRange() && strlen(GetRange()))
7831 _opts->Add(RooFit::Range(GetRange()).Clone());
7832
7833 // copy over opts ... need to clone each so can safely delete when _opts destroyed
7834 for (int i = 0; i < opts.GetSize(); i++) {
7835 if (strlen(opts.At(i)->GetName()) == 0)
7836 continue; // skipping "none" cmds
7837 if (strcmp(opts.At(i)->GetName(), "GlobalObservables") == 0) {
7838 // maybe warn here?
7839 } else {
7840 _opts->Add(opts.At(i)->Clone(nullptr)); // nullptr needed because accessing Clone via TObject base class puts
7841 // "" instead, so doesnt copy names
7842 }
7843 }
7844
7845 // use shared_ptr method so NLLVar will take ownership of datasets etc if created above
7846 // snapshots the globs out of the nllOpts (see specific constructor of xRooNLLVar)
7847 auto out = xRooFit::createNLL(std::dynamic_pointer_cast<RooAbsPdf>(fComp),
7848 std::dynamic_pointer_cast<RooAbsData>(_data.fComp), *_opts);
7849 return out;
7850}
7851
7852// xRooNode xRooNode::fitTo(const xRooNode& _data) const {
7853//
7854//
7855// auto _pdf = get<RooAbsPdf>();
7856// if (!_pdf) throw std::runtime_error("Not a pdf");
7857//
7858// auto _globs = _data.globs(); // keep alive because may own the globs
7859// RooArgSet globsSet(_globs.argList());
7860//
7861// std::shared_ptr<RooSimultaneous> newPdf;
7862// if(auto s = get<RooSimultaneous>(); s) {
7863// auto rangeName = GetRange();
7864// if (rangeName) {
7865// // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
7866// std::vector<TString> chanPatterns;
7867// TStringToken pattern(rangeName, ",");
7868// while (pattern.NextToken()) {
7869// chanPatterns.emplace_back(pattern);
7870// }
7871// auto& _cat = const_cast<RooAbsCategoryLValue&>(s->indexCat());
7872// newPdf = std::make_shared<RooSimultaneous>(TString::Format("%s_reduced",GetName()),"Reduced model",_cat);
7873// for(auto& c : variations()) {
7874// TString cName(c->GetName());
7875// cName = cName(cName.Index('=')+1,cName.Length());
7876// _cat.setLabel(cName);
7877// bool matchAny=false;
7878// for(auto& p : chanPatterns) {
7879// if (cName.Contains(TRegexp(p,true))) { matchAny=true; break; }
7880// if (_cat.hasRange(p) && _cat.inRange(p)) { matchAny=true; break; }
7881// }
7882// if(matchAny) {
7883// newPdf->addPdf( *c->get<RooAbsPdf>(), cName );
7884// }
7885// }
7886// RooFitResultTree t(newPdf->GetName(),"",*newPdf);
7887// auto _fr = std::const_pointer_cast<RooFitResult>(t.fitTo(_data.get<RooAbsData>(), &globsSet));
7888// xRooNode parent(_data.GetName(),nullptr,*this);
7889// xRooNode out(_fr->GetName(),/*acquire(_fr)*/ _fr,parent);
7890// // do full propagation by 'checking' the fr ...
7891// out.Checked(&out,true);
7892// return out;
7893// }
7894// }
7895//
7896//
7897//
7898// std::string treeName = TString::Format("fits_%s",GetName()).Data();
7899//
7900// auto _frt = getObject<TTree>(treeName); // get existing frt
7901//
7902// std::shared_ptr<RooFitResultTree> t;
7903// if (_frt) {
7904// t = std::make_shared<RooFitResultTree>(_frt.get());
7905// } else {
7906// t = std::make_shared<RooFitResultTree>(treeName.c_str(),"",*_pdf);
7907// }
7908// //t->SetProgress(true);
7909// auto _fr = std::const_pointer_cast<RooFitResult>(t->fitTo(_data.get<RooAbsData>(), &globsSet));
7910//
7911//
7912//
7913// /*
7914// obs().argList() = s; // sets global observables to their values
7915// auto _fr =
7916// std::shared_ptr<RooFitResult>(_pdf->fitTo(*_data->get<RooAbsData>(),RooFit::GlobalObservables(s),RooFit::Offset(true),RooFit::Save()));
7917// _fr->SetName(TUUID().AsString());
7918// // restore parameters before returning
7919// *std::unique_ptr<RooArgSet>(_pdf->getDependents(_fr->floatParsFinal())) = _fr->floatParsInit();
7920// */
7921//
7922// //_fr->SetTitle(TString::Format("%s;%s",GetName(),datasetName));
7923// if (!_frt) {
7924// t =
7925// std::make_shared<RooFitResultTree>(std::dynamic_pointer_cast<TTree>(const_cast<xRooNode*>(this)->acquire(t->fTree)).get());
7926// }
7927// xRooNode parent(_data.GetName(),nullptr,xRooNode(t,*this));
7928// xRooNode out(_fr->GetName(),/*acquire(_fr)*/ _fr,parent);
7929// // do full propagation by 'checking' the fr ...
7930// out.Checked(&out,true);
7931// return out;
7932// }
7933
7934std::shared_ptr<xRooNode> xRooNode::parentPdf() const
7935{
7936 // find first parent that is a pdf
7937 auto out = fParent;
7938 while (out && !out->get<RooAbsPdf>()) {
7939 out = out->fParent;
7940 }
7941 return out;
7942}
7943
7944xRooNode xRooNode::reduced(const std::string &_range, bool invert) const
7945{
7946 auto rangeName = (_range.empty()) ? GetRange() : _range;
7947 if (!rangeName.empty()) {
7948 std::vector<TString> patterns;
7949 TStringToken pattern(rangeName, ",");
7950 while (pattern.NextToken()) {
7951 patterns.emplace_back(pattern);
7952 }
7953 if (auto s = get<RooSimultaneous>(); s) {
7954 // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
7955 auto &_cat = const_cast<RooAbsCategoryLValue &>(s->indexCat());
7956 auto newPdf =
7957 std::make_shared<RooSimultaneous>(TString::Format("%s_reduced", GetName()), "Reduced model", _cat);
7958 for (auto &c : bins()) {
7959 TString cName(c->GetName());
7960 cName = cName(cName.Index('=') + 1, cName.Length());
7961 _cat.setLabel(cName);
7962 bool matchAny = false;
7963 for (auto &p : patterns) {
7965 if (pNoCatName.Contains('='))
7966 pNoCatName = pNoCatName(pNoCatName.Index('=') + 1, pNoCatName.Length());
7967 if (cName.Contains(TRegexp(p, true)) || cName.Contains(TRegexp(pNoCatName, true))) {
7968 matchAny = true;
7969 break;
7970 }
7971 if (_cat.hasRange(p) && _cat.inRange(p)) {
7972 matchAny = true;
7973 break;
7974 }
7975 }
7976 if ((matchAny && !invert) || (!matchAny && invert)) {
7977 newPdf->addPdf(*c->get<RooAbsPdf>(), cName);
7978 }
7979 }
7980 return xRooNode(newPdf, fParent);
7981 } else if (get() && !get<RooAbsCollection>() && !components().empty()) {
7982 // create a new obj and remove non-matching components
7983 xRooNode out(std::shared_ptr<TObject>(get()->Clone(TString::Format("%s_reduced", get()->GetName()))), fParent);
7984 // go through components and remove any that don't match pattern
7985 std::vector<TObject *> funcs; // to be removed
7986 for (auto &c : out.components()) {
7987 bool matchAny = false;
7988 for (auto &p : patterns) {
7989 if (TString(c->GetName()).Contains(TRegexp(p, true))) {
7990 matchAny = true;
7991 break;
7992 }
7993 }
7994 if (!((matchAny && !invert) || (!matchAny && invert)))
7995 funcs.push_back(c->get());
7996 }
7997 for (auto &c : funcs)
7998 out.Remove(*c);
7999 if (!funcs.empty()) {
8000 if (auto _pdf = out.get<RooRealSumPdf>(); _pdf) {
8001 _pdf->setFloor(false); // remove floor if removed some functions, which allows evaluation of negative
8002 // valued components
8003 }
8004 }
8005 out.browse();
8006 return out;
8007 } else if (auto fr = get<RooFitResult>()) {
8008 // reduce the fit result by moving unselected float pars into the constPars list and dropping their covariances
8009 xRooNode out(std::shared_ptr<TObject>(fr->Clone(TString::Format("%s_reduced", fr->GetName()))), fParent);
8010 fr = out.get<RooFitResult>();
8011 RooArgList _pars = fr->floatParsFinal();
8013 for (auto c : _pars) {
8014 bool matchAny = false;
8015 for (auto &p : patterns) {
8016 if (TString(c->GetName()).Contains(TRegexp(p, true))) {
8017 matchAny = true;
8018 break;
8019 }
8020 }
8021 if (!((matchAny && !invert) || (!matchAny && invert))) {
8022 _remPars.add(*c);
8023 }
8024 }
8025 _pars.remove(_remPars, true);
8026
8027 auto _tmp = fr->reducedCovarianceMatrix(_pars);
8028 int covQualBackup = fr->covQual();
8029 fr->setCovarianceMatrix(_tmp);
8030 fr->setCovQual(covQualBackup);
8031 const_cast<RooArgList &>(fr->floatParsFinal())
8032 .remove(_remPars, true); // is this a memory leak ... should delete the remPars?
8033 return out;
8034
8035 } else if (!get() || get<RooAbsCollection>()) {
8036 // filter the children .... handle special case of filtering ".vars" with "x" option too
8037 xRooNode out(std::make_shared<RooArgList>(), fParent);
8038 out.SetName(TString(GetName()) + "_reduced");
8039 size_t nobs = 0;
8040 bool notAllArgs = false;
8041 bool isVars = (strcmp(GetName(), ".vars") == 0);
8042 for (auto c : *this) {
8043 nobs += (c->fFolder == "!robs" || c->fFolder == "!globs");
8044 bool matchAny = false;
8045 for (auto &p : patterns) {
8046 if (TString(c->GetName()).Contains(TRegexp(p, true)) ||
8047 (isVars && p == "x" && (c->fFolder == "!robs" || c->fFolder == "!globs") && nobs == 1)) {
8048 matchAny = true;
8049 break;
8050 }
8051 }
8052 if ((matchAny && !invert) || (!matchAny && invert)) {
8053 out.push_back(c);
8054 if (auto a = c->get<RooAbsArg>()) {
8055 out.get<RooArgList>()->add(*a);
8056 } else {
8057 notAllArgs = true;
8058 }
8059 }
8060 }
8061 if (notAllArgs) {
8062 out.fComp.reset();
8063 }
8064 return out;
8065 }
8066 }
8067
8068 return get<RooArgList>() ? xRooNode(std::make_shared<RooArgList>(), fParent) : *this;
8069}
8070
8071xRooNode xRooNode::reduced(const std::function<bool(const xRooNode &)> selector) const
8072{
8073 if (empty()) {
8074 const_cast<xRooNode &>(*this).browse();
8075 }
8076 // build a list of children to keep
8077 std::string noName = "___"; // assume this is never a name
8078 std::string childNames;
8079 for (auto &c : *this) {
8080 if (selector(*c)) {
8081 if (!childNames.empty())
8082 childNames += ",";
8083 childNames += c->GetName();
8084 }
8085 }
8086 if (childNames.empty())
8087 childNames = noName; // if childNames was blank it would return the full list;
8088 return reduced(childNames); // calls main method above ... this will ensure we construct a reduced version of ourself
8089}
8090
8091// xRooNode xRooNode::generate(bool expected) const {
8092//
8093// auto fr = fitResult();
8094// auto _fr = fr.get<RooFitResult>();
8095//
8096// auto _pdf = (get<RooAbsPdf>()) ? std::shared_ptr<const xRooNode>(this, [](const xRooNode*){}) : parentPdf();
8097// if (!_pdf) {
8098// throw std::runtime_error("Could not find pdf");
8099// }
8100//
8101// std::shared_ptr<RooDataTree> t;
8102//
8103// std::shared_ptr<RooSimultaneous> newPdf;
8104// if(auto s = _pdf->get<RooSimultaneous>(); s) {
8105// auto rangeName = GetRange();
8106// if (rangeName) {
8107// // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
8108// std::vector<TString> chanPatterns;
8109// TStringToken pattern(rangeName, ",");
8110// while (pattern.NextToken()) {
8111// chanPatterns.emplace_back(pattern);
8112// }
8113// auto& _cat = const_cast<RooAbsCategoryLValue&>(s->indexCat());
8114// newPdf = std::make_shared<RooSimultaneous>(TString::Format("%s_reduced",GetName()),"Reduced model",_cat);
8115// for(auto& c : _pdf->variations()) {
8116// TString cName(c->GetName());
8117// cName = cName(cName.Index('=')+1,cName.Length());
8118// _cat.setLabel(cName);
8119// bool matchAny=false;
8120// for(auto& p : chanPatterns) {
8121// if (cName.Contains(TRegexp(p,true))) { matchAny=true; break; }
8122// if (_cat.hasRange(p) && _cat.inRange(p)) { matchAny=true; break; }
8123// }
8124// if(matchAny) {
8125// newPdf->addPdf( *c->get<RooAbsPdf>(), cName );
8126// }
8127// }
8128// t = std::make_shared<RooDataTree>(newPdf->GetName(),"",*newPdf);
8129// RooArgSet s1(_pdf->obs().argList());
8130// RooArgSet s2(_pdf->globs().argList());s1.remove(s2);
8131// t->SetObservables(&s1,&s2);
8132// auto _data = t->generate(_fr,expected);
8133//
8134// xRooNode parent(_fr ? _fr->GetName() : "unknown",nullptr,xRooNode(t,*this));
8135// xRooNode out(_data.first->GetName(),/*acquire(_fr)*/ _data.first,parent);
8136// out.emplace_back(std::make_shared<xRooNode>(".globs",std::const_pointer_cast<RooArgSet>(_data.second),out));
8137// return out;
8138// }
8139// }
8140//
8141//
8142// std::string treeName = TString::Format("gen_%s",_pdf->GetName()).Data();
8143//
8144// auto _frt = getObject<TTree>(treeName); // get existing frt
8145//
8146//
8147// if (_frt) {
8148// t = std::make_shared<RooDataTree>(_frt.get());
8149// } else {
8150// t = std::make_shared<RooDataTree>(treeName.c_str(),"",*_pdf->get<RooAbsPdf>());
8151// RooArgSet s1(_pdf->obs().argList());
8152// RooArgSet s2(_pdf->globs().argList());s1.remove(s2);
8153// t->SetObservables(&s1,&s2);
8154// }
8155// auto _data = t->generate(_fr,expected);
8156// if (!_frt) {
8157// t =
8158// std::make_shared<RooDataTree>(std::dynamic_pointer_cast<TTree>(const_cast<xRooNode*>(this)->acquire(t->fTree)).get());
8159// }
8160// xRooNode parent(_fr ? _fr->GetName() : "unknown",nullptr,xRooNode(t,*this));
8161// xRooNode out(_data.first->GetName(),/*acquire(_fr)*/ _data.first,parent);
8162// out.emplace_back(std::make_shared<xRooNode>(".globs",std::const_pointer_cast<RooArgSet>(_data.second),out));
8163// return out;
8164// }
8165
8167public:
8169 double expectedEvents(const RooArgSet *nset) const override
8170 {
8171 return static_cast<RooAbsPdf *>(intpdf.absArg())->expectedEvents(nset);
8172 }
8173 ExtendMode extendMode() const override { return static_cast<RooAbsPdf *>(intpdf.absArg())->extendMode(); }
8174 TObject *clone(const char *newname) const override { return new xRooProjectedPdf(*this, newname); }
8175
8176protected:
8177 double evaluate() const override
8178 {
8179 int code;
8180 return getProjection(&intobs, _normSet, (_normRange.Length() > 0 ? _normRange.Data() : nullptr), code)->getVal();
8181 }
8182};
8183
8184std::vector<double> new_getPropagatedErrors(const RooAbsReal &f, const RooFitResult &fr, const RooArgSet &nset,
8185 RooArgList **pars, bool asymHi, bool asymLo, const std::vector<int> &bins,
8186 const std::function<void(int)> &setBin)
8187{
8188 const size_t nBins = bins.size();
8189 std::vector<double> out(nBins, 0.);
8190 if (nBins == 0)
8191 return out;
8192
8193 // Build the list of parameters f depends on that have a non-zero error.
8194 // The result is cached in *pars so it is only computed once across repeated
8195 // calls (e.g. the separate Hi and Lo passes of an asymmetric error band).
8196 RooArgList *_pars = (pars) ? *pars : nullptr;
8197
8198 if (!_pars) {
8199
8201 f.getParameters(&nset, allParamsInAbsReal);
8202
8203 _pars = new RooArgList;
8205
8206 auto rrvInAbsReal = static_cast<RooRealVar const *>(allParamsInAbsReal.find(*rrvFitRes));
8207
8208 // Strip out parameters with zero error
8209 if (rrvFitRes->getError() <= std::abs(rrvFitRes->getVal()) * std::numeric_limits<double>::epsilon())
8210 continue;
8211
8212 // Ignore parameters in the fit result that this RooAbsReal doesn't depend on
8213 if (!rrvInAbsReal)
8214 continue;
8215
8216 // Checking for float equality is a bad. We check if the values are
8217 // negligibly far away from each other, relative to the uncertainty.
8218 if (std::abs(rrvInAbsReal->getVal() - rrvFitRes->getVal()) > 0.01 * rrvFitRes->getError()) {
8219 std::stringstream errMsg;
8220 errMsg << "RooAbsReal::getPropagatedError(): the parameters of the RooAbsReal don't have"
8221 << " the same values as in the fit result! The logic of getPropagatedError is broken in this case."
8222 << " \n " << rrvInAbsReal->GetName() << " : " << rrvInAbsReal->getVal() << " vs "
8223 << rrvFitRes->getVal();
8224
8225 throw std::runtime_error(errMsg.str());
8226 }
8227
8228 _pars->add(*rrvInAbsReal);
8229 }
8230 }
8231
8232 const size_t nPars = _pars->size();
8233
8234 // Covariance and correlation matrices depend only on the fit result and the
8235 // parameter set, so build them (including the matrix reduction) once for all
8236 // bins rather than once per bin.
8237 // TODO: if _pars includes pars not in fr, need to extend matrix with uncorrelated errors of those pars
8238 // (as built above, _pars is a subset of fr.floatParsFinal(), so such pars are currently dropped silently).
8240
8241 TMatrixDSym C(nPars);
8242 for (size_t i = 0; i < nPars; i++) {
8243 for (size_t j = i; j < nPars; j++) {
8244 C(i, j) = V(i, j) / std::sqrt(V(i, i) * V(j, j));
8245 C(j, i) = C(i, j);
8246 }
8247 }
8248
8249 // Nominal value of each bin (only needed to symmetrize asymmetric errors).
8250 std::vector<double> nomVals;
8251 if (asymHi || asymLo) {
8252 nomVals.resize(nBins);
8253 for (size_t k = 0; k < nBins; k++) {
8254 setBin(bins[k]);
8255 nomVals[k] = f.getVal(nset);
8256 }
8257 }
8258
8259 // F[k] holds, for bin k, the vector of (signed) variations w.r.t. each
8260 // parameter - i.e. the column of the Jacobian times the parameter error.
8261 std::vector<TVectorD> F(nBins, TVectorD(nPars));
8262
8263 std::vector<double> plusVar(nBins), minusVar(nBins);
8264
8265 for (size_t ivar = 0; ivar < nPars; ivar++) {
8266
8267 auto &rrv = static_cast<RooRealVar &>((*_pars)[ivar]);
8268 auto *frrrv = static_cast<RooRealVar *>(fr.floatParsFinal().find(rrv));
8269
8270 double cenVal = rrv.getVal();
8271
8272 if (asymHi || asymLo) {
8273 double errValHi = frrrv->getErrorHi();
8274 rrv.setVal(errValHi > 0 ? std::min(cenVal + errValHi, rrv.getMax())
8275 : std::max(cenVal + errValHi, rrv.getMin()));
8276 for (size_t k = 0; k < nBins; k++) {
8277 setBin(bins[k]);
8278 plusVar[k] = f.getVal(nset);
8279 }
8280 double errValLo = frrrv->getErrorLo();
8281 rrv.setVal(errValLo > 0 ? std::min(cenVal + errValLo, rrv.getMax())
8282 : std::max(cenVal + errValLo, rrv.getMin()));
8283 for (size_t k = 0; k < nBins; k++) {
8284 setBin(bins[k]);
8285 minusVar[k] = f.getVal(nset);
8286 }
8287 rrv.setVal(cenVal);
8288 for (size_t k = 0; k < nBins; k++) {
8289 double hi = plusVar[k];
8290 double lo = minusVar[k];
8291 if (asymHi) {
8292 // pick the one that moved result 'up' most
8293 hi = std::max(plusVar[k], minusVar[k]);
8294 lo = 2 * nomVals[k] - hi; // symmetrizes
8295 } else {
8296 // pick the one that moved result 'down' most
8297 lo = std::min(plusVar[k], minusVar[k]);
8298 hi = 2 * nomVals[k] - lo; // symmetrizes
8299 }
8300 F[k][ivar] = (hi - lo) * 0.5;
8301 }
8302 } else {
8303 double errVal = sqrt(V(ivar, ivar));
8304 // Make Plus variation
8305 rrv.setVal(std::min(cenVal + errVal, rrv.getMax()));
8306 for (size_t k = 0; k < nBins; k++) {
8307 setBin(bins[k]);
8308 plusVar[k] = f.getVal(nset);
8309 }
8310 // Make Minus variation
8311 rrv.setVal(std::max(cenVal - errVal, rrv.getMin()));
8312 for (size_t k = 0; k < nBins; k++) {
8313 setBin(bins[k]);
8314 minusVar[k] = f.getVal(nset);
8315 }
8316 rrv.setVal(cenVal);
8317 for (size_t k = 0; k < nBins; k++) {
8318 F[k][ivar] = (plusVar[k] - minusVar[k]) * 0.5;
8319 }
8320 }
8321 }
8322
8323 // Re-evaluate f with the central parameters just to be extra-safe that this
8324 // call doesn't leave any state changed. It should not be necessary because
8325 // thanks to the dirty flag propagation f is re-evaluated anyway the next
8326 // time getVal() is called, but there are imaginable corner cases where that
8327 // would not be triggered (e.g. if the caller changes the RooFit operation
8328 // mode after the error propagation).
8329 setBin(bins[nBins - 1]);
8330 f.getVal(nset);
8331
8332 // Calculate error in linear approximation from variations and correlation
8333 // coefficients - this is pure arithmetic (no function evaluations).
8334 for (size_t k = 0; k < nBins; k++) {
8335 out[k] = std::sqrt(F[k] * (C * F[k]));
8336 }
8337
8338 if (!pars) {
8339 delete _pars;
8340 } else {
8341 *pars = _pars;
8342 }
8343
8344 return out;
8345}
8346
8347class PdfWrapper : public RooAbsPdf {
8348public:
8349 // need expPdf option while RooProjectedPdf doesn't support keeping things extended
8350 PdfWrapper(RooAbsReal &f, RooAbsReal *coef, bool expEvMode = false, RooAbsPdf *expPdf = nullptr)
8351 : RooAbsPdf(Form("exp_%s", f.GetName())),
8352 fFunc("func", "func", this, f),
8353 fCoef("coef", "coef", this),
8354 fExpPdf("expPdf", "expPdf", this)
8355 {
8356 // don't treat pdf as extended if it has a coefficient and is RooAddPdf: RooAddPdf doesn't extend them unless no
8357 // coefs for any (and all are extendable)
8358 if (coef) {
8359 fCoef.setArg(*coef);
8360 }
8361 if (expPdf && expPdf->canBeExtended() && !(coef && dynamic_cast<RooAddPdf *>(expPdf))) {
8362 fExpPdf.setArg(*expPdf);
8363 } else if (auto _p = dynamic_cast<RooAbsPdf *>(&f);
8364 _p && _p->canBeExtended() && !(coef && dynamic_cast<RooAddPdf *>(_p))) {
8365 fExpPdf.setArg(f); // using self for expectation
8366 }
8367 fExpectedEventsMode = expEvMode;
8368 }
8369 ~PdfWrapper() override {};
8370 PdfWrapper(const PdfWrapper &other, const char *name = nullptr)
8371 : RooAbsPdf(other, name),
8372 fFunc("func", this, other.fFunc),
8373 fCoef("coef", this, other.fCoef),
8374 fExpPdf("expPdf", this, other.fExpPdf),
8375 fExpectedEventsMode(other.fExpectedEventsMode)
8376 {
8377 }
8378 TObject *clone(const char *newname) const override { return new PdfWrapper(*this, newname); }
8379 bool isBinnedDistribution(const RooArgSet &obs) const override { return fFunc->isBinnedDistribution(obs); }
8380 std::list<double> *binBoundaries(RooAbsRealLValue &obs, double xlo, double xhi) const override
8381 {
8382 return fFunc->binBoundaries(obs, xlo, xhi);
8383 }
8384
8385 double evaluate() const override
8386 {
8387 return (fExpectedEventsMode ? 1. : fFunc) *
8388 ((fExpPdf.absArg()) ? static_cast<RooAbsPdf *>(fExpPdf.absArg())->expectedEvents(_normSet) : 1.) *
8389 (fCoef.absArg() ? fCoef : 1.);
8390 }
8391
8392 bool selfNormalized() const override
8393 {
8394 return true;
8395 } // so that doesn't try to do an integral because we are passing integration onto fFunc in evaluate
8396
8397 // faster than full evaluation because doesnt make the integral dependent on the full expression
8399 {
8400#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 28, 00)
8401 double oo = getPropagatedError(fr, nset_in); // method was improved in 6.28 so use this instead
8402 if (std::isnan(oo)) {
8403 // may be consequence of zero uncerts
8404 // Calling getParameters() might be costly, but necessary to get the right
8405 // parameters in the RooAbsReal. The RooFitResult only stores snapshots.
8407 getParameters(&nset_in, allParamsInAbsReal);
8408
8409 RooArgList paramList;
8411
8412 auto rrvInAbsReal = static_cast<RooRealVar const *>(allParamsInAbsReal.find(*rrvFitRes));
8413
8414 // If this RooAbsReal is a RooRealVar in the fit result, we don't need to
8415 // propagate anything and can just return the error in the fit result
8416 if (rrvFitRes->namePtr() == namePtr())
8417 return rrvFitRes->getError();
8418
8419 // Strip out parameters with zero error
8420 if (!rrvFitRes->hasError() ||
8421 rrvFitRes->getError() <= std::abs(rrvFitRes->getVal()) * std::numeric_limits<double>::epsilon())
8422 continue;
8423
8424 // Ignore parameters in the fit result that this RooAbsReal doesn't depend on
8425 if (!rrvInAbsReal)
8426 continue;
8427
8428 // Checking for float equality is a bad. We check if the values are
8429 // negligibly far away from each other, relative to the uncertainty.
8430 if (std::abs(rrvInAbsReal->getVal() - rrvFitRes->getVal()) > 0.01 * rrvFitRes->getError()) {
8431 std::stringstream errMsg;
8432 errMsg
8433 << "RooAbsReal::getPropagatedError(): the parameters of the RooAbsReal don't have"
8434 << " the same values as in the fit result! The logic of getPropagatedError is broken in this case.";
8435
8436 throw std::runtime_error(errMsg.str());
8437 }
8438
8439 paramList.add(*rrvInAbsReal);
8440 }
8441 if (paramList.empty())
8442 return 0.;
8443
8444 std::vector<double> plusVar;
8445 std::vector<double> minusVar;
8446 plusVar.reserve(paramList.size());
8447 minusVar.reserve(paramList.size());
8448
8449 // Create std::vector of plus,minus variations for each parameter
8450 TMatrixDSym V(paramList.size() == fr.floatParsFinal().size() ? fr.covarianceMatrix()
8451 : fr.reducedCovarianceMatrix(paramList));
8452
8453 for (std::size_t ivar = 0; ivar < paramList.size(); ivar++) {
8454
8455 auto &rrv = static_cast<RooRealVar &>(paramList[ivar]);
8456
8457 double cenVal = rrv.getVal();
8458 double errVal = sqrt(V(ivar, ivar));
8459
8460 // this next thing happens if the par has errors but the covariance matrix is empty
8461 // this only happens if the fit was dodgy, so perhaps best to not even try to recover from this
8462 // screwup ... hence I've commented out this fixup here and will let the errors be nan
8463 // if(errVal==0) {
8464 // Warning("getPropagatedError","Missing variance for %s",rrv.GetName());
8465 // errVal = rrv.getError();
8466 // V(ivar,ivar) = errVal*errVal;
8467 // }
8468
8469 // Make Plus variation
8470 rrv.setVal(cenVal + errVal);
8471 plusVar.push_back(getVal(nset_in));
8472
8473 // Make Minus variation
8474 rrv.setVal(cenVal - errVal);
8475 minusVar.push_back(getVal(nset_in));
8476#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
8477 // can try to recover nans ... this stopped being possible in 6.27 onwards because NaNPacker made private
8478 if (std::isnan(plusVar.back()) && RooNaNPacker::isNaNWithPayload(plusVar.back())) {
8479 plusVar.back() = -RooNaNPacker::unpackNaN(plusVar.back());
8480 }
8481 if (std::isnan(minusVar.back()) && RooNaNPacker::isNaNWithPayload(minusVar.back())) {
8482 minusVar.back() = -RooNaNPacker::unpackNaN(minusVar.back());
8483 }
8484#endif
8485 // std::cout << plusVar.back() << " and " << minusVar.back() << std::endl;
8486
8487 rrv.setVal(cenVal);
8488 }
8489
8490 // Re-evaluate this RooAbsReal with the central parameters just to be
8491 // extra-safe that a call to `getPropagatedError()` doesn't change any state.
8492 // It should not be necessary because thanks to the dirty flag propagation
8493 // the RooAbsReal is re-evaluated anyway the next time getVal() is called.
8494 // Still there are imaginable corner cases where it would not be triggered,
8495 // for example if the user changes the RooFit operation more after the error
8496 // propagation.
8497 getVal(nset_in);
8498
8499 TMatrixDSym C(paramList.size());
8500 std::vector<double> errVec(paramList.size());
8501 for (std::size_t i = 0; i < paramList.size(); i++) {
8502 errVec[i] = std::sqrt(V(i, i));
8503 for (std::size_t j = i; j < paramList.size(); j++) {
8504 C(i, j) = V(i, j) / std::sqrt(V(i, i) * V(j, j));
8505 C(j, i) = C(i, j);
8506 }
8507 }
8508
8509 // Make std::vector of variations
8510 TVectorD F(plusVar.size());
8511 for (unsigned int j = 0; j < plusVar.size(); j++) {
8512 F[j] = (plusVar[j] - minusVar[j]) / 2;
8513 }
8514
8515 // Calculate error in linear approximation from variations and correlation coefficient
8516 double sum = F * (C * F);
8517
8518 return sqrt(sum);
8519 }
8520 return oo;
8521#else
8522
8523 // Strip out parameters with zero error
8526 if (frv->getError() > 1e-20) {
8527 fpf_stripped.add(*frv);
8528 }
8529 }
8530
8531 // Clone self for internal use
8532 RooAbsReal *cloneFunc = const_cast<PdfWrapper *>(this); // (RooAbsReal *)fFunc.absArg()->cloneTree();
8533 // RooAbsPdf *clonePdf = dynamic_cast<RooAbsPdf *>(cloneFunc);
8534 RooArgSet *errorParams = cloneFunc->getObservables(fpf_stripped);
8535
8536 RooArgSet *nset =
8537 nset_in.size() == 0 ? cloneFunc->getParameters(*errorParams) : cloneFunc->getObservables(nset_in);
8538
8539 // Make list of parameter instances of cloneFunc in order of error matrix
8540 RooArgList paramList;
8541 const RooArgList &fpf = fpf_stripped;
8542 std::vector<int> fpf_idx;
8543 for (Int_t i = 0; i < fpf.size(); i++) {
8544 RooAbsArg *par = errorParams->find(fpf[i].GetName());
8545 if (par) {
8546 paramList.add(*par);
8547 fpf_idx.push_back(i);
8548 }
8549 }
8550
8551 std::vector<double> plusVar, minusVar;
8552
8553 // Create vector of plus,minus variations for each parameter
8554 TMatrixDSym V(paramList.size() == fr.floatParsFinal().size() ? fr.covarianceMatrix()
8555 : fr.reducedCovarianceMatrix(paramList));
8556
8557 for (Int_t ivar = 0; ivar < paramList.size(); ivar++) {
8558
8560
8561 double cenVal = rrv.getVal();
8562 double errVal = sqrt(V(ivar, ivar));
8563
8564 // Make Plus variation
8565 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal + errVal);
8566 // plusVar.push_back((fExpectedEventsMode ? 1. : cloneFunc->getVal(nset)) *
8567 // (clonePdf ? clonePdf->expectedEvents(nset) : 1.));
8568 plusVar.push_back(cloneFunc->getVal(nset));
8569
8570 // Make Minus variation
8571 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal - errVal);
8572 // minusVar.push_back((fExpectedEventsMode ? 1. : cloneFunc->getVal(nset)) *
8573 // (clonePdf ? clonePdf->expectedEvents(nset) : 1.));
8574 minusVar.push_back(cloneFunc->getVal(nset));
8575
8576 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal);
8577 }
8578
8579 getVal(nset); // reset state
8580
8581 TMatrixDSym C(paramList.size());
8582 std::vector<double> errVec(paramList.size());
8583 for (int i = 0; i < paramList.size(); i++) {
8584 errVec[i] = sqrt(V(i, i));
8585 for (int j = i; j < paramList.size(); j++) {
8586 C(i, j) = V(i, j) / sqrt(V(i, i) * V(j, j));
8587 C(j, i) = C(i, j);
8588 }
8589 }
8590
8591 // Make vector of variations
8592 TVectorD F(plusVar.size());
8593 for (unsigned int j = 0; j < plusVar.size(); j++) {
8594 F[j] = (plusVar[j] - minusVar[j]) / 2;
8595 }
8596
8597 // Calculate error in linear approximation from variations and correlation coefficient
8598 double sum = F * (C * F);
8599
8600 // delete cloneFunc;
8601 delete errorParams;
8602 delete nset;
8603
8604 return sqrt(sum);
8605#endif
8606 }
8607
8608private:
8612 bool fExpectedEventsMode = false;
8613};
8614
8615const xRooNode *runningNode = nullptr;
8617
8619{
8620 std::cout << "Got signal " << signum << std::endl;
8621 if (signum == SIGINT) {
8622 std::cout << "Keyboard interrupt while building histogram" << std::endl;
8623 // TODO: create a global mutex for this
8624 runningNode->fInterrupted = true;
8625 } else {
8627 }
8628}
8629
8631{
8632 auto _doSterilize = [](RooAbsArg *obj) {
8633 if (!obj)
8634 return;
8635 for (int i = 0; i < obj->numCaches(); i++) {
8636 if (auto cache = dynamic_cast<RooObjCacheManager *>(obj->getCache(i))) {
8637 cache->reset();
8638 }
8639 }
8640 if (RooAbsPdf *p = dynamic_cast<RooAbsPdf *>(obj); p) {
8641 p->setNormRange(p->normRange());
8642 }
8643#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
8644 if (RooAbsReal *p = dynamic_cast<RooAbsReal *>(obj); p) {
8645 // need to forget about any normSet that was passed to getVal(...)
8646 // doesn't seem necessary in 6.28
8647
8648 p->setProxyNormSet(nullptr);
8649 p->_lastNSet = nullptr;
8650 }
8651#endif
8652 obj->setValueDirty();
8653 };
8654 if (auto w = get<RooWorkspace>(); w) {
8655 // sterilizing all nodes
8656 for (auto &c : w->components()) {
8657 _doSterilize(c);
8658 }
8659 return;
8660 }
8661 // recursive through all clients and sterlize their normalization caches
8662 std::function<void(RooAbsArg *)> func;
8663 func = [&](RooAbsArg *a) {
8664 if (!a) {
8665 return;
8666 }
8667 _doSterilize(a); // sterilize first so that cache elements don't appear in the client list
8668 // safety net in case sterilizing one client deletes another one of our clients
8669 // monitor for change in clients list size
8670 // found this was only case in 6.26 (valgrind shows invalid read), in 6.28 these went away
8671 // might be in 6.28 the client list iterator became able to handle in-loop edits but didn't see
8672 // in test case that client count changed so just resterilizing if that's the case.
8673 size_t nClients;
8674 do {
8675 nClients = a->clients().size();
8676 for (auto obj : a->clients()) {
8677 func(dynamic_cast<RooAbsArg *>(obj));
8678 if (a->clients().size() != nClients) {
8679 break; // means sterilizing a client changed our clients, so don't trust the client iterator at this
8680 // point
8681 }
8682 }
8683 } while (a->clients().size() != nClients);
8684 };
8685 func(get<RooAbsArg>());
8686}
8687
8688// observables not in the axisVars are automatically projected over
8689xRooNode xRooNode::histo(const xRooNode &vars, const xRooNode &fr, bool content, bool errors, bool stack, bool errorsHi,
8690 bool errorsLo, int nErrorToys) const
8691{
8692
8693 if (!vars.fComp && strlen(vars.GetName())) {
8695 }
8696
8697 xRooNode out(TString::Format("%s.histo", GetName()), nullptr, *this);
8698
8699 RooAbsLValue *v = nullptr;
8700 if (vars.empty()) {
8701 // does an integral
8702 out.fComp = std::shared_ptr<TH1>(
8703 BuildHistogram(nullptr, !content, errors, -1, -1, fr, errorsHi, errorsLo, nErrorToys, nullptr, !stack, false));
8704 } else if (vars.size() == 1) {
8705 v = vars.at(0)->get<RooAbsLValue>();
8706 out.fComp = std::shared_ptr<TH1>(
8707 BuildHistogram(v, !content, errors, 1, 0, fr, errorsHi, errorsLo, nErrorToys, nullptr, !stack, true));
8708 } else {
8709 throw std::runtime_error("multi-dim histo not yet supported");
8710 }
8711
8712 return out;
8713}
8714
8716{
8717 return xRooNode(fComp, xRooNode(range.GetName(), nullptr, *this));
8718}
8719
8721 bool errorsHi, bool errorsLo, int nErrorToys, TH1 *templateHist, bool nostack,
8722 bool setInterp) const
8723{
8724 auto rar = get<RooAbsReal>();
8725 if (!rar) {
8726 if (get<TH1>()) {
8727 return dynamic_cast<TH1 *>(get()->Clone());
8728 }
8729 return nullptr;
8730 }
8731
8732 TObject *vv = rar;
8733
8734 TH1 *h = nullptr;
8735 if (!v) {
8736 if (binStart != -1 || binEnd != -1) { // allow v to stay nullptr if doing integral (binStart=binEnd=-1)
8737 if (auto _ax = GetXaxis())
8738 v = dynamic_cast<RooAbsLValue *>(_ax->GetParent());
8739 } else {
8740 // don't need to integrate if doing a self-histogram
8741 v = dynamic_cast<RooRealVar *>(rar);
8742 }
8743 if (v) {
8744 vv = dynamic_cast<TObject *>(v);
8745 } else {
8746 // make a single-bin histogram of just this value
8747 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
8748 h = new TH1D(rar->GetName(), rar->GetTitle(), 1, 0, 1);
8749 h->GetXaxis()->SetBinLabel(1, rar->GetName());
8750 h->GetXaxis()->SetTimeFormat(rar->GetName());
8751 }
8752 }
8753
8754 auto x = dynamic_cast<RooRealVar *>(v);
8755 bool setTitle = false;
8756 if (templateHist) {
8757 // using template hist for the binning
8758 {
8759 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
8760 h = static_cast<TH1 *>(templateHist->Clone(rar->GetName()));
8761 }
8762 h->SetTitle(rar->GetTitle());
8763 h->Reset();
8764 } else if (x) {
8765 if (x == rar) {
8766 // self histogram ...
8767 {
8768 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
8769 h = new TH1D(rar->GetName(), rar->GetTitle(), 1, 0, 1);
8770 }
8771 h->Sumw2();
8772 h->GetXaxis()->SetBinLabel(1, rar->GetName());
8773 h->SetBinContent(1, rar->getVal());
8774 if (x->getError()) {
8775 h->SetBinError(1, x->getError());
8776 h->SetFillStyle(3005);
8777 h->SetFillColor(h->GetLineColor());
8778 }
8779 h->SetMaximum(x->hasMax() ? x->getMax()
8780 : (h->GetBinContent(1) + std::max(std::abs(h->GetBinContent(1) * 0.1), 50.)));
8781 h->SetMinimum(x->hasMin() ? x->getMin()
8782 : (h->GetBinContent(1) - std::max(std::abs(h->GetBinContent(1) * 0.1), 50.)));
8783 h->GetXaxis()->SetName(dynamic_cast<TObject *>(v)->GetName());
8784 h->SetOption("e2");
8785 h->SetMarkerSize(0);
8786 h->SetMarkerStyle(0);
8787
8788 return h;
8789 }
8790 auto _ax = GetXaxis();
8791 TString binningName = (_ax && _ax->GetParent() == x) ? _ax->GetName() : rar->getStringAttribute("binning");
8792 if (binningName == "")
8793 binningName = rar->GetName();
8794 if (x->hasBinning(binningName)) {
8795 if (x->getBinning(binningName).isUniform()) {
8796 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
8797 h = new TH1D(rar->GetName(), rar->GetTitle(), x->numBins(binningName) <= 0 ? 100 : x->numBins(binningName),
8798 x->getMin(binningName), x->getMax(binningName));
8799 } else {
8800 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
8801 h = new TH1D(rar->GetName(), rar->GetTitle(), x->numBins(binningName), x->getBinning(binningName).array());
8802 }
8803 h->GetXaxis()->SetTitle(x->getBinning(binningName).GetTitle());
8804 setTitle = true;
8805 } else if (auto _boundaries =
8806 _or_func(/*rar->plotSamplingHint(*x,x->getMin(),x->getMax())*/ (std::list<double> *)(nullptr),
8807 rar->binBoundaries(*x, -std::numeric_limits<double>::infinity(),
8808 std::numeric_limits<double>::infinity()));
8809 _boundaries) {
8810 std::vector<double> _bins;
8811 for (auto &b : *_boundaries) {
8812 if (_bins.empty() || std::abs(_bins.back() - b) > 1e-5 * _bins.back())
8813 _bins.push_back(b);
8814 } // found sometimes get virtual duplicates in the binning
8815 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
8816 h = new TH1D(rar->GetName(), rar->GetTitle(), _bins.size() - 1, &_bins[0]);
8817 delete _boundaries;
8818 } else if (!x->hasMax() || !x->hasMin()) {
8819 // use current value of x to estimate range with
8820 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
8821 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(), x->getVal() * 0.2, x->getVal() * 5);
8822 } else {
8823 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
8824 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(), x->getBinning().array());
8825 }
8826 h->Sumw2();
8827 } else if (!h) {
8828 {
8829 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
8830 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(rar->GetName()), 0, v->numBins(rar->GetName()));
8831 }
8832 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(v)) {
8833 int i = 1;
8834 std::map<int, std::string> cats; // fill into a map to preserve index ordering
8835 for (auto &c : *cat) {
8836 cats[c.second] = c.first;
8837 }
8838 for (auto &[_, label] : cats) {
8839 h->GetXaxis()->SetBinLabel(i++, label.c_str());
8840 }
8841 }
8842 h->Sumw2();
8843 }
8844 if (auto o = dynamic_cast<TObject *>(v); o && !setTitle) {
8845 h->GetXaxis()->SetTitle(o->GetTitle());
8846 }
8847
8848 if (v) {
8849 if (h->GetXaxis()->IsAlphanumeric()) {
8850 // store the variable name in the TimeFormat property as well, b.c. alphanumeric requires axis name to be
8851 // "xaxis"
8852 h->GetXaxis()->SetTimeFormat(dynamic_cast<TObject *>(v)->GetName());
8853 } else {
8854 h->GetXaxis()->SetName(dynamic_cast<TObject *>(v)->GetName()); // WARNING: messes up display of bin labels
8855 }
8856 }
8857
8858 if (auto s = styles(nullptr, false); s) {
8859 auto _style = s.get<TStyle>();
8860 static_cast<TAttLine &>(*h) = *_style;
8861 static_cast<TAttFill &>(*h) = *_style;
8862 static_cast<TAttMarker &>(*h) = *_style;
8863 }
8864 if (strlen(h->GetXaxis()->GetTitle()) == 0)
8865 h->GetXaxis()->SetTitle(vv->GetTitle());
8866 auto p = dynamic_cast<RooAbsPdf *>(rar);
8867
8868 // possible speed improvement:
8869 // if(auto spdf = dynamic_cast<RooRealSumPdf*>(p); spdf && spdf->canBeExtended()) {
8870 // p = nullptr; // faster to evaluate sumpdf as a function not a pdf
8871 // }
8872
8873 if (empty && !errors) {
8874 return h;
8875 }
8876
8877 // if (!empty) {
8878
8879 auto _coefs = coefs();
8880
8881 RooFitResult *fr = nullptr;
8882 if (errors) {
8883 // must ensure the fit result we obtain includes pars from coefficients if present
8884 if (_fr.get<RooFitResult>()) {
8885 fr = static_cast<RooFitResult *>(_fr.get<RooFitResult>()->Clone());
8886 } else {
8887 auto frn =
8888 (!_coefs.get() ? *this : xRooNode(RooProduct("tmp", "tmp", RooArgList(*rar, *_coefs.get<RooAbsReal>()))))
8889 .fitResult();
8890 if (strlen(_fr.GetName()))
8891 frn = frn.reduced(_fr.GetName());
8892 fr = dynamic_cast<RooFitResult *>(frn->Clone());
8893 }
8894 if (!GETDMP(fr, _finalPars)) {
8896 }
8897
8898 /// Oct2022: No longer doing this because want to allow fitResult to be used to get partial error
8899 // // need to add any floating parameters not included somewhere already in the fit result ...
8900 // now done in the fitResult() method, where a fit result from the workspace can be automatically extended
8901
8902 TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr, _VM));
8903
8904 if (!prevCov || size_t(fr->covarianceMatrix().GetNcols()) < fr->floatParsFinal().size()) {
8905 TMatrixDSym cov(fr->floatParsFinal().size());
8906 if (prevCov) {
8907 for (int i = 0; i < prevCov->GetNcols(); i++) {
8908 for (int j = 0; j < prevCov->GetNrows(); j++) {
8909 cov(i, j) = (*prevCov)(i, j);
8910 }
8911 }
8912 }
8913 int i = 0;
8914 for (auto &p2 : fr->floatParsFinal()) {
8915 if (!prevCov || i >= prevCov->GetNcols()) {
8916 cov(i, i) = pow(dynamic_cast<RooRealVar *>(p2)->getError(), 2);
8917 }
8918 i++;
8919 }
8920 int covQualBackup = fr->covQual();
8923 }
8924
8925 if (v) {
8926 // need to remove v from result as we are plotting as function of v
8927 if (auto _p = fr->floatParsFinal().find(dynamic_cast<TObject *>(v)->GetName()); _p) {
8929 _pars.remove(*_p, true);
8931 int covQualBackup = fr->covQual();
8934 const_cast<RooArgList &>(fr->floatParsFinal())
8935 .remove(*_p, true); // NOTE: I think this might be a memory leak, should delete _p after removal
8936 }
8937 }
8938 // finally check at least one float has errors defined (might not be cause if in prefit state)
8939 bool hasErrors = false;
8940 for (auto pp : fr->floatParsFinal()) {
8941 if (dynamic_cast<RooRealVar *>(pp)->hasError()) {
8942 hasErrors = true;
8943 break;
8944 }
8945 }
8946 if (!hasErrors) {
8947 errors = false;
8948 delete fr;
8949 }
8950 }
8951
8953 if (v)
8954 normSet.add(*dynamic_cast<RooAbsArg *>(v));
8955
8956 if (binEnd == 0)
8957 binEnd = h->GetNbinsX();
8958
8959 bool needBinWidth = false;
8960 // may have MULTIPLE coefficients for the same pdf!
8961
8962 if (x && (p || _coefs.get() || rar->getAttribute("density"))) {
8963 // pdfs of samples embedded in a sumpdf (aka have a coef) will convert their density value to a content
8964 needBinWidth = true;
8965 }
8966
8967 if (auto spdf = dynamic_cast<RooRealSumPdf *>(p);
8968 spdf && spdf->canBeExtended() && !spdf->getFloor() && !_coefs.get()) {
8969 p = nullptr; // if pdf has no floor, will evaluate it as a function to allow it to be negative - evaluation should
8970 // also be faster (no integral)
8971 // exception is if RooRealSumPdf is embedded in a RooAddPdf (detected by presence of coefs) ... then it must be
8972 // evaluated as a pdf technically should check parent is a RooAddPdf, because if was inside a RooRealSumPdf then
8973 // would be evaluated as a function!
8974 }
8975
8976 // check if we need to do any projecting of other observables
8977 RooAbsReal *oldrar = nullptr;
8978 auto _obs = obs();
8979
8980 for (auto o : _obs) {
8981 if (auto rr = o->get<RooRealVar>(); rr && rr->hasRange("coordRange")) {
8982 rr->removeMin();
8983 rr->removeMax(); // rr->removeRange("coordRange"); // doesn't actually remove, just sets to
8984 // -inf->+inf
8985 rr->setStringAttribute("coordRange", nullptr); // removes the attribute
8986 }
8987 }
8988 // probably should also remove any range on the x-axis variable too, if there is one
8989 if (auto rr = dynamic_cast<RooRealVar *>(v); rr && rr->hasRange("coordRange")) {
8990 rr->removeMin();
8991 rr->removeMax(); // rr->removeRange("coordRange"); // doesn't actually remove, just sets to
8992 // -inf->+inf
8993 rr->setStringAttribute("coordRange", nullptr); // removes the attribute
8994 }
8995 coords(); // loads current coordinates and populates coordRange, if any
8996
8997 if (auto a = dynamic_cast<RooAbsArg *>(v))
8998 _obs.get<RooArgList>()->remove(*a);
8999 if (!_obs.get<RooArgList>()->empty()) {
9000 oldrar = rar;
9001 normSet.add(*_obs.get<RooArgList>());
9002 // check if any obs are restricted range
9003 bool hasRange = false;
9004 for (auto o : normSet) {
9005 if (auto rr = dynamic_cast<RooRealVar *>(o);
9006 rr && (rr->getStringAttribute("coordRange")) && strlen(rr->getStringAttribute("coordRange"))) {
9007 hasRange = true;
9008 break;
9009 }
9010 }
9011 if (p) {
9012 // need to handle special case of RooSimultaneous ... each pdf needs individually projecting over just its
9013 // dependent obs
9014 if (auto s = dynamic_cast<RooSimultaneous *>(p)) {
9015 auto newrar = new RooSimultaneous("projSim", "projSim", const_cast<RooAbsCategoryLValue &>(s->indexCat()));
9016 for (auto pdf : bins()) {
9017 // auto _pdf =
9018 // pdf->get<RooAbsPdf>()->createProjection(*pdf->get<RooAbsPdf>()->getObservables(*_obs.get<RooArgList>()));
9019 std::unique_ptr<RooArgSet> projObs{pdf->get<RooAbsPdf>()->getObservables(*_obs.get<RooArgList>())};
9020 auto _pdf = new xRooProjectedPdf(TString::Format("%s_projection", pdf->GetName()), "",
9021 *pdf->get<RooAbsPdf>(), *projObs);
9022 if (hasRange) {
9023 dynamic_cast<RooAbsPdf *>(_pdf)->setNormRange("coordRange");
9024 }
9025 newrar->addPdf(*_pdf, pdf->coords()[s->indexCat().GetName()]->get<RooCategory>()->getLabel());
9026 }
9027 rar = newrar;
9028 } else {
9029 rar = p->createProjection(
9030 *_obs.get<RooArgList>()); // TODO should use xRooProjectedPdf here too, because not fixed range and
9031 // extend behaviour of RooProjectedPdf in ROOT yet
9032 if (hasRange) {
9033 dynamic_cast<RooAbsPdf *>(rar)->setNormRange("coordRange");
9034 }
9035 }
9036 if (hasRange)
9037 p->setNormRange("coordRange"); // should get cleared when we sterilize
9038 } else {
9039 if (hasRange) {
9040 // commented out passing of normset so that getVal of non-pdf is always a 'raw' value (needed for raw eval
9041 // of RooRealSumPdf)
9042 rar = std::unique_ptr<RooAbsReal>{rar->createIntegral(
9043 *_obs.get<RooArgList>(),
9044 /*RooFit::NormSet(normSet),*/ RooFit::Range("coordRange"))}
9045 .release();
9046 } else {
9047 rar =
9048 std::unique_ptr<RooAbsReal>{rar->createIntegral(*_obs.get<RooArgList>() /*, RooFit::NormSet(normSet)*/)}
9049 .release();
9050 }
9051 }
9052 }
9053
9054 bool scaleExpected = (p && p->canBeExtended() && !_coefs.get());
9055 // Note about above: if pdf has coefficients then its embedded in a RooAddPdf that has coefs defined ...
9056 // in this case we should *not* scale by expected, since the coefs become the scaling instead
9057 // we should also not build a stack for this (may be a RooRealSumPdf inside a RooAddPdf, but the
9058 // samples of the RooRealSumPdf wont be correctly scaled to line up with overall RooRealSumPdf
9059 // which will be normalized to its coefficient
9060 if (!nostack && p && p->canBeExtended() && _coefs.get()) {
9061 nostack = true;
9062 // if wanted to still hve a stack, would need to scale the stack subcomponents by
9063 // coefs-value / p_integral(raw) ... since raw p-integral will be what stack integrates to
9064 }
9065
9066 std::unique_ptr<RooArgSet> snap(normSet.snapshot());
9068 std::vector<double> lapTimes;
9069 bool warned = false;
9070 if (binStart == -1 && binEnd == -1) {
9071 binEnd = 1;
9072 }
9073 auto cat = (!x) ? dynamic_cast<RooAbsCategoryLValue *>(v) : nullptr;
9074 RooArgList *errorPars = nullptr;
9075 std::unique_ptr<RooAbsCollection> errorParsSnap;
9076
9077 if (!v) {
9078 setInterp = false;
9079 }
9080
9081 if (setInterp) {
9082 RooAbsArg *vvv = dynamic_cast<RooAbsArg *>(v);
9083 // determining if histogram should have interpolation drawing options set on it
9084 // need to strip namespace to discount the "HistFactory" namespace classes from all being treated as binned
9085 TString clNameNoNamespace = rar->ClassName();
9087 setInterp = (clNameNoNamespace.Contains("Hist") || vvv->isCategory() || rar->isBinnedDistribution(*vvv) ||
9088 h->GetNbinsX() == 1 || rar->getAttribute("BinnedLikelihood") ||
9089 (dynamic_cast<RooAbsRealLValue *>(vvv) &&
9090 std::unique_ptr<std::list<double>>(rar->binBoundaries(*dynamic_cast<RooAbsRealLValue *>(vvv),
9091 -std::numeric_limits<double>::infinity(),
9092 std::numeric_limits<double>::infinity()))))
9093 ? false
9094 : true;
9095 if (auto d = dynamic_cast<RooHistFunc *>(rar); d && !d->isBinnedDistribution(*vvv) && h->GetNbinsX() != 1) {
9096 setInterp = true; // hist func is interpolated, so draw it as such
9097 }
9098 if (setInterp && !components().empty()) {
9099 // check if all components of dOpt are "Hist" type (CMS model support)
9100 // if so then don't interp;
9101 bool allHist = true;
9102 for (auto &s : components()) {
9103 TString _clName = s->get()->ClassName();
9104 _clName = _clName(_clName.Last(':') + 1, _clName.Length());
9105 if (!(s->get() && _clName.Contains("Hist"))) {
9106 allHist = false;
9107 break;
9108 }
9109 }
9110 if (allHist)
9111 setInterp = false;
9112 }
9113 if (setInterp) {
9114 h->SetOption("l"); // does linear interpolation between points
9115 }
9116 }
9117
9118 if (errors) {
9119 // may be computing potentially asymmetric errors
9120 // the main histogram will be the error band, and the nominal histogram will be added as a function
9121 // so that it is drawn over the top of the error band
9122 // note that this means GetBinContent on returned histogram will return midpoint of the up and down error
9123 auto l = static_cast<TH1 *>(h->Clone("nominal"));
9124 l->SetDirectory(0);
9125 l->SetFillStyle(0);
9126 h->GetListOfFunctions()->Add(l, (setInterp) ? "lsame" : "histsame");
9127 h->SetOption(setInterp ? "e3" : "e2"); // default draw option E2 or E3 so error band shown .. could have used
9128 // 'EX0' to draw "classic style"
9129 // could take this from the 'band' style object if we create one in future?
9130 h->SetMarkerSize(0);
9131 h->SetFillStyle(3005);
9132 h->SetFillColor(h->GetLineColor());
9133 }
9134
9135 if (nErrorToys > 0) {
9136 errors = false; // wont evaluate error on each toy, will estimate for std.dev or normiles of toys
9137 // need list of errorPars
9138 auto allPars =
9139 (!_coefs.get() ? *this : xRooNode(RooProduct("tmp", "tmp", RooArgList(*rar, *_coefs.get<RooAbsReal>()))))
9140 .pars();
9141 errorPars = new RooArgList; // will be in same order as appear in fr.
9142 for (auto a : fr->floatParsFinal()) {
9143 if (auto par = allPars.get<RooArgList>()->find(*a)) {
9144 errorPars->add(*par);
9145 }
9146 }
9147 errorParsSnap.reset(errorPars->snapshot());
9148 auto l = static_cast<TH1 *>(h->Clone("toys"));
9149 l->Reset(); // removes any functions
9150 l->SetDirectory(0);
9151 h->GetListOfFunctions()->Add(
9152 l, "histsame"); // ensures just this empty hist will be drawn, and not each individual toy
9153
9154 if (errorsLo || errorsHi)
9155 empty = false; // must not be empty b.c. calculation of error relies on knowing nominal (see after loop)
9156 }
9157
9158 std::vector<int> errorBins; // bins (in-range) for which to compute the propagated error band, filled below
9159 for (int toy = 0; toy < (nErrorToys + 1); toy++) {
9160
9161 TH1 *main_h = h;
9162 if (toy > 0) {
9163 h = static_cast<TH1 *>(main_h->Clone(TString::Format("toy_%d", toy)));
9164 h->SetDirectory(0);
9165 h->Reset();
9166 static_cast<TH1 *>(main_h->GetListOfFunctions()->FindObject("toys"))->GetListOfFunctions()->Add(h);
9167 // randomize the parameter values according to the fr's covariance matrix
9168 errorPars->assignValueOnly(fr->randomizePars());
9169 // if any par has 0 error, randomizePars can end up assigning a nan, so replace
9170 // all zero errors with value
9171 for (auto pp : fr->floatParsFinal()) {
9172 auto _vv = dynamic_cast<RooRealVar *>(pp);
9173 if (!_vv)
9174 continue;
9175 if (_vv->getError() == 0)
9176 errorPars->setRealValue(pp->GetName(), _vv->getVal());
9177 }
9178 }
9179
9180 for (int i = std::max(1, binStart); i <= std::min(h->GetNbinsX(), binEnd); i++) {
9181 timeIt.Start(true);
9182 if (x) {
9183 x->setVal(h->GetBinCenter(i));
9184 } else if (cat) {
9185 cat->setLabel(h->GetXaxis()->GetBinLabel(i)); // because order might not match "binning" order
9186 } else if (v) {
9187 v->setBin(i - 1);
9188 }
9189 if (x && !x->inRange("coordRange"))
9190 continue;
9191
9192 double r = 0;
9193 if (!empty || toy > 0) {
9194 r = /*(p && p->selfNormalized())*/ rar->getVal(p ? &normSet : nullptr);
9195#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
9196 if (std::isnan(r) && RooNaNPacker::isNaNWithPayload(r)) {
9198 }
9199#endif
9200 if (r && _coefs.get()) {
9201 r *= _coefs.get<RooAbsReal>()->getVal(normSet);
9202 }
9203 if (needBinWidth) {
9204 r *= h->GetBinWidth(i);
9205 }
9206 if (scaleExpected) {
9207 // std::cout << r << " exp = " << p->expectedEvents(normSet) << " for normRange " << (p->normRange() ?
9208 // p->normRange() : "null") << std::endl; p->Print();rar->Print();
9209 r *= (p->expectedEvents(normSet));
9210 } // do in here in case dependency on var
9211 }
9212 h->SetBinContent(i, r);
9213
9214 if (errors) {
9215 static_cast<TH1 *>(h->FindObject("nominal"))->SetBinContent(i, r); // transfer nominal to nominal hist
9216 // Record for which bins to compute the error later.
9217 if (toy == 0) {
9218 errorBins.push_back(i);
9219 }
9220 }
9221 timeIt.Stop();
9222 lapTimes.push_back(timeIt.RealTime());
9223 double time_estimate =
9224 (lapTimes.size() > 1)
9225 ? (h->GetNbinsX() * (std::accumulate(lapTimes.begin() + 1, lapTimes.end(), 0.) / (lapTimes.size() - 1)))
9226 : 0.;
9227 if (!warned && (lapTimes.at(0) > 10 || (lapTimes.size() > 2 && time_estimate > 60.))) {
9228 TTimeStamp t2;
9229 t2.Add(time_estimate);
9230 Warning("BuildHistogram", "Building this histogram will take until %s", t2.AsString());
9231 if (errors) {
9232 // install interrupt handler
9233 runningNode = this;
9235 }
9236 warned = true;
9237 }
9238 if (fInterrupted) {
9239 if (errors) {
9240 Warning("BuildHistogram", "Skipping errors for remaining bins");
9241 errors = false;
9242 }
9243 fInterrupted = false;
9244 }
9245 }
9246 if (toy > 0) {
9247 h = main_h;
9248 }
9249 }
9250
9251 if (errors && !errorBins.empty()) {
9252 // Batched linear error propagation over all (in-range) bins recorded above. By varying each parameter only
9253 // once and sweeping the observable inside that variation, the normalization / expected-events integrals are
9254 // reused across bins instead of being recomputed for every bin (see new_getPropagatedErrors).
9255 std::unique_ptr<RooAbsReal> errFunc;
9257 if (p) {
9258 errFunc =
9259 std::make_unique<PdfWrapper>((oldrar) ? *rar : *p, _coefs.get<RooAbsReal>(), !v, oldrar ? p : nullptr);
9260 } else {
9261 // especially important not to pass a normSet in the case we are evaluating a RooRealSumPdf as a function!
9262 // otherwise the error will be wrong
9263 errFunc = std::make_unique<RooProduct>(
9264 "errorEval", "errorEval",
9265 RooArgList(*rar, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>()));
9266 }
9268
9269 auto setBin = [&](int i) {
9270 if (x) {
9271 x->setVal(h->GetBinCenter(i));
9272 } else if (cat) {
9273 cat->setLabel(h->GetXaxis()->GetBinLabel(i));
9274 } else if (v) {
9275 v->setBin(i - 1);
9276 }
9277 };
9278
9279 bool doAsym = (errorsHi && errorsLo);
9280 // For asymmetric bands we need both the Lo and Hi propagated errors; otherwise a single (possibly one-sided)
9281 // pass. errorPars is reused across both passes so the parameter list is only built once.
9282 std::vector<double> errLo = new_getPropagatedErrors(
9283 *errFunc, *fr, errNormSet, &errorPars, doAsym ? false : errorsHi, doAsym ? true : errorsLo, errorBins, setBin);
9284 std::vector<double> errHi;
9285 if (doAsym) {
9286 errHi = new_getPropagatedErrors(*errFunc, *fr, errNormSet, &errorPars, true, false, errorBins, setBin);
9287 }
9288#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
9289 if (p) {
9290 // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
9291 p->_normSet = nullptr;
9292 }
9293#endif
9294
9295 for (size_t k = 0; k < errorBins.size(); k++) {
9296 int i = errorBins[k];
9297 double resLo = errLo[k];
9298 if (needBinWidth)
9299 resLo *= h->GetBinWidth(i);
9300 h->SetBinError(i, resLo);
9301 if (doAsym) {
9302 double resHi = errHi[k];
9303 if (needBinWidth)
9304 resHi *= h->GetBinWidth(i);
9305 // lowVal = content - resLo, highVal = content + resHi
9306 // => band/2 = (resHi+resLo)/2 and band-mid = content + (resHi-resLo)/2
9307 h->SetBinContent(i, h->GetBinContent(i) + (resHi - resLo) * 0.5);
9308 h->SetBinError(i, (resHi + resLo) * 0.5);
9309 }
9310 }
9311 }
9312
9313 if (gOldHandlerr) {
9315 gOldHandlerr = nullptr;
9316 }
9317 normSet = *snap;
9318
9319 if (errorPars) {
9320 if (errorParsSnap)
9322 delete errorPars;
9323 }
9324 if (nErrorToys) {
9325 // compute main histogram error bar from toys
9326 // if not doing asymmetric, then will display std.dev
9327 // otherwise will copy main to nominal and make main error bar s.t. it shows +/-1sigma vals
9328 if (errorsLo && errorsHi) {
9329 auto nomHist = static_cast<TH1 *>(h->FindObject("nominal"));
9330 nomHist->Add(h);
9331 }
9332 for (int i = 1; i <= h->GetNbinsX(); i++) {
9333 std::vector<double> vals;
9334 vals.reserve(nErrorToys);
9335 for (int j = 1; j < (nErrorToys + 1); j++) {
9336 vals.push_back(
9337 static_cast<TH1 *>(h->FindObject("toys")->FindObject(TString::Format("toy_%d", j)))->GetBinContent(i));
9338 }
9339 double upVal, downVal;
9340 if (errorsLo || errorsHi) {
9341 std::sort(vals.begin(), vals.end());
9342 upVal = vals.at(std::floor(vals.size() * ROOT::Math::gaussian_cdf(1)));
9343 downVal = vals.at(std::floor(vals.size() * ROOT::Math::gaussian_cdf(-1)));
9344 if (!errorsLo)
9345 downVal = 2. * h->GetBinContent(i) - upVal;
9346 if (!errorsHi)
9347 upVal = 2. * h->GetBinContent(i) - downVal;
9348 } else {
9349 double err = TMath::StdDev(vals.begin(), vals.end());
9350 upVal = h->GetBinContent(i) + err;
9351 downVal = h->GetBinContent(i) - err;
9352 }
9353 h->SetBinContent(i, (upVal + downVal) * 0.5);
9354 h->SetBinError(i, (upVal - downVal) * 0.5);
9355 }
9356 }
9357
9358 if (oldrar) {
9359 std::vector<RooAbsArg *> extra;
9360 if (auto s = dynamic_cast<RooSimultaneous *>(rar)) {
9361 // need to delete all the subpdfs we created too
9362 for (auto _pdf : s->servers()) {
9363 if (dynamic_cast<RooAbsPdf *>(_pdf))
9364 extra.push_back(_pdf);
9365 }
9366 }
9367 extra.push_back(rar);
9368 rar = oldrar;
9369 xRooNode(*rar).sterilize(); // need to clear the cache of the created integral - do this before deleting things!
9370 for (auto a : extra)
9371 delete a;
9372 } else {
9373 sterilize(); // needed to forget about the normSet that was passed to getVal()
9374 }
9375
9376 if (!p && !rar->getAttribute("density") && !needBinWidth) {
9377 h->GetYaxis()->SetTitle(rar->getStringAttribute("units"));
9378 } else if ((p && p->canBeExtended()) || (!p && needBinWidth)) {
9379 h->GetYaxis()->SetTitle("Events");
9380 } else {
9381 h->GetYaxis()->SetTitle("Probability Mass");
9382 }
9383 h->GetYaxis()->SetMaxDigits(3);
9384
9385 if (errors) {
9386 delete fr;
9387 }
9388
9389 // build a stack unless not requested
9390 if (!nostack) {
9391 // need to draw copy of hist so shown over the stack
9392 auto hCopy = static_cast<TH1 *>(h->Clone("copy"));
9393 hCopy->Reset();
9394 hCopy->Add(h); // use Reset and Add to clear the function list (dont clear directly as may double-delete if same
9395 // object added twice)
9396 hCopy->SetStats(false);
9397 h->GetListOfFunctions()->Add(hCopy, TString(h->GetOption()) + "same");
9398 h->GetListOfFunctions()->Add(hCopy, "axissame"); // prevents stack covering axis
9399 TString dOpt = (setInterp) ? "LF2" : ""; // should become lf2 if interpolation of histogram is appropriate
9400
9401 const xRooNode *rarNode = this;
9402 RooAbsReal *sf = nullptr;
9403 if (get()->InheritsFrom("RooExtendPdf")) {
9404 const_cast<xRooNode *>(this)->browse();
9405 rarNode = find(".pdf").get();
9406 // rar = rarNode->get<RooAbsReal>();
9407 sf = find(".n")->get<RooAbsReal>();
9408 }
9409
9410 THStack *stack = new THStack("stack", TString::Format("%s;%s", rar->GetTitle(), h->GetXaxis()->GetTitle()));
9411 int count = 0;
9412 std::map<std::string, int> colorByTitle; // TODO: should fill from any existing legend
9413 std::set<std::string> allTitles;
9414 bool titleMatchName = true;
9415 std::map<std::string, TH1 *> histGroups;
9416 std::vector<TH1 *> hhs;
9417 std::set<std::pair<size_t, TH1 *>> ordered_hhs;
9418 std::set<TH1 *> histsWithBadTitles; // these histograms will have their titles autoFormatted
9419
9420 // support for CMS model case where has single component containing many coeffs
9421 // will build stack by setting each coeff equal to 0 in turn, rebuilding the histogram
9422 // the difference from the "full" histogram will be the component
9424 if (!rarNode->components().empty()) {
9425 auto comps = rarNode->components()[0];
9426 for (auto &c : *comps) {
9427 if (c->fFolder == "!.coeffs" || c->fFolder == "!.coeffpars")
9428 cms_coefs.add(*c->get<RooAbsArg>());
9429 }
9430 }
9431 if (!cms_coefs.empty()) {
9432 RooRealVar zero("zero", "", 0);
9433 std::shared_ptr<TH1> prevHist(static_cast<TH1 *>(h->Clone()));
9434 prevHist->Reset();
9435 prevHist->Add(h);
9436 // Nov25: discovered cms CMSHistSum has really heavy caching, that wont clear until I clone
9437 // i.e. I must first copy the function, then redirect the server, then copy that function
9438 // the copy of the copy will give the correct results, not the copy
9439 // This wasn't necessary for the CMSHistErrorPropagator class that I had before.
9440 std::unique_ptr<RooAbsReal> forig(
9441 dynamic_cast<RooAbsReal *>(rarNode->components()[0]->get()->Clone("tmpCopy0")));
9442 for (auto c : cms_coefs) {
9443 // seems I have to remake the function each time, as haven't figured out what cache needs clearing?
9444
9445 zero.setAttribute(Form("ORIGNAME:%s", c->GetName())); // used in redirectServers to say what this replaces
9446 forig->redirectServers(RooArgSet(zero), false, true); // each time will replace one additional coef
9447 std::unique_ptr<RooAbsReal> f(dynamic_cast<RooAbsReal *>(forig->Clone("tmpCopy")));
9448
9449 // zero.setAttribute(Form("ORIGNAME:%s",c->GetName()),false); (commented out so that on next iteration
9450 // will still replace all prev)
9451 auto hh = xRooNode(*f, *this).BuildHistogram(v);
9452 hh->SetName(c->GetName());
9453 if (sf)
9454 hh->Scale(sf->getVal());
9455 if (strlen(hh->GetTitle()) == 0) {
9456 hh->SetTitle(c->GetName()); // ensure all hists has titles
9457 // special case for CMS ... if find "_proc_" in the title, take whatever is after that
9458 auto idx = TString(hh->GetTitle()).Index("_proc_");
9459 if (idx >= 0) {
9460 hh->SetTitle(TString(TString(hh->GetTitle())(idx + 6, strlen(hh->GetTitle()))));
9461 }
9462 histsWithBadTitles.insert(hh);
9463 } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
9464 histsWithBadTitles.insert(hh);
9465 }
9466 titleMatchName &= (TString(c->GetName()) == hh->GetTitle() ||
9467 TString(hh->GetTitle()).BeginsWith(TString(c->GetName()) + "_"));
9468 std::shared_ptr<TH1> nextHist(static_cast<TH1 *>(hh->Clone()));
9469 hh->Add(prevHist.get(), -1.);
9470 hh->Scale(-1.);
9471 // remove the errors ... the above lines will have introduced errors
9472 hh->TH1::Reset("ICE"); // calling the base class method explicitly will only clear errors
9473 ordered_hhs.insert(std::pair(ordered_hhs.size(), hh));
9475 }
9476 } else if (get<RooSimultaneous>()) {
9477 // need to create a histogram for each sample across all the channels - will rely on functionality below to
9478 // merge them based on titles
9479
9480 for (auto &chan : bins()) {
9481 TString chanName(chan->GetName());
9482 chanName = chanName(chanName.Index("=") + 1, chanName.Length());
9483 auto samps = chan->mainChild();
9484 if (!samps)
9485 samps = *chan;
9486 for (auto &samp : samps.components()) {
9487 auto hh = static_cast<TH1 *>(h->Clone(samp->GetName()));
9488 hh->Reset();
9489 hh->SetTitle(samp->GetTitle());
9490 if (strlen(hh->GetTitle()) == 0) {
9491 hh->SetTitle(samp->GetName());
9492 histsWithBadTitles.insert(hh);
9493 } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
9494 histsWithBadTitles.insert(hh);
9495 }
9496 hh->SetTitle(TString(hh->GetTitle())
9497 .ReplaceAll(TString(chan->get()->GetName()) + "_",
9498 "")); // remove occurance of channelname_ in title (usually prefix)
9499 titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
9500 TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
9501 hh->SetBinContent(hh->GetXaxis()->FindFixBin(chanName), samp->GetContent());
9502 ordered_hhs.insert(std::pair(ordered_hhs.size(), hh));
9503 }
9504 }
9505 } else {
9506 for (auto &samp : rarNode->components()) {
9507 auto hh = samp->BuildHistogram(
9508 v, empty, false /* no errors for stack*/, binStart, binEnd, _fr, false, false, 0, h, true,
9509 setInterp); // passing h to ensure binning is the same for all subcomponent hists
9510 hh->SetName(samp->GetName());
9511 if (sf)
9512 hh->Scale(sf->getVal());
9513 ordered_hhs.insert(
9514 std::pair((samp->get<RooAbsArg>() && samp->get<RooAbsArg>()->getStringAttribute("StackOrder"))
9515 ? TString(samp->get<RooAbsArg>()->getStringAttribute("StackOrder")).Atoi()
9516 : ordered_hhs.size(),
9517 hh));
9518 if (strlen(hh->GetTitle()) == 0) {
9519 hh->SetTitle(samp->GetName()); // ensure all hists has titles
9520 histsWithBadTitles.insert(hh);
9521 } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
9522 histsWithBadTitles.insert(hh);
9523 }
9524 titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
9525 TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
9526 }
9527 }
9528
9529 // pull histograms in their order
9530 for (auto &[_, hh] : ordered_hhs) {
9531 hhs.push_back(hh);
9532 }
9533
9534 if (!hhs.empty()) {
9535 for (auto &hh : hhs) {
9536 allTitles.insert(hh->GetTitle());
9537 }
9538
9539 // get common prefix to strip off only if all titles match names and
9540 // any title is longer than 10 chars
9541 size_t e = std::min(allTitles.begin()->size(), allTitles.rbegin()->size());
9542 size_t ii = 0;
9543 bool goodPrefix = false;
9544 std::string commonSuffix;
9545 if (titleMatchName && hhs.size() > 1) {
9546 while (ii < e - 1 && allTitles.begin()->at(ii) == allTitles.rbegin()->at(ii)) {
9547 ii++;
9548 if (allTitles.begin()->at(ii) == '_' || allTitles.begin()->at(ii) == ' ')
9549 goodPrefix = true;
9550 }
9551
9552 // find common suffix if there is one .. must start with a "_"
9553 bool stop = false;
9554 while (!stop && commonSuffix.size() < size_t(e - 1)) {
9555 commonSuffix = allTitles.begin()->substr(allTitles.begin()->length() - commonSuffix.length() - 1);
9556 for (auto &tt : allTitles) {
9557 if (!TString(tt).EndsWith(commonSuffix.c_str())) {
9558 commonSuffix = commonSuffix.substr(1);
9559 stop = true;
9560 break;
9561 }
9562 }
9563 }
9564 if (commonSuffix.find('_') == std::string::npos) {
9565 commonSuffix = "";
9566 } else {
9567 commonSuffix = commonSuffix.substr(commonSuffix.find('_'));
9568 }
9569 }
9570 if (!goodPrefix)
9571 ii = 0;
9572
9573 // also find how many characters are needed to distinguish all entries (that dont have the same name)
9574 // then carry on up to first space or underscore
9575 size_t jj = 0;
9576 std::map<std::string, std::string> reducedTitles;
9577 while (reducedTitles.size() != allTitles.size()) {
9578 jj++;
9579 std::map<std::string, int> titlesMap;
9580 for (auto &s : allTitles) {
9581 if (reducedTitles.count(s))
9582 continue;
9583 titlesMap[s.substr(0, jj)]++;
9584 }
9585 for (auto &s : allTitles) {
9586 if (titlesMap[s.substr(0, jj)] == 1 && (jj >= s.length() || s.at(jj) == ' ' || s.at(jj) == '_')) {
9587 reducedTitles[s] = s.substr(0, jj);
9588 }
9589 }
9590 }
9591
9592 // strip common prefix and suffix before adding
9593 for (auto ritr = hhs.rbegin(); ritr != hhs.rend(); ++ritr) { // go in reverse order
9594 if (!histsWithBadTitles.count((*ritr))) {
9595 continue;
9596 }
9597 auto _title = (hhs.size() > 5) ? reducedTitles[(*ritr)->GetTitle()] : (*ritr)->GetTitle();
9598 _title = _title.substr(ii < _title.size() ? ii : 0);
9599 if (!commonSuffix.empty() && TString(_title).EndsWith(commonSuffix.c_str()))
9600 _title = _title.substr(0, _title.length() - commonSuffix.length());
9601 (*ritr)->SetTitle(_title.c_str());
9602 }
9603 }
9604
9605 for (auto &hh : hhs) {
9606 // automatically group hists that all have the same title
9607 if (histGroups.find(hh->GetTitle()) == histGroups.end()) {
9608 histGroups[hh->GetTitle()] = hh;
9609 } else {
9610 // add it into this group
9611 histGroups[hh->GetTitle()]->Add(hh);
9612 delete hh;
9613 hh = nullptr;
9614 continue;
9615 }
9616 auto hhMin = (hh->GetMinimum() == 0) ? hh->GetMinimum(1e-9) : hh->GetMinimum();
9617 if (!stack->GetHists() && h->GetMinimum() > hhMin) {
9618 auto newMin = hhMin - (h->GetMaximum() - hhMin) * gStyle->GetHistTopMargin();
9619 if (hhMin >= 0 && newMin < 0)
9620 newMin = hhMin * 0.99;
9621 // adjustYRange(newMin, h->GetMaximum());
9622 }
9623
9624 /*if(stack->GetHists() && stack->GetHists()->GetEntries()>0) {
9625 // to remove rounding effects on bin boundaries, see if binnings compatible
9626 auto _h1 = dynamic_cast<TH1*>(stack->GetHists()->At(0));
9627 if(_h1->GetNbinsX()==hh->GetNbinsX()) TODO ... finish dealing with silly rounding effects
9628 }*/
9629 TString thisOpt = TString(hh->GetOption()) == "l" ? "LF2" : ""; // need LF2 to get smooth line with fill
9630 // uncomment next line to blend continuous with discrete components .. get some unpleasant "poke through"
9631 // effects though
9632 // if(auto s = samp->get<RooAbsReal>(); s) thisOpt = s->isBinnedDistribution(*dynamic_cast<RooAbsArg*>(v)) ?
9633 // "" : "LF2";
9634 stack->Add(hh, thisOpt);
9635 }
9636 // stack->SetBit(kCanDelete); // should delete its sub histograms
9637 h->GetListOfFunctions()->AddFirst(stack, "noclear same");
9638 // stack->Draw("noclear same");
9639 // h->Draw(
9640 // dOpt + sOpt +
9641 // "same"); // overlay again .. if stack would cover original hist (negative components) we still see
9642 // integral
9643 // h->Draw("axissame"); // redraws axis
9644
9645 TList *ll = stack->GetHists();
9646 if (ll && ll->GetEntries()) {
9647
9648 // finally, ensure all hists are styled
9649 for (auto ho : *ll) {
9650 TH1 *hh = dynamic_cast<TH1 *>(ho);
9651 if (!hh)
9652 continue;
9653 bool createdStyle = (xRooNode(*hh, *this).styles(nullptr, false).get<TStyle>() == nullptr);
9654
9655 if (createdStyle) {
9656 // give hist a color, that isn't the same as any other hists color
9657 hh->SetFillStyle(1001); // solid fill style
9658 bool used = false;
9659 do {
9660 hh->SetFillColor(gEnv->GetValue("XRooFit.MinFillColor", kP10Blue /* was previously 2*/) + (count++));
9661 if (!gROOT->GetColor(hh->GetFillColor())) {
9662 // color doesn't exist, default it to transparent?
9663 hh->SetFillColor(0);
9664 }
9665 // check not already used this color
9666 used = false;
9667 for (auto ho2 : *ll) {
9668 TH1 *hh2 = dynamic_cast<TH1 *>(ho2);
9669 if (!hh2)
9670 continue;
9671 auto _styleNode = xRooNode(*hh2, *this).styles(hh2, false);
9672 auto _style = _styleNode.get<TStyle>();
9673 if (hh != hh2 && _style && _style->GetFillColor() == hh->GetFillColor()) {
9674 used = true;
9675 break;
9676 }
9677 }
9678 } while (used);
9679 }
9680
9681 auto _styleNode = xRooNode(*hh, *this).styles(hh);
9682 if (auto _style = _styleNode.get<TStyle>()) {
9683 *dynamic_cast<TAttLine *>(hh) = *_style;
9684 *dynamic_cast<TAttFill *>(hh) = *_style;
9685 *dynamic_cast<TAttMarker *>(hh) = *_style;
9686 }
9687 // for stacks, fill color of white should be color 10 unless fill style is 0
9688 if (hh->GetFillColor() == kWhite && hh->GetFillStyle() != 0) {
9689 // kWhite means 'transparent' in ROOT ... should really use a FillStyle of 0 for that
9690 // so assume user wanted actual white, which is color 10
9691 hh->SetFillColor(10);
9692 }
9693 }
9694 }
9695 }
9696
9697 return h;
9698}
9699
9700double xRooNode::GetBinData(int bin, const xRooNode &data)
9701{
9702 if (data.get<RooAbsData>()) {
9703 // attach as a child before calling datasets(), so that is included in the list
9704 push_back(std::make_shared<xRooNode>(data));
9705 }
9706 auto node = datasets().find(data.GetName());
9707 if (data.get<RooAbsData>()) {
9708 // remove the child we attached
9709 resize(size() - 1);
9710 }
9711 if (!node)
9712 return std::numeric_limits<double>::quiet_NaN();
9713 return node->GetBinContent(bin);
9714}
9715
9716std::vector<double> xRooNode::GetBinContents(int binStart, int binEnd) const
9717{
9718 if (fBinNumber != -1) {
9719 if (binStart != binEnd || !fParent) {
9720 throw std::runtime_error(TString::Format("%s is a bin - only has one value", GetName()));
9721 }
9722 return fParent->GetBinContents(fBinNumber, fBinNumber);
9723 }
9724 std::vector<double> out;
9725 if (get<RooAbsData>()) {
9726 auto g = BuildGraph(
9727 nullptr,
9728 (binStart != -1 ||
9729 binEnd != -1) /*include points for zeros unless we are asking for a single point with start=end=-1*/);
9730 if (!g) {
9731 return out;
9732 }
9733 if (binStart == binEnd && binStart == -1) {
9734 // integral over all bins if getting bin content -1
9735 double integral(0);
9736 for (int i = 0; i < g->GetN(); i++)
9737 integral += g->GetPointY(i);
9738 out.push_back(integral);
9739 delete g;
9740 return out;
9741 }
9742 for (int i = binStart - 1; i < g->GetN() && (binEnd == 0 || i < binEnd); i++) {
9743 out.push_back(g->GetPointY(i));
9744 }
9745 delete g;
9746 return out;
9747 }
9748
9749 bool doIntegral = false;
9750 if (binStart == binEnd && binStart == -1) {
9751 binStart = -1;
9752 binEnd = -1;
9753 doIntegral = true;
9754 } // return integral if request bin -1
9755 auto h = BuildHistogram(nullptr, false, false, binStart, binEnd);
9756 if (!h) {
9757 throw std::runtime_error(TString::Format("%s has no content", GetName()));
9758 }
9759 if (binEnd == 0) {
9760 binEnd = h->GetNbinsX();
9761 }
9762 if (doIntegral) {
9763 double tot = 0;
9764 for (int i = 1; i <= h->GetNbinsX(); i++) {
9765 tot += h->GetBinContent(i);
9766 }
9767 out.push_back(tot);
9768 } else {
9769 for (int i = binStart; i <= binEnd; i++) {
9770 out.push_back(h->GetBinContent(i));
9771 }
9772 }
9773 delete h;
9774 return out;
9775}
9776
9778{
9779 if (auto a = get<RooAbsArg>(); a) {
9780 // go through servers looking for 'main' thing
9781 for (auto &l : a->servers()) {
9782 if (l->getAttribute("MAIN_MEASUREMENT") || l->InheritsFrom("RooRealSumPdf") || l->InheritsFrom("RooAddPdf")) {
9783 return xRooNode(*l, *this);
9784 }
9785 }
9786 // the main child of a RooProduct is one that has the same name (/alias) as the product (except if is a bin
9787 // factor)
9788 if (a->IsA() == RooProduct::Class() && fBinNumber == -1) {
9789 for (auto &l : factors()) {
9790 if (strcmp(l->GetName(), GetName()) == 0) {
9791 return *l;
9792 }
9793 }
9794 }
9795 }
9796 return xRooNode();
9797}
9798
9800{
9801 if (auto o = get(); o) {
9802 o->Inspect();
9803 } else {
9805 }
9806}
9807
9808bool TopRightPlaceBox(TPad *p, TObject *o, double w, double h, double &xl, double &yb)
9809{
9810#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
9811 // reinitialize collide grid because the filling depends on fUxmin and fUxmax (and ymin ymax too)
9812 // and these aren't filled on the first time we do the placement (they init to 0 and 1), but will be filled
9813 // subsequently
9814 for (int i = 0; i < p->fCGnx; i++) {
9815 for (int j = 0; j < p->fCGny; j++) {
9816 p->fCollideGrid[i + j * p->fCGnx] = true;
9817 }
9818 }
9819 p->FillCollideGrid(o);
9820 Int_t iw = (int)(p->fCGnx * w);
9821 Int_t ih = (int)(p->fCGny * h);
9822
9823 Int_t nxmax = p->fCGnx - iw - 1 - p->fCGnx * p->GetRightMargin();
9824 Int_t nymax = p->fCGny - ih - 1 - p->fCGny * p->GetTopMargin();
9825
9826 for (Int_t j = nymax; j >= 0; j--) {
9827 for (Int_t i = nxmax; i >= 0; i--) {
9828 if (p->Collide(i, j, iw, ih)) {
9829 continue;
9830 } else {
9831 xl = (double)(i) / (double)(p->fCGnx);
9832 yb = (double)(j) / (double)(p->fCGny);
9833 return true;
9834 }
9835 }
9836 }
9837 return false;
9838#else
9839 return p->PlaceBox(o, w, h, xl, yb, "trw");
9840#endif
9841}
9842
9843TPaveText *getPave(const char *name = "labels", bool create = true, bool doPaint = false)
9844{
9845 if (auto p = dynamic_cast<TPaveText *>(gPad->GetPrimitive(name)); p) {
9846 if (doPaint)
9847 gPad->PaintModified(); //-- slows down x11 so trying to avoid
9848 return p;
9849 }
9850 if (!create) {
9851 return nullptr;
9852 }
9853 auto l = new TPaveText(gPad->GetLeftMargin() + 0.02, 1. - gPad->GetTopMargin() - 0.08, 0.6,
9854 1. - gPad->GetTopMargin() - 0.08);
9855 l->SetBorderSize(0);
9856 if (l->GetTextSize() == 0)
9857 l->SetTextSize(gStyle->GetTitleYSize());
9858
9860 // l->SetMargin(0);
9861 l->SetFillStyle(0);
9862 l->SetName(name);
9863 l->Draw();
9864 l->ConvertNDCtoPad();
9865 return l;
9866}
9867
9868TLegend *getLegend(bool create = true, bool doPaint = false)
9869{
9870 if (auto p = dynamic_cast<TLegend *>(gPad->GetPrimitive("legend")); p) {
9871 double x;
9872 double y;
9873 double w = p->GetX2NDC() - p->GetX1NDC();
9874 double h = p->GetY2NDC() - p->GetY1NDC();
9875 if (doPaint)
9876 gPad->PaintModified(); //-- slows down x11 so trying to avoid
9877 if (TopRightPlaceBox(dynamic_cast<TPad *>(gPad), p, w, h, x, y)) {
9878 // squash inside the frame ..
9879 // std::cout << gPad->GetName() << ":" << x << " , " << y << " , " << w << " , " << h << std::endl;
9880 x = std::max(x, (gPad->GetLeftMargin() + 0.02));
9881 y = std::max(y, (gPad->GetBottomMargin() + 0.02));
9882 x = std::min(x, (1. - gPad->GetRightMargin() - 0.02) - w);
9883 y = std::min(y, (1. - gPad->GetTopMargin() - 0.02) - h);
9884 h = std::min(h, (1. - gPad->GetTopMargin() - 0.02) - y);
9885 w = std::min(w, (1. - gPad->GetRightMargin() - 0.02) - x);
9886 // std::cout << gPad->GetName() << ":" << x << " , " << y << " , " << h << " , " << w << std::endl;
9887 p->SetX1NDC(x);
9888 p->SetY1NDC(y);
9889 p->SetX2NDC(x + w);
9890 p->SetY2NDC(y + h);
9891 gPad->Modified();
9892 }
9893 return p;
9894 }
9895 // look for a parent pad called 'legend' and create it there if existing
9896 auto p = gPad;
9897 while ((p != p->GetMother()) && (p = p->GetMother())) {
9898 if (auto q = dynamic_cast<TVirtualPad *>(p->GetPrimitive("legend")); q) {
9899 q->Modified();
9900 p = q;
9901 break;
9902 }
9903 }
9904 auto tmpPad = gPad;
9905 TLegend *l = nullptr;
9906 if (p && strcmp(p->GetName(), "legend") == 0) {
9907 if (l = dynamic_cast<TLegend *>(p->GetPrimitive("legend")); l || !create)
9908 return l;
9909 p->cd();
9910 l = new TLegend(gPad->GetLeftMargin(), 1. - gPad->GetTopMargin(), 1. - gPad->GetRightMargin(),
9911 gPad->GetBottomMargin());
9912 l->SetBorderSize(1); // ensure has a border
9913 } else {
9914 if (!create)
9915 return nullptr;
9916 l = new TLegend(0.6, 1. - gPad->GetTopMargin() - 0.08, 0.75, 1. - gPad->GetTopMargin() - 0.08);
9917 l->SetBorderSize(0);
9918 // legend text will be required to match y-axis
9919 if (l->GetTextSize() == 0) {
9920 l->SetTextSize(gStyle->GetTitleYSize());
9921 l->SetTextFont(gStyle->GetTitleFont("Y"));
9922 }
9923 }
9925 // l->SetMargin(0);
9926 l->SetFillStyle(0);
9927 l->SetName("legend");
9928 l->Draw();
9929 l->ConvertNDCtoPad();
9930 tmpPad->cd();
9931 return l;
9932}
9933
9934std::string formatLegendString(const std::string &s)
9935{
9936 auto i = s.find("\n");
9937 if (i == std::string::npos) {
9938 return s;
9939 }
9940 return std::string("#splitline{") + s.substr(0, i) + "}{" + formatLegendString(s.substr(i + 1)) + "}";
9941}
9942
9943void addLegendEntry(TObject *o, const char *title, const char *opt)
9944{
9945 auto l = getLegend();
9946 if (!l)
9947 return;
9948 // check for entry already existing with same title
9949 for (auto a : *l->GetListOfPrimitives()) {
9950 if (formatLegendString(title) == dynamic_cast<TLegendEntry *>(a)->GetLabel())
9951 return;
9952 }
9953 if (l->GetListOfPrimitives()->GetEntries() > 20)
9954 return; // todo: create an 'other' entry?
9955
9956 auto e = l->AddEntry(o, formatLegendString(title).c_str(), opt);
9957 // move to top of the legend (we add things in at the top)
9958 l->GetListOfPrimitives()->RemoveLast();
9959 l->GetListOfPrimitives()->AddFirst(e);
9960 if (auto nObj = l->GetListOfPrimitives()->GetEntries(); nObj > 0) {
9961 // each entry takes up 0.05 ... maximum of N*(N+4) (where N is # cols) before next column
9962 int nn = l->GetNColumns();
9963 nn *= (nn + 4);
9964 if (nObj > 1 && (nObj % nn) == 1) {
9965 l->SetNColumns(l->GetNColumns() + 1);
9966 if (l->GetBorderSize() == 0) {
9967 l->SetX1NDC(l->GetX2NDC() - 0.15 * l->GetNColumns());
9968 }
9969 }
9970 if (l->GetBorderSize() == 0) {
9971 l->SetY1NDC(l->GetY2NDC() - 0.05 * gPad->GetHNDC() * std::ceil((double(nObj) / l->GetNColumns())));
9972 }
9973 }
9974
9975 getLegend(); // to mark modified
9976}
9977
9978// this exists to avoid calling update excessively because it slows down x11 ... but still
9979// need to call update twice if have a legend drawn in order to relocate it.
9981public:
9982 PadRefresher(TVirtualPad *p) : fPad(p) { nExisting++; }
9984 {
9985 if (fPad) {
9986 getLegend(false, true);
9987 fPad->GetCanvas()->Paint();
9988 fPad->GetCanvas()->Update();
9989#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 30, 00)
9990 fPad->GetCanvas()->ResetUpdated(); // stops previous canvas being replaced in a jupyter notebook
9991#endif
9992 fPad->cd();
9993 }
9994 nExisting--;
9995 }
9996 TVirtualPad *fPad = nullptr;
9997 static int nExisting;
9998};
9999
10001
10003{
10004 // in order to catch exceptions to prevent crash of GUI, do this:
10005 if (gROOT->FromPopUp()) {
10006 gROOT->SetFromPopUp(false);
10007 try {
10008 Draw(opt);
10009 } catch (const std::exception &e) {
10010 new TGMsgBox(
10011 gClient->GetRoot(),
10012 (gROOT->GetListOfBrowsers()->At(0))
10013 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
10014 : gClient->GetRoot(),
10015 "Exception", e.what(),
10016 kMBIconExclamation); // deletes self on dismiss?
10017 }
10018 gROOT->SetFromPopUp(true);
10019 return;
10020 }
10021
10022 TString sOpt2(opt);
10023 sOpt2.ToLower();
10024 if (!get() && !IsFolder() && !sOpt2.Contains("x="))
10025 return;
10026
10027 if (auto mc = get<RooStats::ModelConfig>()) {
10028 xRooNode(*mc->GetPdf(), fParent).Draw(opt); // draw the pdf of the config
10029 } else if (auto ir = get<RooStats::HypoTestInverterResult>()) {
10030 xRooHypoSpace(ir).Draw(opt);
10032 return;
10033 } else if (get<RooStats::HypoTestResult>()) {
10034 if (gPad)
10035 gPad->Clear();
10036 xRooNLLVar::xRooHypoPoint(std::dynamic_pointer_cast<RooStats::HypoTestResult>(fComp)).Draw(opt);
10037 {
10038 PadRefresher p(gPad); // refreshes the pad
10039 }
10041 return;
10042 }
10043
10044 if (sOpt2 == "pcls" && get<RooRealVar>() && fParent && fParent->get<RooAbsPdf>()) {
10045 // use the first selected dataset
10046 auto _dsets = fParent->datasets();
10047 // bool _drawn=false;
10048 TString dsetName = "";
10049 for (auto &d : _dsets) {
10050 if (d->get()->TestBit(1 << 20)) {
10051 dsetName = d->get()->GetName();
10052 break;
10053 }
10054 }
10055 auto hs = fParent->nll(dsetName.Data()).hypoSpace(get<RooRealVar>()->GetName());
10056 hs.limits("cls visualize");
10057 hs.SetName(TUUID().AsString());
10058 if (ws()) {
10059 ws()->import(*hs.result());
10060 }
10061 return;
10062 }
10063
10064 if (auxFunctions.empty()) {
10065 // add the defaults: Ratio and Signif
10067 "Ratio",
10068 [](double a, double b, double) {
10069 if (a == 0)
10070 return 0.;
10071 if (b == 0 && a == 0)
10072 return 1.;
10073 return a / b;
10074 },
10075 true);
10077 "Signif",
10078 [](double n, double b, double sigma) {
10079 double t0 = 0;
10080 if (sigma <= 0.) {
10081 // use simplified expression ...
10082 t0 = 2. * (((n == 0) ? 0 : n * log(n / b)) - (n - b));
10083 } else {
10084 double sigma2 = sigma * sigma;
10085 double b_hathat = 0.5 * (b - sigma2 + sqrt(pow(b - sigma2, 2) + 4 * n * sigma2));
10086 // double s_hat = n - m;
10087 // double b_hat = m;
10088 t0 = 2. * (((n == 0) ? 0 : n * log(n / b_hathat)) + b_hathat - n + pow(b - b_hathat, 2) / (2. * sigma2));
10089 }
10090 if (t0 < 0)
10091 return 0.; // can happen from numerical precision
10092 return (n >= b) ? sqrt(t0) : -sqrt(t0);
10093 },
10094 false);
10095 }
10096
10097 TString sOpt(opt);
10098
10099 RooAbsLValue *v = nullptr;
10100 std::vector<double> xPoints;
10101 if (sOpt2.Contains("x=")) {
10102 // specifying a particular var to scan over ...
10103 int _idx = sOpt2.Index("x=");
10104 int _eidx = sOpt2.Index(';', _idx);
10105 TString varPart = sOpt(_idx + 2, (_eidx < 0 ? sOpt2.Length() : _eidx) - (_idx + 2));
10107 // if varName is of form str(num,num,num) then can infer scan points
10108 if (auto _idx2 = varPart.Index("("); _idx2 > 0) {
10109 varName = varPart(0, _idx2);
10110 TStringToken pattern(TString(varPart(_idx2 + 1, varPart.Length() - _idx2 - 2)), ",");
10111 double min(0);
10112 double max(0);
10113 int nBins = 0;
10114 int ii = 0;
10115 while (pattern.NextToken()) {
10116 TString s = pattern;
10117 if (ii == 0) {
10118 nBins = s.Atoi();
10119 } else if (ii == 1) {
10120 min = s.Atof();
10121 } else if (ii == 2) {
10122 max = s.Atof();
10123 }
10124 ii++;
10125 }
10126 if (nBins > 100)
10127 nBins = 100; // limit scanning to 100 points
10128 if (nBins > 1) {
10129 for (double x = min; x <= max; x += (max - min) / (nBins - 1)) {
10130 xPoints.push_back(x);
10131 }
10132 } else if (nBins == 1)
10133 xPoints.push_back((min + max) / 2.);
10134 }
10135 v = getObject<RooAbsLValue>(varName.Data()).get();
10136 if (!v) {
10137 throw std::runtime_error(TString::Format("Could not find variable %s", varName.Data()));
10138 }
10139 if (xPoints.empty() && !obs().find(varName.Data()) &&
10140 dynamic_cast<RooAbsRealLValue *>(v)) { // will draw obs as regular (e.g. hist)
10141 double tmp = static_cast<RooAbsRealLValue *>(v)->getVal();
10142 for (int i = 0; i < v->numBins(GetName()); i++) {
10143 v->setBin(i, GetName());
10144 xPoints.push_back(static_cast<RooAbsRealLValue *>(v)->getVal());
10145 }
10146 static_cast<RooAbsRealLValue *>(v)->setVal(tmp);
10147 }
10148 sOpt2 = TString(sOpt2(0, _idx)) + sOpt2(_idx + 2 + varPart.Length() + 1, sOpt2.Length());
10149 sOpt = TString(sOpt(0, _idx)) + sOpt(_idx + 2 + varPart.Length() + 1, sOpt.Length());
10150 }
10151 TString forceNames = "";
10152 if (sOpt2.Contains("force")) {
10153 // force plots show how much NLL changes wrt to a change of variables
10154 if (get<RooRealVar>() && fParent && fParent->get<RooAbsPdf>()) {
10155 // assume want force of this parameter from the parent pdf
10156 TString ff = sOpt(sOpt2.Index("force"), sOpt2.Index("force") + 5);
10157 sOpt.ReplaceAll(ff, TString::Format("force%s", get()->GetName()));
10158 fParent->Draw(sOpt);
10159 return;
10160 } else if (get<RooAbsPdf>()) {
10161 // extract the parameter(s) to calculate force for
10162 forceNames = sOpt(sOpt2.Index("force") + 5, sOpt2.Length());
10163 sOpt = sOpt(0, sOpt2.Index("force"));
10164 sOpt2 = sOpt2(0, sOpt2.Index("force"));
10165 } else {
10166 Error("Draw", "Can only compute forces with PDFs");
10167 return; // don't throw because will cause browser to exit if done from there
10168 }
10169 }
10170 bool hasOverlay = sOpt2.Contains("overlay");
10171 TString overlayName = "";
10172 if (hasOverlay) {
10173 // whatever follows overlay is the variation name
10174 overlayName = sOpt(sOpt2.Index("overlay") + 7, sOpt2.Length());
10175 sOpt = sOpt(0, sOpt2.Index("overlay"));
10176 sOpt2 = sOpt2(0, sOpt2.Index("overlay"));
10177 }
10178 if (sOpt2.Contains("ratio") && !sOpt2.Contains("auxratio"))
10179 sOpt += "auxRatio";
10180 if (sOpt2.Contains("significance") && !sOpt2.Contains("auxsignif"))
10181 sOpt += "auxSignif";
10182
10183 std::string auxPlotTitle;
10184 for (auto &[k, _] : auxFunctions) {
10185 if (sOpt.Contains(TString::Format("aux%s", k.c_str()))) {
10186 auxPlotTitle = k;
10187 }
10188 sOpt.ReplaceAll(TString::Format("aux%s", k.c_str()), "");
10189 }
10190
10191 sOpt.ToLower();
10192 sOpt.ReplaceAll("ratio", "");
10193 sOpt.ReplaceAll("significance", ""); // remove old option if still given
10194 bool nostack = sOpt.Contains("nostack");
10195 sOpt.ReplaceAll("nostack", "");
10196 bool hasSame = sOpt.Contains("same");
10197 sOpt.ReplaceAll("same", "");
10198 bool hasGoff = sOpt.Contains("goff");
10199 sOpt.ReplaceAll("goff", "");
10200 bool hasFR = sOpt.Contains("pull") && !get<RooFitResult>();
10201 sOpt.ReplaceAll("pull", "");
10202 bool hasText = sOpt.Contains("text");
10203 bool hasTexte = sOpt.Contains("texte");
10204 bool hasErrorOpt = sOpt.Contains("e");
10205 sOpt.ReplaceAll("e", "");
10206 if (hasTexte) {
10207 sOpt.ReplaceAll("txt", "texte");
10208 } else if (hasText) {
10209 sOpt.ReplaceAll("txt", "text");
10210 }
10211 if (auxPlotTitle == "Signif")
10212 hasErrorOpt = true; // must calculate error to calculate significance
10213 if (hasOverlay)
10214 hasSame = true; // when overlaying must be putting on same
10215
10216 TVirtualPad *pad = gPad;
10217
10218 TH1 *hAxis = nullptr;
10219
10220 auto clearPad = []() {
10221 gPad->Clear();
10222 if (gPad->GetNumber() == 0) {
10223 gPad->SetBottomMargin(gStyle->GetPadBottomMargin());
10224 gPad->SetTopMargin(gStyle->GetPadTopMargin());
10225 gPad->SetLeftMargin(gStyle->GetPadLeftMargin());
10226 gPad->SetRightMargin(gStyle->GetPadRightMargin());
10227 }
10228 // if (gPad == gPad->GetCanvas()) {
10229 // gPad->GetCanvas()->SetCanvasSize( gPad->GetCanvas()->GetWindowWidth() - 4,
10230 // gPad->GetCanvas()->GetWindowHeight() - 28 );
10231 // }
10232 };
10233
10234 if (!hasSame || !pad) {
10235 if (!pad) {
10237 pad = gPad;
10238 }
10239
10240 } else {
10241 // get the histogram representing the axes
10242 hAxis = dynamic_cast<TH1 *>(pad->GetPrimitive("axis"));
10243 if (!hAxis) {
10244 for (auto o : *pad->GetListOfPrimitives()) {
10245 if (hAxis = dynamic_cast<TH1 *>(o); hAxis)
10246 break;
10247 }
10248 }
10249 if (hAxis && !v) {
10250 v = getObject<RooAbsLValue>(hAxis->GetXaxis()->IsAlphanumeric() ? hAxis->GetXaxis()->GetTimeFormatOnly()
10251 : hAxis->GetXaxis()->GetName())
10252 .get();
10253 }
10254 }
10255
10256 if (!hasSame) {
10257 if (gPad != gPad->GetCanvas()) {
10258 gPad->SetName(GetName()); // only rename the pad if its not the parent canvas
10259 }
10260 gPad->SetTitle(GetTitle());
10261 }
10262
10264
10265 auto adjustYRange = [&](double min, double max, TH1 *hh = nullptr, bool symmetrize = false) {
10266 if (!hh)
10267 hh = hAxis;
10268 // give max and min a buffer ...
10269 max += gStyle->GetHistTopMargin() * (max - min);
10270 if (min > 0)
10271 min = std::max(min * 0.9, min - gStyle->GetHistTopMargin() * (max - min));
10272 if (hh) {
10273 double ymin = hh->GetMinimum();
10274 double ymax = hh->GetMaximum();
10275 if (hh->GetMaximumStored() == -1111)
10276 ymax += gStyle->GetHistTopMargin() * (ymax - ymin);
10277 if (hh->GetMinimumStored() == -1111) {
10278 if (gStyle->GetHistMinimumZero() && ymax >= 0) {
10279 ymin = 0;
10280 } else if (ymin < 0) {
10281 ymin -= gStyle->GetHistTopMargin() * (ymax - ymin);
10282 } else {
10283 ymin = std::max(ymin * 0.9, ymin - gStyle->GetHistTopMargin() * (ymax - ymin));
10284 }
10285 // see TGLPlotPainter to complete the mimic, but we leave off here truncating @ 0 if ymax>0
10286 }
10287 // make ymax at least 3x bigger than biggest error if has error
10288 if (hh->GetSumw2()) {
10289 double smallestErrDown3 = -std::numeric_limits<double>::infinity();
10290 double smallestErrUp3 = std::numeric_limits<double>::infinity();
10291 for (int i = 1; i <= hh->GetNbinsX(); i++) {
10292 smallestErrDown3 = std::max(smallestErrDown3, hh->GetBinContent(i) - 3 * hh->GetBinError(i));
10293 smallestErrUp3 = std::min(smallestErrUp3, hh->GetBinContent(i) + 3 * hh->GetBinError(i));
10294 }
10295 max = std::max(max, smallestErrUp3);
10296 min = std::min(min, smallestErrDown3);
10297 }
10298 bool change = false;
10299 if (min < ymin) {
10300 ymin = min;
10301 change = true;
10302 }
10303 if (max > ymax) {
10304 ymax = max;
10305 change = true;
10306 }
10307 if (change) {
10308 // note: unfortunately when user 'unzooms' y axis it resets stored minimum to -1111, so lose range
10309 if (symmetrize) {
10310 double down = hh->GetBinContent(1) - ymin;
10311 double up = ymax - hh->GetBinContent(1);
10312 if (down > up) {
10313 ymax = hh->GetBinContent(1) + down;
10314 } else {
10315 ymin = hh->GetBinContent(1) - up;
10316 }
10317 }
10318 if (hh == hAxis && pad && !pad->GetLogy() && ymin > 0 && (log10(ymax) - log10(max)) >= 3) {
10319 // auto-log the pad
10320 pad->SetLogy();
10321 }
10322 if (hh == hAxis && pad && ymin == 0 && pad->GetLogy()) {
10323 ymin = 1e-2;
10324 }
10325 if (ymin == 0 && ymax > 10)
10326 ymin = 0.1; // adjust min so if user activates log scale it isn't bad
10327 hh->SetMinimum(ymin);
10328 hh->SetMaximum(ymax);
10329 hh->GetYaxis()->Set(1, ymin, ymax);
10330 hh->SetAxisRange(ymin, ymax, "Y");
10331 }
10332 }
10333 };
10334
10335 auto graphMinMax = [](TGraphAsymmErrors *gr) {
10336 double ymax = -std::numeric_limits<double>::infinity();
10337 double ymin = std::numeric_limits<double>::infinity();
10338 for (int i = 0; i < gr->GetN(); i++) {
10339 ymax = std::max(ymax, gr->GetPointY(i) + gr->GetErrorYhigh(i));
10340 ymin = std::min(ymin, gr->GetPointY(i) - gr->GetErrorYlow(i));
10341 }
10342 return std::make_pair(ymin, ymax);
10343 };
10344
10345 if (!xPoints.empty()) {
10346 // create a graph using GetContent
10348 out->SetName(GetName());
10349 out->SetTitle(GetTitle());
10350 out->SetFillColor(out->GetLineColor());
10351 out->SetMarkerStyle(0);
10352 out->SetFillStyle(hasErrorOpt ? 3005 : 0);
10353 double tmp = static_cast<RooAbsRealLValue *>(v)->getVal();
10354 for (auto &x : xPoints) {
10355 static_cast<RooAbsRealLValue *>(v)->setVal(x);
10356 out->AddPoint(x, GetContent());
10357 if (hasErrorOpt) {
10358 out->SetPointEYlow(out->GetN() - 1, GetError());
10359 out->SetPointEYhigh(out->GetN() - 1, out->GetErrorYlow(out->GetN() - 1)); // symmetric error for now
10360 }
10361 }
10362 static_cast<RooAbsRealLValue *>(v)->setVal(tmp);
10363 out->GetHistogram()->GetXaxis()->SetTitle(static_cast<RooAbsRealLValue *>(v)->GetTitle());
10364 out->SetBit(kCanDelete);
10365 out->Draw(TString(hasSame ? "L" : "AL") + (hasErrorOpt ? "3" : ""));
10366 return;
10367 }
10368
10369 if (hasFR) {
10370 // drawing the fitresult as a pull plot on a subpad, and rest of the draw elsewhere
10371 clearPad();
10372 pad->Divide(1, 2, 1e-9, 1e-9); //,0,0);
10373 pad->GetPad(1)->SetPad(0, 0.2, 1, 1);
10374 pad->GetPad(2)->SetPad(0, 0, 1, 0.2);
10375 TString optNoFR(opt);
10376 optNoFR.ReplaceAll("pull", "");
10377 pad->cd(1);
10378 Draw(optNoFR);
10379 pad->cd(2);
10380 auto _fr = fitResult();
10381 _fr.Draw();
10382 // switch into subpad
10383 gPad->cd(1);
10384 gPad->SetFillColor(kGray);
10385 gPad->GetFrame()->SetFillColor(kWhite);
10386 gPad->GetFrame()->SetFillStyle(1001);
10387 gPad->SetTopMargin(0);
10388 gPad->SetBottomMargin(0);
10389 gPad->SetName("pull");
10390 // split the pull graph into individual points -- for benefit of GUI status bar
10391 auto pullGraph = dynamic_cast<TGraphAsymmErrors *>(gPad->GetPrimitive("pulls"));
10392 if (!pullGraph) {
10393 Error("Draw", "Couldn't find pull graph");
10394 return;
10395 }
10396 pullGraph->SetName("nominal");
10397 TMultiGraph *mg = new TMultiGraph;
10398 mg->SetName("editables");
10399
10400 auto scaleHist = static_cast<TH1 *>(pullGraph->FindObject("scales"));
10401 if (!scaleHist)
10402 throw std::runtime_error("Could not find scales in fit result");
10403
10404 for (auto i = 0; i < pullGraph->GetN(); i++) {
10405 auto g = new TGraphAsymmErrors;
10406 g->SetName(scaleHist->GetXaxis()->GetBinLabel(i + 1));
10407 auto _p = dynamic_cast<RooRealVar *>(_fr.get<RooFitResult>()->floatParsFinal().find(g->GetName()));
10408 if (!_p) {
10409 Warning("Draw", "Found a non-var in the floatParsFinal list: %s - this shouldn't happen", g->GetName());
10410 continue;
10411 }
10412 g->SetTitle(TString::Format(
10413 "%s=%g +/- %s [%g,%g]", strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName(), _p->getVal(),
10414 _p->hasAsymError() ? TString::Format("(%g,%g)", _p->getAsymErrorHi(), _p->getAsymErrorLo()).Data()
10415 : TString::Format("%g", _p->getError()).Data(),
10416 scaleHist->GetBinContent(i + 1), scaleHist->GetBinError(i + 1)));
10417 g->SetPoint(0, pullGraph->GetPointX(i), pullGraph->GetPointY(i));
10418 g->SetPointEYhigh(0, pullGraph->GetErrorYhigh(i));
10419 g->SetPointEYlow(0, pullGraph->GetErrorYlow(i));
10420 g->SetEditable(true);
10421 g->SetHighlight(true);
10422 g->SetMarkerStyle(20);
10423 g->SetMarkerSize(0.5);
10424 mg->Add(g);
10425 }
10426 // gPad->GetListOfPrimitives()->Remove(pullGraph); delete pullGraph;
10427 mg->Draw("z0p");
10428 mg->SetBit(kCanDelete);
10429 auto _thisClone = new xRooNode("node", fComp, fParent);
10430 _thisClone->SetBit(kCanDelete);
10431 _thisClone->AppendPad();
10432
10433 // ensure statusbar visible for interactive plot
10434 // turned this off for now ... as not needed if doing through browser, status bar already there
10435 // if (gPad->GetCanvas() && !gPad->GetCanvas()->TestBit(TCanvas::kShowEventStatus)) {
10436 // gPad->GetCanvas()->ToggleEventStatus();
10437 // }
10438 gPad->AddExec("interactivePull", TString::Format("%s::Interactive_Pull()", ClassName()));
10439
10440 pad->cd();
10441 return;
10442 }
10443
10444 if (auto _simPdf = get<RooSimultaneous>();
10445 _simPdf && !(v && strcmp(_simPdf->indexCat().GetName(), dynamic_cast<TObject *>(v)->GetName()) == 0)) {
10446 auto _channels = bins();
10447 int _size = 0;
10448 for (auto &_v : _channels) {
10449 if (!_v->IsHidden())
10450 _size++;
10451 }
10452 if (!hasSame) {
10453 if (_size > 4) {
10454 // add a pad for the common legends
10455 _size++;
10456 }
10457 clearPad();
10458 pad->SetBorderSize(0);
10459 // if (pad->GetCanvas() == pad) {
10460 // if(_size>4) {
10461 // int n = _size;
10462 // Int_t w = 1, h = 1;
10463 // if (pad->GetCanvas()->GetWindowWidth() > pad->GetCanvas()->GetWindowHeight()) {
10464 // w = TMath::Ceil(TMath::Sqrt(n));
10465 // h = TMath::Floor(TMath::Sqrt(n));
10466 // if (w*h < n) w++;
10467 // } else {
10468 // h = TMath::Ceil(TMath::Sqrt(n));
10469 // w = TMath::Floor(TMath::Sqrt(n));
10470 // if (w*h < n) h++;
10471 // }
10472 // // adjust the window size to display only 4 in the window, with scroll bars
10473 // pad->GetCanvas()->SetCanvasSize( w*((pad->GetCanvas()->GetWindowWidth()-4)/2.) -16
10474 // ,h*((pad->GetCanvas()->GetWindowHeight()-28)/2.) - 16 );
10475 // } else {
10476 // //pad->GetCanvas()->Set(
10477 // w*(pad->GetCanvas()->GetWindowWidth()/2.),h*(pad->GetCanvas()->GetWindowHeight()/2.)) )
10478 // }
10479 // }
10480 int ncols = _simPdf->getStringAttribute("ncols") ? TString(_simPdf->getStringAttribute("ncols")).Atoi() : 0;
10481 if (ncols > 0) {
10482 dynamic_cast<TPad *>(pad)->Divide(ncols, std::ceil(double(_size) / ncols), 1e-9, 1e-9);
10483 } else {
10484 dynamic_cast<TPad *>(pad)->DivideSquare(_size, 1e-9, 1e-9);
10485 }
10486 if (_size > 5) {
10487 auto _pad = pad->GetPad(_size); // will use as the legend pad
10488 _pad->SetName("legend");
10489 // stretch the pad all the way to the left
10490 _pad->SetPad(_pad->GetXlowNDC(), _pad->GetYlowNDC(), 1.0, _pad->GetYlowNDC() + _pad->GetHNDC());
10491 // and make all the remaining pads transparent
10492 int x = _size;
10493 while (pad->GetPad(x + 1)) {
10494 pad->GetPad(x + 1)->SetFillStyle(0);
10495 x++;
10496 }
10497 }
10498 }
10499 int i = 0;
10500 auto &chanVar = const_cast<RooAbsCategoryLValue &>(_simPdf->indexCat());
10501 // auto _idx = chanVar.getIndex();
10502 auto _range = GetRange();
10503 std::vector<TString> chanPatterns;
10504 if (_range && strlen(_range)) {
10505 TStringToken pattern(_range, ",");
10506 while (pattern.NextToken()) {
10507 chanPatterns.emplace_back(pattern);
10508 }
10509 }
10510 for (auto &_v : _channels) {
10511 if (_v->IsHidden())
10512 continue;
10513 TString s(_v->GetName());
10514 pad->cd(++i);
10515 gPad->SetName(s);
10516 TString cName = s(s.Index('=') + 1, s.Length());
10517 chanVar.setLabel(cName);
10518 bool inRange = chanPatterns.empty();
10519 for (auto &p : chanPatterns) {
10520 if (chanVar.inRange(p)) {
10521 inRange = true;
10522 break;
10523 }
10524 }
10525 if (!inRange || !_v->get<RooAbsReal>()->isSelectedComp())
10526 gPad->SetFillColor(kGray);
10527 if (!hasSame && _size > 1 && (gStyle->GetTitleFont("Y") % 10) == 3)
10528 gPad->SetLeftMargin(std::min(gPad->GetLeftMargin() * (1. / gPad->GetWNDC()), 0.3));
10529 _v->Draw(opt);
10531 }
10532 pad->cd(0);
10533 gPad->Modified();
10534 // gPad->Update();
10535 return;
10536 }
10537
10538 if (!get() || get<RooArgList>()) {
10539 // is a group draw all the submembers
10540 browse();
10541 int _size = 0;
10542 // int _size = _channels.size(); // size(); if (find("!.vars")) _size--;
10543 for (auto &_v : *this) {
10544 if (_v->IsHidden())
10545 continue;
10546 if (strcmp(GetName(), ".vars") == 0) {
10547 // auto hide obs and "1" and const var
10548 if (_v->get<RooAbsArg>()->getAttribute("obs"))
10549 continue;
10550 if (strcmp(_v->get()->GetName(), "1") == 0 || strcmp(_v->get()->GetName(), "ONE") == 0 ||
10551 TString(_v->get()->GetName()).BeginsWith("binWidth_"))
10552 continue;
10553 if (_v->get()->InheritsFrom("RooConstVar"))
10554 continue;
10555 }
10556 TString s(_v->GetName());
10557 if (s.BeginsWith(".") || s.BeginsWith("!"))
10558 continue;
10559 _size++;
10560 }
10561 if (!hasSame) {
10562 clearPad();
10563 pad->SetBorderSize(0);
10564 dynamic_cast<TPad *>(pad)->DivideSquare(_size, 1e-9, 1e-9);
10565 }
10566 int i = 0;
10567 for (auto &_v : *this) {
10568 if (_v->IsHidden())
10569 continue;
10570 if (strcmp(GetName(), ".vars") == 0) {
10571 // auto hide obs and "1" and const var
10572 if (_v->get<RooAbsArg>()->getAttribute("obs"))
10573 continue;
10574 if (strcmp(_v->get()->GetName(), "1") == 0 || strcmp(_v->get()->GetName(), "ONE") == 0 ||
10575 TString(_v->get()->GetName()).BeginsWith("binWidth_"))
10576 continue;
10577 if (_v->get()->InheritsFrom("RooConstVar"))
10578 continue;
10579 }
10580 TString s(_v->GetName());
10581 if (s.BeginsWith(".") || s.BeginsWith("!"))
10582 continue;
10583 pad->cd(++i);
10584 gPad->SetName(s);
10585 if (!hasSame && _size > 1 && (gStyle->GetTitleFont("Y") % 10) == 3)
10586 gPad->SetLeftMargin(std::min(gPad->GetLeftMargin() * (1. / gPad->GetWNDC()), 0.3));
10587 _v->Draw(opt);
10588 // pad->Modified();//pad->Update();
10590 }
10591 pad->cd(0);
10592 gPad->Modified();
10593 // gPad->Update();
10594 return;
10595 }
10596
10597 if (get()->InheritsFrom("RooProdPdf")) {
10598 // draw the main pdf, if there is one...
10599 auto _mainChild = mainChild();
10600 if (_mainChild) {
10601 _mainChild.Draw(opt);
10602 gPad->SetName(GetName());
10603 return;
10604 }
10605 }
10606
10607 if (auto fr = get<RooFitResult>(); fr) {
10608 if (sOpt.Contains("corr")) {
10609 // do correlation matrix
10610 // if a number follows 'corr', reduce the correlation matrix to show only the most extreme correlations
10611 int numCorrs = TString(sOpt(sOpt.Index("corr") + 4, sOpt.Length())).Atoi();
10612 if (numCorrs == 0)
10613 numCorrs = fr->correlationMatrix().GetNcols();
10614
10615 TH2 *hist = nullptr;
10616 if (numCorrs < fr->correlationMatrix().GetNcols()) {
10617 // need to reduce
10618 std::set<std::pair<double, size_t>> maxCorrs;
10619 for (int i = 0; i < fr->correlationMatrix().GetNcols(); i++) {
10620 double maxCorr = 0;
10621 for (int j = 0; j < fr->correlationMatrix().GetNcols(); j++) {
10622 if (j == i)
10623 continue;
10624 maxCorr = std::max(std::abs(fr->correlationMatrix()(i, j)), maxCorr);
10625 }
10626 maxCorrs.insert({maxCorr, i});
10627 }
10628 std::vector<size_t> topN;
10629 int c = 0;
10630 for (auto itr = maxCorrs.rbegin(); itr != maxCorrs.rend(); ++itr) {
10631 topN.push_back(itr->second);
10632 c++;
10633 if (c == numCorrs)
10634 break;
10635 }
10636 hist = new TH2D(fr->GetName(), TString::Format("%s - Top %d correlations", fr->GetTitle(), numCorrs),
10638 for (size_t i = 0; i < topN.size(); i++) {
10639 hist->GetXaxis()->SetBinLabel(i + 1, fr->floatParsFinal().at(topN.at(i))->GetTitle());
10640 hist->GetYaxis()->SetBinLabel(numCorrs - i, fr->floatParsFinal().at(topN.at(i))->GetTitle());
10641 for (size_t j = 0; j < topN.size(); j++) {
10642 hist->Fill(i + 0.5, numCorrs - j - 0.5, fr->correlationMatrix()(topN.at(i), topN.at(j)));
10643 }
10644 }
10645 hist->SetMinimum(-1);
10646 hist->SetMaximum(1);
10647
10648 } else {
10649 hist = fr->correlationHist(fr->GetName());
10650 hist->SetTitle(fr->GetTitle());
10651 }
10652
10653 hist->SetBit(kCanDelete);
10654 hist->Scale(100);
10655 hist->SetStats(false);
10656 hist->SetDirectory(nullptr);
10658 gStyle->SetPaintTextFormat(".1f");
10659 hist->GetXaxis()->SetTickSize(0);
10660 hist->GetYaxis()->SetTickSize(0);
10661 hist->SetMinimum(-100);
10662 hist->Draw(sOpt);
10664 gPad->SetGrid(1, 1);
10665 gPad->SetLogy(0);
10666 gPad->SetLogx(0);
10667 return;
10668 }
10669
10670 if (sOpt.Contains("brakdown")) { // e will have been removed above
10671
10672 // breakdown is quadrature difference between total error and conditional error
10673 // group by 'group' attribute
10674
10675 std::string poiName;
10676 if (sOpt.Contains("brakdown:")) {
10677 TString sOpt3(opt);
10678 poiName = sOpt3(sOpt3.Index("breakdown:") + 10, sOpt3.Length());
10679 } else {
10680 std::unique_ptr<RooAbsCollection> _poi(fr->floatParsFinal().selectByAttrib("poi", true));
10681 if (_poi->empty()) {
10682 throw std::runtime_error("No floating poi in the fit");
10683 } else if (_poi->size() != 1) {
10684 throw std::runtime_error("Multiple poi in the fit");
10685 }
10686 poiName = _poi->first()->GetName();
10687 }
10688 RooRealVar *poi = dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(poiName.c_str()));
10689 if (!poi) {
10690 throw std::runtime_error(TString::Format("Cannot find parameter %s", poiName.c_str()));
10691 }
10692 std::set<std::string> groups;
10693 for (auto p : fr->floatParsFinal()) {
10694 if (p == poi) {
10695 continue;
10696 } else if (p->getStringAttribute("group")) {
10697 groups.insert(p->getStringAttribute("group"));
10698 } else {
10699 groups.insert(p->GetTitle());
10700 }
10701 }
10702
10703 auto roundedVal = xRooFit::matchPrecision(std::pair(poi->getVal(), poi->getError()));
10704
10705 TPie *pie = new TPie(TString::Format("breakdown:%s", poi->GetName()),
10706 TString::Format("%s: %g #pm %g", poi->GetTitle(), roundedVal.first, roundedVal.second),
10707 groups.size() + 1);
10708
10709 // for display of errors will go to one extra dp ...
10710 roundedVal.second *= .1;
10711
10712 // do breakdown by removing parameters in blocks according to groups and seeing impact on variance
10713 // this will give the correct sum but will be order-dependent if there are correlations between
10714 // groups. therefore we will stick with group-by-group
10715 // RooArgList pars(fr->floatParsFinal()); // pars to not condition on
10716 // double variance = pow(dynamic_cast<RooRealVar*>(poi)->getError(),2);
10717 int i = 0;
10718 for (auto group : groups) {
10719 RooArgList pars(fr->floatParsFinal()); // pars to not condition on
10720 double variance = pow(dynamic_cast<RooRealVar *>(poi)->getError(), 2);
10721 for (auto p : fr->floatParsFinal()) {
10722 if (p == poi) {
10723 continue;
10724 } else if ((p->getStringAttribute("group") && group == p->getStringAttribute("group")) ||
10725 (!p->getStringAttribute("group") && group == p->GetTitle())) {
10726 // conditioning on this parameter ... remove from pars list
10727 pars.remove(*p);
10728 }
10729 }
10730 int idx = pars.index(poiName.c_str());
10731 double reducedVar = fr->conditionalCovarianceMatrix(pars)(idx, idx);
10732 if (reducedVar > variance) {
10733 Warning("Draw", "breakdown group %s variance bigger than preceding?", group.c_str());
10734 pie->SetEntryVal(i, 0);
10735 pie->SetEntryLabel(i, TString::Format("%s: NaN", group.c_str()));
10736 } else {
10737 pie->SetEntryVal(i, variance - reducedVar);
10739 std::pair(sqrt(variance - reducedVar), roundedVal.second)); // r.first will be the rounded error
10740 if (r.first > 0) {
10741 pie->SetEntryLabel(i, TString::Format("%s: %g", group.c_str(), r.first));
10742 } else {
10743 pie->SetEntryLabel(i, group.c_str()); // suppress labels for negligible errors.
10744 }
10745 }
10746 pie->SetEntryFillColor(i, TColor::GetColorPalette(TColor::GetNumberOfColors() * i / pie->GetEntries()));
10747 // variance = reducedVar;
10748 i++;
10749 }
10750 // remaining variance is statistical=
10751 double variance = fr->conditionalCovarianceMatrix(*poi)(0, 0);
10752 auto r =
10753 xRooFit::matchPrecision(std::pair(sqrt(variance), roundedVal.second)); // r.first will be the rounded error
10754 pie->SetEntryVal(i, variance);
10755 pie->SetEntryLabel(i, TString::Format("stat: %g", r.first));
10756 pie->SetEntryFillColor(i, TColor::GetColorPalette(TColor::GetNumberOfColors() * i / pie->GetEntries()));
10757 pie->SetBit(kCanDelete);
10758 pie->SetRadius(0.17);
10759 pie->SetTextSize(gStyle->GetTitleYSize());
10760 pie->Draw("NOL");
10761 return;
10762 }
10763
10764 // plot pull or impact
10766 out->SetName(TString::Format("%s_pull", fr->GetName()));
10767 out->SetTitle("Fit Result Pulls");
10768 std::vector<TString> graphLabels;
10770 ugraph->SetName(TString::Format("%s_pull_unconstrained", fr->GetName()));
10771 ugraph->SetTitle("Fit Result Pulls");
10772 std::vector<TString> ugraphLabels;
10773 std::map<std::string, double> scale;
10774 std::map<std::string, double> offset;
10775 for (auto &p : fr->floatParsFinal()) {
10776 auto _v = dynamic_cast<RooRealVar *>(p);
10777 if (!_v)
10778 continue;
10779
10780 if (std::isnan(_v->getErrorHi()) || std::isnan(_v->getErrorLo())) {
10781 Warning("Draw", "%s error is invalid", _v->GetName());
10782 }
10783
10784 // need to get constraint mean and error parameters ....
10785 // look for normal gaussian and poisson cases
10786 double prefitError = 0;
10787 double prefitVal = 0;
10788 double customScale = 0;
10789 if (auto ip =
10790 dynamic_cast<RooRealVar *>(fr->floatParsInit().find(p->GetName()))) { // handles if no prefit available
10791 prefitError = ip->getError();
10792 prefitVal = ip->getVal();
10793 };
10794
10795 std::shared_ptr<xRooNode> pConstr;
10796 if (fParent && fParent->getObject<RooRealVar>(p->GetName())) {
10797 auto _vv = fParent->getObject<RooRealVar>(p->GetName());
10798 if (_vv->hasRange("pullScale")) {
10799 customScale = (_vv->getMax("pullScale") - _vv->getMin("pullScale")) / 2.;
10800 }
10801 auto _constr = xRooNode(_vv, *this).constraints();
10802 for (auto &c : _constr) {
10803 if (c->get<RooPoisson>() || c->get<RooGaussian>()) {
10804 // require parameter to be a direct server of the constraint pdf to count if its a gaussian
10805 bool isServer = true;
10806 if (c->get<RooGaussian>()) {
10807 isServer = false;
10808 for (auto s : c->get<RooAbsArg>()->servers()) {
10809 if (strcmp(s->GetName(), p->GetName()) == 0) {
10810 isServer = true;
10811 break;
10812 }
10813 }
10814 }
10815 if (isServer) {
10816 pConstr = c;
10817 break;
10818 }
10819 }
10820 }
10821 }
10822 if (pConstr) {
10823
10824 // there will be 3 deps, one will be this par, the other two are the mean and error (or error^2 in case of
10825 // poisson
10826
10827 // std::cout << p->GetName() << " extracted " << prefitVal << " " << prefitError << " from ";
10828 // pConstr->deps().Print();
10829 pConstr->browse();
10830 if (pConstr->get<RooPoisson>() && pConstr->find(".x")) {
10831 std::string xName = pConstr->find(".x")->get()->GetName();
10832 prefitVal = pConstr->find(".x")->get<RooAbsReal>()->getVal();
10833 for (auto &_d : pConstr->vars()) {
10834 if (strcmp(p->GetName(), _d->get()->GetName()) == 0)
10835 continue;
10836 if (xName == _d->get()->GetName())
10837 continue;
10838 if (_d->get<RooAbsReal>()->getVal())
10839 prefitError = _d->get<RooAbsReal>()->getVal();
10840 }
10841 if (fr->constPars().find(pConstr->find(".x")->get()->GetName())) {
10842 // globs was saved to fr, use that instead of current value
10843 prefitVal = fr->constPars().getRealValue(pConstr->find(".x")->get()->GetName());
10844 }
10845 // prefitVal will be the global observable value, need to divide that by tau
10847 // prefiterror will be tau ... need 1/sqrt(tau) for error
10848 prefitError = 1. / sqrt(prefitError);
10849 } else if (auto _g = pConstr->get<RooGaussian>(); _g) {
10850 prefitError =
10851 (pConstr->find(".sigma")) ? pConstr->find(".sigma")->get<RooAbsReal>()->getVal() : prefitError;
10852 prefitVal =
10853 (pConstr->find(".x")) ? pConstr->find(".x")->get<RooAbsReal>()->getVal() : 0; // usually the globs
10854 if (pConstr->find(".x") && fr->constPars().find(pConstr->find(".x")->get()->GetName())) {
10855 // globs was saved to fr, use that instead of current value
10856 prefitVal = fr->constPars().getRealValue(pConstr->find(".x")->get()->GetName());
10857 }
10858 if (pConstr->find(".x") &&
10859 strcmp(p->GetName(), pConstr->find(".x")->get<RooAbsReal>()->GetName()) == 0) {
10860 // hybrid construction case,
10861 prefitVal = pConstr->find(".mean")->get<RooAbsReal>()->getVal();
10862 if (fr->constPars().find(pConstr->find(".mean")->get()->GetName())) {
10863 // globs was saved to fr, use that instead of current value
10864 prefitVal = fr->constPars().getRealValue(pConstr->find(".mean")->get()->GetName());
10865 }
10866 }
10867 }
10868
10869 if (customScale)
10871 if (prefitError == 0) {
10872 Warning("Draw", "failed to determine prefit error of %s, using post-fit error", p->GetName());
10873 prefitError = _v->getError();
10874 }
10875 out->SetPoint(out->GetN(), out->GetN(), (_v->getVal() - prefitVal) / prefitError);
10876 out->SetPointError(out->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
10877 (_v->getErrorHi()) / prefitError);
10878 graphLabels.push_back(p->GetName());
10879 scale[p->GetName()] = prefitError;
10880 offset[p->GetName()] = prefitVal;
10881 } else if (!fParent) {
10882 // no parent to determine constraints from ... prefitError=0 will be the unconstrained ones
10883 if (customScale)
10885 if (prefitError == 0) {
10886 // uses range of var
10887 prefitError = (std::max({_v->getMax() - _v->getVal(), _v->getVal() - _v->getMin(), 4.}) / 4);
10888 ugraph->SetPoint(ugraph->GetN(), ugraph->GetN(), (_v->getVal() - prefitVal) / prefitError);
10889 ugraph->SetPointError(ugraph->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
10890 (_v->getErrorHi()) / prefitError);
10891 ugraphLabels.push_back(p->GetName());
10892 } else {
10893 out->SetPoint(out->GetN(), out->GetN(), (_v->getVal() - prefitVal) / prefitError);
10894 out->SetPointError(out->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
10895 (_v->getErrorHi()) / prefitError);
10896 graphLabels.push_back(p->GetName());
10897 }
10898 scale[p->GetName()] = prefitError;
10899 offset[p->GetName()] = prefitVal;
10900
10901 } else {
10902 // unconstrained (or at least couldn't determine constraint) ... use par range if no prefit error
10903 if (customScale)
10905 if (prefitError == 0) {
10906 prefitError = (std::max({_v->getMax() - _v->getVal(), _v->getVal() - _v->getMin(), 4.}) / 4);
10907 }
10908 ugraph->SetPoint(ugraph->GetN(), ugraph->GetN(), (_v->getVal() - prefitVal) / prefitError);
10909 ugraph->SetPointError(ugraph->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
10910 (_v->getErrorHi()) / prefitError);
10911 ugraphLabels.push_back(p->GetName());
10912 scale[p->GetName()] = prefitError;
10913 offset[p->GetName()] = prefitVal;
10914 }
10915 }
10916 auto graph = out;
10917
10918 // append ugraph points to end of graph
10919 for (int i = 0; i < ugraph->GetN(); i++)
10920 ugraph->SetPointX(i, i + graph->GetN());
10921 int nUnconstrained = ugraph->GetN();
10922 TList tmpList;
10923 tmpList.SetName("tmpList");
10924 tmpList.Add(ugraph);
10925 graph->Merge(&tmpList);
10926 tmpList.RemoveAll();
10927 delete ugraph;
10928 for (auto &l : ugraphLabels) {
10929 graphLabels.push_back(l);
10930 }
10931
10932 graph->SetBit(kCanDelete);
10933 graph->SetMarkerStyle(20);
10934 graph->SetMarkerSize(0.5);
10935
10936 graph->SetMaximum(4);
10937 graph->SetMinimum(-4);
10938
10939 bool doHorizontal =
10940 (!sOpt.Contains("impact") && sOpt.Contains("v")) || (sOpt.Contains("impact") && !sOpt.Contains("himpact"));
10941
10942 std::vector<std::pair<double, std::string>> covariances;
10943 /*double poiError = 0;*/ std::string poiName;
10944 double maxImpact = 0;
10945 if (sOpt.Contains("impact")) {
10946 if (sOpt.Contains("impact:")) {
10947 TString sOpt3(opt);
10948 poiName = sOpt3(sOpt3.Index("impact:") + 7, sOpt3.Length());
10949 } else {
10950 std::unique_ptr<RooAbsCollection> _poi(fr->floatParsFinal().selectByAttrib("poi", true));
10951 if (_poi->empty()) {
10952 throw std::runtime_error("No floating poi in the fit");
10953 } else if (_poi->size() != 1) {
10954 throw std::runtime_error("Multiple poi in the fit");
10955 }
10956 poiName = _poi->first()->GetName();
10957 }
10958 RooAbsArg *poi = fr->floatParsFinal().find(poiName.c_str());
10959 if (!poi) {
10960 throw std::runtime_error(TString::Format("Cannot find parameter %s", poiName.c_str()));
10961 }
10962 size_t poiIdx = fr->floatParsFinal().index(*poi);
10963 // put parameters in order of impact on the poi
10964
10965 // impact is regression coefficient * npError
10966 // relevant regression coefficient is cov / (npVariance)
10967 // i.e. DeltaX/sigmaX = [cov(X,Y)/(sigmaXsigmaY)]DeltaY/sigmaY
10968 // ... DeltaX = [cov(X,Y)/(sigmaY^2)]DeltaY
10969 // if DeltaY is just sigmaY then DeltaX = cov(X,Y)/sigmaY
10970
10971 for (auto &label : graphLabels) {
10972 covariances.emplace_back(fr->covarianceMatrix()(poiIdx, fr->floatParsFinal().index(label)) /
10973 dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(label))->getError(),
10974 label);
10975 }
10976 std::sort(covariances.begin(), covariances.end(),
10977 [&](std::pair<double, std::string> i, std::pair<double, std::string> j) {
10978 return doHorizontal ? (std::abs(i.first) < std::abs(j.first))
10979 : (std::abs(i.first) > std::abs(j.first));
10980 });
10981
10983 std::vector<TString> sortedLabels;
10984 maxImpact = (doHorizontal) ? covariances.back().first
10985 : covariances.front().first; // note: max impact is likely to be self variance
10986 for (auto &c : covariances) {
10987 if (c.second == poi->GetName()) {
10988 // poiError = sqrt(c.first);
10989 continue; // skip self
10990 }
10991 c.first *= 4. / (maxImpact * 1.2);
10992 sortedLabels.push_back(c.second);
10993 size_t i = 0;
10994 for (; i < graphLabels.size(); i++) {
10995 if (graphLabels[i] == c.second) {
10996 break;
10997 }
10998 }
10999 sortedGraph.AddPoint(sortedGraph.GetN(), graph->GetPointY(i));
11000 sortedGraph.SetPointError(sortedGraph.GetN() - 1, 0, 0, graph->GetErrorYlow(i), graph->GetErrorYhigh(i));
11001 }
11002 graph->Set(0);
11004 tmpList2.SetName("tmpList");
11005 tmpList2.Add(&sortedGraph);
11006 graph->Merge(&tmpList2);
11007 tmpList2.RemoveAll();
11009 graph->SetTitle("Fit Result Impact");
11010 }
11011
11012 // create a framing histogram
11013 TH2D *hist;
11014 if (doHorizontal) {
11015 hist = new TH2D(GetName(), fr->GetTitle(), 100, -4, 4, std::max(graph->GetN(), 1), -0.5,
11016 std::max(graph->GetN(), 1) - 0.5);
11017 int i = 1;
11018 for (auto &l : graphLabels) {
11019 hist->GetYaxis()->SetBinLabel(i++, l);
11020 }
11021 if (!graphLabels.empty())
11022 hist->GetYaxis()->LabelsOption("v");
11023 hist->GetXaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
11024 } else {
11025 hist = new TH2D(GetName(), fr->GetTitle(), std::max(graph->GetN(), 1), -0.5, std::max(graph->GetN(), 1) - 0.5,
11026 100, -4, 4);
11027 int i = 1;
11028 for (auto &l : graphLabels) {
11029 hist->GetXaxis()->SetBinLabel(i++, l);
11030 }
11031 if (!graphLabels.empty())
11032 hist->GetXaxis()->LabelsOption("v");
11033 hist->GetYaxis()->SetNdivisions(8, 0, 0);
11034 hist->GetYaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
11035 }
11036 hist->SetStats(false);
11037 hist->SetDirectory(nullptr);
11038 hist->SetBit(kCanDelete);
11039 auto histCopy = dynamic_cast<TH1 *>(hist->Clone(".axis"));
11040 histCopy->SetDirectory(nullptr);
11041 histCopy->SetBit(kCanDelete);
11042 auto _axis = (doHorizontal ? histCopy->GetYaxis() : histCopy->GetXaxis());
11043
11044 graph->GetHistogram()->GetXaxis()->Set(std::max(graph->GetN(), 1), -0.5, std::max(graph->GetN(), 1) - 0.5);
11045 for (int ii = 1; ii <= _axis->GetNbins(); ii++) {
11046 graph->GetHistogram()->GetXaxis()->SetBinLabel(ii, _axis->GetBinLabel(ii));
11047 }
11048 // int i = 1;
11049 // for (auto &l : graphLabels) {
11050 // hist->GetXaxis()->SetBinLabel(i++, l);
11051 // }
11052 // hist->SetMaximum(4);
11053 // hist->SetMinimum(-4);
11054 // if (graph->GetN())
11055 // hist->GetXaxis()->LabelsOption("v");
11056 // hist->GetYaxis()->SetNdivisions(8, 0, 0);
11057 // hist->GetYaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
11058 clearPad();
11059 // create a new pad because adjust the margins ...
11060 auto oldPad = gPad;
11061 gPad->Divide(1, 1, 1e-9, 1e-9);
11062 gPad->cd(1);
11063
11064 if (doHorizontal) {
11065 gPad->SetLeftMargin(0.4);
11066 } else {
11067 gPad->SetBottomMargin(0.4);
11068 }
11069
11070 auto pNamesHist = dynamic_cast<TH1F *>(graph->GetHistogram()->Clone("scales")); // used by interactive "pull" plot
11071 pNamesHist->Sumw2();
11072 pNamesHist->SetDirectory(nullptr);
11073
11074 for (int ii = 1; ii <= graph->GetN(); ii++) { // use graph->GetN() to protect against the 0 pars case
11075 auto _p = fr->floatParsFinal().find(_axis->GetBinLabel(ii));
11076 pNamesHist->SetBinContent(ii, offset[_p->GetName()]);
11077 pNamesHist->SetBinError(ii, scale[_p->GetName()]);
11078 _axis->SetBinLabel(ii, strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName());
11079 }
11080
11081 // hist->Draw(); -- now just draw the graph
11082
11083 if (!sOpt.Contains("impact")) {
11084 for (int ii = 2; ii >= 1; ii--) {
11085 auto pullBox = new TGraphErrors;
11086 pullBox->SetName(TString::Format("%dsigmaBand", ii));
11087 pullBox->SetBit(kCanDelete);
11088 pullBox->SetPoint(0, (doHorizontal) ? -ii : -0.5, (doHorizontal) ? -0.5 : 0);
11089 pullBox->SetPoint(1, (doHorizontal) ? ii : (_axis->GetNbins() - 0.5 - nUnconstrained),
11090 (doHorizontal) ? -0.5 : 0);
11091 pullBox->SetPointError(0, 0, (doHorizontal) ? (_axis->GetNbins() - nUnconstrained) : ii);
11092 pullBox->SetPointError(1, 0, (doHorizontal) ? (_axis->GetNbins() - nUnconstrained) : ii);
11093 pullBox->SetFillColor((ii == 2) ? kYellow : kGreen);
11094 hist->GetListOfFunctions()->Add(pullBox, "3"); // pullBox->Draw("3");
11095 }
11096 auto pullLine = new TGraph;
11097 pullLine->SetName("0sigmaLine");
11098 pullLine->SetBit(kCanDelete);
11099 pullLine->SetPoint(0, -0.5, 0);
11100 pullLine->SetPoint(1, _axis->GetNbins() - 0.5, 0);
11101 pullLine->SetLineStyle(2);
11102 pullLine->SetEditable(false);
11103 hist->GetListOfFunctions()->Add(pullLine, "l"); // pullLine->Draw("l");
11104
11105 // also draw vertical line separating constrained from unconstrained, if necessary
11106 if (nUnconstrained > 0) {
11107 pullLine = new TGraph;
11108 pullLine->SetName("dividerLine");
11109 pullLine->SetBit(kCanDelete);
11110 pullLine->SetPoint(0, graph->GetN() - 0.5 - nUnconstrained, -100);
11111 pullLine->SetPoint(1, graph->GetN() - 0.5 - nUnconstrained, 100);
11112 pullLine->SetLineStyle(2);
11113 pullLine->SetEditable(false);
11114 hist->GetListOfFunctions()->Add(pullLine, "l"); // pullLine->Draw("l");
11115 }
11116
11117 // and draw a pave with fr status info
11118 TPaveText *pave =
11119 new TPaveText(gPad->GetLeftMargin(), 1. - gPad->GetTopMargin(), 1. - gPad->GetRightMargin(), 0.98, "NDCNB");
11120 pave->SetFillStyle(0);
11121 pave->SetBorderSize(0);
11122 pave->SetMargin(0.);
11123 pave->SetName("status");
11124 pave->SetTextAlign(31);
11125 pave->AddText(TString::Format("minNLL: %g edm: %g", fr->minNll(), fr->edm()))
11126 ->SetTextColor((fr->status() == 3) ? kRed : kBlack);
11127 std::string covQualTxt;
11128 switch (fr->covQual()) {
11129 case -1: covQualTxt = "Unknown"; break;
11130 case 0: covQualTxt = "Not calculated"; break;
11131 case 1: covQualTxt = "Approximate"; break;
11132 case 2: covQualTxt = "Forced Positive-Definite"; break;
11133 case 3: covQualTxt = "Accurate"; break;
11134 }
11135 pave->AddText(TString::Format("Cov. Quality: %d (%s)", fr->covQual(), covQualTxt.c_str()))
11136 ->SetTextColor((fr->covQual() == 3) ? kBlack : kRed);
11137
11138 std::string statusCodes;
11139 for (unsigned int i = 0; i < fr->numStatusHistory(); i++) {
11140 statusCodes += TString::Format(" %s = %d", fr->statusLabelHistory(i), fr->statusCodeHistory(i));
11141 }
11142 pave->AddText(statusCodes.c_str())->SetTextColor(fr->status() == 0 ? kBlack : kRed);
11143
11144 hist->GetListOfFunctions()->Add(pave);
11145
11146 } else {
11147 gPad->SetTicks(0, 0); // ensure mirrored ticks aren't drawn in this pad
11148
11149 if (doHorizontal) {
11150 // ensure canvas height big enough
11151 if (int(gPad->GetCanvas()->GetWh()) < pNamesHist->GetNbinsX() * 15) {
11152 gPad->GetCanvas()->SetCanvasSize(gPad->GetCanvas()->GetWw(), pNamesHist->GetNbinsX() * 15);
11153 }
11154 }
11155
11156 double factor = 475. / gPad->GetCanvas()->GetWh(); // Wh is the full canvas height, not window height
11157 gPad->SetTopMargin(gStyle->GetPadTopMargin() * factor); // fixed margin height
11158 gPad->SetBottomMargin(gStyle->GetPadBottomMargin() * factor); // fixed margin height
11159
11160 TGaxis *axis =
11161 new TGaxis(_axis->GetXmin(), -4, _axis->GetXmin(), 4, -1.2 * maxImpact, 1.2 * maxImpact, 510, "-S");
11162
11163 if (doHorizontal) {
11164 // _axis->SetLabelSize(
11165 // (_axis->GetLabelFont() % 10 > 2)
11166 // ? (20 / factor)
11167 // : ((gPad->AbsPixeltoY(0) - gPad->AbsPixeltoY(20 / factor)) / (gPad->GetY2() -
11168 // gPad->GetY1())));
11169 // histCopy->GetXaxis()->SetTickLength(histCopy->GetXaxis()->GetTickLength() * factor);
11170 // hist->GetXaxis()->SetTickLength(hist->GetXaxis()->GetTickLength() * factor);
11171 // histCopy->GetYaxis()->SetTickLength(histCopy->GetYaxis()->GetTickLength() * factor);
11172 // hist->GetYaxis()->SetTickLength(hist->GetYaxis()->GetTickLength() * factor);
11173 // histCopy->GetXaxis()->SetTitleOffset(histCopy->GetXaxis()->GetTitleOffset() * factor);
11174 // histCopy->GetXaxis()->SetLabelOffset(histCopy->GetXaxis()->GetLabelOffset() * factor);
11175 // hist->GetXaxis()->SetTitleOffset(hist->GetXaxis()->GetTitleOffset() * factor);
11176 // hist->GetXaxis()->SetLabelOffset(hist->GetXaxis()->GetLabelOffset() * factor);
11177 // histCopy->GetXaxis()->SetTitleOffset(histCopy->GetXaxis()->GetTitleOffset() * factor);
11178 // histCopy->GetXaxis()->SetLabelOffset(histCopy->GetXaxis()->GetLabelOffset() * factor);
11179 }
11180 // copy attributes from TAxis to TGaxis
11181 axis->ImportAxisAttributes((doHorizontal) ? histCopy->GetXaxis() : histCopy->GetYaxis());
11182 axis->SetTitle(TString::Format("#Delta %s", fr->floatParsFinal().find(poiName.c_str())->GetTitle()));
11183
11184 // create impact bar charts
11185 for (int tt = 0; tt < 2; tt++) {
11186 auto impact = static_cast<TH1 *>(
11187 graph->GetHistogram()->Clone(TString::Format("%s_impact+", tt == 0 ? "prefit" : "postfit")));
11188 impact->SetDirectory(nullptr);
11189 impact->GetYaxis()->SetTitle(TString::Format("#Delta%s/#sigma", poiName.c_str()));
11190 impact->SetBarWidth(0.9);
11191 impact->SetBarOffset(0.05);
11192 impact->SetLineColor(kBlack);
11193 impact->SetFillColor(kAzure - 4);
11194 impact->SetFillStyle(tt == 0 ? 3013 : 1001);
11195 auto impact2 =
11196 static_cast<TH1 *>(impact->Clone(TString::Format("%s_impact-", tt == 0 ? "prefit" : "postfit")));
11197 impact2->SetDirectory(nullptr);
11198 impact2->SetFillColor(kCyan);
11199 for (int ii = 1; ii <= pNamesHist->GetNbinsX(); ii++) {
11200 for (auto &c : covariances) {
11201 if (c.second != pNamesHist->GetXaxis()->GetBinLabel(ii))
11202 continue;
11203 auto vv = dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(c.second.c_str()));
11204 auto vv_init = dynamic_cast<RooRealVar *>(fr->floatParsInit().find(c.second.c_str()));
11205 impact->SetBinContent(ii, ((tt == 0 && !vv_init->hasError()) || !vv->hasError())
11206 ? 0.
11207 : c.first * vv->getError() / vv->getErrorHi() *
11208 (tt == 0 ? (vv_init->getErrorHi() / vv->getErrorHi()) : 1.));
11209 impact2->SetBinContent(ii, ((tt == 0 && !vv_init->hasError()) || !vv->hasError())
11210 ? 0.
11211 : c.first * vv->getError() / vv->getErrorLo() *
11212 (tt == 0 ? (vv_init->getErrorLo() / vv->getErrorLo()) : 1.));
11213 }
11214 }
11215 hist->GetListOfFunctions()->Add(impact, (doHorizontal) ? "hbarsamemin0" : "bsamey+");
11216 hist->GetListOfFunctions()->Add(impact2, (doHorizontal) ? "hbarsamemin0" : "bsamey+");
11217 }
11218 // add three lines
11219 for (int ii = -1; ii <= 1; ii++) {
11220 auto pullLine = new TGraph;
11221 pullLine->SetName(TString::Format("%dsigmaLine", ii));
11222 pullLine->SetBit(kCanDelete);
11223 pullLine->SetPoint(0, -0.5, ii);
11224 pullLine->SetPoint(1, hist->GetNbinsY() - 0.5, ii);
11225 pullLine->SetLineStyle(2);
11226 pullLine->SetEditable(false);
11227 hist->GetListOfFunctions()->Add(pullLine, "l");
11228 }
11229 hist->GetListOfFunctions()->Add(axis); // draw axis last
11230 TLegend *leg1 =
11231 new TLegend(0.02, doHorizontal ? (1. - 0.22 * factor) : 0.02, 0.27, (doHorizontal ? 1. : 0.24));
11232 leg1->SetFillStyle(0);
11233 leg1->SetBorderSize(0);
11234 leg1->SetMargin(0.25);
11235 leg1->SetNColumns(2);
11236
11237 leg1->SetTextSize(_axis->GetLabelSize());
11238 leg1->SetTextFont(_axis->GetLabelFont());
11239 leg1->AddEntry((TObject *)nullptr, "Hessian Pre-fit", "");
11240 leg1->AddEntry((TObject *)nullptr, "Impact:", "");
11241 leg1->AddEntry(hist->FindObject("prefit_impact+"), "#theta = #hat{#theta}+#Delta#theta", "f");
11242 leg1->AddEntry(hist->FindObject("prefit_impact-"), "#theta = #hat{#theta}-#Delta#theta", "f");
11243 leg1->AddEntry((TObject *)nullptr, "Hessian Post-fit", "");
11244 leg1->AddEntry((TObject *)nullptr, "Impact:", "");
11245 leg1->AddEntry(hist->FindObject("postfit_impact+"), "#theta = #hat{#theta}+#Delta#theta", "f");
11246 leg1->AddEntry(hist->FindObject("postfit_impact-"), "#theta = #hat{#theta}-#Delta#theta", "f");
11247
11248 hist->GetListOfFunctions()->Add(leg1);
11249 if (gStyle->GetOptTitle()) {
11250 histCopy->SetBit(TH1::kNoTitle);
11251 TPaveText *title =
11252 new TPaveText(gPad->GetLeftMargin(), 1. - gPad->AbsPixeltoY(14), 1. - gPad->GetRightMargin(), 1., "NDC");
11253 title->ConvertNDCtoPad();
11254 title->SetY1NDC(1. - gPad->GetTopMargin() * 0.6);
11255 title->SetY2NDC(1);
11256 title->SetTextSize(
11257 (title->GetTextFont() % 10 > 2)
11258 ? (14 / factor)
11259 : ((gPad->AbsPixeltoY(0) - gPad->AbsPixeltoY(10 / factor)) / (gPad->GetY2() - gPad->GetY1())));
11260 title->SetFillStyle(0);
11261 title->SetBorderSize(0);
11262 title->AddText(histCopy->GetTitle());
11263 hist->GetListOfFunctions()->Add(title);
11264 }
11265 }
11266
11267 graph->SetEditable(false);
11268 pNamesHist->SetLineWidth(0);
11269 pNamesHist->SetMarkerSize(0);
11270 pNamesHist->SetMarkerStyle(0);
11271 graph->GetListOfFunctions()->Add(pNamesHist, "same"); // graph->SetHistogram(pNamesHist);
11272 if (doHorizontal) {
11273
11274 // flip the graph and contained graphs
11275 for (int p = 0; p < graph->GetN(); p++) {
11276 graph->SetPoint(p, graph->GetPointY(p), graph->GetPointX(p));
11277 graph->SetPointError(p, graph->GetErrorYlow(p), graph->GetErrorYhigh(p), graph->GetErrorXlow(p),
11278 graph->GetErrorXhigh(p));
11279 }
11280 for (auto f : *hist->GetListOfFunctions()) {
11281 if (f->InheritsFrom("TH1")) {
11282 // f->Draw("hbarsamemin0");
11283 } /*else if (auto g2 = dynamic_cast<TGraphErrors *>(f)) {
11284 for (int p = 0; p < g2->GetN(); p++) {
11285 g2->SetPoint(p, g2->GetPointY(p), g2->GetPointX(p));
11286 g2->SetPointError(p, g2->GetErrorY(p), _axis->GetNbins());
11287 }
11288 //g2->Draw("3");
11289 } */
11290 else if (auto g = dynamic_cast<TGraph *>(f)) {
11291 for (int p = 0; p < g->GetN(); p++) {
11292 g->SetPoint(p, g->GetPointY(p), g->GetPointX(p));
11293 }
11294 // g->Draw("l");
11295 } else if (auto l = dynamic_cast<TLine *>(f)) {
11296 l->SetX1(l->GetY1());
11297 l->SetX2(l->GetY2());
11298 l->SetY1(_axis->GetXmax());
11299 l->SetY2(_axis->GetXmax());
11300 // l->Draw();
11301 }
11302 }
11303 }
11304
11305 if (!sOpt.Contains("impact")) {
11306 // add labels to graph for unconstrained parameters
11307 for (size_t i = 0; i < ugraphLabels.size(); i++) {
11308 int bin = pNamesHist->GetNbinsX() - ugraphLabels.size() + i + 1;
11309 auto p = dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(pNamesHist->GetXaxis()->GetBinLabel(bin)));
11310 if (!p)
11311 continue;
11312 auto x = graph->GetPointX(graph->GetN() - ugraphLabels.size() + i);
11313 auto y = graph->GetPointY(graph->GetN() - ugraphLabels.size() + i) +
11314 graph->GetErrorYhigh(graph->GetN() - ugraphLabels.size() + i);
11315 auto l = xRooFit::matchPrecision({p->getVal(), p->getError()});
11316 auto t = new TLatex(x, y, TString::Format("%g #pm %g", l.first, l.second));
11317 t->SetBit(kCanDelete);
11318 t->SetTextSize(0.025);
11319 t->SetTextAngle(90);
11320 graph->GetListOfFunctions()->Add(t);
11321 }
11322 }
11323
11324 graph->SetName("pulls");
11325 hist->GetListOfFunctions()->Add(graph, "z0p");
11326 // hist->GetListOfFunctions()->Add(histCopy->Clone(".axis"),(sOpt.Contains("impact") &&
11327 // !doHorizontal)?"axissamey+":"axissame"); // doesn't display right when zoom the axis
11328 if (!hasSame) {
11329 histCopy->Draw((sOpt.Contains("impact") && !doHorizontal)
11330 ? "axisy+"
11331 : "axis"); // draws the axis, called ".axis" for easy access
11332 }
11333 hist->Draw("same");
11334 //
11335 // if(sOpt.Contains("impact")) {
11336 // // make main object the histogram
11337 // auto h = (TH1*)graph->GetHistogram()->Clone("impact");
11338 // graph->GetListOfFunctions()->RemoveAll();
11339 // for(int ii=1;ii<=h->GetNbinsX();ii++) h->SetBinContent(ii,-4);
11340 // h->GetListOfFunctions()->Add(graph,"z0p");
11341 // h->Draw("hbar");
11342 // } else {
11343 // graph->Draw(sOpt.Contains("impact") ? "az0py+" : "az0p");
11344 // }
11345 auto hh = dynamic_cast<TH1 *>(histCopy->Clone(".axiscopy"));
11346 hh->SetDirectory(nullptr);
11347 hh->SetBit(kCanDelete);
11348 hh->Draw(
11349 (sOpt.Contains("impact") && !doHorizontal)
11350 ? "axissamey+"
11351 : "axissame"); // overlay axis again -- important is last so can remove if don't pad->Update before reclear
11352 gPad->Modified();
11353 oldPad->cd();
11354 // gPad->Update();
11355 return;
11356 }
11357
11358 if (get()->InheritsFrom("RooAbsData")) {
11359 auto s = parentPdf();
11360 if (s && s->get<RooSimultaneous>()) {
11361 // drawing dataset associated to a simultaneous means must find subpads with variation names
11362 // may not have subpads if drawning a "Yield" plot ...
11363 bool doneDraw = false;
11364 // in the case of hybrid datasets, the parentPdf will be a reducedPdf ...
11365 // but if the dataset has been added to, then there may be additional entries
11366 // in other channels ... so loop over all labels of the categorical
11367 // and for ones we don't have a channel for, just draw directly on
11368
11369 for (auto [catName, catVal] : s->get<RooSimultaneous>()->indexCat()) {
11370 auto _pad = dynamic_cast<TPad *>(gPad->GetPrimitive(
11371 TString::Format("%s=%s", s->get<RooSimultaneous>()->indexCat().GetName(), catName.c_str())));
11372 if (!_pad)
11373 continue; // channel was hidden?
11374 // attach as a child before calling datasets(), so that if this dataset is external to workspace it is
11375 // included still attaching the dataset ensures dataset reduction for the channel is applied
11376 auto tmp = gPad;
11377 _pad->cd();
11378 if (auto c = s->bins().find(catName)) {
11379 c->push_back(std::make_shared<xRooNode>(*this));
11380 auto ds = c->datasets().find(GetName());
11381 c->resize(c->size() - 1); // remove the child we attached
11382 if (!ds) {
11383 std::cout << " no ds " << GetName() << " - this should never happen!" << std::endl;
11384 continue;
11385 }
11386 ds->Draw(opt);
11387 } else {
11388 Draw(opt);
11389 }
11390 doneDraw = true;
11391 tmp->cd();
11392 }
11393 if (doneDraw) {
11394 gPad->Modified();
11395 return;
11396 }
11397 }
11398
11399 if (!s && hasSame) {
11400 // draw onto all subpads with = in the name
11401 // if has no such subpads, draw onto this pad
11402 bool doneDraw = false;
11403 for (auto o : *gPad->GetListOfPrimitives()) {
11404 if (auto p = dynamic_cast<TPad *>(o); p && TString(p->GetName()).Contains('=')) {
11405 auto _tmp = gPad;
11406 p->cd();
11407 Draw(opt);
11408 _tmp->cd();
11409 doneDraw = true;
11410 }
11411 }
11412 if (doneDraw) {
11413 gPad->Modified();
11414 return;
11415 }
11416 }
11417
11418 auto dataGraph = BuildGraph(v, false, (!s && hasSame) ? gPad : nullptr);
11419 if (!dataGraph)
11420 return;
11421
11422 dataGraph->SetBit(kCanDelete); // will be be deleted when pad is cleared
11423 dataGraph->SetMarkerSize(dataGraph->GetMarkerSize() * gPad->GetWNDC()); // scale marker sizes to pad size
11424
11425 if (s && !s->get<RooAbsPdf>()->canBeExtended()) {
11426 // normalize dataGraph to 1
11427 double tot = 0;
11428 for (int i = 0; i < dataGraph->GetN(); i++)
11429 tot += dataGraph->GetPointY(i);
11430 dataGraph->Scale(1. / tot);
11431 }
11432
11433 if (!hasSame) {
11434 clearPad();
11435 dataGraph->Draw("Az0p");
11436 addLegendEntry(dataGraph, strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(), "pEX0");
11437 gPad->Modified();
11438 // gPad->Update();
11439 return;
11440 }
11441
11442 bool noPoint = false;
11443 if (v && dynamic_cast<RooAbsArg *>(v)->getAttribute("global") && dataGraph->GetN() == 1) {
11444 // global observable ... if graph has only 1 data point line it up on the histogram value
11445 for (auto o : *gPad->GetListOfPrimitives()) {
11446 if (auto h = dynamic_cast<TH1 *>(o);
11447 h && strcmp(h->GetXaxis()->GetName(), dynamic_cast<TObject *>(v)->GetName()) == 0) {
11448 dataGraph->SetPointY(0, h->Interpolate(dataGraph->GetPointX(0)));
11449 noPoint = true;
11450 break;
11451 }
11452 }
11453 }
11454
11455 if (auto _pad = dynamic_cast<TPad *>(gPad->FindObject("auxPad")); _pad) {
11456 if (auto h = dynamic_cast<TH1 *>(_pad->GetPrimitive("auxHist")); h) {
11457 TString histName = h->GetTitle(); // split it by | char
11458 TString histType = histName(histName.Index('|') + 1, histName.Length());
11459 histName = histName(0, histName.Index('|'));
11460 if (auto mainHist = dynamic_cast<TH1 *>(gPad->GetPrimitive(histName));
11461 mainHist && auxFunctions.find(h->GetYaxis()->GetTitle()) != auxFunctions.end()) {
11462 // decide what to do based on title of auxHist (previously used name of y-axis but that changed axis
11463 // behaviour) use title instead
11464 auto ratioGraph = dynamic_cast<TGraphAsymmErrors *>(dataGraph->Clone(dataGraph->GetName()));
11465 ratioGraph->SetBit(kCanDelete);
11466 for (int i = 0; i < ratioGraph->GetN(); i++) {
11467 double val = ratioGraph->GetPointY(i);
11468 int binNum = mainHist->FindFixBin(ratioGraph->GetPointX(i));
11469 double nom = mainHist->GetBinContent(binNum);
11470 double nomerr = mainHist->GetBinError(binNum);
11471 double yval =
11472 std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(ratioGraph->GetPointY(i), nom, nomerr);
11473 double yup = std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(val + ratioGraph->GetErrorYhigh(i),
11474 nom, nomerr) -
11475 yval;
11476 double ydown = yval - std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(
11477 val - ratioGraph->GetErrorYlow(i), nom, nomerr);
11478 if (!std::isnan(yval)) {
11479 ratioGraph->SetPointY(i, yval);
11480 if (!std::isnan(yup))
11481 ratioGraph->SetPointEYhigh(i, yup);
11482 if (!std::isnan(ydown))
11483 ratioGraph->SetPointEYlow(i, ydown);
11484 }
11485 }
11486 // remove the zero points
11487 int i = 0;
11488 while (i < ratioGraph->GetN()) {
11489 if (ratioGraph->GetPointY(i) == 0 && ratioGraph->GetErrorYhigh(i) == 0 &&
11490 ratioGraph->GetErrorYlow(i) == 0) {
11491 ratioGraph->RemovePoint(i);
11492 } else {
11493 i++;
11494 }
11495 }
11496 auto _tmpPad = gPad;
11497 _pad->cd();
11498 ratioGraph->Draw("z0psame");
11500 adjustYRange(minMax.first, minMax.second, h, std::get<1>(auxFunctions[h->GetYaxis()->GetTitle()]));
11501 _tmpPad->cd();
11502 }
11503 }
11504 }
11505
11506 dataGraph->Draw("z0p same");
11507 addLegendEntry((noPoint) ? nullptr : dataGraph, strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(),
11508 noPoint ? "" : "pEX0");
11509
11510 auto minMax = graphMinMax(dynamic_cast<TGraphAsymmErrors *>(dataGraph));
11511 adjustYRange(minMax.first, minMax.second);
11512
11513 gPad->Modified();
11514 // gPad->Update();
11515 return;
11516 }
11517
11518 // auto _ax = GetXaxis();
11519 // auto v = (_ax) ? dynamic_cast<RooRealVar*>(/*possibleObs.first()*/_ax->GetParent()) : nullptr;
11520 // if (!v) { v = get<RooRealVar>(); } // self-axis
11521 // if (!v) return;
11522
11523 if (auto lv = get<RooAbsLValue>(); lv && fParent && fParent->get<RooAbsData>()) {
11524 // drawing an observable from a dataset ... build graph, and exit
11525 auto gr = fParent->BuildGraph(lv, true);
11527 gr->Draw(hasSame ? "P" : "AP");
11528 return;
11529 }
11530
11531 if (forceNames != "") {
11532 // drawing a force plot ... build nll and fill a histogram with force terms
11533 auto _dsets = datasets();
11534 bool _drawn = false;
11535 auto _coords = coords();
11536 auto _fr = fitResult();
11537 auto initPar = dynamic_cast<RooRealVar *>(_fr.get<RooFitResult>()->floatParsInit().find(forceNames));
11538 if (!initPar)
11539 return;
11540 std::vector<double> valuesToDo = {initPar->getVal()};
11541 if (initPar->hasError() || initPar->hasAsymError()) {
11542 valuesToDo.push_back(initPar->getVal() + initPar->getErrorLo());
11543 valuesToDo.push_back(initPar->getVal() + initPar->getErrorHi());
11544 }
11545 int ii = 0;
11546 for (auto valueToDo : valuesToDo) {
11547 ii++;
11548 for (auto &d : _dsets) {
11549 if (!d->get()->TestBit(1 << 20))
11550 continue;
11551 auto emptyHist = BuildHistogram(v, true);
11552 emptyHist->SetBit(kCanDelete);
11553 auto _obs = d->obs();
11554 auto x = _obs.find((v) ? dynamic_cast<TObject *>(v)->GetName() : emptyHist->GetXaxis()->GetName());
11555 auto _nll = nll(d);
11556 auto theData = d->get<RooAbsData>();
11557 int nevent = theData->numEntries();
11558 for (int i = 0; i < nevent; i++) {
11559 theData->get(i);
11560 bool _skip = false;
11561 for (const auto &_c : _coords) {
11562 if (auto cat = _c->get<RooAbsCategoryLValue>(); cat) {
11563 if (cat->getIndex() != theData->get()->getCatIndex(cat->GetName())) {
11564 _skip = true;
11565 break;
11566 }
11567 }
11568 }
11569 if (_skip)
11570 continue;
11571
11572 if (x) {
11573 auto val = _nll.pars()->getRealValue(initPar->GetName());
11574 if (ii > 1)
11575 _nll.pars()->setRealValue(initPar->GetName(), valueToDo);
11576 auto nllVal = _nll.getEntryVal(i);
11577 _nll.pars()->setRealValue(initPar->GetName(), initPar->getVal());
11578 auto nllVal2 = _nll.getEntryVal(i);
11579 _nll.pars()->setRealValue(initPar->GetName(), val);
11580 emptyHist->Fill(x->get<RooAbsReal>()->getVal(), (nllVal2 - nllVal));
11581 }
11582 }
11583 // include the extendedTerm, distributed evenly over the bins
11584 // probably should be somehow dependent on data density though (i.e. bins with more data get more of it?)
11585 auto val = _nll.pars()->getRealValue(initPar->GetName());
11586 if (ii > 1)
11587 _nll.pars()->setRealValue(initPar->GetName(), valueToDo);
11588 auto _extTerm = _nll.extendedTermVal();
11589 _nll.pars()->setRealValue(initPar->GetName(), initPar->getVal());
11590 auto _extTerm2 = _nll.extendedTermVal();
11591 _nll.pars()->setRealValue(initPar->GetName(), val);
11592 for (int i = 1; i <= emptyHist->GetNbinsX(); i++) {
11593 emptyHist->SetBinContent(i,
11594 emptyHist->GetBinContent(i) + (_extTerm2 - _extTerm) / emptyHist->GetNbinsX());
11595 emptyHist->SetBinError(i, 0);
11596 }
11597 emptyHist->GetYaxis()->SetTitle("log (L(#theta)/L(#theta_{0}))");
11598 emptyHist->SetTitle(TString::Format("#theta = %g", (ii > 1) ? valueToDo : val));
11599 if (ii == 1)
11600 emptyHist->SetLineColor(kBlack);
11601 if (ii == 2) {
11602 emptyHist->SetLineColor(kRed);
11603 } else if (ii == 3) {
11604 emptyHist->SetLineColor(kBlue);
11605 }
11606 emptyHist->Draw(_drawn ? "same" : "");
11607 _drawn = true;
11608 }
11609 }
11610 return;
11611 }
11612
11613 auto rar = get<RooAbsReal>();
11614 const xRooNode *rarNode = this;
11615 if (!rar) {
11616 // draw a deleteable clone of the object we wrap (since we might own the object)
11617 get()->DrawClone(opt);
11618 return;
11619 }
11620 // RooAbsReal *sf = nullptr;
11621 if (get()->InheritsFrom("RooExtendPdf")) {
11622 browse();
11623 rarNode = find(".pdf").get();
11624 // rar = rarNode->get<RooAbsReal>();
11625 // sf = find(".n")->get<RooAbsReal>();
11626 }
11627
11628 if (!nostack && !hasOverlay &&
11629 (rarNode->get()->InheritsFrom("RooRealSumPdf") || rarNode->get()->InheritsFrom("RooAddPdf") ||
11630 (v && rarNode->get()->InheritsFrom("RooSimultaneous") &&
11631 strcmp(dynamic_cast<TObject *>(v)->GetName(), rarNode->get<RooSimultaneous>()->indexCat().GetName()) == 0))) {
11632 nostack = false;
11633 } else {
11634 // in all other cases, we do not build a stack
11635 nostack = true;
11636 }
11637
11638 auto h = BuildHistogram(v, false, hasErrorOpt, 1, 0, "", false, false, 0, nullptr, nostack, true /*setInterp*/);
11639 if (!h) {
11640 if (get()) {
11641 // draw a deleteable clone of the object we wrap (since we might own the object)
11642 get()->DrawClone(opt);
11643 }
11644 return;
11645 }
11646 h->SetBit(kCanDelete);
11647
11648 if (!v)
11649 v = getObject<RooAbsLValue>(h->GetXaxis()->IsAlphanumeric() ? h->GetXaxis()->GetTimeFormatOnly()
11650 : h->GetXaxis()->GetName())
11651 .get();
11652 RooAbsArg *vv = (v) ? dynamic_cast<RooAbsArg *>(v) : rar;
11653 if (h->GetXaxis()->IsAlphanumeric()) {
11654 // do this to get bin labels
11655 h->GetXaxis()->SetName("xaxis"); // WARNING -- this messes up anywhere we GetXaxis()->GetName()
11656 }
11657
11658 // get style now, before we mess with histogram title
11659 // auto _styleNode = styles(h);
11660
11661 if (rar->InheritsFrom("RooAbsPdf") && !(rar->InheritsFrom("RooRealSumPdf") || rar->InheritsFrom("RooAddPdf") ||
11662 rar->InheritsFrom("RooSimultaneous"))) {
11663 // append parameter values to title if has such
11664 RooArgSet s;
11665 rar->leafNodeServerList(&s);
11666 if (v)
11667 s.remove(*dynamic_cast<RooAbsArg *>(v));
11668 if (!s.empty()) {
11669 TString ss = h->GetTitle();
11670 ss += " [";
11671 bool first = true;
11672 for (auto _p : s) {
11673 auto _v = dynamic_cast<RooRealVar *>(_p);
11674 if (!_v)
11675 continue;
11676 if (!first)
11677 ss += ",";
11678 first = false;
11679 ss += TString::Format("%s=%g", strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName(), _v->getVal());
11680 if (_v->hasError()) {
11681 ss += TString::Format("#pm %g", _v->getError());
11682 }
11683 }
11684 ss += "]";
11685 h->SetTitle(ss);
11686 }
11687 }
11688
11689 if (!hasSame) {
11690 if (obs().find(vv->GetName())) {
11691 gPad->SetGrid(0, 0);
11692 } else {
11693 gPad->SetGrid(1, 1);
11694 }
11695 }
11696 TString dOpt = h->GetOption();
11697 if (dOpt == "l")
11698 h->SetFillStyle(0);
11699 // // need to strip namespace to discount the "HistFactory" namespace classes from all being treated as binned
11700 // TString clNameNoNamespace = rar->ClassName();
11701 // clNameNoNamespace = clNameNoNamespace(clNameNoNamespace.Last(':') + 1, clNameNoNamespace.Length());
11702 // TString dOpt = (clNameNoNamespace.Contains("Hist") || vv->isCategory() || rar->isBinnedDistribution(*vv) ||
11703 // h->GetNbinsX() == 1 || rar->getAttribute("BinnedLikelihood") ||
11704 // (dynamic_cast<RooAbsRealLValue *>(vv) &&
11705 // std::unique_ptr<std::list<double>>(rar->binBoundaries(*dynamic_cast<RooAbsRealLValue *>(vv),
11706 // -std::numeric_limits<double>::infinity(),
11707 // std::numeric_limits<double>::infinity()))))
11708 // ? ""
11709 // : "LF2";
11710 // if (auto d = dynamic_cast<RooHistFunc *>(rar); d && !d->isBinnedDistribution(*vv) && h->GetNbinsX() != 1) {
11711 // dOpt = "LF2"; // hist func is interpolated, so draw it as such
11712 // }
11713 // if (dOpt == "LF2" && !components().empty()) {
11714 // // check if all components of dOpt are "Hist" type (CMS model support)
11715 // // if so then dOpt="";
11716 // bool allHist = true;
11717 // for (auto &s : components()) {
11718 // TString _clName = s->get()->ClassName();
11719 // _clName = _clName(_clName.Last(':') + 1, _clName.Length());
11720 // if (!(s->get() && _clName.Contains("Hist"))) {
11721 // allHist = false;
11722 // break;
11723 // }
11724 // }
11725 // if (allHist)
11726 // dOpt = "";
11727 // }
11728 //
11729 // if(dOpt=="LF2") {
11730 // // ensure any sub hists have lf2 option
11731 // TObjLink *lnk = h->GetListOfFunctions()->FirstLink();
11732 // while (lnk) {
11733 // if(auto hh = dynamic_cast<TH1*>(lnk->GetObject())) {
11734 // if(TString(hh->GetName())=="band" && TString(lnk->GetOption())=="e2same") {
11735 // lnk->SetOption("LF2 e3same");
11736 // } else if(TString(hh->GetName())=="nominal") {
11737 // lnk->SetOption("L same");
11738 // }
11739 // }
11740 // lnk = lnk->Next();
11741 // }
11742 // }
11743
11744 if (rar == vv && rar->IsA() == RooRealVar::Class()) {
11745 dOpt += "TEXT";
11746 // add a TExec to the histogram so that when edited it will propagate to var
11747 gROOT->SetEditHistograms(true);
11748 } else {
11749 gROOT->SetEditHistograms(false);
11750 }
11751
11752 if (hasSame) {
11753 dOpt += " same";
11754 } else {
11755 hAxis = h;
11756 }
11757
11758 if (dOpt.Contains("TEXT") || sOpt.Contains("text")) {
11759 // adjust marker size so text is good
11760 h->SetMarkerSize(gStyle->GetLabelSize("Z") / (0.02 * gPad->GetHNDC()));
11761 }
11762
11763 bool hasError(false);
11764 for (int i = 0; i < h->GetSumw2N(); i++) {
11765 if (h->GetSumw2()->At(i)) {
11766 hasError = true;
11767 break;
11768 }
11769 }
11770
11771 /** This doesn't seem necessary in at least 6.26 any more - pads seem adjusted on their own
11772 if (!hasSame && h->GetYaxis()->GetTitleFont()%10 == 2) {
11773 h->GetYaxis()->SetTitleOffset( gPad->GetLeftMargin() / gStyle->GetPadLeftMargin() );
11774 } */
11775 // don't this instead - dont want to leave as zero (auto) in case show aux plot
11776 if (!hasSame && h->GetYaxis()->GetTitleFont() % 10 == 2) {
11777 h->GetYaxis()->SetTitleOffset(1.);
11778 }
11779
11780 TH1 *errHist = nullptr;
11781
11782 if (!hasSame)
11783 clearPad();
11784
11785 if (rar->getAttribute("Logy")) {
11786 gPad->SetLogy(1);
11787 }
11788
11789 if (rar == vv && rar->IsA() == RooRealVar::Class()) {
11790 // add a TExec to the histogram so that when edited it will propagate to var
11791 // h->GetListOfFunctions()->Add(h->Clone("self"),"TEXTHIST");
11792 dOpt = "TEXT";
11793 auto node = new xRooNode(*this);
11794 auto _hist = (errHist) ? errHist : h;
11795 auto hCopy = (errHist) ? nullptr : dynamic_cast<TH1 *>(h->Clone());
11796 if (hCopy) {
11797 hCopy->Reset();
11798 hCopy->Add(_hist);
11799 hCopy->SetDirectory(nullptr);
11800 }
11801 _hist->GetListOfFunctions()->Add(node);
11802 _hist->GetListOfFunctions()->Add(new TExec(
11803 ".update",
11805 "gROOT->SetEditHistograms(true);auto h = dynamic_cast<TH1*>(gPad->GetPrimitive(\"%s\")); if(h) { double "
11806 "range= h->GetMaximum()-h->GetMinimum(); if(auto n "
11807 "= dynamic_cast<xRooNode*>(h->GetListOfFunctions()->FindObject(\"%s\")); n && "
11808 "n->TestBit(TObject::kNotDeleted) && n->get<RooRealVar>()->getVal() != h->GetBinContent(1)) {"
11809 "h->SetBinContent(1, "
11810 "TString::Format(\"%%.2g\",int(h->GetBinContent(1)/(range*0.01))*range*0.01).Atof());n->SetContent( "
11811 "h->GetBinContent(1) ); for(auto pp : *h->GetListOfFunctions()) if(auto hh = "
11812 "dynamic_cast<TH1*>(pp))hh->SetBinContent(1,h->GetBinContent(1));} if(h->GetBinContent(1)==0.) "
11813 "h->SetBinContent(1,range*0.005); gPad->Modified();gPad->Update(); }",
11814 _hist->GetName(), node->GetName())));
11815 if (errHist) {
11816 errHist->GetListOfFunctions()->Add(h, "TEXT HIST same");
11817 errHist->SetFillColor(h->GetLineColor());
11818 } else {
11819 hCopy->SetBit(kCanDelete);
11820 hCopy->SetFillStyle(0);
11821 _hist->GetListOfFunctions()->Add(hCopy, "TEXT HIST same");
11822 //_hist->SetBinError(1, 0);
11823 }
11824 _hist->SetStats(false);
11825 // if (_hist->GetBinContent(1)==0.) _hist->SetBinContent(1,(_hist->GetMaximum()-_hist->GetMinimum())*0.005);
11826 _hist->Draw(); //_hist->Draw(((hasError) ? "e2" : ""));
11827 gPad->Modified();
11828 return;
11829 }
11830
11831 bool overlayExisted = false;
11832 if (hasOverlay) {
11833 h->SetName(TString::Format("%s%s", h->GetName(), overlayName.Data()));
11834 if (auto existing = dynamic_cast<TH1 *>(gPad->GetPrimitive(h->GetName())); existing) {
11835 existing->Reset();
11836 existing->Add(h);
11837 delete h;
11838 h = existing;
11839 overlayExisted = true;
11840 } else {
11841 TString oldStyle = (rar && rar->getStringAttribute("style")) ? rar->getStringAttribute("style") : "";
11842 h->SetTitle(overlayName);
11843 // for overlays will take style from current gStyle before overriding with personal style
11844 // this ensures initial style will be whatever gStyle is, rather than whatever ours is
11845 static_cast<TAttLine &>(*h) = *gStyle;
11846 static_cast<TAttFill &>(*h) = *gStyle;
11847 static_cast<TAttMarker &>(*h) = *gStyle;
11848 h->SetFillStyle(0); // explicit default for overlays will be transparent fill
11849
11850 // std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case
11851 // getObject has decided to return the owning ptr (for some reason) if
11852 // (!gROOT->GetStyle(h->GetTitle())) {
11853 // if ( (style = getObject<TStyle>(h->GetTitle())) ) {
11854 // // loaded style (from workspace?) so put in list and use that
11855 // gROOT->GetListOfStyles()->Add(style.get());
11856 // } else {
11857 // // create new style - gets put in style list automatically so don't have to delete
11858 // // acquire them so saved to workspaces for auto reload ...
11859 // style = acquireNew<TStyle>(h->GetTitle(),
11860 // TString::Format("Style for %s component", h->GetTitle()));
11861 // (TAttLine &) (*style) = *dynamic_cast<TAttLine *>(h);
11862 // (TAttFill &) (*style) = *dynamic_cast<TAttFill *>(h);
11863 // (TAttMarker &) (*style) = *dynamic_cast<TAttMarker *>(h);
11864 // gROOT->GetListOfStyles()->Add(style.get());
11865 // }
11866 // }
11867 // (TAttLine&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
11868 // (TAttFill&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
11869 // (TAttMarker&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
11870 auto _styleNode = styles(h);
11871 rar->setStringAttribute("style", oldStyle == "" ? nullptr : oldStyle.Data()); // restores old style
11872 if (auto _style = _styleNode.get<TStyle>()) {
11873 (TAttLine &)(*h) = *_style;
11874 (TAttFill &)(*h) = *_style;
11875 (TAttMarker &)(*h) = *_style;
11876 }
11877 h->Draw(dOpt == "LF2" ? "e3" : dOpt);
11878 if (errHist) {
11879 errHist->SetTitle(overlayName);
11880 (TAttLine &)(*errHist) = *h;
11881 errHist->SetFillColor(h->GetLineColor());
11882 }
11883 }
11884 } else {
11885 // if (auto _style = _styleNode.get<TStyle>()) {
11886 // (TAttLine &)(*h) = *_style;
11887 // (TAttFill &)(*h) = *_style;
11888 // (TAttMarker &)(*h) = *_style;
11889 // if (errHist) {
11890 // (TAttLine &)(*errHist) = *h;
11891 // errHist->SetFillColor(h->GetLineColor());
11892 // }
11893 // }
11894 h->Draw(dOpt);
11895 }
11896
11897 if (!hasOverlay && (rarNode->get()->InheritsFrom("RooRealSumPdf") || rarNode->get()->InheritsFrom("RooAddPdf") ||
11898 (rarNode->get()->InheritsFrom("RooSimultaneous") &&
11899 strcmp(vv->GetName(), rarNode->get<RooSimultaneous>()->indexCat().GetName()) == 0))) {
11900 if (auto stack = dynamic_cast<THStack *>(h->FindObject("stack"))) {
11901 // access the stack and set draw options, adjust ranges etc
11902 TObjLink *lnk = stack->GetHists()->FirstLink();
11903 while (lnk) {
11904 TH1 *hh = static_cast<TH1 *>(lnk->GetObject());
11905 // lnk->SetOption(dOpt); - not needed
11906 auto hhMin = (hh->GetMinimum() == 0) ? hh->GetMinimum(1e-9) : hh->GetMinimum();
11907 if (lnk == stack->GetHists()->FirstLink() && h->GetMinimum() > hhMin) {
11908 auto newMin = hhMin - (h->GetMaximum() - hhMin) * gStyle->GetHistTopMargin();
11909 if (hhMin >= 0 && newMin < 0)
11910 newMin = hhMin * 0.99;
11911 adjustYRange(newMin, h->GetMaximum());
11912 }
11913 addLegendEntry(hh, hh->GetTitle(), "f");
11914 lnk = lnk->Next();
11915 }
11916 }
11917
11918 // // build a stack unless not requested
11919 // if (!nostack) {
11920 // THStack *stack = new THStack(TString::Format("%s_stack", rar->GetName()),
11921 // TString::Format("%s;%s", rar->GetTitle(), h->GetXaxis()->GetTitle()));
11922 // int count = 2;
11923 // std::map<std::string, int> colorByTitle; // TODO: should fill from any existing legend
11924 // std::set<std::string> allTitles;
11925 // bool titleMatchName = true;
11926 // std::map<std::string, TH1 *> histGroups;
11927 // std::vector<TH1 *> hhs;
11928 // std::set<TH1 *> histsWithBadTitles; // these histograms will have their titles autoFormatted
11929 //
11930 // // support for CMS model case where has single component containing many coeffs
11931 // // will build stack by setting each coeff equal to 0 in turn, rebuilding the histogram
11932 // // the difference from the "full" histogram will be the component
11933 // RooArgList cms_coefs;
11934 // if (!rarNode->components().empty()) {
11935 // auto comps = rarNode->components()[0];
11936 // for (auto &c : *comps) {
11937 // if (c->fFolder == "!.coeffs")
11938 // cms_coefs.add(*c->get<RooAbsArg>());
11939 // }
11940 // }
11941 // if (!cms_coefs.empty()) {
11942 // RooRealVar zero("zero", "", 0);
11943 // std::shared_ptr<TH1> prevHist(static_cast<TH1 *>(h->Clone()));
11944 // for (auto c : cms_coefs) {
11945 // // seems I have to remake the function each time, as haven't figured out what cache needs
11946 // clearing? std::unique_ptr<RooAbsReal> f(
11947 // dynamic_cast<RooAbsReal *>(rarNode->components()[0]->get()->Clone("tmpCopy")));
11948 // zero.setAttribute(
11949 // Form("ORIGNAME:%s", c->GetName())); // used in redirectServers to say what this
11950 // replaces
11951 // f->redirectServers(RooArgSet(zero), false, true); // each time will replace one additional coef
11952 // // zero.setAttribute(Form("ORIGNAME:%s",c->GetName()),false); (commented out so that on next
11953 // iteration
11954 // // will still replace all prev)
11955 // auto hh = xRooNode(*f, *this).BuildHistogram(v);
11956 // hh->SetName(c->GetName());
11957 // if (sf)
11958 // hh->Scale(sf->getVal());
11959 // if (strlen(hh->GetTitle()) == 0) {
11960 // hh->SetTitle(c->GetName()); // ensure all hists has titles
11961 // histsWithBadTitles.insert(hh);
11962 // } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
11963 // histsWithBadTitles.insert(hh);
11964 // }
11965 // titleMatchName &= (TString(c->GetName()) == hh->GetTitle() ||
11966 // TString(hh->GetTitle()).BeginsWith(TString(c->GetName()) + "_"));
11967 // std::shared_ptr<TH1> nextHist(static_cast<TH1 *>(hh->Clone()));
11968 // hh->Add(prevHist.get(), -1.);
11969 // hh->Scale(-1.);
11970 // hhs.push_back(hh);
11971 // prevHist = nextHist;
11972 // }
11973 // } else if (get<RooSimultaneous>()) {
11974 // // need to create a histogram for each sample across all the channels - will rely on functionality
11975 // below to
11976 // // merge them based on titles
11977 //
11978 // for (auto &chan : bins()) {
11979 // TString chanName(chan->GetName());
11980 // chanName = chanName(chanName.Index("=") + 1, chanName.Length());
11981 // auto samps = chan->mainChild();
11982 // if (!samps)
11983 // samps = *chan;
11984 // for (auto &samp : samps.components()) {
11985 // auto hh = static_cast<TH1 *>(h->Clone(samp->GetName()));
11986 // hh->Reset();
11987 // hh->SetTitle(samp->GetTitle());
11988 // if (strlen(hh->GetTitle()) == 0) {
11989 // hh->SetTitle(samp->GetName());
11990 // histsWithBadTitles.insert(hh);
11991 // } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
11992 // histsWithBadTitles.insert(hh);
11993 // }
11994 // hh->SetTitle(TString(hh->GetTitle())
11995 // .ReplaceAll(TString(chan->get()->GetName()) + "_",
11996 // "")); // remove occurance of channelname_ in title (usually
11997 // prefix)
11998 // titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
11999 // TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
12000 // hh->SetBinContent(hh->GetXaxis()->FindFixBin(chanName), samp->GetContent());
12001 // hhs.push_back(hh);
12002 // }
12003 // }
12004 // } else {
12005 // for (auto &samp : rarNode->components()) {
12006 // auto hh = samp->BuildHistogram(v,false,false,1,0,"",false,false,0,h); // passing h to ensure
12007 // binning is the same for all subcomponent hists if (sf)
12008 // hh->Scale(sf->getVal());
12009 // hhs.push_back(hh);
12010 // if (strlen(hh->GetTitle()) == 0) {
12011 // hh->SetTitle(samp->GetName()); // ensure all hists has titles
12012 // histsWithBadTitles.insert(hh);
12013 // } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
12014 // histsWithBadTitles.insert(hh);
12015 // }
12016 // titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
12017 // TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
12018 // }
12019 // }
12020 //
12021 // if (!hhs.empty()) {
12022 // for (auto &hh : hhs) {
12023 // allTitles.insert(hh->GetTitle());
12024 // }
12025 //
12026 // // get common prefix to strip off only if all titles match names and
12027 // // any title is longer than 10 chars
12028 // size_t e = std::min(allTitles.begin()->size(), allTitles.rbegin()->size());
12029 // size_t ii = 0;
12030 // bool goodPrefix = false;
12031 // std::string commonSuffix;
12032 // if (titleMatchName && hhs.size() > 1) {
12033 // while (ii < e - 1 && allTitles.begin()->at(ii) == allTitles.rbegin()->at(ii)) {
12034 // ii++;
12035 // if (allTitles.begin()->at(ii) == '_' || allTitles.begin()->at(ii) == ' ')
12036 // goodPrefix = true;
12037 // }
12038 //
12039 // // find common suffix if there is one .. must start with a "_"
12040 // bool stop = false;
12041 // while (!stop && commonSuffix.size() < size_t(e - 1)) {
12042 // commonSuffix = allTitles.begin()->substr(allTitles.begin()->length() - commonSuffix.length() -
12043 // 1); for (auto &t : allTitles) {
12044 // if (!TString(t).EndsWith(commonSuffix.c_str())) {
12045 // commonSuffix = commonSuffix.substr(1);
12046 // stop = true;
12047 // break;
12048 // }
12049 // }
12050 // }
12051 // if (commonSuffix.find('_') == std::string::npos) {
12052 // commonSuffix = "";
12053 // } else {
12054 // commonSuffix = commonSuffix.substr(commonSuffix.find('_'));
12055 // }
12056 // }
12057 // if (!goodPrefix)
12058 // ii = 0;
12059 //
12060 // // also find how many characters are needed to distinguish all entries (that dont have the same
12061 // name)
12062 // // then carry on up to first space or underscore
12063 // size_t jj = 0;
12064 // std::map<std::string, std::string> reducedTitles;
12065 // while (reducedTitles.size() != allTitles.size()) {
12066 // jj++;
12067 // std::map<std::string, int> titlesMap;
12068 // for (auto &s : allTitles) {
12069 // if (reducedTitles.count(s))
12070 // continue;
12071 // titlesMap[s.substr(0, jj)]++;
12072 // }
12073 // for (auto &s : allTitles) {
12074 // if (titlesMap[s.substr(0, jj)] == 1 && (jj >= s.length() || s.at(jj) == ' ' || s.at(jj) ==
12075 // '_')) {
12076 // reducedTitles[s] = s.substr(0, jj);
12077 // }
12078 // }
12079 // }
12080 //
12081 // // strip common prefix and suffix before adding
12082 // for (auto ritr = hhs.rbegin(); ritr != hhs.rend(); ++ritr) { // go in reverse order
12083 // if (!histsWithBadTitles.count((*ritr))) {
12084 // continue;
12085 // }
12086 // auto _title = (hhs.size() > 5) ? reducedTitles[(*ritr)->GetTitle()] : (*ritr)->GetTitle();
12087 // _title = _title.substr(ii < _title.size() ? ii : 0);
12088 // if (!commonSuffix.empty() && TString(_title).EndsWith(commonSuffix.c_str()))
12089 // _title = _title.substr(0, _title.length() - commonSuffix.length());
12090 // (*ritr)->SetTitle(_title.c_str());
12091 // }
12092 // }
12093 //
12094 // for (auto &hh : hhs) {
12095 // // automatically group hists that all have the same title
12096 // if (histGroups.find(hh->GetTitle()) == histGroups.end()) {
12097 // histGroups[hh->GetTitle()] = hh;
12098 // } else {
12099 // // add it into this group
12100 // histGroups[hh->GetTitle()]->Add(hh);
12101 // delete hh;
12102 // hh = nullptr;
12103 // continue;
12104 // }
12105 // auto hhMin = (hh->GetMinimum() == 0) ? hh->GetMinimum(1e-9) : hh->GetMinimum();
12106 // if (!stack->GetHists() && h->GetMinimum() > hhMin) {
12107 // auto newMin = hhMin - (h->GetMaximum() - hhMin) * gStyle->GetHistTopMargin();
12108 // if (hhMin >= 0 && newMin < 0)
12109 // newMin = hhMin * 0.99;
12110 // adjustYRange(newMin, h->GetMaximum());
12111 // }
12112 //
12113 // /*if(stack->GetHists() && stack->GetHists()->GetEntries()>0) {
12114 // // to remove rounding effects on bin boundaries, see if binnings compatible
12115 // auto _h1 = dynamic_cast<TH1*>(stack->GetHists()->At(0));
12116 // if(_h1->GetNbinsX()==hh->GetNbinsX()) TODO ... finish dealing with silly rounding effects
12117 // }*/
12118 // TString thisOpt = dOpt;
12119 // // uncomment next line to blend continuous with discrete components .. get some unpleasant "poke
12120 // through"
12121 // // effects though
12122 // // if(auto s = samp->get<RooAbsReal>(); s) thisOpt =
12123 // s->isBinnedDistribution(*dynamic_cast<RooAbsArg*>(v)) ?
12124 // // "" : "LF2";
12125 // stack->Add(hh, thisOpt);
12126 // }
12127 // stack->SetBit(kCanDelete); // should delete its sub histograms
12128 // stack->Draw("noclear same");
12129 // h->Draw(
12130 // dOpt + sOpt +
12131 // "same"); // overlay again .. if stack would cover original hist (negative components) we still see
12132 // integral
12133 // h->Draw("axissame"); // redraws axis
12134 //
12135 // TList *ll = stack->GetHists();
12136 // if (ll && ll->GetEntries()) {
12137 //
12138 // // finally, ensure all hists are styled
12139 // for (auto ho : *ll) {
12140 // TH1 *hh = dynamic_cast<TH1 *>(ho);
12141 // if (!hh)
12142 // continue;
12143 // bool createdStyle = (xRooNode(*hh, *this).styles(nullptr, false).get<TStyle>() == nullptr);
12144 //
12145 // if (createdStyle) {
12146 // // give hist a color, that isn't the same as any other hists color
12147 // hh->SetFillStyle(1001); // solid fill style
12148 // bool used = false;
12149 // do {
12150 // hh->SetFillColor((count++));
12151 // // check not already used this color
12152 // used = false;
12153 // for (auto ho2 : *ll) {
12154 // TH1 *hh2 = dynamic_cast<TH1 *>(ho2);
12155 // if (!hh2)
12156 // continue;
12157 // auto _styleNode = xRooNode(*hh2, *this).styles(hh2, false);
12158 // auto _style = _styleNode.get<TStyle>();
12159 // if (hh != hh2 && _style && _style->GetFillColor() == hh->GetFillColor()) {
12160 // used = true;
12161 // break;
12162 // }
12163 // }
12164 // } while (used);
12165 // }
12166 //
12167 // auto _styleNode = xRooNode(*hh, *this).styles(hh);
12168 // if (auto _style = _styleNode.get<TStyle>()) {
12169 // *dynamic_cast<TAttLine *>(hh) = *_style;
12170 // *dynamic_cast<TAttFill *>(hh) = *_style;
12171 // *dynamic_cast<TAttMarker *>(hh) = *_style;
12172 // }
12173 // // for stacks, fill color of white should be color 10 unless fill style is 0
12174 // if (hh->GetFillColor() == kWhite && hh->GetFillStyle() != 0) {
12175 // // kWhite means 'transparent' in ROOT ... should really use a FillStyle of 0 for that
12176 // // so assume user wanted actual white, which is color 10
12177 // hh->SetFillColor(10);
12178 // }
12179 // addLegendEntry(hh, hh->GetTitle(), "f");
12180 // }
12181 // }
12182 // }
12183 } else if (!overlayExisted) {
12184
12185 if (errHist) {
12186 addLegendEntry(errHist, strlen(errHist->GetTitle()) ? errHist->GetTitle() : GetName(), "fl");
12187 } else {
12188 addLegendEntry(h, strlen(h->GetTitle()) ? h->GetTitle() : GetName(), (hasError) ? "fl" : "l");
12189 }
12190 }
12191
12192 if (errHist) {
12193 dOpt.ReplaceAll("TEXT", "");
12194 errHist->Draw(dOpt + (dOpt.Contains("LF2") ? "e3same" : "e2same"));
12195 double ymax = -std::numeric_limits<double>::infinity();
12196 double ymin = std::numeric_limits<double>::infinity();
12197 for (int i = 1; i <= errHist->GetNbinsX(); i++) {
12198 ymax = std::max(ymax, errHist->GetBinContent(i) + errHist->GetBinError(i));
12199 ymin = std::min(ymin, errHist->GetBinContent(i) - errHist->GetBinError(i));
12200 }
12202 } else {
12203 adjustYRange(h->GetMinimum() * 0.9, h->GetMaximum() * 1.1);
12204 }
12205
12206 if ((!auxPlotTitle.empty()) && !hasSame) {
12207 // create a pad for the ratio ... shift the bottom margin of this pad to make space for it
12208 double padFrac = 0.3;
12209 auto _tmpPad = gPad;
12210 gPad->SetBottomMargin(padFrac);
12211 auto ratioPad = new TPad("auxPad", "aux plot", 0, 0, 1, padFrac);
12212 ratioPad->SetFillColor(_tmpPad->GetFillColor());
12213 ratioPad->SetNumber(1);
12214 ratioPad->SetBottomMargin(ratioPad->GetBottomMargin() / padFrac);
12215 ratioPad->SetTopMargin(0.04);
12216 ratioPad->SetLeftMargin(gPad->GetLeftMargin());
12217 ratioPad->SetRightMargin(gPad->GetRightMargin());
12218 ratioPad->cd();
12219 TH1 *ratioHist = dynamic_cast<TH1 *>((errHist) ? errHist->Clone("auxHist") : h->Clone("auxHist"));
12220 ratioHist->Reset();
12221 ratioHist->Add(h); // removes function list
12222 ratioHist->SetDirectory(nullptr);
12223 ratioHist->SetTitle((errHist) ? errHist->GetName()
12224 : h->GetName()); // abuse the title string to hold the name of the main hist
12225
12226 ratioHist->GetYaxis()->SetNdivisions(5, 0, 0);
12227 ratioHist->GetYaxis()->SetTitle(auxPlotTitle.c_str());
12228 ratioHist->SetTitle(
12229 TString::Format("%s|%s", ratioHist->GetTitle(),
12230 auxPlotTitle.c_str())); // used when plotting data (above) to decide what to calculate
12231 ratioHist->SetMaximum();
12232 ratioHist->SetMinimum(); // resets min and max
12233 ratioPad->SetGridy();
12234
12235 for (int i = 1; i <= ratioHist->GetNbinsX(); i++) {
12236 double val = ratioHist->GetBinContent(i);
12237 double err = ratioHist->GetBinError(i);
12238 ratioHist->SetBinContent(i, std::get<0>(auxFunctions[auxPlotTitle])(val, val, err));
12239 ratioHist->SetBinError(i, std::get<0>(auxFunctions[auxPlotTitle])(val + err, val, err) -
12240 ratioHist->GetBinContent(i));
12241 }
12242
12243 double rHeight = (1. - padFrac) / padFrac; //(_tmpPad->GetWNDC())/(gPad->GetHNDC());
12244 if (ratioHist->GetYaxis()->GetTitleFont() % 10 == 2) {
12245 ratioHist->GetYaxis()->SetTitleSize(ratioHist->GetYaxis()->GetTitleSize() * rHeight);
12246 ratioHist->GetYaxis()->SetLabelSize(ratioHist->GetYaxis()->GetLabelSize() * rHeight);
12247 ratioHist->GetXaxis()->SetTitleSize(ratioHist->GetXaxis()->GetTitleSize() * rHeight);
12248 ratioHist->GetXaxis()->SetLabelSize(ratioHist->GetXaxis()->GetLabelSize() * rHeight);
12249 ratioHist->GetYaxis()->SetTitleOffset(ratioHist->GetYaxis()->GetTitleOffset() / rHeight);
12250 } else {
12251#if ROOT_VERSION_CODE < ROOT_VERSION(6, 26, 00)
12252 ratioHist->GetYaxis()->SetTitleOffset(ratioHist->GetYaxis()->GetTitleOffset() / rHeight);
12253#endif
12254 }
12255 ratioHist->GetXaxis()->SetTickLength(ratioHist->GetXaxis()->GetTickLength() * rHeight);
12256 ratioHist->SetStats(false);
12257 ratioHist->SetBit(TH1::kNoTitle);
12258 ratioHist->SetBit(kCanDelete);
12259 if (errHist) {
12260 auto _h = dynamic_cast<TH1 *>(ratioHist->Clone("auxHist_clone"));
12261 _h->SetDirectory(nullptr);
12262 _h->SetFillColor(0);
12263 ratioHist->GetListOfFunctions()->Add(_h, "histsame");
12264 //_h->Draw("histsame");
12265 }
12266 ratioHist->GetListOfFunctions()->Add(new TExec(
12267 ".updateAxis",
12268 TString::Format("auto h1 = (TH1*)%p; auto h2 = (TH1*)%p; if(h2->GetXaxis()->GetFirst() != "
12269 "h1->GetXaxis()->GetFirst() || h1->GetXaxis()->GetLast()!=h2->GetXaxis()->GetLast()) "
12270 "{h2->GetXaxis()->SetRange(h1->GetXaxis()->GetFirst(),h1->GetXaxis()->GetLast());if(gPad) "
12271 "{gPad->GetCanvas()->Paint();gPad->GetCanvas()->Update();}}",
12272 (void *)ratioHist, (void *)(h))));
12273 ratioHist->Draw((errHist ? "e2" : ""));
12274
12275 _tmpPad->cd();
12276 ratioPad->Draw();
12277 } else if (auto ratioPad = dynamic_cast<TPad *>(gPad->GetPrimitive("auxPad")); hasSame && ratioPad) {
12278 // need to draw histogram in the ratio pad ...
12279 // if doing overlay need to update histogram
12280
12281 if (auto hr = dynamic_cast<TH1 *>(ratioPad->GetPrimitive("auxHist"));
12282 hr && auxFunctions.find(hr->GetYaxis()->GetTitle()) != auxFunctions.end()) {
12283 TString histName = hr->GetTitle(); // split it by | char
12284 TString histType = histName(histName.Index('|') + 1, histName.Length());
12285 histName = histName(0, histName.Index('|'));
12286
12287 if (auto hnom = dynamic_cast<TH1 *>(gPad->GetPrimitive(histName)); hnom) {
12288 h = dynamic_cast<TH1 *>(h->Clone(h->GetName()));
12289 h->SetDirectory(nullptr);
12290 h->SetBit(kCanDelete);
12291 for (int i = 1; i <= hnom->GetNbinsX(); i++) {
12292 double val = h->GetBinContent(i);
12293 double err = h->GetBinError(i);
12294 h->SetBinContent(i, std::get<0>(auxFunctions[hr->GetYaxis()->GetTitle()])(
12295 h->GetBinContent(i), hnom->GetBinContent(i), hnom->GetBinError(i)));
12296 h->SetBinError(i, std::get<0>(auxFunctions[hr->GetYaxis()->GetTitle()])(
12297 val + err, hnom->GetBinContent(i), hnom->GetBinError(i)) -
12298 h->GetBinContent(i));
12299 }
12300 auto _tmpPad = gPad;
12301 ratioPad->cd();
12302 if (hasOverlay) {
12303 if (auto existing = dynamic_cast<TH1 *>(ratioPad->GetPrimitive(h->GetName())); existing) {
12304 existing->Reset();
12305 existing->Add(h);
12306 delete h;
12307 h = existing;
12308 overlayExisted = true;
12309 } else {
12310 h->Draw(dOpt);
12311 }
12312 } else {
12313 h->Draw(dOpt);
12314 }
12315 double ymax = -std::numeric_limits<double>::infinity();
12316 double ymin = std::numeric_limits<double>::infinity();
12317 for (int i = 1; i <= h->GetNbinsX(); i++) {
12318 ymax = std::max(ymax, h->GetBinContent(i) + h->GetBinError(i));
12319 ymin = std::min(ymin, h->GetBinContent(i) - h->GetBinError(i));
12320 }
12321 adjustYRange(ymin, ymax, hr, std::get<1>(auxFunctions[hr->GetYaxis()->GetTitle()]));
12322 // adjustYRange(h->GetMinimum() * (h->GetMinimum()<0 ? 1.1 : 0.9), h->GetMaximum() * (h->GetMinimum()<0 ?
12323 // 0.9 : 1.1), hr, std::get<1>(auxFunctions[hr->GetYaxis()->GetTitle()]));
12324 gPad->Modified();
12325 _tmpPad->cd();
12326 }
12327 }
12328 }
12329
12330 // see if it's in a simultaneous so need to select a cat
12331 /*auto _parent = fParent;
12332 auto _me = rar;
12333 while(_parent) {
12334 if (auto s = _parent->get<RooSimultaneous>(); s) {
12335 for (auto c : s->indexCat()) {
12336 if (auto p = s->getPdf(c.first.c_str());_me==p) {
12337 gPad->SetName(c.first.c_str());
12338 break;
12339 }
12340 }
12341 break;
12342 }
12343 _me = _parent->get<RooAbsReal>();
12344 _parent = _parent->fParent;
12345 }*/
12346
12347 // now draw selected datasets on top if this was a pdf
12348 if (auto _pdf = get<RooAbsPdf>();
12349 !hasSame && _pdf /*&& (_pdf->canBeExtended() || robs().empty())*/ && coefs(true).empty()) {
12350 auto _dsets = datasets();
12351 // bool _drawn=false;
12352 for (auto &d : _dsets) {
12353 if (d->get()->TestBit(1 << 20)) {
12354 d->Draw("same");
12355 //_drawn=true;
12356 }
12357 }
12358 // if (!_drawn && !_dsets.empty()) _dsets[0]->Draw("same"); // always draw if has a dataset
12359 }
12360
12361 gPad->Modified();
12362 // gPad->Update();
12363 getLegend();
12364 gPad->Modified();
12365 // gPad->Update();
12366}
12367
12368void xRooNode::SaveAs(const char *filename, Option_t *option) const
12369{
12371 sOpt.ToLower();
12374 if (sFilename.Contains(".root:")) {
12375 objName = TString(sFilename(sFilename.Index(".root:") + 6, sFilename.Length()));
12376 sFilename = sFilename(0, sFilename.Index(".root:") + 5);
12377 }
12378
12379 if (auto pdf = get<RooAbsPdf>(); pdf) {
12380 // if saving a pdf, will put it inside a workspace, with its datasets, and a modelconfig
12381 // then save the workspace
12382 RooWorkspace w(objName, TString::Format("Workspace of %s", GetTitle()));
12383 xRooNode ws(w);
12384 auto addedPdf = ws.Add(*this);
12385 for (auto ds : datasets()) {
12386 ws.Add(*ds);
12387 }
12388 RooStats::ModelConfig mc("ModelConfig", GetTitle(), &w);
12389 mc.SetPdf(addedPdf->GetName());
12390 mc.SetObservables(*addedPdf.robs().get<RooArgList>());
12391 mc.SetGlobalObservables(*addedPdf.globs().get<RooArgList>());
12392 mc.SetNuisanceParameters(*addedPdf.np().get<RooArgList>());
12393 mc.SetParametersOfInterest(*addedPdf.poi().get<RooArgList>());
12394 ws.Add(mc);
12395
12396 // save the workspace
12398
12399 } else if (auto w = get<RooWorkspace>(); w) {
12400 // ensure the current color set is saved in the workspace
12401 w->import(*gROOT->GetListOfColors(), true);
12402
12403 if (sFilename.EndsWith(".json")) {
12404#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
12405 // stream with json tool
12407 if (tool.exportJSON(sFilename.Data())) {
12408 Info("SaveAs", "%s saved to %s", w->GetName(), sFilename.Data());
12409 } else {
12410 Error("SaveAs", "Unable to save to %s", sFilename.Data());
12411 }
12412#else
12413 Error("SaveAs", "json format workspaces only in ROOT 6.26 onwards");
12414#endif
12415 return;
12416 }
12417
12418#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
12419 // before saving, clear the eocache of all owned nodes
12420 // because causes memory leak when read back in (workspace streamer immediately overwrites the caches)
12421 // fixed in: https://github.com/root-project/root/pull/12024
12422 for (auto &c : w->components()) {
12423 c->_eocache = nullptr;
12424 }
12425#endif
12426 // const_cast<Node2*>(this)->sterilize(); - tried this to reduce mem leak on readback but no improve
12427#if ROOT_VERSION_CODE < ROOT_VERSION(6, 38, 00)
12428 if (!w->writeToFile(sFilename, sOpt != "update")) {
12429#else
12430 if (w->writeToFile(sFilename, sOpt != "update")) {
12431#endif
12432 Info("SaveAs", "%s saved to %s", w->GetName(), sFilename.Data());
12433 // save any fitDatabase that is loaded in memory too
12434 // TODO: We should do this as well for SaveAs on a scan object
12435 std::function<void(TDirectory *, TDirectory *)> CopyDir;
12436
12438 auto dir = dest->GetDirectory(source->GetName());
12439 if (!dir) {
12440 dir = dest->mkdir(source->GetName());
12441 }
12442 for (auto k : *source->GetListOfKeys()) {
12443 auto key = dynamic_cast<TKey *>(k);
12444 const char *classname = key->GetClassName();
12445 TClass *cl = gROOT->GetClass(classname);
12446 // std::cout << "processing " << key->GetName() << " " << classname << std::endl;
12447 if (!cl) {
12448 continue;
12449 } else if (cl->InheritsFrom(TDirectory::Class())) {
12450 CopyDir(source->GetDirectory(key->GetName()), dir);
12451 } else {
12452 // don't write object if it already exists
12453 if (dir->FindKey(key->GetName()))
12454 continue;
12455 // support FitConfigs ....
12456 if (strcmp(classname, "ROOT::Fit::FitConfig") == 0) {
12457 auto fc = key->ReadObject<ROOT::Fit::FitConfig>();
12458 dir->WriteObject(fc, key->GetName());
12459 delete fc;
12460 } else {
12461 TObject *obj = key->ReadObj();
12462 if (obj) {
12463 dir->WriteTObject(obj, key->GetName());
12464 delete obj;
12465 }
12466 }
12467 }
12468 }
12469 };
12470 if (gROOT->GetListOfFiles()) {
12471 for (auto key : *gROOT->GetListOfFiles()) {
12472 if (auto fitDb = dynamic_cast<TMemFile *>(key);
12473 fitDb /*&& TString(key->GetName()).BeginsWith("fitDatabase_")*/) {
12474 CopyDir(fitDb, std::make_unique<TFile>(sFilename, "UPDATE").get());
12475 Info("SaveAs", "Saved %s to %s", fitDb->GetName(), sFilename.Data());
12476 }
12477 }
12478 }
12479 } else {
12480 Error("SaveAs", "Unable to save to %s", sFilename.Data());
12481 }
12482#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
12483 // restore the cache to every node
12484 for (auto &c : w->components()) {
12485 c->setExpensiveObjectCache(w->expensiveObjectCache());
12486 }
12487#endif
12488 }
12489}
12490
12491double xRooNode::GetBinError(int bin, const xRooNode &fr, int nToys, bool errorsHi, bool errorsLo) const
12492{
12493 auto res = GetBinErrors(bin, bin, fr, nToys, errorsHi, errorsLo);
12494 if (res.empty())
12495 return std::numeric_limits<double>::quiet_NaN();
12496 return res.at(0);
12497}
12498
12499std::vector<double> xRooNode::contents() const
12500{
12501 std::vector<double> out;
12502 out.reserve(size());
12503 for (auto child : *this) {
12504 out.push_back(child->GetContent());
12505 }
12506 return out;
12507}
12508
12510{
12511
12512 auto _fr = fr.get<RooFitResult>();
12513
12514 if (!_fr) {
12515 return covariances(fitResult());
12516 }
12517
12518 auto rho = _fr->correlationMatrix();
12519
12520 TMatrixDSym out(size());
12521
12522 auto _pars = pars();
12523
12524 // 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]
12525 // - nu[theta_n_down]) consistent with propagatedError formula
12526
12527 for (int m = 0; m < rho.GetNrows(); m++) {
12528 auto p_m = dynamic_cast<RooRealVar *>(_fr->floatParsFinal().at(m));
12529 if (!p_m)
12530 continue; // skip categoricals
12531 auto _p = dynamic_cast<RooAbsRealLValue *>(_pars.get<RooArgList>()->find(p_m->GetName()));
12532 if (!_p)
12533 continue;
12534 auto tmp = _p->getVal();
12535 _p->setVal(p_m->getVal() + p_m->getErrorHi());
12536 auto nu_m = contents();
12537 _p->setVal(p_m->getVal() + p_m->getErrorLo());
12538 auto nu_m2 = contents();
12539 _p->setVal(tmp);
12540 for (int n = 0; n < rho.GetNrows(); n++) {
12541 auto p_n = dynamic_cast<RooRealVar *>(_fr->floatParsFinal().at(n));
12542 if (!p_n)
12543 continue; // skip categoricals
12544 auto _p2 = dynamic_cast<RooAbsRealLValue *>(_pars.get<RooArgList>()->find(p_n->GetName()));
12545 if (!_p2)
12546 continue;
12547 auto tmp2 = _p2->getVal();
12548 _p2->setVal(p_n->getVal() + p_n->getErrorHi());
12549 auto nu_n = (p_n == p_m) ? nu_m : contents();
12550 _p2->setVal(p_n->getVal() + p_n->getErrorLo());
12551 auto nu_n2 = (p_n == p_m) ? nu_m2 : contents();
12552 _p2->setVal(tmp2);
12553 for (int i = 0; i < out.GetNrows(); i++) {
12554 for (int j = 0; j < out.GetNrows(); j++) {
12555 out(i, j) += 0.25 * (nu_m[i] - nu_m2[i]) * rho(m, n) * (nu_n[j] - nu_n2[j]);
12556 }
12557 }
12558 }
12559 }
12560 return out;
12561}
12562
12563std::pair<double, double>
12564xRooNode::IntegralAndError(const xRooNode &fr, const char *rangeName, int nToys, bool errorsHi, bool errorsLo) const
12565{
12566 double out = 1.;
12567 double err = std::numeric_limits<double>::quiet_NaN();
12568
12569 std::unique_ptr<RooAbsCollection> _snap;
12571 if (auto _fr = fr.get<RooFitResult>()) {
12572 _pars.add(pars().argList());
12573 _snap.reset(_pars.snapshot());
12574 _pars = _fr->floatParsFinal();
12575 _pars = _fr->constPars();
12576 }
12577
12578 auto _obs = obs();
12579 RooArgSet sobs(*_obs.get<RooArgList>()); // need to make explicit RooArgSet for ROOT 6.36 onwards
12580 auto _coefs = coefs(); // need here to keep alive owned RooProduct
12581 if (auto c = _coefs.get<RooAbsReal>(); c) {
12582 out = c->getVal(sobs); // assumes independent of observables!
12583 }
12584
12585 if (auto p = dynamic_cast<RooAbsPdf *>(get()); p) {
12586 // prefer to use expectedEvents for integrals of RooAbsPdf e.g. for RooProdPdf wont include constraint terms
12587 if (rangeName)
12588 p->setNormRange(rangeName);
12589#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 27, 00)
12591#endif
12592 out *= p->expectedEvents(*_obs.get<RooArgList>());
12593#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
12594 // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
12595 p->_normSet = nullptr;
12596#endif
12597 err = GetBinError(-1, fr, nToys, errorsHi, errorsLo);
12598 if (rangeName)
12599 p->setNormRange(nullptr);
12600 } else if (auto p2 = dynamic_cast<RooAbsReal *>(get()); p2) {
12601 // only integrate over observables we actually depend on
12602 auto f = std::shared_ptr<RooAbsReal>(
12603 p2->createIntegral(*std::unique_ptr<RooArgSet>(p2->getObservables(*_obs.get<RooArgList>())),
12604 rangeName)); // did use x here before using obs
12605 RooProduct pr("int_x_coef", "int_x_coef",
12606 RooArgList(*f, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>()));
12607 out *= f->getVal();
12608 err = xRooNode(pr, *this).GetBinError(-1, fr, nToys, errorsHi, errorsLo);
12609 sterilize(); // needed so that we can forget properly about the integral we just created (and are deleting)
12610 } else if (get<RooAbsData>()) {
12611 out = 0;
12612 auto vals = GetBinContents(1, 0); // returns all bins
12613 auto ax = (rangeName) ? GetXaxis() : nullptr;
12614 auto rv = (ax) ? dynamic_cast<RooRealVar *>(ax->GetParent()) : nullptr;
12615 auto cv = (ax && !rv) ? dynamic_cast<RooCategory *>(ax->GetParent()) : nullptr;
12616 int i = 0;
12617 for (auto &v : vals) {
12618 i++;
12619 if (rangeName) {
12620 if (rv && !rv->inRange(ax->GetBinCenter(i), rangeName))
12621 continue;
12622 if (cv && !cv->isStateInRange(rangeName, ax->GetBinLabel(i)))
12623 continue;
12624 }
12625 out += v;
12626 }
12627 err = 0; // should this be sqrt(sum(v^2)) or something similar
12628 } else {
12629 out = std::numeric_limits<double>::quiet_NaN();
12630 }
12631 if (_snap) {
12632 _pars.RooAbsCollection::operator=(*_snap);
12633 }
12634 return std::make_pair(out, err);
12635}
12636
12637std::vector<double>
12638xRooNode::GetBinErrors(int binStart, int binEnd, const xRooNode &_fr, int nToys, bool errorHi, bool errorLo) const
12639{
12640 // note: so far this method is inconsistent with the BuildHistogram in ways:
12641 // no projection over other variables
12642 // July2023: made RooRealSumPdf evaluate as a function if doesn't have a floor
12643 // but this method will still evaluate it as a pdf (uses PdfWrapper)
12644 // but can get away with it while added NaN recovery to getSimplePropagatedError to pickup raw values
12645
12646 if (fBinNumber != -1) {
12647 if (binStart != binEnd || !fParent) {
12648 throw std::runtime_error(TString::Format("%s is a bin - only has one value", GetName()));
12649 }
12650 return fParent->GetBinErrors(fBinNumber, fBinNumber, _fr, nToys, errorHi, errorLo);
12651 }
12652
12653 std::vector<double> out;
12654
12655 auto _hist = BuildHistogram(nullptr, true, true, binStart, binEnd, _fr, errorHi, errorLo, nToys);
12656 if (!_hist)
12657 return out;
12658 if (binEnd == 0) {
12659 binEnd = _hist->GetNbinsX();
12660 } else if (binEnd == binStart && binEnd == -1) {
12661 binStart = 1;
12662 binEnd = 1; // done an integral, so histogram has only 1 bin
12663 }
12664 for (int bin = binStart; bin <= binEnd; bin++) {
12665 out.push_back(((errorLo && !errorHi) ? (-1.) : 1.) *
12666 _hist->GetBinError(bin)); // using same convention as RooFit that Lo errors are negative
12667 }
12668 delete _hist;
12669 return out;
12670
12671 // auto o = dynamic_cast<RooAbsReal *>(get());
12672 // if (!o)
12673 // return out;
12674 //
12675 // std::shared_ptr<RooFitResult> fr = std::dynamic_pointer_cast<RooFitResult>(_fr.fComp);
12676 // //= dynamic_cast<RooFitResult*>( _fr.get<RooFitResult>() ? _fr->Clone() : fitResult()->Clone());
12677 //
12678 // auto _coefs = coefs();
12679 //
12680 // if (!fr) {
12681 // // need to ensure coefs, if any, are included in fit result retrieval so all pars are loaded
12682 // auto frn = (!_coefs.get() ? *this : xRooNode(RooProduct("tmp", "tmp", RooArgList(*o,
12683 // *_coefs.get<RooAbsReal>()))))
12684 // .fitResult();
12685 // if (strlen(_fr.GetName()))
12686 // frn = frn.reduced(_fr.GetName());
12687 //
12688 // // use name to reduce the fit result, if one given
12689 // fr = std::dynamic_pointer_cast<RooFitResult>(frn.fComp);
12690 // }
12691 //
12692 // if (!GETDMP(fr.get(), _finalPars)) {
12693 // fr->setFinalParList(RooArgList());
12694 // }
12695 //
12696 // /// Oct2022: No longer doing this because want to allow fitResult to be used to get partial error
12697 // // // need to add any floating parameters not included somewhere already in the fit result ...
12698 // // RooArgList l;
12699 // // for(auto& p : pars()) {
12700 // // auto v = p->get<RooRealVar>();
12701 // // if (!v) continue;
12702 // // if (v->isConstant()) continue;
12703 // // if (fr->floatParsFinal().find(v->GetName())) continue;
12704 // // if (fr->_constPars && fr->_constPars->find(v->GetName())) continue;
12705 // // l.add(*v);
12706 // // }
12707 // //
12708 // // if (!l.empty()) {
12709 // // RooArgList l2; l2.addClone(fr->floatParsFinal());
12710 // // l2.addClone(l);
12711 // // fr->setFinalParList(l2);
12712 // // }
12713 //
12714 // TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr.get(), _VM));
12715 //
12716 // if (!prevCov || size_t(prevCov->GetNcols()) < fr->floatParsFinal().size()) {
12717 // TMatrixDSym cov(fr->floatParsFinal().size());
12718 // if (prevCov) {
12719 // for (int i = 0; i < prevCov->GetNcols(); i++) {
12720 // for (int j = 0; j < prevCov->GetNrows(); j++) {
12721 // cov(i, j) = (*prevCov)(i, j);
12722 // }
12723 // }
12724 // }
12725 // int i = 0;
12726 // for (auto &p : fr->floatParsFinal()) {
12727 // if (!prevCov || i >= prevCov->GetNcols()) {
12728 // cov(i, i) = pow(dynamic_cast<RooRealVar *>(p)->getError(), 2);
12729 // }
12730 // i++;
12731 // }
12732 // int covQualBackup = fr->covQual();
12733 // fr->setCovarianceMatrix(cov);
12734 // fr->setCovQual(covQualBackup);
12735 // }
12736 //
12737 // bool doBinWidth = false;
12738 // auto ax = (binStart == -1 && binEnd == -1) ? nullptr : GetXaxis();
12739 //
12740 // auto _obs = obs(); // may own an obs so keep alive here
12741 // RooArgList normSet = _obs.argList();
12742 // // to give consistency with BuildHistogram method, should be only the axis var if defined
12743 // if (ax) {
12744 // normSet.clear();
12745 // normSet.add(*dynamic_cast<RooAbsArg *>(ax->GetParent()));
12746 // }
12747 //
12748 // if (auto p = dynamic_cast<RooAbsPdf *>(o); ax && (p || _coefs.get() || o->getAttribute("density"))) {
12749 // // pdfs of samples embedded in a sumpdf (aka have a coef) will convert their density value to a content
12750 // doBinWidth = true;
12751 // }
12752 // if (binEnd == 0) {
12753 // if (ax) {
12754 // binEnd = ax->GetNbins();
12755 // } else {
12756 // binEnd = binStart;
12757 // }
12758 // }
12759 // for (int bin = binStart; bin <= binEnd; bin++) {
12760 // if (ax)
12761 // dynamic_cast<RooAbsLValue *>(ax->GetParent())->setBin(bin - 1, ax->GetName());
12762 // // if (!SetBin(bin)) { return out; }
12763 //
12764 // double res;
12765 // if (auto p = dynamic_cast<RooAbsPdf *>(o); p) {
12766 // // fr->covarianceMatrix().Print();
12767 // res = PdfWrapper(*p, _coefs.get<RooAbsReal>(), !ax).getSimplePropagatedError(*fr, normSet);
12768 // #if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
12769 // // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
12770 // p->_normSet = nullptr;
12771 // #endif
12772 // } else {
12773 // // res = o->getPropagatedError(*fr, normSet);
12774 // // // TODO: What if coef has error? - probably need a FuncWrapper class
12775 // // if (auto c = _coefs.get<RooAbsReal>(); c) {
12776 // // res *= c->getVal(normSet);
12777 // // }
12778 // res = RooProduct("errorEval", "errorEval",
12779 // RooArgList(*o, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>()))
12780 // .getPropagatedError(*fr, normSet);
12781 // }
12782 // if (doBinWidth) {
12783 // res *= ax->GetBinWidth(bin);
12784 // }
12785 // out.push_back(res);
12786 // }
12787 //
12788 // return out;
12789}
12790
12792
12793std::string cling::printValue(const XROOFIT_NAMESPACE_NAME::xRooNode *v)
12794{
12795 if (!v)
12796 return "nullptr\n";
12797 if (!v->empty()) {
12798 std::string out;
12799 size_t left = v->size();
12800 for (auto n : *v) {
12801 left--;
12802 if (!out.empty())
12803 out += ",";
12804 else
12805 out += "{";
12806 out += n->GetName();
12807 if (out.length() > 100 && left > 0) {
12808 out += TString::Format(",... and %lu more", left);
12809 break;
12810 }
12811 }
12812 out += "}\n";
12813 out = std::string(Form("<%s> %s", v->get() ? v->get()->ClassName() : "nullptr", v->GetName())) + out;
12814 return out;
12815 }
12816 std::string out;
12817 if (!(*v)) {
12818 return "<nullptr>";
12819 } else {
12820 return Form("<%s> %s", v->get() ? v->get()->ClassName() : "nullptr", v->GetName());
12821 }
12822
12823 return out;
12824}
@ kButton1Down
Definition Buttons.h:17
TVirtualPad * fPad
The pad where it has been drawn.
Definition HybridPlot.h:81
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
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:126
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:145
float xmin
#define hi
float * q
float ymin
float xmax
float ymax
@ kCanDelete
Definition TObject.h:375
#define gROOT
Definition TROOT.h:426
char * Form(const char *fmt,...)
Formats a string in a circular formatting buffer.
Definition TString.cxx:2496
R__EXTERN TStyle * gStyle
Definition TStyle.h:442
R__EXTERN TSystem * gSystem
Definition TSystem.h:582
TVectorT< Double_t > TVectorD
Definition TVectorDfwd.h:23
#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:970
double GetBinUpEdge(Int_t bin) const override
Return up edge of bin.
Definition xRooNode.cxx:978
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:998
RooAbsRealLValue * rvar() const
void SetTitle(const char *title) override
Set the title of the TNamed.
Definition xRooNode.cxx:989
void Set(Int_t nbins, double xmin, double xmax) override
Initialize axis with fix bins.
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.
const char * GetTitle() const override
Returns title of object.
Definition xRooNode.cxx:985
double GetBinWidth(Int_t bin) const override
Return bin width.
Definition xRooNode.cxx:964
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:463
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:154
static xRooNLLVar createNLL(const std::shared_ptr< RooAbsPdf > pdf, const std::shared_ptr< RooAbsData > data, const RooLinkedList &nllOpts)
Definition xRooFit.cxx:105
static std::pair< double, double > matchPrecision(const std::pair< double, double > &in)
Definition xRooFit.cxx:2089
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:551
xRooNLLVar nll(const xRooNode &_data, std::initializer_list< RooCmdArg > nllOpts) const
xRooNode poi() const
List of parameters of interest: parameters marked as "of interest" These parameters have the "poi" at...
void SetName(const char *name) override
Set the name of the TNamed.
TGListTreeItem * GetTreeItem(TBrowser *b) const
void SetTitle(const char *title) override
Set the title of the TNamed.
xRooNode Multiply(const xRooNode &child, Option_t *opt="")
xRooNode Replace(const xRooNode &node)
bool SetData(const TObject &obj, const xRooNode &data="obsData")
xRooNode Remove(const xRooNode &child)
xRooNode globs() const
List of global observables of this node.
xRooNode Add(const xRooNode &child, Option_t *opt="")
xRooNode(const char *type, const char *name, const char *title="")
const char * GetIconName() const override
Returns mime type name of object.
void Draw(Option_t *opt="") override
Default Draw method for all objects.
TGraph * BuildGraph(RooAbsLValue *v=nullptr, bool includeZeros=false, TVirtualPad *fromPad=nullptr) const
const std::shared_ptr< xRooNode > & at(size_t idx, bool browseResult=true) const
Definition xRooNode.h:147
std::string fRange
! only here so can have char* GetRange return so can return nullptr for no range set !...
Definition xRooNode.h:535
xRooNode reduced(const std::string &range="", bool invert=false) const
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:550
void Print(Option_t *opt="") const override
Print TNamed name and title.
std::shared_ptr< T > acquireNew(Args &&...args)
Definition xRooNode.h:290
void SaveAs(const char *filename="", Option_t *option="") const override
Save this object in the file specified by filename.
xRooNode bins() const
bins of a channel or sample, or channels of a multi-channel pdf
std::shared_ptr< xRooNode > find(const std::string &name, bool browseResult=true) const
TGListTree * GetListTree(TBrowser *b) const
xRooNode styles(TObject *initObject=nullptr, bool autoCreate=true) const
xRooNode generate(const xRooNode &fr="", bool expected=false, int seed=0)
void Inspect() const override
Dump contents of this object in a graphics canvas.
std::shared_ptr< xRooNode > getBrowsable(const char *name) const
bool SetXaxis(const RooAbsBinning &binning)
double GetBinError(int bin, const xRooNode &fr="", int nToys=0, bool errorsHi=false, bool errorsLo=false) const
bool SetBinData(int bin, double value, const xRooNode &data="obsData")
std::vector< double > GetBinContents(int binStart=1, int binEnd=0) const
xRooNode pp() const
List of prespecified parameters: non-floatable parameters.
double GetError(const xRooNode &fr="", int nToys=0, bool errorsHi=false, bool errorsLo=false) const
Definition xRooNode.h:439
std::shared_ptr< xRooNode > fProvider
! like a parent but only for use by getObject
Definition xRooNode.h:544
std::shared_ptr< TStyle > style(TObject *initObject=nullptr, bool autoCreate=true) const
xRooNode fitResult(const char *opt="") const
std::shared_ptr< TAxis > fXAxis
! appears that if was fXaxis then dialog box for SetXaxis will take as current value
Definition xRooNode.h:539
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:949
double GetBinData(int bin, const xRooNode &data="obsData")
bool SetBinError(int bin, double value)
bool SetContents(const TObject &obj)
Definition xRooNode.h:369
std::shared_ptr< TObject > fComp
!
Definition xRooNode.h:526
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:411
std::vector< double > GetBinErrors(int binStart=1, int binEnd=0, const xRooNode &fr="", int nToys=0, bool errorsHi=false, bool errorsLo=false) const
auto begin() const -> xRooNodeIterator
Definition xRooNode.h:200
std::shared_ptr< xRooNode > parentPdf() const
void _scan_(const char *what="plr", double nToys=0, const char *xvar="", int nPointsX=0, double lowX=0, double highX=0, const char *constParValues="", const char *options="")
std::shared_ptr< xRooNode > fParent
!
Definition xRooNode.h:529
xRooNode robs() const
List of regular observables of this node.
TClass * IsA() const override
Definition xRooNode.h:557
xRooNode & operator=(const TObject &o)
auto end() const -> xRooNodeIterator
Definition xRooNode.h:217
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:552
void _SetAttribute_(const char *name, const char *value=nullptr)
Definition xRooNode.cxx:514
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:665
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'.
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.
void setName(const char *name)
RooAbsArg * find(const char *name) const
Find object with given name in list.
Abstract base class for binned and unbinned datasets.
Definition RooAbsData.h:56
RooFit::OwningPtr< RooAbsData > reduce(const RooCmdArg &arg1, const RooCmdArg &arg2={}, const RooCmdArg &arg3={}, const RooCmdArg &arg4={}, const RooCmdArg &arg5={}, const RooCmdArg &arg6={}, const RooCmdArg &arg7={}, const RooCmdArg &arg8={}) const
Create a reduced copy of this dataset.
RooArgSet const * getGlobalObservables() const
Returns snapshot of global observables stored in this data.
Definition RooAbsData.h:274
Abstract base class for objects that are lvalues, i.e.
Abstract interface for all probability density functions.
Definition RooAbsPdf.h:32
bool canBeExtended() const
If true, PDF can provide extended likelihood term.
Definition RooAbsPdf.h:214
Abstract base class for objects that represent a real value that may appear on the left hand side of ...
void setBin(Int_t ibin, const char *rangeName=nullptr) override
Set value to center of bin 'ibin' of binning 'rangeName' (or of default binning if no range is specif...
Context to temporarily change the error logging mode as long as the context is alive.
Definition RooAbsReal.h:344
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:21
virtual void SetFillColor(Color_t fcolor)
Set the fill area color.
Definition TAttFill.h:40
virtual void SetFillStyle(Style_t fstyle)
Set the fill area style.
Definition TAttFill.h:42
Line Attributes class.
Definition TAttLine.h:21
virtual Color_t GetLineColor() const
Return the line color.
Definition TAttLine.h:36
Marker Attributes class.
Definition TAttMarker.h:21
virtual Size_t GetMarkerSize() const
Return the marker size.
Definition TAttMarker.h:35
virtual void SetMarkerStyle(Style_t mstyle=1)
Set the marker style.
Definition TAttMarker.h:43
virtual Font_t GetTextFont() const
Return the text font.
Definition TAttText.h:38
virtual void SetTextSize(Float_t tsize=1)
Set the text size.
Definition TAttText.h:51
Class to manage histogram axis.
Definition TAxis.h:32
virtual void LabelsOption(Option_t *option="h")
Set option(s) to draw axis with labels option can be:
Definition TAxis.cxx:667
virtual void SetBinLabel(Int_t bin, const char *label)
Set label for bin.
Definition TAxis.cxx:891
TAxis()
Default constructor.
Definition TAxis.cxx:50
virtual void Set(Int_t nbins, Double_t xmin, Double_t xmax)
Initialize axis with fix bins.
Definition TAxis.cxx:790
virtual Int_t FindFixBin(Double_t x) const
Find bin number corresponding to abscissa x
Definition TAxis.cxx:422
Using a TBrowser one can browse all ROOT objects.
Definition TBrowser.h:37
TBrowserImp * GetBrowserImp() const
Definition TBrowser.h:94
static TCanvas * MakeDefCanvas()
Static function to build a default canvas.
Definition TCanvas.cxx:1509
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:4932
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:2994
static Int_t GetColorPalette(Int_t i)
Static function returning the color number i in current palette.
Definition TColor.cxx:1509
static void InitializeColors()
Initialize colors used by the TCanvas based graphics (via TColor objects).
Definition TColor.cxx:1172
static Int_t GetNumberOfColors()
Static function returning number of colors in the color palette.
Definition TColor.cxx:1529
TDirectory::TContext keeps track and restore the current directory.
Definition TDirectory.h:89
Describe directory structure in memory.
Definition TDirectory.h:45
static TClass * Class()
virtual Int_t GetValue(const char *name, Int_t dflt) const
Returns the integer value for a resource.
Definition TEnv.cxx:511
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:752
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 file, usually with extension .root, that stores data and code in the form of serialized objects in ...
Definition TFile.h:130
Create a dialog for fit function parameter settings.
System file browser, used as TRootBrowser plug-in.
A list tree is a widget that can contain a number of items arranged in a tree structure.
Definition TGListTree.h:197
ROOT GUI Window base class.
Definition TGWindow.h:23
The axis painter class.
Definition TGaxis.h:26
virtual void SetTitle(const char *title="")
Change the title of the axis.
Definition TGaxis.cxx:2927
virtual void ImportAxisAttributes(TAxis *axis)
Internal method to import TAxis attributes to this TGaxis.
Definition TGaxis.cxx:954
TGraph with asymmetric error bars.
virtual void SetPointEYlow(Int_t i, Double_t eyl)
Set EYlow for point i.
virtual void SetPointError(Double_t exl, Double_t exh, Double_t eyl, Double_t eyh)
Set ex and ey values for point pointed by the mouse.
virtual void SetPointEYhigh(Int_t i, Double_t eyh)
Set EYhigh for point i.
Double_t GetErrorYlow(Int_t i) const override
Get low error on Y.
A TGraphErrors is a TGraph with error bars.
Double_t GetErrorYlow(Int_t bin) const override
It returns the error along Y at point i.
Double_t GetErrorYhigh(Int_t bin) const override
It returns the error along Y at point i.
A TGraph is an object made of two arrays X and Y with npoints each.
Definition TGraph.h:41
virtual void AddPoint(Double_t x, Double_t y)
Append a new point to the graph.
Definition TGraph.h:97
virtual void SetPoint(Int_t i, Double_t x, Double_t y)
Set x and y values for point number i.
Definition TGraph.cxx:2387
Int_t GetN() const
Definition TGraph.h:131
void SetName(const char *name="") override
Set graph name.
Definition TGraph.cxx:2426
void Draw(Option_t *chopt="") override
Draw this graph with its current attributes.
Definition TGraph.cxx:859
virtual TH1F * GetHistogram() const
Returns a pointer to the histogram used to draw the axis Takes into account the two following cases.
Definition TGraph.cxx:1458
virtual Double_t GetPointY(Int_t i) const
Get y value for point i.
Definition TGraph.cxx:1585
void SetTitle(const char *title="") override
Change (i.e.
Definition TGraph.cxx:2442
1-D histogram with a double per channel (see TH1 documentation)
Definition TH1.h:926
1-D histogram with a float per channel (see TH1 documentation)
Definition TH1.h:878
TH1 is the base class of all histogram classes in ROOT.
Definition TH1.h:109
virtual void SetDirectory(TDirectory *dir)
By default, when a histogram is created, it is added to the list of histogram objects in the current ...
Definition TH1.cxx:9090
void SetTitle(const char *title) override
Change/set the title.
Definition TH1.cxx:6852
virtual Int_t GetNbinsY() const
Definition TH1.h:542
@ kNoTitle
Don't draw the histogram title.
Definition TH1.h:408
TAxis * GetXaxis()
Definition TH1.h:571
TObject * FindObject(const char *name) const override
Search object named name in the list of functions.
Definition TH1.cxx:3928
virtual void SetMaximum(Double_t maximum=-1111)
Definition TH1.h:652
virtual Bool_t Add(TF1 *h1, Double_t c1=1, Option_t *option="")
Performs the operation: this = this + c1*f1 if errors are defined (see TH1::Sumw2),...
Definition TH1.cxx:852
TAxis * GetYaxis()
Definition TH1.h:572
void Draw(Option_t *option="") override
Draw this histogram with options.
Definition TH1.cxx:3113
virtual void SetMinimum(Double_t minimum=-1111)
Definition TH1.h:653
TList * GetListOfFunctions() const
Definition TH1.h:488
virtual void Scale(Double_t c1=1, Option_t *option="")
Multiply this histogram by a constant c1.
Definition TH1.cxx:6735
TObject * Clone(const char *newname="") const override
Make a complete copy of the underlying object.
Definition TH1.cxx:2802
virtual void SetStats(Bool_t stats=kTRUE)
Set statistics option on/off.
Definition TH1.cxx:9143
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:364
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 Add(TObject *obj) override
Definition TList.h:81
Int_t GetNrows() const
A TMemFile is like a normal TFile except that it reads and writes only from memory.
Definition TMemFile.h:27
A TMultiGraph is a collection of TGraph (or derived) objects.
Definition TMultiGraph.h:34
virtual void Add(TGraph *graph, Option_t *chopt="")
Add a new graph to the list of graphs.
void Draw(Option_t *chopt="") override
Draw this multigraph with its current attributes.
The TNamed class is the base class for all named ROOT classes.
Definition TNamed.h:29
TObject * Clone(const char *newname="") const override
Make a clone of an object using the Streamer facility.
Definition TNamed.cxx:73
virtual void SetTitle(const char *title="")
Set the title of the TNamed.
Definition TNamed.cxx:173
const char * GetName() const override
Returns name of object.
Definition TNamed.h:49
const char * GetTitle() const override
Returns title of object.
Definition TNamed.h:50
virtual void SetName(const char *name)
Set the name of the TNamed.
Definition TNamed.cxx:149
virtual void SetNameTitle(const char *name, const char *title)
Set all the TNamed parameters (name and title).
Definition TNamed.cxx:163
Mother of all ROOT objects.
Definition TObject.h:42
virtual void Inspect() const
Dump contents of this object in a graphics canvas.
Definition TObject.cxx:567
virtual const char * GetName() const
Returns name of object.
Definition TObject.cxx:459
virtual TObject * Clone(const char *newname="") const
Make a clone of an object using the Streamer facility.
Definition TObject.cxx:240
virtual const char * ClassName() const
Returns name of class to which the object belongs.
Definition TObject.cxx:224
virtual void Warning(const char *method, const char *msgfmt,...) const
Issue warning message.
Definition TObject.cxx:1081
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:316
virtual TObject * FindObject(const char *name) const
Must be redefined in derived classes.
Definition TObject.cxx:422
virtual void SaveAs(const char *filename="", Option_t *option="") const
Save this object in the file specified by filename.
Definition TObject.cxx:705
virtual void Delete(Option_t *option="")
Delete this object.
Definition TObject.cxx:265
void SetBit(UInt_t f, Bool_t set)
Set or unset the user status bits as specified in f.
Definition TObject.cxx:885
virtual Bool_t InheritsFrom(const char *classname) const
Returns kTRUE if object inherits from class "classname".
Definition TObject.cxx:546
virtual void Error(const char *method, const char *msgfmt,...) const
Issue error message.
Definition TObject.cxx:1095
virtual const char * GetTitle() const
Returns title of object.
Definition TObject.cxx:504
virtual void Draw(Option_t *option="")
Default Draw method for all objects.
Definition TObject.cxx:290
virtual void Print(Option_t *option="") const
This method must be overridden when a class wants to print itself.
Definition TObject.cxx:658
@ kCanDelete
if object in a list can be deleted
Definition TObject.h:71
virtual void Info(const char *method, const char *msgfmt,...) const
Issue info message.
Definition TObject.cxx:1069
The most important graphics class in the ROOT system.
Definition TPad.h:28
A Pave (see TPave) with text, lines or/and boxes inside.
Definition TPaveText.h:21
virtual TText * AddText(Double_t x1, Double_t y1, const char *label)
Add a new Text line to this pavetext at given coordinates.
virtual void SetY1NDC(Double_t y1)
Definition TPave.h:86
virtual void ConvertNDCtoPad()
Convert pave coordinates from NDC to Pad coordinates.
Definition TPave.cxx:138
virtual void SetBorderSize(Int_t bordersize=4)
Sets the border size of the TPave box and shadow.
Definition TPave.h:79
virtual void SetY2NDC(Double_t y2)
Definition TPave.h:87
Define a Pie Chart.
Definition TPie.h:23
This is the ROOT implementation of the Qt object communication mechanism (see also http://www....
Definition TQObject.h:48
Regular expression class.
Definition TRegexp.h:31
This class creates a ROOT object browser, constituted by three main tabs.
Sequenceable collection abstract base class.
Stopwatch class.
Definition TStopwatch.h:28
Provides iteration through tokens of a given string.
Definition TPRegexp.h:143
Bool_t NextToken()
Get the next token, it is stored in this TString.
Basic string class.
Definition TString.h:138
Ssiz_t Length() const
Definition TString.h:425
Bool_t IsDec() const
Returns true if all characters in string are decimal digits (0-9).
Definition TString.cxx:1946
Int_t Atoi() const
Return integer value of string.
Definition TString.cxx:1994
Bool_t EndsWith(const char *pat, ECaseCompare cmp=kExact) const
Return true if string ends with the specified string.
Definition TString.cxx:2250
Double_t Atof() const
Return floating-point value contained in string.
Definition TString.cxx:2060
Bool_t IsFloat() const
Returns kTRUE if string contains a floating point or integer number.
Definition TString.cxx:1864
const char * Data() const
Definition TString.h:384
Bool_t IsAlpha() const
Returns true if all characters in string are alphabetic.
Definition TString.cxx:1804
Bool_t BeginsWith(const char *s, ECaseCompare cmp=kExact) const
Definition TString.h:632
static TString Format(const char *fmt,...)
Static method which formats a string using a printf style format descriptor and return a TString.
Definition TString.cxx:2385
Bool_t Contains(const char *pat, ECaseCompare cmp=kExact) const
Definition TString.h:641
Ssiz_t Index(const char *pat, Ssiz_t i=0, ECaseCompare cmp=kExact) const
Definition TString.h:660
TStyle objects may be created to define special styles.
Definition TStyle.h:29
void SetPaintTextFormat(const char *format="g")
Definition TStyle.h:390
Int_t GetOptTitle() const
Definition TStyle.h:248
Float_t GetLabelSize(Option_t *axis="X") const
Return label size.
Definition TStyle.cxx:1146
Float_t GetPadRightMargin() const
Definition TStyle.h:216
Style_t GetTitleFont(Option_t *axis="X") const
Return title font.
Definition TStyle.cxx:1217
Bool_t GetHistMinimumZero() const
Definition TStyle.h:239
Float_t GetPadLeftMargin() const
Definition TStyle.h:215
Float_t GetTitleYSize() const
Definition TStyle.h:281
Double_t GetHistTopMargin() const
Definition TStyle.h:240
Float_t GetPadBottomMargin() const
Definition TStyle.h:213
const char * GetPaintTextFormat() const
Definition TStyle.h:252
Float_t GetPadTopMargin() const
Definition TStyle.h:214
virtual Bool_t ExpandPathName(TString &path)
Expand a pathname getting rid of special shell characters like ~.
Definition TSystem.cxx:1289
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:1311
virtual Bool_t ProcessEvents()
Process pending events (GUI, timers, sockets).
Definition TSystem.cxx:418
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:602
static TVirtualPadEditor * GetPadEditor(Bool_t load=kTRUE)
Returns the pad editor dialog. Static method.
TVirtualPad is an abstract base class for the Pad and Canvas classes.
Definition TVirtualPad.h:51
virtual TVirtualPad * cd(Int_t subpadnumber=0)=0
virtual TCanvas * GetCanvas() const =0
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:682
The namespace RooFit contains mostly switches that change the behaviour of functions of PDFs (or othe...
Definition CodegenImpl.h:72
static constexpr auto NumIntegration
Alias of MsgLevel::NumericIntegration for backwards compatibility.
MsgLevel
Verbosity level for RooMsgService::StreamConfig in RooMsgService.
@ NumericIntegration
Double_t Prob(Double_t chi2, Int_t ndf)
Computation of the probability for a certain Chi-squared (chi2) and number of degrees of freedom (ndf...
Definition TMath.cxx:637
Double_t ChisquareQuantile(Double_t p, Double_t ndf)
Evaluate the quantiles of the chi-squared probability distribution function.
Definition TMath.cxx:2196
Double_t StdDev(Long64_t n, const T *a, const Double_t *w=nullptr)
Definition TMath.h:534
#define BEGIN_XROOFIT_NAMESPACE
Definition Config.h:24
#define END_XROOFIT_NAMESPACE
Definition Config.h:25
static const char * what
Definition stlLoader.cc:5
bool isNaNWithPayload() const
Test if this struct has a float packed into its mantissa.
static float unpackNaN(double val)
If val is NaN and a this NaN has been tagged as containing a payload, unpack the float from the manti...
th1 Draw()
TLine lv
Definition textalign.C:5
TMarker m
Definition textangle.C:8
TLine l
Definition textangle.C:4
auto * tt
Definition textangle.C:16
static uint64_t sum(uint64_t i)
Definition Factory.cxx:2338
#define GETWS(a)
#define GETWSSETS(w)
void(* gOldHandlerr)(int)
void addLegendEntry(TObject *o, const char *title, const char *opt)
void buildHistogramInterrupt(int signum)
auto GETACTBROWSER(TRootBrowser *b)
Definition xRooNode.cxx:111
auto & GETWSSNAPSHOTS(RooWorkspace *w)
Definition xRooNode.cxx:107
bool TopRightPlaceBox(TPad *p, TObject *o, double w, double h, double &xl, double &yb)
#define GETDMP(o, m)
Definition xRooNode.cxx:123
TPaveText * getPave(const char *name="labels", bool create=true, bool doPaint=false)
std::string formatLegendString(const std::string &s)
auto GETROOTDIR(TGFileBrowser *b)
Definition xRooNode.cxx:115
const xRooNode * runningNode
auto GETLISTTREE(TGFileBrowser *b)
Definition xRooNode.cxx:119
const T & _or_func(const T &a, const T &b)
Definition xRooNode.cxx:229
std::vector< double > new_getPropagatedErrors(const RooAbsReal &f, const RooFitResult &fr, const RooArgSet &nset, RooArgList **pars, bool asymHi, bool asymLo, const std::vector< int > &bins, const std::function< void(int)> &setBin)
TLegend * getLegend(bool create=true, bool doPaint=false)