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 should fix your workspaces!");
442 } else {
443 // get the first top-level pdf
444 browse();
445 for (auto &a : *this) {
446 if (noErrorPars.empty()) {
447 break;
448 }
449 if (a->fFolder == "!pdfs") {
450 try {
451 auto fr = a->floats().reduced(parNames).fitResult("prefit");
452 if (auto _fr = fr.get<RooFitResult>(); _fr) {
453 std::set<RooRealVar *> foundPars;
454 for (auto &v : noErrorPars) {
455 if (auto arg = dynamic_cast<RooRealVar *>(_fr->floatParsFinal().find(v->GetName()));
456 arg && arg->hasError()) {
457 v->setError(arg->getError());
458 foundPars.insert(v);
459 }
460 }
461 for (auto &v : foundPars) {
462 noErrorPars.erase(v);
463 }
464 }
465 } catch (...) {
466 }
467 }
468 }
469 }
470 }
471 }
472 }
473
474 if (strlen(GetTitle()) == 0) {
475 if (fComp) {
476 TNamed::SetTitle(fComp->GetTitle());
477 } else {
478 TNamed::SetTitle(GetName());
479 }
480 }
481}
482
483xRooNode::xRooNode(const TObject &comp, const std::shared_ptr<xRooNode> &parent)
484 : xRooNode(/*[](const TObject& c) {
485c.InheritsFrom("RooAbsArg");
486if (s) {
487return (s->getStringAttribute("alias")) ? s->getStringAttribute("alias") : c.GetName();
488}
489return c.GetName();
490}(comp)*/
491 (comp.InheritsFrom("RooAbsArg") && dynamic_cast<const RooAbsArg *>(&comp)->getStringAttribute("alias"))
492 ? dynamic_cast<const RooAbsArg *>(&comp)->getStringAttribute("alias")
493 : comp.GetName(),
494 std::shared_ptr<TObject>(const_cast<TObject *>(&comp), [](TObject *) {}), parent)
495{
496}
497
498xRooNode::xRooNode(const std::shared_ptr<TObject> &comp, const std::shared_ptr<xRooNode> &parent)
499 : xRooNode(
500 [&]() {
501 if (auto a = std::dynamic_pointer_cast<RooAbsArg>(comp); a && a->getStringAttribute("alias"))
502 return a->getStringAttribute("alias");
503 if (comp)
504 return comp->GetName();
505 return "";
506 }(),
507 comp, parent)
508{
509}
510
512
513void xRooNode::_SetAttribute_(const char *name, const char *value)
514{
515 TString v(value);
516 v.ToUpper();
517 bool isBool = (v == "TRUE" || v == "FALSE");
518 if (auto a = get<RooAbsArg>(); a) {
519 if (value == nullptr || v == "NULLPTR") {
520 if (a->getAttribute(name))
521 a->setAttribute(name, false);
522 else if (a->getStringAttribute(name))
523 a->setStringAttribute(name, nullptr);
524 } else {
525 if (isBool)
526 a->setAttribute(name, (v == "TRUE"));
527 else
528 a->setStringAttribute(name, value);
529 }
530 } else {
531 RooArgList l = argList();
532 for (auto a2 : l) {
533 xRooNode(*a2)._SetAttribute_(name, value);
534 }
535 }
536 // should update this node's state in any browsers ...
537 for (auto a : *gROOT->GetListOfBrowsers()) {
538 TBrowser *b = dynamic_cast<TBrowser *>(a);
539 if (b && GetTreeItem(b)) {
540 if (auto bi = dynamic_cast<TRootBrowser *>(b->GetBrowserImp())) {
541 if (auto fb = dynamic_cast<TGFileBrowser *>(bi->GetActBrowser())) {
542 fb->DoubleClicked(GetTreeItem(b), 0);
543 }
544 }
545 }
546 }
547}
548
549void xRooNode::Checked(TObject *obj, bool val)
550{
551 if (obj != this)
552 return;
553
554 // cycle through states:
555 // unhidden and selected: tick, no uline
556 // hidden and unselected: notick, uline
557 // unhidden and unselected: tick, uline
558 if (auto o = get<RooAbsReal>(); o) {
559 if (o->isSelectedComp() && !val) {
560 // deselecting and hiding
561 o->selectComp(val);
562 o->setAttribute("hidden");
563 } else if (!o->isSelectedComp() && !val) {
564 // selecting
565 o->selectComp(!val);
566 } else if (val) {
567 // unhiding but keeping unselected
568 o->setAttribute("hidden", false);
569 }
570 auto item = GetTreeItem(nullptr);
571 item->CheckItem(!o->getAttribute("hidden"));
572 if (o->isSelectedComp()) {
573 item->ClearColor();
574 } else {
575 item->SetColor(kGray);
576 }
577 return;
578 }
579
580 if (auto o = get(); o) {
581 // if (o->TestBit(1<<20)==val) return; // do nothing
582 o->SetBit(1 << 20, val); // TODO: check is 20th bit ok to play with?
583 if (auto fr = get<RooFitResult>(); fr) {
584 if (auto _ws = ws(); _ws) {
585 if (val) {
586 // ensure fit result is in genericObjects list or snapshots ... if not, add a copy ...
587 if (fr->numStatusHistory() && !_ws->genobj(fr->GetName())) {
588 _ws->import(*fr);
589 if (auto wfr = dynamic_cast<RooFitResult *>(_ws->genobj(fr->GetName()))) {
590 fr = wfr;
591 }
592 }
593 RooArgSet _allVars = _ws->allVars();
594 _allVars = fr->floatParsFinal();
595 _allVars = fr->constPars();
596 for (auto &i : fr->floatParsInit()) {
597 auto v = dynamic_cast<RooRealVar *>(_allVars.find(i->GetName()));
598 if (v)
599 v->setStringAttribute("initVal", TString::Format("%f", dynamic_cast<RooRealVar *>(i)->getVal()));
600 }
601 // uncheck all other fit results
602 for (auto oo : _ws->allGenericObjects()) {
603 if (auto ffr = dynamic_cast<RooFitResult *>(oo); ffr && ffr != fr) {
604 ffr->ResetBit(1 << 20);
605 }
606 }
607 } else
608 _ws->allVars() = fr->floatParsInit();
609 }
610
611 TBrowser *b = nullptr;
612 for (auto a : *gROOT->GetListOfBrowsers()) {
613 b = dynamic_cast<TBrowser *>(a);
614 if (b && GetTreeItem(b)) {
615 break;
616 }
617 }
618 if (b) {
619 auto p = GetTreeItem(b);
620
621 if (p) {
622 // update check marks on siblings
623 if (auto first = p->GetParent()->GetFirstChild()) {
624 do {
625 if (first->HasCheckBox()) {
626 auto _obj = static_cast<xRooNode *>(first->GetUserData());
627 first->CheckItem(_obj->get() && _obj->get()->TestBit(1 << 20));
628 }
629 } while ((first = first->GetNextSibling()));
630 }
631 }
632
633 // also since const status of pars could have changed, refresh all 'poi' and 'np' open nodes
634 if (auto bi = dynamic_cast<TRootBrowser *>(b->GetBrowserImp())) {
635 if (auto fb = dynamic_cast<TGFileBrowser *>(bi->GetActBrowser())) {
636 while (p) {
637 if (TString(p->GetText()).BeginsWith("RooWorkspace::")) {
638 std::function<void(TGListTreeItem *)> rfunc;
639
640 rfunc = [&](TGListTreeItem *i) {
641 if (auto first = i->GetFirstChild()) {
642 do {
643 if (first->IsOpen() &&
644 (TString(first->GetText()) == "poi" || TString(first->GetText()) == "np")) {
645 fb->DoubleClicked(first, 0);
646 } else
647 rfunc(first);
648 } while ((first = first->GetNextSibling()));
649 }
650 };
651 rfunc(p);
652 break;
653 } else {
654 p = p->GetParent();
655 }
656 }
657 }
658 }
659 }
660 }
661 }
662}
663
665{
666 static bool blockBrowse = false;
667 if (blockBrowse)
668 return;
669 if (b == nullptr) {
670 auto b2 = dynamic_cast<TBrowser *>(gROOT->GetListOfBrowsers()->Last());
671 if (!b2 || !b2->GetBrowserImp()) { // no browser imp if browser was closed
672 blockBrowse = true;
673 gEnv->SetValue("X11.UseXft", "no"); // for faster x11
674 gEnv->SetValue("X11.Sync", "no");
675 gEnv->SetValue("X11.FindBestVisual", "no");
676 gEnv->SetValue("Browser.Name", "TRootBrowser"); // forces classic root browser (in 6.26 onwards)
677 gEnv->SetValue("Canvas.Name", "TRootCanvas");
678 b2 = new TBrowser("nodeBrowser", this, "RooFit Browser");
679 blockBrowse = false;
680 } else if (strcmp(b2->GetName(), "nodeBrowser") == 0) {
681 blockBrowse = true;
682 b2->BrowseObject(this);
683 blockBrowse = false;
684 } else {
685 auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b2->GetBrowserImp())));
686 if (_b) {
687 _b->AddFSDirectory("Workspaces", nullptr, "SetRootDir");
688 _b->GotoDir(nullptr);
689 _b->Add(this, GetName());
690 }
691 }
692 return;
693 }
694
695 if (auto item = GetTreeItem(b); item) {
696 if (!item->IsOpen() && IsFolder())
697 return; // no need to rebrowse if closing
698 // update check marks on any child items
699 if (auto first = item->GetFirstChild()) {
700 do {
701 if (first->HasCheckBox()) {
702 auto _obj = static_cast<xRooNode *>(first->GetUserData());
703 first->CheckItem(_obj->get() &&
704 (_obj->get()->TestBit(1 << 20) ||
705 (_obj->get<RooAbsArg>() && !_obj->get<RooAbsArg>()->getAttribute("hidden"))));
706 }
707 } while ((first = first->GetNextSibling()));
708 }
709 }
710
711 browse();
712
713 // for top-level pdfs default to having the .vars browsable too
714 if (get<RooAbsPdf>() && fFolder == "!pdfs" && !_IsShowVars_()) {
715 fBrowsables.push_back(std::make_shared<xRooNode>(vars()));
716 }
717
718 if (auto _fr = get<RooFitResult>(); _fr && fBrowsables.empty()) {
719 // have some common drawing options
720 fBrowsables.push_back(std::make_shared<xRooNode>(".Draw(\"pull\")", nullptr, *this));
721 fBrowsables.push_back(std::make_shared<xRooNode>(".Draw(\"corr10colztext\")", nullptr, *this));
722 if (std::unique_ptr<RooAbsCollection>(_fr->floatParsFinal().selectByAttrib("poi", true))->size() == 1) {
723 fBrowsables.push_back(std::make_shared<xRooNode>(".Draw(\"impact\")", nullptr, *this));
724 }
725 }
726
727 if (empty() && fBrowsables.empty()) {
728 try {
729 if (auto s = get<TStyle>()) {
730 s->SetFillAttributes();
731 if (auto ed = dynamic_cast<TGedEditor *>(TVirtualPadEditor::GetPadEditor())) {
732 ed->SetModel(gPad, s, kButton1Down, true);
733 }
734 } else if (TString(GetName()).BeginsWith(".Draw(\"") && fParent) {
735 fParent->Draw(TString(TString(GetName())(7, strlen(GetName()) - 9)) + b->GetDrawOption());
736 } else {
737 Draw(b->GetDrawOption());
738 }
739 } catch (const std::exception &e) {
740 new TGMsgBox(
741 gClient->GetRoot(),
742 (gROOT->GetListOfBrowsers()->At(0))
743 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
744 : gClient->GetRoot(),
745 "Exception", e.what(),
746 kMBIconExclamation); // deletes self on dismiss?
747 }
748 }
749
750 bool hasFolders = false;
751 if (strlen(GetName()) > 0 && GetName()[0] != '!') { // folders don't have folders
752 for (auto &c : *this) {
753 if (!c->fFolder.empty()) {
754 hasFolders = true;
755 break;
756 }
757 }
758 }
759 // auto _ws = get<RooWorkspace>();
760 if (/*_ws*/ hasFolders) {
761 // organize in folders
762 auto _folders = find(".folders");
763 if (!_folders) {
764 _folders = emplace_back(std::make_shared<xRooNode>(".folders", nullptr, *this));
765 }
766 // ensure entry in folders for every folder type ...
767 for (auto &v : *this) {
768 if (!v->fFolder.empty() && !_folders->find(v->fFolder, false)) {
769 _folders->emplace_back(std::make_shared<xRooNode>(v->fFolder.c_str(), nullptr, *this));
770 }
771 }
772 // now just add all the folders
773 for (auto &v : *_folders) {
774 TString _name = v->GetName();
775 if (_name.BeginsWith('!'))
776 _name = _name(1, _name.Length()); // strip ! from display
777 b->Add(v.get(), _name);
778 }
779 }
780
781 for (auto &v : *this) {
782 if (hasFolders && !v->fFolder.empty())
783 continue; // in the folders
784 if (strcmp(v->GetName(), ".folders") == 0)
785 continue; // never 'browse' the folders property
786 auto _fr = v->get<RooFitResult>();
787 int _checked = (v->get<RooAbsData>() || _fr) ? v->get()->TestBit(1 << 20) : -1;
788 if (_fr && ((_fr->status() == 0 && _fr->numStatusHistory() == 0) || (_fr->floatParsFinal().empty()))) {
789 // this is a "PARTIAL" fit result ... don't allow it to be selected
790 _checked = -1;
791 }
792 if (v->get<RooAbsPdf>() && get<RooSimultaneous>())
793 _checked = !v->get<RooAbsArg>()->getAttribute("hidden");
794 TString _name = v->GetName();
795 if (v->get() && _name.BeginsWith(TString(v->get()->ClassName()) + "::")) {
796 _name = _name(strlen(v->get()->ClassName()) + 2, _name.Length());
797 }
798 if (_name.BeginsWith(".")) {
799 // property node -- display the name of the contained object
800 if (v->get()) {
801 _name = TString::Format("%s: %s::%s", _name.Data(), v->get()->ClassName(),
802 (v->get<RooAbsArg>() && v->get<RooAbsArg>()->getStringAttribute("alias"))
803 ? v->get<RooAbsArg>()->getStringAttribute("alias")
804 : v->get()->GetName());
805 }
806 } else if (v->get() && !v->get<TFile>() && !TString(v->GetName()).BeginsWith('/'))
807 _name = TString::Format("%s::%s", v->get()->ClassName(), _name.Data());
808 if (auto _type = v->GetNodeType(); strlen(_type)) {
809 // decided not to show const values until figure out how to update if value changes
810 /*if (TString(_type)=="Const") _name += TString::Format(" [%s=%g]",_type,v->get<RooConstVar>()->getVal());
811 else*/
812 _name += TString::Format(" [%s]", _type);
813 }
814 if (auto fv = v->get<RooFormulaVar>()) {
815 TString formu = TString::Format(" [%s]", fv->expression());
816 for (size_t i = 0; i < fv->dependents().size(); i++) {
817 formu.ReplaceAll(TString::Format("x[%zu]", i), fv->dependents()[i].GetName());
818 }
819 _name += formu;
820 } else if (auto gv = v->get<RooGenericPdf>()) {
821 TString formu = TString::Format(" [%s]", gv->expression());
822 for (size_t i = 0; i < gv->dependents().size(); i++) {
823 formu.ReplaceAll(TString::Format("x[%zu]", i), gv->dependents()[i].GetName());
824 }
825 _name += formu;
826 } else if(auto pi = v->get<PiecewiseInterpolation>()) {
827 // check if all interpCodes are the same.
828 std::set<int> interpCodes;
829 for(auto& c : pi->interpolationCodes()) interpCodes.insert(c);
830 if(interpCodes.size()==1) { _name += TString::Format(" [InterpCode=%d]",*interpCodes.begin()); }
831 } else if(auto fiv = v->get<RooStats::HistFactory::FlexibleInterpVar>()) {
832 // check if all interpCodes are the same.
833 std::set<int> interpCodes;
834 for(auto& c : fiv->interpolationCodes()) interpCodes.insert(c==4 ? 5 : c); // in definition of FlexibleInterpVar 4 gets replaced with 5
835 if(interpCodes.size()==1) { _name += TString::Format(" [InterpCode=%d]",*interpCodes.begin()); }
836 }
837 // tool tip defaults to displaying name and title, so temporarily set name to obj name if has one
838 // and set title to the object type
839 TString nameSave(v->TNamed::GetName());
840 TString titleSave(v->TNamed::GetTitle());
841 if (auto o = v->get(); o)
842 v->TNamed::SetNameTitle(o->GetName(), o->ClassName());
843 b->Add(v.get(), _name, _checked);
844 if (auto o = v->get(); o)
845 v->TNamed::SetNameTitle(nameSave, titleSave);
846 if (_checked != -1) {
847 dynamic_cast<TQObject *>(b->GetBrowserImp())
848 ->Connect("Checked(TObject *, bool)", ClassName(), v.get(), "Checked(TObject *, bool)");
849 }
850 if (_fr) {
851 if (_fr->status() || _fr->covQual() != 3) { // snapshots or bad fits
852 v->GetTreeItem(b)->SetColor((_fr->numStatusHistory() && !_fr->floatParsFinal().empty()) ? kRed : kBlue);
853 } else if (_fr->numStatusHistory() == 0) { // partial fit result ..
854 v->GetTreeItem(b)->SetColor(kGray);
855 }
856 }
857 if ((v->fFolder == "!np" || v->fFolder == "!poi")) {
858 if (v->get<RooAbsArg>()->getAttribute("Constant")) {
859 v->GetTreeItem(b)->SetColor(kGray);
860 } else
861 v->GetTreeItem(b)->ClearColor();
862 }
863 if (auto _htr = v->get<RooStats::HypoTestResult>(); _htr) {
864 // check for fit statuses
865 if (auto fits = _htr->GetFitInfo()) {
866 for (int i = 0; i < fits->numEntries(); i++) {
867 // if any fit (other than a genFit) is bad, flag point as bad
868 if (fits->get(i)->getCatIndex("type") != 5 && fits->get(i)->getRealValue("status") != 0) {
869 v->GetTreeItem(b)->SetColor(kRed);
870 break;
871 }
872 }
873 } else {
874 v->GetTreeItem(b)->SetColor(kBlue); // unknown fit status
875 }
876 }
877
878 // v.fBrowsers.insert(b);
879 }
880
881 // for pdfs, check for datasets too and add to list
882 /*if (get<RooAbsPdf>()) {
883 auto dsets = datasets();
884 if (!dsets.empty()) {
885 // check if already have .datasets() in browsables
886 bool found(false);
887 for(auto& p : fBrowsables) {
888 if (TString(p->GetName())==".datasets()") {found=true;
889 // add
890 break;
891 }
892 }
893 if (!found) {
894 fBrowsables.push_back(std::make_shared<xRooNode>(dsets));
895 }
896 }
897 }*/
898 // browse the browsables too
899 for (auto &v : fBrowsables) {
900 TString _name = v->GetName();
901 if (_name == ".memory")
902 continue; // hide the memory from browsing, if put in browsables
903 TString nameSave(v->TNamed::GetName());
904 TString titleSave(v->TNamed::GetTitle());
905 if (auto o = v->get(); o)
906 v->TNamed::SetNameTitle(o->GetName(), o->ClassName());
907 b->Add(v.get(), _name, -1);
908 if (auto o = v->get(); o)
909 v->TNamed::SetNameTitle(nameSave, titleSave);
910 }
911
912 b->SetSelected(this);
913}
914
916{
917 if (!set) {
918 // can't remove as causes a crash, need to remove from the browser first
919 /*for(auto itr = fBrowsables.begin(); itr != fBrowsables.end(); ++itr) {
920 if (strcmp((*itr)->GetName(),".vars")==0) {
921 fBrowsables.erase(itr);
922 }
923 }*/
924 } else {
925 auto v = std::make_shared<xRooNode>(vars());
926 fBrowsables.push_back(v);
927 if (auto l = GetListTree(nullptr)) {
928 l->AddItem(GetTreeItem(nullptr), v->GetName(), v.get());
929 }
930 }
931}
932
934{
935 for (auto &b : fBrowsables) {
936 if (strcmp(b->GetName(), ".vars") == 0)
937 return true;
938 }
939 return false;
940}
941
943{
944 if (strlen(GetName()) > 0 && GetName()[0] == '!')
945 return true;
946 if (strlen(GetName()) > 0 && GetName()[0] == '.' && !(TString(GetName()).BeginsWith(".Draw(\"")))
947 return true;
948 if (empty())
949 const_cast<xRooNode *>(this)->browse();
950 return !empty();
951}
952
953class Axis2 : public TAxis {
954
955public:
956 using TAxis::TAxis;
957 double GetBinWidth(Int_t bin) const override
958 {
959 if (auto v = var(); v)
960 return v->getBinWidth(bin - 1, GetName());
961 return 1;
962 }
963 double GetBinLowEdge(Int_t bin) const override
964 {
965 if (auto v = rvar(); v) {
966 return (bin == v->getBinning(GetName()).numBins() + 1) ? v->getBinning(GetName()).binHigh(bin - 2)
967 : v->getBinning(GetName()).binLow(bin - 1);
968 }
969 return bin - 1;
970 }
971 double GetBinUpEdge(Int_t bin) const override
972 {
973 if (auto v = rvar(); v)
974 return (bin == 0) ? v->getBinning(GetName()).binLow(bin) : v->getBinning(GetName()).binHigh(bin - 1);
975 return bin;
976 }
977
978 const char *GetTitle() const override
979 {
980 return (binning() && strlen(binning()->GetTitle())) ? binning()->GetTitle() : GetParent()->GetTitle();
981 }
982 void SetTitle(const char *title) override
983 {
984 if (binning()) {
985 const_cast<RooAbsBinning *>(binning())->SetTitle(title);
986 } else {
987 dynamic_cast<TNamed *>(GetParent())->SetTitle(title);
988 }
989 }
990
991 void Set(Int_t nbins, const double *xbins) override
992 {
993 if (auto v = dynamic_cast<RooRealVar *>(rvar()))
994 v->setBinning(RooBinning(nbins, xbins), GetName());
995 TAxis::Set(nbins, xbins);
996 }
997 void Set(Int_t nbins, const float *xbins) override
998 {
999 std::vector<double> bins(nbins + 1);
1000 for (int i = 0; i <= nbins; i++)
1001 bins.at(i) = xbins[i];
1002 return Set(nbins, &bins[0]);
1003 }
1004 void Set(Int_t nbins, double xmin, double xmax) override
1005 {
1006 if (auto v = dynamic_cast<RooRealVar *>(rvar()))
1007 v->setBinning(RooUniformBinning(xmin, xmax, nbins), GetName());
1008 TAxis::Set(nbins, xmin, xmax);
1009 }
1010
1011 const RooAbsBinning *binning() const { return var()->getBinningPtr(GetName()); }
1012
1013 Int_t FindFixBin(const char *label) const override { return TAxis::FindFixBin(label); }
1014 Int_t FindFixBin(double x) const override { return (binning()) ? (binning()->binNumber(x) + 1) : x; }
1015
1016private:
1017 RooAbsLValue *var() const { return dynamic_cast<RooAbsLValue *>(GetParent()); }
1018 RooAbsRealLValue *rvar() const { return dynamic_cast<RooAbsRealLValue *>(GetParent()); }
1019};
1020
1021std::shared_ptr<TObject> xRooNode::getObject(const std::string &name, const std::string &type) const
1022{
1023 // if (fParent) return fParent->getObject(name);
1024
1025 if (auto _owned = find(".memory"); _owned) {
1026 for (auto &o : *_owned) {
1027 if (name == o->GetName()) {
1028 if (type.empty() || o->get()->InheritsFrom(type.c_str()))
1029 return o->fComp;
1030 }
1031 }
1032 }
1033
1034 // see if have a provider
1035 auto _provider = fProvider;
1036 auto _parent = fParent;
1037 while (!_provider && _parent) {
1038 _provider = _parent->fProvider;
1039 _parent = _parent->fParent;
1040 }
1041 if (_provider)
1042 return _provider->getObject(name, type);
1043
1044 if (ws()) {
1045 std::shared_ptr<TObject> out;
1046 if (auto arg = ws()->arg(name.c_str()); arg) {
1047 auto _tmp = std::shared_ptr<TObject>(arg, [](TObject *) {});
1048 if (!type.empty() && arg->InheritsFrom(type.c_str()))
1049 return _tmp;
1050 if (!out)
1051 out = _tmp;
1052 }
1053 if (auto arg = ws()->data(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()->genobj(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()->embeddedData(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 = GETWSSNAPSHOTS(ws()).find(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 return out;
1082 }
1083 if (auto arg = get<RooAbsArg>()) {
1084 // can try all nodes
1085 RooArgSet nodes;
1086 arg->treeNodeServerList(&nodes);
1087 if (auto server = nodes.find(name.c_str())) {
1088 return std::shared_ptr<TObject>(server, [](TObject *) {});
1089 }
1090 }
1091 return nullptr;
1092}
1093
1095{
1096 if (fXAxis) {
1097 // check if num bins needs update or not
1098 if (auto cat = dynamic_cast<RooAbsCategory *>(fXAxis->GetParent());
1099 cat && cat->numTypes() != fXAxis->GetNbins()) {
1100 fXAxis.reset();
1101 } else {
1102 return fXAxis.get();
1103 }
1104 }
1105 RooAbsLValue *x = nullptr;
1106 if (auto a = get<RooAbsArg>(); a && a->isFundamental())
1107 x = dynamic_cast<RooAbsLValue *>(a); // self-axis
1108
1109 auto _parentX = (!x && fParent && !fParent->get<RooSimultaneous>()) ? fParent->GetXaxis() : nullptr;
1110
1111 auto o = get<RooAbsReal>();
1112 if (!o)
1113 return _parentX;
1114
1115 if (auto xName = o->getStringAttribute("xvar"); xName) {
1116 x = dynamic_cast<RooAbsLValue *>(getObject(xName).get());
1117 }
1118
1119 // if xvar has become set equal to an arg and this is a pdf, we will allow a do-over
1120 if (!x) {
1121 // need to choose from dependent fundamentals, in following order:
1122 // parentX (if not a glob), robs, globs, vars, args
1123
1124 if (_parentX && !dynamic_cast<RooAbsArg *>(_parentX->GetParent())->getAttribute("global") &&
1125 (o->dependsOn(*dynamic_cast<RooAbsArg *>(_parentX->GetParent())) || vars().empty())) {
1126 x = dynamic_cast<RooAbsLValue *>(_parentX->GetParent());
1127 } else if (auto _obs = obs(); !_obs.empty()) {
1128 for (auto &v : _obs) {
1129 if (!v->get<RooAbsArg>()->getAttribute("global")) {
1130 x = v->get<RooAbsLValue>();
1131 if (x)
1132 break;
1133 } else if (!x) {
1134 x = v->get<RooAbsLValue>();
1135 }
1136 }
1137 } else if (auto _pars = pars(); !_pars.empty()) {
1138 for (auto &v : _pars) {
1139 if (!v->get<RooAbsArg>()->getAttribute("Constant")) {
1140 x = v->get<RooAbsLValue>();
1141 if (x)
1142 break;
1143 } else if (!x) {
1144 x = v->get<RooAbsLValue>();
1145 }
1146 }
1147 }
1148
1149 if (!x) {
1150 return nullptr;
1151 }
1152 }
1153
1154 /* no longer 'remembering' xvars when call GetXaxis(), as was causing incorrect obs to go onto node
1155 * when e.g. adding histogram with bin errors to a channel - the statFactor and constraint terms were
1156 * created first, causing the channel's xaxis to become equal to a globs at some point because of a GetXaxis call
1157 * Alternatively we could clear the xvar attribute of all client nodes whenever we 'modify' something
1158 * Should do that if processing large workspaces becomes slow
1159 if (o != dynamic_cast<TObject *>(x)) {
1160 o->setStringAttribute("xvar", dynamic_cast<TObject *>(x)->GetName());
1161 } */
1162
1163 // decide binning to use
1164 TString binningName = o->getStringAttribute("binning");
1165 auto _bnames = x->getBinningNames();
1166 bool hasBinning = false;
1167 for (auto &b : _bnames) {
1168 if (b == binningName) {
1169 hasBinning = true;
1170 break;
1171 }
1172 }
1173 if (!hasBinning) {
1174 // doesn't have binning, so clear binning attribute
1175 // this can happen after Combine of models because binning don't get combined yet (should fix this)
1176 Warning("GetXaxis", "Binning %s not defined on %s - clearing", binningName.Data(),
1177 dynamic_cast<TObject *>(x)->GetName());
1178 o->setStringAttribute("binning", nullptr);
1179 binningName = "";
1180 }
1181
1182 if (binningName == "" && o != dynamic_cast<TObject *>(x)) {
1183 // has var has a binning matching this nodes name then use that
1184 auto __bnames = x->getBinningNames();
1185 for (auto &b : __bnames) {
1186 if (b == GetName())
1187 binningName = GetName();
1188 if (b == o->GetName()) {
1189 binningName = o->GetName();
1190 break;
1191 } // best match
1192 }
1193 if (binningName == "") {
1194 // if we are binned in this var then will define that as a binning
1195 if (/*o->isBinnedDistribution(*dynamic_cast<RooAbsArg *>(x))*/
1196 auto bins = _or_func(
1197 /*o->plotSamplingHint(*dynamic_cast<RooAbsRealLValue
1198 *>(x),-std::numeric_limits<double>::infinity(),std::numeric_limits<double>::infinity())*/
1199 (std::list<double> *)(nullptr),
1200 (dynamic_cast<RooAbsRealLValue *>(x))
1201 ? o->binBoundaries(*dynamic_cast<RooAbsRealLValue *>(x), -std::numeric_limits<double>::infinity(),
1202 std::numeric_limits<double>::infinity())
1203 : nullptr);
1204 bins) {
1205 std::vector<double> _bins;
1206 for (auto &b : *bins) {
1207 if (_bins.empty() || std::abs(_bins.back() - b) > 1e-5 * _bins.back())
1208 _bins.push_back(b);
1209 }
1210 fXAxis = std::make_shared<Axis2>(_bins.size() - 1, &_bins[0]);
1211 // add this binning to the var to avoid recalling ...
1212 if (auto _v = dynamic_cast<RooRealVar *>(x); _v) {
1213 _v->setBinning(RooBinning(_bins.size() - 1, &_bins[0], o->GetName()), o->GetName());
1214 _v->getBinning(o->GetName())
1215 .SetTitle(""); // indicates to use the current var title when building histograms etc
1216 //_v->getBinning(o->GetName()).SetTitle(strlen(dynamic_cast<TObject*>(x)->GetTitle()) ?
1217 // dynamic_cast<TObject*>(x)->GetTitle() : dynamic_cast<TObject*>(x)->GetName());
1218 }
1219 binningName = o->GetName();
1220 delete bins;
1221 } else if (_parentX) {
1222 // use parent axis binning if defined, otherwise we will default
1223 binningName = _parentX->GetName();
1224 }
1225 }
1226 }
1227
1228 if (!fXAxis) {
1229 if (auto r = dynamic_cast<RooAbsRealLValue *>(x); r) {
1230 if (r->getBinning(binningName).isUniform()) {
1231 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), r->getMin(binningName), r->getMax(binningName));
1232 } else {
1233 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), r->getBinning(binningName).array());
1234 }
1235 } else if (auto cat = dynamic_cast<RooCategory *>(x)) {
1236 std::vector<double> bins = {};
1237 for (int i = 0; i <= x->numBins(binningName); i++)
1238 bins.push_back(i);
1239 fXAxis = std::make_shared<Axis2>(x->numBins(binningName), &bins[0]);
1240 // TODO have to load current state of bin labels if was a category (sadly not a virtual method)
1241 int i = 1;
1242 std::map<int, std::string> cats; // fill into a map to preserve index ordering
1243 for (auto &c : *cat) {
1244 if (cat->isStateInRange(binningName, c.first.c_str())) {
1245 cats[c.second] = c.first;
1246 }
1247 }
1248 for (auto &[_, label] : cats) {
1249 fXAxis->SetBinLabel(i++, label.c_str());
1250 }
1251 }
1252 }
1253
1254 fXAxis->SetName(binningName);
1255 fXAxis->SetParent(dynamic_cast<TObject *>(x));
1256 return fXAxis.get();
1257}
1258
1259const char *xRooNode::GetIconName() const
1260{
1261 if (auto o = get(); o) {
1262 if (o->InheritsFrom("RooWorkspace"))
1263 return "TFile";
1264 if (o->InheritsFrom("RooAbsData"))
1265 return "TProfile";
1266 if (o->InheritsFrom("RooSimultaneous"))
1267 return "TH3D";
1268
1269 if (o->InheritsFrom("RooProdPdf"))
1270 return "a.C"; // or nullptr for folder
1271 if (o->InheritsFrom("RooRealSumPdf") || o->InheritsFrom("RooAddPdf"))
1272 return "TH2D";
1273 // if(o->InheritsFrom("RooProduct")) return "TH1D";
1274 if (o->InheritsFrom("RooFitResult")) {
1275 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitRooFitResult", true)) {
1276 gClient->GetMimeTypeList()->AddType("xRooFitRooFitResult", "xRooFitRooFitResult", "package.xpm",
1277 "package.xpm", "->Browse()");
1278 }
1279 return "xRooFitRooFitResult";
1280 }
1281 if (o->InheritsFrom("RooRealVar") || o->InheritsFrom("RooCategory")) {
1282 if (get<RooAbsArg>()->getAttribute("obs")) {
1283 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitObs", true)) {
1284 gClient->GetMimeTypeList()->AddType("xRooFitObs", "xRooFitObs", "x_pic.xpm", "x_pic.xpm", "->Browse()");
1285 }
1286 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitGlobs", true)) {
1287 gClient->GetMimeTypeList()->AddType("xRooFitGlobs", "xRooFitGlobs", "z_pic.xpm", "z_pic.xpm",
1288 "->Browse()");
1289 }
1290 return (get<RooAbsArg>()->getAttribute("global") ? "xRooFitGlobs" : "xRooFitObs");
1291 }
1292 return "TLeaf";
1293 }
1294 if (o->InheritsFrom("TStyle")) {
1295 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitTStyle", true)) {
1296 gClient->GetMimeTypeList()->AddType("xRooFitTStyle", "xRooFitTStyle", "bld_colorselect.xpm",
1297 "bld_colorselect.xpm", "->Browse()");
1298 }
1299 return "xRooFitTStyle";
1300 }
1301 if (o->InheritsFrom("RooConstVar")) {
1302 /*if (!gClient->GetMimeTypeList()->GetIcon("xRooFitRooConstVar",true)) {
1303 gClient->GetMimeTypeList()->AddType("xRooFitRooConstVar", "xRooFitRooConstVar", "stop_t.xpm", "stop_t.xpm",
1304 "->Browse()");
1305 }
1306 return "xRooFitRooConstVar";*/
1307 return "TMethodBrowsable-leaf";
1308 }
1309 if (o->InheritsFrom("RooStats::HypoTestInverterResult")) {
1310 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitScanStyle", true)) {
1311 gClient->GetMimeTypeList()->AddType("xRooFitScanStyle", "xRooFitScanStyle", "f2_s.xpm", "f2_s.xpm",
1312 "->Browse()");
1313 }
1314 return "xRooFitScanStyle";
1315 }
1316 if (o->InheritsFrom("RooStats::HypoTestResult")) {
1317 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitTestStyle", true)) {
1318 gClient->GetMimeTypeList()->AddType("xRooFitTestStyle", "xRooFitTestStyle", "diamond.xpm", "diamond.xpm",
1319 "->Browse()");
1320 }
1321 return "xRooFitTestStyle";
1322 }
1323 if (o->InheritsFrom("RooStats::HistFactory::FlexibleInterpVar"))
1324 return "TBranchElement-folder";
1325 if (o->InheritsFrom("RooAbsPdf")) {
1326 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitPDFStyle", true)) {
1327 gClient->GetMimeTypeList()->AddType("xRooFitPDFStyle", "xRooFitPDFStyle", "pdf.xpm", "pdf.xpm",
1328 "->Browse()");
1329 }
1330 return "xRooFitPDFStyle";
1331 }
1332 if (o->InheritsFrom("RooStats::ModelConfig")) {
1333 if (!gClient->GetMimeTypeList()->GetIcon("xRooFitMCStyle", true)) {
1334 gClient->GetMimeTypeList()->AddType("xRooFitMCStyle", "xRooFitMCStyle", "app_t.xpm", "app_t.xpm",
1335 "->Browse()");
1336 }
1337 return "xRooFitMCStyle";
1338 }
1339 if (auto a = dynamic_cast<RooAbsReal *>(o); a) {
1340 if (auto _ax = GetXaxis();
1341 _ax && (a->isBinnedDistribution(*dynamic_cast<RooAbsArg *>(_ax->GetParent())) ||
1342 (dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
1343 std::unique_ptr<std::list<double>>(a->binBoundaries(
1344 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
1345 std::numeric_limits<double>::infinity()))))) {
1346 return "TH1D";
1347 }
1348 return "TF1";
1349 }
1350 return o->ClassName();
1351 }
1352 if (!IsFolder()) {
1353 return "Unknown";
1354 }
1355 return nullptr;
1356}
1357
1358const char *xRooNode::GetNodeType() const
1359{
1360 if(auto rrs = get<RooRealSumPdf>(); rrs) {
1361 // if is BinnedLikelihood show that option
1362 if(rrs->getAttribute("BinnedLikelihood")) return "BinnedLikelihood";
1363 }
1364 if (auto o = get(); o && fParent && (fParent->get<RooProduct>() || fParent->get<RooRealSumPdf>())) {
1365 if (o->InheritsFrom("RooStats::HistFactory::FlexibleInterpVar"))
1366 return "Overall";
1367 if (o->InheritsFrom("PiecewiseInterpolation")) {
1368 // check if children are all RooHistFunc ... if so, it's a HistoFactor, otherwise it's a Varied
1369 bool isHisto = true;
1370 for (auto c : const_cast<xRooNode *>(this)->browse()) {
1371 if (!c->get<RooHistFunc>()) {
1372 isHisto = false;
1373 break;
1374 }
1375 }
1376 if (isHisto) {
1377 return (dynamic_cast<RooAbsArg *>(o)->getAttribute("density")) ? "HistoDensity" : "Histo";
1378 } else {
1379 return (dynamic_cast<RooAbsArg *>(o)->getAttribute("density")) ? "VariedDensity" : "Varied";
1380 }
1381 }
1382 if (o->InheritsFrom("RooHistFunc"))
1383 return (dynamic_cast<RooAbsArg *>(o)->getAttribute("density")) ? "SimpleDensity" : "Simple";
1384 if (o->InheritsFrom("RooBinWidthFunction"))
1385 return "Density";
1386 if (o->InheritsFrom("ParamHistFunc"))
1387 return "Shape";
1388 if (o->InheritsFrom("RooRealVar"))
1389 return "Norm";
1390 if (o->InheritsFrom("RooConstVar"))
1391 return "Const";
1392 }
1393 return "";
1394}
1395
1397{
1398 xRooNode out(".coords", nullptr, *this);
1399 // go up through parents looking for slice obs
1400 auto _p = std::shared_ptr<xRooNode>(const_cast<xRooNode *>(this), [](xRooNode *) {});
1401 while (_p) {
1402 TString pName(_p->GetName());
1403 // following is commented out while still considering, but idea is to include category in coords
1404 /*if (auto s = _p->get<RooSimultaneous>(); s && s->indexCat().InheritsFrom("RooCategory") &&
1405 !out.find(s->indexCat().GetName())) { auto cat = const_cast<RooCategory*>(dynamic_cast<const
1406 RooCategory*>(&s->indexCat()));
1407 // check if we have a pdf for every category ... if not then add to cut
1408 cat->clearRange("coordRange",true);
1409 bool hasMissing = false;
1410 std::string includedStates;
1411 for (auto state : *cat) {
1412 if (!s->getPdf(state.first.c_str())) {
1413 hasMissing = true;
1414 } else {
1415 if (!includedStates.empty()) {
1416 includedStates += ",";
1417 }
1418 includedStates += state.first;
1419 }
1420 }
1421 if (hasMissing) {
1422 if(includedStates.find(",") != std::string::npos) {
1423 cat->addToRange("coordRange",includedStates.c_str());
1424 } else {
1425 cat->setLabel(includedStates);
1426 }
1427 out.emplace_back(std::make_shared<xRooNode>(cat->GetName(),_p->getObject<RooAbsArg>(cat->GetName()),_p));
1428 }
1429 } else*/
1430 if (auto pos = pName.Index('='); pos != -1) {
1431 if (pos > 0 && pName(pos - 1) == '<') {
1432 // should be a range on a real lvalue, of form low<=name<high
1433 double low = TString(pName(0, pos - 1)).Atof();
1434 pName = pName(pos + 1, pName.Length());
1435 double high = TString(pName(pName.Index('<') + 1, pName.Length())).Atof();
1436 pName = pName(0, pName.Index('<'));
1437 if (auto _obs = _p->getObject<RooAbsRealLValue>(pName.Data()); _obs) {
1438 if (setVals) {
1439 _obs->setVal((high + low) / 2.);
1440 static_cast<RooRealVar *>(_obs.get())->setRange("coordRange", low, high);
1441 _obs->setStringAttribute(
1442 "coordRange", "coordRange"); // will need if we allow multi disconnected regions, need comma list
1443 }
1444 out.emplace_back(std::make_shared<xRooNode>(_obs->GetName(), _obs, _p));
1445 } else {
1446 throw std::runtime_error(TString::Format("Unknown observable: %s", pName.Data()));
1447 }
1448
1449 } else if (auto _obs = _p->getObject<RooAbsArg>(pName(0, pos)); _obs) {
1450 if (setVals) {
1451 if (auto _cat = dynamic_cast<RooAbsCategoryLValue *>(_obs.get()); _cat) {
1452 _cat->setLabel(pName(pos + 1, pName.Length()));
1453 } else if (auto _var = dynamic_cast<RooAbsRealLValue *>(_obs.get()); _var) {
1454 _var->setVal(TString(pName(pos + 1, pName.Length())).Atof());
1455 }
1456 }
1457 out.emplace_back(std::make_shared<xRooNode>(_obs->GetName(), _obs, _p));
1458 } else {
1459 throw std::runtime_error("Unknown observable, could not find");
1460 }
1461 }
1462 _p = _p->fParent;
1463 }
1464 return out;
1465}
1466
1467void xRooNode::_Add_(const char *name, const char *opt)
1468{
1469 try {
1470 Add(name, opt);
1471 } catch (const std::exception &e) {
1472 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
1473 kMBIconExclamation); // deletes self on dismiss?
1474 }
1475}
1476void xRooNode::_Vary_(const char *what)
1477{
1478 try {
1479 Vary(what);
1480 } catch (const std::exception &e) {
1481 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
1482 kMBIconExclamation); // deletes self on dismiss?
1483 }
1484}
1485
1487{
1488
1489 if (strcmp(GetName(), ".poi") == 0) {
1490 // demote a parameter from being a poi
1491 auto toRemove =
1492 (child.get<RooAbsArg>() || !find(child.GetName())) ? child : xRooNode(find(child.GetName())->fComp);
1493 if (toRemove) {
1494 if (!toRemove.get<RooAbsArg>()->getAttribute("poi")) {
1495 throw std::runtime_error(TString::Format("%s is not a poi", toRemove.GetName()));
1496 }
1497 toRemove.get<RooAbsArg>()->setAttribute("poi", false);
1498 return toRemove;
1499 }
1500 } else if (strcmp(GetName(), ".factors") == 0 || strcmp(GetName(), ".constraints") == 0 ||
1501 strcmp(GetName(), ".components") == 0) {
1502 auto toRemove =
1503 (child.get<RooAbsArg>() || !find(child.GetName())) ? child : xRooNode(find(child.GetName())->fComp);
1504 if (auto p = fParent->get<RooProdPdf>(); p) {
1505 auto pdf = toRemove.get<RooAbsArg>();
1506 if (!pdf)
1507 pdf = p->pdfList().find(child.GetName());
1508 if (!pdf)
1509 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1510 auto i = p->pdfList().index(*pdf);
1511 if (i >= 0) {
1512#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
1513 const_cast<RooArgList &>(p->pdfList()).remove(*pdf);
1514#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
1515 p->_pdfNSetList.erase(p->_pdfNSetList.begin() + i);
1516#else
1517 auto nset = p->_pdfNSetList.At(i);
1518 p->_pdfNSetList.Remove(nset);
1519 delete nset; // I don't think the RooLinkedList owned it so must delete ourself
1520#endif
1521 if (p->_extendedIndex == i)
1522 p->_extendedIndex = -1;
1523 else if (p->_extendedIndex > i)
1524 p->_extendedIndex--;
1525#else
1526 p->removePdfs(RooArgSet(*pdf));
1527#endif
1528 sterilize();
1529 return xRooNode(*pdf);
1530 } else {
1531 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1532 }
1533 } else if (auto p2 = fParent->get<RooProduct>(); p2) {
1534 auto arg = toRemove.get<RooAbsArg>();
1535 if (!arg)
1536 arg = p2->components().find(child.GetName());
1537 if (!arg)
1538 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1539 // remove server ... doesn't seem to trigger removal from proxy
1540#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
1541 p2->_compRSet.remove(*arg);
1542#else
1543 const_cast<RooArgList &>(p2->realComponents()).remove(*arg);
1544#endif
1545 p2->removeServer(*arg, true);
1546 sterilize();
1547 return xRooNode(*arg);
1548 } else if (fParent->get<RooSimultaneous>()) {
1549 // remove from all channels
1550 bool removed = false;
1551 for (auto &c : fParent->bins()) {
1552 try {
1553 c->constraints().Remove(toRemove);
1554 removed = true;
1555 } catch (std::runtime_error &) { /* wasn't a constraint in channel */
1556 }
1557 }
1558 sterilize();
1559 if (!removed)
1560 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1561 return toRemove;
1562 } else if (auto p4 = fParent->get<RooRealSumPdf>(); p4) {
1563 auto arg = toRemove.get<RooAbsArg>();
1564 if (!arg)
1565 arg = p4->funcList().find(child.GetName());
1566 if (!arg)
1567 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1568 // remove, including coef removal ....
1569 auto idx = p4->funcList().index(arg);
1570
1571 if (idx != -1) {
1572
1573 const_cast<RooArgList &>(p4->funcList()).remove(*arg);
1574 p4->removeServer(*arg, true);
1575 // have to be careful removing coef because if shared will end up removing them all!!
1576 std::vector<RooAbsArg *> _coefs;
1577 for (size_t ii = 0; ii < const_cast<RooArgList &>(p4->coefList()).size(); ii++) {
1578 if (ii != size_t(idx))
1579 _coefs.push_back(const_cast<RooArgList &>(p4->coefList()).at(ii));
1580 }
1581 const_cast<RooArgList &>(p4->coefList()).removeAll();
1582 for (auto &a : _coefs)
1583 const_cast<RooArgList &>(p4->coefList()).add(*a);
1584
1585 sterilize();
1586 } else {
1587 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1588 }
1589 return xRooNode(*arg);
1590 } else if (auto p5 = fParent->get<RooAddPdf>(); p5) {
1591 auto arg = toRemove.get<RooAbsArg>();
1592 if (!arg)
1593 arg = p5->pdfList().find(child.GetName());
1594 if (!arg)
1595 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1596 // remove, including coef removal ....
1597 auto idx = p5->pdfList().index(arg);
1598
1599 if (idx != -1) {
1600
1601 const_cast<RooArgList &>(p5->pdfList()).remove(*arg);
1602 p5->removeServer(*arg, true);
1603 // have to be careful removing coef because if shared will end up removing them all!!
1604 std::vector<RooAbsArg *> _coefs;
1605 for (size_t ii = 0; ii < const_cast<RooArgList &>(p5->coefList()).size(); ii++) {
1606 if (ii != size_t(idx))
1607 _coefs.push_back(const_cast<RooArgList &>(p5->coefList()).at(ii));
1608 }
1609 const_cast<RooArgList &>(p5->coefList()).removeAll();
1610 for (auto &a : _coefs)
1611 const_cast<RooArgList &>(p5->coefList()).add(*a);
1612
1613 sterilize();
1614 } else {
1615 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1616 }
1617 return xRooNode(*arg);
1618 } else if (auto p6 = fParent->get<RooAddition>(); p6) {
1619 auto arg = toRemove.get<RooAbsArg>();
1620 if (!arg)
1621 arg = p6->list().find(child.GetName());
1622 if (!arg)
1623 throw std::runtime_error(TString::Format("Cannot find %s in %s", child.GetName(), fParent->GetName()));
1624 // remove server ... doesn't seem to trigger removal from proxy
1625 const_cast<RooArgList &>(p6->list()).remove(*arg);
1626 p6->removeServer(*arg, true);
1627 sterilize();
1628 return xRooNode(*arg);
1629 }
1630 }
1631
1632 if (auto w = get<RooWorkspace>(); w) {
1633 xRooNode out(child.GetName());
1634 auto arg = w->components().find(child.GetName());
1635 if (!arg)
1636 arg = operator[](child.GetName())->get<RooAbsArg>();
1637 if (!arg) {
1638 throw std::runtime_error(TString::Format("Cannot find %s in workspace %s", child.GetName(), GetName()));
1639 }
1640 // check has no clients ... if so, cannot delete
1641 if (arg->hasClients()) {
1642 throw std::runtime_error(
1643 TString::Format("Cannot remove %s from workspace %s, because it has dependencies - first remove from those",
1644 child.GetName(), GetName()));
1645 }
1646 const_cast<RooArgSet &>(w->components()).remove(*arg); // deletes arg
1647 Info("Remove", "Deleted %s from workspace %s", out.GetName(), GetName());
1648 return out;
1649 } else if (get<RooProduct>() || get<RooProdPdf>()) {
1650 return factors().Remove(child);
1651 } else if (get<RooRealSumPdf>() || get<RooAddPdf>() || get<RooAddition>()) {
1652 return components().Remove(child);
1653 }
1654
1655 throw std::runtime_error("Removal not implemented for object type " +
1656 std::string(get() ? get()->ClassName() : "null"));
1657}
1658
1660{
1661
1662 class AutoUpdater {
1663 public:
1664 AutoUpdater(xRooNode &_n) : n(_n) {}
1665 ~AutoUpdater() { n.browse(); }
1666 xRooNode &n;
1667 };
1668 AutoUpdater xxx(*this);
1669
1670 TString sOpt(opt);
1671 bool considerType(sOpt == "+");
1672
1673 if (strlen(GetName()) > 0 && GetName()[0] == '!' && fParent) {
1674 // folder .. pass onto parent and add folder to child folder list
1675 const_cast<xRooNode &>(child).fFolder += GetName();
1676 return fParent->Add(child, opt);
1677 }
1678 // this is how to get the first real parent ... may be useful at some point?
1679 /*auto realParent = fParent;
1680 while(!realParent->get()) {
1681 realParent = realParent->fParent;
1682 if (!realParent) throw std::runtime_error("No parentage");
1683 }*/
1684
1685 // adding to a collection node will incorporate the child into the parent of the collection
1686 // in the appropriate way
1687 if (strcmp(GetName(), ".factors") == 0) {
1688 // multiply the parent
1689 return fParent->Multiply(child, opt);
1690 } else if (strcmp(GetName(), ".components") == 0) {
1691 // add to the parent
1692 return fParent->Add(child, opt);
1693 } else if (strcmp(GetName(), ".variations") == 0) {
1694 // vary the parent
1695 return fParent->Vary(child);
1696 } else if (strcmp(GetName(), ".constraints") == 0) {
1697 // constrain the parent
1698 return fParent->Constrain(child);
1699 } else if (strcmp(GetName(), ".bins") == 0 && fParent->get<RooSimultaneous>()) {
1700 // adding a channel (should adding a 'bin' be an 'Extend' operation?)
1701 return fParent->Vary(child);
1702 } else if ((strcmp(GetName(), ".globs") == 0)) {
1703 if (child.get<RooAbsArg>() || (!child.fComp && getObject<RooAbsArg>(child.GetName()))) {
1704 auto out = (child.get<RooAbsArg>()) ? child.get<RooAbsArg>() : getObject<RooAbsArg>(child.GetName()).get();
1705 out->setAttribute("obs");
1706 out->setAttribute("global");
1707 return xRooNode(*out, *this);
1708 }
1709 throw std::runtime_error("Failed to add global observable");
1710 } else if ((strcmp(GetName(), ".poi") == 0)) {
1711 if (child.get<RooAbsLValue>() || (!child.fComp && getObject<RooAbsLValue>(child.GetName()))) {
1712 auto out = (child.get<RooAbsArg>()) ? child.get<RooAbsArg>() : getObject<RooAbsArg>(child.GetName()).get();
1713 out->setAttribute("poi");
1714 return xRooNode(*out, *this);
1715 } else if (!child.get() && fParent->get<RooWorkspace>()) {
1716 // may be creating poi at same time as adding, try add to parent
1717 auto res = fParent->Add(child);
1718 if (res.get<RooAbsLValue>())
1719 return Add(res);
1720 }
1721 throw std::runtime_error("Failed to add parameter of interest");
1722 } else if ((strcmp(GetName(), ".pars") == 0 || strcmp(GetName(), ".vars") == 0) && fParent->get<RooWorkspace>()) {
1723 // adding a parameter, interpret as factory string unless no "[" then create RooRealVar
1724 TString fac(child.GetName());
1725 if (!fac.Contains("[") && !fac.Contains("("))
1726 fac += "[1]";
1727 return xRooNode(*fParent->get<RooWorkspace>()->factory(fac), fParent);
1728 } else if (strcmp(GetName(), ".datasets()") == 0) {
1729
1730 if (auto _data = child.get<RooAbsData>(); _data) {
1731 if (find(_data->GetName())) {
1732 throw std::runtime_error(TString::Format("Cannot add dataset %s, already exists for %s. If intending to "
1733 "combine datasets, please add directly to dataset",
1734 child->GetName(), GetName()));
1735 }
1736 return fParent->Add(child); // add the dataset to the parent
1737 }
1738
1739 // create a dataset - only allowed for pdfs or workspaces
1740 if (auto _ws = ws(); _ws && fParent) {
1741 sOpt.ToLower();
1742 if (!fParent->get<RooAbsPdf>() && (!fParent->get<RooWorkspace>() || sOpt == "asimov")) {
1743 throw std::runtime_error(
1744 "Datasets can only be created for pdfs or workspaces (except if generated dataset, then must be pdf)");
1745 }
1746
1747 if (sOpt == "asimov" || sOpt == "toy") {
1748 // generate expected dataset - note that globs will be frozen at this time
1749 auto _fr = fParent->fitResult();
1750 if (strlen(_fr->GetName()) == 0) { // ensure fit result has a name so that name is saved inside dataset
1751 _fr.get<RooFitResult>()->SetName(TUUID().AsString());
1752 }
1753 auto ds = fParent->generate(_fr, sOpt == "asimov");
1754 if (strlen(child.GetName())) {
1755 ds.SetName(child.GetName());
1756 ds.get<TNamed>()->SetName(child.GetName());
1757 }
1758 if (auto _ds = ds.get<RooAbsData>()) {
1759 _ws->import(*_ds);
1760 }
1761 if (_fr.get<RooFitResult>()->numStatusHistory() == 0) {
1762 if (!GETWSSNAPSHOTS(_ws).find(_fr->GetName())) {
1763 const_cast<RooLinkedList &>(GETWSSNAPSHOTS(_ws)).Add(_fr->Clone());
1764 }
1765 } else if (!_ws->obj(_fr->GetName())) {
1766 _ws->import((*_fr.get<RooFitResult>()));
1767 } // save fr to workspace, for later retrieval
1768 return xRooNode(*_ws->data(ds.GetName()), fParent);
1769 }
1770
1771 auto parentObs = fParent->obs(); // may own globs so keep alive
1772 auto _obs = parentObs.argList();
1773 // put globs in a snapshot
1774 std::unique_ptr<RooAbsCollection> _globs(_obs.selectByAttrib("global", true));
1775 // RooArgSet _tmp; _tmp.add(*_globs);_ws->saveSnapshot(child.GetName(),_tmp);
1776 _obs.remove(*_globs);
1777
1778 // include any coords
1779 _obs.add(coords(false).argList(), true);
1780 // include axis var too, provided it's an observable
1781 if (auto ax = GetXaxis(); ax && dynamic_cast<RooAbsArg *>(ax->GetParent())->getAttribute("obs")) {
1782 _obs.add(*dynamic_cast<RooAbsArg *>(ax->GetParent()));
1783 }
1784 // check if ws already has a dataset with this name, if it does we may need to extend columns
1785 if (auto _d = _ws->data(child.GetName()); _d) {
1786 // add any missing obs
1787 RooArgSet l(_obs);
1788 l.remove(*_d->get(), true, true);
1789 if (!l.empty()) {
1790 auto _dd = dynamic_cast<RooDataSet *>(_d);
1791 if (!_dd)
1792 throw std::runtime_error("Cannot extend dataset with new columns");
1793 for (auto &x : l) {
1794 _dd->addColumn(*x);
1795 }
1796 }
1797 } else {
1798 RooRealVar w("weightVar", "weightVar", 1);
1799 _obs.add(w);
1800 RooDataSet d(child.GetName(), child.GetTitle(), _obs, RooFit::WeightVar("weightVar"));
1801 _ws->import(d);
1802 // seems have to set bits after importing, not before
1803 if (auto __d = _ws->data(child.GetName()))
1804 __d->SetBit(1 << 20, _ws->allData().size() == 1); // sets as selected if is only ds
1805 }
1806 /*if(!_ws->data(child.GetName())) {
1807 RooRealVar w("weightVar", "weightVar", 1);
1808 RooArgSet _obs; _obs.add(w);
1809 RooDataSet d(child.GetName(), child.GetTitle(), _obs, "weightVar");
1810 _ws->import(d);
1811 }*/
1812 auto out = std::shared_ptr<TObject>(_ws->data(child.GetName()), [](TObject *) {});
1813
1814 if (out) {
1815 xRooNode o(out, fParent);
1816 if (child.get<TH1>())
1817 o = *child.get();
1818 return o;
1819 }
1820 }
1821 throw std::runtime_error("Cannot create dataset");
1822 }
1823
1824 if (!get()) {
1825 if (!fParent)
1826 throw std::runtime_error("Cannot add to null object with no parentage");
1827
1828 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
1829 try {
1830 fComp = fParent->Add(*this, "+").fComp;
1831 } catch (...) {
1832 resize(size() - 1);
1833 std::rethrow_exception(std::current_exception());
1834 }
1835 resize(size() - 1); // remove the temporarily added node
1836
1837 if (!fComp) {
1838 throw std::runtime_error("No object");
1839 }
1840 }
1841
1842 if (auto p = get<RooAbsData>(); p) {
1843 if (auto bb = getBrowsable(".sourceds"))
1844 bb->Add(child, opt);
1845 if (auto _data = child.get<RooDataSet>()) {
1846 auto ds = dynamic_cast<RooDataSet *>(p);
1847 if (!ds) {
1848 throw std::runtime_error("Can only add datasets to a dataset");
1849 }
1850
1851 // append any missing globs, and check any existing globs have matching values
1853 auto _globs = globs();
1854 for (auto &glob : child.globs()) {
1855 if (auto g = _globs.find(glob->GetName()); !g) {
1856 globsToAdd.addClone(*glob->get<RooAbsArg>());
1857 } else if (g->GetContent() != glob->GetContent()) {
1858 Warning("Add", "Global observable %s=%g in dataset %s mismatches %s value %g ... ignoring latter",
1859 g->GetName(), g->GetContent(), GetName(), child.GetName(), glob->GetContent());
1860 }
1861 }
1862 // add any existing globs to list then set the list
1863 if (auto _dglobs = p->getGlobalObservables()) {
1864 globsToAdd.addClone(*_dglobs);
1865 } else {
1866 for (auto g : _globs)
1867 globsToAdd.addClone(*g->get<RooAbsArg>());
1868 }
1869 p->setGlobalObservables(globsToAdd);
1870
1871 // append any missing observables to our dataset, then append the dataset
1872
1873 std::set<std::pair<RooAbsCategory *, RooCategory *>> cats;
1874
1875 for (auto col : *_data->get()) {
1876 if (!p->get()->contains(*col)) {
1877 ds->addColumn(*col);
1878 } else if (auto c = dynamic_cast<RooAbsCategory *>(col)) {
1879 // check if any of the states of c have different index to c2
1880 auto c2 = dynamic_cast<RooCategory *>(p->get()->find(*col));
1881 if (!c2) {
1882 throw std::runtime_error(
1883 TString::Format("unexpected type for regular observable: %s", col->GetName()));
1884 }
1885 bool iMatches = true;
1886 for (const auto &nameIdx : *c) {
1887 if (!c2->hasLabel(nameIdx.first) && !c2->hasIndex(nameIdx.second)) {
1888 // can define the state
1889 c2->defineType(nameIdx.first, nameIdx.second);
1890 } else if (c2->lookupIndex(nameIdx.first) != nameIdx.second) {
1891 iMatches = false;
1892 break; // state exists, but with different index!
1893 }
1894 }
1895 if (!iMatches)
1896 cats.insert({c, c2});
1897 }
1898 }
1899 if (cats.empty()) {
1900 ds->append(*_data);
1901 } else {
1902 // cannot use append, because if categoricals use same idx for different states, will not do correct thing
1903 for (int i = 0; i < _data->numEntries(); i++) {
1904 auto row = _data->get(i);
1905 auto w = _data->weight();
1906 ds->get()->assign(*row);
1907 for (auto [c, c2] : cats) {
1908 c2->setLabel(row->getCatLabel(c->GetName()));
1909 }
1910 ds->add(*ds->get(), w);
1911 }
1912 }
1913
1914 ds->SetTitle(TString(ds->GetTitle()) + " + " + _data->GetTitle());
1915 SetTitle(TString(GetTitle()) + " + " + child.GetTitle());
1916 return *this;
1917 }
1918 auto _arg = child.get<RooAbsArg>();
1919 if(auto _ds = dynamic_cast<RooDataSet*>(p); _arg && _ds) {
1920 // can add var or function of existing obs to dataset as a column
1921 _ds->addColumn(*_arg);
1922 _arg->setAttribute("obs");
1923 return xRooNode(*_arg,*this);
1924 }
1925 auto _h = child.get<TH1>();
1926 if (!_h) {
1927 throw std::runtime_error("Can only add histogram or var/expr or dataset to data");
1928 }
1929 auto _pdf = parentPdf();
1930 if (!_pdf)
1931 throw std::runtime_error("Could not find pdf");
1932 auto _ax = _pdf->GetXaxis();
1933 if (!_ax) {
1934 throw std::runtime_error("Cannot determine binning to add data");
1935 }
1936
1937 RooArgSet obs;
1938 obs.add(*dynamic_cast<RooAbsArg *>(_ax->GetParent()));
1939 obs.add(coords().argList()); // will also move obs to coords
1940
1941 // add any missing obs
1942 RooArgSet l(obs);
1943 l.remove(*p->get(), true, true);
1944 if (!l.empty()) {
1945 auto _d = dynamic_cast<RooDataSet *>(p);
1946 if (!_d)
1947 throw std::runtime_error("Cannot extend dataset with new columns");
1948 for (auto &x : l) {
1949 _d->addColumn(*x);
1950 }
1951 }
1952
1953 // before adding, ensure range is good to cover
1954 for (auto &o : obs) {
1955 if (auto v = dynamic_cast<RooRealVar *>(o); v) {
1956 if (auto dv = dynamic_cast<RooRealVar *>(p->get()->find(v->GetName())); dv) {
1957 if (v->getMin() < dv->getMin())
1958 dv->setMin(v->getMin());
1959 if (v->getMax() > dv->getMax())
1960 dv->setMax(v->getMax());
1961 }
1962 } else if (auto c = dynamic_cast<RooCategory *>(o); c) {
1963 if (auto dc = dynamic_cast<RooCategory *>(p->get()->find(c->GetName())); dc) {
1964 for (const auto &nameIdx : *c) {
1965 if (!dc->hasLabel(nameIdx.first)) {
1966 dc->defineType(nameIdx.first, nameIdx.second);
1967 }
1968 }
1969 }
1970 }
1971 }
1972
1973 for (int i = 1; i <= _h->GetNbinsX(); i++) {
1974 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(_ax->GetParent())) {
1975 if (!_h->GetXaxis()->GetBinLabel(i)) {
1976 throw std::runtime_error(
1977 TString::Format("Categorical observable %s requires bin labels", _ax->GetParent()->GetName()));
1978 } else if (!cat->hasLabel(_h->GetXaxis()->GetBinLabel(i))) {
1979 throw std::runtime_error(TString::Format("Categorical observable %s does not have label %s",
1980 _ax->GetParent()->GetName(), _h->GetXaxis()->GetBinLabel(i)));
1981 } else {
1982 cat->setLabel(_h->GetXaxis()->GetBinLabel(i));
1983 }
1984 } else {
1985 dynamic_cast<RooAbsRealLValue *>(_ax->GetParent())->setVal(_h->GetBinCenter(i));
1986 }
1987 p->add(obs, _h->GetBinContent(i));
1988 }
1989
1990 return *this;
1991 }
1992
1993 if (auto p = get<RooAddPdf>(); p) {
1994 auto cc = child.fComp;
1995 //bool isConverted = (cc != child.convertForAcquisition(*this, sOpt));
1996 child.convertForAcquisition(*this, sOpt);
1997 if ((child.get<RooAbsPdf>() || (!child.fComp && getObject<RooAbsPdf>(child.GetName())))) {
1998 auto out = (child.fComp) ? acquire(child.fComp) : getObject<RooAbsArg>(child.GetName());
1999 // don't add a coef if in 'all-extended' mode and this pdf is extendable
2000 auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out);
2001 if (!_pdf) {
2002 throw std::runtime_error("Something went wrong with pdf acquisition");
2003 }
2004
2005 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
2006 _pdf->dependsOn(*static_cast<RooAbsArg *>(_ax->GetParent()))) {
2007 auto _p = _pdf;
2008
2009 if (auto _boundaries = std::unique_ptr<std::list<double>>(_p->binBoundaries(
2010 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
2011 std::numeric_limits<double>::infinity()));
2012 !_boundaries && _ax->GetNbins() > 0) {
2013#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
2014 Warning("Add", "Adding unbinned pdf %s to binned %s - will wrap with RooBinSamplingPdf(...)",
2015 _p->GetName(), GetName());
2016 _p = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _p->GetName()), _p->GetTitle(),
2017 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *_p);
2018 _p->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
2019 if (!_p->getStringAttribute("alias"))
2020 _p->setStringAttribute("alias", out->GetName());
2021#else
2022 throw std::runtime_error(
2023 "unsupported addition of unbinned pdf to binned model - please upgrade to at least ROOT 6.24");
2024#endif
2025 _pdf = _p;
2026 }
2027 }
2028
2029 if (!(_pdf->canBeExtended() && p->coefList().empty())) {
2030 // if extended, use an extended binding as the coef
2031 // otherwise e.g. if adding a RooRealSumPdf the stacked histograms will be above the
2032 // actual pdf histogram because the pdf histogram is just normalized down
2033 if (_pdf->canBeExtended()) {
2034 // FIXME: ExtendedBinding needs the obs list passing to it ... should be fixed in RooFit
2035 std::cout << " warning " << _pdf->GetName() << " wont be correctly normalized" << std::endl;
2036 // until then, this will return "1" and so the pdf's histograms wont be normalized properly in relation
2037 // to stacks of its comps
2038 const_cast<RooArgList &>(p->coefList())
2039 .add(*acquireNew<RooExtendedBinding>(TString::Format("%s_extBind", _pdf->GetName()),
2040 TString::Format("Expected Events of %s", _pdf->GetTitle()),
2041 *_pdf));
2042 } else {
2043
2044 // need to create a coefficient for each existing pdf first, like above
2045 for (auto i = p->coefList().size(); i < p->pdfList().size(); i++) {
2046 const_cast<RooArgList &>(p->coefList())
2048 TString::Format("%s_extBind", p->pdfList().at(i)->GetName()),
2049 TString::Format("Expected Events of %s", p->pdfList().at(i)->GetTitle()),
2050 *static_cast<RooAbsPdf *>(p->pdfList().at(i))));
2051 }
2052
2053 const_cast<RooArgList &>(p->coefList()).add(*acquire2<RooAbsArg, RooRealVar>("1", "1", 1));
2054 }
2055 // ensure not in no-coef mode any more
2056 *reinterpret_cast<bool *>(reinterpret_cast<unsigned char *>(p) +
2057 p->Class()->GetDataMemberOffset("_allExtendable")) = false;
2058 *reinterpret_cast<bool *>(reinterpret_cast<unsigned char *>(p) +
2059 p->Class()->GetDataMemberOffset("_haveLastCoef")) = true;
2060 }
2061 const_cast<RooArgList &>(p->pdfList()).add(*_pdf);
2062 sterilize();
2063 return xRooNode(*_pdf, *this);
2064 } else if ((child.get<TH1>() || child.get<RooAbsReal>() ||
2065 (!child.get() && getObject<RooAbsReal>(child.GetName()))) &&
2066 !child.get<RooAbsPdf>()) {
2067 RooRealSumPdf *_pdf = nullptr;
2068 bool tooMany(false);
2069 for (auto &pp : factors()) {
2070 if (auto _p = pp->get<RooRealSumPdf>(); _p) {
2071 if (_pdf) {
2072 _pdf = nullptr;
2073 tooMany = true;
2074 break;
2075 } // more than one!
2076 _pdf = _p;
2077 }
2078 }
2079 if (_pdf) {
2080 return xRooNode(*_pdf, *this).Add(child);
2081 } else if (!tooMany) {
2082 // create a RooRealSumPdf to hold the child
2083 auto _sumpdf = Add(*acquireNew<RooRealSumPdf>(TString::Format("%s_samples", p->GetName()),
2084 TString::Format("%s samples", GetTitle()), RooArgList(),
2085 RooArgList(), true));
2086 _sumpdf.get<RooAbsArg>()->setStringAttribute("alias", "samples");
2087 return _sumpdf.Add(child);
2088 }
2089 }
2090 }
2091
2092 if (auto p = get<RooRealSumPdf>(); p) {
2093 std::shared_ptr<TObject> out;
2094 auto cc = child.fComp;
2095 bool isConverted = (cc != child.convertForAcquisition(*this, sOpt));
2096 if (child.get<RooAbsReal>()) {
2097 out = acquire(child.fComp);
2098 if (std::dynamic_pointer_cast<TH1>(cc) && !TString(cc->GetOption()).Contains("nostyle")) {
2099 xRooNode(out, *this).styles(cc.get()); // transfer style if adding a histogram
2100 }
2101 }
2102 if (!child.fComp && getObject<RooAbsReal>(child.GetName())) {
2103 Info("Add", "Adding existing function %s to %s", child.GetName(), p->GetName());
2104 out = getObject<RooAbsReal>(child.GetName());
2105 }
2106
2107 if (!out && !child.fComp) {
2108 std::shared_ptr<RooAbsArg> _func;
2109 // a null node .. so create either a new RooProduct or RooHistFunc if has observables (or no deps but has
2110 // x-axis)
2111 auto _obs = robs();
2112 if (!_obs.empty() || GetXaxis()) {
2113 if (_obs.empty()) {
2114 // using X axis to construct hist
2115 auto _ax = dynamic_cast<Axis2 *>(GetXaxis());
2116 std::unique_ptr<TH1D> h;
2117 {
2118 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
2119 h = std::make_unique<TH1D>(child.GetName(), child.GetTitle(), _ax->GetNbins(),
2120 _ax->binning()->array());
2121 }
2122 h->GetXaxis()->SetName(TString::Format("%s;%s", _ax->GetParent()->GetName(), _ax->GetName()));
2123 // technically convertForAcquisition has already acquired so no need to re-acquire but should be harmless
2124 _func = std::dynamic_pointer_cast<RooAbsArg>(acquire(xRooNode(*h).convertForAcquisition(*this)));
2125 } else if (_obs.size() == 1) {
2126 // use the single obs to make a TH1D
2127 auto _x = _obs.at(0)->get<RooAbsLValue>();
2128 auto _bnames = _x->getBinningNames();
2129 TString binningName = p->getStringAttribute("binning");
2130 for (auto &b : _bnames) {
2131 if (b == p->GetName()) {
2132 binningName = p->GetName();
2133 break;
2134 }
2135 }
2136 std::unique_ptr<TH1D> h;
2137 {
2138 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
2139 h = std::make_unique<TH1D>(child.GetName(), child.GetTitle(), _x->numBins(binningName),
2140 _x->getBinningPtr(binningName)->array());
2141 }
2142 h->GetXaxis()->SetName(
2143 TString::Format("%s;%s", dynamic_cast<TObject *>(_x)->GetName(), binningName.Data()));
2144 // technically convertForAcquisition has already acquired so no need to re-acquire but should be harmless
2145 _func = std::dynamic_pointer_cast<RooAbsArg>(acquire(xRooNode(*h).convertForAcquisition(*this)));
2146 Info("Add", "Created SimpleDensity factor %s (xaxis=%s) for %s", _func->GetName(), _obs.at(0)->GetName(),
2147 p->GetName());
2148 } else {
2149 throw std::runtime_error("Unsupported creation of new component in SumPdf for this many obs");
2150 }
2151 } else {
2152 _func = acquireNew<RooProduct>(TString::Format("%s_%s", p->GetName(), child.GetName()), child.GetTitle(),
2153 RooArgList());
2154 }
2155 _func->setStringAttribute("alias", child.GetName());
2156 out = _func;
2157 }
2158
2159 if (auto _f = std::dynamic_pointer_cast<RooHistFunc>(
2160 (child.get<RooProduct>()) ? child.factors()[child.GetName()]->fComp : out);
2161 _f) {
2162 // adding a histfunc directly to a sumpdf, should be a density
2163 _f->setAttribute("density");
2164 if (_f->getAttribute("autodensity")) {
2165 // need to divide by bin widths first
2166 for (int i = 0; i < _f->dataHist().numEntries(); i++) {
2167 auto bin_pars = _f->dataHist().get(i);
2168 _f->dataHist().set(*bin_pars, _f->dataHist().weight(i) / _f->dataHist().binVolume(*bin_pars));
2169 }
2170 _f->setAttribute("autodensity", false);
2171 _f->setValueDirty();
2172 }
2173
2174 // promote the axis vars to observables
2175 // can't use original child as might refer to unacquired deps
2176 for (auto &x : xRooNode("tmp", _f).vars()) {
2177 x->get<RooAbsArg>()->setAttribute("obs");
2178 }
2179 if (isConverted) {
2180 Info("Add", "Created %s factor RooHistFunc::%s for %s",
2181 _f->getAttribute("density") ? "SimpleDensity" : "Simple", _f->GetName(), p->GetName());
2182 }
2183 }
2184
2185 if (auto _p = std::dynamic_pointer_cast<RooAbsPdf>(out); _p) {
2186 // adding a pdf to a RooRealSumPdf will replace it with a RooAddPdf and put the RooRealSumPdf inside that
2187 // if pdf is extended will use in the "no coefficients" state, where the expectedEvents are taking from
2188 // the pdf integrals
2189 TString newName(p->GetName());
2190 newName.ReplaceAll("_samples", "");
2191 newName += "_components";
2192 Warning("Add", "converting samples to components");
2193
2194 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
2195 _p->dependsOn(*static_cast<RooAbsArg *>(_ax->GetParent()))) {
2196
2197 if (auto _boundaries = std::unique_ptr<std::list<double>>(_p->binBoundaries(
2198 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
2199 std::numeric_limits<double>::infinity()));
2200 !_boundaries && _ax->GetNbins() > 0) {
2201#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
2202 Warning("Add", "Adding unbinned pdf %s to binned %s - will wrap with RooBinSamplingPdf(...)",
2203 _p->GetName(), GetName());
2204 _p = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _p->GetName()), _p->GetTitle(),
2205 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *_p);
2206 _p->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
2207 if (!_p->getStringAttribute("alias"))
2208 _p->setStringAttribute("alias", out->GetName());
2209#else
2210 throw std::runtime_error(
2211 "unsupported addition of unbinned pdf to binned model - please upgrade to at least ROOT 6.24");
2212#endif
2213 }
2214 }
2215
2216 // require to be extended to be in coefficient-free mode ...
2217 // otherwise would lose the integral of the sumPdf (can't think of way to have a coef be the integral)
2218 if (!_p->canBeExtended()) {
2219 _p = acquireNew<RooExtendPdf>(TString::Format("%s_extended", _p->GetName()), _p->GetTitle(), *_p,
2220 *acquire2<RooAbsReal, RooRealVar>("1", "1", 1));
2221 }
2222
2223 return *(Replace(*acquireNew<RooAddPdf>(newName, _p->GetTitle(), RooArgList(*p, *_p)))
2224 .browse()[1]); // returns second node.
2225 }
2226
2227 if (auto _f = std::dynamic_pointer_cast<RooAbsReal>(out); _f) {
2228
2229 // todo: if adding a pdf, should actually replace RooRealSumPdf with a RooAddPdf and put
2230 // the sumPdf and *this* pdf inside that pdf
2231 // only exception is the binSamplingPdf below to integrate unbinned functions across bins
2232
2233 if (auto _ax = GetXaxis(); _ax && dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()) &&
2234 _f->dependsOn(*static_cast<RooAbsArg *>(_ax->GetParent()))) {
2235
2236 if (auto _boundaries = std::unique_ptr<std::list<double>>(_f->binBoundaries(
2237 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), -std::numeric_limits<double>::infinity(),
2238 std::numeric_limits<double>::infinity()));
2239 !_boundaries && _ax->GetNbins() > 0) {
2240#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 24, 00)
2241 Warning(
2242 "Add",
2243 "Adding unbinned function %s to binned %s - will wrap with RooRealSumPdf(RooBinSamplingPdf(...))",
2244 _f->GetName(), GetName());
2245 auto sumPdf = acquireNew<RooRealSumPdf>(TString::Format("%s_pdfWrapper", _f->GetName()), _f->GetTitle(),
2246 *_f, *acquire2<RooAbsArg, RooRealVar>("1", "1", 1), true);
2247 sumPdf->setStringAttribute("alias", _f->getStringAttribute("alias"));
2248 if (!sumPdf->getStringAttribute("alias"))
2249 sumPdf->setStringAttribute("alias", out->GetName());
2250 _f = acquireNew<RooBinSamplingPdf>(TString::Format("%s_binned", _f->GetName()), _f->GetTitle(),
2251 *dynamic_cast<RooAbsRealLValue *>(_ax->GetParent()), *sumPdf);
2252 _f->setStringAttribute("alias", std::dynamic_pointer_cast<RooAbsArg>(out)->getStringAttribute("alias"));
2253 if (!_f->getStringAttribute("alias"))
2254 _f->setStringAttribute("alias", out->GetName());
2255#else
2256 throw std::runtime_error(
2257 "unsupported addition of unbinned function to binned model - please upgrade to at least ROOT 6.24");
2258#endif
2259 }
2260 }
2261
2262 const_cast<RooArgList &>(p->coefList()).add(*acquire2<RooAbsArg, RooRealVar>("1", "1", 1));
2263 const_cast<RooArgList &>(p->funcList()).add(*_f);
2264 // inherit binning if we dont have one yet
2265 if (!p->getStringAttribute("binning"))
2266 p->setStringAttribute("binning", _f->getStringAttribute("binning"));
2267
2268 xRooNode _out(_f, *this);
2269 if (auto gf = p->getStringAttribute("global_factors"); gf) {
2270 TStringToken pattern(gf, ";");
2271 while (pattern.NextToken()) {
2272 auto fac = getObject<RooAbsReal>(pattern.Data());
2273 if (!fac) {
2274 throw std::runtime_error(TString::Format("Could not find global factor %s", pattern.Data()));
2275 }
2276 _out.Multiply(fac);
2277 }
2278 }
2279 sterilize();
2280 // clear children for reload and update shared axis
2281 clear();
2282 fXAxis.reset();
2283 p->setStringAttribute("xvar", nullptr);
2284 browse();
2285 return _out;
2286 }
2287 } else if (auto p2 = get<RooProdPdf>(); p2) {
2288 // can "add" to a RooProdPdf provided trying to add a RooAbsReal not a RooAbsPdf and have a zero or 1
2289 // RooRealSumPdf child.convertForAcquisition(*this); - don't convert here because want generated objects named
2290 // after roorealsumpdf
2291 // would like exception is if child is a factory string! - TODO
2292 if (child.get<RooAbsPdf>() || (!child.get() && getObject<RooAbsPdf>(child.GetName()))) {
2293 // can add if 0 or 1 RooAddPdf ....
2294 RooAddPdf *_pdf = nullptr;
2295 bool tooMany(false);
2296 for (auto &pp : factors()) {
2297 if (auto _p = pp->get<RooAddPdf>(); _p) {
2298 if (_pdf) {
2299 _pdf = nullptr;
2300 tooMany = true;
2301 break;
2302 } // more than one!
2303 _pdf = _p;
2304 }
2305 }
2306 if (_pdf) {
2307 return xRooNode(*_pdf, *this).Add(child);
2308 } else if (!tooMany) {
2309 auto out = this->operator[]("components")->Add(child);
2310 return out;
2311 }
2312 } else if ((child.get<TH1>() || child.get<RooAbsReal>() ||
2313 (!child.get() && getObject<RooAbsReal>(child.GetName()))) &&
2314 !child.get<RooAbsPdf>()) {
2315 RooRealSumPdf *_pdf = nullptr;
2316 RooAddPdf *_backup = nullptr;
2317 bool tooMany(false);
2318 for (auto &pp : factors()) {
2319 if (auto _p = pp->get<RooRealSumPdf>(); _p) {
2320 if (_pdf) {
2321 _pdf = nullptr;
2322 tooMany = true;
2323 break;
2324 } // more than one!
2325 _pdf = _p;
2326 } else if (auto _p2 = pp->get<RooAddPdf>(); _p2) {
2327 _backup = _p2;
2328 for (auto &_pdfa : pp->components()) {
2329 if (auto _p3 = _pdfa->get<RooRealSumPdf>(); _p3) {
2330 if (_pdf) {
2331 _pdf = nullptr;
2332 tooMany = true;
2333 break;
2334 } // more than one!
2335 _pdf = _p3;
2336 }
2337 }
2338 }
2339 }
2340 if (_pdf) {
2341 return xRooNode(*_pdf, *this).Add(child);
2342 } else if (_backup) {
2343 // added *INSIDE* the addPdf -- will create a RooRealSumPdf to hold it
2344 return xRooNode(*_backup, *this).Add(child);
2345 } else if (!tooMany) {
2346 auto out = this->operator[]("samples")->Add(child);
2347 // clear our x-axis to re-evaluate
2348 fXAxis.reset();
2349 p2->setStringAttribute("xvar", nullptr);
2350 return out;
2351 }
2352 }
2353 } else if (auto s = get<RooSimultaneous>(); s) {
2354
2355 // adding to a simultaneous means adding a bin
2356 return bins().Add(child);
2357
2358 // if the child is a RooAbsPdf can just add it as a new channel using name of pdf as the channel name
2359 // if child is a histogram, will create a RooProdPdf
2360
2361 } else if (auto w = get<RooWorkspace>(); w) {
2362 child.convertForAcquisition(
2363 *this, child.get() ? "" : "func" /* if child is a string, allow it to be passed to factory */);
2364 if (child.get()) {
2365 if (auto _d = child.get<RooAbsData>()) {
2366 // don't use acquire method to import, because that adds datasets as Embeddded
2367 if (!w->import(*_d)) {
2368 // should upgrade vars with any obs from the dataset
2369 if (_d->get()) {
2370 std::unique_ptr<RooAbsCollection>(w->allVars().selectCommon(*_d->get()))->setAttribAll("obs");
2371 }
2372 if (_d->getGlobalObservables()) {
2373 std::unique_ptr<RooAbsCollection> globs(w->allVars().selectCommon(*_d->getGlobalObservables()));
2374 globs->setAttribAll("obs");
2375 globs->setAttribAll("global");
2376 }
2377 return xRooNode(child.GetName(), *w->data(child.GetName()), *this);
2378 } else {
2379 throw std::runtime_error(
2380 TString::Format("Could not import dataset %s into workspace %s", child.GetName(), w->GetName())
2381 .Data());
2382 }
2383 } else {
2384 auto out = acquire(child.fComp);
2385 if (out)
2386 return xRooNode(child.GetName(), out, *this);
2387 }
2388 }
2389
2390 if (!child.empty() || child.fFolder == "!pdfs") {
2391 // create a RooSimultaneous using the children as the channels
2392 // children either have "=" in name if specifying channel cat name or otherwise assume
2393 std::string catName = "channelCat";
2394 if (!child.empty()) {
2395 if (TString ss = child.at(0)->GetName(); ss.Contains("=")) {
2396 catName = ss(0, ss.Index('='));
2397 }
2398 }
2399 auto _cat = acquire<RooCategory>(catName.c_str(), catName.c_str());
2400 _cat->setAttribute("obs");
2401 auto out = acquireNew<RooSimultaneous>(child.GetName(), child.GetTitle(), *_cat);
2402 Info("Add", "Created pdf RooSimultaneous::%s in workspace %s", out->GetName(), w->GetName());
2403 return xRooNode(out, *this);
2404 }
2405 } else if (auto coll = get<RooAbsCollection>(); coll && child.get<RooAbsArg>()) {
2406 if (coll->isOwning()) {
2407 coll->addOwned(*static_cast<RooAbsArg *>(child.get<RooAbsArg>()->Clone()));
2408 } else if (child.ws() != ws()) {
2409 coll->add(*static_cast<RooAbsArg *>(acquire(child.fComp).get()));
2410 } else {
2411 coll->add(*child.get<RooAbsArg>());
2412 }
2413 return xRooNode(child.GetName(), *coll->find(child.GetName()), *this);
2414 }
2415
2416 if (sOpt == "pdf") {
2417 // can only add a pdf to a workspace
2418 if (get<RooWorkspace>()) {
2419 const_cast<xRooNode &>(child).fFolder = "!pdfs";
2420 return Add(child);
2421 }
2422 } else if (sOpt == "channel") {
2423 // can add to a model or to a workspace (creates a RooProdPdf either way)
2424 if (get<RooSimultaneous>()) {
2425 return Vary(child);
2426 } else if (get<RooWorkspace>()) {
2427 std::shared_ptr<TObject> out;
2428 child.convertForAcquisition(*this);
2429 if (child.get<RooAbsPdf>()) {
2430 out = acquire(child.fComp);
2431 } else if (!child.fComp) {
2432 out = acquireNew<RooProdPdf>(child.GetName(),
2433 (strlen(child.GetTitle())) ? child.GetTitle() : child.GetName(), RooArgList());
2434 Info("Add", "Created channel RooProdPdf::%s in workspace %s", out->GetName(), get()->GetName());
2435 }
2436 return xRooNode(out, *this);
2437 }
2438 } else if (sOpt == "sample" || sOpt == "func") {
2439 if (get<RooProdPdf>()) {
2440 auto _mainChild = mainChild();
2441 if (_mainChild.get<RooRealSumPdf>()) {
2442 return _mainChild.Add(child, sOpt == "func" ? "func" : "");
2443 } else {
2444 return (*this)["samples"]->Add(child, sOpt == "func" ? "func" : "");
2445 }
2446 }
2447 } else if (sOpt == "dataset") {
2448 if (get<RooWorkspace>()) {
2449 // const_cast<xRooNode&>(child).fFolder = "!datasets";return Add(child);
2450 return (*this).datasets().Add(child);
2451 }
2452 }
2453
2454 if (considerType) {
2455
2456 // interpret 'adding' here as dependent on the object type ...
2457 if (get<RooSimultaneous>()) {
2458 return bins().Add(child);
2459 } else if (TString(child.GetName()).Contains('=')) {
2460 return variations().Add(child);
2461 } else if (get<RooProduct>() || get<RooProdPdf>()) {
2462 return factors().Add(child);
2463 }
2464 }
2465
2466 // Nov 2022 - removed ability to add placeholders ... could bring back if rediscover need for them
2467 // if (!child.get() && child.empty() && strlen(child.GetName())) {
2468 // // can add a 'placeholder' node, note it will be deleted at the next browse
2469 // xRooNode out(child.GetName(),nullptr,*this);
2470 // out.SetTitle(child.GetTitle());
2471 // emplace_back(std::make_shared<xRooNode>(out));
2472 // // update the parent in the out node so that it's copy of the parent knows it has itself in it
2473 // // actually maybe not want this :-/
2474 // //out.fParent = std::make_shared<Node2>(*this);
2475 // for(auto o : *gROOT->GetListOfBrowsers()) {
2476 // if(auto b = dynamic_cast<TBrowser*>(o); b && b->GetBrowserImp()){
2477 // if(auto _b = dynamic_cast<TGFileBrowser*>(
2478 // dynamic_cast<TRootBrowser*>(b->GetBrowserImp())->fActBrowser ); _b) {
2479 // auto _root = _b->fRootDir;
2480 // if (!_root) _root = _b->fListTree->GetFirstItem();
2481 // if (auto item = _b->fListTree->FindItemByObj(_root,this); item) {
2482 // _b->fListTree->AddItem(item,back()->GetName(),back().get());
2483 // }
2484 // }
2485 // }
2486 // }
2487 // return out;
2488 // }
2489
2490 throw std::runtime_error(TString::Format("Cannot add %s to %s", child.GetName(), GetName()));
2491}
2492
2493std::string xRooNode::GetPath() const
2494{
2495 if (!fParent)
2496 return GetName();
2497 return fParent->GetPath() + "/" + GetName();
2498}
2499
2501{
2502 // std::cout << "deleting " << GetPath() << std::endl;
2503}
2504
2506{
2507 if (auto a = get<RooAbsArg>()) {
2508 a->setAttribute("hidden", set);
2509 // if(auto item = GetTreeItem(nullptr); item) {
2510 // if(set) item->SetColor(kRed);
2511 // else item->ClearColor();
2512 // }
2513 }
2514}
2516{
2517 auto a = get<RooAbsArg>();
2518 if (a)
2519 return a->getAttribute("hidden");
2520 return false;
2521}
2522
2524{
2525
2526 if (get() == rhs.get()) {
2527 // nothing to do because objects are identical
2528 return *this;
2529 }
2530
2531 if (auto lhsa = get<RooAbsArg>(), rhsa = rhs.get<RooAbsArg>(); lhsa && rhsa && lhsa->isIdentical(*rhsa)) {
2532 return *this;
2533 }
2534
2535 if (get<RooWorkspace>() && rhs.get<RooWorkspace>()) {
2536
2537 // report which top-level pdfs will be combined
2538 std::set<std::string> pdfs;
2539 for (auto &c : rhs.components()) {
2540 if ((*this)["pdfs"]->find(c->GetName())) {
2541 pdfs.insert(c->GetName());
2542 }
2543 }
2544 if (pdfs.empty()) {
2545 Warning("Combine", "No pdfs will be combined. Please check and/or rename pdfs to match");
2546 } else {
2547 std::stringstream s;
2548 for (auto &p : pdfs)
2549 s << p << ",";
2550 Info("Combine", "pdfs that will be combined: %s", s.str().c_str());
2551 }
2552
2553 std::set<std::string> _np;
2554 auto mynp = np();
2555 for (auto &c : rhs.np()) {
2556 if (mynp.find(c->GetName())) {
2557 _np.insert(c->GetName());
2558 }
2559 }
2560 if (_np.empty()) {
2561 Warning("Combine", "No correlated np");
2562 } else {
2563 std::stringstream s;
2564 for (auto &p : _np)
2565 s << p << ",";
2566 Info("Combine", "np that will be shared (correlated): %s", s.str().c_str());
2567 }
2568 std::set<std::string> _poi;
2569 auto mypoi = poi();
2570 for (auto &c : rhs.poi()) {
2571 if (mypoi.find(c->GetName())) {
2572 _poi.insert(c->GetName());
2573 }
2574 }
2575 if (_poi.empty()) {
2576 Warning("Combine", "No correlated poi");
2577 } else {
2578 std::stringstream s;
2579 for (auto &p : _poi)
2580 s << p << ",";
2581 Info("Combine", "poi that will be shared (correlated): %s", s.str().c_str());
2582 }
2583
2584 // TODO: Check for derived components that have matching names and aren't top-level pdfs
2585 //
2586 // const auto comps = get<RooWorkspace>()->components();
2587 //
2588 // std::set<std::string> leafs;
2589 //
2590 // for(auto& c : rhs.get<RooWorkspace>()->components()) {
2591 // if(comps.find(c->GetName())) {
2592 //
2593 // }
2594 // }
2595 }
2596
2597 // combine components, factors, and variations ... when there is a name clash will combine on that object
2598 for (auto &c : rhs.components()) {
2599 if (get<RooWorkspace>() &&
2600 (c->fFolder == "!scratch" || c->fFolder == "!sets" || c->fFolder == "!snapshots" || c->fFolder == "!models"))
2601 continue;
2602 if (auto _c = components().find(c->GetName()); _c) {
2603 if (!silent) {
2604 Info("Combine", "Combining %s into %s", c->GetPath().c_str(), _c->GetPath().c_str());
2605 }
2606 _c->Combine(*c, true);
2607 } else {
2608 try {
2609 if (!silent) {
2610 Info("Combine", "Adding %s into %s", c->GetPath().c_str(), GetPath().c_str());
2611 }
2612 Add(*c);
2613 } catch (std::exception &e) {
2614 Warning("Combine", "Could not combine %s into %s", c->GetPath().c_str(), GetPath().c_str());
2615 }
2616 }
2617 }
2618
2619 if (!get<RooWorkspace>()) { // don't combine factors of a workspace
2620 for (auto &f : rhs.factors()) {
2621 if (auto _f = factors().find(f->GetName()); _f) {
2622 if (!silent) {
2623 Info("Combine", "Combining %s into %s", f->GetPath().c_str(), _f->GetPath().c_str());
2624 }
2625 _f->Combine(*f, true);
2626 } else {
2627 if (!silent) {
2628 Info("Combine", "Multiplying %s into %s", f->GetPath().c_str(), GetPath().c_str());
2629 }
2630 Multiply(*f);
2631 }
2632 }
2633 } else {
2634 // todo: go back through components, doing sets, snapshots, models, ....
2635 // do after import of pdfs etc so that can acquire the copies from the workspace
2636 for (auto &c : rhs.components()) {
2637 if (c->fFolder == "!sets") {
2638 if (components().find(c->GetName())) {
2639 Info("Combine", "Extending set %s", c->GetName());
2640 get<RooWorkspace>()->extendSet(c->GetName(), c->get<RooAbsCollection>()->contentsString().c_str());
2641 } else {
2642 Info("Combine", "Defining set %s", c->GetName());
2643 get<RooWorkspace>()->defineSet(c->GetName(), c->get<RooAbsCollection>()->contentsString().c_str());
2644 }
2645 }
2646 }
2647
2648 // also transfer datasets
2649 for (auto &ds : rhs.datasets()) {
2650 if (auto _ds = datasets().find(ds->GetName()); _ds) {
2651 if (!silent) {
2652 Info("Combine", "Combining %s into %s", ds->GetPath().c_str(), _ds->GetPath().c_str());
2653 }
2654 _ds->Add(*ds);
2655 } else {
2656 if (!silent) {
2657 Info("Combine", "Adding %s into %s", ds->GetPath().c_str(), GetPath().c_str());
2658 }
2659 datasets().Add(*ds);
2660 }
2661 }
2662 }
2663
2664 for (auto &v : rhs.variations()) {
2665 if (auto _v = variations().find(v->GetName()); _v) {
2666 if (!silent) {
2667 Info("Combine", "Combining variation %s into %s", v->GetPath().c_str(), _v->GetPath().c_str());
2668 }
2669 _v->Combine(*v, true);
2670 } else {
2671 if (!silent) {
2672 Info("Combine", "Varying %s into %s", v->GetPath().c_str(), GetPath().c_str());
2673 }
2674 Vary(*v);
2675 }
2676 }
2677
2678 if (get<RooSimultaneous>()) {
2679 // combine bins (channels) ... special case, never done silently
2680 for (auto &b : rhs.bins()) {
2681 if (auto _b = bins().find(b->GetName()); _b) {
2682 Info("Combine", "Combining %s into %s", b->GetPath().c_str(), _b->GetPath().c_str());
2683 _b->Combine(*b, true);
2684 } else {
2685 Info("Combine", "Extending with %s into %s", b->GetPath().c_str(), GetPath().c_str());
2686 Vary(*b); // extending channels currently done through Vary method
2687 }
2688 }
2689 }
2690
2691 // todo: Should also transfer over binnings of observables
2692
2693 return *this;
2694}
2695
2696xRooNode xRooNode::shallowCopy(const std::string &name, std::shared_ptr<xRooNode> parent)
2697{
2698 xRooNode out(name.c_str(), nullptr,
2699 parent /*? parent : fParent -- was passing fParent for getObject benefit before fProvider concept*/);
2700 // if(!parent) out.fAcquirer = true;
2701 if (!parent)
2702 out.fProvider = fParent;
2703
2704 auto o = get();
2705 if (!o) {
2706 return out;
2707 }
2708
2709 if (auto s = get<RooSimultaneous>(); s) {
2710 auto chans = bins();
2711 if (!chans.empty()) {
2712 // create a new RooSimultaneous with shallow copies of each channel
2713
2714 std::shared_ptr<RooSimultaneous> pdf = out.acquire<RooSimultaneous>(
2715 name.c_str(), o->GetTitle(), const_cast<RooAbsCategoryLValue &>(s->indexCat()));
2716
2717 for (auto &c : chans) {
2718 TString cName(c->GetName());
2719 cName = cName(cName.Index('=') + 1, cName.Length());
2720 // by passing out as the parent, will ensure out acquires everything created
2721 auto c_copy =
2722 c->shallowCopy(name + "_" + c->get()->GetName(), std::shared_ptr<xRooNode>(&out, [](xRooNode *) {}));
2723 pdf->addPdf(*dynamic_cast<RooAbsPdf *>(c_copy.get()), cName);
2724 }
2725 out.fComp = pdf;
2726 return out;
2727 }
2728 } else if (auto p = dynamic_cast<RooProdPdf *>(o); p) {
2729 // main pdf will be copied too
2730 std::shared_ptr<RooProdPdf> pdf =
2731 std::dynamic_pointer_cast<RooProdPdf>(out.acquire(std::shared_ptr<TObject>(p->Clone(/*name.c_str()*/)), false,
2732 true)); // use clone to copy all attributes etc too
2733 auto main = mainChild();
2734 if (main) {
2735 auto newMain =
2736 std::dynamic_pointer_cast<RooAbsArg>(out.acquire(std::shared_ptr<TObject>(main->Clone()), false, true));
2737 std::cout << newMain << " " << newMain->GetName() << std::endl;
2738 // pdf->replaceServer(*pdf->pdfList().find(main->GetName()), *newMain, true, true);
2739 // const_cast<RooArgList&>(pdf->pdfList()).replace(*pdf->pdfList().find(main->GetName()), *newMain);
2740 pdf->redirectServers(RooArgList(*newMain));
2741 }
2742 out.fComp = pdf;
2743 out.sterilize();
2744 return out;
2745 }
2746
2747 return out;
2748}
2749
2751{
2752 static std::unique_ptr<cout_redirect> capture;
2753 std::string captureStr;
2754 bool doCapture = false;
2755 if (!capture && gROOT->FromPopUp()) { // FromPopUp means user executed from the context menu
2756 capture = std::make_unique<cout_redirect>(captureStr);
2757 doCapture = true;
2758 }
2759
2760 TString sOpt(opt);
2761 int depth = 0;
2762 if (sOpt.Contains("depth=")) {
2763 depth = TString(sOpt(sOpt.Index("depth=") + 6, sOpt.Length())).Atoi();
2764 sOpt.ReplaceAll(TString::Format("depth=%d", depth), "");
2765 }
2766 int indent = 0;
2767 if (sOpt.Contains("indent=")) {
2768 indent = TString(sOpt(sOpt.Index("indent=") + 7, sOpt.Length())).Atoi();
2769 sOpt.ReplaceAll(TString::Format("indent=%d", indent), "");
2770 }
2771 bool _more = sOpt.Contains("m");
2772 if (_more)
2773 sOpt.Replace(sOpt.Index("m"), 1, "");
2774 if (sOpt != "")
2775 _more = true;
2776
2777 if (indent == 0) { // only print self if not indenting (will already be printed above if tree traverse)
2778 std::cout << GetPath();
2779 if (get() && get() != this) {
2780 std::cout << ": ";
2781 if (_more || (get<RooAbsArg>() && get<RooAbsArg>()->isFundamental()) || get<RooConstVar>() ||
2783 auto _deps = coords(false).argList(); // want to revert coords after print
2784 auto _snap = std::unique_ptr<RooAbsCollection>(_deps.snapshot());
2785 coords(); // move to coords before printing (in case this matters)
2786 get()->Print(sOpt);
2787 if (auto _fr = get<RooFitResult>(); _fr && dynamic_cast<RooStringVar *>(_fr->constPars().find(".log"))) {
2788 std::cout << "Minimization Logs:" << std::endl;
2789 std::cout << dynamic_cast<RooStringVar *>(_fr->constPars().find(".log"))->getVal() << std::endl;
2790 }
2791 _deps.assignValueOnly(*_snap);
2792 // std::cout << std::endl;
2793 } else {
2794 TString _suffix = "";
2795 if (auto _type = GetNodeType(); strlen(_type)) {
2796 // decided not to show const values until figure out how to update if value changes
2797 /*if (TString(_type)=="Const") _name += TString::Format("
2798 [%s=%g]",_type,v->get<RooConstVar>()->getVal()); else*/
2799 _suffix += TString::Format(" [%s]", _type);
2800 }
2801 if (auto fv = get<RooFormulaVar>()) {
2802 TString formu = TString::Format(" [%s]", fv->expression());
2803 for (size_t i = 0; i < fv->dependents().size(); i++) {
2804 formu.ReplaceAll(TString::Format("x[%zu]", i), fv->dependents()[i].GetName());
2805 }
2806 _suffix += formu;
2807 } else if (auto gv = get<RooGenericPdf>()) {
2808 TString formu = TString::Format(" [%s]", gv->expression());
2809 for (size_t i = 0; i < gv->dependents().size(); i++) {
2810 formu.ReplaceAll(TString::Format("x[%zu]", i), gv->dependents()[i].GetName());
2811 }
2812 _suffix += formu;
2813 } else if(auto pi = get<PiecewiseInterpolation>()) {
2814 // check if all interpCodes are the same. Will include in the NodeType
2815 std::set<int> interpCodes;
2816 for(auto& c : pi->interpolationCodes()) interpCodes.insert(c);
2817 if(interpCodes.size()==1) { _suffix += TString::Format(" [InterpCode=%d]",*interpCodes.begin()); }
2819 // check if all interpCodes are the same.
2820 std::set<int> interpCodes;
2821 for(auto& c : fiv->interpolationCodes()) interpCodes.insert(c==4 ? 5 : c); // in definition of FlexibleInterpVar 4 gets replaced with 5
2822 if(interpCodes.size()==1) { _suffix += TString::Format(" [InterpCode=%d]",*interpCodes.begin()); }
2823 }
2824 std::cout << get()->ClassName() << "::" << get()->GetName() << _suffix.Data() << std::endl;
2825 }
2826
2827 } else if (!get()) {
2828 std::cout << std::endl;
2829 }
2830 }
2831 const_cast<xRooNode *>(this)->browse();
2832 std::vector<std::string> folderNames;
2833 for (auto &k : *this) {
2834 if (std::find(folderNames.begin(), folderNames.end(), k->fFolder) == folderNames.end()) {
2835 folderNames.push_back(k->fFolder);
2836 }
2837 }
2838 for (auto &f : folderNames) {
2839 int i = 0;
2840 int iindent = indent;
2841 if (!f.empty()) {
2842 for (int j = 0; j < indent; j++)
2843 std::cout << " ";
2844 std::cout << f << std::endl;
2845 iindent += 1;
2846 }
2847 for (auto &k : *this) {
2848 if (k->fFolder != f) {
2849 i++;
2850 continue;
2851 }
2852 for (int j = 0; j < iindent; j++)
2853 std::cout << " ";
2854 std::cout << i++ << ") " << k->GetName() << " : ";
2855 if (k->get()) {
2856 if (_more || (k->get<RooAbsArg>() && k->get<RooAbsArg>()->isFundamental()) || k->get<RooConstVar>() ||
2857 k->get<RooAbsData>() /*|| k->get<RooProduct>()*/) {
2858 auto _deps = k->coords(false).argList();
2859 auto _snap = std::unique_ptr<RooAbsCollection>(_deps.snapshot());
2860 k->coords(); // move to coords before printing (in case this matters)
2861 k->get()->Print(sOpt); // assumes finishes with an endl
2862 _deps.assignValueOnly(*_snap);
2863 } else {
2864 TString _suffix = "";
2865 if (auto _type = k->GetNodeType(); strlen(_type)) {
2866 // decided not to show const values until figure out how to update if value changes
2867 /*if (TString(_type)=="Const") _name += TString::Format("
2868 [%s=%g]",_type,v->get<RooConstVar>()->getVal()); else*/
2869 _suffix += TString::Format(" [%s]", _type);
2870 }
2871 if (auto fv = k->get<RooFormulaVar>()) {
2872 TString formu = TString::Format(" [%s]", fv->expression());
2873 for (size_t j = 0; j < fv->dependents().size(); j++) {
2874 formu.ReplaceAll(TString::Format("x[%zu]", j), fv->dependents()[j].GetName());
2875 }
2876 _suffix += formu;
2877 } else if (auto gv = k->get<RooGenericPdf>()) {
2878 TString formu = TString::Format(" [%s]", gv->expression());
2879 for (size_t j = 0; j < gv->dependents().size(); j++) {
2880 formu.ReplaceAll(TString::Format("x[%zu]", j), gv->dependents()[j].GetName());
2881 }
2882 _suffix += formu;
2883 } else if(auto pi = k->get<PiecewiseInterpolation>()) {
2884 // check if all interpCodes are the same. Will include in the NodeType
2885 std::set<int> interpCodes;
2886 for(auto& c : pi->interpolationCodes()) interpCodes.insert(c);
2887 if(interpCodes.size()==1) { _suffix += TString::Format(" [InterpCode=%d]",*interpCodes.begin()); }
2888 } else if(auto fiv = k->get<RooStats::HistFactory::FlexibleInterpVar>()) {
2889 // check if all interpCodes are the same.
2890 std::set<int> interpCodes;
2891 for(auto& c : fiv->interpolationCodes()) interpCodes.insert(c==4 ? 5 : c); // in definition of FlexibleInterpVar 4 gets replaced with 5
2892 if(interpCodes.size()==1) { _suffix += TString::Format(" [InterpCode=%d]",*interpCodes.begin()); }
2893 }
2894 std::cout << k->get()->ClassName() << "::" << k->get()->GetName() << _suffix.Data() << std::endl;
2895 }
2896 if (depth != 0) {
2897 k->Print(sOpt + TString::Format("depth=%dindent=%d", depth - 1, iindent + 1));
2898 }
2899 } else
2900 std::cout << " NULL " << std::endl;
2901 }
2902 }
2903 if (doCapture) {
2904 capture.reset(); // no captureStr has the string to display
2905 // inject line breaks to avoid msgbox being too wide
2906 size_t lastBreak = 0;
2907 std::string captureStrWithBreaks;
2908 for (size_t i = 0; i < captureStr.size(); i++) {
2910 if (captureStr[i] == '\n') {
2911 lastBreak = i;
2912 }
2913 if (i - lastBreak > 150) {
2914 captureStrWithBreaks += '\n';
2915 lastBreak = i;
2916 }
2917 }
2918 const TGWindow *w =
2919 (gROOT->GetListOfBrowsers()->At(0))
2920 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
2921 : gClient->GetRoot();
2922 new TGMsgBox(gClient->GetRoot(), w, GetName(),
2923 captureStrWithBreaks.c_str()); //,nullptr,kMBDismiss,nullptr,kVerticalFrame,kTextLeft|kTextCenterY);
2924 }
2925}
2926
2928{
2929 if (!child.get()) {
2930
2931 if (auto v = get<RooRealVar>(); v) {
2932
2933 TString constrType = child.GetName();
2934 double mean = std::numeric_limits<double>::quiet_NaN();
2935 double sigma = mean;
2936 if (constrType.BeginsWith("gaussian(")) {
2937 // extract the mean and stddev parameters
2938 // if only one given, it is the stddev
2939 if (constrType.Contains(",")) {
2940 mean = TString(constrType(9, constrType.Index(',') - 9)).Atof();
2941 sigma = TString(constrType(constrType.Index(',') + 1, constrType.Index(')') - constrType.Index(',') + 1))
2942 .Atof();
2943 } else {
2944 mean = std::numeric_limits<double>::quiet_NaN(); // will use the var current value below to set mean
2945 sigma = TString(constrType(9, constrType.Index(')') - 9)).Atof();
2946 }
2947 constrType = "normal";
2948 } else if (constrType == "normal") {
2949 mean = 0;
2950 sigma = 1;
2951 } else if (constrType == "gaussian") {
2952 // extract parameters from the variable
2953 // use current value and error on v as constraint
2954 if (!v->hasError())
2955 throw std::runtime_error("No error on parameter for gaussian constraint");
2956 sigma = v->getError();
2957 mean = v->getVal();
2958 constrType = "normal";
2959 } else if (constrType == "poisson") {
2960 if (!v->hasError())
2961 throw std::runtime_error("No error on parameter for poisson constraint");
2962 mean = 1;
2963 sigma = pow(v->getVal() / v->getError(), 2);
2964 }
2965
2966 if (constrType == "poisson") {
2967 // use current value and error on v as constraint
2968 double tau_val = sigma;
2969 auto globs = acquire<RooRealVar>(Form("globs_%s", v->GetName()), Form("globs_%s", v->GetName()),
2970 v->getVal() * tau_val, (v->getVal() - 5 * v->getError()) * tau_val,
2971 (v->getVal() + 5 * v->getError()) * tau_val);
2972 globs->setConstant();
2973 globs->setAttribute("obs");
2974 globs->setAttribute("global");
2975 globs->setStringAttribute("nominal", TString::Format("%f", tau_val));
2976 auto tau = acquireNew<RooConstVar>(TString::Format("tau_%s", v->GetName()), "", tau_val);
2978 Form("pois_%s", v->GetName()), TString::Format("Poisson Constraint of %s", v->GetTitle()), *globs,
2979 *acquireNew<RooProduct>(TString::Format("mean_%s", v->GetName()),
2980 TString::Format("Poisson Constraint of %s", globs->GetTitle()),
2981 RooArgList(*v, *tau)),
2982 true /* no rounding */);
2983
2984 auto out = Constrain(xRooNode(Form("pois_%s", GetName()), constr));
2985 if (!v->hasError())
2986 v->setError(mean / sqrt(tau_val)); // if v doesnt have an uncert, will put one on it now
2987 Info("Constrain", "Added poisson constraint pdf RooPoisson::%s (tau=%g) for %s", out->GetName(), tau_val,
2988 GetName());
2989 return out;
2990 } else if (constrType == "normal") {
2991
2992 auto globs = acquire<RooRealVar>(Form("globs_%s", v->GetName()), Form("globs_%s", v->GetName()), mean,
2993 mean - 10 * sigma, mean + 10 * sigma);
2994 globs->setAttribute("obs");
2995 globs->setAttribute("global");
2996 globs->setConstant();
2997
2998 globs->setStringAttribute("nominal", TString::Format("%f", mean));
3000 Form("gaus_%s", v->GetName()), TString::Format("Gaussian Constraint of %s", v->GetTitle()), *globs, *v,
3001 *acquireNew<RooConstVar>(TString::Format("sigma_%s", v->GetName()), "", sigma));
3002 auto out = Constrain(xRooNode(Form("gaus_%s", GetName()), constr));
3003 if (!v->hasError())
3004 v->setError(sigma); // if v doesnt have an uncert, will put one on it now
3005 Info("Constrain", "Added gaussian constraint pdf RooGaussian::%s (mean=%g,sigma=%g) for %s", out->GetName(),
3006 mean, sigma, GetName());
3007 return out;
3008 }
3009 }
3010 } else if (auto p = child.get<RooAbsPdf>(); p) {
3011
3012 auto _me = get<RooAbsArg>();
3013 if (!_me) {
3014 throw std::runtime_error("Cannot constrain non arg");
3015 }
3016
3017 if (!p->dependsOn(*_me)) {
3018 throw std::runtime_error("Constraint does not depend on constrainee");
3019 }
3020
3021 // find a parent that can swallow this pdf ... either a RooProdPdf or a RooWorkspace
3022 auto x = fParent;
3023 while (x && !x->get<RooProdPdf>() && !x->get<RooSimultaneous>() && !x->get<RooWorkspace>()) {
3024 x = x->fParent;
3025 }
3026 if (!x) {
3027 throw std::runtime_error("Nowhere to put constraint");
3028 }
3029 // get datasets of the swallower, and add glob to any globs lists
3030 auto childGlobs = child.globs();
3031 if (!childGlobs.empty()) {
3032 for (auto d : x->datasets()) {
3033 if (auto globs = d->get<RooAbsData>()->getGlobalObservables()) {
3035 newGlobs.add(*childGlobs.get<RooArgList>());
3036 d->get<RooAbsData>()->setGlobalObservables(newGlobs);
3037 }
3038 }
3039 // also add to the workspaces globalObservables lists
3040 if (x->ws()) {
3041 for (auto &[k, v] : GETWSSETS(x->ws())) {
3042 if (k == "globalObservables" || TString(k).EndsWith("_GlobalObservables")) {
3043 const_cast<RooArgSet &>(v).add(*childGlobs.get<RooArgList>());
3044 }
3045 }
3046 }
3047 }
3048 if (auto s = x->get<RooSimultaneous>(); s) {
3049 // put into every channel that features parameter
3050 x->browse();
3051 for (auto &c : *x) {
3052 if (auto a = c->get<RooAbsArg>(); a->dependsOn(*_me))
3053 c->Multiply(child);
3054 }
3055 return child;
3056 } else if (x->get<RooProdPdf>()) {
3057 return x->Multiply(child);
3058 } else {
3059 return x->Add(child, "+");
3060 }
3061 }
3062
3063 throw std::runtime_error(TString::Format("Cannot constrain %s", GetName()));
3064}
3065
3067{
3068
3069 class AutoUpdater {
3070 public:
3071 AutoUpdater(xRooNode &_n) : n(_n) {}
3072 ~AutoUpdater() { n.browse(); }
3073 xRooNode &n;
3074 };
3075 AutoUpdater xxx(*this);
3076
3077 if (fBinNumber != -1) {
3078 // scaling a bin ...
3079 if (child.get<RooAbsReal>()) { // if not child then let fall through to create a child and call self again below
3080 // doing a bin-multiplication .. the parent should have a ParamHistFunc called binFactors
3081 // if it doesn't then create one
3082 auto o = std::dynamic_pointer_cast<RooAbsReal>(acquire(child.fComp));
3083
3084 // get binFactor unless parent is a ParamHistFunc already ...
3085
3086 auto binFactors = (fParent->get<ParamHistFunc>()) ? fParent : fParent->factors().find("binFactors");
3087
3088 // it can happen in a loop over bins() that another node has moved fParent inside a product
3089 // so check for fParent having a client with the ORIGNAME:<name> attribute
3090 if (!binFactors && fParent->get<RooAbsArg>()) {
3091 for (auto c : fParent->get<RooAbsArg>()->clients()) {
3092 if (c->IsA() == RooProduct::Class() &&
3093 c->getAttribute(TString::Format("ORIGNAME:%s", fParent->get()->GetName()))) {
3094 // try getting binFactors out of this
3095 binFactors = xRooNode(*c).factors().find("binFactors");
3096 break;
3097 }
3098 }
3099 }
3100
3101 if (!binFactors) {
3102 fParent
3103 ->Multiply(TString::Format("%s_binFactors",
3104 (fParent->mainChild().get())
3105 ? fParent->mainChild()->GetName()
3106 : (fParent->get() ? fParent->get()->GetName() : fParent->GetName()))
3107 .Data(),
3108 "blankshape")
3109 .SetName("binFactors"); // creates ParamHistFunc with all pars = 1 (shared const)
3110 binFactors = fParent->factors().find("binFactors");
3111 if (!binFactors) {
3112 throw std::runtime_error(
3113 TString::Format("Could not create binFactors in parent %s", fParent->GetName()));
3114 }
3115 // auto phf = binFactors->get<ParamHistFunc>();
3116
3117 // create RooProducts for all the bins ... so that added factors don't affect selves
3118 int i = 1;
3119 for (auto &b : binFactors->bins()) {
3120 auto p = acquireNew<RooProduct>(TString::Format("%s_bin%d", binFactors->get()->GetName(), i),
3121 TString::Format("binFactors of bin %d", i), RooArgList());
3122 p->setStringAttribute("alias", TString::Format("%s=%g", binFactors->GetXaxis()->GetParent()->GetName(),
3123 binFactors->GetXaxis()->GetBinCenter(i)));
3124 b->Multiply(*p);
3125 i++;
3126 }
3127 }
3128 // then scale the relevant bin ... if the relevant bin is a "1" then just drop in our factor (inside a
3129 // RooProduct though, to avoid it getting modified by subsequent multiplies)
3130 auto _bin = binFactors->bins().at(fBinNumber - 1);
3131 if (auto phf = binFactors->get<ParamHistFunc>(); phf && _bin) {
3132#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3133 RooArgList &pSet = phf->_paramSet;
3134#else
3135 RooArgList &pSet = const_cast<RooArgList &>(phf->paramList());
3136#endif
3137 if (strcmp(_bin->GetName(), "1") == 0) {
3139 for (std::size_t i = 0; i < pSet.size(); i++) {
3140 if (int(i) != fBinNumber - 1) {
3141 all.add(*pSet.at(i));
3142 } else {
3143 all.add(*o);
3144 }
3145 }
3146 pSet.removeAll();
3147 pSet.add(all);
3148 } else {
3149 _bin->fBinNumber = -1; // to avoid infinite loop
3150 return _bin->Multiply(child, opt);
3151 }
3152 // } else {else if(_bin->get<RooProduct>()) {
3153 // // multiply the element which will just add it as a factor in the rooproduct
3154 // return _bin->Multiply(child,opt);
3155 // } else {
3156 // // not a rooproduct in this bin yet ... so need to replace with a rooproduct and
3157 // multiply that
3158 // // this avoids the undesired behaviour of shared binFactors getting all impacted by
3159 // mulitplies RooArgList all; auto new_p =
3160 // acquireNew<RooProduct>(TString::Format("%s_bin%d",binFactors->get()->GetName(),fBinNumber),TString::Format("binFactors
3161 // of bin %d",fBinNumber),RooArgList(*_bin->get<RooAbsArg>()));
3162 // new_p->setStringAttribute("alias","")
3163 // for (int i = 0; i < phf->_paramSet.size(); i++) {
3164 // if (i != fBinNumber - 1) all.add(*phf->_paramSet.at(i));
3165 // else all.add(*new_p);
3166 // }
3167 // phf->_paramSet.removeAll();
3168 // phf->_paramSet.add(all);
3169 // // now multiply that bin having converted it to RooProduct
3170 // return binFactors->bins().at(fBinNumber - 1)->Multiply(child,opt);
3171 // }
3172 }
3173 return xRooNode(*o, binFactors);
3174 }
3175 } else if (!get() && fParent) {
3176 // try to 'create' object based on parentage
3177 // add child as a temporary child to help with decision making
3178 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
3179 try {
3180 fComp = fParent->Add(*this, "+").fComp;
3181 } catch (...) {
3182 resize(size() - 1);
3183 std::rethrow_exception(std::current_exception());
3184 }
3185 resize(size() - 1); // remove the temporarily added node
3186 }
3187
3188 if (!child.get()) {
3189 TString sOpt(opt);
3190 sOpt.ToLower();
3191 if (auto o = getObject<RooAbsReal>(child.GetName())) {
3192 auto out = Multiply(xRooNode(o, child.fParent));
3193 // have to protect bin case where get() is null (could change but then must change logic above too)
3194 if (get()) {
3195 Info("Multiply", "Scaled %s by existing factor %s::%s",
3196 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), o->ClassName(), o->GetName());
3197 }
3198 return out;
3199 } else if (sOpt == "const") {
3200 auto out = Multiply(RooConstVar(child.GetName(), child.GetTitle(), 1));
3201 if (get()) {
3202 Info("Multiply", "Scaled %s by new const factor %s",
3203 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
3204 }
3205 return out;
3206 } else if (sOpt == "norm") {
3207 if (TString(child.GetName()).Contains("[") && ws()) {
3208 // assume factory method wanted
3209 auto arg = ws()->factory(child.GetName());
3210 if (arg) {
3211 auto out = Multiply(*arg);
3212 if (get()) {
3213 Info("Multiply", "Scaled %s by new norm factor %s",
3214 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
3215 }
3216 return out;
3217 }
3218 throw std::runtime_error(TString::Format("Failed to create new normFactor %s", child.GetName()));
3219 }
3220 auto out = Multiply(RooRealVar(child.GetName(), child.GetTitle(), 1, -1e-5, 100));
3221 if (get()) {
3222 Info("Multiply", "Scaled %s by new norm factor %s",
3223 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
3224 }
3225 return out;
3226 } else if (sOpt == "shape" || sOpt == "simple" || sOpt == "blankshape") {
3227 // needs axis defined
3228 if (auto ax = GetXaxis(); ax) {
3229 auto h = std::shared_ptr<TH1>(BuildHistogram(dynamic_cast<RooAbsLValue *>(ax->GetParent()), true));
3230 h->Reset();
3231 for (int i = 1; i <= h->GetNbinsX(); i++) {
3232 h->SetBinContent(i, 1);
3233 }
3234 h->SetMinimum(0);
3235 h->SetMaximum(100);
3236 h->SetName(TString::Format(";%s", child.GetName())); // ; char indicates don't "rename" this thing
3237 h->SetTitle(child.GetTitle());
3238 if (sOpt.Contains("shape"))
3239 h->SetOption(sOpt);
3240 auto out = Multiply(*h);
3241 if (get()) {
3242 Info("Multiply", "Scaled %s by new %s factor %s",
3243 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), sOpt.Data(), out->GetName());
3244 }
3245 return out;
3246 }
3247 } else if (sOpt == "overall") {
3249 child.GetName(), child.GetTitle(), RooArgList(), 1, std::vector<double>(), std::vector<double>()));
3250 if (get() /* can happen this is null if on a bin node with no shapeFactors*/) {
3251 Info("Multiply", "Scaled %s by new overall factor %s",
3252 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
3253 }
3254 return out;
3255 } else if (sOpt == "func" && ws()) {
3256 // need to get way to get dependencies .. can't pass all as causes circular dependencies issues.
3257 if (auto arg = ws()->factory(TString("expr::") + child.GetName())) {
3258 auto out = Multiply(*arg);
3259 if (get() /* can happen this is null if on a bin node with no shapeFactors*/) {
3260 Info("Multiply", "Scaled %s by new func factor %s",
3261 mainChild().get() ? mainChild().get()->GetName() : get()->GetName(), out->GetName());
3262 }
3263 return out;
3264 }
3265 }
3266 }
3267 if (auto h = child.get<TH1>(); h && strlen(h->GetOption()) == 0 && strlen(opt) > 0) {
3268 // put the option in the hist
3269 h->SetOption(opt);
3270 }
3271 if (auto w = get<RooWorkspace>(); w) {
3272 // just acquire
3273 std::shared_ptr<TObject> out;
3274 child.convertForAcquisition(*this);
3275 if (child.get<RooAbsReal>())
3276 out = acquire(child.fComp);
3277 return out;
3278 }
3279
3280 if (strcmp(GetName(), ".coef") == 0) { // covers both .coef and .coefs
3281 // need to add this into the relevant coef ... if its not a RooProduct, replace it with one first
3282 if (auto p = fParent->fParent->get<RooAddPdf>()) {
3283 // may be in no-coef mode ... in which case must create coefs (use "ExtendedBindings" but note that these need
3284 // obs list passing to them
3285 if (p->coefList().empty() && !p->pdfList().empty()) {
3286 for (auto _pdf : p->pdfList()) {
3287 const_cast<RooArgList &>(p->coefList())
3288 .add(*acquireNew<RooExtendedBinding>(TString::Format("%s_extBind", _pdf->GetName()),
3289 TString::Format("Expected Events of %s", _pdf->GetTitle()),
3290 *static_cast<RooAbsPdf *>(_pdf)));
3291 }
3292 Info("Multiply", "Created RooExtendedBinding coefficients for all pdfs of %s so that can multiply coef",
3293 p->GetName());
3294 *reinterpret_cast<bool *>(reinterpret_cast<unsigned char *>(p) +
3295 p->Class()->GetDataMemberOffset("_allExtendable")) = false;
3296 *reinterpret_cast<bool *>(reinterpret_cast<unsigned char *>(p) +
3297 p->Class()->GetDataMemberOffset("_haveLastCoef")) = true;
3298 }
3299 for (size_t i = 0; i < p->pdfList().size(); i++) {
3300 if (p->pdfList().at(i) == fParent->get<RooAbsArg>()) {
3301 auto coefs = p->coefList().at(i);
3302 if (!coefs->InheritsFrom("RooProduct")) {
3304 if (!(strcmp(coefs->GetName(), "1") == 0 || strcmp(coefs->GetName(), "ONE") == 0))
3305 oldCoef.add(*coefs);
3306 auto newCoefs = fParent->acquireNew<RooProduct>(
3307 TString::Format("coefs_%s", fParent->GetName()),
3308 TString::Format("coefficients for %s", fParent->GetName()), oldCoef);
3310 for (size_t j = 0; j < p->coefList().size(); j++) {
3311 if (i == j) {
3312 oldCoefs.add(*newCoefs);
3313 } else {
3314 oldCoefs.add(*p->coefList().at(j));
3315 }
3316 }
3317 const_cast<RooArgList &>(p->coefList()).removeAll();
3318 const_cast<RooArgList &>(p->coefList()).add(oldCoefs);
3319 coefs = newCoefs.get();
3320 }
3321 return xRooNode(*coefs, fParent).Multiply(child);
3322 }
3323 }
3324 } else if (auto p2 = fParent->fParent->get<RooRealSumPdf>()) {
3325 // find our function in the funcList, and then update the coefs of it
3326
3327 for (size_t i = 0; i < p2->funcList().size(); i++) {
3328 if (p2->funcList().at(i) == fParent->get<RooAbsArg>()) {
3329 auto coefs = p2->coefList().at(i);
3330 if (!coefs->InheritsFrom("RooProduct")) {
3332 if (!(strcmp(coefs->GetName(), "1") == 0 || strcmp(coefs->GetName(), "ONE") == 0))
3333 oldCoef.add(*coefs);
3334 auto newCoefs = fParent->acquireNew<RooProduct>(
3335 TString::Format("coefs_%s", fParent->GetName()),
3336 TString::Format("coefficients for %s", fParent->GetName()), oldCoef);
3338 for (size_t j = 0; j < p2->coefList().size(); j++) {
3339 if (i == j) {
3340 oldCoefs.add(*newCoefs);
3341 } else {
3342 oldCoefs.add(*p2->coefList().at(j));
3343 }
3344 }
3345 const_cast<RooArgList &>(p2->coefList()).removeAll();
3346 const_cast<RooArgList &>(p2->coefList()).add(oldCoefs);
3347 coefs = newCoefs.get();
3348 }
3349 return xRooNode(*coefs, fParent).Multiply(child);
3350 }
3351 }
3352 }
3353 throw std::runtime_error("this coefs case is not supported");
3354 }
3355
3356 if (auto p = get<RooProduct>(); p) {
3357 std::shared_ptr<TObject> out;
3358 auto cc = child.fComp;
3359 bool isConverted = (child.convertForAcquisition(*this) != cc);
3360 if (child.get<RooAbsReal>())
3361 out = acquire(child.fComp);
3362
3363 // child may be a histfunc or a rooproduct of a histfunc and a paramhist if has stat errors
3364 if (auto _f = std::dynamic_pointer_cast<RooHistFunc>(
3365 (child.get<RooProduct>()) ? child.factors()[child.GetName()]->fComp : out);
3366 _f && _f->getAttribute("autodensity")) {
3367 // should we flag this as a density? yes if there's no other term marked as the density
3368 bool hasDensity = false;
3369 for (auto &f : factors()) {
3370 if (f->get<RooAbsArg>()->getAttribute("density")) {
3371 hasDensity = true;
3372 break;
3373 }
3374 }
3375 _f->setAttribute("density", !hasDensity && fParent && fParent->get<RooRealSumPdf>());
3376 if (_f->getAttribute("density")) {
3377
3378 // need to divide by bin widths first
3379 for (int i = 0; i < _f->dataHist().numEntries(); i++) {
3380 auto bin_pars = _f->dataHist().get(i);
3381 _f->dataHist().set(*bin_pars, _f->dataHist().weight(i) / _f->dataHist().binVolume(*bin_pars));
3382 }
3383 _f->setValueDirty();
3384
3385 // promote the axis vars to observables
3386 for (auto &x : xRooNode("tmp", _f).vars()) {
3387 x->get<RooAbsArg>()->setAttribute("obs");
3388 }
3389 }
3390 _f->setAttribute("autodensity", false);
3391 }
3392
3393 if (isConverted && child.get<RooHistFunc>()) {
3394 Info("Multiply", "Created %s factor %s in %s",
3395 child.get<RooAbsArg>()->getAttribute("density") ? "SimpleDensity" : "Simple", child->GetName(),
3396 p->GetName());
3397 } else if (isConverted && child.get<ParamHistFunc>()) {
3398 Info("Multiply", "Created Shape factor %s in %s", child->GetName(), p->GetName());
3399 }
3400
3401 if (auto _f = std::dynamic_pointer_cast<RooAbsReal>(out); _f) {
3402#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3403 p->_compRSet.add(*_f);
3404#else
3405 const_cast<RooArgList &>(p->realComponents()).add(*_f);
3406#endif
3407 p->setValueDirty();
3408
3409 browse();
3410 xRooNode _out(_f, *this);
3411 for (auto &_par : _out.pars()) {
3412 if (auto s = _par->get<RooAbsArg>()->getStringAttribute("boundConstraint"); s) {
3413 bool found = false;
3414 for (auto &_constr : _par->constraints()) {
3415 if (strcmp(s, _constr->get()->GetName()) == 0) {
3416 // constraint is already included
3417 found = true;
3418 break;
3419 }
3420 }
3421 if (!found) {
3422 Info("Multiply", "Pulling in %s boundConstraint: %s", _par->GetName(), s);
3423 auto _pdf = getObject<RooAbsPdf>(s);
3424 if (!_pdf) {
3425 throw std::runtime_error("Couldn't find boundConstraint");
3426 }
3427 _par->Constrain(_pdf);
3428 }
3429 }
3430 }
3431 sterilize();
3432 return _out;
3433 }
3434 } else if (auto p2 = get<RooProdPdf>(); p2) {
3435
3436 std::shared_ptr<TObject> out;
3437 child.convertForAcquisition(*this);
3438 if (child.get<RooAbsPdf>()) {
3439 out = acquire(child.fComp);
3440 } else if (child.get<RooAbsReal>() && mainChild().get<RooRealSumPdf>()) {
3441 // cannot multiply a RooProdPdf by a non pdf
3442 throw std::runtime_error(TString::Format("Cannot multiply %s by non-pdf %s", GetName(), child.GetName()));
3443 // return mainChild().Add(child); - nov 2022 - used to do this but now replaced with exception above
3444 } else if (!child.get() || child.get<RooAbsReal>()) {
3445 // need to create or hide inside a sumpdf or rooadpdf
3446 std::shared_ptr<RooAbsPdf> _pdf;
3447 if (!child.get() && strcmp(child.GetName(), "components") == 0) {
3449 Form("%s_%s", p2->GetName(), child.GetName()),
3450 (strlen(child.GetTitle()) && strcmp(child.GetTitle(), child.GetName())) ? child.GetTitle()
3451 : p2->GetTitle(),
3452 RooArgList() /*, RooArgList() forces coef-mode if we specify this list */);
3453 _pdf = _sumpdf;
3454 } else {
3456 Form("%s_%s", p2->GetName(), child.GetName()),
3457 (strlen(child.GetTitle()) && strcmp(child.GetTitle(), child.GetName())) ? child.GetTitle()
3458 : p2->GetTitle(),
3459 RooArgList(), RooArgList(), true);
3460 _sumpdf->setFloor(true);
3461 _pdf = _sumpdf;
3462 }
3463 _pdf->setStringAttribute("alias", child.GetName());
3464 // transfer axis attributes if present (TODO: should GetXaxis look beyond the immediate parent?)
3465 _pdf->setStringAttribute("xvar", p2->getStringAttribute("xvar"));
3466 _pdf->setStringAttribute("binning", p2->getStringAttribute("binning"));
3467 out = _pdf;
3468 Info("Multiply", "Created %s::%s in channel %s", _pdf->ClassName(), _pdf->GetName(), p2->GetName());
3469 if (child.get<RooAbsReal>())
3470 xRooNode(*out, *this).Add(child);
3471 }
3472
3473 if (auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out); _pdf) {
3474#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3475 const_cast<RooArgList &>(p2->pdfList()).add(*_pdf);
3476#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
3477 p2->_pdfNSetList.emplace_back(std::make_unique<RooArgSet>("nset"));
3478#else
3479 p->_pdfNSetList.Add(new RooArgSet("nset"));
3480#endif
3481 if (!p2->canBeExtended() && _pdf->canBeExtended()) {
3482 p2->_extendedIndex = p2->_pdfList.size() - 1;
3483 }
3484#else
3485 p2->addPdfs(RooArgSet(*_pdf));
3486#endif
3487 sterilize();
3488 browse();
3489 return xRooNode(_pdf, *this);
3490 }
3491 } else if (auto p3 = get<RooRealSumPdf>(); p3) {
3492 // multiplying all current and future components
3493 std::shared_ptr<TObject> out;
3494 child.convertForAcquisition(*this);
3495 if (child.get<RooAbsReal>()) {
3496 out = acquire(child.fComp);
3497 for (auto &c : components()) {
3498 c->Multiply(out);
3499 }
3500 TString s = p3->getStringAttribute("global_factors");
3501 if (s != "")
3502 s += ";";
3503 s += out->GetName();
3504 p3->setStringAttribute("global_factors", s);
3505 Info(
3506 "Multiply",
3507 "Flagged %s as a global factor in channel %s (is applied to all current and future samples in the channel)",
3508 out->GetName(), p3->GetName());
3509 return xRooNode(out, *this);
3510 }
3511
3512 } else if (auto p4 = get<RooAbsPdf>(); p4 && !(fParent && fParent->get<RooRealSumPdf>())) {
3513 // multiply the coefs (if this isn't part of a RooAddPdf or RooRealSumPdf then we will eventually throw exception
3514 return coefs().Multiply(child);
3515 } else if (auto p5 = get<RooAbsReal>(); p5 && (!get<RooAbsPdf>() || (fParent && fParent->get<RooRealSumPdf>()))) {
3516 // replace this obj with a RooProduct to allow for multiplication
3517
3518 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
3519 std::set<RooAbsArg *> cl;
3520 for (auto &arg : p5->clients()) {
3521 cl.insert(arg);
3522 }
3523
3524 // if multiple clients, see if only one client is in parentage route
3525 // if so, then assume thats the only client we should replace in
3526 if (cl.size() > 1) {
3527 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
3528 cl.clear();
3529 cl.insert(fParent->get<RooAbsArg>());
3530 } else {
3531 Warning("Multiply", "Scaling %s that has multiple clients", p5->GetName());
3532 }
3533 }
3534
3535 auto new_p = acquireNew<RooProduct>(TString::Format("prod_%s", p5->GetName()), p5->GetTitle(), RooArgList(*p5));
3536 // copy attributes over
3537 for (auto &a : p5->attributes())
3538 new_p->setAttribute(a.c_str());
3539 for (auto &a : p5->stringAttributes())
3540 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
3541 if (!new_p->getStringAttribute("alias"))
3542 new_p->setStringAttribute("alias", p5->GetName());
3543 auto old_p = p5;
3544 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
3545 for (auto arg : cl) {
3546 arg->redirectServers(RooArgSet(*new_p), false, true);
3547 }
3548
3549 fComp = new_p;
3550 return Multiply(child);
3551 }
3552
3553 // before giving up here, assume user wanted a norm factor type if child is just a name
3554 if (!child.get() && strlen(opt) == 0)
3555 return Multiply(child, "norm");
3556
3557 throw std::runtime_error(
3558 TString::Format("Cannot multiply %s by %s%s", GetPath().c_str(), child.GetName(),
3559 (!child.get() && strlen(opt) == 0) ? " (forgot to specify factor type?)" : ""));
3560}
3561
3563{
3564
3565 auto p5 = get<RooAbsArg>();
3566 if (!p5) {
3567 throw std::runtime_error("Only replacement of RooAbsArg is supported");
3568 }
3569 node.convertForAcquisition(*this, "func");
3570
3571 auto new_p = node.get<RooAbsArg>();
3572 if (!new_p) {
3573 throw std::runtime_error(TString::Format("Cannot replace with %s", node.GetName()));
3574 }
3575 auto out = acquire(node.fComp);
3576 new_p = std::dynamic_pointer_cast<RooAbsArg>(out).get();
3577
3578 std::set<RooAbsArg *> cl;
3579 for (auto &arg : p5->clients()) {
3580 if (arg == new_p)
3581 continue; // do not replace in self ... although redirectServers will prevent that anyway
3582 cl.insert(arg);
3583 }
3584
3585 // if multiple clients, see if only one client is in parentage route
3586 // if so, then assume thats the only client we should replace in
3587 if (cl.size() > 1) {
3588 if (fParent && fParent->get<RooAbsArg>() && cl.count(fParent->get<RooAbsArg>()) > 0) {
3589 cl.clear();
3590 cl.insert(fParent->get<RooAbsArg>());
3591 } else {
3592 std::stringstream clientList;
3593 for (auto c : cl)
3594 clientList << c->GetName() << ",";
3595 Warning("Replace", "Replacing %s in all clients: %s", p5->GetName(), clientList.str().c_str());
3596 }
3597 }
3598
3599 new_p->setAttribute(Form("ORIGNAME:%s", p5->GetName())); // used in redirectServers to say what this replaces
3600 for (auto arg : cl) {
3601 // if RooFormulaVar need to ensure the internal formula has been "constructed" otherwise will try to construct
3602 // it from the original expression that may have old parameter in it.
3603 if (auto p = dynamic_cast<RooFormulaVar *>(arg))
3604 p->ok(); // triggers creation of RooFormula
3605 arg->redirectServers(RooArgSet(*new_p), false, true);
3606 }
3607 return node;
3608}
3609
3611{
3612
3613 class AutoUpdater {
3614 public:
3615 AutoUpdater(xRooNode &_n) : n(_n) {}
3616 ~AutoUpdater() { n.browse(); }
3617 xRooNode &n;
3618 };
3619 AutoUpdater xxx(*this);
3620
3621 if (!get() && fParent) {
3622 // try to 'create' object based on parentage
3623 // add child as a temporary child to help with decision making
3624 auto _ref = emplace_back(std::shared_ptr<xRooNode>(&const_cast<xRooNode &>(child), [](TObject *) {}));
3625 try {
3626 fComp = fParent->Add(*this, "+").fComp;
3627 } catch (...) {
3628 resize(size() - 1);
3629 std::rethrow_exception(std::current_exception());
3630 }
3631 resize(size() - 1); // remove the temporarily added node
3632 }
3633
3634 if (auto p = mainChild(); p) {
3635 // variations applied to the main child if has one
3636 return p.Vary(child);
3637 }
3638
3639 if (auto s = get<RooSimultaneous>(); s && s->indexCat().IsA() == RooCategory::Class()) {
3640 // name is used as cat label
3641 std::string label = child.GetName();
3642 if (auto pos = label.find('='); pos != std::string::npos)
3643 label = label.substr(pos + 1);
3644 if (!s->indexCat().hasLabel(label)) {
3645 // auto idx = static_cast<const RooCategory &>(s->indexCat()).nextAvailableStateIndex(); - can't access,
3646 // protected method ... will have to just assume indices stay in sync
3647 // ensure added to category in any of our datasets too
3648 for (auto _ds : datasets()) {
3649 if (auto bb = _ds->getBrowsable(".sourceds")) {
3650 _ds = bb;
3651 } // shouldn't happen
3652 auto dsCat = _ds->robs()[s->indexCat().GetName()]->get<RooCategory>();
3653 if (!dsCat) {
3654 throw std::runtime_error(TString::Format("Failed to find %s regular observable in %s dataset",
3655 s->indexCat().GetName(), _ds->GetName()));
3656 }
3657 dsCat->defineType(label.c_str());
3658 }
3659 // adding to the index cat after, so that we don't need to generate subdatasets in the call to datasets() above
3660 // (missing cat will trigger cut)
3661 static_cast<RooCategory &>(const_cast<RooAbsCategoryLValue &>(s->indexCat())).defineType(label.c_str());
3662 }
3663 std::shared_ptr<TObject> out;
3664 child.convertForAcquisition(*this);
3665 if (child.get<RooAbsPdf>()) {
3666 out = acquire(child.fComp); // may create a channel from a histogram
3667 } else if (!child.fComp) {
3668 out = acquireNew<RooProdPdf>(TString::Format("%s_%s", s->GetName(), label.c_str()),
3669 (strlen(child.GetTitle())) ? child.GetTitle() : label.c_str(), RooArgList());
3670 Info("Vary", "Created channel RooProdPdf::%s in model %s", out->GetName(), s->GetName());
3671 }
3672
3673 if (auto _pdf = std::dynamic_pointer_cast<RooAbsPdf>(out); _pdf) {
3674 // before adding the channel, we need to see if we are about to add any globs, and if necessary we must update
3675 // the dataset globs
3676 std::set<RooAbsData *> dsToUpdate;
3677 for (auto _ds : datasets()) {
3678 if (auto bb = _ds->getBrowsable(".sourceds")) {
3679 _ds = bb;
3680 } // shouldn't happen
3681 if (_ds->get<RooAbsData>()->getGlobalObservables()) {
3682 dsToUpdate.insert(_ds->get<RooAbsData>());
3683 }
3684 }
3685 if (!dsToUpdate.empty()) {
3687 _pdf->leafNodeServerList(&leafs);
3688 std::unique_ptr<RooAbsCollection> globals(leafs.selectByAttrib("global", true));
3689 for (auto _ds : dsToUpdate) {
3690 std::string alist;
3692 globs.addClone(*_ds->getGlobalObservables());
3693 for (auto &aa : *globals) {
3694 if (!globs.contains(*aa)) {
3695 globs.addClone(*aa);
3696 alist += std::string(aa->GetName()) + ",";
3697 }
3698 }
3699 if (!alist.empty()) {
3700 Warning("Vary", "Adding %s to global observables of %s", alist.c_str(), _ds->GetName());
3701 _ds->setGlobalObservables(globs);
3702 }
3703 }
3704 }
3705
3706 s->addPdf(*_pdf, label.c_str());
3707 sterilize();
3708 // clear children for reload and update shared axis
3709 clear();
3710 fXAxis.reset();
3711 browse();
3712 return xRooNode(TString::Format("%s=%s", s->indexCat().GetName(), label.data()), _pdf, *this);
3713 }
3714
3715 } else if (auto p = get<RooStats::HistFactory::FlexibleInterpVar>(); p) {
3716
3717 // child needs to be a constvar ...
3718 child.convertForAcquisition(*this);
3719 auto _c = child.get<RooConstVar>();
3720 if (!_c && child.get()) {
3721 throw std::runtime_error("Only pure consts can be set as variations of a flexible interpvar");
3722 }
3723#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3724 double value = (_c ? _c->getVal() : p->_nominal);
3725 double nomVal = p->_nominal;
3726#else
3727 double value = (_c ? _c->getVal() : p->nominal());
3728 double nomVal = p->nominal();
3729#endif
3730
3731 TString cName(child.GetName());
3732 if (cName == "nominal") {
3733 p->setNominal(value);
3734 return *(this->variations().at(cName.Data()));
3735 }
3736 if (cName.CountChar('=') != 1) {
3737 throw std::runtime_error("unsupported variation form");
3738 }
3739 std::string parName = cName(0, cName.Index('='));
3740 double parVal = TString(cName(cName.Index('=') + 1, cName.Length())).Atof();
3741 if (parVal != 1 && parVal != -1) {
3742 throw std::runtime_error("unsupported variation magnitude");
3743 }
3744 bool high = parVal > 0;
3745
3746 if (parName.empty()) {
3747 p->setNominal(value);
3748 } else {
3749 auto v = fParent->getObject<RooRealVar>(parName);
3750 if (!v)
3751 v = fParent->acquire<RooRealVar>(parName.c_str(), parName.c_str(), -5, 5);
3752 if (!v->hasError())
3753 v->setError(1);
3754
3755 if (!p->findServer(*v)) {
3756#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3757 p->_paramList.add(*v);
3758 p->_low.push_back(0);
3759 p->_high.push_back(0);
3760 p->_interpCode.push_back(4);
3761#else
3762 const_cast<RooListProxy &>(p->variables()).add(*v);
3763 const_cast<std::vector<double> &>(p->low()).push_back(0);
3764 const_cast<std::vector<double> &>(p->high()).push_back(0);
3765 const_cast<std::vector<int> &>(p->interpolationCodes()).push_back(4);
3766#endif
3767 v->setAttribute(Form("SYMMETRIC%s_%s", high ? "+" : "-", GetName())); // flag for symmetrized
3768 }
3769
3770 if (high) {
3771 p->setHigh(*v, value);
3772 if (v->getAttribute(Form("SYMMETRIC+_%s", GetName()))) {
3773 p->setLow(*v, 2 * nomVal - value);
3774 }
3775 v->setAttribute(Form("SYMMETRIC-_%s", GetName()), false);
3776 } else {
3777 p->setLow(*v, value);
3778 if (v->getAttribute(Form("SYMMETRIC-_%s", GetName()))) {
3779 p->setHigh(*v, 2 * nomVal - value);
3780 }
3781 v->setAttribute(Form("SYMMETRIC+_%s", GetName()), false);
3782 }
3783
3784 /*if (!unconstrained && fParent->pars()[v->GetName()].constraints().empty()) {
3785 fParent->pars()[v->GetName()].constraints().add("normal");
3786 }*/
3787 }
3788 return *(this->variations().at(cName.Data()));
3789 } else if (auto p2 = get<PiecewiseInterpolation>(); p2) {
3790 TString cName(child.GetName());
3791 if (cName.CountChar('=') != 1) {
3792 throw std::runtime_error("unsupported variation form");
3793 }
3794 TString parName = cName(0, cName.Index('='));
3795 double parVal = TString(cName(cName.Index('=') + 1, cName.Length())).Atof();
3796 if (parVal != 1 && parVal != -1) {
3797 throw std::runtime_error("unsupported variation magnitude");
3798 }
3799#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3800 RooHistFunc *f = dynamic_cast<RooHistFunc *>(p2->_nominal.absArg());
3801 if (!f) {
3802 throw std::runtime_error(
3803 TString::Format("Interpolating %s instead of RooHistFunc", p2->_nominal.absArg()->ClassName()));
3804 }
3805#else
3806 RooHistFunc *f = dynamic_cast<RooHistFunc *>(const_cast<RooAbsReal *>(p2->nominalHist()));
3807 if (!f) {
3808 throw std::runtime_error(
3809 TString::Format("Interpolating %s instead of RooHistFunc", p2->nominalHist()->ClassName()));
3810 }
3811#endif
3812 RooHistFunc *nomf = f;
3813 RooHistFunc *otherf = nullptr;
3814 size_t i = 0;
3815 for (auto par : p2->paramList()) {
3816 if (parName == par->GetName()) {
3817 f = dynamic_cast<RooHistFunc *>((parVal > 0 ? p2->highList() : p2->lowList()).at(i));
3818 otherf = dynamic_cast<RooHistFunc *>((parVal > 0 ? p2->lowList() : p2->highList()).at(i));
3819 break;
3820 }
3821 i++;
3822 }
3823 if (i == p2->paramList().size() && !child.get<RooAbsReal>()) {
3824
3825 // need to add the parameter
3826 auto v = acquire<RooRealVar>(parName, parName, -5, 5);
3827 if (!v->hasError())
3828 v->setError(1);
3829
3830 std::shared_ptr<RooHistFunc> up(
3831 static_cast<RooHistFunc *>(f->Clone(Form("%s_%s_up", f->GetName(), parName.Data()))));
3832 std::shared_ptr<RooHistFunc> down(
3833 static_cast<RooHistFunc *>(f->Clone(Form("%s_%s_down", f->GetName(), parName.Data()))));
3834 // RooHistFunc doesn't clone it's data hist ... do it ourself (will be cloned again if imported into a ws)
3835#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3836 std::unique_ptr<RooDataHist> h1(
3837 static_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", up->GetName()))));
3838 std::unique_ptr<RooDataHist> h2(
3839 static_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", down->GetName()))));
3840 up->_dataHist = dynamic_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", up->GetName())));
3841 down->_dataHist = dynamic_cast<RooDataHist *>(f->dataHist().Clone(Form("hist_%s", down->GetName())));
3842#else
3843 up->cloneAndOwnDataHist(TString::Format("hist_%s", up->GetName()));
3844 down->cloneAndOwnDataHist(TString::Format("hist_%s", down->GetName()));
3845#endif
3846 auto ups = std::dynamic_pointer_cast<RooHistFunc>(acquire(up, false, true));
3847 auto downs = std::dynamic_pointer_cast<RooHistFunc>(acquire(down, false, true));
3848#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
3849 p2->_highSet.add(*ups.get());
3850 p2->_lowSet.add(*downs.get());
3851 p2->_interpCode.push_back(4);
3852 p2->_paramSet.add(*v);
3853#else
3854 const_cast<RooArgList &>(p2->highList()).add(*ups);
3855 const_cast<RooArgList &>(p2->lowList()).add(*downs);
3856 const_cast<std::vector<int> &>(p2->interpolationCodes()).push_back(4);
3857 const_cast<RooArgList &>(p2->paramList()).add(*v);
3858#endif
3859 p2->setValueDirty();
3860 f = ((parVal > 0) ? ups : downs).get();
3861 otherf = ((parVal > 0) ? downs : ups).get();
3862 // start off with everything being symmetric
3863 f->setStringAttribute("symmetrizes", otherf->GetName());
3864 f->setStringAttribute("symmetrize_nominal", nomf->GetName());
3865 otherf->setStringAttribute("symmetrized_by", f->GetName());
3866
3867 // constrain par if required
3868 /*if (!unconstrained && fParent->pars()[v->GetName()].constraints().empty()) {
3869 fParent->pars()[v->GetName()].constraints().add("normal");
3870 }*/
3871 }
3872
3873 // child.convertForAcquisition(*this);
3874 if (f) {
3875 if (child.get())
3876 xRooNode("tmp", *f, *this) = *child.get();
3877 f->setValueDirty();
3878 xRooNode out(*f, *this);
3879 out.sterilize();
3880 return out;
3881 }
3882
3883#if ROOT_VERSION_CODE > ROOT_VERSION(6, 37, 00)
3884 } else if (auto pmr = get<RooMultiReal>(); pmr) {
3885 TString cName(child.GetName());
3886 if (cName.CountChar('=') != 1) {
3887 throw std::runtime_error("unsupported variation form");
3888 }
3889 TString parName = cName(0, cName.Index('='));
3890 TString parVal = TString(cName(cName.Index('=') + 1, cName.Length()));
3891
3892 // add parVal to categorical if not already there
3893 if (!pmr->indexCategory()->hasLabel(parVal.Data())) {
3894 dynamic_cast<RooCategory &>(*(pmr->indexCategory())).defineType(parVal);
3895 }
3896 auto idx = pmr->indexCategory()->lookupIndex(parVal.Data());
3897 if (idx < 0) {
3898 throw std::runtime_error("Invalid index");
3899 }
3900 // add child to list ... use copy of "nominal" (0th) if child is empty
3901 child.convertForAcquisition(*this);
3902 auto _c = child.get<RooAbsReal>();
3903 if (!_c) {
3904 if (pmr->getModelList().empty()) {
3905 throw std::runtime_error("No real function given for variation, and no nominal function to clone");
3906 }
3907 _c = std::dynamic_pointer_cast<RooAbsReal>(
3908 acquire(std::shared_ptr<TObject>(pmr->getModelList().at(0)->Clone(
3909 TString::Format("%s_%s", get()->GetName(), child.GetName()))),
3910 false, true))
3911 .get();
3912 _c->setStringAttribute("alias", child.GetName());
3913 }
3914 const_cast<RooListProxy &>(pmr->getModelList()).add(*_c);
3915 return xRooNode(*_c, *this);
3916
3917#endif
3918
3919 } else if (auto p3 = get<RooConstVar>(); p3) {
3920
3921 // never vary the universal consts ... its too dangerous
3922 if (p3->getAttribute("RooRealConstant_Factory_Object")) {
3923 throw std::runtime_error("Cannot vary pure constants");
3924 }
3925
3926 // inject a FlexibleInterpVar ...
3927
3928 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
3929 std::set<RooAbsArg *> cl;
3930 for (auto &arg : p3->clients()) {
3931 cl.insert(arg);
3932 }
3933 // if multiple clients, see if only one client is in parentage route
3934 // if so, then assume thats the only client we should replace in
3935 if (cl.size() > 1) {
3936 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
3937 cl.clear();
3938 cl.insert(fParent->get<RooAbsArg>());
3939 } else {
3940 Warning("Vary", "Varying %s that has multiple clients", p3->GetName());
3941 }
3942 }
3943 p3->setStringAttribute("origName", p3->GetName());
3944 TString n = p3->GetName();
3945 p3->SetName(Form("%s_nominal", p3->GetName())); // if problems should perhaps not rename here
3946
3948 std::vector<double>(), std::vector<double>());
3949
3950 // copy attributes over
3951 for (auto &a : p3->attributes())
3952 new_p->setAttribute(a.c_str());
3953 for (auto &a : p3->stringAttributes())
3954 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
3955 // if (!new_p->getStringAttribute("alias")) new_p->setStringAttribute("alias",p->GetName());
3956 auto old_p = p3;
3957 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
3958 for (auto arg : cl) {
3959 arg->redirectServers(RooArgSet(*new_p), false, true);
3960 }
3961
3962 fComp = new_p;
3963 return Vary(child);
3964
3965 } else if (auto p4 = get<RooAbsReal>(); p4) {
3966 // inject an interpolation node
3967
3968 // get the list of clients BEFORE creating the new interpolation ... seems list of clients is inaccurate after
3969 std::set<RooAbsArg *> cl;
3970 for (auto &arg : p4->clients()) {
3971 cl.insert(arg);
3972 }
3973 // if multiple clients, see if only one client is in parentage route
3974 // if so, then assume thats the only client we should replace in
3975 if (cl.size() > 1) {
3976 if (cl.count(fParent->get<RooAbsArg>()) > 0) {
3977 cl.clear();
3978 cl.insert(fParent->get<RooAbsArg>());
3979 } else {
3980 Warning("Vary", "Varying %s that has multiple clients", p4->GetName());
3981 }
3982 }
3983 p4->setStringAttribute("origName", p4->GetName());
3984 TString n = p4->GetName();
3985 p4->SetName(Form("%s_nominal", p4->GetName())); // if problems should perhaps not rename here
3986
3987#if ROOT_VERSION_CODE > ROOT_VERSION(6, 37, 00)
3988 std::shared_ptr<RooAbsArg> new_p;
3989 // if alphanumeric variation name is not a 1 or -1, inject a RooMultiReal not a PiecewiseInterpolation ...
3990 TString cName(child.GetName());
3991 if (cName.CountChar('=') == 1) {
3992 TString parName = cName(0, cName.Index('='));
3993 double parVal = TString(cName(cName.Index('=') + 1, cName.Length())).Atof();
3994 if (parVal != 1 && parVal != -1) {
3995 // get the idxCat ...
3997 if (!idxCat) {
3998 throw std::runtime_error("Failed to acquire categorical index for RooMultiReal");
3999 }
4000 // add an index if none already
4001 if (idxCat->numTypes() == 0) {
4002 idxCat->defineType("nominal");
4003 }
4004 Info("Vary", "Creating a RooMultiReal with category %s", idxCat->GetName());
4006 n, p4->GetTitle(), *idxCat,
4007 RooArgList()); // can't pass nominal model as that will force defining types on category
4008 const_cast<RooListProxy &>(newFunc->getModelList()).add(*p4);
4009 new_p = newFunc;
4010 }
4011 }
4012 if (!new_p) {
4014 }
4015
4016#else
4018#endif
4019
4020 // copy attributes over
4021 for (auto &a : p4->attributes())
4022 new_p->setAttribute(a.c_str());
4023 for (auto &a : p4->stringAttributes())
4024 new_p->setStringAttribute(a.first.c_str(), a.second.c_str());
4025 // if (!new_p->getStringAttribute("alias")) new_p->setStringAttribute("alias",p->GetName());
4026 auto old_p = p4;
4027 new_p->setAttribute(Form("ORIGNAME:%s", old_p->GetName())); // used in redirectServers to say what this replaces
4028 for (auto arg : cl) {
4029 arg->redirectServers(RooArgSet(*new_p), false, true);
4030 }
4031
4032 fComp = new_p;
4033 return Vary(child);
4034 }
4035
4036 Print();
4037 throw std::runtime_error(TString::Format("Cannot vary %s with %s", GetName(), child.GetName()));
4038}
4039
4041{
4043}
4044
4045bool xRooNode::SetContent(double value, const char *par, double val)
4046{
4047 return SetContents(RooConstVar(GetName(), GetTitle(), value), par, val);
4048}
4049
4052 {
4053 if (x && b)
4054 x->setBinning(*b);
4055 if (b)
4056 delete b;
4057 }
4058 RooRealVar *x = nullptr;
4059 RooAbsBinning *b = nullptr;
4060};
4061
4063{
4064
4065 if (!get()) {
4066 fComp = std::shared_ptr<TObject>(const_cast<TObject *>(&o), [](TObject *) {});
4067 if (fParent && !fParent->find(GetName())) {
4068 // either a temporary or a placeholder so need to try genuinely adding
4069 fComp = fParent->Add(*this, "+").fComp;
4070 if (auto a = get<RooAbsArg>(); a && strcmp(a->GetName(), GetName()) && !a->getStringAttribute("alias")) {
4071 a->setStringAttribute("alias", GetName());
4072 }
4073 if (!fComp)
4074 throw std::runtime_error("Cannot determine type");
4075 return *this;
4076 }
4077 }
4078
4079 if (auto h = dynamic_cast<const TH1 *>(&o); h) {
4080 /*auto f = get<RooHistFunc>();
4081 if (!f) {
4082 // if it's a RooProduct locate child with the same name
4083 if (get<RooProduct>()) {
4084 f = factors()[GetName()]->get<RooHistFunc>();
4085 }
4086
4087
4088
4089 }*/
4090 bool _isData = get<RooAbsData>();
4092 if (_isData) {
4093 // need to ensure x-axis matches this h
4094 auto ax = GetXaxis();
4095 if (!ax)
4096 throw std::runtime_error("no xaxis");
4097 auto _v = dynamic_cast<RooRealVar *>(ax->GetParent());
4098 if (_v) {
4099 _b.x = _v;
4100 _b.b = dynamic_cast<RooAbsBinning *>(_v->getBinningPtr(nullptr)->Clone());
4101 if (h->GetXaxis()->IsVariableBinSize()) {
4102 _v->setBinning(RooBinning(h->GetNbinsX(), h->GetXaxis()->GetXbins()->GetArray()));
4103 } else {
4104 _v->setBinning(RooUniformBinning(h->GetXaxis()->GetXmin(), h->GetXaxis()->GetXmax(), h->GetNbinsX()));
4105 }
4106 }
4107 }
4108
4109 if (true) {
4110 for (int bin = 1; bin <= h->GetNbinsX(); bin++) {
4111 SetBinContent(bin, h->GetBinContent(bin));
4112 /*double value = h->GetBinContent(bin);
4113 auto bin_pars = f->dataHist().get(bin - 1);
4114 if (f->getAttribute("density")) {
4115 value /= f->dataHist().binVolume(*bin_pars);
4116 }
4117 f->dataHist().set(*bin_pars, value);*/
4118 if (!_isData && h->GetSumw2N() && !SetBinError(bin, h->GetBinError(bin)))
4119 throw std::runtime_error("Failed setting stat error");
4120 }
4121 return *this;
4122 }
4123 } else if (auto _c = dynamic_cast<const RooConstVar *>(&o); _c) {
4124
4125 if (auto a = get<RooAbsArg>();
4126 (a && a->isFundamental()) || get<RooConstVar>() || get<RooStats::HistFactory::FlexibleInterpVar>()) {
4127 SetBinContent(1, _c->getVal());
4128 return *this;
4129 } else if (get<RooAbsData>()) { // try to do assignment to a dataset (usually setting a bin content)
4130 SetBinContent(0, _c->getVal());
4131 return *this;
4132 }
4133 }
4134
4135 throw std::runtime_error("Assignment failed");
4136
4137 /*
4138
4139 if (fParent && !fParent->mk()) {
4140 throw std::runtime_error("mk failure");
4141 }
4142
4143 if (fComp) return *this;
4144
4145 if (o.InheritsFrom("RooAbsArg")) {
4146 fComp = acquire(std::shared_ptr<TObject>(const_cast<TObject*>(&o),[](TObject* o){}));
4147 std::dynamic_pointer_cast<RooAbsArg>(fComp)->setStringAttribute("alias",GetName());
4148 }
4149
4150 if (fComp && fParent) {
4151 fParent->incorporate(fComp);
4152 }
4153
4154
4155 return *this;
4156 */
4157}
4158
4159void xRooNode::_fit_(const char *constParValues, const char *options)
4160{
4161 try {
4162 // re-float all poi and np before fitting
4163 np().get<RooArgList>()->setAttribAll("Constant", false);
4164 poi().get<RooArgList>()->setAttribAll("Constant", false);
4165 auto _pars = pars();
4166 // std::unique_ptr<RooAbsCollection> snap(_pars.argList().snapshot());
4167 TStringToken pattern(constParValues, ",");
4168 std::map<RooAbsRealLValue *, double> valsToSet;
4169 while (pattern.NextToken()) {
4170 auto idx = pattern.Index('=');
4171 TString pat = (idx == -1) ? TString(pattern) : TString(pattern(0, idx));
4172 double val =
4173 (idx == -1) ? std::numeric_limits<double>::quiet_NaN() : TString(pattern(idx + 1, pattern.Length())).Atof();
4174 bool foundArg = false;
4175 for (auto p : _pars.argList()) {
4176 if (TString(p->GetName()).Contains(TRegexp(pat, true))) {
4177 foundArg = true;
4178 p->setAttribute("Constant", true);
4179 if (!std::isnan(val)) {
4180 valsToSet[dynamic_cast<RooAbsRealLValue *>(p)] = val;
4181 // dynamic_cast<RooAbsRealLValue *>(p)->setVal(val); // don't set yet, to allow for asimov dataset
4182 // creation based on current values
4183 }
4184 }
4185 }
4186 if (!foundArg) {
4187 throw std::runtime_error(std::string("Unrecognised parameter: ") + pat.Data());
4188 }
4189 }
4190
4191 // parse options
4192 TStringToken pattern2(options, ",");
4194 while (pattern2.NextToken()) {
4195 auto idx = pattern2.Index('=');
4196 TString pat = (idx == -1) ? TString(pattern2) : TString(pattern2(0, idx));
4197 TString val = TString(pattern2(idx + 1, pattern2.Length()));
4198 if (auto o = defaultOpts->FindObject(pat)) {
4199 defaultOpts->Remove(o);
4200 delete o;
4201 }
4202 defaultOpts->Add(new RooCmdArg(pat, val.IsDec() ? val.Atoi() : 0, 0, val.IsFloat() ? val.Atof() : 0., 0.,
4203 val.IsAlpha() ? val : nullptr));
4204 }
4205
4206 // use the first selected dataset
4207 auto _dsets = datasets();
4208 TString dsetName = "";
4209 for (auto &d : _dsets) {
4210 if (d->get()->TestBit(1 << 20)) {
4211 dsetName = d->get()->GetName();
4212 break;
4213 }
4214 }
4215 auto _nll = nll(dsetName.Data(), *defaultOpts);
4216 // can now set the values
4217 for (auto [p, v] : valsToSet) {
4218 p->setVal(v);
4219 }
4220 _nll.fitConfigOptions()->SetValue("LogSize", 65536);
4221 _nll.fitConfig()->MinimizerOptions().SetPrintLevel(0);
4222 auto fr = _nll.minimize();
4223 //_pars.argList() = *snap; // restore values - irrelevant as SetFitResult will restore values
4224 if (!fr.get())
4225 throw std::runtime_error("Fit Failed");
4226 SetFitResult(fr.get());
4228 for (unsigned int i = 0; i < fr->numStatusHistory(); i++) {
4229 statusCodes += TString::Format("\n%s = %d", fr->statusLabelHistory(i), fr->statusCodeHistory(i));
4230 }
4231 const TGWindow *w =
4232 (gROOT->GetListOfBrowsers()->At(0))
4233 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4234 : gClient->GetRoot();
4235 TString gofResult = "";
4236 if (_nll.fOpts->find("GoF")) {
4237 gofResult = TString::Format("GoF p-value = %g (mainTerm = %g)\n", fr->constPars().getRealValue(".pgof"),fr->constPars().getRealValue(".mainterm_pgof"));
4238 }
4239 if (fr->status() != 0) {
4240 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished with Bad Status Code",
4241 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n%s-------------%s",
4242 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), gofResult.Data(),
4243 statusCodes.Data()),
4245 } else if (fr->covQual() != 3 && _nll.fitConfig()->ParabErrors()) {
4246 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished with Bad Covariance Quality",
4247 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n%s-------------%s",
4248 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), gofResult.Data(),
4249 statusCodes.Data()),
4251 } else {
4252 new TGMsgBox(gClient->GetRoot(), w, "Fit Finished Successfully",
4253 TString::Format("%s\nData = %s\nFit Status Code = %d\nCov Quality = %d\n%s-------------%s",
4254 fr->GetName(), dsetName.Data(), fr->status(), fr->covQual(), gofResult.Data(),
4255 statusCodes.Data()));
4256 }
4257 TBrowser *b = nullptr;
4258 for (auto a : *gROOT->GetListOfBrowsers()) {
4259 b = dynamic_cast<TBrowser *>(a);
4260 if (b && GetTreeItem(b)) {
4261 break;
4262 }
4263 }
4264 if (b) {
4265 auto p = GetTreeItem(b);
4266 while (p) {
4267 if (TString(p->GetText()).BeginsWith("RooWorkspace::")) {
4268 // found the workspace ... refresh this node, and if there's a fits node, refresh that
4269 if (auto bi = dynamic_cast<TRootBrowser *>(b->GetBrowserImp())) {
4270 if (auto fb = dynamic_cast<TGFileBrowser *>(bi->GetActBrowser())) {
4271 fb->DoubleClicked(p, 0);
4272 if (auto first = p->GetFirstChild()) {
4273 do {
4274 if (first->IsOpen() && TString(first->GetText()) == "fits") {
4275 fb->DoubleClicked(first, 0);
4276 }
4277 } while ((first = first->GetNextSibling()));
4278 }
4279 }
4280 }
4281 break;
4282 } else {
4283 p = p->GetParent();
4284 }
4285 }
4286 }
4287 } catch (const std::exception &e) {
4288 new TGMsgBox(
4289 gClient->GetRoot(),
4290 (gROOT->GetListOfBrowsers()->At(0))
4291 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4292 : gClient->GetRoot(),
4293 "Exception", e.what(), kMBIconExclamation, kMBOk); // deletes self on dismiss?
4294 }
4295}
4296
4297void xRooNode::_generate_(const char *datasetName, bool expected)
4298{
4299 try {
4300 datasets().Add(datasetName, expected ? "asimov" : "toy");
4301 // refresh datasets folder of workspace
4302 TBrowser *b = nullptr;
4303 for (auto a : *gROOT->GetListOfBrowsers()) {
4304 b = dynamic_cast<TBrowser *>(a);
4305 if (b && GetTreeItem(b)) {
4306 break;
4307 }
4308 }
4309 if (b) {
4310 auto p = GetTreeItem(b);
4311 while (p) {
4312 if (TString(p->GetText()).BeginsWith("RooWorkspace::")) {
4313 // found the workspace ... refresh this node, and if there's a datasets node, refresh that
4314 if (auto bi = dynamic_cast<TRootBrowser *>(b->GetBrowserImp())) {
4315 if (auto fb = dynamic_cast<TGFileBrowser *>(bi->GetActBrowser())) {
4316 fb->DoubleClicked(p, 0);
4317 if (auto first = p->GetFirstChild()) {
4318 do {
4319 if (first->IsOpen() && TString(first->GetText()) == "datasets") {
4320 fb->DoubleClicked(first, 0);
4321 }
4322 } while ((first = first->GetNextSibling()));
4323 }
4324 }
4325 }
4326 break;
4327 } else {
4328 p = p->GetParent();
4329 }
4330 }
4331 }
4332 } catch (const std::exception &e) {
4333 new TGMsgBox(
4334 gClient->GetRoot(),
4335 (gROOT->GetListOfBrowsers()->At(0))
4336 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4337 : gClient->GetRoot(),
4338 "Exception", e.what(),
4339 kMBIconExclamation); // deletes self on dismiss?
4340 }
4341}
4342
4343void xRooNode::_scan_(const char *what, double nToys, const char *xvar, int nBinsX, double lowX,
4344 double highX /*, const char*, int, double, double*/, const char *constParValues,
4345 const char *options)
4346{
4347 try {
4350
4351 // use the first selected dataset
4352 auto _dsets = datasets();
4353 TString dsetName = "";
4354 for (auto &d : _dsets) {
4355 if (d->get()->TestBit(1 << 20)) {
4356 dsetName = d->get()->GetName();
4357 break;
4358 }
4359 }
4360 auto _pars = pars();
4361 std::unique_ptr<RooAbsCollection> snap(_pars.argList().snapshot());
4362 TStringToken pattern(constParValues, ",");
4363 while (pattern.NextToken()) {
4364 auto idx = pattern.Index('=');
4365 TString pat = (idx == -1) ? TString(pattern) : TString(pattern(0, idx));
4366 double val =
4367 (idx == -1) ? std::numeric_limits<double>::quiet_NaN() : TString(pattern(idx + 1, pattern.Length())).Atof();
4368 bool foundArg = false;
4369 for (auto par : _pars.argList()) {
4370 if (TString(par->GetName()).Contains(TRegexp(pat, true))) {
4371 foundArg = true;
4372 par->setAttribute("Constant", true);
4373 if (!std::isnan(val)) {
4374 dynamic_cast<RooAbsRealLValue *>(par)->setVal(val);
4375 }
4376 }
4377 }
4378 if (!foundArg) {
4379 throw std::runtime_error(std::string("Unrecognised parameter: ") + pat.Data());
4380 }
4381 }
4382
4383 // parse options
4384 TStringToken pattern2(options, ",");
4386 while (pattern2.NextToken()) {
4387 auto idx = pattern2.Index('=');
4388 TString pat = (idx == -1) ? TString(pattern2) : TString(pattern2(0, idx));
4389 TString val = TString(pattern2(idx + 1, pattern2.Length()));
4390 if (auto o = defaultOpts->FindObject(pat)) {
4391 defaultOpts->Remove(o);
4392 delete o;
4393 }
4394 defaultOpts->Add(new RooCmdArg(pat, val.IsDec() ? val.Atoi() : 0, 0, val.IsFloat() ? val.Atof() : 0., 0.,
4395 val.IsAlpha() ? val : nullptr));
4396 }
4397
4398 auto hs = nll(dsetName.Data(), *defaultOpts).hypoSpace(sXvar);
4399 hs.SetName(TUUID().AsString());
4400 if (nToys) {
4401 sWhat += " toys";
4402 if (nToys > 0) {
4403 sWhat += TString::Format("=%g", nToys);
4404 }
4405 }
4406 hs.SetTitle(sWhat + " scan" + ((dsetName != "") ? TString::Format(" [data=%s]", dsetName.Data()) : ""));
4407 int scanStatus = hs.scan(sWhat + " visualize", nBinsX, lowX, highX);
4408 if (scanStatus != 0) {
4409 new TGMsgBox(
4410 gClient->GetRoot(),
4411 (gROOT->GetListOfBrowsers()->At(0))
4412 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4413 : gClient->GetRoot(),
4414 "Scan Finished with Bad Status Code",
4415 TString::Format("%s\nData = %s\nScan Status Code = %d", hs.GetName(), dsetName.Data(), scanStatus),
4417 }
4418 if (ws()) {
4419 if (auto res = hs.result())
4420 ws()->import(*res);
4421 }
4422
4423 _pars.argList() = *snap; // restore pars
4424
4425 TBrowser *b = nullptr;
4426 for (auto a : *gROOT->GetListOfBrowsers()) {
4427 b = dynamic_cast<TBrowser *>(a);
4428 if (b && GetTreeItem(b)) {
4429 break;
4430 }
4431 }
4432 if (b) {
4433 auto p = GetTreeItem(b);
4434 while (p) {
4435 if (TString(p->GetText()).BeginsWith("RooWorkspace::")) {
4436 // found the workspace ... refresh this node, and if there's a scans node, refresh that
4437 if (auto bi = dynamic_cast<TRootBrowser *>(b->GetBrowserImp())) {
4438 if (auto fb = dynamic_cast<TGFileBrowser *>(bi->GetActBrowser())) {
4439 fb->DoubleClicked(p, 0);
4440 if (auto first = p->GetFirstChild()) {
4441 do {
4442 if (first->IsOpen() && TString(first->GetText()) == "scans") {
4443 fb->DoubleClicked(first, 0);
4444 }
4445 } while ((first = first->GetNextSibling()));
4446 }
4447 }
4448 }
4449 break;
4450 } else {
4451 p = p->GetParent();
4452 }
4453 }
4454 }
4455
4456 } catch (const std::exception &e) {
4457 new TGMsgBox(
4458 gClient->GetRoot(),
4459 (gROOT->GetListOfBrowsers()->At(0))
4460 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4461 : gClient->GetRoot(),
4462 "Exception", e.what(), kMBIconExclamation);
4463 }
4464}
4465
4466void xRooNode::_SetBinContent_(int bin, double value, const char *par, double parVal)
4467{
4468 try {
4469 SetBinContent(bin, value, strlen(par) > 0 ? par : nullptr, parVal);
4470 } catch (const std::exception &e) {
4471 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
4472 kMBIconExclamation); // deletes self on dismiss?
4473 }
4474}
4475
4477{
4478 try {
4479#if ROOT_VERSION_CODE > ROOT_VERSION(6, 29, 00)
4480 // if this is a collection of values, populate a TF1 and display as a dialog
4481 if (!get() && TString(GetName()).BeginsWith("!")) {
4482 browse();
4483 RooArgList args;
4484 for (auto a : *this) {
4485 if (auto arg = a->get<RooRealVar>())
4486 args.add(*arg);
4487 }
4488 TF1 f(GetName(), 0.0, 1.0, std::min(int(args.size()), 10));
4489 int i = 0;
4490 int j = 0;
4491 for (auto c : args) {
4492 j++;
4493 if (j < value) {
4494 continue;
4495 }
4496 auto v = dynamic_cast<RooRealVar *>(c);
4497 f.SetParName(i, c->GetName());
4498 if (v) {
4499 f.SetParLimits(i, v->getMin(), v->getMax());
4500 if (v->isConstant())
4501 f.FixParameter(i, v->getVal());
4502 else {
4503 f.SetParameter(i, v->getVal());
4504 f.SetParError(i, v->getError());
4505 }
4506 }
4507 i++;
4508 if (i == 10) {
4509 break; // max 10 pars shown
4510 }
4511 }
4512 int ret = 0;
4514 gClient->GetDefaultRoot(),
4515 (gROOT->GetListOfBrowsers()->At(0))
4516 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
4517 : gClient->GetDefaultRoot(),
4518 &f, nullptr, &ret);
4519 if (ret) {
4520 // user has changed parameter values etc, propagate back to parameters
4521 for (i = 0; i < f.GetNpar(); i++) {
4522 auto c = args.find(f.GetParName(i));
4523 auto v = dynamic_cast<RooRealVar *>(c);
4524 if (v) {
4525 v->setVal(f.GetParameter(i));
4526 double low, high;
4527 f.GetParLimits(i, low, high);
4528 if (low == high) {
4529 v->setConstant(low); // if low==high==0 then is not marked constant
4530 } else {
4531 v->setRange(low, high);
4532 }
4533 }
4534 }
4535 }
4536 return;
4537 }
4538#endif
4539
4540 if (!SetContent(value))
4541 throw std::runtime_error("Failed to SetContent");
4542 } catch (const std::exception &e) {
4543 new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),
4544 kMBIconExclamation); // deletes self on dismiss?
4545 }
4546}
4547
4548bool xRooNode::SetBinContent(int bin, double value, const char *par, double parVal)
4549{
4550
4551 // create if needed
4552 if (!get()) {
4553 if (fParent && !find(GetName())) {
4554 // if have a binning we create a histogram to match it
4555 if (auto ax = GetXaxis(); ax) {
4556 std::shared_ptr<TH1D> h;
4557 auto _b = dynamic_cast<Axis2 *>(ax)->binning();
4558 if (_b->isUniform()) {
4559 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
4560 h.reset(new TH1D(GetName(), GetTitle(), _b->numBins(), _b->lowBound(), _b->highBound()));
4561 } else {
4562 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
4563 h.reset(new TH1D(GetName(), GetTitle(), _b->numBins(), _b->array()));
4564 }
4565 h->SetOption("nostyle"); // don't transfer style when added
4566 h->GetXaxis()->SetName(TString::Format("%s;%s", ax->GetParent()->GetName(), ax->GetName()));
4567 fComp = h;
4568 }
4569 fComp = fParent->Add(*this, "sample").fComp;
4570 }
4571 }
4572
4573 // if it's a RooProduct locate child with the same name
4574 if (get<RooProduct>()) {
4575 return factors()[GetName()]->SetBinContent(bin, value, par, parVal);
4576 }
4577
4578 if (get<RooAbsData>()) {
4579 if (auto _data = get<RooDataSet>(); _data) {
4580 auto _ax = (bin) ? GetXaxis() : nullptr;
4581 if (!_ax && bin) {
4582 throw std::runtime_error("Cannot determine binning to fill data");
4583 }
4584 if (_ax && _ax->GetNbins() < bin) {
4585 throw std::out_of_range(TString::Format("%s range %s only has %d bins", _ax->GetParent()->GetName(),
4586 _ax->GetName(), _ax->GetNbins()));
4587 }
4588 RooArgSet obs;
4589
4590 TString cut = "";
4591
4592 for (auto _c : coords()) { // coords() moves vars to their respective coordinates too
4593 if (auto _cat = _c->get<RooAbsCategoryLValue>(); _cat) {
4594 if (cut != "")
4595 cut += " && ";
4596 cut += TString::Format("%s==%d", _cat->GetName(), _cat->getCurrentIndex());
4597 obs.add(*_cat); // note: if we ever changed coords to return clones, would need to keep coords alive
4598 } else if (auto _rv = _c->get<RooAbsRealLValue>(); _rv) {
4599 // todo: check coordRange is a single range rather than multirange
4600 if (cut != "")
4601 cut += " && ";
4602 cut +=
4603 TString::Format("%s>=%f&&%s<%f", _rv->GetName(), _rv->getMin(_rv->getStringAttribute("coordRange")),
4604 _rv->GetName(), _rv->getMax(_rv->getStringAttribute("coordRange")));
4605 obs.add(*_rv); // note: if we ever changed coords to return clones, would need to keep coords alive
4606 } else {
4607 throw std::runtime_error("SetBinContent of data: Unsupported coordinate type");
4608 }
4609 }
4610
4611 RooFormulaVar cutFormula("cut1", cut, obs); // doing this to avoid complaints about unused vars
4612 RooFormulaVar icutFormula("icut1", TString::Format("!(%s)", cut.Data()), obs);
4613
4614 TString cut2;
4615 if (_ax) {
4616 cut2 = TString::Format("%s >= %f && %s < %f", _ax->GetParent()->GetName(), _ax->GetBinLowEdge(bin),
4617 _ax->GetParent()->GetName(), _ax->GetBinUpEdge(bin));
4618 obs.add(*dynamic_cast<RooAbsArg *>(_ax->GetParent()));
4619 } else {
4620 cut2 = "1==1";
4621 }
4622 RooFormulaVar cutFormula2("cut2", cut + " && " + cut2, obs);
4623 RooFormulaVar icutFormula2("icut2", TString::Format("!(%s && %s)", cut.Data(), cut2.Data()), obs);
4624
4625 // // go up through parents looking for slice obs
4626 // auto _p = fParent;
4627 // while(_p) {
4628 // TString pName(_p->GetName());
4629 // if (auto pos = pName.Index('='); pos != -1) {
4630 // if(auto _obs = _p->getObject<RooAbsLValue>(pName(0,pos)); _obs) {
4631 // if(auto _cat = dynamic_cast<RooAbsCategoryLValue*>(_obs.get()); _cat) {
4632 // _cat->setLabel(pName(pos+1,pName.Length()));
4633 // cut += TString::Format("%s%s==%d", (cut=="")?"":" && ",_cat->GetName(),
4634 // _cat->getCurrentIndex());
4635 // } else if(auto _var = dynamic_cast<RooAbsRealLValue*>(_obs.get()); _var) {
4636 // _var->setVal(TString(pName(pos+1,pName.Length())).Atof());
4637 // // TODO: Cut for this!!
4638 // }
4639 // obs.add(*dynamic_cast<RooAbsArg*>(_obs.get()));
4640 // } else {
4641 // throw std::runtime_error("Unknown observable, could not find");
4642 // }
4643 // }
4644 // _p = _p->fParent;
4645 // }
4646
4647 // add observables to dataset if necessary
4648 RooArgSet l(obs);
4649 l.remove(*_data->get(), true, true);
4650 if (!l.empty()) {
4651 // addColumns method is buggy: https://github.com/root-project/root/issues/8787
4652 // incredibly though, addColumn works??
4653 for (auto &x : l) {
4654 _data->addColumn(*x);
4655 }
4656 // instead create a copy dataset and merge it into current
4657 // cant use merge because it drops weightVar
4658 /*RooDataSet tmp("tmp","tmp",l);
4659 for(int i=0;i<_data->numEntries();i++) tmp.add(l);
4660 _data->merge(&tmp);*/
4661 // delete _data->addColumns(l);
4662 }
4663 // before adding, ensure range is good to cover
4664 for (auto &o : obs) {
4665 if (auto v = dynamic_cast<RooRealVar *>(o); v) {
4666 if (auto dv = dynamic_cast<RooRealVar *>(_data->get()->find(v->GetName())); dv) {
4667 if (v->getMin() < dv->getMin())
4668 dv->setMin(v->getMin());
4669 if (v->getMax() > dv->getMax())
4670 dv->setMax(v->getMax());
4671 }
4672 } else if (auto c = dynamic_cast<RooCategory *>(o); c) {
4673 if (auto dc = dynamic_cast<RooCategory *>(_data->get()->find(c->GetName())); dc) {
4674 if (!dc->hasLabel(c->getCurrentLabel())) {
4675 dc->defineType(c->getCurrentLabel(), c->getCurrentIndex());
4676 }
4677 }
4678 }
4679 }
4680
4681 // using SetBinContent means dataset must take on a binned form at these coordinates
4682 // if number of entries doesnt match number of bins then will 'bin' the data
4683 if (bin) {
4684 if (auto _nentries = std::unique_ptr<RooAbsData>(_data->reduce(cutFormula))->numEntries();
4685 _nentries != _ax->GetNbins()) {
4686 auto _contents = GetBinContents(1, _ax->GetNbins());
4687
4688 if (_nentries > 0) {
4689 Info("SetBinContent", "Binning %s in channel: %s", GetName(), cut.Data());
4690 auto _reduced = std::unique_ptr<RooAbsData>(_data->reduce(icutFormula));
4691 _data->reset();
4692 for (int j = 0; j < _reduced->numEntries(); j++) {
4693 auto _obs = _reduced->get(j);
4694 _data->add(*_obs, _reduced->weight());
4695 }
4696 }
4697 for (int i = 1; i <= _ax->GetNbins(); i++) {
4698 // can skip over the bin we will be setting to save a reduce step below
4699 if (i == bin)
4700 continue;
4701 dynamic_cast<RooAbsLValue *>(_ax->GetParent())->setBin(i - 1, _ax->GetName());
4702 _data->add(obs, _contents.at(i - 1));
4703 }
4704 }
4705 }
4706 // remove existing entries
4707 if (std::unique_ptr<RooAbsData>(_data->reduce(cutFormula2))->numEntries() > 0) {
4708 auto _reduced = std::unique_ptr<RooAbsData>(_data->reduce(icutFormula2));
4709 _data->reset();
4710 for (int j = 0; j < _reduced->numEntries(); j++) {
4711 auto _obs = _reduced->get(j);
4712 _data->add(*_obs, _reduced->weight());
4713 }
4714 }
4715 if (_ax)
4716 dynamic_cast<RooAbsLValue *>(_ax->GetParent())->setBin(bin - 1, _ax->GetName());
4717 _data->add(obs, value);
4718 if (auto bb = getBrowsable(".sourceds"))
4719 return bb->SetBinContent(bin, value, par, parVal); // apply to source ds if we have one
4720 return true;
4721
4722 } else if (get<RooDataHist>()) {
4723 throw std::runtime_error("RooDataHist not supported yet");
4724 }
4725 }
4726
4727 if (auto _varies = variations(); !_varies.empty() || (par && strlen(par))) {
4728 if (!par || strlen(par) == 0) {
4729 return _varies["nominal"]->SetBinContent(bin, value, par, parVal);
4730 } else if (auto it = _varies.find(Form("%s=%g", par, parVal)); it) {
4731 return it->SetBinContent(bin, value);
4732 } else {
4733 // need to create the variation : note - if no variations existed up to now this will inject a new node
4734 // so we should redirect ourself to the new node
4735 // TODO: Do we need to redirect parents?
4736 TString s = Form("%s=%g", par, parVal);
4737 return Vary(s.Data()).SetBinContent(bin, value);
4738 }
4739 }
4740
4741 auto o = get();
4742 if (auto p = dynamic_cast<RooRealVar *>(o); p) {
4743 if (!par || strlen(par) == 0) {
4744 if (p->getMax() < value)
4745 p->setMax(value);
4746 if (p->getMin() > value)
4747 p->setMin(value);
4748 p->setVal(value);
4749 sterilize();
4750 return true;
4751 }
4752
4753 } else if (auto c = dynamic_cast<RooConstVar *>(o); c) {
4754
4755 // if parent is a FlexibleInterpVar, change the value in that .
4756 if (strcmp(c->GetName(), Form("%g", c->getVal())) == 0) {
4757 c->SetNameTitle(Form("%g", value), Form("%g", value));
4758 }
4759#if ROOT_VERSION_CODE < ROOT_VERSION(6, 24, 00)
4760 c->_value = value; // in future ROOT versions there is a changeVal method!
4761#else
4762 c->changeVal(value);
4763#endif
4764
4766 fParent->Vary(*this);
4767 }
4768
4769 sterilize();
4770 return true;
4771 } else if (auto f = dynamic_cast<RooHistFunc *>(o); f) {
4772 auto bin_pars = f->dataHist().get(bin - 1);
4773 if (f->getAttribute("density")) {
4774 value /= f->dataHist().binVolume(*bin_pars);
4775 }
4776 f->dataHist().set(*bin_pars, value);
4777 f->setValueDirty();
4778
4779 if (auto otherfName = f->getStringAttribute("symmetrized_by"); otherfName) {
4780 // broken symmetry, so update flags ...
4781 f->setStringAttribute("symmetrized_by", nullptr);
4782 if (auto x = getObject<RooAbsArg>(otherfName); x) {
4783 x->setStringAttribute("symmetrizes", nullptr);
4784 x->setStringAttribute("symmetrize_nominal", nullptr);
4785 }
4786 } else if (auto otherfName2 = f->getStringAttribute("symmetrizes"); otherfName2) {
4787 auto nomf = getObject<RooHistFunc>(f->getStringAttribute("symmetrize_nominal"));
4789 if (nomf && otherf) {
4790 otherf->dataHist().set(*bin_pars, 2 * nomf->dataHist().weight(bin - 1) - value);
4791 otherf->setValueDirty();
4792 }
4793 }
4794 sterilize();
4795 return true;
4796 } else if (auto f2 = dynamic_cast<RooStats::HistFactory::FlexibleInterpVar *>(o); f2) {
4797 // changing nominal value
4798 f2->setNominal(value);
4799 }
4800 throw std::runtime_error(TString::Format("unable to set bin content of %s", GetPath().c_str()));
4801}
4802
4803bool xRooNode::SetBinData(int bin, double value, const xRooNode &data)
4804{
4805 if (data.get<RooAbsData>()) {
4806 // attach as a child before calling datasets(), so that is included in the list
4807 push_back(std::make_shared<xRooNode>(data));
4808 }
4809 auto node = datasets()[data.GetName()];
4810 if (data.get<RooAbsData>()) {
4811 // remove the child we attached
4812 resize(size() - 1);
4813 }
4814 return node->SetBinContent(bin, value);
4815}
4816
4817bool xRooNode::SetData(const TObject &obj, const xRooNode &data)
4818{
4819 if (data.get<RooAbsData>()) {
4820 // attach as a child before calling datasets(), so that is included in the list
4821 push_back(std::make_shared<xRooNode>(data));
4822 }
4823 auto node = datasets()[data.GetName()];
4824 if (data.get<RooAbsData>()) {
4825 // remove the child we attached
4826 resize(size() - 1);
4827 }
4828 return node->SetContents(obj);
4829}
4830
4831bool xRooNode::SetBinError(int bin, double value)
4832{
4833
4834 // if it's a RooProduct locate child with the same name
4835 if (get<RooProduct>()) {
4836 return factors()[GetName()]->SetBinError(bin, value);
4837 }
4838
4839 if (auto _varies = variations(); !_varies.empty()) {
4840 return _varies["nominal"]->SetBinError(bin, value);
4841 }
4842
4843 auto o = get();
4844
4845 if (auto f = dynamic_cast<RooHistFunc *>(o); f) {
4846
4847 // if (f->getAttribute("density")) { value /= f->dataHist().binVolume(*bin_pars); } - commented out because DON'T
4848 // convert .. sumw and sumw2 attributes will be stored not as densities
4849
4850 // NOTE: Can only do this because factors() makes parents of its children it's own parent (it isn't the parent)
4851 // If ever make factors etc part of the parentage then this would need tweaking to get to the true parent
4852 // find first parent that is a RooProduct, that is where the statFactor would live
4853 // stop as soon as we reach pdf object
4854 auto _prodParent = fParent;
4855 while (_prodParent && !_prodParent->get<RooProduct>() && !_prodParent->get<RooAbsPdf>()) {
4856 if (_prodParent->get<PiecewiseInterpolation>() && strcmp(GetName(), "nominal")) {
4857 _prodParent.reset();
4858 break; // only the 'nominal' variation can look for a statFactor outside the variation container
4859 }
4860 _prodParent = _prodParent->fParent;
4861 }
4862 auto _f_stat =
4863 (_prodParent && !_prodParent->get<RooAbsPdf>()) ? _prodParent->factors().find("statFactor") : nullptr;
4864 auto f_stat = (_f_stat) ? _f_stat->get<ParamHistFunc>() : nullptr;
4865 if (_f_stat && _f_stat->get() && !f_stat) {
4866 throw std::runtime_error("stat factor must be a paramhistfunc");
4867 }
4868
4869 // stat uncertainty lives in the "statFactor" factor, each sample has its own one,
4870 // but they can share parameters
4871 if (!f_stat) {
4872 if (value == 0)
4873 return true;
4875 for (auto &p : xRooNode("tmp", *f, std::shared_ptr<xRooNode>(nullptr)).vars()) {
4876 if (parNames != "")
4877 parNames += ",";
4878 parNames += p->get()->GetName();
4879 }
4880 auto h = std::unique_ptr<TH1>(f->dataHist().createHistogram(parNames
4881#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 27, 00)
4882 ,
4884#endif
4885 ));
4886 h->Reset();
4887 h->SetName("statFactor");
4888 h->SetTitle(TString::Format("StatFactor of %s", f->GetTitle()));
4889 h->SetOption("blankshape");
4890
4891 // multiply parent if is nominal
4892 auto toMultiply = this;
4893 if (strcmp(GetName(), "nominal") == 0 && fParent && fParent->get<PiecewiseInterpolation>())
4894 toMultiply = fParent.get();
4895
4896 f_stat = dynamic_cast<ParamHistFunc *>(toMultiply->Multiply(*h).get());
4897 if (!f_stat) {
4898 throw std::runtime_error("Failed creating stat shapeFactor");
4899 }
4900 }
4901
4902 auto phf = f_stat;
4903
4904 TString prefix = f->getStringAttribute("statPrefix");
4905 if (value && prefix == "") {
4906 // find the first parent that can hold components (RooAddPdf, RooRealSumPdf, RooAddition, RooWorkspace) ... use
4907 // that name for the stat factor
4908 auto _p = fParent;
4909 while (_p && !(_p->get()->InheritsFrom("RooRealSumPdf") || _p->get()->InheritsFrom("RooAddPdf") ||
4910 _p->get()->InheritsFrom("RooWorkspace") || _p->get()->InheritsFrom("RooAddition"))) {
4911 _p = _p->fParent;
4912 }
4913 prefix = TString::Format("stat_%s", (_p && _p->get<RooAbsReal>()) ? _p->get()->GetName() : f->GetName());
4914 }
4915 auto newVar = (value == 0) ? getObject<RooRealVar>("1")
4916 : acquire<RooRealVar>(Form("%s_bin%d", prefix.Data(), bin),
4917 Form("#gamma^{%s}_{%d}", prefix.Data(), bin), 1);
4918#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
4919 RooArgList &pSet = phf->_paramSet;
4920#else
4921 RooArgList &pSet = const_cast<RooArgList &>(phf->paramList());
4922#endif
4923 auto var = dynamic_cast<RooRealVar *>(&pSet[bin - 1]);
4924
4925 if (newVar.get() != var) {
4926 // need to swap out var for newVar
4927 // replace ith element in list with new func, or inject into RooProduct
4929 for (std::size_t i = 0; i < pSet.size(); i++) {
4930 if (int(i) != bin - 1) {
4931 all.add(*pSet.at(i));
4932 } else {
4933 all.add(*newVar);
4934 }
4935 }
4936 pSet.removeAll();
4937 pSet.add(all);
4938 }
4939
4940 xRooNode v((value == 0) ? *var : *newVar, *this);
4941 auto rrv = dynamic_cast<RooRealVar *>(v.get());
4942 if (strcmp(rrv->GetName(), "1") != 0) {
4943 TString origName = (f->getStringAttribute("origName")) ? f->getStringAttribute("origName") : GetName();
4944 rrv->setStringAttribute(Form("sumw2_%s", origName.Data()), TString::Format("%f", pow(value, 2)));
4945 auto bin_pars = f->dataHist().get(bin - 1);
4946 auto _binContent = f->dataHist().weight(bin-1);
4947 if (f->getAttribute("density")) {
4948 _binContent *= f->dataHist().binVolume(*bin_pars);
4949 }
4950 rrv->setStringAttribute(Form("sumw_%s", origName.Data()), TString::Format("%f", _binContent));
4951 double sumw2 = 0;
4952 double sumw = 0;
4953 for (auto &[s, sv] : rrv->stringAttributes()) {
4954 if (s.find("sumw_") == 0) {
4955 sumw += TString(sv).Atof();
4956 } else if (s.find("sumw2_") == 0) {
4957 sumw2 += TString(sv).Atof();
4958 }
4959 }
4960 if (sumw2 && sumw2 != std::numeric_limits<double>::infinity()) {
4961 double tau = pow(sumw, 2) / sumw2;
4962 rrv->setError((tau < 1e-15) ? 1e15 : (/*rrv->getVal()*/ 1. / sqrt(tau))); // not sure why was rrv->getVal()?
4963 rrv->setConstant(false);
4964 // parameter must be constrained
4965 auto _constr = v.constraints();
4966 // std::cout << " setting constraint " << v.GetName() << " nomin=" << tau << std::endl;
4967 if (_constr.empty()) {
4968 rrv->setStringAttribute("boundConstraint", _constr.Add("poisson").get()->GetName());
4969 } else {
4970 auto _glob = _constr.at(0)->obs().at(0)->get<RooRealVar>();
4971 // TODO: Update any globs snapshots that are designed to match the nominal
4972 _glob->setStringAttribute("nominal", TString::Format("%f", tau));
4973 double _min = tau * (1. - 5. * sqrt(1. / tau));
4974 double _max = tau * (1. + 5. * sqrt(1. / tau));
4975 _glob->setRange(_min, _max);
4976 _glob->setVal(tau);
4977 _constr.at(0)->pp().at(0)->SetBinContent(0, tau);
4978 rrv->setStringAttribute("boundConstraint", _constr.at(0)->get()->GetName());
4979 }
4980 rrv->setRange(std::max((1. - 5. * sqrt(1. / tau)), 1e-15), 1. + 5. * sqrt(1. / tau));
4981 } else {
4982 // remove constraint
4983 if (auto _constr = v.constraints(); !_constr.empty()) {
4984 v.constraints().Remove(*_constr.at(0));
4985 }
4986 // set const if sumw2 is 0 (i.e. no error)
4987 rrv->setVal(1);
4988 rrv->setError(0);
4989 rrv->setConstant(sumw2 == 0);
4990 }
4991 }
4992
4993 return true;
4994 }
4995
4996 throw std::runtime_error(TString::Format("%s SetBinError failed", GetName()));
4997}
4998
4999std::shared_ptr<xRooNode> xRooNode::at(const std::string &name, bool browseResult) const
5000{
5001 auto res = find(name, browseResult);
5002 if (res == nullptr)
5003 throw std::out_of_range(name + " does not exist");
5004 return res;
5005}
5006
5007////////////////////////////////////////////////////////////////////////////////
5008/// The RooWorkspace this node belong to, if any
5009
5011{
5012 if (auto _w = get<RooWorkspace>(); _w)
5013 return _w;
5014 if (auto a = get<RooAbsArg>(); a && GETWS(a)) {
5015 return GETWS(a);
5016 }
5017 if (fParent)
5018 return fParent->ws();
5019 return nullptr;
5020}
5021
5023{
5024
5025 xRooNode out(".constraints", nullptr, *this);
5026
5027 std::function<RooAbsPdf *(const xRooNode &n, RooAbsArg &par, std::set<RooAbsPdf *> ignore)> getConstraint;
5028 getConstraint = [&](const xRooNode &n, RooAbsArg &par, std::set<RooAbsPdf *> ignore) {
5029 if (auto _pdf = n.get<RooAbsPdf>()) {
5030 if (ignore.count(_pdf))
5031 return (RooAbsPdf *)nullptr;
5032 ignore.insert(_pdf);
5033 }
5034 auto o = n.get<RooProdPdf>();
5035 if (!o) {
5036 if (n.get<RooSimultaneous>()) {
5037 // check all channels for a constraint if is simultaneous
5038 for (auto &c : n.bins()) {
5039 if (auto oo = getConstraint(*c, par, ignore); oo) {
5040 return oo;
5041 }
5042 }
5043 return (RooAbsPdf *)nullptr;
5044 } else if (n.get<RooAbsPdf>() && n.fParent && n.fParent->get<RooWorkspace>()) {
5045 // reached top-level pdf, which wasn't a simultaneous, so stop here
5046 return (RooAbsPdf *)nullptr;
5047 } else if (auto _ws = n.get<RooWorkspace>(); _ws) {
5048 // reached a workspace, check for any pdf depending on parameter that isnt the ignore
5049 for (auto p : _ws->allPdfs()) {
5050 if (ignore.count(static_cast<RooAbsPdf *>(p)))
5051 continue;
5052 if (p->dependsOn(par)) {
5053 out.emplace_back(std::make_shared<xRooNode>(par.GetName(), *p, *this));
5054 }
5055 }
5056 }
5057 if (!n.fParent)
5058 return (RooAbsPdf *)nullptr;
5059 return getConstraint(*n.fParent, par, ignore);
5060 }
5061 for (auto p : o->pdfList()) {
5062 if (ignore.count(static_cast<RooAbsPdf *>(p)))
5063 continue;
5064 if (p->dependsOn(par)) {
5065 out.emplace_back(std::make_shared<xRooNode>(par.GetName(), *p, *this));
5066 }
5067 }
5068 return (RooAbsPdf *)nullptr;
5069 };
5070
5071 for (auto &p : vars()) {
5072 auto v = dynamic_cast<RooAbsReal *>(p->get());
5073 if (!v)
5074 continue;
5075 if (v->getAttribute("Constant") && v != get<RooAbsReal>())
5076 continue; // skip constants unless we are getting the constraints of a parameter itself
5077 if (v->getAttribute("obs"))
5078 continue; // skip observables ... constraints constrain pars not obs
5079 getConstraint(*this, *v, {get<RooAbsPdf>()});
5080 /*if (auto c = ; c) {
5081 out.emplace_back(std::make_shared<Node2>(p->GetName(), *c, *this));
5082 }*/
5083 }
5084
5085 // finish by removing any constraint that contains another constraint for the same par
5086 // and consolidate common pars
5087 auto it = out.std::vector<std::shared_ptr<xRooNode>>::begin();
5088 while (it != out.std::vector<std::shared_ptr<xRooNode>>::end()) {
5089 bool removeIt = false;
5090 for (auto &c : out) {
5091 if (c.get() == it->get())
5092 continue;
5093 if ((*it)->get<RooAbsArg>()->dependsOn(*c->get<RooAbsArg>())) {
5094 removeIt = true;
5095 std::set<std::string> parNames;
5096 std::string _cName = c->GetName();
5097 do {
5098 parNames.insert(_cName.substr(0, _cName.find(';')));
5099 _cName = _cName.substr(_cName.find(';') + 1);
5100 } while (_cName.find(';') != std::string::npos);
5101 parNames.insert(_cName);
5102 _cName = it->get()->GetName();
5103 do {
5104 parNames.insert(_cName.substr(0, _cName.find(';')));
5105 _cName = _cName.substr(_cName.find(';') + 1);
5106 } while (_cName.find(';') != std::string::npos);
5107 parNames.insert(_cName);
5108 _cName = "";
5109 for (auto &x : parNames) {
5110 if (!_cName.empty())
5111 _cName += ";";
5112 _cName += x;
5113 }
5114 c->TNamed::SetName(_cName.c_str());
5115 break;
5116 }
5117 }
5118 if (removeIt) {
5119 it = out.erase(it);
5120 } else {
5121 ++it;
5122 }
5123 }
5124
5125 // if getting constraints of a fundamental then use the constraint names instead of the par name (because would be
5126 // all same otherwise)
5127 if (get<RooAbsArg>() && get<RooAbsArg>()->isFundamental()) {
5128 for (auto &o : out) {
5129 o->TNamed::SetName(o->get()->GetName());
5130 }
5131 }
5132
5133 return out;
5134}
5135
5136std::shared_ptr<TObject> xRooNode::convertForAcquisition(xRooNode &acquirer, const char *opt) const
5137{
5138
5139 TString sOpt(opt);
5140 sOpt.ToLower();
5142 if (sOpt == "func")
5143 sName = TString("factory:") + sName;
5144
5145 // if arg is a histogram, will acquire it as a RooHistFunc unless no conversion
5146 // todo: could flag not to convert
5147 if (auto h = get<TH1>(); h) {
5148 TString sOpt2(h->GetOption());
5149 std::map<std::string, std::string> stringAttrs;
5150 while (sOpt2.Contains("=")) {
5151 auto pos = sOpt2.Index("=");
5152 auto start = sOpt2.Index(";") + 1;
5153 if (start > pos)
5154 start = 0;
5155 auto end = sOpt2.Index(";", pos);
5156 if (end == -1)
5157 end = sOpt2.Length();
5158 stringAttrs[sOpt2(start, pos - start)] = sOpt2(pos + 1, end - pos - 1);
5159 sOpt2 = TString(sOpt2(0, start)) + TString(sOpt2(end + 1, sOpt2.Length()));
5160 }
5163 if (origName.BeginsWith(';'))
5164 origName = origName(1, origName.Length());
5165 if (newObjName.BeginsWith(';')) {
5166 newObjName =
5167 newObjName(1, newObjName.Length()); // special case if starts with ';' then don't create a fancy name
5168 } else if (acquirer.get() && !acquirer.get<RooWorkspace>()) {
5170 "%s_%s", (acquirer.mainChild().get()) ? acquirer.mainChild()->GetName() : acquirer->GetName(),
5171 newObjName.Data());
5172 }
5173 // can convert to a RooHistFunc, or RooParamHist if option contains 'shape'
5174 TString varName = h->GetXaxis()->GetName();
5175 std::string binningName = newObjName.Data();
5176 if (auto pos = varName.Index(';'); pos != -1) {
5177 binningName = varName(pos + 1, varName.Length());
5178 varName = varName(0, pos);
5179 }
5180
5181 if (varName == "xaxis" &&
5182 !acquirer.get<RooSimultaneous>()) { // default case, try to take axis var and binning from the acquirer
5183 if (auto ax = acquirer.GetXaxis(); ax) {
5184 varName = ax->GetParent()->GetName();
5185 // TODO: check the binning is consistent before using - at least will check nBins below
5186 binningName = ax->GetName();
5187 } else if (acquirer.obs().size() == 1)
5188 varName = acquirer.obs().at(0)->get()->GetName(); // TODO what if no obs but Xaxis var is defined?
5189 }
5190 auto x = acquirer.acquire<RooRealVar>(varName, h->GetXaxis()->GetTitle(), h->GetXaxis()->GetXmin(),
5191 h->GetXaxis()->GetXmax());
5192 if (x->getMin() > h->GetXaxis()->GetXmin())
5193 x->setMin(h->GetXaxis()->GetXmin());
5194 if (x->getMax() < h->GetXaxis()->GetXmax())
5195 x->setMax(h->GetXaxis()->GetXmax());
5196 if (!x->hasBinning(binningName.c_str())) {
5197 if (h->GetXaxis()->IsVariableBinSize()) {
5198 x->setBinning(RooBinning(h->GetNbinsX(), h->GetXaxis()->GetXbins()->GetArray()), binningName.c_str());
5199 } else {
5200 x->setBinning(
5201 RooUniformBinning(h->GetXaxis()->GetXmin(), h->GetXaxis()->GetXmax(), h->GetXaxis()->GetNbins()),
5202 binningName.c_str());
5203 }
5204 x->getBinning(binningName.c_str()).SetTitle(h->GetXaxis()->GetTitle());
5205 if (x->getBinningNames().size() == 2) {
5206 // this was the first binning, so copy it over to be the default binning too
5207 x->setBinning(x->getBinning(binningName.c_str()));
5208 }
5209 } else {
5210 // TODO check binning is compatible with histogram
5211 if (x->getBinning(binningName.c_str()).numBins() != h->GetNbinsX()) {
5212 throw std::runtime_error(
5213 TString::Format("binning mismatch for binning %s of %s", binningName.c_str(), x->GetName()));
5214 }
5215 }
5216
5217 std::shared_ptr<RooAbsArg> _f;
5218
5219 // if acquirer is a RooSimultaneous, will use histogram to define a channel
5220 if (acquirer.get<RooSimultaneous>()) {
5221 _f = acquirer.acquireNew<RooProdPdf>(newObjName, (strlen(h->GetTitle())) ? h->GetTitle() : h->GetName(),
5222 RooArgList());
5223 for (auto &[k, v] : stringAttrs) {
5224 _f->setStringAttribute(k.c_str(), v.c_str());
5225 }
5226 x->setAttribute("obs", true);
5227 } else if (sOpt2.Contains("shape")) {
5228 RooArgList list;
5229 for (int i = 0; i < x->getBinning(binningName.c_str()).numBins(); i++) {
5230 std::shared_ptr<RooAbsArg> arg;
5231 if (sOpt2.Contains("blankshape")) {
5232 arg = acquirer.acquire2<RooAbsArg, RooRealVar>("1", "1", 1);
5233 } else {
5234 if (!h) {
5235 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1), "", 1);
5236 } else if (h->GetMinimumStored() != -1111 || h->GetMaximumStored() != -1111) {
5237 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1),
5238 TString::Format("%s_{%d}", h->GetTitle(), i + 1),
5239 h->GetBinContent(i + 1), h->GetMinimumStored(),
5240 h->GetMaximumStored());
5241 } else {
5242 arg = acquirer.acquireNew<RooRealVar>(TString::Format("%s_bin%d", newObjName.Data(), i + 1),
5243 TString::Format("%s_{%d}", h->GetTitle(), i + 1),
5244 h->GetBinContent(i + 1));
5245 }
5246 }
5247 list.add(*arg);
5248 }
5249 // paramhistfunc requires the binnings to be loaded as default at construction time
5250 // so load binning temporarily
5251 auto tmp = dynamic_cast<RooAbsBinning *>(x->getBinningPtr(nullptr)->Clone());
5252 x->setBinning(x->getBinning(binningName.c_str()));
5253 _f = acquirer.acquireNew<ParamHistFunc>(newObjName, h->GetTitle(), *x, list);
5254#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
5255 dynamic_cast<ParamHistFunc *>(_f.get())->_paramSet.setName("paramSet"); // so can see when print
5256#else
5257 const_cast<RooArgList &>(dynamic_cast<ParamHistFunc *>(_f.get())->paramList())
5258 .setName("paramSet"); // so can see when print
5259#endif
5260 x->setBinning(*tmp); // restore binning
5261 delete tmp;
5262 for (auto &[k, v] : stringAttrs) {
5263 _f->setStringAttribute(k.c_str(), v.c_str());
5264 }
5265 } else {
5266 auto dh = acquirer.acquireNew<RooDataHist>(Form("hist_%s", newObjName.Data()), h->GetTitle(), *x,
5267 binningName.c_str() /* binning name*/);
5268 if (!dh) {
5269 throw std::runtime_error("Couldn't make data hist");
5270 }
5271 auto f = acquirer.acquireNew<RooHistFunc>(newObjName, h->GetTitle(), *x, *dh,
5272 0 /*interpolation order between bins*/);
5273 f->forceNumInt();
5274 f->setAttribute("autodensity"); // where it gets inserted will determine if this is a density or not
5275 _f = f;
5276
5277 for (auto &[k, v] : stringAttrs) {
5278 _f->setStringAttribute(k.c_str(), v.c_str());
5279 }
5280
5281 // need to do these settings here because used in the assignment step
5282 _f->setStringAttribute("xvar", x->GetName());
5283 _f->setStringAttribute("binning", binningName.c_str());
5284 if (strcmp(_f->GetName(), origName.Data()) && !_f->getStringAttribute("alias"))
5285 _f->setStringAttribute("alias", origName);
5286
5287 // copy values over using the assignment operator - may convert to a RooProduct if there are stat uncerts
5288 xRooNode tmp(h->GetName(), _f, acquirer);
5289 tmp = *h;
5290 _f = std::dynamic_pointer_cast<RooAbsArg>(tmp.fComp); // in case got upgrade to a RooProduct
5291 }
5292
5293 _f->setStringAttribute("xvar", x->GetName());
5294 _f->setStringAttribute("binning", binningName.c_str());
5295 // style(h); // will transfer styling to object if necessary - not doing because this method used with plane hists
5296 // frequently
5297 if (strcmp(_f->GetName(), origName.Data()) && !_f->getStringAttribute("alias"))
5298 _f->setStringAttribute("alias", origName);
5299
5300 fComp = _f;
5301 return _f;
5302 } else if (!get() && (sName.BeginsWith("factory:")||sName.Contains("::")) && acquirer.ws()) {
5303 TString s(sName);
5304 if(sName.BeginsWith("factory:")) s = TString(s(8, s.Length()));
5305 fComp.reset(acquirer.ws()->factory(s), [](TObject *) {});
5306 if (fComp) {
5307 const_cast<xRooNode *>(this)->TNamed::SetName(fComp->GetName());
5308 }
5309 return fComp;
5310 }
5311
5312 return fComp;
5313}
5314
5315std::shared_ptr<TStyle> xRooNode::style(TObject *initObject, bool autoCreate) const
5316{
5317 return std::dynamic_pointer_cast<TStyle>(styles(initObject, autoCreate).fComp);
5318}
5319
5321{
5322 TString t = GetTitle();
5323
5324 auto arg = get<RooAbsArg>();
5325 if (!initObject && !arg && !gROOT->GetStyle(t)) {
5326 return nullptr;
5327 }
5328
5329 std::unique_ptr<TObject> argInitObject;
5330
5331 if (initObject) {
5332 t = (strlen(initObject->GetTitle())) ? initObject->GetTitle() : initObject->GetName();
5333 } else if (arg) {
5334 if (arg->getStringAttribute("style")) {
5335 t = arg->getStringAttribute("style");
5336 } else if (autoCreate) {
5337 // args will default to histo's object styling, whatever that currently may be
5338 argInitObject = std::make_unique<TH1D>(GetName(), GetTitle(), 1, 0, 1);
5339 initObject = argInitObject.get();
5340 } else {
5341 return nullptr;
5342 }
5343 }
5344
5345 std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case getObject has decided to
5346 // return the owning ptr (for some reason)
5347 if (!gROOT->GetStyle(t)) {
5348 if ((style = getObject<TStyle>(t.Data()))) {
5349 // loaded style (from workspace?) so put in list and use that
5350 gROOT->GetListOfStyles()->Add(style.get());
5351 } else {
5352 if (!autoCreate)
5353 return nullptr;
5354 // create new style - gets put in style list automatically so don't have to delete
5355 // acquire them so saved to workspaces for auto reload ...
5356 style = const_cast<xRooNode &>(*this).acquireNew<TStyle>(t.Data(),
5357 TString::Format("Style for %s component", t.Data()));
5358 if (auto x = dynamic_cast<TAttLine *>(initObject))
5359 ((TAttLine &)*style) = *x;
5360 if (auto x = dynamic_cast<TAttFill *>(initObject))
5361 ((TAttFill &)*style) = *x;
5362 if (auto x = dynamic_cast<TAttMarker *>(initObject))
5363 ((TAttMarker &)*style) = *x;
5364 gROOT->GetListOfStyles()->Add(style.get());
5365 }
5366 } else {
5367 style = std::shared_ptr<TStyle>(gROOT->GetStyle(t), [](TStyle *) {});
5368 }
5369
5370 if (arg && !arg->getStringAttribute("style")) {
5371 arg->setStringAttribute("style", style->GetName());
5372 }
5373
5374 return xRooNode(style, *this);
5375}
5376
5377std::shared_ptr<TObject> xRooNode::acquire(const std::shared_ptr<TObject> &arg, bool checkFactory, bool mustBeNew)
5378{
5379 if (!arg)
5380 return nullptr;
5381 if (!fAcquirer && !get<RooWorkspace>() && fParent)
5382 return fParent->acquire(arg, checkFactory, mustBeNew);
5383
5384 // if has a workspace and our object is the workspace or is in the workspace then add this object to workspace
5385 auto _ws = (fAcquirer) ? nullptr : ws();
5386 if (_ws && (get() == _ws || _ws->arg(GetName()) || (arg && strcmp(arg->GetName(), GetName()) == 0))) {
5388 RooMsgService::instance().setGlobalKillBelow(RooFit::WARNING);
5389 if (auto a = dynamic_cast<RooAbsArg *>(arg.get()); a) {
5390 auto out_arg = _ws->arg(a->GetName());
5391 TString aName = arg->GetName();
5392 int ii = 1;
5393 while (out_arg && mustBeNew) {
5394 a->SetName(TString::Format("%s_%d", aName.Data(), ii++));
5395 out_arg = _ws->arg(a->GetName());
5396 }
5397 if (aName != a->GetName())
5398 Warning("acquire", "Renaming to %s", a->GetName());
5399 if (!out_arg) {
5400 bool done = false;
5401 if (checkFactory) {
5402 if (auto res = _ws->factory(arg->GetName()); res) {
5403 a = res;
5404 done = true;
5405 }
5406 }
5407 if (!done && _ws->import(*a, RooFit::RecycleConflictNodes())) {
5408 if (GETWS(a) != _ws) {
5409 Info("acquire", "A copy of %s has been added to workspace %s", a->GetName(), _ws->GetName());
5410 }
5411 RooMsgService::instance().setGlobalKillBelow(msglevel);
5412 return nullptr;
5413 }
5414 // sanitizeWS(); // clears the caches that might exist up to now, as well interfere with getParameters calls
5415 std::set<std::string> setNames;
5416 for (auto &aa : GETWSSETS(_ws)) {
5417 if (TString(aa.first.c_str()).BeginsWith("CACHE_")) {
5418 setNames.insert(aa.first);
5419 }
5420 }
5421 for (auto &aa : setNames)
5422 ws()->removeSet(aa.c_str());
5423 out_arg = _ws->arg(a->GetName());
5424 if (GETWS(out_arg) != _ws) { // seems that when objects imported their ws isn't set
5425 out_arg->setWorkspace(*_ws);
5426 }
5427 // if any of the leaf nodes of the imported object have "global" label on them, ensure propagate to
5428 // "globalObservables" list if ws has one
5429 if (auto globs = const_cast<RooArgSet *>(ws()->set("globalObservables")); globs) {
5431 out_arg->leafNodeServerList(&leafs);
5432 std::unique_ptr<RooAbsCollection> globals(leafs.selectByAttrib("global", true));
5433 for (auto &aa : *globals) {
5434 if (!globs->contains(*aa)) {
5435 globs->add(*aa);
5436 }
5437 }
5438 }
5439 }
5440 RooMsgService::instance().setGlobalKillBelow(msglevel);
5441 return std::shared_ptr<TObject>(out_arg, [](TObject *) {});
5442 } else if (auto a2 = dynamic_cast<RooAbsData *>(arg.get()); a2) {
5443 if (_ws->import(*a2, RooFit::Embedded())) {
5444 RooMsgService::instance().setGlobalKillBelow(msglevel);
5445 return nullptr;
5446 }
5447 RooMsgService::instance().setGlobalKillBelow(msglevel);
5448 return std::shared_ptr<TObject>(_ws->embeddedData(arg->GetName()), [](TObject *) {});
5449 } else if (arg->InheritsFrom("TNamed")) { // can add any TNamed to a workspace
5450 TObject *out_arg = nullptr;
5451 if (auto fr = dynamic_cast<RooFitResult *>(&*arg); fr && fr->numStatusHistory() == 0) {
5452 // fit results without a status history are treated as snapshots
5453 out_arg = fr->Clone();
5454 const_cast<RooLinkedList &>(GETWSSNAPSHOTS(_ws)).Add(out_arg);
5455 } else {
5456 // ensure will have a unique name for import if must be new
5457 TNamed *aNamed = dynamic_cast<TNamed *>(arg.get());
5458 TString aName = arg->GetName();
5459 out_arg = _ws->genobj(arg->GetName());
5460 int ii = 1;
5461 while (aNamed && out_arg && mustBeNew) {
5462 aNamed->SetName(TString::Format("%s;%d", aName.Data(), ii++));
5463 out_arg = _ws->genobj(aNamed->GetName());
5464 }
5465 if (!out_arg) {
5466 if (aName != arg->GetName()) {
5467 Warning("acquire", "Renaming to %s", arg->GetName());
5468 }
5469 if (_ws->import(*arg, false /*replace existing*/)) {
5470 RooMsgService::instance().setGlobalKillBelow(msglevel);
5471 return nullptr;
5472 }
5473 out_arg = _ws->genobj(arg->GetName());
5474 }
5475 }
5476 RooMsgService::instance().setGlobalKillBelow(msglevel);
5477
5478 return std::shared_ptr<TObject>(out_arg, [](TObject *) {});
5479 }
5480 RooMsgService::instance().setGlobalKillBelow(msglevel);
5481 // Warning("acquire","Not implemented acquisition of object %s",arg->GetName());
5482 // return nullptr;
5483 }
5484 if (!mustBeNew && fProvider) {
5485 auto out = fProvider->getObject(arg->GetName(), arg->ClassName());
5486 if (out)
5487 return out;
5488 }
5489 auto _owned = find(".memory");
5490 if (!_owned) {
5491 _owned = emplace_back(std::make_shared<xRooNode>(".memory", nullptr, *this));
5492 }
5493 // look for exact name, dont use 'find' because doesnt work if trying to find "1" and it doesn't exist, will get back
5494 // idx 1 instead
5495 if (!mustBeNew) {
5496 for (auto &r : *_owned) {
5497 if (strcmp(r->GetName(), arg->GetName()) == 0 && strcmp(r->get()->ClassName(), arg->ClassName()) == 0) {
5498 return r->fComp;
5499 }
5500 }
5501 }
5502 if (!fProvider)
5503 std::cout << GetName() << " taking over " << arg->ClassName() << "::" << arg->GetName() << std::endl;
5504 /*emplace_back(std::make_shared<Node2>(".memory",nullptr,*this))*/
5505 return _owned->emplace_back(std::make_shared<xRooNode>(arg->GetName(), arg, *this))->fComp;
5506 // return arg;
5507}
5508
5509bool xRooNode::SetXaxis(const char *name, const char *title, int nbins, double low, double high)
5510{
5511 RooUniformBinning b(low, high, nbins, name);
5512 b.SetTitle(title);
5513 return SetXaxis(b);
5514}
5515
5516bool xRooNode::SetXaxis(const char *name, const char *title, int nbins, const double *bins)
5517{
5518 RooBinning b(nbins, bins, name);
5519 b.SetTitle(title);
5520 return SetXaxis(b);
5521}
5522
5524{
5525
5526 auto name = binning.GetName();
5527 double high = binning.highBound();
5528 double low = binning.lowBound();
5529 // int nbins = binning.numBins();
5530 auto title = binning.GetTitle();
5531
5532 // if have any dependents and name isn't one of them then stop
5533 auto _deps = vars();
5534 /*if(!_deps.empty() && !_deps.find(name)) {
5535 throw std::runtime_error(TString::Format("%s Does not depend on %s",GetName(),name));
5536 }*/
5537
5538 // object will need to exist
5539 if (!get()) {
5540 if (fParent && !find(GetName())) {
5541 fComp = fParent->Add(*this, "+").fComp;
5542 }
5543 }
5544
5545 auto a = get<RooAbsArg>();
5546 if (!a)
5547 throw std::runtime_error("Cannot SetXaxis of non-arg");
5548
5549 auto _x = acquire<RooRealVar>(name, title, low, high);
5550 _x->setBinning(binning, a->GetName());
5551 _x->getBinning(a->GetName()).SetTitle(title);
5552 if (_x->getBinningNames().size() == 2) {
5553 // this was the first binning, so copy it over to be the default binning too
5554 _x->setBinning(_x->getBinning(a->GetName()));
5555 } else {
5556 // ensure the default binning is wide enough to cover this range
5557 // the alternative to this would be to ensure setNormRange of all pdfs
5558 // are set to correct range (then default can be narrower than some of the named binnings)
5559 if (_x->getMax() < high)
5560 _x->setMax(high);
5561 if (_x->getMin() > low)
5562 _x->setMin(low);
5563 }
5564
5565 if (!_deps.find(name) && get<RooAbsPdf>()) {
5566 // creating a variable for a pdf we will assume it should be an observable
5567 _x->setAttribute("obs");
5568 }
5569
5570 a->setStringAttribute("xvar", _x->GetName());
5571 a->setStringAttribute("binning", a->GetName());
5572 fXAxis.reset(); // remove any existing xaxis
5573
5574 return true;
5575}
5576
5578{
5579 if (!ax)
5580 return false;
5581 if (ax->IsVariableBinSize()) {
5582 return SetXaxis(ax->GetName(), ax->GetTitle(), ax->GetNbins(), ax->GetXbins()->GetArray());
5583 } else {
5584 return SetXaxis(ax->GetName(), ax->GetTitle(), ax->GetNbins(), ax->GetXmin(), ax->GetXmax());
5585 }
5586}
5587
5588bool xRooNode::contains(const std::string &name) const
5589{
5590 return find(name, false) != nullptr;
5591}
5592
5593std::shared_ptr<xRooNode> xRooNode::find(const std::string &name, bool browseResult) const
5594{
5595 std::string partname = (name.find('/') != std::string::npos) ? name.substr(0, name.find('/')) : name;
5596 auto _s = (!get() && fParent) ? fParent->get<RooSimultaneous>()
5597 : get<RooSimultaneous>(); // makes work if doing simPdf.bins()["blah"]
5598 std::string extra = (_s) ? _s->indexCat().GetName() : "";
5599 for (auto &child : *this) {
5600 if (auto _obj = child->get(); name == child->GetName() || partname == child->GetName() ||
5601 (_obj && name == _obj->GetName()) || (_obj && partname == _obj->GetName()) ||
5602 (!extra.empty() && ((extra + "=" + name) == child->GetName() ||
5603 (extra + "=" + partname) == child->GetName()))) {
5604 if (browseResult)
5605 child->browse(); // needed so can go at()->at()->at()...
5606 if (partname != name && name != child->GetName()) {
5607 return child->at(name.substr(partname.length() + 1));
5608 }
5609 return child;
5610 }
5611 if (partname.find('.') != 0) { // do not allow mainChild browsing if trying to find a "." child ... as is done in
5612 // getObject for ".memory"
5613 if (auto x = mainChild(); x && strcmp(child->GetName(), x.GetName()) == 0) {
5614 // can browse directly into main children as if their children were our children
5615 for (auto &child2 : x.browse()) {
5616 if (auto _obj = child2->get(); name == child2->GetName() || partname == child2->GetName() ||
5617 (_obj && name == _obj->GetName()) ||
5618 (_obj && partname == _obj->GetName())) {
5619 if (browseResult)
5620 child2->browse(); // needed for onward read (or is it? there's a browse above too??)
5621 if (partname != name && name != child2->GetName()) {
5622 return child2->at(name.substr(partname.length() + 1));
5623 }
5624 return child2;
5625 }
5626 }
5627 }
5628 }
5629 }
5630 // before giving up see if partName is numeric and indexes within the range
5631 if (TString s(partname); s.IsDec() && size_t(s.Atoi()) < size()) {
5632 auto child2 = at(s.Atoi());
5633 if (partname != name) {
5634 return child2->at(name.substr(partname.length() + 1));
5635 }
5636 return child2;
5637 }
5638 // allow calling of find on a RooWorkspace to access getObject objects ...
5639 if (get<RooWorkspace>() && name != ".memory") {
5640 if (auto obj = getObject(name)) {
5641 auto out = std::make_shared<xRooNode>(obj, *this);
5642 if (browseResult)
5643 out->browse();
5644 return out;
5645 }
5646 }
5647 return nullptr;
5648}
5649
5650std::shared_ptr<xRooNode> xRooNode::operator[](const std::string &name)
5651{
5652 std::string partname = (name.find('/') != std::string::npos) ? name.substr(0, name.find('/')) : name;
5653 browse();
5654 auto _s = (!get() && fParent) ? fParent->get<RooSimultaneous>()
5655 : get<RooSimultaneous>(); // makes work if doing simPdf.bins()["blah"]
5656 std::string extra = (_s) ? _s->indexCat().GetName() : "";
5657 std::shared_ptr<xRooNode> folderNode;
5658 for (auto &child : *this) {
5659 if (name == child->GetName() || partname == child->GetName() ||
5660 (!extra.empty() &&
5661 ((extra + "=" + name) == child->GetName() || (extra + "=" + partname) == child->GetName()))) {
5662 child->browse(); // needed for onward read (or is it? there's a browse above too??)
5663 if (partname != name && name != child->GetName()) {
5664 return child->operator[](name.substr(partname.length() + 1));
5665 }
5666 return child;
5667 }
5668 if (auto x = mainChild(); strcmp(child->GetName(), x.GetName()) == 0) {
5669 // can browse directly into main children as if their children were our children
5670 for (auto &child2 : x.browse()) {
5671 if (name == child2->GetName() || partname == child2->GetName()) {
5672 child2->browse(); // needed for onward read (or is it? there's a browse above too??)
5673 if (partname != name && name != child2->GetName()) {
5674 return child2->operator[](name.substr(partname.length() + 1));
5675 }
5676 return child2;
5677 }
5678 }
5679 }
5680 if (child->fFolder == (std::string("!") + partname)) {
5681 if (!folderNode)
5682 folderNode = std::make_shared<xRooNode>(child->fFolder.c_str(), nullptr, *this);
5683 folderNode->push_back(child);
5684 }
5685 }
5686 if (folderNode) {
5687 if (partname != name) {
5688 return folderNode->operator[](name.substr(partname.length() + 1));
5689 }
5690 return folderNode;
5691 }
5692 // before giving up see if partName is numeric and indexes within the range
5693 if (TString s(partname); s.IsDec() && size_t(s.Atoi()) < size()) {
5694 auto child2 = at(s.Atoi());
5695 if (partname != name) {
5696 return child2->operator[](name.substr(partname.length() + 1));
5697 }
5698 return child2;
5699 }
5700 auto out = std::make_shared<xRooNode>(partname.c_str(), nullptr, *this); // not adding as child yeeet
5701 // special case, if creating a node in the workspace with a specific name, it's a folder node ...
5702 if (get<RooWorkspace>() && partname == "pdfs") {
5703 out->SetName("!pdfs");
5704 }
5705 if (partname != name) {
5706 return out->operator[](name.substr(partname.length() + 1));
5707 }
5708 return out;
5709}
5710
5712{
5713 if (!b) {
5714 for (auto o : *gROOT->GetListOfBrowsers()) {
5715 b = dynamic_cast<TBrowser *>(o);
5716 if (!b || !b->GetBrowserImp())
5717 continue;
5718 if (auto out = GetTreeItem(b); out)
5719 return out;
5720 }
5721 return nullptr;
5722 }
5723 if (!b->GetBrowserImp())
5724 return nullptr;
5725 if (auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp()))); _b) {
5726 auto _root = GETROOTDIR(_b);
5727 ;
5728 if (!_root)
5729 _root = GETLISTTREE(_b)->GetFirstItem();
5731 return GETLISTTREE(_b)->FindItemByObj(_root, const_cast<xRooNode *>(this));
5732 }
5733 return nullptr;
5734}
5735
5737{
5738 if (!b) {
5739 for (auto o : *gROOT->GetListOfBrowsers()) {
5740 b = dynamic_cast<TBrowser *>(o);
5741 if (!b || !b->GetBrowserImp())
5742 continue;
5743 if (auto out = GetListTree(b); out)
5744 return out;
5745 }
5746 return nullptr;
5747 }
5748 if (b->GetBrowserImp()) {
5749 if (auto _b = dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp())));
5750 _b) {
5751 auto _root = GETROOTDIR(_b);
5752 if (!_root)
5753 _root = GETLISTTREE(_b)->GetFirstItem();
5754 if (auto item = GETLISTTREE(_b)->FindItemByObj(_root, const_cast<xRooNode *>(this)); item) {
5755 return GETLISTTREE(_b);
5756 }
5757 }
5758 }
5759 return nullptr;
5760}
5761
5762void xRooNode::SetName(const char *name)
5763{
5765 if (auto a = get<RooAbsArg>(); a)
5766 a->setStringAttribute("alias", name);
5767 for (auto o : *gROOT->GetListOfBrowsers()) {
5768 if (auto b = dynamic_cast<TBrowser *>(o); b) {
5769 if (auto item = GetTreeItem(b); item) {
5770 item->SetText(name);
5771 }
5772 }
5773 }
5774}
5775
5776void xRooNode::SetTitle(const char *title)
5777{
5778 if (auto o = (get<TNamed>()); o) {
5779 if (auto c = mainChild(); c.get()) {
5780 c.SetTitle(title);
5781 }
5782 o->SetTitle(title);
5783 }
5784 TNamed::SetTitle(title);
5785}
5786
5788{
5789 if (get<RooArgList>() || (!get() && !(strlen(GetName()) > 0 && (GetName()[0] == '!')) && !fBrowseOperation))
5790 return *this; // nothing to browse - 'collection' nodes should already be populated except for folders
5791 // alternative could have been to mandate that the 'components' of a collection node are the children it has.
5792
5793 auto findByObj = [&](const std::shared_ptr<xRooNode> &n) {
5794 std::vector<std::shared_ptr<xRooNode>> &nn = *this;
5795 for (auto &c : nn) {
5796 if (c->get() == n->get() && strcmp(n->GetName(), c->GetName()) == 0)
5797 return c;
5798 }
5799 return std::shared_ptr<xRooNode>(nullptr);
5800 };
5801
5802 auto appendChildren = [&](const xRooNode &n) {
5803 size_t out = 0;
5804 const std::vector<std::shared_ptr<xRooNode>> &nn(n);
5805 for (auto &c : nn) {
5806 if (auto existing = findByObj(c); existing) {
5807 existing->fTimes++;
5808 existing->fFolder = c->fFolder; // transfer folder assignment
5809 } else {
5810 emplace_back(c);
5811 }
5812 if (!TString(c->GetName()).BeginsWith(".coef"))
5813 out++; // don't count .coef as a child, as technically part of parent
5814 }
5815 return out;
5816 };
5817
5818 const std::vector<std::shared_ptr<xRooNode>> &nn2(*this);
5819 for (auto &c : nn2) {
5820 if (strlen(c->GetName()) > 0 && (c->GetName()[0] == '.')) {
5821 c->fTimes = 1;
5822 continue;
5823 } // never auto-cleanup property children
5824 if (strcmp(c->GetName(), "!.pars") == 0) {
5825 c->fTimes = 1;
5826 continue;
5827 } // special collection, also not cleaned up
5828 if (c->get<RooWorkspace>() || c->get<TFile>()) {
5829 c->fTimes = 1;
5830 continue;
5831 } // workspaces and files not cleaned up: TODO have a nocleanup flag instead
5832 c->fTimes = 0;
5833 }
5834
5835 size_t addedChildren = 0;
5836 if (fBrowseOperation) {
5838 } else {
5839 if (get<RooWorkspace>()) {
5841 }
5842
5843 // if (get<RooAbsPdf>() && ((fParent && fParent->get<RooWorkspace>()) || !fParent)) {
5844 // // top-level pdfs will also list the ".vars" property for -- should make this updateable
5845 // //if (auto x = find("!.vars"); !x) { // this is slower because it triggers a browse of !.vars
5846 // if(!contains("!.vars")) {
5847 // emplace_back(std::make_shared<Node2>("!.vars",nullptr,*this));
5848 // } /*else {
5849 // x->fTimes++;
5850 // }*/
5851 // }
5852
5853 // go through components factors and variations, adding all as children if required
5855 if (!get<RooWorkspace>())
5857 // include coefs if any
5858 auto _coefs = coefs();
5859 if (_coefs.get() && strcmp(_coefs->GetName(), "1") != 0 && strcmp(_coefs->GetName(), "ONE") != 0) {
5860 if (_coefs.size() == 1 && _coefs.get<RooAddition>()) {
5861 if (strcmp(_coefs.at(0)->GetName(), "1") != 0 &&
5862 strcmp(_coefs.at(0)->GetName(), "ONE") != 0) { // don't add the "1"
5863 auto coef = std::make_shared<xRooNode>(".coef", *_coefs.at(0)->get(), *this);
5864 if (auto existing = findByObj(coef); existing) {
5865 existing->fTimes++;
5866 existing->fFolder = _coefs.at(0)->fFolder; // transfer folder assignment
5867 } else {
5868 emplace_back(coef);
5869 }
5870 }
5871 } else {
5872 if (auto existing = find(_coefs.GetName()); existing) {
5873 existing->fTimes++;
5874 existing->fFolder = _coefs.fFolder; // transfer folder assignment
5875 } else {
5876 emplace_back(std::make_shared<xRooNode>(_coefs));
5877 }
5878 }
5879 }
5883 if (get<RooAbsData>())
5885 }
5886 // if has no children and is a RooAbsArg, add all the proxies
5887 if (auto arg = get<RooAbsArg>(); arg && addedChildren == 0) {
5888 for (int i = 0; i < arg->numProxies(); i++) {
5889 auto _proxy = arg->getProxy(i);
5890 if (auto a = dynamic_cast<RooArgProxy *>(_proxy)) {
5891 auto c = std::make_shared<xRooNode>(TString::Format(".%s", _proxy->name()), *(a->absArg()), *this);
5892 if (auto existing = findByObj(c); existing) {
5893 existing->fTimes++;
5894 existing->fFolder = c->fFolder; // transfer folder assignment
5895 } else {
5896 // mark any existing children with the same name for cleanup - this happens e.g. if did a Replace on one
5897 // of these nodes note that the child nodes will still become reordered (the old node will be deleted,
5898 // new node will appear at end)
5899 for (auto &child : *this) {
5900 if (strcmp(child->GetName(), c->GetName()) == 0) {
5901 child->fTimes = 0;
5902 }
5903 }
5904 emplace_back(c);
5905 }
5906 } else if (auto s = dynamic_cast<RooAbsCollection *>(_proxy)) {
5907 for (auto a2 : *s) {
5908 auto c = std::make_shared<xRooNode>(*a2, *this);
5909 if (arg->numProxies() != 1) {
5910 c->fFolder = std::string("!.") +
5911 _proxy->name(); // don't put in a folder if there's just 1 proxy (the collection)
5912 }
5913 if (auto existing = findByObj(c); existing) {
5914 existing->fTimes++;
5915 existing->fFolder = c->fFolder; // transfer folder assignment
5916 } else {
5917 emplace_back(c);
5918 }
5919 }
5920 }
5921 }
5922 /*for(auto& s : arg->servers()) {
5923 auto c = std::make_shared<xRooNode>(*s,*this);
5924 if (auto existing = findByObj(c); existing) {
5925 existing->fTimes++;
5926 existing->fFolder = c->fFolder; // transfer folder assignment
5927 } else {
5928 emplace_back(c);
5929 }
5930 }*/
5931 } else if (auto ir = get<RooStats::HypoTestInverterResult>()) {
5932 // check if we already have a hypoSpace in our memory
5933 bool hasHS = false;
5934 for (auto &c : fBrowsables) {
5935 if (strcmp(c->GetName(), ".memory") == 0 && c->get<xRooHypoSpace>()) {
5936 hasHS = true;
5937 break;
5938 }
5939 }
5940 if (!hasHS) {
5941 // add the HS
5942 auto hs =
5943 fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", std::make_shared<xRooHypoSpace>(ir), *this));
5944 // add the hypoPoints first so they appear first
5945 auto _axes = hs->get<xRooHypoSpace>()->axes();
5946
5947 int i = 0;
5948 for (auto &hp : *hs->get<xRooHypoSpace>()) {
5950 for (auto a : _axes) {
5951 if (a != _axes.first())
5952 coordString += ",";
5953 coordString +=
5954 TString::Format("%s=%g", a->GetName(), hp.coords->getRealValue(a->GetName(), ir->GetXValue(i)));
5955 }
5956 auto hpn = emplace_back(std::make_shared<xRooNode>(coordString, hp.hypoTestResult, hs));
5957 hpn->fTimes++;
5958 hpn->fBrowsables.emplace_back(std::make_shared<xRooNode>(
5959 ".memory", std::shared_ptr<xRooNLLVar::xRooHypoPoint>(&hp, [](xRooNLLVar::xRooHypoPoint *) {}), hpn));
5960 i++;
5961 }
5962 } else {
5963 // ensure all hypoTestResults are flagged as keep-alive
5964 std::vector<std::shared_ptr<xRooNode>> &nn = *this;
5965 for (auto &c : nn) {
5966 if (c->get<RooStats::HypoTestResult>())
5967 c->fTimes++;
5968 }
5969 }
5970 // xRooNode tests;
5971 // for(int i=0;i<ir->ArraySize();i++) {
5972 // tests.push_back(std::make_shared<xRooNode>(TString::Format("%g",ir->GetXValue(i)),*ir->GetResult(i),*this));
5973 // }
5974 // appendChildren(tests);
5975 } else if (get<RooStats::HypoTestResult>()) {
5976
5977 // create the xRooHypoPoint if necessary
5978 xRooNLLVar::xRooHypoPoint *hp = nullptr;
5979 for (auto &c : fBrowsables) {
5980 if (strcmp(c->GetName(), ".memory") == 0 && c->get<xRooNLLVar::xRooHypoPoint>()) {
5981 hp = c->get<xRooNLLVar::xRooHypoPoint>();
5982 c->fTimes++; // keep it alive
5983 break;
5984 }
5985 }
5986 if (!hp) {
5987 auto shp =
5988 std::make_shared<xRooNLLVar::xRooHypoPoint>(std::dynamic_pointer_cast<RooStats::HypoTestResult>(fComp));
5989 fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", shp, *this));
5990 hp = shp.get();
5991 }
5992
5993 xRooNode fits;
5994
5995 if (auto fit = hp->ufit()) {
5996 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("ufit");
5997 }
5998 if (auto fit = hp->cfit_null()) {
5999 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("cfit_null");
6000 }
6001 if (auto fit = hp->cfit_alt()) {
6002 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("cfit_alt");
6003 }
6004 if (auto fit = hp->gfit()) {
6005 fits.emplace_back(std::make_shared<xRooNode>(fit, *this))->TNamed::SetName("gfit");
6006 }
6007 if (auto asi = hp->asimov()) {
6008 auto asiP = fits.emplace_back(std::make_shared<xRooNode>(
6009 asi->hypoTestResult ? asi->hypoTestResult : std::make_shared<RooStats::HypoTestResult>(asi->result()),
6010 *this));
6011 asiP->TNamed::SetName("asimov");
6012 asiP->fBrowsables.emplace_back(std::make_shared<xRooNode>(".memory", asi, asiP));
6013 }
6015 }
6016
6017 // clear anything that has fTimes = 0 still
6018 auto it = std::vector<std::shared_ptr<xRooNode>>::begin();
6019 while (it != std::vector<std::shared_ptr<xRooNode>>::end()) {
6020 if (it->get()->fTimes == 0) {
6021 for (auto o : *gROOT->GetListOfBrowsers()) {
6022 auto b = dynamic_cast<TBrowser *>(o);
6023 if (b && b->GetBrowserImp()) { // browserImp is null if browser was closed
6024 // std::cout << GetPath() << " Removing " << it->get()->GetPath() << std::endl;
6025
6026 if (auto _b =
6027 dynamic_cast<TGFileBrowser *>(GETACTBROWSER(dynamic_cast<TRootBrowser *>(b->GetBrowserImp())));
6028 _b) {
6029 auto _root = GETROOTDIR(_b);
6030 if (!_root)
6031 _root = GETLISTTREE(_b)->GetFirstItem();
6032 if (auto item = GETLISTTREE(_b)->FindItemByObj(_root, this); item) {
6033 GETLISTTREE(_b)->OpenItem(item);
6034 }
6035 }
6036
6037 b->RecursiveRemove(
6038 it->get()); // problem: if obj is living in a collapsed node it wont actually get deleted
6039 /*auto _b = dynamic_cast<TGFileBrowser*>( dynamic_cast<TRootBrowser*>(b->GetBrowserImp())->fActBrowser );
6040 if (_b) {
6041 std::cout << _b->fRootDir->GetText() << std::endl;
6042 if (auto item = _b->fListTree->FindItemByObj(_b->fRootDir,it->get()); item) {
6043 std::cout << "Found obj: " << item << " " << item->GetText() << std::endl;
6044 _b->fListTree->RecursiveDeleteItem(_b->fRootDir,it->get());
6045 }
6046
6047 //b->RecursiveRemove(it->get());
6048 if (auto item = _b->fListTree->FindItemByObj(_b->fRootDir,it->get()); item) {
6049 std::cout << "Still Found obj: " << item << std::endl;
6050 }
6051 _b->fListTree->ClearViewPort();
6052
6053 }*/
6054 }
6055 }
6056 /*it->get()->ResetBit(TObject::kNotDeleted); ++it;*/ it = erase(it);
6057 } else {
6058 ++it;
6059 }
6060 }
6061
6062 return *this;
6063}
6064
6065////////////////////////////////////////////////////////////////////////////////
6066/// List of observables (global and regular) of this node.
6067
6069{
6070 xRooNode out(".obs", std::make_shared<RooArgList>(), *this);
6071 out.get<RooArgList>()->setName((GetPath() + ".obs").c_str());
6072 for (auto o : vars()) {
6073 if (o->get<RooAbsArg>()->getAttribute("obs")) {
6074 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6075 out.emplace_back(o);
6076 }
6077 }
6078 return out;
6079}
6080
6081////////////////////////////////////////////////////////////////////////////////
6082/// List of global observables of this node.
6083
6085{
6086 xRooNode out(".globs", std::make_shared<RooArgList>(), *this);
6087 out.get<RooArgList>()->setName((GetPath() + ".globs").c_str());
6088 for (auto o : obs()) {
6089 if (o->get<RooAbsArg>()->getAttribute("global")) {
6090 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6091 out.emplace_back(o);
6092 }
6093 }
6094 return out;
6095}
6096
6097////////////////////////////////////////////////////////////////////////////////
6098/// List of regular observables of this node.
6099
6101{
6102 xRooNode out(".robs", std::make_shared<RooArgList>(), *this);
6103 out.get<RooArgList>()->setName((GetPath() + ".robs").c_str());
6104 for (auto o : obs()) {
6105 if (!o->get<RooAbsArg>()->getAttribute("global")) {
6106 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6107 out.emplace_back(o);
6108 }
6109 }
6110 return out;
6111}
6112
6113////////////////////////////////////////////////////////////////////////////////
6114/// List of parameters (non-observables) of this node.
6115
6117{
6118 if (strcmp(GetName(), ".bins") == 0 && fParent) {
6119 // return pars of the parent - this method is used by covariances() if e.g. do node.bins().covariances()
6120 return fParent->pars();
6121 }
6122 xRooNode out(".pars", std::make_shared<RooArgList>(), *this);
6123 out.get<RooArgList>()->setName((GetPath() + ".pars").c_str());
6124 for (auto o : vars()) {
6125 if (!o->get<RooAbsArg>()->getAttribute("obs")) {
6126 out.get<RooArgList>()->add(*(o->get<RooAbsArg>()));
6127 out.emplace_back(o);
6128 }
6129 }
6130 return out;
6131}
6132
6133////////////////////////////////////////////////////////////////////////////////
6134/// List of parameters that are currently constant
6135
6137{
6138 xRooNode out(".consts", std::make_shared<RooArgList>(), *this);
6139 out.get<RooArgList>()->setName((GetPath() + ".consts").c_str());
6140 for (auto o : pars()) {
6141 if (o->get<RooAbsArg>()->getAttribute("Constant") || o->get<RooConstVar>()) {
6142 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6143 out.emplace_back(o);
6144 }
6145 }
6146 return out;
6147}
6148
6149////////////////////////////////////////////////////////////////////////////////
6150/// List of parameters that are currently non-constant
6151/// These parameters do not have the "Constant" attribute
6152
6154{
6155 xRooNode out(".floats", std::make_shared<RooArgList>(), *this);
6156 out.get<RooArgList>()->setName((GetPath() + ".floats").c_str());
6157 for (auto o : pars()) {
6158 if (!o->get<RooAbsArg>()->getAttribute("Constant") && !o->get<RooConstVar>()) {
6159 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6160 out.emplace_back(o);
6161 }
6162 }
6163 return out;
6164}
6165
6166////////////////////////////////////////////////////////////////////////////////
6167/// List of parameters of interest: parameters marked as "of interest"
6168/// These parameters have the "poi" attribute
6169
6171{
6172 xRooNode out(".poi", std::make_shared<RooArgList>(), *this);
6173 out.get<RooArgList>()->setName((GetPath() + ".poi").c_str());
6174 for (auto o : pars()) {
6175 if (o->get<RooAbsArg>()->getAttribute("poi")) {
6176 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6177 out.emplace_back(o);
6178 }
6179 }
6180 return out;
6181}
6182
6183////////////////////////////////////////////////////////////////////////////////
6184/// List of nuisance parameters: non-constant parameters that are not marked of interest,
6185/// as well as any parameters that have been marked by the "np" attribute
6186
6188{
6189 xRooNode out(".np", std::make_shared<RooArgList>(), *this);
6190 out.get<RooArgList>()->setName((GetPath() + ".np").c_str());
6191 for (auto o : pars()) {
6192 if (o->get<RooAbsArg>()->getAttribute("np") ||
6193 (!o->get<RooAbsArg>()->getAttribute("Constant") && !o->get<RooAbsArg>()->getAttribute("poi") &&
6194 !o->get<RooConstVar>())) {
6195 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6196 out.emplace_back(o);
6197 }
6198 }
6199 return out;
6200}
6201
6202////////////////////////////////////////////////////////////////////////////////
6203/// List of prespecified parameters: non-floatable parameters
6204
6206{
6207 xRooNode out(".pp", std::make_shared<RooArgList>(), *this);
6208 out.get<RooArgList>()->setName((GetPath() + ".pp").c_str());
6209 for (auto o : pars()) {
6210 if (!o->get<RooAbsArg>()->getAttribute("np") && !o->get<RooAbsArg>()->getAttribute("poi") &&
6211 (o->get<RooAbsArg>()->getAttribute("Constant") || o->get<RooConstVar>())) {
6212 out.get<RooArgList>()->add(*o->get<RooAbsArg>());
6213 out.emplace_back(o);
6214 }
6215 }
6216 return out;
6217}
6218
6219////////////////////////////////////////////////////////////////////////////////
6220/// List of variables (observables and parameters) of this node
6221
6223{
6224 xRooNode out(".vars", std::make_shared<RooArgList>(), *this);
6225 out.get<RooArgList>()->setName((GetPath() + ".vars").c_str());
6226 if (auto coll = get<RooAbsCollection>(); coll) {
6227 for (auto &x : *this) {
6228 for (auto &y : x->vars()) {
6229 out.push_back(y);
6230 }
6231 }
6232 return out;
6233 }
6234 if (auto p = get<RooAbsArg>(); p) {
6235 // also need to get all constPars so use leafNodeServerList .. will include self if is fundamental, which is what
6236 // we want
6237 // ensure all globs appear after robs, as we rely on this ordering for picking "x" var in "reduced" method
6240 p->leafNodeServerList(&allLeaves);
6241 for (auto &c : allLeaves) {
6242 if (c->isFundamental() || (dynamic_cast<RooConstVar *>(c) && !TString(c->GetName()).IsFloat())) {
6243 if (!c->getAttribute("global")) {
6244 out.get<RooArgList>()->add(*c);
6245 out.emplace_back(std::make_shared<xRooNode>(*c, *this));
6246 }
6247 if (c->getAttribute("global")) {
6248 _globs.emplace_back(std::make_shared<xRooNode>(*c, *this));
6249 _globs.back()->fFolder = "!globs";
6250 } else if (c->getAttribute("obs")) {
6251 out.back()->fFolder = "!robs";
6252 } else if (c->getAttribute("poi")) {
6253 out.back()->fFolder = "!poi";
6254 } else if (c->getAttribute("np") ||
6255 (!c->getAttribute("Constant") && !c->getAttribute("poi") && c->IsA() != RooConstVar::Class())) {
6256 out.back()->fFolder = "!np";
6257 } else if (!c->getAttribute("Constant") && c->IsA() != RooConstVar::Class()) {
6258 out.back()->fFolder = "!floats";
6259 } else {
6260 out.back()->fFolder = "!pp";
6261 }
6262 }
6263 }
6264 for (auto g : _globs) {
6265 out.get<RooArgList>()->add(*g->get<RooAbsArg>());
6266 out.emplace_back(g);
6267 }
6268 } else if (auto p2 = get<RooAbsData>(); p2) {
6269 for (auto a : *p2->get()) {
6270 a->setAttribute("obs");
6271 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6272 out.get<RooArgList>()->add(*a);
6273 }
6274 if (auto _dglobs = p2->getGlobalObservables()) {
6275 for (auto &a : *_dglobs) {
6276 a->setAttribute("obs");
6277 a->setAttribute("global");
6278 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6279 out.get<RooArgList>()->add(*a);
6280 }
6281 } else if (auto _globs = find(".globs"); _globs && _globs->get<RooAbsCollection>()) {
6282 for (auto &a : *_globs->get<RooAbsCollection>()) {
6283 a->setAttribute("obs");
6284 a->setAttribute("global");
6285 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6286 out.get<RooArgList>()->add(*a);
6287 }
6288 } else if (auto _ws = ws(); _ws) {
6289 if (auto _globs2 = dynamic_cast<RooArgSet *>(GETWSSNAPSHOTS(_ws).find(p2->GetName())); _globs2) {
6290 for (auto a : *_globs2) {
6291 a->setAttribute("obs");
6292 a->setAttribute("global");
6293 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6294 out.get<RooArgList>()->add(*a);
6295 }
6296 } else if (auto _gl = GETWSSETS(_ws).find("globalObservables"); _gl != GETWSSETS(_ws).end()) {
6297 for (auto &_g : _gl->second) {
6298 auto _clone = std::shared_ptr<RooAbsArg>(dynamic_cast<RooAbsArg *>(_g->Clone(_g->GetName())));
6299 if (auto v = std::dynamic_pointer_cast<RooAbsRealLValue>(_clone); v && _g->getStringAttribute("nominal"))
6300 v->setVal(TString(_g->getStringAttribute("nominal")).Atof());
6301 out.emplace_back(std::make_shared<xRooNode>(_clone, *this));
6302 out.get<RooArgList>()->add(*_clone);
6303 }
6304 } else if (fParent) {
6305 // note: this is slow in large workspaces ... too many obs to look through?
6306 std::unique_ptr<RooAbsCollection> _globs3(fParent->obs().get<RooArgList>()->selectByAttrib("global", true));
6307 // std::unique_ptr<RooAbsCollection> _globs(_ws->allVars().selectByAttrib("global",true)); - tried this to
6308 // be quicker but it wasn't
6309 for (auto &_g : *_globs3) {
6310 auto _clone = std::shared_ptr<RooAbsArg>(dynamic_cast<RooAbsArg *>(_g->Clone(_g->GetName())));
6311 if (auto v = std::dynamic_pointer_cast<RooAbsRealLValue>(_clone); v && _g->getStringAttribute("nominal"))
6312 v->setVal(TString(_g->getStringAttribute("nominal")).Atof());
6313 out.emplace_back(std::make_shared<xRooNode>(_clone, *this));
6314 out.get<RooArgList>()->add(*_clone);
6315 }
6316 }
6317 }
6318 } else if (auto w = get<RooWorkspace>(); w) {
6319 for (auto a : w->allVars()) {
6320 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6321 out.get<RooArgList>()->add(*a);
6322 }
6323 // add all cats as well
6324 for (auto a : w->allCats()) {
6325 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6326 out.get<RooArgList>()->add(*a);
6327 }
6328 }
6329 return out;
6330}
6331
6333{
6334 xRooNode out(".components", nullptr, *this);
6335
6336 if (auto p = get<RooAddPdf>(); p) {
6337 // only add each pdf once (the coefs will be accumulated in coefs() method) ...
6338 std::set<RooAbsArg *> donePdfs;
6339 for (auto &o : p->pdfList()) {
6340 if (donePdfs.count(o))
6341 continue;
6342 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6343 donePdfs.insert(o);
6344 }
6345 } else if (auto p2 = get<RooRealSumPdf>(); p2) {
6346 // check for common prefixes and suffixes, will use to define aliases to shorten names
6347 // if have more than 1 function
6348 // TString commonPrefix=""; TString commonSuffix="";
6349 // if (p->funcList().size() > 1) {
6350 // bool checked=false;
6351 // for(auto& o : p->funcList()) {
6352 // if (!checked) {
6353 // commonPrefix = o->GetName(); commonSuffix = o->GetName(); checked=true;
6354 // } else {
6355 //
6356 // }
6357 // }
6358 // }
6359 std::set<RooAbsArg *> doneFuncs;
6360 for (auto &o : p2->funcList()) {
6361 if (doneFuncs.count(o))
6362 continue;
6363 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6364 doneFuncs.insert(o);
6365 }
6366 } else if (auto p3 = get<RooAddition>(); p3) {
6367 for (auto &o : p3->list()) {
6368 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6369 }
6370 } else if (auto p4 = get<RooAbsCollection>(); p4) {
6371 for (auto &a : *p4) {
6372 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6373 }
6374 } else if (auto p5 = get<RooWorkspace>(); p5) {
6375 for (auto &o : p5->components()) {
6376 // only top-level nodes (only clients are integrals or things that aren't part of the workspace)
6377 // if (o->hasClients()) continue;
6378 bool hasClients = false;
6379 for (auto &c : o->clients()) {
6380 if (!c->InheritsFrom("RooRealIntegral") && p5 == GETWS(c)) {
6381 hasClients = true;
6382 break;
6383 }
6384 }
6385 if (hasClients)
6386 continue;
6387 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6388 if (o->InheritsFrom("RooAbsPdf")) {
6389 out.back()->fFolder = "!pdfs";
6390 } else {
6391 out.back()->fFolder = "!scratch";
6392 }
6393 }
6394 for (auto &o : p5->allGenericObjects()) {
6395 if (auto fr = dynamic_cast<RooFitResult *>(o); fr) {
6396 TString s(fr->GetTitle());
6397 if (s.Contains(';'))
6398 s = s(0, s.Index(';'));
6399 if (auto _pdf = out.find(s.Data()); _pdf) {
6400 // std::cout << " type = " << _pdf->get()->ClassName() << std::endl;
6401 out.emplace_back(std::make_shared<xRooNode>(fr->GetName(), *fr, _pdf));
6402 // for a while, this node's parent pointed to something of type Node2!!
6403 // how to fix??? - I fxied it with a new constructor to avoid the shared_ptr<Node2> calling the const
6404 // Node2& constructor via getting wrapped in a Node2(shared_ptr<TObject>) call
6405 // out.back()->fParent = _pdf;
6406 // std::cout << " type2 = " << out.back()->fParent->get()->ClassName() << std::endl;
6407 } else {
6408 out.emplace_back(std::make_shared<xRooNode>(fr->GetName(), *fr, *this));
6409 }
6410 out.back()->fFolder = "!fits";
6411 } else {
6412 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6413 if (strcmp(out.back()->get()->ClassName(), "TStyle") == 0) {
6414 out.back()->fFolder = "!styles";
6415 } else if (strcmp(out.back()->get()->ClassName(), "RooStats::HypoTestInverterResult") == 0) {
6416 out.back()->fFolder = "!scans";
6417 } else if (strcmp(out.back()->get()->ClassName(), "RooStats::ModelConfig") == 0) {
6418 out.back()->fFolder = "!models";
6419 } else {
6420 out.back()->fFolder = "!objects";
6421 }
6422 }
6423 }
6424 for (auto &[k, v] : GETWSSETS(p5)) {
6425 // skip 'CACHE' sets because they are auto-removed when sanitizing workspaces, which will invalidate these
6426 // children
6427 if (k.find("CACHE_") == 0)
6428 continue;
6429 out.emplace_back(std::make_shared<xRooNode>(k.c_str(), v, *this));
6430 out.back()->fFolder = "!sets";
6431 }
6432
6434 std::unique_ptr<TIterator> iter(snaps.MakeIterator());
6435 TObject *snap;
6436 while ((snap = iter->Next())) {
6437 out.emplace_back(std::make_shared<xRooNode>(*snap, *this));
6438 out.back()->fFolder = "!snapshots";
6439 }
6440 } else if (auto mc = get<RooStats::ModelConfig>()) {
6441 // add the pdf as a child, and the external constraints set if its there
6442 if (mc->GetPdf()) {
6443 out.emplace_back(std::make_shared<xRooNode>(".pdf", *mc->GetPdf(), *this));
6444 }
6445 if (mc->GetExternalConstraints()) {
6446 out.emplace_back(std::make_shared<xRooNode>(".extCons", *mc->GetExternalConstraints(), *this));
6447 }
6448 } else if (strlen(GetName()) > 0 && GetName()[0] == '!' && fParent) {
6449 // special case of dynamic property
6450 if (TString(GetName()) == "!.pars") {
6451 for (auto &c : fParent->pars()) {
6452 out.emplace_back(c);
6453 }
6454 } else {
6455 // the components of a folder are the children of the parent (after browsing) that live in this folder
6456 fParent->browse();
6457 for (auto &c : *fParent) {
6458 if (c->fFolder == GetName()) {
6459 out.emplace_back(c);
6460 }
6461 }
6462 }
6463 }
6464
6465 return out;
6466}
6467
6468////////////////////////////////////////////////////////////////////////////////
6469/// bins of a channel or sample, or channels of a multi-channel pdf
6470
6472{
6473 xRooNode out(".bins", nullptr, *this);
6474
6475 if (auto p = get<RooSimultaneous>(); p) {
6476 std::map<int, std::shared_ptr<xRooNode>> cats; // fill into a map to preserve index ordering
6477 for (auto &c : p->indexCat()) { // is alphabetical in labels
6478 auto pp = p->getPdf(c.first.c_str());
6479 if (!pp)
6480 continue;
6481 cats[c.second] =
6482 std::make_shared<xRooNode>(TString::Format("%s=%s", p->indexCat().GetName(), c.first.c_str()), *pp, *this);
6483 }
6484 for (auto &[_, n] : cats)
6485 out.emplace_back(n);
6486 } else if (auto phf = get<ParamHistFunc>(); phf) {
6487 int i = 1;
6488#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
6489 auto &pSet = phf->_paramSet;
6490#else
6491 auto &pSet = phf->paramList();
6492#endif
6493 for (auto par : pSet) {
6494 out.emplace_back(std::make_shared<xRooNode>(*par, *this));
6495 out.back()->fBinNumber = i;
6496 i++;
6497 }
6498 } else if (auto ax = GetXaxis(); ax) {
6499 for (int i = 1; i <= ax->GetNbins(); i++) {
6500 // create a RooProduct of all bin-specific factors of all shapeFactors
6501 std::vector<RooAbsArg *> _factors;
6502 for (auto f : factors()) {
6503 if (f->get<ParamHistFunc>()) {
6504 if (f->bins()[i - 1]->get<RooProduct>()) {
6505 for (auto &ss : f->bins()[i - 1]->factors())
6506 _factors.push_back(ss->get<RooAbsArg>());
6507 } else {
6508 _factors.push_back(f->bins()[i - 1]->get<RooAbsArg>());
6509 }
6510 }
6511 }
6512 out.emplace_back(std::make_shared<xRooNode>(
6513 TString::Format("%g<=%s<%g", ax->GetBinLowEdge(i), ax->GetParent()->GetName(), ax->GetBinLowEdge(i + 1)),
6514 _factors.empty() ? nullptr
6515 : std::make_shared<RooProduct>(TString::Format("%s.binFactors.bin%d", GetName(), i),
6516 "binFactors", RooArgList()),
6517 *this));
6518 for (auto f : _factors) {
6519#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
6520 out.back()->get<RooProduct>()->_compRSet.add(*f);
6521#else
6522 const_cast<RooArgList &>(out.back()->get<RooProduct>()->realComponents()).add(*f);
6523#endif
6524 }
6525 out.back()->fBinNumber = i;
6526 }
6527 }
6528
6529 return out;
6530}
6531
6533{
6535
6536 if (recurse && fParent) {
6537 // get our coefs and multiply it by the parents coefs ...
6538 auto ourCoefs = xRooNode::coefs(false);
6539 auto parentCoefs = fParent->coefs(true);
6540 if (!parentCoefs.get<RooAbsReal>()) {
6541 // no coefs to include, just return our coefs
6542 return ourCoefs;
6543 }
6544 if (!ourCoefs.get<RooAbsReal>()) {
6545 // just return the parent's coefs
6546 return parentCoefs;
6547 }
6548 // if got here, must combine parentCoefs and outCoefs into a RooProduct
6549 xRooNode out(".recursiveCoefs",
6550 std::make_shared<RooProduct>(".recursiveCoefs",
6551 TString::Format("Recursive Coefficients of %s", GetName()),
6552 *ourCoefs.get<RooAbsReal>(), *parentCoefs.get<RooAbsReal>()),
6553 *this);
6554 // keep alive the two coef nodes by adding to out's memory
6555 auto mem = out.emplace_back(std::make_shared<xRooNode>(".memory", nullptr, *this));
6556 mem->emplace_back(std::make_shared<xRooNode>(ourCoefs));
6557 mem->emplace_back(std::make_shared<xRooNode>(parentCoefs));
6558 return out;
6559 }
6560
6561 bool isResidual = false;
6562
6563 // if parent is a sumpdf or addpdf then include the coefs
6564 // if func appears multiple times then coefs must be combined into a RooAddition temporary
6565 if (fParent) {
6566 // handle case where filters are applied .. need to pass through these
6567 // do this by iterating while fComp is null
6568 auto parent = fParent;
6569 if (!parent->fComp) {
6570 while (!parent->fComp && parent->fParent) {
6571 parent = parent->fParent;
6572 }
6573 // parent should now be node above the filters ... need parent of that
6574 parent = parent->fParent;
6575 if (!parent)
6576 parent = fParent; // revert t original parent in case something went wrong
6577 }
6578 if (auto p = parent->get<RooRealSumPdf>(); p) {
6579 std::size_t i = 0;
6580 for (auto &o : p->funcList()) {
6581 if (o == get()) {
6582 if (i >= p->coefList().size()) {
6583 isResidual = true;
6584 coefs.add(p->coefList());
6585 } else {
6586 coefs.add(*p->coefList().at(i));
6587 }
6588 }
6589 i++;
6590 }
6591 } else if (auto p2 = parent->get<RooAddPdf>(); p2) {
6592 std::size_t i = 0;
6593 if (p2->coefList().empty()) {
6594 // this can happen if all pdfs are extended then the coef is effectively the
6595 // expected number of events
6596 // TODO: test behaviour of xRooNode under this scenario (are histograms correct?)
6597 } else {
6598 for (auto &o : p2->pdfList()) {
6599 if (o == get()) {
6600 if (i >= p2->coefList().size()) {
6601 isResidual = true;
6602 coefs.add(p2->coefList());
6603 } else {
6604 coefs.add(*p2->coefList().at(i));
6605 }
6606 }
6607 i++;
6608 }
6609 }
6610 }
6611 }
6612 if (isResidual) {
6613 // return a node representing 1.-sumOfCoefs
6614 // involves creating sumOfCoefs unless there is only 1 coef, then just use that
6615 auto coefSum = coefs.empty()
6616 ? nullptr
6617 : (coefs.size() == 1 ? std::shared_ptr<RooAbsArg>(coefs.at(0), [](RooAbsArg *) {})
6618 : std::make_shared<RooAddition>((isResidual) ? ".sumOfCoefs" : ".coefs",
6619 "Coefficients of", coefs));
6620 xRooNode out(".coef", coefSum ? std::dynamic_pointer_cast<RooAbsArg>(std::make_shared<RooFormulaVar>(
6621 ".coef", "1-sum(otherCoefs)", "1. - @0", *coefSum))
6622 : nullptr /* should we return a "1." instead? */);
6623 if (coefSum && coefs.size() != 1) {
6624 out.emplace_back(std::make_shared<xRooNode>(".memory", nullptr, *this))
6625 ->emplace_back(
6626 std::make_shared<xRooNode>(".sumOfCoefs", coefSum, out)); // added to keep the sum alive! with the node
6627 }
6628 if (!coefs.empty()) {
6629 out.browse();
6630 }
6631 return out;
6632 } else if (coefs.size() == 1) {
6633 xRooNode out(".coef", std::shared_ptr<RooAbsArg>(coefs.at(0), [](RooAbsArg *) {}), *this);
6634 if (!coefs.empty()) {
6635 out.browse();
6636 }
6637 return out;
6638 } else {
6639 auto coefSum =
6640 coefs.empty()
6641 ? nullptr
6642 : std::make_shared<RooAddition>(".coefs", TString::Format("Coefficients of %s", GetName()), coefs);
6643 xRooNode out(".coefs", coefSum, *this);
6644 if (!coefs.empty())
6645 out.browse();
6646
6647 return out;
6648 }
6649}
6650
6652{
6653 xRooNode out(".factors", nullptr, *this);
6654
6655 if (auto p = get<RooProdPdf>(); p) {
6656 auto _main = mainChild();
6657 if (auto a = _main.get<RooRealSumPdf>(); a && !a->getStringAttribute("alias")) {
6658 a->setStringAttribute("alias", "samples");
6659 } else if (auto a2 = _main.get<RooAddPdf>(); a2 && !a2->getStringAttribute("alias")) {
6660 a2->setStringAttribute("alias", "components");
6661 }
6662 int _npdfs = p->pdfList().size();
6663 for (auto &o : p->pdfList()) {
6664 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6665 if (_npdfs > 5 && o != _main.get() && out.back()->robs().size() == 0) // constraints have no robs in them
6666 out.back()->fFolder = "!constraints";
6667 }
6668 } else if (auto p2 = get<RooProduct>(); p2) {
6669 for (auto &o : p2->components()) {
6670 if (o->InheritsFrom("RooProduct")) {
6671 // get factors of this term
6672 auto x = xRooNode("tmp", *o, *this).factors();
6673 for (auto &n : x) {
6674 out.emplace_back(std::make_shared<xRooNode>(n->GetName(), n->fComp, *this));
6675 }
6676 } else {
6677 out.emplace_back(std::make_shared<xRooNode>(*o, *this));
6678 }
6679 }
6680 } else if (auto w = get<RooWorkspace>(); w) {
6681 // if workspace, return all functions (not pdfs) that have a RooProduct as one of their clients
6682 // or not clients
6683 // exclude obs and globs
6684 auto oo = obs(); // need to keep alive as may contain owning globs
6685 auto &_obs = *(oo.get<RooArgList>());
6686 for (auto a : w->allFunctions()) {
6687 if (_obs.contains(*a))
6688 continue;
6689 bool show(true);
6690 for (auto c : a->clients()) {
6691 show = false;
6692 if (c->InheritsFrom("RooProduct")) {
6693 show = true;
6694 break;
6695 }
6696 }
6697 if (show)
6698 out.emplace_back(std::make_shared<xRooNode>(*a, *this));
6699 }
6700 }
6701
6702 /*
6703 // if parent is a sumpdf or addpdf then include the coefs
6704 // if func appears multiple times then coefs must be combined into a RooAddition temporary
6705 if (fParent) {
6706 RooArgList coefs;
6707 if(auto p = fParent->get<RooRealSumPdf>();p) {
6708 int i=0;
6709 for(auto& o : p->funcList()) {
6710 if (o == get()) {
6711 coefs.add( *p->coefList().at(i) );
6712 }
6713 i++;
6714 }
6715 } else if(auto p = fParent->get<RooAddPdf>(); p) {
6716 int i=0;
6717 for(auto& o : p->pdfList()) {
6718 if (o == get()) {
6719 coefs.add( *p->coefList().at(i) );
6720 }
6721 i++;
6722 }
6723 }
6724 if (!coefs.empty()) {
6725 if (coefs.size() == 1) {
6726 if (strcmp(coefs.at(0)->GetName(),"1")) { // don't add the "1"
6727 out.emplace_back(std::make_shared<Node2>(".coef", *coefs.at(0), *this));
6728 }
6729 } else {
6730 out.emplace_back(std::make_shared<Node2>(".coefs",
6731 std::make_shared<RooAddition>(".coefs", "Coefficients of",
6732 coefs), *this));
6733 }
6734 }
6735 }
6736 */
6737 return out;
6738}
6739
6741{
6742 xRooNode out(".variations", nullptr, *this);
6743
6744 // if (auto p = get<RooSimultaneous>(); p) {
6745 // for (auto &c : p->indexCat()) {
6746 // auto pp = p->getPdf(c.first.c_str());
6747 // if (!pp)
6748 // continue;
6749 // out.emplace_back(
6750 // std::make_shared<xRooNode>(TString::Format("%s=%s", p->indexCat().GetName(), c.first.c_str()), *pp,
6751 // *this));
6752 // }
6753 // } else
6754 if (auto p2 = get<PiecewiseInterpolation>(); p2) {
6755#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
6756 out.emplace_back(std::make_shared<xRooNode>("nominal", p2->_nominal.arg(), *this));
6757#else
6758 out.emplace_back(std::make_shared<xRooNode>("nominal", *(p2->nominalHist()), *this));
6759#endif
6760 for (size_t i = 0; i < p2->paramList().size(); i++) {
6761 // TODO: should we only return one if we find they are symmetrized?
6762 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p2->paramList().at(i)->GetName()),
6763 *p2->highList().at(i), *this));
6764 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p2->paramList().at(i)->GetName()),
6765 *p2->lowList().at(i), *this));
6766 }
6768#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
6769 out.emplace_back(std::make_shared<xRooNode>("nominal", RooFit::RooConst(p3->_nominal), *this));
6770 for (size_t i = 0; i < p3->_paramList.size(); i++) {
6771 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p3->_paramList.at(i)->GetName()),
6772 RooFit::RooConst(p3->_high.at(i)), *this));
6773 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p3->_paramList.at(i)->GetName()),
6774 RooFit::RooConst(p3->_low.at(i)), *this));
6775 }
6776#else
6777 out.emplace_back(std::make_shared<xRooNode>("nominal", RooFit::RooConst(p3->nominal()), *this));
6778 for (size_t i = 0; i < p3->variables().size(); i++) {
6779 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=1", p3->variables().at(i)->GetName()),
6780 RooFit::RooConst(p3->high().at(i)), *this));
6781 out.emplace_back(std::make_shared<xRooNode>(TString::Format("%s=-1", p3->variables().at(i)->GetName()),
6782 RooFit::RooConst(p3->low().at(i)), *this));
6783 }
6784#endif
6785
6786 } else if (auto p4 = get<ParamHistFunc>(); p4) {
6787 // I *think* I put this here so that can browse into a ParamHistFunc
6788 // int i = 0;
6789 // for (auto par : p4->_paramSet) {
6790 // TString _name = par->GetName();
6791 // // if(auto _v = dynamic_cast<RooRealVar*>(p->_dataSet.get(i)->first()); _v) {
6792 // // _name = TString::Format("%s=%g",_v->GetName(),_v->getVal());
6793 // // }
6794 // // out.emplace_back(std::make_shared<xRooNode>(_name,*par,*this)); -- -removed cos now have bin()
6795 // method i++;
6796 // }
6797 }
6798 return out;
6799}
6800
6802{
6803 RooArgList out;
6804 out.setName(GetName());
6805 for (auto &k : *this) {
6806 if (auto o = k->get<RooAbsArg>(); o)
6807 out.add(*o);
6808 }
6809 return out;
6810}
6811
6813{
6814 xRooNode out(".datasets()", nullptr, *this);
6815 // removed the browse operation since no longer showing '.datasets()' in browser
6816 // and otherwise this means dataset reduction operation will be called every time we 'browse()' the datasets node
6817 // out.fBrowseOperation = [](xRooNode *f) { return f->fParent->datasets(); };
6818
6819 if (auto _ws = get<RooWorkspace>(); _ws) {
6820 for (auto &d : _ws->allData()) {
6821 out.emplace_back(std::make_shared<xRooNode>(*d, *this));
6822 out.back()->fFolder = "!datasets";
6823 }
6824 } else if (get<RooAbsPdf>() ||
6825 (!get() && fParent &&
6826 fParent->get<RooAbsPdf>())) { // second condition handles 'bins' nodes of pdf, which have null ptr
6827 // only add datasets that have observables that cover all our observables
6828 auto oo = obs(); // must keep alive in case is owning the globs
6829 RooArgSet _obs(*oo.get<RooArgList>());
6830 //_obs.add(coords(true).argList(), true); // include coord observables too, and current xaxis if there's one -
6831 // added in loop below
6832
6833 TString cut;
6835 for (auto _c : coords()) { // coords() moves vars to their respective coordinates too
6836 if (auto _cat = _c->get<RooAbsCategoryLValue>(); _cat) {
6837 if (cut != "")
6838 cut += " && ";
6839 cut += TString::Format("%s==%d", _cat->GetName(), _cat->getCurrentIndex());
6840 _obs.add(*_cat,
6841 true); // note: if we ever changed coords to return clones, would need to keep coords alive
6842 cutobs.add(*_cat);
6843 } else if (auto _rv = _c->get<RooAbsRealLValue>(); _rv) {
6844 // todo: check coordRange is a single range rather than multirange
6845 if (cut != "")
6846 cut += " && ";
6847 cut += TString::Format("%s>=%f&&%s<%f", _rv->GetName(), _rv->getMin(_rv->getStringAttribute("coordRange")),
6848 _rv->GetName(), _rv->getMax(_rv->getStringAttribute("coordRange")));
6849 _obs.add(*_rv,
6850 true); // note: if we ever changed coords to return clones, would need to keep coords alive
6851 cutobs.add(*_rv);
6852 } else {
6853 throw std::runtime_error("datasets(): Unsupported coordinate type");
6854 }
6855 }
6856 if (auto s = get<RooSimultaneous>()) {
6857 // check if we have a pdf for every category ... if not then add to cut
6858 bool hasMissing = false;
6859 TString extraCut = "";
6860 for (auto cat : s->indexCat()) {
6861 if (!s->getPdf(cat.first.c_str())) {
6862 hasMissing = true;
6863 } else {
6864 if (extraCut != "")
6865 extraCut += " || ";
6866 extraCut += TString::Format("%s==%d", s->indexCat().GetName(), cat.second);
6867 }
6868 }
6869 if (hasMissing) {
6870 if (cut != "")
6871 cut += " && ";
6872 cut += "(" + extraCut + ")";
6873 cutobs.add(s->indexCat());
6874 }
6875 }
6876
6877 if (auto ax = GetXaxis(); ax && dynamic_cast<RooAbsArg *>(ax->GetParent())->getAttribute("obs")) {
6878 auto a = dynamic_cast<RooAbsArg *>(ax->GetParent());
6879 _obs.add(*a, true);
6880 }
6881 xRooNode _datasets; // will be any child datasets, along with datasets of the workspace
6882 for (auto &child : *this) {
6883 if (child->get<RooAbsData>())
6884 _datasets.push_back(child);
6885 }
6886 if (auto __ws = ws(); __ws) {
6887 xRooNode _wsNode(*__ws, *this);
6888 for (auto &d : _wsNode.datasets()) {
6889 _datasets.push_back(d);
6890 }
6891 }
6892
6893 for (auto &d : _datasets) {
6894 if (std::unique_ptr<RooAbsCollection>(d->obs().argList().selectCommon(_obs))->size() == _obs.size()) {
6895 // all obs present .. include
6896
6897 if (cut != "") {
6898 RooFormulaVar cutFormula("cut1", cut, cutobs); // doing this to avoid complaints about unused vars
6899 // TODO: Could consider using a 'filter' node (see filter() method) applied to the dataset instead
6900 // of creating and using a reduced dataset here
6901 out.emplace_back(std::make_shared<xRooNode>(
6902 std::shared_ptr<RooAbsData>(d->get<RooAbsData>()->reduce(
6903 *std::unique_ptr<RooAbsCollection>(d->robs().get<RooArgList>()->selectCommon(_obs)), cutFormula)),
6904 *this));
6905 // put a subset of the globs in the returned dataset too
6906 out.back()->get<RooAbsData>()->setGlobalObservables(*std::unique_ptr<RooAbsCollection>(
6907 d->globs().get<RooArgList>()->selectCommon(*globs().get<RooArgList>())));
6908 if (d->get()->TestBit(1 << 20))
6909 out.back()->get()->SetBit(1 << 20);
6910 // need to attach the original dataset so that things like SetBinContent can interact with it
6911 out.back()->fBrowsables.emplace_back(std::make_shared<xRooNode>(".sourceds", d->fComp, *this));
6912 } else {
6913 out.emplace_back(std::make_shared<xRooNode>(d->fComp, *this));
6914 }
6915 }
6916 }
6917 /*else if(auto p = get<RooFitResult>(); p) {
6918 // look for datasets in workspace that match the fit result name after hashing
6919 for(auto& _d : xRooNode(*_ws,*this).datasets()) {
6920 auto _hash = RooAbsTree::nameToHash(_d->get()->GetName());
6921 if (TString::Format("%d;%d",_hash.first,_hash.second) == p->GetTitle()) {
6922 out.emplace_back(std::make_shared<xRooNode>(_d->fComp, *this));
6923 }
6924 }
6925 }*/
6926 } else if (auto mc = get<RooStats::ModelConfig>()) {
6927 return xRooNode(*mc->GetPdf(), fParent).datasets();
6928 }
6929
6930 return out;
6931}
6932
6933std::shared_ptr<xRooNode> xRooNode::getBrowsable(const char *name) const
6934{
6935 for (auto b : fBrowsables) {
6936 if (b && strcmp(b->GetName(), name) == 0)
6937 return b;
6938 }
6939 return nullptr;
6940}
6941
6943{
6944
6945 if (auto fr = get<RooFitResult>(); fr) {
6946 return nullptr;
6947 }
6948
6949 if (auto theData = get<RooDataSet>(); theData) {
6950
6951 TH1 *theHist = nullptr;
6952
6953 if (fromPad) {
6954 // find first histogram in pad
6955 for (auto o : *fromPad->GetListOfPrimitives()) {
6956 theHist = dynamic_cast<TH1 *>(o);
6957 if (theHist) {
6958 theHist = static_cast<TH1 *>(theHist->Clone());
6959 theHist->Reset();
6960 break;
6961 } // clone because theHist gets deleted below
6962 }
6963 }
6964
6965 if (!theHist) {
6966 auto _parentPdf = parentPdf();
6967 if (!_parentPdf) {
6968 // can still build graph if v is an obs ... will use v binning
6969 auto vo = dynamic_cast<TObject *>(v);
6970 if (v && obs().find(vo->GetName())) {
6971 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(v)) {
6972 theHist = new TH1D(
6973 TString::Format("%s_%s", GetName(), vo->GetName()),
6974 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
6975 cat->numTypes(), 0, cat->numTypes());
6976 int i = 1;
6977 std::map<int, std::string> cats; // fill into a map to preserve index ordering
6978 for (auto &c : *cat) {
6979 cats[c.second] = c.first;
6980 }
6981 for (auto &[_, label] : cats) {
6982 theHist->GetXaxis()->SetBinLabel(i++, label.c_str());
6983 }
6984 } else {
6985 auto _binning = v->getBinningPtr(nullptr);
6986 if (_binning->isUniform()) {
6987 theHist = new TH1D(
6988 TString::Format("%s_%s", GetName(), vo->GetName()),
6989 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
6990 v->numBins(), _binning->lowBound(), _binning->highBound());
6991 } else {
6992 theHist = new TH1D(
6993 TString::Format("%s_%s", GetName(), vo->GetName()),
6994 TString::Format("my temp hist;%s", strlen(vo->GetTitle()) ? vo->GetTitle() : vo->GetName()),
6995 v->numBins(), _binning->array());
6996 }
6997 }
6998 } else {
6999 throw std::runtime_error("Cannot draw dataset without parent PDF");
7000 }
7001 } else {
7002 theHist = _parentPdf->BuildHistogram(v, true);
7003 }
7004 }
7005 if (!theHist)
7006 return nullptr;
7007 // this hist will get filled with w*x to track weighted x position per bin
7008 TH1 *xPos = static_cast<TH1 *>(theHist->Clone("xPos"));
7009 xPos->Reset();
7010 TH1 *xPos2 = static_cast<TH1 *>(theHist->Clone("xPos2"));
7011 xPos2->Reset();
7012 auto nHist = std::unique_ptr<TH1>(static_cast<TH1 *>(theHist->Clone("nEntries")));
7013 nHist->Reset();
7014
7015 auto dataGraph = new TGraphAsymmErrors;
7016 dataGraph->SetEditable(false);
7017 dataGraph->SetName(GetName());
7018 dataGraph->SetTitle(strlen(theData->GetTitle()) ? theData->GetTitle() : theData->GetName());
7019 // next line triggers creation of the histogram inside the graph, in root 6.22 that isn't protected from being
7020 // added to gDirectory
7021 dataGraph->SetTitle(TString::Format("%s;%s;Events", dataGraph->GetTitle(), theHist->GetXaxis()->GetTitle()));
7022 *static_cast<TAttMarker *>(dataGraph) = *static_cast<TAttMarker *>(theHist);
7023 *static_cast<TAttLine *>(dataGraph) = *static_cast<TAttLine *>(theHist);
7024 dataGraph->SetMarkerStyle(20);
7025 dataGraph->SetLineColor(kBlack);
7026 dataGraph->SetMarkerSize(gStyle->GetMarkerSize());
7027
7028 auto _obs = obs();
7029
7030 // auto x = theData->get()->find((v) ? dynamic_cast<TObject*>(v)->GetName() : theHist->GetXaxis()->GetName());
7031 // const RooAbsReal* xvar = (x) ? dynamic_cast<RooAbsReal*>(x) : nullptr;
7032 // const RooAbsCategory* xcat = (x && !xvar) ? dynamic_cast<RooAbsCategory*>(x) : nullptr;
7033 auto x = _obs.find((v) ? dynamic_cast<TObject *>(v)->GetName()
7034 : (theHist->GetXaxis()->IsAlphanumeric() ? theHist->GetXaxis()->GetTimeFormatOnly()
7035 : theHist->GetXaxis()->GetName()));
7036 if (x && x->get<RooAbsArg>()->getAttribute("global")) {
7037 // is global observable ...
7038 dataGraph->SetPoint(0, x->get<RooAbsReal>()->getVal(), 1e-15);
7039 dataGraph->SetTitle(TString::Format("%s = %f", dataGraph->GetTitle(), dataGraph->GetPointX(0)));
7040 delete xPos;
7041 delete xPos2;
7042 delete theHist;
7043 return dataGraph;
7044 }
7045
7046 const RooAbsReal *xvar = (x) ? x->get<RooAbsReal>() : nullptr;
7047 const RooAbsCategory *xcat = (x && !xvar) ? x->get<RooAbsCategory>() : nullptr;
7048
7049 auto _coords = coords();
7050
7051 TString pName((fromPad) ? fromPad->GetName() : "");
7052 auto _pos = pName.Index('=');
7053
7054 int nevent = theData->numEntries();
7055 for (int i = 0; i < nevent; i++) {
7056 theData->get(i);
7057 bool _skip = false;
7058 for (auto _c : _coords) {
7059 if (auto cat = _c->get<RooAbsCategoryLValue>(); cat) {
7060 if (cat->getIndex() != theData->get()->getCatIndex(cat->GetName())) {
7061 _skip = true;
7062 break;
7063 }
7064 } else if (auto rv = _c->get<RooAbsRealLValue>(); rv) {
7065 // must be in range
7066 if (!rv->inRange(theData->get()->getRealValue(rv->GetName()), rv->getStringAttribute("coordRange"))) {
7067 _skip = true;
7068 break;
7069 }
7070 }
7071 }
7072 if (_pos != -1) {
7073 if (auto cat = dynamic_cast<RooAbsCategory *>(theData->get()->find(TString(pName(0, _pos))));
7074 cat && cat->getLabel() != pName(_pos + 1, pName.Length())) {
7075 _skip = true;
7076 }
7077 }
7078 if (_skip)
7079 continue;
7080
7081 if (xvar) {
7082 xPos->Fill(xvar->getVal(), xvar->getVal() * theData->weight());
7083 xPos2->Fill(xvar->getVal(), pow(xvar->getVal(), 2) * theData->weight());
7084 }
7085
7086 if (xcat) {
7087 theHist->Fill(xcat->getLabel(), theData->weight());
7088 nHist->Fill(xcat->getLabel(), 1);
7089 } else {
7090 theHist->Fill((x) ? xvar->getVal() : 0.5, theData->weight());
7091 nHist->Fill((x) ? xvar->getVal() : 0.5, 1);
7092 }
7093 }
7094
7095 xPos->Divide(theHist);
7096 xPos2->Divide(theHist);
7097
7098 // update the x positions to the means for each bin and use poisson asymmetric errors for data ..
7099 for (int i = 0; i < theHist->GetNbinsX(); i++) {
7100 if (includeZeros || nHist->GetBinContent(i + 1)) {
7101 double val = theHist->GetBinContent(i + 1);
7102
7103 dataGraph->SetPoint(dataGraph->GetN(),
7104 (xvar && val) ? xPos->GetBinContent(i + 1) : theHist->GetBinCenter(i + 1), val);
7105
7106 // x-error will be the (weighted) standard deviation of the x values ...
7107 double xErr = xPos2->GetBinContent(i + 1) - pow(xPos->GetBinContent(i + 1), 2);
7108 xErr = (xErr <= 0) ? 0. : sqrt(xErr); // protects against floating point rounding effects
7109
7110 if (xErr || val) {
7111 dataGraph->SetPointError(dataGraph->GetN() - 1, xErr, xErr,
7112 val - 0.5 * TMath::ChisquareQuantile(TMath::Prob(1, 1) / 2., 2. * (val)),
7113 0.5 * TMath::ChisquareQuantile(1. - TMath::Prob(1, 1) / 2., 2. * (val + 1)) -
7114 val);
7115 }
7116 }
7117 }
7118
7119 // transfer limits from theHist to dataGraph hist
7120 dataGraph->GetHistogram()->GetXaxis()->SetLimits(theHist->GetXaxis()->GetXmin(), theHist->GetXaxis()->GetXmax());
7121 // and bin labels, if any
7122 if (xcat) {
7123 dataGraph->GetHistogram()->GetXaxis()->Set(theHist->GetNbinsX(), 0, theHist->GetNbinsX());
7124 for (int i = 1; i <= theHist->GetNbinsX(); i++)
7125 dataGraph->GetHistogram()->GetXaxis()->SetBinLabel(i, theHist->GetXaxis()->GetBinLabel(i));
7126 }
7127
7128 delete xPos;
7129 delete xPos2;
7130 delete theHist;
7131
7132 // std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case getObject
7133 // has decided to return the owning ptr (for some reason) std::string _title =
7134 // strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(); if (!gROOT->GetStyle(_title.c_str()))
7135 // {
7136 // if ( (style = getObject<TStyle>(_title)) ) {
7137 // // loaded style (from workspace?) so put in list and use that
7138 // gROOT->GetListOfStyles()->Add(style.get());
7139 // } else {
7140 // // create new style - gets put in style list automatically so don't have to delete
7141 // // acquire them so saved to workspaces for auto reload ...
7142 // style = const_cast<xRooNode&>(*this).acquireNew<TStyle>(_title.c_str(),
7143 // TString::Format("Style for %s component", _title.c_str()));
7144 // (TAttLine &) (*style) = *dynamic_cast<TAttLine *>(dataGraph);
7145 // (TAttFill &) (*style) = *dynamic_cast<TAttFill *>(dataGraph);
7146 // (TAttMarker &) (*style) = *dynamic_cast<TAttMarker *>(dataGraph);
7147 // gROOT->GetListOfStyles()->Add(style.get());
7148 // }
7149 // }
7150 auto _styleNode = styles(dataGraph);
7151 if (auto _style = _styleNode.get<TStyle>()) {
7152 *dynamic_cast<TAttLine *>(dataGraph) = *_style;
7153 *dynamic_cast<TAttFill *>(dataGraph) = *_style;
7154 *dynamic_cast<TAttMarker *>(dataGraph) = *_style;
7155 }
7156 return dataGraph;
7157 }
7158
7159 throw std::runtime_error("Cannot build graph");
7160}
7161
7163{
7164 if (fr) {
7165 if (auto _w = ws(); _w) {
7166 auto res = acquire(std::shared_ptr<RooFitResult>(const_cast<RooFitResult *>(fr), [](RooFitResult *) {}));
7167 for (auto o : _w->allGenericObjects()) {
7168 if (auto _fr = dynamic_cast<RooFitResult *>(o); _fr) {
7169 _fr->ResetBit(1 << 20);
7170 }
7171 }
7172 res->SetBit(1 << 20);
7173 // assign values
7174 auto allVars = _w->allVars();
7175 allVars = fr->floatParsFinal();
7176 allVars = fr->constPars();
7177 } else {
7178 // need to add to memory as a specific name
7179 throw std::runtime_error("Not supported yet"); // complication is how to replace an existing fitResult in
7180 // .memory auto _clone = std::make_shared<RooFitResult>(*fr);
7181 //_clone->SetName("fitResult");
7182 }
7183 } else {
7185 }
7186}
7187
7189{
7190 if (auto _fr = fr.get<const RooFitResult>()) {
7192 } else
7193 throw std::runtime_error("Not a RooFitResult");
7194}
7195
7196xRooNode xRooNode::fitResult(const char *opt) const
7197{
7198
7199 if (get<RooFitResult>())
7200 return *this;
7201 if (get<RooAbsData>()) {
7202 if (auto _fr = find(".fitResult"); _fr)
7203 return _fr;
7204#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
7205 // check if weightVar of RooAbsData has fitResult attribute on it, will be the generation fit result
7206 if (get<RooDataSet>() && get<RooDataSet>()->weightVar() &&
7207 get<RooDataSet>()->weightVar()->getStringAttribute("fitResult")) {
7208 return xRooNode(getObject<const RooFitResult>(get<RooDataSet>()->weightVar()->getStringAttribute("fitResult")),
7209 *this);
7210 }
7211#endif
7212 return xRooNode();
7213 }
7214
7215 TString sOpt(opt);
7216 if (sOpt == "prefit") {
7217 // build a fitResult using nominal values and infer errors from constraints
7218 // that aren't the 'main' constraints
7219 // Warning("fitResult","Building prefitResult by examining pdf. Consider setting an explicit prefitResult
7220 // (SetFitResult(fr)) where fr name is prefitResult");
7221
7222 // ensure coefs are included if there are any
7223 auto _coefs = coefs();
7224 if (_coefs.get()) {
7225 return xRooNode(RooProduct("tmp", "tmp", RooArgList(*get<RooAbsArg>(), *_coefs.get<RooAbsReal>())))
7226 .fitResult(opt);
7227 }
7228
7229 std::unique_ptr<RooArgList> _pars(dynamic_cast<RooArgList *>(pars().argList().selectByAttrib("Constant", false)));
7230 auto fr = std::make_shared<RooFitResult>("prefitResult", "Prefit");
7231 fr->setFinalParList(*_pars);
7232 for (auto &p : fr->floatParsFinal()) {
7233 auto _v = dynamic_cast<RooRealVar *>(p);
7234 if (!_v)
7235 continue;
7236 if (auto s = _v->getStringAttribute("nominal"); s)
7237 _v->setVal(TString(s).Atof());
7238 auto _constr = xRooNode(fParent->getObject<RooRealVar>(p->GetName()), *this).constraints();
7239 std::shared_ptr<xRooNode> pConstr;
7240 for (auto &c : _constr) {
7241 if (c->get<RooPoisson>() || c->get<RooGaussian>()) {
7242 // require parameter to be a direct server of the constraint pdf to count
7243 bool isServer = true;
7244 if (c->get<RooGaussian>()) {
7245 isServer = false;
7246 for (auto s : c->get<RooAbsArg>()->servers()) {
7247 if (strcmp(s->GetName(), p->GetName()) == 0) {
7248 isServer = true;
7249 break;
7250 }
7251 }
7252 }
7253 if (isServer) {
7254 pConstr = c;
7255 break;
7256 }
7257 }
7258 }
7259 if (pConstr) {
7260 // there will be 3 deps, one will be this par, the other two are the mean and error (or error^2 in case of
7261 // poisson use the one that's a ConstVar as the error to break a tie ...
7262 double prefitVal = 0;
7263 double prefitError = 0;
7264 for (auto &_d : pConstr->vars()) {
7265 if (strcmp(p->GetName(), _d->get()->GetName()) == 0)
7266 continue;
7267 if (auto _c = _d->get<RooConstVar>(); _c && _c->getVal() != 0) {
7268 if (prefitError)
7269 prefitVal = prefitError; // loading val into error already, so move it over
7270 prefitError = _c->getVal();
7271 } else if (prefitError == 0) {
7272 prefitError = _d->get<RooAbsReal>()->getVal();
7273 } else {
7274 prefitVal = _d->get<RooAbsReal>()->getVal();
7275 }
7276 }
7277
7278 if (pConstr->get<RooGaussian>() && pConstr->browse().find(".sigma")) {
7279 prefitError = pConstr->find(".sigma")->get<RooAbsReal>()->getVal();
7280 }
7281 // std::cout << p->GetName() << " extracted " << prefitVal << " " << prefitError << " from ";
7282 // pConstr->deps().Print();
7283 if (pConstr->get<RooPoisson>()) {
7284 // prefitVal will be the global observable value, need to divide that by tau
7286 // prefiterror will be tau ... need 1/sqrt(tau) for error
7287 prefitError = 1. / sqrt(prefitError);
7288 }
7289 if (!_v->getStringAttribute("nominal"))
7290 _v->setVal(prefitVal);
7291 _v->setError(prefitError);
7292 } else {
7293 // unconstrained, remove error
7294 _v->removeError();
7295 }
7296 }
7297 auto _args = consts().argList();
7298 _args.add(pp().argList());
7299 // global obs are added to constPars list too
7300 auto _globs = globs(); // keep alive as may own glob
7301 _args.add(_globs.argList());
7302 fr->setConstParList(_args);
7303 std::unique_ptr<RooArgList> _snap(dynamic_cast<RooArgList *>(_pars->snapshot()));
7304 for (auto &p : *_snap) {
7305 if (auto atr = p->getStringAttribute("initVal"); atr && dynamic_cast<RooRealVar *>(p))
7306 dynamic_cast<RooRealVar *>(p)->setVal(TString(atr).Atof());
7307 }
7308 fr->setInitParList(*_snap);
7309 return xRooNode(fr, *this);
7310 }
7311
7312 // return first checked fit result present in the workspace
7313 if (auto _w = ws(); _w) {
7314 auto checkFr = [&](TObject *o) {
7315 if (auto _fr = dynamic_cast<RooFitResult *>(o); _fr && _fr->TestBit(1 << 20)) {
7316 // check all pars match final/const values ... if mismatch need to create a new RooFitResult
7320 for (auto p : pars()) {
7321 if (p->get<RooAbsArg>()->getAttribute("Constant") || p->get<RooConstVar>()) {
7322 // par must not be in the float list or have different value to what is in constPars (if it is there)
7323 if (_fr->floatParsFinal().find(p->GetName()) ||
7324 (p->get<RooAbsReal>() &&
7325 std::abs(_fr->constPars().getRealValue(p->GetName(), std::numeric_limits<double>::quiet_NaN()) -
7326 p->get<RooAbsReal>()->getVal()) > 1e-15) ||
7327 (p->get<RooAbsCategory>() &&
7328 p->get<RooAbsCategory>()->getCurrentIndex() !=
7329 _fr->constPars().getCatIndex(p->GetName(), std::numeric_limits<int>().max()))) {
7330 newConsts.add(*p->get<RooAbsArg>());
7331 }
7332 } else {
7333 // floating par must be present in the floatPars list with the same value
7334 if (!_fr->floatParsFinal().find(p->GetName())) {
7335 newFloats.add(*p->get<RooAbsArg>());
7336 } else if ((p->get<RooAbsReal>() &&
7337 std::abs(_fr->floatParsFinal().getRealValue(p->GetName(),
7338 std::numeric_limits<double>::quiet_NaN()) -
7339 p->get<RooAbsReal>()->getVal()) > 1e-15) ||
7340 (p->get<RooAbsCategory>() &&
7341 p->get<RooAbsCategory>()->getCurrentIndex() !=
7342 _fr->floatParsFinal().getCatIndex(p->GetName(), std::numeric_limits<int>().max()))) {
7343 // value of existing float changed
7344 oldFloats.add(*p->get<RooAbsArg>());
7345 }
7346 }
7347 }
7348 if (!oldFloats.empty() || !newFloats.empty() || !newConsts.empty()) {
7349 // create new fit result using covariance from the fit result
7350 // remove any new consts from the list before extracting covariance matrix
7351 RooArgList existingFloats(_fr->floatParsFinal());
7352 existingFloats.remove(newConsts, true, true /* match name*/);
7353 auto cov = _fr->reducedCovarianceMatrix(existingFloats);
7354 if (!newFloats.empty()) {
7355 // extend the covariance matrix and add variances using current parameter errors
7356 size_t oldSize = existingFloats.size();
7357 cov.ResizeTo(oldSize + newFloats.size(), oldSize + newFloats.size());
7358 for (size_t i = 0; i < newFloats.size(); i++) {
7359 existingFloats.add(*newFloats.at(i));
7360 auto v = dynamic_cast<RooRealVar *>(newFloats.at(i));
7361 if (v)
7362 cov(oldSize + i, oldSize + i) = std::pow(v->getError(), 2);
7363 }
7364 }
7365 RooArgList existingConsts(_fr->constPars());
7366 existingConsts.remove(newFloats, true, true);
7368
7369 // do we need to add our remaining const pars to the const par list? or the globs?
7370 // for speed we wont bother
7371 // note that generating datasets needs the globs in the const pars list so the robs can be determined
7372 // at the moment this check is done in the generate() method (along with check for missing pars)
7373
7374 auto fr = std::make_shared<RooFitResult>(TString::Format("%s-dirty", _fr->GetName()));
7375 fr->setFinalParList(existingFloats);
7376 fr->setConstParList(existingConsts);
7377 fr->setCovarianceMatrix(cov);
7378 fr->setInitParList(_fr->floatParsInit()); // will only be the pars that were actually float for the fit
7379
7380 return xRooNode(fr, *this);
7381 } else {
7382 // all matching, can return the fit result as-is
7383 return xRooNode(*_fr, std::make_shared<xRooNode>(*_w, std::make_shared<xRooNode>()));
7384 }
7385 }
7386 return xRooNode();
7387 };
7388 for (auto o : _w->allGenericObjects()) {
7389 auto out = checkFr(o);
7390 if (out)
7391 return out;
7392 }
7393 for (auto o : GETWSSNAPSHOTS(_w)) {
7394 auto out = checkFr(o);
7395 if (out)
7396 return out;
7397 }
7398 } else {
7399 // objects not in workspaces are allowed to have a fitResult set in their memory
7400 // use getObject to get it
7401 if (auto fr = getObject<RooFitResult>(".fitResult"); fr) {
7402 return xRooNode(fr, *this);
7403 }
7404 }
7405
7406 // ensure coefs are included if there are any
7407 auto _coefs = coefs();
7408 if (_coefs.get()) {
7409 return xRooNode(RooProduct("tmp", "tmp", RooArgList(*get<RooAbsArg>(), *_coefs.get<RooAbsReal>())))
7410 .fitResult(opt);
7411 }
7412
7413 std::unique_ptr<RooArgList> _pars(dynamic_cast<RooArgList *>(pars().argList().selectByAttrib("Constant", false)));
7414 auto fr = std::make_shared<RooFitResult>(TUUID().AsString());
7415 fr->SetTitle(TString::Format("%s uncorrelated parameter snapshot", GetName()));
7416 fr->setFinalParList(*_pars);
7417 fr->setStatus(-1);
7418
7419 TMatrixDSym cov(fr->floatParsFinal().size());
7420 TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr.get(), _VM));
7421 if (prevCov) {
7422 for (int i = 0; i < prevCov->GetNcols(); i++) {
7423 for (int j = 0; j < prevCov->GetNrows(); j++) {
7424 cov(i, j) = (*prevCov)(i, j);
7425 }
7426 }
7427 }
7428 int i = 0;
7429 for (auto &p : fr->floatParsFinal()) {
7430 if (!prevCov || i >= prevCov->GetNcols()) {
7431 if (auto v = dynamic_cast<RooRealVar *>(p)) {
7432 cov(i, i) = pow(v->getError(), 2);
7433 } else {
7434 cov(i, i) = 0;
7435 }
7436 }
7437 i++;
7438 }
7439 int covQualBackup = fr->covQual();
7440 fr->setCovarianceMatrix(cov);
7441 fr->setCovQual(covQualBackup);
7442
7443 auto _args = consts().argList();
7444 _args.add(pp().argList());
7445 // global obs are added to constPars list too
7446 auto _globs = globs(); // keep alive as may own glob
7447 _args.add(_globs.argList());
7448 fr->setConstParList(_args);
7449 std::unique_ptr<RooArgList> _snap(dynamic_cast<RooArgList *>(_pars->snapshot()));
7450 for (auto &p : *_snap) {
7451 if (auto atr = p->getStringAttribute("initVal"); atr && dynamic_cast<RooRealVar *>(p))
7452 dynamic_cast<RooRealVar *>(p)->setVal(TString(atr).Atof());
7453 }
7454 fr->setInitParList(*_snap);
7455
7456 // return *const_cast<Node2*>(this)->emplace_back(std::make_shared<Node2>(".fitResult",fr,*this));
7457 return xRooNode(fr, *this);
7458}
7459
7460// xRooNode xRooNode::fitTo_(const char* datasetName) const {
7461// try {
7462// return fitTo(datasetName);
7463// } catch(const std::exception& e) {
7464// new TGMsgBox(gClient->GetRoot(), gClient->GetRoot(), "Exception", e.what(),kMBIconExclamation); // deletes
7465// self on dismiss? return xRooNode();
7466// }
7467// }
7468//
7469// xRooNode xRooNode::fitTo(const char* datasetName) const {
7470// return fitTo(*datasets().at(datasetName));
7471// }
7472
7473void xRooNode::SetRange(const char *range, double low, double high)
7474{
7475 if (!std::isnan(low) && !std::isnan(high) && get<RooRealVar>()) {
7476 if (range && strlen(range)) {
7477 get<RooRealVar>()->setRange(range, low, high);
7478 } else {
7479 get<RooRealVar>()->setRange(low, high);
7480 }
7481 return;
7482 }
7483 if (auto o = get<RooAbsArg>(); o)
7484 o->setStringAttribute("range", range);
7485 // todo: clear the range attribute on all servers
7486 // could make this controlled by a flag but probably easiest to enforce so you must set range
7487 // in children after if you wanted to override
7488}
7489const char *xRooNode::GetRange() const
7490{
7491 std::string &out = fRange;
7492 if (auto o = get<RooAbsArg>(); o && o->getStringAttribute("range"))
7493 out = o->getStringAttribute("range");
7494 auto _parent = fParent;
7495 while (out.empty() && _parent) {
7496 if (auto o = _parent->get<RooAbsArg>(); o && o->getStringAttribute("range"))
7497 out = o->getStringAttribute("range");
7498 _parent = _parent->fParent;
7499 }
7500 return out.c_str();
7501}
7502
7503xRooNLLVar xRooNode::nll(const char *_data, std::initializer_list<RooCmdArg> nllOpts) const
7504{
7505 return nll(xRooNode(_data), nllOpts);
7506}
7507
7509{
7510 return nll(_data, *xRooFit::createNLLOptions());
7511}
7512
7513xRooNLLVar xRooNode::nll(const xRooNode &_data, std::initializer_list<RooCmdArg> nllOpts) const
7514{
7515 auto defaultOpts = xRooFit::createNLLOptions(); // smart pointer will cleanup the list
7516 // add user-specified options to list ... if already existing in default list, override and warn
7518 for (auto opt : *defaultOpts) {
7519 l.Add(opt);
7520 }
7521 for (auto &i : nllOpts) {
7522 if (auto o = l.FindObject(i.GetName())) {
7523 Info("nll", "Overriding NLL Option: %s", o->GetName());
7524 l.Remove(o);
7525 }
7526 l.Add(const_cast<RooCmdArg *>(&i));
7527 }
7528
7529 return nll(_data, l);
7530}
7531
7532xRooNode xRooNode::generate(const xRooNode &fr, bool expected, int seed)
7533{
7534 if (auto mc = get<RooStats::ModelConfig>()) {
7535 return xRooNode(*mc->GetPdf(), fParent).generate(fr, expected, seed);
7536 }
7537
7538 if (!get<RooAbsPdf>()) {
7539 // before giving up, if this is a workspace we can proceed if we only have one model
7540 if (get<RooWorkspace>()) {
7541 std::shared_ptr<xRooNode> mainModel;
7542 for (auto &c : const_cast<xRooNode *>(this)->browse()) {
7543 if (c->get<RooAbsPdf>()) {
7544 if (!mainModel) {
7545 mainModel = c;
7546 } else {
7547 throw std::runtime_error(TString::Format("Workspace has multiple models, you must specify which to "
7548 "generate with (found at least %s and %s)",
7549 mainModel->GetName(), c->GetName()));
7550 }
7551 }
7552 }
7553 if (mainModel)
7554 return mainModel->generate(fr, expected, seed);
7555 }
7556 throw std::runtime_error(TString::Format("%s is not a pdf", GetName()));
7557 }
7558
7559 // when generating, will only include channels that are selected
7560 // any unselected but not hidden channel will have data from the only selected dataset added to it
7561 if (get<RooSimultaneous>()) {
7562 std::string selected;
7563 std::string fromds; // list of channels to take from selected ds
7564 bool hasDeselected = false;
7565 for (auto c : bins()) {
7566 TString cName(c->GetName());
7567 cName = cName(cName.Index('=') + 1, cName.Length());
7568 if (!c->get<RooAbsReal>()->isSelectedComp()) {
7569 hasDeselected = true;
7570 if (!c->get<RooAbsArg>()->getAttribute("hidden")) {
7571 if (!fromds.empty())
7572 fromds += ",";
7573 fromds += cName.Data();
7574 }
7575 } else {
7576 if (!selected.empty())
7577 selected += ",";
7578 selected += cName.Data();
7579 }
7580 }
7581 if (hasDeselected) {
7582 std::string dsetName = "";
7583 if (!fromds.empty()) {
7584 // use the first selected dataset as protodata
7585 auto _dsets = datasets();
7586 for (auto &d : _dsets) {
7587 if (d->get()->TestBit(1 << 20)) {
7588 dsetName = d->get()->GetName();
7589 break;
7590 }
7591 }
7592 if (dsetName.empty()) {
7593 throw std::runtime_error(
7594 "Need at least one dataset selected (SetChecked) to use for deselected regions");
7595 }
7596 }
7597 auto result = reduced(selected).generate(fr, expected, seed);
7598 if (!fromds.empty()) {
7599 auto ds = reduced(fromds).datasets()[dsetName];
7600 result.Add(*ds);
7601 result.SetName(TString(result.GetName()) + "_and_" + dsetName.c_str());
7602 }
7603 return result;
7604 }
7605 }
7606
7607 auto _fr = fr.get<RooFitResult>();
7608 xRooNode fr2;
7609 if (!_fr) {
7610 fr2 = fitResult();
7611 _fr = fr2.get<RooFitResult>();
7612 }
7613
7614 // must ensure fr has all the globs in its constPars list ... any missing must be added
7615 // otherwise generateFrom method wont determine globs properly
7616 // same for any missing pars
7617 auto _globs = globs();
7618 bool missingGlobs(false);
7619 for (auto glob : _globs) {
7620 if (!_fr->constPars().find(*glob->get<RooAbsArg>())) {
7621 missingGlobs = true;
7622 break;
7623 }
7624 }
7625
7626 std::unique_ptr<RooFitResult> newFr;
7627 if (missingGlobs) {
7628 newFr = std::make_unique<RooFitResult>(*_fr);
7629 for (auto glob : _globs) {
7630 if (!newFr->constPars().find(*glob->get<RooAbsArg>())) {
7631 const_cast<RooArgList &>(newFr->constPars()).addClone(*glob->get<RooAbsArg>());
7632 }
7633 }
7634 _fr = newFr.get();
7635 }
7636
7637 // check for missing fundamental pars (consts are not fundamentals)
7638 auto _pars = pars();
7639 bool missingPars(false);
7640 for (auto par : _pars) {
7641 if (!par->get<RooAbsArg>()->isFundamental())
7642 continue;
7643 if (!_fr->constPars().find(*par->get<RooAbsArg>()) && !_fr->floatParsFinal().find(*par->get<RooAbsArg>())) {
7644 missingPars = true;
7645 break;
7646 }
7647 }
7648
7649 if (missingPars) {
7650 newFr = std::make_unique<RooFitResult>(*_fr);
7651 for (auto par : _pars) {
7652 if (!par->get<RooAbsArg>()->isFundamental())
7653 continue;
7654 if (!newFr->constPars().find(*par->get<RooAbsArg>()) &&
7655 !newFr->floatParsFinal().find(*par->get<RooAbsArg>())) {
7656 const_cast<RooArgList &>(newFr->constPars()).addClone(*par->get<RooAbsArg>());
7657 }
7658 }
7659 _fr = newFr.get();
7660 }
7661
7662 return xRooNode(xRooFit::generateFrom(*get<RooAbsPdf>(), *_fr, expected, seed).first, *this);
7663
7664 // should add coords to the dataset too?
7665 // e.g. in the case of generating a dataset for a single channel, include the channelCat
7666 // this will allow datasets to then be combined.
7667 // could just say users must use 'reduced' on the simPdf, even if reducing to a single channel
7668}
7669
7670xRooNLLVar xRooNode::nll(const xRooNode &_data, const RooLinkedList &opts) const
7671{
7672 if (auto mc = get<RooStats::ModelConfig>()) {
7673 if (mc->GetExternalConstraints()) {
7675 for (auto o : opts) {
7676 optsWithConstraints.Add(o->Clone(nullptr));
7677 }
7678 optsWithConstraints.Add(RooFit::ExternalConstraints(*mc->GetExternalConstraints()).Clone(nullptr));
7679 return xRooNode(*mc->GetPdf(), fParent).nll(_data, optsWithConstraints);
7680 } else {
7681 return xRooNode(*mc->GetPdf(), fParent).nll(_data, opts);
7682 }
7683 }
7684
7685 if (!get<RooAbsPdf>()) {
7686 // before giving up, if this is a workspace we can proceed if we only have one model or pdf
7687 if (get<RooWorkspace>()) {
7688 std::shared_ptr<xRooNode> mainPdf, mainModel, otherPdf;
7689 for (auto &c : const_cast<xRooNode *>(this)->browse()) {
7690 if (c->get<RooAbsPdf>()) {
7691 if (!mainPdf) {
7692 mainPdf = c;
7693 } else {
7694 otherPdf = c;
7695 }
7696 } else if (c->get<RooStats::ModelConfig>()) {
7697 if (!mainModel) {
7698 mainModel = c;
7699 } else {
7700 throw std::runtime_error(TString::Format("Workspace has multiple models, you must specify which to "
7701 "build nll with (found at least %s and %s)",
7702 mainModel->GetName(), c->GetName()));
7703 }
7704 }
7705 }
7706 if (mainModel)
7707 return mainModel->nll(_data, opts);
7708 if (mainPdf) {
7709 if (otherPdf) {
7710 throw std::runtime_error(TString::Format("Workspace has multiple pdfs, you must specify which to "
7711 "build nll with (found at least %s and %s)",
7712 mainPdf->GetName(), otherPdf->GetName()));
7713 }
7714 return mainPdf->nll(_data, opts);
7715 }
7716 }
7717 throw std::runtime_error(TString::Format("%s is not a pdf", GetName()));
7718 }
7719
7720 // if simultaneous and any channels deselected then reduce and return
7721 if (get<RooSimultaneous>()) {
7722 std::string selected;
7723 bool hasDeselected = false;
7724 for (auto c : bins()) {
7725 if (!c->get<RooAbsReal>()->isSelectedComp()) {
7726 hasDeselected = true;
7727 } else {
7728 TString cName(c->GetName());
7729 cName = cName(cName.Index('=') + 1, cName.Length());
7730 if (!selected.empty())
7731 selected += ",";
7732 selected += cName.Data();
7733 }
7734 }
7735 if (hasDeselected)
7736 return reduced(selected).nll(_data, opts);
7737 }
7738
7739 if (!_data.get<RooAbsData>()) {
7740 // use node name to find dataset and recall
7741 auto _d = strlen(_data.GetName()) ? datasets().find(_data.GetName()) : nullptr;
7742 if (strlen(_data.GetName()) == 0) {
7743 // create the EXPECTED (asimov) dataset with the observables
7745 _d = std::make_shared<xRooNode>(asi.first, *this);
7746 if (asi.second) {
7747 _d->emplace_back(
7748 std::make_shared<xRooNode>(".globs", std::const_pointer_cast<RooAbsCollection>(asi.second), *_d));
7749 }
7750 } else if (!_d) {
7751 throw std::runtime_error(TString::Format("Cannot find dataset %s", _data.GetName()));
7752 }
7753 return nll(*_d, opts);
7754 } else if (!_data.fParent || _data.fParent->fComp != fComp) {
7755 // dataset is not parented by this node ... meaning it may need to be reduced,
7756 // do this via the datasets() method by attaching and detaching the dataset
7757 xRooNode me(*this); // since we are in a const method, need to create a copy node.
7758 me.push_back(std::make_shared<xRooNode>(_data));
7759 return nll(*me.datasets().at(_data.GetName()), opts);
7760 }
7761
7762 auto _globs = _data.globs(); // keep alive because may own the globs
7763
7764 auto _opts = std::shared_ptr<RooLinkedList>(new RooLinkedList, [](RooLinkedList *l) {
7765 if (l)
7766 l->Delete();
7767 delete l;
7768 });
7769 RooArgSet _globsSet(_globs.argList());
7771 if (GetRange() && strlen(GetRange()))
7772 _opts->Add(RooFit::Range(GetRange()).Clone());
7773
7774 // copy over opts ... need to clone each so can safely delete when _opts destroyed
7775 for (int i = 0; i < opts.GetSize(); i++) {
7776 if (strlen(opts.At(i)->GetName()) == 0)
7777 continue; // skipping "none" cmds
7778 if (strcmp(opts.At(i)->GetName(), "GlobalObservables") == 0) {
7779 // maybe warn here?
7780 } else {
7781 _opts->Add(opts.At(i)->Clone(nullptr)); // nullptr needed because accessing Clone via TObject base class puts
7782 // "" instead, so doesnt copy names
7783 }
7784 }
7785
7786 // use shared_ptr method so NLLVar will take ownership of datasets etc if created above
7787 // snapshots the globs out of the nllOpts (see specific constructor of xRooNLLVar)
7788 auto out = xRooFit::createNLL(std::dynamic_pointer_cast<RooAbsPdf>(fComp),
7789 std::dynamic_pointer_cast<RooAbsData>(_data.fComp), *_opts);
7790 return out;
7791}
7792
7793// xRooNode xRooNode::fitTo(const xRooNode& _data) const {
7794//
7795//
7796// auto _pdf = get<RooAbsPdf>();
7797// if (!_pdf) throw std::runtime_error("Not a pdf");
7798//
7799// auto _globs = _data.globs(); // keep alive because may own the globs
7800// RooArgSet globsSet(_globs.argList());
7801//
7802// std::shared_ptr<RooSimultaneous> newPdf;
7803// if(auto s = get<RooSimultaneous>(); s) {
7804// auto rangeName = GetRange();
7805// if (rangeName) {
7806// // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
7807// std::vector<TString> chanPatterns;
7808// TStringToken pattern(rangeName, ",");
7809// while (pattern.NextToken()) {
7810// chanPatterns.emplace_back(pattern);
7811// }
7812// auto& _cat = const_cast<RooAbsCategoryLValue&>(s->indexCat());
7813// newPdf = std::make_shared<RooSimultaneous>(TString::Format("%s_reduced",GetName()),"Reduced model",_cat);
7814// for(auto& c : variations()) {
7815// TString cName(c->GetName());
7816// cName = cName(cName.Index('=')+1,cName.Length());
7817// _cat.setLabel(cName);
7818// bool matchAny=false;
7819// for(auto& p : chanPatterns) {
7820// if (cName.Contains(TRegexp(p,true))) { matchAny=true; break; }
7821// if (_cat.hasRange(p) && _cat.inRange(p)) { matchAny=true; break; }
7822// }
7823// if(matchAny) {
7824// newPdf->addPdf( *c->get<RooAbsPdf>(), cName );
7825// }
7826// }
7827// RooFitResultTree t(newPdf->GetName(),"",*newPdf);
7828// auto _fr = std::const_pointer_cast<RooFitResult>(t.fitTo(_data.get<RooAbsData>(), &globsSet));
7829// xRooNode parent(_data.GetName(),nullptr,*this);
7830// xRooNode out(_fr->GetName(),/*acquire(_fr)*/ _fr,parent);
7831// // do full propagation by 'checking' the fr ...
7832// out.Checked(&out,true);
7833// return out;
7834// }
7835// }
7836//
7837//
7838//
7839// std::string treeName = TString::Format("fits_%s",GetName()).Data();
7840//
7841// auto _frt = getObject<TTree>(treeName); // get existing frt
7842//
7843// std::shared_ptr<RooFitResultTree> t;
7844// if (_frt) {
7845// t = std::make_shared<RooFitResultTree>(_frt.get());
7846// } else {
7847// t = std::make_shared<RooFitResultTree>(treeName.c_str(),"",*_pdf);
7848// }
7849// //t->SetProgress(true);
7850// auto _fr = std::const_pointer_cast<RooFitResult>(t->fitTo(_data.get<RooAbsData>(), &globsSet));
7851//
7852//
7853//
7854// /*
7855// obs().argList() = s; // sets global observables to their values
7856// auto _fr =
7857// std::shared_ptr<RooFitResult>(_pdf->fitTo(*_data->get<RooAbsData>(),RooFit::GlobalObservables(s),RooFit::Offset(true),RooFit::Save()));
7858// _fr->SetName(TUUID().AsString());
7859// // restore parameters before returning
7860// *std::unique_ptr<RooArgSet>(_pdf->getDependents(_fr->floatParsFinal())) = _fr->floatParsInit();
7861// */
7862//
7863// //_fr->SetTitle(TString::Format("%s;%s",GetName(),datasetName));
7864// if (!_frt) {
7865// t =
7866// std::make_shared<RooFitResultTree>(std::dynamic_pointer_cast<TTree>(const_cast<xRooNode*>(this)->acquire(t->fTree)).get());
7867// }
7868// xRooNode parent(_data.GetName(),nullptr,xRooNode(t,*this));
7869// xRooNode out(_fr->GetName(),/*acquire(_fr)*/ _fr,parent);
7870// // do full propagation by 'checking' the fr ...
7871// out.Checked(&out,true);
7872// return out;
7873// }
7874
7875std::shared_ptr<xRooNode> xRooNode::parentPdf() const
7876{
7877 // find first parent that is a pdf
7878 auto out = fParent;
7879 while (out && !out->get<RooAbsPdf>()) {
7880 out = out->fParent;
7881 }
7882 return out;
7883}
7884
7885xRooNode xRooNode::reduced(const std::string &_range, bool invert) const
7886{
7887 auto rangeName = (_range.empty()) ? GetRange() : _range;
7888 if (!rangeName.empty()) {
7889 std::vector<TString> patterns;
7890 TStringToken pattern(rangeName, ",");
7891 while (pattern.NextToken()) {
7892 patterns.emplace_back(pattern);
7893 }
7894 if (auto s = get<RooSimultaneous>(); s) {
7895 // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
7896 auto &_cat = const_cast<RooAbsCategoryLValue &>(s->indexCat());
7897 auto newPdf =
7898 std::make_shared<RooSimultaneous>(TString::Format("%s_reduced", GetName()), "Reduced model", _cat);
7899 for (auto &c : bins()) {
7900 TString cName(c->GetName());
7901 cName = cName(cName.Index('=') + 1, cName.Length());
7902 _cat.setLabel(cName);
7903 bool matchAny = false;
7904 for (auto &p : patterns) {
7905 if (cName.Contains(TRegexp(p, true))) {
7906 matchAny = true;
7907 break;
7908 }
7909 if (_cat.hasRange(p) && _cat.inRange(p)) {
7910 matchAny = true;
7911 break;
7912 }
7913 }
7914 if ((matchAny && !invert) || (!matchAny && invert)) {
7915 newPdf->addPdf(*c->get<RooAbsPdf>(), cName);
7916 }
7917 }
7918 return xRooNode(newPdf, fParent);
7919 } else if (get() && !get<RooAbsCollection>() && !components().empty()) {
7920 // create a new obj and remove non-matching components
7921 xRooNode out(std::shared_ptr<TObject>(get()->Clone(TString::Format("%s_reduced", get()->GetName()))), fParent);
7922 // go through components and remove any that don't match pattern
7923 std::vector<TObject *> funcs; // to be removed
7924 for (auto &c : out.components()) {
7925 bool matchAny = false;
7926 for (auto &p : patterns) {
7927 if (TString(c->GetName()).Contains(TRegexp(p, true))) {
7928 matchAny = true;
7929 break;
7930 }
7931 }
7932 if (!((matchAny && !invert) || (!matchAny && invert)))
7933 funcs.push_back(c->get());
7934 }
7935 for (auto &c : funcs)
7936 out.Remove(*c);
7937 if (!funcs.empty()) {
7938 if (auto _pdf = out.get<RooRealSumPdf>(); _pdf) {
7939 _pdf->setFloor(false); // remove floor if removed some functions, which allows evaluation of negative
7940 // valued components
7941 }
7942 }
7943 out.browse();
7944 return out;
7945 } else if (auto fr = get<RooFitResult>()) {
7946 // reduce the fit result by moving unselected float pars into the constPars list and dropping their covariances
7947 xRooNode out(std::shared_ptr<TObject>(fr->Clone(TString::Format("%s_reduced", fr->GetName()))), fParent);
7948 fr = out.get<RooFitResult>();
7949 RooArgList _pars = fr->floatParsFinal();
7951 for (auto c : _pars) {
7952 bool matchAny = false;
7953 for (auto &p : patterns) {
7954 if (TString(c->GetName()).Contains(TRegexp(p, true))) {
7955 matchAny = true;
7956 break;
7957 }
7958 }
7959 if (!((matchAny && !invert) || (!matchAny && invert))) {
7960 _remPars.add(*c);
7961 }
7962 }
7963 _pars.remove(_remPars, true);
7964
7965 auto _tmp = fr->reducedCovarianceMatrix(_pars);
7966 int covQualBackup = fr->covQual();
7967 fr->setCovarianceMatrix(_tmp);
7968 fr->setCovQual(covQualBackup);
7969 const_cast<RooArgList &>(fr->floatParsFinal())
7970 .remove(_remPars, true); // is this a memory leak ... should delete the remPars?
7971 return out;
7972
7973 } else if (!get() || get<RooAbsCollection>()) {
7974 // filter the children .... handle special case of filtering ".vars" with "x" option too
7975 xRooNode out(std::make_shared<RooArgList>(), fParent);
7976 out.SetName(TString(GetName()) + "_reduced");
7977 size_t nobs = 0;
7978 bool notAllArgs = false;
7979 bool isVars = (strcmp(GetName(), ".vars") == 0);
7980 for (auto c : *this) {
7981 nobs += (c->fFolder == "!robs" || c->fFolder == "!globs");
7982 bool matchAny = false;
7983 for (auto &p : patterns) {
7984 if (TString(c->GetName()).Contains(TRegexp(p, true)) ||
7985 (isVars && p == "x" && (c->fFolder == "!robs" || c->fFolder == "!globs") && nobs == 1)) {
7986 matchAny = true;
7987 break;
7988 }
7989 }
7990 if ((matchAny && !invert) || (!matchAny && invert)) {
7991 out.push_back(c);
7992 if (auto a = c->get<RooAbsArg>()) {
7993 out.get<RooArgList>()->add(*a);
7994 } else {
7995 notAllArgs = true;
7996 }
7997 }
7998 }
7999 if (notAllArgs) {
8000 out.fComp.reset();
8001 }
8002 return out;
8003 }
8004 }
8005
8006 return get<RooArgList>() ? xRooNode(std::make_shared<RooArgList>(), fParent) : *this;
8007}
8008
8009// xRooNode xRooNode::generate(bool expected) const {
8010//
8011// auto fr = fitResult();
8012// auto _fr = fr.get<RooFitResult>();
8013//
8014// auto _pdf = (get<RooAbsPdf>()) ? std::shared_ptr<const xRooNode>(this, [](const xRooNode*){}) : parentPdf();
8015// if (!_pdf) {
8016// throw std::runtime_error("Could not find pdf");
8017// }
8018//
8019// std::shared_ptr<RooDataTree> t;
8020//
8021// std::shared_ptr<RooSimultaneous> newPdf;
8022// if(auto s = _pdf->get<RooSimultaneous>(); s) {
8023// auto rangeName = GetRange();
8024// if (rangeName) {
8025// // need to reduce the RooSimultaneous until fix: https://github.com/root-project/root/issues/8231
8026// std::vector<TString> chanPatterns;
8027// TStringToken pattern(rangeName, ",");
8028// while (pattern.NextToken()) {
8029// chanPatterns.emplace_back(pattern);
8030// }
8031// auto& _cat = const_cast<RooAbsCategoryLValue&>(s->indexCat());
8032// newPdf = std::make_shared<RooSimultaneous>(TString::Format("%s_reduced",GetName()),"Reduced model",_cat);
8033// for(auto& c : _pdf->variations()) {
8034// TString cName(c->GetName());
8035// cName = cName(cName.Index('=')+1,cName.Length());
8036// _cat.setLabel(cName);
8037// bool matchAny=false;
8038// for(auto& p : chanPatterns) {
8039// if (cName.Contains(TRegexp(p,true))) { matchAny=true; break; }
8040// if (_cat.hasRange(p) && _cat.inRange(p)) { matchAny=true; break; }
8041// }
8042// if(matchAny) {
8043// newPdf->addPdf( *c->get<RooAbsPdf>(), cName );
8044// }
8045// }
8046// t = std::make_shared<RooDataTree>(newPdf->GetName(),"",*newPdf);
8047// RooArgSet s1(_pdf->obs().argList());
8048// RooArgSet s2(_pdf->globs().argList());s1.remove(s2);
8049// t->SetObservables(&s1,&s2);
8050// auto _data = t->generate(_fr,expected);
8051//
8052// xRooNode parent(_fr ? _fr->GetName() : "unknown",nullptr,xRooNode(t,*this));
8053// xRooNode out(_data.first->GetName(),/*acquire(_fr)*/ _data.first,parent);
8054// out.emplace_back(std::make_shared<xRooNode>(".globs",std::const_pointer_cast<RooArgSet>(_data.second),out));
8055// return out;
8056// }
8057// }
8058//
8059//
8060// std::string treeName = TString::Format("gen_%s",_pdf->GetName()).Data();
8061//
8062// auto _frt = getObject<TTree>(treeName); // get existing frt
8063//
8064//
8065// if (_frt) {
8066// t = std::make_shared<RooDataTree>(_frt.get());
8067// } else {
8068// t = std::make_shared<RooDataTree>(treeName.c_str(),"",*_pdf->get<RooAbsPdf>());
8069// RooArgSet s1(_pdf->obs().argList());
8070// RooArgSet s2(_pdf->globs().argList());s1.remove(s2);
8071// t->SetObservables(&s1,&s2);
8072// }
8073// auto _data = t->generate(_fr,expected);
8074// if (!_frt) {
8075// t =
8076// std::make_shared<RooDataTree>(std::dynamic_pointer_cast<TTree>(const_cast<xRooNode*>(this)->acquire(t->fTree)).get());
8077// }
8078// xRooNode parent(_fr ? _fr->GetName() : "unknown",nullptr,xRooNode(t,*this));
8079// xRooNode out(_data.first->GetName(),/*acquire(_fr)*/ _data.first,parent);
8080// out.emplace_back(std::make_shared<xRooNode>(".globs",std::const_pointer_cast<RooArgSet>(_data.second),out));
8081// return out;
8082// }
8083
8085public:
8087 double expectedEvents(const RooArgSet *nset) const override
8088 {
8089 return static_cast<RooAbsPdf *>(intpdf.absArg())->expectedEvents(nset);
8090 }
8091 ExtendMode extendMode() const override { return static_cast<RooAbsPdf *>(intpdf.absArg())->extendMode(); }
8092 TObject *clone(const char *newname) const override { return new xRooProjectedPdf(*this, newname); }
8093
8094protected:
8095 double evaluate() const override
8096 {
8097 int code;
8098 return getProjection(&intobs, _normSet, (_normRange.Length() > 0 ? _normRange.Data() : nullptr), code)->getVal();
8099 }
8100};
8101
8102double new_getPropagatedError(const RooAbsReal &f, const RooFitResult &fr, const RooArgSet &nset = {},
8103 RooArgList **pars = nullptr, bool asymHi = false, bool asymLo = false)
8104{
8105 // Calling getParameters() might be costly, but necessary to get the right
8106 // parameters in the RooAbsReal. The RooFitResult only stores snapshots.
8107
8108 // handle simple case that function is a RooRealVar
8109 if (auto rrv = dynamic_cast<const RooRealVar *>(&f); rrv) {
8110 if (auto frrrv = dynamic_cast<RooRealVar *>(fr.floatParsFinal().find(*rrv)); frrrv) {
8111 rrv = frrrv; // use value from fit result
8112 }
8113 if (asymHi) {
8114 return rrv->getErrorHi();
8115 } else if (asymLo) {
8116 return rrv->getErrorLo();
8117 } else {
8118 return rrv->getError();
8119 }
8120 }
8121
8122 RooArgList *_pars = (pars) ? *pars : nullptr;
8123
8124 if (!_pars) {
8125
8127 f.getParameters(&nset, allParamsInAbsReal);
8128
8129 _pars = new RooArgList;
8130 for (auto *rrvFitRes : static_range_cast<RooRealVar *>(fr.floatParsFinal())) {
8131
8132 auto rrvInAbsReal = static_cast<RooRealVar const *>(allParamsInAbsReal.find(*rrvFitRes));
8133
8134 // Strip out parameters with zero error
8135 if (rrvFitRes->getError() <= std::abs(rrvFitRes->getVal()) * std::numeric_limits<double>::epsilon())
8136 continue;
8137
8138 // Ignore parameters in the fit result that this RooAbsReal doesn't depend on
8139 if (!rrvInAbsReal)
8140 continue;
8141
8142 // Checking for float equality is a bad. We check if the values are
8143 // negligibly far away from each other, relative to the uncertainty.
8144 if (std::abs(rrvInAbsReal->getVal() - rrvFitRes->getVal()) > 0.01 * rrvFitRes->getError()) {
8145 std::stringstream errMsg;
8146 errMsg << "RooAbsReal::getPropagatedError(): the parameters of the RooAbsReal don't have"
8147 << " the same values as in the fit result! The logic of getPropagatedError is broken in this case."
8148 << " \n " << rrvInAbsReal->GetName() << " : " << rrvInAbsReal->getVal() << " vs "
8149 << rrvFitRes->getVal();
8150
8151 throw std::runtime_error(errMsg.str());
8152 }
8153
8154 _pars->add(*rrvInAbsReal);
8155 }
8156 }
8157
8158 // Make std::vector of variations
8159 TVectorD F(_pars->size());
8160
8161 // Create std::vector of plus,minus variations for each parameter
8162 TMatrixDSym V(_pars->size() == fr.floatParsFinal().size() ? fr.covarianceMatrix()
8163 : fr.reducedCovarianceMatrix(*_pars));
8164
8165 // TODO: if _pars includes pars not in fr, need to extend matrix with uncorrelated errors of those pars
8166
8167 double nomVal = f.getVal(nset);
8168
8169 for (std::size_t ivar = 0; ivar < _pars->size(); ivar++) {
8170
8171 auto &rrv = static_cast<RooRealVar &>((*_pars)[ivar]);
8172 auto *frrrv = static_cast<RooRealVar *>(fr.floatParsFinal().find(rrv));
8173
8174 double cenVal = rrv.getVal();
8175 double plusVar, minusVar, errVal;
8176
8177 if (asymHi || asymLo) {
8178 errVal = frrrv->getErrorHi();
8179 rrv.setVal(cenVal + errVal);
8180 plusVar = f.getVal(nset);
8181 errVal = frrrv->getErrorLo();
8182 rrv.setVal(cenVal + errVal);
8183 minusVar = f.getVal(nset);
8184 if (asymHi) {
8185 // pick the one that moved result 'up' most
8186 plusVar = std::max(plusVar, minusVar);
8187 minusVar = 2 * nomVal - plusVar; // symmetrizes
8188 } else {
8189 // pick the one that moved result 'down' most
8190 minusVar = std::min(plusVar, minusVar);
8191 plusVar = 2 * nomVal - minusVar; // symmetrizes
8192 }
8193 } else {
8194 errVal = sqrt(V(ivar, ivar));
8195 // Make Plus variation
8196 rrv.setVal(cenVal + errVal);
8197 plusVar = f.getVal(nset);
8198 // Make Minus variation
8199 rrv.setVal(cenVal - errVal);
8200 minusVar = f.getVal(nset);
8201 }
8202 F[ivar] = (plusVar - minusVar) * 0.5;
8203 rrv.setVal(cenVal);
8204 }
8205
8206 // Re-evaluate this RooAbsReal with the central parameters just to be
8207 // extra-safe that a call to `getPropagatedError()` doesn't change any state.
8208 // It should not be necessary because thanks to the dirty flag propagation
8209 // the RooAbsReal is re-evaluated anyway the next time getVal() is called.
8210 // Still there are imaginable corner cases where it would not be triggered,
8211 // for example if the user changes the RooFit operation more after the error
8212 // propagation.
8213 f.getVal(nset);
8214
8215 TMatrixDSym C(_pars->size());
8216 std::vector<double> errVec(_pars->size());
8217 for (std::size_t i = 0; i < _pars->size(); i++) {
8218 errVec[i] = std::sqrt(V(i, i));
8219 for (std::size_t j = i; j < _pars->size(); j++) {
8220 C(i, j) = V(i, j) / std::sqrt(V(i, i) * V(j, j));
8221 C(j, i) = C(i, j);
8222 }
8223 }
8224
8225 // Calculate error in linear approximation from variations and correlation coefficient
8226 double sum = F * (C * F);
8227
8228 if (!pars) {
8229 delete _pars;
8230 } else {
8231 *pars = _pars;
8232 }
8233
8234 return sqrt(sum);
8235}
8236
8237class PdfWrapper : public RooAbsPdf {
8238public:
8239 // need expPdf option while RooProjectedPdf doesn't support keeping things extended
8240 PdfWrapper(RooAbsReal &f, RooAbsReal *coef, bool expEvMode = false, RooAbsPdf *expPdf = nullptr)
8241 : RooAbsPdf(Form("exp_%s", f.GetName())),
8242 fFunc("func", "func", this, f),
8243 fCoef("coef", "coef", this),
8244 fExpPdf("expPdf", "expPdf", this)
8245 {
8246 // don't treat pdf as extended if it has a coefficient and is RooAddPdf: RooAddPdf doesn't extend them unless no
8247 // coefs for any (and all are extendable)
8248 if (coef) {
8249 fCoef.setArg(*coef);
8250 }
8251 if (expPdf && expPdf->canBeExtended() && !(coef && dynamic_cast<RooAddPdf *>(expPdf))) {
8252 fExpPdf.setArg(*expPdf);
8253 } else if (auto _p = dynamic_cast<RooAbsPdf *>(&f);
8254 _p && _p->canBeExtended() && !(coef && dynamic_cast<RooAddPdf *>(_p))) {
8255 fExpPdf.setArg(f); // using self for expectation
8256 }
8257 fExpectedEventsMode = expEvMode;
8258 }
8259 ~PdfWrapper() override{};
8260 PdfWrapper(const PdfWrapper &other, const char *name = nullptr)
8261 : RooAbsPdf(other, name),
8262 fFunc("func", this, other.fFunc),
8263 fCoef("coef", this, other.fCoef),
8264 fExpPdf("expPdf", this, other.fExpPdf),
8265 fExpectedEventsMode(other.fExpectedEventsMode)
8266 {
8267 }
8268 TObject *clone(const char *newname) const override { return new PdfWrapper(*this, newname); }
8269 bool isBinnedDistribution(const RooArgSet &obs) const override { return fFunc->isBinnedDistribution(obs); }
8270 std::list<double> *binBoundaries(RooAbsRealLValue &obs, double xlo, double xhi) const override
8271 {
8272 return fFunc->binBoundaries(obs, xlo, xhi);
8273 }
8274
8275 double evaluate() const override
8276 {
8277 return (fExpectedEventsMode ? 1. : fFunc) *
8278 ((fExpPdf.absArg()) ? static_cast<RooAbsPdf *>(fExpPdf.absArg())->expectedEvents(_normSet) : 1.) *
8279 (fCoef.absArg() ? fCoef : 1.);
8280 }
8281
8282 bool selfNormalized() const override
8283 {
8284 return true;
8285 } // so that doesn't try to do an integral because we are passing integration onto fFunc in evaluate
8286
8287 // faster than full evaluation because doesnt make the integral dependent on the full expression
8289 {
8290#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 28, 00)
8291 double oo = getPropagatedError(fr, nset_in); // method was improved in 6.28 so use this instead
8292 if (std::isnan(oo)) {
8293 // may be consequence of zero uncerts
8294 // Calling getParameters() might be costly, but necessary to get the right
8295 // parameters in the RooAbsReal. The RooFitResult only stores snapshots.
8297 getParameters(&nset_in, allParamsInAbsReal);
8298
8299 RooArgList paramList;
8301
8302 auto rrvInAbsReal = static_cast<RooRealVar const *>(allParamsInAbsReal.find(*rrvFitRes));
8303
8304 // If this RooAbsReal is a RooRealVar in the fit result, we don't need to
8305 // propagate anything and can just return the error in the fit result
8306 if (rrvFitRes->namePtr() == namePtr())
8307 return rrvFitRes->getError();
8308
8309 // Strip out parameters with zero error
8310 if (!rrvFitRes->hasError() ||
8311 rrvFitRes->getError() <= std::abs(rrvFitRes->getVal()) * std::numeric_limits<double>::epsilon())
8312 continue;
8313
8314 // Ignore parameters in the fit result that this RooAbsReal doesn't depend on
8315 if (!rrvInAbsReal)
8316 continue;
8317
8318 // Checking for float equality is a bad. We check if the values are
8319 // negligibly far away from each other, relative to the uncertainty.
8320 if (std::abs(rrvInAbsReal->getVal() - rrvFitRes->getVal()) > 0.01 * rrvFitRes->getError()) {
8321 std::stringstream errMsg;
8322 errMsg
8323 << "RooAbsReal::getPropagatedError(): the parameters of the RooAbsReal don't have"
8324 << " the same values as in the fit result! The logic of getPropagatedError is broken in this case.";
8325
8326 throw std::runtime_error(errMsg.str());
8327 }
8328
8329 paramList.add(*rrvInAbsReal);
8330 }
8331 if (paramList.empty())
8332 return 0.;
8333
8334 std::vector<double> plusVar;
8335 std::vector<double> minusVar;
8336 plusVar.reserve(paramList.size());
8337 minusVar.reserve(paramList.size());
8338
8339 // Create std::vector of plus,minus variations for each parameter
8340 TMatrixDSym V(paramList.size() == fr.floatParsFinal().size() ? fr.covarianceMatrix()
8341 : fr.reducedCovarianceMatrix(paramList));
8342
8343 for (std::size_t ivar = 0; ivar < paramList.size(); ivar++) {
8344
8345 auto &rrv = static_cast<RooRealVar &>(paramList[ivar]);
8346
8347 double cenVal = rrv.getVal();
8348 double errVal = sqrt(V(ivar, ivar));
8349
8350 // this next thing happens if the par has errors but the covariance matrix is empty
8351 // this only happens if the fit was dodgy, so perhaps best to not even try to recover from this
8352 // screwup ... hence I've commented out this fixup here and will let the errors be nan
8353 // if(errVal==0) {
8354 // Warning("getPropagatedError","Missing variance for %s",rrv.GetName());
8355 // errVal = rrv.getError();
8356 // V(ivar,ivar) = errVal*errVal;
8357 // }
8358
8359 // Make Plus variation
8360 rrv.setVal(cenVal + errVal);
8361 plusVar.push_back(getVal(nset_in));
8362
8363 // Make Minus variation
8364 rrv.setVal(cenVal - errVal);
8365 minusVar.push_back(getVal(nset_in));
8366#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
8367 // can try to recover nans ... this stopped being possible in 6.27 onwards because NaNPacker made private
8368 if (std::isnan(plusVar.back()) && RooNaNPacker::isNaNWithPayload(plusVar.back())) {
8369 plusVar.back() = -RooNaNPacker::unpackNaN(plusVar.back());
8370 }
8371 if (std::isnan(minusVar.back()) && RooNaNPacker::isNaNWithPayload(minusVar.back())) {
8372 minusVar.back() = -RooNaNPacker::unpackNaN(minusVar.back());
8373 }
8374#endif
8375 // std::cout << plusVar.back() << " and " << minusVar.back() << std::endl;
8376
8377 rrv.setVal(cenVal);
8378 }
8379
8380 // Re-evaluate this RooAbsReal with the central parameters just to be
8381 // extra-safe that a call to `getPropagatedError()` doesn't change any state.
8382 // It should not be necessary because thanks to the dirty flag propagation
8383 // the RooAbsReal is re-evaluated anyway the next time getVal() is called.
8384 // Still there are imaginable corner cases where it would not be triggered,
8385 // for example if the user changes the RooFit operation more after the error
8386 // propagation.
8387 getVal(nset_in);
8388
8389 TMatrixDSym C(paramList.size());
8390 std::vector<double> errVec(paramList.size());
8391 for (std::size_t i = 0; i < paramList.size(); i++) {
8392 errVec[i] = std::sqrt(V(i, i));
8393 for (std::size_t j = i; j < paramList.size(); j++) {
8394 C(i, j) = V(i, j) / std::sqrt(V(i, i) * V(j, j));
8395 C(j, i) = C(i, j);
8396 }
8397 }
8398
8399 // Make std::vector of variations
8400 TVectorD F(plusVar.size());
8401 for (unsigned int j = 0; j < plusVar.size(); j++) {
8402 F[j] = (plusVar[j] - minusVar[j]) / 2;
8403 }
8404
8405 // Calculate error in linear approximation from variations and correlation coefficient
8406 double sum = F * (C * F);
8407
8408 return sqrt(sum);
8409 }
8410 return oo;
8411#else
8412
8413 // Strip out parameters with zero error
8416 if (frv->getError() > 1e-20) {
8417 fpf_stripped.add(*frv);
8418 }
8419 }
8420
8421 // Clone self for internal use
8422 RooAbsReal *cloneFunc = const_cast<PdfWrapper *>(this); // (RooAbsReal *)fFunc.absArg()->cloneTree();
8423 // RooAbsPdf *clonePdf = dynamic_cast<RooAbsPdf *>(cloneFunc);
8424 RooArgSet *errorParams = cloneFunc->getObservables(fpf_stripped);
8425
8426 RooArgSet *nset =
8427 nset_in.size() == 0 ? cloneFunc->getParameters(*errorParams) : cloneFunc->getObservables(nset_in);
8428
8429 // Make list of parameter instances of cloneFunc in order of error matrix
8430 RooArgList paramList;
8431 const RooArgList &fpf = fpf_stripped;
8432 std::vector<int> fpf_idx;
8433 for (Int_t i = 0; i < fpf.size(); i++) {
8434 RooAbsArg *par = errorParams->find(fpf[i].GetName());
8435 if (par) {
8436 paramList.add(*par);
8437 fpf_idx.push_back(i);
8438 }
8439 }
8440
8441 std::vector<double> plusVar, minusVar;
8442
8443 // Create vector of plus,minus variations for each parameter
8444 TMatrixDSym V(paramList.size() == fr.floatParsFinal().size() ? fr.covarianceMatrix()
8445 : fr.reducedCovarianceMatrix(paramList));
8446
8447 for (Int_t ivar = 0; ivar < paramList.size(); ivar++) {
8448
8450
8451 double cenVal = rrv.getVal();
8452 double errVal = sqrt(V(ivar, ivar));
8453
8454 // Make Plus variation
8455 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal + errVal);
8456 // plusVar.push_back((fExpectedEventsMode ? 1. : cloneFunc->getVal(nset)) *
8457 // (clonePdf ? clonePdf->expectedEvents(nset) : 1.));
8458 plusVar.push_back(cloneFunc->getVal(nset));
8459
8460 // Make Minus variation
8461 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal - errVal);
8462 // minusVar.push_back((fExpectedEventsMode ? 1. : cloneFunc->getVal(nset)) *
8463 // (clonePdf ? clonePdf->expectedEvents(nset) : 1.));
8464 minusVar.push_back(cloneFunc->getVal(nset));
8465
8466 ((RooRealVar *)paramList.at(ivar))->setVal(cenVal);
8467 }
8468
8469 getVal(nset); // reset state
8470
8471 TMatrixDSym C(paramList.size());
8472 std::vector<double> errVec(paramList.size());
8473 for (int i = 0; i < paramList.size(); i++) {
8474 errVec[i] = sqrt(V(i, i));
8475 for (int j = i; j < paramList.size(); j++) {
8476 C(i, j) = V(i, j) / sqrt(V(i, i) * V(j, j));
8477 C(j, i) = C(i, j);
8478 }
8479 }
8480
8481 // Make vector of variations
8482 TVectorD F(plusVar.size());
8483 for (unsigned int j = 0; j < plusVar.size(); j++) {
8484 F[j] = (plusVar[j] - minusVar[j]) / 2;
8485 }
8486
8487 // Calculate error in linear approximation from variations and correlation coefficient
8488 double sum = F * (C * F);
8489
8490 // delete cloneFunc;
8491 delete errorParams;
8492 delete nset;
8493
8494 return sqrt(sum);
8495#endif
8496 }
8497
8498private:
8502 bool fExpectedEventsMode = false;
8503};
8504
8505const xRooNode *runningNode = nullptr;
8507
8509{
8510 std::cout << "Got signal " << signum << std::endl;
8511 if (signum == SIGINT) {
8512 std::cout << "Keyboard interrupt while building histogram" << std::endl;
8513 // TODO: create a global mutex for this
8514 runningNode->fInterrupted = true;
8515 } else {
8517 }
8518}
8519
8521{
8522 auto _doSterilize = [](RooAbsArg *obj) {
8523 if (!obj)
8524 return;
8525 for (int i = 0; i < obj->numCaches(); i++) {
8526 if (auto cache = dynamic_cast<RooObjCacheManager *>(obj->getCache(i))) {
8527 cache->reset();
8528 }
8529 }
8530 if (RooAbsPdf *p = dynamic_cast<RooAbsPdf *>(obj); p) {
8531 p->setNormRange(p->normRange());
8532 }
8533#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
8534 if (RooAbsReal *p = dynamic_cast<RooAbsReal *>(obj); p) {
8535 // need to forget about any normSet that was passed to getVal(...)
8536 // doesn't seem necessary in 6.28
8537
8538 p->setProxyNormSet(nullptr);
8539 p->_lastNSet = nullptr;
8540 }
8541#endif
8542 obj->setValueDirty();
8543 };
8544 if (auto w = get<RooWorkspace>(); w) {
8545 // sterilizing all nodes
8546 for (auto &c : w->components()) {
8547 _doSterilize(c);
8548 }
8549 return;
8550 }
8551 // recursive through all clients and sterlize their normalization caches
8552 std::function<void(RooAbsArg *)> func;
8553 func = [&](RooAbsArg *a) {
8554 if (!a) {
8555 return;
8556 }
8557 _doSterilize(a); // sterilize first so that cache elements don't appear in the client list
8558 // safety net in case sterilizing one client deletes another one of our clients
8559 // monitor for change in clients list size
8560 // found this was only case in 6.26 (valgrind shows invalid read), in 6.28 these went away
8561 // might be in 6.28 the client list iterator became able to handle in-loop edits but didn't see
8562 // in test case that client count changed so just resterilizing if that's the case.
8563 size_t nClients;
8564 do {
8565 nClients = a->clients().size();
8566 for (auto obj : a->clients()) {
8567 func(dynamic_cast<RooAbsArg *>(obj));
8568 if (a->clients().size() != nClients) {
8569 break; // means sterilizing a client changed our clients, so don't trust the client iterator at this
8570 // point
8571 }
8572 }
8573 } while (a->clients().size() != nClients);
8574 };
8575 func(get<RooAbsArg>());
8576}
8577
8578// observables not in the axisVars are automatically projected over
8579xRooNode xRooNode::histo(const xRooNode &vars, const xRooNode &fr, bool content, bool errors, bool stack, bool errorsHi,
8580 bool errorsLo, int nErrorToys) const
8581{
8582
8583 if (!vars.fComp && strlen(vars.GetName())) {
8585 }
8586
8587 xRooNode out(TString::Format("%s.histo", GetName()), nullptr, *this);
8588
8589 RooAbsLValue *v = nullptr;
8590 if (vars.empty()) {
8591 // does an integral
8592 out.fComp = std::shared_ptr<TH1>(
8593 BuildHistogram(nullptr, !content, errors, -1, -1, fr, errorsHi, errorsLo, nErrorToys, nullptr, !stack, false));
8594 } else if (vars.size() == 1) {
8595 v = vars.at(0)->get<RooAbsLValue>();
8596 out.fComp = std::shared_ptr<TH1>(
8597 BuildHistogram(v, !content, errors, 1, 0, fr, errorsHi, errorsLo, nErrorToys, nullptr, !stack, true));
8598 } else {
8599 throw std::runtime_error("multi-dim histo not yet supported");
8600 }
8601
8602 return out;
8603}
8604
8606{
8607 return xRooNode(fComp, xRooNode(range.GetName(), nullptr, *this));
8608}
8609
8611 bool errorsHi, bool errorsLo, int nErrorToys, TH1 *templateHist, bool nostack,
8612 bool setInterp) const
8613{
8614 auto rar = get<RooAbsReal>();
8615 if (!rar) {
8616 if (get<TH1>()) {
8617 return dynamic_cast<TH1 *>(get()->Clone());
8618 }
8619 return nullptr;
8620 }
8621
8622 TObject *vv = rar;
8623
8624
8625 TH1 *h = nullptr;
8626 if (!v) {
8627 if (binStart != -1 || binEnd != -1) { // allow v to stay nullptr if doing integral (binStart=binEnd=-1)
8628 if (auto _ax = GetXaxis())
8629 v = dynamic_cast<RooAbsLValue *>(_ax->GetParent());
8630 } else {
8631 // don't need to integrate if doing a self-histogram
8632 v = dynamic_cast<RooRealVar *>(rar);
8633 }
8634 if (v) {
8635 vv = dynamic_cast<TObject *>(v);
8636 } else {
8637 // make a single-bin histogram of just this value
8638 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
8639 h = new TH1D(rar->GetName(), rar->GetTitle(), 1, 0, 1);
8640 h->GetXaxis()->SetBinLabel(1, rar->GetName());
8641 h->GetXaxis()->SetTimeFormat(rar->GetName());
8642 }
8643 }
8644
8645 auto x = dynamic_cast<RooRealVar *>(v);
8646 bool setTitle = false;
8647 if (templateHist) {
8648 // using template hist for the binning
8649 {
8650 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
8651 h = static_cast<TH1 *>(templateHist->Clone(rar->GetName()));
8652 }
8653 if (h->GetListOfFunctions())
8655 h->SetTitle(rar->GetTitle());
8656 h->Reset();
8657 } else if (x) {
8658 if (x == rar) {
8659 // self histogram ...
8660 {
8661 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
8662 h = new TH1D(rar->GetName(), rar->GetTitle(), 1, 0, 1);
8663 }
8664 h->Sumw2();
8665 h->GetXaxis()->SetBinLabel(1, rar->GetName());
8666 h->SetBinContent(1, rar->getVal());
8667 if (x->getError()) {
8668 h->SetBinError(1, x->getError());
8669 h->SetFillStyle(3005);
8670 h->SetFillColor(h->GetLineColor());
8671 }
8672 h->SetMaximum(x->hasMax() ? x->getMax()
8673 : (h->GetBinContent(1) + std::max(std::abs(h->GetBinContent(1) * 0.1), 50.)));
8674 h->SetMinimum(x->hasMin() ? x->getMin()
8675 : (h->GetBinContent(1) - std::max(std::abs(h->GetBinContent(1) * 0.1), 50.)));
8676 h->GetXaxis()->SetName(dynamic_cast<TObject *>(v)->GetName());
8677 h->SetOption("e2");
8678 h->SetMarkerSize(0);
8679 h->SetMarkerStyle(0);
8680
8681 return h;
8682 }
8683 auto _ax = GetXaxis();
8684 TString binningName = (_ax && _ax->GetParent() == x) ? _ax->GetName() : rar->getStringAttribute("binning");
8685 if (binningName == "")
8686 binningName = rar->GetName();
8687 if (x->hasBinning(binningName)) {
8688 if (x->getBinning(binningName).isUniform()) {
8689 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
8690 h = new TH1D(rar->GetName(), rar->GetTitle(), x->numBins(binningName) <= 0 ? 100 : x->numBins(binningName),
8691 x->getMin(binningName), x->getMax(binningName));
8692 } else {
8693 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
8694 h = new TH1D(rar->GetName(), rar->GetTitle(), x->numBins(binningName), x->getBinning(binningName).array());
8695 }
8696 h->GetXaxis()->SetTitle(x->getBinning(binningName).GetTitle());
8697 setTitle = true;
8698 } else if (auto _boundaries =
8699 _or_func(/*rar->plotSamplingHint(*x,x->getMin(),x->getMax())*/ (std::list<double> *)(nullptr),
8700 rar->binBoundaries(*x, -std::numeric_limits<double>::infinity(),
8701 std::numeric_limits<double>::infinity()));
8702 _boundaries) {
8703 std::vector<double> _bins;
8704 for (auto &b : *_boundaries) {
8705 if (_bins.empty() || std::abs(_bins.back() - b) > 1e-5 * _bins.back())
8706 _bins.push_back(b);
8707 } // found sometimes get virtual duplicates in the binning
8708 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
8709 h = new TH1D(rar->GetName(), rar->GetTitle(), _bins.size() - 1, &_bins[0]);
8710 delete _boundaries;
8711 } else if (!x->hasMax() || !x->hasMin()) {
8712 // use current value of x to estimate range with
8713 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
8714 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(), x->getVal() * 0.2, x->getVal() * 5);
8715 } else {
8716 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
8717 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(), x->getBinning().array());
8718 }
8719 h->Sumw2();
8720 } else if (!h) {
8721 {
8722 TDirectory::TContext ctx{nullptr}; // No self-registration to directories
8723 h = new TH1D(rar->GetName(), rar->GetTitle(), v->numBins(rar->GetName()), 0, v->numBins(rar->GetName()));
8724 }
8725 if (auto cat = dynamic_cast<RooAbsCategoryLValue *>(v)) {
8726 int i = 1;
8727 std::map<int, std::string> cats; // fill into a map to preserve index ordering
8728 for (auto &c : *cat) {
8729 cats[c.second] = c.first;
8730 }
8731 for (auto &[_, label] : cats) {
8732 h->GetXaxis()->SetBinLabel(i++, label.c_str());
8733 }
8734 }
8735 h->Sumw2();
8736 }
8737 if (auto o = dynamic_cast<TObject *>(v); o && !setTitle) {
8738 h->GetXaxis()->SetTitle(o->GetTitle());
8739 }
8740
8741 if (v) {
8742 if (h->GetXaxis()->IsAlphanumeric()) {
8743 // store the variable name in the TimeFormat property as well, b.c. alphanumeric requires axis name to be
8744 // "xaxis"
8745 h->GetXaxis()->SetTimeFormat(dynamic_cast<TObject *>(v)->GetName());
8746 } else {
8747 h->GetXaxis()->SetName(dynamic_cast<TObject *>(v)->GetName()); // WARNING: messes up display of bin labels
8748 }
8749 }
8750
8751 if (auto s = styles(nullptr, false); s) {
8752 auto _style = s.get<TStyle>();
8753 static_cast<TAttLine &>(*h) = *_style;
8754 static_cast<TAttFill &>(*h) = *_style;
8755 static_cast<TAttMarker &>(*h) = *_style;
8756 }
8757 if (strlen(h->GetXaxis()->GetTitle()) == 0)
8758 h->GetXaxis()->SetTitle(vv->GetTitle());
8759 auto p = dynamic_cast<RooAbsPdf *>(rar);
8760
8761 // possible speed improvement:
8762 // if(auto spdf = dynamic_cast<RooRealSumPdf*>(p); spdf && spdf->canBeExtended()) {
8763 // p = nullptr; // faster to evaluate sumpdf as a function not a pdf
8764 // }
8765
8766 if (empty && !errors) {
8767 return h;
8768 }
8769
8770 // if (!empty) {
8771
8772 auto _coefs = coefs();
8773
8774 RooFitResult *fr = nullptr;
8775 if (errors) {
8776 // must ensure the fit result we obtain includes pars from coefficients if present
8777 if (_fr.get<RooFitResult>()) {
8778 fr = static_cast<RooFitResult *>(_fr.get<RooFitResult>()->Clone());
8779 } else {
8780 auto frn =
8781 (!_coefs.get() ? *this : xRooNode(RooProduct("tmp", "tmp", RooArgList(*rar, *_coefs.get<RooAbsReal>()))))
8782 .fitResult();
8783 if (strlen(_fr.GetName()))
8784 frn = frn.reduced(_fr.GetName());
8785 fr = dynamic_cast<RooFitResult *>(frn->Clone());
8786 }
8787 if (!GETDMP(fr, _finalPars)) {
8789 }
8790
8791 /// Oct2022: No longer doing this because want to allow fitResult to be used to get partial error
8792 // // need to add any floating parameters not included somewhere already in the fit result ...
8793 // now done in the fitResult() method, where a fit result from the workspace can be automatically extended
8794
8795 TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr, _VM));
8796
8797 if (!prevCov || size_t(fr->covarianceMatrix().GetNcols()) < fr->floatParsFinal().size()) {
8798 TMatrixDSym cov(fr->floatParsFinal().size());
8799 if (prevCov) {
8800 for (int i = 0; i < prevCov->GetNcols(); i++) {
8801 for (int j = 0; j < prevCov->GetNrows(); j++) {
8802 cov(i, j) = (*prevCov)(i, j);
8803 }
8804 }
8805 }
8806 int i = 0;
8807 for (auto &p2 : fr->floatParsFinal()) {
8808 if (!prevCov || i >= prevCov->GetNcols()) {
8809 cov(i, i) = pow(dynamic_cast<RooRealVar *>(p2)->getError(), 2);
8810 }
8811 i++;
8812 }
8813 int covQualBackup = fr->covQual();
8816 }
8817
8818 if (v) {
8819 // need to remove v from result as we are plotting as function of v
8820 if (auto _p = fr->floatParsFinal().find(dynamic_cast<TObject *>(v)->GetName()); _p) {
8822 _pars.remove(*_p, true);
8824 int covQualBackup = fr->covQual();
8827 const_cast<RooArgList &>(fr->floatParsFinal())
8828 .remove(*_p, true); // NOTE: I think this might be a memory leak, should delete _p after removal
8829 }
8830 }
8831 // finally check at least one float has errors defined (might not be cause if in prefit state)
8832 bool hasErrors = false;
8833 for (auto pp : fr->floatParsFinal()) {
8834 if (dynamic_cast<RooRealVar *>(pp)->hasError()) {
8835 hasErrors = true;
8836 break;
8837 }
8838 }
8839 if (!hasErrors) {
8840 errors = false;
8841 delete fr;
8842 }
8843 }
8844
8846 if (v)
8847 normSet.add(*dynamic_cast<RooAbsArg *>(v));
8848
8849 if (binEnd == 0)
8850 binEnd = h->GetNbinsX();
8851
8852 bool needBinWidth = false;
8853 // may have MULTIPLE coefficients for the same pdf!
8854
8855 if (x && (p || _coefs.get() || rar->getAttribute("density"))) {
8856 // pdfs of samples embedded in a sumpdf (aka have a coef) will convert their density value to a content
8857 needBinWidth = true;
8858 }
8859
8860 if (auto spdf = dynamic_cast<RooRealSumPdf *>(p);
8861 spdf && spdf->canBeExtended() && !spdf->getFloor() && !_coefs.get()) {
8862 p = nullptr; // if pdf has no floor, will evaluate it as a function to allow it to be negative - evaluation should
8863 // also be faster (no integral)
8864 // exception is if RooRealSumPdf is embedded in a RooAddPdf (detected by presence of coefs) ... then it must be
8865 // evaluated as a pdf technically should check parent is a RooAddPdf, because if was inside a RooRealSumPdf then
8866 // would be evaluated as a function!
8867 }
8868
8869 // check if we need to do any projecting of other observables
8870 RooAbsReal *oldrar = nullptr;
8871 auto _obs = obs();
8872
8873 for (auto o : _obs) {
8874 if (auto rr = o->get<RooRealVar>(); rr && rr->hasRange("coordRange")) {
8875 rr->removeMin();rr->removeMax();//rr->removeRange("coordRange"); // doesn't actually remove, just sets to -inf->+inf
8876 rr->setStringAttribute("coordRange", nullptr); // removes the attribute
8877 }
8878 }
8879 // probably should also remove any range on the x-axis variable too, if there is one
8880 if (auto rr = dynamic_cast<RooRealVar *>(v); rr && rr->hasRange("coordRange")) {
8881 rr->removeMin();rr->removeMax();//rr->removeRange("coordRange"); // doesn't actually remove, just sets to -inf->+inf
8882 rr->setStringAttribute("coordRange", nullptr); // removes the attribute
8883 }
8884 coords(); // loads current coordinates and populates coordRange, if any
8885
8886 if (auto a = dynamic_cast<RooAbsArg *>(v))
8887 _obs.get<RooArgList>()->remove(*a);
8888 if (!_obs.get<RooArgList>()->empty()) {
8889 oldrar = rar;
8890 normSet.add(*_obs.get<RooArgList>());
8891 // check if any obs are restricted range
8892 bool hasRange = false;
8893 for (auto o : normSet) {
8894 if (auto rr = dynamic_cast<RooRealVar *>(o);
8895 rr && (rr->getStringAttribute("coordRange")) && strlen(rr->getStringAttribute("coordRange"))) {
8896 hasRange = true;
8897 break;
8898 }
8899 }
8900 if (p) {
8901 // need to handle special case of RooSimultaneous ... each pdf needs individually projecting over just its
8902 // dependent obs
8903 if (auto s = dynamic_cast<RooSimultaneous *>(p)) {
8904 auto newrar = new RooSimultaneous("projSim", "projSim", const_cast<RooAbsCategoryLValue &>(s->indexCat()));
8905 for (auto pdf : bins()) {
8906 // auto _pdf =
8907 // pdf->get<RooAbsPdf>()->createProjection(*pdf->get<RooAbsPdf>()->getObservables(*_obs.get<RooArgList>()));
8908 auto _pdf =
8909 new xRooProjectedPdf(TString::Format("%s_projection", pdf->GetName()), "", *pdf->get<RooAbsPdf>(),
8910 *pdf->get<RooAbsPdf>()->getObservables(*_obs.get<RooArgList>()));
8911 if (hasRange) {
8912 dynamic_cast<RooAbsPdf *>(_pdf)->setNormRange("coordRange");
8913 }
8914 newrar->addPdf(*_pdf, pdf->coords()[s->indexCat().GetName()]->get<RooCategory>()->getLabel());
8915 }
8916 rar = newrar;
8917 } else {
8918 rar = p->createProjection(
8919 *_obs.get<RooArgList>()); // TODO should use xRooProjectedPdf here too, because not fixed range and
8920 // extend behaviour of RooProjectedPdf in ROOT yet
8921 if (hasRange) {
8922 dynamic_cast<RooAbsPdf *>(rar)->setNormRange("coordRange");
8923 }
8924 }
8925 if (hasRange)
8926 p->setNormRange("coordRange"); // should get cleared when we sterilize
8927 } else {
8928 if (hasRange) {
8929 // commented out passing of normset so that getVal of non-pdf is always a 'raw' value (needed for raw eval
8930 // of RooRealSumPdf)
8931 rar = std::unique_ptr<RooAbsReal>{rar->createIntegral(
8932 *_obs.get<RooArgList>(),
8933 /*RooFit::NormSet(normSet),*/ RooFit::Range("coordRange"))}
8934 .release();
8935 } else {
8936 rar =
8937 std::unique_ptr<RooAbsReal>{rar->createIntegral(*_obs.get<RooArgList>() /*, RooFit::NormSet(normSet)*/)}
8938 .release();
8939 }
8940 }
8941 }
8942
8943 bool scaleExpected = (p && p->canBeExtended() && !_coefs.get());
8944 // Note about above: if pdf has coefficients then its embedded in a RooAddPdf that has coefs defined ...
8945 // in this case we should *not* scale by expected, since the coefs become the scaling instead
8946 // we should also not build a stack for this (may be a RooRealSumPdf inside a RooAddPdf, but the
8947 // samples of the RooRealSumPdf wont be correctly scaled to line up with overall RooRealSumPdf
8948 // which will be normalized to its coefficient
8949 if (!nostack && p && p->canBeExtended() && _coefs.get()) {
8950 nostack = true;
8951 // if wanted to still hve a stack, would need to scale the stack subcomponents by
8952 // coefs-value / p_integral(raw) ... since raw p-integral will be what stack integrates to
8953 }
8954
8955 std::unique_ptr<RooArgSet> snap(normSet.snapshot());
8957 std::vector<double> lapTimes;
8958 bool warned = false;
8959 if (binStart == -1 && binEnd == -1) {
8960 binEnd = 1;
8961 }
8962 auto cat = (!x) ? dynamic_cast<RooAbsCategoryLValue *>(v) : nullptr;
8963 RooArgList *errorPars = nullptr;
8964 std::unique_ptr<RooAbsCollection> errorParsSnap;
8965
8966 if (!v) {
8967 setInterp = false;
8968 }
8969
8970 if (setInterp) {
8971 RooAbsArg *vvv = dynamic_cast<RooAbsArg *>(v);
8972 // determining if histogram should have interpolation drawing options set on it
8973 // need to strip namespace to discount the "HistFactory" namespace classes from all being treated as binned
8974 TString clNameNoNamespace = rar->ClassName();
8976 setInterp = (clNameNoNamespace.Contains("Hist") || vvv->isCategory() || rar->isBinnedDistribution(*vvv) ||
8977 h->GetNbinsX() == 1 || rar->getAttribute("BinnedLikelihood") ||
8978 (dynamic_cast<RooAbsRealLValue *>(vvv) &&
8979 std::unique_ptr<std::list<double>>(rar->binBoundaries(*dynamic_cast<RooAbsRealLValue *>(vvv),
8980 -std::numeric_limits<double>::infinity(),
8981 std::numeric_limits<double>::infinity()))))
8982 ? false
8983 : true;
8984 if (auto d = dynamic_cast<RooHistFunc *>(rar); d && !d->isBinnedDistribution(*vvv) && h->GetNbinsX() != 1) {
8985 setInterp = true; // hist func is interpolated, so draw it as such
8986 }
8987 if (setInterp && !components().empty()) {
8988 // check if all components of dOpt are "Hist" type (CMS model support)
8989 // if so then don't interp;
8990 bool allHist = true;
8991 for (auto &s : components()) {
8992 TString _clName = s->get()->ClassName();
8993 _clName = _clName(_clName.Last(':') + 1, _clName.Length());
8994 if (!(s->get() && _clName.Contains("Hist"))) {
8995 allHist = false;
8996 break;
8997 }
8998 }
8999 if (allHist)
9000 setInterp = false;
9001 }
9002 if (setInterp) {
9003 h->SetOption("l"); // does linear interpolation between points
9004 }
9005 }
9006
9007 if (errors) {
9008 // may be computing potentially asymmetric errors
9009 // the main histogram will be the error band, and the nominal histogram will be added as a function
9010 // so that it is drawn over the top of the error band
9011 // note that this means GetBinContent on returned histogram will return midpoint of the up and down error
9012 auto l = static_cast<TH1 *>(h->Clone("nominal"));
9013 l->SetDirectory(0);
9014 l->SetFillStyle(0);
9015 h->GetListOfFunctions()->Add(l, (setInterp) ? "lsame" : "histsame");
9016 h->SetOption(setInterp ? "e3" : "e2"); // default draw option E2 or E3 so error band shown .. could have used
9017 // 'EX0' to draw "classic style"
9018 // could take this from the 'band' style object if we create one in future?
9019 h->SetMarkerSize(0);
9020 h->SetFillStyle(3005);
9021 h->SetFillColor(h->GetLineColor());
9022 }
9023
9024 if (nErrorToys > 0) {
9025 errors = false; // wont evaluate error on each toy, will estimate for std.dev or normiles of toys
9026 // need list of errorPars
9027 auto allPars =
9028 (!_coefs.get() ? *this : xRooNode(RooProduct("tmp", "tmp", RooArgList(*rar, *_coefs.get<RooAbsReal>()))))
9029 .pars();
9030 errorPars = new RooArgList; // will be in same order as appear in fr.
9031 for (auto a : fr->floatParsFinal()) {
9032 if (auto par = allPars.get<RooArgList>()->find(*a)) {
9033 errorPars->add(*par);
9034 }
9035 }
9036 errorParsSnap.reset(errorPars->snapshot());
9037 auto l = static_cast<TH1 *>(h->Clone("toys"));
9038 l->Reset(); // removes any functions
9039 l->SetDirectory(0);
9040 h->GetListOfFunctions()->Add(
9041 l, "histsame"); // ensures just this empty hist will be drawn, and not each individual toy
9042
9043 if (errorsLo || errorsHi)
9044 empty = false; // must not be empty b.c. calculation of error relies on knowing nominal (see after loop)
9045 }
9046
9047 for (int toy = 0; toy < (nErrorToys + 1); toy++) {
9048
9049 TH1 *main_h = h;
9050 if (toy > 0) {
9051 h = static_cast<TH1 *>(main_h->Clone(TString::Format("toy_%d", toy)));
9052 h->SetDirectory(0);
9053 h->Reset();
9054 static_cast<TH1 *>(main_h->GetListOfFunctions()->FindObject("toys"))->GetListOfFunctions()->Add(h);
9055 // randomize the parameter values according to the fr's covariance matrix
9056 errorPars->assignValueOnly(fr->randomizePars());
9057 // if any par has 0 error, randomizePars can end up assigning a nan, so replace
9058 // all zero errors with value
9059 for (auto pp : fr->floatParsFinal()) {
9060 auto _vv = dynamic_cast<RooRealVar *>(pp);
9061 if (!_vv)
9062 continue;
9063 if (_vv->getError() == 0)
9064 errorPars->setRealValue(pp->GetName(), _vv->getVal());
9065 }
9066 }
9067
9068 for (int i = std::max(1, binStart); i <= std::min(h->GetNbinsX(), binEnd); i++) {
9069 timeIt.Start(true);
9070 if (x) {
9071 x->setVal(h->GetBinCenter(i));
9072 } else if (cat) {
9073 cat->setLabel(h->GetXaxis()->GetBinLabel(i)); // because order might not match "binning" order
9074 } else if (v) {
9075 v->setBin(i - 1);
9076 }
9077 if (x && !x->inRange("coordRange"))
9078 continue;
9079
9080 double r = 0;
9081 if (!empty || toy > 0) {
9082 r = /*(p && p->selfNormalized())*/ rar->getVal(p ? &normSet : nullptr);
9083#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
9084 if (std::isnan(r) && RooNaNPacker::isNaNWithPayload(r)) {
9086 }
9087#endif
9088 if (r && _coefs.get()) {
9089 r *= _coefs.get<RooAbsReal>()->getVal(normSet);
9090 }
9091 if (needBinWidth) {
9092 r *= h->GetBinWidth(i);
9093 }
9094 if (scaleExpected) {
9095 // std::cout << r << " exp = " << p->expectedEvents(normSet) << " for normRange " << (p->normRange() ?
9096 // p->normRange() : "null") << std::endl; p->Print();rar->Print();
9097 r *= (p->expectedEvents(normSet));
9098 } // do in here in case dependency on var
9099 }
9100 h->SetBinContent(i, r);
9101
9102 if (errors) {
9103 static_cast<TH1 *>(h->FindObject("nominal"))->SetBinContent(i, r); // transfer nominal to nominal hist
9104 double res;
9105 bool doAsym = (errorsHi && errorsLo);
9106 if (doAsym) {
9107 errorsHi = false;
9108 }
9109 if (p) {
9110 // std::cout << "computing error of :" << h->GetBinCenter(i) << std::endl;
9111 // //fr->floatParsFinal().Print(); fr->covarianceMatrix().Print();
9112 // res = PdfWrapper((oldrar) ? *rar : *p, _coefs.get<RooAbsReal>(), !v, oldrar ? p : nullptr)
9113 // .getSimplePropagatedError(*fr, normSet);
9115 PdfWrapper((oldrar) ? *rar : *p, _coefs.get<RooAbsReal>(), !v, oldrar ? p : nullptr), *fr, normSet,
9117#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
9118 // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
9119 p->_normSet = nullptr;
9120#endif
9121 } else {
9122 // res = RooProduct("errorEval", "errorEval",
9123 // RooArgList(*rar, !_coefs.get() ? RooFit::RooConst(1) :
9124 // *_coefs.get<RooAbsReal>()))
9125 // .getPropagatedError(
9126 // *fr /*, normSet*/); // should be no need to pass a normSet to a non-pdf (but
9127 // not verified this)
9129 RooProduct("errorEval", "errorEval",
9130 RooArgList(*rar, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>())),
9131 *fr, {}, &errorPars, errorsHi,
9132 errorsLo); // should be no need to pass a normSet to a non-pdf (but not verified this)
9133 // especially important not to pass in the case we are evaluated RooRealSumPdf as a function! otherwise
9134 // error will be wrong
9135 }
9136 if (needBinWidth) {
9137 res *= h->GetBinWidth(i);
9138 }
9139 h->SetBinError(i, res);
9140 if (doAsym) {
9141 // compute Hi error
9142 errorsHi = true;
9143 errorsLo = false;
9144 if (p) {
9146 PdfWrapper((oldrar) ? *rar : *p, _coefs.get<RooAbsReal>(), !v, oldrar ? p : nullptr), *fr, normSet,
9148 } else {
9150 RooProduct("errorEval", "errorEval",
9151 RooArgList(*rar, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>())),
9152 *fr, {}, &errorPars, errorsHi, errorsLo);
9153 }
9154 if (needBinWidth) {
9155 res *= h->GetBinWidth(i);
9156 }
9157 errorsLo = true;
9158 // lowVal = content - error, highVal = content + res
9159 // => band/2 = (res+error)/2 and band-mid = (2*content+res-error)/2
9160 h->SetBinContent(i, h->GetBinContent(i) + (res - h->GetBinError(i)) * 0.5);
9161 h->SetBinError(i, (res + h->GetBinError(i)) * 0.5);
9162 }
9163 }
9164 timeIt.Stop();
9165 lapTimes.push_back(timeIt.RealTime());
9166 double time_estimate =
9167 (lapTimes.size() > 1)
9168 ? (h->GetNbinsX() * (std::accumulate(lapTimes.begin() + 1, lapTimes.end(), 0.) / (lapTimes.size() - 1)))
9169 : 0.;
9170 if (!warned && (lapTimes.at(0) > 10 || (lapTimes.size() > 2 && time_estimate > 60.))) {
9171 TTimeStamp t2;
9172 t2.Add(time_estimate);
9173 Warning("BuildHistogram", "Building this histogram will take until %s", t2.AsString());
9174 if (errors) {
9175 // install interrupt handler
9176 runningNode = this;
9178 }
9179 warned = true;
9180 }
9181 if (fInterrupted) {
9182 if (errors) {
9183 Warning("BuildHistogram", "Skipping errors for remaining bins");
9184 errors = false;
9185 }
9186 fInterrupted = false;
9187 }
9188 }
9189 if (toy > 0) {
9190 h = main_h;
9191 }
9192 }
9193 if (gOldHandlerr) {
9195 gOldHandlerr = nullptr;
9196 }
9197 normSet = *snap;
9198
9199 if (errorPars) {
9200 if (errorParsSnap)
9202 delete errorPars;
9203 }
9204 if (nErrorToys) {
9205 // compute main histogram error bar from toys
9206 // if not doing asymmetric, then will display std.dev
9207 // otherwise will copy main to nominal and make main error bar s.t. it shows +/-1sigma vals
9208 if (errorsLo && errorsHi) {
9209 auto nomHist = static_cast<TH1 *>(h->FindObject("nominal"));
9210 nomHist->Add(h);
9211 }
9212 for (int i = 1; i <= h->GetNbinsX(); i++) {
9213 std::vector<double> vals;
9214 vals.reserve(nErrorToys);
9215 for (int j = 1; j < (nErrorToys + 1); j++) {
9216 vals.push_back(
9217 static_cast<TH1 *>(h->FindObject("toys")->FindObject(TString::Format("toy_%d", j)))->GetBinContent(i));
9218 }
9219 double upVal, downVal;
9220 if (errorsLo || errorsHi) {
9221 std::sort(vals.begin(), vals.end());
9222 upVal = vals.at(std::floor(vals.size() * ROOT::Math::gaussian_cdf(1)));
9223 downVal = vals.at(std::floor(vals.size() * ROOT::Math::gaussian_cdf(-1)));
9224 if (!errorsLo)
9225 downVal = 2. * h->GetBinContent(i) - upVal;
9226 if (!errorsHi)
9227 upVal = 2. * h->GetBinContent(i) - downVal;
9228 } else {
9229 double err = TMath::StdDev(vals.begin(), vals.end());
9230 upVal = h->GetBinContent(i) + err;
9231 downVal = h->GetBinContent(i) - err;
9232 }
9233 h->SetBinContent(i, (upVal + downVal) * 0.5);
9234 h->SetBinError(i, (upVal - downVal) * 0.5);
9235 }
9236 }
9237
9238 if (oldrar) {
9239 std::vector<RooAbsArg *> extra;
9240 if (auto s = dynamic_cast<RooSimultaneous *>(rar)) {
9241 // need to delete all the subpdfs we created too
9242 for (auto _pdf : s->servers()) {
9243 if (dynamic_cast<RooAbsPdf *>(_pdf))
9244 extra.push_back(_pdf);
9245 }
9246 }
9247 extra.push_back(rar);
9248 rar = oldrar;
9249 xRooNode(*rar).sterilize(); // need to clear the cache of the created integral - do this before deleting things!
9250 for (auto a : extra)
9251 delete a;
9252 } else {
9253 sterilize(); // needed to forget about the normSet that was passed to getVal()
9254 }
9255
9256 if (!p && !rar->getAttribute("density") && !needBinWidth) {
9257 h->GetYaxis()->SetTitle(rar->getStringAttribute("units"));
9258 } else if ((p && p->canBeExtended()) || (!p && needBinWidth)) {
9259 h->GetYaxis()->SetTitle("Events");
9260 } else {
9261 h->GetYaxis()->SetTitle("Probability Mass");
9262 }
9263 h->GetYaxis()->SetMaxDigits(3);
9264
9265 if (errors) {
9266 delete fr;
9267 }
9268
9269 // build a stack unless not requested
9270 if (!nostack) {
9271 // need to draw copy of hist so shown over the stack
9272 auto hCopy = static_cast<TH1 *>(h->Clone("copy"));
9273 hCopy->Reset();
9274 hCopy->Add(h); // use Reset and Add to clear the function list (dont clear directly as may double-delete if same
9275 // object added twice)
9276 hCopy->SetStats(false);
9277 h->GetListOfFunctions()->Add(hCopy, TString(h->GetOption()) + "same");
9278 h->GetListOfFunctions()->Add(hCopy, "axissame"); // prevents stack covering axis
9279 TString dOpt = (setInterp) ? "LF2" : ""; // should become lf2 if interpolation of histogram is appropriate
9280
9281 const xRooNode *rarNode = this;
9282 RooAbsReal *sf = nullptr;
9283 if (get()->InheritsFrom("RooExtendPdf")) {
9284 const_cast<xRooNode *>(this)->browse();
9285 rarNode = find(".pdf").get();
9286 // rar = rarNode->get<RooAbsReal>();
9287 sf = find(".n")->get<RooAbsReal>();
9288 }
9289
9290 THStack *stack = new THStack("stack", TString::Format("%s;%s", rar->GetTitle(), h->GetXaxis()->GetTitle()));
9291 int count = 0;
9292 std::map<std::string, int> colorByTitle; // TODO: should fill from any existing legend
9293 std::set<std::string> allTitles;
9294 bool titleMatchName = true;
9295 std::map<std::string, TH1 *> histGroups;
9296 std::vector<TH1 *> hhs;
9297 std::set<std::pair<size_t,TH1*>> ordered_hhs;
9298 std::set<TH1 *> histsWithBadTitles; // these histograms will have their titles autoFormatted
9299
9300 // support for CMS model case where has single component containing many coeffs
9301 // will build stack by setting each coeff equal to 0 in turn, rebuilding the histogram
9302 // the difference from the "full" histogram will be the component
9304 if (!rarNode->components().empty()) {
9305 auto comps = rarNode->components()[0];
9306 for (auto &c : *comps) {
9307 if (c->fFolder == "!.coeffs" || c->fFolder == "!.coeffpars")
9308 cms_coefs.add(*c->get<RooAbsArg>());
9309 }
9310 }
9311 if (!cms_coefs.empty()) {
9312 RooRealVar zero("zero", "", 0);
9313 std::shared_ptr<TH1> prevHist(static_cast<TH1 *>(h->Clone()));
9314 prevHist->Reset();
9315 prevHist->Add(h);
9316 // Nov25: discovered cms CMSHistSum has really heavy caching, that wont clear until I clone
9317 // i.e. I must first copy the function, then redirect the server, then copy that function
9318 // the copy of the copy will give the correct results, not the copy
9319 // This wasn't necessary for the CMSHistErrorPropagator class that I had before.
9320 std::unique_ptr<RooAbsReal> forig(
9321 dynamic_cast<RooAbsReal *>(rarNode->components()[0]->get()->Clone("tmpCopy0")));
9322 for (auto c : cms_coefs) {
9323 // seems I have to remake the function each time, as haven't figured out what cache needs clearing?
9324
9325 zero.setAttribute(Form("ORIGNAME:%s", c->GetName())); // used in redirectServers to say what this replaces
9326 forig->redirectServers(RooArgSet(zero), false, true); // each time will replace one additional coef
9327 std::unique_ptr<RooAbsReal> f(dynamic_cast<RooAbsReal *>(forig->Clone("tmpCopy")));
9328
9329 // zero.setAttribute(Form("ORIGNAME:%s",c->GetName()),false); (commented out so that on next iteration
9330 // will still replace all prev)
9331 auto hh = xRooNode(*f, *this).BuildHistogram(v);
9332 hh->SetName(c->GetName());
9333 if (sf)
9334 hh->Scale(sf->getVal());
9335 if (strlen(hh->GetTitle()) == 0) {
9336 hh->SetTitle(c->GetName()); // ensure all hists has titles
9337 // special case for CMS ... if find "_proc_" in the title, take whatever is after that
9338 auto idx = TString(hh->GetTitle()).Index("_proc_");
9339 if(idx>=0) {
9340 hh->SetTitle(TString(TString(hh->GetTitle())(idx+6,strlen(hh->GetTitle()))));
9341 }
9342 histsWithBadTitles.insert(hh);
9343 } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
9344 histsWithBadTitles.insert(hh);
9345 }
9346 titleMatchName &= (TString(c->GetName()) == hh->GetTitle() ||
9347 TString(hh->GetTitle()).BeginsWith(TString(c->GetName()) + "_"));
9348 std::shared_ptr<TH1> nextHist(static_cast<TH1 *>(hh->Clone()));
9349 hh->Add(prevHist.get(), -1.);
9350 hh->Scale(-1.);
9351 // remove the errors ... the above lines will have introduced errors
9352 hh->TH1::Reset("ICE"); // calling the base class method explicitly will only clear errors
9353 ordered_hhs.insert(std::pair(ordered_hhs.size(),hh));
9355 }
9356 } else if (get<RooSimultaneous>()) {
9357 // need to create a histogram for each sample across all the channels - will rely on functionality below to
9358 // merge them based on titles
9359
9360 for (auto &chan : bins()) {
9361 TString chanName(chan->GetName());
9362 chanName = chanName(chanName.Index("=") + 1, chanName.Length());
9363 auto samps = chan->mainChild();
9364 if (!samps)
9365 samps = *chan;
9366 for (auto &samp : samps.components()) {
9367 auto hh = static_cast<TH1 *>(h->Clone(samp->GetName()));
9368 hh->Reset();
9369 hh->SetTitle(samp->GetTitle());
9370 if (strlen(hh->GetTitle()) == 0) {
9371 hh->SetTitle(samp->GetName());
9372 histsWithBadTitles.insert(hh);
9373 } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
9374 histsWithBadTitles.insert(hh);
9375 }
9376 hh->SetTitle(TString(hh->GetTitle())
9377 .ReplaceAll(TString(chan->get()->GetName()) + "_",
9378 "")); // remove occurance of channelname_ in title (usually prefix)
9379 titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
9380 TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
9381 hh->SetBinContent(hh->GetXaxis()->FindFixBin(chanName), samp->GetContent());
9382 ordered_hhs.insert(std::pair(ordered_hhs.size(),hh));
9383 }
9384 }
9385 } else {
9386 for (auto &samp : rarNode->components()) {
9387 auto hh = samp->BuildHistogram(
9388 v, empty, false /* no errors for stack*/, binStart, binEnd, _fr, false, false, 0, h, true,
9389 setInterp); // passing h to ensure binning is the same for all subcomponent hists
9390 hh->SetName(samp->GetName());
9391 if (sf)
9392 hh->Scale(sf->getVal());
9393 ordered_hhs.insert(std::pair((samp->get<RooAbsArg>() && samp->get<RooAbsArg>()->getStringAttribute("StackOrder")) ? TString(samp->get<RooAbsArg>()->getStringAttribute("StackOrder")).Atoi() : ordered_hhs.size(),hh));
9394 if (strlen(hh->GetTitle()) == 0) {
9395 hh->SetTitle(samp->GetName()); // ensure all hists has titles
9396 histsWithBadTitles.insert(hh);
9397 } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
9398 histsWithBadTitles.insert(hh);
9399 }
9400 titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
9401 TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
9402 }
9403 }
9404
9405 // pull histograms in their order
9406 for(auto& [_,hh] : ordered_hhs) {
9407 hhs.push_back(hh);
9408 }
9409
9410 if (!hhs.empty()) {
9411 for (auto &hh : hhs) {
9412 allTitles.insert(hh->GetTitle());
9413 }
9414
9415 // get common prefix to strip off only if all titles match names and
9416 // any title is longer than 10 chars
9417 size_t e = std::min(allTitles.begin()->size(), allTitles.rbegin()->size());
9418 size_t ii = 0;
9419 bool goodPrefix = false;
9420 std::string commonSuffix;
9421 if (titleMatchName && hhs.size() > 1) {
9422 while (ii < e - 1 && allTitles.begin()->at(ii) == allTitles.rbegin()->at(ii)) {
9423 ii++;
9424 if (allTitles.begin()->at(ii) == '_' || allTitles.begin()->at(ii) == ' ')
9425 goodPrefix = true;
9426 }
9427
9428 // find common suffix if there is one .. must start with a "_"
9429 bool stop = false;
9430 while (!stop && commonSuffix.size() < size_t(e - 1)) {
9431 commonSuffix = allTitles.begin()->substr(allTitles.begin()->length() - commonSuffix.length() - 1);
9432 for (auto &tt : allTitles) {
9433 if (!TString(tt).EndsWith(commonSuffix.c_str())) {
9434 commonSuffix = commonSuffix.substr(1);
9435 stop = true;
9436 break;
9437 }
9438 }
9439 }
9440 if (commonSuffix.find('_') == std::string::npos) {
9441 commonSuffix = "";
9442 } else {
9443 commonSuffix = commonSuffix.substr(commonSuffix.find('_'));
9444 }
9445 }
9446 if (!goodPrefix)
9447 ii = 0;
9448
9449 // also find how many characters are needed to distinguish all entries (that dont have the same name)
9450 // then carry on up to first space or underscore
9451 size_t jj = 0;
9452 std::map<std::string, std::string> reducedTitles;
9453 while (reducedTitles.size() != allTitles.size()) {
9454 jj++;
9455 std::map<std::string, int> titlesMap;
9456 for (auto &s : allTitles) {
9457 if (reducedTitles.count(s))
9458 continue;
9459 titlesMap[s.substr(0, jj)]++;
9460 }
9461 for (auto &s : allTitles) {
9462 if (titlesMap[s.substr(0, jj)] == 1 && (jj >= s.length() || s.at(jj) == ' ' || s.at(jj) == '_')) {
9463 reducedTitles[s] = s.substr(0, jj);
9464 }
9465 }
9466 }
9467
9468 // strip common prefix and suffix before adding
9469 for (auto ritr = hhs.rbegin(); ritr != hhs.rend(); ++ritr) { // go in reverse order
9470 if (!histsWithBadTitles.count((*ritr))) {
9471 continue;
9472 }
9473 auto _title = (hhs.size() > 5) ? reducedTitles[(*ritr)->GetTitle()] : (*ritr)->GetTitle();
9474 _title = _title.substr(ii < _title.size() ? ii : 0);
9475 if (!commonSuffix.empty() && TString(_title).EndsWith(commonSuffix.c_str()))
9476 _title = _title.substr(0, _title.length() - commonSuffix.length());
9477 (*ritr)->SetTitle(_title.c_str());
9478 }
9479 }
9480
9481 for (auto &hh : hhs) {
9482 // automatically group hists that all have the same title
9483 if (histGroups.find(hh->GetTitle()) == histGroups.end()) {
9484 histGroups[hh->GetTitle()] = hh;
9485 } else {
9486 // add it into this group
9487 histGroups[hh->GetTitle()]->Add(hh);
9488 delete hh;
9489 hh = nullptr;
9490 continue;
9491 }
9492 auto hhMin = (hh->GetMinimum() == 0) ? hh->GetMinimum(1e-9) : hh->GetMinimum();
9493 if (!stack->GetHists() && h->GetMinimum() > hhMin) {
9494 auto newMin = hhMin - (h->GetMaximum() - hhMin) * gStyle->GetHistTopMargin();
9495 if (hhMin >= 0 && newMin < 0)
9496 newMin = hhMin * 0.99;
9497 // adjustYRange(newMin, h->GetMaximum());
9498 }
9499
9500 /*if(stack->GetHists() && stack->GetHists()->GetEntries()>0) {
9501 // to remove rounding effects on bin boundaries, see if binnings compatible
9502 auto _h1 = dynamic_cast<TH1*>(stack->GetHists()->At(0));
9503 if(_h1->GetNbinsX()==hh->GetNbinsX()) TODO ... finish dealing with silly rounding effects
9504 }*/
9505 TString thisOpt = TString(hh->GetOption()) == "l" ? "LF2" : ""; // need LF2 to get smooth line with fill
9506 // uncomment next line to blend continuous with discrete components .. get some unpleasant "poke through"
9507 // effects though
9508 // if(auto s = samp->get<RooAbsReal>(); s) thisOpt = s->isBinnedDistribution(*dynamic_cast<RooAbsArg*>(v)) ?
9509 // "" : "LF2";
9510 stack->Add(hh, thisOpt);
9511 }
9512 // stack->SetBit(kCanDelete); // should delete its sub histograms
9513 h->GetListOfFunctions()->AddFirst(stack, "noclear same");
9514 // stack->Draw("noclear same");
9515 // h->Draw(
9516 // dOpt + sOpt +
9517 // "same"); // overlay again .. if stack would cover original hist (negative components) we still see
9518 // integral
9519 // h->Draw("axissame"); // redraws axis
9520
9521 TList *ll = stack->GetHists();
9522 if (ll && ll->GetEntries()) {
9523
9524 // finally, ensure all hists are styled
9525 for (auto ho : *ll) {
9526 TH1 *hh = dynamic_cast<TH1 *>(ho);
9527 if (!hh)
9528 continue;
9529 bool createdStyle = (xRooNode(*hh, *this).styles(nullptr, false).get<TStyle>() == nullptr);
9530
9531 if (createdStyle) {
9532 // give hist a color, that isn't the same as any other hists color
9533 hh->SetFillStyle(1001); // solid fill style
9534 bool used = false;
9535 do {
9536 hh->SetFillColor(gEnv->GetValue("XRooFit.MinFillColor", kP10Blue /* was previously 2*/) + (count++));
9537 if (!gROOT->GetColor(hh->GetFillColor())) {
9538 // color doesn't exist, default it to transparent?
9539 hh->SetFillColor(0);
9540 }
9541 // check not already used this color
9542 used = false;
9543 for (auto ho2 : *ll) {
9544 TH1 *hh2 = dynamic_cast<TH1 *>(ho2);
9545 if (!hh2)
9546 continue;
9547 auto _styleNode = xRooNode(*hh2, *this).styles(hh2, false);
9548 auto _style = _styleNode.get<TStyle>();
9549 if (hh != hh2 && _style && _style->GetFillColor() == hh->GetFillColor()) {
9550 used = true;
9551 break;
9552 }
9553 }
9554 } while (used);
9555 }
9556
9557 auto _styleNode = xRooNode(*hh, *this).styles(hh);
9558 if (auto _style = _styleNode.get<TStyle>()) {
9559 *dynamic_cast<TAttLine *>(hh) = *_style;
9560 *dynamic_cast<TAttFill *>(hh) = *_style;
9561 *dynamic_cast<TAttMarker *>(hh) = *_style;
9562 }
9563 // for stacks, fill color of white should be color 10 unless fill style is 0
9564 if (hh->GetFillColor() == kWhite && hh->GetFillStyle() != 0) {
9565 // kWhite means 'transparent' in ROOT ... should really use a FillStyle of 0 for that
9566 // so assume user wanted actual white, which is color 10
9567 hh->SetFillColor(10);
9568 }
9569 }
9570 }
9571 }
9572
9573 return h;
9574}
9575
9576double xRooNode::GetBinData(int bin, const xRooNode &data)
9577{
9578 if (data.get<RooAbsData>()) {
9579 // attach as a child before calling datasets(), so that is included in the list
9580 push_back(std::make_shared<xRooNode>(data));
9581 }
9582 auto node = datasets().find(data.GetName());
9583 if (data.get<RooAbsData>()) {
9584 // remove the child we attached
9585 resize(size() - 1);
9586 }
9587 if (!node)
9588 return std::numeric_limits<double>::quiet_NaN();
9589 return node->GetBinContent(bin);
9590}
9591
9592std::vector<double> xRooNode::GetBinContents(int binStart, int binEnd) const
9593{
9594 if (fBinNumber != -1) {
9595 if (binStart != binEnd || !fParent) {
9596 throw std::runtime_error(TString::Format("%s is a bin - only has one value", GetName()));
9597 }
9598 return fParent->GetBinContents(fBinNumber, fBinNumber);
9599 }
9600 std::vector<double> out;
9601 if (get<RooAbsData>()) {
9602 auto g = BuildGraph(
9603 nullptr,
9604 (binStart != -1 ||
9605 binEnd != -1) /*include points for zeros unless we are asking for a single point with start=end=-1*/);
9606 if (!g) {
9607 return out;
9608 }
9609 if (binStart == binEnd && binStart == -1) {
9610 // integral over all bins if getting bin content -1
9611 double integral(0);
9612 for (int i = 0; i < g->GetN(); i++)
9613 integral += g->GetPointY(i);
9614 out.push_back(integral);
9615 delete g;
9616 return out;
9617 }
9618 for (int i = binStart - 1; i < g->GetN() && (binEnd == 0 || i < binEnd); i++) {
9619 out.push_back(g->GetPointY(i));
9620 }
9621 delete g;
9622 return out;
9623 }
9624
9625 bool doIntegral = false;
9626 if (binStart == binEnd && binStart == -1) {
9627 binStart = -1;
9628 binEnd = -1;
9629 doIntegral = true;
9630 } // return integral if request bin -1
9631 auto h = BuildHistogram(nullptr, false, false, binStart, binEnd);
9632 if (!h) {
9633 throw std::runtime_error(TString::Format("%s has no content", GetName()));
9634 }
9635 if (binEnd == 0) {
9636 binEnd = h->GetNbinsX();
9637 }
9638 if (doIntegral) {
9639 double tot = 0;
9640 for (int i = 1; i <= h->GetNbinsX(); i++) {
9641 tot += h->GetBinContent(i);
9642 }
9643 out.push_back(tot);
9644 } else {
9645 for (int i = binStart; i <= binEnd; i++) {
9646 out.push_back(h->GetBinContent(i));
9647 }
9648 }
9649 delete h;
9650 return out;
9651}
9652
9654{
9655 if (auto a = get<RooAbsArg>(); a) {
9656 // go through servers looking for 'main' thing
9657 for (auto &l : a->servers()) {
9658 if (l->getAttribute("MAIN_MEASUREMENT") || l->InheritsFrom("RooRealSumPdf") || l->InheritsFrom("RooAddPdf")) {
9659 return xRooNode(*l, *this);
9660 }
9661 }
9662 // the main child of a RooProduct is one that has the same name (/alias) as the product (except if is a bin
9663 // factor)
9664 if (a->IsA() == RooProduct::Class() && fBinNumber == -1) {
9665 for (auto &l : factors()) {
9666 if (strcmp(l->GetName(), GetName()) == 0) {
9667 return *l;
9668 }
9669 }
9670 }
9671 }
9672 return xRooNode();
9673}
9674
9676{
9677 if (auto o = get(); o) {
9678 o->Inspect();
9679 } else {
9681 }
9682}
9683
9684bool TopRightPlaceBox(TPad *p, TObject *o, double w, double h, double &xl, double &yb)
9685{
9686#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
9687 // reinitialize collide grid because the filling depends on fUxmin and fUxmax (and ymin ymax too)
9688 // and these aren't filled on the first time we do the placement (they init to 0 and 1), but will be filled
9689 // subsequently
9690 for (int i = 0; i < p->fCGnx; i++) {
9691 for (int j = 0; j < p->fCGny; j++) {
9692 p->fCollideGrid[i + j * p->fCGnx] = true;
9693 }
9694 }
9695 p->FillCollideGrid(o);
9696 Int_t iw = (int)(p->fCGnx * w);
9697 Int_t ih = (int)(p->fCGny * h);
9698
9699 Int_t nxmax = p->fCGnx - iw - 1 - p->fCGnx * p->GetRightMargin();
9700 Int_t nymax = p->fCGny - ih - 1 - p->fCGny * p->GetTopMargin();
9701
9702 for (Int_t j = nymax; j >= 0; j--) {
9703 for (Int_t i = nxmax; i >= 0; i--) {
9704 if (p->Collide(i, j, iw, ih)) {
9705 continue;
9706 } else {
9707 xl = (double)(i) / (double)(p->fCGnx);
9708 yb = (double)(j) / (double)(p->fCGny);
9709 return true;
9710 }
9711 }
9712 }
9713 return false;
9714#else
9715 return p->PlaceBox(o, w, h, xl, yb, "trw");
9716#endif
9717}
9718
9719TPaveText *getPave(const char *name = "labels", bool create = true, bool doPaint = false)
9720{
9721 if (auto p = dynamic_cast<TPaveText *>(gPad->GetPrimitive(name)); p) {
9722 if (doPaint)
9723 gPad->PaintModified(); //-- slows down x11 so trying to avoid
9724 return p;
9725 }
9726 if (!create) {
9727 return nullptr;
9728 }
9729 auto l = new TPaveText(gPad->GetLeftMargin() + 0.02, 1. - gPad->GetTopMargin() - 0.08, 0.6,
9730 1. - gPad->GetTopMargin() - 0.08);
9731 l->SetBorderSize(0);
9732 if (l->GetTextSize() == 0)
9733 l->SetTextSize(gStyle->GetTitleYSize());
9734
9736 // l->SetMargin(0);
9737 l->SetFillStyle(0);
9738 l->SetName(name);
9739 l->Draw();
9740 l->ConvertNDCtoPad();
9741 return l;
9742}
9743
9744TLegend *getLegend(bool create = true, bool doPaint = false)
9745{
9746 if (auto p = dynamic_cast<TLegend *>(gPad->GetPrimitive("legend")); p) {
9747 double x;
9748 double y;
9749 double w = p->GetX2NDC() - p->GetX1NDC();
9750 double h = p->GetY2NDC() - p->GetY1NDC();
9751 if (doPaint)
9752 gPad->PaintModified(); //-- slows down x11 so trying to avoid
9753 if (TopRightPlaceBox(dynamic_cast<TPad *>(gPad), p, w, h, x, y)) {
9754 // squash inside the frame ..
9755 // std::cout << gPad->GetName() << ":" << x << " , " << y << " , " << w << " , " << h << std::endl;
9756 x = std::max(x, (gPad->GetLeftMargin() + 0.02));
9757 y = std::max(y, (gPad->GetBottomMargin() + 0.02));
9758 x = std::min(x, (1. - gPad->GetRightMargin() - 0.02) - w);
9759 y = std::min(y, (1. - gPad->GetTopMargin() - 0.02) - h);
9760 h = std::min(h, (1. - gPad->GetTopMargin() - 0.02) - y);
9761 w = std::min(w, (1. - gPad->GetRightMargin() - 0.02) - x);
9762 // std::cout << gPad->GetName() << ":" << x << " , " << y << " , " << h << " , " << w << std::endl;
9763 p->SetX1NDC(x);
9764 p->SetY1NDC(y);
9765 p->SetX2NDC(x + w);
9766 p->SetY2NDC(y + h);
9767 gPad->Modified();
9768 }
9769 return p;
9770 }
9771 // look for a parent pad called 'legend' and create it there if existing
9772 auto p = gPad;
9773 while ((p != p->GetMother()) && (p = p->GetMother())) {
9774 if (auto q = dynamic_cast<TVirtualPad *>(p->GetPrimitive("legend")); q) {
9775 q->Modified();
9776 p = q;
9777 break;
9778 }
9779 }
9780 auto tmpPad = gPad;
9781 TLegend *l = nullptr;
9782 if (p && strcmp(p->GetName(), "legend") == 0) {
9783 if (l = dynamic_cast<TLegend *>(p->GetPrimitive("legend")); l || !create)
9784 return l;
9785 p->cd();
9786 l = new TLegend(gPad->GetLeftMargin(), 1. - gPad->GetTopMargin(), 1. - gPad->GetRightMargin(),
9787 gPad->GetBottomMargin());
9788 l->SetBorderSize(1); // ensure has a border
9789 } else {
9790 if (!create)
9791 return nullptr;
9792 l = new TLegend(0.6, 1. - gPad->GetTopMargin() - 0.08, 0.75, 1. - gPad->GetTopMargin() - 0.08);
9793 l->SetBorderSize(0);
9794 // legend text will be required to match y-axis
9795 if (l->GetTextSize() == 0) {
9796 l->SetTextSize(gStyle->GetTitleYSize());
9797 l->SetTextFont(gStyle->GetTitleFont("Y"));
9798 }
9799 }
9801 // l->SetMargin(0);
9802 l->SetFillStyle(0);
9803 l->SetName("legend");
9804 l->Draw();
9805 l->ConvertNDCtoPad();
9806 tmpPad->cd();
9807 return l;
9808}
9809
9810std::string formatLegendString(const std::string &s)
9811{
9812 auto i = s.find("\n");
9813 if (i == std::string::npos) {
9814 return s;
9815 }
9816 return std::string("#splitline{") + s.substr(0, i) + "}{" + formatLegendString(s.substr(i + 1)) + "}";
9817}
9818
9819void addLegendEntry(TObject *o, const char *title, const char *opt)
9820{
9821 auto l = getLegend();
9822 if (!l)
9823 return;
9824 // check for entry already existing with same title
9825 for (auto a : *l->GetListOfPrimitives()) {
9826 if (formatLegendString(title) == dynamic_cast<TLegendEntry *>(a)->GetLabel())
9827 return;
9828 }
9829 if (l->GetListOfPrimitives()->GetEntries() > 20)
9830 return; // todo: create an 'other' entry?
9831
9832 auto e = l->AddEntry(o, formatLegendString(title).c_str(), opt);
9833 // move to top of the legend (we add things in at the top)
9834 l->GetListOfPrimitives()->RemoveLast();
9835 l->GetListOfPrimitives()->AddFirst(e);
9836 if (auto nObj = l->GetListOfPrimitives()->GetEntries(); nObj > 0) {
9837 // each entry takes up 0.05 ... maximum of N*(N+4) (where N is # cols) before next column
9838 int nn = l->GetNColumns();
9839 nn *= (nn + 4);
9840 if (nObj > 1 && (nObj % nn) == 1) {
9841 l->SetNColumns(l->GetNColumns() + 1);
9842 if (l->GetBorderSize() == 0) {
9843 l->SetX1NDC(l->GetX2NDC() - 0.15 * l->GetNColumns());
9844 }
9845 }
9846 if (l->GetBorderSize() == 0) {
9847 l->SetY1NDC(l->GetY2NDC() - 0.05 * gPad->GetHNDC() * std::ceil((double(nObj) / l->GetNColumns())));
9848 }
9849 }
9850
9851 getLegend(); // to mark modified
9852}
9853
9854// this exists to avoid calling update excessively because it slows down x11 ... but still
9855// need to call update twice if have a legend drawn in order to relocate it.
9857public:
9858 PadRefresher(TVirtualPad *p) : fPad(p) { nExisting++; }
9860 {
9861 if (fPad) {
9862 getLegend(false, true);
9863 fPad->GetCanvas()->Paint();
9864 fPad->GetCanvas()->Update();
9865#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 30, 00)
9866 fPad->GetCanvas()->ResetUpdated(); // stops previous canvas being replaced in a jupyter notebook
9867#endif
9868 fPad->cd();
9869 }
9870 nExisting--;
9871 }
9872 TVirtualPad *fPad = nullptr;
9873 static int nExisting;
9874};
9875
9877
9879{
9880 // in order to catch exceptions to prevent crash of GUI, do this:
9881 if (gROOT->FromPopUp()) {
9882 gROOT->SetFromPopUp(false);
9883 try {
9884 Draw(opt);
9885 } catch (const std::exception &e) {
9886 new TGMsgBox(
9887 gClient->GetRoot(),
9888 (gROOT->GetListOfBrowsers()->At(0))
9889 ? dynamic_cast<TGWindow *>(static_cast<TBrowser *>(gROOT->GetListOfBrowsers()->At(0))->GetBrowserImp())
9890 : gClient->GetRoot(),
9891 "Exception", e.what(),
9892 kMBIconExclamation); // deletes self on dismiss?
9893 }
9894 gROOT->SetFromPopUp(true);
9895 return;
9896 }
9897
9898 TString sOpt2(opt);
9899 sOpt2.ToLower();
9900 if (!get() && !IsFolder() && !sOpt2.Contains("x="))
9901 return;
9902
9903 if (auto mc = get<RooStats::ModelConfig>()) {
9904 xRooNode(*mc->GetPdf(), fParent).Draw(opt); // draw the pdf of the config
9905 } else if (auto ir = get<RooStats::HypoTestInverterResult>()) {
9906 xRooHypoSpace(ir).Draw(opt);
9908 return;
9909 } else if (get<RooStats::HypoTestResult>()) {
9910 if (gPad)
9911 gPad->Clear();
9912 xRooNLLVar::xRooHypoPoint(std::dynamic_pointer_cast<RooStats::HypoTestResult>(fComp)).Draw(opt);
9913 {
9914 PadRefresher p(gPad); // refreshes the pad
9915 }
9917 return;
9918 }
9919
9920 if (sOpt2 == "pcls" && get<RooRealVar>() && fParent && fParent->get<RooAbsPdf>()) {
9921 // use the first selected dataset
9922 auto _dsets = fParent->datasets();
9923 // bool _drawn=false;
9924 TString dsetName = "";
9925 for (auto &d : _dsets) {
9926 if (d->get()->TestBit(1 << 20)) {
9927 dsetName = d->get()->GetName();
9928 break;
9929 }
9930 }
9931 auto hs = fParent->nll(dsetName.Data()).hypoSpace(get<RooRealVar>()->GetName());
9932 hs.limits("cls visualize");
9933 hs.SetName(TUUID().AsString());
9934 if (ws()) {
9935 ws()->import(*hs.result());
9936 }
9937 return;
9938 }
9939
9940 if (auxFunctions.empty()) {
9941 // add the defaults: Ratio and Signif
9943 "Ratio",
9944 [](double a, double b, double) {
9945 if (a == 0)
9946 return 0.;
9947 if (b == 0 && a == 0)
9948 return 1.;
9949 return a / b;
9950 },
9951 true);
9953 "Signif",
9954 [](double n, double b, double sigma) {
9955 double t0 = 0;
9956 if (sigma <= 0.) {
9957 // use simplified expression ...
9958 t0 = 2. * (((n == 0) ? 0 : n * log(n / b)) - (n - b));
9959 } else {
9960 double sigma2 = sigma * sigma;
9961 double b_hathat = 0.5 * (b - sigma2 + sqrt(pow(b - sigma2, 2) + 4 * n * sigma2));
9962 // double s_hat = n - m;
9963 // double b_hat = m;
9964 t0 = 2. * (((n == 0) ? 0 : n * log(n / b_hathat)) + b_hathat - n + pow(b - b_hathat, 2) / (2. * sigma2));
9965 }
9966 if (t0 < 0)
9967 return 0.; // can happen from numerical precision
9968 return (n >= b) ? sqrt(t0) : -sqrt(t0);
9969 },
9970 false);
9971 }
9972
9973 TString sOpt(opt);
9974
9975 RooAbsLValue *v = nullptr;
9976 std::vector<double> xPoints;
9977 if (sOpt2.Contains("x=")) {
9978 // specifying a particular var to scan over ...
9979 int _idx = sOpt2.Index("x=");
9980 int _eidx = sOpt2.Index(';', _idx);
9981 TString varPart = sOpt(_idx + 2, (_eidx < 0 ? sOpt2.Length() : _eidx) - (_idx + 2));
9983 // if varName is of form str(num,num,num) then can infer scan points
9984 if (auto _idx2 = varPart.Index("("); _idx2 > 0) {
9985 varName = varPart(0, _idx2);
9986 TStringToken pattern(TString(varPart(_idx2 + 1, varPart.Length() - _idx2 - 2)), ",");
9987 double min(0);
9988 double max(0);
9989 int nBins = 0;
9990 int ii = 0;
9991 while (pattern.NextToken()) {
9992 TString s = pattern;
9993 if (ii == 0) {
9994 nBins = s.Atoi();
9995 } else if (ii == 1) {
9996 min = s.Atof();
9997 } else if (ii == 2) {
9998 max = s.Atof();
9999 }
10000 ii++;
10001 }
10002 if (nBins > 100)
10003 nBins = 100; // limit scanning to 100 points
10004 if (nBins > 1) {
10005 for (double x = min; x <= max; x += (max - min) / (nBins - 1)) {
10006 xPoints.push_back(x);
10007 }
10008 } else if (nBins == 1)
10009 xPoints.push_back((min + max) / 2.);
10010 }
10011 v = getObject<RooAbsLValue>(varName.Data()).get();
10012 if (!v) {
10013 throw std::runtime_error(TString::Format("Could not find variable %s", varName.Data()));
10014 }
10015 if (xPoints.empty() && !obs().find(varName.Data()) &&
10016 dynamic_cast<RooAbsRealLValue *>(v)) { // will draw obs as regular (e.g. hist)
10017 double tmp = static_cast<RooAbsRealLValue *>(v)->getVal();
10018 for (int i = 0; i < v->numBins(GetName()); i++) {
10019 v->setBin(i, GetName());
10020 xPoints.push_back(static_cast<RooAbsRealLValue *>(v)->getVal());
10021 }
10022 static_cast<RooAbsRealLValue *>(v)->setVal(tmp);
10023 }
10024 sOpt2 = TString(sOpt2(0, _idx)) + sOpt2(_idx + 2 + varPart.Length() + 1, sOpt2.Length());
10025 sOpt = TString(sOpt(0, _idx)) + sOpt(_idx + 2 + varPart.Length() + 1, sOpt.Length());
10026 }
10027 TString forceNames = "";
10028 if (sOpt2.Contains("force")) {
10029 // force plots show how much NLL changes wrt to a change of variables
10030 if (get<RooRealVar>() && fParent && fParent->get<RooAbsPdf>()) {
10031 // assume want force of this parameter from the parent pdf
10032 TString ff = sOpt(sOpt2.Index("force"), sOpt2.Index("force") + 5);
10033 sOpt.ReplaceAll(ff, TString::Format("force%s", get()->GetName()));
10034 fParent->Draw(sOpt);
10035 return;
10036 } else if (get<RooAbsPdf>()) {
10037 // extract the parameter(s) to calculate force for
10038 forceNames = sOpt(sOpt2.Index("force") + 5, sOpt2.Length());
10039 sOpt = sOpt(0, sOpt2.Index("force"));
10040 sOpt2 = sOpt2(0, sOpt2.Index("force"));
10041 } else {
10042 Error("Draw", "Can only compute forces with PDFs");
10043 return; // don't throw because will cause browser to exit if done from there
10044 }
10045 }
10046 bool hasOverlay = sOpt2.Contains("overlay");
10047 TString overlayName = "";
10048 if (hasOverlay) {
10049 // whatever follows overlay is the variation name
10050 overlayName = sOpt(sOpt2.Index("overlay") + 7, sOpt2.Length());
10051 sOpt = sOpt(0, sOpt2.Index("overlay"));
10052 sOpt2 = sOpt2(0, sOpt2.Index("overlay"));
10053 }
10054 if (sOpt2.Contains("ratio") && !sOpt2.Contains("auxratio"))
10055 sOpt += "auxRatio";
10056 if (sOpt2.Contains("significance") && !sOpt2.Contains("auxsignif"))
10057 sOpt += "auxSignif";
10058
10059 std::string auxPlotTitle;
10060 for (auto &[k, _] : auxFunctions) {
10061 if (sOpt.Contains(TString::Format("aux%s", k.c_str()))) {
10062 auxPlotTitle = k;
10063 }
10064 sOpt.ReplaceAll(TString::Format("aux%s", k.c_str()), "");
10065 }
10066
10067 sOpt.ToLower();
10068 sOpt.ReplaceAll("ratio", "");
10069 sOpt.ReplaceAll("significance", ""); // remove old option if still given
10070 bool nostack = sOpt.Contains("nostack");
10071 sOpt.ReplaceAll("nostack", "");
10072 bool hasSame = sOpt.Contains("same");
10073 sOpt.ReplaceAll("same", "");
10074 bool hasGoff = sOpt.Contains("goff");
10075 sOpt.ReplaceAll("goff", "");
10076 bool hasFR = sOpt.Contains("pull") && !get<RooFitResult>();
10077 sOpt.ReplaceAll("pull", "");
10078 bool hasText = sOpt.Contains("text");
10079 bool hasTexte = sOpt.Contains("texte");
10080 bool hasErrorOpt = sOpt.Contains("e");
10081 sOpt.ReplaceAll("e", "");
10082 if (hasTexte) {
10083 sOpt.ReplaceAll("txt", "texte");
10084 } else if (hasText) {
10085 sOpt.ReplaceAll("txt", "text");
10086 }
10087 if (auxPlotTitle == "Signif")
10088 hasErrorOpt = true; // must calculate error to calculate significance
10089 if (hasOverlay)
10090 hasSame = true; // when overlaying must be putting on same
10091
10092 TVirtualPad *pad = gPad;
10093
10094 TH1 *hAxis = nullptr;
10095
10096 auto clearPad = []() {
10097 gPad->Clear();
10098 if (gPad->GetNumber() == 0) {
10099 gPad->SetBottomMargin(gStyle->GetPadBottomMargin());
10100 gPad->SetTopMargin(gStyle->GetPadTopMargin());
10101 gPad->SetLeftMargin(gStyle->GetPadLeftMargin());
10102 gPad->SetRightMargin(gStyle->GetPadRightMargin());
10103 }
10104 // if (gPad == gPad->GetCanvas()) {
10105 // gPad->GetCanvas()->SetCanvasSize( gPad->GetCanvas()->GetWindowWidth() - 4,
10106 // gPad->GetCanvas()->GetWindowHeight() - 28 );
10107 // }
10108 };
10109
10110 if (!hasSame || !pad) {
10111 if (!pad) {
10113 pad = gPad;
10114 }
10115
10116 } else {
10117 // get the histogram representing the axes
10118 hAxis = dynamic_cast<TH1 *>(pad->GetPrimitive("axis"));
10119 if (!hAxis) {
10120 for (auto o : *pad->GetListOfPrimitives()) {
10121 if (hAxis = dynamic_cast<TH1 *>(o); hAxis)
10122 break;
10123 }
10124 }
10125 if (hAxis && !v) {
10126 v = getObject<RooAbsLValue>(hAxis->GetXaxis()->IsAlphanumeric() ? hAxis->GetXaxis()->GetTimeFormatOnly()
10127 : hAxis->GetXaxis()->GetName())
10128 .get();
10129 }
10130 }
10131
10132 if (!hasSame) {
10133 if (gPad != gPad->GetCanvas()) {
10134 gPad->SetName(GetName()); // only rename the pad if its not the parent canvas
10135 }
10136 gPad->SetTitle(GetTitle());
10137 }
10138
10140
10141 auto adjustYRange = [&](double min, double max, TH1 *hh = nullptr, bool symmetrize = false) {
10142 if (!hh)
10143 hh = hAxis;
10144 // give max and min a buffer ...
10145 max += gStyle->GetHistTopMargin() * (max - min);
10146 if (min > 0)
10147 min = std::max(min * 0.9, min - gStyle->GetHistTopMargin() * (max - min));
10148 if (hh) {
10149 double ymin = hh->GetMinimum();
10150 double ymax = hh->GetMaximum();
10151 if (hh->GetMaximumStored() == -1111)
10152 ymax += gStyle->GetHistTopMargin() * (ymax - ymin);
10153 if (hh->GetMinimumStored() == -1111) {
10154 if (gStyle->GetHistMinimumZero() && ymax >= 0) {
10155 ymin = 0;
10156 } else if (ymin < 0) {
10157 ymin -= gStyle->GetHistTopMargin() * (ymax - ymin);
10158 } else {
10159 ymin = std::max(ymin * 0.9, ymin - gStyle->GetHistTopMargin() * (ymax - ymin));
10160 }
10161 // see TGLPlotPainter to complete the mimic, but we leave off here truncating @ 0 if ymax>0
10162 }
10163 // make ymax at least 3x bigger than biggest error if has error
10164 if (hh->GetSumw2()) {
10165 double smallestErrDown3 = -std::numeric_limits<double>::infinity();
10166 double smallestErrUp3 = std::numeric_limits<double>::infinity();
10167 for (int i = 1; i <= hh->GetNbinsX(); i++) {
10168 smallestErrDown3 = std::max(smallestErrDown3, hh->GetBinContent(i) - 3 * hh->GetBinError(i));
10169 smallestErrUp3 = std::min(smallestErrUp3, hh->GetBinContent(i) + 3 * hh->GetBinError(i));
10170 }
10171 max = std::max(max, smallestErrUp3);
10172 min = std::min(min, smallestErrDown3);
10173 }
10174 bool change = false;
10175 if (min < ymin) {
10176 ymin = min;
10177 change = true;
10178 }
10179 if (max > ymax) {
10180 ymax = max;
10181 change = true;
10182 }
10183 if (change) {
10184 // note: unfortunately when user 'unzooms' y axis it resets stored minimum to -1111, so lose range
10185 if (symmetrize) {
10186 double down = hh->GetBinContent(1) - ymin;
10187 double up = ymax - hh->GetBinContent(1);
10188 if (down > up) {
10189 ymax = hh->GetBinContent(1) + down;
10190 } else {
10191 ymin = hh->GetBinContent(1) - up;
10192 }
10193 }
10194 if (hh == hAxis && pad && !pad->GetLogy() && ymin > 0 && (log10(ymax) - log10(max)) >= 3) {
10195 // auto-log the pad
10196 pad->SetLogy();
10197 }
10198 if (hh == hAxis && pad && ymin == 0 && pad->GetLogy()) {
10199 ymin = 1e-2;
10200 }
10201 if (ymin == 0 && ymax > 10)
10202 ymin = 0.1; // adjust min so if user activates log scale it isn't bad
10203 hh->SetMinimum(ymin);
10204 hh->SetMaximum(ymax);
10205 hh->GetYaxis()->Set(1, ymin, ymax);
10206 hh->SetAxisRange(ymin, ymax, "Y");
10207 }
10208 }
10209 };
10210
10211 auto graphMinMax = [](TGraphAsymmErrors *gr) {
10212 double ymax = -std::numeric_limits<double>::infinity();
10213 double ymin = std::numeric_limits<double>::infinity();
10214 for (int i = 0; i < gr->GetN(); i++) {
10215 ymax = std::max(ymax, gr->GetPointY(i) + gr->GetErrorYhigh(i));
10216 ymin = std::min(ymin, gr->GetPointY(i) - gr->GetErrorYlow(i));
10217 }
10218 return std::make_pair(ymin, ymax);
10219 };
10220
10221 if (!xPoints.empty()) {
10222 // create a graph using GetContent
10224 out->SetName(GetName());
10225 out->SetTitle(GetTitle());
10226 out->SetFillColor(out->GetLineColor());
10227 out->SetMarkerStyle(0);
10228 out->SetFillStyle(hasErrorOpt ? 3005 : 0);
10229 double tmp = static_cast<RooAbsRealLValue *>(v)->getVal();
10230 for (auto &x : xPoints) {
10231 static_cast<RooAbsRealLValue *>(v)->setVal(x);
10232 out->AddPoint(x, GetContent());
10233 if (hasErrorOpt) {
10234 out->SetPointEYlow(out->GetN() - 1, GetError());
10235 out->SetPointEYhigh(out->GetN() - 1, out->GetErrorYlow(out->GetN() - 1)); // symmetric error for now
10236 }
10237 }
10238 static_cast<RooAbsRealLValue *>(v)->setVal(tmp);
10239 out->GetHistogram()->GetXaxis()->SetTitle(static_cast<RooAbsRealLValue *>(v)->GetTitle());
10240 out->SetBit(kCanDelete);
10241 out->Draw(TString(hasSame ? "L" : "AL") + (hasErrorOpt ? "3" : ""));
10242 return;
10243 }
10244
10245 if (hasFR) {
10246 // drawing the fitresult as a pull plot on a subpad, and rest of the draw elsewhere
10247 clearPad();
10248 pad->Divide(1, 2, 1e-9, 1e-9); //,0,0);
10249 pad->GetPad(1)->SetPad(0, 0.2, 1, 1);
10250 pad->GetPad(2)->SetPad(0, 0, 1, 0.2);
10251 TString optNoFR(opt);
10252 optNoFR.ReplaceAll("pull", "");
10253 pad->cd(1);
10254 Draw(optNoFR);
10255 pad->cd(2);
10256 auto _fr = fitResult();
10257 _fr.Draw();
10258 // switch into subpad
10259 gPad->cd(1);
10260 gPad->SetFillColor(kGray);
10261 gPad->GetFrame()->SetFillColor(kWhite);
10262 gPad->GetFrame()->SetFillStyle(1001);
10263 gPad->SetTopMargin(0);
10264 gPad->SetBottomMargin(0);
10265 gPad->SetName("pull");
10266 // split the pull graph into individual points -- for benefit of GUI status bar
10267 auto pullGraph = dynamic_cast<TGraphAsymmErrors *>(gPad->GetPrimitive("pulls"));
10268 if (!pullGraph) {
10269 Error("Draw", "Couldn't find pull graph");
10270 return;
10271 }
10272 pullGraph->SetName("nominal");
10273 TMultiGraph *mg = new TMultiGraph;
10274 mg->SetName("editables");
10275
10276 auto scaleHist = static_cast<TH1 *>(pullGraph->FindObject("scales"));
10277 if (!scaleHist)
10278 throw std::runtime_error("Could not find scales in fit result");
10279
10280 for (auto i = 0; i < pullGraph->GetN(); i++) {
10281 auto g = new TGraphAsymmErrors;
10282 g->SetName(scaleHist->GetXaxis()->GetBinLabel(i + 1));
10283 auto _p = dynamic_cast<RooRealVar *>(_fr.get<RooFitResult>()->floatParsFinal().find(g->GetName()));
10284 if (!_p) {
10285 Warning("Draw", "Found a non-var in the floatParsFinal list: %s - this shouldn't happen", g->GetName());
10286 continue;
10287 }
10288 g->SetTitle(TString::Format(
10289 "%s=%g +/- %s [%g,%g]", strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName(), _p->getVal(),
10290 _p->hasAsymError() ? TString::Format("(%g,%g)", _p->getAsymErrorHi(), _p->getAsymErrorLo()).Data()
10291 : TString::Format("%g", _p->getError()).Data(),
10292 scaleHist->GetBinContent(i + 1), scaleHist->GetBinError(i + 1)));
10293 g->SetPoint(0, pullGraph->GetPointX(i), pullGraph->GetPointY(i));
10294 g->SetPointEYhigh(0, pullGraph->GetErrorYhigh(i));
10295 g->SetPointEYlow(0, pullGraph->GetErrorYlow(i));
10296 g->SetEditable(true);
10297 g->SetHighlight(true);
10298 g->SetMarkerStyle(20);
10299 g->SetMarkerSize(0.5);
10300 mg->Add(g);
10301 }
10302 // gPad->GetListOfPrimitives()->Remove(pullGraph); delete pullGraph;
10303 mg->Draw("z0p");
10304 mg->SetBit(kCanDelete);
10305 auto _thisClone = new xRooNode("node", fComp, fParent);
10306 _thisClone->SetBit(kCanDelete);
10307 _thisClone->AppendPad();
10308
10309 // ensure statusbar visible for interactive plot
10310 // turned this off for now ... as not needed if doing through browser, status bar already there
10311 // if (gPad->GetCanvas() && !gPad->GetCanvas()->TestBit(TCanvas::kShowEventStatus)) {
10312 // gPad->GetCanvas()->ToggleEventStatus();
10313 // }
10314 gPad->AddExec("interactivePull", TString::Format("%s::Interactive_Pull()", ClassName()));
10315
10316 pad->cd();
10317 return;
10318 }
10319
10320 if (auto _simPdf = get<RooSimultaneous>();
10321 _simPdf && !(v && strcmp(_simPdf->indexCat().GetName(), dynamic_cast<TObject *>(v)->GetName()) == 0)) {
10322 auto _channels = bins();
10323 int _size = 0;
10324 for (auto &_v : _channels) {
10325 if (!_v->IsHidden())
10326 _size++;
10327 }
10328 if (!hasSame) {
10329 if (_size > 4) {
10330 // add a pad for the common legends
10331 _size++;
10332 }
10333 clearPad();
10334 pad->SetBorderSize(0);
10335 // if (pad->GetCanvas() == pad) {
10336 // if(_size>4) {
10337 // int n = _size;
10338 // Int_t w = 1, h = 1;
10339 // if (pad->GetCanvas()->GetWindowWidth() > pad->GetCanvas()->GetWindowHeight()) {
10340 // w = TMath::Ceil(TMath::Sqrt(n));
10341 // h = TMath::Floor(TMath::Sqrt(n));
10342 // if (w*h < n) w++;
10343 // } else {
10344 // h = TMath::Ceil(TMath::Sqrt(n));
10345 // w = TMath::Floor(TMath::Sqrt(n));
10346 // if (w*h < n) h++;
10347 // }
10348 // // adjust the window size to display only 4 in the window, with scroll bars
10349 // pad->GetCanvas()->SetCanvasSize( w*((pad->GetCanvas()->GetWindowWidth()-4)/2.) -16
10350 // ,h*((pad->GetCanvas()->GetWindowHeight()-28)/2.) - 16 );
10351 // } else {
10352 // //pad->GetCanvas()->Set(
10353 // w*(pad->GetCanvas()->GetWindowWidth()/2.),h*(pad->GetCanvas()->GetWindowHeight()/2.)) )
10354 // }
10355 // }
10356 int ncols = _simPdf->getStringAttribute("ncols") ? TString(_simPdf->getStringAttribute("ncols")).Atoi() : 0;
10357 if (ncols > 0) {
10358 dynamic_cast<TPad *>(pad)->Divide(ncols, std::ceil(double(_size) / ncols), 1e-9, 1e-9);
10359 } else {
10360 dynamic_cast<TPad *>(pad)->DivideSquare(_size, 1e-9, 1e-9);
10361 }
10362 if (_size > 5) {
10363 auto _pad = pad->GetPad(_size); // will use as the legend pad
10364 _pad->SetName("legend");
10365 // stretch the pad all the way to the left
10366 _pad->SetPad(_pad->GetXlowNDC(), _pad->GetYlowNDC(), 1.0, _pad->GetYlowNDC() + _pad->GetHNDC());
10367 // and make all the remaining pads transparent
10368 int x = _size;
10369 while (pad->GetPad(x + 1)) {
10370 pad->GetPad(x + 1)->SetFillStyle(0);
10371 x++;
10372 }
10373 }
10374 }
10375 int i = 0;
10376 auto &chanVar = const_cast<RooAbsCategoryLValue &>(_simPdf->indexCat());
10377 // auto _idx = chanVar.getIndex();
10378 auto _range = GetRange();
10379 std::vector<TString> chanPatterns;
10380 if (_range && strlen(_range)) {
10381 TStringToken pattern(_range, ",");
10382 while (pattern.NextToken()) {
10383 chanPatterns.emplace_back(pattern);
10384 }
10385 }
10386 for (auto &_v : _channels) {
10387 if (_v->IsHidden())
10388 continue;
10389 TString s(_v->GetName());
10390 pad->cd(++i);
10391 gPad->SetName(s);
10392 TString cName = s(s.Index('=') + 1, s.Length());
10393 chanVar.setLabel(cName);
10394 bool inRange = chanPatterns.empty();
10395 for (auto &p : chanPatterns) {
10396 if (chanVar.inRange(p)) {
10397 inRange = true;
10398 break;
10399 }
10400 }
10401 if (!inRange || !_v->get<RooAbsReal>()->isSelectedComp())
10402 gPad->SetFillColor(kGray);
10403 if (!hasSame && _size > 1 && (gStyle->GetTitleFont("Y") % 10) == 3)
10404 gPad->SetLeftMargin(std::min(gPad->GetLeftMargin() * (1. / gPad->GetWNDC()), 0.3));
10405 _v->Draw(opt);
10407 }
10408 pad->cd(0);
10409 gPad->Modified();
10410 // gPad->Update();
10411 return;
10412 }
10413
10414 if (!get() || get<RooArgList>()) {
10415 // is a group draw all the submembers
10416 browse();
10417 int _size = 0;
10418 // int _size = _channels.size(); // size(); if (find("!.vars")) _size--;
10419 for (auto &_v : *this) {
10420 if (_v->IsHidden())
10421 continue;
10422 if (strcmp(GetName(), ".vars") == 0) {
10423 // auto hide obs and "1" and const var
10424 if (_v->get<RooAbsArg>()->getAttribute("obs"))
10425 continue;
10426 if (strcmp(_v->get()->GetName(), "1") == 0 || strcmp(_v->get()->GetName(), "ONE") == 0 ||
10427 TString(_v->get()->GetName()).BeginsWith("binWidth_"))
10428 continue;
10429 if (_v->get()->InheritsFrom("RooConstVar"))
10430 continue;
10431 }
10432 TString s(_v->GetName());
10433 if (s.BeginsWith(".") || s.BeginsWith("!"))
10434 continue;
10435 _size++;
10436 }
10437 if (!hasSame) {
10438 clearPad();
10439 pad->SetBorderSize(0);
10440 dynamic_cast<TPad *>(pad)->DivideSquare(_size, 1e-9, 1e-9);
10441 }
10442 int i = 0;
10443 for (auto &_v : *this) {
10444 if (_v->IsHidden())
10445 continue;
10446 if (strcmp(GetName(), ".vars") == 0) {
10447 // auto hide obs and "1" and const var
10448 if (_v->get<RooAbsArg>()->getAttribute("obs"))
10449 continue;
10450 if (strcmp(_v->get()->GetName(), "1") == 0 || strcmp(_v->get()->GetName(), "ONE") == 0 ||
10451 TString(_v->get()->GetName()).BeginsWith("binWidth_"))
10452 continue;
10453 if (_v->get()->InheritsFrom("RooConstVar"))
10454 continue;
10455 }
10456 TString s(_v->GetName());
10457 if (s.BeginsWith(".") || s.BeginsWith("!"))
10458 continue;
10459 pad->cd(++i);
10460 gPad->SetName(s);
10461 if (!hasSame && _size > 1 && (gStyle->GetTitleFont("Y") % 10) == 3)
10462 gPad->SetLeftMargin(std::min(gPad->GetLeftMargin() * (1. / gPad->GetWNDC()), 0.3));
10463 _v->Draw(opt);
10464 // pad->Modified();//pad->Update();
10466 }
10467 pad->cd(0);
10468 gPad->Modified();
10469 // gPad->Update();
10470 return;
10471 }
10472
10473 if (get()->InheritsFrom("RooProdPdf")) {
10474 // draw the main pdf, if there is one...
10475 auto _mainChild = mainChild();
10476 if (_mainChild) {
10477 _mainChild.Draw(opt);
10478 gPad->SetName(GetName());
10479 return;
10480 }
10481 }
10482
10483 if (auto fr = get<RooFitResult>(); fr) {
10484 if (sOpt.Contains("corr")) {
10485 // do correlation matrix
10486 // if a number follows 'corr', reduce the correlation matrix to show only the most extreme correlations
10487 int numCorrs = TString(sOpt(sOpt.Index("corr") + 4, sOpt.Length())).Atoi();
10488 if (numCorrs == 0)
10489 numCorrs = fr->correlationMatrix().GetNcols();
10490
10491 TH2 *hist = nullptr;
10492 if (numCorrs < fr->correlationMatrix().GetNcols()) {
10493 // need to reduce
10494 std::set<std::pair<double, size_t>> maxCorrs;
10495 for (int i = 0; i < fr->correlationMatrix().GetNcols(); i++) {
10496 double maxCorr = 0;
10497 for (int j = 0; j < fr->correlationMatrix().GetNcols(); j++) {
10498 if (j == i)
10499 continue;
10500 maxCorr = std::max(std::abs(fr->correlationMatrix()(i, j)), maxCorr);
10501 }
10502 maxCorrs.insert({maxCorr, i});
10503 }
10504 std::vector<size_t> topN;
10505 int c = 0;
10506 for (auto itr = maxCorrs.rbegin(); itr != maxCorrs.rend(); ++itr) {
10507 topN.push_back(itr->second);
10508 c++;
10509 if (c == numCorrs)
10510 break;
10511 }
10512 hist = new TH2D(fr->GetName(), TString::Format("%s - Top %d correlations", fr->GetTitle(), numCorrs),
10514 for (size_t i = 0; i < topN.size(); i++) {
10515 hist->GetXaxis()->SetBinLabel(i + 1, fr->floatParsFinal().at(topN.at(i))->GetTitle());
10516 hist->GetYaxis()->SetBinLabel(numCorrs - i, fr->floatParsFinal().at(topN.at(i))->GetTitle());
10517 for (size_t j = 0; j < topN.size(); j++) {
10518 hist->Fill(i + 0.5, numCorrs - j - 0.5, fr->correlationMatrix()(topN.at(i), topN.at(j)));
10519 }
10520 }
10521 hist->SetMinimum(-1);
10522 hist->SetMaximum(1);
10523
10524 } else {
10525 hist = fr->correlationHist(fr->GetName());
10526 hist->SetTitle(fr->GetTitle());
10527 }
10528
10529 hist->SetBit(kCanDelete);
10530 hist->Scale(100);
10531 hist->SetStats(false);
10532 hist->SetDirectory(nullptr);
10534 gStyle->SetPaintTextFormat(".1f");
10535 hist->GetXaxis()->SetTickSize(0);
10536 hist->GetYaxis()->SetTickSize(0);
10537 hist->SetMinimum(-100);
10538 hist->Draw(sOpt);
10540 gPad->SetGrid(1, 1);
10541 gPad->SetLogy(0);
10542 gPad->SetLogx(0);
10543 return;
10544 }
10545
10546 if (sOpt.Contains("brakdown")) { // e will have been removed above
10547
10548 // breakdown is quadrature difference between total error and conditional error
10549 // group by 'group' attribute
10550
10551 std::string poiName;
10552 if (sOpt.Contains("brakdown:")) {
10553 TString sOpt3(opt);
10554 poiName = sOpt3(sOpt3.Index("breakdown:") + 10, sOpt3.Length());
10555 } else {
10556 std::unique_ptr<RooAbsCollection> _poi(fr->floatParsFinal().selectByAttrib("poi", true));
10557 if (_poi->empty()) {
10558 throw std::runtime_error("No floating poi in the fit");
10559 } else if (_poi->size() != 1) {
10560 throw std::runtime_error("Multiple poi in the fit");
10561 }
10562 poiName = _poi->first()->GetName();
10563 }
10564 RooRealVar *poi = dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(poiName.c_str()));
10565 if (!poi) {
10566 throw std::runtime_error(TString::Format("Cannot find parameter %s", poiName.c_str()));
10567 }
10568 std::set<std::string> groups;
10569 for (auto p : fr->floatParsFinal()) {
10570 if (p == poi) {
10571 continue;
10572 } else if (p->getStringAttribute("group")) {
10573 groups.insert(p->getStringAttribute("group"));
10574 } else {
10575 groups.insert(p->GetTitle());
10576 }
10577 }
10578
10579 auto roundedVal = xRooFit::matchPrecision(std::pair(poi->getVal(), poi->getError()));
10580
10581 TPie *pie = new TPie(TString::Format("breakdown:%s", poi->GetName()),
10582 TString::Format("%s: %g #pm %g", poi->GetTitle(), roundedVal.first, roundedVal.second),
10583 groups.size() + 1);
10584
10585 // for display of errors will go to one extra dp ...
10586 roundedVal.second *= .1;
10587
10588 // do breakdown by removing parameters in blocks according to groups and seeing impact on variance
10589 // this will give the correct sum but will be order-dependent if there are correlations between
10590 // groups. therefore we will stick with group-by-group
10591 // RooArgList pars(fr->floatParsFinal()); // pars to not condition on
10592 // double variance = pow(dynamic_cast<RooRealVar*>(poi)->getError(),2);
10593 int i = 0;
10594 for (auto group : groups) {
10595 RooArgList pars(fr->floatParsFinal()); // pars to not condition on
10596 double variance = pow(dynamic_cast<RooRealVar *>(poi)->getError(), 2);
10597 for (auto p : fr->floatParsFinal()) {
10598 if (p == poi) {
10599 continue;
10600 } else if ((p->getStringAttribute("group") && group == p->getStringAttribute("group")) ||
10601 (!p->getStringAttribute("group") && group == p->GetTitle())) {
10602 // conditioning on this parameter ... remove from pars list
10603 pars.remove(*p);
10604 }
10605 }
10606 int idx = pars.index(poiName.c_str());
10607 double reducedVar = fr->conditionalCovarianceMatrix(pars)(idx, idx);
10608 if (reducedVar > variance) {
10609 Warning("Draw", "breakdown group %s variance bigger than preceding?", group.c_str());
10610 pie->SetEntryVal(i, 0);
10611 pie->SetEntryLabel(i, TString::Format("%s: NaN", group.c_str()));
10612 } else {
10613 pie->SetEntryVal(i, variance - reducedVar);
10615 std::pair(sqrt(variance - reducedVar), roundedVal.second)); // r.first will be the rounded error
10616 if (r.first > 0) {
10617 pie->SetEntryLabel(i, TString::Format("%s: %g", group.c_str(), r.first));
10618 } else {
10619 pie->SetEntryLabel(i, group.c_str()); // suppress labels for negligible errors.
10620 }
10621 }
10622 pie->SetEntryFillColor(i, TColor::GetColorPalette(TColor::GetNumberOfColors() * i / pie->GetEntries()));
10623 // variance = reducedVar;
10624 i++;
10625 }
10626 // remaining variance is statistical=
10627 double variance = fr->conditionalCovarianceMatrix(*poi)(0, 0);
10628 auto r =
10629 xRooFit::matchPrecision(std::pair(sqrt(variance), roundedVal.second)); // r.first will be the rounded error
10630 pie->SetEntryVal(i, variance);
10631 pie->SetEntryLabel(i, TString::Format("stat: %g", r.first));
10632 pie->SetEntryFillColor(i, TColor::GetColorPalette(TColor::GetNumberOfColors() * i / pie->GetEntries()));
10633 pie->SetBit(kCanDelete);
10634 pie->SetRadius(0.17);
10635 pie->SetTextSize(gStyle->GetTitleYSize());
10636 pie->Draw("NOL");
10637 return;
10638 }
10639
10640 // plot pull or impact
10642 out->SetName(TString::Format("%s_pull", fr->GetName()));
10643 out->SetTitle("Fit Result Pulls");
10644 std::vector<TString> graphLabels;
10646 ugraph->SetName(TString::Format("%s_pull_unconstrained", fr->GetName()));
10647 ugraph->SetTitle("Fit Result Pulls");
10648 std::vector<TString> ugraphLabels;
10649 std::map<std::string, double> scale;
10650 std::map<std::string, double> offset;
10651 for (auto &p : fr->floatParsFinal()) {
10652 auto _v = dynamic_cast<RooRealVar *>(p);
10653 if (!_v)
10654 continue;
10655
10656 if (std::isnan(_v->getErrorHi()) || std::isnan(_v->getErrorLo())) {
10657 Warning("Draw", "%s error is invalid", _v->GetName());
10658 }
10659
10660 // need to get constraint mean and error parameters ....
10661 // look for normal gaussian and poisson cases
10662 double prefitError = 0;
10663 double prefitVal = 0;
10664 double customScale = 0;
10665 if (auto ip =
10666 dynamic_cast<RooRealVar *>(fr->floatParsInit().find(p->GetName()))) { // handles if no prefit available
10667 prefitError = ip->getError();
10668 prefitVal = ip->getVal();
10669 };
10670
10671 std::shared_ptr<xRooNode> pConstr;
10672 if (fParent && fParent->getObject<RooRealVar>(p->GetName())) {
10673 auto _vv = fParent->getObject<RooRealVar>(p->GetName());
10674 if (_vv->hasRange("pullScale")) {
10675 customScale = (_vv->getMax("pullScale") - _vv->getMin("pullScale")) / 2.;
10676 }
10677 auto _constr = xRooNode(_vv, *this).constraints();
10678 for (auto &c : _constr) {
10679 if (c->get<RooPoisson>() || c->get<RooGaussian>()) {
10680 // require parameter to be a direct server of the constraint pdf to count if its a gaussian
10681 bool isServer = true;
10682 if (c->get<RooGaussian>()) {
10683 isServer = false;
10684 for (auto s : c->get<RooAbsArg>()->servers()) {
10685 if (strcmp(s->GetName(), p->GetName()) == 0) {
10686 isServer = true;
10687 break;
10688 }
10689 }
10690 }
10691 if (isServer) {
10692 pConstr = c;
10693 break;
10694 }
10695 }
10696 }
10697 }
10698 if (pConstr) {
10699
10700 // there will be 3 deps, one will be this par, the other two are the mean and error (or error^2 in case of
10701 // poisson
10702
10703 // std::cout << p->GetName() << " extracted " << prefitVal << " " << prefitError << " from ";
10704 // pConstr->deps().Print();
10705 pConstr->browse();
10706 if (pConstr->get<RooPoisson>() && pConstr->find(".x")) {
10707 std::string xName = pConstr->find(".x")->get()->GetName();
10708 prefitVal = pConstr->find(".x")->get<RooAbsReal>()->getVal();
10709 for (auto &_d : pConstr->vars()) {
10710 if (strcmp(p->GetName(), _d->get()->GetName()) == 0)
10711 continue;
10712 if (xName == _d->get()->GetName())
10713 continue;
10714 if (_d->get<RooAbsReal>()->getVal())
10715 prefitError = _d->get<RooAbsReal>()->getVal();
10716 }
10717 if (fr->constPars().find(pConstr->find(".x")->get()->GetName())) {
10718 // globs was saved to fr, use that instead of current value
10719 prefitVal = fr->constPars().getRealValue(pConstr->find(".x")->get()->GetName());
10720 }
10721 // prefitVal will be the global observable value, need to divide that by tau
10723 // prefiterror will be tau ... need 1/sqrt(tau) for error
10724 prefitError = 1. / sqrt(prefitError);
10725 } else if (auto _g = pConstr->get<RooGaussian>(); _g) {
10726 prefitError =
10727 (pConstr->find(".sigma")) ? pConstr->find(".sigma")->get<RooAbsReal>()->getVal() : prefitError;
10728 prefitVal =
10729 (pConstr->find(".x")) ? pConstr->find(".x")->get<RooAbsReal>()->getVal() : 0; // usually the globs
10730 if (pConstr->find(".x") && fr->constPars().find(pConstr->find(".x")->get()->GetName())) {
10731 // globs was saved to fr, use that instead of current value
10732 prefitVal = fr->constPars().getRealValue(pConstr->find(".x")->get()->GetName());
10733 }
10734 if (pConstr->find(".x") &&
10735 strcmp(p->GetName(), pConstr->find(".x")->get<RooAbsReal>()->GetName()) == 0) {
10736 // hybrid construction case,
10737 prefitVal = pConstr->find(".mean")->get<RooAbsReal>()->getVal();
10738 if (fr->constPars().find(pConstr->find(".mean")->get()->GetName())) {
10739 // globs was saved to fr, use that instead of current value
10740 prefitVal = fr->constPars().getRealValue(pConstr->find(".mean")->get()->GetName());
10741 }
10742 }
10743 }
10744
10745 if (customScale)
10747 if (prefitError == 0) {
10748 Warning("Draw", "failed to determine prefit error of %s, using post-fit error", p->GetName());
10749 prefitError = _v->getError();
10750 }
10751 out->SetPoint(out->GetN(), out->GetN(), (_v->getVal() - prefitVal) / prefitError);
10752 out->SetPointError(out->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
10753 (_v->getErrorHi()) / prefitError);
10754 graphLabels.push_back(p->GetName());
10755 scale[p->GetName()] = prefitError;
10756 offset[p->GetName()] = prefitVal;
10757 } else if (!fParent) {
10758 // no parent to determine constraints from ... prefitError=0 will be the unconstrained ones
10759 if (customScale)
10761 if (prefitError == 0) {
10762 // uses range of var
10763 prefitError = (std::max({_v->getMax() - _v->getVal(), _v->getVal() - _v->getMin(), 4.}) / 4);
10764 ugraph->SetPoint(ugraph->GetN(), ugraph->GetN(), (_v->getVal() - prefitVal) / prefitError);
10765 ugraph->SetPointError(ugraph->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
10766 (_v->getErrorHi()) / prefitError);
10767 ugraphLabels.push_back(p->GetName());
10768 } else {
10769 out->SetPoint(out->GetN(), out->GetN(), (_v->getVal() - prefitVal) / prefitError);
10770 out->SetPointError(out->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
10771 (_v->getErrorHi()) / prefitError);
10772 graphLabels.push_back(p->GetName());
10773 }
10774 scale[p->GetName()] = prefitError;
10775 offset[p->GetName()] = prefitVal;
10776
10777 } else {
10778 // unconstrained (or at least couldn't determine constraint) ... use par range if no prefit error
10779 if (customScale)
10781 if (prefitError == 0) {
10782 prefitError = (std::max({_v->getMax() - _v->getVal(), _v->getVal() - _v->getMin(), 4.}) / 4);
10783 }
10784 ugraph->SetPoint(ugraph->GetN(), ugraph->GetN(), (_v->getVal() - prefitVal) / prefitError);
10785 ugraph->SetPointError(ugraph->GetN() - 1, 0, 0, (-_v->getErrorLo()) / prefitError,
10786 (_v->getErrorHi()) / prefitError);
10787 ugraphLabels.push_back(p->GetName());
10788 scale[p->GetName()] = prefitError;
10789 offset[p->GetName()] = prefitVal;
10790 }
10791 }
10792 auto graph = out;
10793
10794 // append ugraph points to end of graph
10795 for (int i = 0; i < ugraph->GetN(); i++)
10796 ugraph->SetPointX(i, i + graph->GetN());
10797 int nUnconstrained = ugraph->GetN();
10798 TList tmpList;
10799 tmpList.SetName("tmpList");
10800 tmpList.Add(ugraph);
10801 graph->Merge(&tmpList);
10802 tmpList.RemoveAll();
10803 delete ugraph;
10804 for (auto &l : ugraphLabels) {
10805 graphLabels.push_back(l);
10806 }
10807
10808 graph->SetBit(kCanDelete);
10809 graph->SetMarkerStyle(20);
10810 graph->SetMarkerSize(0.5);
10811
10812 graph->SetMaximum(4);
10813 graph->SetMinimum(-4);
10814
10815 bool doHorizontal =
10816 (!sOpt.Contains("impact") && sOpt.Contains("v")) || (sOpt.Contains("impact") && !sOpt.Contains("himpact"));
10817
10818 std::vector<std::pair<double, std::string>> covariances;
10819 /*double poiError = 0;*/ std::string poiName;
10820 double maxImpact = 0;
10821 if (sOpt.Contains("impact")) {
10822 if (sOpt.Contains("impact:")) {
10823 TString sOpt3(opt);
10824 poiName = sOpt3(sOpt3.Index("impact:") + 7, sOpt3.Length());
10825 } else {
10826 std::unique_ptr<RooAbsCollection> _poi(fr->floatParsFinal().selectByAttrib("poi", true));
10827 if (_poi->empty()) {
10828 throw std::runtime_error("No floating poi in the fit");
10829 } else if (_poi->size() != 1) {
10830 throw std::runtime_error("Multiple poi in the fit");
10831 }
10832 poiName = _poi->first()->GetName();
10833 }
10834 RooAbsArg *poi = fr->floatParsFinal().find(poiName.c_str());
10835 if (!poi) {
10836 throw std::runtime_error(TString::Format("Cannot find parameter %s", poiName.c_str()));
10837 }
10838 size_t poiIdx = fr->floatParsFinal().index(*poi);
10839 // put parameters in order of impact on the poi
10840
10841 // impact is regression coefficient * npError
10842 // relevant regression coefficient is cov / (npVariance)
10843 // i.e. DeltaX/sigmaX = [cov(X,Y)/(sigmaXsigmaY)]DeltaY/sigmaY
10844 // ... DeltaX = [cov(X,Y)/(sigmaY^2)]DeltaY
10845 // if DeltaY is just sigmaY then DeltaX = cov(X,Y)/sigmaY
10846
10847 for (auto &label : graphLabels) {
10848 covariances.emplace_back(fr->covarianceMatrix()(poiIdx, fr->floatParsFinal().index(label)) /
10849 dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(label))->getError(),
10850 label);
10851 }
10852 std::sort(covariances.begin(), covariances.end(),
10853 [&](std::pair<double, std::string> i, std::pair<double, std::string> j) {
10854 return doHorizontal ? (std::abs(i.first) < std::abs(j.first))
10855 : (std::abs(i.first) > std::abs(j.first));
10856 });
10857
10859 std::vector<TString> sortedLabels;
10860 maxImpact = (doHorizontal) ? covariances.back().first
10861 : covariances.front().first; // note: max impact is likely to be self variance
10862 for (auto &c : covariances) {
10863 if (c.second == poi->GetName()) {
10864 // poiError = sqrt(c.first);
10865 continue; // skip self
10866 }
10867 c.first *= 4. / (maxImpact * 1.2);
10868 sortedLabels.push_back(c.second);
10869 size_t i = 0;
10870 for (; i < graphLabels.size(); i++) {
10871 if (graphLabels[i] == c.second) {
10872 break;
10873 }
10874 }
10875 sortedGraph.AddPoint(sortedGraph.GetN(), graph->GetPointY(i));
10876 sortedGraph.SetPointError(sortedGraph.GetN() - 1, 0, 0, graph->GetErrorYlow(i), graph->GetErrorYhigh(i));
10877 }
10878 graph->Set(0);
10880 tmpList2.SetName("tmpList");
10881 tmpList2.Add(&sortedGraph);
10882 graph->Merge(&tmpList2);
10883 tmpList2.RemoveAll();
10885 graph->SetTitle("Fit Result Impact");
10886 }
10887
10888 // create a framing histogram
10889 TH2D *hist;
10890 if (doHorizontal) {
10891 hist = new TH2D(GetName(), fr->GetTitle(), 100, -4, 4, std::max(graph->GetN(), 1), -0.5,
10892 std::max(graph->GetN(), 1) - 0.5);
10893 int i = 1;
10894 for (auto &l : graphLabels) {
10895 hist->GetYaxis()->SetBinLabel(i++, l);
10896 }
10897 if (!graphLabels.empty())
10898 hist->GetYaxis()->LabelsOption("v");
10899 hist->GetXaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
10900 } else {
10901 hist = new TH2D(GetName(), fr->GetTitle(), std::max(graph->GetN(), 1), -0.5, std::max(graph->GetN(), 1) - 0.5,
10902 100, -4, 4);
10903 int i = 1;
10904 for (auto &l : graphLabels) {
10905 hist->GetXaxis()->SetBinLabel(i++, l);
10906 }
10907 if (!graphLabels.empty())
10908 hist->GetXaxis()->LabelsOption("v");
10909 hist->GetYaxis()->SetNdivisions(8, 0, 0);
10910 hist->GetYaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
10911 }
10912 hist->SetStats(false);
10913 hist->SetDirectory(nullptr);
10914 hist->SetBit(kCanDelete);
10915 auto histCopy = dynamic_cast<TH1 *>(hist->Clone(".axis"));
10916 histCopy->SetDirectory(nullptr);
10917 histCopy->SetBit(kCanDelete);
10918 auto _axis = (doHorizontal ? histCopy->GetYaxis() : histCopy->GetXaxis());
10919
10920
10921 graph->GetHistogram()->GetXaxis()->Set(std::max(graph->GetN(), 1), -0.5, std::max(graph->GetN(), 1) - 0.5);
10922 for (int ii = 1; ii <= _axis->GetNbins(); ii++) {
10923 graph->GetHistogram()->GetXaxis()->SetBinLabel(ii, _axis->GetBinLabel(ii));
10924 }
10925 // int i = 1;
10926 // for (auto &l : graphLabels) {
10927 // hist->GetXaxis()->SetBinLabel(i++, l);
10928 // }
10929 // hist->SetMaximum(4);
10930 // hist->SetMinimum(-4);
10931 // if (graph->GetN())
10932 // hist->GetXaxis()->LabelsOption("v");
10933 // hist->GetYaxis()->SetNdivisions(8, 0, 0);
10934 // hist->GetYaxis()->SetTitle("(#hat{#theta}-#theta_{i})/#sigma_{i}");
10935 clearPad();
10936 // create a new pad because adjust the margins ...
10937 auto oldPad = gPad;
10938 gPad->Divide(1, 1, 1e-9, 1e-9);
10939 gPad->cd(1);
10940
10941 if (doHorizontal) {
10942 gPad->SetLeftMargin(0.4);
10943 } else {
10944 gPad->SetBottomMargin(0.4);
10945 }
10946
10947 auto pNamesHist = dynamic_cast<TH1F *>(graph->GetHistogram()->Clone("scales")); // used by interactive "pull" plot
10948 pNamesHist->Sumw2();
10949 pNamesHist->SetDirectory(nullptr);
10950
10951 for (int ii = 1; ii <= graph->GetN(); ii++) { // use graph->GetN() to protect against the 0 pars case
10952 auto _p = fr->floatParsFinal().find(_axis->GetBinLabel(ii));
10953 pNamesHist->SetBinContent(ii, offset[_p->GetName()]);
10954 pNamesHist->SetBinError(ii, scale[_p->GetName()]);
10955 _axis->SetBinLabel(ii, strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName());
10956 }
10957
10958 // hist->Draw(); -- now just draw the graph
10959
10960 if (!sOpt.Contains("impact")) {
10961 for (int ii = 2; ii >= 1; ii--) {
10962 auto pullBox = new TGraphErrors;
10963 pullBox->SetName(TString::Format("%dsigmaBand", ii));
10964 pullBox->SetBit(kCanDelete);
10965 pullBox->SetPoint(0, (doHorizontal) ? -ii : -0.5, (doHorizontal) ? -0.5 : 0);
10966 pullBox->SetPoint(1, (doHorizontal) ? ii : (_axis->GetNbins() - 0.5 - nUnconstrained),
10967 (doHorizontal) ? -0.5 : 0);
10968 pullBox->SetPointError(0, 0, (doHorizontal) ? (_axis->GetNbins() - nUnconstrained) : ii);
10969 pullBox->SetPointError(1, 0, (doHorizontal) ? (_axis->GetNbins() - nUnconstrained) : ii);
10970 pullBox->SetFillColor((ii == 2) ? kYellow : kGreen);
10971 hist->GetListOfFunctions()->Add(pullBox, "3"); // pullBox->Draw("3");
10972 }
10973 auto pullLine = new TGraph;
10974 pullLine->SetName("0sigmaLine");
10975 pullLine->SetBit(kCanDelete);
10976 pullLine->SetPoint(0, -0.5, 0);
10977 pullLine->SetPoint(1, _axis->GetNbins() - 0.5, 0);
10978 pullLine->SetLineStyle(2);
10979 pullLine->SetEditable(false);
10980 hist->GetListOfFunctions()->Add(pullLine, "l"); // pullLine->Draw("l");
10981
10982 // also draw vertical line separating constrained from unconstrained, if necessary
10983 if (nUnconstrained > 0) {
10984 pullLine = new TGraph;
10985 pullLine->SetName("dividerLine");
10986 pullLine->SetBit(kCanDelete);
10987 pullLine->SetPoint(0, graph->GetN() - 0.5 - nUnconstrained, -100);
10988 pullLine->SetPoint(1, graph->GetN() - 0.5 - nUnconstrained, 100);
10989 pullLine->SetLineStyle(2);
10990 pullLine->SetEditable(false);
10991 hist->GetListOfFunctions()->Add(pullLine, "l"); // pullLine->Draw("l");
10992 }
10993
10994 // and draw a pave with fr status info
10995 TPaveText *pave =
10996 new TPaveText(gPad->GetLeftMargin(), 1. - gPad->GetTopMargin(), 1. - gPad->GetRightMargin(), 0.98, "NDCNB");
10997 pave->SetFillStyle(0);
10998 pave->SetBorderSize(0);
10999 pave->SetMargin(0.);
11000 pave->SetName("status");
11001 pave->SetTextAlign(31);
11002 pave->AddText(TString::Format("minNLL: %g edm: %g", fr->minNll(), fr->edm()))
11003 ->SetTextColor((fr->status() == 3) ? kRed : kBlack);
11004 std::string covQualTxt;
11005 switch (fr->covQual()) {
11006 case -1: covQualTxt = "Unknown"; break;
11007 case 0: covQualTxt = "Not calculated"; break;
11008 case 1: covQualTxt = "Approximate"; break;
11009 case 2: covQualTxt = "Forced Positive-Definite"; break;
11010 case 3: covQualTxt = "Accurate"; break;
11011 }
11012 pave->AddText(TString::Format("Cov. Quality: %d (%s)", fr->covQual(), covQualTxt.c_str()))
11013 ->SetTextColor((fr->covQual() == 3) ? kBlack : kRed);
11014
11015 std::string statusCodes;
11016 for (unsigned int i = 0; i < fr->numStatusHistory(); i++) {
11017 statusCodes += TString::Format(" %s = %d", fr->statusLabelHistory(i), fr->statusCodeHistory(i));
11018 }
11019 pave->AddText(statusCodes.c_str())->SetTextColor(fr->status() == 0 ? kBlack : kRed);
11020
11021 hist->GetListOfFunctions()->Add(pave);
11022
11023 } else {
11024 gPad->SetTicks(0, 0); // ensure mirrored ticks aren't drawn in this pad
11025
11026 if (doHorizontal) {
11027 // ensure canvas height big enough
11028 if (int(gPad->GetCanvas()->GetWh()) < pNamesHist->GetNbinsX() * 15) {
11029 gPad->GetCanvas()->SetCanvasSize(gPad->GetCanvas()->GetWw(), pNamesHist->GetNbinsX() * 15);
11030 }
11031 }
11032
11033 double factor = 475. / gPad->GetCanvas()->GetWh(); // Wh is the full canvas height, not window height
11034 gPad->SetTopMargin(gStyle->GetPadTopMargin() * factor); // fixed margin height
11035 gPad->SetBottomMargin(gStyle->GetPadBottomMargin() * factor); // fixed margin height
11036
11037 TGaxis *axis =
11038 new TGaxis(_axis->GetXmin(), -4, _axis->GetXmin(), 4, -1.2 * maxImpact, 1.2 * maxImpact, 510, "-S");
11039
11040 if (doHorizontal) {
11041 // _axis->SetLabelSize(
11042 // (_axis->GetLabelFont() % 10 > 2)
11043 // ? (20 / factor)
11044 // : ((gPad->AbsPixeltoY(0) - gPad->AbsPixeltoY(20 / factor)) / (gPad->GetY2() -
11045 // gPad->GetY1())));
11046 // histCopy->GetXaxis()->SetTickLength(histCopy->GetXaxis()->GetTickLength() * factor);
11047 // hist->GetXaxis()->SetTickLength(hist->GetXaxis()->GetTickLength() * factor);
11048 // histCopy->GetYaxis()->SetTickLength(histCopy->GetYaxis()->GetTickLength() * factor);
11049 // hist->GetYaxis()->SetTickLength(hist->GetYaxis()->GetTickLength() * factor);
11050 // histCopy->GetXaxis()->SetTitleOffset(histCopy->GetXaxis()->GetTitleOffset() * factor);
11051 // histCopy->GetXaxis()->SetLabelOffset(histCopy->GetXaxis()->GetLabelOffset() * factor);
11052 // hist->GetXaxis()->SetTitleOffset(hist->GetXaxis()->GetTitleOffset() * factor);
11053 // hist->GetXaxis()->SetLabelOffset(hist->GetXaxis()->GetLabelOffset() * factor);
11054 // histCopy->GetXaxis()->SetTitleOffset(histCopy->GetXaxis()->GetTitleOffset() * factor);
11055 // histCopy->GetXaxis()->SetLabelOffset(histCopy->GetXaxis()->GetLabelOffset() * factor);
11056 }
11057 // copy attributes from TAxis to TGaxis
11058 axis->ImportAxisAttributes((doHorizontal) ? histCopy->GetXaxis() : histCopy->GetYaxis());
11059 axis->SetTitle(TString::Format("#Delta %s", fr->floatParsFinal().find(poiName.c_str())->GetTitle()));
11060
11061 // create impact bar charts
11062 for (int tt = 0; tt < 2; tt++) {
11063 auto impact = static_cast<TH1 *>(
11064 graph->GetHistogram()->Clone(TString::Format("%s_impact+", tt == 0 ? "prefit" : "postfit")));
11065 impact->SetDirectory(nullptr);
11066 impact->GetYaxis()->SetTitle(TString::Format("#Delta%s/#sigma", poiName.c_str()));
11067 impact->SetBarWidth(0.9);
11068 impact->SetBarOffset(0.05);
11069 impact->SetLineColor(kBlack);
11070 impact->SetFillColor(kAzure - 4);
11071 impact->SetFillStyle(tt == 0 ? 3013 : 1001);
11072 auto impact2 =
11073 static_cast<TH1 *>(impact->Clone(TString::Format("%s_impact-", tt == 0 ? "prefit" : "postfit")));
11074 impact2->SetDirectory(nullptr);
11075 impact2->SetFillColor(kCyan);
11076 for (int ii = 1; ii <= pNamesHist->GetNbinsX(); ii++) {
11077 for (auto &c : covariances) {
11078 if (c.second != pNamesHist->GetXaxis()->GetBinLabel(ii))
11079 continue;
11080 auto vv = dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(c.second.c_str()));
11081 auto vv_init = dynamic_cast<RooRealVar *>(fr->floatParsInit().find(c.second.c_str()));
11082 impact->SetBinContent(ii, ((tt == 0 && !vv_init->hasError()) || !vv->hasError())
11083 ? 0.
11084 : c.first * vv->getError() / vv->getErrorHi() *
11085 (tt == 0 ? (vv_init->getErrorHi() / vv->getErrorHi()) : 1.));
11086 impact2->SetBinContent(ii, ((tt == 0 && !vv_init->hasError()) || !vv->hasError())
11087 ? 0.
11088 : c.first * vv->getError() / vv->getErrorLo() *
11089 (tt == 0 ? (vv_init->getErrorLo() / vv->getErrorLo()) : 1.));
11090 }
11091 }
11092 hist->GetListOfFunctions()->Add(impact, (doHorizontal) ? "hbarsamemin0" : "bsamey+");
11093 hist->GetListOfFunctions()->Add(impact2, (doHorizontal) ? "hbarsamemin0" : "bsamey+");
11094 }
11095 // add three lines
11096 for (int ii = -1; ii <= 1; ii++) {
11097 auto pullLine = new TGraph;
11098 pullLine->SetName(TString::Format("%dsigmaLine", ii));
11099 pullLine->SetBit(kCanDelete);
11100 pullLine->SetPoint(0, -0.5, ii);
11101 pullLine->SetPoint(1, hist->GetNbinsY() - 0.5, ii);
11102 pullLine->SetLineStyle(2);
11103 pullLine->SetEditable(false);
11104 hist->GetListOfFunctions()->Add(pullLine, "l");
11105 }
11106 hist->GetListOfFunctions()->Add(axis); // draw axis last
11107 TLegend *leg1 =
11108 new TLegend(0.02, doHorizontal ? (1. - 0.22 * factor) : 0.02, 0.27, (doHorizontal ? 1. : 0.24));
11109 leg1->SetFillStyle(0);
11110 leg1->SetBorderSize(0);
11111 leg1->SetMargin(0.25);
11112 leg1->SetNColumns(2);
11113
11114 leg1->SetTextSize(_axis->GetLabelSize());
11115 leg1->SetTextFont(_axis->GetLabelFont());
11116 leg1->AddEntry((TObject *)nullptr, "Hessian Pre-fit", "");
11117 leg1->AddEntry((TObject *)nullptr, "Impact:", "");
11118 leg1->AddEntry(hist->FindObject("prefit_impact+"), "#theta = #hat{#theta}+#Delta#theta", "f");
11119 leg1->AddEntry(hist->FindObject("prefit_impact-"), "#theta = #hat{#theta}-#Delta#theta", "f");
11120 leg1->AddEntry((TObject *)nullptr, "Hessian Post-fit", "");
11121 leg1->AddEntry((TObject *)nullptr, "Impact:", "");
11122 leg1->AddEntry(hist->FindObject("postfit_impact+"), "#theta = #hat{#theta}+#Delta#theta", "f");
11123 leg1->AddEntry(hist->FindObject("postfit_impact-"), "#theta = #hat{#theta}-#Delta#theta", "f");
11124
11125 hist->GetListOfFunctions()->Add(leg1);
11126 if (gStyle->GetOptTitle()) {
11127 histCopy->SetBit(TH1::kNoTitle);
11128 TPaveText *title =
11129 new TPaveText(gPad->GetLeftMargin(), 1. - gPad->AbsPixeltoY(14), 1. - gPad->GetRightMargin(), 1., "NDC");
11130 title->ConvertNDCtoPad();
11131 title->SetY1NDC(1. - gPad->GetTopMargin() * 0.6);
11132 title->SetY2NDC(1);
11133 title->SetTextSize(
11134 (title->GetTextFont() % 10 > 2)
11135 ? (14 / factor)
11136 : ((gPad->AbsPixeltoY(0) - gPad->AbsPixeltoY(10 / factor)) / (gPad->GetY2() - gPad->GetY1())));
11137 title->SetFillStyle(0);
11138 title->SetBorderSize(0);
11139 title->AddText(histCopy->GetTitle());
11140 hist->GetListOfFunctions()->Add(title);
11141 }
11142 }
11143
11144 graph->SetEditable(false);
11145 pNamesHist->SetLineWidth(0);
11146 pNamesHist->SetMarkerSize(0);
11147 pNamesHist->SetMarkerStyle(0);
11148 graph->GetListOfFunctions()->Add(pNamesHist, "same"); // graph->SetHistogram(pNamesHist);
11149 if (doHorizontal) {
11150
11151 // flip the graph and contained graphs
11152 for (int p = 0; p < graph->GetN(); p++) {
11153 graph->SetPoint(p, graph->GetPointY(p), graph->GetPointX(p));
11154 graph->SetPointError(p, graph->GetErrorYlow(p), graph->GetErrorYhigh(p), graph->GetErrorXlow(p),
11155 graph->GetErrorXhigh(p));
11156 }
11157 for (auto f : *hist->GetListOfFunctions()) {
11158 if (f->InheritsFrom("TH1")) {
11159 // f->Draw("hbarsamemin0");
11160 } /*else if (auto g2 = dynamic_cast<TGraphErrors *>(f)) {
11161 for (int p = 0; p < g2->GetN(); p++) {
11162 g2->SetPoint(p, g2->GetPointY(p), g2->GetPointX(p));
11163 g2->SetPointError(p, g2->GetErrorY(p), _axis->GetNbins());
11164 }
11165 //g2->Draw("3");
11166 } */
11167 else if (auto g = dynamic_cast<TGraph *>(f)) {
11168 for (int p = 0; p < g->GetN(); p++) {
11169 g->SetPoint(p, g->GetPointY(p), g->GetPointX(p));
11170 }
11171 // g->Draw("l");
11172 } else if (auto l = dynamic_cast<TLine *>(f)) {
11173 l->SetX1(l->GetY1());
11174 l->SetX2(l->GetY2());
11175 l->SetY1(_axis->GetXmax());
11176 l->SetY2(_axis->GetXmax());
11177 // l->Draw();
11178 }
11179 }
11180 }
11181
11182 if (!sOpt.Contains("impact")) {
11183 // add labels to graph for unconstrained parameters
11184 for (size_t i = 0; i < ugraphLabels.size(); i++) {
11185 int bin = pNamesHist->GetNbinsX() - ugraphLabels.size() + i + 1;
11186 auto p = dynamic_cast<RooRealVar *>(fr->floatParsFinal().find(pNamesHist->GetXaxis()->GetBinLabel(bin)));
11187 if (!p)
11188 continue;
11189 auto x = graph->GetPointX(graph->GetN() - ugraphLabels.size() + i);
11190 auto y = graph->GetPointY(graph->GetN() - ugraphLabels.size() + i) +
11191 graph->GetErrorYhigh(graph->GetN() - ugraphLabels.size() + i);
11192 auto l = xRooFit::matchPrecision({p->getVal(), p->getError()});
11193 auto t = new TLatex(x, y, TString::Format("%g #pm %g", l.first, l.second));
11194 t->SetBit(kCanDelete);
11195 t->SetTextSize(0.025);
11196 t->SetTextAngle(90);
11197 graph->GetListOfFunctions()->Add(t);
11198 }
11199 }
11200
11201 graph->SetName("pulls");
11202 hist->GetListOfFunctions()->Add(graph, "z0p");
11203 // hist->GetListOfFunctions()->Add(histCopy->Clone(".axis"),(sOpt.Contains("impact") &&
11204 // !doHorizontal)?"axissamey+":"axissame"); // doesn't display right when zoom the axis
11205 if (!hasSame) {
11206 histCopy->Draw((sOpt.Contains("impact") && !doHorizontal)
11207 ? "axisy+"
11208 : "axis"); // draws the axis, called ".axis" for easy access
11209 }
11210 hist->Draw("same");
11211 //
11212 // if(sOpt.Contains("impact")) {
11213 // // make main object the histogram
11214 // auto h = (TH1*)graph->GetHistogram()->Clone("impact");
11215 // graph->GetListOfFunctions()->RemoveAll();
11216 // for(int ii=1;ii<=h->GetNbinsX();ii++) h->SetBinContent(ii,-4);
11217 // h->GetListOfFunctions()->Add(graph,"z0p");
11218 // h->Draw("hbar");
11219 // } else {
11220 // graph->Draw(sOpt.Contains("impact") ? "az0py+" : "az0p");
11221 // }
11222 auto hh = dynamic_cast<TH1 *>(histCopy->Clone(".axiscopy"));
11223 hh->SetDirectory(nullptr);
11224 hh->SetBit(kCanDelete);
11225 hh->Draw(
11226 (sOpt.Contains("impact") && !doHorizontal)
11227 ? "axissamey+"
11228 : "axissame"); // overlay axis again -- important is last so can remove if don't pad->Update before reclear
11229 gPad->Modified();
11230 oldPad->cd();
11231 // gPad->Update();
11232 return;
11233 }
11234
11235 if (get()->InheritsFrom("RooAbsData")) {
11236 auto s = parentPdf();
11237 if (s && s->get<RooSimultaneous>()) {
11238 // drawing dataset associated to a simultaneous means must find subpads with variation names
11239 // may not have subpads if drawning a "Yield" plot ...
11240 bool doneDraw = false;
11241 for (auto c : s->bins()) {
11242 auto _pad = dynamic_cast<TPad *>(gPad->GetPrimitive(c->GetName()));
11243 if (!_pad)
11244 continue; // channel was hidden?
11245 // attach as a child before calling datasets(), so that if this dataset is external to workspace it is
11246 // included still attaching the dataset ensures dataset reduction for the channel is applied
11247 c->push_back(std::make_shared<xRooNode>(*this));
11248 auto ds = c->datasets().find(GetName());
11249 c->resize(c->size() - 1); // remove the child we attached
11250 if (!ds) {
11251 std::cout << " no ds " << GetName() << " - this should never happen!" << std::endl;
11252 continue;
11253 }
11254 auto tmp = gPad;
11255 _pad->cd();
11256 ds->Draw(opt);
11257 doneDraw = true;
11258 tmp->cd();
11259 }
11260 if (doneDraw) {
11261 gPad->Modified();
11262 return;
11263 }
11264 }
11265
11266 if (!s && hasSame) {
11267 // draw onto all subpads with = in the name
11268 // if has no such subpads, draw onto this pad
11269 bool doneDraw = false;
11270 for (auto o : *gPad->GetListOfPrimitives()) {
11271 if (auto p = dynamic_cast<TPad *>(o); p && TString(p->GetName()).Contains('=')) {
11272 auto _tmp = gPad;
11273 p->cd();
11274 Draw(opt);
11275 _tmp->cd();
11276 doneDraw = true;
11277 }
11278 }
11279 if (doneDraw) {
11280 gPad->Modified();
11281 return;
11282 }
11283 }
11284
11285 auto dataGraph = BuildGraph(v, false, (!s && hasSame) ? gPad : nullptr);
11286 if (!dataGraph)
11287 return;
11288
11289 dataGraph->SetBit(kCanDelete); // will be be deleted when pad is cleared
11290 dataGraph->SetMarkerSize(dataGraph->GetMarkerSize() * gPad->GetWNDC()); // scale marker sizes to pad size
11291
11292 if (s && !s->get<RooAbsPdf>()->canBeExtended()) {
11293 // normalize dataGraph to 1
11294 double tot = 0;
11295 for (int i = 0; i < dataGraph->GetN(); i++)
11296 tot += dataGraph->GetPointY(i);
11297 dataGraph->Scale(1. / tot);
11298 }
11299
11300 if (!hasSame) {
11301 clearPad();
11302 dataGraph->Draw("Az0p");
11303 addLegendEntry(dataGraph, strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(), "pEX0");
11304 gPad->Modified();
11305 // gPad->Update();
11306 return;
11307 }
11308
11309 bool noPoint = false;
11310 if (v && dynamic_cast<RooAbsArg *>(v)->getAttribute("global") && dataGraph->GetN() == 1) {
11311 // global observable ... if graph has only 1 data point line it up on the histogram value
11312 for (auto o : *gPad->GetListOfPrimitives()) {
11313 if (auto h = dynamic_cast<TH1 *>(o);
11314 h && strcmp(h->GetXaxis()->GetName(), dynamic_cast<TObject *>(v)->GetName()) == 0) {
11315 dataGraph->SetPointY(0, h->Interpolate(dataGraph->GetPointX(0)));
11316 noPoint = true;
11317 break;
11318 }
11319 }
11320 }
11321
11322 if (auto _pad = dynamic_cast<TPad *>(gPad->FindObject("auxPad")); _pad) {
11323 if (auto h = dynamic_cast<TH1 *>(_pad->GetPrimitive("auxHist")); h) {
11324 TString histName = h->GetTitle(); // split it by | char
11325 TString histType = histName(histName.Index('|') + 1, histName.Length());
11326 histName = histName(0, histName.Index('|'));
11327 if (auto mainHist = dynamic_cast<TH1 *>(gPad->GetPrimitive(histName));
11328 mainHist && auxFunctions.find(h->GetYaxis()->GetTitle()) != auxFunctions.end()) {
11329 // decide what to do based on title of auxHist (previously used name of y-axis but that changed axis
11330 // behaviour) use title instead
11331 auto ratioGraph = dynamic_cast<TGraphAsymmErrors *>(dataGraph->Clone(dataGraph->GetName()));
11332 ratioGraph->SetBit(kCanDelete);
11333 for (int i = 0; i < ratioGraph->GetN(); i++) {
11334 double val = ratioGraph->GetPointY(i);
11335 int binNum = mainHist->FindFixBin(ratioGraph->GetPointX(i));
11336 double nom = mainHist->GetBinContent(binNum);
11337 double nomerr = mainHist->GetBinError(binNum);
11338 double yval =
11339 std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(ratioGraph->GetPointY(i), nom, nomerr);
11340 double yup = std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(val + ratioGraph->GetErrorYhigh(i),
11341 nom, nomerr) -
11342 yval;
11343 double ydown = yval - std::get<0>(auxFunctions[h->GetYaxis()->GetTitle()])(
11344 val - ratioGraph->GetErrorYlow(i), nom, nomerr);
11345 if (!std::isnan(yval)) {
11346 ratioGraph->SetPointY(i, yval);
11347 if (!std::isnan(yup))
11348 ratioGraph->SetPointEYhigh(i, yup);
11349 if (!std::isnan(ydown))
11350 ratioGraph->SetPointEYlow(i, ydown);
11351 }
11352 }
11353 // remove the zero points
11354 int i = 0;
11355 while (i < ratioGraph->GetN()) {
11356 if (ratioGraph->GetPointY(i) == 0 && ratioGraph->GetErrorYhigh(i) == 0 &&
11357 ratioGraph->GetErrorYlow(i) == 0) {
11358 ratioGraph->RemovePoint(i);
11359 } else {
11360 i++;
11361 }
11362 }
11363 auto _tmpPad = gPad;
11364 _pad->cd();
11365 ratioGraph->Draw("z0psame");
11367 adjustYRange(minMax.first, minMax.second, h, std::get<1>(auxFunctions[h->GetYaxis()->GetTitle()]));
11368 _tmpPad->cd();
11369 }
11370 }
11371 }
11372
11373 dataGraph->Draw("z0p same");
11374 addLegendEntry((noPoint) ? nullptr : dataGraph, strlen(dataGraph->GetTitle()) ? dataGraph->GetTitle() : GetName(),
11375 noPoint ? "" : "pEX0");
11376
11377 auto minMax = graphMinMax(dynamic_cast<TGraphAsymmErrors *>(dataGraph));
11378 adjustYRange(minMax.first, minMax.second);
11379
11380 gPad->Modified();
11381 // gPad->Update();
11382 return;
11383 }
11384
11385 // auto _ax = GetXaxis();
11386 // auto v = (_ax) ? dynamic_cast<RooRealVar*>(/*possibleObs.first()*/_ax->GetParent()) : nullptr;
11387 // if (!v) { v = get<RooRealVar>(); } // self-axis
11388 // if (!v) return;
11389
11390 if (auto lv = get<RooAbsLValue>(); lv && fParent && fParent->get<RooAbsData>()) {
11391 // drawing an observable from a dataset ... build graph, and exit
11392 auto gr = fParent->BuildGraph(lv, true);
11394 gr->Draw(hasSame ? "P" : "AP");
11395 return;
11396 }
11397
11398 if (forceNames != "") {
11399 // drawing a force plot ... build nll and fill a histogram with force terms
11400 auto _dsets = datasets();
11401 bool _drawn = false;
11402 auto _coords = coords();
11403 auto _fr = fitResult();
11404 auto initPar = dynamic_cast<RooRealVar *>(_fr.get<RooFitResult>()->floatParsInit().find(forceNames));
11405 if (!initPar)
11406 return;
11407 std::vector<double> valuesToDo = {initPar->getVal()};
11408 if (initPar->hasError() || initPar->hasAsymError()) {
11409 valuesToDo.push_back(initPar->getVal() + initPar->getErrorLo());
11410 valuesToDo.push_back(initPar->getVal() + initPar->getErrorHi());
11411 }
11412 int ii = 0;
11413 for (auto valueToDo : valuesToDo) {
11414 ii++;
11415 for (auto &d : _dsets) {
11416 if (!d->get()->TestBit(1 << 20))
11417 continue;
11418 auto emptyHist = BuildHistogram(v, true);
11419 emptyHist->SetBit(kCanDelete);
11420 auto _obs = d->obs();
11421 auto x = _obs.find((v) ? dynamic_cast<TObject *>(v)->GetName() : emptyHist->GetXaxis()->GetName());
11422 auto _nll = nll(d);
11423 auto theData = d->get<RooAbsData>();
11424 int nevent = theData->numEntries();
11425 for (int i = 0; i < nevent; i++) {
11426 theData->get(i);
11427 bool _skip = false;
11428 for (const auto &_c : _coords) {
11429 if (auto cat = _c->get<RooAbsCategoryLValue>(); cat) {
11430 if (cat->getIndex() != theData->get()->getCatIndex(cat->GetName())) {
11431 _skip = true;
11432 break;
11433 }
11434 }
11435 }
11436 if (_skip)
11437 continue;
11438
11439 if (x) {
11440 auto val = _nll.pars()->getRealValue(initPar->GetName());
11441 if (ii > 1)
11442 _nll.pars()->setRealValue(initPar->GetName(), valueToDo);
11443 auto nllVal = _nll.getEntryVal(i);
11444 _nll.pars()->setRealValue(initPar->GetName(), initPar->getVal());
11445 auto nllVal2 = _nll.getEntryVal(i);
11446 _nll.pars()->setRealValue(initPar->GetName(), val);
11447 emptyHist->Fill(x->get<RooAbsReal>()->getVal(), (nllVal2 - nllVal));
11448 }
11449 }
11450 // include the extendedTerm, distributed evenly over the bins
11451 // probably should be somehow dependent on data density though (i.e. bins with more data get more of it?)
11452 auto val = _nll.pars()->getRealValue(initPar->GetName());
11453 if (ii > 1)
11454 _nll.pars()->setRealValue(initPar->GetName(), valueToDo);
11455 auto _extTerm = _nll.extendedTermVal();
11456 _nll.pars()->setRealValue(initPar->GetName(), initPar->getVal());
11457 auto _extTerm2 = _nll.extendedTermVal();
11458 _nll.pars()->setRealValue(initPar->GetName(), val);
11459 for (int i = 1; i <= emptyHist->GetNbinsX(); i++) {
11460 emptyHist->SetBinContent(i,
11461 emptyHist->GetBinContent(i) + (_extTerm2 - _extTerm) / emptyHist->GetNbinsX());
11462 emptyHist->SetBinError(i, 0);
11463 }
11464 emptyHist->GetYaxis()->SetTitle("log (L(#theta)/L(#theta_{0}))");
11465 emptyHist->SetTitle(TString::Format("#theta = %g", (ii > 1) ? valueToDo : val));
11466 if (ii == 1)
11467 emptyHist->SetLineColor(kBlack);
11468 if (ii == 2) {
11469 emptyHist->SetLineColor(kRed);
11470 } else if (ii == 3) {
11471 emptyHist->SetLineColor(kBlue);
11472 }
11473 emptyHist->Draw(_drawn ? "same" : "");
11474 _drawn = true;
11475 }
11476 }
11477 return;
11478 }
11479
11480 auto rar = get<RooAbsReal>();
11481 const xRooNode *rarNode = this;
11482 if (!rar) {
11483 // draw a deleteable clone of the object we wrap (since we might own the object)
11484 get()->DrawClone(opt);
11485 return;
11486 }
11487 // RooAbsReal *sf = nullptr;
11488 if (get()->InheritsFrom("RooExtendPdf")) {
11489 browse();
11490 rarNode = find(".pdf").get();
11491 // rar = rarNode->get<RooAbsReal>();
11492 // sf = find(".n")->get<RooAbsReal>();
11493 }
11494
11495 if (!nostack && !hasOverlay &&
11496 (rarNode->get()->InheritsFrom("RooRealSumPdf") || rarNode->get()->InheritsFrom("RooAddPdf") ||
11497 (v && rarNode->get()->InheritsFrom("RooSimultaneous") &&
11498 strcmp(dynamic_cast<TObject *>(v)->GetName(), rarNode->get<RooSimultaneous>()->indexCat().GetName()) == 0))) {
11499 nostack = false;
11500 } else {
11501 // in all other cases, we do not build a stack
11502 nostack = true;
11503 }
11504
11505 auto h = BuildHistogram(v, false, hasErrorOpt, 1, 0, "", false, false, 0, nullptr, nostack, true /*setInterp*/);
11506 if (!h) {
11507 if (get()) {
11508 // draw a deleteable clone of the object we wrap (since we might own the object)
11509 get()->DrawClone(opt);
11510 }
11511 return;
11512 }
11513 h->SetBit(kCanDelete);
11514
11515 if (!v)
11516 v = getObject<RooAbsLValue>(h->GetXaxis()->IsAlphanumeric() ? h->GetXaxis()->GetTimeFormatOnly()
11517 : h->GetXaxis()->GetName())
11518 .get();
11519 RooAbsArg *vv = (v) ? dynamic_cast<RooAbsArg *>(v) : rar;
11520 if (h->GetXaxis()->IsAlphanumeric()) {
11521 // do this to get bin labels
11522 h->GetXaxis()->SetName("xaxis"); // WARNING -- this messes up anywhere we GetXaxis()->GetName()
11523 }
11524
11525 // get style now, before we mess with histogram title
11526 // auto _styleNode = styles(h);
11527
11528 if (rar->InheritsFrom("RooAbsPdf") && !(rar->InheritsFrom("RooRealSumPdf") || rar->InheritsFrom("RooAddPdf") ||
11529 rar->InheritsFrom("RooSimultaneous"))) {
11530 // append parameter values to title if has such
11531 RooArgSet s;
11532 rar->leafNodeServerList(&s);
11533 if (v)
11534 s.remove(*dynamic_cast<RooAbsArg *>(v));
11535 if (!s.empty()) {
11536 TString ss = h->GetTitle();
11537 ss += " [";
11538 bool first = true;
11539 for (auto _p : s) {
11540 auto _v = dynamic_cast<RooRealVar *>(_p);
11541 if (!_v)
11542 continue;
11543 if (!first)
11544 ss += ",";
11545 first = false;
11546 ss += TString::Format("%s=%g", strlen(_p->GetTitle()) ? _p->GetTitle() : _p->GetName(), _v->getVal());
11547 if (_v->hasError()) {
11548 ss += TString::Format("#pm %g", _v->getError());
11549 }
11550 }
11551 ss += "]";
11552 h->SetTitle(ss);
11553 }
11554 }
11555
11556 if (!hasSame) {
11557 if (obs().find(vv->GetName())) {
11558 gPad->SetGrid(0, 0);
11559 } else {
11560 gPad->SetGrid(1, 1);
11561 }
11562 }
11563 TString dOpt = h->GetOption();
11564 if (dOpt == "l")
11565 h->SetFillStyle(0);
11566 // // need to strip namespace to discount the "HistFactory" namespace classes from all being treated as binned
11567 // TString clNameNoNamespace = rar->ClassName();
11568 // clNameNoNamespace = clNameNoNamespace(clNameNoNamespace.Last(':') + 1, clNameNoNamespace.Length());
11569 // TString dOpt = (clNameNoNamespace.Contains("Hist") || vv->isCategory() || rar->isBinnedDistribution(*vv) ||
11570 // h->GetNbinsX() == 1 || rar->getAttribute("BinnedLikelihood") ||
11571 // (dynamic_cast<RooAbsRealLValue *>(vv) &&
11572 // std::unique_ptr<std::list<double>>(rar->binBoundaries(*dynamic_cast<RooAbsRealLValue *>(vv),
11573 // -std::numeric_limits<double>::infinity(),
11574 // std::numeric_limits<double>::infinity()))))
11575 // ? ""
11576 // : "LF2";
11577 // if (auto d = dynamic_cast<RooHistFunc *>(rar); d && !d->isBinnedDistribution(*vv) && h->GetNbinsX() != 1) {
11578 // dOpt = "LF2"; // hist func is interpolated, so draw it as such
11579 // }
11580 // if (dOpt == "LF2" && !components().empty()) {
11581 // // check if all components of dOpt are "Hist" type (CMS model support)
11582 // // if so then dOpt="";
11583 // bool allHist = true;
11584 // for (auto &s : components()) {
11585 // TString _clName = s->get()->ClassName();
11586 // _clName = _clName(_clName.Last(':') + 1, _clName.Length());
11587 // if (!(s->get() && _clName.Contains("Hist"))) {
11588 // allHist = false;
11589 // break;
11590 // }
11591 // }
11592 // if (allHist)
11593 // dOpt = "";
11594 // }
11595 //
11596 // if(dOpt=="LF2") {
11597 // // ensure any sub hists have lf2 option
11598 // TObjLink *lnk = h->GetListOfFunctions()->FirstLink();
11599 // while (lnk) {
11600 // if(auto hh = dynamic_cast<TH1*>(lnk->GetObject())) {
11601 // if(TString(hh->GetName())=="band" && TString(lnk->GetOption())=="e2same") {
11602 // lnk->SetOption("LF2 e3same");
11603 // } else if(TString(hh->GetName())=="nominal") {
11604 // lnk->SetOption("L same");
11605 // }
11606 // }
11607 // lnk = lnk->Next();
11608 // }
11609 // }
11610
11611 if (rar == vv && rar->IsA() == RooRealVar::Class()) {
11612 dOpt += "TEXT";
11613 // add a TExec to the histogram so that when edited it will propagate to var
11614 gROOT->SetEditHistograms(true);
11615 } else {
11616 gROOT->SetEditHistograms(false);
11617 }
11618
11619 if (hasSame) {
11620 dOpt += " same";
11621 } else {
11622 hAxis = h;
11623 }
11624
11625 if (dOpt.Contains("TEXT") || sOpt.Contains("text")) {
11626 // adjust marker size so text is good
11627 h->SetMarkerSize(gStyle->GetLabelSize("Z") / (0.02 * gPad->GetHNDC()));
11628 }
11629
11630 bool hasError(false);
11631 for (int i = 0; i < h->GetSumw2N(); i++) {
11632 if (h->GetSumw2()->At(i)) {
11633 hasError = true;
11634 break;
11635 }
11636 }
11637
11638 /** This doesn't seem necessary in at least 6.26 any more - pads seem adjusted on their own
11639 if (!hasSame && h->GetYaxis()->GetTitleFont()%10 == 2) {
11640 h->GetYaxis()->SetTitleOffset( gPad->GetLeftMargin() / gStyle->GetPadLeftMargin() );
11641 } */
11642 // don't this instead - dont want to leave as zero (auto) in case show aux plot
11643 if (!hasSame && h->GetYaxis()->GetTitleFont() % 10 == 2) {
11644 h->GetYaxis()->SetTitleOffset(1.);
11645 }
11646
11647 TH1 *errHist = nullptr;
11648
11649 if (!hasSame)
11650 clearPad();
11651
11652 if(rar->getAttribute("Logy")) {
11653 gPad->SetLogy(1);
11654 }
11655
11656 if (rar == vv && rar->IsA() == RooRealVar::Class()) {
11657 // add a TExec to the histogram so that when edited it will propagate to var
11658 // h->GetListOfFunctions()->Add(h->Clone("self"),"TEXTHIST");
11659 dOpt = "TEXT";
11660 auto node = new xRooNode(*this);
11661 auto _hist = (errHist) ? errHist : h;
11662 auto hCopy = (errHist) ? nullptr : dynamic_cast<TH1 *>(h->Clone());
11663 if (hCopy) {
11664 hCopy->Reset();
11665 hCopy->Add(_hist);
11666 hCopy->SetDirectory(nullptr);
11667 }
11668 _hist->GetListOfFunctions()->Add(node);
11669 _hist->GetListOfFunctions()->Add(new TExec(
11670 ".update",
11672 "gROOT->SetEditHistograms(true);auto h = dynamic_cast<TH1*>(gPad->GetPrimitive(\"%s\")); if(h) { double "
11673 "range= h->GetMaximum()-h->GetMinimum(); if(auto n "
11674 "= dynamic_cast<xRooNode*>(h->GetListOfFunctions()->FindObject(\"%s\")); n && "
11675 "n->TestBit(TObject::kNotDeleted) && n->get<RooRealVar>()->getVal() != h->GetBinContent(1)) {"
11676 "h->SetBinContent(1, "
11677 "TString::Format(\"%%.2g\",int(h->GetBinContent(1)/(range*0.01))*range*0.01).Atof());n->SetContent( "
11678 "h->GetBinContent(1) ); for(auto pp : *h->GetListOfFunctions()) if(auto hh = "
11679 "dynamic_cast<TH1*>(pp))hh->SetBinContent(1,h->GetBinContent(1));} if(h->GetBinContent(1)==0.) "
11680 "h->SetBinContent(1,range*0.005); gPad->Modified();gPad->Update(); }",
11681 _hist->GetName(), node->GetName())));
11682 if (errHist) {
11683 errHist->GetListOfFunctions()->Add(h, "TEXT HIST same");
11684 errHist->SetFillColor(h->GetLineColor());
11685 } else {
11686 hCopy->SetBit(kCanDelete);
11687 hCopy->SetFillStyle(0);
11688 _hist->GetListOfFunctions()->Add(hCopy, "TEXT HIST same");
11689 //_hist->SetBinError(1, 0);
11690 }
11691 _hist->SetStats(false);
11692 // if (_hist->GetBinContent(1)==0.) _hist->SetBinContent(1,(_hist->GetMaximum()-_hist->GetMinimum())*0.005);
11693 _hist->Draw(); //_hist->Draw(((hasError) ? "e2" : ""));
11694 gPad->Modified();
11695 return;
11696 }
11697
11698 bool overlayExisted = false;
11699 if (hasOverlay) {
11700 h->SetName(TString::Format("%s%s", h->GetName(), overlayName.Data()));
11701 if (auto existing = dynamic_cast<TH1 *>(gPad->GetPrimitive(h->GetName())); existing) {
11702 existing->Reset();
11703 existing->Add(h);
11704 delete h;
11705 h = existing;
11706 overlayExisted = true;
11707 } else {
11708 TString oldStyle = (rar && rar->getStringAttribute("style")) ? rar->getStringAttribute("style") : "";
11709 h->SetTitle(overlayName);
11710 // for overlays will take style from current gStyle before overriding with personal style
11711 // this ensures initial style will be whatever gStyle is, rather than whatever ours is
11712 static_cast<TAttLine &>(*h) = *gStyle;
11713 static_cast<TAttFill &>(*h) = *gStyle;
11714 static_cast<TAttMarker &>(*h) = *gStyle;
11715 h->SetFillStyle(0); // explicit default for overlays will be transparent fill
11716
11717 // std::shared_ptr<TStyle> style; // use to keep alive for access from GetStyle below, in case
11718 // getObject has decided to return the owning ptr (for some reason) if
11719 // (!gROOT->GetStyle(h->GetTitle())) {
11720 // if ( (style = getObject<TStyle>(h->GetTitle())) ) {
11721 // // loaded style (from workspace?) so put in list and use that
11722 // gROOT->GetListOfStyles()->Add(style.get());
11723 // } else {
11724 // // create new style - gets put in style list automatically so don't have to delete
11725 // // acquire them so saved to workspaces for auto reload ...
11726 // style = acquireNew<TStyle>(h->GetTitle(),
11727 // TString::Format("Style for %s component", h->GetTitle()));
11728 // (TAttLine &) (*style) = *dynamic_cast<TAttLine *>(h);
11729 // (TAttFill &) (*style) = *dynamic_cast<TAttFill *>(h);
11730 // (TAttMarker &) (*style) = *dynamic_cast<TAttMarker *>(h);
11731 // gROOT->GetListOfStyles()->Add(style.get());
11732 // }
11733 // }
11734 // (TAttLine&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
11735 // (TAttFill&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
11736 // (TAttMarker&)(*h) = *(gROOT->GetStyle(h->GetTitle()) ? gROOT->GetStyle(h->GetTitle()) : gStyle);
11737 auto _styleNode = styles(h);
11738 rar->setStringAttribute("style", oldStyle == "" ? nullptr : oldStyle.Data()); // restores old style
11739 if (auto _style = _styleNode.get<TStyle>()) {
11740 (TAttLine &)(*h) = *_style;
11741 (TAttFill &)(*h) = *_style;
11742 (TAttMarker &)(*h) = *_style;
11743 }
11744 h->Draw(dOpt == "LF2" ? "e3" : dOpt);
11745 if (errHist) {
11746 errHist->SetTitle(overlayName);
11747 (TAttLine &)(*errHist) = *h;
11748 errHist->SetFillColor(h->GetLineColor());
11749 }
11750 }
11751 } else {
11752 // if (auto _style = _styleNode.get<TStyle>()) {
11753 // (TAttLine &)(*h) = *_style;
11754 // (TAttFill &)(*h) = *_style;
11755 // (TAttMarker &)(*h) = *_style;
11756 // if (errHist) {
11757 // (TAttLine &)(*errHist) = *h;
11758 // errHist->SetFillColor(h->GetLineColor());
11759 // }
11760 // }
11761 h->Draw(dOpt);
11762 }
11763
11764 if (!hasOverlay && (rarNode->get()->InheritsFrom("RooRealSumPdf") || rarNode->get()->InheritsFrom("RooAddPdf") ||
11765 (rarNode->get()->InheritsFrom("RooSimultaneous") &&
11766 strcmp(vv->GetName(), rarNode->get<RooSimultaneous>()->indexCat().GetName()) == 0))) {
11767 if (auto stack = dynamic_cast<THStack *>(h->FindObject("stack"))) {
11768 // access the stack and set draw options, adjust ranges etc
11769 TObjLink *lnk = stack->GetHists()->FirstLink();
11770 while (lnk) {
11771 TH1 *hh = static_cast<TH1 *>(lnk->GetObject());
11772 // lnk->SetOption(dOpt); - not needed
11773 auto hhMin = (hh->GetMinimum() == 0) ? hh->GetMinimum(1e-9) : hh->GetMinimum();
11774 if (lnk == stack->GetHists()->FirstLink() && h->GetMinimum() > hhMin) {
11775 auto newMin = hhMin - (h->GetMaximum() - hhMin) * gStyle->GetHistTopMargin();
11776 if (hhMin >= 0 && newMin < 0)
11777 newMin = hhMin * 0.99;
11778 adjustYRange(newMin, h->GetMaximum());
11779 }
11780 addLegendEntry(hh, hh->GetTitle(), "f");
11781 lnk = lnk->Next();
11782 }
11783 }
11784
11785 // // build a stack unless not requested
11786 // if (!nostack) {
11787 // THStack *stack = new THStack(TString::Format("%s_stack", rar->GetName()),
11788 // TString::Format("%s;%s", rar->GetTitle(), h->GetXaxis()->GetTitle()));
11789 // int count = 2;
11790 // std::map<std::string, int> colorByTitle; // TODO: should fill from any existing legend
11791 // std::set<std::string> allTitles;
11792 // bool titleMatchName = true;
11793 // std::map<std::string, TH1 *> histGroups;
11794 // std::vector<TH1 *> hhs;
11795 // std::set<TH1 *> histsWithBadTitles; // these histograms will have their titles autoFormatted
11796 //
11797 // // support for CMS model case where has single component containing many coeffs
11798 // // will build stack by setting each coeff equal to 0 in turn, rebuilding the histogram
11799 // // the difference from the "full" histogram will be the component
11800 // RooArgList cms_coefs;
11801 // if (!rarNode->components().empty()) {
11802 // auto comps = rarNode->components()[0];
11803 // for (auto &c : *comps) {
11804 // if (c->fFolder == "!.coeffs")
11805 // cms_coefs.add(*c->get<RooAbsArg>());
11806 // }
11807 // }
11808 // if (!cms_coefs.empty()) {
11809 // RooRealVar zero("zero", "", 0);
11810 // std::shared_ptr<TH1> prevHist(static_cast<TH1 *>(h->Clone()));
11811 // for (auto c : cms_coefs) {
11812 // // seems I have to remake the function each time, as haven't figured out what cache needs
11813 // clearing? std::unique_ptr<RooAbsReal> f(
11814 // dynamic_cast<RooAbsReal *>(rarNode->components()[0]->get()->Clone("tmpCopy")));
11815 // zero.setAttribute(
11816 // Form("ORIGNAME:%s", c->GetName())); // used in redirectServers to say what this
11817 // replaces
11818 // f->redirectServers(RooArgSet(zero), false, true); // each time will replace one additional coef
11819 // // zero.setAttribute(Form("ORIGNAME:%s",c->GetName()),false); (commented out so that on next
11820 // iteration
11821 // // will still replace all prev)
11822 // auto hh = xRooNode(*f, *this).BuildHistogram(v);
11823 // hh->SetName(c->GetName());
11824 // if (sf)
11825 // hh->Scale(sf->getVal());
11826 // if (strlen(hh->GetTitle()) == 0) {
11827 // hh->SetTitle(c->GetName()); // ensure all hists has titles
11828 // histsWithBadTitles.insert(hh);
11829 // } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
11830 // histsWithBadTitles.insert(hh);
11831 // }
11832 // titleMatchName &= (TString(c->GetName()) == hh->GetTitle() ||
11833 // TString(hh->GetTitle()).BeginsWith(TString(c->GetName()) + "_"));
11834 // std::shared_ptr<TH1> nextHist(static_cast<TH1 *>(hh->Clone()));
11835 // hh->Add(prevHist.get(), -1.);
11836 // hh->Scale(-1.);
11837 // hhs.push_back(hh);
11838 // prevHist = nextHist;
11839 // }
11840 // } else if (get<RooSimultaneous>()) {
11841 // // need to create a histogram for each sample across all the channels - will rely on functionality
11842 // below to
11843 // // merge them based on titles
11844 //
11845 // for (auto &chan : bins()) {
11846 // TString chanName(chan->GetName());
11847 // chanName = chanName(chanName.Index("=") + 1, chanName.Length());
11848 // auto samps = chan->mainChild();
11849 // if (!samps)
11850 // samps = *chan;
11851 // for (auto &samp : samps.components()) {
11852 // auto hh = static_cast<TH1 *>(h->Clone(samp->GetName()));
11853 // hh->Reset();
11854 // hh->SetTitle(samp->GetTitle());
11855 // if (strlen(hh->GetTitle()) == 0) {
11856 // hh->SetTitle(samp->GetName());
11857 // histsWithBadTitles.insert(hh);
11858 // } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
11859 // histsWithBadTitles.insert(hh);
11860 // }
11861 // hh->SetTitle(TString(hh->GetTitle())
11862 // .ReplaceAll(TString(chan->get()->GetName()) + "_",
11863 // "")); // remove occurance of channelname_ in title (usually
11864 // prefix)
11865 // titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
11866 // TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
11867 // hh->SetBinContent(hh->GetXaxis()->FindFixBin(chanName), samp->GetContent());
11868 // hhs.push_back(hh);
11869 // }
11870 // }
11871 // } else {
11872 // for (auto &samp : rarNode->components()) {
11873 // auto hh = samp->BuildHistogram(v,false,false,1,0,"",false,false,0,h); // passing h to ensure
11874 // binning is the same for all subcomponent hists if (sf)
11875 // hh->Scale(sf->getVal());
11876 // hhs.push_back(hh);
11877 // if (strlen(hh->GetTitle()) == 0) {
11878 // hh->SetTitle(samp->GetName()); // ensure all hists has titles
11879 // histsWithBadTitles.insert(hh);
11880 // } else if (strcmp(hh->GetName(), hh->GetTitle()) == 0) {
11881 // histsWithBadTitles.insert(hh);
11882 // }
11883 // titleMatchName &= (TString(samp->GetName()) == hh->GetTitle() ||
11884 // TString(hh->GetTitle()).BeginsWith(TString(samp->GetName()) + "_"));
11885 // }
11886 // }
11887 //
11888 // if (!hhs.empty()) {
11889 // for (auto &hh : hhs) {
11890 // allTitles.insert(hh->GetTitle());
11891 // }
11892 //
11893 // // get common prefix to strip off only if all titles match names and
11894 // // any title is longer than 10 chars
11895 // size_t e = std::min(allTitles.begin()->size(), allTitles.rbegin()->size());
11896 // size_t ii = 0;
11897 // bool goodPrefix = false;
11898 // std::string commonSuffix;
11899 // if (titleMatchName && hhs.size() > 1) {
11900 // while (ii < e - 1 && allTitles.begin()->at(ii) == allTitles.rbegin()->at(ii)) {
11901 // ii++;
11902 // if (allTitles.begin()->at(ii) == '_' || allTitles.begin()->at(ii) == ' ')
11903 // goodPrefix = true;
11904 // }
11905 //
11906 // // find common suffix if there is one .. must start with a "_"
11907 // bool stop = false;
11908 // while (!stop && commonSuffix.size() < size_t(e - 1)) {
11909 // commonSuffix = allTitles.begin()->substr(allTitles.begin()->length() - commonSuffix.length() -
11910 // 1); for (auto &t : allTitles) {
11911 // if (!TString(t).EndsWith(commonSuffix.c_str())) {
11912 // commonSuffix = commonSuffix.substr(1);
11913 // stop = true;
11914 // break;
11915 // }
11916 // }
11917 // }
11918 // if (commonSuffix.find('_') == std::string::npos) {
11919 // commonSuffix = "";
11920 // } else {
11921 // commonSuffix = commonSuffix.substr(commonSuffix.find('_'));
11922 // }
11923 // }
11924 // if (!goodPrefix)
11925 // ii = 0;
11926 //
11927 // // also find how many characters are needed to distinguish all entries (that dont have the same
11928 // name)
11929 // // then carry on up to first space or underscore
11930 // size_t jj = 0;
11931 // std::map<std::string, std::string> reducedTitles;
11932 // while (reducedTitles.size() != allTitles.size()) {
11933 // jj++;
11934 // std::map<std::string, int> titlesMap;
11935 // for (auto &s : allTitles) {
11936 // if (reducedTitles.count(s))
11937 // continue;
11938 // titlesMap[s.substr(0, jj)]++;
11939 // }
11940 // for (auto &s : allTitles) {
11941 // if (titlesMap[s.substr(0, jj)] == 1 && (jj >= s.length() || s.at(jj) == ' ' || s.at(jj) ==
11942 // '_')) {
11943 // reducedTitles[s] = s.substr(0, jj);
11944 // }
11945 // }
11946 // }
11947 //
11948 // // strip common prefix and suffix before adding
11949 // for (auto ritr = hhs.rbegin(); ritr != hhs.rend(); ++ritr) { // go in reverse order
11950 // if (!histsWithBadTitles.count((*ritr))) {
11951 // continue;
11952 // }
11953 // auto _title = (hhs.size() > 5) ? reducedTitles[(*ritr)->GetTitle()] : (*ritr)->GetTitle();
11954 // _title = _title.substr(ii < _title.size() ? ii : 0);
11955 // if (!commonSuffix.empty() && TString(_title).EndsWith(commonSuffix.c_str()))
11956 // _title = _title.substr(0, _title.length() - commonSuffix.length());
11957 // (*ritr)->SetTitle(_title.c_str());
11958 // }
11959 // }
11960 //
11961 // for (auto &hh : hhs) {
11962 // // automatically group hists that all have the same title
11963 // if (histGroups.find(hh->GetTitle()) == histGroups.end()) {
11964 // histGroups[hh->GetTitle()] = hh;
11965 // } else {
11966 // // add it into this group
11967 // histGroups[hh->GetTitle()]->Add(hh);
11968 // delete hh;
11969 // hh = nullptr;
11970 // continue;
11971 // }
11972 // auto hhMin = (hh->GetMinimum() == 0) ? hh->GetMinimum(1e-9) : hh->GetMinimum();
11973 // if (!stack->GetHists() && h->GetMinimum() > hhMin) {
11974 // auto newMin = hhMin - (h->GetMaximum() - hhMin) * gStyle->GetHistTopMargin();
11975 // if (hhMin >= 0 && newMin < 0)
11976 // newMin = hhMin * 0.99;
11977 // adjustYRange(newMin, h->GetMaximum());
11978 // }
11979 //
11980 // /*if(stack->GetHists() && stack->GetHists()->GetEntries()>0) {
11981 // // to remove rounding effects on bin boundaries, see if binnings compatible
11982 // auto _h1 = dynamic_cast<TH1*>(stack->GetHists()->At(0));
11983 // if(_h1->GetNbinsX()==hh->GetNbinsX()) TODO ... finish dealing with silly rounding effects
11984 // }*/
11985 // TString thisOpt = dOpt;
11986 // // uncomment next line to blend continuous with discrete components .. get some unpleasant "poke
11987 // through"
11988 // // effects though
11989 // // if(auto s = samp->get<RooAbsReal>(); s) thisOpt =
11990 // s->isBinnedDistribution(*dynamic_cast<RooAbsArg*>(v)) ?
11991 // // "" : "LF2";
11992 // stack->Add(hh, thisOpt);
11993 // }
11994 // stack->SetBit(kCanDelete); // should delete its sub histograms
11995 // stack->Draw("noclear same");
11996 // h->Draw(
11997 // dOpt + sOpt +
11998 // "same"); // overlay again .. if stack would cover original hist (negative components) we still see
11999 // integral
12000 // h->Draw("axissame"); // redraws axis
12001 //
12002 // TList *ll = stack->GetHists();
12003 // if (ll && ll->GetEntries()) {
12004 //
12005 // // finally, ensure all hists are styled
12006 // for (auto ho : *ll) {
12007 // TH1 *hh = dynamic_cast<TH1 *>(ho);
12008 // if (!hh)
12009 // continue;
12010 // bool createdStyle = (xRooNode(*hh, *this).styles(nullptr, false).get<TStyle>() == nullptr);
12011 //
12012 // if (createdStyle) {
12013 // // give hist a color, that isn't the same as any other hists color
12014 // hh->SetFillStyle(1001); // solid fill style
12015 // bool used = false;
12016 // do {
12017 // hh->SetFillColor((count++));
12018 // // check not already used this color
12019 // used = false;
12020 // for (auto ho2 : *ll) {
12021 // TH1 *hh2 = dynamic_cast<TH1 *>(ho2);
12022 // if (!hh2)
12023 // continue;
12024 // auto _styleNode = xRooNode(*hh2, *this).styles(hh2, false);
12025 // auto _style = _styleNode.get<TStyle>();
12026 // if (hh != hh2 && _style && _style->GetFillColor() == hh->GetFillColor()) {
12027 // used = true;
12028 // break;
12029 // }
12030 // }
12031 // } while (used);
12032 // }
12033 //
12034 // auto _styleNode = xRooNode(*hh, *this).styles(hh);
12035 // if (auto _style = _styleNode.get<TStyle>()) {
12036 // *dynamic_cast<TAttLine *>(hh) = *_style;
12037 // *dynamic_cast<TAttFill *>(hh) = *_style;
12038 // *dynamic_cast<TAttMarker *>(hh) = *_style;
12039 // }
12040 // // for stacks, fill color of white should be color 10 unless fill style is 0
12041 // if (hh->GetFillColor() == kWhite && hh->GetFillStyle() != 0) {
12042 // // kWhite means 'transparent' in ROOT ... should really use a FillStyle of 0 for that
12043 // // so assume user wanted actual white, which is color 10
12044 // hh->SetFillColor(10);
12045 // }
12046 // addLegendEntry(hh, hh->GetTitle(), "f");
12047 // }
12048 // }
12049 // }
12050 } else if (!overlayExisted) {
12051
12052 if (errHist) {
12053 addLegendEntry(errHist, strlen(errHist->GetTitle()) ? errHist->GetTitle() : GetName(), "fl");
12054 } else {
12055 addLegendEntry(h, strlen(h->GetTitle()) ? h->GetTitle() : GetName(), (hasError) ? "fl" : "l");
12056 }
12057 }
12058
12059 if (errHist) {
12060 dOpt.ReplaceAll("TEXT", "");
12061 errHist->Draw(dOpt + (dOpt.Contains("LF2") ? "e3same" : "e2same"));
12062 double ymax = -std::numeric_limits<double>::infinity();
12063 double ymin = std::numeric_limits<double>::infinity();
12064 for (int i = 1; i <= errHist->GetNbinsX(); i++) {
12065 ymax = std::max(ymax, errHist->GetBinContent(i) + errHist->GetBinError(i));
12066 ymin = std::min(ymin, errHist->GetBinContent(i) - errHist->GetBinError(i));
12067 }
12069 } else {
12070 adjustYRange(h->GetMinimum() * 0.9, h->GetMaximum() * 1.1);
12071 }
12072
12073 if ((!auxPlotTitle.empty()) && !hasSame) {
12074 // create a pad for the ratio ... shift the bottom margin of this pad to make space for it
12075 double padFrac = 0.3;
12076 auto _tmpPad = gPad;
12077 gPad->SetBottomMargin(padFrac);
12078 auto ratioPad = new TPad("auxPad", "aux plot", 0, 0, 1, padFrac);
12079 ratioPad->SetFillColor(_tmpPad->GetFillColor());
12080 ratioPad->SetNumber(1);
12081 ratioPad->SetBottomMargin(ratioPad->GetBottomMargin() / padFrac);
12082 ratioPad->SetTopMargin(0.04);
12083 ratioPad->SetLeftMargin(gPad->GetLeftMargin());
12084 ratioPad->SetRightMargin(gPad->GetRightMargin());
12085 ratioPad->cd();
12086 TH1 *ratioHist = dynamic_cast<TH1 *>((errHist) ? errHist->Clone("auxHist") : h->Clone("auxHist"));
12087 ratioHist->Reset();
12088 ratioHist->Add(h); // removes function list
12089 ratioHist->SetDirectory(nullptr);
12090 ratioHist->SetTitle((errHist) ? errHist->GetName()
12091 : h->GetName()); // abuse the title string to hold the name of the main hist
12092
12093 ratioHist->GetYaxis()->SetNdivisions(5, 0, 0);
12094 ratioHist->GetYaxis()->SetTitle(auxPlotTitle.c_str());
12095 ratioHist->SetTitle(
12096 TString::Format("%s|%s", ratioHist->GetTitle(),
12097 auxPlotTitle.c_str())); // used when plotting data (above) to decide what to calculate
12098 ratioHist->SetMaximum();
12099 ratioHist->SetMinimum(); // resets min and max
12100 ratioPad->SetGridy();
12101
12102 for (int i = 1; i <= ratioHist->GetNbinsX(); i++) {
12103 double val = ratioHist->GetBinContent(i);
12104 double err = ratioHist->GetBinError(i);
12105 ratioHist->SetBinContent(i, std::get<0>(auxFunctions[auxPlotTitle])(val, val, err));
12106 ratioHist->SetBinError(i, std::get<0>(auxFunctions[auxPlotTitle])(val + err, val, err) -
12107 ratioHist->GetBinContent(i));
12108 }
12109
12110 double rHeight = (1. - padFrac) / padFrac; //(_tmpPad->GetWNDC())/(gPad->GetHNDC());
12111 if (ratioHist->GetYaxis()->GetTitleFont() % 10 == 2) {
12112 ratioHist->GetYaxis()->SetTitleSize(ratioHist->GetYaxis()->GetTitleSize() * rHeight);
12113 ratioHist->GetYaxis()->SetLabelSize(ratioHist->GetYaxis()->GetLabelSize() * rHeight);
12114 ratioHist->GetXaxis()->SetTitleSize(ratioHist->GetXaxis()->GetTitleSize() * rHeight);
12115 ratioHist->GetXaxis()->SetLabelSize(ratioHist->GetXaxis()->GetLabelSize() * rHeight);
12116 ratioHist->GetYaxis()->SetTitleOffset(ratioHist->GetYaxis()->GetTitleOffset() / rHeight);
12117 } else {
12118#if ROOT_VERSION_CODE < ROOT_VERSION(6, 26, 00)
12119 ratioHist->GetYaxis()->SetTitleOffset(ratioHist->GetYaxis()->GetTitleOffset() / rHeight);
12120#endif
12121 }
12122 ratioHist->GetXaxis()->SetTickLength(ratioHist->GetXaxis()->GetTickLength() * rHeight);
12123 ratioHist->SetStats(false);
12124 ratioHist->SetBit(TH1::kNoTitle);
12125 ratioHist->SetBit(kCanDelete);
12126 if (errHist) {
12127 auto _h = dynamic_cast<TH1 *>(ratioHist->Clone("auxHist_clone"));
12128 _h->SetDirectory(nullptr);
12129 _h->SetFillColor(0);
12130 ratioHist->GetListOfFunctions()->Add(_h, "histsame");
12131 //_h->Draw("histsame");
12132 }
12133 ratioHist->GetListOfFunctions()->Add(new TExec(
12134 ".updateAxis",
12135 TString::Format("auto h1 = (TH1*)%p; auto h2 = (TH1*)%p; if(h2->GetXaxis()->GetFirst() != "
12136 "h1->GetXaxis()->GetFirst() || h1->GetXaxis()->GetLast()!=h2->GetXaxis()->GetLast()) "
12137 "{h2->GetXaxis()->SetRange(h1->GetXaxis()->GetFirst(),h1->GetXaxis()->GetLast());if(gPad) "
12138 "{gPad->GetCanvas()->Paint();gPad->GetCanvas()->Update();}}",
12139 (void *)ratioHist, (void *)(h))));
12140 ratioHist->Draw((errHist ? "e2" : ""));
12141
12142 _tmpPad->cd();
12143 ratioPad->Draw();
12144 } else if (auto ratioPad = dynamic_cast<TPad *>(gPad->GetPrimitive("auxPad")); hasSame && ratioPad) {
12145 // need to draw histogram in the ratio pad ...
12146 // if doing overlay need to update histogram
12147
12148 if (auto hr = dynamic_cast<TH1 *>(ratioPad->GetPrimitive("auxHist"));
12149 hr && auxFunctions.find(hr->GetYaxis()->GetTitle()) != auxFunctions.end()) {
12150 TString histName = hr->GetTitle(); // split it by | char
12151 TString histType = histName(histName.Index('|') + 1, histName.Length());
12152 histName = histName(0, histName.Index('|'));
12153
12154 if (auto hnom = dynamic_cast<TH1 *>(gPad->GetPrimitive(histName)); hnom) {
12155 h = dynamic_cast<TH1 *>(h->Clone(h->GetName()));
12156 h->SetDirectory(nullptr);
12157 h->SetBit(kCanDelete);
12158 for (int i = 1; i <= hnom->GetNbinsX(); i++) {
12159 double val = h->GetBinContent(i);
12160 double err = h->GetBinError(i);
12161 h->SetBinContent(i, std::get<0>(auxFunctions[hr->GetYaxis()->GetTitle()])(
12162 h->GetBinContent(i), hnom->GetBinContent(i), hnom->GetBinError(i)));
12163 h->SetBinError(i, std::get<0>(auxFunctions[hr->GetYaxis()->GetTitle()])(
12164 val + err, hnom->GetBinContent(i), hnom->GetBinError(i)) -
12165 h->GetBinContent(i));
12166 }
12167 auto _tmpPad = gPad;
12168 ratioPad->cd();
12169 if (hasOverlay) {
12170 if (auto existing = dynamic_cast<TH1 *>(ratioPad->GetPrimitive(h->GetName())); existing) {
12171 existing->Reset();
12172 existing->Add(h);
12173 delete h;
12174 h = existing;
12175 overlayExisted = true;
12176 } else {
12177 h->Draw(dOpt);
12178 }
12179 } else {
12180 h->Draw(dOpt);
12181 }
12182 double ymax = -std::numeric_limits<double>::infinity();
12183 double ymin = std::numeric_limits<double>::infinity();
12184 for (int i = 1; i <= h->GetNbinsX(); i++) {
12185 ymax = std::max(ymax, h->GetBinContent(i) + h->GetBinError(i));
12186 ymin = std::min(ymin, h->GetBinContent(i) - h->GetBinError(i));
12187 }
12188 adjustYRange(ymin, ymax, hr, std::get<1>(auxFunctions[hr->GetYaxis()->GetTitle()]));
12189 // adjustYRange(h->GetMinimum() * (h->GetMinimum()<0 ? 1.1 : 0.9), h->GetMaximum() * (h->GetMinimum()<0 ?
12190 // 0.9 : 1.1), hr, std::get<1>(auxFunctions[hr->GetYaxis()->GetTitle()]));
12191 gPad->Modified();
12192 _tmpPad->cd();
12193 }
12194 }
12195 }
12196
12197 // see if it's in a simultaneous so need to select a cat
12198 /*auto _parent = fParent;
12199 auto _me = rar;
12200 while(_parent) {
12201 if (auto s = _parent->get<RooSimultaneous>(); s) {
12202 for (auto c : s->indexCat()) {
12203 if (auto p = s->getPdf(c.first.c_str());_me==p) {
12204 gPad->SetName(c.first.c_str());
12205 break;
12206 }
12207 }
12208 break;
12209 }
12210 _me = _parent->get<RooAbsReal>();
12211 _parent = _parent->fParent;
12212 }*/
12213
12214 // now draw selected datasets on top if this was a pdf
12215 if (auto _pdf = get<RooAbsPdf>();
12216 !hasSame && _pdf /*&& (_pdf->canBeExtended() || robs().empty())*/ && coefs(true).empty()) {
12217 auto _dsets = datasets();
12218 // bool _drawn=false;
12219 for (auto &d : _dsets) {
12220 if (d->get()->TestBit(1 << 20)) {
12221 d->Draw("same");
12222 //_drawn=true;
12223 }
12224 }
12225 // if (!_drawn && !_dsets.empty()) _dsets[0]->Draw("same"); // always draw if has a dataset
12226 }
12227
12228 gPad->Modified();
12229 // gPad->Update();
12230 getLegend();
12231 gPad->Modified();
12232 // gPad->Update();
12233}
12234
12235void xRooNode::SaveAs(const char *filename, Option_t *option) const
12236{
12238 sOpt.ToLower();
12241 if (sFilename.Contains(".root:")) {
12242 objName = TString(sFilename(sFilename.Index(".root:") + 6, sFilename.Length()));
12243 sFilename = sFilename(0, sFilename.Index(".root:") + 5);
12244 }
12245
12246 if (auto pdf = get<RooAbsPdf>(); pdf) {
12247 // if saving a pdf, will put it inside a workspace, with its datasets, and a modelconfig
12248 // then save the workspace
12249 RooWorkspace w(objName, TString::Format("Workspace of %s", GetTitle()));
12250 xRooNode ws(w);
12251 auto addedPdf = ws.Add(*this);
12252 for (auto ds : datasets()) {
12253 ws.Add(*ds);
12254 }
12255 RooStats::ModelConfig mc("ModelConfig", GetTitle(), &w);
12256 mc.SetPdf(addedPdf->GetName());
12257 mc.SetObservables(*addedPdf.robs().get<RooArgList>());
12258 mc.SetGlobalObservables(*addedPdf.globs().get<RooArgList>());
12259 mc.SetNuisanceParameters(*addedPdf.np().get<RooArgList>());
12260 mc.SetParametersOfInterest(*addedPdf.poi().get<RooArgList>());
12261 ws.Add(mc);
12262
12263 // save the workspace
12265
12266 } else if (auto w = get<RooWorkspace>(); w) {
12267 // ensure the current color set is saved in the workspace
12268 w->import(*gROOT->GetListOfColors(), true);
12269
12270 if (sFilename.EndsWith(".json")) {
12271#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 26, 00)
12272 // stream with json tool
12274 if (tool.exportJSON(sFilename.Data())) {
12275 Info("SaveAs", "%s saved to %s", w->GetName(), sFilename.Data());
12276 } else {
12277 Error("SaveAs", "Unable to save to %s", sFilename.Data());
12278 }
12279#else
12280 Error("SaveAs", "json format workspaces only in ROOT 6.26 onwards");
12281#endif
12282 return;
12283 }
12284
12285#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
12286 // before saving, clear the eocache of all owned nodes
12287 // because causes memory leak when read back in (workspace streamer immediately overwrites the caches)
12288 // fixed in: https://github.com/root-project/root/pull/12024
12289 for (auto &c : w->components()) {
12290 c->_eocache = nullptr;
12291 }
12292#endif
12293 // const_cast<Node2*>(this)->sterilize(); - tried this to reduce mem leak on readback but no improve
12294#if ROOT_VERSION_CODE < ROOT_VERSION(6, 38, 00)
12295 if (!w->writeToFile(sFilename, sOpt != "update")) {
12296#else
12297 if (w->writeToFile(sFilename, sOpt != "update")) {
12298#endif
12299 Info("SaveAs", "%s saved to %s", w->GetName(), sFilename.Data());
12300 // save any fitDatabase that is loaded in memory too
12301 // TODO: We should do this as well for SaveAs on a scan object
12302 std::function<void(TDirectory *, TDirectory *)> CopyDir;
12303
12305 auto dir = dest->GetDirectory(source->GetName());
12306 if (!dir) {
12307 dir = dest->mkdir(source->GetName());
12308 }
12309 for (auto k : *source->GetListOfKeys()) {
12310 auto key = dynamic_cast<TKey *>(k);
12311 const char *classname = key->GetClassName();
12312 TClass *cl = gROOT->GetClass(classname);
12313 // std::cout << "processing " << key->GetName() << " " << classname << std::endl;
12314 if (!cl) {
12315 continue;
12316 } else if (cl->InheritsFrom(TDirectory::Class())) {
12317 CopyDir(source->GetDirectory(key->GetName()), dir);
12318 } else {
12319 // don't write object if it already exists
12320 if (dir->FindKey(key->GetName()))
12321 continue;
12322 // support FitConfigs ....
12323 if (strcmp(classname, "ROOT::Fit::FitConfig") == 0) {
12324 auto fc = key->ReadObject<ROOT::Fit::FitConfig>();
12325 dir->WriteObject(fc, key->GetName());
12326 delete fc;
12327 } else {
12328 TObject *obj = key->ReadObj();
12329 if (obj) {
12330 dir->WriteTObject(obj, key->GetName());
12331 delete obj;
12332 }
12333 }
12334 }
12335 }
12336 };
12337 if (gROOT->GetListOfFiles()) {
12338 for (auto key : *gROOT->GetListOfFiles()) {
12339 if (auto fitDb = dynamic_cast<TMemFile *>(key);
12340 fitDb /*&& TString(key->GetName()).BeginsWith("fitDatabase_")*/) {
12341 CopyDir(fitDb, std::make_unique<TFile>(sFilename, "UPDATE").get());
12342 Info("SaveAs", "Saved %s to %s", fitDb->GetName(), sFilename.Data());
12343 }
12344 }
12345 }
12346 } else {
12347 Error("SaveAs", "Unable to save to %s", sFilename.Data());
12348 }
12349#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
12350 // restore the cache to every node
12351 for (auto &c : w->components()) {
12352 c->setExpensiveObjectCache(w->expensiveObjectCache());
12353 }
12354#endif
12355 }
12356}
12357
12358double xRooNode::GetBinError(int bin, const xRooNode &fr, int nToys, bool errorsHi, bool errorsLo) const
12359{
12360 auto res = GetBinErrors(bin, bin, fr, nToys, errorsHi, errorsLo);
12361 if (res.empty())
12362 return std::numeric_limits<double>::quiet_NaN();
12363 return res.at(0);
12364}
12365
12366std::vector<double> xRooNode::contents() const
12367{
12368 std::vector<double> out;
12369 out.reserve(size());
12370 for (auto child : *this) {
12371 out.push_back(child->GetContent());
12372 }
12373 return out;
12374}
12375
12377{
12378
12379 auto _fr = fr.get<RooFitResult>();
12380
12381 if (!_fr) {
12382 return covariances(fitResult());
12383 }
12384
12385 auto rho = _fr->correlationMatrix();
12386
12387 TMatrixDSym out(size());
12388
12389 auto _pars = pars();
12390
12391 // 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]
12392 // - nu[theta_n_down]) consistent with propagatedError formula
12393
12394 for (int m = 0; m < rho.GetNrows(); m++) {
12395 auto p_m = dynamic_cast<RooRealVar *>(_fr->floatParsFinal().at(m));
12396 if (!p_m)
12397 continue; // skip categoricals
12398 auto _p = dynamic_cast<RooAbsRealLValue *>(_pars.get<RooArgList>()->find(p_m->GetName()));
12399 if (!_p)
12400 continue;
12401 auto tmp = _p->getVal();
12402 _p->setVal(p_m->getVal() + p_m->getErrorHi());
12403 auto nu_m = contents();
12404 _p->setVal(p_m->getVal() + p_m->getErrorLo());
12405 auto nu_m2 = contents();
12406 _p->setVal(tmp);
12407 for (int n = 0; n < rho.GetNrows(); n++) {
12408 auto p_n = dynamic_cast<RooRealVar *>(_fr->floatParsFinal().at(n));
12409 if (!p_n)
12410 continue; // skip categoricals
12411 auto _p2 = dynamic_cast<RooAbsRealLValue *>(_pars.get<RooArgList>()->find(p_n->GetName()));
12412 if (!_p2)
12413 continue;
12414 auto tmp2 = _p2->getVal();
12415 _p2->setVal(p_n->getVal() + p_n->getErrorHi());
12416 auto nu_n = (p_n == p_m) ? nu_m : contents();
12417 _p2->setVal(p_n->getVal() + p_n->getErrorLo());
12418 auto nu_n2 = (p_n == p_m) ? nu_m2 : contents();
12419 _p2->setVal(tmp2);
12420 for (int i = 0; i < out.GetNrows(); i++) {
12421 for (int j = 0; j < out.GetNrows(); j++) {
12422 out(i, j) += 0.25 * (nu_m[i] - nu_m2[i]) * rho(m, n) * (nu_n[j] - nu_n2[j]);
12423 }
12424 }
12425 }
12426 }
12427 return out;
12428}
12429
12430std::pair<double, double>
12431xRooNode::IntegralAndError(const xRooNode &fr, const char *rangeName, int nToys, bool errorsHi, bool errorsLo) const
12432{
12433 double out = 1.;
12434 double err = std::numeric_limits<double>::quiet_NaN();
12435
12436 std::unique_ptr<RooAbsCollection> _snap;
12438 if (auto _fr = fr.get<RooFitResult>()) {
12439 _pars.add(pars().argList());
12440 _snap.reset(_pars.snapshot());
12441 _pars = _fr->floatParsFinal();
12442 _pars = _fr->constPars();
12443 }
12444
12445 auto _obs = obs();
12446 RooArgSet sobs(*_obs.get<RooArgList>()); // need to make explicit RooArgSet for ROOT 6.36 onwards
12447 auto _coefs = coefs(); // need here to keep alive owned RooProduct
12448 if (auto c = _coefs.get<RooAbsReal>(); c) {
12449 out = c->getVal(sobs); // assumes independent of observables!
12450 }
12451
12452 if (auto p = dynamic_cast<RooAbsPdf *>(get()); p) {
12453 // prefer to use expectedEvents for integrals of RooAbsPdf e.g. for RooProdPdf wont include constraint terms
12454 if (rangeName)
12455 p->setNormRange(rangeName);
12456#if ROOT_VERSION_CODE >= ROOT_VERSION(6, 27, 00)
12458#endif
12459 out *= p->expectedEvents(*_obs.get<RooArgList>());
12460#if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
12461 // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
12462 p->_normSet = nullptr;
12463#endif
12464 err = GetBinError(-1, fr, nToys, errorsHi, errorsLo);
12465 if (rangeName)
12466 p->setNormRange(nullptr);
12467 } else if (auto p2 = dynamic_cast<RooAbsReal *>(get()); p2) {
12468 // only integrate over observables we actually depend on
12469 auto f = std::shared_ptr<RooAbsReal>(
12470 p2->createIntegral(*std::unique_ptr<RooArgSet>(p2->getObservables(*_obs.get<RooArgList>())),
12471 rangeName)); // did use x here before using obs
12472 RooProduct pr("int_x_coef", "int_x_coef",
12473 RooArgList(*f, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>()));
12474 out *= f->getVal();
12475 err = xRooNode(pr, *this).GetBinError(-1, fr, nToys, errorsHi, errorsLo);
12476 sterilize(); // needed so that we can forget properly about the integral we just created (and are deleting)
12477 } else if (get<RooAbsData>()) {
12478 out = 0;
12479 auto vals = GetBinContents(1, 0); // returns all bins
12480 auto ax = (rangeName) ? GetXaxis() : nullptr;
12481 auto rv = (ax) ? dynamic_cast<RooRealVar *>(ax->GetParent()) : nullptr;
12482 auto cv = (ax && !rv) ? dynamic_cast<RooCategory *>(ax->GetParent()) : nullptr;
12483 int i = 0;
12484 for (auto &v : vals) {
12485 i++;
12486 if (rangeName) {
12487 if (rv && !rv->inRange(ax->GetBinCenter(i), rangeName))
12488 continue;
12489 if (cv && !cv->isStateInRange(rangeName, ax->GetBinLabel(i)))
12490 continue;
12491 }
12492 out += v;
12493 }
12494 err = 0; // should this be sqrt(sum(v^2)) or something similar
12495 } else {
12496 out = std::numeric_limits<double>::quiet_NaN();
12497 }
12498 if (_snap) {
12499 _pars.RooAbsCollection::operator=(*_snap);
12500 }
12501 return std::make_pair(out, err);
12502}
12503
12504std::vector<double>
12505xRooNode::GetBinErrors(int binStart, int binEnd, const xRooNode &_fr, int nToys, bool errorHi, bool errorLo) const
12506{
12507 // note: so far this method is inconsistent with the BuildHistogram in ways:
12508 // no projection over other variables
12509 // July2023: made RooRealSumPdf evaluate as a function if doesn't have a floor
12510 // but this method will still evaluate it as a pdf (uses PdfWrapper)
12511 // but can get away with it while added NaN recovery to getSimplePropagatedError to pickup raw values
12512
12513 if (fBinNumber != -1) {
12514 if (binStart != binEnd || !fParent) {
12515 throw std::runtime_error(TString::Format("%s is a bin - only has one value", GetName()));
12516 }
12517 return fParent->GetBinErrors(fBinNumber, fBinNumber, _fr, nToys, errorHi, errorLo);
12518 }
12519
12520 std::vector<double> out;
12521
12522 auto _hist = BuildHistogram(nullptr, true, true, binStart, binEnd, _fr, errorHi, errorLo, nToys);
12523 if (!_hist)
12524 return out;
12525 if (binEnd == 0) {
12526 binEnd = _hist->GetNbinsX();
12527 } else if (binEnd == binStart && binEnd == -1) {
12528 binStart = 1;
12529 binEnd = 1; // done an integral, so histogram has only 1 bin
12530 }
12531 for (int bin = binStart; bin <= binEnd; bin++) {
12532 out.push_back(((errorLo && !errorHi) ? (-1.) : 1.) *
12533 _hist->GetBinError(bin)); // using same convention as RooFit that Lo errors are negative
12534 }
12535 delete _hist;
12536 return out;
12537
12538 // auto o = dynamic_cast<RooAbsReal *>(get());
12539 // if (!o)
12540 // return out;
12541 //
12542 // std::shared_ptr<RooFitResult> fr = std::dynamic_pointer_cast<RooFitResult>(_fr.fComp);
12543 // //= dynamic_cast<RooFitResult*>( _fr.get<RooFitResult>() ? _fr->Clone() : fitResult()->Clone());
12544 //
12545 // auto _coefs = coefs();
12546 //
12547 // if (!fr) {
12548 // // need to ensure coefs, if any, are included in fit result retrieval so all pars are loaded
12549 // auto frn = (!_coefs.get() ? *this : xRooNode(RooProduct("tmp", "tmp", RooArgList(*o,
12550 // *_coefs.get<RooAbsReal>()))))
12551 // .fitResult();
12552 // if (strlen(_fr.GetName()))
12553 // frn = frn.reduced(_fr.GetName());
12554 //
12555 // // use name to reduce the fit result, if one given
12556 // fr = std::dynamic_pointer_cast<RooFitResult>(frn.fComp);
12557 // }
12558 //
12559 // if (!GETDMP(fr.get(), _finalPars)) {
12560 // fr->setFinalParList(RooArgList());
12561 // }
12562 //
12563 // /// Oct2022: No longer doing this because want to allow fitResult to be used to get partial error
12564 // // // need to add any floating parameters not included somewhere already in the fit result ...
12565 // // RooArgList l;
12566 // // for(auto& p : pars()) {
12567 // // auto v = p->get<RooRealVar>();
12568 // // if (!v) continue;
12569 // // if (v->isConstant()) continue;
12570 // // if (fr->floatParsFinal().find(v->GetName())) continue;
12571 // // if (fr->_constPars && fr->_constPars->find(v->GetName())) continue;
12572 // // l.add(*v);
12573 // // }
12574 // //
12575 // // if (!l.empty()) {
12576 // // RooArgList l2; l2.addClone(fr->floatParsFinal());
12577 // // l2.addClone(l);
12578 // // fr->setFinalParList(l2);
12579 // // }
12580 //
12581 // TMatrixTSym<double> *prevCov = static_cast<TMatrixTSym<double> *>(GETDMP(fr.get(), _VM));
12582 //
12583 // if (!prevCov || size_t(prevCov->GetNcols()) < fr->floatParsFinal().size()) {
12584 // TMatrixDSym cov(fr->floatParsFinal().size());
12585 // if (prevCov) {
12586 // for (int i = 0; i < prevCov->GetNcols(); i++) {
12587 // for (int j = 0; j < prevCov->GetNrows(); j++) {
12588 // cov(i, j) = (*prevCov)(i, j);
12589 // }
12590 // }
12591 // }
12592 // int i = 0;
12593 // for (auto &p : fr->floatParsFinal()) {
12594 // if (!prevCov || i >= prevCov->GetNcols()) {
12595 // cov(i, i) = pow(dynamic_cast<RooRealVar *>(p)->getError(), 2);
12596 // }
12597 // i++;
12598 // }
12599 // int covQualBackup = fr->covQual();
12600 // fr->setCovarianceMatrix(cov);
12601 // fr->setCovQual(covQualBackup);
12602 // }
12603 //
12604 // bool doBinWidth = false;
12605 // auto ax = (binStart == -1 && binEnd == -1) ? nullptr : GetXaxis();
12606 //
12607 // auto _obs = obs(); // may own an obs so keep alive here
12608 // RooArgList normSet = _obs.argList();
12609 // // to give consistency with BuildHistogram method, should be only the axis var if defined
12610 // if (ax) {
12611 // normSet.clear();
12612 // normSet.add(*dynamic_cast<RooAbsArg *>(ax->GetParent()));
12613 // }
12614 //
12615 // if (auto p = dynamic_cast<RooAbsPdf *>(o); ax && (p || _coefs.get() || o->getAttribute("density"))) {
12616 // // pdfs of samples embedded in a sumpdf (aka have a coef) will convert their density value to a content
12617 // doBinWidth = true;
12618 // }
12619 // if (binEnd == 0) {
12620 // if (ax) {
12621 // binEnd = ax->GetNbins();
12622 // } else {
12623 // binEnd = binStart;
12624 // }
12625 // }
12626 // for (int bin = binStart; bin <= binEnd; bin++) {
12627 // if (ax)
12628 // dynamic_cast<RooAbsLValue *>(ax->GetParent())->setBin(bin - 1, ax->GetName());
12629 // // if (!SetBin(bin)) { return out; }
12630 //
12631 // double res;
12632 // if (auto p = dynamic_cast<RooAbsPdf *>(o); p) {
12633 // // fr->covarianceMatrix().Print();
12634 // res = PdfWrapper(*p, _coefs.get<RooAbsReal>(), !ax).getSimplePropagatedError(*fr, normSet);
12635 // #if ROOT_VERSION_CODE < ROOT_VERSION(6, 27, 00)
12636 // // improved normSet invalidity checking, so assuming no longer need this in 6.28 onwards
12637 // p->_normSet = nullptr;
12638 // #endif
12639 // } else {
12640 // // res = o->getPropagatedError(*fr, normSet);
12641 // // // TODO: What if coef has error? - probably need a FuncWrapper class
12642 // // if (auto c = _coefs.get<RooAbsReal>(); c) {
12643 // // res *= c->getVal(normSet);
12644 // // }
12645 // res = RooProduct("errorEval", "errorEval",
12646 // RooArgList(*o, !_coefs.get() ? RooFit::RooConst(1) : *_coefs.get<RooAbsReal>()))
12647 // .getPropagatedError(*fr, normSet);
12648 // }
12649 // if (doBinWidth) {
12650 // res *= ax->GetBinWidth(bin);
12651 // }
12652 // out.push_back(res);
12653 // }
12654 //
12655 // return out;
12656}
12657
12658std::string cling::printValue(const xRooNode *v)
12659{
12660 if (!v)
12661 return "nullptr\n";
12662 if (!v->empty()) {
12663 std::string out;
12664 size_t left = v->size();
12665 for (auto n : *v) {
12666 left--;
12667 if (!out.empty())
12668 out += ",";
12669 else
12670 out += "{";
12671 out += n->GetName();
12672 if (out.length() > 100 && left > 0) {
12673 out += TString::Format(",... and %lu more", left);
12674 break;
12675 }
12676 }
12677 out += "}\n";
12678 out = std::string(Form("<%s> %s", v->get() ? v->get()->ClassName() : "nullptr", v->GetName())) + out;
12679 return out;
12680 }
12681 std::string out;
12682 if (!(*v)) {
12683 return "<nullptr>";
12684 } else {
12685 return Form("<%s> %s", v->get() ? v->get()->ClassName() : "nullptr", v->GetName());
12686 }
12687
12688 return out;
12689}
12690
@ 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
ROOT::RRangeCast< T, false, Range_t > static_range_cast(Range_t &&coll)
size_t size(const MatrixT &matrix)
retrieve the size of a square matrix
const char Option_t
Option string (const char)
Definition RtypesCore.h:80
@ kGray
Definition Rtypes.h:66
@ kRed
Definition Rtypes.h:67
@ kBlack
Definition Rtypes.h:66
@ kGreen
Definition Rtypes.h:67
@ kWhite
Definition Rtypes.h:66
@ kCyan
Definition Rtypes.h:67
@ kBlue
Definition Rtypes.h:67
@ kAzure
Definition Rtypes.h:68
@ kYellow
Definition Rtypes.h:67
@ kP10Blue
Definition Rtypes.h:72
static void indent(ostringstream &buf, int indent_level)
ROOT::Detail::TRangeCast< T, true > TRangeDynCast
TRangeDynCast is an adapter class that allows the typed iteration through a TCollection.
R__EXTERN TEnv * gEnv
Definition TEnv.h:170
void Warning(const char *location, const char *msgfmt,...)
Use this function in warning situations.
Definition TError.cxx:252
#define gClient
Definition TGClient.h:157
@ kMBOk
Definition TGMsgBox.h:33
@ kMBIconExclamation
Definition TGMsgBox.h:24
winID h TVirtualViewer3D TVirtualGLPainter p
winID h TVirtualViewer3D vv
Option_t Option_t option
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void data
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t dest
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char filename
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h offset
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t np
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t r
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t result
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t child
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void value
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void funcs
Option_t Option_t TPoint TPoint const char GetTextMagnitude GetFillStyle GetLineColor GetLineWidth GetMarkerStyle GetTextAlign GetTextColor GetTextSize void char Point_t Rectangle_t WindowAttributes_t Float_t Float_t Float_t Int_t Int_t UInt_t UInt_t Rectangle_t Int_t Int_t Window_t TString Int_t GCValues_t GetPrimarySelectionOwner GetDisplay GetScreen GetColormap GetNativeEvent const char const char dpyName wid window const char font_name cursor keysym reg const char only_if_exist regb h Point_t winding char text const char depth char const char Int_t count const char ColorStruct_t color const char Pixmap_t Pixmap_t PictureAttributes_t attr const char char ret_data h unsigned char height h Atom_t Int_t ULong_t ULong_t unsigned char prop_list Atom_t Atom_t Atom_t Time_t type
Option_t Option_t style
char name[80]
Definition TGX11.cxx:145
float xmin
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:2495
R__EXTERN TStyle * gStyle
Definition TStyle.h:442
R__EXTERN TSystem * gSystem
Definition TSystem.h:582
#define gPad
Color * colors
Definition X3DBuffer.c:21
#define _(A, B)
Definition cfortran.h:108
double GetBinLowEdge(Int_t bin) const override
Return low edge of bin.
Definition xRooNode.cxx:963
double GetBinUpEdge(Int_t bin) const override
Return up edge of bin.
Definition xRooNode.cxx:971
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:991
RooAbsRealLValue * rvar() const
void SetTitle(const char *title) override
Set the title of the TNamed.
Definition xRooNode.cxx:982
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.
Definition xRooNode.cxx:997
const char * GetTitle() const override
Returns title of object.
Definition xRooNode.cxx:978
double GetBinWidth(Int_t bin) const override
Return bin width.
Definition xRooNode.cxx:957
PadRefresher(TVirtualPad *p)
static int nExisting
A class which maps the current values of a RooRealVar (or a set of RooRealVars) to one of a number of...
bool isBinnedDistribution(const RooArgSet &obs) const override
Tests if the distribution is binned. Unless overridden by derived classes, this always returns false.
bool selfNormalized() const override
Shows if a PDF is self-normalized, which means that no attempt is made to add a normalization term.
~PdfWrapper() override
double getSimplePropagatedError(const RooFitResult &fr, const RooArgSet &nset_in) const
RooRealProxy fCoef
double evaluate() const override
Evaluate this PDF / function / constant. Needs to be overridden by all derived classes.
TObject * clone(const char *newname) const override
std::list< double > * binBoundaries(RooAbsRealLValue &obs, double xlo, double xhi) const override
Retrieve bin boundaries if this distribution is binned in obs.
PdfWrapper(const PdfWrapper &other, const char *name=nullptr)
RooRealProxy fExpPdf
PdfWrapper(RooAbsReal &f, RooAbsReal *coef, bool expEvMode=false, RooAbsPdf *expPdf=nullptr)
RooRealProxy fFunc
The PiecewiseInterpolation is a class that can morph distributions into each other,...
static std::shared_ptr< RooLinkedList > createNLLOptions()
Definition xRooFit.cxx:460
static std::pair< std::shared_ptr< RooAbsData >, std::shared_ptr< const RooAbsCollection > > generateFrom(RooAbsPdf &pdf, const RooFitResult &fr, bool expected=false, int seed=0)
Definition xRooFit.cxx:150
static xRooNLLVar createNLL(const std::shared_ptr< RooAbsPdf > pdf, const std::shared_ptr< RooAbsData > data, const RooLinkedList &nllOpts)
Definition xRooFit.cxx:101
static std::pair< double, double > matchPrecision(const std::pair< double, double > &in)
Definition xRooFit.cxx:2014
void Draw(Option_t *opt="") override
Default Draw method for all objects.
void Draw(Option_t *opt="") override
Default Draw method for all objects.
This xRooNLLVar object has several special methods, e.g.
Definition xRooNLLVar.h:59
xRooHypoSpace hypoSpace(const char *parName, int nPoints, double low, double high, double alt_value=std::numeric_limits< double >::quiet_NaN(), const xRooFit::Asymptotics::PLLType &pllType=xRooFit::Asymptotics::Unknown, int tsType=0)
The xRooNode class is designed to wrap over a TObject and provide functionality to aid with interacti...
Definition xRooNode.h:52
void _Add_(const char *name, const char *opt)
TH1 * BuildHistogram(RooAbsLValue *v=nullptr, bool empty=false, bool errors=false, int binStart=1, int binEnd=0, const xRooNode &fr="", bool errorsHi=false, bool errorsLo=false, int nErrorToys=0, TH1 *templateHist=nullptr, bool nostack=true, bool setInterp=false) const
xRooNode filter(const xRooNode &range) const
std::vector< std::shared_ptr< xRooNode > > fBrowsables
Definition xRooNode.h:531
xRooNLLVar nll(const xRooNode &_data, std::initializer_list< RooCmdArg > nllOpts) const
xRooNode poi() const
List of parameters of interest: parameters marked as "of interest" These parameters have the "poi" at...
void SetName(const char *name) override
Set the name of the TNamed.
TGListTreeItem * GetTreeItem(TBrowser *b) const
void SetTitle(const char *title) override
Set the title of the TNamed.
xRooNode Multiply(const xRooNode &child, Option_t *opt="")
xRooNode Replace(const xRooNode &node)
bool SetData(const TObject &obj, const xRooNode &data="obsData")
xRooNode Remove(const xRooNode &child)
xRooNode globs() const
List of global observables of this node.
xRooNode Add(const xRooNode &child, Option_t *opt="")
xRooNode(const char *type, const char *name, const char *title="")
const char * GetIconName() const override
Returns mime type name of object.
void Draw(Option_t *opt="") override
Default Draw method for all objects.
TGraph * BuildGraph(RooAbsLValue *v=nullptr, bool includeZeros=false, TVirtualPad *fromPad=nullptr) const
const std::shared_ptr< xRooNode > & at(size_t idx, bool browseResult=true) const
Definition xRooNode.h:147
std::string fRange
! only here so can have char* GetRange return so can return nullptr for no range set !...
Definition xRooNode.h:515
void _fit_(const char *constParValues="", const char *options="GoF")
bool SetBinContent(int bin, double value, const char *par=nullptr, double parVal=1)
std::shared_ptr< xRooNode > operator[](size_t idx)
Definition xRooNode.h:164
void Checked(TObject *obj, bool val)
Definition xRooNode.cxx:549
void Print(Option_t *opt="") const override
Print TNamed name and title.
std::shared_ptr< T > acquireNew(Args &&...args)
Definition xRooNode.h:274
void SaveAs(const char *filename="", Option_t *option="") const override
Save this object in the file specified by filename.
xRooNode bins() const
bins of a channel or sample, or channels of a multi-channel pdf
std::shared_ptr< xRooNode > find(const std::string &name, bool browseResult=true) const
TGListTree * GetListTree(TBrowser *b) const
xRooNode styles(TObject *initObject=nullptr, bool autoCreate=true) const
xRooNode generate(const xRooNode &fr="", bool expected=false, int seed=0)
void Inspect() const override
Dump contents of this object in a graphics canvas.
std::shared_ptr< xRooNode > getBrowsable(const char *name) const
bool SetXaxis(const RooAbsBinning &binning)
double GetBinError(int bin, const xRooNode &fr="", int nToys=0, bool errorsHi=false, bool errorsLo=false) const
bool SetBinData(int bin, double value, const xRooNode &data="obsData")
std::vector< double > GetBinContents(int binStart=1, int binEnd=0) const
xRooNode pp() const
List of prespecified parameters: non-floatable parameters.
double GetError(const xRooNode &fr="", int nToys=0, bool errorsHi=false, bool errorsLo=false) const
Definition xRooNode.h:419
std::shared_ptr< xRooNode > fProvider
! like a parent but only for use by getObject
Definition xRooNode.h:524
std::shared_ptr< TStyle > style(TObject *initObject=nullptr, bool autoCreate=true) const
xRooNode fitResult(const char *opt="") const
std::shared_ptr< TAxis > fXAxis
! appears that if was fXaxis then dialog box for SetXaxis will take as current value
Definition xRooNode.h:519
xRooNode coords(bool setVals=true) const
xRooNode Vary(const xRooNode &child)
xRooNode Constrain(const xRooNode &child)
static std::map< std::string, std::tuple< std::function< double(double, double, double)>, bool > > auxFunctions
Definition xRooNode.h:58
xRooNode pars() const
List of parameters (non-observables) of this node.
void SetFitResult(const RooFitResult *fr=nullptr)
xRooNode coefs(bool recurse=false) const
bool IsFolder() const override
Returns kTRUE in case object contains browsable objects (like containers or lists of other objects).
Definition xRooNode.cxx:942
double GetBinData(int bin, const xRooNode &data="obsData")
bool SetBinError(int bin, double value)
bool SetContents(const TObject &obj)
Definition xRooNode.h:349
std::shared_ptr< TObject > fComp
!
Definition xRooNode.h:506
std::pair< double, double > IntegralAndError(const xRooNode &fr="", const char *rangeName=nullptr, int nToys=0, bool errorsHi=false, bool errorsLo=false) const
std::vector< double > contents() const
RooWorkspace * ws() const
The RooWorkspace this node belong to, if any.
xRooNode shallowCopy(const std::string &name, std::shared_ptr< xRooNode > parent=nullptr)
void _generate_(const char *name="", bool expected=false)
std::shared_ptr< TObject > getObject(const std::string &name, const std::string &type="") const
xRooNode np() const
List of nuisance parameters: non-constant parameters that are not marked of interest,...
xRooNode Combine(const xRooNode &rhs, bool silent=false)
xRooNode floats() const
List of parameters that are currently non-constant These parameters do not have the "Constant" attrib...
bool contains(const std::string &name) const
double GetBinContent(int bin) const
Definition xRooNode.h:391
xRooNode reduced(const std::string &range="", bool invert=false) const
std::vector< double > GetBinErrors(int binStart=1, int binEnd=0, const xRooNode &fr="", int nToys=0, bool errorsHi=false, bool errorsLo=false) const
auto begin() const -> xRooNodeIterator
Definition xRooNode.h:200
std::shared_ptr< xRooNode > parentPdf() const
void _scan_(const char *what="plr", double nToys=0, const char *xvar="", int nPointsX=0, double lowX=0, double highX=0, const char *constParValues="", const char *options="")
std::shared_ptr< xRooNode > fParent
!
Definition xRooNode.h:509
xRooNode robs() const
List of regular observables of this node.
TClass * IsA() const override
Definition xRooNode.h:537
xRooNode & operator=(const TObject &o)
auto end() const -> xRooNodeIterator
Definition xRooNode.h:201
xRooNode consts() const
List of parameters that are currently constant.
void _SetBinContent_(int bin, double value, const char *par="", double parVal=1)
std::shared_ptr< TObject > acquire(const std::shared_ptr< TObject > &arg, bool checkFactory=false, bool mustBeNew=false)
std::function< xRooNode(xRooNode *) fBrowseOperation)
Definition xRooNode.h:532
void _SetAttribute_(const char *name, const char *value=nullptr)
Definition xRooNode.cxx:513
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:664
xRooNode obs() const
List of observables (global and regular) of this node.
void SetRange(const char *range, double low=std::numeric_limits< double >::quiet_NaN(), double high=std::numeric_limits< double >::quiet_NaN())
static void SetAuxFunction(const char *title, const std::function< double(double, double, double)> &func, bool symmetrize=false)
Definition xRooNode.cxx:222
std::shared_ptr< TObject > convertForAcquisition(xRooNode &acquirer, const char *opt="") const
xRooNode vars() const
List of variables (observables and parameters) of this node.
TMatrixDSym covariances(const xRooNode &fr="") const
Class describing the configuration of the fit, options and parameter settings using the ROOT::Fit::Pa...
Definition FitConfig.h:49
const_iterator begin() const
const_iterator end() const
Common abstract base class for objects that represent a value and a "shape" in RooFit.
Definition RooAbsArg.h:76
bool dependsOn(const RooAbsCollection &serverList, const RooAbsArg *ignoreArg=nullptr, bool valueOnly=false) const
Test whether we depend on (ie, are served by) any object in the specified collection.
void setStringAttribute(const Text_t *key, const Text_t *value)
Associate string 'value' to this object under key 'key'.
RooFit::OwningPtr< RooArgSet > getObservables(const RooArgSet &set, bool valueOnly=true) const
Given a set of possible observables, return the observables that this PDF depends on.
void SetName(const char *name) override
Set the name of the TNamed.
const Text_t * getStringAttribute(const Text_t *key) const
Get string attribute mapped under key 'key'.
bool getAttribute(const Text_t *name) const
Check if a named attribute is set. By default, all attributes are unset.
TObject * Clone(const char *newname=nullptr) const override
Make a clone of an object using the Streamer facility.
Definition RooAbsArg.h:88
virtual bool isFundamental() const
Is this object a fundamental type that can be added to a dataset? Fundamental-type subclasses overrid...
Definition RooAbsArg.h:175
Abstract base class for RooRealVar binning definitions.
virtual double highBound() const =0
virtual double lowBound() const =0
Abstract base class for objects that represent a discrete value that can be set from the outside,...
A space to attach TBranches.
const char * getLabel() const
Retrieve current label. Use getCurrentLabel() for more clarity.
Abstract container object that can hold multiple RooAbsArg objects.
RooAbsCollection * selectByAttrib(const char *name, bool value) const
Create a subset of the current collection, consisting only of those elements with the specified attri...
virtual bool remove(const RooAbsArg &var, bool silent=false, bool matchByNameOnly=false)
Remove the specified argument from our list.
virtual bool add(const RooAbsArg &var, bool silent=false)
Add the specified argument to list.
Storage_t::size_type size() const
bool selectCommon(const RooAbsCollection &refColl, RooAbsCollection &outColl) const
Create a subset of the current collection, consisting only of those elements that are contained as we...
std::string contentsString() const
Return comma separated list of contained object names as STL string.
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:503
virtual void SetValue(const char *name, const char *value, EEnvLevel level=kEnvChange, const char *type=nullptr)
Set the value of a resource or create a new resource.
Definition TEnv.cxx:744
TExec is a utility class that can be used to execute a C++ command when some event happens in a pad.
Definition TExec.h:26
1-Dim function class
Definition TF1.h:182
A 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:9074
void SetTitle(const char *title) override
Change/set the title.
Definition TH1.cxx:6836
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:3912
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:836
TAxis * GetYaxis()
Definition TH1.h:572
void Draw(Option_t *option="") override
Draw this histogram with options.
Definition TH1.cxx:3097
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:6719
TObject * Clone(const char *newname="") const override
Make a complete copy of the underlying object.
Definition TH1.cxx:2786
virtual void SetStats(Bool_t stats=kTRUE)
Set statistics option on/off.
Definition TH1.cxx:9127
2-D histogram with a double per channel (see TH1 documentation)
Definition TH2.h:400
Service class for 2-D histogram classes.
Definition TH2.h:30
Int_t Fill(Double_t) override
Invalid Fill method.
Definition TH2.cxx:363
The Histogram stack class.
Definition THStack.h:40
TList * GetHists() const
Definition THStack.h:72
virtual void Add(TH1 *h, Option_t *option="")
Add a new histogram to the list.
Definition THStack.cxx:364
Book space in a file, create I/O buffers, to fill them, (un)compress them.
Definition TKey.h:28
To draw Mathematical Formula.
Definition TLatex.h:20
Storage class for one entry of a TLegend.
This class displays a legend box (TPaveText) containing several legend entries.
Definition TLegend.h:23
Use the TLine constructor to create a simple line.
Definition TLine.h:22
virtual void SetY2(Double_t y2)
Definition TLine.h:70
virtual void SetX2(Double_t x2)
Definition TLine.h:68
Double_t GetY1() const
Definition TLine.h:52
virtual void SetX1(Double_t x1)
Definition TLine.h:67
virtual void SetY1(Double_t y1)
Definition TLine.h:69
Double_t GetY2() const
Definition TLine.h:53
A doubly linked list.
Definition TList.h:38
void Clear(Option_t *option="") override
Remove all objects from the list.
Definition TList.cxx:532
void Add(TObject *obj) override
Definition TList.h:81
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:2384
Bool_t Contains(const char *pat, ECaseCompare cmp=kExact) const
Definition TString.h:641
Ssiz_t Index(const char *pat, Ssiz_t i=0, ECaseCompare cmp=kExact) const
Definition TString.h:660
TStyle objects may be created to define special styles.
Definition TStyle.h:29
void SetPaintTextFormat(const char *format="g")
Definition TStyle.h:390
Int_t GetOptTitle() const
Definition TStyle.h:248
Float_t GetLabelSize(Option_t *axis="X") const
Return label size.
Definition TStyle.cxx:1146
Float_t GetPadRightMargin() const
Definition TStyle.h:216
Style_t GetTitleFont(Option_t *axis="X") const
Return title font.
Definition TStyle.cxx:1217
Bool_t GetHistMinimumZero() const
Definition TStyle.h:239
Float_t GetPadLeftMargin() const
Definition TStyle.h:215
Float_t GetTitleYSize() const
Definition TStyle.h:281
Double_t GetHistTopMargin() const
Definition TStyle.h:240
Float_t GetPadBottomMargin() const
Definition TStyle.h:213
const char * GetPaintTextFormat() const
Definition TStyle.h:252
Float_t GetPadTopMargin() const
Definition TStyle.h:214
virtual Bool_t ExpandPathName(TString &path)
Expand a pathname getting rid of special shell characters like ~.
Definition TSystem.cxx: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
constexpr Double_t C()
Velocity of light in .
Definition TMath.h:117
Double_t Prob(Double_t chi2, Int_t ndf)
Computation of the probability for a certain Chi-squared (chi2) and number of degrees of freedom (ndf...
Definition TMath.cxx:637
Double_t ChisquareQuantile(Double_t p, Double_t ndf)
Evaluate the quantiles of the chi-squared probability distribution function.
Definition TMath.cxx:2196
Double_t StdDev(Long64_t n, const T *a, const Double_t *w=nullptr)
Definition TMath.h:534
#define BEGIN_XROOFIT_NAMESPACE
Definition Config.h:24
#define END_XROOFIT_NAMESPACE
Definition Config.h:25
static const char * what
Definition stlLoader.cc:5
bool isNaNWithPayload() const
Test if this struct has a float packed into its mantissa.
static float unpackNaN(double val)
If val is NaN and a this NaN has been tagged as containing a payload, unpack the float from the manti...
th1 Draw()
TLine lv
Definition textalign.C:5
TMarker m
Definition textangle.C:8
TLine l
Definition textangle.C:4
auto * tt
Definition textangle.C:16
static uint64_t sum(uint64_t i)
Definition Factory.cxx:2338
#define GETWS(a)
#define GETWSSETS(w)
void(* gOldHandlerr)(int)
void addLegendEntry(TObject *o, const char *title, const char *opt)
void buildHistogramInterrupt(int signum)
auto GETACTBROWSER(TRootBrowser *b)
Definition xRooNode.cxx:111
auto & GETWSSNAPSHOTS(RooWorkspace *w)
Definition xRooNode.cxx:107
bool TopRightPlaceBox(TPad *p, TObject *o, double w, double h, double &xl, double &yb)
#define GETDMP(o, m)
Definition xRooNode.cxx:123
TPaveText * getPave(const char *name="labels", bool create=true, bool doPaint=false)
std::string formatLegendString(const std::string &s)
auto GETROOTDIR(TGFileBrowser *b)
Definition xRooNode.cxx:115
const xRooNode * runningNode
auto GETLISTTREE(TGFileBrowser *b)
Definition xRooNode.cxx:119
const T & _or_func(const T &a, const T &b)
Definition xRooNode.cxx:229
double new_getPropagatedError(const RooAbsReal &f, const RooFitResult &fr, const RooArgSet &nset={}, RooArgList **pars=nullptr, bool asymHi=false, bool asymLo=false)
TLegend * getLegend(bool create=true, bool doPaint=false)